init: llm-wiki-haness 하네스 설계
This commit is contained in:
+113
@@ -0,0 +1,113 @@
|
||||
<mxfile host="Claude Code" agent="ca-superpowers" version="24.0.0" type="device">
|
||||
<diagram name="P3A-Single-EC2-no-Google" id="p3a-overall">
|
||||
<mxGraphModel dx="1422" dy="800" grid="0" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1400" pageHeight="900" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
|
||||
<!-- TITLE -->
|
||||
<mxCell id="title" value="P3A — Single EC2 (no Google) vanilla JS SPA + Spring Boot Resource Server + Keycloak (localhost)" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=16;fontStyle=1;fontFamily=Pretendard,Helvetica;fontColor=#1F6FEB;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="20" width="900" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- EXTERNAL: User -->
|
||||
<mxCell id="grp-external" value="클러스터 외부 (Internet)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#F6F8FA;strokeColor=#D0D7DE;strokeWidth=1;dashed=1;fontSize=12;fontStyle=1;fontFamily=Pretendard,Helvetica;fontColor=#57606A;align=left;verticalAlign=top;spacingLeft=12;spacingTop=8;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="90" width="240" height="700" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="user" value="User Browser (SPA 호출 + OIDC redirect)" style="ellipse;whiteSpace=wrap;html=1;fillColor=#FFFFFF;strokeColor=#57606A;strokeWidth=1;fontSize=12;fontStyle=0;fontFamily=Pretendard,Helvetica;fontColor=#24292F;align=center;verticalAlign=middle;" vertex="1" parent="1">
|
||||
<mxGeometry x="80" y="380" width="160" height="100" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- TRUST BOUNDARY: Single EC2 -->
|
||||
<mxCell id="grp-ec2" value="Trust Boundary: Single EC2 (localhost containers)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#FBFCFD;strokeColor=#1F6FEB;strokeWidth=2;dashed=0;fontSize=13;fontStyle=1;fontFamily=Pretendard,Helvetica;fontColor=#1F6FEB;align=left;verticalAlign=top;spacingLeft=12;spacingTop=8;" vertex="1" parent="1">
|
||||
<mxGeometry x="320" y="90" width="1000" height="700" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- nginx -->
|
||||
<mxCell id="nginx" value="<b>nginx</b> static SPA serving + reverse proxy :443 (or :80)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#FFF7ED;strokeColor=#FB923C;strokeWidth=1;fontSize=11;fontStyle=0;fontFamily=Pretendard,Helvetica;fontColor=#C2410C;align=center;verticalAlign=middle;" vertex="1" parent="1">
|
||||
<mxGeometry x="380" y="140" width="220" height="100" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Backend -->
|
||||
<mxCell id="backend" value="<b>Spring Boot</b> Resource Server Spring Security 6.x JWT validator (iss, aud, exp) localhost:8080" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#D4EDDA;strokeColor=#155724;strokeWidth=1;fontSize=11;fontStyle=0;fontFamily=Pretendard,Helvetica;fontColor=#155724;align=center;verticalAlign=middle;" vertex="1" parent="1">
|
||||
<mxGeometry x="850" y="140" width="240" height="120" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Keycloak -->
|
||||
<mxCell id="keycloak" value="<b>Keycloak 25.x</b> Authorization Server KC_HOSTNAME=&lt;public-host&gt; localhost:8180" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#CFE2FF;strokeColor=#0D6EFD;strokeWidth=1;fontSize=11;fontStyle=0;fontFamily=Pretendard,Helvetica;fontColor=#0D6EFD;align=center;verticalAlign=middle;" vertex="1" parent="1">
|
||||
<mxGeometry x="380" y="400" width="260" height="120" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- PostgreSQL -->
|
||||
<mxCell id="postgres" value="<b>PostgreSQL 16</b> Keycloak 백엔드 DB localhost:5432" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#F8D7DA;strokeColor=#842029;strokeWidth=1;fontSize=11;fontStyle=0;fontFamily=Pretendard,Helvetica;fontColor=#842029;align=center;verticalAlign=middle;" vertex="1" parent="1">
|
||||
<mxGeometry x="900" y="420" width="180" height="100" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- EDGES -->
|
||||
<!-- User <-> nginx (HTTPS) -->
|
||||
<mxCell id="e-user-nginx" value="HTTPS :443 GET / + GET /api/v1/* Bearer access_token" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.3;exitDx=0;exitDy=0;entryX=0;entryY=0.4;entryDx=0;entryDy=0;strokeColor=#0969DA;strokeWidth=2;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#0969DA;" edge="1" parent="1" source="user" target="nginx">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="e-nginx-user" value="static SPA (HTML/JS/CSS)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.7;exitDx=0;exitDy=0;entryX=1;entryY=0.6;entryDx=0;entryDy=0;strokeColor=#57606A;strokeWidth=1;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#57606A;dashed=1;" edge="1" parent="1" source="nginx" target="user">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- User <-> Keycloak (OIDC browser redirect) -->
|
||||
<mxCell id="e-user-kc" value="OIDC Authorization Code + PKCE (browser redirect)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.7;exitDx=0;exitDy=0;entryX=0;entryY=0.3;entryDx=0;entryDy=0;strokeColor=#0D6EFD;strokeWidth=2;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#0D6EFD;" edge="1" parent="1" source="user" target="keycloak">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="e-kc-user" value="{access_token, refresh_token, id_token}" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.7;exitDx=0;exitDy=0;entryX=1;entryY=0.85;entryDx=0;entryDy=0;strokeColor=#57606A;strokeWidth=1;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#57606A;dashed=1;" edge="1" parent="1" source="keycloak" target="user">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- nginx -> Backend (reverse proxy) -->
|
||||
<mxCell id="e-nginx-backend" value="reverse proxy localhost:8080" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeColor=#155724;strokeWidth=2;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#155724;" edge="1" parent="1" source="nginx" target="backend">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Backend -> Keycloak (JWKS) -->
|
||||
<mxCell id="e-backend-kc" value="JWKS (localhost:8180/realms/.../certs)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.3;exitY=1;exitDx=0;exitDy=0;entryX=0.7;entryY=0;entryDx=0;entryDy=0;strokeColor=#155724;strokeWidth=1;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#155724;dashed=1;" edge="1" parent="1" source="backend" target="keycloak">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Keycloak -> PostgreSQL (JDBC) -->
|
||||
<mxCell id="e-kc-pg" value="JDBC (localhost:5432)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeColor=#842029;strokeWidth=2;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#842029;" edge="1" parent="1" source="keycloak" target="postgres">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- LEGEND -->
|
||||
<mxCell id="grp-legend" value="Legend / 범례" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#FFFFFF;strokeColor=#D0D7DE;strokeWidth=1;dashed=0;fontSize=11;fontStyle=1;fontFamily=Pretendard,Helvetica;fontColor=#24292F;align=left;verticalAlign=top;spacingLeft=10;spacingTop=6;" vertex="1" parent="1">
|
||||
<mxGeometry x="900" y="620" width="400" height="160" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="legend-ext" value="외부 (Internet) — 점선 테두리" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#F6F8FA;strokeColor=#D0D7DE;dashed=1;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#57606A;" vertex="1" parent="1">
|
||||
<mxGeometry x="920" y="650" width="180" height="22" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="legend-tb" value="Trust Boundary — 파란 실선" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#FBFCFD;strokeColor=#1F6FEB;strokeWidth=2;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#1F6FEB;" vertex="1" parent="1">
|
||||
<mxGeometry x="920" y="680" width="180" height="22" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="legend-proxy" value="Edge proxy (nginx) — 주황" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#FFF7ED;strokeColor=#FB923C;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#C2410C;" vertex="1" parent="1">
|
||||
<mxGeometry x="920" y="710" width="180" height="22" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="legend-backend" value="Backend (Spring Boot) — 초록" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#D4EDDA;strokeColor=#155724;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#155724;" vertex="1" parent="1">
|
||||
<mxGeometry x="1110" y="650" width="180" height="22" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="legend-kc" value="Auth Server (Keycloak) — 파랑" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#CFE2FF;strokeColor=#0D6EFD;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#0D6EFD;" vertex="1" parent="1">
|
||||
<mxGeometry x="1110" y="680" width="180" height="22" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="legend-db" value="DB (PostgreSQL) — 빨강" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#F8D7DA;strokeColor=#842029;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#842029;" vertex="1" parent="1">
|
||||
<mxGeometry x="1110" y="710" width="180" height="22" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
<mxfile host="Claude Code" agent="ca-superpowers + diagram-standards v1" version="24.0.0" type="device">
|
||||
<diagram name="P3A-Single-EC2-no-Google-Conference" id="p3a-conf">
|
||||
<mxGraphModel dx="2000" dy="1100" grid="0" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="2000" pageHeight="1200" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
|
||||
<!-- ================= HEADER ================= -->
|
||||
<mxCell id="header-title" value="P3A — Single EC2 (no Google federation)" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=22;fontStyle=1;fontFamily=Pretendard,Helvetica;fontColor=#1F2937;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="20" width="1000" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="header-question" value="❓ 질문: 단일 EC2 호스트(localhost)에 SPA + Spring Boot + Keycloak 이 동거할 때, 사용자 요청이 어떤 컴포넌트를 어떤 순서로 거치며 인증·인가·서비스되는가? KC_HOSTNAME 함정은 어디에서 발생하는가?" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=12;fontStyle=0;fontFamily=Pretendard,Helvetica;fontColor=#4B5563;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="58" width="1500" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="header-project" value="📁 Project: keycloak-patterns | branch: develop-keycloak-single-ec2-no-google | status: documented-only" style="text;html=1;strokeColor=none;fillColor=none;align=right;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=11;fontStyle=2;fontFamily=Pretendard,Helvetica;fontColor=#6B7280;" vertex="1" parent="1">
|
||||
<mxGeometry x="1050" y="20" width="900" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ================= NETWORK BOUNDARY: Public Internet ================= -->
|
||||
<mxCell id="grp-internet" value="🌐 Public Internet (untrusted)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#F6F8FA;strokeColor=#D0D7DE;strokeWidth=1;dashed=1;fontSize=11;fontStyle=1;fontFamily=Pretendard,Helvetica;fontColor=#6B7280;align=left;verticalAlign=top;spacingLeft=12;spacingTop=8;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="120" width="320" height="880" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- USER -->
|
||||
<mxCell id="user" value="<b style='font-size:13px'>User Browser</b> Role: SPA 호출 + OIDC redirect Stack: Chromium/Firefox/Safari Endpoint: HTTPS :443 Owner: End user" style="rounded=8;whiteSpace=wrap;html=1;fillColor=#FFFFFF;strokeColor=#57606A;strokeWidth=1.5;fontSize=11;fontStyle=0;fontFamily=Pretendard,Helvetica;fontColor=#24292F;align=center;verticalAlign=middle;" vertex="1" parent="1">
|
||||
<mxGeometry x="80" y="480" width="240" height="140" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ================= NETWORK BOUNDARY: Public-facing edge ================= -->
|
||||
<mxCell id="grp-edge-net" value="🔓 Public-facing edge (HTTPS termination)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#FEF9C3;strokeColor=#EAB308;strokeWidth=1;dashed=1;fontSize=11;fontStyle=1;fontFamily=Pretendard,Helvetica;fontColor=#854D0E;align=left;verticalAlign=top;spacingLeft=12;spacingTop=8;" vertex="1" parent="1">
|
||||
<mxGeometry x="400" y="120" width="380" height="880" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ================= TRUST BOUNDARY: Single EC2 ================= -->
|
||||
<mxCell id="grp-ec2" value="🔒 Trust Boundary: Single EC2 host (localhost docker-compose)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#FBFCFD;strokeColor=#1F6FEB;strokeWidth=2.5;dashed=0;fontSize=12;fontStyle=1;fontFamily=Pretendard,Helvetica;fontColor=#1F6FEB;align=left;verticalAlign=top;spacingLeft=12;spacingTop=8;" vertex="1" parent="1">
|
||||
<mxGeometry x="820" y="120" width="1140" height="880" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- NGINX -->
|
||||
<mxCell id="nginx" value="<b style='font-size:13px'>nginx 1.27</b> Role: SPA static serving + reverse proxy → backend Stack: nginx (alpine docker) Endpoint: :443 (TLS) / :80 (HTTP) Capacity: ~5000 conn (default) Owner: 본인 (vanilla JS 호스팅)" style="rounded=8;whiteSpace=wrap;html=1;fillColor=#FFF7ED;strokeColor=#FB923C;strokeWidth=2;fontSize=11;fontStyle=0;fontFamily=Pretendard,Helvetica;fontColor=#9A3412;align=center;verticalAlign=middle;" vertex="1" parent="1">
|
||||
<mxGeometry x="450" y="180" width="280" height="180" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- BACKEND -->
|
||||
<mxCell id="backend" value="<b style='font-size:13px'>Spring Boot Resource Server</b> Role: 비즈니스 API + JWT validation (iss / aud / exp / signature) Stack: Spring Boot 3.4 / Java 21 + Spring Security 6.x Endpoint: localhost:8080 Capacity: 1 instance (single-host) Owner: 본인 (백엔드)" style="rounded=8;whiteSpace=wrap;html=1;fillColor=#D4EDDA;strokeColor=#155724;strokeWidth=2.5;fontSize=11;fontStyle=0;fontFamily=Pretendard,Helvetica;fontColor=#155724;align=center;verticalAlign=middle;" vertex="1" parent="1">
|
||||
<mxGeometry x="1620" y="180" width="320" height="180" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- KEYCLOAK -->
|
||||
<mxCell id="keycloak" value="<b style='font-size:13px'>Keycloak 25.x</b> Role: OIDC Authorization Server (realm + client + user DB) Stack: Keycloak 25.x (docker) Endpoint: localhost:8180 Config: KC_HOSTNAME=&lt;public-host&gt; KC_HTTP_ENABLED=true Capacity: 1 instance Owner: 본인 (인증)" style="rounded=8;whiteSpace=wrap;html=1;fillColor=#E0E7FF;strokeColor=#3730A3;strokeWidth=2.5;fontSize=11;fontStyle=0;fontFamily=Pretendard,Helvetica;fontColor=#3730A3;align=center;verticalAlign=middle;" vertex="1" parent="1">
|
||||
<mxGeometry x="980" y="500" width="320" height="200" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- POSTGRES -->
|
||||
<mxCell id="postgres" value="<b style='font-size:13px'>PostgreSQL 16</b> Role: Keycloak 사용자/realm DB Stack: postgres:16-alpine Endpoint: localhost:5432 Capacity: 1 instance (no replica) Owner: 본인 (Keycloak 백엔드)" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#F8D7DA;strokeColor=#842029;strokeWidth=2;fontSize=11;fontStyle=0;fontFamily=Pretendard,Helvetica;fontColor=#842029;align=center;verticalAlign=middle;" vertex="1" parent="1">
|
||||
<mxGeometry x="1620" y="510" width="280" height="180" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ================= EDGES ================= -->
|
||||
|
||||
<!-- ① User → nginx: HTTPS SPA request -->
|
||||
<mxCell id="e1" value="① HTTPS GET / (HTML/JS) payload: ~50KB (initial SPA bundle) p99: ~80ms (cold) / ~10ms (cache)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.2;exitDx=0;exitDy=0;entryX=0;entryY=0.3;entryDx=0;entryDy=0;strokeColor=#1F6FEB;strokeWidth=3;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#1F6FEB;fontStyle=1;" edge="1" parent="1" source="user" target="nginx">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ② nginx → User: SPA bundle (response) -->
|
||||
<mxCell id="e2" value="② static bundle HTML / JS / CSS" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.6;exitDx=0;exitDy=0;entryX=1;entryY=0.7;entryDx=0;entryDy=0;strokeColor=#57606A;strokeWidth=1.5;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#57606A;dashed=1;" edge="1" parent="1" source="nginx" target="user">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ③ User → Keycloak: OIDC redirect (browser) -->
|
||||
<mxCell id="e3" value="③ HTTPS GET /realms/&lt;r&gt;/protocol/openid-connect/auth OIDC Authorization Code + PKCE query: ?client_id=spa&code_challenge=...&redirect_uri=... (browser redirect, full URL navigation)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.8;exitDx=0;exitDy=0;entryX=0;entryY=0.3;entryDx=0;entryDy=0;strokeColor=#3730A3;strokeWidth=3;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#3730A3;fontStyle=1;" edge="1" parent="1" source="user" target="keycloak">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ④ Keycloak → User: token (after login) -->
|
||||
<mxCell id="e4" value="④ POST /token (auth code → tokens) { access_token, refresh_token, id_token } p99 ~150ms (PBKDF2 password hash + JWT sign)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.9;entryDx=0;entryDy=0;strokeColor=#57606A;strokeWidth=1.5;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#57606A;dashed=1;" edge="1" parent="1" source="keycloak" target="user">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ⑤ User → nginx (Bearer token, API call) -->
|
||||
<mxCell id="e5" value="⑤ HTTPS GET /api/v1/&lt;resource&gt; Authorization: Bearer &lt;access_token&gt; (XHR/fetch, ~1000 QPS @ peak) p99 &lt; 100ms (target)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.4;exitDx=0;exitDy=0;entryX=0;entryY=0.6;entryDx=0;entryDy=0;strokeColor=#0969DA;strokeWidth=3;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#0969DA;fontStyle=1;" edge="1" parent="1" source="user" target="nginx">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ⑥ nginx → Backend (reverse proxy) -->
|
||||
<mxCell id="e6" value="⑥ proxy_pass http://localhost:8080 Authorization header forward (timeout: 30s, keepalive: 60s)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeColor=#155724;strokeWidth=3;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#155724;fontStyle=1;" edge="1" parent="1" source="nginx" target="backend">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ⑦ Backend → Keycloak (JWKS) -->
|
||||
<mxCell id="e7" value="⑦ GET /realms/&lt;r&gt;/protocol/openid-connect/certs (JWKS — JWT signature 검증용 키) 캐시: 5분 (NimbusJwtDecoder default)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.3;exitY=1;exitDx=0;exitDy=0;entryX=0.7;entryY=0;entryDx=0;entryDy=0;strokeColor=#155724;strokeWidth=1.5;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#155724;dashed=1;" edge="1" parent="1" source="backend" target="keycloak">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ⑧ Keycloak → PostgreSQL -->
|
||||
<mxCell id="e8" value="⑧ JDBC SELECT realm/user/client (Hikari pool: 100 conn default)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeColor=#842029;strokeWidth=3;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#842029;fontStyle=1;" edge="1" parent="1" source="keycloak" target="postgres">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ❌ ERROR PATH: JWT iss mismatch -->
|
||||
<mxCell id="e-err1" value="❌ ⓔ JWT iss mismatch → 401 Unauthorized 원인: KC_HOSTNAME 미설정 시 발생" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.4;exitDx=0;exitDy=0;entryX=1;entryY=0.3;entryDx=0;entryDy=0;strokeColor=#DC2626;strokeWidth=2;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#DC2626;dashed=1;fontStyle=1;" edge="1" parent="1" source="backend" target="user">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ================= CALLOUTS (gotchas) ================= -->
|
||||
|
||||
<!-- Callout 1: KC_HOSTNAME 함정 -->
|
||||
<mxCell id="callout-kc-hostname" value="⚠️ <b>핵심 함정 #1 — KC_HOSTNAME</b> Browser는 EC2 public hostname 으로 Keycloak 호출 → JWT 의 iss claim = 그 hostname. Backend는 localhost:8180 로 JWKS 조회 → iss 비교 시 mismatch → ⓔ 401 발생. ✅ 해결: docker-compose env 에 KC_HOSTNAME=&lt;public-host&gt; KC_HTTP_ENABLED=true 📎 [[raw/branch-notes/develop-keycloak-iss-claim-hostname-mismatch]]" style="rounded=12;whiteSpace=wrap;html=1;fillColor=#FEF2F2;strokeColor=#DC2626;strokeWidth=2;dashed=0;fontSize=10;fontStyle=0;fontFamily=Pretendard,Helvetica;fontColor=#7F1D1D;align=left;verticalAlign=top;spacingLeft=12;spacingTop=8;" vertex="1" parent="1">
|
||||
<mxGeometry x="980" y="740" width="320" height="200" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Callout 2: redirect_uri 함정 -->
|
||||
<mxCell id="callout-redirect-uri" value="⚠️ <b>핵심 함정 #2 — redirect_uri</b> Keycloak client 의 Valid Redirect URIs 등록 시 localhost 만 등록 / browser 는 127.0.0.1 접근 → OIDC redirect 실패. ✅ 해결: 등록과 접근 hostname 1:1 일치 또는 둘 다 등록. 📎 [[raw/branch-notes/develop-keycloak-docker-compose-stack]]" style="rounded=12;whiteSpace=wrap;html=1;fillColor=#FEF2F2;strokeColor=#DC2626;strokeWidth=2;dashed=0;fontSize=10;fontStyle=0;fontFamily=Pretendard,Helvetica;fontColor=#7F1D1D;align=left;verticalAlign=top;spacingLeft=12;spacingTop=8;" vertex="1" parent="1">
|
||||
<mxGeometry x="1340" y="740" width="280" height="200" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Callout 3: SPoF -->
|
||||
<mxCell id="callout-spof" value="⚠️ <b>SPoF (단일 실패 지점)</b> EC2 1대에 모든 컴포넌트 동거 → EC2 다운 = 전체 시스템 정지. P3A 는 학습 / 개발 환경 한정. 운영급은 P2A (cluster-internal) + Keycloak HA cluster 검토. 📎 [[raw/project-notes/keycloak-patterns-overview]] §10 Phase" style="rounded=12;whiteSpace=wrap;html=1;fillColor=#FEF2F2;strokeColor=#DC2626;strokeWidth=2;dashed=0;fontSize=10;fontStyle=0;fontFamily=Pretendard,Helvetica;fontColor=#7F1D1D;align=left;verticalAlign=top;spacingLeft=12;spacingTop=8;" vertex="1" parent="1">
|
||||
<mxGeometry x="1660" y="740" width="280" height="200" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ================= LEGEND ================= -->
|
||||
<mxCell id="grp-legend" value="📖 Legend" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#FFFFFF;strokeColor=#D0D7DE;strokeWidth=1;dashed=0;fontSize=12;fontStyle=1;fontFamily=Pretendard,Helvetica;fontColor=#24292F;align=left;verticalAlign=top;spacingLeft=12;spacingTop=8;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="1010" width="1920" height="160" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Legend: Boundaries -->
|
||||
<mxCell id="leg-title-bnd" value="경계 (Boundaries)" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=11;fontStyle=1;fontFamily=Pretendard,Helvetica;fontColor=#374151;" vertex="1" parent="1">
|
||||
<mxGeometry x="60" y="1040" width="200" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="leg-tb" value="Trust Boundary (파란 실선)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#FBFCFD;strokeColor=#1F6FEB;strokeWidth=2;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#1F6FEB;" vertex="1" parent="1">
|
||||
<mxGeometry x="60" y="1065" width="200" height="22" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg-net-pub" value="Public Internet (회색 점선)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#F6F8FA;strokeColor=#D0D7DE;dashed=1;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#6B7280;" vertex="1" parent="1">
|
||||
<mxGeometry x="60" y="1090" width="200" height="22" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg-net-edge" value="Public edge zone (노란 점선)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#FEF9C3;strokeColor=#EAB308;dashed=1;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#854D0E;" vertex="1" parent="1">
|
||||
<mxGeometry x="60" y="1115" width="200" height="22" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg-warn" value="Critical / Warning (빨간 점선)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#FEF2F2;strokeColor=#DC2626;dashed=1;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#7F1D1D;" vertex="1" parent="1">
|
||||
<mxGeometry x="60" y="1140" width="200" height="22" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Legend: Component colors -->
|
||||
<mxCell id="leg-title-cmp" value="컴포넌트 (§A.7 색상 시맨틱)" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=11;fontStyle=1;fontFamily=Pretendard,Helvetica;fontColor=#374151;" vertex="1" parent="1">
|
||||
<mxGeometry x="290" y="1040" width="200" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="leg-user" value="User / 외부 (흰색)" style="rounded=8;whiteSpace=wrap;html=1;fillColor=#FFFFFF;strokeColor=#57606A;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#24292F;" vertex="1" parent="1">
|
||||
<mxGeometry x="290" y="1065" width="190" height="22" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg-proxy" value="Edge / Proxy / GW (주황)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#FFF7ED;strokeColor=#FB923C;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#9A3412;" vertex="1" parent="1">
|
||||
<mxGeometry x="290" y="1090" width="190" height="22" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg-backend" value="App / Backend (초록)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#D4EDDA;strokeColor=#155724;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#155724;" vertex="1" parent="1">
|
||||
<mxGeometry x="290" y="1115" width="190" height="22" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg-auth" value="Auth / Identity (남색)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#E0E7FF;strokeColor=#3730A3;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#3730A3;" vertex="1" parent="1">
|
||||
<mxGeometry x="290" y="1140" width="190" height="22" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="leg-db" value="Data store (빨강)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#F8D7DA;strokeColor=#842029;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#842029;" vertex="1" parent="1">
|
||||
<mxGeometry x="490" y="1065" width="190" height="22" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg-external" value="External / 3rd-party (회색 점선)" style="rounded=8;whiteSpace=wrap;html=1;fillColor=#F6F8FA;strokeColor=#D0D7DE;dashed=1;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#6B7280;" vertex="1" parent="1">
|
||||
<mxGeometry x="490" y="1090" width="190" height="22" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Legend: Edge semantics -->
|
||||
<mxCell id="leg-title-edge" value="화살표 (Edges)" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=11;fontStyle=1;fontFamily=Pretendard,Helvetica;fontColor=#374151;" vertex="1" parent="1">
|
||||
<mxGeometry x="710" y="1040" width="200" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="leg-edge-hot" value="굵은 실선 (3px) — Critical / Hot path (sync request)" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#1F6FEB;" vertex="1" parent="1">
|
||||
<mxGeometry x="710" y="1065" width="400" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg-edge-thin" value="점선 (1.5px, 회색) — Response / passive return" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#57606A;" vertex="1" parent="1">
|
||||
<mxGeometry x="710" y="1090" width="400" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg-edge-aux" value="얇은 점선 — Auxiliary (JWKS, cache lookup, etc.)" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#155724;" vertex="1" parent="1">
|
||||
<mxGeometry x="710" y="1115" width="400" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg-edge-err" value="빨간 점선 — Error path (Failure mode 표시)" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#DC2626;" vertex="1" parent="1">
|
||||
<mxGeometry x="710" y="1140" width="400" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Legend: Symbols -->
|
||||
<mxCell id="leg-title-sym" value="기호 (Symbols)" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=11;fontStyle=1;fontFamily=Pretendard,Helvetica;fontColor=#374151;" vertex="1" parent="1">
|
||||
<mxGeometry x="1130" y="1040" width="200" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<mxCell id="leg-sym-step" value="① ② ③ ... — Numbered flow (읽기 순서)" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#374151;" vertex="1" parent="1">
|
||||
<mxGeometry x="1130" y="1065" width="400" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg-sym-err" value="❌ ⓔ — Error step (라벨 시작에 표시)" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#DC2626;" vertex="1" parent="1">
|
||||
<mxGeometry x="1130" y="1090" width="400" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg-sym-warn" value="⚠️ — Callout: 비자명한 함정 / 결정 / 위협" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#7F1D1D;" vertex="1" parent="1">
|
||||
<mxGeometry x="1130" y="1115" width="400" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg-sym-src" value="📎 — Source wikilink (사실 출처 / 검증 가능)" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#4B5563;" vertex="1" parent="1">
|
||||
<mxGeometry x="1130" y="1140" width="400" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- Legend: Scale annotation explanation -->
|
||||
<mxCell id="leg-title-scale" value="스케일 표기" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=11;fontStyle=1;fontFamily=Pretendard,Helvetica;fontColor=#374151;" vertex="1" parent="1">
|
||||
<mxGeometry x="1530" y="1040" width="200" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg-scale-qps" value="QPS — Queries per second" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#374151;" vertex="1" parent="1">
|
||||
<mxGeometry x="1530" y="1065" width="400" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg-scale-p99" value="p99 — 99th percentile latency" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontFamily=Pretendard,Helvetica;fontColor=#374151;" vertex="1" parent="1">
|
||||
<mxGeometry x="1530" y="1090" width="400" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="leg-scale-ec2" value="P3A 는 학습/개발 한정 — 운영 SLA / SLO 미정" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontStyle=2;fontFamily=Pretendard,Helvetica;fontColor=#6B7280;" vertex="1" parent="1">
|
||||
<mxGeometry x="1530" y="1115" width="400" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
<!-- ================= FOOTER ================= -->
|
||||
<mxCell id="footer-meta" value="Version 2.0 (v1 → archived/) · 2026-05-26 · @donghyeon · Source: [[raw/branch-notes/develop-keycloak-single-ec2-no-google]] · Standard: [[templates/diagram-standards]] §A" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontStyle=2;fontFamily=Pretendard,Helvetica;fontColor=#6B7280;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="1180" width="1920" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
Reference in New Issue
Block a user