데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      FastAPI 프로젝트의 결합도 낮추기 전략

      broccoli 25.01.14
      3,204 8 1
      DEVOTEE 요약
      FastAPI 프로젝트에서 모듈 간 결합도를 낮추고 응집도를 높이는 아키텍처 전략은 소프트웨어의 유지보수성과 확장성을 향상시키는 데 중점을 둡니다. 서비스 구현 그룹과 라우터 구현 그룹으로 파일을 그룹핑하여 결합도를 낮추어 코드의 재사용성과 테스트 용이성을 높이는 방법을 소개합니다. 이를 통해 모듈 간 독립성을 강화하고 코드의 가독성과 유지보수성을 높이는 것이 가능해지며, FastAPI를 활용한 개발 생산성을 크게 향상시킬 수 있습니다.
      DEVOTEE 추천 블로그

      서론

      소프트웨어 개발에서 결합도(Coupling)를 낮추고 응집도(Cohesion)를 높이는 것은 유지보수성과 확장성을 향상시키는 핵심 전략입니다.

      특히 웹 애플리케이션 개발에서 모듈간의 결합도를 낮추면 코드의 재사용성과 테스트 용이성이 크게 향상됩니다.

      이번 글에서는 FastAPI 프로젝트에서 파일들을 service 구현 그룹과 router 구현 그룹으로 그룹핑하여 결합도를 낮추는 방법을 살펴보겠습니다.


      결합도를 낮추는 아키텍처의 필요성

      높은 결합도가 초래하는 위험

      아키텍처 설계를 소홀히 하거나 결합도를 낮추는 전략을 고려하지 않으면 다음과 같은 문제들이 발생할 수 있습니다:

      1. 유지보수성 저하: 한 모듈의 변경이 다른 모듈에 연쇄적으로 영향을 미쳐 수정 범위가 넓어집니다.

        • 예시: 데이터베이스 스키마 변경 시, 비즈니스 로직과 API 레이어 모두를 수정해야 하는 상황이 발생합니다.

      2. 확장성 제한: 새로운 기능을 추가하거나 변경하기 어려워집니다.

        • 예시: 새로운 API 엔드포인트를 추가하려면 기존 모듈들을 대폭 수정해야 합니다.

      3. 재사용성 감소: 특정 기능을 다른 프로젝트나 모듈에서 재사용하기 어렵습니다.

        • 예시: 다른 프로젝트에서 비슷한 기능이 필요해도 기존 코드를 활용할 수 없습니다.

      4. 협업 장애: 팀원 간 작업이 겹치거나 충돌하여 생산성이 떨어집니다.

        • 예시: 여러 개발자가 동일한 파일을 수정하면서 충돌이 빈번하게 발생합니다.

      5. 테스트 어려움: 모듈간 의존성이 높아 개별적인 단위 테스트가 어렵습니다.

        • 예시: 서비스 레이어를 테스트하려면 데이터베이스와 API 레이어까지 모두 설정해야 합니다.

      이러한 문제를 해결하기 위해 결합도를 낮추는 아키텍처가 필요합니다.


      FastAPI의 특징

      FastAPI는 Python의 최신 기능과 표준을 활용하여 빠르고 효율적인 API를 구축할 수 있게 해주는 프레임워크입니다. 주요 특징은 다음과 같습니다:

      • 고성능: 비동기 지원으로 높은 성능을 제공합니다.

      • 타입 힌트 기반 개발: Python의 타입 힌트를 활용하여 코드의 가독성과 안정성을 높입니다.

      • 자동 문서화: Swagger UI 및 ReDoc을 통한 자동 API 문서 생성을 지원합니다.

      • 개발 생산성 향상: 최소한의 코드로 강력한 기능을 구현할 수 있습니다.

      하지만 이러한 장점을 최대한 활용하려면, 모듈 간 결합도를 낮추고 응집도를 높이는 아키텍처 설계가 필요합니다.


      결합도 낮추기: service 구현 그룹과 router 구현 그룹

      1. service 구현 그룹

      역할: 비즈니스 로직과 데이터 처리를 담당하는 핵심 그룹입니다. 데이터베이스와의 상호 작용 및 데이터 가공 로직을 포함합니다.
      포함 파일:

      파일명

      설명

      entity/*.py

      도메인 모델 엔티티 클래스

      dto/*.py

      서비스 내부에서 사용하는 데이터 전송 객체(DTO)

      repository/*.py

      데이터베이스와의 CRUD 작업을 담당

      service/*.py

      비즈니스 로직을 구현

      2. router 구현 그룹

      역할: 클라이언트와의 통신을 담당하는 그룹입니다. 클라이언트의 요청을 받아 서비스 그룹의 기능을 호출하여 데이터를 처리하고, 결과를 클라이언트에 응답합니다.
      포함 파일:

      파일명

      설명

      dto/request/*.py

      클라이언트 요청을 위한 DTO

      dto/response/*.py

      클라이언트 응답을 위한 DTO

      usecase/*.py

      특정 유스케이스에 대한 애플리케이션 로직을 처리

      router/*.py

      API 엔드포인트를 정의


      FastAPI 프로젝트 파일 구조 예시

      기본 구조

      main/
      ├── domain/
      │   └── example/
      │       ├── entity/
      │       │   └── example.py
      │       ├── dto/
      │       │   └── example_dto.py
      │       ├── repository/
      │       │   └── example_repository.py
      │       └── service/
      │           └── example_service.py
      ├── app/
      │   └── example/
      │       ├── dto/
      │       │   ├── request/
      │       │   │   └── example_request_dto.py
      │       │   └── response/
      │       │       └── example_response_dto.py
      │       └── usecase/
      │           └── example_usecase.py
      └── api/
          └── example/
              └── router/
                  └── example_router.py

      core 모듈을 포함한 구조

      main/
      ├── core/
      │   ├── config.py
      │   ├── datastore.py
      │   ├── enum.py
      │   ├── helper.py
      │   ├── util.py
      │   └── dto.py
      ├── domain/
      │   └── example/
      │       ├── entity/
      │       │   └── example.py
      │       ├── dto/
      │       │   └── example_dto.py
      │       ├── repository/
      │       │   └── example_repository.py
      │       └── service/
      │           └── example_service.py
      ├── app/
      │   └── example/
      │       ├── dto/
      │       │   ├── request/
      │       │   │   └── example_request_dto.py
      │       │   └── response/
      │       │       └── example_response_dto.py
      │       └── usecase/
      │           └── example_usecase.py
      └── api/
          └── example/
              └── router/
                  └── example_router.py

      core 모듈 설명:

      파일명

      설명

      config.py

      프로젝트 설정을 관리합니다.

      datastore.py

      데이터베이스 연결 및 세션 관리를 담당합니다.

      enum.py

      프로젝트에서 사용하는 열거형 타입을 정의합니다.

      helper.py

      여러 곳에서 사용되는 헬퍼 함수들을 제공합니다.

      util.py

      공통 유틸리티 함수를 제공합니다.

      dto.py

      공통적으로 사용하는 DTO를 정의합니다.


      service 구현 그룹과 router 구현 그룹의 사례

      service 구현 그룹의 예시

      1. Entity Layer (example.py)

      from sqlalchemy import Column, Integer, String
      from database import Base
      
      class Example(Base):
          __tablename__ = 'examples'
      
          id = Column(Integer, primary_key=True, index=True)
          name = Column(String, index=True)
          description = Column(String)

      2. DTO (example_dto.py)

      from pydantic import BaseModel
      
      class ExampleDTO(BaseModel):
          id: int
          name: str
          description: str
      
          @staticmethod
          def from_entity(entity):
              return ExampleDTO(
                  id=entity.id,
                  name=entity.name,
                  description=entity.description
              )

      3. Repository Layer (example_repository.py)

      from sqlalchemy.orm import Session
      from .entity.example import Example
      
      class ExampleRepository:
          def get_example(self, db: Session, example_id: int):
              return db.query(Example).filter(Example.id == example_id).first()
      
          def create_example(self, db: Session, example: Example):
              db.add(example)
              db.commit()
              db.refresh(example)
              return example

      4. Service Layer (example_service.py)

      from sqlalchemy.orm import Session
      from .repository.example_repository import ExampleRepository
      from .dto.example_dto import ExampleDTO
      from .entity.example import Example
      
      class ExampleService:
          def __init__(self):
              self.repository = ExampleRepository()
      
          def get_example(self, db: Session, example_id: int) -> ExampleDTO:
              example = self.repository.get_example(db, example_id)
              if example:
                  return ExampleDTO.from_entity(example)
              else:
                  return None
      
          def create_example(self, db: Session, name: str, description: str) -> ExampleDTO:
              new_example = Example(name=name, description=description)
              created_example = self.repository.create_example(db, new_example)
              return ExampleDTO.from_entity(created_example)


      router 구현 그룹의 예시

      1. Request DTO(example_request_dto.py)

      from pydantic import BaseModel
      
      class ExampleRequestDTO(BaseModel):
          name: str
          description: str

      2. Response DTO (example_response_dto.py)

      from pydantic import BaseModel
      
      class ExampleResponseDTO(BaseModel):
          id: int
          name: str
          description: str

      3. UseCase Layer (example_usecase.py)

      from sqlalchemy.orm import Session
      from main.domain.example.service.example_service import ExampleService
      from main.app.example.dto.request.example_request_dto import ExampleRequestDTO
      from main.app.example.dto.response.example_response_dto import ExampleResponseDTO
      
      class ExampleUseCase:
          def __init__(self):
              self.service = ExampleService()
      
          def get_example(self, db: Session, example_id: int) -> ExampleResponseDTO:
              example_dto = self.service.get_example(db, example_id)
              if example_dto:
                  return ExampleResponseDTO(**example_dto.dict())
              else:
                  return None
      
          def create_example(self, db: Session, request_dto: ExampleRequestDTO) -> ExampleResponseDTO:
              example_dto = self.service.create_example(db, request_dto.name, request_dto.description)
              return ExampleResponseDTO(**example_dto.dict())

      4. Router Layer (example_router.py)

      from fastapi import APIRouter, Depends, HTTPException
      from sqlalchemy.orm import Session
      from main.app.example.usecase.example_usecase import ExampleUseCase
      from main.app.example.dto.request.example_request_dto import ExampleRequestDTO
      from main.app.example.dto.response.example_response_dto import ExampleResponseDTO
      from main.database import get_db
      
      router = APIRouter()
      use_case = ExampleUseCase()
      
      @router.get("/examples/{example_id}", response_model=ExampleResponseDTO)
      def read_example(example_id: int, db: Session = Depends(get_db)):
          result = use_case.get_example(db, example_id)
          if result:
              return result
          else:
              raise HTTPException(status_code=404, detail="Example not found")
      
      @router.post("/examples/", response_model=ExampleResponseDTO)
      def create_example(request_dto: ExampleRequestDTO, db: Session = Depends(get_db)):
          return use_case.create_example(db, request_dto)


      결합도를 낮추는 아키텍처의 이점

      image.png

      1. 모듈간 독립성 강화

      예시 스토리:

      개발자 민수는 비즈니스 로직에 새로운 할인 정책을 적용해야 했습니다.

      서비스 레이어에서 로직을 수정했지만, router 레이어와 독립적이기 때문에 다른 팀원들이 작업 중인 API 엔드포인트 코드에는 영향이 없었습니다.

      덕분에 민수는 빠르게 수정 작업을 완료하고 배포할 수 있었습니다.

      2. 유지보수성 향상

      예시 스토리:

      신입 개발자 지혜는 프로젝트에 투입된 첫 날, 코드 구조를 파악해야 했습니다.

      모듈이 명확히 분리되어 있어 지혜는 서비스 레이어와 router 레이어를 각각 이해할 수 있었고, 필요한 부분에 집중하여 빠르게 업무에 적응할 수 있었습니다.

      3. 테스트 용이성

      예시 스토리:

      QA 엔지니어 철수는 새로운 기능에 대한 단위 테스트를 작성하고 있었습니다.

      모듈간 결합도가 낮아 Mock 객체를 사용하여 데이터베이스와 API 호출 없이도 서비스 레이어의 로직을 검증할 수 있었습니다.

      이는 테스트 시간을 크게 단축시켰습니다.

      4. 확장성 증가

      예시 스토리:

      프로덕트 매니저는 새로운 모바일 앱 출시를 계획하고 있었습니다.

      개발자 수진은 기존 서비스 레이어를 그대로 활용하여 모바일 전용 API를 빠르게 구축할 수 있었습니다.

      덕분에 출시 일정에 맞춰 개발을 완료할 수 있었습니다.

      5. 재사용성 강화

      예시 스토리:

      다른 팀에서 비슷한 기능이 필요하다는 요청이 들어왔습니다.

      개발자 영호는 기존 서비스 레이어의 비즈니스 로직을 공유하여 코드 재사용을 가능하게 했고, 다른 팀은 빠르게 기능을 구현할 수 있었습니다.


      빅테크의 FastAPI 사례

      마이크로소프트(Microsoft)

      • 애저(Azure) SDK: 마이크로소프트는 일부 서비스에서 FastAPI를 사용하여 API를 구축하고 있습니다. FastAPI의 빠른 개발 속도와 높은 성능을 활용하고 있습니다.

      우버(Uber) 엔지니어링

      • 내부 도구 개발: 우버의 엔지니어들은 빠른 프로토타이핑과 내부 도구 개발을 위해 FastAPI를 활용하고 있습니다. 비동기 처리를 통한 고성능이 요구되는 환경에서 효과적으로 사용되고 있습니다.

      넷플릭스(Netflix)

      • 데이터 사이언스 API: 넷플릭스의 데이터 사이언스 팀은 모델 서빙을 위해 FastAPI를 사용하고 있습니다. 빠른 응답 시간과 자동 문서화 기능이 큰 이점으로 작용합니다.


      마무리

      FastAPI를 활용한 웹 애플리케이션 개발에서 service 구현 그룹과 router 구현 그룹으로의 그룹핑은 결합도를 낮추고 응집도를 높이는 효과적인 전략입니다.

      이를 통해 개발 생산성을 향상시키고, 유지보수성과 확장성을 높일 수 있습니다.

      • 역할 분리를 통해 모듈간 독립성을 확보합니다.

      • 결합도 감소로 변경의 영향 범위를 최소화합니다.

      • 응집도 향상으로 코드의 가독성과 유지보수성을 높입니다.

      • 유연한 확장성으로 다양한 요구사항에 대응할 수 있습니다.

      프로젝트의 규모와 복잡도에 상관없이, 모듈간 결합도를 낮추는 아키텍처를 채택함으로써 더 나은 코드 품질과 개발 효율성을 달성할 수 있습니다.

      이러한 전략은 FastAPI의 장점을 최대한 활용하는 데에도 도움이 됩니다.

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      broccoli 님의 최신 블로그

      더보기

      DEVOTEE 추천 블로그

      동영상 기고하기