CS/데이터베이스

데이터 베이스에 완전한 한글 문자만 저장하는 방법이 있을까?(’ㅇ’, ‘ㅏ’ 같은 문자는 저장하기 싫음!)

duddn 2024. 3. 15. 00:32

문제상황

  • 사용자의 닉네임을 저장하는 기능이 있는 애플리케이션을 만들려고 합니다. '영ㅇ'과 같은 불완전한 한글을 검증하는 로직은 애플리케이션에 정규식을 사용하여 쉽게 넣을 수 있지만, 어떠한 경우에도 데이터베이스에 들어가지 않게 만들고 싶습니다.
  • 아무 설정도 하지 않고 VARCHAR(10)으로 테이블을 만들면, '조영ㅇ'과 같은 데이터가 들어갈 수 있습니다.
create table krtext (txt varchar(10));

mysql> select * from krtext;
+-----------+
| txt       |
+-----------+
| yw        |
| 조영우    |
| 조영ㅇ    |
+-----------+

왜 완전하지 않은 한글이 저장가능할까요?

  • 현재 테이블의 문자집합이 'ㅇ'이라는 문자를 지원하기 때문입니다. 현재 테이블의 문자집합을 확인해 봅시다.
    • utf8mb4인 것을 확인할 수 있습니다. 즉, utf8mb4가 'ㅇ'이라는 문자를 지원해서 테이블에 저장 가능한 것입니다.
      CREATE TABLE `krtext` (
          `txt` varchar(10) DEFAULT NULL
      ) ENGINE=InnoDB 
        DEFAULT CHARSET=utf8mb4 
        COLLATE=utf8mb4_0900_ai_ci;

문자집합이 뭔가요?

  • 문자집합은 컴퓨터에서 문자를 표현하는 데 사용되는 일련의 데이터입니다. 'A'라는 문자를 컴퓨터에서 표현할 때 어떤 데이터를 사용할지는 문자집합에 달려 있습니다.
    • '가'는 UTF-8에서 3바이트로 표현됩니다: 0xEA 0xB0 0x80
    • '가'는 EUC-KR에서 2바이트로 표현됩니다: 0xB0 0xA1
  • 문자집합마다 지원하는 언어의 범위가 다를 수 있습니다.
  • MySQL에서 지원하는 문자집합을 보려면 아래와 같이 명령할 수 있습니다.
mysql> show character set;
+----------+---------------------------------+---------------------+--------+
| Charset  | Description                     | Default collation   | Maxlen |
+----------+---------------------------------+---------------------+--------+
| armscii8 | ARMSCII-8 Armenian              | armscii8_general_ci |      1 |
| ascii    | US ASCII                        | ascii_general_ci    |      1 |
| big5     | Big5 Traditional Chinese        | big5_chinese_ci     |      2 |
| binary   | Binary pseudo charset           | binary              |      1 |
| cp1250   | Windows Central European        | cp1250_general_ci   |      1 |
| cp1251   | Windows Cyrillic                | cp1251_general_ci   |      1 |
| cp1256   | Windows Arabic                  | cp1256_general_ci   |      1 |
| cp1257   | Windows Baltic                  | cp1257_general_ci   |      1 |
| cp850    | DOS West European               | cp850_general_ci    |      1 |
| cp852    | DOS Central European            | cp852_general_ci    |      1 |
| cp866    | DOS Russian                     | cp866_general_ci    |      1 |
| cp932    | SJIS for Windows Japanese       | cp932_japanese_ci   |      2 |
| dec8     | DEC West European               | dec8_swedish_ci     |      1 |
| eucjpms  | UJIS for Windows Japanese       | eucjpms_japanese_ci |      3 |
| euckr    | EUC-KR Korean                   | euckr_korean_ci     |      2 |
| utf8mb4  | UTF-8 Unicode                   | utf8mb4_0900_ai_ci  |      4 |
...

그렇다면 완전한 한글만 저장하려면 어떻게 해야할까요?

  • 첫 번째 방법으로는 완전한 문자만 지원하는 문자집합이 있는지 궁금했습니다. 혹시 존재한다면, 문자집합만 바꾸는 것으로 해결할 수도 있다고 생각했습니다. 한글을 지원하는 대표적인 문자집합인 eucKR을 확인해 보았으나, 이 문자집합 역시 'ㅇ'과 같은 불완전한 한글도 지원했습니다.
  • 그래서 결국 문자집합을 변경하는 방법으로 구현하기는 어렵다고 생각했습니다.
  • 두 번째 방법으로 트리거를 생각했습니다. 트리거는 데이터베이스에서 제공하는 기능으로 삽입/삭제와 같은 이벤트 발생 시 할 일을 정의해 놓을 수 있습니다.
    • krtext 테이블에서 insert 이벤트 발생 시 트리거가 실행됩니다.
    • 각 행에 대해 정규식으로 완전한 한글로 구성되어 있는지 검사합니다.
    • 완전한 한글이 아니라면, 에러를 발생시켜 삽입 작업을 중단합니다.
DELIMITER $$

CREATE TRIGGER CheckValidKorean
BEFORE INSERT ON krtext
FOR EACH ROW
BEGIN
  IF NOT (NEW.txt REGEXP '^[가-힣]+$') THEN
    SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Invalid Korean characters';
  END IF;
END$$

DELIMITER ;
  • 삽입이 실패하고, 제가 정의한 에러가 발생한것을 확인할 수 있습니다.
mysql> insert into krtext (txt) values ('아');
Query OK, 1 row affected (0.01 sec)

mysql> insert into krtext (txt) values ('ㅇ');
ERROR 1644 (45000): Invalid Korean characters

UTF-8 VS eucKR

  • eucKR이 한글 전문적으로 지원하는 대표적인 문자집합이라고 해서 어떤 상황에 사용해야 할지 궁금해서 비교해보았습니다.
  • UTF-8:
    • 유니코드를 사용해 전 세계의 모든 문자를 표현할 수 있습니다.
    • 가변 길이로 인코딩되어 최소 1바이트, 최대 4바이트로 표현합니다.
    • 영문은 1바이트이고, 한글의 경우에는 3바이트입니다.
    • MySQL에서는 utf8mb4가 완전한 구현입니다. utf8을 사용하면 이모지와 같은 4바이트의 문자들을 표현할 수 없습니다.
  • eucKR:
    • 한글을 표현하기 위해 설계되었고, 한/영 이외의 문자 지원은 제한적입니다.
    • 2바이트의 고정 길이로 인코딩됩니다.
  • 결론:
    • 다국어 지원이 필요하면 UTF-8을 사용합니다.
    • 한글 처리에 특화된 서비스는 2바이트 저장 공간이 작은 eucKR을 사용할 만합니다.