Github
개발

Next.js로 이사 완료! 집주인은 Obsidian입니다만?

2025년 4월 17일5 min read
Next.js로 이사 완료! 집주인은 Obsidian입니다만?

새롭게 변경된 블로그

GatsbyJS 프레임워크로 작성되어 있던 블로그를 Next.js로 마이그레이션을 진행하게 되었습니다.
블로그의 디자인과 로고도 바꾸면서 이전과는 완전히 다른 모습으로 탈바꿈을 했어요!

왜 Next.js로 결정했을까?

GatsbyJS 단점

GraphQL
최고의 장점이자 단점이 되는 GraphQL이었습니다.
많은 최적화 부분이 있기도 하고 하나의 쿼리 안에 다양한 정보들을 가져올 수 있는 장점이 뚜렷하지만, 블로그에 필요한 쿼리를 작성하는데 초기에는 익숙치 않다 보니 약 10분 정도 소요되었고, 단순히 mdx에서 frontmatter만 추출하면 되는 작업이기 때문에 제 블로그에서는 GraphQL의 장점을 이용하기에는 적합하지 않다고 생각했습니다.

동적 라우팅
GatsbyJS에서도 많은 라우팅 방법이 있습니다. 동적 라우팅을 위해서는 gatsby-node.js를 작성하거나 파일 시스템 기반으로 작성하며 {} 중괄호에 GraphQL 필드명을 통해서 생성이 가능합니다.
gatsby-node.js는 루트 위치에 있어야 하기 때문에 응집성이 떨어져 아쉬웠으며, {} 중괄호를 이용한 동적 라우팅도 GraphQL 필드명으로 파일을 만들어야 해서 불편했습니다.

개발 경험
gatsby-ssr.js gatsby-browser.js gatsby-node.js gatsby-config.js 등의 설정 파일이 많아 서드파티 라이브러리를 적용하는데 어려움이 있었고 관리할 포인트가 많았습니다.

Next.js 선택 이유

라우팅
Next.js는 파일 시스템 기반으로 라우팅이 가능하며 layout.js라던지 파일 컨벤션이 정해져 있고 각 파일이 무엇을 할 지 예측이 되어 좋았습니다. metadata등의 설정도 페이지를 기반으로 작성할 수 있어 가독성이 좋다고 생각했습니다.
또한 다양한 라우팅을 제공하며 파일 시스템 기반으로 작성이 가능하여 응집성있게 적용할 수 있는 점이 매력적이었습니다.

다양한 렌더링 방식과 최신 기능
Next.js는 React의 최신 기능들을 지원하고 다양한 렌더링 방식과 다양한 기능을 지원하기 때문에 추후에 블로그를 확장하기 좋을 것 같아 선택하게 되었습니다.

빌드 속도
GatsbyJS에서는 평균 3분 정도 소요되었지만 Next.js에서는 평균 1분으로 감소했습니다.

번들 크기의 경우에는 프레임워크 뿐만 아니라 사용하는 기술이 달라졌기 때문에 비교하기 어렵지만
번들 크기는 GatsbyJS에서 약 420kB정도로 측정이 되었고 Next.js로 마이그레이션 하면서 50% 감소한 212kB로 줄었습니다.

Remix도 한 번 경험해 보고 싶기 때문에 간단하게 구현을 진행해보려고해요.
아주 작은 프로젝트이기 때문에 걱정 없이 사용해보고 있어요

콘텐츠 관리 고민

자체적으로 블로그글을 작성하시는 분들은 글을 관리하는데 많은 고민을 하셨을 것 같아요, 저도 많은 고민을 했습니다.
이전에는 컴퓨터로만 작성할 것이니 git으로만 관리해도 충분할 것 같았습니다. 배포할 글만 커밋하면 되니까요. 하지만 모바일 환경에서의 글 작성에 대한 니즈가 생기게 되고 블로그를 마이그레이션 할 때 이 문제를 해결해야 하는 목표가 생겼어요.

포스트 작성 고민을 해결한 보석같은 놈

요즘은 노션을 통해서 글을 많이 작성하기도 하고, 노션이 API를 지원하니까 이것으로도 CMS로 가능하겠다 싶었지만, 추가적인 연결을 해야하니 조금 리소스가 추가되는 것이 마음에 들지 않았어요.
현재 git으로 관리하듯이 모바일에서도 할 수 있으면 좋겠다 싶었어요.

기존처럼 git으로 관리를 하되 모바일에서도 접근이 가능한 솔루션을 찾다가 옵시디언(Obsidian)을 발견하게 되었어요. 옵시디언은 강력한 마크다운 편집기로, 단순히 메모뿐만 아니라 다양한 플러그인 생태계를 가지고 있습니다.
특히 눈에 띈 것은 옵시디언에서 git과 연동이 가능하다는 점이었어요. 플러그인을 활용하면 모바일에서도 git 저장소에 접근하고 변경사항을 커밋하고 푸시할 수 있다는 점이 매력적이었죠.
그리고 글은 mdx로 작성하고 있었는데 이것도 플러그인을 통해 파일을 생성하고 수정할 수 있어 매우 편리해 보였어요.

하지만 이것만으로는 완벽한 해결책이 되지 못했어요. git으로 관리를 하기 때문에 저장소에 push를 하지 않는 이상 기기가 다르다면 알 수가 없었죠. 모바일과 컴퓨터가 동일한 파일을 바라보고 수정할 수 있어야 했는데, 이 부분이 고민이었습니다.

이 문제는 애플의 생태계를 통해서 해결할 수 있는 방법이 있었어요. 옵시디언은 VaultiCloud에 생성할 수 있었고, 애플기기는 iCloud에 모두 접근할 수 있기 때문에 이를 통해 모바일과 데스크톱 환경에서 동일한 파일에 접근하고 수정할 수 있게 되었습니다.

유레카!!

구조

추가적인 API 연결 없이도 기존의 git기반 관리 방식을 유지하면서 모바일에서도 블로그 글을 작성하고 수정할 수 있는 완벽한 솔루션을 찾게 되었습니다. 옵시디언의 템플릿 기능까지 활용하니 모바일 환경에서 블로그 글 작성 시 필요한 메타데이터도 편리하게 추가할 수 있게 되었습니다.

저는 애플 생태계로 해결했는데 Obsidian Sync에서도 가능한지는 확인해봐야할 것 같아요 다른 방법으로도 하신 분이 계시다면 공유해주세요

템플릿을 통한 프로퍼티 작성

블로그글을 작성할 때에 제목, 설명, slug 등을 작성해야하는 부분이 있습니다.
옵시디언은 새롭게 생성한 파일에 이러한 정보를 추가할 수 있어요. 하지만 이것은 처음 파일을 만들면 존재하지 않습니다.
일일히 하나하나 추가해서 작성을 해야하지만 옵시디언은 템플릿 기능을 사용할 수 있어 기입해야 할 properties를 선언해둔 템플릿을 삽입해서 사용할 수 있어 매우 편리합니다!

템플릿

사용한 Obsidian Plugin

이미지 서빙

Next.js에서 제공하는 이미지 최적화 기능을 최대한 활용하기로 했어요. Vercel로 배포를 하면 CDN도 자동 적용 되기 때문에 별다른 설정은 필요하지 않았습니다.
이미지 관리를 위해 두 가지 접근 방식을 적용했어요.

썸네일 이미지 관리

썸네일 이미지는 public/thmbnails 폴더에 직접 넣어 관리하고 있어요.
이렇게 하면 메타데이터에서 이미지 경로를 간단히 /thumbnails/{slug}.확장자 형태로 지정할 수 있어서 편리합니다.

context.mdx
---
image: /thumbnails/blog-migration.png
---

포스트 내 이미지 관리

포스트 내에서 사용되는 이미지는 관리 방식이 조금 다릅니다. public 폴더에 직접 파일을 올리면 다음과 같은 문제가 있었습니다.

  1. 이름 중복 가능성
  2. 이미지가 많아질수록 복잡한 관리
  3. 모바일에서 파일을 직접 이동하기 불편함

이 문제를 해결하기 위해 특별한 스크립트를 작성했습니다. 이 스크립트는 다음과 같은 역할을 해요

  1. posts 디렉토리를 재귀적으로 순회
  2. 이미지 파일을 발견하면 public 폴더에 동일한 폴더 구조로 복사
  3. pre-build 단계에서 자동으로 실행되어 빌드 전 이미지 파일 이동
포스트 내 이미지 사용방법
![pinterest 레이아웃](/development/blog-migration/3.png)

public폴더에서 직접 이미지를 넣기 위해서 폴더를 생성하고 이미지를 넣고 관리할 필요없이. 포스트 글을 작성하는 폴더 내부에 이미지를 같이 올려두기만 해도 public폴더로 옮겨주니 관리가 용이해졌습니다.

아쉬운 점과 개선 방향

현재 방식의 한 가지 아쉬운 점은 옵시디언에서 편집할 때 미리보기에서 이미지가 보이지 않는다는 점입니다. 이는 옵시디언과 Next.js의 내부 파일 접근 경로가 다르기 때문에 발생한 문제에요. 옵시디언에게는 /public 경로인 것을 알려주지 않았기 때문에 이미지를 찾을 수 없다는 오류가 발생하죠. 이미지를 옵시디언에서 못 본다는게 아쉬운 점이네요.

썸네일의 경우 직접 /public/thumbnails에 넣고 있는데, 이것도 빌드 스크립트에서 thumbnails 이름을 가진 이미지라면 자동으로 옮겨줄 수 있도록 하는 것이 더 일관된 워크플로우를 제공할 수 있을 것 같습니다. 이 부분은 현재 개선을 고려 중입니다.

스크립트 보러가기

블로그 포스트 레이아웃

이전과는 다르게 썸네일이 존재하지 않은 글도 있어서 기존의 레이아웃을 사용하면 크기가 맞지 않아 아쉬웠어요.
그래서 이번에는 핀터레스트의 masonry 레이아웃을 사용해서 블로그 포스트 목록을 보여주는 레이아웃을 구현했어요.

pinterest 레이아웃

저는 CSS Grid를 사용해서 구현했어요.
카드마다 resizeObserver를 이용해서 높이를 측정하고 이를 바탕으로 레이아웃을 재배치합니다.

가장 루트에 있는 grid에는

export const container = recipe({
  base: {
    display: 'grid',
    gridTemplateColumns: 'repeat(12, 1fr)', // 그리드 컬럼 수
    gridColumnGap: '32px', // 그리드 컬럼 사이 간격
    rowGap: '24px', // 그리드 행 사이 간격
    marginLeft: 'auto',
    marginRight: 'auto',
    gridAutoRows: '10px', // 그리드 행 자체의 높이
    alignItems: 'start',
    width: '100%',
  },
});

이처럼 CSS Grid를 사용하여 컨테이너를 설정하고, column과 row의 크기를 정하여 Grid를 구성합니다. 그 안에 들어갈 각 카드는 화면 크기의 변동에 따라서도 적용을 해주어야 하기 때문에 resizeObserver를 통해 자신의 실제 높이에 따라 몇 개의 행을 차지할지 계산합니다.

const calcGridRowEnd = () => {
  if (!targetRef.current || !window.visualViewport) return;

  if (window.visualViewport.width < 700) {
    targetRef.current.style.gridRow = 'auto';
    return;
  }

  const child = targetRef.current.children[0];
  if (!child) return;

  const rect = child.getBoundingClientRect();

  const cal = Math.ceil((rect.height + 24) / 34);
  targetRef.current.style.gridRow = `auto / span ${cal}`;
};

Grid내부에 들어갈 각 카드는 resizeObserver를 통해 콘텐츠 크기가 변경될 때마다 함수를 호출하여 gridRow 스타일 속성을 동적으로 조정하여 다양한 높이의 카드들이 자연스럽게 배치됩니다.

모바일 환경에서는 단일 컬럼으로 전환되도록 미디어 쿼리를 적용했습니다.

이 구현 방식으로 Grid를 통해 masonry 레이아웃을 구현할 수 있었어요.

resize 때마다 계속 함수를 호출하다보니 쓰로틀링이 필요한데 아직은 성능적으로 버벅임은 없어서 천천히 개선해보려고 합니다.

마치며.

블로그 마이그레이션 여정과 옵시디언을 활용한 글 관리 시스템에 대해 공유드렸습니다.

GatsbyJS에서 Next.js로의 전환으로 직관적인 코드 구조와 최신 React 기능을 활용할 수 있게 되었고, 블로그 글 작성과 관리 방식도 크게 개선되었습니다.

특히 옵시디언과 iCloud의 조합으로 모바일과 데스크톱 환경에서 모두 편리하게 콘텐츠를 관리할 수 있게 된 것은 큰 만족감을 주었습니다.
별도의 복잡한 CMS 없이도 Git 기반의 워크플로우를 유지하면서 모바일에서의 접근성을 확보했다는 점이 가장 큰 성과라고 생각합니다.

여러분도 자신만의 블로그 환경을 구축하는데 이 글이 도움이 되었으면 좋겠습니다!
함께 성장하는 개발 여정이 되길 바랍니다. 읽어주셔서 감사합니다.

profile
한동룡