DI를 통한 서비스 계층과 API 계층의 연동
API 계층에서 구현한 Controller 클래스와 서비스 계층의 Service 클래스의 상호작용
Service: 도메인 업무 영역을 구현하는 비즈니스 로직과 관련을 가진다.
도메인 모델: 빈약한 도메인 모델과 풍부한 도메인 모델로 구분하며 DDD(도메인 주도 설계)와 관련이 깊다.
매퍼를 이용한 DTO 클래스와 엔티티 클래스 매핑
📄 Member 도메인 엔티티
DTO의 역할: 클라이언트의 요청 데이터 ↔ Controller의 핸들러 메서드 (@ReqeustBody)
Member 클래스의 역할: API 계층 ↔ 서비스 계층
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Member {
private long memberId;
private String email;
private String name;
private String phone;
}
📄 MemberController
@RestController
@RequestMapping("/v2/members")
@Validated
public class MemberController {
private final MemberService memberService;
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
@PostMapping
public ResponseEntity postMember(@Valid @RequestBody MemberPostDto memberDto) {
Member member = new Member();
member.setEmail(memberDto.getEmail());
member.setName(memberDto.getName());
member.setPhone(memberDto.getPhone());
Member response = memberService.createMember(member);
return new ResponseEntity<>(response, HttpStatus.CREATED);
}
// . . .
}
📄 MemberService
Cotroller의 핸들러 메서드와 Service의 메서드가 1대1로 매치된다.
public class MemberService {
public Member createMember(Member member) {
// TODO should business logic
// TODO member 객체는 나중에 DB에 저장 후, 되돌려 받는 것으로 변경 필요.
Member createdMember = member;
return createdMember;
}
public Member updateMember(Member member) {
// TODO should business logic
// member 객체는 나중에 DB에 업데이트 후, 되돌려 받는 것으로 변경 필요.
Member updatedMember = member;
return updatedMember;
}
// . . .
}
💡 문제점
1
Controller의 핸들러 메서드는 클라이언트의 요청 데이터를 Service에 전달하고 응답 데이터를 클라이언트에 전달하는 단순한 역할만 해야 한다!
위의 코드를 보면 DTO 클래스를 엔티티 객체로 변환하는 작업까지 하고 있다.
2
엔티티 객체를 클라이언트 응답으로 전송하고 있다.
엔티티 클래스는 서비스 계층에서만 데이터 처리 역할을 해야 한다.
Mapper 클래스 구현
DTO 클래스와 엔티티 클래스를 변환하는 작업을 핸들러 메서드가 아닌 다른 클래스에 위임한다.
📄 MemberMapper
@Component
public class MemberMapper {
public Member memberPostDtoToMember(MemberPostDto memberPostDto) {
return new Member(0L,
memberPostDto.getEmail(),
memberPostDto.getName(),
memberPostDto.getPhone());
}
// . . .
public MemberResponseDto memberToMemberResponseDto(Member member) {
return new MemberResponseDto(member.getMemberId(),
member.getEmail(),
member.getName(),
member.getPhone());
}
}
📄 MemberResponseDto
@Getter
@AllArgsConstructor
public class MemberResponseDto {
private long memberId;
private String email;
private String name;
private String phone;
}
위와 같이 직접 만들 수도 있다.
MapStruct를 이용한 Mapper 자동 생성
dependencies {
...
...
implementation 'org.mapstruct:mapstruct:1.4.2.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
}
📄 MemberMapper
MapStruct가 자동으로 구현 클래스를 만들어 준다.
@Mapper(componentModel = "spring") //스프링 빈으로 등록해 준다
public interface MemberMapper {
Member memberPostDtoToMember(MemberPostDto memberPostDto);
Member memberPatchDtoToMember(MemberPatchDto memberPatchDto);
MemberResponseDto memberToMemberResponseDto(Member member);
}
MemberMapperImpl 클래스는 언제, 어떻게 생성될까요? IntelliJ IDE의 오른쪽 상단의 [Gradle] 탭을 클릭한 후, [프로젝트 명 > Tasks 디렉토리 > build 디렉토리 > build task]를 더블 클릭하면 MapStruct로 정의된 인터페이스의 구현 클래스가 생성됩니다.
MemberMapperImpl 클래스는 어디에 생성될까요? IntelliJ IDE의 좌측에서 [Project 탭 > 프로젝트명 > build] 디렉토리내의 MemberMapper 인터페이스가 위치한 패키지 안에 생성됩니다.
📄 MemberController 적용
@RestController
@RequestMapping("/v5/members")
@Validated
public class MemberController {
private final MemberService memberService;
private final MemberMapper mapper;
public MemberController(MemberService memberService, MemberMapper mapper) {
this.memberService = memberService;
this.mapper = mapper;
}
@PostMapping
public ResponseEntity postMember(@Valid @RequestBody MemberPostDto memberDto) {
Member member = mapper.memberPostDtoToMember(memberDto);
Member response = memberService.createMember(member);
return new ResponseEntity<>(mapper.memberToMemberResponseDto(response),
HttpStatus.CREATED);
}
// . . .
}
'Back-End > Spring' 카테고리의 다른 글
비즈니스 예외 던지기 및 예외 처리 (0) | 2022.08.24 |
---|---|
Spring MVC 패턴의 예외 처리 | @ExceptionHandeler, @RestControllerAdvice (0) | 2022.08.24 |
DTO(Data Transfer Object) (0) | 2022.08.22 |
[Error] unknown enum constant When.MAYBE (0) | 2022.08.16 |
AOP(Aspect-Oriented Programming) : 애너테이션(Annotation)의 이용 (0) | 2022.08.16 |
댓글