[Spring] 파라미터 바인딩, 응답 방식

이지연·2026년 1월 15일

개요

※ 먼저 이전글을 반드시 읽고와야한다.
[Spring] 스프링 요청 처리 : DispatcherServlet부터 컨트롤러 메서드 실행까지

이전 글에서 DispatcherServlet이 URL + HTTP 메서드를 분석해서 알맞은 컨트롤러 메서드를 찾는 과정까지 정리했다.
이번 글에서는 그 다음 단계인 “요청에 들어있는 값(파라미터/바디)을 자바 메서드 파라미터로 변환해서 꽂아주는 과정(파라미터 바인딩)”과, 컨트롤러 반환값이 어떤 방식으로 text/json/html 응답으로 나뉘는지 실습 코드 기준으로 정리한다.


1) 응답 방식: text/json/html이 갈리는 기준

스프링 MVC에서 응답이 갈리는 가장 큰 기준은 @ResponseBody의 유무다.
@ResponseBody가 있으면 반환값을 “뷰 이름”이 아니라 “HTTP 응답 바디 데이터”로 처리하고, 없으면 반환값을 “뷰 이름”으로 해석해 템플릿을 렌더링한다.

1-1) text 응답: @ResponseBody + String

아래 코드는 문자열을 그대로 HTTP body에 내려준다

@GetMapping("")
@ResponseBody
public String textDataReturn() {
    return "홍길동";
}
  • 요청: GET /member
  • 응답: "홍길동"이 text 응답으로 내려감

1-2) JSON 응답: @ResponseBody + Object

객체를 반환하면 스프링이 JSON으로 직렬화해서 내려준다(보통 REST API에서 가장 흔한 형태).

@GetMapping("/json")
@ResponseBody
public Member jsonDataReturn() {
    Member m1 = new Member("이지연", "jiyean@naver.com");
    return m1;
}
  • 요청: GET /member/json
  • 응답: Member 객체가 JSON으로 직렬화되어 내려감

보충: ObjectMapper로 JSON 문자열을 직접 만들어 String으로 반환하면, 구조는 JSON처럼 보여도 “응답 타입 관점에서는 문자열(text)”로 다뤄질 수 있으니 의도를 명확히 하는 게 좋다

1-3) HTML(View) 응답: @ResponseBody 없음 + String

@ResponseBody가 없고 String을 반환하면, 해당 문자열은 “데이터”가 아니라 “뷰 이름”으로 해석된다.

1-3-1) 정적 HTML(View) 응답

@GetMapping("/html")
public String htmlReturn() {
    return "simple_html";
}

1-3-2) SSR 동적 HTML(View) 화면: Model로 값 전달하기

/member/html/dynamic은 서버가 화면과 데이터를 함께 내려주는 SSR(Server Side Rendering) 방식이다.
컨트롤러에서 Model에 값을 담아두면, 템플릿 엔진(Thymeleaf)이 ${name} 같은 표현식 자리에 값을 주입해 최종 HTML을 만들어 내려준다.

@GetMapping("/html/dynamic")
public String htmlForDynamicReturn(Model model) {
    model.addAttribute("name", "김아무개");
    model.addAttribute("email", "kim@naver.com");
    return "dynamic_html";
}

2) GET 요청: URL에서 값 꺼내기

GET 요청에서 서버로 값을 보내는 대표 방법은 2가지다.

  • Path Variable: /member/path/1 형태
  • Query Parameter: /member/param1?name=홍길동 형태

2-1) Path Variable: @PathVariable

URL 경로에 포함된 값을 꺼내서 파라미터에 바인딩한다

@GetMapping("/path/{id}")
@ResponseBody
public String path(@PathVariable Long id) {
    System.out.println(id);
    return "OK";
}
  • 요청: GET /member/path/1
  • 결과: idLong으로 바인딩되어 들어옴

2-2) Query Parameter: @RequestParam

쿼리 스트링의 key=value 값을 꺼내서 바인딩한다.

2-2-1) 단일 파라미터 받기

@GetMapping("/param1")
@ResponseBody
public String param1(@RequestParam(value = "name") String nameValue) {
    System.out.println("Member name === " + nameValue);
    return "OK";
}
  • 요청: GET /member/param1?name=홍길동
  • 결과: name 값이 nameValue로 들어옴

2-2-2) 여러개의 파라미터 받기

파라미터가 늘어나면 아래처럼 여러 개를 받을 수도 있다

@GetMapping("/param2")
@ResponseBody
public String param2(@RequestParam(value = "name") String nameValue,
                     @RequestParam(value = "email") String emailValue) {
    System.out.println("Member name === " + nameValue);
    System.out.println("Member email === " + emailValue);
    return "OK";
}
  • 요청: GET /member/param1?name=홍길동&email=hong@naver.com

  • 결과: name 값은 nameValue로, email 값은 emailValue로 들어옴

2-2-3) 파라미터가 많아지면: @ModelAttribute로 객체 바인딩

@ModelAttribute는 요청 파라미터 형태로 들어온 데이터들을 모아서 객체로 자동 매핑(데이터 바인딩)해준다
name=...&email=...처럼 들어온 값들을 Member 객체 필드에 맞춰 채워서 메서드 파라미터로 넣어준다.

@GetMapping("/param3")
@ResponseBody
public String param3(@ModelAttribute Member member) {
    System.out.println("Member === " + member);
    return "OK";
}
  • 요청: GET /member/param3?name=홍길동&email=hong@naver.com

  • 결과: member.getName(), member.getEmail() 형태로 바로 사용 가능

보충: @ModelAttribute는 생략 가능한 경우도 많아서, 코드를 간단히 쓰고 싶을 때는 생략 문법을 쓰기도 한다(팀 컨벤션에 맞추는 게 중요).


3) POST 요청: Body에서 값 꺼내기

POST는 보통 데이터가 body로 들어오며, 대표적으로 아래 3가지 형태를 자주 다룬다.

  • application/x-www-form-urlencoded
  • multipart/form-data
  • application/json

3-1) url-encoded: @ModelAttribute로 바인딩

url-encoded는 구조가 쿼리 파라미터와 유사해서 @ModelAttribute로도 자연스럽게 객체 바인딩이 가능하다

@PostMapping("/url-encoded")
@ResponseBody
public String urlEncoded(@ModelAttribute Member member) {
    System.out.println(member);
    return "OK";
}
  • 데이터의 형식 : name=홍길동&email=hong@naver.com
  • POSTMAN 요청 완료 화면:
  • 콘솔 출력:

3-2) multipart/form-data: MultipartFile 받기

파일 업로드가 포함되면 보통 multipart/form-data를 사용하고, 스프링에서는 MultipartFile로 파일을 받을 수 있다.

3-2-1) 단일 파일 처리

@PostMapping("/multipart-formdata")
@ResponseBody
public String multipartFormData(@ModelAttribute Member member,
                                @RequestParam(value = "profileImage") MultipartFile profileImg) {
    System.out.println("Member === " + member);
    System.out.println("File name === " + profileImg.getOriginalFilename());
    return "OK";
}
  • 데이터의 형식 : namename=홍길동&email=hong@naver.com&profileImage=(binary file)
  • POSTMAN 요청 완료 화면:
  • 콘솔 출력:

3-2-2) 다중 파일 처리

여러 파일도 리스트로 받을 수 있다.

@PostMapping("/multipart-formdata-plura")
@ResponseBody
public String pluraMultipartFormData(@ModelAttribute Member member,
                                     @RequestParam(value = "profileImages") List<MultipartFile> profileImgList) {
    System.out.println("Member === " + member);
    System.out.println("List size === " + profileImgList.size());
    return "OK";
}
  • 데이터의 형식 : name=홍길동&email=hong@naver.com&profileImages=(binary file1)&profileImages=(binary file2)&profileImages=(binary file3)
  • POSTMAN 요청 완료 화면:
  • 콘솔 출력:

3-3) JSON: @RequestBody로 바인딩

JSON 요청은 @RequestBody로 받는 것이 기본 패턴이다.

3-3-1) 단일 JSON 데이터 처리

@PostMapping("/json")
@ResponseBody
public String json(@RequestBody Member member) {
    System.out.println("Member === " + member);
    return "OK";
}
  • 데이터 형식 : {"name": "홍길동", "email": "hong@naver.com"}
  • POSTMAN 요청 완료 화면:
  • 콘솔 출력:

3-3-2) JSON 배열 데이터 처리

JSON 배열도 리스트로 그대로 받을 수 있다.

@PostMapping("/json-list")
@ResponseBody
public String jsonList(@RequestBody List<Member> memberList) {
    System.out.println("Member === " + memberList);
    return "OK";
}
  • 데이터 형식 : [{"name": "홍길동1", "email": "hong1@naver.com"}, {"name": "홍길동2", "email": "hong2@naver.com"}, {"name": "홍길동3", "email": "hong3@naver.com"}]
  • POSTMAN 요청 완료 화면:
  • 콘솔 출력:

3-3-3) 중첩된 JSON 데이터 처리

중첩 JSON도 객체 구조만 맞으면 그대로 바인딩된다(예: Student 안에 scores 리스트).

@PostMapping("/json-nested")
@ResponseBody
public String jsonNested(@RequestBody Student student) {
    System.out.println("Member === " + student);
    return "OK";
}
  • 데이터 형식 : {"name":"홍길동", "email":"hong1@naver.com", "scores":[{"subject":"math", "point":100}, {"subject":"english", "point":90}, {"subject":"korean", "point":100}]}
  • POSTMAN 요청 완료 화면:
  • 콘솔 출력:

3-3-4) JSON + 파일이 합쳐진 데이터 처리 : @RequestPart

“JSON 데이터 + 파일”을 한 번에 보내야 하면 보통 요청은 multipart/form-data가 되고, 이때 JSON 파트와 파일 파트를 분리해서 받기 위해 @RequestPart를 사용한다.
즉 multipart 내부의 각 part를 (JSON은 객체로 역직렬화하고, 파일은 MultipartFile로) 함께 처리하는 방식이다.

@PostMapping("/json-file")
@ResponseBody
public String jsonWithFile(@RequestPart("member") Member member,
                           @RequestPart("profileImg") MultipartFile profileImg) {
    System.out.println(member);
    System.out.println(profileImg.getOriginalFilename());
    return "OK";
}
  • 데이터 형식: member={...JSON...}`&`profileImg=(바이너리)
  • POSTMAN 요청시 주의 사항 및 요청 완료 화면:
  • 콘솔 출력:
profile
Eazy하게

0개의 댓글