📚 전체 글

총 62편

📚 교육용 풀스택 SaaS 개발기

Mock에선 되던 게 REST에선 안 됐다 — 응답 포맷 한 칸 차이가 만든 하루

VITE_USE_MOCK_API=false로 토글한 순간 useTable이 멈췄다. BE는 TransformInterceptor로 모든 응답을 `{ success, data, meta }`로 표준화했고, Refine은 `{ data, total }`을 기대했다. dataProvider 어댑터 한 줄로 메우면 끝나는 줄 알았는데 — 경로가 `/api/v1/api/v1/...`로 중복되고, FE가 `order`로 보낸 정렬 키를 BE는 `sortOrder`로 받았으며, 중첩 리소스 `/contents/:id/problems`는 dataProvider 기본 구현이 못 그렸다. 결국 어댑터 한 줄이 아니라 경로 정규화·필드 매핑·중첩 리소스 라우터·401 인터셉터까지 네 자리를 박아야 토글이 진짜 한 줄이 됐다.

RefineNestJSDataProvider
📚 교육용 풀스택 SaaS 개발기

1인 다역으로 5일 만에 90% — Admin Portal MVP를 끌어올린 토글 한 줄

SC-A01부터 SC-A20까지 20개 시나리오를 5일 만에 75%에서 90%까지 끌어올린 Admin Portal MVP 사이클. 혼자 PM·BE·FE 세 역할을 돌리면서 만든 병렬화의 환상은 사실 코드 한 줄 — `VITE_USE_MOCK_API=true` 환경변수 토글과 그 뒤에 붙은 Mock/REST DataProvider 두 구현체에서 시작됐다. Refine + Vite 위에서 14개 page 모듈을 어떤 순서로 박았는지, [FE → PM] 태그로 셀프 보고하던 협업 프로토콜이 왜 효과가 있었는지, Mock에서 REST로 갈아끼울 때 실제로 바뀐 건 한 줄이었는지 — 다섯 H2로 정리한다.

RefineViteNestJS
📚 교육용 풀스택 SaaS 개발기

갈아엎고 80일 — v2.0 마이그레이션 8편 메타 회고

devlog-13부터 devlog-20까지, v2.0 갈아엎기 5편과 v2.1로 끌어올리기 2편, v3.0 한 단계 더 1편을 한 발 떨어져서 다시 읽는다. Block에서 Bundle로의 전환, 5-Phase로 쪼갠 마이그레이션, 같은 날 v2.1로 SSoT를 끌어올린 결정, 1,682줄을 한 커밋에 박은 도메인 레이어, 그리고 그 위에 얇은 막을 입힌 Application Service까지. Before/After가 정말로 달라진 다섯 축(코드 책임, 문서 트랙, 멘탈, 도메인 명확성, 테스트 가능성)과 80일이 지나도 안 달라진 한 축(혼자 결정하고 혼자 박는 운영 모드)을 코드 위에서 정리한다.

DDD마이그레이션NestJS
📚 교육용 풀스택 SaaS 개발기

v3.0 Application Layer 재작성 — 도메인 서비스 위에 얇은 막을 한 Phase에 박은 날

도메인 서비스를 박은 다음 그 위에 Application Service 4종 + Controller 4종 + DTO 4종을 한 번에 박은 결정 이야기. trackState, secondLevel, track1/2Completed, curriculumProgress 같은 v3.0 필드가 Application 경계에서 어떻게 흡수되는지, 그리고 그 결과로 터진 빌드 에러 51개를 다섯 카테고리로 잡아간 새벽의 디버깅 흐름까지. 도메인이 비즈니스 규칙을 가지고, Application은 그것을 호출하는 얇은 오케스트레이션 막이 된다는 결합 규약을 코드 위에서 다시 한 번 확인한 한 Phase의 기록.

DDDApplication LayerNestJS
📚 교육용 풀스택 SaaS 개발기

v2.1 Domain Layer — 도메인 서비스 1,682줄을 한 커밋에 박은 날의 설계 철학

전날 4,658줄짜리 DDD 문서를 박은 다음 날 저녁, 그 문서를 입력으로 받아 도메인 레이어 코드를 다시 박았다. 한 커밋에 1,682줄 추가. MetricRank TOP1~5와 V21_THRESHOLDS를 도메인 타입으로 박은 결정, ContentCandidateService의 후보 폴백 설계, 90% 이상도 복습 필수로 바꾼 ReviewModuleService 리팩토링, BundleCompleted 이벤트로 Aggregate 간 결합을 끊은 결정 — 다섯 개의 도메인 서비스가 같은 커밋에 들어간 이유와 각각의 설계 철학.

DDDDomain LayerNestJS
📚 교육용 풀스택 SaaS 개발기

코드를 박은 다음 날 — 4,658줄 DDD 문서를 24분 사이에 다시 쓴 하루

v2.0 코드 마이그레이션이 끝난 직후, 같은 날 밤 24분 사이에 두 개의 docs 커밋이 들어갔다. v2.0 DDD 문서들은 _archived/v2.0/으로 묻혔고, 마스터 문서가 v2.1로 진화하면서 1,354줄이 갈아 끼워졌고, 그 위에 새 DDD 문서 4,658줄이 새로 적혔다. 코드와 문서를 다른 트랙으로 두지 않기 위해 무엇을 묶었고 무엇을 일부러 묻었는지의 기록.

DDDSSoT문서화
📚 교육용 풀스택 SaaS 개발기

Phase 3-3·3-4·3-5 — Application부터 Module까지, v2.0 마이그레이션 닫는 날

Phase 3-1·3-2가 만든 Repository와 Domain Service 위에 Application(953줄), Controller+DTO(763줄), Module(39줄)을 차례로 얹어 v2.0 번들 기반 학습 시스템을 닫는 단계. UC-06~10과 UC-14~17을 어떻게 코드로 옮겼는지, 인메모리 챌린지 스토어를 왜 일부러 남겼는지, 39줄짜리 모듈 한 장이 왜 끝의 끝인지를 기록한다.

NestJSApplication ServiceController
📚 교육용 풀스택 SaaS 개발기

Phase 3-1·3-2 — Repository와 Domain 서비스로 36개 빌드 에러 잡기

Phase 2가 남긴 36개 빌드 에러를 봉합하는 단계. BundleRepository 인터페이스 204줄과 Prisma 구현체 294줄, BundleGenerationService 417줄을 어떻게 분리해 작성했는지, 5콘텐츠 고정 구조와 4단계 폴백 전략을 코드로 어떻게 옮겼는지의 기록. 인터페이스 분리가 멘탈에 어떤 보호막을 쳐줬는지 포함.

NestJSPrismaDDD
📚 교육용 풀스택 SaaS 개발기

Phase 2 스키마 마이그레이션 — 데이터 안 날리고 구조 바꾸기

v2.0 Phase 2는 self-reference를 배열로 바꾸고, enum 두 개를 추가하고, NOT NULL을 nullable로 푸는 스키마 대수술이었다. Prisma migrate가 자동 생성한 SQL의 'data will be lost' 경고 4개를 어떻게 무력화했는지, 그리고 마이그레이션 직후 36개 빌드 에러가 났는데도 왜 멘탈이 멀쩡했는지의 기록.

NestJSPrismaPostgreSQL
📚 블로그 SEO 실험실

Google E-E-A-T를 개인 블로그에 실제로 적용하는 방법 — 공식 문서 기반 실전 가이드

Google E-E-A-T(경험, 전문성, 권위, 신뢰)를 개인 기술 블로그에 실제로 적용한 과정을 정리합니다. 품질 평가 가이드라인을 읽고, 환경 명시 박스·레퍼런스 박스·구조화 데이터·시리즈 구조를 도입하기까지 — 공식 문서에서 근거를 찾고 하나씩 적용한 기록입니다.

SEOE-E-A-TGoogle
📚 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