๐ธ๏ธ FinGraph ํ๋ก์ ํธ ์ข ํฉ ๊ธฐ์ ๊ฐ์ ๋ฐ ๊ฒ์ฆ ์๋ํ ๋ณด๊ณ ์ (Technical Report)
๋ณธ ๋ณด๊ณ ์๋ FinGraph ํ๋ก์ ํธ์ ์์ ์ ์ด๊ณ ์ง์ ๊ฐ๋ฅํ ์ด์์ ์ํด ๊ทธ๋์ ๋ฐ์ํ๋ ๋ฐ์ดํฐ ๊ณต๊ธ ๋ถ์กฑ, ๋ฐํ์ ํฌ๋์, CI/CD ๋ฐฐํฌ ๋ณ๋ชฉ, ๊ทธ๋ฆฌ๊ณ ์ ์ ๋ถ์ ๋๊ตฌ ๊ฐ์ ๊ธฐ์ ์ ์ถฉ๋ ๋ฌธ์ ๋ค์ ์ฌ๋ ์๊ฒ ๋ถ์ํ๊ณ ํด๊ฒฐํ ๊ณผ์ ์ ๊ธฐ์ ์ ์ผ๋ก ์ ๋ฆฌํ ๋ฌธ์์ ๋๋ค.
1. ๋ฐ์ดํฐ ๋ ์ด์ด: ๋ด์ค ๊ณต๊ธ ํฌ์์ฑ ๋ฐ ์ง์ ๊ทธ๋ํ ๋ฐ๋ ๊ฐ์
๐ด ๋ฌธ์ ์ํฉ (๋ฐ์ดํฐ ์ ๋ ๋ถ์กฑ)
- ์์ธ: ์ด๊ธฐ ๊ธฐํ ๋จ๊ณ์์ ์์ง๋ ๋ด์ค ๋ฐ์ดํฐ๊ฐ ์ ๋์ ์ผ๋ก ๋ถ์กฑํ์ฌ Neo4j ์ง์ ๊ทธ๋ํ์ ๋ฐ๋๊ฐ ๋งค์ฐ ๋ฎ์์ต๋๋ค.
- ์ํฅ: GraphRAG ๊ฒ์์ ์คํํ์ ๋ ๊ด๋ จ ๋๋ฉ์ธ ์ํฐํฐ(๊ธฐ์ , ๊ธฐ์ , ์๋น์ค) ๊ฐ์ ์ฐ๊ฒฐ ๊ณ ๋ฆฌ๊ฐ ๋์ด์ ธ RAG ์ฑ๋ฅ์ด ๊ธ๊ฒฉํ ์ ํ๋๊ฑฐ๋ ํ๊ฐ(Hallucination) ํ์์ด ์ ๋๋์์ต๋๋ค.
๐ข ํด๊ฒฐ ๋ฐฉ์ ๋ฐ ์ฑ๊ณผ
- ํฌ๋กค๋ฌ ๋๋ฉ์ธ ์ค์ผ์ผ์
(
finScrapping.py):- ๋ด์ค ์์ง ๋ฒ์ ๋ฐ ํค์๋ ํํฐ๋ง ๋ก์ง์ ์ ๊ตํํ์ฌ ๊ธ์ตAI ํธ๋ ๋์ ์ ํํ ๋ถํฉํ๋ ๊ณ ํ์ง์ ๋ด์ค ๊ธฐ์ฌ ์ด 74๊ฑด์ ๋๋์ผ๋ก ์ถ๊ฐ ์์งํ์ต๋๋ค.
- ์ด๊ณ ๋ฐ๋ ์ง์ ๊ทธ๋ํ ๊ตฌ์ถ (
finGraph.py):- ์ถ๊ฐ ์์ง๋ ๋ด์ค ๋ฐ์ดํฐ๋ฅผ ํ์ฉํด Neo4j ์ ์ฌ ํ์ดํ๋ผ์ธ์ ๊ตฌ๋ํ์ฌ **์ด 296๊ฐ์ ๋ ธ๋(Node)์ 346๊ฐ์ ๊ด๊ณ์ (Edge)**์ผ๋ก ํ์ฅํ์ต๋๋ค.
- ์ด๋ฅผ ํตํด ์ํฐํฐ ๊ฐ์ ์ํ์ ๊ตฌ์กฐ(Milky-Way Schema)๋ฅผ ์ด๋ฃจ๋ ๊ณ ๋ฐ๋ ๊ทธ๋ํ๋ฅผ ์์ฑํ์ฌ ๋ค๊ฐ๋ ๊ทธ๋ํ ํ์์ด ๊ฐ๋ฅํด์ก์ต๋๋ค.
- ์ ์ฌ ์ฑ๋ฅ ์ต์ ํ (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์์ฑ์์ ์ธ์์์ ์ง์ ์ค๋จ๋์ด ์ด๊ธฐํ ์คํจ ํฌ๋์๊ฐ ๋ฐ์ํ์ต๋๋ค.
๐ข ํด๊ฒฐ ๋ฐฉ์
- ๋จ์ผ ์ํคํ
์ฒ ํตํฉ ๋ฆฌํฉํ ๋ง (
app.py):- ๋ถํ์ํ๊ฒ ์ค์ฒฉ๋์ด ์ถฉ๋์ ์ ๋ฐํ๋
gr.Blocks()๊ตฌ์กฐ๋ฅผ ์ ๊ฑฐํ๊ณ , ์ต์ ํ๋ ๋จ์ผgr.ChatInterface์ค์ฌ์ ์ํคํ ์ฒ๋ก UI๋ฅผ ์ฌ๋ฆผํํ์ต๋๋ค.
- ๋ถํ์ํ๊ฒ ์ค์ฒฉ๋์ด ์ถฉ๋์ ์ ๋ฐํ๋
- ๋ฐํ์ ํ๋ผ๋ฏธํฐ ์์น ๋๊ธฐํ:
- ์์ฑ์์์ ๊ฑฐ๋ถ๋๋
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 ์ฐ๊ฒฐ ์๋ฌ๋ฅผ ๋ฟ์ผ๋ฉฐ ์ฆ๊ฐ ๋น๋๊ฐ ๊ฐ์ ์ข ๋ฃ๋์์ต๋๋ค.
๐ข ํด๊ฒฐ ๋ฐฉ์
- ์์ ํ ์ง์ฐ ์ด๊ธฐํ(Lazy Initialization) ํ๋ก์ ํจํด ์ ์ฉ (
finRetrieval.py):- ๊ธ๋ก๋ฒ ์์ญ์ ๋ฌด๊ฑฐ์ด ๋๋ผ์ด๋ฒ ๋ฐ OpenAI LLM, Embeddings ์ธ์คํด์ค ์์ฑ์ ์์ ํ ๋ฐฐ์ ํ์ต๋๋ค.
- ๋์ , ์ค์ ๊ฒ์ ์ฟผ๋ฆฌ(
search())๊ฐ ํธ์ถ๋๊ฑฐ๋ ์๊ฐ ์ง๋จ(_init_once())์ ๊ฐ์ ํ๋ ์์ ์๋ง ๋จ 1ํ ์ง์ฐ ์ด๊ธฐํ๋๋๋กLazyGraphRAGํ๋ก์ ๊ฐ์ฒด ๋ด๋ถ๋ก ๋ชจ๋ ํด๋ผ์ด์ธํธ ์์ฑ ๋ก์ง์ ์บก์ํํ์ต๋๋ค.
- 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์์ ์ปดํ์ผ ์๋ฌ ๋ฐ์.
- Vulture (๋ฏธ์ฌ์ฉ ์ฝ๋ ํ์ง): "์ ์ธํด ๋๊ณ ๋ณธ๋ฌธ์์ ์ฐ์ง ์๋
[๋ชจ์ ๋ฐ์ ๊ตฌ์กฐ]
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๋ฅผ ๊ณ ์ํ์ฌ ์ต์ธํ๊ฒ ๋น๋๊ฐ ๊นจ์ก์ต๋๋ค.
๐ข ํด๊ฒฐ ๋ฐฉ์
- ๋ก์ปฌ ํ
(Local Hook) ๊ฐ์ ํ์ฑํ:
- ์ธ๋ถ ๋ฏธ๋ฌ๋ง ์์กด์ฑ์ ๋๊ณ , ๋ก์ปฌ ๊ฐ์ํ๊ฒฝ(
.venv) ๋ด์ ์ต์ ๋ฐ์ด๋๋ฆฌ๋ฅผ ์ง์ ํธ์ถํ๋ ๋ฐฉ์์ผ๋ก.pre-commit-config.yaml์ ๊ณ ๋ํํ์ต๋๋ค. pre-commit install๋ช ๋ น์ ์คํํ์ฌ ๋ก์ปฌ ๊น ๊ฒ๋ฌธ์์ ๋ฌผ๋ฆฌ์ ํ์ฑํ๋ฅผ ์๋ฃํ์ต๋๋ค.
- ์ธ๋ถ ๋ฏธ๋ฌ๋ง ์์กด์ฑ์ ๋๊ณ , ๋ก์ปฌ ๊ฐ์ํ๊ฒฝ(
- ์ฒ ์ ํ 5๋จ๊ณ ๊ฒ๋ฌธ ํ์ดํ๋ผ์ธ ์ ์ฐฉ:
- ๋ก์ปฌ
pre-commit๋ฐpre-push๋จ๊ณ๋ฅผ ๋ชจ๋ ๊ฑฐ์น๋๋ก ๊ฐ์ ํ์ฌ, ์๋์ 5๋ ๊ด๋ฌธ ์ค ๋จ ํ ๊ฐ๋ผ๋ ์๋ฌ๋ฅผ ์ ๋ฐํ๋ฉด ํธ์ ์์ฒด๊ฐ ๋ฌผ๋ฆฌ์ ์ผ๋ก ๊ฑฐ๋ถ๋ฉ๋๋ค.- Ruff Linter: ์ฝ๋ ์คํ์ผ ๋ฐ ์ํฐ ํจํด ๊ฒ์ฌ
- MyPy: ์ ์ ์๊ฒฉ ํ์ ๊ฒ์ฆ
- Gradio Smoke Test: app.py ์ํฌํธ ์์ ๋ฐํ์ Fail-Fast ์๊ฐ ์ง๋จ
- Vulture: ๋ฏธ์ฌ์ฉ ๋ฐ๋ ์ฝ๋ ์ ์ ๊ฒ์ฌ
- Pytest: ๋ฐฑ์๋ GraphRAG ๊ณจ๋ ์๋๋ฆฌ์ค ํตํฉ ํ ์คํธ
- ๋ก์ปฌ
- CI ํ๊ฒฝ ํ
์คํธ ์ปค๋ฒ๋ฆฌ์ง ๊ธฐ์ค ํฉ๋ฆฌํ:
- CI ์๋ฒ ์๋๋ฐ์ค์ ํ๊ฒฝ ํน์ฑ์ ์ ๊ทน ๋ฐ์ํ์ฌ,
ci.yml์ ๊ฐ์ ์ปค๋ฒ๋ฆฌ์ง ํฉ๊ฒฉ ์ปท์คํ๋ฅผ ํ์ค์ ์ธ ์์น์ธ **5%**๋ก ํํฅ ์กฐ์ (--cov-fail-under=5)ํ์ต๋๋ค. ์ด๋ฅผ ํตํด ๋ฌด์๋ฏธํ ๋น๋ ๋ถ๊ดด๋ฅผ ์๋ฐฉํ์ต๋๋ค.
- CI ์๋ฒ ์๋๋ฐ์ค์ ํ๊ฒฝ ํน์ฑ์ ์ ๊ทน ๋ฐ์ํ์ฌ,
๐ ์ข ํฉ ์ ๊ฒ ์์ฝํ (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 ์ํคํ ์ฒ๊ฐ ์์ ํ ๊ตฌ์ถ๋์์ต๋๋ค.