PHP 버퍼 캐시 이슈

Seongjin Jo·2025년 4월 23일
0

PHP 리다이렉트와 캐시 문제 분석

캐시에 의한 웹 동작 차이 분석


목차

  1. 문제 상황 개요
  2. 정상 동작 프로세스 (최초 요청)
  3. 문제 발생 프로세스 (재요청)
  4. 원인 분석
  5. 핵심 개념 설명
  6. 해결 방안

1. 문제 상황 개요

  • 테스트서버: 리다이렉트가 정상 동작
  • 실서버: 최초 1회만 리다이렉트가 작동하고 이후 작동하지 않음
  • 현상: 같은 코드이지만 환경에 따라 다른 동작을 보임
  • 원인: 캐시 사용 여부에 따른 차이

2. 정상 동작 프로세스 (최초 요청)

메뉴의 다시보기 클릭 → "tv_sub/552/2636" 요청

최초 요청 프로세스

  1. 캐시 파일이 없으므로 ob_start() 실행
  2. 응답 페이지 구성 시작 (헤더 + 바디)
  3. 헤더에 301 리다이렉트와 Location: "tv/552/" 포함
  4. 페이지 내용은 출력 버퍼에 저장됨
  5. 응답 시 헤더의 Location으로 리다이렉트 발생
  6. 동시에 출력 버퍼 내용은 캐시 파일로 저장됨

3. 문제 발생 프로세스 (재요청)

메뉴의 다시보기 클릭 → "tv_sub/552/2636" 재요청

재요청 프로세스

  1. 캐시 파일이 존재함을 확인
  2. 캐시 파일을 echo로 즉시 출력
  3. exit 명령으로 이후 코드 실행 중단
  4. 이 과정에서 리다이렉트를 위한 헤더 설정 코드가 실행되지 않음
  5. 결과적으로 Location 헤더가 없어 리다이렉트 발생하지 않음
  6. 원래 요청한 "tv_sub/552/2636" 페이지가 그대로 표시됨

4. 원인 분석

핵심 원인:

  • 캐시 사용 시 echo로 내용 출력 후 exit로 종료
  • 이로 인해 리다이렉트 헤더가 설정되기 전에 응답이 완료됨
// 캐시 처리 관련 코드 예시
if (file_exists($cache_file)) {
    echo file_get_contents($cache_file);
    exit; // 여기서 코드 실행이 중단됨
}

// 아래 코드는 캐시 파일이 있으면 실행되지 않음
ob_start();
// 페이지 콘텐츠 생성...
header("HTTP/1.1 301 Moved Permanently");
header("Location: tv/552/");
// 나머지 코드...

5. 핵심 개념 설명

PHP의 출력 버퍼링 (ob_start())

  • 출력을 바로 전송하지 않고 버퍼에 저장
  • 버퍼 내용은 스크립트 종료 시 또는 명시적 호출 시 전송
  • 중요: 버퍼링은 출력 내용(body)만 제어하며 헤더는 제어하지 않음

헤더와 출력의 관계

  • PHP에서 echo 등으로 출력이 시작되면 헤더도 함께 전송됨
  • 헤더는 항상 본문(body) 이전에 전송되어야 함
  • 출력이 시작된 후에는 header() 함수가 작동하지 않음

6. 해결 방안

가능한 해결책:

  1. 캐시 파일에 헤더 정보도 포함시키기

    • 캐시 데이터에 리다이렉트 정보도 저장
  2. 캐시 파일 출력 전에 헤더 설정하기

    if (file_exists($cache_file)) {
        header("HTTP/1.1 301 Moved Permanently");
        header("Location: tv/552/");
        echo file_get_contents($cache_file);
        exit;
    }
  3. 조건부 캐시 사용

    • 리다이렉트가 필요한 페이지는 캐시를 사용하지 않거나
    • 리다이렉트 정보를 별도로 확인하여 처리

질문에 대한 답변

"exit이 없으면 어떻게 되나요?"

  • exit이 없어도 echo가 실행되면 헤더가 이미 전송됨
  • 이후 header() 함수는 효과가 없음 ("Headers already sent" 오류)
  • 단, 이후 코드는 계속 실행되어 추가 출력이나 로직이 동작함

"ob_start()는 header 값을 담을 수 없나요?"

  • ob_start()는 출력 내용(body)만 버퍼링함
  • 헤더 정보는 별도로 관리되며 버퍼링되지 않음
  • 출력 버퍼는 본문 콘텐츠만 제어할 뿐임

정리

  1. 문제 원인: 캐시 사용 시 리다이렉트 헤더를 설정하기 전에 출력과 종료가 발생
  2. PHP 특성: 출력 시작 시 헤더도 함께 전송됨
  3. 해결 방향: 캐시 처리 전에 필요한 헤더 설정 또는 캐시에 헤더 정보 포함
  4. 교훈: 출력 버퍼링과 헤더 관리는 별개의 메커니즘임을 이해해야 함

원본

[ 상황 정리 ]
테스트서버에서는 제대로 리다이렉트가 동작한다. 근데, 실서버에서는 리다이렉트가 최초 1회만 동작하고 제대로 동작하지않는다.
뭣도 모르고 됏다가 안됏다가 하는데 거의 안되네라고 생각했다. ㅎ.

1) 최초 요청 (캐시 동작 x, 실서버 기준)

  • 메뉴의 다시보기를 클릭 -> "tv_sub/552/2636" 으로 요청이 된다.
  • "tv_sub/552/2636" 에 진입해서 캐시가 없으면 ob_start 시작된다.
    그와 동시에 요청에 따른 응답 페이지(응답할 헤더와 바디)를 구성한다. (header에 리다이렉트 301 요청과 Location ("tv/552/") 을 포함한다)
    그리고 ob_start에 body의 내용들이 쌓이게 된다. (이때 header의 정보는 쌓이지 못한다)
  • 페이지를 모두 구성하면 브라우저(Reqest)에 Response 한다. Response 하면 header에 Location이 값이 담겨 있기 때문에 바로 "tv/552/"로 리다이렉트
    이와 동시에 캐시파일에도 해당 페이지의 내용이 담겨 저장된다.

2) 재 요청 (캐시 동작 o, 실서버 기준)

  • 메뉴의 다시보기를 클릭 -> "tv_sub/552/2636" 으로 요청이 된다.
  • 응답할 페이지를 구성하기 위해서 위에서부터 아래로 순차적으로 스캔하면서 상단에 캐시파일 로직을 지나는데 캐시파일이 존재하기 때문에
    그냥 캐시파일을 echo로 뿌려버리고 exit 한다. 이렇게 되면 Response body와 header를 그 순간 그 상태로 응답해버린다. 그렇기 때문에 header에
    리다이렉트를 해야할 Location 정보를 담지 못한다.
  • 그래서 응답 페이지의 header에는 Location정보가 없어 리다이렉트 하지 않고, 그냥 요청한 "tv_sub/552/2636" 페이지가 캐시에 의해 응답된다.

[ 궁금한 점 ]

  1. 재요청 상황에서 echo로 뿌려버리고 exit 한다. 라고 작성 했는데 exit이 없으면? 어떻게?
    -> php는 echo출력과 동시에 header 정보도 함께 응답해버린다. 그리고 exit을 해야 뒤에 내용들이 동작을 안한다. exit이 없어도 echo가 있으면 header 정보도 바로 응답이 되기 때문에 이후의 header()는 무용지물!
  2. ob_start()는 header 값을 담을 수 없나?
    -> 출력 제어만 가능할 뿐이다. 원한다면 내용 출력이전에 header를 셋팅해라!

0개의 댓글