티스토리 뷰

FeignClient 란?

Feign Client란 Netflix에서 개발한 Http Client다.


 

카카오 소셜로그인

카카오 토큰을 받는 API는  x-www-form-urlencoded 타입이 필수값이다.

x-www-form-urlencoded 타입은 Spring에서 @RequestParam, @ModelAttribute 어노테이션으로 주입받을 수 있다.

둘중에 하나를 선택해야하는데 @RequestParam은 사용하고싶지않았다.

 


 

@RequestParam 단일 파라메터

@FeignClient(url = "https://kauth.kakao.com", value = "kakaoAuthApi")
public interface KakaoAccessTokenClient {

    @PostMapping(
            value = "/oauth/token",
            consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
            produces = MediaType.APPLICATION_JSON_VALUE
    )
    KakaoAccessTokenResponse getKakaoToken(
            @RequestParam("grant_type") String grantType,
            @RequestParam("client_id") String clientId,
            @RequestParam("redirect_uri") String redirectUri,
            @RequestParam("code") String code
    );
}

첫번째 방식은 파라메터가 많고 모두 타입이 동일해서 실수할 가능성이 높아서 사용하고싶지않았다. (Builder를 사용하고싶었다)

 


 

@RequestParam Map

@FeignClient(url = "https://kauth.kakao.com", value = "kakaoAuthApi")
public interface KakaoAccessTokenClient {

    @PostMapping(
            value = "/oauth/token",
            consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
            produces = MediaType.APPLICATION_JSON_VALUE
    )
    KakaoAccessTokenResponse getKakaoToken(
            @RequestParam HashMap<String,String> paramMap
            );
}

HashMap<Object, Object> request = new HashMap<>();
request.put("grant_type", grantType);
request.put("client_id", clientId);
request.put("redirect_uri", redirectUri);
request.put("code", code);

두번째 방식은 HashMap으로 HttpClient 이외의 Service 등의 layer에서 구체적인 Api 명세를 알아야해서 사용하고싶지않았다.


 

@ModelAttribute

@FeignClient(url = "https://kauth.kakao.com", value = "kakaoAuthApi")
public interface KakaoAccessTokenClient {

    @PostMapping(
            value = "/oauth/token",
            consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
            produces = MediaType.APPLICATION_JSON_VALUE
    )
    KakaoAccessTokenResponse getKakaoToken(
            KakaoAccessTokenRequest request
    );
}

@Builder
public record KakaoAccessTokenRequest(
        @JsonProperty("grant_type")
        String grantType,
        @JsonProperty("client_id")
        String clientId,
        @JsonProperty("redirect_uri")
        String redirectUri,
        String code
) {
}

위와 같이 작성했지만 정삭적으로 값이 들어오지 않았다.

이유는 x-www-form-urlencoded 타입은 Json 타입이 아니기때문에 @JsonProperty를 사용할 수 없었다.

https://stackoverflow.com/questions/38757946/jsonproperty-not-working-for-content-type-application-x-www-form-urlencoded

그렇다고 변수명을 스네이크 케이스로 사용하고싶지도 않았다.

 


@BindParam

spring-framwork 이슈를 찾아보니 나와 같은 고민을 하는 사람들이 있었고 @BindParam이 추가되었다.

https://github.com/spring-projects/spring-framework/issues/18012#issuecomment-1651169636

 

@Getter
public class KakaoAccessTokenRequest {
    private String grantType;
    private String clientId;
    private String redirectUri;
    private String code;
    @Builder
    public KakaoAccessTokenRequest(
            @BindParam("grant_Type")
            final String grantType,
            @BindParam("client_id")
            final String clientId,
            @BindParam("redirect_uri")
            final String redirectUri,
            final String code
    ) {
        this.grantType = grantType;
        this.clientId = clientId;
        this.redirectUri = redirectUri;
        this.code = code;
    }
}

public record KakaoAccessTokenRequest(
        String grantType,
        String clientId,
        String redirectUri,
        String code
) {
    @Builder
    public KakaoAccessTokenRequest(
            @BindParam("grant_Type")
            final String grantType,
            @BindParam("client_id")
            final String clientId,
            @BindParam("redirect_uri")
            final String redirectUri,
            final String code
    ) {
        this.grantType = grantType;
        this.clientId = clientId;
        this.redirectUri = redirectUri;
        this.code = code;
    }
}

 

하지만 @BindParam 방식 또한 정삭적을 동작하지 않았습니다.

아마 FeignClient에 BindParamNameResolver를 아직 지원하지 않는것같다.


@FormProperty

조금더 찾아보니 FeignClient에서는 @FormProperty 어노테이션을 제공하고 x-www-form-urlencoded 타입에서 사용이 가능할것같았다.

https://github.com/OpenFeign/feign-form

public record KakaoAccessTokenRequest(
        @FormProperty("grantType")
        String grantType,
        @FormProperty("clientId")
        String clientId,
        @FormProperty("redirectUri")
        String redirectUri,
        String code
) {
}

하지만 @FormProperty 또한 정상적으로 동작하지않았다.

디버깅을 해보니 FeignClient @FormProperty를 바인딩 하기위해서 내부적으로 PojoUtil 클래스를 사용한다.

문제는 isFinal 메서드에 있었다.

프로젝트에서 Dto에서 record를 사용하고있었고 record를 사용하면 각 필드는 private final 필드로 정의되기 때문에 정상적으로 작동하지않는다.

 

접근 제어자를 강제하는것은 Pojo가 아니라 Java-Bean이다.

https://www.geeksforgeeks.org/pojo-vs-java-beans/


마무리

아직 FeignClient @FormProperty에서 record를 사용할 수 없다.

https://github.com/OpenFeign/feign-form/pull/120

위 코드를 제거해서 PR을 올렸는데 마지막 업데이트가 4년전인 하위 모듈이기때문에 언제 업데이트 될지는 모르겠다.