diff --git a/src/test/java/com/kakao/golajuma/common/BaseEntity.java b/src/test/java/com/kakao/golajuma/common/BaseEntity.java new file mode 100644 index 0000000..a5665ae --- /dev/null +++ b/src/test/java/com/kakao/golajuma/common/BaseEntity.java @@ -0,0 +1,40 @@ +package com.kakao.golajuma.common; + +import java.time.LocalDateTime; +import javax.persistence.Column; +import javax.persistence.EntityListeners; +import javax.persistence.MappedSuperclass; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@MappedSuperclass +@EntityListeners({AuditingEntityListener.class, SoftDeleteListener.class}) +@SuperBuilder(toBuilder = true) +public class BaseEntity { + + @CreatedDate + @Column(nullable = false, updatable = false) + private LocalDateTime createdDate; + + @LastModifiedDate + @Column(nullable = false) + private LocalDateTime updatedDate; + + @Builder.Default + @Column(nullable = false) + private Boolean deleted = false; + + public void delete() { + this.deleted = true; + } +} diff --git a/src/test/java/com/kakao/golajuma/common/SoftDeleteListener.java b/src/test/java/com/kakao/golajuma/common/SoftDeleteListener.java new file mode 100644 index 0000000..b6d4908 --- /dev/null +++ b/src/test/java/com/kakao/golajuma/common/SoftDeleteListener.java @@ -0,0 +1,11 @@ +package com.kakao.golajuma.common; + +import javax.persistence.PreRemove; + +public class SoftDeleteListener { + + @PreRemove + private void preRemove(BaseEntity entity) { + entity.delete(); + } +} diff --git a/src/test/java/com/kakao/golajuma/common/exception/BusinessException.java b/src/test/java/com/kakao/golajuma/common/exception/BusinessException.java new file mode 100644 index 0000000..dbf0b99 --- /dev/null +++ b/src/test/java/com/kakao/golajuma/common/exception/BusinessException.java @@ -0,0 +1,14 @@ +package com.kakao.golajuma.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class BusinessException extends RuntimeException { + private final HttpStatus httpStatus; + + public BusinessException(String message, HttpStatus httpStatus) { + super(message); + this.httpStatus = httpStatus; + } +} diff --git a/src/test/java/com/kakao/golajuma/common/exception/GlobalExceptionHandler.java b/src/test/java/com/kakao/golajuma/common/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..df64fed --- /dev/null +++ b/src/test/java/com/kakao/golajuma/common/exception/GlobalExceptionHandler.java @@ -0,0 +1,54 @@ +package com.kakao.golajuma.common.exception; + +import com.kakao.golajuma.common.support.respnose.ApiResponse; +import com.kakao.golajuma.common.support.respnose.ApiResponseBody.FailureBody; +import com.kakao.golajuma.common.support.respnose.ApiResponseGenerator; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.validation.BindException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; + +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + /** javax.validation.Valid 또는 @Validated binding error가 발생할 경우 */ + @ExceptionHandler(BindException.class) + protected ApiResponse handleBindException(BindException e) { + log.error("handleBindException", e); + return ApiResponseGenerator.fail(e.getMessage(), HttpStatus.BAD_REQUEST); + } + + /** 주로 @RequestParam enum으로 binding 못했을 경우 발생 */ + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + protected ApiResponse handleMethodArgumentTypeMismatchException( + MethodArgumentTypeMismatchException e) { + log.error("handleMethodArgumentTypeMismatchException", e); + return ApiResponseGenerator.fail(e.getMessage(), HttpStatus.BAD_REQUEST); + } + + /** 지원하지 않은 HTTP method 호출 할 경우 발생 */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + protected ApiResponse handleHttpRequestMethodNotSupportedException( + HttpRequestMethodNotSupportedException e) { + log.error("handleHttpRequestMethodNotSupportedException", e); + return ApiResponseGenerator.fail(e.getMessage(), HttpStatus.METHOD_NOT_ALLOWED); + } + + /** 비즈니스 로직 실행 중 오류 발생 */ + @ExceptionHandler(value = {BusinessException.class}) + protected ApiResponse handleConflict(BusinessException e) { + log.error("BusinessException", e); + return ApiResponseGenerator.fail(e.getMessage(), e.getHttpStatus()); + } + + /** 나머지 예외 발생 */ + @ExceptionHandler(Exception.class) + protected ApiResponse handleException(Exception e) { + log.error("Exception", e); + return ApiResponseGenerator.fail(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } +} diff --git a/src/test/java/com/kakao/golajuma/common/marker/AbstractDto.java b/src/test/java/com/kakao/golajuma/common/marker/AbstractDto.java new file mode 100644 index 0000000..a779491 --- /dev/null +++ b/src/test/java/com/kakao/golajuma/common/marker/AbstractDto.java @@ -0,0 +1,3 @@ +package com.kakao.golajuma.common.marker; + +public interface AbstractDto {} diff --git a/src/test/java/com/kakao/golajuma/common/marker/AbstractRequestDto.java b/src/test/java/com/kakao/golajuma/common/marker/AbstractRequestDto.java new file mode 100644 index 0000000..f19366e --- /dev/null +++ b/src/test/java/com/kakao/golajuma/common/marker/AbstractRequestDto.java @@ -0,0 +1,3 @@ +package com.kakao.golajuma.common.marker; + +public interface AbstractRequestDto extends AbstractDto {} diff --git a/src/test/java/com/kakao/golajuma/common/marker/AbstractResponseDto.java b/src/test/java/com/kakao/golajuma/common/marker/AbstractResponseDto.java new file mode 100644 index 0000000..cb77628 --- /dev/null +++ b/src/test/java/com/kakao/golajuma/common/marker/AbstractResponseDto.java @@ -0,0 +1,3 @@ +package com.kakao.golajuma.common.marker; + +public interface AbstractResponseDto extends AbstractDto {} diff --git a/src/test/java/com/kakao/golajuma/common/support/respnose/ApiResponse.java b/src/test/java/com/kakao/golajuma/common/support/respnose/ApiResponse.java new file mode 100644 index 0000000..fe4710d --- /dev/null +++ b/src/test/java/com/kakao/golajuma/common/support/respnose/ApiResponse.java @@ -0,0 +1,17 @@ +package com.kakao.golajuma.common.support.respnose; + +import lombok.Getter; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +@Getter +public class ApiResponse extends ResponseEntity { + + public ApiResponse(final HttpStatus status) { + super(status); + } + + public ApiResponse(final B body, final HttpStatus status) { + super(body, status); + } +} diff --git a/src/test/java/com/kakao/golajuma/common/support/respnose/ApiResponseBody.java b/src/test/java/com/kakao/golajuma/common/support/respnose/ApiResponseBody.java new file mode 100644 index 0000000..8c8ae11 --- /dev/null +++ b/src/test/java/com/kakao/golajuma/common/support/respnose/ApiResponseBody.java @@ -0,0 +1,23 @@ +package com.kakao.golajuma.common.support.respnose; + +import java.io.Serializable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +public class ApiResponseBody { + @Getter + @AllArgsConstructor + public static class FailureBody implements Serializable { + + private String code; + private String message; + } + + @Getter + @AllArgsConstructor + public static class SuccessBody implements Serializable { + private D data; + private String message; + private String code; + } +} diff --git a/src/test/java/com/kakao/golajuma/common/support/respnose/ApiResponseGenerator.java b/src/test/java/com/kakao/golajuma/common/support/respnose/ApiResponseGenerator.java new file mode 100644 index 0000000..ee5d454 --- /dev/null +++ b/src/test/java/com/kakao/golajuma/common/support/respnose/ApiResponseGenerator.java @@ -0,0 +1,44 @@ +package com.kakao.golajuma.common.support.respnose; + +import lombok.experimental.UtilityClass; +import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; + +@UtilityClass +public class ApiResponseGenerator { + public static ApiResponse> success( + final HttpStatus status, MessageCode code) { + return new ApiResponse<>( + new ApiResponseBody.SuccessBody<>(null, code.getMessage(), code.getCode()), status); + } + + public static ApiResponse> success( + final D data, final HttpStatus status, MessageCode code) { + return new ApiResponse<>( + new ApiResponseBody.SuccessBody<>(data, code.getMessage(), code.getCode()), status); + } + + public static ApiResponse>> success( + final Page data, final HttpStatus status, final MessageCode code) { + return new ApiResponse<>( + new ApiResponseBody.SuccessBody<>( + new PageResponse<>(data), code.getMessage(), code.getCode()), + status); + } + + public static ApiResponse fail( + final ApiResponseBody.FailureBody body, final HttpStatus status) { + return new ApiResponse<>(body, status); + } + + public static ApiResponse fail( + final String message, final HttpStatus status) { + return new ApiResponse<>( + new ApiResponseBody.FailureBody(String.valueOf(status.value()), message), status); + } + + public static ApiResponse fail( + final String code, final String message, final HttpStatus status) { + return new ApiResponse<>(new ApiResponseBody.FailureBody(code, message), status); + } +} diff --git a/src/test/java/com/kakao/golajuma/common/support/respnose/MessageCode.java b/src/test/java/com/kakao/golajuma/common/support/respnose/MessageCode.java new file mode 100644 index 0000000..2eb6809 --- /dev/null +++ b/src/test/java/com/kakao/golajuma/common/support/respnose/MessageCode.java @@ -0,0 +1,20 @@ +package com.kakao.golajuma.common.support.respnose; + +public enum MessageCode { + CREATE("200", "생성 성공"); + private final String code; + private final String message; + + MessageCode(String code, String message) { + this.code = code; + this.message = message; + } + + public String getCode() { + return code; + } + + public String getMessage() { + return message; + } +} diff --git a/src/test/java/com/kakao/golajuma/common/support/respnose/PageResponse.java b/src/test/java/com/kakao/golajuma/common/support/respnose/PageResponse.java new file mode 100644 index 0000000..6d84aa8 --- /dev/null +++ b/src/test/java/com/kakao/golajuma/common/support/respnose/PageResponse.java @@ -0,0 +1,30 @@ +package com.kakao.golajuma.common.support.respnose; + +import java.util.List; +import lombok.Getter; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +@Getter +public class PageResponse { + + /** 페이지를 구성하는 일정 수의 크기 */ + private final int pageSize; + /** 데이터를 가져온 페이지 번호 */ + private final int pageNumber; + /** size 크기에 맞춰 페이징했을 때 나오는 총 페이지 개수 */ + private final int totalPageCount; + /** 전체 데이터 개수 */ + private final Long totalCount; + + private final List data; + + public PageResponse(final Page source) { + final Pageable pageable = source.getPageable(); + this.pageSize = pageable.getPageSize(); + this.pageNumber = pageable.getPageNumber(); + this.totalPageCount = source.getTotalPages(); + this.totalCount = source.getTotalElements(); + this.data = source.getContent(); + } +}