https://spring.io/guides/gs/rest-service
Spring Boot를 처음 공부하다 보면 가장 먼저 접하게 되는 것이 바로 어노테이션(Annotation) 이다.
@SpringBootApplication
@RestController
@GetMapping("/hello")
@RequestParam(defaultValue = "World")
처음에는 예제를 따라 작성하기만 했지만, 프로젝트를 진행하다 보면 자연스럽게 이런 궁금증이 생긴다.
이번 글에서는 Spring Boot에서 가장 많이 사용하는 핵심 어노테이션인
@SpringBootApplication@RestController@GetMapping@RequestParam에 대해 하나씩 자세히 정리해보려고 한다.
@SpringBootApplication은 Spring Boot 애플리케이션의 시작점(Start Point) 을 나타내는 어노테이션이다.
보통 main() 메서드가 있는 클래스에 붙으며, Spring Boot가 어디서부터 애플리케이션을 시작해야 하는지 알려준다.
@SpringBootApplication
public class RestServiceApplication {
public static void main(String[] args) {
SpringApplication.run(RestServiceApplication.class, args);
}
}
프로그램을 실행하면
SpringApplication.run(...)
이 호출되고, Spring Boot는 @SpringBootApplication이 붙어 있는 클래스를 기준으로 프로젝트를 초기화한다.
이 어노테이션 하나만 붙여도 Spring Boot는 여러 가지 작업을 자동으로 수행한다.
대표적으로
등이 모두 자동으로 이루어진다.
즉,
@SpringBootApplication
하나만 작성해도 Spring Boot 프로젝트가 실행될 수 있는 이유가 바로 여기에 있다.
사실 @SpringBootApplication은 하나의 기능만 수행하는 어노테이션이 아니다.
아래의 세 가지 어노테이션을 하나로 합쳐놓은 형태이다.
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
즉,
@SpringBootApplication
은 내부적으로 위 세 개를 모두 포함하고 있다.
이제 각각이 어떤 역할을 하는지 알아보자.
Spring Boot의 설정 클래스라는 의미이다.
Spring Framework에서는 보통
@Configuration
public class AppConfig {
}
처럼 사용한다.
Spring Boot에서는 이를 조금 확장한
@SpringBootConfiguration
을 사용한다.
즉,
"이 클래스는 Spring Boot의 설정 클래스입니다."
라고 알려주는 역할을 한다.
실제로는 대부분 @SpringBootApplication 안에 포함되어 있기 때문에 따로 작성하는 경우는 거의 없다.
Spring Boot의 가장 큰 장점이라고 할 수 있는 기능이다.
이 어노테이션은
"프로젝트에 추가된 라이브러리를 보고 필요한 설정을 자동으로 구성하라."
는 의미를 가진다.
예를 들어
spring-boot-starter-web
을 의존성으로 추가했다고 가정해보자.
그러면 Spring Boot는 자동으로
등을 모두 등록한다.
예전 Spring Framework에서는
dispatcher-servlet.xml
같은 설정 파일을 직접 작성해야 했지만,
Spring Boot에서는 대부분의 설정을 자동으로 처리해 준다.
이 기능 덕분에 개발자는 비즈니스 로직에만 집중할 수 있다.
Spring이 프로젝트를 실행할 때
현재 클래스가 위치한 패키지를 기준으로
아래의 모든 패키지를 검색한다.
예를 들어
com.example
├── RestServiceApplication
├── controller
│ HelloController
├── service
│ HelloService
└── repository
HelloRepository
라고 되어 있다면
Spring은 자동으로
를 찾아 Bean으로 등록한다.
이 과정을 Component Scan이라고 한다.
Spring에서는 객체를 직접 생성하는 것이 아니라
Spring Container가 객체를 생성하고 관리한다.
이렇게 Spring가 관리하는 객체를
Bean이라고 한다.
예를 들어
@Service
public class HelloService {
}
가 있다면
Spring이 자동으로
HelloService service = new HelloService();
를 생성하고 관리한다.
개발자는 직접 객체를 생성하지 않아도 된다.
만약
public class RestServiceApplication {
}
처럼 아무 어노테이션도 없다면
Spring Boot는
을 수행하지 않는다.
즉,
Controller도 찾지 못하고
Service도 등록하지 못하며
웹 서버도 정상적으로 실행되지 않는다.
이를 직접 구현하려면
@Configuration
@EnableAutoConfiguration
@ComponentScan
을 모두 작성해야 한다.
그래서 대부분의 Spring Boot 프로젝트에서는
반드시
@SpringBootApplication
을 사용한다.
✔ 코드가 매우 간결하다.
✔ Spring Boot의 자동 설정을 사용할 수 있다.
✔ Bean을 자동으로 등록한다.
✔ 프로젝트의 시작점을 명확하게 알 수 있다.
✔ 대부분의 설정을 자동으로 처리하므로 생산성이 높다.
| 항목 | 설명 |
|---|---|
| 역할 | Spring Boot 애플리케이션의 시작점 |
| 내부 구성 | @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan |
| 사용하지 않으면 | Bean 등록, 자동 설정, 컴포넌트 스캔이 수행되지 않는다. |
| 장점 | 코드가 간결하고 대부분의 설정을 자동으로 처리한다. |
지금까지는 Spring Boot 프로젝트가 어떻게 시작되는지 살펴보았다.
다음으로는 실제로 HTTP 요청을 처리하는 Controller를 만드는 핵심 어노테이션인 @RestController와 @GetMapping에 대해 알아보자.
@RestController는 해당 클래스를 REST API를 처리하는 Controller로 등록하는 어노테이션이다.
즉,
"이 클래스는 HTTP 요청을 받아 처리하고, 결과를 응답(Response)으로 반환하는 Controller입니다."
라는 의미를 가진다.
예를 들어
@RestController
public class HelloController {
}
라고 작성하면 Spring은 이 클래스를 Controller로 인식하여 HTTP 요청을 처리할 준비를 한다.
Spring MVC에서 Controller는
클라이언트의 요청(Request)을 받아 적절한 처리를 수행하고 응답(Response)을 반환하는 역할을 한다.
예를 들어 사용자가
GET /hello
요청을 보내면
Controller가 이를 받아 처리한다.
사용자
│
▼
Controller
│
▼
Service
│
▼
Database
즉, Controller는 사용자의 요청이 가장 먼저 도착하는 곳이라고 생각하면 된다.
사실 @RestController도 하나의 어노테이션이 아니라
아래 두 개를 합쳐놓은 것이다.
@Controller
@ResponseBody
즉,
Spring MVC에서 메서드가 값을 반환하면
기본적으로는
"반환된 문자열은 View 이름이다."
라고 생각한다.
하지만
@ResponseBody
를 붙이면
"반환값을 그대로 HTTP Response Body에 넣어라."
라는 의미가 된다.
예를 들어
@ResponseBody
public String hello() {
return "Hello";
}
라고 하면
브라우저에는
Hello
가 그대로 출력된다.
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello Spring Boot";
}
}
브라우저에서
http://localhost:8080/hello
로 접속하면
Hello Spring Boot
가 그대로 출력된다.
많은 사람들이 가장 헷갈려하는 부분이다.
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello";
}
}
Spring은
hello
를
View 이름
이라고 생각한다.
즉,
hello.html
또는
hello.jsp
파일을 찾으려고 한다.
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello";
}
}
이번에는
hello
를
HTTP 응답으로 그대로 반환한다.
브라우저 출력
hello
주로
같은 화면(View)을 반환할 때 사용한다.
예를 들어
@Controller
public class HomeController {
@GetMapping("/")
public String home() {
return "index";
}
}
라면
Spring은
index.html
을 찾아 화면을 보여준다.
REST API를 만들 때 사용한다.
예를 들어
등 대부분의 백엔드 API는
@RestController를 사용한다.
public class HelloController {
}
처럼 작성하면
Spring은
이 클래스를 Controller인지 알지 못한다.
따라서
GET /hello
요청이 와도
메서드가 실행되지 않는다.
결과적으로
404 Not Found
가 발생한다.
✔ REST API 개발에 최적화되어 있다.
✔ JSON 반환이 매우 쉽다.
✔ 문자열도 그대로 반환할 수 있다.
✔ @ResponseBody를 매번 작성하지 않아도 된다.
| 항목 | 설명 |
|---|---|
| 역할 | REST API를 처리하는 Controller 등록 |
| 내부 구성 | @Controller + @ResponseBody |
| 사용하지 않으면 | Controller로 등록되지 않아 요청을 처리하지 못한다. |
| 장점 | JSON 및 문자열 반환이 매우 편리하다. |
@GetMapping은
HTTP의 GET 요청을 특정 메서드와 연결(Mapping) 하는 어노테이션이다.
즉,
GET /hello
요청이 들어오면
어떤 메서드를 실행할지 Spring에게 알려주는 역할을 한다.
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello Spring";
}
}
사용자가
GET /hello
요청을 보내면
Spring은
hello()
메서드를 실행한다.
결과
Hello Spring
사실
@GetMapping("/hello")
은
아래 코드의 축약형이다.
@RequestMapping(
value="/hello",
method=RequestMethod.GET
)
즉,
기능은 동일하지만
가독성을 높이기 위해 만들어진 어노테이션이다.
HTTP에는 여러 종류의 요청 방식(Method)이 있다.
각각의 역할이 다르다.
| HTTP Method | 역할 |
|---|---|
| GET | 데이터 조회 |
| POST | 데이터 생성 |
| PUT | 데이터 전체 수정 |
| PATCH | 데이터 일부 수정 |
| DELETE | 데이터 삭제 |
예를 들어
회원 조회
GET /users/1
회원 생성
POST /users
회원 삭제
DELETE /users/1
처럼 사용하는 것이 REST API의 기본 규칙이다.
예전에는
@RequestMapping(
value="/hello",
method=RequestMethod.GET
)
처럼 작성했다.
Spring 4.3 이후부터는
@GetMapping("/hello")
을 사용하는 것이 권장된다.
왜냐하면
메서드는 존재하지만
Spring이
GET /hello
요청과 연결하지 못한다.
따라서
사용자가 요청을 보내도
메서드는 실행되지 않는다.
✔ 코드가 간결하다.
✔ 어떤 HTTP 요청인지 바로 알 수 있다.
✔ REST API를 작성하기 쉽다.
✔ @RequestMapping보다 읽기 쉽다.
| 항목 | 설명 |
|---|---|
| 역할 | GET 요청과 메서드를 연결 |
| 내부 구조 | @RequestMapping(method = GET)의 축약형 |
| 사용하지 않으면 | URL 요청과 연결되지 않는다. |
| 장점 | 가독성이 뛰어나고 REST API 작성이 쉬워진다. |
지금까지는 Controller를 등록하고(GET 요청을 연결하는 과정) 을 살펴보았다.
다음으로는 URL의 파라미터를 Java 변수로 받아오는 @RequestParam과 defaultValue 옵션에 대해 알아보자.
@RequestParam은 URL의 쿼리 파라미터(Query Parameter)를 메서드의 매개변수(Parameter)로 전달받는 어노테이션이다.
예를 들어 다음과 같은 요청이 있다고 가정해보자.
GET /hello?name=Spring
여기서
name=Spring
이 바로 쿼리 파라미터(Query Parameter) 이다.
Spring은 @RequestParam을 사용하여 이 값을 Java 변수로 자동 변환해 준다.
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(@RequestParam String name) {
return "Hello " + name;
}
}
브라우저에서
GET /hello?name=Spring
을 요청하면
Hello Spring
을 반환한다.
즉,
URL Parameter
│
▼
@RequestParam
│
▼
Java 변수
로 변환되는 것이다.
쿼리 파라미터는 여러 개도 받을 수 있다.
@GetMapping("/member")
public String member(
@RequestParam String name,
@RequestParam int age
) {
return name + " : " + age;
}
요청
GET /member?name=Kim&age=25
결과
Kim : 25
Spring이 자동으로
name → String
age → int
로 변환해 준다.
매개변수 이름이 달라도
value
속성을 사용하면 된다.
@GetMapping("/hello")
public String hello(
@RequestParam("name")
String username
) {
return username;
}
요청
GET /hello?name=Spring
결과
Spring
즉,
URL name
↓
Java username
으로 연결된다.
다음과 같이 작성하면
public String hello(String name) {
}
Spring은
이 값이
명확하게 알 수 없다.
따라서
URL Parameter를 받을 때는
@RequestParam
을 사용하는 것이 가장 명확하다.
@RequestParam에는
가장 많이 사용하는 옵션이 있다.
@RequestParam(defaultValue = "World")
의 의미는
"파라미터가 없으면 World를 사용해라."
이다.
@GetMapping("/hello")
public String hello(
@RequestParam(defaultValue = "World")
String name
) {
return "Hello " + name;
}
GET /hello
결과
Hello World
GET /hello?name=Spring
결과
Hello Spring
즉,
파라미터 존재
↓
사용자가 입력한 값 사용
----------------------------
파라미터 없음
↓
defaultValue 사용
기본적으로
@RequestParam String name
은
required = true
상태이다.
즉,
반드시 값이 있어야 한다.
예를 들어
@GetMapping("/hello")
public String hello(
@RequestParam String name
) {
return name;
}
에서
GET /hello
를 호출하면
Spring은
400 Bad Request
를 반환한다.
왜냐하면
필수 파라미터가 없기 때문이다.
많이 헷갈리는 부분이다.
@RequestParam(required = false)
String name
이라면
파라미터가 없을 경우
name == null
이 된다.
반면
@RequestParam(defaultValue = "World")
String name
이라면
name == "World"
가 된다.
즉,
| 설정 | 파라미터가 없을 때 |
|---|---|
@RequestParam | 400 Bad Request |
required = false | null |
defaultValue = "World" | World |
실무에서는
null 체크를 줄일 수 있기 때문에
defaultValue
를 사용하는 경우가 많다.
Spring에서는 데이터를 받아오는 방법이 여러 가지 있다.
각각 사용하는 위치가 다르다.
| 어노테이션 | 사용하는 데이터 |
|---|---|
@RequestParam | Query Parameter (?name=kim) |
@PathVariable | URL Path (/users/1) |
@RequestBody | JSON Body |
예를 들어
GET /member?id=1
@RequestParam Long id
GET /member/1
@PathVariable Long id
POST /member
{
"name": "Kim",
"age": 25
}
@RequestBody MemberDto dto
각각 데이터를 받아오는 위치가 다르므로 목적에 맞게 사용해야 한다.
지금까지 살펴본 어노테이션들이 실제로 어떻게 동작하는지 하나의 흐름으로 정리해보자.
사용자가
GET /hello?name=Spring
요청을 보낸다.
↓
Spring Boot가 실행되어 있다.
@SpringBootApplication
↓
Spring이 Controller를 찾는다.
@RestController
↓
URL을 확인한다.
@GetMapping("/hello")
↓
URL Parameter를 읽는다.
@RequestParam
↓
Java 변수로 변환한다.
String name = "Spring";
↓
메서드를 실행한다.
return "Hello " + name;
↓
브라우저로 응답한다.
Hello Spring
전체 흐름을 그림으로 표현하면 다음과 같다.
사용자 요청
GET /hello?name=Spring
│
▼
@SpringBootApplication
│
▼
@RestController
│
▼
@GetMapping("/hello")
│
▼
@RequestParam
│
▼
Java 변수 생성
│
▼
메서드 실행
│
▼
Response 반환
| 어노테이션 | 역할 | 사용하지 않으면 | 장점 |
|---|---|---|---|
@SpringBootApplication | Spring Boot 실행, 자동 설정, Bean 등록 | Spring Boot가 정상적으로 동작하지 않는다. | 설정이 매우 간단하다. |
@RestController | REST API Controller 등록 | Controller로 인식되지 않아 요청을 처리하지 못한다. | JSON 및 문자열 반환이 편리하다. |
@GetMapping | GET 요청과 메서드를 연결 | URL 요청과 메서드가 연결되지 않는다. | HTTP Method가 명확하다. |
@RequestParam | URL Query Parameter를 Java 변수로 변환 | URL 데이터를 명확하게 받을 수 없다. | URL Parameter를 쉽게 사용할 수 있다. |
defaultValue | 기본값 지정 | 값이 없으면 400 오류가 발생할 수 있다. | null 체크를 줄일 수 있다. |
Spring Boot의 어노테이션은 단순히 코드를 짧게 작성하기 위한 문법이 아니다.
각 어노테이션은 Spring에게 "이 클래스는 어떤 역할을 하는지", "이 메서드는 어떤 요청을 처리하는지", "이 값은 어디에서 가져오는지" 를 알려주는 중요한 역할을 한다.
특히 이번에 살펴본
@SpringBootApplication@RestController@GetMapping@RequestParam은 거의 모든 Spring Boot 프로젝트에서 사용되는 핵심 어노테이션이다.
단순히 사용법만 외우기보다
까지 함께 이해한다면 이후 Spring MVC와 REST API를 학습할 때 훨씬 수월하게 이해할 수 있을 것이다.