Vol. 36

개발일지

페이지를 넘겨도 음악이 안 끊기게 — 방탈출 '히든페이지' 만든 이야기

방탈출 테마가 끝난 뒤에도 이어서 즐기는 모바일 히든페이지를 직접 만들었어요. 굿즈 QR로 들어오는 스토리 페이지인데, 만들면서 제일 골치 아팠던 건 '페이지를 넘길 때마다 배경음악이 처음부터 다시 시작되는' 문제였어요. iframe 셸 구조로 음악을 안 끊기게 만든 과정을 적어둡니다.

2026.06.25 · 발신 우체부J


title: "페이지를 넘겨도 음악이 안 끊기게 — 방탈출 '히든페이지' 만든 이야기" date: "2026-06-25" description: "방탈출 테마가 끝난 뒤에도 이어서 즐기는 모바일 히든페이지를 직접 만들었어요. 굿즈 QR로 들어오는 스토리 페이지인데, 만들면서 제일 골치 아팠던 건 '페이지를 넘길 때마다 배경음악이 처음부터 다시 시작되는' 문제였어요. iframe 셸 구조로 음악을 안 끊기게 만든 과정을 적어둡니다." tags: ["dev", "frontend", "audio", "mobile"] category: "dev"

방탈출 매장에서는 손님이 정해진 시간 안에서만 테마를 즐길 수 있어요. 그게 좀 아쉬워서, 테마가 끝난 뒤에도 집에서든 카페에서든 이어서 즐길 수 있는 '히든페이지' 를 만들었어요. 굿즈에 QR코드를 넣어두고, 그걸 찍으면 본편의 뒷이야기와 세계관을 확장한 스토리를 따라가는 모바일 웹페이지로 연결되는 구조예요.

빌드 도구 없이 순수 HTML/CSS/JS로 짜고 Vercel에 정적 호스팅했어요. 그런데 막상 만들다 보니 생각도 못 한 데서 막혔어요. 오늘은 그 이야기를 적어둘게요.

제일 골치 아팠던 건 '음악'이었어요

이 페이지는 분위기가 9할이거든요. 첩보물 같은 배경음악이 깔려 있어야 몰입이 되는데, 문제가 하나 있었어요. 페이지를 넘길 때마다 음악이 뚝 끊기고 처음부터 다시 시작되는 거예요.

당연한 일이에요. 도입부 → 브리핑 → 1막 → 2막 …처럼 페이지를 이동하면 브라우저는 새 페이지를 처음부터 로드하니까, 그 페이지에 들어있는 <audio>도 0초부터 다시 재생되죠. 손님 입장에선 한참 빠져들 만하면 음악이 끊겨서 김이 새는 거예요.

해결: 음악은 '바깥'에 두고, 내용만 iframe으로 갈아끼우기

여러 방법을 고민하다가 iframe 셸(shell) 구조로 갔어요. 핵심은 이거예요.

음악을 재생하는 <audio>는 절대 안 바뀌는 바깥 페이지에 두고, 실제로 넘어가는 내용물만 그 안의 iframe에서 갈아끼운다.

구조를 그림으로 그리면 이래요.

index.html  ← 이 페이지는 한 번 켜지면 안 바뀜 (<audio>가 여기 살아있음)
   └── <iframe> → intro.html → briefing.html → case1.html → ...
                  (손님이 넘기는 건 사실 iframe 안의 내용물)

손님은 페이지를 넘긴다고 느끼지만, 실제로 바뀌는 건 iframe 안쪽뿐이에요. 바깥 index.html은 처음 켜진 그대로 계속 살아있으니까, 거기서 돌고 있는 음악도 안 끊겨요. 이걸 적용하고 나서야 비로소 처음부터 끝까지 음악이 자연스럽게 이어지더라고요.

내용이 담긴 iframe과 바깥 셸은 postMessage로 대화하게 했어요. 예를 들어 1막에 들어가면 그 페이지가 바깥에 "지금 1막 분위기야"라고 신호를 보내고, 바깥은 거기 맞는 음악으로 부드럽게 바꿔주는 식이에요.

// iframe 안쪽 페이지에서, 현재 장면을 바깥 셸에 알려줌
window.parent.postMessage({ type: "hh-theme", theme: "case1" }, "*");

음악도 두 종류를 섞었어요. 첩보 본부 느낌의 메인 BGM은 짧은 오디오 파일을 루프로 돌리고(원본이 4MB가 넘어서 앞부분 30초만 잘라 0.5MB로 줄였어요), 각 막의 배경음은 파일 없이 Web Audio로 그때그때 만들어 깔았어요. 막마다 음계랑 박자를 다르게 줘서 같은 페이지인데도 분위기가 확 달라지게요.

자잘하지만 꼭 필요했던 모바일 처리들

손님은 100% 폰으로 들어와요(굿즈 QR로 찍으니까요). 그래서 PC에선 멀쩡한데 폰에서만 어색한 것들을 하나하나 잡아야 했어요.

  • 100dvh 사용 — 모바일 브라우저는 스크롤할 때 주소창이 나타났다 사라지면서 화면 높이가 출렁여요. 옛날 방식(100vh)으로 하면 그때마다 레이아웃이 흔들리는데, 100dvh로 바꾸니 흔들림이 사라졌어요.
  • 노치/둥근 모서리 대응viewport-fit=cover + env(safe-area-inset-*) 로 요즘 폰 위아래 안전 영역만큼 자동으로 여백을 줬어요. 안 그러면 버튼이 노치에 가려요.
  • 터치 타깃 키우기 — 버튼은 최소 48px. 작으면 손가락으로 누르다 자꾸 헛눌러요.
  • 회색 깜빡임 제거 — 모바일에서 버튼 누르면 회색 네모가 번쩍이는데, -webkit-tap-highlight-color: transparent 로 껐어요.
  • 더블탭 확대 방지touch-action: manipulation 으로 빠르게 두 번 눌러도 화면이 확대 안 되게요.
  • 한글 줄바꿈word-break: keep-all 로 어절 단위로만 줄이 바뀌게 했어요. 안 그러면 단어 중간에서 뚝뚝 끊겨서 읽기 불편하거든요.

이런 건 하나하나는 사소한데, 빼먹으면 "왠지 어설픈 페이지" 느낌이 나요. 모으면 '폰에서 매끄럽다'는 인상을 만드는 디테일이라 끝까지 챙겼어요.

음악 자동재생이 막히는 문제

마지막으로 하나 더. 브라우저는 사용자가 아무것도 안 눌렀는데 소리가 나는 걸 막아둬요(자동재생 정책). 그래서 페이지에 들어오자마자 음악이 안 나오는 게 정상이에요. 이건 손님의 첫 터치 순간에 음악을 살짝 풀어주는 식으로 해결했어요. "화면을 탭하세요" 같은 시작 안내가 그 역할을 겸하는 거죠.

마무리

별것 아닌 스토리 페이지처럼 보여도, 막상 만들어 보니 진짜 일은 '내용'보다 '끊김 없이 매끄럽게 굴러가게 하는 것'에 있더라고요. 음악이 안 끊기고, 폰에서 출렁이지 않고, 버튼이 안 가려지고… 이런 게 다 갖춰져야 비로소 손님이 이야기에 온전히 빠질 수 있으니까요.

직접 만들어서 좋은 건, 이 모든 디테일을 제가 원하는 그대로 100% 손볼 수 있다는 거예요. 어디가 어색하면 바로 그 자리에서 고치면 되니까요. 다음엔 이 히든페이지에 들어가는 퍼즐을 어떻게 짰는지도 한번 적어볼게요.