1. 도입 배경
프론트엔드 개발에서 API 연동과 타입 작성은 빈번하고 반복적인 작업입니다. 새로운 API가 추가되거나 요청 파라미터, 응답 구조가 변경될 때마다 타입 정의를 수정하고, API 호출 함수를 작성하고, TanStack Query 훅까지 구성해야 합니다. API 수가 적을 때는 직접 작성해도 큰 문제가 없지만, 프로젝트 규모가 커질수록 수정해야 하는 파일이 많아지고, 이 과정에서 필드명 오기입이나 타입 불일치 같은 휴먼 에러가 발생하기 쉽습니다.
특히 백엔드와 프론트엔드가 병렬로 개발되는 환경에서는 Swagger 문서가 계속 변경될 수 있습니다. 이때 프론트엔드에서 타입과 호출 함수를 수동으로 관리하면 실제 API 명세와 코드가 달라지는 문제가 생기기 쉽습니다. 예를 들어 응답 필드명이 바뀌었거나 optional 여부가 변경되었는데, 일부 파일만 수정되는 경우 런타임 오류나 TypeScript 에러로 이어질 수 있습니다.
이러한 문제를 줄이기 위해 실제 참여했던 프로젝트에서는 Swagger 문서를 기반으로 API 코드를 자동 생성하는 Orval을 도입했습니다. Orval은 OpenAPI 또는 Swagger 스펙을 기준으로 TypeScript 타입과 API 호출 함수를 생성할 수 있고, 설정에 따라 TanStack Query 기반 훅까지 함께 생성할 수 있는 도구입니다.
도입의 목적은 단순히 코드를 빠르게 생성하는 것이 아니라, API 명세와 프론트엔드 코드 사이의 불일치를 줄이는 것이었습니다. 반복적인 보일러플레이트 코드를 자동화하고, 개발자는 화면 로직, 상태 관리, 예외 처리, 사용자 경험 개선에 더 집중할 수 있도록 하는 것이 주요 목적이었습니다.
또한 모노레포 환경에서는 여러 앱이나 패키지가 공통 API 타입과 호출 로직을 공유하는 경우가 많습니다. Orval을 사용하면 Swagger 기준으로 공통 API 레이어를 생성하고 각 패키지에서 이를 활용할 수 있어, 중복 작성과 유지보수 부담을 줄일 수 있었습니다.
2. 실제 적용 시 발생했던 이슈
Orval을 적용하면서 실제로 겪었던 대표적인 문제는 모노레포 환경에서 TanStack Query 버전을 제대로 감지하지 못해 타입 오류가 발생한 사례였습니다.
프로젝트는 모노레포와 pnpm 조합으로 관리되고 있었습니다. 이 구조에서는 루트에 모든 의존성을 설치하기보다, 실제 사용하는 패키지에만 의존성을 선언하는 경우가 많습니다. 당시 TanStack Query도 실제 API 훅을 사용하는 패키지에만 설치되어 있었고, 모노레포 루트에는 존재하지 않았습니다.
문제는 Orval 실행 명령을 모노레포 루트에서 수행하고 있었다는 점입니다. Orval은 실행 위치를 기준으로 프로젝트 환경을 판단하는데, 루트에서는 실제 API 훅을 사용하는 패키지에 설치된 TanStack Query v5 버전을 정상적으로 감지하지 못했습니다. 그 결과 당시 생성된 코드는 v5가 아닌 v4 기준으로 생성되었고, 실제 프로젝트에서 사용하는 TanStack Query v5의 options 객체 타입과 충돌하면서 TypeScript 오류가 발생했습니다.
처음에는 단순히 생성된 코드의 타입 문제처럼 보였지만, 원인을 확인해보니 생성 코드 자체보다 Orval이 어떤 기준으로 버전을 판단했는지가 핵심이었습니다. 개발자 입장에서는 프로젝트에서 TanStack Query v5를 사용하고 있다고 생각하지만, 도구는 실행 위치인 루트에서 해당 의존성을 찾지 못한 것입니다. 즉, 실제 사용 환경과 도구가 판단하는 실행 환경이 달랐던 것입니다.
이 문제에서 한 가지 더 주목할 점은, 생성된 파일을 직접 수정하는 방식으로 해결하려 했다면 다음 Orval 실행 시 같은 문제가 반복되었을 가능성이 높다는 것입니다. 자동 생성 파일은 재생성될 때 덮어써지기 때문에, 생성 결과를 직접 수정하는 방식은 근본적인 해결책이 되기 어렵습니다.
3. 원인 분석과 해결 방안
이번 이슈의 원인은 Orval의 실행 위치와 실제 의존성 위치가 달랐다는 점, 그리고 TanStack Query v5 기준의 코드 생성을 명확히 통제하지 못했다는 점입니다. 자동화 도구는 편리하지만, 프로젝트 구조를 완전히 이해해서 동작한다고 볼 수는 없습니다. 특히 모노레포, pnpm workspace, 패키지별 의존성 관리처럼 구조가 복잡한 경우에는 기본 설정만으로 의도한 결과가 나오지 않을 수 있습니다.
문제가 발생했을 때 고려할 수 있었던 해결 방법은 세 가지가 있었습니다.
첫째, Orval 실행 위치를 실제 TanStack Query가 설치된 패키지 기준으로 변경하는 방법입니다. 실행 스크립트를 해당 패키지 디렉토리에서 수행하도록 조정하면 Orval이 올바른 의존성을 감지할 수 있습니다.
둘째, 루트에도 TanStack Query 의존성을 추가해 Orval이 버전을 감지할 수 있도록 하는 방법입니다. 다만 이 방식은 모노레포에서 실제 사용하지 않는 위치에 의존성을 추가하는 것이므로, 의존성 관리 원칙과 충돌할 수 있다는 점을 고려해야 합니다.
셋째, Orval 설정 파일에서 TanStack Query 훅 생성 기준을 명시하는 방법입니다.
이 중 실제로는 Orval 설정에서 TanStack Query v5 기준으로 생성되도록 명시하는 방식으로 해결했습니다. 이 방법은 의존성 구조를 크게 바꾸지 않으면서도 코드 생성 기준을 명확히 통제할 수 있다는 장점이 있었습니다. 또한 설정 파일에 의도가 남기 때문에, 이후 팀원이 같은 문제를 확인할 때도 원인을 파악하기 쉬웠습니다.
추가적으로, 자동 생성 코드와 실제 화면에서 사용하는 코드를 분리하는 것도 고려할 수 있습니다. Orval이 생성한 API 함수와 훅을 그대로 화면에서 직접 사용하기보다, 필요한 경우 custom hook이나 service layer에서 한 번 감싸는 방식을 적용하면 유지보수성이 좋아질 수 있습니다. 생성 코드는 API 스펙에 가까운 영역으로 두고, 화면에 필요한 데이터 가공, 기본 options 설정, error handling 등은 별도 레이어에서 관리하면 Orval이 재생성되더라도 화면 코드에 직접적인 영향을 줄일 수 있고, API 변경 대응과 화면 개발의 책임을 분리할 수 있기 때문입니다.
4. 도입 효과
Orval 도입의 가장 큰 효과는 반복 작업 감소였습니다. API가 추가될 때마다 타입을 수동으로 정의하고 API 호출 함수를 직접 작성하는 반복 작업을 줄일 수 있었습니다. Orval을 React Query 기반으로 설정하면 TanStack Query 훅까지 함께 생성할 수 있어, 기본적인 API 연동 코드 작성 부담을 크게 줄일 수 있었습니다.
두 번째 효과는 휴먼 에러 감소였습니다. 수동으로 타입을 작성하다 보면 필드명을 잘못 입력하거나, 응답 구조를 일부 누락하거나, optional 여부를 잘못 판단하는 경우가 생길 수 있습니다. Orval을 사용하면 Swagger 명세를 기준으로 타입이 생성되기 때문에, API 명세와 프론트엔드 타입 사이의 불일치를 줄일 수 있었습니다.
세 번째 효과는 API 변경 사항을 더 쉽게 추적할 수 있다는 점입니다. 자동 생성된 파일을 커밋하는 방식으로 운영하면, Swagger 변경으로 인해 어떤 타입이나 함수가 바뀌었는지 Git diff로 확인할 수 있습니다. 이를 통해 API path, request body, response type 등의 변경 사항을 코드 기준으로 검토할 수 있었습니다.
또한 API 명세 기반의 코드와 화면 로직을 분리할 수 있다는 점도 기대할 수 있는 장점입니다. 생성된 코드는 API 레이어로 두고, 실제 화면에서는 별도의 custom hook이나 service를 통해 사용하는 방식으로 구성하면 API 변경 대응과 화면 개발의 책임을 나눌 수 있습니다.
5. 운영 시 주의할 점
Orval을 안정적으로 사용하려면 몇 가지 기준이 필요합니다.
먼저 자동 생성 시점을 정해야 합니다. 빌드 시점에 자동으로 실행하면 최신 스펙을 항상 반영할 수 있지만, 의도하지 않은 API 변경이 갑자기 반영되어 빌드가 깨질 수 있습니다. 반대로 개발자가 수동으로 실행하고 생성 파일을 검토한 뒤 커밋하는 방식은 변경 내용을 확인할 수 있다는 장점이 있지만, 실행을 누락하면 최신 스펙과 코드가 달라질 수 있습니다. 현재 프로젝트의 상황과 팀 운영 방식에 맞게 선택해야 합니다.
또한 어느 Swagger 환경을 기준으로 생성할지도 정해야 합니다. 개발계, QA, 운영계의 Swagger 스펙이 다를 수 있기 때문에 팀 내 합의가 필요합니다. 개발계는 최신 변경이 빠르게 반영되지만 아직 확정되지 않은 API가 포함될 수 있고, 운영계는 안정적이지만 최신 개발 내용을 반영하지 못할 수 있습니다. 그렇지 않으면 의도하지 않은 스펙 변경이 반영되어 오히려 디버깅이 늘어날 수 있습니다.
생성 범위도 제어해야 합니다. Swagger 문서에 포함된 모든 API를 한 번에 생성하면 실제 프론트엔드에서 사용하지 않는 API까지 포함되어 코드가 불필요하게 커질 수 있습니다. 따라서 태그, 경로, 도메인 기준으로 필요한 API만 생성하는 방식이 좋습니다.
마지막으로, 자동 생성 파일은 직접 수정하지 않는 것이 좋습니다. 다음 생성 시점에 수정 내용이 덮어써질 수 있기 때문입니다. 필요한 커스텀 로직이 있다면 생성 파일을 수정하기보다 별도의 wrapper나 custom hook에서 처리하는 방식이 더 안전합니다.
6. 마무리
이번 Orval 도입 경험을 통해 자동화 도구는 반복 작업을 줄여주는 유용한 도구이지만, 프로젝트 구조와 실행 환경을 개발자가 명확히 이해하고 통제해야 안정적으로 사용할 수 있다는 점을 배웠습니다.
특히 모노레포와 pnpm 환경에서는 실행 위치, 의존성 선언 위치, 패키지 구조에 따라 도구의 동작 결과가 달라질 수 있습니다. 이번 이슈도 단순한 타입 오류처럼 보였지만, 실제로는 Orval이 TanStack Query 버전을 정상적으로 감지하지 못하면서 당시 v4 기준의 코드가 생성되었고, 실제 프로젝트에서 사용하는 v5 타입과 충돌한 것이 원인이었습니다.
결과적으로 Orval은 API 연동 작업을 자동화하고, Swagger 기반으로 타입과 API 호출 코드, 그리고 설정에 따라 TanStack Query 훅까지 일관되게 관리할 수 있게 해주는 유용한 도구였습니다. 다만 안정적으로 활용하기 위해서는 버전 명시, 환경 분리, 생성 범위 제어, 생성 파일 관리 원칙이 함께 필요합니다.
자동화 도구를 도입할 때는 단순히 도구의 장점만 보는 것이 아니라, 실제 프로젝트 구조 안에서 어떻게 실행되고 어떤 기준으로 결과물을 만드는지까지 함께 확인해야 한다고 생각합니다.