Published on

페이지네이션 가독성 리디자인 회고 — 파란색 테마 위의 활성 상태 표현

Authors
  • avatar
    Name
    Hyo814
    Twitter

페이지네이션 가독성 리디자인 회고 — 파란색 테마 위의 활성 상태 표현

게시판 페이지네이션을 Bootstrap 기본 스타일로 붙여뒀더니, 실제 화면에서 활성 페이지 번호가 전혀 눈에 안 띄는 문제가 있었습니다. 원인을 파고 들어가 보니 사이트 전체 테마와의 충돌이었고, 결국 전면 리디자인으로 해결했습니다. 그 과정을 정리합니다.


1. 증상 — 현재 페이지가 어디인지 모르겠다

Bootstrap의 기본 페이지네이션은 활성 상태에 background-color: var(--bs-primary) 를 씁니다. 기본값은 파란색 계열이죠.

문제는 이 서비스의 브랜드 컬러 자체가 파란색이었다는 점입니다. 헤더, 링크, 주요 버튼이 전부 파란색이라, 활성 페이지 번호도 파란 배경에 흰 숫자로 나오는데 주변의 비활성 페이지 번호도 파란 글자라서 색상 대비가 거의 사라졌습니다.

<!-- 기본 Bootstrap -->
<ul class="pagination">
  <li class="page-item"><a class="page-link">1</a></li>
  <li class="page-item active"><a class="page-link">2</a></li>
  <li class="page-item"><a class="page-link">3</a></li>
</ul>

사용자 피드백:

"지금 몇 페이지에 있는지 한눈에 안 들어와요."

"이전/다음 버튼도 그냥 텍스트라 클릭 가능한지 헷갈려요."


2. 원인 분석 — 세 가지가 겹친 문제

단순히 색만 바꾸는 걸로 해결되지 않겠다 싶어서, 문제를 세 개로 분해했습니다.

2.1 색상 충돌

활성 페이지의 파란색이 사이트의 어떤 파란색 인지 구별이 안 됩니다. 헤더도 파랑, 링크도 파랑, 활성 페이지도 파랑.

2.2 모양 차이 없음

활성/비활성이 동일한 모양(직사각형)에 색만 반전되어 있습니다. 시각적 hierarchy가 색 하나에만 의존하는 상태.

2.3 "이전/다음" 의 클릭 가능성 애매

< > 문자만 있어서 버튼이 아니라 구분자처럼 보입니다. 특히 비활성 상태(첫/마지막 페이지)에서는 회색 텍스트라 더 그렇습니다.


3. 리디자인 방향 — 형태·그림자·아이콘 세 축으로

색상 하나가 아니라 형태+그림자+아이콘 세 가지를 동시에 써서 hierarchy를 만들기로 했습니다.

요소비활성호버활성
배경흰색연한 회색진한 파란색
테두리회색 1px진한 회색 1px없음
그림자없음얕은 그림자진한 그림자
글자진한 회색검정흰색
모양평면약간 떠보임카드처럼 떠있음

"카드형" 이라고 부른 이유: 활성 페이지가 약간 떠올라 보이는 입체감을 줬습니다. 그림자가 있으면 색만 볼 때보다 훨씬 빠르게 "이게 지금 위치" 임을 인지합니다.


4. 구현 — 커스텀 CSS 클래스

Bootstrap 기본 클래스를 오버라이드하는 대신, 별도 클래스를 만들어서 섞었습니다. 유지보수할 때 "내가 건드린 것" 과 "Bootstrap 기본" 을 구분하기 위해서입니다.

/* static/css/pagination.css */
.pagination--card .page-link {
  min-width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid #d9d9d9;
  border-radius: 8px;
  color: #333;
  background: #fff;
  margin: 0 4px;
  transition: all 0.15s ease;
}

.pagination--card .page-link:hover {
  background: #f5f5f5;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
  color: #000;
}

.pagination--card .page-item.active .page-link {
  background: #2a4dd0;       /* 사이트 파랑보다 한 톤 진하게 */
  border-color: transparent;
  color: #fff;
  box-shadow: 0 4px 12px rgba(42, 77, 208, 0.35);
  transform: translateY(-1px);
}

.pagination--card .page-item.disabled .page-link {
  background: #f7f7f7;
  color: #bbb;
}

포인트:

  • 활성 색은 브랜드 파랑보다 한 톤 진하게. 완전히 다른 색으로 바꾸면 브랜드 일관성이 깨지고, 똑같이 쓰면 본래 문제로 돌아갑니다.
  • transform: translateY(-1px) — 1px만 띄워도 "지금 여기" 가 확 살아납니다. 과하게 띄우면 버튼이 잘려 보이니 주의.
  • min-width — 숫자 페이지와 아이콘 페이지(<, >)의 너비를 통일해서 라인이 흔들리지 않게.

5. 이전/다음 버튼 — 텍스트에서 아이콘으로

<, > 문자는 대체로 폰트마다 다르게 그려지고, 특히 안티에일리어싱이 어중간할 때 삐뚤어 보입니다. 아이콘으로 교체했습니다.

<li class="page-item {% if not page.has_previous %}disabled{% endif %}">
  <a class="page-link" href="?page={{ page.previous_page_number }}" aria-label="이전">
    <i class="bi bi-chevron-left"></i>
  </a>
</li>
...
<li class="page-item {% if not page.has_next %}disabled{% endif %}">
  <a class="page-link" href="?page={{ page.next_page_number }}" aria-label="다음">
    <i class="bi bi-chevron-right"></i>
  </a>
</li>
  • aria-label — 아이콘만 있으면 스크린 리더가 읽을 게 없습니다. "이전", "다음" 을 명시합니다.
  • <&lt; 로 직접 쓸 때의 위험: 템플릿 엔진에 따라 이스케이프 동작이 달라집니다. 아이콘 쪽이 안전.

6. 페이지 범위 표시 — "1 ... 4 5 6 ... 20"

글이 많아지면 페이지 번호가 화면을 꽉 채우는 문제가 있어서, 현재 페이지 기준 앞뒤 2개 씩만 보여주고 나머지는 ... 으로 축약했습니다.

# board/templatetags/pagination_extras.py
from django import template

register = template.Library()


@register.simple_tag
def pagination_range(page_obj, window=2):
    current = page_obj.number
    total = page_obj.paginator.num_pages
    start = max(1, current - window)
    end = min(total, current + window)
    pages = list(range(start, end + 1))
    if start > 1:
        pages = [1, '...'] + pages
    if end < total:
        pages = pages + ['...', total]
    return pages

템플릿에서는 '...' 이면 클릭 불가한 span으로, 숫자면 링크로 렌더링합니다. 이 패턴은 어느 프레임워크에서나 동일하게 쓸 수 있습니다.


7. 회고 — "기능은 되는데 안 보이는" 것도 버그다

처음에는 이 문제를 "사소한 CSS 이슈" 로 미뤄뒀습니다. 기능적으로는 클릭하면 페이지가 넘어가니까요. 하지만 실제 사용자 테스트를 해 보면 "지금 몇 페이지인지 모르겠다" 는 피드백이 가장 먼저 나왔습니다. 기능이 완성이어도 현재 상태를 인지할 수 없으면 실패입니다.

작업을 마치고 배운 것:

  • 색 하나로 상태를 표현하면 테마와 충돌하기 쉽다. 형태·그림자·아이콘을 보조 축으로 가져가면 훨씬 안전.
  • Bootstrap 기본 스타일을 오버라이드할 때는 별도 클래스로. .page-item.active 를 직접 덮어쓰지 말고 .pagination--card .page-item.active 식으로 스코프를 좁히는 편이 유지보수에 유리.
  • aria-label은 기능 구현의 일부다. 아이콘을 넣는 순간 세트로 들어가야 합니다.

기능이 완성된 이후에 오히려 "지금 여기가 어디인지 보여주는" 작은 UI 가 체감 품질을 크게 끌어올린다는 걸 다시 한 번 확인했습니다.