Spring MVC 기능

zwon·2023년 8월 31일
0

Spring

목록 보기
8/12
post-thumbnail

스프링의 대표적인 컨트롤러는 @RequestMapping 컨트롤러이다.
앞서 Spring MVC 구조에서 살펴본 핸들러 매핑과 핸들러 어댑터에서 우선순위가 제일 높았던 것도 @RequestMapping과 관련된 것이었다.

그리고 @Controller도 @RequestMapping처럼 어노테이션 기반 컨트롤러이다.
@Controller안에 @Component가 있어 컴포넌트 스캔을 통해서 스프링 빈으로 등록된다.

RequestMappingHanderMapping은 스프링 빈 중에서 @RequestMapping뿐만 아니라 @Controller가 클래스단에 붙어 있는 경우 매핑 정보로 인식한다.
업로드중..
그래서 다음과 같이 작성하면 RequestMappingHanderMapping이 인식할 수 있다.

@Controller
pulibc class SpringController {
	...
}
@Component
@RequestMapping
pulibc class SpringController {
	...
}

그리고 메서드단에 @RequestMapping을 붙여주면 HTTP 메서드가 Get, Post, Put모두 허용해서 만약 메서드마다 다르게 실행하고싶으면 @GetMapping, @PostMapping 등으로 제약을 걸어줄 수 있다.
제약을 거는 것을 추천한다.


Spring MVC 기능

요청 매핑

요청이 왔을때 어떤 컨트롤러가 호출되는지 매핑하는 방법에는 여러가지가 있다.
대표적으로는 URL로 정할 수 있고, HTTP 메서드에 따라 정할 수 있고, @PathVariable을 사용할 수 있는 등 매우 다양한 방법이 있다.

@RequestMapping("/spring/hello")
public String springHello() {
	...
}

/spring/hello이 경로로 들어오면 springHello가 실행된다.

@GetMapping("/spring/hello")
public String springHello() {
	...
}

/spring/hello경로로 들어오지면 HTTP 메서드가 Get일 떄 실행된다.

@GetMapping("/spring/hello/{id}")
public String springHello(@PathVariable int age) {
	...
}

/spring/hello/{id}은 /spring/hello/1, /spring/hello/2이런식으로 요청이 오는 것이다.
이 뿐만 아니라 더 다양한 방식이 있지만 우선 이정도로만 정리하겠다.

클라이언트에서 서버로 데이터를 보낼 때 크게 3가지 방법이 있다.

1. Get - 쿼리 파라미터 방식

  • url에 ?쿼리 파라미터로 넘어오는 방식을 말한다.
  • ex) /spring/hello?username=spring&age=25
  • 검색, 페이징 등에서 주로 사용하는 방식

@RequestParam("값") 타입 변수명 -> 값과 변수명이 같으면 ("값") 생략가능하다.

 @ResponseBody
 @RequestMapping("/request-param")
  public String requestParam( @RequestParam String username, @RequestParam int age) {
    log.info("username={}, age={}", username, age);
    return "ok";
  }

2. Post - HTML Form

  • content-type:application/x-www-form-urlencoded
  • 메시지 바디에 쿼리 파라미터 형식으로 담김
  • 회원 가입, 수정 등에서 주로 사용

HTML Form도 requestParam으로 데이터 값을 가져올 수 있다.
회원가입을 예시로 requestParam으로 가져오고 회원 객체랑 매핑하는 것과
@ModelAttribute를 사용해서 자동화된 코드를 함께 보여주겠다

 @Data
 public class Member {
 	private String username;
    private int age;
 }
  @ResponseBody
  @RequestMapping("/model-attribute")
  public String modelAttribute(@RequestParam String username, @RequestParam int age){
    Member member = new Member();
    member.setUsername(username);
    member.setAge(age);

    return "ok";
  }

@ModelAttribute를 사용하면 자동화가 가능하다.

  @ResponseBody
  @RequestMapping("/model-attribute")
  public String modelAttribute(@ModelAttribute member member){
    log.info("username={}, age={}", member.getUsername(), member.getAge());
    return "ok";
  }

3. HTTP Message body에 데이터를 담아 전송

  • HTTP API에서 주로 사용.
  • 데이터 형식은 주로 JSON
  • Post, Put, Patch를 주로 사용한다.

여기서는 @RequestParam, @ModelAttribute를 사용할 수 없다.
inputStream, HttpEntity, @RequestBody 등을 사용할 수 있는데 여기서는 HttpEntity, @RequestBody에 대한 예시만 보여주겠다.

  1. Http message body에 String 데이터가 담겨있는 경우1
    HttpEntity는 HTTP 메시지 컨버터가 동작해서 자동으로 메시지를 가져와준다.아래의 코드 HttpEntity<String> httpEntity는 HTTP 메시지 컨버터가 HTTP Body에 있는 데이터를 String으로 변환해서 httpEntity에 담아줘서httpEntity.getBody()를 통해 값을 꺼낼 수 있다.
    httpEntity는 응답에도 사용할 수 있다.
  @PostMapping("/request-body-string-v3")
  public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) throws IOException {
    String messageBody = httpEntity.getBody();
    log.info("messageBody={}", messageBody);
    return new HttpEntity<>("ok");
  }
  1. Http message body에 String 데이터가 담겨있는 경우2
    @RequestBody는 편하다..
    @RequestBody는 HTTP Message Body에 담겨있는 값을 읽어서 넣어준 것이다.
  @ResponseBody
  @PostMapping("/request-body-string")
  public String requestBodyString(@RequestBody String messageBody) throws IOException {
  
    log.info("messageBody={}", messageBody);
    return "ok";
  1. Http message body에 JSON 데이터가 담겨있는 경우1
    HttpEntity<member>는 HTTP 메시지 컨버터가 자동으로 HTTP message body의 값을 member객체로 변환해준다.
 @ResponseBody
  @RequestMapping("/request-body-json")
  public String requestBodyJson(HttpEntity<member> messageBody){
    Member member = member.getBody();
    log.info("messageBody = {} ", messageBody);
    log.info("username={}, age={}", member.getUsername(), member.getAge());

    return "ok";
  }
  1. Http message body에 JSON 데이터가 담겨있는 경우2
    @RequestBody에 직접 정의한 객체 타입을 적으면 HTTP 메시지 컨버터가 HTTP message body의 값을 자동으로 변환해준다.
    HTTP 메시지 컨버터는 응답에도 적용된다. 객체가 HTTP 메시지 컨버터에 의해서 JSON으로 변환된다.
    주의 @RequestBody는 생략 불가능!
@ResponseBody
  @RequestMapping("/request-body-json")
  public HelloData requestBodyJson(@RequestBody Member member){

    log.info("messageBody = {} ", member);
    log.info("username={}, age={}", member.getUsername(), member.getAge());

    return member; 
}

서버에서 클라이언트로 응답을 보낼 때 크게 3가지 방법이 있다.

1. 정적 리소스

  • html, css, Js 등을 제공할 때는 정적 리소스를 사용한다.
  • 스프링 부트는 다음과 같은 디렉토리에 있는 정적 리소스를 제공한다.
    • /static
    • /public
    • /resources
    • /META-INF/resources

2. 뷰 템플릿 사용

  • 동적인 html을 제공할 때는 뷰 템플릿을 사용한다.
  • 뷰 템플릿을 거쳐서 HTML이 생성되어 동적으로 가능하다.
  • 뷰 템플리 경로
    • /templates

3. HTTP 메시지 사용

  • HTTP API를 제공하는 경우에는 HTTP 메시지 바디에 JSON과 같은 형식으로 데이터를 실어 보낸다.
  1. String 데이터를 HTTP 메시지 바디에 담는 경우
  @GetMapping("/response-body-string")
  public ResponseEntity<String> responseBody() {
    return new ResponseEntity<>("ok", HttpStatus.CREATED);
  }

@ResposeBody를 사용하면 다음과 같이 간단하게 표현 가능

  @ResponseBody
  @GetMapping("/response-body-string")
  public String responseBody() {
    return "ok";
  }
  1. JSON 데이터를 HTTP 메시지 바디에 담는 경우
  @ResponseBody
  @GetMapping("/response-body-json")
  public ResponseEntity<Member> responseBodyJson() {
    Member member = new Member();
    member.setUsername("userA");
    member.setAge(20);
    return new ResponseEntity<>(member, HttpStatus.OK);
  }

@ResponseBody를 사용하면 다음과 같이 작성할 수 있다.
@ResponseStatus를 사용해서 상태코드 변경도 가능하다.

 @ResponseStatus(HttpStatus.OK) 
  @ResponseBody
  @GetMapping("/response-body-json")
  public Member responseBodyJson() {
    Member member = new Member();
    member.setUsername("userA");
    member.setAge(20);
    return helloData;
  }

만약 @ResponseBody를 붙이기 귀찮으면 @RestController를 사용하면된다.
@RestController는 @ResponseBody + @Controller라고 생각하면 된다.
주로 @RestController는 Rest API를 설계할 때 사용하는 어노테이션이다.


HTTP 메시지 컨버터

HTTP 메시지 컨버터는 감을 잡았을 수도 있지만 요청과 응답의 HTTP Message Body의 형식을 변환해주는 역할을 한다고 생각하면 된다.
그래서 다양한 형식의 데이터를 주고받을 수 있는 것이다.

참고로 스프링 부트는 다양한 메시지 컨버터를 제공해준다.

HTTP 메시지 컨버터가 적용되는 경우

  • HTTP 요청 - @RequestBody, HttpEntity(RequestEntity)
  • HTTP 응답 - @ResponseBody, HttpEntity(ResponseEntity)

그러면 Spring MVC에서 HTTP 메시지 컨버터는 언제 사용이 될까?
핸들러 어댑터를 통해서 실제 컨트롤러가 실행이 된다고 Spring MVC 구조 포스팅에서 정리를 했다.
그래서 메시지 컨버터는 핸들러 어댑터와 관련이 있는데 자세히 알아보자.

어노테이션 기반의 컨트롤러인 @RequestMapping을 처리하는 어댑터 RequestMappingHanderAdapter에 대해 알아보겠다.

디스패처 서블릿에서 이러쿵 저러쿵 해서 RequestMappingHanderAdapter를 가져왔다고 치자.

그러면 어댑터가 컨트롤러를 호출할텐데, 우리가 여태 작성했던 코드들을 생각해보면 컨트롤러를 실행할 때 매우 다양한 파라미터를 사용할 수 있다.
A컨트롤러는 InputStream, B컨트롤러는 @RequestParam, C컨트롤러는 @ModelAttribute, @RequestBody 등을 파라미터로 받았다.

그렇다면 무언가가 파라미터의 타입대로 파라미터를 생성 후 던져줘야하는데 그게 바로 Argument Resolver이다.
Argument Resolver덕분에 매우 다양한 파라미터를 사용할 수 있다. 즉 파라미터를 유연하게 처리할 수 있는 것이다.

RequestMappingHanderAdapter는 Argument Resolver호출해서 파라미터를 모두 생성하고 컨트롤러를 호출하면서 값을 넘겨주는 것이다.

스프링은 다양한 Argument Resolver를 제공해준다.

요청 파라미터는 Argument Resolver로 해결하고, 응답 데이터의 타입은 ReturnValueHandler가 해결을 해주는 것이다.

그럼 도대체 HTTP 메시지 컨버터는 어디있는가?
Argument Resolver가 사용하는게 바로 HTTP 메시지 컨버터이다.
ReturnValueHandler역시 HTTP 메시지 컨버터를 사용한다.

예시와 함께 얘기하면,
@RequestBody타입의 파라미터 컨트롤러가 호출되기전에 @RequestBody의 argument를 처리하는 Argument Resolver가 HTTP 메시지 컨버터를 사용해서 필요한 객체를 생성하고 생성한 파라미터를 컨트롤러로 넘겨주는 것이다.


혹시 잘못알고있는 부분이 있다면 알려주시면 감사하겠습니다.
해당 포스팅은 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술의 내용을 공부하고 정리했습니다.

profile
Backend 관련 지식을 정리하는 Back과사전

0개의 댓글