문제
혜택홈은 혜택을 나열만 해서 사용자는 오늘 할 일과 받을 금액을 알기 어려웠고, 광고를 넣을 자리가 없어 방문이 늘어도 수익으로 이어지지 않았습니다.
운영자는 카드 순서나 노출 기간을 바꾸거나 미션을 새로 만들 때마다 개발과 배포를 거쳐야 했습니다.
그래서 하루 약 10만 번 열리는 화면을 운영자가 개발 없이 바꾸는 편성 데이터로 그리는 문제로 정했고, 운영자가 직접 다룰 대상은 카드와 광고의 순서, 노출 기간, 변경 이력이었습니다.
접근
- 화면 구성: 화면 영역마다 새 모듈을 만드는 안과, 이미 있는 공식 모듈·아이템 81종에서 재사용할 것을 찾는 안을 비교했습니다. 재사용하면 클라이언트가 새로 처리할 분기가 줄어서 재사용 안을 골랐고, 화면 타입 13개를 하나씩 검증해 재사용 3종과 신규 10종으로 나누며 이름 불일치 등 결함 5건을 먼저 찾았습니다.
- 경계: 전시와 콘텐츠 사이 R&R(역할과 책임)을 ADR로 정하고, 캐시는 판 서빙이 아니라 콘텐츠(소스 서비스) 쪽이 갖게 했습니다.
- 데이터 구조: 편성 값은 컬럼과 JSON으로 나눠 두었고, 사내 기존 사례 13건 등을 조사해 근거로 삼았습니다.
- 조회 방식: 래플은 건건 조회 대신 ‘지금 열려 있거나 7일 안에 열릴’ 시간창 쿼리 한 번으로 바꿨습니다.
- 응답 상한: 화면 전체 0.7초, 연결 서비스 1곳당 0.41초로 먼저 정했습니다.
- 혜택 총액: 시안에 적힌 총액(41,030원)을 실제로 받을 수 있는 최대 총액(91,020원)과 대조해 약 5만 원 차이를 찾았습니다.
결정
- 13개 화면 영역을 판 API 한 번으로 내려주는 SDUI(서버가 화면 구성을 내려주는 방식) 계약을 만들고, 캐러셀 3개는 기존 렌더러를 재사용하고 미션 4개는 계약 1종으로 합쳐 새 분기 3개를 줄였습니다.
- 판은 생성기와 변환기 두 계층으로 나눠, 섹션 종류가 늘어도 조립 코드를 고치지 않게 했습니다.
- 편성 어드민에서 카드와 광고의 순서, 노출 기간, 변경 이력을 개발과 배포 없이 관리하고, 기능 스위치 14개로 배포와 사용자 공개를 나눴습니다.
- 래플과 체험단 카드 한 장을 조회하는 API를 각 소스 서비스에 새로 만들고, 게이트웨이 경로 3개를 옮겼습니다.
결과
- 편성 어드민을 계획보다 13영업일 앞당겨 8월 20일에 열었습니다.
- 출시 전 알파에서 판 응답 p95 약 0.5초를 확인하고 목표를 p99 약 0.9초로 잡았는데, 운영에서는 9월 17일부터 27일까지 하루 약 10만 번 열리는 화면을 p99 약 0.25초(첫 이틀 약 0.4초), 서버 오류 0건으로 처리했습니다. 오픈 전 7일 동안에도 판 서버와 원천 서비스 모두 서버 오류는 0건이었습니다.
- 검증: 운영 수치는 혜택홈 전용 Grafana 대시보드에서 확인했습니다. 출시 전 자체 점검에서는 기존 테스트가 모두 통과하던 상태 API 결함을 찾아, 로그인 회원에게 미션과 자산 값이 비어 보일 뻔한 문제를 막고 회귀 테스트 2건을 더했습니다.
- 출시 전에 혜택 총액 오류(약 5만 원 차이)를 막았습니다.
- 전에는 카드 순서나 노출 기간을 바꾸려면 개발과 배포가 필요했지만, 지금은 운영자가 어드민에서 직접 바꿉니다.
기록과 자동화
- 문서: 전시·콘텐츠 R&R ADR, 판 전체 HLD, 설계 위키 19건, 지식베이스 PR 32건(29건 머지), 서버 런치 리뷰, 전시팀 인수인계 점검 9항목을 충족한 인수인계 문서를 남겼습니다.
- 대시보드: 혜택홈 전용 Grafana 대시보드에서 엔드포인트별 p95·p99 지연, 성공률, 서버 오류율, 초당 요청 수를 SLO와 견줘 봅니다.
- 프로세스 자동화: 시뮬레이터를 자동으로 띄워 AOS, iOS, 크롬 웹뷰에서 테스트하고 오류를 찾으면 티켓을 자동으로 만드는 Slack 봇과 스킬, 진행 현황을 알리는 봇, 인수인계 문서를 쓰는 플러그인을 만들었습니다.
- 누가 쓰나: 인수인계 문서는 넘겨받는 전시팀이, 대시보드는 오픈 뒤 운영 모니터링을 맡은 PM과 백엔드가, 편성 어드민은 운영자가 씁니다.
배운 점
- 화면에 보이는 숫자는 시안이 아니라 실제 데이터로 다시 계산한 뒤에 확정합니다.
- 상황: 시안의 혜택 총액은 41,030원이었는데 실제 데이터로는 최대 91,020원이 나와 약 5만 원이 달랐습니다.
- 한 일: 시안 숫자를 그대로 쓰지 않고 실제로 받을 수 있는 최대 총액과 대조해 차이를 찾고, 총액 산출 정책을 고쳤습니다.
- 결과: 혜택 총액 오류를 출시 전에 막았습니다.
- 테스트가 모두 통과해도 출시 전에 직접 다시 점검하고, 결함을 찾은 자리에는 회귀 테스트를 남깁니다.
- 상황: 상태 API가 소스 서비스에 회원 정보를 넘기지 않는 결함이 있었는데, 기존 테스트는 모두 통과하고 있었습니다.
- 한 일: 출시 전 자체 점검에서 결함을 찾아 고치고, 같은 자리를 다시 건드리면 실패하는 회귀 테스트 2건을 더했습니다.
- 결과: 로그인 회원에게 미션과 자산 값이 비어 보일 뻔한 문제를 출시 전에 막았습니다.
- 구현 방식이 갈리면 후보를 모두 만들어 같은 조건에서 재 본 뒤 고릅니다.
- 상황: 섹션 요약을 조회할 때 애플리케이션 메모리에서 집계할지, DB에서 집계할지 정해야 했습니다.
- 한 일: 메모리 집계와 DB 집계를 모두 구현해 같은 조건에서 속도를 쟀습니다.
- 결과: DB 집계가 지금 규모에서 3.3배, 1만 행에서 6.3배 빨라 DB 집계를 골랐습니다.
