반응형

파이썬 FastAPI 실전 프로젝트 구현 (2) – 기능 보완 및 마무리

여러분, 프로젝트 마무리할 때 뭔가 찝찝하게 끝낸 적 있으시죠?
이번에는 FastAPI로 만든 API를 제대로 다듬고,
진짜 ‘완성된’ 느낌을 만들어봐요!

 

 

안녕하세요, 개발자 여러분 😊

드디어 FastAPI 실전 프로젝트의 마지막 단계에 도달했습니다.

앞서 API를 설계하고 기본적인 CRUD 기능을 구현했지만, 사실 프로젝트가 ‘완성됐다’고 말하기 위해서는 몇 가지 마무리 작업이 꼭 필요하죠.

예를 들어 빠진 기능을 채우거나, 리팩토링을 통해 코드 품질을 끌어올리고, 전체 테스트를 통해 문제가 없는지 꼼꼼히 검증하는 과정 말이에요.

이번 글에서는 Update/Delete API 구현, 리팩토링, 테스트 마무리, 추가 기능 구현까지 포함해서, 진짜 실전에 가까운 프로젝트 완성 과정을 함께 해볼 거예요.

이제 진짜 개발자 포트폴리오에 올릴 수 있는 API를 만들어볼 준비되셨나요? 😎 그럼 시작해볼게요!

 

1. CRUD 기능 보완: Update/Delete 엔드포인트 구현 🛠️

1.1 글(Post) 및 할일(Todo) 수정 API 구현

이제 남은 CRUD 기능 중 '수정(Update)' 엔드포인트부터 완성해볼 차례입니다.

블로그 API와 Todo API 모두에 해당하는 기능으로, RESTful한 방식에 따라 PUT 메서드를 사용합니다.

 

FastAPI에서는 @router.put("/posts/{id}") 형식으로 경로를 선언하고, 입력 데이터는 schemas.PostUpdate 같은 Pydantic 모델을 사용해 받습니다.

 

- 수정 대상이 존재하지 않으면 404 Not Found
- 성공 시에는 수정된 객체를 JSON으로 반환
- 인증 기능이 있다면 작성자만 수정 가능하게 조건을 걸 수 있음 (지금은 생략)

예시 코드

@router.put("/posts/{id}")
def update_post(id: int, post_update: PostUpdate, db: Session = Depends(get_db)):
    post = db.query(Post).filter(Post.id == id).first()
    if not post:
        raise HTTPException(status_code=404, detail="Post not found")
    post.title = post_update.title
    post.content = post_update.content
    db.commit()
    db.refresh(post)
    return post

1.2 삭제(Delete) API 구현

삭제 기능은 프로젝트의 정돈을 위해 반드시 필요한 부분이에요.

블로그의 경우 글 삭제, Todo의 경우 할일 삭제가 이에 해당합니다.

완전 삭제 방식으로 구현하되, 보안이나 감사 로그가 필요한 경우에는 Soft Delete 방식도 고민해볼 수 있어요.

예시 코드

@router.delete("/todos/{id}", status_code=204)
def delete_todo(id: int, db: Session = Depends(get_db)):
    todo = db.query(Todo).filter(Todo.id == id).first()
    if not todo:
        raise HTTPException(status_code=404, detail="Todo not found")
    db.delete(todo)
    db.commit()
    return Response(status_code=204)

1.3 마무리 요약

  • PUT 메서드로 수정 API를 추가한다.
  • DELETE 메서드로 삭제 API를 완성한다.
  • 없는 리소스에 접근 시 404 예외 처리를 꼼꼼히 해준다.

이제 CRUD의 4가지 기본 기능이 완성됐습니다.

이 다음은 조금 더 재미있는(?) 추가 기능 구현 시간입니다!

 

 

2. 주제별 추가 기능과 쿼리 개선 🎯

2.1 블로그 API – 사용자별 글 목록 및 검색 기능

블로그 API에선 기본적인 CRUD 외에도, 사용자별 글 목록이나 검색 기능처럼 실전에서 많이 쓰는 기능을 추가해보면 실력이 쑥쑥 자랍니다.

  • GET /users/{user_id}/posts – 특정 유저가 작성한 글만 필터링
  • GET /posts?search=키워드 – 제목/내용에 포함된 키워드로 검색

쿼리 필터 예시

@router.get("/posts")
def list_posts(search: str = None, db: Session = Depends(get_db)):
    query = db.query(Post)
    if search:
        query = query.filter(Post.title.contains(search))
    return query.all()

2.2 Todo API – 완료 여부 필터링 및 정렬

Todo API에선 할 일의 완료 상태우선순위 정렬 기능을 추가하면 사용성이 훨씬 높아져요.

  • GET /todos?completed=true – 완료된 항목만 필터링
  • GET /todos?sort=priority – 우선순위 오름차순 정렬

정렬 필터 예시

@router.get("/todos")
def get_todos(completed: bool = None, sort: str = None, db: Session = Depends(get_db)):
    query = db.query(Todo)
    if completed is not None:
        query = query.filter(Todo.done == completed)
    if sort == "priority":
        query = query.order_by(Todo.priority.asc())
    return query.all()

2.3 마무리 팁 💡

이런 기능은 구현해보면 어렵지 않지만, 실제 API 사용 경험을 엄청나게 향상시켜줍니다.

쿼리 파라미터동적 필터링은 FastAPI와 SQLAlchemy를 익히는 데 정말 좋은 연습이 돼요!

 

꼭 필요한 핵심 기능 외에도 사용자가 편리하게 쓸 수 있는 부가 기능 몇 개만 추가해도, 프로젝트의 깊이가 확 달라진다는 거 잊지 마세요 😉

 

 

3. 코드 리팩토링과 공통 모듈 정리 📦

3.1 중복 로직 정리: get_object_or_404

FastAPI로 프로젝트를 하다 보면, 가장 자주 반복되는 패턴 중 하나가 바로 이거예요.

query.first()로 객체를 가져오고 없으면 HTTPException(404)을 던지는 코드!

매번 반복하지 말고, 다음과 같이 공통 헬퍼 함수로 만들어두면 가독성과 유지보수성이 확 올라갑니다.

def get_object_or_404(query, model_name: str = "Object"):
    obj = query.first()
    if not obj:
        raise HTTPException(status_code=404, detail=f"{model_name} not found")
    return obj

 

이제 이렇게 쓰면 됩니다:

post = get_object_or_404(db.query(Post).filter(Post.id == id), "Post")

3.2 인증/권한 검사 공통화

만약 로그인 인증 기능이 있다면, API마다 인증 정보를 검사하는 코드도 반복됩니다.

FastAPI는 Depends()를 이용해 공통 처리할 수 있어요.

def get_current_user(token: str = Depends(oauth2_scheme)):
    payload = decode_token(token)
    user = db.query(User).filter(User.id == payload["sub"]).first()
    if not user:
        raise HTTPException(status_code=401, detail="Invalid credentials")
    return user

 

라우터에서는 이렇게 간단히 호출하면 되죠:

@router.get("/me/posts")
def my_posts(current_user: User = Depends(get_current_user)):
    return current_user.posts

3.3 Swagger 문서화 향상

Swagger UI를 더 보기 좋게 꾸미고 싶다면, Pydantic 모델에 description 필드를 넣는 게 좋아요.

이게 곧 API 문서가 되거든요.

class PostCreate(BaseModel):
    title: str = Field(..., description="게시글 제목")
    content: str = Field(..., description="게시글 본문")

 

이렇게 하면 Swagger에서 각 필드 설명이 뜨고, 사용자나 동료 개발자에게도 친절한 API가 됩니다. 🧾

3.4 디버그 흔적 제거

그리고 가장 중요한 마무리 습관!

print 같은 디버깅 코드는 모두 제거해주세요.

콘솔에 찍히는 메시지는 실서비스에서 보안 위험이 될 수도 있고, 로그 관리도 어려워지니까요.

  • print() 제거
  • 테스트용 더미 코드 제거

깔끔한 코드는 실력의 증거입니다.

동료도, 미래의 나도 기뻐할 거예요. 😉

 

 

4. Swagger 기반 수동 테스트 🧪

4.1 Swagger UI에서 테스트 시나리오 따라가기

FastAPI의 강력한 무기 중 하나는 바로 자동 생성 API 문서, Swagger UI죠!

개발한 모든 엔드포인트를 웹 UI로 손쉽게 테스트할 수 있다는 점에서, 이건 정말 필수 기능이에요.

 

테스트를 시작할 땐, 가능한 실제 사용자 시나리오 기반으로 순서대로 실행해보는 게 좋아요.

예를 들어:

  1. 1️⃣ 사용자 생성 (POST /users)
  2. 2️⃣ 로그인 (만약 구현했다면)
  3. 3️⃣ 글 or 할일 생성
  4. 4️⃣ 목록 조회
  5. 5️⃣ 글/할일 수정
  6. 6️⃣ 삭제 요청
  7. 7️⃣ 삭제 후 목록 재조회

4.2 예외 상황도 꼭 테스트해보자!

정상 흐름만 테스트하면 부족해요.

진짜 완성도 높은 API는 경계 상황까지 잘 처리하는 경우죠.

 

꼭 체크해야 할 케이스 리스트 ✔️

  • 존재하지 않는 ID로 요청했을 때 → 404 Not Found 잘 반환되는가?
  • 필수 값이 빠진 입력 → 422 Unprocessable Entity 나오는가?
  • 중복된 값 입력 → 에러 메시지가 친절한가?

이런 테스트는 Swagger UI에서 직접 해볼 수도 있지만, Postman이나 curl 같은 도구로 해보는 것도 좋은 연습이에요.

다양한 환경에서 동일한 API가 잘 작동하는지 확인할 수 있거든요.

4.3 마무리하며

직접 테스트해보면서 API 동작을 확인하는 건, 말 그대로 ‘현장감 있는 검증’입니다.

직접 손으로 다뤄보는 만큼, 놓쳤던 버그도 쉽게 발견되죠.

 

이제 우리 프로젝트도 한 단계 성숙해졌네요.

다음은 자동화 테스트로 더 철저하게 완성도를 높이는 시간입니다! 😎

 

 

5. pytest를 활용한 자동화 테스트 🔍

5.1 pytest 기본 구조와 테스트 함수 작성

FastAPI 프로젝트에선 pytest를 사용해 자동화 테스트를 작성하는 게 표준입니다.

기능 하나하나를 수동으로 확인할 수는 없으니, 테스트 코드를 작성해두면 나중에 기능을 수정하거나 리팩토링해도 안정성을 체크할 수 있어요.

예시 테스트 코드

from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)

def test_create_todo():
    response = client.post("/todos", json={
        "title": "테스트 할일",
        "description": "자동화 테스트용"
    })
    assert response.status_code == 200
    assert response.json()["title"] == "테스트 할일"

 

단순해 보이지만, POST 요청 → 응답 확인 → 예상 결과 비교까지 핵심 흐름이 모두 담겨 있어요.

5.2 테스트 커버리지 넓히기

단일 테스트로는 부족하죠.

아래 항목들을 테스트 목록에 꼭 포함시켜보세요:

  • 목록 조회 (GET) 응답이 올바른지
  • 수정/삭제 요청 시 상태코드 확인
  • 없는 ID로 요청 시 404 테스트

5.3 테스트 실행 및 자동화 팁

터미널에서 다음 명령어를 입력하면 테스트가 실행됩니다.

pytest tests/

 

Tip: GitHub Actions 또는 GitLab CI와 연동하면 커밋할 때마다 자동으로 테스트가 돌아가요.

       이것만 잘해도 실무에선 ‘코드 퀄리티 보장’으로 인정받습니다.

5.4 테스트 작성 못했다면?

사실 시간이 부족하거나 학습이 익숙하지 않다면, 모든 자동화 테스트를 작성하는 건 쉽지 않아요.

그럴 땐 수동 테스트와 로그 확인으로 충분히 대체 가능합니다.

 

하지만 이번 프로젝트가 끝나고 나면 꼭 pytest + FastAPI 조합으로 테스트를 구성해보세요.

다음 프로젝트에서는 코드 품질의 자신감이 달라질 거예요!

 

 

6. 프로젝트 마무리 및 다음 단계 제안 🚀

6.1 FastAPI 프로젝트 완성 축하합니다! 🎉

정말 고생 많으셨어요!

지금까지 FastAPI + SQLAlchemy를 활용해서 REST API를 설계하고, 구현하고, 테스트하고, 마무리까지 해냈습니다.

처음엔 생소하고 어렵게 느껴졌던 코드들도 이제는 익숙하게 다뤘을 거예요. 👏👏👏

6.2 포트폴리오로 남기는 법 💼

  • GitHub에 코드 업로드: 기능별 커밋 기록은 매우 큰 가산점!
  • README.md 작성: 사용 방법, 기능 목록, 설치 방법 포함하면 👍
  • 시연 영상 or 이미지 첨부: 블로그나 유튜브 링크도 함께!

6.3 다음에 도전해볼 것들 ✨

이제 진짜 FastAPI를 익힌 셈이니까요, 다음 단계로 이런 걸 시도해보면 좋습니다:

  • JWT 인증과 OAuth2 로그인 적용 (보안 강화)
  • Docker로 서비스 배포 준비
  • 프론트엔드 연동 (React, Vue 등으로 UI 구현)

6.4 참고 자료 📚

학습을 계속 이어가고 싶다면, 아래 자료들을 추천합니다:

FastAPI는 앞으로도 계속 성장할 프레임워크입니다.

꾸준히 공식 문서를 체크하고, 오픈소스 프로젝트에 기여해보는 것도 추천드려요!

 

 

마무리 🎁

지금까지 우리는 FastAPI를 활용한 실전 프로젝트를 단계별로 구축해왔고, 그 마지막 단계인 기능 보완과 테스트, 리팩토링까지 성공적으로 마무리했습니다.

이 과정을 통해 FastAPI의 기본 구조뿐 아니라 RESTful API 설계, ORM 연동, 예외 처리, 테스트 자동화에 이르기까지 백엔드 개발에서 꼭 필요한 스킬들을 하나씩 익혔어요.

특히 이번 프로젝트는 단순히 작동만 되는 코드가 아닌, 실제 서비스에 가까운 형태로 구현했다는 점에서 큰 의미가 있습니다.

지금까지 작성한 코드들은 포트폴리오로 활용할 수 있을 만큼 구조적이고 완성도가 높아졌습니다.

여기까지 따라오셨다면 정말 큰 박수를 드리고 싶어요. 👏👏👏

 

앞으로는 이 기반 위에 인증, 배포, 프론트엔드 연동 등 더 넓은 분야로 확장해보세요.

그리고 중요한 건 꾸준히 연습하고, 실제 프로젝트에 응용해보는 겁니다.

이제 여러분도 자신 있게 말할 수 있어요.

"FastAPI로 백엔드 개발할 줄 압니다!" 💪

🎯 이 글의 핵심 요약

  • ✅ CRUD 완성: Update/Delete 엔드포인트 구현
  • ✅ 사용자 중심 필터링, 검색 등 추가 기능
  • ✅ 공통 함수 및 인증 구조 리팩토링
  • ✅ Swagger를 통한 수동 테스트
  • ✅ pytest 기반 자동화 테스트 도입
  • ✅ 실무형 프로젝트 구조와 문서화

 

여기서 끝나지 않아요.

지금까지 쌓은 지식을 바탕으로 더 멋진 API, 더 나은 백엔드 시스템을 계속 만들어가시길 바랍니다.

여러분의 여정을 응원합니다! 🚀

반응형
반응형

파이썬 FastAPI 실전 프로젝트 (1) – 블로그 API/Todo API 설계 및 구현 

단순한 예제만으로는 실력이 늘지 않아요.
진짜로 내가 만든 FastAPI 서비스,
지금부터 직접 설계하고 구현해보는 시간입니다!

 

 

안녕하세요 여러분 😊

드디어 지금까지 배운 FastAPI 내용을 바탕으로 직접 프로젝트를 시작할 차례입니다!

이제는 단순한 이론이나 짧은 실습이 아니라, 사용자 관리부터 게시글 또는 할일 등록까지 기능이 갖춰진 나만의 API 서비스를 만들게 될 거예요.

이번 실전 프로젝트는 두 가지 주제 중 하나를 선택해서 진행할 수 있어요:

1) 블로그 API 또는 2) Todo 관리 API.

둘 다 FastAPI + SQLAlchemy를 활용해 RESTful 백엔드 API를 직접 설계하고 구현하는 경험을 하게 됩니다.

각 기능별 CRUD 구현뿐 아니라, 모델 설계, 프로젝트 구조 구성, 예외 처리, 테스트까지 고려한 구조로 만들어볼 거예요.

지금부터 7일차 여정을 함께 출발해볼까요? 🚀

 

1. 최종 프로젝트 개요 및 설계 🧭

여러분, 여기까지 따라오시느라 정말 고생 많으셨어요. 😊

지금까지 FastAPI의 기본적인 사용법, SQLAlchemy 연동, 라우팅, Pydantic 모델링, 예외 처리 등을 하나씩 차근차근 배워오셨죠?

이제는 그 모든 퍼즐을 모아서 직접 하나의 프로젝트로 완성해볼 차례입니다.

1.1 프로젝트 주제와 목표 🎯

이번 프로젝트에서는 두 가지 주제 중 하나를 선택할 수 있어요:

  • 블로그 API: 사용자 인증, 글 작성, 글 목록 조회, 상세 보기, 댓글 작성/삭제, 태그 기능 포함
  • Todo API: 사용자 생성, 할일 CRUD, 우선순위 설정, 완료 상태 필터링 기능 제공

물론 두 프로젝트 모두 공통적으로 사용자 모델, 데이터베이스 연동, CRUD 기능이 핵심이 되며, 선택한 주제에 따라 약간의 기능 차이가 있을 뿐입니다.

1.2 이번 실습의 핵심 목표 🧩

  1. FastAPI의 주요 기능을 실제 API에 적용
  2. SQLAlchemy를 통한 모델 정의와 DB 연동 실습
  3. Pydantic을 활용한 데이터 검증
  4. 기본적인 예외 처리와 테스트 코드 작성
  5. REST API의 CRUD 기능 전반 구현

즉, 이번 실습의 핵심은 단순히 동작하는 코드를 작성하는 것이 아니라, 정리된 구조와 유지보수 가능한 코드를 직접 설계하고 구현하는 것입니다.

실무에서 백엔드를 처음 시작할 때 어떤 흐름으로 개발을 진행해야 하는지, 그 뼈대를 이번 실습을 통해 직접 익히게 될 거예요.

 

이제 다음 단계로 넘어가서, 구체적인 데이터 모델을 어떻게 설계할지 알아보겠습니다.

 

 

2. 데이터 모델 설계 📐

서비스를 만들기 전, 가장 먼저 해야 할 일은 데이터 구조를 어떻게 설계할 것인가입니다.

바로 모델링이죠.

데이터베이스 구조가 깔끔하게 설계되어야 API도 명확하게 작동하고, 유지보수도 수월해집니다.

이번엔 선택한 프로젝트 유형에 따라 각기 다른 모델을 설계하게 됩니다.

2.1 블로그 API 모델 예시 ✍️

블로그 API를 선택한 경우, 보통 다음과 같은 세 가지 모델이 필요해요:

모델 필드 설명
User id, username, email, hashed_password 회원 정보 (비밀번호는 해싱 처리)
Post id, title, content, created_at, author_id 글 제목, 본문, 작성 시간 및 작성자
Comment id, content, created_at, post_id, author_id 댓글 내용과 작성자, 연결된 게시글

여기서 중요한 건 외래키 관계입니다.

Post와 Comment는 모두 User와 연결되고, Comment는 Post에도 연결되죠.

SQLAlchemy에서는 ForeignKeyrelationship으로 이를 구현할 수 있어요.

2.2 Todo API 모델 예시 ✅

Todo 프로젝트는 더 단순합니다. 모델은 아래 두 개만 필요하죠.

모델 필드 설명
User id, username 사용자 이름 (unique 설정 권장)
Todo id, title, description, priority, completed, user_id 할일 제목, 설명, 우선순위, 완료 여부, 사용자

💡 모델 설계 팁

  • 문자열 길이 제한을 String(length)으로 명확하게 지정하세요.
  • nullable=False는 필수 필드에 반드시 지정해줘야 합니다.
  • 고유성이 필요한 필드는 unique=True로 중복 방지!

이제 데이터 모델이 명확하게 정리되었으니,

다음 단계는 실제 프로젝트 디렉토리 구조를 만들고 빈 틀을 잡아보는 것입니다.

프로젝트가 커질수록 구조가 중요해지니까요.

계속해서 다음 단계에서 프로젝트 뼈대를 만들어볼게요!

 

 

3. 프로젝트 구조 뼈대 만들기 🧱

FastAPI 프로젝트를 시작할 때 가장 먼저 해야 할 일은 전체 폴더 구조를 체계적으로 잡는 것이에요.

한 파일 안에 모든 코드를 몰아넣는 방식은 소규모 예제에선 괜찮지만, 프로젝트가 커지면 유지보수가 거의 불가능하죠.

이번 프로젝트에서는 아래와 같은 구조를 기준으로 설계를 시작합니다.

3.1 기본 디렉토리 구조

myapi/
├── main.py               # FastAPI 앱 실행 진입점
├── models.py             # SQLAlchemy ORM 모델 정의
├── schemas.py            # Pydantic 데이터 모델 정의
├── database.py           # DB 설정 (engine, session, get_db)
├── routers/
│   ├── users.py          # 사용자 관련 라우터
│   └── posts.py          # 블로그 글 관련 라우터 (or todos.py)
└── __init__.py           # 패키지 초기화 파일 (선택 사항)

✅ 핵심 원칙은 다음과 같아요:

  • 각 기능을 독립적인 모듈로 구성 → 관심사의 분리(SOC) 원칙 준수
  • DB 관련 코드는 database.py에 모아서 일원화
  • Pydantic 모델은 schemas.py에서 관리하여 API 스키마 명확화

🛠 main.py 기본 틀

from fastapi import FastAPI
from routers import users, posts  # 또는 todos

app = FastAPI()

app.include_router(users.router)
app.include_router(posts.router)

🔌 database.py 기본 구성

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

DATABASE_URL = "sqlite:///./test.db"

engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

이처럼 프로젝트 초기 구조를 잘 설계하면, 나중에 기능을 추가할 때도 훨씬 덜 헤매고 유지보수가 쉬워집니다.

앞으로 posts.py, todos.py, comments.py 등을 기능별로 확장할 수 있도록 유연한 구조로 시작하세요!

 

이제 구조가 준비되었으니, 본격적으로 사용자 기능부터 구현해볼까요?

다음 섹션에서는 사용자 생성과 조회를 구현하면서 실전 코딩을 시작합니다!

 

 

4. 사용자 기능 구현 🔐

프로젝트에서 사용자(User)는 모든 기능의 출발점이에요.

할일이든 게시글이든 결국 ‘누가 작성했는지’가 중요하니까요.

이번 단계에서는 사용자 등록(POST)과 조회(GET) API를 구현합니다.

이 두 기능만 제대로 작동해도 프로젝트의 절반은 성공한 셈이에요!

4.1 Pydantic 스키마 만들기

우선 schemas.py에 사용자 관련 데이터 구조를 정의합니다:

from pydantic import BaseModel, EmailStr

class UserBase(BaseModel):
    username: str
    email: EmailStr

class UserCreate(UserBase):
    password: str

class User(UserBase):
    id: int

    class Config:
        orm_mode = True

4.2 SQLAlchemy 모델 정의

models.py에는 사용자 모델을 다음과 같이 정의해요:

from sqlalchemy import Column, Integer, String
from database import Base

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True, index=True)
    username = Column(String(30), unique=True, nullable=False)
    email = Column(String(100), unique=True, nullable=False)
    hashed_password = Column(String(128), nullable=False)

💡 비밀번호는 평문 저장 No!

여기선 해시 없이 저장하겠지만, 실무에서는 꼭 bcrypt 같은 해싱 알고리즘을 사용해야 해요.

4.3 사용자 생성 API

# routers/users.py
from fastapi import APIRouter, HTTPException, Depends
from sqlalchemy.orm import Session
from database import get_db
import models, schemas

router = APIRouter(prefix="/users", tags=["Users"])

@router.post("/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
    existing_user = db.query(models.User).filter(
        (models.User.username == user.username) | (models.User.email == user.email)
    ).first()
    if existing_user:
        raise HTTPException(status_code=400, detail="Username or email already exists")

    db_user = models.User(
        username=user.username,
        email=user.email,
        hashed_password=user.password  # 실무에서는 해싱 필수!
    )
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return db_user

4.4 사용자 조회 API

@router.get("/{user_id}", response_model=schemas.User)
def read_user(user_id: int, db: Session = Depends(get_db)):
    user = db.query(models.User).filter(models.User.id == user_id).first()
    if user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return user

🧪 테스트 팁

  • Swagger UI(/docs)에서 POST → 바로 GET으로 테스트 가능!
  • uvicorn main:app --reload로 서버 실행하세요.

이제 사용자가 등록되고 조회되는 API가 완성되었습니다!

이 데이터를 기반으로 다음 단계에서는 본격적인 게시글(Post) 혹은 할일(Todo) 기능을 구현해보겠습니다.

 

 

5. 글쓰기 or 할일 등록 기능 구현 ✏️

이제 사용자 기능이 잘 작동하는 걸 확인했다면, 다음은 본 서비스의 중심 기능인 게시글(Post) 또는 할일(Todo)을 구현할 차례입니다.

블로그 API를 선택한 분은 글쓰기 기능을, Todo API를 선택한 분은 할일 등록 기능부터 차근차근 구현해보면 됩니다.

5.1 블로그 글쓰기 기능 (POST /posts/) 📝

# schemas.py
from datetime import datetime

class PostBase(BaseModel):
    title: str
    content: str

class PostCreate(PostBase):
    author_id: int

class Post(PostBase):
    id: int
    created_at: datetime
    author_id: int

    class Config:
        orm_mode = True
# models.py
from sqlalchemy import Column, Integer, String, ForeignKey, DateTime
from sqlalchemy.orm import relationship
from datetime import datetime

class Post(Base):
    __tablename__ = "posts"

    id = Column(Integer, primary_key=True, index=True)
    title = Column(String(100), nullable=False)
    content = Column(String, nullable=False)
    created_at = Column(DateTime, default=datetime.utcnow)
    author_id = Column(Integer, ForeignKey("users.id"), nullable=False)

    author = relationship("User", backref="posts")
# routers/posts.py
@router.post("/", response_model=schemas.Post)
def create_post(post: schemas.PostCreate, db: Session = Depends(get_db)):
    db_post = models.Post(
        title=post.title,
        content=post.content,
        author_id=post.author_id
    )
    db.add(db_post)
    db.commit()
    db.refresh(db_post)
    return db_post

5.2 글 목록 및 상세 조회 (GET /posts/, /posts/{id}) 📚

@router.get("/", response_model=List[schemas.Post])
def read_posts(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)):
    posts = db.query(models.Post).offset(skip).limit(limit).all()
    return posts

@router.get("/{post_id}", response_model=schemas.Post)
def read_post(post_id: int, db: Session = Depends(get_db)):
    post = db.query(models.Post).filter(models.Post.id == post_id).first()
    if not post:
        raise HTTPException(status_code=404, detail="Post not found")
    return post

쿼리 파라미터로 skiplimit을 받아 간단한 페이징 기능도 적용해봤어요.

물론 실제 서비스라면 페이지 번호, 정렬 기준 등 더 다양한 필터가 필요하겠지만, 지금은 핵심 흐름만 익히는 게 중요합니다.

5.3 할일 등록 기능 (POST /todos/) ✅

# schemas.py
class TodoBase(BaseModel):
    title: str
    description: str

class TodoCreate(TodoBase):
    user_id: int

class Todo(TodoBase):
    id: int
    completed: bool
    priority: int

    class Config:
        orm_mode = True
# models.py
class Todo(Base):
    __tablename__ = "todos"

    id = Column(Integer, primary_key=True, index=True)
    title = Column(String(100), nullable=False)
    description = Column(String, nullable=False)
    completed = Column(Boolean, default=False)
    priority = Column(Integer, default=1)
    user_id = Column(Integer, ForeignKey("users.id"))

    user = relationship("User", backref="todos")
# routers/todos.py
@router.post("/", response_model=schemas.Todo)
def create_todo(todo: schemas.TodoCreate, db: Session = Depends(get_db)):
    db_todo = models.Todo(
        title=todo.title,
        description=todo.description,
        user_id=todo.user_id
    )
    db.add(db_todo)
    db.commit()
    db.refresh(db_todo)
    return db_todo

5.4 할일 목록 및 상세 조회 (GET /todos/, /todos/{id}) 📋

@router.get("/", response_model=List[schemas.Todo])
def read_todos(db: Session = Depends(get_db)):
    return db.query(models.Todo).all()

@router.get("/{todo_id}", response_model=schemas.Todo)
def read_todo(todo_id: int, db: Session = Depends(get_db)):
    todo = db.query(models.Todo).filter(models.Todo.id == todo_id).first()
    if not todo:
        raise HTTPException(status_code=404, detail="Todo not found")
    return todo

여기까지 완성되면 사용자 - 게시글, 또는 사용자 - 할일 구조의 기본 CRUD의 반 이상이 만들어진 셈입니다. 다음 단계에선 이 모든 기능들이 실제로 잘 작동하는지 테스트해보는 실습을 진행할 거예요!

 

 

6. 서버 실행 및 결과 테스트 ⚙️

이제 우리 프로젝트의 기본 기능은 모두 구현이 끝났습니다!

이제는 서버를 실행해보고, 실제 요청을 보내보며 테스트하는 단계입니다.

FastAPI의 가장 큰 장점 중 하나가 바로 Swagger UI를 통한 인터랙티브 테스트 환경이죠.

하나씩 점검하면서 내가 만든 API가 잘 작동하는지 직접 확인해봅시다.

6.1 서버 실행하기

uvicorn main:app --reload

이 명령어로 로컬 서버를 실행하면 http://127.0.0.1:8000/docs 에서 자동 생성된 Swagger 문서에 접속할 수 있습니다.

이제 각 API 기능을 직접 클릭해보며 테스트해볼 수 있어요.

6.2 주요 테스트 시나리오 🧪

  • POST /users/ : 사용자 등록 요청 → 성공 시 200 반환 및 ID 확인
  • GET /users/{id} : 등록한 사용자 조회 → 사용자 정보 반환
  • POST /posts/ 또는 /todos/ : 글 또는 할일 등록
  • GET /posts/ 또는 /todos/ : 전체 목록 확인
  • GET /posts/{id} 또는 /todos/{id} : 상세 페이지 확인

🚨 자주 발생하는 오류

  • IntegrityError: 이미 존재하는 username 또는 email → 400 예외 처리 필요
  • AttributeError: 'dict' object has no attribute → Pydantic 모델에 orm_mode = True 빠짐

6.3 마무리 점검 리스트 ✅

항목 확인 여부
DB 모델 생성 완료
Pydantic 스키마 정의
CRUD API 작동 확인
Swagger UI 테스트

축하합니다! 여기까지 완성했다면 여러분은 이제 FastAPI 기반의 실제 동작하는 백엔드 서비스를 손수 구현해본 경험을 쌓으신 거예요.

 

 

마무리 🎯

지금까지 FastAPI와 SQLAlchemy를 활용한 실전 API 프로젝트를 함께 구현해보았습니다.

단순한 Hello World 수준을 넘어, 사용자 생성부터 게시글/할일 등록, 조회 API까지 완성된 상태의 백엔드가 완성되었죠.

직접 설계한 데이터 모델, 모듈화된 프로젝트 구조, 그리고 인터랙티브한 Swagger UI 테스트까지. 이건 단순한 튜토리얼이 아니라, 실무 감각을 기를 수 있는 제대로 된 훈련이었어요.

 

이번 실습이 끝이 아니라는 점, 기억해주세요!

이후에는 글 수정 및 삭제 기능, 댓글 기능, JWT 인증 및 권한 관리, 에러 핸들링 구조화, pytest를 활용한 테스트 자동화 등 보다 확장된 기능을 다룰 예정입니다.

 

실제로 서비스를 만들고 싶다면 지금 만든 코드를 바탕으로 Docker, GitHub Actions, AWS EC2 배포까지 이어가볼 수 있겠죠? 이제 여러분 손에 달렸습니다.

지금 당장 터미널을 열고 새로운 아이디어로 코딩을 시작해보세요.

세상에 단 하나뿐인 여러분만의 API 서비스가 될 수도 있으니까요!

반응형
반응형

파이썬 FastAPI 예외 처리, 테스트 및 프로젝트 구조화

실무에서 진짜 중요한 FastAPI 프로젝트의 마무리 3대장!
예외 처리, 테스트, 구조화로 완성도를 끌어올려보세요.

 

 

안녕하세요, 여러분!

오늘은 FastAPI를 활용한 프로젝트에서 완성도와 유지보수성을 높이는 핵심 주제 3가지를 함께 살펴보려 합니다.

FastAPI는 정말 빠르고 유연한 웹 프레임워크지만, 규모가 커지면 예외 처리와 테스트, 그리고 깔끔한 프로젝트 구조가 필수죠.

초반엔 하나의 파일에 모든 걸 몰아넣는 게 편하게 느껴질 수 있지만, 진짜 문제는 그 이후부터입니다.

에러가 터지거나, 기능이 많아지고, 팀과 협업하는 상황이 오면 코드 관리가 점점 지옥이 되거든요. 😓

그래서 오늘은 그런 혼돈을 예방할 수 있는 3가지 실전 기술을 소개하려고 합니다.

하나씩 차근차근 설명드릴게요. 초보자도 충분히 따라올 수 있으니 걱정 마세요!

 

1. 효율적인 프로젝트 구조 설계하기 🏗️

1.1 모듈화의 필요성과 폴더 구조

FastAPI 프로젝트가 커지기 시작하면 코드가 한 파일에 몰려 있으면 유지보수가 매우 어려워져요.

기능이 많아질수록 파일을 나누고, 책임을 분리하고, 폴더 구조를 체계적으로 구성하는 것이 필수입니다.

FastAPI에서 추천하는 기본적인 디렉터리 구조는 다음과 같습니다:

app/
├── main.py          # FastAPI 앱 실행 진입점
├── models.py        # SQLAlchemy 모델 정의
├── schemas.py       # Pydantic 스키마 정의
├── database.py      # DB 설정, 연결 관리
├── routers/
│   ├── __init__.py
│   ├── users.py     # 사용자 관련 API
│   └── todos.py     # 할일 관련 API
└── core/
    └── config.py    # 환경 변수 및 설정 관리

 

각 파일과 폴더가 맡는 역할이 명확하죠?

특히 routers 폴더는 기능별 라우트를 나누기 좋고, core 폴더에는 설정 파일을 정리할 수 있어요.

1.2 APIRouter로 구조화하는 방법

FastAPI에서는 APIRouter를 활용해서 라우팅을 기능 단위로 나눌 수 있어요.

예를 들어 todos 관련 API는 routers/todos.py

다음과 같이 구성합니다:

from fastapi import APIRouter, Depends, HTTPException
from .. import models, schemas
from ..database import get_db
from sqlalchemy.orm import Session

router = APIRouter(prefix="/todos", tags=["Todos"])

@router.get("/", response_model=List[schemas.TodoItem])
def list_todos(db: Session = Depends(get_db)):
    return db.query(models.Todo).all()

 

이제 main.py에서는 이렇게 간단히 router를 불러와 등록하면 돼요:

from fastapi import FastAPI
from .routers import users, todos

app = FastAPI()
app.include_router(users.router)
app.include_router(todos.router)

 

이런 식으로 각 기능은 독립적으로 관리되고, main에서는 전체 앱을 조립하는 역할만 하게 됩니다.

마치 블록처럼요!

1.3 모듈 간 의존성과 순환참조 방지

구조화할 때 주의할 점 중 하나는 순환 참조(Circular Import)입니다. 예를 들어 models.pyschemas.py를 import하고, schemas.py가 다시 models.py를 참조하면 문제 발생! 😵‍💫

해결 방법은 의외로 간단해요.

보통 schemas.py는 오직 필드 선언에만 집중하고, models.py는 비즈니스 로직 중심으로 구성합니다.

서로 직접 참조하지 않게 하고, 실제 연결은 routers/*.py 파일에서 이루어지도록 조정하면 돼요.

추가로 환경 설정은 core/config.py에 넣고, .env 파일과 pydantic.BaseSettings를 함께 쓰면 환경별로 설정을 유연하게 다룰 수 있어요.

 

 

2. APIRouter를 활용한 모듈화 📦

2.1 APIRouter란 무엇인가요?

FastAPI에서 APIRouter는 말 그대로 “라우터” 역할을 해요.

여러 개의 API 경로들을 하나로 묶어주는 객체인데, 마치 미니 FastAPI 인스턴스처럼 동작해요.

FastAPI의 구조화된 애플리케이션을 만들기 위해 필수적인 도구라고 볼 수 있죠.

main.py 하나에 모든 API 경로를 넣으면 작고 단순할 땐 괜찮지만, 규모가 커지면 지옥 같은 수정의 나락이 펼쳐집니다. 😱

그래서 각 기능별로 라우터를 나눠주는 것이 좋아요.

2.2 APIRouter 사용 예제

routers/todos.py 파일에서 할 일(Todo) 목록을 관리하는 API를 만든다고 가정해볼게요:

from fastapi import APIRouter, Depends, HTTPException
from typing import List
from .. import models, schemas
from ..database import get_db
from sqlalchemy.orm import Session

router = APIRouter(prefix="/todos", tags=["Todos"])

@router.get("/", response_model=List[schemas.TodoItem])
def list_todos(db: Session = Depends(get_db)):
    return db.query(models.Todo).all()

 

이제 main.py에 가서 해당 라우터를 연결하면 됩니다:

from fastapi import FastAPI
from .routers import todos

app = FastAPI()
app.include_router(todos.router)

 

prefix="/todos" 덕분에 /todos로 시작하는 모든 경로가 이 라우터에 포함됩니다.

tags는 자동 문서화(swagger UI)에서 그룹 이름처럼 사용되죠.

2.3 APIRouter의 이점 💡

  • 기능별로 API를 독립적으로 관리 가능 → 유지보수 용이
  • Swagger 문서에서 각 API 그룹을 구분해 보여줌 → 테스트 용이
  • 다른 파일과 독립적으로 테스트 가능 → 유닛 테스트 구성에도 유리

프로젝트가 점점 커질수록 APIRouter를 쓰는 구조는 선택이 아니라 필수가 됩니다.

기획자나 다른 개발자가 함께 보는 문서화된 API를 만드는 데도 매우 유리하거든요.

 

 

3. FastAPI의 예외 처리 방법 알아보기 ⚠️

3.1 HTTPException의 기본 사용법

FastAPI에서 예외를 처리할 때 가장 기본이 되는 클래스가 바로 HTTPException이에요.

이걸 이용하면 코드에서 명시적으로 에러 응답을 보낼 수 있죠.

예를 들면 이렇습니다:

from fastapi import HTTPException

if not user:
    raise HTTPException(status_code=404, detail="User not found")

 

이 코드가 실행되면 FastAPI는 자동으로 {"detail": "User not found"} 같은 JSON 응답과 함께 404 상태 코드를 반환합니다.

3.2 자주 쓰는 예외 처리 시나리오

  • 조회할 데이터가 없을 때 → raise HTTPException(status_code=404)
  • 사용자 인증 실패 시 → status_code=401 또는 403
  • 클라이언트의 잘못된 요청 → status_code=400과 적절한 메시지

detail에 넣는 메시지는 문자열이나 JSON 형태가 가능해서 에러 코드와 설명을 같이 담을 수도 있어요. 예:

raise HTTPException(
    status_code=400,
    detail={"error": "Invalid email", "code": 1001}
)

 

이렇게 하면 클라이언트에서 에러 코드를 받아서 처리하기 쉬워지죠.

3.3 FastAPI의 자동 유효성 검사

FastAPI는 입력 데이터의 유효성 검사를 자동으로 해줍니다.

예를 들어 Pydantic 모델에서 title: str로 지정해두면, 숫자나 null이 들어왔을 때 자동으로 422 Unprocessable Entity 오류를 반환해요.

다만, 값은 타입이 맞더라도 비즈니스 로직에 어긋나는 경우는 직접 HTTPException으로 처리해줘야 합니다.

예를 들어 제목이 빈 문자열이면 아래처럼 처리할 수 있어요:

if todo.title.strip() == "":
    raise HTTPException(status_code=400, detail="Title cannot be empty")

 

이처럼 자동 유효성 검사 + 비즈니스 검증을 조합하면, 훨씬 견고한 API를 만들 수 있어요.

 

 

4. 전역 예외 핸들러 활용법 💥

4.1 커스텀 예외 핸들러란?

FastAPI는 예외 처리기를 전역으로 등록할 수 있어요.

즉, 특정 예외가 발생했을 때 공통된 형식의 응답을 보내고 싶다면 핸들러를 따로 정의해서 자동으로 처리되게 할 수 있다는 말이죠.

예를 들어 데이터베이스의 제약 조건 위반 같은 경우, 매번 처리하기보다는 한 번에 묶어서 처리하면 편하겠죠?

4.2 SQLAlchemy 예외 핸들링 예제

SQLAlchemy에서 가장 자주 만나는 예외 중 하나는 IntegrityError입니다.

중복된 키 삽입, not null 위반 등에서 발생하는 예외죠.

이걸 전역으로 처리해봅시다.

from fastapi.responses import JSONResponse
from sqlalchemy.exc import IntegrityError
from fastapi import Request, FastAPI

app = FastAPI()

@app.exception_handler(IntegrityError)
async def integrity_error_handler(request: Request, exc: IntegrityError):
    return JSONResponse(
        status_code=400,
        content={"detail": "데이터베이스 제약 조건 위반 (중복, null 등)"}
    )

 

이제 어떤 라우터에서든 IntegrityError가 발생하면 자동으로 이 핸들러가 실행되어 공통된 메시지를 보내게 됩니다. 무척 깔끔하죠? 👍

4.3 RequestValidationError 커스터마이징

FastAPI는 입력 검증에 실패하면 기본적으로 422 오류와 함께 RequestValidationError를 발생시켜요. 그런데 이 에러 메시지가 너무 상세하거나 개발자스러워서, 사용자 입장에서는 당황스러울 수 있어요.

그럴 땐 아래처럼 기본 예외 핸들러를 오버라이딩해서 에러 메시지를 심플하게 바꿔줄 수 있습니다:

from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from fastapi.exception_handlers import request_validation_exception_handler

@app.exception_handler(RequestValidationError)
async def custom_validation_handler(request, exc):
    return JSONResponse(
        status_code=422,
        content={"detail": "입력 형식이 잘못되었습니다. 필수 값을 확인하세요."}
    )

 

물론 실제 서비스에서는 유형별 메시지 분기exc.errors()를 순회하여 더 정교하게 구성할 수도 있어요.

4.4 실무에서의 활용 포인트 💡

  • 모든 라우터에서 반복되는 예외 처리 코드 제거
  • 사용자에게 친절하고 일관된 오류 메시지 제공
  • 로깅과 모니터링에 유리한 구조 구성 가능

전역 예외 핸들러는 단순히 “에러를 막는” 게 아니라 일관성 있고 신뢰감 있는 서비스를 만드는 첫걸음입니다.

 

 

5. TestClient를 사용한 테스트 자동화 🧪

5.1 FastAPI의 TestClient란?

FastAPI는 Starlette를 기반으로 만들어졌기 때문에 TestClient라는 테스트 도구를 제공합니다.

이 도구는 실제 서버를 실행하지 않고도 app 객체를 직접 호출하여 테스트를 할 수 있도록 해주는 매우 강력한 기능이에요.

보통 pytest와 함께 사용되며, REST API의 요청/응답 시나리오를 자동화하는 데 아주 적합합니다.

즉, 매번 브라우저나 Postman으로 테스트하지 않아도 된다는 거죠!

5.2 기본 테스트 코드 작성법

아래는 간단한 예시입니다.

test_main.py 파일을 만들고 다음과 같은 테스트 코드를 작성해볼 수 있어요:

from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)

def test_create_todo():
    response = client.post("/todos", json={"title": "Test", "description": "Test Desc"})
    assert response.status_code == 201
    assert response.json()["title"] == "Test"

def test_read_todo_not_found():
    response = client.get("/todos/999")
    assert response.status_code == 404

 

위 테스트는 할일을 생성하고, 존재하지 않는 할일을 조회할 때 404를 반환하는지를 검증합니다.

.get, .post, .delete 등을 통해 실제 요청을 시뮬레이션할 수 있고, response.status_coderesponse.json()으로 응답 값을 비교합니다.

5.3 테스트에서 자주 검증하는 항목들

  • 상태 코드가 올바르게 반환되는가?
  • 응답 데이터의 필드/값이 기대한 대로 구성되어 있는가?
  • 에러 발생 시 에러 메시지가 정확히 반환되는가?

5.4 실무 팁 💡

  • 테스트 함수는 반드시 test_로 시작해야 pytest에서 인식합니다.
  • 반복되는 검증 로직은 함수로 분리해 재사용성을 높입니다.
  • 정상 케이스뿐 아니라 에러 케이스도 테스트하세요!

 

테스트는 개발자의 실수를 사전에 방지해주는 방패입니다.

특히 협업하거나 유지보수가 필요한 프로젝트에서는 테스트 코드의 유무가 신뢰성과 품질의 기준이 되기도 해요.

 

 

6. 테스트 환경의 DB 처리 전략 🗄️

6.1 테스트에 실제 DB를 쓰면 안 되는 이유

실제 개발용 데이터베이스를 테스트에 사용하면 어떤 일이 벌어질까요?

예기치 않게 데이터를 삭제하거나 오염시킬 수 있습니다. 😱

특히 DELETEDROP 같은 쿼리를 테스트하는 경우에는 더더욱 위험하죠.

그래서 테스트 환경에서는 격리된 테스트 전용 DB를 사용하는 것이 원칙입니다.

6.2 메모리 SQLite DB 사용 예제

FastAPI에서 SQLite의 메모리 DB는 테스트 환경을 빠르게 구성할 수 있는 좋은 도구입니다.

아래는 예시 설정입니다:

# test_db.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from app.models import Base

SQLALCHEMY_DATABASE_URL = "sqlite:///:memory:"

engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

def override_get_db():
    db = TestingSessionLocal()
    try:
        yield db
    finally:
        db.close()

 

그리고 main.py 또는 conftest.py에서 FastAPI의 의존성을 오버라이드 해줍니다:

from app.main import app
from app.test_db import override_get_db

app.dependency_overrides[get_db] = override_get_db

 

이렇게 하면 테스트에서 사용하는 get_db()가 실제 DB가 아닌 임시 메모리 DB로 교체됩니다.

테스트마다 새로운 환경에서 시작되니 데이터 충돌 걱정 없이 깔끔하게 실행 가능해요.

6.3 데이터 초기화 전략

테스트 전에 테이블을 생성하고 데이터를 초기화해주는 것도 중요합니다.

아래처럼 setup_function 혹은 pytest.fixture를 사용해 테스트 시작 전에 초기 세팅을 할 수 있어요.

import pytest
from app.test_db import engine, Base

@pytest.fixture(scope="function", autouse=True)
def setup_and_teardown():
    Base.metadata.create_all(bind=engine)
    yield
    Base.metadata.drop_all(bind=engine)

 

이렇게 하면 테스트마다 DB가 깨끗하게 리셋되기 때문에, 데이터 충돌로 인한 테스트 실패를 방지할 수 있어요.

6.4 실전 활용 팁 💡

  • 테스트용 DB URL을 별도로 구성하고 설정 파일로 분리하세요.
  • 테스트 중 발생하는 로그는 최소화하여 결과에 집중할 수 있도록 합니다.
  • CI 환경에서는 --disable-warnings 옵션을 이용해 깔끔한 출력 유지

 

테스트의 핵심은 "예측 가능한 상태에서 테스트가 반복 가능해야 한다"는 것이에요.

이를 위해 테스트용 DB는 선택이 아닌 필수 전략입니다.

 

 

마무리하며 🌱

이번 글에서는 FastAPI 프로젝트의 후반부에서 반드시 챙겨야 할 예외 처리, 테스트, 구조화 전략에 대해 하나씩 짚어봤습니다.

 

단순히 기능을 구현하는 것을 넘어서, 확장성과 유지보수성을 고려한 설계가 왜 중요한지를 느끼셨을 거예요.

특히 예외 처리에서는 HTTPException과 전역 핸들러의 활용, 테스트에서는 TestClient와 SQLite 메모리 DB의 유용함, 그리고 APIRouter를 통한 구조화는 프로젝트의 ‘완성도’를 좌우합니다.

코드가 잘 돌아가는 것도 좋지만, 에러에 강하고 테스트가 보장된 프로젝트가 진짜 안정적인 프로젝트예요.

FastAPI를 쓰면 API를 정말 빠르게 만들 수 있지만, 그 위에 신뢰성과 관리 용이성이라는 무기를 더해보세요. 그게 바로 한 단계 높은 개발자의 길이니까요. 😉

 

이제 여러분의 FastAPI 프로젝트는 단순한 샘플이 아닌, 실제 서비스로 연결될 수 있는 기반이 갖춰졌습니다. 이제 자신 있게 확장하고 테스트하고, 에러를 두려워하지 마세요!

반응형

+ Recent posts