ServletContextListener
1. Listener
이벤트 | 발생한 특정 사건(마우스 클릭, 키보드 입력, 클라이언트로부터의 HTTP 요청, 웹어플리케이션 시작, 웹어플리케이션 종료 등) |
이벤트 소스 | 이벤트가 발생한 대상(근원지)으로 마우스, 키보드, 웹어플리케이션(ServletContext) 등 |
리스너, 핸들러 | 이벤트가 발생되기를 기다렸다가 발생시 실행되는 메서드나 함수. 또는 메서드를 가진 객체 |
2. ServletContextListener
Servlet/JSP는 웹 어플리케이션을 개발하기 위한 기술이므로, 웹 환경에 관련된 이벤트들이 존재한다.
따라서 웹과 관련된 이벤트를 리스닝하고 처리할 수 있는 리스너를 구현해야 한다. Servlet/JSP에서는 웹 환경과 관련된 이벤트 리스너를 구현할 수 있도록 몇 가지 리스너 인터페이스를 제공하고 있다.
*Servlet / JSP 이벤트소스와 리스너 종류
이벤트 소스
|
이벤트 리스너
|
발생 이벤트 객체
|
설명
|
ServletContext
|
ServletContextListener
|
ServletContextEvent
|
웹어플리케이션의 시작, 종료 이벤트에 대한 이벤트 리스너
|
ServletContextAttributeListener
|
ServletContextAttributeEvent
|
ServletContext에 attribute를 추가하거나 제거, 수정됐을 때에 대한 이벤트 리스너
|
|
HttpSession
|
HttpSessionListener
|
HttpSessionEvent
|
HTTP 세션의 시작, 종료 이벤트에 대한 이벤트 리스너
|
HttpSessionAttributeListener
|
HttpSessionBindingEvent
|
HttpSession에 attribute를 추가하거나 제거, 수정됐을 때에 대한 이벤트 리스너
|
|
ServletRequest
|
ServletRequestListener
|
ServletRequestEvent
|
클라이언트로부터의 요청으로 인한 ServletRequest 생성과 응답 이후 ServletRequest 제거시에 대한 이벤트 리스너
|
ServletRequestAttributeListener
|
ServletRequestAttributeEvent
|
ServletRequest에 attribute를 추가하거나 제거, 수정됐을 때에 대한 이벤트 리스너
|
3. ServletContextListener 구현
웹 어플리케이션이 시작되고 종료될 때 특정한 기능을 실행하려면 다음과 같이 코드를 작성하면 된다.
1. javax.servlet.ServletContextListener 인터페이스를 구현한 클래스를 작성한다.
2. web.xml 파일에 1번에서 작성한 클래스를 등록한다.
init-param & context-param
특정 서블릿이 생성될 때 초기에 필요한 데이터들이 있다. 이러한 데이터들을 초기화 파라미터라고 한다.
init parameter는 그 매개변수가 선언된 서블릿에서만 사용할 수 있고 다른 서블릿은 참조할 수 없다. 여러 서블릿이 공통의 환경 정보를 사용한다면 context parameter를 사용하는게 좋다. context parameter는 같은 웹 어플리케이션의 서블릿들이 같이 공유할 수 있는 매개변수이다.
*초기화 작성하는 태그
<init-param> | 특정 서블릿에만 적용시킨다. |
<context-param> | 여러 서블릿에서 사용가능하다. |
*태그 안에 작성할 데이터
<param-name> | 데이터를 구분하는 이름 |
<param-value> | 초기화 데이터 값 |
<context-param>
<param-name>mySettingName</param-name>
<param-value>mySettingValue</param-value>
</context-param>
*태그별 호출 방식
<init-param> | getInitParameter( ) |
<context-praram> | getServletContext( ).getInitParameter( ) |
DataSource
1. 커넥션풀(ConnectionPool)
기존 데이터베이스 연동 방법은 애플리케이션에서 데이터베이스에 연결하는 과정에서 시간이 많이 걸리는데,
이 문제를 해결하기 위해 나온 방법이 커넥션풀(ConnectionPool)이다.
웹 애플리케이션이 실행됨과 동시에 연동할 데이터베이스와의 연결을 미리 설정해둔다.
그리고 필요할 때마다 미리 연결해 놓은 상태를 이용해 빠르게 데이터베이스와 연동하여 작업한다.
2. JNDI(Java Naming and Directory Interface)
웹 애플리케이션 실행 시 톰캣이 만들어 놓은 ConnectionPool 객체에 접근할 때는 JNDI를 이용한다.
즉, 미리 접근할 자원에 키를 지정한 후 애플리케이션이 실행 중일 때 이 키를 이용해 자원에 접근해서 작업한다.
*JNDI 사용예
-웹 브라우저에서 name/value 쌍으로 전송한 후 서블릿에서 getParameter(name)으로 값을 가져올 때
-해시맵(HashMap)이나 해시테이블(HashTable)에 키/값으로 저장한 후 키를 이용해 값을 가져올 때
3. DataSource
커넥션풀에는 여러개의 Connection 객체가 생성되어 운용되는데, 이를 직접 웹 애플리케이션에서 다루기 힘들기 때문에 DataSource라는 개념을 도입하여 사용한다.
DataSource는 커넥션 풀의 Connection을 관리하기 위한 객체로, JNDI Server를 통해서 이용된다.
DataSource 객체를 통해서 필요한 Connection을 획득, 반납 등의 작업을 한다.
*DataSource 사용방법
(1) JNDI Server에서 lookup( ) 메소드를 통해 DataSource 객체를 획득한다.
(2) DataSource 객체의 getConnection( ) 메소드를 통해서 Connection Pool에서 Free 상태의 Connection 객체를 획득한다.
(3) Connection 객체를 통한 DBMS 작업을 수행한다.
(4) 모든 작업이 끝나면 DataSource 객체를 통해서 Connection Pool에 Connection을 반납한다.
DataSource 연결
*MyContextListener.java
package web06;
import java.sql.Connection;
import java.sql.SQLException;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
public class MyContextListener implements ServletContextListener {
public static DataSource dataSource;
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("어플리케이션 시작시 발생하는 이벤트를 처리 가능합니다.");
System.out.println("어플리케이션 전역에서 사용할 설정을 미리 수행합니다.");
ServletContext context = sce.getServletContext();
String jdbcUrl = context.getInitParameter("jdbcUrl");
String jdbcClassname = context.getInitParameter("jdbcClassName");
String jdbcUser = context.getInitParameter("jdbcUser");
String jdbcPassword = context.getInitParameter("jdbcPassword");
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName(jdbcClassname);
ds.setUrl(jdbcUrl);
ds.setUsername(jdbcUser);
ds.setPassword(jdbcPassword);
dataSource = ds;
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
}
*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"
version="4.0">
<display-name>web06</display-name>
<listener>
<listener-class>web06.MyContextListener</listener-class>
</listener>
<context-param>
<param-name>jdbcUrl</param-name>
<param-value>jdbc:mysql://localhost:3306/my_db</param-value>
</context-param>
<context-param>
<param-name>jdbcClassName</param-name>
<param-value>com.mysql.cj.jdbc.Driver</param-value>
</context-param>
<context-param>
<param-name>jdbcUser</param-name>
<param-value>root</param-value>
</context-param>
<context-param>
<param-name>jdbcPassword</param-name>
<param-value>root</param-value>
</context-param>
</web-app>
*TestDB.jsp
<%@page import="java.sql.ResultSet"%>
<%@page import="java.sql.PreparedStatement"%>
<%@page import="java.sql.Connection"%>
<%@page import="web06.MyContextListener"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>데이터소스 연결 확인 페이지</title>
</head>
<body>
<%
try (Connection conn = MyContextListener.getConnection();
PreparedStatement stmt = conn.prepareStatement("SELECT 1");
ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
out.println(rs.getInt(1) == 1);
}
}
%>
</body>
</html>
Build Tool
빌드 툴을 소스 코드의 빌드 과정을 자동으로 처리해주며 외부 소스코드(외부 라이브러리)를 자동 추가 및 관리해주는 프로그램이다. 빌드 툴은 외부 라이브러리 의존성을 해결하기 위해서 사용한다.
빌드 툴에는 Ant, Maven, Gradle 이 있다.
*Maven vs Gradle
Maven | Gradle | |
특징 | - 프로젝트에 필요한 모든 종속성(Dependency)를 리스트의 형태로 Maven에게 알려서 종속성을 관리 - XML, Repository를 가져올 수 있음 ('Jar', 'Class Path'를 선언하면 자동으로 필요한 라이브러리 파일을 가져옴) |
- JVM 기반의 빌드도구 - Ant와 Maven의 단점을 보완 - 오픈소스 기반의 Builde 자동화 도구 - Groovy 기반 DSL로 작성 |
장단점 | - 라이브러리가 서로 종속할 경우 XML이 복잡함 - 계층적인 데이터를 표현하기에는 좋지만, 플로우나 조건부 상황을 표현하기 어려움 - 편리하나 맞춤화된 로직 실행이 어려움 |
- 프로젝트 시작시 설정에 드는 시간 절약 |
Maven
메이븐(Maven) 은 아파치에서 만든 프로젝트 관리 도구이다.
특히 Web Application 을 개발할 때 참조하고 있는 라이브러리들의 의존성 문제를 해결할 수 있기 때문에 많이 사용한다.
메이븐 프로젝트를 생성 및 설정하면 pom.xml이라는 파일이 생성되는데, 메이븐 프로젝트의 설정을 위해 존재하는 xml 파일이다.
프로젝트 우클릭 > Configure > Convert to Maven Project > Artifact 설정
Artifact 설정
*Group Id : 회사 패키지명 역순(영소문자)
*Artifact Id : 프로젝트 이름(영소문자)
*Version : 버전
*Packaging : 패키지 방식(war)
외부 라이브러리 사용
1. Apache Commons DBCP
https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2/2.9.0
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.9.0</version>
</dependency>
2. MySQL Connector Java
https://mvnrepository.com/artifact/mysql/mysql-connector-java/8.0.32
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
</dependency>
*pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>web06</groupId>
<artifactId>web06</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.9.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.3</version>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
</configuration>
</plugin>
</plugins>
</build>
</project>

logger
1. logging
프로그램을 개발하거나 운영할 때 생기는 문제점을 관리하고 모니터링할 수 있는 데이터를 말한다.
기록이란 뜻으로 프로그램이 제대로 동작하고 있는지 실행 기록을 남기는 것을 말한다.
2. console 기록 - System.out.println()
System.out.println()로 console 기록을 남기는 것은 기록을 남기는 형태 중 가장 느리다.
기록은 영구히 남기고자 한다면 파일이나 디비, 이메일 등 다른 형태로 다양하게 남기는 것이 좋으나 콘솔기록은 콘솔로 밖에 남기지 못한다. 또한, 메세지의 중요도를 알 수 있는 방법이 적다.
3. logger
중요도, 퍼포먼스, 다양한곳에 원하는만큼 기록으로 남기기 위해 외부라이브러리 logger를 사용하여 기록을 남긴다.
logger 라이브러리는 사용법이 다 다르기 때문에 다양한 logger를 사용하더라도 똑같은 방식 제공해 줄 수 있는 SLF4J 라이브러리를 추가로 같이 사용한다.
4. logging tool
log4j | 효율적인 메모리 관리로 그동안 많이 사용됨 |
logback | log4j 보다 성능이 더 우수해 최근 주로 사용 |
SLF4j | logback을 사용하기 위한 인터페이스 |
*Logback Classic Module » 1.3.5
https://mvnrepository.com/artifact/ch.qos.logback/logback-classic/1.3.5
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.3.5</version>
</dependency>
*SLF4J API Module
https://mvnrepository.com/artifact/org.slf4j/slf4j-api
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.6</version>
</dependency>
5. logger 생성
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
...
private static final Logger logger = LoggerFactory.getLogger(MyRequestListener.class);
6. Log Level(로그레벨)
TRACE > DEBUG > INFO > WARN > ERROR > FATAL
7. Logback Documentation
https://logback.qos.ch/documentation.html
Documentation
Logback documentation Below is a list of logback-related documentation currently available. Source code related documentation: Articles and Presentations In french
logback.qos.ch
로그 파일 작성하기
1. configuration 설정
2. appender(어디에 출력할 지)에서 콘솔에 출력되는 형식 지정
pattern 에서 지정한 방식대로 시간과 레벨 등의 설정이 되고난 후 콘솔에 메세지를 출력한다. 해당 append 설정은 STDOUT 이라는 변수명으로 저장해뒀다고 생각하면 된다.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
3. 로그를 저장할 방식을 지정
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder
by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp-
%msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>d:/myfolder/testFile.log</file> <!-- 파일 저장 경로 -->
<append>true</append>
<!-- set immediateFlush to false for much higher logging throughput -->
<immediateFlush>true</immediateFlush>
<!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder
by default -->
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n
<!-- 해당 패턴 네이밍으로 현재 로그 기록됨 -->
</pattern>
</encoder>
</appender>
<!-- 로그 레벨을 출력 -->
<root level="debug">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
</configuration>
4. 로그 파일 확인

logger들은 패키지의 계층구조 많이 따진다. 동작할 패키지를 명시할 수 있으며, 원하는 패키지만 숨길수 있다.
logger 라이브러리를 통해 세부적인 기록을 남기는 것이 가능하고 기록을 패키지별로 레벨별로 나누어서 원하는 만큼 원하는 장소에 출력을 할 수 있다.
FileAppender
https://logback.qos.ch/manual/appenders.html#FileAppender
Chapter 4: Appenders
There is so much to tell about the Western country in that day that it is hard to know where to start. One thing sets off a hundred others. The problem is to decide which one to tell first. —JOHN STEINBECK, East of Eden Chapter 4: Appenders What is an Ap
logback.qos.ch
파일 업로드를 위한 서블릿 구성
파일 업로드를 하기 위해서는 파일 업로드를 수행하는 서블릿을 가장 먼저 구성해야 한다.
서블릿을 구성하는 방법은 다음의 두 가지 어노테이션을 작성한다.
1. @WebServlet 어노테이션을 통해 서블릿 선언 및 URL 매핑을 수행
2. @MultipartConfig 어노테이션을 통해 서블릿이 파일 업로드 기능을 할 수 있도록 웹 컨테이너에 지시
@MultipartConfig 어노테이션의 속성은 다음과 같다.
속성명
|
설명
|
fileSizeThreshold
|
파일을 임시 디렉터리에 저장할 기준 크기 (단위: Bytes)
|
maxFileSize
|
한 번에 업로드 할 수 있는 파일 크기 제한 (단위: Bytes)
|
maxRequestSize
|
전체 요청의 크기 제한. 기본값은 무제한 (-1L) (단위: Bytes)
|
location
|
임시 파일을 저장할 웹 컨테이너 지정 (작성하지 않으면 서버가 기본 임시 디렉터리 설정)
|
파일 업로드


*fileupload.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>파일 업로드</title>
</head>
<body>
<form action="./file/upload" method="post" enctype="multipart/form-data">
<input type="file" name="upload" />
<input type="submit" />
</form>
</body>
</html>
*FileUploadServlet.java
package web06;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@WebServlet(urlPatterns = "/file/upload")
@MultipartConfig(location = "d:\\myfolder",
fileSizeThreshold = 1024 * 1024 * 5, maxFileSize = 1024 * 1024 * 50)
public class FileUploadServlet extends HttpServlet {
private static final Logger logger = LoggerFactory.getLogger(FileUploadServlet.class);
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Part part = req.getPart("upload");
logger.info(part.getName());
logger.info(part.getSubmittedFileName());
for (String name : part.getHeaderNames()) {
logger.info(name + ":" + part.getHeader(name));
}
String rootPath = getServletContext().getRealPath("");
logger.info("애플리케이션 root 경로 확인 : " + rootPath);
Path uploadPath = Paths.get(rootPath, "upload");
if (Files.exists(uploadPath)) {
Files.createDirectories(uploadPath);
}
Files.copy(part.getInputStream(), Paths.get("d:\\").resolve(part.getSubmittedFileName()),
StandardCopyOption.REPLACE_EXISTING);
}
}
요약
1. 연습
사용자에게 파일 업로드 양식을 제공하고 DB에 저장해보기.
2. 정리
(1) build tool (maven, gradle)
*build tool을 사용하는 목적?
*maven의 설정 파일 이름?
*maven 설정에서 외부 라이브러리 의존성 해결하는 법.
(2) logging tool (log4j, log4j2, logback, commons logging, juli ...)
*logging tool을 사용하는 목적?
*logging level, hierarchy?
*slf4j의 특징?