### 스프링 부트의 WebClient에 대한 설명

스프링 부트에서 `WebClient`는 스프링 5에서 도입된 **비동기(Asynchronous)** 및 **리액티브(Reactive)** HTTP 클라이언트로, 외부 REST API 호출을 처리하기 위한 현대적인 도구입니다. `RestTemplate`의 후속으로 설계된 `WebClient`는 리액티브 스트림(Reactive Streams)을 기반으로 동작하며, 비동기 처리와 높은 동시성을 요구하는 환경에서 뛰어난 성능을 제공합니다. 스프링 부트와 통합 시, 선언적이고 유연한 API로 HTTP 요청을 쉽게 구성할 수 있습니다. 아래에서 설정, 사용 방법, 주요 기능을 설명합니다.

---

#### 1. WebClient의 기본 개념
- **비동기/리액티브**: 요청을 보내고 응답을 기다리지 않고, 결과를 `Mono`나 `Flux`로 비동기적으로 처리.
- **HTTP 메서드 지원**: GET, POST, PUT, DELETE 등 모든 표준 메서드 지원.
- **리액티브 스트림**: `Mono` (단일 결과), `Flux` (다중 결과)를 반환하여 데이터 스트리밍 가능.
- **대체재**: `RestTemplate`을 대체하며, 스프링 5.0 이상에서 권장.

#### 2. 의존성
`WebClient`는 `spring-boot-starter-webflux`에 포함되어 있습니다.

```xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
```

- **참고**: `WebFlux`는 리액티브 웹 애플리케이션 프레임워크로, `WebClient`를 포함.

---

#### 3. 기본 설정
`WebClient`는 스프링 빈으로 등록하거나, 필요 시 커스터마이징하여 사용할 수 있습니다.

```java
@Configuration
public class WebClientConfig {

    @Bean
    public WebClient webClient(WebClient.Builder builder) {
        return builder
            .baseUrl("https://api.example.com")  // 기본 URL 설정
            .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)  // 기본 헤더
            .clientConnector(new ReactorClientHttpConnector(
                HttpClient.create().responseTimeout(Duration.ofSeconds(10))  // 타임아웃 설정
            ))
            .build();
    }
}
```

- **`WebClient.Builder`**: 타임아웃, 기본 헤더, 필터 등을 설정.

---

#### 4. WebClient 사용 예시

##### (1) GET 요청
```java
@Service
public class UserService {

    private final WebClient webClient;

    public UserService(WebClient webClient) {
        this.webClient = webClient;
    }

    // 단일 사용자 조회 (Mono)
    public Mono<User> getUser(Long id) {
        return webClient.get()
            .uri("/users/{id}", id)
            .retrieve()
            .bodyToMono(User.class);
    }

    // 모든 사용자 조회 (Flux)
    public Flux<User> getAllUsers() {
        return webClient.get()
            .uri("/users")
            .retrieve()
            .bodyToFlux(User.class);
    }
}

@Data
public class User {
    private Long id;
    private String name;
    private int age;
}
```

- **`Mono`**: 단일 결과 예상 시 사용.
- **`Flux`**: 다중 결과(리스트 또는 스트림) 예상 시 사용.

##### (2) POST 요청
```java
public Mono<User> createUser(User user) {
    return webClient.post()
        .uri("/users")
        .bodyValue(user)  // 요청 바디 설정
        .retrieve()
        .bodyToMono(User.class);
}
```

- **`bodyValue`**: 객체를 JSON으로 직렬화해 전송.

##### (3) PUT 요청
```java
public Mono<Void> updateUser(Long id, User user) {
    return webClient.put()
        .uri("/users/{id}", id)
        .bodyValue(user)
        .retrieve()
        .bodyToMono(Void.class);  // 응답 본문 없음
}
```

##### (4) DELETE 요청
```java
public Mono<Void> deleteUser(Long id) {
    return webClient.delete()
        .uri("/users/{id}", id)
        .retrieve()
        .bodyToMono(Void.class);
}
```

##### (5) 커스텀 요청
```java
public Mono<User> getUserWithHeaders(Long id) {
    return webClient.get()
        .uri("/users/{id}", id)
        .header("Authorization", "Bearer token123")
        .accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .bodyToMono(User.class);
}
```

- **헤더/쿼리 파라미터**: `.header()`, `.uri(builder -> builder.queryParam("key", "value").build())`로 추가.

##### (6) 컨트롤러에서 사용
```java
@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/{id}")
    public Mono<User> getUser(@PathVariable Long id) {
        return userService.getUser(id);
    }

    @GetMapping
    public Flux<User> getAllUsers() {
        return userService.getAllUsers();
    }

    @PostMapping
    public Mono<User> createUser(@RequestBody User user) {
        return userService.createUser(user);
    }
}
```

---

#### 5. 주요 메서드 및 빌더 체인
- **요청 빌더**:
  - `.get()`, `.post()`, `.put()`, `.delete()`: HTTP 메서드 지정.
  - `.uri(String uri, Object... uriVariables)`: URL 설정.
  - `.header(String name, String value)`: 헤더 추가.
  - `.bodyValue(Object body)`: 요청 바디 설정.
  - `.accept(MediaType... mediaTypes)`: 수용 가능한 응답 타입.

- **응답 처리**:
  - `.retrieve()`: 응답을 간단히 처리 (4xx, 5xx는 예외 발생).
  - `.bodyToMono(Class<T> elementClass)`: 단일 객체로 변환.
  - `.bodyToFlux(Class<T> elementClass)`: 다중 객체 스트림으로 변환.

---

#### 6. 에러 처리
`WebClient`는 HTTP 오류를 `WebClientResponseException`으로抛출합니다. 이를 처리하거나 커스텀 로직을 추가할 수 있습니다.

```java
public Mono<User> getUserWithErrorHandling(Long id) {
    return webClient.get()
        .uri("/users/{id}", id)
        .retrieve()
        .onStatus(HttpStatus::is4xxClientError, response -> 
            Mono.error(new RuntimeException("Client Error: " + response.statusCode())))
        .onStatus(HttpStatus::is5xxServerError, response -> 
            Mono.error(new RuntimeException("Server Error: " + response.statusCode())))
        .bodyToMono(User.class)
        .onErrorResume(e -> {
            log.error("Error occurred: {}", e.getMessage());
            return Mono.just(new User(-1L, "Unknown", 0));  // 기본값 반환
        });
}
```

- **`onStatus`**: 특정 상태 코드에 대한 처리.
- **`onErrorResume`**: 예외 발생 시 대체 로직.

---

#### 7. 장점과 한계
- **장점**:
  - 비동기/리액티브로 높은 동시성 처리.
  - 스트리밍 데이터 지원 (예: 대용량 데이터).
  - 선언적이고 체인 방식의 API.
- **한계**:
  - 리액티브 프로그래밍에 익숙해야 함 (러닝 커브 존재).
  - 간단한 동기 작업에서는 `RestTemplate`보다 복잡할 수 있음.

---

#### 8. RestTemplate과의 비교
- **`RestTemplate`**: 동기식, 간단한 사용, 스프링 5 이전 표준.
- **`WebClient`**: 비동기/리액티브, 대규모 트래픽 및 스트리밍에 적합, 스프링 5 이후 권장.
- **선택 기준**: 비동기와 성능이 중요한 경우 `WebClient`, 단순 동기 호출이면 `RestTemplate`.

---

#### 9. 결론
`WebClient`는 스프링 부트에서 현대적인 REST API 호출을 위한 강력한 도구로, 비동기 및 리액티브 특성을 활용해 높은 확장성과 성능을 제공합니다. `Mono`와 `Flux`를 통해 유연한 데이터 처리가 가능하며, 에러 처리와 커스터마이징 옵션도 풍부합니다. 위 예시를 참고하면 기본적인 HTTP 요청부터 복잡한 시나리오까지 쉽게 구현할 수 있습니다. 추가 질문이 있다면 언제든 물어보세요!