기업사용자 모바일 API 계약 버그 감사 결과 (2026-07-23)
개요
모바일 앱 의 18개 feature가 호출하는 98개 엔드포인트를 백엔드 라우터 / Pydantic 스키마 / 서비스 레이어와 1:1 대조(경로·메서드·쿼리·요청 body·응답 필드·스코프·인증)하여 계약 불일치를 감사했다. 각 발견은 양측 파일을 다시 읽는 적대적 검증을 거쳤으며, 오탐 0건 · 확인 6건이다. 감사 완료 시점 기준 수정은 미진행(보류) 상태다. 원본 감사 문서 참조. 기업사용자 모바일 API 계약 버그 감사 (2026-07-23)
매핑 규칙: 모바일apiFetch('/v2/X')→ 백엔드/api/v2/X(라우터 prefix/api/v2)
발견 요약
상세 발견
#1 🔴 HIGH — 내 근무표: 주간 근무 집계 오류
페이지: (work)/my-schedule 유형: 응답 필드 미소비
백엔드(policy_service.py:2954-2963)는 GET /v2/user/policy/my-week-schedule 응답에 week_worked_min·week_target_min(주간 레벨) 및 일자별 recognized_min을 이미 포함해 반환한다. 반면 모바일 WeekScheduleResponse/WeekScheduleDay 타입에 이 3개 필드가 아예 누락되어 있고, 대신 스케줄 블록 span을 로컬에서 합산(weekTotalMin = Σ block.workMin)하고 주간 소정 분모를 2,400분(40시간) 하드코딩한다. 웹 원본은 이 3개 필드를 그대로 소비했으나, 모바일 이관 시 반영 실패했다.
영향 (코드로 성립된 5가지 케이스):
| 케이스 | 백엔드 recognized | 모바일 표시 | 방향 |
|---|---|---|---|
| 정상 출근 정규직 | 주 초부터 실제 근무분 | 7일 전체 span 합 → 주 초부터 40:00/40:00, 100% | 과대 |
| 결근일 포함 주 | recognized=0 | ≈480분 | 과대 |
| 승인 휴가 포함 주 | ≈40h(소정 인정) | 0:00 | 과소 |
| 파트타임/공휴일 주 | week_target_min=실소정 | 항상 2400분 분모 | 진행률 오류 |
| 연장근로 주 | recognized=연장 포함 | 연장 미반영 | 과소 |
수정 방향: WeekScheduleResponse/WeekScheduleDay 타입에 3개 필드 추가 → weekTotalMin = week_worked_min, 분모 = week_target_min(폴백만 40h), 일자별 라벨 = recognized_min. 순수 프론트 변경이므로 크래시 없고 회귀 위험 낮음. 기업사용자 모바일 API 계약 버그 감사 (2026-07-23)
#2 🟠 MEDIUM — 캘린더: 뷰 경계일 시간지정 일정 조용히 누락
페이지: (community)/schedule 유형: 쿼리 파라미터
모바일이 end='2026-06-30'(시간 없음)으로 전송하면 백엔드가 datetime으로 파싱하여 자정(T00:00)으로 해석, 그날 시간지정 이벤트(start_datetime > 00:00)가 필터에서 탈락한다. 오류/422 없이 조용히 누락되며, 종일 이벤트는 무해하고 midnight이 아닌 start를 가진 이벤트에 한정된 영향이다.
- 예: 6-30 14:00 회의 → 6월 뷰(end 06-30)와 7월 뷰(start 07-01) 양쪽에서 안 보임
- 수정 방향: range end에 시간 부착(
T23:59:59) 또는end+1일date-only 전송 기업사용자 모바일 API 계약 버그 감사 (2026-07-23)
#3 🟠 MEDIUM — 결재 반려시트: 보완요청 최소 길이 불일치
페이지: (requests)/approval (반려시트) 유형: 요청 body 계약 불일치
결재 관리 보완요청 기능에서 최소 길이 기준이 불일치한다.
| 위치 | 조건 |
|---|---|
모바일 RejectSheet.tsx:48 | trimmed.length >= 1 (1자 이상이면 버튼 활성화) |
백엔드 document.py:161 | SupplementRequest.comment: min_length=5 |
1~4자 입력 시 버튼은 활성화되지만 FastAPI가 422를 반환한다. '수정요망'·'보완필요'(4자)처럼 자연스러운 한국어 메모가 정확히 실패 구간에 해당한다.
- 수정 방향: 모바일
canSupplement게이트를>= 5로 상향 + 카운터 문구 추가. 백엔드min_length완화는 정책 결정 필요. 기업사용자 모바일 API 계약 버그 감사 (2026-07-23)
#4 🟠 MEDIUM — 결재 '내 신청' 탭: 잘못된 엔드포인트 스코프
페이지: (requests)/approval ('내 신청' 세그먼트) 유형: 스코프 오류
'내 신청' 탭은 본인이 작성한 문서를 보여줘야 하나, 네 세그먼트 모두 /v2/approvals(결재자-스코프)만 호출한다. 백엔드 list_approvals는 DocumentApprovalStep.approver_id == 현재 사용자인 문서만 반환하므로, 자기 문서의 결재자는 통상 상급자인 일반 사용자의 작성 문서는 결과에 포함되지 않는다.
- 영향: 신청 이력이 있어도 '내 신청' 카운트 0 · 빈 화면
- 올바른 소스:
/v2/documents(list_my_documents, author-scoped) - 수정 방향: mine 세그먼트를
/v2/documents로 분리 호출 기업사용자 모바일 API 계약 버그 감사 (2026-07-23)
#5 🟡 LOW — 휴가 이력: '일수' 배지 항상 미표시
페이지: (work)/vacation 유형: 응답 필드 불일치
휴가 관리 신청 이력에서 '(N일)' 배지가 어떤 건에서도 표시되지 않는다.
| 위치 | 필드명 |
|---|---|
모바일 logic.ts:101 | r.used_days |
백엔드 LeaveRequestInfo 스키마 | total_days (used_days 없음) |
백엔드 response_model이 여분 필드를 스트립하므로 r.used_days는 항상 undefined. 옵셔널 가드로 크래시는 없으나 배지는 렌더되지 않는다.
- 수정 방향:
logic.ts:101의r.used_days→r.total_days기업사용자 모바일 API 계약 버그 감사 (2026-07-23)
#6 🟡 LOW — 비밀번호 변경: 클라 규칙 하드코딩
페이지: (more)/change-password 유형: 정책 드리프트
모바일이 GET /v2/password/policy를 조회하지 않고 8자·대문자·소문자·숫자 4규칙을 하드코딩. 백엔드는 테넌트별 PasswordPolicy를 강제하며 해당 조회 엔드포인트가 이미 존재한다.
- 영향: 관리자가 정책을 완화하면 서버는 통과시킬 비밀번호를 모바일이 제출 전 차단. 기능적 lockout은 아니나 조건부 비차단 UX 오류.
- 수정 방향: 화면 진입 시
/v2/password/policy조회 후 클라 검증 규칙에 반영 기업사용자 모바일 API 계약 버그 감사 (2026-07-23)
클린 판정 (계약 불일치 없음 · 12개 feature)
dashboard · attendance(출퇴근) · attendance-request · overtime · notices · messages · notifications · applications · payroll · directory · work-location · profile 기업사용자 모바일 API 계약 버그 감사 (2026-07-23)
커버리지 (엔드포인트 98개)
주요 감사 라우터: attendance.py(근태/휴가) · policy.py(근무표/정책) · calendar.py · documents.py(결재/문서) · payroll.py · notifications.py · password.py · users.py·employees.py·organization.py(프로필/조직) 기업사용자 모바일 API 계약 버그 감사 (2026-07-23)