결제 플랫폼에 근태관리 모듈 추가하고 메뉴얼 자동 생성 도입
목차
이커머스 결제 플랫폼에 근태관리 SaaS 모듈을 끼워 넣는 작업을 시작했다. 파트너들이 "정산도 여기서 보는데 직원 출퇴근까지 한 화면에서 보면 안 되냐"고 계속 요청해와서 결국 사이드 모듈로 붙이기로 결정한 건데, 여기서부터 방향 설정이 핵심이었다.
요청 자체는 단순해 보임. 결제 플랫폼이 이미 파트너 데이터를 쥐고 있고, 직원 출퇴근도 같은 파트너 단위로 관리하면 되니까 "그냥 테이블 몇 개 더 만들면 되는 거 아닌가"라는 느낌. 근데 그게 아님. 사이드 모듈을 본 시스템에 얹을 때 문제는 항상 기존 코드가 아니라, 기존 코드가 "이런 확장이 없다"고 가정하고 짜였다는 데서 온다. 결제 인증 가드, 결제 권한 매트릭스, 결제 시드 데이터—다 자기 맥락 안에서만 완결된 코드라 새 도메인이 들어오면 곳곳에서 예상 밖 마찰이 생김. 이번에도 그대로 재현됐다.
테이블 설계: 최소한으로 자르되 격리는 철저하게
근태 도메인은 본업이 아니니까 처음부터 과도하게 설계하지 않으려 했다. 일단 5개만 만듬.
| 테이블 | 용도 |
|---|---|
| 직원 | 파트너 소속 직원 마스터 |
| 출퇴근 기록 | 일자별 in/out 타임스탬프 |
| 근무 스케줄 | 시프트, 야간/주말 플래그 |
| 휴가 신청 | 사유, 결재 상태 |
| 월 집계 | 연장/야간/주말 합산 |
핵심 설계 원칙은 파트너 단위 격리. 모든 테이블에 파트너 식별자 칼럼을 박아 넣었다. 결제 모듈이랑 같은 DB를 쓰니까 스키마 분리 대신 행 수준 분리로 가야 했고, 그 통제는 인증 가드 쪽에서 담당하게 했다.
스키마를 아예 분리하는 방법도 있다. 근태 도메인이 커질수록 그 선택이 나을 수도 있는데, 지금 단계에서는 DB 커넥션 풀을 두 개로 나누는 오버헤드가 더 크다고 봤다. 도메인 경계가 명확하게 자리잡히면 그때 분리를 검토할 예정.
월 집계 테이블은 일단 NULL 허용으로 깔았다. 배치 처리가 붙기 전까지는 집계 데이터가 없는 행이 생길 수밖에 없어서 불가피한 선택인데, 배치가 안정화되면 NOT NULL + DEFAULT 0으로 조이는 게 맞다. 지금 NULL 허용인 채로 두면 나중에 집계 쿼리에서 COALESCE 도배가 생긴다.
인증 가드: 두 개 체인이 왜 틀렸나
기존 가드는 결제 권한 기준으로만 짜여 있었다. 근태 모듈 URL이 들어오면 권한 룩업 자체가 안 되니까 무조건 401이 나왔음.
처음 시도는 가드 두 개를 체인으로 거는 방식이었다.
AuthGuard(결제) → AttendanceGuard(근태)
결제 가드가 먼저 통과시켜주고 근태 가드가 추가 확인하는 구조가 될 거라 생각했는데, 실제론 두 가드가 독립적으로 실행되면서 우선순위가 꼬였다. 결제 가드가 근태 URL에 대해 401을 반환하기 전에 근태 가드가 결제 URL을 차단하는 케이스가 섞이면서, 멀쩡한 결제 화면까지 같이 막혔다. 이 디버깅에 두 시간을 날렸음.
결국 단일 가드 안에서 모듈을 분기하는 방식으로 통합했다.
요청 URL → 모듈 식별 → 파트너 권한 룩업 → 통과/거부
이 구조가 맞는 이유는 권한 결정이 항상 한 곳에서 나와야 추적이 가능하기 때문이다. 가드가 여러 개면 어떤 가드에서 막혔는지 로그 없이는 추적이 어렵고, 우선순위 설정 실수가 곧 보안 홀이 된다. 권한 모델은 처음부터 한 줄로 흐르게 짜는 게 답이라는 걸 또 배웠다. 다음에 또 같은 상황이 생기면 가드 등록 순서를 로그로 먼저 찍고 시작할 것.
시드 데이터에서도 한 번 더 막혔다. 초기 데이터 넣을 때 휴일 코드가 기존 결제용 코드랑 키 충돌이 났음. 같은 DB라는 건 인지하고 있었는데 코드 키까지 충돌할 거라는 건 미처 못 봤다. 네임스페이스 prefix를 붙여서 회피했는데, 이런 식으로 도메인 코드를 처음부터 구분해두면 나중에 도메인이 늘어도 충돌 걱정을 안 해도 된다.
- 결제 도메인:
PAY_HOLIDAY_* - 근태 도메인:
ATT_HOLIDAY_*
메뉴얼 자동 생성기: 이번 커밋의 진짜 핵심
새 모듈 만들 때마다 운영팀이 "사용법 좀" 하고 찾아온다. 매번 워드 열어서 캡처 붙이고 있을 수 없어서 이번에 아예 라우트 하나를 만들었다. 테이블 스키마와 라우트 정보를 읽어서 마크다운으로 토해내는 방식.
생성 흐름은 세 단계다.
- 테이블 스키마 + 컬럼 코멘트 → 마크다운 표
- 라우트 + 파라미터 → API 레퍼런스 섹션
- 시드 데이터 → 샘플 시나리오
운영팀이 URL 하나만 치면 그 자리에서 메뉴얼을 받아갈 수 있음. 배포 직후 "이거 어떻게 써요"가 아니라 "아 이미 있네요"가 나오는 경험은 생각보다 팀 분위기에 영향이 크다.
이걸 만들면서 체감한 것: 메뉴얼을 자동으로 뽑으려면 처음부터 "자동화 가능한 형태"로 코드를 짜야 한다는 거다. 컬럼 코멘트가 비어있으면 메뉴얼에도 빈칸으로 출력된다. 스키마 코멘트 강제 정책이 없으면 결국 쓸모없는 메뉴얼이 나옴. PR 체크나 CI 단계에서 코멘트 누락을 잡는 lint를 걸어야 이 구조가 제대로 돌아간다. 자동화기는 있는데 결과물이 반쪽짜리가 되는 상황은 그냥 안 쓰는 것보다 나쁘다.
이 생성기는 근태 모듈에만 붙인 게 아니라 범용으로 만들었다. 다음 모듈 작업 때 우선 붙여볼 생각이고, 그때 컬럼 코멘트 lint도 같이 넣을 예정.
돌아보면 근태 모듈 자체는 작은데 권한, 메뉴얼, 시드 세 갈래로 영향 범위가 퍼져서 생각보다 오래 걸렸다. 사이드 모듈을 본 시스템에 끼울 때는 항상 권한 모델부터 다시 그려야 한다는 걸 매번 까먹고 매번 다시 배운다. 이번에도 그랬고, 아마 다음에도 초반엔 또 그럴 것 같다. 다만 이번에 메뉴얼 자동 생성기를 빼고 나면 모듈 하나 더 얹는 데 드는 비용이 줄어들 거라는 기대는 있다. 일단 다음 모듈에서 확인해 보는 걸로.
댓글 0
첫 댓글 달아줘.