본문으로 건너뛰기
/home/soohan

흩어진 작업의 맥락을 쌓아두기

이력서 한 줄과 저장소의 코드 사이에 흩어지던 판단과 운영 경험을 다시 찾을 수 있도록, 경력과 프로젝트, 생각을 한곳에 모으는 개인 사이트를 만들었다.

5#nextjs#mdx#vercel

프로젝트가 끝나면 결과는 이력서 한 줄로 줄고, 구현은 저장소의 코드로 남았습니다. 나중에 다시 보면 무엇을 만들었는지는 알 수 있지만 왜 그 구조를 골랐는지, 실제로 운영한 뒤 무엇을 바꿨는지는 찾기 어려웠습니다. 그때 했던 판단과 이후의 경험이 프로젝트마다 흩어지는 것도 아쉬웠습니다.

soohan-dev는 그 사이를 채우기 위해 만든 개인 사이트입니다. 경력과 프로젝트, 오픈소스 기여, 일하며 부딪힌 문제와 그 밖의 기록을 한곳에 모읍니다. 완성된 결과만 전시하기보다, 시간이 지난 뒤에도 제가 왜 그렇게 만들었는지 다시 찾을 수 있는 장소를 만들고 싶었습니다.

흩어진 기록을 한곳에

처음에는 무엇을 어디까지 적을지부터 나눌 필요가 있었습니다. 경력, 완성된 프로젝트, 그 밖의 생각이 한 목록에 섞이면 다시 찾기 어려울 것 같아 세 갈래로 구분했습니다.

경로담는 내용
/user지금 하는 일과 지금까지의 경력
/projects소속별로 직접 책임진 작업, 개인 프로젝트, 오픈소스 기여
/notes프로젝트 결과물 밖의 생각과 조사 기록

경력은 /user에서 한 번에 읽고, 프로젝트는 이력서의 한 줄에서 빠진 이야기를 따로 남깁니다. 어떤 문제에서 시작했는지, 무엇을 선택했고 운영 뒤에 무엇을 알게 됐는지를 글 하나로 다시 풀어 씁니다. 오픈소스 기여도 저장소 이름만 나열하지 않고, 기여마다 제가 바꾼 내용과 그 이유를 기록합니다.

notes에는 아직 프로젝트로 묶이지 않는 생각이나 정기적으로 살펴보는 주제를 둡니다. 만들기 전 읽고 생각한 내용도 시간이 지나면 제가 어떤 판단을 하게 됐는지 설명하는 기록이 되기 때문입니다.

구조가 곧 탐색 방식

글을 추가할 때 파일과 메뉴를 따로 등록하고 싶지는 않았습니다. 그래서 사이트의 정보 구조와 파일 구조를 일치시켰습니다. content/ 아래의 디렉터리는 카테고리가 되고, 파일명은 URL이 됩니다. 왼쪽 사이드바도 이 구조를 그대로 보여줍니다.

content/
├── _pages/user.mdx                 → /user
├── projects/
│   ├── nexon/                      → /projects/nexon
│   ├── open-source/                → /projects/open-source
│   └── personal/soohan-dev.mdx     → /projects/personal/soohan-dev
└── notes/
    ├── soohan/                     → /notes/soohan
    └── hanibot/research/           → /notes/hanibot/research

카테고리를 한 번 정의하고 MDX 파일을 추가하면 URL, 사이드바, 목록, 이전·다음 글이 같은 구조에서 나옵니다. 글이 늘어날 때마다 메뉴와 목록을 각각 고치다가 하나를 빼먹는 일을 만들고 싶지 않았습니다.

하나의 원본, 여러 독자

글을 여러 곳에 다시 작성하지 않도록 원본은 MDX 파일 하나만 둡니다. 빌드 과정이 이 파일을 읽고 검증한 뒤, 사람과 프로그램이 각자 읽기 좋은 형태로 내보냅니다.

content/**/*.mdx
        ↓ parse · validate
Post
├── HTML                  웹에서 읽는 글
├── /md/{slug}            Markdown 원문
├── rss.xml · sitemap.xml 구독과 검색
├── llms.txt              글 목록과 요약
├── llms-full.txt         전체 본문
└── BlogPosting JSON-LD   구조화된 메타데이터

브라우저에서는 정적 HTML로 읽고, 피드 리더에서는 RSS로 구독할 수 있습니다. Markdown 원문과 llms.txt, llms-full.txt도 함께 만들어 AI나 다른 도구가 화면을 다시 파싱하지 않고 글에 접근할 수 있게 했습니다. 독자마다 다른 원고를 관리하지 않고 같은 글을 여러 인터페이스로 보여주는 방식입니다.

Next.js App Router는 이 경로들을 정적으로 생성하고, MDX는 링크·표·코드와 이미지 컴포넌트를 글 안에서 함께 쓰게 해줍니다. 새로운 기능을 많이 붙이기보다 원본 하나에서 이 출력들을 계속 만들 수 있는지가 기술을 고른 기준이었습니다.

배포 전에 실패하게

파일을 추가하는 것만으로 글이 공개되는 구조는 잘못된 파일도 그대로 원본이 된다는 뜻이었습니다. 실제로 한글 파일명은 정적 생성과 목록에서는 멀쩡해 보이는데 링크를 열면 404가 됐고, 파일명에 &가 들어가면 RSS와 sitemap XML 전체가 깨졌습니다. 조용히 잘못된 글을 내보내는 것보다 빌드가 깨지는 편이 나았습니다.

그래서 콘텐츠를 읽을 때 title, date, summary와 날짜, 카테고리를 검사합니다. URL로 쓰는 슬러그도 ASCII 소문자·숫자·하이픈만 허용하고, 규칙에서 벗어나면 기본값으로 넘기지 않고 바로 실패시킵니다.

draft: true인 글은 개발 환경에서만 보이고 프로덕션 빌드에서는 제외됩니다. MDX 주석에 적어둔 작성 메모도 HTML뿐 아니라 Markdown과 llms-full.txt에서 함께 제거합니다. 어느 한 출력만 정상인 상태를 피하려고 모든 공개 경로가 같은 파싱 결과를 사용합니다.

빌드가 성공해도 실제 요청에서만 드러나는 문제가 있었습니다. 그래서 빌드 뒤에는 서버를 직접 띄우고 HTML과 Markdown 경로, 리다이렉트, RSS, sitemap, LLM용 문서를 함께 확인합니다. 한 화면만 보고 괜찮다고 판단하지 않도록 이 검사를 스크립트 하나로 묶었습니다.

적게 운영하고 오래 남기기

글은 에디터에서 쓰고, 변경 이력은 Git에서 보고, push하면 배포됩니다. 제가 이 사이트를 운영하는 데는 이 정도면 충분해서 CMS와 데이터베이스는 두지 않았습니다. 관리 화면과 인증, 데이터베이스 백업까지 관리하기보다 텍스트 파일을 오래 보관하고 어디서든 다시 빌드할 수 있는 편을 택했습니다.

텍스트와 이미지는 분리했습니다. MDX는 저장소에 두고, 이미지는 Vercel Blob에 올린 뒤 크기 정보와 함께 본문에 연결합니다. 저장소를 가볍게 유지하고 next/image 최적화를 쓰는 대신, 외부 스토리지가 사라지면 과거 글의 이미지가 깨진다는 의존성은 남습니다. 편한 점뿐 아니라 나중에 무엇을 복구해야 하는지도 알고 선택한 구조입니다.

검색과 댓글도 아직 넣지 않았습니다. 지금은 사이드바와 카테고리만으로 글을 찾을 수 있고, 새로운 기능을 운영하는 것보다 기록을 계속 남기는 일이 먼저라고 생각했습니다. 제가 실제로 필요하다고 느낄 때 추가하고, 언젠가 필요할 것 같다는 이유만으로 관리할 대상을 늘리지는 않으려 합니다.

계속 바뀌는 홈 디렉터리

soohan-dev는 지금까지 한 일을 한 번 정리하고 멈추는 포트폴리오가 아닙니다. 새 프로젝트를 맡거나 오픈소스에 기여하고, 일하는 방식이 달라질 때마다 내용과 구조도 함께 바뀝니다.

무엇을 만들었는지만 적으면 시간이 지난 뒤에는 결과만 남습니다. 왜 그렇게 만들었고 이후에 무엇을 고쳤는지까지 제 손으로 계속 쌓아두는 것, 그게 이 사이트를 만든 이유입니다.

이 글의 Markdown 원문 →