์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆฌ๋ฐ: Kafka์™€ Flink๋กœ ๋ฐ์ดํ„ฐ ์„ธ์ƒ ์ •๋ณตํ•˜๊ธฐ

๐Ÿš€ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆฌ๋ฐ: Kafka์™€ Flink๋กœ ๋ฐ์ดํ„ฐ ์„ธ์ƒ ์ •๋ณตํ•˜๊ธฐ

๋น…๋ฐ์ดํ„ฐ ์‹œ๋Œ€์˜ ํ•„์ˆ˜ ๋ฌด๊ธฐ, ์นดํ”„์นด์™€ ํ”Œ๋งํฌ๋ฅผ ์นœ๊ตฌ์ฒ˜๋Ÿผ ์‰ฝ๊ฒŒ ์•Œ์•„๋ณด์ž!

๐ŸŽฏ ์™œ ์ง€๊ธˆ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆฌ๋ฐ์ธ๊ฐ€?


์•ผ, ์š”์ฆ˜ ์„ธ์ƒ ๋Œ์•„๊ฐ€๋Š” ๊ฑฐ ๋ดค์–ด? ๋„ทํ”Œ๋ฆญ์Šค๋Š” ๋‚ด๊ฐ€ ๋ญ˜ ์ข‹์•„ํ• ์ง€ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์•Œ์•„์ฑ„๊ณ , ์šฐ๋ฒ„๋Š” ๋‚ด ์œ„์น˜์—์„œ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์ฐจ๋ฅผ ์ฐพ์•„์ฃผ๊ณ , ์ฟ ํŒก์€ ๋‚ด๊ฐ€ ํด๋ฆญํ•˜๋Š” ์ˆœ๊ฐ„ ์ถ”์ฒœ ์ƒํ’ˆ์„ ๋ฐ”๊ฟ”๋ฒ„๋ ค. ์ด๊ฒŒ ๋‹ค ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆฌ๋ฐ ๋•๋ถ„์ด์•ผ! โšก

์˜ˆ์ „์—๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ์•„๋’€๋‹ค๊ฐ€ ๋‚˜์ค‘์— ์ฒ˜๋ฆฌํ•˜๋Š” '๋ฐฐ์น˜ ์ฒ˜๋ฆฌ' ๋ฐฉ์‹์ด ๋Œ€์„ธ์˜€์–ด. ํ•˜๋ฃจ์— ํ•œ ๋ฒˆ์”ฉ ๋ฐค์— ๋Œ๋ฆฌ๋Š” ๊ฑฐ์ง€. ๊ทผ๋ฐ ์ง€๊ธˆ์€? 1์ดˆ๊ฐ€ ๊ธ‰ํ•ด! ๊ณ ๊ฐ์ด ํด๋ฆญํ•˜๋Š” ์ˆœ๊ฐ„, ์„ผ์„œ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด๋Š” ์ˆœ๊ฐ„, ๊ทธ ์ฆ‰์‹œ ์ฒ˜๋ฆฌํ•ด์•ผ ๊ฒฝ์Ÿ์—์„œ ์‚ด์•„๋‚จ์„ ์ˆ˜ ์žˆ์–ด.

๐Ÿ’ก ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ์ด ํ•„์š”ํ•œ ์ˆœ๊ฐ„๋“ค

โ€ข ๊ธˆ์œต ๊ฑฐ๋ž˜ ์‚ฌ๊ธฐ ํƒ์ง€ (0.1์ดˆ ์•ˆ์— ํŒ๋‹จํ•ด์•ผ ํ•จ!)
โ€ข ์‹ค์‹œ๊ฐ„ ์ถ”์ฒœ ์‹œ์Šคํ…œ (ํด๋ฆญํ•˜๋Š” ์ˆœ๊ฐ„ ์ทจํ–ฅ ๋ถ„์„)
โ€ข IoT ์„ผ์„œ ๋ชจ๋‹ˆํ„ฐ๋ง (๊ณต์žฅ ์„ค๋น„ ์ด์ƒ ์ฆ‰์‹œ ๊ฐ์ง€)
โ€ข ์‹ค์‹œ๊ฐ„ ๋Œ€์‹œ๋ณด๋“œ (CEO๊ฐ€ ์ง€๊ธˆ ๋‹น์žฅ ๋ณด๊ณ  ์‹ถ์–ด ํ•จ)
โ€ข ๋กœ๊ทธ ๋ถ„์„ ๋ฐ ์•Œ๋ฆผ (์„œ๋ฒ„ ํ„ฐ์ง€๊ธฐ ์ „์— ์•Œ์•„์•ผ์ง€!)

๊ทธ๋ž˜์„œ ์˜ค๋Š˜์€ ์ด ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆฌ๋ฐ์˜ ์–‘๋Œ€ ์‚ฐ๋งฅ, Apache Kafka์™€ Apache Flink์— ๋Œ€ํ•ด ๊นŠ์ด ํŒŒํ—ค์ณ๋ณผ ๊ฑฐ์•ผ. ์žฌ๋Šฅ๋„ท์—์„œ๋„ ์ด๋Ÿฐ ๊ธฐ์ˆ ์„ ๋‹ค๋ฃจ๋Š” ์ „๋ฌธ๊ฐ€๋“ค์ด ๋งŽ์€๋ฐ, ์ด ๊ธ€ ์ฝ๊ณ  ๋‚˜๋ฉด ๋„ˆ๋„ ๊ทธ๋“ค๊ณผ ๋Œ€ํ™”ํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ! ๐ŸŽ“
์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆฌ๋ฐ ์•„ํ‚คํ…์ฒ˜ ๋ฐ์ดํ„ฐ ์†Œ์Šค ์›น ๋กœ๊ทธ IoT ์„ผ์„œ ๋ชจ๋ฐ”์ผ ์•ฑ Kafka ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค ๋ฐ์ดํ„ฐ ๋ฒ„ํผ๋ง Flink ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ ์‹ค์‹œ๊ฐ„ ๋ถ„์„ ๊ฒฐ๊ณผ ์ €์žฅ DB ๋Œ€์‹œ๋ณด๋“œ ๋ฐ์ดํ„ฐ๊ฐ€ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ๋ฅด๋Š” ํŒŒ์ดํ”„๋ผ์ธ ์ดˆ๋‹น ์ˆ˜๋ฐฑ๋งŒ ๊ฑด์˜ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ!

๐Ÿ“ฆ Apache Kafka: ๋ฐ์ดํ„ฐ ๊ณ ์†๋„๋กœ์˜ ์™•


๐ŸŽช Kafka๊ฐ€ ๋ญ๊ธธ๋ž˜?


์นดํ”„์นด๋Š” ๋งํฌ๋“œ์ธ์—์„œ 2011๋…„์— ๋งŒ๋“  ๋ถ„์‚ฐ ๋ฉ”์‹œ์ง• ์‹œ์Šคํ…œ์ด์•ผ. ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด ์—„์ฒญ๋‚˜๊ฒŒ ๋น ๋ฅธ ์šฐ์ฒด๊ตญ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ผ. ์ˆ˜๋งŽ์€ ๋ฐœ์‹ ์ž(Producer)๊ฐ€ ํŽธ์ง€(๋ฉ”์‹œ์ง€)๋ฅผ ๋ณด๋‚ด๋ฉด, ์นดํ”„์นด๊ฐ€ ์ด๊ฑธ ๋ฐ›์•„์„œ ๋ถ„๋ฅ˜ํ•˜๊ณ , ์ˆ˜์‹ ์ž(Consumer)๋“ค์—๊ฒŒ ์ „๋‹ฌํ•ด์ฃผ๋Š” ๊ฑฐ์ง€.

๊ทผ๋ฐ ์ผ๋ฐ˜ ์šฐ์ฒด๊ตญ๊ณผ ๋‹ค๋ฅธ ์ ์€? ๐Ÿš€

1. ์ดˆ๊ณ ์†์ด์•ผ! ์ดˆ๋‹น ์ˆ˜๋ฐฑ๋งŒ ๊ฐœ์˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด.
2. ์ ˆ๋Œ€ ์žƒ์–ด๋ฒ„๋ฆฌ์ง€ ์•Š์•„! ๋””์Šคํฌ์— ์ €์žฅํ•˜๋‹ˆ๊นŒ ์„œ๋ฒ„๊ฐ€ ์ฃฝ์–ด๋„ ๋ฐ์ดํ„ฐ๋Š” ์‚ด์•„์žˆ์–ด.
3. ํ™•์žฅ์ด ์‰ฌ์›Œ! ์„œ๋ฒ„ ๋ช‡ ๋Œ€ ๋” ์ถ”๊ฐ€ํ•˜๋ฉด ์„ฑ๋Šฅ์ด ์ญ‰์ญ‰ ์˜ฌ๋ผ๊ฐ€.
4. ์žฌ์ƒ ๊ฐ€๋Šฅํ•ด! ๊ณผ๊ฑฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ์ฝ์„ ์ˆ˜ ์žˆ์–ด. ํƒ€์ž„๋จธ์‹  ๊ฐ™์€ ๊ฑฐ์ง€.

๐Ÿข Kafka๋ฅผ ์“ฐ๋Š” ํšŒ์‚ฌ๋“ค

โ€ข Netflix: ํ•˜๋ฃจ 7์กฐ ๊ฐœ ์ด์ƒ์˜ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ
โ€ข LinkedIn: ์‹ค์‹œ๊ฐ„ ํ™œ๋™ ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ
โ€ข Uber: ์‹ค์‹œ๊ฐ„ ์œ„์น˜ ์ถ”์  ๋ฐ ์š”๊ธˆ ๊ณ„์‚ฐ
โ€ข Airbnb: ์‹ค์‹œ๊ฐ„ ๊ฐ€๊ฒฉ ์ตœ์ ํ™”
โ€ข ๊ตญ๋‚ด ๋Œ€ํ˜• ํฌํ„ธ, ๊ธˆ์œต์‚ฌ ๋‹ค์ˆ˜

๐Ÿ”ง Kafka์˜ ํ•ต์‹ฌ ๊ฐœ๋…๋“ค


์ž, ์ด์ œ ์นดํ”„์นด์˜ ๋‚ด๋ถ€๋ฅผ ๋“ค์—ฌ๋‹ค๋ณผ ์‹œ๊ฐ„์ด์•ผ. ์ฒ˜์Œ์—” ์ข€ ํ—ท๊ฐˆ๋ฆด ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ฒœ์ฒœํžˆ ๋”ฐ๋ผ์™€๋ด! ๐Ÿงญ

1. Topic (ํ† ํ”ฝ)
ํ† ํ”ฝ์€ ๋ฉ”์‹œ์ง€๋ฅผ ๋ถ„๋ฅ˜ํ•˜๋Š” ์นดํ…Œ๊ณ ๋ฆฌ์•ผ. ์˜ˆ๋ฅผ ๋“ค์–ด '์ฃผ๋ฌธ ๋ฐ์ดํ„ฐ', 'ํด๋ฆญ ๋กœ๊ทธ', '๊ฒฐ์ œ ์ •๋ณด' ์ด๋Ÿฐ ์‹์œผ๋กœ ๋‚˜๋ˆ . ์šฐ์ฒด๊ตญ์˜ ์šฐํŽธํ•จ ๊ฐ™์€ ๊ฑฐ๋ผ๊ณ  ๋ณด๋ฉด ๋ผ.

// ํ† ํ”ฝ ์ƒ์„ฑ ์˜ˆ์‹œ
kafka-topics.sh --create \
  --topic user-clicks \
  --bootstrap-server localhost:9092 \
  --partitions 3 \
  --replication-factor 2

2. Partition (ํŒŒํ‹ฐ์…˜)
ํ•˜๋‚˜์˜ ํ† ํ”ฝ์„ ์—ฌ๋Ÿฌ ์กฐ๊ฐ์œผ๋กœ ๋‚˜๋ˆˆ ๊ฑฐ์•ผ. ์™œ ๋‚˜๋ˆ„๋ƒ๊ณ ? ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด์„œ! ํŒŒํ‹ฐ์…˜์ด 3๊ฐœ๋ฉด 3๊ฐœ์˜ ์„œ๋ฒ„๊ฐ€ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์„œ 3๋ฐฐ ๋นจ๋ผ์ง€๋Š” ๊ฑฐ์ง€. ๊ฐ ํŒŒํ‹ฐ์…˜์€ ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋˜๋Š” ํ(Queue)์•ผ.

3. Producer (ํ”„๋กœ๋“€์„œ)
๋ฐ์ดํ„ฐ๋ฅผ ์นดํ”„์นด์— ๋ณด๋‚ด๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด์•ผ. ์›น ์„œ๋ฒ„, ๋ชจ๋ฐ”์ผ ์•ฑ, IoT ์„ผ์„œ ๋“ฑ์ด ํ”„๋กœ๋“€์„œ๊ฐ€ ๋  ์ˆ˜ ์žˆ์–ด.

// Java๋กœ ์ž‘์„ฑํ•œ ๊ฐ„๋‹จํ•œ Producer ์˜ˆ์‹œ
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

KafkaProducer<String, String> producer = new KafkaProducer<>(props);

ProducerRecord<String, String> record = 
    new ProducerRecord<>("user-clicks", "user123", "clicked-product-456");

producer.send(record);
producer.close();

4. Consumer (์ปจ์Šˆ๋จธ)
์นดํ”„์นด์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด๊ฐ€๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด์•ผ. ์—ฌ๋Ÿฌ ์ปจ์Šˆ๋จธ๊ฐ€ ํ•˜๋‚˜์˜ ํ† ํ”ฝ์„ ๋™์‹œ์— ์ฝ์„ ์ˆ˜ ์žˆ์–ด. ๊ฐ์ž ์ž๊ธฐ ์†๋„๋กœ ์ฝ์œผ๋ฉด ๋ผ.

// Consumer ์˜ˆ์‹œ
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "analytics-group");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("user-clicks"));

while (true) {
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
    for (ConsumerRecord<String, String> record : records) {
        System.out.printf("User: %s, Action: %s%n", record.key(), record.value());
    }
}

5. Consumer Group (์ปจ์Šˆ๋จธ ๊ทธ๋ฃน)
์—ฌ๋Ÿฌ ์ปจ์Šˆ๋จธ๋ฅผ ํ•˜๋‚˜์˜ ๊ทธ๋ฃน์œผ๋กœ ๋ฌถ์€ ๊ฑฐ์•ผ. ๊ฐ™์€ ๊ทธ๋ฃน ๋‚ด์˜ ์ปจ์Šˆ๋จธ๋“ค์€ ํŒŒํ‹ฐ์…˜์„ ๋‚˜๋ˆ ์„œ ์ฝ์–ด. ์˜ˆ๋ฅผ ๋“ค์–ด ํŒŒํ‹ฐ์…˜์ด 6๊ฐœ๊ณ  ์ปจ์Šˆ๋จธ๊ฐ€ 3๊ฐœ๋ฉด, ๊ฐ ์ปจ์Šˆ๋จธ๊ฐ€ 2๊ฐœ์”ฉ ๋‹ด๋‹นํ•˜๋Š” ๊ฑฐ์ง€. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ฒ˜๋ฆฌ ์†๋„๊ฐ€ ๋นจ๋ผ์ ธ! โšก

6. Broker (๋ธŒ๋กœ์ปค)
์นดํ”„์นด ์„œ๋ฒ„ ํ•œ ๋Œ€๋ฅผ ๋ธŒ๋กœ์ปค๋ผ๊ณ  ๋ถˆ๋Ÿฌ. ๋ณดํ†ต ์—ฌ๋Ÿฌ ๋Œ€์˜ ๋ธŒ๋กœ์ปค๋ฅผ ํด๋Ÿฌ์Šคํ„ฐ๋กœ ๋ฌถ์–ด์„œ ์šด์˜ํ•ด. ํ•œ ๋Œ€๊ฐ€ ์ฃฝ์–ด๋„ ๋‹ค๋ฅธ ๋ธŒ๋กœ์ปค๊ฐ€ ๋Œ€์‹  ์ผํ•˜๋‹ˆ๊นŒ ์•ˆ์ „ํ•˜์ง€!

7. Zookeeper (์ฃผํ‚คํผ)
์นดํ”„์นด ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ฝ”๋””๋„ค์ดํ„ฐ์•ผ. ์–ด๋–ค ๋ธŒ๋กœ์ปค๊ฐ€ ์‚ด์•„์žˆ๋Š”์ง€, ๋ฆฌ๋”๋Š” ๋ˆ„๊ตฌ์ธ์ง€ ๋“ฑ์„ ๊ด€๋ฆฌํ•ด. ์ตœ๊ทผ์—๋Š” KRaft ๋ชจ๋“œ๋กœ ์ฃผํ‚คํผ ์—†์ด๋„ ์šด์˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋์–ด!

๐Ÿ’ก ์žฌ๋Šฅ๋„ท ํŒ!
์นดํ”„์นด ์„ค์ •์ด๋‚˜ ์šด์˜์ด ์–ด๋ ต๋‹ค๋ฉด, ์žฌ๋Šฅ๋„ท์—์„œ ์นดํ”„์นด ์ „๋ฌธ๊ฐ€๋ฅผ ์ฐพ์•„๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ. ์‹ค๋ฌด ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ๊ฐœ๋ฐœ์ž๋“ค์ด ์ปจ์„คํŒ…์ด๋‚˜ ๊ต์œก์„ ์ œ๊ณตํ•˜๊ณ  ์žˆ๊ฑฐ๋“ !

๐ŸŽฏ Kafka์˜ ํ•ต์‹ฌ ํŠน์ง•๊ณผ ์žฅ์ 


1. ๋†’์€ ์ฒ˜๋ฆฌ๋Ÿ‰ (High Throughput)
์นดํ”„์นด๋Š” ๋””์Šคํฌ I/O๋ฅผ ์ตœ์ ํ™”ํ•ด์„œ ์ดˆ๋‹น ์ˆ˜๋ฐฑ๋งŒ ๊ฐœ์˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด. ๋น„๊ฒฐ์€ ์ˆœ์ฐจ ์“ฐ๊ธฐ(Sequential Write)์•ผ. ๋””์Šคํฌ์— ์ˆœ์„œ๋Œ€๋กœ ์ญ‰ ์“ฐ๋ฉด ๋žœ๋ค ์“ฐ๊ธฐ๋ณด๋‹ค ํ›จ์”ฌ ๋นจ๋ผ. SSD๊ฐ€ ์•„๋‹Œ ์ผ๋ฐ˜ HDD๋กœ๋„ ์ถฉ๋ถ„ํžˆ ๋น ๋ฅธ ์„ฑ๋Šฅ์„ ๋‚ผ ์ˆ˜ ์žˆ์–ด!

2. ๋‚ด๊ตฌ์„ฑ (Durability)
๋ฉ”์‹œ์ง€๋ฅผ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์•„๋‹Œ ๋””์Šคํฌ์— ์ €์žฅํ•ด. ๊ทธ๋ฆฌ๊ณ  ์—ฌ๋Ÿฌ ๋ธŒ๋กœ์ปค์— ๋ณต์ œ(Replication)ํ•ด๋‘๋‹ˆ๊นŒ, ์„œ๋ฒ„ ํ•œ๋‘ ๋Œ€๊ฐ€ ์ฃฝ์–ด๋„ ๋ฐ์ดํ„ฐ๋Š” ์•ˆ์ „ํ•ด. ๊ธˆ์œต๊ถŒ์—์„œ๋„ ์“ฐ๋Š” ์ด์œ ๊ฐ€ ์ด๊ฑฐ์•ผ! ๐Ÿ›ก๏ธ

3. ํ™•์žฅ์„ฑ (Scalability)
ํŠธ๋ž˜ํ”ฝ์ด ๋Š˜์–ด๋‚˜๋ฉด? ๋ธŒ๋กœ์ปค ๋ช‡ ๋Œ€ ๋” ์ถ”๊ฐ€ํ•˜๋ฉด ๋! ํŒŒํ‹ฐ์…˜ ์ˆ˜๋ฅผ ๋Š˜๋ฆฌ๋ฉด ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ๋Šฅ๋ ฅ๋„ ์ญ‰์ญ‰ ์˜ฌ๋ผ๊ฐ€. ์ˆ˜ํ‰ ํ™•์žฅ(Horizontal Scaling)์ด ์ •๋ง ์‰ฌ์›Œ.

4. ๋ฉ”์‹œ์ง€ ์ˆœ์„œ ๋ณด์žฅ
๊ฐ™์€ ํŒŒํ‹ฐ์…˜ ๋‚ด์—์„œ๋Š” ๋ฉ”์‹œ์ง€ ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋ผ. ์ฃผ๋ฌธ ์ฒ˜๋ฆฌ ๊ฐ™์€ ๊ณณ์—์„œ ์ˆœ์„œ๊ฐ€ ์ค‘์š”ํ•˜์ž–์•„? ์นดํ”„์นด๋Š” ์ด๊ฑธ ์™„๋ฒฝํ•˜๊ฒŒ ์ง€์ผœ์ค˜.

5. ์žฌ์ƒ ๊ฐ€๋Šฅ (Replayable)
๊ณผ๊ฑฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ์ฝ์„ ์ˆ˜ ์žˆ์–ด. ๋ฒ„๊ทธ ์ˆ˜์ • ํ›„ ๋ฐ์ดํ„ฐ๋ฅผ ์žฌ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜, ์ƒˆ๋กœ์šด ๋ถ„์„ ๋กœ์ง์„ ๊ณผ๊ฑฐ ๋ฐ์ดํ„ฐ์— ์ ์šฉํ•  ๋•Œ ์—„์ฒญ ์œ ์šฉํ•ด!

ํŠน์ง• ์„ค๋ช… ์‹ค๋ฌด ํ™œ์šฉ
์ฒ˜๋ฆฌ๋Ÿ‰ ์ดˆ๋‹น ์ˆ˜๋ฐฑ๋งŒ ๋ฉ”์‹œ์ง€ ๋Œ€์šฉ๋Ÿ‰ ๋กœ๊ทธ ์ˆ˜์ง‘
์ง€์—ฐ์‹œ๊ฐ„ ๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„ ์‹ค์‹œ๊ฐ„ ์•Œ๋ฆผ, ๋ชจ๋‹ˆํ„ฐ๋ง
๋ณด๊ด€ ๊ธฐ๊ฐ„ ์„ค์ • ๊ฐ€๋Šฅ (์ผ, ์ฃผ, ์›”) ๋ฐ์ดํ„ฐ ์žฌ์ฒ˜๋ฆฌ, ๊ฐ์‚ฌ
๋ณต์ œ ๋‹ค์ค‘ ๋ธŒ๋กœ์ปค ๋ณต์ œ ๊ณ ๊ฐ€์šฉ์„ฑ ๋ณด์žฅ
ํŒŒํ‹ฐ์…”๋‹ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ์„ฑ๋Šฅ ์ตœ์ ํ™”

๐Ÿš€ Kafka ์‹ค์ „ ํ™œ์šฉ ์‚ฌ๋ก€


์‚ฌ๋ก€ 1: ์‹ค์‹œ๊ฐ„ ๋กœ๊ทธ ์ˆ˜์ง‘
์ˆ˜๋ฐฑ ๋Œ€์˜ ์›น ์„œ๋ฒ„์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋กœ๊ทธ๋ฅผ ์นดํ”„์นด๋กœ ๋ชจ์•„. ๊ทธ๋Ÿผ ์ค‘์•™์—์„œ ํ•œ ๋ฒˆ์— ๋ถ„์„ํ•  ์ˆ˜ ์žˆ์ง€. ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ฆ‰์‹œ ์•Œ๋ฆผ์„ ๋ณด๋‚ผ ์ˆ˜๋„ ์žˆ๊ณ !

์‚ฌ๋ก€ 2: ์ด๋ฒคํŠธ ์†Œ์‹ฑ (Event Sourcing)
๋ชจ๋“  ์ƒํƒœ ๋ณ€๊ฒฝ์„ ์ด๋ฒคํŠธ๋กœ ๊ธฐ๋กํ•ด. ์˜ˆ๋ฅผ ๋“ค์–ด '์ฃผ๋ฌธ ์ƒ์„ฑ', '๊ฒฐ์ œ ์™„๋ฃŒ', '๋ฐฐ์†ก ์‹œ์ž‘' ๊ฐ™์€ ์ด๋ฒคํŠธ๋“ค์„ ์นดํ”„์นด์— ์ €์žฅํ•˜๋ฉด, ๋‚˜์ค‘์— ์ „์ฒด ํ๋ฆ„์„ ์žฌ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์–ด.

์‚ฌ๋ก€ 3: ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ๊ฐ„ ํ†ต์‹ 
์ฃผ๋ฌธ ์„œ๋น„์Šค, ๊ฒฐ์ œ ์„œ๋น„์Šค, ๋ฐฐ์†ก ์„œ๋น„์Šค๊ฐ€ ์นดํ”„์นด๋ฅผ ํ†ตํ•ด ๋น„๋™๊ธฐ๋กœ ํ†ต์‹ ํ•ด. ํ•œ ์„œ๋น„์Šค๊ฐ€ ๋А๋ ค์ ธ๋„ ๋‹ค๋ฅธ ์„œ๋น„์Šค์— ์˜ํ–ฅ์„ ์•ˆ ์ค˜. ๋А์Šจํ•œ ๊ฒฐํ•ฉ(Loose Coupling)์ด ๊ฐ€๋Šฅํ•ด์ง€๋Š” ๊ฑฐ์ง€! ๐Ÿ”—

์‚ฌ๋ก€ 4: CDC (Change Data Capture)
๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์บก์ฒ˜ํ•ด์„œ ์นดํ”„์นด๋กœ ๋ณด๋‚ด. ๊ทธ๋Ÿผ ๋‹ค๋ฅธ ์‹œ์Šคํ…œ๋“ค์ด ์ด ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ˜์˜ํ•  ์ˆ˜ ์žˆ์–ด. ๋ฐ์ดํ„ฐ ๋™๊ธฐํ™”๊ฐ€ ์—„์ฒญ ์‰ฌ์›Œ์ ธ!

๐ŸŒŠ Apache Flink: ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ์˜ ์ตœ๊ฐ•์ž


๐ŸŽญ Flink๋Š” ๋˜ ๋ญ์•ผ?


์นดํ”„์นด๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ์šฐ์ฒด๊ตญ์ด๋ผ๋ฉด, ํ”Œ๋งํฌ๋Š” ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ณต์žฅ์ด์•ผ. ๋…์ผ ๋ฒ ๋ฅผ๋ฆฐ ๊ณต๊ณผ๋Œ€ํ•™์—์„œ ์‹œ์ž‘๋œ ํ”„๋กœ์ ํŠธ์ธ๋ฐ, ์ง€๊ธˆ์€ ์•„ํŒŒ์น˜ ์ตœ์ƒ์œ„ ํ”„๋กœ์ ํŠธ๋กœ ์ž๋ฆฌ์žก์•˜์–ด.

ํ”Œ๋งํฌ์˜ ๊ฐ€์žฅ ํฐ ํŠน์ง•์€? ๐Ÿ’Ž

1. ์ง„์งœ ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ์•ผ! ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด์˜ค๋Š” ์ฆ‰์‹œ ์ฒ˜๋ฆฌํ•ด. ๋งˆ์ดํฌ๋กœ ๋ฐฐ์น˜๊ฐ€ ์•„๋‹ˆ๋ผ ์ง„์งœ ์ŠคํŠธ๋ฆฌ๋ฐ์ด์•ผ.
2. ์ •ํ™•ํžˆ ํ•œ ๋ฒˆ ์ฒ˜๋ฆฌ ๋ณด์žฅ! Exactly-once semantics๋ฅผ ์ง€์›ํ•ด. ์ค‘๋ณต๋„ ์—†๊ณ  ๋ˆ„๋ฝ๋„ ์—†์–ด.
3. ์ƒํƒœ ๊ด€๋ฆฌ๊ฐ€ ๋›ฐ์–ด๋‚˜! ๋ณต์žกํ•œ ๊ณ„์‚ฐ ์ƒํƒœ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์œ ์ง€ํ•˜๋ฉด์„œ๋„ ์žฅ์•  ๋ณต๊ตฌ๊ฐ€ ๊ฐ€๋Šฅํ•ด.
4. ์ด๋ฒคํŠธ ์‹œ๊ฐ„ ์ฒ˜๋ฆฌ! ๋ฐ์ดํ„ฐ๊ฐ€ ์‹ค์ œ๋กœ ๋ฐœ์ƒํ•œ ์‹œ๊ฐ„ ๊ธฐ์ค€์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด.

๐Ÿ† Flink vs Spark Streaming

๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด Spark Streaming๊ณผ ๋น„๊ตํ•˜๋Š”๋ฐ, ํ•ต์‹ฌ ์ฐจ์ด๋Š” ์ด๊ฑฐ์•ผ:

โ€ข Spark Streaming: ๋งˆ์ดํฌ๋กœ ๋ฐฐ์น˜ ๋ฐฉ์‹. ์ž‘์€ ๋ฐฐ์น˜๋ฅผ ๋น ๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•ด์„œ ์‹ค์‹œ๊ฐ„์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ํ•จ.
โ€ข Flink: ์ง„์งœ ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ. ๋ ˆ์ฝ”๋“œ ๋‹จ์œ„๋กœ ์ฆ‰์‹œ ์ฒ˜๋ฆฌ.

๊ฒฐ๊ณผ์ ์œผ๋กœ Flink๊ฐ€ ์ง€์—ฐ์‹œ๊ฐ„์ด ๋” ๋‚ฎ๊ณ , ๋ณต์žกํ•œ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ์— ๋” ์ ํ•ฉํ•ด!

๐Ÿ” Flink์˜ ํ•ต์‹ฌ ๊ฐœ๋…๋“ค


1. DataStream API
ํ”Œ๋งํฌ์˜ ํ•ต์‹ฌ API์•ผ. ์ŠคํŠธ๋ฆผ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ชจ๋“  ์—ฐ์‚ฐ์„ ์ œ๊ณตํ•ด. map, filter, reduce, window ๋“ฑ๋“ฑ.

// Flink DataStream ์˜ˆ์‹œ
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

// Kafka์—์„œ ๋ฐ์ดํ„ฐ ์ฝ๊ธฐ
FlinkKafkaConsumer<String> consumer = new FlinkKafkaConsumer<>(
    "user-clicks",
    new SimpleStringSchema(),
    properties
);

DataStream<String> stream = env.addSource(consumer);

// ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
DataStream<Tuple2<String, Integer>> result = stream
    .map(value -> new Tuple2<>(value, 1))
    .keyBy(0)
    .timeWindow(Time.minutes(1))
    .sum(1);

// ๊ฒฐ๊ณผ ์ถœ๋ ฅ
result.print();

env.execute("Flink Kafka Example");

2. Window (์œˆ๋„์šฐ)
๋ฌดํ•œํ•œ ์ŠคํŠธ๋ฆผ์„ ์œ ํ•œํ•œ ์กฐ๊ฐ์œผ๋กœ ๋‚˜๋ˆ„๋Š” ๋ฐฉ๋ฒ•์ด์•ผ. ์˜ˆ๋ฅผ ๋“ค์–ด "์ตœ๊ทผ 5๋ถ„๊ฐ„์˜ ํด๋ฆญ ์ˆ˜"๋ฅผ ๊ณ„์‚ฐํ•˜๋ ค๋ฉด 5๋ถ„ ์œˆ๋„์šฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฑฐ์ง€.

์œˆ๋„์šฐ ์ข…๋ฅ˜๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐ€์ง€์•ผ:
โ€ข Tumbling Window: ๊ฒน์น˜์ง€ ์•Š๋Š” ๊ณ ์ • ํฌ๊ธฐ ์œˆ๋„์šฐ (์˜ˆ: ๋งค 1๋ถ„๋งˆ๋‹ค)
โ€ข Sliding Window: ๊ฒน์น˜๋Š” ์œˆ๋„์šฐ (์˜ˆ: 5๋ถ„ ์œˆ๋„์šฐ๋ฅผ 1๋ถ„๋งˆ๋‹ค ๊ณ„์‚ฐ)
โ€ข Session Window: ํ™œ๋™์ด ์—†์œผ๋ฉด ์œˆ๋„์šฐ๊ฐ€ ๋‹ซํž˜ (์˜ˆ: ์‚ฌ์šฉ์ž ์„ธ์…˜ ๋ถ„์„)

// Tumbling Window ์˜ˆ์‹œ
stream
    .keyBy(value -> value.getUserId())
    .window(TumblingEventTimeWindows.of(Time.minutes(5)))
    .sum("amount");

// Sliding Window ์˜ˆ์‹œ
stream
    .keyBy(value -> value.getUserId())
    .window(SlidingEventTimeWindows.of(Time.minutes(10), Time.minutes(5)))
    .sum("amount");

// Session Window ์˜ˆ์‹œ
stream
    .keyBy(value -> value.getUserId())
    .window(EventTimeSessionWindows.withGap(Time.minutes(30)))
    .sum("amount");

3. Time (์‹œ๊ฐ„)
ํ”Œ๋งํฌ๋Š” ์„ธ ๊ฐ€์ง€ ์‹œ๊ฐ„ ๊ฐœ๋…์„ ์ง€์›ํ•ด:

โ€ข Event Time: ์ด๋ฒคํŠธ๊ฐ€ ์‹ค์ œ๋กœ ๋ฐœ์ƒํ•œ ์‹œ๊ฐ„. ๊ฐ€์žฅ ์ •ํ™•ํ•˜์ง€๋งŒ ์ฒ˜๋ฆฌ๊ฐ€ ๋ณต์žกํ•ด.
โ€ข Ingestion Time: ํ”Œ๋งํฌ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์€ ์‹œ๊ฐ„.
โ€ข Processing Time: ํ”Œ๋งํฌ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์‹œ๊ฐ„. ๊ฐ€์žฅ ๊ฐ„๋‹จํ•˜์ง€๋งŒ ๋ถ€์ •ํ™•ํ•  ์ˆ˜ ์žˆ์–ด.

์‹ค๋ฌด์—์„œ๋Š” ๋ณดํ†ต Event Time์„ ์จ. ๋„คํŠธ์›Œํฌ ์ง€์—ฐ์ด๋‚˜ ์‹œ์Šคํ…œ ์žฅ์• ๊ฐ€ ์žˆ์–ด๋„ ์ •ํ™•ํ•œ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๊ฑฐ๋“ ! โฐ

4. Watermark (์›Œํ„ฐ๋งˆํฌ)
์ด๋ฒคํŠธ ํƒ€์ž„์„ ์“ธ ๋•Œ ํ•„์š”ํ•œ ๊ฐœ๋…์ด์•ผ. "์ด ์‹œ๊ฐ„ ์ด์ „์˜ ๋ฐ์ดํ„ฐ๋Š” ๋” ์ด์ƒ ์•ˆ ์˜ฌ ๊ฑฐ์•ผ"๋ผ๊ณ  ์•Œ๋ ค์ฃผ๋Š” ์‹ ํ˜ธ์•ผ. ๋Šฆ๊ฒŒ ๋„์ฐฉํ•˜๋Š” ๋ฐ์ดํ„ฐ(Late Data)๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ผ.

// Watermark ์„ค์ • ์˜ˆ์‹œ
DataStream<Event> withTimestampsAndWatermarks = stream
    .assignTimestampsAndWatermarks(
        WatermarkStrategy
            .<Event>forBoundedOutOfOrderness(Duration.ofSeconds(10))
            .withTimestampAssigner((event, timestamp) -> event.getTimestamp())
    );

5. State (์ƒํƒœ)
ํ”Œ๋งํฌ์˜ ์ง„์งœ ๊ฐ•์ ์ด์•ผ! ์ฒ˜๋ฆฌ ์ค‘๊ฐ„ ๊ฒฐ๊ณผ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•  ์ˆ˜ ์žˆ์–ด. ์˜ˆ๋ฅผ ๋“ค์–ด "์‚ฌ์šฉ์ž๋ณ„ ๋ˆ„์  ๊ตฌ๋งค ๊ธˆ์•ก"์„ ๊ณ„์‚ฐํ•  ๋•Œ, ๊ฐ ์‚ฌ์šฉ์ž์˜ ํ˜„์žฌ ๊ธˆ์•ก์„ ์ƒํƒœ๋กœ ์œ ์ง€ํ•˜๋Š” ๊ฑฐ์ง€.

์ƒํƒœ ์ข…๋ฅ˜:
โ€ข Keyed State: ํ‚ค๋ณ„๋กœ ๊ด€๋ฆฌ๋˜๋Š” ์ƒํƒœ (์˜ˆ: ์‚ฌ์šฉ์ž๋ณ„ ์นด์šดํ„ฐ)
โ€ข Operator State: ์˜คํผ๋ ˆ์ดํ„ฐ ์ „์ฒด๊ฐ€ ๊ณต์œ ํ•˜๋Š” ์ƒํƒœ

// Keyed State ์‚ฌ์šฉ ์˜ˆ์‹œ
public class CountFunction extends RichFlatMapFunction<Event, Tuple2<String, Long>> {
    private transient ValueState<Long> count;

    @Override
    public void open(Configuration config) {
        ValueStateDescriptor<Long> descriptor =
            new ValueStateDescriptor<>("count", Long.class, 0L);
        count = getRuntimeContext().getState(descriptor);
    }

    @Override
    public void flatMap(Event event, Collector<Tuple2<String, Long>> out) throws Exception {
        Long currentCount = count.value();
        currentCount += 1;
        count.update(currentCount);
        out.collect(new Tuple2<>(event.getUserId(), currentCount));
    }
}

6. Checkpoint (์ฒดํฌํฌ์ธํŠธ)
ํ”Œ๋งํฌ๋Š” ์ฃผ๊ธฐ์ ์œผ๋กœ ์ƒํƒœ๋ฅผ ์Šค๋ƒ…์ƒท์œผ๋กœ ์ €์žฅํ•ด. ์žฅ์• ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋งˆ์ง€๋ง‰ ์ฒดํฌํฌ์ธํŠธ๋ถ€ํ„ฐ ์žฌ์‹œ์ž‘ํ•˜๋ฉด ๋ผ. ์ด๊ฒŒ ๋ฐ”๋กœ Exactly-once๋ฅผ ๋ณด์žฅํ•˜๋Š” ๋น„๊ฒฐ์ด์•ผ! ๐Ÿ’พ

// Checkpoint ์„ค์ •
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

// 5์ดˆ๋งˆ๋‹ค ์ฒดํฌํฌ์ธํŠธ
env.enableCheckpointing(5000);

// Exactly-once ๋ชจ๋“œ
env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);

// ์ฒดํฌํฌ์ธํŠธ ํƒ€์ž„์•„์›ƒ 1๋ถ„
env.getCheckpointConfig().setCheckpointTimeout(60000);

// ๋™์‹œ ์ฒดํฌํฌ์ธํŠธ 1๊ฐœ๋งŒ
env.getCheckpointConfig().setMaxConcurrentCheckpoints(1);

๐ŸŽฏ Flink์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๋“ค


1. CEP (Complex Event Processing)
๋ณต์žกํ•œ ์ด๋ฒคํŠธ ํŒจํ„ด์„ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ์–ด. ์˜ˆ๋ฅผ ๋“ค์–ด "๋กœ๊ทธ์ธ ์‹คํŒจ 3๋ฒˆ ํ›„ ์„ฑ๊ณต"์ด๋ผ๋Š” ํŒจํ„ด์„ ์ฐพ์•„๋‚ผ ์ˆ˜ ์žˆ์ง€.

// CEP ํŒจํ„ด ์ •์˜
Pattern<LoginEvent, ?> pattern = Pattern.<LoginEvent>begin("first")
    .where(new SimpleCondition<LoginEvent>() {
        @Override
        public boolean filter(LoginEvent event) {
            return event.getStatus().equals("fail");
        }
    })
    .times(3)
    .next("second")
    .where(new SimpleCondition<LoginEvent>() {
        @Override
        public boolean filter(LoginEvent event) {
            return event.getStatus().equals("success");
        }
    })
    .within(Time.minutes(5));

// ํŒจํ„ด ๋งค์นญ
PatternStream<LoginEvent> patternStream = CEP.pattern(loginStream, pattern);

2. Table API & SQL
SQL๋กœ ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์–ด! ๋ฐฐ์น˜ ๋ฐ์ดํ„ฐ ๋‹ค๋ฃจ๋“ฏ์ด ์ŠคํŠธ๋ฆผ์„ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด์„œ ์ง„์ž… ์žฅ๋ฒฝ์ด ๋‚ฎ์•„์ ธ.

// Table API ์˜ˆ์‹œ
StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);

// ์ŠคํŠธ๋ฆผ์„ ํ…Œ์ด๋ธ”๋กœ ๋ณ€ํ™˜
tableEnv.createTemporaryView("Clicks", clickStream, $("user"), $("url"), $("timestamp"));

// SQL ์ฟผ๋ฆฌ
Table result = tableEnv.sqlQuery(
    "SELECT user, COUNT(*) as cnt " +
    "FROM Clicks " +
    "GROUP BY TUMBLE(timestamp, INTERVAL '1' MINUTE), user"
);

// ๋‹ค์‹œ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณ€ํ™˜
DataStream<Row> resultStream = tableEnv.toDataStream(result);

3. Async I/O
์™ธ๋ถ€ ์‹œ์Šคํ…œ(DB, API ๋“ฑ)์„ ์กฐํšŒํ•  ๋•Œ ๋น„๋™๊ธฐ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด. ์„ฑ๋Šฅ์ด ์—„์ฒญ ์ข‹์•„์ ธ!

// Async I/O ์˜ˆ์‹œ
class AsyncDatabaseRequest extends RichAsyncFunction<String, Tuple2<String, String>> {
    private transient DatabaseClient client;

    @Override
    public void open(Configuration parameters) throws Exception {
        client = new DatabaseClient();
    }

    @Override
    public void asyncInvoke(String key, ResultFuture<Tuple2<String, String>> resultFuture) {
        CompletableFuture<String> future = client.query(key);
        
        future.thenAccept(result -> {
            resultFuture.complete(Collections.singleton(new Tuple2<>(key, result)));
        });
    }
}

// ์ ์šฉ
DataStream<Tuple2<String, String>> result = AsyncDataStream.unorderedWait(
    stream,
    new AsyncDatabaseRequest(),
    1000, TimeUnit.MILLISECONDS,
    100
);

4. Side Output
๋ฉ”์ธ ์ŠคํŠธ๋ฆผ์—์„œ ํŠน์ • ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„๋ฆฌํ•ด๋‚ผ ์ˆ˜ ์žˆ์–ด. ์˜ˆ๋ฅผ ๋“ค์–ด ์—๋Ÿฌ ๋ฐ์ดํ„ฐ๋งŒ ๋”ฐ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์œ ์šฉํ•ด! ๐Ÿ”€

// Side Output ์˜ˆ์‹œ
final OutputTag<Event> lateDataTag = new OutputTag<Event>("late-data"){};

SingleOutputStreamOperator<Event> mainStream = stream
    .process(new ProcessFunction<Event, Event>() {
        @Override
        public void processElement(Event event, Context ctx, Collector<Event> out) {
            if (event.getTimestamp() < ctx.timerService().currentWatermark()) {
                // ๋Šฆ์€ ๋ฐ์ดํ„ฐ๋Š” ์‚ฌ์ด๋“œ ์•„์›ƒํ’‹์œผ๋กœ
                ctx.output(lateDataTag, event);
            } else {
                // ์ •์ƒ ๋ฐ์ดํ„ฐ๋Š” ๋ฉ”์ธ ์ŠคํŠธ๋ฆผ์œผ๋กœ
                out.collect(event);
            }
        }
    });

// ๋Šฆ์€ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ ๊ฐ€์ ธ์˜ค๊ธฐ
DataStream<Event> lateStream = mainStream.getSideOutput(lateDataTag);
Flink ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ ํŒŒ์ดํ”„๋ผ์ธ Source Kafka Topic Map ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜ Filter ์กฐ๊ฑด ํ•„ํ„ฐ๋ง KeyBy ํ‚ค๋ณ„ ๊ทธ๋ฃนํ™” Window ์‹œ๊ฐ„ ์œˆ๋„์šฐ State Management ์‚ฌ์šฉ์ž๋ณ„ ๋ˆ„์  ๋ฐ์ดํ„ฐ ์ €์žฅ ์ฒดํฌํฌ์ธํŠธ๋กœ ์žฅ์•  ๋ณต๊ตฌ ๋ณด์žฅ Checkpoint ์ฃผ๊ธฐ์  ์ƒํƒœ ์Šค๋ƒ…์ƒท ์ €์žฅ Exactly-once ๋ณด์žฅ ์ฒ˜๋ฆฌ๋Ÿ‰: ์ดˆ๋‹น ์ˆ˜๋ฐฑ๋งŒ ์ด๋ฒคํŠธ ์ง€์—ฐ์‹œ๊ฐ„: ๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„ ์ •ํ™•์„ฑ: Exactly-once ํ™•์žฅ์„ฑ: ์ˆ˜ํ‰ ํ™•์žฅ

๐Ÿ”— Kafka + Flink: ์ตœ๊ฐ• ์กฐํ•ฉ


์ž, ์ด์ œ ์นดํ”„์นด์™€ ํ”Œ๋งํฌ๋ฅผ ํ•จ๊ป˜ ์“ฐ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž! ์ด ๋‘˜์€ ์ •๋ง ํ™˜์ƒ์˜ ๊ถํ•ฉ์ด์•ผ. ๐ŸŽฏ

๐Ÿ—๏ธ ์ „์ฒด ์•„ํ‚คํ…์ฒ˜


์ „ํ˜•์ ์ธ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ํŒŒ์ดํ”„๋ผ์ธ์€ ์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑ๋ผ:

1๋‹จ๊ณ„: ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘
โ€ข ์›น ์„œ๋ฒ„, ๋ชจ๋ฐ”์ผ ์•ฑ, IoT ์„ผ์„œ ๋“ฑ์—์„œ ๋ฐ์ดํ„ฐ ๋ฐœ์ƒ
โ€ข Producer๊ฐ€ Kafka๋กœ ๋ฐ์ดํ„ฐ ์ „์†ก

2๋‹จ๊ณ„: ๋ฐ์ดํ„ฐ ๋ฒ„ํผ๋ง
โ€ข Kafka๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ Topic์— ์ €์žฅ
โ€ข ์—ฌ๋Ÿฌ Consumer๊ฐ€ ๋™์‹œ์— ์ฝ์„ ์ˆ˜ ์žˆ๋„๋ก ์ค€๋น„

3๋‹จ๊ณ„: ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ
โ€ข Flink๊ฐ€ Kafka์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์˜ด
โ€ข ๋ณ€ํ™˜, ํ•„ํ„ฐ๋ง, ์ง‘๊ณ„, ์กฐ์ธ ๋“ฑ ๋ณต์žกํ•œ ์ฒ˜๋ฆฌ ์ˆ˜ํ–‰

4๋‹จ๊ณ„: ๊ฒฐ๊ณผ ์ €์žฅ
โ€ข ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์‹œ Kafka๋กœ ๋ณด๋‚ด๊ฑฐ๋‚˜
โ€ข DB, Elasticsearch, S3 ๋“ฑ์— ์ €์žฅ

๐Ÿ’ป ์‹ค์ „ ์˜ˆ์ œ: ์‹ค์‹œ๊ฐ„ ํด๋ฆญ ์ŠคํŠธ๋ฆผ ๋ถ„์„


์ „์ž์ƒ๊ฑฐ๋ž˜ ์‚ฌ์ดํŠธ์—์„œ ์‚ฌ์šฉ์ž ํด๋ฆญ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ถ„์„ํ•˜๋Š” ์‹œ์Šคํ…œ์„ ๋งŒ๋“ค์–ด๋ณด์ž!

์š”๊ตฌ์‚ฌํ•ญ:
โ€ข ์‚ฌ์šฉ์ž๋ณ„ ์ตœ๊ทผ 1๋ถ„๊ฐ„ ํด๋ฆญ ์ˆ˜ ๊ณ„์‚ฐ
โ€ข ์ƒํ’ˆ๋ณ„ ์ธ๊ธฐ๋„ ์‹ค์‹œ๊ฐ„ ์ง‘๊ณ„
โ€ข ์ด์ƒ ํŒจํ„ด ๊ฐ์ง€ (๋ด‡ ํŠธ๋ž˜ํ”ฝ ๋“ฑ)

// 1. ํ™˜๊ฒฝ ์„ค์ •
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
env.enableCheckpointing(5000);

// 2. Kafka Source ์„ค์ •
Properties kafkaProps = new Properties();
kafkaProps.setProperty("bootstrap.servers", "localhost:9092");
kafkaProps.setProperty("group.id", "flink-consumer");

FlinkKafkaConsumer<ClickEvent> consumer = new FlinkKafkaConsumer<>(
    "user-clicks",
    new ClickEventSchema(),
    kafkaProps
);

// Watermark ์„ค์ • (10์ดˆ ์ง€์—ฐ ํ—ˆ์šฉ)
consumer.assignTimestampsAndWatermarks(
    WatermarkStrategy
        .<ClickEvent>forBoundedOutOfOrderness(Duration.ofSeconds(10))
        .withTimestampAssigner((event, timestamp) -> event.getTimestamp())
);

// 3. ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ ์ƒ์„ฑ
DataStream<ClickEvent> clickStream = env.addSource(consumer);

// 4. ์‚ฌ์šฉ์ž๋ณ„ 1๋ถ„๊ฐ„ ํด๋ฆญ ์ˆ˜ ๊ณ„์‚ฐ
DataStream<Tuple2<String, Long>> userClickCounts = clickStream
    .keyBy(ClickEvent::getUserId)
    .window(TumblingEventTimeWindows.of(Time.minutes(1)))
    .aggregate(new CountAggregator());

// 5. ์ƒํ’ˆ๋ณ„ ์ธ๊ธฐ๋„ ๊ณ„์‚ฐ
DataStream<Tuple2<String, Long>> productPopularity = clickStream
    .filter(event -> event.getEventType().equals("product_view"))
    .keyBy(ClickEvent::getProductId)
    .window(SlidingEventTimeWindows.of(Time.minutes(5), Time.minutes(1)))
    .aggregate(new CountAggregator());

// 6. ์ด์ƒ ํŒจํ„ด ๊ฐ์ง€ (1๋ถ„์— 100๋ฒˆ ์ด์ƒ ํด๋ฆญ)
DataStream<Alert> alerts = userClickCounts
    .filter(tuple -> tuple.f1 > 100)
    .map(tuple -> new Alert(tuple.f0, "Suspicious activity detected"));

// 7. ๊ฒฐ๊ณผ๋ฅผ Kafka๋กœ ๋‹ค์‹œ ์ „์†ก
FlinkKafkaProducer<Alert> producer = new FlinkKafkaProducer<>(
    "alerts",
    new AlertSerializationSchema(),
    kafkaProps,
    FlinkKafkaProducer.Semantic.EXACTLY_ONCE
);

alerts.addSink(producer);

// 8. ์‹คํ–‰
env.execute("Real-time Click Stream Analysis");

๐ŸŽ“ ์ฝ”๋“œ ์„ค๋ช…

โ€ข Watermark: 10์ดˆ๊นŒ์ง€ ๋Šฆ๊ฒŒ ๋„์ฐฉํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํ—ˆ์šฉ
โ€ข Tumbling Window: ๊ฒน์น˜์ง€ ์•Š๋Š” 1๋ถ„ ์œˆ๋„์šฐ๋กœ ํด๋ฆญ ์ˆ˜ ๊ณ„์‚ฐ
โ€ข Sliding Window: 5๋ถ„ ์œˆ๋„์šฐ๋ฅผ 1๋ถ„๋งˆ๋‹ค ๊ณ„์‚ฐํ•ด์„œ ํŠธ๋ Œ๋“œ ํŒŒ์•…
โ€ข Exactly-once: ์ค‘๋ณต ์—†์ด ์ •ํ™•ํžˆ ํ•œ ๋ฒˆ๋งŒ ์ฒ˜๋ฆฌ ๋ณด์žฅ
โ€ข Checkpoint: 5์ดˆ๋งˆ๋‹ค ์ƒํƒœ ์ €์žฅ์œผ๋กœ ์žฅ์•  ๋ณต๊ตฌ ๊ฐ€๋Šฅ

๐ŸŽจ ๊ณ ๊ธ‰ ํŒจํ„ด: ์ŠคํŠธ๋ฆผ ์กฐ์ธ


์‹ค๋ฌด์—์„œ๋Š” ์—ฌ๋Ÿฌ ์ŠคํŠธ๋ฆผ์„ ์กฐ์ธํ•ด์•ผ ํ•  ๋•Œ๊ฐ€ ๋งŽ์•„. ์˜ˆ๋ฅผ ๋“ค์–ด ํด๋ฆญ ์ŠคํŠธ๋ฆผ๊ณผ ๊ตฌ๋งค ์ŠคํŠธ๋ฆผ์„ ์กฐ์ธํ•ด์„œ ์ „ํ™˜์œจ์„ ๊ณ„์‚ฐํ•˜๋Š” ๊ฑฐ์ง€!

// ํด๋ฆญ ์ŠคํŠธ๋ฆผ
DataStream<ClickEvent> clicks = env.addSource(clickConsumer);

// ๊ตฌ๋งค ์ŠคํŠธ๋ฆผ
DataStream<PurchaseEvent> purchases = env.addSource(purchaseConsumer);

// ์ŠคํŠธ๋ฆผ ์กฐ์ธ (๊ฐ™์€ ์‚ฌ์šฉ์ž์˜ ํด๋ฆญ๊ณผ ๊ตฌ๋งค๋ฅผ 10๋ถ„ ๋‚ด์— ๋งค์นญ)
DataStream<ConversionEvent> conversions = clicks
    .keyBy(ClickEvent::getUserId)
    .intervalJoin(purchases.keyBy(PurchaseEvent::getUserId))
    .between(Time.minutes(-10), Time.minutes(0))
    .process(new ConversionProcessFunction());

// ์ „ํ™˜์œจ ๊ณ„์‚ฐ
DataStream<Double> conversionRate = conversions
    .windowAll(TumblingEventTimeWindows.of(Time.hours(1)))
    .aggregate(new ConversionRateAggregator());

๐Ÿ”ฅ ์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ


1. ๋ณ‘๋ ฌ๋„(Parallelism) ์กฐ์ •
Kafka ํŒŒํ‹ฐ์…˜ ์ˆ˜์™€ Flink ๋ณ‘๋ ฌ๋„๋ฅผ ๋งž์ถฐ์ค˜. ํŒŒํ‹ฐ์…˜์ด 10๊ฐœ๋ฉด ๋ณ‘๋ ฌ๋„๋„ 10์œผ๋กœ ์„ค์ •ํ•˜๋Š” ๊ฒŒ ์ข‹์•„.

env.setParallelism(10);

// ํŠน์ • ์˜คํผ๋ ˆ์ดํ„ฐ๋งŒ ๋ณ‘๋ ฌ๋„ ์กฐ์ •
stream.map(...).setParallelism(5);

2. ์ƒํƒœ ๋ฐฑ์—”๋“œ ์„ ํƒ
์ƒํƒœ๊ฐ€ ํฌ๋ฉด RocksDB ๋ฐฑ์—”๋“œ๋ฅผ ์จ. ๋ฉ”๋ชจ๋ฆฌ๋ณด๋‹ค ๋А๋ฆฌ์ง€๋งŒ ๋””์Šคํฌ๋ฅผ ์“ฐ๋‹ˆ๊นŒ ์šฉ๋Ÿ‰ ์ œํ•œ์ด ์—†์–ด.

env.setStateBackend(new RocksDBStateBackend("hdfs://namenode:9000/flink/checkpoints"));

3. ์ฒดํฌํฌ์ธํŠธ ๊ฐ„๊ฒฉ ์กฐ์ •
๋„ˆ๋ฌด ์ž์ฃผ ํ•˜๋ฉด ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ํฌ๊ณ , ๋„ˆ๋ฌด ๋“œ๋ฌผ๋ฉด ์žฅ์•  ์‹œ ๋ณต๊ตฌ ์‹œ๊ฐ„์ด ๊ธธ์–ด์ ธ. ๋ณดํ†ต 5~10์ดˆ๊ฐ€ ์ ๋‹นํ•ด.

4. ๋„คํŠธ์›Œํฌ ๋ฒ„ํผ ํŠœ๋‹
๋ฐ์ดํ„ฐ ์ „์†ก๋Ÿ‰์ด ๋งŽ์œผ๋ฉด ๋„คํŠธ์›Œํฌ ๋ฒ„ํผ๋ฅผ ๋Š˜๋ ค์ค˜.

taskmanager.network.memory.fraction: 0.2
taskmanager.network.memory.min: 128mb
taskmanager.network.memory.max: 1gb

5. ๋ฐฑํ”„๋ ˆ์…”(Backpressure) ๋ชจ๋‹ˆํ„ฐ๋ง
Flink Web UI์—์„œ ๋ฐฑํ”„๋ ˆ์…”๋ฅผ ํ™•์ธํ•ด. ํŠน์ • ์˜คํผ๋ ˆ์ดํ„ฐ๊ฐ€ ๋ณ‘๋ชฉ์ด๋ฉด ๋ณ‘๋ ฌ๋„๋ฅผ ๋†’์ด๊ฑฐ๋‚˜ ๋กœ์ง์„ ์ตœ์ ํ™”ํ•ด์•ผ ํ•ด! ๐Ÿ“Š

๐Ÿ› ๏ธ ์‹ค์ „ ์šด์˜ ๋…ธํ•˜์šฐ


์ด๋ก ์€ ์ด์ œ ์ถฉ๋ถ„ํ•ด! ์‹ค์ œ๋กœ ์šด์˜ํ•  ๋•Œ ์•Œ์•„์•ผ ํ•  ๊ฒƒ๋“ค์„ ์•Œ๋ ค์ค„๊ฒŒ.

๐Ÿšจ ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ์•Œ๋ฆผ


Kafka ๋ชจ๋‹ˆํ„ฐ๋ง ์ง€ํ‘œ
โ€ข Under-replicated Partitions: ๋ณต์ œ๊ฐ€ ์•ˆ ๋œ ํŒŒํ‹ฐ์…˜ ์ˆ˜. 0์ด์–ด์•ผ ์ •์ƒ!
โ€ข Offline Partitions: ์˜คํ”„๋ผ์ธ ํŒŒํ‹ฐ์…˜ ์ˆ˜. ์ ˆ๋Œ€ 0์ด์–ด์•ผ ํ•จ!
โ€ข ISR Shrink/Expand Rate: ๋ณต์ œ๋ณธ ์ƒํƒœ ๋ณ€ํ™”์œจ. ๋„ˆ๋ฌด ์ž์ฃผ ๋ฐ”๋€Œ๋ฉด ๋ฌธ์ œ
โ€ข Bytes In/Out: ์ดˆ๋‹น ์ „์†ก๋Ÿ‰. ๊ธ‰์ฆํ•˜๋ฉด ์šฉ๋Ÿ‰ ์ฆ์„ค ๊ณ ๋ ค
โ€ข Consumer Lag: ์ปจ์Šˆ๋จธ๊ฐ€ ์–ผ๋งˆ๋‚˜ ๋’ค์ฒ˜์กŒ๋Š”์ง€. ๊ณ„์† ์ฆ๊ฐ€ํ•˜๋ฉด ์œ„ํ—˜!

// Consumer Lag ํ™•์ธ
kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --describe --group my-group

GROUP           TOPIC           PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG
my-group        user-clicks     0          1000            1500            500
my-group        user-clicks     1          2000            2100            100

Flink ๋ชจ๋‹ˆํ„ฐ๋ง ์ง€ํ‘œ
โ€ข Checkpoint Duration: ์ฒดํฌํฌ์ธํŠธ ์†Œ์š” ์‹œ๊ฐ„. ๋„ˆ๋ฌด ๊ธธ๋ฉด ๋ฌธ์ œ
โ€ข Checkpoint Failure Rate: ์‹คํŒจ์œจ. 0์— ๊ฐ€๊นŒ์›Œ์•ผ ํ•จ
โ€ข Records Processed: ์ฒ˜๋ฆฌ๋œ ๋ ˆ์ฝ”๋“œ ์ˆ˜
โ€ข Backpressure: ๋ฐฑํ”„๋ ˆ์…” ์ƒํƒœ. HIGH๋ฉด ๋ณ‘๋ชฉ ํ•ด๊ฒฐ ํ•„์š”
โ€ข Task Failure Rate: ํƒœ์Šคํฌ ์‹คํŒจ์œจ

๐Ÿ’ก ๋ชจ๋‹ˆํ„ฐ๋ง ๋„๊ตฌ ์ถ”์ฒœ

โ€ข Prometheus + Grafana: ๊ฐ€์žฅ ์ธ๊ธฐ ์žˆ๋Š” ์กฐํ•ฉ. ๋ฌด๋ฃŒ๊ณ  ๊ฐ•๋ ฅํ•ด!
โ€ข Kafka Manager: ์นดํ”„์นด ํด๋Ÿฌ์Šคํ„ฐ ๊ด€๋ฆฌ UI
โ€ข Burrow: LinkedIn์ด ๋งŒ๋“  Consumer Lag ๋ชจ๋‹ˆํ„ฐ๋ง ๋„๊ตฌ
โ€ข Flink Dashboard: ๊ธฐ๋ณธ ์ œ๊ณต๋˜๋Š” ์›น UI. ์‹ค์‹œ๊ฐ„ ๋ฉ”ํŠธ๋ฆญ ํ™•์ธ ๊ฐ€๋Šฅ

๐Ÿ”ง ์žฅ์•  ๋Œ€์‘ ์‹œ๋‚˜๋ฆฌ์˜ค


์‹œ๋‚˜๋ฆฌ์˜ค 1: Kafka ๋ธŒ๋กœ์ปค ๋‹ค์šด
โ€ข ์ฆ์ƒ: ํŠน์ • ํŒŒํ‹ฐ์…˜์— ์“ฐ๊ธฐ/์ฝ๊ธฐ ์‹คํŒจ
โ€ข ์›์ธ: ๋ธŒ๋กœ์ปค ์žฅ์• 
โ€ข ํ•ด๊ฒฐ: ๋ณต์ œ๋ณธ์ด ์žˆ์œผ๋ฉด ์ž๋™์œผ๋กœ ๋‹ค๋ฅธ ๋ธŒ๋กœ์ปค๊ฐ€ ๋ฆฌ๋”๊ฐ€ ๋จ. ๋ธŒ๋กœ์ปค ์žฌ์‹œ์ž‘ ํ›„ ๋ณต์ œ ์žฌ๊ฐœ

์‹œ๋‚˜๋ฆฌ์˜ค 2: Consumer Lag ๊ธ‰์ฆ
โ€ข ์ฆ์ƒ: ์ปจ์Šˆ๋จธ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๋”ฐ๋ผ์žก์ง€ ๋ชปํ•จ
โ€ข ์›์ธ: ์ฒ˜๋ฆฌ ์†๋„ < ์ƒ์‚ฐ ์†๋„
โ€ข ํ•ด๊ฒฐ: ์ปจ์Šˆ๋จธ ์ธ์Šคํ„ด์Šค ์ถ”๊ฐ€, ํŒŒํ‹ฐ์…˜ ์ˆ˜ ์ฆ๊ฐ€, ์ฒ˜๋ฆฌ ๋กœ์ง ์ตœ์ ํ™”

์‹œ๋‚˜๋ฆฌ์˜ค 3: Flink Job ์‹คํŒจ
โ€ข ์ฆ์ƒ: Job์ด ๊ณ„์† ์žฌ์‹œ์ž‘๋จ
โ€ข ์›์ธ: ์ฝ”๋“œ ๋ฒ„๊ทธ, ๋ฆฌ์†Œ์Šค ๋ถ€์กฑ, ์™ธ๋ถ€ ์‹œ์Šคํ…œ ์žฅ์• 
โ€ข ํ•ด๊ฒฐ: ๋กœ๊ทธ ํ™•์ธ, ์ฒดํฌํฌ์ธํŠธ์—์„œ ๋ณต๊ตฌ, ๋ฆฌ์†Œ์Šค ์ฆ์„ค

์‹œ๋‚˜๋ฆฌ์˜ค 4: ๋””์Šคํฌ ๊ณต๊ฐ„ ๋ถ€์กฑ
โ€ข ์ฆ์ƒ: ์นดํ”„์นด๊ฐ€ ์ƒˆ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์ง€ ๋ชปํ•จ
โ€ข ์›์ธ: ๋กœ๊ทธ ๋ณด๊ด€ ๊ธฐ๊ฐ„์ด ๋„ˆ๋ฌด ๊ธธ๊ฑฐ๋‚˜ ํŠธ๋ž˜ํ”ฝ ๊ธ‰์ฆ
โ€ข ํ•ด๊ฒฐ: ์˜ค๋ž˜๋œ ๋กœ๊ทธ ์‚ญ์ œ, ๋ณด๊ด€ ๊ธฐ๊ฐ„ ๋‹จ์ถ•, ๋””์Šคํฌ ์ฆ์„ค

// ๋กœ๊ทธ ๋ณด๊ด€ ๊ธฐ๊ฐ„ ์„ค์ • (7์ผ)
log.retention.hours=168

// ๋กœ๊ทธ ํฌ๊ธฐ ์ œํ•œ (10GB)
log.retention.bytes=10737418240

// ๋กœ๊ทธ ์„ธ๊ทธ๋จผํŠธ ํฌ๊ธฐ (1GB)
log.segment.bytes=1073741824

๐ŸŽฏ ๋ฐฐํฌ ์ „๋žต


๋ธ”๋ฃจ-๊ทธ๋ฆฐ ๋ฐฐํฌ
์ƒˆ ๋ฒ„์ „์„ ๋ณ„๋„ ํด๋Ÿฌ์Šคํ„ฐ์— ๋ฐฐํฌํ•˜๊ณ , ํŠธ๋ž˜ํ”ฝ์„ ์ ์ง„์ ์œผ๋กœ ์ด๋™์‹œ์ผœ. ๋ฌธ์ œ ์ƒ๊ธฐ๋ฉด ์ฆ‰์‹œ ๋กค๋ฐฑ ๊ฐ€๋Šฅ!

์นด๋‚˜๋ฆฌ ๋ฐฐํฌ
์ผ๋ถ€ ํŠธ๋ž˜ํ”ฝ๋งŒ ์ƒˆ ๋ฒ„์ „์œผ๋กœ ๋ณด๋‚ด์„œ ํ…Œ์ŠคํŠธํ•ด. ๋ฌธ์ œ ์—†์œผ๋ฉด ์ ์ง„์ ์œผ๋กœ ํ™•๋Œ€.

Savepoint ํ™œ์šฉ
Flink๋Š” Savepoint๋ฅผ ์ฐ์–ด๋‘๋ฉด ๋‚˜์ค‘์— ์ •ํ™•ํžˆ ๊ทธ ์‹œ์ ๋ถ€ํ„ฐ ์žฌ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์–ด. ์—…๊ทธ๋ ˆ์ด๋“œํ•  ๋•Œ ํ•„์ˆ˜์•ผ!

// Savepoint ์ƒ์„ฑ
flink savepoint <jobId> [targetDirectory]

// Savepoint์—์„œ ์žฌ์‹œ์ž‘
flink run -s :savepointPath [:runArgs]

๐Ÿ’ฐ ๋น„์šฉ ์ตœ์ ํ™”


1. ์ ์ ˆํ•œ ๋ณด๊ด€ ๊ธฐ๊ฐ„ ์„ค์ •
์นดํ”„์นด ๋กœ๊ทธ๋ฅผ ๋ฌดํ•œ์ • ๋ณด๊ด€ํ•˜๋ฉด ๋””์Šคํฌ ๋น„์šฉ์ด ํญ๋ฐœํ•ด. ํ•„์š”ํ•œ ๋งŒํผ๋งŒ ๋ณด๊ด€ํ•ด.

2. ์••์ถ• ํ™œ์šฉ
๋ฉ”์‹œ์ง€๋ฅผ ์••์ถ•ํ•˜๋ฉด ๋„คํŠธ์›Œํฌ ๋Œ€์—ญํญ๊ณผ ๋””์Šคํฌ ๊ณต๊ฐ„์„ ์ ˆ์•ฝํ•  ์ˆ˜ ์žˆ์–ด.

compression.type=snappy  // ๋˜๋Š” lz4, gzip, zstd

3. ๋ฆฌ์†Œ์Šค Right-sizing
๊ณผ๋„ํ•œ ๋ฆฌ์†Œ์Šค ํ• ๋‹น์€ ๋‚ญ๋น„์•ผ. ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๊ณ  ์ ์ • ์ˆ˜์ค€์œผ๋กœ ์กฐ์ •ํ•ด.

4. ์ŠคํŒŸ ์ธ์Šคํ„ด์Šค ํ™œ์šฉ
ํด๋ผ์šฐ๋“œ์—์„œ๋Š” ์ŠคํŒŸ ์ธ์Šคํ„ด์Šค๋ฅผ ์“ฐ๋ฉด ๋น„์šฉ์„ 70% ์ด์ƒ ์ ˆ๊ฐํ•  ์ˆ˜ ์žˆ์–ด. ๋‹จ, ์ค‘์š”ํ•œ ๋ธŒ๋กœ์ปค๋Š” ์˜จ๋””๋งจ๋“œ๋กœ! ๐Ÿ’ต

๐ŸŒŸ ์‹ค์ „ ์‚ฌ๋ก€ ์—ฐ๊ตฌ


๐Ÿ“ฑ ์‚ฌ๋ก€ 1: ๋Œ€ํ˜• ์†Œ์…œ ๋ฏธ๋””์–ด ํ”Œ๋žซํผ


๋ฌธ์ œ: ํ•˜๋ฃจ ์ˆ˜์‹ญ์–ต ๊ฑด์˜ ์‚ฌ์šฉ์ž ํ™œ๋™ ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์„œ ์ถ”์ฒœ ์‹œ์Šคํ…œ์— ๋ฐ˜์˜ํ•ด์•ผ ํ•จ.

์†”๋ฃจ์…˜:
โ€ข Kafka ํด๋Ÿฌ์Šคํ„ฐ: 100๋Œ€ ์ด์ƒ์˜ ๋ธŒ๋กœ์ปค
โ€ข ํŒŒํ‹ฐ์…˜: ํ† ํ”ฝ๋‹น 1000๊ฐœ ์ด์ƒ
โ€ข Flink ํด๋Ÿฌ์Šคํ„ฐ: ์ˆ˜๋ฐฑ ๋Œ€์˜ TaskManager
โ€ข ์ฒ˜๋ฆฌ๋Ÿ‰: ์ดˆ๋‹น 1000๋งŒ ์ด๋ฒคํŠธ ์ด์ƒ

ํ•ต์‹ฌ ๊ธฐ์ˆ :
โ€ข ์‚ฌ์šฉ์ž๋ณ„ ์‹ค์‹œ๊ฐ„ ํ”„๋กœํ•„ ์—…๋ฐ์ดํŠธ (Keyed State ํ™œ์šฉ)
โ€ข ๋ณต์žกํ•œ ์ด๋ฒคํŠธ ํŒจํ„ด ๊ฐ์ง€ (CEP ํ™œ์šฉ)
โ€ข ๋‹ค์ค‘ ์ŠคํŠธ๋ฆผ ์กฐ์ธ (์นœ๊ตฌ ๊ด€๊ณ„, ๊ด€์‹ฌ์‚ฌ ๋“ฑ)
โ€ข ์‹ค์‹œ๊ฐ„ A/B ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ์ง‘๊ณ„

๊ฒฐ๊ณผ:
โ€ข ์ถ”์ฒœ ์ •ํ™•๋„ 30% ํ–ฅ์ƒ
โ€ข ์‚ฌ์šฉ์ž ์ฐธ์—ฌ๋„ 25% ์ฆ๊ฐ€
โ€ข ์ธํ”„๋ผ ๋น„์šฉ 40% ์ ˆ๊ฐ (๊ธฐ์กด ๋ฐฐ์น˜ ์‹œ์Šคํ…œ ๋Œ€๋น„)

๐Ÿฆ ์‚ฌ๋ก€ 2: ๊ธ€๋กœ๋ฒŒ ๊ธˆ์œต ๊ธฐ๊ด€


๋ฌธ์ œ: ์‹ค์‹œ๊ฐ„ ์‚ฌ๊ธฐ ๊ฑฐ๋ž˜ ํƒ์ง€. ๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„๋กœ ํŒ๋‹จํ•ด์•ผ ํ•จ.

์†”๋ฃจ์…˜:
โ€ข Kafka: ๊ฑฐ๋ž˜ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆฌ๋ฐ
โ€ข Flink: ์‹ค์‹œ๊ฐ„ ์ด์ƒ ํŒจํ„ด ๊ฐ์ง€
โ€ข ๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ ํ†ตํ•ฉ (Async I/O๋กœ ๋ชจ๋ธ ์„œ๋ฒ„ ํ˜ธ์ถœ)

ํ•ต์‹ฌ ๋กœ์ง:
โ€ข ์‚ฌ์šฉ์ž๋ณ„ ์ตœ๊ทผ ๊ฑฐ๋ž˜ ํŒจํ„ด ํ•™์Šต (State ํ™œ์šฉ)
โ€ข ์ง€๋ฆฌ์  ์œ„์น˜ ๊ธฐ๋ฐ˜ ์ด์ƒ ํƒ์ง€
โ€ข ๊ฑฐ๋ž˜ ๊ธˆ์•ก ํŒจํ„ด ๋ถ„์„
โ€ข ๋ณตํ•ฉ ๊ทœ์น™ ์—”์ง„ (CEP ํ™œ์šฉ)

๊ฒฐ๊ณผ:
โ€ข ์‚ฌ๊ธฐ ํƒ์ง€์œจ 95% ์ด์ƒ
โ€ข ์˜คํƒ๋ฅ  50% ๊ฐ์†Œ
โ€ข ํ‰๊ท  ํƒ์ง€ ์‹œ๊ฐ„ 100ms ์ดํ•˜
โ€ข ์—ฐ๊ฐ„ ์ˆ˜๋ฐฑ์–ต ์›์˜ ์†์‹ค ๋ฐฉ์ง€

๐Ÿš— ์‚ฌ๋ก€ 3: ์ฐจ๋Ÿ‰ ๊ณต์œ  ์„œ๋น„์Šค


๋ฌธ์ œ: ์ˆ˜๋ฐฑ๋งŒ ๋Œ€์˜ ์ฐจ๋Ÿ‰ ์œ„์น˜๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ถ”์ ํ•˜๊ณ , ์ตœ์ ์˜ ๋งค์นญ์„ ์ œ๊ณตํ•ด์•ผ ํ•จ.

์†”๋ฃจ์…˜:
โ€ข Kafka: ์ฐจ๋Ÿ‰ ์œ„์น˜, ์‚ฌ์šฉ์ž ์š”์ฒญ ์ŠคํŠธ๋ฆฌ๋ฐ
โ€ข Flink: ์‹ค์‹œ๊ฐ„ ์ง€๋ฆฌ ๊ณต๊ฐ„ ๋ถ„์„
โ€ข ๋™์  ๊ฐ€๊ฒฉ ์ฑ…์ • ์•Œ๊ณ ๋ฆฌ์ฆ˜

์•„ํ‚คํ…์ฒ˜:

// ์ฐจ๋Ÿ‰ ์œ„์น˜ ์ŠคํŠธ๋ฆผ
DataStream<VehicleLocation> vehicles = env.addSource(vehicleConsumer);

// ์‚ฌ์šฉ์ž ์š”์ฒญ ์ŠคํŠธ๋ฆผ
DataStream<RideRequest> requests = env.addSource(requestConsumer);

// ์ง€๋ฆฌ์  ๊ทธ๋ฆฌ๋“œ๋กœ ๋ถ„ํ•  (GeoHash ์‚ฌ์šฉ)
DataStream<Tuple2<String, VehicleLocation>> vehiclesByGrid = vehicles
    .map(v -> new Tuple2<>(GeoHash.encode(v.getLat(), v.getLon(), 6), v));

DataStream<Tuple2<String, RideRequest>> requestsByGrid = requests
    .map(r -> new Tuple2<>(GeoHash.encode(r.getLat(), r.getLon(), 6), r));

// ๊ฐ™์€ ๊ทธ๋ฆฌ๋“œ ๋‚ด์—์„œ ๋งค์นญ
DataStream<Match> matches = vehiclesByGrid
    .keyBy(0)
    .connect(requestsByGrid.keyBy(0))
    .process(new MatchingFunction());

// ๋™์  ๊ฐ€๊ฒฉ ๊ณ„์‚ฐ
DataStream<PriceUpdate> prices = requests
    .keyBy(r -> r.getRegion())
    .window(TumblingEventTimeWindows.of(Time.minutes(5)))
    .aggregate(new DynamicPricingAggregator());

๊ฒฐ๊ณผ:
โ€ข ํ‰๊ท  ๋Œ€๊ธฐ ์‹œ๊ฐ„ 40% ๊ฐ์†Œ
โ€ข ์ฐจ๋Ÿ‰ ํ™œ์šฉ๋ฅ  30% ์ฆ๊ฐ€
โ€ข ๋™์  ๊ฐ€๊ฒฉ ์ฑ…์ •์œผ๋กœ ์ˆ˜์ต 20% ์ฆ๊ฐ€
โ€ข ์‹œ์Šคํ…œ ์•ˆ์ •์„ฑ 99.99% ๋‹ฌ์„ฑ

๐Ÿญ ์‚ฌ๋ก€ 4: ์Šค๋งˆํŠธ ํŒฉํ† ๋ฆฌ


๋ฌธ์ œ: ์ˆ˜์ฒœ ๊ฐœ์˜ IoT ์„ผ์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ถ„์„ํ•ด์„œ ์„ค๋น„ ์ด์ƒ์„ ์กฐ๊ธฐ์— ๊ฐ์ง€ํ•ด์•ผ ํ•จ.

์†”๋ฃจ์…˜:
โ€ข Kafka: ์„ผ์„œ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ (์˜จ๋„, ์ง„๋™, ์••๋ ฅ ๋“ฑ)
โ€ข Flink: ์‹ค์‹œ๊ฐ„ ์ด์ƒ ํƒ์ง€ ๋ฐ ์˜ˆ์ธก ์œ ์ง€๋ณด์ˆ˜
โ€ข ์‹œ๊ณ„์—ด ๋ถ„์„ ๋ฐ ๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ ์ ์šฉ

ํ•ต์‹ฌ ๊ธฐ๋Šฅ:
โ€ข ์„ผ์„œ ๋ฐ์ดํ„ฐ ์ •๊ทœํ™” ๋ฐ ํ•„ํ„ฐ๋ง
โ€ข ์ด๋™ ํ‰๊ท  ๊ณ„์‚ฐ (Sliding Window)
โ€ข ์ž„๊ณ„๊ฐ’ ๊ธฐ๋ฐ˜ ์•Œ๋ฆผ
โ€ข ํŒจํ„ด ๊ธฐ๋ฐ˜ ๊ณ ์žฅ ์˜ˆ์ธก (CEP)

๊ฒฐ๊ณผ:
โ€ข ์„ค๋น„ ๊ฐ€๋™๋ฅ  15% ํ–ฅ์ƒ
โ€ข ์˜ˆ๋ฐฉ ์ •๋น„๋กœ ๊ณ ์žฅ 60% ๊ฐ์†Œ
โ€ข ์œ ์ง€๋ณด์ˆ˜ ๋น„์šฉ 35% ์ ˆ๊ฐ
โ€ข ์ƒ์‚ฐ์„ฑ 25% ์ฆ๊ฐ€

๐ŸŽ“ ํ•™์Šต ํŒ!
์ด๋Ÿฐ ์‹ค์ „ ์‚ฌ๋ก€๋“ค์„ ๊ณต๋ถ€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ์žฌ๋Šฅ๋„ท์—์„œ ์‹ค๋ฌด ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ์ „๋ฌธ๊ฐ€๋“ค์˜ ๋ฉ˜ํ† ๋ง์„ ๋ฐ›์•„๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ. ์ฑ…์ด๋‚˜ ๊ฐ•์˜๋กœ๋Š” ๋ฐฐ์šธ ์ˆ˜ ์—†๋Š” ์‹ค์ „ ๋…ธํ•˜์šฐ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๊ฑฐ๋“ !

๐Ÿš€ ๋ฏธ๋ž˜ ํŠธ๋ Œ๋“œ์™€ ๋ฐœ์ „ ๋ฐฉํ–ฅ


๐Ÿ”ฎ Kafka์˜ ๋ฏธ๋ž˜


1. KRaft ๋ชจ๋“œ (Zookeeper ์ œ๊ฑฐ)
Kafka 2.8๋ถ€ํ„ฐ Zookeeper ์—†์ด๋„ ์šด์˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋์–ด. KRaft(Kafka Raft)๋ผ๋Š” ์ž์ฒด ํ•ฉ์˜ ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฑฐ์ง€. ๋” ๊ฐ„๋‹จํ•˜๊ณ  ํ™•์žฅ์„ฑ๋„ ์ข‹์•„์ ธ!

2. Tiered Storage
์˜ค๋ž˜๋œ ๋ฐ์ดํ„ฐ๋ฅผ S3 ๊ฐ™์€ ์ €๋ ดํ•œ ์Šคํ† ๋ฆฌ์ง€๋กœ ์ž๋™์œผ๋กœ ์˜ฎ๊ฒจ. ๋น„์šฉ์€ ์ค„์ด๋ฉด์„œ ์žฅ๊ธฐ ๋ณด๊ด€์ด ๊ฐ€๋Šฅํ•ด์ ธ.

3. Kafka Streams ๊ฐœ์„ 
Kafka ์ž์ฒด์— ํฌํ•จ๋œ ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์•ผ. Flink๋ณด๋‹ค ๊ฐ€๋ณ๊ณ  ๊ฐ„๋‹จํ•ด์„œ ์ธ๊ธฐ๊ฐ€ ๋†’์•„์ง€๊ณ  ์žˆ์–ด.

4. ํด๋ผ์šฐ๋“œ ๋„ค์ดํ‹ฐ๋ธŒ
Confluent Cloud, Amazon MSK ๊ฐ™์€ ๋งค๋‹ˆ์ง€๋“œ ์„œ๋น„์Šค๊ฐ€ ๋ฐœ์ „ํ•˜๊ณ  ์žˆ์–ด. ์šด์˜ ๋ถ€๋‹ด ์—†์ด ์“ธ ์ˆ˜ ์žˆ์–ด! โ˜๏ธ

๐ŸŒŠ Flink์˜ ๋ฏธ๋ž˜


1. Flink SQL ๊ฐ•ํ™”
SQL๋งŒ์œผ๋กœ ๋ณต์žกํ•œ ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋์–ด. ์ง„์ž… ์žฅ๋ฒฝ์ด ๋‚ฎ์•„์ง€๋Š” ๊ฑฐ์ง€!

// Flink SQL๋กœ ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ
CREATE TABLE clicks (
    user_id STRING,
    product_id STRING,
    event_time TIMESTAMP(3),
    WATERMARK FOR event_time AS event_time - INTERVAL '5' SECOND
) WITH (
    'connector' = 'kafka',
    'topic' = 'user-clicks',
    'properties.bootstrap.servers' = 'localhost:9092'
);

-- 1๋ถ„ ์œˆ๋„์šฐ๋กœ ํด๋ฆญ ์ˆ˜ ๊ณ„์‚ฐ
SELECT 
    user_id,
    COUNT(*) as click_count,
    TUMBLE_START(event_time, INTERVAL '1' MINUTE) as window_start
FROM clicks
GROUP BY 
    user_id,
    TUMBLE(event_time, INTERVAL '1' MINUTE);

2. ๋จธ์‹ ๋Ÿฌ๋‹ ํ†ตํ•ฉ
Flink ML์ด ๋ฐœ์ „ํ•˜๋ฉด์„œ ์ŠคํŠธ๋ฆผ ๋ฐ์ดํ„ฐ์— ๋จธ์‹ ๋Ÿฌ๋‹์„ ๋ฐ”๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋์–ด.

3. Stateful Functions
์„œ๋ฒ„๋ฆฌ์Šค ์Šคํƒ€์ผ๋กœ ์ƒํƒœ๋ฅผ ๊ฐ€์ง„ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์–ด. ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค์™€ ์ž˜ ์–ด์šธ๋ ค!

4. Kubernetes ๋„ค์ดํ‹ฐ๋ธŒ
Kubernetes์—์„œ Flink๋ฅผ ์‰ฝ๊ฒŒ ๋ฐฐํฌํ•˜๊ณ  ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋์–ด. ์˜คํ† ์Šค์ผ€์ผ๋ง๋„ ์ง€์›ํ•ด!

๐ŸŽฏ ์ƒˆ๋กœ์šด ํŒจ๋Ÿฌ๋‹ค์ž„


1. Lakehouse ์•„ํ‚คํ…์ฒ˜
๋ฐ์ดํ„ฐ ๋ ˆ์ดํฌ์™€ ๋ฐ์ดํ„ฐ ์›จ์–ดํ•˜์šฐ์Šค์˜ ์žฅ์ ์„ ๊ฒฐํ•ฉํ•œ ๊ฑฐ์•ผ. Kafka + Flink + Delta Lake ์กฐํ•ฉ์ด ์ธ๊ธฐ!

2. Real-time OLAP
์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ณต์žกํ•œ ๋ถ„์„ ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฑฐ์•ผ. Apache Druid, ClickHouse ๊ฐ™์€ ๋„๊ตฌ๋“ค๊ณผ ํ†ตํ•ฉ๋ผ.

3. Edge Computing
IoT ๊ธฐ๊ธฐ์—์„œ ์ง์ ‘ ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋Š” ๊ฑฐ์•ผ. ์ง€์—ฐ์‹œ๊ฐ„์„ ๊ทน๋„๋กœ ์ค„์ผ ์ˆ˜ ์žˆ์–ด!

4. AI/ML ํŒŒ์ดํ”„๋ผ์ธ
์‹ค์‹œ๊ฐ„ ํŠน์ง• ์ถ”์ถœ, ๋ชจ๋ธ ์„œ๋น™, ์˜จ๋ผ์ธ ํ•™์Šต๊นŒ์ง€ ๋ชจ๋‘ ์ŠคํŠธ๋ฆฌ๋ฐ์œผ๋กœ! ๐Ÿค–

๐Ÿ“š ํ•™์Šต ๋กœ๋“œ๋งต


์ž, ์ด์ œ ์–ด๋–ป๊ฒŒ ๊ณต๋ถ€ํ•ด์•ผ ํ• ์ง€ ์•Œ๋ ค์ค„๊ฒŒ!

๐ŸŽ“ ์ดˆ๊ธ‰ ๋‹จ๊ณ„ (1-2๊ฐœ์›”)


1์ฃผ์ฐจ: ๊ธฐ์ดˆ ๊ฐœ๋…
โ€ข ๋ฉ”์‹œ์ง• ์‹œ์Šคํ…œ์ด๋ž€?
โ€ข ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ vs ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ
โ€ข Kafka ์•„ํ‚คํ…์ฒ˜ ์ดํ•ด
โ€ข ๊ฐ„๋‹จํ•œ Producer/Consumer ์ž‘์„ฑ

2-3์ฃผ์ฐจ: Kafka ์‹ค์Šต
โ€ข ๋กœ์ปฌ์— Kafka ์„ค์น˜
โ€ข Topic, Partition ๊ฐœ๋… ์‹ค์Šต
โ€ข Consumer Group ๋™์ž‘ ํ™•์ธ
โ€ข ๊ฐ„๋‹จํ•œ ๋กœ๊ทธ ์ˆ˜์ง‘ ์‹œ์Šคํ…œ ๊ตฌ์ถ•

4-6์ฃผ์ฐจ: Flink ์ž…๋ฌธ
โ€ข Flink ์„ค์น˜ ๋ฐ ์‹คํ–‰
โ€ข DataStream API ๊ธฐ์ดˆ
โ€ข ๊ฐ„๋‹จํ•œ ๋ณ€ํ™˜ ์ž‘์—… (map, filter)
โ€ข Kafka์™€ ์—ฐ๋™

7-8์ฃผ์ฐจ: ์ฒซ ํ”„๋กœ์ ํŠธ
โ€ข ์‹ค์‹œ๊ฐ„ ๋‹จ์–ด ์นด์šดํŠธ
โ€ข ๊ฐ„๋‹จํ•œ ๋กœ๊ทธ ๋ถ„์„
โ€ข ๊ธฐ๋ณธ ๋ชจ๋‹ˆํ„ฐ๋ง ์„ค์ •

๐Ÿš€ ์ค‘๊ธ‰ ๋‹จ๊ณ„ (3-4๊ฐœ์›”)


Window ์—ฐ์‚ฐ
โ€ข Tumbling, Sliding, Session Window
โ€ข Event Time vs Processing Time
โ€ข Watermark ์ดํ•ด ๋ฐ ์„ค์ •

์ƒํƒœ ๊ด€๋ฆฌ
โ€ข Keyed State ํ™œ์šฉ
โ€ข Checkpoint ์„ค์ •
โ€ข Savepoint ํ™œ์šฉ

๊ณ ๊ธ‰ ์—ฐ์‚ฐ
โ€ข ์ŠคํŠธ๋ฆผ ์กฐ์ธ
โ€ข CEP (Complex Event Processing)
โ€ข Async I/O

์šด์˜ ๊ธฐ์ดˆ
โ€ข ๋ชจ๋‹ˆํ„ฐ๋ง ์„ค์ •
โ€ข ๋กœ๊ทธ ๋ถ„์„
โ€ข ๊ฐ„๋‹จํ•œ ์žฅ์•  ๋Œ€์‘

๐Ÿ’Ž ๊ณ ๊ธ‰ ๋‹จ๊ณ„ (6๊ฐœ์›” ์ด์ƒ)


์„ฑ๋Šฅ ์ตœ์ ํ™”
โ€ข ๋ณ‘๋ ฌ๋„ ํŠœ๋‹
โ€ข ๋„คํŠธ์›Œํฌ ๋ฒ„ํผ ์ตœ์ ํ™”
โ€ข ์ƒํƒœ ๋ฐฑ์—”๋“œ ์„ ํƒ
โ€ข ๋ฐฑํ”„๋ ˆ์…” ํ•ด๊ฒฐ

๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ ์„ค๊ณ„
โ€ข ๋ฉ€ํ‹ฐ ํด๋Ÿฌ์Šคํ„ฐ ์•„ํ‚คํ…์ฒ˜
โ€ข ์žฌํ•ด ๋ณต๊ตฌ ์ „๋žต
โ€ข ๋ณด์•ˆ ์„ค์ •
โ€ข ๋น„์šฉ ์ตœ์ ํ™”

๊ณ ๊ธ‰ ํŒจํ„ด
โ€ข Lambda ์•„ํ‚คํ…์ฒ˜
โ€ข Kappa ์•„ํ‚คํ…์ฒ˜
โ€ข Event Sourcing
โ€ข CQRS ํŒจํ„ด

์‹ค์ „ ํ”„๋กœ์ ํŠธ
โ€ข ์‹ค์‹œ๊ฐ„ ์ถ”์ฒœ ์‹œ์Šคํ…œ
โ€ข ์‚ฌ๊ธฐ ํƒ์ง€ ์‹œ์Šคํ…œ
โ€ข ์‹ค์‹œ๊ฐ„ ๋Œ€์‹œ๋ณด๋“œ
โ€ข IoT ๋ฐ์ดํ„ฐ ๋ถ„์„

๐Ÿ“– ์ถ”์ฒœ ํ•™์Šต ์ž๋ฃŒ

๊ณต์‹ ๋ฌธ์„œ
โ€ข Kafka Documentation: kafka.apache.org
โ€ข Flink Documentation: flink.apache.org

์ฑ…
โ€ข "Kafka: The Definitive Guide" - Neha Narkhede
โ€ข "Stream Processing with Apache Flink" - Fabian Hueske
โ€ข "Designing Data-Intensive Applications" - Martin Kleppmann

์˜จ๋ผ์ธ ๊ฐ•์˜
โ€ข Confluent Developer Courses
โ€ข Udemy Kafka & Flink ๊ฐ•์˜
โ€ข YouTube ํŠœํ† ๋ฆฌ์–ผ

์ปค๋ฎค๋‹ˆํ‹ฐ
โ€ข Kafka Users ๋ฉ”์ผ๋ง ๋ฆฌ์ŠคํŠธ
โ€ข Flink User Mailing List
โ€ข Stack Overflow
โ€ข ๊ตญ๋‚ด ๊ฐœ๋ฐœ์ž ์ปค๋ฎค๋‹ˆํ‹ฐ

๐Ÿ’ก ์‹ค๋ฌด ํŒ๊ณผ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค


โœ… Kafka ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค


1. ์ ์ ˆํ•œ ํŒŒํ‹ฐ์…˜ ์ˆ˜ ์„ ํƒ
โ€ข ์ฒ˜๋ฆฌ๋Ÿ‰ ๊ธฐ์ค€: ๋ชฉํ‘œ ์ฒ˜๋ฆฌ๋Ÿ‰ / ํŒŒํ‹ฐ์…˜๋‹น ์ฒ˜๋ฆฌ๋Ÿ‰
โ€ข ๋ณดํ†ต ๋ธŒ๋กœ์ปค๋‹น 2000-4000๊ฐœ ํŒŒํ‹ฐ์…˜์ด ์ ๋‹น
โ€ข ๋„ˆ๋ฌด ๋งŽ์œผ๋ฉด ์˜ค๋ฒ„ํ—ค๋“œ, ๋„ˆ๋ฌด ์ ์œผ๋ฉด ๋ณ‘๋ ฌ์„ฑ ๋ถ€์กฑ

2. ๋ณต์ œ ํŒฉํ„ฐ๋Š” ์ตœ์†Œ 3
โ€ข ํ”„๋กœ๋•์…˜์—์„œ๋Š” replication.factor=3 ๊ถŒ์žฅ
โ€ข min.insync.replicas=2๋กœ ์„ค์ •ํ•ด์„œ ์•ˆ์ „์„ฑ ํ™•๋ณด

3. ์••์ถ• ํ™œ์šฉ
โ€ข ๋„คํŠธ์›Œํฌ ๋Œ€์—ญํญ์ด ๋ณ‘๋ชฉ์ด๋ฉด ์••์ถ• ์‚ฌ์šฉ
โ€ข snappy๋‚˜ lz4 ์ถ”์ฒœ (์†๋„์™€ ์••์ถ•๋ฅ  ๊ท ํ˜•)

4. ๋ฐฐ์น˜ ํฌ๊ธฐ ์กฐ์ •
โ€ข batch.size์™€ linger.ms ํŠœ๋‹
โ€ข ์ฒ˜๋ฆฌ๋Ÿ‰ ์ค‘์š”ํ•˜๋ฉด ํฌ๊ฒŒ, ์ง€์—ฐ์‹œ๊ฐ„ ์ค‘์š”ํ•˜๋ฉด ์ž‘๊ฒŒ

5. ๋ฉฑ๋“ฑ์„ฑ ํ”„๋กœ๋“€์„œ ์‚ฌ์šฉ
โ€ข enable.idempotence=true ์„ค์ •
โ€ข ์ค‘๋ณต ๋ฉ”์‹œ์ง€ ๋ฐฉ์ง€

โœ… Flink ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค


1. Event Time ์‚ฌ์šฉ
โ€ข ๊ฐ€๋Šฅํ•˜๋ฉด ํ•ญ์ƒ Event Time ์‚ฌ์šฉ
โ€ข Processing Time์€ ํ…Œ์ŠคํŠธ๋‚˜ ๊ฐ„๋‹จํ•œ ์ผ€์ด์Šค์—๋งŒ

2. Watermark ์‹ ์ค‘ํ•˜๊ฒŒ ์„ค์ •
โ€ข ๋„ˆ๋ฌด ์งง์œผ๋ฉด ๋Šฆ์€ ๋ฐ์ดํ„ฐ ๋ˆ„๋ฝ
โ€ข ๋„ˆ๋ฌด ๊ธธ๋ฉด ๊ฒฐ๊ณผ ์ง€์—ฐ
โ€ข ๋ฐ์ดํ„ฐ ํŠน์„ฑ ํŒŒ์•… ํ›„ ๊ฒฐ์ •

3. ์ƒํƒœ ํฌ๊ธฐ ๊ด€๋ฆฌ
โ€ข ์ƒํƒœ๊ฐ€ ๋ฌดํ•œ์ • ์ปค์ง€์ง€ ์•Š๋„๋ก ์ฃผ์˜
โ€ข TTL ์„ค์ •์œผ๋กœ ์˜ค๋ž˜๋œ ์ƒํƒœ ์ž๋™ ์‚ญ์ œ
โ€ข ํ•„์š”ํ•˜๋ฉด RocksDB ๋ฐฑ์—”๋“œ ์‚ฌ์šฉ

// State TTL ์„ค์ • ์˜ˆ์‹œ
StateTtlConfig ttlConfig = StateTtlConfig
    .newBuilder(Time.days(7))
    .setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite)
    .setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired)
    .build();

ValueStateDescriptor<Long> descriptor = new ValueStateDescriptor<>("count", Long.class);
descriptor.enableTimeToLive(ttlConfig);

4. ๋ฆฌ์†Œ์Šค ์ ์ ˆํžˆ ํ• ๋‹น
โ€ข TaskManager ๋ฉ”๋ชจ๋ฆฌ ์ถฉ๋ถ„ํžˆ ํ• ๋‹น
โ€ข ๋„คํŠธ์›Œํฌ ๋ฒ„ํผ ์ ์ ˆํžˆ ์„ค์ •
โ€ข CPU ์ฝ”์–ด ์ˆ˜์™€ ๋ณ‘๋ ฌ๋„ ๋งž์ถ”๊ธฐ

5. ์ฒดํฌํฌ์ธํŠธ ์ตœ์ ํ™”
โ€ข ์ฆ๋ถ„ ์ฒดํฌํฌ์ธํŠธ ์‚ฌ์šฉ (RocksDB)
โ€ข ๋น„๋™๊ธฐ ์ฒดํฌํฌ์ธํŠธ ํ™œ์„ฑํ™”
โ€ข ์ฒดํฌํฌ์ธํŠธ ๊ฐ„๊ฒฉ ์ ์ ˆํžˆ ์„ค์ •

๐Ÿšซ ํ”ผํ•ด์•ผ ํ•  ์•ˆํ‹ฐํŒจํ„ด


โŒ Kafka
โ€ข ๋„ˆ๋ฌด ํฐ ๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๊ธฐ (1MB ์ด์ƒ)
โ€ข ํŒŒํ‹ฐ์…˜ ํ‚ค ์—†์ด ๋ณด๋‚ด๊ธฐ (์ˆœ์„œ ๋ณด์žฅ ์•ˆ ๋จ)
โ€ข Consumer์—์„œ ๋™๊ธฐ ์ฒ˜๋ฆฌ (๋А๋ ค์ง)
โ€ข ๋กœ๊ทธ ๋ณด๊ด€ ๊ธฐ๊ฐ„ ๋ฌดํ•œ์ • ์„ค์ •

โŒ Flink
โ€ข Processing Time์œผ๋กœ ์ค‘์š”ํ•œ ๊ณ„์‚ฐํ•˜๊ธฐ
โ€ข ์ƒํƒœ ํฌ๊ธฐ ์ œํ•œ ์—†์ด ์‚ฌ์šฉ
โ€ข ์ฒดํฌํฌ์ธํŠธ ์—†์ด ์šด์˜
โ€ข ๋ฐฑํ”„๋ ˆ์…” ๋ฌด์‹œํ•˜๊ธฐ
โ€ข ์™ธ๋ถ€ ์‹œ์Šคํ…œ ๋™๊ธฐ ํ˜ธ์ถœ (Async I/O ์จ์•ผ ํ•จ)

๐ŸŽฏ ์„ฑ๋Šฅ ํŠœ๋‹ ์ฒดํฌ๋ฆฌ์ŠคํŠธ


ํ•ญ๋ชฉ ํ™•์ธ ์‚ฌํ•ญ ๊ถŒ์žฅ ๊ฐ’
Kafka ํŒŒํ‹ฐ์…˜ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅํ•œ๊ฐ€? ์ปจ์Šˆ๋จธ ์ˆ˜์™€ ๋™์ผ
๋ณต์ œ ํŒฉํ„ฐ ๋ฐ์ดํ„ฐ ์•ˆ์ „ํ•œ๊ฐ€? 3 ์ด์ƒ
Flink ๋ณ‘๋ ฌ๋„ ๋ฆฌ์†Œ์Šค ํ™œ์šฉ ์ถฉ๋ถ„ํ•œ๊ฐ€? ํŒŒํ‹ฐ์…˜ ์ˆ˜์™€ ๋™์ผ
์ฒดํฌํฌ์ธํŠธ ์žฅ์•  ๋ณต๊ตฌ ๊ฐ€๋Šฅํ•œ๊ฐ€? 5-10์ดˆ ๊ฐ„๊ฒฉ
Watermark ๋Šฆ์€ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋˜๋‚˜? ๋ฐ์ดํ„ฐ ํŠน์„ฑ์— ๋งž๊ฒŒ
์ƒํƒœ ๋ฐฑ์—”๋“œ ์ƒํƒœ ํฌ๊ธฐ ์ ์ ˆํ•œ๊ฐ€? ํฐ ์ƒํƒœ๋Š” RocksDB
๋„คํŠธ์›Œํฌ ๋ฒ„ํผ ๋ฐฑํ”„๋ ˆ์…” ์—†๋Š”๊ฐ€? ๋ฉ”๋ชจ๋ฆฌ์˜ 10-20%

๐ŸŽฌ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ


์™€! ์—ฌ๊ธฐ๊นŒ์ง€ ์ฝ์—ˆ๋‹ค๋ฉด ์ •๋ง ๋Œ€๋‹จํ•ด! ๐ŸŽ‰

์šฐ๋ฆฌ๋Š” Kafka์™€ Flink๋ฅผ ๊นŠ์ด ์žˆ๊ฒŒ ์‚ดํŽด๋ดค์–ด. ๊ธฐ๋ณธ ๊ฐœ๋…๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ์•„ํ‚คํ…์ฒ˜, ํ•ต์‹ฌ ๊ธฐ๋Šฅ, ์‹ค์ „ ํ™œ์šฉ, ์šด์˜ ๋…ธํ•˜์šฐ, ๊ทธ๋ฆฌ๊ณ  ๋ฏธ๋ž˜ ํŠธ๋ Œ๋“œ๊นŒ์ง€ ๋ชจ๋‘ ๋‹ค๋ค˜์ง€.

ํ•ต์‹ฌ ์š”์•ฝ:

๐Ÿ“ฆ Kafka๋Š” ์ดˆ๊ณ ์† ๋ฉ”์‹œ์ง• ์‹œ์Šคํ…œ์ด์•ผ. ๋ฐ์ดํ„ฐ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ „๋‹ฌํ•˜๊ณ  ๋ฒ„ํผ๋งํ•˜๋Š” ์—ญํ• ์„ ํ•ด. ์ดˆ๋‹น ์ˆ˜๋ฐฑ๋งŒ ๊ฐœ์˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ณ , ํ™•์žฅ์„ฑ๊ณผ ๋‚ด๊ตฌ์„ฑ์ด ๋›ฐ์–ด๋‚˜.

๐ŸŒŠ Flink๋Š” ์ง„์งœ ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ ์—”์ง„์ด์•ผ. ๋ณต์žกํ•œ ๊ณ„์‚ฐ, ์ƒํƒœ ๊ด€๋ฆฌ, ์ด๋ฒคํŠธ ์‹œ๊ฐ„ ์ฒ˜๋ฆฌ๋ฅผ ์™„๋ฒฝํ•˜๊ฒŒ ์ง€์›ํ•ด. Exactly-once ๋ณด์žฅ์œผ๋กœ ๋ฐ์ดํ„ฐ ์ •ํ™•์„ฑ๋„ ์™„๋ฒฝํ•ด.

๐Ÿš€ ์ด ๋‘˜์„ ํ•จ๊ป˜ ์“ฐ๋ฉด ์ตœ๊ฐ•์˜ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ํŒŒ์ดํ”„๋ผ์ธ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด. ๋„ทํ”Œ๋ฆญ์Šค, ์šฐ๋ฒ„, ๋งํฌ๋“œ์ธ ๊ฐ™์€ ๊ธ€๋กœ๋ฒŒ ๊ธฐ์—…๋“ค์ด ์ด๋ฏธ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์ง€.

๐ŸŽ“ ๋‹ค์Œ ๋‹จ๊ณ„๋Š”?

1. ์ง์ ‘ ํ•ด๋ณด๊ธฐ: ๋กœ์ปฌ์— ์„ค์น˜ํ•ด์„œ ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด๋ด
2. ํ”„๋กœ์ ํŠธ ๋งŒ๋“ค๊ธฐ: ์‹ค์‹œ๊ฐ„ ๋กœ๊ทธ ๋ถ„์„์ด๋‚˜ ๊ฐ„๋‹จํ•œ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ ๊ตฌ์ถ•
3. ์ปค๋ฎค๋‹ˆํ‹ฐ ์ฐธ์—ฌ: ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๋“ค๊ณผ ๊ฒฝํ—˜ ๊ณต์œ 
4. ์ „๋ฌธ๊ฐ€ ๋ฉ˜ํ† ๋ง: ๋ง‰ํžˆ๋Š” ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด ์žฌ๋Šฅ๋„ท์—์„œ ์ „๋ฌธ๊ฐ€ ๋„์›€ ๋ฐ›๊ธฐ
5. ๊ณ„์† ํ•™์Šต: ๊ธฐ์ˆ ์€ ๊ณ„์† ๋ฐœ์ „ํ•˜๋‹ˆ๊นŒ ์ตœ์‹  ํŠธ๋ Œ๋“œ ํŒ”๋กœ์šฐ

์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆฌ๋ฐ์€ ์ด์ œ ์„ ํƒ์ด ์•„๋‹ˆ๋ผ ํ•„์ˆ˜์•ผ. ๋ชจ๋“  ์‚ฐ์—…์—์„œ ์‹ค์‹œ๊ฐ„ ์ธ์‚ฌ์ดํŠธ๊ฐ€ ๊ฒฝ์Ÿ๋ ฅ์ด ๋˜๊ณ  ์žˆ๊ฑฐ๋“ . ๊ธˆ์œต, ์ด์ปค๋จธ์Šค, ์†Œ์…œ ๋ฏธ๋””์–ด, ์ œ์กฐ, ๋ฌผ๋ฅ˜... ์–ด๋””๋“  ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•ด.

์ฒ˜์Œ์—๋Š” ์–ด๋ ต๊ฒŒ ๋А๊ปด์งˆ ์ˆ˜ ์žˆ์–ด. ์ƒˆ๋กœ์šด ๊ฐœ๋…๋„ ๋งŽ๊ณ , ์„ค์ •ํ•  ๊ฒƒ๋„ ๋งŽ๊ณ , ๊ณ ๋ คํ•  ๊ฒƒ๋„ ๋งŽ์ง€. ํ•˜์ง€๋งŒ ํ•˜๋‚˜์”ฉ ์ฐจ๊ทผ์ฐจ๊ทผ ๋ฐฐ์›Œ๊ฐ€๋‹ค ๋ณด๋ฉด ์–ด๋А์ƒˆ ์ „๋ฌธ๊ฐ€๊ฐ€ ๋˜์–ด ์žˆ์„ ๊ฑฐ์•ผ! ๐Ÿ’ช

๊ธฐ์–ตํ•ด:
โ€ข ์™„๋ฒฝํ•œ ์‹œ์Šคํ…œ์€ ์—†์–ด. ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๋ฅผ ์ดํ•ดํ•˜๊ณ  ์ƒํ™ฉ์— ๋งž๊ฒŒ ์„ ํƒํ•˜๋Š” ๊ฒŒ ์ค‘์š”ํ•ด.
โ€ข ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ๋กœ๊น…์€ ํ•„์ˆ˜์•ผ. ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ์„ ๋•Œ ๋น ๋ฅด๊ฒŒ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ด.
โ€ข ์ž‘๊ฒŒ ์‹œ์ž‘ํ•ด์„œ ์ ์ง„์ ์œผ๋กœ ํ™•์žฅํ•ด. ์ฒ˜์Œ๋ถ€ํ„ฐ ์™„๋ฒฝํ•˜๊ฒŒ ๋งŒ๋“ค๋ ค๊ณ  ํ•˜์ง€ ๋งˆ.
โ€ข ์ปค๋ฎค๋‹ˆํ‹ฐ๋ฅผ ํ™œ์šฉํ•ด. ํ˜ผ์ž ๊ณ ๋ฏผํ•˜์ง€ ๋ง๊ณ  ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์˜ ๊ฒฝํ—˜์„ ๋ฐฐ์›Œ.

์ž, ์ด์ œ ๋‹น์‹  ์ฐจ๋ก€์•ผ! ์ด ๊ธ€์—์„œ ๋ฐฐ์šด ๋‚ด์šฉ์„ ๋ฐ”ํƒ•์œผ๋กœ ๋ฉ‹์ง„ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ํŒŒ์ดํ”„๋ผ์ธ์„ ๋งŒ๋“ค์–ด๋ด. ๊ทธ๋ฆฌ๊ณ  ์–ธ์  ๊ฐ€ ๋‹น์‹ ์˜ ์„ฑ๊ณต ์‚ฌ๋ก€๋ฅผ ๋“ฃ๊ฒŒ ๋˜๊ธธ ๋ฐ”๋ผ! ๐ŸŒŸ

์งˆ๋ฌธ์ด๋‚˜ ๋„์›€์ด ํ•„์š”ํ•˜๋ฉด ์–ธ์ œ๋“  ๊ฐœ๋ฐœ์ž ์ปค๋ฎค๋‹ˆํ‹ฐ๋‚˜ ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์„ ํ™œ์šฉํ•ด๋ด. ์‹ค๋ฌด ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ์ „๋ฌธ๊ฐ€๋“ค์ด ๊ธฐ๊บผ์ด ๋„์™€์ค„ ๊ฑฐ์•ผ.

ํ–‰์šด์„ ๋นŒ์–ด! ํ™”์ดํŒ…! ๐Ÿš€๐ŸŽฏ๐Ÿ’ป

๐ŸŽ“ ๋” ๊นŠ์ด ๋ฐฐ์šฐ๊ณ  ์‹ถ๋‹ค๋ฉด?

์žฌ๋Šฅ๋„ท์—์„œ Kafka, Flink ์ „๋ฌธ๊ฐ€๋“ค์˜ ๋ฉ˜ํ† ๋ง๊ณผ ์ปจ์„คํŒ…์„ ๋ฐ›์•„๋ณด์„ธ์š”!
์‹ค๋ฌด ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ๊ฐœ๋ฐœ์ž๋“ค์ด ๋‹น์‹ ์˜ ํ”„๋กœ์ ํŠธ๋ฅผ ์„ฑ๊ณต์œผ๋กœ ์ด๋Œ์–ด๋“œ๋ฆฝ๋‹ˆ๋‹ค.

www.jaenung.net

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

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