페이지 내보내기에 대한 LUXCM의 처리방법

페이지 선택 내보내기 — 카테고리가 없는 페이지를 다루는 방식

나는 “페이지 선택” 모드를 “글 선택” 모드의 복사본으로 만들지 않았다.

겉보기엔 둘 다 체크박스로 항목을 고르는 화면이지만, 페이지는 글과 근본적으로 다른 구조를 갖는다.

글은 카테고리라는 다대다 분류 체계로 묶이지만, 페이지에는 애초에 카테고리라는 개념이 없다.

대신 페이지는 부모-자식 관계, 즉 계층 구조로 조직된다. 이 차이를 무시하고 페이지를 글과 똑같은 방식으로 옮기면, 옮긴 페이지들이 흩어져서 원래의 사이트 구조를 잃어버린다.

페이지는 “부모”가 곧 사이트 구조다

워드프레스에서 글은 “카테고리”라는 별도의 분류 체계로 관리되지만, 페이지는 이 taxonomy를 쓰지 않는다.

대신 각 페이지는 post_parent라는 필드로 다른 페이지를 부모로 지정할 수 있고, 이렇게 만들어진 상하위 구조가 곧 그 사이트의 “정보 구조” 그 자체가 된다.

UIC의 페이지 계층 구조 안내 문서는 이 부모 페이지 설정이 왜 중요한지를 명확히 짚는다 — 부모 페이지를 지정하면 “자동으로 브레드크럼(breadcrumb) 내비게이션이 만들어지고”, 편집자 입장에서도 “백엔드에서의 저작적 조직화”가 이루어진다는 것이다.

반대로 말하면, 이 계층 구조가 깨지면 방문자는 “지금 사이트의 어느 위치에 있는지” 알려주는 시각적 경로를 잃고, 관리자 입장에서도 페이지들이 논리적으로 묶이지 않아 콘텐츠 관리 자체가 혼란스러워진다.

나는 이 지점에서 “페이지 선택”을 “카테고리별 글 선택”과 똑같은 로직으로 만들 수 없다는 걸 확인했다.

글은 카테고리라는 태그를 붙이고 떼는 것만으로 분류가 완성되지만, 페이지는 “이 페이지의 부모가 무엇인가”라는 관계 자체가 콘텐츠의 일부다.

이 관계를 옮기지 못하면, 옮긴 페이지들은 개별적으로는 멀쩡해 보여도 사이트 전체로 보면 갈피를 잃은 파편들의 집합이 된다.

댓글에서 배운 교훈을 페이지에도 그대로 적용했다

나는 이 시리즈의 첫 글에서 워드프레스 코어의 Trac 티켓 #8019를 다룬 적이 있다.

댓글을 가져올 때 워드프레스가 ID 충돌을 피하려고 새로운 ID를 부여하면서, 정작 “이 댓글이 누구의 답글인가”를 가리키는 부모 ID는 갱신하지 않아서 답글 구조가 깨진다는 문제였다.

나는 페이지의 부모-자식 관계에도 정확히 같은 함정이 있다는 걸 알고 있었다. 원본 사이트에서 “회사 소개” 페이지의 ID가 12번이고, 그 아래 “연혁” 페이지가 post_parent = 12로 저장돼 있다고 해도, 대상 사이트에서 “회사 소개” 페이지가 새로 만들어지면 그 ID는 12번이 아니라 전혀 다른 번호가 된다.

“연혁” 페이지가 여전히 옛날 ID인 12를 부모로 들고 있으면, 그 관계는 새 사이트의 아무 페이지도 가리키지 못하는 죽은 참조가 된다.

그래서 나는 페이지 가져오기를 두 단계로 나눴다.

이때는 아직 부모를 연결하지 않는다 — 아직 부모 페이지 자체가 새 사이트에서 몇 번 ID를 받을지 모르기 때문이다.

모든 페이지가 다 만들어진 뒤, 두 번째 단계에서 “옛날 ID → 새 ID” 매핑표를 놓고, 각 페이지의 옛 부모 ID를 이 표에서 찾아 새 ID로 바꿔서 다시 연결한다.

이건 Trac #8019가 댓글에 적용했던 해법과 원리가 완전히 같다 — 관계를 옮기려면, 그 관계가 참조하는 대상의 ID가 바뀐다는 전제 위에서 매핑표를 만들고 나중에 그 매핑표로 다시 이어 붙여야 한다는 원칙이다.

나는 이 원칙을 댓글 하나에만 적용하고 끝내는 대신, 페이지의 부모-자식 관계에도 똑같이 적용했다.

정직하게 짚어야 할 한계 하나

여기서 나는 한 가지를 숨기지 않고 말해야 한다고 생각한다.

이 매핑은 “부모 페이지도 같은 가져오기 작업에 함께 포함되어 있을 때”만 성립한다.

만약 사용자가 “연혁” 페이지만 선택하고 그 부모인 “회사 소개” 페이지는 선택하지 않았다면, 매핑표에는 “회사 소개”의 옛 ID에 대응하는 새 ID가 아예 존재하지 않는다.

이 경우 “연혁” 페이지는 부모 연결이 복원되지 않은 채로, 계층의 최상위(부모 없음) 상태로 만들어진다.

이건 버그가 아니라 논리적으로 당연한 결과다 — 존재하지도 않는 부모에게 연결할 방법은 없기 때문이다. 나는 이걸 오류로 처리해서 가져오기 전체를 막는 대신, 조용히 최상위로 만들어지도록 두었다.

다만 이 시리즈를 읽는 사용자에게는 명확히 말하고 싶다 — 계층 구조를 가진 페이지들을 “페이지 선택” 모드로 옮길 때는, 자식 페이지만 고르지 말고 그 부모 페이지도 함께 선택해야 구조가 온전히 유지된다.

메뉴는 페이지와 별개의 구조라는 것도 분명히 해야 한다

또 하나 짚어야 할 지점이 있다.

워드프레스.org 지원 포럼의 한 스레드는 이 구분을 정확히 보여준다.

이 사용자는 “회사 소개” 아래 “우리 학교”, “커리큘럼” 같은 자식 페이지를 만들고, 내비게이션 메뉴도 똑같은 구조로 반영되길 원했다.

그런데 이 사용자가 쓰던 도구의 개발자는 “계층형 메뉴(부모와 하위 메뉴 항목이 있는)를 가져오는 기능은 기본적으로 지원되지 않는다”고 답했다.

post_parent 필드로 페이지 간의 부모-자식 관계를 옮기는 것과, 워드프레스의 내비게이션 메뉴(wp_nav_menu)에 등록된 메뉴 항목의 계층 구조를 옮기는 것은 서로 다른 데이터이기 때문이다.

나는 이 구분을 사용자에게 정확히 전달해야 한다고 생각한다.

LUX Content Migration의 페이지 선택 모드가 복원하는 건 페이지 자체의 부모-자식 관계, 즉 post_parent 필드다.

메뉴는 대상 사이트에서 페이지를 옮긴 뒤 사용자가 직접(혹은 다시) 구성해야 한다. 나는 이 부분을 마치 이 플러그인이 다 처리해주는 것처럼 말하고 싶지 않다.

구조를 정리하면

페이지는 글과 다른 종류의 데이터다.

카테고리라는 태그로 분류되는 게 아니라, 부모-자식이라는 관계로 사이트의 구조 자체를 이룬다.

나는 이 차이를 무시하고 페이지 선택 모드를 만들지 않았다.

댓글의 답글 구조를 복원할 때 썼던 것과 같은 원리 — 옛 ID와 새 ID를 매핑표로 연결하고, 모든 항목이 만들어진 뒤에 그 표를 보고 관계를 다시 이어 붙이는 방식 — 를 페이지의 부모-자식 관계에도 그대로 적용했다.

다만 이 방식이 성립하려면 부모 페이지도 같은 작업 안에 함께 포함되어 있어야 한다는 조건이 있고, 메뉴 구조는 페이지 계층과는 완전히 다른 데이터라서 이 플러그인이 대신 복원해주지 않는다는 것도 분명히 밝혀둔다.

나는 이 플러그인이 할 수 있는 것과 할 수 없는 것을 정확히 구분해서 말하는 게, 실제로 계층 구조를 가진 사이트를 옮겨야 하는 사람에게 더 도움이 된다고 생각한다.

참고 자료