diff --git a/src/main/java/com/yeoro/twogether/domain/member/controller/MemberController.java b/src/main/java/com/yeoro/twogether/domain/member/controller/MemberController.java index 1c4b7d8..1bf2456 100644 --- a/src/main/java/com/yeoro/twogether/domain/member/controller/MemberController.java +++ b/src/main/java/com/yeoro/twogether/domain/member/controller/MemberController.java @@ -3,6 +3,7 @@ import com.yeoro.twogether.domain.member.dto.request.*; import com.yeoro.twogether.domain.member.dto.response.LoginResponse; import com.yeoro.twogether.domain.member.dto.response.MemberInfoResponse; +import com.yeoro.twogether.domain.member.dto.response.PasswordResetVerifyResponse; import com.yeoro.twogether.domain.member.entity.Member; import com.yeoro.twogether.domain.member.service.EmailVerificationService; import com.yeoro.twogether.domain.member.service.MemberService; @@ -18,8 +19,8 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import org.springframework.web.server.ResponseStatusException; import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.server.ResponseStatusException; import java.net.URL; @@ -183,4 +184,27 @@ public ResponseEntity deleteMe(@Login Long memberId) { public LoginResponse refreshToken(HttpServletRequest request, HttpServletResponse response) { return memberService.refreshTokens(request, response); } + + // 비밀번호 재설정 코드 발송 + @PostMapping("/password/forgot") + public ResponseEntity forgotPassword(@RequestBody @Valid PasswordResetCodeRequest req) { + memberService.issuePasswordResetCode(req.email()); + return ResponseEntity.ok("비밀번호 재설정 코드가 이메일로 전송되었습니다."); + } + + // 코드 검증 → Reset Ticket 발급 + @PostMapping("/password/verify") + public ResponseEntity verifyCode(@RequestBody @Valid PasswordResetVerifyRequest req) { + PasswordResetVerifyResponse resp = memberService.verifyPasswordResetCode(req.email(), req.code()); + return ResponseEntity.ok(resp); + } + + // Reset Ticket + 새 비밀번호로 최종 변경 + @PostMapping("/password/reset") + public ResponseEntity resetPassword(@RequestBody @Valid PasswordResetFinalizeRequest req, + HttpServletRequest httpRequest, + HttpServletResponse httpResponse) { + memberService.resetPasswordWithTicket(req.email(), req.resetTicket(), req.newPassword(), httpRequest, httpResponse); + return ResponseEntity.ok("성공적으로 비밀번호를 변경했습니다."); + } } \ No newline at end of file diff --git a/src/main/java/com/yeoro/twogether/domain/member/dto/request/PasswordResetCodeRequest.java b/src/main/java/com/yeoro/twogether/domain/member/dto/request/PasswordResetCodeRequest.java new file mode 100644 index 0000000..6e0b76c --- /dev/null +++ b/src/main/java/com/yeoro/twogether/domain/member/dto/request/PasswordResetCodeRequest.java @@ -0,0 +1,8 @@ +package com.yeoro.twogether.domain.member.dto.request; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; + +public record PasswordResetCodeRequest( + @NotBlank @Email String email +) {} \ No newline at end of file diff --git a/src/main/java/com/yeoro/twogether/domain/member/dto/request/PasswordResetFinalizeRequest.java b/src/main/java/com/yeoro/twogether/domain/member/dto/request/PasswordResetFinalizeRequest.java new file mode 100644 index 0000000..7c891ab --- /dev/null +++ b/src/main/java/com/yeoro/twogether/domain/member/dto/request/PasswordResetFinalizeRequest.java @@ -0,0 +1,11 @@ +package com.yeoro.twogether.domain.member.dto.request; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public record PasswordResetFinalizeRequest( + @NotBlank @Email String email, + @NotBlank String resetTicket, + @NotBlank @Size(min = 8, max = 64) String newPassword +) {} \ No newline at end of file diff --git a/src/main/java/com/yeoro/twogether/domain/member/dto/request/PasswordResetVerifyRequest.java b/src/main/java/com/yeoro/twogether/domain/member/dto/request/PasswordResetVerifyRequest.java new file mode 100644 index 0000000..8f8cb88 --- /dev/null +++ b/src/main/java/com/yeoro/twogether/domain/member/dto/request/PasswordResetVerifyRequest.java @@ -0,0 +1,9 @@ +package com.yeoro.twogether.domain.member.dto.request; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; + +public record PasswordResetVerifyRequest( + @NotBlank @Email String email, + @NotBlank String code +) {} diff --git a/src/main/java/com/yeoro/twogether/domain/member/dto/response/PasswordResetVerifyResponse.java b/src/main/java/com/yeoro/twogether/domain/member/dto/response/PasswordResetVerifyResponse.java new file mode 100644 index 0000000..e371cd7 --- /dev/null +++ b/src/main/java/com/yeoro/twogether/domain/member/dto/response/PasswordResetVerifyResponse.java @@ -0,0 +1,5 @@ +package com.yeoro.twogether.domain.member.dto.response; + +public record PasswordResetVerifyResponse( + String resetTicket +) {} \ No newline at end of file diff --git a/src/main/java/com/yeoro/twogether/domain/member/mail/MailService.java b/src/main/java/com/yeoro/twogether/domain/member/mail/MailService.java index ce8970d..480cce1 100644 --- a/src/main/java/com/yeoro/twogether/domain/member/mail/MailService.java +++ b/src/main/java/com/yeoro/twogether/domain/member/mail/MailService.java @@ -17,34 +17,43 @@ public class MailService { @Value("${spring.mail.username}") private String senderEmail; + // 이메일 인증 코드 발송 public void sendSimpleMessage(String to, String code) throws MessagingException { - MimeMessage message = mailSender.createMimeMessage(); - MimeMessageHelper helper = new MimeMessageHelper(message, false, "UTF-8"); - - helper.setTo(to); - helper.setFrom(senderEmail); - helper.setSubject("인증을 위한 이메일 인증번호"); - String body = "" + "" + "
" + "

인증을 위한 이메일 인증번호

" + - "

안녕하세요, 회원님.

" + - "

요청하신 인증 번호는 아래와 같습니다:

" + + "

요청하신 인증 번호는 아래와 같습니다:

" + "
" + "

" + code + "

" + - "

이 코드를 입력하여 이메일 인증을 완료하세요.

" + "
" + "

감사합니다!

" + - "
" + - "

© 2025 Your Company

" + - "
" + - "
" + - "" + - ""; + "

© 2025 Your Company

" + + ""; + sendHtml(to, "인증을 위한 이메일 인증번호", body); + } - helper.setText(body, true); // HTML true 설정 + // 비밀번호 재설정 코드 발송 + public void sendPasswordResetCode(String to, String code) throws MessagingException { + String html = """ + +

비밀번호 재설정 코드

+

아래 코드를 입력하면 새 비밀번호를 설정하실 수 있습니다. (유효시간 10분)

+
+ %s +
+ + """.formatted(code); + sendHtml(to, "비밀번호 재설정 코드", html); + } + private void sendHtml(String to, String subject, String body) throws MessagingException { + MimeMessage message = mailSender.createMimeMessage(); + MimeMessageHelper helper = new MimeMessageHelper(message, false, "UTF-8"); + helper.setTo(to); + helper.setFrom(senderEmail); + helper.setSubject(subject); + helper.setText(body, true); mailSender.send(message); } } diff --git a/src/main/java/com/yeoro/twogether/domain/member/service/Impl/MemberServiceImpl.java b/src/main/java/com/yeoro/twogether/domain/member/service/Impl/MemberServiceImpl.java index bbfa90b..2d10da1 100644 --- a/src/main/java/com/yeoro/twogether/domain/member/service/Impl/MemberServiceImpl.java +++ b/src/main/java/com/yeoro/twogether/domain/member/service/Impl/MemberServiceImpl.java @@ -5,9 +5,11 @@ import com.yeoro.twogether.domain.member.dto.request.LoginRequest; import com.yeoro.twogether.domain.member.dto.request.SignupRequest; import com.yeoro.twogether.domain.member.dto.response.LoginResponse; +import com.yeoro.twogether.domain.member.dto.response.PasswordResetVerifyResponse; import com.yeoro.twogether.domain.member.entity.Gender; import com.yeoro.twogether.domain.member.entity.LoginPlatform; import com.yeoro.twogether.domain.member.entity.Member; +import com.yeoro.twogether.domain.member.mail.MailService; import com.yeoro.twogether.domain.member.repository.MemberRepository; import com.yeoro.twogether.domain.member.service.EmailVerificationService; import com.yeoro.twogether.domain.member.service.MemberService; @@ -20,6 +22,7 @@ import com.yeoro.twogether.global.service.s3.HighlightS3Service; import com.yeoro.twogether.global.service.s3.ProfileS3Service; import com.yeoro.twogether.global.store.PartnerCodeStore; +import com.yeoro.twogether.global.store.PasswordResetStore; import com.yeoro.twogether.global.token.JwtService; import com.yeoro.twogether.global.token.TokenPair; import com.yeoro.twogether.global.token.TokenService; @@ -43,6 +46,7 @@ import java.time.LocalDate; import java.time.format.DateTimeParseException; import java.util.List; +import java.util.Locale; import java.util.UUID; import static com.yeoro.twogether.global.exception.ErrorCode.MEMBER_NOT_FOUND; @@ -67,8 +71,8 @@ public class MemberServiceImpl implements MemberService { private final WaypointItemRepository waypointItemRepository; private final MemberHardDeleteTx memberHardDeleteTx; private final ProfileS3Service profileS3Service; - - + private final PasswordResetStore passwordResetStore; + private final MailService mailService; /** @@ -703,5 +707,108 @@ private static String extractKey(String imageUrlOrKey) { return imageUrlOrKey; } } + + // 비밀번호 재설정 코드 발송 + // MemberServiceImpl.issuePasswordResetCode + @Override + @Transactional + public void issuePasswordResetCode(String email) { + var normEmail = email == null ? null : email.trim().toLowerCase(Locale.ROOT); + var opt = memberRepository.findByEmail(normEmail); + + // 존재하지 않으면 실제 메일 발송/저장은 생략 (응답은 항상 동일 문구로) + if (opt.isEmpty()) { + // 시도 카운트도 증가시키지 않고 조용히 반환 + return; + } + + // LOCAL 계정만 허용 + Member m = opt.get(); + if (m.getLoginPlatform() != LoginPlatform.LOCAL) { + // 소셜 계정은 미지원: 조용히 반환 + return; + } + + // 발송 시도 제한: 과도한 요청 방지 + if (passwordResetStore.tooManyAttempts(normEmail)) { + throw new ServiceException(ErrorCode.TOO_MANY_REQUESTS); + } + + String code = CodeGenerator.generateNumericCode(6); + passwordResetStore.saveCode(normEmail, code); + + try { + mailService.sendPasswordResetCode(normEmail, code); + } catch (Exception e) { + log.error("[pwdreset] 메일 전송 실패: {}", normEmail, e); + throw new ServiceException(ErrorCode.MAIL_SEND_FAILED); + } + } + + + @Override + @Transactional + public PasswordResetVerifyResponse verifyPasswordResetCode(String email, String code) { + if (passwordResetStore.tooManyAttempts(email)) { + throw new ServiceException(ErrorCode.TOO_MANY_REQUESTS); + } + + String saved = passwordResetStore.getCode(email); + if (saved == null) { + throw new ServiceException(ErrorCode.PASSWORD_RESET_CODE_EXPIRED); + } + if (!saved.equalsIgnoreCase(code)) { + throw new ServiceException(ErrorCode.PASSWORD_RESET_CODE_INVALID); + } + + passwordResetStore.deleteCode(email); + passwordResetStore.clearAttempts(email); + + String ticket = passwordResetStore.issueTicket(email); + return new PasswordResetVerifyResponse(ticket); + } + + @Override + @Transactional + public void resetPasswordWithTicket(String email, + String resetTicket, + String newPassword, + HttpServletRequest request, + HttpServletResponse response) { + boolean ticketOk = passwordResetStore.consumeTicket(email, resetTicket); + if (!ticketOk) throw new ServiceException(ErrorCode.PASSWORD_RESET_TICKET_INVALID); + + Member member = memberRepository.findByEmail(email) + .orElseThrow(() -> new ServiceException(ErrorCode.MEMBER_NOT_FOUND)); + + if (member.getLoginPlatform() != LoginPlatform.LOCAL) { + throw new ServiceException(ErrorCode.NOT_LOCAL_MEMBER); + } + if (!PasswordValidator.isValid(newPassword)) { + throw new ServiceException(ErrorCode.PASSWORD_NOT_VALID); + } + if (passwordEncoder.matches(newPassword, member.getPassword())) { + throw new ServiceException(ErrorCode.PASSWORD_RESET_SAME_AS_OLD); + } + + // 비밀번호 업데이트 + member.setPassword(passwordEncoder.encode(newPassword)); + + // 기존 토큰 무효화(해당 회원만) + jwtService.invalidateRefreshToken(member.getId()); + tokenService.removeRefreshTokenFromRedis(member.getId()); + jwtService.clearRefreshTokenCookie(response); + + // 추가: 현재 요청에 Access Token이 있다면 블랙리스트 처리(선택적) + String currentAccessToken = jwtService.resolveToken(request); + if (currentAccessToken != null && !currentAccessToken.isBlank()) { + tokenService.blacklistAccessToken(currentAccessToken); + } + + // 클린업 + passwordResetStore.deleteCode(email); + passwordResetStore.clearAttempts(email); + } + } diff --git a/src/main/java/com/yeoro/twogether/domain/member/service/MemberService.java b/src/main/java/com/yeoro/twogether/domain/member/service/MemberService.java index f6d0f34..a63cb8a 100644 --- a/src/main/java/com/yeoro/twogether/domain/member/service/MemberService.java +++ b/src/main/java/com/yeoro/twogether/domain/member/service/MemberService.java @@ -4,6 +4,7 @@ import com.yeoro.twogether.domain.member.dto.request.LoginRequest; import com.yeoro.twogether.domain.member.dto.request.SignupRequest; import com.yeoro.twogether.domain.member.dto.response.LoginResponse; +import com.yeoro.twogether.domain.member.dto.response.PasswordResetVerifyResponse; import com.yeoro.twogether.domain.member.entity.Gender; import com.yeoro.twogether.domain.member.entity.LoginPlatform; import com.yeoro.twogether.domain.member.entity.Member; @@ -11,126 +12,58 @@ import jakarta.servlet.http.HttpServletResponse; import org.springframework.web.multipart.MultipartFile; +import java.net.URL; + /** * 사용자 서비스 */ public interface MemberService { - - /** - * 일반 회원가입 - */ + // 가입/로그인 LoginResponse signup(SignupRequest request, HttpServletResponse response); - - /** - * 일반 로그인 - */ LoginResponse login(LoginRequest request, HttpServletRequest httpRequest, HttpServletResponse httpResponse); + Long findOrCreateMember(OauthProfile profile, LoginPlatform loginPlatform, String encodedPassword); - /** - * 로컬(자체) 가입용: 이메일 존재 여부 확인 소셜(OAuth) 가입 시 사용하지 않음 - */ + // 조회/검증 boolean isExistEmail(String email); - - /** - * 로컬 가입용: 이메일 기반 회원 ID 조회 존재하지 않으면 예외 발생 - */ Long getMemberId(String email); - - /** - * 소셜(OAuth) 회원가입 처리 이메일은 선택적 정보 (nullable) - */ - Long signupByOauth(OauthProfile profile, LoginPlatform loginPlatform, String encodedPassword); - - /** - * 플랫폼 ID 존재 여부 확인 - */ boolean isExistPlatformId(String platformId); - - /** - * 플랫폼 ID 기반 회원 ID 조회 존재하지 않으면 예외 발생 - */ Long getMemberIdByPlatformId(String platformId); - - /** - * 회원 ID 기반 이름 조회 존재하지 않으면 예외 발생 - */ String getNameByMemberId(Long memberId); - - /** - * 회원 ID 기반 파트너 ID 조회 파트너 없으면 null 반환 - */ Long getPartnerId(Long memberId); - - /** - * 파트너 연결 코드 생성 - */ - String generatePartnerCode(Long memberId); - - /** - * 파트너 연결 처리 - */ - LoginResponse connectPartner(Long requesterId, String inputCode, - HttpServletRequest request, - HttpServletResponse response); - - /** - * 플랫폼 ID 기준으로 회원 존재 시 ID 반환, 없으면 신규 가입 후 ID 반환 - */ - Long findOrCreateMember(OauthProfile profile, LoginPlatform loginPlatform, String encodedPassword); - - /** - * 카카오 로그인 처리 OAuth 프로필 조회 → 회원 조회/가입 → JWT 발급 및 클라이언트 전달 → 응답 DTO 반환 - */ - LoginResponse kakaoLogin(String accessToken, HttpServletRequest request, - HttpServletResponse response); - - /** - * 로그아웃 - */ - void logout(Long memberId, String accessToken); - - /** - * 현재 로그인한 사용자 정보 조회 @Login에서 사용자 식별 후 Member 엔티티 반환 - */ Member getCurrentMember(Long memberId); - /** - * 프로필 이미지 수정 - */ - void updateProfileImage(Long memberId, MultipartFile image); - - java.net.URL getProfileImagePresignedUrl(Long memberId); - - /** - * 이름 수정 - */ - void updateName(Long memberId, String newName); + // OAuth + Long signupByOauth(OauthProfile profile, LoginPlatform loginPlatform, String encodedPassword); + LoginResponse kakaoLogin(String accessToken, HttpServletRequest request, HttpServletResponse response); - /** - * 파트너 별명 설정 - */ + // 파트너 + String generatePartnerCode(Long memberId); + LoginResponse connectPartner(Long requesterId, String inputCode, HttpServletRequest request, HttpServletResponse response); void setPartnerNickname(Long requesterId, String nickname); - - /** - * 파트너 연결 끊기 - */ void disconnectPartner(Long memberId); - /** - * 비밀번호 변경 (LOCAL 사용자 전용) - */ - void updatePassword(Long memberId, String currentPassword, String newPassword); - + // 프로필/정보 수정 + void updateProfileImage(Long memberId, MultipartFile image); + URL getProfileImagePresignedUrl(Long memberId); + void updateName(Long memberId, String newName); void updateGender(Long memberId, Gender gender); void updateAgeRange(Long memberId, String ageRange); - /** - * JWT 재발급 - */ + // 비밀번호(로그인 상태) + void updatePassword(Long memberId, String currentPassword, String newPassword); + + // 토큰/세션 + void logout(Long memberId, String accessToken); LoginResponse refreshTokens(HttpServletRequest request, HttpServletResponse response); + // 기타 LoginResponse updateRelationshipStartDate(Long memberId, String date, HttpServletRequest request, HttpServletResponse response); - void deleteMember(Long memberId); + + // ===== 비밀번호 찾기(비로그인) 플로우 ===== + void issuePasswordResetCode(String email); + PasswordResetVerifyResponse verifyPasswordResetCode(String email, String code); + void resetPasswordWithTicket(String email, String resetTicket, String newPassword, + HttpServletRequest request, HttpServletResponse response); } diff --git a/src/main/java/com/yeoro/twogether/global/exception/ErrorCode.java b/src/main/java/com/yeoro/twogether/global/exception/ErrorCode.java index bf9e582..0689114 100644 --- a/src/main/java/com/yeoro/twogether/global/exception/ErrorCode.java +++ b/src/main/java/com/yeoro/twogether/global/exception/ErrorCode.java @@ -21,6 +21,16 @@ public enum ErrorCode { PASSWORD_SAME_AS_OLD("400-5", "member.password.same.as.old", HttpStatus.BAD_REQUEST), PASSWORD_NOT_SET("400-6", "member.password.not.set", HttpStatus.BAD_REQUEST), + // Password Reset + PASSWORD_RESET_CODE_EXPIRED("400-30", "password.reset.code.expired", HttpStatus.BAD_REQUEST), + PASSWORD_RESET_CODE_INVALID("400-31", "password.reset.code.invalid", HttpStatus.BAD_REQUEST), + PASSWORD_RESET_TICKET_INVALID("400-32", "password.reset.ticket.invalid", HttpStatus.BAD_REQUEST), + PASSWORD_RESET_SAME_AS_OLD("400-33", "password.reset.same.as.old", HttpStatus.BAD_REQUEST), + TOO_MANY_REQUESTS("429-1", "too.many.requests", HttpStatus.TOO_MANY_REQUESTS), + MAIL_SEND_FAILED("500-30", "mail.send.failed", HttpStatus.INTERNAL_SERVER_ERROR), + + + // Partner Errors PARTNER_CODE_INVALID("400-1", "partner.code.invalid", HttpStatus.BAD_REQUEST), SELF_PARTNER_NOT_ALLOWED("400-2", "partner.self.not.allowed", HttpStatus.BAD_REQUEST), diff --git a/src/main/java/com/yeoro/twogether/global/store/PasswordResetStore.java b/src/main/java/com/yeoro/twogether/global/store/PasswordResetStore.java new file mode 100644 index 0000000..a392aef --- /dev/null +++ b/src/main/java/com/yeoro/twogether/global/store/PasswordResetStore.java @@ -0,0 +1,70 @@ +package com.yeoro.twogether.global.store; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.util.Locale; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +public class PasswordResetStore { + private final StringRedisTemplate redis; + + private static final String CODE_KEY = "pwdreset:code:"; // email -> code + private static final String ATTEMPT_KEY = "pwdreset:attempts:"; // email -> int + private static final String TICKET_KEY = "pwdreset:ticket:"; // email -> reset ticket + + private static final Duration CODE_TTL = Duration.ofMinutes(10); + private static final Duration ATTEMPT_TTL = Duration.ofMinutes(15); + private static final Duration TICKET_TTL = Duration.ofMinutes(10); + private static final int MAX_ATTEMPTS = 5; + + private static String norm(String email) { + return email == null ? null : email.trim().toLowerCase(Locale.ROOT); + } + + // ----- CODE ----- + public void saveCode(String email, String code) { + redis.opsForValue().set(CODE_KEY + norm(email), code, CODE_TTL); + } + public String getCode(String email) { + return redis.opsForValue().get(CODE_KEY + norm(email)); + } + public void deleteCode(String email) { + redis.delete(CODE_KEY + norm(email)); + } + + // ----- ATTEMPTS ----- + /** 증가 후 과다 여부 반환(true면 차단) */ + public boolean tooManyAttempts(String email) { + String key = ATTEMPT_KEY + norm(email); + Long cnt = redis.opsForValue().increment(key); + if (cnt != null && cnt == 1L) { + redis.expire(key, ATTEMPT_TTL); + } + return cnt != null && cnt > MAX_ATTEMPTS; + } + public void clearAttempts(String email) { + redis.delete(ATTEMPT_KEY + norm(email)); + } + + // ----- RESET TICKET ----- + /** 코드 검증 성공 시 발급할 일회용 티켓 생성 & 저장(10분 TTL) */ + public String issueTicket(String email) { + String ticket = UUID.randomUUID().toString(); + redis.opsForValue().set(TICKET_KEY + norm(email), ticket, TICKET_TTL); + return ticket; + } + + /** 일회성 검증/소비(성공 시 true, 저장된 티켓 삭제) */ + public boolean consumeTicket(String email, String ticket) { + String key = TICKET_KEY + norm(email); + String saved = redis.opsForValue().get(key); + if (saved == null || !saved.equals(ticket)) return false; + redis.delete(key); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/com/yeoro/twogether/global/util/CodeGenerator.java b/src/main/java/com/yeoro/twogether/global/util/CodeGenerator.java index 5d4fdb6..b2f015d 100644 --- a/src/main/java/com/yeoro/twogether/global/util/CodeGenerator.java +++ b/src/main/java/com/yeoro/twogether/global/util/CodeGenerator.java @@ -32,5 +32,12 @@ public static String generateEmailCode() { } return sb.toString(); } + + // 비밀번호 재설정용 6자리 숫자 코드 + public static String generateNumericCode(int digits) { + int bound = (int) Math.pow(10, digits); + int n = RANDOM.nextInt(bound); + return String.format("%0" + digits + "d", n); + } } diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index d754523..9f6a3c5 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -8,6 +8,12 @@ member.password.not.valid=\uBE44\uBC00\uBC88\uD638\uB294 \uC601\uBB38, \uC22B\uC member.password.same.as.old=\uC774\uC804 \uBE44\uBC00\uBC88\uD638\uC640 \uB3D9\uC77C\uD569\uB2C8\uB2E4. \uB2E4\uB978 \uBE44\uBC00\uBC88\uD638\uB97C \uC785\uB825\uD574 \uC8FC\uC138\uC694. member.password.not.set=\uBE44\uBC00\uBC88\uD638\uAC00 \uC124\uC815\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uBE44\uBC00\uBC88\uD638\uB97C \uBA3C\uC800 \uC124\uC815\uD574 \uC8FC\uC138\uC694. +# Password Reset +password.reset.code.expired=\uC7AC\uC124\uC815 \uCF54\uB4DC\uAC00 \uB9CC\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC694\uCCAD\uD574 \uC8FC\uC138\uC694. +password.reset.code.invalid=\uC7AC\uC124\uC815 \uCF54\uB4DC\uAC00 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC785\uB825\uAC12\uC744 \uD655\uC778\uD574 \uC8FC\uC138\uC694. +password.reset.ticket.invalid=\uC7AC\uC124\uC815 \uC138\uC158\uC774 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uCF54\uB4DC\uB97C \uB2E4\uC2DC \uC778\uC99D\uD574 \uC8FC\uC138\uC694. +password.reset.same.as.old=\uAE30\uC874 \uBE44\uBC00\uBC88\uD638\uC640 \uB3D9\uC77C\uD55C \uBE44\uBC00\uBC88\uD638\uB294 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uB2E4\uB978 \uBE44\uBC00\uBC88\uD638\uB97C \uC785\uB825\uD574 \uC8FC\uC138\uC694. + # Partner partner.code.invalid=\uD30C\uD2B8\uB108 \uCF54\uB4DC\uAC00 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. partner.code.generation.failed=\uD30C\uD2B8\uB108 \uCF54\uB4DC\uB97C \uC0DD\uC131\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694. @@ -15,6 +21,12 @@ partner.self.not.allowed=\uC790\uAE30 \uC790\uC2E0\uACFC\uB294 \uD30C\uD2B8\uB10 relationship.date.format.invalid=\uB0A0\uC9DC \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. 'YYYY-MM-DD' \uD615\uC2DD\uC73C\uB85C \uC785\uB825\uD574 \uC8FC\uC138\uC694. relationship.date.rule.violation=\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uC5F0\uC560 \uC2DC\uC791\uC77C\uC785\uB2C8\uB2E4. \uBBF8\uB798 \uB0A0\uC9DC\uB294 \uC785\uB825\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. +# Common +common.too.many.requests=\uC694\uCCAD\uC774 \uB108\uBB34 \uB9CE\uC2B5\uB2C8\uB2E4. \uC7A0\uC2DC \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694. + +# Mail +mail.send.failed=\uBA54\uC77C \uBC1C\uC1A1 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. \uC7A0\uC2DC \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694. + # Token / Auth token.expired=\uD1A0\uD070\uC774 \uB9CC\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uB85C\uADF8\uC778\uD574 \uC8FC\uC138\uC694. token.invalid=\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uD1A0\uD070\uC785\uB2C8\uB2E4. \uB2E4\uC2DC \uB85C\uADF8\uC778\uD574 \uC8FC\uC138\uC694.