Skip to content

Commit 3156492

Browse files
authored
Feat: 아이디 찾기 및 비밀번호 재설정 API 구 (#124) (#130)
* Feat: 아이디 찾기 API 구현 * Test: 테스트 작성 * Docs: Swagger 문서 작성 * Feat: 비밀번호 재설정 요청 API 구현 * Test: 테스트 작성 * Docs: Swagger 문서 작성 * Comment: 주석 추가 * Feat: 비밀번호 재설정 API 구현 * Test: 테스트 작성 * Docs: Swagger 문서 작성
1 parent e216eb2 commit 3156492

File tree

11 files changed

+831
-5
lines changed

11 files changed

+831
-5
lines changed

src/main/java/com/back/domain/user/controller/AuthController.java

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import lombok.RequiredArgsConstructor;
1010
import org.springframework.http.HttpStatus;
1111
import org.springframework.http.ResponseEntity;
12-
import org.springframework.security.access.prepost.PreAuthorize;
1312
import org.springframework.web.bind.annotation.*;
1413

1514
import java.util.Map;
@@ -50,7 +49,7 @@ public ResponseEntity<RsData<UserResponse>> verifyEmail(
5049
// 인증 메일 재발송
5150
@PostMapping("/email/verify")
5251
public ResponseEntity<RsData<Void>> resendVerificationEmail(
53-
@Valid @RequestBody ResendVerificationRequest request
52+
@Valid @RequestBody SendEmailRequest request
5453
) {
5554
authService.resendVerificationEmail(request.email());
5655
return ResponseEntity
@@ -100,4 +99,43 @@ public ResponseEntity<RsData<Map<String, String>>> refreshToken(
10099
Map.of("accessToken", newAccessToken)
101100
));
102101
}
102+
103+
// 아이디 찾기
104+
@PostMapping("/username/recover")
105+
public ResponseEntity<RsData<Void>> recoverUsername(
106+
@Valid @RequestBody SendEmailRequest request
107+
) {
108+
authService.recoverUsername(request.email());
109+
return ResponseEntity
110+
.ok(RsData.success(
111+
"아이디를 이메일로 전송했습니다.",
112+
null
113+
));
114+
}
115+
116+
// 비밀번호 재설정 요청
117+
@PostMapping("/password/recover")
118+
public ResponseEntity<RsData<Void>> recoverPassword(
119+
@Valid @RequestBody SendEmailRequest request
120+
) {
121+
authService.recoverPassword(request.email());
122+
return ResponseEntity
123+
.ok(RsData.success(
124+
"비밀번호 재설정 링크를 이메일로 전송했습니다.",
125+
null
126+
));
127+
}
128+
129+
// 비밀번호 재설정
130+
@PostMapping("/password/reset")
131+
public ResponseEntity<RsData<Void>> resetPassword(
132+
@Valid @RequestBody PasswordResetRequest request
133+
) {
134+
authService.resetPassword(request.token(), request.newPassword());
135+
return ResponseEntity
136+
.ok(RsData.success(
137+
"비밀번호가 성공적으로 재설정되었습니다.",
138+
null
139+
));
140+
}
103141
}

src/main/java/com/back/domain/user/controller/AuthControllerDocs.java

Lines changed: 230 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import jakarta.validation.Valid;
1414
import org.springframework.http.ResponseEntity;
1515
import org.springframework.web.bind.annotation.GetMapping;
16+
import org.springframework.web.bind.annotation.PostMapping;
1617
import org.springframework.web.bind.annotation.RequestBody;
1718
import org.springframework.web.bind.annotation.RequestParam;
1819

@@ -305,7 +306,7 @@ ResponseEntity<RsData<UserResponse>> verifyEmail(
305306
)
306307
})
307308
ResponseEntity<RsData<Void>> resendVerificationEmail(
308-
@Valid @RequestBody ResendVerificationRequest request
309+
@Valid @RequestBody SendEmailRequest request
309310
);
310311

311312
@Operation(
@@ -690,4 +691,232 @@ ResponseEntity<RsData<Map<String, String>>> refreshToken(
690691
HttpServletRequest request,
691692
HttpServletResponse response
692693
);
694+
695+
@Operation(
696+
summary = "아이디 찾기",
697+
description = "사용자가 이메일을 입력하면, 해당 이메일로 가입된 계정의 아이디(username)를 일부 마스킹 처리하여 이메일로 전송합니다."
698+
)
699+
@ApiResponses({
700+
@ApiResponse(
701+
responseCode = "200",
702+
description = "아이디 찾기 성공",
703+
content = @Content(
704+
mediaType = "application/json",
705+
examples = @ExampleObject(value = """
706+
{
707+
"success": true,
708+
"code": "SUCCESS_200",
709+
"message": "아이디를 이메일로 전송했습니다.",
710+
"data": null
711+
}
712+
""")
713+
)
714+
),
715+
@ApiResponse(
716+
responseCode = "404",
717+
description = "존재하지 않는 사용자",
718+
content = @Content(
719+
mediaType = "application/json",
720+
examples = @ExampleObject(value = """
721+
{
722+
"success": false,
723+
"code": "USER_001",
724+
"message": "존재하지 않는 사용자입니다.",
725+
"data": null
726+
}
727+
""")
728+
)
729+
),
730+
@ApiResponse(
731+
responseCode = "400",
732+
description = "잘못된 요청 (이메일 누락 등)",
733+
content = @Content(
734+
mediaType = "application/json",
735+
examples = @ExampleObject(value = """
736+
{
737+
"success": false,
738+
"code": "COMMON_400",
739+
"message": "잘못된 요청입니다.",
740+
"data": null
741+
}
742+
""")
743+
)
744+
),
745+
@ApiResponse(
746+
responseCode = "500",
747+
description = "서버 내부 오류",
748+
content = @Content(
749+
mediaType = "application/json",
750+
examples = @ExampleObject(value = """
751+
{
752+
"success": false,
753+
"code": "COMMON_500",
754+
"message": "서버 오류가 발생했습니다.",
755+
"data": null
756+
}
757+
""")
758+
)
759+
)
760+
})
761+
@PostMapping("/username/recover")
762+
ResponseEntity<RsData<Void>> recoverUsername(
763+
@Valid @RequestBody SendEmailRequest request
764+
);
765+
766+
@Operation(
767+
summary = "비밀번호 재설정 요청",
768+
description = "사용자가 가입한 이메일을 입력하면, 해당 이메일로 비밀번호 재설정 링크가 발송됩니다."
769+
)
770+
@ApiResponses({
771+
@ApiResponse(
772+
responseCode = "200",
773+
description = "비밀번호 재설정 메일 발송 성공",
774+
content = @Content(
775+
mediaType = "application/json",
776+
examples = @ExampleObject(value = """
777+
{
778+
"success": true,
779+
"code": "SUCCESS_200",
780+
"message": "비밀번호 재설정 메일을 전송했습니다.",
781+
"data": null
782+
}
783+
""")
784+
)
785+
),
786+
@ApiResponse(
787+
responseCode = "404",
788+
description = "존재하지 않는 사용자",
789+
content = @Content(
790+
mediaType = "application/json",
791+
examples = @ExampleObject(value = """
792+
{
793+
"success": false,
794+
"code": "USER_001",
795+
"message": "존재하지 않는 사용자입니다.",
796+
"data": null
797+
}
798+
""")
799+
)
800+
),
801+
@ApiResponse(
802+
responseCode = "400",
803+
description = "잘못된 요청 (이메일 누락 등)",
804+
content = @Content(
805+
mediaType = "application/json",
806+
examples = @ExampleObject(value = """
807+
{
808+
"success": false,
809+
"code": "COMMON_400",
810+
"message": "잘못된 요청입니다.",
811+
"data": null
812+
}
813+
""")
814+
)
815+
),
816+
@ApiResponse(
817+
responseCode = "500",
818+
description = "서버 내부 오류",
819+
content = @Content(
820+
mediaType = "application/json",
821+
examples = @ExampleObject(value = """
822+
{
823+
"success": false,
824+
"code": "COMMON_500",
825+
"message": "서버 오류가 발생했습니다.",
826+
"data": null
827+
}
828+
""")
829+
)
830+
)
831+
})
832+
@PostMapping("/password/recover")
833+
ResponseEntity<RsData<Void>> recoverPassword(
834+
@Valid @RequestBody SendEmailRequest request
835+
);
836+
837+
@Operation(
838+
summary = "비밀번호 재설정",
839+
description = "비밀번호 재설정 토큰과 새로운 비밀번호를 입력받아 계정 비밀번호를 변경합니다. 토큰은 1시간 동안만 유효합니다."
840+
)
841+
@ApiResponses({
842+
@ApiResponse(
843+
responseCode = "200",
844+
description = "비밀번호 재설정 성공",
845+
content = @Content(
846+
mediaType = "application/json",
847+
examples = @ExampleObject(value = """
848+
{
849+
"success": true,
850+
"code": "SUCCESS_200",
851+
"message": "비밀번호가 성공적으로 재설정되었습니다.",
852+
"data": null
853+
}
854+
""")
855+
)
856+
),
857+
@ApiResponse(
858+
responseCode = "400",
859+
description = "잘못된 요청 (필드 누락, 비밀번호 정책 위반)",
860+
content = @Content(
861+
mediaType = "application/json",
862+
examples = @ExampleObject(value = """
863+
{
864+
"success": false,
865+
"code": "COMMON_400",
866+
"message": "잘못된 요청입니다.",
867+
"data": null
868+
}
869+
""")
870+
)
871+
),
872+
@ApiResponse(
873+
responseCode = "401",
874+
description = "유효하지 않은 비밀번호 재설정 토큰",
875+
content = @Content(
876+
mediaType = "application/json",
877+
examples = @ExampleObject(value = """
878+
{
879+
"success": false,
880+
"code": "TOKEN_003",
881+
"message": "유효하지 않은 비밀번호 재설정 토큰입니다.",
882+
"data": null
883+
}
884+
""")
885+
)
886+
),
887+
@ApiResponse(
888+
responseCode = "404",
889+
description = "존재하지 않는 사용자",
890+
content = @Content(
891+
mediaType = "application/json",
892+
examples = @ExampleObject(value = """
893+
{
894+
"success": false,
895+
"code": "USER_001",
896+
"message": "존재하지 않는 사용자입니다.",
897+
"data": null
898+
}
899+
""")
900+
)
901+
),
902+
@ApiResponse(
903+
responseCode = "500",
904+
description = "서버 내부 오류",
905+
content = @Content(
906+
mediaType = "application/json",
907+
examples = @ExampleObject(value = """
908+
{
909+
"success": false,
910+
"code": "COMMON_500",
911+
"message": "서버 오류가 발생했습니다.",
912+
"data": null
913+
}
914+
""")
915+
)
916+
)
917+
})
918+
@PostMapping("/password/reset")
919+
ResponseEntity<RsData<Void>> resetPassword(
920+
@Valid @RequestBody PasswordResetRequest request
921+
);
693922
}

src/main/java/com/back/domain/user/dto/LoginResponse.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package com.back.domain.user.dto;
22

3+
/**
4+
* 사용자 로그인 응답을 나타내는 DTO
5+
*
6+
* @param accessToken 사용자 인증에 사용되는 JWT 토큰
7+
* @param user 로그인한 사용자 정보
8+
*/
39
public record LoginResponse(
410
String accessToken,
511
UserResponse user
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.back.domain.user.dto;
2+
3+
import jakarta.validation.constraints.NotBlank;
4+
5+
/**
6+
* 비밀번호 재설정 요청 DTO
7+
*
8+
* @param token 비밀번호 재설정 토큰
9+
* @param newPassword 새 비밀번호
10+
*/
11+
public record PasswordResetRequest(
12+
@NotBlank String token,
13+
@NotBlank String newPassword
14+
) {
15+
}

src/main/java/com/back/domain/user/dto/ResendVerificationRequest.java renamed to src/main/java/com/back/domain/user/dto/SendEmailRequest.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33
import jakarta.validation.constraints.Email;
44
import jakarta.validation.constraints.NotBlank;
55

6-
public record ResendVerificationRequest(
6+
/**
7+
* 이메일 전송 요청을 나타내는 DTO
8+
*
9+
* @param email 이메일 주소
10+
*/
11+
public record SendEmailRequest(
712
@NotBlank @Email String email
813
) {
914
}

0 commit comments

Comments
 (0)