서블릿의 라이프사이클
- 브라우저가 톰캣에게 hello를 요청 했을 때 HelloServlet이 생성되고 객체를 생성해서 init을 실행해 init / destroy를 실행하고 service가 실행된다.
- 한 번의 사이클이 돌게 되면 더 이상 init과 destroy는 실행되지 않고 service만 실행된다.
- init과 destroy는 서블릿에서 딱 한 번 실행된다.
- service라는 메소드는 계속해서 실행된다.
- HTML 컨텐츠와 재요청 URL을 응답으로 보낸다.
- 싱글턴 객체 : 작업을 실행하는(클라이언트의 요청을 처리하는) 객체 / 프로그램 실행 중 한번 생성하면 계속 사용하는 객체(새로 만들지 않는다.)
JSP의 라이프사이클
- 서블릿의 동일한 과정에서 하나의 과정이 더 추가된다. (hello.jsp를 hello_jsp.java로 변환해서 컴파일하는 과정)
- hello.jsp를 hello_jsp.java로 변환하고 그 변환된 것을 다시 hello_jsp.class로 컴파일 한다.
- 그래서 서블릿과 달리 class와 객체에 _jsp~로 변경이 된다.
- 결국 hello_jsp.java가 서블릿 클래스가 된다.
- 그러나 _jsp는 사실 빈 객체이므로 하는 일은 적다.
- 싱글턴 객체 : 작업을 실행하는(클라이언트의 요청을 처리하는) 객체 / 프로그램 실행 중 한번 생성하면 계속 사용하는 객체(새로 만들지 않는다.)
- 서블릿은 바로 서블릿 파일이 생성되는 반면, JSP는 JSP가 생성되는 과정이 하나 더 생긴다.
프로토타입 객체 : vo객체와 같이 정보(데이터)를 담는 객체 / 여러 개를 만들어 사용하는 객체
톰캣
- 서블릿 /JSP 객체를 요청했을 때, 이 과정을 거쳐 객체를 가진다. 그리고 그렇게 만들어진 객체를 나중에 또 사용한다.
- 웹 애플리케이션 서버(Web Application Server:WAS)다.
- 서블릿/JSP 엔진이다. (서블릿/JSP를 실행시킨다.)
- 서블릿/JSP 컨테이너다. (서블릿/JSP를 생성,관리,유지,저장한다.)
Filter
com.sample.filter/SampleFilter.java
package com.sample.filter;
import java.io.IOException;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
@WebFilter("/*")
public class SampleFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("SampleFilter의 init()메소드 실행됨");
}
@Override
public void destroy() {
System.out.println("SampleFilter의 destroy()메소드 실행됨");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
System.out.println("SampleFilter의 doFilter() 메소드의 전처리 작업 수행됨");
filterChain.doFilter(request, response);
System.out.println("SampleFilter의 doFilter() 메소드의 후처리 작업 수행됨");
}
}
}
}
![](https://velog.velcdn.com/images/hcw0709/post/26e5044d-6d14-4dcd-b584-9dd9fd7cdf9a/image.png)
- Filter는 JSP나 서블릿 작업이 실행되기 전 혹은 후에 실행할 작업을 정의한다.
- 클라이언트의 요청이 들어오면 FirstFilter의 doFilter의 request로 전달되고 그 이후 chain.doFilter로 간다. 그리고 secondFilter에 가서 같은 업무를 실행하고 객체를 실행한다.
- 응답은 이와 반대로 진행되며 전처리 수행문에 가는 과정은 없고 후처리 수행문들을 거쳐 간다.
- Filter라는 객체가 생성되면 FilterConfig는 항상 같이 만들어진다.(톰캣이 만듦)
- FilterConfig에는 초기화 파라미터라는 값을 가지고 있다.
Filter를 실행 한 후 hello를 켜보았을 때
![](https://velog.velcdn.com/images/hcw0709/post/74eda546-98c8-46b6-8ca2-1288628a8318/image.png)
WEB-INF/web.xml
hello-servlet com.sample.servlet.HelloServlet email admin@sample.com tel 02) 1234-5678 hello-servlet /hello /hi /ye ``` ![](https://velog.velcdn.com/images/hcw0709/post/dab08c56-04a8-43d8-984d-726be0e15405/image.png) - 어떤 이름으로라도 접속이 가능해진다.초기화 파라미터
com.sample.servlet/HelloServlet.java
// String getInitParameter(String name) 메소드로 초기화 파라미터값을 조회한다.
String emailValue = config.getInitParameter("email");
String telValue = config.getInitParameter("tel");
out.println("<p>이메일 : "+ emailValue + "</p>");
out.println("<p>연락처 : "+ telValue + "</p>");
Filter로 배포하기
WEB-INF/web.xml <!-- 필터 배포하기 1. @WebFilter으로 배포하기
@WebFilter("/*")
public class FirstFilter implements Filter {
}
2. web.xml에서 <filter>, <filter-mapping>으로 배포하기
<filter>
<filter-name>별칭</filter-name>
<filter-class>필터클래스의 전체이름(패키지경로와 클래스명)</filter-class>
</filter>
<filter-mapping>
<filter-name>별칭</servlet-name>
<url-pattern>매핑할 URL</url-pattern>
</filter-mapping>
-->
<filter>
<filter-name>first</filter-name>
<filter-class>com.sample.filter.FirstFilter</filter-class>
</filter>
<filter>
<filter-name>second</filter-name>
<filter-class>com.sample.filter.SecondFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>first</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>second</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
com.sample.filter/FirstFilter
package com.sample.filter;
import java.io.IOException;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
public class FirstFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("FirstFilter의 init()메소드 실행됨");
}
@Override
public void destroy() {
System.out.println("FirstFilter의 destroy()메소드 실행됨");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
System.out.println("FirstFilter의 doFilter() 메소드의 전처리 작업 수행됨");
filterChain.doFilter(request, response);
System.out.println("FirstFilter의 doFilter() 메소드의 후처리 작업 수행됨");
}
}
com.sample.filter/SecondFilter
package com.sample.filter;
import java.io.IOException;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
//@WebFilter("/*")
public class SecondFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("SecondFilter의 init()메소드 실행됨");
}
@Override
public void destroy() {
System.out.println("SecondFilter의 destroy()메소드 실행됨");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
System.out.println("SecondFilter의 doFilter() 메소드의 전처리 작업 수행됨");
filterChain.doFilter(request, response);
System.out.println("SecondFilter의 doFilter() 메소드의 후처리 작업 수행됨");
}
}
![](https://velog.velcdn.com/images/hcw0709/post/3bc1be9f-eab3-4f92-8b61-2070eed13a79/image.png)
![](https://velog.velcdn.com/images/hcw0709/post/11491e6b-3fa1-416d-ac8d-7e25ad6f84f4/image.png)
WEB-INF/web.xml
secret-key 1a2cb5fde675caed3 ``` 를 추가하고com.sample.filter/SecondFilter.java
this.secretKey = filterConfig.getInitParameter("secret-key");
}
를 추가하고 실행하면
가 나온다.
그러나 이 secretKey는 SecondFilter에서만 사용 가능하다. 절대 FirstFilter에서는 사용 불가다.
WEB-INF/web.xml
<!--
웹 애플리케이션 전체에서 사용가능한 초기화 파라미터를 설정한다.
모든 서블릿/JSP/필터/리스너에서 사용가능하다.
<context-param/>으로 설정한 초기화파라미터 값은 ServletContext 객체의 초기화파라미터로 저장된다.
ServletContext는 웹 애플리케이션 프로젝트 당 하나 생성되는 객체다.
WAS가 켜질 때 ServletContext 객체가 생성되고, WAS가 종료될 때 ServletContext 객체는 폐기된다.
ServletContext 객체는 서블릿의 여러 객체 중에서 가장 오랫동안 유지되는 개체다.
ServletContext 객체는 속성과 초기화 파라미터를 저장할 수 있다.
ServletContext 객체에 저장된 속성과 초기화 파라미터는 모든 서블릿/JSP/필터/리스너에게 공유된다.
-->
<context-param>
<param-name>company-name</param-name>
<param-value>중앙HTA 학원</param-value>
</context-param>
HelloServlet.java
// 애플리케이션의 초기화 파라미터 정보를 제공하는 ServletConfig 객체를 획득한다.
ServletContext application = getServletContext();
// String getInitParameter(String name) 메소드로 초기화 파라미터값을 조회한다.
String companyNameValue = application.getInitParameter("company-name");
out.println("<p>회사명 : "+ companyNameValue + "</p>");
web-sample/sample-1.jsp
String companyName = application.getInitParameter("company-name");
<p>회사명 : <%=companyName %></p>
-JSP와 sevlet을 함께 사용할 수 있다.
첨부파일
SQL의 SAMPLE_BOARDS에 먼저 BOARD_FILE_NAME을 추가한다.com.sample.vo/Board.java
private String fileName; // 첨부파일이름을 추가
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
webapp/board/form.jsp
<p>제목, 작성자, 내용을 입력해서 새 게시글을 등록하세요</p>
<form class="bg-light border p-3" method="post" action="register.jsp" enctype="multilpart/form-data">
<div class="mb-3">
<label class="form-label">첨부파일</label>
<input type="file" class="form-control" name="attachedFile" />
</div>
MultipartRequest.java 파일을 util 패키지로 복사한다.
## 첨부파일 업로드하기
1. <form /> 속성에 속성 추가하기
<form method="post" enctype="multipart/form-data" action="register.jsp">
* method=post
post방식의 요청만 첨부파일 업로드를 지원한다.
* enctype="multipart/form-data"
enctype은 요청메세지의 바디부에 포함되는 데이터의 인코딩 방식을 지정하는 속성이다.
enctype="application/x-www-form-urlencoded"이다.
* <form />의 기본 enctype이다.
* 폼입력요소의 값을 url의 쿼리스트링과 같은 형식으로 변환해서 요청메세지의 바디부에 포함시킨다.
* 첨부파일 업로드가 없는 폼에서만 사용하는 인코딩 방식이다
* 요청헤더의 컨텐츠 타입이 아래와 같이 지정된다.
Content-Type: application/x-www-form-urlencoded
* 요청메세지의 바디부 예시)
title=연습&writer=홍길동&content=게시글 작성 연습입니다.
enctype="multipart/form-data"
* 첨부파일 업로드가 있는 폼에서 사용하는 인코딩 방식이다.
* 요청헤더의 컨텐츠 타입이 아래와 같이 지정된다.
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryoPqBs89onRcKRXQd
* 요청메세지의 바디부 예시)
----WebKitFormBoundaryoPqBs89onRcKRXQd
Content-Disposition: form-data; name="title"
파일업로드 연습
----WebKitFormBoundaryoPqBs89onRcKRXQd
Content-Disposition: form-data; name="writer"
홍길동
----WebKitFormBoundaryoPqBs89onRcKRXQd
Content-Disposition: form-data; name="content"
파일업로드 연습입니다. 연습입니다. 연습입니다.
----WebKitFormBoundaryoPqBs89onRcKRXQd
Content-Disposition: form-data; name="attachedFile"; filename="sample.png"
Content-Type: image/png
이미지파일 데이터..............
----WebKitFormBoundaryoPqBs89onRcKRXQd
//요청메세지의 바디부가 완전히 다르기 때문에 별도의 library가 필요하다.
2. <input type="file" /> 태그 추가하기
3. Multipart요청(첨부파일 업로드가 포함된 요청)을 처리하는 API 사용을 첨부파일을 처리하기
서블릿
jakarta.servlet.http.Part
주요 메소드
String getContentType()
업로드된 첨부파일의 컨텐츠 타입을 반환한다.
String getSubmittedFileName()
업로드된 첨부파일의 이름을 반환한다.
InputStream getInputStream()
업로드된 첨부파일을 읽어오는 객체를 반환한다.
long getSize()
업로드된 첨부파일의 사이즈를 반환한다.
* Part는 JSP에서는 사용불가, HttpServlet을 상속받은 사용자정의 서블릿 클래스에서만 사용가능하다.
사용자 정의 유틸 클래스
com.sample.MultipartRequest
생성자
MultipartRequest(HttpServletRequest request, String directory)
* 요청객체와 첨부파일을 저장할 디렉토리 경로를 전달받아서 MultipartRequest 객체를 초기화한다.
* 요청메세지를 분석한다.
* 첨부파일을 지정된 디렉토리에 저장한다.
* 폼입력값을 분석해 놓는다.
주요 메소드
String getParameter(String name)
String[] getParameterValues(String name)
String getFilename(String name)
Spring framework의 MultipartFile
주요 메소드
String getContentType()
업로드된 첨부파일의 컨텐츠 타입을 반환한다.
String getOriginalFileName()
업로드된 첨부파일의 이름을 반환한다.
InputStream getInputStream()
업로드된 첨부파일을 읽어오는 객체를 반환한다.
long getSize()
업로드된 첨부파일의 사이즈를 반환한다.
boolean isEmpty()
업로드된 첨부파일이 없으면 true를 반환한다.
byte[] getBytes()
업로드된 첨부파일 데이터를 반환한다.
register.jsp
// 요청객체에 저장된 요청파라미터값을 가져온다.
String title = request.getParameter("title");
String content = request.getParameter("content");
String filename = request.getParameter("attachedFile");
System.out.println("게시글 제목: " + title);
System.out.println("게시글 내용: " + content);
System.out.println("게시글 첨부파일명: " + filename);
이 나오게 된다.
그래서 파일이 업로드되는지 확인하기 위해서 app폴더 안의 web-workspace 폴더에 temp폴더를 생성해주고 아래의 코드를 작성하고
register.jsp
// 멀티파트 요청처리를 지원하는 MultipartRequest 객체를 생성한다.
MultipartRequest mr = new MultipartRequest(request, "C:\\app\\web-workspace\\temp"); // \\를 두개 사용한 이유는 오류 때문!()
// 요청객체에 저장된 요청파라미터값을 가져온다.
String title = mr.getParameter("title");
String content = mr.getParameter("content");
String filename = mr.getParameter("attachedFile");
실행하면
이와 같이 완성된 모습을 볼 수 있다.
이러한 과정은
"인터넷"을 통해 얻은 결과이다.
MultipartRequest.java
/**
* MultipartRequest 객체를 초기화한다.
* <p>enctype="multipart/form-data"요청을 처리하는 클래스다.
* <p>MultipartRequest객체를 생성하면 요청 메세지를 바디부에 저장된 폼 입력값, 첨부파일을 분석하고 처리한다.
* <p>MultipartRequest객체를 생성하면 업로드된 첨부파일은 지정된 디렉토리에 자동으로 저장된다.
*
* <p>폼 입력값은 MultipartRequest가 제공하는
* String getParameter(String name), String[] getParameterValues(String name)를 이용해서 조회할 수 있다.
* <p>첨부파일이름은 MultipartRequest가 제공하는
* String getFilename(String name)를 이용해서 조회할 수 있다.
* @param request 요청객체
* @param saveDirectory 첨부파일 저장디렉토리 경로
*/
public MultipartRequest(HttpServletRequest request, String saveDirectory) throws ServletException, IOException {
this.request = request;
this.directory = saveDirectory;
createParameterMap();
parseParameterMap();
}
register.jsp
// 조회된 요청파라미터값을 Board객체에 저장한다.
Board board = new Board();
board.setTitle(title);
board.setWriter(user.getId()); // 작성자에 로그인한 사용자의 아이디를 저장한다.
board.setContent(content);
board.setFileName(filename);
boards.xml
<insert id="insertBoard" parameterClass="com.sample.vo.Board">
insert into sample_boards
(board_no, board_title, board_writer, board_content, board_file_name)
values
(sample_boards_seq.nextval, #title#, #writer#, #content#, #fileName#)
</insert>
<select id="getBoardByNo" parameterClass="int" resultClass="com.sample.vo.Board">
select
board_no as no,
board_title as title,
board_writer as writer,
board_read_count as readCount,
board_review_count as reviewCount,
board_content as content,
board_deleted as deleted,
board_created_date as createdDate,
board_updated_date as updatedDate,
board_file_name as fileName
<update id="updateBoard" parameterClass="com.sample.vo.Board">
update
sample_boards
set
board_title = #title#,
board_writer = #writer#,
board_content = #content#,
board_read_count = #readCount#,
board_review_count = #reviewCount#,
board_deleted = #deleted#,
board_file_name = #fileName#,
board_updated_date = sysdate
파일의 이름이 DB에 추가된 것을 볼 수 있다.
detail.jsp
<tr>
<th>첨부파일</th>
<td colspan="3">
<%=board.getFileName() %>
</td>
</tr>
그런데,
파일이 없으면 null이 나온다.
그래서
detail.jsp
<tr>
<th>첨부파일</th>
<td colspan="3">
<%=board.getFileName() != null ? board.getFileName() : "없음" %>
</td>
</tr>
해주면
null이 아닌 "없음"이 나오게 된다.
다운로드 버튼 추가하기
detail.jsp <tr> <th>첨부파일</th> <td colspan="3"> <%=board.getFileName() != null ? board.getFileName() : "없음" %> <a href="" class="ms-5 btn btn-success" style="--bs-btn-padding-y: .25rem; --bs-btn-padding-x: .4rem; --bs-btn-font-size: .65rem;">다운로드</a> </td> </tr>
detail.jsp
<tr>
<th>첨부파일</th>
<td colspan="3">
<%=board.getFileName() != null ? board.getFileName() : "없음" %>
<%
if (board.getFileName() != null) {
%>
<a href="../download?no=<%=board.getNo() %>" class="ms-5 btn btn-success"
style="--bs-btn-padding-y: .25rem; --bs-btn-padding-x: .4rem; --bs-btn-font-size: .65rem;">다운로드</a>
<%
}
%>
</td>
</tr>
파일이 없다면 다운로드 버튼이 안 보이게 만든다.