diff --git a/src/main/java/com/writon/admin/domain/controller/ParticipationController.java b/src/main/java/com/writon/admin/domain/controller/ParticipationController.java index 38788af..68697ff 100644 --- a/src/main/java/com/writon/admin/domain/controller/ParticipationController.java +++ b/src/main/java/com/writon/admin/domain/controller/ParticipationController.java @@ -63,7 +63,7 @@ public SuccessDto> participate( return new SuccessDto<>(sendedEmailList); } else { - throw new CustomException(ErrorCode.EMAIL_DUPLICATION); + throw new CustomException(ErrorCode.EMAIL_DUPLICATE); } } diff --git a/src/main/java/com/writon/admin/domain/repository/activity/UserTemplateRepository.java b/src/main/java/com/writon/admin/domain/repository/activity/UserTemplateRepository.java index c883d0e..fa6deb4 100644 --- a/src/main/java/com/writon/admin/domain/repository/activity/UserTemplateRepository.java +++ b/src/main/java/com/writon/admin/domain/repository/activity/UserTemplateRepository.java @@ -7,7 +7,7 @@ public interface UserTemplateRepository extends JpaRepository { - Optional> findByUserChallengeId(Long userChallengeId); + List findByUserChallengeId(Long userChallengeId); int countByUserChallengeId(Long userChallengeId); } \ No newline at end of file diff --git a/src/main/java/com/writon/admin/domain/repository/challenge/ChallengeDayRepository.java b/src/main/java/com/writon/admin/domain/repository/challenge/ChallengeDayRepository.java index 97e3263..34c7c4f 100644 --- a/src/main/java/com/writon/admin/domain/repository/challenge/ChallengeDayRepository.java +++ b/src/main/java/com/writon/admin/domain/repository/challenge/ChallengeDayRepository.java @@ -7,6 +7,6 @@ public interface ChallengeDayRepository extends JpaRepository { - Optional> findByChallengeId(Long challengeId); + List findByChallengeId(Long challengeId); } \ No newline at end of file diff --git a/src/main/java/com/writon/admin/domain/repository/challenge/ChallengeRepository.java b/src/main/java/com/writon/admin/domain/repository/challenge/ChallengeRepository.java index af45554..c04d8b3 100644 --- a/src/main/java/com/writon/admin/domain/repository/challenge/ChallengeRepository.java +++ b/src/main/java/com/writon/admin/domain/repository/challenge/ChallengeRepository.java @@ -7,6 +7,6 @@ public interface ChallengeRepository extends JpaRepository { - Optional> findByOrganizationId(Long organizationId); + List findByOrganizationId(Long organizationId); } \ No newline at end of file diff --git a/src/main/java/com/writon/admin/domain/repository/challenge/EmailRepository.java b/src/main/java/com/writon/admin/domain/repository/challenge/EmailRepository.java index b471d88..e91df65 100644 --- a/src/main/java/com/writon/admin/domain/repository/challenge/EmailRepository.java +++ b/src/main/java/com/writon/admin/domain/repository/challenge/EmailRepository.java @@ -7,6 +7,6 @@ public interface EmailRepository extends JpaRepository { - Optional> findByChallengeId(Long challengeId); + List findByChallengeId(Long challengeId); } \ No newline at end of file diff --git a/src/main/java/com/writon/admin/domain/repository/organization/PositionRepository.java b/src/main/java/com/writon/admin/domain/repository/organization/PositionRepository.java index 1e414c6..1da6879 100644 --- a/src/main/java/com/writon/admin/domain/repository/organization/PositionRepository.java +++ b/src/main/java/com/writon/admin/domain/repository/organization/PositionRepository.java @@ -7,6 +7,6 @@ public interface PositionRepository extends JpaRepository { - Optional> findByOrganizationId(Long organizationId); + List findByOrganizationId(Long organizationId); } \ No newline at end of file diff --git a/src/main/java/com/writon/admin/domain/repository/question/QuestionRepository.java b/src/main/java/com/writon/admin/domain/repository/question/QuestionRepository.java index 26a6f7e..f74354e 100644 --- a/src/main/java/com/writon/admin/domain/repository/question/QuestionRepository.java +++ b/src/main/java/com/writon/admin/domain/repository/question/QuestionRepository.java @@ -8,7 +8,7 @@ public interface QuestionRepository extends JpaRepository { - Optional> findByChallengeId(Long challengeId); + List findByChallengeId(Long challengeId); int countByKeyword(Keyword keyword); diff --git a/src/main/java/com/writon/admin/domain/repository/user/UserChallengeRepository.java b/src/main/java/com/writon/admin/domain/repository/user/UserChallengeRepository.java index 4cf605a..051fe10 100644 --- a/src/main/java/com/writon/admin/domain/repository/user/UserChallengeRepository.java +++ b/src/main/java/com/writon/admin/domain/repository/user/UserChallengeRepository.java @@ -7,7 +7,7 @@ public interface UserChallengeRepository extends JpaRepository { - Optional> findByChallengeId(Long challengeId); - Optional> findByAffiliationId(Long affiliationId); + List findByChallengeId(Long challengeId); + List findByAffiliationId(Long affiliationId); } \ No newline at end of file diff --git a/src/main/java/com/writon/admin/domain/service/AuthService.java b/src/main/java/com/writon/admin/domain/service/AuthService.java index 28e2661..0082937 100644 --- a/src/main/java/com/writon/admin/domain/service/AuthService.java +++ b/src/main/java/com/writon/admin/domain/service/AuthService.java @@ -48,7 +48,7 @@ public class AuthService { // ========== SignUp API ========== public SignUpResponseDto signup(SignUpRequestDto signUpRequestDto) { if (adminUserRepository.existsByIdentifier(signUpRequestDto.getIdentifier())) { - throw new CustomException(ErrorCode.ETC_ERROR); + throw new CustomException(ErrorCode.USER_IDENTIFIER_DUPLICATE); } AdminUser encodedAdminUser = signUpRequestDto.toAdminUser(passwordEncoder); @@ -89,14 +89,13 @@ public LoginResponseWrapper login(LoginRequestDto loginRequestDto) { // 5. 해당 Organization 정보 가져오기 AdminUser adminUser = adminUserRepository.findByIdentifier(identifier) - .orElseThrow(() -> new UsernameNotFoundException(" -> 데이터베이스에서 찾을 수 없습니다.")); + .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND)); Optional organization = organizationRepository.findByAdminUserId(adminUser.getId()); // 7. 챌린지 정보 가져오기 List challenges = Collections.emptyList(); if (organization.isPresent()) { - challenges = challengeRepository.findByOrganizationId(organization.get().getId()) - .orElse(Collections.emptyList()); // 데이터가 없을 때 빈 리스트 반환 + challenges = challengeRepository.findByOrganizationId(organization.get().getId()); // 데이터가 없을 때 빈 리스트 반환 } List challengeList = challenges.stream() .map(entity -> new ChallengeResponse(entity.getId(), entity.getName())) diff --git a/src/main/java/com/writon/admin/domain/service/ChallengeService.java b/src/main/java/com/writon/admin/domain/service/ChallengeService.java index ff85084..82cb998 100644 --- a/src/main/java/com/writon/admin/domain/service/ChallengeService.java +++ b/src/main/java/com/writon/admin/domain/service/ChallengeService.java @@ -56,7 +56,7 @@ public class ChallengeService { private final UserTemplateRepository userTemplateRepository; private final EmailService emailService; - // ========== Create API ========== + // ========== CreateChallenge API ========== public CreateChallengeResponseDto createChallenge(CreateChallengeRequestDto requestDto) { // 1. 조직정보 추출 Organization organization = tokenUtil.getOrganization(); @@ -98,8 +98,11 @@ public CreateChallengeResponseDto createChallenge(CreateChallengeRequestDto requ } // 6. Response 생성 - List challenges = challengeRepository.findByOrganizationId(organization.getId()) - .orElseThrow(() -> new CustomException(ErrorCode.CHALLENGE_NOT_FOUND)); + List challenges = challengeRepository.findByOrganizationId(organization.getId()); + if (challenges.isEmpty()) { + throw new CustomException(ErrorCode.CHALLENGE_NOT_FOUND); + } + List challengeList = challenges.stream() .map(entity -> new ChallengeResponse(entity.getId(), entity.getName())) .collect(Collectors.toList()); @@ -110,16 +113,18 @@ public CreateChallengeResponseDto createChallenge(CreateChallengeRequestDto requ // ========== Get Dashboard API ========== public List getDashboard(Long challengeId) { // 1. 챌린지 날짜 리스트 추출 - List challengeDayList = challengeDayRepository.findByChallengeId(challengeId) - .orElseThrow(() -> new CustomException(ErrorCode.ETC_ERROR)); + List challengeDayList = challengeDayRepository.findByChallengeId(challengeId); + + if (challengeDayList.isEmpty()) { + throw new CustomException(ErrorCode.CHALLENGE_DAY_NOT_FOUND); + } challengeDayList = challengeDayList.stream() .sorted(Comparator.comparing(ChallengeDay::getDay)) .toList(); // 2. 챌린지 참여 유저 리스트 추출 - List userChallengeList = userChallengeRepository.findByChallengeId(challengeId) - .orElseThrow(() -> new CustomException(ErrorCode.ETC_ERROR)); + List userChallengeList = userChallengeRepository.findByChallengeId(challengeId); // 3. 유저별 참여여부 확인 List statusTable = new ArrayList<>(); @@ -127,8 +132,10 @@ public List getDashboard(Long challengeId) { for (UserChallenge userChallenge : userChallengeList) { List statusList = new ArrayList<>(); List userTemplateList = userTemplateRepository.findByUserChallengeId( - userChallenge.getId()) - .orElseThrow(() -> new CustomException(ErrorCode.ETC_ERROR)); + userChallenge.getId()); + if (userTemplateList.isEmpty()) { + throw new CustomException(ErrorCode.USER_TEMPLATE_NOT_FOUND); + } for (ChallengeDay challengeDay : challengeDayList) { // 참여여부 확인과정 @@ -149,8 +156,10 @@ public List getDashboard(Long challengeId) { // ========== Get Questions API ========== public QuestionsResponseDto getQuestions(Long challengeId) { - List questionList = questionRepository.findByChallengeId(challengeId) - .orElseThrow(() -> new CustomException((ErrorCode.ETC_ERROR))); + List questionList = questionRepository.findByChallengeId(challengeId); + if (questionList.isEmpty()) { + throw new CustomException(ErrorCode.QUESTION_NOT_FOUND); + } List basicQuestions = questionList.stream() .filter(question -> question.getKeyword() == null) @@ -179,17 +188,20 @@ public QuestionsResponseDto getQuestions(Long challengeId) { public ChallengeInfoResponseDto getInfo(Long challengeId) { // 1. 챌린지 기본 정보 가져오기 Challenge challenge = challengeRepository.findById(challengeId) - .orElseThrow(() -> new CustomException(ErrorCode.ETC_ERROR)); + .orElseThrow(() -> new CustomException(ErrorCode.CHALLENGE_NOT_FOUND)); // 2. 챌린지 날짜 정보 가져오기 - List challengeDays = challengeDayRepository.findByChallengeId(challengeId) - .orElseThrow(() -> new CustomException(ErrorCode.ETC_ERROR)); + List challengeDayList = challengeDayRepository.findByChallengeId(challengeId); + + if (challengeDayList.isEmpty()) { + throw new CustomException(ErrorCode.CHALLENGE_DAY_NOT_FOUND); + } return new ChallengeInfoResponseDto( challenge.getName(), challenge.getStartAt(), challenge.getFinishAt(), - challengeDays.stream().map(ChallengeDay::getDay).collect(Collectors.toList()) + challengeDayList.stream().map(ChallengeDay::getDay).collect(Collectors.toList()) ); } @@ -197,11 +209,13 @@ public ChallengeInfoResponseDto getInfo(Long challengeId) { public QuestionsResponseDto putQuestions(Long challengeId, QuestionsRequestDto requestDto) { // 1. 챌린지 조회 Challenge challenge = challengeRepository.findById(challengeId) - .orElseThrow(() -> new CustomException(ErrorCode.ETC_ERROR)); + .orElseThrow(() -> new CustomException(ErrorCode.CHALLENGE_NOT_FOUND)); // 2. 질문 리스트 조회 - List questionList = questionRepository.findByChallengeId(challengeId) - .orElseThrow(() -> new CustomException((ErrorCode.ETC_ERROR))); + List questionList = questionRepository.findByChallengeId(challengeId); + if (questionList.isEmpty()) { + throw new CustomException(ErrorCode.QUESTION_NOT_FOUND); + } // 3. 베이직 질문 처리 List requestBasicQuestions = requestDto.getBasicQuestions(); @@ -310,7 +324,7 @@ public QuestionsResponseDto putQuestions(Long challengeId, QuestionsRequestDto r public ChallengeInfoResponseDto putInfo(Long challengeId, ChallengeInfoRequestDto requestDto) { // 1. 챌린지 기본 정보 조회 Challenge challenge = challengeRepository.findById(challengeId) - .orElseThrow(() -> new CustomException(ErrorCode.ETC_ERROR)); + .orElseThrow(() -> new CustomException(ErrorCode.CHALLENGE_NOT_FOUND)); // 2. 챌린지 기본 정보 수정 및 저장 challenge.setName(requestDto.getName()); @@ -319,8 +333,11 @@ public ChallengeInfoResponseDto putInfo(Long challengeId, ChallengeInfoRequestDt Challenge editedChallenge = challengeRepository.save(challenge); // 3. 챌린지 날짜 정보 조회 - List challengeDays = challengeDayRepository.findByChallengeId(challengeId) - .orElseThrow(() -> new CustomException(ErrorCode.ETC_ERROR)); + List challengeDays = challengeDayRepository.findByChallengeId(challengeId); + + if (challengeDays.isEmpty()) { + throw new CustomException(ErrorCode.CHALLENGE_DAY_NOT_FOUND); + } // 4. 챌린지 날짜 정보 수정 및 저장 // 1) 새로운 날짜가 기존 리스트에 없으면 추가 @@ -345,8 +362,11 @@ public ChallengeInfoResponseDto putInfo(Long challengeId, ChallengeInfoRequestDt } // 5. 변경된 날짜 정보 조회 - List editedChallengeDays = challengeDayRepository.findByChallengeId(challengeId) - .orElseThrow(() -> new CustomException(ErrorCode.ETC_ERROR)); + List editedChallengeDays = challengeDayRepository.findByChallengeId(challengeId); + + if (editedChallengeDays.isEmpty()) { + throw new CustomException(ErrorCode.CHALLENGE_DAY_NOT_FOUND); + } return new ChallengeInfoResponseDto( editedChallenge.getName(), diff --git a/src/main/java/com/writon/admin/domain/service/EmailService.java b/src/main/java/com/writon/admin/domain/service/EmailService.java index 71c6056..a8c4272 100644 --- a/src/main/java/com/writon/admin/domain/service/EmailService.java +++ b/src/main/java/com/writon/admin/domain/service/EmailService.java @@ -47,7 +47,7 @@ public void sendEmail(Challenge challenge, String email) { log.info("Succeeded to send Email"); } catch (Exception e) { log.info("Failed to send Email"); - throw new CustomException(ErrorCode.ETC_ERROR); + throw new CustomException(ErrorCode.EMAIL_SEND_FAILED); } } diff --git a/src/main/java/com/writon/admin/domain/service/OrganizationService.java b/src/main/java/com/writon/admin/domain/service/OrganizationService.java index 996132f..008d005 100644 --- a/src/main/java/com/writon/admin/domain/service/OrganizationService.java +++ b/src/main/java/com/writon/admin/domain/service/OrganizationService.java @@ -22,7 +22,6 @@ public class OrganizationService { private final OrganizationRepository organizationRepository; - private final AdminUserRepository adminUserRepository; private final TokenUtil tokenUtil; private final PositionRepository positionRepository; @@ -34,6 +33,12 @@ public CreateOrganizationResponseDto createOrganization( // 1. 사용자 정보 불러오기 AdminUser adminUser = tokenUtil.getAdminUser(); + // 1. 조직 생성 여부 확인하기 + organizationRepository.findByAdminUserId(adminUser.getId()) + .ifPresent(org -> { + throw new CustomException(ErrorCode.ORGANIZATION_DUPLICATE); + }); + // 2. Organization 객체 생성 Organization organization = new Organization( createOrganizationRequestDto.getName(), @@ -63,10 +68,12 @@ public CreateOrganizationResponseDto createOrganization( public List getPositions() { Organization organization = tokenUtil.getOrganization(); - List responseDto = positionRepository.findByOrganizationId(organization.getId()) - .orElseThrow(() -> new CustomException(ErrorCode.ETC_ERROR)); + List positionList = positionRepository.findByOrganizationId(organization.getId()); + if (positionList.isEmpty()) { + throw new CustomException(ErrorCode.POSITION_NOT_FOUND); + } - return responseDto.stream() + return positionList.stream() .map(Position::getName) .toList(); } @@ -97,8 +104,10 @@ public List editPositions(List positionList) { Organization organization = tokenUtil.getOrganization(); // 1. 현재 조직의 모든 Position을 조회 - List existingPositions = positionRepository.findByOrganizationId(organization.getId()) - .orElseThrow(() -> new CustomException(ErrorCode.ETC_ERROR)); + List existingPositions = positionRepository.findByOrganizationId(organization.getId()); + if (positionList.isEmpty()) { + throw new CustomException(ErrorCode.POSITION_NOT_FOUND); + } // 2. 기존의 position names 리스트를 생성 List existingPositionNames = existingPositions.stream() diff --git a/src/main/java/com/writon/admin/domain/service/ParticipationService.java b/src/main/java/com/writon/admin/domain/service/ParticipationService.java index 84e362e..f6da23e 100644 --- a/src/main/java/com/writon/admin/domain/service/ParticipationService.java +++ b/src/main/java/com/writon/admin/domain/service/ParticipationService.java @@ -39,8 +39,7 @@ public class ParticipationService { // ========== Get Email API ========== public List getEmailList(Long challengeId) { - List emailList = emailRepository.findByChallengeId(challengeId) - .orElseThrow(() -> new CustomException(ErrorCode.ETC_ERROR)); + List emailList = emailRepository.findByChallengeId(challengeId); return emailList.stream().map(Email::getEmail).toList(); } @@ -49,11 +48,10 @@ public List getEmailList(Long challengeId) { public List getParticipationInfo(Long challengeId) { // 1. 챌린지 조회 Challenge challenge = challengeRepository.findById(challengeId) - .orElseThrow(() -> new CustomException(ErrorCode.ETC_ERROR)); + .orElseThrow(() -> new CustomException(ErrorCode.CHALLENGE_NOT_FOUND)); // 2. 유저 챌린지 조회 - List userChallengeList = userChallengeRepository.findByChallengeId(challengeId) - .orElseThrow(() -> new CustomException(ErrorCode.ETC_ERROR)); + List userChallengeList = userChallengeRepository.findByChallengeId(challengeId); // 3. 유저 정보 조회 List participationInfoList = new ArrayList<>(); @@ -62,8 +60,8 @@ public List getParticipationInfo(Long challengeId) { Affiliation affiliation = userChallenge.getAffiliation(); User user = affiliation.getUser(); - List userChallenges = userChallengeRepository.findByAffiliationId(affiliation.getId()) - .orElseThrow(() -> new CustomException(ErrorCode.ETC_ERROR)); + List userChallenges = userChallengeRepository.findByAffiliationId(affiliation.getId()); + String challenges = userChallenges.stream() .map(entity -> entity.getChallenge().getName()) .collect(Collectors.joining(", ")); @@ -104,7 +102,7 @@ public List withdrawal(Long challengeId, List userChall for (Long userChallengeId : userChallengeIdList) { UserChallenge userChallenge = userChallengeRepository.findById(userChallengeId) - .orElseThrow(() -> new CustomException(ErrorCode.ETC_ERROR)); + .orElseThrow(() -> new CustomException(ErrorCode.CHALLENGE_USER_NOT_FOUND)); userChallenge.setWithdrawn(true); userChallengeRepository.save(userChallenge); @@ -116,15 +114,17 @@ public List withdrawal(Long challengeId, List userChall // ========== Post Participate API ========== public List participate(Long challengeId, List emailList) { Challenge challenge = challengeRepository.findById(challengeId) - .orElseThrow(() -> new CustomException(ErrorCode.ETC_ERROR)); + .orElseThrow(() -> new CustomException(ErrorCode.CHALLENGE_NOT_FOUND)); for (String email : emailList) { emailService.sendEmail(challenge, email); emailRepository.save(new Email(email, challenge)); } - List sendedEmailList = emailRepository.findByChallengeId(challengeId) - .orElseThrow(() -> new CustomException(ErrorCode.ETC_ERROR)); + List sendedEmailList = emailRepository.findByChallengeId(challengeId); + if (sendedEmailList.isEmpty()) { + throw new CustomException(ErrorCode.EMAIL_NOT_FOUND); + } return sendedEmailList.stream().map(Email::getEmail).toList(); } diff --git a/src/main/java/com/writon/admin/global/config/auth/JwtAuthenticationEntryPoint.java b/src/main/java/com/writon/admin/global/config/auth/JwtAuthenticationEntryPoint.java index 642ad39..65afba4 100644 --- a/src/main/java/com/writon/admin/global/config/auth/JwtAuthenticationEntryPoint.java +++ b/src/main/java/com/writon/admin/global/config/auth/JwtAuthenticationEntryPoint.java @@ -43,6 +43,7 @@ public void commence( if (exception.equals(ErrorCode.UNAUTHORIZED_TOKEN.getCode())) { errorCode = ErrorCode.UNAUTHORIZED_TOKEN; } + } exceptionResponseHandler.setResponse(response, errorCode); diff --git a/src/main/java/com/writon/admin/global/error/ErrorCode.java b/src/main/java/com/writon/admin/global/error/ErrorCode.java index 51718ad..f405f53 100644 --- a/src/main/java/com/writon/admin/global/error/ErrorCode.java +++ b/src/main/java/com/writon/admin/global/error/ErrorCode.java @@ -27,17 +27,26 @@ public enum ErrorCode { ACCESS_TOKEN_NOT_FOUND(HttpStatus.NOT_FOUND, "A05", "AccessToken이 존재하지 않습니다"), REFRESH_TOKEN_EXPIRATION(HttpStatus.UNAUTHORIZED, "A06", "RefreshToken이 만료되었습니다"), REFRESH_TOKEN_INCONSISTENCY(HttpStatus.NOT_FOUND, "A07", "RefreshToken이 일치하지 않습니다"), - DISABLED_USER(HttpStatus.NOT_FOUND, "A08", "비활성화된 계정입니다"), - LOCKED_USER(HttpStatus.NOT_FOUND, "A09", "계정이 잠겨 있습니다"), + DISABLED_USER(HttpStatus.FORBIDDEN, "A08", "비활성화된 계정입니다"), + LOCKED_USER(HttpStatus.FORBIDDEN, "A09", "계정이 잠겨 있습니다"), + USER_IDENTIFIER_DUPLICATE(HttpStatus.CONFLICT, "A10", "동일한 계정이 이미 존재합니다"), // organization ORGANIZATION_NOT_FOUND(HttpStatus.NOT_FOUND, "O01", "조직 정보를 찾을 수 없습니다"), + ORGANIZATION_DUPLICATE(HttpStatus.CONFLICT, "O02", "중복되는 조직이 존재합니다"), + POSITION_NOT_FOUND(HttpStatus.NOT_FOUND, "O03", "포지션 정보를 찾을 수 없습니다"), // challenge CHALLENGE_NOT_FOUND(HttpStatus.NOT_FOUND, "C01", "챌린지 정보를 찾을 수 없습니다"), - - // participation - EMAIL_DUPLICATION(HttpStatus.BAD_REQUEST, "P01", "중복되는 이메일이 존재합니다"); + CHALLENGE_DAY_NOT_FOUND(HttpStatus.NOT_FOUND, "C02", "챌린지 날짜 정보를 찾을 수 없습니다"), + CHALLENGE_USER_NOT_FOUND(HttpStatus.NOT_FOUND, "C03", "챌린지에 참여한 유저 정보를 찾을 수 없습니다"), + USER_TEMPLATE_NOT_FOUND(HttpStatus.NOT_FOUND, "C04", "해당 유저의 챌린지 참여 정보를 찾을 수 없습니다"), + QUESTION_NOT_FOUND(HttpStatus.NOT_FOUND, "C05", "질문 정보를 찾을 수 없습니다"), + + // email + EMAIL_NOT_FOUND(HttpStatus.NOT_FOUND, "E01", "해당하는 이메일 정보를 찾을 수 없습니다"), + EMAIL_DUPLICATE(HttpStatus.CONFLICT, "E02", "중복되는 이메일이 존재합니다"), + EMAIL_SEND_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "E03", "이메일 전송에 실패하였습니다"); private final HttpStatus httpStatus; private final String code;