자바 백엔드 입문 29편 — RequestParam PathVariable RequestBody

2026-05-16자바 백엔드 입문

자바 백엔드 입문 29편. HTTP 요청 데이터를 자바 메서드 매개변수로 받는 세 가지 표준 어노테이션 @RequestParam·@PathVariable·@RequestBody를 택배 송장 비유로 풀어쓴 학습 노트.

📚 자바 백엔드 입문 · 29편 — RequestParam PathVariable RequestBody

이 글은 자바 백엔드 입문 시리즈 59편 중 29편이에요. 28편에서 "자바 객체를 어떻게 JSON 응답으로 보내는가" 를 풀었다면, 이번 29편은 그 반대 방향 — "클라이언트가 보낸 데이터를 어떻게 자바 매개변수로 받는가" 를 다룹니다. 세 가지 표준 어노테이션이 등장해요.

요청 데이터 추출이 헷갈리는 이유

HTTP 요청 안에는 데이터가 들어올 자리가 여러 군데예요. URL 경로 · 쿼리 스트링 · 헤더 · 본문. 각각 어떤 어노테이션으로 받아야 하는지가 처음에 안 잡혀요.

이 글에서는 택배 송장 비유로 풀어요. URL 경로 = 송장의 "수신 주소", 쿼리 스트링 = 송장 "옵션 메모", 본문 = "택배 박스 안 내용물". 끝까지 따라오시면 세 어노테이션 + 보너스 둘 = 총 5종이 한 그림에 들어와요.

한눈에 보는 5종 어노테이션

어노테이션 추출 위치 사용 예
@PathVariable URL 경로 안 {...} /orders/{id}
@RequestParam 쿼리 스트링 또는 form 파라미터 /orders?status=active
@RequestBody HTTP 요청 본문 (JSON 등) POST/PUT 본문
@RequestHeader HTTP 헤더 Authorization: Bearer ...
@CookieValue 쿠키 Cookie: SESSION=...

앞 세 개가 90%. 뒤 두 개는 가끔 만나는 보조.

@PathVariable — URL 경로 안의 변수

@PathVariable 은 URL 경로 안 {...} 자리에서 값을 추출.

@GetMapping("/orders/{id}")
public Order getOrder(@PathVariable Long id) {
    return orderService.findById(id);
}
// GET /orders/123  →  id = 123L

매개변수 이름이 URL 변수 이름과 같으면 자동 매핑. 다르면 명시:

@GetMapping("/orders/{id}")
public Order get(@PathVariable("id") Long orderId) { ... }

여러 개 동시에:

@GetMapping("/orders/{orderId}/items/{itemId}")
public Item getItem(@PathVariable Long orderId, @PathVariable Long itemId) { ... }

타입 변환은 Spring이 자동. Long 으로 받으면 문자열 "123"123L 로 자동 변환. 변환 실패 시 400 Bad Request 자동.

언제 쓰나 — REST API의 "리소스 식별자". /orders/{id}·/users/{userId}/posts/{postId} 같이 "무엇을 가리키는지" 가 URL 자체에 박힐 때.

@RequestParam — 쿼리 스트링 또는 form

@RequestParam 은 쿼리 스트링(?key=value) 또는 application/x-www-form-urlencoded form 본문에서 추출.

@GetMapping("/orders")
public List<Order> list(@RequestParam(required = false) String status,
                         @RequestParam(defaultValue = "1") int page,
                         @RequestParam(defaultValue = "20") int size) {
    return orderService.findAll(status, page, size);
}
// GET /orders?status=active&page=2&size=10  →  status="active", page=2, size=10
// GET /orders                                →  status=null, page=1, size=20

3가지 옵션:

옵션 의미
value (또는 name) 쿼리 키 이름 (생략 시 매개변수 이름)
required 필수 여부. 기본 true (없으면 400)
defaultValue 없을 때 기본값

required = false + defaultValue 둘 중 하나가 거의 표준 — "클라이언트가 안 보내도 동작" 보장.

리스트로도 받기:

@GetMapping("/products")
public List<Product> byCategories(@RequestParam List<String> categories) { ... }
// GET /products?categories=electronics&categories=clothing
//   →  categories = ["electronics", "clothing"]

언제 쓰나"검색·필터·정렬·페이징" 같이 "선택적이거나 다양하게 조합되는" 파라미터.

@RequestBody — JSON 본문

@RequestBody 는 HTTP 요청 본문 전체를 자바 객체로 변환. JSON·XML 등 Jackson(또는 다른 컨버터)이 알아서 매핑.

@PostMapping("/orders")
public Order create(@RequestBody OrderRequest req) {
    return orderService.create(req);
}

// 클라이언트 요청:
// POST /orders
// Content-Type: application/json
// {"productId": 1, "quantity": 2}

// req 객체에 자동 매핑됨

DTO 클래스 한 개 정의 필요.

@Getter @Setter         // Jackson은 setter 또는 필드 접근 둘 다 OK
public class OrderRequest {
    private Long productId;
    private int quantity;
}

여기서 시험 함정 자주 나와요. "@RequestBody 받는 DTO에 기본 생성자 필요한가?" — 답은 필요해요. Jackson이 객체를 만들 때 기본 생성자 호출. @AllArgsConstructor 만 박고 기본 생성자 없으면 — 데시리얼라이즈 실패. Lombok @NoArgsConstructor 도 같이 박거나, 별도 @JsonCreator 생성자 명시.

언제 쓰나 — POST·PUT·PATCH 요청에서 JSON 본문을 받을 때. REST API의 99% 패턴.

@RequestParam vs @RequestBody — 자주 헷갈리는 비교

이 둘이 가장 혼동되는 부분.

@RequestParam @RequestBody
출처 쿼리 스트링 / form HTTP 본문
Content-Type application/x-www-form-urlencoded application/json
적합한 HTTP 메서드 주로 GET 주로 POST·PUT·PATCH
매핑 대상 단일 값 또는 리스트 객체 전체
GET 본문 거의 안 씀 안 씀 (REST 비표준)

GET 요청에는 본문이 거의 없어요. 모든 데이터는 쿼리 스트링으로. @RequestBody 와 GET을 같이 쓰는 패턴은 거의 만나지 않습니다.

@RequestHeader·@CookieValue — 보조 둘

가끔 만나는 두 어노테이션.

@GetMapping("/profile")
public User profile(@RequestHeader("Authorization") String token,
                    @CookieValue(value = "SESSION", required = false) String session) {
    return userService.findByToken(token);
}
  • @RequestHeader — HTTP 헤더 한 줄 추출
  • @CookieValue — 쿠키 한 개 추출

@RequestHeader 는 Authorization 헤더에서 JWT 토큰 추출에 자주 등장. 단 실무에서는 Spring Security가 자동 처리하니까 직접 박는 일은 드물어요.

@ModelAttribute — form 데이터를 객체로

HTML form 제출(application/x-www-form-urlencoded)에서 form 필드들을 한 객체로 묶을 때 @ModelAttribute.

@PostMapping("/users")
public String register(@ModelAttribute UserRegisterForm form) {
    userService.register(form);
    return "redirect:/users";
}

@Getter @Setter
public class UserRegisterForm {
    private String username;
    private String email;
    private String password;
}

@RequestBody 와 비슷해 보이지만 — Content-Type이 form-urlencoded일 때 동작. JSON은 @RequestBody, 전통 HTML 폼은 @ModelAttribute. REST API 시대엔 거의 안 쓰지만 "이런 게 있다" 만 알아두세요.

@Valid — 자동 검증

@RequestBody 와 가장 자주 짝짓는 어노테이션 @Valid.

@PostMapping("/orders")
public Order create(@RequestBody @Valid OrderRequest req) {
    return orderService.create(req);
}

@Getter @Setter
@NoArgsConstructor
public class OrderRequest {
    @NotNull
    private Long productId;

    @Min(1)
    @Max(100)
    private int quantity;

    @Email
    private String customerEmail;
}

@Valid 한 줄만 박으면 — Spring이 자동으로 DTO 필드의 @NotNull·@Min·@Email 같은 검증 어노테이션을 실행. 검증 실패 시 MethodArgumentNotValidException 자동 발생, 400 Bad Request 응답.

24·25편 Validation에서 깊이 다룰 내용. 미리 그림만 잡아두세요.

실전 예 — REST API 전체 메서드 시그니처

지금까지 다룬 어노테이션을 합쳐 "주문 검색 + 상세 + 생성 + 수정" 컨트롤러.

@RestController
@RequestMapping("/api/orders")
@RequiredArgsConstructor
public class OrderController {

    // 검색 — 쿼리 스트링
    @GetMapping
    public List<OrderResponse> search(
            @RequestParam(required = false) String status,
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "20") int size) { ... }

    // 상세 — Path Variable
    @GetMapping("/{id}")
    public OrderResponse detail(@PathVariable Long id) { ... }

    // 생성 — JSON 본문 + 검증
    @PostMapping
    public OrderResponse create(
            @RequestBody @Valid OrderCreateRequest req,
            @RequestHeader("X-Tenant-Id") String tenantId) { ... }

    // 수정 — Path + JSON 본문
    @PatchMapping("/{id}")
    public OrderResponse update(
            @PathVariable Long id,
            @RequestBody @Valid OrderUpdateRequest req) { ... }
}

이 네 메서드가 REST API 컨트롤러의 표준 골격이에요. 22편의 모든 어노테이션이 자연스럽게 합쳐진 형태.

💡 어떤 위치에 무엇을 박을까 빠른 룰

리소스 식별(이것 = id) → @PathVariable. 옵션·필터(어떤 조건) → @RequestParam. 생성·수정 데이터(무엇을 만들/바꿀) → @RequestBody. 인증·메타(누가) → @RequestHeader.

한 줄 정리 — @PathVariable (URL 변수), @RequestParam (쿼리·form), @RequestBody (JSON 본문), @RequestHeader (헤더). 5개 + @Valid 검증 한 줄로 REST API의 모든 입력 처리.

시험 직전 한 번 더 — 요청 데이터 추출 입문자가 매번 헷갈리는 것

  • 요청 데이터 추출 5종 = @PathVariable / @RequestParam / @RequestBody / @RequestHeader / @CookieValue
  • @PathVariable = URL 경로 안 {...} (리소스 식별)
  • 매개변수 이름이 URL 변수와 같으면 자동 매핑
  • 다르면 @PathVariable("id") Long orderId 명시
  • @RequestParam = 쿼리 스트링 또는 form (선택적 옵션)
  • 옵션 3가지 = value / required / defaultValue
  • required = false + defaultValue 패턴이 표준
  • 리스트로 받기 = @RequestParam List<String> tags
  • @RequestBody = HTTP 본문 (JSON·XML)
  • POST·PUT·PATCH 요청의 99% 패턴
  • DTO에 기본 생성자 필수 — Jackson 데시리얼라이즈
  • Lombok @NoArgsConstructor 박아두기
  • @RequestHeader = HTTP 헤더 추출
  • @CookieValue = 쿠키 추출
  • @ModelAttribute = form-urlencoded form 데이터를 객체로 (REST 시대엔 거의 X)
  • @Valid 한 줄 = DTO 필드 검증 자동 실행
  • 검증 실패 시 MethodArgumentNotValidException 자동 → 400
  • GET 요청에 @RequestBody 거의 안 씀
  • 타입 변환 자동 = String "123"Long 123L, 실패 시 400
  • "리소스 식별 → PathVariable / 옵션 → RequestParam / 데이터 → RequestBody"

시리즈 다른 편 (앞뒤 글 모음)

이전 글:

다음 글:

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!