Spring과 Servlet 2.2/JSP 1.1 - 한글문제 넘어가기 프로그래밍

Spring MVC 1.2.x 를 Servlet 2.2/JSP 1.1 스펙의 WAS(Tomcat 3.x, iPlanet 6.0 이하 등..)에서 사용할 수 있을까?

있다.

내 생각에는 Spring 2.0 에서도 될 것 같다. Spring은 개발시 하위 호환성 유지에 철저히 신경을 쓴다. 정말 믿을 만한 녀석인 것이다.

* ContextLoaderListener대신 ContextLoaderServlet을 사용하면 된다. Listener는 Servlet 2.3 부터 도입된 것이다.

* 그러나 Spring MVC의 태그들은 지원되지만, JSTL은 JSP 1.2 부터 지원되는 것이기 때문에 사용할 수 없다. JSTL없는 Spring MVC의 태그는 무용지물에 가깝다.

* 이럴때는 Spring MVC를 컨트롤러로서 사용하고 VelocityFreeMarker를 뷰단에서 사용해야 한다.

위의 내용에 대한 언급은 Spring 공식 문서에서 본 기억은 없다.
포럼 글을 뒤지다 찾았다.

* 예상되는 또 다른 문제 : 한글 문제가 발생할 수 있다. Servlet Spec 2.2 에는 request.setCharacterEncoding()이 없다. Spring MVC에서 파라미터로 넘어 온 값을 아무 생각없이 객체에 매핑하면 한글이 모두 깨질 것으로 보인다. 나중에 테스트 해보고 추가 적으로 올려야 겠다.

참조 :
 - Spring MVC servlet specification compatibility
 - ContextLoaderServlet 사용 예제
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/sampleBanking-services.xml</param-value>
</context-param>

<servlet>
  <servlet-name>context<servlet-name>
  <servlet-class>
     org.springframework.web.context.ContextLoaderServlet
  </servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>
-

한글문제 넘어가기

Servlet Spec 2.2 에는 Filter와 HttpServletRequest.setCharacterEncoding() 메소드 부재로 인한 한글 처리 문제가 발생한다.
헌데, 모든 요청이 Spring의 org.springframework.web.servlet.DispatcherServlet를 통과해서 가기 때문에 이에 대해 별다른 고생없이 매끄러운 해결이 가능하다.

아래는 이 문제를 해결하기 위해서 새로 만든 DispatcherServlet이다. 원리는 간단하다. request 객체를 가로채어
getParameter() 수행시 한글 인코딩을 해서 값을 리턴하는 새로운 request 객체를 만들어 넘겨준다.
* DispatcherServlet4Servlet22.java
package springhelper.dispatcher;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.Enumeration;
import java.util.Locale;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.servlet.DispatcherServlet;

public class DispatcherServlet4Servlet22 extends DispatcherServlet {
    private static final long serialVersionUID = 1L;

    /** 파일 업로드 요청(Multipart Request)인지 확인 할 때 사용하는 문자열 */
    public static final String MULTIPART = "multipart";
    
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String encoding = getServletConfig().getInitParameter("encoding");
        
        HttpServletRequest wrappedRequest = null;
        
        if (request.getContentType() != null && request.getContentType().startsWith(MULTIPART)) {
            // multipart 요청일 경우에는 한글 처리를 하지 않고 넘긴다.
            // 이 경우 한글 인코딩은 Multipart 처리 라이브러리에서 지정한다.
            wrappedRequest = request;
        } else {
            wrappedRequest = new HttpServletRequestWrapper4Servlet22(request, encoding);
        }
        
        super.doDispatch(wrappedRequest, response);
    }
    
    /**
     * 한글 파라미터 처리를 마친 HttpServletRequest 객체
     * @author kwon37xi
     *
     */
    protected class HttpServletRequestWrapper4Servlet22 implements HttpServletRequest {
        
        private HttpServletRequest originalHttpServletRequest = null;

        private String paramEncoding = null;
        
        public HttpServletRequestWrapper4Servlet22(HttpServletRequest original, String paramEncoding) {
            this.originalHttpServletRequest = original;
            this.paramEncoding = paramEncoding;
        }
        
        public String getAuthType() {
            return originalHttpServletRequest.getAuthType();
        }

        public String getContextPath() {
            return originalHttpServletRequest.getContextPath();
        }

        public Cookie[] getCookies() {
            return originalHttpServletRequest.getCookies();
        }

        public long getDateHeader(String arg0) {
            return originalHttpServletRequest.getDateHeader(arg0);
        }

        public String getHeader(String arg0) {
            return originalHttpServletRequest.getHeader(arg0);
        }

        public Enumeration getHeaderNames() {
            return originalHttpServletRequest.getHeaderNames();
        }

        public Enumeration getHeaders(String arg0) {
            return originalHttpServletRequest.getHeaders(arg0);
        }

        public int getIntHeader(String arg0) {
            return originalHttpServletRequest.getIntHeader(arg0);
        }

        public String getMethod() {
            return originalHttpServletRequest.getMethod();
        }

        public String getPathInfo() {
            return originalHttpServletRequest.getPathInfo();
        }

        public String getPathTranslated() {
            return originalHttpServletRequest.getPathTranslated();
        }

        public String getQueryString() {
            return originalHttpServletRequest.getQueryString();
        }

        public String getRemoteUser() {
            return originalHttpServletRequest.getRemoteUser();
        }

        public String getRequestURI() {
            return originalHttpServletRequest.getRequestURI();
        }

        public String getRequestedSessionId() {
            return originalHttpServletRequest.getRequestedSessionId();
        }

        public String getServletPath() {
            return originalHttpServletRequest.getServletPath();
        }

        public HttpSession getSession() {
            return originalHttpServletRequest.getSession();
        }

        public HttpSession getSession(boolean arg0) {
            return originalHttpServletRequest.getSession(arg0);
        }

        public Principal getUserPrincipal() {
            return originalHttpServletRequest.getUserPrincipal();
        }

        public boolean isRequestedSessionIdFromCookie() {
            return originalHttpServletRequest.isRequestedSessionIdFromCookie();
        }

        public boolean isRequestedSessionIdFromURL() {
            return originalHttpServletRequest.isRequestedSessionIdFromURL();
        }

        /**
         * @deprecated
         */
        public boolean isRequestedSessionIdFromUrl() {
            return originalHttpServletRequest.isRequestedSessionIdFromUrl();
        }

        public boolean isRequestedSessionIdValid() {
            return originalHttpServletRequest.isRequestedSessionIdValid();
        }

        public boolean isUserInRole(String arg0) {
            return originalHttpServletRequest.isUserInRole(arg0);
        }

        public Object getAttribute(String arg0) {
            return originalHttpServletRequest.getAttribute(arg0);
        }

        public Enumeration getAttributeNames() {
            return originalHttpServletRequest.getAttributeNames();
        }

        public String getCharacterEncoding() {
            return originalHttpServletRequest.getCharacterEncoding();
        }

        public int getContentLength() {
            return originalHttpServletRequest.getContentLength();
        }

        public String getContentType() {
            return originalHttpServletRequest.getContentType();
        }

        public ServletInputStream getInputStream() throws IOException {
            return originalHttpServletRequest.getInputStream();
        }

        public Locale getLocale() {
            return originalHttpServletRequest.getLocale();
        }

        public Enumeration getLocales() {
            return originalHttpServletRequest.getLocales();
        }

        public String getParameter(String arg0) {
            String value = originalHttpServletRequest.getParameter(arg0);
            
            if (value != null) {
                try {
                    value = new String(value.getBytes("iso8859-1"), paramEncoding);
                } catch (UnsupportedEncodingException e) {
                    // 예외가 발생하면 예외  메시지를 값으로 설정한다.
                    // TODO 예외에 대한 처리 추가 필요.
                    value = e.getMessage();
                }
            }
            return value;
        }

        public Enumeration getParameterNames() {
            return originalHttpServletRequest.getParameterNames();
        }

        public String[] getParameterValues(String arg0) {
            String[] parameterValues = originalHttpServletRequest.getParameterValues(arg0);
            
            if (parameterValues != null) {
                for (int i = 0; i < parameterValues.length; i++) {
                    try {
                        parameterValues[i] = new String(parameterValues[i].getBytes("iso8859-1"), paramEncoding);
                    } catch (UnsupportedEncodingException e) {
                        // 예외가 발생하면 예외 메시지를 값으로 설정한다.
                        // TODO 예외에 대한 처리 추가 필요.
                        parameterValues[i] = e.getMessage();
                    }
                }
            }
            
            return parameterValues;
        }

        public String getProtocol() {
            return originalHttpServletRequest.getProtocol();
        }

        public BufferedReader getReader() throws IOException {
            return originalHttpServletRequest.getReader();
        }

        /**
         * @deprecated
         */
        public String getRealPath(String arg0) {
            return originalHttpServletRequest.getRealPath(arg0);
        }

        public String getRemoteAddr() {
            return originalHttpServletRequest.getRemoteAddr();
        }

        public String getRemoteHost() {
            return originalHttpServletRequest.getRemoteHost();
        }

        public RequestDispatcher getRequestDispatcher(String arg0) {
            return originalHttpServletRequest.getRequestDispatcher(arg0);
        }

        public String getScheme() {
            return originalHttpServletRequest.getScheme();
        }

        public String getServerName() {
            return originalHttpServletRequest.getServerName();
        }

        public int getServerPort() {
            return originalHttpServletRequest.getServerPort();
        }

        public boolean isSecure() {
            return originalHttpServletRequest.isSecure();
        }

        public void removeAttribute(String arg0) {
            originalHttpServletRequest.removeAttribute(arg0);
        }

        public void setAttribute(String arg0, Object arg1) {
            originalHttpServletRequest.setAttribute(arg0, arg1);
        }
    }
}

여기서 문제가 되는 점이 있는데, 파일 업로드를 처리하는 Multipart Resolver가 HttpServletRequestWrapper 라는 것을 사용한다. 이게 Servlet 2.3 부터 생긴 것인지라 Servlet 2.2에서는 클래스가 없다는 오류를 발생시킨다.
이것을 위해 Servlet 2.2 용 Multipart Resolver 를 만들던지 아니면, 파일 업로드 관련해서는 각 Controller에서 COSCommons File Upload 등을 직접 사용해서 처리하는 방식으로 해야한다.
일단은, COS나 Commons File Upload로 수작업으로 처리할 수 있도록 파일 업로드 관련 요청일 경우에는 request 객체 바꿔치기를 하지 않았다.
COS 등에 문자 인코딩 지정하는 기능이 있기 때문이다.

핑백

  • links for 2007-08-20 2007-08-20 23:22:14 #

    ... ags: springframwork file upload) 까먹지말자! : Spring과 Servlet 2.2/JSP 1.1 - 한글문제 넘어가기 springframework에서 한글문제를 해결할 수 있는 방 ... more

덧글

  • 아름프로 2006/08/01 18:30 # 삭제

    빨랑 테스트해서 알려주시게.. ㅎㅎㅎ
    spring의 사용여부도 그렇지만, 같이 써야할 넘(!)들마다 제대로 돌아갈지가 더 찜찜하네.
    jdk부터가 1.2 1.3 써야하기도 하고... (예전에 어떻게 개발했나 몰라.. ^^ )
※ 로그인 사용자만 덧글을 남길 수 있습니다.