이번에는 Spring MVC 패턴을 적용하면서 궁금했던 HTML을 불러오는 부분을 정리해보려고 한다.
미니 프로젝트를 진행하면서 궁금했던 또 다른 부분은 바로 컨트롤러 코드에 있었다.
우선 코드를 살펴보자
Sample Code
@Controller public class UserController { @Autowired() private UserService userService; @GetMapping("/") public ModelAndView userSelect() { ModelAndView modelAndView = new ModelAndView("/user/userList"); List<UserProfileDto> list = userService.getAllUsers(); return modelAndView.addObject("list", list); } @GetMapping("/userAdd.do") public String goToUserAdd() { return "/user/userAdd"; } @PostMapping("/user/add") public String addUser(UserProfileDto userProfileDto, RedirectAttributes redirectAttributes) { Boolean isSuccess = userService.addUser(userProfileDto); if (isSuccess) { redirectAttributes.addFlashAttribute("success", "사용자가 성공적으로 등록되었습니다.\n(메인으로 돌아가기)"); return "redirect:/userAdd.do"; } else { redirectAttributes.addFlashAttribute("fail", "사용자 등록에 실패했습니다.\n[DB Error]"); return "redirect:/userAdd.do"; } } @GetMapping("/userUpdate.do") public ModelAndView openUserDetail(@RequestParam("userId") Long userId) { ModelAndView modelAndView = new ModelAndView("/user/userUpdate"); UserProfileDto userProfileDto = userService.selectUserDetail(userId); modelAndView.addObject("userProfileDto", userProfileDto); return modelAndView; } ... }
위에서 작성된 UserController의 코드를 살펴보면, ModelAndView
를 반환하는 메서드와 String
을 반환하는 메서드가 존재한다.
ModelAndView는 View Template을 생성하는 시점에 매핑되는 HTML 파일의 경로를 파라미터로 전달한다.
이러한 방식으로 적절한 HTML을 반환하고 있음을 직관적으로 이해할 수 있다.
하지만, String으로 단순한 문자열을 반환하는 경우에는 어떻게 적절한 View Template을 찾아서 반환할 수 있는 걸까?
그 해답은 바로 ViewResolver에 있다.
Spring MVC에서 String을 반환하면 다음과 같은 과정이 일어난다.
Controller에서 String 반환하는 경우
- 컨트롤러에서 String 반환
- ViewResolver가 해당 문자열을 받아서 실제 뷰 경로로 변환
- 접두사(prefix) 추가
- EX) "/WEB-INF/views/"- 접미사(suffix) 추가
- EX) ".html"- 최종 경로의 뷰 파일을 찾아서 렌더링
즉, String으로 반환하면 접두사와 접미사를 추가하여 뷰 경로를 찾아내고 이를 렌더링하는 방식으로 동작한다!
여기서 중요한 점은 이러한 ViewResolver의 동작이 @Controller
와 @RestController
에서 다르다는 것이다.
두가지 어노테이션을 정리하면 다음과 같다.
@Controller
- ViewResolver를 통해 뷰를 찾아 렌더링
- 문자열 반환 시 뷰 이름으로 인식
- 주로 웹 페이지를 반환할 때 사용
@RestController
- ViewResolver를 사용하지 않음
- 문자열 반환 시 그 문자열 자체를 반환
- 주로 데이터(JSON/XML)를 반환할 때 사용
이러한 차이점 때문에 같은 코드라도 다른 결과를 볼 수 있다.
Sample Code
// @Controller의 경우 return "/user/userAdd"; // userAdd.html 파일을 찾아 렌더링 // @RestController의 경우 return "/user/userAdd"; // 문자열 "/user/userAdd"를 그대로 반환
따라서, 데이터를 주고받는 목적이라면 @RestController를 사용하고 아니라면 @Controller를 사용하면 된다.
@Controller를 사용하면 String으로 리턴되는 경우 ViewResolver가 경로로 인식하여 적절한 템플릿 HTML을 찾아서 렌더링한다!
사실 이 미니 프로젝트를 진행하면서 정리하려고 목표했던 내용은 더 있다.
예를들어, 필자는 장바구니 같은 기능을 구현했는데 기존에 사용하던 방식(Html -> Controllor로 요청 -> 적절한 View Template 반환)을 사용하면 +
버튼을 눌러서 아이템을 추가할 때마다 리렌더링이 발생한다.
이런 문제를 DOM 요소를 직접 컨트롤하는 스크립트 코드를 추가해서 해결할 수 있었다.
하지만, 지금 정리하면서 생각해보니 SpringBoot 정리와는 조금 멀어지는 것 같아서 생략하려고 한다! 👊