logo

Blog

신년 맞이 블로그 리뉴얼

#nextjs#SSG#blog
ryxxn profileryxxn
2025.01.03
thumbnail

우선 내 블로그는 Next로 개발했고, 전부 SSG이다.

그래서 DB도 없다. 그저 로컬 파일의 md 파일을 변환해서 SSG로 만들고 Vercel로 배포했다.


1. 다크모드 문제

큰 인식을 하고 있지는 않았지만, default theme mode가 light 모드여서

다크모드로 바꾸고 새로고침을 하면 클라이언트 사이드에서 localStorage에서 가져오는 딜레이 때문에

잠깐 light 모드였다가 다크모드로 바뀌는 것을 발견했다.

dark mode bug capture

화면이 깜빡이는 효과를 없애기 위해

서버사이드에서 사용자의 theme mode에 맞춰서 html을 내려주는 방법을 생각해보았다.

  • request시 쿠키에 theme mode를 담아서 보낸 후 middleware에서 처리할까?
  • dynamic rendering을 이용해서 html head 부분만 바꿔서 보낼까?

등등의 생각을 했지만, SSG로 만든 이상 구현이 힘들었다.

그래서

html 로드 전 theme mode 동기화 스크립트를 실행하도록 했다.

head 태그에 아래 스크립트를 심었다.

javascript
(function () {  const theme = localStorage.getItem('ryxxn-tech-blog-theme') || 'light';  document.documentElement.setAttribute('data-mode', theme);  const classList = document.documentElement.classList;  if (theme === 'dark') {    classList.add('dark');  }  classList.remove(theme === 'dark' ? 'light' : 'dark');})();

이제 새로고침을 했을 때 현재 theme이 바로 반영이 된다.


2. 카드 형식 UI 추가

기존에는 일반적인 블로그 목록 UI였지만, 카드 형태의 UI도 추가했다.

card ui capture

우선 다크모드의 경우, 카드 하단 부분 프레임을 썸네일의 대표 색상을 gradient로 주었다. 빌드시 sharp 라이브러리를 이용해서 이미지를 webp 확장자로 변환하는 과정을 거치고 있었다. 그래서 sharp가 설치된 김에 이미지 메인 컬러 추출 함수를 만들었다.

tsx
import sharp from 'sharp';import path from 'path';
export const getImageDominantColor = async (  relativePath: string): Promise<string> => {  try {    const absolutePath = path.join(process.cwd(), 'public', relativePath);
    // Sharp를 사용해 Dominant Color 추출    const { dominant } = await sharp(absolutePath).stats();    const { r, g, b } = dominant;
    return `rgb(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)})`;  } catch (error) {    console.error('Error extracting dominant color from WebP:', error);    throw error;  }};

이미지에서 dominant color 추출 후

tsx
// ...  <div    style={{ '--gradient-color': thumbnailDominantColor } as React.CSSProperties}    className={cx(styles.articleContent, 'p-4 h-full relative bg-white')}  >// ...

style 변수로 넣어주고

css
[data-mode='dark'] .articleContent {  background: linear-gradient(    to bottom,    var(--gradient-color),    rgb(53, 65, 91)  );}

적용시켰다.


그렇지만 또 문제가 생겼다.

3. 목록 layout 동기화 문제

카드 UI와 리스트 UI를 선택했을 때, 새로고침시 어떻게 동기화시키지? 라는 문제가 생겼다.

localStorage에 현재 layout 상태를 담는다고 했을 때,

Client 사이드 컴포넌트로 만들면 새로고침시 분명 바로 적용이 되지 않을 것이다.

그렇지만. 방법을 생각해냈다.

기본에 충실하여 html attribute랑 css만을 이용하여 구현해보기로 했다. 그렇게 하면 다크모드 문제와 같이 해결이 가능할 것 같았다.

html
<html lang="ko" data-theme="dark" class="dark" data-article-layout="grid"><!-- ... --></html>

localStorage에 grid인지 list인지 담고, html에 적용한다.

tsx
{/* CSS 수준에서 하나는 display none, 다른 하나는 보여지게 함. */}<section  className={cx(styles.gridLayout, 'grid 대충 어쩌고 class name들')}>  {articles.map((article) => (    <CardArticle key={article.slug} article={article} />  ))}</section>
<ul  style={{ viewTransitionName: 'article-layout' }}  className={cx(styles.listLayout, 'max-w-[700px] mx-auto')}>  {articles.map((article) => (    <Article key={article.slug} article={article} />  ))}</ul>

그리고 css에서

css
[data-article-layout='grid'] .listLayout {  display: none;}[data-article-layout='list'] .gridLayout {  display: none;}

하면 잘 작동한다.

다크모드 때처럼 head 태그에 스크립트를 추가하면 새로고침해도 즉시 반영된다.


그치만 이렇게 하면 문제가 하나 있었다.
SEO 측면에서 동일한 컨텐츠가 2개씩 뜬다는 것이었다.
그래서
일단 list 형식의 UI가 excerpt가 있기 때문에 정보가 더 많아서 default UI로 하고,
그래서 card UI에

tsx
// cart-article.tsx// ...<Link  data-article-type="card"  href={`/article/${slug}`}  // card 타입만 구글 크롤러가 무시하게 하고 싶은데..  aria-hidden="true"// ...

display:none;aria-hidden="true"를 주면 크롤링 봇이 무시할까 했지만, 구글 크롤러가 aria attribute를 신뢰한다는 증거를 찾을 수 없었다..
그래서 그냥 aria-hidden은 사용하지 않고 냅뒀다.
(사용하면 스크린 리더 측면에서도 고려해야 할 부분이 더 있었기에..)


4. hover시 layout이 깨지는 현상

card에 hover시 translateY(-8px) 이런식으로 줬는데,
transition시 미세하게 레이아웃이 깨진다.

layout-breaking-hover-card

처음에는 will-change: transform을 사용하여 1차 해결.
그러나 원인을 모름.

며칠간 문제 원인 분석을 하고 나름의 결론을 냈는데,
아무리 검색을 해봐도 이 문제에 대한 명확한 근거를 찾을 수 없었다.
그래서 명확한 근거를 알고자
크로미움이슈 제기를 했다. 구글 크롬 렌더링 버그였다
이 문제에 대한 자세한 내용은 따로 글을 남길 예정이다.

구글 버그를 찾고 이슈를 제기했다 (feat. GPU 가속 렌더링)hover 애니메이션에서 레이아웃이 깨지는 현상의 원인을 일주일간 찾았는데 버그였던 이야기
구글 버그를 찾고 이슈를 제기했다 (feat. GPU 가속 렌더링)
tech.ryxxn.com

5. 이미지 블러 효과 적용

좀 더 자연스러운 이미지 로드를 위해 Next Image에서 지원하는 blur 기능을 적용했다.

그냥 blur 기능만 넣으면 되긴 했지만, 영 부드럽지가 않아 또 나름의 고민을 하여 애니메이션을 추가했다.

이것도 따로 글을 남겼다.

Next Image blur 전환 애니메이션 넣기Next Image 컴포넌트의 placeholder 기능에 애니메이션을 추가했어요.
Next Image blur 전환 애니메이션 넣기
tech.ryxxn.com

6. view transition api 적용

이것도 정말 힘들었다.

그저 화면 전환을 부드럽게 하고자 도입한 기능이지만, 또한 너무 힘들었다.

결과물만 보자면 일단 이렇다.

view-transition-result

이것도 따로 글을 남겼다.

블로그 화면 전환 부드럽게 하기 (feat. view transition api)Nextjs로 만든 블로그에 view transition api 적용기. 생각보다 많은 고민이 필요했던 작업이에요.
블로그 화면 전환 부드럽게 하기 (feat. view transition api)
tech.ryxxn.com

리뉴얼에 이렇게까지 힘을 쏟을 줄은 상상도 못 했다. 며칠 밤을 샌 건지 모를 정도로 참 힘들었다 . .

Related Articles