애플리케이션에서 서비스의 의미는 비즈니스 영역과 관련이 있다. API 계층을 처리하는 controller 클래스와 service 클래스를 메소드 호출을 통해 상호 작용을 하도록 연동한다.
MemberService
public class MemberService {
public Member createMember(Member member) {
Member createdMember = member;
return createdMember;
}
public Member updateMember(Member member) {
Member updatedMember = member;
return updatedMember;
}
public Member findMember(long memberId) {
Member member =
new Member(memberId, "hgd@gmail.com", "홍길동", "010-1234-5678");
return member;
}
public List<Member> findMembers() {
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) {
}
}
MemberController
@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) {
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);
}
@PatchMapping("/{member-id}")
public ResponseEntity patchMember(
@PathVariable("member-id") @Positive long memberId,
@Valid @RequestBody MemberPatchDto memberPatchDto) {
memberPatchDto.setMemberId(memberId);
Member member = new Member();
member.setMemberId(memberPatchDto.getMemberId());
member.setName(memberPatchDto.getName());
member.setPhone(memberPatchDto.getPhone());
Member response = memberService.updateMember(member);
return new ResponseEntity<>(response, HttpStatus.OK);
}
@GetMapping("/{member-id}")
public ResponseEntity getMember(
@PathVariable("member-id") @Positive long memberId) {
Member response = memberService.findMember(memberId);
return new ResponseEntity<>(response, HttpStatus.OK);
}
@GetMapping
public ResponseEntity getMembers() {
List<Member> response = memberService.findMembers();
return new ResponseEntity<>(response, HttpStatus.OK);
}
@DeleteMapping("/{member-id}")
public ResponseEntity deleteMember(
@PathVariable("member-id") @Positive long memberId) {
memberService.deleteMember(memberId);
return new ResponseEntity(HttpStatus.NO_CONTENT);
}
}
위 코드처럼 MemberController 클래스에서 MemeberService 클래스의 객체를 생성하여 해당 메소드를 사용한다.
DTO(Data Transfer Object)클래스를 Entity 클래스로 변환하는 작업은 컨트롤러와 분리되어야 한다. 사용되는 계층에 따라 관심사(Aspect)가 다르기 때문이다. 그렇기에 이 역할을 하는 Mapper 클래스가 필요하다. Mapper를 사용하는 것이 코드가 더욱 간결해지고 생산성이 좋아진다.
MemberController
@RestController
@RequestMapping("/v4/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);
}
@PatchMapping("/{member-id}")
public ResponseEntity patchMember(
@PathVariable("member-id") @Positive long memberId,
@Valid @RequestBody MemberPatchDto memberPatchDto) {
memberPatchDto.setMemberId(memberId);
Member response = memberService.updateMember(mapper.memberPatchDtoToMember(memberPatchDto));
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);
return new ResponseEntity<>(mapper.memberToMemberResponseDto(response),
HttpStatus.OK);
}
@GetMapping
public ResponseEntity getMembers() {
List<Member> members = memberService.findMembers();
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);
}
}
컨트롤러에 Mapper를 적용한 모습이다.
MemberMapper
package com.codestates.member.mapstruct.mapper;
import com.codestates.member.dto.MemberPatchDto;
import com.codestates.member.dto.MemberPostDto;
import com.codestates.member.dto.MemberResponseDto;
import com.codestates.member.entity.Member;
import org.mapstruct.Mapper;
@Mapper(componentModel = "spring")
public interface MemberMapper {
Member memberPostDtoToMember(MemberPostDto memberPostDto);
Member memberPatchDtoToMember(MemberPatchDto memberPatchDto);
MemberResponseDto memberToMemberResponseDto(Member member);
}
@Mapper 애너테이션을 통새 해당 인터페이스가 MapStruct의 매퍼 인터페이스로 정의가 된다. componetModel="spring"을 지정하면 스프링 빈으로 등록된다. Gradle의 build task를 실행하면 인터페이스의 구현체가 자동으로 생성된다.