Development environment

[FastAPI] FastAPI 개발하기3 - 엔드포인트와 라우터

hjjummy 2026. 2. 12. 17:08

앞서 FastAPI 개발하기 2편에서 CORS 개념을 정리하였다.
CORS를 통해 브라우저 보안 모델을 이해했다면, 이제는 서버 내부 구조를 이해할 차례이다.

2026.02.06 - [Development environment] - [FastAPI] FastAPI로 백엔드 개발하기 2 - CORS 개념 및 설정

 

[FastAPI] FastAPI로 백엔드 개발하기 2 - CORS 개념 및 설정

― CORS 개념부터 실제 설정 의미까지 정리 FastAPI로 API 서버를 만들때 반드시 마주치는 설정이 CORS이다.특히 프론트엔드(React, Vue 등)와 API 서버를 분리해서 개발할 때 CORS 설정이 없으면 브라우저

hjjummy.tistory.com

 

CORS 다음으로 반드시 마주치는 개념이 엔드포인트(endpoint)라우터(router) 이다.

처음에는 다음과 같은 코드 한 줄로 API가 완성된 것처럼 보인다.

@app.get("/health")
async def health():
    return {"status": "ok"}

하지만 프로젝트가 조금만 커져서 엔드포인트가 늘어나기 시작하면 다음과 같은 문제가 발생한다.

  • main.py에 라우팅 코드가 계속 늘어나며 파일이 비대해짐
  • 비즈니스 로직과 HTTP 핸들링 코드가 섞여서 읽기 어려워짐
  • 버전(v1/v2), 도메인(users/search/admin) 단위로 분리하기 어려움
  • 팀 작업에서 API 위치를 찾는 비용 증가

이번 글에서는

1. 엔드포인트의 구성 요소
2. URL · 메서드 · 함수의 관계
3. 라우터의 역할
4. 라우터가 필수인지 여부
5. 라우터를 사용하는 경우와 그렇지 않은 경우의 차이

를 중심으로 FastAPI의 구조를 정리하려고 한


1. 엔드포인트(endpoint)란 ?

엔드포인트는 클라이언트가 호출하는 API의 ‘진입 지점’

 

좀 더 정확히 말하면, 다음 3가지 조합으로 하나의 엔드포인트가 결정된다.

  • HTTP 메서드 (GET/POST/PUT/DELETE …)
  • 경로(Path) (/search, /users/{id} …)
  • 핸들러 함수(처리 함수) (요청을 받아서 응답을 반환하는 함수)

예를 들어 아래는 하나의 엔드포인트이다.

@app.get("/health")
async def health():
    return {"status": "ok"}

의미는 다음과 같다.

  • GET /health 요청이 오면
  • health() 함수를 실행하고
  • 결과를 JSON 응답으로 반환한다.
즉 엔드포인트 = URL + HTTP 메서드 + 함수
  • URL = /health
  • 메서드 = GET
  • 함수 = health()

1.1 URL이란?

URL은 클라이언트가 요청을 보내는 주소이다.

http://localhost:8003/api/v1/search

 

구성 요소 의미
http 프로토콜
localhost 서버 주소
8003 포트 번호
/api/v1/search 경로(Path)

FastAPI 코드에서 우리가 직접 정의하는 부분은 경로(Path) 이다.

 

여기서 "/search"는 전체 URL이 아니라 경로이다.

👉 URL은 “어디에 접근하는가”를 구분하는 역할을 한다.

  • /users → 사용자 목록
  • /users/1 → 특정 사용자
  • /search → 검색 API
  • /health → 서버 상태 확인

위 처럼 구분이 되도록 설정하는것이 좋다.


1.2 HTTP 메서드란?

HTTP 메서드는 “무엇을 하려는가”를 나타낸다.

  • GET → 데이터 조회
  • POST → 데이터 생성
  • PUT → 전체 수정
  • DELETE → 삭제

같은 URL이라도 메서드가 다르면 완전히 다른 엔드포인트가 된다.

 

@app.get("/users")
async def get_users():
    ...

@app.post("/users")
async def create_user():
    ...
  • GET /users → 조회
  • POST /users → 생성

1.3 함수 (Function)란?

함수는 실제로 동작하는 프로그램 코드 이다.

@app.get("/users")
async def get_users():
    return {"data": []}

여기서 get_users()가 함수이다.


2. 라우터(router)란 ?

라우터는 엔드포인트들을 묶어서 관리하는 그룹 단위

 

엔드포인트가 몇 개 안 될 때는 굳이 필요하지 않다.
그러나 서비스가 커질수록 엔드포인트는 수십~수백 개로 늘어난다.

 

이를 main.py에 모두 작성하면 유지보수가 어려워진다.

그래서 FastAPI는 APIRouter라는 기능을 제공한다.

 

라우터는 다음을 담당한다.

  • 관련 엔드포인트를 파일 단위로 묶음
  • 공통 prefix 설정 (/api/v1/search 등)
  • 공통 tags 설정 → Swagger 문서 그룹화
  • 공통 dependency(인증 등) 적용

정리하면 다음과 같다.

엔드포인트 = API 1개
라우터 = 엔드포인트 묶음(그룹)


3. FastAPI에서 app과 router의 관계

FastAPI의 app은 서버 전체의 중심 객체이다.
라우터는 그 app에 등록되어 동작한다.

.

구조는 다음과 같다.

  • APIRouter()로 라우터 생성
  • 라우터에 엔드포인트 정의
  • app.include_router(router)로 앱에 등록
from fastapi import FastAPI
from fastapi.routing import APIRouter

app = FastAPI()
router = APIRouter()

@router.get("/health")
async def health():
    return {"status": "ok"}

app.include_router(router)

이 라우터를 사용한 구조의 장점은 다음과 같다.

  • 라우터 단위로 파일 분리가 가능해짐
  • 도메인 단위로 API를 관리 가능
  • 프로젝트가 커져도 main.py는 얇게 유지 가능해짐

4. 라우터는 필수인가?

결론부터 말하면 필수가 아니다.

FastAPI는 다음 코드만으로도 완벽히 동작한다.

app = FastAPI()

@app.get("/health")
async def health():
    ...

 

라우터는 구조를 정리하기 위한 도구이지, 반드시 사용해야 하는 문법은 아니다.

 

4.1. 라우터 없이 사용하는 경우 vs 사용하는 경우 비교

- 1. 라우터 없이 작성 경우 -> main 파일에 작성

from fastapi import FastAPI

app = FastAPI()

@app.get("/health")
async def health():
    return {"status": "ok"}

@app.get("/users")
async def get_users():
    return {"data": []}

@app.post("/users")
async def create_user():
    return {"status": "created"}

장점: 구조가 단순함, 작은 프로젝트에 적합, 빠르게 개발 가능

단점: 파일이 빠르게 비대해짐, 기능 단위 분리가 어려움, 협업 시 관리 어려움


- 2. 라우터를 사용하는 경우- > router.py와 main 파일 분리

users_router.py

from fastapi import APIRouter

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

@router.get("/")
async def get_users():
    return {"data": []}

@router.post("/")
async def create_user():
    return {"status": "created"}

 

main.py

from fastapi import FastAPI
from routers.users_router import router as users_router

app = FastAPI()
app.include_router(users_router)

 

 


5. 언제 라우터를 쓰는 것이 좋은가? 라우터 설계 방식은?

개발자 스타일마다 다르지만,  다음 중 하나라도 해당하면 사용하는 것이 좋다.

  • 엔드포인트가 5~10개 이상
  • 기능이 명확히 분리됨 (/search, /users, /admin 등)
  • API 버전 관리 필요
  • 인증/권한같은 공통 로직을 도메인 단위로 적용하고 싶음
  • 팀 단위 협업

작은 토이 프로젝트라면 굳이 필요하지 않다.
그러나 서비스로 운영할 계획이라면 초기에 구조를 잡는 것이 유리하다.

5-1. 도메인 기준 분리

  • search 관련 API → search_router.py
  • auth 관련 API → auth_router.py
  • users 관련 API → users_router.py

이 방식이 가장 일반적이다.

 

5-2. 버전(prefix) 관리

서비스가 운영되면 API 변경이 필연적으로 생긴다.
이때 v1/v2를 분리하지 않으면 FE/외부 연동이 한 번에 깨질 수 있다.

그래서 보통 아래처럼 prefix를 둔다.

  • /api/v1/...
  • /api/v2/...

라우터에 prefix를 붙이면 구조가 깔끔해진다.


5-3. 라우터 있는 경우 구조

search_app/
├── main.py
├── schemas.py
├── routers/
│   └── search_router.py
├── services/
│   └── search.py
├── requirements.txt
└── search_api.log

역할 분리

  • main.py → 앱 생성 + 라우터 등록
  • routers → HTTP 엔드포인트 정의
  • services → 비즈니스 로직
  • schemas → Request/Response 계약

이 구조는 코드가 짧은 프로젝트에도 부담이 적고, 동시에 확장 가능하다.


마무리

CORS가 브라우저와 서버 사이의 보안 규칙이라면,
엔드포인트와 라우터는 서버 내부 설계의 기본 구조이다.

 

핵심 정리

  • 엔드포인트 = URL + 메서드 + 함수
  • 라우터 = 엔드포인트 묶음
  • 라우터는 필수가 아니라 구조 관리 도구
  • 작은 프로젝트는 없어도 가능
  • 서비스 규모라면 사용하는 것이 권장됨

FastAPI를 잘 사용한다는 것은 문법을 많이 아는 것이 아니라, 상황에 맞게 적절한 구조를 설계할 수 있는 것이다!