RESTful 웹 서비스 만들기

이 가이드는 Spring으로 "hello world" RESTful 웹 서비스를 만드는 과정을 안내한다.

무엇을 만들게 되는가

다음 HTTP GET 요청을 수락하는 서비스를 만들것이다:

http://localhost:8080/greeting

그리고 JSON 방식으로 greeting을 응답한다.

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

쿼리 문자열에 선택적 name 파라미터를 사용하여 greeting을 사용자 정의할 수 있다.

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

name 파라미터 값은 기본값 "World"를 대체하여 응답에 반영된다:

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

무엇이 필요한가

이 가이드를 완료하는 방법

대부분의 Spring Getting Started 가이드와 마찬가지로, 처음부터 시작하여 각 단계를 완료하거나 이미 익숙한 기본 설정 단계를 건너뛸 수 있다. 어느 쪽이든 코드가 작동한다.

처음부터 시작하려면 Gradle로 빌드하기로 이동하시오.

기본 사항을 건너뛰려면 다음을 수행하라:

끝나면 gs-rest-service/complete 코드와 결과를 비교할 수 있다.

Gradle로 빌드하기

먼저 기본 빌드 스크립트를 설정한다. Spring으로 app을 만들 때 원하는 빌드 시스템을 사용할 수 있지만 Gradle  Maven으로 작업하는 데 필요한 코드가 여기에 있다. 둘다 익숙하지 않은 경우 Gradle로 Java 프로젝트 만들기 또는 Maven으로 Java 프로젝트 만들기를 참조하시오.

디렉토리 구조 만들기

선택한 프로젝트 디렉토리에서 *nix 시스템의 mkdir -p src/main/java/hello를 사용하여 다음 하위 디렉토리 구조를 작성한다:

└── src └── main └── java └── hello

Gradle 빌드 파일 만들기

아래는 초기 Gradle 빌드 파일이다.

build.gradle

buildscript { repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.5.RELEASE") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'idea' apply plugin: 'org.springframework.boot' apply plugin: 'io.spring.dependency-management' bootJar { baseName = 'gs-rest-service' version = '0.1.0' } repositories { mavenCentral() } sourceCompatibility = 1.8 targetCompatibility = 1.8 dependencies { compile("org.springframework.boot:spring-boot-starter-web") testCompile('org.springframework.boot:spring-boot-starter-test') }

Spring Boot Gralde 플러그인은 다음과 같은 여러 편리한 기능을 제공한다:

  • 클래스 경로에 있는 모든 jar를 모으고 서비스를 전송하고 실행하는데 좀 더 편하게 만들어주는 실행 가능한 "über-jar"를 빌드한다.

  • public static void main() 메소드를 검색하여 실행 가능 클래스로 플래그를 지정한다.

  • Spring Boot 의존성과 일치하도록 버전 번호를 설정하는 빌트인 의존성 리졸버를 제공한다. 모든 버전을 재정의할 수 있지만 기본적으로 Boot에서 선택한 버전으로 설정된다.

Maven으로 빌드하기

먼저 기본 빌드 스크립트를 설정한다. Spring으로 app을 만들 때 원하는 빌드 시스템을 사용할 수 있지만 Maven으로 작업하는 데 필요한 코드가 여기에 있다. Maven이 익숙하지 않은 경우 Maven으로 Java 프로젝트 만들기를 참조하시오.

디렉토리 구조 만들기

선택한 프로젝트 디렉토리에서 *nix 시스템의 mkdir -p src/main/java/hello를 사용하여 다음 하위 디렉토리 구조를 작성한다:

└── src └── main └── java └── hello

pom.xml

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.springframework</groupId> <artifactId>gs-rest-service</artifactId> <version>0.1.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.5.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.jayway.jsonpath</groupId> <artifactId>json-path</artifactId> <scope>test</scope> </dependency> </dependencies> <properties> <java.version>1.8</java.version> </properties> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-releases</id> <url>https://repo.spring.io/libs-release</url> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>spring-releases</id> <url>https://repo.spring.io/libs-release</url> </pluginRepository> </pluginRepositories> </project>

Spring Boot Maven 플러그인은 다음과 같은 여러 편리한 기능을 제공한다:

  • 클래스 경로에 있는 모든 jar를 모으고 서비스를 전송하고 실행하는데 좀 더 편하게 만들어주는 실행 가능한 "über-jar"를 빌드한다.

  • public static void main() 메소드를 검색하여 실행 가능 클래스로 플래그를 지정한다.

  • Spring Boot 의존성과 일치하도록 버전 번호를 설정하는 빌트인 의존성 리졸버를 제공한다. 모든 버전을 재정의할 수 있지만 기본적으로 Boot에서 선택한 버전으로 설정된다.

IDE로 빌드하기

resource representation 클래스 생성하기

이제 프로젝트와 빌드 시스템을 구축했으므로 웹 서비스를 만들 수 있다.

서비스 상호 작용에 대해 생각하며 단계를 진행하세요.

서비스는 /greeting 에 대한 GET 요청을 처리하며 선택적으로 쿼리 문자열에 name 매개 변수를 사용한다. GET 요청은 greeting을 JSON body에 표현하여 200 OK 를 함께 리턴해야 한다. 다음과 같이 보일 것이다:

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

id 필드는 greeting에 대한 고유한 식별자이며 content 는 greeting의 텍스트이다.

greeting을 모델링하려면 resource representation 클래스를 작성하시오. id  content 데이터에 대한 필드, 생성자 및 접근자를 POJO (Plain Old Java Object)로 제공하시오.

src/main/java/hello/Greeting.java

package hello; public class Greeting { private final long id; private final String content; public Greeting(long id, String content) { this.id = id; this.content = content; } public long getId() { return id; } public String getContent() { return content; } }

아래 단계에서 볼 수 있듯이, Spring은 Jackson JSON 라이브러리를 사용하여 Greeting 유형의 인스턴스를 JSON으로 자동 정리한다.

그런 다음 greetings를 지원하는 리소스 컨트롤러를 생성한다.

리소스 컨트롤러 생성

RESTful 웹 서비스 구축에 대한 Spring의 접근 방식에서 HTTP 요청은 컨트롤러에 의해 처리된다. 이런 컴포넌트는 @RestController 어노테이션을 통해 쉽게 식별할 수 있으며, GreetingController  Greeting 클래스의 새 인스턴스를 반환하여 /greeting 에 대한 GET 요청을 처리한다:

src/main/java/hello/GreetingController.java

package hello; import java.util.concurrent.atomic.AtomicLong; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class GreetingController { private static final String template = "Hello, %s!"; private final AtomicLong counter = new AtomicLong(); @RequestMapping("/greeting") public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name) { return new Greeting(counter.incrementAndGet(), String.format(template, name)); } }

이 컨트롤러는 간단하지만 내부에서 많은 일들이 일어나고 있다. 단계별로 진행을 해봅시다.

@RequestMapping 어노테이션은 /greeting 에 대한 HTTP 요청이 greeting() 메소드에 매핑되도록 한다.

@RequestMapping 은 기본적으로 모든 HTTP 작업을 매핑하기 때문에 위의 예에서는 GET vs PUT, POST 등을 지정하지 않는다. @RequestMapping(method=GET) 을 사용하여 매핑의 범위를 좁힌다.

@RequestParam 은 쿼리 문자열 매개 변수 name 의 값을 greeting() 메소드의 name 매개 변수에 바인딩한다. 요청에 name 매개 변수가 없으면 defaultValue "World"가 사용된다.

메소드 본문을 구현하면 counter`의 다음 값을 기반으로 `id  content 속성을 갖는 새 Greeting 객체가 만들어지고 반환되며 greeting 템플릿 을 사용하여 지정된 name 의 서식이 지정된다.

기존 MVC 컨트롤러와 RESTful 웹 서비스 컨트롤러 간의 주요 차이점은 HTTP response body를 만드는 방법이다. 뷰 기술을 사용하여 greeting 데이터를 HTML로 server-side 렌더링하는 대신 RESTful 웹 서비스 컨트롤러는 Greeting 객체를 채우고 반환한다. 객체 데이터는 JSON으로 HTTP 응답에 직접 작성된다.

이 코드는 모든 메소드가 뷰가 아닌 도메인 객체를 반환하는 컨트롤러로 클래스를 표시한다는 Spring 4의 새로운 어노테이션 @RestController 을 사용한다. 이것은 @Controller  @ResponseBody 를 함께 사용한 것의 단축형이다.

Greeting 객체는 JSON으로 반환해야 한다. Spring의 HTTP 메시지 컨버터 지원 덕분에 이 변환을 수동으로 수행할 필요가 없다. Jackson 2가 클래스 경로에 있기 때문에 Spring의 MappingJackson2HttpMessageConverter 가 자동으로 선택되어 Greeting 인스턴스를 JSON으로 변환한다.

실행 가능한 어플리케이션 만들기

이 서비스를 외부 어플리케이션 서버에 배포하기 위해 기존의 WAR 파일로 패키징할 수 있지만, 아래에서 설명하는 더 간단한 접근 방식으로 독립 실행형 어플리케이션을 생성할 수 있다. Java main() 메소드로 구동되는 실행 가능한 단일 JAR 파일로 모든 것을 패키징한다. 이 과정에서 외부 인스턴스에 배포하는 대신 HTTP 런타임으로 Spring에서 지원하는 내장형 Tomcat 서블릿 컨테이너를 사용한다.

src/main/java/hello/Application.java

package hello; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

@SpringBootApplication 은 다음을 모두 추가하는 편리한 주석이다:

  • @Configuration 은 클래스를 어플리케이션 context의 bean 정의 소스로 태그 지정한다.

  • @EnableAutoConfiguration 은 Spring Boot에게 클래스 경로 설정, 다른 bean 및 다양한 프로퍼티 설정을 기반으로 bean 추가를 시작하도록 한다.

  • 일반적으로 @EnableWebMvc 를 Spring MVC app에 추가할 것이지만, Spring Boot는 클래스 경로에서 spring-webmvc를 발견할 때 그것을 자동으로 추가한다. 이것은 어플리케이션에 웹 어플리케이션으로 플래그를 지정하고 DispatcherServlet 설정과 같은 주요 동작을 활성화 한다.

  • @ComponentScan  hello 패키지에서 다른 컴포넌트, 구성 및 서비스를 찾아서 컨트롤러를 찾을 수 있도록 Spring에 지시한다.

main() 메소드는 Spring Boot의 SpringApplicaiton.run() 메소드를 사용하여 어플리케이션을 시작한다. 한줄의 XML이 없다는 것을 알고 있었는가? web.xml 파일도 없다. 이 웹 어플리케이션은 100% 순수 자바이며 인프라 구성에 대해 다룰 필요가 없다.

실행 가능한 JAR 만들기

Gradle 또는 Maven을 사용하여 커맨드 라인에서 어플리케이션을 실행할 수 있다. 또는 모든 필요한 의존성, 클래스 및 리소스 포함하는 단일 실행 가능한 JAR 파일을 빌드하고 실행할 수 있다. 따라서 개발 생명주기(life cycle), 다양한 환경에 걸쳐 어플리케이션으로 서비스를 쉽게 제공 및 배포할 수 있다.

Gradle을 사용하는 경우 ./gradlew bootRun 을 사용하여 어플리케이션을 실행할 수 있다. 또는 ./gradlew build 를 사용하여 JAR 파일을 작성할 수 있다. 그런 다음 JAR 파일을 실행할 수 있다:

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

Maven을 사용하는 경우 ./mvnw spring-boot:run을 사용하여 어플리케이션을 실행할 수 있다. 또는 ./mvnw clean package로 JAR 파일을 빌드할 수 있다. 그런 다음 JAR 파일을 실행할 수 있다:

java -jar target/gs-rest-service-0.1.0.jar

위 절차는 실행 가능한 JAR를 생성한다. 고전의 WAR 파일을 빌드하도록 선택할 수도 있다.

로깅 출력이 표시된다. 몇 초 이내에 서비스가 실행되어야 한다.

서비스 테스트

이제 서비스가 시작되었으니 http://localhost:8080/greeting을 방문해 보시오:

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

http://localhost:8080/greeting?name=User와 같이 name 쿼리 문자열 매개 변수를 제공하시오. content 속성의 값이 "Hello World!"에서 "Hello User!"로 변경되는지 확인하시오:

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

이 변경은 GreetingController  @RequestParam 방식이 예상대로 작동함을 보여준다. name 매개 변수에는 기본값 "World"가 지정되었지만 항상 쿼리 문자열을 통해 명시적으로 재정의될 수 있다.

id 속성이 1 에서 2 로 어떻게 바뀌었는지 확인하시오. 이는 여러 요청에서 동일한 GreetingController 인스턴스에 대해 작업 중이며 counter 필드가 예상대로 각 호출에서 증가되고 있음을 증명한다.

요약

축하합니다! 방금 Spring과 함께 RESTful 웹 서비스를 개발했다.

다른 예제들

다음 가이드들도 도움이 될 것이다:

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기