고택을 예약한다는 것 — '이음'을 만들며 배운 것

고택은 왜 찾기 어려울까요

고택에서 하룻밤 묵어 보고 싶다는 생각, 한 번쯤 해보셨을 겁니다.
그런데 막상 찾아보면 막막합니다. 어느 집이 손님을 받는지, 방은 몇 개인지, 어떻게 예약하는지가 집마다 다 다릅니다. 무엇보다 그 집이 어떤 집인지, 누가 살았고 어떤 이야기가 남아 있는지는 거의 알 수 없습니다.
좋은 고택은 많은데, 그 이야기가 숨어 있었던 겁니다.
방보다 이야기를 먼저

'이음'은 그 숨은 이야기를 찾아내 보여 주는 데서 시작합니다.
첫 화면에서 고택은 '종가에 머물다'처럼 주제별로 묶여 있습니다. 고택을 누르면 가격표 대신 에디터가 쓴 글이 나옵니다. 담장이 없는 집, 누마루에 앉아 보는 시간, 함께 걸어 보면 좋은 곳.
이런 글을 담으려면 게시판 하나로는 부족했습니다. 글 하나를 에디터 노트, 고택 연혁, 사진·영상 갤러리 같은 조각으로 나눠 저장했습니다. 특히 연혁은 본문 속 글자가 아니라 한 줄 한 줄 따로 저장했습니다. 편집기 안에서 표나 목록을 고치다 보면 모양이 깨지기 쉬워서입니다.
국가유산 지정 정보는 고택이 아니라 글에 붙였습니다. 같은 고택이라도 글마다 소개하는 문화유산이 다를 수 있기 때문입니다.
방마다 사정이 다릅니다

예약 시스템을 만들면서 가장 먼저 부딪힌 차이는 이것이었습니다. 호텔은 똑같은 방이 여러 개지만, 고택은 방 하나하나가 다릅니다.
그래서 재고를 '객실 종류'가 아니라 방 하나, 하룻밤 하나 단위로 관리했습니다. 2박을 예약하면 그 방의 밤 두 칸이 채워지는 식입니다. 그리고 데이터베이스에 '같은 방의 같은 밤은 한 번만 기록된다'는 규칙을 걸었습니다. 두 사람이 같은 순간에 같은 방을 눌러도 한 명만 성공합니다. 앱 예약과 고택 주인이 직접 받는 전화 예약도 같은 길을 지나게 했습니다. 두 곳에 따로 적으면 같은 방이 두 번 팔리기 때문입니다.
요금도 밤마다 따로 정합니다. 특정한 날, 시즌, 요일 순으로 우선하고, 공휴일과 그 전날은 공공데이터의 특일 정보로 따로 고를 수 있습니다. 요금이 정해지지 않은 밤이 하나라도 끼어 있으면 예약을 막습니다. 실수로 0원에 팔리는 일을 없애기 위해서입니다.
결제창에 가 있는 몇 분
예약에서 가장 까다로운 순간은 손님이 결제창에 가 있는 몇 분입니다.
예약 버튼을 누르면 그 방은 잠시 그 손님 몫으로 잡아 둡니다. 결제를 끝내면 확정되고, 정해진 시간이 지나면 자동으로 풀립니다. 여기까지는 단순해 보이지만, 실제로 만들어 보니 두 문제가 반대 방향에서 부딪혔습니다.
결제를 포기하고 나간 손님 때문에 방이 한동안 묶였습니다.
그렇다고 화면을 벗어나는 순간 방을 풀어 버리면, 모바일 결제는 원래 결제 앱으로 잠깐 넘어갔다 돌아오기 때문에 결제하는 도중에 방이 다른 사람에게 팔립니다.
그래서 방을 풀기 직전에 결제사에 '이 예약, 정말 결제 안 됐나요?'를 한 번 더 묻게 했습니다. 결제가 되어 있으면 풀지 않고 확정합니다. 반대로 방이 이미 풀린 뒤에 결제가 들어오면 자동으로 전액 취소합니다. 뒤로 가기로 돌아온 화면에서는 방을 다시 잡습니다. 이 한 구간을 다듬는 데만 여러 차례 고쳐 쓰는 과정을 거쳤습니다.
돈이 오가는 곳은 두 번 확인합니다
결제와 환불 쪽에서는 '믿지 않는 것'을 원칙으로 삼았습니다.
앱이 '결제 성공, 얼마'라고 알려와도 그대로 믿지 않고, 서버가 결제사에 직접 다시 조회해 금액을 맞춰 봅니다.
결제 완료 알림이 두 경로로 두 번 와도 결과는 한 번만 반영됩니다.
환불 요청이 결제사에서 실패하면 그 기록까지 함께 사라지던 문제가 있었습니다. 실패해도 '환불을 시도했다'는 이력은 반드시 남도록 고쳤습니다.
쿠폰 하나로 버튼을 빠르게 두 번 누르면 할인이 두 번 들어가던 문제도 찾아냈습니다. 방은 막혀 있었지만 쿠폰은 아니었던 거죠. 쿠폰을 쓰는 순간 잠가서 막았습니다.
이런 문제는 평소에는 거의 드러나지 않습니다. 그래서 더 먼저 찾아서 막아 두어야 했습니다.
예약은 한 사람의 일이 아닙니다

손님 화면만 있다고 예약이 굴러가지는 않습니다. 이음은 손님 앱, 고택 주인을 위한 파트너 관리자, 전체를 운영하는 마스터 관리자 이렇게 세 화면으로 나뉘고, 각자 볼 수 있는 것과 할 수 있는 일이 다릅니다.
정산이 대표적입니다. 이용을 마친 예약을 체크아웃 날짜 기준으로 모아, 수수료를 뺀 금액을 회차별로 계산합니다. 회차는 예정, 확정, 지급완료 순으로 넘어가고, 확정하는 순간의 수수료율이 그대로 기록됩니다. 정산이 끝난 뒤에 환불이 생기면 다음 회차에서 빼고, 모자라면 다음으로 넘깁니다.
예약 한 건 뒤에는 손님, 고택 주인, 운영사 세 사람의 일이 함께 움직입니다. 셋 중 누구의 화면이 불편해도 예약은 멈춥니다.
이렇게 만들었습니다
약 10주 동안 590여 번의 변경을 쌓아 70여 개 화면을 만들었습니다. 그 과정에서 지킨 방식은 이렇습니다.
정할 것은 기록으로 정합니다. 환불 규칙, 파트너 가입 방식처럼 사업에 영향을 주는 결정은 안건마다 문제와 선택지, 저희 추천안을 적어 고객과 함께 정하고, 결정과 반영 내역을 남겼습니다. 중간에 전체 코드를 한 번 검토해, 동작이 바뀌는 안건 17개를 같은 방식으로 정리했습니다.
테스트가 먼저 지킵니다. 700여 개의 자동 테스트가 기능을 고칠 때마다 돌아갑니다. 한번은 테스트를 돌리는 동안 예약 만료 작업이 함께 돌아 개발용 데이터가 실제로 만료된 일이 있었습니다. 그 뒤로는 테스트 중에 모든 자동 작업이 꺼지도록 막았습니다.
화면 기능을 전수 대조합니다. 디자인에 있는 기능 약 430개를 하나씩 목록으로 만들어 구현 여부를 대조했습니다.
다음 예약 서비스를 위해 나눠 둡니다. 회원, 인증, 파일, 약관처럼 어떤 예약 서비스든 필요한 부분은 공통 모듈로, 고택·객실·요금·정산처럼 이음에만 필요한 부분은 따로 두었습니다. 둘이 섞이지 않는지는 테스트가 확인합니다.
이번 프로젝트로 배운 것
저희는 공공시설 예약 서비스를 만들며 '어디서 예약하는지 몰라 헤매는 불편'을 풀어 본 적이 있습니다. 이음에서는 한 걸음 더 나아가, 예약하고 싶은 마음이 드는 곳까지 함께 만들어야 했습니다.
그리고 예약 시스템의 어려움은 화면이 아니라 화면 사이에 있다는 것을 다시 배웠습니다. 두 사람이 동시에 누를 때, 결제창에 가 있는 동안, 환불이 실패할 때처럼요.
좋은 예약 시스템은 결제 버튼에서 끝나지 않습니다. 무엇을, 누구에게, 어떤 순서로 보여 줄지에서 시작해, 아무도 보지 않는 순간까지 책임지는 일입니다.
이음 프로젝트의 화면은 프로젝트 페이지에서 더 보실 수 있습니다.