일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- CSS
- 엘리스sw트랙
- 데이터베이스시스템
- 방송대컴퓨터과학과
- nestjs
- JavaScript
- Cookie
- Git
- 방송대
- Python
- TiL
- 코드잇
- 99클럽
- HTML
- 파이썬프로그래밍기초
- presignedurl
- MySQL
- 항해99
- 꿀단집
- redis
- node.js
- 개발자취업
- 코딩테스트준비
- SQL
- 중간이들
- 프로그래머스
- 파이썬
- 코딩테스트
- aws
- 유노코딩
- Today
- Total
배꼽파지 않도록 잘 개발해요
[중간이들] NestJS 세션 로그아웃 구현 중 쿠키 문제 해결 과정 본문
NestJS 서버에서 express-session과 passport, connect-redis를 사용하여 세션 로그인을 구현하였다.
API를 점검하는 도중에 로그아웃에 문제가 있는 것을 발견하였다.
어제 몇 시간 동안 테스트한 결과, 자잘한 문제가 다양하게 발생하여 해결하는 것에 시간이 좀 걸렸다.
우선 큼직하게 핵심만 요약하면 아래와 같다.
세션 로그인/로그아웃 문제 및 해결과정 요약
문제
- 로그아웃 시 Redis 데이터 삭제 문제: 서버에서는 삭제되었다고 기록되지만, 실제 Redis에서 데이터가 삭제되지 않음.
- 쿠키 삭제 문제: 로그아웃 시 쿠키가 완전히 삭제되지 않고, 새로운 값으로 변경됨.
- 환경 설정 문제: 세션의 saveUninitialized 설정이 의도치 않게 true로 설정되어 불필요한 세션이 생성됨.
- CORS 문제: 서버와 클라이언트의 도메인이 다를 때 쿠키가 제대로 전달되지 않아 세션 관리에 문제 발생.
해결 방안
1. Redis 관련
- Redis Client가 제대로 설정되어 동작하는지 확인한다.
→ 보통 이런 경우 실행 중 오류가 나는 경우가 많다.
- CLI로 del (지우고 싶은 세션키)를 입력하였을 때 정상적으로 작동하는지 확인한다.
→ 이게 작동한다면 Redis 문제가 아니라 서버에 다른 문제가 있는 것이다. 예를 들면 세션 키를 제대로 인식하지 못한다든지 등
2. 세션 관련 환경 설정 재확인
- resave, saveUninitialized 설정이 정확히 false로 설정되었는지 재확인한다.
(saveUninitialized이 true일 경우, 아무 내용이 없는 uninitialized의 세션 정보를 강제로 저장한다.
예를 들면, 로그인한 유저 정보가 없는 쿠키 옵션만 있는 세션정보)
- 다른 관련 환경 변수도 다시 확인하여 문제가 없는지 점검한다.
3. 쿠키 설정을 확인한다.
- 쿠키 옵션을 모듈의 한 곳에 설정해놓고, 그것을 세션 설정과 쿠키 제거(res.clearCookie)하는 곳에 모두 import하여 재사용한다.
- 한 곳에 만들어 놓고 재사용해야 하는 이유 : 쿠키 옵션을 제대로 세팅해서 붙여넣기 해야 필요한 곳에 import할 때 해당 설정이 의도치 않게 변경되지 않는다. 불러오는 곳마다 쿠키 설정값이 달라지면 예상치 못한 오류가 날 수 있다.
4. 클라이언트에서 CORS 오류가 나는지 확인한다.
1) 서버
- origin 설정: true (O), '*'(X), 특정 URL만 입력 (ex. localhost:1010)
- 쿠키 옵션 확인 (특히 secure, sameSite)
2) 클라이언트
- 요청 헤더에 쿠키 자동 전송을 위해 withCredentials: true 확인
(axios에서는 credentials: true, fetch에서는 credentials: 'include')
3) 클라이언트 테스트는 썬더클라이언트 말고 일반 브라우저에서도 테스트해보기
Thunder Client | HTML | React | |
클라이언트 | http://localhost:3000 | http://127.0.0.1:5500 | http://localhost:3001 |
서버 | localhost:3000 | ||
테스트 결과 | 정상 작동 | 비정상 작동 | 정상 작동 |
기타 | 동일한 세션으로 인식하지 않음. → 로그인과 로그아웃시 세션 ID가 다르게 나오거나, 쿠키가 2개씩 들어감 등 문제 발생 | 일반 HTML,JS만 사용하는 것보다 통신 속도가 빨랐음. |
문제 상황
auth/sign-out (로그인) 요청을 보내면 response는 성공으로 돌아오나 다음과 같은 문제가 발생함.
1) redis에서 sessionId와 일치하는 키값을 가진 데이터 삭제가 안됨.
서버에서는 삭제 완료되었다고 기록이 뜨나, 실제 redis cloud에서 직접 확인해보면 데이터 삭제가 안 되어 있음.
redis cli에서 직접 del 명령어로 해당 sessionId를 삭제하면 잘 지워짐.
2) 클라이언트 측에서 로그아웃 요청을 보냈을 때, 브라우저에 있는 쿠키의 coonect.sid 값이 삭제가 되지 않고, 다른 값으로 변경이 됨.
- 예상 응답 : connect.sid 쿠키가 브라우저에서 삭제됨.
- 실제 응답 : connect.sid 쿠키의 값이 기존 값과 다른 값으로 변경되고 쿠키가 여전히 있음.
문제 상황
1차 시도
- 일단 쿠키 옵션을 auth 모듈에 만들어서 세션의 cookie 옵션과 일치시켜줌.
- res.clearCookie()에서도 두 번쨰 인자로 해당 쿠키 옵션을 넣어주었음.
→ 결과 : 여전히 문제 해결이 안 됨.
2차 시도
- req.session.destroy로 세션을 삭제해줌.
// 로그아웃
async signOut(req: Request, res: Response): Promise<void> {
const sessionId = req.sessionID;
// console.log("로그아웃 메서드 sessionId", sessionId)
// await this.redisClient.del(`sess:${sessionId}`);
const keyToDelete = `sess:${sessionId}`;
console.log('삭제할 키', keyToDelete);
// Redis에서 키가 실제로 존재하는지 확인
const exists = await this.redisClient.exists(keyToDelete);
console.log('키 존재 여부', exists);
await this.redisClient.persist(keyToDelete);
// 키 삭제
const result = await this.redisClient.del(keyToDelete);
console.log('삭제 결과', result);
// 쿠키 옵션 불러오기
const cookieOptions = await getCookieOptions();
// 쿠키 삭제
res.clearCookie('connect.sid', cookieOptions);
res.status(200).json({ message: '로그아웃이 성공적으로 완료되었습니다.' });
}
썬더클라이언트에서 테스트
1) 로그인 하였을 때 redis에 세션정보가 잘 저장됨.
2) 로그아웃 했을 때
- connect.sid 쿠키 : 값이 삭제됨. 쿠키 key값 자체는 남아 있음.
- redis에서 세션 데이터 삭제 : 잘 됨.
브라우저에서 테스트
1) 로그인 하였을 떄 redis에 세션정보가 잘 저장되나, 쿠키 정보가 1개 더 들어감. (세션이 1개 더 생김)
2) 로그아웃 했을 때
- connect.sid 쿠키 : 값이 삭제됨. 쿠키 key값 자체는 남아 있음.
- redis에서 세션 데이터 삭제 : 안 됨.
썬더클라이언트 테스트 (정상 동작)
일반 HTML에서 테스트
→ 결과 : 여전히 문제 해결이 안 됨.
3차 해결 시도
1. 쿠키 옵션 설정의 문제이다.
- 확인해보니까 서버에서 설정한 것, 브라우저에서 들어온 쿠키의 내용, redis에 저장된 세션정보의 쿠키 설정이 모두 일치함.
→ 해결 안 됨.
2. main.ts의 미들웨어 문제이다.
- 미들웨어 순서를 cookieparser를 passport보다 먼저 오도록 바꿔주고 확인해봄.
→ 해결 안 됨.
3. 환경변수 문제
env 파일에서
이걸 boolean이 아닌 string으로 인식하여 (문자열은 true임)
SESSION_SAVE_UNINITIALIZED가 true가 되어 유저 정보가 없는 쿠키 정보만 있는 세션이 하나 더 생겼음.
boolean으로 변경해주는 함수를 만들어서 적용함.
→ 해결 됨. 하지만 클라이언트(썬더클라이언트 말고 일반 HTML)에 쿠키가 정상적으로 안 들어옴.
3. 클라이언트의 문제
일반 HTML (http://127.0.0.1:5500)이 아닌 React (http://localhost:3000)으로 테스트하니 잘 작동함.
127.0.0.1과 localhost는 다르게 인식함.
앞으로 클라이언트에서 브라우저상 테스트할 때는 React로 띄워놓기로 결심함.
쿠키 브라우저에서 삭제되는 옵션은 expires: 과거 날짜, maxAge: 0
https://programming-bellybutton.tistory.com/218
[node.js] 서버에서 로그아웃시 HttpOnly 쿠키 브라우저에서 삭제하는 방법
벌써 프로젝트가 1차적으로 마무리되었다. 우리 서버는 NestJS 프레임워크를 사용하여 express-session과 connect-redis, passport를 통해 세션 인증을 사용하고 있다. 이전 프로젝트에서 토큰만 사용해
programming-bellybutton.tistory.com
'Project' 카테고리의 다른 글
[중간이들] URL 생성할 때는 항상 슬래시 (경로 구분자)를 조심해야함 (0) | 2024.10.04 |
---|---|
[중간이들] AWS S3 presigned-url content type 불일치 오류 해결 (0) | 2024.10.04 |
[중간이들] SMS 인증번호 발송서비스 플랫폼으로 네이버 클라우드를 선택하지 않은 이유 (0) | 2024.09.10 |
[꿀단집] 구글 OAuth 소셜 로그인: 심사 통과 후 프로덕션 환경에서 외부인 로그인 가능 (2) | 2024.09.10 |
[꿀단집] 배포환경에서 multer로 프로필 이미지 변경이 안 되는 문제 - 서버 파일 경로 구분자 문제를 path.normalize로 해결 (0) | 2024.09.10 |