그누보드7 데이터를 WordPress WXR로 변환하는 방법: 대량 게시글 이전에 적합한 이유

게시글 8,000건을 옮기며 포기한 것들

그누보드로 쌓아 온 게시판을 워드프레스로 옮기기로 하고 가장 먼저 한 일은 무엇을 포기할지 정하는 것이었습니다. 게시글 8,000건에 댓글과 첨부파일이 딸려 있었는데, 이걸 한 번에 온전히 옮기는 방법은 없었습니다. 어떤 방식을 골라도 무언가는 손으로 마무리해야 했고, 그래서 처음부터 “무엇을 자동으로 옮기고 무엇을 나중에 붙일 것인가”를 나눠 놓고 시작했습니다. 이 글은 그 선택들과 각각을 그렇게 정한 이유입니다.

API로 하나씩 넣는 방법을 먼저 접었다

워드프레스에는 REST API가 있어서 글을 하나씩 만들어 넣을 수 있습니다. 코드가 직관적이고 중간에 실패해도 그 글만 다시 넣으면 되니 처음에는 이쪽이 편해 보였습니다.

접은 이유는 건수였습니다. 게시글 8,000건이면 요청이 최소 8,000번입니다. 댓글과 첨부파일까지 각각 만들면 요청 수는 그보다 훨씬 늘어납니다. 요청이 그만큼 쌓이면 속도 제한에 걸리거나 인증이 중간에 풀릴 여지가 생기고, 그때 어디까지 들어갔는지 세는 일이 본작업보다 커집니다.

그래서 워드프레스가 가져오기로 읽는 WXR 형식으로 한 번에 넘기기로 했습니다. WXR은 글 목록이 아니라 작성자·카테고리·댓글·메타데이터까지 담는 확장 RSS 구조라, 파일 하나를 만들어 넣으면 끝납니다. 대신 파일이 잘못되면 통째로 다시 만들어야 합니다. 요청 8,000번의 위험과 파일 한 번의 위험 중에 뒤쪽을 골랐습니다.

첨부파일은 옮기지 않기로 했다

가장 고민한 부분입니다. 첨부파일을 워드프레스 미디어 라이브러리에 정식으로 등록하려면 파일을 실제로 올려야 하는데, 8,000건에 딸린 파일을 한 번에 올리는 동안 네트워크가 한 번만 끊겨도 어디까지 올라갔는지 알 수 없게 됩니다.

그래서 파일은 건드리지 않고 경로만 메타데이터로 적어 넘겼습니다. 이미지에 썸네일 정보가 있으면 그것도 따로 적어 두었습니다. 가져오기가 끝난 뒤 별도 스크립트로 파일을 연결하는 순서인데, 이렇게 나누면 파일 연결이 실패해도 글은 이미 들어가 있습니다.

항목 처리 이유
게시판 카테고리로 변환 구조가 1대1로 맞음
게시글·댓글 그대로 유지 이게 옮기는 목적
첨부파일 경로만 메타로 기록 대량 업로드 실패 위험
작성자 한 명으로 통합 계정 매핑 시 가져오기 충돌

작성자를 한 명으로 합친 이유

작성자 이름은 남기되 워드프레스 계정과는 연결하지 않았습니다. 계정까지 매핑하려면 그누보드 회원과 워드프레스 사용자를 짝지어야 하는데, 같은 아이디가 이미 있거나 이름만 같고 다른 사람인 경우가 섞이면 가져오기 도중에 충돌합니다. 글 8,000건을 넣다가 중간에 멈추는 것보다 작성자 표시가 하나로 뭉치는 편이 낫다고 봤습니다. 작성자가 여럿이고 그 구분이 중요한 사이트라면 이 부분은 따로 매핑 표를 만들어야 합니다.

본문이 한 가지 형식이 아니었다

변환에서 손이 가장 많이 간 곳은 본문 형식이었습니다. 게시판에 쌓인 글이 전부 같은 형식이 아닙니다. 어떤 글은 HTML로 저장돼 있고 어떤 글은 마크다운으로 저장돼 있습니다. 그대로 내보내면 마크다운으로 쓴 글이 워드프레스에서 별표와 우물 정자가 그대로 보이는 상태가 됩니다.

글마다 저장된 형식 값을 보고 갈라 처리하도록 했습니다. 마크다운이면 HTML로 바꾸고, HTML이면 그대로 둡니다. 변환에는 마크다운 라이브러리를 쓰되, 라이브러리가 없는 환경에서도 돌아가도록 없으면 정규식으로 최소한만 바꾸는 경로를 따로 뒀습니다. 이전 작업은 대개 남의 서버에서 한 번 돌리고 끝나는 일이라, 그 서버에 무엇이 깔려 있는지 미리 알 수 없습니다. 라이브러리를 설치해 달라고 요청하는 것보다 없어도 도는 편이 빠릅니다.

데이터베이스 접속 정보도 같은 이유로 코드에 적지 않고 환경 설정 파일에서 읽게 했습니다. 서버마다 계정이 다른데 코드를 고쳐 가며 쓰면 실수하기 좋습니다. 접속은 예외 처리와 값 바인딩을 같이 쓸 수 있는 방식으로 했습니다. 잘못된 게시판 이름이 들어와도 쿼리가 깨지지 않고 오류로 떨어지게 하기 위해서입니다.

게시판은 이름 하나, 번호 하나, 또는 전체 중에 골라 지정할 수 있게 했습니다. 전체만 되게 만들면 한 게시판만 다시 옮겨야 할 때 쓸 수가 없습니다. 이전 작업은 한 번에 끝나는 일이 드물어서, 부분만 다시 돌릴 수 있는 입구를 처음부터 열어 두는 편이 낫습니다.

8,000건에 3.2초, 시간은 대부분 DB가 썼다

실제로 돌려 보니 XML을 만드는 데 걸린 시간은 8,000건 기준 약 3.2초였습니다. 그중 대부분이 데이터베이스에서 읽어 오는 시간이었고 XML을 쓰는 것 자체는 빨랐습니다. 변환이 느릴까 봐 걱정했는데 병목은 다른 곳에 있었습니다.

구간 비중
DB 조회 대부분
XML 출력 나머지
전체 약 3.2초

이 결과를 보고 나서는 XML 쪽을 더 다듬지 않았습니다. 손댈 곳은 쿼리였습니다. 원글만 가져오도록 조건을 걸고, 비활성 게시판은 조회 단계에서 빼고, 댓글은 글 번호로 한 번에 묶어 읽는 식으로 정리했습니다.

비활성 게시판을 거르는 조건은 꼭 넣으시길 권합니다. 오래 운영한 사이트에는 닫아 둔 게시판이 남아 있게 마련인데, 조건 없이 긁으면 그것까지 카테고리로 만들어집니다. 옮긴 뒤에 카테고리를 지우는 일은 글을 다시 분류하는 일과 붙어 있어서, 가져오기 전에 빼는 편이 훨씬 쌉니다.

메모리를 아끼려고 바로 내보냈다

데이터를 전부 배열에 모은 다음 한 번에 출력하는 방식은 쓰지 않았습니다. 8,000건이면 배열만으로 10MB를 넘길 수 있어서, 만들면서 바로 내보내는 방식으로 했습니다. 코드가 조금 지저분해지는 대신 건수가 늘어도 메모리 사용량이 따라 늘지 않습니다. 나중에 건수가 더 많은 게시판을 옮길 때 이 선택이 값을 했습니다.

파일 구조 자체는 단순합니다. 바깥이 RSS이고 그 안에 채널이 하나, 채널 안에 항목이 줄줄이 들어갑니다. 게시판은 카테고리 항목으로 먼저 등록해 두고, 글마다 어느 카테고리에 속하는지 한 줄씩 붙입니다. 글에는 번호를 직접 매겨 넣는데 이 번호가 댓글이 어느 글에 붙는지 가리키는 기준이 됩니다. 번호를 건너뛰거나 겹치게 만들면 댓글이 엉뚱한 글에 붙으므로, 번호는 한 곳에서만 발급하도록 해 두는 편이 안전합니다.

댓글은 부모 번호를 그대로 들고 가서 계층을 유지했습니다. 댓글이 하나도 없는 글은 댓글을 닫은 상태로, 하나라도 있으면 열린 상태로 표시했습니다. 이 값이 가져온 뒤 댓글 허용 여부를 그대로 결정하기 때문에, 원래 댓글이 닫혀 있던 글이 갑자기 열리는 것을 막으려면 여기서 정해 줘야 합니다.

이 방법이 맞지 않는 경우

건수에 따라 답이 갈립니다.

게시글 수 권하는 방식
1,000건 이하 REST API로 하나씩 넣어도 됨
1,000~5,000건 둘 다 가능, 댓글 구조가 중요하면 WXR
5,000건 이상 WXR

건수가 적으면 API 쪽이 낫습니다. 실패한 글만 골라 다시 넣을 수 있고, 진행 상황이 눈에 보이기 때문입니다. WXR은 파일을 다 만들 때까지 결과를 알 수 없고, 한 군데가 어긋나면 처음부터 다시 만들어야 합니다. 대신 건수가 커질수록 그 단점보다 “한 번에 끝난다”는 장점이 커집니다.

댓글 구조가 중요한 경우에는 건수가 애매해도 WXR 쪽을 권합니다. API로 댓글까지 계층을 맞춰 넣으려면 부모 댓글이 먼저 들어가 있어야 해서 순서를 관리해야 하는데, XML은 구조를 그대로 적어 두면 끝입니다.

지금도 남아 있는 한계

이 방식으로 옮기면 글과 댓글은 온전히 남지만 나머지는 손이 더 갑니다. 첨부파일은 경로만 들어가 있으니 실제 파일을 올리고 연결하는 작업이 남고, 미디어 라이브러리와 완전히 붙으려면 그 단계에서 다시 처리해야 합니다. 사용자 계정도 연결되지 않습니다.

카테고리도 마찬가지입니다. 지금 구조는 게시판 하나를 카테고리 하나로 보내는 데 맞춰져 있어서, 분류 체계가 여러 단계인 사이트라면 설계를 더 해야 합니다. 옮긴 뒤에 카테고리를 다시 짜는 편이 빠를 수도 있습니다.

이전 자체보다 이전 뒤에 확인할 것이 더 많았습니다. 티스토리 글을 옮겼을 때도 옮기는 작업보다 주소 연결과 용량 정리가 오래 걸렸습니다. 옮기고 나면 에러 로그에 없는 경로를 찾는 요청이 한동안 쌓이는데, 그게 예전 주소로 들어오는 링크입니다. 그 목록을 보면서 연결을 맞춰 가는 편이 확실합니다.

Similar Posts

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다