싸피코피(SSAFY COFFY) 프로젝트 회고
최근 진행한 1인 프로젝트가 어느정도 안정기에 접어들었다고 판단해, 회고를 작성해보려 한다.
사실 프로젝트를 진행하면서 문제점을 해결할 때마다 기록하는게 Best이지만, 개발 기간이 워낙 타이트했고 정신없이 코드를 뿌려대느라 기록할 틈이 없었다..
하루에 1~2시간 자면서 코드짜고 오류잡고 테스트하면서 힘들다는 생각보단 너무 너무 재밌어서 하루가 48시간이었으면 좋겠다는 생각을 몇 번을 했는지 모르겠다.
현재 안정적으로 커피 주문이 이뤄지고 있고, 매일 커피 주문이 진행되고 있다. (내기, 지각 벌칙 등 팀 단위 주문이 종종 몰리는 것 같다.)
카페 사장님으로부터 “너무너무 감사하다…” 라는 말을 매일 듣고 있어 기분이 좋으니 한번 빠르게 시작해보자.
#0 프로젝트 개요
- 진행 기간 : 24.06.29 ~ 24.07.03 (약 5일)
- Next.js, Recoil, Tailwind, shadcn/ui, Radix UI
- Next API Router, FireBase DB, Vercel 배포
- 매일 20 ~ 60 건의 주문 발생, 첫 운영 일주일 간 약 170건 주문
#1 싸피코피는 이렇게 시작됐습니다.
1. SSAFY 광주캠퍼스의 단체 커피 주문
우선 필자는 SSAFY 광주캠퍼스에서 교육을 받고 있다. 삼성전자 광주 사업장에 위치한 이 캠퍼스는 사업장이라는 환경으로 여러 보안 요소들이 존재한다. 그 중 가장 큰 요소는 모든 촬영기기, 저장기기를 봉인하는 것, 그리고 교육생이 카페를 이용할 수 없는 것이다.
믹스커피나 카누 같은 스틱커피 등이 있지만, 한국인에게.. 그것도 개발자를 지망하는 이들에게 씁쓸한 아이스 아메리카노는 뗄레야 뗄 수가 없는 존재이지 않을까.
역시나 교육 초기부터 일부 인원의 주도로 단체 커피 주문이 시작됐고, 어느새부터 구글 폼을 통해 커피 메뉴를 종합하고 한번에 주문하는 커피 주문 현상이 생겨났다.
다만 종합한 커피를 주문하는 과정과 수량 체크 과정을 한 사람이 일일히 손으로 진행하다보니, 주문 오류가 잦았고 불편함이 많았다. (실제로 많은 부분에서 해당 교육생이 많은 희생을 했다.)
2. 커피 주문 서비스를 만들자!
1학기 교육이 끝나고 맞이한 방학 기간을 거의 다 보냈을 무렵, 방학 동안 학습한 내용으로 무엇을 할 수 있을까 고민하다 이 불편함을 직접 해결해보자고 생각했다.
처음 떠올린 컨셉과 계획은, 모든 주문 과정의 자동화. 손으로 일일히 옮겨 적는 극한의 비효율성을 보다 효율적으로 처리할 순 없을까.. 라는 고민에서 출발했다.
다만 아쉬운 점은 계획 시점이 2학기 개강까지 약 4일 밖에 남지 않아, 기획과 설계를 할 시간적 여유가 없었다.
컨셉 기획과 동시에 개발과 디자인을 진행했고, 어플리케이션 화면을 보면서 디자인을 그때그때 수정했다. (혼자서 모든걸 개발해서 가능했다…)
3. 기존의 커피 주문 프로세스는?
아침 9시, 대표 교육생이 메신저 단체방에 커피 수합 시작 글을 올린다. 주문 마감은 11시.
카페 메뉴판을 검색해 커피 가격을 찾아보고 구글 폼에 메뉴를 입력하고 계좌이체 송금한다.
아이스 아메리카노 한 잔에 1,500원. 두 잔을 주문하면 3,000원이다.
내기라도 걸려서 여러 잔을 사야하면, 직접 계산을 해서 금액을 맞춰 보내야 한다.
주문 마감이 되면, 메신저에 주문 마감 글이 올라온다.
내가 주문한 커피 목록은 대표 교육생에게 개인적으로 연락을 해 확인해야 한다.
커피 배달을 수령할 사람이 필요하므로, 커피 주문자들 중에서 제비뽑기로 3명을 선정한다.
점심시간에 커피 배달이 도착하면 3명이 밖으로 나가 커피를 가져온다.
메신저 단체방에 커피 도착 메시지를 올리면 각자 주문한 커피를 가져간다.
#2 무엇을, 어떻게 바꿨을까?
1. 장바구니를 상태관리로 금액을 자동 계산하자! → Recoil 도입
커피 메뉴는 변하지 않는다. 단체 주문 특성상 시즌/한정 메뉴 주문은 제외하면, 메뉴의 이름과 가격 및 옵션(샷,펄,우유,시럽 추가 등)은 변동이 없다. 주문하는 카페 기업에서 따로 API를 제공하지 않으니, 이미지와 메뉴, 그리고 카페 매장의 메뉴 가격을 수집해 데이터를 생성한다.
내가 장바구니에 담은 메뉴를 자동으로 집계해 가격과 수량, 옵션을 보여줬으면 했다.
장바구니를 전역 상태로 관리하면, 처리하기도 쉽고 주문 과정과의 연계도 괜찮을 것 같았다.
시간이 많지 않기에 가장 쉽고 익숙한 Recoil을 선택했다.
장바구니를 Atom으로 관리하고, 메뉴별 금액과 수량, 장바구니에 담은 총 금액을 Selector에 담아 보여주면 되겠다고 생각했다.
장바구니의 각 부분의 UI를 구상하고 쪼개서, CartList와 Item 컴포넌트로 구분해 구현했다.
2. 원하는 커피를 빠르게 찾고 싶어! → NEXT JS API ROUTER로 카테고리 만들어!
방학 중에 NEXT JS를 학습했으므로 이를 활용해보고 싶었다. 페이지 라우팅, 앱 라우팅 방식 중 고민했는데 앱 라우팅이 조금 더 복잡해 보였고 더 학습해보고 싶어 앱 라우터 방식을 선택했다. (useClient를 선언하는게 뭔가 특별해 보이고 좀 있어 보이기도 했다… 대체 무슨 생각이었을까…)
초기엔 커피 메뉴를 DB에 저장하려 했지만, 메뉴가 많지 않고 추가나 삭제가 없으므로 굳이 DB에 넣을 것도 없이 그냥 json 파일로 목업 데이터처럼 만들기로 했다.
메뉴 목록을 보고 커피를 주문하는 상상을 했다. 모바일이라면 화면에 보이는 메뉴가 많아봤자 2~3개일 텐데 메뉴가 많지않다고는 했지만, 그래도 스크롤을 일일히 내려가며 찾는건 너무 불편할 것 같았다.
다행히 카페의 메뉴들은 커피, 음료, 티, 쥬스 등으로 카테고리가 나눠져있었고, DB Json 파일에도 이를 기록해뒀다.
백엔드와 DB까지 구성할 시간도 없었고, Next JS의 API Router 기능을 사용해보기에 좋다고 생각해 API를 동적으로 라우팅 처리해 …/api/menu/[category].tsx 로 서버리스하게 구현했다.
그리고 페이지에서 선택하는 메뉴 필터(카테고리)에 따라 해당 API를 요청해, 메뉴 리스트를 렌더링했다.
3. 그래서 이 커피 주문하신 분이 어느 분이죠…? → 메신저 봇을 통한 메시지 발송
커피 주문에서 자동화 불가능 한 부분을 생각해봤다.
- 카페 사장님께 문자로 직접 주문
- 프로젝트 이전에 문자로 사장님께 주문을 전달하던 프로세스가 있었다.
- 주문 내역을 카톡 메시지로 자동 전달하는 것도 고려했으나, 사장님이 문자에 더 익숙하실 수 있고, 사장님이 안계시면 직원들이 내역을 전달받아야 하기에 우선은 기존 프로세스를 유지하기로 했다.
- 커피 배달 수령
- 주문자 중 3명을 뽑아 사장님이 가져다 주시는 커피를 수령해야 한다.
- 3명 뽑기는 난수로 추출하면 되겠다고 생각했다.
- 계좌이체 거래이므로 주문 삭제(취소)는 관리자만 가능
- 계좌 입출금 내역과 주문 내역의 통일을 위해 관리자만 주문 취소가 가능하다.
- 송금 실패, 오류 등의 문제로 이체 실패하거나 주문을 취소하고 싶으면 Footer에 연결한 메신저 주소를 통해 연락해야 한다.
이 외의 과정은 모두 자동화한다는 생각이었다.
커피가 도착해 수령해야 하거나, 문제가 생겼을 경우 커피 주문자 정보를 알고, 주문자에게 연락할 수단이 있어야 했다.
다행히 교육장에서 사용하는 메신저는 ID를 통해 메시지를 보낼 수 있고, 봇을 통해 자동으로 메시지를 보낼 수 있었다.
커피 주문 이전에 반드시 주문자 정보(이름, 메신저 ID, 반)를 입력하도록 했다.
가장 중요한 것은 ID 였는데, 모든 연락이 ID를 통해 이뤄지므로 입력한 ID로 직접 메시지를 보내 인증 하는 과정까지 구현하고 싶었지만, 주문의 편의성을 위해 생략했다.
다만, 종종 ID의 일부 글자가 누락되거나 오타가 있는 경우가 있어 어떻게 해결해야할지 고민중이다.
(모든 교육생 ID를 DB에 두고 필터링 하는 것을 생각해봤는데… 음… 여러모로 좋지 않은 것 같다.)
이외의 기능으론 봇을 통한 주문 시작/마감 전 알림이 있고, 주문이 마감되면 전체 주문 내역과 메뉴 통계, 그리고 커피를 수령할 인원 선정이 있다.
인원 선정의 경우 난수로 3명을 뽑게 되는데, 이때 단순 주문자 나열이 아니라 시킨 메뉴 갯수로 뽑게 된다.
다시 말해서 A가 5잔을 시키면 확률이 그만큼 증가하는 것이다.
4. 사장님! 오늘 주문입니다…! → FireBase로 주문 데이터 관리
교육생이 계좌이체로 주문한 내역은 FireBase의 FireStore Database로 들어간다.
주문 객체를 어떻게 구성할까 고민했는데, 몇 번 테스트를 하면서 필요한 항목을 그때 그때 추가했다. 그리고 한 사람이 여러 개의 메뉴를 주문할 수 있으므로(내기, 친구꺼 등) menus에 여러 메뉴를 넣을 수 있도록 했다.
주문자는 자신의 주문 내역(핫/아이스 여부와 메뉴, 옵션, 가격)을 볼 수 있다. 이전에 언급한 대로 계좌이체 거래이므로 임의 삭제(주문 취소)는 불가하게 하였다.
11시가 되면 10분 후 주문이 마감된다는 알림이 발송된다.
11시 10분이 되면 주문이 마감되는 동시에, 전체 주문 내역과 메뉴 통계, 수령인원 선정이 진행된다.
그리고 이 메뉴 통계를 캡쳐해 사장님께 문자로 전달드리면 점심시간에 커피가 배달된다.
초기에 해당 봇을 구동하는 서버를 배포해서 자동화할까 하는 생각을 했었는데, 가끔 주문 마감 후에 수정/취소가 들어오기도 하고 주문 내역을 수정해야 하는 경우가 간간히 있어 그냥 로컬에서 실행하기로 결정했다.
운영 시간이 평일 9시~11시 10분으로 교육장에서 PC를 사용하고 있는 상황이므로 서버 가동에도 부담이 없는데, 혹시나 지각/결석을 하게 되거나 중간에 취업 등으로 교육장을 나가지 못할 땐 아마 배포로 자동화를 해야 할 듯 싶다.
#3 서비스 운영은 처음이라… (트러블 슈팅)
(서비스라 하기엔 부족하지만…)
1. 테스트가 부족했다.
이전의 커피 주문을 내가 담당하지 않았기에, 프로세스를 파악하는데 시간이 걸렸다. 개강 직후 서비스를 시작해야 했기에 제대로 된 테스트도 못하고 바로 개시하게 되었다.
테스트 코드와 철저한 검증 과정을 거치진 못했지만, 그래도 개발 중 틈틈히 시뮬레이션 해보고, 스스로 직접 테스트하며 많이 보완했다고 생각했는데 생각지도 못한 문제가 발생했다.
개발 속도와 편의성을 위해 유저 정보를 잠시 전역 상태로 관리했던 것을 깜빡했던 것…
주문 관련을 제외하면 따로 DB를 두지 않았으므로 로컬 스토리지든 세션 스토리지든 어떤 방식을 써서든 저장을 해야했는데, Recoil에서 관리를 했던 것이다.
주문 중에 새로고침을 하거나 페이지 이동 중 문제가 발생하면 유저 정보가 날아가 버려, 개시 첫 날 주문자 정보가 누락된 주문이 몇 건 발생했다.
다행히 급하게 수소문해 누락 인원들을 찾았지만, 주문한걸 깜빡했는지.. 무언가의 오류였던지… 1잔의 커피가 남게 되었다.
그래도 커피 한 잔으로 문제를 발견한건 다행이 아닌가.. 싶다.
그날 저녁 바로 Recoil에 담았던 유저 정보를 세션 스토리지에 암호화해서 보관하도록 변경했다.
개발 기간을 변명삼긴 싫지만, 테스트를 철저하게 진행했다면 아마 미리 발견할 수 있었을까?
“테스트를 진행하지 않으면, 유저가 테스트를 겪게 된다.” 라는 말을 절실히 체감한 순간이었다.
2. 카카오페이 송금 버튼을 제거하다.
초기부터 주문의 편의성 또한 고려했기에, 결제(송금)을 카카오페이로 진행하면 어떨까? 라는 생각을 했다.
마침 카카오페이에 QR코드를 생성해 송금하는 기능이 있었고, 이를 적용해 PC화면에 생성된 QR을 촬영해 모바일로 바로 송금하면 좋겠다고 생각했는데…
교육장에선 카메라를 사용할 수 없다는 것을 깜빡했다…(보안 사업장..)
모바일에선 QR코드 생성 없이 바로 카카오페이가 실행돼 계좌이체 화면으로 연결됐지만, 이래서야 카카오페이 버튼을 넣은 의미가 없다고 생각해 결국 카카오페이 관련 기능을 제거하기로 했다.
다행히 기존에 일반 계좌 송금(계좌번호 보여주기) 버튼과 카카오페이 버튼을 함께 구현해두었고, 계좌번호는 클립보드에 복사 할 수 있도록 했으므로 큰 문제는 없었다.
오히려 1. 일반 계좌, 2. 카카오페이 계좌 로 분산돼 입금되던 금액이 하나의 계좌로 통일돼 입금되면서 관리의 편의성이 좋아졌다.
타겟 고객층(교육생 환경)에 대한 고려, 그리고 기획과 페르소나 선정 과정이 얼마나 중요한지 직접 느낀 경험이었다. 이 역시 조금 더 탄탄한 기획을 진행했다면 분명 예측할 수 있었을 것이다..
3. 핫/아이스 여부가 누락되다.
정말 정말 부끄럽게도… 개시 후 무려 3일이 넘도록 봇이 생성하는 최종 주문 통계에 핫/아이스 여부 없이 메뉴 이름(옵션)과 수량만 집계되고 있었다.
그동안 뜨거운 음료 주문이 없었기에 전혀 모르고 있었는데, 정말 다행히도(?) 핫 라떼 3잔 주문이 같은 날 발생했고…
당연히 핫/아이스 여부가 없으니 전부 아이스로 왔고…
그렇게 주문자들은 아이스 라떼를 받았고…
필자 혼자 처리하는걸 알고 있으니 불평을 하지도 못하고…
여러모로 너무 미안하고 죄송하고 마음이 불편한 하루였다..
당연히 모든 분께 직접 찾아가서 사과했고, 혹시나 진짜 불편을 느끼셨으면 전부 보상까지 할 생각이었는데…
다행히 주문하신 분들께서 넓은 아량으로 이해해주셨고 오히려 덕분에 커피 잘 마시고 있다고 응원해주셨다.
정말 정말 부끄러운 동시에 이때 받은 응원이 기억에 정말 많이 남았다.
뭔가 뿌듯하면서도 민망하기도 하고, 또 한편으론 얼떨떨하기도 하고..
개발하면서도 재밌고 좋았지만, 아마 이 에피소드가 기억에 더 오래오래 남지 않을까.
#4 마무리
개발하면서 많은 생각을 했지만 가장 중요한 것은 “내가 사용하면서 불편함이 없어야 한다.” 였고, 실제로 최대한 그런 부분에 신경을 쓰며 개발했다.
전체적으로 급하게 진행한 티가 많이 나고, 부족한 부분이 많지만 그래도 사용해주는 교육생들이 항상 좋은 평가해주고 응원해줘서 매일 매일 뿌듯함을 느끼고 있다.
주문 내역에 주문이 쌓이는 걸 보면 필자에게 1원도 떨어지는 것이 없지만 괜스레 기분이 좋아지기도 한다.
현재 기수 지인을 통해 주문을 하는 다른 기수도 있고, 여러모로 다양한 요청과 의견을 받고 있는데 어떻게 해야하나 고민도 많이 하게 되는 것 같다.
코드나 여러 설정들을 물려주거나 수정해 사용하도록 하는 것도 생각하고 있는데, 다들 열정이 넘치고 개발자가 되겠다는 꿈을 갖고 오는 사람들이니 굳이 나서지 않더라도 뭔가 더 그럴듯한 솔루션을 찾지 않을까 하는 생각도 든다.