앞의 여러 편에서 캐시가 자원을 재사용하고, 스토리지가 데이터를 담으며, 보안 모델이 출처를 격리한다는 것을 다루었다. 이번 편은 이 요소들을 한데 엮어, 페이지가 서버와 나누는 통신 자체를 가로채 통제하는 특별한 스크립트를 다룬다. 이 스크립트는 페이지와 통신망 사이에 자리 잡고 오가는 요청을 중간에서 받아, 무엇을 저장된 것으로 답하고 무엇을 통신망으로 내보낼지를 스스로 정한다. 이 통제 덕분에 통신망이 끊긴 상황에서도 화면을 띄우는 일이 가능해진다.
이 스크립트는 앞서 다룬 자동 캐시보다 훨씬 강한 통제력을 가진다. 자동 캐시가 정해진 규칙에 따라 브라우저가 알아서 다루는 것이라면, 이 스크립트는 요청 하나하나를 어떻게 처리할지를 직접 정한다. 강한 통제력은 큰 가능성을 열지만 그만큼 다루기 까다롭다. 이 스크립트의 특별한 생명주기와 동작 원리를 이해하지 못하면, 오히려 사용자를 낡은 화면에 가두는 사고를 낸다.
페이지와 통신망 사이의 중개자
이 스크립트의 가장 큰 특징은 페이지와 통신망 사이에 끼어 요청을 가로챈다는 것이다. 페이지가 어떤 자원을 요청하면, 그 요청은 곧바로 통신망으로 나가지 않고 먼저 이 중개자에게 전달된다. 중개자는 그 요청을 보고, 저장해 둔 것으로 답할지 아니면 통신망으로 내보낼지를 결정한다. 페이지는 자신의 요청이 이렇게 가로채인다는 것을 알 필요조차 없다.
이 가로채기 덕분에 중개자는 통신을 세밀하게 다룰 수 있다. 자주 쓰이는 자원은 저장해 두었다가 통신망에 나가지 않고 곧바로 답해 속도를 높이고, 새로워야 하는 자원은 통신망으로 내보내 최신을 받아 온다. 어떤 자원을 어떻게 다룰지를 요청의 성격에 따라 나누어 정하는 것이 이 중개자의 핵심 역할이다.
이 중개자는 페이지를 그리는 흐름과 떨어진 자리에서 돌아간다. 그래서 무거운 처리를 하더라도 화면이 멈추지 않는다. 또한 이 중개자는 자신을 등록한 페이지가 닫힌 뒤에도 배경에 남아, 나중에 통신망으로 무언가가 도착하거나 예약된 일이 있을 때 깨어나 처리할 수 있다. 페이지의 수명과 별개로 존재하는 이 성질이 여러 고급 기능의 바탕이 된다.
이 강한 능력에는 엄격한 제약이 따른다. 요청을 가로채 통제하는 힘은 잘못 쓰이면 위험하므로, 이 중개자는 안전한 연결 위에서만 동작하고 자신을 등록한 출처의 범위 안에서만 요청을 가로챌 수 있다. 앞 편에서 다룬 출처의 격리가 여기서도 그대로 적용되어, 한 출처의 중개자가 다른 출처의 통신을 넘보지 못하게 막는다.
단계를 밟아 자리 잡는 생명주기
이 중개자는 곧바로 일을 시작하지 않고 정해진 단계를 밟아 자리를 잡는다. 이 생명주기를 이해하는 것이 이 스크립트를 다루는 첫걸음이다. 첫 단계는 등록으로, 페이지가 이 중개자를 쓰겠다고 브라우저에 알리는 것이다. 등록되면 브라우저가 이 중개자를 내려받아 준비를 시작한다.
다음 단계는 설치다. 이 단계에서 중개자는 앞으로 쓸 자원들을 미리 저장해 두는 준비 작업을 한다. 화면을 이루는 데 꼭 필요한 자원들을 이때 저장소에 담아 두면, 나중에 통신망이 없어도 그 저장본으로 화면을 세울 수 있다. 설치는 이 중개자가 오프라인에 대비해 살림을 갖추는 단계인 셈이다.
설치가 끝나면 활성 단계로 넘어간다. 이 단계에 이르러야 중개자는 비로소 요청을 가로채기 시작한다. 활성 단계에서는 흔히 지난 판이 남긴 낡은 저장본을 정리하는 작업을 함께 한다. 새 중개자가 새로운 살림을 차리면서 옛 살림의 잔재를 걷어내는 것이다. 이 정리를 소홀히 하면 낡은 저장본이 남아 뒤섞인다.
이 단계적인 자리 잡기에는 중요한 성질이 하나 있다. 새로운 판의 중개자가 준비되어도, 옛 중개자가 통제하던 페이지가 아직 열려 있는 동안에는 새 중개자가 곧바로 통제권을 넘겨받지 않는다. 열려 있던 페이지가 모두 닫히고 다시 열려야 비로소 새 중개자가 통제한다. 이 신중함은 페이지가 도중에 옛 통제자와 새 통제자 사이에서 오락가락하며 뒤섞이는 것을 막기 위한 것이다.
요청을 다루는 여러 전략
중개자가 가로챈 요청을 어떻게 처리할지에는 여러 전략이 있고, 자원의 성격에 따라 골라 써야 한다. 첫 번째 전략은 저장된 것을 먼저 쓰는 것이다. 요청이 오면 저장소를 먼저 뒤져 있으면 그것으로 답하고, 없을 때만 통신망으로 나간다. 이 전략은 빠르지만, 저장본이 낡았을 때 옛 내용을 내줄 위험이 있다. 거의 바뀌지 않는 자원에 알맞다.
두 번째 전략은 통신망을 먼저 쓰는 것이다. 요청이 오면 먼저 통신망으로 나가 최신을 받아 오고, 통신망이 없을 때만 저장된 것으로 답한다. 이 전략은 항상 최신을 우선하되 연결이 없을 때는 저장본으로 버틴다. 새로워야 하지만 연결이 끊겨도 무언가는 보여야 하는 자원에 알맞다. 대신 통신망을 먼저 거치므로 저장된 것을 바로 쓰는 것보다 느리다.
세 번째 전략은 저장된 것으로 먼저 빠르게 답하되, 동시에 통신망으로도 나가 받아온 최신으로 저장본을 갱신해 두는 것이다. 사용자는 저장본으로 빠르게 화면을 보고, 다음번 방문에서는 갱신된 최신을 본다. 속도와 신선함을 절충한 이 전략은 자주 방문하지만 최신이 급하지는 않은 자원에 널리 쓰인다.
이 전략들은 하나만 고르는 것이 아니라 자원마다 나누어 적용한다. 화면의 뼈대를 이루는 자원은 저장된 것을 먼저 쓰고, 실시간에 가까운 데이터는 통신망을 먼저 쓰며, 자주 보는 콘텐츠는 절충 전략을 쓰는 식이다. 요청의 성격을 보고 알맞은 전략을 배정하는 것이 중개자를 잘 다루는 요령이다. 앞 편에서 다룬 구조적인 데이터 저장과 짝을 지어, 하나는 화면을 이루는 자원을, 다른 하나는 그 화면에 채울 데이터를 맡기면 오프라인 대응이 완성된다.
배경에서 깨어나는 일들
이 중개자가 페이지와 별개로 배경에 존재한다는 성질은 여러 고급 기능을 가능하게 한다. 페이지가 닫혀 있어도 통신망으로 무언가가 도착하면, 중개자가 깨어나 그것을 처리하고 사용자에게 알릴 수 있다. 새 소식이 도착했을 때 페이지를 열어 두지 않아도 알림이 오는 것이 이 성질 덕분이다.
또한 통신망이 끊긴 동안 사용자가 한 조작을 저장해 두었다가, 나중에 연결이 돌아왔을 때 배경에서 대신 처리하는 일도 가능하다. 사용자가 연결이 없는 상태에서 무언가를 보냈다면, 그 요청을 담아 두었다가 중개자가 연결이 복구된 것을 감지해 뒤늦게 전송한다. 사용자는 연결의 유무를 신경 쓰지 않고 조작하고, 나머지는 중개자가 알아서 맞춰 준다.
이런 배경 동작은 강력하지만 절제해서 써야 한다. 배경에서 깨어나 처리하는 일이 잦거나 무거우면 기기의 자원과 전력을 축내기 때문이다. 그래서 브라우저는 배경 동작에 여러 제약을 두어, 꼭 필요한 만큼만 짧게 처리하도록 제한한다. 배경에서 오래 무언가를 붙들고 있으려는 시도는 브라우저에 의해 정리된다.
이 기능들은 모두 앞서 다룬 보안의 틀 안에서 동작한다. 배경에서 도착하는 소식을 알리거나 저장된 조작을 뒤늦게 보내는 일도, 등록된 출처의 범위와 안전한 연결이라는 제약을 지킨다. 강한 능력일수록 격리의 울타리 안에 두어야 오용을 막을 수 있기 때문이다. 능력과 제약이 함께 가는 것이 이 중개자 설계의 일관된 원칙이다.
등록과 갱신을 다루는 실무
중개자를 실제로 다룰 때 가장 먼저 부딪히는 것은 언제 등록하느냐다. 페이지가 열리자마자 서둘러 등록하면 첫 화면을 그리는 일과 경쟁해 오히려 초반을 느리게 만들 수 있다. 그래서 첫 화면을 그리는 급한 일이 끝난 뒤, 여유가 생긴 시점에 등록을 시작하는 것이 흔한 방식이다. 중개자의 이득은 대개 두 번째 방문부터 나타나므로, 첫 방문의 속도를 희생하면서까지 서두를 이유가 없다.
등록한 뒤에는 그 중개자가 어느 단계에 있는지를 확인하며 다뤄야 한다. 앞서 본 생명주기의 각 단계는 눈에 보이지 않게 진행되므로, 지금 설치 중인지 활성 상태인지, 새 판이 통제권을 기다리고 있는지를 도구로 확인해야 한다. 이 상태를 모른 채 다루면, 분명히 새 중개자를 준비했는데 왜 옛 동작이 이어지는지를 이해하지 못해 혼란에 빠진다.
갱신의 어려움은 이 통제권 이양의 신중함에서 온다. 새 판을 준비해도 옛 페이지가 열려 있는 동안에는 새 중개자가 곧바로 나서지 않으므로, 사용자가 계속 같은 탭에 머물면 갱신이 언제까지고 반영되지 않을 수 있다. 그래서 새 판이 준비되었음을 감지하면 사용자에게 알려, 페이지를 다시 열도록 안내하는 배려가 필요하다. 갱신을 강제로 앞당기는 방법도 있지만, 도중에 통제자가 바뀌어 동작이 뒤섞일 위험이 있어 신중해야 한다.
갱신이 특히 위험해지는 경우는 중개자 자신을 다루는 규칙이 잘못되었을 때다. 만약 중개자를 내려받는 요청마저 옛 저장본으로 답하도록 규칙을 짜 두면, 새 판을 올려도 브라우저가 옛 중개자만 계속 받아 영영 갱신되지 않는 사고가 생긴다. 그래서 중개자 자신과 그 핵심 자원은 저장된 것을 먼저 쓰는 전략에서 제외해, 항상 최신을 확인하도록 두는 것이 안전하다.
이런 어려움 때문에 중개자는 여러 상황을 실제로 시험해 보고 나서 넓게 적용해야 한다. 처음 방문할 때, 두 번째로 방문할 때, 연결이 끊긴 채 방문할 때, 새 판으로 갱신될 때의 동작을 각각 확인해, 어느 상황에서도 사용자가 낡은 화면에 갇히거나 빈 화면을 마주하지 않는지를 점검한다. 강한 통제력을 가진 만큼, 넓게 풀기 전에 좁게 시험하는 신중함이 사고를 막는다.
중개자를 아예 걷어내야 하는 상황도 대비해 두어야 한다. 심각한 문제가 발견되어 중개자를 멈춰야 할 때, 그것을 깔끔히 등록 해제하고 저장해 둔 것까지 비우는 길이 마련되어 있지 않으면, 이미 사용자의 브라우저에 자리 잡은 옛 중개자가 계속 옛 동작을 이어간다. 그래서 새 판이 옛 저장을 정리하는 절차와 더불어, 필요하면 중개자 자체를 물릴 수 있는 길을 함께 갖추는 것이 안전한 운영의 조건이다.
강한 통제에 따르는 책임
이 중개자의 강한 통제력은 잘못 다루면 그대로 위험이 된다. 가장 흔한 사고는 옛 저장본을 계속 내주어 사용자를 낡은 화면에 가두는 것이다. 저장된 것을 먼저 쓰는 전략을 무거운 자원에 넓게 적용하면, 자원을 바꿔도 사용자는 옛 저장본만 보게 된다. 중개자가 요청을 가로채 옛것으로 답하기 때문에, 평소의 새로고침으로도 풀리지 않아 원인을 찾기 어렵다.
그래서 무엇을 저장하고 언제 갱신할지의 규칙을 처음부터 신중하게 설계해야 한다. 자주 바뀌는 자원과 거의 바뀌지 않는 자원을 나누어 서로 다른 전략을 배정하고, 저장본을 언제 새 판으로 교체할지를 분명히 정해 두어야 한다. 앞 편에서 다룬 이름에 표식을 넣는 방법을 함께 쓰면, 바뀐 자원이 새 이름으로 보여 옛 저장본과 자연스럽게 구분된다.
중개자를 새 판으로 교체할 때도 신중해야 한다. 생명주기의 신중함 덕분에 옛 페이지가 도중에 뒤섞이지는 않지만, 새 판이 언제 통제권을 넘겨받는지를 이해하지 못하면 갱신이 제때 반영되지 않는 것처럼 보인다. 새 판이 준비되었음을 사용자에게 알리고 페이지를 다시 열도록 안내하는 배려가 매끄러운 갱신을 돕는다.
정리하면, 이 중개자는 페이지와 통신망 사이에서 요청을 가로채 저장과 통신을 통제하며, 단계를 밟아 자리 잡고, 자원마다 알맞은 전략으로 요청을 처리해 오프라인 동작과 배경 기능까지 가능하게 한다. 강한 통제력에는 낡은 화면에 가두지 않을 책임이 따르므로, 저장과 갱신의 규칙을 신중히 설계하는 것이 이 스크립트를 다루는 핵심이다. 다음 편에서는 지금까지 다룬 모든 단계를 실제로 눈으로 확인하고 진단하는 도구, 곧 개발자 도구의 활용으로 이 시리즈를 맺는다.