che01 님의 블로그
Spring Boot JWT 인증 & 프로필 관리 트러블슈팅 가이드 본문
Spring Boot에서 JWT 인증과 프로필 관리 기능을 구현하면서 마주칠 수 있는 주요 문제점들과 해결 방법을 정리했습니다.
1. @AuthenticationPrincipal JwtPayload 사용 문제 해결
문제 상황
컨트롤러 메서드 매개변수에 JwtPayload jwtPayload를 선언했지만 사용하지 않아서 IDE에서 "사용되지 않는 변수" 경고가 발생하는 경우
해결 방법
@PutMapping("/{id}")
public ResponseEntity<UpdateProfileResponseDto> updateProfile(
@PathVariable Long id,
@RequestBody UpdateProfileRequestDto requestDto,
@AuthenticationPrincipal JwtPayload jwtPayload) {
// 받은 인증 정보를 반드시 사용
Long userId = jwtPayload.getUserId();
UpdateProfileResponseDto responseDto = profileService.updateProfile(userId, id, requestDto);
return ResponseEntity.ok(responseDto);
}
핵심 포인트:
- 인증 정보를 받았으면 반드시 사용할 것
- 인증이 필요 없다면 매개변수에서 제거
- 인증이 필요하다면 서비스 레이어로 전달하여 권한 검증에 활용
2. 프로필 수정 권한 검증 (서비스 레이어)
문제 상황
다른 사용자의 프로필을 수정할 수 있는 보안 취약점
해결 방법
@Transactional
public UpdateProfileResponseDto updateProfile(Long userId, Long profileId, UpdateProfileRequestDto requestDto) {
Profile profile = profileRepository.findById(profileId)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "프로필을 찾을 수 없습니다."));
// 권한 검증: 요청자와 프로필 소유자가 같은지 확인
if (!profile.getAccount().getId().equals(userId)) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "수정 권한이 없습니다.");
}
profile.updateProfile(requestDto.getNickname(), requestDto.getBirth(), requestDto.getBio());
return UpdateProfileResponseDto.from(profile);
}
핵심 포인트:
- 요청자(userId)와 프로필 소유자(accountId) 일치 여부 검증
- 권한이 없으면 403 Forbidden 반환
- 서비스 레이어에서 비즈니스 로직과 권한 검증을 함께 처리
3. 엔티티 updateProfile 메서드 최적화
문제 상황
불필요한 매개변수로 인한 코드 복잡성 증가
해결 방법
@Entity
public class Profile {
// ... 필드 생략
// 수정이 필요한 필드만 매개변수로 받기
public void updateProfile(String nickname, Date birth, String bio) {
this.nickname = nickname;
this.birth = birth;
this.bio = bio;
// Account는 수정하지 않으므로 매개변수에서 제외
}
}
핵심 포인트:
- 실제로 수정할 필드만 매개변수로 받기
- 연관관계 엔티티(Account)는 수정 대상이 아니므로 제외
- 메서드 시그니처를 간단하고 명확하게 유지
4. 컨트롤러-서비스 메서드 호출 일치성
문제 상황
컨트롤러에서 서비스 메서드 호출 시 매개변수 순서나 타입 불일치
해결 방법
// 서비스 메서드 시그니처
public UpdateProfileResponseDto updateProfile(Long userId, Long profileId, UpdateProfileRequestDto requestDto)
// 컨트롤러 호출 부분
@PutMapping("/{id}")
public ResponseEntity<UpdateProfileResponseDto> updateProfile(
@PathVariable Long id,
@RequestBody UpdateProfileRequestDto requestDto,
@AuthenticationPrincipal JwtPayload jwtPayload) {
Long userId = jwtPayload.getUserId();
// 서비스 메서드 시그니처와 정확히 일치
UpdateProfileResponseDto responseDto = profileService.updateProfile(userId, id, requestDto);
return ResponseEntity.ok(responseDto);
}
핵심 포인트:
- 메서드 시그니처 일치성 확인
- 매개변수 순서와 타입 정확히 맞추기
- IDE의 자동완성 기능 적극 활용
마무리
이런 패턴들을 미리 정리해두면 비슷한 기능을 구현할 때 빠르게 참고할 수 있고, 팀 내에서 일관된 코드 스타일을 유지하는 데도 도움이 됩니다.
특히 인증과 권한 검증은 보안에 직결되는 부분이므로, 위의 체크리스트를 활용해서 빠짐없이 검토하시기 바랍니다!