Spring Feign

김기현·2022년 7월 27일
0
post-thumbnail

Spring Feign

Spring Feign 소개

Feign Client는 Web Service 클라이언트를 보다 쉽게 작성할 수 있도록 돕는 라이브러리입니다. Feign은 Netflix에서 개발된 Http Client Binder로 이를 사용하면 웹 서비스 클라이언트를 보다 쉽게 작성할 수 있습니다. Feign을 사용하기 위해서는 Interface를 작성하고 annotation을 선언하기만 하면 되는데요. 마치 Spring Data JPA에서 실제 쿼리를 작성하지 않고 Interface만 지정하여 쿼리실행 구현체를 자동으로 만드는 것과 유사합니다. Interface를 작성하고 바로 사용하기 때문에 코드 복잡도가 낮아지고, 서비스 간 통신을 원활하게 할 수 있다는 장점이 있습니다.

요약하자면 Spring Feign은 선언적 방식을 띄고 있고, 인터페이스를 통해 클라이언트 측 프로그램을 작성하고, Spring이 런타임에 구현체를 제공한다입니다.

How Feign Work?

Architecture
1. 브라우저에서 http://localhost:8080으로 GET 요청을 보냅니다.
2. Feign Client가 /testfiegn에 GET 요청을 보냅니다.
3. Controller가 결과값을 브라우저에 반환합니다.

Spring Boot 프로젝트에서 Lombok, OpenFeign 등을 추가하였습니다.
구조는 아래와 같습니다. 깃허브 소스코드 링크입니다.

Dependency

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

Application.java
Feign Client를 사용하기 위해 annotation을 붙여줍니다. 이 때 Root Package에 있어야 하고, 그렇지 않은 경우 basePackages 또는 basePackageClasses를 지정해야 합니다. @EnableFeignClients는 지정된 package를 돌아다니면서 @FeignClient를 찾아 구현체를 만들어줍니다.

// FeignTestApplication.java 

package dev.be.feigntest;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableFeignClients
@SpringBootApplication
public class FeignTestApplication {

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

}

Client
Client를 Interface로 작성합니다. @FeignClient를 사용하면 Interface를 클래스로 구체화할 필요가 없으며 annotation이 대신 이를 해줍니다. annotaion을 통해 요청할 URL을 넣어주고, 메서드 위의 annotaion을 통해 세부 URL을 입력해줍니다. 만약 default 설정을 Override하고 싶다면 Configuration에 설정값을 넣으면 됩니다.

@FeignClient annotation은 아래에 자세히 다루었습니다.

// TestClient.java
package dev.be.feigntest;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(name = "feign", url = "http://localhost:8081")
public interface TestClient {
    @GetMapping("/testfeign")
    String testFeign();
}

Service
Feign 클라이언트를 사용하는 Service입니다.

// TestService.java 

package dev.be.feigntest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class TestService {
    @Autowired
    TestClient testClient;

    public String TestFeign(){
        return testClient.testFeign();
    }
}

Controller

package dev.be.feigntest;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

	// 작성한 테스트서비스 Bean주입
    private final TestService testService;

	// 서비스를 사용하기 위한 생성자 제작
    public TestController(TestService testService){
        this.testService = testService;
    }
	
    // '/'에 접근 후 Feign Client가 '/testfeign'으로 get 호출
    // 반환값을 받고 메인에서 보여줌
    @GetMapping("/")
    public String main(){
        return  testService.TestFeign(); // testFeign
    }

	// Feign Client 요청에 응답을 주기 위한 Controller
    @GetMapping("/testfeign")
    public String testFeign(){
        return "Test Feign Client";
    }

}

Use Feign Client

Use Feign Client
스프링부트 프로젝트를 실행하고 http://localhost:8081/ (application.properties default값이 8080포트이나 Nginx으로 인한 port 변경) 으로 접근하면 다음과 같이 바인딩되어 결과가 나타납니다

Header에 값 설정하기

Request Header에 값을 넣는 방식은 3가지가 있습니다.

  • Annotation 선언
@FeignClient(value = "step2", url = "https, configuration = {HeaderConfiguration.class})
public interface Step2Client {
    ...
}
  • Configuration 이용

Header 값을 설정할 configuration class 를 생성을 하고, RequestInterceptor 을 생성해줍니다.

public class HeaderConfiguration {
@Bean
    public RequestInterceptor requestInterceptor() {
        return requestTemplate -> requestTemplate.header("header", "header1", "header2");
    }
}
  • headers 선언
@GetMapping(value = "/status/", headers = "key2=value2")
void status2(@PathVariable("status") int status);

@GetMapping(value = "/status/")
void status3(@RequestHeader("key3") String headers, @PathVariable("status") int status);

// 이 호출은 Header 에 값이 설정되지 않습니다.
// @GetMapping 은 SpringMVcContract 를 사용해야하고, @Headers 는 feign Contract 를 사용해야 합니다.
@org.springframework.web.bind.annotation.GetMapping(value = "/status/")
@feign.Headers("key3: value3")
void status4(@PathVariable("status") int status);

Feign Log Level

loggerLevel : NONE, BASIC, HEADER, FULL을 지정할 수 있습니다.
해당 값들은 다음을 의미합니다.

  • NONE : default, 로그를 남기지 않음
  • BASIC : Request Method, URL과 응답 코드, 실행 시간을 남김
  • HEADERS : BASIC의 정보를 포함하여, 요청, 응답의 헤더를 남김
  • FULL : 요청, 응답의 header, body, metadata를 남김
# application.yml

feign:
  client:
    config: 
      feignName: # @FeignClient에서 name 값, 전역으로 설정하려면 default
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: full
        errorDecoder: com.example.SimpleErrorDecoder
        retryer: com.example.SimpleRetryer
        requestInterceptors:
          - com.example.FooRequestInterceptor
          - com.example.BarRequestInterceptor
        decode404: false
        encoder: com.example.SimpleEncoder
        decoder: com.example.SimpleDecoder
        contract: com.example.SimpleContract

@FeignClient

@FeignClient(name = "feign", url = "http://localhost:8081")
public interface TestClient {
	.
    .
    .
}
  • name : 서비스ID 혹은 논리적인 이름, spring-cloud의 eureka, ribbon에 사용합니다.
  • url : 실제 호출할 서비스의 URL, eureka, ribbon을 사용하지 않고서도 동작합니다.
  • decode404 : 404응답이 올 때 FeignExeption을 발생시킬지, 아니면 응답을 decode할 지 여부입니다.
  • configuration : feign configuration class를 지정합니다.
  • fallback : hystrix fallback class를 지정합니다.
  • fallbackFactory : hystrix fallbak factory를 지정합니다.
profile
피자, 코드, 커피를 사랑하는 피코커

0개의 댓글