본문 바로가기
개발/SpringBoot

SpringBoot OpenFeign(FeignClient) 사용하기

by 궁즉변 변즉통 통즉구 2023. 5. 31.
반응형

Spring5.0 버전부터 그동안 Spring에서 HTTP Client로 많이 사용되면 RestTemplate이 유지 모드로 변경이 되었다고 한다. 이에 WebClient 사용을 Spring에서 권고하고 있지만 대안으로 간단하게 적용 가능한 OpenFeign 사용법을 알아본다.

OpenFeign은  Spring Cloud 처음에는 Spring Cloud Netflix Feign 였으나 Spring Cloud OpenFeign에 통합되면서 SpringMVC 어노테이션 및 HttpMessageConverters를 사용할 수 있게 되었다. OpenFeign은 HTTP API 클라이언트를 단순화하는 것을 목표로 하고 있고 실제 RestTemplate와 비교해봤을 때도 사용하기 간편하다.

 

먼저 OpenFeign Gradle 의존성을 추가한다.

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

 

다음으로 JavaConfig설정에 @EnableFeignClients 어노테이션을 추가한다. 필자는 간단하게 Main 클래스에 추가했다.

@EnableFeignClients
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
    	SpringApplication application = new SpringApplication(MyApplication.class);
    	application.run(args);
    }
}

 

application.yml 파일에 환경설정으로 관리할 feign 관련 정보를 작성한다. 예를들면 아래와 같이 서비스 호출 URL 등이 될 수 있다.

feign:
  svc1:
    name: svc1
    url: http://svc1.com/api

 

Feign JavaConfig를 작성한다. 해당 Config가 적용되는 FeignClient에 공통으로 적용할 Config를 설정한다. 먼저 공통으로 사용하는 header를 추가하기 위해서는 다음과 같이 RequestInterceptor를 통해 설정이 가능하다. 다음으로 Log설정부분이 있는데 Feign의 디테일한 디버깅을 위해서 아래와 같이 FULL 레벨로 설정을 한다. 설정가능한 로그 레벨은 다음과 같다.

 - NONE : 로깅하지 않음(DEFAULT) 

 - BASIC : Request Method와 URL, Reponse 상태 코드 및 실행 시간을 로깅한다.

 - HEADERS : Request, Response Header + BASIC 정보를 로깅한다.

 - FULL : Request, Response의 Header, Body, 메타데이터를 로깅합니다.

public class Svc1FeignConfig {

    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL; // log레벨 설정
    }

    @Bean
    public RequestInterceptor requestInterceptor() {
        return requestTemplate -> {
            requestTemplate.header("Content-Type", "application/json");
            requestTemplate.header("Accept", "application/json");
        };
    }
}

추가적으로 위에서 @Configuration 어노테이션을 적용하지 않았는데, Feign에 사용할 JavaConfig에 해당 어노테이션을 설정하게 되면 Bean으로 등록되고, 모든 FeignClient에서 해당 설정이 적용된다. 따라서 모든 FeignClient에 공통적으로 적용되어야 하는 설정은 @Configuration으로 적용하고, 각각의 FeignClient에 적용되어야 하는 설정은 @Configuration 어노테이션을 설정하지 않고 인터페이스의 configuration 설정을 통해 추가해준다.

 

이제 Feign Client 인터페이스를 작성한다. Feign Client는 구현체 없이 인터페이스만 작성하면 되고 Spring MVC의 어노테이션이 사용가능하다.

@FeignClient(name="${feign.svc1.name}", url="${feign.svc1.url}", configuration = Svc1FeignConfig.class)
public interface Svc1FeignClient {

    @GetMapping(value="/objects")
    ObjectListDto getSvcList(@RequestParam(value="srch") String srch);

    @PostMapping(value="/objects")
    ObjectDto createSvc(@RequestBody ObjectReqDto objectReqDto);
    
    @PutMapping(value="/objects/{objId}")
    Response putSvc(@PathVariable(value="objId") String objId, 
                    @RequestBody ObjectReqDto objectReqDto);


}

 

이제 Service Layer에서 FeignClient를 호출해서 사용하면 된다.

@Service
@Slf4j
public class MyService{

	@Autowired
	private Svc1FeignClient svc1FeignClient;

	@Autowired
	private ObjectMapper objectMapper;
	...
    
	// FeignClient 호출 샘플 메소드
	private void callApi() {
            // ResponseBody를 Custom객체로 직접 바인딩
            ObjectListDto objectList = svc1FeignClient.getSvcList(srch);

            ...

            // 객체를 RequestBody로 전달
            svc1FeignClient.createSvc(objectDto);

            ...

            // Feign에서 정의한 Response 객체로 바인딩
            Response response = svc1FeignClient.putSvc(objId, objectReqDto);
            log.debug("{}", response);
            log.debug("{}", response.status()); // 상태확인
            log.debug("{}", response.body()); // response body 확인

            // Response를 Sring타입으로 변환 샘플(NoSQL등에 API Reponse를 Text형태로 저장할 때)
            String textBody = extractContent(response); 
            ...
            // objectMapper를 활용해서 String Body를 객체로 변환 샘플
            ObjectDto objDto = objectMapper.readValue(textBody, ObjectDto.class);
	}
    
    
	// Feign Response객체 => String 변환
	private String extractContent(Response response) {
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            try{
                StreamUtils.copy(response.body().asInputStream(), output);
            } catch (FileNotFoundException ex) {
                log.debug("ExtractContent error:", ex);
                return null;
            } catch (Exception ex) {
                log.error("ExtractContent error:", ex);
                return null;
            }
            return new String(output.toByteArray());
	}
}

FeignClient를 사용해서 인터페이스만으로 좀 더 명시적이고 간결하게 HTTP Client가 작성이 가능하다.

 

반응형

댓글