API 개발 기본

유동우·2023년 8월 12일
0
post-thumbnail

API ?

API 란 "운영체제와 응용프로그램 사이의 통신에 사용되는 언어나 메시지 형식을 말한다" - NAVER 지식백과

단순히 쉽게 생각하면 API는 자동차로 생각하면 운전자가 제어할 수 있는 인터페이스 모음이다.
"API 스펙" 은 각각의 API가 어떤 기능을 하는지, 어떤 입력에 대해 어떤 출력이 발생하는지를 명시한 문서이다.

회원 등록 API

V1 엔티티를 Request Body에 직접 매핑

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

@RequestBody : JSON 수신받은 body를 Member에 매핑해서 쫙 넣어준다

위 V1 코드는 한 엔티티에 각각의 API를 위한 모든 요구사항을 담기가 어렵고,
엔티티를 외부에 노출하거나 파라미터로 그대로 받은 문제점이 있다.

=> API 요청 스펙에 맞춰 별도의 DTO를 파라미터로 받아야 한다.

V2 엔티티 대신에 DTO를 RequestBody에 매핑

 @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);
    }
    
 @Data
    static class CreateMemberRequest {
        private String name;
    }

V1코드의 Member 대신 CreateMemberRequest를 RequestBody와 매핑한다
=> 엔티티와 프레젠테이션 계층의 로직을 구분할 수 있다

회원 수정 API

@Transactional
public void update(Long id, String name) {
     Member member = memberRepository.findOne(id);
     member.setName(name);
}

변경 감지를 이용한 데이터 수정 코드이다
=> JPA가 영속성 컨텍스트에서 DB에서 영속성 컨텍스트에 올린 값을 반환하여 member는 영속상태이다
이후 영속상태인 member를 .setName( )으로 이름을 변경해주고 메서드가 종료되면
spring AOP가 동작하면서 @Transactional에 의해서 트랜젝션 관련 AOP가 끝나는 시점에 커밋이 된다
마지막으로 JPA가 flush 하고 commit을 진행한다
즉, 영속성 컨텍스트 flush하고 DB 트랜잭션 커밋

@PutMapping("/api/v2/members/{id}") //부분 업데이트 -> PATCH or POST
    public UpdateMemberResponse updateMemberV2(
            @PathVariable("id") Long id,
            @RequestBody @Valid UpdateMemberRequest requeest) {
            
        memberService.update(id, requeest.getName());
        Member findMember = memberService.findOne(id);

        return new UpdateMemberResponse(findMember.getId(), findMember.getName());
    }

회원 조회 API

@GetMapping("api/v1/members")
    public List<Member> membersV1() {
        return memberService.findMembers();
    }

매우 간단하게 작성이 가능하지만 엔티티를 직접 노출함으로써 다음과 같은 문제가 발생한다

  • 엔티티에 프레젠테이션 계층을 위한 로직이 추가된다 (@JsonIgnore)
  • 엔티티가 변경될 경우 API 스펙이 변한다
@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;
    }

API 스펙과 매핑할때 필요한 정보만 들어있는 MemberDto 생성

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

오브젝트로 한 번 감싸서 반환
=> count 같은 정보 추가로 작성 가능

즉,
API 스펙 <-> 엔티티 : 엔티티를 모두 노출하는것이 아닌
API 스펙 <-> DTO : 필요한 정보만 노출시키기

Reference
김영한 님 - 실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화

profile
효율적이고 꾸준하게

0개의 댓글