본문 바로가기
개발/스프링

[Spring] 스프링 웹 개발의 기초

by 카펀 2022. 3. 3.

개요

지금까지 Spring Boot를 이용해 간단한 서비스를 만드는 과정을 체험해 보았고, 어렴풋이 Spring 개발은 어떻게 진행되고, 어디에 어떤 클라스를 작성하는지 알게 되었습니다.

Spring을 어느 정도 체감하였으니, 이제 내부 구조, 동작 방식 등 이론적인 내용을 알아볼 때라고 생각됩니다.

 

Spring으로 웹 서비스 개발을 할 때, 컨텐츠를 보여 주는 방법은 크게 세 가지가 있습니다.

 

  • 정적 컨텐츠
  • MVC
  • API

이 세 방식에 대해 각각 간단한 소개를 해 보려고 합니다.

더 자세한 내용인 추후 공부하며 알아갈 예정입니다.

 

정적 컨텐츠

정적 컨텐츠라고 함은, 변화가 없는 컨텐츠를 뜻합니다.

사진, 문서 등 있는 그대로를 보여주는 것을 정적 컨텐츠를 보여준다고 하며, 어떤 사용자가 와도 같은 내용을 보여주게 됩니다.

Spring에서는 이러한 정적 컨텐츠를 보여주는 기능을 제공합니다.

 

공식 문서 (Spring Boot 2.6.4 공식 문서 - 정적 컨텐츠)에 보면,

스프링 부트는 디렉토리 /static (또는 /public, /resources, /META-INF/resources)로부터 정적 컨텐츠를 읽어 온다고 합니다.

이 컨텐츠의 접근 방법은 (서비스 주소)/파일 이름 이 되겠습니다.

예를 들어, localhost:8080이 서비스 주소이고, /static 디렉토리 내에 hello.html이라는 파일이 있다면,

localhost:8080/hello.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. 클라이언트는 Spring Boot의 내장 Tomcat에게 요청을 전달함
  3. Tomcat은 이 요청을 Spring Container에 넘김
  4. Spring Container는 Controller에 hello-static.html이 있는지 찾아봄 (Controller가 우선권을 가짐)
  5. Controller에 등록되지 않은 요청이므로, resources/hello-static.html을 찾아봄
  6. 존재하므로, Spring Container는 이를 클라이언트에 반환함

MVC

MVC란 model, view, controller의 앞 글자를 따서 부르는 말입니다.

예전에는 파일 (jsp 파일) 하나에 모든 기능이 다 들어 있었습니다. view를 그리는 로직, 비지니스/컨트롤러 로직, db 접근 등 모든 내용이 파일 하나 내에서 이루어졌습니다. 이 방식을 Model 1 방식이라고 부릅니다.

서비스가 확장될수록 파일의 코드 수는 길어지기 시작합니다. 유지보수가 어려워지고, 모든 기능이 파일 하나에 들어 있게 되면서 내용 파악이 어려워집니다.

이를 해결하기 위해 기능별로 분리한 것이 MVC 구조입니다.

Model, view, controller로 서비스 구성 요소를 분리하고, 각각의 역할에만 충실하고 책임을 지도록 구성하였습니다.

  • Model: 시스템의 비지니스 로직을 담당합니다.
  • View: 화면을 그리는 기능에만 집중합니다.
  • Controller: 내부적인 요소 처리에 집중합니다. 유저의 요청에 따라 Model과 View를 관리 (control)합니다.

이러한 방식을 Model 2 방식이라고 합니다. 사실상 업계 표준이 되었다고 할 수 있습니다.

 

다음과 같은 컨트롤러를 작성해 보겠습니다.

 

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

 

위 코드는 hello-mvc라는 요청을 받으면 동작합니다. name과 model을 인자로 각각 받고, model에 key: name, value: name인 데이터 쌍을 추가한 후, hello-template를 호출합니다.

 

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

hello-template는 위와 같이 이루어져 있습니다. 템플릿 엔진은 Thymeleaf를 사용하였습니다.

앞에 controller로부터 전달받은 model에서는 key: name, value: name인 값이 들어 있습니다.

이 값이 ${name}의 위치에 치환이 됩니다.

 

좌: localhost:8080/hello-mvc?name=티스토리 접근 결과, 우: 해당 페이지 소스 보기

좌측을 보면, 요청에 파라미터로 'name=티스토리' 를 전달했고, 그 결과로 hello + '티스토리' 가 출력된 것을 볼 수 있습니다.

우측에서 소스를 보면, 완전한 HTML 문서로 완성된 채로 전달받은 것을 볼 수 있는데요.

어떤 과정을 통해 이런 일이 이루어졌는지 확인해 봅시다.

 

  1. 클라이언트를 이용해 localhost:8080/hello-mvc?name=티스토리 라는 요청을 보냄
  2. 클라이언트는 Spring Boot의 내장 Tomcat에게 요청을 전달함
  3. Tomcat은 이 요청을 Spring Container에 넘김
  4. Spring Container는 hello-mvc라는 요청을 받을 Controller를 찾아봄 -> Controller가 존재
  5. 해당 Controller에게 key: name, value: 티스토리 라는 값을 함께 전달함
  6. helloMvc 메소드는 model에 'name:티스토리' 인 값을 넣고, hello-template를 호출함
  7. hello-template는 'name:티스토리' 인 값을 전달받으며 호출됨
  8. 템플릿의 ${name}이 '티스토리'로 치환된 후 html 파일로 완성됨
  9. Spring Container는 완성된 html 파일을 클라이언트에게 전달

API

API는 Application Programming Interface의 약자로, 보통 우리가 무언가 데이터를 요청할 때 'API를 호출한다'고 합니다.

Controller는 아래와 같이 작성할 수 있는데요.

 

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

 

앞서 MVC의 Controller를 작성할 때에 비해 annotation이 한개 더 추가되었습니다.

@ResponseBody 어노테이션인데요.

메소드 앞에 이 어노테이션이 붙으면, MVC 때처럼 model을 구성하여 전달하는 대신, 값을 직접 전달하게 됩니다.

무슨 말이냐 하면, HTML의 body 부분에 해당하는 내용을 직접 리턴한다고 볼 수 있습니다.

 

따로 템플릿 엔진 없이, 위 메소드를 호출해 보겠습니다.

http://localhost:8080/hello-string?name=카펀 이라고 입력해 보겠습니다.

 

좌: localhost:8080/hello-string?name=카펀 접근 결과, 우: 해당 페이지 소스 보기

좌측 결과만 보면 앞서 MVC 때와 큰 차이가 없어 보입니다.

하지만 소스 보기를 하면, 우측과 같이 아무런 HTML 태그 없이 내용 그 자체만 있는 것을 볼 수 있습니다.

앞서 말했던 '값을 직접 전달한다' 가 이런 뜻입니다.

 

하지만 보통 이렇게 사용하는 경우는 잘 없습니다.

앞서 말한 바와 같이, API는 데이터를 요청할 때 주로 호출합니다. 즉, 데이터를 전달하는 수단으로 더 널리 쓰입니다.

방금 전에는 문자열을 리턴했지만, 객체를 리턴할 수도 있습니다.

 

@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;
    }
}

 

위와 같이 Hello라는 클라스가 있습니다. 멤버 변수는 private String인 name 하나 뿐이고, getter와 setter가 있습니다.

메소드 helloApi가 이 클라스를 생성하고, 인자로 받은 name을 hello의 setter를 통해 설정한 후, hello를 리턴합니다.

그러면 어떻게 될까요?

 

좌: localhost:8080/hello-api?name=카펀 접근 결과, 우: 해당 페이지 소스 보기

위와 같은 결과를 받게 됩니다.

위 형식은 JSON 형식이라고 합니다. 데이터를 나타내는 형식 중 하나로, "key" : "value" 형태로 데이터를 나타냅니다.

앞서 우리가 Controller에서 클라스 hello의 멤버 변수 name의 값을 '카펀' 으로 설정했었죠?

그러한 결과를 위처럼 나타내는 겁니다.

JSON 외에도 XML 등 다양한 데이터 형식으로 값을 받을 수 있지만, Spring에서는 기본 형식으로 JSON을 사용하고 있습니다 (그만큼 대중적이고 널리 사용됩니다).

 

전달받는 과정을 살펴보겠습니다.

  1. 클라이언트를 이용해 localhost:8080/hello-api?name=카펀 이라는 요청을 보냄
  2. 클라이언트는 Spring Boot의 내장 Tomcat에게 요청을 전달함
  3. Tomcat은 이 요청을 Spring Container에 넘김
  4. Spring Container는 hello-api라는 요청을 받을 Controller를 찾아봄 -> Controller가 존재
  5. 해당 Controller에게 key: name, value: 카펀 이라는 값을 함께 전달함
  6. helloMvc 메소드는 @ResponseBody 어노테이션이 달려 있으므로, model 및 view를 거치지 않고 값을 바로 리턴함
  7. 리턴 형식에 맞게 데이터를 반환함
    1. 문자열을 반환하는 경우, StringConverter (StringHttpMessageConverter)에게 데이터가 전달되어 문자열을 반환함
    2. 객체를 반환하는 경우, JsonConverter (MappingJackson2HttpMessageConverter)에게 전달되어, 데이터가 JSON 형식으로 변환된 후 반환됨
  8. Spring Container는 완성된 데이터를 클라이언트에게 전달

 

*이 글은 인프런 - 김영한 님의 '스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술' 강의를 보고 정리한 내용입니다.

댓글