Backend service dedicated to RAG chatbot functionalities using Langchain and OpenAI API
๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ
โโโ src
โโโ app
โ โโโ __init__.py
โ โโโ config.py
โ โโโ virtual_db.py // ๋ฐฑ ์ฐ๋์ฉ ๊ฐ์ db ์ค์ฒฉ ๊ฐ์ฒด
โโโ sql_executor.py
โโโ sql_queries.py
โ โโโ service // ํด๋ - ์ง์ธ
โ โโโ preprocess // ํด๋ - ์น์ฒ
โโโ data
โ โโโ db - ๋ฐฑ์๋ ์ฐ๋ ์ ์ ํ
์คํธ ์ํด ์ฌ์ฉํ ๊ฐ์ db (๋์๋์ด ์
๋ฐ์ดํธํ๋ db์ ๋ง์ถฐ์ ๋ณ๊ฒฝํ๊ธฐ)
โ โ โโโ admin.json
โ โ โโโ flight.json
โ โ โโโ hotel.json
โ โ โโโ querylog.json
โ โ โโโ reservation.json
โ โ โโโ user.json
โ โโโ processed
โ โโโ raw
โ โโโ // ๋น์ ํ ๋ฐ์ดํฐ ์ถ๊ฐ
โโโ evaluations ํด๋
โโโ main_preprocess.py // ์ ์ฒ๋ฆฌ ๋ฉ์ธ ํ๋ก์ฐ ์คํ ํด๋
โโโ requirements.txt (์ฌ๊ธฐ๋ค๊ฐ ์คํ ์ ํ์ํ ํจํค์ง ๋ชฉ๋ก๋ค ์ถ๊ฐํ๊ธฐ)
โโโ main_service.py
โโโ tests ํด๋
โโโ
-- .env // ๊น์ ์ฌ๋ฆฌ์ง ์์ ์์ - ๊ฐ์ ๋ก์ปฌ์์ ์์ฑํด์ ํ ํฐ ๋ณ์ ์ ์ฅํ๊ธฐ
- preprocess ํด๋
- ์๋ฒ ๋ฉ, ๋ฒกํฐ์คํ ์ด ์์ฑ, ์ฒญํน, ๋ฌธ์ ๋ก๋๋ฑ ์ ์ฒ๋ฆฌ ๋จ๊ณ๋ฅผ ๋ด๋น
- service ํด๋
- ์ฒด์ธ, ํ๋กฌํํธ, ๋ฆฌํธ๋ฆฌ๋ฒ ๋ฑ์ ๊ฒ์ ์์ญ
- init.py(๊ณต์ฉ)
- ์ญํ :ย
appย ํจํค์ง๋ฅผ ์ด๊ธฐํํฉ๋๋ค. - ํน์ง:ย ๋ค๋ฅธ ๋ชจ๋์์ ์๋ ๊ฒฝ๋ก๋ก ๋ถ๋ฌ์ฌ ์ ์๋๋ก ํจํค์ง๋ก ์ธ์์ํต๋๋ค.
- ์ญํ :ย
- config.py(๊ณต์ฉ)
- ์ญํ :ย ํ๊ฒฝ๋ณ์ ๋ฐ ์์ ๊ฐ๋ค์ ์ค์ ํฉ๋๋ค.
- ์ฃผ์ ๋ด์ฉ:
.envย ํ์ผ์ ํตํดยOPENAI_API_KEY,ยVIRTUAL_DB_DIR,ยMODEL_NAMEย ๋ฑ์ ์ค์ ์ ๋ก๋ํฉ๋๋ค.
- sql_executor.py (๊ณต์ฉ)
- ์ญํ :ย ๊ฐ์ DB ๋ฐ์ดํฐ์์ SQL ์ฟผ๋ฆฌ ์คํ์ ํ๋ด ๋ด๋(pseudo) ๋ก์ง์ ๊ตฌํํฉ๋๋ค.
- ์ฃผ์ ํจ์:
pseudo_execute_sql(query_key, db_data)- query_key์ ๋ฐ๋ผ DB์ ํธํ , ํญ๊ณตํธ, ์์ฝ ๋ฑ์ ๋ฐ์ดํฐ๋ฅผ ํํฐ๋งํ์ฌ ๋ฐํํฉ๋๋ค.
- sql_queries.py (๊ณต์ฉ)
- ์ญํ :ย ์ง๋ฌธ ๋ถ๋ฅ์ ๋ฐ๋ฅธ SQL ์ฟผ๋ฆฌ ํค ๋งคํ ์ ๋ณด๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ์ฃผ์ ๋ด์ฉ:
sql_query_mapย ๋์ ๋๋ฆฌ- ์:ย
"available_rooms": "SHOW_AVAILABLE_ROOMS",ย"customer_reservation": "SHOW_CUSTOMER_RESERVATION"ย ๋ฑ.
- ์:ย
- virtual_db.py (๊ณต์ฉ)
- ์ญํ :ย JSON ํ์ผ๋ค์ ๋ก๋ํ์ฌ ๊ฐ์ DB๋ฅผ ๊ตฌ์ฑํฉ๋๋ค.
- ์ฃผ์ ํจ์:
load_json_file(filename: str)- ๊ฐ๋ณ JSON ํ์ผ์ ์ฝ์ด ํ์ด์ฌ ๊ฐ์ฒด๋ก ๋ฐํํฉ๋๋ค.
load_virtual_db()- ์ฌ๋ฌ JSON ํ์ผ์ ์กฐํฉํ์ฌ ๊ฐ์ DB(Hotel, Flight, Reservation, User ๋ฑ)๋ฅผ ์์ฑํฉ๋๋ค.
-
db ํด๋
- ์ญํ :ย ๊ฐ์ DB ๋ฐ์ดํฐ ํ์ผ๋ค์ด ์์นํฉ๋๋ค.
- ๋์๋์ด ์ค๊ณํ ์คํค๋ง ์ฐธ๊ณ ํ์์ต๋๋ค.
- ์ฃผ์ ํ์ผ:
admin.json: ์์คํ ๊ด๋ฆฌ์ ๊ด๋ จ ์ค์ flight.json: ํญ๊ณตํธ ์ ๋ณดhotel.json: ํธํ ์ ๋ณดquerylog.json: ์ฌ์ฉ์ ์ง์ ๊ธฐ๋กreservation.json: ์์ฝ ๋ด์ญ ์ ๋ณดuser.json: ์ฌ์ฉ์ ์ ๋ณด
-
processed ํด๋
- ์ญํ :ย ์ ์ฒ๋ฆฌ ํ ์ฒ๋ฆฌ๋ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ ๋๋ ํ ๋ฆฌ
-
raw ํด๋
- ์ญํ :ย ์๋ณธ ๋ฌธ์ ํ์ผ๋ค์ด ์์นํฉ๋๋ค.
- ํธํ ์์ฝ ๊ด๋ จ PDF ๋ฌธ์ ๋ฑ
- experiments ํด๋ (๊ณต์ฉ)
- ์ญํ :ย ๋ค์ํ ์คํ์ ์๋๋ ํ๋กํ ํ์ ์ฝ๋๊ฐ ์์นํ๋ ๋๋ ํ ๋ฆฌ์ ๋๋ค.
- main_preprocess.py
- ์ญํ :ย PDF ๋ฌธ์๋ฅผ ๋ก๋, ํ ์คํธ ์ฒญํฌ ๋ถํ , ์๋ฒ ๋ฉ ์์ฑ ๋ฐ ๋ฒกํฐ์คํ ์ด ์ ์ฅ ๊ณผ์ ์ ์คํํ๋ ์ ์ฒ๋ฆฌ ํ์ดํ๋ผ์ธ์ ๊ตฌํํฉ๋๋ค. - ์ ์ฒ๋ฆฌ ๋ฉ์ธ ํ๋ก์ฐ ์คํ ํ์ผ
- requirements.txt (๊ณต์ฉ)
- ์ญํ :ย ํ๋ก์ ํธ์ ํ์ํ Python ํจํค์ง๋ค์ ๋ชฉ๋ก์ ์ ๊ณตํฉ๋๋ค.
- main_service.py
- ์ญํ :ย ์ ์ฒ๋ฆฌ๋ ๋ฒกํฐ์คํ ์ด๋ฅผ ๋ก๋ํ๊ณ , retriever์ ์ฒด์ธ๋ค์ ์์ฑํ์ฌ ์ฌ์ฉ์ ์ง๋ฌธ์ ์ฒ๋ฆฌํ๊ณ ์ต์ข ๋ต๋ณ์ ์ถ๋ ฅํ๋ ๋ฉ์ธ ์คํ ์คํฌ๋ฆฝํธ์ ๋๋ค. - ๊ฒ์ ๋ฉ์ธ ํ๋ก์ฐ ์คํ ํ์ผ
- tests (๊ณต์ฉ)
- ์ญํ :ย ๊ฐ ๋ชจ๋์ ๋ํ ๋จ์ ํ ์คํธ๋ฅผ ํฌํจํฉ๋๋ค.
- ํ
์คํธ ์ผ์ด์ค ์์
- PDF ํ์ผ ๋ก๋ฉ ํ ์คํธ
- ํ ์คํธ ๋ถํ ํ ์คํธ
- ๊ฐ์ DB์ JSON ํ์ผ ๋ก๋ ๋ฐ ํ์ ํค ๊ฒ์ฆ
- ํ์ด์ฌ ๊ฐ์ํ๊ฒฝ ์์ฑ ๋ฐ ํ์ฑํ
-
๊ฐ์ํ๊ฒฝ ์์ฑ
python -m venv env
-
๊ฐ์ํ๊ฒฝ ํ์ฑํ
-
Windows
env\Scripts\activate
-
macOS/Linux
source env/bin/activate
-
-
ํจํค์ง ์ค์น
-
๊ฐ์ํ๊ฒฝ์ด ํ์ฑํ๋ ์ํ์์ cd src๋ก ๋๋ ํ ๋ฆฌ ์ด๋ ํ, ์๋ ๋ช ๋ น์ด๋ก ํจํค์ง๋ฅผ ํ ๋ฒ์ ์ค์นํฉ๋๋ค.
pip install -r requirements.txt // ํจํค์ง ์ญ์ pip uninstall -r requirements.txt -y์ฐธ๊ณ ) numpy ๋ฒ์ ์ถฉ๋ ๋๋ฌธ์, numpy๋ ๋ณ๋๋ก ๋ฒ์ ์ ์ง์ ํ์ฌ numpy==1.26.2๋ฅผ ์ถ๊ฐํ์์ต๋๋ค.
ํ์
- langchain
- python-dotenv
- langchain_community
- langchain-openai
- numpy==1.26.2
- langsmith
- openai
๋ณ๋ ๊ฐ๋ฅ
- huggingface_hub (์์๋ก ํ๊น ํ์ด์ ์๋ฒ ๋ฉ ์ง์ ํด์ ์ถ๊ฐํจ)
- pypdf
- faiss-cpu
- langchain-text
- langchain-huggingface
-
-
ํ๊ฒฝ๋ณ์ ์ค์
- ํ๋ก์ ํธ ๋ฃจํธ(backed-ai/.env)์ย
.envย ํ์ผ์ ์์ฑํ๊ณ ,ยOPENAI_API_KEYย ๋ฐย๋ญ์ค๋ฏธ์ค ํค,VIRTUAL_DB_DIRย ๋ฑ ํ์ํ ํ๊ฒฝ๋ณ์๋ฅผ ์ค์ ํฉ๋๋ค.
.env ๊ตฌ์ฑ ์์ : ์ด์ค์์ ํ์ํ ๋ถ๋ถ๋ง ์ถ๊ฐํ๋ฉด ๋ ๋ฏ
- ํ๋ก์ ํธ ๋ฃจํธ(backed-ai/.env)์ย
-
์ ์ฒ๋ฆฌ
-
์ ์ฒ๋ฆฌ ๋ฉ์ธ ํ๋ก์ฐ ์คํ
python main_preprocess.py
-
-
์์คํ ์คํ
-
์๋น์ค ๋ฉ์ธ ํ๋ก์ฐ ์คํ
python main_service.py
-
ํ ํ๋ก์ ํธ ํ์ ์ ์ํด, ์ฝ๋ฉ์์ jinjja-fun-trip ์กฐ์ง์ backend-ai ๋ฆฌํฌ์งํ ๋ฆฌ๋ฅผ ํด๋ก (clone)ํ๊ณ , ์ํ๋ ๋ธ๋์น์ ์ฝ๋ ์ปค๋ฐ ๋ฐ ํธ์ฌํ๋ ๋ฐฉ๋ฒ์ ๋ํ ๋ฆฌ๋๋ฏธ์ ๋๋ค.
-
ํด๋ก ํ ๋ฆฌํฌ์งํ ๋ฆฌ ๋ด ํ์ผ(backend-ai ํด๋ ๋ด ํ์ผ)
์ค์ ์ฝ๋ ์์ฑ ๋ฐ ์์ ์ ํด๋ก ํ backend-ai ๋ฆฌํฌ์งํ ๋ฆฌ ๋ด๋ถ์ ํ์ผ์์ ์งํํฉ๋๋ค. ๊ฐ ํ์์ ์์ ์ ๊ธฐ๋ฅ์ด๋ ๋ฒ๊ทธ ์์ ์ ์ํด ํด๋น ๋ฆฌํฌ์งํ ๋ฆฌ ๋ด์์ ์์ ํ๊ฒ ๋ฉ๋๋ค.
-
์ฐ๋์ฉ ์ฝ๋ฉํ์ผ (backend-ai ํด๋ ๋ด ํ์ผ์ด ์๋ ์ฐ๋์ ์ํ ๋ณ๋์ ํ์ผ)
์๋ ์ฝ๋๋ ์๊ฐ๋
๊นํ๋ธ๋ ํฌ์งํ ๋ฆฌ์ฐ๋์ฉ.ipynbํ์ผ์ ๋ฆฌํฌ์งํ ๋ฆฌ์์ ์ฐ๋ ๋ฐ ํ๊ฒฝ ์ค์ ์ ์ํ ๋ณ๋์ ํ์ผ์ ๋๋ค. ์ด ํ์ผ์ ํตํด ๋ฆฌํฌ์งํ ๋ฆฌ ํด๋ก , ์ปค๋ฐ, ํธ์ฌ ๋ฐ Pull ์์ ์ ์ฝ๊ฒ ์ํํ ์ ์์ต๋๋ค.์ด ํ์ผ์ ์ฝ๋ ์์ ์ ์ํ ์ฃผ์ ํ์ผ์ด ์๋๋ฏ๋ก, ์์ ์์ ์ ํด๋ก ํ ๋ฆฌํฌ์งํ ๋ฆฌ์์ ์งํํด ์ฃผ์ธ์.
- Google ๊ณ์ ๋ฐ Google Drive
- ์ฝ๋ฉ ์ฌ์ฉ์ ์ํด Google Drive๋ฅผ ๋ง์ดํธํ ์ ์์ด์ผ ํฉ๋๋ค.
- GitHub ๊ณ์
- ๋ฆฌํฌ์งํ ๋ฆฌ ์ ๊ทผ ๋ฐ ์ฝ๋ ํธ์ฌ/ํ์ ์ํด GitHub ๊ณ์ ์ด ํ์ํฉ๋๋ค.
- GitHub Personal Access Token ๋ฐ๊ธ
- ์์ ํ ์ธ์ฆ์ ์ํด ๊ฐ์ธ ๊นํ๋ธ ํ ํฐ์ด ํ์ํ๋ฉฐ, ์ด๋ฅผ ํ๊ฒฝ๋ณ์๋ก ๊ด๋ฆฌํฉ๋๋ค.
- GitHub์ ๋ก๊ทธ์ธํ ํ, ์ฐ์ธก ์๋จ์ ํ๋กํ ์์ด์ฝ์ ํด๋ฆญํ๊ณ Settings๋ก ์ด๋ํฉ๋๋ค.
- ์ข์ธก ๋ฉ๋ด์์ Developer settings๋ฅผ ์ ํํ ํ, Personal access tokens > Tokens (classic) ๋ฉ๋ด๋ก ์ด๋ํฉ๋๋ค.
- Generate new token ๋ฒํผ์ ํด๋ฆญํ์ฌ ์ ํ ํฐ์ ์์ฑํฉ๋๋ค.
- ํ ํฐ์ ์ฌ์ฉํ Note์ Expiration์ ์ค์ ํ๊ณ , ํ์ํ scope (์:
repoโ ๋ฆฌํฌ์งํ ๋ฆฌ ์ ๊ทผ ๊ถํ)๋ฅผ ์ ํํฉ๋๋ค. (ํ๋ก์ ํธ ๊ธฐ๊ฐ๋์์ ์ฌ์ฉํ ์ ์๋๋ก ์ ํจ ๊ธฐ๊ฐ์ ์ค์ ํฉ๋๋ค. ) - Generate token์ ํด๋ฆญํ๋ฉด, ์๋ก ๋ฐ๊ธ๋ ํ ํฐ์ ๋ณต์ฌํ ์ ์์ต๋๋ค.
๋ณธ์ธ์ GitHub ์ ๋ณด๋ฅผ ์ ๋ ฅํ ์ ์๋๋ก ์๋์ ๊ฐ์ด ํ๊ฒฝ ๋ณ์๋ฅผ ๊ด๋ฆฌํฉ๋๋ค.
์ ์ ๋ค์ ์ฝ๋๋ฅผ ์ถ๊ฐํ์ฌ ํ๊ฒฝ๋ณ์๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค.
import os
os.environ["GIT_TOKEN"] = "๋ฐ๊ธ๋ฐ์_ํ ํฐ๊ฐ์_์ฌ๊ธฐ์_์
๋ ฅ"
os.environ["GIT_USERNAME"] = "๋ณธ์ธ๊นํ๋ธ์์ด๋"
os.environ["GIT_EMAIL"] = "๋ณธ์ธ์ด๋ฉ์ผ@example.com"์ฝ๋ฉ ๋ ธํธ๋ถ ๋ด์์ ์ค์ ํ ํ๊ฒฝ๋ณ์๋ฅผ ๊ฐ์ ธ์ Git ์ค์ ์ ์ฌ์ฉํฉ๋๋ค.
# ํ๊ฒฝ๋ณ์์์ ๊ฐ์ ธ์ค๊ธฐ
GIT_TOKEN = os.getenv("GIT_TOKEN")
GIT_USERNAME = os.getenv("GIT_USERNAME")
GIT_EMAIL = os.getenv("GIT_EMAIL")
# Git ์ฌ์ฉ์ ์ ๋ณด ์ค์
!git config --global user.email "{GIT_EMAIL}"
!git config --global user.name "{GIT_USERNAME}"Google Drive๋ฅผ ๋ง์ดํธํ๊ณ ์ง์ ํ ๊ฒฝ๋ก์ ๋ฆฌํฌ์งํ ๋ฆฌ๋ฅผ ํด๋ก ํ๋ ์ ์ฒด ๊ณผ์ ์ ํด๋นํฉ๋๋ค.
# Google Drive ๋ง์ดํธ
from google.colab import drive
ROOT = "/gdrive"
drive.mount(ROOT)โ Mounted at /gdrive
# Google Drive ๋ด์ ํ๋ก์ ํธ ๊ฒฝ๋ก ์ค์
from os.path import join
MY_GOOGLE_DRIVE_PATH = 'My Drive/Colab Notebooks/'
PROJECT_PATH = join(ROOT, MY_GOOGLE_DRIVE_PATH)
print("PROJECT_PATH: ", PROJECT_PATH)โ PROJECT_PATH: /gdrive/My Drive/Colab Notebooks/
# GitHub ๋ฆฌํฌ์งํ ๋ฆฌ ์ ๋ณด ์ค์
GIT_ORGANIZATION = "jinjja-fun-trip"
GIT_REPOSITORY = "backend-ai"
# Git ํด๋ก ์ฉ URL ์์ฑ
GIT_PATH = "https://" + GIT_TOKEN + "@github.com/" + GIT_ORGANIZATION + "/" + GIT_REPOSITORY + ".git"
print("GIT_PATH: ", GIT_PATH)# ํ๋ก์ ํธ ๊ฒฝ๋ก๋ก ์ด๋
%cd "{PROJECT_PATH}"โ /gdrive/My Drive/Colab Notebooks
# ์ด๋ํ ํด๋น ๊ฒฝ๋ก์์ ๋ฆฌํฌ์งํ ๋ฆฌ ํด๋ก
!git clone "{GIT_PATH}"
project path์ ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ ๋์๋๋ ํด๋ [backend-ai]๊ฐ ์์ฑ๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
# ํด๋ก ๋ฐ์ ๋๋ ํ ๋ฆฌ๋ก ์ด๋
%cd backend-ai/
# ๋ธ๋์น ํ์ธ ๋ฐ ์ํ ์ฒดํฌ
!git branch
!git status
# ํด๋ ๊ตฌ์กฐ ํ์ธ์ฉ
!apt-get install tree
!treeํ์x : ํด๋๊ตฌ์กฐ ํ์ธ์ ์ํด tree๋ฅผ installํด ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ฝ๋ฉ์ ํด๋ก ํ ํด๋น ๋ฆฌํฌ์งํ ๋ฆฌ์์ ์์ ์ ํฉ๋๋ค. (backend-ai ํด๋ ๋ด ํ์ผ์์ ์ค์ ์์ ์ ์ํํฉ๋๋ค. )
์์
ํ ๋ณ๊ฒฝ์ฌํญ์ GitHub์ ๋ฐ์ํ๊ธฐ ์ํด์ ๊นํ๋ธ๋ ํฌ์งํ ๋ฆฌ์ฐ๋์ฉ.ipynb ํ์ผ์์ GitHub ๋ฆฌํฌ์งํ ๋ฆฌ์ ์ปค๋ฐ, ํธ์ฌ ๋ฑ์ ์์
์ ์ฝ๊ฒ ์ํํฉ๋๋ค.
# ๋ณ๊ฒฝ๋ ํ์ผ ์ถ๊ฐ
!git add .
# ์ปค๋ฐ (์ปค๋ฐ ๋ฉ์์ง ์์ ๊ฐ๋ฅ)
!git commit -m "[chore] ์ปค๋ฐ ํ
์คํธ"
# ๋ณ๊ฒฝ์ฌํญ ํธ์ฌ
!git push
cf : ๋ธ๋์น๋ฅผ ๋ณ๊ฒฝํ๊ณ ์ถ๋ค๋ฉด !git checkout ๋ธ๋์น๋ช ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํ์ธ์.
๊นํ๋ธ์ ์ฝ๋ฉ์์ pushํ ์ปค๋ฐ์ด ๋ฐ์๋จ์ ํ์ธํ ์ ์์ต๋๋ค.
๋ฆฌํฌ์งํ ๋ฆฌ์ ๋ฐ์๋ ์ต์ ๋ณ๊ฒฝ์ฌํญ์ ๊ฐ์ ธ์ค๋ ค๋ฉด ์๋ ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํฉ๋๋ค.
(+ ์ฝ๋ฉ์ด ์๋, backend-ai ๋ฆฌํฌ์งํ ๋ฆฌ์ ๋น์ฃผ์ผ ์คํํฐ์ค ์ฝ๋ ๋ฑ์ผ๋ก ์์ ํด์ ์๊ฒฉ ์ ์ฅ์์ Pushํ ์ฌํญ๋ pull ๋ฐ์ ์ ์์ต๋๋ค. )
# ํ์ฌ ๋ธ๋์น์ ์ต์ ๋ณ๊ฒฝ์ฌํญ Pull ๋ฐ๊ธฐ
!git pull
# ๋๋, ํน์ ๋ธ๋์น์์ Pull ๋ฐ์ผ๋ ค๋ฉด:
!git pull origin ๋ธ๋์น๋ช
๊นํ๋ธ๋ ํฌ์งํ ๋ฆฌ์ฐ๋์ฉ.ipynb ํ์ผ์์ Pull ๋ช ๋ ์ ์คํํ ํ์ ์ฝ๋ฉ ์์ backend-ai ๋ฆฌํฌ์งํ ๋ฆฌ๋ฅผ ํ์ธํ๋ฉด, ๋ฆฌํฌ์งํ ๋ฆฌ์ ๋ฐ์๋ ์ต์ ๋ณ๊ฒฝ์ฌํญ์ด ๋ฐ์๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
