1+ package com .ai .lawyer .domain .member .controller ;
2+
3+ import com .ai .lawyer .domain .member .dto .MemberLoginRequest ;
4+ import com .ai .lawyer .domain .member .dto .MemberResponse ;
5+ import com .ai .lawyer .domain .member .dto .MemberSignupRequest ;
6+ import com .ai .lawyer .domain .member .entity .Member ;
7+ import com .ai .lawyer .domain .member .service .MemberService ;
8+ import io .swagger .v3 .oas .annotations .Operation ;
9+ import io .swagger .v3 .oas .annotations .responses .ApiResponse ;
10+ import io .swagger .v3 .oas .annotations .responses .ApiResponses ;
11+ import io .swagger .v3 .oas .annotations .tags .Tag ;
12+ import jakarta .servlet .http .HttpServletRequest ;
13+ import jakarta .servlet .http .HttpServletResponse ;
14+ import jakarta .validation .Valid ;
15+ import lombok .RequiredArgsConstructor ;
16+ import lombok .extern .slf4j .Slf4j ;
17+ import org .springframework .http .HttpStatus ;
18+ import org .springframework .http .ResponseEntity ;
19+ import org .springframework .security .core .Authentication ;
20+ import org .springframework .web .bind .annotation .*;
21+
22+ @ RestController
23+ @ RequestMapping ("/api/auth" )
24+ @ RequiredArgsConstructor
25+ @ Slf4j
26+ @ Tag (name = "Member" , description = "회원 관리 API" )
27+ public class MemberController {
28+
29+ private final MemberService memberService ;
30+
31+ @ PostMapping ("/signup" )
32+ @ Operation (summary = "회원가입" , description = "새로운 회원을 등록합니다." )
33+ @ ApiResponses ({
34+ @ ApiResponse (responseCode = "201" , description = "회원가입 성공" ),
35+ @ ApiResponse (responseCode = "400" , description = "잘못된 요청 (중복 이메일/닉네임, 유효성 검증 실패)" )
36+ })
37+ public ResponseEntity <MemberResponse > signup (@ Valid @ RequestBody MemberSignupRequest request ) {
38+ log .info ("회원가입 요청: email={}, nickname={}" , request .getLoginId (), request .getNickname ());
39+
40+ try {
41+ MemberResponse response = memberService .signup (request );
42+ log .info ("회원가입 성공: memberId={}" , response .getMemberId ());
43+ return ResponseEntity .status (HttpStatus .CREATED ).body (response );
44+ } catch (IllegalArgumentException e ) {
45+ log .warn ("회원가입 실패: {}" , e .getMessage ());
46+ return ResponseEntity .badRequest ().build ();
47+ }
48+ }
49+
50+ @ PostMapping ("/login" )
51+ @ Operation (summary = "로그인" , description = "이메일과 비밀번호로 로그인합니다." )
52+ @ ApiResponses ({
53+ @ ApiResponse (responseCode = "200" , description = "로그인 성공" ),
54+ @ ApiResponse (responseCode = "401" , description = "인증 실패 (존재하지 않는 회원, 비밀번호 불일치)" )
55+ })
56+ public ResponseEntity <MemberResponse > login (@ Valid @ RequestBody MemberLoginRequest request ,
57+ HttpServletResponse response ) {
58+ log .info ("로그인 요청: email={}" , request .getLoginId ());
59+
60+ try {
61+ MemberResponse memberResponse = memberService .login (request , response );
62+ log .info ("로그인 성공: memberId={}" , memberResponse .getMemberId ());
63+ return ResponseEntity .ok (memberResponse );
64+ } catch (IllegalArgumentException e ) {
65+ log .warn ("로그인 실패: {}" , e .getMessage ());
66+ return ResponseEntity .status (HttpStatus .UNAUTHORIZED ).build ();
67+ }
68+ }
69+
70+ @ PostMapping ("/logout" )
71+ @ Operation (summary = "로그아웃" , description = "현재 로그인된 사용자를 로그아웃합니다." )
72+ @ ApiResponses ({
73+ @ ApiResponse (responseCode = "200" , description = "로그아웃 성공" )
74+ })
75+ public ResponseEntity <Void > logout (HttpServletResponse response ) {
76+ log .info ("로그아웃 요청" );
77+
78+ memberService .logout (response );
79+ log .info ("로그아웃 완료" );
80+ return ResponseEntity .ok ().build ();
81+ }
82+
83+ @ PostMapping ("/refresh" )
84+ @ Operation (summary = "토큰 재발급" , description = "리프레시 토큰을 사용하여 새로운 액세스 토큰을 발급받습니다." )
85+ @ ApiResponses ({
86+ @ ApiResponse (responseCode = "200" , description = "토큰 재발급 성공" ),
87+ @ ApiResponse (responseCode = "401" , description = "유효하지 않은 리프레시 토큰" )
88+ })
89+ public ResponseEntity <MemberResponse > refreshToken (HttpServletRequest request ,
90+ HttpServletResponse response ) {
91+ log .info ("토큰 재발급 요청" );
92+
93+ // 쿠키에서 리프레시 토큰 추출 (간단한 방법)
94+ String refreshToken = extractRefreshTokenFromCookies (request );
95+
96+ if (refreshToken == null ) {
97+ log .warn ("리프레시 토큰이 없음" );
98+ return ResponseEntity .status (HttpStatus .UNAUTHORIZED ).build ();
99+ }
100+
101+ try {
102+ MemberResponse memberResponse = memberService .refreshToken (refreshToken , response );
103+ log .info ("토큰 재발급 성공: memberId={}" , memberResponse .getMemberId ());
104+ return ResponseEntity .ok (memberResponse );
105+ } catch (IllegalArgumentException e ) {
106+ log .warn ("토큰 재발급 실패: {}" , e .getMessage ());
107+ return ResponseEntity .status (HttpStatus .UNAUTHORIZED ).build ();
108+ }
109+ }
110+
111+ @ DeleteMapping ("/withdraw" )
112+ @ Operation (summary = "회원탈퇴" , description = "현재 로그인된 사용자의 계정을 삭제합니다." )
113+ @ ApiResponses ({
114+ @ ApiResponse (responseCode = "200" , description = "회원탈퇴 성공" ),
115+ @ ApiResponse (responseCode = "401" , description = "인증되지 않은 사용자" ),
116+ @ ApiResponse (responseCode = "404" , description = "존재하지 않는 회원" )
117+ })
118+ public ResponseEntity <Void > withdraw (Authentication authentication , HttpServletResponse response ) {
119+ if (authentication == null || authentication .getName () == null ) {
120+ log .warn ("인증되지 않은 회원탈퇴 요청" );
121+ return ResponseEntity .status (HttpStatus .UNAUTHORIZED ).build ();
122+ }
123+
124+ String loginId = authentication .getName ();
125+ log .info ("회원탈퇴 요청: email={}" , loginId );
126+
127+ try {
128+ // loginId로 Member를 조회하여 실제 memberId 사용
129+ Member member = memberService .findByLoginId (loginId );
130+ memberService .withdraw (member .getMemberId ());
131+ memberService .logout (response ); // 탈퇴 후 로그아웃 처리
132+ log .info ("회원탈퇴 성공: email={}, memberId={}" , loginId , member .getMemberId ());
133+ return ResponseEntity .ok ().build ();
134+ } catch (IllegalArgumentException e ) {
135+ log .warn ("회원탈퇴 실패: {}" , e .getMessage ());
136+ return ResponseEntity .notFound ().build ();
137+ }
138+ }
139+
140+ @ GetMapping ("/me" )
141+ @ Operation (summary = "내 정보 조회" , description = "현재 로그인된 사용자의 정보를 조회합니다." )
142+ @ ApiResponses ({
143+ @ ApiResponse (responseCode = "200" , description = "조회 성공" ),
144+ @ ApiResponse (responseCode = "401" , description = "인증되지 않은 사용자" )
145+ })
146+ public ResponseEntity <MemberResponse > getMyInfo (Authentication authentication ) {
147+ if (authentication == null || authentication .getName () == null ) {
148+ log .warn ("인증되지 않은 정보 조회 요청" );
149+ return ResponseEntity .status (HttpStatus .UNAUTHORIZED ).build ();
150+ }
151+
152+ String loginId = authentication .getName ();
153+ log .info ("내 정보 조회 요청: email={}" , loginId );
154+
155+ try {
156+ // loginId로 Member를 조회하여 실제 memberId 사용
157+ Member member = memberService .findByLoginId (loginId );
158+ MemberResponse response = memberService .getMemberById (member .getMemberId ());
159+ log .info ("내 정보 조회 성공: memberId={}" , response .getMemberId ());
160+ return ResponseEntity .ok (response );
161+ } catch (IllegalArgumentException e ) {
162+ log .warn ("내 정보 조회 실패: {}" , e .getMessage ());
163+ return ResponseEntity .notFound ().build ();
164+ }
165+ }
166+
167+ private String extractRefreshTokenFromCookies (HttpServletRequest request ) {
168+ if (request .getCookies () != null ) {
169+ for (jakarta .servlet .http .Cookie cookie : request .getCookies ()) {
170+ if ("refreshToken" .equals (cookie .getName ())) {
171+ return cookie .getValue ();
172+ }
173+ }
174+ }
175+ return null ;
176+ }
177+ }
0 commit comments