Web Service Structure
A. 클라이언트 사이드 (Client-Side)
•
Web Browser/Client: 사용자가 직접 상호작용하는 인터페이스
•
DNS: 도메인 이름을 IP 주소로 변환하는 시스템
•
HTML 내부: DOM(Document Object Model) 구조로 페이지 구성
B. 네트워크 통신 계층
•
Internet: 클라이언트와 서버를 연결하는 네트워크 인프라
•
TCP/IP: 데이터 전송 프로토콜
•
HTTP: 웹 통신 프로토콜
C. 서버 사이드 (Server-Side)
•
Web Server: 클라이언트 요청 처리
•
HDD: 데이터 저장소
•
HTML/CSS/기타 파일들: 웹 페이지 구성 요소
1.
웹 서비스의 작동 방식:
A. 기본 GET 요청 흐름
1.
사용자가 브라우저에서 URL 입력
2.
DNS(Domain Name System)를 통해 도메인을 IP로 변환
3.
HTTP GET 요청이 서버로 전송
4.
서버는 요청된 리소스(HTML + CSS + 기타)를 응답
5.
브라우저는 받은 데이터를 DOM으로 파싱하여 화면 렌더링
B. POST 요청 흐름 (예: 폼 제출)
1.
사용자가 폼에서 "Click" 이벤트 발생
2.
HTTP POST 요청이 서버로 전송
3.
서버는 요청을 처리하고 적절한 응답 반환
4.
핵심 용어 및 개념 설명:
•
HTTP (HyperText Transfer Protocol)
◦
GET: 리소스 요청 메소드
◦
POST: 데이터 제출 메소드
◦
Request/Response: 클라이언트-서버 간 통신 방식
•
DOM (Document Object Model)
◦
HTML 문서의 프로그래밍 인터페이스
◦
동적 페이지 조작을 위한 구조화된 표현
•
TCP/IP
◦
인터넷 통신의 기본 프로토콜
◦
데이터 패킷의 안정적인 전송 보장
FastAPI
#### What is FastAPI ?
•
Python 기반의 현대적이고 빠른 웹 프레임워크
•
비동기 처리를 지원하는 ASGI 서버 기반
#### 특징
•
자동 API 문서화 (OpenAPI/Swagger)
•
Python 타입 힌트 기반의 데이터 검증
•
비동기 처리 지원
•
높은 성능과 빠른 개발 속도
#### 장점
•
빠른 실행 속도 (Node.js, Go와 비견됨)
•
직관적인 코드 작성
•
자동화된 데이터 검증
•
풍부한 문서화 기능
#### 단점
•
학습 리소스가 Django나 Flask에 비해 적음
#### Toy Project/Code
######## Structure
.
├── main.py
├── static
│ ├── form.html
│ ├── form.js
│ ├── pics.png
│ ├── profile.jpg
│ └── style.css
└── templates
└── intro.html
Python
복사
######## main.py
from fastapi import FastAPI, Request
# FastAPI: 웹 프레임워크 클래스로, ASGI 서버를 구현하는 핵심 클래스
# Request: HTTP 요청 객체를 처리하는 클래스, 클라이언트의 요청 정보를 담음
from fastapi.responses import FileResponse
# FileResponse: 파일을 HTTP 응답으로 보내는 클래스
# 정적 파일(HTML, 이미지 등)을 클라이언트에게 전송할 때 사용
from fastapi.staticfiles import StaticFiles
# StaticFiles: 정적 파일(CSS, JavaScript, 이미지 등)을 서빙하기 위한 클래스
# 웹 서버가 변경 없이 그대로 제공하는 파일들을 관리
from fastapi.templating import Jinja2Templates
# Jinja2Templates: Python의 템플릿 엔진
# HTML 파일에 동적 데이터를 삽입할 수 있게 해주는 기능 제공
from pydantic import BaseModel
# BaseModel: 데이터 검증과 설정을 위한 Pydantic의 기본 모델 클래스
from typing import List
# FastAPI 인스턴스 생성
app = FastAPI()
# 정적 파일 디렉토리 설정 (/static 경로로 접근 가능)
app.mount("/static", StaticFiles(directory="static"), name="static")
# /static : 웹 브라우저에서 파일을 요청할 때 사용할 경로
# StaticFiles(directory="static"): "static"이라는 실제 폴더와 연결하라는 설정
# > 웹에서 "/static/style.css"로 요청이 오면 → 실제 "static/style.css" 파일을 찾아 제공
# name="static": 템플릿에서 정적 파일 경로를 동적으로 생성할 때 사용하는 식별자
# > url_for('static', ...) 처럼 사용할 때 'static'이라는 이름으로 참조
# Jinja2 템플릿 엔진 설정 (templates 디렉토리 사용)
templates = Jinja2Templates(directory="templates")
# Profile 데이터 모델 정의
class Profile(BaseModel):
name: str
title: str
skills: List[str]
# 루트 경로(/) GET 요청 처리
@app.get("/")
def root():
return FileResponse("static/form.html")
# 루트 경로(/) POST 요청 처리
@app.post("/")
async def create_profile(request: Request, profile: Profile):
return templates.TemplateResponse(
"intro.html",
{
"request": request,
"name": profile.name,
"title": profile.title,
"skills": profile.skills
}
)
Python
복사
######## 실제 Post에서 데이터의 흐름
1.
클라이언트 -> 서버: POST / 요청
// form.js에서
const response = await fetch('/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
Python
복사
2.
서버 -> 클라이언트: intro.html 응답
# main.py에서
@app.post("/")
async def create_profile(request: Request, profile: Profile):
return templates.TemplateResponse("intro.html", {...})
Python
복사
3.
클라이언트(브라우저): HTML 반환된거 받고 파싱시작 (아래 코드는 html 교체과정)
// form.js에서 응답을 받아 HTML을 교체
if (response.ok) {
const html = await response.text();
document.documentElement.innerHTML = html;
}
Python
복사
4.
클라이언트(브라우저): intro.html의 리소스 태그들 발견 (랜더링을 위해서 추가 데이터가 필요한 상황)
<link rel="stylesheet" href="/static/style.css">
<img src="/static/pics.png" alt="프로필 이미지">
Python
복사
5.
클라이언트 -> 서버: 각 리소스 요청
•
CSS 파일을 위한 GET /static/style.css 요청
•
이미지를 위한 GET /static/pics.png 요청
6.
서버: mount 설정으로 리소스 응답
# main.py의 이 설정으로 static 폴더의 파일들이 자동으로 제공됨
app.mount("/static", StaticFiles(directory="static"), name="static")
Python
복사
→ 구체적으로 클라이언트가 ‘GET /static/style.css’를 날려서 서버에 도착하면,
•
"아, URL이 /static으로 시작하네? → "그러면 이건 mount로 설정한 정적 파일 요청이구나" →"static 폴더에서 style.css를 찾아서 보내줘야지"의 논리적 흐름을 탐
•
이렇게 함으로써:
1.
각 정적 파일마다 별도의 라우트 함수를 만들 필요가 없어짐
@app.get("/static/style.css")
def get_css():
return FileResponse("static/style.css")
@app.get("/static/form.js")
def get_js():
return FileResponse("static/form.js")
@app.get("/static/pics.png")
def get_image():
return FileResponse("static/pics.png")
Python
복사
2.
파일 시스템의 보안을 유지하면서도 필요한 파일들을 제공할 수 있음