From 162aec5cae87ae6b5c929a90df8d5cb8cb43f752 Mon Sep 17 00:00:00 2001 From: ymkim97 Date: Fri, 27 Oct 2023 18:26:12 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20Exception=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../error/exception/BadRequestException.java | 10 +++ .../error/exception/ConflictException.java | 10 +++ .../error/exception/ForbiddenException.java | 10 +++ .../error/exception/MoabamException.java | 13 ++++ .../error/exception/NotFoundException.java | 13 ++++ .../exception/UnauthorizedException.java | 10 +++ .../error/handler/GlobalExceptionHandler.java | 68 +++++++++++++++++++ .../global/error/model/ErrorMessage.java | 13 ++++ .../global/error/model/ErrorResponse.java | 11 +++ 9 files changed, 158 insertions(+) create mode 100644 src/main/java/com/moabam/global/error/exception/BadRequestException.java create mode 100644 src/main/java/com/moabam/global/error/exception/ConflictException.java create mode 100644 src/main/java/com/moabam/global/error/exception/ForbiddenException.java create mode 100644 src/main/java/com/moabam/global/error/exception/MoabamException.java create mode 100644 src/main/java/com/moabam/global/error/exception/NotFoundException.java create mode 100644 src/main/java/com/moabam/global/error/exception/UnauthorizedException.java create mode 100644 src/main/java/com/moabam/global/error/handler/GlobalExceptionHandler.java create mode 100644 src/main/java/com/moabam/global/error/model/ErrorMessage.java create mode 100644 src/main/java/com/moabam/global/error/model/ErrorResponse.java diff --git a/src/main/java/com/moabam/global/error/exception/BadRequestException.java b/src/main/java/com/moabam/global/error/exception/BadRequestException.java new file mode 100644 index 00000000..e0826af2 --- /dev/null +++ b/src/main/java/com/moabam/global/error/exception/BadRequestException.java @@ -0,0 +1,10 @@ +package com.moabam.global.error.exception; + +import com.moabam.global.error.model.ErrorMessage; + +public class BadRequestException extends MoabamException { + + public BadRequestException(ErrorMessage errorMessage) { + super(errorMessage); + } +} diff --git a/src/main/java/com/moabam/global/error/exception/ConflictException.java b/src/main/java/com/moabam/global/error/exception/ConflictException.java new file mode 100644 index 00000000..fe756197 --- /dev/null +++ b/src/main/java/com/moabam/global/error/exception/ConflictException.java @@ -0,0 +1,10 @@ +package com.moabam.global.error.exception; + +import com.moabam.global.error.model.ErrorMessage; + +public class ConflictException extends MoabamException { + + public ConflictException(ErrorMessage errorMessage) { + super(errorMessage); + } +} diff --git a/src/main/java/com/moabam/global/error/exception/ForbiddenException.java b/src/main/java/com/moabam/global/error/exception/ForbiddenException.java new file mode 100644 index 00000000..05ca2c3c --- /dev/null +++ b/src/main/java/com/moabam/global/error/exception/ForbiddenException.java @@ -0,0 +1,10 @@ +package com.moabam.global.error.exception; + +import com.moabam.global.error.model.ErrorMessage; + +public class ForbiddenException extends MoabamException { + + public ForbiddenException(ErrorMessage errorMessage) { + super(errorMessage); + } +} diff --git a/src/main/java/com/moabam/global/error/exception/MoabamException.java b/src/main/java/com/moabam/global/error/exception/MoabamException.java new file mode 100644 index 00000000..e4a8b5ec --- /dev/null +++ b/src/main/java/com/moabam/global/error/exception/MoabamException.java @@ -0,0 +1,13 @@ +package com.moabam.global.error.exception; + +import com.moabam.global.error.model.ErrorMessage; + +public class MoabamException extends RuntimeException { + + private final ErrorMessage errorMessage; + + public MoabamException(ErrorMessage errorMessage) { + super(errorMessage.getMessage()); + this.errorMessage = errorMessage; + } +} diff --git a/src/main/java/com/moabam/global/error/exception/NotFoundException.java b/src/main/java/com/moabam/global/error/exception/NotFoundException.java new file mode 100644 index 00000000..08273e0e --- /dev/null +++ b/src/main/java/com/moabam/global/error/exception/NotFoundException.java @@ -0,0 +1,13 @@ +package com.moabam.global.error.exception; + +import com.moabam.global.error.model.ErrorMessage; + +import lombok.Getter; + +@Getter +public class NotFoundException extends MoabamException { + + public NotFoundException(ErrorMessage errorMessage) { + super(errorMessage); + } +} diff --git a/src/main/java/com/moabam/global/error/exception/UnauthorizedException.java b/src/main/java/com/moabam/global/error/exception/UnauthorizedException.java new file mode 100644 index 00000000..9f99a3e8 --- /dev/null +++ b/src/main/java/com/moabam/global/error/exception/UnauthorizedException.java @@ -0,0 +1,10 @@ +package com.moabam.global.error.exception; + +import com.moabam.global.error.model.ErrorMessage; + +public class UnauthorizedException extends MoabamException { + + public UnauthorizedException(ErrorMessage errorMessage) { + super(errorMessage); + } +} diff --git a/src/main/java/com/moabam/global/error/handler/GlobalExceptionHandler.java b/src/main/java/com/moabam/global/error/handler/GlobalExceptionHandler.java new file mode 100644 index 00000000..2653c8d5 --- /dev/null +++ b/src/main/java/com/moabam/global/error/handler/GlobalExceptionHandler.java @@ -0,0 +1,68 @@ +package com.moabam.global.error.handler; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.http.HttpStatus; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import com.moabam.global.error.exception.BadRequestException; +import com.moabam.global.error.exception.ConflictException; +import com.moabam.global.error.exception.ForbiddenException; +import com.moabam.global.error.exception.MoabamException; +import com.moabam.global.error.exception.NotFoundException; +import com.moabam.global.error.exception.UnauthorizedException; +import com.moabam.global.error.model.ErrorMessage; +import com.moabam.global.error.model.ErrorResponse; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ResponseStatus(HttpStatus.NOT_FOUND) + @ExceptionHandler(NotFoundException.class) + protected ErrorResponse handleNotFoundException(MoabamException moabamException) { + return new ErrorResponse(moabamException.getMessage(), null); + } + + @ResponseStatus(HttpStatus.UNAUTHORIZED) + @ExceptionHandler(UnauthorizedException.class) + protected ErrorResponse handleUnauthorizedException(MoabamException moabamException) { + return new ErrorResponse(moabamException.getMessage(), null); + } + + @ResponseStatus(HttpStatus.NOT_FOUND) + @ExceptionHandler(ForbiddenException.class) + protected ErrorResponse handleForbiddenException(MoabamException moabamException) { + return new ErrorResponse(moabamException.getMessage(), null); + } + + @ResponseStatus(HttpStatus.CONFLICT) + @ExceptionHandler(ConflictException.class) + protected ErrorResponse handleConflictException(MoabamException moabamException) { + return new ErrorResponse(moabamException.getMessage(), null); + } + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(BadRequestException.class) + protected ErrorResponse handleBadRequestException(MoabamException moabamException) { + return new ErrorResponse(moabamException.getMessage(), null); + } + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(MethodArgumentNotValidException.class) + public ErrorResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException moabamException) { + List fieldErrors = moabamException.getBindingResult().getFieldErrors(); + Map validation = new HashMap<>(); + + for (FieldError fieldError : fieldErrors) { + validation.put(fieldError.getField(), fieldError.getDefaultMessage()); + } + + return new ErrorResponse(ErrorMessage.INVALID_REQUEST_FIELD.getMessage(), validation); + } +} diff --git a/src/main/java/com/moabam/global/error/model/ErrorMessage.java b/src/main/java/com/moabam/global/error/model/ErrorMessage.java new file mode 100644 index 00000000..662874f6 --- /dev/null +++ b/src/main/java/com/moabam/global/error/model/ErrorMessage.java @@ -0,0 +1,13 @@ +package com.moabam.global.error.model; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum ErrorMessage { + + INVALID_REQUEST_FIELD("올바른 요청 정보가 아닙니다."); + + private final String message; +} diff --git a/src/main/java/com/moabam/global/error/model/ErrorResponse.java b/src/main/java/com/moabam/global/error/model/ErrorResponse.java new file mode 100644 index 00000000..b4349e9c --- /dev/null +++ b/src/main/java/com/moabam/global/error/model/ErrorResponse.java @@ -0,0 +1,11 @@ +package com.moabam.global.error.model; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonInclude; + +public record ErrorResponse( + String message, + @JsonInclude(JsonInclude.Include.NON_EMPTY) Map validation +) { +} From 800b9f387216d0f9534c099f80b39bfd8e28f1d3 Mon Sep 17 00:00:00 2001 From: ymkim97 Date: Fri, 27 Oct 2023 18:26:24 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20Config=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/moabam/global/config/JpaConfig.java | 23 +++++++++++++++++++ .../com/moabam/global/config/WebConfig.java | 23 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 src/main/java/com/moabam/global/config/JpaConfig.java create mode 100644 src/main/java/com/moabam/global/config/WebConfig.java diff --git a/src/main/java/com/moabam/global/config/JpaConfig.java b/src/main/java/com/moabam/global/config/JpaConfig.java new file mode 100644 index 00000000..9f0b6906 --- /dev/null +++ b/src/main/java/com/moabam/global/config/JpaConfig.java @@ -0,0 +1,23 @@ +package com.moabam.global.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +import com.querydsl.jpa.impl.JPAQueryFactory; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; + +@Configuration +@EnableJpaAuditing +public class JpaConfig { + + @PersistenceContext + private EntityManager entityManager; + + @Bean + public JPAQueryFactory jpaQueryFactory() { + return new JPAQueryFactory(entityManager); + } +} diff --git a/src/main/java/com/moabam/global/config/WebConfig.java b/src/main/java/com/moabam/global/config/WebConfig.java new file mode 100644 index 00000000..2f276c96 --- /dev/null +++ b/src/main/java/com/moabam/global/config/WebConfig.java @@ -0,0 +1,23 @@ +package com.moabam.global.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + private static final String ALLOWED_METHOD_NAMES = "GET,HEAD,POST,PUT,DELETE,TRACE,OPTIONS,PATCH"; + private static final String ALLOW_ORIGIN_PATTERN = "[a-z]+\\.moabam.com"; + private static final String ALLOW_LOCAL_HOST = "http://localhost:3000"; + + @Override + public void addCorsMappings(final CorsRegistry registry) { + registry.addMapping("/**") + .allowedOriginPatterns(ALLOW_ORIGIN_PATTERN, ALLOW_LOCAL_HOST) + .allowedMethods(ALLOWED_METHOD_NAMES.split(",")) + .allowedHeaders("*") + .allowCredentials(true) + .maxAge(3600); + } +} From 4d6c3c7b79510648c388a191390239d5b32b74f9 Mon Sep 17 00:00:00 2001 From: ymkim97 Date: Fri, 27 Oct 2023 18:26:35 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20Entity=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/common/entity/BaseTimeEntity.java | 29 +++++++++++++++ .../global/common/util/DynamicQuery.java | 37 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/main/java/com/moabam/global/common/entity/BaseTimeEntity.java create mode 100644 src/main/java/com/moabam/global/common/util/DynamicQuery.java diff --git a/src/main/java/com/moabam/global/common/entity/BaseTimeEntity.java b/src/main/java/com/moabam/global/common/entity/BaseTimeEntity.java new file mode 100644 index 00000000..07b008fa --- /dev/null +++ b/src/main/java/com/moabam/global/common/entity/BaseTimeEntity.java @@ -0,0 +1,29 @@ +package com.moabam.global.common.entity; + +import java.time.LocalDateTime; + +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@MappedSuperclass +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@EntityListeners(AuditingEntityListener.class) +public abstract class BaseTimeEntity { + + @CreatedDate + @Column(name = "created_at", updatable = false, nullable = false) + private LocalDateTime createdAt; + + @LastModifiedDate + @Column(name = "updated_at") + private LocalDateTime updatedAt; +} diff --git a/src/main/java/com/moabam/global/common/util/DynamicQuery.java b/src/main/java/com/moabam/global/common/util/DynamicQuery.java new file mode 100644 index 00000000..31fa8bb9 --- /dev/null +++ b/src/main/java/com/moabam/global/common/util/DynamicQuery.java @@ -0,0 +1,37 @@ +package com.moabam.global.common.util; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; + +import org.springframework.util.CollectionUtils; + +import com.querydsl.core.types.dsl.BooleanExpression; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public class DynamicQuery { + + public static BooleanExpression generateEq(T value, Function function) { + if (Objects.isNull(value)) { + return null; + } + + return function.apply(value); + } + + public static BooleanExpression filterCondition(T condition, Function function) { + T tempCondition = condition; + + if (tempCondition instanceof List c && CollectionUtils.isEmpty(c)) { + tempCondition = null; + } + + return Optional.ofNullable(tempCondition) + .map(function) + .orElse(null); + } +}