refactor: 문서 개선 중

This commit is contained in:
donghyeon-ka
2026-09-21 14:30:55 +09:00
parent c93cdea150
commit 805a18f486
1497 changed files with 525837 additions and 59152 deletions
@@ -4,20 +4,20 @@
"title": "열린 질문 네 개가 닿은 곳",
"question": "설계로 못 푼 질문들은 어디에서 답을 얻었는가",
"type": "architecture",
"direction": "TB",
"direction": "LR",
"audience": [
"같은 질문을 안고 있는 백엔드 엔지니어"
],
"summary": "네 질문 중 셋은 저장소가 아니라 스키마와 조회 키에서 답이 나왔고, 하나는 세션 스냅샷이라는 성질에서 나왔다.",
"summary": "Q1은 외부 저장소와 스키마, Q2는 client session 삭제, Q3는 서로 다른 조회 키, Q4는 헤더 도착과 인가의 구분 및 재인증 전 세션 스냅샷에서 답이 나왔다.",
"alt": "네 개의 열린 질문이 각각 어느 실험 층에 닿아 어떤 구조적 원인으로 이어지는지 보이는 구성.",
"long_description": "Q1 은 저장소를 밖으로 빼면 다중 인스턴스와 재시작은 풀리지만 덮어쓰기와 로그아웃 정리는 남는다는 답을 얻었고 원인은 기본키에 세션 id 가 없다는 것이었다. Q2 는 이긴 요청의 토큰조차 못 쓴다는 답이다. Q3 은 세션과 토큰의 조회 키가 다르므로 각각 결정해야 한다는 답이다. Q4 프록시가 헤더를 먼저 지워야 하고 IdP 의 클레임 변경은 재인증 전까지 반영되지 않는다는 답이다.",
"long_description": "Q1은 저장소를 밖으로 빼면 다중 인스턴스와 재시작은 풀리지만 기본키에 session id가 없어 덮어쓰기와 정리 문제가 남는다는 답이다. Q2는 refresh 경쟁이 감지되면 client session이 지워져 이긴 요청의 새 token도 쓸 수 없다는 답이다. Q3는 Application Session과 OAuth2AuthorizedClient의 조회 키가 달라 각각 저장소를 결정해야 한다는 답이다. Q4는 위조 헤더가 permitAll echo까지 도착해도 JWT 보호 경로는 401이었고, 헤더 삭제 처방은 미적용이며 IdP claim 변경은 재인증 전까지 반영되지 않는다는 답이다.",
"source_context": {
"document": "docs/keycloak-session-store/final/document.md",
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
"document_sha256": "28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560",
"anchor": {
"kind": "heading",
"value": "열린 질문 네 개에 대한 답",
"line": 680
"line": 1221
}
},
"composition": {
@@ -36,68 +36,86 @@
"kind": "component",
"role": "source",
"emphasis": "primary",
"description": "설계로는 답할 수 없었다.",
"description": "설계만으로는 닫히지 않았던 Q1부터 Q4다.",
"details": [
"Q1 · Q2 · Q3 · Q4"
],
"evidence": [
{
"start_line": 682,
"end_line": 690
"start_line": 1223,
"end_line": 1228
}
],
"assumption": false
},
{
"id": "store-choice",
"label": "저장소 선택",
"kind": "process",
"role": "control",
"emphasis": "normal",
"description": "다중 인스턴스와 재시작은 여기서 풀린다.",
"details": [
"Redis · PostgreSQL"
],
"evidence": [
{
"start_line": 682,
"end_line": 692
}
],
"assumption": false
},
{
"id": "schema",
"label": "스키마와 조회 키",
"kind": "process",
"role": "control",
"emphasis": "warning",
"description": "덮어쓰기와 정리 누락은 여기가 원인이다.",
"details": [
"키에 세션 id 없음"
],
"evidence": [
{
"start_line": 682,
"end_line": 694
}
],
"assumption": false
},
{
"id": "snapshot",
"label": "세션 스냅샷",
"id": "q1",
"label": "Q1 · 저장소와 스키마",
"kind": "process",
"role": "target",
"emphasis": "warning",
"description": "IdP 에서 클레임을 바꿔도 재인증까지 옛 값이 간다.",
"description": "①②는 외부 저장소로 풀리고 ③④는 기본키 문제로 남는다.",
"details": [
"옛 클레임 값"
"외부 저장소 · 세션 id 없는 PK"
],
"evidence": [
{
"start_line": 682,
"end_line": 696
"start_line": 1225,
"end_line": 1225
}
],
"assumption": false
},
{
"id": "q2",
"label": "Q2 · 회전 경쟁",
"kind": "process",
"role": "target",
"emphasis": "warning",
"description": "경쟁이 감지되면 client session이 지워진다.",
"details": [
"이긴 새 token도 사용 불가"
],
"evidence": [
{
"start_line": 1226,
"end_line": 1226
}
],
"assumption": false
},
{
"id": "q3",
"label": "Q3 · 조회 키 분리",
"kind": "process",
"role": "target",
"emphasis": "normal",
"description": "Session과 AuthorizedClient는 각각 결정해야 한다.",
"details": [
"session id · principal"
],
"evidence": [
{
"start_line": 1227,
"end_line": 1227
}
],
"assumption": false
},
{
"id": "q4",
"label": "Q4 · Edge 신뢰 경계",
"kind": "process",
"role": "target",
"emphasis": "warning",
"description": "헤더 도착과 인가 우회는 다르고 claim은 재인증까지 snapshot이다.",
"details": [
"permitAll 200 · JWT 401 · 삭제 처방 미검증"
],
"evidence": [
{
"start_line": 1228,
"end_line": 1228
}
],
"assumption": false
@@ -105,43 +123,57 @@
],
"edges": [
{
"id": "q-s",
"id": "questions-q1",
"from": "questions",
"to": "store-choice",
"label": "1차 검토",
"to": "q1",
"label": "",
"kind": "request",
"evidence": [
{
"start_line": 682,
"end_line": 692
"start_line": 1225,
"end_line": 1225
}
],
"assumption": false
},
{
"id": "s-sc",
"from": "store-choice",
"to": "schema",
"label": "미해결 잔여",
"id": "questions-q2",
"from": "questions",
"to": "q2",
"label": "",
"kind": "request",
"evidence": [
{
"start_line": 682,
"end_line": 694
"start_line": 1226,
"end_line": 1226
}
],
"assumption": false
},
{
"id": "sc-sn",
"from": "schema",
"to": "snapshot",
"label": "최종 잔여",
"id": "questions-q3",
"from": "questions",
"to": "q3",
"label": "",
"kind": "request",
"evidence": [
{
"start_line": 682,
"end_line": 696
"start_line": 1227,
"end_line": 1227
}
],
"assumption": false
},
{
"id": "questions-q4",
"from": "questions",
"to": "q4",
"label": "답",
"kind": "request",
"evidence": [
{
"start_line": 1228,
"end_line": 1228
}
],
"assumption": false
@@ -149,6 +181,6 @@
],
"legend": [],
"metadata": {
"rationale": "네 질문을 나열하는 대신 공통 원인으로 모았다. 저장소 선택으로 풀리지 않는 것들이 한자리에 있다."
"rationale": "네 질문을 하나의 순차 원인으로 만들지 않고, 각 질문이 실험에서 얻은 서로 다른 답으로 갈라지는 구조를 그렸다."
}
}
}