| # ๐ธ๏ธ 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)** | |
|
|
| > [!IMPORTANT] |
| > **๊ฐ๋ฐ์ ์์ฝ ๋ฐ ๋ฐฉ์ด๋ฒฝ ์ ์ธ**: |
| > ์ด์ ์ด๋ ํ ๋ถ์ ํฉํ ์ฝ๋๋, ๋ฐํ์ ํฌ๋์ ๊ฐ๋ฅ์ฑ์ด ์๋ ์ค์ ํ์ผ, ํน์ ๋ฏธ์ฌ์ฉ ๋ฐฉ์น ์ฝ๋๋ **์ด์ค ๋ก์ปฌ ๊ด๋ฌธ(Hook)**์ ์ํด ์๊ฒฉ ๊นํ๋ธ ์ ์ฅ์ ๊ทผ์ฒ์๋ ๊ฐ์ง ๋ชปํ๊ณ ๋ก์ปฌ์์ ์ฆ์ ๊ฒฉ๋ฆฌ ๋ฐ ๋ฐ๋ ค๋ฉ๋๋ค. ์์ ํ๊ณ ๊นจ๋ํ๋ฉฐ ์ ๋ขฐํ ์ ์๋ FinGraph ์ํคํ
์ฒ๊ฐ ์์ ํ ๊ตฌ์ถ๋์์ต๋๋ค. |
|
|