소개
프론트엔드와 백엔드에서 동일한 Enum을 사용해야 할 때, 이를 효과적으로 관리하고 동기화하는 것은 중요한 과제입니다. 이번 글에서는 FastAPI와 React 환경에서 Enum을 일관성 있게 관리하는 다양한 방법을 알아보겠습니다.
1. OpenAPI Specification을 활용한 방법
FastAPI 백엔드 설정
# backend/enums.py
from enum import Enum
class UserRole(str, Enum):
ADMIN = "ADMIN"
USER = "USER"
GUEST = "GUEST"
# backend/models.py
from pydantic import BaseModel
from .enums import UserRole
class User(BaseModel):
name: str
role: UserRole
프론트엔드에서 Enum 자동 생성
// scripts/generate-enums.ts
import { generate } from 'openapi-typescript-codegen';
async function generateEnums() {
await generate({
input: 'http://localhost:8000/openapi.json',
output: './src/generated',
exportEnums: true
});
}
generateEnums();
장점 |
단점 |
자동 동기화 가능 |
추가 빌드 스텝 필요 |
타입 안전성 보장 |
초기 설정이 복잡 |
API 스펙과 일치 보장 |
실시간 변경 반영 어려움 |
2. 공유 코드 생성기 활용
Python 스크립트로 Enum 정의
# scripts/generate_enums.py
enum_definitions = {
"UserRole": {
"ADMIN": "ADMIN",
"USER": "USER",
"GUEST": "GUEST"
},
"PostStatus": {
"DRAFT": "DRAFT",
"PUBLISHED": "PUBLISHED",
"ARCHIVED": "ARCHIVED"
}
}
def generate_python_enums():
with open("backend/enums.py", "w") as f:
f.write("from enum import Enum\n\n")
for enum_name, values in enum_definitions.items():
f.write(f"class {enum_name}(str, Enum):\n")
for key, value in values.items():
f.write(f" {key} = \"{value}\"\n")
f.write("\n")
def generate_typescript_enums():
with open("frontend/src/enums.ts", "w") as f:
for enum_name, values in enum_definitions.items():
f.write(f"export enum {enum_name} {{\n")
for key, value in values.items():
f.write(f" {key} = \"{value}\",\n")
f.write("}\n\n")
if __name__ == "__main__":
generate_python_enums()
generate_typescript_enums()
장점 |
단점 |
중앙 집중식 관리 |
수동 실행 필요 |
커스텀 로직 추가 용이 |
빌드 프로세스 통합 필요 |
다양한 포맷 지원 가능 |
추가 스크립트 관리 필요 |
3. 공유 패키지 활용
NPM 패키지 생성
// shared-enums/index.ts
export enum UserRole {
ADMIN = "ADMIN",
USER = "USER",
GUEST = "GUEST"
}
// package.json
{
"name": "@your-org/shared-enums",
"version": "1.0.0",
"types": "index.ts",
"scripts": {
"build": "tsc",
"generate-python": "node scripts/generate-python.js"
}
}
Python 패키지 자동 생성
// scripts/generate-python.js
const fs = require('fs');
const path = require('path');
const template = `
from enum import Enum
class UserRole(str, Enum):
ADMIN = "ADMIN"
USER = "USER"
GUEST = "GUEST"
`;
fs.writeFileSync(
path.join(__dirname, '../python/shared_enums.py'),
template
);
장점 |
단점 |
버전 관리 용이 |
패키지 배포 과정 필요 |
재사용성 높음 |
업데이트 프로세스 복잡 |
의존성 관리 명확 |
초기 설정 복잡 |
4. REST API를 통한 동적 동기화
FastAPI 엔드포인트
# backend/routes/enums.py
from fastapi import APIRouter
from enum import Enum
from typing import Dict
router = APIRouter()
class UserRole(str, Enum):
ADMIN = "ADMIN"
USER = "USER"
GUEST = "GUEST"
@router.get("/enums")
async def get_enums() -> Dict:
return {
"UserRole": {e.name: e.value for e in UserRole}
}
React에서 Enum 동적 로드
// frontend/src/hooks/useEnums.ts
import { useQuery } from 'react-query';
export const useEnums = () => {
return useQuery('enums', async () => {
const response = await fetch('/api/enums');
return response.json();
});
};
// 사용 예시
const EnumExample = () => {
const { data: enums } = useEnums();
return (
<select>
{Object.entries(enums?.UserRole ?? {}).map(([key, value]) => (
<option key={key} value={value}>
{key}
</option>
))}
</select>
);
};
장점 |
단점 |
실시간 동기화 가능 |
초기 로딩 필요 |
동적 업데이트 가능 |
네트워크 의존성 |
구현 간단 |
타입 안전성 부족 |
5. 하이브리드 접근법
기본값 정의 (TypeScript)
// frontend/src/enums.ts
export enum UserRole {
ADMIN = "ADMIN",
USER = "USER",
GUEST = "GUEST"
}
// frontend/src/hooks/useEnums.ts
import { UserRole } from '../enums';
export const useEnums = () => {
const { data } = useQuery('enums', async () => {
const response = await fetch('/api/enums');
return response.json();
}, {
initialData: {
UserRole: Object.entries(UserRole).reduce((acc, [key, value]) => ({
...acc,
[key]: value
}), {})
}
});
return data;
};
장점 |
단점 |
초기값 제공 |
코드 중복 가능성 |
타입 안전성 |
관리 포인트 증가 |
유연한 업데이트 |
동기화 로직 필요 |
결론
프로젝트의 규모와 요구사항에 따라 적절한 방법을 선택하는 것이 중요합니다:
- 소규모 프로젝트: REST API를 통한 동적 동기화
- 중규모 프로젝트: OpenAPI Specification 또는 공유 코드 생성기
- 대규모 프로젝트: 공유 패키지 또는 하이브리드 접근법
어떤 방법을 선택하든 중요한 것은 일관성 있는 유지보수와 개발자 경험을 고려하는 것입니다.