@Controller 이해하기
내가 처음 스프링의 MVC패턴에 대해 배울 때에는 controller에서 @Controller 어노테이션을 사용해서 View를 반환하는 방식을 배웠었다.
[Controller로 View 반환 예제코드]
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
@GetMapping("/members/new")
public String createForm(){
return "members/createMemberForm";
}
}
위 코드처럼 Get메소드로 요청이 들어왔을 때 "/members/createMemberForm"이라는 String을 반환함으로써, 사용자에게 View를 반환해는 방식을 사용한다. 위 예제 코드에서는 URL을 반환하는데 이 URL로부터 View를 렌더링하기 위해서는 ViewResolver가 사용된다. ViewResolver는 설정에 맞게 View를 찾아 랜더링한다.
하지만 Controller에서 데이터를 반환해야 하는 상황도 빈번하게 발생한다. Controller에서 Data를 반환하기 위해서는 @ResponseBody 어노테이션을 사용해야 한다. @ResponseBody 어노테이션을 활용하면, Controller에서도 Data를 반환할 수 있다.
[Controller로 Data 반환 예제코드]
@Controller
public class HelloController {
@GetMapping("hello-string")
@ResponseBody
public String helloString(@RequestParam("name") String name){
return "hello " + name;
}
}
위 코드는 @RequestParam 어노테이션을 통해 name의 데이터를 받아 반환으로는 "hello "라는 String과 name을 @RequestBody로 감싸서 반환했다. 이렇게 반환하면 "hello " + name이라는 View를 반환하는 것이 아닌, "hello " + name이라는 String이 반환값이 되기 때문에, 데이터를 반환한 것이다.
위의 예제에서는 그렇지 않지만 보통 컨트롤러를 통해서 객체를 반환할 때에는 일반적으로 ResponseEntity로 감싸서 반환한다. 그리고 객체를 반환하기 위해서는 viewResolver 대신에 HttpMessageConverter가 동작한다. HttpMessageConverter에는 여러 Converter가 등록되어 있고, 반환해야 하는 데이터에 따라 사용되는 Converter가 달라진다.
@RestController 이해하기
이렇게 @Controller만 알고있었던 나는 프로젝트를 하며, 프론트와 협업을 하다보니 View를 다루는 일은 프론트에서 많이 다루기 때문에, 백엔드에서는 주로 JSON형태로 데이터를 반환하는 것이 주요한 일이 되었다. 그러다가 @RestController를 알게 되었다. @RestController는 @Controller와 @RequestBody가 합쳐진 것으로, 객체를 ResponseEntity로 감싸서 반환한다. 그렇기 때문에 REST API를 개발할 때는 주로 @RestController가 사용된다.
[RestController 예제코드]
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class MemberController {
// 생성자 주입
private final MemberService memberService;
private final SecurityService securityService;
@PostMapping("/register")
public ResponseEntity<MemberDTO> save(@RequestBody MemberDTO memberDTO) {
memberService.save(memberDTO);
return new ResponseEntity<>(HttpStatus.OK);
}
@PostMapping("/login")
public ResponseEntity<Map<String, Object>> login(@RequestBody LoginDTO loginDTO) {
MemberDTO loginResult = memberService.login(loginDTO);
if (loginResult != null) {
// login 성공
String subject = loginDTO.getMemberEmail();
String token = securityService.createToken(subject, (2*1000*60));
Map<String, Object> map = new LinkedHashMap<>();
map.put("token", token);
return new ResponseEntity<>(map, HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
}
위 예제 코드처럼 데이터를 ResponseEntity에 감싸서 HTTP 응답 상태 코드와 함께 HTTP body에 데이터를 JSON방식으로 보낼 수 있다. 이렇게 @RestController를 사용하면 백엔드 개발자는 View에 대해서는 신경쓰지 않고 REST API개발에 초점을 맞추며 개발할 수 있다.
정리
RESTful API 개발에 초점을 맞춰야 한다면, @RestController를 선택하고, 전통적인 웹 어플리케이션 개발(HTML 기반의 View렌더링)에 초점을 맞춰야 한다면 @Controller를 선택하는 것이 좋을 것 같다.