MediaWiki
고친 사람 github-actions[bot]
MediaWiki 는 여러 사람이 브라우저에서 같은 문서를 함께 고쳐 쓰게 해 줍니다. 위키백과가 이것으로 돌아갑니다. 문서를 고쳐도 옛 판을 지우지 않고 쌓아 둡니다. 누가 무엇을 바꿨는지 언제든 되짚을 수 있습니다.
쉽고 빠른 이해
MediaWiki 는 위키 사이트를 차려 주는 프로그램입니다. 위키백과의 문서 하나를 열고 편집 버튼을 누르면 누구나 그 문서를 고칠 수 있습니다. 그 화면을 이 프로그램이 만듭니다.
여럿이 한 문서를 고치면 누군가는 잘못 고칩니다. 일부러 망가뜨리는 사람도 있습니다. 그래서 고친 판을 하나도 버리지 않고 남겨 둡니다. 언제든 옛 판으로 되돌릴 수 있습니다.
문서는 고치는 사람보다 읽는 사람이 훨씬 많습니다. 읽을 때마다 화면을 새로 만들면 서버가 버티지 못합니다. 한 번 만든 화면을 저장해 두고 다음 사람에게 다시 씁니다.
어떻게 도나:
- 문서를 저장하면 새 판이 데이터베이스에 하나 더 쌓입니다
- 누가 문서를 열면 최신 판의 원문을 화면 모양으로 바꿉니다
- 바꾼 결과를 저장해 두었다가 다음 사람에게 바로 내줍니다
대가도 있습니다. 판이 계속 쌓여 저장 공간이 늘어납니다. 문서 본문은 한 덩어리 글입니다. 「인구가 백만 넘는 도시만 골라 달라」처럼 문서 속 숫자로 묻지 못합니다.
처음부터 누구나 읽는 위키를 전제로 만들었습니다. 그래서 문서마다 읽을 사람을 가르는 일에는 약합니다.
상세
MediaWiki 는 위키 사이트를 차리는 웹 애플리케이션입니다. 서버에 설치해 두면 브라우저로 접속한 사람들이 문서를 읽고 고치고 새로 씁니다. 이 절은 문서 하나가 저장되고 읽히는 길을 따라갑니다. 그 길에 선 부품을 하나씩 풉니다.
위키라는 도구
위키는 방문자가 브라우저에서 바로 문서를 고치는 웹사이트입니다. 문서마다 편집 버튼이 있습니다. 누르면 원문이 입력 칸에 뜹니다. 고쳐서 저장하면 모든 방문자에게 바뀐 문서가 보입니다.
문서끼리는 제목으로 잇습니다. 본문에 다른 문서의 제목을 표시해 두면 그 문서로 가는 링크가 됩니다. 아직 없는 문서를 가리키면 링크가 빨간색으로 나옵니다. 그 링크를 누르면 그 문서를 새로 쓰는 화면이 열립니다.
MediaWiki 는 위키백과를 굴리려고 만들어진 위키 프로그램입니다. 지금은 위키백과를 운영하는 위키미디어 재단이 관리합니다. 소스가 공개되어 있어서 누구나 내려받아 자기 서버에 설치할 수 있습니다.
요청이 지나가는 계층
MediaWiki 는 PHP(PHP: Hypertext Preprocessor, 웹 서버에서 실행되는 스크립트 언어)로 짠 프로그램입니다. 스스로 요청을 받는 서버 프로그램이 아닙니다. 요청은 웹 서버가 받습니다. MediaWiki 의 PHP 코드는 그 뒤에서 실행됩니다.
flowchart TD
A["브라우저"] --> B["웹 서버"]
B --> C["MediaWiki · PHP 코드"]
C --> D["객체 캐시"]
C --> E["데이터베이스"]
브라우저가 보낸 HTTP(HyperText Transfer Protocol, 웹 요청을 주고받는 규약) 요청을 웹 서버가 받습니다. 웹 서버는 그 요청을 MediaWiki 의 PHP 코드에 넘깁니다.
PHP 코드는 필요한 값을 객체 캐시에서 먼저 찾습니다. 객체 캐시는 자주 묻는 값을 메모리에 담아 두는 캐시입니다. 거기에 없으면 데이터베이스에서 읽습니다.
이 그림의 캐시는 MediaWiki 가 값을 꺼내 쓰는 캐시 하나뿐입니다. 웹 서버 앞에 두는 캐시까지 합친 캐시 계층은 아래 「캐시를 겹겹이 두는 까닭」에서 풉니다.
문서와 판, 사용자 계정은 관계형 데이터베이스에 담깁니다. MySQL 이나 MariaDB 가 흔한 선택입니다. PostgreSQL 과 SQLite 로도 돌릴 수 있습니다.
설정은 LocalSettings.php 라는 PHP 파일 하나에 모입니다. 어느 데이터베이스에 붙을지, 어떤 캐시를 쓸지가 이 파일에 적힙니다. 설치 화면을 마치면 이 파일이 만들어집니다.
판이 쌓이는 저장 방식
MediaWiki 는 문서를 덮어쓰지 않습니다. 저장할 때마다 판을 하나 새로 만듭니다. 판 하나는 고친 뒤의 원문 전체를 담습니다. 누가 언제 고쳤는지와 무엇을 고쳤는지 적은 짧은 요약도 함께 담습니다.
flowchart TD
P["문서"] -- 최신 판을 가리킨다 --> R3["판 3"]
R3 -- 바로 앞 판 --> R2["판 2"]
R2 -- 바로 앞 판 --> R1["판 1"]
문서 쪽은 가장 최근 판 하나만 가리킵니다. 판마다 바로 앞 판이 무엇인지 기억합니다. 새로 저장하면 판 4가 생깁니다. 문서가 가리키는 판만 판 4로 옮겨 갑니다.
이 사슬 덕분에 이력을 볼 수 있습니다. 문서의 이력 화면은 판을 최신 것부터 늘어놓습니다. 두 판을 골라 견주면 어느 줄이 빠지고 더해졌는지 보여 줍니다. diff는 이렇게 두 글의 차이를 뽑는 일입니다.
되돌리기도 판을 지우지 않습니다. 옛 판의 원문으로 새 판을 하나 더 만듭니다. 망가뜨린 편집도 이력에 남습니다. 되돌린 일마저 나중에 다시 되돌릴 수 있습니다.
코드 대신 문서 하나를 단위로 삼은 버전관리라고 보면 됩니다. 대가는 저장 공간입니다. 한 글자만 고쳐도 원문 한 벌이 더 쌓입니다. 이를 덜려고 옛 판의 원문을 압축해 두는 설정이 따로 있습니다.
둘이 동시에 고칠 때
두 사람 갑과 을이 같은 문서를 동시에 열어 고친다고 해 봅시다. MediaWiki 는 편집을 시작할 때 문서를 잠그지 않습니다. 대신 저장할 때 「처음 연 판이 아직 최신인가」를 봅니다.
sequenceDiagram
participant 갑
participant 을
participant MediaWiki
갑->>MediaWiki: 편집 시작 · 판 5를 받는다
을->>MediaWiki: 편집 시작 · 판 5를 받는다
갑->>MediaWiki: 판 5 기준으로 저장
Note over MediaWiki: 판 6이 생긴다
을->>MediaWiki: 판 5 기준으로 저장
Note over MediaWiki: 최신은 판 6 · 기준이 어긋났다
MediaWiki-->>을: 두 변경을 합쳐 보고 안 되면 충돌 화면
을이 저장할 때 MediaWiki 는 기준 판과 최신 판이 다르다는 것을 압니다. 두 사람이 서로 다른 줄을 고쳤다면 두 변경을 합쳐 판 7로 저장합니다. 같은 줄을 고쳤다면 합치지 못합니다. 이때 을에게 두 원문을 나란히 보여 주는 편집 충돌 화면이 뜹니다. 을이 손으로 합쳐 다시 저장합니다.
이런 방식을 낙관적 잠금이라고 부릅니다. 충돌이 드물다고 보고 미리 잠그지 않습니다. 저장하는 순간에만 어긋남을 확인합니다.
위키에서 이 방식을 고른 까닭은 편집 창이 오래 열려 있기 때문입니다. 편집 창을 열어 둔 채 한참 뒤에 저장하거나 그냥 닫는 사람이 많습니다. 편집을 시작할 때 잠갔다면 풀리지 않은 잠금이 문서마다 남았을 겁니다.
위키텍스트와 파서
원문은 HTML(HyperText Markup Language, 웹 페이지를 적는 언어)로 적지 않습니다. 위키텍스트라는 더 간단한 표기로 적습니다. 편집하는 사람이 태그를 몰라도 글을 꾸밀 수 있게 하려는 것입니다.
아래 표는 자주 쓰는 표기 넷을 보입니다. 왼쪽은 편집 칸에 적는 원문입니다. 오른쪽은 화면에 나오는 결과입니다.
| 적는 원문 | 화면에 나오는 결과 |
|---|---|
'''굵게''' |
굵은 글씨 |
[[MySQL]] |
MySQL 문서로 가는 링크 |
== 역사 == |
「역사」라는 절 제목 |
{{출처 필요}} |
다른 문서에 적어 둔 안내문이 이 줄에 들어감 |
원문을 화면용 HTML 로 바꾸는 일은 파서가 합니다. 이 변환에는 품이 듭니다. 링크마다 대상 문서가 있는지 데이터베이스에 물어야 빨간 링크를 가릴 수 있습니다. 다른 문서에서 끼워 넣을 내용도 하나씩 가져와 펼쳐야 합니다.
틀과 네임스페이스
표의 마지막 줄처럼 다른 문서의 내용을 끌어오는 표기가 틀입니다. 틀은 여러 문서에 되풀이해 넣을 내용을 문서 하나로 따로 적어 둔 것입니다. 본문에 틀 이름을 적으면 그 틀의 내용이 그 줄에 끼워져 화면에 나옵니다.
틀 덕분에 같은 안내문을 문서마다 베껴 쓰지 않습니다. 안내문을 고칠 때도 틀 문서 하나만 고치면 그 틀을 쓴 모든 문서에 바뀐 내용이 나옵니다.
틀도 문서의 한 종류라 제목 앞에 「틀:」이 붙습니다. 이렇게 제목 앞 접두어로 문서의 종류를 가르는 것이 MediaWiki 의 네임스페이스입니다. 「사용자:」는 사용자 소개 문서를 담습니다. 「토론:」은 문서마다 딸린 의견 나눔 문서를 담습니다.
캐시를 겹겹이 두는 까닭
위키는 고치는 일보다 읽는 일이 훨씬 많습니다. 방문할 때마다 원문을 다시 변환하면 파서가 서버를 다 씁니다. 그래서 MediaWiki 는 캐싱을 여러 계층에 둡니다.
flowchart TD
A["문서 읽기 요청"] --> B{"리버스 프록시 캐시에 있나"}
B -- 있다 --> Z["바로 응답"]
B -- 없다 --> C["MediaWiki"]
C --> D{"파서 캐시에 있나"}
D -- 있다 --> Y["저장해 둔 HTML 로 응답"]
D -- 없다 --> E["파서로 변환"]
E --> F["파서 캐시에 넣고 응답"]
그림의 첫 갈림은 바깥 계층인 리버스 프록시 캐시입니다. 리버스 프록시는 웹 서버 앞에서 요청을 대신 받는 서버입니다. 이 캐시는 완성된 페이지를 저장해 두었다가 바로 내줍니다. MediaWiki 까지 요청이 가지 않습니다.
이 캐시는 로그인하지 않은 방문자에게만 씁니다. 로그인한 사람의 화면에는 자기 이름과 설정이 들어갑니다. 그래서 페이지가 사람마다 다릅니다. 한 사람의 화면을 다른 사람에게 내줄 수 없습니다.
바깥 계층에 없으면 요청이 MediaWiki 에 닿습니다. 안쪽 계층은 파서 캐시입니다. 변환을 마친 HTML 을 저장해 둡니다. 원문이 바뀌지 않는 동안 다음 방문자는 변환 없이 이 HTML 을 받습니다.
첫 그림에서 PHP 코드가 먼저 들르던 객체 캐시는 이 두 계층과 별개입니다. 사용자 정보나 화면에 쓰는 문구처럼 자주 묻는 값을 memcached 같은 인메모리 저장소에 둡니다. 데이터베이스에 가는 질의가 그만큼 줄어듭니다. 어떤 저장소를 쓸지는 LocalSettings.php 에서 고릅니다.
무효화는 낡은 캐시를 버리는 일입니다. 문서가 바뀌면 그 문서의 캐시를 무효화합니다. 다음 방문자가 새로 변환된 결과를 받습니다.
뒤로 미루는 작업 큐
틀 하나를 고치면 일이 커집니다. 그 틀을 끼운 문서가 수천 개면 그 문서들의 캐시가 전부 낡습니다. 저장 요청 안에서 수천 개를 다시 변환하면 저장 버튼을 누른 사람은 응답을 한참 기다립니다.
MediaWiki 는 이런 일을 작업 큐에 쌓아 두고 나중에 처리합니다. 저장은 새 판을 만드는 데서 끝납니다. 다른 문서의 캐시를 버리는 일은 큐가 넘겨받습니다.
링크 목록을 고치는 일도 큐가 맡습니다. 링크 목록은 어느 문서가 어느 문서와 틀을 가리키는지 적어 둔 표입니다. 틀 하나를 고쳤을 때 그 틀을 끼운 문서를 이 표에서 찾습니다.
큐를 비우는 방법은 둘입니다. 작은 위키는 웹 요청이 들어올 때마다 쌓인 작업을 조금씩 꺼내 처리합니다. 방문이 많은 위키는 큐만 비우는 프로세스를 따로 돌립니다.
확장 기능과 API
MediaWiki 는 코드 곳곳에 이름 붙은 호출 지점을 둡니다. 문서를 저장하기 직전, 화면을 그리기 직전 같은 때입니다. 확장 기능은 이 지점에 자기 함수를 걸어 동작을 더합니다. 이런 호출 지점을 훅이라고 부릅니다.
사람이 아니라 프로그램이 위키를 다룰 때는 API(Application Programming Interface, 프로그램이 부르는 창구)를 씁니다. api.php 라는 주소로 문서를 읽고 고치고 검색합니다. 같은 편집을 수많은 문서에 되풀이하는 봇이 이 창구를 씁니다.
포기한 것
MediaWiki 는 누구나 읽고 고치는 공개 위키를 먼저 생각하고 만들어졌습니다. 그 전제 때문에 내려놓은 것이 있습니다.
| 포기한 것 | 무엇이 곤란해지나 |
|---|---|
| 문서마다 읽을 사람 가르기 | 기본 기능이 아닙니다. 확장으로 덧대도 내용이 새어 나갈 길이 남습니다 |
| 값으로 묻는 데이터 | 본문이 위키텍스트 한 덩어리입니다. 「인구가 백만이 넘는 도시」 같은 조건으로 문서 안의 값을 골라 묻지 못합니다 |
| 편집 중 잠금 | 저장할 때 충돌이 드러납니다. 같은 줄을 고쳤다면 사람이 손으로 합쳐야 합니다 |
| 변환 없는 읽기 | 원문을 읽을 때마다 변환해야 합니다. 캐시 계층을 두고 관리하지 않으면 방문이 늘 때 버티지 못합니다 |
| 작은 저장 공간 | 판마다 원문 한 벌을 남깁니다. 오래 고친 문서일수록 저장소가 계속 커집니다 |
첫 줄의 새어 나갈 길은 이런 것입니다. 읽기를 막아 둔 문서도 공개 문서에 틀로 끼울 수 있습니다. 그러면 막아 둔 내용이 공개 문서 화면에 그대로 펼쳐집니다. 검색 결과와 이력 화면에도 막아 둔 문서의 글이 드러날 수 있습니다.
쓰는 곳과 피하는 곳
여러 사람이 오래 쌓아 가는 공개 문서에 맞습니다. 누가 무엇을 바꿨는지가 남아야 하는 문서입니다. 망가진 편집을 누구나 되돌릴 수 있어야 하는 문서이기도 합니다. 위키백과와 그 자매 사이트가 이렇게 씁니다.
문서마다 읽을 사람을 갈라야 하는 사내 문서에는 맞지 않습니다. 권한을 문서 단위로 거는 Confluence 같은 도구가 이런 일을 맡습니다.
값을 조건으로 골라 묻는 데이터가 본체라면 위키 본문에 담지 않습니다. 본문에 담으면 같은 값을 여러 문서에 따로 적게 됩니다. 한쪽만 고치면 문서끼리 값이 서로 달라집니다. 이런 데이터는 데이터베이스의 표에 한 번만 담습니다.
관련 항목
MediaWiki 가 올라타는 실행 환경
PHP · 웹 서버 · nginx · Apache HTTP Server · HTTP · LAMP · 웹 애플리케이션
MediaWiki 가 문서와 판을 담는 데이터베이스
MySQL · MariaDB · PostgreSQL · SQLite · 관계형 데이터베이스
MediaWiki 가 읽기 부하를 덜어 내는 캐시 계층
memcached · Redis · 파서 캐시 · HTTP 캐시 · 리버스 프록시 · CDN · 캐싱 · 무효화 · 인메모리 저장소
MediaWiki 문서를 이루는 구성 요소
위키텍스트 · 틀 (위키) · 네임스페이스 · 리비전 · 파서 · HTML
MediaWiki 가 편집 이력과 동시 편집을 다루는 방식
버전관리 · diff · 되돌리기 · 편집 충돌 · 낙관적 잠금 · 병합 충돌 · 3-way 병합
MediaWiki 를 넓히고 부르는 수단
확장 기능 · 훅 (프로그래밍) · API · 봇 · 작업 큐
MediaWiki 로 굴러가는 위키미디어 프로젝트
위키미디어 재단 · 위키백과 · 위키낱말사전 · 위키미디어 공용 · 위키데이터
MediaWiki 와 같은 역할을 두고 겨루는 문서 도구
DokuWiki · Confluence · Wiki.js · Notion
MediaWiki 가 속하는 상위 분류
위키 · 콘텐츠 관리 시스템 · 오픈소스 · 협업 도구
다른 이름: 미디어위키 · Mediawiki