Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ea9e53f
[#107] feat(profile): Profile 엔티티 추가
shinheekim Dec 20, 2025
331648d
[#107] feat(profile): ProfileRepository 추가
shinheekim Dec 20, 2025
8b1d623
[#107] refactor(user): User 엔티티에서 프로필 관련 필드 제거
shinheekim Dec 20, 2025
45f5cef
[#107] refactor(profile): user 패키지에서 profile 패키지로 클래스 이동 및 리팩토링
shinheekim Dec 20, 2025
8e9beb5
[#107] feat(auth): OauthAuthService에서 User 생성 시 Profile도 함께 생성
shinheekim Dec 20, 2025
e9e388f
[#107] refactor(profile): 다른 도메인에서 Profile 사용하도록 변경
shinheekim Dec 20, 2025
31f81fc
[#107] test(profile): Profile 도입에 따른 테스트 파일 업데이트
shinheekim Dec 20, 2025
d360165
[#107] feat(profile): 전화번호 인증 토큰 검증 메서드 추가
shinheekim Dec 28, 2025
cb15fc4
[#107] feat(profile): Profile 엔티티에 프로필 초기 설정 메서드 추가
shinheekim Dec 28, 2025
257ffe7
[#107] feat(profile): 프로필 설정 요청 DTO 생성
shinheekim Dec 28, 2025
eca6f09
[#107] feat(profile): 프로필 초기 설정 서비스 로직 구현
shinheekim Dec 28, 2025
7327f5c
[#107] feat(profile): 프로필 초기 설정 API 엔드포인트 추가
shinheekim Dec 28, 2025
a40c6f1
[#107] feat(profile): 테스트 코드 서비스명 변경 반영
shinheekim Dec 28, 2025
21a2cf1
[#107] refactor(profile): UserInfo DTO null 체크 제거
shinheekim Dec 28, 2025
e3eddca
[#107] docs(profile): 프로필 초기 설정 API docs swagger 추가
shinheekim Dec 28, 2025
d39b6fe
[#107] chore(profile): 현재는 불필요한 메서드 삭제
shinheekim Dec 29, 2025
edb026a
[#107] chore(profile): 기획상 변경으로 띄어쓰기 없는 랜덤 닉네임으로 생성
shinheekim Dec 29, 2025
1f8e5d3
[#107] chore(profile): UserId가 아닌 User객체로 Profile 조회
shinheekim Dec 29, 2025
2816dcf
[#107] chore(profile): 프로필 최초 저장 request 유효성 추가
shinheekim Dec 29, 2025
c3bf3fe
[#107] feat(profile): 닉네임 Race condition을 방지하기 위해 비관적 잠금 적용
shinheekim Dec 29, 2025
39810b8
[#107] test(profile): 프로필 최초 생성 단위 테스트 작성
shinheekim Dec 29, 2025
700cc6c
[#107] docs(profile): 프로필 관련 API 응답 구체적으로 작성
shinheekim Dec 29, 2025
9a7eb3a
[#107] chore(profile): 에러 코드 enum 위치 일부 수정
shinheekim Dec 30, 2025
283f866
[#107] chore(profile): 불필요한 주석 제거
shinheekim Dec 30, 2025
b3523d6
[#107] chore(profile): 닉네임 검증 시 동시성 락으로 제어
shinheekim Dec 30, 2025
bcc7ceb
[#107] chore(profile): 프로필 초기 세팅 request dto에서 검증 강화
shinheekim Dec 30, 2025
97bf1cd
[#107] test(profile): 제거된 코드에 대한 테스트 코드 제거
shinheekim Dec 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import com.dduru.gildongmu.common.exception.BusinessException;
import com.dduru.gildongmu.common.exception.ErrorCode;
import com.dduru.gildongmu.common.jwt.JwtTokenProvider;
import com.dduru.gildongmu.profile.domain.Profile;
import com.dduru.gildongmu.profile.repository.ProfileRepository;
import com.dduru.gildongmu.user.domain.User;
import com.dduru.gildongmu.user.enums.OauthType;
import com.dduru.gildongmu.user.enums.Role;
Expand All @@ -30,6 +32,7 @@ public class OauthAuthService {

private final OauthFactory oauthFactory;
private final UserRepository userRepository;
private final ProfileRepository profileRepository;
private final JwtTokenProvider jwtTokenProvider;
private final RefreshTokenService refreshTokenService;

Expand Down Expand Up @@ -145,22 +148,27 @@ private record UserCreationResult(User user, boolean isNewUser) {
}

private User createNewUser(OauthUserInfo oauthUserInfo) {
// 추가 정보는 회원가입 이후 별도 입력으로 변경
// 닉네임은 온보딩에서 설정하므로 null로 초기화

User newUser = User.builder()
.email(oauthUserInfo.email())
.name(oauthUserInfo.name())
.nickname(null)
.profileImage(oauthUserInfo.profileImage())
.oauthId(oauthUserInfo.oauthId())
.oauthType(oauthUserInfo.loginType())
.role(Role.USER)
.build();

User savedUser = userRepository.save(newUser);

Profile profile = Profile.builder()
.user(savedUser)
.profileImage(null)
.nickname(null)
.gender(null)
.phoneNumber(null)
.birthday(null)
.build();

return userRepository.save(newUser);
profileRepository.save(profile);

return savedUser;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ public static CommentResponse from(Comment comment) {
authorProfileImage = null;
} else {
content = comment.getContent();
author = comment.getUser().getNickname();
authorProfileImage = comment.getUser().getProfileImage();
author = comment.getUser().getProfile().getNickname();
authorProfileImage = comment.getUser().getProfile().getProfileImage();
}

return new CommentResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public enum ErrorCode {
UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "인증되지 않은 사용자입니다."),
DUPLICATE_EMAIL(HttpStatus.CONFLICT, "이미 다른 소셜 계정으로 가입된 이메일입니다."),

// 닉네임 (NICKNAME) - 이전 '온보딩 (ONBD)'
// 닉네임 (NICKNAME)
NICKNAME_NOT_BLANK(HttpStatus.BAD_REQUEST, "닉네임은 공백일 수 없습니다."),
NICKNAME_INVALID_LENGTH(HttpStatus.BAD_REQUEST, "닉네임은 2자 이상 14자 이하로 입력해주세요."),
NICKNAME_INVALID_CHARACTERS(HttpStatus.BAD_REQUEST, "닉네임은 한글, 영어, 숫자만 사용 가능합니다."),
Expand All @@ -25,6 +25,7 @@ public enum ErrorCode {

// 사용자 (USER)
USER_NOT_FOUND(HttpStatus.NOT_FOUND, "사용자를 찾을 수 없습니다."),
PROFILE_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 유저의 프로필을 찾을 수 없습니다."),

// 게시글 (POST)
POST_NOT_FOUND(HttpStatus.NOT_FOUND, "게시글을 찾을 수 없습니다."),
Expand Down
44 changes: 44 additions & 0 deletions src/main/java/com/dduru/gildongmu/common/jwt/JwtTokenProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,50 @@ public String createVerificationToken(Long userId, String phoneNumber) {
.compact();
}

/**
* Verification Token 검증 및 전화번호 추출
*
* @param token verification token
* @param expectedPhoneNumber 검증할 전화번호
* @return 검증 성공 여부
*/
public boolean validateVerificationToken(String token, String expectedPhoneNumber) {
try {
Claims claims = getClaims(token);
String tokenType = claims.get("type", String.class);
if (!"verification".equals(tokenType)) {
log.warn("Verification Token이 아닙니다 - {}", tokenType);
return false;
}

String phoneNumber = claims.get("phone_number", String.class);
if (phoneNumber == null || !phoneNumber.equals(expectedPhoneNumber)) {
log.warn("전화번호가 일치하지 않습니다. 토큰: {}, 요청: {}", phoneNumber, expectedPhoneNumber);
return false;
}

return true;
} catch (ExpiredJwtException e) {
log.warn("Verification Token이 만료되었습니다");
return false;
} catch (UnsupportedJwtException e) {
log.warn("JWT 형식이 틀렸습니다");
return false;
} catch (MalformedJwtException e) {
log.warn("JWT 구조가 잘못되었습니다");
return false;
} catch (SignatureException e) {
log.warn("JWT 서명 검증 실패하였습니다");
return false;
} catch (IllegalArgumentException e) {
log.warn("부적절한 값이 들어왔습니다");
return false;
} catch (JwtException e) {
log.warn("Verification Token이 유효하지 않습니다: {}", e.getMessage());
return false;
}
}

private Claims getClaims(String token) {
return Jwts.parser()
.setSigningKey(jwtSecret)
Expand Down
11 changes: 8 additions & 3 deletions src/main/java/com/dduru/gildongmu/post/domain/Post.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@
import com.dduru.gildongmu.destination.domain.Destination;
import com.dduru.gildongmu.participation.domain.Participation;
import com.dduru.gildongmu.post.enums.PostStatus;
import com.dduru.gildongmu.post.exception.*;
import com.dduru.gildongmu.post.exception.InvalidPostStatusException;
import com.dduru.gildongmu.post.exception.InvalidRecruitCapacityException;
import com.dduru.gildongmu.post.exception.RecruitCountBelowZeroException;
import com.dduru.gildongmu.post.exception.RecruitCountExceedCapacityException;
import com.dduru.gildongmu.post.exception.TravelAlreadyEndedException;
import com.dduru.gildongmu.post.exception.TravelAlreadyStartedException;
import com.dduru.gildongmu.profile.domain.enums.AgeRange;
import com.dduru.gildongmu.profile.domain.enums.Gender;
import com.dduru.gildongmu.user.domain.User;
import com.dduru.gildongmu.user.enums.AgeRange;
import com.dduru.gildongmu.user.enums.Gender;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.dduru.gildongmu.post.repository;

import com.dduru.gildongmu.user.enums.AgeRange;
import com.dduru.gildongmu.user.enums.Gender;
import com.dduru.gildongmu.post.domain.Post;
import com.dduru.gildongmu.post.dto.PostListRequest;
import com.dduru.gildongmu.post.enums.PostStatus;
import com.dduru.gildongmu.user.exception.InvalidGenderException;
import com.dduru.gildongmu.profile.domain.enums.AgeRange;
import com.dduru.gildongmu.profile.domain.enums.Gender;
import com.dduru.gildongmu.profile.exception.InvalidGenderException;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
import com.dduru.gildongmu.post.exception.InvalidPostDateException;
import com.dduru.gildongmu.post.exception.PostAccessDeniedException;
import com.dduru.gildongmu.post.repository.PostRepository;
import com.dduru.gildongmu.profile.domain.enums.AgeRange;
import com.dduru.gildongmu.profile.domain.enums.Gender;
import com.dduru.gildongmu.user.domain.User;
import com.dduru.gildongmu.user.enums.AgeRange;
import com.dduru.gildongmu.user.enums.Gender;
import com.dduru.gildongmu.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand Down
Loading