아래는 GPT 선생의 도움을 받아 임시방편으로 소스 상에서 디코딩하여 값을 넘길 때 만들었던 메소드이다. 아까워서 버리지 않고 여기에 남긴다.
// DB에 전송되는 파라미터 ISO-8859-1 문자 집합으로 변환
public static Object convertToISO88591(Object obj) throws UnsupportedEncodingException {
// 넘어온 객체에 선언된 필드부를 배열로 정렬
Field[] fields = obj.getClass().getDeclaredFields();
// 각 필드에 접근해서 저장된 값을 변환
for (Field field : fields) {
if (field.getType() == String.class) {
field.setAccessible(true);
try {
String originalValue = (String) field.get(obj);
if (originalValue != null) {
// UTF-8 문자열을 ISO-8859-1 바이트 배열로 변환하고 다시 문자열로 변환
byte[] isoBytes = originalValue.getBytes();
String convertedValue = new String(isoBytes, "ISO-8859-1");
field.set(obj, convertedValue);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return obj;
}
1. 사건 발생
예약 기능이 갑자기 안된다는 연락을 받았다.
운영 로그를 보니 예약을 할 때 아래와 같은 오류가 발생했다.
Cause: com.sybase.jdbc4.jdbc.SybSQLException: Error converting characters into server's character set. Some character(s) could not be converted.
서비스에서 예약과 관련된 DB는 sybase를 사용하고 있는데 call 프로시저를 호출할 때 ' mode = in '으로 넘기는 변수의 값들 중에 DB의 문자 집합으로 변환이 안되는 문자열이 포함되어 있다는 오류였다.
sybase는 기본적으로 ISO-8859-1을 문자 집합으로 사용하고 있는데 이는 서유럽권의 언어들을 지원하는 문자 집합이다. 하지만, 우리가 call 프로시저를 호출하며 넘기는 매개변수에는 한글 문자열이 필수적으로 포함이 되어 이 부분에서 오류가 발생했던 것이다.
그런데 이 기능은 예전부터 쭉 사용했던 기능인데 갑자기 안된다는 게 이해가 안 갔다. DB 쪽에서 무언가 설정을 바꿨거나 무슨 이유에서든 어떤 이슈가 발생해서 우리 쪽 서비스와 설정이 틀어진 듯 했다.
그래서 해당 전산팀에 문의를 했다. 바뀐 게 없단다. DB 쪽에서 설정을 바꾼 게 없고, 최근에 이슈가 될 만한 것도 없다는 답변을 받았다. '우리 쪽에서 뭔가 문제가 발생했구나'라고 생각했다.
2. 조치 시작
DB 클라이언트 툴을 켜서 해당 DB의 아무 테이블이나 조회해봤다. 한글이 다 깨져서 출력이 됐다. 인코딩 설정이 틀어진 것 같았다. 전산팀에 혹시 인코딩 설정이 어떻게 되냐고 문의했다. 인코딩 설정은 ISO-8859-1이고, 저장 형태는 EUC-KR이라고 답변을 해주셨다.
DB 클라이언트 툴의 문자 집합은 eucksc였다. 지원하는 문자 집합 리스트의 항목들로 하나씩 다 바꾸어서 적용해봐도 출력이 제대로 되지 않았다.
전산팀에서 DB 엔지니어 분이 한 번 적용해보라고 코드를 전달해 주셨다. 사실 잘하면 이때 해결이 가능했을 것이다. 전달해주신 코드를 정리하면 ' key : JAVA_CHARSET_MAPPING , value : ms949 '로 해당 키값과 벨류값을 DB에 접속하는 url에 붙여서 적용을 해보라는 말씀이었다. 그런데 나는 벨류값이 ms949만 보고 ' ?charset=ms949 ' 이렇게 붙여넣었다.
이렇게 적용하고 서버를 다시 돌리니 해당 문자 집합은 인식되지 않는 문자 집합이라는 오류가 발생했다. JDBC driver의 문제일 수도 있겠다는 생각에 maven repository에서 jconn4.jar의 최신 버전을 찾아서 라이브러리 폴더에 옮겨보았다. 그래도 개선이 되지 않았다.
3. 임시방편 조치
막막했다. 시간이 많이 흐르고 한글 문자열을 ISO-8859-1로 인코딩해서 넘겨야겠다고 생각했다. 처음에는 한글 문자열이 들어오는 부분만 new String("한글 문자열".getBytes("UTF-8"), "ISO-8859-1")로 처리해서 프로시저를 호출했더니 잘 처리가 되었다.
new String("한글 문자열".getBytes("UTF-8"), "ISO-8859-1")
=> 한글 문자열을 UTF-8로 인코딩 후 ISO-8859-1로 디코딩한 문자열
그런데 이렇게 하면서도 계속 찜찜한 마음이 들었다. 근본적으로 설정을 맞추지 않고 깨진 부분만 테이프로 붙이는 느낌이라 불안했다. call 프로시저를 호출하며 데이터를 넘길 때는 위와 같이 DB의 인코딩 설정대로 디코딩한 문자열을 같이 넘겼고, DB에서 값을 조회해서 받아올 때는 깨져서 넘어온 값(실제로 데이터 자체가 깨진 것은 아님)을 한글 문자열로 변환 후에 화면에 노출해줬다.
이렇게 임시방편으로 예약 기능과 조회, 변경 및 삭제 기능을 복구한 후에 한 주가 마무리 되었다. 12시가 다 되어 택시를 타고 퇴근을 하면서도 찜찜한 마음이 가시지 않았다.
4. 근본적인 해결의 실마리 발견
다음주가 되었다. 출근을 하니 역시나 예약과 관련된 다른 부분에서 한글이 깨져서 출력된다는 피드백을 받았다. 이렇게 깨지는 부분을 찾아서 하나하나 고치는 게 아닌 것 같다는 생각이 더 커졌다. 마침 책임님이 본사로 출근을 하셔서 고민 끝에 도움을 요청드렸다. 책임님께서도 이것저것 살펴보시더니 소스 상에 문제가 있는 게 아니고 DB 쪽에서 뭔가가 틀어진 것 같다고 말씀하셨다. 되던 기능이 아무것도 안 했는데 갑자기 이렇게 해당 DB와 관련된 부분만 통째로 틀어진다는 것은 우리 쪽에서 관련된 작업을 한 게 아니라면 DB 쪽 틀림없이 이슈가 있는 것이라고 하셨다.
다시 전산팀에 연락을 했다. DB 엔지니어 분과 이야기를 나누고 싶다고 말씀을 드렸고, 유선 상으로 연결이 되었다. 궁금한 것을 이것저것 여쭙고 답변을 듣다가 DB 버전업을 했다는 말을 들었다. 이전에 우리 쪽 DB 클라이언트 툴에서 데이터 조회 시에 한글이 깨져서 출력이 되어 전산팀에 문의를 했을 때 그 쪽에서는 데이터가 정상적으로 나온다고 했었다. 그 이유가 DB 버전업을 하면서 그 쪽은 사용하는 DB 클라이언트 툴에 해당 설정을 다 마친 상태였던 것이다.
우리 쪽은 새로운 버전과 환경이 맞지 않아서 오류가 발생했던 것이다. 그리고 저번에 전달해준 코드를 적용해도 안되냐고 하셔서 charset=ms949로 했는데 인식할 수 없는 문자 집합이라는 오류가 떴다고 말씀을 드렸더니 charset 말고 전달해준 키 값 그대로 적용을 해야된다고 하셨다. 이건 진짜 내가 싼 빅똥이었다.
그 이후에 코드를 제대로 적용하고 개발 환경에서 테스트를 해보니 이전처럼 잘 작동을 했다. 그래서 운영에 반영을 했는데 운영에서는 또 적용이 안되는 것이다. 개발과 운영의 properties 파일 내용이 다른가 해서 맞춰보고 이것저것 다 해보다가 개발 환경에서 JDBC driver를 최신 버전으로 교체했던 것이 떠올랐다. 운영에 해당 라이브러리를 적용하니 한 서비스는 해결이 되었다. (문제가 된 서비스는 2군데였다.)
5. 또 다른 오류 발생
그러나 나머지 하나에 문제가 생겼다. 이전 버전의 라이브러리에서는 잘 되던 곳이 최신 버전으로 바꾸니 오류가 발생했다.
Cause: java.lang.IndexOutOfBoundsException: Index: 1, Size: 1; nested exception is com.ibatis.common.jdbc.exception.NestedSQLException:
리스트 형식에서 주로 발생하는 오류였다. 해당 쿼리를 살펴보니 값이 String으로 단일 결과가 출력되는 쿼리였다. resultClass도 String으로 설정되어 있고 위의 오류가 날 수 없는 구조였다. 값을 리스트로도 받아보고, 맵으로도 받아보고 했는데 여전히 같은 오류가 발생했다.
한참을 또 살펴보다가 쿼리 안에 주석 처리 되어 있는 부분에 주목했다. 쿼리 안의 주석은 ' /* */ ' 주석이었다. 하지만 ctrl+shift+/을 눌렀을 때는 ' <!-- --> ' 주석이 출력되었다. 해당 주석을 지우고 다시 돌려보니 오류가 해결되었다. 이전 버전의 라이브러리에서는 문제가 됐던 주석이 주석으로 인식이 잘 되었는데 최신 버전의 라이브러리에서는 그게 주석으로 인식이 안됐던 모양이다.
6. 사건 해결 및 느낀점
이로써 근 2주간 날 괴롭혔던 요놈은 해결이 됐다. 지금은 임시방편으로 ISO-8859-1로 디코딩해서 처리했던 그 기간에 쌓인 데이터의 값의 한글 데이터가 뭔지 복원을 하는 작업을 하고 있다.
사실 이 문제는 2주씩이나 끌고 갈 문제는 아니었다. 내가 고집을 안 부리고 감이 안 잡혔을 때 바로바로 선배님들께 여쭤봤으면 금방 끝났을 것이다. 그리고 DB 담당팀에서 넘겨줬던 키 값을 임의로 charset이라고 안 하고 그대로 적용했으면 금방 해결이 됐을 수도 있다. (물론 라이브러리와 쿼리 문제가 남아있었겠지만..)
이번에 삽질도 많이 하면서 느낀 점들이 많다. 혼자 하는 일이 아니기 때문에 내 작업이 끝날 때까지 기다리는 분이 계신다. 내가 일을 질질 끌면 그 분들은 하염없이 기다리는 시간이 길어지는 것이다. 회사는 공부하는 곳이 아니기 때문에 업무는 빨리빨리 쳐낼 수 있어야 한다는 것을 이번 기회에 절실히 느꼈다. 많은 분들께 피해를 끼친 것 같아 죄송스러운 마음이 컸다.
'개발' 카테고리의 다른 글
HTTP에서 꼭 알아야 할 핵심 요소들에는 어떤 것들이 있을까 (0) | 2025.04.08 |
---|---|
왜 개발자는 HTTP를 알아야 할까 (0) | 2025.04.08 |
가비아 SSL 인증서 설치하기 (0) | 2024.04.29 |
MSSQL 함수 톺아보기(getdate(), dateadd(), convert(), LEFT()) (0) | 2024.04.02 |
웹 취약점 조치 방안 (0) | 2024.04.02 |