Skip to content
created by Aha00aAha00a at 2026-05-22
last modified by Aha00aAha00a at 2026-09-26
revision: 58

Kanban 인터프리터는 위키 텍스트를 단일 원본으로 사용한다.

보기 화면에서는 보드 UI로 편집하고, 저장 시 Kanban 블록을 다시 위키 텍스트로 직렬화한다.

1. Example

=== Info
==== 작업 제목 ==== #cardid123
===== Description
카드에 대한 설명을 여러 줄로 작성할 수 있다.
새 댓글이 추가되어도 밀리지 않는다.
===== Property
 * DueDate: [2027-01-01]
 * Assignee: [User:Aha00a]
 * Attachment: [[Attachment(path/to/file.png)]]
===== Activity
 * [User:Aha00a] [2026-05-04]T10:36:17
  * ["#cardid123" #작업 제목] moved - '''Done''' to '''Info'''
=== ToDo
==== 점심 설거지 ==== #cmq4akx08r020y2
===== Property
 * Creator: [User:Aha00a]
 * dateCreated: [2026-06-07]T21:26:27Z
 * ParentCard: [#cmq4ak09d89cnyp 점심에 할일]
===== Activity
 * [User:Aha00a] [2026-06-07]T21:29:15Z
  * ["#cmq4akx08r020y2" #점심 설거지] updated - ParentCard - [#cmq4ak09d89cnyp 점심에 할일]
 * [User:Aha00a] [2026-06-07]T21:26:27Z
  * ["#cmq4akx08r020y2" #점심 설거지] added
=== Pending
==== 점심 식사 ==== #cmq4aksurw4h9b6
===== Property
 * Creator: [User:Aha00a]
 * dateCreated: [2026-06-07]T21:26:22Z
 * ParentCard: [#cmq4ak09d89cnyp 점심에 할일]
===== Activity
 * [User:Aha00a] [2026-06-07]T21:30:17Z
  * ["#cmq4aksurw4h9b6" #점심 식사] moved - '''ToDo''' to '''Pending'''
 * [User:Aha00a] [2026-06-07]T21:29:10Z
  * ["#cmq4aksurw4h9b6" #점심 식사] updated - ParentCard - [#cmq4ak09d89cnyp 점심에 할일]
 * [User:Aha00a] [2026-06-07]T21:26:22Z
  * ["#cmq4aksurw4h9b6" #점심 식사] added
=== Doing
==== 점심에 할일 ==== #cmq4ak09d89cnyp
===== Property
 * Creator: [User:Aha00a]
 * dateCreated: [2026-06-07]T21:25:45Z
 * BlockedBy: [#cmq4aiz5lnvvyen 일어나기]
===== Activity
 * [User:Aha00a] [2026-06-07]T21:29:36Z
  * ["#cmq4ak09d89cnyp" #점심에 할일] moved - '''ToDo''' to '''Doing'''
 * [User:Aha00a] [2026-06-07]T21:29:28Z
  * ["#cmq4ak09d89cnyp" #점심에 할일] updated - BlockedBy - [#cmq4aiz5lnvvyen 일어나기]
 * [User:Aha00a] [2026-06-07]T21:25:54Z
  * ["#cmq4ak09d89cnyp" #점심에 할일] renamed - '''점심에할일'''
 * [User:Aha00a] [2026-06-07]T21:25:45Z
  * ["#cmq4ak09d89cnyp" #점심에할일] added
==== 점심밥 차리기 ==== #cmq4akmkpy6chrx
===== Property
 * Creator: [User:Aha00a]
 * dateCreated: [2026-06-07]T21:26:14Z
 * ParentCard: [#cmq4ak09d89cnyp 점심에 할일]
===== Activity
 * [User:Aha00a] [2026-06-07]T21:29:57Z
  * ["#cmq4akmkpy6chrx" #점심밥 차리기] moved - '''ToDo''' to '''Doing'''
 * [User:Aha00a] [2026-06-07]T21:29:04Z
  * ["#cmq4akmkpy6chrx" #점심밥 차리기] updated - ParentCard - [#cmq4ak09d89cnyp 점심에 할일]
 * [User:Aha00a] [2026-06-07]T21:26:14Z
  * ["#cmq4akmkpy6chrx" #점심밥 차리기] added
=== Done
==== 일어나기 ==== #cmq4aiz5lnvvyen
===== Property
 * Creator: [User:Aha00a]
 * dateCreated: [2026-06-07]T21:24:57Z
===== Activity
 * [User:Aha00a] [2026-06-07]T21:28:07Z
  * ["#cmq4aiz5lnvvyen" #일어나기] moved - '''ToDo''' to '''Done'''
 * [User:Aha00a] [2026-06-07]T21:24:57Z
  * ["#cmq4aiz5lnvvyen" #일어나기] added
==== 아침에 할일 ==== #cmq4ajpa7tig43b
===== Property
 * Creator: [User:Aha00a]
 * dateCreated: [2026-06-07]T21:25:30Z
 * BlockedBy: [#cmq4aiz5lnvvyen 일어나기]
===== Activity
 * [User:Aha00a] [2026-06-07]T21:28:38Z
  * ["#cmq4ajpa7tig43b" #아침에 할일] moved - '''Doing''' to '''Done'''
 * [User:Aha00a] [2026-06-07]T21:28:15Z
  * ["#cmq4ajpa7tig43b" #아침에 할일] updated - BlockedBy - [#cmq4aiz5lnvvyen 일어나기]
 * [User:Aha00a] [2026-06-07]T21:27:31Z
  * ["#cmq4ajpa7tig43b" #아침에 할일] moved - '''ToDo''' to '''Doing'''
 * [User:Aha00a] [2026-06-07]T21:25:50Z
  * ["#cmq4ajpa7tig43b" #아침에 할일] renamed - '''아침에할일'''
 * [User:Aha00a] [2026-06-07]T21:25:30Z
  * ["#cmq4ajpa7tig43b" #아침에할일] added
==== 아침밥 차리기 ==== #cmq4aj3iyvga6ec
===== Property
 * Creator: [User:Aha00a]
 * dateCreated: [2026-06-07]T21:25:02Z
 * ParentCard: [#cmq4ajpa7tig43b 아침에 할일]
===== Activity
 * [User:Aha00a] [2026-06-07]T21:28:43Z
  * ["#cmq4aj3iyvga6ec" #아침밥 차리기] moved - '''Doing''' to '''Done'''
 * [User:Aha00a] [2026-06-07]T21:27:39Z
  * ["#cmq4aj3iyvga6ec" #아침밥 차리기] moved - '''ToDo''' to '''Doing'''
 * [User:Aha00a] [2026-06-07]T21:26:56Z
  * ["#cmq4aj3iyvga6ec" #아침밥 차리기] updated - ParentCard - [#cmq4ajpa7tig43b 아침에 할일]
 * [User:Aha00a] [2026-06-07]T21:26:34Z
  * ["#cmq4aj3iyvga6ec" #아침밥 차리기] renamed - '''아침밥차리기'''
 * [User:Aha00a] [2026-06-07]T21:25:08Z
  * ["#cmq4aj3iyvga6ec" #아침밥차리기] renamed - '''밥차리기'''
 * [User:Aha00a] [2026-06-07]T21:25:02Z
  * ["#cmq4aj3iyvga6ec" #밥차리기] added
==== 아침 식사 ==== #cmq4ajesg43bp1g
===== Property
 * Creator: [User:Aha00a]
 * dateCreated: [2026-06-07]T21:25:17Z
 * ParentCard: [#cmq4ajpa7tig43b 아침에 할일]
===== Activity
 * [User:Aha00a] [2026-06-07]T21:28:45Z
  * ["#cmq4ajesg43bp1g" #아침 식사] moved - '''Doing''' to '''Done'''
 * [User:Aha00a] [2026-06-07]T21:27:42Z
  * ["#cmq4ajesg43bp1g" #아침 식사] moved - '''ToDo''' to '''Doing'''
 * [User:Aha00a] [2026-06-07]T21:27:13Z
  * ["#cmq4ajesg43bp1g" #아침 식사] updated - ParentCard - [#cmq4ajpa7tig43b 아침에 할일]
 * [User:Aha00a] [2026-06-07]T21:26:45Z
  * ["#cmq4ajesg43bp1g" #아침 식사] renamed - '''아침식사'''
 * [User:Aha00a] [2026-06-07]T21:25:17Z
  * ["#cmq4ajesg43bp1g" #아침식사] added
==== 아침 설거지 ==== #cmq4ajlewxrxne2
===== Property
 * Creator: [User:Aha00a]
 * dateCreated: [2026-06-07]T21:25:25Z
 * ParentCard: [#cmq4ajpa7tig43b 아침에 할일]
===== Activity
 * [User:Aha00a] [2026-06-07]T21:28:48Z
  * ["#cmq4ajlewxrxne2" #아침 설거지] moved - '''Doing''' to '''Done'''
 * [User:Aha00a] [2026-06-07]T21:27:44Z
  * ["#cmq4ajlewxrxne2" #아침 설거지] moved - '''ToDo''' to '''Doing'''
 * [User:Aha00a] [2026-06-07]T21:27:24Z
  * ["#cmq4ajlewxrxne2" #아침 설거지] updated - ParentCard - [#cmq4ajpa7tig43b 아침에 할일]
 * [User:Aha00a] [2026-06-07]T21:26:39Z
  * ["#cmq4ajlewxrxne2" #아침 설거지] renamed - '''아침설거지'''
 * [User:Aha00a] [2026-06-07]T21:25:25Z
  * ["#cmq4ajlewxrxne2" #아침설거지] added

2. Syntax

  • === 리스트명: 컬럼
  • ==== 카드 제목 ==== #cardId: 카드
  • ===== Description: 카드 설명 (선택, 여러 줄, Activity에 밀리지 않음). 다음 ===== 줄이나 === 리스트 줄까지가 설명이다. 설명 바로 뒤에 ==== 카드 줄을 쓰면 그 카드도 설명으로 읽히므로, 손으로 쓸 때는 설명 뒤에 ===== Property를 둔다. UI로 저장하면 늘 그렇게 쓴다.
  • ===== Property: 카드 속성
  • ===== Activity: 활동 로그

3. 규칙

  • 카드 ID(#...)는 생성 후 변경하지 않는다.
  • 카드/리스트 순서는 입력 순서를 그대로 유지한다.
  • Kanban 블록 외부 텍스트는 저장 시 변경하지 않는다. 저장은 줄 번호로 자리를 찾으므로, 페이지를 연 뒤 첫 저장은 먼저 페이지 원문을 읽어 블록의 자리를 원문에서 잡는다. 화면이 그려진 줄 번호는 맨 위 #! 줄을 뺀 번호라서, 2026-09-15 전에는 #!var 같은 줄이 있는 페이지에서 원격 업데이트를 한 번 받기 전까지 저장이 그 줄 수만큼 위에 들어갔다.
  • 저장 전 현재 리비전이 확인되지 않으면 최신 리비전을 1회 조회한 뒤 저장한다.
  • 저장마다 reCAPTCHA token 을 새로 받아 함께 보낸다(409 재시도 포함). 받지 못하면 저장하지 않고 알린다. 규칙은 Dev Editor 저장. 2026-09-13 전에는 늘 빈 token 을 보냈다.
  • 409 Conflict 가 오면 최신 원문을 다시 읽고, 이 보드가 아직 저장하지 못한 변경을 서버의 보드 위에 다시 얹어(목록의 추가·삭제·이름·순서, 카드의 추가·삭제·내용·이동) 원문 기준 줄 범위로 다시 보낸다. 최대 3회. 양쪽이 같은 것을 바꿨으면 원격 업데이트처럼 서버 쪽이 남고, 그랬다는 것을 토스트로 알린다. 다시 얹을 것이 남지 않으면 보내지 않는다. 모두 실패하면 화면 하단에 non-blocking 토스트를 표시하며 페이지는 새로고침하지 않는다. 2026-09-15 전에는 리비전 번호만 다시 받아 같은 텍스트를 같은 줄 범위에 보냈고, 그 사이 다른 세션이 보드에 한 변경이 사라졌다.
  • 다른 탭/사용자의 저장을 받아 보드를 자동 갱신한다 — 조건과 한계는 아래 실시간 동기화.
  • 카드 글(제목·Description·댓글)은 AhaMark 로 그린다. 백틱으로 감싼 코드도 보드 안에서 코드로 보인다. 2026-09-25 전에는 백틱이 페이지를 그릴 때 이미 코드 태그로 바뀌어 버려서, 보드가 그것을 태그 없이 읽고 다음 저장에 백틱을 지운 채 문서를 다시 썼다 — 쓴 사람이 지우지 않은 글자가 사라지는 셈이었다. 까닭은 Interpreter 구간 안의 백틱.

4. 실시간 동기화

다른 탭 또는 다른 사용자가 같은 Kanban 페이지를 저장하면 현재 탭의 보드가 자동으로 갱신된다. 2026-09-15 부터는 저장이 어느 인스턴스를 거쳤든 온다 — page.updated 를 인스턴스 사이로 전달한다(Dev WebSocket). 그 전에는 같은 인스턴스에 붙은 사람에게만 갔다.

  • 다른 세션의 저장을 WebSocket page.updated 이벤트로 감지한다.
  • 최신 원문을 다시 읽고 Kanban 블록을 파싱한 뒤 현재 보드에 병합한다.
  • 저장 중인 변경이 있으면 저장 완료 후 원격 업데이트를 다시 적용한다.
  • 병합 상세와 구현 노트는 Dev Kanban Realtime에 둔다.

4.1. 동작 조건

  • WebSocket이 연결된 상태에서 다른 세션의 저장이 완료되면 page.updated 이벤트를 수신한다.
  • 이벤트 수신 후 500ms debounce 처리 후 GET /w/{page}?action=raw로 최신 원문을 가져온다.
  • 가져온 원문에서 칸반 블록 위치를 재계산하여 파싱한다.

4.2. 병합 전략 (3-way merge)

  • base — 이 탭과 서버가 마지막으로 같았던 칸반 상태. 처음 읽을 때, 저장에 성공할 때, 원격 업데이트를 적용할 때(방금 받은 서버 상태로) 다시 잡는다.
  • local — 현재 로컬 in-memory 상태 (저장 중인 변경 포함)
  • server — 방금 가져온 서버 상태
로컬만 변경된 카드    → 로컬 버전 유지
서버만 변경된 카드    → 서버 버전 적용
양쪽 모두 변경된 카드 → 서버 우선 + 카드 표면에 주황 conflict 배지
로컬에만 추가된 카드  → 서버 상태에 병합하여 유지
모달에서 열린 카드   → 편집 중이 아닌 칸(제목·설명·속성)은 서버 값으로 바로 바꾸고, 편집 중인 칸을 서버가 바꿨으면 그 칸은 두고 모달 상단 배너 표시

4.3. pending 뮤테이션 처리

  • 저장 중인 뮤테이션이 있으면 업데이트를 즉시 적용하지 않고 뮤테이션 완료 후 재적용한다.

4.4. 부수 효과

  • 원격 업데이트를 적용하면 interpreterStartLine과 줄 범위를 서버 원문 기준으로, 현재 리비전을 받은 리비전으로, base를 방금 받은 서버 상태로 다시 잡는다.
    • * 리비전이 맞으니 다음 저장이 409를 받지 않고, 줄 번호가 맞으니 저장이 보드 밖 줄을 덮지 않는다.

5. 속성

UI가 쓰는 속성:

  • Creator (1) — 카드를 만들 때 쓴다.
  • dateCreated (1) — 카드를 만들 때 쓴다.
  • DueDate (1)
  • Assignee (n)
  • BlockedBy (n)
  • ParentCard (n)
  • RelatedCard (n)
  • Attachment (n)

(1)·(n)은 UI가 쓰는 개수일 뿐 강제하지 않는다. 목록에 없는 키도 그대로 읽고 저장하며 모달에 표시한다.

5.1. 카드 관계

같은 InterpreterKanban 블록 안의 카드 관계를 Property로 표현한다.

관계 대상은 페이지명을 저장하지 않고 카드 ID hash만 저장한다.

===== Property
 * BlockedBy
  * [#card-api-spec API spec 확정]
 * ParentCard: [#card-login-epic Login Epic]
 * RelatedCard
  * [#card-design-review Design review]
  • 저장값은 [#cardId] 또는 [#cardId label] 형식을 사용한다.
  • 실제 식별자는 cardId이고, label은 원문 가독성을 위한 보조 정보로만 사용한다.
  • 관계 해석 범위는 같은 InterpreterKanban 블록 내부로 제한한다.
  • BlockedBy, ParentCard, RelatedCard를 직접 저장한다.
  • Blocks, SubCards는 저장하지 않고 BlockedBy, ParentCard를 역방향으로 스캔해서 계산 표시한다.

5.2. 카드 표면 관계 배지

보드 뷰의 카드 표면에 관계가 있는 경우 관계 배지를 표시한다.

  • 직접 저장 관계: BlockedBy, ParentCard, RelatedCard
  • 파생 계산 관계: Blocks, SubCards
  • 관계가 없는 카드는 배지 영역을 표시하지 않는다.
  • 관계가 2개 이상인 경우 배지에 개수를 함께 표시한다.
  • 배지와 카드 하단 메타(담당자·통계)는 kanban-card-footer 안에 묶여, 내용이 있을 때만 구분선과 함께 표시된다.
  • 내용(관계·담당자·Activity·첨부·기한)이 없으면 footer에 여백과 구분선이 붙지 않는다. Activity는 시스템 기록(added·moved 등)도 세므로, UI에서 만든 카드는 처음부터 댓글 수 1과 함께 footer가 보인다.

5.3. 관계 색상 체계

카드 표면 배지와 카드 상세 모달의 관계 Property 행에 동일한 색상 체계를 적용한다.

관계 타입

색상

BlockedBy

빨간색 (error)

Blocks (파생)

주황색 (warning)

ParentCard

파란색 (primary)

SubCards (파생)

파란색 (primary)

RelatedCard

회색 (muted)

카드 상세 모달에서는 관계 Property 행의 라벨과 카드 칩 테두리·배경에 위 색상을 적용한다.

5.4. 카드 상세 모달 관계 표시

  • 관계 Property별로 현재 연결된 카드를 카드 칩으로 표시한다.
  • 칩에는 최신 카드 제목과 카드 ID(# 포함 전체)를 표시한다.
  • 칩 클릭 시 openCardDetailById(cardId)로 대상 카드 모달을 연다.
  • 연결 추가는 카드 선택 목록으로 한다. 리스트별로 묶여 있고, 자기 자신과 이미 연결한 카드는 빠진다. ID를 직접 입력하는 칸은 없다(2026-06-10에 없앴다).
  • 원문에 있는 값이 보드에 없는 카드를 가리키면, 누를 수 없는 칩으로 표시한다.
  • 현재 카드 자신을 관계 대상으로 선택하지 못하게 한다.
  • 관계 삭제 버튼을 제공하고, 삭제 시 card:property:update로 저장한다.

5.5. Assignee

Assignee는 기존 Property 형식을 유지하되 카드 상세 모달에서 직접 편집할 수 있게 한다.

===== Property
 * Assignee
  * [User:Aha00a]
  * [User:Alice]
  • Assignee는 복수 값을 허용한다.
  • 입력값은 User:Aha00a, [User:Aha00a], Aha00a를 받아 [User:Aha00a]로 정규화한다.
  • 현재 Assignee는 사용자 칩으로 표시한다.
  • Assignee는 선택 목록(현재 사용자와 같은 보드에 등장한 Assignee)으로만 고른다. 목록에 없는 이름은 위키 텍스트를 직접 고친다.
  • Assignee 변경은 card:property:update로 저장하고 Activity에 기록한다.
  • 카드 목록에는 Assignee 프로필 이미지 아바타를 표시하고, 이름은 mouseover tooltip으로 제공한다.

6. 모바일 UX

뷰포트 너비가 767px 이하이면 자동으로 모바일 모드로 전환된다.

window.matchMedia('(max-width: 767px)') 이벤트를 구독하여, 리사이즈 시에도 탭 바·단일 컬럼 보기·카드 드래그는 바로 바뀐다. 스와이프와 리스트 드래그는 페이지를 열 때의 너비로 정해진다.

6.1. 탭 바 + 단일 컬럼 뷰

  • 보드 상단에 컬럼 이름을 탭으로 나열한다.
  • 활성 탭만 해당 컬럼을 표시하고, 나머지 컬럼은 숨긴다.
  • 탭에는 해당 컬럼의 카드 수를 배지로 표시한다.
  • 탭 바 우측 끝의 + 버튼으로 리스트를 추가할 수 있다.

6.2. 컬럼 이동

  • 탭을 탭하여 컬럼을 전환한다.
  • 컬럼 영역을 좌우로 스와이프(50px 이상)하여 인접 컬럼으로 이동한다.

6.3. 드래그 비활성화

  • 모바일에서는 카드/리스트 드래그가 비활성화된다.
  • 카드 이동은 카드 상세 모달의 "List" 셀렉트로 대신한다.

6.4. List 이동 (카드 상세 모달)

  • 카드 상세 모달 Properties 영역 최상단에 "List" 셀렉트를 표시한다.
  • 현재 카드가 속한 컬럼이 기본 선택된다.
  • 다른 컬럼을 선택하면 즉시 이동하고 모달을 닫는다.
  • Activity에 이동 기록이 남으며 card:move로 저장된다.
  • 컬럼이 하나뿐이거나 read-only인 경우 표시하지 않는다.
  • 데스크톱에서도 동일하게 표시된다.

6.5. 바텀시트 모달

  • 카드 상세 모달이 화면 하단에서 올라오는 바텀시트 형태로 표시된다.
  • 높이 최대 90dvh, 상단 모서리 border-radius 16px.

7. 카드 모달

  • 카드 열기 시 URL hash를 #<cardId>로 설정한다.
  • 카드 닫기 시 해당 hash를 제거한다.
  • #<cardId>로 직접 진입하면 해당 카드 모달을 자동으로 열고 보드 위치로 이동한다.
  • 존재하지 않는 #<cardId>는 오류 없이 일반 보드 화면을 유지한다.
  • Property 영역에서 DueDate, Assignee, 카드 관계를 직접 편집할 수 있다.
  • 카드 관계 칩 클릭 시 대상 카드 모달을 연다.
  • 카드 목록에는 Assignee 프로필 이미지 아바타가 표시된다.

8. 리비전 코멘트

형식: Kanban - [주체] 동사과거형 - Detail

  • 카드 이벤트: 카드링크가 주체. ex) Kanban - ["Kanban#cardId" Kanban#할일] moved - '''ToDo''' to '''Done'''
  • 리스트 이벤트: 리스트명(bold)이 주체. ex) Kanban - '''ToDo''' added
  • 지원 액션:
    • list:add, list:rename, list:move, list:delete
    • card:add, card:rename, card:move, card:delete
    • card:comment:add, card:property:update, card:description:update
  • card:comment:add는 코멘트 첫 줄 기준 최대 80자만 기록한다.
  • card:rename의 Detail은 이전 제목(fromTitle). 카드링크 자체가 새 제목을 담고 있으므로 redundancy 없이 변경 내역을 표현한다.
  • card:delete는 카드가 삭제되므로 Activity에는 별도로 기록하지 않는다.
  • list:move와, 같은 리스트 안의 card:move는 minor edit로 저장한다(MacroRecentChanges).

9. Read Only

InterpreterKanban은 일반 위키 페이지와 같은 쓰기 권한 정책을 따른다.

페이지 렌더링 시 계산된 isWritable 값은 .wikiContent의 data-wiki-writable 속성으로 전달되고, Kanban UI는 이 값을 기준으로 root별 read-only 모드를 결정한다.

옛 리비전을 보는 화면(?revision=N 이 최신이 아닐 때)은 쓰기 권한이 있어도 read-only 이고, 실시간 동기화도 하지 않는다 — Dev Page 옛 리비전 화면.

쓰기 권한이 없는 사용자는 Kanban 보드를 조회할 수 있지만, 내용을 변경하는 UI는 노출되지 않는다.

  • 보드, 컬럼, 카드, 속성, Activity, 첨부 이미지는 조회할 수 있다.
  • 카드 상세 모달, 이미지 라이트박스, #<cardId> hash navigation은 동작한다.
  • 보드 배경 드래그를 이용한 가로 스크롤은 read-only에서도 동작한다.
  • list/card 추가, rename, delete, drag sort는 비활성화된다.
  • 댓글 입력, DueDate 수정, 첨부 삭제, 파일 drop 업로드는 비활성화된다.
  • 보드에는 read-only 상태가 표시된다.

클라이언트의 read-only 처리는 UX를 위한 방어선이다.

실제 보안 경계는 서버 저장 로직의 /w/{page} POST 쓰기 권한 검사이며, 쓰기 권한이 없으면 403 Permission denied로 차단된다.

한 페이지에 여러 Kanban interpreter가 있을 수 있으므로 read-only 상태와 mutation 방어는 interpreter root별로 처리한다.

signed read URL로 페이지를 조회하는 경우에도 쓰기 권한이 없으면 read-only로 동작해야 한다.

10. See Also

10.2. Similar Pages

Similar pages by cosine similarity. Words after page name are term frequency.

10.3. Adjacent Pages

Control
≤ 32
all
1.0x
1.0x
80
-120
ON
Metrics
Nodes(visible/total)0/0
Links(visible/total)0/0
Avg degree0.00
Depth coverage0
Queue(fetch/graph)0 / 0
Zoom(scale)1.00x
Ctrl/⌘ + Scroll: Zoom
Root 1-hop 2-hop+