
FastAPI는 가볍고 빠르게 API를 개발할 수 있는 프레임워크다. 비동기 I/O를 쉽게 지원하며, Pydantic을 활용한 데이터 검증과 직렬화가 편리하다. 하지만 내가 FastAPI를 사용했던 이유는 비동기 처리 때문이 아니라 단순히 사용하기 편하고 경량화된 프레임워크였기 때문이었다.
FastAPI를 사용하면서 API 개발 자체는 간결하고 효율적이었지만, 점점 기능이 확장되면서 더 복잡한 요구사항을 처리해야 했다. 그 과정에서 Spring으로 마이그레이션할 필요성이 생겼다.
Spring은 개발자에게 많은 기능을 제공해주는 편리한 프레임워크지만, 처음 실무에서 접했을 때 여러 가지 어려움이 있었다. 특히 JPA 연관관계 설정과 자동 구성(Auto Configuration), 상속 관계에서 발생하는 문제에서 많은 시행착오를 겪었다.
FastAPI에서는 SQLAlchemy를 사용하여 데이터 조회 최적화를 진행했지만, Spring에서는 JPA(Hibernate)를 사용하면서 여러 성능 이슈가 발생했다. 특히 연관관계를 맺은 엔티티를 조회할 때 불필요한 조인과 다량의 쿼리가 실행되는 문제를 해결하는 과정에서 중요한 경험을 얻었다.
LIMIT이나 OFFSET을 사용할 경우 데이터베이스 내부적으로 모든 데이터를 조회한 후 페이징을 적용하기 때문에 성능 저하 발생.@EntityGraph 또는 JOIN FETCH를 사용하여 단일 쿼리로 데이터를 가져옴.SELECT u FROM User u JOIN FETCH u.orders WHERE u.id = :idcreateNativeQuery를 활용하여 페이징 쿼리를 직접 작성.SELECT * FROM orders o JOIN users u ON o.user_id = u.id WHERE u.status = 'ACTIVE' LIMIT 10 OFFSET 0SELECT new com.example.dto.UserOrderDTO(u.id, u.name, o.id, o.totalPrice) FROM User u JOIN u.orders o WHERE u.status = 'ACTIVE'이 과정을 통해 JPA의 기본 동작을 그대로 따르는 것이 항상 최적은 아니며, 상황에 맞게 Fetch Join, 네이티브 쿼리, DTO Projection을 적절히 활용해야 한다는 점을 체감했다.
이 과정에서 CQRS(Command Query Responsibility Segregation) 패턴을 알게 되었고, 읽기와 쓰기를 분리하는 방식이 성능 최적화에 적절하다는 점을 체감했다.
CQRS(Command Query Responsibility Segregation)은 읽기(쿼리)와 쓰기(커맨드)의 책임을 분리하는 아키텍처 패턴이다. 단일 데이터 모델을 사용하여 읽기와 쓰기를 모두 수행하는 전통적인 방식과 달리, 읽기 전용 모델과 쓰기 전용 모델을 분리함으로써 성능 최적화와 확장성을 높일 수 있다. 이 패턴을 적용하면서, 복잡한 조회는 별도의 최적화된 쿼리를 활용하고, 쓰기 연산은 도메인 로직을 통해 일관성을 유지하는 접근 방식을 고려했다.
JPA에서 @Inheritance(strategy = InheritanceType.JOINED)을 사용하여 상속 관계를 정의할 때, @DiscriminatorColumn을 통해 DTYPE을 명시적으로 관리했다. 그러나, 필드 셰도윙(Field Shadowing) 문제로 인해 데이터가 null이 되는 문제가 발생했다.
@MappedSuperclass를 사용하지 않고 SINGLE_TABLE 전략을 사용할 경우 DTYPE이 예상과 다르게 동작할 가능성이 있음.이 과정에서 JPA의 동작 방식과 상속 매핑 전략을 더욱 깊이 이해하게 되었으며, ORM을 사용할 때 발생할 수 있는 예기치 않은 문제들에 대한 대응력을 키울 수 있었다.
Spring은 다양한 자동 구성을 제공하여 개발자가 직접 설정하지 않아도 편리하게 사용할 수 있도록 지원한다. 하지만 이러한 자동 구성은 내부 동작을 정확히 이해하지 못하면 오히려 장애를 유발할 수 있다.
@ConfigurationProperties, @Bean 등을 활용하여 원하는 설정을 명확히 정의.application.yml에서 설정 값을 명확히 관리하여 불필요한 오버라이딩을 방지.이 과정에서 제어의 역전(IoC)이란 무엇인지 더 깊이 이해하게 되었고, Spring이 많은 것을 자동으로 해주지만 결국 개발자가 도구를 정확히 이해하고 사용해야 한다는 점을 배웠다.
Spring으로 마이그레이션하면서 가장 크게 배운 점은 다음과 같다.
FastAPI와 Spring은 각각 장단점이 있으며, 특정 기술을 선택할 때는 그 기술이 필요한 이유를 정확히 이해하는 것이 중요하다. FastAPI는 빠른 개발과 간결한 구조가 강점이었지만, 복잡한 비즈니스 로직을 다루고 대규모 트래픽을 처리하는 데는 Spring이 더 적합했다.