์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์™€ Apache Kafka๋ฅผ ํ™œ์šฉํ•œ ์ŠคํŠธ๋ฆฌ๋ฐ: Python์œผ๋กœ ๊ตฌํ˜„ํ•˜๋Š” ํ˜„๋Œ€์  ๋ฐ์ดํ„ฐ ํŒŒ์ดํ”„๋ผ์ธ

๐Ÿš€ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์™€ Apache Kafka๋ฅผ ํ™œ์šฉํ•œ ์ŠคํŠธ๋ฆฌ๋ฐ: Python์œผ๋กœ ๊ตฌํ˜„ํ•˜๋Š” ํ˜„๋Œ€์  ๋ฐ์ดํ„ฐ ํŒŒ์ดํ”„๋ผ์ธ

์นœ๊ตฌ์ฒ˜๋Ÿผ ์‰ฝ๊ฒŒ ๋ฐฐ์šฐ๋Š” ์นดํ”„์นด์™€ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์˜ ๋ชจ๋“  ๊ฒƒ ๐Ÿ’ฌ

๐Ÿ’ก ์ด ๊ธ€์„ ์ฝ์œผ๋ฉด ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๊ฐ€ ์™œ ํ•„์š”ํ•œ์ง€, Apache Kafka๊ฐ€ ๋ฌด์—‡์ธ์ง€, ๊ทธ๋ฆฌ๊ณ  Python์œผ๋กœ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•˜๋Š”์ง€ ์™„๋ฒฝํ•˜๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์–ด! ๋„ทํ”Œ๋ฆญ์Šค, ์šฐ๋ฒ„, ๋งํฌ๋“œ์ธ ๊ฐ™์€ ๊ธ€๋กœ๋ฒŒ ๊ธฐ์—…๋“ค์ด ์™œ ์นดํ”„์นด๋ฅผ ์„ ํƒํ–ˆ๋Š”์ง€๋„ ์•Œ๊ฒŒ ๋  ๊ฑฐ์•ผ. ๐Ÿ˜Š

๐ŸŽฏ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ, ์™œ ์ด๋ ‡๊ฒŒ ์ค‘์š”ํ•ด์กŒ์„๊นŒ?

์นœ๊ตฌ์•ผ, ์š”์ฆ˜ ์„ธ์ƒ์ด ์–ผ๋งˆ๋‚˜ ๋น ๋ฅด๊ฒŒ ๋ณ€ํ•˜๋Š”์ง€ ๋А๊ปด์ง€์ง€? ๐Ÿƒโ€โ™‚๏ธ
์šฐ๋ฆฌ๊ฐ€ ์Šค๋งˆํŠธํฐ์œผ๋กœ ๋ฐฐ๋‹ฌ ์•ฑ์„ ์ผœ๋Š” ์ˆœ๊ฐ„, ์ˆ˜๋งŽ์€ ๋ฐ์ดํ„ฐ๊ฐ€ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์˜ค๊ฐ€๊ณ  ์žˆ์–ด. ๋‚ด ์œ„์น˜, ์ฃผ๋ฌธ ์ •๋ณด, ๋ฐฐ๋‹ฌ์›์˜ ์œ„์น˜, ์˜ˆ์ƒ ๋„์ฐฉ ์‹œ๊ฐ„... ์ด ๋ชจ๋“  ๊ฒŒ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๊ณ  ์žˆ๋‹ค๋Š” ๊ฑฐ ์•Œ๊ณ  ์žˆ์—ˆ์–ด?

์ „ํ†ต์ ์ธ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ(Batch Processing) ๋ฐฉ์‹์€ ํ•˜๋ฃจ์— ํ•œ ๋ฒˆ์”ฉ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ์•„์„œ ์ฒ˜๋ฆฌํ–ˆ์–ด. ๋งˆ์น˜ ์šฐํŽธ๋ฌผ์„ ๋ชจ์•„๋’€๋‹ค๊ฐ€ ํ•œ๊บผ๋ฒˆ์— ์ •๋ฆฌํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ง์ด์•ผ. ๐Ÿ“ฌ ํ•˜์ง€๋งŒ ์ง€๊ธˆ์€ ๋‹ค๋ฅด์ง€! ๊ณ ๊ฐ๋“ค์€ "์ง€๊ธˆ ๋‹น์žฅ" ์ •๋ณด๋ฅผ ์›ํ•ด. ์‹ค์‹œ๊ฐ„ ์ถ”์ฒœ, ์‹ค์‹œ๊ฐ„ ์•Œ๋ฆผ, ์‹ค์‹œ๊ฐ„ ๋ถ„์„... ๋ชจ๋“  ๊ฒŒ ์ฆ‰๊ฐ์ ์ด์–ด์•ผ ํ•ด.

๐Ÿ“Š ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ vs ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ

๋ฐฐ์น˜ ์ฒ˜๋ฆฌ: ๋ฐ์ดํ„ฐ๋ฅผ ์ผ์ • ๊ธฐ๊ฐ„ ๋™์•ˆ ๋ชจ์•„์„œ ํ•œ๊บผ๋ฒˆ์— ์ฒ˜๋ฆฌ (์˜ˆ: ๋งค์ผ ๋ฐค 12์‹œ์— ์ผ๊ด„ ์ฒ˜๋ฆฌ)
์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ: ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ฆ‰์‹œ ์ฒ˜๋ฆฌ (์˜ˆ: ์ฃผ๋ฌธ์ด ๋“ค์–ด์˜ค๋ฉด ๋ฐ”๋กœ ์ฒ˜๋ฆฌ)

์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ์˜ ์žฅ์ ์€ ๋ช…ํ™•ํ•ด:
โœ… ์ฆ‰๊ฐ์ ์ธ ์˜์‚ฌ๊ฒฐ์ • ๊ฐ€๋Šฅ
โœ… ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ํ–ฅ์ƒ
โœ… ์ด์ƒ ์ง•ํ›„ ๋น ๋ฅธ ๊ฐ์ง€
โœ… ๋น„์ฆˆ๋‹ˆ์Šค ๊ธฐํšŒ ํฌ์ฐฉ

์˜ˆ๋ฅผ ๋“ค์–ด๋ณผ๊ฒŒ. ์‹ ์šฉ์นด๋“œ ์‚ฌ๊ธฐ ํƒ์ง€ ์‹œ์Šคํ…œ์„ ์ƒ๊ฐํ•ด๋ด. ๐Ÿฆ
๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์ด๋ผ๋ฉด ํ•˜๋ฃจ๊ฐ€ ์ง€๋‚˜์„œ์•ผ "์–ด์ œ ๋‹น์‹  ์นด๋“œ๋กœ ์ด์ƒํ•œ ๊ฑฐ๋ž˜๊ฐ€ ์žˆ์—ˆ์–ด์š”"๋ผ๊ณ  ์•Œ๋ ค์ฃผ๊ฒ ์ง€. ํ•˜์ง€๋งŒ ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ๋ผ๋ฉด? ์นด๋“œ๊ฐ€ ๊ธํžˆ๋Š” ๊ทธ ์ˆœ๊ฐ„์— ์ด์ƒ ํŒจํ„ด์„ ๊ฐ์ง€ํ•˜๊ณ  ์ฆ‰์‹œ ์ฐจ๋‹จํ•  ์ˆ˜ ์žˆ์–ด! ์ด๊ฒŒ ๋ฐ”๋กœ ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ์˜ ํž˜์ด์•ผ. ๐Ÿ’ช

์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ํ๋ฆ„๋„ ๋ฐ์ดํ„ฐ ์†Œ์Šค ์›น ์•ฑ ๋ชจ๋ฐ”์ผ ์•ฑ IoT ์„ผ์„œ Apache Kafka ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ ์ฒ˜๋ฆฌ ๋ ˆ์ด์–ด Python Consumer ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜ ๋ถ„์„ ๋ฐ ํ•„ํ„ฐ๋ง ์ตœ์ข… ๋ชฉ์ ์ง€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋Œ€์‹œ๋ณด๋“œ ์•Œ๋ฆผ ์‹œ์Šคํ…œ ์ดˆ๋‹น ์ˆ˜์ฒœ~์ˆ˜๋งŒ ๊ฑด์˜ ์ด๋ฒคํŠธ ๋ฐœ์ƒ ๊ณ ์„ฑ๋Šฅ ๋ฉ”์‹œ์ง€ ํ์ž‰ ๋ฐ ๋ฒ„ํผ๋ง ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ๊ฐ€๊ณต ๋ฐ ๋ถ„์„ ์ฆ‰๊ฐ์ ์ธ ์ธ์‚ฌ์ดํŠธ ์ œ๊ณต ํ•ต์‹ฌ ํฌ์ธํŠธ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ฆ‰์‹œ ์ฒ˜๋ฆฌ๋˜์–ด ๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„์˜ ์ง€์—ฐ์‹œ๊ฐ„์œผ๋กœ ๊ฒฐ๊ณผ ์ œ๊ณต

๐ŸŽช 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}")
์นดํ”„์นด ๋ฉ”์‹œ์ง€ ๋ณด์žฅ ์ˆ˜์ค€ ๋น„๊ต At Most Once ์ตœ๋Œ€ 1๋ฒˆ ๋ฉ”์‹œ์ง€ ์ „์†ก ํ™•์ธ ์•ˆํ•จ ์žฅ์ : ๋น ๋ฅธ ์†๋„ ๋‹จ์ : ์†์‹ค ๊ฐ€๋Šฅ ์‚ฌ์šฉ ์‚ฌ๋ก€ ๋กœ๊ทธ ์ˆ˜์ง‘ ๋ฉ”ํŠธ๋ฆญ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹ค์‹œ๊ฐ„ ๋ถ„์„ ์•ฝ๊ฐ„์˜ ์†์‹ค ํ—ˆ์šฉ ๊ฐ€๋Šฅ At Least Once ์ตœ์†Œ 1๋ฒˆ ๋ฉ”์‹œ์ง€ ์ „์†ก ํ™•์ธ ๋Œ€๊ธฐ ์žฅ์ : ์†์‹ค ์—†์Œ ๋‹จ์ : ์ค‘๋ณต ๊ฐ€๋Šฅ ์‚ฌ์šฉ ์‚ฌ๋ก€ ์ฃผ๋ฌธ ์ฒ˜๋ฆฌ ๊ฒฐ์ œ ์‹œ์Šคํ…œ ์ด๋ฉ”์ผ ๋ฐœ์†ก ์ค‘๋ณต ์ œ๊ฑฐ ๋กœ์ง ํ•„์š” Exactly Once ์ •ํ™•ํžˆ 1๋ฒˆ ๋ฉ”์‹œ์ง€ ์ „์†ก ํŠธ๋žœ์žญ์…˜ ์žฅ์ : ์™„๋ฒฝํ•œ ๋ณด์žฅ ๋‹จ์ : ์„ฑ๋Šฅ ์˜ค๋ฒ„ํ—ค๋“œ ์‚ฌ์šฉ ์‚ฌ๋ก€ ๊ธˆ์œต ๊ฑฐ๋ž˜ ์žฌ๊ณ  ๊ด€๋ฆฌ ํšŒ๊ณ„ ์‹œ์Šคํ…œ ์ •ํ™•์„ฑ์ด ์ตœ์šฐ์„ 

๐Ÿ› ๏ธ ์นดํ”„์นด ์šด์˜๊ณผ ๋ชจ๋‹ˆํ„ฐ๋ง

์นดํ”„์นด๋ฅผ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ์šด์˜ํ•˜๋ ค๋ฉด ๋ชจ๋‹ˆํ„ฐ๋ง์ด ํ•„์ˆ˜์•ผ! ๐Ÿ”
์‹œ์Šคํ…œ์ด ์ž˜ ๋Œ์•„๊ฐ€๋Š”์ง€, ๋ฌธ์ œ๋Š” ์—†๋Š”์ง€ ํ•ญ์ƒ ์ฒดํฌํ•ด์•ผ ํ•ด.

๐Ÿ“Š ์ฃผ์š” ๋ชจ๋‹ˆํ„ฐ๋ง ์ง€ํ‘œ

์ง€ํ‘œ ์„ค๋ช… ์ •์ƒ ๋ฒ”์œ„
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
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.