원본 글: Preload, Preconnect, Prefetch: Improve Your Site's Performance with Resource Hints
요약: 리소스 힌트(Resource hint)는 브라우저가 웹페이지를 더 빠르게 로딩할 수 있게 해주는 HTML 태그입니다. 리소스 힌트를 사용하면 페이지 로딩 속도를 단축시키거나 더 나은 사용자 경험을 제공할 수 있습니다. 프리로드(Preload)는 중요한 리소스들을 페치하고 캐싱합니다. 프리커넥트(Preconnect)는 외부 사이트로의 연결을 미리 설정합니다. 그리고 프리페치(Prefetch)는 중요할 것 같은 리소스들을 미리 로드합니다.
모던 웹 브라우저들은 리소스들의 우선순위를 정하고 중요한 리소스를 먼저 페칭하거나 사용자가 방문할 것 같은 페이지를 추측하는 등의 다양한 테크닉들을 사용하여 웹사이트의 전체적인 성능을 끌어올립니다.
하지만, 아무리 브라우저의 성능이 좋아졌더라도, 사이트의 성능에 대한 결정을 브라우저에만 의존하는 건 좋지 않은 선택일 수 있습니다.
어떤 리소스가 가장 중요하고 사용자가 실제로 어떻게 사이트를 사용하는 지는 웹사이트의 주인인 당신이 가장 잘 알고 있습니다. 그리고 웹사이트의 전체적인 성능, 체감 속도, 그리고 사용자 경험을 끌어올리기 위해서 브라우저가 페이지를 더 빨리 로드하게 하고 싶을 겁니다.
여기서 우리는 리소스 힌트를 사용할 수 있습니다.
다음 내용들에선 리소스 힌트에 대해서 알아보고 어떻게 가장 잘 활용할 수 있는 지 살펴 보겠습니다.
리소스 힌트에 대한 이해를 하기 위해 브라우저가 어떻게 동작하는 지 간단히 알아봅시다.
선언문: 다음 내용은 브라우저의 실제 동작 방식을 매우 간단하게 표현한 것입니다. 만약 더 자세한 내용을 알고 싶으면 이 글을 살펴 보세요. - Populating the page: how browsers work
우리는 브라우저가 페이지를 로딩할 때 전체 동작 과정을 세 단계로 나눌 수 있습니다.
첫번째 단계에서, 브라우저는 리소스를 다운로드하기 위해 서버와의 연결을 수립합니다. 이 과정에서 도메인의 IP 주소를 조회하고, 서버와의 연결을 설정하고, 보안을 위해 연결을 암호화합니다.
위 과정이 완료되면, 브라우저는 정보를 다운로드 받고 파싱하여, 도큐먼트 객체 모델(DOM)과 CSS 객체 모델(CSSOM)을 생성하고, 콘텐츠를 렌더링하게 됩니다.
마지막 단계는 페이지를 상호작용하게 만드는 것입니다. 앞서 말한 모든 과정은 모두 메인 스레드에서 일어나는 일입니다. 브라우저의 메인 스레드에서 파싱, 렌더링, 페인팅이 끝나면 그 뒤엔 지연된 자바스크립트 파일을 통해 스크롤, 터치 등의 상호 작용을 가능하게 합니다.
간단하게 표현했지만, 페이지가 로딩될 때마다 브라우저 내부에선 이런 과정이 매번 일어나게 됩니다.
이제 리소스 힌트를 어떻게 이 단계들에 적용할 수 있을 지 알아봅니다.
이름이 말해주는 것처럼, 리소스 힌트는 브라우저에게 특정 리소스나 웹페이지를 어떻게 다뤄야 할 지 알려주는 힌트 또는 지침입니다. 다른 말로 하면, 이 지침들을 이용해서 페치하거나 렌더할 출처와 리소스들의 우선 순위를 정하는 데에 도움을 줄 수 있습니다.
모든 리소스 힌트는 HTML 문서 head
태그 안에 있는 link
요소의 rel
속성에서 사용할 수 있습니다.
이 코드 스니펫들을 웹사이트에 적용하면 특정 파일들을 일반적인 방법으로 페이지를 로딩할 때 찾아내는 것보다 브라우저가 더 빨리 로딩할 수 있게 합니다.
이제 리소스 힌트의 개요와 장점, 그리고 사용법에 대해 알아보겠습니다.
link rel=prefetch
는 나중에 사용될 수도 있는 리소스를 페치하고 브라우저의 캐시에 저장하게 하는 낮은 우선 순위의 리소스 힌트입니다.
prefetch
는 매우 낮는 우선 순위를 가지기 때문에, 높은 중요성을 가진 파일에 사용하는 건 올바르지 않습니다.
prefetch
를 사용하는 좋은 방법은 후속 페이지의 로딩 시간을 개선시키기 위해서입니다. 예를 들어, prefetch
디렉티브를 사용자 인증에 적용할 수 있습니다. 이 방법을 사용하면, 사용자가 개인정보를 입력하는 시간을 활용하여 다음에 보게 될 페이지에 필요한 리소스들을 미리 로드할 수 있습니다.
사이트 방문자의 행동을 예상하고 리소스를 프리페칭함으로써, 콘텐츠가 포함된 첫 페인트와 상호작용 시작 시간같은 지표들을 향상시킬 수 있습니다. 넷플릭스가 상호작용 시작 시간을 30% 줄였던 것처럼 말이죠.
지금까지 말한 모든 건 링크 프리페칭이라고도 할 수 있는 프리페칭에 대한 이야기입니다. 하지만 다른 두가지의 프리페칭 또한 중요하므로 살펴보고 갑니다.
브라우저는 호스트(서버)에 연결하기 전에 호스트 이름(URL)을 IP 주소로 변환하는 DNS 룩업이라는 과정을 거칩니다.
이 과정은 몇 밀리초밖에 걸리지 않습니다. 하지만 만약 대부분의 웹사이트에서 하는 것처럼 서드 파티 도메인에서 파일들을 로딩하려면, 브라우저는 DNS 룩업을 각각의 도메인마다 실행해야 합니다. 몇몇 사이트(예: 뉴스 웹사이트)는 매우 많은 외부 리소스를 사용하는데, 이는 페이지당 몇십개의 DNS 룩업이 있을 수도 있다는 것을 의미합니다.
이런 케이스에서 dns-prefetch
힌트를 사용하면 로딩 과정에서 리소스가 필요하다는 걸 알고 난 뒤 수행하는 게 아니라, 지금 바로 이러한 작업이 필요하다는 걸 브라우저에게 알려줍니다. 결과적으로는 이 몇 밀리초들을 아낄 수 있습니다.
Web Almanac 2021에서 소개되었듯이, 좋은 실례는 최적화된 결과를 위해 dns-prefetch
를 preconnect
힌트와 함께 사용하는 것입니다. 그 이유는 preconnect
에 대해 이야기하는 섹션에서 알아보겠습니다.
프리렌더링은 사용자가 방문할 지도 모르는 리소스를 미리 최적화한다는 점에서 프리페칭과 매우 비슷합니다. 차이점은, 특정 리소스 대신에 프리렌더는 전체 페이지를 실제로 렌더링한다는 점입니다.
dns-prefetch
처럼, preconnect
디렉티브는 서버에 첫번째 요청을 보내기 전에 브라우저가 초기 연결을 설립하는 데에 도움을 줍니다.
그렇지만 preconnect
는 한발짝 더 나아가 있습니다. preconnect
를 사용한 DNS 룩업에서는 TLS 협상과 TCP 핸드셰이크를 포함합니다. 이것은 결과적으로, 왕복 시간(Time To Interactive)을 없애고 시간을 더 많이 아낄 수 있게 합니다.
하지만 여기서 궁금증이 하나 생깁니다.
dns-prefetch
가 하는 것, 그리고 그 이상을 preconnect
가 할 수 있다면 애초에 dns-prefetch
를 사용할 이유가 없지 않나요?
대부분의 케이스에서 preconnect
는 dns-prefetch
를 대신해 선택될 수 있지만, 문제는 preconnect
가 몇몇 브라우저에서 지원하지 않는다는 점입니다.
(역자 주: 2024년 현재 preconnect
는 IE와 Opera Mobile을 제외한 대부분의 브라우저에서 지원하고 있습니다.)
다행인 점은 둘을 같이 사용하면 최적의 결과를 낼 수 있습니다. preconnect
에 대한 폴백으로 다음과 같이 dns-prefetch
를 사용할 수 있습니다.
구글에 따르면 다음과 같습니다.
중요한 서드 파티 원본에 대한 조기 연결을 설정하면 로드 시간을 100~500ms 단축할 수 있습니다. 이 수치는 사소해 보일 수 있지만 사용자가 웹페이지 성능을 인식하는 방식에 차이를 만듭니다.
2019년에는 크롬이 중요한 출처들을 프리커넥트함으로써 왕복 시간을 1초 가까이 줄일 수도 있었습니다.
preload
디렉티브가 어떻게 동작하는 지 설명하기 전에 한가지를 분명히 하고자 합니다.
preload
가 주로 '리소스 힌트'로 언급되긴 하지만 사실 그렇지 않습니다. 프리로드는 선언적 페칭이고, 힌트보단 명령어에 가까워 브라우저가 무조건 실행하게 됩니다.
다시 말하면, 페이지에서 매우 중요한 리소스를 preload
는 브라우저가 발견하기 전에 강제로 다운로드하게 할 수 있습니다.
preload
디렉티브는 중요 렌더링 경로에 포함되지만 브라우저가 쉽게 발견하지 못하는 리소스에 제일 잘 동작합니다. 예를 들어, 폰트, CSS, 중요한 자바스크립트 파일 등이 있습니다.
dns-prefetch
그리고 preconnect
와의 또다른 차이점은, 앞의 두가지가 rel
과 href
속성만 필요하다면 preload
는 좀 더 복잡하다는 것입니다. 프리로드하고 싶은 리소스의 컨텐츠 타입을 명시한 as
속성을 추가해야 합니다.
구글 엔지니어링 매니저인 Addy Osmani에 따르면, 프리로딩할 때 as
속성을 명시해주는 건 필수적입니다.
만약 무엇을 프리로드할 지 명시할 때 유효한
as
를 사용하지 않는다면, 예를 들어 스크립트의 경우, 결과적으로 두번 페치하게 될 것입니다.
아래는 표시할 수 있는 모든 as
속성의 값 목록입니다.
as
속성을 포함시키는 것은 브라우저가 타입에 따라 프리페치된 리소스의 우선 순위를 정하는 데에 도움을 주고 캐시에 그 리소스가 이미 있는 지 판단할 수 있게 해줍니다.
어떻게 리소스 타입에 따라 우선 순위가 결정되는 지 알아보려면 Chrome Resource Priorities and Scheduling을 참고하세요.
폰트 같은 몇몇 리소스들에는 crossorigin
속성 또한 포함해 주어야 합니다.
crossorigin
속성은 HTTP CORS 리퀘스트의 모드를 설정합니다. CORS(교차 출처 리소스 공유, Cross-Origin Resource Sharing)은 브라우저가 리소스를 로딩하기 위해 허용해야 하는 출처들을 서버가 자기 자신 이외에도 표시하도록 허락해주는 메커니즘입니다. 여기서 더 자세하게 언급하지는 않겠지만, CORS에 대해 알아보고 싶다면 이 문서를 참고하세요.
as
속성과 비슷하게, crossorigin
없이 폰트를 프리로딩 하는 건 더블 페칭이 될 겁니다. 여기에 Addy Osmani의 글에서 발췌한 이 주제에 관한 또다른 내용입니다.
프리로드를 사용해 폰트를 페칭할 때
crossorigin
속성을 추가하지 않으면, 두번 다운로드 될 겁니다. 리퀘스트가 익명 모드 CORS를 사용하기 때문입니다. 페이지와 같은 출처에서 폰트가 로딩된다고 해도 마찬가지입니다. 또한 다른 익명 페칭에도 적용될 수 있습니다(예: 기본 XHR).
여기까지 읽고 난 다음, 가능한 한 많은 리소스 힌트를 사용하면 무조건 브라우저가 페이지를 더 빠르게 로딩하게 될 거라고 생각할 수도 있습니다.
하지만 그건 정답이 아닙니다.
아래는 리소스 힌트를 적용할 때 고려해야 할 몇가지 방해물입니다.
prefetch
가 낮은 다운로드 우선 순위를 가지고 있다고 해도, 그것이 아무 문제점이 없다는 뜻은 아닙니다. prefetch
는 사이트의 데이터 사용량을 늘릴 수 있는데, 그건 당신(늘어난 서버 트래픽)과 사용자(불필요한 리소스 사용) 모두에게 영향을 줄 수 있습니다. 또한, 결과적으로 사용하지 않을 데이터를 로딩할 수도 있습니다. 그러므로 여러번 심사숙고하고 사용하세요.
prerender
를 이용한 도박은 전체 페이지들을 미리 다운로드 하기 때문에, prefetch
보다 더 큰 판돈을 걸어야 합니다. 이는 prerender
를 리소스적으로 무거운 힌트로 만들고, 대역폭 낭비를 유발하게 합니다. 특히 모바일 디바이스에서 말이죠. 그리고 최악의 부분은 유저가 페이지를 요청하지 않는 경우 힌트의 효과가 아무것도 발휘되지 않는다는 점입니다.
preconnect
가 낮은 우선 순위를 가지고 있다고 해도, 여전히 사이트의 성능에 안좋은 영향을 끼칠 수 있습니다. 만약 설정된 연결이 빨리 사용되지 않는다면(크롬의 경우 10초 이내), preconnect
디렉티브는 CPU 사용량만 늘린 뒤 브라우저에 의해 자동으로 종료될 것입니다. 추가적으로 암호화 인증의 크기는 3KB 내외인데, 이는 다른 중요한 리소스들과 대역폭 경쟁을 할만한 정도의 크기입니다. 그러므로 preconnect
를 아껴서 사용하세요.
preload
는 브라우저가 즉시 리소스를 다운로드하게 만드는 강력한 지시문입니다. 하지만, 모던 브라우저는 리소스 우선 순위 최적화가 꽤 잘 되어있어 preload
를 지나치게 사용하는 건 부정적인 결과가 나올 수도 있습니다. 예를 들어, 비동기 리소스 URL에 preload
를 사용한다고 생각해 봅시다. 그러면 브라우저는 리소스를 더 빨리 페치하고 더 빨리 파싱해서, 페이지 로딩 시 초기 메인 스레드를 방해해 비동기의 장점을 모두 없애버릴 것입니다.
지금까지 많은 내용에 대해 이야기했는데, 그 중에서 가장 중요한 부분들을 다시 살펴봅시다.
dns-prefetch
와 preconnect
는 도메인 이름의 우선 순위를 정하는 데에 사용됩니다(예: https://example.com).prefetch
와 preload
는 리소스 로딩의 우선 순위를 정하는 데에 사용됩니다. prefetch
가 뒤따라 오는 페이지의 로딩 시간을 개선하는 데에 쓰이는 반면, preload
는 현재 페이지의 중요한 리소스에 가장 잘 작동합니다.prerender
는 전체 페이지를 참조합니다(예: blog.html).prefetch
, prerender
, 그리고 preconnect
는 리소스 힌트들이고, 브라우저가 봤을 때 잘 맞을 것 같은 곳에 사용됩니다. preload
디렉티브는 브라우저가 실행해야만 하는 명령어입니다.preload
를 사용할 때 더블 페칭을 피하기 위해 as
와 crossorigin
속성을 포함하는 걸 잊지 마세요.preload
는 브라우저 분석기의 우선 순위를 덮어 씌울 수 있는 매우 강력한 디렉티브입니다. 모던 브라우저는 리소스 우선 순위 최적화가 매우 잘 되어 있다는 걸 명심하시고, preload
'힌트'는 아껴서 사용하세요.새롭게 알게 된 리소스 힌트에 대한 지식을 사용해서 컨텐츠와 에셋 로딩 속도를 늘리고 사이트의 전체적인 성능을 끌어올려 보세요. 그리고 새로운 업데이트를 할 때마다 실제 환경(현장 데이터에 집중하세요)에서 웹사이트를 테스트 해보는 걸 잊지 마세요.
Niko Kaleev, 2024