출입기록 이상패턴 분석
근태 시스템이 기록하지 않는 근무 중 이동을 출입 통제 로그로 재구성. 결과물이 개인을 지목하는 자료임을 전제로, 설계의 대부분을 오탐 제거에 썼다.
출입 단말기 로그에서 장시간 외출 패턴을 찾아내되, 오탐을 걸러내는 데 설계의 대부분을 쓴 근태 데이터 분석 도구. 2026년 1~3월 3개월치 분석에 사용.
1. 한눈에
| 역할 | 분석 설계 · 구현 · 결과 검토 (1인) |
| 기간 | 2026-03 (3개월치 소급 분석) |
| 스택 | Python 3 · pandas · numpy · openpyxl |
| 규모 | 분석 스크립트 약 250줄 + 월별 통합 스크립트 |
| 입력 | 출입 단말기 원시 로그 (사원번호 · 일시 · 단말기 ID) |
| 출력 | 이상외출 상세내역 + 인별 요약 (월별 / 통합) |
2. 배경 — 무엇이 문제였나
근태 시스템은 출근과 퇴근을 기록한다. 그 사이에 무슨 일이 있었는지는 기록하지 않는다.
한편 출입 통제 단말기는 모든 통과를 기록한다. 즉 근무 시간 중 나갔다가 돌아온 흔적은 여기에만 남는다. 이 데이터로 “장시간 자리 비움”의 실태를 파악해 달라는 요청이었다.
문제는 이 데이터를 그대로 세면 거의 전부가 오탐이라는 것이었다.
- 문 앞에서 잠깐 나갔다 바로 들어온 기록
- 흡연·택배 수령 같은 짧은 이동
- 그리고 결정적으로 — 밖에 나간 것처럼 보이지만 실제로는 건물 안에 있던 경우
그리고 이 분석에는 다른 종류의 위험이 있었다. 결과물이 특정 개인을 지목하는 목록이라는 점이다. 오탐 한 건은 통계상의 오차가 아니라 한 사람에 대한 근거 없는 의심이 된다.
그래서 이 도구의 설계 목표는 “많이 찾아내기”가 아니라 “틀리게 지목하지 않기” 였다.
3. 무엇을 만들었나
원시 로그를 넣으면 이상 외출 세션을 뽑아 상세내역과 인별 요약 두 시트로 내보내는 스크립트.
원시 로그 (전체 통과 기록)
│
▼
① 범위 축소 ── 주 출입 단말기 2개 · 16시 이후로 한정
│
▼
② 세션 구성 ── 퇴실 → 입실 페어링, 인별·일별로 순차 스캔
│ ├─ 5분 이내 재입실 = 노이즈 → 양쪽 모두 폐기
│ └─ 복귀 후 그날 최종 퇴실 시각도 함께 기록
▼
③ 이상 판정 ── 외출 60분 초과만 남긴다
│
▼
④ 오탐 제거 ── 퇴실~입실 사이에 다른 단말기 활동이 있으면 제외
│ (= 건물 안에 있었다는 증거)
▼
⑤ 정합성 검사 ── 상세 건수 == 요약 합계, 불일치면 즉시 실패
│
▼
상세내역 + 인별 요약 (Excel 2시트)
4. 설계의 핵심은 4단계 — 오탐 제거
이상 판정(3단계)까지는 단순하다. 판정된 것을 다시 무너뜨리는 4단계가 이 도구의 실질이다.
주 출입구 단말기 두 개만 보면 “나갔다”로 보이지만, 사내에는 다른 단말기가 여러 개 있다. 퇴실 기록과 입실 기록 사이의 시간대에 다른 단말기 통과 기록이 존재한다면, 그 사람은 그때 건물 안에 있었다는 뜻이다. 주 출입구 기록만 보고 판정한 결과가 틀렸다는 반증이다.
그래서 이미 “이상”으로 판정된 세션에 대해, 같은 사람의 그 시간대 다른 단말기 활동을 역으로 조회해 증거가 나오면 목록에서 뺀다.
이 필터는 자기가 만든 판정을 스스로 부정하는 단계이고, 그래서 실행 로그에 제거 건수를 반드시 출력하도록 했다. 몇 건을 잡았는지가 아니라 몇 건을 취소했는지가 이 분석의 신뢰도 지표이기 때문이다.
5. 판정 기준을 상수로 노출한 이유
임계값 네 개를 코드 상단에 상수로 모아 뒀다.
| 상수 | 값 | 의미 |
|---|---|---|
| 분석 시작 시각 | 16:00 | 이 시각 이후 기록만 본다 |
| 노이즈 임계 | 5분 | 이내 재입실은 외출로 치지 않는다 |
| 이상 임계 | 60분 | 초과해야 이상으로 본다 |
| 내부 활동 필터 | on | 중간 단말기 활동 있으면 제외 |
이 숫자들은 기술적 최적값이 아니라 운영 정책이다. “몇 분부터 외출로 볼 것인가”는 데이터가 정해주는 것이 아니라 회사가 정하는 것이다.
그래서 코드 안에 흩어놓지 않고 한곳에 모아 이름을 붙였고, 실행할 때마다 적용된 기준을 콘솔 첫 줄에 출력하게 했다. 결과를 보는 사람이 “무슨 기준으로 뽑은 건가”를 묻기 전에 답이 나와 있어야 한다.
6. 기술적 의사결정
① 세션 페어링을 인별·일별 순차 스캔으로 처리했다 출입 로그는 한 사람이 하루에 여러 번 나가고 들어온다. 퇴실을 만나면 그 뒤에서 가장 가까운 입실을 찾고, 페어가 성립하면 그 지점부터 다시 스캔한다. 노이즈 페어는 양쪽 기록을 모두 폐기하고 그 다음부터 재개한다 — 노이즈의 입실을 다른 퇴실과 잘못 짝지으면 없던 외출이 만들어지기 때문이다.
② 복귀 후 최종 퇴실 시각을 함께 기록했다 “몇 시에 나갔다 언제 들어왔다”만으로는 판단이 어렵다. 돌아온 뒤 언제 퇴근했는지가 같이 있어야 그 외출의 성격이 보인다. 판정에는 쓰지 않고 사람이 검토할 때 보라고 넣은 컬럼이다.
③ 상세와 요약의 정합성을 assert로 강제했다 요약 시트의 횟수 합계가 상세 건수와 다르면 즉시 실패한다. 두 시트가 조용히 어긋난 리포트는, 틀린 리포트보다 나쁘다 — 어느 쪽이 맞는지 알 수 없기 때문이다.
④ 입력 파일을 자동 탐지하되 모호하면 물어본다 파일명 규칙으로 자동 인식하고, 후보가 여러 개면 목록을 보여주고 선택하게 한다. 없으면 실행을 중단한다. 매달 같은 작업을 반복할 때 틀린 파일로 조용히 돌아가는 것이 가장 흔한 실패다.
7. 숫자로 본 결과
| 항목 | 수치 |
|---|---|
| 분석 기간 | 2026-01 ~ 03 (3개월) |
| 최종 이상 외출 | 39건 |
| 관련 인원 | 20명 (중복 제외) |
| 산출물 | 월별 리포트 3건 + 3개월 통합 리포트 1건 |
| 필터 단계 | 2단계 (노이즈 페어 + 내부 활동 반증) |
개별 대상자 정보는 이 문서에 담지 않는다. 원 산출물은 인사 목적의 내부 자료다.
8. 이 도구의 한계 — 문서에 같이 적어 둔 것
- 출입 로그는 의도를 담지 않는다. 60분 이상 자리를 비웠다는 사실과 그것이 문제인지는 별개다. 이 도구는 확인 대상을 좁힐 뿐 판단하지 않는다.
- 단말기를 통과하지 않는 이동(동승 출입 등)은 애초에 데이터에 없다. 즉 누락은 구조적으로 존재하며, 이 목록은 완전한 목록이 아니다.
- 그래서 산출물은 “위반자 명단”이 아니라 “확인이 필요한 건” 으로 이름 붙였다.