핸들러란?
컨트롤러의 메소드인 핸들러
스프링에서 @Controller 애노테이션이 사용된 클래스를 컨트롤러라고 부릅니다. 핸들러는 컨트롤러 안에서 어떤 요청을 처리할 수 있는 메소드를 핸들러라고 부릅니다.
@Controller
public class UserController {
//===핸들러====================
@RequestMapping("/hello")
@ResponseBody
public String handler() {
return "hello";
}
//=============================
}
요청이 들어오고 핸들러가 실행되기 까지
1. 클라이언트가 서버에 요청을 보내면,
2. 서버는 요청정보를 디스패쳐 서블릿에 담아 실행시킵니다.
3. 디스패쳐 서블릿에서 요청맵핑정보와 핸들러 맵핑정보를 대조해보고 적절한 핸들러를 찾습니다.
4. 디스패쳐 서블릿에서 찾은 핸들러에 아규먼트를 넣어주고 실행시킵니다.
핸들러에 요청 맵핑하기
@RequestMapping
디스패쳐 서블릿이 핸들러를 찾으려면 단서가 있어야겠죠. 스프링에서는 그 단서를 @(어노테이션) 으로 제공합니다. 핸들러 맵핑에서 가장 기본이 되는 어노테이션은 @RequestMapping 어노테이션 입니다. "/hello" 라는 uri 패턴에 맵핑하고 싶다면, @RequestMapping("/hello") 라고 적어주면 됩니다.
@RequestMapping("/hello")
// OR
@RequestMapping(value="/hello")
Http Method
- Http Method에는 GET, POST, PUT, DELETE, FETCH ... 등이 있습니다.
- @RequestMapping 는 기본적으로 모든 http 메소드를 허용합니다.
- @RequestMapping(method = RequestMethod.GET) 와 같이 메소드를 지정할 수 있습니다.
- @GetMapping 처럼 줄여서 쓸 수 있습니다.
@RequestMapping(value = "/hello", method = RequestMethod.GET)
// OR
@GetMapping("/hello")
조합해서 쓰기
@RequestMapping은 컨트롤러에 사용할 수도 있는데, 핸들러의 맵핑과 조합해서 사용할 수 있습니다. 컨트롤러에 "/john"을, 핸들러에 "/hello"를 사용하면, 조합되어 "/john/hello"와 맵핑됩니다.
@Controller
@RequestMapping("/john")
public class UserController {
@GetMapping("/hello")
@ResponseBody
public String handler() {
return "hello";
}
}
// "http://localhost:8080/john/hello" 요청에 맵핑됩니다.
여러 url 맵핑하기
문자열로 맵핑할 수 있습니다.
@RequestMapping({"/hello", "/hi"})
@ResponseBody
public String handler() {
return "hello";
}
// "localhost:8080/hello" 요청,
// "localhsot:8080/hi" 요청 둘 다 맵핑됩니다.
와일드카드 사용하기
특정한 패턴도 매핑할 수 있습니다.
▶ ? : 한 글자 ( "/hello/???" -> "/hello/123" )
▶ * : 여러 글자 ( "/hello/*" -> "/hello/junistudyhard" )
▶ ** : 여러 패스 ( "/hello/**" -> "/hello/juni/study/hard" )
uri에 중괄호 사용하기
uri에 중괄호{} 를 사용하면, 중괄호 안에 들어있는 변수로 uri를 받아 @PathVariable로 사용할 수 있습니다.
예를들어, GET "/hello/juni" 라고 요청을 보내면 "{name}" 에 해당하는 juni를 @PathVariable을 사용한 파라미터 name으로 받아서 사용할 수 있습니다. 여기서 @PathVariable을 핸들러 메소드 아규먼트 라고 하는데, 핸들러에서 사용할 파라미터를 핸들러 메소드 아규먼트를 통해 받아서 사용할 수 있습니다.
@GetMapping("/hello/{name}")
@ResponseBody
public string handler(@PathVariable String name){
return "hello " + name;
}
// 요청을 "/hello/juni" 이라고 보냈을 때,
// 응답 "hello juni"
핸들러 메소드 아규먼트
파라미터, 아규먼트?
void foo (int bar) {...}
foo(baz);
이 예제에서, bar는 foo의 파라미터입니다. baz는 foo의 아규먼트 입니다.
여기서 int가 아규먼트 타입입니다.
핸들러 메소드 아규먼트의 역할
스프링에서 정한 아규먼트 타입을 사용하면, 요청정보를 파라미터에 담아서 사용할 수 있습니다. 예를들어, HttpMethod라는 아규먼트 타입을 사용하면, Http요청의 Method 정보를 알 수 있습니다.
@RequestMapping("/events")
@ResponseBody
public String events(HttpMethod httpMethod) {
System.out.println(httpMethod.toString());
return "events";
}
// 실행시 콘솔에 요청 method가 나타납니다.
// get요청의 경우 응답 : GET
핸들러 메소드 아규먼트
핸들러 메소드 아규먼트는 요청에 들어있는 여러가지 값 중 핸들러에서 사용하고 싶은 정보를 꺼내서 사용하는 개념입니다. 꼭 요청에 들어 있지 않더라도, 스프링이 넣어줄 수 있는 정보라면 받아서 사용할 수 있습니다. 핸들러 메소드 아규먼트에 대한 공식문서는 아래 링크에 있습니다. 아주 많기 때문에 이 포스팅에서는 대표적인 몇가지만 살펴보겠습니다.
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-arguments
1. @PathVariable
이 핸들러 메소드 아규먼트는 uri에 들어있는 특정 부분을 메소드 아규먼트로 가져오는 방법입니다. 이 아규먼트의 특징은 uri 문자열의 타입컨버전이 가능합니다. uri에 문자로 "15"를 쓰더라도, Integer 숫자 15로 받을 수 있는 것이죠. 그리고 괄호를 사용하면 변수명이 달라도 받을 수 있습니다.
@GetMapping("events/{eventId}")
public class doEvent(@PathVariable Integer eventId) {...}
@GetMapping("events/{eventId}")
public class doEvent(@PathVariable("eventId") Integer no) {...}
2. @RequestParam
요청 파라미터를 받아오는 핸들러 메소드 아규먼트 입니다.
요청 파라미터는 두가지 종류가 있는데, 하나는 쿼리파라미터이고, 다른 하나는 formData 입니다.
- 쿼리 파라미터는 uri요청이 "/person/new?name=gildong&age=20" 라고 할 때, "?name=gildong&age=20"에 해당하는 부분입니다.
- forData는 http에서 <form> 태그로 보낼 때, 키벨류쌍으로 값을 보낼때를 말합니다.
이렇게 보내온 데이터를 둘 다 @RequestParam으로 받을 수 있습니다.
// POST person/new?name=gildong&age=20
@GetMapping("person/new")
@ResponseBody
public String newPerson(@RequestParam String name,
@RequestParam Integer age) {
return name + age;
}
// 결과 : gildong20
@RequstParam은 생략할 수 있으나, 다른 생략가능한 아규먼토인 @ModelAttribute와 헷갈릴 수 있어서 생략하지 않는 걸 추천드립니다.
// POST person/new?name=gildong&age=20
@GetMapping("person/new")
@ResponseBody
public String newPerson(String name, Integer age) {
return name + age;
}
// 결과 : gildong20
@RequestParam은 required 라는 속성이 있는데, 이 값이 기본적으로 true여서 값이 없으면 에러가 납니다. 속성값을 required=false로 바꾸면 값이 없어도 에러가 나지 않고, defaultValue를 함께 쓸 수 있습니다.
// POST person/new?name=gildong
@GetMapping("person/new")
@ResponseBody
public String newPerson(@RequestParam String name,
@RequestParam(required=false, defaultValue="100") Integer age) {
return name + age;
}
// 결과 : gildong100
3. @ModelAttribute
요청 파라미터를 복합객체로 받아오는 핸들러 메소드 아규먼트 입니다.
@RequestParam이 제각각 하나의 값을 받아왔다면, @ModelAttribute는 복합객체를 사용해서 받아올 수 있습니다.
필드값으로 name, age가 있는 클래스를 만들어서, 그 클래스타입으로 받아올 수 있습니다.
public class Person {
String name;
Integer age;
//getter,setter
}
요청에 Person 객체를 사용해서 받습니다.
// POST person/new?name=gildong&age=20
@GetMapping("person/new")
@ResponseBody
public String newPerson(@ModelAttribute Person person) {
return person.getName() + person.getAge();
}
// 결과 : gildong20
@RequestParam과 마찬가지로 생략가능하나 추천하지 않습니다.
// POST person/new?name=gildong&age=20
@GetMapping("person/new")
@ResponseBody
public String newPerson(Person person) {
return person.getName() + person.getAge();
}
// 결과 : gildong20
만약에 "person/new?name=gildong&age=스무살" 이라고 요청을 보내면 스무살이 Integer로 바인딩이 되지 않아 에러가 발생합니다. BindingResult 아규먼트를 사용하면 바인딩 에러를 담을 수 있고, 요청은 그냥 처리가 됩니다.
// POST person/new?name=gildong&age=스무살
@GetMapping("person/new")
@ResponseBody
public String newPerson(@ModelAttribute Person person, BindingResult bindingResult) {
return person.getName() + person.getAge();
}
// 결과 : gildongnull
BindingResult에 담긴 에러를 사용할 수 있습니다.
// POST person/new?name=gildong&age=스무살
@GetMapping("person/new")
@ResponseBody
public String newPerson(@ModelAttribute Person person, BindingResult bindingResult) {
if(bindingResult.hasErrors()){
person.setAge(100);
}
return person.getName() + person.getAge();
}
// 결과 : gildong100
4. @RequestBody
요청 본문을 복합 객체로 받는 핸들러 메소드 아규먼트 입니다.
요청 파라미터 (request parameter) | 쿼리파라미터, formData ... |
요청 본문 (request body) | JSON, XML ... |
@RequestBody는 요청 본문에 들어있는 데이터를 HttpMessageConverter를 사용해서 객체로 받아올 수 있는 아규먼트입니다. 요청 본문에 name과 age를 담아 보내면, HttpMessageConverter가 해석해서 Person 객체를 생성해줍니다. 포스트맨을 사용해서 JSON 요청을 보내겠습니다.
/* Post person/new
{
"name":"gildong",
"age":"20"
}
*/
@PostMapping("person/new")
@ResponseBody
public String addPerson(@RequestBody Person person) {
return person.getName() + person.getAge();
}
// 결과 : gildong20
핸들러가 응답하는 리턴 타입
1. String
String 타입으로 리턴하게 되면, templates 등 특정 디렉토리 밑에 문자열에 해당하는 html 파일을 찾아서 view를 리턴합니다.
2. @ResponseBody
리턴값을 HttpMessageConverter를 사용해서 응답 본문에 담아 응답합니다. @RestConroller를 사용하면 모든 핸들러가 @ResponseBody를 붙인것 처럼 동작합니다.
3. ResponseEntity<T>
응답 헤더와 상태 코드, 본문 등 응답을 입맛에 맞게 직접 만들어서 리턴합니다.
참고: https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-arguments
참고: https://www.inflearn.com/course/%EC%9B%B9-mvc
참고: https://stackoverflow.com/questions/427653/arguments-or-parameters
'back > spring' 카테고리의 다른 글
[스프링] 스프링을 사용하는 이유 (0) | 2022.11.02 |
---|---|
[스프링] 빈 팩토리를 사용하는 이유 (0) | 2022.11.02 |
[번역]Inversion of Control Containers and the Dependency Injection pattern - Martin Fowler (0) | 2022.05.08 |
[Spring] DispatcherServlet 분석 (0) | 2021.11.27 |
댓글