2025-04-08T19:56:24
This commit is contained in:
189
docs/webflux/10_실전 프로젝트.md
Normal file
189
docs/webflux/10_실전 프로젝트.md
Normal file
@@ -0,0 +1,189 @@
|
||||
아래는 **"스프링부트 웹플럭스 시리즈"**의 **10장: 실전 프로젝트: 웹플럭스로 마이크로서비스 구축"**에 대한 초안입니다. 9장까지의 내용을 바탕으로 간단한 마이크로서비스를 설계하고, 웹플럭스와 다른 스프링 모듈을 통합하며 배포와 모니터링 팁을 다룹니다. 실습은 간략히 유지하며 초보자도 따라 할 수 있도록 자연스러운 문체로 작성했습니다.
|
||||
|
||||
---
|
||||
|
||||
## 10. 실전 프로젝트: 웹플럭스로 마이크로서비스 구축
|
||||
|
||||
이제 웹플럭스의 기본기를 실전에서 활용해볼 시간입니다. 이번 장에서는 간단한 마이크로서비스를 설계하고, 웹플럭스를 중심으로 다른 스프링 모듈과 통합하며, 배포와 모니터링까지 다뤄보겠습니다. 작은 프로젝트지만 실무에서 유용한 패턴을 경험할 수 있을 거예요. 준비되셨죠? 출발합시다!
|
||||
|
||||
### 간단한 마이크로서비스 설계
|
||||
|
||||
마이크로서비스는 독립적으로 배포 가능한 작은 서비스 단위로, 여기서는 두 개의 서비스를 만들어보겠습니다:
|
||||
1. **User Service**: 사용자 정보를 관리 (6~7장에서 만든 기능 재사용).
|
||||
2. **Order Service**: 주문 데이터를 처리하며 User Service와 통신.
|
||||
|
||||
두 서비스는 REST API로 상호작용하며, 웹플럭스의 비동기 특성을 활용합니다.
|
||||
|
||||
#### User Service
|
||||
기존 프로젝트를 그대로 사용합니다. `UserController`와 `UserRepository`가 이미 준비되어 있죠. 포트를 명확히 하기 위해 `application.yaml`을 수정합니다:
|
||||
|
||||
```yaml
|
||||
server:
|
||||
port: 8081
|
||||
spring:
|
||||
r2dbc:
|
||||
url: r2dbc:mariadb://localhost:3306/webflux_db
|
||||
username: root
|
||||
password: yourpassword
|
||||
```
|
||||
|
||||
#### Order Service
|
||||
새 프로젝트를 생성하거나, 같은 프로젝트 내에서 별도 패키지로 분리합니다. 여기서는 새 프로젝트로 진행한다고 가정하고, `build.gradle`을 설정합니다:
|
||||
|
||||
```gradle
|
||||
plugins {
|
||||
id 'org.springframework.boot' version '3.2.4'
|
||||
id 'java'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-webflux'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc'
|
||||
implementation 'io.r2dbc:r2dbc-mariadb:1.1.3'
|
||||
runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
|
||||
}
|
||||
```
|
||||
|
||||
`application.yaml` 설정:
|
||||
|
||||
```yaml
|
||||
server:
|
||||
port: 8082
|
||||
spring:
|
||||
r2dbc:
|
||||
url: r2dbc:mariadb://localhost:3306/webflux_db
|
||||
username: root
|
||||
password: yourpassword
|
||||
```
|
||||
|
||||
주문 엔티티와 리포지토리를 추가합니다:
|
||||
|
||||
```java
|
||||
// Order.java
|
||||
package com.example.order;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
|
||||
@Data
|
||||
public class Order {
|
||||
@Id
|
||||
private Long id;
|
||||
private Long userId;
|
||||
private String product;
|
||||
}
|
||||
|
||||
// OrderRepository.java
|
||||
package com.example.order;
|
||||
|
||||
import org.springframework.data.r2dbc.repository.R2dbcRepository;
|
||||
|
||||
public interface OrderRepository extends R2dbcRepository<Order, Long> {
|
||||
}
|
||||
```
|
||||
|
||||
Order Service의 컨트롤러는 User Service와 통신합니다:
|
||||
|
||||
```java
|
||||
package com.example.order;
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/orders")
|
||||
public class OrderController {
|
||||
|
||||
private final OrderRepository orderRepository;
|
||||
private final WebClient webClient;
|
||||
|
||||
public OrderController(OrderRepository orderRepository) {
|
||||
this.orderRepository = orderRepository;
|
||||
this.webClient = WebClient.create("http://localhost:8081");
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public Mono<Order> createOrder(@RequestBody Order order) {
|
||||
return webClient.get()
|
||||
.uri("/users/{id}", order.getUserId())
|
||||
.retrieve()
|
||||
.bodyToMono(User.class)
|
||||
.flatMap(user -> orderRepository.save(order));
|
||||
}
|
||||
|
||||
@GetMapping("/user/{userId}")
|
||||
public Flux<Order> getOrdersByUser(@PathVariable Long userId) {
|
||||
return orderRepository.findAll()
|
||||
.filter(order -> order.getUserId().equals(userId));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `createOrder`: User Service에서 사용자 존재 여부를 확인 후 주문을 저장.
|
||||
- `getOrdersByUser`: 특정 사용자의 주문 목록 반환.
|
||||
|
||||
MariaDB에 `orders` 테이블 생성:
|
||||
|
||||
```sql
|
||||
CREATE TABLE orders (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL,
|
||||
product VARCHAR(255) NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
### 웹플럭스와 다른 스프링 모듈 통합
|
||||
|
||||
1. **WebClient**: Order Service에서 User Service와 통신할 때 사용. 비동기 HTTP 클라이언트로, 웹플럭스와 잘 맞습니다.
|
||||
2. **Actuator**: 모니터링을 위해 추가:
|
||||
```gradle
|
||||
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||
```
|
||||
```yaml
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: health,metrics
|
||||
```
|
||||
`http://localhost:8082/actuator/health`로 상태 확인 가능.
|
||||
|
||||
3. **스프링 시큐리티** (선택): 간단한 인증을 추가하려면:
|
||||
```gradle
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||
```
|
||||
기본 사용자/비밀번호로 엔드포인트 보호 가능.
|
||||
|
||||
### 배포 및 모니터링 팁
|
||||
|
||||
1. **배포**:
|
||||
- JAR 파일 생성: `./gradlew bootJar`.
|
||||
- 실행: `java -jar build/libs/order-service-0.0.1-SNAPSHOT.jar`.
|
||||
- Docker 사용 시:
|
||||
```dockerfile
|
||||
FROM openjdk:17-jdk-slim
|
||||
COPY build/libs/*.jar app.jar
|
||||
ENTRYPOINT ["java", "-jar", "/app.jar"]
|
||||
```
|
||||
|
||||
2. **모니터링**:
|
||||
- Actuator 메트릭을 Prometheus와 Grafana로 시각화.
|
||||
- 로그는 SLF4J와 Logback으로 관리하며, `doOn` 연산자로 디버깅 로그 추가.
|
||||
|
||||
### 테스트해보기
|
||||
|
||||
1. User Service 실행 (`port: 8081`).
|
||||
2. Order Service 실행 (`port: 8082`).
|
||||
3. 사용자 생성: `POST http://localhost:8081/users`에 `{"name": "Alice", "email": "alice@example.com"}`.
|
||||
4. 주문 생성: `POST http://localhost:8082/orders`에 `{"userId": 1, "product": "Book"}`.
|
||||
5. 주문 조회: `GET http://localhost:8082/orders/user/1`.
|
||||
|
||||
### 마무리
|
||||
|
||||
간단한 마이크로서비스를 웹플럭스로 구축하며, 서비스 간 통신과 통합의 맛을 봤습니다. 비동기 처리와 논블로킹의 장점을 실무에 적용하는 첫걸음이죠. 다음 장에서는 웹소켓을 다루며 실시간 통신까지 확장해보겠습니다. 이번 프로젝트로 웹플럭스의 실전 감각이 생기셨길 바랍니다!
|
||||
|
||||
---
|
||||
|
||||
이 장은 마이크로서비스 설계와 통합을 중심으로 실습을 구성했으며, 배포와 모니터링 팁을 간략히 다뤘습니다. 추가 예제나 수정이 필요하면 말씀해주세요!
|
||||
Reference in New Issue
Block a user