모두의 웨이터 : 회고
힘들었지만 얻은것도 많았던 프로젝트
들어가기 전에
프로젝트를 시작한지 대략 반년이란 시간이 흘렀다.
처음에 기획했던 웨이팅 관리 서비스에 테이블 오더 서비스가 추가 되었다.
3개월 정도면 충분하겠다 싶었지만 반년이 지나서야 프로젝트를 마무리 할 수 있었다.
물론 가게가 바빠져 쉬었던 기간도 있었지만 중간중간 만나는 문제들을 해결하는데 생각보다 많은 시간을 소비한 것이 가장 큰 원인이라고 생각한다.
이 회고에서 프로젝트를 진행하면서 마주쳤던 문제들과 느낀점을 기록함으로써 다음엔 같은 실수를 반복하지 않길 바란다.
무슨 프로젝트야?
“모두의 웨이터”는 매장의 웨이팅을 관리하고 손님들이 테이블에서 메뉴를 주문하고, 그 주문들을 관리 하는 서비스이다.
이 프로젝트를 시작한 계기는 아래 글에서 작성하였다.
웨이팅의 경우 매장을 방문한 손님이 태블릿을 통해 웨이팅을 등록하게 되면 카카오 알림톡으로 현재 웨이팅 상황과 매장의 메뉴를 미리 볼 수 있다.
매장에선 입장 메시지를 전송하여 간단하게 웨이팅을 관리 할 수 있고, 손님들의 메뉴 선택 시간을 줄임으로 테이블 회전을 늘릴 수 있다.
테이블 오더는 손님은 테이블에서 태블릿을 통해 주문하고, 매장에선 들어온 주문을 통해 주방 주문서 프린트, 홀 메뉴 서빙 관리, 결제 등의 기능을 제공한다.
이 프로젝트를 해야 했던 이유
처음엔 웨이팅을 수기로 관리하고 있었다.
손님이 나간 자리를 정리하고나면 매장 문을 열고 나가 웨이팅 손님을 찾았고, 이는 매우 큰 동선낭비를 가져왔기 때문에 웨이팅 관리 서비스를 만들고자 하였다.결과는 나름 성공적이었고 나는 손님을 찾기 위해 뛰어다니지 않아도 되었다.
이제 손님이 주문을 한다며 직원을 불러놓고 그제서야 메뉴를 고르는 손님, 어떤 메뉴가 잘 나가는지 물어보는 손님, 메뉴 하나하나 이건 뭐에요? 라며 물어보던 손님 등 시간과 동선의 낭비가 있는 것이 분명했다.
부모님도 똑같은 생각을 하고 계셨고, 테이블 오더 서비스를 도입하기로 결정했다.
찾아본 결과 테이블 오더 서비스는 생각보다 많았다.
그 중 3군데 정도 견적을 받아보았고 약정과 비용을 생각해서 직접 만들게 되었다.
결과는 1500만원 -> 300만원으로 약 4/5의 비용을 절감 할 수 있었으며, 주말 홀 기준 3인 -> 2인으로 인원을 줄일 수 있었고 1인 구인효과로 한달 기준 약 800,000원의 인건비를 절감 할 수 있었다.
마주쳤던 문제점
Java를 공부해보자! 하고 시작한 첫 프로젝트였기에 무수히 많은 작고 큰 문제들이 있었다.
언어는 물론 프레임워크, 관계형 DB 등 모두 새롭게 익혀야 하는 것들 이었기에 문제가 많을 수 밖에 없었다.
그 중에서 가장 뼈 아프고 기억에 남은 문제를 기록한다.
설계는 탄탄하게
지금까지 엔티티와 DTO를 얼마나 많이 수정했는지 모른다.
한 번 변경될 때 마다 API 스펙은 변경되었으며 그 API를 사용하는 부분은 하나하나 수정이 필요했다.
닥치는대로 코드만 작성하고 그때그때 필요한 것들을 추가하곤 했다.
그러다보니 버그 발생이 상당히 잦을 수 밖에 없었고 찾는데 시간을 소요했다.
기능을 붙이는데 상당히 오랜 시간이 걸리고 회의감이 들기 시작했을 때 어떤 기능이 필요한지 요구사항은 무엇인지 정리를 하기 시작했다.
정리를 하고 나서야 하나의 기능에 대한 요구사항을 단위로 개발을 시작했고, 이는 개발 속도는 물론 API 스펙 변화를 최소화 할 수 있었다.
중간에 정신차리지 않았다면 아직도 프로젝트 진행중이지 않았을까..?
UUID는 순서를 보장하지 않아
프로젝트 초기 2023년 3월 26일. 4월 축제기간을 앞두고 급하게 웨이팅 관리 서비스만 개발하고 배포하여 축제기간 전에 시범 운영을 시작했다.
오후 4시경 부모님의 전화를 받았는데 대기 번호가 중복이 있으며 급하게 삭제하려 해도 삭제 또한 불가능하고 손님들이 우리가 먼저 왔는데 왜 저 팀 먼저 들어가냐는 것이었다.
전화를 받자마자 확인하니 대기열이 10팀 넘게 걸려있는 상황이었고 이 서비스로 인해 식당 운영에 차질이 생기게 되었다.
먼저 로그부터 확인해보니 sequence처럼 증가해야 할 대기 번호가 6번 -> 7번 -> 8번이 아닌 7번 -> 6번 -> 7번 같이 뒤죽박죽으로 증가되고 있었다.
대기 번호는 마지막 웨이팅을 기준으로 생성하고 있었기 때문에 테이블을 조회 해보니 순서가 일정하지 않을 것을 확인했다.
비인증 사용자도 웨이팅 취소가 가능해야 했기에 BigInt를 사용하면 아무나 다른팀의 대기를 취소시키기 쉬울 것이라 생각하고 유추하기 힘든 UUID를 사용하여 대기 번호를 생성하였는데 이로인해 버그가 발생한 것이었다.
UUID를 사용하면서 순서 보장에 대해 크게 생각하지 않았고, 2~3개 정도의 웨이팅을 가지고 테스트 했을 때 잘 동작했기에 너무나 당연하게 “순서가 보장되고 있다”라는 생각을 하고 있었다.
데이터 10개 아니 단 5개의 데이터만 가지고 테스트를 진행했다면 이러한 문제는 발생하지 않았을거라 생각한다.
웨이팅을 조회 할 때 생성일을 기준으로 정렬함으로써 간단하게 해결 할 수 있었지만 불편을 겪었을 부모님과 손님들, 그리고 나 대신 욕 먹은 직원분께 너무 죄송했다.
동시성 이슈 — 웨이팅 또 너야
UUID 이슈가 있고나서 얼마 지나지 않아 축제기간이 되었다.
이번 동시성 이슈는 축제기간에 발생하였는데 이 또한 UUID 이슈와 마찬가지로 대기번호 중복이 발생했다.
마침 매장에서 내가 일하던 중이었기에 빠르게 수기로 전환하면서 큰 문제는 겪지 않았지만 도저히 발생 원인을 알 수 없었다.
로그를 확인해도 순서가 뒤죽박죽이라거나 크리티컬한 이슈는 확인 할 수 없었으나 1초가 안되는 시간 사이 같은 요청이 10번 이상 들어오는 것을 확인 할 수 있었다.
원인을 찾기 위해 이슈를 재연하고자 하였지만 실패했고 한 주가 지나 또 다시 같은 상황이 발생하여 개발자 친구에게 이슈를 공유하였고 DB Lock과 같은 백엔드에서의 키워드와 더불어 “프론트에서의 문제일 수도 있다.” 라는 조언을 듣게 되었다.
매장의 현장을 재현하고자 네트워크 쓰로틀링을 걸고 웨이팅 등록을 시작하니 원인을 찾을 수 있었다.
등록 버튼을 누르고 요청이 전송중 일 때 버튼이 disabled 되지 않아 발생한 문제로 매장 야외에 약한 와이파이로 인해 손님들이 요청 완료 전에 등록 버튼을 연타하면서 동시성 이슈가 발생하게 된 것이다.
동시성 이슈에 대해 들어만 봤었는데 이번 일을 통해 경험하게 되었다.
프론트에서 등록 버튼에 disabled 속성을 주었고, 서버에서 redisson을 사용하여 매장 ID를 기준으로 웨이팅 등록, 취소에 Lock을 걸어주면서 해결 할 수 있었다.
프로젝트를 진행하면서 좋았던 점
프로젝트 기획단계 부터 최종 운영까지 전체 프로세스를 겪어 볼 수 있었던 것.
각 프로세스를 한번씩 경험하며 완주함으로써 얻을 수 있는 게 참 많은 것 같다.
설계, 고민은 많을 수록 좋아
지금까지 설계에 대해 큰 고민이 없었다.
팀 프로젝트를 진행하면서도 요구사항이 추가되거나 변경된 적 없었으니까..하지만 이번 프로젝트를 진행하면서 지금까지도 추가되는 부모님의 요구사항을 수용하며 설계에 많은 시간을 쏟더라도 탄탄히 하자는 교훈을 얻을 수 있었다.
만약 설계가 탄탄했다면 추가, 확장, 변경에 쉽게 대응할 수 있었을 것이라 확신한다.
새로운 기술스택 맛있는데?
이번 프로젝트에 사용된 언어, 프레임워크, DB 전부 처음 사용하는 것이다.
자바를 새롭게 학습하고, 스프링 프레임워크를 공부하며 OOP의 특징을 살리기 위해 제공하는 기능들, 이를 제공하기 위해 사용된 여러 디자인 패턴들을 보며 감탄했다.
해당 기술을 사용하면서 아직 겉핥기 수준이지만 깊이 있게 공부하고 싶은 마음이 들게 만드는 기술임은 분명하다.
처음 DI 를 맛보았을 때의 충격이란..
프로젝트를 진행하면서 아쉬웠던 점
스프링 부트를 사용하면서 프론트엔드로 Thymeleaf를 사용했다.
스프링과의 통합이 잘 되어있다고 듣기도 했고 간단하고 빠르게 개발하고자 타임리프를 적용했다.
하지만 타임리프를 사용하면서 불편함이 있었고 이러한 부분은 아쉬움이 남았다.
그립습니다.. Typescript
초반 설계를 탄탄히 하지 못해 API 스펙이 변경 될 때 마다 버그와 마주쳤고, 타입스크립트의 타입 추론이 너무 그리웠다.
API 스펙이 변경 되었을 때 타입스크립트를 사용하고 있었더라면 컴파일 시점에 다 잡아 주었을텐데..
프론트 변경 사항 반영이 느려요
타임리프의 수정 사항을 확인하려면 gradle을 다시 빌드 해야 했다.
물론 Intellij 설정에서 빌드를 Intellij로 변경하면 조금 빨라지긴 했지만, 만족스럽진 못했다.
한번의 수정 사항을 확인하려면 3~4초 정도의 빌드 시간이 필요했고, 못해도 수백, 수천번 다시 실행했으니 꽤 큰 시간이다.
생산성이 저하되고 있다는 것을 가장 크게 체감 할 수 있었다.
느낀점
부족한게 많지만 도전적인 프로젝트였다는 생각이 든다.
영수증 프린터와 카드 리더기를 연동해야 할 땐 눈 앞이 깜깜했었는데 말이다.
매장에서 실 사용을 위해 태블릿과 거치대, 프린터 등 구입에 큰 돈을 사용할 때 무서웠었고, 부모님께 전화가 걸려올 때 마다 ‘뭐가 잘못된건가?’ 라는 생각에 긴장했으며, 손님들이 태블릿을 통해 주문을 하고 “이야 잘해놨네” 라고 했을 땐 뿌듯했다.
주방에 주문서가 안나오면 어쩌나, 결제 도중 잘못된 금액으로 결제되면 어쩌지와 같은 공포, 며칠 간 모니터링 후 크리티컬한 이슈가 없다는 안도감, 날 믿고 맡겨주신 부모님에 대한 감사함, 잘했다며 칭찬해주시는 것에 대한 뿌듯함 등 많은 감정을 느낄 수 있었던 프로젝트였다.
앞으로의 계획
현재 프로젝트를 개선이 가능한 부분을 찾아 개선하거나, 기능을 덧붙여 서비스를 고도화 할 수도 있을 것이지만, 이는 잠시 후순위로 미뤄 두려한다.
프로젝트 진행 중 좋았던 점에도 적어놨듯 기술 학습에 깊이 있게 파고들고 싶은 마음이 너무 크기 때문이다.
기초가 너무 부족하다는 생각이 들어 처음부터 시작해보려 나름대로 학습 순서를 정해 두었다.
자바 -> DB -> 스프링 -> JPA -> 스프링 시큐리티
학습 도중 조바심과 조급함을 느끼겠지만 그런 마음은 접어두고 천천히 완주를 목표로 달리다 보면 목표에 도달할 수 있을거라 생각한다.