개발 slecs

파트너 포털 버튼 권한 데이터 불일치 원인 분석 보고서 작성

목차

파트너 포털에서 버튼 권한 데이터가 실제 동작과 맞지 않는다는 게 잡혔다. 로그 레벨에서 보면 에러는 없는데, 특정 역할의 사용자가 눌러야 할 버튼이 안 보이거나 반대로 눌리면 안 되는 버튼이 노출되는 케이스가 간헐적으로 신고됐다. "간헐적"이 가장 골치 아픈 단어다. 재현이 안 되는 것처럼 보이면 코드 탓인지 데이터 탓인지 구분을 못 하고 시간을 날린다.

이 케이스는 빠르게 데이터 탓이라는 심증이 생겼다. 권한 판단 로직 자체는 단순했고, 같은 코드에서 어떤 사용자는 정상이고 어떤 사용자는 이상했으니까. 그래서 SQL로 직접 들어가기로 했다.

분석 보고서를 먼저 만드는 이유

버그를 잡으러 바로 코드를 열면 빠를 것 같지만, 데이터 불일치 문제는 그게 오히려 더 늦다. 어떤 데이터가 얼마나 틀렸는지 모르는 상태에서 코드를 건드리면 픽스 범위를 잡을 수 없고, 수정 후에도 "다 고쳐졌나?"를 판단할 기준이 없다. 보고서를 먼저 만드는 게 결국 더 빠른 이유가 거기 있다.

이번 보고서의 구조는 이렇게 잡았다.

  • 대상 테이블에서 역할-버튼-권한 플래그 조합 전수 집계
  • 시스템이 기대하는 상태(소스코드 기준 정의)와 실제 DB 상태 대조
  • 불일치 항목만 추출해서 별도 섹션으로 분리
  • 각 불일치 항목에 원인 가설 기재 (마이그레이션 누락, 기본값 오염, 중복 삽입 등)
  • 재현 가능한 케이스는 재현 시나리오까지 기재

집계 쿼리 자체는 복잡하지 않다. 핵심은 조건을 어디서 거느냐다.

SELECT
  r.role_code,
  b.button_key,
  bp.is_visible,
  bp.is_enabled,
  COUNT(*) AS cnt
FROM button_permissions bp
JOIN roles r ON r.id = bp.role_id
JOIN buttons b ON b.id = bp.button_id
WHERE b.portal = 'partner'
GROUP BY r.role_code, b.button_key, bp.is_visible, bp.is_enabled
ORDER BY r.role_code, b.button_key;

이걸 역할-버튼 단위로 집계해서 기대값 테이블과 JOIN하면 불일치 행이 바로 나온다. 기대값 테이블은 코드에 박힌 상수 정의를 보고 직접 작성했다. 코드와 DB가 다른 상태를 "불일치"라고 정의하는 거니까, 기대값의 출처를 명확히 해두는 게 중요하다. 출처가 모호하면 비교 자체가 의미 없어진다.

결과는 HTML로 뽑았다. 이유는 단순하다. 숫자가 많으면 터미널 출력은 가독성이 떨어지고, 팀원에게 공유할 때도 파일 하나 보내면 되니까 편하다. 차트는 선택이지만 이번엔 역할별 불일치 건수를 바 차트로 시각화했다. 숫자만 나열하면 어느 역할이 가장 오염됐는지 한눈에 안 들어오기 때문이다.

발견된 불일치 패턴

이번 분석에서 나온 발견 사항을 정리하면 아래와 같다.

항목 상태 비고
데이터 정합성 일부 불일치 발견 특정 역할에 집중됨
처리 누락 건 후속 조치 필요 권한 플래그 미설정 행 존재
쿼리 성능 허용 범위 내 파트너 포털 규모 기준
엣지 케이스 추가 검토 필요 역할 중복 부여 시나리오

불일치가 특정 역할에 집중된다는 건 전역 버그가 아니라 특정 시점 마이그레이션이나 역할 추가 작업에서 누락이 생겼다는 신호다. 코드 자체가 잘못된 게 아니라 데이터를 세팅하는 스크립트 어딘가에 구멍이 있었다는 뜻이다. 이 가설을 갖고 git log에서 해당 역할이 추가된 커밋을 찾으면 원인 파악이 훨씬 빨라진다.

파트너 포털처럼 역할과 버튼 조합이 많은 구조에서는 권한 세팅을 애플리케이션 레이어에서만 관리하려다가 이런 불일치가 생기기 쉽다. 역할이 추가될 때마다 버튼 권한 초기 데이터도 같이 삽입돼야 하는데, 그 작업이 별도 스크립트로 분리돼 있으면 누락될 여지가 생긴다. 역할 생성 트랜잭션 안에 권한 초기화를 묶거나, 최소한 배포 후 검증 쿼리를 CI에 포함시키는 게 이런 문제를 예방하는 방향이다.

보고서 → 수정 → 재검증 사이클

보고서에서 도출된 항목들은 별도 커밋으로 수정했다. 보고서와 픽스 커밋을 분리하는 이유는 롤백 시 추적이 편해서다. 데이터 수정 스크립트가 로직 코드 변경과 섞이면 나중에 어떤 커밋이 뭘 건드렸는지 구분하기 어렵다. "이 커밋이 데이터 픽스다"라고 명확히 나눠두면 문제가 생겼을 때 원인을 거슬러 올라가기가 훨씬 쉽다.

수정 후에 보고서를 다시 돌렸다. 불일치 건수가 0이 되는 걸 확인하는 게 목표였고, 실제로 0이 됐다. 이 "재검증" 단계가 없으면 수정이 잘 됐는지 느낌으로 판단하게 되는데, 데이터 문제에서 느낌은 믿을 게 못 된다.

이 방식을 반복하면서 한 가지 습관이 생겼다. 분석 쿼리를 재사용 가능한 형태로 유지하는 것이다. 한 번 만든 집계 쿼리를 다음에도 돌릴 수 있으면 "저번에도 이런 문제 있었나?"라는 질문에 바로 답할 수 있다. 파일 이름에 날짜-타임스탬프를 박는 것도 그래서다. 20260421_1519처럼 타임스탬프를 넣으면 언제 어떤 상태를 스냅샷했는지 나중에도 바로 안다. 보고서가 스냅샷이기도 하고 동시에 이후 코드 수정의 근거 문서 역할도 하는 셈이다.

가장 단순하지만 가장 효과적인 원칙 하나를 이번에도 다시 확인했다. 내가 기대하는 숫자와 DB에 실제로 있는 숫자를 나란히 놓고 비교하면 된다. 차이가 나는 행이 버그의 위치를 가리킨다. 이걸 자동화하면 모니터링이 되고, 수동으로 돌리면 디버깅이 된다. 어느 쪽이든 "데이터로 확인한다"는 원칙은 같다.

댓글 0

첫 댓글 달아줘.