From b0503baac5b63a0aa104f48ddc3e1041320f06ee Mon Sep 17 00:00:00 2001 From: cooney Date: Wed, 17 Jun 2026 18:27:04 +0900 Subject: [PATCH] =?UTF-8?q?1.=20sqlalchemy=20CRUD=20=EC=98=88=EC=A0=9C=20?= =?UTF-8?q?=EC=8B=A4=EC=8A=B5=202.=20callcenter=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EC=A0=9D=ED=8A=B8=20=EC=A0=9C=EC=9E=91=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- project/CALLCENTER_APP/.idea/.gitignore | 10 + .../CALLCENTER_APP/.idea/CALLCENTER_APP.iml | 15 + project/CALLCENTER_APP/.idea/dataSources.xml | 12 + .../inspectionProfiles/profiles_settings.xml | 6 + project/CALLCENTER_APP/.idea/modules.xml | 8 + project/CALLCENTER_APP/.idea/pyLspTools.xml | 34 + project/CALLCENTER_APP/.idea/vcs.xml | 6 + .../CALLCENTER_APP/backend/ai/embedding.py | 9 + project/CALLCENTER_APP/backend/ai/llm.py | 17 + .../CALLCENTER_APP/backend/config/settings.py | 12 + project/CALLCENTER_APP/backend/main.py | 34 + .../backend/prompts/all_prompt.py | 28 + .../backend/repository/db_init.py | 23 + .../backend/repository/models.py | 30 + .../CALLCENTER_APP/backend/repository/seed.py | 28 + .../backend/routers/call_router.py | 18 + .../backend/schemas/summary_schema.py | 28 + .../backend/scripts/build_vector_db.py | 26 + .../backend/services/call_service.py | 72 ++ project/CALLCENTER_APP/data/faq.txt | 15 + project/CALLCENTER_APP/data/transcript.txt | 5 + project/CALLCENTER_APP/data/transcript1.txt | 27 + project/CALLCENTER_APP/data/transcript2.txt | 17 + project/CALLCENTER_APP/data/transcript3.txt | 29 + project/CALLCENTER_APP/data/transcript4.txt | 21 + project/CALLCENTER_APP/db/callcenter.db | Bin 0 -> 12288 bytes .../data_level0.bin | Bin 0 -> 321200 bytes .../header.bin | Bin 0 -> 100 bytes .../length.bin | Bin 0 -> 400 bytes .../link_lists.bin | 0 .../CALLCENTER_APP/vectordb/chroma.sqlite3 | Bin 0 -> 290816 bytes project/CREDIT_APP/.idea/dataSources.xml | 7 + project/CREDIT_APP/.idea/db-forest-config.xml | 1 + project/CREDIT_APP/db/demo.db | Bin 0 -> 12288 bytes project/CREDIT_APP/db/users.db | Bin 12288 -> 20480 bytes project/CREDIT_APP/sql.ipynb | 771 +++++++++++++++++- 36 files changed, 1286 insertions(+), 23 deletions(-) create mode 100644 project/CALLCENTER_APP/.idea/.gitignore create mode 100644 project/CALLCENTER_APP/.idea/CALLCENTER_APP.iml create mode 100644 project/CALLCENTER_APP/.idea/dataSources.xml create mode 100644 project/CALLCENTER_APP/.idea/inspectionProfiles/profiles_settings.xml create mode 100644 project/CALLCENTER_APP/.idea/modules.xml create mode 100644 project/CALLCENTER_APP/.idea/pyLspTools.xml create mode 100644 project/CALLCENTER_APP/.idea/vcs.xml create mode 100644 project/CALLCENTER_APP/backend/ai/embedding.py create mode 100644 project/CALLCENTER_APP/backend/ai/llm.py create mode 100644 project/CALLCENTER_APP/backend/config/settings.py create mode 100644 project/CALLCENTER_APP/backend/main.py create mode 100644 project/CALLCENTER_APP/backend/prompts/all_prompt.py create mode 100644 project/CALLCENTER_APP/backend/repository/db_init.py create mode 100644 project/CALLCENTER_APP/backend/repository/models.py create mode 100644 project/CALLCENTER_APP/backend/repository/seed.py create mode 100644 project/CALLCENTER_APP/backend/routers/call_router.py create mode 100644 project/CALLCENTER_APP/backend/schemas/summary_schema.py create mode 100644 project/CALLCENTER_APP/backend/scripts/build_vector_db.py create mode 100644 project/CALLCENTER_APP/backend/services/call_service.py create mode 100644 project/CALLCENTER_APP/data/faq.txt create mode 100644 project/CALLCENTER_APP/data/transcript.txt create mode 100644 project/CALLCENTER_APP/data/transcript1.txt create mode 100644 project/CALLCENTER_APP/data/transcript2.txt create mode 100644 project/CALLCENTER_APP/data/transcript3.txt create mode 100644 project/CALLCENTER_APP/data/transcript4.txt create mode 100644 project/CALLCENTER_APP/db/callcenter.db create mode 100644 project/CALLCENTER_APP/vectordb/8aa1a33e-b040-4aa6-8954-320af0dc9c67/data_level0.bin create mode 100644 project/CALLCENTER_APP/vectordb/8aa1a33e-b040-4aa6-8954-320af0dc9c67/header.bin create mode 100644 project/CALLCENTER_APP/vectordb/8aa1a33e-b040-4aa6-8954-320af0dc9c67/length.bin create mode 100644 project/CALLCENTER_APP/vectordb/8aa1a33e-b040-4aa6-8954-320af0dc9c67/link_lists.bin create mode 100644 project/CALLCENTER_APP/vectordb/chroma.sqlite3 create mode 100644 project/CREDIT_APP/db/demo.db diff --git a/project/CALLCENTER_APP/.idea/.gitignore b/project/CALLCENTER_APP/.idea/.gitignore new file mode 100644 index 0000000..93bca08 --- /dev/null +++ b/project/CALLCENTER_APP/.idea/.gitignore @@ -0,0 +1,10 @@ +# 디폴트 무시된 파일 +/shelf/ +/workspace.xml +# 에디터 기반 HTTP 클라이언트 요청 +/httpRequests/ +# 쿼리 파일을 포함한 무시된 디폴트 폴더 +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/project/CALLCENTER_APP/.idea/CALLCENTER_APP.iml b/project/CALLCENTER_APP/.idea/CALLCENTER_APP.iml new file mode 100644 index 0000000..97aee2d --- /dev/null +++ b/project/CALLCENTER_APP/.idea/CALLCENTER_APP.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/project/CALLCENTER_APP/.idea/dataSources.xml b/project/CALLCENTER_APP/.idea/dataSources.xml new file mode 100644 index 0000000..9961c60 --- /dev/null +++ b/project/CALLCENTER_APP/.idea/dataSources.xml @@ -0,0 +1,12 @@ + + + + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:$PROJECT_DIR$/db/callcenter.db + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/project/CALLCENTER_APP/.idea/inspectionProfiles/profiles_settings.xml b/project/CALLCENTER_APP/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/project/CALLCENTER_APP/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/project/CALLCENTER_APP/.idea/modules.xml b/project/CALLCENTER_APP/.idea/modules.xml new file mode 100644 index 0000000..a31569c --- /dev/null +++ b/project/CALLCENTER_APP/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/project/CALLCENTER_APP/.idea/pyLspTools.xml b/project/CALLCENTER_APP/.idea/pyLspTools.xml new file mode 100644 index 0000000..e202fc5 --- /dev/null +++ b/project/CALLCENTER_APP/.idea/pyLspTools.xml @@ -0,0 +1,34 @@ + + + + + + \ No newline at end of file diff --git a/project/CALLCENTER_APP/.idea/vcs.xml b/project/CALLCENTER_APP/.idea/vcs.xml new file mode 100644 index 0000000..b2bdec2 --- /dev/null +++ b/project/CALLCENTER_APP/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/project/CALLCENTER_APP/backend/ai/embedding.py b/project/CALLCENTER_APP/backend/ai/embedding.py new file mode 100644 index 0000000..4f1a98a --- /dev/null +++ b/project/CALLCENTER_APP/backend/ai/embedding.py @@ -0,0 +1,9 @@ +from langchain_ibm import WatsonxEmbeddings +from backend.config.settings import settings + +watson_embedding = WatsonxEmbeddings( + model_id="ibm/granite-embedding-278m-multilingual", + url=f"{settings.watsonx_url}", + api_key=f"{settings.watsonx_api_key}", + project_id=f"{settings.watsonx_project_id}", +) \ No newline at end of file diff --git a/project/CALLCENTER_APP/backend/ai/llm.py b/project/CALLCENTER_APP/backend/ai/llm.py new file mode 100644 index 0000000..5f52f60 --- /dev/null +++ b/project/CALLCENTER_APP/backend/ai/llm.py @@ -0,0 +1,17 @@ +from langchain_ibm import ChatWatsonx +from backend.config.settings import settings +from langchain_openai import ChatOpenAI + +watson_llm = ChatWatsonx( + model_id="ibm/granite-4-h-small", + url=f"{settings.watsonx_url}", + api_key=f"{settings.watsonx_api_key}", + project_id=f"{settings.watsonx_project_id}", + max_tokens=2000, +) + +hugging_llm = ChatOpenAI( + base_url="https://router.huggingface.co/v1", + api_key=f"{settings.hf_token}", + model_name="Qwen/Qwen3-8B:nscale", +) \ No newline at end of file diff --git a/project/CALLCENTER_APP/backend/config/settings.py b/project/CALLCENTER_APP/backend/config/settings.py new file mode 100644 index 0000000..1d35237 --- /dev/null +++ b/project/CALLCENTER_APP/backend/config/settings.py @@ -0,0 +1,12 @@ +from pydantic import Field +from pydantic_settings import BaseSettings, SettingsConfigDict + +class Settings(BaseSettings): + model_config = SettingsConfigDict(env_file="backend/.env", extra="ignore") + # 사용할 모델 + watsonx_api_key: str = Field(alias="WATSONX_API_KEY") + watsonx_project_id: str = Field(alias="WATSONX_PROJECT_ID") + watsonx_url: str = Field(alias="WATSONX_URL") + hf_token: str = Field(alias="HF_TOKEN") + +settings = Settings() \ No newline at end of file diff --git a/project/CALLCENTER_APP/backend/main.py b/project/CALLCENTER_APP/backend/main.py new file mode 100644 index 0000000..64b0b27 --- /dev/null +++ b/project/CALLCENTER_APP/backend/main.py @@ -0,0 +1,34 @@ +from fastapi import FastAPI +from starlette.staticfiles import StaticFiles +from contextlib import asynccontextmanager + +from backend.repository.db_init import Base, SessionLocal, engine +from backend.routers.call_router import router as call_router +from backend.repository.seed import seed_customers + +@asynccontextmanager +async def lifespan(app: FastAPI): + print("서버 시작") + + # Base 에 등록된 모든 모델에 테이블 자동 생성 + Base.metadata.create_all(bind=engine) + print("[DB] 테이블 생성 완료 (또는 이미 존재)") + + db = SessionLocal() + try: + # 기본 user 데이터 삽입 + seed_customers(db) + finally: + db.close() + + yield + print("서버 종료") + +# app = FastAPI() +app = FastAPI(title="상담 LLM", version="1.0", lifespan=lifespan) + +# static 폴더 지정 +app.mount("/static", StaticFiles(directory="backend/static"), name="static") + +# 라우터 등록 +app.include_router(call_router) \ No newline at end of file diff --git a/project/CALLCENTER_APP/backend/prompts/all_prompt.py b/project/CALLCENTER_APP/backend/prompts/all_prompt.py new file mode 100644 index 0000000..757b8fb --- /dev/null +++ b/project/CALLCENTER_APP/backend/prompts/all_prompt.py @@ -0,0 +1,28 @@ +# 요약 프롬프트 생성 +SUMMARY_SYSTEM_PROMPT = """ +당신은 콜센터 상담 기록 분석 전문가입니다. + +다음 항목을 추출하세요. + +1. summary + - 상담 내용을 한 문장으로 요약 + +2. keywords + - 상담 내용에서 중요한 키워드 3~5개 추출 + +3. category + - 상담 유형 + - 예: 장애신고, 기술지원, 요금문의, 해지문의, 서비스변경 + +4. sentiment + - 상담 내용의 감정 분석 결과 = 예: 긍정(positive), 부정(negative), 중립(neutral) 중 하나 + +5. action_items: + - 상담 후 필요한 후속 조치 + +6. customer_issue + - 고객이 겪은 문제 + +7. resolution + - 상담사가 제공한 해결 방법 +""" \ No newline at end of file diff --git a/project/CALLCENTER_APP/backend/repository/db_init.py b/project/CALLCENTER_APP/backend/repository/db_init.py new file mode 100644 index 0000000..a9dec18 --- /dev/null +++ b/project/CALLCENTER_APP/backend/repository/db_init.py @@ -0,0 +1,23 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import DeclarativeBase, sessionmaker +from pathlib import Path + +Path("db").mkdir(parents=True, exist_ok=True) + +# echo sql 구문 출력시키는 +engine = create_engine('sqlite:///db/callcenter.db', echo=True) + +# 세션 팩토리 생성 +SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False) + +# Base = declarative_Base() +class Base(DeclarativeBase): + pass + +# session 을 다른 모듈에서 사용할 수 있도록 제공 +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() \ No newline at end of file diff --git a/project/CALLCENTER_APP/backend/repository/models.py b/project/CALLCENTER_APP/backend/repository/models.py new file mode 100644 index 0000000..b2c10ce --- /dev/null +++ b/project/CALLCENTER_APP/backend/repository/models.py @@ -0,0 +1,30 @@ +from backend.repository.db_init import Base +from sqlalchemy.orm import Mapped, mapped_column +from sqlalchemy import DateTime, func, String, ForeignKey +from datetime import datetime + +class Customer(Base): + __tablename__ = 'customers' + + customer_id:Mapped[int] = mapped_column(primary_key=True, autoincrement=True) + name:Mapped[str] = mapped_column(String(50), nullable=False) + phone:Mapped[str] = mapped_column(String(20), nullable=False) + + def __str__(self): + return f" 내 요금제 메뉴에서 변경 가능합니다. + +Q. 데이터 사용량은 어디서 확인하나요? + +A. 고객 포털 > 데이터 사용량 메뉴에서 확인 가능합니다. \ No newline at end of file diff --git a/project/CALLCENTER_APP/data/transcript.txt b/project/CALLCENTER_APP/data/transcript.txt new file mode 100644 index 0000000..17eb993 --- /dev/null +++ b/project/CALLCENTER_APP/data/transcript.txt @@ -0,0 +1,5 @@ +상담사: 안녕하세요. 고객센터입니다. 무엇을 도와드릴까요?\n고객: 몇 시간 전부터 인터넷 속도가 너무 느려서 답답합니다.\n상담사: 고객 확인을 위해 성함을 알려주시겠습니까?\n고객: 네, 홍길동입니다.\n상담사: 감사합니다. 본인 확인을 위해 PIN 번호도 알려주시겠습니까?\n고객: 지금은 PIN 번호를 가지고 있지 않습니다.\n상담사: 괜찮습니다. 우선 제가 회선 상태를 확인하고 조정해보겠습니다. 잠시만 기다려 주세요.\n(잠시 후)\n상담사: 회선 설정을 일부 조정했습니다. 현재 인터넷 속도를 다시 확인해보시겠습니까?\n고객: 잠시만요... 네, 훨씬 빨라졌네요.\n상담사: 다행입니다. 상담 종료 후 설문조사가 발송될 예정입니다. 설문은 인터넷 서비스 품질이 아니라 상담 서비스에 대한 평가입니다.\n고객: 네, 알겠습니다.\n상담사: 이용해 주셔서 감사합니다. 좋은 하루 보내세요.\n고객: 감사합니다. + +---- + +상담사: 안녕하세요. 인터넷 서비스 고객센터입니다. 성함과 PIN 번호를 알려주시겠습니까?\n고객: 홍길동이고 PIN은 1234입니다.\n상담사: 감사합니다. 어떤 문제로 연락주셨나요?\n고객: 어제부터 인터넷 속도가 너무 느립니다.\n상담사: 불편을 드려 죄송합니다. 연결 상태를 확인해보겠습니다.\n(확인 중)\n상담사: 현재 고객님 지역의 회선 장애가 의심됩니다. 관련 부서에 장애 내용을 접수하겠습니다.\n고객: 네, 빨리 해결되면 좋겠네요.\n상담사: 최대한 신속히 처리하겠습니다. 신고해 주셔서 감사합니다. \ No newline at end of file diff --git a/project/CALLCENTER_APP/data/transcript1.txt b/project/CALLCENTER_APP/data/transcript1.txt new file mode 100644 index 0000000..119ba55 --- /dev/null +++ b/project/CALLCENTER_APP/data/transcript1.txt @@ -0,0 +1,27 @@ +상담사: 안녕하세요. 고객센터입니다. 무엇을 도와드릴까요? + +고객: 몇 시간 전부터 인터넷 속도가 너무 느려서 답답합니다. + +상담사: 고객 확인을 위해 성함을 알려주시겠습니까? + +고객: 네, 홍길동입니다. + +상담사: 감사합니다. 본인 확인을 위해 PIN 번호도 알려주시겠습니까? + +고객: 지금은 PIN 번호를 가지고 있지 않습니다. + +상담사: 괜찮습니다. 우선 제가 회선 상태를 확인하고 조정해보겠습니다. 잠시만 기다려 주세요. + +(잠시 후) + +상담사: 회선 설정을 일부 조정했습니다. 현재 인터넷 속도를 다시 확인해보시겠습니까? + +고객: 잠시만요... 네, 훨씬 빨라졌네요. + +상담사: 다행입니다. 상담 종료 후 설문조사가 발송될 예정입니다. 설문은 인터넷 서비스 품질이 아니라 상담 서비스에 대한 평가입니다. + +고객: 네, 알겠습니다. + +상담사: 이용해 주셔서 감사합니다. 좋은 하루 보내세요. + +고객: 감사합니다. \ No newline at end of file diff --git a/project/CALLCENTER_APP/data/transcript2.txt b/project/CALLCENTER_APP/data/transcript2.txt new file mode 100644 index 0000000..0274846 --- /dev/null +++ b/project/CALLCENTER_APP/data/transcript2.txt @@ -0,0 +1,17 @@ +상담사: 안녕하세요. 인터넷 서비스 고객센터입니다. 성함과 PIN 번호를 알려주시겠습니까? + +고객: 홍길동이고 PIN은 1234입니다. + +상담사: 감사합니다. 어떤 문제로 연락주셨나요? + +고객: 어제부터 인터넷 속도가 너무 느립니다. + +상담사: 불편을 드려 죄송합니다. 연결 상태를 확인해보겠습니다. + +(확인 중) + +상담사: 현재 고객님 지역의 회선 장애가 의심됩니다. 관련 부서에 장애 내용을 접수하겠습니다. + +고객: 네, 빨리 해결되면 좋겠네요. + +상담사: 최대한 신속히 처리하겠습니다. 신고해 주셔서 감사합니다. \ No newline at end of file diff --git a/project/CALLCENTER_APP/data/transcript3.txt b/project/CALLCENTER_APP/data/transcript3.txt new file mode 100644 index 0000000..72f4306 --- /dev/null +++ b/project/CALLCENTER_APP/data/transcript3.txt @@ -0,0 +1,29 @@ +상담사: 안녕하세요. 무엇을 도와드릴까요? + +고객: 어제부터 인터넷이 계속 끊어집니다. + +상담사: 공유기를 재부팅해 보셨나요? + +고객: 네. 이미 해봤는데 똑같습니다. + +상담사: 현재 공유기 상태 표시등은 어떻게 되어 있나요? + +고객: 전원등은 켜져 있고 인터넷 표시등은 깜빡입니다. + +상담사: 공유기 케이블 연결 상태를 확인해 주시겠습니까? + +고객: 네. 확인했는데 이상 없습니다. + +상담사: 그렇다면 공유기 초기화를 진행해보겠습니다. 초기화 방법을 알고 계신가요? + +고객: 아니요. + +상담사: 공유기 뒷면의 리셋 버튼을 10~15초 정도 눌러주세요. + +고객: 네. 지금 재시작 중입니다. + +(몇 분 후) + +고객: 이제 정상적으로 작동하는 것 같습니다. + +상담사: 다행입니다. 다른 문제가 있으시면 언제든 연락 주세요. \ No newline at end of file diff --git a/project/CALLCENTER_APP/data/transcript4.txt b/project/CALLCENTER_APP/data/transcript4.txt new file mode 100644 index 0000000..fa4fd3b --- /dev/null +++ b/project/CALLCENTER_APP/data/transcript4.txt @@ -0,0 +1,21 @@ +상담사: 안녕하세요. 고객센터입니다. 성함과 PIN 번호를 확인하겠습니다. + +고객: 홍길동이며 PIN은 1234입니다. + +상담사: 감사합니다. 어떤 문제가 있으신가요? + +고객: 인터넷이 느리고 가끔 끊깁니다. + +상담사: 불편을 드려 죄송합니다. 연결 상태를 확인하겠습니다. + +(잠시 후) + +상담사: 회선에 일시적인 문제가 있었으며 현재 초기화 처리를 완료했습니다. 다시 확인해보시겠습니까? + +고객: 네. 정상적으로 작동합니다. + +상담사: 다행입니다. 상담 후 설문조사가 발송될 예정이며 상담 서비스에 대한 평가입니다. + +고객: 알겠습니다. + +상담사: 감사합니다. 좋은 하루 보내세요. \ No newline at end of file diff --git a/project/CALLCENTER_APP/db/callcenter.db b/project/CALLCENTER_APP/db/callcenter.db new file mode 100644 index 0000000000000000000000000000000000000000..aea8cd2fc73bfccd8593f4e915d7f6687dd4adc3 GIT binary patch literal 12288 zcmeHM-EY%Y6i)&gqEgkpObA}MFV$L&lqRGf>kG?RVBJb!p-e)1Kv~H~rGBs^orJW9 zGWKX`=|HIC;o!CxO=-Y&Jt;TV3?mZvBbI$MF zs5icum`=vf5A*TaXcAp;eduz#U7sQ3a=E(Ubqrq3%L5O`n;+oaZF@fKvD-E9^CPOr z(_L`&gyF_6h5$o=A;1t|2rvW~0t^9$07HNwz!0z_uoUo|IMv(he(*^$`u$97DmpWB z`|fliIUm1oe?E385(-X+&}8uQi4d|&P+!+c{qyu4G#;J|T?s`{_}U~2-<+5@hd{Of z%4TEn+lR=L@#tJ)Dn5NLiM|a+E`1qvZci-C&PL<+JIbb_$=HwJXj9lXG}!O3orujP zr)S~TTH$Y7VR@HGEX11297^J`#Qe-ca(aHQqj)MF12^v6h99+G078@FS3{2N*CXRs zgOOY4>(DLKX9Cqvfw_Dw5*oh}rh-1pjXV7)61p6Ugu<6XH&FBY1Z=f%?Cg-u}+H0iE6egOh5*}y5nsPl`mZpvwn~To2kT?M8y}R>smb4vCXRwwX@L2zm-&1k* z{M+*nys?WRzz|>vFa#I^3;~7!Lx3T`5MT%}1Q-Ggf&UqSGslj*&VGy)fryKmR2lRQ zdWVKbM!i1&g}@mPm62CPvL_Q>q_V&Tzt1~5GCXv~O=a5pW3^t#E9+E7x9FSo|IL5! z($)0>ZtP+RFa#I^?;ir$uRPtZllhBFAN8KTnf36sz`)uXq-YemvNTI+0p>Ql^Q8LBoc@43P;vc`1c~-si|*ru<2r#v1tt9KOZ8J zEvgEKh|J;FDNurlREIar{f)?j$FN&fQV4TAXhb-Z$8w2qMTE1j;7=2FO@m!F9}VqD zTNgkTb%7K)O{gKl{iO+I{R<&S^=vn&aiI7NDC5C7n`d0<9B42}-U`fNN67;wmJbM5 z!Cd{I;p^jJg!gz&gnJ>Pky6OE^H~D{5>!-`5!vM72~=ea-znw}*J?$g zytHJ9Y$$|-eKLTZLfS?SK0W*e((KNRq|f)BjxPQb^w=;wkY`&~xFUYTkxc2Z+#5Kn z)kd59?+)AoT&mG9A$*Dgov%ks0a>fzwGxzH9YV!hMMMe;miB3?m&-UUI?4|g(^W?* zyPf?Gm4;vQ+OK+v(fXE4h-`D@!IrHgz`VK#HNCW`Efq23(5Ol%M_jG7N>5K;pCLk| z^rYXOOHJg+X2k>>XGH{=CxsV863sk9H9R8%v>_CPB$8|e=bLST)LIJ5Wdt!o*N{Tq zT#InJMmBc!UXY)_5eGWj=}oIeX(Qk_WxP{C;57t;^CI4bZu%n&J1|$GG+_4^j!ih1 z(geUhD?-?WA}DA#LKlD?y*FusR%vIeZCzzJLL{)*MhsBRU`Eul3?k@ons$lC zf_Ig36m)~OaAwsB72Trc@;F=8ZR_xXHVOp7%1YC58iMXPO&k&SX)r=rtt*6=X%e31 zAy;^3|J`+D@YeIZnae{mXs-{E6d>mZvX`y^QJSt&IIA+mX~5jsTmPVc*gH7v^^G9k z=$L3PczruI?Ja|_MZN3@gigSBT~$J#_}Dwhn5W@TMCh3k{=)b} zB7UAjQ0hQU2C+5FiQm;{$dUGxbMllvRA9=MK(Ed$QUiFWX=shNjiY8Cv~@IX05AyF z9XxsHlx}%SIcNY*9s1OMS|+O%owK9{*~)dGD6pZeJ+ks`QU!3@GTzS7$Y>apO#q~H zML&TkMPPQE2jE1W256mi%%w1>Yz);JlG<-Ds$rM|e+e9&Arf$j&P-s#oTQYPP0rL- z9?>!1(r%rVXlyV-oiyFzI5?U~l+xc$rj)hmM5PqD4P&8Nql3UW%C#&V#=tOn+VZ6X zV{qG6juabC+NDzCq+u@#v+Q(!GY)t(>ngy@wI)HqRx5V8RqK!S+UjsXY)|d aL30E@+TwbYwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA Sz<>b*1`HT5V8DQZ&w&Bv>;M4( literal 0 HcmV?d00001 diff --git a/project/CALLCENTER_APP/vectordb/8aa1a33e-b040-4aa6-8954-320af0dc9c67/header.bin b/project/CALLCENTER_APP/vectordb/8aa1a33e-b040-4aa6-8954-320af0dc9c67/header.bin new file mode 100644 index 0000000000000000000000000000000000000000..dff34e75f054d83937963d81e17374ba51682b2e GIT binary patch literal 100 rcmZQ%K!6kk6U^%2fe5s~XsG;uC=h`16`(YX|F20q)m`+uJQlei2O-1s=uM1WdX=NFh0zDMgr!;)Iacn tm=EK_%s&7%;6M%}eBgY4i2oTFUmgJ2%LKC!LN`G5GnoGWAO0T+0009|I)nfK literal 0 HcmV?d00001 diff --git a/project/CALLCENTER_APP/vectordb/8aa1a33e-b040-4aa6-8954-320af0dc9c67/link_lists.bin b/project/CALLCENTER_APP/vectordb/8aa1a33e-b040-4aa6-8954-320af0dc9c67/link_lists.bin new file mode 100644 index 0000000..e69de29 diff --git a/project/CALLCENTER_APP/vectordb/chroma.sqlite3 b/project/CALLCENTER_APP/vectordb/chroma.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..0380404d4f0b0e5b7cd82ccb97847975986e93e8 GIT binary patch literal 290816 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 literal 0 HcmV?d00001 diff --git a/project/CREDIT_APP/.idea/dataSources.xml b/project/CREDIT_APP/.idea/dataSources.xml index b70d3a3..d853b46 100644 --- a/project/CREDIT_APP/.idea/dataSources.xml +++ b/project/CREDIT_APP/.idea/dataSources.xml @@ -15,5 +15,12 @@ jdbc:sqlite:$PROJECT_DIR$/db/users.db $ProjectFileDir$ + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:$PROJECT_DIR$/db/demo.db + $ProjectFileDir$ + \ No newline at end of file diff --git a/project/CREDIT_APP/.idea/db-forest-config.xml b/project/CREDIT_APP/.idea/db-forest-config.xml index 90ec0d8..1fa70d9 100644 --- a/project/CREDIT_APP/.idea/db-forest-config.xml +++ b/project/CREDIT_APP/.idea/db-forest-config.xml @@ -5,6 +5,7 @@ ---------------------------------------- 1:0:d34d9830-06d9-4e92-bd10-84567e5a4809 2:0:2d5d9d87-b30f-47d4-9d9d-61542cb64ea0 + 3:0:fab7f51e-43ea-4d26-b7f0-6be132dd06fb . \ No newline at end of file diff --git a/project/CREDIT_APP/db/demo.db b/project/CREDIT_APP/db/demo.db new file mode 100644 index 0000000000000000000000000000000000000000..5b0589c8e660b8c7bc607023548bca354eb0a948 GIT binary patch literal 12288 zcmeI&KTE?v7zXgWB*t2h6CBR*9a~yl`~s$ONHI-oyn>x1t)~?Fhc>N4w~l@(XFq_m zAHZ36FOgR5;#%Z+$sM_e_b%|;4svxF_GgCf#?xUkqZ4+(IA^Csj4{s+*N#11Fp z%{Qr;%+08`xpdEB6=+2|sD+9)dP}2ZXsUK+dFk?9DJ5T;{LFc3rfEJozxL-5m5OAm ze@pd9t8*1oE3P+!xI-7JLqS_N>yh2nP?4_Yo!M7Nt7}bB(~fo+e&xx%Ssju0m9x9K zFP@lq7jNQKyohJJKtTWk5P$##AOHafKmY;|fB*y_utfp|-(!coHtrr3cRVI}FzEM; zEO_hG{gFEBlz*q${hx~uW*-y;AOHafKmY;|fB*y_009U<00RF>V2`_!=RX$spBr&q Ak^lez literal 0 HcmV?d00001 diff --git a/project/CREDIT_APP/db/users.db b/project/CREDIT_APP/db/users.db index 18cac5cba4764390bf6fb260f47a9f7dc22c91c2..065e0cfbef7ef0aa7371844b599c76d9b3714210 100644 GIT binary patch literal 20480 zcmeI%&ui0Q7zgk-ZMNA;7X_gQ8G9WJ8rVj+%7%)#+P#~_wA->&*kRPTM(ya2*=Dli zIyY2?w>d@Vkl{gO2az3m5O(nF#fx~@KVb6*c=SzNhaIaFVIt$3HhJH?dEd9s^9eb; z>C~i>HVt|=U&!hv4UscM5Xdm4gpe-2xA48To#qv>cEU^Hw|b*$7wLQW)F=HQqOU~S zqrOu6p7gm%usTg|vS}Jfr(}Ds(Q%bU$KrNdV1KMkRW{007LTwgYPS`g-d?$M*4^g!`-Nq} z$-}wi9K^Z|_J=eK-%OhsqZWK%AQ+UJ&hBsqJf>`m6VszhPD~3$qfm4XuDY0TZ3WjbPsU%-(kMP^7MPrPPu+eZr(P$*0a#t;BH%@8c z*hJlohqJTqPa+K8~lmogTM4>EjZpVSx3ymOv((oFU{g_}Q9 zr|jp-C%qxE3( znEh1@8;g2&K4bLpS3Bnhf&+uS!NK1CAsP%_=HLDy9@y>U49+WkoKO3HIOna#K_KcrTaqfk@x@0UHD$009U< z00Izz00bZa0SG_<0{@9Xw@V}!t|&^3TAQoY?Fz41<(=y060L6UST7!1<;wT>OV_%E zrn=ede^FW?(iiEIw9YHoAOHafKmY;|fB*y_009U<00Iy=7J+tAB%Ok_wPrnEv0kma zdfg(K5uA1X616re*2WI4zI$7JR>A<# diff --git a/project/CREDIT_APP/sql.ipynb b/project/CREDIT_APP/sql.ipynb index 52625e7..0fcbf34 100644 --- a/project/CREDIT_APP/sql.ipynb +++ b/project/CREDIT_APP/sql.ipynb @@ -17,29 +17,19 @@ "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2026-06-16T08:48:53.375278031Z", - "start_time": "2026-06-16T08:48:51.509377417Z" + "end_time": "2026-06-17T03:03:17.531519156Z", + "start_time": "2026-06-17T03:03:17.519312421Z" } }, - "source": "!pip install sqlalchemy", - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Requirement already satisfied: sqlalchemy in ./.venv/lib/python3.12/site-packages (2.0.51)\n", - "Requirement already satisfied: greenlet>=1 in ./.venv/lib/python3.12/site-packages (from sqlalchemy) (3.5.1)\n", - "Requirement already satisfied: typing-extensions>=4.6.0 in ./.venv/lib/python3.12/site-packages (from sqlalchemy) (4.15.0)\n" - ] - } - ], - "execution_count": 1 + "source": "# !pip install sqlalchemy", + "outputs": [], + "execution_count": 36 }, { "metadata": { "ExecuteTime": { - "end_time": "2026-06-16T08:57:27.860045705Z", - "start_time": "2026-06-16T08:57:27.561065535Z" + "end_time": "2026-06-17T03:03:17.555640871Z", + "start_time": "2026-06-17T03:03:17.532414273Z" } }, "cell_type": "code", @@ -49,13 +39,19 @@ "from sqlalchemy import Column, Integer, String, create_engine\n", "from sqlalchemy.orm import declarative_base, sessionmaker\n", "\n", + "# 1.x 버전 코드\n", "# 데이터베이스 연결\n", - "engine = create_engine('sqlite:///db/users.db')\n", + "# 애플리케이션 당 하나만 만들어 사용\n", + "# echo=True : 실행 SQL 을 콘솔에 추력\n", + "engine = create_engine('sqlite:///db/users.db', echo=True)\n", "# engine = create_engine('sqlite:///users.db')\n", "\n", "# 모든 모델 크래스의 부모 클래스가 될 Base 객체 생성\n", "Base = declarative_base()\n", + "\n", + "# 데이터베이스랑 관련있는 클래스 : 모델 클래스\n", "class User(Base):\n", + " # 테이블 이름 지정\n", " __tablename__ = 'users'\n", "\n", " id = Column(Integer, primary_key=True)\n", @@ -65,20 +61,749 @@ " def __str__(self):\n", " return f\"\"\n", "\n", - "# 테이블 생성\n", + "# 테이블 생성 ( 없는 경우만 새로 생성됨 )\n", "Base.metadata.create_all(engine)" ], "id": "7a41f863de1efd48", - "outputs": [], - "execution_count": 2 + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-06-17 12:03:17,542 INFO sqlalchemy.engine.Engine BEGIN (implicit)\n", + "2026-06-17 12:03:17,543 INFO sqlalchemy.engine.Engine PRAGMA main.table_info(\"users\")\n", + "2026-06-17 12:03:17,545 INFO sqlalchemy.engine.Engine [raw sql] ()\n", + "2026-06-17 12:03:17,548 INFO sqlalchemy.engine.Engine COMMIT\n" + ] + } + ], + "execution_count": 37 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-17T03:03:17.576534965Z", + "start_time": "2026-06-17T03:03:17.557316858Z" + } + }, + "cell_type": "code", + "source": [ + "# 모든 테이블 삭제\n", + "Base.metadata.drop_all(engine)" + ], + "id": "28ae00aa8564cd68", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-06-17 12:03:17,561 INFO sqlalchemy.engine.Engine BEGIN (implicit)\n", + "2026-06-17 12:03:17,563 INFO sqlalchemy.engine.Engine PRAGMA main.table_info(\"users\")\n", + "2026-06-17 12:03:17,563 INFO sqlalchemy.engine.Engine [raw sql] ()\n", + "2026-06-17 12:03:17,565 INFO sqlalchemy.engine.Engine \n", + "DROP TABLE users\n", + "2026-06-17 12:03:17,566 INFO sqlalchemy.engine.Engine [no key 0.00051s] ()\n", + "2026-06-17 12:03:17,571 INFO sqlalchemy.engine.Engine COMMIT\n" + ] + } + ], + "execution_count": 38 }, { "metadata": {}, + "cell_type": "markdown", + "source": "#### text() : 원시 SQL 실행", + "id": "d4b2b34ebf185234" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-17T03:03:17.599302675Z", + "start_time": "2026-06-17T03:03:17.577699609Z" + } + }, "cell_type": "code", + "source": [ + "from sqlalchemy import text\n", + "\n", + "engine = create_engine('sqlite:///db/demo.db', echo=True)\n", + "with engine.connect() as conn:\n", + " conn.execute(text(\"\"\"\n", + " CREATE TABLE IF NOT EXISTS users (\n", + " id INTEGER PRIMARY KEY AUTOINCREMENT,\n", + " name TEXT NOT NULL,\n", + " age INTEGER\n", + " )\n", + " \"\"\"\n", + " ))\n", + "\n", + " conn.commit()\n", + " # 삽입\n", + " conn.execute(text(\"\"\"\n", + " INSERT INTO users (name, age) VALUES (:name, :age)\"\"\"),\n", + " [{\"name\" : \"Alice\", \"age\" : 23}, {\"name\":\"Bob\", \"age\" : 24}])\n", + "\n", + " conn.commit()\n", + "\n", + " # 조회\n", + " result = conn.execute(text(\"\"\"\n", + " SELECT * FROM users\n", + " \"\"\"))\n", + "\n", + " for row in result:\n", + " print(row.id, row.name, row.age)" + ], + "id": "de1ebcd8f162fd42", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-06-17 12:03:17,581 INFO sqlalchemy.engine.Engine BEGIN (implicit)\n", + "2026-06-17 12:03:17,581 INFO sqlalchemy.engine.Engine \n", + " CREATE TABLE IF NOT EXISTS users (\n", + " id INTEGER PRIMARY KEY AUTOINCREMENT,\n", + " name TEXT NOT NULL,\n", + " age INTEGER\n", + " )\n", + " \n", + "2026-06-17 12:03:17,582 INFO sqlalchemy.engine.Engine [generated in 0.00126s] ()\n", + "2026-06-17 12:03:17,583 INFO sqlalchemy.engine.Engine COMMIT\n", + "2026-06-17 12:03:17,584 INFO sqlalchemy.engine.Engine BEGIN (implicit)\n", + "2026-06-17 12:03:17,585 INFO sqlalchemy.engine.Engine \n", + " INSERT INTO users (name, age) VALUES (?, ?)\n", + "2026-06-17 12:03:17,585 INFO sqlalchemy.engine.Engine [generated in 0.00116s] [('Alice', 23), ('Bob', 24)]\n", + "2026-06-17 12:03:17,586 INFO sqlalchemy.engine.Engine COMMIT\n", + "2026-06-17 12:03:17,591 INFO sqlalchemy.engine.Engine BEGIN (implicit)\n", + "2026-06-17 12:03:17,593 INFO sqlalchemy.engine.Engine \n", + " SELECT * FROM users\n", + " \n", + "2026-06-17 12:03:17,593 INFO sqlalchemy.engine.Engine [generated in 0.00211s] ()\n", + "1 Alice 23\n", + "2 Bob 24\n", + "3 Alice 23\n", + "4 Bob 24\n", + "5 Alice 23\n", + "6 Bob 24\n", + "2026-06-17 12:03:17,595 INFO sqlalchemy.engine.Engine ROLLBACK\n" + ] + } + ], + "execution_count": 39 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-17T03:03:17.624396207Z", + "start_time": "2026-06-17T03:03:17.600052176Z" + } + }, + "cell_type": "code", + "source": [ + "# 2.x 버전 코드\n", + "from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column\n", + "from sqlalchemy import DateTime, func\n", + "from datetime import datetime\n", + "\n", + "\n", + "engine = create_engine('sqlite:///db/users.db', echo=True)\n", + "# engine = create_engine('sqlite:///users.db')\n", + "\n", + "# 모든 모델 크래스의 부모 클래스가 될 Base 객체 생성\n", + "class Base(DeclarativeBase):\n", + " pass\n", + "\n", + "# 데이터베이스랑 관련있는 클래스 : 모델 클래스\n", + "class User(Base):\n", + " # 테이블 이름 지정\n", + " __tablename__ = 'users'\n", + "\n", + " # 컬럽 타입 지정 : 티입힌트(파이썬 데이터 타입) 사용\n", + " id:Mapped[int] = mapped_column(primary_key=True, autoincrement=True)\n", + " name:Mapped[str]\n", + " email:Mapped[str] = mapped_column(unique=True)\n", + " age:Mapped[int]\n", + " created_at:Mapped[datetime] = mapped_column(DateTime, server_default=func.now())\n", + "\n", + " def __str__(self):\n", + " return f\"\"\n", + "\n", + "# 테이블 생성 ( 없는 경우만 새로 생성됨 )\n", + "# create_all() : IF NOT EXISTS\n", + "Base.metadata.create_all(engine)" + ], + "id": "d7eb9178f27499a7", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-06-17 12:03:17,606 INFO sqlalchemy.engine.Engine BEGIN (implicit)\n", + "2026-06-17 12:03:17,607 INFO sqlalchemy.engine.Engine PRAGMA main.table_info(\"users\")\n", + "2026-06-17 12:03:17,609 INFO sqlalchemy.engine.Engine [raw sql] ()\n", + "2026-06-17 12:03:17,610 INFO sqlalchemy.engine.Engine PRAGMA temp.table_info(\"users\")\n", + "2026-06-17 12:03:17,610 INFO sqlalchemy.engine.Engine [raw sql] ()\n", + "2026-06-17 12:03:17,612 INFO sqlalchemy.engine.Engine \n", + "CREATE TABLE users (\n", + "\tid INTEGER NOT NULL, \n", + "\tname VARCHAR NOT NULL, \n", + "\temail VARCHAR NOT NULL, \n", + "\tage INTEGER NOT NULL, \n", + "\tcreated_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, \n", + "\tPRIMARY KEY (id), \n", + "\tUNIQUE (email)\n", + ")\n", + "\n", + "\n", + "2026-06-17 12:03:17,613 INFO sqlalchemy.engine.Engine [no key 0.00171s] ()\n", + "2026-06-17 12:03:17,618 INFO sqlalchemy.engine.Engine COMMIT\n" + ] + } + ], + "execution_count": 40 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "#### 세션\n", + "- 데이터베이스 연동\n", + "- 변경사항 추적하고 트랜잭션 관리\n", + "- sessionmaker 팩토리 사용" + ], + "id": "de5e7ee1adb4cee6" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-17T03:03:17.633680827Z", + "start_time": "2026-06-17T03:03:17.625430327Z" + } + }, + "cell_type": "code", + "source": [ + "# 세션 팩토리 생성\n", + "Session = sessionmaker(bind=engine, autoflush=False, autocommit=False)\n", + "# 세션 객체 생성\n", + "session = Session()" + ], + "id": "140093069c78dd21", "outputs": [], - "execution_count": null, + "execution_count": 41 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-17T03:03:17.647449353Z", + "start_time": "2026-06-17T03:03:17.637405010Z" + } + }, + "cell_type": "code", + "source": "session.close()", + "id": "c9f8fdb361307ea", + "outputs": [], + "execution_count": 42 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-17T03:03:17.667693242Z", + "start_time": "2026-06-17T03:03:17.648384205Z" + } + }, + "cell_type": "code", + "source": [ + "# insert\n", + "# session.add() / session.add_all() / session.commit()\n", + "\n", + "session = Session()\n", + "# 사용자 생성\n", + "new_User = User(name=\"Alice\", email=\"alice@example.com\", age=23)\n", + "session.add(new_User)\n", + "session.commit()\n", + "session.close()" + ], + "id": "8c0f8f2c60597d73", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-06-17 12:03:17,651 INFO sqlalchemy.engine.Engine BEGIN (implicit)\n", + "2026-06-17 12:03:17,654 INFO sqlalchemy.engine.Engine INSERT INTO users (name, email, age) VALUES (?, ?, ?) RETURNING id, created_at\n", + "2026-06-17 12:03:17,655 INFO sqlalchemy.engine.Engine [generated in 0.00099s] ('Alice', 'alice@example.com', 23)\n", + "2026-06-17 12:03:17,658 INFO sqlalchemy.engine.Engine COMMIT\n" + ] + } + ], + "execution_count": 43 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-17T03:03:17.691852173Z", + "start_time": "2026-06-17T03:03:17.669654096Z" + } + }, + "cell_type": "code", + "source": [ + "with Session() as session:\n", + " session.add_all(\n", + " [\n", + " User(name=\"Bob\", email=\"bob@example.com\", age=25),\n", + " User(name=\"Charlie\", email=\"charlie@example.com\", age=30),\n", + " ]\n", + " )\n", + " session.commit()" + ], + "id": "2c91d42f265ded17", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-06-17 12:03:17,672 INFO sqlalchemy.engine.Engine BEGIN (implicit)\n", + "2026-06-17 12:03:17,674 INFO sqlalchemy.engine.Engine INSERT INTO users (name, email, age) VALUES (?, ?, ?) RETURNING id, created_at\n", + "2026-06-17 12:03:17,675 INFO sqlalchemy.engine.Engine [generated in 0.00009s (insertmanyvalues) 1/2 (ordered; batch not supported)] ('Bob', 'bob@example.com', 25)\n", + "2026-06-17 12:03:17,676 INFO sqlalchemy.engine.Engine INSERT INTO users (name, email, age) VALUES (?, ?, ?) RETURNING id, created_at\n", + "2026-06-17 12:03:17,676 INFO sqlalchemy.engine.Engine [insertmanyvalues 2/2 (ordered; batch not supported)] ('Charlie', 'charlie@example.com', 30)\n", + "2026-06-17 12:03:17,678 INFO sqlalchemy.engine.Engine COMMIT\n" + ] + } + ], + "execution_count": 44 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-17T03:03:17.733033419Z", + "start_time": "2026-06-17T03:03:17.693025923Z" + } + }, + "cell_type": "code", + "source": [ + "from sqlalchemy import select\n", + "\n", + "with Session() as session:\n", + " # id 를 사용해 조회\n", + " user = session.get(User, 1)\n", + " print(\"--- 단일 사용자 ---\")\n", + " print(user)\n", + "\n", + " print(\"--- 모든 사용자 ---\")\n", + " all_users = session.query(User).all()\n", + " for user in all_users:\n", + " print(user)\n", + "\n", + " stmt = select(User)\n", + " all_users = session.scalars(stmt)\n", + " for user in all_users:\n", + " print(user)\n", + "\n", + " print(\"--- where ---\")\n", + " alice = session.query(User).filter_by(name = \"Alice\").first()\n", + " print(f\"찾은 사용자 {alice}\")\n", + "\n", + " stmt = select(User).where(User.age >= 30)\n", + " find_users = session.scalars(stmt).all()\n", + " for user in find_users:\n", + " print(user)\n", + "\n", + " stmt = select(User).where(User.email.like('%ali%'))\n", + " find_users = session.scalars(stmt).all()\n", + " for user in find_users:\n", + " print(user)\n", + "\n", + " stmt = select(User).order_by(User.id.desc()).limit(2)\n", + " find_users = session.scalars(stmt).all()\n", + " for user in find_users:\n", + " print(user)\n", + "\n", + " # 집계 함수\n", + " count = session.scalar(select(func.count()).select_from(User))\n", + " print(count)" + ], + "id": "b0ce2cc0ee9d68ed", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-06-17 12:03:17,698 INFO sqlalchemy.engine.Engine BEGIN (implicit)\n", + "2026-06-17 12:03:17,704 INFO sqlalchemy.engine.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.age AS users_age, users.created_at AS users_created_at \n", + "FROM users \n", + "WHERE users.id = ?\n", + "2026-06-17 12:03:17,704 INFO sqlalchemy.engine.Engine [generated in 0.00082s] (1,)\n", + "--- 단일 사용자 ---\n", + "\n", + "--- 모든 사용자 ---\n", + "2026-06-17 12:03:17,706 INFO sqlalchemy.engine.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.age AS users_age, users.created_at AS users_created_at \n", + "FROM users\n", + "2026-06-17 12:03:17,707 INFO sqlalchemy.engine.Engine [generated in 0.00096s] ()\n", + "\n", + "\n", + "\n", + "2026-06-17 12:03:17,710 INFO sqlalchemy.engine.Engine SELECT users.id, users.name, users.email, users.age, users.created_at \n", + "FROM users\n", + "2026-06-17 12:03:17,711 INFO sqlalchemy.engine.Engine [generated in 0.00100s] ()\n", + "\n", + "\n", + "\n", + "--- where ---\n", + "2026-06-17 12:03:17,713 INFO sqlalchemy.engine.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.age AS users_age, users.created_at AS users_created_at \n", + "FROM users \n", + "WHERE users.name = ?\n", + " LIMIT ? OFFSET ?\n", + "2026-06-17 12:03:17,714 INFO sqlalchemy.engine.Engine [generated in 0.00058s] ('Alice', 1, 0)\n", + "찾은 사용자 \n", + "2026-06-17 12:03:17,718 INFO sqlalchemy.engine.Engine SELECT users.id, users.name, users.email, users.age, users.created_at \n", + "FROM users \n", + "WHERE users.age >= ?\n", + "2026-06-17 12:03:17,719 INFO sqlalchemy.engine.Engine [generated in 0.00090s] (30,)\n", + "\n", + "2026-06-17 12:03:17,720 INFO sqlalchemy.engine.Engine SELECT users.id, users.name, users.email, users.age, users.created_at \n", + "FROM users \n", + "WHERE users.email LIKE ?\n", + "2026-06-17 12:03:17,721 INFO sqlalchemy.engine.Engine [generated in 0.00080s] ('%ali%',)\n", + "\n", + "2026-06-17 12:03:17,723 INFO sqlalchemy.engine.Engine SELECT users.id, users.name, users.email, users.age, users.created_at \n", + "FROM users ORDER BY users.id DESC\n", + " LIMIT ? OFFSET ?\n", + "2026-06-17 12:03:17,724 INFO sqlalchemy.engine.Engine [generated in 0.00092s] (2, 0)\n", + "\n", + "\n", + "2026-06-17 12:03:17,727 INFO sqlalchemy.engine.Engine SELECT count(*) AS count_1 \n", + "FROM users\n", + "2026-06-17 12:03:17,728 INFO sqlalchemy.engine.Engine [generated in 0.00053s] ()\n", + "3\n", + "2026-06-17 12:03:17,728 INFO sqlalchemy.engine.Engine ROLLBACK\n" + ] + } + ], + "execution_count": 45 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-17T03:03:17.756463366Z", + "start_time": "2026-06-17T03:03:17.734005295Z" + } + }, + "cell_type": "code", + "source": [ + "with Session() as session:\n", + " alice = session.query(User).filter_by(name = \"Alice\").first()\n", + " alice.email = \"alice.new@example.com\"\n", + " session.commit()\n", + "\n", + " print(f\"수정된 사용자 {session.query(User).filter_by(name='Alice').first()}\")" + ], + "id": "93f58b5307abd93f", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-06-17 12:03:17,737 INFO sqlalchemy.engine.Engine BEGIN (implicit)\n", + "2026-06-17 12:03:17,739 INFO sqlalchemy.engine.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.age AS users_age, users.created_at AS users_created_at \n", + "FROM users \n", + "WHERE users.name = ?\n", + " LIMIT ? OFFSET ?\n", + "2026-06-17 12:03:17,740 INFO sqlalchemy.engine.Engine [cached since 0.02646s ago] ('Alice', 1, 0)\n", + "2026-06-17 12:03:17,741 INFO sqlalchemy.engine.Engine UPDATE users SET email=? WHERE users.id = ?\n", + "2026-06-17 12:03:17,742 INFO sqlalchemy.engine.Engine [generated in 0.00075s] ('alice.new@example.com', 1)\n", + "2026-06-17 12:03:17,743 INFO sqlalchemy.engine.Engine COMMIT\n", + "2026-06-17 12:03:17,747 INFO sqlalchemy.engine.Engine BEGIN (implicit)\n", + "2026-06-17 12:03:17,749 INFO sqlalchemy.engine.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.age AS users_age, users.created_at AS users_created_at \n", + "FROM users \n", + "WHERE users.name = ?\n", + " LIMIT ? OFFSET ?\n", + "2026-06-17 12:03:17,749 INFO sqlalchemy.engine.Engine [cached since 0.03613s ago] ('Alice', 1, 0)\n", + "수정된 사용자 \n", + "2026-06-17 12:03:17,751 INFO sqlalchemy.engine.Engine ROLLBACK\n" + ] + } + ], + "execution_count": 46 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-17T03:03:17.785689642Z", + "start_time": "2026-06-17T03:03:17.757935816Z" + } + }, + "cell_type": "code", + "source": [ + "from sqlalchemy import update\n", + "\n", + "with Session() as session:\n", + " user = session.get(User, 1)\n", + " if user:\n", + " user.age = 40\n", + " session.commit()\n", + "\n", + " # 일괄 업데이트\n", + " stmt = update(User).where(User.age <= 30).values(age=20)\n", + " session.execute(stmt)\n", + " session.commit()" + ], + "id": "1f333d8d8bcfbf54", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-06-17 12:03:17,761 INFO sqlalchemy.engine.Engine BEGIN (implicit)\n", + "2026-06-17 12:03:17,762 INFO sqlalchemy.engine.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.age AS users_age, users.created_at AS users_created_at \n", + "FROM users \n", + "WHERE users.id = ?\n", + "2026-06-17 12:03:17,763 INFO sqlalchemy.engine.Engine [cached since 0.0592s ago] (1,)\n", + "2026-06-17 12:03:17,764 INFO sqlalchemy.engine.Engine UPDATE users SET age=? WHERE users.id = ?\n", + "2026-06-17 12:03:17,766 INFO sqlalchemy.engine.Engine [generated in 0.00217s] (40, 1)\n", + "2026-06-17 12:03:17,769 INFO sqlalchemy.engine.Engine COMMIT\n", + "2026-06-17 12:03:17,773 INFO sqlalchemy.engine.Engine BEGIN (implicit)\n", + "2026-06-17 12:03:17,775 INFO sqlalchemy.engine.Engine UPDATE users SET age=? WHERE users.age <= ?\n", + "2026-06-17 12:03:17,776 INFO sqlalchemy.engine.Engine [generated in 0.00108s] (20, 30)\n", + "2026-06-17 12:03:17,777 INFO sqlalchemy.engine.Engine COMMIT\n" + ] + } + ], + "execution_count": 47 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-17T03:03:17.805168757Z", + "start_time": "2026-06-17T03:03:17.787206837Z" + } + }, + "cell_type": "code", + "source": [ + "with Session() as session:\n", + " bob = session.query(User).filter_by(name=\"Bob\").first()\n", + " session.delete(bob)\n", + " session.commit()\n" + ], + "id": "f6c6f87020eb7927", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-06-17 12:03:17,790 INFO sqlalchemy.engine.Engine BEGIN (implicit)\n", + "2026-06-17 12:03:17,791 INFO sqlalchemy.engine.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.age AS users_age, users.created_at AS users_created_at \n", + "FROM users \n", + "WHERE users.name = ?\n", + " LIMIT ? OFFSET ?\n", + "2026-06-17 12:03:17,793 INFO sqlalchemy.engine.Engine [cached since 0.07924s ago] ('Bob', 1, 0)\n", + "2026-06-17 12:03:17,794 INFO sqlalchemy.engine.Engine DELETE FROM users WHERE users.id = ?\n", + "2026-06-17 12:03:17,795 INFO sqlalchemy.engine.Engine [generated in 0.00093s] (2,)\n", + "2026-06-17 12:03:17,797 INFO sqlalchemy.engine.Engine COMMIT\n" + ] + } + ], + "execution_count": 48 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-17T03:03:17.824210114Z", + "start_time": "2026-06-17T03:03:17.807206458Z" + } + }, + "cell_type": "code", + "source": [ + "from sqlalchemy import delete\n", + "\n", + "# delete from 테이블명 where id = 1\n", + "with Session() as session:\n", + " stmt = delete(User).where(User.id == 1)\n", + " session.execute(stmt)\n", + " session.commit()" + ], + "id": "b35c028d86f48e31", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-06-17 12:03:17,810 INFO sqlalchemy.engine.Engine BEGIN (implicit)\n", + "2026-06-17 12:03:17,811 INFO sqlalchemy.engine.Engine DELETE FROM users WHERE users.id = ?\n", + "2026-06-17 12:03:17,812 INFO sqlalchemy.engine.Engine [generated in 0.00074s] (1,)\n", + "2026-06-17 12:03:17,813 INFO sqlalchemy.engine.Engine COMMIT\n" + ] + } + ], + "execution_count": 49 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-17T03:03:17.834127797Z", + "start_time": "2026-06-17T03:03:17.824909859Z" + } + }, + "cell_type": "code", + "source": "Base.metadata.clear()", + "id": "9da5d9421e56853f", + "outputs": [], + "execution_count": 50 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-17T03:03:17.863676311Z", + "start_time": "2026-06-17T03:03:17.834932678Z" + } + }, + "cell_type": "code", + "source": [ + "from sqlalchemy.orm import relationship\n", + "from sqlalchemy import ForeignKey\n", + "\n", + "# 관계(외래키)\n", + "# 1:N, N:1, M:N\n", + "# 게시글 하나 : 댓글 여러개\n", + "\n", + "class Post(Base):\n", + " __tablename__ = 'posts'\n", + "\n", + " id : Mapped[int] = mapped_column(primary_key=True, autoincrement=True)\n", + " title:Mapped[str] = mapped_column(String(200))\n", + " content:Mapped[str]\n", + " # 역참조\n", + " comments: Mapped[list['Comment']] = relationship('Comment', back_populates='post', cascade='all, delete-orphan')\n", + "\n", + "# FOREIGN KEY (post_id) REFERENCES posts(id);\n", + "class Comment(Base):\n", + " __tablename__ = 'comments'\n", + "\n", + " id : Mapped[int] = mapped_column(primary_key=True, autoincrement=True)\n", + " content:Mapped[str]\n", + " # 어느 게시글의 댓글인가?\n", + " post_id:Mapped[int] = mapped_column(ForeignKey(\"posts.id\"))\n", + " # 역참조\n", + " post:Mapped['Post'] = relationship('Post', back_populates='comments')\n" + ], + "id": "cc7b55c5bf90b92b", + "outputs": [], + "execution_count": 51 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-17T03:03:17.884050919Z", + "start_time": "2026-06-17T03:03:17.864744917Z" + } + }, + "cell_type": "code", + "source": "Base.metadata.create_all(engine)", + "id": "50e66cc696cfdd21", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-06-17 12:03:17,868 INFO sqlalchemy.engine.Engine BEGIN (implicit)\n", + "2026-06-17 12:03:17,872 INFO sqlalchemy.engine.Engine PRAGMA main.table_info(\"posts\")\n", + "2026-06-17 12:03:17,873 INFO sqlalchemy.engine.Engine [raw sql] ()\n", + "2026-06-17 12:03:17,873 INFO sqlalchemy.engine.Engine PRAGMA main.table_info(\"comments\")\n", + "2026-06-17 12:03:17,874 INFO sqlalchemy.engine.Engine [raw sql] ()\n", + "2026-06-17 12:03:17,876 INFO sqlalchemy.engine.Engine COMMIT\n" + ] + } + ], + "execution_count": 52 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-17T03:03:17.932695111Z", + "start_time": "2026-06-17T03:03:17.885317745Z" + } + }, + "cell_type": "code", + "source": [ + "with Session() as session:\n", + " post = Post(title=\"LLM 입문\", content=\"LLM이란 무엇인가?\")\n", + " post.comments = [\n", + " Comment(content=\"LLM은 언어 모델의 일종입니다.\"),\n", + " Comment(content=\"좋아요\"),\n", + "\n", + " ]\n", + "\n", + " session.add(post)\n", + " session.commit()\n", + "\n", + " # 역참조 이용\n", + " p = session.get(Post, 1)\n", + " for c in p.comments:\n", + " print(c.content)" + ], + "id": "7400ebdad0575a44", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-06-17 12:03:17,897 INFO sqlalchemy.engine.Engine BEGIN (implicit)\n", + "2026-06-17 12:03:17,902 INFO sqlalchemy.engine.Engine INSERT INTO posts (title, content) VALUES (?, ?)\n", + "2026-06-17 12:03:17,903 INFO sqlalchemy.engine.Engine [generated in 0.00105s] ('LLM 입문', 'LLM이란 무엇인가?')\n", + "2026-06-17 12:03:17,904 INFO sqlalchemy.engine.Engine INSERT INTO comments (content, post_id) VALUES (?, ?) RETURNING id\n", + "2026-06-17 12:03:17,906 INFO sqlalchemy.engine.Engine [generated in 0.00008s (insertmanyvalues) 1/2 (ordered; batch not supported)] ('LLM은 언어 모델의 일종입니다.', 2)\n", + "2026-06-17 12:03:17,906 INFO sqlalchemy.engine.Engine INSERT INTO comments (content, post_id) VALUES (?, ?) RETURNING id\n", + "2026-06-17 12:03:17,907 INFO sqlalchemy.engine.Engine [insertmanyvalues 2/2 (ordered; batch not supported)] ('좋아요', 2)\n", + "2026-06-17 12:03:17,908 INFO sqlalchemy.engine.Engine COMMIT\n", + "2026-06-17 12:03:17,918 INFO sqlalchemy.engine.Engine BEGIN (implicit)\n", + "2026-06-17 12:03:17,920 INFO sqlalchemy.engine.Engine SELECT posts.id AS posts_id, posts.title AS posts_title, posts.content AS posts_content \n", + "FROM posts \n", + "WHERE posts.id = ?\n", + "2026-06-17 12:03:17,920 INFO sqlalchemy.engine.Engine [generated in 0.00071s] (1,)\n", + "2026-06-17 12:03:17,923 INFO sqlalchemy.engine.Engine SELECT comments.id AS comments_id, comments.content AS comments_content, comments.post_id AS comments_post_id \n", + "FROM comments \n", + "WHERE ? = comments.post_id\n", + "2026-06-17 12:03:17,924 INFO sqlalchemy.engine.Engine [generated in 0.00067s] (1,)\n", + "LLM은 언어 모델의 일종입니다.\n", + "좋아요\n", + "2026-06-17 12:03:17,925 INFO sqlalchemy.engine.Engine ROLLBACK\n" + ] + } + ], + "execution_count": 53 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-17T03:03:17.950832461Z", + "start_time": "2026-06-17T03:03:17.933917669Z" + } + }, + "cell_type": "code", + "source": [ + "# 💡 이 셀을 실행해야 'Session'이라는 이름이 메모리에 등록됩니다!\n", + "from sqlalchemy import create_engine\n", + "from sqlalchemy.orm import sessionmaker\n", + "\n", + "engine = create_engine(\"sqlite:///users.db\") # 본인의 engine 설정 코드\n", + "Session = sessionmaker(bind=engine)" + ], + "id": "7d17932707f7f879", + "outputs": [], + "execution_count": 54 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-17T03:03:17.968159853Z", + "start_time": "2026-06-17T03:03:17.952470603Z" + } + }, + "cell_type": "code", "source": "", - "id": "de1ebcd8f162fd42" + "id": "48795cdb32be042b", + "outputs": [], + "execution_count": 54 } ], "metadata": {