[portfolio](https://geonwoo.kim/ko/portfolio) › 도구와 오픈소스

# offon · 출퇴근·휴가를 관리하는 오픈소스 슬랙 앱

https://geonwoo.kim/ko/portfolio/offon-oss

기간: 2026.04 — 2026.08

루쿠쿠에 합류했을 때 근태 관리가 제대로 되지 않아, 퇴근 후 시간에 만든 시스템에서 출발했습니다. 이 시스템은 이후 회사 온톨로지까지 붙은 전사 업무 시스템으로 커졌습니다. 그중 근태·휴가 기능만 떼어 오픈소스 슬랙 앱 offon을 만들었습니다.

사내 업무 시스템에서 떼어 낸, MIT 라이선스의 직접 설치형 앱

- [github](https://github.com/takealook97/offon)

팀: 1인

역할: 설계 · 풀스택 개발

Next.js 16 · React 19 · TypeScript · Tailwind CSS 4 · shadcn/ui · PostgreSQL · Prisma · Slack Web API · Vercel Cron

- 자정을 넘긴 근무와 하루 여러 번의 출퇴근, 근무·휴식·초과근무 시간 자동 계산
- 시작할 때 끝나는 시각을 정하고 복귀 알림을 보내는 60분 식사
- 주말·공휴일을 뺀 종일·반차 휴가 신청과 남은 연차 추적
- 달력에서 하는 근태 정정 신청과 승인 뒤 시간 재계산
- 휴가와 근태 정정을 한곳에서 처리하는 관리자 승인함
- 슬랙 일회용 코드 로그인, 슬래시 명령 5개, 출퇴근 누락 알림

## 동시에 들어온 출근 요청

문제: 슬랙 재전송이나 두 번 클릭으로 출근 요청이 거의 동시에 오면, 두 요청 모두 사전 검사를 통과해 열린 근무가 두 개 생길 수 있었습니다.

해결: PostgreSQL 부분 유니크 인덱스로 근태 하나에 열린 근무를 하나만 허용했습니다. 두 번째 요청은 사전 검사와 같은 '이미 근무 중' 응답을 받습니다.

## 정정 신청 뒤에 생긴 기록

문제: 정정을 신청한 뒤 실제로 퇴근하거나 휴식을 더 하면, 승인할 때 그 기록을 덮어쓸 수 있었습니다.

해결: 신청 당시 값, 신청한 값, 승인 시점 값을 비교합니다. 신청에서 건드리지 않은 값은 이후 기록을 그대로 두고, 같은 항목이 양쪽에서 다르게 바뀌면 충돌로 처리합니다.

## 서버와 로컬의 시간대 차이

문제: UTC 서버와 한국 시간 로컬에서 날짜를 그대로 다루면 9시간이 두 번 더해지거나 요일이 틀릴 수 있었습니다.

해결: 시각은 한국 시간으로 해석해 UTC로 저장합니다. 한국 시간으로 표시하는 함수와 날짜로 요일을 구하는 계산을 나눴습니다. 실행 환경이 달라도 같은 시각과 요일을 표시합니다.

## 슬랙의 재전송

문제: 슬랙은 명령 응답이 늦으면 같은 요청을 다시 보냅니다. 그대로 처리하면 같은 기록과 알림이 두 번 생길 수 있었습니다.

해결: 요청의 재전송 표시를 먼저 확인하고, 재전송이면 처리하지 않고 바로 200으로 응답합니다. 응답이 늦어도 같은 알림이 두 번 가지 않습니다.


[vat · 상위 폴더 하나에서 여러 저장소를 개발하는 작업 공간 CLI](https://geonwoo.kim/ko/portfolio/vat) · [sshift · 점프 서버 접속과 비밀번호 저장을 묶은 SSH CLI](https://geonwoo.kim/ko/portfolio/sshift)
