🍃 Spring

Controller에 Service가 생기면서 시작되는 2-Layer 아키텍처 정리

보배 진 2026. 1. 19. 11:37

Controller에 Service DI가 생기면 왜 “2-Layer”인가?

	@Autowired
	private MemberService memberService;

 

이 코드가 Controller에 추가되는 순간,

이 프로젝트의 구조는 2-Layerd 아키텍처가 된다

 

Controller는 더 이상 혼자 존재하지 않는다

Service 계층을 호출하는 구조가 된다

 


 

기존 MVC 레이어 == Presentation Layer (MVC Layer)

새로 생긴 Controller를 레이더 ==  Business Layer ( 비즈니스 == 핵심의 == 서비스 == 기능 == CRUD )

스프링 부트에서 내부적으로 여러 가지의 컨테이너를 갖고 있다

DS는 유일한 서블릿이고, DI할 대상으로 HM, VR를 갖고 있는데

HM가 Controller를 반환한다 여기까지가 Presentation Layer

 

그런데 여기에 멤버변수로 Service를 끼우는 순간

Service는 DAO를 미리 새팅해야 하는데

DAO는 DB 연결 관련 로직 및 객체이다

그래서 Presentation Layer가 준비가 되려면 DAO, DB연결 관련 로직 및 객체가 먼저 준비가 되야 하는 상황이 생긴것이다

기존의 컨테이너보다 먼저 준비가 되어 있어야 해서 "루트 컨테이너 == 스프링 루트 컨테이너"라고 부른다

 

 


기존 MVC 구조

Presentation Layer (MVC Layer)

▪ 여기까지만 있으면 Service 계층이 없는 구조

구성  요소역할
Controller 요청 수신
Model 데이터
View 화면

 

 

 

Service 계층의 의미 (Business Layer)

Service는 단순한 클래스가 아니다

✅ Service = Business Layer

▪ 비즈니스 로직 담당

▪ 핵심 기능

▪ CRUD 처리

▪ 여러 DAO 처리

▪ 트랜잭션 처리

▪ AOP 적용 지점

 

 

 

Spring 내부 컨테이너 구조 이해

Spring은 내부적으로 역할이 다른 컨테이너를 나눠서 관리한다

 

🔹 DispatcherServlet (FrontController)

▪ 유일한 서블릿

▪ 내부적으로 HandlerMapping(HM), ViewResolver(VR)을 DI 대상으로 가짐

DispatcherServlet
 ├─ HandlerMapping
 └─ ViewResolver

여기까지가 Presentation Layer

 

 

 

Service를 Controller에 DI하면 무슨 일이 생길까?

@Controller
public class TestController {
    @Autowired
    private MemberService memberService;
}

 

이 순간 발생하는 구조 변화

▪ Controller 생성 시 Service가 먼저 준비되어 있어야 함

▪ Service 는 DAO를 필요로 함

▪ DAO는 DB 연결 객체를 필요로 함

Controller
   ↓
Service
   ↓
DAO
   ↓
DB Connection

 

 즉, Presentation Layer보다 먼저 준비되어야 하는 계층이 생김

 

 

 

그래서 등장하는 개념: 루트 컨테이너 (Root Container)

📢 루트 컨테이너란?

웹 요청과 상관없이

애플리케이션 시작 시 먼저 로딩되는 컨테이너

Service, DAO, DB 관련 Bean 관리

 

이름 : Spring Root Application Context, Root Container

 

 

 

 

 

 

 


web.xml에서 루트 컨테이너를 만드는 방법

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">

    <servlet>
        <servlet-name>ds</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ds</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    
      <filter>
       <filter-name>enc</filter-name>
       <filter-class>org.springframework.web.filter.CharaterEncodingFilter</filter-class>
       <init-param>
          <param-name>encoding</param-name>
          <param-value>UTF-8</param-value>
       </init-param>
    </filter>
    <filter-mapping>
      <filter-name>enc</filter-name>
      <url-pattern>*.do</url-pattern>
    </filter-mapping>

   <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:applicationContext.xml</param-value>
   </context-param>
   <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
   </listener>

</web-app>

 

🔹 ContextLoaderListener 등록

/*
필터 등록할 때 처럼 태그를 활용하여 리스너를 등록했다
리스너는 로딩을 할 때는 어떤 파일을 참고하도록 할 수 있다
그래서 Context param으로 resource에 있는 서비스 자원이 들어있는 xml을 주었다
*/
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

 

역할 

▪ 서버 시작 시

▪ applicationContext.xml을 먼저 읽음

▪ Service / DAO Bean 생성

▪ DB 연결 객체 준비

 

구분 담당
Root Container Service, DAO
Servlet Container Controller, HM, VR

Controller는 Root Container의 Bean(Service)을 주입받아 사용

 

 

 

왜 필터처럼 리스너를 등록하는가?

필터 : 요청 전/후 처리

리스너 : 서버 시작/종료 시점 처리

ContextLoaderListener는 "서버가 켜질 때, 이 설정 파일부터 읽어라" 라는 역할을 한다

 

 

 

결론 2- Layer 아키텍쳐가 됐을 때 .xml에 위의 코드가 꼭 있어야 한다

 

 

 


 

[ 1. ]  Controller에 Service DI가 생기면 2-Layer 아키텍처
[ 2. ]  Service는 Business Layer이며 핵심 로직 담당
[ 3. ]  Service/DAO는 DispatcherServlet보다 먼저 준비되어야 하므로
ContextLoaderListener + applicationContext.xml로
루트 컨테이너를 구성해야 한다

 

Controller에 Service가 생기는 순간, Spring은 “웹 요청용 컨테이너”와 “비즈니스 로직용 컨테이너”를 분리해서 관리한다.