Skip to content

오른쪽 Sidebar 에 광고판 추가 #1004

Description

@jk-kim0

목적

corp-web-app의 Site Notice / Floating Spotlight Card 구현을 기준으로, QueryPie Docs의 오른쪽 Sidebar 영역에 Spotlight 광고판을 추가한다.

현재 요청의 원문 범위는 “홈페이지의 Spotlight 광고판을 오른쪽 Sidebar에 동일하게 노출하기”다. 구현 시에는 corp-web-app의 floating card UX와 visibility/analytics 계약을 참고하되, querypie-docs의 Nextra right TOC/sidebar 구조에 맞게 sidebar-embedded card로 적용한다.

참조

  • 기준 이슈: querypie/corp-web-app#1044
  • corp-web-app 확인 기준: origin/main 7320add2f248948d5e6d69d9f909806e44f57d55
  • 주요 참조 구현:
    • src/components/sections/floating-spotlight-card/floating-spotlight-card.component.tsx
    • src/components/sections/floating-spotlight-card/floating-spotlight-card-position.ts
    • src/components/sections/floating-spotlight-card/floating-spotlight-card.module.css
    • src/lib/resources/site-notice.ts
    • src/lib/resources/site-notice-spotlight-storage.ts
    • src/utils/site-notice-utm.ts
    • src/utils/site-notice-analytics.ts
  • querypie-docs의 예상 integration point:
    • src/app/[lang]/layout.tsx의 Nextra Layout toc.extraContent
    • 기존 ConfluenceSourceLink는 유지하고, Spotlight Card를 같은 right sidebar 영역에 함께 배치한다.

범위

In scope

  • 오른쪽 Sidebar / TOC 영역에 Spotlight Card 추가
  • locale별 Spotlight 광고판 데이터 모델과 active item filtering
  • 데스크톱 노출, 모바일 숨김 처리
  • carousel rotation, 이전/다음 controls, indicator, hover/focus pause
  • CTA click 및 dismiss 동작
  • browser-local 30일 visibility suppression
  • owned URL UTM 부여 및 GA event 전송
  • 관련 unit/component/integration 수준 검증

Out of scope

  • corp-web-app 코드 변경
  • querypie-docs의 left navigation sidebar 구조 변경
  • 문서 본문 MDX 구조 변경
  • sitemap, canonical, redirect, Nextra pageMap 변경
  • 모바일 Top Announcement Bar 추가
  • local dev server 기반 시각 검증을 필수 완료 조건으로 삼는 것

기능 명세

1. 배치

  • Spotlight Card는 Nextra 오른쪽 TOC/sidebar 영역에 렌더링한다.
  • src/app/[lang]/layout.tsxtoc.extraContent를 확장해 기존 ConfluenceSourceLink와 함께 노출한다.
  • 카드가 렌더되지 않는 상태에서도 right sidebar와 문서 본문 레이아웃에 빈 placeholder를 남기지 않는다.
  • 카드 추가로 On This Page, 언어 선택기, Confluence source link의 접근성과 클릭 영역이 깨지면 안 된다.
  • 오른쪽 Sidebar가 없는 viewport 또는 Nextra가 TOC/sidebar를 숨기는 viewport에서는 카드도 숨긴다.

2. 데이터 모델

Spotlight 데이터는 locale별로 관리한다. 구현 위치는 PR에서 확정하되, 다음 field를 표현할 수 있어야 한다.

type DocsSpotlightContent = {
  ariaLabel: string;
  spotlightLabel: string;
  spotlightCtaLabel: string;
  spotlightDismissLabel: string;
  nextLabel: string;
  previousLabel: string;
  items: DocsSpotlightItem[];
};

type DocsSpotlightItem = {
  id: string;
  date: string; // YYYY-MM-DD
  href: string;
  imageSrc: string;
  imageAlt: string;
  title: string;
  spotlightMeta: string;
  visibleUntil: string; // YYYY-MM-DD, inclusive
};
  • id는 campaign을 식별하는 구체적인 kebab-case semantic key를 사용한다.
  • datevisibleUntilYYYY-MM-DD 형식이어야 한다.
  • visibleUntil은 inclusive active filter로 동작한다.
  • active item이 없으면 Spotlight Card를 렌더하지 않는다.
  • 같은 campaign은 locale별로 같은 id를 사용한다.
  • 특정 locale에만 노출해야 하는 campaign은 허용하되, 데이터 검증에서 의도적인 locale-specific item임을 식별할 수 있어야 한다.
  • href는 canonical destination만 저장하고, UTM은 render 시점에 부여한다.
  • imageSrc는 repo-local public asset path를 우선 사용한다.

3. Active item ordering

  • active item은 visibleUntil >= today인 item이다.
  • 날짜 기준은 Seoul business day 기준으로 계산한다.
  • 여러 active item이 있으면 최신 date item을 우선 노출한다.
  • 나머지 item은 재방문 피로도를 낮추기 위해 shuffle하거나, 테스트 가능한 deterministic random injection을 제공한다.
  • 모든 active item이 visibility suppression 대상이면 카드 전체를 렌더하지 않는다.

4. Card UI

  • card는 오른쪽 Sidebar 폭 안에 맞는 compact card로 렌더링한다.
  • corp-web-app Floating Spotlight Card와 동일한 정보 구조를 따른다.
    • eyebrow / label
    • dismiss button
    • image
    • title
    • meta/date
    • CTA
    • indicator
    • previous / next controls
  • title은 최대 2줄로 clamp하고, 긴 텍스트가 sidebar 밖으로 overflow되면 안 된다.
  • image는 고정된 frame 안에서 object-fit: cover로 표시한다.
  • CTA는 card link와 동일 목적지를 가리키며, keyboard focus 상태가 명확해야 한다.
  • dismiss button은 accessible name을 가져야 한다.
  • previous / next buttons는 locale별 previousLabel, nextLabel을 accessible name으로 사용한다.
  • indicator는 현재 active item을 시각적으로 표시한다.

5. Responsive behavior

  • card는 desktop/right-sidebar viewport에서만 보인다.
  • corp-web-app 기준과 동일하게 max-width: 1023px 이하에서는 숨기는 것을 기본값으로 한다.
  • 모바일에서는 별도 Top Announcement Bar를 만들지 않는다.
  • browser-local visibility state 확인 전에는 initial flash를 만들지 않도록 card body를 렌더하지 않는다.

6. Carousel behavior

  • item이 2개 이상이면 4초 interval로 다음 item으로 rotation한다.
  • hover 또는 keyboard focus가 card 내부에 있으면 rotation을 pause한다.
  • focus가 card 밖으로 이동하거나 hover가 끝나면 rotation을 resume한다.
  • prefers-reduced-motion: reduce 환경에서는 auto rotation을 실행하지 않는다.
  • previous / next controls는 suppression 이후 남은 renderable item 기준으로 순환한다.
  • view event는 같은 page lifecycle에서 item별 1회만 전송한다.

7. Visibility suppression

corp-web-app의 Spotlight visibility gate를 docs용으로 가져온다.

  • browser localStorage에 versioned namespace key를 사용한다.
  • 권장 key: querypie:docs:spotlight:v1
  • storage value는 item id를 key로 하는 object map이다.
type DocsSpotlightVisibilityRecord = {
  disposition: 'viewed' | 'dismissed';
  updatedAt: string; // ISO timestamp
  expiresAt: string; // ISO timestamp
};
  • CTA click은 active item에 disposition: 'viewed' record를 저장한다.
  • dismiss click은 active item에 disposition: 'dismissed' record를 저장하고 현재 card instance를 숨긴다.
  • record TTL은 저장 시점 기준 30일이다.
  • unexpired viewed 또는 dismissed record가 있는 item은 card render/rotation 대상에서 제외한다.
  • expired record는 suppression하지 않고, read 또는 write 시점에 prune할 수 있다.
  • localStorage read, parse, schema validation, write 실패는 recoverable하게 처리한다.
  • storage 실패가 CTA navigation, dismiss UI state, page rendering을 막으면 안 된다.

8. URL tracking

  • owned QueryPie URL에만 UTM을 append한다.
  • external URL은 원본 href를 유지한다.
  • relative URL은 docs/current locale route semantics를 유지해야 한다.
  • 권장 UTM taxonomy:
    • utm_source=qp
    • utm_medium=notice
    • utm_campaign=<spotlight-item-id>
    • utm_content=docs_sidebar_card
    • utm_id=sn_<spotlight-item-id>
  • 기존 query string과 hash가 있으면 보존한다.

9. Analytics

production에서 gtag가 있으면 corp-web-app과 호환되는 promotion event 계열을 전송한다.

  • view: view_promotion
  • click: select_promotion
  • dismiss: site_notice_dismiss

공통 parameter는 다음 값을 포함한다.

  • promotion_id: sn_<spotlight-item-id>
  • promotion_name: <spotlight-item-id>
  • creative_slot: docs_sidebar_card
  • creative_name: <title>
  • spotlight_id: <spotlight-item-id>
  • spotlight_surface: docs_sidebar_card
  • spotlight_title: <title>
  • spotlight_destination: <canonical href>

Analytics failure는 navigation, dismiss, visibility persistence, rendering을 막으면 안 된다.

10. 접근성

  • Card container는 aside 또는 equivalent landmark semantics를 사용하고 locale별 ariaLabel을 제공한다.
  • Carousel active content 변경은 과도한 screen reader announcement를 만들지 않아야 한다.
  • 비활성 carousel item이 DOM에 남는 구현이라면 keyboard tab 순서에서 제외한다.
  • Dismiss, previous, next controls는 keyboard로 조작 가능해야 한다.
  • Focus-visible outline을 제공한다.

Acceptance criteria

  • 오른쪽 Sidebar/TOC가 보이는 desktop viewport에서 Spotlight Card가 노출된다.
  • 오른쪽 Sidebar/TOC가 숨겨지는 viewport에서는 Spotlight Card도 숨겨진다.
  • active item이 없거나 모든 active item이 unexpired visibility record로 suppressed되면 아무것도 렌더하지 않는다.
  • CTA click은 UTM이 붙은 destination으로 이동하고, 해당 item의 viewed record를 30일 TTL로 저장한다.
  • dismiss click은 dismissed record를 30일 TTL로 저장하고 현재 card를 숨긴다.
  • localStorage가 unavailable 또는 corrupt 상태여도 page rendering은 실패하지 않는다.
  • hover/focus pause, previous/next controls, reduced-motion 조건이 동작한다.
  • existing ConfluenceSourceLink, language selector, TOC title 영역은 회귀하지 않는다.
  • GA event가 가능한 환경에서는 view/click/dismiss event가 전송되고, 불가능한 환경에서는 조용히 no-op 처리된다.

검증 계획

  • Data loader/schema unit test
    • 필수 field, date format, image path, active filtering, inclusive visibleUntil, no active item case
  • Visibility storage unit test
    • read/write, TTL, expired pruning, corrupt payload recovery, storage exception recovery
  • URL tracking unit test
    • owned URL UTM append, external URL no-op, existing query/hash 보존
  • Component test
    • desktop render, mobile hidden, initial flash 방지, carousel rotation, hover/focus pause, reduced-motion no auto rotation, previous/next, CTA click, dismiss
  • Layout integration test 또는 source-level assertion
    • toc.extraContent에 Spotlight Card와 기존 ConfluenceSourceLink가 함께 남아 있는지 확인
  • 문서/issue 명세만 갱신하는 현재 작업은 local build 대상이 아니며, 구현 PR에서 위 검증을 수행한다.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions