
블로그 글이 20편을 넘어가니까 문제가 생겼다. 시간순으로 쭉 나열된 목록에서 원하는 글을 찾기가 어렵다. '홈랩 시리즈 글만 보고 싶은데'라는 생각이 자꾸 든다. 카테고리 텍스트 필드 하나로는 한계가 분명했다. PayloadCMS에 시리즈 컬렉션을 새로 만들고, 블로그 UI를 탭 필터링 방식으로 리디자인한 과정을 정리한다.
기존에는 category라는 텍스트 필드 하나로 글을 분류했다. voice-ai, app-web-dev, infra 같은 값을 직접 입력하는 방식이다. 문제는 두 가지였다.
voice-ai와 voiceai가 다른 카테고리로 취급된다.필요한 건 '시리즈'라는 독립적인 엔티티였다. 제목, 설명, 이모지, 대표 이미지를 가지고, 포스트와 1:N 관계를 맺는 컬렉션.
PayloadCMS에서 새 컬렉션을 만드는 건 간단하다. 스키마를 정의하면 어드민 UI, API, 타입이 자동으로 생성된다.
핵심 필드는 order다. 시리즈 목록을 보여줄 때 정렬 기준이 된다. emoji는 탭 UI에서 시리즈를 시각적으로 구분하는 용도.
Posts 컬렉션의 category 텍스트 필드를 series relationship 필드로 교체했다. hasMany: true로 설정해서 하나의 글이 여러 시리즈에 속할 수 있게 했다.
여기에 seriesOrder 숫자 필드를 추가했다. 같은 시리즈 안에서 글의 순서를 지정하는 필드다. 시리즈별로 1, 2, 3... 이런 식으로 매긴다.
매번 시리즈 순서를 수동으로 입력하는 건 귀찮다. beforeChange 훅으로 자동 계산하도록 만들었다.
시리즈에 속하는 글을 새로 만들 때, 해당 시리즈의 마지막 순서를 조회해서 +1한 값을 자동으로 넣어준다. seriesOrder를 직접 입력하면 그 값을 그대로 사용한다.
블로그 목록 페이지에 시리즈별 탭을 만들었다. '전체' 탭과 각 시리즈 탭이 있고, 탭을 클릭하면 URL의 쿼리 파라미터가 바뀌면서 해당 시리즈의 글만 필터링된다.
전체 탭에서는 시간순(최신 우선), 시리즈 탭에서는 seriesOrder 오름차순으로 정렬한다. 시리즈를 순서대로 읽고 싶은 사용자를 위한 배려다.
홈페이지도 시리즈 중심으로 바꿨다. 시리즈 카드가 그리드로 배치되고, 각 카드에는 이모지, 제목, 설명, 글 개수가 표시된다. 카드를 클릭하면 해당 시리즈로 필터링된 블로그 목록으로 이동한다.
최신 글과 추천 글(isFeatured: true) 섹션도 추가해서 시리즈에 속하지 않는 글도 자연스럽게 노출되도록 했다.
Lexical 에디터의 upload 노드가 때에 따라 { value: { url: '...' } } 객체로 저장되기도 하고, 문자열 ID로 저장되기도 한다. 렌더링 코드에서 두 경우를 모두 처리해야 했다. 타입 체크 없이 .url에 접근하면 런타임 에러가 터진다.
카드 목록에서는 sizes.card 크롭 이미지를 쓰지만, 글 본문에서는 원본 이미지를 써야 한다. 처음에 sizes.card를 본문에도 적용해서 잘린 이미지가 나왔다. 용도에 따라 이미지 사이즈를 구분해서 써야 한다.
기존 포스트의 category 텍스트 값을 새 series relationship으로 매핑하는 작업은 REST API로 일괄 처리했다. 20편 정도라서 스크립트를 짜는 게 수동 작업보다 빨랐다.
카테고리 텍스트 필드 하나를 시리즈 컬렉션으로 바꿨을 뿐인데, 블로그의 탐색 경험이 확 달라졌다. 스키마 설계가 UI를 결정한다는 걸 다시 한번 느꼈다. 데이터 모델이 잘 잡혀 있으면 UI는 자연스럽게 따라온다.