프론트엔드
BO에서 글을 수정해도 Web이 안 바뀌는 이유: 모노레포 환경 캐시 전략
BO에서 글 수정 시 Web에 미반영 문제 관리자 페이지(BO)에서 게시글을 수정하면 DB 및 BO에는 즉시 반영된다. 문제는 수정 직후 Web에서 해당 글을 확인하면, 내용이 이전 버전으로 계속 노출되었다. 관리자에서 수정이 완료되었는데 Web에는 반영되지 않는 상황이기 때문에, 운영 흐름상 수정 직후 화면에 변경 사항이 보이지 않는 것은 문제가 될 수밖에 없다. Web은 Next.js App Router + fetch 기반 SSR 구조로 구성되어 있었고, App Router의 fetch는 서버 레벨 캐싱을 기본으로 동작한다. 이는 일반적인 클라이언트 캐싱 라이브러리(예: React Query)와는 동작 방식이 다르다. App Router 환경에서 fetch는 기본적으로 force-cache로 동작한다. 단순 새로고침은 서버 재요청을 의미하지 않는다. 데이터는 빌드 시점 혹은 ISR 주기에 따라 캐시된다. 명시적으로 무효화하지 않는 한 캐시는 유지된다. 따라서 DB가 갱신되더라도, Web 서버의 캐시가 무효화되지 않으면 화면에는 반영되지 않는다. Web과 BO가 분리된 모노레포 구조 프로젝트는 pnpm 기반 모노레포로 구성되어 있다. 구조적으로 다음과 같은 특성을 가진다. web: SSR 기반 콘텐츠 렌더링 bo: CRUD 중심 관리자 기능 api: 데이터 처리 백엔드 이 세 애플리케이션은 같은 레포에 존재하지만, 런타임은 완전히 독립적으로 동작한다. 즉, 모노레포는 코드 레벨의 공유를 의미할 뿐, 런타임 공유를 의미하지 않는다. BO에서 게시글을 수정하면 DB는 갱신 되지만, Web 서버 내부 캐시는 별도의 프로세스로 유지된다. 따라서 BO에서 직접 revalidate를 호출하더라도 Web 서버의 캐시는 무효화되지 않는다. 설계한 캐시 무효화 전략 ① Web에 invalidation 전용 API route 생성 Web 서버 내부에서만 실행되는 캐시 무효화 엔드포인트를 별도로 구성하였다. 캐시 무효화는 반드시 Web 서버 내부에서 실행되도록 설계하였다. ② BO → API → Web Invalidation API 호출 게시글 생성/수정/삭제 성공 이후 BO가 호출한 백엔드 api는 Web의 invalidation API를 호출한다. 이를 통해 캐시 무효화 요청이 Web 서버로 전달된다. ③ Web 서버 내부에서 revalidateTag 실행 Web 서버 내부에서 revalidateTag를 호출하여 도메인 단위 캐시를 선택적응로 무효화한다. 게시글 상세는 도메인 단위 태그 + ID 단위 태그를 함께 사용했다. 전체 흐름은 다음과 같다. contracts 패키지에 revalidate 전략 포함하기 이 과정에서 contracts 패키지를 단순 타입 공유 계층으로 사용하지 않았다. 캐시 무효화 전략을 API 계약의 일부로 정의하였다. API contract는 다음과 같은 요소를 포함하였고, 캐시 전략을 위해서 여기에 revalidate 전략을 포함시켰다. HTTP Method Path Request Params / Body Response 타입 revalidate 즉, 특정 API가 호출되었을 때 어떤 캐시 태그가 무효화되어야 하는지까지 명시했다. 예시: UpdatePost Contract 설계 의도 이를 통해 다음과 같은 효과를 얻었다. BO와 Web이 동일한 무효화 규칙을 공유 도메인 단위 무효화 정책의 재사용 가능 캐시 전략을 명시적 설계 요소로 승격 결과적으로 contracts는 단순한 타입 패키지가 아니라 애플리케이션 간 계약을 정의하는 계층으로 확장되었다. 회고 이전에도 모노레포 구조를 구성해본 적은 있었지만, 두 개 이상의 애플리케이션을 동시에 개발하고 실제 데이터 흐름과 런타임 경계를 고려하며 설계한 것은 처음이었다. 특히 web, bo, api 세 애플리케이션과 contracts, ui 패키지가 유기적으로 연결된 구조에서 타입 공유만으로는 해결되지 않는 문제들이 드러났다. 같은 레포에 있다는 사실이 곧 실행 환경까지 공유한다는 의미는 아니었다. 런타임은 명확히 분리되어 있었고, 이번 캐시 이슈는 그 차이를 분명하게 보여준 사례였다. 또한 콘텐츠 운영 환경에서는 단순히 CRUD를 구현하는 것만으로는 충분하지 않다는 점을 알게 되었다. 데이터를 저장하는 것과 사용자 화면에 반영되는 것 사이에는 여러 계층이 존재한다. 이번 경험은 기능 구현을 넘어서 운영을 고려한 설계에 대해 고민하게 된 계기였다.
2026년 02월 19일 16:20
