인증을 붙이다 만난 무한 리다이렉트 루프
로그인 한 번 붙이는 일이 왜 무한 리다이렉트 루프로 이어졌는지, 그리고 쿠키 크기 한계가 설계를 어떻게 바꿨는지 정리합니다.
지난 편에서 서비스 를 여러 개로 나눈 이야기를 했습니다. 서비스가 나뉘면 화면(프론트엔드)도 여러 곳에 흩어집니다. 제품 사이트가 따로 있고, 로그인을 전담하는 인증 사이트가 따로 있습니다. 그러면 "한 곳에서 로그인하면 다른 곳에서도 로그인 상태가 유지되어야 한다"는 문제가 생깁니다.
이 문제를 풀다가 무한 리다이렉트 루프에 빠졌습니다. 그 과정을 적어 봅니다.
왜 인증 사이트를 따로 두었나
로그인 화면을 제품마다 만들면, 제품이 늘어날 때마다 로그인 코드도 복제됩니다. 그래서 로그인은 인증 사이트 한 곳에서만 처리하기로 했습니다.

건물에 비유하면 이렇습니다. 제품은 여러 동(棟)으로 나뉜 건물이고, 출입증을 발급하는 곳은 별도의 안내 데스크 한 곳입니다. 어느 동에 들어가려 하든, 출입증이 없으면 먼저 안내 데스크로 보냅니다. 거기서 신원을 확인하고 출입증을 받아 다시 원래 동으로 돌아옵니다.
흐름으로 옮기면 다음과 같습니다.
- 사용자가 제품 사이트에서 로그인을 누릅니다.
- 인증 사이트로 보냅니다. 이때 "끝나면 어디로 돌아갈지"(
return_to)를 함께 넘깁니다. - 인증 사이트에서 신원을 확인합니다.
- 토큰을 그대로 넘기면 위험하니, 토큰을 암호화한 짧은 코드를 만들어 제품 사이트로 돌려보냅니다.
- 제품 사이트가 그 코드를 토큰으로 교환합니다(exchange).
- 토큰을 저장하고, 원래 보려던 화면으로 돌아갑니다.
이 방식을 BFF(Backend for Frontend) 패턴이라고 부릅니다. 토큰 자체를 주소창에 노출하지 않고, 한 단계 안전하게 교환하는 구조입니다.
그런데 회전문에 갇혔습니다
문제가 생겼습니다. 로그인을 누르면 인증 사이트로 갔다가, 다시 제품 사이트로 왔다가, 또 인증 사이트로 가기를 끝없이 반복했습니다. 회전문에 갇힌 것처럼 같은 자리를 빙빙 돌았습니다.

원인을 파보니 한 가지가 아니라 세 가지가 겹쳐 있었습니다.
원인 1. 돌아갈 주소를 잃어버렸다
"끝나면 어디로 돌아갈지"(return_to)를 임시 저장소(sessionStorage)에 담아 두었습니다. 그런데 로그인이 한 번 실패하고 재시도하는 사이 이 저장소가 비워졌습니다. 돌아갈 주소를 잃으니, 인증이 끝나도 제품 사이트로 못 가고 인증 사이트 안에서만 맴돌았습니다.
안내 데스크에 "저는 3동에서 왔어요"라고 적은 메모를 맡겼는데, 그 메모가 중간에 사라진 셈입니다. 출입증은 받았지만 어느 동으로 가야 할지 모르게 된 것이지요.
원인 2. 출입증이 주머니에 안 들어갔다
이게 가장 찾기 어려웠습니다. 토큰을 쿠키에 저장하려 했는데, 쿠키 하나의 크기 한계는 약 4KB(4096바이트)입니다.
그런데 우리가 담아야 할 토큰은 두 종류였습니다.
- 신원 토큰(id token): 약 1,500바이트
- 갱신 토큰(refresh token): 약 1,700바이트
두 개만 해도 3,200바이트입니다. 여기에 토큰을 암호화하면 크기가 더 늘어납니다(암호화와 인코딩을 거치면 대략 1.3배). 쿠키에 붙는 부가 정보까지 더하니 4,096바이트를 넘어 버렸습니다.
문제는 브라우저가 이때 아무 오류도 내지 않고 조용히 쿠키를 버린다는 점입니다. 저장된 줄 알았는데 실제로는 비어 있었습니다. 그러니 다음 요청에서 "인증 안 됨"으로 막히고, 다시 로그인 화면으로 돌아갑니다. 회전문의 진짜 동력이 여기 있었습니다.
출입증을 주머니에 넣었다고 생각했는데, 주머니가 작아서 출입증이 바닥에 떨어진 것입니다. 본인은 가진 줄 알고 다음 문에 갔다가 "출입증 없으시네요" 하고 다시 안내 데스크로 돌아가기를 반복합니다.
원인 3. 옆 건물 사물함은 못 연다
토큰을 인증 사이트 쪽 저장소에 넣어 두면, 제품 사이트에서는 그 값을 읽을 수 없습니다. 브라우저는 보안을 위해 사이트(도메인)마다 저장소를 격리하기 때문입니다. 인증 사이트의 사물함은 제품 사이트가 열 수 없습니다.