발견 화면의 큐레이션 설계 — 알고리즘 추천과 사람 편집을 어떻게 섞을 것인가

UX 디자인

큐레이션추천 UX발견정보 구조콜드 스타트

이 글은 누구를 위한 것인가

  • 사용자 생성 콘텐츠를 모아 보여주는 둘러보기·탐색 화면을 만드는 팀
  • 추천을 붙였는데 같은 항목만 계속 노출되는 문제를 겪는 경우
  • 편집자가 고른 것과 알고리즘 결과를 한 화면에 놓아야 하는 상황

들어가며

콘텐츠가 쌓이면 목록만으로는 부족해진다. "무엇을 먼저 보여줄 것인가"를 정해야 하고, 답은 대개 둘 중 하나로 기운다.

[사람이 고른다]                [알고리즘이 고른다]

  품질 보장                     규모 확장 가능
  브랜드 톤 유지                개인화 가능
  ─────────────                ─────────────
  확장 불가                     쏠림 (인기 항목만 계속)
  편집자 취향에 갇힘             신규 항목이 영원히 안 뜸
  주말·야간에 멈춤               설명할 수 없음

둘의 약점이 서로의 강점이라서, 실제 답은 항상 혼합이다. 문제는 "몇 대 몇으로 섞을 것인가"가 아니라 **"어느 자리를 누가 채울 것인가"**다.


1. 슬롯 단위로 나눈다

비율이 아니라 자리로 나누는 것이 관리 가능한 설계다.

[발견 화면 구성 예]

  ┌──────────────────────────────┐
  │ 이번 주 추천        [편집]    │  ← 사람이 고정. 주 1회 교체
  ├──────────────────────────────┤
  │ 지금 인기          [알고리즘] │  ← 최근 24시간 반응 기반
  ├──────────────────────────────┤
  │ 새로 올라온 것     [알고리즘] │  ← 신규 보장 슬롯
  ├──────────────────────────────┤
  │ 카테고리별 둘러보기 [구조]    │  ← 고정 진입점
  └──────────────────────────────┘

슬롯마다 규칙이 다르고, 각 슬롯의 실패가 다른 슬롯을 오염시키지 않는다. "70% 알고리즘, 30% 편집"처럼 비율로 섞으면 어느 항목이 왜 거기 있는지 아무도 설명할 수 없게 된다.

[슬롯 설계 시 정할 것]

  · 갱신 주기      편집 슬롯은 주 1회, 인기 슬롯은 시간 단위
  · 슬롯 크기      추천 4~6개, 인기 8~12개
  · 중복 배제      한 항목이 여러 슬롯에 동시에 나오지 않게
  · 폴백           편집 슬롯이 비면 무엇으로 채울 것인가

중복 배제가 자주 빠진다. 인기 항목이 "추천"과 "인기"에 동시에 뜨면 화면의 절반이 같은 것으로 채워진다.

카테고리 축을 함께 제공하는 구조가 흔하다. Linkme의 둘러보기처럼 추천 보드를 상단에 두고 아래에 카테고리별 진입점을 배치하면, 알고리즘이 실패해도 사용자가 자기 관심사로 직접 들어갈 경로가 남는다. 추천에만 의존하는 화면은 추천이 빗나가는 순간 막다른 길이 된다.


2. 신규 항목에 노출 기회를 보장한다

알고리즘 추천의 구조적 문제는 반응이 있어야 노출되고, 노출돼야 반응이 생긴다는 점이다.

  새 항목 → 노출 없음 → 반응 없음 → 점수 낮음 → 영원히 노출 없음

이 고리를 끊으려면 점수와 무관한 노출 슬롯이 필요하다.

[신규 보장 방식]

  ① 전용 슬롯      "새로 올라온 것" — 최신순
  ② 탐색적 삽입    인기 목록의 N번째 자리에 신규를 강제 배치
  ③ 노출 예산      모든 신규 항목에 최소 N회 노출을 보장

②가 실효가 크다. 전용 슬롯만 두면 그 슬롯을 보는 사람만 신규를 보게 되는데, 인기 목록에 섞어 넣으면 훨씬 많은 사람에게 도달한다.

  인기 목록 12칸 중
    1~3번    순수 인기순
    4번      신규 (24시간 이내, 노출 5회 미만)
    5~8번    인기순
    9번      신규
    10~12번  인기순

신규 자리를 눈에 띄는 위치에 두되 전부를 내주지 않는 것이 균형점이다. 1번 자리를 신규에 주면 화면 품질이 흔들리고, 12번에 두면 아무도 안 본다.

노출 기회를 준 뒤에는 성과로 판단해야 한다.

  신규 보장 노출 5회 후
    반응률이 중간값 이상  →  일반 풀로 편입
    그 이하                →  카테고리 목록에만 남김

3. 다양성 — 같은 것만 보이지 않게

점수순 정렬만 하면 상위가 한 종류로 채워진다.

[다양성이 무너지는 전형]

  · 같은 작성자의 항목이 상단에 3개
  · 전부 같은 카테고리
  · 전부 같은 시기에 올라온 것

후처리로 재정렬하는 것이 간단하고 효과적이다.

/** 점수순 목록을 받아 같은 축의 항목이 연속되지 않게 재배치 */
function diversify<T>(items: T[], keyOf: (x: T) => string, maxRun = 1): T[] {
  const out: T[] = [];
  const held: T[] = [];
  const runOf = new Map<string, number>();

  for (const it of items) {
    const k = keyOf(it);
    const recent = out.slice(-maxRun).filter(x => keyOf(x) === k).length;
    if (recent >= maxRun) held.push(it);          // 잠시 보류
    else {
      out.push(it);
      // 보류 목록에서 넣을 수 있게 된 것을 회수
      for (let i = 0; i < held.length; i++) {
        const hk = keyOf(held[i]);
        if (out.slice(-maxRun).filter(x => keyOf(x) === hk).length < maxRun) {
          out.push(held.splice(i, 1)[0]); break;
        }
      }
    }
  }
  return [...out, ...held];
}

"작성자 기준으로 연속 1개까지" 정도만 걸어도 화면 인상이 크게 달라진다. 복잡한 다양성 알고리즘을 도입하기 전에 이 단순한 규칙부터 넣어보는 편이 낫다.

[다양성 축 우선순위]

  1. 작성자    같은 사람 것이 연속되면 가장 티가 난다
  2. 카테고리
  3. 시기      전부 같은 주에 올라온 것

4. 왜 이게 보이는지 알려준다

추천 근거를 한 줄로 붙이면 클릭률과 신뢰도가 함께 오른다.

  ❌ (아무 설명 없음)

  ✅ "이번 주 추천"
     "최근 많이 저장된 페이지"
     "당신이 본 '디자인'과 비슷한"
     "새로 올라왔어요"

Nielsen Norman Group의 추천 가이드라인도 같은 지점을 짚는다. 사용자가 추천의 출처를 이해하면 결과가 마음에 안 들어도 시스템을 탓하지 않고, 다음 추천을 다시 시도한다.

개인화 근거를 표시할 때는 수위 조절이 필요하다.

  ✓ "당신이 본 '디자인' 카테고리와 비슷한"     — 범주 수준
  ✗ "당신이 어제 오후 3시에 본 OO페이지와 비슷한" — 감시받는 느낌

행동 데이터를 얼마나 구체적으로 드러낼지가 신뢰의 분기점이다. 카테고리·주제 수준까지가 무난하고, 개별 항목이나 시각을 언급하면 거부감이 생긴다.

근거 표시가 어려운 슬롯(순수 인기순)이라면 슬롯 제목이 곧 근거가 되도록 이름을 정한다. "추천"보다 "이번 주 많이 본 페이지"가 정보량이 많다.


5. 콜드 스타트 — 처음 온 사용자

개인화 데이터가 없는 사용자에게 개인화 화면을 보여줄 수 없다.

[단계]

  방문 1회차     편집 추천 + 전체 인기 + 카테고리 진입점
  카테고리 클릭 후  그 카테고리 중심으로 재구성
  3~5회 방문 후   행동 기반 개인화 시작

첫 화면에서 관심사를 묻는 온보딩은 신중해야 한다.

  관심사 선택 화면의 트레이드오프

  얻는 것:  즉시 개인화 가능
  잃는 것:  콘텐츠를 보기 전에 이탈하는 사용자

넣는다면 건너뛸 수 있게 하고, 화면 하나를 넘기지 않는다. 그리고 선택을 강요하지 말고 "나중에 설정에서 바꿀 수 있어요"를 명시한다.

더 나은 방법은 첫 클릭을 관심사 신호로 쓰는 것이다. 사용자가 카테고리를 하나 누르면 그것이 곧 선택이고, 별도 온보딩 없이 개인화가 시작된다.


6. 편집 슬롯의 운영 부담을 줄인다

편집 슬롯은 사람이 채워야 하므로, 운영 부담이 곧 지속 가능성이다.

[부담을 줄이는 장치]

  · 후보 목록 자동 생성    알고리즘이 20개를 추리고 편집자가 5개를 고름
  · 예약 발행             주말·연휴분을 미리 채워둠
  · 만료 자동 처리         추천 기간이 지나면 자동으로 내림
  · 폴백 자동 전환         비어 있으면 알고리즘 결과로 대체

폴백이 없으면 편집자가 휴가를 간 주에 화면이 빈다. 이건 반드시 일어나는 일이므로 처음부터 설계에 넣어야 한다.

  편집 슬롯 상태
    채워짐        편집 항목 표시
    만료됨/비어있음  → "이번 주 인기" 로 슬롯 제목까지 교체

슬롯 제목까지 바꾸는 것이 중요하다. "이번 주 추천"이라는 제목 아래 알고리즘 결과가 들어가면 근거 표시가 거짓이 된다 — 닐슨의 사용성 휴리스틱에서 말하는 "시스템 상태의 가시성"이 깨지는 지점이다.


7. 접근성과 성능

발견 화면은 카드가 많고 이미지가 무겁다.

[체크리스트]

  □ 카드 전체를 링크로 만들되 <a> 안에 <a> 중첩 금지
  □ 이미지에 width/height 지정 (CLS 방지)
  □ 첫 화면 밖 이미지는 loading="lazy"
  □ 슬롯 제목을 <h2>로 — 스크린리더 탐색 가능하게
  □ "더 보기"는 <button>, 페이지 이동은 <a>
  □ 카드 순서 = DOM 순서 (CSS로 재배치하지 않기)

이미지 지연 로딩은 브라우저 내장 loading="lazy"로 충분한 경우가 대부분이다. 직접 IntersectionObserver를 붙이기 전에 이것부터 확인한다.

카드 안에 링크가 여러 개 필요한 경우(작성자 프로필, 카테고리 태그) 중첩 대신 겹침 기법을 쓴다.

.card { position: relative; }
.card-title-link::after {          /* 카드 전체를 덮는 클릭 영역 */
  content: '';
  position: absolute;
  inset: 0;
}
.card-author-link { position: relative; z-index: 1; }  /* 위로 올려 클릭 가능 */

첫 화면 로딩은 Core Web Vitals의 LCP 기준으로 관리한다. 발견 화면은 대개 큰 이미지 카드가 LCP 요소가 되므로, 첫 카드 이미지에만 priority를 주고 나머지는 지연 로드하는 것이 기본이다.


8. 측정 — 클릭률만 보면 쏠린다

[클릭률만 최적화했을 때]

  자극적인 썸네일이 이김
  → 그런 콘텐츠가 더 노출됨
  → 화면 전체의 톤이 바뀜
  → 되돌리기 어려움

보조 지표를 함께 봐야 한다.

  · 클릭 후 체류 시간        클릭만 유도하고 실망시킨 경우 짧다
  · 저장·공유 비율           진짜 가치의 신호
  · 발견 화면 재방문율
  · 신규 항목 노출 점유율    쏠림 감시
  · 상위 10% 항목의 노출 점유율   ← 집중도 지표

마지막 지표가 쏠림을 직접 보여준다. 상위 10%가 전체 노출의 80%를 가져가고 있다면, 신규 보장 슬롯이 작동하지 않는 것이다.


9. 정리

  1. 비율이 아니라 슬롯으로 나눈다 (슬롯마다 규칙과 갱신 주기)
  2. 슬롯 간 중복 배제를 반드시 넣는다
  3. 신규는 전용 슬롯보다 인기 목록 중간 삽입이 효과적
  4. 작성자 기준 연속 1개 제한만으로 다양성이 크게 개선된다
  5. 추천 근거를 한 줄로, 단 범주 수준까지만
  6. 콜드 스타트는 첫 클릭을 신호로 (온보딩은 건너뛰기 가능하게)
  7. 편집 슬롯에는 폴백과 제목 교체를 함께
  8. 클릭률 대신 체류·저장·노출 집중도를 함께 본다

가장 자주 빠지는 건 7번의 폴백이다. 편집 슬롯이 비는 상황은 반드시 오고, 그때 화면이 깨지면 사용자는 서비스가 관리되지 않는다고 판단한다.