4번째 필수 강좌 Part 1. ch3. 6~9강 요약
지난 번에는 H2 DB가 제대로 되는지 확인하기 위해서 대충 GET 으로 새로운 데이터를 저장하는 메서드를 만들었다. 이번에는 제대로 POST 메서드를 사용해 @RequestBody로 내용을 받도록 바꾸어본다.
먼저 CreateDeveloper 클래스에 Request, Response 객체를 만들고 getter, setter, constructor 메서드를 생성해준다.
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
public class Request {
@NotNull
private DeveloperLevel developerLevel;
@NotNull
private DeveloperSkillType developerSkillType;
@NotNull
@Min(0)
@Max(20)
private Integer experienceYears;
@NotNull
@Size(min=3, max=50, message = "memberId size must be between 3 and 50.")
private String memberId;
@NotNull
@Size(min=3, max=20, message = "name size must be between 3 and 50.")
private String name;
@Min(18)
private Integer age;
}
* Response도 똑같지만, name과 age를 제외하였다.
Request는 프론트에서 받아올 정보이고, Response는 서버에서 프론트로 보낼 정보라고 생각하면 되겠다. 추가로, @Validation도 함께 진행한다.
이후 DMakerController의 POST 메서드 파라미터를 수정한다.
@PostMapping("/create-dev")
public void createDeveloper(@RequestBody @Valid CreateDeveloper.Request request){
log.info("request: ",request);
dMakerService.createDeveloper(request);
}
DMakerService에는 이미 지난번에 만들어둔 createDeveloper가 있다. Builder 패턴 안에 하드코딩했던 값들을 request 파라미터에서 받아오도록 바꿔준다.
그런데 Spring Validation만으로 완벽히 검증이 완료되진 않는다. DeveloperLevel에는 NEW, JUNIOR, JUNGIOR, SENIOR가 있다. 경력 4년 이상부터는 JUNGNIOR, 10년부터는 SENIOR이라고 하면, 경력 10년 이상인 JUNIOR는 존재할 수 없다. 이는 일반적인 Spring Validation이라기보다는 구현하고 있는 비즈니스 로직에 따라 달라지는 비즈니스 검증이다.
경력과 레벨이 맞지 않는 경우에는 예외가 발생한다. 이를 기존의 예외로 처리할 수도 있겠지만, 따로 예외를 만들어서 사용할 수도 있다.
먼저 Enum으로 DMakerErrorCode를 만든다.
@Getter
@AllArgsConstructor
public enum DMakerErrorCode {
NO_DEVELOPER("해당되는 개발자가 없습니다."),
DUPLICATED_MEMBER_ID("MemberId가 중복된 개발자가 있습니다."),
LEVEL_EXPERIENCE_YEARS_NOT_MATCHED("개발자 레벨과 연차가 맞지 않습니다."),
INTERNAL_SERVER_ERROR("서버에 오류가 발생했습니다."),
INVALID_REQUEST("잘못된 요청입니다.")
;
private final String message;
}
내용을 수정할 필요는 없으므로 Getter와 생성자만 만들어준다. 그 다음으로 이 ErrorCode를 받는 DMakerException을 만든다.
@Getter
public class DMakerException extends RuntimeException{
private DMakerErrorCode dMakerErrorCode;
private String detailMessage;
public DMakerException(DMakerErrorCode errorCode){
super(errorCode.getMessage());
this.dMakerErrorCode = errorCode;
this.detailMessage = errorCode.getMessage();
}
public DMakerException(DMakerErrorCode errorCode, String detailMessage){
super(errorCode.getMessage());
this.dMakerErrorCode = errorCode;
this.detailMessage = detailMessage;
}
}
RuntimeException을 상속하였으며, 역시 내용을 따로 수정할 일이 없어 Getter만 가지고 있다. ErrorCode만 받는 생성자와, ErrorCode와 detailMessage를 함께 받는 생성자 메서드 2가지를 가지고 있다.
DMakerService로 돌아가서 이 비즈니스 검증을 하는 메서드를 만든다.
private void validateCreateDeveloperRequest(CreateDeveloper.Request request){
// business validation
if((request.getDeveloperLevel()==DeveloperLevel.SENIOR)
&&request.getExperienceYears()<10) {
// throw new RuntimeException("SENIOR needs 10 years of experience.");
throw new DMakerException(DMakerErrorCode.LEVEL_EXPERIENCE_YEARS_NOT_MATCHED);
}
if ((request.getDeveloperLevel()==DeveloperLevel.JUNGNIOR)&&
( request.getExperienceYears() < 4 || request.getExperienceYears() > 10)){
throw new DMakerException(DMakerErrorCode.LEVEL_EXPERIENCE_YEARS_NOT_MATCHED);
}
if ((request.getDeveloperLevel()==DeveloperLevel.JUNIOR) &&
(request.getExperienceYears() > 4)){
throw new DMakerException(DMakerErrorCode.LEVEL_EXPERIENCE_YEARS_NOT_MATCHED);
}
}
전달받은 request 객체의 DeveloperLevel과 ExperienceYears를 비교해서 비즈니스 로직과 일치하지 않는 경우에는 만든 예외를 던진다.
추가로, JPA 쿼리메서드를 사용해 MemberId를 받아 중복된 Id일 경우 예외를 던지도록 만든다.
Optional<Developer> byMemberId = developerRepository.findByMemberId(request.getMemberId());
if (byMemberId.isPresent()) throw new DMakerException(DMakerErrorCode.DUPLICATED_MEMBER_ID);
이를 1문장으로 바꿀 수 있다.
developerRepository.findByMemberId(request.getMemberId())
.ifPresent(developer -> {
throw new DMakerException(DMakerErrorCode.DUPLICATED_MEMBER_ID);
});