💽 Server
백엔드 개발자가 만들어낸 유저 임팩트
최소의 직군과 인원으로, 유저 임팩트를 남긴 경험에 대해 공유합니다.
유저의 재방문 횟수를 높이기 위한 고민
우리가 만드는 서비스의 핵심 기능은 ‘대출비교’이다.
이러한 서비스의 특징상 유저는 1-2차례의 비교를 돌려본 후에 대출을 시작하거나 앱을 종료한다.
그 요인은 여러가지가 있을 것이다.
가장 먼저 살펴봐야 하는 것은 다음과 같다.
- 단순 호기심으로 대출비교를 해보기 위해 앱을 실행한 경우.
- 정말 대출을 진행하기 위해서 앱을 실행한 경우.
우리의 수익 구조를 살펴볼 때, 우선시 되는 유저는 2번째 케이스다.
그럼에도 그 유저들이 1-2차례의 비교 끝에 앱을 종료한다면 그 이유는 다음과 같이 유추할 수 있다.
- 정말 대출을 진행하기 위해서 앱을 실행한 경우.
- 원하는 조건의 대출이 존재하지 않아서.
- 지금 바로 시작하기엔 고민해볼 점들이 남아있어서.
여기서 원하는 조건의 대출이 존재하지 않는 경우는 당장 해결할 수 없다.
- 다른 서비스에서 더 좋은 조건을 제공하거나
- 아예 본인의 신용등 기준이 이상치의 금액과 이자를 받을 수 없거나
- 특정 금융사를 원했으나, 해당 금융사가 현재 서비스내에 없거나
이런 케이스들은 앞으로 금융사들과 협의 등 외부 요소가 포함되어 있기 때문에 우리의 욕심만으로는 해결할 수 없이 기다려야한다는 공통점이 있다.
때문에 우리가 우리만의 기능으로 해결하고자 한다면 유저는 하나의 경우로 귀속된다.
- 정말 대출이 필요해서 앱을 실행했고
- 어떤 이유에선지 지금 바로 시작하기엔 고민해볼 것이 남아서
사실 이 경우에도 ‘어떤 이유’인지가 중요하다.
다른 대출 비교를 통해 그 결과를 기다리고 있는 상황이면 예외로 둘 수 있다.
또 접근하는 유저들의 나이대가 마냥 젊지는 않다는 점에서 앱의 사용성이 불편하거나, 혹은 서비스의 신뢰도가 불확실한 경우도 있을 것이다.
이 경우들도 우리 서비스의 발전을 통해 해결할 수 있겠지만, 개발자뿐만 아니라 여러 직군이 모여서 논의해볼 필요가 있다. (실제로 진행되고 있다)
결과적으로 지금 우리가 다시 불러올 수 있는 유저의 상황은 다음과 같다.
- 정말 대출이 필요하지만 고민만 하다가 잠시 멈춘 유저.
이 상황을 풀어줄 수 있는 실마리는
- 오늘 대출 비교를 진행한 조건이 오늘까지라는 점, 즉 유한하다는 점을 어필하고 한번 더 알림을 통해 다시 앱에 접속하게 하는 것.
AS-IS
fun 대출비교() {
...
// when job done
alert.toUser()
}
대출비교가 끝났을 때 바로 알림톡을 발송하는 기능이 있고
fun alert() {
val users = 오늘 대출비교를 실행한 유저들 중 알림을 거부하지 않은 유저를 불러온다.
...
alert(users)
}
특정 시간이 되면 오늘 하루 알림 대상자들에게 알림 발송을 통해 다시 한번 앱에 접속하여 결과 확인을 요청한다.
What’s issue
레거시 코드로 인한 리팩토링 이슈는 별개로 두고, 우선 유저에게 이 기능이 어떻게 느껴질 지 생각해보자.
첫째. 대출비교가 끝난 뒤에 알림톡은 다른 알림 방식으로 변경할 수도 있고 어차피 대출비교가 오래걸리지 않기 때문에 사용하지 않을 기능일 수 있지만, 동시에 5:5 확률이 아닌 확실하게 이 기능이 유저에게 도움이 된다 vs 되지 않는다는 유의미한 데이터를 얻지 못했다.
둘째. 특정시간에 배치성 알림은 24시가 거의 다 되어서야 동작한다. 실제 데이터를 수집해본 결과, 이 때 알림을 받고 앱에 접속하는 사람들은 한 자릿수 퍼센트이고 이후 대출까지 간 경우까지 따져볼 때 사실상 무의미했다.
결과적으로 우리는 2번째의 경우, 즉 배치성 알림을 뜯어보기로 했다.
TO-BE
가장 먼저 배치성 알림을 제거하자는 극단적인 의견은 없었다.
사실상 무의미한 이유는 발송 시점에 문제가 있다고 봤다.
때문에 배치 시작 시간을 더 앞당기는 것으로 1차 해결이었다.
그 다음, 새로운 알림을 추가하기로 했다.
- 유저가 낮 2시에 대출비교를 진행한다.
- 유저는 밤 11시 30분쯤 오늘 진행한 비교가 사라져간다는 알림을 받는다.
저 두 푸시의 간격이 너무 길기 때문에 그 사이에
- 유저는 대출비교를 돌리고 일정 시간이 지난 뒤에 다시 앱에 접속해달라는 알림을 받는다 (실제 문구는 적지 않도록 하겠다)
이 때 필요한게 바로 ‘시간 지연 푸시’였다.
For the Goal : RabbitMQ를 활용한 지연푸시 개발
현재 알림을 보내기 위한 메시지 큐로 우리는 RabbitMq를 사용하고 있다.
그래서 RabbitMq 서치를 해본 결과 다음과 같은 오픈소스를 찾을 수 있었다.
rabbitmq/rabbitmq-delayed-message-exchange
사용은 간단하다. 필요하다는 사전 작업을 진행해준 뒤에는 다음과 같은 코드를 작성해주면 된다.
// ... elided code ...
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-delayed-type", "direct");
channel.exchangeDeclare("my-exchange", "x-delayed-message", true, false, args);
// ... more code ...
여기서 args.put() 의 2번째 인자로 정수값을 줄 수 있다. 예를 들면
// ... elided code ...
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-delay", 10000);
channel.exchangeDeclare("my-exchange", "x-delayed-message", true, false, args);
// ... more code ...
정수는 딜레이 될 시간을 의미하고 단위는 밀리세컨드다.
그리고 최소, 최대 범위는 다음과 같다.
Delay > 0, Delay =< ?ERL_MAX_T
우리에게 필요한 딜레이 시간은 10분에서 최대 30분 정도였으므로 충분히 커버 가능한 범위다.
토픽도 사용가능하기에 고려해볼만 하다. (우리는 사용하지 않았다.)
대략 원리를 추적해보면, 데드 큐에 먼저 심어두고 정해진 시간 뒤에 데드 큐에서 다시 꺼내면서 동작하는 방식이었다.
때문에 기본 RabbitMq에서 사용하는 방식보다는 속도가 느린편에 속한다.
그래서 모든 메시지를 이 방법을 통해 전달해선 안된다.
나는 유연한 구조를 위해서 인터페이스를 만들어두었다.
fun example(message, time) {
// message 는 유저와 알림 내용을 포함한다.
// 정해진 time 뒤에 message 가 발송된다.
}
위 코드는 그 인터페이스로 만들어진 예시 메소드다.
이제 로직의 어느 곳에서든 보낼 메시지와 딜레이 시킬 시간만 적어두면 바로 적용할 수 있다.
또 time의 경우엔 개발자의 입장에선 밀리세컨드를 그대로 쓸까 싶었지만, 마케터분들과 기획자분들은 분 단위로 소통하였기에 그에 맞춰 설계했다.
Result
테스트 기간동안 10분, 20분, 30분 등 언제 다시 접속하는 횟수가 가장 많을지 데이터를 확보하였고 그 결과는 30분이었다.
결과적으로 재접률은 약 24% 이상 증가했다는 발표 결과를 봤다. (사내 블로그가 아니라 개인 블로그라 자료를 가져올 순 없..)
특히 뿌듯했던 것은 백엔드 개발자로써 보통 비즈니스 임팩트, 유저 임팩트를 얻기위해선 반드시 디자이너와 프론트엔드 등 여러 직군이 합심하여야 한다.
그러나 이번 작업은 데이터 엔지니어와 백엔드 개발자간의 소통으로 간결하게 얻어낸 유저 임팩트였기에 아주 유의미한 경험이라고 생각한다!