Files
document-haness/docs/n+1liner/tech-log-studio/tech-log-tree.json
T

410 lines
17 KiB
JSON

{
"schemaVersion": 2,
"project": "n+1liner",
"ssot": "final/document.md",
"ssotSha256": "a32d9e8ba07129fc26deeef2ce07e3624ce6f34888d6cf503d0b7f226bbb4b6b",
"generatedAt": "2026-09-04",
"note": "글감 목록이다. file 이 있으면 이미 쓴 기록이고, 없으면 아직 쓰지 않은 글감이다. ssotSha256 이 지금 final/document.md 와 다르면 SSOT 가 바뀐 뒤 트리를 다시 보지 않은 것이다.",
"readinessValues": [
"READY",
"NEEDS_EVIDENCE",
"BLOCKED",
"REJECTED"
],
"topics": {
"jpa-feed-query-performance": {
"topic": "jpa-feed-query-performance",
"kinds": {
"case": [
{
"title": "Collection Fetch Join Pagination의 In-memory Paging",
"slug": "collection-fetch-join-in-memory-paging",
"file": "jpa-feed-query-performance/case/case-collection-fetch-join-in-memory-paging.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "c1158754-e3d2-47b8-bb41-81787c0ca84b",
"assets": [
"in-memory-paging"
],
"evidence": [
"../../../final/evidence/raw/explain/l4-entity-paging-limit.txt",
"../../../final/evidence/raw/explain/l5-entity-paging-limit.txt"
],
"relations": [
"Collection Fetch Join과 Pagination을 같이 사용하지 않는다",
"Fetch Join으로 N+1을 해결하다 만난 MultiBag과 행 폭증",
"Fetch Join · Batch · Projection 선택 기준"
]
},
{
"title": "Fetch 타입이 아니라 조회 방식이 만든 ToOne N+1",
"slug": "eager-toone-nplus1-without-access",
"file": "jpa-feed-query-performance/case/case-eager-toone-nplus1-without-access.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "32d0be7d-d88e-4760-8d91-35d3a233a99a",
"assets": [
"eager-lazy-query-sequence"
],
"evidence": [
"../../../final/evidence/raw/explain/highlights-child-plan-A.txt",
"../../../final/evidence/raw/explain/toone-pages-plan.txt",
"../../../final/evidence/raw/explain/toone-users-plan.txt"
],
"relations": [
"Fetch Type과 Fetch Strategy 구분",
"Projection 이후에도 1,509행을 읽은 Row Over-fetch",
"JPA N+1 정량 진단 기준"
]
},
{
"title": "Fetch Join으로 N+1을 해결하다 만난 MultiBag과 행 폭증",
"slug": "fetch-join-multibag-and-row-explosion",
"file": "jpa-feed-query-performance/case/case-fetch-join-multibag-and-row-explosion.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "7ed75172-fd56-42bf-956a-8f9fc1cca235",
"assets": [
"cartesian-row-multiplication"
],
"evidence": [
"../../../final/evidence/raw/explain/l3-cartesian-join-plan.txt"
],
"relations": [
"Fetch Join · Batch · Projection 선택 기준",
"Fetch 타입이 아니라 조회 방식이 만든 ToOne N+1",
"Collection Fetch Join Pagination의 In-memory Paging"
]
},
{
"title": "Projection 이후에도 1,509행을 읽은 Row Over-fetch",
"slug": "projection-row-over-fetch",
"file": "jpa-feed-query-performance/case/case-projection-row-over-fetch.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "4c9c3b90-bc89-4300-9334-088ea95d37d8",
"assets": [
"projection-row-over-fetch"
],
"evidence": [
"../../../final/evidence/raw/explain/l6-parent-projection.txt"
],
"relations": [
"화면 조회는 Read Projection을 사용한다",
"Top-N-per-group 선택 기준",
"Fetch Join · Batch · Projection 선택 기준"
]
},
{
"title": "Visibility OR이 Keyset Index를 깨뜨린 문제",
"slug": "visibility-or-breaks-keyset-index",
"file": "jpa-feed-query-performance/case/case-visibility-or-breaks-keyset-index.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "e6715e81-6dbd-4287-8e19-946c334f38fb",
"assets": [
"keyset-vs-offset"
],
"evidence": [
"../../../final/evidence/raw/explain/l15-keyset-no-index.txt",
"../../../final/evidence/raw/explain/l15-offset-deep-page.txt"
],
"relations": [
"Feed Visibility Query Pattern",
"Keyset Pagination 설계 기준",
"feed_visible을 Production CQRS로 승격할 것인가"
]
}
],
"concept": [],
"reference": [
{
"title": "Feed Visibility Query Pattern",
"slug": "feed-visibility-query-pattern",
"file": "jpa-feed-query-performance/reference/reference-feed-visibility-query-pattern.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "635fcedd-d402-4297-bcf3-9fcdf4200d28",
"assets": [],
"evidence": [],
"relations": [
"Visibility OR이 Keyset Index를 깨뜨린 문제",
"feed_visible을 Production CQRS로 승격할 것인가",
"Keyset Pagination 설계 기준"
]
},
{
"title": "Fetch Join · Batch · Projection 선택 기준",
"slug": "fetch-strategy-selection",
"file": "jpa-feed-query-performance/reference/reference-fetch-strategy-selection.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "db99cbc5-9123-4599-b368-39ff3170e81d",
"assets": [],
"evidence": [],
"relations": [
"Fetch Join으로 N+1을 해결하다 만난 MultiBag과 행 폭증",
"Collection Fetch Join Pagination의 In-memory Paging",
"Projection 이후에도 1,509행을 읽은 Row Over-fetch"
]
},
{
"title": "Fetch Type과 Fetch Strategy 구분",
"slug": "fetch-type-vs-fetch-strategy",
"file": "jpa-feed-query-performance/reference/reference-fetch-type-vs-fetch-strategy.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "51095f6e-2cc8-439c-8648-065033614215",
"assets": [],
"evidence": [],
"relations": [
"필드 접근 없이 발생한 EAGER ToOne N+1",
"Fetch Join · Batch · Projection 선택 기준",
"JPA N+1 정량 진단 기준"
]
},
{
"title": "Keyset Pagination 설계 기준",
"slug": "keyset-pagination-design",
"file": "jpa-feed-query-performance/reference/reference-keyset-pagination-design.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "06788903-3dfa-4f70-b159-f1224384fd0b",
"assets": [],
"evidence": [],
"relations": [
"Visibility OR이 Keyset Index를 깨뜨린 문제",
"Feed Pagination은 Keyset을 사용한다",
"PostgreSQL Query Plan 측정 기준"
]
},
{
"title": "JPA N+1 정량 진단 기준",
"slug": "nplus1-quantitative-diagnosis",
"file": "jpa-feed-query-performance/reference/reference-nplus1-quantitative-diagnosis.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "b0b55ac9-c0a3-4c01-ba84-0aa478923ace",
"assets": [],
"evidence": [],
"relations": [
"Fetch 타입이 아니라 조회 방식이 만든 ToOne N+1",
"PostgreSQL Query Plan 측정 기준"
]
},
{
"title": "PostgreSQL Query Plan 측정 기준",
"slug": "postgresql-query-plan-measurement",
"file": "jpa-feed-query-performance/reference/reference-postgresql-query-plan-measurement.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "e8c2e9ea-cd87-46f8-9469-849dbd433d86",
"assets": [],
"evidence": [],
"relations": [
"Query Plan은 실제 PostgreSQL에서 측정한다",
"JPA N+1 정량 진단 기준",
"ANALYZE 이후 Cardinality Estimate는 어떻게 달라지는가"
]
},
{
"title": "Top-N-per-group 선택 기준",
"slug": "top-n-per-group-selection",
"file": "jpa-feed-query-performance/reference/reference-top-n-per-group-selection.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "bf5f2462-0e94-4723-bdc8-f7dd709b2dbb",
"assets": [],
"evidence": [],
"relations": [
"Projection 이후에도 1,509행을 읽은 Row Over-fetch",
"PostgreSQL Query Plan 측정 기준",
"Fetch Join · Batch · Projection 선택 기준"
]
}
],
"question": [
{
"title": "ANALYZE 이후 Cardinality Estimate는 어떻게 달라지는가",
"slug": "cardinality-estimate-after-analyze",
"file": "jpa-feed-query-performance/question/question-cardinality-estimate-after-analyze.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "e1e0e2a0-c6b6-45bf-be42-f697ba5e2fff",
"assets": [],
"evidence": [],
"relations": [
"PostgreSQL Query Plan 측정 기준",
"Fetch 타입이 아니라 조회 방식이 만든 ToOne N+1"
]
},
{
"title": "실제 동시 트래픽에서도 이 구조가 안정적인가",
"slug": "concurrency-stability",
"file": "jpa-feed-query-performance/question/question-concurrency-stability.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "6cbe963f-86f8-4df6-be5a-900712970d01",
"assets": [],
"evidence": [],
"relations": [
"PostgreSQL Query Plan 측정 기준",
"feed_visible을 Production CQRS로 승격할 것인가"
]
},
{
"title": "Round Trip과 Row Volume을 독립 측정할 것인가",
"slug": "isolate-round-trip-and-row-volume",
"file": "jpa-feed-query-performance/question/question-isolate-round-trip-and-row-volume.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "5159c415-232d-424a-970a-b0db52746767",
"assets": [],
"evidence": [],
"relations": [
"JPA N+1 정량 진단 기준",
"Fetch 타입이 아니라 조회 방식이 만든 ToOne N+1"
]
},
{
"title": "Highlight 없는 FeedItem을 허용할 것인가",
"slug": "nullable-first-highlighted-at",
"file": "jpa-feed-query-performance/question/question-nullable-first-highlighted-at.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "b099ca65-bf9f-4d61-814c-74722453fa3c",
"assets": [],
"evidence": [],
"relations": [
"Keyset Pagination 설계 기준",
"Visibility OR이 Keyset Index를 깨뜨린 문제"
]
},
{
"title": "feed_visible을 Production CQRS로 승격할 것인가",
"slug": "promote-feed-visible-to-cqrs",
"file": "jpa-feed-query-performance/question/question-promote-feed-visible-to-cqrs.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "5088ce14-b096-41d3-abba-64b7afb48bb9",
"assets": [],
"evidence": [],
"relations": [
"Feed Visibility Query Pattern",
"현재 Read Model은 CQRS-lite로 유지한다",
"Visibility OR이 Keyset Index를 깨뜨린 문제"
]
}
],
"decision": [
{
"title": "Entity Graph 조회에는 Batch Fetch를 사용한다",
"slug": "batch-fetch-for-entity-graph",
"file": "jpa-feed-query-performance/decision/decision-batch-fetch-for-entity-graph.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "08a74b35-10c3-4874-8fbc-209b0b6e942e",
"assets": [],
"evidence": [],
"relations": [
"Fetch Join · Batch · Projection 선택 기준",
"Collection Fetch Join Pagination의 In-memory Paging",
"Projection 이후에도 1,509행을 읽은 Row Over-fetch"
]
},
{
"title": "현재 Read Model은 CQRS-lite로 유지한다",
"slug": "keep-read-model-as-cqrs-lite",
"file": "jpa-feed-query-performance/decision/decision-keep-read-model-as-cqrs-lite.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "7f248f68-ce2b-43ec-94ce-82324d0bd1a7",
"assets": [],
"evidence": [],
"relations": [
"feed_visible을 Production CQRS로 승격할 것인가",
"화면 조회는 Read Projection을 사용한다",
"Feed Visibility Query Pattern"
]
},
{
"title": "Feed Pagination은 Keyset을 사용한다",
"slug": "keyset-for-feed-pagination",
"file": "jpa-feed-query-performance/decision/decision-keyset-for-feed-pagination.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "1dbce381-f0dc-4d49-ad68-bd31d205677e",
"assets": [],
"evidence": [],
"relations": [
"Keyset Pagination 설계 기준",
"Visibility OR이 Keyset Index를 깨뜨린 문제",
"Highlight 없는 FeedItem을 허용할 것인가"
]
},
{
"title": "Query Plan은 실제 PostgreSQL에서 측정한다",
"slug": "measure-plan-on-real-postgresql",
"file": "jpa-feed-query-performance/decision/decision-measure-plan-on-real-postgresql.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "ae6c9bea-d3a3-46e1-bbd4-8d580d336394",
"assets": [],
"evidence": [],
"relations": [
"PostgreSQL Query Plan 측정 기준",
"Fetch 타입이 아니라 조회 방식이 만든 ToOne N+1",
"Visibility OR이 Keyset Index를 깨뜨린 문제"
]
},
{
"title": "Collection Fetch Join과 Pagination을 같이 사용하지 않는다",
"slug": "no-collection-fetch-join-with-pagination",
"file": "jpa-feed-query-performance/decision/decision-no-collection-fetch-join-with-pagination.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "5e4d033c-d6fe-4257-a4dc-1ade44473c72",
"assets": [],
"evidence": [],
"relations": [
"Collection Fetch Join Pagination의 In-memory Paging",
"Fetch Join으로 N+1을 해결하다 만난 MultiBag과 행 폭증",
"Fetch Join · Batch · Projection 선택 기준"
]
},
{
"title": "Query Strategy는 FeedQueryPort 뒤에서 소유한다",
"slug": "query-strategy-behind-port",
"file": "jpa-feed-query-performance/decision/decision-query-strategy-behind-port.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "4e3200c8-eff5-4442-ae84-ae7b7fa92c8b",
"assets": [],
"evidence": [],
"relations": [
"Fetch Join · Batch · Projection 선택 기준",
"Collection Fetch Join Pagination의 In-memory Paging",
"화면 조회는 Read Projection을 사용한다"
]
},
{
"title": "화면 조회는 Read Projection을 사용한다",
"slug": "read-projection-for-screen-query",
"file": "jpa-feed-query-performance/decision/decision-read-projection-for-screen-query.md",
"readiness": "READY",
"status": "게시 전",
"studioId": "30a37f34-b406-4061-b924-e22e0be0c3bf",
"assets": [],
"evidence": [],
"relations": [
"Projection 이후에도 1,509행을 읽은 Row Over-fetch",
"Fetch Join · Batch · Projection 선택 기준",
"Query Strategy는 FeedQueryPort 뒤에서 소유한다"
]
}
]
}
}
}
}