Spring Boot 3 / Spring 6부터 등장한 HttpInterface는 외부 HTTP API 호출 방식을 완전히 바꿔놓았다.
예전처럼 RestTemplate, WebClient 코드를 서비스 안에서 직접 작성하지 않고, 자바 인터페이스 + 애노테이션 선언만으로 HTTP 클라이언트를 만들 수 있다.
이 글에서는 실무 기준으로 HttpInterface의 개념, 동작 구조, 그리고 실제 사용 흐름을 정리한다.
한 줄 요약
HttpInterface는 외부 API 호출을 자바 인터페이스로 추상화하고, Spring이 이를 실제 HTTP 호출로 변환해주는 선언형 HTTP 클라이언트다.
Feign과 매우 유사하지만, Spring Core에서 공식 제공한다는 점이 가장 큰 차이다.
왜 HttpInterface가 나왔을까?
외부 API를 호출할 때 보통 다음과 같은 문제가 있었다.
- WebClient는 유연하지만 코드가 장황해짐
- HTTP 호출 코드가 서비스 로직과 섞임
- API 스펙이 코드로 명확히 드러나지 않음
Spring 팀은 이런 문제를 이렇게 해결했다.
“컨트롤러는 애노테이션으로 선언형 정의가 가능한데, 왜 HTTP 클라이언트는 항상 직접 구현해야 하지?”
그 결과가 HttpInterface다.
핵심 개념 정리 (중요)
1️⃣ Controller가 아니다 (역할이 반대)
HttpInterface는 모양은 Controller와 비슷하지만, 역할은 완전히 반대다.
구분ControllerHttpInterface
| 역할 | 요청을 받음 | 요청을 보냄 |
| 대상 | 클라이언트 | 외부 서버 |
| 구현 | 직접 구현 | Spring이 자동 생성 |
즉, 요청을 처리하는 쪽이 아니라 요청을 보내는 계약이다.
2️⃣ 구현체가 없는 인터페이스
@HttpExchange(url = "https://api.example.com")
public interface ExternalUserClient {
@GetExchange("/users/{id}")
UserResponse getUser(@PathVariable Long id);
}
이 인터페이스의 특징은 다음과 같다.
- 구현 클래스 없음
- @Service 없음
- 로직 없음
하지만 이 메서드를 호출하면 실제 HTTP 요청이 발생한다.
👉 이유는 Spring이 런타임에 프록시 구현체를 자동 생성하기 때문이다.
3️⃣ 실제 HTTP 호출은 누가 하나?
HttpInterface는 선언만 담당한다.
실제 통신은 다음 중 하나가 수행한다.
- WebClient (기본)
- RestClient (Spring 6.1+)
구조를 단순화하면 아래와 같다.
[HttpInterface]
↓
[Spring Proxy]
↓
[WebClient / RestClient]
↓
[외부 API]
실무 기준 전체 흐름 (3단계)
1️⃣ 외부 API를 인터페이스로 선언
@HttpExchange(url = "https://api.example.com")
public interface ExternalUserClient {
@GetExchange("/users/{id}")
UserResponse getUser(@PathVariable Long id);
}
이 단계에서는 API 스펙만 정의한다.
2️⃣ 인터페이스를 실제 HTTP 클라이언트로 변환
@Configuration
public class HttpClientConfig {
@Bean
ExternalUserClient externalUserClient(WebClient.Builder builder) {
WebClient webClient = builder.build();
HttpServiceProxyFactory factory =
HttpServiceProxyFactory
.builder(WebClientAdapter.forClient(webClient))
.build();
return factory.createClient(ExternalUserClient.class);
}
}
여기서 핵심 역할을 하는 것이 HttpServiceProxyFactory다.
- 인터페이스를 분석
- 애노테이션 기반으로 HTTP 요청 구성
- 동적 프록시 생성
👉 이 시점에 실행 가능한 HTTP 클라이언트 Bean이 만들어진다.
3️⃣ 서비스에서는 그냥 메서드 호출
@Service
@RequiredArgsConstructor
public class UserService {
private final ExternalUserClient externalUserClient;
public UserResponse getUser(Long id) {
return externalUserClient.getUser(id);
}
}
서비스 입장에서는 이 객체가 HTTP 클라이언트인지조차 알 필요가 없다.
메서드 호출 = HTTP 요청
이라는 구조가 완성된다.
이 구조의 장점 (실무 기준)
- HTTP 호출 코드와 비즈니스 로직 완전 분리
- 외부 API 스펙이 인터페이스로 명확히 문서화됨
- 테스트 시 인터페이스를 Mock으로 대체 가능
- Feign 없이 Spring 공식 스택 사용
Feign과의 차이
| 항목 | Feign | HttpInterface |
| 제공 주체 | Netflix / Spring Cloud | Spring Core |
| 설정 | 간단 | 약간의 설정 필요 |
| 기반 | 동기 중심 | WebClient 기반 |
| 미래 방향 | 유지 | 적극 권장 |
Spring Boot 3 이상이라면 HttpInterface가 사실상 정답이다.
결론
HttpInterface는 단순한 문법 설탕이 아니다.
- 외부 API 호출을 선언형으로 표준화하고
- 서비스 로직을 더 순수하게 유지하게 해준다.
외부 API 연동이 많은 서비스라면, 도입하지 않을 이유가 없다.
다음 글로 이어진다면 아래 주제가 자연스럽다.
- 인증 헤더 / 토큰 자동 처리
- 공통 에러 핸들링 전략
- 테스트 및 Mocking 방법
- Feign → HttpInterface 마이그레이션
필요하면 바로 이어서 정리한다.
'개발일지 > SPRINGBOOT' 카테고리의 다른 글
| Spring Boot 3.x + Elasticsearch 8.x 연동기 — 레거시 코드에서 타입세이프 클라이언트 (2) | 2026.04.23 |
|---|---|
| springboot , record 를 통한 dto (0) | 2025.12.16 |
| UUID vs Sequential ID: 데이터베이스 설계에서 무엇을 선택할까? (0) | 2025.10.24 |
| JPA에서 @ManyToOne과 @OneToMany, 그리고 양방향 매핑의 차이 (0) | 2025.10.11 |
| Spring Data Envers와 RevisionRepository로 엔티티 변경 이력 관리하기 (0) | 2025.10.08 |