[Java] 파일 업로드 - MacOS 한글 자모음 분리

dondonee·2024년 1월 26일
0
post-thumbnail
post-custom-banner

MacOS 한글 자모음 분리

박매일 님의 스프 1탄을 들으면서 게시판을 만들어보고 있다.

지금은 프로필 사진을 업데이트하기 위한 파일 업로드 기능을 만드는 중이다. 유저가 폼(<input type="file">)을 통해 이미지를 업로드하면 멀티파트(MultipartRequest) 형식으로 서버에 보낸 뒤, 로컬 디렉토리에 이미지를 저장하고 데이터베이스에는 파일 이름만 저장해두는 방식이다.


문제

파일명 깨짐

그런데 파일명이 한글인 경우에는 지정한 디렉토리에 정상적으로 저장되었음에도 이미지를 찾아오지 못했다.


디버깅 모드로 돌려보니 MultipartRequest 객체가 생성됐을 때부터 자모음이 분리되 전달된 것을 확인할 수 있었다. (업로드 한 파일명은 사진.png이다.)

MySQL 콘솔에서 저장된 파일명(memProfile)의 길이를 확인하니 9로 저장이 되었다. DB에도 자모음이 분리되어 ㅅㅏㅈㅣㄴ.png로 저장된 것이다.

select char_length(memProfile) from mem_tbl where memName = '세균맨';
9

IDE 파일 인코딩 문제? (X)

인텔리제이의 파일 인코딩 문제인가 싶어 IDE 세팅을 확인해보았지만 이미 UTF-8로 설정이 되어 있었다.



원인

유니코드 정규화 차이

이처럼 한글 자모음이 분리되는 현상은 MacOS가 사용하는 유니코드 정규화 방식 때문이라고 한다.

  • 자소분리 현상이라고 하는 것 같다. (자소: 글자를 이루는 최소 단위)

한글 유니코드

유니코드는 전세계의 주요 문자들을 포괄하기 위해 제정된 표준이다.

현대 한글 표현의 경우 '가'(U+AC00)부터 '힣'(U+D7A3)까지 총 11,172자에 각각 고유한 코드를 부여하고 있다.

한글 글자들은 초성, 중성, 종성을 기준으로 순서가 정해져 있으며 특정한 규칙에 따라 코드가 부여되어 있다. 한글 음절의 코드 포인트 값은 시작 값인 U+AC00에 ((초성 값 x 21) + 중성 값) x 28 + 종성 값을 더하면 된다.


이러한 특징 덕분에 유니코드의 한글 글자는 '한' 처럼 완성형으로 다루거나 'ㅎㅏㄴ'과 같이 분리하여 조합형으로 다룰 수 있다.

  • NFC(Normalization Form Canonical Composition)
  • NFD(Normalization Form Canonical Decomposition)

텍스트 정규화(Normalizing Text)는 문자열의 검색이나 정렬을 위해 사용된다. 한글은 정규화 방식으로 NFC(완성형) 또는 NFD(조합형)을 사용할 수 있는데, 보편적으로 NFC 방식을 사용하지만 MacOS는 NFD 방식을 사용하기 때문에 자소분리 문제가 발생했다.



해결

Normalizer API

자바에서는 Normalizer라는 텍스트 정규화 인터페이스를 제공한다. 다음과 같이 DB에 파일명을 저장하기 전에 NFC 방식으로 정규화 시켜준 뒤 저장하니 문제가 해결되었다.

mvo.setMemProfile(Normalizer.normalize(newProfile, Normalizer.Form.NFC));
  • 참고) mvo는 업데이트를 실행하기 위해 인자로 보낼 VO 객체이다.

select char_length(memProfile) from mem_tbl where memName = '세균맨';
6

데이터베이스에서 파일명(memProfile)의 문자열 길이를 확인해 보았다. 이전에는 문자열 길이가 9(ㅅㅏㅈㅣㄴ.png)였지만 현재는 6(사진.png)인 것을 보아 완성형인 NFC 방식으로 잘 저장되었다.


브라우저에서 프로필 이미지도 정상적으로 보여진다.


참고

디버깅 모드로 확인해 보았다.

정확한 동작은 알 수 없지만 Normalizer 메서드가 호출되기 이전, MultipartRequest 객체를 생성할 때 이미 파일명이 NFC 형식(사진.png)으로 전달되었다.

multi = new MultipartRequest(request, savePath, fileMaxSize, "UTF-8", new DefaultFileRenamePolicy());

Normalizer 사용에 문제가 없고 동작을 정확히 알아야 할 부분은 아닌 것 같아서 패스했다.




🔗 References

post-custom-banner

0개의 댓글