ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

๋”๋ณด๊ธฐ
1๏ธโƒฃ Lymo ๊ฐœ๋ฐœ์ผ์ง€ #1: https://parkcool.tistory.com/8

2๏ธโƒฃ Lymo ๊ฐœ๋ฐœ์ผ์ง€ #2: https://parkcool.tistory.com/12

3๏ธโƒฃ Lymo ๊ฐœ๋ฐœ์ผ์ง€ #3: ํ˜„์žฌ ๊ธ€

4๏ธโƒฃ Lymo ๊ฐœ๋ฐœ์ผ์ง€ #4: https://parkcool.tistory.com/17

์•ˆ๋…•ํ•˜์„ธ์š”! ์ง€๋‚œ ๊ธ€์—์„œ๋Š” SSE๋ฅผ ๋ฒ„๋ฆฌ๊ณ  RTDB๋กœ ๊ฐˆ์•„ํƒ„ ์‚ฝ์งˆ๊ธฐ๋ฅผ ํ†ตํ•ด ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆฌ๋ฐ์„ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ–ˆ๋Š”์ง€ ์ด์•ผ๊ธฐํ–ˆ์—ˆ์ฃ .

 

์˜ค๋Š˜์€ ํ˜ผ๋ž€์˜ AI ๊ฐœ๋ฐœ ๊ณผ์ •์„ ๋‹ค๋ค„๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ทธ ํ˜ผ๋ž€ ์†์—์„œ ์ €๋ฅผ ๊ตฌ์›ํ•ด ์ค€ TypeScript์™€ Zod ์ด์•ผ๊ธฐ๋„์š”.

 

๐Ÿคฏ AI ๊ฐœ๋ฐœ์˜ ํ˜„์‹ค

ํ”ํžˆ AI ๊ฐœ๋ฐœ์ด๋ผ๊ณ  ํ•˜๋ฉด, "ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž…๋ ฅํ•˜๋‹ˆ AI๊ฐ€ ์ฒ™์ฒ™๋ฐ•์‚ฌ์ฒ˜๋Ÿผ ๋Œ€๋‹ตํ•ด์ฃผ๋Š”" ๋ชจ์Šต์„ ์ƒ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ €๋„ ๊ทธ๋žฌ๊ณ ์š”. Genkit ์ฝ”๋“œ ๋ช‡ ์ค„๋กœ LLM์„ ํ˜ธ์ถœํ•˜๊ณ , ๊ฐ€์‚ฌ๋ฅผ ๋ฒˆ์—ญํ•ด๋‹ฌ๋ผ๊ณ  ๋ช…๋ นํ•˜๋ฉด ๋๋‚  ์ค„ ์•Œ์•˜์ฃ . ์ €๋Š” ์‹ฌ์ง€์–ด ์œ„ ์‚ฌ์ง„์ฒ˜๋Ÿผ ํ”„๋กฌํ”„ํŠธ๋„ ์ •๋ง ์ž์„ธํ•˜๊ฒŒ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ ํ˜„์‹ค์€ ์‹œ๊ถ์ฐฝ์ด์—ˆ์Šต๋‹ˆ๋‹ค. Lymo๋Š” ๋ฐ˜์‘ ์†๋„๋ฅผ ์œ„ํ•ด ๊ฐ€๋ณ๊ณ  ๋น ๋ฅธ gemini-2.5-flash-lite ๋ชจ๋ธ์„ ์ฃผ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์–˜๋Š” ๋น„์šฉ์ด ์ €๋ ดํ•˜๊ณ  ๋ฐ˜์‘ ์†๋„๊ฐ€ ๋น ๋ฆฟ๋น ๋ฆฟํ•œ ๋Œ€์‹ , ์ง€์นจ์„ ๋ฌด์‹œํ•˜๊ฑฐ๋‚˜ ๋”ด์†Œ๋ฆฌ, ํ—›์†Œ๋ฆฌ๋ฅผ ํ•˜๋Š” ๋นˆ๋„๊ฐ€ ๊ฝค ๋†’์•˜์Šต๋‹ˆ๋‹ค. ์‹ฌ์ง€์–ด ์ผ๋ฐ˜ ๋ชจ๋ธ์ธ gemini-2.5-flash ๋ชจ๋ธ์—์„œ๋„ ์ด๋Ÿฐ ํ˜„์ƒ์€ ์ ์ง€ ์•Š๊ฒŒ ์ผ์–ด๋‚ฌ์Šต๋‹ˆ๋‹ค. ๋Œ€ํ‘œ์ ์œผ๋กœ ๋ฐœ์ƒํ•œ ํ˜„์ƒ๋“ค์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๐Ÿคฏ 1. ์ง€์นจ ๋ฌด์‹œ

์ €๋Š” ๊ฐ€์‚ฌ ์ค‘๊ฐ„์— ๋“ค์–ด๊ฐ€๋Š” ์ถ”์ž„์ƒˆ(์˜ˆ: "Yeah", "Ooh", "Baby")์ฒ˜๋Ÿผ ์˜๋ฏธ ์—†๋Š” ๋ง์€ ๋ฒˆ์—ญํ•˜์ง€ ๋ง๊ณ  null์„ ๋ฐ˜ํ™˜ํ•˜๋ผ๊ณ  ๋ช…์‹œํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ AI๋Š” ๊ตณ์ด ์›๋ฌธ์„ ๊ทธ๋Œ€๋กœ ๋ฑ‰์–ด๋‚ด๊ฑฐ๋‚˜("Yeah"), ์–ต์ง€๋กœ ๋ฒˆ์—ญํ•˜๋ ค๊ณ  ์‹œ๋„ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

ํ•ด์„ ๋Œ€์ƒ ์–ธ์–ด์™€ ์›๋ฌธ ์–ธ์–ด๊ฐ€ ๋™์ผํ•œ ๊ฒฝ์šฐ์—๋„ null์„ ๋ฐ˜ํ™˜ํ•˜๋ผ๊ณ  ํ–ˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ€์‚ฌ๋ฅผ ํ•œ๊ตญ์–ด๋กœ ๋ฒˆ์—ญํ•˜๊ธฐ๋ฅผ ์š”์ฒญํ–ˆ๋Š”๋ฐ, ๊ฐ€์‚ฌ๊ฐ€ ์ด๋ฏธ ํ•œ๊ตญ์–ด์ธ ๊ฒฝ์šฐ์—๋Š” null๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด ๊ฒฝ์šฐ์—๋„ AI๋Š” ์›๋ฌธ์„ ๊ทธ๋Œ€๋กœ ๋ฑ‰์–ด๋‚ด๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•˜์Šต๋‹ˆ๋‹ค.

๐Ÿคฏ 2. ํ• ๋ฃจ์‹œ๋„ค์ด์…˜

์œ„ ์งค์„ ๋ณด์‹  ์  ์žˆ์œผ์‹ ๊ฐ€์š”? ChatGPT ์ถœ์‹œ ์ดˆ๋ฐ˜ ๋Œ€์ค‘๋“ค์—๊ฒŒ AI ํ™˜๊ฐ์ด๋ผ๋Š” ๊ฐœ๋…์— ๋Œ€ํ•ด ๊ฐ์ธ์‹œ์ผœ์ค€ ์‚ฌ๋ก€์˜€์Šต๋‹ˆ๋‹ค.

 

AI๊ฐ€ ์—†๋Š” ์‚ฌ์‹ค์„ ์ง€์–ด๋‚ด๊ฑฐ๋‚˜, ๊ทธ๋Ÿด ๋“ฏํ•˜๊ฒŒ ๊พธ๋ฉฐ๋‚ด์–ด ์ง„์‹ค์ธ ์–‘ ๋‹ต๋ณ€ํ•˜๋Š” ํ•˜๋Š” ๊ฒƒ์„ ํ™˜๊ฐ, ํ• ๋ฃจ์‹œ๋„ค์ด์…˜์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ œ ๊ฒฝ์šฐ์—๋Š” ์ตœ์‹  ๋…ธ๋ž˜์— ๋Œ€ํ•ด ํ•ด์„์„ ์š”์ฒญํ–ˆ์„ ๋•Œ ํŠนํžˆ ๋งŽ์ด ๋ฐœ์ƒํ–ˆ๋Š”๋ฐ์š”. AI๋Š” ํ•™์Šต๋œ ์‹œ์ ์˜ ์ดํ›„์˜ ์‚ฌ๊ฑด์— ๋Œ€ํ•ด์„œ๋Š” ์ „ํ˜€ ์•Œ์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•˜๋Š” ํ˜„์ƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค. (cutoff ๋ฌธ์ œ) ์•Œ์ง€ ๋ชปํ•˜๋Š” ์ •๋ณด์— ๋Œ€ํ•ด ๋ฌผ์–ด๋ณด๋‹ˆ ์—†๋Š” ์‚ฌ์‹ค์„ ์–ต์ง€๋กœ ๋งŒ๋“ค์–ด ์ด์ƒํ•œ ์†Œ๋ฆฌ๋ฅผ ํ•ด๋Œ€๋Š” ๊ฒƒ์ด์ฃ .

๐Ÿคฏ 3. ๋ฐ˜ํ™˜ ํ˜•์‹ ๋ถˆ์ผ์น˜

๊ฐ€์žฅ ์น˜๋ช…์ ์ธ ๋ฌธ์ œ๋Š” ๋ฐฐ์—ด ๊ธธ์ด ๋ถˆ์ผ์น˜์˜€์Šต๋‹ˆ๋‹ค. ๊ฐ€์‚ฌ 4์ค„์„ ๋ฐฐ์—ด๋กœ ๋ณด๋ƒˆ์œผ๋ฉด ๋ฒˆ์—ญ๋„ ์ •ํ™•ํžˆ 4์ค„์ด ์™€์•ผ, ์•ฑ ํ™”๋ฉด์—์„œ ์›๋ฌธ๊ณผ ๋ฒˆ์—ญ๋ฌธ์„ 1:1๋กœ ๋งค์นญํ•  ์ˆ˜ ์žˆ๊ฒ ์ฃ ? ๊ทธ๋Ÿฐ๋ฐ AI๊ฐ€ ์ž๊ธฐ ๋งˆ์Œ๋Œ€๋กœ ๋‘ ๋ฌธ์žฅ์„ ํ•ฉ์น˜๊ฑฐ๋‚˜, ํ•œ ๋ฌธ์žฅ์„ ์ชผ๊ฐœ์„œ 3์ค„์ด๋‚˜ 5์ค„์„ ๋ฐ˜ํ™˜ํ•ด๋ฒ„๋ฆฌ๋Š” ๊ฒ๋‹ˆ๋‹ค. ์ด๋Ÿฌ๋ฉด ๋’ค๋”ฐ๋ผ์˜ค๋Š” ๋ชจ๋“  ๊ฐ€์‚ฌ ์‹ฑํฌ๊ฐ€ ๋‹ค ๋ฐ€๋ ค๋ฒ„๋ฆฌ๋Š” ๋Œ€์ฐธ์‚ฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

 

๐Ÿ› ๏ธ ํ”„๋กฌํ”„ํŠธ ๊นŽ๊ธฐ

์ฒ˜์Œ์—” AI๋ฅผ ์„ค๋“ํ•˜๋ ค๊ณ  ๋…ธ๋ ฅํ–ˆ์Šต๋‹ˆ๋‹ค. ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ˆ˜์ •ํ•˜๊ณ  ๋˜ ์ˆ˜์ •ํ–ˆ์ฃ .

  1. ํ”„๋กฌํ”„ํŠธ ๊นŽ๊ธฐ: "๋ชจ๋ฅด๋Š” ๊ฒƒ์€ ๋‹ต๋ณ€ํ•˜์ง€ ๋ง ๊ฒƒ.", "์ ˆ๋Œ€๋กœ ~~ํ•˜์ง€ ๋ง ๊ฒƒ.", ...์ฒ˜๋Ÿผ ํ•ด์„œ๋Š” ์•ˆ ๋˜๋Š” ๊ฒƒ์„ ๋ช…ํ™•ํžˆ ๋ช…์‹œํ•˜๊ณ , ์ž…/์ถœ๋ ฅ ํ˜•์‹๊ณผ ๊ทธ ์˜ˆ์‹œ๋ฅผ ์ œ์‹œํ•˜๊ณ , "๋„ˆ๋Š” ๊ฐ€์‚ฌ ๋ถ„์„ ์ „๋ฌธ๊ฐ€์•ผ.", "๋„ˆ๋Š” ๊ฐ€์‚ฌ ํ•ด์„์— ํƒ€๊ณ ๋‚œ ๋ฒˆ์—ญ๊ฐ€์•ผ.", ...์ฒ˜๋Ÿผ AI์˜ ์—ญํ• ์„ ์ •ํ•ด์ฃผ๋Š” ๋“ฑ AI์˜ ๋‹ต๋ณ€ ํ’ˆ์งˆ์„ ์˜ฌ๋ฆฌ๋Š” ๋ฐ ํšจ๊ณผ์ ์ธ ๊ฒƒ์œผ๋กœ ์•Œ๋ ค์ง„ ๋ฌธ๊ตฌ๋“ค์„ ๋‹ค์ˆ˜ ํฌํ•จ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.
  2. ๋ฒˆํ˜ธํ‘œ ๋ถ™์—ฌ์ฃผ๊ธฐ: ์ž…๋ ฅ ๋ฐ์ดํ„ฐ (JSON ๋ฐฐ์—ด)๋ฅผ ๊ทธ๋ƒฅ ์ฃผ์ง€ ์•Š๊ณ , ๊ฐ ๋ฌธ์žฅ ์•ž์— 1, 2, 3 ๋ฒˆํ˜ธ๋ฅผ ๋ถ™์—ฌ์„œ ์คฌ์Šต๋‹ˆ๋‹ค. "์ž, 1๋ฒˆ ์ค„ ๋ฒˆ์—ญํ•ด. 2๋ฒˆ ์ค„ ๋ฒˆ์—ญํ•ด."๋ผ๊ณ  ๋ช…ํ™•ํ•˜๊ฒŒ ๋ฒˆํ˜ธ๋ฅผ ๋ช…์‹œํ•œ ๊ฑฐ์ฃ .
  3. ์ƒ๊ฐํ•  ์‹œ๊ฐ„ ์ฃผ๊ธฐ: ๋ฐ”๋กœ ๋‹ต์„ ๋‚ด๋†“์œผ๋ผ๊ณ  ํ•˜๋ฉด ํ‹€๋ฆฌ๋‹ˆ๊นŒ, ์ถœ๋ ฅ ์Šคํ‚ค๋งˆ ๋งจ ์•ž์— reasoning ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. "๋‹ต๋ณ€ํ•˜๊ธฐ ์ „์— ์ด ๋ฌธ์žฅ์„ ์–ด๋–ป๊ฒŒ ๋ฒˆ์—ญํ• ์ง€, ํ˜น์‹œ ํ•ฉ์ณ์•ผ ํ•  ๋ฌธ์žฅ์€ ์—†๋Š”์ง€ ๋จผ์ € ์ƒ๊ฐํ•ด ๋ด" ๋ผ๊ณ  ๋‚™์„œ์žฅ์„ ์ฅ์—ฌ์ค€ ์…ˆ์ž…๋‹ˆ๋‹ค.

๊ทธ ๊ฒฐ๊ณผ ์ •๋‹ต๋ฅ ์ด (์ฒด๊ฐ์ƒ) 70%์—์„œ 80%๊นŒ์ง€ ์˜ฌ๋ผ๊ฐ”์Šต๋‹ˆ๋‹ค. ๋ถ€์กฑํ–ˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ 20%์˜ ํ™•๋ฅ ๋กœ ์—‰๋ง์ง„์ฐฝ์ธ ํ•ด์„์„ ๋ณด๊ฒŒ ํ•  ์ˆ˜๋Š” ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ํ• ๋ฃจ์‹œ๋„ค์ด์…˜ ๋ฌธ์ œ๋ฅผ ๊ทผ๋ณธ์ ์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

 

๐ŸŒ ์›น ๊ฒ€์ƒ‰ ๋Šฅ๋ ฅ ์ฅ์—ฌ์ฃผ๊ธฐ

ํ• ๋ฃจ์‹œ๋„ค์ด์…˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋Œ€ํ‘œ์ ์ธ ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜๋Š” ๊ฒ€์ƒ‰์ฆ๊ฐ•์ƒ์„ฑ, RAG์ž…๋‹ˆ๋‹ค. ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•˜๋ฉด AI์—๊ฒŒ ์™ธ๋ถ€ ์ง€์‹์„ ์ œ๊ณตํ•˜์—ฌ ์ •ํ™•ํ•˜๊ณ  ์ตœ์‹ ์˜ ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค. ์ด ๊ธฐ์ˆ ์„ ์ด์šฉํ•˜๋ฉด AI๊ฐ€ ๊ฒ€์ƒ‰ ์—”์ง„์„ ํ†ตํ•ด ์Šค์Šค๋กœ ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ๊ฒ€์ƒ‰ํ•˜๊ณ , ์ด ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์‘๋‹ต์„ ์ƒ์„ฑํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

import { genkit } from 'genkit';
import { vertexAI } from '@genkit-ai/google-genai';

const ai = genkit({
  plugins: [ vertexAI({ location: 'us-central1' }) ]
});

const response = await ai.generate({
  model: vertexAI.model('gemini-2.5-pro'),
  prompt: 'Explain Vertex AI in simple terms.',
});

console.log(response.text());

์ œ๊ฐ€ ์‚ฌ์šฉ ์ค‘์ธ AI ๊ฐœ๋ฐœ ํ”„๋ ˆ์ž„์›Œํฌ์ธ Genkit์—์„œ๋Š” ์ด ๊ธฐ๋Šฅ์„ ์ •๋ง ์‰ฝ๊ฒŒ ๊ฒฐํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Genkit์ด ์ œ๊ณตํ•˜๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ ์ค‘ ํ•˜๋‚˜์ธ Vertex AI๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. Vertex AI๋Š” Google Cloud์—์„œ ์ œ๊ณตํ•˜๋Š” AI ํ”Œ๋žซํผ์ธ๋ฐ, ์—ฌ๊ธฐ์„œ ์ œ๊ณตํ•˜๋Š” ์›น ๊ฒ€์ƒ‰ + ์Šคํฌ๋ž˜ํ•‘ + ๋‹ต๋ณ€ ๊ธฐ๋Šฅ์„ "์„ค์ • ๋”ธ๊น" ํ•œ ๋ฒˆ์œผ๋กœ ๊ฐ–๊ณ  ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์‹ค์ œ๋กœ ์ œ๊ฐ€ ์›น ๊ฒ€์ƒ‰ RAG์„ ๊ฒฐํ•ฉํ•˜๊ธฐ ์œ„ํ•ด ์ž‘์„ฑํ–ˆ๋˜ ์ฝ”๋“œ์˜ ์–‘์ด 10์ค„์ด ์ฑ„ ์•ˆ ๋ผ์š”. Genkit์„ ์‚ฌ์šฉํ•˜์‹œ๋Š” ๋ถ„๋“ค ์ค‘์— ์ž์‹ ์˜ ์„œ๋น„์Šค์— ์›น ๊ฒ€์ƒ‰ RAG๋ฅผ ๊ฒฐํ•ฉํ•ด๋ณด๊ณ  ์‹ถ์œผ์‹  ๋ถ„๋“ค์€ ๋ฌด์กฐ๊ฑด Vertex AI ์ฐพ์•„๋ณด์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค!! ๐Ÿฅน๐Ÿฅน ์ž์„ธํ•œ ์ •๋ณด๋Š” ๋‹ค์Œ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•ด๋ณด์„ธ์š”.

 

Vertex AI plugin

Learn how to use Google Cloud Vertex AI with Genkit across JavaScript, Go, and Python, including enterprise features like grounding, Vector Search, Model Garden, and evaluation metrics.

genkit.dev

 

ํ”„๋กฌํ”„ํŠธ๋ฅผ ๊ฐœ์„ ํ•˜๊ณ , ์›น ๊ฒ€์ƒ‰ RAG๊นŒ์ง€ ๋ถ™์ด๋‹ˆ ์ •๋‹ต๋ฅ ์ด 80%์—์„œ 90%๊นŒ์ง€๋Š” ์˜ฌ๋ผ๊ฐ„ ๊ฒƒ ๊ฐ™์•˜์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ์ตœ์‹  ๋…ธ๋ž˜์— ๋Œ€ํ•ด ๋ง์„ ์ง€์–ด๋‚ด๋Š” ํ˜„์ƒ์€ ์ •๋ง ๋งŽ์ด ๊ฐœ์„ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์„œ๋น„์Šค์— ํ•„์š”ํ•œ ๊ฑด 90%๊ฐ€ ์•„๋‹ˆ๋ผ 99%์˜ ์•ˆ์ •์„ฑ์ด์—ˆ์Šต๋‹ˆ๋‹ค. 10%์˜ ํ™•๋ฅ ๋กœ ์‚ฌ์šฉ์ž๋Š” ์—ฌ์ „ํžˆ ์—‰๋ง์ง„์ฐฝ์ธ ํ•ด์„์„ ๋ด์•ผ ํ–ˆ์œผ๋‹ˆ๊นŒ์š”.

 

๐Ÿ›ก๏ธ ๊ฒฐ๊ตญ ๋ฏฟ์„ ๊ฑด '์ฝ”๋“œ'๋ฟ์ด๋‹ค

์ €๋Š” AI๋ฅผ 100% ์‹ ๋ขฐํ•˜๋Š” ๊ฒƒ์„ ํฌ๊ธฐํ•˜๊ณ , ๊ธฐ๊ณ„์ ์ธ ๊ฒ€์ฆ ๋กœ์ง์œผ๋กœ AI๋ฅผ ๊ฒ€์ฆํ•˜๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค. AI๊ฐ€ ๋ฑ‰์€ ๊ฒฐ๊ณผ๋ฌผ์„ ์ฝ”๋“œ๊ฐ€ ์ฑ„์ ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์งœ๋Š” ๊ฒƒ์ด์ฃ .

1. ๐Ÿ“ ํ…์ŠคํŠธ ์œ ์‚ฌ๋„ ๊ฒ€์‚ฌ

const similarity = calculateSimilarity(lyrics[index].text, translation);
if (similarity > 0.8) return null;

AIํ•œํ…Œ "Yeah"๋ฅผ ๋ฒˆ์—ญํ•˜๋ผ๊ณ  ํ–ˆ๋”๋‹ˆ "Yeah"๋ผ๊ณ  ๊ทธ๋Œ€๋กœ ๋‹ตํ–ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ด์š”. ํ”„๋กฌํ”„ํŠธ๋กœ๋Š” ์ด๊ฑธ ๋ง‰๊ธฐ๊ฐ€ ๋„ˆ๋ฌด ํž˜๋“ญ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ฝ”๋“œ๋กœ ์ž…๋ ฅ๊ฐ’๊ณผ ์ถœ๋ ฅ๊ฐ’์˜ ํ…์ŠคํŠธ ์œ ์‚ฌ๋„ (Levenshtein ๊ฑฐ๋ฆฌ, ๋ ˆ๋ฒค์Šˆํƒ€์ธ ๊ฑฐ๋ฆฌ)๋ฅผ ๊ณ„์‚ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์œ ์‚ฌ๋„๊ฐ€ ์ง€๋‚˜์น˜๊ฒŒ ๋†’์œผ๋ฉด(๊ฑฐ์˜ ๋ฒ ๊ปด ์ผ์œผ๋ฉด) ์ฝ”๋“œ๊ฐ€ ๊ฐ•์ œ๋กœ ๊ทธ ๊ฐ’์„ null๋กœ ๋ฎ์–ด์”Œ์›Œ ๋ฒ„๋ฆฝ๋‹ˆ๋‹ค.

2. ๐ŸŒช๏ธ ์ž…๋ ฅ๊ฐ’ ํ•„ํ„ฐ๋ง

const filteredLyrics = lyrics.filter((lyric) => {
  const detectedLang = detectLanguage(lyric.text);
  return detectedLang !== targetLanguage;
});

๋ฒˆ์—ญ ๋Œ€์ƒ ์–ธ์–ด๊ฐ€ ํ•œ๊ตญ์–ด์ด๊ณ  ๋ฒˆ์—ญํ•  ๊ฐ€์‚ฌ๊ฐ€ "์•ˆ๋…•ํ•˜์„ธ์š”"์ธ๋ฐ, AI๊ฐ€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ "์•ˆ๋…•ํ•˜์„ธ์š”"๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฝ์šฐ๋„ ๋งŽ์•˜์Šต๋‹ˆ๋‹ค. ๊ฐ€์‚ฌ๊ฐ€ ์ด๋ฏธ ๋ฒˆ์—ญ ๋Œ€์ƒ ์–ธ์–ด์ธ ๊ฒฝ์šฐ์—๋Š” null์„ ๋ฐ˜ํ™˜ํ•˜๋ผ๊ณ  ๋ช…๋ นํ–ˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ ์š”. ๊ทธ๋ž˜์„œ ์ €๋Š” ์ด๋ฏธ ๋ฒˆ์—ญ ๋Œ€์ƒ ์–ธ์–ด์ธ ๋ฌธ์žฅ์€ ์ž…๋ ฅ๊ฐ’์—์„œ ์ œ์™ธํ•˜๋Š” ํ•„ํ„ฐ๋ง ๋กœ์ง์„ ์ถ”๊ฐ€ํ•˜์˜€์Šต๋‹ˆ๋‹ค. 1๋ฒˆ ๋ฐฉ๋ฒ•์ธ ํ…์ŠคํŠธ ์œ ์‚ฌ๋„ ๊ฒ€์‚ฌ ๋ฐฉ๋ฒ•๊ณผ๋Š” ๋‹ฌ๋ฆฌ, AI์—๊ฒŒ ๊ฐ’์„ ์ „๋‹ฌํ•˜๊ธฐ ์ „์— ํ•„ํ„ฐ๋งํ•ด๋ฒ„๋ฆฌ๋Š” ๋ฐฉ๋ฒ•์ด๊ธฐ ๋•Œ๋ฌธ์—, ์ž…์ถœ๋ ฅ ํ† ํฐ์„ ์ ˆ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ถ”๊ฐ€์ ์ธ ์ด์ ๋„ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

 

import { franc } from "franc-min";

const language = franc(text)

๊ฐ€์‚ฌ๊ฐ€ ์–ด๋–ค ์–ธ์–ด์ธ์ง€ ๊ตฌ๋ณ„ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” franc ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ™œ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ franc ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜๋ฉด ์œ„ ์ฝ”๋“œ์ฒ˜๋Ÿผ ๋งค์šฐ ์†์‰ฝ๊ฒŒ ๋ฌธ์žฅ์˜ ์–ธ์–ด๋ฅผ ํŒ๋ณ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

3. ๐Ÿ”„ ์žฌ์‹œ๋„

config: { temperature: Math.max(0.1, 0.4 - (retry * 0.3) / MAX_RETRIES) }

๋ฐฐ์—ด ๊ธธ์ด๊ฐ€ ์•ˆ ๋งž์œผ๋ฉด ์–ด๋–ป๊ฒŒ ํ• ๊นŒ์š”? ๋‹ต์ด ์—†์Šต๋‹ˆ๋‹ค. ๋งž์„ ๋•Œ๊นŒ์ง€ ๋‹ค์‹œ ์‹œํ‚ค๋Š” ์ˆ˜๋ฐ–์—์š”. ๋‹จ, ๊ทธ๋ƒฅ ๋‹ค์‹œ ์‹œํ‚ค๋ฉด ๋˜ ํ‹€๋ฆด ์ˆ˜ ์žˆ์œผ๋‹ˆ, ์žฌ์‹œ๋„ํ•  ๋•Œ๋งˆ๋‹ค temperature (์ฐฝ์˜์„ฑ ์ˆ˜์น˜)๋ฅผ ์กฐ๊ธˆ์”ฉ ๋‚ฎ์ท„์Šต๋‹ˆ๋‹ค. "๋‹ค์‹œ ํ•ด. ์ด๋ฒˆ์—” ์ข€ ๋œ ์ฐฝ์˜์ ์œผ๋กœ, ๋” ๊ธฐ๊ณ„์ ์œผ๋กœ ๋ฒˆ์—ญํ•ด" ๋ผ๊ณ  ๋‹ค๊ทธ์น˜๋Š” ๋กœ์ง์„ ๊ตฌํ˜„ํ•œ ๊ฑฐ์ฃ . (๋ฌผ๋ก  ์ด ๋ฐฉ๋ฒ• ์—ญ์‹œ ๋ณด์กฐ์ ์ธ ์ˆ˜๋‹จ์ผ ๋ฟ, ์™„๋ฒฝํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์žฅํ•˜๋Š” ์ˆ˜๋‹จ์€ ์•„๋‹™๋‹ˆ๋‹ค.)

 

์ด ๊ณผ์ •์—์„œ ๋ฐฐ์šด ๊ตํ›ˆ์€ ๋ช…ํ™•ํ•ฉ๋‹ˆ๋‹ค. "AI ๊ธฐ๋Šฅ ๊ตฌํ˜„์—์„œ AI ๋ชจ๋ธ์€ ๋ถ€ํ’ˆ์ผ ๋ฟ์ด๋‹ค. ๊ทธ ๋ถ€ํ’ˆ์„ ์™„๋ฒฝํ•˜๊ฒŒ ๋™์ž‘ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฑด ๊ฒฐ๊ตญ ๊ฐœ๋ฐœ์ž์˜ ์ง์ ‘ ์ž‘์„ฑํ•œ ์ฝ”๋“œ์ด๋‹ค."

 

๐Ÿค Typescript + Zod: ํ˜ผ๋ž€์Šค๋Ÿฌ์šด ๋ฐฑ์—”๋“œ์— ํ‰ํ™”๋ฅผ

AI์™€ ์”จ๋ฆ„ํ•˜๋ฉฐ ๋ฉ˜ํƒˆ์ด ๋‚˜๊ฐˆ ๋•Œ์ฏค, ์ €๋ฅผ ์น˜์œ ํ•ด ์ค€ ๊ฑด TypeScript์™€ Zod์˜€์Šต๋‹ˆ๋‹ค. 1์ธ ๊ฐœ๋ฐœ์˜ ์ตœ๋Œ€ ์žฅ์ ์€ ํ”„๋ก ํŠธ์—”๋“œ์™€ ๋ฐฑ์—”๋“œ๋ฅผ ํ˜ผ์ž ๋‹ค ์ง ๋‹ค๋Š” ์ ์ด๊ณ , ์ด ์žฅ์ ์„ ๊ทน๋Œ€ํ™”ํ•˜๋ ค๋ฉด ํƒ€์ž…๊ณผ ์Šคํ‚ค๋งˆ๋ฅผ ๊ณต์œ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

์ €๋Š” packages/schemas๋ผ๋Š” ํด๋”๋ฅผ ๋งŒ๋“ค๊ณ , ๊ฑฐ๊ธฐ์— DB ์Šคํ‚ค๋งˆ์™€ API์˜ ์ž…์ถœ๋ ฅ ์Šคํ‚ค๋งˆ๋ฅผ Zod๋กœ ์ •์˜ํ•ด์„œ ์–‘์ชฝ์—์„œ ๊ฐ™์ด ์ผ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ RTDB ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ์ •์˜์—์„œ Discriminated Union ํŒจํ„ด์ด ๋น›์„ ๋ฐœํ–ˆ์Šต๋‹ˆ๋‹ค.

export const StoryRequestSchema = z.union([
  // status๊ฐ€ "PENDING"์ผ ๋•Œ
  z.object({
    status: z.literal("PENDING"),
    trackId: z.string(),
  }),
  
  // ...
  
  // status๊ฐ€ "COMPLETED"์ผ ๋•Œ
  z.object({
    status: z.literal("COMPLETED"),
    generatedStory: StorySchema,
  }),
]);

Discriminated Union์ด๋ž€, ์—ฌ๋Ÿฌ ๊ฐ์ฒด ํƒ€์ž…์ด ๊ณตํ†ต๋œ ๋ฆฌํ„ฐ๋Ÿด ํ•„๋“œ(์—ฌ๊ธฐ์„œ๋Š” status)๋ฅผ ๊ฐ€์ง์œผ๋กœ์จ, ๊ทธ ํ•„๋“œ์˜ ๊ฐ’๋งŒ์œผ๋กœ ๊ฐ์ฒด์˜ ๊ตฌ์ฒด์ ์ธ ํƒ€์ž…์„ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์ •์˜ํ•ด๋‘๋ฉด, ์ฝ”๋“œ๋ฅผ ์งค ๋•Œ if (request.status === "PENDING") ์กฐ๊ฑด๋ฌธ ์•ˆ์œผ๋กœ ๋“ค์–ด๊ฐ€๋Š” ์ˆœ๊ฐ„, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ "์—ฌ๊ธฐ์—” ์•„์ง generatedStory๋Š” ์—†๊ณ  trackId๋Š” ๋ฌด์กฐ๊ฑด ์žˆ๋‹ค" ๋ผ๊ณ  ๋˜‘๋˜‘ํ•˜๊ฒŒ ์ถ”๋ก ํ•ด๋ฒ„๋ฆฝ๋‹ˆ๋‹ค. ์˜ˆ์‹œ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณผ๊นŒ์š”?

 

๐Ÿ“Œ Discriminated Union ์ ์šฉ ์‚ฌ๋ก€

const reducer = (_acc, chunk) => {
  if (chunk.status === "IN_PROGRESS" || chunk.status === "COMPLETED") {
    return {
      id: chunk.storyId,
      data: chunk,
    };
  }
}

์œ„ ์ฝ”๋“œ๋Š” ํ”„๋ก ํŠธ์—”๋“œ์—์„œ RTDB์— ์ž‘์„ฑ๋œ storyRequest ๊ฐ’์„ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. chunk๋Š” StoryRequestSchema์˜ ์Šคํ‚ค๋งˆ๋ฅผ ๋”ฐ๋ฅด๋Š” ๊ฐ’์œผ๋กœ, status ๊ฐ’์ด "IN_PROGRESS"์ด๊ฑฐ๋‚˜ "COMPLETED"์ด๋ฉด overview, lyricsTranslations ๋“ฑ ํ•ด์„ ์ •๋ณด๊ฐ€ ์„ ํƒ์ ์œผ๋กœ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด๋Ÿฌํ•œ ๊ฐ’๋“ค์„ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์‚ฌ์šฉ์ž ํ™”๋ฉด์— ๋ฟŒ๋ ค์ฃผ๋Š” ๊ตฌ์กฐ์ธ ๊ฒƒ์ด์ฃ .

 

์ด๊ฒƒ์„ Type Narrowing์ด๋ผ๊ณ  ํ•˜๋Š”๋ฐ์š”, ๋•๋ถ„์— ์˜ต์…”๋„ ์ฒด์ด๋‹(?.)์„ ๋‚จ๋ฐœํ•  ํ•„์š”๊ฐ€ ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค. AI๊ฐ€ ๋ฑ‰์–ด๋‚ด๋Š” ๋ฐ์ดํ„ฐ๋Š” ๋ถˆํ™•์‹คํ•˜์ง€๋งŒ, Zod์ด ์ง€์ผœ์ฃผ๋Š” ํƒ€์ž… ์‹œ์Šคํ…œ ๋•๋ถ„์—, ์ปดํŒŒ์ผ ์—๋Ÿฌ๊ฐ€ ์•ˆ ๋‚˜๋ฉด ๋Ÿฐํƒ€์ž„์—์„œ๋„ ์•ˆ์ „ํ•˜๋‹ค๋Š” ํ™•์‹ ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ํ™•์‹ ์€ ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ์„ ์—„์ฒญ๋‚˜๊ฒŒ ๋†’์—ฌ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

 

โžก๏ธ ์ •๋ฆฌ ๋ฐ ๋‹ค์Œ ๋‹จ๊ณ„

์ด๋ ‡๊ฒŒ ์šฐ์—ฌ๊ณก์ ˆ ๋์— Lymo์˜ ๋ฐฑ์—”๋“œ๊ฐ€ ์–ผ์ถ” ์™„์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

  • Firebase + Genkit์œผ๋กœ ์„œ๋ฒ„๋ฆฌ์Šค AI ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•˜๊ณ ,
  • RTDB๋กœ ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ์„ ๊ตฌํ˜„ํ•ด๋ƒˆ์œผ๋ฉฐ,
  • ํ”„๋กฌํ”„ํŠธ ์—”์ง€๋‹ˆ์–ด๋ง + ๊ฒฌ๊ณ ํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ AI๋ฅผ ์ฐธ๊ต์œกํ•˜๊ณ ,
  • Typescript + Zod๋กœ ๊ฒฌ๊ณ ํ•œ ํƒ€์ž… ์•ˆ์ •์„ฑ๋„ ํ™•๋ณดํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ˜ผ์ž์„œ ์ด ๋ชจ๋“  ์˜์‚ฌ๊ฒฐ์ •์„ ํ•˜๊ณ , ๋””๋ฒ„๊น…ํ•˜๊ณ , ์‚ฝ์งˆํ•˜๋Š” ๊ณผ์ •์ด ์ฐธ ๊ณ ๋˜๋ฉด์„œ๋„ ์ฆ๊ฑฐ์› ์Šต๋‹ˆ๋‹ค. "์ด๊ฒŒ ๋˜๋„ค?" ํ•˜๋Š” ์ˆœ๊ฐ„์˜ ์งœ๋ฆฟํ•จ์ด ๊ฐœ๋ฐœ์„ ๋ฉˆ์ถœ ์ˆ˜ ์—†๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ ๊ฐ™๋„ค์š”.

 

๋‹ค์Œ ํŽธ๋ถ€ํ„ฐ๋Š” ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ๊ธฐ๋กœ ๋„˜์–ด๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค. ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!!!

๊ณต์ง€์‚ฌํ•ญ
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
์ตœ๊ทผ์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€
Total
Today
Yesterday
๋งํฌ
ยซ   2026/08   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
๊ธ€ ๋ณด๊ด€ํ•จ