본문 바로가기

Java/Spring

Spring.io 따라하기: RESTful 웹 서비스에 요청하기

테스트 환경 및 주요 아젠다

이 예제에서는 Spring의 RestTemplate을 사용하여 무작위 인용구를 검색하는 애플리케이션을 구축합니다.

더보기

이 프로젝트의 개발 환경

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

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

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

Spring Initializr를 사용하여 시작하기

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

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

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

RESTful 웹 서비스 실행

예제에 앞서 요청 대상이되는 RESTful 웹 서비스가 필요합니다. https://github.com/spring-guides/quoters에서 소스 코드를 다운로드합니다.

ZIP 파일을 압축 해제하고 프로젝트의 루트 디렉토리에서 Maven 프로젝트를 실행합니다.

$ ./mvnw spring-boot:run

웹 브라우저를 열고 주소창에 http://localhost:8080/api/random을 입력합니다.

{
   type: "success",
   value: {
      id: 10,
      quote: "Really loving Spring Boot, makes stand alone Spring apps easy."
   }
}

요청의 결과로 임의의 인용구를 JSON으로 반환합니다.

  • http://localhost:8080/api/random: 사용 가능한 무작위 인용구를 표시합니다.
  • http://localhost:8080/api/: 사용 가능한 모든 인용구를 표시합니다.
  • http://localhost:8080/api/1: 첫 번째 인용구를 표시합니다.
  • http://localhost:8080/api/2: 두 번째 인용구를 표시합니다.
  • ...
  • http://localhost:8080/api/12: 마지막 인용구를 표시합니다. 

RESTful 웹 서비스에서 리소스 가져오기

우선 필수 데이터를 포함하고 있는 도메인 클래스가 필요합니다.

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

package com.example.consumingrest;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public record Quote(String type, Value value) { }
코드 비고
Line 5 @JsonIgnoreProperties(ignoreUnknown = true) 레코드 클래스에서 바인딩되지 않은 속성을 무시합니다.
더보기

레코드 클래스는 JSON 데이터가 저장하는 키와 정확하게 일치해야합니다.

만약 키가 일치하지 않으면 @JsonProperty를 사용해 JSON 데이터로부터 키를 추출 할 수 있도록 합니다.

이 예제에서는 JSON 데이터와 레코드 클래스의 속성이 완전히 일치하므로 @JsonProperty를 사용하지 않습니다.

추가적으로 value에 대한 JSON 데이터를 바인딩하기 위한 레코드 클래스가 필요합니다.

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

package com.example.consumingrest;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public record Value(Long id, String quote) { }

Initializr는 main()에서 Spring 애플리케이션을 시작합니다.

다음 경로 src/main/java/com/example/consumingrest 패키지에서 ConsumingRestApplication.java 파일을 엽니다.

package com.example.consumingrest;

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

@SpringBootApplication
public class ConsumingRestApplication {

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

}

ConsumingRestApplication 클래스에서 RESTful 웹 서비스와 통신하기 위해 몇 가지 코드를 추가해야 합니다.

  • logger: 실행 결과를 출력합니다.
  • RestTemplate: 응답 데이터를 Jackson JSON으로 처리합니다.
  • CommandLineRunner: RestTemplate를 실행하여 RESTful 웹 서비스에 요청을 보냅니다.
package com.example.consumingrest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class ConsumingRestApplication {
	private static final Logger log = LoggerFactory.getLogger(ConsumingRestApplication.class);

	public static void main(String[] args) {
		SpringApplication.run(ConsumingRestApplication.class, args);
	}
	
	@Bean
	public RestTemplate restTemplate(RestTemplateBuilder builder) {
		return builder.build();
	}
	
	@Bean
	@Profile("!test")
	public CommandLineRunner run(RestTemplate restTemplate) throws Exception {
		return args -> {
			Quote quote = restTemplate.getForObject("http://localhost:8080/api/random", Quote.class);
			log.info(quote.toString());
		};
	}
}

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

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

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

$ ./gradlew bootRun

...

Description:

Web server failed to start. Port 8080 was already in use.

Action:

Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.

FAILURE: Build failed with an exception.

다른 Spring Boot로 RESTful 웹 서비스를 실행하고 있기 때문에 8080 Spring Boot 기본 포트가 충돌합니다.

서비스 포트를 다른 포트로 변경하기 위해서 JAR 파일을 빌드합니다. JAR 빌드는 ./gradlew build를 사용합니다.

$ ./gradlew build

$ cd build/libs/
$ ls
consuming-rest-0.0.1-SNAPSHOT-plain.jar consuming-rest-0.0.1-SNAPSHOT.jar

프로젝트 루트 디렉토리의 build/libs에서 application.properties 파일을 생성합니다.

server.port=8081

Java 명령문 java -jar으로 JAR 파일을 실행합니다.

JAR 파일과 동일한 디렉토리에서 application.properties 파일의 애플리케이션 설정 값을 따릅니다.

$ java -jar target/gs-consuming-rest-0.1.0.jar


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

2023-11-22T18:18:11.501+09:00  INFO 13446 --- [           main] c.e.c.ConsumingRestApplication           : Starting ConsumingRestApplication v0.0.1-SNAPSHOT using Java 17.0.9 with PID 13446 (/Users/guenbongpark/Desktop/consuming-rest/build/libs/consuming-rest-0.0.1-SNAPSHOT.jar started by guenbongpark in /Users/guenbongpark/Desktop/consuming-rest/build/libs)
2023-11-22T18:18:11.503+09:00  INFO 13446 --- [           main] c.e.c.ConsumingRestApplication           : No active profile set, falling back to 1 default profile: "default"
2023-11-22T18:18:12.113+09:00  INFO 13446 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8081 (http)
2023-11-22T18:18:12.121+09:00  INFO 13446 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-11-22T18:18:12.121+09:00  INFO 13446 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.15]
2023-11-22T18:18:12.179+09:00  INFO 13446 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-11-22T18:18:12.180+09:00  INFO 13446 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 637 ms
2023-11-22T18:18:12.455+09:00  INFO 13446 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8081 (http) with context path ''
2023-11-22T18:18:12.466+09:00  INFO 13446 --- [           main] c.e.c.ConsumingRestApplication           : Started ConsumingRestApplication in 1.199 seconds (process running for 1.464)
2023-11-22T18:18:12.537+09:00  INFO 13446 --- [           main] c.e.c.ConsumingRestApplication           : Quote[type=success, value=Value[id=6, quote=It embraces convention over configuration, providing an experience on par with frameworks that excel at early stage development, such as Ruby on Rails.]]
코드 비고
Line 19 Tomcat started on port(s): 8081 (http) 포트 충돌을 회피하기 위해서 기본 8080 대신 8081으로 Tomcat을 실행합니다.
Line 21 Quote[type=success, value=Value[id=6, quote=""]] RESTful 웹 서비스에서 리소스를 요청하고 결과를 출력합니다.