본문으로 건너뛰기

ADR-0013: 빌드 시점에 생성한 문서 HTML 에 한해 sanitizer 우회를 허용한다

상태

Accepted

날짜

2026-08-11

맥락

문서 사이트가 docs/ 의 마크다운을 HTML 로 변환해 표시합니다. 변환은 빌드 전에 한 번 수행되며 결과는 소스 파일로 생성됩니다.

문서마다 목차를 제공하려면 본문의 각 절 제목이 프래그먼트 대상이 되어야 합니다. 변환기가 <h2 id="2-골격과-표면"> 형태로 식별자를 부여하고, 목차 링크가 #2-골격과-표면 으로 그 위치를 가리키는 구조입니다.

실측 결과 Angular 의 sanitizer 가 [innerHTML] 로 삽입된 요소에서 id 속성을 제거합니다. 목차 링크는 정상 생성되지만 본문에 대상이 없어 앵커 이동이 동작하지 않습니다. 오류가 발생하지 않고 클릭해도 아무 일이 일어나지 않으므로 눈으로 확인하기 전까지 드러나지 않습니다.

보안 3.1절bypassSecurityTrust* 호출을 금지하며, 서식 있는 텍스트는 컴포넌트로 렌더링하고 HTML 문자열을 주입하지 말라고 규정합니다. 그 금지의 근거는 서버가 신뢰 가능해도 서버가 받은 값은 사용자 입력이라는 점입니다.

결정

빌드 시점에 생성한 문서 HTML 에 한해 bypassSecurityTrustHtml 을 허용합니다.

적용 조건은 다음 셋을 모두 충족하는 경우로 한정합니다.

조건 내용
출처 입력이 저장소에 커밋된 마크다운이며 외부에서 오지 않습니다
시점 변환이 빌드 전에 끝나며 런타임에 다시 수행되지 않습니다
혼입 없음 생성된 문자열에 요청 파라미터·서버 응답·사용자 입력이 결합되지 않습니다

적용 범위는 pages/docs/docs-article 한 슬라이스입니다. 다른 화면에서 같은 우회가 필요해지면 본 ADR 을 근거로 삼지 않고 별도로 판단합니다.

ESLint 의 no-restricted-properties 는 그대로 두고 해당 호출에만 지역 예외 주석을 답니다. 주석에 본 ADR 번호를 적어 사유가 코드에서 추적되게 합니다.

검토한 대안

하이드레이션 이후 JavaScript 로 식별자 부여

구분 내용
장점 sanitizer 를 우회하지 않습니다. 목차 데이터가 이미 있으므로 순서대로 대응시킬 수 있습니다
단점 서버가 생성한 HTML 에는 식별자가 없습니다. 링크로 직접 진입하면 브라우저가 앵커를 찾지 못한 뒤 뒤늦게 부여되므로 이동이 일어나지 않습니다
기각 사유 본 사이트는 전 경로가 정적 생성이며 문서 링크가 절 단위로 공유되는 것이 정상 사용입니다. 첫 진입에서 실패하는 방식은 목차를 두는 목적을 없앱니다

본문을 구조화된 블록 배열로 생성하고 컴포넌트가 렌더링

구분 내용
장점 HTML 문자열 주입이 사라져 보안 3.1절을 그대로 지킵니다. 블록별로 컴포넌트를 교체할 수 있습니다
단점 표·코드 블록·인용·중첩 목록·인라인 서식을 모두 블록 타입으로 정의하고 각각의 렌더링 컴포넌트를 만들어야 합니다. 마크다운 렌더러를 직접 소유하게 됩니다
기각 사유 얻는 것이 이미 신뢰 가능한 입력에 대한 방어인 반면, 치르는 비용은 렌더러 유지보수입니다. 문서 문법이 늘어날 때마다 컴포넌트를 추가하게 됩니다

목차를 제공하지 않음

구분 내용
장점 문제 자체가 사라집니다
기각 사유 참조 문서 다수가 절이 열 개를 넘습니다. 목차 없이는 원하는 규칙에 도달하는 수단이 브라우저 검색뿐입니다

결과

목차 앵커가 서버 렌더 결과에서부터 동작하며, 절 단위 링크 공유가 가능해집니다. 변환 파이프라인은 마크다운을 HTML 로 바꾸는 한 단계로 유지됩니다.

감수하는 사항은 다음과 같습니다.

  • 이 경로에 런타임 데이터가 섞이면 XSS 가 됩니다. 지금은 입력이 빌드 산출물뿐이지만, 나중에 검색어 강조나 사용자 주석 같은 기능이 같은 문자열에 결합되면 방어가 사라집니다.
  • 자동 강제 수단이 없습니다. ESLint 는 호출 위치를 지적할 뿐 입력의 출처를 판정하지 못합니다. 위 세 조건의 충족 여부는 코드 리뷰에서 확인합니다.
  • bypassSecurityTrust* 의 첫 사례가 생깁니다. 선례가 확대 적용되지 않도록 적용 범위를 슬라이스 하나로 명시했습니다.

개정

2026-08-12 — 다이어그램 렌더로 런타임 주입 경로 추가

ADR-0015 로 Mermaid 다이어그램을 화면에서 그리게 되면서, 같은 슬라이스에 런타임 DOM 주입이 하나 생겼습니다. mermaid 가 만든 SVG 문자열을 innerHTML 로 넣는 경로이며 Angular sanitizer 를 거치지 않습니다.

본 ADR 의 세 조건 중 시점 조건이 문면상 어긋나므로 적용 조건을 다음과 같이 보완합니다. 결론은 바뀌지 않으므로 신규 ADR 을 발행하지 않습니다.

조건 보완 내용
출처 그대로 적용합니다. 다이어그램의 입력은 저장소에 커밋된 마크다운의 코드 블록입니다
시점 변환이 런타임에 일어나도 입력이 빌드 산출물뿐이면 허용합니다
혼입 없음 그대로 적용합니다. 원본 문자열에 요청 파라미터나 서버 응답이 결합되지 않습니다

mermaid 의 securityLevel 은 기본값 strict 를 유지하며 내장 dompurify 가 스크립트와 이벤트 핸들러를 제거합니다. 이 값을 낮추는 변경은 본 조건의 전제를 무너뜨리므로 금지합니다.

© 2026 dev.goraebap