7.8 KiB
7.8 KiB
title, source_type, status, related_branches, related_projects, tags, created, status_label, target_audience, inspiration_url, archive_url
| title | source_type | status | related_branches | related_projects | tags | created | status_label | target_audience | inspiration_url | archive_url | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| blog-topic / checkoutable N+1 API replay | blog-topic | raw |
|
|
|
2026-07-15 | expanded | backend-engineer |
blog-topic: checkoutable N+1 API replay
Layer:
raw/blog-topics/— 테스트 코드에서만 보이던 N+1 관찰값을 checkout 가능한 Git stage, 로컬 HTTP API, PostgreSQL row 확인으로 바꾼 작업의 글감이다. 이 문서는 블로그 초안이나 canonical 문서가 아니다.
Parent / 부모
- raw/branch-notes/experiment-nplus1-feed-api-replay — D1의 11개 checkout checkpoint, D3의 Crown/L12 분리, D4의 실제 Docker PostgreSQL HTTP+DB smoke에서 나온 글감이다.
트리거 / Trigger
- 트리거 유형:
branch-work - 트리거 날짜: 2026-07-15
- 트리거 연결 노트: raw/branch-notes/experiment-nplus1-feed-api-replay
글감 / Topic seed
- 한 문장 요지: N+1 학습을 테스트 결과 읽기로 끝내지 않고, 각 Git tag를 checkout해 fixture reset → HTTP 응답 → PostgreSQL row를 직접 보는 11단계 실습으로 바꾼 과정을 기록한다.
- 예상 제목 후보:
- 테스트만으로는 보이지 않는 N+1: 11개 checkout point로 만든 API·DB 실습
- L1의 N+1부터 Crown까지: Git tag, curl, psql로 따라가는 JPA 조회 실험
- 쿼리 수 최적화와 read model을 같은 해법으로 말하지 않기
핵심 주장 후보 / Claim candidates
아직 canonical이 아니다. 각 사실 후보의 검증 범위는 아래 근거에 적힌 로컬 환경까지다.
- 사실 후보:
- 학습 경로는
L1 → L2 → L3 → L4 → L5 → L6 → L14 → L15 → L16 → Crown → L12순서의 11개 checkout 가능한 tag로 고정되었다. — 근거: raw/branch-notes/experiment-nplus1-feed-api-replay D1, §구현 가이드 / 고정 replay checkpoint - final L12 tag의 로컬 Docker Compose smoke에서 reset 100건, Crown feed의 prepared statement 1·entity load 0, L12 read model의 부모당 Top-3, marker row count 100이 관찰되었다. — 근거: raw/branch-notes/experiment-nplus1-feed-api-replay D3, D4, §검증 기록 / Docker HTTP + PostgreSQL smoke
- 별도 L1 historical smoke에서 lazy highlight 전략과
collectionFetches=10이 관찰되어, 마지막 상태만 보는 방식과 다른 출발점의 문제를 HTTP 응답으로 확인할 수 있었다. — 근거: raw/branch-notes/experiment-nplus1-feed-api-replay D1, §검증 기록 / Historical L1 smoke
- 학습 경로는
- 경험 후보:
- 학습자는 stage를 checkout한 뒤 lab fixture를 reset하고 API 응답을 본 다음
psqlmarker row를 확인하는 같은 루프를 반복할 수 있다. — 근거: raw/branch-notes/experiment-nplus1-feed-api-replay D2, D4, §목표 / WHY - Crown의 one-query endpoint와 L12의 same-store CQRS-lite two-query read port를 별도 경로로 두면, "쿼리 수 최소화"와 "application read-model 분리"를 한 결과로 오해하지 않게 된다. — 근거: raw/branch-notes/experiment-nplus1-feed-api-replay D3, §Crown과 L12의 의도적 차이
- 학습자는 stage를 checkout한 뒤 lab fixture를 reset하고 API 응답을 본 다음
- 의견/해석 후보:
- N+1 실습의 핵심 산출물은 최종 쿼리 하나가 아니라, 각 선택이 response·Hibernate 관찰값·DB 데이터에 어떻게 나타나는지 비교할 수 있는 반복 가능한 관찰 루프다.
Outline seed
- 왜 마지막 Crown 코드만으로는 학습이 어려웠는가 — 최종 상태는 출발점의 lazy collection N+1과 중간 선택지를 숨긴다는 점을 보여준다.
- 11개 tag를 실습 단위로 고정한 방법 — Git checkout을 문서 목차가 아니라 실행 가능한 실험의 시작점으로 사용한다.
- fixture reset, HTTP, psql의 관찰 루프 — 테스트 assertion 밖에서 response shape와 marker-owned row를 함께 확인하는 이유를 설명한다.
- L1에서 무엇을 보고 Crown에서 무엇이 달라지는가 — collection fetch 수와 one-query/zero-entity-load 관찰값을 같은 질문으로 비교한다.
- Crown과 L12를 분리해서 읽어야 하는 이유 — one native query 최적화와 same-store CQRS-lite projection은 해결하려는 문제가 다르다는 점을 정리한다.
- 재현 결과를 과장하지 않는 법 — 로컬 Docker 검증은 production latency, deployment profile, 다른 machine의 모든 tag 재현을 증명하지 않는다고 명시한다.
Canonical 전환 후보 / Canonical extraction candidates
wiki/blog/로 바로 가지 않는다. 먼저 아래 후보를 canonical로 정제한다.
wiki/projects/nplus1-presentation-prep/nplus1-feed-api-replay.md후보:- 11-stage replay catalog, lab-only API boundary, local Docker/PostgreSQL smoke의 구현 사실과 evidence grade를 분리해 기록한다.
wiki/concepts/n-plus-one-query-observability.md후보:- lazy loading, fetch join paging, batch fetch, projection, Top-N, keyset의 관찰 지표를 일반 개념으로 정리한다.
- 필요한 추가 검증:
- 깨끗한 clone/worktree에서 11개 tag의 compose/API smoke를 반복한다.
- deployment manifest/env registry에서
labprofile이 운영 환경에 활성화되지 않는지 확인한다. - base architecture failure를 분리 수정한 뒤 full
./gradlew check결과를 기록한다.
Sources / 근거 후보
글감 단계의 후보 링크다. 최종 블로그의 사실 근거는 canonical 문서에서 다시 검증한다.
- raw/branch-notes/experiment-nplus1-feed-api-replay — 구현·Docker smoke·검증 한계의 직접 근거.
- raw/official-docs/test-taxonomy-testcontainers-official — D4가 실제 PostgreSQL integration evidence를 택한 외부 근거.
- raw/official-docs/cqrs-pattern-azure-architecture-center — D3의 same-store CQRS-lite와 별도 read store CQRS 구분 근거.
- raw/official-docs/spring-data-jpa-projections-spring-official — D3의 application 반환용 projection/read shape 분리 근거.
미해결 / Unknown
- 아직 확인해야 할 사실:
- 다른 깨끗한 machine/worktree에서도 모든 11개 tag가 동일한 Compose/API guide로 재현되는지는 확인되지 않았다.
- 운영 환경에서
labprofile이 활성화되지 않는다는 deployment-level 증거는 아직 없다.
- 과장하면 안 되는 부분:
- 기록된 HTTP·
psql결과는 local Docker Compose PostgreSQL smoke이며 production 성능, latency SLA, 운영 권한 경계를 증명하지 않는다. - L12는 Crown과 동등한 visibility/keyset 해법이 아니라 same-store CQRS-lite의 two-query projection이다.
- 기록된 HTTP·
- 블로그로 쓰기 전에 필요한 canonical 정제:
- stage/tag와 guide의 매핑을 history rewrite 이후에도 다시 확인한다.
- 사실 후보별 evidence grade와 관찰 명령을 canonical project 문서에 고정한다.
Decision / 처리 결정
- 액션:
keep-as-topic - 이유: checkout replay와 로컬 smoke는 evidence가 있지만, 아직 canonical project/concept 문서와 다른 machine 재현 근거가 없다.
- 다음 단계:
wiki/projects/nplus1-presentation-prep/nplus1-feed-api-replay.md후보를 evidence grade와 함께 정제한 뒤에만/blogify를 검토한다.
Related / 관련
- 관련 branch: raw/branch-notes/experiment-nplus1-feed-api-replay
- 관련 error: 생성 전. replay worktree root discovery 및 base architecture failure는 Parent의 Cluster에서 별도 raw error로 추적한다.
- 관련 interview prep: 생성 전. Crown one-query와 CQRS-lite read model의 구분은 Parent의 Cluster에서 별도 raw interview note로 추적한다.
- derived blog: 생성 전. 생성 시
wiki/blog/nplus1-lab-checkoutable-api-replay-2026-07-15.md후보