# MicroService Communication

## Communication Types

* 동기 방식으로 통신하는 경우 HTTP 통신을 사용할 수 있다. 이 방식을 사용하면 요청을 보냈을 때 응답이 올 때 까지 기다려야 하므로 그동안 다른 작업을 수행할 수 없다.
* 비동기 방식으로는 AMQP 를 사용할 수 있다.

## RestTemplate

* REST API 요청을 위해 스프링 프레임워크에서 제공하는 클래스이다.

### 사용 방법

* 보통은 하나의 RestTemplate 객체를 만들어 각 서비스마다 빈으로 주입해 사용한다.

```java
@Configuration
public class Config {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
```

```java
@Service
public class UserService {
    
    // 생성자 주입
    private final RestTemplate restTemplate;
    
    // ...
    public UserDto getUserByUserId(String userId) {
        // user 정보 조회
        // ...
    
        // order 정보 조회
        String orderUrl = "http://.../order-service/.../orders";
        ResponseEntity<List<ResponseOrder>> response = restTemplate.exchange(orderUrl, HttpMethod.GET, null,
            new ParameterizedTypeReference<List<ResponseOrder>>() {});
        
        List<ResponseOrder> orders = response.getBody();
        userDto.setOrders(orders);
        
        return userDto;
    }
}
```

## FeignClient

* REST 호출을 추상화한 Spring Cloud Netflix 라이브러리이다.
* 호출하려는 HTTP Endpoint에 대한 인터페이스를 생성하고 @FeignClient 어노테이션을 추가해 사용할 수 있다.
* 로드밸런싱 기능을 제공한다.

### 사용 방법

* 의존성을 추가하고 @EnableFeignClients 어노테이션을 설정 클래스에 추가한다.

```groovy
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
```

```java
@EnableFeignClients
public class Config {
}
```

* 인터페이스를 생성한다. @FeignClient 어노테이션의 name 속성에는 API 호출 대상인 마이크로 서비스의 이름을 입력한다. 인터페이스 메서드는 Controller와 동일한 형태로 작성하면 된다.

```java
@FeignClient(name=order-service)
public interface OrderServiceClient {
    @GetMapping("/order-service/{userId}/orders")
    List<ResponseOrder> getOrders(@PathVariable String userId);
}
```

* 서비스 클래스에서는 인터페이스를 빈으로 입력받아 사용할 수 있다.

```java
@Service
public class UserService {
    
    // 생성자 주입
    private final OrderServiceClient orderServiceClient;
    
    // ...
    public UserDto getUserByUserId(String userId) {
        // user 정보 조회
        // ...
    
        // order 정보 조회
        List<ResponseOrder> orders = orderServiceClient.getOrders(userId);
        
        userDto.setOrders(orders);
        
        return userDto;
    }
}
```

### 예외 처리

* FeignClient 사용 중에 만약 http uri가 잘못되었다면 FeignException이 발생할 것이다.
* try-catch로 예외를 처리할 수도 있지만 ErrorDecoder를 이용해 예외를 집약적으로 처리할 수 있다.

#### 사용 방법

* Errordecoder 클래스를 생성해 각 예외 원인마다 원하는 예외 처리를 해주고 이 클래스를 빈으로 등록해주면 된다.

```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 order is empty");
                }
                break;
            default:
                return new Exception(response.reason());
        }
    }
}
```

```java
@Configuration
public class Config {

    @Bean
    public FeignErrorDecoder feignErrorDecoder() {
        return new FeignErrorDecoder();
    }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://develop-footprint.gitbook.io/o/spring/spring-cloud/microservice-communication.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
