Node.js

Puppeteer를 활용한 에어비앤비 메시지 자동화

안녕하세요? 곰프로입니다.

오늘은 Node.js를 활용해서 웹 브라우저를 제어하고 자동화할 수 있는 라이브러리인 Puppeteer를 소개하고 어떻게 제가 에어비앤비 메시지를 자동화했는지 알아보겠습니다.

Why?


에어비앤비는 호스트(임대인)가 게스트(임차인)에게 숙박을 제공하고 돈을 받는 숙박 공유 플랫폼입니다.

그렇기 때문에 여타 숙박업처럼 호스트와 게스트 간의 원할한 의사소통이 굉장히 중요합니다.

게스트가 비밀번호를 잊어버렸다거나 다른 궁금증이 있을 때 빠르게 답변을 주는 게 중요하죠.

하지만 호스트가 24시간 내내 게스트들의 응답에 답변을 해줄 수는 없습니다.

게다가 늦은 시간에도 빠른 시간 안에 응답해야된다는 점도 큰 부담으로 다가왔습니다.


<에어비앤비 메시지>

관리해야 되는 숙소가 많아질수록 신경써야될 부분도 많아지지만 동시에 대부분의 게스트가 물어보는 내용은 비슷비슷하기 때문에 자동화의 여지가 크다고 할 수 있습니다.

Version 1


처음에는 에어비앤비 제휴사가 제공하는 서비스인 YourPorter를 사용할까 했지만 

메시지 자동화 설정도 번거로운 데다가 예약 메시지를 한글로 작성할 시 제대로 저장되지 않는 오류도 있고 가격도 숙소 하나당 7달러+ 라는점때문에 직접 만들어야 되겠다는 생각을 했습니다. 개발자가 다 그렇죠 :)

이렇게 에어비앤비 메시지 자동화 프로그램(airbnb-checkout-reminder 이하 ACR) 버전1을 만들게 되었습니다.

버전1에는

  • 매일 아침 정해진 시간에 체크인/체크아웃 리마인더(체크인/체크아웃을 위해 필요한 사항을 담은 메시지)를 보내고,
  • 새로운 예약이 들어올 때마다 예약 관련 중요한 사항을 전달하는 메시지를 보낸다.

는 두 가지 기능을 구현했습니다.

ACR은 크롬 브라우저를 제어하는 라이브러리인 Puppeteer와 여러 구글 API를 활용해서 만들었는데요,

기본적인 구조는 예약이 들어올 시 지메일로 [예약 확정]이라는 제목의 메일이 간다는 점을 착안해서 특정 시간마다 Gmail API를 통해 메일함을 읽어들이고, 그 내용을 파싱한다음 Puppeteer로 에어비앤비 메시지 페이지로 이동해서 해당 게스트에게 필요한 메시지를 전달하는 것입니다. 

하지만 오랜 시간 동안 잘 사용해왔던 ACR을 버리고, 버전 2를 만들게 된 것은 몇 가지 한계를 느끼게 되었기 때문입니다.


<캡챠:인생에서 가장 분노스러운 순간>

  1. 지메일 API는 구글 OAuth2를 요구합니다. 구글 OAuth2는 원격 서버에서 봇을 작동시키는 것을 막기 위해 여러가지 보안 매커니즘 (로그인 시 랜덤하게 캡챠을 풀 것을 요구하거나 액세스 토큰을 만료시킴) 을 사용하기 때문에 꽤 자주 프로그램이 동작을 멈춥니다.
  2. 에어비앤비 역시 에어락이라는 자체 보안 매커니즘으로 자동화 브라우저를 통한 봇 사용을 제한하고 있습니다. (400 에러만 발생하면서 응답값을 받아올 수 없음)

이 두 가지 한계를 극복하고 새로 태어나게 된 것이 Autobnb입니다.


Autobnb (깃허브 링크)


ACR의 버전2인 Autobnb는 위 두 가지 한계를 극복하기 위해

<네이버 메일 자동 분류 설정>

  1. 지메일 API를 통해 예약 확정 메일을 읽어오는 방식에서 네이버 메일과 node-imap(imap-simple) 라이브러리을 사용해서 메일을 읽어오는 방식으로 바뀌었습니다. node-imap(imap-simple)은 imap 프로토콜만 설정되어 있으면 사용이 가능하고, 네이버 메일은 원격 접속에 대해 구글보다 상대적으로 관대하기 때문에 접속 실패 횟수를 줄일 수 있었습니다.
  2. 에어비앤비의 에어락을 회피하기 위해 자동 메시지를 보내기 위한 전용 계정을 생성한 뒤 에어비앤비 팀 멤버로 추가했습니다. (에어락의 발생원인은 불명확하지만 로그인 시 IP가 자주 바뀔 때 발생하는 빈도가 크다고 들었습니다.)

이제 프로젝트 설정과 각 기능의 구현을 살펴봅시다.

첫 번째는 프로젝트 설정입니다.

Autobnb는 typescript로 작성되었으며, webpack을 사용하고 있습니다.

가장 중요한 webpack 설정부터 살펴볼까요?

아래는 개발서버를 위한 webpack.config.dev.js 파일입니다. (프로덕션 설정은 개발서버 설정에서 몇 가지 부분을 제외한게 전부라 여기서 다루지는 않겠습니다.)

webpack 강좌가 아니기 때문에 자세하게 다루지는 않겠지만

  1. BannerPlugin과 source-map-support 라이브러리를 활용해서 디버깅 시 소스맵에서 정확한 라인 넘버를 확인할 수 있습니다. (devtool을 cheap-inline-module-source-map으로 같이 설정해주셔야 합니다.)
  2. Nodemon Plugin을 사용해서 파일이 변경될 때마다 자동으로 개발 서버를 재시작하게 설졍했습니다.
  3. HappyPack 플러그인을 사용해서 ts-loader의 빌드 속도를 높였습니다. (개발 노트북 사양이 훌륭하지 못해서 드라마틱한 성능 향상은 없었습니다... 또르륵)
  4. resolve.alias를 저런 식으로 해두면 파일을 import할 때 상대경로 대신 절대경로를 사용할 수 있습니다. ('../../../file.js' 대신 '~/file'과 같은 import 경로를 사용할 수 있게 됩니다!)

이제 typescript 설정입니다.

typescript 설정은 기본 설정에서 큰 변화가 없지만 webpack에서 소스맵과 절대 경로 import를 활용하기 위해 몇 가지 설정만 변경했습니다.

webpack에서 소스맵을 사용하려면 tsconfig.js에도 "sourceMap"을 true로 설정해주셔야 하고,

절대 경로 import를 활용하려면 위의 설정처럼 baseUrl을 "./src"로 paths를 "~/*" : ["./*"]로 설정해주셔야합니다. (저 같은 경우 루트 폴더 아래에 src 폴더를 두었기 때문에 위와 같이 설정했습니다. 폴더 구조가 다르다면 그에 맞게 조정하시면 됩니다.)


두 번째는 메일 리더의 구현입니다.

메일 리더는 Autobnb의 핵심 기능 중 하나라고 할 수 있습니다. 정해진 시간마다 메일함에 [예약 확정] 메일이 왔는지를 확인하고 내용을 파싱한 뒤, Puppeteer에 전달하는 역할을 담당하고 있죠.

저는 imap 프로토콜을 활용해서 메일을 읽어오기 위해 imap-simple 라이브러리(node-imap 모듈의 프로미스 버전)을 사용했습니다.

코드를 읽어보시면 구현 자체는 어렵지 않지만 몇 가지 주의할 사항이 있습니다.

  1. 네이버 메일은 기본적으로 imap 프로토콜을 사용하게 설정되어 있지 않습니다. (이는 계정마다 허용으로 설정을 변경해주셔야합니다.)
  2. 예약 확정이 제목에 포함된 메일만 분류되는 AIRBNB 메일함을 따로 만들 필요가 있습니다. (메일함 설정에서 메일 자동 분류-분류 추가를 통해 새로운 메일함을 만들 수 있습니다.)
  3. 메시지를 삭제하거나 읽음 표시를 해두고 싶다면 connection.moveMessage나 connection.setFlags api를 활용할 수 있습니다.
  4. 메일 검색 설정은 UNSEEN, UNANSWERED, RECENT 등 여러가지 유용한 기준이 있기 때문에 입맛에 맞게 활용하면 됩니다. 다만 날짜 기반 검색은 일 단위보다 작은 단위를 사용할 수 없습니다.

이 외에는 필요한 정보를 가져오기 위해 메일 텍스트에 정규식을 사용했습니다.


세 번째는 에어비앤비 모듈입니다.

에어비앤비 모듈은 Autobnb의 가장 핵심적인 부분으로 브라우저를 제어하여, 에어비앤비에 로그인하거나 예약 내역을 불러오고 게스트에게 메시지를 전달합니다.

코드는 이 곳에서 확인하실 수 있고, 메일 리더와 마찬가지로 주의사항을 살펴보면,

  1. 게스트가 사용하는 언어를 파악하기 위해 구글 번역 API를 사용하고 있습니다. 구글 클라우드 콘솔에서 프로젝트를 생성하고 API를 사용설정할 필요가 있습니다.
  2. 간혹 Promise.all 패턴을 사용해야는 경우가 있습니다. 이는 일반적인 await 으로 비동기 동작을 수행할 경우 리눅스 환경에서 timeout 에러가 발생하기 때문입니다. (관련 이슈)
  3. 대부분의 Puppeteer API는 waitUntil 옵션을 제공합니다. 이 옵션 역시 리눅스 환경에서 유용하게 사용할 수 있습니다. 일반적인 옵션은 networkidle0 (모든 API 요청이 종료될 때까지 동작을 대기) 입니다.
  4. Puppeteer의 waitForResponse 함수를 활용하면 페이지의 API 응답을 가져올 수 있습니다. 이를 활용하면 위의 코드와 같이 예약 내역을 불러오는 것도 가능합니다.


네 번째는 App.ts 파일입니다.

App.ts에서는 위의 모든 작업을 시작하고 시간 설정을 관리합니다.

크론잡을 설정하기 위해 node-cron 라이브러리를 사용했고, 주의할 점은 timezone 설정을 따로 해주셔야 된다는 점입니다. (Asia/Seoul)

또 node-cron의 크론 표현식은 일반 크론 표현식과 조금 구조가 다르기 때문에 반드시 라이브러리 문서에서 예시를 먼저 살펴봐야합니다.


마지막으로 호스팅은 300달러의 무료 크레딧을 제공한다는 말에 혹해서 구글 클라우드 플랫폼을 선택했습니다. 이미 AWS의 프리티어를 초과해서 사용하고 있었기 때문에 가뭄에 단비와도 같은 존재였죠.

결국 AppEngine에서 가장 작은 VM을 하나 생성해서 호스팅까지 마치게 되었습니다.

호스팅 관련해서 설정해야 되는 부분은

  1. node.js 설치 (NVM를 사용해서 간단하게 설치했습니다.)
  2. pm2 라이브러리를 설치해서 프로세스 관리 (프로세스 재시작, 로그 보기 등 유용한 기능이 많습니다.)
  3. 리눅스에서 크로미움 브라우저를 사용하기 위한 몇 가지 리눅스 라이브러리 설치

정도 였습니다.


후기


<클라우드 콘솔 앱 스크린샷>

몇 일마다 에러가 나서 동작을 멈춰버렸던 ACR과는 달리 Autobnb는 현재까지 별다른 문제없이 잘 동작해주고 있습니다. 아침에 일어날 때마다 게스트들에게 메시지를 보내기 위해 항상 불편하게 신경써야했던 과거는 안녕, 편하게 꿀잠자고 있죠. 예약이 많이 들어오더라도 자동으로 메시지를 보내주기 때문에 크게 신경쓸 부분이 줄어든 건 덤입니다.

여러분도 자동화하고 싶은 귀찮은 일들이 있다면 Puppeteer에 도전해보시는 건 어떨까요? Puppeteer를 활용한 다른 자동화 사례가 있다면 댓글로 공유해주세요!


이준형 님의 창작활동을 응원하고 싶으세요?

곰프로의 블로그
곰프로의 블로그
구독자 52

0개의 댓글

SNS 계정으로 간편하게 로그인하고 댓글을 남겨주세요.
새로운 알림이 없습니다.