외부 API 호출 시 FeignClient 사용

joona95·2024년 8월 21일

문제 상황

public class HttpUtil {

    public static JSONObject getHTTP(String apiURL, Map<String, String> requestHeaders) throws IOException, ParseException {

        URL url = new URL(apiURL);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        connection.setRequestMethod("GET");
        for (Map.Entry<String, String> rqheader : requestHeaders.entrySet()) {
            connection.setRequestProperty(rqheader.getKey(), rqheader.getValue());
        }

        return getHTTPResponse(connection);
    }

    public static JSONObject postHTTP(String apiURL, String request) throws IOException, ParseException {

        URL url = new URL(apiURL);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        connection.setRequestMethod("POST");
        connection.setDoOutput(true);
        connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");

        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream()));
        bw.write(request);
        bw.flush();
        bw.close();

        return getHTTPResponse(connection);
    }

    private static JSONObject getHTTPResponse(HttpURLConnection connection) throws IOException, ParseException {

        InputStreamReader streamReader;
        if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { // 정상 호출
            streamReader = new InputStreamReader(connection.getInputStream());
        } else { // 에러 발생
            streamReader = new InputStreamReader(connection.getErrorStream());
        }

        BufferedReader lineReader = new BufferedReader(streamReader);
        StringBuilder responseBody = new StringBuilder();

        String line;
        while ((line = lineReader.readLine()) != null) {
            responseBody.append(line);
        }

        String body = responseBody.toString();

        connection.disconnect();

        return (JSONObject) new JSONParser().parse(body);
    }
}

레시피 조회 시 네이버 검색과 유튜브 검색 기능이 필요하여 외부 API를 호출하고 있다.

또한, SNS 로그인을 위해 네이버/카카오/구글 회원정보 조회 API를 호출하고 있다.

이런 외부 API 호출을 위해서 HttpUtil 클래스를 작성하여 활용하고 있었다.

FeignClient 라이브러리를 활용하면 외부 API 호출 시 코드를 좀 더 쉽고 깔끔하게 작성할 수 있었다.

해결 방안

1. 의존성 추가

implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.1.0'

2. FeignClient 사용 가능 설정 추가

@EnableFeignClients
@SpringBootApplication
public class AppApplication extends SpringBootServletInitializer {

...

}
  • @EnableFeignClients 어노테이션을 springboot application 실행 클래스에 추가

3. FeignClient 적용한 인터페이스 추가

@FeignClient(name = "google-client", url = "https://oauth2.googleapis.com")
public interface GoogleOAuthFeignClient {

    @PostMapping("/token")
    GoogleAccessTokenResponse getAccessToken(@RequestParam(name = "grant_type") String grantType,
                                             @RequestParam(name = "client_id") String clientId,
                                             @RequestParam(name = "client_secret") String clientSecret,
                                             @RequestParam(name = "redirect_uri") String redirectUri,
                                             @RequestParam(name = "code") String code);

    @GetMapping("/tokeninfo")
    GoogleAuthResponse getAuthInfo(@RequestParam(name = "id_token") String accessToken);
}
@FeignClient(name = "kakao-client", url = "https://kapi.kakao.com")
public interface KakaoFeignClient {

    @GetMapping("/v2/user/me")
    KakaoAuthResponse getAuthInfo(@RequestHeader("Authorization") String authorization);
}
@FeignClient(name = "kakao-oauth-client", url = "https://kauth.kakao.com/oauth")
public interface KakaoOAuthFeignClient {

    @PostMapping(value = "/token", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    KakaoAccessTokenResponse getAccessToken(@RequestParam(name = "grant_type") String grantType,
                                            @RequestParam(name = "client_id") String clientId,
                                            @RequestParam(name = "redirect_uri") String redirectUri,
                                            @RequestParam(name = "code") String code);
}
@FeignClient(name = "naver-client", url = "https://openapi.naver.com")
public interface NaverFeignClient {

    @GetMapping("/v1/search/blog")
    NaverBlogSearchResponse searchNaverBlog(
            @RequestHeader("X-Naver-Client-Id") String clientId,
            @RequestHeader("X-Naver-Client-Secret") String clientSecret,
            @RequestParam(value = "start") int start,
            @RequestParam(value = "display") int display,
            @RequestParam(value = "sort") String sort,
            @RequestParam(value = "query") String query);

    @GetMapping("/v1/nid/me")
    NaverAuthResponse getAuthInfo(@RequestHeader("Authorization") String authorization);
}
@FeignClient(name = "naver-oauth-client", url = "https://nid.naver.com/oauth2.0")
public interface NaverOAuthFeignClient {

    @GetMapping("/token")
    NaverAccessTokenResponse getAccessToken(
            @RequestParam(value = "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,
            @RequestParam(value = "state") String state
    );
}
  • @FeignClient(name = "이름", url = "외부 API 호출 URL") 설정하여 특정 URL 호출을 위한 FeignClient 적용
  • 응답 DTO 는 해당 API에서 전달주는 값에 맞춰서 클래스 생성하여 적용하면 자동으로 값을 매칭
  • 요청 파라미터나 헤더는 값을 통해 전달하면 됨

4. 테스트 시 FeignClient 적용 오류 발생

@DataJpaTest
@ImportAutoConfiguration(classes = FeignAutoConfiguration.class)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@TestPropertySource(locations = "classpath:application-test.yml")
class IngredientCustomRepositoryTest extends Specification {

...

}
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.cloud.netflix.feign.FeignContext' available
  • DataJpaTest 에서 FeignContext 빈을 찾지 못한다는 오류가 발생
  • @ImportAutoConfiguration(classes = FeignAutoConfiguration.class) 을 추가하면 해결됨
  • SpringBoot Application 에 @EnableFeignClients 어노테이션을 적용하여 생기는 문제라는 것 같음

0개의 댓글