iframe 안의 CTA 버튼: 창 안에서는 문의 링크를 숨기는 규칙
듀오랩스의 데모 앱들은 두 곳에서 창으로 열립니다. 하나는 홈페이지의 데모 모달이고, 다른 하나는 브라우저 안에서 도는 웹 데스크톱(os.duolabs.co.kr)의 창입니다. 둘 다 iframe입니다.
데모 앱에는 "듀오랩스에 제작 문의" 버튼이 있습니다. 앱을 단독으로 열었을 때는 당연히 있어야 하는 버튼입니다. 그런데 이 앱이 창 안에서 열리면 이 버튼이 이상하게 동작했습니다. 홈페이지의 모달 안에서 누르면 모달 안에 홈페이지가 한 번 더 떴습니다. 웹 데스크톱의 창 안에서 누르면 웹 데스크톱 탭이 통째로 홈페이지로 넘어가 버렸습니다.
앱마다 알아서 처리했던 결과
형제 앱을 모아 보니 창 안에서의 동작이 세 가지로 갈려 있었습니다. 아홉 곳은 창 안에서 버튼을 숨겼습니다. 여섯 곳은 링크에 target="_top"을 달아 바깥 창 전체를 이동시켰습니다. 한 곳은 창 안에서 그대로 이동했습니다.
_top을 단 앱들은 나름의 이유가 있었습니다. 창 안에 홈페이지가 겹쳐 뜨는 것보다는 바깥으로 나가는 편이 낫다는 판단입니다. 그런데 웹 데스크톱에서는 이것이 가장 나쁜 결과였습니다. 사용자가 창 여러 개를 열어 두고 이것저것 보던 중에 버튼 하나를 누르면, 열어 둔 창이 전부 사라지고 탭이 홈페이지로 바뀝니다.
창 안에서도 보이게 하자는 첫 계획
처음에는 창 안에서도 버튼을 살리는 방향으로 설계했습니다. 웹 데스크톱이 창 주소에 표시를 붙이고, 앱 안의 버튼은 누르면 postMessage로 웹 데스크톱에 "홈페이지 창을 하나 열어 달라"고 부탁하는 방식입니다. 탭을 빼앗지 않고 새 창으로 홈페이지를 띄울 수 있습니다. 설계 문서에 규칙까지 적었습니다.
계획을 버린 이유
이 계획은 하루 만에 폐기했습니다. 질문을 바꿔 보니 답이 달라졌습니다. 창 안에서 이 앱을 보고 있는 사람은 누구인가. 홈페이지의 데모 모달을 연 사람이거나 웹 데스크톱에 들어온 사람입니다. 둘 다 이미 듀오랩스에 와 있는 사람입니다.
제작 문의 버튼은 다른 곳에서 우리 앱을 발견한 사람을 우리에게 데려오는 문입니다. 이미 우리 집 안에 있는 사람에게 우리 집 주소를 알려 주는 버튼은 필요가 없습니다. 창 안에서 보이게 하려고 postMessage 규약을 설계하는 것은, 없어도 되는 버튼을 위해 두 앱 사이의 약속을 하나 더 만드는 일이었습니다.
그래서 규칙을 하나로 정했습니다. 창 안에서는 CTA를 보이지 않는다. 예외를 두지 않습니다.
판정은 한 줄, 빠뜨릴 수 없게
창 안인지는 이렇게 판정합니다.
const embedded = window.self !== window.top;바깥 창이 다른 출처라서 window.top의 내용에 접근할 수 없어도, 이 비교 자체는 됩니다. 비교가 거짓이면 창 안입니다.
주의할 점은 서버 렌더링입니다. 서버에는 window가 없으니 서버에서 버튼을 그리고 브라우저에서 지우면, 창 안에서 버튼이 잠깐 보였다가 사라지는 깜빡임이 생깁니다. 그래서 서버에서는 그리지 않고, 브라우저에서 최상위 창인 것이 확인된 뒤에만 그립니다. 단독으로 연 페이지에서는 버튼이 한 박자 늦게 나타나지만, 창 안에서 깜빡이는 것보다는 낫다고 판단했습니다.
이 판정을 각 앱이 직접 쓰게 두면 언젠가 한 앱이 빠뜨립니다. 앞의 세 가지 동작이 갈린 것도 앱마다 따로 처리했기 때문입니다. 그래서 CTA 컴포넌트 자체가 이 판정을 기본으로 품게 했습니다. 앱은 CTA를 넣기만 하고, 창 안에서 숨는 것은 컴포넌트가 알아서 합니다.
한 번 더 확인하게 된 것
규칙을 정한 뒤, 한 앱의 바닥 링크에 target="_top"을 넣었다가 다시 뺀 일이 있었습니다. 그 사이에 웹 데스크톱 쪽 동작이 바뀌었기 때문입니다. 창 안의 링크는 창 밖의 동작에 기대고 있어서, 바깥이 바뀌면 안쪽의 올바른 답도 바뀝니다. 창 안에서 링크를 아예 보이지 않게 한 규칙은 이런 흔들림에서도 자유롭습니다. 보이지 않는 링크는 바깥이 어떻게 바뀌어도 틀리지 않습니다.
함께 읽기
- iframe 안의 분석 스크립트: 자사 사이트를 창으로 띄웠을 때 생기는 일저희 홈페이지를 저희가 만든 브라우저 안 데스크톱(os.duolabs.co.kr)에 창 하나로 띄웠습니다. 데모를 한 번 열어 본 사람이 방문 기록에는 두 번 찍혔습니다. 처음에는 창을 두 개 여니까 그렇겠거니 했는데, 세어 보니 문제는 개수가 아니라 그 숫자가 우리 통계 전부를 통과하고 있다는 쪽이었습니다.
- 웹 워커를 언제 꺼내는가: 지뢰찾기와 체스의 차이브라우저 안에서 도는 데스크톱을 만들고 있습니다. 창과 Dock과 터미널은 있는데 그 위에 올릴 앱이 없어서, 유틸리티 칸에 지뢰찾기를 하나 넣었습니다. 잘 돌길래 다음으로 체스를 만들기 시작했는데, 규칙을 다 짜고 엔진을 붙이려는 자리에서 한 번 멈춰야 했습니다. 지뢰찾기에는 없던 문제가 있었기 때문입니다.
- React error 300: 조건부 return 뒤의 훅과 가끔 나는 오류관리자 화면에 들어갈 때마다 오류 카드가 한 번씩 떴습니다. 「문제가 발생했습니다. 잠시 후 다시 시도해 주세요.」 다시 시도를 누르면 그냥 들어가집니다. 그래서 한동안 넘겼습니다.
- WebP가 JPEG보다 커질 때: 사진 종류별 WebP·AVIF 실측인쇄기 롤러 견적 사이트를 만들면서 화면에 나가는 사진을 전부 WebP로 정했습니다. 제품 사진 네 장으로 재 보고 내린 결론이었습니다. 그 뒤에 설계도 사진 세 장이 들어왔고, 같은 스크립트에 그대로 태웠습니다. 이 글을 쓰려고 다시 재 보니 그중 두 장은 WebP가 JPEG보다 컸습니다.
- 웹 퍼블리셔와 프론트엔드 개발자 차이: 산출물이 갈리는 지점견적서에 이런 두 줄이 나란히 놓이는 경우가 있습니다.