Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -63,8 +63,8 @@ public String getGender() {
return member.getGender();
}

public Integer getAge() {
return member.getAge();
public String getBirthDate() {
return member.getBirthDate();
}
// =============================================================

Expand All @@ -85,4 +85,4 @@ public String getName() {
@Override public boolean isAccountNonLocked() { return true; }
@Override public boolean isCredentialsNonExpired() { return true; }
@Override public boolean isEnabled() { return true; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,26 +38,26 @@ public LoginResponse.TokenResponse generateTokens(Authentication authentication)
String email = "";
String name = "";
String gender = null;
Integer age = null;
String birthDate = null;

Object principal = authentication.getPrincipal();
if (principal instanceof CustomUserDetails userDetails) {
memberId = userDetails.getName();
email = userDetails.getEmail();
name = userDetails.getRealName();
gender = userDetails.getGender();
age = userDetails.getAge();
birthDate = userDetails.getBirthDate();
} else {
memberId = authentication.getName();
}

String accessToken = createToken(memberId, email, name, gender, age, authorities, now, jwtConfig.getAccess().getExpireMin());
String refreshToken = createToken(memberId, email, name, gender, age, authorities, now, jwtConfig.getRefresh().getExpireMin());
String accessToken = createToken(memberId, email, name, gender, birthDate, authorities, now, jwtConfig.getAccess().getExpireMin());
String refreshToken = createToken(memberId, email, name, gender, birthDate, authorities, now, jwtConfig.getRefresh().getExpireMin());

return new LoginResponse.TokenResponse(accessToken, refreshToken);
}

private String createToken(String subject, String email, String name, String gender, Integer age,
private String createToken(String subject, String email, String name, String gender, String birthDate,
String authorities, Instant issuedAt, long expirationMinutes) {
try {
PrivateKey privateKey = getPrivateKey(jwtConfig.getPrivateKey());
Expand All @@ -70,7 +70,7 @@ private String createToken(String subject, String email, String name, String gen
.claim("authorities", authorities); // 권한이 없으면 빈 문자열 ""이 들어감

if (gender != null) builder.claim("gender", gender);
if (age != null) builder.claim("age", age);
if (birthDate != null) builder.claim("birthDate", birthDate);

return builder
.issuedAt(Date.from(issuedAt))
Expand All @@ -97,7 +97,7 @@ public Authentication getAuthentication(String token) {
String name = claims.get("name", String.class);
String authoritiesStr = claims.get("authorities", String.class);
String gender = claims.get("gender", String.class);
Integer age = claims.get("age", Integer.class);
String birthDate = claims.get("birthDate", String.class);

// 2. 권한 목록 생성 [수정된 부분: 빈 문자열 처리 추가]
List<GrantedAuthority> authorities = new ArrayList<>();
Expand All @@ -115,7 +115,7 @@ public Authentication getAuthentication(String token) {
.email(new MemberEmail(email))
.name(new MemberName(name))
.gender(gender)
.age(age)
.birthDate(birthDate)
.password(null)
.build();

Expand Down Expand Up @@ -174,4 +174,4 @@ private List<String> getAuthorities(Authentication authentication) {
.map(GrantedAuthority::getAuthority)
.toList();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class ProfileResponse {
private String email;
private String name;
private String gender;
private Integer age;
private String birthDate;
private Boolean isVerified;
private String profileImageUrl;
private String bio;
Expand All @@ -25,7 +25,7 @@ public static ProfileResponse from(Member member, List<String> travelStyles) {
.email(member.getEmailValue())
.name(member.getNameValue())
.gender(member.getGender())
.age(member.getAge())
.birthDate(member.getBirthDate())
.isVerified(member.isVerified())
.profileImageUrl(member.getProfileImageUrl())
.bio(member.getBio())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
public class VerifyIdentityResponse {
private String name;
private String gender;
private Integer age;
private String birthDate;
private Boolean isVerified;

public static VerifyIdentityResponse from(Member member) {
return VerifyIdentityResponse.builder()
.name(member.getNameValue())
.gender(member.getGender())
.age(member.getAge())
.birthDate(member.getBirthDate())
.isVerified(member.isVerified())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public MemberUpdateResponse updateUser(UUID memberId, MemberUpdateRequest reques
request.name(),
encodedNewPassword,
request.gender(),
request.age()
request.birthDate()
);

// Access Token 재발급
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.retrip.auth.domain.entity.Member;
import io.swagger.v3.oas.annotations.media.Schema;

import java.util.List;

@Schema(description = "Member 회원가입 Request")
public record MemberCreateRequest(
@Schema(description = "이메일")
Expand All @@ -13,20 +15,23 @@ public record MemberCreateRequest(
String name,
@Schema(description = "성별 (M/F)")
String gender,
@Schema(description = "나이")
Integer age
@Schema(description = "생년월일 (YYYY-MM-DD)")
String birthDate,
@Schema(description = "필수 약관 동의")
boolean termsAgreed,
@Schema(description = "마케팅 수신 동의")
boolean marketingAgreed
) {
public Member to(String encodePassword) {
return Member.builder()
.email(new com.retrip.auth.domain.vo.MemberEmail(email))
.password(new com.retrip.auth.domain.vo.MemberPassword(encodePassword))
.name(new com.retrip.auth.domain.vo.MemberName(name))
.gender(gender)
.age(age)
.isDeleted(false)
.provider("local")
.isVerified(false)
.id(java.util.UUID.randomUUID())
.build();
return Member.create(
name,
email,
encodePassword,
List.of("user"),
gender,
birthDate,
termsAgreed,
marketingAgreed
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public record MemberUpdateRequest(
String name,
@Schema(description = "성별 (M/F)")
String gender,
@Schema(description = "나이")
Integer age
@Schema(description = "생년월일 (YYYY-MM-DD)")
String birthDate
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ public VerifyIdentityResponse verifyAndSave(String identityVerificationId, Strin
// 성별 변환 (MALE -> M, FEMALE -> F)
String gender = "MALE".equals(certInfo.getGender()) ? "M" : "F";

log.info("✅ 회원 본인인증 정보 업데이트 - Name: {}, Gender: {}, Age: {}",
certInfo.getName(), gender, member.calculateAge(certInfo.getBirthday()));
log.info("✅ 회원 본인인증 정보 업데이트 - Name: {}, Gender: {}, BirthDate: {}",
certInfo.getName(), gender, certInfo.getBirthday());

member.updateIdentityVerification(
certInfo.getName(),
Expand Down
45 changes: 19 additions & 26 deletions src/main/java/com/retrip/auth/domain/entity/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,17 @@ public class Member extends BaseEntity {
@Column(name = "gender", length = 1)
private String gender;

@Column(name = "age")
private Integer age;
@Column(length = 10)
private String birthDate;

// 약관 동의
@Column(nullable = false)
@Builder.Default
private boolean termsAgreed = false;

@Column(nullable = false)
@Builder.Default
private boolean marketingAgreed = false;

// 프로필 관련
@Column(length = 500)
Expand Down Expand Up @@ -90,7 +99,7 @@ public String getNameValue() {
return this.name != null ? this.name.getValue() : null;
}

public static Member create(String name, String email, String password, List<String> authorities, String gender, Integer age) {
public static Member create(String name, String email, String password, List<String> authorities, String gender, String birthDate, boolean termsAgreed, boolean marketingAgreed) {
Member member = Member.builder()
.id(UUID.randomUUID())
.name(new MemberName(name))
Expand All @@ -100,7 +109,9 @@ public static Member create(String name, String email, String password, List<Str
.provider("local")
.isVerified(false)
.gender(gender)
.age(age)
.birthDate(birthDate)
.termsAgreed(termsAgreed)
.marketingAgreed(marketingAgreed)
.build();
member.authorities = new Authorities(authorities, member);
return member;
Expand All @@ -121,11 +132,11 @@ public static Member createSocialMember(String name, String email, String provid
return member;
}

public void update(String name, String password, String gender, Integer age) {
public void update(String name, String password, String gender, String birthDate) {
if (name != null) this.name = new MemberName(name);
if (password != null) this.password = new MemberPassword(password);
if (gender != null) this.gender = gender;
if (age != null) this.age = age;
if (birthDate != null) this.birthDate = birthDate;
}

public void updateProfile(String bio, String mbti, String profileImageUrl) {
Expand All @@ -138,34 +149,16 @@ public void updateNotificationSettings(boolean enabled) {
this.notificationEnabled = enabled;
}

public void updateIdentityVerification(String name, String gender, String birthday, String ci, String di) {
public void updateIdentityVerification(String name, String gender, String birthDate, String ci, String di) {
this.name = new MemberName(name);
this.gender = gender;
this.age = calculateAge(birthday);
this.birthDate = birthDate;
this.ci = ci;
this.di = di;
this.isVerified = true;
this.verifiedAt = LocalDateTime.now();
}

public int calculateAge(String birthday) {
// V2: "1990-01-01" 형식
// V1: "19900101" 형식

String yearStr;
if (birthday.contains("-")) {
// V2 형식: YYYY-MM-DD
yearStr = birthday.substring(0, 4);
} else {
// V1 형식: YYYYMMDD
yearStr = birthday.substring(0, 4);
}

int birthYear = Integer.parseInt(yearStr);
int currentYear = LocalDateTime.now().getYear();
return currentYear - birthYear;
}

public void updateSocialInfo(String name) {
this.name = new MemberName(name);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,95 @@
package com.retrip.auth.infra.adapter.in.rest.in;

import io.swagger.v3.oas.annotations.tags.Tag;
import com.retrip.auth.application.config.CustomUserDetails;
import com.retrip.auth.application.config.JwtProvider;
import com.retrip.auth.application.in.response.LoginResponse;
import com.retrip.auth.application.out.repository.MemberRepository;
import com.retrip.auth.domain.entity.Authorities;
import com.retrip.auth.domain.entity.Member;
import com.retrip.auth.domain.vo.MemberEmail;
import com.retrip.auth.domain.vo.MemberName;
import com.retrip.auth.domain.vo.MemberPassword;
import com.retrip.auth.infra.adapter.in.rest.common.ApiResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Profile;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.UUID;

@Slf4j
@Profile({"local", "dev"})
@RestController
@RequestMapping("/test")
@RequestMapping("/debug")
@RequiredArgsConstructor
@Tag(name = "Test", description = "테스트 관련 API")
public class TestController {
@GetMapping
public String test(Authentication authentication){
return authentication.getName();

private final JwtProvider jwtProvider;
private final MemberRepository memberRepository;
private final PasswordEncoder passwordEncoder;

/**
* 테스트용 백도어 API
* 프론트엔드 개발자가 로그인 없이 JWT 토큰을 발급받을 수 있도록 제공
*
* ⚠️ 주의: local, dev 프로파일에서만 활성화됩니다.
*/
@GetMapping("/test-token")
@Transactional
public ApiResponse<String> getTestToken() {
log.warn("🚨 테스트 토큰 발급 - 개발 환경 전용 API");

UUID testUserId = UUID.fromString("00000000-0000-0000-0000-000000000000");
String testEmail = "test@test.com";

// 1. 기존 테스트 사용자 조회 (이메일로)
Member testMember = memberRepository.findByEmail_Value(testEmail)
.filter(m -> !m.getIsDeleted()) // 탈퇴하지 않은 사용자만
.orElseGet(() -> {
log.info("🔨 테스트 사용자 생성 중...");

// 2. Member.create() 사용
Member newMember = Member.create(
"테스트유저",
testEmail,
passwordEncoder.encode("test1234"),
List.of("user"),
"M",
"1995-01-01",
true, // termsAgreed
true // marketingAgreed
);

// 3. DB 저장
return memberRepository.save(newMember);
});

log.info("✅ 테스트 사용자 확인 - UUID: {}, Email: {}",
testMember.getId(), testMember.getEmailValue());

// CustomUserDetails로 감싸기
CustomUserDetails userDetails = new CustomUserDetails(testMember);

// Authentication 객체 생성
Authentication authentication = new UsernamePasswordAuthenticationToken(
userDetails,
null,
List.of(new SimpleGrantedAuthority("ROLE_USER"))
);

// JWT 토큰 생성
LoginResponse.TokenResponse tokens = jwtProvider.generateTokens(authentication);

log.info("✅ 테스트 토큰 발급 완료");

return ApiResponse.ok(tokens.accessToken());
}
}