SSAFY 7기 Spring 수업 기록: 요청이 Controller·Service·DAO를 흐르는 과정

반응형

2022년 4월 27일, SSAFY 7기 Spring 수업에서 적은 원문은 IoC, DI, DispatcherServlet, Controller, Service, DAO, MyBatis, REST API가 한 줄씩 이어지는 형태였다. 배운 범위는 넓었지만, 키워드 사이의 관계는 나중에 다시 읽기 어려웠다.

지금 그날의 메모를 한 문장으로 묶으면 이렇다. Spring MVC 애플리케이션에서는 HTTP 요청이 공통 진입점을 거쳐 controller에 도달하고, application logic과 data access를 지난 결과가 view나 response body로 돌아간다.

요청 하나를 따라가며 구조 보기

수업 메모에 있던 흐름을 현재의 용어로 정리하면 다음 순서다.

Browser or API client
  → Servlet container
  → DispatcherServlet
  → HandlerMapping / HandlerAdapter
  → Controller
  → Service
  → Repository or MyBatis Mapper
  → Database
  → Controller return value
  → View rendering or HttpMessageConverter
  → HTTP response

Spring MVC의 DispatcherServlet은 front controller 역할을 한다. 요청에 맞는 handler를 찾고, 연결된 interceptor와 controller를 실행한 뒤 model을 view로 넘기거나 annotated controller의 응답을 바로 만든다. 이 흐름은 Spring MVC의 DispatcherServlet 처리 과정에서 확인할 수 있다.

Controller → Service → Repository는 Spring이 강제하는 유일한 구조라기보다 책임을 나누는 흔한 방식이다.

  • Controller: HTTP 입력을 애플리케이션 입력으로 바꾸고 응답을 결정한다.
  • Service: use case와 transaction 경계를 표현한다.
  • Repository·Mapper: 저장소와의 통신을 감싼다.

계층 이름보다 중요한 것은 HTTP 세부사항, business rule, SQL이 한 메서드에 섞이지 않도록 변화 이유를 분리하는 일이다.

IoC와 DI가 이 흐름을 연결하는 방법

원문에는 @Component, @Bean, @Controller, @Service, @Repository, @Autowired가 연달아 적혀 있었다. 이들을 관통하는 개념이 IoC(Inversion of Control) container와 DI(Dependency Injection)다.

ApplicationContext는 bean을 생성하고 구성하며 bean 사이의 의존 관계를 조립한다. 설정 방식은 annotation, Java configuration, XML 등일 수 있다. 즉, controller가 필요한 service 구현을 직접 생성하기보다 외부에서 전달받도록 만드는 것이 핵심이다. Spring 공식 IoC container 설명도 이 책임을 bean의 생성·설정·조립으로 정의한다.

@RestController
class OrderController {
    private final OrderService orderService;

    OrderController(OrderService orderService) {
        this.orderService = orderService;
    }
}

생성자 주입을 사용하면 객체가 동작하는 데 필요한 의존성이 signature에 드러난다. @RestController@Controller@ResponseBody의 의미를 결합한 annotation이지만, 모든 controller가 REST API여야 한다는 뜻은 아니다. HTML view를 반환하는 controller에는 @Controller가 자연스럽다.

HTTP 입력은 어디에 있느냐에 따라 받는다

그날 메모에는 RequestBody, PathVariable, RequestParam, ModelAttribute가 떨어져 있었다. 이 annotation들은 이름보다 입력값이 HTTP request의 어디에 실렸는지로 구분하면 쉽다.

입력 위치 Spring MVC annotation 예시
URI path @PathVariable /orders/{orderId}orderId
query string·form parameter @RequestParam ?page=2
JSON 같은 request body @RequestBody JSON을 DTO로 역직렬화
form·query 값을 object에 binding @ModelAttribute 검색 조건 form

Spring MVC는 @RequestBodyHttpMessageConverter로 object에 변환한다. @RequestParam과 다른 controller argument의 정확한 범위는 Spring MVC method arguments 문서에서 확인할 수 있다.

원문의 GET=SELECT, POST=INSERT, PUT=UPDATE, DELETE=DELETE는 입문 단계의 기억법으로는 짧지만 그대로 설계 규칙으로 쓰기에는 거칠다. HTTP method는 DB 문장이 아니라 resource에 대한 의도를 표현한다. 같은 POST라도 생성 외의 처리를 할 수 있고, PUT과 PATCH의 선택도 SQL 수행 시간으로 정하지 않는다.

transaction과 MyBatis에서 남겨야 할 경계

@Transactional → All or Nothing이라는 메모는 방향은 맞지만, annotation 하나만 붙이면 모든 호출이 같은 방식으로 동작한다고 이해하면 부족하다. Spring의 선언적 transaction은 일반적으로 AOP proxy와 transaction manager를 통해 적용되며, method 호출 경계와 예외 종류 같은 조건의 영향을 받는다. Spring 선언적 transaction 구현 설명은 이 proxy 경계를 명시한다.

MyBatis 메모의 #{value}${key}도 구분해야 한다.

  • #{value}는 prepared statement parameter로 처리한다.
  • ${text}는 문자열을 SQL에 그대로 삽입한다.
  • 따라서 table·column name 같은 동적 SQL에 ${}가 필요할 수 있지만, 검증하지 않은 사용자 입력을 넣으면 SQL injection 위험이 있다.

DB column과 Java property 이름이 다르면 SQL alias나 resultMap으로 연결할 수 있다. 구체적인 mapping 방식과 ${} 주의사항은 MyBatis Mapper XML 공식 문서에 정리돼 있다.

하루치 메모를 다시 읽으며 남은 것

원문에는 EJB, Struts, JSP, SiteMesh, JNDI, connection pool, Swagger, Ajax, CORS까지 한꺼번에 등장한다. 모두 수업에서 다룬 실제 키워드지만, 하나의 글에서 정의를 늘어놓으면 다시 같은 목록이 된다. 이번 정리에서는 HTTP 요청이 Spring MVC와 data layer를 지나가는 흐름에 직접 연결되는 부분만 남겼다.

특히 다음 세 문장은 이후 학습의 기준으로 쓸 수 있다.

  1. annotation 이름을 외우기 전에 요청과 응답의 위치를 본다.
  2. Controller·Service·DAO는 계층 자체보다 책임을 분리하기 위해 둔다.
  3. framework가 대신 처리하는 부분도 proxy, converter, mapping 같은 실행 경계를 확인한다.

IoC와 DI를 더 좁게 정리한 스프링 기본 원리 공부 기록, HTTP resource와 URI 설계는 REST하게 URI 설계하는 법에서 이어서 볼 수 있다.

이 글은 당시 수업의 완전한 강의록이나 실행 예제가 아니다. 2022년 4월 27일 남긴 키워드를 현재 공식 문서와 대조해, 다시 학습할 수 있는 요청 흐름으로 재구성한 기록이다.

반응형
KEEP READING
카테고리 전체 보기 →

댓글