← 전체 글로 돌아가기

웹 개발

로그인이 풀렸을 때 원인 찾기

사용자가 갑자기 로그인 상태를 잃으면 세션, 토큰, 쿠키 중 어디가 문제인지 확인해야 합니다.

사용자가 "왜 자꾸 로그인이 풀려?"라고 물어온다. 버튼을 누른 후 깜깜한 에러만 떴다고 한다. 문제는 여러 곳에 숨어 있을 수 있다.

에러 메시지부터 읽기

UI에 에러가 없어도 콘솔에는 있다. Network 탭, Console 탭을 켜고 로그인 상태가 풀리는 순간을 다시 해본다.

  • 401 응답이 떠도 UI 에러가 없을 수 있다
  • 토큰이 만료된 건 아닌가
  • 에러 응답에 무엇이 적혀 있나
// 요청 전
console.log('토큰:', localStorage.getItem('auth_token'));
console.log('쿠키:', document.cookie);

// API 요청 후
fetch('/api/user')
  .then(res => {
    console.log('응답 상태:', res.status);
    console.log('응답 헤더:', res.headers);
    return res.json();
  })
  .catch(err => console.error('요청 실패:', err));

토큰 확인하기

JWT 토큰이라면 내용을 디코딩할 수 있다.

const token = localStorage.getItem('auth_token');
const parts = token.split('.');
const payload = JSON.parse(atob(parts[1]));

console.log('토큰 만료 시간:', new Date(payload.exp * 1000));
console.log('지금:', new Date());
console.log('만료됐나?', payload.exp * 1000 < Date.now());

토큰이 만료되면 새로운 토큰을 요청해야 한다. 많은 앱은 "Refresh Token"을 사용한다.

쿠키 vs localStorage 확인

인증 정보가 쿠키에 있는가, localStorage에 있는가?

// localStorage 확인
console.log('localStorage의 모든 항목:');
for (let key in localStorage) {
  console.log(key, localStorage[key].substring(0, 20) + '...');
}

// 쿠키 확인
console.log('쿠키:', document.cookie);

// 또는 개발자도구에서 Application → Cookies 탭

쿠키에 로그인 정보가 있다면 HttpOnly 플래그가 설정되면 자바스크립트에서 접근할 수 없다. (보안상 좋은 일)

요청 헤더 확인

API 요청을 보낼 때 인증 정보를 제대로 보내는가?

// 나쁜 예: 토큰을 안 보낸다
fetch('/api/user')

// 낫다: Authorization 헤더에 토큰을 넣는다
const token = localStorage.getItem('auth_token');
fetch('/api/user', {
  headers: {
    'Authorization': `Bearer ${token}`
  }
})

Network 탭에서 요청 헤더를 확인하면 Authorization 헤더가 있는가?

요청 시점이 늦은가

앱이 시작되는 동안 인증 정보를 확인하는 시간이 있다. 그 전에 API를 호출하면 로그인되지 않은 상태다.

// 나쁜 예: 즉시 요청한다
setItems([]);
fetch('/api/items'); // 아직 로그인 안 됨

// 낫다: 로그인 확인 후 요청한다
useEffect(() => {
  checkAuth().then(() => {
    fetch('/api/items');
  });
}, []);

CORS나 쿠키 정책

크로스 도메인 요청에서 쿠키를 보내려면 명시적으로 설정해야 한다.

// 쿠키를 함께 보낸다
fetch('/api/user', {
  credentials: 'include' // 중요!
})

서버에서도 CORS 헤더를 제대로 설정해야 한다.

// 서버 (Express 예)
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Credentials', 'true');
  res.header('Access-Control-Allow-Origin', 'https://example.com');
  next();
});

로컬과 운영 환경 비교

로컬에서는 잘 되는데 배포 후에 로그인이 풀진다?

  • 로컬: HTTP (쿠키 같은 민감한 정보는 HTTPS에서만 전송)
  • 운영: HTTPS
  • 도메인이 다르면 쿠키가 공유되지 않을 수 있다
  • 로컬 개발할 때 localhost와 운영의 도메인이 다름

최종 확인: 인증 흐름 전체 추적

  1. 앱 시작 → 저장된 인증 정보 확인
  2. 요청 → 인증 헤더 포함
  3. 응답 → 401이면 로그인 상태 초기화
  4. 새로운 요청 → 처음부터 다시

이 흐름의 각 단계를 Console과 Network에서 본다.

마지막으로, 로그인 상태는 UI 말고도 실제 API 요청으로 확인해야 한다. 자신이 본 화면과 서버가 보는 인증 정보가 일치하는가?