본문 바로가기
[inflearn] 스프링 입문

스프링 입문) 5. 스프링 웹 개발 기초 개념

by 슬픈 야옹이 2023. 3. 27.

웹 서비스를 개발하는 방법(패턴, 방법론이라고도 한다)은 크게 다음 세 가지가 있다.

 

정적 컨텐츠

  • 서버에서 웹 페이지를 그대로 클라이언트에 전달해줌

 

MVC와 템플릿 엔진

  • Model, View, Controller로 서버 기능을 나누어 서비스.
  • 서버에서 웹 페이지를 가공하여 클라이언트에 전달.

 

API

  • 서버는 JSON 등의 데이터 포맷으로 데이터만 클라이언트에게 전달하는 방식.
  • 전달받은 데이터를 가지고 클라이언트 측에서 페이지를 구성한다.
  • 안드로이드 앱 개발 등에서 사용한다.
  • 데이터만 주고받으면 되는 서버 간 통신에서도 사용한다.

 

 

정적 컨텐츠

서버에서 클라이언트에게 웹 페이지 원본을 그대로 전달해주는 방식이다.

스프링은 기본적으로 resources/static 폴더에 있는 파일들에 대해 정적 컨텐츠 제공 기능을 지원한다.

 

static 폴더는 resources 폴더 하위에 위치한다.

리소스란 소스코드 외에 서버가 관리 및 제공하는 각종 파일 및 데이터 등의 총칭으로, 자원이라고도 부른다.

 

resources/static 폴더에 아무 페이지나 작성한 뒤 접속해서 요청해보자.

 

resources/static 폴더 하위에 hello-static.html 파일을 작성한 뒤 저장한다.

hello-static.html 작성

 

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] 경로로 접속해보면 작성했던 페이지가 나타난다.

 

페이지 소스를 살펴보면 작성했던 페이지 원본이 그대로 전송된 것을 확인할 수 있다.

 

 

동작 과정을 그림으로 표현하면 다음과 같다.

 

1. 웹 브라우저가 [localhost:8080/hello-static.html] 경로로 접속한다.

2. 서버가 들어온 요청에 대해 처리를 시작한다.

    스프링 내부에 hello-static 관련 컨트롤러가 없으므로 resources/static 폴더를 찾아본다.

3. resources/static 폴더에 hello-static.html 파일이 있으므로 해당 파일을 웹 브라우저에게 전송한다.

4. hello-static.html 파일을 전달받은 웹 브라우저가 페이지를 화면에 띄운다.

 

 

 

 

MVC와 템플릿 엔진

MVC는 Model, View, Controller의 약자로,

MVC 모델이란 서버 기능을 Model, View, Controller의 세 부분으로 나누어서 구현하는 개발 방식을 말한다.

 

서버 기능을 Model, View, Controller로 나누어 기능을 구현하면

각 부분은 자신이 맡은 기능만 구현하면 되므로 유지보수가 용이하다는 장점이 있다.

 

기본적으로 View는 화면 구성을, Controller는 서버로 들어오는 요청(지금까지는 모두 접속의 형태로 표현하였다)에 대한 처리를 구현하고, Model은 View와 Controller 간 데이터 교환을 구현한다.

 

Model, View, Controller를 정리하면 다음과 같다.

View - 화면 구성

Controller - 요청처리

Model - View, Controller 간 데이터 교환

 

템플릿 엔진은 쉽게 말해 웹 페이지를 설정에 맞게 구성해주는 도구다.

지금 사용중인 thymeleaf가 템플릿 엔진 중 하나다.

 

 

MVC와 템플릿 엔진을 사용해서 간단한 서버 기능을 구현해보자.

 

HelloController 클래스에 helloMvc() 메서드를 추가한다.

 

HelloController 클래스 전체 소스코드

package hdxian.hdxianspring.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 HelloController {

    @GetMapping("hello")
    public String hello(Model model) {
        model.addAttribute("data", "suction~");
        return "hello";
    }

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

}

 

helloMvc() 메서드의 내용을 살펴보면

@GetMapping("hello-mvc")

- "hello-mvc" 라는 내용으로 들어오는 get 요청을 처리하는 메서드로 지정하는 어노테이션.

- 예를 들면 http://localhost:8080/hello-mvc 라고 입력한 요청이 들어올 때 이 메서드가 동작한다.

 

@RequestParam("name") String name

- 이 메서드에 연결된 경로로 요청하려면 name이라는 이름의 파라미터를 함께 전달해야 한다.

- 전달한 파라미터는 name(String name) 변수에 저장된다.

 

model.addAttribute("name", name);
return "hello-template";

- html에 전달할 model에 name이라는 이름으로 변수 name의 내용을 전달함.

- hello-template이라는 이름의 페이지를 요청한 클라이언트에게 전달한다.

 

 

 

templates 폴더에 hello-template.html 파일을 작성해 저장한다.

 

hello-template.html 전체 소스코드

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

이 페이지에 name이라는 이름으로 전달된 데이터는 ${name} 부분에 치환된다.

 

hello! empty 부분은 서버를 거치지 않고 html 파일을 열어볼 때 hello! ${name} 대신에 출력되는 부분이다.

thymeleaf 엔진이 제공하는 기능이며, html 페이지만 다루는 작업 등에서 쓰인다.

 

 

서버를 시작하고 localhost:8080/hello-mvc 경로로 접속한다.

그러면 짜잔. 에러 페이지가 뜬다. 왜 뜬걸까?

 

일단 이런 식으로 에러 페이지가 뜨면 intelliJ에서 에러 메시지를 확인할 수 있다.

intelliJ 아래쪽 Run 창을 보면 다음과 같은 에러 메시지가 나타나 있다.

보아하니 name 파라미터가 존재하지 않는다는 내용이다.

 

 

name 파라미터를 추가해서 접속 경로를 수정한다.

주소창에 localhost:8080/hello-mvc?name=hdxian 이라 입력하여 접속한다.

이번에는 페이지가 제대로 표시된 걸 확인할 수 있다.

 

 

전체 실행 과정을 보면 다음과 같다.

1. 웹 브라우저에서 localhost:8080/hello-mvc?name=hdxian으로 접속한다.

 

2. 스프링 내부에 hello-mvc라는 이름의 요청에 대한 Controller 메서드가 helloMvc()로 정의되어 있으므로 해당 메서드가 실행된다. 이 메서드가 실행될 때 ?name=hdxian으로 전달한 내용은 메서드의 name 변수에 저장된다.

 

3. helloMvc() 메서드에서 model 인스턴스에 name이라는 이름으로 변수 name의 내용을 저장한 뒤 hello-template라는 이름의 웹 페이지에게 전달한다.

 

4. 스프링 내부의 viewResolver가 templates/hello-template.html을 찾아 model 인스턴스의 내용과 함께 html 페이지 내용을 구성한 후 웹 브라우저에게 전달한다.

 

5. 웹 브라우저는 전달받은 html 페이지를 띄운다.

 

 

+) @RequestParam 추가

실습에서는웹 브라우저에서 접속할 때 name 파라미터를 전달하지 않으면 에러가 난다.

@RequestParam으로 지정한 name 변수에 값이 들어오지 않으면 에러가 나기 때문이다.

하지만 설정을 조금 바꾸면 name 파라미터를 전달하지 않아도 에러가 나지 않게 할 수 있다.

 

@RequestParam에 설정할 수 있는 값들을 보면 value, required 등이 있다.

이 중 required는 @RequestParam으로 지정한 매개변수가 반드시 있어야 하는지에 대한 설정인데,

기본값이 true로 되어있다.

이를 false로 설정하면 name을 전달하지 않아도 에러가 발생하지 않는다.

 

required를 false로 설정하고 서버를 재시작한 뒤 name을 전달하지 않고 접속해보면

name을 전달하지 않았음에도 에러가 발생하지 않고 페이지가 잘 뜨는걸 확인할 수 있다.

name에 전달된 값이 없으므로 출력은 null이라 뜬다.

 

 

 

 

 

 

API

서버 측에서 클라이언트가 요청하는 데이터를 특정 형식으로 보내주는 방식이다.

MVC 방식과의 차이점이라면 MVC는 템플릿 엔진을 통해 가공한 html 파일을 클라이언트에게 전달하지만,

API는 서버에서 데이터만 보내주고, 클라이언트 측에서 이 데이터를 이용해 페이지를 구성한다는 점에서 차이가 있다.

 

클라이언트가 웹 브라우저 혹은 모바일 어플리케이션이라면 받은 데이터를 바탕으로 페이지를 구성하고,

또 다른 서버라면 받은 데이터를 자신의 다른 작업에 사용한다.

 

손님에게 음식을 제공하는 식당도 식재료 납품업체 입장에선 손님이듯,

서버도 다른 누군가에게 데이터를 요청하는 순간에는 클라이언트가 될 수 있다.

 

 

코드를 통해 스프링에서 API 방식을 구현하는 방식을 알아보자.

 

HelloController 클래스에 helloString 메서드를 추가한다.

단, 메서드에 @ResponseBody 어노테이션을 추가한다.

 

helloString 메서드 소스코드

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

 

@ResponseBody 어노테이션이 붙은 메서드는 요청을 처리할 때 데이터를 Http 메시지의 Body 부분에 직접 넣어서 클라이언트에게 전달한다.

 

자세한 내용은 잠시 뒤로 미루고, 서버를 시작한 뒤 name에 적당한 값을 넣어 접속해보면

다음과 같이 페이지가 뜨는걸 확인할 수 있다.

 

얼핏 봐서는 MVC와 차이가 없어보이지만, 페이지 소스를 보면 차이점이 분명하다.

hello-string 페이지 소스

 

mvc 방식에서는 페이지 소스가 html 파일인 반면, hello-string에서는 문자열만 달랑 왔다.

 

helloString 메서드가 "hello " + name 의 형태로 리턴한 문자열이 그대로 전달된 것이다.

 

 

hello-mvc 페이지 소스

 

 

 

 

같은 방식으로 자바 클래스도 전달할 수 있다.

HelloController 클래스에 helloApi() 메서드와 Hello 클래스를 작성한다.

 

 

helloApi() 메서드와 Hello 클래스를 포함한 전체 HelloController 클래스의 소스코드는 다음과 같다.

package hdxian.hdxianspring.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 HelloController {

    @GetMapping("hello")
    public String hello(Model model) {
        model.addAttribute("data", "suction~");
        return "hello";
    }

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

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


    @GetMapping("hello-api")
    @ResponseBody
    public Hello helloApi(@RequestParam("name") String name) {
        Hello h1 = new Hello();
        h1.setName(name);
        return h1;
    }

    static class Hello {
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

    }



}

 

helloApi() 메서드를 보면 이전의 helloStirng() 메서드와 같이 @ResponseBody 어노테이션이 붙어있고,

리턴 타입이 Hello 클래스다.

 

요청이 들어오면 메서드 내에서 Hello 객체를 하나 생성(h1)하고, 전달받은 name 파라미터 값을 객체의 name 멤버로 설정한 다음, 생성한 객체를 통째로 리턴한다.

 

이렇게 객체를 통째로 리턴하면 그 데이터는 JSON 형식으로 클라이언트에 전달된다.

 

Hello 클래스를 보면 name 멤버는 private로 접근이 제한되어 있고, name에 대한 getter, setter가 정의되어 있다.

이런 식으로 객체 멤버에 접근하도록 하는 방식을 property 접근 방식이라고 한다.

 

또한 스프링의 다른 기능들이 자바 클래스의 객체 멤버에 접근할 때 getter와 setter를 이용하도록 규약이 정해져 있는데,

이를 자바 빈 규약이라 한다.

 

 

직접 서버를 실행해 접속해보자.

localhost:8080/hello-api?name=suction 으로 접속하였다.

조금 다른 모습으로 페이지가 뜨는 것을 확인할 수 있다.

 

페이지 소스를 확인해보아도 마찬가지다.

 

 

이처럼 {속성 : 값}의 형태로 데이터를 구성하는 방식을 JSON이라 한다.

JSON에 대한 자세한 내용은 직접 찾아보는 것이 좋다. 그리 어렵지 않은 개념이다.

 

 

@ResponseBody 어노테이션이 붙은 메서드의 동작 과정을 자세히 살펴보면 다음과 같다.

@ResponseBody 동작원리

 

1. 웹 브라우저에서 서버로 hello-api라는 이름의 요청을 보낸다.

 

2. 스프링 내부에 해당 요청에 대한 메서드(helloApi())가 정의되어 있으므로 메서드가 실행된다.

 

3. helloApi() 메서드는 @ResponseBody 어노테이션이 붙어있으므로 리턴 값을 http 메시지의 body에 직접 넣어서 전달한다.

 

4. 스프링 내부의 HttpMessageConverter가 동작하여 리턴 타입에 따라 전달할 데이터를 구성한다.

(helloMvc()에서는 HttpMessageConverter가 아니라 viewResolver가 동작했다.)

 

5. hello-api는 리턴 타입이 클래스(Hello)이므로 JsonConverter가 동작해 데이터를 JSON으로 구성하여 전달한다.

 

6. 브라우저는 전달받은 데이터를 창에 표시한다.

 

 

@ResponseBody가 붙은 메서드가 실행되면 viewResolver가 아니라 HttpMessageConverter가 동작한다.

@ResponseBody 어노테이션은 리턴 값을 http 메시지의 body에 직접 넣어서 전달하라는 의미이기 때문이다.

 

HttpMessageConverter는 메서드의 리턴 타입에 따라 JSONConverter, StringConverter 등 다양한 converter를 이용하여 전달할 데이터를 구성한다.

 

helloApi() 메서드는 리턴 타입이 클래스였으므로 JsonConverter가 동작하였고,

이전의 helloString() 메서드가 동작할 때는StringConverter가 동작하여 문자열 데이터가 그대로 전달되었다.

 

그 외에도 byte 타입 처리 등 다양한 HttpMessageConverter가 스프링에 기본적으로 등록되어 있다.

대표적으로 클래스 타입에 대한 처리는 MappingJackson2HttpMessageConverter가, 

기본 문자 처리는 StringHttpMessageConverter로 등록되어 있는데,

이들은 모두 라이브러리라 내가 사용하고싶은 다른 라이브러리로 바꿀 수도 있다.

하지만 지금 단계에서는 다루지 않는다.

 

따라서

1. @ResponseBody를 사용한 메서드는 http 메시지의 body에 직접 넣어서 전달한다는 점,

2. 그래서 viewResolver가 아닌 HttpMessageConverter가 동작한다는 점,

3. HttpMessageConverter는 리턴 타입별로 다양한 Converter를 사용하여 데이터를 구성한다는 점.

이 세가지를 알면 충분하다.

 

 

 

 

 

정리하면

이 포스트에서는 스프링이 웹 서비스를 제공하는 3가지 방법에 대해 정리하였다.

상세 내용이 많으므로 각 키워드에서 관련 내용이 떠올라야 한다.

 

정적 컨텐츠

  • 서버에서 웹 페이지를 그대로 클라이언트에 전달.
  • resources/static 폴더에서 페이지를 관리한다.

 

MVC와 템플릿 엔진

  • Model, View, Controller로 서버 기능을 나누어 구현한다.
  • 서버에서 웹 페이지를 가공하여 클라이언트에 전달.
  • resources/templates 폴더에서 페이지를 관리한다.

 

API

  • 서버는 JSON 등의 데이터 포맷으로 데이터만 클라이언트에게 전달하는 방식.
  • @ResponseBody를 이용하여 http 메시지의 body에 직접 데이터를 넣어 전달한다.
  • controller 메서드의 리턴 타입에 따라서 다양한 형태의 데이터를 전달할 수 있다.
  • 전달받은 데이터를 가지고 클라이언트 측에서 페이지를 구성한다.
  • 안드로이드 앱 개발, 서버 간 통신에서도 사용한다.