Ticker UI 디자인: UI/UX/AX의 원칙과 실전

“움직이는 정보는 왜 읽기 어려울까?”

주식 티커를 만들면서 고민이 생겼습니다. 데스크톱에서는 완벽하게 보이는데, 과연 사용자들이 제대로 읽을 수 있을까?

UX 연구 자료를 찾아보니 예상대로였습니다. Nielsen Norman Group의 연구에 따르면, 움직이는 콘텐츠는 사용자의 주의를 99%로 끌어당기지만, 정작 사용자는 그 내용을 제대로 읽지 못하는 경우가 많습니다.

더 큰 문제는 접근성이었습니다. WCAG 커뮤니티와 스크린 리더 사용자들의 피드백을 보니:

“티커가 너무 빨라서 읽기 전에 지나가버립니다.”

“멈출 수 없어서 답답합니다.”

“스크린 리더가 계속 주식 정보를 읽어서 다른 내용을 들을 수 없습니다.”

그리고 최근에는 새로운 고민이 하나 더 생겼습니다. AI가 웹을 크롤링할 때 티커를 제대로 이해할 수 있을까? LLM이나 검색 엔진은 끊임없이 변하는 티커를 어떻게 처리할까요?

아름답게 디자인된 티커가 오히려 사용자 경험, 접근성, 그리고 AI 경험을 모두 해칠 수 있습니다.

이 글에서는 Ticker UI를 UI(시각 디자인), UX(사용자 경험), AX(접근성 + AI UX)의 관점에서 근본적으로 이해하고, 올바르게 디자인하는 방법을 알아봅니다.


원론: Ticker UI는 왜 존재하는가?

물리적 제약의 산물

Ticker UI는 원래 물리적 공간의 제약 때문에 탄생했습니다.

전광판의 문제:
┌─────────────────────────────┐
│ 제한된 공간 (가로 3m)        │
│                             │
│ 보여줄 정보:                │
│ - 100개 주식 정보           │
│ - 환율 정보                 │
│ - 뉴스 헤드라인             │
└─────────────────────────────┘

해결책:
정보를 "스크롤"시켜서 제한된 공간에
더 많은 정보를 보여주자!

뉴욕 증권거래소의 티커 테이프(1867년)가 시작이었습니다. 전신기로 전송된 주식 정보를 좁은 종이 테이프에 인쇄하며 계속 흘러가는 형태였죠.

웹에서의 재발견

그런데 웹에는 공간 제약이 거의 없습니다. 스크롤할 수 있고, 페이지를 나눌 수 있고, 아코디언을 쓸 수 있습니다.

그럼에도 Ticker UI가 웹에서 사용되는 이유는 무엇일까요?

1. 시각적 계층(Visual Hierarchy):

┌──────────────────────────────────┐
│ [메인 콘텐츠]                     │ ← 사용자의 주요 관심사
│                                  │
│ 큰 제목과 중요한 내용            │
│                                  │
├──────────────────────────────────┤
│ ← 코스피 2,500 | 달러 1,430 →   │ ← 보조 정보 (주변 시야)
└──────────────────────────────────┘

티커는 “중요하지만 주의를 끌면 안 되는” 정보를 전달합니다. 메인 콘텐츠를 방해하지 않으면서도, 사용자의 주변 시야(peripheral vision)에 정보를 제공합니다.

2. 실시간성 표현:

움직임은 “지금 이 순간”을 시각적으로 전달합니다. 정적인 숫자보다 스크롤되는 숫자가 “살아있는 데이터”라는 느낌을 줍니다.

3. 공간 효율:

좁은 헤더나 푸터에 여러 정보를 담을 수 있습니다.

❌ 정적 표시 (공간 부족):
코스피 2,500 | 달러 1,430 | 유로 1,520 | 금 2,100 | [잘림...]

✅ 티커 (모든 정보 표시):
← 코스피 2,500 | 달러 1,430 | 유로 1,520 | 금 2,100 | 비트코인 88,000 →

Ticker UI의 4가지 관점: UI, UX, AX (Human + AI)

Ticker를 제대로 디자인하려면 네 가지 관점을 모두 고려해야 합니다.

1. UI (User Interface): 시각 디자인

“어떻게 보일 것인가?”

가독성(Readability)

움직이는 텍스트는 정적인 텍스트보다 읽기 어렵습니다.

/* ❌ 잘못된 예: 작고 빠른 티커 */
.ticker {
    font-size: 12px;            /* 너무 작음 */
    animation: scroll 10s;       /* 너무 빠름 */
    font-weight: 300;            /* 얇은 폰트 */
}

/* ✅ 올바른 예: 크고 느린 티커 */
.ticker {
    font-size: 16px;             /* 충분히 큼 */
    animation: scroll 40s;        /* 여유 있는 속도 */
    font-weight: 500;             /* 적절한 두께 */
    letter-spacing: 0.02em;       /* 자간 확보 */
}

권장 사항:

  • 최소 폰트 크기: 16px (모바일), 14px (데스크톱)
  • 속도: 50-100px/초 (1초에 약 3-5글자)
  • 명암 대비: 4.5:1 이상 (WCAG AA)
  • 폰트: 산세리프, Medium(500) 이상

시각적 계층

티커는 보조 정보이므로, 시각적으로 덜 두드러져야 합니다.

시각적 무게 (Visual Weight):

높음  ████████  메인 헤딩 (h1, 32px, Bold)
     ██████    부제목 (h2, 24px, Semibold)
     ████      본문 (16px, Regular)
낮음  ██        티커 (14px, Medium, 회색 배경)

Good Practice:

<header>
    <h1 class="main-title">주식 시장 분석</h1>
    <p class="subtitle">2025년 10월 29일</p>

    <!-- 티커는 시각적으로 덜 두드러지게 -->
    <div class="ticker" role="marquee" aria-label="실시간 주식 정보">
        <div class="ticker-content">
            코스피 2,500.22 | 달러 1,430.80 | 유로 1,668.03
        </div>
    </div>
</header>

<style>
.main-title {
    font-size: 32px;
    font-weight: 700;
    color: #000;
}

.ticker {
    font-size: 14px;
    font-weight: 500;
    color: #555;                    /* 덜 두드러지는 색상 */
    background: #f5f5f5;            /* 부드러운 배경 */
    padding: 8px 0;
}
</style>

색상과 대비

WCAG 2.1 SC 1.4.3 (Contrast Minimum):

  • 일반 텍스트: 4.5:1 이상
  • 큰 텍스트 (18px+): 3:1 이상
❌ 나쁜 예: 색상만으로 정보 전달
<span style="color: red;">-2.5%</span>
<span style="color: green;">+3.2%</span>

✅ 좋은 예: 아이콘 + 색상 + aria-label
<span class="change negative" aria-label="하락 2.5%">
    <svg class="icon-down" aria-hidden="true"></svg>
    <span>-2.5%</span>
</span>

<span class="change positive" aria-label="상승 3.2%">
    <svg class="icon-up" aria-hidden="true"></svg>
    <span>+3.2%</span>
</span>

<style>
.change {
    font-weight: 600;
}

.negative {
    color: #c62828;              /* 4.5:1 대비 */
}

.positive {
    color: #2e7d32;              /* 4.5:1 대비 */
}

.icon-down,
.icon-up {
    display: inline-block;
    width: 12px;
    margin-right: 4px;
}
</style>

2. UX (User Experience): 사용자 경험

“사용자는 어떻게 느낄 것인가?”

인지 부하(Cognitive Load)

움직이는 콘텐츠는 자동으로 주의를 끕니다. 이것은 좋은 것일 수도, 나쁜 것일 수도 있습니다.

인간의 시각 시스템:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[움직임 감지]
    ↓
[무의식적 주의 전환]  ← 의지와 무관!
    ↓
[집중 방해]

Jakob Nielsen의 연구 (2010):

“움직이는 콘텐츠는 사용자의 주의를 99%로 끌어당깁니다. 사용자는 움직임을 무시하려 해도 무시할 수 없습니다.”

해결책: 신중한 배치

✅ 좋은 위치: 주변 시야
┌───────────────────────────────────┐
│ ← Ticker (상단)                   │ ← OK: 주변 시야
├───────────────────────────────────┤
│                                   │
│    [메인 콘텐츠]                   │ ← 사용자 집중
│                                   │
│                                   │
├───────────────────────────────────┤
│ ← Ticker (하단)                   │ ← OK: 주변 시야
└───────────────────────────────────┘

❌ 나쁜 위치: 중앙 또는 메인 콘텐츠 옆
┌───────────────────────────────────┐
│    [메인 콘텐츠]                   │
│                                   │
├───────────────────────────────────┤
│ ← Ticker (중앙)                   │ ← ❌ 집중 방해!
├───────────────────────────────────┤
│    [메인 콘텐츠 계속]              │
└───────────────────────────────────┘

읽기 속도 vs 스크롤 속도

문제:

  • 빠른 독자: 티커를 다 읽고 다음을 기다림 (지루함)
  • 느린 독자: 티커가 읽기 전에 지나감 (좌절감)

Nielsen Norman Group 연구: 평균 읽기 속도는 200-250 단어/분 = 약 3-4 단어/초

// ❌ 너무 빠른 티커
const tickerSpeed = 150; // 150px/sec
// "코스피 2,500.22" (약 70px)가 0.5초 만에 지나감
// → 읽을 시간 없음!

// ✅ 적절한 속도
const tickerSpeed = 50; // 50px/sec
// "코스피 2,500.22" (약 70px)가 1.4초 동안 보임
// → 읽을 시간 충분

Best Practice: 사용자 제어권 제공

<div class="ticker-container">
    <!-- 일시 정지 버튼 -->
    <button
        id="ticker-pause"
        aria-label="티커 일시 정지"
        aria-pressed="false"
    ></button>

    <div
        class="ticker"
        role="marquee"
        aria-label="실시간 주식 정보"
    >
        <div class="ticker-content">
            코스피 2,500.22 | 달러 1,430.80
        </div>
    </div>
</div>

<style>
/* 호버 시 자동 일시 정지 */
.ticker-container:hover .ticker-content,
.ticker-container:focus-within .ticker-content {
    animation-play-state: paused;
}

.ticker-content {
    animation: scroll 40s linear infinite;
}

@keyframes scroll {
    from { transform: translateX(100%); }
    to { transform: translateX(-100%); }
}
</style>

<script>
const pauseBtn = document.getElementById('ticker-pause');
const ticker = document.querySelector('.ticker-content');

pauseBtn.addEventListener('click', () => {
    const isPaused = pauseBtn.getAttribute('aria-pressed') === 'true';

    if (isPaused) {
        ticker.style.animationPlayState = 'running';
        pauseBtn.setAttribute('aria-pressed', 'false');
        pauseBtn.textContent = '';
    } else {
        ticker.style.animationPlayState = 'paused';
        pauseBtn.setAttribute('aria-pressed', 'true');
        pauseBtn.textContent = '';
    }
});
</script>

정보의 완전성

문제:

사용자가 티커를 보기 시작한 시점:
    ↓
← 달러 1,430 | 유로 1,668 | 금 2,100 | 코스피 2,500 →
              ↑
         사용자는 처음을 놓침!

해결책 1: 무한 반복

// 티커 콘텐츠를 복제하여 끊김 없이 반복
const tickerContent = document.querySelector('.ticker-content');
const clone = tickerContent.cloneNode(true);
clone.setAttribute('aria-hidden', 'true'); // 중복 읽기 방지
tickerContent.parentNode.appendChild(clone);

해결책 2: 대체 정보 제공

<!-- 티커 외에 완전한 정보를 제공 -->
<div class="ticker-wrapper">
    <div class="ticker" role="marquee" aria-label="실시간 주식 정보">
        <!-- 스크롤되는 티커 -->
    </div>

    <!-- 전체 정보를 볼 수 있는 링크 -->
    <a href="/markets/live" class="view-all">
        전체 시장 정보 보기
    </a>
</div>

3. AX (Accessibility): 접근성

“모든 사용자가 접근할 수 있는가?”

WCAG 2.2.2: Pause, Stop, Hide (Level A)

공식 요구사항:

움직이거나, 깜박이거나, 스크롤되는 정보가:

  1. 자동으로 시작되고
  2. 5초 이상 지속되며
  3. 다른 콘텐츠와 함께 표시되면

사용자가 일시 정지, 중지, 숨길 수 있어야 합니다.

예외: 움직임이 필수적인(essential) 활동인 경우만 제외

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

❌ WCAG 위반: 제어 불가능한 티커
<div class="ticker">
    <div class="auto-scroll">
        <!-- 계속 스크롤, 멈출 수 없음 -->
    </div>
</div>

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

✅ WCAG 준수: 호버 시 일시 정지
<div class="ticker">
    <div class="auto-scroll">
        <!-- 호버/포커스 시 자동 일시 정지 -->
    </div>
</div>

<style>
.ticker:hover .auto-scroll,
.ticker:focus-within .auto-scroll {
    animation-play-state: paused;
}
</style>

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

✅ WCAG 준수: 명시적 제어 버튼
<button aria-label="티커 일시 정지" aria-pressed="false"></button>

스크린 리더 고려사항

role="marquee"의 기본 동작:

  • aria-live="off" (기본값)
  • 스크린 리더가 자동으로 읽지 않음
<!-- ✅ 올바른 사용: 비필수 정보 -->
<div
    role="marquee"
    aria-label="실시간 주식 정보"
    aria-live="off"
>
    <!-- 티커 콘텐츠 -->
</div>

스크린 리더 동작:
1. 사용자가 요소에 도달하면: "실시간 주식 정보 영역"
2. 자동 업데이트: 읽지 않음 (조용함)
3. 사용자가 원하면: 탐색하여 읽을 수 있음

중요한 정보는 marquee 사용 금지:

❌ 잘못된 예: 중요한 알림에 marquee
<div role="marquee" aria-label="시스템 알림">
    서버 점검이 10분 후 시작됩니다
</div>
→ 스크린 리더가 읽지 않아 사용자가 놓칠 수 있음!

✅ 올바른 예: alert 사용
<div role="alert" aria-live="assertive">
    서버 점검이 10분 후 시작됩니다
</div>
→ 즉시 읽어줌

prefers-reduced-motion 지원

WCAG 2.3.3 (Level AAA): Animation from Interactions

일부 사용자는 모션으로 인해 현기증, 메스꺼움, 두통을 경험합니다.

/* 기본: 애니메이션 있음 */
.ticker-content {
    animation: scroll 40s linear infinite;
}

/* 사용자가 모션 감소를 선호하면: 애니메이션 없음 */
@media (prefers-reduced-motion: reduce) {
    .ticker-content {
        animation: none !important;

        /* 대신 스크롤 가능하게 */
        overflow-x: auto;
        scroll-behavior: smooth;
    }
}

테스트 방법:

# macOS
System Settings > Accessibility > Display > Reduce motion

# Windows
Settings > Ease of Access > Display > Show animations

# CSS에서 확인
@media (prefers-reduced-motion: reduce) {
    /* 이 블록이 적용되는지 확인 */
}

키보드 접근성

<!-- ❌ 키보드로 접근 불가 -->
<div class="ticker">
    <div class="ticker-item">코스피 2,500</div>
    <div class="ticker-item">달러 1,430</div>
</div>

<!-- ✅ 키보드로 탐색 가능 -->
<div class="ticker" role="marquee" aria-label="실시간 주식 정보">
    <a href="/stock/kospi" class="ticker-item" tabindex="0">
        코스피 2,500 <span class="sr-only">자세히 보기</span>
    </a>
    <a href="/currency/usd" class="ticker-item" tabindex="0">
        달러 1,430 <span class="sr-only">자세히 보기</span>
    </a>
</div>

<style>
/* 포커스 시 애니메이션 일시 정지 */
.ticker:focus-within .ticker-content {
    animation-play-state: paused;
}

/* 포커스 표시 */
.ticker-item:focus {
    outline: 2px solid #0066cc;
    outline-offset: 2px;
}

/* 스크린 리더 전용 텍스트 */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}
</style>

4. AI UX: AI와 크롤러 경험

“AI는 티커를 어떻게 이해하는가?”

웹은 더 이상 사람만 보는 것이 아닙니다. 검색 엔진, LLM, 데이터 스크래퍼 등 AI 에이전트들도 여러분의 웹페이지를 읽습니다.

검색 엔진과 SEO

문제:

<!-- Googlebot이 페이지를 크롤링할 때 -->
<div class="ticker" role="marquee">
    <div class="ticker-content">
        코스피 2,500 | 달러 1,430 | 유로 1,668
    </div>
</div>

Googlebot의 시점:
- JavaScript 실행 전: 빈 티커 또는 초기 상태만 봄
- 애니메이션: 무시
- 동적으로 업데이트되는 값: 놓침
- 결과: 불완전한 정보 인덱싱

해결책: 구조화된 데이터 제공

<!-- 1. 시맨틱 HTML로 정적 버전 제공 -->
<div class="ticker-wrapper">
    <!-- AI와 검색 엔진용 정적 콘텐츠 -->
    <div class="ticker-static" aria-hidden="true">
        <ul itemscope itemtype="https://schema.org/ItemList">
            <li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
                <meta itemprop="position" content="1">
                <span itemprop="name">코스피</span>
                <span itemprop="value">2,500.22</span>
            </li>
            <li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
                <meta itemprop="position" content="2">
                <span itemprop="name">달러</span>
                <span itemprop="value">1,430.80</span>
            </li>
        </ul>
    </div>

    <!-- 사람용 동적 티커 -->
    <div class="ticker" role="marquee" aria-label="실시간 시장 정보">
        <div class="ticker-content">
            코스피 2,500.22 | 달러 1,430.80
        </div>
    </div>
</div>

<style>
/* 정적 콘텐츠는 화면에서 숨김 (검색 엔진은 읽음) */
.ticker-static {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}
</style>

JSON-LD로 구조화:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "ItemList",
  "name": "실시간 주식 시장 정보",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "name": "코스피",
      "item": {
        "@type": "FinancialProduct",
        "name": "KOSPI",
        "price": 2500.22,
        "priceCurrency": "KRW"
      }
    },
    {
      "@type": "ListItem",
      "position": 2,
      "name": "달러",
      "item": {
        "@type": "ExchangeRateSpecification",
        "currency": "USD",
        "currentExchangeRate": 1430.80
      }
    }
  ]
}
</script>

LLM과 웹 크롤링

ChatGPT, Claude 등이 웹을 브라우징할 때:

AI의 관점:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

1. HTML 구조 파싱
   └─ <div role="marquee"> 발견
       └─ "이건 비필수 정보구나" (role="marquee"의 의미)

2. 텍스트 추출
   └─ 티커의 현재 상태만 캡처
       └─ 애니메이션이나 실시간 업데이트는 놓침

3. 컨텍스트 이해
   └─ aria-label을 읽어 "실시간 주식 정보"임을 파악
       └─ 하지만 전체 데이터는 보지 못함

결과: 불완전한 정보 제공

Best Practice:

<!-- 1. 명확한 메타데이터 -->
<meta name="description" content="실시간 주식 시장 정보: 코스피, 달러, 유로 환율">

<!-- 2. 의미 있는 aria-label -->
<div
    class="ticker"
    role="marquee"
    aria-label="실시간 주식 정보: 코스피 2,500, 달러 1,430, 유로 1,668"
>
    <!-- 티커 콘텐츠 -->
</div>

<!-- 3. 대체 콘텐츠 링크 -->
<a href="/markets/live-data" class="ticker-alt">
    전체 시장 데이터 보기 (JSON/CSV 다운로드 가능)
</a>

데이터 스크래핑 고려사항

많은 서비스들이 웹페이지를 스크래핑해서 데이터를 수집합니다.

문제:

// 스크래퍼가 티커를 크롤링할 때
const tickerText = document.querySelector('.ticker').textContent;
// → "코스피 2,500달러 1,430유로 1,668"
// 구분자 없음! 파싱 불가능

해결책: 데이터 속성 활용

<div class="ticker" role="marquee" aria-label="실시간 시장 정보">
    <div class="ticker-content">
        <span
            class="ticker-item"
            data-market="KOSPI"
            data-value="2500.22"
            data-change="+0.45"
        >
            코스피 2,500.22 ▲ +0.45%
        </span>

        <span
            class="ticker-item"
            data-currency="USD"
            data-rate="1430.80"
            data-change="-0.30"
        >
            달러 1,430.80 ▼ -0.30%
        </span>
    </div>
</div>

<!-- AI가 쉽게 파싱 가능 -->
<script>
// 스크래퍼가 이렇게 읽을 수 있음
const items = document.querySelectorAll('.ticker-item');
const data = Array.from(items).map(item => ({
    market: item.dataset.market || item.dataset.currency,
    value: parseFloat(item.dataset.value || item.dataset.rate),
    change: parseFloat(item.dataset.change)
}));
// 결과: 구조화된 데이터!
</script>

API 엔드포인트 제공

최선의 방법: 별도의 API 제공

<!-- 웹페이지에 API 링크 명시 -->
<div class="ticker-wrapper">
    <div class="ticker" role="marquee">
        <!-- 티커 UI -->
    </div>

    <div class="ticker-api-info">
        <p>
            이 데이터는
            <a href="/api/markets/live" rel="alternate" type="application/json">
                JSON API
            </a>
            로도 제공됩니다.
        </p>
    </div>
</div>

<!-- Link 헤더로 명시 -->
<link
    rel="alternate"
    type="application/json"
    href="/api/markets/live"
    title="실시간 시장 데이터 API"
>

API 응답 예시:

{
  "timestamp": "2025-10-29T10:30:00Z",
  "markets": [
    {
      "id": "KOSPI",
      "name": "코스피",
      "value": 2500.22,
      "change": 0.45,
      "changePercent": 0.018,
      "currency": "KRW"
    },
    {
      "id": "USD_KRW",
      "name": "미국 달러",
      "rate": 1430.80,
      "change": -0.30,
      "changePercent": -0.021,
      "baseCurrency": "USD",
      "quoteCurrency": "KRW"
    }
  ]
}

robots.txt와 크롤링 정책

# robots.txt

# 티커 데이터는 자주 변하므로 캐시 비활성화
User-agent: *
Disallow: /ticker/live-stream

# 대신 정적 스냅샷 페이지 제공
Allow: /markets/snapshot

# API는 명시적으로 허용
Allow: /api/markets/live

# 크롤링 주기 제안 (초당 1회)
Crawl-delay: 1

요약: AI 친화적 티커 체크리스트

[ ] 구조화된 데이터 제공 (Schema.org, JSON-LD)
[ ] 의미 있는 aria-label과 메타데이터
[ ] data-* 속성으로 파싱 가능한 데이터 제공
[ ] 정적 대체 콘텐츠 제공 (SEO용)
[ ] JSON API 엔드포인트 제공
[ ] robots.txt로 크롤링 정책 명시
[ ] 검색 엔진용 스냅샷 페이지 제공

접근성 체크리스트

Ticker UI를 디자인할 때 다음 체크리스트를 확인하세요:

### WCAG 2.2.2 (Pause, Stop, Hide) - Level A

[ ] 5초 이상 움직이는 콘텐츠에 일시 정지 기능이 있는가?
[ ] 호버 또는 포커스 시 자동으로 일시 정지되는가?
[ ] 명시적인 제어 버튼이 있는가?

### ARIA 및 시맨틱

[ ] role="marquee"를 사용했는가?
[ ] aria-label 또는 aria-labelledby로 이름을 제공했는가?
[ ] aria-live="off"를 유지하고 있는가? (비필수 정보)
[ ] 중복 콘텐츠에 aria-hidden="true"를 적용했는가?

### 모션 및 애니메이션

[ ] prefers-reduced-motion을 지원하는가?
[ ] 모션 감소 모드에서 대체 UI를 제공하는가?
[ ] 애니메이션 속도가 적절한가? (50-100px/초)

### 키보드 및 포커스

[ ] 티커 내 요소에 키보드로 접근 가능한가?
[ ] 포커스 표시가 명확한가?
[ ] Tab 순서가 논리적인가?

### 색상 및 대비

[ ] 명암 대비가 4.5:1 이상인가?
[ ] 색상만으로 정보를 전달하지 않는가?
[ ] 아이콘 + 텍스트 + aria-label을 함께 사용하는가?

### 콘텐츠

[ ] 중요한 정보를 티커에 넣지 않았는가?
[ ] 티커 외에 완전한 정보를 제공하는가?
[ ] 텍스트가 충분히 큰가? (최소 14px)

### AI 및 검색 엔진

[ ] 구조화된 데이터를 제공하는가? (JSON-LD, Schema.org)
[ ] data-* 속성으로 기계가 읽을 수 있는 데이터를 제공하는가?
[ ] 정적 대체 콘텐츠를 제공하는가? (SEO용)
[ ] API 엔드포인트를 제공하거나 링크하는가?
[ ] 메타 태그에 의미 있는 설명이 있는가?
[ ] robots.txt에 크롤링 정책을 명시했는가?

흔한 실수와 함정

❌ 실수 1: 너무 빠른 속도

/* ❌ 10초에 전체 화면을 스크롤 - 너무 빠름 */
.ticker-content {
    animation: scroll 10s linear infinite;
}

/* ✅ 40초에 전체 화면을 스크롤 - 읽을 시간 충분 */
.ticker-content {
    animation: scroll 40s linear infinite;
}

권장 속도 계산:

// 티커 속도 계산기
function calculateTickerDuration(contentWidth, speed = 60) {
    // speed: px/sec (권장: 50-100)
    return (contentWidth / speed).toFixed(1);
}

// 예: 3000px 콘텐츠, 60px/sec
// → 50초 권장

❌ 실수 2: 중요한 정보를 티커에 넣기

❌ 잘못된 예: 필수 정보
<div role="marquee">
    중요: 비밀번호를 변경하세요. 계정이 해킹 시도를 받았습니다.
</div>
→ 사용자가 놓칠 수 있음!

✅ 올바른 예: alert 사용
<div role="alert" class="alert-danger">
    <strong>중요:</strong> 비밀번호를 변경하세요.
    계정이 해킹 시도를 받았습니다.
</div>

원칙:

“사용자가 놓쳐도 괜찮은 정보만 티커에 넣어라.”

❌ 실수 3: 일시 정지 기능 없음

❌ WCAG 위반
<div class="ticker">
    <div class="auto-scroll">
        <!-- 멈출 수 없음 -->
    </div>
</div>

✅ WCAG 준수
<div class="ticker">
    <div class="auto-scroll">
        <!-- 호버 시 일시 정지 -->
    </div>
</div>

<style>
.ticker:hover .auto-scroll {
    animation-play-state: paused;
}
</style>

❌ 실수 4: 모바일에서 너무 작은 텍스트

/* ❌ 모바일에서 읽기 어려움 */
.ticker {
    font-size: 12px;
}

/* ✅ 모바일에서 읽기 쉬움 */
.ticker {
    font-size: 14px;
}

@media (min-width: 768px) {
    .ticker {
        font-size: 16px;
    }
}

❌ 실수 5: prefers-reduced-motion 무시

/* ❌ 모션 감소 모드 무시 */
.ticker-content {
    animation: scroll 40s linear infinite;
}

/* ✅ 모션 감소 모드 지원 */
.ticker-content {
    animation: scroll 40s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
    .ticker-content {
        animation: none !important;
        /* 대신 스크롤 가능하게 */
        overflow-x: auto;
    }
}

대안: 티커를 사용하지 않는 방법

때로는 티커를 아예 사용하지 않는 것이 최선의 선택입니다.

대안 1: 정적 표시 + 더보기 링크

<div class="market-summary">
    <h2>실시간 시장 정보</h2>

    <ul class="market-list">
        <li>
            <strong>코스피</strong>
            <span>2,500.22</span>
            <span class="positive">▲ +0.45%</span>
        </li>
        <li>
            <strong>달러</strong>
            <span>1,430.80</span>
            <span class="negative">▼ -0.30%</span>
        </li>
        <li>
            <strong>유로</strong>
            <span>1,668.03</span>
            <span class="positive">▲ +0.12%</span>
        </li>
    </ul>

    <a href="/markets" class="view-all">
        모든 시장 정보 보기 →
    </a>
</div>

장점:

  • 읽기 쉬움
  • 접근성 문제 없음
  • 모든 정보 한눈에 파악

단점:

  • 공간을 많이 차지
  • “실시간” 느낌 부족

대안 2: 탭 또는 아코디언

<div class="market-tabs">
    <div role="tablist" aria-label="시장 정보">
        <button role="tab" aria-selected="true" aria-controls="panel-stock">
            주식
        </button>
        <button role="tab" aria-selected="false" aria-controls="panel-currency">
            환율
        </button>
        <button role="tab" aria-selected="false" aria-controls="panel-commodity">
            원자재
        </button>
    </div>

    <div id="panel-stock" role="tabpanel" tabindex="0">
        <!-- 주식 정보 -->
    </div>

    <div id="panel-currency" role="tabpanel" tabindex="0" hidden>
        <!-- 환율 정보 -->
    </div>

    <div id="panel-commodity" role="tabpanel" tabindex="0" hidden>
        <!-- 원자재 정보 -->
    </div>
</div>

대안 3: 자동 회전 카드 (페이드 방식)

<div class="rotating-cards">
    <div class="card active">
        <h3>코스피</h3>
        <div class="value">2,500.22</div>
        <div class="change positive">▲ +0.45%</div>
    </div>

    <!-- 5초마다 페이드 인/아웃 -->
</div>

<style>
.card {
    opacity: 0;
    transition: opacity 0.5s;
}

.card.active {
    opacity: 1;
}

/* 호버 시 자동 회전 멈춤 */
.rotating-cards:hover .card {
    animation-play-state: paused;
}
</style>

언제 Ticker를 사용해야 할까?

✅ Ticker가 적합한 경우

  1. 비필수 보조 정보:
    • 주식 시장 정보
    • 환율, 날씨
    • 스포츠 점수
    • 광고 배너
  2. 공간이 제한된 경우:
    • 좁은 헤더/푸터
    • 모바일 상단 바
    • 사이드바
  3. “실시간” 느낌이 중요한 경우:
    • 라이브 이벤트
    • 속보 뉴스

❌ Ticker를 피해야 하는 경우

  1. 필수 정보:
    • 경고, 오류
    • 중요한 공지사항
    • 사용자 액션이 필요한 메시지
  2. 순서가 중요한 정보:
    • 단계별 지침
    • 튜토리얼
  3. 긴 텍스트:
    • 긴 문장이나 문단
    • 복잡한 설명
  4. 메인 콘텐츠:
    • 페이지의 주요 내용
    • 네비게이션

마치며

Ticker UI는 “양날의 검”입니다.

잘 사용하면 제한된 공간에서 많은 정보를 효과적으로 전달할 수 있지만, 잘못 사용하면 사용자 경험을 해치고 접근성 문제를 일으킵니다.

핵심 원칙:

  1. UI: 가독성을 최우선으로 (크기, 속도, 대비)
  2. UX: 사용자에게 제어권을 주어라 (일시 정지, 호버)
  3. AX (Human): 모든 사용자를 고려하라 (WCAG, prefers-reduced-motion, 스크린 리더)
  4. AX (AI): AI와 검색 엔진을 고려하라 (구조화된 데이터, API, SEO)

실천 가이드:

티커를 만들기 전에 스스로에게 물어보세요:

1. "이 정보를 사용자가 놓쳐도 괜찮은가?"
   → 아니오라면 티커 사용 금지

2. "정적 표시로는 안 되는가?"
   → 정적 표시가 가능하다면 그것을 선택

3. "사용자가 일시 정지할 수 있는가?"
   → 없다면 WCAG 위반

4. "모든 사용자가 접근할 수 있는가?"
   → 스크린 리더, 키보드, prefers-reduced-motion 확인

5. "AI가 이 정보를 이해할 수 있는가?"
   → 구조화된 데이터, data 속성, API 제공 확인

6. "정말 움직여야 하는가?"
   → 정직하게 답하세요

하나씩 적용하다 보면, 어느새 사용자 친화적이고 접근 가능한 Ticker UI를 마스터하게 될 겁니다.


참고 자료

WCAG 공식 문서

ARIA 명세

UX 연구 및 Best Practices

커뮤니티 토론

구현 예제

도구 및 테스팅

AI 및 SEO


관련 문서

댓글