FeignClient는 MSA 환경에서 마이크로 서비스 간 동기적 통신을 지원하는 선언적 웹서비스 클라이언트이다. RestAPI를 사용하기 위한 추상화된 인터페이스를 제공하며, 인터페이스에는 호출하려는 타겟 메서드의 정보를 담아서 생성한다. 어노테이션을 정의하는 것만으로 구현이 가능하다는 장점이 있다.
참고사항
Kubernetes Ingress를 통해 서비스 간 라우팅을 진행하는 환경에서 OpenFeign을 사용하였다.
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
@EnableFeignClients
@ImportAutoConfiguration({FeignConfig.class})
@SpringBootApplication
public class Lucky7PostServiceApplication {
public static void main(String[] args) {
SpringApplication.run(Lucky7PostServiceApplication.class, args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
쿠버네티스 환경만 그런 건지는 모르겠지만, @ImportAutoConfiguration({FeignConfig.class})
를 통해 Config 파일을 주입해주지 않으면 에러가 발생하였다. 일반적인 환경에서는 저걸 쓰지 않아도 동작했으니 참고하면 좋을 거 같다.
@FeignClient(name = "auth-service", configuration = FeignConfig.class)
public interface AuthServiceClient {
@PostMapping("/auth/token/server-validation")
ResponseEntity<Long> validateToken();
}
@FeignClient
@PostMapping
, @GetMapping
, @PatchMapping
, @DeleteMapping
등은 기존 RestAPI와 동일하게 구현하면 된다.@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class PostLikeService {
private final MemberQueryRepository memberQueryRepository;
private final BlogQueryRepository blogQueryRepository;
private final AuthServiceClient authServiceClient;
// 관련 없는 코드 생략
/* 멤버, 블로그 예외 처리 */
public Long userAndBlogValidation() throws BaseException {
// OpenFeign 인터페이스 사용
ResponseEntity<Long> response = authServiceClient.validateToken();
Long memberId = response.getBody();
// 멤버 예외 처리
memberQueryRepository.findByIdAndState(memberId, State.ACTIVE)
.orElseThrow(() -> new BaseException(BaseResponseStatus.INVALID_USER));
// 블로그 예외 처리
blogQueryRepository.findByMemberIdAndState(memberId, State.ACTIVE)
.orElseThrow(() -> new BaseException(BaseResponseStatus.INVALID_BLOG));
return memberId;
}
}
public class FeignConfig {
@Bean
public RequestInterceptor requestInterceptor() {
return template -> {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes != null) {
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
String token = request.getHeader("Authorization");
if (token != null && !token.isEmpty()) {
template.header(HttpHeaders.AUTHORIZATION, token);
}
}
};
}
@Bean
public FeignErrorDecoder errorDecoder() {
return new FeignErrorDecoder();
}
}
requestInterceptor()
FeignClient를 통해 헤더를 전달하는 함수이다. Header를 통해 들어온 JwtToken을 FeignClient의 Header로 넘겨준다.
errorDecoder()
FeignErrorDecorder 사용을 위한 빈을 정의한다.
@Component
public class FeignErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
int status = response.status();
if(status == 403) {
return new BaseException(BaseResponseStatus.UNAUTHORIZED_CLIENT);
} else if(status == 401) {
return new BaseException(BaseResponseStatus.UNAUTHORIZED_CLIENT);
} else if(status == 400) {
return new BaseException(BaseResponseStatus.BAD_ACCESS_TOKEN);
}
return new Exception(response.reason());
}
}
ErrorDecorder로 FeignClient 에러를 핸들링하지 않으면 에러가 발생한 경우 서버 에러로 처리되어 넘어간다. 이렇게 되면 클라이언트에서 에러 상황을 제대로 파악하지 못하기 때문에 FeignClient를 통해 넘어온 에러를 커스텀하여 반환해주는 것이 좋다.
Spring Cloud OpenFeign
Spring Cloud OpenFeign Features :: Spring Cloud Openfeign
[Spring Cloud] Spring Cloud OpenFeign
[Spring Cloud] Spring Cloud OpenFeign - 기본 개념 및 활용
feign client ErrorDecoder 분석
좋아요