1. 요청 매핑

특정 url로 요청이 들어오면 스프링 mvc는 그 요청에 맞는 핸들러(컨트롤러)를 찾아 핸들러 어뎁터와 매핑한다.

그럼 url에 따른 요청 매핑은 어떻게 할까?

 

1) @RequestMapping을 사용한다.

  • @RequestMapping("/example-url")
  • @RequestMapping("/example-url", method = RequestMethod.GET) 
  • @GetMapping("example-url)
  • @RequestMapping(value = {"/example-url", "/example-url2"})

HTTP 메소드를 지정해줄 수 있다. 지정하지 않을 시 모든 메소드를 전부 매핑한다.

@GetMapping, @PostMapping 등등의 어노테이션을 사용하면, 편리하게 축약해서 사용할 수 있다.

배열을 만들어 여러개의 url을 매핑하는 것도 가능하다.

 

2) 경로 변수 사용 - @PathVariable

@GetMapping("/movies/{movieId}")
public String readMovie(@PathVariable("movieId") Long data){}

//변수명 같으면 생략 가능
@GetMapping("/movies/{movieId}")
public String readMovie(@PathVariable  Long movieId){}

restApi를 사용하면 "/movies/1" 등과 같이 url에 id와 같은 변수를 사용한다.

@PathVariable 을 사용하면 경로 변수를 컨트롤러 메소드 함수 인자로 편하게 받아올 수 있다.

변수명이 같으면 생략도 가능하다.

 

3) 특정 파라미터 매핑

GetMapping(value=”/users”, params=”mode=debug”)

파라미터에 특정 값 없으면 매핑이 안되도록 하는 방법이다.

 

4) 특정 헤더 매핑

GetMapping(headers = “..”)

특정 헤더 넣어야지만 매핑 되도록 가능하도록 하는 방법이다.

 

5) 미디어 타입 조건 매핑 (contentType, produces)

GetMapping(value=”/users”, consumes=”application/json”)

GetMapping(value=”/users”, produces=”application/json”)

 

요청 헤더의 content type과, accept를 기반으로도 매핑이 가능하다.

consumes = Content-Type 기반

produces = Accept 헤더 기반

 

2. HTTP요청 기본, 헤더 조회

어노테이션 기반 컨트롤러는 ReqeustMappingHandler가 처리한다.

ReqeustMappingHandler에서 헤더, 기본값을 꺼내는 방법은 파라미터로 다음과 같은 값을 넣어주면 된다.

  • HttpservletRequest request 
  • HttpServletResponse response
  • HttpMethod method
  • Locale locale
  • @RequestHeader("host")
  • @CookieValue(value="cookieName", required=false)

이외에도 여러가지 값들을 메소드 파라미터로 넣을 수 있고, 사용할 수 있다.

 

3. Http 요청 조회

1) 쿼리 파라미터 & HTML Form - @RequestParam

request parameter 형식으로 데이터가 전달되어 같은 방법으로 둘 다 조회 가능하다.

  • HttpServletRequest - request.getParameter()
  • @RequestParam
// url = "/movies?movieName=parasite&stars=200

@RequestMapping("/movies")
public String requestParam(
	@RequestParam("movieName") String movieName,
	@RequestParam("stars") int stars){}
    
//변수명 같으면 생략 가능
@RequestMapping("/movies")
public String requestParam(
	@RequestParam String movieName,
	@RequestParam int stars){}
    
//어노테이션도 사실 생략 가능 -> 헷갈리니까 잘 안씀
@RequestMapping("/movies")
public String requestParam(String movieName, int stars){}

//필수값 지정, 기본은 true
// "/movies?stars=200              오류 x , movieName = null
// "/movies?movieName=&stars=200   오류 x, movieName에 빈 문자열 들어감
// "/movies?movieName=parasite     400 오류
@RequestMapping("/movies")
public String requestParam(
	@RequestParam(required=false) String movieName,
	@RequestParam int stars){}

쿼리 파라미터 혹은 Html form으로 정보가 넘어오는 경우 @RequestParam 어노테이션을 자주 사용한다.

이것도 변수명이 같으면 생략이 가능하며 String, int 단순 타입이면 어노테이션도 사실 생략 가능하다.

 

필수 파라미터 여부를 (required = false, true)로 설정 가능하다.

기본은 true 이고 true로 설정해놓은 파라미터에 값이 없으면 400 Bad Request가 발생한다.

 

false로 설정해놓은 파라미터가 요청에 없을시 null이 지정된다.

파라미터 이름만 사용, 값 입력 x → null이 아니라 빈문자열이 들어간다..

int로 파라미터 받으면 null 설정이 안되서 required=false여도 요청에 없으면 500 오류가 발생한다.

 

<그외>

defaultValue 옵션 : 기본값 설정 (빈 문자도 기본값으로 해줌)

맵으로도 조회 가능 (@RequestParam Map<String,Object>)

MultiValueMap을 이용하면 파라미터 하나로 여러 값 받아오기가 가능하다.

 

2) 쿼리 파라미터 & HTML Form - @ModelAttribute

// url = "/movies?movieName=parasite&stars=200

public class MovieRequest{
	String movieName;
	int stars;
}

@RequestMapping("/movies")
public String modelAttribute(@ModelAttribute MovieRequest request){}

//어노테이션 생략 가능
@RequestMapping("/movies")
public String modelAttribute(MovieRequest request){}

 

@ModelAttribute를 사용하면 객체를 파라미터로 받아올 수 있다.

스프링 mvc는 @ModelAttribute가 있으면 MovieRequest 객체를 생성한 후

요청 파라미터 이름과 객체의 프로퍼티를 비교해 값을 바인딩한다.

 

이것도 어노테이션을 생략해도 된다.

그러나 String, int, Integer 등의 타입이 파라미터에 있는데 생략하면 @RequestParam을 적용시킨다.

후술할 ArgumentResolver 으로 세팅 혹은 예약된 경우 아닌 클래스의 경우만 ModelAttribute 적용한다.

 

3) 단순 텍스트 (text, xml, json 등등) - @RequestBody

 

Http Body에 값이 들어오는 경우는 앞의 두 파라미터로 꺼내오기가 불가능하다.

 

  • ServletInputStream 사용해서 직접 꺼내오기
  • HttpEntity,RequestEntity를 이용해서 꺼내오기
  • @RequestBody 사용하기
// url = "/movies

public class MovieRequest{
	String movieName;
	int stars;
}

@RequestMapping("/movies")
public String modelAttribute(@RequestBody String text){}

@RequestMapping("/movies")
public String modelAttribute(@RequestBody MovieRequest request){}

@RequestBody 어노테이션을 사용하면 String이나 JSON 형식으로 Http 바디에 담겨오는 정보들을 HttpMessageConverter가 알아서 변환해서 잘 매핑해준다.

 

결론은

  • 파라미터 조회 : RequestParam or ModelAttribute
  • 바디 조회 : RequestBody
  • 헤더 조회 : @RequestHeader 또는 HttpEntity 사용하자.

4. HTTP 응답

1) 정적 리소스(html)

spring boot에서 정적 리소스 반환 기능을 자동으로 제공한다.

resource/static 폴더에 html 저장하고 url에 파일 경로를 입력하면 html파일이 전달된다.

 

2) 뷰 템플릿(동적인 html)

@Controller
public TestController{
	@GetMapping("/movies")
    public String getMovie(){
    	return "movie-list"; //뷰 이름 반환
    }
    
    @GetMapping("/movies/1")
    public String getMovieDetail(Model model){
    	String data = "movie-data";
    	
    	model.addAttribute("data",data); //model 사용해 정보 전달
    	
    	return "response/movie-detail.html"; // 뷰 경로 반환
    }
}

타임리프등 뷰 템플릿을 사용할 시에는 resource/templates 폴더에 저장한 후

컨트롤러에서 String 으로 파일경로 혹은 이름을 반환하면 된다.

 

model.addAttribute()로 모델에 값을 넣어 템플릿에 전달이 가능하다.

 

Controller가 String 반환할경우 ResponseBody이 없으면 ViewResolver가 경로나 뷰 이름으로 뷰를 찾아 렌더링해준다.

 

3) 바디에 직접 입력(JSON)

@Controller
public TestController{

    @ResponseBody
    @GetMapping("/movies")
    public String getMovies(){
    	return "movies"; // 문자열 그대로 반환
    }
    
    @GetMapping("/movies/1")
    public ResponseEntity<String> getMovieDetail(){
    	return new ResponseEntity<>("ok", HttpStatus.ok);
    }
}

ResponseBody : 그냥 String 또는 객체를 반환한다. (객체의 경우 JSON 형식으로 반환된다)

ResponseEntity : String,객체 + status code 반환를 반환한다.

 

RestController 쓰면 (ResponseBody + Controller) 를 한 것과 같이 작동한다. 

5. Http 메세지 컨버터

Http Body에서 정보 읽거나 Body에 정보를 입력해서 반환할때 스프링 부트에선 메세지 컨버터를 사용한다.

인터페이스로 만들어져있으며 다양한 종류의 HttpMessageConverter 가 존재한다. (우선순위 존재)

  • ByteArrayHttpMassageConverter : 바이트 단위로 받아오기 가능
  • StringHttpMessage : 문자(String)으로 데이터 처리 (text/plain)
  • MappingJacksonConverter : 객체로 데이터 처리, 주로 JSON

Spring boot는 Accept 헤더(클라이언트의 해석 타입) , 컨트롤러 반환 타입등의 정보를 조합해서 컨버터를 선택한다.

 

요청 처리 순서

  • Http 요청 + @RequestBody or HttpEntity, RequestEntity
  • 모든 컨버터 대상 canRead() 메소드 호출 (대상 클래스 타입, medeaType, Accept type 지원하나 확인)
  • read()로 객체 생성, 정보 입력하고 반환

6. 요청 매핑 핸들러 어뎁터의 구조

HttpMethodConverter는 그러면 어디서 동작할까?

어노테이션 기반 컨트롤러에 요청이 왔을때 DispatcherServlet은 RequestMappingHandler와 어댑터를 불러온다.

RequestMappingHandler 어댑터는 ArgumentResolver를 호출한다.

 

ArgumentResolver는 인터페이스로 만들어져있으며 (30개 넘는 ArgumentResolver 구현 클래스가 존재함)

어노테이션 기반 컨트롤러의 다양한 파라미터에 맞게 객체를 생성해 핸들러에 반환한다.

 

핸들러에서 응답할때는 ReturnValueHandler가 ModelAndView, ResponseEntity, 등등의 응답값 반환, 처리

 

그리고 이 ArgumentResolver,ReturnValueHandler가 상황에 따라(@RequestBody, @ResponseBody 등의 어노테이션이 달려있다면)

HttpMessageConverter를 사용해서 파라미터 에 맞는 객체를 생성한다.

 

Spring은 ArgumentResolver, ReturnValueHandler, HttpMessageConverter 를 모두 인터페이스로 제공한다.

그래서 ArgumentResolver 인터페이스를 수정해서 사용자가 컨트롤러의 파라미터를 새로 생성하는 등의 확장도 가능하다.

 

 

https://velog.io/@somyeong0623/Spring-MVC-1%ED%8E%B8-06.-%EC%8A%A4%ED%94%84%EB%A7%81-MVC-%EA%B8%B0%EB%B3%B8-%EA%B8%B0%EB%8A%A5

 

[Spring] MVC 1편 - 06. 스프링 MVC - 기본 기능

이 글은 스프링 \[스프링 MVC 1편]을 듣고 정리한 내용입니다프로젝트 세팅 정보

velog.io

 

+ Recent posts