Spring

@FeignClient 기본 세팅

쭈녁 2024. 5. 21. 19:18

프로젝트에서 외부 api에 요청을 보내야할 경우들이 있어 FeinClient를 사용해보았다. 이 과정중에 발생한 에러를 해결하며 공부하게된 내용들을 정리해본다.

 

의존성 관리

해당 의존성은 Springboot 3.2.4에 해당하는 의존성이다.

openfeign 라이브러리는 spring cloud 기반의 라이브러리이다. 때문에 spring cloud 의존성이 필요하며

spring cloud는 spring 과 호환성이 중요함으로 버전관리에 신경써야한다. 아래 공식 문서로 확인 가능하다.

https://spring.io/projects/spring-cloud

ext {
    set('springCloudVersion', "2023.0.0")
}

dependencyManagement {
    imports {
       mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

dependencies {
	implementation "org.springframework.cloud:spring-cloud-starter-openfeign"
}

 

FeignClient 기술은 RestTemplet과 흡사하지만 직접 API호출 환경을 설정하여 사용하던 RestTemplet 과는 다르게 선언적 방식과 추상화된 인터페이스를 제공함으로써 외부 API 호출이 가능하도록 한다.

 

기본 세팅

메인 클레스에 @EnableFeignClients 를 추가해주어 하위 패키지에 존재하는 @FeignClient 어노테이션을 찾아 구현체를 만듭니다. (Spring 의 Component와 ComponentScan의 관계와 유사)

@SpringBootApplication
@EnableFeignClients
public class Feign2Application {

    public static void main(String[] args) {
       SpringApplication.run(Feign2Application.class, args);
    }

}

 

@FeignClient 인터페이스

※주의 :

@FeignClient 어노테이션 내부의 url 옵션은 호출할 api의 도메인 or IP 주소값이 입력되어야한다!!

@RequestMapping + @GetMapping 의 경우 두 경로값을 합쳐 EndPoint를 찾아 가는 점과 다르게 url에 접근할 도메인 or 서버의 주소를 기입해주어야한다.

**맨 마지막에 잘못된 사용예와 수정 후 참고**

 

테스트 환경 세팅

1. FeignClient 환경(8080 포트)

1-1 FeignClient 인터페이스

@FeignClient(name = "example", url = "http://localhost:8282")
public interface FeignClientService {
    @GetMapping(value = "/test")
    Result getInfo(
            @RequestHeader(value = "Authorization", required = false) String token,
            @RequestParam(name = "name", required = false)String name,
            @RequestParam(name = "age", required = false) Integer age,
            @RequestParam(name = "status" , required = false)Integer status);
}

 

1-2 FeignClient 컨트롤러

@RestController
@RequiredArgsConstructor
@Slf4j
public class FeignClientController {
    private final FeignClientService feignClientService;

    @GetMapping("/feign")
    public ResponseEntity<Result> feignClientTest(
            @RequestHeader(HttpHeaders.AUTHORIZATION) String header,
            @RequestParam(name = "name", required = false) String name,
            @RequestParam(name = "age", required = false) Integer age,
            @RequestParam(name = "status", required = false) Integer status) {
        log.info("feign 호출");
        Result info = feignClientService.getInfo(header, name, age, status);
        return ResponseEntity.ok(info);
    }
}

 

 

2. 외부 api 환경 세팅 (8282 포트)

받은 요청을 바로 body로 돌려주는 세팅 + 애러 발생 테스트를 위한 HttpStatus를 파라미터 세팅

@Slf4j
@RestController
public class TestController {

    @GetMapping("/test")
    public ResponseEntity<Result> getInfo(HttpServletRequest request , @RequestParam(name = "status", required = false, defaultValue = "200") int status) {
        Result result = new Result();
        request.getHeaderNames().asIterator().forEachRemaining((i) -> result.headerResult.add(i + " : " + request.getHeader(i)));
        request.getParameterNames().asIterator().forEachRemaining((i) -> result.parameterResult.add(i + " : " + request.getParameter(i)));
        HttpStatus httpStatus = HttpStatus.valueOf(status);
        return ResponseEntity.status(httpStatus).body(result);
    }
    @Data
    static class Result {
        List<String> headerResult = new ArrayList<>();
        List<String> parameterResult = new ArrayList<>();
        String body;
    }
}

 

요청 및 응답

8080 포트의 애플리케이션을 통해 8282의 응답을 받아온 결과를 볼 수 있음

잘못 된 사용 예

서로 다른 도메인의 api를 호출할 때 아래와 같이 하나의 인터페이스에 몰아 넣고 EndPoint를 합치려 하였으나 애러가 발생했다. 

@FeignClient(name = "google-oauth2-client", url = "https://")
public interface GoogleOauth2Client {
    @PostMapping("oauth2.googleapis.com/token")
    OAuthTokenResponse getToken(@RequestParam(name = "grant_type") String grantType,
                                @RequestParam(value = "client_id") String clientId,
                                @RequestParam(value = "client_secret") String clientSecret,
                                @RequestParam(value = "redirect_uri") String redirectUri,
                                @RequestParam(value = "code") String code);
    
    @GetMapping(value = "www.googleapis.com/oauth2/v2/userinfo")
    GoogleUserInfo getUserInfo(@RequestHeader(value = HttpHeaders.AUTHORIZATION) String token);

}

 

수정

때문에 아래와 같이 도메인별로 인터페이스를 나누었다.

@FeignClient(name = "google-oauth2-token-client", url = "https://oauth2.googleapis.com")
public interface GoogleOauth2TokenClient {
    @PostMapping("/token")
    OAuthTokenResponse getToken(@RequestParam(name = "grant_type") String grantType,
                                @RequestParam(value = "client_id") String clientId,
                                @RequestParam(value = "client_secret") String clientSecret,
                                @RequestParam(value = "redirect_uri") String redirectUri,
                                @RequestParam(value = "code") String code);

}
@FeignClient(name = "google-oauth2-info-client", url = "https://www.googleapis.com")
public interface GoogleOauth2InfoClient {
    @GetMapping(value = "/oauth2/v2/userinfo")
    GoogleUserInfo getUserInfo(@RequestHeader(value = HttpHeaders.AUTHORIZATION) String token);
}

 

다음 포스팅에서는 해당 기술을 사용하였을 때 전역, 부분적으로 header등을 컨트롤 하는 방법과 외부 api 호출한 후 애러핸들링 (외부 api 요청이 실패하면 500 서버 애러로 나옴) 을 다룰 예정이다.