단일 책임 원칙이란 (feat. FastAPI)

Dev Smile·2024년 12월 15일
1

FastAPI

목록 보기
6/10

단일 책임 원칙(Single Responsibility Principle, SRP)이란?

단일 책임 원칙은 SOLID 원칙 중 하나로, 하나의 클래스 또는 모듈은 오직 하나의 책임만 가져야 한다는 원칙입니다. 즉, 특정 클래스나 함수가 하나의 명확한 역할을 수행하고, 변경의 이유가 하나여야 한다는 뜻입니다. 이를 통해 코드는 이해하기 쉽고, 유지보수와 확장이 용이해집니다.


백엔드 코드에서 SRP 적용 방법

백엔드 코드에 이 원칙을 적용하기 위해서는 다음과 같은 단계를 고려할 수 있습니다.

  1. 역할(Role) 구분

    • API 요청 처리를 위한 엔드포인트, 비즈니스 로직(서비스), 데이터 접근 계층(리포지토리 또는 DAO)을 분리해 각각의 파일이나 클래스로 구성한다.
  2. 책임(Responsibility) 분리

    • 엔드포인트는 HTTP 요청·응답 로직에 집중하고, 서비스 계층은 핵심 비즈니스 로직만 관리하며, 데이터 계층은 DB와의 연동만 책임진다.
  3. 의존성 주입(Dependency Injection)

    • 각 계층을 서로 독립적으로 작성한 뒤 필요한 곳에서 의존성을 주입해 결합도를 낮춘다.

이것을 FastAPI와 같은 프레임워크에서는 다음과 같은 방식으로 SRP를 적용할 수 있습니다.

  1. 라우팅과 비즈니스 로직 분리

    • API 엔드포인트의 정의는 라우팅 파일에서 관리하고, 비즈니스 로직은 별도의 서비스 계층으로 분리합니다.
  2. 데이터베이스 접근 로직 분리

    • 데이터베이스와의 상호작용은 별도의 리포지토리 클래스로 분리하여 관리합니다.
  3. 유틸리티 함수와 공통 기능 분리

    • 공통적으로 사용하는 함수(예: 인증, 데이터 검증)는 별도의 유틸리티 모듈에 작성합니다.

FastAPI 예시: SRP 적용

1. 프로젝트 구조 설계

project/
├── app/
│   ├── main.py               # FastAPI 애플리케이션 초기화
│   ├── routes/
│   │   └── user_routes.py    # 사용자 관련 API 엔드포인트 정의
│   ├── services/
│   │   └── user_service.py   # 사용자 비즈니스 로직
│   ├── repositories/
│   │   └── user_repository.py # 데이터베이스 접근 로직
│   ├── models/
│   │   └── user.py           # 사용자 모델 정의
│   ├── utils/
│   │   └── validators.py     # 공통 유틸리티(예: 데이터 검증)

2. 코드 작성

(1) models/user.py - 데이터 모델
from pydantic import BaseModel

class User(BaseModel):
    id: int
    username: str
    email: str
    is_active: bool

(2) repositories/user_repository.py - 데이터베이스 접근 로직
from typing import List, Optional
from app.models.user import User

class UserRepository:
    def __init__(self):
        # 임시 데이터베이스로 리스트 사용
        self.users = []

    def get_user_by_id(self, user_id: int) -> Optional[User]:
        return next((user for user in self.users if user.id == user_id), None)

    def get_all_users(self) -> List[User]:
        return self.users

    def create_user(self, user: User) -> User:
        self.users.append(user)
        return user

(3) services/user_service.py - 비즈니스 로직
from typing import List
from app.models.user import User
from app.repositories.user_repository import UserRepository

class UserService:
    def __init__(self, repository: UserRepository):
        self.repository = repository

    def get_user_details(self, user_id: int) -> User:
        user = self.repository.get_user_by_id(user_id)
        if not user:
            raise ValueError("User not found")
        return user

    def create_user(self, user_data: User) -> User:
        # 추가 비즈니스 로직 (예: 유효성 검사)
        if not user_data.email:
            raise ValueError("Email is required")
        return self.repository.create_user(user_data)

(4) routes/user_routes.py - 라우팅 정의
from fastapi import APIRouter, HTTPException
from app.models.user import User
from app.services.user_service import UserService
from app.repositories.user_repository import UserRepository

router = APIRouter()
repository = UserRepository()
service = UserService(repository)

@router.get("/users/{user_id}")
def get_user(user_id: int):
    try:
        return service.get_user_details(user_id)
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))

@router.post("/users")
def create_user(user: User):
    try:
        return service.create_user(user)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))

(5) main.py - 애플리케이션 초기화
from fastapi import FastAPI
from app.routes.user_routes import router as user_router

app = FastAPI()

app.include_router(user_router, prefix="/api")

SRP가 적용된 장점

  1. 유지보수 용이성: 라우트, 서비스, 리포지토리가 분리되어 특정 로직을 변경해도 다른 부분에 영향을 미치지 않음.
  2. 재사용성 증가: 서비스 계층과 리포지토리 계층은 다른 API에서 재사용 가능.
  3. 테스트 용이성: 각 계층별로 단위 테스트 작성이 가능.

0개의 댓글

관련 채용 정보