Ver3.0 ๐ ์ค์๊ฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ์ Apache Kafka๋ฅผ ํ์ฉํ ์คํธ๋ฆฌ๋ฐ: Python์ผ๋ก ๊ตฌํํ๋ ํ๋์ ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ

๐ ์ค์๊ฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ์ Apache Kafka๋ฅผ ํ์ฉํ ์คํธ๋ฆฌ๋ฐ: Python์ผ๋ก ๊ตฌํํ๋ ํ๋์ ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ
์น๊ตฌ์ฒ๋ผ ์ฝ๊ฒ ๋ฐฐ์ฐ๋ ์นดํ์นด์ ์ค์๊ฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ์ ๋ชจ๋ ๊ฒ ๐ฌ
๐ก ์ด ๊ธ์ ์ฝ์ผ๋ฉด ์ค์๊ฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ๊ฐ ์ ํ์ํ์ง, Apache Kafka๊ฐ ๋ฌด์์ธ์ง, ๊ทธ๋ฆฌ๊ณ Python์ผ๋ก ์ด๋ป๊ฒ ๊ตฌํํ๋์ง ์๋ฒฝํ๊ฒ ์ดํดํ ์ ์์ด! ๋ทํ๋ฆญ์ค, ์ฐ๋ฒ, ๋งํฌ๋์ธ ๊ฐ์ ๊ธ๋ก๋ฒ ๊ธฐ์ ๋ค์ด ์ ์นดํ์นด๋ฅผ ์ ํํ๋์ง๋ ์๊ฒ ๋ ๊ฑฐ์ผ. ๐
๐ฏ ์ค์๊ฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ, ์ ์ด๋ ๊ฒ ์ค์ํด์ก์๊น?
์น๊ตฌ์ผ, ์์ฆ ์ธ์์ด ์ผ๋ง๋ ๋น ๋ฅด๊ฒ ๋ณํ๋์ง ๋๊ปด์ง์ง? ๐โโ๏ธ
์ฐ๋ฆฌ๊ฐ ์ค๋งํธํฐ์ผ๋ก ๋ฐฐ๋ฌ ์ฑ์ ์ผ๋ ์๊ฐ, ์๋ง์ ๋ฐ์ดํฐ๊ฐ ์ค์๊ฐ์ผ๋ก ์ค๊ฐ๊ณ ์์ด. ๋ด ์์น, ์ฃผ๋ฌธ ์ ๋ณด, ๋ฐฐ๋ฌ์์ ์์น, ์์ ๋์ฐฉ ์๊ฐ... ์ด ๋ชจ๋ ๊ฒ ์ค์๊ฐ์ผ๋ก ์ฒ๋ฆฌ๋๊ณ ์๋ค๋ ๊ฑฐ ์๊ณ ์์์ด?
์ ํต์ ์ธ ๋ฐฐ์น ์ฒ๋ฆฌ(Batch Processing) ๋ฐฉ์์ ํ๋ฃจ์ ํ ๋ฒ์ฉ ๋ฐ์ดํฐ๋ฅผ ๋ชจ์์ ์ฒ๋ฆฌํ์ด. ๋ง์น ์ฐํธ๋ฌผ์ ๋ชจ์๋๋ค๊ฐ ํ๊บผ๋ฒ์ ์ ๋ฆฌํ๋ ๊ฒ์ฒ๋ผ ๋ง์ด์ผ. ๐ฌ ํ์ง๋ง ์ง๊ธ์ ๋ค๋ฅด์ง! ๊ณ ๊ฐ๋ค์ "์ง๊ธ ๋น์ฅ" ์ ๋ณด๋ฅผ ์ํด. ์ค์๊ฐ ์ถ์ฒ, ์ค์๊ฐ ์๋ฆผ, ์ค์๊ฐ ๋ถ์... ๋ชจ๋ ๊ฒ ์ฆ๊ฐ์ ์ด์ด์ผ ํด.
๐ ์ค์๊ฐ ์ฒ๋ฆฌ vs ๋ฐฐ์น ์ฒ๋ฆฌ
๋ฐฐ์น ์ฒ๋ฆฌ: ๋ฐ์ดํฐ๋ฅผ ์ผ์ ๊ธฐ๊ฐ ๋์ ๋ชจ์์ ํ๊บผ๋ฒ์ ์ฒ๋ฆฌ (์: ๋งค์ผ ๋ฐค 12์์ ์ผ๊ด ์ฒ๋ฆฌ)
์ค์๊ฐ ์ฒ๋ฆฌ: ๋ฐ์ดํฐ๊ฐ ๋ฐ์ํ๋ ์ฆ์ ์ฒ๋ฆฌ (์: ์ฃผ๋ฌธ์ด ๋ค์ด์ค๋ฉด ๋ฐ๋ก ์ฒ๋ฆฌ)
์ค์๊ฐ ์ฒ๋ฆฌ์ ์ฅ์ ์ ๋ช
ํํด:
โ
์ฆ๊ฐ์ ์ธ ์์ฌ๊ฒฐ์ ๊ฐ๋ฅ
โ
์ฌ์ฉ์ ๊ฒฝํ ํฅ์
โ
์ด์ ์งํ ๋น ๋ฅธ ๊ฐ์ง
โ
๋น์ฆ๋์ค ๊ธฐํ ํฌ์ฐฉ
์๋ฅผ ๋ค์ด๋ณผ๊ฒ. ์ ์ฉ์นด๋ ์ฌ๊ธฐ ํ์ง ์์คํ
์ ์๊ฐํด๋ด. ๐ฆ
๋ฐฐ์น ์ฒ๋ฆฌ ๋ฐฉ์์ด๋ผ๋ฉด ํ๋ฃจ๊ฐ ์ง๋์์ผ "์ด์ ๋น์ ์นด๋๋ก ์ด์ํ ๊ฑฐ๋๊ฐ ์์์ด์"๋ผ๊ณ ์๋ ค์ฃผ๊ฒ ์ง. ํ์ง๋ง ์ค์๊ฐ ์ฒ๋ฆฌ๋ผ๋ฉด? ์นด๋๊ฐ ๊ธํ๋ ๊ทธ ์๊ฐ์ ์ด์ ํจํด์ ๊ฐ์งํ๊ณ ์ฆ์ ์ฐจ๋จํ ์ ์์ด! ์ด๊ฒ ๋ฐ๋ก ์ค์๊ฐ ์ฒ๋ฆฌ์ ํ์ด์ผ. ๐ช
๐ช Apache Kafka, ๋ ๋๊ตฌ๋?
์, ์ด์ ์ค๋์ ์ฃผ์ธ๊ณต Apache Kafka๋ฅผ ์๊ฐํ ๊ฒ! ๐
์นดํ์นด๋ 2011๋
์ ๋งํฌ๋์ธ(LinkedIn)์์ ๊ฐ๋ฐํ ๋ถ์ฐ ์คํธ๋ฆฌ๋ฐ ํ๋ซํผ์ด์ผ. ์๋๋ ๋งํฌ๋์ธ ๋ด๋ถ์์ ์ฌ์ฉํ๋ ค๊ณ ๋ง๋ค์๋๋ฐ, ๋๋ฌด ์ข์์ ์คํ์์ค๋ก ๊ณต๊ฐํ์ง.
์นดํ์นด๋ผ๋ ์ด๋ฆ์ ์ฒด์ฝ ์๊ฐ ํ๋์ธ ์นดํ์นด(Franz Kafka)์์ ๋ฐ์์ด. ์ ์๊ฐ ์ด๋ฆ์ ๋ถ์๋๊ณ ? ๊ฐ๋ฐ์๋ค์ ์ผ์ค์ธ๋ฐ, "๊ธ์ฐ๊ธฐ ์์คํ "์ด๋ผ๋ ์๋ฏธ๋ฅผ ๋ด๊ณ ์ถ์๋. ๋ฐ์ดํฐ๋ฅผ ์ฐ๊ณ (write) ์ฝ๋(read) ์์คํ ์ด๋๊น ๋ง์ด์ผ. ๐
๐ก ์นดํ์นด๊ฐ ํด๊ฒฐํ๋ ๋ฌธ์ ๋ค
1. ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ์ ๋ณต์ก์ฑ
์์ ์๋ ์์คํ
A์์ ์์คํ
B๋ก, B์์ C๋ก... ์ด๋ ๊ฒ ์ผ์ผ์ด ์ฐ๊ฒฐํด์ผ ํ์ด. ์์คํ
์ด 10๊ฐ๋ฉด? 45๊ฐ์ ์ฐ๊ฒฐ์ด ํ์ํด! (nร(n-1)/2) ๐ฑ
์นดํ์นด๋ ์ค์ ํ๋ธ ์ญํ ์ ํด์ ๋ชจ๋ ์์คํ
์ด ์นดํ์นด๋ง ๋ฐ๋ผ๋ณด๋ฉด ๋ผ. ํจ์ฌ ๊ฐ๋จํ์ง?
2. ๋ฐ์ดํฐ ์์ค ๋ฐฉ์ง
์์คํ
์ด ๋ค์ด๋๋ฉด? ๋ฐ์ดํฐ๊ฐ ๋ ์๊ฐ๋ฒ๋ ค. ๐ญ
์นดํ์นด๋ ๋ฐ์ดํฐ๋ฅผ ๋์คํฌ์ ์ ์ฅํ๊ณ ๋ณต์ ํด์ ์์ ํ๊ฒ ๋ณด๊ดํด.
3. ํ์ฅ์ฑ ๋ฌธ์
ํธ๋ํฝ์ด ๊ฐ์๊ธฐ 10๋ฐฐ ์ฆ๊ฐํ๋ฉด? ๊ธฐ์กด ์์คํ
์ ๋ฒํฐ๊ธฐ ํ๋ค์ด.
์นดํ์นด๋ ์๋ฒ๋ฅผ ์ถ๊ฐํ๋ ๊ฒ๋ง์ผ๋ก ์ฝ๊ฒ ํ์ฅํ ์ ์์ด! ๐
๐๏ธ ์นดํ์นด์ ํต์ฌ ๊ตฌ์ฑ ์์
์นดํ์นด๋ฅผ ์ดํดํ๋ ค๋ฉด ๋ช ๊ฐ์ง ํต์ฌ ๊ฐ๋ ์ ์์์ผ ํด. ์ด๋ ต์ง ์์ผ๋ ์ฒ์ฒํ ๋ฐ๋ผ์๋ด! ๐
๐ฎ 1. Producer (ํ๋ก๋์)
๋ฐ์ดํฐ๋ฅผ ์์ฑํด์ ์นดํ์นด์ ๋ณด๋ด๋ ์ ์ผ. ์ฐ์ฒด๊ตญ์ ํธ์ง๋ฅผ ๋ณด๋ด๋ ์ฌ๋์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋ผ.
์: ์น ์๋ฒ๊ฐ ์ฌ์ฉ์ ํด๋ฆญ ์ด๋ฒคํธ๋ฅผ ์นดํ์นด๋ก ์ ์ก
๐ฌ 2. Topic (ํ ํฝ)
๋ฐ์ดํฐ๊ฐ ์ ์ฅ๋๋ ์นดํ
๊ณ ๋ฆฌ์ผ. ์ฐ์ฒด๊ตญ์ ์ฐํธํจ ๊ฐ์ ๊ฑฐ์ง.
์: "user-clicks", "order-events", "sensor-data" ๊ฐ์ ์ด๋ฆ์ผ๋ก ๊ตฌ๋ถํด
๐ฆ 3. Partition (ํํฐ์ )
ํ ํฝ์ ์ฌ๋ฌ ์กฐ๊ฐ์ผ๋ก ๋๋ ๊ฑฐ์ผ. ๋ณ๋ ฌ ์ฒ๋ฆฌ๋ฅผ ์ํด ํ์ํด.
์: "user-clicks" ํ ํฝ์ 3๊ฐ์ ํํฐ์
์ผ๋ก ๋๋๋ฉด 3๋ฐฐ ๋น ๋ฅด๊ฒ ์ฒ๋ฆฌ ๊ฐ๋ฅ!
๐จ 4. Consumer (์ปจ์๋จธ)
์นดํ์นด์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด๊ฐ๋ ์ ์ผ. ์ฐํธํจ์์ ํธ์ง๋ฅผ ๊ฐ์ ธ๊ฐ๋ ์ฌ๋.
์: Python ์ ํ๋ฆฌ์ผ์ด์
์ด ์นดํ์นด์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์ ๋ถ์
๐ฅ 5. Consumer Group (์ปจ์๋จธ ๊ทธ๋ฃน)
์ฌ๋ฌ ์ปจ์๋จธ๊ฐ ํ์ ์ด๋ค์ ์ผ์ ๋๋ ์ ์ฒ๋ฆฌํด. ํจ์จ์ ์ด์ง?
์: 3๋ช
์ ์ปจ์๋จธ๊ฐ ๊ฐ๊ฐ ๋ค๋ฅธ ํํฐ์
์ ๋ด๋น
๐ฅ๏ธ 6. Broker (๋ธ๋ก์ปค)
์นดํ์นด ์๋ฒ ๊ทธ ์์ฒด์ผ. ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ๊ด๋ฆฌํ๋ ์ญํ .
๋ณดํต ์ฌ๋ฌ ๋์ ๋ธ๋ก์ปค๋ฅผ ํด๋ฌ์คํฐ๋ก ๊ตฌ์ฑํด์ ์ฌ์ฉํด.
๐ Python์ผ๋ก ์นดํ์นด ์์ํ๊ธฐ
์ด์ ์ค์ ์ด์ผ! Python์ผ๋ก ์นดํ์นด๋ฅผ ์ด๋ป๊ฒ ์ฌ์ฉํ๋์ง ์์๋ณด์. ๐
Python์์ ์นดํ์นด๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด kafka-python ๋๋ confluent-kafka ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด. ์ค๋์ kafka-python์ผ๋ก ์ค๋ช
ํ ๊ฒ!
๐ฅ ์ค์นํ๊ธฐ
๋จผ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์นํด์ผ๊ฒ ์ง?
pip install kafka-python
์ฐธ๊ณ ๋ก, ์นดํ์นด ์์ฒด๋ ์ค์นํด์ผ ํด. ๋ก์ปฌ์์ ํ ์คํธํ๋ ค๋ฉด Apache Kafka๋ฅผ ๋ค์ด๋ก๋๋ฐ๊ฑฐ๋, Docker๋ฅผ ์ฌ์ฉํ๋ฉด ํจ์ฌ ํธํด! ๐ณ
Docker๋ก ์นดํ์นด ์คํํ๊ธฐ (๊ฐ๋จ ๋ฒ์ )
docker run -d --name kafka -p 9092:9092 \
-e KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 \
-e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9092 \
wurstmeister/kafka
โ๏ธ Producer ๋ง๋ค๊ธฐ
์, ์ด์ ๋ฐ์ดํฐ๋ฅผ ์นดํ์นด๋ก ๋ณด๋ด๋ ํ๋ก๋์๋ฅผ ๋ง๋ค์ด๋ณผ๊ฒ!
๊ฐ๋จํ ์์ ๋ก ์ฌ์ฉ์ ํด๋ฆญ ์ด๋ฒคํธ๋ฅผ ์ ์กํ๋ ํ๋ก๊ทธ๋จ์ ์์ฑํด๋ณด์. ๐ฑ๏ธ
from kafka import KafkaProducer
import json
import time
from datetime import datetime
# ํ๋ก๋์ ์์ฑ
producer = KafkaProducer(
bootstrap_servers=['localhost:9092'], # ์นดํ์นด ์๋ฒ ์ฃผ์
value_serializer=lambda v: json.dumps(v).encode('utf-8') # JSON์ผ๋ก ์ง๋ ฌํ
)
# ์ฌ์ฉ์ ํด๋ฆญ ์ด๋ฒคํธ ์ ์ก
def send_click_event(user_id, page, action):
event = {
'user_id': user_id,
'page': page,
'action': action,
'timestamp': datetime.now().isoformat()
}
# 'user-clicks' ํ ํฝ์ผ๋ก ์ ์ก
producer.send('user-clicks', value=event)
print(f"์ด๋ฒคํธ ์ ์ก ์๋ฃ: {event}")
# ํ
์คํธ: 10๊ฐ์ ํด๋ฆญ ์ด๋ฒคํธ ์ ์ก
for i in range(10):
send_click_event(
user_id=f"user_{i % 3}", # user_0, user_1, user_2 ์ํ
page=f"/page/{i % 5}",
action="click"
)
time.sleep(1) # 1์ด ๋๊ธฐ
# ๋ชจ๋ ๋ฉ์์ง๊ฐ ์ ์ก๋ ๋๊น์ง ๋๊ธฐ
producer.flush()
print("๋ชจ๋ ์ด๋ฒคํธ ์ ์ก ์๋ฃ!")
์ฝ๋๋ฅผ ํ๋์ฉ ๋ฏ์ด๋ณผ๊น? ๐
1. KafkaProducer ์์ฑ
bootstrap_servers: ์นดํ์นด ๋ธ๋ก์ปค ์ฃผ์๋ฅผ ์ง์ ํด. ์ฌ๋ฌ ๊ฐ๋ฉด ๋ฆฌ์คํธ๋ก ๋ฃ์ผ๋ฉด ๋ผ.
value_serializer: ๋ฐ์ดํฐ๋ฅผ ์ด๋ป๊ฒ ๋ณํํ ์ง ์ ์ํด. ์ฌ๊ธฐ์๋ JSON์ผ๋ก ๋ณํํ๊ณ UTF-8๋ก ์ธ์ฝ๋ฉํ์ด.
2. send() ๋ฉ์๋
์ฒซ ๋ฒ์งธ ์ธ์๋ ํ ํฝ ์ด๋ฆ, value๋ ์ ์กํ ๋ฐ์ดํฐ์ผ.
๋น๋๊ธฐ๋ก ๋์ํด์ ๋น ๋ฅด๊ฒ ์ฌ๋ฌ ๋ฉ์์ง๋ฅผ ๋ณด๋ผ ์ ์์ด!
3. flush() ๋ฉ์๋
๋ฒํผ์ ์๋ ๋ชจ๋ ๋ฉ์์ง๋ฅผ ๊ฐ์ ๋ก ์ ์กํด. ํ๋ก๊ทธ๋จ ์ข
๋ฃ ์ ์ ๊ผญ ํธ์ถํด์ค์ผ ํด!
๐ Consumer ๋ง๋ค๊ธฐ
์ด์ ์นดํ์นด์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์ค๋ ์ปจ์๋จธ๋ฅผ ๋ง๋ค์ด๋ณด์!
์ค์๊ฐ์ผ๋ก ๋ค์ด์ค๋ ํด๋ฆญ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ ํ๋ก๊ทธ๋จ์ด์ผ. ๐
from kafka import KafkaConsumer
import json
# ์ปจ์๋จธ ์์ฑ
consumer = KafkaConsumer(
'user-clicks', # ๊ตฌ๋
ํ ํ ํฝ
bootstrap_servers=['localhost:9092'],
auto_offset_reset='earliest', # ์ฒ์๋ถํฐ ์ฝ๊ธฐ
enable_auto_commit=True, # ์๋์ผ๋ก ์คํ์
์ปค๋ฐ
group_id='click-analyzer-group', # ์ปจ์๋จธ ๊ทธ๋ฃน ID
value_deserializer=lambda x: json.loads(x.decode('utf-8'))
)
print("ํด๋ฆญ ์ด๋ฒคํธ ์์ ๋๊ธฐ ์ค...")
# ๋ฉ์์ง ์์ ๋ฐ ์ฒ๋ฆฌ
for message in consumer:
event = message.value
print(f"\n[์ ์ด๋ฒคํธ ์์ ]")
print(f"์ฌ์ฉ์: {event['user_id']}")
print(f"ํ์ด์ง: {event['page']}")
print(f"์ก์
: {event['action']}")
print(f"์๊ฐ: {event['timestamp']}")
# ์ฌ๊ธฐ์ ์ค์ ๋น์ฆ๋์ค ๋ก์ง ์ฒ๋ฆฌ
# ์: ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ์ฅ, ๋ถ์, ์๋ฆผ ์ ์ก ๋ฑ
# ํน์ ์กฐ๊ฑด์ ๋ฐ๋ฅธ ์ฒ๋ฆฌ ์์
if event['action'] == 'click' and '/page/3' in event['page']:
print("โ ๏ธ ์ค์ ํ์ด์ง ํด๋ฆญ ๊ฐ์ง!")
์ปจ์๋จธ ์ฝ๋๋ ์ดํด๋ณด์! ๐ง
auto_offset_reset
'earliest': ํ ํฝ์ ์ฒ์๋ถํฐ ์ฝ๊ธฐ
'latest': ์ปจ์๋จธ๊ฐ ์์๋ ์ดํ์ ๋ฉ์์ง๋ง ์ฝ๊ธฐ
group_id
๊ฐ์ ๊ทธ๋ฃน ID๋ฅผ ๊ฐ์ง ์ปจ์๋จธ๋ค์ ํํฐ์
์ ๋๋ ์ ์ฒ๋ฆฌํด.
์: ํํฐ์
์ด 3๊ฐ๊ณ ์ปจ์๋จธ๊ฐ 3๊ฐ๋ฉด, ๊ฐ๊ฐ 1๊ฐ์ฉ ๋ด๋น!
enable_auto_commit
True๋ก ์ค์ ํ๋ฉด ๋ฉ์์ง๋ฅผ ์ฝ์ ํ ์๋์ผ๋ก ์คํ์
์ ์ปค๋ฐํด.
์คํ์
์ "์ด๋๊น์ง ์ฝ์๋์ง"๋ฅผ ๊ธฐ๋กํ๋ ํฌ์ธํฐ์ผ.
๐จ ์ค์ ์์ : ์ค์๊ฐ ์ฃผ๋ฌธ ์ฒ๋ฆฌ ์์คํ
์ด๋ก ๋ง ๋ฐฐ์ฐ๋ฉด ์ฌ๋ฏธ์์์? ์ค์ ๋ก ์ฌ์ฉํ ๋ฒํ ์์ ๋ฅผ ๋ง๋ค์ด๋ณด์! ๐
์จ๋ผ์ธ ์ผํ๋ชฐ์ ์ค์๊ฐ ์ฃผ๋ฌธ ์ฒ๋ฆฌ ์์คํ
์ ๊ตฌ์ถํด๋ณผ ๊ฑฐ์ผ. ์ฃผ๋ฌธ์ด ๋ค์ด์ค๋ฉด ์ฌ๊ณ ๋ฅผ ํ์ธํ๊ณ , ๊ฒฐ์ ๋ฅผ ์ฒ๋ฆฌํ๊ณ , ๋ฐฐ์ก ์ค๋น๋ฅผ ์์ํ๋ ์ ์ฒด ํ๋ก์ฐ๋ฅผ ์นดํ์นด๋ก ๊ตฌํํด๋ณด์!
๐ช ์์คํ ๊ตฌ์กฐ
ํ ํฝ ๊ตฌ์กฐ:
1๏ธโฃ orders: ์๋ก์ด ์ฃผ๋ฌธ ์ด๋ฒคํธ
2๏ธโฃ inventory-check: ์ฌ๊ณ ํ์ธ ์์ฒญ
3๏ธโฃ payment: ๊ฒฐ์ ์ฒ๋ฆฌ
4๏ธโฃ shipping: ๋ฐฐ์ก ์ค๋น
5๏ธโฃ notifications: ๊ณ ๊ฐ ์๋ฆผ
๐ ์ฃผ๋ฌธ ์์ฑ Producer
from kafka import KafkaProducer
import json
import random
from datetime import datetime
class OrderProducer:
def __init__(self):
self.producer = KafkaProducer(
bootstrap_servers=['localhost:9092'],
value_serializer=lambda v: json.dumps(v).encode('utf-8')
)
def create_order(self, user_id, product_id, quantity, price):
order = {
'order_id': f"ORD-{datetime.now().strftime('%Y%m%d%H%M%S')}-{random.randint(1000, 9999)}",
'user_id': user_id,
'product_id': product_id,
'quantity': quantity,
'price': price,
'total_amount': quantity * price,
'status': 'created',
'timestamp': datetime.now().isoformat()
}
# orders ํ ํฝ์ผ๋ก ์ ์ก
self.producer.send('orders', value=order)
print(f"โ
์ฃผ๋ฌธ ์์ฑ: {order['order_id']} - {order['product_id']} x {quantity}")
return order
def close(self):
self.producer.flush()
self.producer.close()
# ์ฌ์ฉ ์์
if __name__ == "__main__":
order_producer = OrderProducer()
# ํ
์คํธ ์ฃผ๋ฌธ ์์ฑ
products = [
('PROD-001', '๋
ธํธ๋ถ', 1500000),
('PROD-002', '๋ง์ฐ์ค', 30000),
('PROD-003', 'ํค๋ณด๋', 80000),
('PROD-004', '๋ชจ๋ํฐ', 350000)
]
for i in range(5):
product = random.choice(products)
order_producer.create_order(
user_id=f"USER-{random.randint(1, 100)}",
product_id=product[0],
quantity=random.randint(1, 3),
price=product[2]
)
order_producer.close()
๐ ์ฌ๊ณ ํ์ธ Consumer
from kafka import KafkaConsumer, KafkaProducer
import json
class InventoryChecker:
def __init__(self):
self.consumer = KafkaConsumer(
'orders',
bootstrap_servers=['localhost:9092'],
auto_offset_reset='latest',
group_id='inventory-checker-group',
value_deserializer=lambda x: json.loads(x.decode('utf-8'))
)
self.producer = KafkaProducer(
bootstrap_servers=['localhost:9092'],
value_serializer=lambda v: json.dumps(v).encode('utf-8')
)
# ๊ฐ๋จํ ์ฌ๊ณ DB (์ค์ ๋ก๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฌ์ฉ)
self.inventory = {
'PROD-001': 10,
'PROD-002': 50,
'PROD-003': 30,
'PROD-004': 15
}
def check_inventory(self, order):
product_id = order['product_id']
quantity = order['quantity']
if product_id in self.inventory and self.inventory[product_id] >= quantity:
# ์ฌ๊ณ ์ถฉ๋ถ
self.inventory[product_id] -= quantity
order['status'] = 'inventory_confirmed'
order['inventory_available'] = True
# payment ํ ํฝ์ผ๋ก ์ ๋ฌ
self.producer.send('payment', value=order)
print(f"โ
์ฌ๊ณ ํ์ธ ์๋ฃ: {order['order_id']} - ๋จ์ ์ฌ๊ณ : {self.inventory[product_id]}")
else:
# ์ฌ๊ณ ๋ถ์กฑ
order['status'] = 'inventory_failed'
order['inventory_available'] = False
# notifications ํ ํฝ์ผ๋ก ์ ๋ฌ
self.producer.send('notifications', value={
'type': 'inventory_shortage',
'order': order,
'message': '์ฃ์กํฉ๋๋ค. ์ฌ๊ณ ๊ฐ ๋ถ์กฑํฉ๋๋ค.'
})
print(f"โ ์ฌ๊ณ ๋ถ์กฑ: {order['order_id']}")
def start(self):
print("์ฌ๊ณ ํ์ธ ์๋น์ค ์์...")
for message in self.consumer:
order = message.value
self.check_inventory(order)
if __name__ == "__main__":
checker = InventoryChecker()
checker.start()
๐ณ ๊ฒฐ์ ์ฒ๋ฆฌ Consumer
from kafka import KafkaConsumer, KafkaProducer
import json
import random
import time
class PaymentProcessor:
def __init__(self):
self.consumer = KafkaConsumer(
'payment',
bootstrap_servers=['localhost:9092'],
auto_offset_reset='latest',
group_id='payment-processor-group',
value_deserializer=lambda x: json.loads(x.decode('utf-8'))
)
self.producer = KafkaProducer(
bootstrap_servers=['localhost:9092'],
value_serializer=lambda v: json.dumps(v).encode('utf-8')
)
def process_payment(self, order):
print(f"๐ณ ๊ฒฐ์ ์ฒ๋ฆฌ ์ค: {order['order_id']} - {order['total_amount']:,}์")
# ๊ฒฐ์ ์ฒ๋ฆฌ ์๋ฎฌ๋ ์ด์
(์ค์ ๋ก๋ PG์ฌ API ํธ์ถ)
time.sleep(1)
# 90% ํ๋ฅ ๋ก ์ฑ๊ณต
if random.random() < 0.9:
order['status'] = 'payment_completed'
order['payment_success'] = True
# shipping ํ ํฝ์ผ๋ก ์ ๋ฌ
self.producer.send('shipping', value=order)
# ๊ณ ๊ฐ์๊ฒ ์๋ฆผ
self.producer.send('notifications', value={
'type': 'payment_success',
'order': order,
'message': f"๊ฒฐ์ ๊ฐ ์๋ฃ๋์์ต๋๋ค. ๊ธ์ก: {order['total_amount']:,}์"
})
print(f"โ
๊ฒฐ์ ์ฑ๊ณต: {order['order_id']}")
else:
order['status'] = 'payment_failed'
order['payment_success'] = False
# ๊ณ ๊ฐ์๊ฒ ์๋ฆผ
self.producer.send('notifications', value={
'type': 'payment_failed',
'order': order,
'message': '๊ฒฐ์ ์ ์คํจํ์ต๋๋ค. ๋ค์ ์๋ํด์ฃผ์ธ์.'
})
print(f"โ ๊ฒฐ์ ์คํจ: {order['order_id']}")
def start(self):
print("๊ฒฐ์ ์ฒ๋ฆฌ ์๋น์ค ์์...")
for message in self.consumer:
order = message.value
self.process_payment(order)
if __name__ == "__main__":
processor = PaymentProcessor()
processor.start()
์ด๋ ๊ฒ ๊ฐ ๋จ๊ณ๋ฅผ ๋ ๋ฆฝ์ ์ธ ๋ง์ดํฌ๋ก์๋น์ค๋ก ๊ตฌ์ฑํ๋ฉด ์์ฒญ๋ ์ฅ์ ์ด ์์ด! ๐ฏ
๐ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ์ ์ฅ์
1. ๋
๋ฆฝ์ ํ์ฅ
๊ฒฐ์ ์ฒ๋ฆฌ๊ฐ ๋๋ฆฌ๋ค๋ฉด? ๊ฒฐ์ ์๋น์ค๋ง ์๋ฒ๋ฅผ ์ถ๊ฐํ๋ฉด ๋ผ!
2. ์ฅ์ ๊ฒฉ๋ฆฌ
์ฌ๊ณ ํ์ธ ์๋น์ค๊ฐ ๋ค์ด๋์ด๋ ์ฃผ๋ฌธ ์์ฑ์ ๊ณ์ ๋์ํด.
3. ๊ธฐ์ ์คํ ์์
์ฌ๊ณ ํ์ธ์ Python, ๊ฒฐ์ ๋ Java, ๋ฐฐ์ก์ Go๋ก ๊ฐ๋ฐํด๋ OK!
4. ํ ๋
๋ฆฝ์ฑ
๊ฐ ํ์ด ์๊ธฐ ์๋น์ค๋ง ์ฑ
์์ง๊ณ ๊ฐ๋ฐํ ์ ์์ด.
โก ์นดํ์นด์ ๊ณ ๊ธ ๊ธฐ๋ฅ๋ค
๊ธฐ๋ณธ์ ๋ง์คํฐํ์ผ๋, ์ด์ ์นดํ์นด์ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ๋ค์ ์์๋ณด์! ๐ช
์ค๋ฌด์์ ์ ๋ง ์ ์ฉํ ๊ธฐ๋ฅ๋ค์ด์ผ.
๐ ํํฐ์ ๊ณผ ๋ณ๋ ฌ ์ฒ๋ฆฌ
ํํฐ์ ์ ์นดํ์นด์ ํต์ฌ์ด์ผ! ํ ํฝ์ ์ฌ๋ฌ ํํฐ์ ์ผ๋ก ๋๋๋ฉด ๋ณ๋ ฌ๋ก ์ฒ๋ฆฌํ ์ ์์ด์ ์ฑ๋ฅ์ด ์์ฒญ๋๊ฒ ํฅ์๋ผ. ๐
ํํฐ์ ์ ๋ต
1. ๋ผ์ด๋ ๋ก๋น (Round Robin)
ํค๋ฅผ ์ง์ ํ์ง ์์ผ๋ฉด ๋ฉ์์ง๊ฐ ํํฐ์
์ ์์๋๋ก ๋ถ์ฐ๋ผ.
์: ๋ฉ์์ง1 โ ํํฐ์
0, ๋ฉ์์ง2 โ ํํฐ์
1, ๋ฉ์์ง3 โ ํํฐ์
2...
2. ํค ๊ธฐ๋ฐ ํํฐ์
๋
๊ฐ์ ํค๋ฅผ ๊ฐ์ง ๋ฉ์์ง๋ ํญ์ ๊ฐ์ ํํฐ์
์ผ๋ก ๊ฐ.
์: user_id๋ฅผ ํค๋ก ์ฌ์ฉํ๋ฉด ๊ฐ์ ์ฌ์ฉ์์ ์ด๋ฒคํธ๋ ์์๊ฐ ๋ณด์ฅ๋ผ!
# ํค๋ฅผ ์ง์ ํด์ ์ ์ก
producer.send(
'user-events',
key=user_id.encode('utf-8'), # ํค ์ง์
value=event
)
# ๊ฐ์ user_id๋ ํญ์ ๊ฐ์ ํํฐ์
์ผ๋ก!
# ์ด๋ ๊ฒ ํ๋ฉด ์ฌ์ฉ์๋ณ ์ด๋ฒคํธ ์์๊ฐ ๋ณด์ฅ๋ผ
๐ฏ ์ปจ์๋จธ ๊ทธ๋ฃน๊ณผ ๋ฆฌ๋ฐธ๋ฐ์ฑ
์ปจ์๋จธ ๊ทธ๋ฃน์ ์ ๋ง ๋๋ํด! ์ปจ์๋จธ๊ฐ ์ถ๊ฐ๋๊ฑฐ๋ ์ ๊ฑฐ๋๋ฉด ์๋์ผ๋ก ํํฐ์ ์ ์ฌ๋ถ๋ฐฐํด. ์ด๊ฑธ ๋ฆฌ๋ฐธ๋ฐ์ฑ์ด๋ผ๊ณ ํด. ๐
| ์ํฉ | ํํฐ์ 3๊ฐ | ๊ฒฐ๊ณผ |
|---|---|---|
| ์ปจ์๋จธ 1๊ฐ | P0, P1, P2 | 1๊ฐ๊ฐ ๋ชจ๋ ํํฐ์ ์ฒ๋ฆฌ |
| ์ปจ์๋จธ 2๊ฐ | C1: P0, P1 C2: P2 |
์๋์ผ๋ก ๋ถ๋ฐฐ๋จ |
| ์ปจ์๋จธ 3๊ฐ | C1: P0 C2: P1 C3: P2 |
1:1 ๋งค์นญ์ผ๋ก ์ต์ ํ |
| ์ปจ์๋จธ 4๊ฐ | C1: P0 C2: P1 C3: P2 C4: ๋๊ธฐ |
1๊ฐ๋ ๋๊ฒ ๋จ (ํํฐ์ ๋ณด๋ค ๋ง์) |
๊ทธ๋์ ํํฐ์ ๊ฐ์๋ฅผ ์ ํ ๋๋ ๋ฏธ๋์ ํ์ฅ์ฑ์ ๊ณ ๋ คํด์ผ ํด! ๋ณดํต ์์ ์ปจ์๋จธ ์์ 2~3๋ฐฐ๋ก ์ค์ ํ๋ ๊ฒ ์ข์. ๐
๐พ ์คํ์ ๊ด๋ฆฌ
์คํ์ ์ "์ด๋๊น์ง ์ฝ์๋์ง"๋ฅผ ๊ธฐ๋กํ๋ ํฌ์ธํฐ์ผ. ์นดํ์นด์ ํต์ฌ ๊ฐ๋ ์ค ํ๋์ง! ๐
์คํ์ ์ปค๋ฐ ์ ๋ต
1. ์๋ ์ปค๋ฐ (Auto Commit)
enable_auto_commit=True
์ผ์ ์๊ฐ๋ง๋ค ์๋์ผ๋ก ์ปค๋ฐ. ๊ฐ๋จํ์ง๋ง ๋ฉ์์ง ์์ค ๊ฐ๋ฅ์ฑ ์์.
2. ์๋ ์ปค๋ฐ (Manual Commit)
enable_auto_commit=False
์ฒ๋ฆฌ ์๋ฃ ํ ์ง์ ์ปค๋ฐ. ์์ ํ์ง๋ง ์ฝ๋๊ฐ ๋ณต์กํด์ง.
# ์๋ ์ปค๋ฐ ์์
consumer = KafkaConsumer(
'orders',
enable_auto_commit=False, # ์๋ ์ปค๋ฐ ๋นํ์ฑํ
group_id='order-processor'
)
for message in consumer:
try:
# ๋ฉ์์ง ์ฒ๋ฆฌ
process_order(message.value)
# ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ์ฅ
save_to_db(message.value)
# ์ฒ๋ฆฌ ์๋ฃ ํ ์ปค๋ฐ
consumer.commit()
print(f"โ
์คํ์
์ปค๋ฐ: {message.offset}")
except Exception as e:
print(f"โ ์ฒ๋ฆฌ ์คํจ: {e}")
# ์ปค๋ฐํ์ง ์์ผ๋ฉด ๋ค์์ ๋ค์ ์ฒ๋ฆฌ๋จ!
๐ ๋ฉ์์ง ๋ณด์ฅ ์์ค
์นดํ์นด๋ 3๊ฐ์ง ๋ฉ์์ง ์ ๋ฌ ๋ณด์ฅ ์์ค์ ์ ๊ณตํด. ์ํฉ์ ๋ง๊ฒ ์ ํํ๋ฉด ๋ผ! ๐๏ธ
| ๋ณด์ฅ ์์ค | ์ค๋ช | ์ฌ์ฉ ์ฌ๋ก |
|---|---|---|
| At Most Once (์ต๋ 1๋ฒ) |
๋ฉ์์ง๊ฐ ์์ค๋ ์ ์์ง๋ง ์ค๋ณต์ ์์ |
๋ก๊ทธ ์์ง, ๋ชจ๋ํฐ๋ง (์ฝ๊ฐ์ ์์ค ํ์ฉ) |
| At Least Once (์ต์ 1๋ฒ) |
๋ฉ์์ง ์์ค์ ์์ง๋ง ์ค๋ณต ๊ฐ๋ฅ |
์ฃผ๋ฌธ ์ฒ๋ฆฌ, ๊ฒฐ์ (์ค๋ณต ์ ๊ฑฐ ๋ก์ง ํ์) |
| Exactly Once (์ ํํ 1๋ฒ) |
์์ค๋ ์ค๋ณต๋ ์์ (๊ฐ์ฅ ์์ ) |
๊ธ์ต ๊ฑฐ๋, ์ฌ๊ณ ๊ด๋ฆฌ (์ ํ์ฑ์ด ์ค์) |
# Exactly Once ์ค์
producer = KafkaProducer(
bootstrap_servers=['localhost:9092'],
acks='all', # ๋ชจ๋ ๋ณต์ ๋ณธ์ด ํ์ธํ ๋๊น์ง ๋๊ธฐ
retries=3, # ์คํจ ์ ์ฌ์๋
enable_idempotence=True, # ๋ฉฑ๋ฑ์ฑ ํ์ฑํ (์ค๋ณต ๋ฐฉ์ง)
transactional_id='order-producer-1' # ํธ๋์ญ์
ID
)
# ํธ๋์ญ์
์์
producer.init_transactions()
producer.begin_transaction()
try:
# ์ฌ๋ฌ ๋ฉ์์ง๋ฅผ ์์์ ์ผ๋ก ์ ์ก
producer.send('orders', order1)
producer.send('inventory', inventory_update)
producer.send('notifications', notification)
# ๋ชจ๋ ์ฑ๊ณตํ๋ฉด ์ปค๋ฐ
producer.commit_transaction()
print("โ
ํธ๋์ญ์
์ปค๋ฐ ์๋ฃ")
except Exception as e:
# ํ๋๋ผ๋ ์คํจํ๋ฉด ๋กค๋ฐฑ
producer.abort_transaction()
print(f"โ ํธ๋์ญ์
๋กค๋ฐฑ: {e}")
๐ ๏ธ ์นดํ์นด ์ด์๊ณผ ๋ชจ๋ํฐ๋ง
์นดํ์นด๋ฅผ ํ๋ก๋์
ํ๊ฒฝ์์ ์ด์ํ๋ ค๋ฉด ๋ชจ๋ํฐ๋ง์ด ํ์์ผ! ๐
์์คํ
์ด ์ ๋์๊ฐ๋์ง, ๋ฌธ์ ๋ ์๋์ง ํญ์ ์ฒดํฌํด์ผ ํด.
๐ ์ฃผ์ ๋ชจ๋ํฐ๋ง ์งํ
| ์งํ | ์ค๋ช | ์ ์ ๋ฒ์ |
|---|---|---|
| Throughput | ์ด๋น ์ฒ๋ฆฌ ๋ฉ์์ง ์ | ๋ชฉํ์น์ 70% ์ดํ |
| Latency | ๋ฉ์์ง ์ ์ก/์์ ์ง์ฐ์๊ฐ | 100ms ์ดํ |
| Consumer Lag | ์ปจ์๋จธ๊ฐ ๋ค์ฒ์ง ๋ฉ์์ง ์ | 1000๊ฐ ์ดํ |
| Disk Usage | ๋ธ๋ก์ปค ๋์คํฌ ์ฌ์ฉ๋ฅ | 80% ์ดํ |
| Network I/O | ๋คํธ์ํฌ ์ ์ถ๋ ฅ ์๋ | ๋์ญํญ์ 70% ์ดํ |
๐จ Consumer Lag ๋ชจ๋ํฐ๋ง
Consumer Lag์ ๊ฐ์ฅ ์ค์ํ ์งํ์ผ! ์ปจ์๋จธ๊ฐ ํ๋ก๋์๋ฅผ ๋ฐ๋ผ๊ฐ์ง ๋ชปํ๋ฉด ์์คํ ์ด ๋๋ ค์ง๊ฑฐ๋ . ๐ฐ
from kafka import KafkaConsumer, TopicPartition
from kafka.structs import OffsetAndMetadata
def check_consumer_lag(topic, group_id):
consumer = KafkaConsumer(
bootstrap_servers=['localhost:9092'],
group_id=group_id
)
# ํ ํฝ์ ๋ชจ๋ ํํฐ์
๊ฐ์ ธ์ค๊ธฐ
partitions = consumer.partitions_for_topic(topic)
total_lag = 0
for partition in partitions:
tp = TopicPartition(topic, partition)
# ํ์ฌ ์คํ์
(์ปจ์๋จธ๊ฐ ์ฝ์ ์์น)
committed = consumer.committed(tp)
current_offset = committed if committed else 0
# ์ต์ ์คํ์
(ํ๋ก๋์๊ฐ ์ด ์์น)
consumer.seek_to_end(tp)
end_offset = consumer.position(tp)
# Lag ๊ณ์ฐ
lag = end_offset - current_offset
total_lag += lag
print(f"ํํฐ์
{partition}: Lag = {lag:,} ๋ฉ์์ง")
print(f"\n์ด Lag: {total_lag:,} ๋ฉ์์ง")
# ๊ฒฝ๊ณ ์๊ณ๊ฐ
if total_lag > 10000:
print("โ ๏ธ ๊ฒฝ๊ณ : Consumer Lag์ด ๋๋ฌด ๋์ต๋๋ค!")
print("โ ์ปจ์๋จธ ์ธ์คํด์ค๋ฅผ ์ถ๊ฐํ์ธ์.")
elif total_lag > 1000:
print("โก ์ฃผ์: Consumer Lag์ด ์ฆ๊ฐํ๊ณ ์์ต๋๋ค.")
else:
print("โ
์ ์: Consumer Lag์ด ์์ ์ ์
๋๋ค.")
consumer.close()
# ์ฌ์ฉ ์์
check_consumer_lag('orders', 'order-processor-group')
๐ง ์ฑ๋ฅ ํ๋ ํ
ํ๋ก๋์ ์ต์ ํ
1. ๋ฐฐ์น ์ฒ๋ฆฌ ํ์ฑํ
batch_size=16384: ๋ฉ์์ง๋ฅผ ๋ชจ์์ ํ๋ฒ์ ์ ์ก
linger_ms=10: 10ms ๋์ ๋ฉ์์ง๋ฅผ ๋ชจ์
2. ์์ถ ์ฌ์ฉ
compression_type='snappy': ๋คํธ์ํฌ ๋์ญํญ ์ ์ฝ
snappy, gzip, lz4 ์ค ์ ํ ๊ฐ๋ฅ
3. ๋ฒํผ ํฌ๊ธฐ ์กฐ์
buffer_memory=33554432: 32MB ๋ฒํผ (๊ธฐ๋ณธ๊ฐ)
ํธ๋ํฝ์ด ๋ง์ผ๋ฉด ๋ ํฌ๊ฒ ์ค์
# ์ต์ ํ๋ ํ๋ก๋์ ์ค์
producer = KafkaProducer(
bootstrap_servers=['localhost:9092'],
# ๋ฐฐ์น ์ฒ๋ฆฌ
batch_size=32768, # 32KB
linger_ms=10, # 10ms ๋๊ธฐ
# ์์ถ
compression_type='snappy',
# ๋ฒํผ
buffer_memory=67108864, # 64MB
# ์ฌ์๋
retries=3,
retry_backoff_ms=100,
# ์ฑ๋ฅ
acks='1', # ๋ฆฌ๋๋ง ํ์ธ (๋น ๋ฆ)
max_in_flight_requests_per_connection=5
)
์ปจ์๋จธ ์ต์ ํ
1. ํ์น ํฌ๊ธฐ ์กฐ์
fetch_min_bytes=1024: ์ต์ 1KB ์ด์ ๋ชจ์ฌ์ผ ๊ฐ์ ธ์ด
fetch_max_wait_ms=500: ์ต๋ 500ms ๋๊ธฐ
2. ๋ณ๋ ฌ ์ฒ๋ฆฌ
ํํฐ์
์๋งํผ ์ปจ์๋จธ๋ฅผ ๋์์ ๋ณ๋ ฌ ์ฒ๋ฆฌ
๋ฉํฐ์ค๋ ๋ฉ/๋ฉํฐํ๋ก์ธ์ฑ ํ์ฉ
3. ์คํ์
์ปค๋ฐ ์ต์ ํ
auto_commit_interval_ms=5000: 5์ด๋ง๋ค ์ปค๋ฐ
๋๋ฌด ์์ฃผ ์ปค๋ฐํ๋ฉด ์ค๋ฒํค๋ ๋ฐ์
๐ ์ค์ ํ์ฉ ์ฌ๋ก
์ด๋ก ์ ์ถฉ๋ถํ ๋ฐฐ์ ์ผ๋, ์ค์ ๋ก ์ด๋ป๊ฒ ํ์ฉ๋๋์ง ๋ณผ๊น? ๐ฌ
๊ธ๋ก๋ฒ ๊ธฐ์
๋ค์ ์นดํ์นด ํ์ฉ ์ฌ๋ก๋ฅผ ์ดํด๋ณด์!
๐บ ๋ทํ๋ฆญ์ค (Netflix)
๋ทํ๋ฆญ์ค๋ ํ๋ฃจ์ ์์กฐ ๊ฐ์ ์ด๋ฒคํธ๋ฅผ ์นดํ์นด๋ก ์ฒ๋ฆฌํด! ๐ฑ
ํ์ฉ ๋ถ์ผ:
๐ฏ ์ค์๊ฐ ์ถ์ฒ ์์คํ
: ์ฌ์ฉ์๊ฐ ๋ฌด์์ ๋ณด๋์ง ์ค์๊ฐ์ผ๋ก ๋ถ์
๐ A/B ํ
์คํ
: ์๋ก์ด ๊ธฐ๋ฅ์ ํจ๊ณผ๋ฅผ ์ฆ์ ์ธก์
๐ ์ด์ ํ์ง: ์คํธ๋ฆฌ๋ฐ ํ์ง ๋ฌธ์ ๋ฅผ ์ค์๊ฐ์ผ๋ก ๊ฐ์ง
๐ ๋น์ฆ๋์ค ๋ฉํธ๋ฆญ: ์์ฒญ ์๊ฐ, ์ดํ๋ฅ ๋ฑ์ ์ค์๊ฐ์ผ๋ก ์ง๊ณ
๋ทํ๋ฆญ์ค๋ ์นดํ์นด ํด๋ฌ์คํฐ๋ฅผ ์ฌ๋ฌ ๊ฐ ์ด์ํ๋๋ฐ, ๊ฐ ํด๋ฌ์คํฐ๊ฐ ์ด๋น ์๋ฐฑ๋ง ๊ฐ์ ๋ฉ์์ง๋ฅผ ์ฒ๋ฆฌํ๋! ๐
๐ ์ฐ๋ฒ (Uber)
์ฐ๋ฒ๋ ์ค์๊ฐ ์์น ์ถ์ ๊ณผ ์๊ธ ๊ณ์ฐ์ ์นดํ์นด๋ฅผ ์ฌ์ฉํด.
ํ์ฉ ๋ถ์ผ:
๐ ์ค์๊ฐ ์์น ์ถ์ : ์ด์ ์์ ์น๊ฐ์ ์์น๋ฅผ ์ค์๊ฐ์ผ๋ก ๋งค์นญ
๐ฐ ๋์ ์๊ธ ์ฑ
์ : ์์์ ๊ณต๊ธ์ ๋ฐ๋ผ ์ค์๊ฐ์ผ๋ก ์๊ธ ์กฐ์
๐บ๏ธ ๊ฒฝ๋ก ์ต์ ํ: ์ค์๊ฐ ๊ตํต ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํด์ ์ต์ ๊ฒฝ๋ก ์ ๊ณต
๐ฑ ํธ์ ์๋ฆผ: "์ด์ ์๊ฐ 5๋ถ ๊ฑฐ๋ฆฌ์ ์์ต๋๋ค" ๊ฐ์ ์ค์๊ฐ ์๋ฆผ
์ฐ๋ฒ์ ์นดํ์นด ์์คํ ์ ์ ์ธ๊ณ ์๋ฐฑ๋ง ๋ช ์ ์ฌ์ฉ์๋ฅผ ์ค์๊ฐ์ผ๋ก ์ฐ๊ฒฐํ๊ณ ์์ด! ๐
๐ผ ๋งํฌ๋์ธ (LinkedIn)
์นดํ์นด๋ฅผ ๋ง๋ ํ์ฌ๋ต๊ฒ, ๋งํฌ๋์ธ์ ์นดํ์นด๋ฅผ ๊ฐ์ฅ ์ ๊ทน์ ์ผ๋ก ํ์ฉํด!
ํ์ฉ ๋ถ์ผ:
๐ฐ ๋ด์คํผ๋ ์์ฑ: ์๋ฐฑ๋ง ๋ช
์ ํ๋์ ์ค์๊ฐ์ผ๋ก ์ง๊ณ
๐ ์๋ฆผ ์์คํ
: "๋๊ฐ ๋น์ ์ ํ๋กํ์ ๋ดค์ต๋๋ค" ๊ฐ์ ์ค์๊ฐ ์๋ฆผ
๐ ๋ถ์ ํ์ดํ๋ผ์ธ: ์ฌ์ฉ์ ํ๋ ๋ฐ์ดํฐ๋ฅผ ์ค์๊ฐ์ผ๋ก ๋ถ์
๐ ๊ฒ์ ์ธ๋ฑ์ฑ: ์๋ก์ด ์ฝํ
์ธ ๋ฅผ ์ค์๊ฐ์ผ๋ก ๊ฒ์ ๊ฐ๋ฅํ๊ฒ ๋ง๋ฆ
๋งํฌ๋์ธ์ ํ๋ฃจ์ 1์กฐ ๊ฐ ์ด์์ ๋ฉ์์ง๋ฅผ ์นดํ์นด๋ก ์ฒ๋ฆฌํ๋! ๐ฒ
๐ ์ด์ปค๋จธ์ค ์ค์๊ฐ ์ฌ๊ณ ๊ด๋ฆฌ
์ค์ ๋ก ๊ตฌํํ ์ ์๋ ์์ ๋ฅผ ํ๋ ๋ ๋ง๋ค์ด๋ณผ๊น? ์ด์ปค๋จธ์ค์ ์ค์๊ฐ ์ฌ๊ณ ๊ด๋ฆฌ ์์คํ ์ด์ผ! ๐ช
from kafka import KafkaConsumer, KafkaProducer
import json
import redis
from datetime import datetime
class RealTimeInventoryManager:
def __init__(self):
# ์นดํ์นด ์ปจ์๋จธ (์ฃผ๋ฌธ ์ด๋ฒคํธ ์์ )
self.consumer = KafkaConsumer(
'order-events',
bootstrap_servers=['localhost:9092'],
group_id='inventory-manager',
value_deserializer=lambda x: json.loads(x.decode('utf-8'))
)
# ์นดํ์นด ํ๋ก๋์ (์ฌ๊ณ ์
๋ฐ์ดํธ ์๋ฆผ)
self.producer = KafkaProducer(
bootstrap_servers=['localhost:9092'],
value_serializer=lambda v: json.dumps(v).encode('utf-8')
)
# Redis (์ค์๊ฐ ์ฌ๊ณ ์บ์)
self.redis_client = redis.Redis(host='localhost', port=6379, db=0)
def get_current_stock(self, product_id):
"""ํ์ฌ ์ฌ๊ณ ์กฐํ"""
stock = self.redis_client.get(f"stock:{product_id}")
return int(stock) if stock else 0
def update_stock(self, product_id, quantity_change):
"""์ฌ๊ณ ์
๋ฐ์ดํธ"""
current = self.get_current_stock(product_id)
new_stock = current + quantity_change
# Redis ์
๋ฐ์ดํธ
self.redis_client.set(f"stock:{product_id}", new_stock)
# ์ฌ๊ณ ๋ณ๊ฒฝ ์ด๋ฒคํธ ๋ฐํ
event = {
'product_id': product_id,
'previous_stock': current,
'new_stock': new_stock,
'change': quantity_change,
'timestamp': datetime.now().isoformat()
}
self.producer.send('inventory-updates', value=event)
# ์ฌ๊ณ ๋ถ์กฑ ๊ฒฝ๊ณ
if new_stock < 10:
self.producer.send('inventory-alerts', value={
'type': 'low_stock',
'product_id': product_id,
'current_stock': new_stock,
'message': f'์ฌ๊ณ ๋ถ์กฑ ๊ฒฝ๊ณ ! ํ์ฌ ์ฌ๊ณ : {new_stock}๊ฐ'
})
print(f"โ ๏ธ ์ฌ๊ณ ๋ถ์กฑ ๊ฒฝ๊ณ : {product_id} - {new_stock}๊ฐ")
return new_stock
def process_order(self, order):
"""์ฃผ๋ฌธ ์ฒ๋ฆฌ"""
product_id = order['product_id']
quantity = order['quantity']
print(f"\n๐ฆ ์ฃผ๋ฌธ ์ฒ๋ฆฌ: {order['order_id']}")
print(f" ์ํ: {product_id}, ์๋: {quantity}")
# ์ฌ๊ณ ์ฐจ๊ฐ
new_stock = self.update_stock(product_id, -quantity)
print(f" ์ฌ๊ณ ์
๋ฐ์ดํธ: {new_stock}๊ฐ ๋จ์")
# ์ค์๊ฐ ๋์๋ณด๋ ์
๋ฐ์ดํธ
self.producer.send('dashboard-updates', value={
'type': 'stock_change',
'product_id': product_id,
'stock': new_stock,
'timestamp': datetime.now().isoformat()
})
def start(self):
"""์๋น์ค ์์"""
print("๐ ์ค์๊ฐ ์ฌ๊ณ ๊ด๋ฆฌ ์์คํ
์์...")
print("์ฃผ๋ฌธ ์ด๋ฒคํธ ๋๊ธฐ ์ค...\n")
for message in self.consumer:
order = message.value
if order['type'] == 'order_created':
self.process_order(order)
elif order['type'] == 'order_cancelled':
# ์ฃผ๋ฌธ ์ทจ์ ์ ์ฌ๊ณ ๋ณต๊ตฌ
self.update_stock(order['product_id'], order['quantity'])
print(f"โฉ๏ธ ์ฃผ๋ฌธ ์ทจ์: ์ฌ๊ณ ๋ณต๊ตฌ ์๋ฃ")
# ์คํ
if __name__ == "__main__":
manager = RealTimeInventoryManager()
manager.start()
์ด ์์คํ ์ ์ฅ์ ์ ๋ญ๊น? ๐ค
1. ์ค์๊ฐ ์ฌ๊ณ ๋๊ธฐํ ์ฌ๋ฌ ์ฑ๋(์น, ์ฑ, ์คํ๋ผ์ธ)์์ ๋์์ ์ฃผ๋ฌธ์ด ๋ค์ด์๋ ์ฌ๊ณ ๊ฐ ์ ํํ๊ฒ ๊ด๋ฆฌ๋ผ!
2. ํ์ฅ์ฑ
์ฃผ๋ฌธ์ด ํญ์ฆํด๋ ์นดํ์นด๊ฐ ๋ฒํผ ์ญํ ์ ํด์ ์์คํ
์ด ์์ ์ ์ด์ผ.
3. ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํคํ
์ฒ
์ฌ๊ณ ๋ณ๊ฒฝ ์ด๋ฒคํธ๋ฅผ ๊ตฌ๋
ํ๋ ๋ค๋ฅธ ์์คํ
๋ค์ด ์๋์ผ๋ก ๋ฐ์ํด.
์: ์ฌ๊ณ ๋ถ์กฑ โ ์๋ ๋ฐ์ฃผ, ๋์๋ณด๋ ์
๋ฐ์ดํธ, ๊ณ ๊ฐ ์๋ฆผ ๋ฑ
4. ๊ฐ์ฌ ์ถ์
๋ชจ๋ ์ฌ๊ณ ๋ณ๊ฒฝ ์ด๋ ฅ์ด ์นดํ์นด์ ์ ์ฅ๋์ด ๋์ค์ ๋ถ์ ๊ฐ๋ฅ!
๐ ์นดํ์นด ํ์ต ๋ก๋๋งต
์นดํ์นด๋ฅผ ๋ง์คํฐํ๊ณ ์ถ๋ค๋ฉด? ๋จ๊ณ๋ณ๋ก ํ์ตํด๋ณด์! ๐
์ด๋ณด์๋ถํฐ ์ ๋ฌธ๊ฐ๊น์ง, ์ฒด๊ณ์ ์ธ ํ์ต ๊ฒฝ๋ก๋ฅผ ์๋ ค์ค๊ฒ.
๐ฑ ์ด๊ธ ๋จ๊ณ (1-2๊ฐ์)
ํ์ต ๋ชฉํ: ์นดํ์นด์ ๊ธฐ๋ณธ ๊ฐ๋ ์ดํดํ๊ณ ๊ฐ๋จํ ํ๋ก๋์/์ปจ์๋จธ ์์ฑ
โ
์นดํ์นด ์ํคํ
์ฒ ์ดํด
โ
๋ก์ปฌ ํ๊ฒฝ์ ์นดํ์นด ์ค์น
โ
Python์ผ๋ก ๊ฐ๋จํ ํ๋ก๋์/์ปจ์๋จธ ์์ฑ
โ
ํ ํฝ, ํํฐ์
, ์คํ์
๊ฐ๋
์ดํด
โ
๊ธฐ๋ณธ์ ์ธ ์ค์ ํ๋ผ๋ฏธํฐ ํ์ต
์ค์ต ํ๋ก์ ํธ: ๊ฐ๋จํ ๋ก๊ทธ ์์ง ์์คํ ๋ง๋ค๊ธฐ
๐ฟ ์ค๊ธ ๋จ๊ณ (2-3๊ฐ์)
ํ์ต ๋ชฉํ: ์ค๋ฌด์์ ์ฌ์ฉํ ์ ์๋ ์์ค์ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ
โ
์ปจ์๋จธ ๊ทธ๋ฃน๊ณผ ๋ฆฌ๋ฐธ๋ฐ์ฑ ์ดํด
โ
์คํ์
๊ด๋ฆฌ ์ ๋ต ํ์ต
โ
๋ฉ์์ง ๋ณด์ฅ ์์ค ์ค์
โ
์๋ฌ ์ฒ๋ฆฌ์ ์ฌ์๋ ๋ก์ง ๊ตฌํ
โ
์ฑ๋ฅ ํ๋ ๊ธฐ์ด
โ
๋ชจ๋ํฐ๋ง ์งํ ์ดํด
์ค์ต ํ๋ก์ ํธ: ์ค์๊ฐ ์ฃผ๋ฌธ ์ฒ๋ฆฌ ์์คํ ๊ตฌ์ถ
๐ณ ๊ณ ๊ธ ๋จ๊ณ (3-6๊ฐ์)
ํ์ต ๋ชฉํ: ํ๋ก๋์ ํ๊ฒฝ์์ ์นดํ์นด ํด๋ฌ์คํฐ ์ด์
โ
์นดํ์นด ํด๋ฌ์คํฐ ๊ตฌ์ถ ๋ฐ ์ด์
โ
๋ณต์ (Replication)์ ๊ณ ๊ฐ์ฉ์ฑ ์ค์
โ
Kafka Streams API ํ์ฉ
โ
Kafka Connect๋ก ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ ๊ตฌ์ถ
โ
๋ณด์ ์ค์ (SSL, SASL)
โ
์ฑ๋ฅ ์ต์ ํ์ ํธ๋ฌ๋ธ์ํ
โ
์ฌํด ๋ณต๊ตฌ ์ ๋ต
์ค์ต ํ๋ก์ ํธ: ๋๊ท๋ชจ ์ค์๊ฐ ๋ถ์ ํ๋ซํผ ๊ตฌ์ถ
๐ ์ ๋ฌธ๊ฐ ๋จ๊ณ (6๊ฐ์+)
ํ์ต ๋ชฉํ: ์นดํ์นด ์ํ๊ณ ์ ์ฒด๋ฅผ ์ดํดํ๊ณ ์ํคํ ์ฒ ์ค๊ณ
โ
์นดํ์นด ๋ด๋ถ ๋์ ์๋ฆฌ ๊น์ด ์ดํด
โ
๋๊ท๋ชจ ํด๋ฌ์คํฐ ์ํคํ
์ฒ ์ค๊ณ
โ
๋ฉํฐ ๋ฐ์ดํฐ์ผํฐ ๋ณต์
โ
์นดํ์นด ์ปค์คํฐ๋ง์ด์ง (Interceptor, Serializer ๋ฑ)
โ
์นดํ์นด ์ํ๊ณ ๋๊ตฌ ํ์ฉ (Schema Registry, KSQL ๋ฑ)
โ
์ฑ๋ฅ ๋ฒค์น๋งํน๊ณผ ์ฉ๋ ๊ณํ
์ค์ต ํ๋ก์ ํธ: ๊ธ๋ก๋ฒ ๊ท๋ชจ์ ์ด๋ฒคํธ ์คํธ๋ฆฌ๋ฐ ํ๋ซํผ ์ค๊ณ
๐ก ์นดํ์นด vs ๋ค๋ฅธ ๋ฉ์์ง ์์คํ
์นดํ์นด๋ง ์๋ ๊ฑด ์๋์์? ๋ค๋ฅธ ๋ฉ์์ง ์์คํ
๊ณผ ๋น๊ตํด๋ณด์! ๐
์ํฉ์ ๋ฐ๋ผ ์ ํฉํ ๋๊ตฌ๊ฐ ๋ค๋ฅด๋๊น ๋ง์ด์ผ.
| ์์คํ | ํน์ง | ์ฅ์ | ๋จ์ | ์ ํฉํ ์ฌ์ฉ ์ฌ๋ก |
|---|---|---|---|---|
| Apache Kafka | ๋ถ์ฐ ์คํธ๋ฆฌ๋ฐ ํ๋ซํผ |
๋์ ์ฒ๋ฆฌ๋ ์๊ตฌ ์ ์ฅ ์์ ๋ณด์ฅ |
๋ณต์กํ ์ค์ ๋์ ๋ฆฌ์์ค ์ฌ์ฉ |
๋์ฉ๋ ๋ก๊ทธ ์์ง ์ด๋ฒคํธ ์์ฑ ์ค์๊ฐ ๋ถ์ |
| RabbitMQ | ๋ฉ์์ง ๋ธ๋ก์ปค | ์ ์ฐํ ๋ผ์ฐํ
๋ค์ํ ํ๋กํ ์ฝ ์ฌ์ด ์ค์ |
๋ฎ์ ์ฒ๋ฆฌ๋ ์๊ตฌ ์ ์ฅ ์ ํ |
์์
ํ ๋ง์ดํฌ๋ก์๋น์ค ํต์ RPC ํจํด |
| Redis Pub/Sub | ์ธ๋ฉ๋ชจ๋ฆฌ ๋ฉ์์ง |
๋งค์ฐ ๋น ๋ฆ ๊ฐ๋จํ ์ฌ์ฉ ๋ฎ์ ์ง์ฐ์๊ฐ |
์๊ตฌ ์ ์ฅ ์์ ๋ฉ์์ง ์์ค ๊ฐ๋ฅ |
์ค์๊ฐ ์ฑํ
์๋ฆผ ์์คํ ์บ์ ๋ฌดํจํ |
| AWS SQS | ๊ด๋ฆฌํ ํ ์๋น์ค |
์์ ๊ด๋ฆฌํ ๋ฌดํ ํ์ฅ ์ด์ ๋ถ๋ด ์์ |
์์ ๋ณด์ฅ ์ ํ ๋์ ๋น์ฉ AWS ์ข ์ |
AWS ํ๊ฒฝ ๋น๋๊ธฐ ์์ ์ฒ๋ฆฌ ๋ง์ดํฌ๋ก์๋น์ค |
| Apache Pulsar | ์ฐจ์ธ๋ ๋ฉ์์ง ์์คํ |
๋ฉํฐ ํ
๋์ ์ง์ค ๋ณต์ ์ ์ฐํ ๊ตฌ์กฐ |
์ํ๊ณ ์์ ๋ณต์กํ ์ํคํ ์ฒ |
๋๊ท๋ชจ ๋ฉํฐ ํ
๋ํธ ๊ธ๋ก๋ฒ ์๋น์ค |
๐ฏ ์ ํ ๊ฐ์ด๋
์นดํ์นด๋ฅผ ์ ํํด์ผ ํ ๋:
โ
์ด๋น ์๋ง~์๋ฐฑ๋ง ๊ฑด์ ๋ฉ์์ง ์ฒ๋ฆฌ
โ
๋ฉ์์ง๋ฅผ ์๊ตฌ์ ์ผ๋ก ์ ์ฅํ๊ณ ์ฌ์ฒ๋ฆฌ ํ์
โ
์์๊ฐ ์ค์ํ ์ด๋ฒคํธ ์คํธ๋ฆผ
โ
์ค์๊ฐ ๋ถ์๊ณผ ๋ฐฐ์น ๋ถ์์ ๋์์
โ
์ฌ๋ฌ ์ปจ์๋จธ๊ฐ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋
๋ฆฝ์ ์ผ๋ก ์ฒ๋ฆฌ
RabbitMQ๋ฅผ ์ ํํด์ผ ํ ๋:
โ
๋ณต์กํ ๋ผ์ฐํ
๊ท์น์ด ํ์
โ
์์
ํ ํจํด (ํ ๋ฒ๋ง ์ฒ๋ฆฌ)
โ
๋ค์ํ ํ๋กํ ์ฝ ์ง์ ํ์ (AMQP, MQTT ๋ฑ)
โ
์๋์ ์ผ๋ก ์ ์ ๋ฉ์์ง ์
Redis Pub/Sub์ ์ ํํด์ผ ํ ๋:
โ
๊ทน๋๋ก ๋ฎ์ ์ง์ฐ์๊ฐ ํ์
โ
๋ฉ์์ง ์์ค์ด ํ์ฉ๋จ
โ
๊ฐ๋จํ ๋ฐํ/๊ตฌ๋
ํจํด
โ
์ด๋ฏธ Redis๋ฅผ ์ฌ์ฉ ์ค
๐ ์นดํ์นด์ Python ์ํ๊ณ
Python์ผ๋ก ์นดํ์นด๋ฅผ ์ฌ์ฉํ ๋ ์์๋๋ฉด ์ข์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๊ตฌ๋ค์ ์๊ฐํ ๊ฒ! ๐
๐ ์ฃผ์ Python ๋ผ์ด๋ธ๋ฌ๋ฆฌ
1. kafka-python
ํน์ง: ์์ Python์ผ๋ก ์์ฑ๋ ์นดํ์นด ํด๋ผ์ด์ธํธ
์ฅ์ : ์ค์น ๊ฐ๋จ, ์์กด์ฑ ์ ์, ๋ฌธ์ํ ์๋จ
๋จ์ : ์ฑ๋ฅ์ด ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ณด๋ค ๋ฎ์
์ถ์ฒ: ํ์ต์ฉ, ์ค์๊ท๋ชจ ํ๋ก์ ํธ
pip install kafka-python
2. confluent-kafka-python
ํน์ง: C ๋ผ์ด๋ธ๋ฌ๋ฆฌ(librdkafka) ๊ธฐ๋ฐ
์ฅ์ : ๋งค์ฐ ๋น ๋ฅธ ์ฑ๋ฅ, ์์ ์ , Confluent ๊ณต์ ์ง์
๋จ์ : ์ค์น ์ ์ปดํ์ผ ํ์ํ ์ ์์
์ถ์ฒ: ํ๋ก๋์
ํ๊ฒฝ, ๊ณ ์ฑ๋ฅ ํ์
pip install confluent-kafka
3. aiokafka
ํน์ง: asyncio ๊ธฐ๋ฐ ๋น๋๊ธฐ ์นดํ์นด ํด๋ผ์ด์ธํธ
์ฅ์ : ๋น๋๊ธฐ ์ฒ๋ฆฌ, ๋์ ๋์์ฑ
๋จ์ : asyncio ์ดํด ํ์
์ถ์ฒ: ๋น๋๊ธฐ ์น ์ ํ๋ฆฌ์ผ์ด์
(FastAPI, aiohttp ๋ฑ)
pip install aiokafka
๐ง ์ ์ฉํ ๋๊ตฌ๋ค
1. Kafka Manager (CMAK) ์นดํ์นด ํด๋ฌ์คํฐ๋ฅผ ์น UI๋ก ๊ด๋ฆฌํ ์ ์๋ ๋๊ตฌ. ํ ํฝ ์์ฑ, ํํฐ์ ๊ด๋ฆฌ, ์ปจ์๋จธ ๊ทธ๋ฃน ๋ชจ๋ํฐ๋ง ๋ฑ์ด ๊ฐ๋ฅํด!
2. Kafdrop
์นดํ์นด ํ ํฝ์ ๋ฉ์์ง๋ฅผ ์ค์๊ฐ์ผ๋ก ๋ณผ ์ ์๋ ์น UI. ๋๋ฒ๊น
ํ ๋ ์ ๋ง ์ ์ฉํด!
3. Kafka Connect
๋ฐ์ดํฐ๋ฒ ์ด์ค, ํ์ผ ์์คํ
๋ฑ๊ณผ ์นดํ์นด๋ฅผ ์ฝ๊ฒ ์ฐ๊ฒฐํด์ฃผ๋ ํ๋ ์์ํฌ. ์ฝ๋ ์์ด ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ ๊ตฌ์ถ ๊ฐ๋ฅ!
4. Schema Registry
๋ฉ์์ง ์คํค๋ง๋ฅผ ์ค์์์ ๊ด๋ฆฌ. Avro, Protobuf, JSON Schema ์ง์. ์คํค๋ง ๋ฒ์ ๊ด๋ฆฌ๋ ๊ฐ๋ฅํด!
๐จ ์ฌ๋ฅ๋ท์์ ์นดํ์นด ์ ๋ฌธ๊ฐ ์ฐพ๊ธฐ
ํน์ ์นดํ์นด๋ฅผ ๋ฐฐ์ฐ๋ค๊ฐ ๋งํ๋ ๋ถ๋ถ์ด ์์ด? ๋๋ ํ๋ก์ ํธ์ ์นดํ์นด๋ฅผ ๋์
ํ๊ณ ์ถ์๋ฐ ์ด๋ป๊ฒ ์์ํด์ผ ํ ์ง ๋ชจ๋ฅด๊ฒ ์ด? ๐
๊ทธ๋ด ๋ ์ฌ๋ฅ๋ท์ ํ์ฉํด๋ด!
๐ ์ฌ๋ฅ๋ท์์ ์ฐพ์ ์ ์๋ ์ ๋ฌธ๊ฐ๋ค
์นดํ์นด ์ํคํ
์ฒ ์ปจ์คํ
๋๊ท๋ชจ ์์คํ
์ค๊ณ ๊ฒฝํ์ด ์๋ ์ ๋ฌธ๊ฐ์๊ฒ ์ํคํ
์ฒ ๋ฆฌ๋ทฐ๋ฅผ ๋ฐ์ ์ ์์ด. ๋น์ ์ ํ๋ก์ ํธ์ ์นดํ์นด๊ฐ ์ ํฉํ์ง, ์ด๋ป๊ฒ ์ค๊ณํด์ผ ํ๋์ง ์กฐ์ธ์ ๋ฐ์๋ณด์!
์นดํ์นด ํํ ๋ฆฌ์ผ & ๋ฉํ ๋ง
1:1 ๋ฉํ ๋ง์ผ๋ก ์นดํ์นด๋ฅผ ๋น ๋ฅด๊ฒ ๋ฐฐ์ธ ์ ์์ด. ์ค๋ฌด ๊ฒฝํ์ด ํ๋ถํ ๊ฐ๋ฐ์์๊ฒ ์ง์ ๋ฐฐ์ฐ๋ฉด ํจ์ฌ ํจ์จ์ ์ด์ง!
ํ๋ก์ ํธ ๊ฐ๋ฐ ์ง์
์นดํ์นด๋ฅผ ํ์ฉํ ์ค์๊ฐ ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ ๊ตฌ์ถ, ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ
์ฒ ์ ํ ๋ฑ ์ค์ ํ๋ก์ ํธ ๊ฐ๋ฐ์ ๋์์ค ์ ๋ฌธ๊ฐ๋ฅผ ์ฐพ์ ์ ์์ด.
ํธ๋ฌ๋ธ์ํ
& ์ฑ๋ฅ ์ต์ ํ
์ด์ ์ค์ธ ์นดํ์นด ์์คํ
์ ๋ฌธ์ ๊ฐ ์๊ฒผ์ ๋, ๋น ๋ฅด๊ฒ ํด๊ฒฐํด์ค ์ ๋ฌธ๊ฐ๋ฅผ ์ฐพ์ ์ ์์ด. Consumer Lag ๋ฌธ์ , ์ฑ๋ฅ ์ ํ, ๋ฉ์์ง ์์ค ๋ฑ ๋ค์ํ ์ด์๋ฅผ ํด๊ฒฐํ ์ ์์ง!
์ฌ๋ฅ๋ท(https://www.jaenung.net)์์๋ ๋ค์ํ ๋ถ์ผ์ ์ ๋ฌธ๊ฐ๋ค์ด ์์ ์ ์ฌ๋ฅ์ ๊ณต์ ํ๊ณ ์์ด. ์นดํ์นด๋ฟ๋ง ์๋๋ผ Python, ๋ฐ์ดํฐ ์์ง๋์ด๋ง, ํด๋ผ์ฐ๋ ๋ฑ ๊ด๋ จ๋ ๋ชจ๋ ๋ถ์ผ์ ์ ๋ฌธ๊ฐ๋ฅผ ๋ง๋ ์ ์์ง! ๐ค
๐ฏ ์ค์ ํ๊ณผ ๋ฒ ์คํธ ํ๋ํฐ์ค
๋ง์ง๋ง์ผ๋ก, ์ค๋ฌด์์ ์นดํ์นด๋ฅผ ์ฌ์ฉํ ๋ ๊ผญ ์์์ผ ํ ํ๋ค์ ์ ๋ฆฌํด์ค๊ฒ! ๐ก
์ด๊ฑด ์ ๋ง ๊ฒฝํ์์ ์ฐ๋ฌ๋์จ ์กฐ์ธ๋ค์ด์ผ.
โ ๏ธ ํํ ์ค์๋ค
1. ํํฐ์
์ ๋๋ฌด ์ ๊ฒ ์ค์
ํํฐ์
์ ๋์ค์ ๋๋ฆด ์ ์์ง๋ง, ์ค์ผ ์๋ ์์ด! ์ฒ์๋ถํฐ ์ถฉ๋ถํ ๋ง์ด ๋ง๋ค์ด๋๋ ๊ฒ ์ข์. ๋ณดํต ์์ ์ปจ์๋จธ ์์ 2~3๋ฐฐ๋ก ์ค์ ํด.
2. ์คํ์
์ปค๋ฐ ํ์ด๋ฐ ์ค์
๋ฉ์์ง๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ ์ ์ปค๋ฐํ๋ฉด? ์คํจํด๋ ๋ค์ ์ฒ๋ฆฌ ๋ชป ํด! ๋ฐ๋์ ์ฒ๋ฆฌ ์๋ฃ ํ์ ์ปค๋ฐํด์ผ ํด.
3. ๋ฉ์์ง ํฌ๊ธฐ ๋ฌด์
๋๋ฌด ํฐ ๋ฉ์์ง(1MB ์ด์)๋ ์ฑ๋ฅ ์ ํ์ ์์ธ์ด ๋ผ. ํฐ ๋ฐ์ดํฐ๋ S3 ๊ฐ์ ๊ณณ์ ์ ์ฅํ๊ณ , ์นดํ์นด์๋ ์ฐธ์กฐ๋ง ๋ณด๋ด๋ ๊ฒ ์ข์!
4. ๋ชจ๋ํฐ๋ง ์ํ
Consumer Lag์ ๋ชจ๋ํฐ๋งํ์ง ์์ผ๋ฉด ์์คํ
์ด ๋๋ ค์ ธ๋ ๋ชจ๋ฅผ ์ ์์ด. ๋ฐ๋์ ๋ชจ๋ํฐ๋ง ์์คํ
์ ๊ตฌ์ถํด์ผ ํด!
โ ๋ฒ ์คํธ ํ๋ํฐ์ค
1. ํ ํฝ ๋ค์ด๋ฐ ๊ท์น
์ผ๊ด๋ ๋ค์ด๋ฐ ๊ท์น์ ์ฌ์ฉํด. ์: {ํ๊ฒฝ}.{ํ}.{์๋น์ค}.{์ด๋ฒคํธํ์
}
์์: prod.payment.order.created
2. ๋ฉ์์ง ์คํค๋ง ์ ์
๋ฉ์์ง ๊ตฌ์กฐ๋ฅผ ๋ช
ํํ ์ ์ํ๊ณ ๋ฌธ์ํํด. Schema Registry๋ฅผ ์ฌ์ฉํ๋ฉด ๋ ์ข์!
3. ๋ฉฑ๋ฑ์ฑ ๋ณด์ฅ
๊ฐ์ ๋ฉ์์ง๋ฅผ ์ฌ๋ฌ ๋ฒ ์ฒ๋ฆฌํด๋ ๊ฒฐ๊ณผ๊ฐ ๊ฐ๋๋ก ์ค๊ณํด. ์: ์ฃผ๋ฌธ ID๋ฅผ ์ฒดํฌํด์ ์ค๋ณต ์ฒ๋ฆฌ ๋ฐฉ์ง
4. ์๋ฌ ์ฒ๋ฆฌ ์ ๋ต
Dead Letter Queue(DLQ)๋ฅผ ๋ง๋ค์ด์ ์ฒ๋ฆฌ ์คํจํ ๋ฉ์์ง๋ฅผ ๋ฐ๋ก ์ ์ฅํด. ๋์ค์ ๋ถ์ํ๊ณ ์ฌ์ฒ๋ฆฌํ ์ ์์ด!
5. ํ๊ฒฝ๋ณ ๋ถ๋ฆฌ
๊ฐ๋ฐ, ์คํ
์ด์ง, ํ๋ก๋์
ํ๊ฒฝ์ ์์ ํ ๋ถ๋ฆฌํด. ํ ํฝ๋ ํด๋ฌ์คํฐ๋ ๋ฐ๋ก!
6. ๋ณด์ ์ค์
ํ๋ก๋์
์์๋ ๋ฐ๋์ SSL/TLS์ ์ธ์ฆ์ ํ์ฑํํด. ๋ฏผ๊ฐํ ๋ฐ์ดํฐ๋ ์ํธํํด์ ์ ์ก!
๐ ์ฉ๋ ๊ณํ ๊ฐ์ด๋
์นดํ์นด ํด๋ฌ์คํฐ๋ฅผ ๊ตฌ์ถํ ๋ ์ผ๋ง๋ ํฐ ์๋ฒ๊ฐ ํ์ํ ๊น? ๊ฐ๋จํ ๊ณ์ฐ ๋ฐฉ๋ฒ์ ์๋ ค์ค๊ฒ! ๐งฎ
# ์ฉ๋ ๊ณ์ฐ ์์
# 1. ๋ฉ์์ง ์ฒ๋ฆฌ๋ ๊ณ์ฐ
messages_per_second = 10000 # ์ด๋น ๋ฉ์์ง ์
message_size_bytes = 1024 # ๋ฉ์์ง ํฌ๊ธฐ (1KB)
throughput_mb_per_sec = (messages_per_second * message_size_bytes) / (1024 * 1024)
print(f"ํ์ํ ์ฒ๋ฆฌ๋: {throughput_mb_per_sec:.2f} MB/s")
# 2. ์ ์ฅ ์ฉ๋ ๊ณ์ฐ
retention_hours = 168 # 7์ผ ๋ณด๊ด
total_storage_gb = (throughput_mb_per_sec * 3600 * retention_hours) / 1024
print(f"ํ์ํ ์ ์ฅ ์ฉ๋: {total_storage_gb:.2f} GB")
# 3. ๋ณต์ ๊ณ ๋ ค
replication_factor = 3 # 3๊ฐ ๋ณต์
total_storage_with_replication = total_storage_gb * replication_factor
print(f"๋ณต์ ํฌํจ ์ ์ฅ ์ฉ๋: {total_storage_with_replication:.2f} GB")
# 4. ์ฌ์ ๋ถ ์ถ๊ฐ (50%)
recommended_storage = total_storage_with_replication * 1.5
print(f"๊ถ์ฅ ์ ์ฅ ์ฉ๋: {recommended_storage:.2f} GB")
# ์ถ๋ ฅ ์์:
# ํ์ํ ์ฒ๋ฆฌ๋: 9.77 MB/s
# ํ์ํ ์ ์ฅ ์ฉ๋: 5908.20 GB
# ๋ณต์ ํฌํจ ์ ์ฅ ์ฉ๋: 17724.61 GB
# ๊ถ์ฅ ์ ์ฅ ์ฉ๋: 26586.91 GB (์ฝ 26TB)
๐ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์! ์ฌ๊ธฐ๊น์ง ์ฝ์๋ค๋ฉด ์ ๋ง ๋๋จํด! ๐
์ฐ๋ฆฌ๋ ์นดํ์นด์ ๊ธฐ์ด๋ถํฐ ์ค์ ํ์ฉ๊น์ง ์ ๋ง ๋ง์ ๊ฑธ ๋ฐฐ์ ์ด.
๐ ํต์ฌ ์์ฝ
1. ์ค์๊ฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ์ ์ค์์ฑ
ํ๋ ์ ํ๋ฆฌ์ผ์ด์
์ ์ฆ๊ฐ์ ์ธ ๋ฐ์์ด ํ์. ์นดํ์นด๋ ์ด๋ฅผ ๊ฐ๋ฅํ๊ฒ ํด์ฃผ๋ ํต์ฌ ์ธํ๋ผ์ผ.
2. ์นดํ์นด์ ํต์ฌ ๊ฐ๋
Producer, Consumer, Topic, Partition, Offset... ์ด ๊ฐ๋
๋ค์ ํ์คํ ์ดํดํ๋ฉด ์นดํ์นด์ 80%๋ ๋ง์คํฐํ ๊ฑฐ์ผ!
3. Python๊ณผ์ ์๋ฒฝํ ์กฐํฉ
kafka-python, confluent-kafka ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ์ฝ๊ฒ ์นดํ์นด๋ฅผ ํ์ฉํ ์ ์์ด.
4. ์ค์ ํ์ฉ
์ฃผ๋ฌธ ์ฒ๋ฆฌ, ์ฌ๊ณ ๊ด๋ฆฌ, ์ค์๊ฐ ๋ถ์ ๋ฑ ๋ค์ํ ๋ถ์ผ์์ ์นดํ์นด๊ฐ ํ์ฝํ๊ณ ์์ด.
5. ์ด์๊ณผ ๋ชจ๋ํฐ๋ง
Consumer Lag, Throughput, Latency ๊ฐ์ ์งํ๋ฅผ ๋ชจ๋ํฐ๋งํ๋ฉด์ ์์คํ
์ ์์ ์ ์ผ๋ก ์ด์ํด์ผ ํด.
์นดํ์นด๋ ์ฒ์์๋ ์ด๋ ต๊ฒ ๋๊ปด์ง ์ ์์ด. ํ์ง๋ง ๊ธฐ๋ณธ ๊ฐ๋ ์ ํ์คํ ์ดํดํ๊ณ , ์์ ํ๋ก์ ํธ๋ถํฐ ์์ํด์ ์ ์ง์ ์ผ๋ก ํ์ฅํด๋๊ฐ๋ฉด ์ด๋์ ์ ๋ฌธ๊ฐ๊ฐ ๋์ด ์์ ๊ฑฐ์ผ! ๐
์ค์๊ฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ๋ ์ด์ ์ ํ์ด ์๋๋ผ ํ์์ผ. ๋ทํ๋ฆญ์ค, ์ฐ๋ฒ, ๋งํฌ๋์ธ ๊ฐ์ ๊ธ๋ก๋ฒ ๊ธฐ์ ๋ค์ด ์นดํ์นด๋ฅผ ์ ํํ ์ด์ ๊ฐ ์์ด. ๋์ ์ฒ๋ฆฌ๋, ํ์ฅ์ฑ, ์์ ์ฑ... ์นดํ์นด๋ ํ๋ ๋ฐ์ดํฐ ์ธํ๋ผ์ ํต์ฌ์ด์ง! ๐ช
ํน์ ์นดํ์นด๋ฅผ ๋ฐฐ์ฐ๋ค๊ฐ ๋งํ๋ ๋ถ๋ถ์ด ์๊ฑฐ๋, ํ๋ก์ ํธ์ ๋์ ํ๊ณ ์ถ์๋ฐ ๋์์ด ํ์ํ๋ค๋ฉด ์ฌ๋ฅ๋ท์์ ์ ๋ฌธ๊ฐ๋ฅผ ์ฐพ์๋ด. ๊ฒฝํ ๋ง์ ๊ฐ๋ฐ์๋ค์ด ๋น์ ์ ์ฑ๊ณต์ ๋์์ค ๊ฑฐ์ผ! ๐ค
๐ ๋ค์ ๋จ๊ณ ํ์ต ์ถ์ฒ
1๏ธโฃ Kafka Streams: ์คํธ๋ฆผ ์ฒ๋ฆฌ๋ฅผ ๋ ์ฝ๊ฒ ํ ์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
2๏ธโฃ Kafka Connect: ์ฝ๋ ์์ด ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ ๊ตฌ์ถ
3๏ธโฃ KSQL: SQL๋ก ์คํธ๋ฆผ ๋ฐ์ดํฐ ์ฒ๋ฆฌ
4๏ธโฃ Schema Registry: ๋ฉ์์ง ์คํค๋ง ์ค์ ๊ด๋ฆฌ
5๏ธโฃ Kubernetes ๋ฐฐํฌ: ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ ์นดํ์นด ์ด์
์, ์ด์ ๋น์ ์ฐจ๋ก์ผ! ๐ฏ
์ค๋ ๋ฐฐ์ด ๋ด์ฉ์ ๋ฐํ์ผ๋ก ์์ ํ๋ก์ ํธ๋ฅผ ์์ํด๋ด. ๊ฐ๋จํ ๋ก๊ทธ ์์ง ์์คํ
์ด๋ , ์ค์๊ฐ ์๋ฆผ ์์คํ
์ด๋ , ์ง์ ๋ง๋ค์ด๋ณด๋ฉด์ ๋ฐฐ์ฐ๋ ๊ฒ ๊ฐ์ฅ ๋น ๋ฅธ ๊ธธ์ด์ผ!
์นดํ์นด์ ํจ๊ป๋ผ๋ฉด ๋น์ ๋ ์ค์๊ฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ์ ์ ๋ฌธ๊ฐ๊ฐ ๋ ์ ์์ด. ํ์ดํ ! ๐ชโจ
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.