만루만루는 포털사이트인 디시인사이드의 만화 갤러리(게시판)의 특정 게시물들을 모아 하이퍼링크를 제공해주는 서비스다. 서비스 기획과 개발, 홍보, 운영 전반적인 과정을 다뤄볼까 한다.

0일차 - 서비스 구상하기, 개발 스택 정하기, 개발

서비스 개발을 결정한것은 마루마루라 칭해지던 불법 번역사이트의 운영자가 잠적 후 서비스가 중단 된 뒤, 국내에 판권이 있지 않은 비정발 만화들이 디시인사이드의 만화 갤러리에 올라오면서다. 만화를 찾기 어렵고, 링크들도 난립하고 있는 상황이라 링크를 정리하는 게시물이 올라와도 효과적으로 유저들이 파악하기 힘든 상황이였다.

서비스 개발을 바로 옮기는것은 쉬운 결단을 내려서 되는 일이 아니다. 그렇기에 어느정도 머릿속으로 청사진을 그려넣고 빠른 결정을 내려 빠른 개발을 하기로 진행한다.

해당 사이트는 최소한의 백엔드로 운영되는걸 감안했다. AWS Lambda등을 이용한 Serverless 프레임워크도 생각해보았지만, 서비스의 덩치에 비해 비교적 큰 코스트를 짊어질것이 분명했다. 그렇기에 동적페이지가 아닌 정적페이지 서비스를 결정하고, 데이터도 정형화된 json을 받아와 프론트엔드에서 직접 처리하는 식으로 결정했다.

그러려면 웹 서비스(프론트엔드)와, 데이터 관리 서비스(백엔드)가 필요했다.

코드 스택

프론트엔드

Vue CLI(3.x)로 제작된 webpack 템플릿을 이용하여 제작하였다.

  • Vue
  • Vue-Router
  • TypeScript
  • Webpack
  • SCSS

백엔드(서비스)

별다른 언급이 없을 예정이다.

  • Nginx
  • Cloudflare

백엔드(데이터 관리)

Vue CLI로 제작된 Vue Electron 템플릿을 이용하여 제작하였다.

  • Vue
  • Vue-Router
  • Electron
  • Scss
  • Element-UI framework

1일차 - 서비스 기본 기획, 프론트엔드 개발

기획

서비스 네이밍 결정은 의외로 간단했다. 종료된 불법사이트의 이름과 번역이 올라오던 커뮤니티의 이름을 섞기로 결정했다. 도메인명도 친숙하게 가기로 하여 TLD도 .in으로 결정했다. 네이밍에 걸리는 시간이 많은 반면 쉽게 결정했다.

페이지는 간단하게 구성하기로 생각했다.

  • 메인(인덱스, 최근 아이템 표시)
  • 만화 페이지(사실상 item view 페이지)
  • 리스트
  • 즐겨찾기

의 간단한 구성으로 빠른 개발이 가능했다. 실제로 작품을 사이트 내에서 보는것이 아니기때문에 이정도의 간단한 구성으로 충분했으며, 더이상의 큰 기능 추가는 효율이 좋지 않으리라 판단했다.

프론트엔드 개발

프론트엔드 개발에서 부딪힌점이 몇가지 있었다. 바로 TypeScript의 사용.

원래 TypeScript를 이 기회에 배워보려고 했는데, typescript를 쓸때 .ts 혹은 vue 컴포넌트 파일 내에 <script type="ts">면 되는줄 알았는데 아니였다.

라이브러리를 import로 ES Module 형태로 불러올경우 에러가 뜨고, 그에 맞춰서 TypeScript 라이브러리 형태로 컨버팅해주어야 한다는걸 알게되었다. 아니면 설정을 바꾸던지 해야되는데 설정까지 들여다볼 시간은 그리 넉넉하지 않았다. 그렇게 TypeScript를 적용만 해두고 실제론 사용된건 메인 템플릿 외엔 존재하지 않았다.

그 외에 프론트엔드에서 사용할 더미데이터의 구조를 설계하고 메인페이지 및 기본 레이아웃을 설계하는 작업을 진행했다. 메인 레이아웃 제작은 VuePress에서 고생한 경험이 있어서 그런지 손쉽게 설계했다.

2일차 - 프론트엔드 제작 완료, 데이터 관리 백엔드 제작

프론트엔드 제작 완료

모든 페이지의 디자인 및 설계가 끝났다. 메인과 즐겨찾기의 리스트 형태가 동일해서 컴포넌트 재사용이 이루어졌고, 그 외에 딱히 컴포넌트 단위로 자를만한 레이아웃 설계가 보이지 않아 나머지는 모두 페이지단위로 코드 제작이 이루어졌다.

데이터 관리 백엔드 제작

일렉트론을 통한 개발을 시작했다. 일렉트론 통한 개발은 처음이 아니여서 순조롭고 빠르게 진행했다. 단 문제는 element-ui의 문서가 정말 개차반이였다는 것이다. 그것만 조금 넘기고 나니 괜찮아졌다.

데이터 관리를 하기 위해서 기본 데이터 저장소는 브라우저의 localStorage를 이용했다.

3일차 - 데이터 관리 백엔드 제작완료, 서버 구축, 서비스 홍보

데이터 관리 백엔드 제작 완료

데이터 목록, 추가, 삭제등 기본 데이터 입출력과 관련된 제어를 모두 완료했다. 추가로 섬네일의 크기를 조정해주어야 하는데 이것에 있어서 sharp라는 라이브러리를 사용했다. electron과의 호환이슈가 있었는데 electron-rebuild 패키지를 이용하여 해결했다.

서버 구축

서버 구축은 평상시에 사용하던 Nginx를 이용해서 진행했다. 별다른 무리없이 진행했고, 이번엔 드디어 도메인 연결이 잘 되어서 nginx 통한 certbot연동이 가능했다. 그 외에 CDN으로 클라우드플레어를 연동했다.

클라우드플레어

Cloudflare는 여러분도 알다시피 CDN이다. 서버 로케이션이 부득이하게 저렴한 가격을 위해 해외로 놓여진 바람에, 클라우드플레어 사용을 결정하였다. 매일 2GB가량의 트래픽이 절감되고있으며, 리퀘스트량도 상당히 절감되었다. 나름 혜자.

TLS 1.3 문제

가급적이면 최신 스택을 이용하려 서버에 TLS1.3을 적용하니 클라우드플레어 서버와 통신이 되질 않는다. TLS 1.2로 롤백 후 클라우드플레어에서만 TLS1.3을 지원해주는 반쪽짜리(?)로 적용하였다.

4일차 - 서비스 데이터 수집을 위한 데이터 마이닝

데이터 마이닝 과정은 따로 적지 않으려 한다. 데이터를 백프로 검증할만한 과정이 없어서 정말 막노동의 산물이였다. 데이터 크롤러를 마음만 먹으면 제작할 순 있지만, 사이트의 신뢰도 하락이 수반되는 행위를 놓고볼 순 없었기에 수동으로 데이터를 정리하기로 결정하였다.

이날은 개발을 쉬고 모든 역량을 데이터 수집에 집중했다.

5일차 - 데이터 관리소홀로 인한 위기

실수로 개발자도구에서 삭제가 아니라 전체삭제 아이콘을 눌러버렸다. 개발자도구에서 로컬스토리지 아이템에 대해서 어느정도 관리가 가능하기때문에 해당부분을 활용하려 했기때문이다. 삭제기능 만드는게 뭐가 어렵다고 그짓거리를 해서 이 사단이 나게 만들었는지 아이고 내가 왜 사는지 싶다가 데이터를 웹용으로 변환하기 전에 항상 백업을 해둔게 생각했다.

백업은 역시 최고의 솔루션이다. 덕분에 백업한 파일을 기반으로 복구하고 멘탈을 다듬었다.

6일차 - 데이터 관리 백엔드 보강, 업로드 과정 단순화

실수의 원흉인 삭제기능도 만들었다. 그 외에 아이템들의 정렬이나 나머지 기능들도 추가했다.

업로드 과정은 직접 작성자, 화수(x화 등의 이름), 링크를 적었는데 그 과정이 너무 복잡해서 화수, 링크만 입력하고 나머지는 백엔드에서 크롤링하여 자동기입되도록 처리했다. 귀찮다고 추가하지 않았는데 해당 과정을 도입하자마자 자료 등록 효율이 상당히 올라갔다. 코드쓰는거 끽해야 10분이면 되는데 왜 그 10분을 쓰지않고 1시간을 날리고있었나 싶었다.

7일차 - 서비스 결과 분석 및 유저 피드백 체크

서비스 운영 결과를 클라우드플레어의 통계치를 보고 파악했다. 운영 초기 첫날 홍보했을땐 순방문자수가 4800명정도 되었으며, 그 뒤부터 꾸준히 감소하여 현재 2200명정도를 표시하고 있었다. 아마 그보다 덜 될수도 있을것이다.

유저 피드백을 받기 위하여 surveymonkey 링크를 넣어둔것의 응답들을 확인했다. 50%정도의 피드백이 실제로 반영 가능 혹은 가치있는 피드백들이였으며, 반영 가능한 것들에만 이를 수렴했다.

9일차 - 간단한 최적화 진행

최적화를 진행하기로 했다.

webpack bundle analyzer까지 설치하여 패키지를 분석했지만 이렇다할 최적화 요소는 없었다. 번들 최적화는 안타깝지만 최적화는 다음에 하는걸로.

그 외에는 사이트들에 들어가는 이미지를 tinypng를 이용하여 압축하는 과정이 있었다. cloudflare가 CDN이라 빠르긴 하지만, 한국에 서버가 존재하는것이 아니라서 어느정도 사용자들의 반응속도 체감에 도움이 되는 과정이기에 꼭 필요했지만, 이걸 지금까지 수동으로 진행했었다.

이것을 만화 등록시에 API를 통해서 tinypng로 이미지가 자동 압축되어 올라가도록 변경했다. 그 과정에서 파일을 바로 저장하는것이 아니라 sharp를 통해서 버퍼 데이터를 받은 뒤 버퍼 데이터를 전달, 그 뒤에 압축된 이미지를 받아와 저장하는 식으로 변경했다.

14일차 - 프론트엔드 검색 기능 제작

통합 검색 및 검색 아이템별 가중치 제작

검색 제작은 별 무리없었다. 그래서, 살짝 꼬아서 알고리즘을 넣어볼까 생각해보았다.

검색엔진들을 생각했다. 원래는 데이터간의 연관성이나 상호관계를 생각했지만, 거기까지 가긴 힘들것같아서 나름 사용자들의 검색 패턴을 생각하여 가중치를 부여하여 가중치 순으로 표시하기로 했다.

  • 앞에 제목을 최우선적으로 검색한다
  • 검색 쿼리 안에 단어 혹은 문자열이 반복되면 가중치가 부여된다
  • 제목이 짧은 만화일수록 연관성이 커지므로 가중치를 더 부여하여 상단에 표시한다

이정도로만 생각하여 제작했는데, 100%는 아니더라도 80%정도로 만족스러운 검색결과가 표시되고 있는 상황이다. 가중치 계산도 간단해서 제작상 큰 무리는 없었다.

15일차 - 나머지 추가 기능 넣기 및 모바일 UX 개선, 마감

어느정도 기술적 리뷰는 완전히 끝냈다.

어느정도 알고리즘에 대한 재고가 필요할지도 모른다. 사용자의 방문 빈도 등을 데이터로 이용하여 가중치를 높인다면 더욱 더 정확하고 좋은 결과가 나타날 수 있으나, '사용자의 데이터를 이용하지 않는다'라는 원칙 하에 다음으로 넘기기로 했다.

모바일 UX 개선

완벽하진 않은데, 아이폰 SE등의 가로 320해상도를 가진 핸드폰에서 사용상에 있어 네비게이션 크기가 너무 큰 문제가 있었다.

원래라면 익숙한 햄버거메뉴를 진행하였을텐데, 네비게이션 아이템 자체가 원체 많지 않아 툴팁형태로 표시하는걸로 해결을 보았다. 툴팁으로 놓았을때 사용자가 메뉴 활성화가 되었음을 쉽게 자각하진 못하지만, 아이템 자체가 적어 표시에 무리가 없을거라 판단하였다.

툴팁형태로 진행하였을떄 UI가 중복되는것이 통합검색이였는데, 통합검색과의 겹침문제를 해결하곤 싶지 않아 전체크기로 표시하기보다 조그맣게 메뉴 아래쪽에 표시하는 형태로 제작했다.

통합 리뷰

기술적 리뷰

프론트엔드

해당 토이 프로젝트를 통해서는 아직 타입스크립트를 내가 제대로 프로젝트에 적용시켜서 쓰기에는 시기상조임에 틀림없었다는 결론과, vue formatter는 아직도 타입스크립트를 지원안하며, 그 외에 typescript는 @types/(package-name) 형식으로 된 타입스크립트용 라이브러리를 사용하는것이 큰 제약으로 다가왔다는걸 느꼈다.

물론 해당 문제는 개발하면서 ES Module형태의 import가 아닌 CommonJS형식의 require를 이용하여 해결 가능하지만, 어딘가 모를 찝찝함은 당연히 남아있다.

그리고 정적 형태로 json을 데이터로 제공하는것은 좋지만, 처음으로 이런형식으로 개발하였을때 데이터 형식의 소중함을 일깨우는 시간이 되었다. 데이터를 재차 2차 가공하여 사용해야되는 불상사가 발생했기 때문이다. 이러한 잘못된 구조는 성능 저하, 불필요한 코드 재작성등이 수반되어 결과적으로는 프로젝트 개발 효율의 저하를 일으켰다.

그리고 Vue CLI 3.0을 이용하고 나서부터는 사용중인 웹팩 플러그인을 쉽게 파악 할 수 없다는것과, 기존 webpack.config.js 혹은 이와 비슷한 설정파일을 관리하던것과 달리 vue.config.js로 통합된 webpack 설정 관리는 러닝커브를 높이는 요소로 작용하였음에는 틀림없다. 몇번이고 vue-cli 웹페이지를 뒤져대면서도 설정을 제대로 완료하지 못해서 힘들었다.

상태기반 데이터 관리 라이브러리인 Vuex를 사용해볼까 하는 아쉬움도 많이 남아있다. 이때아니면 Vuex를 배워볼일이 없을것같긴 한데, 나중에 다른 토이프로젝트를 만들던 개인적인 공부를 통해서던 배울 수 있는 기회가 생기도록 노력해야겠다.

그리고 이제와서 생각하는건데 PWA로 개발했음 어떨까 하는 생각도 든다. 역시 끝내고 덮어놓고 보면 후회가 더 많이 남는 편이다.

아마 이 포스트를 작성한 뒤에 맥미니가 도착하면 iOS Safari에서 스타일 틀어짐 디버깅을 하는것이 마지막 과정이 아닐까 싶다.

백엔드(Electron)

백엔드는 이미 기존에 개인용도로 제작중이던 어플리케이션이 있어서 큰 개발의 어려움은 없었지만, element 프레임워크 정말 마음에 안든다. 중국어로 표시되는것들과 폰트 기준이 중국폰트라서 어색한것들 정말 싫을정도다. 언어 베이스를 한번 더 설정해야된다는 것 자체가 마음에 안든다는 의미다. 그 외에는 코드를 Copy&Paste 하는정도로 끝나는 UI제작은 괜찮았다.

어느정도 러닝타임 3시간만에 간단한 레이아웃을 제작했다. 상당히 만족스러웠다.

그리고 데이터베이스로 브라우저 내에서 key-store 형식의 데이터베이스로 사용되는 localStorage를 본격적으로 활용해 보았는데, 성능이 꽤 만족스러워서 좋았다. 그러나 데이터를 한번에 파악할 수 없다는 점, 그리고 key-store 기반으로 string만 저장되어 데이터 형식을 파악하는데 있어서 문제가 생기는 점(그래서 별도 라이브러리를 제작했던 것), 그리고 CRUD의 외의 지원이 사실상 없어서 라이브러리에서 추가적인 헬퍼를 제작했다.

"sqlite나 IndexedDB있는데 왜?" 라고 물어볼 사람이 있을까봐 싶어서 이야기 하는것은, Transaction이 필요할정도로 데이터가 귀중하지도 않고(나중에 가면 또 모른다), 데이터에 접속하는 사람은 프로그램 관리자 외엔 전혀 존재하지 않는 상태다. 그렇기에 일단 localStorage가 가장 적합하다 판단했다. 그리고 결정적으로 저 위에 작성된 둘은 내가 해볼 의지가 없다.

혹시 모를 상황을 대비하여 데이터를 매 분기마다 JSON으로 내보내 저장하고 있으며, 그 덕분에 4일차에 생겼던 미스를 수습 가능했다. 역시 최고의 실수 해결방법은 백업이다.

현재 fs-extra를 이용하여 발생하는 ensureDir함수의 딜레이 문제가 있는 상황인데, 원인을 찾지 못해서 일단 전전긍긍만 하는 상태이다. ensureDirSync던, ensureDir이던 무조건 꼬이며 async-await간에 동기화 문제가 발생해서 프로세스 완료가 1분 넘게 걸리는 등의 문제가 생기는 상황이라 마음에 들진 않는다. 대체하는 라이브러리로 mkdirp도 사용하였지만 마찬가지로 발생하는 문제라서, 무엇이 문제인가 열심히 고민중에 있다.

위에 적어둔 문제로 인해서 업로드 프로세스를 단축시키지 못한것이 아쉽다. 현재 electron을 통하여 파일 내보내기 -> FTP접속 -> FTP로 중복파일 제외 파일 업로드를 진행하는데, 중복파일을 제외하는 과정을 만들기 위하여 별도 폴더를 만들어 파일을 생성한 후 원래 디렉토리로 카피, 별도 폴더의 내용물을 업로드하기의 과정의 처음에서 문제가 생긴것이니 첫 단추부터 꼬여버린것이다.

그래서 업로드 자동화까지는 구축하지 못했다. 이게 너무 아쉽다.

서비스적 리뷰

해당 서비스는 커뮤니티 개입을 최소한으로 운영하였기 때문에 커뮤니티적인 요소들(댓글, 게시물 작성, 게시판, 회원가입)을 배제하였다. 그것이 해당 서비스가 갖는 매력이라고 판단하고 있다.

그 외의 컨텐츠의 부재 혹은 게시자의 삭제로 인하여 게시물이 삭제되는 등 유저들의 불편도 어느정도 있어 이탈률이 진행됨을 확인하였다. 그러나 이것은 컨텐츠가 갖고있는 한계점(위법성 컨텐츠)이라는 문제를 뛰어넘지 않는 한 사용자들에게 매력적인 서비스를 제공해 줄 수 없음에 틀림없다.

어느정도 그레이존을 이용하는 서비스이며, 서비스의 한계점이 명확함을 파악한 상태이니 다음부터는 조금 더 건설적이고 합법적인 토이프로젝트를 진행하기로 생각해본다.

결론

이 토이프로젝트에 대해 오해는 하지 말아주었음 한다. 본인은 토이프로젝트를 특정 집단의 이익을 위하여 개발한것이 아니다.

이로써 데이터 업데이트와 자잘한 버그수정을 제외한 서비스 개발은 사실상 종료되었다.

토이프로젝트 초반에는 정말 열심히 개발했다. 0일차에서 2일차까지는 12시간정도 메달렸던 기억이 난다. 문제는 체력 분배가 실패하니까 그 뒤부터 개발에 대한 의욕이 떨어지면서 점점 피곤해지는 느낌을 지울수가 없었다. 서비스 기획 개발 운영과는 달리 내 체력 분배는 실패했다. 요 며칠 컴퓨터 앞에 앉아만 있지 말고 운동좀 열심히 해야겠다는 생각이 든다.

처음으로 토이프로젝트를 제작하면서 이렇게 정리해보았는데, 만족스러운 정리가 되지 않았나 싶다.

합법과 위법 사이의 그레이존을 악용하는 사이트라 작성하지 않을까 고민도 했지만, 작성하고 나중에 읽어보았을때 같은 실수를 반복하고 있었는지 확인하는게 더 좋은 결론을 내는 일이 아닐까 싶다.

전체 들어간 비용 : 내 시간과 도메인 등록비용 대략 10달러정도, 그리고 서버 호스팅 비용 5달러