47강 구현 중심 ⏱ 약 30분

 

0. 학습 목표

→ 완성된 프로젝트를 README로 정리하고, 처음 보는 사람도 바로 실행할 수 있는 상태로 마무리합니다.

더보기

이번 글은 구현 중심 강의입니다.

38강부터 46강까지, FastAPI WebSocket 서버와 PySide6 데스크톱 앱, 웹 관리창, MySQL 연동까지 하나의 프로젝트로 완성했습니다. 마지막으로 해야 할 일은 이 프로젝트를 "처음 보는 사람도 클론해서 바로 실행할 수 있도록" 정리하는 것입니다.

코드는 이미 완성되어 있습니다. 이번 강의에서 새로 작성하는 파일은 README.mdrequirements.txt 두 개입니다. 그리고 전체 시연 순서를 한 번 정리합니다.

구분 내용
만들 것 requirements.txt — 의존 패키지 목록
README.md — 프로젝트 소개, 설치, 실행 방법
확인할 것 README만 보고 처음부터 끝까지 실행되는지, 시연 흐름이 자연스러운지

 

0.2 최종 프로젝트 구조

이번 강의를 마치면 프로젝트 구조는 다음과 같습니다.

fastapi-chat/
├── server/
│   └── app.py              ← FastAPI 서버 (완성, 변경 없음)
├── desktop/
│   └── main.py             ← PySide6 앱 (완성, 변경 없음)
├── requirements.txt        ← ➕ 이번 강의에서 생성
└── README.md               ← ➕ 이번 강의에서 생성

(➕ 새로 생성 · 표시 없음은 변경 없음)

 

0.3 📦 Part 8 구현

part8b_final.zip
└── fastapi-chat/
    ├── server/
    │   └── app.py          ← 46강 완성본 (MySQL 연동 완전체)
    ├── desktop/
    │   └── main.py         ← 44강 완성본 (로그인 + WebSocket)
    ├── requirements.txt    ← 47강 작성본
    └── README.md           ← 47강 작성본 (9단계 시연 순서 포함)
part8b_final.zip
0.01MB

 

1. requirements.txt 만들기

→ 이 프로젝트를 실행하는 데 필요한 패키지를 한 파일에 정리합니다.

더보기

프로젝트 루트(fastapi-chat/)에 requirements.txt를 만듭니다. 이 파일이 있으면 누구든 pip install -r requirements.txt 한 줄로 필요한 패키지를 모두 설치할 수 있습니다.

# requirements.txt
fastapi
uvicorn[standard]
websockets
PySide6
requests
pymysql

버전을 고정하지 않은 이유가 있습니다. 수업용 프로젝트에서는 설치 시점의 최신 안정 버전이 대부분 잘 동작하고, 버전을 고정하면 몇 달 뒤 환경이 달라졌을 때 오히려 설치가 안 되는 상황이 생길 수 있습니다. 실무 프로젝트라면 pip freeze > requirements.txt로 현재 환경의 정확한 버전을 기록하는 것이 좋습니다.

설치 명령어는 다음과 같습니다.

pip install -r requirements.txt

 

2. README.md 만들기

→ 프로젝트 소개부터 설치, DB 준비, 실행 방법까지 README 한 파일에 담습니다.

더보기

README는 GitHub에 올렸을 때 저장소 첫 화면에 표시되는 문서입니다. "이 프로젝트가 무엇인지", "어떻게 실행하는지"를 처음 보는 사람 기준으로 씁니다. 프로젝트 루트에 README.md를 만들고 아래 내용을 그대로 사용하거나 본인 환경에 맞게 수정합니다.

# FastAPI 실시간 채팅 시스템

PySide6 데스크톱 앱과 웹 관리창이 하나의 FastAPI 백엔드를 공유하는 실시간 채팅 프로젝트입니다.

## 기능

- 회원가입 / 로그인 (아이디·비밀번호·닉네임)
- WebSocket 기반 실시간 채팅
- 접속자 목록 실시간 동기화
- 귓속말 (1:1 전송)
- 웹 관리창 — 현재 접속자 및 로그인 기록 조회
- MySQL 영구 저장 — 서버 재시작 후에도 계정·기록 유지

## 기술 스택

| 구분 | 사용 기술 |
|---|---|
| 백엔드 | FastAPI, WebSocket, uvicorn |
| 프론트엔드 (데스크톱) | PySide6, websockets, requests |
| 데이터베이스 | MySQL, pymysql |

## 프로젝트 구조

fastapi-chat/
├── server/
│   └── app.py        # FastAPI 서버
├── desktop/
│   └── main.py       # PySide6 앱
├── requirements.txt
└── README.md

## 설치

Python 3.10 이상과 MySQL 8.0 이상이 필요합니다.

```bash
pip install -r requirements.txt
```

## DB 준비

MySQL에 접속해 아래 SQL을 실행합니다.

```sql
CREATE DATABASE IF NOT EXISTS chat_app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

USE chat_app;

CREATE TABLE IF NOT EXISTS users (
    username    VARCHAR(50)  PRIMARY KEY,
    password    VARCHAR(100) NOT NULL,
    nickname    VARCHAR(50)  NOT NULL
);

CREATE TABLE IF NOT EXISTS login_log (
    id          INT          AUTO_INCREMENT PRIMARY KEY,
    username    VARCHAR(50)  NOT NULL,
    nickname    VARCHAR(50)  NOT NULL,
    login_time  DATETIME     NOT NULL
);
```

그 다음 `server/app.py` 상단의 `DB_CONFIG`에서 `password`를 본인 MySQL 비밀번호로 수정합니다.

## 실행

터미널을 두 개 엽니다.

**터미널 1 — 서버**
```bash
uvicorn server.app:app --reload
```

**터미널 2 — 앱**
```bash
python desktop/main.py
```

서버가 실행된 상태에서 앱을 실행합니다.  
앱에서 회원가입 후 로그인하면 채팅 화면으로 이동합니다.  
웹 브라우저에서 `http://127.0.0.1:8000/admin` 을 열면 관리 페이지가 표시됩니다.

README에서 가장 중요한 두 곳은 DB 준비실행 순서입니다. 서버보다 앱을 먼저 실행하거나, DB_CONFIG 비밀번호를 바꾸지 않으면 바로 오류가 납니다. 이 두 단계를 README에서 명확히 안내하는 것만으로 "왜 안 되지?" 질문의 대부분을 막을 수 있습니다.

 

3. 시연 순서 정리하기

→ 프로젝트의 모든 기능을 자연스럽게 보여주는 시연 흐름을 정리합니다.

더보기

시연은 기능을 하나씩 나열하는 것이 아니라, 하나의 사용 흐름으로 이어지게 보여주는 것이 효과적입니다. 아래 순서대로 진행하면 회원가입부터 관리 페이지까지 자연스럽게 연결됩니다.

1. 서버 실행
   uvicorn server.app:app --reload
   → 터미널에 "Application startup complete." 출력 확인

2. 앱 실행 (터미널 2)
   python desktop/main.py
   → 로그인 화면 표시 확인

3. 회원가입
   아이디: alice / 비밀번호: 1234 / 닉네임: 앨리스
   → "회원가입 완료. 로그인하세요." 메시지 확인

4. 로그인
   아이디: alice / 비밀번호: 1234
   → 채팅 화면으로 전환, "앨리스 님이 입장했습니다." 시스템 메시지 확인

5. 두 번째 앱 인스턴스 실행 (터미널 3)
   python desktop/main.py
   아이디: bob / 비밀번호: 5678 / 닉네임: 밥
   → 두 화면 모두에 "밥 님이 입장했습니다." 표시, 접속자 목록 2명 확인

6. 채팅
   앨리스 화면에서 메시지 입력 → 밥 화면에 실시간으로 표시 확인

7. 귓속말
   앨리스 화면에서 /밥 안녕 (귓속말 형식)
   → 밥 화면에만 표시, 앨리스 화면에는 발신 확인으로만 표시

8. 관리 페이지
   브라우저에서 http://127.0.0.1:8000/admin 열기
   → 현재 접속자 2명, 로그인 기록 2건 이상 표시 확인

9. 서버 재시작 후 영속성 확인
   서버 Ctrl+C → uvicorn 재실행
   → 기존 계정(alice)으로 로그인 성공, 관리 페이지 로그인 기록 유지 확인

9번 "서버 재시작 후 영속성 확인"은 46강에서 MySQL로 전환한 핵심 성과입니다. 시연에서 이 장면을 빠뜨리지 않는 것이 좋습니다. 메모리 저장과 DB 저장의 차이를 말로 설명하는 것보다 직접 보여주는 것이 훨씬 강렬합니다.

✔ 확인 기준: 시연 전에 DB를 초기화(DELETE FROM users; DELETE FROM login_log;)하고, 위 9단계를 처음부터 순서대로 실행했을 때 모두 정상 동작하면 완료.

 

4. 프로젝트 돌아보기

→ 38강부터 47강까지 무엇을 만들었고, 어떤 기술이 왜 쓰였는지 전체 흐름을 정리합니다.

더보기

4.1 강의별 성과 요약

이 강의에서 추가된 것
38강 FastAPI와 WebSocket 개념, 단일 백엔드 큰 그림
39강 ConnectionManager, /ws 엔드포인트, 브라우저로 채팅 검증
40강 JSON 프로토콜(type·sender·text), 닉네임 입장(join 메시지)
41강 user_list 브로드캐스트, 접속자 목록 실시간 동기화
42강 귓속말(whisper), 닉네임으로 WebSocket 역조회
43강 PySide6 앱이 WebSocket으로 연결, QThread + asyncio 비동기 수신
44강 앱 로그인 화면, POST /register · POST /login HTTP API
45강 login_log, GET /admin 웹 관리 페이지
46강 pymysql, users·login_log 테이블로 영구 저장 전환
47강 requirements.txt, README.md, 시연 정리

 

4.2 각 기술을 선택한 이유

기술 이 프로젝트에서 한 역할
FastAPI HTTP API(로그인·회원가입)와 WebSocket을 한 서버에서 처리. async/await으로 여러 클라이언트를 동시에 수용
WebSocket 서버가 클라이언트에게 먼저 메시지를 보낼 수 있어 실시간 채팅·접속자 목록 동기화에 적합
PySide6 데스크톱 GUI. Qt 이벤트 루프와 asyncio를 QThread로 분리해 화면이 멈추지 않게 처리
MySQL + pymysql 서버를 재시작해도 사용자 계정과 로그인 기록이 유지되도록 영구 저장

 

4.3 다음으로 발전시킬 수 있는 방향

이 프로젝트를 기반으로 더 나아갈 수 있는 방향 몇 가지를 정리합니다. 당장 구현할 필요는 없지만, 취업 포트폴리오로 확장하거나 다음 프로젝트를 고를 때 참고할 수 있습니다.

방향 구체적인 내용
비밀번호 보안 강화 bcrypt로 비밀번호를 해시해 저장. 현재는 평문 저장이라 실제 서비스에는 부적합
채팅 내용 저장 chat_log 테이블을 추가해 채팅 기록을 DB에 저장하고 관리 페이지에서 조회
채팅방 분리 방 ID를 WebSocket URL 파라미터로 받아 여러 채팅방을 운영 (/ws/{room_id})
SQLAlchemy 전환 현재 pymysql로 직접 쿼리를 쓰는 방식을 ORM으로 교체해 코드 간결화
JWT 인증 로그인 후 토큰을 발급해 WebSocket 연결 시 인증을 통과한 사용자만 접속 허용