토이 프로젝트 시작하고 닫기

프로젝트 흥망성쇠

JC
Museion
10 min readJan 12, 2020

--

토이 프로젝트에 대한 이야기는 블로그에서 여럿 한 적이 있다. 토이 프로젝트지만 서비스로 운영하고 있었는데, 이제 이 프로젝트/서비스를 닫을 때(close)가 되었다고 판단해 관련 기록을 남긴다.

토이 프로젝트는 한 모바일 게임의 팬이 제작한 게임 캐릭터 및 데이터를 번역해서 제공하는 것으로 시작했다. 어느 여름, 이 게임을 하던 지인이 사이트 하나 만드는 데 얼마나 시간이 걸리는지 물어봤다. 어떤 사이트인지에 따라 다르다고 했더니 영어로 제공하는 서비스를 보여주면서, 이 사이트를 한글로 번역해 제공하던 사이트가 있었는데, 어떤 이유인지 사이트를 닫아버렸다고 한다. 자신을 포함한 주변 사람들이 게임하기가 불편해졌다고, 번역된 정보를 제공하는 사이트를 만들면 좋아할 것 같다고 했다.

1. 기존 사이트 분석

사이트를 훑어보니 정적 페이지(static page)로 운영하는 사이트이고, 모든 데이터는 github을 통해 제공하고 있었다. 저장된 데이터를 복사해서(fork) 데이터를 확인해보니 모든 캐릭터의 정보를 몇 개의 js 파일로 저장하고 있었다. 이 데이터만 번역하면 번역 정보가 제공될 수 있겠다.

사이트 구조를 보니 angular로 개발되었고, GPL2라이선스를 달고 있다. 히스토리를 살펴보니 최초 개발자가 제공하는 서비스가 있었고(서비스 A), 개발자가 개발을 중단하자 다른 사람들이 서비스를 복사해서(fork) 이어가는 형태다(서비스 B). 다른 나라에서도 서비스 B를 복사해서 번역 데이터를 제공하고 있었다.

2. 개선하기

사이트는 대강 이해했고, 어떻게 운영할지 고민했다. 지인은 더 나은 검색 화면이 있으면 좋겠다고 했다. 서비스 B는 모바일에서 접근하기가 어려운데 이 게임은 모바일 게임이고, 사람들도 대부분 모바일에서 접근하기 때문에 모바일 중심으로 되면 좋겠다는 이야기를 했다.

우선 번역 제공부터 진행했다. Angular는 처음 접해보는 것이라 전체 구조를 파악하기는 어려웠지만 js로 작동하기에 기존 데이터는 놔두고, 번역한 데이터가 있으면 기존 데이터 값을 변경하는 식으로 개선했다. translate/ko/character.js 형태의 파일을 만들고 서비스에 추가했더니 잘 작동한다. 서비스 B의 번역 서비스를 제공하기 시작했다(서비스 C).

앞서도 말했듯이 모바일 친화적이지 않기 때문에 어떻게 데이터를 제공할지 고민했다. 당시엔 우물 안 개구리였기 때문에 vuejs나 react 같은 프런트 기술을 몰랐고, 익숙하던 bootstrap을 활용해 프런트를 만들었다. 모바일 화면을 구성하고, 게임과 유사한 필터를 만들었다(검색 서비스). 백엔드는 php로 구성했다. 나중에 분석해보니 서비스 B의 번역 서비스인 서비스 C보다 내가 만든 검색 서비스를 더 많이 접속했다. 모바일에서 보기 편한 것이 매력이었나 보다.

그 외에 다른 가치를 더할 것은 없을까 살펴보니, 사람들이 자신의 보유 캐릭터를 자랑하기 위해 캡쳐해서 공유하는 모습을 볼 수 있었다. 지인은 캡처하기 좋은 화면을 만들어서 제공하고 싶어 했다. 나는 이게 어떤 의미가 있는지 이해하지 못해서 오랫동안 개발을 거부했다(아직도 어떤 의미가 있는지 이해하지 못하고 있다). 지인의 계속되는 요청에 캐릭터 이미지와 함께 클릭하면 gray filter를 껐다 켤 수 있도록 간단히 만들었는데, 사람들이 가장 좋아하는 서비스가 되었다(자랑 서비스).

그 외에도 보스를 잡기 위해 어떤 캐릭터가 필요한 지 자동으로 계산해주는 서비스를 제공하기도 했는데, 이건 게임을 전혀 모르다 보니 만들면서도 제대로 만들었는지 이해하지 못했고, 마지막까지도 유지 보수하기 가장 어려운 페이지로 남았다.

3. 개발 – 인프라

인프라는 3단계로 발전했다.

초기

최초 구성할 때는 한 대의 ec2에서 모든 것을 처리하도록 만들었다. 개발하는 도중에 api와 update 서버가 필요하다는 생각이 들어 ec2를 3대로 구성하고 web, api, update 서버를 두었다. 비용 문제로 update 서버는 4시간에 한 번씩 켜서 update를 실행하고 종료하는 형태로 구성했다. 나름 클라우드의 장점을 이용한 것이지만 클라우드에 적합한 설계는 아니었다(닫을 때까지도 클라우드 기반 서비스는 완성하지 못했다).

중기

그러다 docker를 활용해 여러 서버를 구성한 것처럼 만들어보자는 생각을 하게 됐다. 인스턴스 3개를 운영하려니 비용 문제가 가장 컸고, 동일한 내용을 서비스하는데 서로 다른 서버에서 데이터를 처리할 필요가 있을까 하는 생각도 있었다.

docker를 제대로 써보지 못했기 때문에 가상 서버 여러 대라고 생각해서 구성했는데, 여러 컨테이너로 생각하고 접근했어야 했다. 서버 여러 대라고 생각하고 컨테이너마다 내부 설정을 변경해놓았더니 컨테이너를 재시작하려고 하면 그때마다 설정을 변경해줘야 하고, 로컬 개발 환경을 구성하는 것도 불편했다. 가장 작은 서버에서 컨테이너 여러 개를 돌리려니 디스크 저장 공간이 부족해져서 고민하다가 Toast cloud로 옮겨봤다.

Docker 재시작하듯이 설정을 변경해서 켜면 되기 때문에 옮기기는 쉬웠다. Toast cloud에서 docker로 구성해서 잘 운영을 하다가 몇 번 문제가 발생했는데 가장 기억에 남는 것은 디스크 저장 공간의 문제였다.

git으로 캐릭터 이미지를 관리했는데 용량이 어느 정도 이상이 되자 저장 공간이 부족해지는 현상이 나타났다. 로그 파일을 지우고 불필요한 코드를 지우는 식으로 처리를 하다가 더 이상 지우지 못하는 상황까지 발생했다. 로그를 쌓지 못해 서비스는 중단됐고 불필요한 파일 삭제도 할 수 없는 상태다.

디스크 추가를 하려고 했더니 애플리케이션을 하나 설치해야 하는데, 이 프로그램을 설치할 용량도 없다(로그 파일도 쌓지 못할 정도니..). 그래서 컨테이너를 중지하고 불필요한 컨테이너도 삭제한 후에 디스크를 추가할 수 있었다. 이때라도 컨테이너 기반으로 구조와 생각을 바꿨어야 했는데 회사에서도 문제가 빵빵 터지는 시기라 그렇게 하지 못해 아쉽다.

후기

그렇게 운영을 했지만 점차 관리해야 하는 것이 많아지니 힘들었다. Database 컨테이너도 추가했고, 자랑 서비스 캐릭터 추가 자동화 등등 몇 가지 기능이 붙었다. 이건 엄연히 토이 프로젝트이고 일과 중에는 손을 안 대고 싶었는데 손을 대야 하는 경우들이 자주 발생했다. 스마트폰으로 접속해 서버 문제를 해결하곤 했다.

고민하다가 aws lambda와 API Gateway를 이용해 일부 기능을 serverless로 옮겼다. 이후에는 데이터 관리에만 집중할 수 있었다.

Serverless로 이전한 내용은 아래 글에 정리해놓았다.

4. 개발 – 코드

앞에서 언급했듯이 백엔드 서비스는 php로 구성했다. API 서비스는 slim framework를 이용해 개발했다. 검색을 요청하면 간단한 연산 이후에 검색 결과를 리턴한다.

업데이트 서버는 여러 가지 고민을 했는데, 서비스 B는 Angular로 되어있고, js 파일을 이리저리 조합해 데이터를 출력하는 것이 기본이다. 모든 캐릭터의 기본 정보가 담긴 파일, 모든 캐릭터의 스킬이 담긴 파일 등으로 구분되어 있는 것을 검색 서비스를 만들면서 캐릭터 별 파일로 변경하는 작업을 진행했다. 이 작업을 하려면 js 파일을 해석해야 했다. js 분석까지는 쉽게 했고, 이 내용을 캐릭터별로 출력하는 것 까지는 간단히 만들었는데, 이 내용을 데이터로 가져오려니 고민이 됐다. 가장 간단하게 접근할 수 있는 방법으로는 selenium을 이용해 모든 로딩이 끝난 script를 조회할 수 있다. 내가 우물 안 개구리가 아니었다면 node.js 등을 이용해 업데이트 서비스를 구성했을 텐데 무척 아쉽다.

Update 서버에서 변경된 내용은 api 저장소에 저장하고 업로드한다. 서비스 B의 변경 내용도 5분마다 받아와서 변경된 내용이 있으면 update 서비스를 실행하도록 했다. 나름 최대한의 자동화를 진행했다. 그래서 서비스 C와 검색 서비스의 데이터는 서비스 B의 최신 데이터와 거의 차이가 나지 않게 만들었다.

번역 업데이트는 admin 페이지를 개발해 처리했다. 번역 데이터를 입력하면 json format으로 저장하고, 서비스 C에도 바로 반영되도록 작업했다.

사용자들이 많이 사용하는 캐릭터는 캡처 데이터가 있기도 하고, 관심도 많아 쉽게 제공할 수 있다. 옮겨 적기만 하면 된다. 많이 사용하지 않는 캐릭터 데이터는 관심에서 멀어지기 마련이다. 그렇지만 그 캐릭터를 좋아하는 사용자가 있기도 한다. 사용자의 피드백을 어떻게 받을 것인가 고민했다. 사용자와 대화하는 것을 원하지는 않았다. 고민 끝에 버튼 하나를 만들고, 클릭하면 운영하는 discord 채널에 메시지가 날아오도록 작업을 했다. 이 기능도 사용자들이 많이 이용했다.

웹 기반이다 보니 앱 기반의 경쟁 서비스보다 빠르게 데이터를 제공할 수 있었고, 사용자들이 요청하는 데이터에도 빠르게 피드백을 줄 수 있었다.

5. 운영

서비스 C와 검색 서비스를 어느 정도 완성하고 사용자 카페 몇 곳에 글을 남겼다. 경쟁 서비스(?)에는 일부러 글을 남기지 않았는데, 사용자들이 알아서 링크를 공유했다. 경쟁 서비스는 외부 링크를 허용하지 않는 암묵적인 정책이 있다고 하는데, 서비스의 관리자가 자랑 서비스를 링크하면서 본인의 캐릭터 자랑을 하기도 했다.

이슈를 정리하고 개발해 정기 배포하기보다는 수시 업데이트를 진행했다. 한 달에 한 번 정도 업데이트한 내용을 정리해서 카페에 홍보 겸 업데이트 정보를 남겼다. 감사 인사가 많이 달렸고, 업데이트 정보 게시글에 버그 리포트가 있는 경우 즉시 처리해주기도 했다.

처음에는 아는 사람만 아는 서비스였지만 어느새 카페의 캡처화면 대부분이 우리 서비스가 되었고, 캐릭터 뽑기 이벤트가 있는 날이면 자랑 서비스에 사람이 붐볐다. 카페의 게시글 섬네일이 자랑 서비스 캡처로 도배된 적도 있었다.

한 번은 게임 내 이벤트가 있었는데, 게임 관련 웹서버 정보를 가져와서 더 보기 좋게 제공하면 어떨까 하는 생각이 들었다. 이벤트 직전에 만들고 카페에 링크만 간단히 남겼다. 점심 식사를 하러 나갔는데, 이벤트 서비스가 작동이 안 된다는 이야기가 들렸다. 디스크 정리도 했고, 안 될 이유가 없는데 왜 그럴까 하면서 느긋하게 점심을 먹고 들어왔는데, 접속자가 폭주해서 메모리 부족 상태가 되었다. 메모리는 항상 여유가 있었고 그전에 다른 문제가 생겼었는데 이번에는 메모리 부족이 발생해 서비스 불능 상태가 되었다. 이때의 추억은 글로 남겼다.

6. 닫기

나는 여전히 게임을 안 하는데 지인도 게임을 접었다고 한다. 게임 내부에 필터 검색이 추가되어 필터를 위해 서비스에 접근하는 사람도 줄었다고 한다. 캐릭터 자랑 서비스는 여전히 사랑받고 있지만, 더 이상 이 서비스를 개선할 의욕도 없고, 관심도 떨어졌다. 이제 닫을 때가 되었다고 생각했다.

번역 데이터는 아까우니 남겨놓기로 했다. 새로 저장소를 만들고 번역 데이터를 남길 예정이다. 필요한 사람이 있다면 쓰겠지.

사람들이 좋아하는 캐릭터 자랑 서비스는 완전한 정적 페이지로 만들어서 남겨놓기로 결정했다. 일부 기능은 사용하지 못하지만 사람들에게는 도움이 될 수도 있겠다.

그리고 서비스 종료 공지를 남긴다.

7. 경험

서비스 론칭을 경험한 사람들은 많겠지만 서비스를 닫는 경험을 한 사람은 많지 않을 것 같다. 내 손으로 만들고 내 손으로 닫는 서비스는 처음이었고 개발-운영-클로징까지 경험해 봤다.

시간을 많이 쏟거나, 기술적인 발전을 많이 하지 못해 아쉬웠지만 회사 업무 외적인 것으로 집중하며 동력을 얻을 수 있었다. 리디 셀렉트의 아티틀을 통해 읽은 밥면빵 기사를 읽으며 괜히 기분이 좋았다.

인프라 관리와 운영에 여러 도전을 해볼 수 있었다. 더 좋은 기술을 선택하거나, 한 번 리팩토링을 했으면 더 좋았을 텐데, 그때는 이미 동력이 떨어지고 관심이 멀어진 상태.

토이 프로젝트나 만들고 아무도 안 쓰면 노잼인데, 사용자들이 많아 방문하고, 피드백이 있어서 좋았다. 다음에도 좋은 기회가 생기고 여유가 생기면 어떤 형태로든 토이 프로젝트를 진행해보고 싶다.

--

--

JC
Museion

책 읽는 개발자. 아빠. 생산성, 책, 개발에 관한 글을 남깁니다.