Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
plugins {
id 'java'
id 'org.springframework.boot' version '3.4.2'
id 'io.spring.dependency-management' version '1.1.7'
id 'org.springframework.boot' version '3.3.1'
id 'io.spring.dependency-management' version '1.1.5'
}

group = 'hyu.erica'
Expand Down Expand Up @@ -33,6 +33,8 @@ dependencies {
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
// h2
implementation 'com.h2database:h2'
// Swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.4'
}

tasks.named('test') {
Expand Down
63 changes: 63 additions & 0 deletions src/main/java/hyu/erica/capstone/api/ApiResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package hyu.erica.capstone.api;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import hyu.erica.capstone.api.code.BaseCode;
import hyu.erica.capstone.api.code.status.ErrorStatus;
import hyu.erica.capstone.api.code.status.SuccessStatus;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
@JsonPropertyOrder({"isSuccess", "code", "message", "result"})
public class ApiResponse<T> {

@JsonProperty("isSuccess")
private final boolean isSuccess; // 성공 여부
private final String code; // 응답 코드
private final String message; // 응답 메시지

@JsonInclude(JsonInclude.Include.NON_NULL)
private T result; // 응답 데이터


@JsonProperty("isSuccess")
public boolean isSuccess() {
return isSuccess;
}

// 성공한 경우 응답 생성
public static <T> ApiResponse<T> onSuccess(SuccessStatus code, T result) {
return new ApiResponse<>(true, code.getCode(), code.getMessage(), result);

}

public static <T> ApiResponse<T> of(BaseCode code, T result) {
return new ApiResponse<>(true, code.getReasonHttpStatus().getCode(), code.getReasonHttpStatus().getMessage(), result);
}

// 성공한 경우 응답 생성 (Void 타입 지원)
public static ApiResponse<Void> onSuccess(SuccessStatus code) {
return new ApiResponse<>(true, code.getCode(), code.getMessage(), null);
}

// 실패한 경우 응답 생성
public static <T> ApiResponse<T> onFailure(String code, String message, T data) {
return new ApiResponse<>(false, code, message, data);
}

public ApiResponse(ErrorStatus status) {
this.isSuccess = false;
this.message = status.getMessage();
this.code = status.getCode();
}

public ApiResponse(ErrorStatus status, T result) {
this.isSuccess = false;
this.message = status.getMessage();
this.code = status.getCode();
this.result = result;
}
}
9 changes: 9 additions & 0 deletions src/main/java/hyu/erica/capstone/api/code/BaseCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package hyu.erica.capstone.api.code;


public interface BaseCode {

public ReasonDTO getReason();

public ReasonDTO getReasonHttpStatus();
}
8 changes: 8 additions & 0 deletions src/main/java/hyu/erica/capstone/api/code/BaseErrorCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package hyu.erica.capstone.api.code;

public interface BaseErrorCode {

public ErrorReasonDTO getReason();

public ErrorReasonDTO getReasonHttpStatus();
}
20 changes: 20 additions & 0 deletions src/main/java/hyu/erica/capstone/api/code/ErrorReasonDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package hyu.erica.capstone.api.code;

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

@Getter
@Builder
public class ErrorReasonDTO {

private HttpStatus httpStatus;

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

public boolean getIsSuccess() {
return isSuccess;
}
}
20 changes: 20 additions & 0 deletions src/main/java/hyu/erica/capstone/api/code/ReasonDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package hyu.erica.capstone.api.code;

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;
}
}
60 changes: 60 additions & 0 deletions src/main/java/hyu/erica/capstone/api/code/status/ErrorStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package hyu.erica.capstone.api.code.status;

import hyu.erica.capstone.api.code.BaseErrorCode;
import hyu.erica.capstone.api.code.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, "COMMON4000", "잘못된 요청입니다."),
_UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "COMMON4001", "인증되지 않은 요청입니다."),
_FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON4002", "접근이 거부되었습니다."),
_BAD_VALUE_REQUEST(HttpStatus.BAD_REQUEST, "COMMON4003", "올바르지 않은 값을 입력 하셨습니다."),
_INVALID_INPUT_VALUE(HttpStatus.BAD_REQUEST, "COMMON4004", "입력값이 유효하지 않습니다."),
_EMPTY_JWT(HttpStatus.BAD_REQUEST, "COMMON4005", "JWT 토큰이 비어있습니다."),
_INVALID_JWT(HttpStatus.BAD_REQUEST, "COMMON4006", "유효하지 않은 JWT token입니다."),
_EXPIRED_JWT(HttpStatus.BAD_REQUEST, "COMMON4007", "만료된 JWT token입니다."),
_UNSUPPORTED_JWT(HttpStatus.BAD_REQUEST, "COMMON4008", "지원되지 않는 JWT token입니다."),
_INVALID_REFRESH_TOKEN(HttpStatus.BAD_REQUEST, "COMMON4009", "유효하지 않은 리프레시 토큰입니다."),
_EXPIRED_REFRESH_TOKEN(HttpStatus.BAD_REQUEST, "COMMON4010", "만료된 리프레시 토큰입니다."),
_NULL_VALUE(HttpStatus.BAD_REQUEST, "COMMON4011", "값이 입력되지 않았습니다."),
_VALUE_RANGE_EXCEEDED(HttpStatus.BAD_REQUEST, "COMMON4012", "값이 지정된 범위를 초과합니다."),
_TERMS_NOT_AGREED(HttpStatus.FORBIDDEN, "COMMON4013", "이용 약관이 동의되지 않았습니다."),
_MEMBER_EMAIL_EXIST(HttpStatus.BAD_REQUEST, "COMMON4014", "이미 가입 된 이메일입니다. 다른 로그인 방식을 이용해주세요."),
_RSA_ERROR(HttpStatus.BAD_REQUEST, "COMMON4015", "RSA 에러가 발생했습니다."),


;


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,42 @@
package hyu.erica.capstone.api.code.status;

import hyu.erica.capstone.api.code.BaseCode;
import hyu.erica.capstone.api.code.ReasonDTO;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum SuccessStatus implements BaseCode {
//공통 응답
_OK(HttpStatus.OK, "COMMON200", "OK"),
_CREATED(HttpStatus.CREATED, "COMMON201", "생성 완료"),
_ACCEPTED(HttpStatus.ACCEPTED, "COMMON202", "요청 수락됨"),
_NO_CONTENT(HttpStatus.NO_CONTENT, "COMMON204", "콘텐츠 없음"),
;

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()
;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package hyu.erica.capstone.api.exception;


import hyu.erica.capstone.api.ApiResponse;
import hyu.erica.capstone.api.code.status.ErrorStatus;
import jakarta.validation.ConstraintViolationException;
import java.util.List;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
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.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;


@Slf4j
@RestControllerAdvice
public class ExceptionAdvice {

/**
* GeneralException 처리
* @param exception GeneralException
* @return ApiResponse - GeneralException
*/
@ExceptionHandler(GeneralException.class)
public ApiResponse<ErrorStatus> baseExceptionHandle(GeneralException exception) {
log.warn("BaseException. error message: {}", exception.getMessage());
return new ApiResponse<>(exception.getStatus());
}

/**
* Exception 처리
* @param exception Exception
* @return ApiResponse - INTERNAL_SERVER_ERROR
*/
@ExceptionHandler(Exception.class)
public ApiResponse<ErrorStatus> exceptionHandle(Exception exception) {
log.error("Exception has occurred. {}", exception);
return new ApiResponse<>(ErrorStatus._INTERNAL_SERVER_ERROR);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package hyu.erica.capstone.api.exception;

import hyu.erica.capstone.api.code.status.ErrorStatus;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class GeneralException extends RuntimeException {
private ErrorStatus status;
public GeneralException(ErrorStatus status) {
super(status.getCode());
this.status = status;
}
}
39 changes: 39 additions & 0 deletions src/main/java/hyu/erica/capstone/config/SwaggerConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package hyu.erica.capstone.config;

import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SwaggerConfig {

@Bean
public OpenAPI SpotAPI() {
Info info = new Info()
.title("Capstone Project API")
.description("Capstone Project API 명세서");

String jwtSchemeName = "accessToken";

SecurityRequirement securityRequirement = new SecurityRequirement()
.addList(jwtSchemeName);

Components components = new Components()
.addSecuritySchemes(jwtSchemeName, new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("Bearer")
.bearerFormat("JWT"));

return new OpenAPI()
.openapi("3.0.1") // 🔥 OpenAPI 버전 명시 추가!
.addServersItem(new Server().url("/"))
.info(info)
.addSecurityItem(securityRequirement)
.components(components);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package hyu.erica.capstone.domain.enums;

public enum PhoneService {
SKT,
KT,
LGT;


}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package hyu.erica.capstone.controller;
package hyu.erica.capstone.web.controller;

import io.swagger.v3.oas.annotations.Operation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DefaultController {

@Operation(summary = "Health Check", description = "서버 상태 확인")
@GetMapping("/health-check")
public String healthCheck() {
return "Hello, World!";
Expand Down
Loading