SMTP 제목 인코딩 오류 Troubleshooting

이슈

  • 유지보수하는 어플리케이션에서 메일서버 API를 사용하고 있었는데 어느날부터 메일 제목이 깨져서 발송되는 문제가 발생했다.
    이리저리 삽질 끝에 문제를 해결했는데 도움이 될까 남겨본다.

분석

1. SMTP FORMAT

발송된 메일의 제목을 확인해보니 아래와 같이 나왔다

1
=?UTF-8?B?7TAGM6Iqk5Yq2OSDHsydvOyeheuLuhC4=

SMTP의 subject format은 다음과 같다

1
=?[charset]?[인코딩타입]?[인코딩된 제목]?=

왜 디코딩이 안됐을까… 하고 한참 보다가 뒤에 물음표 빠진것 발견 ^^

2. eml Test

왜 빠진걸까?

일단 eml 파일을 만들어 테스트 해봤다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args) {
ImMessage message = null;

try{
Properties props = new Properties();
Session session = Session.getDefaultInstance(props);
message = new ImMessage( session );
message.setCharset("utf-8");
message.setFrom("", "timpac61@gmail.com", "utf-8");
message.setSubjectEncode("테스트 메일입니다.", "utf-8");
message.setPriority(3);
message.setHtml("테스트");
message.setRecipientsEx(javax.mail.Message.RecipientType.TO, "조영덕", "utf-8");

message.makeMimeFile("E:\\test.eml");
}catch(Exception ex){
ex.printStackTrace();
}
}

만들어진 eml 파일의 결과는 아래와 같다

1
2
3
4
5
6
7
From: <timpac61@gmail.com>
Subject: =?UTF-8?B?7YWM7Iqk7Yq4IOuplOydvOyeheuLiOuLpC4=
?=
Mime-Version: 1.0
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: base64
X-Priority: 3

Subject의 값이 ?(물음표) 앞에서 개행된 것을 확인할 수 있다. 아하~ 그래서 디코딩을 못했구만..
근데 어디서 바뀐거지? ㅡ.ㅡ

3. BASE64 Encoding Test

메일 API lib에서 subject를 만들어주는 부분을 찾아서 테스트 코드로 만들었다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void subjectEncodingTest() {
String subject = "테스트 메일입니다.";

try {
byte[] result = Base64.encodeBase64(subject.getBytes("UTF-8"), true);
String sTemp = new String(result);
System.out.println("base64 encoding: " + sTemp);

String sRet = "";
String[] arrResult = sTemp.split("\r\n");

for (int i = 0; i < arrResult.length; ++i)
if (i != 0)
sRet = sRet + "\r\n\t=?UTF-8?B?" + arrResult[i] + "?=";
else
sRet = sRet + "=?UTF-8?B?" + arrResult[i] + "?=";

System.out.println("subject format : " + sRet);

} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}

대충 제목을 BASE64로 인코딩한후 SMTP foramt에 맞게 만들어주는 코드다.
결과는 아래와 같이 나왔다

1
2
3
4
base64 encoding: 7YWM7Iqk7Yq4IOuplOydvOyeheuLiOuLpC4=

subject format: =?UTF-8?B?7YWM7Iqk7Yq4IOuplOydvOyeheuLiOuLpC4=
?=

역시나 물음표 앞이 개행됨
sTemp.split(“\r\n”); 구문 때문에 여기서 문제가 있을거 같다고 예상했으나… 아니였고
apache.commons.codec.Base64 클래스에서 인코딩 변환 중 개행문자가 붙어나왔다 Why??
우리의 아파치가 그럴 리 없어~ (무한신뢰)

4. Check Library

라이브러리 버전이나 충돌이 의심되어 클래스 로드를 확인해봤다

1
2
3
4
public void test() {
String path = org.apache.commons.codec.binary.Base64.class.getProtectionDomain().getCodeSource().getLocation().getPath();
System.out.println("Base64:" + path);
}

확인결과 ~~~~~~~
두둥~! 아파치 Commons.codec.jar 가 아닌 다른 jar 파일을 로드하고 있는걸 확인했다.
결제관련 라이브러리에 포함된 Base64 클래스였다.

잡았다 요놈잡았다 요놈~

해결

해당 결제 라이브러리 패키지에서 apache.commons.codec을 삭제하고 리패키징한 후 배포하니 깔끔하게 해결되었다. ^^