Floney

[플로니] Mysql에서는 이모지를 몇 글자로 인식하나요?🦄🍀🍀

딤섬뮨 2024. 1. 16. 17:50
728x90

문제 발생

다음은 플로니가 출시되고 만난 에러 중 하나이다.

로그 파일에 들어가서 보면 Date too long for column 'name' at row 1라는 메시지의 Sql Exception이 터져있다.

 
문제가 있는 name 컬럼은 가계부 이름 칼럼이고, 우리의 비즈니스 로직 상, 가계부 이름은 10자로 제한 돼 있어 DB도 크기가 varchar(10)으로 설정 돼있었다.
 
이상한 건, 분명 프런트에서도 가계부 이름을 10자로 제한하고 있을 텐데 에러가 터질 리가 없었다.
그래서 사용자가 요청했던 로그를 보니, 가계부 이름은 10자를 넘지 않았지만, 이모지(🍀)가 들어가 있었다.
 
그렇게 테스트를 해보았다. DB에서 이모지가 몇글자로 인식되는가? 다음과 같이 ✅❣️ 를 넣으면, 우리 눈엔 두 글자지만 mysql 입장에선 3글자로 나온다. * varchar는 char_length라는 함수 기준으로 글자를 센다

 
팀원과 이야기를 하던 중 utf8mb4로 돼있는 우리 DB 인코딩 방식 때문에 다르게 잡히는 것 같다고 말해 주셔서 해당 개념을 찾아보게 되었다.


유니코드가 뭔데요?

컴퓨터에서 문자들은 각 문자(한국어,한자 등등) 마다 문자 집합이 있다. 쉽게 생각하면, 우리가 학창 시절에 반별로, 학생이 나뉜 것처럼 말이다. 넌 1반 난 10반..
 
그렇게 학생에게 출석번호를 매기듯, 컴퓨터에서도 '가'는 10001 이라던지 이런식으로 코드를 할당해 왔다. 학창 시절 속 1반의 3번과 2반의 3번은 같은 출석 번호지만, 반이 다르기에 다른 사람이듯,  같은 10001이 문자집합마다 할당되어 있어서, 문자를 나타내기 어려웠다.
 
ex. 유럽어의 문자 집합에도 있었다. 유로화를 나타내는 '€' 기호에는 ISO 8859-15(Latin 9)의 코드 값 중 0xA4이 할당되었으나 ISO 8859-1(Latin 1)의 0xA4 코드에 할당된 문자는 '¤'다.
 
이를 극복하고자 나온 방식이 유니코드인 것이다.
 
유니코드는 쉽게 생각해서 학창 시절 속 반을 나누지 말고,전교생을 쫙 나열하는 것처럼 모든 문자를 하나로 쫙 나열한 뒤, 번호를 매기는 것이다.
그렇게 전세계의 모든 문자를 표현하기 위해 만들어진 하나의 문자 집합이다.
 
유니코드는 값을 나타내기 위해 코드 포인트를 사용하는데 보통 U+을 붙여 표시한다. 출석번호 1,2,3,4,5처럼 고유한 번호라고 생각하면 편할 것 같다.
ex. U+0041, U+005..
 
코드포인트는  한글이나, 영어같은 경우 "A" =  U+0041 처럼 일대일 대응이지만, 이모지는 무조건 일대일이 아니라, 일대다 대응도 있다는 것이다.
 
예를 들어 👩🏽‍🤝‍👩🏾 이런 이모티콘은 코드 포인트가 다음과 같다.
1F469 1F3FC 200D 1F91D 200D 1F469 1F3FD (총 7개)  코드 포인트가 7개가 합쳐져서 만들어진다
 
이걸 기억하고 다음 챕터를 봐보자..
 
유니 코드 깊게 들어가면 끝도 없어서 기본 개념만 알면 쓴다.
더 알고 싶으면, 다음 글을 보면 된다.
한글 인코딩의 이해 1편: 한글 인코딩의 역사와 유니코드 (naver.com)


MySQL에서 글자를 인식하는 방식

이렇게 많은 유니코드를 컴퓨터에 어떻게 비트로 표현할까?가 바로 인코딩이다.
대표적으로 UTF-8을 사용한다. UTF-8은 1~4 byte 가변 길이의 인코딩이다. 가변 길이란 문자마다 바이트가 다르다는 것이다.
예를 들어, a는 1byte고 가 는 3byte다. (가변을 구분하기 위해 첫 바이트에 표식을 넣었는데 2byte는 110으로 시작하고, 3byte는 1110으로 시작한다. 나머지는 10으로 시작한다)
 
mysql 4.1버전 전에는 utf-8 인코딩을 사용할 경우 varchar(n)에서 n이 byte기준이었다면, 4.1 이후부터는 글자수 기준이다.

MySQL :: MySQL 5.0 Reference Manual :: 11.1.3 String Type Overview (archive.org)

 
mysql에서 글자수 즉 char를 세는 기준은,  char_lenth()라는 함수이다.
char_length() 함수는 앞서 말한 우리의 출석번호 개수대로 글자를 인식한다고 한다.(팀원이 블로그에 알려주심!!)

 
정말 그런지 테스트를 해보자 앞에서 👩🏽‍🤝‍👩🏾 의 코드 포인트가 7개라고 했다.
 

결론은 내가 아무리 ❣️ (코드 포인트 2개) 하나만 넣는다고 한들, mysql에서는 해당 글자가 2로 잡힌다


해결책

 
그러면 나는 단순히 하나의 이모티콘에 들어갈 수 있는 최대 코드 포인트를 알고 최대 코드 포인트 개수  * 10자(가계부 이름 제한) 하면 DB의 데이터 길이를 잡을 수 있지 않을까 했었는데, 나랑 같은 생각을 하는 사람이 있었다.
결론은 하나의 단일 문자에 들어갈 수 있는 코드 포인트는 무제한이다.
utf 8 - What's the maximum number of code points for a UTF-8 encoded displayed character? - Stack Overflow

What's the maximum number of code points for a UTF-8 encoded displayed character?

We were attempting to insert emojis into our database, but were encountering weird behavior. It turns out this has to do with utf-8 encoding. 👍 would work fine, but 🌶 would not. This is when we l...

stackoverflow.com

 
아예 사용자에게 이모지를 막자니, 현재 쌓인 데이터에서 특수 기호를 쓰는 경우가 많고 사용자의 자유성을 없애는것 같았다. 그리하여 서버팀이 내린 결론은 다음과 같다.
 
DB의 가계부 이름 길이를 varchar(200)으로 넉넉히 잡은 뒤, 그 이상을 초과할 시 이모티콘을 빼달라는 alert 처리하기!
 
사실 이번 이슈 해결책은 간단했는데, 유니코드 이해하고 그러는데 하루는 걸린것 같다. 그래도 유니코드 대충만 알았지 유니코드에 대해 깊게 알게 돼서 재밌었다! 🤩 
 

728x90