[Study-4주차] MapStruct, Exception 적용 + CRUD 수정 + Controller 수정 + ResponseFormat - ①
[Study-4주차] MapStruct, Exception 적용 + CRUD 수정 - ①
[Study-3주차] PetClinicProject CRUD - ② [Study-3주차] PetClinicProject CRUD - ② [Study-3주차] PetClinicProject CRUD - ① [Study-3주차] PetClinicProject CRUD - ① [Study-2주차] PetClinicProject 초기 설정 + 클래스 생성 + 코드 설
soohykeee.tistory.com
이번에는 Exception 적용과 Controller 수정, Controller에서 사용할 ResponseEntity를 커스텀한 ResponseFormat, ReponseStatus를 사용해볼 것이다.
ResponseFormat
해당 코드를 보기 전, ResponseEntity에 대해 먼저 알아보는 것이 좋다.
[Study] ResponseEntity 란 무엇인가?
[Study] ResponseEntity 란 무엇인가?
[개념] REST, REST API, RESTful 이란 무엇인가? [개념] REST, REST API, RESTful 이란 무엇인가? REST란 ? REST란 Representational State Tranfer의 약자로, 자원을 이름으로 구분하여 해당 자원의 상태(정보)를 주고받는
soohykeee.tistory.com
package kr.co.jshpetclinicstudy.infra.model;
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ResponseFormat<T> {
private boolean isSuccessful;
private Optional<T> data;
private String message;
private HttpStatus statusCode;
// Success - If the ResponseStatus is declared + return Only Message
public static <T> ResponseFormat<T> success(ResponseStatus responseStatus) {
return ResponseFormat.<T>builder()
.isSuccessful(true)
.data(Optional.empty())
.message(responseStatus.getMessage())
.statusCode(responseStatus.getStatusCode())
.build();
}
// Success - If the ResponseStatus is Not declared + return Only Message
public static <T> ResponseFormat<T> success(String message,
HttpStatus httpStatus) {
return ResponseFormat.<T>builder()
.isSuccessful(true)
.data(Optional.empty())
.message(message)
.statusCode(httpStatus)
.build();
}
// Success - If the ResponseStatus is declared + return Message And Data
public static <T> ResponseFormat<T> successWithData(ResponseStatus responseStatus,
T data) {
return ResponseFormat.<T>builder()
.isSuccessful(true)
.data(Optional.ofNullable(data))
.message(responseStatus.getMessage())
.statusCode(responseStatus.getStatusCode())
.build();
}
// Success - If the ResponseStatus is Not declared + return Message And Data
public static <T> ResponseFormat<T> successWithData(String message,
HttpStatus httpStatus,
T data) {
return ResponseFormat.<T>builder()
.isSuccessful(true)
.data(Optional.ofNullable(data))
.message(message)
.statusCode(httpStatus)
.build();
}
// Failed - If the ResponseStatus is declared
public static <T> ResponseFormat<T> error(ResponseStatus responseStatus) {
return ResponseFormat.<T>builder()
.isSuccessful(false)
.data(Optional.empty())
.message(responseStatus.getMessage())
.statusCode(responseStatus.getStatusCode())
.build();
}
// Failed - If the ResponseStatus is Not declared
public static <T> ResponseFormat<T> error(String message,
HttpStatus httpStatus) {
return ResponseFormat.<T>builder()
.isSuccessful(false)
.data(Optional.empty())
.message(message)
.statusCode(httpStatus)
.build();
}
}
package kr.co.jshpetclinicstudy.infra.model;
@Getter
@AllArgsConstructor
@NoArgsConstructor
public enum ResponseStatus {
// Success Status
SUCCESS_OK("요청이 성공적으로 처리되었습니다.", HttpStatus.OK),
SUCCESS_CREATE("요청이 성공적으로 처리되어 새로운 리소스가 생성되었습니다.", HttpStatus.CREATED),
SUCCESS_ACCEPTED("요청이 성공적으로 처리되었지만, 결과가 아직 완료되지 않았습니다.", HttpStatus.ACCEPTED),
SUCCESS_NO_CONTENT("요청이 성공적으로 처리되었지만, 응답 데이터가 없습니다.", HttpStatus.NO_CONTENT),
// Failed Status
FAIL_BAD_REQUEST("클라이언트의 요청이 잘못되었습니다.", HttpStatus.BAD_REQUEST),
FAIL_UNAUTHORIZED("클라이언트가 인증되지 않았습니다.", HttpStatus.UNAUTHORIZED),
FAIL_FORBIDDEN("클라이언트가 요청한 리소스에 접근할 권한이 없습니다.", HttpStatus.FORBIDDEN),
FAIL_NOT_FOUND("클라이언트가 요청한 리소스를 찾을 수 없습니다.", HttpStatus.NOT_FOUND),
FAIL_METHOD_NOT_ALLOWED("클라이언트가 요청한 HTTP 메소드가 허용되지 않았습니다.", HttpStatus.METHOD_NOT_ALLOWED),
// Owner Failed Status
FAIL_TELEPHONE_DUPLICATED("클라이언트의 전화번호가 중복되었습니다.", HttpStatus.BAD_REQUEST);
private String message;
private HttpStatus statusCode;
}
Exception
package kr.co.jshpetclinicstudy.infra.exception;
public class BusinessLogicException extends RuntimeException {
private ResponseStatus responseStatus;
public BusinessLogicException(ResponseStatus responseStatus) {
super(responseStatus.getMessage());
this.responseStatus = responseStatus;
}
public BusinessLogicException(String message) {
super(message);
}
}
package kr.co.jshpetclinicstudy.infra.exception;
/**
* 이미 존재하는 리소스를 생성하려 할 때 사용하는 예외
*/
public class DuplicatedException extends BusinessLogicException {
public DuplicatedException(ResponseStatus responseStatus) {
super(responseStatus);
}
public DuplicatedException(String message) {
super(message);
}
}
package kr.co.jshpetclinicstudy.infra.exception;
/**
* 인증된 사용자가 권한이 없는 리소스에 접근하려 할 때 사용하는 예외
*/
public class ForbiddenException extends BusinessLogicException {
public ForbiddenException(ResponseStatus responseStatus) {
super(responseStatus);
}
public ForbiddenException(String message) {
super(message);
}
}
package kr.co.jshpetclinicstudy.infra.exception;
/**
* 클라이언트의 요청이 잘못되었을 때 사용하는 예외
*/
public class InvalidRequestException extends BusinessLogicException {
public InvalidRequestException(ResponseStatus responseStatus) {
super(responseStatus);
}
public InvalidRequestException(String message) {
super(message);
}
}
package kr.co.jshpetclinicstudy.infra.exception;
/**
* 요청한 리소스가 존재하지 않을 때 사용하는 예외
*/
public class NotFoundException extends BusinessLogicException{
public NotFoundException(ResponseStatus responseStatus) {
super(responseStatus);
}
public NotFoundException(String message) {
super(message);
}
}
package kr.co.jshpetclinicstudy.infra.exception;
/**
* 인증되지 않은 사용자가 보호된 리소스에 접근하려 할 때 사용하는 예외
*/
public class UnauthorizedException extends BusinessLogicException {
public UnauthorizedException(ResponseStatus responseStatus) {
super(responseStatus);
}
public UnauthorizedException(String message) {
super(message);
}
}
Controller
OwnerController
package kr.co.jshpetclinicstudy.controller;
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/owners")
public class OwnerController {
private final OwnerService ownerService;
/**
* Create Owner API
*
* @param create
* @return
*/
@PostMapping
public ResponseFormat<Void> createOwner(@RequestBody @Valid OwnerRequestDto.CREATE create){
try {
ownerService.createOwner(create);
return ResponseFormat.success(ResponseStatus.SUCCESS_CREATE);
} catch (DuplicatedException e) {
return ResponseFormat.error(ResponseStatus.FAIL_TELEPHONE_DUPLICATED);
} catch (RuntimeException e) {
return ResponseFormat.error(ResponseStatus.FAIL_BAD_REQUEST);
}
}
/**
* Read(Get) Owner API
*
* @param ownerId
* @return
*/
@GetMapping("/{owner_id}")
public ResponseFormat<OwnerResponseDto.READ> getOwner(@PathVariable(name = "owner_id") Long ownerId) {
try {
return ResponseFormat.successWithData(ResponseStatus.SUCCESS_OK, ownerService.getOwner(ownerId));
} catch (NotFoundException e) {
return ResponseFormat.error(ResponseStatus.FAIL_NOT_FOUND);
} catch (RuntimeException e) {
return ResponseFormat.error(ResponseStatus.FAIL_BAD_REQUEST);
}
}
/**
* Update Owner API
*
* @param update
* @return
*/
@PutMapping
public ResponseFormat<Void> updateOwner(@RequestBody @Valid OwnerRequestDto.UPDATE update) {
try {
ownerService.updateOwner(update);
return ResponseFormat.success(ResponseStatus.SUCCESS_NO_CONTENT);
} catch (DuplicatedException e) {
return ResponseFormat.error(ResponseStatus.FAIL_TELEPHONE_DUPLICATED);
} catch (NotFoundException e) {
return ResponseFormat.error(ResponseStatus.FAIL_NOT_FOUND);
} catch (RuntimeException e) {
return ResponseFormat.error(ResponseStatus.FAIL_BAD_REQUEST);
}
}
/**
* Delete Owner API
*
* @param ownerId
* @return
*/
@DeleteMapping("/{owner_id}")
public ResponseFormat<Void> deleteOwner(@PathVariable(name = "owner_id") Long ownerId) {
try {
ownerService.deleteOwner(ownerId);
return ResponseFormat.success(ResponseStatus.SUCCESS_NO_CONTENT);
} catch (NotFoundException e) {
return ResponseFormat.error(ResponseStatus.FAIL_NOT_FOUND);
} catch (RuntimeException e) {
return ResponseFormat.error(ResponseStatus.FAIL_BAD_REQUEST);
}
}
}
PetController
package kr.co.jshpetclinicstudy.controller;
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/pets")
public class PetController {
private final PetService petService;
/**
* Create Pet API
*
* @param create
* @return
*/
@PostMapping
public ResponseFormat<Void> createPet(@RequestBody @Valid PetRequestDto.CREATE create) {
try {
petService.createPet(create);
return ResponseFormat.success(ResponseStatus.SUCCESS_CREATE);
} catch (NotFoundException e) {
return ResponseFormat.error(ResponseStatus.FAIL_NOT_FOUND);
} catch (RuntimeException e) {
return ResponseFormat.error(ResponseStatus.FAIL_BAD_REQUEST);
}
}
/**
* Read(Get) Pet API
*
* @param petId
* @return
*/
@GetMapping("/{pet_id}")
public ResponseFormat<PetResponseDto.READ> getPet(@PathVariable(name = "pet_id") Long petId) {
try {
return ResponseFormat.successWithData(ResponseStatus.SUCCESS_OK, petService.getPet(petId));
} catch (NotFoundException e) {
return ResponseFormat.error(ResponseStatus.FAIL_NOT_FOUND);
} catch (RuntimeException e) {
return ResponseFormat.error(ResponseStatus.FAIL_BAD_REQUEST);
}
}
/**
* Read(Get) PetList Of Owner API
*
* @param ownerId
* @return
*/
@GetMapping("/owner/{owner_id}")
public ResponseFormat<List<PetResponseDto.READ>> getPetListOfOwner(@PathVariable(name = "owner_id") Long ownerId) {
try {
return ResponseFormat.successWithData(ResponseStatus.SUCCESS_OK, petService.getPetListOfOwner(ownerId));
} catch (NotFoundException e) {
return ResponseFormat.error(ResponseStatus.FAIL_NOT_FOUND);
} catch (RuntimeException e) {
return ResponseFormat.error(ResponseStatus.FAIL_BAD_REQUEST);
}
}
/**
* Update Pet API
*
* @param update
* @return
*/
@PutMapping
public ResponseFormat<Void> updatePet(@RequestBody @Valid PetRequestDto.UPDATE update) {
try {
petService.updatePet(update);
return ResponseFormat.success(ResponseStatus.SUCCESS_NO_CONTENT);
} catch (NotFoundException e) {
return ResponseFormat.error(ResponseStatus.FAIL_NOT_FOUND);
} catch (RuntimeException e) {
return ResponseFormat.error(ResponseStatus.FAIL_BAD_REQUEST);
}
}
/**
* Delete Pet API
*
* @param petId
* @return
*/
@DeleteMapping("/{pet_id}")
public ResponseFormat<Void> deletePet(@PathVariable(name = "pet_id") Long petId) {
try {
petService.deletePet(petId);
return ResponseFormat.success(ResponseStatus.SUCCESS_NO_CONTENT);
} catch (NotFoundException e) {
return ResponseFormat.error(ResponseStatus.FAIL_NOT_FOUND);
} catch (RuntimeException e) {
return ResponseFormat.error(ResponseStatus.FAIL_BAD_REQUEST);
}
}
}
VetController
package kr.co.jshpetclinicstudy.controller;
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/vets")
public class VetController {
private final VetService vetService;
/**
* Create Vet + Specialty API
*
* @param create
* @return
*/
@PostMapping
public ResponseFormat<Void> createVet(@RequestBody @Valid VetRequestDto.CREATE create) {
try {
vetService.createVet(create);
return ResponseFormat.success(ResponseStatus.SUCCESS_CREATE);
} catch (RuntimeException e) {
return ResponseFormat.error(ResponseStatus.FAIL_BAD_REQUEST);
}
}
/**
* Get Vet API
*
* @param vetId
* @return
*/
@GetMapping("/{vet_id}")
public ResponseFormat<VetResponseDto.READ> getVet(@PathVariable(name = "vet_id") Long vetId) {
try {
return ResponseFormat.successWithData(ResponseStatus.SUCCESS_OK, vetService.getVet(vetId));
} catch (NotFoundException e) {
return ResponseFormat.error(ResponseStatus.FAIL_NOT_FOUND);
} catch (RuntimeException e) {
return ResponseFormat.error(ResponseStatus.FAIL_BAD_REQUEST);
}
}
/**
* Update Vet API
*
* @param update
* @return
*/
@PutMapping
public ResponseFormat<Void> updateVet(@RequestBody @Valid VetRequestDto.UPDATE update) {
try {
vetService.updateVet(update);
return ResponseFormat.success(ResponseStatus.SUCCESS_NO_CONTENT);
} catch (NotFoundException e) {
return ResponseFormat.error(ResponseStatus.FAIL_NOT_FOUND);
} catch (RuntimeException e) {
return ResponseFormat.error(ResponseStatus.FAIL_BAD_REQUEST);
}
}
/**
* Delete Vet API
*
* @param vetId
* @return
*/
@DeleteMapping("/{vet_id}")
public ResponseFormat<Void> deleteVet(@PathVariable(name = "vet_id") Long vetId) {
try {
vetService.deleteVet(vetId);
return ResponseFormat.success(ResponseStatus.SUCCESS_NO_CONTENT);
} catch (NotFoundException e) {
return ResponseFormat.error(ResponseStatus.FAIL_NOT_FOUND);
} catch (RuntimeException e) {
return ResponseFormat.error(ResponseStatus.FAIL_BAD_REQUEST);
}
}
}
VisitController
package kr.co.jshpetclinicstudy.controller;
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/visits")
public class VisitController {
private final VisitService visitService;
/**
* Create Visit API
*
* @param create
* @return
*/
@PostMapping
public ResponseFormat<Void> createVisit(@RequestBody @Valid VisitRequestDto.CREATE create) {
try {
visitService.createVisit(create);
return ResponseFormat.success(ResponseStatus.SUCCESS_CREATE);
} catch (NotFoundException e) {
return ResponseFormat.error(ResponseStatus.FAIL_NOT_FOUND);
} catch (RuntimeException e) {
return ResponseFormat.error(ResponseStatus.FAIL_BAD_REQUEST);
}
}
/**
* Read(Get) Visit API
*
* @param visitId
* @return
*/
@GetMapping("/{visit_id}")
public ResponseFormat<VisitResponseDto.READ> getVisit(@PathVariable(name = "visit_id") Long visitId) {
try {
return ResponseFormat.successWithData(ResponseStatus.SUCCESS_OK, visitService.getVisit(visitId));
} catch (NotFoundException e) {
return ResponseFormat.error(ResponseStatus.FAIL_NOT_FOUND);
} catch (RuntimeException e) {
return ResponseFormat.error(ResponseStatus.FAIL_BAD_REQUEST);
}
}
/**
* Read(Get) VisitList Of Pet API
*
* @param petId
* @return
*/
@GetMapping("/pets/{pet_id}")
public ResponseFormat<List<VisitResponseDto.READ>> getVisitListOfPet(@PathVariable(name = "pet_id") Long petId) {
try {
return ResponseFormat.successWithData(ResponseStatus.SUCCESS_OK, visitService.getVisitListOfPet(petId));
} catch (NotFoundException e) {
return ResponseFormat.error(ResponseStatus.FAIL_NOT_FOUND);
} catch (RuntimeException e) {
return ResponseFormat.error(ResponseStatus.FAIL_BAD_REQUEST);
}
}
/**
* Update Visit API
*
* @param update
* @return
*/
@PutMapping
public ResponseFormat<Void> updateVisit(@RequestBody @Valid VisitRequestDto.UPDATE update) {
try {
visitService.updateVisit(update);
return ResponseFormat.success(ResponseStatus.SUCCESS_NO_CONTENT);
} catch (NotFoundException e) {
return ResponseFormat.error(ResponseStatus.FAIL_NOT_FOUND);
} catch (RuntimeException e) {
return ResponseFormat.error(ResponseStatus.FAIL_BAD_REQUEST);
}
}
/**
* Delete Visit API
*
* @param visitId
* @return
*/
@DeleteMapping("/{visit_id}")
public ResponseFormat<Void> deleteVisit(@PathVariable(name = "visit_id") Long visitId) {
try {
visitService.deleteVisit(visitId);
return ResponseFormat.success(ResponseStatus.SUCCESS_NO_CONTENT);
} catch (NotFoundException e) {
return ResponseFormat.error(ResponseStatus.FAIL_NOT_FOUND);
} catch (RuntimeException e) {
return ResponseFormat.error(ResponseStatus.FAIL_BAD_REQUEST);
}
}
}