[Spring, JPA]를 이용한 API 개발 시에는, 그 만의 주의점이나 특징들이 잇다.
이 게시물에서는 [Spring, JPA]를 사용하기 때문에 생기는 특징들에 대해 주목해 보자.
1] JSP,ThymeLeaf 등의 템플릿 엔진을 이용해서 [html 페이지]를 전달하는 API Controoler 패키지와
2] JSON 등의 객체 메시지를 전달하는 API(Rest API) Controller의 패키지를 분리를 한다고 한다(PostMan을 적극 사용해
야 함.
html 페이지가 전달되는 것이 아니기 때문에 브라우저로 API를 호출하고 하는 것보다는 POSTMAN을 사용하는 것이 더 좋
다)
이유 1] 페이지를 전달하는 API와 JSON 객체를 전달하는 API는 서로 적용해야 할 [공통 예외 처리]가 다르다.
예를 들어, 페이지를 전달하는 API의 경우에 만약 페이지 렌더링 과정에서 에러가 발생을 하면,
[에러 페이지]를 띄워 주는 형식으로 예외 처리를 해야 하지만, JSON 객체 메시지에서 에러가 발생을 하면,
에러 페이지를 띄우는 것이 아니라, [객체 에러 메시지]를 띄워 줘야 한다.
-> 이러듯, 둘 사이에 [예외 처리] 방식이 다르기에, API 별로 패키지를 분리하여서 [공통 예외 처리]를 해준다.
이유 2] Rest API 개발은 [도메인 엔티티]에 주의해서 개발해야 한다는 점에서 조금 주의를 해서 개발해야 한다(아래에서
자세히 설명)
이유 3] 실무에서의 Rest API 개발은, 단순히 아래 그림과 같이, 단 1개의 [도메인 엔티티]만을 사용해서 개발할 수 있을 정
도로 단순한 구조가 아니다. (아래에서 자세히 설명)
이유 4] [도메인 엔티티]를 사용하게 되면, 클라이언트들이 도메인 엔티티의 어떠한 속성들이 있는지 알 수가 있으므로, 해
커들에게 악이용 당할 수가 있다.(아래에서 설명)
이유 5] [도메인 엔티티]로 Rest API를 개발하게 되면, 도메인 엔티티의 속성값 중, 어떤 값들이 클라이언트로 넘어 오는지
API 스펙 문서를 보기 이전에는 알지를 못한다.
(예를 들어, Member 엔티티(도메인 엔티티)에 name과 address라는 속성값이 있다고 하자.
Rest API를 호출 시, 이 중 2 속성 모두의 값을 넣어 보낼 수도 있고, 1개만 넣어서 호출할 수도 있기에,
개발자 입장에서는 답답하다.
그러나 아래와 같이 DTO로 데이터를 받게 되면, 한 눈에 API 스펙을 알 수가 있게 된다.)
- Rest API는 절대 [도메인 엔티티]로 Json을 매핑 or 반환해서는 안된다 -
@RestController
@RequiredArgsConstructor
public class MemberApiController { // Rest API용 Controller이다(여기서는 JSON을 사용한다고 가정)
private final MemberService memberService;
@PostMapping("/api/v1/members") // JSON으로 온 HTTP 메시지를 [Member 엔티티]로 [자동] 매핑.
public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member){
Long id = memberService.join(member);
return new CreateMemberResponse(id); // 반대로, CreateMemberResponse 객체를 [자동]으로 JSON으로 매핑하여 반환.
}
Member class는 [도메인 엔티티]인데,
Rest API용 Controller에서 [Member 엔티티]로 클라이언트로부터 Rest API 호출을 받고 있다.
( Member 엔티티는 name 속성 단 1개만 가지고 있다고 가정을 하자 )
그런데, 만약 개발자가 도중에 "아~~ Member 엔티티의 "name"이라고 짓어 놓은 속성명을 음.. "username"으로 바꾸고 싶
은 걸??"이라고 해서, Member :: name -> Member :: username으로 [변경]을 해버리면 어떻게 될까???
Rest API 호출하는 클라이언트 위와 같이 [기존]의 Rest API로 호출을 했는데, 반환을 받지 못할 것이다.
왜냐하면, 호출하는 클라이언트 입장에서는 Member의 name이 username으로 바뀐 줄을 모르기 때문이다.
-> [도메인 엔티티]로 Rest API를 개발하게 되면, [기존]의 Rest API 스펙이 바뀌어 버리기 때문에, 실무에서 Rest API를
개발할 때에는, 절~~~~~~~~대, [도메인 엔티티]를 사용해서 개발하면 안된다.
Q. 그러면, 개발 도중 필연적으로 [도메인 엔티티]를 바꿔야 하는 상황에서도 [기존]의 Rest API가 잘 동작하도록 하기 위
해서는 어떻게 해야 할까??
A. Rest API 스펙과 도메인 엔티티가 1: 1 매핑 관계가 되지 않도록, DTO(Data Transfering Object)를 만들어야 한다.
@PostMapping("/api/v1/members")
public CreateMemberResponse saveMemberV1(@RequestBody @Valid MemberDTO memberDTO){ // DTO 객체
//DTO를 [도메인 엔티티]로 매핑을 해줌으로서 username으로 속성명이 변경되어도 Rest API 정상 동작
Member member = new Member();
memberDTO.setName(memberDTO.getUsername()); // getUsername() 사용
Long id = memberService.join(member);
return new CreateMemberResponse(id);
}
static class MemberDTO{ // 이 DTO 객체가 Rest API 스펙이 되며, 이 속성값들은 절대 바뀌면 안된다
private String name;
String setName(String name){
this.name = name;
}
}
또 다른 이유도 있다.
위와 같이 [도메인 엔티티]를 사용해서 회원가입 API( saveMember() )를 만들었다고 치자.
요즘에는, 페이스북, 카카오톡, 인스타그램, 네이버, 다음, 등 수많은 방법으로 회원 가입을 한다.
그럼 그 수많은 회원 가입 정보의 형태가 있을 것인데, Member 엔티티(도메인 엔티티)하나로 커버가 될까??
-> 정~~~~~~~~~말 간단한 회원가입 기능이 아니면, 단 1개의 도메인 엔티티로 Rest API를 구현하는 것은 불가능.
(많은 DTO 객체를 만들어 줘야 할 것이다)
'CS 잡지식' 카테고리의 다른 글
CQS(Command Query Separation) Policy (0) | 2023.05.03 |
---|---|
@AllArgsConstructor과 @RequiredConstructor의 차이 (2) | 2023.05.03 |
DB에 저장(save)를 하고 나면, 반드시 id값을 반환을 하자!! (0) | 2023.05.03 |
스프링 부트 3.X.X 버전에 따른 설정 변경 (0) | 2023.05.03 |
JPA [수정(update)]의 2가지 전략(feat. dirty checking, 병합(merge) ) (0) | 2023.05.02 |