주요글: 도커 시작하기
반응형
레신 1.2.9, 레신 2.0.4에서의 한글 처리 문제를 원리적으로 해결하는 방법을 살펴본다.

레신 1.2.9와 한글

지난 1부에서는 톰캣의 각 버전(3.2, 3.3, 4.0)에서 파라미터를 읽어올 때 사용되는 캐릭터셋이 기본적으로 규약에 정의된 ISO-8859-1 이라는 것과 버전에 따라서 한글을 어떻게 처리해야 하는지에 대해서 살펴보았다. 이번 2부에서는 톰캣과 더불어 많이 사용되고 있는 레신에서의 한글 처리에 대해서 살펴보도록 하자.

참고적으로 이번 2부에서 테스트용으로 사용하는 코드는 지난 1부와 같으니, 소스 코드는 1부의 관련 링크를 참고하기 바란다.

레신 1.2.9는 1.2 버전대에서 가장 최근에 나온 것으로서 지원하는 서블릿 규약 버전은 2.2이다. 물론, JSP 규약은 서블릿 2.2와 한쌍을 이루는 1.1 버전을 지원하고 있다. 이 말은 레신 1.2.9는 기본적으로 읽어온 파라미터값을 ISO-8859-1 캐릭터셋으로 읽어온다는 것을 의미한다.

하지만, 레신 1.2.9는 완벽하게 규약을 지키고 있지 않으며, 개발자들의 편의를 위해 표준에 정의되어 있지 않은 기능을 제공하고 있다. 레신 1.2.9의 경우 response.setContentType() 메소드를 실행한 후에 request.getParameter() 메소드를 실행할 경우, response.setContentType()에서 지정한 캐릭터셋을 사용하여 파라미터의 값을 읽어오게 된다. 예를 들어, 테스트 코드 HanTestSetCharsetServlet.java의 경우 다음과 같이 클라이언트의 파라미터를 읽어오는데, 이 경우 파라미터를 setContentType()에서 지정한 EUC-KR 캐릭터셋을 사용하여 읽어오기 때문에 특별히 한글처리를 하지 않아도 한글이 올바르게 출력된다.

    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        response.setContentType("text/html; charset=euc-kr");        
        PrintWriter pw = new PrintWriter(response.getWriter());
        
        pw.println("<html><head><title>한글테스트</title></head>");
        pw.println("<body>");
        pw.println("파라미터1(캐릭터셋변환 X)="+
            request.getParameter("param1"));
        pw.println("<br>");
        pw.println("파라미터2(캐릭터셋변환 O)="+
            CharsetUtil.fromUStoKR(request.getParameter("param2")) );
        pw.println("</body></html>");
        pw.flush();
        pw.close();

        System.out.println("파라미터1(캐릭터셋변환 X)="+
            request.getParameter("param1"));
        System.out.println("파라미터2(캐릭터셋변환 O)="+
            CharsetUtil.fromUStoKR(request.getParameter("param2")) );
    }

웹브라우저에서 HanTestSetCharsetServlet에 "param1" 파라미터와 "param2" 파라미터의 값을 "한글"로 전송하면 다음과 같은 결과가 출력된다.


콘솔에는 다음과 같은 문자열이 출력된다.

    파라미터1(캐릭터셋변환 X)=한글
    파라미터2(캐릭터셋변환 O)=??

결과를 통해서 알 수 있듯이 특별히 캐릭터셋을 변환하지 않아도 한글이 올바르게 출력되는 것을 알 수 있다. 하지만, setContentType() 메소드를 사용하여 캐릭터셋을 지정하기 전에 파라미터값을 먼저 읽어올 경우에는 규약에 따라서 파라미터 값을 "ISO-8859-1" 캐릭터셋을 사용하여 읽어온다. 예를 들어, 다음과 같이 HantestSetCharsetServlet과 같은 기능을 수행하지만, 파라미터를 읽어오는 위치를 변경한 HantestSetCharset2Servlet이 있다고 해 보자.

    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        String param1 = request.getParameter("param1");
        String param2 = request.getParameter("param2");
        
        response.setContentType("text/html; charset=euc-kr");        
        PrintWriter pw = new PrintWriter(response.getWriter());
        
        pw.println("<html><head><title>한글테스트</title></head>");
        pw.println("<body>");
        pw.println("파라미터1(캐릭터셋변환 X)="+param1);
        pw.println("<br>");
        pw.println("파라미터2(캐릭터셋변환 O)="+CharsetUtil.fromUStoKR(param2) );
        pw.println("</body></html>");
        pw.flush();
        pw.close();

        System.out.println("파라미터1(캐릭터셋변환 X)="+
            request.getParameter("param1"));
        System.out.println("파라미터2(캐릭터셋변환 O)="+
            CharsetUtil.fromUStoKR(request.getParameter("param2")) );
    }

HantestSetCharset2Servlet을 웹브라우저에서 실행해보면 그 결과는 다음과 같다.


콘솔에 출력되는 결과는 다음과 같다.

    파라미터1(캐릭터셋변환 X)=??±?
    파라미터2(캐릭터셋변환 O)=한글

출력 결과를 통해서 알 수 있듯이 응답의 컨텐츠타입을 지정하기 이전에 파라미터를 읽어올 경우 규약에 따라 "ISO-8859-1" 캐릭터셋을 사용하여 파라미터값을 읽어오는 것을 알 수 있다.

이제 컨텐츠타입을 지정하지 않는 경우에는 어떻게 출력되는 지 살펴보도록 하자. 테스트 코드 중 컨텐츠 타입을 지정하지 않은 HanTestNoCharsetServlet을 웹브라우저에서 보면 그 결과는 다음과 같다.


콘솔에는 다음과 같이 출력된다.

    파라미터1(캐릭터셋변환 X)=??±?
    파라미터2(캐릭터셋변환 O)=한글

위 결과를 보면, 그림3의 경우 캐릭터셋 변환을 하지 않은 파라미터1이 올바르게 출력된 반면에 콘솔에는 캐릭터셋 변환을 한 파라미터2가 올바르게 출력된 것을 알 수 있다. 하지만, 그림3에서 파라미터1을 제외한 나머지 글자들은 모두 깨져서 출력되었다. 이러한 현상이 발생하는 이유는 컨텐츠 타입을 지정하지 않을 경우 서블릿은 기본적으로 "ISO-8859-1" 캐릭터셋으로 내용을 출력한다. 따라서 서블릿에서 특별한 처리를 하지 않는 한 한글은 모두 깨져 출력되고, "ISO-8859-1" 캐릭터셋으로 읽혀진 파라미터1의 경우는 깨지지 않고 그대로 출력되는 것이다.

이젠 JSP에서 파라미터의 캐릭터셋이 어떻게 처리되는 지 살펴보도록 하자. 먼저 page 디렉티브의 contentType을 "text/html; charset=euc-kr"로 지정한 hanTestSetCharset.jsp의 실행결과를 살펴보자.


콘솔에는 다음과 같이 출력된다.

    파라미터1(캐릭터셋변환 X)=한글
    파라미터2(캐릭터셋변환 O)=??

hanTestSetCharset.jsp의 출력결과가 HanTestSetCharsetServlet의 출력 결과와 같은 것을 알 수 있다. 즉, page 디렉티브의 contentType 속성값을 지정할 경우 contentType 속성값에서 지정한 캐릭터셋을 사용하여 파라미터값을 읽어온다.

이제 contentType 속성을 지정하지 않은 hanTestNoCharset.jsp의 출력결과를 살펴보자.


콘솔에는 다음과 같이 출력된다.

    파라미터1(캐릭터셋변환 X)=??±?
    파라미터2(캐릭터셋변환 O)=한글

얼핏 생각해보면 출력 결과가 HanTestNoCharsetServlet과 같을 것 같지만, 그림5를 통해서 그렇지 않다는 것을 알 수 있다. 레신1.2.9의 경우 JSP에 있는 내용이 모두 올바르게 출력되며 파라미터의 경우도 인코딩 변환을 하지 않은 경우에 웹브라우저에 알맞게 출력되고 있다. 하지만, 콘솔에 출력된 값을 보면 ISO-8859-1에서 EUC-KR로 캐릭터셋을 변환한 파라미터2가 올바르게 출력되는 것을 알 수 있는데, 이를 통해서 파라미터값 자체는 ISO-8859-1로 읽혀진다는 것을 알 수 있다.

레신 2.0.4와 한글

레신 2.0.4는 톰캣 4.0과 마찬가지로 서블릿2.3/JSP1.2 규약을 지원하는 콘테이너 중의 하나이다. 레신 2.0.4는 따라서 파라미터의 캐릭터셋을 지정할 수 있는 request.setCharacterEncoding() 메소드를 지원하고 있으며, JSP의 경우는 page 디렉티브의 pageEncoding 속성을 지원하고 있다.

먼저 테스트해볼 테스트 코드는 HanTestSetCharsetServlet 이다. 앞서 레신 1.2.9의 경우 파라미터값을 읽어오기 전에 response.setContentType() 메소드를 사용하여 응답 메시지의 캐릭터셋을 지정할 경우 파라미터값이 응답 메시지로 지정한 캐릭터셋과 같은 캐릭터셋을 사용하여 읽혀진다는 점을 기억해두자. 레신 2.0.4에서 HanTestSetCharsetServlet을 실행한 결과는 다음과 같다.


콘솔에는 다음과 같이 출력된다.

    파라미터1(캐릭터셋변환 X)=??±?
    파라미터2(캐릭터셋변환 O)=한글

그림5와 콘솔의 출력 결과를 보면 HanTestSetCharsetServlet의 실행 결과가 레신 1.2.9와 레신 2.0.4가 서로 다르다는 것을 알 수 있다. 레신 1.2.9의 경우 앞에서 말했다시피 자동으로 캐릭터셋을 변환해주는 (표준에 정의되어 있지 않은) 기능을 제공하고 있으나, 레신 2.0.4의 경우는 그러한 기능을 제공하고 있지 않는다는 것을 알 수 있다. 즉, 레신 2.0.4는 파라미터값을 기본적으로 "ISO-8859-1" 캐릭터셋으로 읽어오는 것이다. 이처럼 레신 2.0.4가 레신 1.2.9와 달리 자동으로 캐릭터셋을 변환해주는 기능을 제공하지 않는 이유는 레신 2.0.4가 서블릿2.3 규약을 지원하고 있고 서블릿 2.3 규약의 경우 request.getCharacterEncoding()을 통해서 파라미터값을 지정할 수 있으며 따라서 레신 2.0.4는 더 이상 파라미터값의 캐릭터셋을 자동으로 변환해줄 필요가 없어졌기 때문이다.

실제로 레신 2.0.4에서 request.setCharacterEncoding() 메소드를 사용하여 요청 메시지의 캐릭터셋을 EUC-KR로 지정하고 있는 HanTestSetCharset23Servlet을 실행하면 다음과 같은 결과가 출력된다.


출력 결과를 보면 캐릭터셋을 변환하지 않은 파라미터1이 올바르게 출력된 것을 알 수 있다. 콘솔에 출력되는 결과는 다음과 같은데, 그림7과 마찬가지로 캐릭터셋을 변환하지 않은 파라미터1이 올바르게 출력된 것을 알 수 있다.

    파라미터1(캐릭터셋변환 X)=한글
    파라미터2(캐릭터셋변환 O)=??

요청과 응답 모두 캐릭터셋을 지정하지 않는 HanTestNoCharsetServlet의 경우는 웹브라우저와 콘솔의 출력 결과가 모두 레신 1.2.9에서 HanTestNoCharsetServlet을 실행했을 때와 같다.

이제 JSP에서는 어떻게 처리되는 지 살펴보도록 하자. 먼저 page 디렉티브의 contentType 속성을 사용하여 캐릭터셋을 "EUC-KR"로 지정한 hanTestSetCharset.jsp의 출력 결과부터 살펴보자. 그 결과는 다음과 같다.


콘솔에는 다음과 같은 값이 출력된다.

    파라미터1(캐릭터셋변환 X)=한글
    파라미터2(캐릭터셋변환 O)=??

출력 결과를 보면 파라미터의 캐릭터셋을 변경하지 않은 경우에 알맞게 출력되는 것을 알 수 있다. hanTestSetCharset의 경우는 request.setCharacterEncoding() 메소드를 실행하지 않고 있기 때문에 규약에 따라 "ISO-8859-1" 캐릭터셋을 사용하여 파라미터 값을 읽어와야 하지만, 레신 2.0.4는 page 디렉티브의 contentType 속성에 지정한 캐릭터셋을 사용하여 파라미터의 값을 읽어오는 기능을 제공하고 있다. 즉, hanTestSetCharset.jsp는 page 디렉티브의 contentType 속성을 사용하여 응답 메시지의 캐릭터셋을 "EUC-KR"로 지정하고 있기 때문에 파라미터값이 자동으로 "EUC-KR" 캐릭터셋으로 읽혀지는 것이다.

반면에 page 디렉티브의 contentType을 지정하지 않은(즉, 기본 캐릭터셋인 "ISO-8859-1"이 사용되는) hanTestNoCharset.jsp는 다음과 같은 결과를 출력한다.


그림9를 보면 그림8과 다르게 출력되는 것을 알 수 있다. 콘솔에는 다음과 같은 내용이 출력된다.

    파라미터1(캐릭터셋변환 X)=??±?
    파라미터2(캐릭터셋변환 O)=한글

콘솔에 출력되는 내용을 통해서 우리는 JSP에서 contentType을 지정하지 않을 경우 파라미터가 ISO-8859-1 캐릭터셋으로 읽혀진다는 점을 알 수 있다. 비록, 그림9에서 파라미터가 캐릭터셋을 변환하지 않아도 올바르게 출력되고 있지만 이는 ISO-8859-1 캐릭터셋이 올바르게 출력되는 것이다.

page 디렉티브에 pageEncoding 속성을 사용하여 캐릭터셋을 지정한 hanTestSetCharset12.jsp의 경우는 아쉽게도 레신 2.0.4에서 올바르게 실행되지 않았다. 이는 레신 2.0.4의 JSP 엔진이 JSP 페이지를 서블릿 소스 코드로 변환하는 과정에서 올바르게 변환하지 못하기 때문인 것으로 생각된다. pageEncoding 속성의 테스트는 다음 버전에서 기대할 수 밖에 없을 것 같다.

모델2에서의 한글 처리

서블릿과 JSP가 같은 (또는 같은 값을 갖는) HTTP Request 객체를 공유하게 되는 모델 2 구조에서는 HTTP 요청을 받아오는 서블릿에서 파라미터값을 어떤 캐릭터셋으로 읽어오느냐가 중요하다. 즉, 서블릿에서 EUC-KR로 파라미터 값을 읽어오게 되면 JSP에서도 EUC-KR로 파라미터 값을 읽어오게 되며, 서블릿에서 ISO-8859-1로 파라미터 값을 읽어오면 JSP에서도 ISO-8859-1로 파라미터 값을 읽어오게 된다.

결론

총 2회에 걸쳐서 톰캣과 레신에서의 한글 처리 문제에 대해서 살펴보았다. 캐릭터 셋을 이렇게 저렇게 변환해보고 contentType을 이렇게 저렇게 지정하다 보면 해결되는 것이 한글 처리 문제이긴 하지만 자신이 사용하는 서블릿/JSP 콘테이너가 파라미터값을 어떻게 처리해주는 지 그리고 JSP 페이지를 서블릿으로 변환할 때 어떻게 캐릭터셋이 변환되는지 등을 이해한다면 좀더 논리적으로 한글이 깨지는 것을 방지할 수 있을 것이며, 더 이상 "레신에서는 이렇게 하면 된다"가 아닌 "레신은 이러한 특징 때문에 이렇게 해야 한다"라는 사고 방식을 통해서 한글 문제를 접근할 수 있게 될 것이다.

톰캣/레신 뿐만 아니라 JRun이나 WebLogic과 WebSphere와 같은 서버에서의 한글 처리도 이와 같이 근본적인 특징을 이해한다면 어렵지 않게 캐릭터셋으로부터 발생하는 문제들을 처리할 수 있을 것이다.

관련링크:

+ Recent posts