🫠 String은 왜 index가 까다로울까?
항상 String을 사용하다 보면 아래와 같이 반복문은 쓸 수 있는데 index로 접근하지 못할까?라는 생각을 많이 해봤다.
아래와 같이 특별한 index로 접근해야 접근이 가능하다. 왜 굳이 복잡하게 이렇게 되어있을까? 궁금했다.
🔤 가변길이 인코딩
우선 이것을 생각해보기 전에 문자의 용량에 대해서 알아보고자 한다. "Hello, 👋" 의 길이(용량)는 얼마일까? 아래처럼 프린트해 보면 11이 나온다. 엇 잠깐? 일단 "Hello, 👋" 의 길이는 8인데 왜 11이라는 숫자가 나온걸까?
하나하나 출력해 보면 마지막 손바닥이 4이다. 여기서 숫자는 16진수 두 자리로 된 byte라고 볼 수 있다.
하나의 String에 여러 용량을 가진 요소들이 저장되어 있구나를 알 수 있었다. String이라는 구조체는 이렇게 용량을 맞춰서 저장해놓고 있는다. 심지어 이모지조차도 아래와 같이 용량이 각각 다르다.
Zero Width Joiner
참고로 "🧑🧑🧒🧒", "👋" 왜 같은 이모지인데 다를까? 좀 더 들어가 보면 이렇게 정말 가족이모티콘은 "👨👩👧👦" 가족 그 자체로 되어있는 것을 볼 수 있었다. 하나의 작은 이모지는 4바이트이고 이것을 연결시켜 주는 200D로 보이는 것을 Zero Width Joiner라고 한다. 3바이트로 16 + 9 = 25 바이트가 된다는 것을 알 수 있었다. 이모지 하나에 25바이트라니...!
참고로 UTF-8의 경우 1바이트는 0xxxxxxx 로 표현할 수 있고, 2바이트는 110xxxxx10xxxxxx 로 헤더 비트가 붙어 표현되기 때문에 실제로 사용하는 비트는 11비트 결국 0 ~ 2047까지 표현이 가능하기 때문에 200D는 10진수로 8205이므로 3바이트가 필요하다.
UTF-8, UTF-16
아래와 UTF-8의 경우 "🧑🧑🧒🧒"은 25바이트였지만 UTF-16의 경우 22바이트인데 그 이유는 Zero Width Joiner가 2바이트의 크기를 가지고 있기 때문이라고 한다. 따라서 (4 * 4 + 2 * 3)으로 22바이트가 된다!
결국 이모지를 쓴다면 UTF-16을 사용하는 것이 좋아 보인다. 참고로 한글의 경우도 utf-8과 utf-16은 용량이 1바이트 차이가 난다.
🥸 String의 index가 까다로운 이유
바로 문자가 항상 동일한 크기가 아니기 때문이다. 메모리상에서는 길이가 다른 문자 조합으로 존재할 수 있다. 따라서 정수 인덱스로 접근이 불가능하게 했다는 생각이 들었다. String을 단순히 [Character]로 보이게 하지 않고, 문자 경계를 엄격하게 명시해주는 식으로 프로그래머가 처리하도록 의도한 것 같았다.
🧩 추가: Extended Grapheme Clusters (문자소 클러스터)
한글이나 é 같은 경우 조합형 문자의 특징을 가지고 있는데, 같은 글자라도 아래와 같이 여러 방법으로 표현할 수 있다. Swift에서는 이런 유니코드 조합을 하나의 Character로 처리한다. 이것을 사람이 인식하는 문자 단위, 문자소 클러스터라고 부른다. 문자소 클러스터가 있는 이유는 한글의 경우 "ㅎㅏㄴ"를 입력하게 되면 "한"이 자동으로 조합되어 처리된다. 용량은 "한"과 비교해서 같은 문자더라도 3배 차이가 난다.
'→ Swift Study' 카테고리의 다른 글
[Swift] map, flatMap, compactMap 정복하기! (0) | 2025.04.29 |
---|---|
[Swift] forEach, for-in 비교하기 (0) | 2025.04.29 |
[Swift] 명령형, 선언형 프로그래밍과 SwiftUI (0) | 2024.10.23 |
[Swift] @State, @Binding? (1) | 2024.10.23 |
[Swift] Do, Try, Catch 간단하게 알아보기 (0) | 2024.02.09 |