우리는 인터넷을 안 보이게 만들어서 쓸 만하게 만들었다.
보급에는 좋은 거래였고, 이해에는 나쁜 거래였다.
대부분의 개발자는 DNS, TCP, TLS, HTTP에 대한 또렷한 그림 없이도 하루 종일 fetch()를 호출할 수 있다. 대부분의 사용자는 리졸버가 무슨 일을 하는지, 인증서 경고가 왜 중요한지, “나만 사이트가 안 열려”가 요청이 앱 서버에 닿기도 전에 왜 열두 가지 다른 뜻일 수 있는지 모른 채 평생을 온라인에서 보낼 수 있다.
모뎀 설정을 손으로 고치던 시절이 그립다는 얘기가 아니다.
불투명함에는 비용이 있고, 그 청구서가 보안과 운영에서 계속 날아온다는 얘기다.
추상화의 층
인터넷의 모든 층은 그 아래 층을 숨긴다. HTTP는 TCP를 숨긴다. TLS 라이브러리는 핸드셰이크를 숨긴다. DNS는 해석 과정을 숨긴다. 클라우드 서비스는 서버를 숨긴다. 프레임워크는 HTTP를 숨긴다. 그리고 맨 꼭대기에서 개발자가 fetch('/api/data')를 쓰고, 그게 되기를 기대한다.
잘 돌아갈 때 추상화는 아름답다. 생산성은 오르고 진입 장벽은 낮아진다. 웹 앱 하나 만들자고 패킷 단편화를 알 필요는 없다. 좋은 일이다.
고장 나면 추상화는 벽이 된다. DNS가 뭘 하는지 배운 적 없는 개발자는 이름 해석 실패를 디버깅하지 못한다. CORS가 뭔지 모르는 개발자는 브라우저 오류와 세 시간을 씨름한다. 블로그 글에서 TLS 설정을 복사해 온 개발자는 감사인이 물을 때 그 설정이 각각 무슨 뜻인지 설명하지 못한다.
모르는 것의 비용
같은 패턴을 보고 또 본다.
아무도 디버깅하지 못하는 DNS 설정 오류. 이전 작업이 꼬인다. 사이트가 내려간다. 팀은 배포 파이프라인만 쳐다본다. 자기들이 이해하는 시스템이 그거니까. DNS 전파가 어떻게 일어나는지, TTL이 무슨 뜻인지, 옛 레코드가 왜 아직 어딘가에 캐시돼 있는지 그림이 없으니 아무도 DNS를 확인하지 않는다.
이해 없이 복사한 보안 헤더. 스캐너가 “이 헤더들을 추가하세요”라고 한다. 팀이 추가한다. 그중 하나인 Content-Security-Policy가 사이트를 깨뜨린다. 그게 무슨 일을 하는지 아무도 몰랐으니까. 깨지지 않게 하려고 'unsafe-inline'을 넣는다. 이제 헤더는 있지만 쓸모가 없다. 스캐너는 초록불. 보안은 연극이다.
수수께끼 취급받는 TLS 오류. “인증서 멀쩡한데요, 방금 갱신했어요.” 그런데 중간 인증서가 빠져 있다. 아니면 호스트 이름이 안 맞는다. 아니면 HSTS max-age가 하루짜리다. TLS는 클라우드 업체가 알아서 하는 거라, 팀에서 이게 무슨 뜻인지 아는 사람이 없다. 알아서 안 해 줄 때까지는.
CORS 혼란. 개발자들은 CORS가 서버 버그가 아니라 브라우저의 보안 장치라는 걸 모른 채 CORS 오류와 몇 시간씩 싸운다. 그러다 “Access-Control-Allow-Origin: *를 추가하세요”라는 Stack Overflow 답을 찾고, 오류가 사라진다. 그 엔드포인트의 응답을 지구상의 모든 웹사이트가 읽어도 된다고 방금 선언한 것이다. 그래도 되는지는 한 번도 따져 보지 않은 채. 오류가 사라진 게 바로 나쁜 결과였다.
클라우드가 더 나쁘게 만들었다
관리형 서비스는 생산성에는 놀랍고, 이해에는 끔찍하다.
버튼 한 번으로 데이터베이스를 띄우면 복제도, 커넥션 풀링도, 백업 검증도 배우지 않는다. DNS를 CDN이 관리하면 존 파일도, 위임도, TTL도 배우지 않는다. TLS 인증서가 자동으로 발급되면 키 관리도, CA 신뢰도, 인증서 체인도 배우지 않는다.
뭔가 잘못되기 전까지는 괜찮다. 잘못되면, 한 번도 속을 들여다본 적 없는 시스템을 “버튼 눌렀더니 전에는 됐는데”에서 멈추는 그림으로 디버깅하게 된다.
클라우드는 서버만 추상화한 게 아니다. 서버가 어떻게 돌아가는지에 대한 지식까지 추상화했다. 이제 많은 개발자가 기계에 SSH로 한 번 들어가 보지도, 로그 파일을 직접 읽어 보지도, 네트워크 인터페이스를 설정해 보지도 않고 프로덕션 시스템을 만든다.
세대의 변화
초창기 인터넷 사용자는 접속하려면 프로토콜을 어느 정도 알아야 했다. 깊이는 아니어도 그럭저럭은. 네트워크 설정에 DNS 서버를 직접 넣었다. IP 주소를 손으로 쳤으니 그게 뭔지 알았다. “내 컴퓨터가 서버와 얘기하고, 서버가 페이지를 돌려준다” 정도의 대략적인 그림은 있었다.
지금의 사용자에게는 그런 그림이 없다. 인터넷은 마법의 창이다. 내용이 나타난다. 안 나타나면 뭔가 고장 난 거고, 다른 누군가가 고쳐야 한다.
사용자를 탓하는 게 아니다. 사용자가 TCP를 알아야 할 이유는 없다. 사용자에게는 추상화가 옳다.
문제는 개발자와 시스템 관리자가 사용자와 같은 수준으로 이해할 때다. 인터넷 서비스를 만들고 운영할 책임이 있는 사람들이 그 밑의 프로토콜을 남의 문제로 여길 때다.
할 수 있는 것
모두가 BGP를 이해하거나 RFC를 읽어야 한다는 말이 아니다. 하지만 기본적인 그림만 있어도 실수 한 부류를 통째로 막을 수 있다.
“DNS는 이름을 숫자로 바꿔 주고, 그 결과는 한동안 캐시된다.” 이 한 문장이 이전 작업 중의 DNS 혼란을 대부분 막는다.
“HTTPS는 연결을 암호화하지, 사이트를 보증하지 않는다. 자물쇠는 암호화됐다는 뜻이지 믿을 만하다는 뜻이 아니다.” 피싱 사이트를 잘못 믿는 일을 막는다.
“CDN은 내 사이트를 사용자 가까이에 캐시하지만, CDN이 내려가면 내 사이트도 같이 내려간다.” 인프라 의존성에 대해 올바른 기대를 갖게 한다.
“CORS 오류는 허락하지 않은 다른 출처의 응답을 페이지가 읽지 못하게 브라우저가 막는 것이지, 서버가 고장 난 게 아니다.” 헤매는 디버깅 몇 시간을 아껴 준다.
깊은 기술 개념이 아니다. 한 문단짜리 설명이다. 그런데 인터넷 위에서 뭔가를 만드는 사람 대부분이 이것조차 갖고 있지 않다.
아이러니
우리는 인류 역사상 가장 정교한 통신 시스템을 만들었는데, 그걸 쓰는 사람들은 — 그 위에서 뭔가를 만드는 사람들조차 — 대개 그게 어떻게 돌아가는지 모른다. 추상화가 너무 완벽하게 성공해서, 추상화로 덮어 둔 지식이 사라지고 있다.
인터넷은 DNS, TCP, TLS, HTTP, BGP 위에서 돈다. 이 프로토콜들은 모든 층을 이해하던 사람들이 설계했다. 지금은 그중 몇 개밖에 모르는 사람들이 운영하는 경우가 많다. 그 틈을 자동화와 관리형 서비스, 그리고 희망이 메운다.
돌아간다. 대개는. 안 돌아가면 어디를 봐야 할지 그림이 있는 사람이 없어서, 디버깅이 필요 이상으로 오래 걸린다.
인터넷이 어떻게 돌아가는지 일부러 안 가르치게 된 건 아니다. 몰라도 되게 만들었을 뿐이다. 그리고 “몰라도 된다”를 “중요하지 않다”로 착각했다.