쪼렙 as! 풀스택

Angular4, SEO 와 페이스북 공유 크롤링 봇을 위한 몸부림 본문

개발 일지/Web & Server

Angular4, SEO 와 페이스북 공유 크롤링 봇을 위한 몸부림

코코앱 2017. 6. 16. 12:09

  Angular는 SPA 방식으로, 완벽하게 클라이언트 사이드 렌더링으로 돌아가게 되어있다. 이때문에 맨처음 index.html 파일과 관련된 js 파일 들을 단순히 다운로드만 받아도, 서버와 통신할 필요없이 사이트가 돌아갈 수 있게 된다. (물론 실제론 데이터 CRUD 를 위한 API 통신을 하겠지만, 이론적으론 이것과 별개의 이야기다)


  그래서 맨처음엔 '우와, 서버리스로 구성해봐야징~' 하면서 아마존 S3 에 올려놓고, static 웹호스팅으로 연결해버렸다. S3 요금은 일반 서버를 사용하는것보다 훨씬 저렴하고, 따로 서버 컴퓨팅 리소스를 사용하지도 않기 때문에, '완전 좋네~' 하면서 만족해 했었다. 그런데 사이트를 완성해 놓고, SEO 와 페이스북 공유를 해보다 보니, 큰 문제가 있었다. ㅠㅠ

 

  HTML 문서의 <head><meta> 정보는 SEO 와 크롤링봇에 정말 중요한 요소이다. 문제는 Angular 같은 SPA 형식의 프레임워크는 이 <head> 의 내용을 한번 다운로드 받고나면, 이 <head> 값은 고정된 채로, 클라이언트 단의 '셀렉터' 혹은 '디렉티브' 안에서만 계속 돌고돌기 때문에, 사용자 입장에서는 페이지가 변하는 것 같지만, 실제론 그렇지 않다는 것이다!! 그래서 헤드의 메타정보를 이용하는 크롤러들은, 그 내용들을 알 수도 볼 수도 없다. ㅠㅠ


  SEO 최적화도 문제지만, 당장 가장 큰 문제는 페이스북 공유나 카카오톡 공유를 하면 '미리보기' 화면에 내가 원하는 페이지의 내용이 보이지 않고 index.html 에 정의되어있는 홈페이지 전체에 대한 메타정보밖에 인식을 못한다. 


페이지 내의 어떤 주소를 공유하던간에 index.html 의 메타 정보가 동일하게 표시된다.



  이 사이트 내에 있는 각 콘텐츠마다 가지고 있는 고유의 제목이나 글, 커버이미지 등이 표시되었으면 좋겠는데, 위와같이 무조건 동일한 미리보기가 생성되는것이다.


  전통적인 방식으로 각 페이지마다 동적으로 페이지를 생성해서 (서버사이드 렌더링) 내보내주면, 저런 문제가 없는데... 어쩌냐... 이미 Angular 를 이용해서 사이트를 다 만들었을 뿐이고! Angular 의 개발 생산성의 편리함을 버리고 싶지도 않았다.


  그래서 몇가지 방법을 찾아 보았다.


  AngularJS의 SEO (검색최적화) 문제와 대안 - https://medium.com/@yoonjs2/angularjs%EC%9D%98-seo-%EA%B2%80%EC%83%89%EC%B5%9C%EC%A0%81%ED%99%94-%EB%AC%B8%EC%A0%9C%EC%99%80-%EB%8C%80%EC%95%88-7feafc2924ba



후보 -  Prerender 서비스. - 돈내야 하고, 뭔가 복잡해 보인다. 내 손 안에서 제어가 되지 않는건 왠만하면 안하고 싶다.


실패 - Angular 4 의 Meta 서비스.


- Angular4 에서 Meta 값을 변경해주는 서비스가 포함되어 나왔다. Angular2 에서도 누군가 오픈소스로 만들어서 사용하던 기능인것 같은데, Angular4 에서 정식으로 포함된 것이다! 매우 기대가 많았다. 

- 그러나 실패 ㅡ,.ㅡ;

- 왜냐면 이것도 결국엔 자바스크립트까지 다 다운로드가 되고서, 클라이언트단에서 자바스크립트가 돌아간 후에야 메타 값이 변경되기 때문이다. 구글 검색의 크롤링봇은 이 자바스크립트가 실행되길 기다린다고 한다.


  따라서 구글 검색에는 잘 될지 모르지만, 페이스북 공유나 카카오톡 공유 등과같은 크롤링봇엔 전혀 소용이 없다. 이것들은 자바스크립트를 실행시키지 않는 채, 서버에서 즉시 응답하는 메타데이터를 이용하기 때문이다 ㅠㅠ




결국,

서버리스를 포기했다. 서버를 운영하고 라우팅을 직접 제어해 주었다.


1. Angular 에 라우팅 규칙을 설정하게 되어있다. 이것을 운영하는 서버에도 동일하게 라우팅을 설정해 준다. 그러면 해시URL(#URL) 을 사용하지 않더라도, 서버의 라우팅을 통해서 Angular 의 라우팅도 동일하게 동작할 수 있게 된다.


2. 서버에 페이지를 요청하면, 설정한 라우팅에 따라, <head>의 메타데이터를 동적으로 생성해서 응답해준다. 이때 <body> 부분에는 그냥 Angular 를 실행하는 소스를 동일하게 넣어준다. 서버에서 하는 일은 단순히 라우팅 & 라우팅에 따른 <head>의 메타데이터를 동적으로 생성해서 응답해주는것 밖에 없는것이다.


 기존 서버리스 Angular ( Hash URL 사용)

서버 운영 & Angular와 라우팅 동일하게 맞춰줌. 

 1. 페이지 요청 (ex) https://findbm.com/#/bm/Voysis

1. 페이지 요청 (ex) https://findbm.com/bm/Voysis 

 2. 요청 URL과 무관하게 고정된 HTML 문서 응답 (index.html)

2. 라우팅 규칙에 따라 동적으로 생성된 HTML 문서 응답

3. 응답받은 HTML 문서의 <head>에는 요청 URL과는 무관하게 고정된 Meta 데이터를 포함하고 있음. 

3. 응답받은 HTML 문서의 <head>에는, 이미 서버에서 라우팅에 따라 동적으로 생성된 각 페이지마다의 고유한 Meta 데이터를 포함하고 있음.

4. <body>부분에는 Angular가 동작하는 디렉티브와 JavaScript 파일다운 -> Angular 실행.

4. <body>부분에는 Angular가 동작하는 디렉티브와 JavaScript 파일다운 -> Angular 실행.



이렇게 해서 각 페이지마다 고유한 메타데이터를 응답할 수 있게 하였고, 막상 프론트단은 100% Angular 를 사용하였다. 그래서 페이스북 공유나 카카오톡 공유의 미리보기에서도 내가 원하는 모양으로 나오게 되었다.



덧. 예상되는 이슈.

- 사실 이건 어느정도 눈속임이라 봐야겠다. 위와 같은 과정 (일단 서버의 라우팅을 통한 페이지 접근)을 거쳐서, 올바른 메타데이터가 표현되었다 하더라도, 한번 Angular 가 도작하기 시작하면, 그때부턴 다시 서버의 라우팅을 거치지 않고, Angular 라우팅만 동작하게된다. 그래서 한번 로드된 페이지의 <head>의 <meta> 값이 변경되지는 않는것이다. 


- 그러나 뭐 어쨌든 상관 안하기로 했다. 어차피 사용자는 사이트를 둘러보면서 페이지의 메타값이 변경되는지 확인할 필요가 없고, 크롤링 봇은, 어차피 페이지 주소마다 요청해서 메타데이터를 불러들이는데 문제없이 될것이기 때문이다.


- 확실히 Angular 같이 SPA 방식의 개발이 편리한 부분이 많아, 기능이 중요한 '웹앱'을 개발하는데는 매우 적합할지 모르나, 미디어같이 '정보'가 중요한 사이트에는 어울리지 않는다는 생각을 한다.



Comments