검색 결과 페이지 주소가 너무 길었습니다. 검색어 하나를 넣었을 뿐인데 URL에는 id, type, site_search_word, x, y 같은 값이 줄줄이 붙었습니다.
처음에는 보기 싫은 주소를 짧게 바꾸는 작업이라고 생각했습니다. 하지만 실제로 해보니 단순한 URL 미화 작업이 아니었습니다. 검색어를 잃지 않아야 하고, 기존 긴 URL도 살려야 하며, 내부 검색 로직은 최대한 건드리지 않아야 했습니다.
이 글은 검색 URL을 예쁘게 바꾸는 방법만 설명하는 글이 아닙니다. 오래된 PHP 검색 기능에서 내부 파라미터 이름은 그대로 두고, 외부에 보이는 주소만 /site/search?q=키워드 형태로 바꾼 과정을 정리한 글입니다.
바꾸기 전 검색 주소
기존 검색 결과 페이지 주소는 이런 형태였습니다.
/page.php?id=mysite&type=search&site_search_word=재테크&x=0&y=0주소만 봐도 길고 복잡했습니다. 공유하기도 어색했고, 검색엔진 입장에서도 어떤 구조의 페이지인지 한눈에 알기 어려웠습니다.
목표는 아래처럼 바꾸는 것이었습니다.
/mysite/search?q=재테크조건은 세 가지였습니다.
- 기존 긴 URL로 들어오는 외부 링크를 살릴 것
- 내부 검색 함수는 최대한 건드리지 않을 것
- 검색어, 페이징, 필터 값이 사라지지 않게 할 것
특히 내부 검색 코드는 오래된 상태였습니다. 검색 함수, 하이라이트 처리, 페이지네이션이 모두 site_search_word를 기준으로 움직이고 있어서 전부 q로 바꾸는 것은 위험했습니다.
내부 이름은 유지하고 입구만 변경
핵심은 내부 코드를 고치지 않는 것이었습니다. 외부 URL에서는 q를 쓰되, 내부 로직에 도달하기 전에 기존 변수명으로 옮겨 담았습니다.
<?php
if (isset($_GET['q']) && !isset($_GET['site_search_word'])) {
$_GET['site_search_word'] = $_GET['q'];
}
?>이 코드 덕분에 기존 검색 함수는 새 URL 구조를 알 필요가 없었습니다. 내부에서는 여전히 $_GET['site_search_word']를 읽고, 외부에서는 /mysite/search?q=재테크처럼 짧은 주소를 사용할 수 있습니다.
레거시 코드를 다룰 때는 이름을 전부 바꾸는 것보다 입구에 어댑터를 하나 두는 방식이 훨씬 안전했습니다.
리라이트 규칙으로 새 검색 경로 연결
먼저 /mysite/search 경로가 기존 page.php로 연결되도록 .htaccess 규칙을 추가했습니다.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([A-Za-z0-9_-]+)/search$ page.php?id=$1&type=search [L,QSA]여기서 중요한 것은 QSA입니다. 이 플래그가 있어야 /mysite/search?q=재테크로 들어온 검색어가 내부로 전달됩니다.
QSA가 빠지면 실제로는 아래처럼 됩니다.
/mysite/search?q=재테크
→ page.php?id=mysite&type=search검색어 q가 사라지기 때문에 빈 검색 결과만 나오게 됩니다. 주소는 예뻐졌지만 검색 기능은 망가지는 상태입니다.
기존 긴 검색 URL을 새 주소로 301 이동
이미 검색엔진에 잡혔거나 외부에 공유된 긴 검색 주소도 있을 수 있습니다. 그래서 기존 주소로 들어오면 새 검색 URL로 301 이동하도록 처리했습니다.
<?php
if (isset($_GET['id'], $_GET['type'], $_GET['site_search_word'])
&& $_GET['type'] === 'search'
&& strpos($_SERVER['REQUEST_URI'], 'page.php') !== false) {
$id = preg_replace('/[^_A-Za-z0-9-]/', '', $_GET['id']);
$q = rawurlencode($_GET['site_search_word']);
if ($id !== '' && $q !== '') {
header(
'Location: https://example.com/' . $id . '/search?q=' . $q,
true,
301
);
exit;
}
}
?>strpos($_SERVER['REQUEST_URI'], 'page.php') !== false 조건은 안전장치였습니다. 리라이트를 거쳐 들어온 새 주소까지 다시 리다이렉트 대상으로 잡으면 무한 이동이 생길 수 있기 때문입니다.
정리 후 흐름은 이렇게 됩니다.
/page.php?id=mysite&type=search&site_search_word=재테크
→ /mysite/search?q=재테크한글 검색어는 반드시 rawurlencode()를 거쳤습니다. 공백이나 한글이 들어간 검색어를 그대로 Location 헤더에 넣으면 주소가 깨질 수 있습니다.
검색 파라미터를 삭제 대상에서 제외
가장 크게 실수했던 부분은 쿼리스트링 정리 코드였습니다. 사이트에는 이미 허용되지 않은 파라미터를 지우고 301로 보내는 코드가 있었습니다.
새 검색 URL을 만들면서 q를 추가했지만, 허용 목록에는 넣지 않았습니다. 그래서 서버가 q=재테크를 불필요한 파라미터로 판단했고, 검색어를 지운 채 /mysite/search로 보내버렸습니다.
/mysite/search?q=재테크
→ /mysite/search검색 기능이 갑자기 빈 결과만 보여준 이유가 이것이었습니다.
수정 후에는 검색에 필요한 파라미터를 허용 목록에 넣었습니다.
<?php
$allowed = [
'id',
'type',
'q',
'site_search_word',
'start',
'page'
];
if (!empty($_SERVER['QUERY_STRING'])) {
parse_str($_SERVER['QUERY_STRING'], $params);
$extra = array_diff(array_keys($params), $allowed);
if (!empty($extra)) {
$keep = array_intersect_key($params, array_flip($allowed));
$clean = strtok($_SERVER['REQUEST_URI'], '?');
if (!empty($keep)) {
$clean .= '?' . http_build_query($keep);
}
header('Location: ' . $clean, true, 301);
exit;
}
}
?>핵심은 q, start, page 같은 값은 쓰레기 파라미터가 아니라 검색 기능에 필요한 값이라는 점입니다.
.htaccess 예외도 같이 수정
PHP 코드만 고치면 끝이 아니었습니다. .htaccess에도 쿼리스트링을 정리하는 규칙이 있으면 그쪽에서도 예외가 필요합니다.
RewriteCond %{QUERY_STRING} !^$
RewriteCond %{QUERY_STRING} !(^|&)q=
RewriteCond %{QUERY_STRING} !(^|&)start=
RewriteCond %{QUERY_STRING} !(^|&)page=
RewriteCond %{REQUEST_URI} !^/[A-Za-z0-9_-]+/search$
RewriteRule ^(.*)$ /$1? [R=301,L]PHP와 .htaccess 중 한 곳만 고치면 계속 헷갈립니다. 브라우저에서는 검색어가 사라지는데 PHP 코드에는 문제가 없어 보이는 상황이 생깁니다.
검색 파라미터를 바꿀 때는 아래 두 군데를 같이 확인해야 했습니다.
- PHP의 허용 파라미터 목록
.htaccess의 쿼리스트링 제거 예외
검색 폼 action과 name 변경
서버 쪽 처리가 끝났으면 검색 폼도 새 주소를 바라보게 바꿨습니다.
<form method="get" action="/mysite/search">
<label for="q" class="sr-only">검색어 입력</label>
<input
type="text"
id="q"
name="q"
placeholder="검색어를 입력하세요"
>
<button type="submit">검색</button>
</form>바꾼 것은 세 가지입니다.
action을/mysite/search로 변경- 검색어 입력의
name을q로 변경 - 이미지 버튼을 일반
<button>으로 변경
기존에는 검색 버튼이 이미지 입력이었습니다.
<input type="image" src="/img/search.gif" alt="검색">이 방식은 클릭 좌표를 x=0&y=0처럼 쿼리스트링에 붙일 수 있습니다. 같은 검색어인데 좌표만 다른 URL이 계속 생기는 원인이 됩니다.
그래서 일반 버튼으로 바꿨습니다.
<button type="submit" aria-label="검색">
<img src="/img/search.gif" alt="">
</button>버튼 이미지는 장식으로 처리하고, 기능 이름은 aria-label로 전달했습니다.
검색 결과 페이지 SEO 태그 분기
검색 URL을 바꿨다면 검색 결과 페이지의 title, description, canonical, H1도 같이 맞춰야 합니다.
<?php
$q = trim($_GET['q'] ?? $_GET['site_search_word'] ?? '');
$escapedQ = htmlspecialchars($q, ENT_QUOTES, 'UTF-8');
$encodedQ = rawurlencode($q);
$title = $q !== ''
? $q . ' 검색결과 - 사이트명'
: '검색 - 사이트명';
$description = $q !== ''
? '사이트명에서 ' . $q . '에 대한 검색 결과입니다.'
: '사이트명에서 원하는 내용을 검색해보세요.';
$canonical = 'https://example.com/mysite/search';
if ($q !== '') {
$canonical .= '?q=' . $encodedQ;
}
?>
<title><?= htmlspecialchars($title, ENT_QUOTES, 'UTF-8') ?></title>
<meta
name="description"
content="<?= htmlspecialchars($description, ENT_QUOTES, 'UTF-8') ?>"
>
<link
rel="canonical"
href="<?= htmlspecialchars($canonical, ENT_QUOTES, 'UTF-8') ?>"
>
<h1><?= $escapedQ ?> 검색결과</h1>검색어는 사용자 입력입니다. 따라서 화면에 출력할 때는 반드시 htmlspecialchars()를 거쳐야 합니다. 검색 결과 페이지는 XSS 문제가 생기기 쉬운 자리라 더 조심해야 했습니다.
검색 결과 페이징 처리
검색 결과가 여러 페이지로 나뉘는 경우에는 q와 start 또는 page를 같이 유지해야 합니다.
<?php
function searchPageUrl(
string $siteId,
string $keyword,
int $start = 0
): string {
$url = sprintf(
'/%s/search?q=%s',
rawurlencode($siteId),
rawurlencode($keyword)
);
if ($start > 0) {
$url .= '&start=' . $start;
}
return $url;
}
?>출력 예시는 아래와 같습니다.
<a href="/mysite/search?q=%EC%9E%AC%ED%85%8C%ED%81%AC&start=20">
다음 페이지
</a>검색어는 유지하고 페이지 값만 바뀌어야 합니다. 페이징 링크에서 q가 빠지면 다음 페이지로 넘어갔을 때 빈 검색 결과가 나옵니다.
적용 후 확인한 검색 흐름
수정 후에는 검색창에서 검색어를 넣고 끝내지 않았습니다. 실제 이동 흐름을 하나씩 확인했습니다.
curl -sIL "https://example.com/page.php?id=mysite&type=search&site_search_word=재테크" | grep -i "^HTTP\|^location"
curl -sIL "https://example.com/mysite/search?q=재테크" | grep -i "^HTTP\|^location"
curl -sIL "https://example.com/mysite/search?q=재테크&start=20" | grep -i "^HTTP\|^location"기대하는 결과는 아래와 같았습니다.
/page.php?id=mysite&type=search&site_search_word=재테크
→ 301 → /mysite/search?q=재테크
/mysite/search?q=재테크
→ 200 OK
/mysite/search?q=재테크&start=20
→ 200 OK브라우저에서도 아래 흐름을 직접 눌러봤습니다.
- 검색 폼에서 한글 검색어 입력
- 검색 결과 제목과 H1 확인
- 다음 페이지 이동 시 검색어 유지
- 쓸데없는
x,y좌표가 붙지 않는지 확인 - 긴 URL로 들어왔을 때 새 주소로 301 이동하는지 확인
- 새 URL에서 다시 검색해도 루프가 생기지 않는지 확인
적용 후 달라진 점
수정 후 검색 URL은 훨씬 짧아졌고, 기능도 유지됐습니다.
- 검색 주소가
/mysite/search?q=재테크로 정리 - 기존 긴 검색 URL은 새 주소로 301 이동
- 내부 검색 로직은
site_search_word기준 그대로 유지 - 이미지 버튼의
x,y좌표 파라미터 제거 - 검색 결과
title,description,H1분기 - 검색 페이징에서
q값 유지
가장 중요한 것은 검색 기능을 건드리지 않고도 외부 URL만 바꿨다는 점입니다. 오래된 검색 코드를 무리하게 고치지 않아도 원하는 주소 구조를 만들 수 있었습니다.
검색 URL 변경 체크리스트
내부 코드는 그대로 두고 입구만 바꾸기
레거시 검색 기능에서 파라미터 이름을 바꾸는 일은 생각보다 위험했습니다. 검색어 하나만 바꾸는 것처럼 보여도 내부 검색 함수, 페이지네이션, 하이라이트, 메타 태그까지 줄줄이 연결되어 있었기 때문입니다.
제가 선택한 방식은 내부를 전부 뜯는 것이 아니라 입구에서 한 번 변환하는 것이었습니다. 외부 URL은 q로 받고, 내부에서는 기존의 site_search_word를 계속 쓰게 했습니다.
이 방식은 화려하지 않지만 안전했습니다. 검색 기능은 그대로 유지하면서 URL만 짧게 바꿀 수 있었고, 기존 긴 URL도 301로 새 주소에 연결할 수 있었습니다.
검색 URL을 바꿀 때는 새 주소만 보지 말고, 검색어가 어디서 들어와 어디까지 유지되는지 따라가야 합니다. 특히 쿼리스트링 정리 코드가 있는 사이트라면 q를 허용 목록에 넣는 것까지가 작업의 일부였습니다.