개발 slecs

거래명세서 다크모드 렌더링 버그 수정

목차

거래명세서 화면은 partner-portal 안에서도 조금 특수한 위치다. 일반 목록 UI와 달리 인쇄 레이아웃에 가깝고, 배경·텍스트·테두리 색상을 요소마다 명시적으로 잡아야 하는 구조다. 그 상태에서 다크모드 토글을 누르면 특정 영역이 검은 배경에 검은 글씨가 되거나, 흰 배경에 흰 글씨가 돼 내용이 사라지는 증상이 있었다. 변경 파일은 뷰·스타일 1개였지만 원인 파악까지 포함하면 생각보다 짚어볼 게 많았다.

color-scheme이 커버하지 못하는 구간

처음 다크모드를 붙일 때 color-scheme: light dark만 선언하면 브라우저가 알아서 처리해 줄 거라 기대하기 쉽다. 실제로 브라우저 기본 UI 요소, 즉 스크롤바·폼 컨트롤·<input> 배경 같은 네이티브 요소는 이 힌트를 받아서 자동으로 색을 바꾼다. 문제는 개발자가 명시적으로 지정한 background-color, color, border-color에는 전혀 영향을 주지 않는다는 점이다. 브라우저는 이미 선언된 값을 color-scheme이 override하지 않는다.

거래명세서 컴포넌트가 딱 이 상태였다. 헤더, 품목 테이블, 합계 영역 전부 #ffffff/#111111 계열로 하드코딩돼 있었고, color-scheme을 선언해봤자 아무것도 바뀌지 않는다.

/* 수정 전: color-scheme만 선언, 명시적 색상은 고정 */
.invoice-container {
  color-scheme: light dark;
  background-color: #ffffff;
  color: #111111;
}

수정 방향은 prefers-color-scheme 미디어쿼리로 명시 분기하거나, CSS custom property로 색상 변수를 뽑아서 다크 테마에서 변수값만 교체하는 방식 두 가지다. 이번엔 전자로 처리했다. 후자가 구조적으로 더 깔끔하지만 기존 스타일 전체를 변수 체계로 리팩터링하는 건 이번 범위를 벗어난다고 판단했다.

/* 수정 후: 미디어쿼리로 명시 분기 */
.invoice-container {
  background-color: #ffffff;
  color: #111111;
}

@media (prefers-color-scheme: dark) {
  .invoice-container {
    background-color: #1a1a1a;
    color: #e5e5e5;
  }

  .invoice-table th {
    background-color: #2a2a2a;
  }

  .invoice-table td {
    border-color: #333333;
  }

  .invoice-amount-badge {
    background-color: #2d2d2d;
    color: #e5e5e5;
  }
}

JS 토글 로직도 함께 손봤다. 다크모드 클래스를 <html>에 붙이는 방식이었는데 .invoice-container까지 cascade가 제대로 내려가지 않는 구간이 있어서 셀렉터 우선순위를 정리했다. @media 분기와 클래스 토글이 동시에 존재하면 우선순위 충돌이 생길 수 있어서 어느 방식으로 통일할지 확인이 필요했다.

수정 범위를 어디까지 잡을 것인가

이런 종류의 버그는 파일 하나 고친다고 끝나는 경우가 드물다. 거래명세서가 쓰는 공통 컴포넌트, 즉 금액 뱃지, 상태 태그, 테이블 셀에도 같은 패턴이 반복될 가능성이 있다. 실제로 확인해 보니 동일한 명시적 색상 고정이 두 곳 더 있었다.

여기서 판단이 필요하다. 전부 한 커밋에 고치는 게 맞아 보이지만, 범위가 넓어질수록 회귀 위험도 같이 커진다. 이번에는 거래명세서와 직접 공유하는 컴포넌트만 이번 커밋에 포함하고, 독립적인 다른 화면의 동일 패턴은 별도 이슈로 분리해 트래킹하는 방향을 택했다.

대상 이번 커밋 포함 별도 이슈 분리
거래명세서 직접 공유 컴포넌트 O -
다른 화면의 동일 패턴 - O
CSS custom property 전환 - O
color-scheme 전역 정리 - O

한 커밋에 여러 화면을 뭉쳐 수정하면 나중에 어느 변경에서 문제가 생겼는지 찾기 훨씬 어려워진다. 논리적으로 독립된 단위로 쪼개는 습관이 결국 디버깅 비용을 줄인다.

금융 도메인 화면에서 렌더링 버그가 무서운 이유

거래명세서는 숫자가 곧 사실 확인 수단인 화면이다. 렌더링 버그가 단순히 보기 불편한 수준으로 끝나지 않는다. 특정 항목 금액이 배경과 동화돼 안 보이거나, 음수 금액이 잘못된 색상으로 표시돼 양수로 오인될 수 있다. 실제 금전 착오로 이어지지 않더라도 신뢰 문제로 이어지는 건 금방이다.

내부 서비스라도 마찬가지다. 보는 사람이 숫자를 신뢰하지 못하기 시작하면 그 화면 자체를 쓰지 않게 된다. "나중에 정리하자"로 쌓아두면 안 되는 유형이다. 발견 즉시 처리하는 게 맞다.

수정 이후 확인 순서는 이렇게 유지하고 있다.

  • 버그 재현 케이스로 직접 동작 확인
  • 라이트 모드 동일 화면과 숫자 cross-check
  • 다크/라이트 토글 반복으로 전환 시 깨짐 없는지
  • 관련 컴포넌트 공유 여부 파악 후 범위 메모
  • 비슷한 패턴이 다른 화면에 있는지 grep 또는 직접 확인

엣지 케이스를 꼼꼼히 따지는 게 귀찮아 보여도, 같은 버그로 다시 오는 시간 비용이 훨씬 크다. 이번에 "이 색상을 왜 인라인으로 박아놨지?"라는 의문도 생겼는데, 처음 만들 때 다크모드 지원 계획이 없었던 흔적이다. 기능을 처음 만들 때 CSS 변수로 색상을 관리해 뒀다면 이번 버그는 처음부터 없었을 거다. 다크모드를 나중에 얹는 구조가 아니라, 초기부터 --color-bg, --color-text 같은 custom property로 묶어두면 테마 전환이 변수 재정의 한 덩어리로 끝난다. 레거시 코드에서 이걸 소급 적용하는 비용이 크니까 그때그때 판단해야 하지만, 신규 화면은 처음부터 이 방식으로 가는 게 장기적으로 낫다고 본다.

커밋 메시지도 "렌더링 색상 수정" 대신 "다크모드에서 color-scheme만으로 명시적 색상값이 override되지 않는 문제 수정"으로 달았다. 무엇보다 왜를 담으려고 노력하는 편인데, 나중에 git log 뒤질 때 그게 훨씬 더 도움이 된다.

댓글 0

첫 댓글 달아줘.