본문 바로가기

백엔드 학습 과정/Section 3 [Spring MVC, JDBC, JPA, RestDo

#2. Spring MVC [Service 계층] - 역할, 생성, 적용, 필요 애너테이션

클라이언트가 요청 데이터를 서버쪽에 전송하면 웹 애플리케이션에서 [API] 계층이 먼저 전달되고,

요청한 데이터에 비즈니스 로직을 요청하는 계층이 [Service 계층]이다.

 

[클라이언트 요청으로부터 앱 애플리케이션의 흐름]

클라이언트 요청 -> API 계층 - Controller 클래스 -> Service 클래스 비즈니스 로직 -> 결과 값 API 계층 전달 -> 클라이언트

 

[Service 계층]

애플리케이션의 도메인 업무 영역을 구현하여

'도메인 엔티티 클래스' 타입으로 처리하는 비즈니스 로직이 있는 계층. 

 

[Service 클래스 생성]

1. Service 클래스 생성 // @Service

새로운 클래스를 생성하여 Controller 핸들러 메서드 1개당 매칭되는 메서드를 이름만 선언한다.

 

ex) @PostMapping/PutMapping/PatchMapping/DeleteMapping

 

2. 도메인 엔티티 클래스 생성 // @Getter, @Setter, @NoArgsConstructor, @AllArgsConstructor 

Controller 클래스와 이름이 동일한 (=기능) 새로운 클래스를 만들어서

Controller클래스-핸들러 메서드에서 필요로 하는 멤버 변수 (=데이터)들을 필드로 private 형태로 선언한다.

 

3. Service 클래스 메서드 완성

핸들러 메서드와 매칭된 메서드로 선언해둔 Service 클래스의 메서드에 엔티티 클래스를 파라미터로 작성한다.

 

4. Controller 클래스와 Service 클래스 연동

[DI]를 통해 Controller 클래스에 Service 클래스를 주입한다.

A. Controller 클래스에서 Service 클래스 객체를 private 형태로 필드 선언 + 생성자 초기화한다.

 

5. DTO 패키지내에 ResponseDto 클래스 생성 // @Getter, @AllArgsConstructor

클라이언트의 요청, 응답 데이터는 DTO 타입을 가진다. 

응답 데이터를 DTO 타입으로 담아 전달하기위해 ResponseDto 클래스를

DTO 패키지 내Controller 클래스 이름과 동일하게 생성한다.

핸들러 메서드에서 필요한 모든 데이터 타입들을 private으로 선언한다.

 

6. MapStruct 기반의 인터페이스 Mapper 인터페이스 생성 // @Mapper(componentModel = "spring")

[API 계층]은 DTO 타입, [SERVICE 계층]은 damain Entity 타입으로만 데이터를 처리해야 하며,

클라이언트에게 요청받은 DTO타입의 데이터를 [Service계층]에서 비즈니스 로직을 처리하기 위해

Entity 타입으로 변경해야하고, 비즈니스 로직 처리 이후 [API 계층]에 전달하여

응답 데이터를 생성하기 위해 Entity > DTO 타입으로 변환해줄 객체가 필요하다.

 

즉, 비즈니스 로직 처리를 위해 API 계층 => Service 계층 전달 시, DTO > Entity 객체 변환 필요,

처리 후 데이터를 응답 메시지에 넣기 위해 Service 계층 => API 계층 전달 시, Entity > DTO 객체 변환 필요.

객체 변환의 역할은 Mapper라는 인터페이스가 담당한다.

 

A. 의존 라이브러리 생성 // src/build.gradle 파일에 코드 추가.

dependencies {
	implementation 'org.mapstruct:mapstruct:1.4.2.Final'
	annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
}

B. MapStuct 기반의 Mapper 인터페이스 생성 후 정의. // 기능별 1개 씩 필요.

@Mapper(componentModel = "spring")  // (1)
public interface MemberMapper {
    Member memberPostDtoToMember(MemberPostDto memberPostDto);
    Member memberPatchDtoToMember(MemberPatchDto memberPatchDto); // 메서드 = 엔티티
    MemberResponseDto memberToMemberResponseDto(Member member); // 응답 = DTO
}

[ API => Service : DTO -> Entity ]

엔티티_타입   변수명1  (핸들러메서드별_DTO   변수명2)                 // 핸들러 메서드만큼 생성.

 

[ Service => API : Entity -> DTO ]

ResponseDTO   변수명1 (엔티티_타입    변수명2)                       // 응답은 1개만 생성하면 됨.

 

7. Mapper 인터페이스와 Controller 클래스 연동

Controller 클래스의 import문에 하기 코드 한줄 추가.

import com.codestates.member.mapstruct.mapper.MemberMapper; // (1) 패키지 변경

 

================= 애너테이션 정리 ====================

1. @Getter / @Setter

캡슐화 개념에서 다른 클래스에서 호출하지 못하도록 필드 변수를 private으로 선언한 이후 호출 할 수 있도록 만든 방법.

 

2. @NoArgsConstructor

파라미터가 없는 기본 생성자를 자동으로 생성

 

3. @AllArgsConstructor 

도메인 엔티티 클래스의 모든 멤버 변수를 파라미터로 갖는 생성자 자동 생성