반응형
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
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)를 토대로 정리한 내용입니다.
반응형