본문으로 건너뛰기

모바일 청첩장

조회수

기획
#

결혼식은 안 하지만 청첩장은 직접 만들고싶은 개발자의 로망?이 있었기에 집들이 청첩장을 만들기로 했다. 결혼식이 없기에 축의를 받는다기보다는 럭키 드로우 방식으로 축의를 하면 뽑기권을 주고 뽑기로 기프티콘 등 간단한 상품권을 주는걸로 초기에 기획했다.
다만 이 방식은 로그인이 반드시 필요했는데, 각자가 입금한 만큼 뽑기권을 줘야했고 이를 단순히 세션에 저장하기에는 날아갈 위험이 있기 때문이다.
하지만 축의를 위해 로그인이 필요하다는건 너무 진입장벽이다보니 기각했다.
로그인 없이 축의의 재미를 줄 수 있는 방법은 없을까 해서 생각한 것이, 물품을 놓고 펀딩을 받았다. 이때 펀딩 현황을 실시간으로 갱신해서 펀딩하는 재미가 있도록 했다.
물론 축의를 받고자 하는게 목적은 아니니 중간에 게임을 넣어서 움직이는 자율차를 잡아야 축의 페이지로 넘어갈 수 있게 했다.
진짜 축의하고 싶으면 이걸 뚫고 해봐 라는 느낌이랄까.

페이지 구성
#

최종적으로는 5개의 뷰를 구성했다.

  • 로딩 뷰: 비행기가 이륙하는 화면.
  • 입장 뷰: 항공권 모양이고 입장 버튼을 누르면 항공권이 뜯기면서 메인 뷰로 이동한다.
  • 메인 뷰 (사진첩 겸 방명록): 다른 모바일 청첩장처럼 사진들이 슬라이드되며, 아래에는 방명록을 작성할 수 있다. 맨 아래에 blink되면서 숨어있는 펀딩 버튼을 누르면 게임 뷰로 이동한다.
  • 게임 뷰: 인천공항 뷰에 작은 자율주행차가 계속해서 움직인다. 자율주행차를 잡으면 펀딩 뷰로 이동한다.
  • 펀딩 뷰: 축의에 대한 감사 메시지와 함께 물품 2개와 펀딩 계좌가 적혀져있다. 펀딩 시 실시간으로 금액이 반영되며 최근 후원자 목록이 초성으로 보여진다. 펀딩이 완료되면 다음 물품으로 자동으로 넘어간다.

프론트엔드
#

프론트 스택으로는 플러터를 선택했다.
플러터를 선택한 것은 여러 플랫폼에서 동일한 화면을 보여준다는 것도 있고, 리액트보다 쉽게 예쁜 디자인을 만들 수 있기 때문이었다.

아무래도 디자인은 나보다는 여자친구가 더 능숙했기 때문에 antigravity에 붙어있는 AI로 여자친구가 디자인 관련해서 코드를 작성하면 코드 오류 수정이나 리팩토링, 백엔드 기능과의 연동을 내가 구현하는 식으로 진행했다.

DB 구성
#

supabase 선택
#

익숙한 관계형 DB를 쓰고 싶었기에 firebase는 고려하지 않았다.
firebase와 유사하지만 관계형 DB라는 supabase를 사용하거나
서버 쪽 경험도 쌓을 겸 postgreSQL을 이용해서 직접 API 서버를 만들고 배포할까 중에 고민했다.
결론적으로는 처음 기획이었던 로그인을 구현하기 위해 oauth 인증도 필요할 거라고 생각했는데 supabase에서 인증 기능도 지원한다고 해서 supabase를 골랐다.
결국 인증 기능을 구현하지는 않았지만 flutter와 python에 라이브러리가 잘 구현되어있어서 편하게 잘 썼다.
혹시 요금이 나가려나 했는데 REST API는 횟수 제한도 없다고 해서 안심했다.
다만 무료 요금제에서는 7일 동안 활동이 없으면 프로젝트를 휴면시키는데, 서비스하는 동안에는 API 요청이 계속 있을테니 이 부분도 괜찮았다.

스키마 설계
#

guestbook_entries
#

방명록을 위해서 방명록 기록이 남는 테이블을 가장 먼저 추가했다.
방명록 작성의 진입장벽을 낮추게 하기 위해 인증 없이 이름과 내용, 집들이 참석 여부를 작성하게 했다. 다만 수정과 삭제는 가능하게 하기 위해 비밀번호를 작성하게 하고, 익명으로 쓰고 싶은 사람들을 위해 DB에만 남는 진짜 이름 column을 추가했다.
혹시 악질 유저가 이상한 댓글을 마구 적을 가능성이 있지만 청첩장인데 그런 사람은 없겠지 믿고 이 부분은 따로 고려하진 않았다.
다만 실제 이름 column에도 본인 실명을 적지 않은 사람들은 일부 있어서 누군지 모르는 장난식의 방명록은 몇 건 있었다.
동명이인이 있을때 헷갈릴까 고민해서 비밀번호를 본인 전화번호로 작성하게 하거나 따로 비밀번호 필드를 만들까 했는데 그건 너무 개인정보라 포기하고, 실제 이름 칸에 우리와의 관계도 같이 적게 했다.
결과적으로는 동명이인은 아무도 없었다.

funding_items
#

펀딩 물품 이름과 현재 펀딩 금액, 목표 금액, 연동 계좌, 물품 타입 (가전/가구), 펀딩 상태 (준비중, 펀딩중, 완료)에 대한 column이 있다.
처음에는 물품별로 계좌를 따로 만들어야하나 했는데 카카오뱅크에서 한달에 하나씩만 계좌를 만들 수 있어서 두 개의 계좌로 펀딩이 완료되면 자동으로 다른 물품에 원래의 계좌가 연동되게 했다.
따라서 해당 물품 펀딩 시작 당시의 계좌의 금액과의 차이로 펀딩 액수를 계산해야해서 start_offset column을 추가했다

sponsors
#

펀딩해준 사람에 대한 테이블이다.
펀딩 물품에 대한 id를 외래키로 가지고, 입금자명, 금액을 column으로 가진다.

테이블 권한 부여
#

supabase에서 테이블에 RLS 설정을 하니 anon key에 권한을 허용해야 API를 사용할 수 있었다. service role key를 사용하면 모든 권한이 허용되지만 key가 탈취되면 후원 목록을 삭제시킬 가능성도 있었기 때문에 anon key를 사용했다. supabase에서 권장하는 것도 anon key를 사용하는 것이기도 했다.
funding_items나 sponsors 같은 경우 클라이언트에서는 읽기 권한만 필요했기 때문에 간단히 select 권한만 추가하면 됐다
하지만 guestbook entry의 경우 수정, 삭제도 가능하기 때문에 혹시 키가 탈취당한다면 삭제당할 가능성도 있었다.
그런 일은 없을거라 가정하지만 혹시 몰라서 insert 권한만 부여한 guestbook backup 테이블을 추가해서 방명록 원본을 백업할 수 있도록 했다.
이 문제에 대한 해결책은 더 좋은 방법이 있을 것 같다.

펀딩 구상
#

펀딩 물품은 항상 두가지만 보이도록 했다.
친구가 결혼 기념으로 펀딩을 받았다는 회사 동료 B님의 의견이었는데, 물품이 너무 많으면 펀딩이 채워지는 재미가 없을거고 하나면 고르는 재미가 없을 거라는 말에 공감했다.
가전과 가구 항목으로 나눠서 가전 물품의 펀딩이 완료되면 해당 물품이 다음 가전 물품으로 바뀌는 식으로 구현했다.
생각보다 펀딩 열기가 대단해서 처음에는 생각하지 않았던 물품들에 대해서도 추가하느라 나중에는 아무 두 물품을 올리게 되기도 했다.
또 펀딩을 해준 분들께는 감사의 의미로 언박싱 혹은 후기를 보내기로 했다.

게임
#

게임 기획은 채은이가 해줬는데 채은이가 다니는 인천국제공항공사를 상징하는 인천공항 지도 위에 내가 다니는 자율주행 회사를 상징하는 자율주행 자동차가 움직이도록 했다
매우 빠르게 움직이게 해서 잡으면 펀딩 페이지로 넘어갈 수 있게 했다
창의 크기에 안에서 랜덤한 좌표로 이동하기 때문에 창을 줄이거나 모바일에서 하면 훨씬 유리했다.
사람들이 자동차를 계속 못 잡는다고 할 때 잘 만든 것 같아 기분이 좋았다.
다만 한 가지 걱정했던 것이 나중에 펀딩 현황을 확인하기 위해 다시 페이지를 켰는데 게임을 다시 해야되면 들어가기가 싫겠다는 생각이 들었다. 그래서 쿠키에 자동차를 잡았는지 여부를 저장하게 해서 한번 잡은 사람은 더 이상 게임 창이 뜨지 않도록 했다.

실시간 펀딩 연동 기능
#

개인적으로는 이 기능을 구현한게 제일 재밌었다.
두 물건에 각각 연동된 계좌에 입금이 들어왔을 때 실시간으로 페이지에 보여야 펀딩하는 재미가 있을거라는 채은이 회사 동료 H 님의 의견이었다. 처음에는 이거 못할텐데 싶었는데 나도 이게 되어야 펀딩하는 재미가 있을거라는 말에 동의해서 결국 구현하게 되었다.

Approach 1) 은행들의 결제 서비스를 이용
카카오 등에서 QR로 결제하는 API를 활용하는 것이다. 처음 이 프로젝트를 시작할 때에는 이렇게 하면 되겠지 하고 간단히 생각했으나, 이 API를 이용하려면 사업자등록이 되어있어야 했다. 결제라는 것이 상거래이니 사업자등록이 필요하다는 것이 이해되었다.
당연하게도 나는 관련해서 사업자등록을 할 생각도 없고 할 수도 없었기 때문에 이 안은 포기했다.

Approach 2) 핸드폰에서 입금 알림 파싱
1번 방법을 사용할 수 없다면 특정 계좌에 입금에 대한 hook을 만들 수 있다면 물건과 계좌를 연동해서 실시간으로 펀딩 현황을 알 수 있을거라 생각했다. 입금 hook API도 찾아봤지만 1번 방법과 마찬가지로 사업자정보가 필요했다.
그래서 다음으로 생각한 것은 입금 알림 카톡 혹은 문자를 파싱하는 것이었다.
핸드폰에 카톡 혹은 문자 알림을 파싱하고 DB를 업데이트하는 서버를 깔면 되지 않을까 했는데 루팅이 필요하기도 하고 핸드폰에 인터넷이 24시간 연결되어있다는 보장이 없을 것 같아 이 안도 포기했다

Approach 3) pc 카톡의 입금알림톡을 OCR로 파싱
pc에서 입금 알림을 파싱할 수 있는 방법을 생각하다 카카오뱅크는 입금알림이 톡으로도 오니 그걸 이용하면 되겠다 싶었다. 카톡 내용을 크롤링할 수 있는 방법을 찾아봤는데 나처럼 개인톡을 파싱하는건 문제가 없지만 이 방법으로 타인과의 톡을 파싱하는게 가능해서 합법은 아닌 듯 했다. 카톡 프로그램을 뜯는것도 리버싱의 영역이라 영 찝찝해서 이건 아닌 것 같다 싶었다.
그래서 생각한 것이 오픈소스 OCR 프로그램인 태서랙트를 이용해서 카톡의 글씨를 인식하는 것이다. 다만 문제가 두가지 있었는데

  • 카카오뱅크와의 톡을 24시간 특정 pc에 틀어놔야 해서 다른 pc에서 카톡pc 버전을 사용할 수가 없었다.
  • 글씨 두께가 얇아서인지 인식률이 생각보다 안 좋았다.

Approach 4) 입금 문자 윈도우 알림을 파싱
카톡을 24시간 특정 pc에 트는 건 별로인 것 같아서 문자를 파싱하는 방법을 고민했드. 그러다 문득 생각난 것이 윈도우에서 핸드폰을 연결하면 문자를 pc에서도 보낼 수 있다는 것이 생각났다.
그런데 핸드폰과 연결 기능을 쓸 필요도 없이 채팅+ 프로그램으로 pc에서 문자를 보내는 것이 가능했다.
심지어는 문자 내용이 윈도우 알림으로도 왔는데, 윈도우 알림은 파이썬에서 winsdk 라이브러리를 써서 쉽게 파싱이 가능했다.
따라서 당시에 놀던 미니 PC에 채팅+를 틀어놓고 윈도우 알림을 주기적으로 읽어서 펀딩 계좌에 입금이 있는 경우 DB를 업데이트하도록 최종적으로 구현할 수 있었다.
계좌번호, 입금자명, 금액, 계좌 총액을 파싱해서 계좌번호에 연동된 물품에 대해 펀딩 금액을 계좌 총액 - 펀딩 시작 시 계좌 총액 (start offset)으로 설정한다. 펀딩 금액이 목표 금액을 초과한 경우 펀딩 상태를 completed로 변경하고 같은 타입의 준비중인 다음 물품을 ready 상태로 변경하고 start offset을 설정한다. 또 계좌 연동을 해당 물품으로 바꾼다.

마지막 문제가 하나 있었는데, 채팅+는 보안 기능으로 특정 시간 동안 자리비움인 경우 잠금 모드가 되어서 알림을 받을 수가 없었다는 것이다. 방법을 찾다가 알아낸 것이
원격 데스크톱 연결 후 다른 모니터에 스크린을 띄우도록 바꾸는 스크립트를 실행하는 것이었다. 말이 어려운데 가상의 모니터에 현재 PC의 화면을 띄우면 PC는 계속 사용자가 PC를 사용 중이라고 생각하고 자리비움 상태가 되지 않는다. 최종적으로 이 방법으로 펀딩 연동을 구현할 수 있었다.

배포
#

아무래도 도메인이 있어야 배포가 가능하니, 도메인을 잠시 살까 하다가 어짜피 잠시 쓸 도메인인데 사는게 아까워서 NAS의 시놀로지 도메인을 그대로 쓰거나 github page에 배포하는 것 중에 고민했다.
결국 NAS를 선택했는데 아무래도 개발자 지인이 많다보니 github page로 배포하면 해당 repo에 들어가서 이것저것 뜯어볼 것 같아 불안했기 때문이다.

후기
#

최종적으로 120개의 방명록이 기록되었고, 59명에게 펀딩을 받아 13개의 물품을 펀딩 완료할 수 있었다.
방명록을 작성해준 모두, 펀딩을 해준 모두, 그리고 베타테스트를 해준 모든 이들에게 감사의 말을 전한다.

데모
#

https://baepnas.synology.me/