#트러블슈팅 (31)

📚 NestJS + Refine 풀스택 트러블슈팅

외부 뷰어 리포트 4탭 N+1 — 14초 응답을 2초로

외부 뷰어 리포트 토큰 한 줄로 들어가는 공개 페이지의 4탭 단일 응답이 최대 14.4초까지 늘어났다. Prisma `include` 만 의심하다 진짜 범인을 놓쳤다. 두 층의 N+1 — 4탭 빌더 6 건의 순차 await + 일별·주별 for 루프 안 `findMany` 반복 — 을 Promise.all 병렬화 + 단일 findMany + 메모리 그룹핑으로 풀어 55 개 쿼리를 10 개로, p95 응답을 ~2.3 초로 끌어내린 트러블슈팅을 정리한다.

NestJSPrisma성능최적화
📚 NestJS + Refine 풀스택 트러블슈팅

Framer Motion whileInView — 일부 카드만 안 뜨던 날

외부 뷰어 리포트의 4탭에 인사이트 카드와 빈 데이터 폴백을 같이 깔고 나니 일부 motion.section의 whileInView 애니메이션이 트리거되지 않았다. 같은 컴포넌트가 같은 코드로 어떤 탭에서는 정상이고 어떤 탭에서는 opacity 0으로 그대로 멈췄다. 원인은 조건부 마운트 시점에 IntersectionObserver의 첫 콜백이 framer-motion 이펙트 부착 전에 끝나 버린 레이스 + viewport.once 기본값 false의 합이었다. once:true 표준화 + sentinel ref + ESLint 가드를 같이 깐 트러블슈팅을 정리한다.

Framer MotionReactwhileInView
📚 NestJS + Refine 풀스택 트러블슈팅

NestJS 권한 가드 — 목록은 막고 상세는 뚫린 날

운영자가 본인 담당 클래스 1개만 떠야 하는데 모든 클래스가 떴다. 목록 API에 operatorId 필터를 깔고 끝낸 줄 알았는데, 직접 URL로 미담당 클래스 ID를 두드리니 상세·수정·승인 5개 엔드포인트가 그대로 200을 돌려줬다. 원인은 JWT payload.sub(User ID)와 Operator 테이블 id(Operator ID)의 분리 + validateClassAccess 헬퍼 부재 둘이었다. 라운드 한 번에 BE → QA → 추가 BE → QA 재검증으로 닫은 NestJS ForbiddenException + Prisma classOperator.findFirst 패턴을 정리한다.

NestJSPrismaJWT
📚 NestJS + Refine 풀스택 트러블슈팅

타이머가 NaN:NaN으로 떴다 — Bundle API 응답 누락 필드와 비어 있는 콘텐츠 후보

QA Round 2에서 EC-3 엣지 케이스가 잡은 타이머 NaN:NaN 버그. 같은 컴포넌트가 같은 코드로 한 화면에서 정상, 다른 화면에서 NaN:NaN으로 뜨던 패턴이다. 원인은 BE 응답 DTO에서 한 필드 누락 + FE 무방어 + seed 데이터 부족 세 가지의 합이었다. 응답 스키마 검증과 콘텐츠 후보 0건 방어를 같이 깐 라운드 회고.

ReactTypeScriptNestJS
📚 NestJS + Refine 풀스택 트러블슈팅

킥오프 배치 첫 구현 — 매시 전체 EXPIRED 사고와 Winston 도입

첫 배치 작업으로 짠 NestJS 킥오프 배치(@nestjs/schedule)가 @Cron('EVERY_HOUR') + findExpiredAssignments() 시간 체크 누락 두 함정에 걸려 모든 ACTIVE 숙제를 매시간 EXPIRED로 굳혀 버린 사고를 복기한다. fix는 두 줄(시간 비교 + cron 표현식)이었지만, 사고가 일어났는데도 콘솔이 흘러가 흔적이 없었던 점이 더 컸다. 콘솔 일변도에서 Winston 파일 로깅(daily-rotate, app/error 분리, GCP severity) 으로 갈아탄 결정과 설정 전문을 정리한다.

NestJSCronBatch
📚 NestJS + Refine 풀스택 트러블슈팅

JWT Guard 적용 — request.user undefined부터 jwt malformed까지

활동 로그 인터셉터에서 request.user가 undefined로 잡혔다. 원인을 파고드니 JwtAuthGuard 구현 누락, Auth Service와 Guard 간 JWT_SECRET 불일치, FE의 base64(JSON) Mock 토큰까지 3중 함정이 차례로 드러났다. 새벽 3시 임시 우회부터 다음 날 17시 정상화까지의 디버깅 흐름과 NestJS 글로벌 Guard·명시적 secret 검증·실제 BE API 시드 패턴을 정리한다.

NestJSJWTGuard
📚 NestJS + Refine 풀스택 트러블슈팅

Prisma 그래프 스키마 — 선형 레벨을 DAG로 옮긴 4가지 결정

단일 정수 sortOrder로 줄세운 선형 레벨을 노드/엣지 분리 DAG로 옮겼다. 셀프 참조 1:N vs 별도 엣지 테이블, PostgreSQL recursive CTE로 진행도 계산, 엣지 INSERT 시점 사이클 검출, 선형 i→i+1 자동 마이그레이션 4가지 결정과 zod invariants e2e로 회귀를 차단한 트러블슈팅.

PrismaPostgreSQLDAG
📚 1인 인프라 구축기

Claude Max 플랜으로 API 호출하면 429가 뜨는 이유 — 인증 체계 5단계 완전 정리

Claude Max 플랜의 OAuth 토큰(sk-ant-oat)으로 Messages API를 직접 호출하면 429 Rate Limit이 뜹니다. Claude Code의 인증 우선순위 5단계, sk-ant-oat vs sk-ant-api 차이, 그리고 스크립트에서 Max 플랜을 활용하는 우회법을 실제 트러블슈팅 사례와 함께 정리합니다.

ClaudeClaude CodeAnthropic API
📚 NestJS 실전 트러블슈팅

prisma generate 누락 — 빌드는 되는데 런타임 에러가 나는 이유

NestJS + Prisma 프로젝트에서 schema.prisma에 새 필드를 추가한 뒤 prisma generate를 빠뜨리면, TypeScript 빌드는 as any 캐스팅 덕에 통과하지만 런타임에서 Prisma Client가 새 필드를 모른다. pnpm build와 prisma generate가 별개 명령인 구조적 함정, prebuild 훅으로 자동화하는 해결법, Docker와 CI에서 놓치지 않는 예방 전략을 실전 코드와 함께 정리한다.

NestJSPrismaprisma generate
📚 React 프론트엔드 삽질기

Refine useCustom config.query가 정수를 보장하지 않는 함정 — 타입은 number인데 왜 400이야?

Refine의 useCustom hook에서 config.query 객체에 number 타입 값을 전달해도, URL 쿼리 파라미터로 직렬화되면서 문자열이 된다. NestJS @Type(() => Number) 검증과 조합하면 targetClassId must be an integer number 400 에러가 터진다. URL 직접 삽입 패턴으로 해결한 실전 사례를 정리한다.

ReactRefineuseCustom
📚 NestJS 실전 트러블슈팅

Soft Delete 필터가 빠진 곳 찾기 — 삭제한 데이터가 되살아나는 미스터리

NestJS + Prisma 프로젝트에서 Soft Delete 필터를 Application Service에만 적용하고 Domain Repository를 누락하면, 삭제된 데이터가 배치 프로세스와 조회 API에서 좀비처럼 되살아난다. PM 코드 리뷰에서 11곳 추가 발견된 실전 사례를 통해, 레이어별 필터 점검 체크리스트와 grep 기반 검증법을 정리한다.

NestJSPrismaSoft Delete
📚 React 프론트엔드 삽질기

Framer Motion whileInView 애니메이션이 스크린샷에서 사라지는 이유와 해결법

Framer Motion whileInView로 만든 스크롤 애니메이션이 Chrome DevTools MCP 스크린샷이나 OG 이미지 생성에서 보이지 않는 원인은 IntersectionObserver의 뷰포트 의존성이다. initial hidden 상태가 캡처되는 근본 원인과 3가지 해결 전략을 실전 코드로 정리한다.

Framer MotionReactwhileInView