게시판 2페이지가 1페이지로 돌아간 이유 | start 파라미터 301 오류

게시판에서 2페이지를 눌렀는데 계속 1페이지로 돌아오는 일이 있었습니다. 게시판 목록 코드는 건드린 적이 없어서 처음에는 원인을 다른 곳에서 찾았습니다. 하지만 실제 문제는 며칠 전에 넣어둔 301 리다이렉트 규칙이었습니다.

중복 URL을 줄이려고 만든 규칙이 페이지네이션에 필요한 start 파라미터까지 지워버린 것입니다. 검색엔진에 보여주고 싶지 않은 쓸데없는 파라미터를 정리하려다가, 사용자가 실제로 쓰는 기능까지 같이 막아버린 사례였습니다.

이 글은 페이지네이션 오류 자체보다 쿼리스트링을 정리할 때 어떤 파라미터를 남겨야 하는지를 다룹니다. 특히 오래된 PHP 게시판이나 직접 만든 CMS에서 301 리다이렉트를 넣을 때 꼭 확인해야 할 부분입니다.

start 파라미터를 유지해 게시판 페이지네이션 오류를 막는 301 리다이렉트 코드

2페이지를 눌러도 1페이지로 돌아가는 증상

처음 보인 증상은 단순했습니다.

  • 게시판 1페이지는 정상적으로 열림
  • 2페이지 버튼을 누르면 다시 1페이지로 돌아옴
  • 검색 결과 목록에서도 다음 페이지로 이동하지 않음
  • 주소창에 ?start=30이 잠깐 보였다가 사라짐
  • 서버 에러 로그에는 아무 내용도 남지 않음

예를 들면 이런 흐름이었습니다.

/board/856              → 정상
/board/856?start=30     → 다시 /board/856
/search?keyword=test&start=30 → 다시 /search?keyword=test

사용자 입장에서는 “2페이지 버튼이 고장났다”고 느끼지만, 서버 입장에서는 정상적으로 301 리다이렉트를 처리한 상태입니다. 그래서 에러 로그만 보고는 문제를 찾기 어렵습니다.

네트워크 탭에서 바로 보인 301 흐름

가장 먼저 확인한 것은 브라우저 개발자 도구의 네트워크 탭이었습니다. Preserve log를 켜고 2페이지 버튼을 눌러보니 원인이 바로 보였습니다.

301 GET /board/856?start=30
200 GET /board/856

브라우저는 분명히 /board/856?start=30으로 요청했습니다. 그런데 서버의 리다이렉트 규칙이 start=30을 불필요한 쿼리스트링으로 판단했고, 최종적으로 /board/856으로 보내버렸습니다.

페이지네이션에서 start=30은 장식이 아닙니다. “몇 번째 글부터 보여줄지”를 결정하는 값입니다. 이 값이 사라지면 게시판은 항상 기본값인 1페이지를 보여줍니다.

검색 결과 페이지에서도 비슷한 일이 생깁니다.

/search?keyword=php&start=30
→ /search?keyword=php

검색어는 남아 있어도 start가 사라지면 검색 결과의 2페이지, 3페이지로 이동할 수 없습니다.

원인은 쿼리스트링을 무조건 지운 규칙

문제가 된 규칙은 “쿼리스트링이 있으면 깨끗한 주소로 301 이동”시키는 방식이었습니다.

개념만 보면 이렇게 단순합니다.

주소에 ?가 붙어 있다
→ 중복 URL일 수 있다
→ ? 뒤를 제거한다
→ 301로 보낸다

이 방식은 fbclid, utm_source, ckattempt 같은 불필요한 파라미터를 제거할 때는 효과가 있습니다. 하지만 모든 파라미터를 같은 취급으로 지우면 문제가 생깁니다.

게시판에서 start는 페이지를 바꾸는 값입니다. 검색에서 keyword나 q는 결과를 바꾸는 값입니다. 카테고리 목록에서 category, 정렬 목록에서 sort도 화면 내용을 바꾸는 값입니다.

즉, 중복 URL을 만드는 파라미터와 기능에 필요한 파라미터를 먼저 나누지 않은 것이 원인이었습니다.

지워도 되는 파라미터와 남겨야 하는 파라미터

쿼리스트링 정리에서 가장 중요한 기준은 “이 값이 페이지 내용을 바꾸는가?”입니다.

구분예시처리 방법
페이지 내용을 바꾸는 파라미터start, page, keyword, q, category, sort유지
추적·유입 확인용 파라미터utm_source, utm_medium, fbclid, gclid제거 가능
의미 없는 임의 파라미터ckattempt, test, abc제거 가능

중복 콘텐츠를 만드는 것은 보통 두 번째와 세 번째입니다. 첫 번째를 같이 지우면 SEO 문제가 아니라 기능 오류가 됩니다.

특히 오래된 게시판은 페이지마다 파라미터 이름이 다를 수 있습니다. 어떤 목록은 start를 쓰고, 다른 목록은 page를 쓰고, 검색창은 keyword 대신 q를 쓸 수도 있습니다. 그래서 코드를 넣기 전에 실제 URL을 먼저 확인해야 합니다.

해결 방법은 허용 목록으로 다시 짜는 것

수정 방향은 간단했습니다. “지울 파라미터”를 찾는 방식이 아니라, “남길 파라미터”를 먼저 정하는 방식으로 바꿨습니다.

$allowedParams = [
    'start',     // 게시판 페이지네이션
    'page',      // 페이지 번호 방식
    'keyword',   // 게시판 검색어
    'q',         // 통합 검색어
    'category',  // 분류
    'sort',      // 정렬
];

그 다음 허용 목록에 없는 파라미터가 섞여 있을 때만 리다이렉트하도록 했습니다.

$allowedParams = ['start', 'page', 'keyword', 'q', 'category', 'sort'];

if (!empty($_SERVER['QUERY_STRING'])) {
    parse_str($_SERVER['QUERY_STRING'], $params);

    $extraParams = array_diff(array_keys($params), $allowedParams);

    if (!empty($extraParams)) {
        $keepParams = array_intersect_key($params, array_flip($allowedParams));
        $cleanUrl = strtok($_SERVER['REQUEST_URI'], '?');

        if (!empty($keepParams)) {
            $cleanUrl .= '?' . http_build_query($keepParams);
        }

        header('Location: ' . $cleanUrl, true, 301);
        exit;
    }
}

이렇게 하면 아래처럼 동작합니다.

/board/856?start=30
→ 그대로 유지

/board/856?start=30&fbclid=abc
→ /board/856?start=30

/search?keyword=php&start=30&utm_source=test
→ /search?keyword=php&start=30

불필요한 값은 제거하지만, 페이지네이션과 검색에 필요한 값은 남기는 구조입니다.

짧은 URL로 보낼 때도 start를 빼면 안 된다

또 하나 놓치기 쉬운 부분은 URL 구조를 바꾸는 리다이렉트입니다. 예를 들어 예전 주소를 새 주소로 보내는 코드가 있다고 가정해보겠습니다.

$newUrl = '/board/' . $boardNo;

header('Location: ' . $newUrl, true, 301);
exit;

이 코드는 1페이지에서는 문제가 없어 보입니다. 하지만 기존 요청이 /old_board.php?id=856&start=30이었다면 start 값이 사라집니다. 그러면 사용자는 새 주소로 이동하더라도 다시 1페이지를 보게 됩니다.

페이지 위치를 유지하려면 새 주소를 만들 때도 start를 함께 붙여야 합니다.

$newUrl = '/board/' . $boardNo;

$query = [];

if (isset($_GET['keyword']) && $_GET['keyword'] !== '') {
    $query['keyword'] = $_GET['keyword'];
}

if (isset($_GET['start']) && $_GET['start'] !== '') {
    $query['start'] = $_GET['start'];
}

if (!empty($query)) {
    $newUrl .= '?' . http_build_query($query);
}

header('Location: ' . $newUrl, true, 301);
exit;

301 리다이렉트는 주소만 바꾸는 작업처럼 보이지만, 실제로는 사용자의 현재 위치도 함께 옮겨야 합니다. 검색어, 페이지 번호, 카테고리 같은 값이 빠지면 “이동”이 아니라 “초기화”가 됩니다.

.htaccess 규칙도 예외 조건이 필요하다

PHP 코드만 수정해도 해결되는 경우가 있지만, .htaccess에서 쿼리스트링을 지우는 규칙이 따로 있다면 그쪽도 확인해야 합니다.

예를 들어 아래처럼 쿼리스트링을 무조건 제거하는 규칙은 위험합니다.

RewriteCond %{QUERY_STRING} !^$
RewriteRule ^(.*)$ /$1? [R=301,L]

이 규칙은 ?start=30도 지우고, ?keyword=php도 지웁니다. 그래서 검색과 페이지네이션이 함께 망가질 수 있습니다.

최소한 기능에 필요한 파라미터는 예외로 빼야 합니다.

# 기능에 필요한 파라미터가 있으면 쿼리스트링 제거를 건너뜀
RewriteCond %{QUERY_STRING} !^$
RewriteCond %{QUERY_STRING} !(^|&)start=
RewriteCond %{QUERY_STRING} !(^|&)page=
RewriteCond %{QUERY_STRING} !(^|&)keyword=
RewriteCond %{QUERY_STRING} !(^|&)q=
RewriteCond %{QUERY_STRING} !(^|&)category=
RewriteCond %{QUERY_STRING} !(^|&)sort=
RewriteRule ^(.*)$ /$1? [R=301,L]

다만 이 방식은 “해당 파라미터가 하나라도 있으면 전체 쿼리스트링 제거를 건너뛰는” 형태입니다. 더 세밀하게 불필요한 값만 제거하려면 PHP나 애플리케이션 레벨에서 허용 목록 방식으로 처리하는 편이 관리하기 쉽습니다.

2페이지 색인은 리다이렉트로 해결하지 않는다

페이지네이션을 살리고 나면 한 가지 고민이 생깁니다. 2페이지, 3페이지 같은 목록 페이지가 검색엔진에 잡히는 것이 괜찮은지입니다.

여기서 다시 리다이렉트로 1페이지에 몰아버리면 안 됩니다.

/board/856?start=30
→ /board/856

이렇게 처리하면 검색엔진 입장에서는 주소가 정리될 수 있지만, 사용자 입장에서는 2페이지를 볼 수 없습니다.

페이지네이션 URL은 실제로 다른 목록을 보여줍니다. 따라서 기능을 없애는 301이 아니라, 페이지 성격에 맞는 색인 정책을 따로 정해야 합니다.

예를 들어 검색 결과 페이지라면 noindex, follow를 고려할 수 있습니다.

<meta name="robots" content="noindex, follow">

게시판 목록 2페이지를 색인시킬지 말지는 사이트 구조에 따라 다릅니다. 중요한 것은 “색인시키고 싶지 않다”는 이유만으로 사용자가 이동해야 할 URL을 301로 없애면 안 된다는 점입니다.

배포 후 확인한 테스트 항목

수정 후에는 단순히 1페이지가 열리는지만 보지 않았습니다. 실제 사용자가 누를 만한 흐름을 기준으로 다시 확인했습니다.

  • 게시판 2페이지, 3페이지 이동 확인
  • 검색 결과에서 다음 페이지 이동 확인
  • 검색어와 start 값이 동시에 유지되는지 확인
  • fbclid, utm_source, ckattempt 같은 불필요한 값이 제거되는지 확인
  • 모바일 화면에서도 페이지네이션이 같은 방식으로 동작하는지 확인
  • 개발자 도구 네트워크 탭에서 301 전후 URL 확인
  • 서치콘솔에서 중복 URL이 다시 늘어나지 않는지 확인

특히 네트워크 탭에서 아래 흐름을 직접 확인하는 것이 중요했습니다.

/board/856?start=30&fbclid=abc
301 → /board/856?start=30
200 → 2페이지 정상 표시

이 흐름이 확인되어야 “불필요한 파라미터는 지우고, 필요한 파라미터는 살렸다”고 볼 수 있습니다.

리다이렉트 적용 전 점검표

301 리다이렉트 규칙을 넣기 전에 아래 항목은 꼭 확인하는 것이 좋습니다.

결국 파라미터를 지우기 전에 역할부터 나눠야 한다

이번 문제는 게시판 페이지네이션 오류처럼 보였지만, 실제로는 파라미터 분류를 하지 않은 리다이렉트 문제였습니다.

start는 쓸데없는 값이 아니라 페이지 위치를 나타내는 값이었습니다. keyword는 중복 URL을 만드는 장식이 아니라 검색 결과를 만드는 값이었습니다. 이런 값을 지워버리면 SEO 정리가 아니라 기능 삭제가 됩니다.

중복 URL을 줄이는 작업은 필요합니다. 하지만 쿼리스트링을 모두 같은 취급으로 지우면 검색, 페이지네이션, 필터처럼 사용자가 직접 만지는 기능이 조용히 멈출 수 있습니다.

리다이렉트 규칙을 넣기 전에는 먼저 파라미터 목록을 적어보는 것이 좋습니다. 어떤 값이 화면을 바꾸는지, 어떤 값이 추적용인지, 어떤 값이 의미 없는 값인지 나누고 나서 301을 걸어야 합니다.

검색엔진을 위한 정리는 사용자가 페이지를 정상적으로 이동할 수 있을 때 의미가 있습니다. SEO 수정은 주소가 깨끗해지는 것에서 끝나는 것이 아니라, 실제 기능 테스트까지 끝나야 완료된 것입니다.

댓글 남기기