오직 크롬에서만 발생하는 요청 문제
이번 에러 케이스에는 서술 트릭이 존재한다.
상황
나는 onef라는 이름의 풀스택 프로젝트를 혼자서 진행하고 있다. AWS로 배포하는 데 익숙해져서, 이번에도 AWS를 통해 서비스를 배포하려고 했다. 늘 그렇듯 VPC를 하나 만들고 거기에 두 개의 서브넷을 만들었다. 퍼블릭 서브넷에는 NextJs와 NestJs 서버가, 프라이빗 서브넷에는 NestJs 서버와만 통신하는 PostgreSQL 데이터베이스가 있다.
그리고 https 통신을 구현하기 위해 [ ELB https 통신 구현 ]의 내용과 같이 로드밸런서를 하나 만들었다. 리스너에 요청 경로에 따른 규칙을 설정하여 중간에 api가 들어가면 NestJs 서버로, 그렇지 않은 경우에는 NextJs 서버로 요청이 가도록 했다. 이후에는 가비아에서 도메인을 하나 구매하여 이를 Route 53을 통해 연결해주었고, SSL 인증서도 발급받았다.
URL을 입력하여 NextJs 서버에 접속해보니 페이지가 정상적으로 떴다. 이때까지만 해도 난 아무 문제가 없는 줄 알았다.
문제
크롬에서 새로고침을 하다보면 간혹 서버로 요청이 가지 않는 현상을 발견했다. 정확히는 요청을 보내면 서버로부터 응답이 돌아오기까지 1.3분이 걸렸다. 아주 어처구니 없게도 사파리나 파이어폭스에서는 이런 현상이 발생하지 않았다. 데스크탑과 모바일을 가리지 않고 오직 크롬에서만 이런 문제가 발생했다. 게다가 요청에 걸리는 시간은 항상 1.3분이었다.
다른 브라우저는 괜찮은데 크롬에서만 발생하니까 크롬 잘못이네! 하고 넘어가고 싶은 마음이 굴뚝같았지만, 그럴 수 없었다. 크롬 브라우저의 시장 점유율은 압도적이고, 대부분의 서비스 이용자들은 크롬을 사용할 것이므로, 결국 이를 해결하지 않고서는 안정적인 서비스를 제공할 수 없다. 프론트엔드 ─ 혹은 풀스택 ─ 개발자로서 용납할 수 없는 상황인 것이다!!
시도
처음에는 이게 규칙적으로 발생하는 문제인지를 확인하고자 데스크탑(맥)∙노트북(윈도)∙모바일(안드) 크롬에서 반복적으로 요청을 보내보았다. 2시간에 걸쳐 진행된 이 실험에서 알게된 건 이 괴현상이 그 어떤 규칙성 없이 랜덤하게 발생한다는 사실이었다. 또한 ARM인지 x86인지 상관 없이 모든 플랫폼의 크롬에서 발생한다는 것도 알게되었다.
그 다음으로는 이게 프론트에서만 발생하는 문제인지, 백엔드에서도 발생하는지를 확인하고자 했다. 크롬 브라우저로는 프론트 서버를 요청하고, postman을 사용해 백엔드로 요청을 보냈다. 그리고 프론트와 백엔드 둘 다에서 이런 괴현상이 발생한다는 사실을 알게되었다.
프론트와 백이 공통적으로 사용하는 자원이 문제다. 따라서 EC2의 문제는 아닐 것이라고 단정했다. 가능성을 검토해본 결과 로드밸런서나 Route53, 네임서버, 서브넷 정도가 물망에 올랐다.
로드밸런서를 새로 만들어보았지만 달라지는 것은 없었다. Route53이나 네임서버의 문제일까 싶어서 기존에 가지고 있던 도메인을 사용해서 두 개의 로드밸런서로 테스트해보았지만 두 도메인 모두에서 이 괴현상이 발생했다. 기존에 가지고 있던 건 AWS에서 구매했고, 이번에 산 건 가비아에서 구매한 것이므로 도메인 구매처가 문제가 되지는 않을 터였다.
에러가 발생한지 7시간 쯤 지나자 크롬을 죽여버리고 싶어졌다. 하지만 끈질기게 이것저것 테스트해보았고, 처음으로 의미있는 무언가를 발견하게 되었다. 이게 새벽 1시 쯤이었다.
이유는 알 수 없지만 DNS를 통해 요청을 보내면 괴현상이 발생하는데, EC2의 퍼블릭 ip로 직접 요청을 보내는 경우에는 절대 발생하지 않았다. 이는 곧 도메인을 사용해서 요청을 보내는 과정 어딘가에 문제가 있다는 의미였다. 때문에 VPC와 서브넷, EC2는 머릿속에서 배제할 수 있었다.
해결
결론만 말하자면 로드밸런서의 설정 문제가 맞았다. 그러나 더 정확하게는 그냥 내가 문제였다. 어떻게 보면 서브넷의 문제였다고도 볼 수 있으려나? 이를 설명하기 위해서는 우선 ENI가 무엇이고, 로드밸런서가 어떤 식으로 구성되는지를 알아야 한다.
ENI는 elastic network interface의 줄임말로써, 논리적으로 구성된 가상의 이더넷 어댑터이다. 로드밸런서는 연결된 서브넷마다 통신을 위한 ENI를 생성하고 여기에 퍼블릭 IP를 할당한다. dig 명령어로 ALB DNS에 질의를 보내면, 가용 영역당 각각 생성된 퍼블릭 IP를 확인해볼 수 있다.

사용자가 로드밸런서의 도메인 주소로 요청을 보내면, DNS 질의 결과인 퍼블릭 IP로 번갈아 가며 접속하게 된다. 이렇게 접속하면 서브넷 안에 생성되어있는 로드밸런서와 그 리스너 규칙에 따라 이후 동작이 결정되는 것이다.
자, 이제 서술 트릭을 밝힐 차례다.
나는 로드밸런서를 만들었다. 로드밸런서를 만들기 위해서는 최소 두 개의 서브넷을 필요로 한다. 마침 나는 딱 두 개의 서브넷을 가지고 있었는데, 그 중 하나는 프라이빗 서브넷이었다. 그리고 로드밸런서는 연결된 서브넷마다 통신을 위한 ENI를 생성하고 여기에 퍼블릭 IP를 할당한다. 프라이빗 서브넷에 퍼블릭 IP를 할당했으니 요청을 보내도 응답이 돌아올 리가 없는 것이다!!
여기서부터는 실험의 결과로부터 추론한 내용이다. 아주 명확한 근거는 없지만 나는 아래의 내용에 대해 얼마간 확신을 가지고 있다.
크롬이 두 개의 퍼블릭 IP 중 프라이빗 서브넷의 ENI와 연결된 IP로 요청을 보내면, 이 요청은 서브넷 안에 있는 로드밸런서에 가닿지 않으므로 당연히 응답이 돌아오지 않는다. 크롬은 요청에 대한 응답을 기다리다가, 얼마간 지나면 내부적으로 이를 취소하고 재요청을 보내는 듯하다.
직전에 프라이빗 서브넷의 ENI와 연결된 IP로 요청보냈기에, 이번에는 퍼블릭 서브넷의 ENI와 연결된 IP로 요청을 보내게 된다. 이 요청은 로드밸런서까지 닿게 되고, 적절한 처리 후 응답을 받기까지 또 얼마간 시간이 걸린다. 첫 요청과 그 다음 요청에 걸리는 시간을 합치면 1.3분이 되는 게 아닐까?
찝찝함
위의 추론을 검증하기 위해 이번에는 프라이빗 서브넷 없이 로드밸런서에 퍼블릭 서브넷만 두 개를 연결해주었다. 다행히 16시간이 지난 지금까지 이 괴현상은 발생하지 않았다. 덕분에 나는 내 추론에 얼마간 확신을 가질 수 있었다.
그런데 이 추론에는 두 가지 중대한 결함이 존재한다. 우선 왜 크롬에서만 이러한 문제가 발생하고, 사파리와 파이어폭스에서는 발생하지 않았는지를 설명하지 못한다. 또한 크롬이 정말로 ENI 퍼블릭 IP에 '번갈아 가며' 요청을 보낸다면 이 괴현상이 랜덤하게 발생하는 이유 역시 설명하지 못한다.
이런저런 검색 끝에 후자의 경우를 설명할 수 있는 내용을 스택오버플로우에서 발견할 수 있었다. 크롬이 ENI 퍼블릭 IP에 번갈아 가며 요청을 보내기는 하지만, 짧은 시간 동안 여러번 요청을 보내는 과정에서는 그러지 않는다는 것이다. 일단 A IP로 한 번 요청을 보냈다면, 일정한 시간이 지나기 전까지는 A IP로만 요청을 보낸다는 것이었다.
분명 설득력 있게 느껴지지만, 아쉽게도 당장은 이 주장이 타당한지를 확인할 방법이 없다. 게다가 왜 사파리와 파이어폭스에서는 문제가 없었는지 설명하는 내용은 끝끝내 찾지 못했다. 때문에 문제가 해결되었음에도 어쩐지 좀 찝찝한 기분이 드는 게 사실이다.
블로그의 정보
Ayden's journal
Beard Weard Ayden