쿠키를 설정하는 건 간단해 보인다. setcookie() 함수 한 줄이면 끝이니까. 다만 대부분의 개발자들은 쿠키가 제대로 저장되지 않는 상황을 겪으면서 막연히 에러 메시지나 구글링에만 의존한다. 정작 쿠키가 왜 작동하지 않는지, 어떤 조건이 필요한지는 모른 채로.

이번에는 PHP에서 setcookie()가 실패하는 가장 흔한 5가지 원인을 정확히 파악하고, 각각을 어떻게 해결하는지 실전 코드로 소개하겠다.

 

1단계: setcookie()의 기본 동작 원리 이해하기

setcookie()는 HTTP 헤더의 Set-Cookie에 값을 담아 브라우저로 전송하는 함수다. 여기서 핵심은 '헤더'다. PHP에서 응답 본문이 한 글자라도 출력되면 헤더 영역이 닫혀버리고, 그 이후로는 어떤 헤더 함수도 작동하지 않는다.

setcookie('user_id', '12345', time() + 3600);
echo '로그인 되었습니다'; // 이 문장이 출력되면서 헤더 전송 종료

즉, setcookie()는 반드시 <?php 태그 직후, 다른 코드가 실행되기 전에 호출되어야 한다.

 

2단계: 실패하는 5가지 원인과 해결법

 

❌ 원인 1: 출력 전에 setcookie() 호출 안 함 (가장 흔한 실수)

✗ 잘못된 코드: 헤더 출력 후에 쿠키 설정 시도

<?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에서 확인 가능

 

❌ 원인 2: 공백이나 BOM 문자가 파일 맨 앞에 있음

✗ 잘못된 코드: 파일 시작 전에 공백이나 BOM이 있는 경우

   <?php // 이 앞에 3개 공백이 있음 = 출력됨
setcookie('user_id', '12345', time() + 3600);
?>

✓ 올바른 코드: 파일 맨 처음부터 <?php 시작 (공백, 빈 줄, BOM 문자 없음)

<?php
setcookie('user_id', '12345', time() + 3600);
?>

해결 방법: 텍스트 에디터에서 인코딩을 'UTF-8 (BOM 없음)' 또는 'UTF-8'로 설정하고 파일을 저장

 

❌ 원인 3: include/require된 파일이 출력을 먼저 함

✗ 잘못된 코드: 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'; // 이제 안전함
?>

 

❌ 원인 4: 도메인/경로 설정이 잘못됨 (쿠키가 저장되지만 전송 안 됨)

✗ 잘못된 코드: 도메인과 경로 지정 누락

// 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 접근 불가)
);

결과: 쿠키가 모든 페이지에서 일관되게 전송됨

 

❌ 원인 5: SameSite 속성이 너무 엄격함 (크로스사이트 요청에서 쿠키 미전송)

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 공격 방지하면서 사용성 유지
]);

 

3단계: 실전 예제 - 로그인 쿠키를 제대로 설정하는 법

완전한 로그인 흐름에서 쿠키를 올바르게 사용하는 예제:

<?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>

 

4단계: 주의사항 - 흔한 실수와 보안
실수 문제점 해결책
암호화 없이 민감 데이터 저장 XSS/중간자 공격으로 쿠키 탈취 httponly=true, secure=true 설정, 중요 데이터는 세션에만 저장
쿠키에 비밀번호 저장 브라우저 DevTools에서 노출 세션 ID만 쿠키에, 실제 사용자 정보는 서버 세션에 저장
expires 값 누락 브라우저 종료 시 쿠키 삭제됨 명시적으로 시간 설정 (time() + 3600 등)
localhost에서 domain='.example.com' 테스트 로컬 개발 환경에서 쿠키 안 저장됨 /etc/hosts에 127.0.0.1 local.example.com 추가 후 테스트

 

5단계: 디버깅 팁 - 쿠키가 작동하는지 확인하는 법

✓ 브라우저 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% 이상의 쿠키 문제를 해결할 수 있다. 특히 로그인이나 세션 관리가 필요한 프로젝트에서는 이 글의 '올바른 코드' 섹션을 그대로 복사해 사용하면, 보안성과 호환성 모두 확보할 수 있을 것이다.