LLM 스트리밍을 느리게 만들기

LLM 스트리밍은 기본적으로 불친절하다. 유저가 읽기 좋은 형태로 출력되는 것이 아니라 전적으로 LLM이 생산하는 속도에 맞춰서 출력되기 때문에, 지나치게 빠른 속도로 많은 양이 출력된다거나 문장 중간이 끊겨서 나오는 일이 부지기수다.

게다가 캐릭터 챗은 다른 대화형 채팅보다 유저 경험이 더욱 중요하다. 메시지 하나 하나에 유저가 웹 소설을 읽는 것처럼 이야기에 몰입할 수 있는 경험을 심어줘야 한다. 그래서 새로운 스트리밍 UI를 만들었다. 기본적으로 문장 단위로 보이게 했고, 한 문장씩 천천히 나오도록 해 읽을 시간을 충분히 줬다. 유저가 즉석으로 작성되는 소설을 체험하는 듯한 경험을 만들었다.

문장 단위로 출력하기

새로운 스트리밍 UI의 가장 큰 특징 중 하나는 문장 단위의 스트리밍이다. 글자가 한 글자씩 나오는 것이 아니라, 문장 하나가 완성될 때 출력이 된다. 자칫하면 채팅이 느려져보일 수 있는 설계다. 유저가 첫 글자를 보기까지의 시간이 chunk가 하나의 문장을 이루기까지로 지연되기 때문이다.

조금의 지연을 감수하고서 문장 스트리밍을 고집한 이유가 있다. 사람은 기본적으로 글을 읽을 때 글자보다 단어, 단어보다 문장 단위로 읽고 해석한다고 한다1. 따라서 중간에 끊긴 불완전한 문장을 일찍 렌더링하는 것보다 온전한 하나의 문장으로 천천히 렌더링되는 것이 당연히 더 이해하기 쉽다.

문장 단위로 나뉜 문단chunk가 모여 하나의 문장이 될 때 렌더링된다

스트리밍 속도 조절

단순히 문장 단위로 출력되는 것뿐만 아니라 템포도 조절했다. 묘사와 표현이 많은 긴 문장은 긴 호흡으로 읽고 짧은 문장은 짧은 호흡대로 읽히도록 지연시간을 다르게 줬다. 또 대사는 기본적으로 긴 호흡으로 읽히도록 했다. 단순한 지문보다는 캐릭터의 대사가 더 여운이 남는 문장이기 때문이다.

다만 어느정도 타협이 필요할 때도 있는데, 어떤 유저들은 대화가 빨리빨리 나오는 걸 원하기도 한다. 그래서 LLM의 출력은 종료되었지만 아직 화면에서 출력이 되고 있는 경우, 스킵하기 버튼을 넣어 대화를 끝낼 수 있도록 했다.

문장 단위 스트리밍 UI문장이 완성될 때마다 출력되도록 했다

스크롤 경험 한 스푼

이런 갖가지 노력에도 불구하고 유저가 그 속도를 원치 않는다면 어쩔 수 없다. 특정 좋았던 문장을 계속해서 다시 읽고 싶을 수도 있고, 애초에 읽는 속도가 시스템이 정한 것보다 느릴 수도 있다. 그래서 유저가 가장 최신의 메시지, 즉 목록의 최하단을 보고 있지 않다면 화면이 스크롤되지 않도록 막았다. 스크롤되다가 중간에 위로 스와이프하면 멈추도록 했다.

다행히 내가 쓰고 있는 스크롤 가상화 라이브러리에서 해당 구현(followOutput)을 지원하고 있어 사용했다.

스크롤 따라가기 비교최하단을 보고 있을 때만 새 문장을 따라 스크롤한다

소설형 뷰

이런 아이디어들을 조합해 기존 채팅형 뷰와는 다른 새로운 형태의 채팅 뷰를 만들었다. 이름은 소설형 뷰. 마치 소설을 읽는 것처럼 읽히는 것을 목표로 했다. 위의 스트리밍 UI에 더해 카드나 칩, 버튼 같은 인공적인 형태의 UI들을 최대한 단순 텍스트 형태로 덜어냈다. 유저 인풋 창도 소설을 써내려나가는 것처럼 디자인했다.

소설형 뷰는 유저들을 더 오래 남게 만들었다. 대화량이 비슷한 유저끼리 묶어 비교했을 때, 소설형 뷰 유저의 5주 차 잔존율은 채팅형보다 모든 구간에서 높았다. 특히 신규 유저에서 차이가 컸다. 다만 AB Test를 통해 직접적인 비교를 한게 아니라 대화량을 몰입도로 통제한 비교이기는 하다.

작업 회고

그와 별개로 소설형 뷰를 작업하는 것은 여러모로 아쉬움이 많이 남는 과정이었다. 우선 구현하는데 너무 오랜 시간을 썼다. 아이디어 자체는 올해 3월에 프로토타입을 만들어 팀에 공개했고, 이후 여러 부서간 협의로 구현이 더디게 진행되었다가 8월에서야 마침내 공개했다. 그 사이에 다른 서비스들이 소설형 뷰와 비슷한 기능들을 출시한 것을 생각하면 아쉬운 일이다.

긴급성 자체가 높지 않은 작업이라 다른 긴급한 작업들에 비해 우선순위가 높지 않았고, 디자인을 내가 하다보니 군데군데 매끄럽지 못한 디자인이 많아, 디자인을 재작업한 뒤 적용해야했다. 나중에는 작업 동기 자체가 떨어져 거의 출시하지 못할 뻔했다. 여러 형태로 출시되는 온갖 모드들과 늘어나는 복잡성을 더 이상 늘리기 싫었던 이유였다. 하지만 소설형 UI에 대한 VoC와 팀 내부의 의견들이 더해져 겨우 배포까지 진행했었다.

배포 이후 몇 달이 지나서 드는 생각은, 사실 굳이 별개의 모드를 만들어 넣을 필요는 없었다는 거다. 그 선택으로 더 많은 디자인 충돌 사항과 QA 사항들이 쌓여 몇 달의 기간이 허비된 것을 생각하면 더더욱 좋지 않은 선택이었다. 만약 돌아간다면 채팅형 UI의 스트리밍을 개선해보고 텍스트 기반의 소설형 뷰는 그걸 기반으로 따로 넣었을 것 같다.

그래도 좋은 UX를 가진 제품이 긍정적인 지표를 만들어낸다는 것을 확인했으니 이 기능을 좀 더 넓은 범위로 적용해볼 수 있을 것 같다. 앞으로는 제품 전체에 스트리밍 UI를 넣어보고싶다.

Footnotes

  1. https://arxiv.org/abs/2504.17999 ↩