Spring

[Spring] Spring MVC 웹 애플리케이션 계층별 개발 흐름 정리 (수정중)

jungha_k 2022. 11. 15. 22:41

내가 헷갈려서 정리하려고 쓰는,, 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. 애플리케이션 빌드 / 실행 / 배포