[Spring] @PathVariable, @RequestParam, @RequestBody, @ModelAttribute

2022. 12. 7. 11:01Web/Spring

@PathVariable, @RequestParam, @RequestBody, @ModelAttribute

 

글을 남기고 생성할 수 있는 간단한 API를 구성하면서 해당 어노테이션들을 사용하게 되었다. 이때 사용했던 어노테이션의 특징부터 차이점까지 궁금하여 학습을 하였고 기록을 남기게 되었다. 

 

@PathVariable

 

URI 경로에 변수를 넣어서 해당 변수를 받아올 수 있다. 각 URI 경로에 변수를 넣어 메서드를 지정하여 호출을 하면 해당 변수가 템플릿 매개변수에서 메서드 매개변수로 사용할 수 있게 설정해주는 역할이다. 

 

 @PathVariable을 사용하는 Path Variable은 리소스를 식별해서 요청할 때 사용이 된다

 

매핑의 종류는 다음과 같다

 

  • 간단한 매핑

 

@PathVariable을 선언하고 해당 URI값과 받아오는 파라미터의 값을 동일하게 해주면 값이 메서드 파라미터 값으로 변환

@GetMapping("/api/{id}")
public List<Post> getSomeList(@PathVariable Long id) {
    return postService.getSomeList(id);
}

 

  • 경로 변수의 이름 지정
@GetMapping("/api/{id}")
public List<Post> getSomeList(@PathVariable("id") Long number) {
    return postService.getSomeList(number);
}

 

  • 단일 요청의 여러 경로 변수
@GetMapping("/api/{id}/{pw}")
public List<Post> getSomeList(@PathVariable("id") Long number, @PathVariable("pw") String password) {
    return postService.getSomeList(number, password);
}

위의 PathVariable의 경우 경로를 지정해서 값이 들어오는 경우이다. 

 

@RequestParam, @RequestBody

 

@RequestParam의 경우 URI 경로에 템플릿 변수를 추가적으로 지정하고 메서드 변수로 받아오는건 아니다.

RequestParam의 경우 쿼리스트링 형태로 변수를 받아온다.

 

쿼리 스트링 방식이 사용되는 경우는 검색, 필터링, 페이징 처리, 정렬의 형식에서 사용이 된다.

 

이제 RequestParam과 RequestBody의 차이는 바로 단일 파라미터인지 혹은 객체 파라미터인지라는 부분에서 갈라진다.

 

RequestParam의 경우 단일 파라미터를 받아와서 스프링 내에 존재하는 단일 객체(필드)와 1대 1 매핑을 시킨다.

RequestBody의 경우 객체 파라미터를 받아와서 스프링 내에 존재하는  객체와 객체 매핑을 한다.

 

 

[ @RequestParam("가져오는 데이터 이름") / 데이터의 타입 / 데이터를 담을 변수 ] 라는 형태를 가지고 있다.

@DeleteMapping("/api")
public String deletePost(@RequestParam("id") Long id, @RequestParam("password") String password) {
    postService.deletePost(id,password);  
    return String.valueOf(id) + "의 글이 삭제 완료되었습니다";
}

 

[ @RequestBody 데이터 타입 / 데이터를 담을 객체 ] 라는 형태로 이루어져 있다.

@PostMapping("/api")
public void postingSomething(@RequestBody PostRequestDtos postRequestDtos) {
    postService.createPosting(postRequestDtos);
}

json형태의 데이터를 post방식으로 보내는 상황

이 RequestBody의 경우 여러 가지 HTTP RequestBody를 자바 객체로 변환하면서 송수신할 때 데이터를 받아올 수 있도록 해주는 역할이다. 여러 가지의 데이터 타입이 들어올 수 있지만 주로 사용하는 데이터는 Json형태의 데이터이다. 이렇게 보내진 데이터는 아까 말한 바와 같이 자바 객체로 매핑이 된다. 

 

@ModelAttribute와 @RequestBody 차이점

 

@ModelAttribute의 경우에도 @ReqeustBody와 같이 클라이언트 측에서 보낸 데이터를 Java 오브젝트로 만들어주는 공통점이 존재한다.  그럼 무엇이 해당 둘의 차이점을 가져오는지 알아보자.


@RequestBody 주석

 

Annotation indicating a method parameter should be bound to the body of the web requestThe body of the request is passed through an HttpMessageConverter to resolve the method argument depending on the content type of the request. Optionally, automatic validation can be applied by annotating the argument with @Valid.
Supported for annotated handler methods.

 

- 클라이언트가 보내는 HTTP 요청 데이터를 HttpMessageConverter를 통해 타입에 맞는 객체로 변환

 

@ModelAttribute 주석

 

Annotation that binds a method parameter or method return value to a named model attribute, exposed to a web view. Supported for controller classes with @RequestMapping methods.

 

- 클라이언트가 보내는 HTTP 파라미터들을 특정 Java Object에 맵핑한다.

 


@RequestBody로 데이터를 받아오기 위해 작성한 DTO 클래스이다. 

@Getter
public class PostRequestDtos {
    private String name;
    private String password;
    private String text;
}

생각을 해보면 해당 객체에 값을 넣을 때 이상이 없고 잘 작동이 되는 것만 생각을 하였다.

근데 해당 클래스를 보면 따로 Setter, 생성자가 지정이 되어있지 않다. 그러면 어떤 방식으로 객체에 값을 넣어주는 것일까?

@PostMapping("/api")
public void postingSomething(@RequestBody PostRequestDtos postRequestDtos) {
    postService.createPosting(postRequestDtos);
}

 

답은 ObjectMapper에 존재하였다. ObjectMapper라는 클래스가 하나 존재한다. 해당 클래스는 Java Object ↔ Json의 변환을 쉽게 해주는 역할을 가진 클래스이다. MappingJackson2 HttpMessageConverter가 ObjectMapper를 부르고 해당 과정을 통해 역직렬화를 하여 객체 값을 받아온다.

 

다만 직렬화의 과정에는 무조건 기본 생성자가 필요하다. 해당 기본 생성자를 DTO로 만들어 생성을 한다. @Getter를 통해 Get메서드가 생성이 되면 ObjectMapper에서 필드명을 알아내서 해당 DTO에 데이터 바인딩을 진행한다.

 

즉 RequestBody의 경우 Get 메서드가 존재하면 해당 메서드를 바탕으로 역직렬화를 거쳐 DTO로 변환이 되기에 @Getter를 통해 Get 메서드만 존재해도 된다.

 


@ModelAttribute

 

ModelAttribute의 경우 Form 형식의 HTTP 요청 데이터를 인식하여 맵핑을 진행한다. 다만 RequestBody처럼 객체에 데이터 바인딩을 위해서는 생성자 혹은 Setter가 존재해야 한다.

 


현재는 해당 어노테이션이 어떤 방식으로 차이가 나는지, 한쪽은 Getter / 한쪽은 Setter만 있으면 된다는 것을 알았다.

 

RequestBody의 경우 ObjectMapper를 통해 해당 기본 생성자를 자동으로 생성을 하게 된다. 그 기본 생성자를 DTO로 만들어서 반환을 한다. 이때 Getter 메서드가 존재하면 이를 통해 필드명을 알아내게 된다.  그리고 그 필드명을 통해 데이터 바인딩을 진행하게 된다는 것을 알 수 있었다. 다만 이 생성자를 만들고 안에 DTO를 작업해주는 부분이 있는 것은 확실한데 아직 이해를 제대로 하지 못했기에 주기적으로 읽어보고 공부를 해서 내용을 추가하려고 한다.

 

또한 ModelAttribute의 경우에도 안에 이 결과를 도출해내는 과정이 존재하지만 아직 무엇이 무엇인지 확실하게 넘어가지 못하고 있다. 그렇기에 이 부분 또한 주기적으로 공부하면서 되짚어볼 생각이다.

 

 

출처

@RequestBody vs @ModelAttribute (techcourse.co.kr)

 

@RequestBody vs @ModelAttribute

1. @RequestBody와 @ModelAttribute Controller.java @RequestBody와 @ModelAttribute는 클라이언트 측에서 보낸 데이터를 Java…

tecoble.techcourse.co.kr

@RequestBody에 왜 기본 생성자는 필요하고, Setter는 필요 없을까? #2 (velog.io)

 

@RequestBody에 왜 기본 생성자는 필요하고, Setter는 필요 없을까? #2

이전 글에서는 어떻게 @RequestBody를 처리하는지를 알아보기 위한 과정을 설명했습니다. 이번 글에서는 @RequestBody를 바인딩하는 ObjectMapper에 대해 알아보고, 결론을 짓겠습니다. 참고로 아래 사진

velog.io