티스토리 뷰

Java

servlet mapping /와 /* 차이점

LichKing 2016. 12. 27. 10:05

프레임워크가 대중화되면서 공부목적이 아닌이상 프레임워크없이 쌩 서블릿으로 웹개발을 하는 경우는 거의 없어졌다. 대표적인 프레임워크로는 스프링 프레임워크가 있을텐데 이번에 설명하고자 하는건 프레임워크에 관련된건 아니고.. 어쨋든 이 프레임워크들도 서블릿 기반에서 구동되기때문에 서블릿 매핑이 1개 이상은 필요하다.


<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>

web.xml에 기술되는 대표적인 서블릿 매핑


일단 요청을 받는 1개 이상의 서블릿들을 생성하고 해당 서블릿이 다시 각각의 컨트롤러들에게 요청을 위임한다. 이때 이 서블릿을 프론트 컨트롤러(Front Controller)라고 부르는데 모든 요청을 받아줘야하기때문에 다수의 요청을 받을 수 있게 url pattern을 지정해야한다. 특정한 요청만 받게끔 하드코딩이 되어있으면 안된다는 뜻이다.


기존에는 확장자를 많이 사용했다. 많은 예제들과 책의 영향으로 *.action, *.do가 정석처럼 자리잡았고, 기업의 경우 기업명을 확장자로 이용하는 경우도 있다. 대표적으로 지금 네이버에 검색하면 url은 이런식으로 표기된다.


https://search.naver.com/search.naver?where=nexearch&query=%EA%B9%80%EC%A7%84%ED%83%9C&sm=top_lve&ie=utf8


어떤 주소가 넘어오든 신경쓰지않고 확장자만 특정하게 끝나면 되기때문에 이 설정은 크게 어려울것도 없고 신경쓸것도 없이 사용할 수있었다. 하지만 몇년전부터 url을 의미있게쓰자는 restful의 바람이 거세게 불면서 더 이상 무의미한 확장자를 쓰는건 죄악과 같고 구시대의 산물이라는 인식이 퍼지게되었다.


기존에 이미 확장자로 하드코딩되어있는 수많은 페이지코드들때문에 수정에 곤란함이 있을지언정 url pattern 자체를 변경하는건 크게 어려울것 같지않다(실제로도 어렵지않다.).

잠깐 생각해봐도 *.do로 모든 요청을 받았는데 확장자가 없어야한다니 /*로 바꾸면 될것같지 않은가?


<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>


변경하고 서버를 구동하자 그리고 테스트 jsp페이지가있다면 호출해보자. 사실 jsp를 직접 호출하는건 web.xml에 특별한 설정이 없이도 가능했던 일이지만 현재는 되지않는걸 확인했을것이다.


그 이유를 이해하려면 서블릿의 구동방식을 이해해야하는데 실제로 WEB-INF 폴더 외부에있는 jsp 에 접근한다고하더라도 해당 파일에 직접적으로 접근하는것은 아니다. 우리가 굳이 WAS라는 미들웨어를 두는건 WS가 해줄수없는 동적인 처리를 위함이다. 그런데 jsp 디렉토리에 직접 접근한다는 것은 뭔가 이상하다. jsp파일을 쌩으로 읽어와도 그걸 브라우저가 처리할수 없을것이다. 즉 jsp도 서블릿으로 변환 후 완성된 html을 서버가 전송하는것이고 이는 곧 확장자가 jsp인 서블릿 매핑이 있다는 것이다.


<servlet-mapping>
<servlet-name>jspServlet</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>


쉽게말해 이런 설정이 어딘가에 있어서 jsp 요청이 처리됐던건데 설정이 /*로 바뀌게 되면서 모든 요청을 블랙홀처럼 빨아들이니 저 설정이 작동을 못하게 된것이다. 그럼 저 설정은 어디있을까?

알고 계신분들도 있겠지만 web.xml은 애플리케이션에만 존재하는게 아니다. 기본적인 설정이 담겨있는 web.xml이 톰캣 conf 폴더에 존재한다. 해당 web.xml을 살펴보면..


<!-- The mapping for the default servlet -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

<!-- The mappings for the JSP servlet -->
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>


이런 설정이 나오게된다. 정리하면 저 설정을 다시 살려야하므로 /*로 매핑하는것은 적절치 못하다. 그러면 어떡하느냐? /로만 매핑을 하면 된다.


<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>


설정을 바꾸고 진행해보면 예전과 달리 잘 되는걸 볼 수 있을것이다. 그런데 한가지문제가 또 발생하게되는데 정적인 자료(js/css/html 등)에 대한 요청이다. 눈썰미가 있는 분들이라면 눈치챘을텐데 톰캣 web.xml설정을 잘 보면 jsp 서블릿만 있는게 아니라 지금 우리가 작성한 /로 지정되어있는 default 서블릿도 존재를 하고있다. 우리가 설정을 /*에서 /로 바꾼건 해당 서블릿을 덮어쓰게된것이다. 한가지 명심해야할점은 .jsp든 .js, .css든 WAS만 구동하는경우 해당 파일에 직접접근하는 경우는 없다는것이다. 일단 요청은 톰캣이 모두 받게된다. 그리고 톰캣은 서블릿에 패턴을 통해서 요청을 처리하게되는데 /로 지정하게되면 *.jsp로 오는 요청은 jsp 서블릿이 처리하겠지만 그 외에는 default 서블릿이처리를 해야한다는 것이다. 기본적으로 설정되어있는 default 서블릿은 정적인 자원을 처리할 수 있게끔 되어있으나 우리는 이 설정을 덮어써버렸으므로 default 서블릿이 작동하지않아 생긴 문제인 것이다.


이를 처리하는 코드를 넣어줘야하는데 서블릿 필터 등을 통해 정적인 요청은 default 서블릿으로 명시적으로 위임하거나 아님 직접 처리코드를 작성해봐도(?) 좋다.

스프링을 사용하는 경우라면


<mvc:resources mapping="/resources/**" location="/resources/"/>

혹은

<mvc:default-servlet-handler />


설정을 넣어주면 손쉽게 해결할 수 있다.

자바 설정을 사용하는 경우라면 WebMvcConfigureAdapter 클래스를 상속받고 configureDefaultServletHandling 메서드를 오버라이딩 해주면 된다.


@Configuration
@EnableWebMvc
public class WebAppConfig extends WebMvcConfigurerAdapter {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
configurer.enable();
}
}


댓글
  • 프로필사진 으v으 제가 저 문제로 이틀을 허비를 했습니다 온갖 구글링을 하며.. 그래도 해결을 못하고 진짜 힘들어하고 있었는데, 글 보자마자 바로 내가 찾던 거다!

    너무 정리를 잘 해놓으셔서 읽으면서 바로 이거다! 내가 찾던 거다!!! 했어요

    감사합니다. 앞으로 자주 오게 될 것 같아요 ㅎㅎㅎ

    이렇게 좋은 글 계속 앞으로도 많이 올려주세요~~~~~

    감사합니다.

    원래 이렇게 댓글 다는 사람아닌데 너무 감사해서 댓글 남기고 가요 ㅎㅎ
    2017.04.07 18:07
  • 프로필사진 LichKing 너무 감사해주셔서 제가 더 감사합나다 ㅎㅎ 종종 들러봐주세요 감사합니다~! 2017.04.09 13:28 신고
  • 프로필사진 고구먀 소스가 왜 이렇게 생겨먹은지 몰랐었는데, 덕분에 알고 갑니다. 감사합니다! 2017.11.03 13:20
댓글쓰기 폼