SpringBoot에서 ServletConfig를 구현하는 방법을 알아보기 전에, 먼저 Servlet이 무엇인지 이해하고, web.xml을 사용하는 전통적인 방법과 Config.java로 구현하는 현대적인 방법 두 가지를 모두 살펴보겠습니다.
목차
1. Servlet이란 무엇인가?
Servlet은 Java 웹 애플리케이션의 핵심 컴포넌트로, 클라이언트의 HTTP 요청을 처리하고 응답을 생성하는 Java 클래스입니다.
1.1 Servlet의 특징
- 서버 사이드 컴포넌트: 웹 서버에서 실행되는 Java 프로그램
- 요청-응답 모델: HTTP 요청을 받아 처리하고 응답을 반환
- 멀티스레드: 서블릿 컨테이너가 여러 요청을 동시에 처리
- 생명주기 관리: 서블릿 컨테이너가 생성, 초기화, 실행, 소멸을 관리
1.2 Servlet 생명주기
1. 서블릿 컨테이너 시작
↓
2. init() 메서드 호출 (한 번만 실행)
↓
3. service() 메서드 호출 (요청마다 실행)
├── doGet()
├── doPost()
└── doPut(), doDelete() 등
↓
4. destroy() 메서드 호출 (서블릿 컨테이너 종료 시)
1.3 기본 Servlet 예제
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class MyServlet extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
// 초기화 로직
}
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("Hello Servlet!");
}
@Override
public void destroy() {
// 정리 로직
}
}
2. ServletConfig란?
ServletConfig는 서블릿의 초기화 파라미터와 서블릿 컨텍스트 정보를 제공하는 인터페이스입니다.
2.1 ServletConfig의 주요 메서드
getInitParameter(String name): 초기화 파라미터 값 가져오기getInitParameterNames(): 모든 초기화 파라미터 이름 가져오기getServletContext(): 서블릿 컨텍스트 가져오기getServletName(): 서블릿 이름 가져오기
ServletConfig는 각 서블릿 인스턴스마다 하나씩 존재하며, ServletContext는 웹 애플리케이션 전체에 하나만 존재합니다.3. SpringBoot 프로젝트 설정
먼저 SpringBoot 프로젝트에 필요한 의존성을 설정합니다.
3.1 build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.1.0'
id 'io.spring.dependency-management' version '1.1.0'
}
dependencies {
// Spring Boot
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// QueryDSL
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
implementation 'com.querydsl:querydsl-apt:5.0.0:jakarta'
// Servlet API (Jakarta EE)
compileOnly 'jakarta.servlet:jakarta.servlet-api:6.0.0'
// Database
runtimeOnly 'com.h2database:h2'
// Lombok
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
// QueryDSL Annotation Processor
annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta'
annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
annotationProcessor 'jakarta.annotation:jakarta.annotation-api'
}
javax.servlet 대신 jakarta.servlet을 사용합니다.4. web.xml을 사용한 Servlet 설정 (전통적 방법)
전통적인 Java 웹 애플리케이션에서는 web.xml 파일을 사용하여 서블릿을 등록하고 설정했습니다.
4.1 프로젝트 구조
src/main/
├── java/
│ └── com/example/
│ └── servlet/
│ └── CustomServlet.java
└── webapp/
└── WEB-INF/
└── web.xml
4.2 CustomServlet.java
package com.example.servlet;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class CustomServlet extends HttpServlet {
private String initParam;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
// web.xml에서 설정한 초기화 파라미터 가져오기
initParam = config.getInitParameter("customParam");
System.out.println("Servlet 초기화: " + initParam);
}
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h1>Custom Servlet</h1>");
out.println("<p>초기화 파라미터: " + initParam + "</p>");
out.println("<p>Servlet 이름: " + getServletConfig().getServletName() + "</p>");
out.println("</body></html>");
}
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
@Override
public void destroy() {
System.out.println("Servlet 종료");
}
}
4.3 web.xml 설정
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<display-name>SpringBoot Servlet App</display-name>
<!-- 서블릿 정의 -->
<servlet>
<servlet-name>customServlet</servlet-name>
<servlet-class>com.example.servlet.CustomServlet</servlet-class>
<!-- 초기화 파라미터 -->
<init-param>
<param-name>customParam</param-name>
<param-value>Hello from web.xml</param-value>
</init-param>
<!-- 로드 순서 (낮을수록 먼저 로드) -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 서블릿 매핑 -->
<servlet-mapping>
<servlet-name>customServlet</servlet-name>
<url-pattern>/custom/*</url-pattern>
</servlet-mapping>
<!-- 컨텍스트 파라미터 (전역 설정) -->
<context-param>
<param-name>appName</param-name>
<param-value>My SpringBoot App</param-value>
</context-param>
</web-app>
4.4 web.xml 주요 요소 설명
| 요소 | 설명 |
|---|---|
<servlet> |
서블릿 클래스를 정의 |
<servlet-mapping> |
URL 패턴과 서블릿을 연결 |
<init-param> |
서블릿 초기화 파라미터 설정 |
<load-on-startup> |
서버 시작 시 서블릿 로드 순서 |
<context-param> |
웹 애플리케이션 전체 파라미터 |
web.xml을 사용하지 않습니다. web.xml을 사용하려면 WAR 패키징이 필요합니다.5. Config.java를 사용한 Servlet 설정 (현대적 방법)
Spring Boot에서는 Java Config를 사용하여 서블릿을 등록하는 것이 권장됩니다. ServletRegistrationBean을 사용하여 서블릿을 등록할 수 있습니다.
5.1 ServletConfig.java (방법 1: ServletRegistrationBean 사용)
package com.example.config;
import com.example.servlet.CustomServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ServletConfig {
@Bean
public ServletRegistrationBean<CustomServlet> customServletRegistration() {
ServletRegistrationBean<CustomServlet> registration =
new ServletRegistrationBean<>(new CustomServlet(), "/custom/*");
// 초기화 파라미터 설정
Map<String, String> initParams = new HashMap<>();
initParams.put("customParam", "Hello from Config.java");
initParams.put("appVersion", "1.0.0");
registration.setInitParameters(initParams);
// 서블릿 이름 설정
registration.setName("customServlet");
// 로드 순서 설정 (낮을수록 먼저 로드)
registration.setLoadOnStartup(1);
return registration;
}
}
5.2 ServletConfig.java (방법 2: @WebServlet 어노테이션 사용)
서블릿 클래스에 @WebServlet 어노테이션을 사용할 수도 있습니다:
package com.example.servlet;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebInitParam;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet(
name = "customServlet",
urlPatterns = {"/custom/*", "/api/custom/*"},
initParams = {
@WebInitParam(name = "customParam", value = "Hello from @WebServlet"),
@WebInitParam(name = "appVersion", value = "1.0.0")
},
loadOnStartup = 1
)
public class CustomServlet extends HttpServlet {
private String initParam;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
initParam = config.getInitParameter("customParam");
System.out.println("Servlet 초기화: " + initParam);
}
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h1>Custom Servlet</h1>");
out.println("<p>초기화 파라미터: " + initParam + "</p>");
out.println("</body></html>");
}
}
@WebServlet을 사용하려면 메인 애플리케이션 클래스에 @ServletComponentScan을 추가해야 합니다.5.3 메인 애플리케이션 클래스에 @ServletComponentScan 추가
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
@ServletComponentScan(basePackages = "com.example.servlet")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
5.4 ServletRegistrationBean의 주요 메서드
| 메서드 | 설명 |
|---|---|
setUrlMappings() |
URL 패턴 설정 |
setInitParameters() |
초기화 파라미터 설정 |
setLoadOnStartup() |
로드 순서 설정 |
setName() |
서블릿 이름 설정 |
setEnabled() |
서블릿 활성화/비활성화 |
6. 실제 예제: 커스텀 Servlet 구현
QueryDSL과 함께 사용하는 실제 예제를 살펴보겠습니다.
6.1 FileDownloadServlet.java
package com.example.servlet;
import com.example.model.entity.FileEntity;
import com.example.repository.FileRepository;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Optional;
public class FileDownloadServlet extends HttpServlet {
@Autowired
private FileRepository fileRepository;
private String uploadDir;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
// Spring Bean 주입을 위한 설정
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
// 초기화 파라미터에서 업로드 디렉토리 가져오기
uploadDir = config.getInitParameter("uploadDir");
if (uploadDir == null) {
uploadDir = "./uploads";
}
}
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
String fileId = request.getParameter("id");
if (fileId == null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "File ID is required");
return;
}
try {
Long id = Long.parseLong(fileId);
Optional<FileEntity> fileEntityOpt = fileRepository.findById(id);
if (fileEntityOpt.isEmpty()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "File not found");
return;
}
FileEntity fileEntity = fileEntityOpt.get();
File file = new File(fileEntity.getFilePath());
if (!file.exists()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "File not found on disk");
return;
}
// 응답 헤더 설정
response.setContentType(fileEntity.getContentType());
response.setHeader("Content-Disposition",
"attachment; filename=\"" + fileEntity.getOriginalFileName() + "\"");
response.setContentLengthLong(file.length());
// 파일 전송
try (FileInputStream fis = new FileInputStream(file);
OutputStream os = response.getOutputStream()) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.flush();
}
} catch (NumberFormatException e) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid file ID");
} catch (Exception e) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Error downloading file: " + e.getMessage());
}
}
}
6.2 ServletConfig.java에 등록
package com.example.config;
import com.example.servlet.FileDownloadServlet;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ServletConfig {
@Value("${file.upload-dir:./uploads}")
private String uploadDir;
@Bean
public ServletRegistrationBean<FileDownloadServlet> fileDownloadServlet() {
ServletRegistrationBean<FileDownloadServlet> registration =
new ServletRegistrationBean<>(
new FileDownloadServlet(),
"/download/*"
);
// 초기화 파라미터 설정
Map<String, String> initParams = new HashMap<>();
initParams.put("uploadDir", uploadDir);
registration.setInitParameters(initParams);
registration.setName("fileDownloadServlet");
registration.setLoadOnStartup(1);
return registration;
}
}
6.3 사용 예제
# 파일 다운로드
curl http://localhost:8080/download?id=1
# 또는 브라우저에서
http://localhost:8080/download?id=1
7. Servlet vs Controller 비교
Spring Boot에서는 대부분의 경우 @RestController를 사용하는 것이 권장되지만, 특정 상황에서는 Servlet이 유용할 수 있습니다.
| 항목 | Servlet | @RestController |
|---|---|---|
| 사용 시기 | 낮은 레벨 제어 필요 시 | 대부분의 REST API |
| Spring 통합 | 수동 설정 필요 | 자동 통합 |
| 의존성 주입 | SpringBeanAutowiringSupport 필요 | 자동 주입 |
| 예외 처리 | 수동 처리 | @ExceptionHandler 사용 가능 |
| 성능 | 약간 빠름 (오버헤드 적음) | 약간 느림 (추가 기능) |
| 유지보수 | 복잡함 | 간단함 |
@RestController를 사용하고, 특별한 요구사항(예: 레거시 시스템 통합, 특정 성능 최적화)이 있을 때만 Servlet을 사용하세요.8. 주의사항 및 Best Practice
8.1 Spring Bean 주입 주의사항
Servlet은 일반적으로 Spring 컨테이너 밖에서 생성되므로, 의존성 주입을 위해서는 특별한 처리가 필요합니다:
// 방법 1: SpringBeanAutowiringSupport 사용
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
// 방법 2: ApplicationContext에서 직접 가져오기
@Autowired
private ApplicationContext applicationContext;
@Override
protected void doGet(...) {
FileRepository repository = applicationContext.getBean(FileRepository.class);
// ...
}
8.2 Best Practice
- 리소스 관리: 파일 스트림, 데이터베이스 연결 등을 try-with-resources로 관리
- 예외 처리: 적절한 HTTP 상태 코드 반환
- 보안: 파일 경로 검증, 권한 체크 등 보안 고려
- 로깅: 적절한 로깅으로 디버깅 용이하게
- 성능: 대용량 파일 전송 시 스트리밍 사용
8.3 web.xml vs Config.java 선택 가이드
| 상황 | 권장 방법 |
|---|---|
| Spring Boot JAR 패키징 | Config.java |
| Spring Boot WAR 패키징 | Config.java (권장) 또는 web.xml |
| 레거시 프로젝트 마이그레이션 | web.xml (임시), 점진적 마이그레이션 |
| 새로운 프로젝트 | Config.java 또는 @WebServlet |
- Servlet은 HTTP 요청을 처리하는 Java 컴포넌트
- ServletConfig는 서블릿 초기화 파라미터를 제공
- web.xml은 전통적인 설정 방법 (WAR 패키징 필요)
- Config.java는 현대적인 설정 방법 (권장)
- @WebServlet 어노테이션으로도 설정 가능
- Spring Bean 주입 시 SpringBeanAutowiringSupport 사용
- 대부분의 경우 @RestController 사용 권장
추가로 궁금한 점이 있으시면 댓글로 남겨주세요! 🚀

카카오톡 오픈채팅 링크
https://open.kakao.com/o/seCteX7h
참고 자료
'개발 > BACK' 카테고리의 다른 글
| Spring 환경에서 DB 접근 최소화 방법 정리 (0) | 2025.12.24 |
|---|---|
| Spring 환경에서 RSA / AES로 데이터 암호화 하기 - 예제 (0) | 2025.12.24 |
| SpringBoot 파일 전송 구현하기 (MVC 패턴 + QueryDSL) (0) | 2025.12.24 |
| WEB-INF 정적 리소스 경로 찾기 - 로컬환경과 배포환경의 차이점 (0) | 2025.12.24 |
| Linux vi / vim 명령어 총 정리 (0) | 2025.12.24 |