hswork.win

Vue로 만든 사이트가 검색에 안 잡히던 이유

2026-07-10 · 프론트엔드 · SEO

Vue로 사이트를 만들고 한참이 지나도 검색 결과에 제대로 잡히지 않았습니다. 글도 있고 계산기도 있는데 검색엔진에서는 존재감이 없었습니다. 원인을 찾다가 아주 기본적인 사실을 마주했습니다. 싱글 페이지 애플리케이션(SPA)은 자바스크립트가 실행된 다음에야 화면에 내용이 채워진다는 것입니다.

브라우저에서 소스 보기를 눌러보면 안다

확인 방법은 간단합니다. 페이지에서 "소스 보기"를 누르면 실제로 서버가 내려준 HTML을 볼 수 있습니다. 제 사이트는 그 안에 본문이 한 줄도 없었습니다. <div id="app"></div> 하나와 스크립트 태그 몇 개가 전부였습니다. 사람이 브라우저로 보면 멀쩡하지만, 자바스크립트를 실행하지 않고 HTML만 읽는 쪽에서는 텅 빈 페이지인 셈입니다.

선택지: SSR이냐, 프리렌더냐

해결책은 크게 둘입니다. 하나는 서버에서 매 요청마다 화면을 그려 내려주는 서버 사이드 렌더링(SSR)이고, 다른 하나는 빌드할 때 페이지들을 미리 HTML 파일로 만들어 두는 프리렌더(SSG)입니다. SSR은 강력하지만 서버가 항상 렌더링을 감당해야 하고 구조도 복잡해집니다. 제 사이트는 내용이 자주 바뀌지 않는 정보성 페이지가 대부분이라 프리렌더가 훨씬 알맞았습니다.

방식은 이렇습니다. 빌드가 끝나면 스크립트가 각 페이지의 본문 텍스트를 정적 HTML로 만들어 결과물 폴더에 저장합니다. 방문자가 주소로 들어오면 서버는 이미 내용이 들어 있는 HTML을 그대로 내려주고, 그 뒤에 자바스크립트가 붙어 원래의 동적인 화면으로 전환됩니다. 사람과 크롤러가 같은 내용을 보게 되는 구조입니다.

데이터가 바뀌는 페이지는 어떻게 할까

여기서 한 가지 문제가 더 있었습니다. 일부 페이지는 데이터베이스의 내용을 화면에서 실시간으로 불러오고 있었습니다. 그러면 정적 HTML에는 그 내용이 담기지 않습니다. 결국 크롤러가 보는 페이지와 사용자가 보는 페이지가 서로 달라지는 상황이 됩니다. 검색엔진 입장에서 좋을 리 없습니다.

그래서 규칙을 하나 세웠습니다. 동적인 데이터는 빌드 시점에 한 번 굳혀서 정적 파일로 만든다는 것입니다. 빌드가 시작되면 스크립트가 데이터베이스에서 공개 상태인 항목만 가져와 데이터 파일로 저장하고, 이후 모든 화면과 정적 HTML 생성이 그 파일 하나만 바라보게 했습니다. 데이터의 원본은 여전히 데이터베이스라 관리·수정은 편하고, 배포된 결과물은 항상 일관됩니다.

없는 주소가 200을 돌려주고 있었다

프리렌더를 붙이고 한동안은 잘 굴러가는 줄 알았습니다. 그런데 나중에 확인해 보니 존재하지 않는 주소도 정상 응답을 내주고 있었습니다. SPA는 어떤 경로로 들어와도 일단 같은 HTML을 내려준 뒤 자바스크립트가 화면을 결정하는 구조라, 서버 설정에 "없는 파일이면 무조건 첫 화면을 준다"는 폴백을 넣어두는 게 관행입니다. 그 설정이 그대로 남아 있었던 겁니다.

사람 눈에는 문제가 없습니다. 오타를 쳐도 홈이 뜨니까요. 문제는 크롤러 쪽입니다. 아무 주소나 넣어도 200이 돌아오니, 내용이 똑같은 페이지가 무한히 있는 사이트로 보입니다. 실제로 광고 심사에서 "가치가 낮은 중복 페이지" 지적을 받고 나서야 이걸 진지하게 봤습니다.

고치는 방법 자체는 한 줄이었습니다. 무조건 첫 화면으로 넘기던 폴백을 없애고, 미리 만들어 둔 정적 파일이 있으면 주고 없으면 404로 응답하게 바꿨습니다. 대신 부작용이 생겼습니다. 정적 생성 목록에 빠진 페이지가 있으면 그 순간 진짜로 404가 됩니다. 그래서 새 화면을 만들 때 등록해야 하는 곳을 세 군데로 못 박았습니다. 화면 경로 설정, 정적 HTML 생성 목록, 사이트맵입니다. 하나라도 빠지면 배포 직후 바로 티가 납니다.

색인에서 빼는 것도 설계다

모든 페이지를 검색에 노출시킬 필요는 없었습니다. 운세나 자가진단처럼 재미로 만든 기능은 굳이 색인될 이유가 없습니다. 정보성 페이지와 놀이용 페이지를 갈라, 후자에는 색인하지 말라는 표시를 붙였습니다.

프리렌더 이야기가 여기서 다시 걸립니다. 색인 제외 표시를 자바스크립트로만 붙이면 정작 HTML만 읽는 크롤러에게는 전달되지 않습니다. 정적 HTML 안에 미리 박혀 있어야 의미가 있습니다. 한 가지 더 배운 건, 이미 색인된 페이지를 빼고 싶을 때 robots.txt로 접근 자체를 막으면 안 된다는 점입니다. 크롤러가 페이지에 들어와 "색인하지 말라"는 표시를 읽어야 빠지는데, 문을 잠가버리면 그 표시를 영원히 못 봅니다.

배운 점

프레임워크가 좋아서 화면이 잘 나온다고 검색까지 잘 되는 건 아니었습니다. "자바스크립트 없이 이 페이지를 열면 무엇이 보이는가"를 한 번만 확인했어도 훨씬 빨리 알았을 문제였습니다. 지금은 새 페이지를 만들 때마다 정적 생성 목록에 등록했는지부터 확인합니다.

그리고 확인은 눈이 아니라 응답 코드로 합니다. 배포한 뒤에는 사이트맵에 실린 주소를 전부 훑어 200인지 보고, 일부러 만든 엉터리 주소 몇 개가 404를 돌려주는지도 같이 봅니다. 브라우저로 열어 화면이 뜨는 것과 서버가 무슨 코드를 주는지는 전혀 다른 이야기였습니다.

← 개발 기록 목록