배경
9월에는 운영 요청이 두 갈래로 몰렸습니다. 하나는 강의 다시보기 개편(목록 구조, 일부 공개 대상자 관리, 플레이어 조작, 수강 여부 표기)이고, 다른 하나는 매번 코드로 문구를 바꾸던 포털 하단 띠배너를 관리자 화면에서 설정하게 만드는 일이었습니다.
둘 다 “클라이언트는 맞게 보냈는데 결과가 이상하다”는 증상이 있었습니다. 다시보기는 끝까지 본 학생도 ‘미수강’으로 표시됐고, 배너는 저장하면 ‘설정됨’이 뜨는데 화면에는 옛 문구가 그대로였습니다. 그리고 둘 다 배포 일정이 정해진 상태라, 이슈가 생기면 그 기능만 빼고 나갈 수 있어야 했습니다.
접근
다시보기 시청시간은 dev 데이터부터 봤습니다. 재생 중 상태로 남아 있고, 일시중지 시각이 없고, 누적 시청시간이 0인 세션이 44건이었습니다. 서버는 일시중지 신호를 받아야 재생 이후 경과시간을 시청시간에 더하는데, 클라이언트가 세 경로에서 그 신호를 보내지 않고 있었습니다. 팝업 X 닫기는 창만 닫았고(정지 이벤트를 구독하는 곳은 있었지만 발행하는 곳이 없었습니다), 끝까지 재생하면 로컬에서만 멈췄고, 탭을 닫으면 일반 fetch가 unload 중에 취소됐습니다.
세 경로를 각각 막았습니다. X 닫기는 정지 신호를 보낸 뒤 닫되 2초 데드라인과 3초 강제 닫기를 걸었습니다. 끝까지 재생은 서버 일시중지까지 포함한 정지로 바꾸고, effect 클로저가 최신 핸들러를 쓰도록 ref로 넘겼습니다. 탭 닫기는 keepalive 옵션으로 보내고, 모바일 사파리를 위해 pagehide도 등록하되 중복 전송은 막았습니다.
배너는 저장 API를 세 번 연달아 호출해 응답을 비교했습니다. 응답 id가 매번 달랐고, 조회는 가장 오래된 행을 돌려줬습니다. 저장이 갱신이 아니라 추가였던 겁니다. dev에서 한 대상에 8건이 쌓여 있었습니다. 서버 수정을 기다리는 동안 저장 전에 대상 설정을 조회되지 않을 때까지 지우는 우회(상한 12회)를 넣고, 저장이 실패하면 직전 값으로 되돌려 배너가 사라진 채 남지 않게 했습니다. 클라이언트 쪽 원인도 하나 있었는데, 클래스 목록이 늦게 도착할 때마다 편집 폼이 서버 값으로 리셋돼 입력 중인 내용이 날아갔습니다. 대상별로 한 번만 적용하는 가드로 막았습니다.
서버 결함이 의심될 때 ‘서버 문제 같다’로 넘기지 않고, 같은 호출을 반복해 응답을 비교한 기록을 남겼습니다. 그 기록이 서버 수정의 근거가 됐고, 나중에 우회 코드를 지워도 되는지 판단하는 기준도 됐습니다.
서버가 upsert로 고친 뒤에는 같은 방식으로 다시 확인했습니다. 저장 3회의 응답 id가 같고, 조회에 매번 최신 값이 나오고, 삭제 1회로 정리되는 것을 보고 나서 우회 코드와 실패 시 원복 로직을 지웠습니다.
배포는 기능 단위로 빠질 수 있게 운영했습니다. 배너 관리와 ‘신규 클래스는 학생 다시보기 기본 비노출’ 기능은 배포 직전 이슈가 보일 때마다 revert 커밋으로 빼고 나머지는 그대로 내보냈습니다. 원인을 고친 뒤에는 그 revert를 다시 되돌려 재적용했습니다. 기본 비노출은 폼 초기값에만 들어가 있어 폼 상태가 리셋되면 저장 데이터에서 빠질 수 있었기 때문에, 관리자가 직접 건드리지 않았다면 저장 시점에 반드시 포함하도록 고쳐 재적용했습니다.
다시보기 UX는 목록을 행 리스트로 바꾸고, 플레이어에 방향키·좌우 더블탭(280ms) 10초 이동을 넣었습니다. 강의시간은 별도 미디어 서비스에서 현재 페이지 영상 길이만 한 번에 받아 합계를 분 단위로 올려 표기했습니다. 일부 공개 대상자 팝업에선 2페이지에서 전체 체크를 하면 1페이지 선택이 지워지던 문제를 ‘현재 페이지 id를 기존 선택에 합치는’ 옵트인 옵션으로 고쳤고, 기존 SMS 화면 동작은 그대로 두었습니다.
결과
- 시청시간 0으로 남던 다시보기 세션(dev 44건)의 원인 3개 경로 모두 수정 — X 닫기, 끝까지 재생, 탭 닫기(
keepalive+ pagehide). - 배너 저장 결함 실측 → 한 대상 누적 8건 → 1건 정리, 서버 upsert 수정 확인 후 클라 우회 제거.
- 배너 관리 기능(13파일, 1,840줄 추가)과 다시보기 기본값 기능을 revert 4회 · 기능별 3차 적용으로 배포 일정과 분리해 운영.
- 다시보기 10초 이동, 실제 강의시간 표기, 페이지 간 전체 체크 유지, 대상자 삭제 시 목록 사라짐 수정.
배운 점
“클라이언트는 맞게 보냈다”는 말은 실측 전엔 가설일 뿐이었습니다. 다시보기는 실제로 클라이언트가 신호를 안 보내고 있었고, 배너는 실제로 서버가 행을 쌓고 있었습니다. 어느 쪽이든 같은 호출을 반복하고 저장된 데이터를 직접 확인하는 것이 가장 빨랐고, 그 기록이 서버팀과 이야기할 때도 그대로 근거가 됐습니다.
또 우회 코드는 지울 조건을 정해두고 넣어야 한다는 것도 배웠습니다. 배너 우회를 넣을 때 “서버가 upsert로 고치면 제거”라고 적어 두었고, 실제로 서버 수정을 같은 방식으로 확인한 날 바로 걷어냈습니다. revert 역시 마찬가지로, 기능 단위로 작게 쪼개 두니 배포 직전에 빼는 것도 다시 넣는 것도 부담이 없었습니다.