공급사 포털 내 상품 탭 렌더링 버그 3건 수정
목차
supplier-portal 작업 중 내 상품 탭에서 버그 3건을 잡았다. 탭 활성 상태, 초기 로드, 날짜 렌더링-세 개가 같은 파일(뷰/스타일 1개)에 몰려 있었고, 그 덕분에 한 번에 묶어 고칠 수 있었다.
버그 자체는 작았다. 고치는 데 걸린 시간도 길지 않았다. 근데 이런 류의 버그가 나오면 항상 드는 생각이 있다 - "이게 왜 여기서만 안 터졌지?"
버그 3건, 각각 왜 터지는가
탭 활성 상태, 초기 로드 타이밍, 날짜 렌더링. 세 개가 겉으로는 달라 보이지만 구조적으로는 거의 같은 원인에서 나온다. 컴포넌트가 처음 마운트될 때 어떤 상태값을 누가 먼저 갖고 있어야 하는지, 그 순서가 어긋나면 이 세 가지가 동시에 터진다.
탭 활성 상태 버그는 보통 라우터 쿼리나 상태 초기값이 비어 있을 때 발생한다. "아무것도 선택 안 됨"을 명시적으로 다루지 않고 첫 번째 탭을 기본값으로 쓰는 코드가 많은데, URL에 탭 파라미터가 없거나 페이지를 새로 고침하면 활성 탭이 리셋된다. 사용자 입장에선 분명히 이 탭을 보고 있었는데 새로 고침 한 번에 첫 탭으로 돌아가는 경험.
초기 로드 버그는 데이터가 준비되기 전에 화면이 먼저 렌더되는 타이밍 문제다. API 응답 전에 빈 배열이나 undefined로 렌더가 돌아가면서 조건 분기가 잘못된 쪽으로 빠진다. v-if / v-else 또는 삼항 연산자 안에서 null 체크를 빠뜨리면 딱 이 패턴이 나온다.
날짜 렌더링 버그는 가장 고전적이다. 서버에서 내려오는 날짜 포맷이 일정하지 않거나(2026-04-23T00:00:00Z vs 2026-04-23), 타임존 처리가 없으면 new Date() 파싱이 브라우저마다 다르게 동작한다. 특히 날짜만 있는 ISO 문자열을 UTC 기준으로 파싱하면 로컬 타임존에 따라 하루가 당겨지는 문제가 자주 생긴다.
이 세 가지가 같은 파일에 몰린 건 우연이 아니다. 탭 컴포넌트를 처음 짤 때 빠른 구현 위주로 가다 보면 엣지 케이스 방어 코드가 뒤로 밀리고, 그게 나중에 한꺼번에 터지는 구조다.
수정 패턴과 검증
수정은 뷰/스타일 파일 1개에서 끝났다. 변경 범위가 좁으면 회귀 범위도 좁다는 게 장점이다.
날짜 처리는 파싱 시점에 방어 로직을 추가하는 게 가장 깔끔하다. 날짜 문자열 처리를 직접 하는 경우 아래처럼 날짜만 추출해서 쓰는 방식이 타임존 영향을 최소화한다.
// 날짜 문자열에서 연/월/일만 추출 (타임존 편차 차단)
function parseLocalDate(dateStr) {
if (!dateStr) return null;
const [year, month, day] = dateStr.slice(0, 10).split('-').map(Number);
return new Date(year, month - 1, day);
}
탭 활성 상태는 초기값을 명시적으로 지정하고, 없을 때 폴백을 타도록 처리했다. 초기 로드 버그는 데이터가 비었을 때와 아직 로딩 중일 때를 분리해서 다루는 걸로 해결했다-이 둘을 같은 케이스로 묶으면 항상 타이밍 버그가 생긴다.
수정 후 검증 방식:
- 버그를 직접 재현해서 증상이 사라졌는지 확인
- 기존에 정상이던 케이스가 여전히 정상인지 확인 (회귀)
- 날짜가 관련 화면(다른 탭, 목록 화면)과 동일하게 보이는지 숫자 cross-check
이 세 개를 묶어서 체크하는 게 귀찮아 보여도, 하나를 빠뜨리면 나중에 "수정했는데 또 다른 게 깨졌다"는 상황이 나온다.
공급사 포털에서 숫자 정합성이 중요한 이유
supplier-portal은 공급사가 자신의 상품 현황과 수치를 확인하는 도구다. 날짜가 틀리게 보이거나 탭이 이상하게 동작하면 단순히 UI 문제로 끝나지 않는다. 공급사 입장에서는 "이 숫자 맞아?"라는 의심이 시스템 전체 신뢰로 번진다.
결제나 정산이 엮인 도메인에서 화면 버그는 기능 버그보다 체감 신뢰 훼손이 빠르다. 백엔드 로직이 100% 맞더라도 화면에 이상하게 보이면 그게 신뢰를 깎아먹는다. 그래서 이 영역은 "대충 맞는 것 같다"로 넘기면 반드시 다시 돌아온다.
작업하면서 확인한 또 다른 포인트 하나 - 같은 날짜 파싱 패턴이 다른 탭에도 있었다. 이번에 문제가 된 케이스만 고치고 나머지를 방치하면, 나머지는 다른 조건에서 같은 버그로 다시 나온다. 그래서 이번 수정 범위를 잡을 때 위험한 케이스를 함께 확인하고, 같은 패턴이면 같이 정리했다.
작은 커밋을 자주 쪼개는 습관을 유지하는 게 이런 상황에서 도움이 된다. 논리적으로 독립된 단위로 커밋을 나눠두면 나중에 특정 변경이 무언가를 깨뜨렸을 때 어디서 깨졌는지 git bisect나 로그만 봐도 좁혀진다. 한 커밋에 여러 수정을 묶으면 그 추적이 배로 힘들어진다.
커밋 메시지에는 "무엇을 고쳤다" 보다 "왜 고쳤다"를 담으려 한다. 3개월 뒤에 이 커밋을 다시 봤을 때 맥락이 남아있어야 하기 때문. fix: 날짜 렌더링 수정보다는 fix: 타임존 편차로 날짜 하루 당겨지는 문제 수정 (parseLocalDate로 UTC 파싱 우회)가 훨씬 유용하다.
버그 수정은 증상을 없애는 게 아니라 원인을 없애는 것이고, 원인을 알아야 비슷한 패턴이 다른 데서 터질 때 빠르게 잡을 수 있다.
댓글 0
첫 댓글 달아줘.