ㅇ Service
API계층에서 구현한 Controller클래스가 서비스 계층의 Service클래스와 상호작용함.
애플리케이션에서 Service 라 하믄, 도메인 업무 영역을 구현하는 비지니스 로직과 관련있음.
비지니스 로직을 처리하는 서비스계층은 도메인 모델을 포함한다.
ㅇ Member 클래스, MemberService 클래스 구현!
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Member {
private long memberId;
private String email;
private String name;
private String phone;
}
Member클래스의 애너테이션들은 Lombok에서 제공하는 아주 편리한 애너테이션으로 저 애너테이션을 사용하면 보이진 않지만 게터세터,생성자를 추가해준다. 개꿀
public class MemberService {
public Member createMember(Member member) {
// TODO member 객체는 나중에 DB에 저장 후, 되돌려 받는 것으로 변경 필요.
Member createdMember = member;
return createdMember;
}
public Member updateMember(Member member) {
// member 객체는 나중에 DB에 업데이트 후, 되돌려 받는 것으로 변경 필요.
Member updatedMember = member;
return updatedMember;
}
public Member findMember(long memberId) {
// TODO member 객체는 나중에 DB에서 조회 하는 것으로 변경 필요.
Member member =
new Member(memberId, "hgd@gmail.com", "홍길동", "010-1234-5678");
return member;
}
public List<Member> findMembers() {
// TODO member 객체는 나중에 DB에서 조회하는 것으로 변경 필요.
List<Member> members = List.of(
new Member(1, "hgd@gmail.com", "홍길동", "010-1234-5678"),
new Member(2, "lml@gmail.com", "이몽룡", "010-1111-2222")
);
return members;
}
public void deleteMember(long memberId) {
// TODO should business logic
}
}
createMember(), updateMember()메서드는 일단은 Member객체를 그대로 반환하지만, 나중에 수정함^^
findMember(),findMembers() 는 Stub데이터를 넘겨줄거다.
ㅇ DI를 통한 비지니스 계층과 API 계층 연동(느슨한 결합)
먼저 MemberControllerdptj DI기능을 적용해boja.
@RestController
@RequestMapping("/v3/members")
@Validated
public class MemberController {
private final MemberService memberService;
// DI기능을 이용함. MemberService의 객체를 생성자 파라미터로 주입받음!
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
...
...
}
Spring이 친절하게 DI 주입해준다.
MemberController 클래스는 @RestController이 붙어서 Spring Bean임.
Service도 SpringBean이 되기 위해
클래스레벨에 @Service 애너테이션 추가함!!
**문제발생**
이렇게 되면 MemberController의 핸들러 메서드가 DTO클래스를 Entity클래스로 변환하는 작업을한다.
-> Mapper클래스로 DTO클래스를 엔티티 클래스로 변환해주면된다.
엔티티클래스의 객체를 클라이언트의 응답으로 전송함으로 계층간의 역할 분리가 필요하다.
->클라이언트 응답으로 엔티티 클래스를 전송하지 않고 엔티티클래스의 객체를 DTO클래스의 객체로 다시 바꿔준다.
ㅇ Mapper 클래스 구현
@Component // (1)Spring Bean으로 등록하기 위해 붙힘! 이 Bean은 이제 Controller에서 사용
public class MemberMapper {
// (2) MemberPostDto를 Member로 변환
public Member memberPostDtoToMember(MemberPostDto memberPostDto) {
return new Member(0L,
memberPostDto.getEmail(),
memberPostDto.getName(),
memberPostDto.getPhone());
}
// (3) MemberPatchDto를 Member로 변환
public Member memberPatchDtoToMember(MemberPatchDto memberPatchDto) {
return new Member(memberPatchDto.getMemberId(),
null,
memberPatchDto.getName(),
memberPatchDto.getPhone());
}
// (4) Member를 MemberResponseDto로 변환
public MemberResponseDto memberToMemberResponseDto(Member member) {
return new MemberResponseDto(member.getMemberId(),
member.getEmail(),
member.getName(),
member.getPhone());
}
}
MemberController에서 사용하는 DTO 클래스와 Member간 서로 타입을 변환해주는 매퍼클래스다.
ㅇ MemberResponseDto클래스
응답 데이터의 역할을 하는 Dto클래스
@Getter
@AllArgsConstructor
public class MemberResponseDto {
private long memberId;
private String email;
private String name;
private String phone;
}
ㅇ Controller 핸들러 메서드에 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);
}
@PatchMapping("/{member-id}")
public ResponseEntity patchMember(
@PathVariable("member-id") @Positive long memberId,
@Valid @RequestBody MemberPatchDto memberPatchDto) {
memberPatchDto.setMemberId(memberId);
// (4) 매퍼를 이용해서 MemberPatchDto를 Member로 변환
Member response =
memberService.updateMember(mapper.memberPatchDtoToMember(memberPatchDto));
// (5) 매퍼를 이용해서 Member를 MemberResponseDto로 변환
return new ResponseEntity<>(mapper.memberToMemberResponseDto(response),
HttpStatus.OK);
}
@GetMapping("/{member-id}")
public ResponseEntity getMember(
@PathVariable("member-id") @Positive long memberId) {
Member response = memberService.findMember(memberId);
// (6) 매퍼를 이용해서 Member를 MemberResponseDto로 변환
return new ResponseEntity<>(mapper.memberToMemberResponseDto(response),
HttpStatus.OK);
}
@GetMapping
public ResponseEntity getMembers() {
List<Member> members = memberService.findMembers();
// (7) 매퍼를 이용해서 List<Member>를 MemberResponseDto로 변환
List<MemberResponseDto> response =
members.stream()
.map(member -> mapper.memberToMemberResponseDto(member))
.collect(Collectors.toList());
return new ResponseEntity<>(response, HttpStatus.OK);
}
@DeleteMapping("/{member-id}")
public ResponseEntity deleteMember(
@PathVariable("member-id") @Positive long memberId) {
System.out.println("# delete member");
memberService.deleteMember(memberId);
return new ResponseEntity(HttpStatus.NO_CONTENT);
}
}
ㅇ MapStruct를 이용해 Mapper 자동생성하기
MapStruct는 자동으로 mapper클래스를 자동으로 구현해준다.
dependencies에
implementation 'org.mapstruct:mapstruct:1.4.2.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
추가해준뒤
@Mapper(componentModel = "spring") //MapStruct의 인터페이스로 정의됨.
//componentModel = "spring"은 Spring의 Bean으로 등록
public interface MemberMapper {
Member memberPostDtoToMember(MemberPostDto memberPostDto);
Member memberPatchDtoToMember(MemberPatchDto memberPatchDto);
MemberResponseDto memberToMemberResponseDto(Member member);
}
gradle의 build task하면 구현해준 클래스를 볼 수 있다.
ㅇ Controller에 MapStruct 적용
import com.codestates.member.mapstruct.mapper.MemberMapper; // 추가해줌
@RestController
@RequestMapping
@Validated
public class MemberController {
...
...
...
}
오늘은 여기까지
다음편은 예외처리를 araboza.
5편에 계속!