๐Ÿ’ก ์ŠคํŠธ๋ฆฌ๋ฐ SQL์ด ๋น„์ฆˆ๋‹ˆ์Šค ์†๋„๋ฅผ 10๋ฐฐ ๋†’์ด๋Š” ๊ตฌ์ถ• ์ „๋žต ๋ฐ ์ฝ”๋“œ ์ ์šฉ ์‚ฌ๋ก€ ๋น„๊ฒฐ!

:hourglass_not_done: ์ŠคํŠธ๋ฆฌ๋ฐ SQL ๊ธฐ๋ฐ˜ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๊ฐ€ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ๋ถ„์„์„ ๋ณต์žกํ•œ ์ฝ”๋”ฉ ์—†์ด SQL๋กœ ์‹ค์‹œ๊ฐ„ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๊ณ , ์•„๋ž˜์˜ ์‹ค์ œ ๊ตฌํ˜„ ๋ฐฉ์‹์„ ํ™•์ธํ•˜์…”์„œ ์ฆ‰๊ฐ์ ์ธ ์ธ์‚ฌ์ดํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์„ธ์š”!

์ฃผ์š” ๊ธฐ๋Šฅ ํšจ๊ณผ ๋ฐ ์ด์ 

  • :high_voltage: ์‹ค์‹œ๊ฐ„ ์ธ์‚ฌ์ดํŠธ:
    • ๊ณ ๊ฐ ํ–‰๋™, ์‹œ์žฅ ๋™ํ–ฅ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํŒŒ์•…ํ•˜์—ฌ ์ฆ‰๊ฐ์ ์ธ ์˜์‚ฌ๊ฒฐ์ •์„ ๋‚ด๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • :laptop: ๊ฐœ๋ฐœ ํšจ์œจ์„ฑ ๊ทน๋Œ€ํ™”:
    • ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์ถ• ๋Œ€์‹ , ์นœ์ˆ™ํ•œ SQL๋กœ ์‹ค์‹œ๊ฐ„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‰ฝ๊ฒŒ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • :chart_increasing: ๋น„์ฆˆ๋‹ˆ์Šค ๋ฏผ์ฒฉ์„ฑ ํ–ฅ์ƒ:
    • ๋ณ€ํ™”ํ•˜๋Š” ์‹œ์žฅ ์ƒํ™ฉ์— ๋น ๋ฅด๊ฒŒ ๋Œ€์‘ํ•˜๊ณ , ์ƒˆ๋กœ์šด ๋น„์ฆˆ๋‹ˆ์Šค ๋ชจ๋ธ์„ ์œ ์—ฐํ•˜๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • :money_bag: ๋น„์šฉ ์ ˆ๊ฐ:
    • ๊ธฐ์กด์˜ ๋ณต์žกํ•œ ์‹œ์Šคํ…œ ๊ตฌ์ถ• ๋ฐ ์œ ์ง€๋ณด์ˆ˜ ๋น„์šฉ์„ ์ ˆ๊ฐํ•˜์—ฌ ํšจ์œจ์ ์ธ ์ž์› ๋ฐฐ๋ถ„์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

"์ฝ”๋“œ๋ฅผ ํ•œ ๋ฒˆ ์ž‘์„ฑํ•˜๋ฉด ๋ชจ๋“  ๊ณณ์—์„œ ์‹คํ–‰๋œ๋‹ค"๋Š” ์ŠคํŠธ๋ฆฌ๋ฐ SQL์˜ ์ฒ ํ•™์€ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์˜ ํŒจ๋Ÿฌ๋‹ค์ž„์„ ๋ฐ”๊พธ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋” ์ด์ƒ ๋ฐ์ดํ„ฐ ์—”์ง€๋‹ˆ์–ด๋ง์˜ ๋ณต์žก์„ฑ์— ์–ฝ๋งค์ด์ง€ ์•Š๊ณ , ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ์ง‘์ค‘ํ•˜์—ฌ ๋” ํฐ ๊ฐ€์น˜๋ฅผ ์ฐฝ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ƒ์„ธ ์ •๋ณด ๋ฐ ์‹ค์ œ ๊ตฌํ˜„ ์˜ˆ์ œ

  • ๋‚ด์žฅ ํ•จ์ˆ˜, UDF, ๋ฌผ๋ฆฌํ™”๋œ ๊ฒฐ๊ณผ, ๊ทธ๋ฆฌ๊ณ  ML ๋ฐ AI ๋ชจ๋ธ๊ณผ์˜ ํ†ตํ•ฉ์€ ์ŠคํŠธ๋ฆฌ๋ฐ SQL์„ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ๊ตฌ์ถ• ์‹œ ๋งค๋ ฅ์ ์ธ ์„ ํƒ์ง€๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

  • ์ „ํ†ต์ ์ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค(์˜ˆ: Postgres)์—์„œ์˜ SQL ์ฟผ๋ฆฌ๋Š” ๊ฒฝ๊ณ„๊ฐ€ ์žˆ๋Š” ์ฟผ๋ฆฌ๋กœ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋‚ด์˜ ์œ ํ•œํ•œ ๋ฐ์ดํ„ฐ ์ง‘ํ•ฉ์„ ๋Œ€์ƒ์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. :bar_chart: ์ด ๊ฒฝ๊ณ„ ๋ฐ์ดํ„ฐ์—๋Š” ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋˜๋Š” ์‹œ์ ์— ์กด์žฌํ•˜๋˜ ๋ฐ์ดํ„ฐ๋งŒ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ์ฟผ๋ฆฌ ์‹คํ–‰ ์ดํ›„์— ๋ฐœ์ƒํ•œ ๋ฐ์ดํ„ฐ ์ง‘ํ•ฉ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ์ตœ์ข… ๊ฒฐ๊ณผ์— ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€์‹ , ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•˜๋ ค๋ฉด ๋‹ค์‹œ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. :repeat_button:

  • ๋ฐ˜๋ฉด, ์ŠคํŠธ๋ฆฌ๋ฐ SQL ์ฟผ๋ฆฌ๋Š” ์ œํ•œ๋˜์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ ์ง‘ํ•ฉ, ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์œผ๋กœ๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ ์ด๋ฒคํŠธ ์ŠคํŠธ๋ฆผ์„ ๋Œ€์ƒ์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. :ocean::satellite_antenna: ์ด ๋ชจ๋ธ์—์„œ ์ŠคํŠธ๋ฆฌ๋ฐ SQL ์—”์ง„์€ ์ŠคํŠธ๋ฆผ์œผ๋กœ๋ถ€ํ„ฐ ์ด๋ฒคํŠธ๋ฅผ ํ•˜๋‚˜์”ฉ ์†Œ๋น„ํ•˜๋ฉฐ, ํƒ€์ž„์Šคํƒฌํ”„์™€ ์˜คํ”„์…‹์— ๋”ฐ๋ผ ์ด๋ฅผ ์ •๋ ฌํ•ฉ๋‹ˆ๋‹ค. :stopwatch:

  • ๋˜ํ•œ ์ŠคํŠธ๋ฆฌ๋ฐ SQL ์ฟผ๋ฆฌ๋Š” ๋ฌด๊ธฐํ•œ ์‹คํ–‰๋˜๋ฉฐ, ์ž…๋ ฅ์œผ๋กœ ์ด๋ฒคํŠธ๊ฐ€ ๋„์ฐฉํ•˜๋Š” ์ฆ‰์‹œ ์ด๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ , ์ƒํƒœ ์ €์žฅ์†Œ๋ฅผ ๊ฐฑ์‹ ํ•˜๋ฉฐ, ๊ฒฐ๊ณผ๋ฅผ ๊ณ„์‚ฐํ•˜๊ณ , ๋‚˜์•„๊ฐ€ ํ•˜์œ„ ์ŠคํŠธ๋ฆผ์œผ๋กœ ์ด๋ฒคํŠธ๋ฅผ ์ถœ๋ ฅํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. :counterclockwise_arrows_button::right_arrow:

  • ํŒจํ„ด 1: AI ๋ฐ ๋จธ์‹ ๋Ÿฌ๋‹๊ณผ์˜ ํ†ตํ•ฉ :robot::bar_chart:

๋ชฉ๋ก์˜ ์ฒซ ๋ฒˆ์งธ๋กœ, ์ŠคํŠธ๋ฆฌ๋ฐ SQL์€ SQL ์ฝ”๋“œ์—์„œ ์ธ๊ณต์ง€๋Šฅ(AI) ๋ฐ ๋จธ์‹ ๋Ÿฌ๋‹(ML) ๋ชจ๋ธ๊ณผ์˜ ์ง์ ‘์ ์ธ ํ†ตํ•ฉ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ŠคํŠธ๋ฆฌ๋ฐ SQL์„ ํ†ตํ•ด AI ๋˜๋Š” ML ๋ชจ๋ธ์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์€ ๊ทธ ์–ด๋А ๋•Œ๋ณด๋‹ค ์‰ฌ์›Œ์กŒ์Šต๋‹ˆ๋‹ค. :rocket:

  • AI๊ฐ€ ๋‹ค์–‘ํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ์›Œํฌ๋กœ๋“œ์˜ ์œ ๋ ฅํ•œ ๋Œ€์•ˆ์œผ๋กœ ๋ถ€์ƒํ•จ์— ๋”ฐ๋ผ, ์ŠคํŠธ๋ฆฌ๋ฐ SQL์€ ๋ณ„๋„์˜ ์ „์šฉ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋ฅผ ๋„์šฐ๊ณ , ์‹คํ–‰ํ•˜๊ณ , ๊ด€๋ฆฌํ•  ํ•„์š” ์—†์ด ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ๋ฐฉ์‹์œผ๋กœ ๋ชจ๋ธ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. :gear::right_arrow::high_voltage: ์ด ํŒจํ„ด์€ ์‚ฌ์šฉ์ž ์ •์˜ ํ•จ์ˆ˜(UDF) ํŒจํ„ด๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ๋ชจ๋ธ์„ ์ƒ์„ฑํ•˜๊ณ , ์‚ฌ์šฉํ•˜๋„๋ก ๋“ฑ๋กํ•œ ๋’ค, SQL ์ฟผ๋ฆฌ ์•ˆ์—์„œ ์ธ๋ผ์ธ์œผ๋กœ ํ˜ธ์ถœํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. :puzzle_piece::memo:

  • Flink ๋ฌธ์„œ์—์„œ๋Š” ๋ชจ๋ธ์„ ์—ฐ๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋” ์ž์„ธํžˆ ์„ค๋ช…ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค: :books:

CREATE MODEL sentiment_analysis_model 
INPUT (text STRING COMMENT 'Input text for sentiment analysis') 
OUTPUT (sentiment STRING COMMENT 'Predicted sentiment (positive/negative/neutral/mixed)')
COMMENT 'A model for sentiment analysis of text'
WITH (
    'provider' = 'openai',
    'endpoint' = 'https://api.openai.com/v1/chat/completions',
    'api-key' = '<YOUR KEY>',
    'model'='gpt-3.5-turbo',
    'system-prompt' = 'Classify the text below into one of the following labels: [positive, negative, neutral, mixed]. Output only the label.'
);
    • ๋ชจ๋ธ ์„ ์–ธ์€ ์ŠคํŠธ๋ฆฌ๋ฐ SQL ์ฝ”๋“œ์—์„œ ํ•ด๋‹น ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” ์ผ๋ จ์˜ ์—ฐ๊ฒฐ ์„ค์ •๊ณผ ๊ตฌ์„ฑ์ด๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. :wrench::gear: ์˜ˆ๋ฅผ ๋“ค์–ด, ์ด ๋ชจ๋ธ ์„ ์–ธ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋ฒคํŠธ์— ํฌํ•จ๋œ ํ…์ŠคํŠธ ๋ณธ๋ฌธ์— ๋Œ€ํ•ด ๊ฐ์„ฑ ๋ถ„์„์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. :speech_balloon::chart_increasing:

    • ์—ฌ๊ธฐ์„œ ML_PREDICT๋Š” ์‚ฌ์šฉ๋˜๋Š” ๊ตฌ์ฒด์ ์ธ ๋ชจ๋ธ ์ด๋ฆ„๊ณผ ํ…์ŠคํŠธ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ชจ๋‘ ํ•„์š”๋กœ ํ•œ๋‹ค๋Š” ์ ์— ์œ ์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. :memo::magnifying_glass_tilted_left:_

INSERT INTO my_sentiment_results 
    SELECT text, sentiment 
    FROM input_event_stream, LATERAL TABLE(ML_PREDICT('sentiment_analysis_model', text));
  • ํŒจํ„ด 2: ํ•จ์ˆ˜๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๋งž์ถคํ˜• ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง :puzzle_piece::gear:

    • ์ŠคํŠธ๋ฆฌ๋ฐ SQL์€ ๋‹ค์–‘ํ•œ ํ•จ์ˆ˜๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜์ง€๋งŒ, ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ์–ธ์–ด ๋ฌธ๋ฒ•์— ํฌํ•จ์‹œํ‚ค๋Š” ๊ฒƒ์€ ํ˜„์‹ค์ ์œผ๋กœ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์ด ์ง€์ ์—์„œ **์‚ฌ์šฉ์ž ์ •์˜ ํ•จ์ˆ˜(UDF)**๊ฐ€ ๋“ฑ์žฅํ•ฉ๋‹ˆ๋‹ค. UDF๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ์ •์˜ํ•œ ํ•จ์ˆ˜๋กœ, SQL ๋ฌธ ์•ˆ์—์„œ ํ”„๋กœ๊ทธ๋žจ์ด ์ด๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. :memo:

    • UDF๋Š” ํ•ต์‹ฌ SQL ๋ฌธ๋ฒ•์—์„œ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ์™ธ๋ถ€ ์‹œ์Šคํ…œ ํ˜ธ์ถœ์ด๋‚˜ ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ ์ƒ์„ฑ๋„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด, ๋…๋ฆฝ์ ์ธ ํŒŒ์ผ์— ํ•จ์ˆ˜ ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•œ ๋’ค, SQL ๋ฌธ์„ ์‹คํ–‰ํ•˜๊ธฐ ์ „์— ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ์ŠคํŠธ๋ฆฌ๋ฐ SQL ์„œ๋น„์Šค์— ์—…๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. :electric_plug::outbox_tray:

    • ์ด์ œ Flink ์˜ˆ์ œ๋ฅผ ํ•˜๋‚˜ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. :magnifying_glass_tilted_left:

// Declare the UDF in a separate java file
import org.apache.flink.table.api.*;
import org.apache.flink.table.functions.ScalarFunction;
import static org.apache.flink.table.api.Expressions.*;

//Returns 2 for high risk, 1 for normal risk, 0 for low risk.
public static class DefaultRiskUDF extends ScalarFunction {
  public Integer eval(Integer debt, 
    Integer interest_basis_points, 
    Integer annual_repayment,
    Integer timespan_in_years) throws Exception {

    int computed_debt = debt;

   	 for (int i = 0; i < timespan_in_years; i++) {
    	    computed_debt = computed_debt + 
(computed_debt * 
interest_basis_points / 10000)
- annual_repayment;
   	 }
    	if ( computed_debt >= debt )
return 2;
else if ( computed_debt < debt && computed_debt > debt / 2)
return 1;
else
return 0;
}
    • ๋‹ค์Œ์œผ๋กœ, Java ํŒŒ์ผ์„ JAR ํŒŒ์ผ๋กœ ์ปดํŒŒ์ผํ•œ ๋’ค ์ŠคํŠธ๋ฆฌ๋ฐ SQL ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ์œ„์น˜์— ์—…๋กœ๋“œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. :package::up_arrow: JAR๊ฐ€ ๋กœ๋“œ๋˜๋ฉด ํ”„๋ ˆ์ž„์›Œํฌ์— ์ด๋ฅผ ๋“ฑ๋กํ•ด์•ผ ํ•˜๋ฉฐ, ์ดํ›„ ์ŠคํŠธ๋ฆฌ๋ฐ SQL ๋ฌธ์—์„œ ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. :puzzle_piece::memo:
    • ์•„๋ž˜๋Š” ๋“ฑ๋ก๊ณผ ํ˜ธ์ถœ ๋ฌธ๋ฒ•์— ๋Œ€ํ•œ ์•„์ฃผ ๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค. :magnifying_glass_tilted_left:
-- Register the function.
CREATE FUNCTION DefaultRiskUDF
  AS 'com.namespace.SubstringUDF'
  USING JAR '<path-to-jar>';

-- Invoke the function to compute the risk of:
-- 100k debt over 15 years, 4% interest rate (400 basis points), and a 10k annual repayment rate
SELECT UserId, DefaultRiskUDF(100000, 400, 10000, 15) AS RiskRating
FROM UserFinances
WHERE RiskRating >= 1;
    • ์ด UDF๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋Œ€์ถœ์ž๊ฐ€ ๋Œ€์ถœ์„ **์—ฐ์ฒด(๋””ํดํŠธ)**ํ•  ์œ„ํ—˜์„ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋‚ฎ์€ ์œ„ํ—˜๊ณผ ๋ณดํ†ต ์œ„ํ—˜์— ๋Œ€ํ•ด์„œ๋Š” ๊ฐ๊ฐ 0๊ณผ 1์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , ๊ณ ์œ„ํ—˜ ์ฐจ์ž…์ž ๋˜๋Š” ์œ„ํ—˜ ์ƒํƒœ์˜ ๊ณ„์ •์— ๋Œ€ํ•ด์„œ๋Š” 2 ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. :credit_card::chart_decreasing:

    • ์ด UDF๋Š” ๋น„๊ต์  ๋‹จ์ˆœํ•˜๊ณ  ๋‹ค์†Œ ์ธ์œ„์ ์ธ ์˜ˆ์‹œ์ด์ง€๋งŒ, ํ‘œ์ค€ Java ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋‚ด์˜ ๊ฑฐ์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•ด ํ›จ์”ฌ ๋” ๋ณต์žกํ•œ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. :hot_beverage::brain: ์ŠคํŠธ๋ฆฌ๋ฐ SQL ์†”๋ฃจ์…˜์— ์—†๋Š” ๊ธฐ๋Šฅ์„ ์–ป๊ธฐ ์œ„ํ•ด ๊ตณ์ด ์ „์ฒด ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋ฅผ ๊ตฌ์ถ•ํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. ๋Œ€์‹ , ๋ถ€์กฑํ•œ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด ์—…๋กœ๋“œํ•œ ๋‹ค์Œ, ์ฝ”๋“œ์—์„œ ์ธ๋ผ์ธ์œผ๋กœ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. :package::right_arrow::puzzle_piece:

  • ํŒจํ„ด 3: ๊ธฐ๋ณธ ํ•„ํ„ฐ, ์ง‘๊ณ„, ์กฐ์ธ :magnifying_glass_tilted_right::bar_chart:

    • ์ด ํŒจํ„ด์€ ์ŠคํŠธ๋ฆฌ๋ฐ SQL ํ”„๋ ˆ์ž„์›Œํฌ์— ๊ธฐ๋ณธ์œผ๋กœ ๋‚ด์žฅ๋œ(ํ•˜์ง€๋งŒ ๊ฐ•๋ ฅํ•œ) ๊ธฐ๋Šฅ๋“ค๋กœ ๋‹ค์‹œ ์ดˆ์ ์„ ์˜ฎ๊น๋‹ˆ๋‹ค. ์ŠคํŠธ๋ฆฌ๋ฐ SQL์˜ ๋Œ€ํ‘œ์ ์ธ ํ™œ์šฉ ์‚ฌ๋ก€ ์ค‘ ํ•˜๋‚˜๋Š” ๋‹จ์ˆœ ํ•„ํ„ฐ๋ง์œผ๋กœ, ํŠน์ • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ๋ ˆ์ฝ”๋“œ๋งŒ ์œ ์ง€ํ•˜๊ณ  ๋‚˜๋จธ์ง€๋Š” ๋ฒ„๋ฆฌ๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. :broom:
    • ์•„๋ž˜ ์˜ˆ์‹œ๋Š” total_price > 10.00์ธ ๋ ˆ์ฝ”๋“œ๋งŒ ๋ฐ˜ํ™˜ํ•˜๋Š” SQL ํ•„ํ„ฐ๋ฅผ ๋ณด์—ฌ์ฃผ๋ฉฐ, ์ด ๊ฒฐ๊ณผ๋ฅผ ๊ฒฐ๊ณผ ํ…Œ์ด๋ธ”๋กœ ์ถœ๋ ฅํ•˜๊ฑฐ๋‚˜ ์ด๋ฒคํŠธ ์‹œํ€€์Šค ํ˜•ํƒœ์˜ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋‚ด๋ณด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. :outbox_tray::right_arrow:
SELECT *
FROM orders
WHERE total_price > 10.00;
    • ์œˆ๋„์ž‰๊ณผ ์ง‘๊ณ„๋Š” ์ŠคํŠธ๋ฆฌ๋ฐ SQL์—์„œ ๋˜ ํ•˜๋‚˜์˜ ๊ฐ•๋ ฅํ•œ ๊ตฌ์„ฑ ์š”์†Œ์ž…๋‹ˆ๋‹ค. :stopwatch::bar_chart: ์ด ๋ณด์•ˆ ์˜ˆ์ œ์—์„œ๋Š” 1๋ถ„ ํ…€๋ธ”๋ง ์œˆ๋„์šฐ ๋‚ด์—์„œ ํŠน์ • ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ์‹œ๋„๋ฅผ ๋ช‡ ๋ฒˆ ํ–ˆ๋Š”์ง€๋ฅผ ์ง‘๊ณ„ํ•ฉ๋‹ˆ๋‹ค(์Šฌ๋ผ์ด๋”ฉ, ์„ธ์…˜ ๋“ฑ ๋‹ค๋ฅธ ์œˆ๋„์šฐ ํƒ€์ž…๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค). :locked_with_key:
SELECT 
    COUNT(user_id) AS login_count, 
    TUMBLE_START(event_time, INTERVAL '1' MINUTE) AS window_start
FROM login_attempts
GROUP BY TUMBLE(event_time, INTERVAL '1' MINUTE);
    • ์œˆ๋„์šฐ ๋‚ด ๋กœ๊ทธ์ธ ์‹œ๋„ ํšŸ์ˆ˜๋ฅผ ๊ตฌํ•œ ๋’ค, ๊ทธ ๊ฐ’์ด ์ผ์ • ๊ธฐ์ค€(์˜ˆ: > 10)์„ ์ดˆ๊ณผํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ํ•„ํ„ฐ๋งํ•˜์—ฌ, ํ•ดํ‚น ๋ฐฉ์ง€ ๊ธฐ๋Šฅ์œผ๋กœ UDF ๋‚ด๋ถ€์˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ํŠธ๋ฆฌ๊ฑฐํ•ด ์ผ์‹œ์ ์œผ๋กœ ๊ณ„์ •์„ ์ž ๊ทธ๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. :prohibited::brain:
    • ๋งˆ์ง€๋ง‰์œผ๋กœ, ๋ช‡ ๊ฐ€์ง€ ๊ฐ„๋‹จํ•œ ๋ช…๋ น๋งŒ์œผ๋กœ๋„ ์—ฌ๋Ÿฌ ์ŠคํŠธ๋ฆผ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐ์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ŠคํŠธ๋ฆผ์„ ์ŠคํŠธ๋ฆผ(๋˜๋Š” ํ…Œ์ด๋ธ”)์œผ๋กœ ์กฐ์ธํ•˜๋Š” ์ž‘์—…์€ ์ŠคํŠธ๋ฆฌ๋ฐ ํ”„๋ ˆ์ž„์›Œํฌ ์—†์ด๋Š”, ํŠนํžˆ ์žฅ์•  ํ—ˆ์šฉ์„ฑ, ํ™•์žฅ์„ฑ, ์„ฑ๋Šฅ์„ ๊ณ ๋ คํ•  ๋•Œ ์ƒ๋‹นํžˆ ๊ตฌํ˜„ํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. :gear::chart_increasing: ์ด ์˜ˆ์ œ์—์„œ๋Š” ์ œํ’ˆ ID๋ฅผ ๊ธฐ์ค€์œผ๋กœ Orders ๋ฐ์ดํ„ฐ์™€ Product ๋ฐ์ดํ„ฐ๋ฅผ ์กฐ์ธํ•˜์—ฌ, ํ™•์žฅ๋œ Order + Product ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
SELECT * FROM Orders
INNER JOIN Product
ON Orders.productId = Product.id
    • ๋ชจ๋“  ์ŠคํŠธ๋ฆฌ๋ฐ ํ”„๋ ˆ์ž„์›Œํฌ(SQL ์—ฌ๋ถ€์™€ ๊ด€๊ณ„์—†์ด)๊ฐ€ ๊ธฐ๋ณธ ํ‚คโ€“์™ธ๋ž˜ ํ‚ค ์กฐ์ธ์„ ์ง€์›ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋ผ๋Š” ์ ์— ์œ ์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ถ€๋Š” ๊ธฐ๋ณธ ํ‚คโ€“๊ธฐ๋ณธ ํ‚ค ์กฐ์ธ๋งŒ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค. :thinking: ์ด์œ ๋ฅผ ๊ฐ„๋‹จํžˆ ๋งํ•˜๋ฉด, ์žฅ์•  ํ—ˆ์šฉ์„ฑ, ํ™•์žฅ์„ฑ, ์„ฑ๋Šฅ์„ ํ•จ๊ป˜ ๊ณ ๋ คํ•  ๋•Œ ์ด๋Ÿฌํ•œ ์กฐ์ธ ์œ ํ˜•์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ์ƒ๋‹นํžˆ ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ์‚ฌ์šฉ ์ค‘์ธ ์ŠคํŠธ๋ฆฌ๋ฐ SQL ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์กฐ์ธ์„ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š”์ง€, ์™ธ๋ž˜ ํ‚ค์™€ ๊ธฐ๋ณธ ํ‚ค ์กฐ์ธ์„ ๋ชจ๋‘ ์ง€์›ํ•˜๋Š”์ง€, ์•„๋‹ˆ๋ฉด ํ›„์ž๋งŒ ์ง€์›ํ•˜๋Š”์ง€๋ฅผ ๋ฐ˜๋“œ์‹œ ๊ฒ€ํ† ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. :magnifying_glass_tilted_left:
    • ์ง€๊ธˆ๊นŒ์ง€ ์ŠคํŠธ๋ฆฌ๋ฐ SQL์˜ ๋ช‡ ๊ฐ€์ง€ ๊ธฐ๋ณธ ๊ธฐ๋Šฅ์„ ์‚ดํŽด๋ณด์•˜์ง€๋งŒ, ์•„์ง ์ด ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋“ค์ด ๋” ๋ณต์žกํ•œ ๋ฌด์–ธ๊ฐ€๋ฅผ ๊ตฌ๋™ํ•˜๊ณ  ์žˆ์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ์ƒํƒœ๋กœ๋Š”, ์ด ๊ฒฐ๊ณผ๋“ค์„ ํ•˜์œ„ ์„œ๋น„์Šค๊ฐ€ ์†Œ๋น„ํ•˜๋„๋ก ๋˜ ๋‹ค๋ฅธ ์ด๋ฒคํŠธ ์ŠคํŠธ๋ฆผ์œผ๋กœ ์ถœ๋ ฅํ•˜๋Š” ์ˆ˜์ค€์— ๋จธ๋ฌด๋ฅด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. :outbox_tray::right_arrow: ์ด๋Š” ๋‹ค์Œ ํŒจํ„ด์ธ **์‚ฌ์ด๋“œ์นด(sidecar)**๋กœ ์ด์–ด์ง‘๋‹ˆ๋‹ค. :automobile:
  • ํŒจํ„ด 4: ์ŠคํŠธ๋ฆฌ๋ฐ SQL ์‚ฌ์ด๋“œ์นด :puzzle_piece::rocket:

    • ์ŠคํŠธ๋ฆฌ๋ฐ SQL ์‚ฌ์ด๋“œ์นด ํŒจํ„ด์€ Flink๋‚˜ Kafka Streams์™€ ๊ฐ™์€ ์™„์ „ํ•œ ๊ธฐ๋Šฅ์„ ๊ฐ–์ถ˜ ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ ์—”์ง„์˜ ๊ธฐ๋Šฅ์„, ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋™์ผํ•œ ์–ธ์–ด๋กœ ์ž‘์„ฑํ•˜์ง€ ์•Š๊ณ ๋„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. :gear::right_arrow:
    • ์ŠคํŠธ๋ฆฌ๋ฐ SQL ์ปดํฌ๋„ŒํŠธ๋Š” ์ง‘๊ณ„, ์กฐ์ธ๊ณผ ๊ฐ™์€ ํ’๋ถ€ํ•œ ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๊ณ , ํ•˜์œ„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์ž์‹ ๋งŒ์˜ ๋…๋ฆฝ์ ์ธ ๋Ÿฐํƒ€์ž„์—์„œ ๊ฒฐ๊ณผ ์ด๋ฒคํŠธ ์ŠคํŠธ๋ฆผ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. :counterclockwise_arrows_button::satellite_antenna:

    • ์ด ์˜ˆ์ œ์—์„œ INTERNAL_STREAM์€ SQL ์‚ฌ์ด๋“œ์นด๊ฐ€ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋กํ•˜๋Š” Kafka ํ† ํ”ฝ์ž…๋‹ˆ๋‹ค. :satellite_antenna: ์‚ฌ์ด๋“œ์นด ๊ธฐ๋ฐ˜ ์ด๋ฒคํŠธ ๋“œ๋ฆฌ๋ธ ์„œ๋น„์Šค๋Š” INTERNAL_STREAM์œผ๋กœ๋ถ€ํ„ฐ ์ด๋ฒคํŠธ๋ฅผ ์†Œ๋น„ํ•˜์—ฌ ์ ์ ˆํžˆ ์ฒ˜๋ฆฌํ•˜๊ณ , ๊ฒฝ์šฐ์— ๋”ฐ๋ผ OUTPUT_STREAM์œผ๋กœ ์ด๋ฒคํŠธ๋ฅผ ๋‹ค์‹œ ๋ฐœํ–‰ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. :counterclockwise_arrows_button::right_arrow:
    • ์‚ฌ์ด๋“œ์นด์˜ ๋˜ ๋‹ค๋ฅธ ์ผ๋ฐ˜์ ์ธ ํ™œ์šฉ ๋ฐฉ์‹์€ ์›น ์„œ๋น„์Šค์—์„œ ๋‹ค๋ฅธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ œ๊ณตํ•  ๋ฐ์ดํ„ฐ๋ฅผ ์ค€๋น„ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ์ปจ์Šˆ๋จธ๋Š” INPUT_STREAM์„ ์†Œ๋น„ํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•œ ๋’ค, ์›น ์„œ๋น„์Šค๊ฐ€ ์ž์ฒด ์ƒํƒœ ์ €์žฅ์†Œ๋กœ ๋ฌผ๋ฆฌํ™”ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. :file_cabinet: ๊ทธ ์ดํ›„์—๋Š” REST๋‚˜ RPC ์š”์ฒญ๊ณผ ๊ฐ™์€ ์š”์ฒญ/์‘๋‹ต ๋ฐฉ์‹์˜ ์งˆ์˜๋ฅผ ๋‹ค๋ฅธ ์„œ๋น„์Šค์— ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. :globe_with_meridians::incoming_envelope:

    • ์‚ฌ์ด๋“œ์นด ํŒจํ„ด์€ ๋‹ค์–‘ํ•œ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์ง€๋งŒ, SQL ์ฟผ๋ฆฌ์™€ ํ•จ๊ป˜ ์‚ฌ์ด๋“œ์นด ์„œ๋น„์Šค๋ฅผ ์ง์ ‘ ๊ตฌ์ถ•ยท๊ด€๋ฆฌยท๋ฐฐํฌํ•ด์•ผ ํ•œ๋‹ค๋Š” ์š”๊ตฌ์‚ฌํ•ญ์ด ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. :hammer_and_wrench::rocket:
    • ์ด ํŒจํ„ด์˜ ํ•ต์‹ฌ์ ์ธ ์žฅ์ ์€, ์ „์ฒด ๊ธฐ์ˆ  ์Šคํƒ์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ ๋„ ์ŠคํŠธ๋ฆฌ๋ฐ ๋ณ€ํ™˜๊ณผ ๋กœ์ง ์ฒ˜๋ฆฌ๋ฅผ ์ŠคํŠธ๋ฆฌ๋ฐ SQL ๊ธฐ๋Šฅ์— ๋งก๊ธธ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ๋Œ€์‹ , ์ŠคํŠธ๋ฆฌ๋ฐ SQL์˜ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ์กด ๊ธฐ์ˆ  ์Šคํƒ์— ๊ทธ๋Œ€๋กœ ์—ฐ๊ฒฐํ•˜์—ฌ, ์ง€๊ธˆ๊นŒ์ง€ ์‚ฌ์šฉํ•ด์˜จ ๋™์ผํ•œ ๋„๊ตฌ๋“ค๋กœ ์›น ์„œ๋น„์Šค๋‚˜ ๊ธฐํƒ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. :electric_plug::package:
  • ์ถ”๊ฐ€ ํ™œ์šฉ ์‚ฌ๋ก€ :link::bar_chart:

    • ๊ฐ ํŒจํ„ด์€ ๊ฐœ๋ณ„์ ์ธ ์—ฐ์‚ฐ์„ ๋…๋ฆฝ์ ์œผ๋กœ ๋ณด์—ฌ์ฃผ๋ฉฐ, ์ด๋Š” ๋น„๊ต์  ๋‹จ์ˆœํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—๋Š” ์ถฉ๋ถ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋” ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ์š”๊ตฌ์‚ฌํ•ญ์˜ ๊ฒฝ์šฐ, ์—ฌ๋Ÿฌ ํŒจํ„ด์„ ์—ฐ์‡„์ ์œผ๋กœ ์—ฐ๊ฒฐํ•˜์—ฌ ์•ž ๋‹จ๊ณ„์˜ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ์ „๋‹ฌ๋˜๋Š” ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋  ๊ฐ€๋Šฅ์„ฑ์ด ํฝ๋‹ˆ๋‹ค. :counterclockwise_arrows_button:
    • ์˜ˆ๋ฅผ ๋“ค์–ด, ๋จผ์ € ๋ฐ์ดํ„ฐ๋ฅผ ํ•„ํ„ฐ๋งํ•œ ๋’ค UDF๋ฅผ ์ ์šฉํ•˜๊ณ , ์ดํ›„ ML_PREDICT๋ฅผ ์‚ฌ์šฉํ•ด ML ๋˜๋Š” AI ๋ชจ๋ธ์„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์•„๋ž˜ ์˜ˆ์‹œ์˜ ์ „๋ฐ˜๋ถ€์— ๋‚˜ํƒ€๋‚˜ ์žˆ์Šต๋‹ˆ๋‹ค. :robot::right_arrow: ์ด์–ด์„œ ์ŠคํŠธ๋ฆฌ๋ฐ SQL์€ ์ฒซ ๋ฒˆ์งธ ML_PREDICT์˜ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์‹œ ํ•„ํ„ฐ๋งํ•˜๊ณ , UDF๋ฅผ ์ ์šฉํ•œ ๋‹ค์Œ, ์ตœ์ข…์ ์œผ๋กœ ๋˜ ๋‹ค๋ฅธ ML ๋ชจ๋ธ๋กœ ์ „๋‹ฌํ•œ ๋’ค OUTPUT_STREAM์— ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. :outbox_tray::sparkles:_

    • ์ŠคํŠธ๋ฆฌ๋ฐ SQL์€ ๋‹ค์ˆ˜์˜ ํด๋ผ์šฐ๋“œ ๋ฒค๋”์—์„œ ์„œ๋ฒ„๋ฆฌ์Šค ๊ธฐ๋Šฅ์œผ๋กœ ์ œ๊ณต๋˜๋ฉฐ, ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐ์ดํ„ฐ ์„œ๋น„์Šค๋ฅผ ๋น ๋ฅด๊ณ  ์‰ฝ๊ฒŒ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ณผ๊ธˆ๋ฉ๋‹ˆ๋‹ค. :cloud::high_voltage:
    • ๋‚ด์žฅ ํ•จ์ˆ˜, UDF, ๋ฌผ๋ฆฌํ™”๋œ ๊ฒฐ๊ณผ, ๊ทธ๋ฆฌ๊ณ  ML ๋ฐ AI ๋ชจ๋ธ๊ณผ์˜ ํ†ตํ•ฉ์„ ํ†ตํ•ด, ์ŠคํŠธ๋ฆฌ๋ฐ SQL์€ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋ฅผ ๊ตฌ์ถ•ํ•  ๋•Œ ์ถฉ๋ถ„ํžˆ ๊ณ ๋ คํ•  ๋งŒํ•œ ๋Œ€์•ˆ์ž„์ด ์ž…์ฆ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. :rocket::robot:

[์ถœ์ฒ˜] Why your next microservices should be streaming SQL-driven | InfoWorld

| This is a space where knowledge is not merely consumed, but respected, sovereign, and connectedโ€”shared together with cloud industry professionals (Bros).|
| ์ง€์‹์ด ์†Œ๋น„๋˜์ง€ ์•Š๊ณ  ์กด์ค‘ยท์ฃผ๊ถŒ๋ณด์žฅยท์—ฐ๊ฒฐ๋˜๋Š” ๊ณต๊ฐ„์œผ๋กœ ํด๋ผ์šฐ๋“œ ํ˜„์—… ์ „๋ฌธ๊ฐ€(Bro)์™€ ํ•จ๊ป˜ ๊ณต์œ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. |