////
Search
📗

6. FastAPI에서 Schema와 Model을 분리하는 이유

5. FastAPI 도메인 구조 이해하기 (Router, Schema, Service, Repository, Model) 에서 FastAPI에서 자주 사용하는 구조와 구조 내에 있는 각 계층에 대해 설명했었다.
계층 중에는 ModelSchema 계층이 존재하는데 처음에는 두 계층이 굉장히 비슷해보였고 차이점을 알지 못했었다. 예를 들어 Model에 있는 UserSchema를 보면 구조도 거의 비슷하다.
이와 같이 구조가 비슷하기 때문에 하나로 통합하는 것이 불필요한 코드도 줄일 수 있을 것으로 생각했다. 하지만 실제 개발에서는 SchemaModel을 분리하는 것이 매우 중요하다는 것을 알게 되었다.
이 글에서는 다음 내용을 정리해보고자 한다.
Model이 무엇인지
Schema가 무엇인지
왜 두 계층을 분리해야 하는지
Model
Model데이터베이스 테이블 구조를 정의하는 클래스이다. FastAPI에서는 보통 SQLAlchemy ORM 모델을 사용한다. 예를 들어 사용자 테이블을 정의하면 다음과 같다.
from sqlalchemy import Column, Integer, String from app.db.base import Base class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True) name = Column(String) email = Column(String)
Python
복사
이 모델은 실제 데이터베이스의 users 테이블과 매핑되고 역할은 다음과 같다.
데이터베이스 테이블 구조 정의
컬럼 타입 정의
테이블 관계 정의
ORM 객체 생성
쉽게 말하면 Model데이터베이스와 연결된 객체이다.
Schema
Schema는 API 요청(Request)응답(Response)의 데이터 구조를 정의하는 클래스이다. FastAPI에서는 Pydantic을 사용하여 schema를 작성한다. 예를 들어 사용자 생성 요청을 위한 schema는 다음과 같다.
from pydantic import BaseModel, EmailStr class UserCreate(BaseModel): name: str email: EmailStr
Python
복사
응답용 schema는 다음과 같이 작성할 수 있다.
class UserResponse(BaseModel): id: int name: str email: EmailStr class Config: from_attributes = True
Python
복사
Schema의 주요 역할은 다음과 같다.
요청 데이터 검증
데이터 타입 검사
직렬화 / 역직렬화
응답 데이터 구조 정의
다시 말해 SchemaAPI에서 사용하는 데이터 구조라고 볼 수 있다.
Model과 Schema의 차이
구분
Model
Schema
목적
데이터베이스 구조 정의
API 데이터 구조 정의
사용 라이브러리
SQLAlchemy
Pydantic
사용 위치
Repository / DB
Router / API
역할
DB 테이블 매핑
요청 / 응답 검증
Schema와 Model을 분리해야 하는 이유
이 글을 시작할 때, SchemaModel 계층의 내용이 크게 다르지 않아서 분리하는것보다 오히려 통합하는 것이 좋지 않을까 고민했다고 했다. 하지만 ModelSchema 계층의 역할이 다르고 차이점도 명확하기 때문에 아래와 같은 이유로 분리하는 것이 좋다.
1.
보안 문제
데이터베이스는 외부로 노출되면 안 되는 정보가 포함될 수 있다.
예를 들어 사용자 테이블이 다음과 같다고 가정해 보자.
class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True) email = Column(String) password = Column(String)
Python
복사
만약 Model을 그대로 API 응답으로 반환하면 다음과 같은 문제가 생긴다.
{ "id": 1, "email": "test@test.com", "password": "hashed_password" }
JSON
복사
비밀번호가 그대로 노출될 수 있다.
하지만 Schema를 활용해 응답 형태를 지정하면 필요한 필드만 사용하여 응답할 수 있다.
class UserResponse(BaseModel): id: int email: str
Python
복사
{ "id": 1, "email": "test@test.com" }
JSON
복사
2.
요청 데이터 검증
Schema는 입력 데이터 검증을 담당한다. 예를 들어 이메일 형식을 검증할 수 있다.
class UserCreate(BaseModel): name: str email: EmailStr
Python
복사
위와 같이 유저 생성 요청에 대한 형식이 정해져 있을 때 사용자가 다음과 같은 요청을 보낸다고 하자.
{ "name": "James", "email": "not-email" }
JSON
복사
이럴 경우 email 형식이 맞지 않기 때문에 FastAPI자동으로 에러를 발생시킨다. 즉, Schema는 API 입력 데이터를 안전하게 만드는 역할도 수행한다.
3.
API와 DB 구조 분리
API에서 사용하는 데이터 구조와 DB 구조는 항상 같지 않다. 예를 들어 데이터베이스에는 다음과 같은 필드가 있을 수 있다.
id email password created_at updated_at
Plain Text
복사
하지만 API 응답은 이렇게 간단할 수도 있다.
id email
Plain Text
복사
이 것은 보안 문제와도 연결 된 내용인데, 이처럼 API 구조DB 구조독립적으로 관리하기 위해 SchemaModel을 분리한다.
4.
목적에 맞는 다양한 데이터 구조 생성 가능
예를 들어 사용자 도메인에서는 다음과 같은 schema가 필요할 수 있다.
UserCreate UserUpdate UserResponse UserListResponse
Plain Text
복사
class UserCreate(BaseModel): name: str email: EmailStr class UserUpdate(BaseModel): name: str | None = None email: EmailStr | None = None
Python
복사
이처럼 API 목적에 맞는 다양한 데이터 구조를 만들 수 있다.
정리
FastAPI에서 Schema와 Model 계층을 분리하는 이유에 대해 알아보았다. 그 큰 이유는 다음과 같다.
1.
데이터 보안
2.
요청 데이터 검증
3.
API 구조와 DB 구조 분리
4.
목적에 맞는 다양한 데이터 구조 생성 가능
Schema요청 데이터 검증응답 데이터 변환을 담당하며, Model과의 구조가 비슷해 보일 수 있지만 프로젝트 규모가 커질수록 SchemaModel의 분리는 매우 중요해진다. FastAPI에서 유지보수하기 좋은 구조를 만들기 위해서는 이 두 개의 역할을 명확히 구분하는 것이 좋다.