Swift에서는 왜 문자열을 Subscript로 접근할 수 없을까?
다른 언어와의 차이점(Subscript)
다른 프로그래밍 언어를 배우다가 Swift를 배웠을 때 제일 처음 당혹스러운 것이 문자열을 Subscript문법으로 접근이 불가능하는 것을 알게됐을 때 많이 당황했던 것 같습니다.
- Python
# python
text = "Python"
result = text[0]
print(result) # P
- Javascript
// Javascript
var text = "Javascript"
var result = text[0]
console.log(result) // J
- Swift
var text = "Swift"
var result = text[0]
>>> 'subscript' is unavailable: cannot subscript String with an Int, see the documentation comment for discussion
Swift에서는 문자열을 Subscript문법으로 인덱싱하게되면 컴파일 에러가 발생하게 됩니다.
Swift에서는 Collection Type으로 Array, Dictionary, Set등을 정의하고 있습니다.
이런 Collection Type은 Subscript을 통해 해당 요소에 접근할 수 있지만 문자열은 불가능한 것입니다.
문자열 또한 Collection Type을 따르고 있습니다. Subscript를 사용할 수는 있지만 단순 Int자료형으로 문자열의 요소에 접근할 수는 없다는 뜻입니다.
요약하자면 다른 C, C++, Python과 같은 언어는 문자열을 문자들의 배열 구조로 메모리에 저장되며 Int 자료형 인덱스로 문자열의 요소를 찾을 수 있는 것입니다.
Swift에서 문자를 다루는 방식
Swift에서 문자를 다루는 방식을 알아보기 전 참고하면 좋을 개념에 대해 조금 알아보고 가겠습니다.
Ascii 코드와 유니코드(UTF-8)에 대해서 알아보겠습니다. 이 두 개념의 역사를 알려주는 영상인데 한번 참고해보면 좋을 것 같습니다.
영상에서 설명을 요약하자면 최초의 컴퓨터가 탄생할 때, 컴퓨터끼리 문서를 주고받으려면 문자나 숫자에 대한 상호 규칙이 필요했습니다. 컴퓨터는 0, 1만 읽을 수 있으므로 문자를 바이너리 코드로 바꿀 수 있도록 규약을 정했습니다. 처음에는 미국에서 알파벳을 대상으로만 그 규칙을 정한 것입니다. 그것이 바로 ASCII(아스키 코드)입니다.
ASCII(아스키 코드)
아스키 코드 - 나무위키
아래 표는 아스키 코드 중 제어 문자와 확장 아스키 코드를 제외한 부호(영문 자판에 사용되는 부호)를 정리한 것이다. 10진수부호10진수부호10진수부호10진수부호0320568080P104h033!0579081Q105i034"058:082
namu.wiki
ASCII(American Standard Code for Information Interchange)의 약자
이름에서도 알 수 있듯이 미국 위주의 즉, 영어의 알파벳 중심의 규칙을 정한 것입니다.
예를 들면 알파벳 “A” → 101, “B” → 102 등의 바이너리 코드로 변환하여 컴퓨터끼리 문서를 주고 받았던겁니다. 4바이트(32비트, 약 42억 자)의 넉넉한 공간에 세상의 모든 문자를 할당한 결과물인 것이죠.
하지만 여기서 문제가 발생했습니다. 만약 영어권 국가가 아닌 곳에서 다른 언어는 주고 받을 규약이 없다는 것입니다. 하지만 모든 글자를 할당하고도 공간이 조금 남았는데 거기에 다른 언어를 할당했었고 문서를 주고 받았더니 글자가 깨졌던 것입니다.

예전에 웹 문서를 읽거나 메일을 보낼 때 간혹 이런 외계어?를 본 적이 있었을 것 입니다. 이런 것들이 ASCII 코드에 저장되지 못했던 글자를 주고 받을 때 나타난 현상이었던 것입니다.
그래서 세계에서 유니코드 컨소시엄이 형성되어 전 세계의 모든 문자를 다루도록 설계된 표준 문자 전산 처리 방식을 만든것이 바로 유니코드 입니다. 영어 위주의 바이너리 형식은 7비트면 충분했었습니다.. 하지만 전세계의 언어의 바이너리 규칙을 만들기 위해서는 훨씬 큰 저장 공간이 필요했고 거기에 세계 모든 언어와 이모지 등, 특수기호를 저장하는 방식이 유니코드였습니다.
Unicode(유니코드)
유니코드 - 나무위키
유니코드는 대개 기본 문자가 들어 있는 BMP(Basic Multilingual Plane), BMP에 없는 옛글자 등을 넣는 SMP(Supplementary Multilingual Plane), 한자를 더 넣기 위해 별도로 정의한 SIP(Supplementary Ideographic Plane), 확장
namu.wiki
UTF-8(Unicode Transformation Format - 8bit)
UTF-8 - 위키백과, 우리 모두의 백과사전
위키백과, 우리 모두의 백과사전. UTF-8은 유니코드를 위한 가변 길이 문자 인코딩 방식 중 하나로, 켄 톰프슨과 롭 파이크가 만들었다. UTF-8은 Universal Coded Character Set + Transformation Format – 8-bit의
ko.wikipedia.org
하지만 유니코드에도 단점은 존재했습니다. 모든 언어에 훨씬 큰 공간을 할당하다 보니 원래 규칙이 있었던 영어에서는 앞에 바이너리 코드가 훨씬 많은 공간으로 붙는 비효율이 발생했습니다. 그러므로 인해 모든 영어 위주의 문서들의 용량이 커지는 문제가 발생했고 이 문제는 메모리 용량을 많이 사용하게 된다는 의미와도 같습니다. 이를 효율적으로 인코딩할 수 있는 방식이었던, UTF-8 인코딩 방식으로 등장하게 됩니다.
유니코드의 인코딩 방식에는 여러가지가 존재합니다. UTF-8, UTF-16, UTF-32, EUC-KR(한글을 읽는 인코딩 방식), UCS-2, UCS-4 ... 등이 존재합니다.
UTF-8
이 중 가장 효율적인 인코딩 방식인 UTF-8은 문자마다 적절한 바이트 수를 차지하도록 다른 인코딩 방식보다 일반적으로 적은 용량만 사용하면서도 호환 문제도 가장 덜 발생하는 방식이기 때문에 전세계적으로 가장 널리 사용됨.

원래의 아스키코드로 할당되었던 문자들도 공간의 비효율없이 원래 그대로 쓰면됩니다.
1바이트 영역은 아스키 코드 와 하위 호환성을 가지게 됩니다.. 아스키 코드의 0~127까지는 UTF-8로 완전히 동일하게 기록됩니다.
다시 돌아와서 Swift는 문자열을 그대로 받아들여지기 보다는 유니코드를 채택하고 있어서 글자마다 가변적인 크기를 가진 문자들 즉 유니코드 문자들의 집합을 보여주는 View에 불과하다는 것입니다. 그렇기 때문에 문자열을 인덱싱하려면 같은 크기가 아니기 때문에 단순 Int에 의한 인덱싱으로는 해당 문자 요소를 꺼낼 수 없다는 것입니다.
- 장점
유니코드 문자가 깨지는 모습 없이 보여주는데 탁월하다는 것입니다.
- 단점
다른 언어에 비해 문자열을 처리하는 방식이 까다롭다는 것입니다.
Swift에서 문자열의 요소에 접근하기 위해서는 다음과 같은 방식으로 사용해야합니다.
let str = "hello world!"
str[str.startIndex] // "h"
Swift 공식문서
String and Characters
Documentation
docs.swift.org
공식문서에 따르면 Character 타입의 데이터는 하나의 확장된 문자소 집합(Extended Grapheme Clusters)으로 표현된다.
Swift Character 유형의 모든 인스턴스는 단일 확장 문자소 클러스터를 나타냅니다. 확장 문자소 클러스터는 (결합 시) 사람이 읽을 수 있는 단일 문자를 생성하는 하나 이상의 유니코드 스칼라 시퀀스입니다.
다음 예시를 보면 조금 더 이해를 도울 수 있을 것 같습니다.
let precomposed: Character = "\u{D55C}" // 한
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ, ᅡ, ᆫ
print(precomposed, decomposed)
// 한 한
precomposed 상수에 담긴 Character는 하나의 글자로 보이며 하나의 유니코드 스칼라 값을 가집니다. 하지만 decomposed 상수에 담긴 문자는 3개의 유니코드 스칼라 값을 가집니다. 하지만 print를 해서 본다면 같은 글자로 보이게 되는 것입니다. 여기서 분명한 차이점을 보이며 왜 Swift에서 문자열에 Int 정수로 인덱싱이 불가능한지 알 수 있습니다.
이렇듯 한 글자마다 여러 개의 유니코드 스칼라값을 가진다면 각 글자의 메모리가 다르기 때문에 정수로 접근했을 때 어떤 문자를 가져와야하는지 불분명하기 때문에 컴파일 에러가 나도록 설계되어 있는 것 같습니다. 그렇기 때문에 Int 정수 데이터가 아닌 String.Index를 사용해 문자열의 요소에 접근할 수 있는 것입니다.
이런 유니코드 방식을 채택한 이유는 전 세계에 서비스해야하는 애플이 고심 끝에 편의성과 성능을 조금 포기한 채 선택한 전략이 아닌가라는 생각을 해보면서 Swift에서의 불편한 문자열 인덱싱을 마무리하겠습니다.
'iOS > Swift' 카테고리의 다른 글
Swift - @objc dynamic property (2) | 2023.12.29 |
---|---|
Swift - KVO(Key-Value Observing) (2) | 2023.12.22 |
Swift - Method Swizzling (1) | 2023.12.01 |
ARC(Automatic Reference Counting) (0) | 2023.09.04 |
Swift - Protocol (0) | 2023.08.07 |
Swift에서는 왜 문자열을 Subscript로 접근할 수 없을까?
다른 언어와의 차이점(Subscript)
다른 프로그래밍 언어를 배우다가 Swift를 배웠을 때 제일 처음 당혹스러운 것이 문자열을 Subscript문법으로 접근이 불가능하는 것을 알게됐을 때 많이 당황했던 것 같습니다.
- Python
# python
text = "Python"
result = text[0]
print(result) # P
- Javascript
// Javascript
var text = "Javascript"
var result = text[0]
console.log(result) // J
- Swift
var text = "Swift"
var result = text[0]
>>> 'subscript' is unavailable: cannot subscript String with an Int, see the documentation comment for discussion
Swift에서는 문자열을 Subscript문법으로 인덱싱하게되면 컴파일 에러가 발생하게 됩니다.
Swift에서는 Collection Type으로 Array, Dictionary, Set등을 정의하고 있습니다.
이런 Collection Type은 Subscript을 통해 해당 요소에 접근할 수 있지만 문자열은 불가능한 것입니다.
문자열 또한 Collection Type을 따르고 있습니다. Subscript를 사용할 수는 있지만 단순 Int자료형으로 문자열의 요소에 접근할 수는 없다는 뜻입니다.
요약하자면 다른 C, C++, Python과 같은 언어는 문자열을 문자들의 배열 구조로 메모리에 저장되며 Int 자료형 인덱스로 문자열의 요소를 찾을 수 있는 것입니다.
Swift에서 문자를 다루는 방식
Swift에서 문자를 다루는 방식을 알아보기 전 참고하면 좋을 개념에 대해 조금 알아보고 가겠습니다.
Ascii 코드와 유니코드(UTF-8)에 대해서 알아보겠습니다. 이 두 개념의 역사를 알려주는 영상인데 한번 참고해보면 좋을 것 같습니다.
영상에서 설명을 요약하자면 최초의 컴퓨터가 탄생할 때, 컴퓨터끼리 문서를 주고받으려면 문자나 숫자에 대한 상호 규칙이 필요했습니다. 컴퓨터는 0, 1만 읽을 수 있으므로 문자를 바이너리 코드로 바꿀 수 있도록 규약을 정했습니다. 처음에는 미국에서 알파벳을 대상으로만 그 규칙을 정한 것입니다. 그것이 바로 ASCII(아스키 코드)입니다.
ASCII(아스키 코드)
아스키 코드 - 나무위키
아래 표는 아스키 코드 중 제어 문자와 확장 아스키 코드를 제외한 부호(영문 자판에 사용되는 부호)를 정리한 것이다. 10진수부호10진수부호10진수부호10진수부호0320568080P104h033!0579081Q105i034"058:082
namu.wiki
ASCII(American Standard Code for Information Interchange)의 약자
이름에서도 알 수 있듯이 미국 위주의 즉, 영어의 알파벳 중심의 규칙을 정한 것입니다.
예를 들면 알파벳 “A” → 101, “B” → 102 등의 바이너리 코드로 변환하여 컴퓨터끼리 문서를 주고 받았던겁니다. 4바이트(32비트, 약 42억 자)의 넉넉한 공간에 세상의 모든 문자를 할당한 결과물인 것이죠.
하지만 여기서 문제가 발생했습니다. 만약 영어권 국가가 아닌 곳에서 다른 언어는 주고 받을 규약이 없다는 것입니다. 하지만 모든 글자를 할당하고도 공간이 조금 남았는데 거기에 다른 언어를 할당했었고 문서를 주고 받았더니 글자가 깨졌던 것입니다.

예전에 웹 문서를 읽거나 메일을 보낼 때 간혹 이런 외계어?를 본 적이 있었을 것 입니다. 이런 것들이 ASCII 코드에 저장되지 못했던 글자를 주고 받을 때 나타난 현상이었던 것입니다.
그래서 세계에서 유니코드 컨소시엄이 형성되어 전 세계의 모든 문자를 다루도록 설계된 표준 문자 전산 처리 방식을 만든것이 바로 유니코드 입니다. 영어 위주의 바이너리 형식은 7비트면 충분했었습니다.. 하지만 전세계의 언어의 바이너리 규칙을 만들기 위해서는 훨씬 큰 저장 공간이 필요했고 거기에 세계 모든 언어와 이모지 등, 특수기호를 저장하는 방식이 유니코드였습니다.
Unicode(유니코드)
유니코드 - 나무위키
유니코드는 대개 기본 문자가 들어 있는 BMP(Basic Multilingual Plane), BMP에 없는 옛글자 등을 넣는 SMP(Supplementary Multilingual Plane), 한자를 더 넣기 위해 별도로 정의한 SIP(Supplementary Ideographic Plane), 확장
namu.wiki
UTF-8(Unicode Transformation Format - 8bit)
UTF-8 - 위키백과, 우리 모두의 백과사전
위키백과, 우리 모두의 백과사전. UTF-8은 유니코드를 위한 가변 길이 문자 인코딩 방식 중 하나로, 켄 톰프슨과 롭 파이크가 만들었다. UTF-8은 Universal Coded Character Set + Transformation Format – 8-bit의
ko.wikipedia.org
하지만 유니코드에도 단점은 존재했습니다. 모든 언어에 훨씬 큰 공간을 할당하다 보니 원래 규칙이 있었던 영어에서는 앞에 바이너리 코드가 훨씬 많은 공간으로 붙는 비효율이 발생했습니다. 그러므로 인해 모든 영어 위주의 문서들의 용량이 커지는 문제가 발생했고 이 문제는 메모리 용량을 많이 사용하게 된다는 의미와도 같습니다. 이를 효율적으로 인코딩할 수 있는 방식이었던, UTF-8 인코딩 방식으로 등장하게 됩니다.
유니코드의 인코딩 방식에는 여러가지가 존재합니다. UTF-8, UTF-16, UTF-32, EUC-KR(한글을 읽는 인코딩 방식), UCS-2, UCS-4 ... 등이 존재합니다.
UTF-8
이 중 가장 효율적인 인코딩 방식인 UTF-8은 문자마다 적절한 바이트 수를 차지하도록 다른 인코딩 방식보다 일반적으로 적은 용량만 사용하면서도 호환 문제도 가장 덜 발생하는 방식이기 때문에 전세계적으로 가장 널리 사용됨.

원래의 아스키코드로 할당되었던 문자들도 공간의 비효율없이 원래 그대로 쓰면됩니다.
1바이트 영역은 아스키 코드 와 하위 호환성을 가지게 됩니다.. 아스키 코드의 0~127까지는 UTF-8로 완전히 동일하게 기록됩니다.
다시 돌아와서 Swift는 문자열을 그대로 받아들여지기 보다는 유니코드를 채택하고 있어서 글자마다 가변적인 크기를 가진 문자들 즉 유니코드 문자들의 집합을 보여주는 View에 불과하다는 것입니다. 그렇기 때문에 문자열을 인덱싱하려면 같은 크기가 아니기 때문에 단순 Int에 의한 인덱싱으로는 해당 문자 요소를 꺼낼 수 없다는 것입니다.
- 장점
유니코드 문자가 깨지는 모습 없이 보여주는데 탁월하다는 것입니다.
- 단점
다른 언어에 비해 문자열을 처리하는 방식이 까다롭다는 것입니다.
Swift에서 문자열의 요소에 접근하기 위해서는 다음과 같은 방식으로 사용해야합니다.
let str = "hello world!"
str[str.startIndex] // "h"
Swift 공식문서
String and Characters
Documentation
docs.swift.org
공식문서에 따르면 Character 타입의 데이터는 하나의 확장된 문자소 집합(Extended Grapheme Clusters)으로 표현된다.
Swift Character 유형의 모든 인스턴스는 단일 확장 문자소 클러스터를 나타냅니다. 확장 문자소 클러스터는 (결합 시) 사람이 읽을 수 있는 단일 문자를 생성하는 하나 이상의 유니코드 스칼라 시퀀스입니다.
다음 예시를 보면 조금 더 이해를 도울 수 있을 것 같습니다.
let precomposed: Character = "\u{D55C}" // 한
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ, ᅡ, ᆫ
print(precomposed, decomposed)
// 한 한
precomposed 상수에 담긴 Character는 하나의 글자로 보이며 하나의 유니코드 스칼라 값을 가집니다. 하지만 decomposed 상수에 담긴 문자는 3개의 유니코드 스칼라 값을 가집니다. 하지만 print를 해서 본다면 같은 글자로 보이게 되는 것입니다. 여기서 분명한 차이점을 보이며 왜 Swift에서 문자열에 Int 정수로 인덱싱이 불가능한지 알 수 있습니다.
이렇듯 한 글자마다 여러 개의 유니코드 스칼라값을 가진다면 각 글자의 메모리가 다르기 때문에 정수로 접근했을 때 어떤 문자를 가져와야하는지 불분명하기 때문에 컴파일 에러가 나도록 설계되어 있는 것 같습니다. 그렇기 때문에 Int 정수 데이터가 아닌 String.Index를 사용해 문자열의 요소에 접근할 수 있는 것입니다.
이런 유니코드 방식을 채택한 이유는 전 세계에 서비스해야하는 애플이 고심 끝에 편의성과 성능을 조금 포기한 채 선택한 전략이 아닌가라는 생각을 해보면서 Swift에서의 불편한 문자열 인덱싱을 마무리하겠습니다.
'iOS > Swift' 카테고리의 다른 글
Swift - @objc dynamic property (2) | 2023.12.29 |
---|---|
Swift - KVO(Key-Value Observing) (2) | 2023.12.22 |
Swift - Method Swizzling (1) | 2023.12.01 |
ARC(Automatic Reference Counting) (0) | 2023.09.04 |
Swift - Protocol (0) | 2023.08.07 |