Spring - 스프링에서 지원하는 @
어노테이션(Annotation)에 관하여
해당 글은 김영한님의 인프런 강의 “스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술”의 내용을 학습 후 정리하기 위해 작성하는 글입니다.
예전에는 자바에 어노테이션 기능 조차 없었지만.. 자바에 어노테이션 @
이 추가되고 스프링에서 어노테이션 기반의 인터페이스들을 지원하게 되면서 개발자들은 더욱더 클린 코드를 작성할 수 있게 되었다. 스프링에서 제공하는 어노테이션들에 대해 정리해보고자 한다.
Class-Level Annotations
@Controller
MVC 패턴의 Controller 클래스임을 명시한다. 해당 클래스에서 URL 매핑된 메소드들은 리턴 값으로 viewName(String)을 반환한다. 따라서 ViewResolver를 통해 viewName에 맞는 뷰를 클라이언트에게 제공한다.
@RestController
REST API를 제공하는 컨트롤러를 위한 어노테이션 해당 클래스에서 URL 매핑된 메소드들은 리턴 값으로 String을 반환한다. html view 형식이 아닌 JSON과 같은 데이터를 클라이언트에게 제공한다.
Method-Level Annotations
@ResponseBody
@Controller로 지정된 클래스 내에서 어떤 비즈니스 로직을 처리하는 메소드는 클라이언트에게 뷰가 아닌 데이터(JSON)를 전송하고 싶을때 사용하는 어노테이션이다.
@Controller
public class SomeController {
/*
* 뷰를 리턴하는 여러 메소드들...
*/
// 해당 클래스 내에서 데이터 BODY를 리턴하고 싶은 경우
@ResponseBody
@RequestMapping("/get-data")
public String dataToClient() {
// View가 아닌 실제 데이터 전송이 가능하다.
return "some data";
}
}
@RequestBody
HTTP 요청 파라미터 형식이 아닌 HTTP BODY 형식의 데이터를 클라이언트에게서 받아서 처리해야 할 때 사용할 수 있는 어노테이션이다.
먼저 기존 서블릿 클래스를 이용한 방법은 아래와 같다.
/*
* 서블릿을 이용한 기존의 BODY 데이터 처리 방법
*/
@PostMapping("/request-body-servlet")
public void requestBodyServlet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// ServletInputStream 클래스를 이용한다.
// BODY 데이터를 입력 스트림의 형태로 가져오기 때문에 반드시 인코딩 명시 필요
ServletInputStream inputStream = req.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
/*
* some logic...
*/
resp.getWriter().write("response");
}
@RequestBody 어노테이션을 사용하면 매우 간단하게 String 타입의 BODY를 가져올 수 있다.
/*
* @RequestBody 어노테이션 이용
*/
@ResponseBody
@PostMapping("request-body-annotation")
public String requestBodyAnnotation(@RequestBody String messageBody) {
/*
* some logic...
*/
return "response";
}
@RequestMapping
어노테이션의 이름과 같이 요청(URL)을 컨트롤러의 메소드와 매핑할 때 사용하는 어노테이션이다. 메소드 뿐만 아니라 클래스 레벨에서도 사용이 가능하다.
기존 서블릿의 구조와 비교해본다면 다음과 같다.
// (1) 기존 @WebServlet의 구조
@WebServlet(name = "someServlet", urlPatterns = "/someURL")
public class SomeServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// HttpServlet 클래스의 service 메소드를 오버라이드 해야한다.
// 파라미터로 request, response 두가지만 받을 수 있다.
}
}
// (2) 스프링 @RequestMapping 구조
@Controller
public class SomeController {
@RequestMapping("/someURL")
public String someRequestMapping(/*매우 많은 파라미터들*/) {
// 메소드 형식으로 여러 매핑 형태를 구현할 수 있다.
// 만든 메소드의 파라미터로 여러가지 값들을 가져올 수 있다.
}
}
파라미터로 받을 수 있는 Method Arguments들은 종류가 매우 많다.
공식 Spring docs 에 자세하게 설명되어 있다.
@GetMapping, @PostMapping, etc
@RequestMapping와 동일하지만, HTTP 메소드를 따로 지정해줘야 했던 @RequestMapping과 다르게 원하는 메소드 매핑을 직접 사용할 수 있다.
// 두가지 방법 모두 동일한 기능을 한다.
@RequestMapping(value = "/someGetPage", method = RequestMethod.GET)
@GetMapping("/someGetPage")
스프링에서는 다음과 같은 HTTP 통신 메소드 어노테이션을 지원한다.
@RequestHeader, @CookieValue
Mapping된 메소드에 파라미터로 HTTP 통신의 헤더값들을 전달받을 수 있다.
서블릿에서 사용했던 HttpServletRequest
, HttpServletResponse
뿐만 아니라 HTTP 통신 메소드를 정의하는 HttpMethod
, 클라이언트의 locale 정보를 담는 Locale
처럼 스프링에서 제공하는 클래스들 말고도 아래처럼 어노테이션 형식의 파라미터를 지원한다.
@RequestMapping("/headers")
public String getHeadersWithAnnotations(
HttpServletRequest req,
HttpServletResponse resp,
// GET, POST, PATCH, etc..
HttpMethod httpMethod,
// ko_KR
Locale locale,
// 헤더 정보 모두 가져오기
// MultiValueMap<K, V>: 하나의 key에 여러 value를 받을 수 있다!
@RequestHeader MultiValueMap<String, String> headerMap,
// 특정 헤더값 가져오기
@RequestHeader("host") String host,
// 쿠키
@CookieValue(value = "someCookie", required = false) String cookie) {
/*
* some logic...
*/
return "";
}
어노테이션의 파라미터들에 대해 자세하게 알아보면 다음과 같다.
@RequestHeader(
// header의 이름을 명시한다. 아래 name의 별칭이다.
String value,
// value 와 같은 기능
String name,
// header 값의 필요 여부, true 일 경우 헤더 값이 존재하지 않으면 exception 발생
boolean required,
// required=false 일 때 헤더 값이 존재하지 않는 경우 기본 값을 설정해줌
String defaultValue
)
@RequestParam
HTTP 요청 파라미터를 쉽게 받을 수 있는 어노테이션이다.
위의 @RequestHeader와 동일한 파라미터 값들을 지원한다
/*
* http://localhost:8080/params?id=myid&pw=1234
*/
@RequestMapping("/params")
public String getParamsWithAnnotations(
@RequestParam(value = "id", defaultValue = "guest") String id,
@RequestParam("pw") int password) {
/*
* some logic...
*/
return "";
}
아래와 같이 @RequestParam 어노테이션 생략 또한 가능하다.
// @RequestParam 어노테이션 생략 가능
// 1. 파라미터의 value name과 값을 저장할 변수 이름이 같은 경우
// 2. int, Integer, String과 같은 단순타입 인 경우
@RequestMapping("/params")
public String getParamsWithAnnotations(
@RequestParam(defaultValue = "guest") String id,
int password) {
/*
* some logic...
*/
return "";
}
- 만약 HTTP 요청에서의 파라미터 값이 공백(“”) 이라면?
http://localhost:8080/params?username=
와 같은 요청의 경우 username의 파라미터에는 “” 공백데이터로 인식되어 정상 작동한다.
"”과 null은 다르기 때문에.. 개발자는 이를 조심해야 한다. 한가지 예방법으로는 defaultValue 값을 설정해주면 된다.
@RequestParam(defaultValue="guest") String id
// 'http://localhost:8080/params?username=' HTTP 요청 시
// username = "guest"
@ModelAttribute
요청 파라미터의 값들을 통해 객체를 만들때 하나하나 파라미터값을 불러오지 않아도 @ModelAttribute를 이용하여 객체 생성을 자동화할 수 있다.
class SomeClass {
private String someData;
/*
* Getter & Setter
*/
}
@Controller
public class ModelAttributeTestClass {
@RequestMapping("/model-attribute")
public String makeModel(@ModelAttribute SomeClass someInstance) {
/*
* some logic...
*/
return "";
}
@RequestParam과 동일하게 어노테이션 생략이 가능하다.
public String makeModel(SomeClass someInstance) { ... }
- 생략된 @RequestParam과 @ModelAttribute는 어떻게 구분될까?
- 단순 타입(int, Integer, String, etc)의 경우 @RequestParam
- 단순 타입을 제외한 나머지 클래스의 경우 @ModelAttribute
하지만 HttpServletRequest
와 같은 클래스 파라미터들은 @ModelAttribute로 인식하지 않는다. 그 이유는 Argument resolver로 지정해둔 클래스 타입이기 때문이다.
댓글남기기