Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] 공통 클래스 & 예외 핸들링 클래스 세팅 #4

Merged
merged 10 commits into from
Dec 31, 2023
4 changes: 3 additions & 1 deletion doorip-api/src/main/java/org/doorip/DooripApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@SpringBootApplication(
scanBasePackageClasses = {DomainRoot.class, CommonRoot.class, ExternalRoot.class}
)
public class DooripApplication {
public static void main(String[] args) {
SpringApplication.run(DooripApplication.class, args);
Expand Down
44 changes: 44 additions & 0 deletions doorip-api/src/main/java/org/doorip/common/ApiResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.doorip.common;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import org.doorip.message.ErrorMessage;
import org.doorip.message.SuccessMessage;

@Builder(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Getter
public class ApiResponse<T> {
private final int status;
@JsonInclude(value = JsonInclude.Include.NON_NULL)
private final String code;
private final String message;
@JsonInclude(value = JsonInclude.Include.NON_NULL)
private final T data;

public static ApiResponse<?> of(SuccessMessage successMessage) {
return builder()
.status(successMessage.getHttpStatus().value())
.message(successMessage.getMessage())
.build();
}

public static <T> ApiResponse<?> of(SuccessMessage successMessage, T data) {
return builder()
.status(successMessage.getHttpStatus().value())
.message(successMessage.getMessage())
.data(data)
.build();
}

public static ApiResponse<?> of(ErrorMessage errorMessage) {
return builder()
.status(errorMessage.getHttpStatus().value())
.code(errorMessage.getCode())
.message(errorMessage.getMessage())
.build();
}
}
22 changes: 22 additions & 0 deletions doorip-api/src/main/java/org/doorip/common/ApiResponseUtil.java
SunwoongH marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.doorip.common;

import org.doorip.message.ErrorMessage;
import org.doorip.message.SuccessMessage;
import org.springframework.http.ResponseEntity;

public interface ApiResponseUtil {
static ResponseEntity<ApiResponse<?>> success(SuccessMessage successMessage) {
return ResponseEntity.status(successMessage.getHttpStatus())
.body(ApiResponse.of(successMessage));
}

static <T> ResponseEntity<ApiResponse<?>> success(SuccessMessage successMessage, T data) {
return ResponseEntity.status(successMessage.getHttpStatus())
.body(ApiResponse.of(successMessage, data));
}

static ResponseEntity<ApiResponse<?>> failure(ErrorMessage errorMessage) {
return ResponseEntity.status(errorMessage.getHttpStatus())
.body(ApiResponse.of(errorMessage));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.doorip.common;

import lombok.extern.slf4j.Slf4j;
import org.doorip.exception.BusinessException;
import org.doorip.message.ErrorMessage;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<ApiResponse<?>> handleMethodArgumentNotValidException(final MethodArgumentNotValidException e) {
log.error(">>> handle: MethodArgumentNotValidException ", e);
return ApiResponseUtil.failure(ErrorMessage.BAD_REQUEST);
}

@ExceptionHandler(BindException.class)
protected ResponseEntity<ApiResponse<?>> handleBindException(final BindException e) {
log.error(">>> handle: BindException ", e);
return ApiResponseUtil.failure(ErrorMessage.BAD_REQUEST);
}

@ExceptionHandler(MethodArgumentTypeMismatchException.class)
protected ResponseEntity<ApiResponse<?>> handleMethodArgumentTypeMismatchException(final MethodArgumentTypeMismatchException e) {
log.error(">>> handle: MethodArgumentTypeMismatchException ", e);
return ApiResponseUtil.failure(ErrorMessage.BAD_REQUEST);
}

@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
protected ResponseEntity<ApiResponse<?>> handleHttpRequestMethodNotSupportedException(final HttpRequestMethodNotSupportedException e) {
log.error(">>> handle: HttpRequestMethodNotSupportedException ", e);
return ApiResponseUtil.failure(ErrorMessage.METHOD_NOT_ALLOWED);
}

@ExceptionHandler(BusinessException.class)
protected ResponseEntity<ApiResponse<?>> handleBusinessException(final BusinessException e) {
log.error(">>> handle: BusinessException ", e);
return ApiResponseUtil.failure(e.getErrorMessage());
}

@ExceptionHandler(Exception.class)
protected ResponseEntity<ApiResponse<?>> handleException(final Exception e) {
log.error(">>> handle: Exception ", e);
return ApiResponseUtil.failure(ErrorMessage.INTERNAL_SERVER_ERROR);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.doorip;
package org.doorip.common;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.doorip.exception;

import lombok.Getter;
import org.doorip.message.ErrorMessage;

@Getter
public class BusinessException extends RuntimeException {
private final ErrorMessage errorMessage;

public BusinessException(ErrorMessage errorMessage) {
super(errorMessage.getMessage());
this.errorMessage = errorMessage;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.doorip.exception;

import org.doorip.message.ErrorMessage;

public class ConflictException extends BusinessException {
public ConflictException() {
super(ErrorMessage.CONFLICT);
}

public ConflictException(ErrorMessage errorMessage) {
super(errorMessage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.doorip.exception;

import org.doorip.message.ErrorMessage;

public class EntityNotFoundException extends BusinessException {
public EntityNotFoundException() {
super(ErrorMessage.ENTITY_NOT_FOUND);
}

public EntityNotFoundException(ErrorMessage errorMessage) {
super(errorMessage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.doorip.exception;

import org.doorip.message.ErrorMessage;

public class InvalidValueException extends BusinessException {
public InvalidValueException() {
super(ErrorMessage.BAD_REQUEST);
}

public InvalidValueException(ErrorMessage errorMessage) {
super(errorMessage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.doorip.exception;

import org.doorip.message.ErrorMessage;

public class UnauthorizedException extends BusinessException {
public UnauthorizedException() {
super(ErrorMessage.UNAUTHORIZED);
}

public UnauthorizedException(ErrorMessage errorMessage) {
super(errorMessage);
}
}
49 changes: 49 additions & 0 deletions doorip-common/src/main/java/org/doorip/message/ErrorMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.doorip.message;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;

@Getter
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public enum ErrorMessage {
/**
* 400 Bad Request
*/
BAD_REQUEST(HttpStatus.BAD_REQUEST, "e4000", "잘못된 요청입니다."),

/**
* 401 Unauthorized
*/
UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "e4010", "리소스 접근 권한이 없습니다."),

/**
* 403 Forbidden
*/
FORBIDDEN(HttpStatus.FORBIDDEN, "e4030", "리소스 접근 권한이 없습니다."),

/**
* 404 Not Found
*/
ENTITY_NOT_FOUND(HttpStatus.NOT_FOUND, "e4040", "대상을 찾을 수 없습니다."),

/**
* 405 Method Not Allowed
*/
METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, "e4050", "잘못된 HTTP method 요청입니다."),

/**
* 409 Conflict
*/
CONFLICT(HttpStatus.CONFLICT, "e4090", "이미 존재하는 리소스입니다."),

/**
* 500 Internal Server Error
*/
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "e5000", "서버 내부 오류입니다.");

private final HttpStatus httpStatus;
private final String code;
private final String message;
}
23 changes: 23 additions & 0 deletions doorip-common/src/main/java/org/doorip/message/SuccessMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.doorip.message;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;

@Getter
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public enum SuccessMessage {
/**
* 200 Ok
*/
OK(HttpStatus.OK, "요청이 성공했습니다."),

/**
* 201 Created
*/
CREATED(HttpStatus.CREATED, "요청이 성공했습니다.");

private final HttpStatus httpStatus;
private final String message;
}
22 changes: 22 additions & 0 deletions doorip-domain/src/main/java/org/doorip/common/BaseTimeEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.doorip.common;

import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;

@Getter
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public abstract class BaseTimeEntity {
@Column(updatable = false)
@CreatedDate
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime lastModifiedDate;
}
9 changes: 9 additions & 0 deletions doorip-domain/src/main/java/org/doorip/config/JpaConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.doorip.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@EnableJpaAuditing
@Configuration
public class JpaConfig {
}