
나는 Gamzatech라는 프로젝트를 진행했었다, 6월인가부터 시작해서 9월 정도에 완성을 했고
지금은 그냥 기능들만 업데이트를 진행중이다. 나는 그때의 이야기를 해보려고 한다.
갑자기 왜 하냐고?
내가 이번에 졸업 프로젝트 수업에 가서 다른 팀이 발표를 하는데 "오류를 겪고 있다" 라는
말을 들었다. 듣자마자 아! 저거 내가 했던 건데, 엄청 고생을 하고 계시구나, 라고 생각을 했다.
그래서 가서 나도 같은 오류를 겪었고 나는 이런이런 방법으로 이렇게 해결했다. 라고
친절.. 하게 알려드렸다.
그런데 나도 당시에 이것 때문에 고생을 좀 했는데 기록을 안 해놔서 나중에 같은 것을 겪을까봐
적어놓으려고 한다 이참에!
프론트는 버셀로 배포를 했고, 나는 도메인을 사서 백엔드를 배포했다.
프론트: gamza-tech-blog-front.vercel.app
백엔드: app.gamzatech.site
딱 이런 느낌으로 서로 다른 도메인을 쓰고 있었다.
문제는 이 상태에서 로그인이나 인증 같은 요청을 하면 백엔드에서 Set-Cookie로 토큰을 내려주는데,
브라우저가 그 쿠키를 제대로 안 먹거나, 먹어도 다음 요청에 쿠키를 안 붙여주는 현상이 있었다.
분명히 응답 헤더에는 Set-Cookie가 찍혀있는데, 개발자 도구 Application 탭 쿠키 목록에는 안 뜨거나,
쿠키는 생겼는데 API 다시 호출하면 Authorization처럼 따라가야 할 게 안 따라가는 상황이었다.
이게 왜 터지냐면 결국 도메인 + 쿠키 정책 때문이다.
프론트 주소랑 백엔드 주소가 아예 다른 도메인이다.
...vercel.app...gamzatech.site브라우저 입장에서는 이건 "다른 사이트"다.
다른 사이트에서 온 쿠키를 막는 기본 동작이 점점 강해졌고,
그걸 우회(?)해서 정상적으로 동작시키려면 쿠키를 특정 방식으로 내려야 한다.
결론부터 말하면, 크로스 도메인으로 쿠키를 쓰려면 SameSite=None + Secure 조합이 꼭 필요하다.
SameSite는 브라우저가 "이 요청이 같은 사이트에서 온 거냐, 다른 사이트에서 온 거냐"를 보고
쿠키를 붙일지 말지를 결정하는 옵션이다.
Lax나 Strict면, 다른 도메인에서 날아온 요청에는 쿠키를 안 붙인다.SameSite=None으로 내려줘야 한다.그런데 여기서 끝이 아니다.
SameSite=None을 쓰는 쿠키는 브라우저가 "그럼 무조건 HTTPS만 써라"라는 식으로 강제한다.
그게 Secure 옵션이다.
Secure=true가 아니면 브라우저가 아예 그 쿠키를 거부하거나, 저장은 안 하거나, 전송을 안 한다.SameSite=None만 주고 Secure 안 주면 그냥 막힌다.그래서 실제로 내려야 하는 건 이런 느낌이다.
Set-Cookie: accessToken=...; HttpOnly; Path=/; Domain=.gamzatech.site; SameSite=None; Secure그리고 중요한 건 이 쿠키가 들어가는 모든 엔드포인트가 실제로 HTTPS여야 한다.
HTTP면 바로 차단이다.
나는 초기에 SSL 인증서를 대충(이라고 하면 좀 그런데) 루트 도메인만 커버하게 발급받았다.
예를 들면 gamzatech.site만 HTTPS고, app.gamzatech.site나 api.gamzatech.site처럼
서브도메인은 전부 항상 HTTPS 보장이 안 되는 상태였다.
그러니까 백엔드에서 Set-Cookie를 내려줄 때 SameSite=None; Secure까지 넣어도
브라우저 쪽에서는 "근데 이거 HTTPS 아니잖아?" 하고 그냥 쿠키를 안 받아버리는 상황이 생긴다.
받더라도 다음 요청에서 쿠키를 자동으로 안 붙여준다. (보안 조건 위반으로 무시되는 거다)
결국 내가 겪은 문제의 본질은 크게 두 개였다.
vercel.app ↔ gamzatech.site
제일 골치였던 건 이거였다. 응답 헤더에는 Set-Cookie가 제대로 내려오고 있었다. 나는 그걸 보고 "아 서버는 잘 던지고 있네? 이제 브라우저가 들고 다니겠지"라고 생각했다. 근데 브라우저 입장에서는 조건이 안 맞으니까 조용히 씹어버리고 있었다.
문제가 뭐였냐면:
SameSite=None만 붙여주면 된다고 생각했다.SameSite=None이면 Secure도 같이 붙어 있어야 하고, HTTPS여야 한다.이 조합 때문에 상황이 이상하게 나왔다.
서버: "쿠키 줄게"
브라우저: "조건 안 맞아서 안 쓸 건데? (근데 에러는 안 띄워줄 거야)"
나: "왜 다음 요청에 쿠키가 없지? 왜 Authorization이 안 따라가지?" 하면서 계속 서버 코드만 뒤집어보고 있었다.

이게 진짜 오래 걸렸다. 그냥 잠깐 막힌 정도가 아니라, 프론트 팀이랑 나랑 새벽까지 붙어서 이 문제만 3일 동안 계속 잡았다. 거의 새벽 감자 회의였다.
프론트에서는 "요청은 나갔는데 왜 쿠키 안 같이 가?"라고 하고, 나는 백엔드에서 토큰 발급 로직이랑 Set-Cookie 포맷을 계속 만지작거리고 있었다.
근데 그게 중요한게 아니었다. 브라우저가 무시 중이었다는 거다.
결국 "우리 코드가 잘못된 게 아니라 브라우저가 정책 때문에 그냥 안 쓰는 거구나"라는 걸 이해하고 나서야 방향이 바뀌었다.
도메인을 하나로 묶고, 서브도메인 전부 HTTPS 깔고, SameSite=None; Secure 세팅을 제대로 넣는 쪽으로.
해결 방향은 이렇게 갔다.
도메인을 정리했다.
프론트랑 백엔드가 최소한 같은 최상위 도메인(예: *.gamzatech.site) 아래로 묶이게 했다.
즉, 프론트도 그냥 front.gamzatech.site, 백엔드는 api.gamzatech.site 같은 식으로 가져가도록 조정했다.
이렇게 되면 이제 둘 다 gamzatech.site 계열이라 쿠키에 Domain=.gamzatech.site를 줄 수 있다.
이러면 같은 “패밀리”라고 볼 수 있어서 훨씬 덜 까다로워진다.
와일드카드 SSL을 깔았다.
*.gamzatech.site 형태로 인증서를 발급해서 front.gamzatech.site, api.gamzatech.site 이런 서브도메인들도 전부 HTTPS가 되게 했다.
(Let's Encrypt로 Nginx 리버스 프록시 두고 certbot 돌리거나, CDN/프록시 레벨에서 와일드카드 인증서 붙이는 식으로 처리할 수 있다.)
이걸 하고 나니까 Secure 쿠키가 브라우저에서 차단되지 않게 됐다.
쿠키 옵션을 제대로 줬다.
HttpOnly, SameSite=None, Secure, Domain=.gamzatech.site, Path=/ 같이 전송 조건을 명확하게 붙여서 내려줬다.
그러고 나니까 브라우저가 응답에서 쿠키를 받고, 다음 요청(예: /me, /post/write 같은 보호된 API)에도 자동으로 쿠키를 붙이기 시작했다.
이걸로 "응답에 Set-Cookie는 있는데 실제 요청에 안 실린다" 문제는 해결이 됐다.
내가 겪은 문제는 단순히 "왜 쿠키 안 붙어?"가 아니었다.
사실은
이걸 정리해서
같은 루트 도메인으로 묶고 → 서브도메인 전부 HTTPS 깔고 → SameSite=None; Secure로 쿠키를 내려주면서
Domain=.gamzatech.site를 줬더니 정상적으로 동작하기 시작했다.
이걸 내가 이번에 다른 팀 오류 듣고 바로 떠올린 이유가 그거다.
"아 그거 나도 겪었는데, 브라우저가 쿠키 안 주워가는 거 그거 HTTPS랑 SameSite 설정 깨져서 그래요…"
그 말을 해주고 나서 아 이거 기록 안 해놨네? 해서 기록을 한다!!
댓글을 작성하려면 로그인이 필요합니다.
아직 작성된 댓글이 없어요. 첫 댓글을 남겨주세요!