๐ŸŒฑ ์Šคํ”„๋ง MVC 1

yeopยท2023๋…„ 5์›” 29์ผ

โœ”๏ธ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ดํ•ด

์›น ์„œ๋ฒ„, ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„

์›น ์„œ๋ฒ„

  • ์ •์  ๋ฆฌ์†Œ์Šค ์ œ๊ณต
  • ์˜ˆ) Nginx, Apache

์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„

  • ์›น ์„œ๋ฒ„ ๊ธฐ๋Šฅ ํฌํ•จ + ๋™์  ๋ฆฌ์†Œ์Šค ์ œ๊ณต
  • ํ”„๋กœ๊ทธ๋žจ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ด์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ์ง ์ˆ˜ํ–‰
  • ์˜ˆ) ํ†ฐ์บฃ, Jetty, Undertow

์„œ๋ธ”๋ฆฟ

  • ์„œ๋ธ”๋ฆฟ์—์„œ ์ดˆ๋ก์ƒ‰ ๋ฐ•์Šค ์•ˆ์˜ ๊ฒƒ๋“ค์„ ์ œ์™ธํ•˜๊ณ  ๋ชจ๋‘ ๋Œ€์‹  ์ฒ˜๋ฆฌํ•ด์ค€๋‹ค.
  • WAS, ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ ๊ตฌ์กฐ
@WebServlet(name="helloServlet", urlPatterns="/hello")
public classs HelloServlet extends HttpServlet {
		
		@Override
		protected void service(HttpsServletRequest request, HttpServletResponse response){
				//  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ์ง
		}
}
  • ํ†ฐ์บฃ์ฒ˜๋Ÿผ ์„œ๋ธ”๋ฆฟ์„ ์ง€์›ํ•˜๋Š” WAS๋ฅผ ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๋ผ๊ณ  ํ•œ๋‹ค
  • ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๋Š” ์„œ๋ธ”๋ฆฟ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ, ์ดˆ๊ธฐํ™”, ํ˜ธ์ถœ, ์ข…๋ฃŒํ•˜๋Š” ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค.
  • ์„œ๋ธ”๋ฆฟ ๊ฐ์ฒด๋Š” ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ด€๋ฆฌ๋œ๋‹ค.
  • ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ ์ข…๋ฃŒ ์‹œ ํ•จ๊ป˜ ์ข…๋ฃŒ๋œ๋‹ค.
  • JSP๋„ ์„œ๋ธ”๋ฆฟ์œผ๋กœ ๋ณ€ํ™” ๋˜์–ด์„œ ์‚ฌ์šฉ
  • ๋™์‹œ ์š”์ฒญ์„ ์œ„ํ•œ ๋ฉ€ํ‹ฐ ์“ฐ๋ ˆ๋“œ ์ฒ˜๋ฆฌ ์ง€์›

๋™์‹œ ์š”์ฒญ - ๋ฉ€ํ‹ฐ ์“ฐ๋ ˆ๋“œ

์“ฐ๋ ˆ๋“œ

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ๋ฅผ ํ•˜๋‚˜ํ•˜๋‚˜ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์€ ์“ฐ๋ ˆ๋“œ
  • ์“ฐ๋ ˆ๋“œ๊ฐ€ ์—†๋‹ค๋ฉด ์ž๋ฐ” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰์ด ๋ถˆ๊ฐ€๋Šฅ

์“ฐ๋ ˆ๋“œ ํ’€

  • ๋™์‹œ ์š”์ฒญ์˜ ๊ฒฝ์šฐ ์š”์ฒญ์ด ์˜ฌ ๋•Œ๋งˆ๋‹ค ์“ฐ๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด ์“ฐ๋ ˆ๋“œ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์ด ์ƒ์„ฑ๋˜๋ฉด ๋ฆฌ์†Œ์Šค๊ฐ€ ์ž„๊ณ„์ ์„ ๋„˜์–ด ์„œ๋ฒ„๊ฐ€ ์ฃฝ์„ ์ˆ˜๋„ ์žˆ๊ณ  ์ƒ์„ฑ ๋น„์šฉ ๋˜ํ•œ ๋น„ํšจ์œจ์ ์ด๋‹ค.
  • ๊ทธ๋ž˜์„œ ํ•„์š”ํ•œ ์“ฐ๋ ˆ๋“œ๋ฅผ ์ตœ๋Œ€์น˜๋ฅผ ์„ค์ •ํ•ด๋†“๊ณ  ์“ฐ๋ ˆ๋“œ ํ’€์— ๋ณด๊ด€ํ•˜๊ณ  ๊ด€๋ฆฌํ•œ๋‹ค.
  • ์“ฐ๋ ˆ๋“œ ํ’€์— ์žˆ๋Š” ์“ฐ๋ ˆ๋“œ๊ฐ€ ๋ชจ๋‘ ์‚ฌ์šฉ ์ค‘์ด๋ผ๋ฉด ๊ธฐ๋‹ค๋ฆฌ๋Š” ์š”์ฒญ์€ ๊ฑฐ์ ˆํ•˜๊ฑฐ๋‚˜ ํŠน์ • ์ˆซ์ž๋งŒํผ ๋Œ€๊ธฐํ•˜๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์žฅ์ 
    • ์“ฐ๋ ˆ๋“œ๊ฐ€ ๋ฏธ๋ฆฌ ์ƒ์„ฑ๋˜์–ด ์žˆ์–ด, ์“ฐ๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ข…๋ฃŒํ•˜๋Š” ๋น„์šฉ(CPU)์ด ์ ˆ์•ฝ๋˜๊ณ  ์‘๋‹ต์‹œ๊ฐ„์ด ๋น ๋ฅด๋‹ค.
    • ์ƒ์„ฑ ๊ฐ€๋Šฅํ•œ ์“ฐ๋ ˆ๋“œ ์ตœ๋Œ€์น˜๊ฐ€ ์žˆ์–ด ๋งŽ์€ ์š”์ฒญ์ด ๋“ค์–ด์™€๋„ ๊ธฐ์กด ์š”์ฒญ์€ ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ตœ๋Œ€ ์“ฐ๋ ˆ๋“œ๋ฅผ ๋„ˆ๋ฌด ๋‚ฎ๊ฒŒ ์„ค์ •ํ•˜๋ฉด?
    • ๋™์‹œ ์š”์ฒญ์ด ๋งŽ์„ ์‹œ, ํด๋ผ์ด์–ธํŠธ๋Š” ๊ธˆ๋ฐฉ ์‘๋‹ต ์ง€์—ฐ, ์„œ๋ฒ„ ๋ฆฌ์†Œ์Šค๊ฐ€ ์—ฌ์œ ๋กญ๋‹ค โ†’ ๋น„ํšจ์œจ์ 
  • ์ตœ๋Œ€ ์“ฐ๋ ˆ๋“œ๋ฅผ ๋„ˆ๋ฌด ๋†’๊ฒŒ ์„ค์ •ํ•˜๋ฉด?
    • ๋™์‹œ ์š”์ฒญ์ด ๋งŽ์„ ์‹œ ๋ฆฌ์†Œ์Šค ์ž„๊ณ„์  ์ดˆ๊ณผ๋กœ ์„œ๋ฒ„ ๋‹ค์šด
  • ์ ์ • ์ˆซ์ž - ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ
    • ํˆด: nGrinder, ์•„ํŒŒ์น˜ ab, ์ œ์ด๋ฏธํ„ฐ

์ž๋ฐ” ์›น ๊ธฐ์ˆ  ์—ญ์‚ฌ

์ž๋ฐ” ์›น ๊ธฐ์ˆ  ์—ญ์‚ฌ

  • ์„œ๋ธ”๋ฆฟ
    • HTML ์ƒ์„ฑ์ด ์–ด๋ ค์›€
  • JSP
    • HTML ์ƒ์„ฑ์€ ํŽธ๋ฆฌํ•˜์ง€๋งŒ, ๋น„์ฆˆ๋‹ˆ์Šค ์กฐ์ง๊นŒ์ง€ ๋„ˆ๋ฌด ๋งŽ์€ ์—ญํ•  ๋‹ด๋‹น
  • ์„œ๋ธ”๋ฆฟ, JSP ์กฐํ•ฉ MVC ํŒจํ„ด ์‚ฌ์šฉ
    • ๋ชจ๋ธ, ๋ทฐ, ์ปจํŠธ๋กค๋Ÿฌ๋กœ ์—ญํ•  ๋‚˜๋ˆ„์–ด ๊ฐœ๋ฐœ
  • MVC ํ”„๋ ˆ์ž„์›Œํฌ ์ถ˜์ถ” ์ „๊ตญ ์‹œ๋Œ€ - 2000๋…„๋Œ€ ์ดˆ ~ 2010๋…„ ์ดˆ
  • ์• ๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜์˜ ์Šคํ”„๋ง MVC ๋“ฑ์žฅ
    • @Controller
  • ์Šคํ”„๋ง ๋ถ€ํŠธ์˜ ๋“ฑ์žฅ
    • ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” ์„œ๋ฒ„๋ฅผ ๋‚ด์žฅ
    • ๊ณผ๊ฑฐ์—๋Š” ์„œ๋ฒ„์— WAS๋ฅผ ์ง์ ‘ ์„ค์น˜ํ•˜๊ณ , ์†Œ์Šค๋Š” WarํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์„œ ์„ค์น˜ํ•œ WAS์— ๋ฐฐํฌ
    • ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” ๋นŒ๋“œ ๊ฒฐ๊ณผ(Jar)์— WAS ์„œ๋ฒ„ ํฌํ•จ โ†’ ๋นŒ๋“œ ๋ฐฐํฌ ๋‹จ์ˆœํ™”
  • ์Šคํ”„๋ง ์›น ๊ธฐ์ˆ ์˜ ๋ถ„ํ™”
    • Web Servlet - Spring MVC
      • ์„œ๋ธ”๋ฆฟ ๊ธฐ์ˆ  ์‚ฌ์šฉ O
    • Web Reactive - Spring WebFlux
      • ํŠน์ง•
        • ๋น„๋™๊ธฐ ๋„Œ ๋ธ”๋Ÿฌํ‚น ์ฒ˜๋ฆฌ
        • ์ตœ์†Œ ์“ฐ๋ ˆ๋“œ๋กœ ์ตœ๋Œ€ ์„ฑ๋Šฅ - ์“ฐ๋ ˆ๋“œ ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ ๋น„์šฉ ํšจ์œจํ™”
        • ํ•จ์ˆ˜ํ˜• ์Šคํƒ€์ผ๋กœ ๊ฐœ๋ฐœ - ๋™์‹œ์ฒ˜๋ฆฌ ์ฝ”๋“œ ํšจ์œจํ™”
        • ์„œ๋ธ”๋ฆฟ ๊ธฐ์ˆ  ์‚ฌ์šฉ X

์ž๋ฐ” ๋ทฐ ํ…œํ”Œ๋ฆฟ ์—ญ์‚ฌ

  • JSP
    • ์†๋„ ๋А๋ฆผ, ๊ธฐ๋Šฅ ๋ถ€์กฑ
  • ํ”„๋ฆฌ๋งˆ์ปค, Velocity (๋ฒจ๋กœ์‹œํ‹ฐ)
    • ์†๋„ ๋ฌธ์ œ ํ•ด๊ฒฐ, ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ
  • ํƒ€์ž„๋ฆฌํ”„(Thymeleaf)
    • ๋‚ด์ถ”๋Ÿด ํ…œํ”Œ๋ฆฟ: HTML์˜ ๋ชจ์–‘์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๋ทฐ ํ…œํ”Œ๋ฆฟ ์ ์šฉ ๊ฐ€๋Šฅ
    • ์Šคํ”„๋ง MVC์™€ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ ํ†ตํ•ฉ, ์„ฑ๋Šฅ์€ ํ”„๋ฆฌ๋งˆ์ปค, ๋ฒจ๋กœ์‹œํ‹ฐ๊ฐ€ ๋” ๋น ๋ฆ„

โœ”๏ธ ์„œ๋ธ”๋ฆฟ

์„œ๋ธ”๋ฆฟ ๊ธฐ๋ณธ ์„ค์ •

  • ServletComponentScan
    • ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” ํ•ด๋‹น Annotation์„ ํ†ตํ•ด์„œ๋ธ”๋ฆฟ์„ ์ง์ ‘ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค
  • @WebServlet ์„œ๋ธ”๋ฆฟ ์• ๋…ธํ…Œ์ด์…˜
    • name: ์„œ๋ธ”๋ฆฟ ์ด๋ฆ„
    • urlPatterns: URL ๋งคํ•‘
  • HTTP์š”์ฒญ์„ ํ†ตํ•ด ๋งคํ•‘๋œ URL์ด ํ˜ธ์ถœ๋˜๋ฉด ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๋Š” ๋‹ค์Œ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
    • protected void service(HttpsServletRequest request, HttpServletResponse response)
    • ๋‹จ์ถ•ํ‚ค : ctrl + o

HTTP์š”์ฒญ ๋ฉ”์‹œ์ง€ ๋กœ๊ทธ๋กœ ํ™•์ธํ•˜๊ธฐ

  • Application.properties
    • logging.level.org.apache.coyote.http11=debug

HttpServletRequest

๊ธฐ๋Šฅ

  • ์ž„์‹œ ์ €์žฅ์†Œ ๊ธฐ๋Šฅ
    • ํ•ด๋‹น HTTP ์š”์ฒญ์ด ์‹œ์ž‘๋ถ€ํ„ฐ ๋๋‚  ๋•Œ๊นŒ์ง€ ์œ ์ง€๋˜๋Š” ์ž„์‹œ ์ €์žฅ์†Œ ๊ธฐ๋Šฅ
      • ์ €์žฅ: request.setAttribute(name, value)
      • ์กฐํšŒ: request.getAttribute(name)
  • ์„ธ์…˜ ๊ด€๋ฆฌ ๊ธฐ๋Šฅ
    • request.getSession(create: true)

๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•, Header ์กฐํšŒ

  • ์กฐํšŒ ์ฝ”๋“œ
    //http://localhost:8080/request-header?username=hello
    @WebServlet(name = "requestHeaderServlet", urlPatterns = "/request-header")
    public class RequestHeaderServlet extends HttpServlet {
        @Override
        protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            printStartLine(request);
            printHeaders(request);
            printHeaderUtils(request);
            printEtc(request);
            response.getWriter().write("ok");
        }
    
        //start line ์ •๋ณด
        private void printStartLine(HttpServletRequest request) {
            System.out.println("--- REQUEST-LINE - start ---");
            System.out.println("request.getMethod() = " + request.getMethod()); //GET
            System.out.println("request.getProtocol() = " + request.getProtocol()); //HTTP / 1.1
            System.out.println("request.getScheme() = " + request.getScheme()); //http
            // http://localhost:8080/request-header
            System.out.println("request.getRequestURL() = " + request.getRequestURL());
            // /request-header
            System.out.println("request.getRequestURI() = " + request.getRequestURI());
            //username=hi
            System.out.println("request.getQueryString() = " +
                    request.getQueryString());
            System.out.println("request.isSecure() = " + request.isSecure()); //https ์‚ฌ์šฉ ์œ ๋ฌด
            System.out.println("--- REQUEST-LINE - end ---");
            System.out.println();
        }
    
        //Header ํŽธ๋ฆฌํ•œ ์กฐํšŒ
        private void printHeaderUtils(HttpServletRequest request) {
            System.out.println("--- Header ํŽธ์˜ ์กฐํšŒ start ---");
            System.out.println("[Host ํŽธ์˜ ์กฐํšŒ]");
            System.out.println("request.getServerName() = " + request.getServerName()); //Host ํ—ค๋”
            System.out.println("request.getServerPort() = " + request.getServerPort()); //Host ํ—ค๋”
            System.out.println();
            System.out.println("[Accept-Language ํŽธ์˜ ์กฐํšŒ]");
            request.getLocales().asIterator().forEachRemaining(locale -> System.out.println("locale = " + locale));
            System.out.println("request.getLocale() = " + request.getLocale());
            System.out.println();
            System.out.println("[cookie ํŽธ์˜ ์กฐํšŒ]");
            if (request.getCookies() != null) {
                for (Cookie cookie : request.getCookies()) {
                    System.out.println(cookie.getName() + ": " + cookie.getValue());
                }
            }
            System.out.println();
            System.out.println("[Content ํŽธ์˜ ์กฐํšŒ]");
            System.out.println("request.getContentType() = " +
                    request.getContentType());
            System.out.println("request.getContentLength() = " +
                    request.getContentLength());
            System.out.println("request.getCharacterEncoding() = " +
                    request.getCharacterEncoding());
            System.out.println("--- Header ํŽธ์˜ ์กฐํšŒ end ---");
            System.out.println();
        }
    
        //Header ๋ชจ๋“  ์ •๋ณด
        private void printHeaders(HttpServletRequest request) {
            System.out.println("--- Headers - start ---");
            /*
             Enumeration<String> headerNames = request.getHeaderNames();
             while (headerNames.hasMoreElements()) {
             String headerName = headerNames.nextElement();
             System.out.println(headerName + ": " + request.getHeader(headerName));
             }
            */
            request.getHeaderNames().asIterator()
                    .forEachRemaining(headerName -> System.out.println(headerName + ": " + request.getHeader(headerName)));
                            System.out.println("--- Headers - end ---");
            System.out.println();
        }
    
        //๊ธฐํƒ€ ์ •๋ณด
        private void printEtc(HttpServletRequest request) {
            System.out.println("--- ๊ธฐํƒ€ ์กฐํšŒ start ---");
            System.out.println("[Remote ์ •๋ณด]");
            System.out.println("request.getRemoteHost() = " +
                    request.getRemoteHost()); //
            System.out.println("request.getRemoteAddr() = " +
                    request.getRemoteAddr()); //
            System.out.println("request.getRemotePort() = " +
                    request.getRemotePort()); //
            System.out.println();
            System.out.println("[Local ์ •๋ณด]");
            System.out.println("request.getLocalName() = " +
                    request.getLocalName()); //
            System.out.println("request.getLocalAddr() = " +
                    request.getLocalAddr()); //
            System.out.println("request.getLocalPort() = " +
                    request.getLocalPort()); //
            System.out.println("--- ๊ธฐํƒ€ ์กฐํšŒ end ---");
            System.out.println();
        }
        }

HTTP ์š”์ฒญ ๋ฉ”์‹œ์ง€ ๋ฐ”๋”” ์กฐํšŒ

  • Get - ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ
    • getParameter ์ฝ”๋“œ
      @WebServlet(name = "requestParamServlet", urlPatterns = "/request-param")
      public class RequestParamServlet extends HttpServlet {
          @Override
          protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              System.out.println("[์ „์ฒด ํŒŒ๋ผ๋ฏธํ„ฐ ์กฐํšŒ] - start");
              request.getParameterNames().asIterator()
                      .forEachRemaining(paramName -> System.out.println(paramName + "=" + request.getParameter(paramName)));
              System.out.println();
      
              System.out.println("[๋‹จ์ผ ํŒŒ๋ผ๋ฏธํ„ฐ ์กฐํšŒ]");
              String username = request.getParameter("username");
              String age = request.getParameter("age");
              System.out.println("age = " + age);
              System.out.println("username = " + username);
              System.out.println();
      
              System.out.println("[์ด๋ฆ„์ด ๊ฐ™์€ ๋ณต์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ ์กฐํšŒ]");
              String[] usernames = request.getParameterValues("username");
              for (String name : usernames) {
                  System.out.println("username = " + name);
              }
      
              response.getWriter().write("ok");
      
          }
      }
    • GET URL ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ ํ˜•์‹์€ http ๋ฉ”์‹œ์ง€ ๋ฐ”๋””๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— content-type์ด ์—†๋‹ค.
  • Post - HTML Form
    • content-type: application/x-www-form-urlencoded
    • message body: username=hello&age=20
    • request.getParameter() ๋Š” GET ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ ํ˜•์‹๋„ ์ง€์›ํ•˜๊ณ  POST HTML Form ํ˜•์‹๋„ ์ง€์›ํ•œ๋‹ค.
  • HTTP API - MessageBody โ†’ Postman ํ…Œ์ŠคํŠธ
    • JSON ํ˜•์‹ ์ „์†ก
      • contenxt-type: application/json
      • message body: {โ€usernameโ€:โ€helloโ€, โ€œageโ€:โ€20}
    • ์ฝ”๋“œ
      @WebServlet(name= "requestBodyJsonServlet", urlPatterns = "/request-body-json")
      public class RequestBodyJsonServlet extends HttpServlet {
          
          private ObjectMapper objectMapper = new ObjectMapper();
      
          @Override
          protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              ServletInputStream inputStream = req.getInputStream();
              String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
      
              System.out.println("messageBody = " + messageBody);
      
              HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
      
              System.out.println("helloData.getUsername() = " + helloData.getUsername());
              System.out.println("helloData.getAge() = " + helloData.getAge());
          }
      }
    • req.getInputStream()์„ ์‚ฌ์šฉํ•ด์„œ ์ธ์ฝ”๋”ฉ๋œ ๋ฐ”๋”” ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์•„์„œ StreamUtils ๋ฅผ ํ†ตํ•ด ๋””์ฝ”๋”ฉํ•œ ๋ฐ”๋”” ๋ฉ”์‹œ์ง€๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.
    • JSON ๊ฒฐ๊ณผ๋ฅผ ํŒŒ์‹ฑํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ž๋ฐ” ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜๋ ค๋ฉด ๋จผ์ € ์ž๋ฐ” ๊ฐ์ฒด ํ˜•์‹(HelloData.class)์„ ๋จผ์ € ์„ ์–ธํ•ด์ค€๋‹ค
    • ๊ทธ ํ›„ ์Šคํ”„๋ง ๋ถ€ํŠธ๋กœ Spring MVC๋ฅผ ์„ ํƒํ•˜๋ฉด ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” Jackson (ObjectMapper)์ด๋ผ๋Š” JSON ๋ณ€ํ™˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

HttpServletResponse

๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•, Header ์กฐํšŒ

  • ์ฝ”๋“œ
    @WebServlet(name = "responseHeaderServlet", urlPatterns = "/response-header")
    public class ResponseHeaderServlet extends HttpServlet {
    
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //[Status-line]
            resp.setStatus(HttpServletResponse.SC_OK);
    
            //[response-headers]
            resp.setHeader("Content-Type", "text/plain");
            resp.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
            resp.setHeader("Pragma", "no-cache");
            resp.setHeader("my-header", "hello");
    
            //[Header ํŽธ์˜ ๋ฉ”์„œ๋“œ]
            content(resp);
            cookie(resp);
            redirect(resp);
    
            resp.getWriter().write("ok");
        }
    
        private void content(HttpServletResponse response) {
            //Content-Type: text/plain;charset=utf-8
            //Content-Length: 2
            //response.setHeader("Content-Type", "text/plain;charset=utf-8");
            response.setContentType("text/plain");
            response.setCharacterEncoding("utf-8");
            //response.setContentLength(2); //(์ƒ๋žต์‹œ ์ž๋™ ์ƒ์„ฑ)
        }
    
        private void cookie(HttpServletResponse response) {
            //Set-Cookie: myCookie=good; Max-Age=600;
            //response.setHeader("Set-Cookie", "myCookie=good; Max-Age=600");
            Cookie cookie = new Cookie("myCookie", "good");
            cookie.setMaxAge(600); //600์ดˆ
            response.addCookie(cookie);
        }
    
        private void redirect(HttpServletResponse response) throws IOException {
            //Status Code 302
            //Location: /basic/hello-form.html
            //response.setStatus(HttpServletResponse.SC_FOUND); //302
            //response.setHeader("Location", "/basic/hello-form.html");
            response.sendRedirect("/basic/hello-form.html");
        }
    }

HTTP ์‘๋‹ต ๋ฉ”์‹œ์ง€ ๋ฐ”๋”” ์กฐํšŒ

  • HTML ์‘๋‹ต
    @WebServlet(name = "responseBodyHtmlServlet", urlPatterns = "/response-html")
    public class ResponseBodyHtmlServlet extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //Content-Type: text/html;charset=utf-8
            resp.setContentType("text/html");
            resp.setCharacterEncoding("utf-8");
    
            PrintWriter writer = resp.getWriter();
            writer.println("<html>");
            writer.println("<body>");
            writer.println(" <div>์•ˆ๋…•?</div>");
            writer.println("</body>");
            writer.println("</html>");
        }
  • HTTP API JSON ์‘๋‹ต
    @WebServlet(name = "responseJsonServlet", urlPatterns = "/response-json")
    public class ResponseJsonServlet extends HttpServlet {
    
        ObjectMapper objectMapper = new ObjectMapper();
    
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //Content-Type: application/json
            resp.setContentType("application/json");
            resp.setCharacterEncoding("utf-8");
    
            HelloData helloData = new HelloData();
            helloData.setUsername("kim");
            helloData.setAge(20);
    
            //{"username":"kim", "age":"20}
            String s = objectMapper.writeValueAsString(helloData);
            resp.getWriter().write(s);
        }
    }

โœ”๏ธ ์„œ๋ธ”๋ฆฟ, JSP, MVC

  • ๊ฐ๊ฐ ์„œ๋ธ”๋ฆฟ, JSP, MVC(์„œ๋ธ”๋ฆฟ + JSP)๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํšŒ์›๊ฐ€์ž… ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ ๋‹ค.

MVC ํŒจํ„ด

  • ํšŒ์› ๋“ฑ๋ก ํผ - ์ปจํŠธ๋กค๋Ÿฌ
    @WebServlet(name = "mvcMemberFormServlet", urlPatterns = "/servlet-mvc/members/new-form")
    public class MvcMemberFormServlet extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String viewPath = "/WEB-INF/views/new-form.jsp";
            RequestDispatcher dispatcher = req.getRequestDispatcher(viewPath);
            dispatcher.forward(req, resp);
        }
    }
    • WEB/INF
      • ์ด ๊ฒฝ๋กœ์•ˆ์— JSP๊ฐ€ ์žˆ์œผ๋ฉด ์™ธ๋ถ€์—์„œ ์ง์ ‘ JSP๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์—†๋‹ค.
      • ์„œ๋ฒ„ ๋‚ด๋ถ€์—์„œ ํ˜ธ์ถœ
    • Redirect vs Forward
      • ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋Š” ํด๋ผ์ด์–ธํŠธ์— ์‘๋‹ต์ด ๋‚˜๊ฐ”๋‹ค๊ฐ€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ Redirect ๊ฒฝ๋กœ๋กœ ๋‹ค์‹œ ์š”์ฒญํ•œ๋‹ค. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ธ์ง€ ๊ฐ€๋Šฅ
      • ํฌ์›Œ๋“œ๋Š” ์„œ๋ฒ„ ๋‚ด๋ถ€์—์„œ ์ผ์–ด๋‚˜๋Š” ํ˜ธ์ถœ์ด๊ธฐ ๋•Œ๋ฌธ์— ํด๋ผ์ด์–ธํŠธ ์ธ์ง€ ๋ถˆ๊ฐ€๋Šฅ
  • ํšŒ์› ์ €์žฅ - ์ปจํŠธ๋กค๋Ÿฌ
    @WebServlet(name = "mvcMemberSaveServlet", urlPatterns = "/servlet-mvc/members/ save")
            public class MvcMemberSaveServlet extends HttpServlet {
            private MemberRepository memberRepository = MemberRepository.getInstance();
            
            @Override
            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
             
                String username = request.getParameter("username");
                int age = Integer.parseInt(request.getParameter("age"));
                Member member = new Member(username, age);
                System.out.println("member = " + member);
                memberRepository.save(member);
             
                //Model ์— ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๊ด€ํ•œ๋‹ค.
                request.setAttribute("member", member);
                String viewPath = "/WEB-INF/views/save-result.jsp";
                RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
                dispatcher.forward(request, response);
                }
             }
    • request.setAttribute()
      • request ๊ฐ์ฒด์— ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๊ด€ํ•ด์„œ ๋ทฐ์— ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

MVC ์ปจํŠธ๋กค๋Ÿฌ์˜ ๋‹จ์ 

  • ViewPath์˜ ์ค‘๋ณต
  • Forward ๋ช…๋ น์–ด์˜ ์ค‘๋ณต
  • ๊ณตํ†ต ์ฒ˜๋ฆฌ๊ฐ€ ์–ด๋ ต๋‹ค. (๊ณตํ†ต์œผ๋กœ ์ฒ˜๋ฆฌํ•  ๋ฌธ์ œ๋“ค์„ ๋ฉ”์„œ๋“œ๋กœ ๋ฌถ์–ด์„œ ์ฒ˜๋ฆฌํ•˜๊ธฐ๊ฐ€ ์–ด๋ ต๋‹ค.)

โœ”๏ธ MVC ํ”„๋ ˆ์ž„์›Œํฌ ๋งŒ๋“ค๊ธฐ

Front Controller ํŠน์ง•

  • ํ”„๋ก ํŠธ ์ปจํŠธ๋กค๋Ÿฌ ์„œ๋ธ”๋ฆฟ ํ•˜๋‚˜๋กœ ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์„ ๋ฐ›์Œ
  • ๊ณตํ†ต ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
  • ํ”„๋ก ํŠธ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€ ์ปจํŠธ๋กค๋Ÿฌ๋Š” ์„œ๋ธ”๋ฆฟ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

Front Controller ๋„์ž… - V1

๊ธฐ์กด ์ƒํƒœ

  • ๋ชจ๋“  ์ปจํŠธ๋กค๋Ÿฌ์—์„œ WebServlet Annotation์„ ์‚ฌ์šฉํ•ด์„œ URL ๋งคํ•‘

V1

  • ๊ฐ ๋กœ์ง์ด ๋“ค์–ด๊ฐˆ ์ปจํŠธ๋กค๋Ÿฌ๋“ค์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋งŒ๋“ค์–ด ๊ฐ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ํ•ด๋‹น ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์†๋ฐ›์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์„œ ์ผ๊ด€์„ฑ์„ ๋†’์˜€๋‹ค.
  • ํ”„๋ก ํŠธ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋‘์–ด URL ๋งคํ•‘์„ ํ•ด์ฃผ์—ˆ๋‹ค.
    • ํ•ด์‰ฌ๋งต์— Key(url), value(controller instance) ํ˜•ํƒœ๋กœ ์ €์žฅํ•ด๋‘๊ณ 
    • url์„ getRequestURI๋ฅผ ํ†ตํ•ด ๋ฐ›์•„์™€ ํ•ด์‰ฌ๋งต์—์„œ ์กฐํšŒํ•œ๋‹ค.
    • ๋งŒ์•ฝ ์ผ์น˜ํ•˜๋Š” Key๊ฐ€ ์—†๋‹ค๋ฉด 404 Error ์žˆ๋‹ค๋ฉด ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ ํ˜ธ์ถœ

V2

V2

  • MyView๋ผ๋Š” ๋ถ€๋ชจ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด์„œ ๊ฐ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์ด ํด๋ž˜์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค.
  • FrontController์—์„œ ์ด ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ›์•„ render ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด forwardํ•œ๋‹ค.

V3

์„œ๋ธ”๋ฆฟ ์ข…์†์„ฑ ์ œ๊ฑฐ

  • FrontController๋ฅผ ์ œ์™ธํ•œ ๊ฐ ์ปจํŠธ๋กค๋Ÿฌ๋Š” ์„œ๋ธ”๋ฆฟ์„ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.
  • request ๊ฐ์ฒด ๋Œ€์‹  Map ํ˜•ํƒœ๋กœ ์ •๋ณด๋ฅผ ๋ฐ›๊ณ  Request ๋ฐ˜ํ™˜ ๊ฐ’ ๋Œ€์‹  Model์„ ๋งŒ๋“ค์–ด ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • ๊ตฌํ˜„ ์ฝ”๋“œ๊ฐ€ ๋‹จ์ˆœํ•ด์ง€๊ณ  ํ…Œ์ŠคํŠธํ•˜๊ธฐ๊ฐ€ ์‰ฝ๋‹ค.

๋ทฐ ์ด๋ฆ„ ์ค‘๋ณต ์ œ๊ฑฐ

  • ViewResolver ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ๋ทฐ ์ด๋ฆ„์˜ ์ค‘๋ณต๋œ ๋ถ€๋ถ„์„ ์ œ๊ฑฐํ•œ๋‹ค.
  • ModelView๋ฅผ ํ†ตํ•ด ๋ทฐ์˜ ๋…ผ๋ฆฌ ์ด๋ฆ„์„ ์ „๋‹ฌํ•˜๊ณ  FrontController์—์„œ ๋ณ€ํ™˜ํ•˜์—ฌ MyView ๊ฐ์ฒด์— ์ „๋‹ฌํ•œ๋‹ค.

V4

  • ๊ธฐ์กด ๊ตฌ์กฐ์—์„œ ๋ชจ๋ธ์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜๊ธฐ๊ณ , ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๋ทฐ์˜ ๋…ผ๋ฆฌ ์ด๋ฆ„์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • ModelView ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

V5

โœ”๏ธ ์Šคํ”„๋ง MVC

์Šคํ”„๋ง MVC ์ „์ฒด ๊ตฌ์กฐ

DispacherServlet ์„œ๋ธ”๋ฆฟ ๋“ฑ๋ก

  • DispacherServlet ์€ HttpServlet ์„ ์ƒ์†๋ฐ›์•„์„œ ์‚ฌ์šฉํ•˜๊ณ  ์„œ๋ธ”๋ฆฟ์œผ๋กœ ๋™์ž‘
    • ์„œ๋ธ”๋ฆฟ์ด ํ˜ธ์ถœ๋˜๋ฉด HttpServlet์ด ์ œ๊ณตํ•˜๋Š” service()๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.
  • ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” DispacherServlet ์„ ์ž๋™์œผ๋กœ ๋“ฑ๋กํ•˜๋ฉด์„œ ๋ชจ๋“  ๊ฒฝ๋กœ(urlPatterns=โ€/โ€)์— ๋Œ€ํ•ด์„œ ๋งคํ•‘ ํ•œ๋‹ค.

Spring MVC ๋™์ž‘ ์ˆœ์„œ

  1. Handler ์กฐํšŒ
  2. Handler Adapter ์กฐํšŒ
  3. Handler Adapter ์‹คํ–‰
  4. Handler ์‹คํ–‰
  5. ModelAndView ๋ฐ˜ํ™˜
  6. ViewResolver ํ˜ธ์ถœ
  7. View ๋ฐ˜ํ™˜
  8. View ๋žœ๋”๋ง

์ฃผ์š” ์ธํ„ฐํŽ˜์ด์Šค

  1. Handler Mapping
  2. Handler Adapter
  3. View Resolver
  4. View

ํ•ธ๋“ค๋Ÿฌ ๋งคํ•‘๊ณผ ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ

์Šคํ”„๋ง ๋ถ€ํŠธ๊ฐ€ ์ž๋™ ๋“ฑ๋กํ•˜๋Š” ํ•ธ๋“ค๋Ÿฌ ๋งคํ•‘๊ณผ ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ

  • HandlerMapping
    • 0 = RequestMappingHandlerMapping: ์• ๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜์˜ ์ปจํŠธ๋กค๋Ÿฌ์ธ @RequestMapping ์—์„œ ์‚ฌ์šฉ
    • 1 = BeanNameUrlHandlerMapping: ์Šคํ”„๋ง ๋นˆ์˜ ์ด๋ฆ„์œผ๋กœ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ฐพ๋Š”๋‹ค.
  • Handler Adapter
    • 0 = RequestMappingHandlerAdapter: @RequestMapping ์–ด๋Œ‘ํ„ฐ
    • 1 = HttpRequestHandlerAdapter : HttpRequestHandler ์–ด๋Œ‘ํ„ฐ
    • 2 = SimpleControllerHandlerAdapter: Contoller ์ธํ„ฐํŽ˜์ด์Šค ์–ด๋Œ‘ํ„ฐ(Annotation X)

HandleRequest - Old Controller ํ˜ธ์ถœ ๊ณผ์ •

@Component("/springmvc/old-controller")
public class OldController implements Controller {
	 @Override
	 public ModelAndView handleRequest(HttpServletRequest request,
	 HttpServletResponse response) throws Exception {
	 System.out.println("OldController.handleRequest");
	 return null;
	 }
}
  1. ํ•ธ๋“ค๋Ÿฌ ๋งคํ•‘์œผ๋กœ ํ•ธ๋“ค๋Ÿฌ ์กฐํšŒ
    1. BeanNameUrlHandlerMapping ์„ ํ†ตํ•ด ๋งคํ•‘
    2. OldController ๋ฐ˜ํ™˜
  2. ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ ์กฐํšŒ
  3. ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ ์‹คํ–‰
    1. SimpleControllerHandlerAdapter ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด์„œ ํ•ธ๋“ค๋Ÿฌ ์ •๋ณด๋„ ํ•จ๊ป˜ ๋„˜๊ฒจ์ค€๋‹ค.
    2. SimpleControllerHandlerAdapter ๋Š” ํ•ธ๋“ค๋Ÿฌ์ธ OldController ๋ฅผ ๋‚ด๋ถ€์—์„œ ์‹คํ–‰ํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋กœ ModelAndView๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

๋ทฐ ๋ฆฌ์กธ๋ฒ„

์Šคํ”„๋ง ๋ถ€ํŠธ๊ฐ€ ์ž์ฃผ ๋“ฑ๋กํ•˜๋Š” ๋ทฐ ๋ฆฌ์กธ๋ฒ„

  • 1 = BeanNameViewResolver: ๋นˆ ์ด๋ฆ„์œผ๋กœ ๋ทฐ๋ฅผ ์ฐพ์•„์„œ ๋ฐ˜ํ™˜
  • 2 = InternalResourceViewResolver: JSP๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ทฐ๋ฅผ ๋ฐ˜ํ™˜
    • ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” InternalResourceViewResolver ๋ผ๋Š” ๋ทฐ ๋ฆฌ์กธ๋ฒ„๋ฅผ ์ž๋™์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค
    • ์ด ๋•Œ [application.properties](http://application.properties) ์— ๋“ฑ๋กํ•œ spring.mvc.view.prefix, spring.mvc.view.suffix ์„ค์ • ์ •๋ณด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋“ฑ๋กํ•œ๋‹ค

MVC ์ ์šฉ

@Controller

  • ์Šคํ”„๋ง์ด ์ž๋™์œผ๋กœ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค.
  • ์Šคํ”„๋ง MVC์—์„œ ์• ๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜ ์ปจํŠธ๋กค๋Ÿฌ๋กœ ์ธ์‹ํ•œ๋‹ค.

@RequestMapping

  • ์š”์ฒญ ์ •๋ณด ๋งคํ•‘
  • ์• ๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜ ์ž‘๋™, ํ•ด๋‹น URL ํ˜ธ์ถœ ์‹œ ์ด ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.

@GetMapping, @PostMapping

  • @GetMapping = @RequestMapping(method=RequestMethod.Get

โœ”๏ธ ์Šคํ”„๋ง MVC ๊ธฐ๋ณธ๊ธฐ๋Šฅ

๋กœ๊น…, RestController

  • SLF4J๋ผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์“ฐ๊ณ  ๊ตฌํ˜„์ฒด๋กœ Logback์„ ์‚ฌ์šฉํ•œ๋‹ค.

์„ ์–ธ ๋ฐฉ๋ฒ•

  • private Logger log = LoggerFactory.getLogger(getClass());
  • @Slf4j Annotation ์‚ฌ์šฉ

๋กœ๊ทธ ์ •๋ณด

  • ํฌ๋ฉง
    • ์‹œ๊ฐ„, ๋กœ๊ทธ ๋ ˆ๋ฒจ, ํ”„๋กœ์„ธ์ŠคID, ์“ฐ๋ ˆ๋“œ ๋ช…, ํด๋ž˜์Šค๋ช…, ๋กœ๊ทธ ๋ฉ”์‹œ์ง€
  • ๋กœ๊ทธ ๋ ˆ๋ฒจ
    • TRACE > DEBUG > INFO > WARN > ERROR
    • ๊ฐœ๋ฐœ ์„œ๋ฒ„๋Š” DEBUG
    • ์šด์˜ ์„œ๋ฒ„๋Š” INFO
  • ๋กœ๊ทธ ๋ ˆ๋ฒจ ์ˆ˜์ • ๋ฐฉ๋ฒ•
    • [application.properties](http://application.properties) - logging.level.hello,springmvc=debug
  • ์˜ฌ๋ฐ”๋ฅธ ๋กœ๊ทธ ์‚ฌ์šฉ๋ฒ•
    • log.debug("data=" + data) X , log.debug("data={}, data) O
    • ์ฒซ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์œผ๋กœ ์ง„ํ–‰ ์‹œ ๋กœ๊ทธ ์ถœ๋ ฅ ๋ ˆ๋ฒจ์„ INFO๋กœ ์„ค์ •ํ•ด๋„ ๋ฌธ์ž ๋”ํ•˜๊ธฐ ์—ฐ์‚ฐ์ด ๋ฐœ์ƒํ•œ๋‹ค.
  • ์žฅ์ 
    • ์“ฐ๋ ˆ๋“œ ์ •๋ณด, ํด๋ž˜์Šค ์ด๋ฆ„๊ณผ ๊ฐ™์€ ๋ถ€๊ฐ€์ •๋ณด๋ฅผ ํ•จ๊ป˜ ๋ณผ ์ˆ˜ ์žˆ๊ณ  ์ถœ๋ ฅ ๋ชจ์–‘ ์กฐ์ • ๊ฐ€๋Šฅ
    • ๋กœ๊ทธ ๋ ˆ๋ฒจ ์กฐ์ ˆ์„ ํ†ตํ•ด ๋กœ๊ทธ ์ถœ๋ ฅ์„ ์ƒํ™ฉ์— ๋งž๊ฒŒ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ์‹œ์Šคํ…œ ์•„์›ƒ ์ฝ˜์†”์—๋งŒ ์ถœ๋ ฅํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ํŒŒ์ผ์ด๋‚˜ ๋„คํŠธ์›Œํฌ ๋“ฑ, ๋กœ๊ทธ๋ฅผ ๋ณ„๋„์˜ ์œ„์น˜์— ๋‚จ๊ธธ ์ˆ˜ ์žˆ๋‹ค.
    • ์„ฑ๋Šฅ์ด System.out๋ณด๋‹ค ์ข‹๋‹ค.

@RestController

  • @Controller๋Š” ๋ฐ˜ํ™˜ ๊ฐ’์ด String์ด๋ฉด ๋ทฐ ์ด๋ฆ„์œผ๋กœ ์ธ์‹๋˜์–ด ๋ทฐ๋ฅผ ์ฐพ๊ณ  ๋ทฐ๊ฐ€ ๋žœ๋”๋ง๋œ๋‹ค.
  • @RestController๋Š” ๋ฐ˜ํ™˜ ๊ฐ’์œผ๋กœ ๋ทฐ๋ฅผ ์ฐพ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์— ๋ฐ”๋กœ ์ž…๋ ฅํ•œ๋‹ค.

์š”์ฒญ ๋งคํ•‘

		@RequestMapping("/hello-basic")
    public String helloBasic(){
        log.info("helloBasic");
        return "ok";
    }

    /**
     * method ํŠน์ • HTTP ๋ฉ”์„œ๋“œ ์š”์ฒญ๋งŒ ํ—ˆ์šฉ
     * GET, HEAD, POST, PUT, PATCH, DELETE
     */
    @RequestMapping(value = "/mapping-get-v1", method = RequestMethod.GET)
    public String mappingGetV1() {
        log.info("mappingGetV1");
        return "ok";
    }

    /**
     * ํŽธ๋ฆฌํ•œ ์ถ•์•ฝ ์• ๋…ธํ…Œ์ด์…˜ (์ฝ”๋“œ๋ณด๊ธฐ)
     * @GetMapping
     * @PostMapping
     * @PutMapping
     * @DeleteMapping
     * @PatchMapping
     */
    @GetMapping(value = "/mapping-get-v2")
    public String mappingGetV2() {
        log.info("mapping-get-v2");
        return "ok";
    }

    /**
     * PathVariable ์‚ฌ์šฉ
     * ๋ณ€์ˆ˜๋ช…์ด ๊ฐ™์œผ๋ฉด ์ƒ๋žต ๊ฐ€๋Šฅ
     * @PathVariable("userId") String userId -> @PathVariable userId
     */
    @GetMapping("/mapping/{userId}")
    public String mappingPath(@PathVariable("userId") String data) {
        log.info("mappingPath userId={}", data);
        return "ok";
    }

    /**
     * PathVariable ์‚ฌ์šฉ ๋‹ค์ค‘
     */
    @GetMapping("/mapping/users/{userId}/orders/{orderId}")
    public String mappingPath(@PathVariable String userId, @PathVariable Long
            orderId) {
        log.info("mappingPath userId={}, orderId={}", userId, orderId);
        return "ok";
    }

    /**
     * ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ถ”๊ฐ€ ๋งคํ•‘
     * params="mode",
     * params="!mode"
     * params="mode=debug"
     * params="mode!=debug" (! = )
     * params = {"mode=debug","data=good"}
     */
    @GetMapping(value = "/mapping-param", params = "mode=debug")
    public String mappingParam() {
        log.info("mappingParam");
        return "ok";
    }

    /**
     * ํŠน์ • ํ—ค๋”๋กœ ์ถ”๊ฐ€ ๋งคํ•‘
     * headers="mode",
     * headers="!mode"
     * headers="mode=debug"
     * headers="mode!=debug" (! = )
     */
    @GetMapping(value = "/mapping-header", headers = "mode=debug")
    public String mappingHeader() {
        log.info("mappingHeader");
        return "ok";
    }

    /**
     * Content-Type ํ—ค๋” ๊ธฐ๋ฐ˜ ์ถ”๊ฐ€ ๋งคํ•‘ Media Type
     * consumes="application/json"
     * consumes="!application/json"
     * consumes="application/*"
     * consumes="*\/*"
     * MediaType.APPLICATION_JSON_VALUE
     */
    @PostMapping(value = "/mapping-consume", consumes = "application/json")
    public String mappingConsumes() {
        log.info("mappingConsumes");
        return "ok";
    }

    /**
     * Accept ํ—ค๋” ๊ธฐ๋ฐ˜ Media Type
     * produces = "text/html"
     * produces = "!text/html"
     * produces = "text/*"
     * produces = "*\/*"
     */
    @PostMapping(value = "/mapping-produce", produces = "text/html")
    public String mappingProduces() {
        log.info("mappingProduces");
        return "ok";
    }

HTTP ์š”์ฒญ - ๊ธฐ๋ณธ, ํ—ค๋” ์กฐํšŒ

@Slf4j
@RestController
public class RequestHeaderController {

    @RequestMapping("/headers")
    public String headers(HttpServletRequest request,
                          HttpServletResponse response,
                          HttpMethod httpMethod,
                          Locale locale,
                          @RequestHeader MultiValueMap<String, String> headerMap,
                          @RequestHeader("host") String host,
                          @CookieValue(value = "myCookie", required = false) String cookie){
        log.info("request={}", request);
        log.info("response={}", response);
        log.info("httpMethod={}", httpMethod);
        log.info("locale={}", locale);
        log.info("headerMap={}", headerMap);
        log.info("header host={}", host);
        log.info("myCookie={}", cookie);
        return "ok";
    }
}
  • HttpServletResponse
  • HttpMethod : HTTP ๋ฉ”์„œ๋“œ๋ฅผ ์กฐํšŒํ•œ๋‹ค.
  • Locale : Locale ์ •๋ณด๋ฅผ ์กฐํšŒํ•œ๋‹ค.
  • @RequestHeader MultiValueMap<String, String> headerMap
    • ๋ชจ๋“  HTTP ํ—ค๋”๋ฅผ MultiValueMap ํ˜•์‹์œผ๋กœ ์กฐํšŒํ•œ๋‹ค.
  • @RequestHeader("host") String host
    • ํŠน์ • HTTP ํ—ค๋”๋ฅผ ์กฐํšŒํ•œ๋‹ค.
  • @CookieValue(value = "myCookie", required = false) String cookie
    • ํŠน์ • ์ฟ ํ‚ค๋ฅผ ์กฐํšŒํ•œ๋‹ค.
    • ์†์„ฑ
      • ํ•„์ˆ˜ ๊ฐ’ ์—ฌ๋ถ€: required
      • ๊ธฐ๋ณธ ๊ฐ’: defaultValue

HTTP ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ - ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ, ModelAttribute

ํด๋ผ์ด์–ธํŠธ์—์„œ ์„œ๋ฒ„๋กœ ์š”์ฒญ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌ ํ•  ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ๋ฐฉ๋ฒ•

  1. GET - ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ
    1. /url?username=hello&age=20
  2. POST - HTML Form
    1. content-type: application/x-www-form-urlencoded
    2. ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์— ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ ํ˜•์‹์œผ๋กœ ์ „๋‹ฌ username=hello&age=20
  3. HTTP message body์— ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ ๋‹ด์•„์„œ ์š”์ฒญ
    1. ์ฃผ๋กœ JSON ์‚ฌ์šฉ

โ€ป 1๋ฒˆ๊ณผ 2๋ฒˆ์€ Parameter ์กฐํšŒ, 3๋ฒˆ์€ Body ์กฐํšŒ

Prameter ์กฐํšŒ ๋ฐฉ๋ฒ•

  1. request.getParameter()
  2. @RequestParam
    • ๋ณ€์ˆ˜๋ช…๊ณผ Parameter Key๋ฅผ ๊ฐ™์€ ์ด๋ฆ„์œผ๋กœ ์„ค์ • ์‹œ Key ๊ฐ’ ์ƒ๋žต๊ฐ€๋Šฅ
    • String, int, Integer ๋“ฑ์˜ ๋‹จ์ˆœ ํƒ€์ž…์ด๋ฉด @RequestParam ๋„ ์ƒ๋žต ๊ฐ€๋Šฅ
    • ๊ธฐ๋ณธ๊ฐ’์ด @RequestParam(required=true)
    • DefaultValue ์„ค์ • ๊ฐ€๋Šฅ
    • Map์œผ๋กœ ์กฐํšŒ
      • @RequestParam Map<String, Object> paramMap

@ModelAttribute

  • ์‹ค์ œ ๊ฐœ๋ฐœ์„ ํ•˜๋ฉด ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ›์•„์„œ ํ•„์š”ํ•œ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ณ  ๊ทธ ๊ฐ์ฒด์— ๊ฐ’์„ ๋„ฃ์–ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.
    • ModelAttribute๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ
      @RequestParam String username;
      @RequestParam int age;
      HelloData data = new HelloData();
      data.setUsername(username);
      data.setAge(age);
    • ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ
      @ResponseBody
      @RequestMapping("/model-attribute-v1")
      public String modelAttributeV1(@ModelAttribute HelloData helloData) {
       log.info("username={}, age={}", helloData.getUsername(),
      helloData.getAge());
       return "ok";
      }
  • ๋™์ž‘์›๋ฆฌ
    • HelloData ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
    • HelloData ๊ฐ์ฒด์˜ ํฌ๋กœํผํ‹ฐ๋ฅผ ์ฐพ์•„ setter๋ฅผ ํ˜ธ์ถœํ•ด์„œ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ๊ฐ’์„ ๋ฐ”์ธ๋”ฉํ•œ๋‹ค.
      • ex) ํŒŒ๋ผ๋ฏธํ„ฐ ์ด๋ฆ„์ด username์ด๋ฉด setUsername() ๋ฉ”์„œ๋“œ๋ฅผ ์ฐพ์•„์„œ ํ˜ธ์ถœ

HTTP ์š”์ฒญ ๋ฉ”์‹œ์ง€ ๋ฐ”๋””

Text ํ˜•์‹

  • ์ฝ”๋“œ
    @Slf4j
    @Controller
    public class RequestBodyStringController {
    
        @PostMapping("/request-body-string-v1")
        public void requestBodyString(HttpServletRequest request, HttpServletResponse response) throws IOException{
            ServletInputStream inputStream = request.getInputStream();
            String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
    
            log.info("messageBody={}", messageBody);
    
            response.getWriter().write("ok");
        }
    
        @PostMapping("/request-body-string-v2")
        public void requestBodyStringV2(InputStream inputStream, Writer responseWriter) throws IOException{
            String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
            log.info("messageBody={}", messageBody);
            responseWriter.write("ok");
        }
    
        @PostMapping("/request-body-string-v3")
        public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) throws IOException{
            String messageBody = httpEntity.getBody();
            log.info("messageBody={}", messageBody);
            return new HttpEntity<>("ok");
        }
    
        @ResponseBody
        @PostMapping("/request-body-string-v4")
        public String requestBodyStringV4(@RequestBody String messageBody) throws IOException{
            log.info("messageBody={}", messageBody);
            return "ok";
        }
    }
  • HttpEntity : Http header, body ์ •๋ณด๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์กฐํšŒ
    • ๋ฉ”์‹œ์ง€ ๋ฐ”๋”” ์ •๋ณด๋ฅผ ์ง์ ‘ ์กฐํšŒ
    • ์‘๋‹ต์—๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • RequestEntity
  • ResponseEntity
    • HTTP ์ƒํƒœ ์ฝ”๋“œ ์„ค์ • ๊ฐ€๋Šฅ

์Šคํ”„๋งMVC ๋‚ด๋ถ€์—์„œ HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””๋ฅผ ์ฝ์–ด์„œ ๋ฌธ์ž๋‚˜ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•ด์„œ ์ „๋‹ฌํ•ด์ฃผ๋Š”๋ฐ, ์ด๋•Œ HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋ผ๋Š” ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•œ๋‹ค.

@RequestBody

  • ๋ฉ”์‹œ์ง€ ๋ฐ”๋””๋ฅผ ๊ฐ€์žฅ ํŽธํ•˜๊ฒŒ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋Š” Annotation
    • @RequestBody String messageBody

Json ํ˜•์‹

  • ์ฝ”๋“œ
    @Slf4j
    @Controller
    public class RequestBodyJsonController {
        private ObjectMapper objectMapper = new ObjectMapper();
    
        @PostMapping("/request-body-json-v1")
        public void requestBodyJsonV1(HttpServletRequest request, HttpServletResponse response) throws IOException{
            ServletInputStream inputStream = request.getInputStream();
            String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
    
            HelloData data = objectMapper.readValue(messageBody, HelloData.class);
            log.info("username={}, age ={}", data.getUsername(), data.getAge());
    
            response.getWriter().write("ok");
        }
    
        @ResponseBody
        @PostMapping("/request-body-json-v2")
        public String requestBodyJsonV2(@RequestBody String messageBody) throws IOException{
            HelloData data = objectMapper.readValue(messageBody, HelloData.class);
            log.info("username={}, age ={}", data.getUsername(), data.getAge());
            return "ok";
        }
    
        @ResponseBody
        @PostMapping("/request-body-json-v3")
        public String requestBodyJsonV3(@RequestBody HelloData data){
            log.info("username={}, age ={}", data.getUsername(), data.getAge());
            return "ok";
        }
    
        @ResponseBody
        @PostMapping("/request-body-json-v4")
        public String requestBodyJsonV4(HttpEntity<HelloData> httpEntity){
            HelloData data = httpEntity.getBody();
            log.info("username={}, age ={}", data.getUsername(), data.getAge());
            return "ok";
        }
    
        @ResponseBody
        @PostMapping("/request-body-json-v5")
        public HelloData requestBodyJsonV5(@RequestBody HelloData data){
            log.info("username={}, age ={}", data.getUsername(), data.getAge());
            return data;
        }
    }
  • ObjectMapper : Json ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋ฐ” ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•  ๋•Œ ์‚ฌ์šฉ

@RequestBody

  • @RequestBody HelloData data ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ ์„ ์–ธ ์‹œ ์ž๋™์œผ๋กœ data์— ๋ฉ”์‹œ์ง€ ๋ฐ”๋””๊ฐ€ ๋ฐ”์ธ๋”ฉ๋œ๋‹ค.
    • JSON ์‘๋‹ต โ†’ HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ โ†’ ๊ฐ์ฒด
  • ์ƒ๋žต ๋ถˆ๊ฐ€๋Šฅ, ์ƒ๋žต ์‹œ ModelAttribute๋กœ ์ธ์‹

@ReponseBody

  • ๋ฆฌํ„ด๊ฐ’์œผ๋กœ ๋ฐ”๋”” ๋ฉ”์‹œ์ง€ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • ๊ฐ์ฒด ๋ฆฌํ„ด ๊ฐ€๋Šฅ
    • ๊ฐ์ฒด โ†’ HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ โ†’ JSON ์‘๋‹ต

HTTP ์‘๋‹ต

์ •์  ๋ฆฌ์†Œ์Šค

  • ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” ํด๋ž˜์Šค ํŒจ์Šค์˜ ๋‹ค์Œ ๋””๋ ‰ํ† ๋ฆฌ์— ์žˆ๋Š” ์ •์  ๋ฆฌ์†Œ์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค
  • /static, /public, /resources. /META-INF/resources

๋ทฐ ํ…œํ”Œ๋ฆฟ

  • ๊ฒฝ๋กœ : src/main/resources/templetes
  • ๋ทฐ ํ…œํ”Œ๋ฆฟ ํ˜ธ์ถœ ์ปจํŠธ๋กค๋Ÿฌ ์ฝ”๋“œ
    @Controller
    public class ResponseViewController {
    
        @RequestMapping("/response-view-v1")
        public ModelAndView responseView1(){
            ModelAndView mv = new ModelAndView("response/hello")
                    .addObject("data", "hello!");
            return mv;
        }
        @RequestMapping("/response-view-v2")
        public String responseView2(Model model){
            model.addAttribute("data", "hello!");
            return "response/hello";
        }
    
        @RequestMapping("/response/hello")
        public void responseView3(Model model){
            model.addAttribute("data", "hello!");
        }
    }
  • Thymeleaf <appication.properties> ๊ธฐ๋ณธ๊ฐ’
    spring.thymeleaf.prefix=classpath:/templates/
    spring.thymeleaf.suffix=.html

HTTP ๋ฉ”์‹œ์ง€

  • ์ฝ”๋“œ
    @Slf4j
    @Controller
    public class ResponseBodyController {
    
        @GetMapping("/response-body-string-v1")
        public void responseBodyV1(HttpServletResponse response) throws IOException {
            response.getWriter().write("ok");
        }
        
        @GetMapping("/response-body-string-v2")
        public ResponseEntity<String> responseBodyV2(){
            return new ResponseEntity<>("ok", HttpStatus.OK);
        }
        
        @ResponseBody
        @GetMapping("/response-body-string-v3")
        public String responseBodyV3(){
            return "ok";
        }
        
        @GetMapping("/response-body-json-v1")
        public ResponseEntity<HelloData> responseBodyJsonV1(){
            HelloData helloData = new HelloData();
            helloData.setUsername("oh");
            helloData.setAge(20);
            return new ResponseEntity<>(helloData, HttpStatus.CREATED);
        }
        
        @ResponseStatus(HttpStatus.OK)
        @ResponseBody
        @GetMapping("/response-body-json-v2")
        public HelloData responseBodyJsonV2(){
            HelloData helloData = new HelloData();
            helloData.setUsername("oh");
            helloData.setAge(20);
            return helloData;
        }
    }
  • responseBodyV1
    • ์„œ๋ธ”๋ฆฟ์„ ์ง์ ‘ ๋‹ค๋ฃฐ ๋•Œ ์ฒ˜๋Ÿผ HttpServletResponse ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด์„œ HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์— ์ง์ ‘ ok ์‘๋‹ต ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.
    • response.getWriter().write("ok")
  • responseBodyV2
    • ResponseEntity ์—”ํ‹ฐํ‹ฐ๋Š” HttpEntity ๋ฅผ ์ƒ์† ๋ฐ›์•˜๋Š”๋ฐ, HttpEntity๋Š” HTTP ๋ฉ”์‹œ์ง€์˜ ํ—ค๋”, ๋ฐ”๋”” ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. ResponseEntity ๋Š” ์—ฌ๊ธฐ์— ๋”ํ•ด์„œ HTTP ์‘๋‹ต ์ฝ”๋“œ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • HttpStatus.CREATED ๋กœ ๋ณ€๊ฒฝํ•˜๋ฉด 201 ์‘๋‹ต์ด ๋‚˜๊ฐ€๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
  • responseBodyV3
    • @ResponseBody ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด view๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ , HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋ฅผ ํ†ตํ•ด์„œ HTTP ๋ฉ”์‹œ์ง€๋ฅผ ์ง์ ‘ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋‹ค. ResponseEntity ๋„ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.
  • responseBodyJsonV1
    • ResponseEntity ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋ฅผ ํ†ตํ•ด์„œ JSON ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜๋˜์–ด์„œ ๋ฐ˜ํ™˜๋œ๋‹ค.
  • responseBodyJsonV2
    • ResponseEntity ๋Š” HTTP ์‘๋‹ต ์ฝ”๋“œ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, @ResponseBody ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋Ÿฐ ๊ฒƒ์„ ์„ค์ •ํ•˜๊ธฐ ๊นŒ๋‹ค๋กญ๋‹ค.
    • @ResponseStatus(HttpStatus.OK) ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ์‘๋‹ต ์ฝ”๋“œ๋„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
      ๋ฌผ๋ก  ์• ๋…ธํ…Œ์ด์…˜์ด๊ธฐ ๋•Œ๋ฌธ์— ์‘๋‹ต ์ฝ”๋“œ๋ฅผ ๋™์ ์œผ๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜๋Š” ์—†๋‹ค. ํ”„๋กœ๊ทธ๋žจ ์กฐ๊ฑด์— ๋”ฐ๋ผ์„œ ๋™์ ์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋ ค๋ฉด ResponseEntity ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

RestController

  • @Controller + @ResponseBody
  • ํด๋ž˜์Šค ๋ ˆ๋ฒจ์— ๋‘๋ฉด ์ „์ฒด ๋ฉ”์„œ๋“œ์— ์ ์šฉ

HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ

0 = ByteArrayHttpMessageConverter

  • ๋ฐ”์ดํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค data[]
  • ํด๋ž˜์Šค ํƒ€์ž…: data[], ๋ฏธ๋””์–ด ํƒ€์ž…: */*

1 = StringHttpMessageConverter

  • String ๋ฌธ์ž๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.
  • ํด๋ž˜์Šค ํƒ€์ž…: String, ๋ฏธ๋””์–ด ํƒ€์ž…: */*

2 = MappingJackson2HttpMessageConverter

  • ํด๋ž˜์Šค ํƒ€์ž…: ๊ฐ์ฒด ๋˜๋Š” HashMap
  • ๋ฏธ๋””์–ด ํƒ€์ž…: application/json ๊ด€๋ จ
content-type: application/json                                                 EX
@RequestMapping
void hello(@RequestBody String data) {}              -> StringHttpMessageConverter

HTTP ์š”์ฒญ ๋ฐ์ดํ„ฐ ์ฝ๊ธฐ

  • ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๊ฐ€ canRead() ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ๋Œ€์ƒ ํด๋ž˜์Šค ํƒ€์ž…์„ ์ง€์›ํ•˜๋Š”์ง€ ํ™•์ธํ•œ ํ›„ HTTP ์š”์ฒญ์˜ Content-Type ๋ฏธ๋””์–ด ํƒ€์ž…์„ ์ง€์›ํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.
  • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋ฉด read() ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๋ฐ˜ํ™˜ํ•œ๋‹ค.

HTTP ์‘๋‹ต ๋ฐ์ดํ„ฐ ์ฝ๊ธฐ

  • ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๊ฐ€ canWrite() ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ๋Œ€์ƒ ํด๋ž˜์Šค ํƒ€์ž…์„ ์ง€์›ํ•˜๋Š”์ง€ ํ™•์ธํ•œ ํ›„ HTTP ์‘๋‹ต์˜ Accept ๋ฏธ๋””์–ด ํƒ€์ž…์„ ์ง€์›ํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.
  • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋ฉด write() ๋ฅผ ํ˜ธ์ถœํ•ด์„œ HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์— ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

์š”์ฒญ ๋งคํ•‘ ํ•ธ๋“ค๋Ÿฌ ๊ตฌ์กฐ

ArgumentResolver (HandlerMethodArgumentResolver)

  • ์• ๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜์˜ ์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋งค์šฐ ๋‹ค์–‘ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์ด๋ฅผ ์œ ์—ฐํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š”๊ฒŒ ArgumentResolver์ด๋‹ค
    • HttpServletRequest, Model, @RequestParam, @ModelAttribute, HttpEntity

ReturnValueHandler (HandlerMethodReturnValueHandler)

์š”์ฒญ์˜ ๊ฒฝ์šฐ @RequestBody์™€ @HttpEntity๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ArgumentResolver ๊ฐ€ ์žˆ๋Š”๋ฐ ์ด๋Š” ๋ฐ”๋””์˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์•„์™€ ์ฒ˜๋ฆฌํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด ArgumentResolver ๋“ค์ด HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ•„์š”ํ•œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

์‘๋‹ต์˜ ๊ฒฝ์šฐ @ReponseBody์™€ @HttpEntity๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ReturnValueHandler ๊ฐ€ ์žˆ๋Š”๋ฐ ์—ฌ๊ธฐ์„œ HTTP ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์‘๋‹ต ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“ ๋‹ค.

๊ธฐ๋Šฅ ํ™•์žฅ

  • ์Šคํ”„๋ง์ด ํ•„์š”ํ•œ ๋Œ€๋ถ€๋ถ„์˜ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์ง€๋งŒ
  • ๊ธฐ๋Šฅํ™•์žฅ์„ ์œ„ํ•ด์„œ๋Š” WebMvcConfigurer ๋ฅผ ์ƒ์† ๋ฐ›์•„์„œ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๋ฉด ๋œ๋‹ค.
    @Bean
    public WebMvcConfigurer webMvcConfigurer() {
     return new WebMvcConfigurer() {
     @Override
     public void addArgumentResolvers(List<HandlerMethodArgumentResolver>
    resolvers) {
     //...
     }
     @Override
     public void extendMessageConverters(List<HttpMessageConverter<?>>
    converters) {
     //...
     }
     };
    }

โœ”๏ธ ๊ทธ ์™ธ

@ModelAttribute

  • @ModelAttribute("name") ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ ์–ธ ์‹œ
    • model.addAttribute(โ€nameโ€, ๊ฐ์ฒด) ์ž๋™ ์‹คํ–‰
  • โ€œnameโ€ ์ƒ๋žต ์‹œ ์ฒซ ๊ธ€์ž๋ฅผ ์†Œ๋ฌธ์ž๋กœ ๋ฐ”๊พผ ํด๋ž˜์Šค๋ช…์ด name์œผ๋กœ ์ž๋™ ์„ค์ •
  • data ํƒ€์ž…์ด ๊ฐ์ฒด๋‚˜ Map์ผ ๊ฒฝ์šฐ @ModelAttribute ์ƒ๋žต๊ฐ€๋Šฅ

PRG(Post-Redirect-Get)

๊ทธ๋ฆผ๊ณผ ๊ฐ™์ด Redirect๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  View๋ฅผ ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœ ๋„์›Œ์ค€ ์ƒํƒœ์—์„œ ์ƒˆ๋กœ๊ณ ์นจ์„ ํ•˜๋ฉด PostRequest๊ฐ€ ๊ณ„์†ํ•ด์„œ ์‹คํ–‰๋œ๋‹ค.

โ†’ ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด Redirect๋ฅผ ํ†ตํ•ด Get Method๋ฅผ ํ˜ธ์ถœํ•ด์ค€๋‹ค.

RedirectAttributes

@PostMapping("/add")
    public String addItem(@ModelAttribute Item item, RedirectAttributes redirectAttributes){
        Item savedItem = itemRepository.save(item);
        redirectAttributes.addAttribute("itemId", savedItem.getId());
        redirectAttributes.addAttribute("status", true);
        // model.addAttribute("item", item);
        return "redirect:/basic/items/{itemId}";
    }
  • addAttributeํ•œ ๊ฐ’์ด URL์—์„œ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ๋‚˜๋จธ์ง€ ๊ฐ’๋“ค์€ Query๊ฐ’์œผ๋กœ ๋“ค์–ด๊ฐ„๋‹ค.
  • redirectAttribute๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ธ์ฝ”๋”ฉ์„ ์ž๋™์œผ๋กœ ํ•ด์ค€๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€