wp-lingua: 다국어 WordPress를 만드는 가볍지만 확실한 플러그인 만들어보기

Hugo 기반으로 만들었던 다국어 블로그 시스템을 WordPress로 마이그레이션하기 위한 과정을 진행하면서 알아보았던 여러 플러그인들은 대개 All-In-One으로 모든 것을 다 포함하려다보니 기능도 많고, 그 나름대로의 가치를 인정받기 위해 상용 라이선스를 요구하는 경우가 많았습니다.

실상 개인적으로 필요했던 것은 글 사이의 다국어 번역 관계 설정, 언어 전환 스위처 정도였기에 GitHub Copilot의 어시스트를 받아서 WordPress용 플러그인을 만들어볼 수 있었습니다.

기억을 더듬어 예전에 WordPress 아키텍처를 공부하던 내용을 되살려 만들어볼 수 있어서 즐거운 시간이었습니다.

4개의 좋아요

바이브코딩 덕분이긴 하나, 사실 잘 모르는 시스템이나 언어를 가지고 개발하는 것은 심적인 부담이 따르기 마련입니다.

그래서 저는 이 플러그인을 만드는 과정에서 확실한 test bed를 만들 필요가 있다고 생각했는데, docker-compose를 사용하는 것이 확실히 좋은 방법이라는 생각이 들었습니다. docker-compose로 데이터베이스, WP CLI 등 여러 필요한 도구들을 모두 선언해두고, 플러그인 파일만 호스트에서 마운트하는 구조를 만들어 달라는 요청 사항을 의도했는데 괜찮은 테스트 베드가 만들어져서 좋았습니다. :smiley:

1개의 좋아요

플러그인을 활성화한 다음, 사용할 언어들을 설정 페이지에 가서 고릅니다.

그리고 새 글을 작성할 때 이 글의 언어 택소노미를 설정해줍니다.

글을 최초로 한 번 등록 한 후, 다시 편집 화면으로 오면 번역할 다른 언어들을 추가할 수 있습니다.

여기서는 영어로 아티클을 추가로 작성해본다고 가정하겠습니다.

택소노미로 관계 설정이 잘 되어있어서 아래 그림처럼 글 단위 switcher가 잘 나타나는 것을 볼 수 있습니다.

이 외에도 구텐베르크에 대응되는 언어 전환기 스위치 위젯도 만들었습니다. :smiley:

간단한 아이디어로 만들어본 것이지만 재미있고 의미있는 작업이었습니다. :smiley:

써보시고 의견 주시면 틈틈이 반영하도록 하겠습니다!

Hugo 등 외부 SSG에서 작성한 다국어 아티클을 WordPress에 퍼블리싱할 때, WP Lingua의 번역 그룹 택소노미(Lingua_group)가 show_in_rest=false라서 REST API로 번역 연결을 제어할 수 없는 문제가 있었습니다.

이에 맞추어 wp-lingua의 새 버전을 0.5.1로 올리면서, 유닛 테스트와 REST API를 새롭게 추가하게 되었습니다.

택소노미를 외부에 노출하는 대신, 플러그인 내부 메서드를 래핑하는 전용 REST endpoint를 추가하는 방향으로 해결했습니다.


변경사항

1. REST API Controller 추가 (includes/class-rest-controller.php)

lingua/v1 네임스페이스에 3개 엔드포인트를 추가했습니다:

Method Endpoint 설명
POST /lingua/v1/link 포스트 배열을 번역 그룹으로 연결
DELETE /lingua/v1/unlink/{post_id} 포스트를 번역 그룹에서 제거
GET /lingua/v1/translations/{post_id} 포스트의 모든 번역 조회

POST /link는 두 가지 형식을 지원합니다:

  • 언어 맵: {"post_ids": {"ko": 10, "en": 20}} — 언어 메타 자동 설정
  • 단순 배열: {"post_ids": [10, 20]} — 기존 _Lingua_language 메타 참조

내부적으로 Lingua_Translation_Group::get_or_create_group(), add_to_group() 등 기존 메서드를 그대로 활용하므로 플러그인 동작과 완전히 일관됩니다. Lingua_group 택소노미는 여전히 show_in_rest=false로 비공개를 유지합니다.

2. 블록 중복 등록 버그 수정 (wp-lingua.php)

init 액션 반복 실행 시 lingua/language-switcher 블록이 중복 등록되는 문제를 WP_Block_Type_Registry::is_registered() 가드로 수정했습니다.

3. PHPUnit 테스트 인프라 (docker-compose.test.yml)

WordPress 공식 테스트 라이브러리(wp-phpunit)를 사용하는 통합 테스트 환경을 Docker로 구성했습니다:

  • 격리된 환경: 개발용 docker-compose.yml과 완전 분리 (별도 DB, tmpfs)
  • 21개 테스트, 63개 assertion: REST controller 13개 + Translation Group 8개
  • 커버리지: link/unlink/get 정상 동작, 권한 검증, 입력 유효성, 전체 라이프사이클
docker compose -f docker-compose.test.yml up --build --abort-on-container-exit

4. GitHub Actions CI (test.yml)

main push 및 PR 시 자동으로 PHPUnit 테스트를 실행하는 워크플로우를 추가했습니다.

5. README 업데이트

REST API 문서, 테스트 실행 방법, CI/CD 설명, 뱃지(Tests/Release/License)를 추가했습니다.


외부 퍼블리싱 스크립트 활용 예시 (Hugo → WordPress)

# 1. 각 언어별 포스트 생성
KO_ID=$(curl -s -X POST "$WP/wp-json/wp/v2/posts" \
  -H "Authorization: Basic $AUTH" \
  -d '{"title":"한국어 제목","content":"...","status":"publish","meta":{"_Lingua_language":"ko"}}' \
  | jq .id)

EN_ID=$(curl -s -X POST "$WP/wp-json/wp/v2/posts" \
  -H "Authorization: Basic $AUTH" \
  -d '{"title":"English Title","content":"...","status":"publish","meta":{"_Lingua_language":"en"}}' \
  | jq .id)

# 2. 번역 그룹 연결
curl -X POST "$WP/wp-json/lingua/v1/link" \
  -H "Authorization: Basic $AUTH" \
  -d "{\"post_ids\":{\"ko\":$KO_ID,\"en\":$EN_ID}}"

파일 변경 목록

파일 변경
class-rest-controller.php 신규 — REST controller
wp-lingua.php require 추가, 부트스트랩 등록, 블록 중복 등록 수정
docker-compose.test.yml 신규 — 테스트 환경
Dockerfile.test 신규 — PHPUnit 이미지
bootstrap.php 신규 — WP 테스트 부트스트랩
phpunit.xml 신규 — PHPUnit 설정
test-rest-controller.php 신규 — REST 테스트 (13케이스)
test-translation-group.php 신규 — 그룹 테스트 (8케이스)
install-wp-tests.sh 신규 — WP 테스트 라이브러리 설치
run-tests.sh 신규 — 테스트 실행 엔트리포인트
test.yml 신규 — CI 워크플로우
README.md REST API/Testing/CI 문서, 뱃지 추가
1개의 좋아요