Technote

개발 워크플로우 실무

연재 WP-CLI 자동화 레시피 8부 중 3부

search-replace 안전 수칙 — 직렬화와 guid

SQL 한 줄로 도메인을 바꾸면 위젯과 테마 설정이 조용히 빈 값이 됩니다. 직렬화가 문자열 길이를 함께 저장하기 때문이고, 이 문제는 도구를 바꾸는 것으로 완전히 사라집니다.

스테이징을 프로덕션에서 복제했거나, 도메인을 옮겼거나, http 에서 https 로 넘어갔다면 데이터베이스 안에 옛 주소가 잔뜩 남아 있습니다. 본문의 이미지 경로, 위젯 설정, 테마 설정, 플러그인 옵션에 흩어져 있습니다.

여기서 UPDATE … REPLACE() 한 줄이 떠오릅니다. 그 한 줄이 데이터를 깨뜨립니다. 왜인지 알면 다시는 그 유혹에 빠지지 않습니다.

왜 SQL 치환이 데이터를 깨뜨리는가

워드프레스는 배열이나 객체 형태의 설정을 직렬화된 PHP 문자열로 한 칸에 저장합니다. 그 형식은 값과 함께 문자열의 길이를 적어 둡니다.

a:1:{s:4:"logo";s:35:"https://staging.example.kr/logo.png";}
                        ↑ 뒤 문자열이 35자라는 선언

SQL 치환 후 (길이는 그대로 남는다)
a:1:{s:4:"logo";s:35:"https://example.kr/logo.png";}
                        ↑ 실제로는 27자 — 선언과 어긋난다

이 값을 unserialize() 하면 실패하고, 실패하면 get_option()false 를 돌려줍니다. 화면에서는 위젯 설정 · 커스터마이저 값 · 사용자 정의 필드가 “초기화된 것처럼” 보입니다. 에러 메시지는 어디에도 없습니다. 이것이 가장 나쁜 종류의 고장입니다 — 실패가 조용하고, 원인과 증상이 멀리 떨어져 있습니다.

같은 치환이라도 직렬화를 아는 도구만 안전하다

wp search-replace 는 값을 읽어 역직렬화하고, 안쪽 문자열을 치환한 뒤, 다시 직렬화해서 씁니다. 길이는 그 과정에서 자동으로 다시 계산됩니다. 중첩된 배열 안쪽까지 따라 들어갑니다.

–dry-run 을 먼저

실행 전에 무엇이 몇 건 바뀌는지를 봅니다.

wp search-replace 'https://staging.example.kr' 'https://example.kr' --dry-run --report-changed-only

테이블별 건수가 나옵니다. 예상보다 훨씬 많다면 검색어가 너무 짧다는 신호입니다 — 도메인 없이 example 만 넣는 식의 실수가 흔합니다. 예상보다 적다면 프로토콜이 섞여 있을 수 있습니다(http://https://, www 있는 형태와 없는 형태를 각각 확인합니다).

건수가 납득되면 그때 백업입니다. wp db export before.sql 한 줄이면 되고, 이 파일이 있는지 여부가 사고의 크기를 결정합니다.

guid 는 링크가 아니라 식별자다

실무에서 쓰는 형태에는 플래그가 하나 더 붙습니다.

wp search-replace 'https://staging.example.kr' 'https://example.kr' --skip-columns=guid --report-changed-only

guid 는 주소처럼 생겼지만 주소가 아니라 영구 식별자입니다. 프론트엔드의 링크는 퍼머링크 규칙에서 만들어지고, guid 는 피드 리더가 “이 항목을 전에 본 적 있는가” 를 판단하는 데 쓰입니다.

그래서 guid 를 바꾸면 구독자의 피드에서 과거 글 전체가 새 글로 다시 뜹니다. 사이트는 멀쩡해 보이고, 문제는 남의 화면에서만 일어납니다. 워드프레스 문서가 “생성 후 절대 바꾸지 말 것” 이라고 적어 둔 값이며, 이 플래그를 습관으로 만드는 편이 낫습니다.

어느 테이블까지 볼 것인가

기본값은 워드프레스가 등록한 테이블만입니다. 플러그인이 만든 테이블은 접두사를 공유하더라도 기본 대상에 들어가지 않으므로, 필요하면 범위를 넓힙니다.

치환 범위 — 아래로 갈수록 넓고, 그만큼 되돌리기 어렵다

몇 가지 플래그를 더 알아 두면 좋습니다. --precise 는 빠른 경로 대신 항상 PHP 로 처리해 확실성을 높이고(느립니다), --export=out.sql 은 DB 를 건드리지 않고 치환된 덤프만 파일로 뽑습니다 — 이관 작업에서 특히 유용합니다. 멀티사이트에서는 --network--url 을 함께 봅니다.

치환 뒤 확인

끝났다고 넘어가지 않습니다. 세 가지를 봅니다. wp option get homesiteurl 이 새 주소인가, 위젯과 커스터마이저 화면이 값을 그대로 갖고 있는가(직렬화가 살아 있다는 뜻입니다), 그리고 이미지가 실제로 열리는가.

브라우저 콘솔에서 혼합 콘텐츠 경고가 남아 있다면 http:// 로 시작하는 옛 경로가 아직 있다는 뜻이므로, 프로토콜만 바꾸는 치환을 한 번 더 돌립니다.

이런 이전 작업의 전체 절차는 작업 과정에 단계별로 공개돼 있고, 관련 글은 개발 워크플로우 아카이브에 있습니다.

다음 회차

명령 조합으로는 안 되는 일이 생기면 스크립트를 씁니다. 다음 회차는 eval-file 인데, 첫 5분 안에 반드시 부딪히는 벽이 하나 있습니다 — 인자 전달입니다.

이 주제의 다른 글

노하우 목록으로

개발 워크플로우 실무

워드프레스를 GitHub 에 올릴 때의 .gitignore

워드프레스 저장소에서 커밋해야 할 것과 절대 커밋하면 안 되는 것의 경계는 명확합니다. wp-config.php 한 파일이 저장소에 들어가는 순간 DB 비밀번호와 인증 솔트가 통째로 유출됩니다.

개발자 · 운영 · 관리자 5분 읽기

개발 워크플로우 실무

취향 논쟁을 규칙 확인으로 바꾸는 디자인 리뷰

"좀 답답해 보인다" 는 반박할 수 없고 고칠 수도 없습니다. 간격 척도 밖의 값, 팔레트 밖의 색, 기준 미달 대비, 빠진 상태 — 네 항목만 규칙으로 바꿔도 리뷰의…

디자이너 4분 읽기

₩270,000 · 신청하기