์ด๋ฒคํธ ์ฃผ๋ํ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ: Kafka๋ก ํ์ฅ ๊ฐ๋ฅํ ๋ง์ดํฌ๋ก์๋น์ค ์ค๊ณํ๊ธฐ
์ด๋ฒคํธ ์ฃผ๋ํ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ ๋ ํ๋ ์ํํธ์จ์ด ๊ฐ๋ฐ์์ ์ค์ํ ํจ๋ฌ๋ค์์ ๋๋ค. ์ด ์ํคํ ์ฒ๋ ์๋น์ค ๊ฐ์ ๊ฒฐํฉ๋๋ฅผ ๋ฎ์ถ๊ณ , ์์คํ ํ์ฅ์ฑ์ ๊ทน๋ํํ๋ฉฐ, ๋น๋๊ธฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ฅผ ํตํด ์ฑ๋ฅ์ ์ต์ ํํ ์ ์์ต๋๋ค. ์ด๋ฒ ํฌ์คํ ์์๋ Kafka ๋ฅผ ํ์ฉํ ์ด๋ฒคํธ ์ฃผ๋ํ ๋ง์ดํฌ๋ก์๋น์ค ์ค๊ณ, ์ด๋ฒคํธ ์์ฑ๊ณผ CQRS ํจํด ์ ์ฉ, ์๋น์ค ๊ฐ ํต์ ์ฒ๋ฆฌ , ๊ทธ๋ฆฌ๊ณ Kafka๋ก ๋ง์ดํฌ๋ก์๋น์ค๋ฅผ ํ์ฅํ๋ ๋ฐฉ๋ฒ ์ ๋ค๋ฃน๋๋ค.
1. Kafka๋ฅผ ํ์ฉํ ๋ง์ดํฌ๋ก์๋น์ค ์ค๊ณ
Kafka๋ ๋ฉ์์ง ๋ธ๋ก์ปค ๋ก์ ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํคํ ์ฒ์์ ์ค์ํ ์ญํ ์ ํฉ๋๋ค. ์๋น์ค ๊ฐ ๋น๋๊ธฐ ํต์ ์ ๊ฐ๋ฅํ๊ฒ ํ๋ฉฐ, ๋ฐ์ดํฐ๋ฅผ ํํฐ์ ์ผ๋ก ๋ถํ ํ์ฌ ๋๊ท๋ชจ ํธ๋ํฝ ์ฒ๋ฆฌ์ ์ ํฉํฉ๋๋ค.
Kafka ๊ธฐ๋ฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ ์์
// ์ด๋ฒคํธ ํ๋ก๋์ ์์
public class OrderService {
@Autowired
private KafkaTemplate<String, OrderEvent> kafkaTemplate;
public void placeOrder(Order order) {
// ์ฃผ๋ฌธ ์ฒ๋ฆฌ ๋ก์ง
OrderEvent event = new OrderEvent(order.getId(), "ORDER_PLACED");
kafkaTemplate.send("order_events", event);
}
}
// ์ด๋ฒคํธ ์ปจ์๋จธ ์์
@KafkaListener(topics = "order_events", groupId = "payment_group")
public void handleOrderEvent(OrderEvent event) {
if ("ORDER_PLACED".equals(event.getStatus())) {
// ๊ฒฐ์ ์ฒ๋ฆฌ ๋ก์ง
processPayment(event.getOrderId());
}
}
์ค๋ช :
- KafkaTemplate ์ ์ฌ์ฉํ์ฌ ์ฃผ๋ฌธ ์ด๋ฒคํธ๋ฅผ order_events ํ ํฝ์ ์ ์กํฉ๋๋ค.
- @KafkaListener ๋ฅผ ์ฌ์ฉํ์ฌ ๊ฒฐ์ ์๋น์ค๊ฐ ํด๋น ์ด๋ฒคํธ๋ฅผ ์์ ํ๊ณ ์ฒ๋ฆฌํฉ๋๋ค.
2. ์ด๋ฒคํธ ์์ฑ๊ณผ CQRS ํจํด ์ ์ฉ
์ด๋ฒคํธ ์์ฑ(Event Sourcing) ์ ์์คํ ์ํ๋ฅผ ์ด๋ฒคํธ์ ์ฐ์ ์ผ๋ก ์ ์ฅํ๋ ๋ฐฉ์์ ๋๋ค. CQRS(Command Query Responsibility Segregation) ๋ ์ฐ๊ธฐ ์์ ๊ณผ ์ฝ๊ธฐ ์์ ์ ๋ถ๋ฆฌํ์ฌ ์์คํ ์ ํ์ฅ์ฑ๊ณผ ์ฑ๋ฅ์ ํฅ์์ํต๋๋ค. ์ด ๋ ํจํด์ Kafka๋ฅผ ํ์ฉํ ๋ง์ดํฌ๋ก์๋น์ค์์ ํนํ ์ ์ฉํฉ๋๋ค.
์ด๋ฒคํธ ์์ฑ ์์
public class OrderEventStore {
@Autowired
private KafkaTemplate<String, OrderEvent> kafkaTemplate;
public void saveEvent(OrderEvent event) {
kafkaTemplate.send("order_event_store", event);
}
public List<OrderEvent> getOrderEvents(String orderId) {
// Kafka์์ ์ด๋ฒคํธ๋ฅผ ๋ถ๋ฌ์ค๋ ๋ก์ง (์๋ต)
return fetchOrderEventsFromKafka(orderId);
}
}
CQRS ํจํด ์ ์ฉ ์์
// Command: ์ฃผ๋ฌธ ์์ฑ ์ฒ๋ฆฌ
public class OrderCommandService {
public void createOrder(Order order) {
// ์ฃผ๋ฌธ ์์ฑ ๋ก์ง
orderRepository.save(order);
eventStore.saveEvent(new OrderEvent(order.getId(), "ORDER_CREATED"));
}
}
// Query: ์ฃผ๋ฌธ ์กฐํ ์ฒ๋ฆฌ
public class OrderQueryService {
public Order getOrderDetails(String orderId) {
return orderRepository.findById(orderId);
}
}
์ค๋ช :
- ์ด๋ฒคํธ ์์ฑ ์ Kafka๋ฅผ ํตํด ๋ชจ๋ ์ด๋ฒคํธ๋ฅผ ์ ์ฅํ์ฌ ์ฃผ๋ฌธ ์ํ๋ฅผ ๋ณต์ํ ์ ์๊ฒ ํฉ๋๋ค.
- CQRS ํจํด ์ ๋ช ๋ น(Command)๊ณผ ์กฐํ(Query)๋ฅผ ๋ถ๋ฆฌํ์ฌ ๋ฐ์ดํฐ ์ผ๊ด์ฑ ์ ์ ์งํ๋ฉด์ ์ฑ๋ฅ์ ์ต์ ํํฉ๋๋ค.
3. ์๋น์ค ๊ฐ ํต์ ์ฒ๋ฆฌ
๋ง์ดํฌ๋ก์๋น์ค ๊ฐ ํต์ ์ ๋ค์ํ ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค. Kafka ๋ฅผ ์ฌ์ฉํ๋ฉด ๋น๋๊ธฐ ๋ฐฉ์์ผ๋ก ๋ฉ์์ง๋ฅผ ์ฃผ๊ณ ๋ฐ์ ์๋น์ค ๊ฐ ๊ฒฐํฉ๋๋ฅผ ๋ฎ์ถ ์ ์์ต๋๋ค. ๋ํ, Kafka๋ ๋์ฉ๋ ํธ๋ํฝ ์ฒ๋ฆฌ์ ์ ํฉํ๋ฏ๋ก, ์ฌ๋ฌ ์๋น์ค๊ฐ ๋์์ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ ์ ์์ต๋๋ค.
์๋น์ค ๊ฐ ํต์ ์ฒ๋ฆฌ ์์
@KafkaListener(topics = "inventory_events", groupId = "order_group")
public void handleInventoryUpdate(InventoryEvent event) {
if ("INVENTORY_UPDATED".equals(event.getStatus())) {
// ์ฌ๊ณ ์
๋ฐ์ดํธ ํ ์ฃผ๋ฌธ ์ํ ๋ณ๊ฒฝ ๋ก์ง
updateOrderStatus(event.getOrderId(), "READY_FOR_SHIPMENT");
}
}
์ค๋ช :
- Kafka๋ฅผ ํตํด ์ฌ๊ณ ์๋น์ค ์ ์ฃผ๋ฌธ ์๋น์ค ๊ฐ ํต์ ์ ์ฒ๋ฆฌํฉ๋๋ค. ์ฌ๊ณ ๊ฐ ์ ๋ฐ์ดํธ๋๋ฉด Kafka ์ด๋ฒคํธ๋ฅผ ํตํด ์ฃผ๋ฌธ ์ํ๋ฅผ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.
4. Kafka๋ก ๋ง์ดํฌ๋ก์๋น์ค ํ์ฅํ๊ธฐ
Kafka๋ฅผ ์ฌ์ฉํ๋ฉด ๋ง์ดํฌ๋ก์๋น์ค์ ํ์ฅ์ฑ ์ ๊ทน๋ํํ ์ ์์ต๋๋ค. ์๋น์ค๋ ๋ ๋ฆฝ์ ์ผ๋ก ์ค์ผ์ผ ์ /๋ค์ด ํ ์ ์์ผ๋ฉฐ, ๊ฐ ์๋น์ค๋ ํ์์ ๋ฐ๋ผ ๊ฐ๋ณ์ ์ผ๋ก ํ์ฅํ ์ ์์ต๋๋ค.
Kafka๋ก ํ์ฅ ๊ฐ๋ฅํ ๋ง์ดํฌ๋ก์๋น์ค ์์
@Configuration
public class KafkaConfig {
@Bean
public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConcurrency(3); // ๋์์ 3๊ฐ์ ์ค๋ ๋๋ก ์ด๋ฒคํธ ์ฒ๋ฆฌ
return factory;
}
}
์ค๋ช :
- ConcurrentKafkaListenerContainerFactory ๋ฅผ ํตํด ๋์์ฑ ์ ์ค์ ํ๊ณ , ์ฌ๋ฌ ์ค๋ ๋์์ ๋์์ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋๋ก ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
์ด๋ฒคํธ ์ฃผ๋ํ ๋ง์ดํฌ๋ก์๋น์ค ํธ๋ ๋ ๋ฐ ํต๊ณ
์ด๋ฒคํธ ์ฃผ๋ํ ์ํคํ ์ฒ๋ ๋๊ท๋ชจ ์์คํ ์์ ํนํ ํจ๊ณผ์ ์ ๋๋ค. Gartner ์ ์ฐ๊ตฌ์ ๋ฐ๋ฅด๋ฉด, ์ด๋ฒคํธ ์ฃผ๋ํ ์ํคํ ์ฒ๋ฅผ ์ฑํํ ๊ธฐ์ ๋ค์ ์๋น์ค ์ค๋จ ์๊ฐ ์ 40% ์ด์ ๊ฐ์ ์์ผฐ์ผ๋ฉฐ, ์๋น์ค ๊ฐ์ ์์กด์ฑ ์ ์ค์ฌ ๊ฐ๋ฐ ์๋ ๋ฅผ ํ๊ท ์ ์ผ๋ก 30% ํฅ์์์ผฐ์ต๋๋ค.
์์ฃผ ๋ฌป๋ ์ง๋ฌธ (FAQ)
Q1. Kafka๋ฅผ ์ฌ์ฉํ ์ด๋ฒคํธ ์ฃผ๋ํ ์ํคํ
์ฒ์ ์ฅ์ ์ ๋ฌด์์ธ๊ฐ์?
A1. Kafka ๋ ์๋น์ค ๊ฐ ๋น๋๊ธฐ ํต์ ์ ๊ฐ๋ฅํ๊ฒ ํ๋ฉฐ, ๋๊ท๋ชจ ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค. ๋ํ, ์๋น์ค ๊ฐ ๊ฒฐํฉ๋ ๋ฅผ ๋ฎ์ถ๊ณ , ํ์ฅ์ฑ ์ ๊ทน๋ํํ ์ ์์ต๋๋ค.
Q2. ์ด๋ฒคํธ ์์ฑ๊ณผ CQRS ํจํด์ ์ด๋ป๊ฒ ์ ์ฉํ๋์?
A2. ์ด๋ฒคํธ ์์ฑ ์ ์ํ๋ฅผ ์ด๋ฒคํธ์ ์ฐ์์ผ๋ก ์ ์ฅํ๋ฉฐ, CQRS ๋ ์ฐ๊ธฐ์ ์กฐํ ์์
์ ๋ถ๋ฆฌํ์ฌ ์ฑ๋ฅ์ ์ต์ ํํ๋ ํจํด์
๋๋ค. Kafka๋ ์ด๋ฌํ ํจํด์ ๊ตฌํํ๋ ๋ฐ ์ ํฉํ ๋ฉ์์ง ์์คํ
์
๋๋ค.
Q3. Kafka๋ก ์๋น์ค ๊ฐ ํต์ ์ ์ฒ๋ฆฌํ๋ฉด ์ด๋ค ์ด์ ์ด ์๋์?
A3. Kafka๋ฅผ ์ฌ์ฉํ๋ฉด ์๋น์ค ๊ฐ ํต์ ์ ๋น๋๊ธฐ ๋ฐฉ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ด ์ฑ๋ฅ๊ณผ ํ์ฅ์ฑ์ด ๋์์ง๋๋ค. ๋ํ, ํธ๋ํฝ์ด ๋ง์ ์์คํ
์์๋ ์์ ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ ์ ์์ต๋๋ค.
Q4. Kafka๋ฅผ ์ฌ์ฉํ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ
์ฒ์์ ํ์ฅ์ ์ด๋ป๊ฒ ์ด๋ฃจ์ด์ง๋์?
A4. Kafka๋ ํํฐ์
๋ ๊ณผ ๋ณต์ ๋ฅผ ํตํด ๋๊ท๋ชจ ํธ๋ํฝ์ ์ฒ๋ฆฌํ ์ ์์ผ๋ฉฐ, ์๋น์ค๋ ๋
๋ฆฝ์ ์ผ๋ก ์ค์ผ์ผ ํ ์ ์์ด ์์คํ
์ ๋ฐ์ ํ์ฅ์ฑ์ ํฌ๊ฒ ๋์ผ ์ ์์ต๋๋ค.
Q5. ์ด๋ฒคํธ ์ฃผ๋ํ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ
์ฒ์์ ์ฅ์ ์ฒ๋ฆฌ ๋ฐฉ๋ฒ์ ๋ฌด์์ธ๊ฐ์?
A5. Kafka๋ ๋ด์ฅ๋ ์ฅ์ ๋ณต๊ตฌ ๋ฉ์ปค๋์ฆ ์ ์ ๊ณตํ์ฌ ์ด๋ฒคํธ ์์ค ์์ด ๋ฐ์ดํฐ๋ฅผ ๋ณต๊ตฌํ ์ ์์ต๋๋ค. ๋ํ, ๊ฐ ์๋น์ค๋ ๋
๋ฆฝ์ ์ผ๋ก ์ฅ์ ๋ณต๊ตฌ๋ฅผ ํ ์ ์์ด, ์์คํ
์ ๋ฐ์ ์ ๋ขฐ์ฑ ์ ๋์ผ ์ ์์ต๋๋ค.
๋๊ธ