개발일지

개발일지

워드프레스 워드프레스 썸네일 관리: DB 롤백 후 발생한 문제와 해결 방법

페이지 정보

profile_image
영삼이
0건 27회 25-04-18 13:01

본문

워드프레스 썸네일 관리: DB 롤백 후 발생한 문제와 해결 방법

문제 상황: DB 롤백으로 발생한 이미지 썸네일 불일치

워드프레스 사이트를 운영하다 보면 예상치 못한 상황에 직면하게 됩니다. 저는 약 7만 건의 데이터가 있는 워드프레스 사이트를 운영하고 있었는데, 어떤 이유로 4만 건 데이터가 있던 시점으로 DB를 롤백해야 하는 상황이 발생했습니다.

DB를 롤백한 후 심각한 문제가 발생했습니다. 이미지 파일은 그대로 있지만, 롤백된 DB에서는 일부 이미지에 대한 정보가 없거나 불일치하게 되었고, 이로 인해 썸네일이 제대로 표시되지 않는 문제가 생겼습니다. 블로그 전체에 걸쳐 이미지가 깨지거나 아예 보이지 않는 상황이 발생했습니다.

특히 메인 페이지의 썸네일 이미지가 모두 깨져 사이트의 첫인상이 완전히 망가졌습니다. 방문자들이 첫 화면에서 깨진 이미지를 보게 된다면 사이트의 신뢰도가 크게 떨어질 것이 분명했습니다.

해결 방안: 모든 썸네일 재생성 스크립트 개발

이 문제를 해결하기 위해 저는 다음과 같은 3단계 접근법을 채택했습니다:

  1. 모든 자동 생성된 썸네일 파일 삭제
  2. 워드프레스 캐시 비우기
  3. 모든 이미지에 대해 새로운 썸네일 생성
  4. 누락된 포스트-썸네일 연결 복구

이 작업을 수동으로 하는 것은 거의 불가능했기 때문에, 자동화된 쉘 스크립트를 개발했습니다. 처음에는 기본적인 스크립트로 시작했지만, 7만 건에 가까운 데이터를 처리하면서 성능 문제에 직면했습니다. 작업이 너무 오래 걸리고 중간에 중단되면 처음부터 다시 시작해야 하는 문제도 있었습니다.

그래서 스크립트를 더욱 발전시켜 다음과 같은 기능을 추가했습니다:

  • 병렬 처리로 성능 대폭 향상
  • 작업 중단 시 이어서 진행할 수 있는 기능
  • 서버 사양에 맞게 최적화할 수 있는 설정 옵션
  • 자세한 로그 기록

고성능 썸네일 관리 스크립트 소개

아래는 제가 개발한 고성능 워드프레스 썸네일 관리 스크립트입니다. 이 스크립트는 대용량 워드프레스 사이트에서도 효율적으로 작동하며, 32코어 서버에서 최적화되어 빠른 처리 속도를 제공합니다.

#!/bin/bash

# 성능 설정 - 서버 사양에 맞게 조정 가능
CHUNK_SIZE=1000      # 한 번에 처리할 포스트 수
PARALLEL_JOBS=32     # 병렬 처리 작업 수 (CPU 코어 수에 맞게 조정)
PHP_MEMORY="4G"      # PHP 메모리 제한

# 로그 및 상태 파일 설정
LOG_FILE="/tmp/thumbnail_fix_$(date +%Y%m%d_%H%M%S).log"
STATUS_FILE="/tmp/thumbnail_fix_status.txt"
WP_PATH="/www/home/allblog/public_html"

# 로그 함수
log() {
  local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
  echo "$msg" | tee -a "$LOG_FILE"
}

log "🔄 썸네일 수정 프로세스 시작 (고성능 모드: 병렬 작업 $PARALLEL_JOBS, 청크 크기 $CHUNK_SIZE)"

# 작업 재개 여부 확인
CURRENT_STEP="삭제"
LAST_PROCESSED_ID=0

if [ -f "$STATUS_FILE" ]; then
  source "$STATUS_FILE"
  log "⏩ 이전 작업 상태 발견: 단계 '$CURRENT_STEP', 마지막 ID $LAST_PROCESSED_ID"
  log "작업을 이어서 진행합니다."
else
  log "🆕 새 작업을 시작합니다."
  echo "CURRENT_STEP=\"삭제\"" > "$STATUS_FILE"
  echo "LAST_PROCESSED_ID=0" >> "$STATUS_FILE"
fi

# 1. 썸네일 삭제 단계
if [ "$CURRENT_STEP" = "삭제" ]; then
  # 업로드 폴더로 이동
  cd "$WP_PATH/wp-content/uploads" || { log "❌ 업로드 폴더로 이동 실패"; exit 1; }
  log "📂 업로드 폴더로 이동 완료"

  # 병렬 처리로 자동 생성된 썸네일 파일 빠르게 삭제
  log "🧹 생성된 썸네일 삭제 중... (병렬 처리 $PARALLEL_JOBS 작업)"

  # 연도별 디렉토리 목록 가져오기
  YEAR_DIRS=$(find . -maxdepth 1 -type d -name "20*" | sort)

  # 연도별로 병렬 처리
  for year_dir in $YEAR_DIRS; do
    log "📅 처리 중: $year_dir"
    
    # 각 디렉토리에서 썸네일 파일 찾아 여러 배치로 병렬 삭제
    find "$year_dir" -type f -regextype posix-extended \
      -regex '.*-[0-9]+x[0-9]+\.(jpg|jpeg|png|webp)' \
      -print0 | xargs -0 -P $PARALLEL_JOBS -n 200 rm -f &
  done

  # 모든 백그라운드 작업이 완료될 때까지 대기
  wait
  log "✅ 썸네일 삭제 완료"
  
  # 다음 단계로 이동
  CURRENT_STEP="캐시"
  echo "CURRENT_STEP=\"캐시\"" > "$STATUS_FILE"
  echo "LAST_PROCESSED_ID=0" >> "$STATUS_FILE"
fi

# 2. 캐시 비우기 단계
if [ "$CURRENT_STEP" = "캐시" ]; then
  log "🔃 워드프레스 캐시 비우는 중..."
  cd "$WP_PATH" || { log "❌ 워드프레스 루트 폴더로 이동 실패"; exit 1; }
  wp cache flush --allow-root --path="$WP_PATH"
  log "✅ 캐시 비우기 완료"
  
  # 다음 단계로 이동
  CURRENT_STEP="재생성"
  echo "CURRENT_STEP=\"재생성\"" > "$STATUS_FILE"
  echo "LAST_PROCESSED_ID=0" >> "$STATUS_FILE"
fi

# 3. 썸네일 재생성 단계
if [ "$CURRENT_STEP" = "재생성" ]; then
  log "🚀 썸네일 재생성 중... (병렬 처리)"
  cd "$WP_PATH" || { log "❌ 워드프레스 루트 폴더로 이동 실패"; exit 1; }
  
  # --skip-delete 옵션으로 삭제 건너뛰기, --only-missing으로 누락된 것만 생성
  wp media regenerate --yes --only-missing --skip-delete --allow-root --path="$WP_PATH"
  log "✅ 썸네일 재생성 완료"
  
  # 다음 단계로 이동
  CURRENT_STEP="썸네일ID"
  echo "CURRENT_STEP=\"썸네일ID\"" > "$STATUS_FILE"
  echo "LAST_PROCESSED_ID=0" >> "$STATUS_FILE"
fi

# 4. 누락된 썸네일 ID 설정 단계
if [ "$CURRENT_STEP" = "썸네일ID" ]; then
  log "🔧 누락된 _thumbnail_id 수정 중... (배치 크기: $CHUNK_SIZE, 시작 ID: $LAST_PROCESSED_ID)"
  cd "$WP_PATH" || { log "❌ 워드프레스 루트 폴더로 이동 실패"; exit 1; }

  # PHP 코드를 배치로 실행 - 이어서 진행 지원
  wp eval "
  // 메모리 및 시간 제한 증가
  ini_set('memory_limit', '$PHP_MEMORY');
  set_time_limit(0);

  // 배치 크기 설정
  \$batch_size = $CHUNK_SIZE;
  \$offset = $LAST_PROCESSED_ID;
  \$total_processed = 0;

  while (true) {
      echo \"⏳ 배치 처리 중 (오프셋: \$offset, 배치 크기: \$batch_size)...\n\";
      
      // 배치로 포스트 가져오기
      \$posts = get_posts([
          'numberposts' => \$batch_size,
          'post_type'   => 'post',
          'offset'      => \$offset,
          'meta_query'  => [
              [
                  'key'     => '_thumbnail_id',
                  'compare' => 'NOT EXISTS'
              ]
          ]
      ]);
      
      // 더 이상 처리할 포스트가 없으면 종료
      if (empty(\$posts)) {
          echo \"✅ 모든 포스트 처리 완료 (총 \$total_processed 개)\n\";
          break;
      }
      
      \$batch_processed = 0;
      
      // 이 배치의 포스트 처리
      foreach (\$posts as \$post) {
          \$attachments = get_children([
              'post_parent' => \$post->ID,
              'post_type' => 'attachment',
              'post_mime_type' => 'image',
              'numberposts' => 1
          ]);
          
          if (\$attachments) {
              \$first = array_shift(\$attachments);
              set_post_thumbnail(\$post->ID, \$first->ID);
              echo \"✅ 썸네일 등록: {\$post->ID} -> {\$first->ID}\n\";
              \$batch_processed++;
          }
          
          // 진행 상태 저장 (10개마다)
          if (\$batch_processed % 10 == 0) {
              // 상태 파일 업데이트
              file_put_contents('/tmp/thumbnail_fix_status.txt', \"CURRENT_STEP=\\\"썸네일ID\\\"\\nLAST_PROCESSED_ID={\$offset}\\n\");
          }
      }
      
      // 배치 통계 출력
      \$total_processed += \$batch_processed;
      echo \"📊 배치 결과: \$batch_processed 포스트 처리됨 (누적: \$total_processed)\n\";
      
      // 다음 배치로 이동
      \$offset += \$batch_size;
      
      // 진행 상태 저장
      file_put_contents('/tmp/thumbnail_fix_status.txt', \"CURRENT_STEP=\\\"썸네일ID\\\"\\nLAST_PROCESSED_ID={\$offset}\\n\");
      
      // 약간의 지연으로 서버 부하 줄이기
      usleep(50000); // 0.05초 대기
  }
  " --allow-root --path="$WP_PATH"
  
  # 모든 작업 완료
  log "✅ 썸네일 ID 설정 완료"
  CURRENT_STEP="완료"
  echo "CURRENT_STEP=\"완료\"" > "$STATUS_FILE"
fi

# 모든 작업 완료
if [ "$CURRENT_STEP" = "완료" ]; then
  log "🎉 모든 작업이 완료되었습니다: $(date)"
  log "📝 로그 파일 위치: $LOG_FILE"
fi

백그라운드에서 실행하기 위한 스크립트

작업이 오래 걸릴 수 있기 때문에, 백그라운드에서 실행할 수 있는 런처 스크립트도 함께 제공합니다:

#!/bin/bash

# 백그라운드에서 썸네일 수정 스크립트 실행
nohup ./high-performance-thumbnail-fix.sh > /dev/null 2>&1 &

# 프로세스 ID 확인
PID=$!
echo "백그라운드 프로세스가 시작되었습니다 (PID: $PID)"
echo "로그 파일은 /tmp 디렉토리에서 thumbnail_fix_*.log 형식으로 확인할 수 있습니다."
echo "로그를 확인하려면: tail -f /tmp/thumbnail_fix_*.log"
echo "상태 파일: /tmp/thumbnail_fix_status.txt"
echo "중지하려면: kill $PID 또는 pkill -f 'high-performance-thumbnail-fix.sh'"

사용 방법

  1. 스크립트 파일 생성:

    # 메인 스크립트 생성
    nano high-performance-thumbnail-fix.sh
    # (위의 스크립트 코드를 붙여넣고 저장)
    chmod +x high-performance-thumbnail-fix.sh
    
    # 백그라운드 런처 생성
    nano background-launcher.sh
    # (위의 백그라운드 런처 코드를 붙여넣고 저장)
    chmod +x background-launcher.sh
    
  2. 서버 환경에 맞게 설정 조정: 스크립트 상단의 다음 값을 서버 환경에 맞게 조정합니다:

    • CHUNK_SIZE: 한 번에 처리할 포스트 수 (기본값: 1000)
    • PARALLEL_JOBS: 병렬 처리 작업 수 (서버 CPU 코어 수에 맞게 조정)
    • PHP_MEMORY: PHP 메모리 제한 (서버 메모리에 맞게 조정)
  3. 백그라운드에서 실행:

    ./background-launcher.sh
    
  4. 진행 상황 모니터링:

    # 로그 실시간 확인
    tail -f /tmp/thumbnail_fix_*.log
    
    # 상태 확인
    cat /tmp/thumbnail_fix_status.txt
    
  5. 필요시 프로세스 중지:

    # PID로 중지
    kill PID번호
    
    # 또는 이름으로 중지
    pkill -f "high-performance-thumbnail-fix.sh"
    

결과 및 이점

이 스크립트를 실행한 결과, 모든 썸네일이 성공적으로 재생성되었고 포스트와 썸네일 간의 연결도 복구되었습니다. 다음과 같은 이점을 얻었습니다:

  1. 성능 향상: 병렬 처리 덕분에 작업 시간이 크게 단축되었습니다.
  2. 안정성 향상: 중간에 스크립트가 중단되어도 이어서 진행할 수 있습니다.
  3. 리소스 효율성: 서버 사양에 맞게 최적화하여 리소스를 효율적으로 사용합니다.
  4. 자세한 로깅: 모든 작업 과정이 기록되어 문제 해결이 용이합니다.

추가 최적화 팁

워드프레스 사이트의 성능을 더욱 향상시키기 위해 다음과 같은 추가 최적화를 고려해 볼 수 있습니다:

  1. 불필요한 썸네일 크기 비활성화: functions.php에 다음 코드를 추가하여 실제로 필요한 썸네일 크기만 생성하도록 설정할 수 있습니다:

    function optimize_thumbnail_sizes() {
        // 불필요한 크기 비활성화
        update_option('medium_large_size_w', 0);
        update_option('medium_large_size_h', 0);
        
        // 불필요한 이미지 크기 제거
        remove_image_size('1536x1536');
        remove_image_size('2048x2048');
    }
    add_action('after_setup_theme', 'optimize_thumbnail_sizes', 11);
    
  2. 조건부 썸네일 생성 고려: 필요한 경우 특정 포스트에만 특정 크기의 썸네일을 생성하도록 설정할 수도 있습니다.

결론

대규모 워드프레스 사이트에서 DB 롤백 후 발생한 썸네일 문제를 해결하기 위해 고성능 스크립트를 개발했습니다. 이 스크립트는 수만 건의 이미지를 효율적으로 처리할 수 있으며, 중단 후 이어서 진행할 수 있는 기능을 제공합니다.

이 방법을 통해 사이트의 시각적 일관성을 복구하고 사용자 경험을 향상시킬 수 있었습니다. 대규모 워드프레스 사이트를 운영하면서 유사한 문제에 직면한 다른 관리자들에게도 이 솔루션이 도움이 되길 바랍니다.

댓글목록

등록된 댓글이 없습니다.