첫 시작
처음에 프로젝트를 시작하게 된 계기가 코인 중 업비트, 빗썸 같은 거래소에 상장된 코인이 아니라 DEX에만 있는 코인을 거래할 경우 대부분의 코인은 사기고 방향을 예측하기 힘들기 때문에 거래를 잘하는 사람의 계좌만 추려서 똑같은 방식으로 사고 팔면 돈을 벌지 않을까 라는 생각에서 시작되었는데
처음에는 단순 전체 계좌 조회 정도만 생각하고 있었는데, 이것도 만만치가 않았던게 API를 활용한다면 손쉽게 긁어올 수 있겠지만 Etherscan, Basescan 등의 API 서비스가 월 200$에 달하는데 개인이 감당할 수 없는 금액이라서 무료로 가져올 수 있는 방법이 없을까 하고 찾다가
Selenium을 사용해서 동적인 웹사이트를 스크래핑 하는 방법을 고안하게 되었는데, headless 모드로 크롬 창이 뜨지 않게 하면서 웹사이트에 들어가서 HTML을 긁어온 다음 코인 내역만 추려내는 식으로 작업을 해서 이후 표와 같이 정보 조합해서 이메일로 발송하는 방식이었다
스크래핑의 경우에는 말은 쉽지만 대부분의 사이트들은 Cloudflare를 사용하고 있는데, ‘당신은 사람입니까?’ 이게 뜨면 사실상 스크래핑이 불가능하고 HTML을 긁어온다고 해도 거기서 필요한 정보만 추려내는 것은 만만치 않은 작업이더라.
이후 필요한 정보만 추려냈으면, 내 이메일 혹은 핸드폰으로 알림을 발송해야 하는데, 핸드폰 같은 경우에는 Twilio 같은 메시징 서비스가 비용도 저렴하고 사용하기도 정말 쉽지만 최대한 돈을 덜 쓰고 싶었기에 신규 Gmail을 생성해서 JavaMailSender를 이용해 무료로 메일을 보내는 서비스를 구성하게 되었고, 정해진 시간마다 이메일을 보내보면 위와 같은 내용을 받을 수 있었다.
10분마다 점검하는 기능 만들기
처음에는 API가 너무 비싸서 대충 해보고 안되면 말고 이런 생각이었는데, Selenium으로 데이터를 정기적으로 긁어오는 데 성공하자 다음 계획은 계정의 전체 코인 내역을 긁어온다 해도 대부분의 계정에는 코인이 50-100개 정도 들어있는 경우가 대부분이고 육안으로 이 변동 내역을 구분하는 것은 불가능하기 때문에 10분마다 스크래핑 해온 정보를 비교해서 변동된 내역만 출력하도록 했는데
스크래핑으로 긁어올 수 있는 정보들은 HTML화 해서 이메일로 쉽게 보낼 수 있었지만, 가격 같은 경우에는 대부분의 사이트들이 Cloudflare를 사용하고 있어 Selenium으로 정보를 가져올 수가 없었기에 Moralis의 API를 사용하면 하루에 800번 까지는 무료로 가격을 가져올 수 있었는데 10분마다 메일 발송 가능성이 있긴 하지만 평균적으로 하루에 30회 이하로 호출되므로 코인 2-3개씩 한다고 한들 800번이면 굉장히 넉넉한 수준이기 때문에 무난히 가격까지 긁어올 수 있었다.
이후 10분마다 점검하는 이메일이 발송되면 a href 링크를 눌러서 거래 정보를 볼 수 있는 사이트로 이동한 다음 사기인지 아닌지 판별한 후, 가격 확인하고 매수/매도를 진행할 수 있게 되었다.
ChromeDriver 관리하기
Selenium을 통해 크롬드라이버를 생성하고 사용 후, driver.quit() 메소드를 사용하면 ChromeDriver와 같이 열린 크롬이 모두 같이 종료되어야 하는데, 간헐적으로 크롬이 닫히지 않는 경우가 있더라.
크롬 창 몇개 해봤자 리소스 얼마나 잡아먹겠어 싶어도, 10분마다 Selenium을 계속 돌리니까 크롬이 엄청나게 쌓이면서 반나절 넘어가면 컴퓨터가 부와아앙 하는 수준으로 리소스를 엄청나게 잡아먹게 되는데 그래도 돌아가긴 하지만 이러면 AWS로 이전 시 가장 저렴한 모델을 사용할 수가 없기 때문에 최대한 리소스를 덜 쓰게 만들어서 이 문제를 해결을 해야 했는데 공식 홈페이지에 가도 마땅히 해결을 하지 못하고 있는 상황이어서 추천 가장 많이 받은 답변이 명령어로 강제 종료시키라고 하던데
운영체제 확인해서 윈도우면 taskkill / 리눅스면 pkill 사용해서 강제로 크롬 드라이버를 종료시키도록 했고, 이렇게 하니 Docker에서 운영할 경우에는 상관이 없는데, 인텔리제이 프로젝트 키고 테스트 해볼 경우 웹서핑 하던 창까지 모두 꺼지는 바람에 뭘 못하게 되는 상황이 나와버렸다.
해결책으로 크롬 드라이버 생성 시 해당 시간별로 Profile을 따로 만들어서 option으로 넣어준 뒤 스크래핑 작업을 하고 해당 프로필에 대해서만 강제로 종료시키는 방식으로 만드니, 리소스에 부하를 주는 경우 없이 쾌적하게 돌아가게 되었다, 아마 AWS에서 가장 저렴한 EC2로 돌릴 수 있지 않을까 싶다.
Dockerize
실제 유용하게 사용 가능한 상태가 된 이상, 매번 인텔리제이 켜서 프로젝트 실행해 놓는 건 굉장히 번거로운 일이었기 때문에 도커로 프로젝트를 도커 이미지화 한 다음, 가동시키면서 10분마다 알아서 정보를 가져다주게 하면 되었는데, 도커에서 자동 실행 옵션을 걸어놓으면 컴퓨터 껐다 킨 후에도 알아서 켜지기에 이후부터는 개발은 개발대로 하되 10분 모니터링은 계속 돌아가서 원하는 계좌를 계속 추적할 수 있었다.
Spring Security 설정
Security는 설정하기 귀찮고 복잡해서 가급적이면 설정하지 않고, 나중에 AWS에서 도커 구동 시 API 호출 용 특정 IP 빼고 나머지를 다 차단하는 식으로 보안을 설정하려고 했는데
실제 로컬에서 운영을 하다가 보니, 보안 설정이 전혀 없으니 전혀 이상한 곳에서 내 서버를 호출하려는 시도가 감지가 되었는데 이러니까 시큐리티 설정해야겠다는 생각이 번쩍 들더라
일단 기본적으로 특정 IP를 제외하고는 다 차단시킬까 하다가, 스프링 쪽의 설명을 보니 이런 방식은 스푸핑 공격에 취약하고, 추후 지원도 되지 않을 예정이라기에 방향을 돌려 Base Auth(id/password)를 사용하지 않으면 모든 API를 호출할 수 없도록 막고 다음에는 application.properties 쪽에 아이디와 계정을 넣어놓고, BCryptpasswordEncoder를 사용해서 암호화 처리까지 한 다음 API 호출 시 ID/PW가 일치해야 호출할 수 있도록 개발을 했는데
코드 상으로는 짧고 어려워 보일 것이 없지만, 인터넷에 뿌려져 있는 정보가 옛날 정보가 정말 많아서 antMatcher 안쓰는데 이거 쓰라고 하거나 혹은 Implements를 받지 않아도 시큐리티를 구성할 수 있는데 이거 받으라고 하는 등 정보가 섞여서 구성 시에 어려움을 좀 겪었었다. 이후 적절한 보안 설정을 통해 허가하지 않은 접근은 차단하고 돌리다 보니
Postman 구성
기존에는 API 호출 시 인텔리제이의 generated-requests 기능을 사용하다가, 스프링 시큐리티가 붙고 나니 Authorization 처리를 해 줘야 하는데 ID/PW를 수동으로 매번 쓰려면 여간 귀찮은 일이 아니더라.
여기에 메소드가 쌓여갈 수록 호출해야 할 API는 점점 늘어나기에 스크롤 움직이면서 쓰기에는 대단히 불편한 관계로 Postman을 사용해서 프로젝트에 필요한 메소드 호출부를 미리 만들어 놓고, Authorization을 설정한 다음 필요한 부분만 호출해주면 매우 쉽고 편하게 사용할 수 있다.
간단한 팁은 local과 Docker로 이미지를 돌릴 경우 매번 IP 바꿔주기가 굉장히 귀찮을 수 있는데, 변수로 등록해놓은 다음 IP 변경 시 변수만 바꿔주면 정말 간단해진다
스캠 차단
스크래핑으로 긁어오는 코인 중 대부분은 사기 용도의 스캠이 대부분인데, 단순 스크래핑으로는 이런 스캠 코인들을 걸러내지 못하니 여러 생각을 해볼 필요가 있는데, 테스트가 많이 되지 않은 상황에서의 초기 접근은 .com airdrop, gift 이런 노골적인 사기 멘트가 들어있는 코인만 일단 걸러낸 후 추후에 추가로 스캠 필터를 고도화 하려는 생각이었는데
이게 설정이 좀 애매한게 노골적인 항목은 걸러내기 쉽지만 갯수가 적거나 유동성 풀에 따라 스캠 처리를 하기에는 갯수가 적어도 가격이 비싼 코인이 있고, 유동성 풀도 제로가 아닌 이상에야 사기다 아니다 라는 기준을 잡기가 굉장히 난해한데 이 부분은 좀 더 오래 운영하면서 경험이 쌓여야 할 듯 싶다.
DB 구성
처음에 서비스를 구상할 때 정말 작은 규모의 프로젝트를 생각하고 있어서 DB가 필요한 케이스가 테이블 2-3개 수준일 것 같은데 DB를 붙이느니 차라리 파일 시스템으로 관리하면서 정규식으로 Row를 필요한 내용으로 분할해서 사용하는 방식을 사용하고 있었는데
의도치 않게 프로그램이 유용하다는 것이 증명되면서 볼륨이 계속 커지기 시작하자, 고안하는 기능들이 파일 시스템으로 처리하려면 정규식 짜기도 힘들고 관리하기는 더더욱 힘들어지면서 DB를 붙여야 하긴 하겠는데 볼륨이 커져도 실제 필요한 테이블은 10개 이하고, 한달에 10,000 Row 이상 생기기는 어렵다고 보기 때문에 MySQL과 PostgreSQL은 프로젝트 규모에 비해 너무 큰 DB 시스템이 아닐까 싶고, EC2에서 운영하거나 혹은 나중에 프로그램을 판매할 가능성도 고려하면 EC2 프리티어에도 문제없이 동작하게끔 최대한 가볍게 만들어야 해서 SQLite를 선택했는데
SQLite를 사용해 보니 .db 파일 하나만 관리하면 되어 편리하고, 용량과 리소스 사용량도 매우 적어서 EC2 프리 티어로도 사용할 수 있는 좋은 선택지가 되었다, 이후 JPA와 Hibernate를 적용하니, 이전에 고통스러운 정규식 짜기에 비해 훨씬 쉽게 DB를 작성할 수 있게 되었다.
스캠 차단 2
이메일 알림 시스템을 사용하다 보면, 스캠은 자동으로 걸러서 메일을 보내지 않는게 굉장히 중요한데 스캠에 낚여서 잘못 매수할 가능성도 있고 메일이 많이 오다보면, 정작 중요한 순간에 메일을 확인하지 않게 되기 때문이었다. 여러 API에서 스캠 차단 기능을 지원하기는 했지만 한 번에 되는 만족스러운 기능은 없었기 때문에 직접 차단 필터를 만들었는데
먼저 이전에 언급했던 필터로 한번 거르고, 신규 코인이 들어오면 해당 코인의 24시간 거래량을 확인한 뒤 1K 이하라면 죽은 코인이거나, 사기일 가능성이 높기 때문에 제외했고
그 외에는 변동 금액이 총 1$ 이하였다면 99%는 스캠이었기 때문에 메일을 발송하지 않고 차단한 결과, 대부분의 스캠을 성공적으로 걷어낼 수 있었다.
EC2를 통한 24/7 운영
시스템이 어느정도 뼈대를 갖추자 이제는 24시간 내내 안정적으로 돌아가게 만들어야 했는데, 추적하는 계좌들이 대부분 외국인이었기 때문에 새벽에 거래가 대부분 이루어졌고 새벽까지 컴퓨터를 켜 놓기에는 좀 그렇고, 아침에 일어나서 한 번에 업데이트하면 금액을 제대로 가져오지 못했기 때문에, 신뢰할 수 없는 시스템이 될 수 있어서
24/7 운영을 하기는 해야 하는데, 일단 도커 이미지를 AWS ECR로 보낸 뒤, EC2에서 운영하냐, 아니면 ECS를 통해 운영하냐의 문제가 되었는데, 일단 사용은 ECS 쪽이 올리기만 하면 되서 훨씬 편했지만
가장 큰 문제는 프리 티어 적용이 안 되어서 공짜가 아니고 비용이 EC2에 비해 비싸서 사용할 수가 없더라. 저렴한 SPOT 요금제의 경우에는 가동되는 시간에만 요금을 청구하지만, 24시간 가동되는 서비스인데 이걸 이용하기에는 좀 앞뒤가 안맞는 것 같더라
결국 EC2를 이용해서 도커 설치하고, 이미지 내려받은 후 가동하게 되었는데 1년 내내 무료로 쓰려면 프리 티어로 사용해야 하기 때문에 Selenium이 RAM을 4gb 까지 소모하는 경향이 있어서 EC2 업그레이드 비용과 비교해본 결과 API를 쓰는 것이 더 저렴하기에 Selenium 대신 API를 사용하게 되었고
운영 이후에는 보안 그룹을 설정해서 인바운드의 경우에는 내 IP에서만 Postman을 통해 EC2 컨테이너를 통제할 수 있게 만들자
처음 목적이었던 24시간 감지하되, 내 로컬 컴퓨터에서 API를 통해 원격 서버를 통제할 수 있게 되었다.
Leave a Reply