
Sprinters에는 스프린터가 작성한 글 목록을 볼 수 있는 기록 페이지와, 스프린터의 프로필 사진과 자기소개 등을 확인할 수 있는 프로필 페이지가 있다. 두 페이지 모두 탭 UI를 사용한다.
기록 페이지에는 Trending, Top, Latest 탭이 있다. 각 탭은 서로 다른 계산 방식으로 글의 점수를 매기고, 그 결과에 따라 글 목록을 정렬해 보여준다.
반면 프로필 페이지의 탭은 성격이 조금 다르다. 이곳에서는 한 스프린터와 관련된 기록, 답변, 소개를 탭으로 나누어 보여준다.
여기서 고민이 생겼다. 각 페이지의 탭 상태를 URL에 표현할 때, 경로 세그먼트를 사용하는 것이 좋을까? 아니면 쿼리 매개변수를 사용하는 것이 좋을까?
식별 vs 표현
경로 세그먼트와 쿼리 매개변수를 구분하는 가장 중요한 기준은 '자원을 식별하는가'와 '자원을 어떻게 가져올지 결정하는가'에 있다. RFC 3986의 3.3. Path 섹션에서는 경로를 계층적 데이터, 즉 hierarchical data를 표현하는 부분으로 설명한다. 반면 쿼리는 경로 안에 담기 어려운 비계층적 데이터, 즉 non-hierarchical data를 표현하는 데 사용된다.
경로는 계층적 구조를 통해 URL이 가리키는 자원이 무엇인지, 그리고 그 자원이 어디에 속해 있는지를 드러낸다. 예를 들어 특정 사용자의 프로필, 특정 게시글, 특정 카테고리처럼 URL의 대상 자체를 식별해야 한다면 경로 세그먼트가 더 적절하다. 경로는 의미가 명확하고 가독성이 좋기 때문에 페이지 단위의 라우팅, 레이아웃, 메타데이터 설계와도 잘 맞는다.
쿼리는 이미 식별된 자원을 어떤 방식으로 보여줄지 결정할 때 사용된다. 정렬 기준, 필터 조건, 검색어, 페이지 번호처럼 자원의 표현 방식이나 조회 조건을 조정하는 값들이 여기에 해당한다. 쿼리는 순서에 의존하지 않고, 새로운 매개변수를 추가해도 기존 URL 구조를 크게 깨뜨리지 않으며, 여러 조건을 자유롭게 조합할 수 있다는 장점이 있다.
결국, 경로는 이 URL이 무엇을 가리키는가를 드러내고, 쿼리는 그 자원을 어떤 조건이나 상태로 보여줄 것인가를 지정한다.
이를 기준으로 경로 세그먼트와 쿼리 매개변수를 결정하면 다음과 같이 정리할 수 있다.
URL이 가리키는 대상 자체가 달라진다면 경로 세그먼트를 사용한다.
같은 대상을 정렬, 필터링, 검색, 페이지네이션처럼 다른 조건이나 상태로 보여줄 뿐이라면 쿼리 매개변수를 사용한다.
기록 페이지의 탭 UI

기록 페이지는 현재 쿼리 매개변수(/records?sort=top) 방식으로 구현되어 있다. Trending, Top, Latest는 같은 기록 목록을 서로 다른 알고리즘으로 정렬해서 보여주는 상태에 가깝다. 그래서 쿼리 매개변수가 더 자연스럽다고 판단했다.
각 탭을 독립된 페이지나 별도의 자원으로 보기보다는, 같은 데이터 집합 안에서 정렬 기준만 바뀌는 형태에 가깝다고 생각했다. 다만, 벨로그(Velog)와 미디엄(Medium), 그리고 레딧(Reddit)과 같은 커뮤니티 플랫폼 모두가 경로 세그먼트로 구성 되어 있어서 고민을 많이 했다.
아마 콘텐츠가 서비스의 핵심 자산인 플랫폼에서는 이런 정렬 옵션들을 단순한 보기 상태가 아니라, 사실상 별도의 진입점처럼 취급하는 것 같다. 각 정렬 페이지를 SEO 관점에서 독립된 랜딩 페이지로 만들고, 그 페이지에 맞는 광고, 추천, 개인화 기능을 붙일 수 있기 때문이 아닐까 싶다.
확장성을 생각해서 미리 경로 세그먼트로 구성해놓는 게 좋을까?
그럼 Sprinters도 경로 세그먼트로 설계해두는 것이 좋을까? 지금은 꼭 그럴 필요는 없다고 생각했다. 아직 콘텐츠 양이 많지 않고, 각 탭이 별도의 자원 또는 독립된 페이지로서 강한 의미를 가지기보다는 같은 기록 목록의 정렬 상태에 가깝다.
따라서 쿼리 매개변수 방식으로 단순하게 유지하고, 서비스가 더 안정화되거나 사용자가 늘어난 뒤에 필요하다면 경로 세그먼트로 마이그레이션해도 늦지 않다고 생각했다.
예를 들어 나중에 Trending이 시간 기반 점수 계산, 개인화, 토픽별 필터링, 추천 알고리즘 등을 포함하게 된다면 /recordes/trending처럼 경로 세그먼트로 분리해도 좋을 것 같다. 그때는 별도의 자원과 의미를 가진 콘텐츠 피드에 가까워지기 때문이다.
지금은 같은 기록 목록의 정렬 상태로 보고, 쿼리 매개변수 방식을 유지한다.
나중에 각 탭이 독립된 콘텐츠 피드나 SEO 진입점으로 성장한다면 경로 세그먼트로 분리한다.
프로필 페이지 탭 UI

프로필 페이지의 탭은 경로 세그먼트 방식으로 구현되어 있다. 예를 들어 /@:username/about와 같은 URL로 구성했다.
프로필 탭은 각각 다른 자원으로 보는 게 맞다고 생각했다. 기록, 답변, 소개는 한 유저 프로필 아래의 서로 다른 하위 리소스 또는 섹션인 것이다. 탭마다 서로 다른 리소스를 조회하고, 서로 다른 의미의 화면을 구성한다.
판단하기 어려운 경계
Trending, Top, Latest는 같은 기록 목록을 서로 다른 기준으로 정렬해서 보여주는 상태로 볼 수도 있다. 이 관점에서는 쿼리 매개변수가 자연스럽다. 하지만 동시에 이 탭들은 단순히 임시적인 정렬 옵션이라기보다, 서비스 안에서 비교적 안정적으로 유지되는 주요 분류 축이기도 하다. 다중 필터나 동적 조합이 필요한 것도 아니었다. 그렇게 본다면 처음부터 경로 세그먼트로 구현해도 충분히 자연스러웠을 것 같다.
경로 세그먼트로 구현했다면 프로필 탭과 URL 구조의 일관성도 더 좋아졌을 것이다. 다만 아직은 어느 쪽이 명확한 정답이라고 말하기 어렵다. 기록 페이지는 탭은 같은 자원의 정렬 상태로도 볼 수 있고, 서비스에서 의미를 갖는 독립적인 피드로도 볼 수 있다. 그래서 지금은 쿼리 매개변수 방식으로 유지하되, 더 운영해보면서 판단하려고 한다. 실제 사용 경험을 통해 한쪽의 장점이 더 분명해지는 시점이 온다면, 그때 다시 정리해보는 편이 더 좋을 것 같다.