서론
출석기능
3기, 4기에 함께 활동했던 개발 동아리 Leets는 매주 목요일 19시에 정기모임 시간을 가진다. 동아리의 원활한 운영을 위해 출석을 관리해줄 수 있는 기능이 필요했고, Weeth 출석 기능을 통해 매주 정기모임 전 4자리 출석 코드를 입력하여 출석을 진행하는 방식을 도입하여 운영되고 있었다. 동아리 원은 정기모임이 시작되기 전 출석 코드를 입력해야 하며, 입력이 정상적으로 이루어지면 출석이 인정된다
정기모임이 끝난뒤, 마감된 출석에 대해서는 아래의 내용처럼 완료된 출석에 대해서 조회를 할 수 있도록 하는 기능 또한 구현되어있었다
만약 정기모임에 출석하지 않는다면 아래의 사진처럼 패널티가 부여되고 3번의 패널티를 받으면 동아리 활동이 제한되도록, 성실하게 참여하는 멤버들에게 동기 부여, 원활한 동아리 운영 등등의 이유에서 동아리 관리 측면에서 원칙으로 정해놓았다
구현할 기능
Weeth 서비스에서 동아리 원들의 출석을 추적관리하기 위해 출석의 상태를 enum으로 ATTEND(출석), PENDING(미결), ABSENT(결석) 총 세가지 상태로 관리하고있다. 별도의 어드민 페이지에서 정기모임이 끝날때 저장(마감)을 눌러주면, 만약 특정 유저가 결석을 하여 출석상태가 그대로 초기상태인 PENDING으로 되어있었다면 ABSENT으로 바꿔주고, 출석을 한 유저는 ATTEND로 그대로 출석처리를 해주는 로직을 통해 출석률과 패널티를 재계산해주는 시스템이였다
근데 운영진이 매번 출석을 마감하는 과정이 번거로웠다. 정기모임이 끝날 때마다 어드민 페이지에 접속하여 직접 마감 버튼을 눌러야 했기 때문에, 이 과정을 자동화할 수 있다면 전보다 편리한 운영이 가능할 것이라고 판단했다. 이에 따라 정기모임 종료 시점에 자동으로 출석을 마감하는 기능을 추가해서 구현하기로 결정했다
본론
기능설계
1. 기존 출석로직 유지
일단 구현전에 가장 먼저 고려했던 내용은 출석 마감 자동화 기능을 추가하면서 기존의 출석 로직을 그대로 유지하는 것이었다. 자동화된 프로세스가 정상적으로 동작한다면 운영진의 개입 없이 출석이 마감될 수 있지만, 예시로 서버 장애나 Cron 스케줄 실행 실패와 같은 문제가 발생하면 예상치 못한 예외 상황으로 발생할 가능성도 출석이 자동으로 마감되지 않을 수 있다고 판단했다. 이때 운영진이 기존처럼 수동으로 출석을 마감할 수 있는 기능이 제공되지 않는다면 출석을 일일히 수동으로 마감해줘야하는 상황이 생길 수도 있어서 안전하게 자동화된 마감 프로세스가 실패하더라도 기존처럼 운영진이 직접 마감을 수행할 수 있도록 기존 로직을 유지했다
2. Scheduled 어노테이션의 cron의 날짜 및 시간 지정
마감 자동화는 어떤 방식으로 해줄지 고민하다가 cron을 활용하여 특정 시간에 출석을 마감하는 방식을 선택했는데. 처음에는 매일 밤 10시에 자동으로 출석을 마감하는 방식도 고려했지만, 이 방법은 너무 과도한 접근이라고 판단했다. 그 이유는 출석 마감은 특정 이벤트(정기모임)에 대한 관리가 핵심이였기에, 매일 출석을 체크해야 하는 시스템이 아니고 매주 목요일에 대한 정기모임에 대한 관리가 구현 목적에 맞다고 생각했기 때문이다
또한, cron을 사용하면서 가장 중요한 요소 중 하나는 마감 시간을 언제로 설정할 것인지 결정하는 것이었다. 가장 중요하게 생각했던건
출석 마감의 시간이 지나치게 늦거나 일찍이면, 출석 관리의 일관성이 떨어지고 변수들이 발생할 수 있을거라고 생각했었다
Leets 활동을 하면서 정기모임을 19시에 시작하면 아무리 늦어도 22시에는 동아리 활동이 종료되었었기에 넉넉하게 출석 마감 시간을 목요일 22시로 설정해주기로 했다
구현중 고민했던 내용
처음 사용해보는 자동화 기능이였기에 기능설계를 하면서도 고민이 많았지만, 구현을 하면서도 같은 백엔드 팀원인 강혁이와 로직을 계속 얘기해볼 만큼 정말 고민이 많았었다. 그중에서도 크게 2가지가 기억에 남는데
먼저 첫번째로는 날짜 조회 방식이였다
초기에는 현재 날짜를 기반으로 CardinalRepository를 통해 현재 학기와 연도에 해당하는 출석 데이터를 필터링하는 방식도 고려했다.
Leets에서는 특정 학기에 속한 멤버만 출석하는 것이 아니라, 여러 기수가 동시에 활동할 수 있는 구조였다. 예시를 들어서 설명하면 나는 3기로 처음 활동을 시작했지만, 4기에도 계속해서 활동할 수 있었기 때문에, 4기 모임에도 당연히 출석이 가능했다. 즉, 특정 학기에 속한 멤버만 출석하는 것이 아니라, 모든 기수가 자유롭게 정기모임에 참여할 수 있는 형태였다
그래서 출석 마감 시점에서 중요한 것은 학기 구분이 아니라, 단순히 해당 날짜까지 진행된 정기모임의 출석 데이터를 마감하는 것이었기때문에 CardinalRepository를 활용하여 학기별로 출석 데이터를 조회하는 방식이 올바르지 않다고 판단해서 단순히 정기모임 일정만을 기준으로 출석 데이터를 조회하고 마감하는 방식으로 설계를 단순화했다
두번째로는 정기모임 조회 방식이였다
정기모임 출석을 마감하려면 현재 날짜를 기준으로 진행된 정기모임을 조회하는 과정이 필요했다. 맨처음에는 Meeting(정기모임) 목록을 전부 조회한 후, 시작 시간(start) + 종료 시간(end)을 기준으로 필터링하는 로직으로 고민도 했었지만 해당 방식은 정기모임의 데이터가 많아질수록 조회 시간이 길어지고, 불필요한 DB 부하가 발생할 가능성이 컸기 때문에 해당 방식으론 구현하지 않았다
이를 해결하기 위해 정기모임 상태 필드를 추가해줘서, 정기모임을 생성할 때 미리 상태 필드를 지정해주는 방식을 선택했다
정기모임이 생성될 때, OPEN(진행 중) 상태로 함께 저장하도록 수정해서 출석 마감 시점에서 CLOSE 상태인 정기모임만 조회하여 출석을 마감할 수 있도록 조회 성능을 최적화할 수 있었다
결론
마지막으로 해당 기능을 구현하기 위해서 공부하고 정리했었던 내용을 공유하면서 글을 마무리 하려고한다
@Scheduled
- fixedDelay : 이전 작업이 끝난 시점으로 부터 고정된 시간(ms)을 설정.
- fixedRate : 이전 작업이 수행되기 시작한 시점으로 부터 고정된 시간(ms)을 설정.
cron 속성 : UNIX계열 잡 스케쥴러 표현식으로 작성 - cron="초 분 시 일 월 요일 [년도]" - 요일 : 1(SUN) ~ 7(SAT)
ex) 2019년 9월 16일 월요일 10시 30분 20초 cron="20 30 10 16 9 2" // 연도 생략 가능
특수문자
* : 모든 수.
- : 두 수 사이의 값. ex) 10-15 -> 10이상 15이하
, : 특정 값 지정. ex) 3,4,7 -> 3,4,7 지정
/ : 값의 증가. ex) 0/5 -> 0부터 시작하여 5마다
? : 특별한 값이 없음. (월, 요일만 해당)
L : 마지막. (월, 요일만 해당)
주의사항 - @Scheduled 어노테이션은 매개변수가 없는 메소드에만 적용 가능.
느낀점
Weeth 유지보수가 거의 끝나가는 느낌이라 뿌듯하기도 하면서 또 실제 사용자(5기)를 대상으로 QA를 빨리 받아보고 싶다는 생각이 든다.수정할게 이번에도 많을까요..... 어떻게 됐든간에 이번 QA를 통해서도 의미 있는 피드백을 얻고 보완을 한다면 더 성장할 수 있을거같다
그리고 Leets도 다른 동아리처럼 Makers라는 단체를 만들어서 동아리를 위한 서비스에 오너십을 가지고, 장기적인 유지보수 경험을 키울 수 있는 별도의 기구를 운영한다고해서 나도 참여하기로 결정했다
사실 개발 경험을 단순히 개인적인 프로젝트로 끝내는 것이 아니라, 더 많은 사람들과 협업하면서 꾸준히 서비스의 지속적인 운영과 유지보수 경험을 쌓을 기회를 가지는 것은 내가 추구하는 방향성과도 부합해서 난 거절할 이유가 없긴했다
아쉽게도 2기수 동안 활동했던 Leets는 4기를 마지막으로 직접적인 참여는 끝나지만, Makers를 통해 기술적인 도전을 이어가고 싶다.
개발을 하면서 개인적으로 중요하게 생각하는 것은 프로젝트를 처음부터 끝까지 개발해서 런칭하는 것도 물론 중요하겠지만, 꾸준히 운영과 유지보수까지 고민하면서 서비스를 개선해 나가는 과정이 정말 중요하다고 생각한다
참고한 자료