summaryrefslogtreecommitdiff
path: root/lib/approval/README_CACHE.md
diff options
context:
space:
mode:
Diffstat (limited to 'lib/approval/README_CACHE.md')
-rw-r--r--lib/approval/README_CACHE.md253
1 files changed, 253 insertions, 0 deletions
diff --git a/lib/approval/README_CACHE.md b/lib/approval/README_CACHE.md
new file mode 100644
index 00000000..b7bee00f
--- /dev/null
+++ b/lib/approval/README_CACHE.md
@@ -0,0 +1,253 @@
+# 결재 시스템 캐시 무효화 가이드
+
+## 문제 상황
+
+Next.js는 서버 컴포넌트의 데이터 fetch 결과를 자동으로 캐시합니다. 결재 시스템에서 `withApproval()`로 결재를 상신하거나 폴링 서비스가 결재를 승인/반려 처리해도 캐시가 무효화되지 않아 페이지에 최신 데이터가 표시되지 않는 문제가 있었습니다.
+
+특히 **백그라운드 프로세스** (폴링 서비스)에서는 Next.js의 `revalidateTag`를 직접 사용할 수 없습니다. `revalidateTag`는 request 컨텍스트가 필요한데, 백그라운드에서는 이 컨텍스트가 존재하지 않기 때문입니다.
+
+## 해결 방법
+
+API 라우트를 통한 캐시 무효화 시스템을 구축했습니다.
+
+### 1. 캐시 태그 시스템
+
+결재 관련 데이터에 캐시 태그를 추가:
+
+```typescript
+// lib/approval-log/service.ts
+export async function getApprovalLogList(input: ListInput) {
+ return unstable_cache(
+ async () => {
+ // ... 데이터 조회 로직
+ },
+ [cacheKey],
+ {
+ tags: ['approval-logs'], // 🏷️ 캐시 태그
+ revalidate: 60, // 60초마다 자동 재검증 (폴백)
+ }
+ )();
+}
+```
+
+### 2. 캐시 무효화 API
+
+백그라운드에서도 사용 가능한 API 라우트:
+
+```typescript
+// app/api/revalidate/approval/route.ts
+export async function POST(request: NextRequest) {
+ const { tags } = await request.json();
+
+ // 캐시 태그 무효화
+ for (const tag of tags) {
+ revalidateTag(tag);
+ }
+
+ return NextResponse.json({ success: true });
+}
+```
+
+### 3. 캐시 무효화 헬퍼 함수
+
+편리하게 사용할 수 있는 유틸리티:
+
+```typescript
+// lib/approval/cache-utils.ts
+
+// 결재 로그 캐시 무효화
+export async function revalidateApprovalLogs() {
+ await fetch('/api/revalidate/approval', {
+ method: 'POST',
+ body: JSON.stringify({ tags: ['approval-logs'] })
+ });
+}
+
+// 모든 결재 관련 캐시 무효화
+export async function revalidateAllApprovalCaches() {
+ await fetch('/api/revalidate/approval', {
+ method: 'POST',
+ body: JSON.stringify({
+ tags: ['approval-logs', 'pending-actions', 'approval-templates']
+ })
+ });
+}
+```
+
+### 4. 워크플로우에 통합
+
+결재 상신/승인/반려 시 자동으로 캐시 무효화:
+
+```typescript
+// lib/approval/approval-workflow.ts
+
+export async function withApproval(...) {
+ // ... 결재 상신 로직
+
+ // 캐시 무효화
+ await revalidateApprovalLogs();
+
+ return result;
+}
+
+export async function executeApprovedAction(apInfId: string) {
+ // ... 액션 실행 로직
+
+ // 캐시 무효화 (백그라운드에서도 동작! ✨)
+ await revalidateApprovalLogs();
+
+ return result;
+}
+
+export async function handleRejectedAction(apInfId: string, reason?: string) {
+ // ... 반려 처리 로직
+
+ // 캐시 무효화
+ await revalidateApprovalLogs();
+}
+```
+
+## 동작 원리
+
+```mermaid
+sequenceDiagram
+ participant BG as 백그라운드 프로세스<br/>(폴링 서비스)
+ participant API as API 라우트<br/>/api/revalidate/approval
+ participant Cache as Next.js 캐시
+ participant Page as 결재 로그 페이지
+
+ BG->>BG: 결재 승인 처리
+ BG->>API: POST /api/revalidate/approval<br/>{ tags: ['approval-logs'] }
+ API->>Cache: revalidateTag('approval-logs')
+ Cache-->>Cache: 캐시 무효화 ✅
+
+ Note over Page: 사용자가 페이지 접속
+ Page->>Cache: 데이터 요청
+ Cache->>Page: 최신 데이터 반환 🎉
+```
+
+## 사용 예시
+
+### 예시 1: 새로운 결재 액션에 캐시 무효화 추가
+
+```typescript
+// lib/my-feature/approval-actions.ts
+'use server';
+
+import { withApproval } from '@/lib/approval';
+
+export async function myActionWithApproval(data: MyData) {
+ // withApproval이 자동으로 캐시 무효화를 처리합니다
+ return await withApproval('my_action', data, {
+ templateName: '나의 액션 결재',
+ variables: { ... },
+ currentUser: { ... }
+ });
+}
+```
+
+### 예시 2: 수동으로 캐시 무효화
+
+```typescript
+import { revalidateApprovalLogs } from '@/lib/approval';
+
+// 필요한 시점에 수동으로 호출
+await revalidateApprovalLogs();
+```
+
+### 예시 3: 여러 캐시 동시 무효화
+
+```typescript
+import { revalidateApprovalCache } from '@/lib/approval';
+
+await revalidateApprovalCache([
+ 'approval-logs',
+ 'pending-actions',
+ 'my-custom-cache'
+]);
+```
+
+## 캐시 태그 규칙
+
+| 태그 이름 | 적용 대상 | 무효화 시점 |
+|----------|---------|-----------|
+| `approval-logs` | 결재 로그 목록 | 결재 상신/승인/반려 시 |
+| `pending-actions` | 대기 중인 액션 목록 | 액션 실행/반려 시 |
+| `approval-log-${apInfId}` | 특정 결재 상세 | 해당 결재 상태 변경 시 |
+| `approval-templates` | 결재 템플릿 목록 | 템플릿 생성/수정/삭제 시 |
+
+## 보안 (선택사항)
+
+환경 변수로 시크릿 키를 설정하여 무단 접근 방지:
+
+```env
+# .env.local
+REVALIDATION_SECRET=your-secret-key-here
+```
+
+```typescript
+// lib/approval/cache-utils.ts
+await fetch('/api/revalidate/approval', {
+ method: 'POST',
+ body: JSON.stringify({
+ tags: ['approval-logs'],
+ secret: process.env.REVALIDATION_SECRET
+ })
+});
+```
+
+## 장점
+
+✅ **백그라운드 프로세스 지원**: 폴링 서비스에서도 캐시 무효화 가능
+✅ **공통 솔루션**: 모든 결재 관련 페이지에 자동 적용
+✅ **유연성**: 필요한 캐시만 선택적으로 무효화
+✅ **신뢰성**: API 호출 실패해도 60초 후 자동 재검증 (폴백)
+✅ **확장성**: 새로운 캐시 태그 추가 용이
+
+## 주의사항
+
+⚠️ **과도한 무효화 방지**: 너무 자주 캐시를 무효화하면 성능 저하 발생
+⚠️ **에러 처리**: 캐시 무효화 실패는 치명적이지 않으므로 로그만 남기고 진행
+⚠️ **개발 환경**: 개발 환경에서는 캐싱이 비활성화될 수 있음
+
+## 트러블슈팅
+
+### 문제: 캐시가 무효화되지 않음
+
+1. API 라우트가 올바르게 호출되는지 확인:
+ ```bash
+ # 로그 확인
+ [Approval Workflow] Revalidating cache after approval submission
+ [Cache Revalidation] Tag revalidated: approval-logs
+ ```
+
+2. 캐시 태그가 올바르게 설정되었는지 확인:
+ ```typescript
+ // getApprovalLogList에 tags: ['approval-logs'] 있는지 확인
+ ```
+
+3. 환경 변수 확인:
+ ```bash
+ # NEXT_PUBLIC_BASE_URL이 올바르게 설정되어 있는지
+ ```
+
+### 문제: API 호출이 실패함
+
+```typescript
+// cache-utils.ts에서 에러 로그 확인
+[Approval Cache] Failed to revalidate cache: Error: ...
+```
+
+원인:
+- 네트워크 이슈
+- 잘못된 BASE_URL
+- 시크릿 키 불일치
+
+해결: 로그를 확인하고 환경 변수를 점검하세요.
+
+## 참고 자료
+
+- [Next.js Caching Documentation](https://nextjs.org/docs/app/building-your-application/caching)
+- [revalidateTag API Reference](https://nextjs.org/docs/app/api-reference/functions/revalidateTag)
+- [unstable_cache API Reference](https://nextjs.org/docs/app/api-reference/functions/unstable_cache)
+