본문으로 건너뛰기
안동민 개발노트 아이콘

안동민 개발노트

본문 시작
7장 : 마이크로서비스 경계와 통신

RestClient와 HTTP Service Client

앞 절의 포트는 보고서가 같은 JVM에 있는지 원격 서비스인지 숨겼습니다. 원격으로 옮기면 메서드 호출 하나에 DNS, 연결, TLS, HTTP 상태, 응답 역직렬화 실패가 새로 들어옵니다.

Spring의 HTTP Service Client는 URI 계약을 Java interface로 표현하고 Boot의 client group은 같은 호스트를 쓰는 proxy의 base URL과 timeout을 묶습니다. 타입이 생겼다고 네트워크가 신뢰할 수 있게 되는 것은 아닙니다.


원격 호출은 실패하는 의존성이다

RestClient는 요청을 직접 조립할 때 적합하고 HTTP interface는 반복되는 URI 호출을 선언형 proxy로 줄입니다. group은 같은 원격 서비스의 base URL과 연결 정책을 한곳에서 관리합니다. 응답 DTO는 내부 도메인 타입과 분리합니다.


타입 안전한 보고서 client를 등록한다

@ImportHttpServicesReportClient proxy bean을 만들고 reports group 설정을 적용합니다. reports.example.test는 실제 서버가 없는 예약 예제 주소이므로 이 절에서는 컴파일과 설정 구조만 확인하고 원격 성공을 주장하지 않습니다.

pom.xml — dependencies에 추가
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-restclient</artifactId>
</dependency>
src/main/java/com/andongmin/studylog/report/client/ReportClient.java
package com.andongmin.studylog.report.client;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.service.annotation.GetExchange;

public interface ReportClient {
    @GetExchange("/api/reports/{subject}")
    ReportResponse find(@PathVariable String subject);

    record ReportResponse(String subject, long sessions, long minutes) {
    }
}
src/main/java/com/andongmin/studylog/report/client/ReportClientConfiguration.java
package com.andongmin.studylog.report.client;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.service.registry.ImportHttpServices;

@Configuration(proxyBeanMethods = false)
@ImportHttpServices(group = "reports", types = ReportClient.class)
public class ReportClientConfiguration {
}
src/main/resources/application.properties
spring.http.serviceclient.reports.base-url=https://reports.example.test
spring.http.serviceclient.reports.connect-timeout=2s
spring.http.serviceclient.reports.read-timeout=2s

interface의 반환 타입이 ReportResponse라고 해서 subject와 집계값이 유효하다는 뜻은 아닙니다. proxy 바깥 adapter가 빈 body, 다른 subject, 음수 집계를 검사해 ReportPort.SubjectReport로 변환해야 합니다.


proxy 등록과 원격 성공을 구분한다

아래 컴파일은 starter, annotation, DTO의 타입 정합성만 확인합니다. 실제 proxy bean 생성은 Spring context가 떠야 확인할 수 있고 404·5xx·timeout 변환은 다음 adapter에서 구현합니다. 존재하지 않는 원격 주소를 호출해 성공 검증처럼 다루지 않습니다.

./mvnw -q -DskipTests compile
Group 확인 결과
컴파일 뒤 context 테스트를 추가한다면 `ReportClient` bean이 하나 존재하는지 확인합니다. 연결·읽기 timeout은 실제 지연을 제어할 stub HTTP server가 있는 테스트에서 검증해야 하며 이 문서에는 그런 서버를 제공하지 않습니다.

원격 응답을 도메인 객체처럼 신뢰하지 않는다

외부 DTO를 별도 타입으로 받고 timeout, 상태 코드, 빈 body를 경계에서 검증한 뒤 내부 모델로 변환합니다.

원격 schema가 바뀌면 역직렬화는 성공해도 의미가 깨질 수 있습니다. subject 일치, 0 이상의 횟수와 분량처럼 내부 포트가 요구하는 조건을 adapter에서 다시 검사합니다.

동기 HTTP만으로 서비스 상태를 맞추면 한쪽 장애가 원본 transaction까지 붙잡습니다. 다음에는 완료 사실을 DB에 먼저 남기고 나중에 전달하는 outbox 경계를 살펴봅니다.