2단계. Serving Web Content with Spring MVC

Back·2026년 7월 22일

Spring Boot를 처음 공부하면 가장 먼저 만드는 프로젝트가 바로 "Hello, World!" 웹 애플리케이션이다.

처음에는 단순히 코드를 따라 작성하기만 했지만, 프로젝트를 진행하면서 자연스럽게 이런 궁금증이 생겼다.

  • HTTP 요청은 어떻게 처리되는 걸까?
  • Controller는 어떤 역할을 하는 걸까?
  • HTML 파일은 어떻게 화면에 출력되는 걸까?
  • @SpringBootApplication, @Controller, @GetMapping 같은 어노테이션은 왜 사용하는 걸까?

이번 글에서는 Spring 공식 가이드인 Serving Web Content를 직접 실습하면서 배운 내용을 정리해보려고 한다.


프로젝트 목표

이번 프로젝트에서 만들 애플리케이션의 동작은 매우 간단하다.

사용자가

http://localhost:8080/greeting

으로 접속하면

Hello, World!

가 출력되는 HTML 페이지를 보여준다.

또한 URL에 Query Parameter를 추가하면

http://localhost:8080/greeting?name=Spring

결과가

Hello, Spring!

으로 변경된다.

즉,

사용자의 요청에 따라 화면의 내용이 달라지는 가장 기본적인 Spring MVC 프로젝트를 만드는 것이 목표이다.


Spring Initializr로 프로젝트 생성하기

Spring Boot 프로젝트는 보통 Spring Initializr를 이용하여 생성한다.

https://start.spring.io

프로젝트 생성 시 다음과 같이 설정하였다.

항목설정
ProjectGradle
LanguageJava
Spring Boot최신 버전
Java17 이상

그리고 다음과 같은 의존성을 추가하였다.

  • Spring Web
  • Thymeleaf
  • Spring Boot DevTools

추가한 의존성은 왜 필요할까?

Spring Web

웹 애플리케이션을 개발하기 위한 핵심 라이브러리이다.

추가하면

  • Controller
  • REST API
  • DispatcherServlet
  • Spring MVC

등을 사용할 수 있다.

즉,

HTTP 요청을 처리하기 위해 반드시 필요한 라이브러리이다.


Thymeleaf

HTML을 동적으로 만들어주는 템플릿 엔진이다.

예를 들어

<p>Hello, World!</p>

처럼 항상 같은 내용을 출력하는 것이 아니라

<p>Hello, Spring!</p>

처럼 Java에서 전달한 데이터를 HTML 안에 출력할 수 있다.

이번 프로젝트에서는

Hello, ${name}

을 출력하기 위해 사용한다.


Spring Boot DevTools

개발할 때 매우 유용한 라이브러리이다.

보통

코드 수정

↓

프로그램 종료

↓

다시 실행

↓

브라우저 새로고침

과정을 반복해야 한다.

하지만 DevTools를 사용하면

  • 자동 재시작
  • Live Reload
  • 캐시 비활성화

등을 제공하여 개발 속도를 크게 높여준다.


프로젝트 구조

프로젝트를 생성하면 다음과 같은 구조가 만들어진다.

src
 ├── main
 │    ├── java
 │    │     └── com.example.servingwebcontent
 │    │             ├── ServingWebContentApplication
 │    │             └── GreetingController
 │    │
 │    └── resources
 │          ├── static
 │          └── templates

각 폴더의 역할은 다음과 같다.

폴더역할
javaJava 코드 작성
templatesThymeleaf HTML 파일
staticCSS, JS, 이미지 등 정적 파일

애플리케이션의 시작점

프로젝트를 생성하면 가장 먼저 보이는 클래스가 있다.

@SpringBootApplication
public class ServingWebContentApplication {

    public static void main(String[] args) {
        SpringApplication.run(
            ServingWebContentApplication.class,
            args
        );
    }

}

이 클래스는 Spring Boot 애플리케이션의 시작점이다.

프로그램을 실행하면

SpringApplication.run(...)

이 호출되면서

Spring Boot가 실행된다.


@SpringBootApplication이란?

Spring Boot에서 가장 중요한 어노테이션이다.

이 어노테이션 하나만 작성하면

  • Spring Container 생성
  • Bean 등록
  • 자동 설정(Auto Configuration)
  • Component Scan

등이 모두 자동으로 수행된다.

사실

@SpringBootApplication

아래 세 개의 어노테이션을 합쳐놓은 것이다.

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

@SpringBootConfiguration

Spring Boot의 설정 클래스라는 의미이다.

애플리케이션의 설정 정보를 관리하는 역할을 한다.


@EnableAutoConfiguration

Spring Boot가 라이브러리를 확인하여 필요한 설정을 자동으로 등록한다.

예를 들어

Spring Web

의존성을 추가하면

자동으로

  • Tomcat
  • DispatcherServlet
  • Spring MVC

등을 등록한다.

예전 Spring Framework에서는 XML 설정을 직접 작성해야 했지만

Spring Boot에서는 대부분 자동으로 처리된다.


@ComponentScan

Spring이 현재 패키지부터 아래의 모든 패키지를 검색한다.

예를 들어

com.example

 ├── controller
 ├── service
 └── repository

가 있다면

Spring은

  • Controller
  • Service
  • Repository

를 자동으로 찾아 Bean으로 등록한다.


Bean이란?

Spring에서는 객체를 직접 생성하지 않는다.

대신

Spring Container가 객체를 생성하고 관리한다.

이렇게 Spring이 관리하는 객체를

Bean이라고 한다.

예를 들어

@Service
public class MemberService {

}

가 있다면

Spring이 자동으로

new MemberService()

를 생성하여 관리한다.

개발자는 직접 객체를 생성할 필요가 없다.


@SpringBootApplication이 없다면?

만약

public class ServingWebContentApplication {

}

처럼 작성한다면

Spring Boot는

  • Bean 등록
  • Component Scan
  • 자동 설정

을 수행하지 않는다.

즉,

Controller도 찾지 못하고

웹 서버도 정상적으로 실행되지 않는다.

그래서 대부분의 Spring Boot 프로젝트에서는

반드시

@SpringBootApplication

을 사용한다.


여기까지 정리

이번에는

Spring Boot 프로젝트를 생성하고

애플리케이션이 어떻게 시작되는지 알아보았다.

다음으로는

사용자의 요청을 처리하는 Controller

@Controller

@GetMapping

@RequestParam

그리고

HTML 화면을 출력하는 Thymeleaf에 대해 자세히 알아보겠다.


Controller 만들기

Spring MVC에서 사용자의 HTTP 요청을 가장 먼저 처리하는 곳이 바로 Controller이다.

예를 들어 사용자가

GET /greeting

으로 요청을 보내면

Controller가 이 요청을 받아 적절한 처리를 수행한 뒤 결과를 사용자에게 전달한다.

이번 프로젝트에서는 다음과 같은 Controller를 작성하였다.

package com.example.servingwebcontent;

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 GreetingController {

    @GetMapping("/greeting")
    public String greeting(
            @RequestParam(name = "name",
                    required = false,
                    defaultValue = "World") String name,
            Model model) {

        model.addAttribute("name", name);

        return "greeting";
    }

}

처음 보면 코드가 매우 짧아 보이지만, Spring MVC의 핵심 개념이 모두 들어 있다.

하나씩 살펴보자.


@Controller

클래스 위에 붙어있는

@Controller

"이 클래스는 HTTP 요청을 처리하는 Controller입니다."

라는 의미를 가진다.

Spring Boot가 실행되면

Component Scan이

@Controller

가 붙은 클래스를 찾아 자동으로 Bean으로 등록한다.

그러면 사용자가 URL을 요청했을 때

Spring이 해당 Controller를 호출할 수 있게 된다.


@Controller가 없다면?

만약

public class GreetingController {

}

처럼 작성하면

Spring은 이 클래스가 Controller인지 알지 못한다.

따라서

GET /greeting

요청이 와도

메서드를 실행하지 않는다.

결과적으로

404 Not Found

가 발생한다.


@GetMapping

다음으로 눈에 띄는 부분은

@GetMapping("/greeting")

이다.

이 어노테이션은

"GET 요청이 /greeting으로 들어오면 이 메서드를 실행하세요."

라는 의미를 가진다.

즉,

사용자가

GET /greeting

요청을 보내면

Spring은

greeting()

메서드를 실행한다.


왜 GET을 사용할까?

HTTP에는 여러 종류의 요청 방식(Method)이 있다.

Method역할
GET데이터 조회
POST데이터 생성
PUT데이터 전체 수정
PATCH데이터 일부 수정
DELETE데이터 삭제

이번 프로젝트는

데이터를 조회하여 화면을 보여주는 것이 목적이므로

GET 요청을 사용한다.


@RequestParam

이번 프로젝트에서 가장 중요한 부분 중 하나이다.

@RequestParam(
    name="name",
    required=false,
    defaultValue="World"
)
String name

사용자가

/greeting?name=Spring

으로 요청하면

Spring

이라는 값을

Java 변수

String name

에 자동으로 저장해 준다.

즉,

URL

/greeting?name=Spring

        │
        ▼

@RequestParam

        │
        ▼

String name = "Spring"

이 되는 것이다.


required = false

기본적으로

@RequestParam

필수값이다.

즉,

/greeting

처럼

name이 없다면

400 Bad Request

오류가 발생한다.

하지만

required=false

를 사용하면

name이 없어도 오류가 발생하지 않는다.


defaultValue = "World"

이번 프로젝트에서는

defaultValue="World"

를 사용하였다.

즉,

사용자가

/greeting

으로 접속하면

Hello, World!

가 출력된다.

반대로

/greeting?name=Spring

이라면

Hello, Spring!

이 출력된다.

정리하면

요청결과
/greetingHello, World!
/greeting?name=SpringHello, Spring!
/greeting?name=KimHello, Kim!

Model이란?

메서드 안에는

Model model

이라는 객체가 있다.

public String greeting(
        ...
        Model model
)

Model은

Java에서 HTML로 데이터를 전달하는 객체이다.

쉽게 말하면

"화면에서 사용할 데이터를 저장하는 상자"

라고 생각하면 된다.


데이터를 저장하기

다음 코드를 보자.

model.addAttribute("name", name);

여기서

name

이라는 이름으로

사용자가 입력한 값을 저장한다.

예를 들어

사용자가

/greeting?name=Spring

으로 접속했다면

Model 안에는

name = Spring

이 저장된다.


return "greeting"의 의미

마지막 줄에는

return "greeting";

이 있다.

처음 보면

문자열을 반환하는 것처럼 보이지만

실제로는

templates/greeting.html

파일을 찾아 실행하라는 의미이다.

즉,

return "greeting";

↓

templates/greeting.html

을 실행한다.


Thymeleaf란?

Spring Boot에서는

HTML 안에서도 Java 데이터를 사용할 수 있다.

이를 가능하게 해주는 것이

Thymeleaf이다.

이번 프로젝트의 HTML은 다음과 같다.

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">

<head>
    <title>Getting Started</title>
</head>

<body>

<p th:text="|Hello, ${name}!|"></p>

</body>

</html>

th:text란?

가장 중요한 부분은

th:text="|Hello, ${name}!|"

이다.

여기서

${name}

Controller에서

model.addAttribute("name", name);

으로 저장한 데이터를 의미한다.

즉,

Controller에서

name = Spring

을 저장했다면

HTML에서는

Hello, Spring!

으로 출력된다.


전체 데이터 흐름

이번 프로젝트의 데이터 흐름은 다음과 같다.

사용자

GET /greeting?name=Spring

        │
        ▼

GreetingController

        │

@RequestParam

        │

String name = "Spring"

        │

Model.addAttribute()

        │

Model

        │

Thymeleaf

        │

greeting.html

        │

Hello, Spring!

즉,

Controller에서 데이터를 만들고

Model에 저장한 뒤

Thymeleaf가 HTML 안에 출력하는 구조이다.


여기까지 정리

이번에는

  • Controller의 역할
  • @Controller
  • @GetMapping
  • @RequestParam
  • Model
  • Thymeleaf

가 어떻게 연결되는지 살펴보았다.

다음으로는

  • 애플리케이션 실행 과정
  • index.html(정적 리소스)
  • Spring Boot의 전체 요청 처리 과정
  • 이번 프로젝트 전체 흐름

을 정리해보겠다.


애플리케이션 실행하기

이제 모든 코드 작성을 마쳤다면 애플리케이션을 실행해 보자.

Gradle을 사용하는 경우

./gradlew bootRun

또는 IDE에서

ServingWebContentApplication

클래스의 main() 메서드를 실행해도 된다.

실행이 완료되면 내장 Tomcat 서버가 함께 실행되며 기본적으로

http://localhost:8080

에서 웹 애플리케이션을 확인할 수 있다.


localhost란?

Spring Boot를 실행하면 자주 보게 되는 주소가 있다.

http://localhost:8080

여기서 각각의 의미를 살펴보자.

  • http : 웹 통신에 사용하는 프로토콜
  • localhost : 현재 내 컴퓨터를 의미
  • 8080 : Spring Boot가 사용하는 기본 포트 번호

즉,

http://localhost:8080

"내 컴퓨터에서 실행 중인 Spring Boot 서버"

를 의미한다.


정적 리소스(Static Resources)

이번 프로젝트에서는

src/main/resources/static

폴더를 사용하였다.

이 폴더 안에 있는 파일들은 별도의 Controller 없이도 브라우저에서 접근할 수 있다.

예를 들어

src/main/resources/static/index.html

파일을 생성하면

<!DOCTYPE html>
<html>

<head>
    <title>Getting Started</title>
</head>

<body>

<p>
    Get your greeting
    <a href="/greeting">here</a>
</p>

</body>

</html>

브라우저에서

http://localhost:8080

로 접속하면

자동으로

index.html

이 실행된다.


왜 Controller가 필요 없을까?

Spring Boot는

resources/static

폴더를 특별하게 관리한다.

Spring Boot가 실행되면

/static

안에 있는 파일을

정적 리소스(Static Resource)로 인식한다.

그래서

index.html

파일이 존재하면

자동으로 첫 화면(Welcome Page)이 된다.

즉,

localhost:8080

↓

index.html

이 자동으로 연결된다.


프로젝트 전체 동작 과정

이제 사용자가 브라우저에서

http://localhost:8080/greeting?name=Spring

으로 접속한다고 가정해보자.

Spring Boot 내부에서는 다음과 같은 과정이 순서대로 일어난다.

사용자

GET /greeting?name=Spring
        │
        ▼
Tomcat
        │
        ▼
DispatcherServlet
        │
        ▼
GreetingController
        │
        ▼
@RequestParam
        │
        ▼
String name = "Spring"
        │
        ▼
Model.addAttribute()
        │
        ▼
Thymeleaf
        │
        ▼
greeting.html
        │
        ▼
HTML 생성
        │
        ▼
브라우저

DispatcherServlet이란?

Spring MVC에서 가장 중요한 객체 중 하나가 바로

DispatcherServlet

이다.

사용자의 모든 요청은 가장 먼저 DispatcherServlet으로 들어온다.

DispatcherServlet은

  • 어떤 Controller를 실행해야 하는지 찾고
  • Controller를 호출하고
  • View를 찾아 실행한 뒤
  • 결과를 사용자에게 반환한다.

즉,

Spring MVC의 중앙 관리자(Front Controller) 역할을 수행한다.


이번 프로젝트에서 DispatcherServlet의 역할

사용자가

GET /greeting

요청을 보내면

DispatcherServlet은

@GetMapping("/greeting")

이 붙어 있는 메서드를 찾아 실행한다.

Controller가

return "greeting";

을 반환하면

DispatcherServlet은

templates/greeting.html

을 찾아

Thymeleaf에게 화면 생성을 요청한다.

생성된 HTML을 다시 사용자에게 전달하면서 요청 처리가 끝난다.


프로젝트 전체 흐름 한눈에 보기

Spring Boot 실행

        │

@SpringBootApplication

        │

Component Scan

        │

GreetingController 등록

        │

사용자 요청

GET /greeting

        │

DispatcherServlet

        │

GreetingController

        │

@RequestParam

        │

Model

        │

Thymeleaf

        │

HTML 생성

        │

브라우저 출력

이 과정을 이해하면

Spring MVC가 어떻게 동작하는지 큰 흐름을 이해할 수 있다.


이번 프로젝트에서 배운 내용

이번 프로젝트를 통해 다음과 같은 내용을 학습하였다.

  • Spring Initializr를 이용한 프로젝트 생성
  • Spring Boot 프로젝트 구조
  • @SpringBootApplication
  • @Controller
  • @GetMapping
  • @RequestParam
  • Model
  • Thymeleaf
  • 정적 리소스(static)
  • DispatcherServlet
  • Spring MVC 요청 처리 과정

처음에는 단순히 예제를 따라 작성했지만, 각각이 어떤 역할을 하는지 이해하고 나니 Spring MVC의 동작 방식이 훨씬 명확하게 보였다.


느낀 점

이번 실습을 통해 가장 크게 느낀 점은 Spring Boot가 개발자가 해야 할 많은 설정을 자동으로 처리해 준다는 것이다.

과거 Spring Framework에서는 XML 설정 파일을 직접 작성하고, DispatcherServlet을 등록하는 등 많은 설정이 필요했지만, Spring Boot에서는 @SpringBootApplication 하나만으로 대부분의 설정이 자동으로 이루어진다.

또한 @Controller, @GetMapping, @RequestParam과 같은 어노테이션을 이용하면 복잡한 HTTP 요청 처리도 매우 간결하게 작성할 수 있다는 점이 인상적이었다.

특히 이번 프로젝트를 통해 단순히 코드를 따라 작성하는 것이 아니라,

  • 사용자의 요청이 어떻게 서버에 전달되는지
  • Controller는 어떤 역할을 하는지
  • Model은 왜 필요한지
  • Thymeleaf가 HTML을 어떻게 생성하는지

를 전체 흐름으로 이해할 수 있었다.

앞으로는 REST API와 데이터베이스를 학습하면서 이번에 배운 Spring MVC의 구조를 기반으로 더 다양한 웹 애플리케이션을 만들어 보고 싶다.


마무리

Spring MVC는 처음에는 다소 어렵게 느껴질 수 있지만, 전체 요청 처리 과정을 한 번 이해하고 나면 생각보다 단순한 구조를 가지고 있다.

이번 프로젝트를 한 문장으로 정리하면 다음과 같다.

사용자의 요청을 Controller가 처리하고, 필요한 데이터를 Model에 저장한 뒤, Thymeleaf가 HTML을 생성하여 사용자에게 전달한다.

Spring Boot는 이러한 과정을 대부분 자동으로 처리해 주기 때문에 개발자는 비즈니스 로직에 집중할 수 있다.

이번 글이 Spring Boot를 처음 시작하는 사람들에게 전체 흐름을 이해하는 데 도움이 되었으면 좋겠다.

0개의 댓글