일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- elasticsearch
- AWS
- ReactJS
- jsp
- tool
- javascript
- jenkins
- Spring Boot
- IntelliJ
- springboot
- 맛집
- java
- redis
- it
- Git
- laravel
- 요리
- Oracle
- Gradle
- db
- php
- Spring
- Design Patterns
- Web Server
- Spring Batch
- JVM
- devops
- MySQL
- linux
- ubuntu
- Today
- Total
아무거나
Spring Boot 에서 FeignClient 를 사용하여 Rest API 를 간편하게 사용하자 본문
Feign 이란
Feign
이란 Netflix 에서 개발된 오픈소스이며 선언적 방식으로 REST 기반 호출을 추상화해서 제공한다. 즉, interface 와 annotaion 만으로 간편하게 HTTP API Client 를 구현할 수 있으며 RestTemplate 을 만들 필요없이 사용할 수 있어 코드의 복잡성이 낮아진다.
샘플코드는 하기 링크로 확인
사용법
Dependency
[build.gradle]
...
// https://spring.io/projects/spring-cloud 에서 Spring Boot 버전에 맞게 세팅한다. (해당 포스팅은 Spring Boot 2.2.2.RELEASE 로 진행)
ext {
set('springCloudVersion', "Hoxton.SR3")
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
dependencies {
...
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
}
SpringApplication.java 에 @EnableFeignClients 을 선언
[ApiApplication.java]
@EnableFeignClients
@SpringBootApplication
...
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}
Feign 사용
먼저 패키지를 생성하자. 패키지는 ApiApplication.java 와 같은 레벨인 Root Package 에 존재하여야 하며, 그렇지 않으면 basePackages 또는 basePackageClasses 를 지정해야 한다. feign 이라는 패키지를 생성하고 그 안에 user 패키지 생성 후 또 그 안에 FeignUserClient.java 라는 클래스를 생성하자. (혹시 수동으로 구현체를 생성하고 싶으면 수동 구현체 생성 링크를 참고)
[../feign/user/FeignUserClient.java]
package com.example.bkjeon.base.feign.user;
import java.util.List;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import com.example.bkjeon.base.feign.user.model.FeignUserRequest;
@FeignClient(
configuration = FeignUserClientConfig.class,
fallbackFactory = FeignUserClientFallback.class,
name = "example",
url = "${external-api.url}"
)
public interface FeignUserClient {
@GetMapping("users")
List<FeignUser> getUserList(@SpringQueryMap FeignUserRequest feignUserRequest);
@GetMapping("users/{userId}")
FeignUser getUser(@PathVariable("userId") Integer userId, @RequestParam("testCode") String testCode);
}
API 를 호출할 도메인 및 토큰 입력 [application.yml]
...
external:
api:
user:
url: https://jsonplaceholder.typicode.com
token: eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ7XCJwb3NpdERlcHRJZFwiOjMsXCJkbW5DZFwiOlwiRUxUXCIsXCJhcGlrZXlJZFwiOlwiRUxUXzE1NTY1MjM
결과값을 받을 클래스를 생성한다.
[../feign/user/entity/FeignUser.java]
package com.example.bkjeon.base.feign.user.entity;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
public class FeignUser {
private Integer id;
private String name;
private String username;
private String email;
private Address address;
private String phone;
private String website;
private Company company;
@Getter
public static class Address {
private String street;
private String suite;
private String city;
private String zipcode;
private Geo geo;
@Getter
public static class Geo {
private String lat;
private String lng;
}
}
@Getter
public static class Company {
private String name;
private String catchPhrase;
private String bs;
}
}
요청시 필요한 파라미터 클래스를 생성한다.
[../feign/user/FeignUserRequest.java]
package com.example.bkjeon.base.feign.user.model;
import lombok.Builder;
import lombok.ToString;
@ToString
public class FeignUserRequest {
private String testCode;
@Builder
public FeignUserRequest(String testCode) {
this.testCode = testCode;
}
}
실패할 경우 예외처리할 클래스를 생성한다.
[../feign/user/FeignUserClientFallback.java]
package com.example.bkjeon.base.feign.user;
import java.util.List;
import org.springframework.stereotype.Component;
import com.example.bkjeon.base.feign.user.model.FeignUserRequest;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
/**
* Exception
*/
@Component
@Slf4j
public class FeignUserClientFallback implements FallbackFactory<FeignUserClient> {
@Override
public FeignUserClient create(Throwable throwable) {
return new FeignUserClient() {
@Override
public List<FeignUser> getUserList(FeignUserRequest feignUserRequest) {
log.error("param: {}, error ==> {}", feignUserRequest.toString(), throwable.toString());
return null;
}
@Override
public FeignUser getUser(Integer userId, String testCode) {
log.error("param: userId ==> {}, testCode ==> {}, error ==> {}", userId, testCode, throwable.toString());
return new FeignUser();
}
};
}
}
API 요청시 헤더에 세팅할 인터셉터를 구현한다.
[../feign/user/FeignClientInterceptor.java]
package com.example.bkjeon.base.feign.user;
import com.google.common.base.Preconditions;
import feign.RequestInterceptor;
import feign.RequestTemplate;
/**
* Header Setting Interceptor
*/
public class FeignClientInterceptor implements RequestInterceptor {
private String apiToken;
public FeignClientInterceptor(String apiToken) {
Preconditions.checkNotNull(apiToken, "Api Token Should Not Be Null");
this.apiToken = apiToken;
}
@Override
public void apply(RequestTemplate template) {
template.header("Authorization", "Bearer " + apiToken);
template.header("Content-Type", "application/json");
}
}
Header 값 등 세팅할 Configuration 을 생성
[../feign/user/FeignUserClientConfig.java]
package com.example.bkjeon.base.feign.user;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import feign.RequestInterceptor;
/**
* Feign Configuration
*/
public class FeignUserClientConfig {
@Bean
public RequestInterceptor requestHeader(@Value("${external-api.token}") String apiToken) {
return new FeignUserClientInterceptor(apiToken);
}
}
Feign 을 사용할 Controller 생성
[FeignExampleController.java]
package com.example.bkjeon.base.services.api.v1.feign;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.bkjeon.base.feign.user.FeignUserClient;
import com.example.bkjeon.base.feign.user.entity.FeignUser;
import com.example.bkjeon.base.feign.user.model.FeignUserRequest;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
@RequestMapping("v1/feign")
public class FeignExampleController {
private final FeignUserClient feignUserClient;
@ApiOperation("Feign 을 통한 User 목록 조회")
@GetMapping("users")
public List<FeignUser> getUserList() {
FeignUserRequest feignUserRequest = FeignUserRequest.builder().testCode("bkjeon").build();
return feignUserClient.getUserList(feignUserRequest);
}
@ApiOperation("Feign 을 통한 User 상세 조회")
@GetMapping("users/{userId}")
public FeignUser getUser(@PathVariable Integer userId) {
return feignUserClient.getUser(userId, "bkjeon");
}
}
결과확인
http://localhost:9090/api/v1/feign/users
또는
http://localhost:9090/api/v1/feign/users/1
Feign 패키지 구조
'Java & Kotlin > Spring' 카테고리의 다른 글
Spring Boot + Gradle 을 활용한 정적 코드 분석 도구 Spotbugs 적용 (0) | 2023.03.27 |
---|---|
SpringBoot + Zipkin 을 활용한 트레이스 환경 구성 (0) | 2023.01.09 |
[Spring boot] MDC 를 활용한 Logback 에 Request Payload 항목 추가하여 로그 세분화 (0) | 2022.11.16 |
Spring Batch 5편 - Scope 와 Job Parameter (0) | 2022.11.02 |
SpEL(Spring Expression Language) (0) | 2022.10.11 |