JAVA/JSP 39. 서블릿(Servlet) 작성 - JSP 없이 서블릿에서 바로 응답 출력하기, 한 번의 매핑으로 여러 가지 요청 처리하기, 서블릿 수명주기 메서드
*JAVA/JSP 35번 글의 시점부터는 노트북이 고장나는 바람에.. 다른 분의 글과 이전에 올렸던 글들을 참고하여, 다른 데스크탑의 윈도우 환경에 환경설정을 다시 진행한 후 작성하였습니다. 환경설정 관련 글들은 아래를 참고해 주세요.
- JDK11 설치 및 환경 변수(JAVA_HOME) 설정 - CLASSPATH는 추가하지 않음
자바(JAVA) 11 설치 및 환경설정 | 자바 11 환경 변수
자바로 코딩 공부를 하려면 개발 할 수 있는 환경을 만들어 주어야 한다. 그 순서를 먼저 작성하자면JAVA SE JDK 설치 -> JAVA 환경 변수 설정 -> IDE 설치 이렇게 볼 수 있겠다. 이 포스팅은 자바로 개
velog.io
- 톰캣 설치, 이클립스 설치 - 이 글을 쓰는 시점에서는 Tomcat 8버전이 없어, Tomcat 9버전 설치
JSP) JDK, 이클립스(eclipse), 톰캣 설치 및 환경 설정
1. JDK 설치 → Path 설정 2.eclipse JAVA EE버전 다운로드 3.웹컨테이너 톰캣 설치 →톰캣 환경설정 1. JDK 설치 - JAVA) 1. JDK 참고하여 설치하고 설정한다. -https://storyblogger.tistory.com/15?category=984696 2. 구글에
storyblogger.tistory.com
- 이클립스 JDK 버전 설정 - JDK11로 설정
[이클립스] JDK 버전 바꾸는 법
스프링을 사용하는 에러가 난다? 그러면 JRE 버전을 확인해보자! 프로젝트의 JRE System Library의 오른쪽을 살펴보면 [JavaSE-17]이라고 되어있다. 스프링이나 하이버네이트를 사용할 때, 자바 8이나 자
myvelop.tistory.com
- JDK 설치 및 환경 변수(JAVA_HOME) 설정, 톰캣 설치, 이클립스 설치 및 JDK 버전 설정 이후~ ⇒ 톰캣 버전과 jsp 파일 경로는 글 내용과 다르게 아래 이미지와 같음
JAVA/JSP 1. 개발 환경 구축 - 이클립스 기본 설정
*JDK 설치 및 환경 변수(JAVA_HOME) 설정, 톰캣 설치, 이클립스 설치를 완료한 상태OpenJDK 11: 자바 프로그램을 컴파일하고 실행해주는 기본 도구*이클립스 JDK-17이 설치되어있어서, https://coding-house.tisto
keep-programming-study.tistory.com
JAVA/JSP 1. 개발 환경 구축 - JSP 예제 테스트
1. 프로젝트 생성1) [File] → [New] → [Dynamic Web Project] 선택 2) 프로젝트 설정 화면 → Project name: HelloJSP, Target runtime: Apache Tomcat v9.0, Dynamic web module version: 4.0으로 설정 후 Next 클릭 3) 자바 소스 파일
keep-programming-study.tistory.com
JAVA/JSP 1. 개발 환경 구축 - 마지막 추가 설정
1. 외부 웹 브라우저로 실행하기-크롬이클립스는 웹 애플리케이션 실행 시, 자체 브라우저를 통해 실행 결과 출력웹 애플리케이션 배포 시 사용자는 크롬/파이어폭스 같은 전용 웹 브라우저로
keep-programming-study.tistory.com
- 오라클(Oracle Database 11gR2 Express Edition) 설치 및 설정
JAVA/JSP 14. 데이터베이스 - 특징, 오라클 설치(Oracle Database 11gR2 Express Edition), 사용자 계정 생성 및
1. 데이터베이스의 특징우리가 매일 PC나 스마트폰을 통해 접하는 거의 모든 웹 애플리케이션에서 사용함매일 업데이트되는 뉴스나 날씨 등의 정보는 데이터베이스가 없다면 클라이언트에 전달
keep-programming-study.tistory.com
JAVA/JSP 15. 데이터베이스 - 테이블 및 시퀀스 생성, JDBC 설정 및 데이터베이스 연결
*회원제 게시판 만들기-회원 인증 필요4. 테이블 및 시퀀스 생성1) 테이블 생성(1) member 테이블 - 아이디, 패스워드, 이름, 가입 날짜테이블 정의컬럼명데이터 타입 null 허용 키 기본값 설명idvarchar2
keep-programming-study.tistory.com
JAVA/JSP 16. 데이터베이스 - 커넥션 풀로 성능 개선, 간단한 쿼리 작성 및 실행
6. 커넥션 풀로 성능 개선웹은 클라이언트의 요청에 서버가 응답하는 구조→ Connection 객체 생성 때마다 네트워크 통신이 이뤄지며, 시간이 걸리는 작업들이 수반됨 == 시스템 성능에 큰 영향을
keep-programming-study.tistory.com
- 시퀀스 설정
JAVA/JSP 15. 데이터베이스 - 테이블 및 시퀀스 생성, JDBC 설정 및 데이터베이스 연결
*회원제 게시판 만들기-회원 인증 필요4. 테이블 및 시퀀스 생성1) 테이블 생성(1) member 테이블 - 아이디, 패스워드, 이름, 가입 날짜테이블 정의컬럼명데이터 타입 null 허용 키 기본값 설명idvarchar2
keep-programming-study.tistory.com
1. JSP 없이 서블릿에서 바로 응답 출력하기
- 요청 방식을 get 대신에 post를 사용
- 순수 데이터만 출력해야 하는 경우에만 사용
예제
- jsp 파일
// src/main/webapp/DirectServletPrint.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>DirectServletPrint.jsp</title>
</head>
<body>
<h2>web.xml에서 매핑 후 Servlet에서 직접 출력하기</h2>
<!-- post 방식으로 전송하기 위해 form 태그를 사용(요청명은 action 속성에 지정) -->
<form method="post" action="./DirectServletPrint.do">
<input type="submit" value="바로가기" />
</form>
</body>
</html>
- web.xml에 요청명과 서블릿 매핑
<!-- src/main/webapp/WEB-INF/web.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">
... 생략 ...
<servlet> <!-- 서블릿 등록 -->
<servlet-name>DirectServletPrint</servlet-name> <!-- 1. 서블릿 매핑을 위한 서블릿명 -->
<servlet-class>servlet.DirectServletPrint</servlet-class> <!-- 2. 요청을 처리할 서블릿을 패키지를 포함하여 명시 -->
</servlet>
<servlet-mapping> <!-- 서블릿과 요청명 매핑 -->
<servlet-name>DirectServletPrint</servlet-name> <!-- 3. 1과 동일한 서블릿명 -->
<url-pattern>/DirectServletPrint.do</url-pattern> <!-- 4. 컨텍스트 루트를 제외한 요청명 작성 -->
</servlet-mapping>
</web-app>
- servlet 패키지에 DirectServletPrint 클래스(서블릿) 생성
// Java Resources/src/servlet/DirectServletPrint.java
package servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class DirectServletPrint extends HttpServlet {
@Override
// 1. post 방식의 요청이므로 doPost() 메소드를 오버라이딩
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 2. 클라이언트에 응답하기 위해 response 내장 객체에 응답 콘텐츠 타입 지정
resp.setContentType("text/html;charset=UTF-8");
// 3. 웹 브라우저에 전송할 응답 결과를 쓰기 위해, response 내장 객체에서 PrintWriter 객체 얻어옴
PrintWriter writer = resp.getWriter();
// 4. PrintWriter의 println() 메서드로 응답 내용 출력
writer.println("<html>");
writer.println("<head><title>DirectServletPrint</title><head>");
writer.println("<body>");
writer.println("<h2>서블릿에서 직접 출력</h2>");
writer.println("<p>jsp로 포워드하지 않음</p>");
writer.println("</body>");
writer.println("</html>");
// 5. PrintWriter 객체를 닫아줌
writer.close();
}
}
Direct ServletPrint.jsp 실행
- 첫 화면에서 [바로가기] 버튼 클릭 시, 서블릿이 실행되며 화면이 아래 이미지처럼 변함
2. 한 번의 매핑으로 여러 가지 요청 처리하기
- 요청명이 추가되면 그에 따른 매핑도 함께 추가해야 한다는 불편함을 개선
예제
- JSP 파일
// src/main/webapp/onemapping/FrontController.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>FrontController.jsp</title>
</head>
<body>
<h3>한 번의 매핑으로 여러 가지 요청 처리하기</h3>
<!-- 1. 서블릿이 request 영역에 저장할 결괏값 -->
${ requestValue }
<ol>
<!-- 2. 클라이언트가 요청한 전체 경로 표시 -->
<li>URI: ${ uri }</li>
<!-- 3. 전체 경로에서 마지막의 xxx.one 부분을 추출할 문자열 -->
<li>요청명: ${ commandStr }</li>
</ol>
<ul>
<!-- 4. 각 페이지로의 바로가기 링크 -->
<li><a href="../onemapping/regist.one">회원가입</a></li>
<li><a href="../onemapping/login.one">로그인</a></li>
<li><a href="../onemapping/freeboard.one">자유게시판</a></li>
</ul>
</body>
</html>
- servlet 패키지에 FrontController 클래스(서블릿) 생성
// Java Resources/src/servlet/FrontController.java
package servlet;
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;
// 1. 와일드카드(*)를 사용하여 URL 패턴이 *.one에 해당하는 요청을 모두 이 서블릿과 매핑
@WebServlet("*.one")
public class FrontContoller extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 2. request 내장 객체로부터 현재 경로에서 호스트명을 제외한 나머지 부분을 알아냄
String uri = req.getRequestURI();
// 3. 마지막 슬래시(/)의 인덱스를 구함
int lastSlash = uri.lastIndexOf("/");
// 4. 3에서 구한 인덱스로 경로 마지막 부분의 문자열을 얻어옴
String commandStr = uri.substring(lastSlash);
// 5. 4의 문자열로 페이지를 구분하며, 각 페이지를 처리할 수 있는 메서드 호출
if (commandStr.equals("/regist.one"))
registFunc(req);
else if (commandStr.equals("/login.one"))
loginFunc(req);
else if (commandStr.equals("/freeboard.one"))
freeboardFunc(req);
// 6. uri와 페이지 구분을 위한 문자열(commandStr)을 request 영역에 저장
req.setAttribute("uri", uri);
req.setAttribute("commandStr", commandStr);
// 7. FrontController.jsp로 포워드
req.getRequestDispatcher("/onemapping/FrontController.jsp").forward(req, resp);
}
// 8. 페이지별 처리 메서드 - 각 페이지에 출력할 데이터를 request 영역에 저장
void registFunc(HttpServletRequest req) {
req.setAttribute("resultValue", "<h4>회원가입</h4>");
}
void loginFunc(HttpServletRequest req) {
req.setAttribute("resultValue", "<h4>로그인</h4>");
}
void freeboardFunc(HttpServletRequest req) {
req.setAttribute("resultValue", "<h4>자유게시판</h4>");
}
}
FrontController.jsp 실행
- [회원가입] 링크 클릭
- [로그인] 링크 클릭
- [자유게시판] 링크 클릭
3. 서블릿 수명주기 메서드
서블릿의 수명주기
- 서블릿 컨테이너가 담당: 클라이언트의 요청이 들어오면 서블릿에서 서블릿 객체를 생성하고 초기화한 후 요청 처리
-> 서블릿 객체 생성 후 각 단계마다 자동으로 특정 메서드를 호출하여 해당 단계에 필요한 기능 수행
=> 서버 종료 시 서블릿 객체 소멸 - 서블릿 수명주기 메서드: 서블릿 컨테이너가 호출하는 콜백 메서드들
@PostConstruct | 객체 생성 직후 init( ) 메서드를 호출하기 전에 호출 어노테이션을 사용하므로, 메서드명은 개발자가 설정 |
init( ) | 서블릿의 초기화 작업을 수행하기 위해 호출 최초 요청 시 딱 한 번 호출 |
service( ) | 클라이언트의 요청을 처리하기 위해 호출 전송 방식에 따라 doGet( ), doPost( ) 사용 |
destroy( ) | 서블릿이 새롭게 컴파일되거나, 서버가 종료될 때 호출 |
@PreDestroy | destroy( ) 메서드 실행 후, 컨테이너가 서블릿 객체를 제거하는 과정에 호출 어노테이션을 사용하므로, 메서드명은 개발자가 설정 |
서블릿 수명주기 메서드 동작 확인 예제
- JSP 파일
// src/main/webapp/cycle/LifeCycle.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>LifeCycle.jsp</title>
</head>
<body>
<script>
// 1. 폼값 전송 자바스크립트 함수: form 태그의 DOM 객체와 전송 방식을 인수로 받음
function requestAction(frm, met) {
// 2. 두번째 인수를 보고 전송 방식 결정
if (met == 1) {
frm.method = 'get';
} else {
frm.method = 'post';
}
// 3. 폼값 전송
frm.submit();
}
</script>
<h2>서블릿 수명주기(Life Cycle) 메서드</h2>
<!-- 4. form 태그 정의: action 속성을 제외한 나머지는 클릭 시 자바스크립트에서 설정 -->
<form action="./LifeCycle.do">
<input type="button" value="Get 방식 요청하기" onclick="requestAction(this.form, 1)" />
<input type="button" value="Post 방식 요청하기" onclick="requestAction(this.form, 2)" />
</form>
</body>
</html>
- 서블릿 클래스 작성(어노테이션 활용)
// Java Resources/src/servlet/LifeCycle.java
package servlet;
import java.io.IOException;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/cycle/LifeCycle.do")
public class LifeCycle extends HttpServlet {
@PostConstruct
public void myPostConstruct() {
System.out.println("myPostConstruct() 호출");
}
@Override
public void init() throws ServletException {
System.out.println("init() 호출");
}
@Override
public void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("service() 호출");
// 전송 방식을 확인해 doGet() 또는 doPost() 호출
super.service(req, resp);
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("doGet() 호출");
req.getRequestDispatcher("/cycle/LifeCycle.jsp").forward(req, resp);
}
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("doPost() 호출");
req.getRequestDispatcher("/cycle/LifeCycle.jsp").forward(req, resp);
}
@Override
public void destroy() {
System.out.println("destroy() 호출");
}
@PreDestroy
public void myPreDestroy() {
System.out.println("myPreDestroy() 호출");
}
}
LifeCycle.jsp 실행
- 첫 화면
- [Get 방식 요청하기] 클릭 시 이클립스의 console 뷰
- [Post 방식 요청하기] 클릭 시 이클립스의 console 뷰
- 톰캣 서버 종료 시 시 이클립스의 console 뷰