본문 바로가기

Java/Spring

Spring.io 따라하기: RESTful 웹 서비스 구현하기

더보기

REST(Representational State Transfer)는 자원을 이름으로 구분하여 해당 자원의 상태를 컴퓨팅 시스템 간에 주고받는 모든 과정을 의미합니다.

이 가이드에서는 Spring을 사용해 Hello, World! RESTful 웹 서비스를 구현합니다.

http://localhost:8080/greeting에서 HTTP GET 요청을 수신하고, 다음과 같이 JSON으로 응답합니다.

{"id":1,"content":"Hello, World!"}

URL은 Optionalname 쿼리 파라미터가 추가되어 http://localhost:8080/greeting?name=User 처럼 호출됩니다.

name 쿼리 파라미터가 Null인 경우 default로 World!를 응답합니다. Not Null인 경우 name 쿼리 파라미터의 값을 사용하여 응답합니다.

{"id":1,"content":"Hello, User!"}

테스트 환경 및 주요 아젠다

RESTful 웹 서비스를 구현하고 쿼리 파라미터에 따라서 JSON 서문을 응답하는 Spring 프로젝트입니다.

더보기

이 프로젝트의 개발 환경

  • 개발 언어 및 환경
    • OpenJDK 17.0.9
    • Gradle 8.4
  • 그 외 환경
    • IntelliJ IDEA 2020.3 Ultimate Edition

Getting Started guides처럼 모든 단계를 처음부터 따라 진행하거나 기본 단계를 건너 뛸 수 있습니다.

단계를 건너뛰려면 소스 파일을 Download하거나 GitHub: spring-guides/gs-rest-service를 참고합니다.

Spring Initializr를 사용하여 시작하기

pre-initialized project에서 ZIP 파일을 다운로드합니다.

또는 Spring Initializr를 처음부터 설정하려면

  • Project: Gradle 또는 Maven 중 하나를 선택합니다.
  • Language: Java를 선택합니다.
  • Dependencies: Spring Web을 선택합니다.

리소스 표현 클래스(Resource Representation Class) 생성

서비스는 /greeting 요청에 대해서 HTTP GET을 수신합니다. 추가적으로는 Optionalname 파라미터를 쿼리 파라미터에서 읽을 수 있습니다.

이 요청은 200 OK와 함께 다음과 같은 JSON 본문으로 응답됩니다.

{
    "id": 1,
    "content": "Hello, World!"
}
코드 비고
Line 2 id id 필드는 요청에 대한 유니크한 값입니다.
Line 3 content content 필드는 요청에 대한 응답 본문입니다.

다음 경로 src/main/java/com/example/restservice 패키지에서 Greeting.java 클래스를 생성합니다.

이 클래스는 /greeting 요청에 대한 리소스를 표현합니다.

package com.example.restservice;

public record Greeting(long id, String content) { }
더보기

이 클래스는 일반적인 자바의 클래스 형식을 따르지 않습니다.

package com.example.restservice;

public class Greeting
{
	// fill up Greeting class's field, method
}

이 애플리케이션은 Jackson JSON을 사용해 Greeting 객체를 자동으로 JSON으로 변환합니다.

리소스 컨트롤러(Resource Controller) 생성 

RESTful 웹 서비스를 구현하는 Spring은 컨트롤러(Controller)에서 HTTP 요청을 처리합니다.

다음 경로 src/main/java/com/example/restservice 패키지에서 GreetingController.java 클래스를 생성합니다.

package com.example.restservice;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.atomic.AtomicLong;

@RestController
public class GreetingController
{
	private static final String template = "Hello, %s!";
	private final AtomicLong counter = new AtomicLong();
	
	@GetMapping("/greeting")
	public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name)
	{
		return new Greeting(counter.incrementAndGet(), String.format(template, name));
	}
}
코드 비고
Line 9 @RestController RESTful 웹 요청을 처리하는 컨트롤러입니다.
모든 메소드는 뷰(View) 대신 도메인(Domain) 객체를 리턴합니다.
다음 어노테이션 @Controller @ResponseBody를 모두 포함합니다.
Line 15 @GetMapping("/greeting") /greeting HTTP GET 요청과 매핑되는 메소드입니다.
Line 16 @RequestParam 어노테이션의 필드 value는 쿼리 파라미터의 이름입니다.
어노테이션의 필드 defaultValue는 쿼리 파라미터의 기본 값입니다.

RESTful 웹 서비스의 컨트롤로와 기존 MVC 컨트롤러와의 차이점은 HTTP 응답에 대한 본문을 생성하는 방식입니다.

Greeting 객체를 HTML로 렌더링하기 위한 뷰(View)에 의존하지 않고, Greeting 객체의 필드를 채우고 리턴하는 것으로 대신합니다.

Greeting 객체는 HTTP 응답 과정에서 JSON으로 작성됩니다.

더보기

Greeting 객체는 JSON으로 파싱되어야 하며, Spring에서는 JSON 파싱을 위한 메뉴얼적인 코드를 작성할 필요는 없습니다.

MappingJackson2HttpMessageConvert가 classpath에 위치한 jackson 2에 의거해 객체를 자동으로 파싱합니다.

@SpringBootApplication

다음 경로 src/main/java/com/example/restservice 패키지에서 RestServiceApplication.java 클래스를 살펴봅니다.

이 클래스는 Spring InitializrSpring Web 의존성을 포함할 때 자동 생성됩니다. 

package com.example.restservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RestServiceApplication 
{
	public static void main(String[] args) 
	{
		SpringApplication.run(RestServiceApplication.class, args);
	}
}
코드 비고
Line 6 @SpringBootApplication 다음 어노테이션 @Configuration @EnableAutoConfiguration @ComponentScan을 모두 포함합니다.
@Configuration 해당 클래스를 Application context를 정의하는 Bean으로 지정합니다.
@EnableAutoConfiguration classpath의 Bean, 기타 Bean 그리고 환경 변수를 Spring Boot에 추가합니다.
예를 들어 classpath에 spring-webmvc가 있으면 웹 애플리케이션으로 지정하고
DispatchServlet과 같은 주요 동작을 활성화합니다.
@ComponentScan 동일한 패키지 com/example에서 Component Configurations Services를 찾고 이를 통해 컨트롤러를 찾습니다.
Line 11 SpringApplication.run() Spring Boot에서 애플리케이션을 실행합니다.
더보기

이 애플리케이션은 XMLweb.xml 파일을 포함하지 않습니다.

별도의 파이프라인 또는 인프라 구성을 요구하지 않으며 100% 자바로 동작합니다.

JAR 빌드 및 애플리케이션 실행

Gradle 또는 Maven 명령줄을 사용해 애플리케이션을 실행합니다(이 프로젝트에서는 Gradle 사용).

macOS 또는 Linux 기반의 Gradle 환경에서는 ./gradlew bootRun을 실행합니다.

 ./gradlew bootRun

> Task :bootRun

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.1.5)

2023-11-20T15:55:14.383+09:00  INFO 83040 --- [           main] c.e.restservice.RestServiceApplication   : Starting RestServiceApplication using Java 17.0.9 with PID 83040 (/Users/guenbongpark/Downloads/rest-service/build/classes/java/main started by guenbongpark in /Users/guenbongpark/Downloads/rest-service)
2023-11-20T15:55:14.384+09:00  INFO 83040 --- [           main] c.e.restservice.RestServiceApplication   : No active profile set, falling back to 1 default profile: "default"
2023-11-20T15:55:14.819+09:00  INFO 83040 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2023-11-20T15:55:14.825+09:00  INFO 83040 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-11-20T15:55:14.825+09:00  INFO 83040 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.15]
2023-11-20T15:55:14.881+09:00  INFO 83040 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-11-20T15:55:14.881+09:00  INFO 83040 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 471 ms
2023-11-20T15:55:15.051+09:00  INFO 83040 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2023-11-20T15:55:15.056+09:00  INFO 83040 --- [           main] c.e.restservice.RestServiceApplication   : Started RestServiceApplication in 0.868 seconds (process running for 1.065)
<==========---> 80% EXECUTING [1m 22s]
> :bootRun

또는 동일한 환경에서 ./gradlew build를 사용해 JAR 파일로 빌드합니다. 빌드가 완료되면 java -jar 명령문으로 JAR 파일을 실행합니다.

$ ./gradlew build
$ java -jar build/libs/gs-rest-service-0.1.0.jar

테스트

애플리케이션을 실행하고 웹 브라우저에서 http://localhost:8080/greeting URL로 요청합니다.

{"id":1,"content":"Hello, World!"}

이번에는 name 쿼리 파라미터를 추가하여 http://localhost:8080/greeting?name=User URL로 요청합니다.

{"id":2,"content":"Hello, User!"}

GreetingController@RequestParam이 예상대로 동작하는 것을 알 수 있습니다.

응답 결과로 확인되는 id 필드 역시 처음 요청 1에서 2로 값이 증가하였습니다.

서로 다른 요청이 동일한 GreetingController 객체에 의해서 처리되고 있으며 counter 필드가 정상대로 증분하고 있음을 알 수 있습니다.