🏅오늘의 목표
✅ 진행한 작업
- 회원 정보 조회 API
- 회원 정보 수정 API
📃 개발내용
UserService - 회원 정보 조회
public ProfileDto getMyProfile(UUID userUuid){
// 유저 찾기
User user = userRepository.findByUuid(userUuid)
.orElseThrow(() -> new BaseException(UserErrorCode.USER_NOT_FOUND));
// 프로필 검증
if (user.getProfile() == null) {
throw new BaseException(UserErrorCode.PROFILE_NOT_FOUND);
}
return profileMapper.toDto(user);
}
주요 내용
- 외부에 노출되는 식별자 uuid 로 조회
- User 는 인증/권한/상태를 담당하고 Profile 은 화면 노출 정보를 담당하도록 분리했기 때문에, 조회 시 user.getProfile() 이 존재하는지 검증
- 응답은 엔티티를 노출하지 않고 ProfileDto 로 변환해 반환
UserController - 회원 정보 조회
@GetMapping("/me")
public ResponseEntity<ProfileDto> myPage(
@AuthenticationPrincipal CustomUserDetails customUserDetails
) {
UUID userId = customUserDetails.getUserUuid();
ProfileDto profile = userService.getMyProfile(userId);
return ResponseEntity.ok().body(profile);
}
주요 내용
- Get /users/me 는 현재 로그인한 사용자 의 정보를 조회
- @AuthenticationPrincipal 을 통해 SecurityContext 에 저장된 인증 사용자 정보를 주입받는다.
- 200 OK 와 함께 ProfileDto 를 반환하여 프론트에서 바로 마이페이지 UI 구성
DTO , Mapper - 회원 정보 조회 관련
public record ProfileDto(
String email,
String name,
String nickname,
String phoneNumber,
String profileUrl,
String region
) {
}
@Mapper(componentModel = "spring")
public interface ProfileMapper {
@Mapping(source = "email", target = "email")
@Mapping(source = "profile.name", target = "name")
@Mapping(source = "profile.nickname", target = "nickname")
@Mapping(source = "profile.phoneNumber", target = "phoneNumber")
@Mapping(source = "profile.profileUrl", target = "profileUrl")
@Mapping(source = "profile.region", target = "region")
ProfileDto toDto(User user);
}
주요 내용
- 마이페이지 응답에서 필요한 값만 담기 위해 ProfileDto 정의
- 불필요한 필드 노출 방지, 순환 참조 문제 방지
- MapStruct 사용
- 서비스 로직에서는 “조회 → 매핑” 흐름만 유지
- 변환 책임은 Mapper
UserService - 회원 정보 수정
@Transactional
public ProfileDto updateProfile(UUID userUuid, UserUpdateRequest request) {
// 유저 찾기
User user = userRepository.findByUuid(userUuid)
.orElseThrow(() -> new BaseException(UserErrorCode.USER_NOT_FOUND));
Profile profile = user.getProfile();
if (profile == null) {
throw new BaseException(UserErrorCode.PROFILE_NOT_FOUND);
}
// 닉네임 변경 시 중복 검증
validateNicknameChange(user,request.nickname());
// 회원정보 수정
profile.update(
request.nickname(),
request.name(),
request.phoneNumber(),
request.profileUrl(),
request.region()
);
log.info("프로필 업데이트 완료 : userId={}, email={}", userUuid, user.getEmail());
return profileMapper.toDto(user);
}
// 닉네임 중복 검증
private void validateNicknameChange(User user,String newNickname) {
// 기존 닉네임과 같으면 검증 불필요
if (newNickname.equals(user.getProfile().getNickname())) return;
// 중복 검증
if (profileRepository.existsByNickname(newNickname)) {
throw new BaseException(UserErrorCode.USER_NICKNAME_EXISTS);
}
}
주요 내용
- 닉네임은 unique 제약이 있으므로, 닉네임이 실제로 변경되는 경우에만 중복 검증
- 실제 값 변경은 Profile.update() 도메인 메서드를 통해 수행
UserController - 회원 정보 수정
@PatchMapping("/me")
public ResponseEntity<ProfileDto> updateMyProfile(
@AuthenticationPrincipal CustomUserDetails customUserDetails,
@Valid @RequestBody UserUpdateRequest userUpdateRequest
){
ProfileDto updateProfile = userService.updateProfile(customUserDetails.getUesrUuid(), userUpdateRequest);
return ResponseEntity.ok().body(updateProfile);
}
주요 내용
- PATCH /users/me 는 로그인한 사용자의 프로필을 수정하는 엔드포인트