222 lines
7.1 KiB
JSON
Executable File
222 lines
7.1 KiB
JSON
Executable File
{
|
|
"version": "1.1",
|
|
"id": "strategy-journey",
|
|
"title": "두 형제 문제는 Fetch Join에서 합류한 뒤 단계별 해법으로 최종 구조가 된다",
|
|
"question": "과제 요구사항에서 시작해 기준선의 N1·N2 분기와 실패·개선을 거쳐 최종 피드 조회 구조는 어떻게 발전하는가?",
|
|
"type": "flow",
|
|
"direction": "LR",
|
|
"audience": [
|
|
"백엔드 엔지니어",
|
|
"성능 개선 과정을 검토하는 독자"
|
|
],
|
|
"summary": "요구사항과 모델을 거친 기준선에서 N1·N2가 동시에 갈라져 Fetch Join으로 합류하고, 실패 뒤 Batch Fetch·DTO Projection·Top-3·Keyset·가시성 인덱싱을 차례로 거쳐 최종 피드 조회 구조에 도달한다.",
|
|
"alt": "요구사항과 모델에서 기준선으로 진행한 뒤 N1과 N2로 분기하고 Fetch Join에서 합류해, 실패와 다섯 개선 단계를 거쳐 최종 피드 조회 구조에 이르는 흐름도.",
|
|
"long_description": "왼쪽에서 과제 요구사항, 도메인·데이터 모델, 최초 피드 조회 기준선 순으로 시작한다. 기준선에서 컬렉션 N+1(N1)과 User·Page 연관의 숨은 쿼리(N2)가 서로 앞뒤가 아닌 형제 문제로 동시에 갈라지고, 두 경로는 Fetch Join 시도에서 합류한다. 이 시도는 다중 컬렉션·페이징 실패로 이어진다. 마지막 노드는 Batch Fetch, DTO Projection, 아이템별 Top-3, Keyset Pagination, 가시성 조건 인덱싱을 거쳐 최종 피드 조회 구조에 도달하는 순서를 담는다.",
|
|
"source_context": {
|
|
"document": "docs/n+1liner/final/document.md",
|
|
"document_sha256": "f9e048a68db0ab82078955bf611b06a71a0033539e0c87ff0aa0d620a3126e36",
|
|
"anchor": {
|
|
"kind": "heading",
|
|
"value": "2. 조회 전략의 전체 여정",
|
|
"line": 30
|
|
}
|
|
},
|
|
"composition": {
|
|
"profile": "component-flow",
|
|
"diagram_only": true,
|
|
"reference_ids": [
|
|
"payment-event-flow"
|
|
],
|
|
"rationale": "요구사항에서 최종 조회 구조까지 한 방향으로 발전하면서 기준선의 두 형제 문제가 분기했다가 Fetch Join 시도에서 합류하므로, 좌측 출발점·중앙의 분기와 합류·우측 종착점을 갖는 component-flow가 전체 여정을 가장 직접적으로 드러낸다.",
|
|
"focus_node": "fetch-join-failure"
|
|
},
|
|
"groups": [],
|
|
"nodes": [
|
|
{
|
|
"id": "requirements-model-baseline",
|
|
"label": "요구사항·모델·기준선",
|
|
"kind": "journey-stage",
|
|
"role": "source",
|
|
"description": "전체 여정은 과제 요구사항에서 도메인·데이터 모델을 거쳐 최초 피드 조회 기준선으로 시작한다.",
|
|
"details": [
|
|
"1 과제 요구사항",
|
|
"2 도메인·데이터 모델",
|
|
"3 최초 피드 조회"
|
|
],
|
|
"evidence": [
|
|
{
|
|
"start_line": 23,
|
|
"end_line": 26
|
|
},
|
|
{
|
|
"start_line": 42,
|
|
"end_line": 42
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "collection-n-plus-one",
|
|
"label": "컬렉션 N+1 (N1)",
|
|
"kind": "problem",
|
|
"role": "service",
|
|
"description": "기준선에서 N2와 동시에 나타나는 컬렉션 조회 문제다.",
|
|
"details": [
|
|
"유형: 컬렉션 N+1",
|
|
"발생: 기준선과 동시에",
|
|
"관계: N2와 형제 문제",
|
|
"진행: Fetch Join 합류"
|
|
],
|
|
"evidence": [
|
|
{
|
|
"start_line": 32,
|
|
"end_line": 34
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "hidden-to-one-queries",
|
|
"label": "User·Page 숨은 쿼리 (N2)",
|
|
"kind": "problem",
|
|
"role": "service",
|
|
"description": "기준선에서 N1과 동시에 나타나는 User·Page 연관의 숨은 쿼리 문제다.",
|
|
"details": [
|
|
"유형: User·Page 숨은 쿼리",
|
|
"발생: 기준선과 동시에",
|
|
"관계: N1과 형제 문제",
|
|
"진행: Fetch Join 합류"
|
|
],
|
|
"evidence": [
|
|
{
|
|
"start_line": 32,
|
|
"end_line": 34
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "fetch-join-failure",
|
|
"label": "Fetch Join → 실패",
|
|
"kind": "query-strategy",
|
|
"role": "service",
|
|
"description": "N1과 N2가 Fetch Join 시도에서 합류한 뒤 다중 컬렉션·페이징 실패로 이어진다.",
|
|
"details": [
|
|
"1 N1·N2 합류",
|
|
"2 다중 컬렉션·페이징 실패"
|
|
],
|
|
"emphasis": "warning",
|
|
"evidence": [
|
|
{
|
|
"start_line": 34,
|
|
"end_line": 35
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "improvement-chain",
|
|
"label": "후속 개선 → 최종 구조",
|
|
"kind": "query-strategy",
|
|
"role": "sink",
|
|
"description": "실패 뒤 다섯 조회 전략 단계가 명시된 순서로 발전해 최종 피드 조회 구조를 만든다.",
|
|
"details": [
|
|
"1 Batch Fetch",
|
|
"2 DTO Projection",
|
|
"3 아이템별 Top-3",
|
|
"4 Keyset Pagination",
|
|
"5 가시성 조건 인덱싱",
|
|
"6 최종 피드 조회 구조"
|
|
],
|
|
"emphasis": "primary",
|
|
"evidence": [
|
|
{
|
|
"start_line": 32,
|
|
"end_line": 32
|
|
},
|
|
{
|
|
"start_line": 35,
|
|
"end_line": 36
|
|
}
|
|
],
|
|
"assumption": false
|
|
}
|
|
],
|
|
"edges": [
|
|
{
|
|
"id": "baseline-reveals-n1",
|
|
"from": "requirements-model-baseline",
|
|
"to": "collection-n-plus-one",
|
|
"label": "동시 분기",
|
|
"kind": "problem",
|
|
"evidence": [
|
|
{
|
|
"start_line": 32,
|
|
"end_line": 33
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "baseline-reveals-n2",
|
|
"from": "requirements-model-baseline",
|
|
"to": "hidden-to-one-queries",
|
|
"label": "동시 분기",
|
|
"kind": "problem",
|
|
"evidence": [
|
|
{
|
|
"start_line": 32,
|
|
"end_line": 33
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "n1-joins-fetch-join",
|
|
"from": "collection-n-plus-one",
|
|
"to": "fetch-join-failure",
|
|
"label": "합류",
|
|
"kind": "flow",
|
|
"evidence": [
|
|
{
|
|
"start_line": 34,
|
|
"end_line": 34
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "n2-joins-fetch-join",
|
|
"from": "hidden-to-one-queries",
|
|
"to": "fetch-join-failure",
|
|
"label": "합류",
|
|
"kind": "flow",
|
|
"evidence": [
|
|
{
|
|
"start_line": 34,
|
|
"end_line": 34
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "failure-to-improvements",
|
|
"from": "fetch-join-failure",
|
|
"to": "improvement-chain",
|
|
"label": "실패 뒤 단계별 개선",
|
|
"kind": "improvement",
|
|
"evidence": [
|
|
{
|
|
"start_line": 34,
|
|
"end_line": 36
|
|
}
|
|
],
|
|
"assumption": false
|
|
}
|
|
],
|
|
"legend": [],
|
|
"metadata": {
|
|
"rationale": "line 25의 전체 순서를 유지하면서 문서 폭에 맞추기 위해 연속된 출발 세 단계, Fetch Join과 그 실패, 실패 뒤 다섯 개선 단계를 각각 하나의 단계 노드 안에 번호로 묶었다."
|
|
}
|
|
}
|