파트너 포털 결제수단 라벨·오늘판매 KPI·XSS 타임존 버그 수정
목차
파트너 포털에 묶여 있던 버그 세 개를 한꺼번에 처리했다. 결제수단 breakdown 라벨 정리, 사이드바 오늘판매 KPI 추가, XSS·타임존 보정. 겉보기엔 각각 독립적인 작업처럼 보이지만, 파고들다 보면 비슷한 뿌리에서 나온 문제들이었음. 변경 파일은 내부 클래스 두 개, 뷰·스타일 두 개, 설정·문서 하나. 숫자만 보면 작은 패치인데 원인을 확인하는 데 수정 자체보다 시간이 더 걸렸다.
결제수단 라벨, 오늘판매 KPI
파트너가 정산 내역을 들여다보는 화면에서 결제수단별 breakdown을 보여준다. 문제는 라벨이었음. 내부적으로 쓰는 키값이 그대로 UI에 노출되거나, 특정 결제수단이 애매한 텍스트로 표시되는 케이스가 있었다. 처음엔 단순 문자열 매핑 누락이겠거니 싶었는데, 내부 클래스 두 곳이 결제수단 타입을 각각 다르게 처리하고 있었음. 한쪽에서 결제수단을 새로 추가하면 반대쪽은 그걸 모르고 있는 구조. 동기화 포인트 자체가 없었다.
# 수정 전: 매핑 누락 → raw key가 UI에 그대로 출력
PAYMENT_LABEL_MAP = {
"card": "신용카드",
"transfer": "계좌이체",
# "virtual_account" 없음 → UI에 "virtual_account" 노출
}
# 수정 후: 명시적 매핑 + fallback 정책
PAYMENT_LABEL_MAP = {
"card": "신용카드",
"transfer": "계좌이체",
"virtual_account": "가상계좌",
}
def get_payment_label(method: str) -> str:
label = PAYMENT_LABEL_MAP.get(method)
if label is None:
logger.warning("Unknown payment method: %s", method)
return label or method # 모르는 타입은 raw key 유지, 빈 문자열보다 낫다
fallback 정책은 항상 고민되는 부분이다. 매핑 없는 타입을 빈 문자열로 처리하면 UI에서 아무것도 안 보이는 게 오히려 더 이상해 보이고, raw key를 그대로 두면 내부 구현이 드러난다. 이번엔 "일단 원래 키값을 유지하되, 경고 로그로 감지"하는 방향으로 정리했음. 사용자 경험을 깨진 채로 두기보다, 눈에 띄진 않아도 일단 작동하게 하고 나중에 추가하는 게 낫다는 판단.
오늘판매 KPI는 사이드바에 집계 수치를 새로 표시하는 작업. 뷰와 스타일도 같이 건드렸다. 쿼리 자체는 단순한 SUM인데 "오늘"을 어떻게 정의하느냐가 항상 발목을 잡는다.
| 집계 기준 | 문제점 |
|---|---|
| UTC 기준 | 한국 시간 오전 9시 이전에는 어제 날짜로 집계됨 |
| 서버 로컬타임 | 서버 설정이 바뀌면 결과가 달라짐, 배포 환경 의존 |
| KST 명시 | 쿼리 안에 타임존 변환 포함, 이식성은 낮지만 의도가 명확 |
결제 도메인에서 "오늘 판매"가 틀리면 파트너가 매일 보는 수치가 어긋난다. 그래서 KST를 쿼리 안에 명시하는 방향으로 갔음. 이식성을 일부 포기하는 트레이드오프인데, 의도가 코드에 드러나는 게 나중에 읽는 사람한테도 낫다고 봤다.
XSS와 타임존 보정이 같은 파일에 있었던 이유
XSS 이슈는 프론트 스크립트에서 사용자 입력값을 DOM에 직접 삽입하는 경로가 남아 있었던 것. 파트너 포털이라 입력 주체가 제한돼 있다고는 해도, XSS는 "이 사용자는 믿을 만하다"는 가정 자체를 공격 경로로 만든다. 신뢰 수준에 관계없이 출력 시점에 이스케이프하는 게 기본값이어야 함.
// 수정 전: innerHTML에 값을 직접 삽입
el.innerHTML = partnerNote;
// 수정 후: 텍스트면 textContent, HTML이 꼭 필요할 때만 escapeHtml
el.textContent = partnerNote;
// HTML 마크업이 필요한 경우에만
el.innerHTML = escapeHtml(partnerNote);
사내 툴이라서 XSS를 낮게 보는 경향이 생기기 쉬운데, 파트너 계정이 탈취되면 내부 툴이라도 피해는 실질적이다. "어차피 사내용"이라는 논리가 쌓이면 어느 순간 진짜 사고가 난다.
타임존 보정은 우연히 같은 파일에 붙어 있었다. 클라이언트에서 날짜 범위를 계산해 서버로 넘길 때, 로컬 타임존을 반영하지 않고 UTC 오프셋을 잘라서 보내고 있었음. 서버 쪽은 KST 기준으로 집계하니까, 날짜 경계 근처에서 결과가 틀어진다. 자정 전후 9시간이 위험 구간. 파트너가 오전에 사이드바를 열었을 때 어제 수치가 오늘 수치에 섞이거나, 반대로 빠지는 현상이 나타날 수 있었음.
두 이슈가 우연히 같은 파일에 있었지만 같이 고친 건 결과적으로 좋았다. 반쯤 손댄 파일을 다음에 다시 여는 건 맥락을 다시 로드해야 해서 비효율적임. "여기 있는 김에" 처리하는 게 맞을 때가 있다.
이번 작업에서 다시 확인한 것들
버그를 고칠 때 내가 밟는 순서는 대략 이렇다:
- 수정 전 현재 상태 캡처 - 스크린샷이든 수치든 "이게 잘못됐다"는 근거를 남겨둔다
- 같은 로직이 다른 경로에도 있는지 확인 - 이번엔 실제로 있었고, 같이 고쳤다
- 수정 후 같은 케이스로 재현 시도 - 안 터지면 통과
- 관련 화면과 숫자 cross-check - 결제수단별 합계가 총 매출과 일치하는지
마지막 항목이 의외로 중요하다. 화면 하나만 보면 맞아 보이는데, 다른 화면에서 같은 숫자를 다르게 표시하는 경우가 있음. 정합성 체크를 생략하면 한쪽은 고쳐져 있고 다른 쪽은 여전히 틀린 채로 있게 된다.
커밋은 세 건을 묶지 않고 각각 쪼갰다. 라벨 정리, KPI 추가, XSS·타임존 보정이 논리적으로 독립적이라서. 귀찮아 보일 수 있는데, 나중에 어느 변경에서 뭔가 깨졌는지 찾을 때 범위를 좁혀주는 게 확실히 도움이 됨. 커밋 메시지는 "무엇을 바꿨다"보다 "왜 바꿨다"를 담으려고 노력하는 편. XSS 건은 "innerHTML 직접 삽입 - 출력 이스케이프 누락"처럼 원인을 그대로 적었다.
사내 서비스를 만들다 보면 기능 하나가 화면에 버튼 하나 추가하는 것으로 끝나지 않는다는 걸 계속 체감한다. SQL 집계, 타임존, 예외 처리, 렌더링, 보안이 다 엮여 있고, 하나만 빠뜨려도 숫자가 맞지 않거나 특정 조건에서 이상한 화면이 나온다. 특히 결제 도메인은 숫자 하나가 틀리면 파트너 신뢰가 흔들릴 수 있어서 꼼꼼함이 기본값이어야 한다고 생각함. "대충 맞는 것 같다"로 넘어가면 같은 문제로 반드시 다시 돌아온다.
댓글 0
첫 댓글 달아줘.