본문으로 건너뛰기

← 블로그 · Projects

[케이스 스터디] Toris Blog - 우리 회사의 기술 블로그를 우리가 만든 방식

#Next.js #TypeScript #블로그 #도그푸딩 #케이스 스터디 #Toris

Toris Blog — 우리 회사의 기술 블로그를 우리가 만든 방식

제품명: Toris Blog 최초 구축: 2024.03 (이후 지속 개선 중) 기술 스택: Next.js, TypeScript, Tailwind CSS, MDX, Vercel, GitHub Actions 포지셔닝: 토리스의 공식 기술 블로그이자, 자사 기술 스택의 상시 실험장

지금 이 글이 실려 있는 블로그가 곧 이 케이스 스터디의 대상이다. Toris Blog는 토리스(Toris)가 외주나 기성 플랫폼 없이 직접 설계·구축·운영하는 자사 미디어다. 우리가 클라이언트 제품에 쓰는 것과 같은 스택으로 우리 사이트를 만들고, 여기서 검증된 패턴을 다시 제품에 가져간다 — 전형적인 도그푸딩 구조다. 이 글은 그 첫 구축에서 내린 기술 결정과, 운영하면서 드러난 트레이드오프를 기록한다.

1. 문제 정의와 제품 가설

소프트웨어 스튜디오에게 기술 블로그는 두 가지 문제를 동시에 푼다. 첫째, 프로젝트에서 얻은 지식이 개인 메모(당시에는 노션)에 갇혀 휘발되는 문제 — 정리되지 않은 지식은 재사용되지 않는다. 둘째, “이 스튜디오가 무엇을 어떻게 만드는가”를 보여줄 공개 지면의 부재다.

기성 블로그 플랫폼을 쓰는 선택지도 있었다. 그러나 우리는 반대로 갔다.

“우리가 만드는 방식을 보여주는 가장 정직한 방법은, 우리 사이트를 우리 손으로 만드는 것이다.”

블로그 자체가 포트폴리오이자 기술 검증장이 되는 구조다. 새 프레임워크 기능, 렌더링 전략, SEO 기법을 클라이언트 제품에 적용하기 전에 여기서 먼저 운용해 본다. 실패해도 피해 범위가 우리 자신에게 갇힌다는 점에서, 자사 블로그는 가장 저렴한 실험 환경이다.

2. 제약 조건

  • 1인 운영: 콘텐츠 작성자와 개발자와 운영자가 같은 사람이다. 유지보수 비용이 낮은 구조가 아니면 블로그는 방치된다.
  • 인프라 예산 최소화: 자사 미디어에 고정 서버 비용을 태울 이유가 없다. 데이터베이스, 관리형 CMS 같은 유상 인프라는 그 가치가 증명되기 전에는 배제한다.
  • 콘텐츠가 곧 코드인 워크플로: 글쓰기가 개발 워크플로(에디터, Git, PR)와 분리되면 두 도구를 오가는 마찰이 생긴다.
  • 검색 유입 의존: 방문자의 대부분은 검색으로 온다. SEO는 부가 기능이 아니라 미디어의 생존 조건이다.

3. 기술 설계와 트레이드오프

3.1 스택 선택: Next.js + TypeScript + Tailwind

프레임워크는 Next.js를 선택했다. 결정 근거는 세 가지다. 첫째, 블로그는 본질적으로 정적 콘텐츠이므로 정적 생성(SSG)과 서버 사이드 렌더링을 페이지 단위로 선택할 수 있는 하이브리드 렌더링이 잘 맞는다. 둘째, 파일 기반 라우팅이 콘텐츠 구조와 URL 구조를 자연스럽게 일치시킨다. 셋째, 이것이 토리스의 주력 스택이다 — 도그푸딩 목적상 블로그는 우리가 제품에 쓰는 것과 같은 기술로 만들어야 의미가 있다.

서버 컴포넌트와 클라이언트 컴포넌트의 경계 설정은 초기 학습 비용이 있었지만, 그 경계를 명시적으로 긋는 훈련 자체가 이후 프로젝트의 설계 감각으로 이어졌다. Image 컴포넌트의 자동 이미지 최적화처럼 프레임워크가 대신 해주는 영역을 파악해 두는 것도, “무엇을 직접 만들지 않을지”를 결정하는 데 중요했다.

TypeScript는 초기에는 타입 정의가 비용처럼 보였지만, 곧 콘텐츠 스키마의 단일 진실 공급원이 됐다.

interface Post {
  title: string;
  content: string;
  date: string;
}

const posts: Post[] = getPosts();

Post 인터페이스는 프론트매터 규약을 코드로 강제한다. 필드를 바꾸면 컴파일러가 영향 범위를 전부 짚어준다. 1인 운영에서 컴파일러는 사실상 코드 리뷰어의 역할을 겸한다. IDE 자동완성의 정확도 향상은 덤이다.

3.2 파일 기반 CMS — 이 프로젝트의 중심 트레이드오프

가장 큰 아키텍처 결정은 콘텐츠 저장소였다. 관리형 CMS·데이터베이스 대신, 마크다운(MDX) 파일을 Git 저장소에 그대로 두는 파일 기반 CMS를 택했다.

관점파일 기반 (선택)DB/관리형 CMS (기각)
인프라 비용0 — 저장소가 곧 CMSDB 호스팅 또는 CMS 구독
버전 관리Git 히스토리가 무료로 따라옴별도 구현 필요
작성 워크플로코드와 동일 (에디터 → 커밋 → PR)별도 어드민 화면
장애 지점없음 (빌드 시점에 정적화)DB가 단일 장애점
동적 콘텐츠어려움용이
대량 관리파일이 늘수록 부담스케일에 유리

기각한 대안의 장점 — 동적 콘텐츠, 대량 관리 — 은 실재하는 비용이다. 파일이 많아지면 관리가 어려워지고, 댓글 같은 동적 기능은 파일 기반만으로는 풀리지 않는다. 그럼에도 이 선택을 한 이유는 운영 주체의 형태 때문이다. 1인 스튜디오에서 “글쓰기 = 커밋”이라는 등식은 콘텐츠 발행의 마찰을 최소로 만든다. 글의 오탈자 수정도, 대규모 개편도 같은 PR 워크플로를 탄다. 콘텐츠의 리뷰·롤백·이력 추적을 Git이 공짜로 제공한다는 점은, 별도 CMS의 어떤 편의 기능보다 값어치가 컸다.

이 결정은 지금까지 유효하다. 단, “파일이 많아지면”의 시점이 오면 재검토한다는 조건부다. 아키텍처 결정에는 유효 기한이 있고, 그것을 명시해 두는 것까지가 설계라고 본다.

3.3 렌더링 파이프라인: 코드 하이라이팅의 교체

기술 블로그의 본문 품질은 코드 블록에서 판가름 난다. 처음에는 highlight.js로 시작했으나, 운영 중에 shiki로 교체했다. 교체 근거는 렌더링 품질이다 — shiki는 에디터급 문법 분석 기반 하이라이팅을 제공하고, 빌드 시점에 처리되므로 클라이언트 부담도 정리된다. 다크/라이트 각 모드에 맞는 테마 대응도 함께 설계했다.

이 교체에서 얻은 운영 감각이 있다. 큰 코드 블록의 로딩 비용처럼, 라이브러리 선택의 성능 함의는 도입 시점이 아니라 콘텐츠가 쌓인 뒤에 드러난다. 자사 블로그였기에 부담 없이 교체 실험을 할 수 있었고, 이런 “운영 중 교체” 경험이 도그푸딩의 실질 수익이다.

3.4 테마 시스템

다크모드는 CSS 변수 기반 테마 전환으로 구현했다. 컴포넌트가 색상 값을 직접 알지 못하게 하고 변수 참조만 남기면, 테마 전환은 루트에서 변수 세트를 갈아 끼우는 문제로 단순해진다.

const [theme, setTheme] = useState('light');

const toggleTheme = () => {
  const newTheme = theme === 'light' ? 'dark' : 'light';
  setTheme(newTheme);
  localStorage.setItem('theme', newTheme); // 재방문 시 유지
};

사용자 선택은 localStorage에 저장해 재방문 시 유지하고, 시스템 테마 감지로 초기값을 정한다. 작은 기능이지만 “사용자의 환경 설정을 존중한다”는 원칙의 축소판이며, 이 패턴은 이후 토리스의 다른 제품에 그대로 이식됐다.

3.5 탐색 구조: URL을 상태의 원천으로

카테고리·태그 필터는 컴포넌트 내부 상태가 아니라 URL 파라미터를 상태의 원천으로 설계했다. Next.js의 useRouter로 파라미터를 관리하면 필터된 화면이 공유 가능한 링크가 되고, 뒤로 가기가 자연스럽게 동작한다. 필터링 로직은 커스텀 훅으로 분리해 목록 화면 전반에서 재사용했다. “공유할 수 있는 화면 상태는 URL에 둔다”는 이 원칙 역시 이후 프로젝트의 기본값이 됐다.

3.6 SEO — 검색 유입이 생존 조건인 미디어의 필수 설계

블로그의 유통 채널은 검색이다. 따라서 SEO는 세 층위로 구조화했다.

  • 메타데이터 계층: 페이지별 generateMetadata로 title/description을 생성하고, Open Graph 태그로 소셜 공유 시의 표시 품질을 관리한다.
  • 구조화 데이터: JSON-LD로 검색 엔진에 콘텐츠의 의미 구조를 전달한다.
  • 크롤링 지원: 사이트맵을 생성해 색인 경로를 명시한다.
export async function generateMetadata({ params }) {
  return {
    title: post.title,
    description: post.description,
    openGraph: {
      title: post.title,
      description: post.description,
      images: [post.image]
    }
  };
}

파일 기반 CMS와 SEO는 궁합이 좋다. 프론트매터가 곧 메타데이터의 원천이므로, 글을 쓰는 행위가 자동으로 SEO 데이터를 생성한다. 별도의 “SEO 입력 화면”이 필요 없는 구조다.

3.7 배포 파이프라인

배포는 Vercel + GitHub Actions 조합이다. 메인 브랜치에 푸시하면 자동으로 빌드·배포되는 파이프라인으로, 콘텐츠 발행과 코드 배포가 동일한 경로를 탄다. 이 구조에서 빌드 실패는 배포 전 방어선 역할을 한다 — 깨진 프론트매터나 타입 오류가 프로덕션에 도달하기 전에 파이프라인이 막아 준다. 환경 변수 관리와 빌드 에러 대응에 초기 비용이 들었지만, 일단 갖춰진 뒤로는 발행 마찰이 사실상 사라졌다.

4. 독자·사용자와의 검증

자사 블로그의 피드백 루프는 두 겹이다. 독자로부터의 루프와, 우리 자신이 첫 번째 사용자라는 도그푸딩 루프다.

4.1 첫 번째 사용자는 우리 자신이다

도그푸딩의 본령은 여기다. 글을 쓸 때마다 우리가 이 블로그의 저자 경험(authoring UX)을 사용하고, 불편은 즉시 백로그가 된다. 코드 하이라이팅을 highlight.js에서 shiki로 교체한 것도 우리가 독자이자 저자로서 렌더링 품질에 만족하지 못했기 때문이고, 필터 훅을 재사용 가능하게 분리한 것도 화면을 늘려가며 우리가 반복 작업을 체감했기 때문이다. 사용자와 개발자의 거리가 0인 제품은 피드백 반영 속도가 곧 개발 속도다.

4.2 검색 엔진과 소셜 그래프도 사용자다

독자가 블로그에 도달하는 경로 — 검색 결과와 소셜 공유 카드 — 는 그 자체가 검증 대상이다. Open Graph 설정 후 공유 화면이 제대로 표시되는지, 구조화 데이터가 검색 엔진에 올바르게 해석되는지는 배포 후 실제 채널에서 확인해야 하는 항목이다. “발행하면 끝”이 아니라 “유통 경로에서 어떻게 보이는가”까지가 콘텐츠 시스템의 책임 범위라는 것을, 이 블로그를 운영하며 체화했다.

4.3 아직 얇은 루프: 독자와의 직접 대화

정직하게 기록하면, 독자로부터의 직접 피드백 채널(댓글 등)은 첫 구축 범위에 포함하지 못했다. 파일 기반 아키텍처에서 동적 기능은 별도 설계가 필요하기 때문이다. 댓글과 검색은 명시적인 로드맵 항목으로 관리하고 있으며, 이 블로그가 지속 개선형 프로젝트인 이유이기도 하다.

5. 운영과 결과

Toris Blog는 2024년 3월 첫 구축 이후 현재까지 운영 중인, 토리스에서 가장 수명이 긴 제품이다(toris-blog.vercel.app). 다크모드, shiki 기반 코드 하이라이팅, 카테고리/태그 탐색, 3층위 SEO, 파일 기반 CMS, 자동 배포 파이프라인이 현재의 골격이다.

기술 부채도 투명하게 관리한다.

  • 테스트 부재: 첫 구축 시 테스트 코드를 작성하지 않았다. 당시에는 우선순위 판단이었지만, 지속 개선형 프로젝트에서 회귀 방지 장치의 부재는 개선 속도를 갉아먹는다는 것을 이후 체감했다.
  • 접근성: 키보드 내비게이션, 스크린 리더 지원이 초기 설계에서 빠졌다. 공개 미디어로서 갖춰야 할 기준이며 보완 대상이다.
  • 성능: 이미지 최적화 강화, 코드 스플리팅 확대, 불필요한 리렌더링 제거가 상시 개선 항목이다.

이 부채 목록 자체가 도그푸딩의 산출물이다. 우리 사이트에서 겪은 부채의 비용을 근거로, 클라이언트 프로젝트에서는 테스트와 접근성을 첫 구축 범위에 포함하도록 프로세스를 바꿨다.

6. 재사용 가능한 교훈

  1. 운영 주체의 형태가 아키텍처를 결정한다. 1인 운영에서는 “글쓰기 = 커밋”인 파일 기반 CMS가 관리형 CMS의 기능 목록을 이긴다. 아키텍처 비교표는 팀 구조 위에서 읽어야 한다.
  2. 아키텍처 결정에 유효 기한을 달아라. 파일 기반 CMS는 “콘텐츠가 대량화되기 전까지”라는 조건부 결정이다. 재검토 트리거를 명시하는 것까지가 설계다.
  3. 공유 가능한 화면 상태는 URL에 둔다. 필터·탐색 상태를 URL 파라미터로 관리하면 링크 공유와 브라우저 히스토리가 공짜로 따라온다.
  4. 콘텐츠 시스템의 책임은 유통 경로까지다. 프론트매터 → 메타데이터 → 검색/소셜 표시로 이어지는 파이프라인 전체가 제품이다.
  5. 자사 서비스는 가장 저렴한 실험장이다. 라이브러리 교체(highlight.js → shiki), 렌더링 전략, 테마 시스템을 여기서 먼저 검증하고 제품으로 가져간다. 그리고 여기서 진 부채(테스트, 접근성)의 비용을 측정해, 다음 프로젝트의 첫 구축 범위를 바꾸는 근거로 쓴다.

링크