Spring MVC API 계층
목차
- Spring MVC
- **Spring MVC의 동작 방식과 구성 요소**
- 핸들러 메서드
- RestClient
배운 내용
Spring MVC
- 서블릿(Servlet) API를 기반으로 클라이언트의 요청을 처리하는 모듈
- 웹프레임워크의 한 종류
서블릿(Servlet)
- 클라이언트의 요청을 처리하도록 특정 규약에 맞추어서 Java 코드로 작성하는 클래스 파일
**Model**
- 작업의 처리 결과 데이터
- 비즈니스 로직(Business Logic) : 요청 사항을 처리하기 위해 Java 코드로 구현한 것
**View**
- Model 데이터를 이용해서 웹브라우저 같은 클라이언트 애플리케이션의 화면에 보여지는 리소스(Resource)를 제공하는 역할
- HTML 페이지의 출력
- HTML 페이지를 직접 렌더링해서 클라이언트 측에 전송하는 방식
- HTML 페이지 출력 기술 : Thymeleaf, FreeMarker, JSP + JSTL,
- PDF, Excel 등의 문서 형태로 출력
- XML, JSON 등 특정 형식의 포맷으로의 변환
- Model 데이터를 특정 프로토콜 형태로 변환해서 변환된 데이터를 클라이언트 측에 전송하는 방식
- 특정 형식의 데이터만 전송하고, 프런트엔드 측에서 이 데이터를 기반으로 HTML 페이지를 만드는 방식
**Controller**
- 클라이언트 측의 요청을 직접적으로 전달 받는 엔드포인트(Endpoint)로써 Model과 View의 중간에서 상호 작용을 해주는 역할
- 클라이언트 측의 요청을 전달 받아서 비즈니스 로직을 거친 후에
Model
데이터가 만들어지면, 이 Model 데이터를 View
로 전달
JSON(JavaScript Object Notation)
- 클라이언트 애플리케이션과 서버 애플리케이션이 주고 받는 데이터 형식
- JSON의 기본 포맷
**Spring MVC의 동작 방식과 구성 요소**
(1) 먼저 클라이언트가 요청을 전송하면 DispatcherServlet
이라는 클래스에 요청이 전달됩니다.
(2) DispatcherServlet
은 클라이언트의 요청을 처리할 Controller에 대한 검색을 HandlerMapping 인터페이스에게 요청합니다.
(3) HandlerMapping
은 클라이언트 요청과 매핑되는 핸들러 객체를 다시 DispatcherServlet에게 리턴해줍니다.
(4) 요청을 처리할 Controller 클래스를 찾았으니 이제는 실제로 클라이언트 요청을 처리할 Handler 메서드를 찾아서 호출해야 합니다. DispatcherServlet
은 Handler 메서드를 직접 호출하지 않고, HandlerAdpater에게 Handler 메서드 호출을 위임합니다.
(5) HandlerAdapter
는 DispatcherServlet으로부터 전달 받은 Controller 정보를 기반으로 해당 Controller의 Handler 메서드를 호출합니다.
이제 전체 처리 흐름의 반환점을 돌았습니다. 이제부터는 반대로 되돌아 갑니다. ^^
(6) Controller
의 Handler 메서드는 비즈니스 로직 처리 후 리턴 받은 Model 데이터를 HandlerAdapter에게 전달합니다.
(7) HandlerAdapter
는 전달받은 Model 데이터와 View 정보를 다시 DispatcherServlet에게 전달합니다.
(8) DispatcherServlet
은 전달 받은 View 정보를 다시 ViewResolver에게 전달해서 View 검색을 요청합니다.
(9) ViewResolver
는 View 정보에 해당하는 View를 찾아서 View를 다시 리턴해줍니다.
(10) DispatcherServlet
은 ViewResolver로부터 전달 받은 View 객체를 통해 Model 데이터를 넘겨주면서 클라이언트에게 전달할 응답 데이터 생성을 요청합니다.
(11) View
는 응답 데이터를 생성해서 다시 DispatcherServlet에게 전달합니다.
(12) DispatcherServlet
은 View로부터 전달 받은 응답 데이터를 최종적으로 클라이언트에게 전달
**Controller 클래스 설계 및 구조 생성**
**패키지 구조 생성**
**기능 기반 패키지 구조(package-by-feature)**
- 애플리케이션에서 구현해야 하는 기능을 기준으로 패키지를 구성
- 패키지 안에는 하나의 기능을 완성하기 위한 계층별(API 계층, 서비스 계층, 데이터 액세스 계층)클래스들이 모여있다.
- REST API 기반의 애플리케이션에서는 일반적으로 애플리케이션이 제공해야 될 기능을 리소스(Resource, 자원)로 분류 → 리소스에 해당하는
Controller
클래스를 작성하면 된다.
**계층 기반 패키지 구조(package-by-layer)**
**엔트리포인트(Entrypoint) 클래스 작성**
- main() 메서드가 포함된 애플리케이션의 엔트리포인트(Entrypoint, 애플리케이션 시작점)를 작성
@SpringBootApplication
- 자동 구성 활성화
- 패키지 내에서
@Component
가 붙은 클래스를 검색한 후(scan), Spring Bean
으로 등록하는 기능을 활성화
@Configuration
이 붙은 클래스를 자동으로 찾아주고, 추가적으로 Spring Bean을 등록하는 기능을 활성화
SpringApplication.run(Section3Week1Application.class, args);
- Spring 애플리케이션을 부트스트랩하고, 실행하는 역할
부트스트랩(Bootstrap)
- 애플리케이션이 실행되기 전에 여러가지 설정 작업을 수행하여 실행 가능한 애플리케이션으로 만드는 단계
**Controller 구조 작성**
@RestController
- 특정 클래스에
@RestController
를 추가하면 해당 클래스가 REST API
의 리소스(자원, Resource)를 처리하기 위한 API 엔드포인트로 동작함을 정의
- Spring Bean으로 등록해줌
@RequestMapping
- 클라이언트의 요청과 클라이언트 요청을 처리하는 핸들러 메서드(Handler Method)를 매핑
해주는 역할
- Controller 클래스 레벨에 추가하여 클래스 전체에 사용되는 공통 URL(Base URL) 설정
- 일반적으로 클래스 레벨에는 @RequestMapping 애너테이션을 사용하고, 메서드 레벨에서는 단축표현을 사용한다.
- 단축표현
- @GetMapping, @PostMapping, @PatchMapping, @DeleteMapping 등
**핸들러 메서드(Handler Method)**
produces 애트리뷰트
produces
애트리뷰트(Attribute)는 응답 데이터를 어떤 미디어 타입으로 클라이언트에게 전송할 지를 설정
@PostMapping
클라이언트의 요청 데이터(request body)를 서버에 생성할 때 사용하는 애너테이션
@RequestParam
-
핸들러 메서드의 파라미터 종류 중 하나
-
요청 데이터를 쿼리 파라미터(Query Parmeter 또는 Query String), 폼 데이터(form-data),
x-www-form-urlencoded형식으로 전송하면 이를 서버 쪽에서 전달 받을 때 사용하는 애너테이션
쿼리 파라미터(Query Parameter 또는 QueryString)
@GetMapping
- 클라이언트가 서버에 리소스를 조회할 때 사용하는 애너테이션
{member-id}
- 회원 식별자를 의미하며 클라이언트가 요청을 보낼 때 URI로 어떤 값을 지정하느냐에 따라서 동적으로 바뀌는 값
@PathVariable
@PathVariable
의 괄호 안에 입력한 문자열 값은 @GetMapping("/{member-id}")
의 중괄호({ }) 안의 문자열과 동일해야 합니다. → “member-id”
식별자(Identifier)
ResponseEntity 적용
Map
객체를 리턴하게 되면 내부적으로 ‘이 데이터는 JSON
형식의 응답 데이터로 변환해야 되는구나’라고 이해하고 JSON 형식으로 자동 변환
return new ResponseEntity<>(map, HttpStatus.CREATED);
- ResponseEntity 객체를 생성하면서 생성자 파라미터로 응답데이터(
map
)과 HttpStatus.CREATED
Http 응답 상태를 함께 전달
HTTP 헤더
- HTTP 헤더(Header)는 HTTP 메시지(Messages)의 구성 요소 중 하나로써 클라이언트의 요청이나 서버의 응답에 포함되어 부가적인 정보를 HTTP 메시지에 포함할 수 있다.
- HTTP Request 헤더(Header) 정보 얻기
@RequestHeader
애너테이션을 이용해서 개별 헤더 정보 및 전체 헤더 정보를 얻을 수 있다.
HttpServletRequest
또는 HttpEntity
객체로 헤더 정보를 얻을 수 있다.
- HTTP Response 헤더(Header) 정보 추가
ResponseEntity
와 HttpHeaders
를 이용해 헤더 정보를 추가할 수 있다.
HttpServletResponse
객체를 이용해 헤더 정보를 추가할 수 있다.
**Rest Client**
- Rest API 서버에 HTTP 요청을 보낼 수 있는 클라이언트 툴 또는 라이브러리(PostMan)
- 어떤 서버가 HTTP 통신을 통해서 다른 서버의 리소스를 이용한다면 그 때만큼은 클라이언트의 역할을 한다
**RestTemplate**
- Spring에서 제공하는 원격지에 있는 다른 Backend서버에 Http 요청을 보낼 수 있는 Rest Client API
- Rest 엔드 포인트 지정, 헤더 설정, 파라미터 및 body 설정을 한 줄의 코드로 손쉽게 전송가능
RestTemplate을 사용할 수 있는 기능 예
- 결제 서비스
- 카카오톡 등의 메시징 서비스
- Google Map 등의 지도 서비스
- 공공 데이터 포털, 카카오, 네이버 등에서 제공하는 Open API
**DTO(Data Transfer Object)**
**DTO가 필요한 이유**
- **DTO 클래스를 이용한 코드의 간결성**
- DTO 클래스가 요청 데이터를 하나의 객체로 전달 받는 역할을 수행
- **데이터 유효성(Validation) 검증의 단순화**
- 유효성 검증 로직을 DTO 클래스로 빼내어 핸들러 메서드의 간결함을 유지
- HTTP 요청의 수를 줄이기 위함
DTO 클래스 생성
- 주의해야 할 부분 : 멤버 변수 이외에 각 멤버 변수에 해당하는 getter 메서드가 있어야 한다
- getter 메서드가 없으면 Response Body에 해당 멤버 변수의 값이 포함되지 않는다.
@RequestBody
- JSON 형식의 Request Body를 MemberPostDto 클래스의 객체로 변환을 시켜주는 역할
클라이언트 쪽에서 전송하는 Request Body가 JSON 형식이어야 한다
@ResponseBody
- DTO 클래스의 객체를 JSON 형식의 Response Body로 변환하는 역할
@ResponseBody
애너테이션이 붙거나 핸들러 메서드의 리턴 값이 ResponseEntity
일 경우, 내부적으로 HttpMessageConverter
가 동작하게 되어 응답 객체( DTO 클래스 객체)를 JSON 형식으로 바꿔준다.
단점
-
Controller 클래스가 늘어남에 따라 DTO 클래스가 두 배(ex. xxxxPostDto + xxxxPatchDto)씩 늘어남
-
JSON 직렬화(Serialization): Java 객체 → JSON
-
JSON 역직렬화(Deserialization): JSON → Java 객체
**DTO 유효성 검증(Validation)**
- 프런트엔드 쪽에서 1차적으로 유효성 검사를 진행했다고 하더라도 서버 쪽에서 한번 더 유효성 검사를 진행해야 된다
- DTO 클래스의 각 멤버 변수에 유효성 검증을 위한 애너테이션을 추가함으로써 , 핸들러 메서드에 별도의 유효성 검증을 추가하지 않고, 깔끔하게 유효성 검증 로직이 분리
- 유효성 검증 로직이 실행되게 하기 위해서는 핸들러 메서드의 DTO 클래스에
@Valid
애너테이션을 추가
@NotBlank
- null 값이나 공백(””), 스페이스(” “) 같은 값들을 모두 허용하지 않음
@Email
@Pattern
- 정규표현식(Reqular Expression)에 매치되는지 검증
**쿼리 파라미터 및 @Pathvariable에 대한 유효성 검증**
@Validated
- 애너테이션을 사용하면 쿼리 파라미터(Query Parameter 또는 Query String) 및
@Pathvariable
에 대한 유효성 검증을 진행할 수 있다.
- 유효성 검증이 정상적으로 수행되려면 클래스 레벨에
@Validated
애너테이션을 반드시 붙여주어야 한다
@Min(1)
- 1 이상의 숫자일 경우에만 유효성 검증에 통과
**Custom Validator를 사용한 유효성 검증**
- 정규 표현식(Regular Expression)은 성능적인 면에서 때로는 비싼 비용을 치뤄야 될 가능성이있다.