개발 slecs

중계결제 브랜드 헤더 정렬

목차

중계결제 브랜드 헤더 가운데 정렬 작업. 변경 파일은 뷰/스타일 1개였는데, 막상 파일을 열고 보니 챙겨야 할 게 생각보다 많았다.

결제 화면에서 브랜드 헤더는 단순한 제목이 아니다. 사용자가 "지금 어떤 결제 수단으로 진행 중인가"를 한눈에 인식하는 시각적 기준점이다. 중계결제 구조에서는 하나의 결제 플로우 안에서 브랜드가 전환될 수 있기 때문에, 헤더가 명확하게 위치해 있지 않으면 사용자 입장에서 "지금 뭘 쓰고 있는 건지" 순간적으로 헷갈릴 수 있다. 그 혼란이 이탈로 이어지는 건 금융 도메인에서 특히 빠르다.

헤더가 왼쪽으로 밀려 있거나 패딩이 맞지 않으면 레이아웃이 어수선해 보인다. 금융 서비스에서 어수선한 UI는 "덜 예쁜 것" 수준이 아니라 신뢰 문제로 번질 수 있다. 그래서 이번 작업을 단순 스타일 정리가 아니라 UX 정합성 작업으로 보고 진행했다.

이번에 한 파일 안에서 손본 항목:

  • 헤더 레이아웃 정렬 수정
  • 칩/뱃지 비주얼 업그레이드
  • 아이콘 위치 및 크기 조정
  • 반응형 헤더 처리

각각 따로 보면 소소하지만, 한 화면 안에서 정렬, 칩 크기, 아이콘이 동시에 맞아야 비로소 정돈된 화면으로 인식된다. 특히 여러 브랜드가 동일 컴포넌트를 공유하는 중계결제 구조에서는 레이아웃 규칙 하나가 틀리면 관련 브랜드 전체에서 틀린 채로 나간다. 수정 후 검토 범위를 해당 화면 하나로 끝내지 않고 관련 케이스 전부를 직접 확인한 이유다.

!important 대신 specificity를 올리는 방향으로

가장 신경 쓴 부분이 CSS 구조였다. 레거시 코드에 !important가 여기저기 박혀 있으면 나중에 override하려고 또 !important를 써야 하는 악순환이 생긴다. 규칙을 이기려는 규칙이 쌓이면서 스타일시트 전체가 점점 읽기 어려워지는 과정이 거기서 시작된다.

CSS specificity는 인라인/ID/클래스·속성·가상클래스/요소 순으로 점수가 매겨진다. !important는 이 점수 체계 바깥에 존재해서, 일단 쓰기 시작하면 정상적인 상속·우선순위 흐름이 무너진다. 부모 셀렉터를 추가하거나 클래스를 조합해서 우선순위를 확보하는 쪽이 나중에 훨씬 다루기 쉽다.

/* 전 */
.brand-header {
  text-align: center !important;
  padding: 0 16px !important;
}

/* 후: 부모 컨텍스트로 specificity 확보 */
.payment-relay .brand-header {
  text-align: center;
  padding: 0 16px;
}

색상, 여백, 아이콘 크기는 CSS 커스텀 프로퍼티로 정리했다. 디자인 토큰이 변경될 때 변수 하나만 바꾸면 관련 컴포넌트 전체에 반영된다. 처음엔 번거롭게 느껴질 수 있는데, 시스템이 커질수록 이 패턴의 효과가 커진다.

:root {
  --brand-header-padding: 0 16px;
  --chip-height: 24px;
  --icon-size-sm: 16px;
  --icon-size-md: 20px;
}

.payment-relay .brand-header {
  padding: var(--brand-header-padding);
}
.payment-relay .brand-chip {
  height: var(--chip-height);
}

반응형은 SCSS mixin으로 처리했다. 브레이크포인트 수치를 컴포넌트마다 직접 박으면 기준값이 파일 전체에 흩어지는데, mixin 하나에 모아두면 기준이 바뀔 때 한 곳만 수정하면 된다. 모바일/태블릿/데스크톱 세 해상도를 각각 확인하는 게 번거롭지만, 중계결제 화면은 다양한 환경에서 쓰이기 때문에 생략하면 어딘가에서 반드시 깨진 채로 나간다.

@mixin respond-to($bp) {
  @if $bp == 'mobile' {
    @media (max-width: 767px) { @content; }
  } @else if $bp == 'tablet' {
    @media (min-width: 768px) and (max-width: 1023px) { @content; }
  }
}

.brand-header {
  font-size: 18px;
  text-align: center;

  @include respond-to('mobile') {
    font-size: 15px;
    padding: 0 12px;
  }
}

텍스트/배경 대비는 WCAG AA 기준 4.5:1로 확인했다. 결제 화면은 사용자 연령대가 넓고 야외 모바일 환경에서도 쓰인다. 대비가 낮으면 개발 환경 모니터에서는 멀쩡해 보이다가 현장에서 민원이 들어오는 패턴이 있다. 숫자 데이터는 크고 굵게, 보조 정보는 작고 흐리게 처리하되 대비 비율은 기준 이상으로 맞춰두는 게 기본값이어야 한다.

SCSS mixin 레이어 하나가 추가되면 빌드 복잡도나 스타일 디버깅 난이도가 조금씩 올라가는 건 사실이다. 그 트레이드오프를 감수하고 쓰는 이유는 간단하다. 브레이크포인트 수치가 흩어져 있으면 언젠가 반드시 맞지 않는 곳이 생기고, 그걸 찾는 데 드는 비용이 mixin 관리 비용보다 훨씬 크다.

금융 도메인에서 UI는 기능이다

사내 서비스를 만들다 보면 기능 하나가 단순히 버튼 하나 추가로 끝나는 경우가 드물다는 걸 계속 체감한다. 이번 스타일 작업만 해도, 칩/뱃지 위치가 틀어지면 결제 상태를 나타내는 시각 단서가 사라지고, 아이콘 크기가 맞지 않으면 터치 타겟이 작아져서 모바일에서 탭 실패로 이어진다. SQL 집계, 상태 머신, 예외 처리, 화면 렌더링이 다 엮여 있는 것처럼, 레이아웃 하나에도 기능적 의미가 있다.

특히 결제 도메인은 숫자 하나, 상태 표시 하나가 틀리면 신뢰가 무너진다. "대충 맞는 것 같다"로 넘기면 반드시 다시 돌아오고, 그때는 처음보다 더 복잡한 형태로 돌아온다. 그래서 작업 전후 확인 루틴을 명시적으로 고정해두고 있다.

단계 내용
작업 전 현재 화면 스크린샷, 주요 수치 메모
수정 후 동일 케이스로 재확인
관련 화면 숫자·상태 cross-check
커밋 "무엇을"보다 "왜"를 메시지에 담기
배포 후 브라우저·디바이스별 직접 확인

커밋은 논리적으로 독립된 단위로 쪼개는 습관을 유지하고 있다. 이번처럼 헤더 정렬과 칩 비주얼이 같은 파일 안에 있어도, 의미 단위로 커밋을 분리하면 나중에 어느 변경에서 깨졌는지 추적이 훨씬 쉽다. git bisect 쓸 일이 없는 게 제일이지만, 커밋이 작을수록 bisect 범위도 좁아지고 리뷰어 입장에서 변경 의도를 파악하기도 쉽다. 작은 커밋 하나가 나중의 큰 삽질을 막는다.

UI 작업이라 "기능 변경 없음"으로 취급하기 쉬운데, 결제 화면 UI는 기능과 동급으로 관리해야 한다고 생각한다. 사용자가 화면을 보고 상태를 판단하는 이상, 시각 정보가 잘못되면 기능도 잘못된 것과 다르지 않다.

댓글 0

첫 댓글 달아줘.