본문 바로가기
BackEnd/Project

[RealPJ] Ch02. 실무 스타일로 Feign Client 사용해보기 - 기본 설정

by 개발 Blog 2024. 8. 30.

공부 내용을 정리하고 앞으로의 학습에 이해를 돕기 위해 작성합니다.

 

Feign Client

Feign Feature

Connection/Read Timeout

  • 외부 서버와 통신할 때, 연결 타임아웃(connectTimeout)과 읽기 타임아웃(readTimeout)을 설정할 수 있다.

Feign Interceptor

  • 외부로 요청이 나가기 전에 공통적으로 처리해야 하는 부분이 있다면, RequestInterceptor를 재정의하여 처리할 수 있다.
  • 예를 들어, 모든 요청에 특정 헤더를 추가하거나 로그를 남기는 등의 작업을 수행할 수 있다.

Feign CustomLogger

 

  • CustomLogger를 사용하면 Feign 클라이언트에서 요청과 응답에 대한 로그를 남길 수 있다.
  • 특정 조건에 따라 로그를 남기거나, 특정 응답 시간 이상인 경우 경고를 출력하는 등의 설정이 가능하다.

Feign ErrorDecoder

 

  • 요청에 대한 응답이 정상적이지 않은 경우, ErrorDecoder를 사용하여 예외를 처리할 수 있다.
  • 응답 코드에 따라 예외를 던지거나, 로그를 출력할 수 있다.

실습

프로젝트 설정

 

1. 의존성 추가

ext {
    set('springCloudVersion', '2023.0.3')
}

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

// Feign
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
  • 프로젝트에서 Feign 클라이언트를 사용하기 위해 spring-cloud-starter-openfeign 의존성을 추가한다.
  • Spring Cloud의 최신 버전과 함께 필요한 의존성을 관리할 수 있도록 spring-cloud-dependencies를 가져온다.

2. Feign 클라이언트 활성화

package dev.be.feign;

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

@EnableFeignClients
@SpringBootApplication
public class FeignApplication {

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

}
  • @EnableFeignClients 애노테이션을 메인 애플리케이션 클래스에 추가하여 Feign 클라이언트를 활성화한다.
  • 이렇게 설정하면 Feign 클라이언트를 사용할 준비가 완료된다.

패키지 구조 및 역할

1. 패키지 구성

  • config: Feign 관련 설정 파일을 포함하며, Feign 클라이언트의 연결 타임아웃, 읽기 타임아웃, 로거 레벨 등의 설정을 관리한다.
  • controller: REST API 요청을 처리하는 컨트롤러 클래스를 포함한다.
  • service: 비즈니스 로직을 처리하는 서비스 클래스를 포함한다.
  • feign: Feign 클라이언트 인터페이스 및 관련 설정 파일을 포함한다.
  • common: DTO(Data Transfer Object) 클래스들을 포함하여 데이터를 전송할 때 사용하는 객체들을 정의한다.

2. Feign 설정

feign:
  url:
    prefix: http://localhost:8080/target_server # DemoFeignClient?? ??? url prefix ?
  client:
    config:
      default:
        connectTimeout: 1000
        readTimeout: 3000
        loggerLevel: NONE
      demo-client: # DemoFeignClient?? ??? Client ?? ?
        connectTimeout: 1000
        readTimeout: 10000
        loggerLevel: HEADERS # ??? ??? ?? FeignCustomLogger -> Logger.Level logLevel ??? ???

#  [loggerLevel ??]
#      ref : feign.Logger.Level
#  ```
#  NONE, // No logging.
#  BASIC, // Log only the request method and URL and the response status code and execution time.
#  HEADERS, // Log the basic information along with request and response headers.
#  FULL // Log the headers, body, and metadata for both requests and responses.
# ```
  • application.yml 파일에서 Feign 클라이언트의 기본 URL, 타임아웃, 로깅 레벨 등을 설정한다.
  • 기본 설정 외에도 특정 클라이언트(demo-client)에 대해 별도의 설정을 적용할 수 있다.
package dev.be.feign.feign.config;

import org.springframework.context.annotation.Configuration;

@Configuration
public class DemoFeignConfig {
}
  • DemoFeignConfig: 특정 Feign 클라이언트(demo-client)에 대한 설정을 포함하는 클래스이다. 이 클래스에서 클라이언트의 타임아웃, 로깅 레벨 등을 설정한다.
  • 이 설정을 통해 Feign 클라이언트의 동작 방식을 세부적으로 조정할 수 있다.

3. BaseRequestsInfo & BaseResponseInfo 클래스

package dev.be.feign.common.dto;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(Include.NON_NULL)
public class BaseRequestsInfo {
    private String name;
    private Long age;
    private String header;
}
  • BaseRequestsInfo: 요청 데이터를 담는 DTO 클래스이다. 클라이언트가 서버로 보내는 이름, 나이, 헤더 정보를 포함한다.
package dev.be.feign.common.dto;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(Include.NON_NULL)
public class BaseResponseInfo {
    private String name;
    private Long age;
    private String header;
}
  • BaseResponseInfo: 응답 데이터를 담는 DTO 클래스이다. 서버에서 클라이언트로 반환하는 이름, 나이, 헤더 정보를 포함한다.

이 두 클래스는 요청과 응답 데이터를 캡슐화하여 코드의 가독성과 유지보수성을 높인다.

Feign 클라이언트 구현

Feign Client 인터페이스

package dev.be.feign.feign.client;

import dev.be.feign.common.dto.BaseRequestsInfo;
import dev.be.feign.feign.config.DemoFeignConfig;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(
        name = "demo-client",
        url = "${feign.url.prefix}",
        configuration = DemoFeignConfig.class
)
public interface DemoFeignClient {
    @GetMapping("/get")
    ResponseEntity<BaseRequestsInfo> callGet(@RequestHeader("CustomHeaderName") String customHeader,
                                             @RequestParam("name") String name,
                                             @RequestParam("age") Long age);
    }
  • @FeignClient 애노테이션을 사용하여 외부 API를 호출할 Feign 클라이언트를 정의한다.
  • 이 인터페이스에는 호출할 API의 엔드포인트와 필요한 요청 파라미터, 헤더 등을 정의한다.

서비스 클래스

package dev.be.feign.service;

import dev.be.feign.common.dto.BaseRequestsInfo;
import dev.be.feign.feign.client.DemoFeignClient;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class DemoService {

    private final DemoFeignClient demoFeignClient;

    public String get() {
        ResponseEntity<BaseRequestsInfo> response = demoFeignClient.callGet("CustomHeader", "CustomName", 1L);
        System.out.println("Name : " + response.getBody().getName());
        System.out.println("Age : " + response.getBody().getAge());
        System.out.println("Header : " + response.getBody().getHeader());
        return "get";
    }
}
  • Feign 클라이언트를 주입받아 실제로 외부 API를 호출하고 그 결과를 처리하는 비즈니스 로직을 구현한다.
  • 외부 API 호출 결과를 받아 필요한 정보들을 콘솔에 출력하고, 컨트롤러로 반환한다.

컨트롤러 클래스

package dev.be.feign.controller;

import dev.be.feign.service.DemoService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RequiredArgsConstructor
@RestController
public class DemoController {

    private final DemoService demoService;

    @GetMapping("/get")
    public String getController() {
        return demoService.get();
    }
}
  • 특정 엔드포인트(/get)를 정의하여 클라이언트 요청을 받아 서비스 클래스에서 정의한 비즈니스 로직을 실행하고 결과를 반환한다.

TargetController 클래스

package dev.be.feign.controller;

import dev.be.feign.common.dto.BaseResponseInfo;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/target_server")
public class TargetController {

    @GetMapping("/get")
    public BaseResponseInfo demoGet(@RequestHeader("CustomHeaderName") String header,
                                    @RequestParam("name") String name,
                                    @RequestParam("age") Long age) {
        return BaseResponseInfo.builder()
                .header(header)
                .name(name)
                .age(age)
                .build();
    }
}
  • 실제 외부 API 역할을 하는 컨트롤러로, Feign 클라이언트가 호출하는 엔드포인트를 정의한다.
  • 요청받은 데이터를 바탕으로 응답을 생성하여 반환한다.

실행 결과

  • Feign 클라이언트를 사용하여 외부 API에 대한 요청을 성공적으로 보내고, 그 결과를 처리하는 것을 확인할 수 있었다.
  • 응답 데이터가 의도한 대로 잘 파싱 되어 출력되었고, 클라이언트 요청에 대해 정확한 응답을 반환할 수 있었다.

이 실습을 통해 Feign 클라이언트를 사용한 외부 API 통신을 실무에서 어떻게 활용할 수 있는지에 대해 학습할 수 있었다. Feign을 사용하면 RESTful API 호출이 더욱 간단하고 선언적으로 이루어지며, 다양한 설정을 통해 유연하게 사용할 수 있다.