try/catch 사용ErrorDecoder 사용✅ ErrorDecoder란?
- Feign 클라이언트에서 HTTP 요청을 보낼 때 발생하는 오류를 처리하기 위해 사용하는 인터페이스
- 서버로부터 반환된 HTTP 응답 코드에 따라 예외를 처리하거나 사용자 정의 에러 메시지를 반환 가능
User-service 에서 Order-service 를 호출하는 과정에서Order-service의 엔드 포인트 주소를 호출//테스트를 위해 잘못된 주소값 설정
//올바른 주소: "/order-service/{userId}/orders"
@GetMapping("/order-service/{userId}/orders_ng")
List<ResponseOrder> getOrders(@PathVariable String userId);

➡ 존재하지 않는 엔드 포인트이기 때문에 500 Internal Server Error와 함께 FeignExceptionNotFound가 404 에러를 반환함.
@Override
public UserDto getUserByUserId(String userId) {
UserEntity userEntity = userRepository.findByUserId(userId);
if(userEntity == null){
throw new UsernameNotFoundException("User not found");
}
UserDto userDto = new ModelMapper().map(userEntity, UserDto.class);
/* Using as FeignClient */
/* Feign Exception Handling */
List<ResponseOrder> orderList = null;
try{
orderList = orderServiceClient.getOrders(userId);
} catch(FeignException e) {
log.error(e.getMessage()); //@Slf4j 추가하여 log 사용
}
/* End */
userDto.setOrders(orderList);
return userDto;
}


200 OK와 함께 사용자 정보만 나타남package com.example.euserservice.error;
import feign.Response;
import feign.codec.ErrorDecoder;
import org.springframework.http.HttpStatusCode;
import org.springframework.web.server.ResponseStatusException;
public class FeignErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
switch (response.status()) {
case 400:
break;
case 404:
if(methodKey.contains("getOrders")){
return new ResponseStatusException(HttpStatusCode.valueOf(response.status()), "User's orders is empty.");
}
break;
default:
return new Exception(response.reason());
}
return null;
}
}
💡 코드 설명
ErrorDecoder를 상속 시,decode()메서드를 필수로 Override 받아야 함- 응답코드에 따라 다른 에러 처리 방법 구현 가능
- 응답코드가
404인 경우에도 세부적으로 특정 메서드의 이름에 따라 다른 처리 방법 구현 가능
@Override
public UserDto getUserByUserId(String userId) {
UserEntity userEntity = userRepository.findByUserId(userId);
if(userEntity == null){
throw new UsernameNotFoundException("User not found");
}
UserDto userDto = new ModelMapper().map(userEntity, UserDto.class);
/* FeignDecoder Handling */
List<ResponseOrder> orderList = orderServiceClient.getOrders(userId);
userDto.setOrders(orderList);
return userDto;
}
➡ try/catch 부분이 불필요하므로 삭제하고 기존 코드대로 변경


➡ 500 Internal Server Error 를 404 Not Found 로 변경됨

➡ 설정한 에러 메시지가 반환됨
Config 설정 파일 중 user-service.yml 수정order_service:
url: http://order-service/order-service/%s/orders
exception:
orders_is_empty: User's orders is empty.
package com.example.euserservice.error;
import feign.Response;
import feign.codec.ErrorDecoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatusCode;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ResponseStatusException;
@Component //추가
public class FeignErrorDecoder implements ErrorDecoder {
Environment env;
@Autowired
public FeignErrorDecoder(Environment env) {
this.env = env;
}
@Override
public Exception decode(String methodKey, Response response) {
switch (response.status()) {
case 400:
break;
case 404:
if(methodKey.contains("getOrders")){
return new ResponseStatusException(HttpStatusCode.valueOf(response.status()), env.getProperty("order_service.exception.orders_is_empty"));
}
break;
default:
return new Exception(response.reason());
}
return null;
}
}
➡ 기존 하드코딩 대신 설정 파일에서 메시지 내용 가져옴
➡ @Component 어노테이션을 추가하여 빈으로 등록
package com.example.euserservice;
import com.example.euserservice.error.FeignErrorDecoder;
import feign.Logger;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class EUserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(EUserServiceApplication.class, args);
}
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
@Bean
public Logger.Level feignLoggerLevel(){ return Logger.Level.FULL; }
// @Bean
// public FeignErrorDecoder getFeignErrorDecoder(){
// return new FeignErrorDecoder();
// }
}
➡ FeignErrorDecoder 파일에서 직접 @Component 어노테이션을 추가하여 빈으로 등록했기 때문에 해당 클래스에서 추가적으로 Bean에 등록할 필요가 없기 때문에 주석 처리
