들어가며
FeignClient 등 외부 API 를 호출하는 환경에서 테스트를 하기 위해 다음과 같은 방법을 사용할 수 있다.
- mock 서버 구성
- Mockito 로 mocking
- WireMock 사용
mock 서버는 Postman 의 Mock Servers 등으로 구성하는 레퍼런스가 꽤 있지만 테스트코드가 아닌 외부 툴에 의존한다는 점과, 그로 인한 호출량 제한 등을 생각해야 하므로 일단은 배제했다.
다음으로 mocking 은 FeignClient 자체의 기능을 테스트하는게 아니라, Feign 을 mocking 해서 이를 의존하는 다른 로직이 정상적으로 실행되는 케이스를 검증하는 것이므로 Decoder 등으로 설정한 Feign 자체 예외 케이스 핸들링에 대한 검증을 할 수 없어 배제했다.
결론적으로 이중 WireMock 를 택해 테스트환경을 구성했는데, 테스트시 NoHttpResponseException 이 특정 엔드포인트에만 발생하는 케이스가 발생해 이를 정리한 글이다.
테스트 환경
외부 api 테스트시 clients 모듈의 testFixtures 에서 설정해두었고 내부의 mappings디렉토리에 stub 용 json 파일을 보관하고 있다.

요청에 대해서는 아래처럼 예외 발생 케이스에 대해 ErrorDecoder 를 구현하고 있다.

해당 Decoder 는 Feign 스펙 정의시에 넘겨주는 configuraton 에 빈으로 등록해 동작할 수 있도록 구성했다.

위에서 구성한 Decoder 를 사용한 api 를 테스트하기 위해 WireMock 의 동작을 json 들을 이용해 정의했다.

예시는 아래와 같다.

문제 발생 원인
위처럼 구성하고 테스트를 실행해보니 일부 케이스만 테스트가 실패했다.

json 파일을 확인해도 크게 이상한 부분이 없어 로그를 확인해보니 AccessDeniedException 대신 RetryableException 이 발생했다고 나온다.

좀더 올라가보면 NoHttpResponseException 이 발생한 것을 확인할 수 있고 Connection 에 의한 문제임을 확인할 수 있었다.

결과가 NoHttpResponseException 이 아니라 RetryableException 인 이유가 궁금해서 찾아보았는데, Feign은 설정된 Client(OkHttpClient 또는 ApacheHttp5Client) 를 사용해 요청을 보내고, 해당 요청 처리 중 IOException이 발생하면, SynchronousMethodHandler의 executeAndDecode() 내부에서 감싸서 예외를 다시 던진다.

그래서 결과가 RetryableException 으로 래핑되고


invoke 메서드에서 RetryableException이 발생했을 때 재시도할지, 아니면 예외를 던질지를 결정하게 된다.

원인은 무엇인가?
Spring Cloud 2023부터는 feign.Client를 자동으로 주입하지 않기 때문에, 개발자가 적절한 HTTP client 의존성을 추가해야만 Feign이 작동한다.

스프링부트 3.X 부터 아래 의존성만 추가하면 yml 설정 없이 ApacheHttp5Client 를 사용할 수 있다.
implementation 'io.github.openfeign:feign-hc5'


필자의 케이스는 ApacheHttp5Client 를 사용할 경우 발생한 문제로 아래 글에서 원인을 찾을 수 있었다.


Apache HttpClient는 연결을 keep-alive로 풀링하여 재사용한다. 각 테스트 클래스마다 wiremock endpoint가 제거되고 새로 생성되는데, 애플리케이션이 기존 커넥션이 끊어졌다는 사실을 감지하고 새로운 커넥션을 생성하는 데 약 2초가 걸린다. 만약 이 2초를 기다리지 않고 테스트를 바로 실행하면, 기존에 끊어진 커넥션을 재사용하려고 하다가 NoHttpResponseException이 발생할 수 있다.
해결하려면 아래 두 방법 중 하나를 택하면 된다.
1. "Connection": "close" 헤더를 사용해 커넥션을 재사용하지 않도록 한다.

2. OkHttpClient 로 Client 를 바꾼다.
feign 은 OkHttpClient 와 ApacheHttpClient5 를 지원한다. OkHttpClient 를 사용하려면 Spring Cloud OpenFeign 4 (Spring Cloud 2023.x) 기준으로 아래 처럼 설정할 수 있다.

테스트문제 때문에 Okhttp 로 교체해도 될지 잘 모르겠어서, 벤치마크 결과를 찾아봤다. 해당 테스트는 단순한 GET 요청이고 애플리케이션 환경에 따라 상이할것이라 참고로만 보는게 좋을 것 같다.


해당 벤치마크 결과에 따르면 Apache HttpClient5 가 OkHttp4보다 속도측면에서는 4배정도 빠르다.

우선은, 변경을 고려할만한 필요성을 느끼지 못해 Connection close 헤더를 추가해서 해결했다.
'Spring' 카테고리의 다른 글
| [SpringBoot] Micrometer 와 Actuator 개념과 사용 이유 알아보기 (2) | 2025.07.21 |
|---|---|
| [Spring Security] Preflight는 성공하지만 실제 요청에서 CORS 오류가 발생하는 문제 해결하기 (0) | 2025.05.26 |
| [Spring] TestContainers, @TestConfiguration 사용시 @DynamicPropertySource 가 적용되지 않는 문제와 해결방안 (0) | 2025.04.10 |
| [Spring] API 응답에서 직접 정의한 Error code 는 왜 사용할까? (0) | 2025.03.16 |
| [Spring] OpenApI 3.0 Swagger 문서 작성 및 설정 방법 (1) | 2025.02.11 |