Pjax 네비게이션으로 라디오가 끊기지 않게 만드는 방식

사이트에 라디오나 오디오 플레이어를 넣는 순간 페이지 이동은 단순한 링크 문제가 아닙니다. 일반적인 문서 이동은 전체 DOM을 다시 만들기 때문에 audio 요소도 사라지고 재생도 끊깁니다. 사용자는 이것을 사이트가 느리거나 불안정하다고 느낍니다.

교체 가능한 영역을 작게 잡는다

PJAX의 핵심은 빠른 애니메이션이 아니라 교체 범위입니다. 헤더, 푸터, 라디오 dock처럼 유지되어야 하는 영역은 그대로 두고 main.site-shell만 교체하면 전역 상태를 보존할 수 있습니다. 이때 페이지별 스크립트가 다시 초기화되어야 하므로 initPage 같은 재진입 가능한 초기화 함수가 필요합니다.

재진입 가능하다는 말은 여러 번 호출해도 이벤트가 중복으로 붙지 않는다는 뜻입니다. 필터, 댓글, 관리자 버튼처럼 이벤트를 붙이는 코드는 초기화 여부를 확인하거나 이벤트 위임을 사용해야 합니다. 그렇지 않으면 페이지 이동을 몇 번 했을 때 클릭이 여러 번 실행됩니다.

로딩 상태는 기능의 일부다

PJAX는 네트워크가 빠를 때만 자연스럽습니다. 응답이 늦어지는 순간 사용자는 클릭이 먹지 않은 것으로 판단합니다. 따라서 상단 로딩 바, 본문 스켈레톤, 포인터 차단 같은 피드백이 필요합니다. 기존 화면을 그대로 남겨두면 새로운 페이지를 기다리는지, 현재 페이지가 멈춘 것인지 구분하기 어렵습니다.

실패하면 문서 이동으로 돌아간다

PJAX는 점진적 향상이어야 합니다. fetch가 실패하거나 다음 문서에서 교체할 main을 찾지 못하면 일반 이동으로 되돌아가야 합니다. 이 원칙을 지키면 PJAX가 깨져도 사이트 전체가 깨지지 않습니다.

오디오를 끊기지 않게 만드는 작업은 결국 상태의 소유권을 정하는 일입니다. 전역 상태는 전역에 남기고, 페이지 본문은 교체 가능하게 만든다. 이 경계를 명확히 하면 라디오뿐 아니라 장바구니, 알림, 플레이어 같은 기능에도 같은 패턴을 적용할 수 있습니다.

실제 적용 메모

라디오가 끊기지 않게 하려면 페이지 전체를 다시 로드하면 안 됩니다. 이 사이트에서는 링크 클릭 시 가능한 경우 main.site-shell만 교체합니다.

흐름은 다음과 같습니다.

링크 클릭
-> fetch로 다음 HTML 요청
-> 새 문서에서 main.site-shell만 추출
-> 현재 main만 교체
-> initPage 다시 실행
-> 라디오 dock은 footer에 남아 계속 유지

여기서 실제로 한 번 문제가 있었습니다. 관리자 페이지에는 radio.js가 없어서 관리자에서 테마 변경 후 블로그로 이동하면 라디오가 새로 생기지 않았습니다. 새로고침하면 생긴 이유는 전체 HTML이 다시 로드되면서 radio.js가 로드됐기 때문입니다.

해결은 관리자 페이지에도 radio.js를 포함하고, 테마 적용 후 IGKRAPRadio.refresh()를 호출하는 것이었습니다. PJAX는 빠른 이동 기술이 아니라 전역 상태를 잃지 않기 위한 경계 설계입니다.

sequenceDiagram participant User as 사용자 participant Page as 현재 페이지 participant Next as 다음 HTML participant Radio as 라디오 Dock User->>Page: 링크 클릭 Page->>Next: fetch Next-->>Page: main.site-shell Page->>Page: main 교체 Page->>Page: initPage 실행 Page->>Radio: refresh Radio-->>User: 재생 상태 유지