# SQLite + FastAPI + Vue CRUD 구현 계획 > **에이전트 작업자용:** 각 작업은 체크박스(`- [ ]`)로 추적합니다. **목표:** SQLite의 `messages` 테이블 데이터를 FastAPI CRUD API로 조작하고 Vue 화면에서 생성, 조회, 수정, 삭제할 수 있게 만든다. **아키텍처:** 백엔드는 `sqlite3`와 FastAPI로 단순 CRUD 엔드포인트를 제공하고, 프론트엔드는 브라우저용 Vue ESM 번들로 목록형 CRUD UI를 구현한다. 구현은 기존 파일 구조를 유지하면서 최소한의 상태만 추가하고, 마지막에 API와 화면 흐름을 직접 검증한다. **기술 스택:** Python 3, FastAPI, sqlite3, Vue 3 ESM, 정적 HTML --- - [ ] **작업 1: DB 초기화 스크립트 유지 여부 점검** - **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/backend/init_db.py` (수정) - **할 일:** `messages` 테이블 구조가 CRUD 요구사항에 충분한지 확인하고, 초기 데이터 `hello world` 1건을 유지하는 방식으로 정리한다. - **최소 구현 예시:** ```python cursor.execute( "CREATE TABLE IF NOT EXISTS messages (id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT NOT NULL)" ) cursor.execute("DELETE FROM messages") cursor.execute("INSERT INTO messages (content) VALUES (?)", ("hello world",)) ``` - **검증 명령:** `python3 backend/init_db.py` - **기대 결과:** `backend/app.db`가 다시 생성되거나 갱신되고, `messages` 테이블에 `hello world` 1건이 들어간다. - [ ] **작업 2: FastAPI용 요청 모델과 DB 헬퍼 추가** - **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/backend/main.py` (수정) - **할 일:** SQLite 연결 헬퍼, 메시지 직렬화 헬퍼, 입력 검증용 Pydantic 모델을 추가한다. - **최소 구현 예시:** ```python class MessagePayload(BaseModel): content: str @field_validator("content") @classmethod def validate_content(cls, value: str) -> str: stripped_value = value.strip() if not stripped_value: raise ValueError("Content must not be empty") return stripped_value ``` - **검증 명령:** `python3 -m py_compile backend/main.py` - **기대 결과:** API 함수들이 공통 검증 로직과 연결 헬퍼를 재사용할 준비가 된다. - [ ] **작업 3: 메시지 목록 조회 API 구현** - **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/backend/main.py` (수정) - **할 일:** `GET /api/messages` 엔드포인트를 추가해 전체 메시지 목록을 `id` 오름차순으로 반환한다. - **최소 구현 예시:** ```python @app.get("/api/messages") def list_messages() -> list[dict[str, object]]: with get_connection() as connection: rows = connection.execute( "SELECT id, content FROM messages ORDER BY id ASC" ).fetchall() return [serialize_message(row) for row in rows] ``` - **검증 명령:** `python3 -c "from backend.main import list_messages; print(list_messages())"` - **기대 결과:** `hello world`가 포함된 리스트가 출력된다. - [ ] **작업 4: 메시지 생성 API 구현** - **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/backend/main.py` (수정) - **할 일:** `POST /api/messages` 엔드포인트를 추가해 메시지를 저장하고 생성된 `id`와 `content`를 반환한다. - **최소 구현 예시:** ```python @app.post("/api/messages", status_code=201) def create_message(payload: MessagePayload) -> dict[str, object]: with get_connection() as connection: cursor = connection.execute( "INSERT INTO messages (content) VALUES (?)", (payload.content,), ) connection.commit() return {"id": cursor.lastrowid, "content": payload.content} ``` - **검증 명령:** `python3 - <<'PY'\nfrom backend.main import create_message, MessagePayload\nprint(create_message(MessagePayload(content='created from plan')))\nPY` - **기대 결과:** 새 `id`와 저장된 `content`가 출력된다. - [ ] **작업 5: 메시지 수정 API 구현** - **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/backend/main.py` (수정) - **할 일:** `PUT /api/messages/{id}` 엔드포인트를 추가해 해당 메시지를 수정하고, 없는 `id`는 404를 반환한다. - **최소 구현 예시:** ```python @app.put("/api/messages/{message_id}") def update_message(message_id: int, payload: MessagePayload) -> dict[str, object]: with get_connection() as connection: cursor = connection.execute( "UPDATE messages SET content = ? WHERE id = ?", (payload.content, message_id), ) connection.commit() if cursor.rowcount == 0: raise HTTPException(status_code=404, detail="Message not found") return {"id": message_id, "content": payload.content} ``` - **검증 명령:** `python3 - <<'PY'\nfrom backend.main import update_message, MessagePayload\nprint(update_message(1, MessagePayload(content='updated hello world')))\nPY` - **기대 결과:** 수정된 메시지 객체가 출력된다. - [ ] **작업 6: 메시지 삭제 API 구현** - **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/backend/main.py` (수정) - **할 일:** `DELETE /api/messages/{id}` 엔드포인트를 추가해 해당 메시지를 삭제하고 성공 여부를 반환한다. - **최소 구현 예시:** ```python @app.delete("/api/messages/{message_id}") def delete_message(message_id: int) -> dict[str, bool]: with get_connection() as connection: cursor = connection.execute( "DELETE FROM messages WHERE id = ?", (message_id,), ) connection.commit() if cursor.rowcount == 0: raise HTTPException(status_code=404, detail="Message not found") return {"success": True} ``` - **검증 명령:** `python3 - <<'PY'\nfrom backend.main import delete_message\nprint(delete_message(1))\nPY` - **기대 결과:** `{'success': True}`가 출력된다. - [ ] **작업 7: Vue 상태를 목록형 CRUD 구조로 변경** - **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/frontend/src/main.js` (수정) - **할 일:** 단일 `message` 상태를 `messages`, `newContent`, `editingId`, `editingContent` 상태로 교체하고 목록 조회 함수를 만든다. - **최소 구현 예시:** ```javascript const messages = ref([]) const newContent = ref('') const editingId = ref(null) const editingContent = ref('') ``` - **검증 명령:** `python3 -m py_compile backend/main.py` - **기대 결과:** 프론트 로직이 CRUD API 구조와 맞는 상태 이름으로 정리된다. - [ ] **작업 8: Vue 생성/수정/삭제 액션 구현** - **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/frontend/src/main.js` (수정) - **할 일:** `fetch`를 사용해 생성, 수정, 삭제 요청을 보내고 성공 후 목록을 다시 불러오거나 상태를 갱신한다. - **최소 구현 예시:** ```javascript const createMessage = async () => { await fetch(`${API_BASE_URL}/messages`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content: newContent.value.trim() }), }) await loadMessages() } ``` - **검증 명령:** `python3 backend/init_db.py` - **기대 결과:** 프론트에서 각 버튼이 API 호출 함수와 연결된다. - [ ] **작업 9: Vue 템플릿을 목록형 CRUD UI로 교체** - **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/frontend/src/main.js` (수정) - **할 일:** 입력 폼, 목록, 인라인 편집, 빈 상태, 에러 상태를 모두 표시하는 템플릿으로 교체한다. - **최소 구현 예시:** ```html