Java Category/Spring

[Spring 입문] 스프링 웹 개발 기초

ReBugs 2024. 1. 20.

이 글은 인프런 김영한님의 Spring 강의를 바탕으로 개인적인 정리를 위해 작성한 글입니다.


 

웹을 개발하는 방법에는 크게 세 가지가 있다.

1. 정적 컨텐츠 (static contents)

서버에서 따로 가공을 거치지 않고 파일을 웹 브라우저에 그대로 보내는 방식이다.



2. MVC와 템플릿 엔진(template engine)

과거의 JSP, PHP 같은 것들이 template engine이다.

정적 컨텐츠처럼 html을 웹 브라우저에 그대로 보내는 것이 아니라, 데이터를 가공 해서 html을 동적으로 바꿔서 웹 브라우저에 보내는 일을 한다.

이런 방법을 사용하기 위해서는 Model, View, Controller가 필요한데, 이를 합쳐서 MVC라고 부른다.

정적 컨텐츠와의 차이점
html을 웹 페이지에 그대로 전달해주는 것이 정적 컨텐츠 방식의 웹 개발인 반면,
MVC와 Template engine 방식은 server에서 약간의 변형을 하여 웹에 띄우는 방식이다.

 

3. API

html과 같은 방식이 아니라 JSON이라는 데이터 구조 포맷으로 클라이언트에게 데이터를 전달하는 방식을 API 방식이라고 한다.

view나 react를 사용할 때에도 API 방식을 사용한다.

 

정적 컨텐츠

Spring boot는 정적 컨텐츠 가능을 자동으로 제공한다.

spring boot는 /static 경로에 있는 static content들을 제공한다.

IntelliJ에서 /static 경로에 hello-static.html 을 만들고 그 안에 아무 내용이나 넣어보자.

resources/static/hello-static.html

<!DOCTYPE HTML>
<html>
<head>
 <title>static content</title>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
정적 컨텐츠 입니다.
</body>
</html>



이후 localhost:8080/hello-static.html 로 들어가면 해당 내용이 그대로 올라와있는 것을 알 수 있다.



이를 통해 알 수 있는 사실은 원하는 파일을 resources/static 폴더에 넣으면 그대로 반환이 된다는 것이다.

대신 이 방법은 정적 컨텐츠 방식이기 때문에 이후 가공이 들어가지 않고 그대로 반환이 된다.

  1. 웹 브라우저에서 localhost:8080/hello-static.html에 대해서 내장 서버인 tomcat에게 요청한다.
  2. tomcat 서버는 hello-static.html에 대해서 다시 Spring에게 요청한다.
  3. Spring이 hello-static.html 요청을 받으면
    3-1 스프링 컨테이너의 Controller에게 요청을 보낸다. (즉, Controller가 우선순위를 가진다.) Controller에 hello-static을 mapping 해놓은 메서드가 있는지 확인한다.
    3-2 Controller에서 hello-static이 mapping된 메서드를 못 찾았으므로, spring은 resources/static 폴더에서 hello-static.html 파일을 찾는다. 해당 파일을 찾게되면 그 파일을 반환해준다.

 

MVC와 템플릿 엔진

대다수의 웹 개발에서는 HTML을 그대로 주는 것이 아니라 서버에서 필요에 따라 프로그래밍 하여 동적으로 바꾸는 작업이 필요하다. 

이런 작업을 원활하게 수행하기 위해서는 각각의 역할을 나눌 필요가 있다.

이때 사용되는 것이 MVC이다.

다시 말해 정적컨텐츠는 파일을 그대로 웹브라우저로 전송하는 것이라면,

MVC는 서버에서 파일을 변형하여 내려주는 방식이다.

 

Model은 데이터 집합을 전달해주는 역할을 수행하고,

View는 화면을 그리는 역할만 수행하고,

Controller는 내부의 비지니스 로직이나 서버단의 일을 처리만 수행한다.

이런 구조를 MVC 구조라고 부른다.

@Controller
public class HelloController {
 	@GetMapping("hello-mvc")
 	public String helloMvc(@RequestParam("name") String nameValue, Model model) {
 		model.addAttribute("name", nameValue);
 		return "hello-template";
	}
}

위 코드는 HTML의 form 태그에서 name으로 전송된 값을 nameValue로 받아준다.

즉, @RequestParam을 사용함으로 인해서 name으로 전송된 값이 nameVal에 저장된 것이다.

이후 Model 객체에 addAttribute 메서드를 통해 값을 저장한다.

model.addAttribute("name", nameValue)에서 name이라는 key에 nameVal 값이 맵핑된다.

그 다음 hello-template를 반환하므로, resources/templates/hello-template.html을 찾아갈 것이다.

 

resources/templates/hello-template.html

<html xmlns:th="http://www.thymeleaf.org">
<body>
<p th:text="'hello ' + ${name}">hello! empty</p>
</body>
</html>

첫째 줄을 통해 Thymeleaf를 사용한다는 것을 알 수 있다.

이 템플릿 엔진은 html을 가공해서 웹 브라우저에 전달한다.

세번째 줄을 보면 ${name} 이라는 부분을 볼 수 있는데, ${name}은 모델에서 name이라는 key에 맵핑된 value 값을 대입하게된다.

hello! empty는 만일 서버 없이 이 html 파일을 열었을 때 출력될 디폴트 값을 표시해놓은 것이다.

 

이후 http://localhost:8080/hello-mvc?name=spring 경로를 통해 실행하면 아래와 같은 화면이 나온다.

?name=spring
name이라는 key에 spring이라는 값이 담겨져서 get방식으로 데이터를 전송한다는 의미
값을 전송하지 않고 에러가 나지 않도록 하는 방법
@GetMapping("hello-mvc")
public String helloMvc(@RequestParam(value = "name", required = false) String nameVal, Model model){
    model.addAttribute("name", nameVal);
    return "hello-template";
}

 "required = false"를 추가적으로 작성해주면 된다.
이 파라미터는 디폴트값이 true이고, true라면 key 값을 무조건 받아야한다.
하지만 false로 바꿔줌으로써 key 값을 받지 않아도 되는 것이다.
key 값을 받지 않았으므로 이 부분엔 null이 처리된다.


또는 값이 존재하지 않을 때, 기본값으로 설정한 값이 name에 들어가도록할 수 있다.

@GetMapping("hello-mvc")
public String helloMvc(@RequestParam(value = "name", required = false, defaultValue = "ReBugs")
                       String nameValue, Model model){
	model.addAttribute("name", nameValue);
	return "hello-template";
}

 

  1. 웹 브라우저에서 localhost:8080/hello-mvc 를 요청한다.
  2. spring boot를 띄울 때 같이 띄우는 내장 tomcat 서버를 먼저 거친다. tomcat 서버는 hello-mvc 요청이 왔음을 spring 컨테이너로 넘긴다.
  3. spring 컨테이너는 helloController에 hello-mvc가 @GetMapping을 통해 hello-mvc가 mapping 되어 있는 것을 확인하고, mapping 되어있는 해당 메서드를 실행한다.
    해당 메서드는 "hello-template"라는 String을 반환하고, model에는 (key : name, value : spring)이라는 값을 넣는다.
    그리고 이 값들을 viewResolver에게 넘겨준다.
  4. viewResolver가 동작한다.
    viewResolver는 view를 찾아주고, 템플릿 엔진을 연결시켜주는 역할을 한다.
    viewResolver가 templates/hello-template.html 을 찾아서 Thymeleaf 템플릿 엔진에게 처리 요청을 한다.
  5. 템플릿 엔진인 Thymeleaf가 렌더링해서 html로 변환한 후, 웹 브라우저에 반환한다.

정적 컨텐츠를 사용할 때에는 템플릿 엔진을 거치지 않고 바로 웹 브라우저에 넘겼지만, MVC 패턴은 템플릿 엔진을 통해 한 차례 변환이 된다.

 

API

spring 개발 시 이야기하는 API방식은, JSON 형식으로 바꾸어 반환하는 것을 말한다.

view없이 그대로 http body에 전달하는 방식이다.

@ResponseBody
@Controller
public class HelloController {
 	@GetMapping("hello-string")
 	@ResponseBody
 	public String helloString(@RequestParam("name") String name) {
 		return "hello " + name;
	}
}

http(통신 프로토콜)의 body 부에 return(반환)값을 직접 넣어주겠다는 뜻.
API를 사용하면 view가 따로 없다.
따라서 소스코드 보기를 통해 보아도 html 관련 코드는 없고, 출력된 문자만 보인다.
Template engine은 view라는 템플릿이 있는 상황에서 view를 html로 변환하는 것이고, API는 데이터를 그대로 내보내는 것이다.
즉, @ResponseBody 를 사용하면 뷰 리졸버( viewResolver )를 사용하지 않고, 대신에 HTTP의 BODY에 문자 내용을 직접 반환한다

 

@ResponseBody를 통해서 문자열(String)을 전달하는 경우는 거의 없고, 데이터. 즉, 객체를 요청할 때 주로 사용한다.

@Controller
public class HelloController {
    @GetMapping("hello-api")
    @ResponseBody
    public Hello helloApi(@RequestParam("name") String nameValue) {
        Hello hello = new Hello();
        hello.setName(nameValue);
        return hello;
    }
    static class Hello {
        private String name;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }
}
인텔리제이에서 getter와 setter 를 생성하는 단축키는 alt + insert (윈도우 기준)

이 코드를 실행하면 "/hello-api" 경로로 GET 방식으로 요청이 올 때, 요청에 포함된 "name" 파라미터를 이용하여 Hello 객체를 생성하고 이를 JSON 형태로 응답으로 반환다.

이러한 방식으로 클라이언트는 JSON 형태의 응답을 받게 된다.

<html xmlns:th="http://www.thymeleaf.org">
<body>
<p th:text="'hello ' + ${name}">hello! empty</p>
</body>
</html>

즉, @RequestParam을 통해 변수 nameValue를 받은 후, 해당 값을 hello 객체의 name 변수에 받은 값을 넣어준다. 그리고 hello 객체를 반환한다.

이때 JSON 형태로 변환되는 이유는 @ResponseBody에 의해 HttpMessageConverter가 실행되고, HttpMessageConverter 중에서 JsonConvert가 실행되기 때문이다.(기본 값)

 

  1. 웹 브라우저에서 localhost:8080/hello-api를 요청하면 tomcat 서버에서 hello-api 요청이 들어왔음을 Spring에게 알린다.
  2. Spring은 먼저 Controller에 hello-api가 맵 되어 있는지 확인한다.
    이 때, 만일 @ResponseBody가 적혀있지 않았다면, viewResolver에게 전달해서 template engine이 html로 변환한다.
  3. 메서드에 @ResponseBody 어노테이션이 있고, String을 반환하는 것이 아닌 hello 객체를 반환한다.
    따라서 기본적으로 JSON 방식으로 데이터를 만들어서 http응답에 반환한다.
  4. helloController에서 메서드가 실행되어 hello 객체를 넘기게 되면 "HttpMessageConverter"가 작동한다.
    만일 단순 문자열을 반환했다면 "StringConverter"가 동작하고, 객체를 반환했다면 "JsonConverter"가 동작한다.
    이 상황에서는 hello 객체를 받았기 때문에 JsonConverter가 동작하여 hello 객체의 내용을 JSON 형식으로 바꾼다.
  5. JSON 스타일로 바꾼 내용을 요청한 웹 브라우저/서버에게 응답한다.
정리
@ResponseBody 를 사용시 아래의 처리가 이뤄짐
HTTP의 BODY에 문자 내용을 직접 반환
viewResolver 대신에 HttpMessageConverter 가 동작
 - 기본 문자처리: StringHttpMessageConverter
 - 기본 객체처리: MappingJackson2HttpMessageConverter
 - byte 처리등등 기타 여러 HttpMessageConverter가 기본으로 등록되어 있음

댓글