확장 기능 만들기
더 많은 작업
이 문서는 Wikven 빌드 안에서 동작해야 하는 MediaWiki 확장 기능이나 스킨을 만드는 사람을 위한 것입니다. 살아 있는 위키용으로 만드는 방법은 안다고 가정하고, 여기서는 정적 내보내기가 달라지게 하는 부분만 다룹니다. Wikven 자체가 어떻게 만들어지는지는 별개의 이야기이며 개발에서 설명합니다.
빌드란 무엇인가
Wikven 빌드는 명령줄에서 MediaWiki를 한 번 부팅해 모든 문서를 파일로 렌더링한 뒤 종료합니다. 그다음 독자에게는 PHP를 실행하지 않는 호스트가 평범한 파일을 내어 줍니다.
아래 내용 전체를 관통하는 두 가지 결과가 있습니다:
- 여러분의 코드는 볼 때가 아니라 빌드할 때 실행됩니다. 사이트가 배포된 뒤에는
load.php도, 액션 API도, 데이터베이스도 없습니다. 평소 요청에 응답하며 하던 일은 빌드 도중에 끝나 있거나, 서버가 필요 없는 정적 자바스크립트로 실려야 합니다. - 빌드는 요청이 아닙니다.
MW_ENTRY_POINT는cli이고, 읽을 만한WebRequest가 없으며, 사용자나 세션이나 URL에서 온 제목을 가정하는 훅은 그것을 찾지 못합니다. Wikven 자신의 훅들이MW_ENTRY_POINT === 'cli'로 가드하는 이유입니다.
확장 기능을 빌드에 넣기
사이트는 필요한 것을 .wikven.yml에 적습니다. 이미지에 함께 들어 있는 확장 기능은 이름만 적으면 되고, 그 밖의 것은 WikvenRepositories 아래 선언한 출처에서 빌드 시점에 가져옵니다 — 타르볼, Git 저장소, 또는 Composer 패키지.
extensions:
- MyExtension
config:
MyExtensionGreeting: Hello
WikvenRepositories:
MyExtension:
repository: https://github.com/example/mediawiki-extensions-MyExtension.git
reference: v1.2.3
설정에는 Wikven의 지원이 따로 필요하지 않습니다. 사이트의 config 맵은 MediaWiki 자신의 설정 로더를 통해 적용되므로, extension.json의 config에 선언한 키라면 무엇이든 거기서 지정할 수 있고, 이미 읽고 있던 그 전역 변수로 도착합니다.
브랜치를 따라가지 말고 참조를 고정하세요. 사이트의 특정 리비전을 빌드하면 그 리비전이 검증된 코드를 가져와야 하며, 문서 사이트가 가져오는 모든 구성 요소를 고정해 두는 것도 그 때문입니다.
로컬 URL: 반드시 읽어야 할 계약
확장 기능 작성자가 걸려 넘어지는 지점이 바로 여기입니다. 살아 있는 위키에서는 맞는 코드가 오직 여기서만 틀리기 때문입니다.
Wikven은 문서 URL을 문서 기준 상대 경로로 만듭니다. Title::getLocalURL()은 ./Page.html을 답하는데, 제목에 슬래시가 있는 문서는 실제 디렉터리로 내보내지므로 한 단계 아래 문서가 같은 뜻이 되려면 ../Page.html이어야 합니다. maintenance/rename.php가 그 보정을 문서마다 자신의 깊이에 따라 적용합니다.
보정되는 곳은 두 군데입니다:
- 문서의 마크업 —
href,src,srcset, 그리고 CSSurl(); - 문서 자신의 자바스크립트 설정인
RLCONF객체. 설정 변수로 내보낸 로컬 URL이href와 같은 방식으로 보정됩니다.
보정할 수 없는 세 번째가 있습니다: ResourceLoader 모듈 번들에 구워 넣은 로컬 URL. 번들 파일 하나가 모든 깊이의 문서를 서빙하므로 보정에 쓸 깊이가 없고, 아무도 대신 고쳐 주지 않습니다. 모듈이 URL을 싣는다면, 문서를 기준으로 삼지 말고 구조상 사이트 루트에 고정된 값을 기준으로 직접 앵커하세요.
이 문제가 발견된 실제 사례: SifterSearch가 검색 결과 문서의 URL을 자기 모듈 설정에 구워 넣었고, 그 결과 한 디렉터리 아래 문서에서 검색한 독자가 존재하지 않는 문서로 보내졌습니다. 사이트가 이미 선언해 둔 번들 경로를 기준으로 그 URL을 해석하는 방식으로, 확장 기능 쪽에서 해결했습니다.
코드는 스킨마다 한 번씩, 병렬로 실행됩니다
스킨이 셋인 사이트는 세 번 렌더링합니다. 각 패스는 자기 MediaWiki 부팅과 자기 데이터베이스 사본과 자기 출력 디렉터리를 가진 별개의 프로세스이며, 패스들은 서로 나란히 실행됩니다. 여기서 세 가지 규칙이 따라 나옵니다:
- 데이터베이스의 기록자가 아니라 독자가 되세요. 패스가 시작되기 전에 내용은 이미 확정돼 있습니다. 기록하는 패스는 같은 파일을 두고 다른 패스와 경쟁하며, 어차피 다음 패스가 똑같이 썼을 것을 씁니다.
- 자기 패스의 출력 디렉터리 안에만 쓰세요(
$wgWikvenHtmlDirectory). 함정을 조심하세요: 메인 스킨은 내보내기 루트에 렌더링하는데 그곳은 다른 모든 스킨 디렉터리의 상위이므로, 그 패스에서는 "내 출력 디렉터리 아래 전부"가 "내 문서들"과 같은 집합이 아닙니다. - 혼자라고 가정하지 마세요. 자기 디렉터리 밖에 만드는 것은 무엇이든 다른 패스와 경쟁할 수 있습니다.
재현성
같은 원본을 두 번 빌드하면 바이트까지 동일한 출력이 나와야 하며, CI가 문서 사이트를 두 번 구워 결과를 비교해 이를 검증합니다. 빌드는 이를 위해 이미 시계를 고정하고, page_touched를 고정하고, MediaWiki가 문서마다 찍는 요청 및 렌더 식별자를 제거하고, 작업 큐를 정해진 순서로 실행합니다.
그러므로 렌더링된 출력에 다음을 넣지 마세요:
- 타임스탬프, 소요 시간, 그 밖에 시계에서 읽은 것;
- 난수, 요청 식별자, 호스트명, 프로세스 식별자, 빌드 기계의 절대 경로;
- 파일시스템 순서에 의존하는 것 — 디렉터리 목록으로부터 쓸 때는 먼저 정렬하세요.
위의 것들을 키로 삼는 캐시가 있다면, 내용을 키로 삼도록 바꾸세요.
작업
작업은 평소처럼 큐에 넣으면 됩니다. 빌드는 어떤 스킨이 렌더링되기 전에 큐를 비우며, 한 번에 한 작업 유형씩 이름 순서로 처리합니다. 따라서 문서가 렌더링될 시점에는 여러분의 작업이 이미 끝나 있습니다.
작업이 비싸고 리비전마다 큐에 들어간다면 알아 둘 것이 하나 있습니다. 빌드는 검색 색인 작업을 큐의 맨 끝까지 미룹니다. 그러지 않으면 큐를 한 바퀴 돌 때마다 다시 실행되어 마지막 것 말고는 전부 버려졌기 때문입니다. 같은 성격의 작업이라면 같은 대우가 필요합니다.
Wikven이 이미 걷어내는 것
살아 있는 위키가 필요한 UI를 다시 넣지 마세요 — Wikven이 일부러 빼놓은 것입니다:
- 편집 및 역사 UI, 그리고 사이드바의 위키 전용 링크;
- 검색 상자. 단, 정적 검색 색인이 결과를 제공하는 경우는 예외입니다;
- 아무것도 렌더링하지 않는 역사 액션.
편집·역사·원본에 대한 푸터 링크는 사이트가 설정한 URL(WikvenEditUrl 등)을 가리킵니다. 그러니 그런 종류의 링크를 추가한다면, 내보내기에 존재하지 않는 Special: 문서를 링크하지 말고 같은 경로를 쓰세요.
확인하기
살아 있는 위키가 동의하리라 믿지 말고, 여러분의 확장 기능을 쓰는 사이트를 빌드해 출력을 직접 보세요:
docker run --rm \
-v "$(pwd)/docs:/workspace/src" \
-v "$(pwd)/dist:/workspace/dist" \
wikven
특히 확인할 만한 것:
- 깊은 곳에 있는 문서 — 제목에 슬래시가 있는 문서 — 를 열어 확장 기능이 렌더링한 링크를 따라가 보세요. URL 버그는 다른 데서는 드러나지 않습니다;
- 서로 다른 디렉터리에 두 번 빌드해
diff -r로 비교하세요. CI보다 먼저 불안정한 값을 잡아냅니다; - 스킨을 둘 이상 설정해 빌드하세요. 자기 출력 밖으로 손을 뻗는 패스가 잡힙니다.