🚩 소개
웹 개발을 하다 보면 MVC라는 용어를 한번쯤은 접해보셨을텐데요! 오늘은 이게 무엇인지 설명할 수 있을 정도로 확실히 알아가려고 합니다!
MVC는 현대 웹 개발에서 필수적인 도구 중 하나입니다. 하지만 그 필요성을 이해하려면 MVC 구조가 왜 중요한지, 그리고 MVC 없이 웹 애플리케이션을 개발할 때 발생하는 문제점을 비교해보는 것이 유용합니다.
스프링(SPRING)은 이러한 MVC 구조를 기본적으로 지원하는 대표적인 자바 프레임워크입니다.
이번 글에서는 MVC란 무엇인지, 그리고 스프링의 MVC 구조와 작동 방식 등에 대해 아주 쉽게 설명해 드릴게요! 😉
⭐ MVC란 무엇인가요?
먼저 MVC는 Model, View, Controller의 약자입니다.
이 세 가지 요소는 웹 애플리케이션을 구조화하는 데 중요한 역할을 합니다. 각 요소에 대해 더 자세히 설명해드리도록 하겠습니다!
✅ Controller : 요청을 처리하는 곳
컨트롤러는 사용자의 요청을 받아서 비즈니스 로직을 수행하고, 그 결과를 뷰에 전달하는 역할을 합니다.
즉, 중간 관리자입니다.
스프링에서는 @Controller 어노테이션을 사용하여 컨트롤러 클래스를 정의합니다.
@Controller
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/users")
public String getUsers(Model model) {
// 엔티티에서 데이터베이스로부터 사용자 목록을 가져옴
List<User> users = userService.getAllUsers();
// 모델에 사용자 데이터를 담아 뷰로 전달
model.addAttribute("users", users);
return "userList"; // userList.jsp 혹은 userList.html로 이동
}
}
이 코드에서 컨트롤러는 사용자의 요청을 처리하고, 비즈니스 로직(userService.getAllUsers())을 호출하여 사용자 목록을 가져옵니다. 그리고 이 데이터를 모델에 담아 뷰로 전달합니다.
더 자세히 설명해드리자면, User는 엔티티 클래스이고, userService.getAllUsers()는 데이터베이스에서 사용자 목록을 가져오는 역할을 합니다. 그런 다음, 가져온 엔티티 데이터를 Model 객체에 담아 뷰로 전달하여 화면에 표시하게 됩니다. 이 코드를 통해 Model은 데이터를 뷰에 전달하는 역할을 한다는 것 또한 알 수 있겠네요!
✅ Model : 데이터를 담는 곳
컨트롤러에서 뷰로 데이터를 전달하기 위한 일시적인 데이터 저장소입니다.
HTML이나 JSP 같은 화면에 데이터를 표시할 때 사용됩니다. model.addAttribute()로 데이터를 담아 뷰로 넘깁니다.
이처럼 스프링에서 Model은 데이터를 뷰로 전달하는 매개체 역할을 합니다. 그래서 컨트롤러에서 뷰로 데이터를 전달하기 위한 일시적인 데이터 저장소라고 할 수 있는 것입니다.
컨트롤러에서 데이터를 모델에 담으면, 그 데이터는 뷰에서 쉽게 접근할 수 있습니다.
model.addAttribute("users", users);
model.addAttribute("users", users); 메서드는 스프링 MVC에서 컨트롤러가 뷰로 데이터를 전달할 때 사용되는 중요한 메서드입니다. 이 메서드는 Model 객체에 데이터를 저장하여, 그 데이터를 뷰에서 접근할 수 있도록 합니다.
addAttribute(String attributeName, Object attributeValue)는 Model 객체에 이름(name)과 값(value)을 쌍으로 추가하는 역할을 합니다.
여기서 attributeName는 데이터를 참조할 키(key)입니다. 뷰에서 이 이름으로 데이터를 참조할 수 있습니다.
attributeValue는 실제로 전달할 데이터(value)입니다. 일반적으로 자바 객체(List, Map, String, int 등 다양한 타입)를 전달할 수 있습니다.
✅ View
사용자가 보는 화면을 담당합니다. HTML, CSS, JavaScript 같은 것을 통해 사용자 인터페이스(UI)를 구성합니다. 즉, 프론트엔드 코드라고 할 수 있습니다.
뷰는 사용자가 실제로 보게 될 화면을 구성하는 부분입니다. 예를 들어, users.jsp는 getUsers 메서드가 반환한 데이터(모델)를 화면에 표시하는 역할을 합니다.
⬇️
이렇게 MVC 패턴을 사용하는 이유는 로직과 UI를 분리할 수 있어서, 코드 유지보수가 더 쉽고, 개발 과정도 더 체계적이기 때문입니다.
☑️ MVC 없이 웹 애플리케이션을 개발하면?
우선, MVC 구조 없이 전통적인 방식으로 웹 애플리케이션을 개발한다고 가정해 봅시다. 과거에는 하나의 JSP 파일에 비즈니스 로직, 데이터 처리, 그리고 화면 표시까지 모두 작성하는 방식이 일반적이었습니다.
참고로, JSP는 JavaServer Pages의 약자로, 자바 코드와 HTML을 함께 사용해 동적 웹 페이지를 생성하는 기술입니다. JSP는 웹 서버에서 실행되며, 주로 서버 측에서 HTML 페이지를 생성하는 데 사용됩니다.
스프링 부트에서는 이 대신 Thymeleaf, Mustache 등의 템플릿 엔진을 많이 사용합니다.
예를 들어, 아래와 같은 JSP 파일을 생각해 보겠습니다.
<%@ page import="java.util.List" %>
<%
// 데이터베이스에서 사용자 목록을 불러옴 (비즈니스 로직)
List<String> users = UserService.getAllUsers();
// 화면에 사용자 목록 표시 (뷰 로직)
for(String user : users) {
out.println("<p>" + user + "</p>");
}
%>
이 방식은 간단해 보일 수 있지만, 시간이 지날수록 코드가 점점 더 복잡해집니다. 화면을 바꾸려면 JSP 파일을 수정해야 하고, 비즈니스 로직을 변경하려면 같은 파일에서 수정해야 하며, 데이터베이스와의 연결 로직도 같은 파일에 위치해 있습니다. 즉, 유지보수가 매우 어렵고, 코드의 재사용성도 낮아지게 됩니다.
저 또한 개발 초보 시절에 하나의 파일에서 여러 명이 기능을 추가했던 적이 있었는데요. (MVC는 분리하긴했지만, 도메인 파일을 더 세세하게 분리하지 않아 한 파일에서 여러 사람이 작업을 하여 문제가 발생했었습니다. 도메인 별 파일 분리의 필요성에 대해서는 뒤에서 더 자세히 설명해드리겠습니다!) 이때 충돌이 많이 나서 고생을 했던 기억이 있습니다.. 😣하지만! 그래서 파일 분리의 필요성을 더 확실히 느낄 수 있었습니다. 이덕분에 MVC 패턴에 대해 더 이해가기 쉽게 설명해드릴 수 있을 거 같습니다! 😁
MVC 없이 개발한 코드는 저보다 더 다양한 기능을 한 파일에 몰아넣는 경향이 있습니다. 이로 인해 코드는 점점 더 복잡해지고, 유지보수 비용이 급격히 증가하게 됩니다.
반면에, MVC 구조를 사용한 코드는 모든 로직이 명확히 구분되어 있고, 수정과 확장이 훨씬 수월합니다.
⭐ 스프링 MVC 더 자세히 알아가기
1. DispatcherServlet: 스프링의 중심
스프링 MVC는 모든 HTTP 요청이 DispatcherServlet이라는 중앙 컨트롤러를 통해 처리됩니다.
DispatcherServlet은 사용자가 요청한 URL을 보고 어떤 컨트롤러가 이 요청을 처리할지 결정하고, 처리된 결과를 다시 사용자에게 반환하는 역할을 합니다.
DispatcherServlet의 핵심 기능은 프론트 컨트롤러 패턴을 사용하여 모든 요청을 한 곳에서 관리하는 것입니다.
이를 통해 다양한 요청을 일관되게 처리할 수 있습니다.
2. 어노테이션 기반 컨트롤러
전통적인 MVC 패턴에서는 URL과 컨트롤러 메서드를 매핑하는 과정이 복잡하고 번거로웠습니다. 하지만 스프링 MVC는 어노테이션을 사용해 매우 간편하게 컨트롤러와 URL을 매핑할 수 있습니다. 이를 통해 각 요청에 대한 처리가 간단해지고 가독성도 높아집니다.
@Controller
public class UserController {
// GET 요청에 대한 처리
@GetMapping("/users")
public String getUsers(Model model) {
// 사용자 목록을 모델에 추가
List<String> users = Arrays.asList("Alice", "Bob", "Charlie");
model.addAttribute("users", users);
// users.html 뷰로 반환
return "users";
}
}
@Controller: 해당 클래스를 컨트롤러로 지정합니다.
@GetMapping: 특정 GET 요청을 이 메서드와 매핑합니다.
스프링은 이러한 어노테이션 기반 컨트롤러를 통해 코드를 간결하게 유지하고, 복잡한 설정을 줄일 수 있습니다.
3. @RequestMapping을 통한 다양한 HTTP 메서드 처리
스프링 MVC에서는 @RequestMapping 어노테이션을 사용하여 다양한 HTTP 메서드(GET, POST, PUT, DELETE 등)에 따른 요청 처리를 간단하게 설정할 수 있습니다.
@Controller
@RequestMapping("/users")
public class UserController {
@GetMapping
public String getUsers(Model model) {
// GET 요청 처리 (사용자 목록 보기)
return "userList";
}
@PostMapping
public String createUser(User user) {
// POST 요청 처리 (새 사용자 등록)
return "redirect:/users";
}
@PutMapping("/{id}")
public String updateUser(@PathVariable Long id, User user) {
// PUT 요청 처리 (사용자 정보 업데이트)
return "redirect:/users";
}
@DeleteMapping("/{id}")
public String deleteUser(@PathVariable Long id) {
// DELETE 요청 처리 (사용자 삭제)
return "redirect:/users";
}
}
@RequestMapping: 클래스 레벨에서 기본 경로를 설정할 수 있습니다.
@GetMapping, @PostMapping 등: 각각의 메서드에 특정 HTTP 메서드를 매핑합니다. 이는 RESTful API 개발에 매우 유용합니다
4. ModelAndView 객체
ModelAndView 객체는 스프링 MVC에서 사용되는 뷰(View)와 데이터(Model)를 함께 관리할 수 있는 객체입니다.
컨트롤러에서 뷰 이름과 데이터를 한 번에 설정할 수 있게 해주며, 그 결과를 사용자에게 반환하는 데 사용됩니다.
이 방식은 보다 명확한 데이터 전달과 뷰 처리를 가능하게 합니다.
@Controller
public class ProductController {
@GetMapping("/product")
public ModelAndView getProduct() {
ModelAndView mav = new ModelAndView("product");
// 데이터 추가
mav.addObject("productName", "Laptop");
mav.addObject("price", 1500);
// 뷰와 데이터 반환
return mav;
}
}
- new ModelAndView("product"): 뷰 이름으로 "product"를 설정하여 product.html이나 product.jsp와 같은 뷰 파일로 이동할 수 있도록 합니다.
- addObject("productName", "Laptop"): "productName"이라는 키로 "Laptop" 값을 모델에 추가합니다.
- addObject("price", 1500): "price"라는 키로 1500이라는 값을 모델에 추가합니다.
ModelAndView의 역할
- View: 사용자에게 표시할 화면(HTML, JSP 등)의 이름을 설정합니다.
- Model: 뷰에 전달할 데이터를 저장합니다. 이 데이터를 뷰에서 사용해 동적으로 화면을 구성합니다.
ModelAndView의 주요 메서드
- setViewName(String viewName): 뷰 이름을 설정합니다.
- addObject(String attributeName, Object attributeValue): 뷰에서 사용할 데이터를 모델에 추가합니다.
5. RestController와 JSON 응답
전통적인 MVC 구조에서는 주로 HTML 뷰를 생성하지만, 스프링 MVC는 RESTful 웹 서비스 개발을 위해 @RestController 어노테이션을 제공합니다. 이를 통해 JSON 형식의 데이터를 반환하는 API를 쉽게 만들 수 있습니다.
@RestController
@RequestMapping("/api/users")
public class UserApiController {
@GetMapping
public List<User> getUsers() {
// 사용자 목록을 JSON으로 반환
return Arrays.asList(new User("Alice"), new User("Bob"), new User("Charlie"));
}
@PostMapping
public User createUser(@RequestBody User user) {
// 새 사용자 생성 후 JSON으로 반환
return user;
}
}
@RestController: 반환 값이 뷰가 아니라 JSON 또는 XML 등의 데이터 형식으로 바로 전달됩니다.
@RequestBody: 요청의 본문(body) 데이터를 자바 객체로 변환해줍니다.
6. HandlerMethodArgumentResolver
스프링 MVC는 HandlerMethodArgumentResolver를 사용해 컨트롤러 메서드의 파라미터를 유연하게 처리합니다. 이는 메서드의 파라미터에 맞춰 적절한 데이터를 자동으로 바인딩해주는 역할을 합니다.
@Controller
@RequestMapping("/orders")
public class OrderController {
// URL 경로에 있는 변수 사용
@GetMapping("/{orderId}")
public String getOrder(@PathVariable Long orderId, Model model) {
model.addAttribute("orderId", orderId);
return "orderDetail";
}
// 쿼리 파라미터 사용
@GetMapping
public String getOrdersByUser(@RequestParam String username, Model model) {
model.addAttribute("username", username);
return "orderList";
}
}
@PathVariable: URL 경로에 포함된 변수 값을 컨트롤러 메서드의 파라미터로 매핑합니다.
@RequestParam: 쿼리 스트링 파라미터를 메서드 파라미터로 받아올 수 있습니다.
7. ViewResolver로 뷰 선택
스프링 MVC는 요청을 처리한 후, 어떤 뷰를 사용할지 결정하기 위해 ViewResolver를 사용합니다. 기본적으로 ViewResolver는 템플릿 파일의 경로와 확장자를 관리합니다.
spring:
thymeleaf:
prefix: classpath:/templates/
suffix: .html
위 설정은 스프링 부트에서 Thymeleaf 템플릿 엔진을 사용할 때, 뷰 파일의 경로와 확장자를 자동으로 관리하는 예시입니다.
결론: 스프링만의 MVC 사용법
스프링 MVC는 전통적인 MVC 패턴을 발전시켜, 어노테이션 기반의 간편한 컨트롤러 매핑, RESTful 서비스 개발을 위한 @RestController, 유연한 데이터 바인딩, 다양한 뷰 처리 방식 등을 통해 웹 개발을 더욱 효율적으로 할 수 있게 도와줍니다.
이러한 스프링만의 기능을 활용하면 복잡한 웹 애플리케이션도 보다 간결하고 유연하게 개발할 수 있습니다.
⭐ 요청이 처리되는 흐름 알아보기
- 사용자가 /users URL을 요청합니다.
- DispatcherServlet이 요청을 받고, /users 요청을 처리할 컨트롤러를 찾습니다.
- UserController의 getUsers() 메서드가 실행됩니다. 이 메서드는 비즈니스 로직을 호출하여 사용자 데이터를 가져오고, 이 데이터를 Model에 담아 View(로 보냅니다.
- ViewResolver가 users.jsp 파일을 찾고, JSP가 데이터를 사용해 HTML을 렌더링합니다.
- 사용자에게 완성된 HTML이 응답으로 반환됩니다.
이 과정을 통해 모든 로직이 분리되고, 각 부분이 독립적으로 유지될 수 있습니다. 이 방식은 특히 유지보수와 확장성에서 큰 이점을 제공합니다.
⭐ MVC를 사용해야 하는 이유
1. 유지보수의 용이성
비즈니스 로직, 데이터, 그리고 화면을 각각 별도로 관리할 수 있어 수정할 때 한 부분만 수정하면 됩니다. 즉, 코드 수정이 더 쉬워지고, 버그 발생 가능성도 줄어듭니다.
2. 역할의 분리
각 부분이 명확한 역할을 가지고 있어, 팀 단위로 작업할 때도 유리합니다. 예를 들어, 프론트엔드 개발자는 View 부분만 신경 쓰고, 백엔드 개발자는 Controller와 Model 부분을 집중적으로 개발할 수 있습니다.
3. 코드의 재사용성
컨트롤러와 모델은 여러 뷰에서 재사용할 수 있습니다. 예를 들어, 같은 데이터를 JSP뿐만 아니라 API 형식으로도 쉽게 제공할 수 있습니다.
'기술 지식 쌓아가기 📚 > Backend 🍔' 카테고리의 다른 글
[Spring] WebFlux: 비동기와 반응형 프로그래밍의 새로운 장 🍃 (3) | 2024.09.27 |
---|---|
[Spring] 안전한 웹 개발의 시작: 스프링의 Validation, Data Binding, Type Conversion 🍃 (0) | 2024.09.27 |
[Spring] 서블릿(Servlet)에 대해 알려드리겠송! 😼🍃 (3) | 2024.09.18 |
[Spring] SpEL(Spring Expression Language)이란? Spring에서 표현식을 다루는 쉬운 방법 알아가기 🍃 (1) | 2024.09.16 |
[Spring] 스프링 컨테이너란? 의존성 주입의 마법 🍃 (0) | 2024.09.15 |