[Spring] 18. @RequestParam이란?

[Spring] 18. @RequestParam이란?

이전 글에서는 @GetMapping@PostMapping에 대해 살펴봤습니다. @GetMapping은 주로 화면 조회나 데이터 조회에 사용하고, @PostMapping은 주로 폼 전송이나 데이터 처리에 사용합니다.

이번 글에서는 사용자가 보낸 요청 값을 Controller에서 받는 방법인 @RequestParam에 대해 정리하겠습니다. 게시글 상세 번호, 검색어, 페이지 번호처럼 URL이나 폼을 통해 전달되는 값을 받을 때 자주 사용합니다.

핵심은 간단합니다. @RequestParam은 요청 파라미터 값을 Controller 메서드의 파라미터로 받아올 때 사용하는 어노테이션입니다.


1. 요청 파라미터란?

요청 파라미터는 사용자가 서버에 함께 보내는 값입니다. 예를 들어 게시글 상세 화면에 들어갈 때 게시글 번호를 함께 보낼 수 있습니다.

/board/detail?boardNo=10

위 주소에서 boardNo=10이 요청 파라미터입니다.

요청 URL /board/detail?boardNo=10
파라미터 이름 boardNo
파라미터 값 10

검색 기능에서도 요청 파라미터를 사용할 수 있습니다.

/board/search?keyword=spring
/board/list?page=2
/board/list?keyword=spring&page=3

이런 값들을 Controller 메서드에서 받기 위해 @RequestParam을 사용할 수 있습니다.


2. @RequestParam 기본 사용법

@RequestParam은 요청 파라미터를 Controller 메서드의 파라미터로 연결합니다.

@Controller
@RequestMapping("/board")
public class BoardController {

    @GetMapping("/detail")
    public String detail(@RequestParam("boardNo") Long boardNo) {
        System.out.println("게시글 번호: " + boardNo);
        return "board/detail";
    }
}

사용자가 다음 주소로 요청하면,

GET /board/detail?boardNo=10

Spring은 boardNo 파라미터 값을 찾아 Controller 메서드의 boardNo 변수에 넣어줍니다.

요청 파라미터 boardNo=10
 → @RequestParam("boardNo")
 → Long boardNo에 10 전달

3. 파라미터 이름이 같으면 생략 가능

요청 파라미터 이름과 메서드 파라미터 이름이 같다면 @RequestParam의 이름을 생략할 수 있습니다.

@GetMapping("/detail")
public String detail(@RequestParam Long boardNo) {
    System.out.println("게시글 번호: " + boardNo);
    return "board/detail";
}

위 코드는 요청 파라미터 이름이 boardNo이고, 메서드 파라미터 이름도 boardNo일 때 사용할 수 있습니다.

/board/detail?boardNo=10
 → Long boardNo

더 단순하게는 @RequestParam 자체를 생략해도 동작하는 경우가 있습니다.

@GetMapping("/detail")
public String detail(Long boardNo) {
    return "board/detail";
}

하지만 처음 공부할 때는 값이 어디서 들어오는지 명확하게 보이도록 @RequestParam을 붙여서 이해하는 것이 좋습니다.


4. 검색어 받기

검색 기능에서는 사용자가 입력한 검색어를 요청 파라미터로 받을 수 있습니다.

GET /board/search?keyword=spring

Controller에서는 다음처럼 받을 수 있습니다.

@GetMapping("/search")
public String search(@RequestParam("keyword") String keyword) {
    System.out.println("검색어: " + keyword);
    return "board/search";
}

요청 흐름은 다음과 같습니다.

/board/search?keyword=spring
 → keyword 파라미터 값 확인
 → String keyword에 "spring" 전달
 → 검색 처리

검색어는 화면에서 입력받아 GET 방식으로 보내는 경우가 많습니다. 사용자가 검색 결과 주소를 복사하거나 다시 접근할 수 있기 때문입니다.


5. 여러 파라미터 받기

요청 파라미터는 여러 개를 함께 보낼 수 있습니다.

/board/list?keyword=spring&page=2&size=10

Controller에서는 각각의 값을 따로 받을 수 있습니다.

@GetMapping("/list")
public String list(
        @RequestParam("keyword") String keyword,
        @RequestParam("page") int page,
        @RequestParam("size") int size
) {
    System.out.println("검색어: " + keyword);
    System.out.println("페이지: " + page);
    System.out.println("개수: " + size);
    return "board/list";
}

이 경우 요청 파라미터와 메서드 파라미터가 다음처럼 연결됩니다.

요청 파라미터 Controller 파라미터
keyword String keyword spring
page int page 2
size int size 10

6. required 속성

@RequestParam은 기본적으로 해당 파라미터가 반드시 있어야 한다고 봅니다. 만약 요청에 파라미터가 없으면 오류가 발생할 수 있습니다.

@GetMapping("/search")
public String search(@RequestParam("keyword") String keyword) {
    return "board/search";
}

위 코드에서 keyword는 기본적으로 필수값입니다. 그런데 사용자가 다음처럼 요청하면 문제가 생길 수 있습니다.

GET /board/search

검색어가 없어도 허용하고 싶다면 required = false를 사용할 수 있습니다.

@GetMapping("/search")
public String search(
        @RequestParam(value = "keyword", required = false) String keyword
) {
    System.out.println("검색어: " + keyword);
    return "board/search";
}

이제 keyword 파라미터가 없어도 요청을 처리할 수 있습니다. 파라미터가 없으면 keyword에는 null이 들어갈 수 있습니다.


7. defaultValue 속성

파라미터가 없을 때 기본값을 넣고 싶다면 defaultValue를 사용할 수 있습니다.

@GetMapping("/list")
public String list(
        @RequestParam(value = "page", defaultValue = "1") int page
) {
    System.out.println("현재 페이지: " + page);
    return "board/list";
}

사용자가 페이지 값을 보내지 않으면 기본값으로 1이 들어갑니다.

GET /board/list
 → page 값 없음
 → 기본값 1 사용

GET /board/list?page=3
 → page 값 있음
 → 3 사용

페이징 기능에서는 defaultValue를 자주 사용합니다. 첫 페이지를 기본값으로 두면 사용자가 별도 페이지 번호 없이 목록에 접근해도 자연스럽게 1페이지를 보여줄 수 있습니다.


8. 검색과 페이징 예시

게시판 목록에서는 검색어와 페이지 번호를 함께 받을 수 있습니다.

/board/list?keyword=spring&page=2

Controller 예시는 다음과 같습니다.

@Controller
@RequestMapping("/board")
public class BoardController {

    private final BoardService boardService;

    public BoardController(BoardService boardService) {
        this.boardService = boardService;
    }

    @GetMapping("/list")
    public String list(
            @RequestParam(value = "keyword", required = false) String keyword,
            @RequestParam(value = "page", defaultValue = "1") int page,
            Model model
    ) {
        List<BoardDto> boardList = boardService.findBoardList(keyword, page);
        model.addAttribute("boardList", boardList);
        model.addAttribute("keyword", keyword);
        model.addAttribute("page", page);
        return "board/list";
    }
}

이 구조에서는 검색어가 없어도 목록을 볼 수 있고, 페이지 값이 없으면 기본값 1을 사용합니다.

keyword
 → 없을 수도 있음

page
 → 없으면 1 사용

목록, 검색, 페이징 기능에서는 이런 식으로 @RequestParam을 활용하는 경우가 많습니다.


9. int와 Integer 사용 차이

요청 파라미터를 숫자로 받을 때 intInteger의 차이도 조심해야 합니다.

@GetMapping("/list")
public String list(@RequestParam(required = false) int page) {
    return "board/list";
}

위 코드는 문제가 될 수 있습니다. required = false로 설정했는데, 요청에 page 값이 없으면 int에는 null을 넣을 수 없기 때문입니다.

이럴 때는 기본값을 주거나, Integer를 사용할 수 있습니다.

@GetMapping("/list")
public String list(
        @RequestParam(value = "page", defaultValue = "1") int page
) {
    return "board/list";
}

또는 다음처럼 받을 수 있습니다.

@GetMapping("/list")
public String list(
        @RequestParam(value = "page", required = false) Integer page
) {
    if (page == null) {
        page = 1;
    }

    return "board/list";
}

페이징에서는 보통 defaultValue를 사용해 기본 페이지 번호를 정하는 방식이 깔끔합니다.


10. @RequestParam Map 사용

요청 파라미터가 많거나 동적으로 받아야 할 때는 Map으로 받을 수도 있습니다.

@GetMapping("/search")
public String search(@RequestParam Map<String, String> params) {
    System.out.println(params.get("keyword"));
    System.out.println(params.get("category"));
    return "board/search";
}

요청이 다음과 같다면,

/board/search?keyword=spring&category=title

Map에는 다음과 같은 값이 들어갈 수 있습니다.

keyword → spring
category → title

다만 명확한 파라미터가 정해져 있다면 각각의 파라미터를 명시적으로 받는 방식이 더 읽기 좋습니다. Map은 파라미터 구조가 유동적일 때만 신중하게 사용하는 것이 좋습니다.


11. POST 폼 데이터도 받을 수 있을까?

@RequestParam은 GET 요청의 쿼리 문자열뿐만 아니라, POST 요청으로 전송된 폼 데이터도 받을 수 있습니다.

<form action="/member/login" method="post">
    <input type="text" name="memberId">
    <input type="password" name="memberPw">
    <button type="submit">로그인</button>
</form>
@PostMapping("/member/login")
public String login(
        @RequestParam("memberId") String memberId,
        @RequestParam("memberPw") String memberPw
) {
    return "redirect:/";
}

폼의 name 값과 @RequestParam 이름이 연결됩니다.

input name="memberId"
 → @RequestParam("memberId")

input name="memberPw"
 → @RequestParam("memberPw")

다만 입력값이 많아지면 @RequestParam으로 하나씩 받기보다 DTO로 받는 것이 더 깔끔합니다.

@PostMapping("/member/login")
public String login(MemberLoginDto memberLoginDto) {
    return "redirect:/";
}

즉, 단순한 값 한두 개는 @RequestParam으로 받아도 되고, 여러 입력값은 DTO로 묶는 것이 좋습니다.


12. @RequestParam과 DTO 구분

요청 값을 받을 때 @RequestParam을 쓸지 DTO를 쓸지 헷갈릴 수 있습니다. 기준은 간단합니다.

상황 추천 방식 예시
값이 1~2개 @RequestParam boardNo, page, keyword
폼 입력값이 많음 DTO 회원가입, 게시글 작성
검색 조건이 많음 SearchDto 검색어, 카테고리, 기간, 정렬

예를 들어 게시글 번호 하나만 필요하다면 @RequestParam이 간단합니다.

@GetMapping("/detail")
public String detail(@RequestParam Long boardNo) {
    return "board/detail";
}

반대로 게시글 작성처럼 제목, 내용, 카테고리, 첨부파일 정보 등 여러 값이 있다면 DTO로 받는 것이 좋습니다.

@PostMapping("/write")
public String write(BoardDto boardDto) {
    return "redirect:/board/list";
}

13. @RequestParam과 @PathVariable 차이

요청 값을 받는 방법에는 @RequestParam 외에도 @PathVariable이 있습니다. 둘은 URL에서 값을 받는다는 점은 비슷하지만, 값이 들어있는 위치가 다릅니다.

구분 @RequestParam @PathVariable
값 위치 쿼리 문자열 URL 경로 일부
예시 URL /board/detail?boardNo=10 /board/10
사용 예시 @RequestParam Long boardNo @PathVariable Long boardNo

이번 글에서는 @RequestParam만 먼저 이해하면 됩니다. @PathVariable은 다음 글에서 자세히 정리하겠습니다.


14. 자주 하는 실수

1) 파라미터 이름이 맞지 않는 경우

요청 파라미터 이름과 @RequestParam 이름이 다르면 값이 들어오지 않을 수 있습니다.

/board/detail?boardNo=10
@GetMapping("/detail")
public String detail(@RequestParam("no") Long boardNo) {
    return "board/detail";
}

요청에서는 boardNo로 보냈는데, Controller에서는 no를 찾고 있습니다. 이런 경우 이름을 맞춰야 합니다.

@GetMapping("/detail")
public String detail(@RequestParam("boardNo") Long boardNo) {
    return "board/detail";
}

2) 필수 파라미터가 없어서 오류가 나는 경우

@RequestParam은 기본적으로 필수값입니다. 없어도 되는 값이라면 required = falsedefaultValue를 사용해야 합니다.

@RequestParam(value = "keyword", required = false) String keyword
@RequestParam(value = "page", defaultValue = "1") int page

3) int에 null이 들어오는 경우

int는 null을 담을 수 없습니다. 파라미터가 없을 수 있다면 기본값을 주는 것이 안전합니다.

@RequestParam(value = "page", defaultValue = "1") int page

4) 폼 name과 DTO 필드명을 다르게 쓰는 경우

DTO로 받을 때는 HTML form의 name과 DTO 필드명이 맞아야 합니다.

<input type="text" name="title">
private String title;

이름이 맞아야 값이 자연스럽게 담깁니다.

5) 모든 값을 @RequestParam으로만 받으려는 경우

입력값이 많아지면 @RequestParam을 여러 개 나열하기보다 DTO로 묶는 것이 좋습니다.

@PostMapping("/write")
public String write(
        @RequestParam String title,
        @RequestParam String content,
        @RequestParam String category,
        @RequestParam String writer
) {
    return "redirect:/board/list";
}

이런 경우에는 다음처럼 DTO로 받는 편이 더 깔끔합니다.

@PostMapping("/write")
public String write(BoardDto boardDto) {
    return "redirect:/board/list";
}

정리

@RequestParam은 요청 파라미터 값을 Controller 메서드에서 받을 때 사용하는 어노테이션입니다. 게시글 번호, 검색어, 페이지 번호처럼 URL이나 폼을 통해 전달되는 값을 받을 수 있습니다.

@RequestParam 요청 파라미터 값을 메서드 파라미터로 받습니다.
required 파라미터 필수 여부를 설정합니다.
defaultValue 파라미터가 없을 때 사용할 기본값을 설정합니다.
DTO 입력값이 많을 때 여러 값을 하나의 객체로 받을 수 있습니다.

처음에는 다음 기준으로 기억하면 좋습니다.

값이 1~2개다
 → @RequestParam

입력값이 여러 개다
 → DTO

값이 없을 수도 있다
 → required = false 또는 defaultValue

페이지 번호처럼 기본값이 필요하다
 → defaultValue 사용

연습해보기

아래 요청을 Controller에서 받아보세요.

GET /board/list?keyword=spring&page=2
GET /board/detail?boardNo=10
GET /board/search?category=title&keyword=java

예시는 다음과 같습니다.

@GetMapping("/board/list")
public String list(
        @RequestParam(value = "keyword", required = false) String keyword,
        @RequestParam(value = "page", defaultValue = "1") int page
) {
    return "board/list";
}

다음 글에서는 URL 경로 자체에서 값을 받는 @PathVariable에 대해 정리하겠습니다. /board/detail?boardNo=10/board/10의 차이를 비교하면서 보면 이해하기 쉽습니다

.