웹 개발
로그인이 풀렸을 때 원인 찾기
사용자가 갑자기 로그인 상태를 잃으면 세션, 토큰, 쿠키 중 어디가 문제인지 확인해야 합니다.
사용자가 "왜 자꾸 로그인이 풀려?"라고 물어온다. 버튼을 누른 후 깜깜한 에러만 떴다고 한다. 문제는 여러 곳에 숨어 있을 수 있다.
에러 메시지부터 읽기
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와 운영의 도메인이 다름
최종 확인: 인증 흐름 전체 추적
- 앱 시작 → 저장된 인증 정보 확인
- 요청 → 인증 헤더 포함
- 응답 → 401이면 로그인 상태 초기화
- 새로운 요청 → 처음부터 다시
이 흐름의 각 단계를 Console과 Network에서 본다.
마지막으로, 로그인 상태는 UI 말고도 실제 API 요청으로 확인해야 한다. 자신이 본 화면과 서버가 보는 인증 정보가 일치하는가?