blog.coinsect.io
🇰🇷
원문
마크다운
2024-06-16
277
공유

SEO 이야기 #1 - 다양한 렌더링 방식과 검색엔진

SEO란 Search Engine Optimizing, 검색 엔진 최적화를 말한다. 검색엔진(특히 구글)이나 SNS들이 내가 만든 웹사이트의 내용을 잘 읽고 내용을 이해할 수 있게 하는 작업이다. 이것은 브랜드 인지도와 마케팅에 큰 영향을 줄 수 있다. 구글은 대개 양질의 정보를 상단에 띄워주는 경향이 있고, 사람들이 많이 검색해볼 법한 키워드들에서 내 컨텐츠가 상위권에 노출된다면 웹사이트 유입을 늘리는데 큰 도움이 된다. 이를 위해 필요한 것들을 알아보자. ### SSR (Server-Side Rendering) 2000년대 말부터 2010년대 초까지 웹개발을 했던 경험이 있다면 아마 딱히 SSR이라는 표현을 들어볼 일도 없었을 것이다. 왜냐하면 그당시 인기있었던 서버 언어들인 PHP, JSP, Ruby 등은 당연히 SSR을 하고 있었기 때문이다. 이들은 HTML 마크업 파일들도 서버에서 갖고 있고, DB 연결부터 이 템플릿에 바인딩할 데이터를 입혀서 response로 만들어주는 것까지 모두 서버에서 처리하는 구조였다. 당연히 크롤러들은 응답이 꽉꽉 차있는 이런 사이트들을 매우 잘 읽었다. ### SPA (Single-Page Application) 그런데 2010년대 중반쯤부터 Angular를 필두로 React, Vue 등이 나타나며 범람(?)하기 시작한 SPA(Single-Page Application)들은 CSR(Client-Side Rendering)을 한다. 유저는 컨텐츠가 다 들어있는 문서를 다운로드하는 것이 아니라, 우선 빈 `index.html` 및 거기 `script src`로 첨부된 자바스크립트 번들 파일만 다운로드하게 된다. 브라우저가 이 자바스크립트를 해석하고 나면 그때부터 화면을 그리기 시작한다. 그래서 처음에 받은 페이지에서 사실은 다른 HTML 페이지로의 이동이 없기 때문에 Single-Page 앱이라는 것이다. ![SPA는 페이지가 비어있다](https://d1085v6s0hknp1.cloudfront.net/boards/free_board/c39edbc0-b24f-4e4a-ac4e-07ebd20690bb_1.png) 위 사진에서 보이듯이, SPA에서는 HTML의 `body` 부분 안쪽이 비어있다. 자바스크립트 해석이 완료되면 클라이언트에서 해당 부분을 채워넣는다. 페이지 이동은 브라우저의 history API를 이용해 구현하며, 유저에게 마치 페이지를 이동하는 듯한 환상을 준다. 실제로는 동일한 페이지인데 라우트 스택만 쌓으면서 내용만 갈아끼우는 것이다. 당연히 검색 엔진 등의 크롤러들이 봤을 때는 페이지의 내용이 아무것도 없다. 물론 구글을 비롯한 요즘 검색엔진들은 워낙 똑똑해서 자바스크립트까지도 돌려서 내용을 확인하기는 하나, 그렇다고 이걸 믿고 SSR 처리를 안 할 수는 없다. 그럼 검색엔진이 볼 수 있는 내용이 없다는 단점에도 불구하고 왜 SPA를 쓰는 것일까? 워드프레스 등을 사용하는 고전적인 MPA(Multi-Page Application) 웹사이트들은 이동시마다 페이지를 새로고침하는 반면, SPA는 같은 페이지에서 화면만 계속 바꿔 끼우는 구조라 빠릿빠릿해서 유저 경험이 매우 좋기 때문이다. 그리고 뷰 로직을 서버에서 분리해 별도의 프로젝트로 만들면 서버는 JSON만 내려주는 온전한 API 서버로 만들 수 있어 비즈니스 로직에만 집중할 수 있게 된다. 또한 이렇게 되면 꼭 웹사이트가 아니라 iPhone이나 Android 등의 네이티브 앱에서도 동일한 API를 공유할 수 있게 되어 사업의 확장성이 커진다. 그리고 서버가 HTML을 렌더링할 필요 없이 JSON만 내려주면 되므로 서버의 부하도 적어진다. 다음 중 어떤 것을 응답하는 것이 서버 입장에서 더 편하겠는가? ```javascript // JSON { "name": "Chris", "birth": "2020-03-09", "website": "https://blog.coinsect.io" } ``` ```markup // HTML <div style="why should server have style codes too?">Chris</div> <div style="there is no reason for it">2020-03-09</div> <div style="well maybe there's good reason for it">https://blog.coinsect.io</div> ``` DOM 구조나 스타일이 복잡해질수록 전자가 훨씬 가벼운 형태임이 자명해지며, 잘 생각해보면 대부분의 경우는 서버가 앱의 뷰 스타일링에 관한 정보까지 갖고 있을 필요가 없다. 물론 SPA는 다운로드해야하는 자바스크립트 양이 많기 때문에 페이지 초기 로딩이 네트워크 상황에 따라 느릴 수 있다는 단점이 있다. 자바스크립트 번들을 잘게 쪼개는 code-splitting을 활용해 초기에 불러와야 할 파일들의 용량을 줄이고~~js 묶으려고 번들러를 쓰는건데 다시 나눌 궁리를 함~~, 또 필요한 것들을 그때그때 적절히 lazy-loading하는 것이 중요하다. 보통 lazy-loading의 대상이 되는 것들은 라우트, 서드 파티 라이브러리들이다. 또한 서드 파티 라이브러리를 사용할때는 꼭 용량을 체크해서 가급적 npm install보다는 lazy-loading을 하도록 하자. [bundlephobia](https://bundlephobia.com/)를 보면 이것이 왜 중요한지 자연히 알게 된다. ### Universal Rendering (SSR + CSR) SSR을 하는 것은 유저가 내용을 빠르게 볼 수 있도록 하는데도 도움이 된다. 물론 자바스크립트 로딩(hydration)이 끝나야 Interactive해지기는 하나, 거기까지 걸리는 시간은 대개 유저가 알아차리기 힘들 정도로 매우 짧기 때문에 유저가 우선 내용을 빨리 볼 수 있게 하는 것이 중요하다. 이에, SPA를 SSR할 수 있는 방법이 등장하기에 이른다. 아이디어는 다음과 같다. 1. 웹서버를 하나 둔다. 2. request가 오면 SPA에서 해당 route에 접속했을 경우의 화면을 string으로 렌더링한다. (e.g. React나 Vue 등의 도구들은 `renderToString` 등 SPA를 string으로 만들 수 있는 함수를 제공한다.) 3. 이 string이 유저가 다운로드할 페이지인 index.html이다. 여기에는 페이지의 내용들과 메타 태그 등을 포함해 검색엔진에게 제공할 정보를 완벽하게 다 담고 있어야 한다. 말은 간단한데, 실제 직접 구현해보면 상당한 난이도가 있다. 서버에서는 컨텐츠와 상태를 문자열로 렌더링하고 클라이언트에서는 이 결과물에 자바스크립트를 붙이는 hydration을 하고 서버에서 만들어진 상태를 다시 브라우저의 메모리에 채워 넣어야 하는데, 이런 과정에 대한 이해가 다소 진입장벽을 가진다. 서버의 상태는 보통 JSON 문자열로 만들어 `<script>window.__INITIAL_STATE__={서버상태JSON}</script>` 형태로 넣고, 이것을 클라이언트가 로드된 후 읽어오는 방식으로 전달한다. **그럼 굳이 이렇게까지 복잡하게 웹앱을 구현하는 이유가 뭘까?** 아래의 두 마리 토끼를 다 잡기 위해서이다.~~(대신 개발자 비용은 비싸진다.)~~ 1. 유저나 봇의 첫 request에 대해서는 SSR을 하기 때문에, 서두의 사진에서처럼 내용이 빈 `index.html`이 아니라 채워진 문서를 보여줄 수 있어 검색엔진 최적화가 가능하다. 2. 이후 hydration이 완료되고 나면 일반적인 SPA 형태로 작동(CSR)하므로 유저 경험이 매우 좋다. 다행히도 요즘은 Universal Rendering을 손쉽게 할 수 있게 해주는 좋은 툴들이 많다. 대표적으로 React 진영의 Next와 Remix, Vue의 Nuxt, Svelte의 SvelteKit이 있다. 툴은 달라도 과정은 대체로 위의 순서를 따르기 때문에 본인이 모던 프론트엔드 개발자라면 반드시 위 과정을 숙지하고 있어야 한다. 어쨌든 이 과정을 통해 웹사이트를 올리고, 이미 구글이 알고 있는 사이트들에 내 사이트의 링크를 올리면 구글이 인덱싱해준다. ![비트코인 블로그 - 비트코인은 최고의 화폐입니다](https://d1085v6s0hknp1.cloudfront.net/boards/free_board/dc60090e-0050-4dd4-8831-9248540a8382_image.png) 그럼 다음 글에서는 검색엔진에 어그로를 끄는 다양한 방법들을 알아보자. (sitemap, robots.txt, metatags...)
0