자바 서블릿으로 이미지 업로드 기능을 구현하다 문제가 발생했다.
이미지 업로드 이후 해당 이미지를 요청할 경우 이미지를 찾지 못한다.
정확히 말하자면,
서버는 로컬에서 8080 포트를 사용한다.
테스트.jpg 이미지를 업로드하면 webapp/resources에 저장된다.
그렇다면 localhost:8080/resources/테스트.jpg로 접속하면 해당 이미지를 가져와야하지만 이미지를 찾지 못한다.
파일명이 영문으로 되어 있을 때는 문제 없이 불러와 진다.
영문은 문제가 없지만 한글일 경우 문제가 발생하는 것으로 보아 한글 인코딩과 관련된 문제로 짐작한다.
RFC 2616 표준에 따르면 URL은 ASCII로 인코딩된다.
URL에 ASCII로 표현할 수 없는 한글과 같은 문자는 ASCII로 나타낼 수 있는 문자로 인코딩한다.
따라서 위의 주소는 아래와 같이 인코딩되어 서버에 요청을 보낸다.
localhost:8080/resources/%E1%84%90%E1%85%A6%E1%84%89%E1%85%B3%E1%84%90%E1%85%B3.jpg
각 시스템마다 한글을 정규화하는 방식이 다르다.
리눅스와 윈도우의 경우 완성형 = NFC(Normalizaiton Form Canonical Compostion) 방식을 사용한다.
맥은 조합형 = NFD(Normalization Form Canonical Decomposition) 방식을 사용한다.
String NFD = "테스트";
String NFC = "테스트";
System.out.println(NFD + " -> " + URLEncoder.encode(NFD));
System.out.println(NFC + " -> " + URLEncoder.encode(NFC));
System.out.println(NFD.equals(NFC));
테스트라고 쓰여진 두 개의 변수가 있다.
각 리터럴은 NFD와 NFC로 만들어진 값이다.
각 변수를 URL 인코딩할 경우 다음과 같은 결과를 얻을 수 있다.
테스트 -> %E1%84%90%E1%85%A6%E1%84%89%E1%85%B3%E1%84%90%E1%85%B3
테스트 -> %ED%85%8C%EC%8A%A4%ED%8A%B8
false
인코딩하기 전에는 같은 글자로 보이지만 인코딩을 한 뒤에는 차이가 분명히 보인다.
NFC 방식으로 만든 localhost:8080/resources/테스트.jpg로 접속하면 해당 이미지를 잘 불러온다.
(URL인코딩된 주소: localhost:8080/resources/%ED%85%8C%EC%8A%A4%ED%8A%B8.jpg)
시스템 별로 정규화 방식이 다르니 앱에서 하나의 방식으로 통일해주도록 한다.
자바의 경우 정규화를 위한 Normalizer 클래스가 존재한다.
URI에는 가능한 한글을 사용하지 않는 것이 좋은 방법이다.
또 이미지 업로드와 같은 기능을 구현할 때, 사용자가 올린 파일명을 그대로 사용하면 파일명이 중복되는 등의 문제가 발생할 수 있다.
따라서 업로드된 파일을 저장할 때는 ASCII로 표현할 수 있는 문자를 임의로 조합해 파일명을 붙여주도록 하자.
(UUID를 사용하는 것도 하나의 방법이 될 수 있다.)