⟨99haus⟩ 프로젝트 - 웹 페이지 로드 속도 개선

2025년 5월 19일 오전 12시 28분 KST

Notion으로 웹 페이지 게시글을 작성하고 관리하도록 한 것은 현명한 선택이었다고 생각합니다. 웹 페이지의 주인 되시는 분도 덕분에 게시글 몇 개를 올리면서 어렵지 않게 잘 써먹고 있고, 조금은 자랑이지만, Google에 99haus를 검색하니 첫 번째 결과로 이 사이트가 표시되는 걸 보고 조금 뿌듯했습니다.

저에게 있어서 참으로 뿌듯한 이 프로젝트 덕분에, 하루에 몇 번씩 이 웹 페이지에 접속하는 게 저의 일상이었습니다. 새로운 글은 없는지, 아니면 개선할 점은 무엇이 있을지 찾아보는 것이었죠.

그러나 그럴 때마다 한 가지 걸리는 게 있었습니다. 바로, 페이지 로드 속도입니다.

좀 많이 느린 페이지 로드 속도

99haus 페이지의 네트워크 정보. 로드 완료까지 6.29초가 소요되었다.

Notion API에서 게시글을 다이렉트로 불러오기 때문에 로드 속도가 좀 처참했습니다. 페이지를 접속하고 게시물 목록이 보이기까지 6초 가량이 소요되던 것이었습니다. 개발 초기에도 이 문제를 인지하고 있었고, React의 Suspense 훅을 이용해서 콘텐츠가 로드되기 전까지 로드 스피너를 표시하는 식으로 대처를 하긴 했습니다만, 매일 일상적으로 이 페이지를 방문하는 입장에서 긴 로드 속도는 점점 참기 힘든 문제였습니다. 검색 엔진 최적화에도 부정적인 영향을 줄 우려도 생기더군요.

이 문제를 해결하기 위해서도 많은 고민을 했습니다만, 답은 금방 찾을 수 있었습니다.

데이터베이스를 쓰자!

Notion API로 게시글을 불러오는 것이 느리다는 것을 알았으니, 해결책은 간단합니다. 데이터를 미리 받아두는 것이죠. 요청할 때마다 Notion API로 일일이 글을 불러오는 게 아니라, 미리 데이터베이스에 글을 저장해 두고, 요청할 때는 데이터베이스에서 불러오는 식으로 문제를 해결하고자 했습니다.

어떤 데이터베이스 솔루션을 사용할 것인지에 대해서도 고민을 했습니다만, 최종적으로는 Supabase를 선택했습니다. 무료 플랜만으로도 API 요청 횟수가 무제한인 점이 매력적이었고, 무엇보다 Firebase Firestore과 같은 NoSQL 경험은 많지만 관계형 데이터베이스에는 아직 익숙하지 않았기에, PostgreSQL을 써보고 싶었습니다.

여튼, Supabase에 Notion 게시글 정보를 담기 위해 저는 별도의 애플리케이션을 만들었습니다. 기존의 Next.js 애플리케이션에서 Notion API을 이용해 게시글을 불러오는 로직은 그대로 가져 오고, 대신 이렇게 가져온 게시글을 Supabase에 저장하는 역할만 수행합니다.

웹 페이지에 데이터 반영이 필요할 때 API를 호출하면 동기화가 진행됩니다. 스케줄링과 같은 주기적인 동기화는 구현하지 않았고, Notion에서 데이터가 변경될 때마다 웹훅을 이용해서 Supabase에 동기화하는 방안도 생각해봤습니다만, 웹 페이지 주인 되시는 분이 지금 방법도 충분히 만족하시기에 이 부분은 차후 개발 목표로 남겨두기로 했습니다.

기왕 이렇게 된 거, 이미지도 같이!

Notion API에 대한 의존을 줄이고자 DB를 도입했으니, 마찬가지로 이미지 파일들도 Notion에 의존하고 싶지는 않았습니다.

기존에 Notion API를 이용할 때도 이미지 처리는 가장 큰 과제였습니다. Notion API를 통해 받아온 이미지 링크를 그대로 쓸 수 없었기 때문입니다. 정확히는, 이미지 파일마다 TTL이 걸려 있어서 일정 시간이 지나면 이미지가 웹 페이지에서 보이지 않을 우려가 있었습니다. 이를 우회하기 위해 실제 Notion의 웹 공유 페이지에서 표시되는 이미지 경로를 응용하여 원본 이미지 경로를 가공하는 방식으로 문제를 해결했었습니다.

이렇게 해서 이미지의 TTL이 문제가 되지는 않았습니다만... 게시글 작성자가 업로드한 이미지가 별도로 압축되지 않는 모양인지 이미지 파일의 용량이 지나치게 크다는 문제도 있었습니다. 이 또한 Notion 웹 공유 페이지에서 사용되는 경로 형식에 width 속성을 잘 건드리면 반응형 이미지를 구현할 수 있다는 점을 찾았기에 어느 정도 해결할 수는 있었지만, 그래도 이미지가 불필요하게 크다는 점은 마음에 계속 걸렸었습니다.

원한다면 이미지 경로는 그대로 Notion의 것을 사용하면서 별도로 스토리지를 구축할 필요가 없었겠지만, 지금 방식이 위와 같은 문제가 있었기 때문에 저는 기왕이면 이미지도 스토리지에 따로 압축해서 저장하고 싶어졌습니다.

스토리지 또한 어떤 솔루션을 사용할지에 대해 고민했습니다. Supabase를 이미 쓰고 있기에 스토리지도 Supabase의 것을 써도 되긴 했겠지만, Cloudflare R2를 기존에 사용해 본 경험이 있기도 했고, 가격 정책도 대단히 매력적이었기 때문에 이쪽을 선택했습니다. Amazon Web Services의 S3 API와 호환된다는 점도 좋았고요.

이미지 파일들은 게시글을 Notion API에서 불러와서 Supabase Database에 담을 때 같이 Cloudflare R2 버킷에 저장합니다. 이 부분은 기존에 Notion API를 사용할 때 만들어 둔 이미지 경로 처리 라이브러리를 응용했기에 어렵지 않았습니다. 이미지 파일들의 원본 경로를 찾아서 새 경로로 바꾸던 작업을, 이미지를 다운로드받아서 압축하고 R2 버킷에 저장하는 작업으로 바꿨을 뿐입니다.

얼마나 줄었나?

개선된 99haus 페이지의 네트워크 정보. 로드 완료까지 471ms가 소요되었다.

결과는 대성공이었습니다! 페이지 로드가 약 6초에서 약 0.4초로 대폭 줄었습니다. 대략 93.33% 감소한 셈이네요. 사실 기존의 로드 속도가 너무나도 터무니없는 수치였기 때문에 이런 기적적인 감소율이 나온 것이긴 합니다만, 어찌 됐든 로드 속도 문제를 해결할 수 있었으니 이걸로 된 거죠.

돌이켜 생각해 보면, 데이터베이스로의 전환은 언젠가는 필요했었을 것 같습니다. 지금이야 방문자가 적기 때문에 별 문제가 되지 않았겠지만, 향후 방문자가 폭증할 경우 Notion API의 할당량이 소진될 수도 있을테니깐요(정확한 할당량은 잘 모르겠습니다).

페이지 로드 속도에 따른 방문자 이탈률 통계 그래프. 페이지 로드 속도가 1초에서 6초로 떨어질 경우 사용자 이탈률이 106% 증가한다.

문제를 해결하고 나서, 문득 페이지 로드 속도에 따른 방문자 이탈률이 궁금해졌습니다. Google에서 만든 통계가 있더군요. 페이지 로드 속도가 1초에서 6초로 증가할 경우 이탈률이 무려 106%나 증가한다고 하네요. 아무리 매력적인 콘텐츠를 제공한다고 한들, 기초적인 사용자 경험부터 좋지 못하다면 아무 소용 없다는 것을 배우게 되었습니다.


jihun의 프로필 일러스트.

jihun, 테크놀로지에 상상력을 더하는 프론트엔드 개발자.