자바 5 + 톰캣 6의 매개변수 파싱 오류

어제 오늘 시간을 많이 잡아먹고 해결한 문제가 있어서 기록으로 남겨볼까 한다.

요즘 내가 진행 중인 웹 프로젝트가 있는데 운영 환경이 자바 버전 5에 제우스를 사용하는 환경이다. 개발 환경도 그에 맞추는 게 맞으므로 자바 버전은 5를 사용하는데 서블릿 컨테이너는 별 문제가 없겠지 생각하고 톰캣(Tomcat) 버전 6을 사용하기로 했다. 원칙적으로 서블릿 버전, JSP 버전도 운영 환경에 맞춰서 선택해야겠지만 요즘엔 아파치 톰캣 사이트에 가보면 6 버전 미만은 창고에 쳐박힌지 오래다. (여기서 창고란 메뉴 항목 중 Archives, 즉 보관소를 말한다)

예전에는 자바 5에서 톰캣 5.5를 많이 사용했지만 이제는 자바 6 이상을 많이 사용하고 톰캣도 6 이상을 많이 사용하므로 자바 5 + 톰캣 6 조합은 사실 일반적이지 않은 조합이었다.

아무튼 이렇게 웹 개발 환경을 구성하고 웹사이트를 띄워보면 매개변수 값의 길이가 한 글자인 경우 request.getParameter가 빈 문자열을 반환하는 문제가 발견되었다. (발견하는 데도 시간이 좀 걸렸다.) 예를 들어 다음과 같이 서버측으로 폼을 넘기면

<form>
<input name="key1" value="1">
<input name="key2" value="12">
</form>

request.getParameter("key1")의 값은 빈 문자열 ""로 나오고 request.getParameter("key2")의 값은 "12"로 정상적으로 나온다.

영 이상한 현상이므로 이건 아무래도 뭔가 환경적인 문제일 것이라는 감이 왔다. 그래서 자바 소스 및 톰캣 소스를 활용해 디버깅을 시작했고 매개변수를 파싱하는 루틴, 특히 HTTP 데이터를 문자열로 디코딩하는 자바 5 런타임의 java.nio.charset.CharsetDecoder.decode(ByteBuffer in) 메서드에 문제가 있음을 알게 됐다. 다음은 그 소스다.

    public final CharBuffer decode(ByteBuffer in)
        throws CharacterCodingException {
	int n = (int)(in.remaining() * averageCharsPerByte());
	CharBuffer out = CharBuffer.allocate(n);

        if (n == 0)
            return out;

위에서 3행은 문자 당 평균 바이트 수 정보를 활용해 버퍼에 확보할 바이트 수를 구한다. 그런데 우리 프로젝트에서 사용하는 문자셋은 euc-kr인데 이 경우 averageCharsPerByte()는 0.5를 반환한다. 즉 euc-kr은 2바이트가 모여 한 글자를 만들기 때문이다. 그리고 in.remaining()은 파싱해야할 매개변수의 바이트 수를 나타내는데 한 글자인 경우 n = 1 x 0.5, 결국 0이 된다. 따라서 위에서 강조 표시한 6행으로 인해 매개변수 파싱이 중단되는 어이 없는 일이 일어난다.

자바 6에서는 이 문제가 수정되었는데 위 소스에서 나머지는 똑같고 6행이 다음과 같이 바뀌었다.

  1.         if ((n == 0) && (in.remaining() == 0))
  2.             return out;

자바에서 바이트 자료형을 문자열로 바꾸는 루틴은 여러가지가 있다. 그 중 위에서 본 CharsetDecoder.decode(ByteBuffer in) 메서드가 자바 5에서는 버그가 있었는데 톰캣 6이 이걸 사용하면서 결국 문제를 일으킨 것이다.

일단 해결은 톰캣을 자바 6에서 실행하는 것으로 처리했다. 그러나 컴파일은 5에서 하므로 별로 깔끔한 방법은 아니다. 원칙대로 운영 환경과 같게 톰캣 5.5를 사용하는 것이 좋을 것 같다.

오늘의 교훈은 검증되지 않은 것을 함부로 사용하지 말아야겠다는 것이다.

자바 5 + 톰캣 6의 매개변수 파싱 오류”에 대한 의견

  1. 오늘 이문제로 하루종일 고생했는데 어떻게 지금 진행중인 개발환경가 똑같네요…전 톰캣 5.5로 적용해봐야겠네요…

    1. 자바가 버전업이 계속 돼도 이런 버그가 있을 줄 몰랐죠. 이런 버그는 고치지 않는 건가 봅니다. 톰캣 5.5를 사용하신다니 그쪽 환경도 좀 된 곳인가 본데요. 성공 개발하세요~

      1. 톰캣5.5로 적용해도 문제가 발생하네요 다시 처리하신 방법으로 시도해봐야겠네요

          1. 축하드립니다. 환경 문제는 여러 가지를 들여다봐야 해서 참 골치 아파요.

        1. 자바 5에서 톰캣 5.5를 하는 경우인데 문제가 있나요? 몇 년전에는 이 조합이 일반적이었기 때문에 문제가 없었던 거 같은데요. 톰캣이 자바 5를 못찾는 건 아닌지요.

  2. 멋진 디버깅 실력이시네요..! 감사합니다.

    JDK 1.5.0_22를 사용하고 톰캣 5.5.35와 5.5.36은 동일한 오류가 발생했습니다.
    톰캣 5.5.26은 이 오류가 안나네요.

    하루동안 삽질했네요.. ㅜㅜ

    1. 에구, 미흡한 글 잘 봐주셔서 감사합니다.

      버전이 낮을 때 오류가 안 난다면 톰캣 개발자들이 오락가락했나 보네요. ㅎㅎ 성공 프로젝트하시길 바랍니다~

  3. 추측컨데 버그가 발생된 자바의 마이너 버전이 홀수 일듯 합니다.
    저도 이전에 같은 현상을 격고 마이너 버전을 짝수 버전으로 변경했었죠..
    관례적으로 홀수 버전에 테스트 및 새로운 시도들이 많이 들어가(짝수 버전에 비해) 홀수 버전은 테스트 버전으로 인식하는 경우도 많다고 합니다.(외국의 경우)
    그래서 전 자바와 톰켓은 짝수버전을 받아쓰곤 합니다.

    1. 의견 감사합니다. 상용 소프트웨어도 그런 게 있을 수 있군요. 어느 버전에서 발생했고 어느 버전에서 고쳐졌는지는 모르지만 그래서 자바가 버전업이 많은 게 아닐지… ^_^

의견 있으시면 냉큼 작성해주세요~