Project

상주 출석 시스템 개발기

monsaumon 2026. 9. 27. 23:09

안녕하세요, SCG 31기 최연우입니다.

 

2026 신입 프로젝트로 진행한 상주 출석 시스템에서 제가 참여한 백엔드 개발 과정에 대해 이야기하고자 합니다. 처음으로 협업하며 개발해서 어려운 점이 꽤 있었던 것 같네요...

 

개발이 상당히 산발적으로 이루어졌기 때문에, 글의 흐름을 매끄럽게 하기 위해 시간순과 다르게 배치된 항목들이 종종 있습니다.


기존 상주 시스템

SCG는 기존에 노션 폼을 이용하여 상주 출석을 체크했습니다.

그러나 이는 아무때나, 아무데서나 입력할 수 있었기 때문에 출석 여부를 양심에 맡겨야 했습니다.

그래서 신입 프로젝트로 이를 보완하여 상주 출석 시스템을 개발하기로 했습니다.

초기 설계

처음에 ERD를 설계했을 때, 시간 정보를 담고 있는 timeblock과 회원 목록이 있고, 이 둘을 묶는 attendance로 누가 어느 시간에 상주하는지를 담기로 했습니다. 추가로 enum과 비슷한 status, resident_status 테이블과 출석 정책 (지각 기준 등)을 담는 config 테이블이 있었습니다.

초기 ERD

ERD를 기준으로 인증·인가 기능, 관리자용 각종 CRUD 기능을 차근차근 개발해 나가며, 출석 방식과 정책에 대해 끊임없이 고민했습니다.

꼼수 막기

처음에 "양심에 맡기는 출석 시스템"을 개선하기 위해 시작했기 때문에, 대리 출석이나 꼼수를 막기 위한 고민을 많이 했었습니다. 첫 ERD에는 입실 시간만 체크하도록 되어 있었지만, 퇴실 태그까지 찍도록 하여 '그 시간 동안 상주했다'는 것을 최대한 보장하려고 했습니다. 일찍 퇴실한 사람과 안온 사람을 구분하기 위해 완료 여부를 저장하는 컬럼도 추가했습니다.

와이파이 등을 기반으로 물리적으로 그곳에 있는지 체크하자는 아이디어도 있었지만, 기술적 어려움과 기기만 두고 떠나면 된다는 점 때문에 기각되었습니다.

 

출석 처리는 NFC 태그를 통해 하기로 했는데, 태그 자체에서 찍을 때마다 올라가는 ctr 값이 링크에 포함되는 방식이었습니다.

이에 따라 최근 ctr 값을 저장하고, 이보다 높은 값을 가진 태그 요청만 정상으로 처리하도록 했습니다.

처음에는 로그인 시점에 ctr 값을 보내려고 했는데, 그렇게 되면 로그인 페이지 링크를 타인에게 복사해 주면 태그를 직접 찍지 않고도 출석할 수 있는 허점이 있었습니다. 그래서 첫 redirect 시점에 nfc 값을 검증하고, 그 데이터를 이용해 attendanceTicket이라는 문자열을 발급해 주었습니다. 보조적인 수단으로 랜덤한 문자열을 deviceToken이라는 쿠키에 담아 출석 요청 시점에 함께 검증했습니다.

 

이렇게 해도 여전히 꼼수는 있었습니다. attendanceTicket과 deviceToken을 복사해 직접 요청을 보내면 막을 수 없고, 더 간단한 방법으로 그냥 계정을 남에게 알려주면 대리 출석이 가능합니다. 이를 막기 위해 디바이스 핑거프린팅을 도입해볼까도 고민했으나, 기술적 어려움과 프라이버시 침해 우려가 있어 기각되었습니다. 계정 알려주기는 이후 Google OAuth를 도입하면서 조금 더 꺼려지는 방법이 되긴 했습니다.

 

단체 도입

여러 단체가 사용할 수 있는 시스템으로 만들기 위해, organization 테이블을 만들고, 각종 데이터를 단체 중심으로 관리하도록 변경했습니다. 관리자는 본인 단체의 timeblock, user, attendance만 관리하며, config도 단체마다 따로 적용했습니다.

이후에는 부가 기능으로 단체 로고를 저장할 수 있도록 했습니다.

 

학기를 관리하는 calendar도 추가했습니다. 학기별 통계, 이번 학기의 상주 목록 등 학기 중심 기능을 추가하면서 이번 학기가 언제부터 언제까지인지 정할 필요가 있었는데, 계산식을 도입하기에는 특별히 정해진 규칙이 없었습니다.

이에 따라 별도의 Calendar 테이블을 추가하여 단체별로 연도, 학기, 시작일과 종료일을 관리하도록 했습니다.

 

상주 교환

기존 상주 시스템에서는 관리자가 요청을 받아 일일히 상주를 교환해주었습니다. 이를 시스템이 처리하도록 하기 위해 교환 요청을 담는 swap 테이블을 만들고, 사용자끼리 교환할 수 있도록 했습니다.

swap은 요청자, 대상자, 교환 대상 출결, 요청 상태와 시각을 저장합니다. 처음에는 양도의 형식이었지만, 기존 시스템과 통일하기 위해 양방향 맞교환을 지원하며 상대방 출결도 함께 저장하게 되었습니다.

 

이렇게 출결 담당자를 바꾸는 과정은 단순히 attendance의 user를 수정하는 일로 끝나지 않았습니다. 이미 해당 시간에 배정된 사용자인지, 과거 상주인지, 요청 중인 출결이 관리자에 의해 수정되지는 않았는지 확인해야 했습니다. 관련 Swap이 함께 취소되거나 삭제되도록 정합성 규칙도 보완했습니다.

 

프론트엔드 연결

재앙이 시작되었습니다.

처음에 간단히 기능만 의논하고 시작했기에, 서로 구현이 안맞는 부분이 너무 많았습니다. API 명세도 백엔드를 개발해 나가면서 작성해서, 프론트 쪽에 제대로 반영되지 않았습니다.

 

프론트에 있는데 백엔드에 없는 기능, 백엔드에 있는데 프론트에서 안쓰는 기능, 프론트에만 있는 검증, 백엔드 철학과 안맞는 UI 등 수많은 문제가 발생했습니다. 프론트엔드 연결도 예정보다 늦게 시작했는데, 할 일이 쏟아져나와서 막바지에 죽어라 일했습니다.

연결 과정에서 발견된 버그도 너무나 많았고, 기능 구현할 때는 생각지도 못했던 부분에서도 많은 수정이 필요했습니다.

 

SCG에 들어오고 나서 가장 많은 걸 배운 것도 이때였던 것 같습니다.

 

서비스 시작과 불만

수많은 버그 수정을 마무리하고, 2학기 상주 시작과 함께 시스템을 실사용하기 시작했습니다.

이에 따라 개발할 때는 인지하지 못했던, 각종 불편함을 제보받았습니다.

 

이메일을 다른 걸 쓰고 싶다는 분이 계셔서, DB로 그냥 바꾸려다가 중간에 이메일이 바뀌면 기록이 날아간다는 것을 깨달았습니다. 이에 따라 이메일 변경 기능을 만들었습니다.

 

상주 상태 변경 (결석 -> 출석 등)은 관리자가 듣고 해주면 된다고 생각했는데, 이는 안일했습니다. 관리자를 언제나 만날 수 있는 것도 아니고, 관리자 부담이 너무 컸습니다. 이에 따라 상주 상태 변경 요청 기능을 만들어 이를 수월하게 진행할 수 있도록 했습니다.

 

가장 큰 문제점이자 불만사항으로는, 태그를 너무 많이 찍는다는 점이 있었습니다. 10분이나 20분 정도 되는 시간에 맞춰서 입실, 갱신, 퇴실 때마다 태그를 찍어야 했고, 기존 시스템에서 퇴실을 찍지 않았다는 점이 더해져 결석처리가 우후죽순 생겨났습니다.

이에 따라 아래의 LOOSE MODE를 개발하기로 했습니다.

 

LOOSE MODE

기존 정책을 STRICT로 두고, LOOSE MODE에서는 훨씬 널널하고 적게 태그하도록 했습니다.

먼저 상주 전 입실 태그 시간 제한과 상주 후 퇴실 태그 시간 제한을 해제하고, 입실 ~ 퇴실 사이의 모든 상주를 자동으로 처리하도록 변경했습니다.

 

이에 따라 이전에 미입실/미퇴실 상주를 자동으로 결석처리하던 스케줄러 등 많은 부분의 동작 방식을 크게 바꿔야 했습니다.

거의 모든 코드에서 STRICT와 LOOSE를 분기하여 처리했고, MODE 전환 과정에서의 오류도 잡아야 했습니다.

이 과정에서 지각 상주가 퇴실 시 출석처리 되거나, 아주 짧은 상주에서 스케줄러가 동작하지 않는 등의 오류를 추가로 수정했습니다.

 

Slack 알림

기존 상주 시스템에서 상주 인원을 Slack에 알려주는 워크플로우가 있었습니다. 이를 새로운 시스템에도 반영하기 위해 Slack 알림 기능을 추가했습니다.

 

Slack Webhook 기능을 이용하기 위해, Slack에 bot을 추가했습니다.

이때 알림 주소와 활성화 여부, 몇 분 전에 알릴지 단체별로 다르게 설정할 수 있어야 했습니다.
그래서 organization에 webhook URL, 활성화 여부, reminder_minutes를 추가했습니다.


멘션을 위해 사용자에게 slack_user_id도 추가됐습니다. Webhook은 단체 설정에 두고, 멘션 대상 식별자는 사용자 정보에 둔 구조입니다.


Slack 알림 스케줄러는 매분 실행되어 설정된 시간에 시작하는 상주를 조회합니다. 조회 결과에서 휴일이나 행사가 아닌 상주만 골라 타임블록별로 묶은 뒤 Slack 메시지를 보냅니다. Slack ID가 있으면 사용자를 멘션하고, 없으면 이름이나 이메일을 표시합니다.
예를 들어 현재 시각이 10시 15분이고 알림 시점이 5분 전으로 설정되어 있다면, 10시 20분에 시작하는 상주가 알림 대상이 됩니다.

마치며

여기까지가 상주 시스템 개발 과정이고, 이후에도 무슨 버그가 터질 지 몰라 유지 보수를 위해 대기하고 있습니다. 첫 ERD와 최종 ERD를 비교해 보면 뭐가 엄청 많아진게 보여서 재미있습니다.

최종 ERD

 

처음의 ERD와 현재 ERD를 나란히 놓고 보면, 상주 출석 시스템이 “누가 언제 상주하는가”에서 출발해 “어떤 정책으로 출결을 처리하고, 변경과 알림을 어떻게 운영할 것인가”까지 다루게 된 흐름을 확인할 수 있습니다.

 

개발 과정이 조금 힘들었지만, 개발한 서비스가 앞으로 잘 쓰일 것 같아서 뿌듯합니다. 프로덕션과 협업에 대한 지식도 많이 얻을 수 있어서 좋은 경험이었습니다.

 

이런 블로그 글을 처음 써봐서 조금 장황한 것 같은데, 좋게 봐주시면 감사드리겠습니다.

 

감사합니다.

상주 서비스 마스코트