티스토리 뷰

반응형

앞서 SEO 작업을 위해 prerender-spa-plugin을 적용한 포스팅 1탄을 작성하였다. 아래 링크 참고

https://soobakba.tistory.com/35

 

[Vue Cli] Vue 앱에 prerender spa plugin 적용하기

회사에서 클라이언트사의 스페셜 페이지를 우리 플랫폼으로 내재화 하는 작업을 맡아 진행하게 되었다. 스페셜 페이지의 가장 중요한 부분 중 하나는 SNS로 링크 공유시 적절한 이미지와 내용이

soobakba.tistory.com

이번에는 실제 prerender를 적용한 파일들을 배포한 뒤 발생했던 이슈들과 그 해결과정을 정리해보고자 한다.

 

동적 라우트 적용

우리 서비스는 리얼 서버에 배포하기 전까지 dev, stage 두 단계의 phase를 두고 큐에이를 진행한다. 그래서 각 단계에 따라 DB 데이터도 다르기 때문에 특정 아레나 또는 스페셜 페이지에 접속하는 경로(url)도 id값에 따라 달라진다.

앞서 1탄 포스팅에 작성한 코드대로 하면 고정 라우터밖에 렌더할 수가 없어서 이를 위해 각 페이즈에 해당되는 파일을 만들고 그 파일에 각각 렌더할 아레나 id 혹은 스페셜 페이지 url 목록을 작성하여 웹팩 빌드 모드에 따라 각 파일을 바라보도록 설정하였다.

 

사실 완전한 동적 라우트라고 할 수는 없다. 파일에 목록을 작성하는것이 아니라 api를 호출해서 데이터를 받아오는 것이 가장 이상적인 방법이긴 하나, 아직 내가 구현을 못했다 ㅠ_ㅠ 조만간 작업해봐야겠다!  작업완료!

 

그리고 vue.config.js 파일에 계속 추가로 작성하다 보니 너무 길어져서 파일을 분리하기 위해 방법을 찾아보다가 Vue CLI에서 제공하는 registerCommand와 chainWebpack을 이용해서 빌드 실행 명령어도 분리하고 파일도 분리하였다. 

package.json 파일에 아래와 같이 추가해준 뒤, build.prerender.js 파일을 만들어서 prerender 옵션을 작성해 주면 된다.

"vuePlugins": {
  "service": [
    "build.prerender.js"
  ]
}
// build.prerender.js 파일

const path = require('path');

module.exports = (api, options) => {
  api.registerCommand('build:prerender', async args => {
    const PrerenderSPAPlugin = require('prerender-spa-plugin');

    // call api or file
    let data = [];
    if (process.env.NODE_ENV === 'production') {
      data = require('./routeList.prod.json').data;
    } else if (process.env.NODE_ENV === 'stage') {
      data = require('./routeList.stage.json').data;
    } else {
      data = require('./routeList.dev.json').data;
    }

    const makeUrl = {
      arena: data => `/arena/${data.game}/${data.id}/info`,
      special: data => `/special/${data.slug}`,
    };

    api.chainWebpack(config => {
      config.plugin('prerender').use(PrerenderSPAPlugin, [
        {
          staticDir: path.join(__dirname, 'dist'),
          server: {
            port: 8090,
          },
          routes: ['/'].concat(data.map(route => makeUrl[route.type](route))),
          renderer: new PrerenderSPAPlugin.PuppeteerRenderer({
            renderAfterElementExists: '#app',
            headless: true,
          }),
        },
      ]);
    });

    await api.service.run('build', args);
  });
};

 

이렇게 작성해주고 실제 라우터 목록이 들어갈 routeList.dev.json / routeList.stage.json / routeList.prod.json 파일들도 작성해준다. 해당 파일에는 동적으로 들어가는 값만 적어주고 실제 url은 build.prerender.js 파일에 makeUrl을 통해 생성된다.

또한 빌드할 때는 vue-cli-service build:prerender --mode development 이런식으로 해주면 된다.

 

적다보니 Vue CLI 관련 작업에 대한 설명이 길어졌는데 실제로는 이 작업 이후 메타정보가 제대로 렌더링 됐는지, 봇이 잘 읽어가는지 테스트 해보는 과정도 만만치 않았다.

 

메타태그, Open Graph 적용 확인

SNS 공유시 또는 검색엔진 봇이 메타태그를 잘 읽어가는지 확인하기 위해 아래 두 가지 사이트를 활용하였다.

https://metatags.io/

 

Meta Tags — Preview, Edit and Generate

With Meta Tags you can edit and experiment with your content then preview how your webpage will look on Google, Facebook, Twitter and more!

metatags.io

https://developers.facebook.com/tools/debug/

 

공유 디버거 - Facebook for Developers

공유 디버거를 사용하면 Facebook에 공유될 때 콘텐츠가 표시되는 모습을 미리 보거나 오픈 그래프 태그를 사용하여 문제를 디버깅할 수 있습니다. 이 도구를 사용하려면 Facebook에 로그인하세요. �

developers.facebook.com

위에 있는 Meta Tags 사이트는 굉장히 관대하게 메타 정보를 잘 불러오는 듯 했다. 동적으로 적용한 이미지나 타이틀, description을 모두 잘 불러왔다.

그러나 문제는 아래에 있는 페이스북 공유 디버거 사이트! Meta Tags 사이트에서 잘 나오던 이미지도 페이스북 공유 디버거는 제대로 불러오지 못했고 캐시를 지우고 재검색할 때 마다 적용되는 메타태그의 범위가 그때 그때 달라졌다. 그래도 깐깐하게 검사해주고 문제점이 있으면 어떤게 잘못됐는지 알려주기 때문에 확실한 걸 원한다면 페이스북 공유 디버거로 테스트 해보는게 바람직한 것 같다.

 

우리의 문제는 og:url 속성에 유효하지 않은 url을 넣었거나 권장 이미지 사이즈보다 작은 이미지를 사용해서 발생했던 문제였다. 이것들을 해결하고 보니 등록한 메타태그가 모두 잘 잡혔다. 처음엔 동적으로 script로 추가된 태그라 <head> 태그의 거의 맨 아래쪽에 메타태그가 있어서 그것때문인가 했었는데 (사실 아직도 정확한 원인은 모르겠지만) 아마 검사중 유효하지 않은 값이 있으면 탐색을 멈춰서 그런것일까? 하고 지레짐작해버렸다...

 

Trailing Slash 적용

prerender-spa-plugin은 기본적으로 라우터 경로에 해당되는 폴더를 만들고 그 안에 index.html 파일을 만든다. 따라서 기존 vue router만 적용했을 때에는 https://lvup.gg/arena/all 이러한 경로로 접속한 페이지라면 prerender 이후에는 https://lvup.gg/arena/all/ 이렇게 Trailing Slash가 붙은 경로가 된다.

그러나 정확한 full url을 입력하지 않으면 SPA의 특성상 처음 로드한 index파일에서 스크립트로 라우터를 변경하는 것이기 때문에 일반적으로 접속한다면 기존 경로인 (vue-router에 적어준대로) Trailing Slash가 없는 경로에 접속할 것이다.

 

문제는 여기서 url에 쿼리가 추가되고 그 url을 공유했을 때 발생했다. 해당 페이지 내에 검색기능이 있어서 검색한 키워드를 라우터 쿼리로 붙여주고 있었는데 해당 url을 공유하고 그걸 클릭해서 접속하게 되면 https://lvup.gg/arena/all/ 로 자동으로 리다이렉트 되는것이었다. 검색 쿼리가 모두 사라져서 의미있는 데이터가 보여지지 않았다.

정확히는 모르겠지만 추측으로는 AWS CloudFront가 기본적으로 접속한 경로에 대응되는 파일이 있는지 먼저 탐색하고 있다면 보여주고 없다면 우리가 설정한대로 root 페이지로 이동하는거 같다. 그래서 리다이렉트되지 않도록 하기 위해 vue-router로 이동하는 path에도 Trailing Slash를 붙여주기로 했다.

{
  path: '/arena/:gameId/', //trailing slash 추가
  name: 'Arena',
  component: loadView('Arena'),
  pathToRegexpOptions: { strict: true }, //항상 trailing slash 유지하기 위해 strict option 추가
},

vue-router는 trailing slash가 있건 없건 같은 path로 취급하는지 pathToRegexpOptions: { strict: true } 이 옵션을 넣어주지 않으면 계속 trailing slash 없는 경로로 이동했다. 그래서 해당 옵션을 추가하고 검색 쿼리를 적용하니 잘 작동했다.

 

결과 및 보완할 점

험난했던 SEO 작업 덕분에 스페셜 페이지 정보를 구글과 네이버가 잘 가져가주었나보다!

본부장님이 슬랙에 올려주신 감동적인 자료 ㅠ_ㅠ

구글 네이버 고마워

처음 생각했던 것 보다 범위가 큰 작업이 되었지만 그만큼 고민하는 시간도 많아졌고 그만큼 공부가 됐던거 같아 뿌듯했다. 카카오톡에 공유할 때 링크가 예쁘게 나오는것만 봐도 기분좋았는데 저렇게 통계상 수치까지 좋게 나오니 뿌듯하기 그지없었다.

 

그러나 아직 갈길이 멀다. 일단 라우터를 하드코딩하지 않고 동적으로 받아오도록 api 호출하는 작업은 필연적인 작업이라 빠른 시일내에 적용해야한다. (적용완료!) 그리고 그 외에 prerender의 한계라고도 볼 수 있는 점은 seo 적용할 페이지가 추가될 때 마다 혹은 seo 정보가 변경될 때 마다 재배포 해야만 변경사항이 적용된다.

우리팀 배포 주기가 1주에서 2주로 늘어난 만큼, 핫픽스가 없는 한 2주동안은 seo 정보를 변경할 여지가 없다는것 ㅠ_ㅠ 

실제로 공유 이미지를 오거나이저가 변경하는 기능이 있는데 사이트에서 변경을 해도 재배포 되지 않는 한 변경된 이미지를 적용할 수가 없어서 해당 부분도 이미지 url을 일관되게 업로드 하거나 하는 등의 조치가 필요하다고 이야기 나누었다.

 

아직 끝나지 않은 SEO 적용기... to be continue...

반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함