๋ณธ๋ฌธ์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

ยฉ 2026 Molayo

Dev.toํ—ค๋“œ๋ผ์ธ2026. 05. 30. 15:07

๐Ÿง  NeuroDoc: ๋ง๊ฐ€์ง„ ํ”„๋กœํ† ํƒ€์ž…์—์„œ ํ”„๋กœ๋•์…˜ ์ค€๋น„๊ฐ€ ๋œ ๋น„๋™๊ธฐ AI ๋ฌธ์„œ ์—”์ง„์œผ๋กœ

์š”์•ฝ

์ทจ์•ฝํ•œ CLI ์Šคํฌ๋ฆฝํŠธ์˜€๋˜ NeuroDoc์„ asyncio์™€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ธฐ๋ฐ˜ ์ž‘์—… ํ๋ฅผ ๋„์ž…ํ•˜์—ฌ ์•ˆ์ •์ ์ธ ๋น„๋™๊ธฐ RAG ์—”์ง„์œผ๋กœ ์žฌ์„ค๊ณ„ํ•œ ๊ณผ์ •์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ๋ธ”๋กœํ‚น ๋ฃจํ”„ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ  ์ž‘์—… ์ง€์†์„ฑ์„ ํ™•๋ณดํ•˜์—ฌ ํ”„๋กœ๋•์…˜ ์ˆ˜์ค€์˜ ๋ฌธ์„œ ์ฒ˜๋ฆฌ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.

ํ•ต์‹ฌ ํฌ์ธํŠธ

  • asyncio์™€ aiohttp๋ฅผ ํ™œ์šฉํ•œ ๋น„๋™๊ธฐ ๊ตฌ์กฐ๋กœ ๋ธ”๋กœํ‚น ๋ฌธ์ œ ํ•ด๊ฒฐ
  • DB ๊ธฐ๋ฐ˜ ์ž‘์—… ํ ๋„์ž…์„ ํ†ตํ•œ ๋ฐ์ดํ„ฐ ์†์‹ค ๋ฐฉ์ง€ ๋ฐ ์ž‘์—… ์žฌ๊ฐœ ๊ธฐ๋Šฅ ๊ตฌํ˜„
  • ๋‹จ์ˆœ ์Šคํฌ๋ž˜ํผ์—์„œ RAG ๋ ˆ์ด์–ด๋ฅผ ๊ฐ–์ถ˜ ์ง€๋Šฅํ˜• ์–ด์‹œ์Šคํ„ดํŠธ๋กœ ์ง„ํ™”
  • ํ”„๋กœํ† ํƒ€์ž…์˜ ๊ตฌ์กฐ์  ๊ฒฐํ•จ์„ ํ•ด๊ฒฐํ•˜๋Š” ์•„ํ‚คํ…์ฒ˜ ์žฌ์„ค๊ณ„ ๊ณผ์ • ๊ณต์œ 

์ด ๊ฒŒ์‹œ๋ฌผ์€ GitHub Finish-Up-A-Thon Challenge๋ฅผ ์œ„ํ•œ ์ œ์ถœ๋ฌผ์ž…๋‹ˆ๋‹ค.

์ €๋Š” ์ด ํ”„๋กœ์ ํŠธ๋ฅผ ํฌ๊ธฐํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‹ค ๋‹ค์‹œ ๋ถ€ํ™œ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค. ์ทจ์•ฝํ•œ CLI ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์–ด๋–ป๊ฒŒ RAG ๊ธฐ๋Šฅ์„ ๊ฐ–์ถ˜ ํ’€์Šคํƒ ๋น„๋™๊ธฐ ์›น ๋Œ€์‹œ๋ณด๋“œ๋กœ ๋ณ€ํ–ˆ๋Š”์ง€ ๊ทธ ๊ณผ์ •์„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.

๐Ÿงจ ๋ฌธ์ œ์  โ€” ์™œ ํฌ๊ธฐํ–ˆ๋Š”๊ฐ€

NeuroDoc์€ ์•ผ์‹ฌ ์ฐฌ ์•„์ด๋””์–ด๋กœ ์‹œ์ž‘๋˜์—ˆ์Šต๋‹ˆ๋‹ค. NLP(์ž์—ฐ์–ด ์ฒ˜๋ฆฌ)์™€ ๋ฉ€ํ‹ฐ์ฝ”์–ด ํ”„๋กœ์„ธ์‹ฑ(multi-core processing)์„ ํ™œ์šฉํ•˜์—ฌ Python, scikit-learn, PyTorch, TensorFlow ์ „๋ฐ˜์˜ ๋ฌธ์„œ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ (fetch), ์Šคํฌ๋ž˜ํ•‘(scrape)ํ•˜๊ณ , ์ฒ˜๋ฆฌ(process)ํ•˜๋ฉฐ, ์š”์•ฝ(summarize)ํ•˜๋Š” ๋‹จ์ผ ๋„๊ตฌ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๊ณง ๋ฒฝ์— ๋ถ€๋”ชํ˜”์Šต๋‹ˆ๋‹ค.

# ๋นŒ๋Ÿฐ: ๋ชจ๋“  ๊ฒƒ์„ ์–ผ๋ ค๋ฒ„๋ฆฌ๋Š” ๋ธ”๋กœํ‚น ๋™๊ธฐ ๋ฃจํ”„(blocking synchronous loop)
while True:
    query = input("Enter query: ")  # ๐Ÿšซ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋ฅผ ์ฐจ๋‹จ(BLOCKS)ํ•จ
...

์›๋ž˜์˜ ํ”„๋กœํ† ํƒ€์ž…์—๋Š” ์„ธ ๊ฐ€์ง€ ์น˜๋ช…์ ์ธ ๊ฒฐํ•จ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค:

๋ฌธ์ œ์ ์˜ํ–ฅ
๋ฉ”์ธ ์Šค๋ ˆ๋“œ์˜ input() ๋ฃจํ”„๋ชจ๋“  ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šคํฌ๋ž˜ํ•‘ ์›Œ์ปค(scraping workers)๋ฅผ ์ฐจ๋‹จํ•จ
...

์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๋ฌธ์„œ ํฌ๋กค๋ง(doc crawls)์ด ์ค‘๋‹จ๋˜๊ณค ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹จ ํ•œ ๋ฒˆ์˜ ์ถฉ๋Œ๋กœ ์ „์ฒด ์ž‘์—… ํ(task queue)๊ฐ€ ์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๋งˆ์น˜ ์‚ฌ์ƒ๋ˆ„๊ฐ ๊ฐ™์•˜์Šต๋‹ˆ๋‹ค. ๋ฉ€๋ฆฌ์„œ ๋ณด๋ฉด ์ธ์ƒ์ ์ด์—ˆ์ง€๋งŒ, ๊ฐ€๊นŒ์ด์„œ ๋ณด๋ฉด ๊ณตํฌ์Šค๋Ÿฌ์› ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์ €๋Š” ํ”„๋กœ์ ํŠธ๋ฅผ ๋ณด๋ฅ˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ’ก ์žฌ๊ธฐ โ€” ๋ฌด์—‡์ด ๋ฐ”๋€Œ์—ˆ๋Š”๊ฐ€

๋ช‡ ๋‹ฌ ํ›„, ์ €๋Š” ๋ง‘์€ ์ •์‹ ๊ณผ ๊ณ„ํš์„ ๊ฐ€์ง€๊ณ  ๋Œ์•„์™”์Šต๋‹ˆ๋‹ค. ์žฌ์ž‘์„ฑ์€ ์ ์ง„์ ์ธ ๋ฐฉ์‹์ด ์•„๋‹ˆ๋ผ ๊ตฌ์กฐ์ ์ธ(architectural) ๋ฐฉ์‹์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์„ธ ๊ฐ€์ง€ ๋ณ€ํ™”๊ฐ€ ๋ชจ๋“  ๊ฒƒ์„ ๋งž์•„๋–จ์–ด์ง€๊ฒŒ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค:

1. ๐Ÿ”„ asyncio + aiohttp๋ฅผ ์ด์šฉํ•œ ์™„์ „ํ•œ ๋น„๋™๊ธฐ(Async) ์žฌ์ž‘์„ฑ

๋ธ”๋กœํ‚น ๋ฃจํ”„๋ฅผ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  ์Šคํฌ๋ž˜ํ•‘, ์ฒ˜๋ฆฌ, ์„œ๋น„์Šค ์ œ๊ณต์ด ์„œ๋กœ ๋ฐฉํ•ดํ•˜์ง€ ์•Š๊ณ  **๋™์‹œ(concurrently)**์— ์ผ์–ด๋‚  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ์ ์ ˆํ•œ ๋น„๋™๊ธฐ ์ด๋ฒคํŠธ ๋ฃจํ”„(async event loop)๋ฅผ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค.

async def fetch_documentation(url: str, session: aiohttp.ClientSession) -> DocResult:
    async with session.get(url, timeout=aiohttp.ClientTimeout(total=30)) as response:
        content = await response.text()
...

๋” ์ด์ƒ ํ„ฐ๋ฏธ๋„์ด ๋ฉˆ์ถ”์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋” ์ด์ƒ ์›Œ์ปค(workers)๊ฐ€ ์ค‘๋‹จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

2. ๐Ÿ—„๏ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ธฐ๋ฐ˜ ์ž‘์—… ํ (ํœ˜๋ฐœ์„ฑ ๋ฉ”๋ชจ๋ฆฌ ์•ˆ๋…•)

์ธ๋ฉ”๋ชจ๋ฆฌ(in-memory) ํ๋ฅผ **์ง€์† ๊ฐ€๋Šฅํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ธฐ๋ฐ˜ ์ž‘์—… ํ(persistent, database-backed task queue)**๋กœ ๊ต์ฒดํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ์ƒˆ๋ฒฝ 3์‹œ์— PyTorch ๋ฌธ์„œ๋ฅผ ํฌ๋กค๋งํ•˜๋˜ ์ค‘ ์„œ๋ฒ„๊ฐ€ ์ถฉ๋Œํ•˜๋”๋ผ๋„ ์ž‘์—…์ด ์†์‹ค๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ž‘์—…์€ ์ •ํ™•ํžˆ ์ค‘๋‹จ๋œ ์ง€์ ๋ถ€ํ„ฐ ์žฌ๊ฐœ๋ฉ๋‹ˆ๋‹ค.

class TaskQueue:
    async def enqueue(self, task: DocumentationTask) -> str:
        task_id = str(uuid.uuid4())
...

3. ๐Ÿ” RAG โ€” ๊ฒ€์ƒ‰ ์ฆ๊ฐ• ์ƒ์„ฑ (Retrieval-Augmented Generation) ๋ ˆ์ด์–ด

์ด ๋‹จ๊ณ„์—์„œ NeuroDoc์€ ๋‹จ์ˆœํ•œ "์Šคํฌ๋ ˆ์ดํผ (scraper)"๋ฅผ ๋„˜์–ด "์ง€๋Šฅํ˜• ๋ฌธ์„œ ์–ด์‹œ์Šคํ„ดํŠธ (intelligent documentation assistant)"๋กœ ์—…๊ทธ๋ ˆ์ด๋“œ๋ฉ๋‹ˆ๋‹ค.

๊ฐ€๊ณต๋˜์ง€ ์•Š์€ ๋ฌธ์„œ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋Œ€์‹ , ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ณผ์ •์„ ๊ฑฐ์นฉ๋‹ˆ๋‹ค:

  1. ์ฒญํ‚น (Chunks): ์Šคํฌ๋ ˆ์ดํ•‘๋œ ์ฝ˜ํ…์ธ ๋ฅผ ์˜๋ฏธ๋ก ์  ์„ธ๊ทธ๋จผํŠธ (semantic segments)๋กœ ๋‚˜๋ˆ•๋‹ˆ๋‹ค.
  2. ์ž„๋ฒ ๋”ฉ (Embeds): ์ด๋ฅผ ๋ฒกํ„ฐ ์Šคํ† ์–ด (vector store)์— ์ž„๋ฒ ๋”ฉํ•ฉ๋‹ˆ๋‹ค.
  3. ๊ฒ€์ƒ‰ (Retrieves): ์ฟผ๋ฆฌ์— ๋Œ€ํ•ด ๊ฐ€์žฅ ๊ด€๋ จ์„ฑ์ด ๋†’์€ ์ฒญํฌ๋ฅผ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.
  4. ์ƒ์„ฑ (Generates): ์ง‘์ค‘์ ์ด๊ณ  ๋ฌธ๋งฅ์„ ์ธ์‹ํ•˜๋Š” (context-aware) ์š”์•ฝ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
class RAGPipeline:
    async def query(self, user_query: str) -> RAGResponse:
        # 1๋‹จ๊ณ„: ์ฟผ๋ฆฌ ์ž„๋ฒ ๋”ฉ
...

๐Ÿ—๏ธ ์•„ํ‚คํ…์ฒ˜ ๊ฐœ์š” (Architecture Overview)

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                   ์›น ๋Œ€์‹œ๋ณด๋“œ (Web Dashboard, FastAPI)            โ”‚
โ”‚              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”            โ”‚
...

๐Ÿ“ฆ ์ง€์›๋˜๋Š” ๋ฌธ์„œ ์†Œ์Šค (Supported Documentation Sources)

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์Šคํฌ๋ ˆ์ดํ•‘๋œ ์„น์…˜NLP ์ฒ˜๋ฆฌ
๐Ÿ Pythonstdlib, builtins, language ref์ฝ”๋“œ ์ถ”์ถœ, ์š”์•ฝ
...

๐Ÿš€ ์‹œ์ž‘ํ•˜๊ธฐ (Getting Started)

# ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํด๋ก 
git clone https://github.com/kaushikcoderpy1/neurodoc
cd neurodoc
...

๊ทธ ๋‹ค์Œ http://localhost:8000์„ ์—ด๊ณ  ์ฟผ๋ฆฌ๋ฅผ ์‹œ์ž‘ํ•˜์„ธ์š”.

๐Ÿงช ์ฃผ์š” ๊ธฐ์ˆ ์  ๊ฒฐ์ • โ€” ๊ทธ๋ฆฌ๊ณ  ๊ทธ ์ด์œ 

์™œ threading ๋Œ€์‹  asyncio๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๊ฐ€?
๋ฌธ์„œ ์Šคํฌ๋ ˆ์ดํ•‘์€ I/O ๋ฐ”์šด๋“œ (I/O-bound, HTTP ๋Œ€๊ธฐ) ์ž‘์—…์ž…๋‹ˆ๋‹ค. asyncio๋Š” ๋‹จ์ผ ์Šค๋ ˆ๋“œ๋กœ ์ˆ˜์ฒœ ๊ฐœ์˜ ๋™์‹œ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, GIL (Global Interpreter Lock) ๊ฒฝํ•ฉ์ด๋‚˜ ๋ ˆ์ด์Šค ์ปจ๋””์…˜ (race conditions)์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์™œ ์ž‘์—… ํ(task queue)๋กœ Redis ๋Œ€์‹  SQLite๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๊ฐ€?
์ธํ”„๋ผ ์„ค์ •์ด ํ•„์š” ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. NeuroDoc์€ ๊ฐœ๋ฐœ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ ํ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด Redis ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์€ ๋งˆ์ฐฐ์„ ์ผ์œผํ‚ต๋‹ˆ๋‹ค. SQLite์˜ WAL (Write-Ahead Logging) ๋ชจ๋“œ๋Š” ์ด ์‚ฌ์šฉ ์‚ฌ๋ก€์—์„œ ๋™์‹œ ์ฝ๊ธฐ/์“ฐ๊ธฐ๋ฅผ ๊น”๋”ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

์™œ ํŒŒ์ธํŠœ๋‹ (fine-tuning) ๋Œ€์‹  RAG๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๊ฐ€?
๋ฌธ์„œ๋Š” ๋Š์ž„์—†์ด ๋ณ€ํ•ฉ๋‹ˆ๋‹ค. RAG๋Š” ์‹ค์‹œ๊ฐ„์œผ๋กœ ์Šคํฌ๋ ˆ์ดํ•‘๋œ ์ฝ˜ํ…์ธ ์—์„œ ์ •๋ณด๋ฅผ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ธํŠœ๋‹๋œ ๋ชจ๋ธ์€ ๋ช‡ ์ฃผ ์•ˆ์— ๋ฐ์ดํ„ฐ๊ฐ€ ๊ตฌ์‹์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๐Ÿค– GitHub Copilot์ด NeuroDoc์„ ๊ตฌํ•œ ๋ฐฉ๋ฒ• โ€” ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ ๋„์›€์„ ์ค€ 4๊ฐ€์ง€ ์น˜๋ช…์ ์ธ ๋ฒ„๊ทธ

์ด ์„น์…˜์€ ์žฌ๊ธฐ ์„ฑ๊ณต๋‹ด์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค. NeuroDoc์€ ๋‹จ์ˆœํžˆ ๋‹ค์‹œ ์ž‘์„ฑ๋œ ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค. Copilot์„ ์ง„์ •ํ•œ ํŽ˜์–ด ํ”„๋กœ๊ทธ๋ž˜๋จธ (pair programmer)๋กœ ํ™œ์šฉํ•˜์—ฌ _์‹ฌ์ธต์ ์ธ ์•„ํ‚คํ…์ฒ˜ ์ˆ˜์ค€์—์„œ ๋””๋ฒ„๊น… (debugged at a deep architectural level)_๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์€ Copilot์ด ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ ๋„์›€์„ ์ค€, ์‹ค์ œ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์„ ๊ฐ€๋กœ๋ง‰์•˜๋˜ ๋„ค ๊ฐ€์ง€ ์น˜๋ช…์ ์ธ ๋ฒ„๊ทธ์ž…๋‹ˆ๋‹ค.

๐Ÿ› ๋ฒ„๊ทธ 1: ๋ฉ€ํ‹ฐ ์ฝ”์–ด ๋ฐฐ์น˜ ์ž‘์—… ์ค‘ ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ํ’€ ๋ˆ„์ˆ˜ (Async Database Connection Pool Leaks)

์žฅ์•  ๋‚ด์šฉ: asyncio.gather๋ฅผ ํ†ตํ•œ ๊ณ ๋™์‹œ์„ฑ (high-concurrency) ๋ถ€ํ•˜ ์ƒํ™ฉ์—์„œ, ์„œ๋ธŒ ์ฝ”๋ฃจํ‹ด (sub-coroutines) ๋‚ด๋ถ€์˜ ์˜ˆ์™ธ ์ผ€์ด์Šค๊ฐ€ ์—ฐ๊ฒฐ ํ•ด์ œ ํ›… (connection release hooks)์„ ์šฐํšŒํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด asyncpg ํ’€ (pool) ์†Œ์ผ“์ด ๊ณ ๊ฐˆ๋˜์—ˆ๊ณ , ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์•„๋ฌด๋Ÿฐ ๋ฉ”์‹œ์ง€ ์—†์ด ๋ฉˆ์ถฐ๋ฒ„๋ฆฌ๋Š” ํ˜„์ƒ์ด ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ‘œ์ค€์ ์ธ try/finally ์ •๋ฆฌ ๋ธ”๋ก์€ ์˜ค๋ž˜๋œ ๋น„๋™๊ธฐ ์ปจํ…์ŠคํŠธ (async contexts)๋ฅผ ์ฐธ์กฐํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๊ฒฐ๊ตญ ํ’€ (pool)์ด ์ตœ๋Œ€ ์šฉ๋Ÿ‰์— ๋„๋‹ฌํ•˜์—ฌ ๋™๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

Copilot์˜ ๋„์›€:

Copilot์€ ๋กœ์ปฌ ํŠธ๋žœ์žญ์…˜ (transaction) ์ƒ๋ช…์ฃผ๊ธฐ์— ์ง์ ‘ ๊ฒฐํ•ฉ๋˜๊ณ  ์ ˆ๋Œ€์ ์ธ ํƒ€์ž„์•„์›ƒ ๊ฐ€๋“œ (timeout guards)๋ฅผ ํฌํ•จํ•˜๋Š” ์—„๊ฒฉํ•œ ์—ฐ๊ฒฐ ํš๋“ ํŒจํ„ด์„ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค:

# Copilot์ด ์ œ์•ˆํ•œ ํš๋“ ํŒจํ„ด
async with pool.acquire() as connection:
    async with connection.transaction():
...

๋˜ํ•œ, ์›์‹œ ๋“œ๋ผ์ด๋ฒ„ (driver) ์—๋Ÿฌ๋ฅผ ๊น”๋”ํ•œ ๊ตฌ์กฐํ™”๋œ ์‘๋‹ต์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ „์—ญ ์˜ˆ์™ธ ๋ž˜ํผ (exception wrappers)๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋‹ค์šด์ŠคํŠธ๋ฆผ ์Šคํฌ๋ž˜ํ•‘ ํŒŒ์ดํ”„๋ผ์ธ (downstream scraping pipeline)์ด ์ถฉ๋Œํ•˜๋”๋ผ๋„ ์—ฐ๊ฒฐ ์ •๋ฆฌ๋ฅผ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ› ๋ฒ„๊ทธ 2: ํŒจํ‚ค์ง• ๋ฒ„์ „ ๊ฐ„ SpecifierSet .contains() AttributeError ๋ฐœ์ƒ

์žฅ์•  ๋‚ด์šฉ: formatter.py๋Š” DependencyAnalyzer๋ฅผ ํ†ตํ•ด ์˜์กด์„ฑ ์ง„๋‹จ (dependency diagnostics)์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๊ตฌ๋ฒ„์ „์˜ packaging ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์„ค์น˜๋œ ํ™˜๊ฒฝ์—์„œ๋Š” SpecifierSet์— ๋Œ€ํ•ด .contains()๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค:

AttributeError: 'SpecifierSet' object has no attribute 'contains'

์ด๋กœ ์ธํ•ด ์ง„๋‹จ ํŒจ๋„์ด ๋ Œ๋”๋ง๋˜๊ธฐ๋„ ์ „์— ์ „์ฒด ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ถฉ๋Œํ–ˆ์œผ๋ฉฐ, ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋Œ€๋‹ค์ˆ˜์˜ ์‚ฌ์šฉ์ž ํ™˜๊ฒฝ์—์„œ ํ™˜๊ฒฝ ๊ฒ€์ฆ (environment validation) ๊ธฐ๋Šฅ์ด ์กฐ์šฉํžˆ ์ž‘๋™์„ ๋ฉˆ์ท„์Šต๋‹ˆ๋‹ค.

Copilot์˜ ๋„์›€:

Copilot์€ .contains()๊ฐ€ ํŠน์ • ๋ฒ„์ „์— ์ข…์†์ ์ด์ง€๋งŒ, ๋„ค์ดํ‹ฐ๋ธŒ in ์—ฐ์‚ฐ์ž๋Š” packaging์˜ ๋ชจ๋“  ๊ณผ๊ฑฐ ๋ฆด๋ฆฌ์Šค์— ๋Œ€ํ•ด **๋ณดํŽธ์ ์ธ ํ•˜์œ„ ํ˜ธํ™˜์„ฑ (backward-compatible)**์„ ๊ฐ€์ง„๋‹ค๋Š” ์ ์„ ์‹๋ณ„ํ•ด๋ƒˆ์Šต๋‹ˆ๋‹ค:

# โŒ ๊ธฐ์กด์˜ ์‹คํŒจํ•˜๋Š” ์ฝ”๋“œ
elif not raw_spec.contains(local):

...

์—ฐ์‚ฐ์ž ํ•˜๋‚˜๋ฅผ ๊ต์ฒดํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ, ๋ชจ๋“  ํ™˜๊ฒฝ์—์„œ ์ถฉ๋Œ์ด ์™„์ „ํžˆ ์‚ฌ๋ผ์กŒ์Šต๋‹ˆ๋‹ค.

๐Ÿ› ๋ฒ„๊ทธ 3: ๋‹จ์ผ ์ฝ”์–ด CLI ๋””์ŠคํŒจ์น˜(Dispatch)๋ฅผ ๊นจ๋œจ๋ฆฌ๋Š” ์•”์‹œ์  ๋ฌธ์ž์—ด ๋งคํ•‘

์‹คํŒจ ์›์ธ: neurodoc.py์—์„œ neurodoc fetch os์™€ ๊ฐ™์€ CLI ์ž…๋ ฅ์ด ์ฝ”์–ด ID `

์ด๊ฒƒ๋“ค์€ ๋‹จ์ˆœํ•œ ์ž๋™ ์™„์„ฑ ์ œ์•ˆ์ด ์•„๋‹ˆ์—ˆ์Šต๋‹ˆ๋‹ค. Copilot์€ ๋น„๋™๊ธฐ ์ƒ๋ช…์ฃผ๊ธฐ ๊ฒฝ๊ณ„ (async lifecycle boundaries), ๊ต์ฐจ ๋ฒ„์ „ API ํ˜ธํ™˜์„ฑ (cross-version API compatibility), ํƒ€์ž… ์‹œ์Šคํ…œ์˜ ์—ฃ์ง€ ์ผ€์ด์Šค (type system edge cases), ๊ทธ๋ฆฌ๊ณ  **์„ ํ˜• ๋Œ€์ˆ˜ ์ œ์•ฝ ์กฐ๊ฑด (linear algebra constraints)**์— ๋Œ€ํ•ด ์ถ”๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์ˆ˜์ •์€์ปค๋…• ์‹ฌ์ง€์–ด ์œ„์น˜๋ฅผ ์ฐพ๋Š” ๋ฐ๋งŒ ์ˆ˜ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฌ๋Š” ์ข…๋ฅ˜์˜ ๋ฒ„๊ทธ๋“ค์ž…๋‹ˆ๋‹ค.

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

์ด๊ฒƒ์ด ๋ฐ”๋กœ ๋„๊ตฌ(tool)์™€ ํ˜‘์—…์ž(collaborator)์˜ ์ฐจ์ด์ž…๋‹ˆ๋‹ค.

๐Ÿ”ญ ํ–ฅํ›„ ๊ณ„ํš

  • ์›ํด๋ฆญ ๋ฌธ์„œ ์กฐํšŒ๋ฅผ ์œ„ํ•œ ๋ธŒ๋ผ์šฐ์ € ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ
  • WebSockets๋ฅผ ํ†ตํ•œ ์ŠคํŠธ๋ฆฌ๋ฐ ์‘๋‹ต
  • Hugging Face, Pandas, NumPy ๋ฌธ์„œ ์ง€์›
  • ์ž์ฒด ํ˜ธ์ŠคํŒ… ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ (API ํ‚ค ๋ถˆํ•„์š”)
  • ์š”์•ฝ๋ณธ์„ Jupyter notebook์œผ๋กœ ๋‚ด๋ณด๋‚ด๊ธฐ

๐Ÿ”— ๋งํฌ

DEV.to ํ•ด์ปคํ†ค์„ ์œ„ํ•ด ์ œ์ž‘๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ณ ์ง‘๊ณผ ๋น„๋™๊ธฐ Python (async Python), ๊ทธ๋ฆฌ๊ณ  ๋„ˆ๋ฌด ๋งŽ์€ ์ปคํ”ผ์˜ ํž˜์œผ๋กœ ๋งŒ๋“ค์–ด์กŒ์Šต๋‹ˆ๋‹ค.

AI ์ž๋™ ์ƒ์„ฑ ์ฝ˜ํ…์ธ 

๋ณธ ์ฝ˜ํ…์ธ ๋Š” Dev.to AI tag์˜ ์›๋ฌธ์„ AI๊ฐ€ ์ž๋™์œผ๋กœ ์š”์•ฝยท๋ฒˆ์—ญยท๋ถ„์„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์› ์ €์ž‘๊ถŒ์€ ์›์ €์ž‘์ž์—๊ฒŒ ์žˆ์œผ๋ฉฐ, ์ •ํ™•ํ•œ ๋‚ด์šฉ์€ ๋ฐ˜๋“œ์‹œ ์›๋ฌธ์„ ํ™•์ธํ•ด ์ฃผ์„ธ์š”.

์›๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
1

๋Œ“๊ธ€

0