스테이징을 프로덕션에서 복제했거나, 도메인을 옮겼거나, 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 home 과 siteurl 이 새 주소인가, 위젯과 커스터마이저 화면이 값을 그대로 갖고 있는가(직렬화가 살아 있다는 뜻입니다), 그리고 이미지가 실제로 열리는가.
브라우저 콘솔에서 혼합 콘텐츠 경고가 남아 있다면 http:// 로 시작하는 옛 경로가 아직 있다는 뜻이므로, 프로토콜만 바꾸는 치환을 한 번 더 돌립니다.
이런 이전 작업의 전체 절차는 작업 과정에 단계별로 공개돼 있고, 관련 글은 개발 워크플로우 아카이브에 있습니다.
다음 회차
명령 조합으로는 안 되는 일이 생기면 스크립트를 씁니다. 다음 회차는 eval-file 인데, 첫 5분 안에 반드시 부딪히는 벽이 하나 있습니다 — 인자 전달입니다.