<aside> ❗

템플릿 엔진에서 사용하는 컨트롤러랑

API 스타일의 컨트롤러 패키지를 분리

→ 공통 예외처리할 때, 패키지나 구성 단위로 공통 처리 많이 수행하기 때문

→ 화면은 공통 화면 에러 페이지가 등장해야 함

→ API는 공통 에러용 JSON 스펙이 나가야 한다.

</aside>

<aside> ❗

@RestController

@Valid - 스프링이 검증을 수행함(jakarta)

@NotEmpty
private String name;

PostMan

<aside> ❗

DTO를 사용해야 하는 이유

API 스펙을 위한 별도의 DTO를 만들어야 함 → 큰 장애 발생

실습 1

<aside> ❗

V1

@PostMapping("/api/v1/members")
public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member) {
	Long id = memberService.join(member);
	return new CreateMemberResponse(id);
}

[문제점]

요청이 들어오고 나갈 때, 절대 엔티티를 사용하지 마라

→ **항상 DTO를 사용**할 것

</aside>

<aside> ❗

V2

@PostMapping("/api/v2/members")
public CreateMemberResponse saveMemberV2(@RequestBody @Valid CreateMemberRequest request) {

  Member member = new Member();
  member.setName(request.getName());

  Long id = memberService.join(member);
  return new CreateMemberResponse(id);
}

**** 실무에서는 엔티티를 API 스펙에 노출하면 안 된다. ****

</aside>

<aside> ❗

회원 수정

@Transactional
    public void update(Long memberId, String updateName) {
        // 영속화
        Member findMember = memberRepository.findOne(memberId);
        // 값 수정
        findMember.setName(updateName);
    }
@PutMapping("/api/v2/members/{id}")
    public UpdateMemberResponse updateMemberV2(
            @RequestBody @Valid UpdateMemberRequest request,
            @PathVariable("id") Long id) {

        memberService.update(id, request.getName());
        Member one = memberService.findOne(id); // 트래픽 많지 않으면 이슈가 되지 않음
        return new UpdateMemberResponse(one.getId(), one.getName());
    }

<aside> ❗

조회 실습

V1

@PostMapping("/api/v1/members")
public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member) {

	Long id = memberService.join(member);
	return new CreateMemberResponse(id);
}

[문제점] - 응답 값으로 엔티티를 직접 외부에 노출

[결론]

<aside> ❗

V2

@GetMapping("/api/v2/members")
    public Result memberV2() {
        List<Member> findMembers= memberService.findMembers();
        List<MemberDto> collect = findMembers.stream()
                .map(m -> new MemberDto(m.getName()))
                .collect(Collectors.toList());
        return new Result(collect.size(), collect);
    }

    @Data
    @AllArgsConstructor
    static class Result<T> {
        private int count;
        private T data;
    }

    @Data
    @AllArgsConstructor
    static class MemberDto {
        private String name;
    }

[결론]