diff --git a/src/main/java/life/mosu/mosuserver/application/auth/SignUpService.java b/src/main/java/life/mosu/mosuserver/application/auth/SignUpService.java index f3aafced..494123f0 100644 --- a/src/main/java/life/mosu/mosuserver/application/auth/SignUpService.java +++ b/src/main/java/life/mosu/mosuserver/application/auth/SignUpService.java @@ -23,13 +23,11 @@ public class SignUpService { @Transactional public Token signUp(final SignUpAccountRequest request) { UserJpaEntity user = doAccountStep(request); - log.info("User signed up successfully: {}", user); return authTokenManager.generateAuthToken(user); } private UserJpaEntity doAccountStep(SignUpAccountRequest request) { UserJpaEntity user = request.toAuthEntity(passwordEncoder); - log.info("log : {}", user); return signUpAccountStepProcessor.process(user); } } \ No newline at end of file diff --git a/src/main/java/life/mosu/mosuserver/application/auth/kmc/tx/KmcContext.java b/src/main/java/life/mosu/mosuserver/application/auth/kmc/tx/KmcContext.java index 8ae72375..5316127c 100644 --- a/src/main/java/life/mosu/mosuserver/application/auth/kmc/tx/KmcContext.java +++ b/src/main/java/life/mosu/mosuserver/application/auth/kmc/tx/KmcContext.java @@ -1,12 +1,13 @@ package life.mosu.mosuserver.application.auth.kmc.tx; -import life.mosu.mosuserver.domain.auth.signup.Token; +import life.mosu.mosuserver.domain.auth.token.Token; public record KmcContext( String certNum, Long expiration, Boolean isSuccess ) { + public static KmcContext ofSuccess(String certNum, Long expiration) { return new KmcContext(certNum, expiration, true); } @@ -17,8 +18,8 @@ public static KmcContext ofFailure(String certNum) { public Token toToken() { return Token.of( - this.certNum, - this.expiration + this.certNum, + this.expiration ); } } diff --git a/src/main/java/life/mosu/mosuserver/application/auth/kmc/tx/KmcTxEventListener.java b/src/main/java/life/mosu/mosuserver/application/auth/kmc/tx/KmcTxEventListener.java index 6e9b6dff..011193f3 100644 --- a/src/main/java/life/mosu/mosuserver/application/auth/kmc/tx/KmcTxEventListener.java +++ b/src/main/java/life/mosu/mosuserver/application/auth/kmc/tx/KmcTxEventListener.java @@ -1,7 +1,7 @@ package life.mosu.mosuserver.application.auth.kmc.tx; -import life.mosu.mosuserver.domain.auth.signup.Token; -import life.mosu.mosuserver.domain.auth.signup.TokenRepository; +import life.mosu.mosuserver.domain.auth.token.Token; +import life.mosu.mosuserver.domain.auth.token.TokenRepository; import life.mosu.mosuserver.global.tx.TxFailureHandler; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/life/mosu/mosuserver/application/auth/processor/SignUpAccountStepProcessor.java b/src/main/java/life/mosu/mosuserver/application/auth/processor/SignUpAccountStepProcessor.java index d1fc29e0..4327f543 100644 --- a/src/main/java/life/mosu/mosuserver/application/auth/processor/SignUpAccountStepProcessor.java +++ b/src/main/java/life/mosu/mosuserver/application/auth/processor/SignUpAccountStepProcessor.java @@ -20,12 +20,28 @@ public class SignUpAccountStepProcessor implements StepProcessor { + if (existingUser.isPendingUser()) { + switch (existingUser.getProvider()) { + case MOSU: + throw new CustomRuntimeException(ErrorCode.USER_ALREADY_EXISTS); + case KAKAO: + throw new CustomRuntimeException(ErrorCode.KAKAO_DUPLICATED); + } + } + }); + + if (userRepository.existsByLoginId(user.getLoginId())) { throw new CustomRuntimeException(ErrorCode.USER_ALREADY_EXISTS); } - log.info("Processing user sign-up: {}", user); - return userRepository.save(user); } } diff --git a/src/main/java/life/mosu/mosuserver/application/auth/provider/OneTimeTokenProvider.java b/src/main/java/life/mosu/mosuserver/application/auth/provider/OneTimeTokenProvider.java index fad9b5ad..81dd3647 100644 --- a/src/main/java/life/mosu/mosuserver/application/auth/provider/OneTimeTokenProvider.java +++ b/src/main/java/life/mosu/mosuserver/application/auth/provider/OneTimeTokenProvider.java @@ -8,19 +8,20 @@ import io.jsonwebtoken.security.Keys; import java.security.Key; import java.util.Date; -import life.mosu.mosuserver.domain.auth.signup.Token; -import life.mosu.mosuserver.domain.auth.signup.TokenRepository; +import life.mosu.mosuserver.domain.auth.token.Token; +import life.mosu.mosuserver.domain.auth.token.TokenRepository; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; + @Slf4j @Service public class OneTimeTokenProvider { - private final TokenRepository tokenRepository; + private static final String tokenType = "ONE_TIME"; private static final String TOKEN_TYPE_KEY = "type"; private static final String PHONE_NUMBER_KEY = "phone"; - + private final TokenRepository tokenRepository; private final Long expireTime; private final Key key; diff --git a/src/main/java/life/mosu/mosuserver/application/notify/NotifyService.java b/src/main/java/life/mosu/mosuserver/application/notify/NotifyService.java index d09b35be..49dbc015 100644 --- a/src/main/java/life/mosu/mosuserver/application/notify/NotifyService.java +++ b/src/main/java/life/mosu/mosuserver/application/notify/NotifyService.java @@ -37,15 +37,10 @@ public void notify(LunaNotificationEvent event) { NotifySender sender = senderResolver.resolve(event.status()); sender.send(phone, event, notifyVariable); - log.info("[NotifyService] 알림톡 전송 성공: userId={}, status={}", event.userId(), event.status()); } @Recover public void recover(CustomRuntimeException exception, LunaNotificationEvent event) { - log.warn( - "[NotifyService] 알림톡 전송 실패: userId={}, status={}, reason={}", - event.userId(), event.status(), exception.getMessage() - ); DiscordExceptionNotifyEventRequest request = DiscordExceptionNotifyEventRequest.of( exception.getCause() != null ? exception.getCause().toString() : "N/A", exception.getMessage(), diff --git a/src/main/java/life/mosu/mosuserver/application/oauth/OAuthUserPersistenceProcessor.java b/src/main/java/life/mosu/mosuserver/application/oauth/OAuthUserPersistenceProcessor.java index a137bb9c..83782f76 100644 --- a/src/main/java/life/mosu/mosuserver/application/oauth/OAuthUserPersistenceProcessor.java +++ b/src/main/java/life/mosu/mosuserver/application/oauth/OAuthUserPersistenceProcessor.java @@ -17,20 +17,23 @@ public class OAuthUserPersistenceProcessor implements StepProcessor { - if (existingUser.isMosuUser()) { - throw new OAuthException("DUPLICATE"); + switch (existingUser.getProvider()) { + case MOSU: + if (existingUser.isPendingUser()) { + throw new OAuthException("DUPLICATE"); + } + break; + case KAKAO: + if (existingUser.isPendingUser()) { + throw new OAuthException("KAKAO_DUPLICATE"); + } + break; } existingUser.updateOAuthUser( info.gender(), diff --git a/src/main/java/life/mosu/mosuserver/application/oauth/OAuthUserService.java b/src/main/java/life/mosu/mosuserver/application/oauth/OAuthUserService.java index 76ba218f..0efab0c2 100644 --- a/src/main/java/life/mosu/mosuserver/application/oauth/OAuthUserService.java +++ b/src/main/java/life/mosu/mosuserver/application/oauth/OAuthUserService.java @@ -46,8 +46,6 @@ public OAuth2User loadUser(final OAuth2UserRequest userRequest) .orElse(false); } - log.info("동의 여부{}", agreedToMarketing); - final String registrationId = userRequest.getClientRegistration().getRegistrationId(); final String userNameAttributeName = userRequest.getClientRegistration() .getProviderDetails() diff --git a/src/main/java/life/mosu/mosuserver/application/user/UserService.java b/src/main/java/life/mosu/mosuserver/application/user/UserService.java index eefaacc2..716df4b1 100644 --- a/src/main/java/life/mosu/mosuserver/application/user/UserService.java +++ b/src/main/java/life/mosu/mosuserver/application/user/UserService.java @@ -1,6 +1,5 @@ package life.mosu.mosuserver.application.user; -import life.mosu.mosuserver.domain.profile.entity.ProfileJpaEntity; import life.mosu.mosuserver.domain.user.entity.UserJpaEntity; import life.mosu.mosuserver.domain.user.repository.UserJpaRepository; import life.mosu.mosuserver.global.exception.CustomRuntimeException; @@ -31,13 +30,6 @@ public UserJpaEntity getUserOrThrow(Long userId) { ); } - public void syncUserInfoFromProfile(UserJpaEntity user, ProfileJpaEntity profile) { - if (user.isMosuUser()) { - user.updateUserInfo(profile.getGender(), profile.getUserName(), - profile.getPhoneNumber(), profile.getBirth()); - } - } - public Long saveOrGetUser(UserJpaEntity user) { return userJpaRepository.findByPhoneNumber( PhoneNumberUtil.formatGuestPhoneNumber(user.getPhoneNumber())) diff --git a/src/main/java/life/mosu/mosuserver/domain/auth/signup/SignUpTokenKeyValueRepository.java b/src/main/java/life/mosu/mosuserver/domain/auth/signup/SignUpTokenKeyValueRepository.java deleted file mode 100644 index 88dfe9ac..00000000 --- a/src/main/java/life/mosu/mosuserver/domain/auth/signup/SignUpTokenKeyValueRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package life.mosu.mosuserver.domain.auth.signup; - -import org.springframework.data.keyvalue.repository.KeyValueRepository; - -public interface SignUpTokenKeyValueRepository extends - KeyValueRepository { -} diff --git a/src/main/java/life/mosu/mosuserver/domain/auth/signup/Token.java b/src/main/java/life/mosu/mosuserver/domain/auth/token/Token.java similarity index 50% rename from src/main/java/life/mosu/mosuserver/domain/auth/signup/Token.java rename to src/main/java/life/mosu/mosuserver/domain/auth/token/Token.java index ce0f3f2a..a7fcb715 100644 --- a/src/main/java/life/mosu/mosuserver/domain/auth/signup/Token.java +++ b/src/main/java/life/mosu/mosuserver/domain/auth/token/Token.java @@ -1,17 +1,18 @@ -package life.mosu.mosuserver.domain.auth.signup; +package life.mosu.mosuserver.domain.auth.token; public record Token( String certNum, Long expiration ) { + public static Token of(String certNum, Long expiration) { return new Token(certNum, expiration); } - public static Token from(SignUpTokenRedisEntity signUpTokenRedisEntity) { + public static Token from(TokenRedisEntity tokenRedisEntity) { return new Token( - signUpTokenRedisEntity.getCertNum(), - signUpTokenRedisEntity.getExpiration() + tokenRedisEntity.getCertNum(), + tokenRedisEntity.getExpiration() ); } } diff --git a/src/main/java/life/mosu/mosuserver/domain/auth/token/TokenKeyValueRepository.java b/src/main/java/life/mosu/mosuserver/domain/auth/token/TokenKeyValueRepository.java new file mode 100644 index 00000000..f4a717bf --- /dev/null +++ b/src/main/java/life/mosu/mosuserver/domain/auth/token/TokenKeyValueRepository.java @@ -0,0 +1,8 @@ +package life.mosu.mosuserver.domain.auth.token; + +import org.springframework.data.keyvalue.repository.KeyValueRepository; + +public interface TokenKeyValueRepository extends + KeyValueRepository { + +} diff --git a/src/main/java/life/mosu/mosuserver/domain/auth/signup/SignUpTokenRedisEntity.java b/src/main/java/life/mosu/mosuserver/domain/auth/token/TokenRedisEntity.java similarity index 72% rename from src/main/java/life/mosu/mosuserver/domain/auth/signup/SignUpTokenRedisEntity.java rename to src/main/java/life/mosu/mosuserver/domain/auth/token/TokenRedisEntity.java index 4b884a66..3c0ad201 100644 --- a/src/main/java/life/mosu/mosuserver/domain/auth/signup/SignUpTokenRedisEntity.java +++ b/src/main/java/life/mosu/mosuserver/domain/auth/token/TokenRedisEntity.java @@ -1,4 +1,4 @@ -package life.mosu.mosuserver.domain.auth.signup; +package life.mosu.mosuserver.domain.auth.token; import java.util.concurrent.TimeUnit; import lombok.AllArgsConstructor; @@ -13,7 +13,7 @@ @RedisHash(value = "token") @AllArgsConstructor(access = lombok.AccessLevel.PRIVATE) @NoArgsConstructor(access = lombok.AccessLevel.PROTECTED) -public class SignUpTokenRedisEntity { +public class TokenRedisEntity { @Id @Indexed @@ -22,7 +22,7 @@ public class SignUpTokenRedisEntity { @TimeToLive(unit = TimeUnit.MILLISECONDS) private Long expiration; - public static SignUpTokenRedisEntity from(final Token token) { - return new SignUpTokenRedisEntity(token.certNum(), token.expiration()); + public static TokenRedisEntity from(final Token token) { + return new TokenRedisEntity(token.certNum(), token.expiration()); } } \ No newline at end of file diff --git a/src/main/java/life/mosu/mosuserver/domain/auth/signup/SignUpTokenRedisRepository.java b/src/main/java/life/mosu/mosuserver/domain/auth/token/TokenRedisRepository.java similarity index 63% rename from src/main/java/life/mosu/mosuserver/domain/auth/signup/SignUpTokenRedisRepository.java rename to src/main/java/life/mosu/mosuserver/domain/auth/token/TokenRedisRepository.java index b17e96b8..ce5a4877 100644 --- a/src/main/java/life/mosu/mosuserver/domain/auth/signup/SignUpTokenRedisRepository.java +++ b/src/main/java/life/mosu/mosuserver/domain/auth/token/TokenRedisRepository.java @@ -1,4 +1,4 @@ -package life.mosu.mosuserver.domain.auth.signup; +package life.mosu.mosuserver.domain.auth.token; import life.mosu.mosuserver.global.exception.CustomRuntimeException; import life.mosu.mosuserver.global.exception.ErrorCode; @@ -9,23 +9,23 @@ @Slf4j @Repository @RequiredArgsConstructor -public class SignUpTokenRedisRepository implements TokenRepository { +public class TokenRedisRepository implements TokenRepository { - private final SignUpTokenKeyValueRepository repository; + private final TokenKeyValueRepository repository; @Override public void save(Token signUpToken) { - SignUpTokenRedisEntity entity = SignUpTokenRedisEntity.from(signUpToken); + TokenRedisEntity entity = TokenRedisEntity.from(signUpToken); repository.save(entity); } @Override public Token findByCertNum(String certNum) { log.info("findByCertNum certNum={}", certNum); - SignUpTokenRedisEntity signUpTokenRedisEntity = repository.findById(certNum).orElseThrow( + TokenRedisEntity tokenRedisEntity = repository.findById(certNum).orElseThrow( () -> new CustomRuntimeException(ErrorCode.NOT_FOUND_TOKEN) ); - return Token.from(signUpTokenRedisEntity); + return Token.from(tokenRedisEntity); } @Override diff --git a/src/main/java/life/mosu/mosuserver/domain/auth/signup/TokenRepository.java b/src/main/java/life/mosu/mosuserver/domain/auth/token/TokenRepository.java similarity index 76% rename from src/main/java/life/mosu/mosuserver/domain/auth/signup/TokenRepository.java rename to src/main/java/life/mosu/mosuserver/domain/auth/token/TokenRepository.java index 4aabd62f..eb783ece 100644 --- a/src/main/java/life/mosu/mosuserver/domain/auth/signup/TokenRepository.java +++ b/src/main/java/life/mosu/mosuserver/domain/auth/token/TokenRepository.java @@ -1,6 +1,7 @@ -package life.mosu.mosuserver.domain.auth.signup; +package life.mosu.mosuserver.domain.auth.token; public interface TokenRepository { + Token findByCertNum(String certNum); void save(Token signUpToken); diff --git a/src/main/java/life/mosu/mosuserver/domain/user/entity/UserJpaEntity.java b/src/main/java/life/mosu/mosuserver/domain/user/entity/UserJpaEntity.java index 06edfb60..9afe7ec9 100644 --- a/src/main/java/life/mosu/mosuserver/domain/user/entity/UserJpaEntity.java +++ b/src/main/java/life/mosu/mosuserver/domain/user/entity/UserJpaEntity.java @@ -113,6 +113,10 @@ public boolean isMosuUser() { return this.provider.equals(AuthProvider.MOSU); } + public boolean isPendingUser() { + return this.userRole.equals(UserRole.ROLE_PENDING); + } + public void grantUserRole() { this.userRole = UserRole.ROLE_USER; } diff --git a/src/main/java/life/mosu/mosuserver/global/annotation/PasswordPattern.java b/src/main/java/life/mosu/mosuserver/global/annotation/PasswordPattern.java index a0cef598..b2f88b96 100644 --- a/src/main/java/life/mosu/mosuserver/global/annotation/PasswordPattern.java +++ b/src/main/java/life/mosu/mosuserver/global/annotation/PasswordPattern.java @@ -9,7 +9,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,20}$", message = "비밀번호 형식이 올바르지 않습니다.") +@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[!@#$%^&*_/+=])[A-Za-z\\d!@#$%^&*_/+=]{8,20}$", message = "비밀번호 형식이 올바르지 않습니다.") @NotBlank @Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) diff --git a/src/main/java/life/mosu/mosuserver/global/exception/CriticalLevel.java b/src/main/java/life/mosu/mosuserver/global/exception/CriticalLevel.java new file mode 100644 index 00000000..c702453e --- /dev/null +++ b/src/main/java/life/mosu/mosuserver/global/exception/CriticalLevel.java @@ -0,0 +1,26 @@ +package life.mosu.mosuserver.global.exception; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public enum CriticalLevel { + LOW("낮음"), + MEDIUM("중간"), + HIGH("높음"), + CRITICAL("심각함"); + + private final String description; + + public String getDescription() { + return description; + } + + public boolean isEmergency() { + return this == HIGH || this == CRITICAL; + } + + @Override + public String toString() { + return this.description; + } +} diff --git a/src/main/java/life/mosu/mosuserver/global/exception/CustomRuntimeException.java b/src/main/java/life/mosu/mosuserver/global/exception/CustomRuntimeException.java index 6df5ab3c..32aa0b19 100644 --- a/src/main/java/life/mosu/mosuserver/global/exception/CustomRuntimeException.java +++ b/src/main/java/life/mosu/mosuserver/global/exception/CustomRuntimeException.java @@ -9,11 +9,13 @@ public class CustomRuntimeException extends RuntimeException { private final HttpStatus status; + private final CriticalLevel criticalLevel; private final String message; private final String code; public CustomRuntimeException(ErrorCode errorCode) { this.status = errorCode.getStatus(); + this.criticalLevel = errorCode.getCriticalLevel(); this.message = errorCode.getMessage(); this.code = errorCode.name(); @@ -22,6 +24,7 @@ public CustomRuntimeException(ErrorCode errorCode) { public CustomRuntimeException(ErrorCode errorCode, Object... cause) { this.status = errorCode.getStatus(); + this.criticalLevel = errorCode.getCriticalLevel(); this.message = String.format(errorCode.getMessage(), cause); this.code = errorCode.name(); diff --git a/src/main/java/life/mosu/mosuserver/global/exception/ErrorCode.java b/src/main/java/life/mosu/mosuserver/global/exception/ErrorCode.java index 1e705d44..88c23116 100644 --- a/src/main/java/life/mosu/mosuserver/global/exception/ErrorCode.java +++ b/src/main/java/life/mosu/mosuserver/global/exception/ErrorCode.java @@ -8,167 +8,192 @@ @RequiredArgsConstructor public enum ErrorCode { // Principal 관련 에러 - ALREADY_MOSU_USER(HttpStatus.BAD_REQUEST, "이미 모수 회원입니다."), - PRINCIPAL_NOT_FOUND(HttpStatus.UNAUTHORIZED, "인증된 사용자를 찾을 수 없습니다."), - LOGIN_BLOCKED(HttpStatus.UNAUTHORIZED, "로그인 시도가 차단되었습니다. 잠시 후 다시 시도해주세요."), - TOO_MANY_REQUESTS(HttpStatus.TOO_MANY_REQUESTS, "요청이 너무 많습니다. 잠시 후 다시 시도해주세요."), + ALREADY_MOSU_USER(HttpStatus.BAD_REQUEST, "이미 모수 회원입니다.", CriticalLevel.HIGH), + PRINCIPAL_NOT_FOUND(HttpStatus.UNAUTHORIZED, "인증된 사용자를 찾을 수 없습니다.", CriticalLevel.MEDIUM), + LOGIN_BLOCKED(HttpStatus.UNAUTHORIZED, "로그인 시도가 차단되었습니다. 잠시 후 다시 시도해주세요.", CriticalLevel.HIGH), + TOO_MANY_REQUESTS(HttpStatus.TOO_MANY_REQUESTS, "요청이 너무 많습니다. 잠시 후 다시 시도해주세요.", + CriticalLevel.HIGH), + // OAuth 관련 에러 - UNSUPPORTED_OAUTH2_PROVIDER(HttpStatus.BAD_REQUEST, "지원하지 않는 OAuth2 제공자입니다."), - OAUTH_USER_ALREADY_EXISTS(HttpStatus.CONFLICT, "이미 존재하는 OAuth 사용자입니다."), - FAILED_TO_GET_KAKAO_OAUTH_USER(HttpStatus.INTERNAL_SERVER_ERROR, "OAuth 사용자 생성에 실패했습니다."), - INSUFFICIENT_KAKAO_USER_DATA(HttpStatus.BAD_REQUEST, "카카오로부터 필수 동의 항목 정보를 모두 받지 못했습니다."), - DO_NOT_PARSE_KAKAO_BIRTHDAY(HttpStatus.BAD_REQUEST, "카카오 생년월일을 파싱할 수 없습니다."), + UNSUPPORTED_OAUTH2_PROVIDER(HttpStatus.BAD_REQUEST, "지원하지 않는 OAuth2 제공자입니다.", + CriticalLevel.HIGH), + OAUTH_USER_ALREADY_EXISTS(HttpStatus.CONFLICT, "이미 존재하는 OAuth 사용자입니다.", CriticalLevel.HIGH), + FAILED_TO_GET_KAKAO_OAUTH_USER(HttpStatus.INTERNAL_SERVER_ERROR, "OAuth 사용자 생성에 실패했습니다.", + CriticalLevel.CRITICAL), + INSUFFICIENT_KAKAO_USER_DATA(HttpStatus.BAD_REQUEST, "카카오로부터 필수 동의 항목 정보를 모두 받지 못했습니다.", + CriticalLevel.CRITICAL), + DO_NOT_PARSE_KAKAO_BIRTHDAY(HttpStatus.BAD_REQUEST, "카카오 생년월일을 파싱할 수 없습니다.", + CriticalLevel.CRITICAL), // Auth 관련 에러 - INCORRECT_ID_OR_PASSWORD(HttpStatus.UNAUTHORIZED, "아이디 또는 비밀번호가 일치하지 않습니다."), - INVALID_TOKEN_TYPE(HttpStatus.UNAUTHORIZED, "유효하지 않은 토큰 타입입니다."), - EXPIRED_TOKEN(HttpStatus.UNAUTHORIZED, "토큰이 만료되었습니다."), - INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 토큰입니다."), - INVALID_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 리프레시 토큰입니다."), - VERIFICATION_FAILED(HttpStatus.UNAUTHORIZED, "인증에 실패했습니다."), + INCORRECT_ID_OR_PASSWORD(HttpStatus.UNAUTHORIZED, "아이디 또는 비밀번호가 일치하지 않습니다.", CriticalLevel.LOW), + INVALID_TOKEN_TYPE(HttpStatus.UNAUTHORIZED, "유효하지 않은 토큰 타입입니다.", CriticalLevel.LOW), + EXPIRED_TOKEN(HttpStatus.UNAUTHORIZED, "토큰이 만료되었습니다.", CriticalLevel.LOW), + INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 토큰입니다.", CriticalLevel.LOW), + INVALID_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 리프레시 토큰입니다.", CriticalLevel.LOW), + VERIFICATION_FAILED(HttpStatus.UNAUTHORIZED, "인증에 실패했습니다.", CriticalLevel.LOW), + + KAKAO_DUPLICATED(HttpStatus.CONFLICT, "이미 존재하는 카카오 계정입니다.", CriticalLevel.LOW), //토큰 관련 에러 - INVALID_SIGN_UP_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 회원가입 인증 토큰입니다."), - MISSING_SIGNUP_TOKEN(HttpStatus.BAD_REQUEST, "회원가입 인증 토큰이 누락되었습니다."), - MISSING_PASSWORD_TOKEN(HttpStatus.BAD_REQUEST, "비밀번호 변경 토큰이 누락되었습니다."), - COOKIE_NOT_FOUND(HttpStatus.NOT_FOUND, "쿠키가 존재하지 않습니다."), + INVALID_SIGN_UP_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 회원가입 인증 토큰입니다.", CriticalLevel.LOW), + MISSING_SIGNUP_TOKEN(HttpStatus.BAD_REQUEST, "회원가입 인증 토큰이 누락되었습니다.", CriticalLevel.LOW), + MISSING_PASSWORD_TOKEN(HttpStatus.BAD_REQUEST, "비밀번호 변경 토큰이 누락되었습니다.", CriticalLevel.LOW), + COOKIE_NOT_FOUND(HttpStatus.NOT_FOUND, "쿠키가 존재하지 않습니다.", CriticalLevel.LOW), - NOT_FOUND_TOKEN(HttpStatus.NOT_FOUND, "인증 토큰을 찾을 수 없습니다."), - NOT_FOUND_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED, "액세스 토큰을 찾을 수 없습니다."), - NOT_FOUND_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED, "리프레시 토큰이 존재하지 않습니다."), - EXPIRED_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED, "리프레시 토큰의 유효기간이 만료 되었습니다."), + NOT_FOUND_TOKEN(HttpStatus.NOT_FOUND, "인증 토큰을 찾을 수 없습니다.", CriticalLevel.LOW), + NOT_FOUND_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED, "액세스 토큰을 찾을 수 없습니다.", CriticalLevel.LOW), + NOT_FOUND_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED, "리프레시 토큰이 존재하지 않습니다.", CriticalLevel.LOW), + EXPIRED_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED, "리프레시 토큰의 유효기간이 만료 되었습니다.", CriticalLevel.LOW), // 유저 관련 에러 - USER_ALREADY_EXISTS(HttpStatus.CONFLICT, "이미 존재하는 사용자입니다."), - USER_NOT_FOUND(HttpStatus.NOT_FOUND, "사용자를 찾을 수 없습니다."), - USER_INFO_INVALID(HttpStatus.BAD_REQUEST, "유효하지 않은 사용자 정보입니다."), - USER_NOT_ACCESS_FORBIDDEN(HttpStatus.BAD_REQUEST, "접근 권한이 없는 사용자입니다"), - USER_SAVE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "사용자 저장에 실패했습니다."), + USER_ALREADY_EXISTS(HttpStatus.CONFLICT, "이미 존재하는 사용자입니다.", CriticalLevel.HIGH), + USER_NOT_FOUND(HttpStatus.NOT_FOUND, "사용자를 찾을 수 없습니다.", CriticalLevel.LOW), + USER_INFO_INVALID(HttpStatus.BAD_REQUEST, "유효하지 않은 사용자 정보입니다.", CriticalLevel.LOW), + USER_NOT_ACCESS_FORBIDDEN(HttpStatus.BAD_REQUEST, "접근 권한이 없는 사용자입니다", CriticalLevel.MEDIUM), + USER_SAVE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "사용자 저장에 실패했습니다.", CriticalLevel.CRITICAL), // 신청 관련 에러 - WRONG_SUBJECT_TYPE(HttpStatus.BAD_REQUEST, "잘못된 과목명 입니다."), - WRONG_LUNCH_TYPE(HttpStatus.BAD_REQUEST, "잘못된 도시락명 입니다."), - WRONG_AREA_TYPE(HttpStatus.BAD_REQUEST, "잘못된 지역명 입니다."), - WRONG_SUBJECT_COUNT(HttpStatus.BAD_REQUEST, "응시과목은 반드시 다른 과목 2개를 신청해야 합니다."), - NOT_AGREED_TO_TERMS(HttpStatus.BAD_REQUEST, "신청 시 모든 약관에 동의해야 합니다."), + WRONG_SUBJECT_TYPE(HttpStatus.BAD_REQUEST, "잘못된 과목명 입니다.", CriticalLevel.LOW), + WRONG_LUNCH_TYPE(HttpStatus.BAD_REQUEST, "잘못된 도시락명 입니다.", CriticalLevel.HIGH), + WRONG_AREA_TYPE(HttpStatus.BAD_REQUEST, "잘못된 지역명 입니다.", CriticalLevel.HIGH), + WRONG_SUBJECT_COUNT(HttpStatus.BAD_REQUEST, "응시과목은 반드시 다른 과목 2개를 신청해야 합니다.", CriticalLevel.LOW), + NOT_AGREED_TO_TERMS(HttpStatus.BAD_REQUEST, "신청 시 모든 약관에 동의해야 합니다.", CriticalLevel.LOW), // 수험표 관련 에러 - EXAM_TICKET_NOT_OPEN(HttpStatus.BAD_REQUEST, "수험표 조회 기간이 아닙니다."), - EXAM_RESOURCE_ACCESS_DENIED(HttpStatus.BAD_REQUEST, "수험표 접근을 허용할 수 없습니다."), - EXAM_QUOTA_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 시험의 신청 정원을 찾을 수 없습니다."), - EXAM_QUOTA_EXCEEDED(HttpStatus.CONFLICT, "해당 시험의 신청 정원이 초과되었습니다."), - EXAM_QUOTA_ZERO_OR_NEGATIVE(HttpStatus.CONFLICT, "해당 시험의 신청 정원이 0이거나 음수입니다."), + EXAM_TICKET_NOT_OPEN(HttpStatus.BAD_REQUEST, "수험표 조회 기간이 아닙니다.", CriticalLevel.LOW), + EXAM_RESOURCE_ACCESS_DENIED(HttpStatus.BAD_REQUEST, "수험표 접근을 허용할 수 없습니다.", CriticalLevel.LOW), + EXAM_QUOTA_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 시험의 신청 정원을 찾을 수 없습니다.", CriticalLevel.HIGH), + EXAM_QUOTA_EXCEEDED(HttpStatus.CONFLICT, "해당 시험의 신청 정원이 초과되었습니다.", CriticalLevel.LOW), + EXAM_QUOTA_ZERO_OR_NEGATIVE(HttpStatus.CONFLICT, "해당 시험의 신청 정원이 0이거나 음수입니다.", + CriticalLevel.LOW), // 신청 학교 관련 에러 - EXAM_APPLICATION_NOT_FOUND(HttpStatus.NOT_FOUND, "신청한 학교 정보를 찾을 수 없습니다."), - APPLICATION_SCHOOL_LIST_NOT_FOUND(HttpStatus.NOT_FOUND, "신청한 학교 목록을 찾을 수 없습니다."), - APPLICATION_SCHOOL_NOT_FOUND(HttpStatus.NOT_FOUND, "신청 정보를 찾을 수 없습니다."), - APPLICATION_LIST_NOT_FOUND(HttpStatus.NOT_FOUND, "신청 내역을 찾을 수 없습니다."), - SCHOOL_NOT_FOUND(HttpStatus.NOT_FOUND, "학교가 존재하지 않습니다."), - SCHOOL_FULL(HttpStatus.CONFLICT, "해당 학교의 신청 정원이 모두 찼습니다."), - APPLICATION_SCHOOL_ALREADY_APPLIED(HttpStatus.CONFLICT, "해당 학교를 이미 예약하였습니다."), - APPLICATION_SCHOOL_DUPLICATED(HttpStatus.BAD_REQUEST, "동일 일자의 같은 학교를 신청할 수 없습니다."), - EXAM_DUPLICATED(HttpStatus.BAD_REQUEST, "동일한 시험을 신청할 수 없습니다."), - WRONG_APPLICATION_ID_TYPE(HttpStatus.BAD_REQUEST, "신청 ID 타입이 올바르지 않습니다."), - PRICE_LOAD_FAILURE(HttpStatus.CONFLICT, "신청 가격 정보를 가져오는데 실패했습니다."), - APPLICATION_CLOSED(HttpStatus.BAD_REQUEST, "신청이 마감되었습니다."), + EXAM_APPLICATION_NOT_FOUND(HttpStatus.NOT_FOUND, "신청한 학교 정보를 찾을 수 없습니다.", CriticalLevel.LOW), + APPLICATION_SCHOOL_LIST_NOT_FOUND(HttpStatus.NOT_FOUND, "신청한 학교 목록을 찾을 수 없습니다.", + CriticalLevel.MEDIUM), + APPLICATION_SCHOOL_NOT_FOUND(HttpStatus.NOT_FOUND, "신청 정보를 찾을 수 없습니다.", CriticalLevel.MEDIUM), + APPLICATION_LIST_NOT_FOUND(HttpStatus.NOT_FOUND, "신청 내역을 찾을 수 없습니다.", CriticalLevel.LOW), + SCHOOL_NOT_FOUND(HttpStatus.NOT_FOUND, "학교가 존재하지 않습니다.", CriticalLevel.HIGH), + SCHOOL_FULL(HttpStatus.CONFLICT, "해당 학교의 신청 정원이 모두 찼습니다.", CriticalLevel.LOW), + APPLICATION_SCHOOL_ALREADY_APPLIED(HttpStatus.CONFLICT, "해당 학교를 이미 예약하였습니다.", + CriticalLevel.LOW), + APPLICATION_SCHOOL_DUPLICATED(HttpStatus.BAD_REQUEST, "동일 일자의 같은 학교를 신청할 수 없습니다.", + CriticalLevel.LOW), + EXAM_DUPLICATED(HttpStatus.BAD_REQUEST, "동일한 시험을 신청할 수 없습니다.", CriticalLevel.HIGH), + WRONG_APPLICATION_ID_TYPE(HttpStatus.BAD_REQUEST, "신청 ID 타입이 올바르지 않습니다.", CriticalLevel.HIGH), + PRICE_LOAD_FAILURE(HttpStatus.CONFLICT, "신청 가격 정보를 가져오는데 실패했습니다.", CriticalLevel.CRITICAL), + APPLICATION_CLOSED(HttpStatus.BAD_REQUEST, "신청이 마감되었습니다.", CriticalLevel.LOW), // 프로필 관련 에러 - PROFILE_ALREADY_EXISTS(HttpStatus.CONFLICT, "프로필이 이미 존재합니다."), - PROFILE_NOT_FOUND(HttpStatus.NOT_FOUND, "프로필을 찾을 수 없습니다."), - PROFILE_DOES_NOT_EXIST(HttpStatus.BAD_REQUEST, "프로필이 존재하지 않습니다."), - PROFILE_CREATE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "프로필 등록에 실패했습니다."), - PROFILE_UPDATE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "프로필 수정에 실패했습니다."), - INVALID_GENDER(HttpStatus.BAD_REQUEST, "유효하지 않은 성별 값입니다."), - ALREADY_REGISTERED_RECOMMENDER(HttpStatus.CONFLICT, "이미 추천인을 등록하였습니다."), + PROFILE_ALREADY_EXISTS(HttpStatus.CONFLICT, "프로필이 이미 존재합니다.", CriticalLevel.LOW), + PROFILE_NOT_FOUND(HttpStatus.NOT_FOUND, "프로필을 찾을 수 없습니다.", CriticalLevel.LOW), + PROFILE_DOES_NOT_EXIST(HttpStatus.BAD_REQUEST, "프로필이 존재하지 않습니다.", CriticalLevel.LOW), + PROFILE_CREATE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "프로필 등록에 실패했습니다.", + CriticalLevel.CRITICAL), + PROFILE_UPDATE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "프로필 수정에 실패했습니다.", + CriticalLevel.CRITICAL), + INVALID_GENDER(HttpStatus.BAD_REQUEST, "유효하지 않은 성별 값입니다.", CriticalLevel.MEDIUM), + ALREADY_REGISTERED_RECOMMENDER(HttpStatus.CONFLICT, "이미 추천인을 등록하였습니다.", CriticalLevel.MEDIUM), // 파일 관련 에러 - FILE_NOT_FOUND(HttpStatus.NOT_FOUND, "파일을 찾을 수 없습니다."), - FILE_UPLOAD_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "파일 업로드에 실패했습니다."), - FILE_DOWNLOAD_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "파일 다운로드에 실패했습니다."), - FILE_DELETE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "파일 삭제에 실패했습니다."), - WRONG_FOLDER_TYPE(HttpStatus.BAD_REQUEST, "잘못된 폴더명 입니다."), + FILE_NOT_FOUND(HttpStatus.NOT_FOUND, "파일을 찾을 수 없습니다.", CriticalLevel.LOW), + FILE_UPLOAD_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "파일 업로드에 실패했습니다.", CriticalLevel.HIGH), + FILE_DOWNLOAD_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "파일 다운로드에 실패했습니다.", CriticalLevel.HIGH), + FILE_DELETE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "파일 삭제에 실패했습니다.", CriticalLevel.MEDIUM), + WRONG_FOLDER_TYPE(HttpStatus.BAD_REQUEST, "잘못된 폴더명 입니다.", CriticalLevel.MEDIUM), // 이벤트 관련 에러 - EVENT_NOT_FOUND(HttpStatus.NOT_FOUND, "이벤트를 찾을 수 없습니다."), + EVENT_NOT_FOUND(HttpStatus.NOT_FOUND, "이벤트를 찾을 수 없습니다.", CriticalLevel.HIGH), // Enum 관련 에러 - NOT_FOUND_AREA(HttpStatus.NOT_FOUND, "해당 지역을 찾을 수 없습니다."), - NOT_FOUND_LUNCH(HttpStatus.NOT_FOUND, "해당 도시락을 찾을 수 없습니다."), + NOT_FOUND_AREA(HttpStatus.NOT_FOUND, "해당 지역을 찾을 수 없습니다.", CriticalLevel.HIGH), + NOT_FOUND_LUNCH(HttpStatus.NOT_FOUND, "해당 도시락을 찾을 수 없습니다.", CriticalLevel.LOW), // FAQ 관련 에러 - FAQ_NOT_FOUND(HttpStatus.NOT_FOUND, "FAQ를 찾을 수 없습니다."), - FAQ_CREATION_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "FAQ 등록에 실패했습니다."), + FAQ_NOT_FOUND(HttpStatus.NOT_FOUND, "FAQ를 찾을 수 없습니다.", CriticalLevel.LOW), + FAQ_CREATION_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "FAQ 등록에 실패했습니다.", + CriticalLevel.CRITICAL), // 서버 관련 에러 - SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 오류가 발생했습니다."), + SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 오류가 발생했습니다.", CriticalLevel.CRITICAL), // 엑셀 생성 관련 에러 - EXCEL_CREATION_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "엑셀 생성에 실패했습니다."), + EXCEL_CREATION_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "엑셀 생성에 실패했습니다.", + CriticalLevel.CRITICAL), // 문의 관련 에러 - INQUIRY_NOT_FOUND(HttpStatus.NOT_FOUND, "문의 내역을 찾을 수 없습니다."), - INQUIRY_ANSWER_NOT_FOUND(HttpStatus.NOT_FOUND, "문의 답변을 찾을 수 없습니다."), - INQUIRY_ANSWER_ALREADY_EXISTS(HttpStatus.CONFLICT, "이미 문의 답변이 존재합니다."), + INQUIRY_NOT_FOUND(HttpStatus.NOT_FOUND, "문의 내역을 찾을 수 없습니다.", CriticalLevel.MEDIUM), + INQUIRY_ANSWER_NOT_FOUND(HttpStatus.NOT_FOUND, "문의 답변을 찾을 수 없습니다.", CriticalLevel.LOW), + INQUIRY_ANSWER_ALREADY_EXISTS(HttpStatus.CONFLICT, "이미 문의 답변이 존재합니다.", CriticalLevel.LOW), // 공지 관련 에러 - NOTICE_NOT_FOUND(HttpStatus.NOT_FOUND, "공지사항을 찾을 수 없습니다."), + NOTICE_NOT_FOUND(HttpStatus.NOT_FOUND, "공지사항을 찾을 수 없습니다.", CriticalLevel.HIGH), // 알림톡 관련 에러 - NOTIFY_STATUS_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 알림을 찾을 수 없습니다."), - STRATEGY_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 전략을 찾을 수 없습니다."), - INVALID_NOTIFICATION_STATUS(HttpStatus.BAD_REQUEST, "유효하지 않은 알림 상태입니다."), + NOTIFY_STATUS_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 알림을 찾을 수 없습니다.", CriticalLevel.HIGH), + STRATEGY_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 전략을 찾을 수 없습니다.", CriticalLevel.HIGH), + INVALID_NOTIFICATION_STATUS(HttpStatus.BAD_REQUEST, "유효하지 않은 알림 상태입니다.", CriticalLevel.HIGH), // multi-insert 관련 - EXAM_APPLICATION_MULTI_INSERT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "신청 학교 정보 삽입 실패하였습니다."), - EXAM_SUBJECT_MULTI_INSERT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "응시 과목 정보 삽입 실패하였습니다."), + EXAM_APPLICATION_MULTI_INSERT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "신청 학교 정보 삽입 실패하였습니다.", + CriticalLevel.CRITICAL), + EXAM_SUBJECT_MULTI_INSERT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "응시 과목 정보 삽입 실패하였습니다.", + CriticalLevel.CRITICAL), // 시험 관련 에러 - EXAM_NOT_FOUND(HttpStatus.NOT_FOUND, "시험 정보를 찾을 수 없습니다."), - EXAM_DATE_PASSED(HttpStatus.BAD_REQUEST, "이미 지난 시험입니다."), - EXAM_NOT_APPLIED(HttpStatus.BAD_REQUEST, "1개 이상의 시험을 신청해야 합니다."), - EXAM_DATE_AFTER_DEADLINE(HttpStatus.BAD_REQUEST, "시험일은 접수 마감일 이후여야 합니다."), + EXAM_NOT_FOUND(HttpStatus.NOT_FOUND, "시험 정보를 찾을 수 없습니다.", CriticalLevel.LOW), + EXAM_DATE_PASSED(HttpStatus.BAD_REQUEST, "이미 지난 시험입니다.", CriticalLevel.LOW), + EXAM_NOT_APPLIED(HttpStatus.BAD_REQUEST, "1개 이상의 시험을 신청해야 합니다.", CriticalLevel.LOW), + EXAM_DATE_AFTER_DEADLINE(HttpStatus.BAD_REQUEST, "시험일은 접수 마감일 이후여야 합니다.", CriticalLevel.LOW), //lunch 관련 - LUNCH_NOT_FOUND(HttpStatus.NOT_FOUND, "점심 정보를 찾을 수 없습니다."), - LUNCH_PRICE_UPDATE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "가격 수정에 실패하였습니다."), - LUNCH_SELECTION_INVALID(HttpStatus.BAD_REQUEST, "점심이 등록되지 않은 시험에 점심 신청을 할 수 없습니다."), + LUNCH_NOT_FOUND(HttpStatus.NOT_FOUND, "점심 정보를 찾을 수 없습니다.", CriticalLevel.HIGH), + LUNCH_PRICE_UPDATE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "가격 수정에 실패하였습니다.", + CriticalLevel.CRITICAL), + LUNCH_SELECTION_INVALID(HttpStatus.BAD_REQUEST, "점심이 등록되지 않은 시험에 점심 신청을 할 수 없습니다.", + CriticalLevel.LOW), //payment 관련 - PAYMENT_ALREADY_EXISTS(HttpStatus.BAD_REQUEST, "이미 존재하는 결제 건 입니다."), - PAYMENT_FAILED(HttpStatus.CONFLICT, "결제에 실패하였습니다."), - PAYMENT_NOT_FOUND(HttpStatus.NOT_FOUND, "결제 정보를 찾을 수 없습니다."), - INVALID_PAYMENT_AMOUNT(HttpStatus.BAD_REQUEST, "유효하지 않은 결제 금액입니다."), + PAYMENT_ALREADY_EXISTS(HttpStatus.BAD_REQUEST, "이미 존재하는 결제 건 입니다.", CriticalLevel.LOW), + PAYMENT_FAILED(HttpStatus.CONFLICT, "결제에 실패하였습니다.", CriticalLevel.CRITICAL), + PAYMENT_NOT_FOUND(HttpStatus.NOT_FOUND, "결제 정보를 찾을 수 없습니다.", CriticalLevel.LOW), + INVALID_PAYMENT_AMOUNT(HttpStatus.BAD_REQUEST, "유효하지 않은 결제 금액입니다.", CriticalLevel.MEDIUM), //어드민 관련 - BANNER_NOT_FOUND(HttpStatus.NOT_FOUND, "배너 정보를 찾을 수 없습니다."), - BANNER_DELETE_FAILURE(HttpStatus.CONFLICT, "배너를 삭제하는 것에 실패하였습니다."), - EXCEL_DATA_EMPTY(HttpStatus.NOT_FOUND, "엑셀 데이터가 없습니다."), - EXCEL_DOWNLOAD_FAILURE(HttpStatus.CONFLICT, "엑셀 다운로드 중 문제가 발생했습니다."), - CACHE_UPDATE_FAIL(HttpStatus.CONFLICT, "캐시 업데이트에 실패하였습니다."), + BANNER_NOT_FOUND(HttpStatus.NOT_FOUND, "배너 정보를 찾을 수 없습니다.", CriticalLevel.MEDIUM), + BANNER_DELETE_FAILURE(HttpStatus.CONFLICT, "배너를 삭제하는 것에 실패하였습니다.", CriticalLevel.MEDIUM), + EXCEL_DATA_EMPTY(HttpStatus.NOT_FOUND, "엑셀 데이터가 없습니다.", CriticalLevel.LOW), + EXCEL_DOWNLOAD_FAILURE(HttpStatus.CONFLICT, "엑셀 다운로드 중 문제가 발생했습니다.", CriticalLevel.CRITICAL), + CACHE_UPDATE_FAIL(HttpStatus.CONFLICT, "캐시 업데이트에 실패하였습니다.", CriticalLevel.CRITICAL), // ID 찾기 관련 - NOT_FOUND_LOGIN_ID(HttpStatus.NOT_FOUND, "해당 아이디를 찾을 수 없습니다."), + NOT_FOUND_LOGIN_ID(HttpStatus.NOT_FOUND, "해당 아이디를 찾을 수 없습니다.", CriticalLevel.LOW), //결제 API 실패 - PAYMENT_API_ERROR(HttpStatus.BAD_REQUEST, "결제 API 호출에 실패하였습니다."), + PAYMENT_API_ERROR(HttpStatus.BAD_REQUEST, "결제 API 호출에 실패하였습니다.", CriticalLevel.CRITICAL), //환불 - REFUND_CALCULATION_FAILED(HttpStatus.CONFLICT, "환불 금액 계산에 실패하였습니다."), - REFUND_NOT_FOUND(HttpStatus.NOT_FOUND, "환불 정보를 찾을 수 없습니다."), + REFUND_CALCULATION_FAILED(HttpStatus.CONFLICT, "환불 금액 계산에 실패하였습니다.", CriticalLevel.LOW), + REFUND_NOT_FOUND(HttpStatus.NOT_FOUND, "환불 정보를 찾을 수 없습니다.", CriticalLevel.MEDIUM), // Form 관련 에러 - NOT_FOUND_FORM(HttpStatus.NOT_FOUND, "신청서를 찾을 수 없습니다."), + NOT_FOUND_FORM(HttpStatus.NOT_FOUND, "신청서를 찾을 수 없습니다.", CriticalLevel.LOW), //LUA 관련 - LUA_SCRIPT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "LUA 스크립트 실행 중 오류가 발생했습니다."), + LUA_SCRIPT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "LUA 스크립트 실행 중 오류가 발생했습니다.", + CriticalLevel.CRITICAL), //캐시 관련 - CACHE_OPERATION_NOT_SUPPORTED(HttpStatus.NOT_IMPLEMENTED, "캐시 작업이 지원되지 않습니다."), + CACHE_OPERATION_NOT_SUPPORTED(HttpStatus.NOT_IMPLEMENTED, "캐시 작업이 지원되지 않습니다.", + CriticalLevel.CRITICAL), - INVALID_VIRTUAL_ACCOUNT_DEPOSIT_EVENT(HttpStatus.BAD_REQUEST, "유효하지 않은 가상 계좌 입금 이벤트입니다."), - VIRTUAL_ACCOUNT_CREATION_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "가상 계좌 생성에 실패했습니다."), - VIRTUAL_ACCOUNT_LOG_NOT_FOUND(HttpStatus.NOT_FOUND, "가상 계좌 로그를 찾을 수 없습니다."); + INVALID_VIRTUAL_ACCOUNT_DEPOSIT_EVENT(HttpStatus.BAD_REQUEST, "유효하지 않은 가상 계좌 입금 이벤트입니다.", + CriticalLevel.MEDIUM), + VIRTUAL_ACCOUNT_CREATION_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "가상 계좌 생성에 실패했습니다.", + CriticalLevel.CRITICAL), + VIRTUAL_ACCOUNT_LOG_NOT_FOUND(HttpStatus.NOT_FOUND, "가상 계좌 로그를 찾을 수 없습니다.", CriticalLevel.LOW); private final HttpStatus status; private final String message; + private final CriticalLevel criticalLevel; } diff --git a/src/main/java/life/mosu/mosuserver/global/exception/GlobalExceptionHandler.java b/src/main/java/life/mosu/mosuserver/global/exception/GlobalExceptionHandler.java index ddf53e9a..7b200061 100644 --- a/src/main/java/life/mosu/mosuserver/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/life/mosu/mosuserver/global/exception/GlobalExceptionHandler.java @@ -32,8 +32,6 @@ public class GlobalExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity handleMethodArgumentNotValidException( MethodArgumentNotValidException ex) { - notifyIfNeeded(ex); - Map errors = new LinkedHashMap<>(); ex.getBindingResult().getFieldErrors().forEach(error -> { errors.put(error.getField(), error.getDefaultMessage()); @@ -51,8 +49,6 @@ public ResponseEntity handleMethodArgumentNotValidException( @ExceptionHandler(IllegalArgumentException.class) public ResponseEntity handleIllegalArgumentException( IllegalArgumentException ex) { - notifyIfNeeded(ex); - ErrorResponse response = ErrorResponse.builder() .status(HttpStatus.BAD_REQUEST.value()) .message(ex.getMessage()) @@ -64,8 +60,6 @@ public ResponseEntity handleIllegalArgumentException( @ExceptionHandler(EntityNotFoundException.class) public ResponseEntity handleEntityNotFoundException(EntityNotFoundException ex) { - notifyIfNeeded(ex); - ErrorResponse response = ErrorResponse.builder() .status(HttpStatus.NOT_FOUND.value()) .message("요청한 리소스가 존재하지 않습니다.") @@ -77,8 +71,6 @@ public ResponseEntity handleEntityNotFoundException(EntityNotFoun @ExceptionHandler(AuthenticationException.class) public ResponseEntity handleAuthenticationException(AuthenticationException ex) { - notifyIfNeeded(ex); - ErrorResponse response = ErrorResponse.builder() .status(HttpStatus.UNAUTHORIZED.value()) .message("인증에 실패했습니다") @@ -91,8 +83,6 @@ public ResponseEntity handleAuthenticationException(Authenticatio @ExceptionHandler(life.mosu.mosuserver.global.exception.AuthenticationException.class) public ResponseEntity handleCustomAuthenticationException( life.mosu.mosuserver.global.exception.AuthenticationException ex) { - notifyIfNeeded(ex); - ErrorResponse response = ErrorResponse.builder() .status(HttpStatus.UNAUTHORIZED.value()) .message("인증에 실패했습니다") @@ -104,8 +94,6 @@ public ResponseEntity handleCustomAuthenticationException( @ExceptionHandler(AccessDeniedException.class) public ResponseEntity handleAccessDeniedException(AccessDeniedException ex) { - notifyIfNeeded(ex); - ErrorResponse response = ErrorResponse.builder() .status(HttpStatus.FORBIDDEN.value()) .message("인가를 실패 했습니다") @@ -118,8 +106,6 @@ public ResponseEntity handleAccessDeniedException(AccessDeniedExc @ExceptionHandler(HttpMessageNotReadableException.class) public ResponseEntity handleHttpMessageNotReadableException( HttpMessageNotReadableException ex) { - notifyIfNeeded(ex); - ErrorResponse response = ErrorResponse.builder() .status(HttpStatus.CONFLICT.value()) .message("필드명 또는 데이터 타입이 일치하지 않습니다.") @@ -131,8 +117,6 @@ public ResponseEntity handleHttpMessageNotReadableException( @ExceptionHandler(NoResourceFoundException.class) public ResponseEntity handleNotFound(Exception ex) { - notifyIfNeeded(ex); - ErrorResponse response = ErrorResponse.builder() .status(HttpStatus.NOT_FOUND.value()) .message("요청하신 리소스를 찾을 수 없습니다.") @@ -145,7 +129,6 @@ public ResponseEntity handleNotFound(Exception ex) { @ExceptionHandler(MethodArgumentTypeMismatchException.class) public ResponseEntity handleTypeMismatch( MethodArgumentTypeMismatchException ex) { - notifyIfNeeded(ex); ErrorResponse response = ErrorResponse.builder() .status(HttpStatus.BAD_REQUEST.value()) .code("TYPE_MISMATCH") @@ -157,7 +140,6 @@ public ResponseEntity handleTypeMismatch( @ExceptionHandler(HandlerMethodValidationException.class) public ResponseEntity handleHandlerMethodValidation(HandlerMethodValidationException ex) { - notifyIfNeeded(ex); ErrorResponse response = ErrorResponse.builder() .status(HttpStatus.BAD_REQUEST.value()) .code("TYPE_MISMATCH") @@ -168,7 +150,6 @@ public ResponseEntity handleHandlerMethodValidation(HandlerMethodValidationEx @ExceptionHandler(ConstraintViolationException.class) public ResponseEntity handleConstraintViolation(ConstraintViolationException ex) { - notifyIfNeeded(ex); String message = ex.getConstraintViolations().stream() .map(ConstraintViolation::getMessage) .findFirst() @@ -212,13 +193,29 @@ public ResponseEntity handleCustomRuntimeException( private void notifyIfNeeded(Exception ex) { try { + CriticalLevel criticalLevel = resolveCriticalLevel(ex); + log.error("critical-level : {}, exception: {}", criticalLevel, ex.getMessage(), ex); + + if (!criticalLevel.isEmergency()) { + return; + } + DiscordExceptionNotifyEventRequest request = DiscordExceptionNotifyEventRequest.of( ex.getCause() != null ? ex.getCause().toString() : "Unknown Cause", - ex.getMessage() + ex.getMessage(), + criticalLevel.toString() ); notifier.send(request); - } catch (Exception notifyEx) { - log.error("[Discord Notify Error]", notifyEx); + + } catch (Exception e) { + log.error("알림 전송 중 오류 발생: {}", e.getMessage(), e); + } + } + + private CriticalLevel resolveCriticalLevel(Exception ex) { + if (ex instanceof CustomRuntimeException customEx) { + return customEx.getCriticalLevel(); } + return CriticalLevel.CRITICAL; } } diff --git a/src/main/java/life/mosu/mosuserver/infra/notify/LunaSoftNotifier.java b/src/main/java/life/mosu/mosuserver/infra/notify/LunaSoftNotifier.java index 2f27f3eb..f06e2c4e 100644 --- a/src/main/java/life/mosu/mosuserver/infra/notify/LunaSoftNotifier.java +++ b/src/main/java/life/mosu/mosuserver/infra/notify/LunaSoftNotifier.java @@ -30,9 +30,8 @@ public void send(LunaNotifyEventRequest request) { .retrieve() .bodyToMono(String.class) .publishOn(Schedulers.boundedElastic()) - .doOnSuccess(response -> log.info("알림톡 응답: {}", response)) + .doOnSuccess(response -> log.debug("알림톡 응답 성공")) .doOnError(error -> log.error("알림톡 전송 실패", error)) - .doOnTerminate(() -> log.info("알림톡 전송 완료: {}", request)) .subscribe(); } diff --git a/src/main/java/life/mosu/mosuserver/infra/notify/component/luna/LunaNotifyWithSmsFallbackSender.java b/src/main/java/life/mosu/mosuserver/infra/notify/component/luna/LunaNotifyWithSmsFallbackSender.java index 4301ba8b..d1de9aa9 100644 --- a/src/main/java/life/mosu/mosuserver/infra/notify/component/luna/LunaNotifyWithSmsFallbackSender.java +++ b/src/main/java/life/mosu/mosuserver/infra/notify/component/luna/LunaNotifyWithSmsFallbackSender.java @@ -46,7 +46,6 @@ public class LunaNotifyWithSmsFallbackSender @Override public void send(String targetPhoneNumber, LunaNotificationEvent event, T dto) { LunaNotifyTemplateCode templateCode = event.status().getTemplateCode(); - log.info("templateCode : {}", templateCode); String alimTalkContent = template.getProcessedMessage(templateCode, dto); diff --git a/src/main/java/life/mosu/mosuserver/infra/notify/dto/discord/DiscordExceptionNotifyEventRequest.java b/src/main/java/life/mosu/mosuserver/infra/notify/dto/discord/DiscordExceptionNotifyEventRequest.java index f07a2d45..e0924057 100644 --- a/src/main/java/life/mosu/mosuserver/infra/notify/dto/discord/DiscordExceptionNotifyEventRequest.java +++ b/src/main/java/life/mosu/mosuserver/infra/notify/dto/discord/DiscordExceptionNotifyEventRequest.java @@ -7,23 +7,28 @@ public record DiscordExceptionNotifyEventRequest( String exceptionCause, String exceptionMessage, String meta, + String criticalLevel, LocalDateTime timestamp ) { public static DiscordExceptionNotifyEventRequest of( String exceptionCause, String exceptionMessage, - String meta + String meta, + String criticalLevel ) { return new DiscordExceptionNotifyEventRequest(exceptionCause, exceptionMessage, meta, + criticalLevel, LocalDateTime.now()); } public static DiscordExceptionNotifyEventRequest of( String exceptionCause, - String exceptionMessage + String exceptionMessage, + String criticalLevel ) { return new DiscordExceptionNotifyEventRequest(exceptionCause, exceptionMessage, null, + criticalLevel, LocalDateTime.now()); } diff --git a/src/main/java/life/mosu/mosuserver/infra/persistence/jpa/ApplicationQueryRepositoryImpl.java b/src/main/java/life/mosu/mosuserver/infra/persistence/jpa/ApplicationQueryRepositoryImpl.java index d8d204e2..c553b001 100644 --- a/src/main/java/life/mosu/mosuserver/infra/persistence/jpa/ApplicationQueryRepositoryImpl.java +++ b/src/main/java/life/mosu/mosuserver/infra/persistence/jpa/ApplicationQueryRepositoryImpl.java @@ -2,8 +2,6 @@ import com.querydsl.core.Tuple; import com.querydsl.core.types.Predicate; -import com.querydsl.core.types.dsl.EnumPath; -import com.querydsl.core.types.dsl.Expressions; import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; import java.time.format.DateTimeFormatter; @@ -18,7 +16,12 @@ import life.mosu.mosuserver.domain.exam.entity.QExamJpaEntity; import life.mosu.mosuserver.domain.examapplication.entity.QExamApplicationJpaEntity; import life.mosu.mosuserver.domain.examapplication.entity.QExamSubjectJpaEntity; +import life.mosu.mosuserver.domain.payment.entity.PaymentMethod; +import life.mosu.mosuserver.domain.payment.entity.PaymentStatus; import life.mosu.mosuserver.domain.payment.entity.QPaymentJpaEntity; +import life.mosu.mosuserver.domain.profile.entity.Education; +import life.mosu.mosuserver.domain.profile.entity.Gender; +import life.mosu.mosuserver.domain.profile.entity.Grade; import life.mosu.mosuserver.domain.profile.entity.QProfileJpaEntity; import life.mosu.mosuserver.domain.user.entity.QUserJpaEntity; import life.mosu.mosuserver.infra.persistence.s3.S3Service; @@ -65,9 +68,9 @@ public Page searchAllApplications(ApplicationFilter fil List content = query.fetch().stream() .map(tuple -> { - Long appSchoolId = tuple.get(examApplication.id); - Set subjects = appSchoolId != null - ? findSubjectsByExamApplicationId(appSchoolId) + Long examApplicationId = tuple.get(examApplication.id); + Set subjects = examApplicationId != null + ? findSubjectsByExamApplicationId(examApplicationId) : new HashSet<>(); return mapToResponse(tuple, subjects); }) @@ -81,9 +84,9 @@ public List searchAllApplicationsForExcel() { JPAQuery query = baseQuery(); return query.fetch().stream() .map(tuple -> { - Long appSchoolId = tuple.get(examApplication.id); - Set subjects = appSchoolId != null - ? findSubjectsByExamApplicationId(appSchoolId) + Long examApplicationId = tuple.get(examApplication.id); + Set subjects = examApplicationId != null + ? findSubjectsByExamApplicationId(examApplicationId) : new HashSet<>(); return mapToExcel(tuple, subjects); }) @@ -96,9 +99,9 @@ private JPAQuery baseQuery() { examApplication.id, payment.paymentKey, examApplication.examNumber, - profile.userName, - profile.gender, - profile.birth, + user.name, + user.gender, + user.birth, profile.phoneNumber, application.parentPhoneNumber, profile.recommenderPhoneNumber, @@ -116,19 +119,20 @@ private JPAQuery baseQuery() { application.createdAt ) .from(examApplication) - .leftJoin(exam).on(examApplication.examId.eq(exam.id)) - .leftJoin(application).on(examApplication.applicationId.eq(application.id)) - .leftJoin(payment).on(payment.examApplicationId.eq(examApplication.id)) - .leftJoin(user).on(application.userId.eq(user.id)) - .leftJoin(profile).on(profile.userId.eq(user.id)) - .leftJoin(examTicketImage) - .on(examTicketImage.applicationId.eq(application.id)); + .join(exam).on(examApplication.examId.eq(exam.id)) + .join(application).on(examApplication.applicationId.eq(application.id)) + .join(payment).on(payment.examApplicationId.eq(examApplication.id)) + .join(user).on(application.userId.eq(user.id)) + .join(profile).on(profile.userId.eq(user.id)) + .join(examTicketImage) + .on(examTicketImage.applicationId.eq(application.id)) + .where(payment.paymentStatus.in(PaymentStatus.DONE, PaymentStatus.ABORTED)); } private Predicate buildNameCondition(String name) { return (name == null || name.isBlank()) ? null - : profile.userName.contains(name); + : user.name.contains(name); } private Predicate buildPhoneCondition(String phone) { @@ -138,7 +142,6 @@ private Predicate buildPhoneCondition(String phone) { } private Set findSubjectsByExamApplicationId(Long examApplicationId) { - EnumPath subject = Expressions.enumPath(Subject.class, "subject"); return new HashSet<>( queryFactory .select( @@ -160,24 +163,29 @@ private ApplicationListResponse mapToResponse(Tuple tuple, Set subjects ExamTicketResponse examTicketResponse = ExamTicketResponse.of( url, - tuple.get(profile.userName), - tuple.get(profile.birth), + tuple.get(user.name), + tuple.get(user.birth), tuple.get(examApplication.examNumber), subjectNames, tuple.get(exam.schoolName) ); + Gender gender = tuple.get(profile.gender); + Education education = tuple.get(profile.education); + Grade grade = tuple.get(profile.grade); + PaymentMethod paymentMethod = tuple.get(payment.paymentMethod); + return new ApplicationListResponse( tuple.get(payment.paymentKey), tuple.get(examApplication.examNumber), - tuple.get(profile.userName), - tuple.get(profile.gender).getGenderName(), - tuple.get(profile.birth), + tuple.get(user.name), + gender != null ? gender.getGenderName() : null, + tuple.get(user.birth), tuple.get(profile.phoneNumber), tuple.get(application.parentPhoneNumber), - tuple.get(profile.education).getEducationName(), + education != null ? education.getEducationName() : null, tuple.get(profile.schoolInfo.schoolName), - tuple.get(profile.grade).getGradeName(), + grade != null ? grade.getGradeName() : null, tuple.get(examApplication.isLunchChecked), tuple.get(exam.lunchName), subjectNames, @@ -185,7 +193,7 @@ private ApplicationListResponse mapToResponse(Tuple tuple, Set subjects tuple.get(exam.examDate), tuple.get(examTicketImage.fileName), tuple.get(payment.paymentStatus), - tuple.get(payment.paymentMethod), + paymentMethod != null ? paymentMethod.getName() : null, tuple.get(application.createdAt), examTicketResponse ); @@ -196,32 +204,33 @@ private ApplicationExcelDto mapToExcel(Tuple tuple, Set subjects) { .map(Subject::getSubjectName) .collect(Collectors.toSet()); - String lunchName = tuple.get(exam.lunchName); - String genderName = tuple.get(profile.gender).getGenderName(); - String gradeName = tuple.get(profile.grade).getGradeName(); - String educationName = tuple.get(profile.education).getEducationName(); + Gender gender = tuple.get(profile.gender); + Education education = tuple.get(profile.education); + Grade grade = tuple.get(profile.grade); + PaymentMethod paymentMethod = tuple.get(payment.paymentMethod); + String appliedAt = tuple.get(application.createdAt) .format(EXCEL_DT_FORMATTER); return new ApplicationExcelDto( tuple.get(payment.paymentKey), tuple.get(examApplication.examNumber), - tuple.get(profile.userName), - genderName, - tuple.get(profile.birth), + tuple.get(user.name), + gender != null ? gender.getGenderName() : null, + tuple.get(user.birth), tuple.get(profile.phoneNumber), tuple.get(application.parentPhoneNumber), tuple.get(profile.recommenderPhoneNumber), - educationName, + education != null ? education.getEducationName() : null, tuple.get(profile.schoolInfo.schoolName), - gradeName, - lunchName, + grade != null ? grade.getGradeName() : null, + tuple.get(exam.lunchName), subjectNames, tuple.get(exam.schoolName), tuple.get(exam.examDate), tuple.get(examTicketImage.fileName), tuple.get(payment.paymentStatus), - tuple.get(payment.paymentMethod), + paymentMethod != null ? paymentMethod.getName() : null, appliedAt ); } diff --git a/src/main/java/life/mosu/mosuserver/infra/persistence/jpa/RefundQueryRepositoryImpl.java b/src/main/java/life/mosu/mosuserver/infra/persistence/jpa/RefundQueryRepositoryImpl.java index b13834f8..96e9c45d 100644 --- a/src/main/java/life/mosu/mosuserver/infra/persistence/jpa/RefundQueryRepositoryImpl.java +++ b/src/main/java/life/mosu/mosuserver/infra/persistence/jpa/RefundQueryRepositoryImpl.java @@ -55,10 +55,7 @@ public Page searchAllRefunds(Pageable pageable) { public List searchAllRefundsForExcel() { JPAQuery query = baseQuery(); return query.fetch().stream() - .map(tuple -> { - log.info("tuple log : {} ", tuple); - return mapToExcel(tuple); - }) + .map(this::mapToExcel) .toList(); } diff --git a/src/main/java/life/mosu/mosuserver/presentation/admin/dto/ApplicationExcelDto.java b/src/main/java/life/mosu/mosuserver/presentation/admin/dto/ApplicationExcelDto.java index 6c15a106..5711c3f6 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/admin/dto/ApplicationExcelDto.java +++ b/src/main/java/life/mosu/mosuserver/presentation/admin/dto/ApplicationExcelDto.java @@ -3,7 +3,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import java.time.LocalDate; import java.util.Set; -import life.mosu.mosuserver.domain.payment.entity.PaymentMethod; import life.mosu.mosuserver.domain.payment.entity.PaymentStatus; import life.mosu.mosuserver.global.annotation.ExcelColumn; @@ -26,7 +25,7 @@ public record ApplicationExcelDto( @Schema(description = "시험 일자", example = "2025-08-10") @ExcelColumn(headerName = "시험 일자") LocalDate examDate, @Schema(description = "수험표 이미지 URL", example = "https://s3.amazonaws.com/bucket/admission/2025-00001.jpg") @ExcelColumn(headerName = "수험표 사진") String admissionTicketImage, @Schema(description = "결제 상태", example = "COMPLETED") @ExcelColumn(headerName = "결제 상태") PaymentStatus paymentStatus, - @Schema(description = "결제 방법", example = "CARD") @ExcelColumn(headerName = "결제 방법") PaymentMethod paymentMethod, + @Schema(description = "결제 방법", example = "CARD") @ExcelColumn(headerName = "결제 방법") String paymentMethod, @Schema(description = "신청 일시", example = "2025-07-10 15:30:00") @ExcelColumn(headerName = "신청 일시") String applicationDate) { } diff --git a/src/main/java/life/mosu/mosuserver/presentation/admin/dto/ApplicationListResponse.java b/src/main/java/life/mosu/mosuserver/presentation/admin/dto/ApplicationListResponse.java index 2e24ea12..8395943a 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/admin/dto/ApplicationListResponse.java +++ b/src/main/java/life/mosu/mosuserver/presentation/admin/dto/ApplicationListResponse.java @@ -4,7 +4,6 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; -import life.mosu.mosuserver.domain.payment.entity.PaymentMethod; import life.mosu.mosuserver.domain.payment.entity.PaymentStatus; @Schema(description = "관리자 신청 목록 응답 DTO") @@ -62,7 +61,7 @@ public record ApplicationListResponse( PaymentStatus paymentStatus, @Schema(description = "결제 방법", example = "CARD") - PaymentMethod paymentMethod, + String paymentMethod, @Schema(description = "신청 일시", example = "2025-07-10T15:30:00") LocalDateTime applicationDate, diff --git a/src/main/java/life/mosu/mosuserver/presentation/oauth/TokenService.java b/src/main/java/life/mosu/mosuserver/presentation/oauth/TokenService.java index 6659de35..891dddec 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/oauth/TokenService.java +++ b/src/main/java/life/mosu/mosuserver/presentation/oauth/TokenService.java @@ -1,8 +1,8 @@ package life.mosu.mosuserver.presentation.oauth; import life.mosu.mosuserver.application.auth.provider.OneTimeTokenProvider; -import life.mosu.mosuserver.domain.auth.signup.Token; -import life.mosu.mosuserver.domain.auth.signup.TokenRepository; +import life.mosu.mosuserver.domain.auth.token.Token; +import life.mosu.mosuserver.domain.auth.token.TokenRepository; import life.mosu.mosuserver.global.exception.AuthenticationException; import life.mosu.mosuserver.global.exception.CustomRuntimeException; import life.mosu.mosuserver.global.exception.ErrorCode; @@ -19,7 +19,7 @@ public class TokenService { private final TokenRepository repository; /** - * 회원가입 토큰을 검증하고, 유효하면 SignUpToken 객체를 반환합니다. + * 회원가입 토큰을 검증하고, 유효하면 SignUpInfo 객체를 반환합니다. * * @param requestToken 검증할 토큰 * @throws AuthenticationException 토큰이 유효하지 않을 경우 diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index ff165fb1..61179d33 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -1,42 +1,40 @@ - - - - - + - %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n UTF-8 + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - DEBUG + WARN - - - - ${LOG_FILE} + - %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n UTF-8 + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + ${LOG_FILE} INFO - - + + + + + + + + + + + + - - - - - -