Skip to content

Commit 264e7fe

Browse files
authored
[refactor] 소셜 로그인 시, 추가 정보 확인/입력 API 구현 (#274)
1 parent 935b815 commit 264e7fe

File tree

11 files changed

+299
-22
lines changed

11 files changed

+299
-22
lines changed

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.springframework.http.ResponseCookie;
2424
import org.springframework.http.ResponseEntity;
2525
import org.springframework.security.core.Authentication;
26+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
2627
import org.springframework.web.bind.annotation.*;
2728

2829
@Slf4j
@@ -177,6 +178,22 @@ public ResponseEntity<RsData<AuthResponse>> refreshToken(
177178
.body(RsData.of("200", "토큰 재발급 성공", response));
178179
}
179180

181+
/**
182+
* 현재 로그인한 사용자 정보 조회
183+
*/
184+
@GetMapping("/me")
185+
@Operation(summary = "현재 사용자 정보 조회", description = "로그인한 사용자의 정보를 조회합니다. 소셜 로그인 후 추가 정보 필요 여부를 확인할 수 있습니다.")
186+
public ResponseEntity<RsData<AuthResponse>> getCurrentUser(
187+
@AuthenticationPrincipal CustomUserDetails userDetails
188+
) {
189+
log.info("현재 사용자 정보 조회 - userId: {}", userDetails.getUserId());
190+
191+
AuthResponse response = authService.getCurrentUser(userDetails.getUserId());
192+
193+
return ResponseEntity.ok(
194+
RsData.of("200", "사용자 정보 조회 성공", response)
195+
);
196+
}
180197

181198
/**
182199
* 쿠키 생성 헬퍼 메서드

src/main/java/com/back/domain/auth/dto/response/AuthResponse.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ public record AuthResponse(
1616
// 로그인 가능한 역할 목록
1717
List<Role> availableRoles,
1818

19-
Long accessTokenExpiresIn
19+
Long accessTokenExpiresIn,
20+
21+
// 추가 정보 필요 여부 (소셜로그인 사용자)
22+
boolean needsAdditionalInfo
2023
) {
2124

2225
// 로그인 성공 응답용 정적 팩토리 메서드
@@ -27,7 +30,8 @@ public static AuthResponse loginSuccess(
2730
String email,
2831
Role selectedRole,
2932
List<Role> availableRoles,
30-
Long accessTokenExpiresIn
33+
Long accessTokenExpiresIn,
34+
boolean needsAdditionalInfo
3135
) {
3236
return new AuthResponse(
3337
accessToken,
@@ -36,7 +40,8 @@ public static AuthResponse loginSuccess(
3640
email,
3741
selectedRole,
3842
availableRoles,
39-
accessTokenExpiresIn
43+
accessTokenExpiresIn,
44+
needsAdditionalInfo
4045
);
4146
}
4247

@@ -48,7 +53,8 @@ public static AuthResponse tokenRefreshSuccess(
4853
String email,
4954
Role selectedRole,
5055
List<Role> availableRoles,
51-
Long accessTokenExpiresIn
56+
Long accessTokenExpiresIn,
57+
boolean needsAdditionalInfo
5258
) {
5359
return new AuthResponse(
5460
accessToken,
@@ -57,7 +63,8 @@ public static AuthResponse tokenRefreshSuccess(
5763
email,
5864
selectedRole,
5965
availableRoles,
60-
accessTokenExpiresIn
66+
accessTokenExpiresIn,
67+
needsAdditionalInfo
6168
);
6269
}
6370
}

src/main/java/com/back/domain/auth/repository/UserTokenRepository.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ public interface UserTokenRepository extends JpaRepository<UserToken, Long> {
2222
@Query("DELETE FROM UserToken ut WHERE ut.user.id = :userId")
2323
int deleteAllRefreshTokenByUserId(@Param("userId") Long userId);
2424

25+
// 특정 사용자의 가장 최근 활성화된 토큰 조회
26+
Optional<UserToken> findFirstByUserIdAndIsActiveTrueOrderByCreateDateDesc(Long userId);
27+
2528
/**
2629
* 토큰 비활성화 메서드 - 주석 처리된 부분은 필요에 따라 활성화하여 사용
2730
*/

src/main/java/com/back/domain/auth/service/AuthService.java

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ public AuthResponse login(LoginRequest request) {
9797
user.getEmail(),
9898
request.selectedRole(),
9999
user.getAvailableLoginRoles(),
100-
accessTokenExpiration / 1000
100+
accessTokenExpiration / 1000,
101+
user.needsAdditionalInfo() // 소셜로그인 사용자 추가 정보 필요 여부
101102
);
102103
}
103104

@@ -143,7 +144,8 @@ public AuthResponse refreshToken(TokenRefreshRequest request) {
143144
user.getEmail(),
144145
userToken.getLoginRole(),
145146
user.getAvailableLoginRoles(),
146-
accessTokenExpiration / 1000
147+
accessTokenExpiration / 1000,
148+
user.needsAdditionalInfo() // 소셜로그인 사용자 추가 정보 필요 여부
147149
);
148150
}
149151

@@ -280,4 +282,32 @@ private UserToken validateAndGetRefreshToken(String refreshToken) {
280282

281283
return userToken;
282284
}
285+
286+
/**
287+
* 현재 로그인한 사용자 정보 조회
288+
*/
289+
@Transactional(readOnly = true)
290+
public AuthResponse getCurrentUser(Long userId) {
291+
User user = userRepository.findById(userId)
292+
.orElseThrow(() -> new ServiceException("404", "사용자를 찾을 수 없습니다."));
293+
294+
// 현재 활성화된 토큰 조회 (가장 최근 토큰 하나만)
295+
UserToken userToken = userTokenRepository
296+
.findFirstByUserIdAndIsActiveTrueOrderByCreateDateDesc(userId)
297+
.orElseThrow(() -> new ServiceException("401", "유효한 토큰이 없습니다."));
298+
299+
log.info("현재 사용자 정보 조회: userId={}, needsAdditionalInfo={}",
300+
userId, user.needsAdditionalInfo());
301+
302+
return AuthResponse.loginSuccess(
303+
null, // accessToken은 이미 쿠키에 있으므로 null
304+
null, // refreshToken도 이미 쿠키에 있으므로 null
305+
user.getId(),
306+
user.getEmail(),
307+
userToken.getLoginRole(),
308+
user.getAvailableLoginRoles(),
309+
accessTokenExpiration / 1000,
310+
user.needsAdditionalInfo()
311+
);
312+
}
283313
}

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

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

3+
import com.back.domain.user.dto.request.UpdateOAuthUserInfoRequest;
34
import com.back.domain.user.dto.request.UpdateUserInfoRequest;
45
import com.back.domain.user.dto.response.UserProfileResponse;
56
import com.back.domain.user.service.UserService;
@@ -58,6 +59,27 @@ public ResponseEntity<RsData<UserProfileResponse>> updateMyInfo(
5859
);
5960
}
6061

62+
/**
63+
* OAuth 사용자 전화번호 입력
64+
*/
65+
@PatchMapping("/me/oauth-info")
66+
@Operation(summary = "OAuth 전화번호 입력",
67+
description = "소셜 로그인 사용자가 전화번호를 입력합니다. (최초 1회)")
68+
public ResponseEntity<RsData<UserProfileResponse>> updateOAuthAdditionalInfo(
69+
@AuthenticationPrincipal CustomUserDetails userDetails,
70+
@Valid @RequestBody UpdateOAuthUserInfoRequest request) {
71+
72+
log.info("OAuth 전화번호 입력 - userId: {}, phone: {}",
73+
userDetails.getUserId(), request.phone());
74+
75+
UserProfileResponse response = userService.updateOAuthAdditionalInfo(
76+
userDetails.getUserId(), request);
77+
78+
return ResponseEntity.ok(
79+
RsData.of("200", "전화번호 입력 완료", response)
80+
);
81+
}
82+
6183
/**
6284
* 회원 탈퇴
6385
*/
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.back.domain.user.dto.request;
2+
3+
import jakarta.validation.constraints.NotBlank;
4+
import jakarta.validation.constraints.Pattern;
5+
6+
public record UpdateOAuthUserInfoRequest(
7+
@NotBlank(message = "전화번호는 필수입니다.")
8+
@Pattern(regexp = "^01[0-9]-\\d{4}-\\d{4}$",
9+
message = "전화번호는 010-1234-5678 형식이어야 합니다.")
10+
String phone
11+
) {
12+
}

src/main/java/com/back/domain/user/entity/User.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,4 +321,11 @@ public void updateOAuthProfile(String name, String profileImageUrl) {
321321
}
322322
}
323323

324+
/**
325+
* OAuth 사용자의 추가 정보 입력 필요 여부 확인
326+
*/
327+
public boolean needsAdditionalInfo() {
328+
return isOAuthUser() && (this.phone == null || this.phone.isBlank());
329+
}
330+
324331
}

src/main/java/com/back/domain/user/service/UserService.java

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

3+
import com.back.domain.user.dto.request.UpdateOAuthUserInfoRequest;
34
import com.back.domain.user.dto.request.UpdateUserInfoRequest;
45
import com.back.domain.user.dto.response.UserProfileResponse;
56
import com.back.domain.user.entity.Grade;
@@ -103,6 +104,41 @@ public UserProfileResponse updateUserInfo(Long userId, UpdateUserInfoRequest req
103104
return UserProfileResponse.from(user);
104105
}
105106

107+
/**
108+
* OAuth 사용자 추가 정보 입력 (전화번호)
109+
*/
110+
@Transactional
111+
public UserProfileResponse updateOAuthAdditionalInfo(Long userId, UpdateOAuthUserInfoRequest request) {
112+
User user = getUserById(userId);
113+
114+
// 1. OAuth 사용자만 가능
115+
if (!user.isOAuthUser()) {
116+
throw new ServiceException("400", "소셜 로그인 사용자만 이용할 수 있습니다.");
117+
}
118+
119+
// 2. 이미 전화번호가 있으면 수정 불가 (최초 1회만)
120+
if (user.getPhone() != null && !user.getPhone().isBlank()) {
121+
throw new ServiceException("400", "이미 전화번호를 등록하셨습니다.");
122+
}
123+
124+
// 3. 전화번호 중복 체크
125+
if (userRepository.existsByPhone(request.phone())) {
126+
throw new ServiceException("409", "이미 사용 중인 전화번호입니다.");
127+
}
128+
129+
// 4. 전화번호 업데이트
130+
user.updateProfile(
131+
null, // name 변경 안 함
132+
request.phone(), // phone만 업데이트
133+
null, null, null, // 주소 변경 안 함
134+
null // 프로필 이미지 변경 안 함
135+
);
136+
137+
log.info("OAuth 전화번호 입력 완료 - userId: {}, phone: {}", userId, request.phone());
138+
139+
return UserProfileResponse.from(user);
140+
}
141+
106142
// ===== 작가 관련 ===== //
107143

108144
/**

src/main/java/com/back/global/initData/TestInitData.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import com.back.domain.product.category.repository.CategoryRepository;
1111
import com.back.domain.product.tag.entity.Tag;
1212
import com.back.domain.product.tag.repository.TagRepository;
13+
import com.back.domain.user.entity.Provider;
1314
import com.back.domain.user.entity.Role;
1415
import com.back.domain.user.entity.User;
1516
import com.back.domain.user.repository.UserRepository;
@@ -64,6 +65,7 @@ public void work1() {
6465

6566
createUser("[email protected]", "1234qwer!", "작가1", "010-2111-1111", Role.ARTIST);
6667
createUser("[email protected]", "1234qwer!", "관리자1", "010-3111-1111", Role.ADMIN);
68+
createOAuthUser("[email protected]", "OAuth테스트유저", Provider.GOOGLE, "google-test-id-123");
6769
}
6870

6971
private void safeSignup(String email, String password, String passwordConfirm, String name, String phone) {
@@ -265,4 +267,16 @@ private Category createSubCategoryIfNotExists(String categoryName, Category pare
265267
return saved;
266268
});
267269
}
270+
271+
// OAuth 테스트 사용자 생성
272+
private void createOAuthUser(String email, String name, Provider provider, String providerId) {
273+
if (userRepository.existsByEmail(email)) {
274+
log.debug("OAuth 사용자 이미 존재: {}", email);
275+
return;
276+
}
277+
278+
User oauthUser = User.createOAuthUser(email, name, provider, providerId);
279+
userRepository.save(oauthUser);
280+
log.info("OAuth 테스트 사용자 생성: email={}, provider={}", email, provider);
281+
}
268282
}

0 commit comments

Comments
 (0)