본문 바로가기

백엔드 개발일지

[Spring] FeignClient로 POST 요청 시 발생하는 Length Required(411) 에러 해결 방법

반응형

최근, Yapp에서 진행하는 프로젝트 '그라밋'에서는 Google OAuth 로그인을 제공한다. 백엔드에서 OAuth 통신 로직을 구현할 때, oauth2-client 라이브러리를 많이 사용하겠지만 개인적으로 인터페이스 기반으로 API 호출을 추상화하는 FeignClient를 선호한다.

 

1. 문제 상황

 

이번 프로젝트에서도 FeignClient를 이용해 Google OAuth 로그인 로직을 구현했는데... Length Required(411)에러가 나타났다.

이전 프로젝트에서는 이런 에러를 마주친 적이 없어서 당황했는데 구글링을 해보니 디폴트로 제공되는 FeignClient에서 Content-Length 헤더가 기본으로 설정되지 않아서 `@Post` 요청을 보낼 시, 411 에러가 발생한다는 것이다.

 

참고: https://github.com/OpenFeign/feign/issues/1251

 

Getting 411 Length Required Error · Issue #1251 · OpenFeign/feign

I am trying to have a post call with token and hitting to the end URL, I am receiving 411 Length Required error. Even after creating the Interceptor and adding Content-Length: 0 to it, still I am r...

github.com

 

 

 

2. 해결 방안

이 문제를 해결하는 방법으로는 크게 두 가지가 있다.

1. 코드로 Content-Length를 0으로 설정하거나 직접 지정하는 직관적인 방법

2. FeignClient의 종류 중 하나인 httpclient나 okhttp를 추가해 설정하는 방법

 

411 에러가 나타나는 이유는 FeignClient는 본문이 없는 POST 요청을 보낼 경우, 기본적으로 Content-Length를 추가하지 않기 때문이다. 결국 서버는 요청을 처리할 때, Content-Length 헤더가 없으니 요청을 거부하고 411 Length Required 에러를 반환한다.

 

이 문제는 Feign의 기본 클라이언트(Default)에서만 발생하며, 2번 방법이 코드 상으로는 가장 깔끔하다. 하지만 우리 프로젝트에서는 2번 방법을 적용했을 때 바로 해결되지 않았고, httpclient나 okhttp에 대한 지식이 부족한 상태에서 함부로 사용하는 것이 조심스러웠다. 이에 1번 방법을 선택해 아래와 같이 해결했다.

 

import feign.RequestInterceptor
import feign.RequestTemplate
import org.springframework.stereotype.Component

// POST 요청에 Content-Length 헤더가 없으면 411 Length Required 에러를 반환
// 이를 방지하기 위해 본문과 Content-Length 헤더를 명시적으로 추가
@Component
class ContentLengthRequestInterceptor : RequestInterceptor {
    override fun apply(requestTemplate: RequestTemplate) {
        val length = if (requestTemplate.body() != null) requestTemplate.body().size else 0
        
        // POST 요청인데 본문이 없는 경우, 요청에 가짜 본문인 "gradmeet"를 추가
        if (length == 0 && requestTemplate.method() == "POST") {
            requestTemplate.body("gradmeet")
            requestTemplate.header(CONTENT_LENGTH_HEADER, "gradmeet".toByteArray().size.toString())
        }
    }

    companion object {
        private const val CONTENT_LENGTH_HEADER = "Content-Length"
    }
}

 

 

이 문제를 해결하기 위해 FeignClient의 인터셉터인 RequestInterceptor를 사용하여, POST 요청을 보낼 때마다 가짜 본문과 적절한 Content-Length 헤더를 추가해 주었다. 이렇게 수정한 후에는 OAuth 로직이 정상적으로 동작했다.

 

 

흥미로운 점은, 이전 프로젝트에서도 Feign을 이용해 OAuth를 처리했지만 이런 문제가 발생하지 않았다는 거다. 그래서 이 문제가 정확히 어떤 조건에서 발생하고, 언제 발생하지 않는지에 대해 더 조사해봐야 할 것 같다...

🤔

반응형