기술 블로그를 시작할 때 많은 사람이 제일 먼저 고민하는 건 디자인이나 글쓰기 도구입니다. 그런데 실제로 오래 운영해 보면 가장 먼저 정해두어야 하는 건 오히려 폴더 구조입니다. 초반에는 파일이 몇 개 없어서 아무 데나 넣어도 돌아갑니다. 문제는 글이 10개, 20개, 30개로 늘고, 이미지가 붙고, 정책 페이지가 추가되고, 작은 도구 페이지까지 생기기 시작하면 그때부터 구조의 차이가 그대로 유지보수 비용 차이로 바뀐다는 점입니다.
특히 Next.js App Router는 폴더가 곧 라우팅 구조가 되는 방식이라, 폴더 이름 하나가 URL 설계와 레이아웃 공유 범위까지 함께 결정합니다. 그래서 저는 블로그를 시작할 때부터 “무엇이 URL이 되는 폴더인지”와 “무엇이 단지 내부 구현 폴더인지”를 먼저 구분하는 편이 훨씬 안전하다고 봅니다.
Next.js 공식 문서도 이 점을 분명히 설명합니다. app 디렉터리에서는 중첩 폴더가 라우트 구조를 정의하지만, page.js나 route.js가 없으면 그 경로가 자동으로 공개되는 것은 아닙니다. 또 route group (group)과 private folder _folder를 이용하면 URL을 바꾸지 않고도 구조를 나눌 수 있습니다. src 폴더 역시 공식적으로 지원되며, 앱 코드와 루트 설정 파일을 분리하는 데 유용합니다. 이 글은 그 공식 규칙을 바탕으로, 1인 개발자가 기술 블로그를 시작할 때 가장 덜 꼬이는 구조를 실제 운영 관점에서 정리한 기록입니다.
Next.js App Router로 기술 블로그를 시작할 때, 어디까지를 라우팅 폴더로 쓰고 어떤 파일을 src/lib, content, public으로 분리해야 나중에 덜 꼬이는지 실제 운영 기준으로 정리합니다. 핵심 1 핵심 2 핵심 3Next.js App Router로 기술 블로그 시작할 때 가장 먼저 정해야 하는 폴더 구조
먼저 결론부터: 저는 이렇게 시작하는 편입니다
왜 글 원문을 app 안이 아니라 content로 빼는가
src 폴더는 쓰는 게 좋은가
먼저 결론부터: 저는 이렇게 시작하는 편입니다
아주 작은 블로그라도 아래 정도는 처음부터 나누는 편이 좋습니다.
my-blog/
├─ content/
│ └─ articles/
├─ public/
│ └─ articles/
├─ src/
│ ├─ app/
│ │ ├─ (site)/
│ │ │ ├─ page.tsx
│ │ │ ├─ blog/
│ │ │ │ ├─ page.tsx
│ │ │ │ └─ [slug]/
│ │ │ │ └─ page.tsx
│ │ │ ├─ about/
│ │ │ ├─ contact/
│ │ │ ├─ privacy/
│ │ │ └─ terms/
│ │ ├─ globals.css
│ │ ├─ layout.tsx
│ │ ├─ robots.ts
│ │ └─ sitemap.ts
│ ├─ components/
│ └─ lib/
├─ package.json
└─ next.config.ts
핵심은 단순합니다.
src/app: URL과 레이아웃, 페이지 진입점src/components: 재사용 UIsrc/lib: 데이터 로더, 포맷터, 메타데이터 계산, 유틸content/articles: 실제 글 원문public/articles: 글에 들어가는 이미지
이 다섯 영역만 명확히 갈라놔도, 나중에 블로그가 커질 때 “이 파일을 어디에 둬야 하지?”라는 고민이 크게 줄어듭니다.
왜 글 원문을 app 안이 아니라 content로 빼는가
App Router를 처음 쓰면 app/blog/[slug]/page.tsx 근처에 글 데이터도 같이 두고 싶어집니다. 공식 문서 기준으로는 app 안에 파일을 colocate해도 안전합니다. page.js나 route.js가 없으면 공개 라우트가 되지 않기 때문입니다. 그래서 소규모 프로젝트라면 app/blog/_lib 같은 private folder를 두고 데이터를 넣는 방식도 충분히 가능합니다.
그런데 블로그는 조금 성격이 다릅니다. 글 원문은 UI 코드보다 훨씬 빨리 불어납니다. 글 파일이 많아질수록 라우팅 폴더 안에 콘텐츠가 섞여 있으면, 페이지 진입점과 실제 원문이 뒤엉켜서 보기 어려워집니다. 특히 이미지 경로나 frontmatter, 발행일, 요약문, SEO 메타 정보를 함께 관리하기 시작하면 “콘텐츠 저장소”와 “렌더링 코드”를 분리해두는 편이 훨씬 안정적입니다.
그래서 저는 초반부터 글 본문을 content/articles로 빼는 방식을 추천합니다. 이 구조의 장점은 분명합니다.
| 위치 | 역할 | 장점 |
|---|---|---|
src/app | 라우트와 레이아웃 | URL 구조가 한눈에 보임 |
src/components | UI 조립 | 페이지 코드가 짧아짐 |
src/lib | 데이터 로딩과 변환 | 본문 포맷이 바뀌어도 대응 쉬움 |
content/articles | 실제 글 원문 | 글쓰기와 UI 수정 흐름을 분리 가능 |
public/articles | 이미지 자산 | 본문 이미지 경로가 단순해짐 |
초기엔 좀 과해 보여도, 글이 10개만 넘어가면 이 분리가 왜 필요한지 체감하게 됩니다.
src 폴더는 쓰는 게 좋은가
Next.js 공식 문서는 src 폴더를 정식으로 지원합니다. 장점은 명확합니다. package.json, next.config.ts, lockfile 같은 루트 설정 파일과, 실제 애플리케이션 코드를 물리적으로 분리할 수 있습니다. 특히 혼자 운영하는 블로그라도 나중에 스크립트, 배포 설정, 콘텐츠 파이프라인이 추가되면 루트가 금방 어수선해지기 때문에, 저는 처음부터 src를 쓰는 쪽이 더 낫다고 봅니다.
다만 주의할 점도 있습니다. public은 루트에 남아야 하고, 환경 변수 파일도 루트에 있어야 합니다. TypeScript path alias를 쓰고 있다면 src 기준으로 import 경로가 맞는지도 확인해야 합니다. 이 정도만 챙기면 src는 장기 운영에 꽤 도움이 됩니다.
App Router에서는 어떤 폴더가 정말 중요한가
App Router에서는 모든 폴더를 똑같이 다루면 오히려 복잡해집니다. 저는 다음 세 가지 구분이 가장 중요하다고 생각합니다.
1. 실제 URL이 되는 폴더
예를 들어 app/blog/[slug]/page.tsx는 실제로 /blog/my-post 같은 주소가 됩니다. 이 영역은 곧 정보 구조이기 때문에, 의미 없는 중첩을 피해야 합니다. URL과 직접 연결되는 폴더는 적을수록 좋고, 각 폴더 이름이 사용자 관점에서도 이해 가능해야 합니다.
2. URL에 영향을 주지 않는 route group
공식 문서에 따르면 (group) 형태의 route group은 URL에 포함되지 않습니다. 이건 생각보다 유용합니다. 예를 들어 블로그 본문, 소개, 정책 페이지가 모두 같은 레이아웃을 공유한다면 app/(site)/... 아래에 묶어둘 수 있습니다. 나중에 대시보드, 툴, 게임 소개 페이지 같은 다른 영역이 생겨도 그룹 단위로 구분하기 쉬워집니다.
다만 route group은 조직화 용도이지, 남발할 기능은 아닙니다. 그룹이 많아질수록 실제 URL과 폴더 구조 사이의 대응이 흐려질 수 있습니다. 보통은 (site), (tools), (admin) 정도처럼 목적이 분명할 때만 쓰는 편이 좋습니다.
3. 내부 구현용 private folder
공식 문서에서 _folder는 라우팅 시스템에서 제외되는 private folder로 설명됩니다. 저는 특정 라우트 안에만 쓰는 컴포넌트나 헬퍼가 있을 때 이 방식을 선호합니다.
src/app/(site)/blog/
├─ [slug]/
│ └─ page.tsx
├─ _components/
│ └─ article-layout.tsx
└─ _lib/
└─ blog-format.ts
이렇게 두면 “블로그 라우트와 관련된 내부 구현”이 한 곳에 모여 있어 찾기 쉽습니다. 특히 협업보다 혼자 운영할 때, _components와 _lib는 맥락 보존에 꽤 도움이 됩니다.
처음부터 이렇게 나누면 좋은 이유
제가 기술 블로그 구조를 잡을 때 제일 피하고 싶은 건 “초기에는 빨리 만들었는데 3개월 뒤에 전부 옮겨야 하는 상태”입니다. 대표적으로 아래 같은 상황이 자주 생깁니다.
- Markdown 파일이
src/lib와app/blog여기저기에 흩어짐 - 이미지가
public/,public/blog/,public/uploads/에 뒤섞임 - 정책 페이지와 실제 글 페이지가 같은 성격처럼 섞여서 탐색이 어수선해짐
- 나중에 툴 페이지를 추가하려는데 블로그 구조에 너무 강하게 묶여 있음
이걸 막으려면 처음부터 “콘텐츠 저장 위치”, “이미지 위치”, “라우트 위치”, “재사용 UI 위치”만은 분리해두는 편이 훨씬 좋습니다.
예를 들어 글을 읽어와서 렌더링하는 로더는 이렇게 별도 lib에 두는 편이 깔끔합니다.
import fs from "node:fs/promises";
import path from "node:path";
import matter from "gray-matter";
const ARTICLES_DIR = path.join(process.cwd(), "content", "articles");
export async function getArticleBySlug(slug: string) {
const raw = await fs.readFile(path.join(ARTICLES_DIR, `${slug}.md`), "utf8");
const parsed = matter(raw);
return {
frontmatter: parsed.data,
body: parsed.content,
};
}
이렇게 해두면 페이지 파일은 “읽고, 메타데이터 만들고, 렌더링한다”는 본래 역할에 집중하게 됩니다.
제가 추천하는 실전 기준
운영 난이도를 기준으로 보면, 초반에 다음 네 가지를 정해두는 게 가장 중요합니다.
콘텐츠는 content/articles
글 원문은 한곳에 모읍니다. 글이 늘어나도 경로가 단순해야 하고, 백업이나 이동도 쉬워야 합니다.
이미지는 public/articles/<slug>
글마다 폴더를 하나씩 갖게 하면 나중에 정리하기 쉽습니다. 예를 들어 첫 글이면 public/articles/nextjs-blog-folder-structure/cover.svg처럼 두는 방식입니다. 이 규칙은 글 수가 늘어날수록 강력해집니다.
라우트는 최대한 얇게
app/blog/[slug]/page.tsx에는 데이터 호출과 메타데이터, 렌더링 조립만 두고, 큰 로직은 lib나 컴포넌트로 뺍니다. 페이지 파일이 길어지기 시작하면 나중에 수정 범위가 넓어집니다.
UI와 데이터는 분리
카드 컴포넌트, 목차, 헤더, 푸터는 components로 빼고, frontmatter 처리나 본문 변환은 lib로 보냅니다. 이 구분이 있어야 UI 개편과 콘텐츠 시스템 변경이 서로 덜 충돌합니다.
자주 하는 실수 5가지
1. app 폴더 안에 모든 것을 다 넣는다
기술적으로는 가능하지만, 글이 늘어날수록 페이지 파일과 콘텐츠 파일이 섞여서 찾기 어려워집니다.
2. 이미지 경로 규칙이 없다
초반에는 그냥 public/에 넣어도 되지만, 나중에는 어떤 이미지가 어느 글 것인지 찾기 어려워집니다.
3. route group을 너무 많이 쓴다
URL에 안 보인다고 해서 그룹을 남발하면 폴더 구조 해석이 오히려 어려워집니다.
4. page.tsx가 데이터 처리까지 전부 떠안는다
지금은 쉬워 보여도, 글 메타데이터나 related post 로직이 붙는 순간 빠르게 비대해집니다.
5. 글 시스템과 사이트 시스템을 따로 보지 않는다
블로그는 결국 “페이지”만이 아니라 “콘텐츠 저장 방식”과 “자산 관리 방식”이 함께 움직이는 시스템입니다. 이 둘을 초반에 분리하지 않으면 확장 단계에서 다시 뜯어고치게 됩니다.
처음 시작하는 사람에게 제가 추천하는 최소 체크리스트
src를 쓸지 말지 먼저 결정했는가app는 라우트와 레이아웃 위주로만 둘 것인가- 실제 글 원문은
content/articles로 분리할 것인가 - 이미지 경로를
public/articles/<slug>로 고정했는가 components,lib,content,public의 역할을 한 줄로 설명할 수 있는가- route group과 private folder는 정말 필요한 곳에만 쓰고 있는가
- 나중에 툴 페이지나 게임 페이지가 추가돼도 이 구조가 버틸 수 있는가
마무리
Next.js App Router에서 좋은 폴더 구조는 “정답”을 찾는 문제가 아니라, 라우트와 콘텐츠, UI와 데이터를 헷갈리지 않게 나누는 문제에 가깝습니다. 제 기준에서는 블로그를 시작할 때 가장 먼저 정해야 하는 건 디자인보다도 이 경계입니다.
처음부터 완벽할 필요는 없습니다. 하지만 최소한 src/app, src/components, src/lib, content/articles, public/articles 정도의 구분만은 초기에 잡아두는 편이 훨씬 낫습니다. 이 정도만 해도 글이 늘어날 때, 이미지가 붙을 때, 정책 페이지가 추가될 때, 작은 도구가 들어올 때 구조가 쉽게 무너지지 않습니다.
App Router는 라우팅 규칙이 강한 대신, 공식 문서가 route group, private folder, src folder, colocation에 대한 기준을 꽤 잘 제공하고 있습니다. 그 규칙을 그대로 믿고, 라우트는 얇게, 콘텐츠는 분리해서 시작하는 것이 1인 개발 블로그 기준으로는 가장 오래 버티는 선택이라고 생각합니다.