며칠 뒤 입사를 앞두고 미리 공부를 해보고자 간단한 할일 관리 API를 만들어봤다.
2월 MS AI School 수료 후 오랜만에,, 거의 4개월 만에 Python을 다시 만져보는 거라 기초부터 차근차근 진행했는데, 생각보다 FastAPI가 정말 직관적이고 사용하기 쉬웠다.
목표: FastAPI 기본 개념 익히기
기간: 하루
기능: 할일 CRUD(생성, 조회, 수정, 삭제)
todo_api/
├── main.py # 메인 애플리케이션
├── database.py # 데이터베이스 설정
├── models.py # SQLAlchemy 모델
├── schemas.py # Pydantic 스키마
└── requirements.txt
간단한 구조지만 실제 실무에서도 많이 사용하는 패턴이라고 한다.
from fastapi import FastAPI
app = FastAPI(title="할일 관리 API", version="1.0.0")
@app.get("/")
def read_root():
return {"message": "Hello FastAPI!"}
Flask보다도 직관적이게 느껴지는 건,, 그 때보다 내가 조금은 성장했기 때문이려나?
아니면 실제 직관적인가.
FastAPI에서 가장 인상 깊었던 기능은 자동 API 문서 생성이었다. 코드만 작성하면 http://localhost:8000/docs
에서 바로 Swagger UI를 만들어줌;;
Java에서 Swagger 설정하던 눈물의 시간들이 떠오른다..
@app.get("/todos/{todo_id}", response_model=schemas.Todo)
def get_todo(todo_id: int, db: Session = Depends(get_db)):
# ...
타입 힌팅을 제대로 해주면:
class TodoCreate(BaseModel):
title: str
description: Optional[str] = None
요청 데이터가 자동으로 검증되고, 잘못된 데이터가 오면 명확한 에러 메시지를 보여준다.
딱 하나 마주한 에러.
PydanticSchemaGenerationError: Unable to generate pydantic-core schema for <class 'sqlalchemy.sql.sqltypes.Boolean'>
문제: Java에서는 Boolean
이 자연스러워서 그대로 사용
해결: Python에서는 소문자 bool
을 사용
# ❌ Java 스타일로 작성
class Todo(BaseModel):
completed: Boolean # Java처럼 대문자
# ✅ Python 스타일로 수정
class Todo(BaseModel):
completed: bool # Python은 소문자
Java에서는 Boolean
, String
, Integer
같은 래퍼 클래스를 사용하지만,
Python에서는 bool
, str
, int
같은 기본 타입을 사용한다는 것을 다시 한번 상기하며,,,
몇 년 전에 처음 Python 쓸 때는 VS Code가 훨씬 편했던 것 같은데, Professional 버전이라 그런가 아니면 IntelliJ에 익숙해져서 그런가 PyCharm이 이제는 꽤나 만족스럽다.
GET /todos
- 모든 할일 조회GET /todos/{id}
- 특정 할일 조회POST /todos
- 새 할일 생성PUT /todos/{id}/complete
- 할일 완료 처리DELETE /todos/{id}
- 할일 삭제def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get("/todos")
def get_todos(db: Session = Depends(get_db)):
# db는 자동으로 주입됨
return db.query(Todo).all()
;
없이 들여쓰기로만 구분하는 파이썬 문법에 오랜만에 어색함을 느끼며,,
@Service
public class TodoService {
@Autowired
private SessionFactory sessionFactory;
public List<Todo> getTodos() {
Session session = sessionFactory.getCurrentSession();
return session.createQuery("FROM Todo", Todo.class).list();
}
}
@RestController
public class TodoController {
@Autowired
private TodoService todoService; // 의존성 주입
@GetMapping("/todos")
public List<Todo> getTodos() {
return todoService.getTodos();
}
}
이렇게 보니까 자바가 진짜 복잡하다는 게 새삼..
측면 | Java Spring | Python FastAPI |
---|---|---|
어노테이션 | @Autowired , @Service | Depends() 함수 |
설정 | XML/Java Config 클래스 | 함수 정의만으로 충분 |
생명주기 관리 | Spring Container | yield 로 명시적 관리 |
타입 안정성 | 컴파일 타임 체크 | 런타임 + 타입 힌트 |
FastAPI의 Depends()
는 Spring의 @Autowired
와 같은 역할을 한다.
yield
를 사용한 리소스 관리가 Python 답게 깔끔하다.
from fastapi import HTTPException
@app.get("/todos/{todo_id}")
def get_todo(todo_id: int):
todo = db.query(Todo).filter(Todo.id == todo_id).first()
if todo is None:
raise HTTPException(status_code=404, detail="할일을 찾을 수 없습니다.")
return todo
@RestController
public class TodoController {
@GetMapping("/todos/{id}")
public ResponseEntity<Todo> getTodo(@PathVariable Long id) {
Todo todo = todoService.findById(id);
if (todo == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(todo);
}
// 또는 Exception Handler 사용
@ExceptionHandler(TodoNotFoundException.class)
public ResponsEntity<ErrorResponse> handleNotFound(TodoNotFoundException ex) {
return ResponseEntity.status(404)
.body(new ErrorResponse("할일을 찾을 수 없습니다."));
}
}
측면 | Java Spring | Python FastAPI |
---|---|---|
에러발생 | ResponseEntity 반환 또는 Exception throw | HTTPException raise |
상태코드 | ResponseEntity.notFound() | status_code=404 |
에러메시지 | 별도 ErrorResponse 클래스 | detail 파라미터 |
글로벌처리 | @ExceptionHandler | Exception Handler 함수 |
FastAPI에서는 Python 예외 처리 방식을 그대로 활용해서 raise HTTPException
으로 간단하게 에러를 던질 수 있어서 직관적이었다.
일단 내가 오늘 경험한 장점으로는
오늘은 다시 Python 감을 찾느라고 기본 CRUD만 구현해봤는데,
까지 차근차근 입사 전까지 공부해보면 좋겠다.
조금 더 여유가 된다면 웹크롤링까지!
오랜만에 Python 다시 만져봤는데 너무 재밌다.
잠 안 자고 더 하고 싶은 마음이 들지만,, 컨디션을 관리해야 하므로.
입사하면 실제로 FastAPI를 쓰게 될텐데, 벌써 새로운 공부들이 기대된다 히히 😛