Skip to content

Commit

Permalink
Merge pull request #10 from Central-MakeUs/feature/3
Browse files Browse the repository at this point in the history
Feature/3: api ์‘๋‹ต ํ†ต์ผ, global exception ์ฒ˜๋ฆฌ
  • Loading branch information
yxhwxn authored Jul 17, 2024
2 parents b3b34de + ee53e65 commit 23e13b0
Show file tree
Hide file tree
Showing 20 changed files with 469 additions and 8 deletions.
File renamed without changes.
File renamed without changes.
10 changes: 5 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.0'
id 'io.spring.dependency-management' version '1.1.5'
id 'org.springframework.boot' version '2.7.7'
id 'io.spring.dependency-management' version '1.1.4'
}

group = 'khu.bigdata'
group = 'com.cmc'
version = '0.0.1-SNAPSHOT'

java {
Expand All @@ -27,10 +27,10 @@ repositories {

dependencies {
implementation 'javax.servlet:javax.servlet-api:4.0.1'
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springdoc:springdoc-openapi-ui:1.6.11'
implementation 'org.springframework.boot:spring-boot-starter-validation'

compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/cmc/suppin/global/config/SwaggerConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ public class SwaggerConfig {
@Bean
public OpenAPI suppinAPI() {
Info info = new Info()
.title("suppin API")
.description("suppin API ๋ช…์„ธ์„œ")
.title("Suppin API")
.description("Suppin API ๋ช…์„ธ์„œ")
.version("1.0.0");

String jwtSchemeName = "JWT TOKEN";
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/com/cmc/suppin/global/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.cmc.suppin.global.config;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.Arrays;


@RequiredArgsConstructor
@Configuration
public class WebConfig implements WebMvcConfigurer {


@Bean
public UrlBasedCorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource corsConfigSource = new UrlBasedCorsConfigurationSource();


CorsConfiguration configuration = new CorsConfiguration();

configuration.addAllowedOriginPattern("*");
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);

corsConfigSource.registerCorsConfiguration("/**", configuration);
return corsConfigSource;
}
}
7 changes: 7 additions & 0 deletions src/main/java/com/cmc/suppin/global/exception/BaseCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.cmc.suppin.global.exception;

public interface BaseCode {
public ReasonDTO getReason();

public ReasonDTO getReasonHttpStatus();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.cmc.suppin.global.exception;

public interface BaseErrorCode {

public ErrorReasonDTO getReason();

public ErrorReasonDTO getReasonHttpStatus();
}
23 changes: 23 additions & 0 deletions src/main/java/com/cmc/suppin/global/exception/ErrorReasonDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.cmc.suppin.global.exception;

import lombok.Builder;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@Builder
public class ErrorReasonDTO {

private final HttpStatus httpStatus;

private final boolean isSuccess;
private final String code;
private final String message;

private final Integer status;
private final String reason;

public boolean getIsSuccess() {
return isSuccess;
}
}
115 changes: 115 additions & 0 deletions src/main/java/com/cmc/suppin/global/exception/ExceptionAdvice.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package com.cmc.suppin.global.exception;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import com.cmc.suppin.global.exception.status.ErrorStatus;
import com.cmc.suppin.global.presentation.ApiResponse;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;

@Slf4j
@RestControllerAdvice(annotations = {RestController.class})
public class ExceptionAdvice extends ResponseEntityExceptionHandler {

@org.springframework.web.bind.annotation.ExceptionHandler
public ResponseEntity<Object> validation(ConstraintViolationException e, WebRequest request) {
String errorMessage = e.getConstraintViolations().stream()
.map(constraintViolation -> constraintViolation.getMessage())
.findFirst()
.orElseThrow(() -> new RuntimeException("ConstraintViolationException ์ถ”์ถœ ๋„์ค‘ ์—๋Ÿฌ ๋ฐœ์ƒ"));

return handleExceptionInternalConstraint(e, ErrorStatus.valueOf(errorMessage), HttpHeaders.EMPTY, request);
}

@Override
public ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException e, HttpHeaders headers, HttpStatus status, WebRequest request) {

Map<String, String> errors = new LinkedHashMap<>();

e.getBindingResult().getFieldErrors().stream()
.forEach(fieldError -> {
String fieldName = fieldError.getField();
String errorMessage = Optional.ofNullable(fieldError.getDefaultMessage()).orElse("");
errors.merge(fieldName, errorMessage, (existingErrorMessage, newErrorMessage) -> existingErrorMessage + ", " + newErrorMessage);
});

return handleExceptionInternalArgs(e, HttpHeaders.EMPTY, ErrorStatus.valueOf("_BAD_REQUEST"), request, errors);
}

@org.springframework.web.bind.annotation.ExceptionHandler
public ResponseEntity<Object> exception(Exception e, WebRequest request) {
e.printStackTrace();

return handleExceptionInternalFalse(e, ErrorStatus._INTERNAL_SERVER_ERROR, HttpHeaders.EMPTY, ErrorStatus._INTERNAL_SERVER_ERROR.getHttpStatus(), request, e.getMessage());
}

@ExceptionHandler(value = GeneralException.class)
public ResponseEntity onThrowException(GeneralException generalException, HttpServletRequest request) {
ErrorReasonDTO errorReasonHttpStatus = generalException.getErrorReasonHttpStatus();
return handleExceptionInternal(generalException, errorReasonHttpStatus, null, request);
}

private ResponseEntity<Object> handleExceptionInternal(Exception e, ErrorReasonDTO reason, HttpHeaders headers, HttpServletRequest request) {
ApiResponse<Object> body = ApiResponse.onFailure(reason.getCode(), reason.getMessage(), null);
// e.printStackTrace();

WebRequest webRequest = new ServletWebRequest(request);
return super.handleExceptionInternal(
e,
body,
headers,
reason.getHttpStatus(),
webRequest
);
}

private ResponseEntity<Object> handleExceptionInternalFalse(Exception e, ErrorStatus errorCommonStatus,
HttpHeaders headers, HttpStatus status, WebRequest request, String errorPoint) {
ApiResponse<Object> body = ApiResponse.onFailure(errorCommonStatus.getCode(), errorCommonStatus.getMessage(), errorPoint);
return super.handleExceptionInternal(
e,
body,
headers,
status,
request
);
}

private ResponseEntity<Object> handleExceptionInternalArgs(Exception e, HttpHeaders headers, ErrorStatus errorCommonStatus,
WebRequest request, Map<String, String> errorArgs) {
ApiResponse<Object> body = ApiResponse.onFailure(errorCommonStatus.getCode(), errorCommonStatus.getMessage(), errorArgs);
return super.handleExceptionInternal(
e,
body,
headers,
errorCommonStatus.getHttpStatus(),
request
);
}

private ResponseEntity<Object> handleExceptionInternalConstraint(Exception e, ErrorStatus errorCommonStatus,
HttpHeaders headers, WebRequest request) {
ApiResponse<Object> body = ApiResponse.onFailure(errorCommonStatus.getCode(), errorCommonStatus.getMessage(), null);
return super.handleExceptionInternal(
e,
body,
headers,
errorCommonStatus.getHttpStatus(),
request
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.cmc.suppin.global.exception;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class GeneralException extends RuntimeException {

private BaseErrorCode code;

public ErrorReasonDTO getErrorReason() {
return this.code.getReason();
}

public ErrorReasonDTO getErrorReasonHttpStatus() {
return this.code.getReasonHttpStatus();
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/cmc/suppin/global/exception/ReasonDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.cmc.suppin.global.exception;

import lombok.Builder;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@Builder
public class ReasonDTO {

private HttpStatus httpStatus;

private final boolean isSuccess;
private final String code;
private final String message;

public boolean getIsSuccess() {
return isSuccess;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.cmc.suppin.global.exception.handler;

public class GlobalExceptionHandler {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.cmc.suppin.global.exception.handler;

import com.cmc.suppin.global.exception.BaseErrorCode;
import com.cmc.suppin.global.exception.GeneralException;

public class JwtHandler extends GeneralException {
public JwtHandler(BaseErrorCode code) {
super(code);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.cmc.suppin.global.exception.status;

import com.cmc.suppin.global.exception.BaseErrorCode;
import com.cmc.suppin.global.exception.ErrorReasonDTO;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum ErrorStatus implements BaseErrorCode {

// ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ์—๋Ÿฌ
_INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON500", "์„œ๋ฒ„ ์—๋Ÿฌ, ๊ด€๋ฆฌ์ž์—๊ฒŒ ๋ฌธ์˜ ๋ฐ”๋ž๋‹ˆ๋‹ค."),
_BAD_REQUEST(HttpStatus.BAD_REQUEST, "COMMON400", "์ž˜๋ชป๋œ ์š”์ฒญ์ž…๋‹ˆ๋‹ค."),
_UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "COMMON401", "์ธ์ฆ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค."),
_FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON403", "๊ธˆ์ง€๋œ ์š”์ฒญ์ž…๋‹ˆ๋‹ค."),


// test
TEMP_EXCEPTION(HttpStatus.BAD_REQUEST, "TEMP4001", "ํ…Œ์ŠคํŠธ"),

// Member
MEMBER_NICKNAME_DUPLICATED(HttpStatus.BAD_REQUEST, "MEMBER4001", "์ค‘๋ณต๋œ ๋‹‰๋„ค์ž„ ์ž…๋‹ˆ๋‹ค."),
MEMBER_PASSWORD_ERROR(HttpStatus.BAD_REQUEST, "MEMBER4002", "๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค."),

//JWT
JWT_BAD_REQUEST(HttpStatus.UNAUTHORIZED, "JWT4001", "์ž˜๋ชป๋œ JWT ์„œ๋ช…์ž…๋‹ˆ๋‹ค."),
JWT_ACCESS_TOKEN_EXPIRED(HttpStatus.UNAUTHORIZED, "JWT4002", "์•ก์„ธ์Šค ํ† ํฐ์ด ๋งŒ๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."),
JWT_REFRESH_TOKEN_EXPIRED(HttpStatus.UNAUTHORIZED, "JWT4003", "๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์ด ๋งŒ๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค."),
JWT_UNSUPPORTED_TOKEN(HttpStatus.UNAUTHORIZED, "JWT4004", "์ง€์›ํ•˜์ง€ ์•Š๋Š” JWT ํ† ํฐ์ž…๋‹ˆ๋‹ค."),
JWT_TOKEN_NOT_FOUND(HttpStatus.UNAUTHORIZED, "JWT4005", "์œ ํšจํ•œ JWT ํ† ํฐ์ด ์—†์Šต๋‹ˆ๋‹ค."),

// ํŽ˜์ด์ง• ๊ด€๋ จ ์—๋Ÿฌ
PAGE_NEGATIVE_INPUT(HttpStatus.BAD_REQUEST, "PAGE4001", "ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ๋Š” 1์ด์ƒ์˜ ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."),
;

private final HttpStatus httpStatus;
private final String code;
private final String message;

@Override
public ErrorReasonDTO getReason() {
return ErrorReasonDTO.builder()
.message(message)
.code(code)
.isSuccess(false)
.build();
}

@Override
public ErrorReasonDTO getReasonHttpStatus() {
return ErrorReasonDTO.builder()
.message(message)
.code(code)
.isSuccess(false)
.httpStatus(httpStatus)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.cmc.suppin.global.exception.status;

import com.cmc.suppin.global.exception.BaseCode;
import com.cmc.suppin.global.exception.ReasonDTO;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum SuccessStatus implements BaseCode {
_OK(HttpStatus.OK, "COMMON200", "์„ฑ๊ณต์ž…๋‹ˆ๋‹ค."),

//Member
MEMBER_DELETE_SUCCESS(HttpStatus.OK, "MEMBER2001", "ํšŒ์› ํƒˆํ‡ด ์„ฑ๊ณต์ž…๋‹ˆ๋‹ค."),
;

private final HttpStatus httpStatus;
private final String code;
private final String message;

@Override
public ReasonDTO getReason() {
return ReasonDTO.builder()
.message(message)
.code(code)
.isSuccess(true)
.build();
}

@Override
public ReasonDTO getReasonHttpStatus() {
return ReasonDTO.builder()
.message(message)
.code(code)
.isSuccess(true)
.httpStatus(httpStatus)
.build();
}
}
Loading

0 comments on commit 23e13b0

Please sign in to comment.