FinGraph / src /utils /technical_report.md
dev-yuje's picture
feat: 4๋Œ€ ๊ณจ๋“œ ์‹œ๋‚˜๋ฆฌ์˜ค RAG ํ’ˆ์งˆ ๊ฐœ์„  ๋ฐ Gradio UI ํฐํŠธ ์‹œ์ธ์„ฑ ๊ณ ๋„ํ™”
ff0395c
|
raw
history blame
10.3 kB

๐Ÿ•ธ๏ธ FinGraph ํ”„๋กœ์ ํŠธ ์ข…ํ•ฉ ๊ธฐ์ˆ  ๊ฐœ์„  ๋ฐ ๊ฒ€์ฆ ์ž๋™ํ™” ๋ณด๊ณ ์„œ (Technical Report)

๋ณธ ๋ณด๊ณ ์„œ๋Š” FinGraph ํ”„๋กœ์ ํŠธ์˜ ์•ˆ์ •์ ์ด๊ณ  ์ง€์† ๊ฐ€๋Šฅํ•œ ์šด์˜์„ ์œ„ํ•ด ๊ทธ๋™์•ˆ ๋ฐœ์ƒํ–ˆ๋˜ ๋ฐ์ดํ„ฐ ๊ณต๊ธ‰ ๋ถ€์กฑ, ๋Ÿฐํƒ€์ž„ ํฌ๋ž˜์‹œ, CI/CD ๋ฐฐํฌ ๋ณ‘๋ชฉ, ๊ทธ๋ฆฌ๊ณ  ์ •์  ๋ถ„์„ ๋„๊ตฌ ๊ฐ„์˜ ๊ธฐ์ˆ ์  ์ถฉ๋Œ ๋ฌธ์ œ๋“ค์„ ์‹ฌ๋„ ์žˆ๊ฒŒ ๋ถ„์„ํ•˜๊ณ  ํ•ด๊ฒฐํ•œ ๊ณผ์ •์„ ๊ธฐ์ˆ ์ ์œผ๋กœ ์ •๋ฆฌํ•œ ๋ฌธ์„œ์ž…๋‹ˆ๋‹ค.


1. ๋ฐ์ดํ„ฐ ๋ ˆ์ด์–ด: ๋‰ด์Šค ๊ณต๊ธ‰ ํฌ์†Œ์„ฑ ๋ฐ ์ง€์‹ ๊ทธ๋ž˜ํ”„ ๋ฐ€๋„ ๊ฐœ์„ 

๐Ÿ”ด ๋ฌธ์ œ ์ƒํ™ฉ (๋ฐ์ดํ„ฐ ์ ˆ๋Œ€ ๋ถ€์กฑ)

  • ์›์ธ: ์ดˆ๊ธฐ ๊ธฐํš ๋‹จ๊ณ„์—์„œ ์ˆ˜์ง‘๋œ ๋‰ด์Šค ๋ฐ์ดํ„ฐ๊ฐ€ ์ ˆ๋Œ€์ ์œผ๋กœ ๋ถ€์กฑํ•˜์—ฌ Neo4j ์ง€์‹ ๊ทธ๋ž˜ํ”„์˜ ๋ฐ€๋„๊ฐ€ ๋งค์šฐ ๋‚ฎ์•˜์Šต๋‹ˆ๋‹ค.
  • ์˜ํ–ฅ: GraphRAG ๊ฒ€์ƒ‰์„ ์‹คํ–‰ํ–ˆ์„ ๋•Œ ๊ด€๋ จ ๋„๋ฉ”์ธ ์—”ํ‹ฐํ‹ฐ(๊ธฐ์—…, ๊ธฐ์ˆ , ์„œ๋น„์Šค) ๊ฐ„์˜ ์—ฐ๊ฒฐ ๊ณ ๋ฆฌ๊ฐ€ ๋Š์–ด์ ธ RAG ์„ฑ๋Šฅ์ด ๊ธ‰๊ฒฉํžˆ ์ €ํ•˜๋˜๊ฑฐ๋‚˜ ํ™˜๊ฐ(Hallucination) ํ˜„์ƒ์ด ์œ ๋„๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๐ŸŸข ํ•ด๊ฒฐ ๋ฐฉ์•ˆ ๋ฐ ์„ฑ๊ณผ

  1. ํฌ๋กค๋Ÿฌ ๋„๋ฉ”์ธ ์Šค์ผ€์ผ์—… (finScrapping.py):
    • ๋‰ด์Šค ์ˆ˜์ง‘ ๋ฒ”์œ„ ๋ฐ ํ‚ค์›Œ๋“œ ํ•„ํ„ฐ๋ง ๋กœ์ง์„ ์ •๊ตํ™”ํ•˜์—ฌ ๊ธˆ์œตAI ํŠธ๋ Œ๋“œ์— ์ •ํ™•ํžˆ ๋ถ€ํ•ฉํ•˜๋Š” ๊ณ ํ’ˆ์งˆ์˜ ๋‰ด์Šค ๊ธฐ์‚ฌ ์ด 74๊ฑด์„ ๋Œ€๋Ÿ‰์œผ๋กœ ์ถ”๊ฐ€ ์ˆ˜์ง‘ํ–ˆ์Šต๋‹ˆ๋‹ค.
  2. ์ดˆ๊ณ ๋ฐ€๋„ ์ง€์‹ ๊ทธ๋ž˜ํ”„ ๊ตฌ์ถ• (finGraph.py):
    • ์ถ”๊ฐ€ ์ˆ˜์ง‘๋œ ๋‰ด์Šค ๋ฐ์ดํ„ฐ๋ฅผ ํ™œ์šฉํ•ด Neo4j ์ ์žฌ ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ๋™ํ•˜์—ฌ **์ด 296๊ฐœ์˜ ๋…ธ๋“œ(Node)์™€ 346๊ฐœ์˜ ๊ด€๊ณ„์„ (Edge)**์œผ๋กœ ํ™•์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ์ด๋ฅผ ํ†ตํ•ด ์—”ํ‹ฐํ‹ฐ ๊ฐ„์˜ ์€ํ•˜์ˆ˜ ๊ตฌ์กฐ(Milky-Way Schema)๋ฅผ ์ด๋ฃจ๋Š” ๊ณ ๋ฐ€๋„ ๊ทธ๋ž˜ํ”„๋ฅผ ์™„์„ฑํ•˜์—ฌ ๋‹ค๊ฐ๋„ ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰์ด ๊ฐ€๋Šฅํ•ด์กŒ์Šต๋‹ˆ๋‹ค.
  3. ์ ์žฌ ์„ฑ๋Šฅ ์ตœ์ ํ™” (Incremental Load):
    • ๋งค๋ฒˆ ์ „์ฒด ์‚ญ์ œ ํ›„ ์žฌ์ ์žฌํ•˜๋˜ ๊ธฐ์กด ๋ฐฉ์‹์—์„œ ํƒˆํ”ผํ•˜์—ฌ, ์ด๋ฏธ ์ ์žฌ๋œ ๊ธฐ์‚ฌ(article_id) ๋ฐ ๋ณธ๋ฌธ ์ฒญํฌ ๋…ธ๋“œ๋Š” OpenAI API ํ˜ธ์ถœ์„ ์Šคํ‚ตํ•˜๋„๋ก ๊ฐœ์„ ํ•˜์—ฌ ์†๋„๋ฅผ ๊ทน๋Œ€ํ™”ํ•˜๊ณ  API ๋น„์šฉ์„ ๋ณด์กดํ–ˆ์Šต๋‹ˆ๋‹ค.

2. ํ”„๋ก ํŠธ์—”๋“œ ๋ ˆ์ด์–ด: Gradio 6.0 ๋Ÿฐํƒ€์ž„ ํฌ๋ž˜์‹œ ์†Œํƒ•

๐Ÿ”ด ๋ฌธ์ œ ์ƒํ™ฉ (Unaligned Interface & Jinja2 Template Error)

  • ์›์ธ: Gradio 6.0์œผ๋กœ ๋ฒ„์ „์ด ์˜ฌ๋ผ๊ฐ€๋ฉด์„œ ๋ ˆ์ด์•„์›ƒ ์ปดํฌ๋„ŒํŠธ ์ •์ฑ…์ด ๋Œ€ํญ ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
    • gr.Blocks() ํ•˜์œ„์— gr.ChatInterface()๋ฅผ ์ค‘์ฒฉํ•˜์—ฌ ๋ Œ๋”๋ง์„ ์‹œ๋„ํ•  ๋•Œ FastAPI ๋ผ์šฐํ„ฐ์™€ Jinja2 ํ…œํ”Œ๋ฆฟ ์—”์ง„์ด ๊ผฌ์ด๋ฉด์„œ TypeError: unhashable type: 'dict' ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉฐ ์›น ํ™”๋ฉด์ด ์•„์˜ˆ ๋กœ๋“œ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
    • ํ…Œ๋งˆ ์„ค์ • ํŒŒ๋ผ๋ฏธํ„ฐ(theme)๊ฐ€ ChatInterface ์ƒ์„ฑ์ž์˜ ์ธ์ž์—์„œ ์ง€์› ์ค‘๋‹จ๋˜์–ด ์ดˆ๊ธฐํ™” ์‹คํŒจ ํฌ๋ž˜์‹œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

๐ŸŸข ํ•ด๊ฒฐ ๋ฐฉ์•ˆ

  1. ๋‹จ์ผ ์•„ํ‚คํ…์ฒ˜ ํ†ตํ•ฉ ๋ฆฌํŒฉํ† ๋ง (app.py):
    • ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์ค‘์ฒฉ๋˜์–ด ์ถฉ๋Œ์„ ์œ ๋ฐœํ•˜๋˜ gr.Blocks() ๊ตฌ์กฐ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ , ์ตœ์ ํ™”๋œ ๋‹จ์ผ gr.ChatInterface ์ค‘์‹ฌ์˜ ์•„ํ‚คํ…์ฒ˜๋กœ UI๋ฅผ ์Šฌ๋ฆผํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.
  2. ๋Ÿฐํƒ€์ž„ ํŒŒ๋ผ๋ฏธํ„ฐ ์œ„์น˜ ๋™๊ธฐํ™”:
    • ์ƒ์„ฑ์ž์—์„œ ๊ฑฐ๋ถ€๋˜๋˜ theme ์ธ์ž๋ฅผ ์‚ญ์ œํ•˜๊ณ , ์ตœ์‹  Gradio 6.0 ๋ช…์„ธ์— ๋งž์ถฐ ์ฑ—๋ด‡์ด ์‹ค์ œ ๊ตฌ๋™๋˜๋Š” ๊ตฌ๋ฌธ์ธ demo.launch(theme=...) ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€๋กœ ์ด๋™์‹œ์ผœ ํฌ๋ž˜์‹œ๋ฅผ ์›์ฒœ ์ฐจ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.

3. ์•„ํ‚คํ…์ฒ˜ ๋ ˆ์ด์–ด: ์ž„ํฌํŠธ ํƒ€์ž„(Import-Time) ์™ธ๋ถ€ ํ†ต์‹  ์ „๋ฉด ํ†ต์ œ

๐Ÿ”ด ๋ฌธ์ œ ์ƒํ™ฉ (GitHub Actions CI ๋ฐ ๋กœ์ปฌ ํ…Œ์ŠคํŠธ ๋จนํ†ต)

  • ์›์ธ: ๋ชจ๋“ˆ ์ „์—ญ ๋ฒ”์œ„(Global Scope)์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์ฆ‰์‹œ ์—ฐ๊ฒฐ(get_neo4j_driver())ํ•˜๊ฑฐ๋‚˜ OpenAI API ํ‚ค๊ฐ€ ํ•„์š”ํ•œ ํด๋ผ์ด์–ธํŠธ ๊ฐ์ฒด(OpenAILLM, OpenAIEmbeddings)๋ฅผ ์ „์—ญ ๊ณต๊ฐ„์— ์„ ์–ธํ•˜์—ฌ ์ƒ์„ฑํ•˜๋„๋ก ์„ค๊ณ„๋˜์–ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ํ–ฅ: ๋น„๋ฐ€ํ‚ค(OPENAI_API_KEY, NEO4J_URI ๋“ฑ)๊ฐ€ ์ œ๊ณต๋˜์ง€ ์•Š๋Š” ์•ˆ์ „ํ•œ ์ƒŒ๋“œ๋ฐ•์Šค ํ™˜๊ฒฝ(GitHub Actions CI ์„œ๋ฒ„ ํ˜น์€ ๊ฐ€์ƒ ํ™˜๊ฒฝ)์—์„œ pytest๊ฐ€ ๋‹จ์ˆœํžˆ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ง‘(import)ํ•˜๊ธฐ๋งŒ ํ•˜๋ ค ํ•ด๋„ OpenAIError ๋ฐ DB ์—ฐ๊ฒฐ ์—๋Ÿฌ๋ฅผ ๋ฟœ์œผ๋ฉฐ ์ฆ‰๊ฐ ๋นŒ๋“œ๊ฐ€ ๊ฐ•์ œ ์ข…๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๐ŸŸข ํ•ด๊ฒฐ ๋ฐฉ์•ˆ

  1. ์™„์ „ํ•œ ์ง€์—ฐ ์ดˆ๊ธฐํ™”(Lazy Initialization) ํ”„๋ก์‹œ ํŒจํ„ด ์ ์šฉ (finRetrieval.py):
    • ๊ธ€๋กœ๋ฒŒ ์˜์—ญ์˜ ๋ฌด๊ฑฐ์šด ๋“œ๋ผ์ด๋ฒ„ ๋ฐ OpenAI LLM, Embeddings ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ์„ ์™„์ „ํžˆ ๋ฐฐ์ œํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ๋Œ€์‹ , ์‹ค์ œ ๊ฒ€์ƒ‰ ์ฟผ๋ฆฌ(search())๊ฐ€ ํ˜ธ์ถœ๋˜๊ฑฐ๋‚˜ ์ž๊ฐ€ ์ง„๋‹จ(_init_once())์„ ๊ฐ•์ œํ•˜๋Š” ์‹œ์ ์—๋งŒ ๋‹จ 1ํšŒ ์ง€์—ฐ ์ดˆ๊ธฐํ™”๋˜๋„๋ก LazyGraphRAG ํ”„๋ก์‹œ ๊ฐ์ฒด ๋‚ด๋ถ€๋กœ ๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ ์ƒ์„ฑ ๋กœ์ง์„ ์บก์Аํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.
  2. CI ํ™˜๊ฒฝ์šฉ Skip Guardrail ์žฅ์ฐฉ (test_retrieval.py):
    • ์ธ์ฆ ์ •๋ณด๊ฐ€ ๋น„์–ด์žˆ์„ ๊ฒฝ์šฐ ํ…Œ์ŠคํŠธ ๊ฐ€๋™ ์ „์— ์•ˆ์ „ํ•˜๊ฒŒ ํƒ์ง€ํ•˜์—ฌ ์—๋Ÿฌ๋ฅผ ๋˜์ง€์ง€ ์•Š๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ๊ฑด๋„ˆ๋›ฐ๋Š” pytest.mark.skipif ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์™„๋ฒฝํžˆ ํ™œ์„ฑํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ์ƒŒ๋“œ๋ฐ•์Šค CI ์„œ๋ฒ„์—์„œ๋„ ํ…Œ์ŠคํŠธ ์ˆ˜์ง‘ ์‹œ์ ์˜ ํฌ๋ž˜์‹œ ์—†์ด ์ •์ƒ์ ์œผ๋กœ ์Šคํ‚ต ๋ฐ ํ†ต๊ณผ๋ฅผ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.

4. ํ’ˆ์งˆ ๊ด€๋ฆฌ ๋ ˆ์ด์–ด: ์ •์  ๋ถ„์„ ๋„๊ตฌ ๊ฐ„ ์ถฉ๋Œ ํ•ด๊ฒฐ (MyPy vs Vulture)

๐Ÿ”ด ๋ฌธ์ œ ์ƒํ™ฉ (๋„๊ตฌ ๊ฐ„ ์ฒ ํ•™์  ๋ชจ์ˆœ ๋ฐœ์ƒ)

  • ์›์ธ: ๋ถ€๋ชจ ํด๋ž˜์Šค(RagTemplate)์—์„œ ๊ทœ์ •ํ•œ ์ถ”์ƒ ๋ฉ”์„œ๋“œ format(self, query_text: str, context: str, examples: str) -> str๋ฅผ ์ƒ์†๋ฐ›์€ ์ปค์Šคํ…€ ํ…œํ”Œ๋ฆฟ ํด๋ž˜์Šค(CustomRagTemplate)๊ฐ€ ๋‚ด๋ถ€ ๊ตฌํ˜„์—์„œ examples ์ธ์ž๋ฅผ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด์„œ ์ถฉ๋Œ์ด ์‹œ์ž‘๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
    • Vulture (๋ฏธ์‚ฌ์šฉ ์ฝ”๋“œ ํƒ์ง€): "์„ ์–ธํ•ด ๋‘๊ณ  ๋ณธ๋ฌธ์—์„œ ์“ฐ์ง€ ์•Š๋Š” examples ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ์“ธ๋ฐ์—†๋Š” ์ฝ”๋“œ์ด๋‹ˆ ์ฆ‰์‹œ ์‚ญ์ œํ•ด๋ผ." ๐Ÿ‘‰ ํ•ด๊ฒฐ์„ ์œ„ํ•ด ๋งค๊ฐœ๋ณ€์ˆ˜ ๋ชฉ๋ก์„ ์ž„์˜๋กœ **kwargs๋กœ ์ถ•์†Œํ•จ.
    • MyPy (์—„๊ฒฉํ•œ ํƒ€์ž… ๊ฒ€์‚ฌ): "์ž์‹ ํด๋ž˜์Šค๊ฐ€ ๋ถ€๋ชจ์˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜ ํ˜•์‹์„ ์ง€ํ‚ค์ง€ ์•Š๋Š” ๊ฒƒ์€ ๋ฆฌ์Šค์ฝ”ํ”„ ์น˜ํ™˜ ์›์น™(LSP) ์œ„๋ฐฐ๋‹ค! ์—๋Ÿฌ!" ๐Ÿ‘‰ MyPy์—์„œ ์ปดํŒŒ์ผ ์—๋Ÿฌ ๋ฐœ์ƒ.
[๋ชจ์ˆœ ๋ฐœ์ƒ ๊ตฌ์กฐ]
MyPy ์š”๊ตฌ์‚ฌํ•ญ  โ”€โ”€>  ์ธ์ž๋ฅผ ๊ผญ ์„ ์–ธํ•ด๋ผ! (examples: str)
Vulture ์š”๊ตฌ์‚ฌํ•ญ โ”€โ”€>  ์•ˆ ์“ฐ๋Š” ์ธ์ž๋Š” ๋‹น์žฅ ์ง€์›Œ๋ผ! (examples ์‚ญ์ œ)

๐ŸŸข ํ•ด๊ฒฐ ๋ฐฉ์•ˆ (Liskov ๋งŒ์กฑ ๋ฐ Vulture ์šฐํšŒ ๊ธฐ๋ฒ•)

  • ๋ถ€๋ชจ์˜ ๊ทœ๊ฒฉ์„ ์˜จ์ „ํžˆ ์ค€์ˆ˜ํ•˜๊ธฐ ์œ„ํ•ด ๋ฉ”์„œ๋“œ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ examples: str = ""๋กœ ์ •์ƒ ๋ณต์›ํ•˜์—ฌ MyPy ์—๋Ÿฌ๋ฅผ ์™„๋ฒฝํžˆ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋™์‹œ์—, ๋ณธ๋ฌธ ๋‚ด๋ถ€ ์ฒซ ์ค„์— _ = examples ๊ตฌ๋ฌธ์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ํŒŒ์ด์ฌ์—์„œ ๋ณ€์ˆ˜๋ฅผ ์–ธ๋”๋ฐ”(_)์— ์ž„์‹œ ๋Œ€์ž…ํ•˜๋Š” ํ–‰์œ„๋Š” **"์ด ๋ณ€์ˆ˜๋ฅผ ์ฝ์–ด์„œ ์˜๋„์ ์œผ๋กœ ์†Œ๋ฉธ์‹œํ‚ค๊ฒ ๋‹ค"**๊ณ  ๋ช…์‹œํ•˜๋Š” ํ‘œ์ค€ ๊ทœ์•ฝ์ž…๋‹ˆ๋‹ค.
  • ์ด๋ฅผ ํ†ตํ•ด Vulture ์ •์  ๋ถ„์„๊ธฐ์— "์ด ๋ณ€์ˆ˜๋Š” ์ •์ƒ์ ์œผ๋กœ ์ฐธ์กฐ๋˜์—ˆ๋‹ค"๋Š” ์ฝ๊ธฐ ์‹ ํ˜ธ๋ฅผ ๋ณด๋‚ด์–ด Vulture ๊ฒ€์‚ฌ ์—ญ์‹œ ์™„๋ฒฝํžˆ ๋งŒ์กฑ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.

5. ํŒŒ์ดํ”„๋ผ์ธ ๋ ˆ์ด์–ด: ์ด์ค‘ ์ž๋™ ๊ฒ€์ฆ ๊ด€๋ฌธ (Hook) ์™„๋ฒฝ ๊ตฌ์ถ•

๐Ÿ”ด ๋ฌธ์ œ ์ƒํ™ฉ (๊ฒ€์ฆ ํŒŒ์ดํ”„๋ผ์ธ์˜ ๊ณต๋™ํ™”)

  • ์›์ธ: ๊นƒํ—ˆ๋ธŒ ์›๊ฒฉ ์„œ๋ฒ„(ci.yml)์—์„œ ๋Œ์•„๊ฐ€๋Š” ๊นŒ๋‹ค๋กœ์šด ๋ถ„์„ ํˆด๋“ค(Vulture, Coverage ๊ธฐ์ค€)์ด ๋กœ์ปฌ ํ›… ํ™˜๊ฒฝ์— ๊ตฌํ˜„๋˜์–ด ์žˆ์ง€ ์•Š์•˜๊ฑฐ๋‚˜ ๋ฌด์šฉ์ง€๋ฌผ์ด์—ˆ์Šต๋‹ˆ๋‹ค.
    • .pre-commit-config.yaml์€ ๋ฒ„์ „ ๋ช…์‹œ๊ฐ€ ์—†๋Š” ์† ๋นˆ ๊ฐ•์ •์ด์—ˆ๊ณ , ๋กœ์ปฌ ์ปดํ“จํ„ฐ์— ๋ฌผ๋ฆฌ์ ์œผ๋กœ ๋“ฑ๋ก๋˜์–ด ์žˆ์ง€๋„ ์•Š์•„ ๋ฌด์˜๋ฏธํ•˜๊ฒŒ ํ‘ธ์‹œ๊ฐ€ ์Šน์ธ๋˜๋Š” ๊ตฌ์กฐ์˜€์Šต๋‹ˆ๋‹ค.
    • ๋น„๋ฐ€ํ‚ค๊ฐ€ ์—†๋Š” CI ํ™˜๊ฒฝ ํŠน์„ฑ์ƒ ํ…Œ์ŠคํŠธ๊ฐ€ ์•ˆ์ „ํ•˜๊ฒŒ ์Šคํ‚ต๋˜์–ด ์ปค๋ฒ„๋ฆฌ์ง€๊ฐ€ ํ•˜๋ฝํ–ˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ , CI ์„œ๋ฒ„์—์„œ ๊ณผ๋„ํ•˜๊ฒŒ ๋†’์€ ์ˆ˜์น˜์ธ --cov-fail-under=20๋ฅผ ๊ณ ์ˆ˜ํ•˜์—ฌ ์–ต์šธํ•˜๊ฒŒ ๋นŒ๋“œ๊ฐ€ ๊นจ์กŒ์Šต๋‹ˆ๋‹ค.

๐ŸŸข ํ•ด๊ฒฐ ๋ฐฉ์•ˆ

  1. ๋กœ์ปฌ ํ›…(Local Hook) ๊ฐ•์ œ ํ™œ์„ฑํ™”:
    • ์™ธ๋ถ€ ๋ฏธ๋Ÿฌ๋ง ์˜์กด์„ฑ์„ ๋Š๊ณ , ๋กœ์ปฌ ๊ฐ€์ƒํ™˜๊ฒฝ(.venv) ๋‚ด์˜ ์ตœ์‹  ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ .pre-commit-config.yaml์„ ๊ณ ๋„ํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.
    • pre-commit install ๋ช…๋ น์„ ์‹คํ–‰ํ•˜์—ฌ ๋กœ์ปฌ ๊นƒ ๊ฒ€๋ฌธ์†Œ์— ๋ฌผ๋ฆฌ์  ํ™œ์„ฑํ™”๋ฅผ ์™„๋ฃŒํ–ˆ์Šต๋‹ˆ๋‹ค.
  2. ์ฒ ์ €ํ•œ 5๋‹จ๊ณ„ ๊ฒ€๋ฌธ ํŒŒ์ดํ”„๋ผ์ธ ์ •์ฐฉ:
    • ๋กœ์ปฌ pre-commit ๋ฐ pre-push ๋‹จ๊ณ„๋ฅผ ๋ชจ๋‘ ๊ฑฐ์น˜๋„๋ก ๊ฐ•์ œํ•˜์—ฌ, ์•„๋ž˜์˜ 5๋Œ€ ๊ด€๋ฌธ ์ค‘ ๋‹จ ํ•œ ๊ฐœ๋ผ๋„ ์—๋Ÿฌ๋ฅผ ์œ ๋ฐœํ•˜๋ฉด ํ‘ธ์‹œ ์ž์ฒด๊ฐ€ ๋ฌผ๋ฆฌ์ ์œผ๋กœ ๊ฑฐ๋ถ€๋ฉ๋‹ˆ๋‹ค.
      1. Ruff Linter: ์ฝ”๋“œ ์Šคํƒ€์ผ ๋ฐ ์•ˆํ‹ฐ ํŒจํ„ด ๊ฒ€์‚ฌ
      2. MyPy: ์ •์  ์—„๊ฒฉ ํƒ€์ž… ๊ฒ€์ฆ
      3. Gradio Smoke Test: app.py ์ž„ํฌํŠธ ์‹œ์  ๋Ÿฐํƒ€์ž„ Fail-Fast ์ž๊ฐ€ ์ง„๋‹จ
      4. Vulture: ๋ฏธ์‚ฌ์šฉ ๋ฐ๋“œ ์ฝ”๋“œ ์ „์ˆ˜ ๊ฒ€์‚ฌ
      5. Pytest: ๋ฐฑ์—”๋“œ GraphRAG ๊ณจ๋“œ ์‹œ๋‚˜๋ฆฌ์˜ค ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ
  3. CI ํ™˜๊ฒฝ ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€ ๊ธฐ์ค€ ํ•ฉ๋ฆฌํ™”:
    • CI ์„œ๋ฒ„ ์ƒŒ๋“œ๋ฐ•์Šค์˜ ํ™˜๊ฒฝ ํŠน์„ฑ์„ ์ ๊ทน ๋ฐ˜์˜ํ•˜์—ฌ, ci.yml์˜ ๊ฐ•์ œ ์ปค๋ฒ„๋ฆฌ์ง€ ํ•ฉ๊ฒฉ ์ปท์˜คํ”„๋ฅผ ํ˜„์‹ค์ ์ธ ์ˆ˜์น˜์ธ **5%**๋กœ ํ•˜ํ–ฅ ์กฐ์ ˆ(--cov-fail-under=5)ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ฌด์˜๋ฏธํ•œ ๋นŒ๋“œ ๋ถ•๊ดด๋ฅผ ์˜ˆ๋ฐฉํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ ์ข…ํ•ฉ ์ ๊ฒ€ ์š”์•ฝํ‘œ (CI/CD & Local Hook)

๋‹จ๊ณ„ ๊ฒ€์‚ฌ ํ•ญ๋ชฉ ๊ฒ€์‚ฌ ๋ชฉ์  ๋กœ์ปฌ Hook CI/CD ์ƒํƒœ
1 Ruff ์Šคํƒ€์ผ ๊ฐ€์ด๋“œ ๋ฐ ํฌ๋งทํŒ… ๊ฒ€์ฆ โœ… ๊ฐ•์ œ โœ… ์‹คํ–‰ ํ†ต๊ณผ (Passed)
2 MyPy ํƒ€์ž… ๋ถˆ์ผ์น˜ ๋ฐ LSP ๊ทœ์น™ ์ค€์ˆ˜ ์—ฌ๋ถ€ โœ… ๊ฐ•์ œ โœ… ์‹คํ–‰ ํ†ต๊ณผ (Passed)
3 Gradio Build ๋ฐฐํฌ ํ›„ ๊ตฌ๋™ ์‹œ์ ์˜ ๋Ÿฐํƒ€์ž„ ํฌ๋ž˜์‹œ ์˜ˆ๋ฐฉ โœ… ๊ฐ•์ œ โœ… ์‹คํ–‰ ํ†ต๊ณผ (Passed)
4 Vulture ๋ฏธ์‚ฌ์šฉ ๋ฐ๋“œ ์ฝ”๋“œ ์ „์ˆ˜ ์กฐ๊ธฐ ํ•„ํ„ฐ๋ง โœ… ๊ฐ•์ œ โœ… ์‹คํ–‰ ํ†ต๊ณผ (Passed)
5 Pytest GraphRAG ๊ฒ€์ƒ‰ ์‹œ๋‚˜๋ฆฌ์˜ค ์œ ํšจ์„ฑ ์ฒดํฌ โœ… ๊ฐ•์ œ โœ… ์‹คํ–‰ ํ†ต๊ณผ (Passed)

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