컬리 핀테크그룹이 MSA 환경에서 GraphQL 기반 BFF(Backend For Frontend)를 도입·운영하며 얻은 인사이트다. 여러 마이크로서비스를 단일 GraphQL 스키마로 통합하고 Apollo Client 정규화 캐싱의 실전 버그까지 다룬다.
핵심 포인트- 엔드포인트 분산·플랫폼별 중복 구현·CORS 문제를 BFF가 프론트 전용 계층으로 흡수하며, 백엔드 스펙이 바뀌어도 BFF 매핑으로 프론트는 그대로 동작한다.
- 스택은 NestJS + Fastify + Apollo Server(여러 MSA를 단일 스키마로 통합)이고 클라이언트는 Apollo Client + graphql-codegen으로 훅·타입을 자동 생성한다.
- Apollo Client는 응답을 __typename:id로 정규화 캐싱하는데, 같은 id·다른 idType/kycSeq 아이템이 한 엔트리로 병합되던 버그가 났다.
- typePolicies의 keyFields를 id·idType·kycSeq 조합으로 지정해 고유 식별로 분리 해결했다.
- BFF는 실패 지점·운영 포인트를 늘리므로 MSA가 늘고 스펙 변경이 잦을 때 도입하고 소규모·단일 플랫폼(Next.js Server Component)엔 불필요하다.
상세 정리- BFF 배경: 서비스마다 엔드포인트가 분산되고, 여러 플랫폼과 API 스펙을 조율하는 비용, 플랫폼별 인증·정책 중복, 웹 CORS 제약이 있었다.
- 스택 근거: NestJS(모듈·데코레이터로 유지보수), GraphQL(한 쿼리로 필요한 데이터만·스키마 매핑으로 백엔드 변경 격리), Fastify(짧고 잦은 요청에 적합), Apollo Server(단일 스키마 통합), Apollo Client(InMemoryCache 정규화 + graphql-codegen 타입 생성).
- 도입 효과: GraphQL + TS 정적 타입 보장, 백엔드 의존성 감소, 여러 MSA 집계를 BFF에서 처리, 인증·인가·로깅·CORS를 BFF 단일 진입점에 일원화했다.
- 코드 자동 생성: query 작성 시 graphql-codegen이 useXxxQuery·LazyQuery·SuspenseQuery 훅과 타입을 생성해, REST의 useState/useEffect/useCallback 수동 조합 대비 간결하다.
- 정규화 캐싱: 응답에 __typename·id가 있으면 __typename:id로 개별 엔티티를 저장하고 동일 데이터 참조처가 하나의 캐시 엔트리를 공유한다.
- 캐싱 버그: RegistrationMember가 같은 id에 다른 idType·kycSeq를 가질 수 있어 한 엔트리로 병합돼 서로 다른 아이템이 동일 데이터로 표시됐다.
- 해결: InMemoryCache typePolicies에서 RegistrationMember의 keyFields를 id·idType·kycSeq 조합으로 지정해 별도 엔트리로 인식하게 했다.
- 도입 부작용: 레이어 추가로 BFF 장애 시 프론트 전체가 차단될 수 있고, 별도 코드베이스·배포·테스트로 운영·디버깅 범위가 늘어난다.
- 도입 시점: 도메인이 복잡해지고 MSA가 늘며 백엔드 스펙 변경이 잦아 프론트가 자주 영향받고 소통 비용이 급증할 때다.
- 불필요한 경우: Next.js 13+ Server Component/Action으로 소규모·단일 플랫폼·프로토타입은 BFF 없이 복잡도를 낮출 수 있다.
- 추가 통찰: 클라이언트 캐싱은 체감 속도(SWR로 캐시 먼저 표시 후 갱신), 백엔드 캐싱은 일관성·부하·레이턴시로 같은 개념이 아니다.
왜 읽나GraphQL BFF와 Apollo Client를 쓰는 프론트엔드 개발자에게 정규화 캐싱 keyFields 함정과 BFF 도입 판단 기준 레퍼런스.