๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๋ฐฑ์—”๋“œ/spring&springboot

[๋‚ด์ผ๋ฐฐ์›€์บ ํ”„_Spring Boot ์ž…๋ฌธ์ฃผ์ฐจ] Spring MVC

by alswlfl 2026. 6. 23.

Spring MVC๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

MVC ๋””์ž์ธ ํŒจํ„ด

๋””์ž์ธ ํŒจํ„ด: ํšจ์œจ์ ์ธ ๋ฐฉ๋ฒ•๋“ค์„ ํŒจํ„ดํ™” ํ•ด๋†“์€ ๊ฒƒ

 

MVC๋Š” ์†Œํ”„ํŠธ์›จ์–ด ๋””์ž์ธ ํŒจํ„ด ์ค‘ ํ•˜๋‚˜๋กœ, ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ์š”์†Œ๋“ค์„ Model-View-Controller๋กœ ๊ตฌ๋ถ„ํ•ด ๊ฐ๊ฐ์˜ ์—ญํ• ์„ ๋ถ„๋ฆฌ

์ฝ”๋“œ์˜ ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ ๋†’์ด๊ณ , ๊ฐœ๋ฐœ์ž๋“ค ๊ฐ„์˜ ํ˜‘์—…์„ ์šฉ์ดํ•˜๊ฒŒ ํ•จ

  • Model: ๋ฐ์ดํ„ฐ์™€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋‹ด๋‹น
    • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๋™
    • ๋ฐ์ดํ„ฐ ์ €์žฅ, ์กฐํšŒ
  • View: ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค ๋‹ด๋‹น
    • ์‚ฌ์šฉ์ž๊ฐ€ ๋ณด๋Š” ํ™”๋ฉด์ด๋‚˜ ๋ฒ„ํŠผ ํผ ๋“ฑ
  • Controller: Model๊ณผ View ์‚ฌ์ด์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ์กฐ์ •ํ•˜๊ณ  ์ œ์–ด ๊ฐ€๋Šฅ
    • ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์„ ์ž…๋ ฅ๋ฐ›์•„ Model์— ์ „๋‹ฌ
    • ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ View ์—…๋ฐ์ดํŠธ

MVC ํŒจํ„ด


Spring MVC

Spring ๊ณต์‹๋ฌธ์„œ์—์„œ Spring MVC๋Š” DispatcherServlet์ด ์ค‘์•™์—์„œ HTTP ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š”๋ฐ ์ด๋Š” Front Controller ํŒจํ„ด์œผ๋กœ ์„ค๊ณ„๋˜์–ด ์žˆ๋‹ค๊ณ  ์„ค๋ช…ํ•˜๊ณ  ์žˆ์Œ.

์ฆ‰, Spring์—์„œ MVC ๋””์ž์ธ ํŒจํ„ด์„ ์ ์šฉํ•ด HTTP ์š”์ฒญ์„ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ์Œ

 

Servlet(์„œ๋ธ”๋ฆฟ)

Servlet(์„œ๋ธ”๋ฆฟ)์€ ์ž๋ฐ”๋ฅผ ์‚ฌ์šฉํ•ด ์›น ํŽ˜์ด์ง€๋ฅผ ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•˜๋Š” ์„œ๋ฒ„ ์ธก ํ”„๋กœ๊ทธ๋žจ ํ˜น์€ ๊ทธ ์‚ฌ์–‘

* ํ†ฐ์บฃ(Tomcat) ์•ˆ์—์„œ ์„œ๋ธ”๋ฆฟ(Servlet) ๋™์ž‘

 

[์‚ฌ์šฉ์ž๊ฐ€ HTTP API ์š”์ฒญ ์‹œ ์„œ๋ฒ„์˜ ์„œ๋ธ”๋ฆฟ ๋™์ž‘ ๊ณผ์ •]

์‚ฌ์šฉ์ž๊ฐ€ HTTP API ์š”์ฒญ ์‹œ ์„œ๋ฒ„์˜ ์„œ๋ธ”๋ฆฟ ๋™์ž‘ ๊ณผ์ •

1. ํด๋ผ์ด์–ธํŠธ๊ฐ€ HTTP๋ฅผ ํ†ตํ•ด Request ์š”์ฒญ

2. ์š”์ฒญ์„ ๋ฐ›์€ ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๋Š” HttpServletRequest์™€ HttpServletResponse ๊ฐ์ฒด ์ƒ์„ฑ

  • ๋‘ ๊ฐ์ฒด๋Š” ์•ฝ์†๋œ HTTP์˜ ๊ทœ๊ฒฉ์„ ๋งž์ถ”๋ฉด์„œ ์‰ฝ๊ฒŒ HTTP์— ๋‹ด๊ธด ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“ค์–ด๋‚ธ ๊ฐ์ฒด
  • ์ฆ‰, ์„œ๋ธ”๋ฆฟ์ด ๊ฐœ๋ฐœ์ž๋ฅผ ๋Œ€์‹ ํ•ด์„œ HTTP๋ฅผ ๋ถ„์„ํ•ด์„œ ๊ทธ ์•ˆ์— ๋“ค์–ด์žˆ๋Š” ๋ฐ์ดํ„ฐ์™€ ์ •๋ณด๋“ค์„ ๊ฐ์ฒด์— ๋„ฃ์–ด์คŒ

3. API ์š”์ฒญ ํ™•์ธ ํ›„, ๊ทธ ์š”์ฒญ์ด ์–ด๋–ค ์„œ๋ธ”๋ฆฟ๊ณผ ๋งค์นญ์ด ๋˜๋Š”์ง€ ๋ถ„์„ํ•จ(web.xml)

4. ๋งค์นญ๋˜๋Š” ์„œ๋ธ”๋ฆฟ์„ ์ฐพ์œผ๋ฉด, ํ•ด๋‹น ์„œ๋ธ”๋ฆฟ์—์„œ service() ํ˜ธ์ถœ

5. ์„œ๋น„์Šค ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ํ›„ HTTP ์š”์ฒญ์ด ์–ด๋–ค ๋ฉ”์„œ๋“œ ๋ฐฉ์‹์ธ์ง€ ํ™•์ธํ•œ ํ›„ ํ•ด๋‹น ๋ฐฉ์‹์— ๋งž๋Š” ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ

6. ํ˜ธ์ถœํ•œ ๋ฉ”์„œ๋“œ๋“ค์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ด HttpServletResponse ๊ฐ์ฒด์— ์‘๋‹ต์„ ๋‹ด์•„์„œ ํด๋ผ์ด์–ธํŠธ์— ๋ฐ˜ํ™˜

 

DispatcherServlet

Dispatcher๋Š” '์ „๋‹ฌํ•˜๋‹ค'๋ผ๋Š” ์˜๋ฏธ๋กœ, DispatcherServlet์€ ์ „๋‹ฌํ•˜๋Š” ์„œ๋ธ”๋ฆฟ

 

์—ฌ๋Ÿฌ ๊ฐœ์˜ API์— ๋งค์นญ๋˜๋Š” Servlet์˜ ๊ฐ์ฒด๋“ค์„ ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ตฌํ˜„ํ•˜๊ณ , ์–ด๋–ค ๋ฉ”์„œ๋“œ ๋ฐฉ์‹์ธ์ง€ ํ™•์ธํ•ด์„œ ๋ฐฉ์‹์— ๋งž๋Š” ๋ฉ”์„œ๋“œ ํ˜ธ์ถœํ•˜๊ณ  ์ˆ˜ํ–‰ํ•ด์ฃผ๋Š” ๋“ฑ ๊ฐœ๋ฐœ์ž๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์ž‘์—…๋“ค์„ ์ž‘์„ฑํ•ด์ฃผ์–ด์•ผํ•จ

์ด๋Ÿฌํ•œ ๋ถˆํŽธํ•จ์„ ์Šคํ”„๋ง์—์„œ ํ•ด๊ฒฐํ•ด์ฃผ๊ธฐ ์œ„ํ•ด DispatcherServlet์„ ๋งŒ๋“ค๊ณ , Front ์ปจํŠธ๋กค ํŒจํ„ด ๋ฐฉ์‹์œผ๋กœ ๋ชจ๋“  API๋“ค์„ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ์Œ

๋””์ŠคํŒจ์ฒ˜ ์„œ๋ธ”๋ฆฟ(Front Controller) ๋™์ž‘ ๊ณผ์ •

1. ๋ธŒ๋ผ์šฐ์ €์—์„œ Request ์š”์ฒญ ๋“ค์–ด์˜ด

2. DispatcherServlet์ด ํ•ด๋‹น ์š”์ฒญ ๋ถ„์„ ํ›„, ๋ถ„์„ํ•œ ํ† ๋Œ€๋กœ Handler mapping์„ ํ†ตํ•ด Controller๋ฅผ ์ฐพ์•„ ์š”์ฒญ ์ „๋‹ฌ

  • Handler mapping์—๋Š” API path์™€ Controller ๋ฉ”์„œ๋“œ๊ฐ€ ๋งค์นญ๋˜์–ด ์žˆ์Œ
@RestController
public class HelloController {
    @GetMapping("/api/hello")
    public String hello() {
    	return "Hello World!";
    }
}

 

3. Controller๋Š” ํ•ด๋‹น ์š”์ฒญ์— ๋Œ€ํ•œ ์ž‘์—… ์ˆ˜ํ–‰ ํ›„, ์š”์ฒญ์— ๋Œ€ํ•œ ๊ฒฐ๊ณผ๋ฅผ Model์— ๋‹ด๊ณ , View์— ๋Œ€ํ•œ ์ •๋ณด๋„ ์ „๋‹ฌํ•ด์คŒ

4. DispatcherServlet์„ ํ†ตํ•ด Client์—๊ฒŒ ์‘๋‹ต ์ „๋‹ฌ

  • ViewResolver๋ฅผ ํ†ตํ•ด View์— Model ์ ์šฉํ•ด View๋ฅผ Client์—๊ฒŒ ์‘๋‹ต์œผ๋กœ ์ „๋‹ฌ

Controller ์ดํ•ดํ•˜๊ธฐ

Controller์˜ ์žฅ์ 

Front Controller ํŒจํ„ด์ด ์—†๋Š” ๊ฒฝ์šฐ

  • Spring MVC์— Front Controller ํŒจํ„ด์ด ์ ์šฉ๋˜์–ด ์žˆ์ง€ ์•Š๋Š” ๊ฒฝ์šฐ, ๋‹จ 4๊ฐœ์˜ API๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด 3๊ฐœ์˜ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•จ
  • ํ•˜์ง€๋งŒ Spring MVC๋Š” ํšจ์œจ์ ์ธ API ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด Front Controller ํŒจํ„ด์„ ๋งŒ๋“ค์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, API๋งˆ๋‹ค ํŒŒ์ผ์„ ๋งŒ๋“ค ํ•„์š” X
    • ๋ณดํ†ต ํ•˜๋‚˜์˜ Controller์— ๋ชจ๋“  API๋ฅผ ๋„ฃ์ง€ ์•Š๊ณ , ์œ ์‚ฌํ•œ ์„ฑ๊ฒฉ์˜ API๋ฅผ ํ•˜๋‚˜์˜ Controller๋กœ ๊ด€๋ฆฌํ•จ
  • ๋ฉ”์„œ๋“œ ์ด๋ฆ„๋„ ๋งˆ์Œ๋Œ€๋กœ ์„ค์ • ๊ฐ€๋Šฅํ•จ!

Front Controller

Controller ๋งŒ๋“ค๊ธฐ

1. ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ ํ›„ Controller ํŒŒ์ผ ์ƒ์„ฑ

ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ ๋ฐ ํŒŒ์ผ ์ƒ์„ฑ

2. API ๋งŒ๋“ค๊ธฐ

* ๊ฒฝ๋กœ๋Š” ์ค‘๋ณต์ด ๋  ์ˆ˜ ์žˆ์ง€๋งŒ, Method๊นŒ์ง€ ์ค‘๋ณต X

package com.sparta.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

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

    @GetMapping("/api/get")
    @ResponseBody
    public String get(){
        return "GET Method ์š”์ฒญ";
    }

    @PostMapping("/api/hello")
    @ResponseBody
    public String postHello() {
        return "Hello Post 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 ์š”์ฒญ";
    }
}

๊ฒฐ๊ณผ

 

3. ์ค‘๋ณต ์ฝ”๋“œ ์—†์• ๊ธฐ

@RequestMapping์„ ์ƒ๋‹จ์— ๋‹ฌ๊ณ , ํŠน์ • url๋กœ ์‹œ์ž‘ํ•˜๋Š” ์š”์ฒญ์€ ํ•ด๋‹น ํด๋ž˜์Šค๋กœ ๋„˜์–ด์˜ด. ๊ทธ ์ดํ›„ path์™€ Method ๋ฐฉ์‹์„ ๋ถ„์„ํ•ด ๋ฉ”์„œ๋“œ์— ๋งค์นญํ•จ

package com.sparta.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@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 ์š”์ฒญ";
    }
}

์ •์  ํŽ˜์ด์ง€์™€ ๋™์  ํŽ˜์ด์ง€

thymeleaf: ํ…œํ”Œ๋ฆฟ ์—”์ง„์œผ๋กœ ๋™์ ์ธ ์›นํŽ˜์ด์ง€ ์ฆ‰, ๋™์ ์ธ HTML ํŒŒ์ผ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

 

์ •์  ํŽ˜์ด์ง€ ์ฒ˜๋ฆฌ

static ํด๋”์— ์žˆ๋Š” HTML์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•

static ํด๋” ๋‚ด ์ •์  ํŽ˜์ด์ง€

1. URL ๊ฒฝ๋กœ์—์„œ ์ง์ ‘ ์ ‘๊ทผ

URL ๊ฒฝ๋กœ ์ ‘๊ทผ

 

 

2. thymeleaf๊ฐ€ ์—”์ง„์— ๊ฑธ๋ ค ์žˆ์ง€ ์•Š์€ ์ƒํ™ฉ์—์„œ Controller๋ฅผ ํ†ตํ•ด ์ ‘๊ทผ

package com.sparta.springmvc.html;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HtmlController {
    @GetMapping("/static-hello")
    public String hello() {
        return "hello.html";
    }
}

Controller๋ฅผ ํ†ตํ•ด ์ ‘๊ทผ

 

3. redirect๋กœ ์ ‘๊ทผ

package com.sparta.springmvc.html;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HtmlController {
    @GetMapping("/static-hello")
    public String hello() {
        return "hello.html";
    }

    @GetMapping("/html/redirect")
    public String htmlStatic() {
        return "redirect:/hello.html"; // ์žฌํ˜ธ์ถœ
    }
}

redirect๋Š” ํ•œ ๋ฒˆ ๋” ๋„คํŠธ์›Œํฌ ์š”์ฒญ ๋ฐœ์ƒํ•จ

 

templates ํด๋”์— ์žˆ๋Š” HTML์— ์ ‘๊ทผํ•˜๊ธฐ

templates ํด๋” ๋‚ด html ํŒŒ์ผ

package com.sparta.springmvc.html;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HtmlController {
    @GetMapping("/html/templates")
    public String htmlTemplate(){
        return "hello";
    }
}

Controller๋ฅผ ํ†ตํ•ด templates ๋‚ด html ํŒŒ์ผ ์ ‘๊ทผ


๋™์  ํŽ˜์ด์ง€ ์ฒ˜๋ฆฌ

๋ฐ˜ํ™˜ํ•  ๋ฐ์ดํ„ฐ๋Š” Model์— ๋‹ด๊ณ , ๊ทธ Model์ด ์ ์šฉ๋  View name์— ์ •๋ณด ์ „๋‹ฌํ•˜๋ฉด View Resolver๋ฅผ ํ†ตํ•ด์„œ ๊ทธ Model์˜ ๋ฐ์ดํ„ฐ๊ฐ€ View์— ์ ์šฉ๋˜๊ณ  ์™„์„ฑ๋œ ํŽ˜์ด์ง€๋ฅผ ํด๋ผ์ด์–ธํŠธ์— ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ณผ์ •

<!DOCTYPE html>
<html>
  <head><meta charset="UTF-8"><title>Hello Spring</title></head>
  <body>
    <div>
      Hello, Spring ๋™์  ์›น ํŽ˜์ด์ง€!!
    </div>
    <div>
      (๋ฐฉ๋ฌธ์ž ์ˆ˜: <span th:text="${visits}"></span>)
    </div>
  </body>
</html>
package com.sparta.springmvc.html;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HtmlController {

    private static long visitCount = 0; // ๋ฐฉ๋ฌธ ํšŸ์ˆ˜

    @GetMapping("/html/dynamic")
    public String htmlDynamic(Model model) {
        visitCount++;
        model.addAttribute("visits", visitCount);
        return "hello-visit";
    }
}

๋™์  ํŽ˜์ด์ง€ ์˜ˆ์‹œ


๋ฐ์ดํ„ฐ๋ฅผ Client์— ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐฉ๋ฒ•

ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„

์›น ์ƒํƒœ๊ณ„์—์„œ ์„œ๋ฒ„๋Š” MySQL๊ณผ ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„œ๋ฒ„์—์„œ ๋ฐ›์•„์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ํด๋ผ์ด์–ธํŠธ์— ๋ฐ˜ํ™˜ํ•ด์ฃผ๋ฉด, ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ๋ธŒ๋ผ์šฐ์ €๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž์—๊ฒŒ ๊ฒฐ๊ณผ๊ฐ’์„ ๋ณด์—ฌ์ฃผ๋Š” ํ˜•ํƒœ๋กœ ๋ฐœ์ „ํ•˜๊ณ  ์žˆ์Œ

์ฆ‰, Client ์ชฝ์„ ๊ฐœ๋ฐœํ•˜๋Š” Frontend์™€ ์„œ๋ฒ„ ์ชฝ์„ ๊ฐœ๋ฐœํ•˜๋Š” Backend๊ฐ€ ์„œ๋กœ ๋А์Šจํ•˜๊ฒŒ ๊ฒฐํ•ฉ๋˜๋Š” ๋ฐฉ์‹์„ ๋” ๋งŽ์ด ์ฑ„ํƒํ•˜๊ณ  ์žˆ์Œ

 

์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ ๊ตํ™˜ ํฌ๋งท ์ค‘์— JSON์ด๋ผ๋Š” ํ˜•ํƒœ๋กœ ๋ฐ์ดํ„ฐ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์žˆ์Œ

์š”์ฒญ-์‘๋‹ต ํฌ๋งท

  • Client์—์„œ ์ตœ์ดˆ Requestํ•˜๋ฉด ์ •์  HTML ํ˜น์€ ๋™์  HTML ๋ฐ˜ํ™˜
  • ๊ทธ ์ดํ›„์—๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ AJAX๋กœ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์„œ๋ฒ„์— ์š”์ฒญ์ด ๋“ค์–ด์™€ ์„œ๋ฒ„๋Š” JSON ํ˜•ํƒœ๋กœ ๋ฐ˜ํ™˜
    • JSON์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ชฝ์—์„œ ์žˆ๋Š” ํƒ€์ž…์ด๊ธฐ ๋•Œ๋ฌธ์— ์ž๋ฐ”์—์„œ ์ฝ์„ ์ˆ˜ X
    • ์ฆ‰, ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•ด์ค„ ๋•Œ JSON ํ˜•์‹์ฒ˜๋Ÿผ ์ƒ๊ธด String ํ˜•์‹์œผ๋กœ ์ž‘์„ฑ

String ๋ฐ˜ํ™˜

package com.sparta.springmvc.response;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/response")
public class ResponseController {
    // Content-Type: text/html
    // Response Body
    // {"name":"Robbie", "age":95}
    @GetMapping("/json/string")
    @ResponseBody
    public String helloStringJson() {
        return "{\"name\":\"Robbie\",\"age\":95}";
    }
}

String ๋ฐ˜ํ™˜ ์‘๋‹ต ๊ฒฐ๊ณผ

์ž๋ฐ” ํด๋ž˜์Šค ๋ฐ˜ํ™˜

์ž๋ฐ” ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด Spring ๋‚ด๋ถ€์ ์œผ๋กœ ์ž๋ฐ” ๊ฐ์ฒด๋ฅผ JSON ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•ด์คŒ

package com.sparta.springmvc.response;

import lombok.Getter;

@Getter
public class Star {
    String name;
    int age;

    public Star(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Star() {}
}
package com.sparta.springmvc.response;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/response")
public class ResponseController {
    // Content-Type: text/html
    // Response Body
    // {"name":"Robbie", "age":95}
    @GetMapping("/json/string")
    @ResponseBody
    public String helloStringJson() {
        return "{\"name\":\"Robbie\",\"age\":95}";
    }

    // Content-Type: application/json
    // Response Body
    // {"name":"Robbie", "age":95}
    @GetMapping("/json/class")
    @ResponseBody
    public Star helloClassJson(){
        return new Star("Robbie", 95);
    }
}

์ž๋ฐ” ๊ฐ์ฒด ๋ฐ˜ํ™˜ ์‘๋‹ต ๊ฒฐ๊ณผ


RestController

RestController๋Š” Controller + ResponseBody๋กœ, ๋ฉ”์„œ๋“œ์— ๋”ฐ๋กœ ResponseBody๋ฅผ ๋‹ฌ์ง€ ์•Š์•„๋„ ๋จ!

package com.sparta.springmvc.response;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/response/rest")
public class ResponseRestController {
    // [Response header]
    //   Content-Type: text/html
    // [Response body]
    //   {"name":"Robbie","age":95}
    @GetMapping("/json/string")
    public String helloStringJson() {
        return "{\"name\":\"Robbie\",\"age\":95}";
    }

    // [Response header]
    //   Content-Type: application/json
    // [Response body]
    //   {"name":"Robbie","age":95}
    @GetMapping("/json/class")
    public Star helloClassJson() {
        return new Star("Robbie", 95);
    }
}

Jackson

Jackson์€ JSON ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, Object(=์ž๋ฐ” ๊ฐ์ฒด)๋ฅผ JSON ํƒ€์ž…์˜ String์œผ๋กœ ๋ณ€ํ™˜ํ•ด ์ค„ ์ˆ˜ ์žˆ๊ณ , ๋ฐ˜๋Œ€๋กœ JSON ํƒ€์ž…์˜ String ๊ฐ’์„ Object๋กœ ๋ณ€ํ™˜ํ•ด ์ค„ ์ˆ˜ ์žˆ์Œ
- Spring 3.0 ๋ฒ„์ „ ์ดํ›„๋กœ Jackson๊ณผ ๊ด€๋ จ๋œ API ์ œ๊ณต

 

ObjectMapper ํด๋ž˜์Šค ์‚ฌ์šฉํ•ด์„œ Object → JSON ํ˜•ํƒœ ํ˜น์€ JSON → Object ํ˜•ํƒœ ๋ณ€ํ™˜

Object → JSON

  • ์ž๋ฐ” ํด๋ž˜์Šค ๋‚ด์— Getter ๋ฉ”์„œ๋“œ ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•จ! → ์ง๋ ฌํ™” ์ˆ˜ํ–‰์„ ์œ„ํ•ด
package com.sparta.springmvc;

import com.sparta.springmvc.response.Star;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import tools.jackson.databind.ObjectMapper;

public class JacksonTest {
    @Test
    @DisplayName("Object To JSON : get Method ํ•„์š”")
    void test1() {
        Star star = new Star("Robbie", 95);

        ObjectMapper objectMapper = new ObjectMapper(); // Jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ObjectMapper
        String json = objectMapper.writeValueAsString(star);

        System.out.println("json = " + json);
    }
}

JSON → Object

  • ์ž๋ฐ” ํด๋ž˜์Šค ๋‚ด์— ๊ธฐ๋ณธ ์ƒ์„ฑ์ž์™€ Getter ํ˜น์€ Setter ํ•„์š”!  ์—ญ์ง๋ ฌํ™” ์ˆ˜ํ–‰์„ ์œ„ํ•ด
package com.sparta.springmvc;

import com.sparta.springmvc.response.Star;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import tools.jackson.databind.ObjectMapper;

public class JacksonTest {
    @Test
    @DisplayName("JSON To Object : ๊ธฐ๋ณธ ์ƒ์„ฑ์ž & (get OR set) Method ํ•„์š”")
    void test2() {
        String json = "{\"name\":\"Robbie\",\"age\":95}"; // JSON ํƒ€์ž…์˜ String

        ObjectMapper objectMapper = new ObjectMapper(); // Jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ObjectMapper

        Star star = objectMapper.readValue(json, Star.class);
        System.out.println("star.getName() = " + star.getName());
    }
}

Path Variable๊ณผ Request Param

ํด๋ผ์ด์–ธํŠธ์—์„œ ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ ๋ฐ์ดํ„ฐ๋ฅผ ํ•จ๊ป˜ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Œ. ์„œ๋ฒ„์—์„œ๋Š” ์ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์„œ ์‚ฌ์šฉํ•ด์•ผํ•จ. ํ•˜์ง€๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด๋Š” ๋ฐฉ์‹์ด ์—ฌ๋Ÿฌ ๊ฐ€์ง€๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ๋“  ๋ฐฉ์‹์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ• ํ•™์Šตํ•ด์•ผ ํ•จ

 

Path Variable

๋ณด๋‚ด๋ ค๊ณ ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๊ฒฝ๋กœ์— ๋„ฃ๋Š” ๊ฒƒ

  • URL ๊ฒฝ๋กœ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๊ณ ์ž ํ•˜๋Š” ์œ„์น˜์˜ ๊ฒฝ๋กœ์— {data} ์ค‘๊ด„ํ˜ธ ์‚ฌ์šฉ
  • ํ•ด๋‹น ์š”์ฒญ ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ์— @PathVariable ์• ๋„ˆํ…Œ์ด์…˜๊ณผ ํ•จ๊ป˜ {data} ์ค‘๊ด„ํ˜ธ์— ์„ ์–ธํ•œ ๋ณ€์ˆ˜๋ช…๊ณผ ๋ณ€์ˆ˜ํƒ€์ž…์„ ์„ ์–ธํ•ด์„œ ํ•ด๋‹น ๊ฒฝ๋กœ์˜ ๋ฐ์ดํ„ฐ ๋ฐ›์•„์˜ด
package com.sparta.springmvc.request;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/hello/request")
public class RequestController {
    // [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);
    }
}

Path Variable ์‘๋‹ต ์˜ˆ์‹œ

 

Request Param(ํ˜น์€ Query String)

URL ๋‚ด ๊ฒฝ๋กœ ๋’ค์— Query String ๋ฐฉ์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์‹œ์ž‘ํ•  ๋•Œ ๋ฌผ์Œํ‘œ๋กœ ์‹œ์ž‘ํ•˜๊ณ , Key-Value ํ˜•ํƒœ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์Œ. ๋ฐ์ดํ„ฐ ์—ฌ๋Ÿฌ ๊ฐœ๋ฅผ ๋ณด๋‚ผ ๋•Œ์—๋Š” &๋กœ ์—ฐ๊ฒฐ

  • ํ•ด๋‹น ์š”์ฒญ ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ์— @RequestParam ์• ๋„ˆํ…Œ์ด์…˜๊ณผ ํ•จ๊ป˜ key ๋ถ€๋ถ„์— ์„ ์–ธํ•œ ๋ณ€์ˆ˜๋ช…๊ณผ ๋ณ€์ˆ˜ํƒ€์ž…์„ ์„ ์–ธํ•ด ๋ฐ์ดํ„ฐ ๋ฐ›์•„์˜ด
package com.sparta.springmvc.request;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping("/hello/request")
public class RequestController {
    // [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);
    }
}

Request Param ์‘๋‹ต ์˜ˆ์‹œ

    • ํŒŒ๋ผ๋ฏธํ„ฐ์— @RequestParam ์• ๋„ˆํ…Œ์ด์…˜ ์ƒ๋žต ๊ฐ€๋Šฅ
// [Request sample]
// GET http://localhost:8080/hello/request/form/param?name=Robbie&age=95
@GetMapping("/form/param")
@ResponseBody
public String helloGetRequestParam(@RequestParam String name, int age) {
    return String.format("Hello, @RequestParam.<br> name = %s, age = %d", name, age);
}
  • ํด๋ผ์ด์–ธํŠธ์—์„œ ์ „๋‹ฌ๋ฐ›์€ ๊ฐ’๋“ค ์ค‘ ํ•ด๋‹น ๊ฐ’์ด ํฌํ•จ๋˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒฝ์šฐ์˜ ์˜ต์…˜ ์„ค์ • → default๋กœ true๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ์Œ!(@PathVariable๋„ ๋™์ผ)
    • ์•ˆ๋“ค์–ด์˜ค๋Š” ๊ฒฝ์šฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€๋Š” ์•Š์ง€๋งŒ, null ๊ฐ’์ด ๋“ค์–ด์˜ด
// [Request sample]
// GET http://localhost:8080/hello/request/form/param?name=Robbie&age=95
@GetMapping("/form/param")
@ResponseBody
public String helloGetRequestParam(@RequestParam(required = false) String name, @RequestParam int age) {
    return String.format("Hello, @RequestParam.<br> name = %s, age = %d", name, age);
}

 

form ํƒœ๊ทธ POST

HTML ๋‚ด์˜ form ํƒœ๊ทธ ์‚ฌ์šฉํ•˜๋ฉด URL ์š”์ฒญ ๊ฐ€๋Šฅ

  • POST๋Š” GET๊ณผ ๋‹ฌ๋ฆฌ body๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Œ. Payload ๋ถ€๋ถ„์— ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Œ
<h2>POST /hello/request/form/param</h2>
<form method="POST" action="/hello/request/form/param">
  <div>
    ์ด๋ฆ„: <input name="name" type="text">
  </div>
  <div>
    ๋‚˜์ด: <input name="age" type="text">
  </div>
  <button>์ „์†ก</button>
</form>
<br>
package com.sparta.springmvc.request;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping("/hello/request")
public class RequestController {
    // [Request sample]
    // POST http://localhost:8080/hello/request/form/param
    // Header
    //  Content type: application/x-www-form-urlencoded
    // Body
    //  name=Robbie&age=95
    @PostMapping("/form/param")
    @ResponseBody
    public String helloPostRequestParam(@RequestParam String name, @RequestParam int age) {
        return String.format("Hello, @RequestParam.<br> name = %s, age = %d", name, age);
    }
}

form POST ์‘๋‹ต


HTTP ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ์ฒด๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•

@ModelAttribute ์• ๋…ธํ…Œ์ด์…˜

Body์˜ Query String ๋ฐฉ์‹์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ์ฒด์— Mapping

body ๋ถ€๋ถ„์— ๋“ค์–ด์˜จ Query String ๋ฐฉ์‹์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ์ฒด์— mappingํ•ด์„œ ๊ฐ€์ง€๊ณ  ์˜ฌ ์ˆ˜ ์žˆ์Œ

  • ํด๋ž˜์Šค(๊ฐ์ฒด)์˜ ํ•„๋“œ ์ด๋ฆ„๊ณผ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ณด๋‚ด๋Š” ๋ฐ์ดํ„ฐ ์ด๋ฆ„์ด ๋™์ผํ•ด์•ผํ•จ
// [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);
}

์š”์ฒญ์„ ๊ฐ์ฒด ํ˜•ํƒœ๋กœ ์ฒ˜๋ฆฌ

 

Request Param ๋ฐฉ์‹์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ์ฒด์— Mapping

request param ๋ฐฉ์‹๋„ @ModelAttribute๋ฅผ ์ด์šฉํ•ด์„œ ๊ฐ์ฒด๋กœ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ์Œ

  • @ModelAttribute๋Š” Setter ํ˜น์€ ์˜ค๋ฒ„๋กœ๋”ฉ๋œ ์ƒ์„ฑ์ž ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•จ!
// [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);
}

์š”์ฒญ์„ ๊ฐ์ฒด ํ˜•ํƒœ๋กœ ์ฒ˜๋ฆฌ

@ModelAttribute ์ƒ๋žตํ•˜๊ธฐ

// [Request sample]
// GET http://localhost:8080/hello/request/form/param/model?name=Robbie&age=95
@GetMapping("/form/param/model")
@ResponseBody
public String helloRequestParam(Star star) {
    return String.format("Hello, @ModelAttribute.<br> (name = %s, age = %d) ", star.name, star.age);
}

 

โ“ Spring์—์„œ @RequestParam๊ณผ @ModelAttribute ๋ชจ๋‘ ์ƒ๋žต์ด ๊ฐ€๋Šฅํ•œ๋ฐ ์–ด๋–ป๊ฒŒ ๊ตฌ๋ถ„ํ• ๊นŒ?

๐Ÿ—ฃ๏ธ Spring์€ ํ•ด๋‹น ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ SimpleValueType(์›์‹œํƒ€์ž…, Wrapper, Date ํƒ€์ž…)์ด๋ฉด @RequestParam์œผ๋กœ ๊ฐ„์ฃผํ•˜๊ณ ,
์•„๋‹ˆ๋ผ๋ฉด @ModelAttribute๊ฐ€ ์ƒ๋žต์ด ๋˜์—ˆ๋‹ค๊ณ  ํŒ๋‹จํ•จ

 

Body์— JSON ํ˜•์‹์œผ๋กœ ๋„˜์–ด์˜จ ๊ฒฝ์šฐ

HTTP Body ๋ถ€๋ถ„์— JSON ํ˜•์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ๋„˜์–ด์™”์„ ๋•Œ์— @RequestBody ์• ๋…ธํ…Œ์ด์…˜์„ ๊ผญ ๋ถ™์—ฌ์ฃผ์–ด์•ผ ํ•จ!

  • field๋ฅผ ๊ผญ ๋งž์ถฐ์ฃผ์–ด์•ผ ํ•จ!
// [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);
}

JSON ํ˜•์‹์œผ๋กœ ์š”์ฒญ