3편에서는 관리자 페이지를 만들면서 예약 승인, 좌석 모니터, 회원 관리, 매출 정리 같은 기능을 어떻게 추가했는지 정리해봤습니다. 이번에는 기능을 만든 뒤 실제로 운영할 수 있게 다듬었던 부분들을 적어보려고 합니다.
처음에는 예약 기능과 관리자 페이지가 있으면 어느 정도 완성됐다고 생각했습니다. 사용자가 예약하고, 관리자가 승인하고, 좌석 상태를 확인할 수 있으면 기본 흐름은 돌아가기 때문입니다. 그런데 실제 운영을 생각해보니까 그것만으로는 부족했습니다.

가장 먼저 신경 쓴 부분은 결제 확인이었습니다. 기존에는 돈 관리도 수기로 하고 있었기 때문에, 온라인 예약을 받으려면 결제 여부를 확인할 수 있어야 했습니다. 처음에는 예약 정보에 결제 방식만 넣어봤습니다. 현장 결제인지, 온라인 결제인지 정도만 구분하면 될 줄 알았습니다.
그런데 온라인 결제는 말로만 “결제했습니다”라고 받으면 관리자가 확인하기 어렵습니다. 그래서 결제 캡처 이미지를 업로드하게 해봤습니다. 사용자가 온라인 결제를 선택하면 결제 증빙을 올리고, 관리자는 관리자 페이지에서 그 이미지를 보고 승인할 수 있게 했습니다.
처음에는 업로드 파일을 서버에 저장하는 방식으로 해봤는데, 배포 환경에서는 로컬 파일 저장이 안정적이지 않을 수 있었습니다. 그래서 나중에는 Cloudinary를 연결해서 업로드 이미지를 외부 저장소에 보관하도록 바꿨습니다. 그리고 아무 URL이나 결제 증빙으로 들어오면 안 되기 때문에, 결제 증빙 URL도 검증하도록 수정했습니다.
알림 기능도 추가해봤습니다. 처음에는 사용자가 예약을 넣고 나면 직접 새로고침해서 상태를 확인해야 했습니다. 그런데 실제 사용자 입장에서는 예약이 승인됐는지, 거절됐는지, 취소됐는지 바로 알 수 있어야 했습니다. 그래서 예약 승인, 거절, 취소, 연장 요청 같은 이벤트가 생기면 알림을 보내는 구조를 만들었습니다.
이 과정에서도 생각보다 고려할 게 많았습니다. 알림 전송이 실패했다고 예약 처리까지 실패하면 안 됐습니다. 예약 승인은 정상적으로 처리하고, 알림은 실패하더라도 전체 기능이 멈추지 않게 해야 했습니다. 그래서 예약 처리 로직과 알림 전송을 분리해서, 알림은 부가적으로 실행되도록 만들었습니다.

채팅 기능도 넣어봤습니다. 처음에는 예약 관련 문의가 있으면 따로 연락하면 된다고 생각했는데, 운영을 생각하면 사용자와 관리자가 앱 안에서 바로 문의할 수 있는 게 편했습니다. 그래서 사용자-관리자 채팅을 만들고, 이미지 첨부도 가능하게 했습니다.
처음 채팅 이미지를 처리할 때는 보안 문제가 생길 수 있었습니다. 이미지 URL이나 첨부 데이터를 그대로 화면에 넣으면 XSS 같은 문제가 생길 수 있어서, 클릭 이벤트에 데이터를 직접 넣는 방식을 피하고 안전하게 렌더링하도록 수정했습니다. 단순히 “이미지가 보이면 된다”가 아니라, 사용자가 올리는 데이터는 항상 조심해서 다뤄야 한다는 걸 느꼈습니다.
PWA도 적용해봤습니다. 앱처럼 홈 화면에 추가해서 사용할 수 있으면 직원이나 사용자가 더 편하게 접근할 수 있을 것 같았습니다. 그래서 manifest, 아이콘, 서비스 워커 캐시를 추가했습니다. 처음에는 캐시를 넣으면 성능이 좋아질 줄 알았는데, 실제로는 캐시 때문에 오래된 화면이 남거나 Safari에서 리다이렉션 문제가 생기기도 했습니다.
그래서 서비스 워커 캐시 버전을 계속 올리고, HTML이나 JS처럼 자주 바뀌는 파일은 캐시 정책을 조정했습니다. 특히 관리자 페이지처럼 최신 데이터가 중요한 화면은 캐시가 오히려 문제가 될 수 있어서 더 조심해야 했습니다.
모바일 대응도 오래 걸렸습니다. 실제 사용자는 대부분 모바일로 접속할 가능성이 높았기 때문에, 화면이 PC에서만 잘 보여서는 의미가 없었습니다. 하단 네비게이션, 스크롤, 터치 이벤트, 로그인 버튼, 팝업 위치를 계속 수정했습니다. iOS나 삼성 브라우저에서는 스크롤이 잠기거나 버튼이 잘리는 문제가 있어서, 레이아웃을 여러 번 바꿔봤습니다.
특히 모바일에서는 작은 UI 차이가 사용성을 크게 바꿨습니다. 버튼이 살짝 겹치거나, 팝업이 화면 밖으로 나가거나, 하단 네비게이션이 내용을 가리면 바로 불편해졌습니다. 그래서 모바일에서는 보기 좋은 디자인보다 실제로 눌리고, 스크롤되고, 안 가려지는 구조가 더 중요했습니다.
보안도 중간부터 계속 보강했습니다. 처음에는 기능 구현에 집중했지만, 관리자 페이지와 파일 업로드가 들어가면서 보안 처리가 중요해졌습니다. 관리자 페이지는 프론트에서만 막으면 안 되고, 서버에서도 관리자 권한을 확인해야 했습니다. 그래서 관리자 페이지 접근을 서버에서 다시 검증하도록 바꿨습니다.
또 업로드 파일, 채팅 첨부 이미지, 공지 이미지처럼 사용자가 넣는 데이터는 전부 위험할 수 있다고 보고 검증을 추가했습니다. 요청 횟수 제한, MongoDB 쿼리 sanitizing, CSP, 민감 경로 차단 같은 설정도 넣었습니다. 만들면서 느낀 건, 운영 시스템은 기능이 많아질수록 공격 표면도 같이 넓어진다는 점이었습니다.
결국 4편에서 정리한 작업들은 눈에 확 띄는 핵심 기능은 아닐 수 있습니다. 하지만 실제로 서비스를 운영하려면 꼭 필요한 부분들이었습니다. 결제 증빙이 있어야 관리자가 신뢰할 수 있고, 알림이 있어야 사용자가 상태를 알 수 있고, 채팅이 있어야 문의가 줄어들고, PWA와 모바일 대응이 되어야 실제 환경에서 쓸 수 있고, 보안 처리가 되어야 관리자 기능을 안전하게 열 수 있었습니다.
처음에는 예약 기능만 완성하면 끝이라고 생각했습니다. 그런데 실제로는 그 이후부터가 운영 품질을 만드는 과정이었습니다. 기능이 “된다”에서 끝나는 게 아니라, 사용자가 헷갈리지 않고, 관리자가 실수하지 않고, 모바일에서도 안정적으로 동작하고, 보안적으로도 문제가 없게 계속 다듬는 작업이 더 오래 걸렸습니다.
다음 글에서는 앱 기능 자체보다는 서비스를 실제로 공개하기 위해 했던 부가 작업들을 정리해보려고 합니다. 카카오 로그인과 구글 OAuth를 붙이면서 겪었던 문제, 도메인 연결, Google Search Console 색인, sitemap과 robots 설정, 배포 환경에서 생긴 문제들을 다뤄보겠습니다.