EMP(Efficient Mobile Service Packaging)
올해 14F 서비스를 개발하면서 "CMS + 앱" 조합의 프로젝트를 보다 쉽게 만들 수 있는 패키징 프로젝트가 있으면 좋을 것 같다는 아이디어가 나왔다.
SI 회사에서 나올법한(사실은 많이 경험해본 -_ -) SI to Solution 패키징 프로젝트였기 때문에 우려 앞섰지만... 위에서 나온 의견이라 울며 겨자 먹기로 시작했다.
아이디어를 개선하다 보니, 아래의 흐름이 만들어졌는데...
1. 앱의 핵심은 서비스도, 마케팅도 아닌 콘텐츠다. 콘텐츠를 지속적으로 제공하는 담당자가 없으면 무의미하다.
2. 현업에서 콘텐츠 및 서비스 관리에 사람과 비용을 지속적으로 투입하는 건 현실적으로 어렵다. 14F처럼 독립 플랫폼을 목표로 하는 특수한 경우를 제외하고는...
3. 그럼 기 생성된 콘텐츠가 존재하거나 앱과 상관없이 지속적으로 콘텐츠를 관리하는 플랫폼은 없을까?!
4. That's YouTube!
다행히도... 우리는 회사의 모든 유튜브 콘텐츠 데이터를 수집하는 CODDAS라는 엄청난(?) 녀석을 개발하지 않았겠음?! 이거 활용하면 뭔가 그림이 나올 거 같다는 생각이 들었다. -_ -b
그렇게 프로젝트가 시작됐고 EMP(Efficient Mobile Service Packaging Platform)가 탄생했다.
그 이야기를 두서없이 써 내려가 보려 한다.
EMP 프로젝트의 정의는 콘텐츠 통합 서비스를 빌드해주는 플랫폼이다. 2022년 7월부터 12월까지 진행했으며, 필자는 Backend(CMS, REST API, Batch Job, Push Message 등)를 책임졌다.
제일 먼저, 마인드 맵을 통해 14F 서비스 중 패키징 대상에 포함되는 기능과 추가 개발이 필요한 내용을 정리했다. 이후 EMP라는 패키징 프로젝트를 진행하고 이를 사용해서 서비스(Music, Health, Kids)를 배포하는 파일럿을 진행했다.
EMP 패키징
14F 서비스 아키텍처를 그대로 활용했기 때문에 크게 변하는 건 없었다. CMS로 오픈소스인 워드프레스를 사용했고 앱에서 CMS를 접근하기 위해 워드프레스의 REST API를 활성화하고 API 서버를 통해 접근했다. 그밖에 푸시 서버와 캐시 서버 역시 기존과 동일하게 사용했다.(성능 때문에 캐시 서버 최적화는 필요했다.)
핵심은 유튜브 콘텐츠를 CMS에 주기적으로 등록 및 업데이트해주는 배치 작업이었다. 14F는 콘텐츠를 관리하는 담당자가 별도로 존재했지만, EMP는 유튜브의 데이터를 활용하기 때문에 다양한 조건으로 유튜브 데이터를 CMS와 동기화시켜줄 수 있는 방법이 필요했다.
초기 기획은 "방송 프로그램별 앱"이었다. 즉 담당자가 방송 프로그램 코드를 지정하면 CODDAS를 통해 해당 프로그램의 유튜브 영상을 CMS에 동기화하고 앱에서 보여주고자 했다.
문제는 시간이 지날수록 요구사항이 다양해졌다는 거... "소프트웨어 엔지니어링" 과목에서 강조하는 요구사항의 초기 정립을 지키지 않은 대표적인 케이스다. -_ -
결국 수집 대상에 유튜브 채널 아이디, 동영상 제목, 동영상 태그, 동영상 토픽 등으로 대상을 선정할 수 있도록 요청사항이 수정됐다. 뿐만 아니라 수집대상에서 예외 처리할 수 있는 조건(프로그램 코드, 채널 아이디, 동영상 제목, 동영상 아이디, 동영상 태그, 동영상 토픽)도 추가됐다. 최악은 이 모든 조건이 CMS를 통해 복수로 입력 가능해야 했다.
지나고 나니 웃으면서 정리하지만, 당시에는 정말 암울하더라. 초기 버전으로 배치 모듈을 개발하고 나니, 명확하지 않은 요구사항을 다시 받는 상황이 반복됐다.
초기 프로그램별 수집 조건으로 배치 모듈 개발을 완료했을 때는 데이터 수집 초기화 모듈과 업데이트(동영상 소비 데이터 동기화) 모듈이 별도로 존재했다. 초기화 작업은 CODDAS API를 활용하고 업데이트 작업은 YouTube API가 주가 될 거라 판단했기 때문이다. 그런데 요구사항이 다양해지고 개발(이라고 쓰고 삽질이라고 읽음;;;)이 반복되면서 리팩토링을 통해 두 개의 모듈이 하나로 합쳐졌다. 중복된 코드가 많이 발생했고 성능 최적화를 통해 두 모듈이 비슷한 일을 수행하게 되면서 굳이 별도의 모듈로 관리할 이유가 없었다.
성능 이야기가 나와서 말인데, 워드프레스 API를 통해 CMS에 데이터를 넣는 작업은 비용이 꽤나 컸다. GUI를 통해 CMS에 콘텐츠를 하나 등록할 때는 알 수 없었지만, 기 유튜브에 올라가 있는 수천, 수만 건의 데이터를 CMS에 동기화하는 작업은 생각보다 성능에 많은 영향을 미쳤고 오래 걸리는 작업이었다. 결국 API를 통한 트랜잭션 작업을 포기하고 DB에 직접 데이터를 처리하는 방식으로 변경했다. 워드프레스의 DB 스키마를 파악하고 데이터 동기화를 위한 시행착오 때문에 꽤나 많은 시간을 잡아 낭비했다.
방송 프로그램별 매칭을 위해 CODDAS API를 사용할 때는 Elasticsearch의 DSL을 사용해서 쉽게 작업이 가능했는데, 유튜브 API에서 가져온 데이터에서 필터링이 필요할 때는 직접 개발을 해야 하니 이것도 꽤나 고역이었다. 그나마 언어적 제약이 약한 NodeJs 기반이라 참을 수 있었던 걸지도;;;
동영상 정보를 현행화 하기 위해 유튜브 API를 사용할 때는 두 가지에 유의했다. 성능, 쿼터(Quota).
위에서 밝혔듯 수집 조건에 따라 꽤나 많은 데이터를 유튜브 API를 통해 조회할 수도 있기 때문에 쿼터 최적화는 반드시 필요했다. (Search 남발하다가 쿼터 거지꼴 못 면한다;;)
게다가 주기적(현재는 2시간 간격)으로 동기화를 수행하기 때문에 작업에 너무 많은 시간이 소요되는 건 지양해야 했다. 클라우드 인스턴스 스펙별로 비동기 호출을 버틸 수 있는 최적화 건수 찾는 게 생각보다 일이었다. 현재는 콘텐츠가 가장 많은 Music 앱에서 100개 비동기 요청(동영상 5,000개)마다 1초의 딜레이 부여해서 처리하고 있다.
특정 조건에 해당하는 동영상 묶음을 관리해주는 플레이리스트 기능을 개발(CMS를 통한 플레이리스트 관리, 플레이리스트 업데이트 배치)했고, 일별로 새로운 영상을 앱으로 푸시해주는 푸시 발송 배치도 추가했다.
워드프레스 API 성능개선을 위해 캐시 서버를 활용했다. 14F 서비스 대비 캐시 종류가 다양해졌기 때문에, 핵심 캐시 데이터는 콘텐츠 업데이트 배치에서 리로드 동작을 수행했다.
EMP 앱 서비스
현재까지 EMP를 통해 배포한 앱은 총 3개다. 앱은 Flutter로 개발되어 안드로이드, iOS 스토어에 배포됐다. 앱 배포의 목적은 기능의 완성도가 아닌, EMP를 통해 쉽고 빠르게 다양한 스타일의 앱을 배포하는데 의의가 있었기 때문에 이후 보다 다양한 기능이 확장될 수 있다.
MY Music : 복수의 채널, 토픽(Music)에 해당하는 콘텐츠 앱
MBC Health : 태그(건강)에 해당하는 콘텐츠 앱
MBC 뽀뽀뽀 : 개별 방송 프로그램에 해당하는 콘텐츠 앱
프로젝트 소고(기술 관점)
워드프레스
워드프레스를 사용해서 사용자 정보를 통합 관리하는 마이크로 서비스를 배포하고 싶을 경우, "멀티사이트" 기능이 유용하다. 다만, EMP처럼 배포하는 서비스가 독립적(회원 등)으로 존재하게 만들고 싶을 경우 멀티사이트 기능은 사용불가.
유튜브의 데이터를 워드프레스의 contents 필드로 넣으면, 자동으로 HTML 태그가 붙는 문제가 발생한다. 결국 워드프레스 플러그인(Pods)으로 생성한 CPT에 동영상 내용 원본 필드를 plain paragraph text 필드로 추가하여 별도로 저장 및 관리했다.
워드프레스를 CMS로 활용하는 서비스를 개발할 경우, 데이터 조회를 위한 API는 반드시 페이징 처리해야 한다. 초기 데이터가 많지 않을 때는 전체 데이터를 조회해도 성능에 문제가 없지만 만 건 이상의 데이터가 쌓이면 생각보다 성능의 제약이 커진다.(특히 검색) 혹, 기능상 페이징 API를 온전히 사용하지 못하는 상황이라면 차라리 워드프레스 DB를 직접 접근하는 방식을 권장하고 싶다.
워드프레스의 업데이트 API는 비동기 작업을 통해 성능을 고려하여 개발해야 한다. 성능이 좋지 않은 개발서버에서 비동기 HTTP 요청은 Internal Server Error(500) 발생시킬 가능성이 농후하다.
(AWS EC2 기준, t2.large가 초당 100건을 채 못 버틴다.)
캐시 서버
캐시 서버는 Redis를 활용했는데, 의외의 복병이었다. 필자가 생각했던 것보다 캐싱 데이터가 커지는 문제가 발생했는데, Redis를 통해 캐싱하려는 데이터가 사이즈가 10MB 이상이 되니 데이터를 불러올 때 많이 느렸다. 2초를 초과하는 성능을 보이기도 했는데, 이럴 거면 차라리 DB 캐시를 활용하는 게 낫지 않을까 하는 생각이 들 정도였다. 결국 캐싱 데이터를 분할해서 해결하기는 했지만 그다지 깔끔한 처리 방식은 아니었다.
그밖에
아무리 솔루션이 좋아도, 제대로 마케팅하지 않는다면 의미가 없다. 반드시 제품을 소개하는 웹페이지를 만들어야 한다. 요즘은 웹 UI 템플릿 시장이 워낙 잘되어있으니, 괜히 고생하지 말고 10불 내외로 템플릿 사서 휘리릭 만들자. 웹에 대한 기본 개념 정도만 있으면 일주일 안에 마무리할 수 있다.