feat: 문서 구조 변경 및 tech-visual 스킬 추가

This commit is contained in:
DongHyeonka
2026-09-04 18:20:00 +09:00
parent 43901f0abf
commit 2efb7ee1f2
683 changed files with 61180 additions and 10479 deletions
@@ -0,0 +1,26 @@
결정(PROJECT_DECISION)의 공개 주소 — V15 마이그레이션 적용 후
출처: hyeonworks-prod / postgres-0 / appdb — 2026-09-04 조회
배경: 게시 시점에 만든 주소가 /projects/{slug}/decisions/{slug} 였는데 그런 라우트가
없어 다른 기록이 건 링크가 404 였다. 계약은 이미 #{slug} 앵커라고 적어 두었다.
주소는 게시 시점에 굳어져 저장되므로 코드만 고치면 기존 행은 깨진 채 남는다 —
그래서 V15 가 이미 게시된 행도 함께 고쳤다.
V15 의 UPDATE:
UPDATE public_resource_projection
SET navigation_path = regexp_replace(navigation_path,
'^(/projects/[^/]+/decisions)/', '\1#')
WHERE resource_type = 'PROJECT_DECISION'
AND navigation_path ~ '^/projects/[^/]+/decisions/';
----------------------------------------------------------------------
[현재] public_resource_projection 의 PROJECT_DECISION 행
/projects/keycloak-patterns/decisions#bff-owns-token-when-browser-must-not ← BFF가 OAuth Token을 관리하는 조건
/projects/keycloak-patterns/decisions#federation-is-not-an-application-pattern ← 외부 IdP와의 연동이라도 별도의 인증 방식이 아니다.
[현재] publication.public_path
/projects/keycloak-patterns/decisions#bff-owns-token-when-browser-must-not
/projects/keycloak-patterns/decisions#federation-is-not-an-application-pattern
[적용된 마이그레이션] flyway_schema_history 의 V14/V15
14 | techlog topic variant | true
15 | techlog decision anchor path | true
@@ -0,0 +1,64 @@
작업본 삭제를 막는 참조 — 실제 사례와 그 해소
출처: hyeonworks-prod / postgres-0 / appdb
진단 시각: 2026-09-04 (아래 [1]~[4])
사후 확인: 2026-09-04, 사용자가 조치한 뒤 (아래 [5])
증상
「DTO 변환 과정에서 발생한 Highlight 컬렉션 N+1」을 지우려는데, 관계를 다 지웠는데도
삭제가 안 된다.
삭제 가드가 보는 다섯 테이블 (DeleteDocumentDraftUseCase → documentReferenced)
SELECT 1 FROM document_relation WHERE target_document_id = :id
UNION ALL SELECT 1 FROM question_document_link WHERE document_id = :id
UNION ALL SELECT 1 FROM project_document_link WHERE document_id = :id ← 여기서 걸림
UNION ALL SELECT 1 FROM topic_featured_document WHERE document_id = :id
UNION ALL SELECT 1 FROM project_decision WHERE source_case_id = :id
다섯 이유가 전부 같은 한 문장으로 나온다:
"another record still links to this one; unlink it first"
「another record」라고 하니 관계를 찾아 지우게 되는데, 정작 막는 것은 record 가 아니라
프로젝트 연결이다. 그리고 프로젝트 연결은 「관계」 편집기가 아니라 문서의 Project 필드다.
======================================================================
[1] 대상 문서 (진단 시점 캡처)
58c2d5d9-5865-4b2c-b1cc-3c5c955c33dc | CASE | collection-nplus1-dto-mapping
| DRAFT | PRIVATE | DTO 변환 과정에서 발생한 Highlight 컬렉션 N+1
→ workflow_status=DRAFT, target_visibility=PRIVATE 이고 publication /
public_resource_projection / public_route 에 행이 없다. 즉 게시 가드는 통과한다.
[2] 이 문서 id 를 참조하는 행 — uuid 컬럼 전수 스캔 (진단 시점 캡처)
NOTICE: project_document_link . document_id => 1 행
NOTICE: document . id => 1 행
NOTICE: case_detail . document_id => 1 행
→ 셋뿐이다. document(자신), case_detail(본문), 그리고 project_document_link 1행.
[3] 막고 있는 그 행의 정체 (진단 시점 캡처)
relation_type=PRIMARY | featured_order=- | project=Liner N + 1문제
→ relation_type 이 PRIMARY 이므로 ProjectLinkStore.setPrimary() 의 DELETE 대상이다.
즉 Studio 에서 Project 를 「미지정」으로 바꿔 저장하면 이 행이 지워진다.
(setPrimary 는 relation_type='PRIMARY' 인 행만 지운다. 만약 이 행이 RELATED 였다면
화면에서 풀 방법이 없어 진짜로 막혔을 것이다 — 그 경우는 아니었다.)
[4] document_relation 은 실제로 비어 있었다 (진단 시점 캡처)
document_relation 참조 행 수: 0
→ 사용자가 관계를 다 지운 것이 맞다. 관계는 문제가 아니었다.
======================================================================
[5] 사후 확인 — 사용자가 Project 를 미지정으로 바꾸고 삭제한 뒤
document 행: 0
project_document_link 행: 0
case_detail 행: 0
→ 삭제가 통과했다. 진단이 맞았다는 것이 이것으로 확인된다 — 프로젝트 연결을 푸는 것이
막힘의 해소였다.
주의: 그래서 이 결함은 이제 실시간으로 재현할 수 없다. [1]~[4] 는 진단 당시의 캡처이고
[5] 만 지금 다시 조회한 값이다.
@@ -0,0 +1,34 @@
축에 실제로 걸린 기록 (record_variant)
출처: hyeonworks-prod / postgres-0 / appdb — 2026-09-04 조회
쿼리: topic_variant JOIN record_variant, 제목은 public_resource_projection 에서
칸: variant.slug | record_kind | 기록 제목
메모: record_variant 는 외래키가 없다 — 기록이 종류마다 다른 테이블에 살아
(kind, id) 쌍으로 가리키기 때문이다(document / open_question / project_decision).
----------------------------------------------------------------------
bff | CASE | Browser Token을 없애면서 BFF에 Session과 CSRF 책임이 생긴 과정
bff | QUESTION | 서버 세션 기반 인증 구조는 다중 인스턴스에서 어떻게 운영할 것인가
bff | QUESTION | BFF의 Session과 OAuth2AuthorizedClient를 어디에 저장할 것인가
bff | QUESTION | Refresh Token Rotation과 다중 Replica 경쟁을 어떻게 처리할 것인가
bff | REFERENCE | BFF 인증 구조 설계 기준
derived-query | CASE | Fetch 타입이 아닌 조회 방식으로 인한 N+1
fetch-join | CASE | Collection Fetch Join으로 N+1을 해결하다 만난 MultiBag예외와 행 폭증 문제
fetch-join-paging | CASE | Collection Fetch Join Pagination의 In-memory Paging
forward-auth | CASE | Forward-Auth에서 Client가 보낸 Identity Header를 신뢰하면 안 되는 이유
forward-auth | QUESTION | Forward-Auth 구조에서 Application Authorization을 어디까지 Edge에 둘 것인가
forward-auth | REFERENCE | Forward-Auth에서 Identity Header를 신뢰하기 위한 조건
mediator | CASE | Refresh Token 관리만 서버로 이전, Access Token은 여전히 Browser에 노출
mediator | QUESTION | 서버 세션 기반 인증 구조는 다중 인스턴스에서 어떻게 운영할 것인가
mediator | QUESTION | Refresh Token Rotation과 다중 Replica 경쟁을 어떻게 처리할 것인가
mediator | REFERENCE | Authorization Code Flow의 Endpoint와 Credential 이동 기준
mediator | REFERENCE | Public Client와 Confidential Client 구분 기준
spa | CASE | SPA에서 토큰을 직접 관리하면서 드러난 Browser Credential 경계
spa | REFERENCE | Authorization Code Flow의 Endpoint와 Credential 이동 기준
spa | REFERENCE | 외부 IdP 연동과 Application 인증 구조의 경계
spa | REFERENCE | Public Client와 Confidential Client 구분 기준
=== 어느 축에도 걸리지 않은 기록 (= 그 주제의 공통 기록) ===
oauth-oidc-auth-boundary | CONCEPT | 외부 IdP Brokering의 동작
oauth-oidc-auth-boundary | PROJECT_DECISION | BFF가 OAuth Token을 관리하는 조건
oauth-oidc-auth-boundary | PROJECT_DECISION | 외부 IdP와의 연동이라도 별도의 인증 방식이 아니다.
oauth-oidc-auth-boundary | REFERENCE | OAuth Token과 Application Session을 구분하는 기준
oauth-oidc-auth-boundary | REFERENCE | OAuth/OIDC 인증 패턴 선택 기준
@@ -0,0 +1,12 @@
주제와 축(topic / topic_variant)의 실제 행
출처: hyeonworks-prod / postgres-0 / appdb — 2026-09-04 조회
쿼리: topic LEFT JOIN topic_variant, display_order 순
칸: topic.slug | topic.variant_label | display_order | variant.slug | variant.title
----------------------------------------------------------------------
jpa-feed-query-performance | 조회 전략 | 1 | derived-query | 파생 쿼리 그대로
jpa-feed-query-performance | 조회 전략 | 2 | fetch-join | 컬렉션 fetch join
jpa-feed-query-performance | 조회 전략 | 3 | fetch-join-paging | fetch join + 페이징
oauth-oidc-auth-boundary | 구조 | 1 | spa | SPA
oauth-oidc-auth-boundary | 구조 | 2 | mediator | Mediator
oauth-oidc-auth-boundary | 구조 | 3 | bff | BFF
oauth-oidc-auth-boundary | 구조 | 4 | forward-auth | Forward-Auth