[Spring] Spring MVC 웹 애플리케이션 계층별 개발 흐름 정리 (수정중)
내가 헷갈려서 정리하려고 쓰는,, Section 3 끝난 기념 구구절절 정리😆
후딱 한바퀴 돌리고 가장 최신 버젼 코드(v12? 13)?를 직접 쳐봐야겠다,,
* 아래 목차는 절대적인 개발 순서가 아님
Spring MVC :
Model - 작업의 처리 결과 데이터
View - 클라이언트 애플리케이션 화면에 보여지는 리소스 제공
Controller - Model ~ View 의 중간에서 상호작용, 클라이언트 요청 직접 받음
Client가 요청 데이터 전송 → Controller가 요청 데이터 수신 → 비즈니스 로직 처리 → Model 데이터 생성
→ Controller에게 Model 데이터 전달 → Controller가 View에게 Model 데이터 전달 → View가 응답 데이터 생성
1. API 계층
- Controller
전체 틀 작성
@RestController
@RequestMapping
틀 안에 핸들러메서드들..
(@PostMapping, @PatchMapping...)
하나의 핸들러 메서드 안에는 요청 데이터, 응답 데이터 有
핸들러 메서드의 파라미터 종류
@RequestParam
: 요청 데이터를 서버 쪽에서 전달 받을 때 사용
@PathVariable
: 괄호 안에 입력한 문자열 값 = 중괄호 안의 문자열
리턴값 - ResponseEntity<>(map, HttpStatus.CREATED);
ResponseEntity : 응답 데이터(map), HTTP 응답 상태
- DTO(Data Transfer Object)
: 요청 데이터(클라~서버) & 응답 데이터(서버~클라) 전송시에 이용됨
데이터를 전송하기 위한 용도의 객체
1. 요청, 응답 데이터를 하나의 객체로 전달 받게 해줌! ➡ 코드 간결성 위해
@RequestBody : JSON 형식의 RequestBody ➡ DTO 클래스 객체로 변환해줌 (직렬화)
@ResponseBody : DTO 클래스 객체 ➡ Response Body 로 변환해줌 (역직렬화)
* ResponseEntity 객체 리턴할 경우 생략 가능
2. 데이터 유효성 (@Validation) 검증의 단순화
@NotBlank, @Email, @Pattern...
2. 서비스 계층
DTO 클래스 - API 계층에서 Request, Response 데이터들 담아주는 역할
↕
Entity 클래스 - 서비스 계층에서 데이터 액세스 계층과 연동해서,
비즈니스 로직의 결과로 생성된 데이터 처리하는 역할
(=DB 테이블과 매칭될 클래스)
- 서비스 계층 : 비즈니스 로직 처리하기 위한 데이터 전달 받음
- 서비스 계층 ~ 데이터 액세스 계층 : 비즈니스 로직 처리 후 결과 값 API 계층으로 리턴해줌
Entity 클래스
@Getter
@Setter
@AllArgsConstructor : 모든 멤버변수 파라미터 갖는 생성자 생성
@NoArgsConstructor : 파라미터 없는 기본 생성자 자동 생성
Service 클래스
: 직접적인 비즈니스 영역을 처리해줌 (crud)
이 안에서 Entity 클래스들을 어떻게 할지 처리해주는 것임
Mapper 클래스
: DTO 클래스와 엔티티 클래스를 서로 변환해주는 역할
@Component : Spring Bean 으로 등록해줌
@RestController
@RequestMapping("/v2/members")
@Validated
public class MemberController {
private final MemberService memberService;
public MemberController() {
this.memberService = new MemberService(); // (1)
}
@PostMapping
public ResponseEntity postMember(@Valid @RequestBody MemberPostDto memberDto) {
// (2) 💥
Member member = new Member();
member.setEmail(memberDto.getEmail());
member.setName(memberDto.getName());
member.setPhone(memberDto.getPhone());
// (3)
Member response = memberService.createMember(member);
💥return new ResponseEntity<>(response, HttpStatus.CREATED);
}
⬆ 기존 레거시 코드 ⬆
-------------------------- Mapper클래스 코드 --------------------------------------
@Component // (1)
public class MemberMapper {
// (2) MemberPostDto를 Member로 변환
public Member 💥memberPostDtoToMember💥(MemberPostDto memberPostDto) {
return new Member(0L,
memberPostDto.getEmail(),
memberPostDto.getName(),
memberPostDto.getPhone());
}
}
--------------------------⬇ Mapper 적용 이후 ⬇-------------------------------------
@RestController
@RequestMapping("/v4/members")
@Validated
public class MemberController {
private final MemberService memberService;
private final MemberMapper mapper;
// (1) MemberMapper DI
public MemberController(MemberService memberService, MemberMapper mapper) {
this.memberService = memberService;
this.mapper = mapper;
}
@PostMapping
public ResponseEntity postMember(@Valid @RequestBody MemberPostDto memberDto) {
// (2) 매퍼를 이용해서 MemberPostDto를 Member로 변환
💥Member member = mapper.memberPostDtoToMember(memberDto);💥
Member response = memberService.createMember(member);
// (3) 매퍼를 이용해서 Member를 MemberResponseDto로 변환
💥return new ResponseEntity<>(mapper.memberToMemberResponseDto(response),
HttpStatus.CREATED);
}
DTO 클래스를 엔티티 클래스로 변환시켜주는 곳들을
공통적으로 뽑아내어 Mapper로 따로 주었고, 응답데이터도 따로 Mapper 로 뽑아내었다.
Mapper 적용 된 곳을 보니 역할 분리가 되어 깔끔해진 것을 볼 수 있다.
MapStruct : Mapper 직접 만들어주는 코드 자동 생성기!
Mapper 인터페이스 생성하고, gradle build task 실행하면 생성됨
3. 예외 처리
1) Spring MVC에서 일어난 예외처리 (API)
컨트롤러에 @ExceptionHandler 만들어서 handleException() 메서드 만들어줌
예외 처리시 필요한 정보만 보기 위한 ErrorResponse 클래스 만듬
➡ but 컨트롤러별로 추가할수는 없다.. & 컨트롤러 내에서 처리하지 못하는 경우도 발생
➡ @RestControllerAdvice 클래스 만들어서 예외처리 공통화
이 클래스 안에서 @ExceptionHandler 달은 예외 case 별로 작성
각각에 해당하는 ErrorResponse 넣어줌
* @ResponseControllerAdvice = @ControllerAdvice + @ResponseBody@ResponseStatus
2) 비즈니스 로직에서 일어난 예외처리
Controller에서 일어나는 예외와 마찬가지로 @RestControllerAdvice 클래스에서 처리한다. 서비스 계층에서 의도적으로 Exception을 throw 하면,
@RestControllerAdvice 클래스에서 잡아서 메서드 추가한다.
* Custom Exception 사용하고 싶을 경우 Enum으로 예외 코드 정의해주고,
RuntimeException을 상속하는 BusinessLogicException 클래스 작성,
작성한 custom enum을 멤버 변수로 지정하여 생성자를 통해서 조금 더 구체적인 예외 정보들을 제공해줌
⬇
원하는 구체적 정보로 서비스 계층에서 의도적 throw 가능
@ResponseStatus (고정된 예외) vs @ResponseEntity (다양한 유형의 custom exception)
4. JDBC 기반 데이터 액세스 계층
5. JPA 기반 데이터 액세스 계층
6. 트랜잭션
7. 테스팅 (Testing)
8. API 문서화
9. 애플리케이션 빌드 / 실행 / 배포