Spring 입문주차 - 2

이진일·2026년 4월 7일
post-thumbnail

Spring MVC란?

MVC 디자인 패턴

  • MVC 패턴은 소프트웨어를 구성하는 요소들을 Model, View, Controller로 구분하여 각각의 역할을 분리한다.

Model

  • 데이터와 비즈니스 로직을 담당한다
  • 데이터베이스와 연동하여 데이터를 저장하고 불러오는 등의 작업을 수행한다.

View

  • 사용자 인터페이스를 담당한다.
  • 사용자가 보는 화면을 구현한다.

Controller

  • Model과 View 사이의 상호작용을 조정하고 제어한다.
  • 사용자의 입력을 받아 Model에 전달하고, Model의 결과를 바탕으로 View를 업데이트한다.

MVC 패턴은 소프트웨어를 구성하는 요소들을 분리함으로써 코드의 재사용성과 유지보수성을 높이고, 개발자들 간의 협업을 용이하게 한다. 따라서 소프트웨어를 개발할 때 MVC 패턴을 적용하여 구조를 잘 설계하는 것이 중요하다.

Spring MVC란?

Spring MVC는 Servlet API를 기반으로 구축된 웹 프레임워크이다.
Spring MVC는 중앙에 있는 DispatcherServlet이 요청을 처리하기 위한 공유 알고리즘을 제고하는 Front Controller 패턴을 중심으로 설계되어 있으며 이 모델은 유연하고 다양한 워크 플로우를 지원한다.

Servlet

Servlet은 자바를 사용하여 웹 페이지를 동적으로 생성하는 서버 측 프로그램이다.

사용자가 (HTTP) API 요청했을 때 서버의 서블릿은 아래 그림처럼 동작한다.

1. 사용자가 Client를 통해 서버에 HTTP Request(API 요청)한다.
2. 요청받은 서블릿 컨테이너는 HttpServletRequest, HttpServletResponse 객체를 생성한다.
3. 설정된 정보(web.xml)를 통해 어떠한 Servlet에 대한 요청인지 찾는다.
4. 해당 Servlet에서 service 메서드를 호출한 뒤 클라이언트의 요청 Method에 따라 doGet 또는 doPost 메서드를 호출한다.
5. 호출한 메서드들의 결과를 그대로 반환하거나 동적 페이지를 생성한 뒤 HttpServletResponse 객체에 응답을 담아 클라이언트에게 반환한다.
6. 응답이 완료되면 생성한 HttpServletRequest, HttpServletResponse 객체를 소멸한다.

Front Controller

모든 API 요청을 위의 서블릿의 동작 방식에 맞춰 코드를 구현하면 너무 많은 Servlet 클래스를 구현해야 한다. 따라서 Spring은 DispatcherServlet을 사용하여 Front Controller 패턴 방식으로 API요청을 효율적으로 처리하고 있다.

Front Controller패턴은 아래 그림과 같이 동작한다.

1. 클라이언트에서 HTTP요청이 들어오면 DispatcherServlet 객체가 요청을 분석한다.
2. DispatcherServlet 객체는 분석한 데이터를 토대로 Handler mapping을 통해 Controller를 찾아 요청을 전달한다.

@Controller 애너테이션이 달려있는 클래스를 찾아서 HTTP Method와 URL이 일치하는 메서드를 찾아 요청을 전달하는 것이다.
3. 요청을 받은 Controller는 요청에 대한 처리를 완료 후 데이터(Model)와 View정보를 전달한다.
4. ViewResolver를 통해 View에 Model을 적용하여 View를 Client에게 응답을 전달한다.


Controller 이해하기

Spring에서 DispatcherServlet으로 반복되고 귀찮은 일은 다 해주기 때문에 우리는 Controller만 잘 만들면 된다.

@Controller

@Controller
public class HelloController {
    @GetMapping("/api/hello")
    @ResponseBody
    public String hello() {
        return "Hello World!";
    }
}

클래스를 생성하여 @Controller 애너테이션을 붙여서 Controller 역할을 수핼할 수 있도록 등록한다.

HTTP Method매핑 애너테이션

  • @GetMapping("/api-url")
  • @PostMapping("/api-url")
  • @PutMappging("/api-url")
  • @DeleteMapping("/api-url")
    등등 HTTP Method와 맞는 애너테이션을 메서드에 붙여 사용한다.
@Controller
public class HelloController {
    @GetMapping("/api/hello")
    @ResponseBody
    public String hello() {
        return "Hello World!";
    }
    
    @PostMapping("/api/post")
    @ResponseBody
    public String post() {
        return "POST Method 요청";
    }
    
    @PutMapping("/api/put")
    @ResponseBody
    public String put() {
        return "PUT Method 요청";
    }
    
    @DeleteMapping("/api/delete")
    @ResponseBody
    public String delete() {
        return "DELETE Method 요청";
    }
}

@RequestMapping

Controller에서 각 메서드마다 api url을 명시하는데 하나의 Controller클래스내에서는 api url이 중복되는 부분이 많이 생긴다. @RequestMapping 애너테이션을 사용하면 중복되는 부분을 한번만 작성하여 사용할 수 있다.

@Controller
@RequestMapping("/api")
public class HelloController {
    @GetMapping("/hello")
    @ResponseBody
    public String hello() {
        return "Hello World!";
    }

    @GetMapping("/get")
    @ResponseBody
    public String get() {
        return "GET Method 요청";
    }

    @PostMapping("/post")
    @ResponseBody
    public String post() {
        return "POST Method 요청";
    }

    @PutMapping("/put")
    @ResponseBody
    public String put() {
        return "PUT Method 요청";
    }

    @DeleteMapping("/delete")
    @ResponseBody
    public String delete() {
        return "DELETE Method 요청";
    }
}

@ResponseBody

Controller에서 메서드가 String을 반환할 때는 기본적으로 해당 문자열을 resources경로에서 문자열에 맞는 html파일이 있는 찾는다.

따라서 순수하게 String값을 반환할 것이다라는 의미로 메서드에 @ResponseBody 애너테이션을 붙여 사용할 수 있다.

@RestController

= @Controller + @ResponseBody
@RestController를 사용하면 해당 클래스의 모든 메서드에 @ResponseBody 애너테이션이 추가되는 효과를 부여할 수 있다.


데이터를 Client에 반환하는 방법(JSON)

요즘에는 프론트엔드와 백엔드가 느슨하게 결합하는 방식을 많이 사용해서 서버에서 직접 view를 반환하기 보다 요청에 맞는 데이터만 반환한다. 이렇게 데이터를 보낼 때 많이 사용하는 데이터 교환 포맷이 JSON이다.

JSON 데이터 반환 방법

  1. 반환값이 String인 경우 Java는 JSON타입을 지원하지 않기 때문에 JSON형태로 문자열을 만들어서 반환해야 한다.
  2. 반환값이 String 외 Java 클래스일 때는 Spring이 자동으로 Java 객체를 JSON으로 변환해준다.

PathVariable과 RequestParam

Path Variable 방식

// [Request sample]
// GET http://localhost:8080/hello/request/star/Robbie/age/95
@GetMapping("/star/{name}/age/{age}")
@ResponseBody
public String helloRequestPath(@PathVariable String name, @PathVariable int age)
{
    return String.format("Hello, @PathVariable.<br> name = %s, age = %d", name, age);
}
  • 데이터를 받기 위해서는 /star/{name}/age/{age} 이처럼 URL 경로에서 데이터를 받고자 하는 위치의 경로에 {data} 중괄호를 사용한다.
  • 해당 요청 메서드 파라미터에 @PathVariable 애너테이션과 함께 {name} 중괄호에 선언한 변수명과 변수타입을 선언하여 해당 경로의 데이터를 받아와 사용한다.

Request Param 방식

// [Request sample]
// GET http://localhost:8080/hello/request/form/param?name=Robbie&age=95
@GetMapping("/form/param")
@ResponseBody
public String helloGetRequestParam(@RequestParam String name, @RequestParam int age) {
    return String.format("Hello, @RequestParam.<br> name = %s, age = %d", name, age);
}
  • 데이터를 받기 위해서는 ?name=Robbie&age=95 에서 key 부분에 선언한 name과 age를 사용하여 value에 선언된 Robbie, 95 데이터를 받아온다.
  • 해당 요청 메서드 파라미터에 @RequestParam 애너테이션과 함께 key 부분에 선언한 변수명과 변수타입을 선언하면 데이터를 받아온다.

POST방식에서도 사용할 수 있는데 이때 데이터는 HTTP Body에 담겨져 전달된다. @RequestParam으로 HTTP Body에 있는 데이터를 받아오는것이다.

@RequestParam은 생략이 가능하다.

@RequestParam애너테이션에는 required 옵션이 있는데 기본값은 true로 해당 애너테이션이 붙은 값은 서버로 전달될 때 포함되어 있지 않으면 오류가 발생한다. 서버로 데이터를 전송할 때 선택적으로 보내도 되는 값은 required 옵션을 false로 설정하면 된다.


HTTP 데이터를 객체로 처리하는 방법

@ModelAttribute

// [Request sample]
// POST http://localhost:8080/hello/request/form/model
// Header
//  Content type: application/x-www-form-urlencoded
// Body
//  name=Robbie&age=95
@PostMapping("/form/model")
@ResponseBody
public String helloRequestBodyForm(@ModelAttribute Star star) {
    return String.format("Hello, @ModelAttribute.<br> (name = %s, age = %d) ", star.name, star.age);
}
  • HTML의 form 태그를 사용하여 POST 방식으로 HTTP 요청을 보낼 때 데이터는 HTTP Body에 name=Robbie&age=95 형태로 담겨져 서버로 전달된다.
  • 해당 데이터를 Java의 객체 형태로 받는 방법은 @ModelAttribute 애너테이션을 사용한 후 Body 데이터를 받아올 객체를 선언한다.
// [Request sample]
// GET http://localhost:8080/hello/request/form/param/model?name=Robbie&age=95
@GetMapping("/form/param/model")
@ResponseBody
public String helloRequestParam(@ModelAttribute Star star) {
    return String.format("Hello, @ModelAttribute.<br> (name = %s, age = %d) ", star.name, star.age);
}
  • Query String 방식일 때 파라미터가 많아지면 @PathVariable로 하나하나 받기 힘들다.
  • 이 때도 @ModelAttribute 애너테이션을 사용하여 객체로 데이터를 받을 수 있다.

@RequestBody

HTTP Body에 JSON데이터를 담아 서버에 전달할 때는 @ResponseBody 애너테이션을 사용하여 데이터를 받을 수 있다.

// [Request sample]
// POST http://localhost:8080/hello/request/form/json
// Header
//  Content type: application/json
// Body
//  {"name":"Robbie","age":"95"}
@PostMapping("/form/json")
@ResponseBody
public String helloPostRequestJson(@RequestBody Star star) {
    return String.format("Hello, @RequestBody.<br> (name = %s, age = %d) ", star.name, star.age);
}
  • HTTP Body에 {"name":"Robbie","age":"95"} JSON 형태로 데이터가 서버에 전달되었을 때 @RequestBody 애너테이션을 사용해 데이터를 객체 형태로 받을 수 있다.

⚠️데이터를 Java객체로 받을 때 주의할 점이 있다.
해당 객체의 필드에 데이터를 넣어주기 위해서는 setter or getter 또는 오버로딩된 생성자가 있어야 한다.

0개의 댓글