쿠키를 설정하는 건 간단해 보인다. setcookie() 함수 한 줄이면 끝이니까. 다만 대부분의 개발자들은 쿠키가 제대로 저장되지 않는 상황을 겪으면서 막연히 에러 메시지나 구글링에만 의존한다. 정작 쿠키가 왜 작동하지 않는지, 어떤 조건이 필요한지는 모른 채로.
이번에는 PHP에서 setcookie()가 실패하는 가장 흔한 5가지 원인을 정확히 파악하고, 각각을 어떻게 해결하는지 실전 코드로 소개하겠다.
setcookie()는 HTTP 헤더의 Set-Cookie에 값을 담아 브라우저로 전송하는 함수다. 여기서 핵심은 '헤더'다. PHP에서 응답 본문이 한 글자라도 출력되면 헤더 영역이 닫혀버리고, 그 이후로는 어떤 헤더 함수도 작동하지 않는다.
setcookie('user_id', '12345', time() + 3600);
echo '로그인 되었습니다'; // 이 문장이 출력되면서 헤더 전송 종료
즉, setcookie()는 반드시 <?php 태그 직후, 다른 코드가 실행되기 전에 호출되어야 한다.
✗ 잘못된 코드: 헤더 출력 후에 쿠키 설정 시도
<?php
echo 'Welcome'; // 먼저 출력됨 = 헤더 닫힘
setcookie('user_id', '12345', time() + 3600); // 이미 늦음 - 에러 발생
?>
<html>
<body>..</body>
</html>
✓ 올바른 코드: 모든 출력 전에 쿠키 설정
<?php
setcookie('user_id', '12345', time() + 3600);
setcookie('session_token', 'abc123', time() + 3600);
// 헤더 함수들은 모두 이 위치에서 끝내야 함
?>
<html>
<head></head>
<body>
<?php echo 'Welcome'; ?>
</body>
</html>
결과: 쿠키가 정상 설정되고 브라우저의 Developer Tools → Application → Cookies에서 확인 가능
✗ 잘못된 코드: 파일 시작 전에 공백이나 BOM이 있는 경우
<?php // 이 앞에 3개 공백이 있음 = 출력됨
setcookie('user_id', '12345', time() + 3600);
?>
✓ 올바른 코드: 파일 맨 처음부터 <?php 시작 (공백, 빈 줄, BOM 문자 없음)
<?php
setcookie('user_id', '12345', time() + 3600);
?>
해결 방법: 텍스트 에디터에서 인코딩을 'UTF-8 (BOM 없음)' 또는 'UTF-8'로 설정하고 파일을 저장
✗ 잘못된 코드: header.php에서 미리 출력
// header.php
<?php
echo '<nav>헤더</nav>'; // 출력 발생
?>
// index.php
<?php
include 'header.php'; // 이미 출력됨
setcookie('user_id', '12345', time() + 3600); // 이제 너무 늦음
?>
✓ 올바른 코드: include 전에 모든 쿠키/헤더 설정
// index.php
<?php
setcookie('user_id', '12345', time() + 3600);
include 'header.php'; // 이제 안전함
?>
✗ 잘못된 코드: 도메인과 경로 지정 누락
// example.com/admin/login.php에서 실행
setcookie('user_id', '12345', time() + 3600);
// 현재 경로(/admin/)에서만 유효한 쿠키 생성
// 다른 경로에서는 이 쿠키가 전송되지 않음
✓ 올바른 코드: 도메인과 경로 명시
setcookie(
'user_id',
'12345',
time() + 3600,
'/', // 모든 경로에서 유효
'.example.com', // 모든 서브도메인에서 유효
true, // HTTPS만 사용
true // HttpOnly (JavaScript 접근 불가)
);
결과: 쿠키가 모든 페이지에서 일관되게 전송됨
PHP 7.3 이상에서 setcookie()는 옵션 배열을 지원한다. SameSite 속성이 'Strict'이면 외부 사이트 링크를 통해 들어올 때 쿠키가 전송되지 않는다.
✗ 잘못된 코드: SameSite=Strict (너무 엄격함)
setcookie('user_id', '12345', [
'expires' => time() + 3600,
'path' => '/',
'samesite' => 'Strict' // 외부 사이트에서 들어오면 쿠키 미전송
]);
✓ 올바른 코드: SameSite=Lax (균형잡힌 설정)
setcookie('user_id', '12345', [
'expires' => time() + 3600,
'path' => '/',
'samesite' => 'Lax' // 안전한 요청(GET)에서는 쿠키 전송, POST는 제한
]);
또는 최신 방식:
setcookie('user_id', '12345', [
'expires' => time() + 3600,
'path' => '/',
'domain' => '.example.com',
'secure' => true, // HTTPS만
'httponly' => true, // JavaScript 접근 방지
'samesite' => 'Lax' // CSRF 공격 방지하면서 사용성 유지
]);
완전한 로그인 흐름에서 쿠키를 올바르게 사용하는 예제:
<?php
session_start();
// POST 요청 처리 (로그인)
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['login'])) {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
// 여기서 DB 검증 (생략)
if ($username === 'admin' && $password === 'pass123') {
// 쿠키 설정 (반드시 echo 전에)
setcookie('user_id', '1', [
'expires' => time() + (7 * 24 * 3600), // 7일
'path' => '/',
'domain' => '.example.com',
'secure' => true, // HTTPS 환경에서만
'httponly' => true, // XSS 공격 방지
'samesite' => 'Lax'
]);
// 세션도 함께 사용
$_SESSION['user_id'] = '1';
$_SESSION['username'] = $username;
header('Location: /dashboard.php');
exit();
} else {
$error = '로그인 실패';
}
}
?>
<!DOCTYPE html>
<html>
<head></head>
<body>
<?php if (isset($error)) echo "<p>$error</p>"; ?>
<form method="POST">
<input type="text" name="username" placeholder="사용자명">
<input type="password" name="password" placeholder="암호">
<button type="submit" name="login">로그인</button>
</form>
</body>
</html>
| 실수 | 문제점 | 해결책 |
|---|---|---|
| 암호화 없이 민감 데이터 저장 | XSS/중간자 공격으로 쿠키 탈취 | httponly=true, secure=true 설정, 중요 데이터는 세션에만 저장 |
| 쿠키에 비밀번호 저장 | 브라우저 DevTools에서 노출 | 세션 ID만 쿠키에, 실제 사용자 정보는 서버 세션에 저장 |
| expires 값 누락 | 브라우저 종료 시 쿠키 삭제됨 | 명시적으로 시간 설정 (time() + 3600 등) |
| localhost에서 domain='.example.com' 테스트 | 로컬 개발 환경에서 쿠키 안 저장됨 | /etc/hosts에 127.0.0.1 local.example.com 추가 후 테스트 |
✓ 브라우저 DevTools에서 확인:
1. F12 → Application 탭 → Cookies
2. 현재 도메인의 쿠키 목록 확인
3. Name, Value, Expires, Domain, Path, HttpOnly, Secure 검증
✓ PHP에서 확인:
<?php
// 쿠키 설정 후 다음 페이지 새로고침하면 $_COOKIE에 나타남
var_dump($_COOKIE);
// 또는 특정 쿠키만 확인
if (isset($_COOKIE['user_id'])) {
echo '쿠키 저장됨: ' . htmlspecialchars($_COOKIE['user_id']);
} else {
echo '쿠키 미저장';
}
?>
setcookie()는 간단하지만, 작동 원리를 모르면 끊임없이 실패한다. 헤더 전송 시점, 출력 순서, 도메인/경로 설정, SameSite 속성까지 이 다섯 가지만 기억해도 90% 이상의 쿠키 문제를 해결할 수 있다. 특히 로그인이나 세션 관리가 필요한 프로젝트에서는 이 글의 '올바른 코드' 섹션을 그대로 복사해 사용하면, 보안성과 호환성 모두 확보할 수 있을 것이다.