Back-End/Spring

[Spring] 웹 개발 기초 개념 - 정적 컨텐츠, MVC와 템플릿 엔진, API

유자맛바나나 2021. 7. 2. 00:23

 

 간단 요약

  • 정적 컨텐츠: html을 그대로 웹 브라우저에 전달하는 것
  • MVC와 템플릿 엔진: 서버에서 html을 수정, 가공하여 전달하는 방식
  • API: 서버에서 html을 전달하는 것이 아니라, JSON, XML 방식으로 데이터만 Client에게 전달하여 Client가 직접 View를 그리는 방식. 최근에는 API 방식으로 많이 개발되고 있으며, 서버끼리 통신할때도 html을 내릴 필요가 없기 때문에 API 방식을 많이 사용한다 

 

 정적 컨텐츠(Static Contents)

Spring Boot Reference Doc > Spring Boot Features > 7.1.5. Static Content 를 보면 기본적으로 String Boot는 static content를 서비스 한다고 나와있다.

By default, Spring Boot serves static content from a directory called /static (or /public or /resources or /META-INF/resources in the classpath or from the root of the ServletContext.
- Spring Boot Features > 7.1.5. Static Content

즉, 그림1과 같이 resources/static 위치에 html 파일을 작성해 넣으면 그림2와 같이 웹 브라우저에서 해당 html을 읽어준다.

이 때 생성한 static content 파일의 이름과 확장자(.html)를 포함해 도메인에 작성한다
(그림 2에서 http://localhost:8080/static_content_practice.html)

그림1. static content 예제 파일 생성

static_content_practice.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>static content</title>
</head>
<body>
정적 컨텐츠 입니다.
</body>
</html>

그림2. 웹 브라우저에서 보여주는 static content

 

정적 컨텐츠 동작 환경 그림

그림3. 정적 컨텐츠 동작 환경 (출처: 인프런 - 스프링 입문(김영한))

동작 순서

  1. 웹 브라우저에서 톰캣 서버(WAS)에 static_content_practice.html 을 요청(Request)
  2. 톰캣이 Spring 컨테이너에서 static_content_practice과 mapping되는 컨트롤러를 먼저 찾는다(그림3의 1)
  3. 컨트롤러가 없는 것을 확인하면 String Boot는 static 하위에 static_content_practice.html을 찾는다
  4. 웹 브라우저에 html을 전달하는 것으로 응답(Response)

 

 MVC와 템플릿 엔진

  • Model: 화면을 구현하는데 사용하는 데이터를 관리하는 객체
  • View: 화면을 구현하는(그리는) 객체
  • Controller: Model을 이용해 비즈니스 로직, 내부 연산 등을 처리하여 View에 전달(Control) 하는 객체

View: mvc-page.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
	<p th:text="'MVC practice. Request Param is ' + ${paramName}"> empty </p>
</body>
</html>
  • 경로: template: mvc-page.html
  • <p> tag: paramName 이라는 변수를 입력 받아 출력한다. 참고로, 템플릿 엔진이 동작하지 않을 땐 empty가 출력된다

 

Controller: HypeopleController.java

package com.hypeople.HYPEOPLE.controller;

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

@Controller
public class HypeopleController {

	...

    @GetMapping("mvc-practice")
    public String mvcMethod(@RequestParam("paramInput") String paramInput, Model model) {
        model.addAttribute("paramName", paramInput);
        return "mvc-page";
    }
}

기존에 생성한 컨트롤러(HypeopleController)에 "mvc-practice"로 요청이 들어올 경우 작동할 메서드인 mvcMethod를 추가했다

  • @RequestParam("paramInput") String paramInput: 클라이언트에서 Server에 요청할 때 parameter로 값을 넘겨줄 수 있도록 한 것이다 → 그림4 URL 참조
  • model.addAttribute("paramName", paramName): 클라이언트로부터 넘겨 받은 parameter를 model에 "paramName"을 key로 value인 paramInput을 추가해준다
  • return "mvc-page": View인 template: mvc-page.html을 return

 

서버 Respone 결과: 웹 브라우저(Client) 화면

그림4. MVC 패턴 적용예제. 클라이언트와 서버가 parameter를 주고 받은 결과

  • mvc-practice?paramInput=test
    1. mvc-practice로 요청하면 Server의 컨트롤러(HypeopleController.java)에서 @GetMapping("mvc-practice")를 찾는다.
    2. ?paramInput=test는 Mapping된 함수인 mvcMethod의 @RequestParam("paramInput") String paramInput 과 Mapping되어 "test"를 전달한다
    3. 마지막으로 html에서 model에 저장된 "test" 값을 받아 수정한 뒤 웹 브라우저에 보여준다

 

MVC, 템플릿 엔진 동작 화면

(출처: 인프런 - 스프링 입문(김영한))

 

 API

API DTO: ApiPracticeDto Class

package com.hypeople.HYPEOPLE.controller;

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

@Controller
public class HypeopleController {
	...

    static class ApiPracticeDto { // static 빼도 가능
        private String param1;
        private String param2;

        public String getParam1() {
            return param1;
        }

        public void setParam1(String param1) {
            this.param1 = param1;
        }

        public String getParam2() {
            return param2;
        }

        public void setParam2(String param2) {
            this.param2 = param2;
        }
    }
}
  • 편의상 HypeopleController 클래스 안에 생성
  • JSON 방식으로 변환되기 때문에 getter와 setter 모두 있어야 한다

 

API Method: adiMethod(@Controller 안에 생성)

package com.hypeople.HYPEOPLE.controller;

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

@Controller 
public class HypeopleController {

	...

    @GetMapping("api-practice")
    @ResponseBody // HTTP의 Body에 아래를 직접 넣어주겠다는 뜻(HTML의 <Body> tag가 아님!)
    public ApiPracticeDto apiMethod(@RequestParam("paramInput") String paramInput) {

        ApiPracticeDto apiPracticeDto = new ApiPracticeDto();
        apiPracticeDto.setParam1(paramInput);
        apiPracticeDto.setParam2(paramInput);

        return apiPracticeDto;
    }
}

"api-practice"로 요청이 들어올 경우 동작할 메서드인 apiMethod를 추가했다

  • API method를 작성할 때 반드시 @Controller 어노테이션이 달린 클래스 안에 작성해야 한다
  • @ResponseBody: View를 찾지 않고 HTTP의 Body에 아래를 직접 넣어 주겠다는 뜻(HTML의 <Body> tag가 아님!).
  • method paramete로 Model을 받지 않는다
  • apiPracticeDto.setParam1(paramInput): DTO 용도로 만든 클래스의 인스턴스를 생성해 클라이언트로부터 넘겨 받은 paramInput을 set해줌
  • return
    1. @ResponseBody 어노테이션이 달려 있으므로 return 할 때 html을 찾지 않고 클래스 객체 apiPracticeDto 자체를 반환한다.
    2. return으로 반환할 타입이 클래스 객체: JSON으로 반환하는 것이 Spring 기본 정책
    3. return으로 반환할 타입이 String: String 그대로 반환

 

서버 Respone 결과: 웹 브라우저(Client) 화면

그림5. api 방식으로 return 된 결과

  • api-practice?paramInput="test"
    1. api-practice로 요청하면 Server의 컨트롤러(HypeopleController.java)에서 @GetMapping("api-practice")를 찾는다.
    2. ?paramInput=test는 Mapping된 함수인 apiMethod의 @RequestParam("paramInput") String paramInput 과 Mapping되어 "test2"를 전달한다
    3. return된 DTO 클래스의 param1과 param2를 JSON

그림6. 페이지 소스

html로 작성된 페이지는 페이지 소스를 열면 html 문법으로 작성된 태그가 나오지만 HTTP 바디를 DTO 클래스 그대로 전달했기 때문에 그림 6과 같이 return으로 작성한 그대로 나온다

 

@ResponseBody 동작 원리

 

그림7. ResopnseBody 동작 원리&nbsp;(출처: 인프런 - 스프링 입문(김영한))

  • 기존에는 viewResolver가 동작했지만 @ResponseBody 어노테이션이 붙으면 HttpMessageConverter가 동작한다
  • Controller return 타입: 클래스 객체
    객체를 JSON 타입으로 변환하는 JsonConverter의 대표적인 라이브러리인 'MappingJackson2HttpMessageConverter'가 작동해 웹 브라우저에 전달함. 그 외에도 구글의 gson이 있지만 Spring의 default JsonConverter는 Jackson 버전2이다.
  • Controller return 타입: 문자열
    문자열일 땐 String Converter가 동작해 String 그대로 전달함