티스토리 뷰

저번 시간에는 FeginClient를 활용한 Error를 처리하는 과정을 거쳤습니다.

다만 Error가 발생할경우 커스텀을 통해서 저희가 원하는 값을 내고 싶을 땐 어떻게 해야할까요?

그럴때 사용하는 것이 ErrorDecoder입니다.


#개요

따로 추가해야할 Dependency는 없습니다. Feign 라이브러리가 해당 인터페이스를 이미 제공하고 있으니까요!, 이번 포스트에서는, ErrorDecoder를 활용해서 특정 에러가 발생하면 특정 동작이 실행되도록 코드를 작성하려 합니다.


#FeignErrorDecoder.java

ErrorDecoder를 작성하기에 앞서, 먼저 

error 패키지 생성 후 FeignErrorDecoder라는 클래스를 생성합니다.

FeignErrorDecoder는 ErrorDecoder를 Implements하는 클래스입니다.

package com.example.userservice.error;

import feign.Response;
import feign.codec.ErrorDecoder;
import org.springframework.http.HttpStatus;
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(HttpStatus.valueOf(response.status()),
                                    "User's orders is empty.");
                }
                break;
            default:
                return new Exception(response.reason());
        }
        return null;
    }
}

404 Error같은 경우에는 저희가 Exception처리를 해줬었죠? 만약 methodKeygetOrders가 포함이 된다면, User's orders is empty 를 출력해, 주문정보를 불러올 수 없음을 출력하도록 설계가 되어있습니다.


#UserServiceApplication.java

등록된 Decoder를 Application에 @Bean을 통해 등록해줍니다.

package com.example.userservice;

import com.example.userservice.error.FeignErrorDecoder;
import feign.Logger;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
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
@EnableEurekaClient
@EnableFeignClients
public class UserServiceApplication {

   public static void main(String[] args) {
      SpringApplication.run(UserServiceApplication.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();
   }
}

#UserServiceImpl

 마지막으로 UserServiceImpl를 다음과 같이 변경해줍니다.

    @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);

        String orderUrl = String.format(env.getProperty("order_service.url"),userId);

        /* ErrorDecoder  */
        List<ResponseOrder> orderList =orderServiceClient.getOrders(userId);
        userDto.setOrders(orderList);

        return userDto;
    }

끝이에요. 정말로요! @Bean에 등록한 시점부터 404에러가 뜨면 자동으로 FeignErrorDecoder에 영향을 받습니다.

정말 그런지 확인 해볼까요?


#TEST 1

( 회원 등록, 로그인 , 주문등록은 미리 진행 한 뒤에 테스트 해주세요. )

저희가 입력한 Message가 정상출력 됩니다!


 

#user-service.yml

언제나 그렇지만, env.getProperty를 통해 활용 할 수 있는 값은 yml파일을 통해 뺴주도록 합시다.

spring:
   datasource:
      driver-class-name: org.h2.Driver
      url: jdbc:h2:mem:testdb
      username: sa
      password: '{cipher}AQA3Wlcwu01k0p9/hdLhSv5L/WKKo452yh576T+I/AV5kiNckUu6PBPdmetCB0zecb7AkoktLfqZPt6ziznW3slrKHkIb39vyQjFcn0wKTdp6t38e84uoQbWp5SH7BXsT3pTYrmSwPGDvgr3K3NgPAnfZ0zOntmjR/ImO1YetawqFD3OQUqVxMYB93l6z6lG92vSbZpRRp2tJ9lke1u6zeV+vG3tEeFuezk3nb/r8dk8SGtRjru41tUD/g0fQ9R3Rf/i/0Sly9eN9Q2NAAVJ1Be6ohOMexth1oSQoeKQqmwPVCXxwglpDWvntxfanmsK5oavLeOeglkoXFg9JWVst2A9hJHPke6jyTabzuiz+kv3gHcNrih9bJ9RrEifJ4Xl2a8='
token:
  expiration_time: 864000000
  secret: '{cipher}AQAIOA+faH9XEcX5usyT6e+7ahXOvdmcZtZHMHQyDznJQ21eXzGq11eXqWh7F8/zo2xywBQhSmCz3YbyT181IvPhUB9djzSiYyndedY/k1XX/1lr8a1eZ5QRJbQeuAawgPmeZpr2xb+/7egZw2uUZtzSZKS7xZHh9cXV218sAWteTOKP9Y0AzWuz0ZiV7CJhK9OuZvTBShIlF3Hfy1Umxdmy2AwSqNIEndH8RQer17CaI7fF/sdj41v6YECyL39jP5143aaUyxTt/qgw2RDgh9WrDNdH4LwavG4Pl7+9ob5PDcpZekmScPsXFouJTjGYaX4ICTk04FhoOgxt9ZSB0lBdruNTgzdEaDTE519BaF1EY8noii/4mQDDTIfkX/Gz4oZfHcz6JwhDHeHgKFmxRc4v'
gateway:
  ip: 0.0.0.0

order_service:
  url: http://ORDER-SERVICE/order-service/%s/orders
  exception:
    order_is_empty: User's orders is empty.

message 부분을 yml에 등록해줍니다.


#FeignErrorDecoder.java

그럼 Yml파일의 환경정보를 가져오기 위해서 먼저 Envirment를 등록해줍니다. @Autowired를 통해서 생성자를 통한 주입도 잊지 말아주세요. 그리고 해당 클래스는 @Componet를 통해 컴포넌트로 취급할 예정입니다. 그럼 이제 env.getProperty 를 통해서 yml파일에 있는 설정정보를 가져올수 있습니다!

package com.example.userservice.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.HttpStatus;
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(HttpStatus.valueOf(response.status()),
                                    env.getProperty("order_service.exception.order_is_empty"));
                }
                break;
            default:
                return new Exception(response.reason());
        }
        return null;
    }
}

 

하지만 여기까지 진행하시면 아마 관련 클래스에서 오류가 발생하게 됩니다.


#UserServiceApplication.java

해당 오류가 발생하는 것은 생성자에 입력인자와 @Bean으로 등록한 생성자의 입력인자가 달라서인데, 이미 @Component로 등록되어 있으니 해당 @Bean은 삭제처리 해줍니다.

package com.example.userservice;

import com.example.userservice.error.FeignErrorDecoder;
import feign.Logger;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
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
@EnableEurekaClient
@EnableFeignClients
public class UserServiceApplication {

   public static void main(String[] args) {
      SpringApplication.run(UserServiceApplication.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;
   }

}

 

그러면 오류가 해결이 됩니다! 야호


 

#TEST 2

마지막으로 다시 테스트를 해볼게요.

 일단 Message를 보니 정상적으로 값을 받아온 것 같습니다. 해당 값은 yml파일을 참조하고 있으니, 값 변경을 통해서 yml파일로 바꾸면 좋은이유를 여기서 다시 보여드릴 수 있겟네요

user-service.yml의 값을 다음과 같이 바꿨습니다.

서비스를 재시작하지 않았으니 Spring-Cloud-Bus를 활용해 Refresh 해봅시다!

http://localhost:8000/user-service/actuator/busrefresh

그럼 다시 주문정보를 확인해볼까요?

잘 바뀐 것을 확인 할 수 있습니다.

 


마치며..

이번 시간에는 ErrorDecoder를 활용해서 에러 메시지를 커스텀 하는 시간이었습니다.이와 동시에 다시한번 Yml파일을 사용해서 env로 값을 가져오면 서비스를 재시작 할 필요없이, 간단하게 값을 바꿀 수 있어 다시한번 장점을 느낄 수 있었네요.

 


감사합니다

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
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
글 보관함