etc

[Unicode] Unicode의 Code Point와 문자열의 길이

sechoi 2024. 1. 11. 19:55

문제

utf8mb4을 사용하는 MySQL에서 한 테이블에 자료형이 varchar(1)인 'name' 컬럼이 있다.

그 테이블에 다음과 같은 쿼리를 실행하면 결과는 어떻게 될까?

insert into test(name) values('✅');
insert into test(name) values('🇰🇷');

 

정답

첫번째 쿼리는 성공하고, 두번째 쿼리는 "Data too long for column name 'name'"으로 실패한다.

같은 이모티콘이고 varchar 자료형을 사용했음에도 왜 다른 길이로 인식된 걸까?

 

 

Unicode (유니코드)

유니코드(The Unicode Standard)는 전 세계의 모든 문자를 컴퓨터에서 일관되게 표현하고 다룰 수 있도록 설계된 산업 표준이다. 한글, 한자, 영어 등의 언어부터 이모티콘까지 다양한 문자들이 유니코드로 표시된다. 예로 "✅" 이모티콘은 유니코드 "U+2705"이다.

이러한 유니코드를 컴퓨터가 이해할 수 있게 인코딩하는 방식 중 하나가 바로 널리 쓰이는 UTF-8 이다.

 

Code Point

그리고 유니코드에서 하나의 의미를 가지는 연속된 bit의 모음을 코드 포인트라고 한다. 하나의 코드 포인트는 인코딩 방식에 따라 1~4byte의 크기를 가진다.

 

예시로 "✅" 이모티콘은 유니코드 "U+2705" 이고, 이를 2진수로 나타내면 "1010 1001 0001(2)" 다. 그리고 해당 이진수를 UTF-8로 인코딩하면 3byte의 크기를 가진다.

final byte[] bytes = "✅".getBytes(StandardCharsets.UTF_8);
System.out.println(bytes.length); // 3

 

그리고 MySQL의 char_length() 메서드는 이 코드 포인트를 기준으로 글자 수를 센다.

 

https://dev.mysql.com/doc/refman/8.0/en/string-functions.html#function_char-length

CHAR_LENGTH(str):
Returns the length of the string str, measured in code points. A multibyte character counts as a single code point. 

 

이는 MySQL 만이 아니다. 유니코드를 지원하는 자바나 파이썬 또한 코드 포인트를 기준으로 글자 수를 계산한다.

유니코드를 인식하지 못하는 언어에서는 다른 기준으로 글자 수를 계산한다.

그 중 C에서는 byte 단위로 글자 수를 계산하므로 "✅"의 글자 수는 3이 된다.

#include <stdio.h>

int main()
{
    char c[] = "✅";
    int len = 0;
    while (c[len] != '\0') {
        len++;
    }
    printf("%d", len); // 3

    return 0;
}

 

하나의 문자 != 하나의 Code Point

한 코드 포인트는 하나의 문자가 아닌 하나의 '의미'를 가리킨다. 따라서 하나의 문자는 한 개의 코드 포인트를 필수적으로 가지며, 필요에 따라 추가 코드 포인트를 포함한다.

 

"❣️" 이모티콘의 코드 포인트 개수를 세면 2개로 나온다.

System.out.println("❣️".length()); // 2

 

그리고 해당 이모티콘의 코드 포인트는 "U+2763, U+FE0F" 이다. 

유니코드 표에서 찾아보면 U+2763은 "❣"이고 U+FE0F는 "Variation Selectors"이다. 즉 U+2763 이모티콘을 변형한 형태 중 하나가 예시인 것이다.

 

이런 식으로 코드 포인트 여러 개가 하나의 문자가 될 수 있다. 

 

"#️⃣" 이모티콘은 "U+0023, U+FE0F, U+20E3"의 코드 포인트를 가지고 있으며 이는 각각 "#", "Variation Selectors", "Combining Enclosing Keycap" 이다.

 

문제에서 여러 글자로 인식 된 "🇰🇷" 이모티콘 역시 여러 개의 코드 포인트를 가진다. 따라서 이모티콘이나 특수 문자의 입력을 허용한다면 글자 수 제한을 넉넉하게 해야한다.

사담

운영하는 서비스에서 사용자가 이름에 이모티콘을 넣다가 에러가 발생한 덕분에 해당 내용에 대해 자세히 알아보게 되었다.

이 짤이 생각나는 에러였다. 끝

 

 

참고

- https://meetup.nhncloud.com/posts/317