Search
〰️

API 개발과 성능 최적화

API 개발 기본

회원 등록 API

Entity를 API request/response에 바로사용하는 것은 위험하다. (외부 노출 지양)
Entity의 스펙이 변경 → API의 스펙 변경과 직결되는 문제 발생
Entity는 어플리케이션 전체에 많은 영향도를 가지고 있기 때문에, API 스펙에 맞는 DTO를 별도로 선언하여 사용해야한다!
Entity를 Parameter/return값으로 사용하지마라!
DTO를 만들어서 Entity를 Mapping, DTO를 Controller에서 사용 → 안정적인 시스템 운용 가능
API 스펙에 맞는 Validation 가능 → 유지보수 편리
DTO를 통해 API 스펙 파악이 바로 가능
Entity <> Presentation Layer(validation 등) Logic을 분리
Entity 스펙이 변경되어도 API에 Side Effect가 없음
// Entity @PostMapping("/api/v1/members") public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member) { Long id = memberService.join(member); return new CreateMemberResponse(id); } // 별도의 DTO를 사용 @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); }
Java
복사

회원 수정 API

수정 - PUT
멱등성
같은 것을 여러 번 호출해도 결과가 동일함
command <> query logic 분리
유지보수 용이
// Controller @PutMapping("/api/v2/members/{id}") public UpdateMemberResponse updateMemberV2( @PathVariable("id") Long id, @RequestBody @Valid UpdateMemberRequest request) { memberService.update(id, request.getName()); Member findMember = memberService.findOne(id); // command <> query의 분리 return new UpdateMemberResponse(findMember.getId(), findMember.getName()); } // Serivce @Transactional public void update(Long id, String name) { Member member = memberRepository.findOne(id); member.setName(name); // 변경감지 (dirty Checking) }
Java
복사

회원 조회 API

V1
@GetMapping("/api/v1/members") public List<Member> membersV1() { return memberService.findMembers(); }
Java
복사
→ 응답에 Orders에 대한 정보도 포함되어서 옴. 하지만 단순 회원 정보만 받고 싶다면?
→ Membe Entity에 @JsonIgnore 어노테이션을 추가하여 해당 정보는 무시하도록 한다.
API스펙에 따라 다양한 변수(컬럼)를 포함하거나, 무시하는 경우 발생. 이럴 때마다 Entity관리를 어떻게 진행할 것인가!?
응답 스펙을 맞추기 위해 로직이 추가된다. (@JsonIgnore, 별도의 뷰 로직 등등)
실무에서는 같은 엔티티에 대해 API가 용도에 따라 다양하게 만들어지는데, 한 엔티티에 각각의 API를 위한 프레젠테이션 응답 로직을 담기는 어렵다.
기본적으로 엔티티의 모든 값이 노출된다.
Entity에 Presentation Layer의 기능도 포함되어 좋지 않은 설계가 됨
Application을 수정하기 어려운 형태가 됨
마찬가지로 Entity를 변경하면, API스펙이 변경되는 문제 (장애 유발 요인)
Entity를 직접 반환하지 말자!
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); } @Data @AllArgsConstructor static class MemberDto { private String name; } @Data @AllArgsConstructor static class Result<T> { //private int count; //field 추가에도 유연한 변경 가능 private T data; }
Java
복사
→ Entity를 Dto로 변환하여, 변경에 유연하게 대비할 수 있도록 Result 객체로 감싼 후 응답하도록 한다.
field가 추가된 경우에도 API스펙, DTO를 바꾸지 않아도 감싸는 Result 객체에 field만 추가하면 되기 떄문!
API 스펙 : DTO = 1: 1
변경에 유연한 설계를 가져갈 수 있다.
API request/response에는 반드시 DTO를 사용해라. Entity를 외부에 노출하지 마라!