처음에 이클립스에서 Dynamic Web Project를 생성하고 나면, 파일 구조가 아래 사진과 같이 되어 있다.
src/main/java 폴더에는 서블릿이 들어 있어야 한다.
브라우저는 webapp 폴더에서부터 접근을 하는데, webapp는 정적 웹 프로그램이 들어 있는 곳이다.
html, css 등으로 이루어진 파일이 들어 있으며, 프로그램 기본 주소와 매칭된다.
webapp 하단의 WEB-INF는 웹 서버가 사용하는 파일이 들어있는 중요한 디렉토리이다.
따라서 외부에서 곧바로 접근할 수 없게 되어 있다.
그런데, 우리가 작성한 서블릿은 /WEB-INF/classes 디렉토리에 컴파일 된다.
서블릿 파일은 클라이언트에 서비스 하려고 만든 것인데, 클라이언트가 직접 접근할 수가 없다니!
👉 따라서 web.xml과 @WebServlet을 이용해서 서블릿 접근 경로를 지정하게 된다.
Web.xml
- 생성
web.xml은 위에 있는 사진에서 보듯이 WEB-INF 하단에 생성된다.
프로젝트 생성 시 web.xml을 만들지 않았다면, 당황할 필요가 없다.
위 사진처럼 프로젝트 우클릭 - Java EE Tools - Generate Deployement Descriptor Stub을 클릭하면 web.xml 파일이 생성이 된다.
- 역할
web.xml은 어떤 것을 배포할지 설명해놓은 DD(Deployment Descriptor) 파일이다.
톰캣이 web.xml 파일을 읽어서 매핑해서 인스턴스를 컨테이너 메모리 안에 생성한다.
쉽게 말하자면, web.xml은 톰캣의 실행환경에 대한 정보를 담당하는 환경 설정 파일이다.
servlet의 설정과 servlet 매핑, 필터, 인코딩 등을 담당한다.
- 태그 종류
- display-name: 웹 애플리케이션의 이름을 정의함
- context-param: 웹 애플리케이션의 컨텍스트 초기화 파라미터를 정의함. 웹 애플리케이션 전체에서 사용되는 설정 정보를 저장할 수 있음. DB 연결에 필요한 드라이버, 사용자 이름, 암호를 설정함
- param-name: 설정 정보(driver, url, username, password 등)
- param-value: 설정 정보의 값
- listener: 웹 애플리케이션에서 이벤트를 수신하고 처리하는 리스너를 정의함
- listener-class: 컨텍스트 로딩 시 실행될 클래스 정의 (패키지명부터 다 적어야 함)
- servlet: 서블릿을 정의함
- servlet-name: 서블릿의 이름
- servlet-class: 서블릿의 클래스 지정 (패키지명부터 다 적어야 함)
- load-on-startup: 서블릿을 언제 로드할지 지정. 1을 적으면 웹 애플리케이션이 시작될 때 로드
- servlet-mapping: <servlet> 태그로 등록한 서블릿을 실행 요청할 때 사용할 URI를 지정
- servlet-name: 실행할 서블릿 이름을 지정함. <servlet> 태그에서 등록한 서블릿 이름으로 지정해야 함. 이 정보를 가지고 <servlet> 태그를 찾아 매핑한 다음 실제 서블릿 클래스와 연결됨
- url-pattern: 서블릿을 실행할 때 사용할 URL을 지정함. 전체 URL 정보가 아니라, 'http:서버주소:포트번호/웹 애플리케이션 이름'까지는 생략하고 그 다음부터만 지정함
- resource-ref: 서버의 자원에 대한 참조를 설정함. DB와 관련된 리소스에 대한 참조 설정
- res-ref-name: 자원에 대한 참조명을 정의함. 웹 애플리케이션에서 자원에 접근할 때 사용됨. Java 코드나 설정 파일에서 해당 이름을 사용해 DB 커넥션 풀에 액세스 할 수 있음
- res-type: 자원의 타입을 정의함. DB와 관련된 자원을 참조하므로 javax.sql.DataSource를 사용함. DB 커넥션 풀을 참조한다는 의미
- res-auth: 자원에 대한 인증 방식을 정의함. 일반적으로는 Container를 사용함. 서버의 보안 시스템에서 자원에 대한 인증을 처리한다는 의미 👉 애플리케이션에서 별도의 인증을 수행할 필요 없이 서버의 보안 시스템을 이용해 자원에 접근할 수 있음
- filter: 필터를 정의함. 필터의 이름과 클래스를 지정함
- filter-name: 필터의 이름을 지정함
- filter-class: 필터 클래스의 전체 경로 지정함
- init-param: 필터 초기화 매개변수. 인코딩을 설정하기 위해 필터에게 전달되는 인코딩 값을 설정하고 있음
- filter-mapping: 필터를 URL 패턴에 매핑함. 필터가 특정 URL 패턴에 대한 요청을 처리할 수 있음
- filter-name: 패팅할 필터의 이름을 지정함. 앞서 선언한 필터의 이름과 동일해야 함
- url-pattern: 필터를 적용할 URL 패턴을 지정함
- welcome-file-list: 웹 애플리케이션의 홈페이지 파일을 정의함. 웹 브라우저가 요청하는 URL 경로에 대해 디렉토리에 있는 파일 중에서 가장 먼저 찾을 파일들을 <welcome-file>로 나열함. 만약 요청된 경로에 해당하는 파일이 없을 경우에는 여기에서 나열된 순서대로 찾게 됨
서블릿과 관련된 부분은 @WebServlet 어노테이션으로,
필터에 대한 부분은 @WebFilter 어노테이션으로 대체할 수 있다.
@WebServlet
- <servlet>, <servlet-mapping>을 @WebServlet으로 전환
<!-- 서블릿 선언(톰캣이 web.xml을 읽어서,
객체 생성 후 브라우저에서 매핑된 주소가 들어오면 이 객체를 호출한다.) -->
<servlet>
<servlet-name>Hello</servlet-name>
<servlet-class>study01.servlets.HelloWorld</servlet-class>
</servlet>
<!-- 주소와 매핑 -->
<servlet-mapping>
<servlet-name>Hello</servlet-name>
<url-pattern>/Hello</url-pattern>
</servlet-mapping>
package study01.servlets;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet 인터페이스(서블릿 생태주기 최상위) - 나머지도 모두 구현해야 한다. - service() GenericServlet 추상
* 클래스: implemented Servlet - service() - service()로 get/post 모두 전달된다.
* HttpServlet 추상 클래스(F3으로 확인 가능) extends GenericServlet - doGet()은 get 요청 처리 -
* doPost()는 post 요청 처리
*/
@WebServlet("/Hello")
@SuppressWarnings("serial")
public class HelloWorld extends HttpServlet {
@Override
public void init() throws ServletException {
System.out.println("init() 호출");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet() 호출");
}
@Override
public void destroy() {
System.out.println("destory() 호출");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("service() 호출");
super.service(req, resp); // 내부에서 doGet()과 doPost()로 분기
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost() 호출");
}
}
첫 번째 코드는 web.xml에서 사용한 필터 관련 코드이고,
두 번째 코드는 서블릿 클래스를 만들어 @WebServlet 어노테이션으로 위 내용을 바꾼 것이다.
servlet-mapping 태그 안에 있던 url-pattern이
@ WebServlet 어노테이션 안의 내용이 되고,
servlet 태그 안의 servlet-class에 적힌 클래스명이
HelloWorld 서블릿의 전체 클래스명인 것을 볼 수 있다.
- web.xml과 @WebServlet 비교
web.xml은 환경설정 파일이기 때문에 서비스 중에 수정이 가능하다는 유연성이 있다.
하지만, 생각보다 만들어진 서비스를 클라이언트가 이용하는 시간이 길지 않고,
위 코드로 비교만 해보아도 쉽게 알 수 있는 것이
web.xml에 일일이 설정을 해주는 것이 무척 번잡하다는 것이다.
따라서 최근에는 어노테이션을 많이 쓰는 추세이다.
- <filter>, <filter-mapping>을 @WebFilter로 전환
<!-- 필터 선언 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>spms.filters.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<!-- 필터 매핑 -->
<!-- 모든 브라우저의 요청에 대해서 UTF-8 전처리 작업을 하는 필터 매핑 -->
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
package spms.filters;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
@WebFilter(
urlPatterns="/*",
initParams = {
@WebInitParam(name="encoding", value="UTF-8")
}
)
public class CharacterEncodingFilter implements Filter {
FilterConfig config;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.config = filterConfig;
System.out.println("CharacterEncodingFileter::init 호출");
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain nextFilter)
throws IOException, ServletException {
// web.xml에 필터를 등록하고, 초기화 매개변수를 얻어옴
// 한글 깨짐을 방지하기 위해서
req.setCharacterEncoding(config.getInitParameter("encoding"));
System.out.println("CharacterEncodingFilter::doFilter() 호출");
// 다음 필터가 있으면 다음 필터에 전달되고, 없으면
nextFilter.doFilter(req, resp);
}
@Override
public void destroy() {
System.out.println("CharacterEncodingFilter::destroy() 호출");
}
}
첫 번째 코드는 web.xml에서 사용한 필터 관련 코드이고,
두 번째 코드는 filter 클래스를 만들어 @WebFilter 어노테이션으로 위 내용을 바꾼 것이다.
filter-mapping 태그 안에 있던 url-pattern이
@WebFilter 어노테이션 안의 urlPatterns가 되고,
filter 태그 안의 init-param에 있던 정보가
@WebFilter 안의 initParams에서 @WebInitParam 안의 내용이 된 것을 볼 수 있다.
- 어노테이션 사용시 주의점
같은 내용이 web.xml에 있는 경우 오류가 발생한다.
따라서 어노테이션으로 환경 설정을 해줄 때에는 web.xml에 있는 해당 정보를 삭제해야 한다.
참고
'Web > Servlet + JSP' 카테고리의 다른 글
[JSP] JSP를 이용해서 GET/POST 처리하기 (0) | 2024.03.31 |
---|---|
[Servlet + JSP] 자바 서버 사이드 프로그래밍 - 자바 웹 개발 워크북 (1) | 2024.03.29 |