구름톡 업데이트 48시간 전, 그 치열함에 대하여

Apr.28.2022 채상아

APP

안녕하세요. 우아한형제들에서 만화경 안드로이드 앱을 개발하고 있는 채상아 입니다.

이전 글에서 말씀드렸듯이 Android 구름톡을 가지고 돌아왔습니다. 지난 2월, 구름톡 성과를 가지고 전사 발표까지 하게 됐는데요. 정말 영광이었습니다. 발표를 하고 나서 가장 공감받았던 부분은 바로 “정말 힘들었습니다” (한숨) 부분이었습니다. 오늘은 도대체 어떤 점이 힘들었고, 어떻게 해결했는지 이야기해 보려 합니다.

불선씂

(배경은 <불편해요 선배님>과 <슴슴슴슴>작품)

구름톡이란
웹툰을 보다가 감정을 표현하고 싶을 때,
반응하고 싶은 장면이 있을 때,
원하는 위치에 바로 댓글을 남길 수 있는 기능입니다.
만화뿐만 아니라, 다른 독자들의 생각을 동시에 볼 수 있는 기능입니다.

기능에 대한 설명은 이전 글에서 했기 때문에 이번엔 기술적인 설명을 해볼까 합니다.

구현 시 고려했던 사항

구름톡 구현 시, 기술적으로 ‘성능’을 가장 중요하게 생각했습니다. 만화경은 제일 주요한 기능은 ‘웹툰 보기’입니다. 구름톡은 웹툰을 보여주는 에피소드 뷰어에 들어가는 기능이기 때문에, 에피소드 감상을 방해하는 성능 저하는 치명적입니다. 스크롤이 부드럽지 않거나, 에피소드를 로드 하는 시간이 오래 걸릴 수 있는 요소는 최대한 피해야 했습니다.

구름톡_개략도

구름톡 개략도

Data

하나의 에피소드는 여러 개의 컷으로 구성되어 있습니다. 구름톡은 이 컷을 기준으로 나눠서 관리했습니다. 컷으로 묶은 구름톡은 Y 축 기준으로 겹치지 않게 일정한 높이만큼 행으로 나누었습니다. 동일한 행에 있는 여러 개의 구름톡은 리스트로 묶었습니다.

Observable

만화경은 비동기 처리에 Rx를 사용하고 있습니다. 위처럼 데이터를 묶은 이유는, 비동기 처리를 위한 Observable을 최소한으로 만들기 위해서였습니다. 만약에 행마다 Observable이 생긴다면? 사용자가 스크롤을 하면서 만화를 볼 때, 적절한 시점에 이를 노출하고 제거하기 힘들다고 생각했습니다. 더불어, Observable이 많아진다면 성능에도 영향을 미칠 것이라 생각했습니다. 이러한 이유로 구름톡 노출을 위한 Observable은 컷 당 하나만 만들었습니다. 컷 안에 있는 구름톡을 각 행별로 랜덤하게 호출해서 화면에 노출했습니다. 컷이 화면에 노출하지 않을 때는 Observable 을 바로 dispose 시켰습니다.

View

성능 저하를 일으킬 수 있는 또 다른 요인은 View입니다. 화면을 렌더링 할 때 안드로이드의 리소스를 가장 많이 사용하기 때문이죠. 그래서 View를 최소한으로 만들고, 뷰 계층 도 선형적으로 가져가려 했습니다. 행별로 구름톡이 존재할 경우 오직 하나의 구름톡 View 만 추가했고, 이전 구름톡 View 가 제거된 이후에 추가했습니다. 구름톡은 하나의 구름톡이 지나간 후 다음 구름톡이 바로 보일 필요는 없기 때문에 미리 그려놓지 않았습니다. 오직 보이는 구름톡 뷰만 존재하는 거죠.

구름톡을 껐을 때와 켰을 때 성능비교

아래 프로파일링에 보이는 것과 같이, CPU 사용량, 메모리 사용량 모두 구름톡 전후로 크게 차이가 나지 않는 것을 볼 수 있습니다. 메모리가 10% 정도 차이가 나긴 하지만, 사용자가 만화를 보는데 무리가 없을 정도였습니다.

  • 테스트 디바이스 스펙
    • SM-F916N (갤럭시 폴드 2)
    • RAM : 12GB
    • CPU : 1. Octa-Core, 3.09 GHz
  • 테스트 시나리오
    • 구름톡 기능 추가 이전 버전 VS 구름톡 기능 추가 이후 버전
    • 앱 실행 후, 에피소드 진입 후, 자동스크롤 후 40초 동안 프로파일링
    • 작품의 23화, 구름톡 3,076개 (2022.03.25, 10:00 기준)

구름톡 기능 추가 이전 버전

구름톡 기능 추가 이전 버전

구름톡 기능 추가 이후 버전

구름톡 기능 추가 이후 버전

구름톡 이슈

이렇게 완성된 구름톡 웬일인지 너무 순조로웠습니다. 운수 좋은 날 처럼요.
업데이트 직전에 이슈가 빵빵 터졌습니다.

UGC 가 뭐라구요?

안드로이드 개발자라면 2021년 연말에, UGC 정책 때문에 업데이트하는데 고생을 하셨을거라 생각합니다. 이슈가 있었던 부분은 UGC 정책 중 아래와 같았는데요. 이 정책 때문에, 몇 시간에 불과하던, 앱 검토 시간도 수일로 늘어났습니다.

(여러분, 구글 정책 변경사항 잘 챙..겨….요……ㅜ)

구글 정책

UGC 관련 구글 정책

구름톡을 못불러와요

급하게 신고 및 차단 기능을 추가한 후, 이슈 티켓이 하나 올라왔습니다. 사용자를 차단 후에는, 보고 있는 구름톡에 차단한 사용자가 작성한 구름톡이 포함될 수 있으므로, 서버로부터 구름톡을 다시 호출해야 했습니다. 이때 구름톡을 호출하지 않는 이슈가 있었습니다. 일정상 내일까지는 반드시 끝내야 했기 때문에 야근을 시작했습니다.

내용을 보니, 컷 위치에 관한 이슈일 거라 생각했습니다. 기존에는 네트워크 호출을 최소화하기 위해, 사용자가 보고 있는 컷의 구름톡만 호출했습니다. 이 문제를 해결하기 위해선 아래 컷의 구름톡도 호출하면 되겠다고 라고 생각했습니다.

이렇게 나이브하게 접근한 게 재앙의 시작이었습니다.

문제의 원인

기존에는 사용자가 보고 있다고 판단되는 컷의 구름톡을 호출했습니다. 첫 번째 보고 있는 컷 위치, 마지막으로 보고 있는 컷 위치 값을 이용했는데, 둘 중에 마지막으로 업데이트된 값을 현재 보고 있는 컷이라 판단했습니다. 이렇게 해야, 스크롤 방향에 상관없이 현재 노출을 시작한 컷의 위치를 알 수 있기 때문입니다.

하지만, 기존에 있던 구름톡을 삭제하고 다시 호출하는 경우, 최신 값이 아닌, 현재 보고 있는 모든 컷의 정보가 필요했습니다. 하나의 컷에서만 부르는 게 아니고, 노출하고 있는 모든 컷의 구름톡을 호출해야 하기 때문입니다. 하지만, 기존에 사용하던 컷 위치로는, 이 값을 알 수 없었습니다. 이유는 기존의 값은 첫 번째로 노출된 컷인지 마지막으로 노출된 컷인지 판단할 수 없었기 때문입니다.

해결

사용자 차단처럼, 기존 구름톡을 지우고 갱신해야 하는 경우, 사용자에게 노출하는 첫 번째 컷과 그다음 컷의 구름톡을 호출하는 것으로 방식을 변경했습니다.

  • 마지막 컷까지 호출이 아니라 다음 컷까지만 호출한 이유는, 각 컷의 길이가 정해져있고, 이 값을 기준으로 한 화면에 세 개의 컷이 동시에 노출되는 케이스는 발생하지 않기 때문이었습니다.

회고

이 문제를 파악하고 해결하는 데 8시간이 걸렸습니다. 퇴근 버튼을 누르기 전, 댓글을 남기며 다짐했습니다. 회고를 통해 이런 실수는 반복하지 않겠다고. 회고는 시간 단위로 어떻게 해결하려 했는지, 안된 이유는 무엇인지 상세히 작성했습니다. 회고 끝에 발견한 가장 큰 문제점은 아래와 같았습니다.

  • 그냥 그럴 것이라 생각한 점
    • 컷 위치에 대한 정확한 확인이 이뤄지지 않은 점
  • 문제 해결 방안을 적지 않은 점
    • 결국, 처음에 생각했던 방법 중 하나로 해결했음
    • 적지 않고 이것저것 들쑤시며 작업해서 시간이 오래 걸렸음

원고가 갈라져요

전날의 전쟁 같은 이슈 처리가 끝나고, 끝난 줄 알았습니다. 하지만 오후쯤 이전에 발생했던 이슈가 리오픈 됐었습니다. 구름톡이 노출될 때, 컷 사이에 공간이 생기는 이슈였습니다. 만화를 보는데 원고가 갈라지다니, 치명적이었습니다. 하지만 이 이슈는 이미 발생 가능성을 염두에 두고, 구현을 했었습니다. 컷의 마지막 행이 컷 높이를 넘어간 위치에 있다면, 해당 높이의 구름톡을 한 칸 위로 올리는 걸로 이슈를 해결했었습니다. 리오픈 되고 나니 머릿속이 하얘졌습니다.

퇴근까지 반나절이 채 남지 않았고, 해결할 때까지 업데이트에 관련된 기획자, QA 팀 모두 기다려야 하는 상황이었습니다. 어제 발생한 이슈로 자신감도 떨어졌고, 시간의 압박 때문에 불안감은 커져갔습니다. 제가 작성한 코드는 다 갈아엎어야 될 것만 같았고, 임시방편적인 해결 방안밖에 떠오르지 않았습니다. 심지어 문제가 발생하는 기기와 발생하지 않는 기기로 나뉘어, 파악도 어려웠습니다.

퇴근 1시간 전 긴급회의가 소집됐습니다.

플랜 A, 플랜 B 가 하나씩 만들어지고,

이제 칼자루는 제가 쥐게 됐습니다. 어제의 실수를 반복하지 않기 위해 추측하지 않고 발생할 수 있는 원인을 하나하나 적어나갔습니다. 믿을 수 있는 팀, 문제 상황에 따른 플랜, 확보된 시간으로 보다 편안한 마음으로 문제를 파악할 수 있었습니다. 정확히 2시간 뒤, 원인을 파악하고 문제를 해결했습니다. 전날 대비 1/4 시간밖에 걸리지 않았습니다.

문제의 원인

구름톡 행 위치를 지정할 때 문제가 있었습니다. 구름톡 이 맨 마지막 행에 올 경우, 기기에 따라 컷 영역을 넘어가는 위치에 구름톡이 추가될 수 있습니다. 그래서, 맨 마지막 행의 위치할 경우, 계산 시 버림을 사용해야 했습니다. 그런데, 구현은 반올림으로 해놓은게 문제였습니다. 공교롭게 제가 가지고 있던 핸드폰 모두 반올림 시, 버림이 되는 케이스였고, 전달받은 폰은 올림이 되는 케이스였습니다.

회고

이 이슈를 회고할 땐 정말 많은 생각이 들었습니다. 전날에 발생한 이슈 때문에 위축되어 있었고, 코드는 쳐다보기도 싫었습니다. 이슈를 공유하고 같이 고민해 준 팀이 없었다면? 어제 이슈를 회고하지 않았더라면? 아마 문제를 해결하지 못했을 겁니다. 이 이슈를 통해 제가 얻은 한 줄은 아래 문장입니다.

나는 의심하고 팀을 믿자

기기

기기와 함께온 QA 분의 마음

그리고 그 후,

역동적인 애니메이션

위에 말씀드렸던 것과 같이, 성능 이슈를 고려해, 컷 기준으로 여러 개의 구름톡을 묶어서 관리를 했습니다. 하지만 이렇게 할 경우 역동성을 부여하는 데 한계가 있습니다. 한 행의 구름톡이 아무리 빨리 끝나도 다음 구름톡 나오기 까지 일정 주기만큼 기다려야 했습니다. 한 행씩 나눠서 관리하지 않는다면 이와 같은 역동성을 주기 어려웠습니다.

이 부분에 대해 기획자분께 설명을 드렸습니다. 복잡한 내용이지만 차분히 듣고 의견도 주셨습니다. 그중에 아래와 그림이 있었습니다. 이 그림 덕에 역동적인 애니메이션이 만들어졌습니다.

구름톡 설명 그림

데이터를 묶어서 관리하다 보니 제 머릿속 구름톡의 모습은 늘 하나로 묶여 있었습니다. 그런데 오른쪽 그림을 보니, 묶여 있던 구름톡이 한 줄 씩 풀어졌습니다. 컷안의 모든 줄의 구름톡을 한꺼번에 관리하되, 한 줄씩 따로 관리할 수 있는 방식이 머릿속에 떠올랐습니다. 이런 점이 우리가 모두 함께 이야기해야 하는 이유입니다. 우리는 기술로만 문제를 해결하지 않거든요.

변경 전변경 후

왼쪽은 변경 전, 오른쪽은 변경 후 애니메이션

그렇게 긴 고민과 여러 사람의 노력이 모여 지금 여러분이 보는 구름톡이 완성되었습니다.

엄청난 피드백

구름톡 런칭 후 받았던 사용자 피드백 중, 저의 눈물 버튼이 된 피드백을 하나 가져왔습니다. 하나의 기능을 만들기 위해 우리는 치열하게 고민하고, 시간을 쏟고, 디테일을 따집니다. 이를 사용자가 알아 줄 거라는 건 동화 속에 나 있는 일이라 생각했죠. 그런데 우리 서비스를 이렇게 관심 있게 들여다 봐주는 사용자가 있다니, 행복해졌습니다. 사랑받는 서비스를 만들고 있다는게 개발자로서 얼마나 뿌듯한 일인지 생각해보게 했거든요.

안녕하십니까, 만화경의 Android 버전을 사용 중인 애독자입니다.
이번 Android 1.32.0 버전을 기해 추가된 ‘구름톡’ 기능에 대한 사용 소감과 피드백을 전달하려 이 글을 작성합니다.
만화의 각 컷별로 댓글을 달 수 있는 기능은 만편구 시즌2, 매일의 선물과 같이 가로 슬라이드 형식인 경우에 한해 네이버 웹툰에서 먼저 채용한 기능으로 알고 있는데, 컷이 연결되어 있는 세로 슬라이드 형식의 웹툰에서 이러한 기능을 쓸 수 있다는 것에 감사를 표합니다.
과거 서비스를 종료한 티비플의 구름 기능의 선례처럼 사용자 경험의 질이 저하되는 것에 대한 우려도 있었으나, 그러한 문제가 거의 없는 것도 다행입니다.
그러나 전체적으로 텍스트가 빠르게 지나가기에 긴 댓글의 경우 읽기가 어려웠으며, 이동 속도 또한 등장할 때마다 무작위여서 이용 중 피로도가 증가하는 문제가 있었습니다.
또한 대부분의 만화가 전개 중 배경색을 흰색으로 두는데, 댓글의 색과 겹쳐 읽기 불편하기도 합니다.
우형 기술 블로그의 구름톡 개발 후기를 읽었는데,디버그 기능의 댓글 이동 속도, 색 등의 변수를 사용자가 설정할 수 있도록 풀어주시는 것은 어떤지 개발진 분들의 생각을 여쭙고 싶습니다.
추가로 가로 슬라이드를 지원하는 만화에 대한 구름톡 지원 의향, 모바일 웹 버전의 서비스 종료의 사유 등을 추가로 알고 싶습니다.
언제나 사용자의 즐거움과 감동을 최우선으로 생각하며 노력하는 만화경 개발진 분들께 감사드리며,
이 엽서의 끝을 맺습니다.

제안해 주신 내용 중, 구름톡 글씨 색상, 속도 등 사용자가 커스텀 할 수 있는 영역을 차차 늘려가려 합니다. 질문해 주신 내용 하나하나 정성스레 답변을 드리고 싶지만, 업무의 우선순위나 방향성에 따라 계속해서 계획이 변경되어 명확한 답변드리지 못해 유감입니다. 다만, 한가지는 자신있게 말씀드릴 수 있습니다. 앞으로 저희가 추가하고 개선해나갈 모든 기능은 말씀해 주신 것처럼 사용자의 즐거움과 감동을 최우선이라는 점입니다.

마무리하며

구름톡 기능을 구현하면서 여러 가지 생각이 들었습니다.

서비스를 개발하다 보면 새롭게 만드는 경우보다, 만들어진 기능을 가져와서 쓰는 경우가 훨씬 많습니다. 내가 고민한 기능은 다양한 라이브러리, SDK, Framework에서 이미 제공하고 있죠. 그래서 우리는 이를 적절히 잘 가져다 쓰고, 비즈니스 로직에 집중하려 합니다. 하지만 구름톡 기능을 만들 땐, 온전히 혼자 고민해야 했고, 방법을 생각해 내야 했습니다. 어디에도 없는 기능을 만든다는 자부심과, 불안함이 아슬아슬한 줄타기를 했습니다.

그럼에도 불구하고 결국 해내게 된 이유는 뭘까 생각했습니다.

회고, 믿을 수 있는 팀 이 두 가지 덕분이었습니다. 회고를 통해 두 번 실수하지 않았고, 믿을 수 있는 팀 덕분에 도전하고 집중할 수 있었습니다.

앞으로 더 끊임없이 성장할, 믿을 수 있는 팀과 함께할 동료분들 기다리고 있습니다!

바로 여기서에서 확인하실 수 있습니다.

긴 글 읽어주셔서 감사합니다!