1. API 컨트롤러를 분리한다.
- API를 제작하기 이전에 기본적인 CRUD기능을 다루는 컨트롤러를 생성했을 것이다.
- 그리고 API를 제작할 때에는 API 컨트롤러를 저장할 패키지를 따로 만들어 사용한다.
2. @Valid, @NotEmpty
a) @Valid
- API를 통해서 POST 메서드로 데이터를 DB에 저장하거나 변경할 때에는 @Valid와 @NotEmpty를 사용한다.
- @Valid는 javax 라이브러리의 기능으로 유효성을 검사하는 기능을 갖는다.
b) @NotEmpty
- @NotEmpty는 @Valid에서 검증하는 조건(= constraints) 중 하나다.
- @NotEmpty가 부착된 Entity의 필드 값을 required로 처리한다.
- 이처럼 반드시 필요한 값이 입력되어야 하는 경우, @Valid와 @NotEmpty를 사용하여 API의 요청을 검증할 수 있다.
c) 위 API 예시의 문제점
- 위의 예시처럼 Entity 레벨에서 validation을 수행하는 구조는 좋지 않다.
- Entity가 presentation 레이어에 노출되는 구조이기 때문이다.
- 더불어, 이와 같은 API 구조는 Entity의 내용이 변경되는 경우, API의 스펙 자체가 변경되기 때문이다.
- 그러므로 API 스펙을 위한 별도의 DTO를 생성하여 사용하는 것이 좋은 구조다.
- 즉, API에서 Entity를 직접적으로 파라미터로 받는 방식을 사용하지 말자.
3. Validation을 위한 올바른 API 구조
a) 올바른 API의 구조
- 위에서 설명한 내용을 보완하여 다음과 같이 API 구조를 설계하는 것을 권장한다.
@RestController // ==> @Controller + @ResponseBody 내부 확인하면 두 어노테이션이 존재함
@RequiredArgsConstructor
public class MemberApiController {
private final MemberService memberService;
@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 CreateMemberResponse {
private Long id;
public CreateMemberResponse(Long id) {
this.id = id;
}
}
@Data
static class CreateMemberRequest {
@NotEmpty
private String name;
}
}
b) 올바른 API의 구조적 특징 - Client에게 영향을 끼치지 않는다.
- 이와 같은 API 구조는 Entity의 필드명이 변경되어도 Client 측면(= API 호출자)에서 변경사항이 없다는 것이다.
- Entity의 필드명이 변경되더라도 Getter의 명칭만 변경하면 되기에 백엔드 측에서 코드를 수정하면 된다.
- 즉, Client는 기존에 사용하던 방식 그대로 API를 호출하면 된다.
c) 올바른 API의 구조적 특징 - Validation을 Entity에서 수행하지 않는다.
- Validation을 Entity 레벨이 아닌 DTO에서 수행하도록 설계하였다.
- 이와 같은 설계를 하는 이유는, 특정 API에서는 특정 필드에 대한 Validation이 필요할 수도 없을 수도 있기 때문이다.
- 위의 코드에서 CreateMemberRequest 클래스를 확인해보면 @NotEmpty가 있는 것을 확인할 수 있다.
- 이 validation은 saveMemberV2() 메서드에서 매개변수에 @Valid를 부착함으로써 활성화된다.
- 그러므로 기존에 Entity의 필드에 부착되었던 @NotEmpty는 없애도 된다.
- 이처럼 Validation을 Entity 레벨에서 수행하지 않고, 직접적인 Entity 사용 대신 DTO를 사용하도록 설계하여
Entity가 변경되어도 API의 스펙에 영향을 끼치지 않는 구조가 좋은 API의 구조적 특징이다.
d) 올바른 API의 구조적 특징 - DTO를 사용한다.
- 위의 코드에서는 DTO 역할을 하는 클래스를 static 클래스로 만들어 사용했다.
- 그러나 이들 또한 개별적인 클래스 파일로 분리하여 사용하는 것을 권장한다.
- 이처럼 DTO를 사용하는 이유는 API의 스펙을 쉽게 파악할 수 있고 유지보수에 용이하기 때문이다.
- API 메서드의 DTO를 살펴보면, 해당 API에서 어떤 데이터를 필요로 또는 필수로 하는지 쉽게 확인할 수 있다.
3. 주요 Annotation 정리
@RestController
- RestAPI 클래스를 작성할 때 사용하는 가장 기본적인 어노테이션
- @RestController는 내부에 @Controller와 @ResponseBody를 포함한다.
- 그러므로 @RestController 대신 @Controller와 @ResponseBody를 부착하면 동일한 역할을 한다.
- 다만, @RestController를 사용하는 것이 Semantic적으로 해당 클래스의 역할이 RestAPI라는 것을 쉽게 이해할 수 있다.
@Data
- DTO 클래스 생성 시 사용하는 어노테이션
- @Getter, @Setter, @RequiredArgsConstructor, @ToString, @EqualsAndHashCode가 포함되어 있다.
@AllArgsConstructor
- 클래스 내부의 모든 필드를 이용하여 생성자를 만드는 어노테이션
@RequiredArgsConstructor
- final이 붙은 필드를 이용하여 생성자를 만드는 어노테이션
@Valid
- 객체 또는 객체의 특정 필드에 대한 유효성을 검증하는 어노테이션
- javax.validation 라이브러리에 속해있는 기능이다.
- 특정 객체를 타고 들어가서 내부의 필드 값을 검증하는 방식으로 Cascading 기능을 가진다.
- 메서드의 매개변수가 객체인 경우에 매개변수에 부착하여 사용한다.
@NotEmpty
- @NotEmpty는 @Valid와 함께 쓰이는 대표적인 어노테이션
- 객체의 필드에 부착하여 사용하며, 해당 필드 값을 required로 처리하는 기능이다.
- 어떤 메서드의 매개변수로 넘어오는 객체에 @Valid를 부착하면, 해당 객체의 필드를 확인한다.
- 해당 객체의 필드에 @NotEmpty가 부착되어 있다면, 유효성 기준으로 해당 필드 값의 유무를 판단한다.
@RequestBody
- API 메서드의 매개변수에 부착되는 어노테이션
- HTTP request에서 넘어오는 데이터를 받는 매개변수라는 것을 명시하는 기능이다.
- HTTP request를 받는 객체이므로, 해당 매개변수는 일반적으로 DTO가 할당된다.
- 일반적으로 조회(= Get 방식)를 제외한 모든 HTTP 메서드의 API를 구현할 때 사용된다.
'Back-end > JPA 개념' 카테고리의 다른 글
API 조회 성능 최적화 - DTO 변환 방식(Fetch Join) (0) | 2022.05.30 |
---|---|
API 조회 성능 최적화 - Entity 직접 노출 방식 (0) | 2022.05.28 |
JPA 개발 꿀팁 (0) | 2022.05.25 |
35. JPQL - 벌크 연산 (0) | 2022.05.08 |
34. JPQL - 다형성 쿼리, Entity 직접 사용, Named 쿼리 (0) | 2022.05.07 |
댓글