3. 텍스트큐브 2차
3.1. 주요 작업내용
텍스트큐브 1차에서 생성된 XML파일을 텍스트큐브 2차 DB에 적재하는 과정, 적재된 텍스트큐브 2차 DB에 직접 UPDATE SQL을 통해 내용을 수정하는 데이터 Cleasing과정, 최종 Cleasing 완료된 텍스트큐브 2차 DB에서 티스토리에 적재하기 위한 최종 XML을 추출하는 과정으로 구성된다.
텍스트큐브 1차에서 직접 최종XML을 생성하지 않고, 이렇게 중간에 임시로 DB에 데이터를 적재한 이유는, 최종 데이터 적재 전에, 원하는 형태로 게시글이 출력되는지를 체크하고, 포맷 등이 깨졌을 경우 직접 SQL을 통해 데이터를 수정하기 위함이었다. 만일 제3자에게 배포를 목적으로 마이그레이션 프로그램을 작성한다면, 이렇게 중간에 수작업이 제거 될 수 있도록 SQL에서 수행되는 모든 작업을 텍스트큐브 1차 추출프로그램에 반영하여야 할 것이다. 본인이 작성한 마이그레이션 프로그램은 배포를 염두해두고 작성한 프로그램이 아니고, 순전히 본인의 홈페이지 이전을 목적으로 한 것이기 때문에 중간에 수작업이 존재하고, 프로그램 내에 하드코딩 부분이 상당히 존재한다.
3.2. XML 데이터 텍스트큐브 2차 DB로 적재
텍스트큐브 1차에서 서버에 생성된 XML파일로 데이터 점검을 위한 중간 DB로 쓰일 텍스트큐브 2차DB에 적재한다. 처음엔 소스의 수정없이 초기 설치된 데이터복원 프로그램으로 적재를 했다. 적재 후 데이터를 확인 결과, 다른 부분은 정상인데, 댓글의 HTML escape문자 ( , > 등)가 그대로 출력되었다. 확인 결과 <> 가 XML파일에 포함될 경우 XML 테그와 구분이 되지 않아, XML생성 시 테그를 escape 처리하는데, 이상하게 데이터 적재프로그램에서 이를 다시 환원하지 않고 있었다. 이는 아마 제로보드XE와 텍스트큐브의 문자처리방식이 약간 차이가 있어서인 것 같다. 이를 수정하기 위해 텍스트큐브 2차의 데이터 복원프로그램 중 댓글 적재부분에서 escape문자를 처리해 주는 로직을 추가한 후 다시 적재를 하였다.
// ROOT/interface/owner/data/import/index.php
// 댓글의 포맷이 깨지는 부분 수정로직 처리
if (isset($node['comment'])) {
for ($i = 0; $i < count($node['comment']); $i++) {
$comment = new Comment();
$comment->entry = $post->id;
$cursor = & $node['comment'][$i];
$comment->name = $cursor['commenter'][0]['name'][0]['.value'];
if (!empty($cursor['commenter'][0]['id'][0]['.value']))
$comment->id = $cursor['commenter'][0]['id'][0]['.value'];
if (!empty($cursor['commenter'][0]['homepage'][0]['.value']))
$comment->homepage = $cursor['commenter'][0]['homepage'][0]['.value'];
if (!empty($cursor['commenter'][0]['ip'][0]['.value']))
$comment->ip = $cursor['commenter'][0]['ip'][0]['.value'];
if (!empty($cursor['commenter'][0]['openid'][0]['.value']))
$comment->openid = $cursor['commenter'][0]['openid'][0]['.value'];
$comment->password = $cursor['password'][0]['.value'];
$comment->secret = $cursor['secret'][0]['.value'];
$comment->written = $cursor['written'][0]['.value'];
$comment->content = $cursor['content'][0]['.value'];
if (!empty($cursor['isFiltered'][0]['.value']))
$comment->isFiltered = $cursor['isFiltered'][0]['.value'];
// 댓글이 HTML esacpe문자로 그대로 적재되는 부분 처리
$comment->content = str_tag_on($comment->content);
if (!$comment->add())
user_error(__LINE__ . $comment->error);
if (isset($node['comment'][$i]['comment'])) {
for ($j = 0; $j < count($node['comment'][$i]['comment']); $j++) {
$childComment = new Comment();
$childComment->entry = $post->id;
$childComment->parent = $comment->id;
$cursor = & $node['comment'][$i]['comment'][$j];
if (!empty($cursor['commenter'][0]['id'][0]['.value']))
$childComment->id = $cursor['commenter'][0]['id'][0]['.value'];
$childComment->name = $cursor['commenter'][0]['name'][0]['.value'];
if (!empty($cursor['commenter'][0]['homepage'][0]['.value']))
$childComment->homepage = $cursor['commenter'][0]['homepage'][0]['.value'];
if (!empty($cursor['commenter'][0]['ip'][0]['.value']))
$childComment->ip = $cursor['commenter'][0]['ip'][0]['.value'];
if (!empty($cursor['commenter'][0]['openid'][0]['.value']))
$childComment->openid = $cursor['commenter'][0]['openid'][0]['.value'];
$childComment->password = $cursor['password'][0]['.value'];
$childComment->secret = $cursor['secret'][0]['.value'];
$childComment->written = $cursor['written'][0]['.value'];
$childComment->content = $cursor['content'][0]['.value'];
if (!empty($cursor['isFiltered'][0]['.value']))
$childComment->isFiltered = $cursor['isFiltered'][0]['.value'];
// 댓글이 HTML esacpe문자로 그대로 적재되는 부분 처리
$childComment->content = str_tag_on($childComment->content);
if (!$childComment->add())
user_error(__LINE__ . $childComment->error);
}
}
}
}
3.3. 텍스트큐브 2차 데이터 점검 및 Cleansing 작업
1차 적재된 결과를 눈으로 각각의 게시글을 점검하고, 포맷이 깨지거나 불필요하게 개행문자가 많이 들어간 부분 등을 mySQL의 REPLACE() 함수를 써서 UPDATE처리 하였다. 이부분은 1차 적재 후, 자신이 바꾸고자 하는 부분을 찾아내서 최대한 SQL 실력을 발휘하면 될 듯 하다. 본인의 경우 이미지갤러리 오브젝트에서 사진 refresh 시간 등을 수정했다.
3.4. 최종 XML 파일 추출
최종 XML파일의 경우는 티스토리의 데이터 구조와 다르지 않기 때문에 기본설치 된 데이터백업프로그램을 사용해도 된다. 본인의 경우 2기가 파일 사이즈의 제약으로 최종 XML 파일을 분리하기 위한 로직을 추가하였으며, 1차 때는 제로보드XE의 게시글 등록일자 기준으로 게시물을 제한한데 반해, 2차의 경우 게시글의 id를 사용하여 추출되는 게시글의 양을 제한하였다.
// ROOT/interface/owner/data/export/index.php
// 2기가의 제약을 피하기 위해 파일을 id 기준으로 2개로 분리하여 추출함.
//$entry_id_from = 1;
//$entry_id_to = 500;
$entry_id_from = 501;
$entry_id_to = 700;
$post_filter = 'id BETWEEN '.$entry_id_from.' AND '.$entry_id_to.' ';
$writer = new OutputWriter();
if (defined('__TEXTCUBE_BACKUP__')) {
if (!file_exists(ROOT . '/cache/backup')) {
mkdir(ROOT . '/cache/backup');
@chmod(ROOT . '/cache/backup', 0777);
}
if (!is_dir(ROOT . '/cache/backup')) {
exit;
}
if ($writer->openFile(ROOT . "/cache/backup/$entry_id_from.xml")) {
} else {
exit;
}
} else {
if ($writer->openStdout()) {
header('Content-Disposition: attachment; filename="Textcube-Backup-' . Timestamp::getDate() . '.xml"');
header('Content-Description: Textcube Backup Data');
header('Content-Transfer-Encoding: binary');
header('Content-Type: application/xml');
} else {
exit;
}
}
....
// 첫번째 파일에서만 카테고리 추출하도록 처리
$category = new Category();
if ($entry_id_from == 1 && $category->open()) {
do {
if($category->id != 0) {
$category->escape();
....
// 게시글을 제외한 나머지 셋팅정보등도 모두 첫번째 파일에서만 추출하도록 처리
$notice = new Notice();
if ($entry_id_from == 1 && $notice->open()) {
do {
$writer->write('<notice' . $newlineStyle . '>' . '<id>' . $notice->id . '</id>' . '<visibility>' . $notice->visibility . '</visibility>' . '<title>' . htmlspecialchars(UTF8::correct($notice->title)) . '</title>' . '<content formatter="' . htmlspecialchars($notice->contentFormatter) . '" editor="' . htmlspecialchars($notice->contentEditor) .'">' . htmlspecialchars(UTF8::correct($notice->content)) . '</content>' . '<published>' . $notice->published . '</published>' . '<created>' . $notice->created . '</created>' . '<modified>' . $notice->modified . '</modified>');
$writer->write(CRLF);
if ($attachment = $notice->getAttachments()) {
do {
...
3.5. 주의사항
1차 XML추출 때와 마찬가지로 삭제 후 적재가 아닌 ADD가 가능하도록 MXL의 migration을 true로 셋팅해야 하며, 2기가가 넘지 않도록 파일분리 로직이 추가되어야 한다. 또, 게시글 이외에 셋팅 및 카테고리 정보 등은 첫번째 파일에서만 추출하도록 구성되어야 한다.
1차와 마찬가지로 트래픽의 제한을 피하기 위해 백업 실행 시 파일다운로드가 아닌 서버에 저장으로 실행하여야 한다. 추출이 완료된 XML파일은 ftp프로그램을 통해 개인PC에 다운받도록 한다.
2008/04/05 - [에세이] - 제로보드XE에서 티스토리로 마이그레이션 성공
2008/04/07 - [에세이] - 마이그레이션[제로보드XE -> 티스토리] (1. 개요)
2008/04/08 - [에세이] - 마이그레이션[제로보드XE -> 티스토리] (2. 텍스트큐브 1차 영역)
2008/04/08 - [에세이] - 마이그레이션[제로보드XE -> 티스토리] (3. 텍스트큐브 2차 영역)
2008/04/08 - [에세이] - 마이그레이션[제로보드XE -> 티스토리] (4. 티스토리 영역)
2008/04/08 - [에세이] - 마이그레이션[제로보드XE -> 티스토리] (5. 맺음말 및 소감)
3.1. 주요 작업내용
텍스트큐브 1차에서 생성된 XML파일을 텍스트큐브 2차 DB에 적재하는 과정, 적재된 텍스트큐브 2차 DB에 직접 UPDATE SQL을 통해 내용을 수정하는 데이터 Cleasing과정, 최종 Cleasing 완료된 텍스트큐브 2차 DB에서 티스토리에 적재하기 위한 최종 XML을 추출하는 과정으로 구성된다.
텍스트큐브 1차에서 직접 최종XML을 생성하지 않고, 이렇게 중간에 임시로 DB에 데이터를 적재한 이유는, 최종 데이터 적재 전에, 원하는 형태로 게시글이 출력되는지를 체크하고, 포맷 등이 깨졌을 경우 직접 SQL을 통해 데이터를 수정하기 위함이었다. 만일 제3자에게 배포를 목적으로 마이그레이션 프로그램을 작성한다면, 이렇게 중간에 수작업이 제거 될 수 있도록 SQL에서 수행되는 모든 작업을 텍스트큐브 1차 추출프로그램에 반영하여야 할 것이다. 본인이 작성한 마이그레이션 프로그램은 배포를 염두해두고 작성한 프로그램이 아니고, 순전히 본인의 홈페이지 이전을 목적으로 한 것이기 때문에 중간에 수작업이 존재하고, 프로그램 내에 하드코딩 부분이 상당히 존재한다.
3.2. XML 데이터 텍스트큐브 2차 DB로 적재
텍스트큐브 1차에서 서버에 생성된 XML파일로 데이터 점검을 위한 중간 DB로 쓰일 텍스트큐브 2차DB에 적재한다. 처음엔 소스의 수정없이 초기 설치된 데이터복원 프로그램으로 적재를 했다. 적재 후 데이터를 확인 결과, 다른 부분은 정상인데, 댓글의 HTML escape문자 ( , > 등)가 그대로 출력되었다. 확인 결과 <> 가 XML파일에 포함될 경우 XML 테그와 구분이 되지 않아, XML생성 시 테그를 escape 처리하는데, 이상하게 데이터 적재프로그램에서 이를 다시 환원하지 않고 있었다. 이는 아마 제로보드XE와 텍스트큐브의 문자처리방식이 약간 차이가 있어서인 것 같다. 이를 수정하기 위해 텍스트큐브 2차의 데이터 복원프로그램 중 댓글 적재부분에서 escape문자를 처리해 주는 로직을 추가한 후 다시 적재를 하였다.
// ROOT/interface/owner/data/import/index.php
// 댓글의 포맷이 깨지는 부분 수정로직 처리
if (isset($node['comment'])) {
for ($i = 0; $i < count($node['comment']); $i++) {
$comment = new Comment();
$comment->entry = $post->id;
$cursor = & $node['comment'][$i];
$comment->name = $cursor['commenter'][0]['name'][0]['.value'];
if (!empty($cursor['commenter'][0]['id'][0]['.value']))
$comment->id = $cursor['commenter'][0]['id'][0]['.value'];
if (!empty($cursor['commenter'][0]['homepage'][0]['.value']))
$comment->homepage = $cursor['commenter'][0]['homepage'][0]['.value'];
if (!empty($cursor['commenter'][0]['ip'][0]['.value']))
$comment->ip = $cursor['commenter'][0]['ip'][0]['.value'];
if (!empty($cursor['commenter'][0]['openid'][0]['.value']))
$comment->openid = $cursor['commenter'][0]['openid'][0]['.value'];
$comment->password = $cursor['password'][0]['.value'];
$comment->secret = $cursor['secret'][0]['.value'];
$comment->written = $cursor['written'][0]['.value'];
$comment->content = $cursor['content'][0]['.value'];
if (!empty($cursor['isFiltered'][0]['.value']))
$comment->isFiltered = $cursor['isFiltered'][0]['.value'];
// 댓글이 HTML esacpe문자로 그대로 적재되는 부분 처리
$comment->content = str_tag_on($comment->content);
if (!$comment->add())
user_error(__LINE__ . $comment->error);
if (isset($node['comment'][$i]['comment'])) {
for ($j = 0; $j < count($node['comment'][$i]['comment']); $j++) {
$childComment = new Comment();
$childComment->entry = $post->id;
$childComment->parent = $comment->id;
$cursor = & $node['comment'][$i]['comment'][$j];
if (!empty($cursor['commenter'][0]['id'][0]['.value']))
$childComment->id = $cursor['commenter'][0]['id'][0]['.value'];
$childComment->name = $cursor['commenter'][0]['name'][0]['.value'];
if (!empty($cursor['commenter'][0]['homepage'][0]['.value']))
$childComment->homepage = $cursor['commenter'][0]['homepage'][0]['.value'];
if (!empty($cursor['commenter'][0]['ip'][0]['.value']))
$childComment->ip = $cursor['commenter'][0]['ip'][0]['.value'];
if (!empty($cursor['commenter'][0]['openid'][0]['.value']))
$childComment->openid = $cursor['commenter'][0]['openid'][0]['.value'];
$childComment->password = $cursor['password'][0]['.value'];
$childComment->secret = $cursor['secret'][0]['.value'];
$childComment->written = $cursor['written'][0]['.value'];
$childComment->content = $cursor['content'][0]['.value'];
if (!empty($cursor['isFiltered'][0]['.value']))
$childComment->isFiltered = $cursor['isFiltered'][0]['.value'];
// 댓글이 HTML esacpe문자로 그대로 적재되는 부분 처리
$childComment->content = str_tag_on($childComment->content);
if (!$childComment->add())
user_error(__LINE__ . $childComment->error);
}
}
}
}
3.3. 텍스트큐브 2차 데이터 점검 및 Cleansing 작업
1차 적재된 결과를 눈으로 각각의 게시글을 점검하고, 포맷이 깨지거나 불필요하게 개행문자가 많이 들어간 부분 등을 mySQL의 REPLACE() 함수를 써서 UPDATE처리 하였다. 이부분은 1차 적재 후, 자신이 바꾸고자 하는 부분을 찾아내서 최대한 SQL 실력을 발휘하면 될 듯 하다. 본인의 경우 이미지갤러리 오브젝트에서 사진 refresh 시간 등을 수정했다.
3.4. 최종 XML 파일 추출
최종 XML파일의 경우는 티스토리의 데이터 구조와 다르지 않기 때문에 기본설치 된 데이터백업프로그램을 사용해도 된다. 본인의 경우 2기가 파일 사이즈의 제약으로 최종 XML 파일을 분리하기 위한 로직을 추가하였으며, 1차 때는 제로보드XE의 게시글 등록일자 기준으로 게시물을 제한한데 반해, 2차의 경우 게시글의 id를 사용하여 추출되는 게시글의 양을 제한하였다.
// ROOT/interface/owner/data/export/index.php
// 2기가의 제약을 피하기 위해 파일을 id 기준으로 2개로 분리하여 추출함.
//$entry_id_from = 1;
//$entry_id_to = 500;
$entry_id_from = 501;
$entry_id_to = 700;
$post_filter = 'id BETWEEN '.$entry_id_from.' AND '.$entry_id_to.' ';
$writer = new OutputWriter();
if (defined('__TEXTCUBE_BACKUP__')) {
if (!file_exists(ROOT . '/cache/backup')) {
mkdir(ROOT . '/cache/backup');
@chmod(ROOT . '/cache/backup', 0777);
}
if (!is_dir(ROOT . '/cache/backup')) {
exit;
}
if ($writer->openFile(ROOT . "/cache/backup/$entry_id_from.xml")) {
} else {
exit;
}
} else {
if ($writer->openStdout()) {
header('Content-Disposition: attachment; filename="Textcube-Backup-' . Timestamp::getDate() . '.xml"');
header('Content-Description: Textcube Backup Data');
header('Content-Transfer-Encoding: binary');
header('Content-Type: application/xml');
} else {
exit;
}
}
....
// 첫번째 파일에서만 카테고리 추출하도록 처리
$category = new Category();
if ($entry_id_from == 1 && $category->open()) {
do {
if($category->id != 0) {
$category->escape();
....
// 게시글을 제외한 나머지 셋팅정보등도 모두 첫번째 파일에서만 추출하도록 처리
$notice = new Notice();
if ($entry_id_from == 1 && $notice->open()) {
do {
$writer->write('<notice' . $newlineStyle . '>' . '<id>' . $notice->id . '</id>' . '<visibility>' . $notice->visibility . '</visibility>' . '<title>' . htmlspecialchars(UTF8::correct($notice->title)) . '</title>' . '<content formatter="' . htmlspecialchars($notice->contentFormatter) . '" editor="' . htmlspecialchars($notice->contentEditor) .'">' . htmlspecialchars(UTF8::correct($notice->content)) . '</content>' . '<published>' . $notice->published . '</published>' . '<created>' . $notice->created . '</created>' . '<modified>' . $notice->modified . '</modified>');
$writer->write(CRLF);
if ($attachment = $notice->getAttachments()) {
do {
...
3.5. 주의사항
1차 XML추출 때와 마찬가지로 삭제 후 적재가 아닌 ADD가 가능하도록 MXL의 migration을 true로 셋팅해야 하며, 2기가가 넘지 않도록 파일분리 로직이 추가되어야 한다. 또, 게시글 이외에 셋팅 및 카테고리 정보 등은 첫번째 파일에서만 추출하도록 구성되어야 한다.
1차와 마찬가지로 트래픽의 제한을 피하기 위해 백업 실행 시 파일다운로드가 아닌 서버에 저장으로 실행하여야 한다. 추출이 완료된 XML파일은 ftp프로그램을 통해 개인PC에 다운받도록 한다.
2008/04/05 - [에세이] - 제로보드XE에서 티스토리로 마이그레이션 성공
2008/04/07 - [에세이] - 마이그레이션[제로보드XE -> 티스토리] (1. 개요)
2008/04/08 - [에세이] - 마이그레이션[제로보드XE -> 티스토리] (2. 텍스트큐브 1차 영역)
2008/04/08 - [에세이] - 마이그레이션[제로보드XE -> 티스토리] (3. 텍스트큐브 2차 영역)
2008/04/08 - [에세이] - 마이그레이션[제로보드XE -> 티스토리] (4. 티스토리 영역)
2008/04/08 - [에세이] - 마이그레이션[제로보드XE -> 티스토리] (5. 맺음말 및 소감)
'블로그 이야기' 카테고리의 다른 글
| 마이그레이션[제로보드XE -> 티스토리] (5. 맺음말 및 소감) (6) | 2008/04/08 |
|---|---|
| 마이그레이션[제로보드XE -> 티스토리] (4. 티스토리 영역) (0) | 2008/04/08 |
| 마이그레이션[제로보드XE -> 티스토리] (3. 텍스트큐브 2차 영역) (0) | 2008/04/08 |
| 마이그레이션[제로보드XE -> 티스토리] (2. 텍스트큐브 1차 영역) (9) | 2008/04/08 |
| 마이그레이션[제로보드XE -> 티스토리] (1. 개요) (0) | 2008/04/07 |
| 제로보드XE에서 티스토리로 마이그레이션 성공 (1) | 2008/04/05 |
