// UserServiceApplication.java
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
...
// Bean에 RestTemplate 등록
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
// UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {
private UserRepository userRepository;
private BCryptPasswordEncoder bCryptPasswordEncoder;
private Environment env;
private RestTemplate restTemplate;
public UserServiceImpl(UserRepository userRepository,
BCryptPasswordEncoder bCryptPasswordEncoder,
Environment env,
RestTemplate restTemplate) {
this.userRepository = userRepository;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
this.env = env;
this.restTemplate = restTemplate;
}
...
@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 Rest Template */
// property파일에 userId를 기준으로 order정보를 받아오는 API주소를 등록한다.
String orderUrl = String.format(env.getProperty("order_service.url"), userId); // property파일에 userId를 기준으로 order정보를 받아오는 API주소
ResponseEntity<List<ResponseOrder>> orderListResponse =
restTemplate.exchange(orderUrl, HttpMethod.GET, null,
new ParameterizedTypeReference<List<ResponseOrder>>() {
});
List<ResponseOrder> ordersList = orderListResponse.getBody();
userDto.setOrders(ordersList);
return userDto;
}
}
order-service:
url: http://127.0.0.1:8000/order-service/%s/orders/
POST
{ContextPath}/order-service/{userId}/orders
로 주문 정보를 담아 요청GET
{ContextPath}/order-service/{userId}/orders
요청을 통해 주문 정보 응답받기// UserServiceApplication.java
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
...
@Bean
@LoadBalanced // @LoadBalanced 추가
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
// property 설정 변경
order-service:
url: http://ORDER-SERVICE/order-service/%s/orders/
FeignCliend
-> HTTP Client
Spring Cloud Netflix
라이브러리HTTP Endpoint
에 대한 Interface를 생성@FeignClient
선언Spring Cloud Netflix
의존성 추가@FeignClient
Interface 생성// UserServiceApplication.java
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients // FeignClient 적용 어노테이션
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
...
}
// OrderServiceClient.java
@FeignClient(name = "order-service")
public interface OrderServiceClient {
@GetMapping("/order-service/{userId}/orders")
List<ResponseOrder> getOrders(@PathVariable("userId") String userId);
}
// UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {
private UserRepository userRepository;
private BCryptPasswordEncoder bCryptPasswordEncoder;
private Environment env;
private RestTemplate restTemplate;
private OrderServiceClient orderServiceClient;
public UserServiceImpl(UserRepository userRepository,
BCryptPasswordEncoder bCryptPasswordEncoder,
Environment env,
RestTemplate restTemplate,
OrderServiceClient orderServiceClient) {
this.userRepository = userRepository;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
this.env = env;
this.restTemplate = restTemplate;
this.orderServiceClient = orderServiceClient;
}
...
@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 Rest Template */
// List<ResponseOrder> orders = new ArrayList<>();
// String orderUrl = String.format(env.getProperty("order-service.url"), userId);
// ResponseEntity<List<ResponseOrder>> orderListResponse =
// restTemplate.exchange(orderUrl, HttpMethod.GET, null,
// new ParameterizedTypeReference<List<ResponseOrder>>() { // 반환 타입 설정
// });
// List<ResponseOrder> ordersList = orderListResponse.getBody();
/* Using as feign client */
List<ResponseOrder> ordersList = orderServiceClient.getOrders(userId);
userDto.setOrders(ordersList);
return userDto;
}
...
}
UserService에서 예외처리
// application.yml
logging:
level:
com.example.userservice.client: DEBUG
// UserServiceApplication.java
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
...
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
// UserServiceImpl.java
@Service
@Slf4j
public class UserServiceImpl implements UserService {
private UserRepository userRepository;
private BCryptPasswordEncoder bCryptPasswordEncoder;
private Environment env;
private RestTemplate restTemplate;
private OrderServiceClient orderServiceClient;
public UserServiceImpl(UserRepository userRepository,
BCryptPasswordEncoder bCryptPasswordEncoder,
Environment env,
RestTemplate restTemplate,
OrderServiceClient orderServiceClient) {
this.userRepository = userRepository;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
this.env = env;
this.restTemplate = restTemplate;
this.orderServiceClient = orderServiceClient;
}
...
@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 Rest Template */
// List<ResponseOrder> orders = new ArrayList<>();
// String orderUrl = String.format(env.getProperty("order-service.url"), userId);
// ResponseEntity<List<ResponseOrder>> orderListResponse =
// restTemplate.exchange(orderUrl, HttpMethod.GET, null,
// new ParameterizedTypeReference<List<ResponseOrder>>() { // 반환 타입 설정
// });
// List<ResponseOrder> ordersList = orderListResponse.getBody();
/* Using as feign client */
List<ResponseOrder> ordersList = null;
try{
ordersList = orderServiceClient.getOrders(userId);
} catch (FeignException ex) {
log.error(ex.getMessage());
}
userDto.setOrders(ordersList);
return userDto;
}
...
}
// 추가
@Bean
public FeignErrorDecoder getFeignErrorDecoder() {
return new FeignErrorDecoder();
}
// FeignErrorDecoder.java
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;
}
}
error코드의 status값 마다 message를 다르게 할 수 있다.
message를 env.getProperty()
를 이용해 property파일로 수정되게 바꿀 수 있다. FeignErrorDecoder
에 @Component
를 적용해 (이 경우 UserServiceApplication에 FeignErrorDecoder
를 없애도 정상 실행된다.)
@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 Rest Template */
// List<ResponseOrder> orders = new ArrayList<>();
// String orderUrl = String.format(env.getProperty("order-service.url"), userId);
// ResponseEntity<List<ResponseOrder>> orderListResponse =
// restTemplate.exchange(orderUrl, HttpMethod.GET, null,
// new ParameterizedTypeReference<List<ResponseOrder>>() { // 반환 타입 설정
// });
// List<ResponseOrder> ordersList = orderListResponse.getBody();
/* Using as feign client */
// List<ResponseOrder> ordersList = null;
// try{
// ordersList = orderServiceClient.getOrders(userId);
// } catch (FeignException ex) {
// log.error(ex.getMessage());
// }
/* ErrorDecoder */
List<ResponseOrder> ordersList = orderServiceClient.getOrders(userId);
userDto.setOrders(ordersList);
return userDto;
}