제육's 휘발성 코딩
article thumbnail
반응형

API 개발 기본

포스트맨을 설치해서 API 개발 예제를 실습해보자.

포스트맨 설치 : https://www.getpostman.com

 

Postman API Platform | Sign Up for Free

Postman is an API platform for building and using APIs. Postman simplifies each step of the API lifecycle and streamlines collaboration so you can create better APIs—faster.

www.postman.com

 

회원 등록 API

package jpabook.jpashop.domain;


import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import javax.validation.constraints.NotEmpty;
import java.util.ArrayList;
import java.util.List;

@Entity
@Getter @Setter
public class Member {

    @Id @GeneratedValue
    @Column(name = "member_id")
    private Long id;

    private String name;

    @Embedded
    private Address address;

    // mappedBy : 연관관계 주인이 아님을 지정
    // (여기서 수정해도 order의 값이 변경되지 않는다.)
    @OneToMany(mappedBy = "member")
    private List<Order> orders = new ArrayList<>();
}
  • 회원 엔티티는 다음과 같다.

회원 등록 API (V1)

package jpabook.jpashop.api;

import jpabook.jpashop.domain.Member;
import jpabook.jpashop.service.MemberService;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
@RequiredArgsConstructor
public class MemberApiController {

    private final MemberService memberService;

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

    @Data
    static class CreateMemberResponse {
        private Long id;

        public CreateMemberResponse(Long id) {
            this.id = id;
        }
    }
}
  • v1 상황 : 요청 값으로 Member 엔티티를 직접 받고 있다.
    • name 검증을 위해선 엔티티에 validation을 적용해야하는 문제점 발생
    • 여러 API 요청을 처리하기 위해 엔티티가 변경되면 API 스펙이 변하는 문제 발생

API 요청 스펙에 맞추어 별도의 DTO를 생성하여 파라미터로 받는다.

 

회원 등록 API (V2)

@PostMapping("/api/v2/members")
public CreateMemberResponse savedMemberV2(@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 {
  @NotEmpty
  private String name;
}
  • V2 상황 : 등록DTO인 CreateMemberRequest를 생성하여 해당 부분만 검증하는 방식
    • 엔티티와 프레젠테이션 계층을 위한 로직을 분리
    • 엔티티와 API 스펙을 명확하게 분리

 

회원 수정 API

@PatchMapping("/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);
  return new UpdateMemberResponse(findMember.getId(), findMember.getName());
}

@Data
static class UpdateMemberRequest {
  private String name;
}

@Data
@AllArgsConstructor
static class UpdateMemberResponse {
  private Long id;
  private String name;
}
  • V2 방식으로 수정을 적용시켰다. 수정할 땐 PUT, PATCH, POST 방식이 있다. 전체를 업데이트 하는 경우 PUT 을 부분 업데이트 하는 경우 PATCH, POST 방식을 사용하는 것이 REST 스타일에 올바르다.
@Transactional
public void update(Long id, String name) {
  Member member = memberRepository.findOne(id);
  member.setName(name);
}
  • update 는 다음과 같이 서비스 단에서 변경감지를 통해 변경하도록 구현되어 있다.

 

회원 조회 API

image

  • data 와 같이 별도의 배열로 감싸면 확장성에 용이해진다.

 

회원 조회 API (V1)

@GetMapping("/api/v1/members")
public List<Member> membersV1() {
  return memberService.findMembers();
}
  • V1 상황 : 회원 정보만 필요한 상황인데 orders 등의 불필요한 정보들도 노출되고 있다.
    • 엔티티의 모든 값이 노출되는 것은 올바르지 않고, 별도의 DTO를 통해 응답 스펙에 맞추어 반환하자.

 

회원 조회 API (V2)

@GetMapping("/api/v2/members")
public Result membersV2() {
  List<Member> findMembers = memberService.findMembers();
  // 엔티티 -> DTO 변환
  List<MemberDto> collect = findMembers.stream().map(m -> new MemberDto(m.getName()))
    .collect(Collectors.toList());

  return new Result(collect);
}

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

@Data
@AllArgsConstructor
static class MemberDto {
  private String name;
}
  • V2 상황 : 엔티티를 DTO로 변환하였고, Result 클래스로 컬렉션을 감싸서 확장성을 높혔다.

본 포스팅은 인프런 김영한님 강의(실전! 스프링 부트와 JPA 활용2)를 토대로 정리한 내용입니다.

반응형
profile

제육's 휘발성 코딩

@sasca37

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요! 맞구독은 언제나 환영입니다^^