1. Servlet, ServletContainer, ServletContext
1) Servlet
Servlet은 웹에서 들어오는 요청을 자바로 처리해서 동적인 웹 페이지를 만들어주는 기술입니다.
public class HelloServlet extends HttpServlet {
}
위와 같이 HttpServlet을 상속받아 구현됩니다.
Servlet은 ServletConainer에 의해 관리되며 init() -> service() -> destroy()의 라이프싸이클을 가집니다.
- init(): Servlet이 초기화(new)될 때 호출됩니다.
- service(): 클라이언트의 요청을 처리하는 메서드입니다. 분기에 따라 doGet(), doPost() 등을 실행합니다.
- destroy(): Servlet이 종료될 때 호출됩니다.
2) ServletConainer
ServletContainer는 서블릿을 관리하고, HTTP 요청/응답을 처리하는 환경을 제공합니다. Tomcat이 서블릿 컨테이너의 역할을 합니다. Tomcat은 이외에도 설정 정보 제공, 동시 요청에 대한 멀티스레드 처리 등을 담당합니다.
3) ServletContext
ServletContext는 애플리케이션 전체에서 공유되는 전역 저장소로, 서블릿 간 데이터 공유, 리소스 접근, 초기화 파라미터 조회 등의 기능을 제공합니다.(이후에 설명할 Spring과 연계해서 사용되기 때문에 기억해 두는 것이 좋습니다.)
ServletContext는 ServletContainer가 웹 애플리케이션을 시작할 때 하나만 생성되며, 모든 서블릿이 이 객체를 통해 전역 정보를 주고받을 수 있습니다.
ServletContext servletContext = getServletContext();
//전역에 제공할 데이터 추가
servletContext.setAttribute("name", "user1");
//다른 servlet 객체에서 이 데이터 접근 가능
servletContext.getAttribute("name");
결과적으로, Servlet을 이용한 웹 아키텍처는 다음과 같습니다.
사용자가 웹서버에 정적 리소스를 요청하면 직접 응답하고, 동적 처리가 필요한 요청은 ServletContainer로 전달됩니다.
ServletContainer는 URL에 매핑된 Servlet 클래스를 찾고, 인스턴스가 없으면 생성 후 init()을 호출합니다. 이후 매 요청마다 service() 메서드를 호출하여 요청을 처리합니다.
4) Controller-Model-View
위의 아키텍처에는 요청 URL마다 Servlet 클래스가 매핑되어 실행되는 구조입니다. 이는 다음과 같은 문제점들이 있습니다.
- 요청 URL마다 Servlet 클래스 필요 (/user, /product, /order → 각각 UserServlet, ProductServlet, OrderServlet)
- 공통 로직(인증, 로깅, 예외처리)이 모든 Servlet에 중복 구현
- 비즈니스 로직, 데이터 처리, 화면 출력 코드가 뒤섞여 코드의 유지보수, 가독성 안좋음
따라서 이를 해결하기 위해서 FrontController 패턴 적용과 관심사 별로 클래스를 분리합니다.
- FrontController: 모든 요청을 하나의 진입점에서 받고, 적절한 Controller에 매핑
- Controller: 요청/응답 처리, 비즈니스 로직 호출
- Model: 데이터 처리, 비즈니스 로직 (Service, Repository)
- View: 화면 렌더링 (JSP, Thymeleaf)
2. Spring Framework
1) IoC/DI
객체를 생성하는 방법은 new를 사용하는 것입니다. 하지만 이처럼 개발자가 직접 객체를 생성하면 다음과 같은 문제가 발생합니다.
- 강한 결합 문제
public class UserService {
private UserRepository userRepository;
public UserService() {
// 직접 구체적인 클래스에 의존
this.userRepository = new MySQLUserRepository();
}
public User findUser(Long id) {
return userRepository.findById(id);
}
}
UserService가 MySQLUserRepository의 구현체에 직접 의존하면서 강한 결합이 발생합니다. 이로 인해 저장소 구현체(MySQL → Oracle 등)를 변경하려면 UserService의 내부 코드를 수정해야 하며, 테스트 시에도 실제 DB와의 연결 없이 단위 테스트를 수행하기 어렵습니다.
- 객체 관리 복잡 문제
public class Application {
public static void main(String[] args) {
// 개발자가 직접 객체 생성 순서와 의존성 결정
UserRepository userRepository = new MySQLUserRepository();
UserService userService = new UserService();
userService.setUserRepository(userRepository);
UserController userController = new UserController();
userController.setUserService(userService);
}
}
위와 같이 객체를 직접 생성하고 관리하는 방식은, 애플리케이션의 규모가 커지고 객체 간의 관계가 복잡해질수록 생성 순서와 의존성 관리가 점점 더 어려워집니다.
따라서 이러한 문제를 해결하기 위해 IoC(제어의 역전)를 사용합니다. IoC는 객체 생성 및 의존성 설정의 제어권을 개발자가 아닌 프레임워크(Spring)가 담당하도록 위임하는 방식입니다.
Spring에서는 Spring Container가 애플리케이션에 필요한 객체들을 생성·등록·관리하고, 이들 간의 의존성을 자동으로 주입(DI)하며, 객체의 생명주기까지 관리합니다. 이 역할을 하는 것이 ApplicationContext입니다.
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);
이때 Bean이라는 용어를 사용합니다. Bean이란 Spring이 생성하고 관리하는 객체를 말합니다.Spring Container에 Bean을 등록한다는 것은 객체의 생성과 관리를 Spring에 위임하는 것으로, 개발자가 직접 new 키워드로 객체를 생성하는 대신 Spring이 이를 대신 처리해준다는 의미입니다. Bean을 등록하는 방법은 다음의 3가지 방식이 있습니다.
- 컴포넌트 스캔 + 어노테이션 방식
- @Configuration + @Bean 방식
- XML 설정 방식
3. SpringWebMVC
1) SpringWebMVC 구성요소
SpringWebMVC는 웹 애플리케이션 개발을 위한 Spring Framework 모듈입니다. 다음의 구현체들로 구성되어 있으며, 각각 다음의 역할을 수행합니다.
- DispatcherServlet: 모든 요청의 진입점으로, FrontController 역할을 수행합니다. 나머지 구성요소(아래)들을 호출하고 최종 응답을 반환합니다.
- Controller: 요청에 대해 로직을 처리하고 결과(데이터 + 뷰 이름)를 반환하는 역할을 수행합니다.
- HandlerMapping: 요청 URL에 맞는 Controller를 찾는 역할을 수행합니다.
- HandlerAdapter: 매핑된 Controller를 실행하는 역할을 수행합니다.
- ModelAndView: Controller가 반환하는 객체입니다. View이름과 Model데이터(전달할 값)를 함께 담는 객체입니다
- ViewResolver: Controller가 반환한 View이름을 실제 파일이 위치한 경로로 변환합니다. (예: userList → /WEB-INF/views/userList.jsp)
2) DispatcherServlet과 WebApplicationContext
DispatcherServlet은 이름에도 알 수 있듯이 서블릿 컨테이너에 등록되고 관리되는 서블릿입니다.
WebApplicationContext는 ApplicationContext의 하위 타입으로, 웹 전용 Spring 컨텍스트입니다. 이 컨텍스트는 DispatcherServlet이 초기화될 때 자동으로 생성되며 Controller, HandlerMapping, ViewResolver 등 웹 요청 처리를 위한 Bean들을 등록 및 관리합니다.
생성된 WebApplicationContext는 서블릿 컨테이너의 ServletContext에 속성으로 저장되고, DispatcherServlet은 이 ServletContext를 통해 웹 관련 빈을 조회하고 사용합니다.
3) Root WebApplicationContext
Spring MVC는 ApplicationContext의 계층구조를 통해 빈의 범위와 역할을 구분합니다.
- Root ApplicationContext (최상위 Context)
- 웹 애플리케이션 전역에서 공유하는 공통 빈들을 관리합니다.
- 보통 비즈니스 서비스, DAO, 데이터소스, 보안 등 애플리케이션 핵심 로직에 해당하는 빈이 위치합니다.
- 이 Root Context는 ContextLoaderListener가 서블릿 컨테이너 시작 시점에 생성합니다.
- WebApplicationContext (서블릿 전용 Context)
- DispatcherServlet이 생성하는 서블릿 별 ApplicationContext입니다.
- 웹 관련 컨트롤러, 뷰 리졸버, 핸들러 매핑 등 웹 계층의 빈을 관리합니다.
- Root Context의 자식 Context로 설정되어 Root Context의 빈을 참조할 수 있지만, 반대는 불가능합니다.
다음은 Spring web MVC의 아키텍처입니다.
'컴퓨터 > JAVA' 카테고리의 다른 글
JAVA - 리플렉션(Reflection) (0) | 2025.02.14 |
---|---|
Spring - Log 레벨 이해 및 JAVA의 로그 프레임워크 종류 (0) | 2025.01.16 |
JPA - Spring Boot의 @RestController, Hibernate Lazy Loading 직렬화 (0) | 2024.12.19 |
JPA - 연관관계 매핑 2(프록시 객체) (0) | 2024.12.05 |
JPA - 연관관계 매핑 1(외래키 설정) (0) | 2024.11.28 |