API ?
API 란 "운영체제와 응용프로그램 사이의 통신에 사용되는 언어나 메시지 형식을 말한다" - NAVER 지식백과
단순히 쉽게 생각하면 API는 자동차로 생각하면 운전자가 제어할 수 있는 인터페이스 모음이다.
"API 스펙" 은 각각의 API가 어떤 기능을 하는지, 어떤 입력에 대해 어떤 출력이 발생하는지를 명시한 문서이다.
회원 등록 API
@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();
}
매우 간단하게 작성이 가능하지만 엔티티를 직접 노출함으로써 다음과 같은 문제가 발생한다
@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 : 필요한 정보만 노출시키기