API 개발
- API tool - postman
- API 개발 시 템플릿 엔진(화면) 사용하는 Controller와 API를 사용하는 Controller를 패키지를 통해 분리하는 것이 좋다.
- 공통 처리를 할 때 패키지 단위로 많이 하는데 화면과 API는 공통 처리해야 할 부분이 많이 다르기 때문
- @ResponseBody : 자바 객체를 json 기반의 HTTP Body로 변환한다.
- 주로 데이터 자체를 바로 Json을 XML로 보낼 때 사용한다.
- @RequestBody : json 기반의 HTTP Body를 자바 객체로 변환한다.
- @RestController : @Controller + @ResponseBody 어노테이션을 합친 어노테이션이다.
- @Data : @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructor를 합쳐놓은 어노테이션이다.
- POJO (Plain Old Java Objects)와 bean과 관련된 모든 보일러플레이트(boilerplate = 재사용 가능한 코드)를 생성한다.
// 1번 예시
@PostMapping("/api/v1/members")
public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member) {
Long id = memberService.join(member);
return new CreateMemberResponse(id);
}
// 2번 예시
@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);
}
- 1번 예제의 잘못된 점은 Member 엔티티를 API에서 사용하고 있다.
- 엔티티는 여기저기서 많이 사용되기에 바뀔 확률이 높다.
- 엔티티를 외부에서 Json을 바인딩 받는 용도로 사용해서는 안된다.
- 엔티티가 변경되면 API 스펙도 변경되기에 올바른 설계 방식이 아니다.
=> 2번 예제와 같이 엔티티와 API 스펙을 명확히 분리하기 위한 별도의 DTO를 만들어서 사용해야 한다. - 엔티티를 매개변수로 받으면 엔티티의 어떤 필드가 값으로 넘어오는지 확인하지 않는 이상 알 수 없다.
- DTO를 사용하면 어떤 값들이 매개변수로 넘어오는지 알기 쉽다.
- Validation 같은 경우에도 각 API 스펙별로 요구하는 검증 조건이 다를 수 있기에 각 API에 맞는 DTO를 생성하고 Validation 조건을 지정해야 한다.
- API 설계 시 주의 사항
- API 설계 시 엔티티를 매개변수로 받지 말 것!!!
- 엔티티를 외부에 노출시키지 말 것!!!!
- 일반적으로 JSON의 키 값은 자바 객체의 필드명과 자동으로 매핑된다.
- @JsonIgnore : 직렬화, 역직렬화에 사용되는 논리적 속성 값을 무시할 때 사용한다.
- @JsonIgnore가 사용된 필드는 JSON 데이터에 포함되지 않는다.
@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 Result<T> {
private T data;
}
@Data
@AllArgsConstructor
static class MemberDto {
private String name;
}
- Result라는 Object 타입으로 한 번 감싸서 반환하는 이유
- 컬렉션을 바로 반환하면 JSON 배열 타입이 되기 때문에 유연성이 떨어진다.
//컬렉션(List)으로 반환한 JSON 결과
[
{
"name": "hello"
},
{
"name": "member1"
},
{
"name": "member2"
}
]
//컬렉션을 Object로 감싼 후 반환한 JSON 결과
{
"data": [
{
"name": "hello"
},
{
"name": "member1"
},
{
"name": "member2"
}
]
}
- 대괄호 [] - 배열, 중괄호 {} - 각 DTO 객체, 중괄호 안 "name" ~ : 각 객체의 필드와 값
- 컬렉션을 반환했을 때의 결과와 같이 대괄호[ ](배열)이 첫 시작인 경우 확장할 수 없다.
=> 해당 객체 외에 다른 값을 넣을 수 없다.
@GetMapping("/api/v2/members")
public Result memberV2() {
// 이전 예제와 동일
return new Result(collect.size(), collect);
}
@Data
@AllArgsConstructor
static class Result<T> {
private int count;
private T data;
}
//JSON 결과
{
"count": 3,
"data": [
{
"name": "hello"
},
{
"name": "member1"
},
{
"name": "member2"
}
]
}
- 컬렉션을 감싸면 유연한 이유? => 확장하기에 유연하다.
- 컬렉션 사이즈와 같은 값들도 함께 반환해줄 수 있다.
- 컬렉션을 감싸지 않고 반환하면 JSON 배열 타입으로 전달되기 때문에 확장할 수 없다.
출처: [인프런 김영한 실전 스프링 부트와 JPA 활용 2 - API 개발과 성능 최적화]
실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화 강의 | 김영한 - 인프런
김영한 | 스프링 부트와 JPA를 활용해서 API를 개발합니다. 그리고 JPA 극한의 성능 최적화 방법을 학습할 수 있습니다., 스프링 부트, 실무에서 잘 쓰고 싶다면? 복잡한 문제까지 해결하는 힘을 길
www.inflearn.com