TypeScript

프론트엔드와 백엔드 간 Enum 동기화

코샵 2024. 12. 7. 10:24
반응형

소개

프론트엔드와 백엔드에서 동일한 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 또는 공유 코드 생성기
  • 대규모 프로젝트: 공유 패키지 또는 하이브리드 접근법

어떤 방법을 선택하든 중요한 것은 일관성 있는 유지보수와 개발자 경험을 고려하는 것입니다.