๋ชฉ์ฐจ

  1. ๊ฐœ์š”
  2. ๋ฐฐ๊ฒฝ์ง€์‹
  3. ๊ฐ€์„ค
    1. ๊ฐ€์„ค ๊ฒ€์ฆ
    2. ๊ฒ€์ฆ ๋ฐฉ๋ฒ•
    3. ํ…Œ์ŠคํŠธ ์„ค๊ณ„
    4. ๋ฒˆ๋“ค๋ง ์ „ (์›๋ณธ ์ฝ”๋“œ๋ฒ ์ด์Šค)
    5. ๋ฒˆ๋“ค๋ง ํ›„ (LLM์—๊ฒŒ ์ „๋‹ฌํ•  ๋‚ด์šฉ)
    6. ํ…Œ์ŠคํŠธ 2: ChatGPT๋ฅผ ํ†ตํ•œ ๋งค๋‰ด์–ผ ์ƒ์„ฑ
  4. ํ•œ๊ณ„์ 
  5. ๋ฐœ๊ฒฌ
  6. ๊ฒฐ๋ก 

๊ฐœ์š”

์ด ๊ธ€์—์„œ๋Š” ๋งŽ์€ ํŒŒ์ผ์„ ๋‚ดํฌํ•˜๋Š” ํŠน์ • TypeScript ์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ ๋ฒˆ๋“ค๋ง(bundling)์„ ํ†ตํ•ด 1๊ฐœ์˜ JavaScript ํŒŒ์ผ๋กœ ์••์ถ•ํ•œ ํ›„ LLM์—๊ฒŒ ์ „๋‹ฌํ•˜์˜€์„ ๋•Œ, LLM์ด ์ฝ”๋“œ๋ฒ ์ด์Šค์™€ ๊ด€๋ จ๋œ ์งˆ๋ฌธ์— ์–ผ๋งˆ๋‚˜ ์ •ํ™•ํ•˜๊ฒŒ ๋‹ต๋ณ€ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•  ๊ฒƒ์ด๋‹ค.

์†Œํ”„ํŠธ์›จ์–ด ์‹œ์Šคํ…œ ๋ถ„์„1 2์„ ์œ„ํ•ด์„œ๋Š” ์ฝ”๋“œ๋ฒ ์ด์Šค์— ์ž‘์„ฑ๋œ ํŒŒ์ผ๋“ค์„ โ€˜๋ฐฉ๋ฌธโ€™ํ•˜๋Š” ๊ฒƒ์ด ํ•„์ˆ˜์ ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ๊ด€ํ•œ ์งˆ๋ฌธ์— LLM์ด ๋Œ€๋‹ตํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š”, LLM์€ ๊ทธ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๊ด€๋ จ๋œ ๋ชจ๋“  ํŒŒ์ผ์„ ์ฝ์„ ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค3. ํ•œํŽธ, ์ฝ”๋“œ๋ฒ ์ด์Šค๋Š” ์ผ์ข…์˜ ๋น„์ •ํ˜• ๋ฐ์ดํ„ฐ์˜ ๋ชจ์Œ์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ๋Š”๋ฐ, LLM์ด ๋น„์ •ํ˜• ๋ฐ์ดํ„ฐ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” ์ผ๋ฐ˜ํ™”๋œ ๋ฐฉ๋ฒ•์€ ์—ฌ๋Ÿฟ ์กด์žฌํ•œ๋‹ค 4 5. ์ด ๊ธ€์—์„œ๋Š” ์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ RAGํ•˜๊ธฐ ์œ„ํ•œ ํ•œ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ์จ ์ฝ”๋“œ๋ฒ ์ด์Šค ์ „์ฒด๋ฅผ ์••์ถ•ํ•œ ํ›„ LLM์—๊ฒŒ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์„ ์‹œ๋„ํ•  ๊ฒƒ์ด๋‹ค.

๋ฐฐ๊ฒฝ์ง€์‹

โ€œ์ฝ”๋“œ๋ฒ ์ด์Šคโ€๋ž€?

๋ฆฌ๋ˆ…์Šค ์ปค๋„์˜ ์ฝ”๋“œ๋ฒ ์ด์Šค ์‚ฌ์ง„: ๋ฆฌ๋ˆ…์Šค ์ฝ”๋“œ๋ฒ ์ด์Šค

ํ•œ ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์ด๋ฃจ๋Š” ์†Œ์Šค ์ฝ”๋“œ ํŒŒ์ผ์˜ ๋ชจ์ž„์„ ์ฝ”๋“œ๋ฒ ์ด์Šค๋ผ๊ณ  ํ•œ๋‹ค.

์†Œํ”„ํŠธ์›จ์–ด์˜ ๊ธฐ๋Šฅ์ด ๋งŽ์„์ˆ˜๋ก, ์ฝ”๋“œ๋ฒ ์ด์Šค๋Š” ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ฐฉ๋Œ€ํ•ด์ง„๋‹ค.

LLM์ด ๋ฐฉ๋Œ€ํ•œ ์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ ์ดํ•ดํ•˜๋ ค๋ฉด

์ฝ”๋“œ๋ฒ ์ด์Šค์™€ ๊ด€๋ จ๋œ ์ž‘์—…์„ LLM์ด ์ฒ˜๋ฆฌํ•ด์ค„ ์ˆ˜ ์žˆ์„๊นŒ?

  • โ€œ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€์— ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ•ด์ค˜โ€
  • โ€œ์†Œ์Šค์ฝ”๋“œ์—์„œ 'ํœด๋ฉด ๊ณ„์ •'๊ณผ ๊ด€๋ จ๋œ ๋กœ์ง์„ ์ œ๊ฑฐํ•ด์ค˜โ€
  • โ€œ์˜ค๋Š˜ ์ž‘์—…ํ•œ ์ฝ”๋“œ๋“ค์— ๋Œ€ํ•ด ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์ค˜โ€
  • โ€œ์ด ํด๋”์— ๋“ค์–ด์žˆ๋Š” ์†Œ์Šค์ฝ”๋“œ๋“ค์„ ํ•œ ๋ฒˆ ๊ฒ€ํ† ํ•ด์ค˜โ€

๋‹น์—ฐํ•˜๊ฒŒ๋„ ์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ LLM์—๊ฒŒ ์ „๋‹ฌํ•˜๋ฉด ๋  ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ ์ฝ”๋“œ๋ฒ ์ด์Šค๊ฐ€ ๋ฐฉ๋Œ€ํ• ์ˆ˜๋ก ๊ณ ๋ คํ•  ์ ์ด ๋Š˜์–ด๋‚œ๋‹ค.

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

์ด๊ฒƒ์€ ๋น„๋‹จ ์ฝ”๋“œ๋ฒ ์ด์Šค๋งŒ์˜ ๋ฌธ์ œ๋Š” ์•„๋‹ˆ๋‹ค. LLM์„ ํ†ตํ•ด ๋ฐฉ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ฒ˜๋ฆฌ์‹œํ‚ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ฐ˜๋“œ์‹œ ํ•ด๊ฒฐํ•ด์•ผ ํ•˜๋Š” ๋ฌธ์ œ์ด๋‹ค.

RAG์˜ ํ•„์š”์„ฑ๊ณผ ํ•œ๊ณ„

RAG์˜ ์ ˆ์ฐจ ์‚ฌ์ง„: RAG์˜ ์ ˆ์ฐจ

๋ฐฉ๋Œ€ํ•œ ์ง€์‹์„ LLM์—๊ฒŒ ์•Œ๋ ค์ฃผ๋Š” ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜๋กœ RAG(Retrieval-Augmented Generation)๊ฐ€ ์žˆ๋‹ค.

RAG๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ์ˆ˜๋‹จ ์ค‘ ํ•˜๋‚˜๋กœ๋Š” semantic search๊ฐ€ ์žˆ๋‹ค. ์‰ฝ๊ฒŒ ๋งํ•˜์ž๋ฉด ์œ ์‚ฌ๋„ ๊ฒ€์ƒ‰์„ ํ†ตํ•œ ์ง€์‹ ์„ ํƒ์ด๋‹ค. ์‘๋‹ต์„ ์ƒ์„ฑํ•˜๊ธฐ ์ „์— ์ง€์‹ ๋ฒ ์ด์Šค์—์„œ โ€˜์งˆ๋ฌธ๊ณผ ๊ด€๋ จ๋˜์—ˆ์„ ํ™•๋ฅ ์ด ๋†’์€ ์ •๋ณด๋“ค๋งŒ ์ฟผ๋ฆฌโ€™ํ•˜์—ฌ LLM์—๊ฒŒ ์ „๋‹ฌํ•œ๋‹ค. ์งˆ๋ฌธ๋งˆ๋‹ค ํ•„์š”ํ•œ ์ง€์‹๋งŒ์„ ์ถ”๋ ค์„œ ๋‹ต๋ณ€์— ๋ฐ˜์˜ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šคํ™”๋ฅผ ์•„๋ฌด๋ฆฌ ์ž˜ ํ–ˆ๋”๋ผ๋„, ์›ํ•˜๋Š” ์ง€์‹์ด ์ œ๋Œ€๋กœ ์กฐํšŒ๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๊ฒฐ๊ตญ ๋‹ต๋ณ€์˜ ํ’ˆ์งˆ์ด ๋–จ์–ด์งˆ ๊ฒƒ์ด๋‹ค.

์ฆ‰, RAG ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•˜์—ฌ LLM์˜ ์‘๋‹ต ํ’ˆ์งˆ์„ ๋†’์ผ ์ˆ˜ ์žˆ์ง€๋งŒ, ์ง€์‹์„ ์ ์ ˆํ•˜๊ฒŒ ์ œ๊ณตํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ์€ ๋ณ€์น˜ ์•Š๋Š”๋‹ค.

RAG ๋ฐฉ๋ฒ•๋ก ์€ ์ง€๊ธˆ๋„ ์ƒˆ๋กญ๊ฒŒ ์—ฐ๊ตฌ๋˜๊ณ  ์žˆ๋‹ค.

๊ฐ€์„ค

์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ (LLM์ด ํ—ˆ์šฉํ•˜๋Š” ์„ ์—์„œ) ํ†ต์งธ๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด, ์ฝ”๋“œ๋ฒ ์ด์Šค ์ „์ฒด์— ๋Œ€ํ•œ ์œ ์˜๋ฏธํ•œ RAG๋ฅผ ๋น„๊ต์  ์‰ฝ๊ฒŒ ์„ฑ์ทจํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

์ด๋Ÿฌํ•œ RAG์˜ ์žฅ์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์„ ๊ฒƒ์ด๋‹ค.

  • RAG๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด โ€˜๊ฒ€์ƒ‰ ์‹œ์Šคํ…œโ€™์„ ๋ฐ˜๋“œ์‹œ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.
  • ์ฝ”๋“œ๋ฒ ์ด์Šค ์ „์ฒด์— ๋Œ€ํ•œ ๋ฌธ๋งฅ์„ ํ™•๋ณดํ•  ์ˆ˜ ์žˆ์–ด, ์งˆ๋ฌธ์ž๊ฐ€ ๊ณ ๋ คํ•˜์ง€ ๋ชปํ•œ ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ๋„ LLM์ด ํŒŒ์•…ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

ํ•ด๊ฒฐํ•ด์•ผ ํ•˜๋Š” ๋ถ€๋ถ„์€ ์•„๋ž˜์™€ ๊ฐ™์„ ๊ฒƒ์ด๋‹ค.

  • ์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ โ€˜์••์ถ•'ํ•ด์•ผ ํ•œ๋‹ค.
  • ๋ฌด์†์‹ค ์••์ถ•์€ ์‚ฌ์‹ค์ƒ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ธฐ์—, ์ •๋ณด ์†์‹ค์„ ๊ฐ์ˆ˜ํ•˜๊ณ  ์••์ถ•์„ ์‹œ๋„ํ•ด์•ผ ํ•œ๋‹ค.
  • ์••์ถ• ๋ฐฉ๋ฒ•์˜ ์ผ๋ฐ˜ํ™”๊ฐ€ ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค.

๊ฐ€์„ค ๊ฒ€์ฆ

๊ฒ€์ฆ ๋ฐฉ๋ฒ•

์ฝ”๋“œ๋ฒ ์ด์Šค ์ „์ฒด๋ฅผ LLM์— RAGํ•˜๊ธฐ ์œ„ํ•œ ์••์ถ• ๋ฐฉ๋ฒ•์œผ๋กœ์จ ๋ฒˆ๋“ค๋ง์„ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•œ๋‹ค.

๋งŽ์€ ํŒŒ์ผ์„ ๋‚ดํฌํ•˜๋Š” TypeScript ์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ ๋ฒˆ๋“ค๋ง์„ ํ†ตํ•ด 1๊ฐœ์˜ JavaScript ํŒŒ์ผ๋กœ ์••์ถ•ํ•œ ํ›„ LLM์—๊ฒŒ ์ „๋‹ฌํ•  ๊ฒƒ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ , LLM์ด ์ฝ”๋“œ๋ฒ ์ด์Šค์™€ ๊ด€๋ จ๋œ ์งˆ๋ฌธ์— ์–ผ๋งˆ๋‚˜ ์ •ํ™•ํ•˜๊ฒŒ ๋‹ต๋ณ€ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•  ๊ฒƒ์ด๋‹ค.

๋ฒˆ๋“ค๋ง์ด๋ž€?

๋ฒˆ๋“ค๋ง ํˆด์„ ์š”์•ฝํ•œ ๊ทธ๋ฆผ

TypeScript/JavaScript ์–ธ์–ด ์ง„์˜์—์„œ์˜ โ€œ๋ฒˆ๋“ค๋งโ€(bundle, bundling)์€ ์ฝ”๋“œ๋ฒ ์ด์Šค ์•ˆ์— ์ž‘์„ฑ๋˜์–ด ์žˆ๋Š” ์—ฌ๋Ÿฌ ์†Œ์Šค ํŒŒ์ผ๋“ค์„ ๋‹จ ๋ช‡ ๊ฐœ์˜ ์†Œ์Šค ํŒŒ์ผ๋กœ ์—ฎ๋Š” ํ–‰์œ„๋ฅผ ๋œปํ•œ๋‹ค.

๋ฒˆ๋“ค๋ง์˜ ํŠน์ง•:

  • ์ฃผ์„, Dead Code, ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๋“ฑ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๊ด€๊ณ„์—†๋Š” ๊ฒƒ๋“ค์€ ์ œ์™ธ๋œ ์ฝ”๋“œ๊ฐ€ ๋„์ถœ๋œ๋‹ค. ๊ทธ๋Ÿผ์—๋„ ๋™์ž‘์˜ ๋™์ผ์„ฑ์ด ๋ณด์žฅ๋œ๋‹ค.
  • ๋ฐฉ๋Œ€ํ•œ ์˜์กด์„ฑ ํŒจํ‚ค์ง€๋“ค(์˜ˆ: node_modules)์„ ์ „๋ถ€ ๋ชจ์•„ ํ•œ ํŒŒ์ผ์— ํฌํ•จ์‹œํ‚จ๋‹ค.
  • Syntactic sugar๋ฅผ ์ ๊ทน ํ™œ์šฉํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ์งง๊ฒŒ ์ค„์ธ๋‹ค.

ํ…Œ์ŠคํŠธ ์„ค๊ณ„

๋ฒˆ๋“ค๋ง์„ ์œ„ํ•ด ์‚ฌ์šฉํ•œ ์„ค์ •6์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

const projectRoot = path.resolve(process.cwd(), "..", "..");

const commonBuildOptions = {
  entryPoints: [
    {
      in: `${projectRoot}/apps/server/src/main.ts`,
      out: "index"
    }
  ],
  tsconfig: `${projectRoot}/apps/server/tsconfig.json`,
  format: "esm",
  platform: "node",
  target: "node19",
  outExtension: { ".js": ".mjs" },
  legalComments: "none",
  banner: {
    /** https://github.com/evanw/esbuild/issues/1921#issuecomment-1152991694 */
    js:
      "import{createRequire}from'module';const require=createRequire(import.meta.url);" +
      "import{fileURLToPath}from'node:url';import{dirname as __pathDirname}from'node:path';const __filename=fileURLToPath(import.meta.url);const __dirname=__pathDirname(__filename);"
  },
  charset: "utf8",
  plugins: [
    esbuildDecorators({
      tsconfig: `${projectRoot}/apps/server/tsconfig.json`
    }),
    esbuildProgressPulgin()
  ]
} satisfies esbuild.BuildOptions;

await esbuild.build({
  ...commonBuildOptions,
  outdir: `${projectRoot}/apps/server/dist/bundle-minify-nokeepnames-package-external`,
  bundle: true,
  minify: true,
  keepNames: false
  external: [
    "@fastify/aws-lambda",
    "@fastify/cookie",
    "@fastify/helmet",
    "@nestjs/axios",
    "@nestjs/common",
    "@nestjs/config",
    "@nestjs/core",
    "@nestjs/jwt",
    "@nestjs/passport",
    "@nestjs/platform-fastify",
    "@nestjs/typeorm",
    "aws-lambda",
    "axios",
    "class-transformer",
    "class-validator",
    "fastify",
    "joi",
    "jsonwebtoken",
    "lodash",
    "passport",
    "passport-jwt",
    "pg",
    "reflect-metadata",
    "rxjs",
    "typeorm",
    "uuid"
  ],
} satisfies esbuild.BuildOptions);

์™ธ๋ถ€ ํŒจํ‚ค์ง€๋“ค์€ ๋ฒˆ๋“ค๋˜์ง€ ์•Š๋„๋ก ํ•˜๊ณ , ์†Œ์Šค์ฝ”๋“œ ๋‚ด๋ถ€ ์ฝ”๋“œ๋งŒ ๋ฒˆ๋“ค๋˜๋„๋ก ์„ค์ •ํ•˜์˜€๋‹ค.

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ:

  • ํ…Œ์ŠคํŠธ ๋Œ€์ƒ ์ฝ”๋“œ๋ฒ ์ด์Šค: TypeScript๋กœ ์ž‘์„ฑ๋œ Node.js ๋Ÿฐํƒ€์ž„์šฉ ์›น ์„œ๋ฒ„7
  • ๋ฒˆ๋“ค๋ง ์ˆ˜๋‹จ: esbuild๋กœ minified bundle 1ํšŒ ์ง„ํ–‰
  • LLM: OpenAI o3-mini ๋ชจ๋ธ 8

๋ฒˆ๋“ค๋ง ์ „ (์›๋ณธ ์ฝ”๋“œ๋ฒ ์ด์Šค)

  • ์ฝ”๋“œ ์ค„: ์•ฝ 15,000์—ฌ์ค„์˜ TypeScript
  • ํŒŒ์ผ ๊ฐœ์ˆ˜: ์•ฝ 300์—ฌ๊ฐœ
  • ์†Œ์Šค ์ฝ”๋“œ ํฌ๊ธฐ: ์•ฝ 2MB
  • ์™ธ๋ถ€ ํŒจํ‚ค์ง€๋“ค์„ ํฌํ•จํ•œ ์†Œ์Šค ์ฝ”๋“œ ํฌ๊ธฐ: ์•ฝ 1GB 9

๋ฒˆ๋“ค๋ง ํ›„ (LLM์—๊ฒŒ ์ „๋‹ฌํ•  ๋‚ด์šฉ)

์ฝ”๋“œ๋ฒ ์ด์Šค ํŒŒ์ผ ์ „์ฒด๋ฅผ JavaScript ํŒŒ์ผ 1๊ฐœ๋กœ ์••์ถ•ํ•œ ๊ฒฐ๊ณผ๋ฌผ ์‚ฌ์ง„: ์ฝ”๋“œ๋ฒ ์ด์Šค ํŒŒ์ผ ์ „์ฒด๋ฅผ JavaScript ํŒŒ์ผ 1๊ฐœ๋กœ ์••์ถ•ํ•œ ๊ฒฐ๊ณผ๋ฌผ

  • ์™ผ์ชฝ ์‚ฌ์ง„: ํ† ํฐ ๊ฐœ์ˆ˜ ๊ณ„์‚ฐ ๊ฒฐ๊ณผ
  • ์˜ค๋ฅธ์ชฝ ์‚ฌ์ง„: ํŒŒ์ผ ์ •๋ณด

๋ฒˆ๋“ค๋ง ํ›„ ์ฝ”๋“œ๋ฒ ์ด์Šค๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์••์ถ•๋˜์—ˆ๋‹ค.

  • ๋ฌธ์ž์ˆ˜: 239,143๊ฐœ
  • ํ† ํฐ ๊ฐœ์ˆ˜: ์•ฝ 76,706๊ฐœ
  • ํŒŒ์ผ ํฌ๊ธฐ: 248KB
  • ํŒŒ์ผ ๊ฐœ์ˆ˜: 1๊ฐœ

ํ…Œ์ŠคํŠธ 1: ChatGPT๋ฅผ ํ†ตํ•œ API ๋ฌธ์„œ ์ƒ์„ฑ

์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ ์••์ถ•ํ•œ ํŒŒ์ผ 1๊ฐœ๋งŒ ์žˆ์–ด๋„, ChatGPT๊ฐ€ Swagger API ๋ฌธ์„œ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์„๊นŒ?

์Šค์›จ๊ฑฐ(Swagger)๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ REST ์›น ์„œ๋น„์Šค๋ฅผ ์„ค๊ณ„, ๋นŒ๋“œ, ๋ฌธ์„œํ™”, ์†Œ๋น„ํ•˜๋Š” ์ผ์„ ๋„์™€์ฃผ๋Š” ์˜คํ”ˆ ์†Œ์Šค ์†Œํ”„ํŠธ์›จ์–ด ํ”„๋ ˆ์ž„์›Œํฌ์ด๋‹ค.

ํ…Œ์ŠคํŠธ ์ ˆ์ฐจ

  1. ๋ฒˆ๋“ค ์ƒ์„ฑ
  2. ์••์ถ•๋œ ์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ ์•„๋ž˜ ํ”„๋กฌํ”„ํŠธ ์•ˆ์— ํ…์ŠคํŠธ๋กœ ์ฒจ๋ถ€
  3. ChatGPT์— ์ „๋‹ฌ

ํ”„๋กฌํ”„ํŠธ

Rules:
- The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED",  "MAY", and "OPTIONAL" in this document are to be interpreted as described in IETF RFC 2119 (https://datatracker.ietf.org/doc/html/rfc2119).

Context: 
- [index.mjs] is a bundled JavaScript code of a TypeScript codebase.
- [index.mjs] was generated by esbuild to reduce size and remove unnecessary contents.
- [index.mjs] keeps every business logic that original codebases implements.

Request: 
- Create a OpenAPI Specification 3.0 content based on [filename]. And, response the specification's content only.

Requirements: 
- The result MUST be in JSON format.
- The result MUST include every HTTP API and it's HTTP Method as path information.
- The result SHOULD include security, schema, response information for each API.
- The result MAY include tag information for each API.

`package.json`'s content:
(์ฝ”๋“œ๋ฒ ์ด์Šค์—์„œ ์“ฐ์ธ ์˜์กด์„ฑ ๋ชฉ๋ก)

`index.mjs`'s content:
(๋ฒˆ๋“ค๋œ ์ฝ”๋“œ ๋‚ด์šฉ)

Response:

ํ…Œ์ŠคํŠธ 1 ๊ฒฐ๊ณผ: ChatGPT๋ฅผ ํ†ตํ•œ API ๋ฌธ์„œ ์ƒ์„ฑ

๋Œ€๋ถ€๋ถ„์˜ ์‹œ๋„์—์„œ 21๊ฐœ ์—”๋“œํฌ์ธํŠธ ์ค‘ 19๊ฐœ ์ด์ƒ์˜ ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ๋ฌธ์„œํ™”ํ•˜๋Š” ๋ฐ ์„ฑ๊ณตํ–ˆ๋‹ค. ์—”๋“œํฌ์ธํŠธ๊ฐ€ ํฌํ•จ๋œ API ๋ฌธ์„œ๋Š” YAML ํ˜•์‹ ์˜ค๋ฅ˜ ์—†์ด ์ƒ์„ฑ๋˜์—ˆ์œผ๋ฉฐ, URL, Method ์ •๋ณด๊ฐ€ ์ •ํ™•ํ•˜๊ฒŒ ๊ธฐ์žฌ๋˜์—ˆ๋‹ค.

์‚ฌ์ง„: ChatGPT๊ฐ€ ์ƒ์„ฑํ•œ Swagger API ๋ฌธ์„œ๋ฅผ https://editor.swagger.io์—์„œ ํ™•์ธํ•œ ๋ชจ์Šต

๋Œ€์กฐ๊ตฐ๊ณผ์˜ ๋น„๊ต

ChatGPT๋ฅผ ํ†ตํ•ด ์ƒ์„ฑํ•œ API ๋ฌธ์„œ๊ฐ€ ์–ผ๋งˆ๋‚˜ ์‚ฌ์‹ค์— ๊ฐ€๊นŒ์šด์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด, ์›น ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ž์ฒด์ ์œผ๋กœ ์ƒ์„ฑํ•œ Swagger ๋‚ด์šฉ๊ณผ ๋น„๊ตํ•˜์˜€๋‹ค.

๋Œ€์กฐ๊ตฐ

์›น ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ž์ฒด์ ์œผ๋กœ ์ƒ์„ฑํ•œ Swagger ํŒŒ์ผ ํ™•์ธํ•˜๊ธฐ

์›น ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ž์ฒด์ ์œผ๋กœ ์ƒ์„ฑํ•œ Swagger ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ (yaml)

openapi: 3.0.0
paths:
  /:
    get:
      operationId: AppController_root
      parameters: []
      responses:
        '200':
          description: ''
      tags:
        - App
  /ads.txt:
    get:
      operationId: AppController_adsTxt
      parameters: []
      responses:
        '200':
          description: ''
      tags:
        - App
  /auth/access-token:
    get:
      operationId: AccessTokenController_getAccessToken
      parameters: []
      responses:
        '200':
          description: ''
      tags:
        - AccessToken
  /auth/logout:
    post:
      operationId: LocalJwtController_logout
      parameters: []
      responses:
        '201':
          description: ''
      tags:
        - LocalJwt
    get:
      operationId: LocalJwtController_logoutGet
      parameters: []
      responses:
        '200':
          description: ''
      tags:
        - LocalJwt
  /auth/google-oauth/{web}:
    get:
      operationId: GoogleOAuthController_authUserWithGoogleOauthCode
      parameters:
        - name: code
          required: true
          in: query
          schema:
            type: string
        - name: web
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
      tags:
        - GoogleOAuth
  /temp-signup:
    post:
      operationId: TempSignupController_temporarySignUpUser
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TemporarySignUpDto'
      responses:
        '201':
          description: ''
      tags:
        - TempSignup
  /frontend-error:
    post:
      operationId: FrontendErrorController_insert
      parameters: []
      responses:
        '201':
          description: ''
      tags:
        - FrontendError
  /land-comments:
    post:
      operationId: UserLandCommentController_registerComment
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Function'
      responses:
        '201':
          description: ''
      tags:
        - UserLandComment
  /land-events:
    get:
      operationId: LandEventController_getLandEvents
      parameters: []
      responses:
        '200':
          description: ''
      tags:
        - LandEvent
  /profile/me:
    get:
      operationId: PublicProfileController_getProfile
      parameters: []
      responses:
        '200':
          description: ''
      tags:
        - PublicProfile
    patch:
      operationId: PublicProfileController_updateUserById
      parameters: []
      responses:
        '200':
          description: ''
      tags:
        - PublicProfile
  /profile/others:
    get:
      operationId: PublicProfileController_getOthersProfiles
      parameters: []
      responses:
        '200':
          description: ''
      tags:
        - PublicProfile
  /profile/complete-signup:
    patch:
      operationId: PublicProfileController_completeSignup
      parameters: []
      responses:
        '200':
          description: ''
      tags:
        - PublicProfile
  /profile:
    delete:
      operationId: PublicProfileController_terminateUser
      parameters: []
      responses:
        '200':
          description: ''
      tags:
        - PublicProfile
  /user-interaction-web:
    post:
      operationId: UserInteractionWebController_makeUserInteractionAndGetWebMessageResponse
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserInteractionDto'
      responses:
        '201':
          description: ''
      tags:
        - UserInteractionWeb
  /dice-toss:
    post:
      operationId: DiceTossController_tossDiceAndGetWebMessageResponse
      parameters: []
      responses:
        '201':
          description: ''
      tags:
        - DiceToss
  /recent-skill-logs:
    get:
      operationId: RecentSkillLogsController_getRecentSkillLogsWeb
      parameters:
        - name: limit
          required: true
          in: query
          schema:
            type: number
      responses:
        '200':
          description: ''
      tags:
        - RecentSkillLogs
  /scenarios/d1/map:
    get:
      operationId: D1Controller_getAliases
      parameters: []
      responses:
        '200':
          description: ''
      tags:
        - D1
  /health/detail:
    get:
      operationId: HealthController_readiness
      parameters: []
      responses:
        '200':
          description: ''
      tags:
        - Health
  /health:
    get:
      operationId: HealthController_simple
      parameters: []
      responses:
        '200':
          description: ''
      tags:
        - Health
info:
  title: Cats example
  description: The cats API description
  version: '1.0'
  contact: {}
tags:
  - name: cats
    description: ''
servers: []
components:
  schemas:
    TemporarySignUpDto:
      type: object
      properties: {}
    Function:
      type: object
      properties: {}
    UserInteractionDto:
      type: object
      properties: {}

o3-mini Generation

ChatGPT๊ฐ€ ์ƒ์„ฑํ•œ Swagger ํŒŒ์ผ ํ™•์ธํ•˜๊ธฐ

ChatGPT๊ฐ€ ์ƒ์„ฑํ•œ Swagger ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ (yaml)

openapi: "3.0.0"
info:
  title: "Server API"
  version: "1.0.0"
servers:
  - url: "https://example.com"
    description: "Primary server (update with actual URL)"
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
    cookieAuth:
      type: apiKey
      in: cookie
      name: refreshToken
  schemas:
    ContactResponse:
      type: string
      example: "contact: lifegame2021team@gmail.com"
    AdsTxtResponse:
      type: string
      example: "ADS TXT content from configuration"
    TokenResponse:
      type: object
      properties:
        accessToken:
          type: string
          example: "eyJhbGciOiJIUzI1NiIsInR..."
    GenericResponse:
      type: object
      additionalProperties: true
    ErrorResponse:
      type: object
      properties:
        message:
          type: string
paths:
  /:
    get:
      tags:
        - Root
      summary: "Get root contact info"
      responses:
        "200":
          description: "Contact information"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactResponse"

  /ads.txt:
    get:
      tags:
        - Root
      summary: "Get ads.txt content"
      responses:
        "200":
          description: "ADS TXT content"
          content:
            text/plain:
              schema:
                $ref: "#/components/schemas/AdsTxtResponse"

  /auth/access-token:
    get:
      tags:
        - Auth
      summary: "Obtain a new access token using a valid refresh token"
      security:
        - cookieAuth: []
      responses:
        "200":
          description: "Access token generated successfully"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TokenResponse"
        "401":
          description: "Unauthorized โ€“ invalid or missing credentials"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /auth/logout:
    post:
      tags:
        - Auth
      summary: "Logout user (POST)"
      security:
        - cookieAuth: []
      responses:
        "200":
          description: "User successfully logged out"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GenericResponse"
    get:
      tags:
        - Auth
      summary: "Logout user (GET)"
      security:
        - cookieAuth: []
      responses:
        "200":
          description: "User successfully logged out"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GenericResponse"

  /auth/google-oauth/{web}:
    get:
      tags:
        - Auth
      summary: "Authenticate user with Google OAuth"
      parameters:
        - in: path
          name: web
          required: true
          schema:
            type: string
          description: "A dynamic segment representing the web redirect path"
        - in: query
          name: code
          required: true
          schema:
            type: string
          description: "The OAuth authorization code returned by Google"
      responses:
        "200":
          description: "Google OAuth callback response"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GenericResponse"
        "400":
          description: "Bad Request"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /temp-signup:
    post:
      tags:
        - Temp Signup
      summary: "Temporarily sign up a new user with hCaptcha verification"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                hCaptchaSuccessToken:
                  type: string
                username:
                  type: string
                countryCode3:
                  type: string
              required:
                - hCaptchaSuccessToken
                - username
                - countryCode3
      responses:
        "200":
          description: "Temporary signup successful"
          content:
            application/json:
              schema:
                type: boolean
                example: true
        "400":
          description: "Bad Request โ€“ invalid hCaptcha token or missing fields"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /land-events:
    get:
      tags:
        - Land Events
      summary: "Retrieve a list of recent land events"
      parameters:
        - in: header
          name: timezone
          schema:
            type: string
          required: false
          description: "Timezone identifier (e.g., 'Asia/Seoul')"
      security:
        - bearerAuth: []
      responses:
        "200":
          description: "A list of land event summaries"
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/GenericResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /dice-toss:
    post:
      tags:
        - Dice Toss
      summary: "Toss dice and receive a gaming response message"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                userId:
                  type: string
              required:
                - userId
      security:
        - bearerAuth: []
      responses:
        "200":
          description: "Dice toss result with game data"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GenericResponse"
        "400":
          description: "Bad Request โ€“ invalid input"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /profile/me:
    get:
      tags:
        - Profile
      summary: "Retrieve the authenticated user's profile information"
      security:
        - bearerAuth: []
      responses:
        "200":
          description: "User profile data"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GenericResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    patch:
      tags:
        - Profile
      summary: "Update the authenticated user's profile"
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                username:
                  type: string
                countryCode3:
                  type: string
              required:
                - username
                - countryCode3
      responses:
        "200":
          description: "Profile updated successfully"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GenericResponse"
        "400":
          description: "Bad Request โ€“ validation errors"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /profile/others:
    get:
      tags:
        - Profile
      summary: "Retrieve profiles of other users"
      security:
        - bearerAuth: []
      parameters:
        - in: query
          name: limit
          required: false
          schema:
            type: integer
          description: "Maximum number of profiles to return"
        - in: query
          name: page
          required: false
          schema:
            type: integer
          description: "Page number for pagination"
        - in: query
          name: updatedAfter
          required: false
          schema:
            type: string
            format: date-time
          description: "Filter profiles updated after this datetime"
      responses:
        "200":
          description: "List of other user profiles"
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/GenericResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /profile/complete-signup:
    post:
      tags:
        - Profile
      summary: "Complete the authenticated user's signup process"
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                countryCode3:
                  type: string
                username:
                  type: string
              required:
                - countryCode3
                - username
      responses:
        "200":
          description: "Signup completed successfully"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GenericResponse"
        "400":
          description: "Bad Request"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /profile:
    delete:
      tags:
        - Profile
      summary: "Terminate (delete) the authenticated user's account"
      security:
        - bearerAuth: []
      responses:
        "200":
          description: "User account terminated successfully"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GenericResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /recent-skill-logs:
    get:
      tags:
        - Skill Logs
      summary: "Retrieve recent skill logs for the authenticated user"
      security:
        - bearerAuth: []
      parameters:
        - in: query
          name: limit
          required: false
          schema:
            type: integer
          description: "The maximum number of logs to retrieve"
      responses:
        "200":
          description: "A list of recent skill logs"
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/GenericResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /land-comments:
    post:
      tags:
        - Land Comments
      summary: "Register a comment on a land (game event)"
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                userId:
                  type: string
                comment:
                  type: string
              required:
                - userId
                - comment
      responses:
        "200":
          description: "Comment registered successfully"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GenericResponse"
        "400":
          description: "Bad Request"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /scenarios/d1/map:
    get:
      tags:
        - Scenarios
      summary: "Retrieve skill group aliases for scenario D1"
      security:
        - bearerAuth: []
      responses:
        "200":
          description: "A mapping of skill groups and their aliases"
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true

  /user-interaction-web:
    post:
      tags:
        - User Interaction
      summary: "Perform a user interaction call to trigger a skill"
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                callingSkillRoute:
                  type: string
                callingSkillParam:
                  type: object
              required:
                - callingSkillRoute
      responses:
        "200":
          description: "Response from the user interaction call"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GenericResponse"
        "400":
          description: "Bad Request"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /health/detail:
    get:
      tags:
        - Health
      summary: "Detailed health check"
      responses:
        "200":
          description: "Detailed health status"
          content:
            application/json:
              schema:
                type: string
                example: "OK"

  /health:
    get:
      tags:
        - Health
      summary: "Simple health check"
      responses:
        "200":
          description: "Simple health status"
          content:
            application/json:
              schema:
                type: string
                example: "m"
security:
  - bearerAuth: []

ํ…Œ์ŠคํŠธ 2: ChatGPT๋ฅผ ํ†ตํ•œ ๋งค๋‰ด์–ผ ์ƒ์„ฑ

์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ ์••์ถ•ํ•œ ํŒŒ์ผ 1๊ฐœ๋งŒ ์žˆ์–ด๋„, ChatGPT๊ฐ€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ดํ•ดํ•˜๊ณ  โ€˜๋งค๋‰ด์–ผโ€™์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์„๊นŒ?

ํ…Œ์ŠคํŠธ ์ ˆ์ฐจ

  1. ํŽธ์˜๋ฅผ ์œ„ํ•ด ํ…Œ์ŠคํŠธ 1์—์„œ ์˜ค๊ฐ„ ๋Œ€ํ™”๋ฅผ ๊ทธ๋Œ€๋กœ ์œ ์ง€
  2. ํ”„๋กฌํ”„ํŠธ ์ „๋‹ฌ

ํ”„๋กฌํ”„ํŠธ

Generate manual for 'POST /temp-signup' endpoint.

[for non-coder]
1. ELI5 for teammates who cannot code
2. runtime behavior
3. 'how dose it works' explanation - should include technical details
4. business logics
5. possible edge cases

[for coder]
1. ELI5 for teammates who cannot code
2. runtime behavior
3. 'how dose it works' explanation - should include technical details
3.1. technical detail for framework and libraries
3.2. sequence diagram if possible
4. possible design pattern it implements
5. possible improvements
6. possible edge cases

tone: no emoji, professional tone, literacy style

each in markdown format
one in english, one in korean.

ํ…Œ์ŠคํŠธ 2 ๊ฒฐ๊ณผ: ChatGPT๋ฅผ ํ†ตํ•œ ๋งค๋‰ด์–ผ ์ƒ์„ฑ

๋น„๊ฐœ๋ฐœ์ž/๊ฐœ๋ฐœ์ž์šฉ ๋งค๋‰ด์–ผ์„ ๋™์‹œ์— ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

์‚ฌ์ง„: ChatGPT๊ฐ€ ์ƒ์„ฑํ•ด๋‚ธ ์‹œํ€€์Šค ๋‹ค์ด์–ด๊ทธ๋žจ์„ ํ†ตํ•œ ์‹œ์Šคํ…œ ์„ค๋ช…

"๋น„๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ๋งค๋‰ด์–ผ" ์ƒ์„ฑ๋ฌผ ํ™•์ธํ•˜๊ธฐ

POST /temp-signup ์—”๋“œํฌ์ธํŠธ ๋งค๋‰ด์–ผ (Korean)

๋น„๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ์„ค๋ช…

1. ELI5 ์„ค๋ช… (์ฝ”๋“œ๋ฅผ ๋ชจ๋ฅด๋Š” ํŒ€์›์„ ์œ„ํ•œ ์„ค๋ช…)

์ด ์—”๋“œํฌ์ธํŠธ๋Š” ์ž„์‹œ ์‚ฌ์šฉ์ž ๊ณ„์ •์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. hCaptcha๋ผ๋Š” ์‚ฌ๋žŒ์ž„์„ ํ™•์ธํ•˜๋Š” ์ ˆ์ฐจ(๋น„๋ฐ€ ์ฝ”๋“œ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ๋ฐฉ์‹)๋ฅผ ๊ฑฐ์ณ์„œ, ์ด๋ฆ„, ์ด๋ฉ”์ผ, ๊ตญ๊ฐ€ ์ฝ”๋“œ ๋“ฑ์˜ ์ •๋ณด๋ฅผ ์ œ์ถœํ•˜๋ฉด ์ž„์‹œ๋กœ ๊ณ„์ •์ด ๋งŒ๋“ค์–ด์ง€๊ณ , ๊ทธ ๊ณ„์ •์œผ๋กœ ์„œ๋น„์Šค๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

2. ๋Ÿฐํƒ€์ž„ ๋™์ž‘

์‚ฌ์šฉ์ž๊ฐ€ ํšŒ์›๊ฐ€์ž… ์ •๋ณด๋ฅผ ์ „์†กํ•˜๋ฉด ์„œ๋ฒ„๋Š” ๋จผ์ € hCaptcha ํ† ํฐ์„ ๊ฒ€์‚ฌํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๋ด‡์ด ์•„๋‹ˆ๋ผ๋Š” ๊ฒƒ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ํ›„, ์ž„์‹œ ์‚ฌ์šฉ์ž ๊ณ„์ •์„ ์ƒ์„ฑํ•˜๊ณ , ์‚ฌ์šฉ์ž ๋ธŒ๋ผ์šฐ์ €์— ๋ณด์•ˆ ์ฟ ํ‚ค(๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ)๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ, ํšŒ์›๊ฐ€์ž…์ด ์„ฑ๊ณตํ–ˆ์Œ์„ true/false ๊ฐ’์œผ๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

3. ๋™์ž‘ ๋ฐฉ์‹ (๋†’์€ ์ˆ˜์ค€์˜ ์„ค๋ช…)

์ž…๋ ฅ ๊ฒ€์ฆ: hCaptcha ํ† ํฐ, ์‚ฌ์šฉ์ž ์ด๋ฆ„, ์ด๋ฉ”์ผ, ๊ตญ๊ฐ€ ์ฝ”๋“œ ๋“ฑ ํ•„์ˆ˜ ํ•„๋“œ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

์ธ๊ฐ„ ์—ฌ๋ถ€ ํ™•์ธ: hCaptcha ํ† ํฐ์„ ์™ธ๋ถ€ hCaptcha ์„œ๋น„์Šค์— ์ „์†กํ•˜์—ฌ ์‚ฌ๋žŒ์ด ๋งž๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

๊ณ„์ • ์ƒ์„ฑ: ๊ฒ€์ฆ์ด ์„ฑ๊ณตํ•˜๋ฉด, ์ž„์‹œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค(๋˜๋Š” ๋‚ด๋ถ€ ์ €์žฅ์†Œ)์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

์ฟ ํ‚ค ์„ค์ •: ์ƒ์„ฑ๋œ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์„ HTTP ์ „์šฉ ์ฟ ํ‚ค๋กœ ์„ค์ •ํ•˜์—ฌ, ์ดํ›„ ์‚ฌ์šฉ์ž๊ฐ€ ๊ณ„์† ์ธ์‹๋  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

์‘๋‹ต: ํšŒ์›๊ฐ€์ž… ์„ฑ๊ณต ์—ฌ๋ถ€๋ฅผ ์‘๋‹ตํ•ฉ๋‹ˆ๋‹ค.

4. ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง

์‚ฌ์šฉ์ž ๊ฒ€์ฆ: hCaptcha๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ŠคํŒธ๊ณผ ์ž๋™ ๊ฐ€์ž…์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

์ž„์‹œ ๊ณ„์ • ์ƒ์„ฑ: ์‚ฌ์šฉ์ž๊ฐ€ ์ฆ‰์‹œ ์„œ๋น„์Šค๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ž„์‹œ ๊ณ„์ •์„ ๋งŒ๋“ค๋˜, ์ดํ›„์— ์ •์‹ ๊ฐ€์ž… ์ ˆ์ฐจ๊ฐ€ ์ถ”๊ฐ€๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ณด์•ˆ ๊ด€๋ฆฌ: ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์„ ๋ณด์•ˆ ์ฟ ํ‚ค๋กœ ๋ฐœ๊ธ‰ํ•˜์—ฌ ์„ธ์…˜ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•ฉ๋‹ˆ๋‹ค.

5. ๊ฐ€๋Šฅํ•œ ์—ฃ์ง€ ์ผ€์ด์Šค

์œ ํšจํ•˜์ง€ ์•Š์€ hCaptcha ํ† ํฐ: hCaptcha ํ† ํฐ์ด ์—†๊ฑฐ๋‚˜ ์ž˜๋ชป๋œ ๊ฒฝ์šฐ ํšŒ์›๊ฐ€์ž…์ด ๊ฑฐ๋ถ€๋ฉ๋‹ˆ๋‹ค.

๋ถˆ์™„์ „ํ•œ ๋ฐ์ดํ„ฐ: ์‚ฌ์šฉ์ž ์ด๋ฆ„, ์ด๋ฉ”์ผ, ๊ตญ๊ฐ€ ์ฝ”๋“œ ์ค‘ ํ•˜๋‚˜๋ผ๋„ ๋น ์ง„ ๊ฒฝ์šฐ ์‹คํŒจํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์„œ๋ฒ„ ์˜ค๋ฅ˜: ์ผ์‹œ์ ์ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋˜๋Š” ๋„คํŠธ์›Œํฌ ๋ฌธ์ œ๋กœ ์ธํ•ด ์˜ˆ๊ธฐ์น˜ ๋ชปํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ค‘๋ณต ๊ฐ€์ž…: ์ด๋ฉ”์ผ์ด๋‚˜ ์‚ฌ์šฉ์ž ์ด๋ฆ„์ด ์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ๊ฒฝ์šฐ ์ถ”๊ฐ€ ๊ฒ€์ฆ์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

"๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ๋งค๋‰ด์–ผ" ์ƒ์„ฑ๋ฌผ ํ™•์ธํ•˜๊ธฐ

POST /temp-signup ์—”๋“œํฌ์ธํŠธ ๋งค๋‰ด์–ผ (Korean)

๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ์„ค๋ช…

1. ELI5 ์„ค๋ช… (์ฝ”๋“œ๋ฅผ ๋ชจ๋ฅด๋Š” ํŒ€์›์„ ์œ„ํ•œ ์„ค๋ช…)

ํšŒ์›๊ฐ€์ž… ๊ณผ์ •์„ ํ‹ฐ์ผ“ ๋ถ€์Šค์— ๋น„์œ ํ•œ๋‹ค๋ฉด, ์‚ฌ์šฉ์ž๋Š” hCaptcha๋ผ๋Š” ๋น„๋ฐ€ ์ฝ”๋“œ๋ฅผ ์ œ์‹œํ•˜์—ฌ ๋กœ๋ด‡์ด ์•„๋‹˜์„ ์ฆ๋ช…ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๋ถ€์Šค์—์„œ๋Š” ์ž„์‹œ ํšŒ์› ์นด๋“œ๋ฅผ ๋ฐœ๊ธ‰ํ•˜๊ณ , ๋‚˜์ค‘์— ํด๋Ÿฝ์— ๋‹ค์‹œ ๋ฐฉ๋ฌธํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ธŒ๋ ˆ์ด์Šฌ๋ฆฟ(์ฟ ํ‚ค)์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

2. ๋Ÿฐํƒ€์ž„ ๋™์ž‘

POST ์š”์ฒญ์ด /temp-signup ๊ฒฝ๋กœ๋กœ ์˜ค๋ฉด, ์„œ๋ฒ„๋Š” JSON ํ˜•์‹์˜ ํŽ˜์ด๋กœ๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ž…๋ ฅ๊ฐ’์ด ๊ฒ€์ฆ๋˜๊ณ , ์™ธ๋ถ€ hCaptcha ์„œ๋น„์Šค์—์„œ hCaptcha ํ† ํฐ์„ ํ™•์ธํ•œ ํ›„, ์ž„์‹œ ์‚ฌ์šฉ์ž ๋ ˆ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„, ๋ณด์•ˆ HTTP ์ „์šฉ ์ฟ ํ‚ค์— ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์„ ์„ค์ •ํ•˜๊ณ , ์ตœ์ข…์ ์œผ๋กœ ๊ฐ€์ž… ์„ฑ๊ณต ์—ฌ๋ถ€๋ฅผ Boolean ๊ฐ’์œผ๋กœ ์‘๋‹ตํ•ฉ๋‹ˆ๋‹ค.

3. ๋™์ž‘ ๋ฐฉ์‹ โ€“ ๊ธฐ์ˆ ์  ์„ธ๋ถ€์‚ฌํ•ญ

3.1. ํ”„๋ ˆ์ž„์›Œํฌ ๋ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ด€๋ จ ๊ธฐ์ˆ  ์„ธ๋ถ€์‚ฌํ•ญ

NestJS ํ”„๋ ˆ์ž„์›Œํฌ: ์ด ์—”๋“œํฌ์ธํŠธ๋Š” NestJS ์ปจํŠธ๋กค๋Ÿฌ๋กœ ๊ตฌํ˜„๋˜์–ด ์žˆ์œผ๋ฉฐ, @Controller(), @Post() ๋“ฑ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์™€ ์˜์กด์„ฑ ์ฃผ์ž…(@Injectable())์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

hCaptcha ์—ฐ๋™: @nestjs/axios์˜ HttpService๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ "https://hcaptcha.com/siteverify" URL์— POST ์š”์ฒญ์„ ๋ณด๋‚ด hCaptcha ํ† ํฐ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•ฉ๋‹ˆ๋‹ค. ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ๊ด€๋ฆฌ๋˜๋Š” hCaptcha ๋น„๋ฐ€ ํ‚ค(HCAPTCHA_SECRET_KEY)๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๋™: TypeORM(@nestjs/typeorm)์„ ํ†ตํ•ด ์ž„์‹œ ์‚ฌ์šฉ์ž ๋ ˆ์ฝ”๋“œ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

์ฟ ํ‚ค ๊ด€๋ฆฌ: @fastify/cookie ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณด์•ˆ ์ฟ ํ‚ค๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ฟ ํ‚ค๋Š” HTTP ์ „์šฉ, ๋ณด์•ˆ, ์„œ๋ช…๋œ ์ฟ ํ‚ค๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ ๊ฒ€์ฆ: NestJS์˜ Validation Pipe ๋ฐ ์„ ํƒ์  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(class-validator ํ˜น์€ Joi)๋ฅผ ์‚ฌ์šฉํ•ด ์ž…๋ ฅ ๋ฐ์ดํ„ฐ์˜ ์œ ํšจ์„ฑ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

3.2. Sequence Diagram (ํ…์ŠคํŠธ ๊ธฐ๋ฐ˜)

sequenceDiagram
  participant Client
  participant Controller as POST /temp-signup
  participant hCaptchaService
  participant UserService
  participant RefreshTokenService
  Client->>Controller: JSON payload {hCaptchaSuccessToken, username, email, countryCode3}
  Controller->>hCaptchaService: Validate hCaptcha token
  hCaptchaService-->>Controller: Return true/false
  alt hCaptcha valid
    Controller->>UserService: Create temporary user record
    UserService-->>Controller: Return new user object
    Controller->>RefreshTokenService: Generate refresh token
    RefreshTokenService-->>Controller: Return token
    Controller->>Client: Set HTTP-only cookie and return true
  else
    Controller->>Client: Return error response (signup failed)
  end

4. Possible Design Patterns

์„œ๋น„์Šค ์ง€ํ–ฅ ์•„ํ‚คํ…์ฒ˜: ์ธ์ฆ, ์‚ฌ์šฉ์ž ๊ด€๋ฆฌ, ํ† ํฐ ๊ด€๋ฆฌ ๋“ฑ ๊ฐ๊ฐ์˜ ์ฑ…์ž„์„ ๊ฐœ๋ณ„ ์„œ๋น„์Šค๋กœ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

์˜์กด์„ฑ ์ฃผ์ž…: NestJS์˜ ์ฃผ์š” ํŠน์ง•์„ ํ™œ์šฉํ•ด ๊ฐ ์„œ๋น„์Šค๊ฐ€ ๋‹ค๋ฅธ ์„œ๋น„์Šค์— ์˜์กดํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

ํŒŒ์‚ฌ๋“œ ํŒจํ„ด: ์ปจํŠธ๋กค๋Ÿฌ๋Š” ์—ฌ๋Ÿฌ ๋‚ด๋ถ€ ์„œ๋น„์Šค(hCaptcha, User, Token ์„œ๋น„์Šค)๋ฅผ ๋‹จ์ˆœํ™”ํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ์— ์ œ๊ณตํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

5. Possible Improvements

๊ณ ๋„ํ™”๋œ ๊ฒ€์ฆ: class-validator ๋˜๋Š” Joi์™€ ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด ๋” ์ƒ์„ธํ•œ ์š”์ฒญ ์Šคํ‚ค๋งˆ ๊ฒ€์ฆ์„ ์ˆ˜ํ–‰.

์—๋Ÿฌ ํ•ธ๋“ค๋ง ๊ฐœ์„ : ํด๋ผ์ด์–ธํŠธ์— ์ข€ ๋” ๊ตฌ์ฒด์ ์ธ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋„๋ก ์—๋Ÿฌ ํ•ธ๋“ค๋ง ๊ตฌ์กฐ๋ฅผ ๊ฐœ์„ .

์†๋„ ์ œํ•œ: ์—”๋“œํฌ์ธํŠธ ๋‚จ์šฉ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์†๋„ ์ œํ•œ(Rate Limiting) ๊ธฐ๋Šฅ ๋„์ž….

๋กœ๊น… ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง: ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ํ†ตํ•œ ์ƒ์„ธ ๋กœ๊น…์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์šด์˜ ๋ฐ ๊ฐ์‚ฌ์— ๋Œ€๋น„.

์ค‘๋ณต ์ฒดํฌ: ๊ธฐ์กด ์‚ฌ์šฉ์ž์™€์˜ ์ค‘๋ณต ๊ฐ€์ž… ์—ฌ๋ถ€๋ฅผ ๋ฏธ๋ฆฌ ํ™•์ธํ•˜์—ฌ ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ ๋ฐฉ์ง€.

6. Possible Edge Cases

์œ ํšจํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ๋งŒ๋ฃŒ๋œ hCaptcha ํ† ํฐ: ์™ธ๋ถ€ hCaptcha ์„œ๋น„์Šค์—์„œ ์œ ํšจ์„ฑ ๊ฒ€์ฆ์— ์‹คํŒจํ•œ ๊ฒฝ์šฐ.

ํ•„์ˆ˜ ์ž…๋ ฅ ๋ˆ„๋ฝ: ์‚ฌ์šฉ์ž ์ด๋ฆ„, ์ด๋ฉ”์ผ ๋˜๋Š” ๊ตญ๊ฐ€ ์ฝ”๋“œ์˜ ๋ˆ„๋ฝ.

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์žฅ์• : ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์žฅ์•  ์‹œ ์ž„์‹œ ๊ณ„์ • ์ƒ์„ฑ ์‹คํŒจ.

์ฟ ํ‚ค ๊ตฌ์„ฑ ์˜ค๋ฅ˜: ์ฟ ํ‚ค ์„ค์ • ์˜ต์…˜(HTTP ์ „์šฉ, ๋ณด์•ˆ ๋“ฑ)์ด ์ œ๋Œ€๋กœ ์„ค์ •๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ๋กœ ์ธํ•œ ๋ณด์•ˆ ์ทจ์•ฝ์„ฑ.

์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋ฏธํก: ์™ธ๋ถ€ hCaptcha API ์‘๋‹ต์—์„œ ๋น„ํ‘œ์ค€ ์‘๋‹ต์ด ์˜ฌ ๋•Œ ์ฒ˜๋ฆฌํ•  ๋กœ์ง์˜ ๋ถ€์žฌ.

์ด ํ…Œ์ŠคํŠธ์—์„œ ๋‚˜๋ˆˆ ๋Œ€ํ™”์˜ ์ „๋ฌธ์„ ์•„์นด์ด๋ธŒ์‹œ์ผœ ๋‘์—ˆ๋‹ค. ChatGPT ๋Œ€ํ™” ๊ธฐ๋ก์—์„œ ํ™•์ธํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.

ํ•œ๊ณ„์ 

ChatGPT์™€์˜ ํ™œ์šฉ์— ๊ด€ํ•œ ํ•œ๊ณ„์ 

์‚ฌ์ง„: GPT-4.5 ๋ชจ๋ธ์ด ์‘๋‹ต์„ ๊ฑฐ๋ถ€ํ•˜๋Š” ๋ชจ์Šต.

๋ฒˆ๋“ค๋œ JavaScript ํŒŒ์ผ์„ ChatGPT ๋Œ€ํ™”์— ์ฒจ๋ถ€ํ•  ๋•Œ, โ€œChatGPT ํŒŒ์ผ ์—…๋กœ๋“œโ€๋ฅผ ํ†ตํ•ด ์—…๋กœ๋“œํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค, ํ”„๋กฌํ”„ํŠธ ์•ˆ์— ํŒŒ์ผ ๋‚ด์šฉ์„ ์ง์ ‘ ํฌํ•จ์‹œํ‚ค๋Š” ๊ฒƒ์ด ๋‹ต๋ณ€์˜ ์ •ํ™•๋„๋ฅผ ๊ฑฐ์˜ ์™„๋ฒฝํ•œ ์ˆ˜์ค€์œผ๋กœ ๋†’์ธ๋‹ค๋Š” ์ ์„ ๋ฐœ๊ฒฌํ–ˆ๋‹ค.

๊ธˆ๋ฒˆ ํ…Œ์ŠคํŠธ์—์„œ๋Š” o3-mini ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์˜€๋‹ค. ํ•œํŽธ, ๋‹ค๋ฅธ ๋ชจ๋ธ์„ ํ†ตํ•œ ๋‹ต๋ณ€ ์ƒ์„ฑ์„ ์‹œ๋„ํ–ˆ์„ ๋•Œ์—๋Š” โ€˜๋ฉ”์‹œ์ง€ ๊ธธ์ด ์ œํ•œโ€™์— ๊ฑธ๋ ค ์‘๋‹ต์„ ๋ฐ›์•„๋‚ผ ์ˆ˜ ์—†์—ˆ๋‹ค.

๋ฒˆ๋“ค๋ง์— ๊ด€ํ•œ ํ•œ๊ณ„์ 

์‚ฌ์ง„: ์†Œ์Šค ์ฝ”๋“œ ํŒŒ์ผ ์œ„์น˜๋ฅผ ๋ฌป๋Š” ์งˆ๋ฌธ์— ์‘๋‹ต์„ ๊ฑฐ๋ถ€ํ•˜๋Š” ๋ชจ์Šต.

๋ฒˆ๋“ค๋ง์„ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š”, ๋ฒˆ๋“ค๋Ÿฌ ๋„๊ตฌ ์‚ฌ์šฉ๋ฒ•์„ ๊ณต๋ถ€ํ•ด์•ผ ํ•  ๊ฒƒ์ด๋‹ค.

์ฝ”๋“œ๋ฒ ์ด์Šค์— ๋Œ€ํ•œ ๋ฒˆ๋“ค๋ง์„ ์‹œ๋„ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ฒˆ๋“ค๋ง์— ์‹คํŒจํ•  ๋งŒํ•œ ๋ฌธ๋ฒ•์  ์˜ค๋ฅ˜๊ฐ€ ์†Œ์Šค์ฝ”๋“œ์— ์กด์žฌํ•ด์„œ๋Š” ์•ˆ ๋œ๋‹ค.

๋ฒˆ๋“ค๋ง ์ค‘ ์†์‹ค๋˜๋Š” ์ •๋ณด๋Š” LLM์— ์ „๋‹ฌ๋˜์ง€ ์•Š๋Š”๋‹ค. ๋ณ„๋„์˜ RAG๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

๋ฐœ๊ฒฌ

์ „ํ†ต์ ์ธ ๋„๊ตฌ๋ฅผ ์จ์„œ RAG๋ฅผ ์„ฑ์ทจํ•œ๋‹ค๋Š” ์ 

์ฝ”๋“œ๋ฒ ์ด์Šค๊ฐ€ context window์— ํฌํ•จ๋  ์ˆ˜ ์žˆ๊ฒŒ๋” โ€˜์†์‹ค ์••์ถ•โ€™ํ•˜์—ฌ๋„ ๋™์ž‘๊ณผ ๊ด€๋ จ๋œ ์ง€์‹์„ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Œ์„ ํ™•์ธํ•˜์˜€๋‹ค. ๋˜ํ•œ, ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ๋ฒˆ๋“ค๋ง ๋„๊ตฌ๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜์˜€์Œ์— ์˜๋ฏธ๊ฐ€ ์žˆ๋‹ค.

๋‹ค๋ฅธ ์–ธ์–ด๋กœ ์ž‘์„ฑ๋œ ์ฝ”๋“œ๋ฒ ์ด์Šค์— ๋Œ€ํ•œ ์ผ๋ฐ˜ํ™” ๊ฐ€๋Šฅ์„ฑ

TypeScript/JavaScript ์ฝ”๋“œ ์ง„์˜์—์„œ๋Š” ์˜ค๋žซ๋™์•ˆ ๋ฒˆ๋“ค๋ง์— ๋Œ€ํ•œ ์—ฐ๊ตฌ๊ฐ€ ์ด๋ฃจ์–ด์ง„ ์ƒํ™ฉ์ด๋ผ, ๋ฒˆ๋“ค๋ง ์‹œ์Šคํ…œ์„ ๊ตฌ์„ฑํ•˜๋Š” ๋‚œ๋„๋Š” ๋น„๊ต์  ๋‚ฎ๋‹ค.

ํ•˜์ง€๋งŒ ๋‹ค๋ฅธ ์–ธ์–ด๋Š” ์ƒํ™ฉ์ด ๋‹ค๋ฅด๋‹ค. ๋‹จ์ ์ธ ์˜ˆ๋กœ์จ ์ปดํŒŒ์ผ ์–ธ์–ด(C++ ๋“ฑ)๋กœ ์ž‘์„ฑ๋˜๋Š” ์ฝ”๋“œ๋ฒ ์ด์Šค์— ๋Œ€ํ•ด์„œ๋Š” ์ด๋Ÿฌํ•œ ๋ฒˆ๋“ค๋ง์ด ๊ฐ€๋Šฅํ•œ์ง€ ํ™•์ธ์ด ํ•„์š”ํ•  ๊ฒƒ์ด๋‹ค.

๋‹ค๋งŒ, LLM์ด .exe ํŒŒ์ผ์„ ์ดํ•ดํ•˜๊ณ , ํŒŒ์ด์ฌ ํฌํŒ…ํ•˜๋Š” ๋ฐ ์„ฑ๊ณตํ–ˆ๋‹ค๋ฉฐ ์ฃผ์žฅํ•˜๋Š” ์‚ฌ๋ก€๊ฐ€ ์กด์žฌํ•œ๋‹ค10. ๋”ฐ๋ผ์„œ, ์ฝ”๋“œ๋ฒ ์ด์Šค์— ๋Œ€ํ•œ ์ ์ ˆํ•œ ์••์ถ•์ด ์ด๋ฃจ์–ด์ง„๋‹ค๋ฉด LLM์ด ์ดํ•ดํ•˜๋Š” ๋ฐ์—๋Š” ๋ฌธ์ œ๊ฐ€ ์—†๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

Semantic search์™€์˜ ์‹œ๋„ˆ์ง€

semantic search์™€ ๋ฒˆ๋“ค๋ง๋œ ์ฝ”๋“œ๋ฒ ์ด์Šค ์ •๋ณด๋ฅผ ๋™์‹œ์— RAG์— ํ™œ์šฉํ•œ๋‹ค๋ฉด, ์„œ๋กœ ์ƒํ˜ธ๋ณด์™„์ ์œผ๋กœ ์ž‘์šฉํ•˜์—ฌ ์ƒ์Šนํšจ๊ณผ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค.

ํ˜„์žฌ ์ƒ์šฉ ์ฝ”๋”ฉ AI ์—์ด์ „ํŠธ๋“ค์€ ์—ฌ๋Ÿฌ ํŒŒ์ผ๋กœ ๊ตฌ์„ฑ๋œ ์ฝ”๋“œ๋ฒ ์ด์Šค์˜ ์ „์ฒด์ ์ธ ๋งฅ๋ฝ์„ ์˜จ์ „ํžˆ ์ดํ•ดํ•˜์ง€ ๋ชปํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋‹ค๋ฐ˜์‚ฌ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, โ€œ์šด์˜์ž ๋กœ๊ทธ์ธ ๋กœ๊ทธ๊ฐ€ โ€˜ISMS ๋กœ๊ทธ ํ…Œ์ด๋ธ”โ€™์— ๊ธฐ๋ก๋˜๋„๋ก ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์ฃผ์‹ญ์‹œ์˜คโ€๋ผ๋Š” ์š”์ฒญ์ด ์ฃผ์–ด์ง€๋ฉด, AI ์—์ด์ „ํŠธ๋Š” โ€˜์šด์˜์ž ๋กœ๊ทธ์ธโ€™ ๊ธฐ๋Šฅ๊ณผ โ€˜ISMS ๋กœ๊ทธ ํ…Œ์ด๋ธ”โ€™๊ณผ ๊ด€๋ จ๋œ ๋ชจ๋“  ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ํƒ์ƒ‰ํ•˜๋Š” ๊ณผ์ •์—์„œ semantic search(์˜๋ฏธ ๊ธฐ๋ฐ˜ ๊ฒ€์ƒ‰)๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.

Semantic search ๊ธฐ๋ฒ•์€ ์ž…๋ ฅ๋œ ํ‚ค์›Œ๋“œ์™€ ๊ฐ€์žฅ ์œ ์‚ฌํ•œ ์›๋ณธ ๋ฐ์ดํ„ฐ๋ฅผ ์‹๋ณ„ํ•˜๋Š” ๋ฐ ๊ธฐ์—ฌํ•˜๋‚˜, ๊ฒ€์ƒ‰ ๋Œ€์ƒ์— ํฌํ•จ๋˜์ง€ ์•Š์€ ํŒŒ์ผ์€ ์—์ด์ „ํŠธ๊ฐ€ ์ธ์ง€ํ•˜์ง€ ๋ชปํ•  ์œ„ํ—˜์ด ์žˆ๋‹ค.

ํ•œํŽธ, ๋ฒˆ๋“ค๋ง์„ ํ†ตํ•ด ์••์ถ•๋œ ์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ LLM์—๊ฒŒ ์‚ฌ์ „์— ์ œ๊ณตํ•˜๊ณ , ์—ฌ๊ธฐ์— semantic search๋ฅผ ๊ฒฐํ•ฉํ•œ๋‹ค๋ฉด, ์ฝ”๋“œ๋ฒ ์ด์Šค์˜ ์ „๋ฐ˜์ ์ธ ๋งฅ๋ฝ์„ ๋ณด๋‹ค ์ถฉ์‹คํžˆ ์ดํ•ดํ•จ๊ณผ ๋™์‹œ์— ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šคํ™”๋œ ์›๋ณธ ์†Œ์Šค ์ฝ”๋“œ์— ํšจ๊ณผ์ ์œผ๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด ์œ ๋ฆฌํ•œ ์ƒํ™ฉ์„ ๋งˆ๋ จํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ฒฐ๋ก 

LLM์— ๋Œ€ํ•ด ์ฝ”๋“œ๋ฒ ์ด์Šค ์ „์ฒด๋ฅผ RAGํ•˜๊ธฐ ์œ„ํ•ด, ์ฝ”๋“œ๋ฒ ์ด์Šค ์ „์ฒด๋ฅผ ํ•œ ํŒŒ์ผ๋กœ ์••์ถ•ํ•˜์—ฌ LLM์—๊ฒŒ ์ „๋‹ฌํ•˜๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ์„ค๊ณ„ํ–ˆ๋‹ค.

ํ˜„์กดํ•˜๋Š” ๋ฒˆ๋“ค๋ง ํˆด์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ ์••์ถ•ํ•˜์˜€์„ ๋•Œ, LLM์€ ์ฝ”๋“œ๋ฒ ์ด์Šค ์ „์ฒด์— ๋Œ€ํ•œ API ๋ฌธ์„œํ™”๋ฅผ ์ง„ํ–‰ํ•˜๊ฑฐ๋‚˜, ํŠน์ • API์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋งค๋‰ด์–ผ์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

์ฝ”๋“œ๋ฒ ์ด์Šค ์••์ถ•์„ ์ง„ํ–‰ํ•˜๋ฉฐ ๋ฐœ์ƒํ•˜๋Š” ์ •๋ณด ์†์‹ค์— ๋Œ€ํ•ด์„œ๋Š” ์›๋ณธ ํŒŒ์ผ์— ๋Œ€ํ•œ ์ถ”๊ฐ€์ ์ธ Semantic search๋ฅผ ํ†ตํ•ด ๋ณด์™„ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์œผ๋กœ ์ „๋งํ•œ๋‹ค.

GeekNews์—์„œ ๋Œ“๊ธ€์„ ๋‚จ๊ธฐ์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Footnotes

  1. ์ •์  ๋ถ„์„์— ์†ํ•˜๋Š” ์˜ˆ: ํ˜ธ์ถœ ๊ทธ๋ž˜ํ”„ ์ž‘์„ฑ์„ ํ†ตํ•œ ์ œ์–ด ํ๋ฆ„ ํŒŒ์•… & ์šฐ์„ ์ˆœ์œ„ ํ™•์ธ, (์›น ์„œ๋ฒ„์˜ ๊ฒฝ์šฐ) โ€œ์ปจํŠธ๋กค๋Ÿฌโ€ ์ฝ”๋“œ๋ฅผ ์‹œ์ž‘์œผ๋กœ ๋ถ„์„ ์ง„ํ–‰, ์ฝ”๋“œ๋ฒ ์ด์Šค์— ์ด๋ฏธ ๋„์ž…๋œ ์•„ํ‚คํ…์ณ ํŒจํ„ด์ด ๋ฌด์—‡์ธ์ง€ ํŒŒ์•…ํ•œ ํ›„, ์—ฐ์—ญ์  ์ถ”๋ก ์„ ํ†ตํ•œ ๋ถ„์„ ๋ฐฉ๋ฒ• ์ˆ˜๋ฆฝ. Wikipedia: Call graph โ†ฉ

  2. ๋™์  ๋ถ„์„์— ์†ํ•˜๋Š” ์˜ˆ: ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ๊ณผ์ •์—์„œ ํ˜ธ์ถœ๋˜๋Š” ์ฝ”๋“œ์™€ ๊ทธ๋ ‡์ง€ ์•Š์€ ์ฝ”๋“œ๋ฅผ ๋ถ„๋ฅ˜ํ•˜์—ฌ ๊ฐ ์ฝ”๋“œ๊ฐ„ ๊ด€๊ณ„๋ฅผ ํŒŒ์•…. Wikipedia: Runtime verification โ†ฉ

  3. LLM ๋ชจ๋ธ์— ์ง€์‹์„ ๋น ๋ฅด๊ฒŒ ์ถ”๊ฐ€ํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ Retrieval-augmented generation(RAG)๊ฐ€ ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค. Wikipedia: Retrieval-augmented generation โ†ฉ

  4. OpenAI: "How does the new file uploads capability work? " - ChatGPT์˜ ํŒŒ์ผ ์—…๋กœ๋“œ(File Upload)๋Š” ์ „์ฒ˜๋ฆฌ, ๋ณ€ํ™˜, ์ถ”์ถœ์„ ๊ฑฐ์ณ ์ •๋ณด๋ฅผ ์ •์ œํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด Word ๋ฌธ์„œ๋ฅผ ์ฝ์„ ๋•Œ์—๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์ ์œผ๋กœ ๋ฏธ๋ฆฌ ํ•ด์„ํ•˜๊ณ  ๋ฌธ์„œ์˜ ์ œ๋ชฉ/๋ถ€์ œ๋ชฉ๋“ค์„ ์ถ”์ถœํ•˜์—ฌ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋กœ ํ™œ์šฉํ•œ๋‹ค. โ†ฉ

  5. ์ด์ „์— ์ž‘์„ฑํ•œ ์ ์ด ์žˆ๋Š” esbuild ๋นŒ๋“œ ์„ค์ •์„ ์กฐ๊ธˆ ์ˆ˜์ •ํ•˜์—ฌ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜์˜€๋‹ค. ์›๋ณธ์€ GitHub์—์„œ ํ™•์ธ ๊ฐ€๋Šฅํ•˜๋‹ค. GitHub: build-option.ts โ†ฉ

  6. GitHub: atjsh/mini-dice-v1, Mini Dice ์†Œ๊ฐœ โ†ฉ

  7. o3-mini๋Š” OpenAI์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ฐ€์žฅ ์ €๋ ดํ•œ LLM ์ค‘ ํ•˜๋‚˜์ด๋‹ค. OpenAI Pricing โ†ฉ

  8. devDependencies์— ์†ํ•˜๋Š” ํŒจํ‚ค์ง€ ํฌํ•จ. โ†ฉ

  9. GeekNews: 27๋…„ ๋œ EXE ํŒŒ์ผ์„ Claude 3.7์— ์—…๋กœ๋“œํ•œ ํ›„ ์ผ์–ด๋‚œ ๋†€๋ผ์šด ์ผ โ†ฉ