런닝 코스 공유 서비스

[런닝 코스 공유 서비스] - 13. 회원 정보 조회 및 수정

sson-coding 2026. 1. 11. 15:20

🏅오늘의 목표

  • 회원 정보 조회
  • 회원 정보 수정

진행한 작업

  • 회원 정보 조회 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 는 로그인한 사용자의 프로필을 수정하는 엔드포인트