Spring MVC 개요 및 기본 개념
1. Spring MVC 소개
1) Spring MVC란?
Spring MVC는 Spring Framework가 제공하는 Servlet API 기반의 웹 프레임워크입니다. 기존의 서블릿과 JSP(JavaServer Pages) 기반의 개발 방식보다 구조화된 웹 애플리케이션 개발을 가능하게 합니다.
Spring MVC는 MVC(Model-View-Controller) 패턴과 Front Controller 패턴을 구현하여 웹 애플리케이션을 보다 효율적으로 관리할 수 있도록 돕습니다.
2) MVC 패턴이란?
MVC 패턴(Model-View-Controller Pattern)은 애플리케이션을 Model, View, Controller의 세 가지 역할로 구분하여 코드의 복잡도를 줄이고 유지보수를 쉽게 하는 패턴입니다.
구성 요소역할
| Model | 비즈니스 로직을 처리하고 데이터를 저장 및 관리 |
| View | 사용자에게 데이터를 보여주는 역할 (HTML, JSON 등) |
| Controller | 사용자의 요청을 받아 처리하고 Model과 View를 연결 |
JSP Model2 아키텍처와 비슷하지만, Spring MVC는 더 구조화된 방식으로 애플리케이션을 개발할 수 있도록 지원합니다.
3) Front Controller 패턴
Front Controller 패턴은 모든 웹 요청을 하나의 컨트롤러(DispatcherServlet)에서 받아서 처리하는 방식을 의미합니다.
- 요청을 일관되게 처리할 수 있으며,
- 공통적으로 수행해야 하는 작업(인증, 인가, 로깅 등)을 하나의 컨트롤러에서 관리할 수 있습니다.
Spring MVC에서 DispatcherServlet이 Front Controller 역할을 수행합니다.
2. Spring MVC의 핵심 구조
Spring MVC는 DispatcherServlet을 중심으로 동작합니다.
1) DispatcherServlet이란?
- 모든 웹 요청을 처리하는 Spring MVC의 중앙 컨트롤러
- Front Controller 패턴을 적용한 서블릿
- 사용자의 요청을 적절한 컨트롤러로 라우팅하고, 결과를 반환하는 역할
2) Spring MVC의 요청 처리 흐름
- 사용자가 요청을 보냄 (예: GET /home)
- DispatcherServlet이 요청을 받음
- HandlerMapping을 통해 요청을 처리할 컨트롤러 찾음
- Controller가 요청을 처리하고 Model을 생성
- ViewResolver를 이용하여 View를 결정
- View에서 사용자에게 응답을 렌더링
3. Spring Boot에서의 Spring MVC
Spring Boot는 Spring MVC를 보다 쉽게 사용할 수 있도록 도와줍니다.
1) 내장 웹 서버 제공
Spring Boot는 Tomcat, Jetty, Undertow 등의 내장 WAS(Web Application Server)를 제공하여 별도의 서버 설치 없이 애플리케이션을 실행할 수 있습니다.
2) spring-boot-starter-web 의존성 추가
Spring Boot에서 Spring MVC를 사용하려면 **spring-boot-starter-web**을 추가하면 자동으로 설정됩니다.
Maven 설정
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Gradle 설정
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
}
3) 자동 구성 (WebMvcAutoConfiguration)
Spring Boot는 자동으로 Spring MVC 설정을 구성해주며, @EnableWebMvc 없이도 웹 애플리케이션을 실행할 수 있습니다.
주요 자동 구성 클래스
| 클래스 | 역할 |
| EnableWebMvcConfiguration | RequestMappingHandlerMapping을 생성하고 설정 |
| WebMvcConfigurer | 개발자가 기본 설정을 커스터마이징할 수 있도록 제공 |
예제: WebMvcConfigurer 사용
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
}
4. Spring MVC : 컨트롤러 작성하기
1) 프로젝트 생성
Spring Boot 프로젝트를 생성하고 Spring Web, Thymeleaf를 선택합니다.
2) Controller 클래스 작성
Spring MVC에서는 @Controller 또는 **@RestController**를 사용하여 컨트롤러를 정의합니다.
@Controller
public class HomeController {
@GetMapping("/")
public String index() {
return "index";
}
}
- @Controller → Spring MVC의 컨트롤러로 등록
- @GetMapping("/") → 루트 URL(/) 요청을 처리
- return "index"; → index.html 뷰를 반환
3) View 파일 작성 (Thymeleaf)
Spring Boot에서 Thymeleaf 템플릿 엔진을 사용하여 View를 작성할 수 있습니다.
src/main/resources/templates/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Spring MVC</title>
</head>
<body>
<h1>Hello, Spring MVC!</h1>
</body>
</html>
4) 애플리케이션 실행
Spring Boot는 main() 메서드를 실행하면 내장 WAS(Tomcat)가 자동으로 실행됩니다.
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
- 브라우저에서 http://localhost:8080 접속 시 Hello, Spring MVC!가 출력됩니다.
5. Request Mapping (요청 매핑)
Spring MVC에서는 @RequestMapping 및 HTTP 메서드별 매핑 어노테이션(@GetMapping, @PostMapping 등)을 사용하여 클라이언트의 요청을 컨트롤러 메서드와 연결합니다.
1) @RequestMapping
- 요청 URL과 컨트롤러 메서드를 매핑하는 어노테이션
- HTTP 메서드(GET, POST, PUT, DELETE 등)를 지정할 수 있음
@Controller
@RequestMapping("/users") // 모든 메서드는 '/users' 경로를 기준으로 동작
public class UserController {
@GetMapping("/{id}") // GET /users/{id}
public String getUser(@PathVariable Long id, Model model) {
model.addAttribute("userId", id);
return "userView";
}
@PostMapping // POST /users
public String createUser(@RequestParam String name) {
System.out.println("User Created: " + name);
return "redirect:/users";
}
}
2) 경로 변수 (@PathVariable)
- URL의 일부를 변수로 사용 가능
- {}를 사용하여 경로 변수 지정
@GetMapping("/users/{id}")
public String getUser(@PathVariable("id") Long userId, Model model) {
model.addAttribute("userId", userId);
return "userView";
}
3) 요청 파라미터 (@RequestParam)
- URL의 쿼리 파라미터를 메서드 인자로 받을 때 사용
@GetMapping("/search")
public String search(@RequestParam String keyword, Model model) {
model.addAttribute("keyword", keyword);
return "searchResult";
}
2. Model (데이터 전달 객체)
1) Model이란?
- Controller에서 생성한 데이터를 View에 전달할 때 사용
- Model, ModelMap, ModelAndView를 이용하여 데이터를 전달할 수 있음
2) Model 사용 예제
@GetMapping("/greeting")
public String greeting(Model model) {
model.addAttribute("message", "Hello, Spring MVC!");
return "greetingView";
}
- model.addAttribute("message", "Hello, Spring MVC!") → View에서 message를 사용할 수 있음
3) ModelAndView 사용 예제
- ModelAndView는 Model과 View 정보를 함께 설정할 수 있음
@GetMapping("/welcome")
public ModelAndView welcome() {
ModelAndView mav = new ModelAndView("welcomeView");
mav.addObject("message", "Welcome to Spring MVC");
return mav;
}
6. ViewResolver (뷰 리졸버)
1) ViewResolver란?
- 컨트롤러가 반환하는 뷰 이름을 실제 View 파일로 매핑하는 역할
- 기본적으로 Thymeleaf, JSP, FreeMarker 등을 사용 가능
2) Spring Boot의 기본 설정
Spring Boot에서는 Thymeleaf가 기본적으로 설정되어 있으며, application.properties에서 경로를 수정할 수 있음.
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
- prefix: 뷰 파일이 위치하는 디렉토리 설정
- suffix: 뷰 파일의 확장자 설정
3) Thymeleaf 뷰 파일 예제 (greetingView.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Greeting</title>
</head>
<body>
<h1 th:text="${message}"></h1>
</body>
</html>
- th:text="${message}" → Model에서 전달된 message 값을 출력
7. Interceptor (인터셉터)
1) Interceptor란?
- Spring MVC에서 요청이 컨트롤러에 도달하기 전/후에 추가 작업을 수행하는 기능
- 인증, 로깅, 권한 체크 등에 활용
2) 인터셉터 구현하기
public class LoggingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("[Request] URI: " + request.getRequestURI());
return true; // true를 반환해야 요청이 계속 진행됨
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("[Response] Status: " + response.getStatus());
}
}
3) 인터셉터 등록
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor()).addPathPatterns("/**");
}
}
- addInterceptor(new LoggingInterceptor()) → 인터셉터 등록
- addPathPatterns("/**") → 모든 요청에 대해 인터셉터 적용
8. 예외 처리 (Exception Handling)
1) 예외 처리의 필요성
웹 애플리케이션에서는 예상치 못한 오류가 발생할 수 있습니다. 이를 적절하게 처리하지 않으면 사용자에게 불친절한 에러 페이지가 표시되거나, 보안적으로 취약한 정보가 노출될 수 있습니다.
Spring MVC에서는 @ExceptionHandler, @ControllerAdvice 등을 이용하여 예외를 중앙에서 처리할 수 있습니다.
2) @ExceptionHandler를 이용한 개별 컨트롤러 예외 처리
특정 컨트롤러에서 발생하는 예외를 처리할 때 사용합니다.
@Controller
public class UserController {
@GetMapping("/users/{id}")
public String getUser(@PathVariable Long id) {
if (id <= 0) {
throw new IllegalArgumentException("유효하지 않은 사용자 ID입니다.");
}
return "userView";
}
@ExceptionHandler(IllegalArgumentException.class)
public String handleIllegalArgumentException(IllegalArgumentException e, Model model) {
model.addAttribute("errorMessage", e.getMessage());
return "errorPage";
}
}
- 특정 예외(IllegalArgumentException) 발생 시 errorPage.html로 이동
- 모델에 errorMessage를 추가하여 뷰에서 출력 가능
3) @ControllerAdvice를 이용한 전역 예외 처리
모든 컨트롤러에서 발생하는 예외를 공통으로 처리할 때 사용합니다.
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public String handleException(Exception e, Model model) {
model.addAttribute("errorMessage", "오류가 발생했습니다: " + e.getMessage());
return "errorPage";
}
}
- 모든 컨트롤러에서 발생하는 Exception을 처리하는 전역 예외 핸들러
9. Form 데이터 처리
1) @ModelAttribute를 이용한 Form 데이터 바인딩
Form 데이터를 객체로 자동 매핑할 수 있도록 도와줍니다.
public class UserForm {
private String name;
private int age;
// Getter & Setter
}
@Controller
public class UserController {
@PostMapping("/register")
public String registerUser(@ModelAttribute UserForm userForm) {
System.out.println("이름: " + userForm.getName());
System.out.println("나이: " + userForm.getAge());
return "redirect:/success";
}
}
- 사용자가 제출한 form 데이터가 UserForm 객체로 자동 매핑됩니다.
- name, age 값이 UserForm 객체의 필드에 저장됨.
10. 입력값 검증 (Validation)
1) Spring의 Bean Validation (@Valid 및 @Validated)
Spring Boot에서는 Bean Validation (JSR-303, JSR-380)를 이용한 입력값 검증이 가능하며, @Valid 또는 @Validated를 활용합니다.
2) @Valid를 사용한 입력값 검증
import jakarta.validation.constraints.*;
public class UserForm {
@NotBlank(message = "이름은 필수 입력 항목입니다.")
private String name;
@Min(value = 18, message = "나이는 18세 이상이어야 합니다.")
private int age;
// Getter & Setter
}
@Controller
public class UserController {
@PostMapping("/register")
public String registerUser(@Valid @ModelAttribute UserForm userForm, BindingResult result) {
if (result.hasErrors()) {
return "registerForm";
}
return "redirect:/success";
}
}
- @NotBlank, @Min 등의 검증 어노테이션을 이용해 입력값을 검증
- BindingResult를 사용하여 검증 오류가 발생한 경우 다시 입력 폼을 보여줌
3) 커스텀 Validator 구현
Spring의 Validator 인터페이스를 구현하여 커스텀 검증 로직을 작성할 수도 있습니다.
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
public class UserFormValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return UserForm.class.equals(clazz);
}
@Override
public void validate(Object target, Errors errors) {
UserForm userForm = (UserForm) target;
if (userForm.getAge() < 18) {
errors.rejectValue("age", "Invalid.age", "나이는 18세 이상이어야 합니다.");
}
}
}
@Controller
public class UserController {
private final UserFormValidator validator;
public UserController(UserFormValidator validator) {
this.validator = validator;
}
@PostMapping("/register")
public String registerUser(@ModelAttribute UserForm userForm, BindingResult result) {
validator.validate(userForm, result);
if (result.hasErrors()) {
return "registerForm";
}
return "redirect:/success";
}
}
11. 파일 업로드 (File Upload)
1) 파일 업로드 개요
Spring MVC에서는 사용자가 업로드한 파일을 처리할 수 있도록 MultipartResolver를 제공합니다. 파일 업로드를 처리하려면 Spring Boot에서 기본 제공하는 StandardServletMultipartResolver 또는 CommonsMultipartResolver를 사용할 수 있습니다.
2) 파일 업로드 설정 (Spring Boot)
Spring Boot에서는 application.properties 또는 application.yml에서 파일 업로드를 설정할 수 있습니다.
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=5MB
spring.servlet.multipart.max-request-size=10MB
- max-file-size: 업로드할 수 있는 개별 파일의 최대 크기
- max-request-size: 한 번의 요청에서 업로드할 수 있는 전체 파일 크기
3) 파일 업로드 컨트롤러 구현
@Controller
public class FileUploadController {
@PostMapping("/upload")
public String handleFileUpload(@RequestParam("file") MultipartFile file, Model model) {
if (!file.isEmpty()) {
try {
byte[] bytes = file.getBytes();
Path path = Paths.get("uploads/" + file.getOriginalFilename());
Files.write(path, bytes);
model.addAttribute("message", "파일 업로드 성공: " + file.getOriginalFilename());
} catch (IOException e) {
model.addAttribute("message", "파일 업로드 실패: " + e.getMessage());
}
} else {
model.addAttribute("message", "업로드할 파일을 선택하세요.");
}
return "uploadResult";
}
}
- @RequestParam("file") MultipartFile file: 클라이언트가 업로드한 파일을 매개변수로 받음
- file.getBytes(): 파일의 내용을 바이트 배열로 변환
- Files.write(path, bytes): 지정된 경로에 파일 저장
4) 파일 업로드를 위한 HTML 폼 예제
<form method="POST" enctype="multipart/form-data" action="/upload">
<input type="file" name="file" />
<button type="submit">업로드</button>
</form>
12. 다국어 지원 (Internationalization, i18n)
1) 다국어 지원 개요
Spring MVC는 다국어 지원을 위해 MessageSource를 활용하며, 사용자의 언어 설정에 따라 다른 언어의 메시지를 제공할 수 있습니다.
2) messages.properties 파일 생성
src/main/resources/messages.properties에 기본 메시지 파일을 생성하고, 특정 언어에 따라 파일을 추가합니다.
# 기본 (한국어)
greeting=안녕하세요
# 영어 (messages_en.properties)
greeting=Hello
# 일본어 (messages_ja.properties)
greeting=こんにちは
3) MessageSource 설정
Spring Boot에서는 자동으로 MessageSource를 설정하지만, 커스텀 설정도 가능합니다.
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
4) 다국어 메시지 사용 방법
@Controller
public class GreetingController {
@Autowired
private MessageSource messageSource;
@GetMapping("/greeting")
public String getGreeting(Locale locale, Model model) {
String greetingMessage = messageSource.getMessage("greeting", null, locale);
model.addAttribute("message", greetingMessage);
return "greetingView";
}
}
- Locale locale: 현재 요청의 언어 설정을 자동으로 감지
- messageSource.getMessage("greeting", null, locale): 다국어 메시지 가져오기
5) 다국어 메시지를 HTML에서 사용하기 (Thymeleaf)
<p th:text="#{greeting}"></p>
13. Spring MVC 테스트 방법
1) Spring Boot 테스트 환경 설정
Spring Boot에서 컨트롤러를 테스트하려면 spring-boot-starter-test 의존성이 필요합니다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2) MockMvc를 사용한 컨트롤러 테스트
MockMvc를 사용하면 실제 서버를 실행하지 않고 HTTP 요청을 테스트할 수 있습니다.
@WebMvcTest(GreetingController.class)
class GreetingControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void greetingShouldReturnMessage() throws Exception {
mockMvc.perform(get("/greeting").locale(Locale.ENGLISH))
.andExpect(status().isOk())
.andExpect(content().string(containsString("Hello")));
}
}
- @WebMvcTest(GreetingController.class): GreetingController만 테스트하도록 지정
- mockMvc.perform(get("/greeting").locale(Locale.ENGLISH)): 가상의 요청 실행
- andExpect(status().isOk()): HTTP 상태 코드 검증
- andExpect(content().string(containsString("Hello"))): 응답 내용 검증
3) 통합 테스트 (@SpringBootTest)
실제 애플리케이션의 동작을 확인하는 통합 테스트를 수행할 수 있습니다.
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class HttpRequestTest {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
void greetingShouldReturnDefaultMessage() {
String response = this.restTemplate.getForObject("http://localhost:" + port + "/greeting", String.class);
assertThat(response).contains("Hello");
}
}
- @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT): 랜덤 포트에서 애플리케이션 실행
- @LocalServerPort: 사용 중인 포트 정보 가져오기
- TestRestTemplate을 사용하여 실제 HTTP 요청 테스트
'개발 이론 > Spring' 카테고리의 다른 글
| SSR (0) | 2025.03.18 |
|---|---|
| Spring, Spring Boot, JPA의 정의 (0) | 2025.03.01 |
| Spring Security (0) | 2025.02.20 |
| Spring Boot란? (0) | 2025.02.20 |