[Spring] 스프링 웹 개발 기초 - 정적 컨텐츠/동적 컨텐츠
*인프런 김영한의 스프링 입문 강의를 기반으로 작성되었습니다.
웹 개발을 하는 3가지 방식은 다음과 같다.
정적 컨텐츠 - 서버에서 파일을 있는 그대로 웹 브라우저(클라이언트)로 전달한다.
MVC와 템플릿 엔진: 서버에서 프로그래밍을 통해 HTML을 동적으로 바꿔서 보내는 일을 하고, 이를 위해 MVC(Model, View, Controller) 패턴으로 개발하는 경우가 많다.
API - 클라이언트 또는 다른 서버와 JSON이라는 데이터 구조 포맷으로 클라이언트에게 데이터를 전달하는 방식을 말한다.
1. 정적 컨텐츠
스프링 부트는 정적 컨텐츠 기능을 자동으로 제공한다.
스프링부트 프로젝트 내 src/main/resource/static 위치에 hello-static.html 파일을 생성한다.
그리고 다음과 같은 코드를 입력 후 프로젝트(HelloSpringApplication.java 파일)를 실행한다.
<!DOCTYPE HTML>
<html>
<head>
<title>static content</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
정적 컨텐츠 입니다.
</body>
</html>
그런 다음 "http://localhost:8080/hello-static.html" 주소를 입력하여 브라우저에서 열어보면
body 태그 안에서 작성한 문장이 화면이 뜨는 것을 볼 수 있다.
스프링부트에서 정적 컨텐츠를 제공하는 동작 원리는 아래와 같다.
- 웹브라우저에서 localhost:8080/hello-static.html 주소를 내장 서버인 tomcat에게 요청한다.
- tomcat이 해당 요청을 받아서 hello-static.html에 대해 스프링 컨테이너에게 요청이 왔음을 알린다.
- spring이 hello-static.html 요청을 받으면 우선 컨트롤러에서 hello-static과 mapping되는 것이 있는지 확인한다.(즉, 컨트롤러가 우선순위를 가진다)
- 없다면 resources/static 폴더에서 hello-static.html 파일을 찾고, 해당 위치에 파일이 존재한다면 그 파일을 반환해준다.
동적 컨텐츠는 어떤 클라이언트가 언제, 어디서, 어떻게 서버에 요청했는지에 따라 각기 다른 결과를 보여준다
2. MVC와 템플릿 엔진
MVC 패턴은 Model, View, Controller을 의미한다.
디자인 패턴 중 하나로, 데이터를 표현하는 디자인(view)과 로직(model)을 분리함으로써 느슨한 결합이 가능하다. Model 은 앱에서 필요로 하는 모든 지속적 데이터를 관리하고, View 는 model 에서 받은 데이터를 시각적으로 보여주는 역할을 한다. Control 은 model 과 view 를 연결하는 역할을 한다.
템플릿 엔진은 정적 컨텐츠와 다르게 프로그래밍을 해서 html을 동적으로 바꿔서 웹 브라우저에 보내는 일을 한다. JSP, PHP 등이 이에 해당한다.
이제 MVC와 템플릿 엔진 방식의 원리를 살펴보자
Controller
scr/main/java/hello.hello_spring 위치에 controller 폴더를 하나 만들어서 HelloController.java라는 이름의 자바 파일을 만들어준다.
(폴더 위치는 아래 사진 참고)
그리고 다음과 같이 코드를 작성해준다. "hello-mvc"라는 요청이 들어왔을 경우 동작하는 메소드이다.
@Controller
public class HelloController {
@GetMapping("hello-mvc")
public String helloMvc(@RequestParam("name") String name, Model model) {
model.addAttribute("name", name);
return "hello-template";
}
}
- @GetMapping("hello-mvc")는 GET 요청(/hello-mvc)이 들어오면 helloMVC() 메소드를 호출한다.
- helloMVC() 메소드의 파라미터 값으로 @RequestParam이라는 어노테이션(주석이라는 뜻을 가진 코드 사이에 특별한 의미, 기능을 수행하도록 하는 기술)을 사용하여 클라이언트가 요청한 URL의 쿼리 파라미터에 대한 값을 받아올 수 있다.
- addAttribute() 메소드를 이용하여 View에 전달할 데이터를 key, value 형태로 model에 저장한다.
- View 파일명인 "hello-template"를 반환하면 뷰 리졸버(viewResolver: 객체를 View 영역으로 전달하기 위해 알맞은 View 정보를 설정하는 역할)가 해당 템플릿을 찾아서 Thymeleaf에게 처리해달라고 넘기고, Thymeleaf는 model에 저장된 데이터를 가져와 값을 변환하고, 변환된 HTML을 웹 브라우저에게 반환한다.
참고
@RequestParam("가져올 데이터의 이름") [데이터타입] [가져온 데이터를 담을 변수]
View
resources/templates 위치에 hello-template.html 이라는 이름으로 파일을 만들어 준다.
(폴더 위치는 아래 사진 참고)
그리고 다음과 같은 코드를 작성해준다.
<html xmlns:th="http://www.thymeleaf.org">
<body>
<p th:text="'hello ' + ${name}">hello! empty</p>
</body>
</html>
프로젝트(HelloSpringApplication.java)를 실행시키고 "http://localhost:8080/hello-mvc?name=spring" 처럼 /hello-mvc 요청 다음에 ?name=spring이라고 쿼리 파라미터를 넣어주면 전달받은 데이터를 모델에 저장하고 화면에서 출력시킨다.

MVC와 템플릿 엔진의 동작 원리는 아래와 같다.
- localhost:8080/hello-mvc 경로로 접속하게 되면 내장 톰캣 서버가 해당 요청을 받아서 스프링 컨테이너에게 해당 주소로 요청이 왔음을 알린다.
- 스프링 컨테이너는 helloController(내부 컨트롤러)에서 해당 URL과 mapping되는 메소드를 찾아서 호출한다. (매개변수로 model을 넘겨받는데, model은 스프링이 만들어서 넣어준다)
- 메소드 내에서 model 객체에 data라는 이름에 파라미터로 받은 name 값을 넣고 이후 반환값을 리턴한다.(model에 담으면 view에서 렌더링 할 때 사용)
- 이때 컨트롤러에서 리턴 값으로 문자를 반환하면, 뷰 리졸버가 화면을 찾고 Thymeleaf 템플릿 엔진에 처리를 요청한다. (뷰 리졸버: view를 찾아서 템플릿 엔진을 연결시켜줌)
- 템플릿 엔진이 렌더링을 해서 변환한 html을 웹 브라우저에 반환한다.
3. API
서버에 요청이 들어오면 view를 거치지 않고 컨트롤러에서 직접 JSON 형식의 데이터를 클라이언트에게 리턴해준다.
Controller
@ResponseBody (문자 반환)
다음 코드는 HelloController.java 파일에서 문자를 반환해 주는 코드이다.
HelloController에 "hello-string"이라는 요청이 들어왓을 경우 동작하는 메소드를 작성해주었다.
@Controller
public class HelloController {
@GetMapping("hello-string")
@ResponseBody
public String helloString(@RequestParam("name") String name) {
return "hello " + name;
}
}
@ResponseBody를 사용하면 뷰 리졸버를 사용하지 않고 HttpMessageConverter가 동작해서 HTTP의 BODY에 문자 내용을 직접 반환하겠다는 것을 의미한다.
프로젝트 실행 후 "http://localhost:8080/hello-string?name=spring!" 링크로 /hello-string 요청을 spring!이라는 파라미터와 같이 보내게 되면 아래 처럼 해당 문자값이 반환되어 있는 걸 볼 수 있다.
(오른쪽은 페이지 소스 보기 화면이다)
@ResponseBody (객체 반환)
다음은 HelloController.java 파일에서 객체를 반환하는 코드이다.
HelloController에 "hello-api"이라는 요청이 들어왓을 경우 동작하는 메소드와 hello 객체를 작성해주었다.
@Controller
public class HelloController {
@GetMapping("hello-api")
@ResponseBody
public Hello helloApi(@RequestParam("name") String name) {
Hello hello = new Hello();
hello.setName(name);
return hello;
}
static class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
helloApi() 메소드가 실행되면 hello라는 객체를 새로 생성하고 setName()을 통해서 파라미터로 전달받은 값을 이름으로 설정한다.
@ResponseBody를 사용하고, 객체를 반환하면 객체가 JSON으로 변환된다. (hello 객체를 반환)
프로젝트 실행 후 "http://localhost:8080/hello-api?name=spring!" 링크로 /hello-api 요청을 srping!이라는 파라미터와 같이 보내게 되면 다음과 같이 JSON 형식으로 변환되어 출력된 것을 볼 수 있다.(MappingJackson2HttpMessageConverter를 통하여 변환)

ResponseBody 작동원리는 아래와 같다.
- "localhost:8080/hello-api?name=spring"로 접속하게 되면 내장 톰켓 서버에서 요청을 받아 스프링 컨테이너에 해당 주소로 요청이 왔음을 알린다.
- 스프링 컨테이너는 helloController(내부 컨트롤러)에서 해당 URL과 mapping되는 메소드를 찾아서 호출한다. @ResponseBody 어노테이션을 확인하고 hello 객체를 넘긴다. 뷰 리졸버 대신에 HttpMessageCoonverter가 동작하고, 메서드 내 객체 존재를 확인 후 JsonConverter을 실행한다.
- 스프링 컨테이너에 @ResponseBody가 붙어있으면 반환값을 보고 HttpMessageConverter가 Json과 String 형식 중에 어떠한 것으로 변환할건지 결정한다. 반환값이 단순 문자열이면 StringConverter, 객체라면 JsonConverter가 동작한다.
<정리>
@ResponseBody를 사용하면
- HTTP의 BODY에 문자 내용을 직접 반환
- viewResolver 대신에 HttpMessageConverter가 동작
- 기본 문자처리: StringHttpMessageConverter
- 기본 객체처리: MappingJackson2HttpMessageConverter
- byte 처리 등 기타 여러 HttpMessageConverter가 기본적으로 등록되어 있음
참고 블로그
[ 김영한 스프링 입문 #2 ] 스프링 웹 개발 기초
[스프링 입문] 두 번째 section은 스프링 웹 개발 기초에 관한 내용이다. 참고로 지난 첫 번째 section의 [view 환경설정]과 유사한 내용이 있으니, 해당 링크를 참고하면서 봐도 좋을 것 같다. 내용은
velog.io
[Spring] 정적 컨텐츠/동적 컨텐츠
정적 컨텐츠와 동적 컨텐츠의 차이에 대해 알아보고 API 방식을 이용하여 JSON 형식의 데이터를 받아오는 코드를 작성해보자
velog.io