java.lang.IllegalArgumentException: rawPassword cannot be null

Kim jisu·2025년 1월 27일

 Debugging Note

목록 보기
9/37

Spring MVC @RequestBody 디버깅과 해결 과정

Spring MVC에서 컨트롤러 메서드를 작성할 때 @RequestBody의 유무가 클라이언트 요청 처리 방식에 영향을 미칩니다. 최근 한 프로젝트에서 @RequestBody를 생략했을 때 발생한 문제를 디버깅하고 해결한 과정을 정리해보았습니다.


문제 상황

아래와 같이 회원가입 요청을 처리하는 컨트롤러 메서드를 작성했습니다.

@PostMapping("/signup")
public ResponseEntity<?> signup(SignupRequestDTO requestDTO) {
    userService.signup(requestDTO);
    return ResponseEntity.ok(new ApiResponse("success", requestDTO));
}

클라이언트는 JSON 데이터를 POST 요청으로 전송했습니다.

POST /signup
Content-Type: application/json

{
    "username": "user1",
    "password": "password123"
}

하지만 서버 로그에 다음과 같은 에러가 발생했습니다:

2025-01-27T23:40:17.070+09:00 ERROR 23676 --- [BE] [nio-8082-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.IllegalArgumentException: rawPassword cannot be null] with root cause

java.lang.IllegalArgumentException: rawPassword cannot be null

원인 분석

  1. @RequestBody의 역할

    • @RequestBody는 HTTP 요청의 바디에 포함된 JSON 데이터를 Java 객체로 변환합니다. 이를 위해 Spring의 HttpMessageConverter가 작동합니다.
    • @RequestBody가 없으면 Spring은 요청 데이터를 기본적으로 쿼리 파라미터폼 데이터로 처리하려고 시도합니다. 하지만 클라이언트는 JSON 데이터를 요청 바디에 전송했기 때문에 변환이 이루어지지 않았습니다.
  2. DTO 필드 초기화 실패

    • @RequestBody가 없으므로 SignupRequestDTO 객체의 필드가 초기화되지 않고 모두 null로 설정되었습니다.
    • 서비스 계층에서 비밀번호 처리 로직(passwordEncoder.encode(requestDTO.getPassword()))이 호출되면서, rawPasswordnull로 전달되어 IllegalArgumentException이 발생했습니다.
  3. 클라이언트와 서버의 요청 처리 방식 불일치

    • 클라이언트는 JSON 형식으로 데이터를 보냈지만, 서버는 이를 처리할 준비가 되어 있지 않았습니다.

디버깅 방법

1. 요청 데이터 확인

우선 HTTP 요청 데이터가 제대로 전달되는지 확인하기 위해 요청의 Content-Type을 로깅했습니다.

@PostMapping("/signup")
public ResponseEntity<?> signup(HttpServletRequest request) {
    System.out.println("Content-Type: " + request.getContentType());
    return ResponseEntity.badRequest().build();
}

결과 로그:

Content-Type: application/json

요청은 JSON 형식으로 제대로 전달되고 있음을 확인했습니다.

2. @RequestBody 추가 후 동작 확인

컨트롤러 메서드에 @RequestBody를 추가하여 요청 데이터를 매핑하도록 수정했습니다.

@PostMapping("/signup")
public ResponseEntity<?> signup(@RequestBody SignupRequestDTO requestDTO) {
    userService.signup(requestDTO);
    return ResponseEntity.ok(new ApiResponse("success", requestDTO));
}

클라이언트 요청:

POST /signup
Content-Type: application/json

{
    "username": "user1",
    "password": "password123"
}

수정 후 정상적으로 동작하며 요청 데이터가 DTO에 매핑되었습니다.


해결 방법

최종 코드

컨트롤러에서 @RequestBody를 추가하여 요청 데이터를 매핑하도록 설정했습니다.

@PostMapping("/signup")
@Operation(summary = "회원가입")
public ResponseEntity<?> signup(@RequestBody SignupRequestDTO requestDTO) {
    userService.signup(requestDTO);
    return ResponseEntity.ok(new ApiResponse("success", requestDTO));
}

요청 데이터의 예시

클라이언트는 반드시 다음과 같은 JSON 데이터를 요청 바디에 포함해야 합니다.

{
    "username": "user1",
    "password": "password123"
}

헤더에는 Content-Type: application/json이 포함되어야 합니다.


추가 학습 포인트

1. DTO 유효성 검사

SignupRequestDTO에 유효성 검사 어노테이션을 추가하여 입력 데이터를 검증합니다.

public class SignupRequestDTO {
    @NotNull
    private String username;

    @NotNull
    private String password;

    // Getters and Setters
}

2. 요청 데이터가 올바르지 않을 경우의 처리

입력값이 잘못되었을 때 적절한 에러 메시지를 반환하기 위해 예외 처리를 추가합니다.

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?> handleValidationExceptions(MethodArgumentNotValidException ex) {
    Map<String, String> errors = new HashMap<>();
    ex.getBindingResult().getFieldErrors().forEach(error -> {
        errors.put(error.getField(), error.getDefaultMessage());
    });
    return ResponseEntity.badRequest().body(errors);
}

3. 폼 데이터 사용

만약 클라이언트가 JSON이 아닌 폼 데이터나 쿼리 파라미터를 사용해야 한다면 @RequestParam을 사용하여 요청 데이터를 처리할 수 있습니다.

@PostMapping("/signup")
public ResponseEntity<?> signup(@RequestParam String username, @RequestParam String password) {
    SignupRequestDTO requestDTO = new SignupRequestDTO(username, password);
    userService.signup(requestDTO);
    return ResponseEntity.ok(new ApiResponse("success", requestDTO));
}

결론

@RequestBody는 클라이언트 요청에서 JSON 데이터를 객체로 매핑할 때 반드시 필요합니다. 이 문제는 요청 데이터 형식과 컨트롤러 처리 방식의 불일치에서 발생한 것으로, @RequestBody를 추가하여 해결했습니다.

이번 디버깅 과정을 통해 요청 처리 방식과 데이터를 매핑하는 방법에 대한 이해를 깊게 할 수 있었습니다.

profile
Dreamer

0개의 댓글