init
This commit is contained in:
BIN
01_fastapi_first_step/.DS_Store
vendored
Normal file
BIN
01_fastapi_first_step/.DS_Store
vendored
Normal file
Binary file not shown.
88
01_fastapi_first_step/DESCRIPTION.md
Normal file
88
01_fastapi_first_step/DESCRIPTION.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# [수업 자료] FastAPI 첫 걸음: 간단한 채팅 프로그램 만들기
|
||||
|
||||
이 자료는 FastAPI를 사용하여 기본적인 클라이언트-서버 구조의 채팅 프로그램을 이해하기 위한 설명서입니다. PPT 제작 시 각 섹션을 슬라이드로 활용할 수 있도록 구성되었습니다.
|
||||
|
||||
---
|
||||
|
||||
## Slide 1: 프로젝트 개요
|
||||
### "FastAPI로 만드는 나만의 채팅 서버"
|
||||
- **목표**: Python의 현대적인 웹 프레임워크인 FastAPI를 이용해 메시지를 주고받는 서버와 클라이언트를 구현합니다.
|
||||
- **핵심 기술**:
|
||||
- **FastAPI**: 빠르고 생산적인 Python 웹 프레임워크
|
||||
- **Uvicorn**: ASGI 서버로 FastAPI 앱을 실행
|
||||
- **Pydantic**: 데이터 검증 및 설정을 위한 라이브러리
|
||||
- **Requests**: Python에서 HTTP 요청을 보내는 라이브러리
|
||||
|
||||
---
|
||||
|
||||
## Slide 2: 전체 구조 (Architecture)
|
||||
### "누가 무엇을 하나요?"
|
||||
|
||||

|
||||
|
||||
- **서버 (Server - `server.py`)**:
|
||||
- 메시지를 저장하는 장소 (메모리 내 리스트)
|
||||
- 클라이언트의 요청을 기다리고 처리함 (API 제공)
|
||||
- 메시지에 '언제' 보냈는지 시간을 기록함
|
||||
- **클라이언트 (Client - `client.py`)**:
|
||||
- 사용자와 직접 소통하는 화면 (터미널)
|
||||
- 서버에 메시지를 보내거나(POST), 새로운 메시지를 가져옴(GET)
|
||||
|
||||
---
|
||||
|
||||
## Slide 3: 데이터의 약속 (JSON & Model)
|
||||
### "서버와 클라이언트는 어떻게 대화하나요?"
|
||||
- **데이터 형식 (JSON)**:
|
||||
```json
|
||||
{
|
||||
"name": "고도푸",
|
||||
"message": "안녕하세요!"
|
||||
}
|
||||
```
|
||||
- **데이터 모델 (Pydantic)**:
|
||||
- 서버는 어떤 데이터가 들어올지 미리 정의합니다.
|
||||
- `name`과 `message`가 문자열(str)인지 자동으로 검사합니다.
|
||||
|
||||
---
|
||||
|
||||
## Slide 4: 메시지 전송 (POST Request)
|
||||
### "서버로 메시지 보내기"
|
||||
|
||||

|
||||
|
||||
1. 사용자가 내용을 입력합니다.
|
||||
2. 클라이언트가 서버의 `/messages` 주소로 데이터를 실어 보냅니다. (POST)
|
||||
3. 서버는 현재 시간을 계산하여 메시지와 함께 저장합니다.
|
||||
- **서버 로직**: `datetime.now().strftime(...)`를 사용하여 친숙한 시간 형식으로 변환
|
||||
|
||||
---
|
||||
|
||||
## Slide 5: 메시지 확인 (GET Request & Polling)
|
||||
### "새로운 메시지 가져오기"
|
||||
- **효율적인 조회**: 모든 메시지를 매번 다 가져오는 것은 비효율적입니다.
|
||||
- **인덱스 활용 (`start` 파라미터)**:
|
||||
- 클라이언트는 "내가 5번까지 봤으니, 6번부터 줘!"라고 요청합니다.
|
||||
- 예: `/messages?start=5`
|
||||
- **결과**: 클라이언트는 아직 보지 못한 '새 소식'만 화면에 출력합니다.
|
||||
|
||||
---
|
||||
|
||||
## Slide 6: 실습 흐름 (Workflow)
|
||||
### "직접 실행해 봅시다!"
|
||||
1. **서버 실행**: `python server.py` (비서가 전화를 기다리는 상태)
|
||||
2. **클라이언트 실행**: `python client.py` (사용자가 대화를 시작)
|
||||
3. **상호작용**:
|
||||
- 1번 선택: 메시지 작성 및 전송
|
||||
- 2번 선택: 새 메시지 확인 (다른 사람이 보낸 것도 보임!)
|
||||
- 3번 선택: 종료
|
||||
|
||||
---
|
||||
|
||||
## Slide 7: 요약 및 발전 방향
|
||||
### "무엇을 배웠나요?"
|
||||
- **HTTP Method**: 데이터를 보낼 때는 POST, 가져올 때는 GET을 사용합니다.
|
||||
- **Stateless**: 서버는 클라이언트가 누구인지 기억하지 않지만, 요청에 따라 데이터를 관리합니다.
|
||||
- **Next Step**:
|
||||
- 데이터베이스(DB) 연결하여 재시작해도 대화 유지하기
|
||||
- 웹 브라우저 화면(HTML/CSS)으로 예쁘게 만들기
|
||||
- 실시간 통신(WebSocket) 적용해보기
|
||||
105
01_fastapi_first_step/README.md
Normal file
105
01_fastapi_first_step/README.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# 프로젝트 목표
|
||||
|
||||
- fastapi 학습을 위한 간단한 채팅 프로그램 구현
|
||||
|
||||
# 구성요소 및 요구사항
|
||||
|
||||
- 서버: server.py
|
||||
- 클라이언트로 부터 메시지를 수신, 수신한 메시지를 리스트로 저장
|
||||
- 수신한 메시지는 다음과 같은 형식으로 전달됨
|
||||
```json
|
||||
{
|
||||
"name": "사용자 이름",
|
||||
"message": "메시지 내용"
|
||||
}
|
||||
```
|
||||
- 메시지에 'timestamp' 필드를 추가하여 현재 시간을 저장함. 다음과 같은 항목으로 리스트에 저장함.
|
||||
```json
|
||||
{
|
||||
"name": "사용자 이름",
|
||||
"message": "메시지 내용",
|
||||
"timestamp": "2022-01-01 00:00:00"
|
||||
}
|
||||
```
|
||||
- 클라이언트에 메시지 전송
|
||||
- 클라이언트에 메시지를 전송함. 클라이언트로 부터 query message로 `start` 매개변수를 전달받으며, `start` 매개변수 이후 메시지를 모두 전송함.
|
||||
예: `start=2` 일 경우 messages[2:]를 전송함.
|
||||
|
||||
- 클라이언트: client.py
|
||||
- 초기화: 프로그램 시작 시, 사용자의 이름을 물어봄
|
||||
```bash
|
||||
이름을 입력해주세요 >
|
||||
```
|
||||
- 작업 수행을 위한 프롬프트 출력: 다음과 같은 프롬프트를 출력하고, 1~3사이 숫자를 입력받습니다. 만약 잘못된 값을 입력하면 다시 입력받습니다.
|
||||
```bash
|
||||
수행할 작업을 선택해주세요:
|
||||
1. 메시지 전송
|
||||
2. 수신한 메시지 보기
|
||||
3. 종료
|
||||
```
|
||||
- 메시지 전송: 전송할 메시지를 입력받고, 서버로 전송합니다. 전송할 메시지 형식은 다음과 같습니다.
|
||||
```json
|
||||
{
|
||||
"name": "사용자 이름",
|
||||
"message": "메시지 내용"
|
||||
}
|
||||
```
|
||||
- 서버로 부터 메시지를 수신:
|
||||
- 수신한 메시지는 다음과 같은 형식을 가짐
|
||||
```json
|
||||
{
|
||||
"name": "사용자 이름",
|
||||
"message": "메시지 내용",
|
||||
"timestamp": "2022-01-01 00:00:00"
|
||||
}
|
||||
```
|
||||
|
||||
- 수신한 메시지를 다음과 같은 형식으로 출력함
|
||||
```bash
|
||||
[2022-01-01 00:00:00] 사용자 이름: 메시지 내용
|
||||
```
|
||||
|
||||
- `start` 변수를 통해 메시지 수신을 시작할 인덱스를 저장하고 있어야 함. 이 값을 메시지를 수신 요청시 서버로 `start` 매개변수를 전달하여야 하며, 메시지를 수신한 수 만큼 `start` 변수를 증가시켜야 함.
|
||||
|
||||
# Specification
|
||||
## Server
|
||||
- 다음과 같은 API를 제공함
|
||||
|
||||
### 1. 메시지 전송
|
||||
- **URL**: `/messages`
|
||||
- **Method**: `POST`
|
||||
- **Request Body**:
|
||||
```json
|
||||
{
|
||||
"name": "사용자 이름",
|
||||
"message": "메시지 내용"
|
||||
}
|
||||
```
|
||||
- **Response**:
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"received": {
|
||||
"name": "사용자 이름",
|
||||
"message": "메시지 내용",
|
||||
"timestamp": "2022-01-01 00:00:00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 메시지 수신 (조회)
|
||||
- **URL**: `/messages`
|
||||
- **Method**: `GET`
|
||||
- **Query Parameters**:
|
||||
- `start` (int, default=0): 이 인덱스부터의 메시지를 가져옴
|
||||
- **Response**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "사용자 이름",
|
||||
"message": "메시지 내용",
|
||||
"timestamp": "2022-01-01 00:00:00"
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
BIN
01_fastapi_first_step/assets/architecture.png
Normal file
BIN
01_fastapi_first_step/assets/architecture.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
BIN
01_fastapi_first_step/assets/flow.png
Normal file
BIN
01_fastapi_first_step/assets/flow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 442 KiB |
83
01_fastapi_first_step/client.py
Normal file
83
01_fastapi_first_step/client.py
Normal file
@@ -0,0 +1,83 @@
|
||||
import requests
|
||||
import sys
|
||||
|
||||
SERVER_URL = "http://localhost:8000"
|
||||
|
||||
def post_message(user_name, content):
|
||||
"""서버로 메시지를 전송합니다."""
|
||||
payload = {
|
||||
"name": user_name,
|
||||
"message": content
|
||||
}
|
||||
|
||||
try:
|
||||
|
||||
response = requests.post(f"{SERVER_URL}/messages", json=payload)
|
||||
if response.status_code == 200:
|
||||
print("전송 성공!")
|
||||
return True
|
||||
else:
|
||||
print(f"전송 실패: {response.text}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"오류 발생: {e}")
|
||||
return False
|
||||
|
||||
def get_messages(start_index):
|
||||
"""서버로부터 새로운 메시지를 가져옵니다."""
|
||||
try:
|
||||
response = requests.get(f"{SERVER_URL}/messages", params={"start": start_index})
|
||||
|
||||
if response.status_code == 200:
|
||||
new_messages = response.json()
|
||||
if not new_messages:
|
||||
print("새로운 메시지가 없습니다.")
|
||||
else:
|
||||
for msg in new_messages:
|
||||
print(f"[{msg['timestamp']}] {msg['name']}: {msg['message']}")
|
||||
return new_messages
|
||||
else:
|
||||
print(f"메시지 수신 실패: {response.text}")
|
||||
return []
|
||||
|
||||
except Exception as e:
|
||||
print(f"오류 발생: {e}")
|
||||
return []
|
||||
|
||||
def main():
|
||||
print("FastAPI 채팅 클라이언트에 오신 것을 환영합니다!")
|
||||
user_name = input("이름을 입력해주세요 > ").strip()
|
||||
if not user_name:
|
||||
user_name = "Anonymous"
|
||||
|
||||
start_index = 0
|
||||
|
||||
while True:
|
||||
print("\n수행할 작업을 선택해주세요:")
|
||||
print("1. 메시지 전송")
|
||||
print("2. 수신한 메시지 보기")
|
||||
print("3. 종료")
|
||||
|
||||
choice = input("> ").strip()
|
||||
|
||||
if choice == "1":
|
||||
message_content = input("메시지 내용: ").strip()
|
||||
if message_content:
|
||||
post_message(user_name, message_content)
|
||||
else:
|
||||
print("메시지를 입력해주세요.")
|
||||
|
||||
elif choice == "2":
|
||||
new_msgs = get_messages(start_index)
|
||||
start_index += len(new_msgs)
|
||||
|
||||
elif choice == "3":
|
||||
print("프로그램을 종료합니다.")
|
||||
sys.exit(0)
|
||||
|
||||
else:
|
||||
print("잘못된 입력입니다. 1~3 사이의 숫자를 입력해주세요.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
41
01_fastapi_first_step/server.py
Normal file
41
01_fastapi_first_step/server.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from fastapi import FastAPI, Query
|
||||
from pydantic import BaseModel
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
# 메시지 저장을 위한 리스트
|
||||
messages = []
|
||||
|
||||
class Message(BaseModel):
|
||||
name: str
|
||||
message: str
|
||||
|
||||
@app.post("/messages")
|
||||
def post_message(msg: Message):
|
||||
# 현재 시간을 YYYY-MM-DD HH:MM:SS 형식으로 저장
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
new_message = {
|
||||
"name": msg.name,
|
||||
"message": msg.message,
|
||||
"timestamp": timestamp
|
||||
}
|
||||
messages.append(new_message)
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"received": new_message,
|
||||
"timestamp": timestamp
|
||||
}
|
||||
|
||||
@app.get("/messages")
|
||||
def get_messages(start: int = 0):
|
||||
# start 인덱스부터 끝까지의 메시지를 반환
|
||||
return messages[start:]
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
# 터미널에서 직접 실행할 때를 위해 uvicorn 설정 추가
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
Reference in New Issue
Block a user