블로그 개츠비 마이그레이션

2023-10-03


블로그 또 이사?

벌써 3번째 이사중이다. 처음에는 tistory를 이용했는데 커스터마이징이 부족하다고 느껴서 jekyll로 갔다가 ruby on rails에 익숙하지 않아 사실상 블랙박스로 사용하다 보니 막상 커스터마이징은 하지도 않았다. 이번엔 개츠비로 이사했는데 프론트는 잘 모르지만 그나마 리액트 네이티브를 해본 적이 있어 리액트와 typescript에 어느정도 발은 담가놓은 상태였다.

부족한 themes

개츠비는 jekyll, Hugo등 다른 정적 웹사이트 생성 프레임워크에 비해 템플릿이 부족한 느낌이었다. https://www.gatsbyjs.com/starters 여기에 좋은 템플릿이 많지만 마음에 드는 건 없어서 Hugo 템플릿 중 하나https://github.com/leafee98/hugo-theme-flat 를 가져와 개츠비에 맞게 수정했다.

먼저, 이 자리를 빌어 블로그 이사에 많은 도움을 준 @kasterra 학부 선배님한테 감사의 말을 전한다.

프로젝트 구조는 단순했다. 기존 프로젝트는 html/css로 되어있었고 각 html을 리액트에 맞게 tsx로 바꾸는 작업은 chatGPT가 열심히 해 줬다. 개츠비로 빈 프로젝트를 생성 후 layout/index.html을 메인 페이지인 post-list.tsx로 마이그레이션 후 나머지 파일도 적당히 html로 쓰인 파일을 tsx로 바꿔주는 작업이 주였다. 그렇게 뼈대를 잡은 후 static/css파일을 src/css로 옮겨서 각 페이지별로 적용시켜주면 되는 일이었다. 라고 생각했지만 생각보다 순조롭지는 않았던 게 문제였다.

문제!

chatGPT가 학습한 자료가 최신이 아니었다.

생각보다 간단한 이슈였는데 graphQL에서 deprecated된 방법으로 데이터를 정렬하고 있어 직접 쿼리를 수정해줬다.

메인 페이지에서 포스트가 최신 순으로 보이지 않았다.

post-list.tsx에서 graphql로 포스트 데이터를 가져온 후 map으로 순회하면서 보여주는 방식인데 데이터를 그냥 가져오니까 순서가 뒤죽박죽이었다.

예전 블로그에서 md파일을 작성할 때 yyyy-mm-dd-제-목.md 형식으로 작성했는데 처음에는 date부분을 가져와서 순서대로 정렬하려고 생각했지만 하루에 여러 포스트를 작성할 때 꼬일 수 있을 위험이 있어서 파일 제목에 date를 빼버리고 포스트를 작성한 순서대로 번호를 붙였다. 그렇게한 후 gatsby-config.ts에서 onCreateNode의 field 추가하는 부분에 포스트 번호를 같이 주고 그것을 데이터를 가져올 때 sort 기준으로 주는 방식으로 해결했다.

gatsby-node.ts(일부)
getData.graphql

export const onCreateNode: GatsbyNode['onCreateNode'] = ({ node, getNode, actions }) => {
const { createNodeField } = actions
if (node.internal.type === `Mdx`) {
const fileName: string = createFilePath({ node, getNode, basePath: `pages` })
const formattedSlug: string = fileName.replace(/\/(\d{1})-/, '/')
const nodeNum: number = parseInt(fileName.substring(1).split("-")[0])
if (Number.isNaN(nodeNum)) {
console.log("nodeNum should not be NaN")
reporter.panicOnBuild("nodeNum should not be NaN")
}
createNodeField({
node,
name: `slug`,
value: formattedSlug,
});
createNodeField({
node,
name: `nodeNum`,
value: nodeNum
})
}
};

SSR vs CSR

개츠비는 동적 사이트(React.js)를 정적 사이트(html, css, js)로 바꿔주는 프레임워크다. 여기서 일단 SSR과 CSR의 차이를 모르고 그냥 사용하면 당할 수 밖에 없는 부분이 존재했다.

  • SSR(Server-Side Rendering)의 경우 서버에서 모든 렌더링을 처리해서 클라이언트에 전달한다. 이 과정에서 클라이언트는 동적으로 페이지를 생성하지 않는다.(정적 사이트)
  • 반면 CSR(Client-Side Rendering)의 경우 서버에서 클라이언트한테 이런 코드로 만들어지는 사이트를 줘 라고 js를 전달하고 클라이언트에서 실행시킨 결과로 보여준다.(동적 사이트)

이 부분은 동적 사이트를 웹페이지 크롤링을 해 봤다면 한 번씩 겪는 이슈일 것이다. 서버의 HTML을 그냥 긁어 왔더니(BeautifulSoup 같은걸로) 브라우저로 보는거랑 다르게 내용은 없고 이상한 js코드만 있더라 하는 경우가 있을 수 있다. 이 문제를 해결하려면 WebDriver를 사용해 직접 브라우저가 실행시키는 것 처럼 클라이언트가 필요하단 게 결론이었다.

다시 돌아와서 개츠비에서 문제되는 이유는 develop 환경은 동적 사이트지만 빌드 후에는 정적 사이트가 된다. 따라서 이 차이로 인해 window, date등 클라이언트에서 동작하는 객체를 사용하면 작동하지 않는다.

다시 말해, 사용할 수 있는건 오직 node.js객체 뿐이다. 이것이 일반적인 React 개발과의 가장 큰 차이점이다. 내 경우는 date객체가 문제였는데 node.js Date객체로 바꾼 후 문제가 해결됐다.

빌드 후 뒤늦게 문제를 깨달은 뒤 구글링을 해 보니까 develop 환경에서도 SSR과 CSR이 다른 경우 에러를 띄워주게 하는 옵션이 존재했다. 간단하게 gatsby-config.ts에 아래 코드를 추가하자.


flags: {
DEV_SSR: true,
}

Hydration failed

개츠비를 사용하면서 절대 body에 CSS flex 속성을 주지 말자!!!

단순히 date가 깨지는 문제만 있었으면 얼마나 좋았겠는가.. 현실은 그렇지 않았다.

Hydration failed 에러가 나는 이유는 간단하게 SSR과 CSR이 달라서 생기는 오류다. 내 경우는 메인 페이지에서 포스트가 가운데 정렬이 되지 않는 문제가 있었다.

개츠비 플러그인 자체에서 빌드할 때 body 밑에 <div id="___gatsby">와 <div id="gatsby-focus-wrapper">태그를 넣어준다. 원래 이게 문제될 일은 없어야 한다... 없어야 하는데 body 태그에 CSS 속성을 줬을 경우 개츠비가 삽입한 저 태그 때문에 엉뚱한 태그가 자식이 되버리고 정작 내가 적용하고 싶은 자식 태그는 더 밑에 있기에 CSS가 원하는대로 적용되지 않는다.

자세히는 모르지만 CSR에서는 body밑에 내가 적용하고 싶은 태그에 CSS가 적용된 후 div 태그가 삽입된 것 아닌가 하는 추측을 했다. 따라서 body에 있는 CSS 속성이 적용돼야 하는 곳을 div로 감싸고 거기에 적용시켰더니 해결됐다.

결론과 TODO

이 글을 쓰는 시점에서는 사이트 링크를 누르다보면 404가 나온다던가 하는 미완성인 부분이 아직 많이 존재한다. 이번에 개츠비로 옮기면서 SSR이랑 CSR에 대해서는 확실히 짚고 넘어갈 수 있었다... 앞으로 해야할 것 중 당장 생각나는건

  1. 댓글 기능 추가
  2. 404 나오는 페이지 내용 채우기(About, Tags, Categories 등)
  3. 하단 혹은 상단 바에 깃헙이나 sns링크 추가
  4. 현재는 사용자 환경에 따라 라이트/다크 모드가 바뀌지만 상단 버튼으로 라이트/다크 모드를 전환
  5. favicon 추가, 탭 이름이 현재는 blog.skeep.link로 고정되어 있지만 이 부분을 포스트 이름으로 변경
  6. 포스트 자주 쓰기

아직 갈길이 멀다고만 느꼈다!