0. 학습 이유
재직 중 국비 교육으로 Multi AI Agent 프로젝트 과정을 수강하게 되었고,
백엔드 구현 기술로 FastAPI를 사용하게 되었다.
기존에 Python을 사용해 본 경험은
4년전 첫 직장에서 입사 초기에 3~4개월 가량 진행했던 Spoon Radio 백오피스 Django 프로젝트를
Kotlin + Spring Boot로 컨버팅했던 것이 전부라
사실상 Python과 FastAPI 모두 처음이라고 봐도 무방한 상태였다.
이번 프로젝트를 진행하면서
FastAPI 프레임워크를 학습해야 했고,
이 과정에서 Python 언어 자체도 함께 다시 정리할 필요가 있다고 생각했다.
그래서 FastAPI를 학습하면서
프레임워크 사용 방법뿐만 아니라
Python 기본 문법과 비동기 처리 방식까지 같이 정리하려고 한다.
이 글은 FastAPI를 얕게 학습하면서 정리한 내용이다.
2. FastAPI란?
FastAPI는 Python으로 API 서버를 만들기 위한 웹 프레임워크이다.
공식 문서에서는 FastAPI를 다음과 같이 설명한다.
- 빠름: (Starlette과 Pydantic 덕분에) NodeJS 및 Go와 대등할 정도로
- 매우 높은 성능. 사용 가능한 가장 빠른 파이썬 프레임워크 중 하나.
- 빠른 코드 작성: 약 200%에서 300%까지 기능 개발 속도 증가. *
- 적은 버그: 사람(개발자)에 의한 에러 약 40% 감소. *
- 직관적: 훌륭한 편집기 지원. 자동완성이 모든 곳에서 동작. 적은 디버깅 시간.
- 쉬움: 쉽게 사용하고 배우도록 설계. 적은 문서 읽기 시간.
- 짧음: 코드 중복 최소화. 각 매개변수 선언의 여러 기능. 적은 버그.
- 견고함: 준비된 프로덕션 용 코드를 얻으십시오. 자동 대화형 문서와 함께. 표준 기반: API에 대한 (완전히 호환되는) 개방형 표준 기반: OpenAPI (이전에 Swagger로 알려졌던) 및 JSON Schema.
3. Spring과 FastAPI API(Input Adapter) 처리 방식 비교
3.1 Endpoint 정의
- 비교
Spring:Annotation기반으로 컨트롤러와 요청 URL을 매핑한다.FastAPI:Decorator기반으로 함수와 요청 URL을 직접 매핑한다.
- 차이
- 구조적으로
Spring은 클래스 > 함수 계층으로 구성되어있어 클래스별로 그 역할이 명확하게 드러나고,FastAPI는 함수 단위로 매핑되어 간결하고 빠르게 작성가능.
- 구조적으로
- 코드
Spring1 2 3 4 5 6 7 8 9 10 11 12
@RestController @RequestMapping("/users") public class UserController { @GetMapping public String getUsers() { return "users"; } @PostMapping public String createUser() { return "created"; } }
FastAPI1 2 3 4 5 6 7 8
from fastapi import FastAPI app = FastAPI() @app.get("/users") def get_users(): return {"result": "users"} @app.post("/users") def create_user(): return {"result": "created"}
3.2 Path Variable
차이 :
Spring에서는@Pathvariable을 이용하여 함수 파라미터로 선언해야 했으나FastAPI에서는 경로 변수와 함수 인자명이 같으면 자동으로 매칭이된다.코드
Spring1 2 3 4
@GetMapping("/users/{id}") public String getUser(@PathVariable Long id) { return "user id = " + id; }
FastAPI1 2 3
@app.get("/users/{id}") def get_user(id: int): return {"user_id": id}
3.3 Query Parameter
차이 :
Spring에서는@RequestParam을 이용하여 함수 파라미터로 선언해야 했으나FastAPI에서는 경로 변수와 함수 인자명이 같으면 자동으로 매칭이된다.코드
Spring1 2 3 4
@GetMapping("/users") public String getUsers(@RequestParam(required = false) String name) { return "name = " + name; }
FastAPI1 2 3 4 5 6
from typing import Optional from fastapi import FastAPI app = FastAPI() @app.get("/users") def get_users(name: Optional[str] = None): return {"name": name}
3.4 Request Body
차이 :
Spring은@RequestBody를 이용하여 단순 객체 바인딩을 지원하지만FastAPI는Pydantic모델을 사용하여 타입 검증 + 문서 생성 자동화를 지원해준다.코드
Spring1 2 3 4 5 6 7 8 9 10 11 12 13 14
public class UserRequest { private String name; private int age; public String getName() { return name; } public int getAge() { return age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } } @PostMapping("/users") public String createUser(@RequestBody UserRequest request) { return request.getName(); }
FastAPI1 2 3 4 5 6 7 8 9 10 11 12
from pydantic import BaseModel from fastapi import FastAPI app = FastAPI() class UserRequest(BaseModel): name: str age: int @app.post("/users") def create_user(request: UserRequest): return {"name": request.name}
3.5 Validation
차이 :
Spring은@Valid,Bean Validation을 이용하여 어노테이션 기반 검증,FastAPI는Pydantic을 이용하여 타입 + 필드 제약 기반 검증을 지원해준다.코드
Spring1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
public class UserRequest { @NotBlank private String name; @Min(1) private int age; public String getName() { return name; } public int getAge() { return age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } } @PostMapping("/users") public String createUser(@Valid @RequestBody UserRequest request) { return "ok"; }
FastAPI1 2 3 4 5 6 7 8 9 10 11 12
from pydantic import BaseModel, Field from fastapi import FastAPI app = FastAPI() class UserRequest(BaseModel): name: str = Field(..., min_length=1) age: int = Field(..., ge=1) @app.post("/users") def create_user(request: UserRequest): return {"result": "ok"}
3.6 Response 처리
차이 :
Spring객체 반환 또는ResponseEntity로 감싸서 상태코드 제어 반환,FastAPIdict,Pydantic Model, 객체 반환이 기본, 필요시 상태 코드 지정.코드
Spring1 2 3 4 5 6 7
@GetMapping("/users/{id}") public ResponseEntity<UserResponse> getUser(@PathVariable Long id) { UserResponse response = new UserResponse(id, "angrypig"); return ResponseEntity.ok(response, HttpStatus.CREATED); } public record UserResponse(Long id, String name) {}
FastAPI1 2 3 4 5 6 7 8 9
from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class UserResponse(BaseModel): id: int name: str @app.get("/users/{id}", response_model=UserResponse, status_code=status.HTTP_201_CREATED) def get_user(id: int): return UserResponse(id=id, name="angrypig")
3.7 의존성 주입 (DI)
차이 :
Spring생성자 주입, 컨테이너 기반DI,FastAPI함수기반DI코드
Spring1 2 3 4 5 6 7 8 9 10 11 12
@RestController @RequestMapping("/users") public class UserController { private final UserService userService; public UserController(UserService userService) { this.userService = userService; } @GetMapping public String getUsers() { return userService.getUser(); } }
FastAPI1 2 3 4 5 6 7 8 9 10 11 12 13 14
from fastapi import Depends, FastAPI app = FastAPI() class UserService: def get_user(self) -> str: return "user" def get_user_service() -> UserService: return UserService() @app.get("/users") def get_users(user_service: UserService = Depends(get_user_service)): return {"result": user_service.get_user()}
3.8 비동기 처리
차이 :
Spring기본 동기 처리, 동기 처리시WebFlux학습 필요,FastAPIasync def로 간단히 사용 가능.코드
Spring: 구성 코드는 같으나spring-boot-starter-web이 아닌org.springframework.boot:spring-boot-starter-webflux의존을 가지고 있음.1 2 3 4
@GetMapping("/users") public String getUsers() { return "users"; }
FastAPI1 2 3 4 5
from fastapi import FastAPI app = FastAPI() @app.get("/users") async def get_users(): return {"result": "users"}
4. Spring과 FastAPI HTTP 처리 방식 비교
4.1 HttpRequest 접근
차이 :
SrpingServlet객체 직접 사용,FastAPIStarlette Request(ASGI기반)사용코드
Spring1 2 3 4 5
@GetMapping("/test") public String test(HttpServletRequest request) { String ip = request.getRemoteAddr(); return ip; }
FastAPI1 2 3 4 5 6
from fastapi import FastAPI, Request app = FastAPI() @app.get("/test") def test(request: Request): ip = request.client.host return {"ip": ip}
4.2 HttpResponse 접근
차이 :
SpringResponseEntity많이 사용 직접적으로HttpServletResponse호출할일이 거의 없음(호출 케이스 : 파일 작업,Filter작업)코드
Spring1 2 3 4
@GetMapping("/test") public ResponseEntity<String> test() { return ResponseEntity.ok("ok"); }
FastAPI1 2 3 4 5 6
from fastapi import Response, FastAPI app = FastAPI() @app.get("/test") def test(response: Response): response.status_code = 200 return {"result": "ok"}
4.3 Header 처리
차이 :
Spring어노테이션 기반 처리(HttpRequest를 통해서도 가능),FastAPI함수 파라미터 기반코드
Spring1 2 3 4
@GetMapping("/header") public String header(@RequestHeader("User-Agent") String userAgent) { return userAgent; }
FastAPI1 2 3 4 5
from fastapi import Header from typing import Optional @app.get("/header") def header(user_agent: Optional[str] = Header(None)): return {"ua": user_agent}
4.4 Cookie 처리
차이 :
Spring어노테이션 기반 처리(HttpRequest를 통해서도 가능),FastAPI함수 파라미터 기반코드
Spring1 2 3 4 5 6
@GetMapping("/cookie") public String cookie( @CookieValue(value="sessionId", required=false) String sessionId ) { return sessionId; }
FastAPI1 2 3 4 5 6 7 8
from fastapi import Cookie from typing import Optional @app.get("/cookie") def cookie( session_id: Optional[str] = Cookie(default=None, alias="sessionId") ): return {"session_id": session_id}
4.5 Responses
차이 :
Spring서버 세션 기본 기원,FastAPI기본 제공이 없어서 직접 구현하여 사용.코드
Spring1 2 3 4 5
@GetMapping("/session") public String session(HttpSession session) { session.setAttribute("user", "angrypig"); return (String) session.getAttribute("user"); }
FastAPI- 기본 세션을 제공하지 않아 아래의 방법중 택해서 사용
Cookie+JWTRedis SessionMiddlewareStarlette Session
- 기본 세션을 제공하지 않아 아래의 방법중 택해서 사용
5. 느낀점
FastAPI를 학습하면서 느낀점은 아직 프레임워크 내부 구조까지 깊게 들어가 보지는 않았고,
API 서버를 구성하는 수준에서만 사용해 본 상태이다.
그래서 FastAPI 자체를 깊게 이해했다고 보기는 어렵지만,
기존에 Spring을 사용하면서 HTTP 요청 처리 방식이나
Controller, Request, Response 구조에 익숙해져 있어서
공식 문서를 보면서 학습하는 데에는 큰 어려움은 없었다.
오히려 FastAPI는 구조가 단순하고
기본 기능들이 자동으로 연결되는 부분이 많아서
Spring보다 훨씬 빠르게 API 서버를 만들 수 있다는 느낌을 받았다.
특히 타입 힌트 기반으로 요청을 받고,
자동으로 검증과 문서 생성까지 되는 부분은
개발 속도를 크게 높여주는 요소라고 생각했다.
그리고 FastAPI의 가장 큰 장점은
Python 생태계를 그대로 사용할 수 있다는 점이라고 느꼈다.
이번 프로젝트처럼
LLM호출AI Agent구성- 외부
Python라이브러리 사용
같은 작업을 해야 할 경우
Python 기반으로 구현하는 것이 훨씬 자연스럽고 편했다.
Java로도 구현이 가능하긴 하지만
결국 Python 코드를 따로 실행하거나
외부 프로세스를 호출해야 하는 경우가 많아서
개발 과정이 상당히 번거로워질 것 같다는 느낌을 받았다.
이런 점에서 FastAPI는
AI서버LLM서버Agent서버
같이 Python 라이브러리를 적극적으로 사용해야 하는 프로젝트에서
매우 적합한 프레임워크라고 생각했다.
아직 FastAPI를 깊게 사용해 본 것은 아니지만,
이번 프로젝트를 통해 기본적인 구조와 특징을 이해할 수 있었고
앞으로 AI 관련 프로젝트를 진행할 때 자주 사용하게 될 것 같다.