From 0de0eca32e1c79098fc000afae546964e9342e77 Mon Sep 17 00:00:00 2001 From: UNGGU0704 Date: Thu, 12 Jun 2025 13:44:22 +0900 Subject: [PATCH] =?UTF-8?q?add:=20common/exception=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hi/common/exception/ApiException.java | 82 +++++++++++++++++ .../exception/AuthenticationException.java | 28 ++++++ .../exception/AuthorizationException.java | 32 +++++++ .../common/exception/BusinessException.java | 88 +++++++++++++++++++ .../exception/BusinessRuleException.java | 32 +++++++ .../exception/ConcurrencyException.java | 37 ++++++++ .../exception/ConfigurationException.java | 37 ++++++++ .../common/exception/DataAccessException.java | 37 ++++++++ .../exception/DataIntegrityException.java | 24 +++++ .../exception/DuplicateResourceException.java | 36 ++++++++ .../exception/ExternalServiceException.java | 36 ++++++++ .../exception/FileProcessingException.java | 44 ++++++++++ .../hi/common/exception/JwtException.java | 37 ++++++++ .../common/exception/RateLimitException.java | 37 ++++++++ .../exception/ResourceNotFoundException.java | 40 +++++++++ .../common/exception/SecurityException.java | 37 ++++++++ .../hi/common/exception/ServiceException.java | 34 +++++++ .../hi/common/exception/TokenException.java | 44 ++++++++++ .../common/exception/ValidationException.java | 36 ++++++++ 19 files changed, 778 insertions(+) create mode 100644 common/src/main/java/com/ktds/hi/common/exception/ApiException.java create mode 100644 common/src/main/java/com/ktds/hi/common/exception/AuthenticationException.java create mode 100644 common/src/main/java/com/ktds/hi/common/exception/AuthorizationException.java create mode 100644 common/src/main/java/com/ktds/hi/common/exception/BusinessException.java create mode 100644 common/src/main/java/com/ktds/hi/common/exception/BusinessRuleException.java create mode 100644 common/src/main/java/com/ktds/hi/common/exception/ConcurrencyException.java create mode 100644 common/src/main/java/com/ktds/hi/common/exception/ConfigurationException.java create mode 100644 common/src/main/java/com/ktds/hi/common/exception/DataAccessException.java create mode 100644 common/src/main/java/com/ktds/hi/common/exception/DataIntegrityException.java create mode 100644 common/src/main/java/com/ktds/hi/common/exception/DuplicateResourceException.java create mode 100644 common/src/main/java/com/ktds/hi/common/exception/ExternalServiceException.java create mode 100644 common/src/main/java/com/ktds/hi/common/exception/FileProcessingException.java create mode 100644 common/src/main/java/com/ktds/hi/common/exception/JwtException.java create mode 100644 common/src/main/java/com/ktds/hi/common/exception/RateLimitException.java create mode 100644 common/src/main/java/com/ktds/hi/common/exception/ResourceNotFoundException.java create mode 100644 common/src/main/java/com/ktds/hi/common/exception/SecurityException.java create mode 100644 common/src/main/java/com/ktds/hi/common/exception/ServiceException.java create mode 100644 common/src/main/java/com/ktds/hi/common/exception/TokenException.java create mode 100644 common/src/main/java/com/ktds/hi/common/exception/ValidationException.java diff --git a/common/src/main/java/com/ktds/hi/common/exception/ApiException.java b/common/src/main/java/com/ktds/hi/common/exception/ApiException.java new file mode 100644 index 0000000..8bb4c50 --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/exception/ApiException.java @@ -0,0 +1,82 @@ +package com.ktds.hi.common.exception; + +import org.springframework.http.HttpStatus; + +/** + * API 관련 예외 + * HTTP 상태 코드와 함께 사용되는 예외 + */ +public class ApiException extends BusinessException { + + private final HttpStatus httpStatus; + + public ApiException(HttpStatus httpStatus, String message) { + super(httpStatus.name(), message); + this.httpStatus = httpStatus; + } + + public ApiException(HttpStatus httpStatus, String message, Throwable cause) { + super(httpStatus.name(), message, cause); + this.httpStatus = httpStatus; + } + + public HttpStatus getHttpStatus() { + return httpStatus; + } + + /** + * Bad Request (400) + */ + public static ApiException badRequest(String message) { + return new ApiException(HttpStatus.BAD_REQUEST, message); + } + + /** + * Unauthorized (401) + */ + public static ApiException unauthorized(String message) { + return new ApiException(HttpStatus.UNAUTHORIZED, message); + } + + /** + * Forbidden (403) + */ + public static ApiException forbidden(String message) { + return new ApiException(HttpStatus.FORBIDDEN, message); + } + + /** + * Not Found (404) + */ + public static ApiException notFound(String message) { + return new ApiException(HttpStatus.NOT_FOUND, message); + } + + /** + * Method Not Allowed (405) + */ + public static ApiException methodNotAllowed(String message) { + return new ApiException(HttpStatus.METHOD_NOT_ALLOWED, message); + } + + /** + * Conflict (409) + */ + public static ApiException conflict(String message) { + return new ApiException(HttpStatus.CONFLICT, message); + } + + /** + * Internal Server Error (500) + */ + public static ApiException internalServerError(String message) { + return new ApiException(HttpStatus.INTERNAL_SERVER_ERROR, message); + } + + /** + * Service Unavailable (503) + */ + public static ApiException serviceUnavailable(String message) { + return new ApiException(HttpStatus.SERVICE_UNAVAILABLE, message); + } +} diff --git a/common/src/main/java/com/ktds/hi/common/exception/AuthenticationException.java b/common/src/main/java/com/ktds/hi/common/exception/AuthenticationException.java new file mode 100644 index 0000000..e71d064 --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/exception/AuthenticationException.java @@ -0,0 +1,28 @@ +package com.ktds.hi.common.exception; + +public class AuthenticationException extends BusinessException { + + public AuthenticationException(String message) { + super("AUTHENTICATION_ERROR", message); + } + + public AuthenticationException(String message, Throwable cause) { + super("AUTHENTICATION_ERROR", message, cause); + } + + public static AuthenticationException loginFailed() { + return new AuthenticationException("아이디 또는 비밀번호가 일치하지 않습니다"); + } + + public static AuthenticationException tokenExpired() { + return new AuthenticationException("토큰이 만료되었습니다"); + } + + public static AuthenticationException invalidToken() { + return new AuthenticationException("유효하지 않은 토큰입니다"); + } + + public static AuthenticationException authenticationRequired() { + return new AuthenticationException("로그인이 필요합니다"); + } +} diff --git a/common/src/main/java/com/ktds/hi/common/exception/AuthorizationException.java b/common/src/main/java/com/ktds/hi/common/exception/AuthorizationException.java new file mode 100644 index 0000000..0326d23 --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/exception/AuthorizationException.java @@ -0,0 +1,32 @@ +package com.ktds.hi.common.exception; + +public class AuthorizationException extends BusinessException { + + public AuthorizationException(String message) { + super("AUTHORIZATION_ERROR", message); + } + + public AuthorizationException(String message, Object... args) { + super("AUTHORIZATION_ERROR", message, args); + } + + public AuthorizationException(String message, Throwable cause) { + super("AUTHORIZATION_ERROR", message, cause); + } + + public static AuthorizationException accessDenied() { + return new AuthorizationException("접근 권한이 없습니다"); + } + + public static AuthorizationException accessDenied(String resource) { + return new AuthorizationException(resource + "에 대한 접근 권한이 없습니다"); + } + + public static AuthorizationException insufficientRole(String requiredRole) { + return new AuthorizationException(requiredRole + " 권한이 필요합니다"); + } + + public static AuthorizationException ownershipRequired() { + return new AuthorizationException("본인만 접근할 수 있습니다"); + } +} diff --git a/common/src/main/java/com/ktds/hi/common/exception/BusinessException.java b/common/src/main/java/com/ktds/hi/common/exception/BusinessException.java new file mode 100644 index 0000000..e7c707d --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/exception/BusinessException.java @@ -0,0 +1,88 @@ +// common/src/main/java/com/ktds/hi/common/exception/BusinessException.java +package com.ktds.hi.common.exception; + +/** + * 비즈니스 로직 예외의 기본 클래스 + * 모든 커스텀 예외의 부모 클래스 + */ +public class BusinessException extends RuntimeException { + + private String errorCode; + private Object[] args; + + /** + * 메시지만으로 예외 생성 + */ + public BusinessException(String message) { + super(message); + this.errorCode = "BUSINESS_ERROR"; + } + + /** + * 메시지와 원인으로 예외 생성 + */ + public BusinessException(String message, Throwable cause) { + super(message, cause); + this.errorCode = "BUSINESS_ERROR"; + } + + /** + * 에러 코드와 메시지로 예외 생성 + */ + public BusinessException(String errorCode, String message) { + super(message); + this.errorCode = errorCode; + } + + /** + * 에러 코드, 메시지, 파라미터로 예외 생성 + */ + public BusinessException(String errorCode, String message, Object... args) { + super(message); + this.errorCode = errorCode; + this.args = args; + } + + /** + * 에러 코드, 메시지, 원인으로 예외 생성 + */ + public BusinessException(String errorCode, String message, Throwable cause) { + super(message, cause); + this.errorCode = errorCode; + } + + /** + * 에러 코드 반환 + */ + public String getErrorCode() { + return errorCode; + } + + /** + * 메시지 파라미터 반환 + */ + public Object[] getArgs() { + return args; + } + + /** + * 정적 팩토리 메서드 - 일반적인 비즈니스 예외 + */ + public static BusinessException of(String message) { + return new BusinessException(message); + } + + /** + * 정적 팩토리 메서드 - 에러 코드와 메시지 + */ + public static BusinessException of(String errorCode, String message) { + return new BusinessException(errorCode, message); + } + + /** + * 정적 팩토리 메서드 - 원인 포함 + */ + public static BusinessException of(String message, Throwable cause) { + return new BusinessException(message, cause); + } +} \ No newline at end of file diff --git a/common/src/main/java/com/ktds/hi/common/exception/BusinessRuleException.java b/common/src/main/java/com/ktds/hi/common/exception/BusinessRuleException.java new file mode 100644 index 0000000..9718583 --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/exception/BusinessRuleException.java @@ -0,0 +1,32 @@ +package com.ktds.hi.common.exception; + +public class BusinessRuleException extends BusinessException { + + public BusinessRuleException(String message) { + super("BUSINESS_RULE_VIOLATION", message); + } + + public BusinessRuleException(String message, Object... args) { + super("BUSINESS_RULE_VIOLATION", message, args); + } + + public BusinessRuleException(String message, Throwable cause) { + super("BUSINESS_RULE_VIOLATION", message, cause); + } + + public static BusinessRuleException invalidStateTransition(String from, String to) { + return new BusinessRuleException(String.format("%s 상태에서 %s 상태로 변경할 수 없습니다", from, to)); + } + + public static BusinessRuleException unauthorizedOperation(String operation) { + return new BusinessRuleException(operation + " 작업을 수행할 권한이 없습니다"); + } + + public static BusinessRuleException timeConstraintViolation(String operation) { + return new BusinessRuleException(operation + "의 시간 제한을 위반했습니다"); + } + + public static BusinessRuleException duplicateOperation(String operation) { + return new BusinessRuleException("이미 " + operation + "을(를) 수행했습니다"); + } +} diff --git a/common/src/main/java/com/ktds/hi/common/exception/ConcurrencyException.java b/common/src/main/java/com/ktds/hi/common/exception/ConcurrencyException.java new file mode 100644 index 0000000..044e05b --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/exception/ConcurrencyException.java @@ -0,0 +1,37 @@ +package com.ktds.hi.common.exception; + +/** + * 동시성 관련 예외 + * 동시 접근, 락 충돌 등에서 발생하는 예외 + */ +public class ConcurrencyException extends BusinessException { + + public ConcurrencyException(String message) { + super("CONCURRENCY_ERROR", message); + } + + public ConcurrencyException(String message, Throwable cause) { + super("CONCURRENCY_ERROR", message, cause); + } + + /** + * 낙관적 락 충돌 + */ + public static ConcurrencyException optimisticLockConflict() { + return new ConcurrencyException("동시 수정으로 인한 충돌이 발생했습니다. 다시 시도해주세요"); + } + + /** + * 비관적 락 타임아웃 + */ + public static ConcurrencyException lockTimeout() { + return new ConcurrencyException("리소스 잠금 시간이 초과되었습니다"); + } + + /** + * 데드락 발생 + */ + public static ConcurrencyException deadlockDetected() { + return new ConcurrencyException("데드락이 감지되었습니다"); + } +} diff --git a/common/src/main/java/com/ktds/hi/common/exception/ConfigurationException.java b/common/src/main/java/com/ktds/hi/common/exception/ConfigurationException.java new file mode 100644 index 0000000..74c8e81 --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/exception/ConfigurationException.java @@ -0,0 +1,37 @@ +package com.ktds.hi.common.exception; + +/** + * 설정 관련 예외 + * 애플리케이션 설정 오류, 환경 변수 누락 등에서 발생하는 예외 + */ +public class ConfigurationException extends BusinessException { + + public ConfigurationException(String message) { + super("CONFIGURATION_ERROR", message); + } + + public ConfigurationException(String message, Throwable cause) { + super("CONFIGURATION_ERROR", message, cause); + } + + /** + * 필수 설정 누락 + */ + public static ConfigurationException missingProperty(String propertyName) { + return new ConfigurationException("필수 설정이 누락되었습니다: " + propertyName); + } + + /** + * 잘못된 설정 값 + */ + public static ConfigurationException invalidValue(String propertyName, String value) { + return new ConfigurationException(String.format("잘못된 설정 값입니다. %s: %s", propertyName, value)); + } + + /** + * 설정 파일 로드 실패 + */ + public static ConfigurationException loadFailed(String configFile) { + return new ConfigurationException("설정 파일 로드에 실패했습니다: " + configFile); + } +} diff --git a/common/src/main/java/com/ktds/hi/common/exception/DataAccessException.java b/common/src/main/java/com/ktds/hi/common/exception/DataAccessException.java new file mode 100644 index 0000000..e7465e3 --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/exception/DataAccessException.java @@ -0,0 +1,37 @@ +package com.ktds.hi.common.exception; + +/** + * 데이터 접근 관련 예외 + * 데이터베이스, 캐시 등 데이터 저장소 접근 시 발생하는 예외 + */ +public class DataAccessException extends BusinessException { + + public DataAccessException(String message) { + super("DATA_ACCESS_ERROR", message); + } + + public DataAccessException(String message, Throwable cause) { + super("DATA_ACCESS_ERROR", message, cause); + } + + /** + * 데이터베이스 연결 실패 + */ + public static DataAccessException connectionFailed() { + return new DataAccessException("데이터베이스 연결에 실패했습니다"); + } + + /** + * 데이터 정합성 오류 + */ + public static DataAccessException integrityViolation(String detail) { + return new DataAccessException("데이터 정합성 오류: " + detail); + } + + /** + * 캐시 접근 실패 + */ + public static DataAccessException cacheAccessFailed() { + return new DataAccessException("캐시 접근에 실패했습니다"); + } +} diff --git a/common/src/main/java/com/ktds/hi/common/exception/DataIntegrityException.java b/common/src/main/java/com/ktds/hi/common/exception/DataIntegrityException.java new file mode 100644 index 0000000..85c69d5 --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/exception/DataIntegrityException.java @@ -0,0 +1,24 @@ +package com.ktds.hi.common.exception; + +public class DataIntegrityException extends BusinessException { + + public DataIntegrityException(String message) { + super("DATA_INTEGRITY_ERROR", message); + } + + public DataIntegrityException(String message, Throwable cause) { + super("DATA_INTEGRITY_ERROR", message, cause); + } + + public static DataIntegrityException foreignKeyViolation(String entity, Object id) { + return new DataIntegrityException(String.format("%s(ID: %s)에 참조된 데이터가 있어 삭제할 수 없습니다", entity, id)); + } + + public static DataIntegrityException uniqueConstraintViolation(String field, Object value) { + return new DataIntegrityException(String.format("중복된 값입니다. %s: %s", field, value)); + } + + public static DataIntegrityException notNullViolation(String field) { + return new DataIntegrityException(field + "은(는) 필수 값입니다"); + } +} diff --git a/common/src/main/java/com/ktds/hi/common/exception/DuplicateResourceException.java b/common/src/main/java/com/ktds/hi/common/exception/DuplicateResourceException.java new file mode 100644 index 0000000..0d945a6 --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/exception/DuplicateResourceException.java @@ -0,0 +1,36 @@ +package com.ktds.hi.common.exception; + +public class DuplicateResourceException extends BusinessException { + + public DuplicateResourceException(String resourceType, String field, Object value) { + super("DUPLICATE_RESOURCE", String.format("이미 존재하는 %s입니다. %s: %s", resourceType, field, value)); + } + + public DuplicateResourceException(String message) { + super("DUPLICATE_RESOURCE", message); + } + + public DuplicateResourceException(String message, Throwable cause) { + super("DUPLICATE_RESOURCE", message, cause); + } + + public static DuplicateResourceException username(String username) { + return new DuplicateResourceException("사용자", "사용자명", username); + } + + public static DuplicateResourceException email(String email) { + return new DuplicateResourceException("사용자", "이메일", email); + } + + public static DuplicateResourceException nickname(String nickname) { + return new DuplicateResourceException("사용자", "닉네임", nickname); + } + + public static DuplicateResourceException phone(String phone) { + return new DuplicateResourceException("사용자", "전화번호", phone); + } + + public static DuplicateResourceException storeName(String storeName) { + return new DuplicateResourceException("매장", "매장명", storeName); + } +} diff --git a/common/src/main/java/com/ktds/hi/common/exception/ExternalServiceException.java b/common/src/main/java/com/ktds/hi/common/exception/ExternalServiceException.java new file mode 100644 index 0000000..52a5a2a --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/exception/ExternalServiceException.java @@ -0,0 +1,36 @@ +package com.ktds.hi.common.exception; + +public class ExternalServiceException extends BusinessException { + + private final String serviceName; + + public ExternalServiceException(String serviceName, String message) { + super("EXTERNAL_SERVICE_ERROR", String.format("%s 서비스 오류: %s", serviceName, message)); + this.serviceName = serviceName; + } + + public ExternalServiceException(String serviceName, String message, Throwable cause) { + super("EXTERNAL_SERVICE_ERROR", String.format("%s 서비스 오류: %s", serviceName, message), cause); + this.serviceName = serviceName; + } + + public String getServiceName() { + return serviceName; + } + + public static ExternalServiceException apiCallFailed(String serviceName, String endpoint) { + return new ExternalServiceException(serviceName, endpoint + " API 호출에 실패했습니다"); + } + + public static ExternalServiceException serviceUnavailable(String serviceName) { + return new ExternalServiceException(serviceName, "서비스를 사용할 수 없습니다"); + } + + public static ExternalServiceException timeout(String serviceName) { + return new ExternalServiceException(serviceName, "응답 시간이 초과되었습니다"); + } + + public static ExternalServiceException authenticationFailed(String serviceName) { + return new ExternalServiceException(serviceName, "인증에 실패했습니다"); + } +} diff --git a/common/src/main/java/com/ktds/hi/common/exception/FileProcessingException.java b/common/src/main/java/com/ktds/hi/common/exception/FileProcessingException.java new file mode 100644 index 0000000..aae85dd --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/exception/FileProcessingException.java @@ -0,0 +1,44 @@ +package com.ktds.hi.common.exception; + +/** + * 파일 처리 관련 예외 + * 파일 업로드, 다운로드, 변환 등에서 발생하는 예외 + */ +public class FileProcessingException extends BusinessException { + + public FileProcessingException(String message) { + super("FILE_PROCESSING_ERROR", message); + } + + public FileProcessingException(String message, Throwable cause) { + super("FILE_PROCESSING_ERROR", message, cause); + } + + /** + * 지원하지 않는 파일 형식 + */ + public static FileProcessingException unsupportedFileType(String fileType) { + return new FileProcessingException("지원하지 않는 파일 형식입니다: " + fileType); + } + + /** + * 파일 크기 초과 + */ + public static FileProcessingException fileSizeExceeded(long maxSize) { + return new FileProcessingException("파일 크기가 제한을 초과했습니다. 최대 크기: " + maxSize + "bytes"); + } + + /** + * 파일 업로드 실패 + */ + public static FileProcessingException uploadFailed() { + return new FileProcessingException("파일 업로드에 실패했습니다"); + } + + /** + * 파일 변환 실패 + */ + public static FileProcessingException conversionFailed(String fromType, String toType) { + return new FileProcessingException(String.format("파일 변환에 실패했습니다: %s -> %s", fromType, toType)); + } +} diff --git a/common/src/main/java/com/ktds/hi/common/exception/JwtException.java b/common/src/main/java/com/ktds/hi/common/exception/JwtException.java new file mode 100644 index 0000000..0a41398 --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/exception/JwtException.java @@ -0,0 +1,37 @@ +package com.ktds.hi.common.exception; + +/** + * JWT 처리 관련 예외 + * JWT 파싱, 서명 검증 등에서 발생하는 예외 + */ +public class JwtException extends TokenException { + + public JwtException(String message) { + super(message); + } + + public JwtException(String message, Throwable cause) { + super(message, cause); + } + + /** + * JWT 서명 검증 실패 + */ + public static JwtException signatureVerificationFailed() { + return new JwtException("JWT 서명 검증에 실패했습니다"); + } + + /** + * JWT 파싱 실패 + */ + public static JwtException parsingFailed() { + return new JwtException("JWT 파싱에 실패했습니다"); + } + + /** + * JWT 클레임 누락 + */ + public static JwtException missingClaim(String claimName) { + return new JwtException("필수 클레임이 누락되었습니다: " + claimName); + } +} diff --git a/common/src/main/java/com/ktds/hi/common/exception/RateLimitException.java b/common/src/main/java/com/ktds/hi/common/exception/RateLimitException.java new file mode 100644 index 0000000..d123a53 --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/exception/RateLimitException.java @@ -0,0 +1,37 @@ +package com.ktds.hi.common.exception; + +/** + * 요청 제한 관련 예외 + * API 호출 제한, 처리량 제한 등에서 발생하는 예외 + */ +public class RateLimitException extends BusinessException { + + private final long retryAfter; // 재시도 가능 시간 (초) + + public RateLimitException(String message, long retryAfter) { + super("RATE_LIMIT_EXCEEDED", message); + this.retryAfter = retryAfter; + } + + public RateLimitException(String message) { + this(message, 60); // 기본값: 60초 후 재시도 + } + + public long getRetryAfter() { + return retryAfter; + } + + /** + * API 호출 제한 초과 + */ + public static RateLimitException apiCallLimitExceeded(long retryAfter) { + return new RateLimitException("API 호출 제한을 초과했습니다. 잠시 후 다시 시도해주세요", retryAfter); + } + + /** + * 동시 요청 제한 초과 + */ + public static RateLimitException concurrentRequestLimitExceeded() { + return new RateLimitException("동시 요청 제한을 초과했습니다", 5); + } +} diff --git a/common/src/main/java/com/ktds/hi/common/exception/ResourceNotFoundException.java b/common/src/main/java/com/ktds/hi/common/exception/ResourceNotFoundException.java new file mode 100644 index 0000000..2c37a91 --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/exception/ResourceNotFoundException.java @@ -0,0 +1,40 @@ +package com.ktds.hi.common.exception; + +public class ResourceNotFoundException extends BusinessException { + + public ResourceNotFoundException(String resourceType, Object id) { + super("RESOURCE_NOT_FOUND", String.format("%s를 찾을 수 없습니다. ID: %s", resourceType, id)); + } + + public ResourceNotFoundException(String message) { + super("RESOURCE_NOT_FOUND", message); + } + + public ResourceNotFoundException(String resourceType, String field, Object value) { + super("RESOURCE_NOT_FOUND", String.format("%s를 찾을 수 없습니다. %s: %s", resourceType, field, value)); + } + + public ResourceNotFoundException(String message, Throwable cause) { + super("RESOURCE_NOT_FOUND", message, cause); + } + + public static ResourceNotFoundException byId(String resourceType, Object id) { + return new ResourceNotFoundException(resourceType, id); + } + + public static ResourceNotFoundException user(Long userId) { + return new ResourceNotFoundException("사용자", userId); + } + + public static ResourceNotFoundException store(Long storeId) { + return new ResourceNotFoundException("매장", storeId); + } + + public static ResourceNotFoundException review(Long reviewId) { + return new ResourceNotFoundException("리뷰", reviewId); + } + + public static ResourceNotFoundException userByUsername(String username) { + return new ResourceNotFoundException("사용자", "사용자명", username); + } +} diff --git a/common/src/main/java/com/ktds/hi/common/exception/SecurityException.java b/common/src/main/java/com/ktds/hi/common/exception/SecurityException.java new file mode 100644 index 0000000..32171af --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/exception/SecurityException.java @@ -0,0 +1,37 @@ +package com.ktds.hi.common.exception; + +/** + * 보안 관련 예외 + * 인증, 인가 외의 보안 관련 예외 + */ +public class SecurityException extends BusinessException { + + public SecurityException(String message) { + super("SECURITY_ERROR", message); + } + + public SecurityException(String message, Throwable cause) { + super("SECURITY_ERROR", message, cause); + } + + /** + * 보안 정책 위반 + */ + public static SecurityException policyViolation(String policy) { + return new SecurityException("보안 정책 위반: " + policy); + } + + /** + * 의심스러운 활동 감지 + */ + public static SecurityException suspiciousActivity(String activity) { + return new SecurityException("의심스러운 활동이 감지되었습니다: " + activity); + } + + /** + * IP 차단 + */ + public static SecurityException ipBlocked(String ip) { + return new SecurityException("차단된 IP 주소입니다: " + ip); + } +} diff --git a/common/src/main/java/com/ktds/hi/common/exception/ServiceException.java b/common/src/main/java/com/ktds/hi/common/exception/ServiceException.java new file mode 100644 index 0000000..88f4130 --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/exception/ServiceException.java @@ -0,0 +1,34 @@ +package com.ktds.hi.common.exception; + +/** + * 서비스 레이어 예외 + * 비즈니스 로직 실행 중 발생하는 일반적인 예외 + */ +public class ServiceException extends BusinessException { + + public ServiceException(String message) { + super("SERVICE_ERROR", message); + } + + public ServiceException(String message, Throwable cause) { + super("SERVICE_ERROR", message, cause); + } + + public ServiceException(String errorCode, String message) { + super(errorCode, message); + } + + /** + * 서비스 일시 중단 + */ + public static ServiceException temporarilyUnavailable(String serviceName) { + return new ServiceException("서비스가 일시적으로 이용할 수 없습니다: " + serviceName); + } + + /** + * 처리 한도 초과 + */ + public static ServiceException limitExceeded(String limitType) { + return new ServiceException("처리 한도를 초과했습니다: " + limitType); + } +} diff --git a/common/src/main/java/com/ktds/hi/common/exception/TokenException.java b/common/src/main/java/com/ktds/hi/common/exception/TokenException.java new file mode 100644 index 0000000..7c2dcd1 --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/exception/TokenException.java @@ -0,0 +1,44 @@ +package com.ktds.hi.common.exception; + +/** + * JWT 토큰 관련 예외 + * 토큰 만료, 유효하지 않은 토큰 등에 사용 + */ +public class TokenException extends BusinessException { + + public TokenException(String message) { + super("TOKEN_ERROR", message); + } + + public TokenException(String message, Throwable cause) { + super("TOKEN_ERROR", message, cause); + } + + /** + * 토큰 만료 예외 + */ + public static TokenException expired() { + return new TokenException("토큰이 만료되었습니다"); + } + + /** + * 토큰 형식 오류 예외 + */ + public static TokenException invalid() { + return new TokenException("유효하지 않은 토큰입니다"); + } + + /** + * 토큰 누락 예외 + */ + public static TokenException missing() { + return new TokenException("토큰이 필요합니다"); + } + + /** + * 잘못된 토큰 타입 예외 + */ + public static TokenException wrongType(String expectedType) { + return new TokenException(expectedType + " 토큰이 필요합니다"); + } +} diff --git a/common/src/main/java/com/ktds/hi/common/exception/ValidationException.java b/common/src/main/java/com/ktds/hi/common/exception/ValidationException.java new file mode 100644 index 0000000..1095219 --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/exception/ValidationException.java @@ -0,0 +1,36 @@ +package com.ktds.hi.common.exception; + +public class ValidationException extends BusinessException { + + public ValidationException(String message) { + super("VALIDATION_ERROR", message); + } + + public ValidationException(String message, Object... args) { + super("VALIDATION_ERROR", message, args); + } + + public ValidationException(String field, String message) { + super("VALIDATION_ERROR", String.format("%s: %s", field, message)); + } + + public ValidationException(String message, Throwable cause) { + super("VALIDATION_ERROR", message, cause); + } + + public static ValidationException requiredField(String fieldName) { + return new ValidationException(fieldName + "은(는) 필수 입력 항목입니다"); + } + + public static ValidationException invalidFormat(String fieldName) { + return new ValidationException(fieldName + "의 형식이 올바르지 않습니다"); + } + + public static ValidationException outOfRange(String fieldName, Object min, Object max) { + return new ValidationException(String.format("%s는 %s와 %s 사이의 값이어야 합니다", fieldName, min, max)); + } + + public static ValidationException lengthExceeded(String fieldName, int maxLength) { + return new ValidationException(String.format("%s는 %d자 이하로 입력해주세요", fieldName, maxLength)); + } +}