Spring Boot를 처음 공부하면 가장 먼저 만드는 프로젝트가 바로 "Hello, World!" 웹 애플리케이션이다.
처음에는 단순히 코드를 따라 작성하기만 했지만, 프로젝트를 진행하면서 자연스럽게 이런 궁금증이 생겼다.
@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 Boot 프로젝트는 보통 Spring Initializr를 이용하여 생성한다.
프로젝트 생성 시 다음과 같이 설정하였다.
| 항목 | 설정 |
|---|---|
| Project | Gradle |
| Language | Java |
| Spring Boot | 최신 버전 |
| Java | 17 이상 |
그리고 다음과 같은 의존성을 추가하였다.
웹 애플리케이션을 개발하기 위한 핵심 라이브러리이다.
추가하면
등을 사용할 수 있다.
즉,
HTTP 요청을 처리하기 위해 반드시 필요한 라이브러리이다.
HTML을 동적으로 만들어주는 템플릿 엔진이다.
예를 들어
<p>Hello, World!</p>
처럼 항상 같은 내용을 출력하는 것이 아니라
<p>Hello, Spring!</p>
처럼 Java에서 전달한 데이터를 HTML 안에 출력할 수 있다.
이번 프로젝트에서는
Hello, ${name}
을 출력하기 위해 사용한다.
개발할 때 매우 유용한 라이브러리이다.
보통
코드 수정
↓
프로그램 종료
↓
다시 실행
↓
브라우저 새로고침
과정을 반복해야 한다.
하지만 DevTools를 사용하면
등을 제공하여 개발 속도를 크게 높여준다.
프로젝트를 생성하면 다음과 같은 구조가 만들어진다.
src
├── main
│ ├── java
│ │ └── com.example.servingwebcontent
│ │ ├── ServingWebContentApplication
│ │ └── GreetingController
│ │
│ └── resources
│ ├── static
│ └── templates
각 폴더의 역할은 다음과 같다.
| 폴더 | 역할 |
|---|---|
| java | Java 코드 작성 |
| templates | Thymeleaf HTML 파일 |
| static | CSS, JS, 이미지 등 정적 파일 |
프로젝트를 생성하면 가장 먼저 보이는 클래스가 있다.
@SpringBootApplication
public class ServingWebContentApplication {
public static void main(String[] args) {
SpringApplication.run(
ServingWebContentApplication.class,
args
);
}
}
이 클래스는 Spring Boot 애플리케이션의 시작점이다.
프로그램을 실행하면
SpringApplication.run(...)
이 호출되면서
Spring Boot가 실행된다.
Spring Boot에서 가장 중요한 어노테이션이다.
이 어노테이션 하나만 작성하면
등이 모두 자동으로 수행된다.
사실
@SpringBootApplication
은
아래 세 개의 어노테이션을 합쳐놓은 것이다.
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
Spring Boot의 설정 클래스라는 의미이다.
애플리케이션의 설정 정보를 관리하는 역할을 한다.
Spring Boot가 라이브러리를 확인하여 필요한 설정을 자동으로 등록한다.
예를 들어
Spring Web
의존성을 추가하면
자동으로
등을 등록한다.
예전 Spring Framework에서는 XML 설정을 직접 작성해야 했지만
Spring Boot에서는 대부분 자동으로 처리된다.
Spring이 현재 패키지부터 아래의 모든 패키지를 검색한다.
예를 들어
com.example
├── controller
├── service
└── repository
가 있다면
Spring은
를 자동으로 찾아 Bean으로 등록한다.
Spring에서는 객체를 직접 생성하지 않는다.
대신
Spring Container가 객체를 생성하고 관리한다.
이렇게 Spring이 관리하는 객체를
Bean이라고 한다.
예를 들어
@Service
public class MemberService {
}
가 있다면
Spring이 자동으로
new MemberService()
를 생성하여 관리한다.
개발자는 직접 객체를 생성할 필요가 없다.
만약
public class ServingWebContentApplication {
}
처럼 작성한다면
Spring Boot는
을 수행하지 않는다.
즉,
Controller도 찾지 못하고
웹 서버도 정상적으로 실행되지 않는다.
그래서 대부분의 Spring Boot 프로젝트에서는
반드시
@SpringBootApplication
을 사용한다.
이번에는
Spring Boot 프로젝트를 생성하고
애플리케이션이 어떻게 시작되는지 알아보았다.
다음으로는
사용자의 요청을 처리하는 Controller와
@Controller
@GetMapping
@RequestParam
그리고
HTML 화면을 출력하는 Thymeleaf에 대해 자세히 알아보겠다.
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
는
"이 클래스는 HTTP 요청을 처리하는 Controller입니다."
라는 의미를 가진다.
Spring Boot가 실행되면
Component Scan이
@Controller
가 붙은 클래스를 찾아 자동으로 Bean으로 등록한다.
그러면 사용자가 URL을 요청했을 때
Spring이 해당 Controller를 호출할 수 있게 된다.
만약
public class GreetingController {
}
처럼 작성하면
Spring은 이 클래스가 Controller인지 알지 못한다.
따라서
GET /greeting
요청이 와도
메서드를 실행하지 않는다.
결과적으로
404 Not Found
가 발생한다.
다음으로 눈에 띄는 부분은
@GetMapping("/greeting")
이다.
이 어노테이션은
"GET 요청이
/greeting으로 들어오면 이 메서드를 실행하세요."
라는 의미를 가진다.
즉,
사용자가
GET /greeting
요청을 보내면
Spring은
greeting()
메서드를 실행한다.
HTTP에는 여러 종류의 요청 방식(Method)이 있다.
| Method | 역할 |
|---|---|
| GET | 데이터 조회 |
| POST | 데이터 생성 |
| PUT | 데이터 전체 수정 |
| PATCH | 데이터 일부 수정 |
| DELETE | 데이터 삭제 |
이번 프로젝트는
데이터를 조회하여 화면을 보여주는 것이 목적이므로
GET 요청을 사용한다.
이번 프로젝트에서 가장 중요한 부분 중 하나이다.
@RequestParam(
name="name",
required=false,
defaultValue="World"
)
String name
사용자가
/greeting?name=Spring
으로 요청하면
Spring
이라는 값을
Java 변수
String name
에 자동으로 저장해 준다.
즉,
URL
/greeting?name=Spring
│
▼
@RequestParam
│
▼
String name = "Spring"
이 되는 것이다.
기본적으로
@RequestParam
은
필수값이다.
즉,
/greeting
처럼
name이 없다면
400 Bad Request
오류가 발생한다.
하지만
required=false
를 사용하면
name이 없어도 오류가 발생하지 않는다.
이번 프로젝트에서는
defaultValue="World"
를 사용하였다.
즉,
사용자가
/greeting
으로 접속하면
Hello, World!
가 출력된다.
반대로
/greeting?name=Spring
이라면
Hello, Spring!
이 출력된다.
정리하면
| 요청 | 결과 |
|---|---|
/greeting | Hello, World! |
/greeting?name=Spring | Hello, Spring! |
/greeting?name=Kim | Hello, Kim! |
메서드 안에는
Model model
이라는 객체가 있다.
public String greeting(
...
Model model
)
Model은
Java에서 HTML로 데이터를 전달하는 객체이다.
쉽게 말하면
"화면에서 사용할 데이터를 저장하는 상자"
라고 생각하면 된다.
다음 코드를 보자.
model.addAttribute("name", name);
여기서
name
이라는 이름으로
사용자가 입력한 값을 저장한다.
예를 들어
사용자가
/greeting?name=Spring
으로 접속했다면
Model 안에는
name = Spring
이 저장된다.
마지막 줄에는
return "greeting";
이 있다.
처음 보면
문자열을 반환하는 것처럼 보이지만
실제로는
templates/greeting.html
파일을 찾아 실행하라는 의미이다.
즉,
return "greeting";
↓
templates/greeting.html
을 실행한다.
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="|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 안에 출력하는 구조이다.
이번에는
가 어떻게 연결되는지 살펴보았다.
다음으로는
을 정리해보겠다.
이제 모든 코드 작성을 마쳤다면 애플리케이션을 실행해 보자.
Gradle을 사용하는 경우
./gradlew bootRun
또는 IDE에서
ServingWebContentApplication
클래스의 main() 메서드를 실행해도 된다.
실행이 완료되면 내장 Tomcat 서버가 함께 실행되며 기본적으로
http://localhost:8080
에서 웹 애플리케이션을 확인할 수 있다.
Spring Boot를 실행하면 자주 보게 되는 주소가 있다.
http://localhost:8080
여기서 각각의 의미를 살펴보자.
즉,
http://localhost:8080
은
"내 컴퓨터에서 실행 중인 Spring Boot 서버"
를 의미한다.
이번 프로젝트에서는
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
이 실행된다.
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 생성
│
▼
브라우저
Spring MVC에서 가장 중요한 객체 중 하나가 바로
DispatcherServlet
이다.
사용자의 모든 요청은 가장 먼저 DispatcherServlet으로 들어온다.
DispatcherServlet은
즉,
Spring MVC의 중앙 관리자(Front Controller) 역할을 수행한다.
사용자가
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가 어떻게 동작하는지 큰 흐름을 이해할 수 있다.
이번 프로젝트를 통해 다음과 같은 내용을 학습하였다.
@SpringBootApplication@Controller@GetMapping@RequestParamModelThymeleafstatic)처음에는 단순히 예제를 따라 작성했지만, 각각이 어떤 역할을 하는지 이해하고 나니 Spring MVC의 동작 방식이 훨씬 명확하게 보였다.
이번 실습을 통해 가장 크게 느낀 점은 Spring Boot가 개발자가 해야 할 많은 설정을 자동으로 처리해 준다는 것이다.
과거 Spring Framework에서는 XML 설정 파일을 직접 작성하고, DispatcherServlet을 등록하는 등 많은 설정이 필요했지만, Spring Boot에서는 @SpringBootApplication 하나만으로 대부분의 설정이 자동으로 이루어진다.
또한 @Controller, @GetMapping, @RequestParam과 같은 어노테이션을 이용하면 복잡한 HTTP 요청 처리도 매우 간결하게 작성할 수 있다는 점이 인상적이었다.
특히 이번 프로젝트를 통해 단순히 코드를 따라 작성하는 것이 아니라,
를 전체 흐름으로 이해할 수 있었다.
앞으로는 REST API와 데이터베이스를 학습하면서 이번에 배운 Spring MVC의 구조를 기반으로 더 다양한 웹 애플리케이션을 만들어 보고 싶다.
Spring MVC는 처음에는 다소 어렵게 느껴질 수 있지만, 전체 요청 처리 과정을 한 번 이해하고 나면 생각보다 단순한 구조를 가지고 있다.
이번 프로젝트를 한 문장으로 정리하면 다음과 같다.
사용자의 요청을 Controller가 처리하고, 필요한 데이터를 Model에 저장한 뒤, Thymeleaf가 HTML을 생성하여 사용자에게 전달한다.
Spring Boot는 이러한 과정을 대부분 자동으로 처리해 주기 때문에 개발자는 비즈니스 로직에 집중할 수 있다.
이번 글이 Spring Boot를 처음 시작하는 사람들에게 전체 흐름을 이해하는 데 도움이 되었으면 좋겠다.