본문 바로가기
Web/spring study

[Spring] Spring Controller 어노테이션 정리 (MVC, REST api)

by 장인이 2023. 5. 26.

1. 개요

 스프링을 공부하면서, 컨트롤러 작성 시 여러 어노테이션들을 배우게 되었다. 각각의 특징들을 간단하게나마 모두 정리하면 좋을 것 같아, 해당 게시물에서 최대한 나열해 보려고 한다.

 

 

목차
@Controller
@RestController
@RequestMapping()
@GetMapping(), @PostMapping(), ...
@ResponseBody
@RequestParam()
@ModelAttribute
@PathVariable()
HttpEntity, ResponseEntity
@RequestBody
@ResponseStatus(HttpStatus.OK)
RedirectAttributes
etc

 

 

2. @Controller

@Controller
public class MyMVCController {
}

 @ControllerMVC 패턴의 컨트롤러를 만들고 싶은 경우 주로 사용된다. 해당 어노테이션이 붙은 class를 스프링은 자동으로 스프링 빈으로 등록해준다(내부에 @Component 존재). 또한, 스프링 MVC에서 어노테이션 기반 컨트롤러로 인식한다.

 

@RequestMapping("/return-string")
public String returnString() {
    return "response/hi";
}

 만일 반환 값이 String이면, 이를 View 이름으로 자동인식하게 된다. 따라서 뷰를 찾고, 해당 뷰가 렌더링된다.

 

 

3. @RestController

@RestController
public class MyRestController {
}

 Resfull한 컨트롤러 제작 시 사용되며, 클래스 레벨에 사용한다. 이는 뒤에서 언급할 @ResponseBody를 모든 메서드에 붙이는 효과를 가지고 있다.

 

@RequestMapping("return-rest")
public String returnRest() {
    return "ok";
}

 이 경우 메서드가 String 값을 반환하면, @Controller와 다르게 HTTP 메시지 바디에 값을 바로 입력한다.

 

 

4. @RequestMapping()

 핸들러 매핑, 핸들러 어댑터의 첫 번째 우선순위로 찾는 방법이다. 메서드 레벨에 사용되며, 컨트롤러로 인식된다. 핸들러 매핑, 핸들러 어댑터와 관련된 내용은 아래 링크를 참고 부탁한다.

스프링 MVC 구조 이해

 

@RequestMapping("/v1/save")
public String save() {
}

 괄호 안의 URL이 호출되면, 해당 어노테이션이 달린 메서드가 호출된다. 이는 어노테이션 기반이므로, 메서드 이름을 자유롭게 작성할 수 있다는 장점을 지니고 있다.

 

@RequestMapping(value = "/save", method = RequestMethod.GET)

위와 같은 방식으로 HTTP 메서드를 구분할 수 있다. 하지만, 아래의 방법을 주로 사용한다.

 

 

1) @GetMapping(), @PostMapping(), ...

 @RequestMapping() 대신에 해당 어노테이션을 사용하면, URL을 매핑함과 동시에 HTTP 메서드를 구분할 수 있다.

 

@GetMapping("v1/save")
@PostMapping("v1/save")
@PutMapping("v1/save")
@DeleteMapping("v1/save")
@PatchMapping("v1/save")

 GET, POST, PUT, DELETE, PATCH 모두 해당하는 어노테이션이 준비되어 있다.

 

 

2) @ResponseBody

 이를 사용하면 @Controller 클래스 안의 메서드들로 return type이 String일 때 그 값을 HTTP message body에 넣을 수 있다. 당연히, 이 경우 view는 사용되지 않는다.

 

@ResponseBody
@RequestMapping("/response-body-text")
public String responseBodyText() {
    return "ok";
}

 단순 메시지(text)를 HTTP 응답 message body에 넣고 싶은 경우, String 값을 return하면 된다.

 

@ResponseBody
@RequestMapping("/response-body-json")
public HelloData responseBodyText() {
    HelloData data = new HelloData("hi");
    return data;
}

 객체를 json 형식으로 보내고 싶은 경우, 위와 같이 인스턴스를 만들어서 return하면 된다.

 

 

3) @RequestParam()

 HTTP 요청 파라미터 @RequestParam()으로 받을 수 있다. 이는 Get 쿼리 파라미터, POST Form 방식을 모두 지원한다.

 

@ResponseBody
@RequestMapping("/request-v1")
public String requestParamV1(
        @RequestParam("username") String name
        @RequestParam("age") int age) {
    return "ok";
}

 위와 같이 Controller 메서드의 파라미터 자리에 온다. 서블릿의 String name = request.getParameter("username")과 같은 코드라고 생각하면 된다. 서블릿 사용법은 아래 링크를 참고 부탁한다.

서블릿, HttpServletRequest, HttpServletResponse

 

@ResponseBody
@RequestMapping("/request-v2")
public String requestParamV2(
        @RequestParam String username
        @RequestParam int age) {
    return "ok";
}

 만일 변수 이름과 HTTP 파라미터의 이름이 같으면, 괄호 생략 가능하다.

 

@ResponseBody
@RequestMapping("/request-v3")
public String requestParamV3(
        String username
        int age) {
    return "ok";
}

 @RequestParam 어노테이션 자체를 생략할 수도 있다. 스프링은 String, int, Integer와 같은 타입은 생략시 @RequestParam 어노테이션을 자동으로 붙여준다. 하지만, 개인적으로는 굳이 생략 안하는 것이 좋은 것 같다.

 

- required = 

@ResponseBody
@RequestMapping("/request-v4")
public String requestParamV4(@RequestParam(value = "username", required = false) String name) {
    return "ok";
}

 required = false를 통해 파라미터 필수 여부를 지정할 수 있다. default 값은 true이다. 이때, default가 true일 경우 파라미터 이름만 있고 값이 없더라도, 통과된다!

- /request-v3?username=

 

- null이 입력될 경우 주의사항

 @RequestParam int age에 null 값이 들어온다면? null을 int에 넣는 것을 불가능하므로, 500 예외가 발생한다. 이때는 파라미터 타입을 Integer로 변경하거나, 아래 나올 defaultValue를 사용하자.

 

@ResponseBody
@RequestMapping("/request-v5")
public String requestParamV5(@RequestParam Integer age) {
    return "ok";
}

 파라미터 타입을 Integer로 변경하면, 해결할 수 있다.

 

- defaultValue = 

@ResponseBody
@RequestMapping("/request-v6")
public String requestParamV6(@RequestParam(defaultValue = "-1") int age) {
    return "ok";
}

 파라미터에 값이 없을 경우, defaultValue에 들어가있는 값이 사용된다. 이때, 값이 없으면 기본값을 사용하면 되기 때문에 required = true는 의미가 없어진다. null이 입력되는 경우도 해결된다.

 

@ResponseBody
@RequestMapping("/request-v7")
public String requestParamV7(@RequestParam Map<String, Object> paramMap) {
    return "ok";
}

 파라미터를 Map으로 조회할 수 있다. 하지만 파라미터의 값이 2개 이상일 수도 있으므로, MultiValueMap을 사용하자.

 

 

4) @ModelAttribute

 @ModelAttribute는 파라미터에서 객체에 자동으로 값을 넣어주는 어노테이션이다. 이를 사용하려면 요청 파라미터를 바인딩 받을 객체가 필요하다.

 

@Data
public class HelloData{
}

 롬복의 @Data를 붙이면, @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructor를 자동으로 적용해준다.

 

@ResponseBody
@RequestMapping("/request-v8")
public String requestParamV8(@ModelAttribute HelloData helloData) {
    return "ok";
}

 스프링은 위 코드를 발견하면 다음과 같은 과정을 실행한다.

- HelloData 객체를 생성한다.

- 요청 파라미터 이름으로 HelloData의 값을 찾고, 해당 값의 setter를 호출해 파라미터의 값을 바인딩한다.

 

@ResponseBody
@RequestMapping("/request-v9")
public String requestParamV9(HelloData helloData) {
    return "ok";
}

 @ModelAttribute를 생략할 수도 있다. 스프링은 String, int , Integer와 같은 타입들이 아닌 나머지는 생략된 경우 @ModelAttribute를 붙여준다. 개인적으로는 굳이 생략 안하는 것이 좋은 것 같다.

 

 

5) @PathVariable()

 @PathVariable은 리소스 경로에 식별자가 들어가 있는 경우, 조회할 수 있도록 도와준다.

 

@ResponseBody
@RequestMapping("/request-v10/{userId}")
public String requestParamV10(@PathVariable("userId") String data) {
    return "ok";
}

 만일 "/request-v10/hi" 라는 경로가 실행되면, data의 값은 "hi"가 될 것이다.

 

@ResponseBody
@RequestMapping("/request-v10/{userId}")
public String requestParamV10(@PathVariable String userId) {
    return "ok";
}

 @PathVariable의 이름과 파라미터의 이름이 같으면, 괄호 안의 값은 생략 가능하다.

 

 

6) HttpEntity, ResponseEntity

@ResponseBody
@RequestMapping("/request-v11")
public void requestParamV11(HttpEntity<String> httpEntity) {
}

 HTTP header, body 정보를 편리하게 조회 할 수 있는 스프링 MVC에서 지원해주는 파라미터이다.

 

@ResponseBody
@RequestMapping("/request-v12")
public void requestParamV12(HttpEntity<String> httpEntity) {
    String messageBody = httpEntity.getBody();
}

 HTTP message body에서 단순 텍스트를 가지고 오고 싶다면, 위와 같이 사용하면 된다.

 

@ResponseBody
@RequestMapping("/request-v13")
public void requestParamV13(HttpEntity<HelloData> httpEntity) {
    HelloData data = httpEntity.getBody();
}

 HTTP message body가 JSON 데이터 형식이라면, 위와 같은 방법으로 바로 객체에 대입 가능하다.

 

 

@ResponseBody
@RequestMapping("/request-v14")
public HttpEntity<String> requestParamV14() {
    return new HttpEntity<>("ok");
}

 HttpEntity는 응답에도 사용이 가능하다. 그러면 message body 정보를 직접 반환하게 된다. 하지만, 보통 HTTP 응답시 상태코드와 함께 반환해야 하는 경우가 많다...

 

@ResponseBody
@RequestMapping("/request-v15")
public ResponseEntity<HelloData> requestParamV15() {
    HelloData hellodata = new HelloData("hi");
    return new ResponseEntity(helloData, HttpStatus.OK);
}

 이때는 HttpEntity를 상속받은 ResponseEntity를 사용하자. ResponseEntity는 상태코드를 같이 받는다.

 

7) @RequestBody

 @RequestBody는 HTTP message body 정보를 편리하게 조회할 수 있도록 도와준다. 만일 헤더 정보가 필요하다면, 위에서 언급한 HttpEntity 혹은 아래서 잠깐 소개할 @RequestHeader를 사용하자. 당연히 view는 사용되지 않는다.

 

@ResponseBody
@RequestMapping("/request-v16")
public String requestParamV16(@RequestBody String messageBody) {
    log.info("messageBody={}", messageBody);
    return "ok";
}

 HTTP message body가 단순 텍스트로 이루어져 있다면, 위처럼 파라미터를 적으면 된다.

 

@ResponseBody
@RequestMapping("/request-v17")
public String requestParamV17(@RequestBody HelloData helloData) {
    log.info("username={}", helloData.getUsername());
    return "ok";
}

 HTTP message body가 JSON 데이터 형식이라면, 위처럼 적으면 된다. 그러면 helloData 객체에 자동으로 값이 바인딩 되어 들어간다.

 이때, @RequestBody는 생략하면 안된다!! 생략시 스프링에서는 @ModelAttribute로 생각하고 진행할 것이다.

 

8) @ResponseStatus(HttpStatus.OK)

 메서드 레벨에 붙일 수 있는 어노테이션이며, 응답 코드를 설정할 수 있다. @ResponseBody 혹은 HttpEntity<>로 객체를 응답할때 추가로 사용하면 된다.

 하지만 동적으로 변경할 수 없으므로(어노테이션이기 때문) 되도록이면 ResponseEntity를 사용하자

 

 

9) RedirectAttributes

PRG(Post Redirect Get) 패턴과 같은 곳에서 view 렌더링시 redirect 하고 싶은 경우에 사용한다.

 

@RequestMapping("/request-v18")
public String requestParamV18(@ModelAttribute Item item, RedirectAttributes redirectAttributes) {
    Item savedItem = itemRepository.save(item);
    redirectAttributes.addAttribute("itemId", savedItem.getId);
    redirectAttributes.addAttribute("status", true);
    return "redirect:/basic/items/{itemId}";
}

 실제로 작동시, redirect 주소는 /basic/items/3?status=true 이렇게 나올 것이다. 이는 URL 인코딩, pathVariable, 쿼리 파라미터 모두를 처리해준다.

 물론 return "redirect:/basic/items" + item.getId() 해도 작동은 되지만, URL 인코딩이 되지 않아 위험하다.

 

 

10) 기타

(1) Locale locale

 파라미터에 들어가는 값으로, ko_KR와 같은 Locale 정보를 조회한다.

 

(2) @RequestHeader MultiValueMap<String, String> headerMap

 HTTP 요청 메시지 헤더들을 조회하고 싶을때 사용하며, 헤더가 여러개 있을 수 있어 key 중복을 허용하는 MultiValueMap을 사용한다. headerMap.get() 사용시, List<String>을 반환한다.

 

(3) @RequestHeader("host") String host

 특정 HTTP 헤더를 조회한다. 필수 값 여부 조절하고 싶으면 required, 기본 값 설정하고 싶으면 defaultValue 설정하자.

 

(4) @CookieValue(value = "myCookie", required = false) String cookie

 특정 쿠키를 조회한다. 마찬가지로 필수 값 여부 조절하고 싶으면 required, 기본 값 설정하고 싶으면 defaultValue 설정하자.

 


위 내용은 김영한 님의 인프런 강의 "스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술"의 내용과 강의자료를 토대로 작성된 게시글입니다.

강의 링크:

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard

댓글