🎫
네이버와 카카오의 QR 체크인 페이지 비교해보기

웹 기반으로 만들어진 네이버와 카카오의 QR 체크인 페이지를 기술적으로 분석하고 구현 방식을 비교해봅니다.

May 02, 2021


FrontEnd JavaScript


코로나 19가 국내에 창궐한 지도 어느덧 1년이 넘었습니다. 이러한 시대적 상황에 맞추어, 지난 1년 동안 우리나라에서도 코로나 19와 관련한 여러 서비스가 만들어졌습니다.

그중에서도 현재까지 유용하게 쓰이고 있는 서비스가 있습니다. 사람이 여러 명 모이는 장소라면 어디를 가더라도 무조건 사용해야 하는 서비스, 바로 QR 체크인입니다.

1 QR 체크인

전자출입명부라고도 불리는데, 일상생활에서는 보통 QR 체크인이라고 더 많이 부르는 것 같습니다. 이러한 QR 체크인 서비스의 개발 덕분에, 혹시라도 내가 방문한 장소와 확진자의 동선이 겹쳐서 발생할 수 있는 2차 감염 가능성을 기술적으로 추적할 수 있게 되었습니다.

사실 사용자의 입장에서는 단순하게 QR 코드를 띄워주는 것뿐이다 보니, 큰 생각 없이 본인인증을 하고 사용하게 됩니다. 하지만 이 페이지를 만들어야 하는 기획자와 개발자의 입장에서 생각해보면 어떨까요?

그래서 오늘은 프론트엔드 개발자의 입장에서 QR 체크인 페이지의 요구사항을 분석하고, 네이버와 카카오가 서비스를 기술적으로 어떻게 구현했는지를 분석해보려고 합니다.

이 포스트를 통해 QR 체크인 페이지의 기술적인 분석 결과에 대해 궁금하신 기획자 또는 개발자분, 그리고 상용 웹 서비스를 디버그하고 분석하는 방법에 관해 관심이 있는 개발자분들에게 도움이 되기를 바랍니다.

QR 체크인 페이지의 요구사항

1 QR 체크인 페이지의 사용자는… 무려 전 국민입니다

위에서 기획자와 개발자의 입장에서 생각해보자고 이야기를 했던 것을 다시 떠올려 봅시다. 어떤 서비스를 분석하기 위해서는 해당 서비스가 어떠한 상황에서 어떠한 문제를 해결하기 위해 만들어진 도구인지에 대해 끊임없는 질문을 던지면서 접근해야 합니다. 다른 말로는 요구사항(requirement) 분석이라고도 하죠. 이러한 태도가 이유는 대부분의 서비스가 특정한 목적을 이루기 위한 도구로 활용되기 때문입니다.

그럼 다시 QR 체크인 페이지의 이야기로 돌아와 보죠. 페이지의 목적은 간단합니다. 개인을 식별할 수 있는 고유한 QR 코드를 생성하는 것입니다. 하지만 이것이 말처럼 쉽지 않죠. 현실 세계에서는 이 목적을 이루기 위해 고려해야 하는 수많은 조건이 있기 때문입니다.

우선 첫 번째, 사용 대상이 전 국민입니다. 그러니까 말 그대로 스마트폰을 갖고 있는 남녀노소 누구나 사용할 수 있어야 합니다. 그래서 최대한 쉽고 간단한 UI와 UX를 고민해야 합니다. 당연하게도 복잡한 기능이나 용어가 들어가서는 안 됩니다.

두 번째, 사용자의 스펙트럼이 넓고 두꺼운 만큼 트래픽이 많이 발생할 것이고, 그에 반해 응답 속도는 빨라야 합니다. 가능하다면 웹 페이지가 빠르게 화면에 그려져야 하고, 서버에서는 혹시 트래픽이 몰리더라도 견고하게 버텨낼 수 있어야 합니다.

세 번째, 어떤 정보를 이용해 개인을 식별할지도 결정해야 합니다. 이름과 주민등록번호의 조합 같은 개인정보는 고유하지만, QR 코드에 싣기에는 너무 민감한 정보들입니다. 각 서비스에서 사용자를 고유하게 식별할 수 있는 데이터를 비롯해, 어떤 값을 추가로 실은 후 QR 코드를 만들지 고민해야 합니다.

네 번째, 개인정보 유출과 데이터 위변조를 막기 위해 주기적으로 정보를 갱신해야 합니다. 누군가 나의 QR 코드를 도용할 수도 있고, QR 코드의 내용을 위조 또는 변조할 수도 있기 때문입니다. 현재는 월 1회 SMS로 본인 인증을 하고, 한 번 발급된 QR 코드는 15초간 유효하다는 암묵적인 규칙이 정해졌는데, 이러한 규칙을 어떤 기준으로 정하고 이것이 기술적으로 구현 가능한지도 고려해야 합니다.

마지막으로 이 서비스는 시대적 상황상 최대한 빨리 개발되어야 했습니다. 하루가 멀다하고 확진자가 늘어나는 상황에서 방역 당국이 최대한 빠르게 기능 개발을 요청했다는 뉴스가 있습니다. 방역 당국에서는 아무래도 접근성을 고려해야 하기 때문에, 대부분의 국민들이 일상적으로 사용하고 있는 네이버와 카카오 앱에 해당 기능을 실어 달라고 부탁한 것이겠죠.

만약 제가 이 개발을 맡게 되었더라면 정신이 아득해지지 않았을까… 라는 생각이 드네요. 아무튼 네이버와 카카오에 다니시는 똑똑하고 멋진 개발자분들께서 서비스를 뚝딱뚝딱 만들어주셨습니다. 참고로 네이버가 6월에 먼저 QR 체크인 기능을 출시했고, 카카오에서는 개발적인 문제로 인해 7월부터 서비스를 시작했다는 뉴스가 있네요.

QR 체크인 페이지는 왜 웹으로 개발되었을까

웹과 앱 단순하게 개발하는 언어의 차이 뿐만 아니라, 플랫폼 자체의 특성을 고려해야 합니다.

결론부터 이야기하자면 짧은 시간 내에 빠르게 개발해야 했고, 사용자에게 항상 최신 배포를 제공하기 위함이 아닐까 추측해봅니다.

우선 웹을 이용하면 HTML과 CSS, JavaScript만으로도 간단하게 사용자에게 보이는 뷰(View)를 만들 수 있죠. 같은 페이지를 네이티브 앱으로 만든다고 하면 안드로이드와 iOS용 앱을 별도로 만들어야 하므로 비효율적입니다. 네이티브 언어로 개발할 만큼 퍼포먼스가 중요한 것도 아니고, 디바이스 접근 권한이 필요한 것도 아니구요.

인앱 브라우저를 통해 렌더되기 때문에 안드로이드 기기와 iOS 기기 사이에서 발생할 수 있는 크로스 플랫폼 걱정을 하지 않아도 되는 것 역시 장점입니다.

물론 웹뷰를 쓴다면 크로스 브라우징을 고려해야 한다는 단점이 있긴 합니다. 왜냐하면 시스템의 기본 웹뷰가 Chrome과 Safari를 이용해 그려지거든요. 그래도 크로스 플랫폼에서 발생하는 이슈보다는 더 쉽고 유연하게 대처할 수가 있으니까요. (뭔가 조삼모사 같기도 하지만…)

배포 환경 역시 자유롭습니다. 네이티브 앱은 최신 상태를 유지하기 위해서는 구글 플레이나 앱스토어를 통해 앱 업데이트를 거쳐야 합니다. 하지만 이게 시간과 데이터를 많이 잡아먹기도 하고, 단순하게 귀찮아서 앱 업데이트를 안 하시는 분들도 계십니다. 그래서 앱을 사용하는 유저들은 일반적으로 각자의 상황에 따라 다른 버전의 앱을 쓰게 됩니다. 필수 업데이트라면 강제로 업데이트를 하게 만들수도 있는데, 이게 UX상 좋은 것이 아니기도 하고 리스크가 커서… 권장되는 방법은 아닙니다.

반면 웹은 브라우저에 고유한 주소를 입력하고, 해당 웹 페이지에서 현재 제공하고 있는 가장 최신의 소스 코드를 가져옵니다. 그래서 대부분의 경우에는 최신 상태를 유지할 수 있습니다. 여기서 대부분이라고 말한 이유는 그렇지 않은 경우도 있다는 것인데, 바로 브라우저에서 성능 최적화를 위한 목적으로 예전에 받아왔던 데이터들을 캐싱해서 최신 상태가 반영이 안 되는 경우도 있다는 의미입니다. 하지만 캐싱된 데이터들을 강제로 초기화하게 만드는 기법들이 많이 있고, 확실히 네이티브 앱보다는 쉽고 빠르게 최신 버전의 서비스를 제공할 수 있습니다.

이러한 이유로 QR 체크인 페이지가 웹으로 개발된 것이 아닐까 생각해봅니다.

최신 네이버 앱(11.0.6 버전 이후)부터는 QR 체크인 기능이 웹뷰가 아니라 네이티브 앱의 기능으로 실리게 됐는데… 이것이 바로 웹으로 빠르게 구현하고, 나중에 앱으로 옮겨서 성능을 챙기는 경우가 아닐까 싶습니다.

본격적인 기술 관련 이야기로 넘어가기 전에

저는 아래와 같은 관점에서 두 서비스를 비교했다는 점을 참고 바랍니다.

  • 분석 방식은 브라우저의 개발자 도구를 통해 다운로드된 웹 서비스의 소스 코드에 중단점(breakpoint)을 걸고 디버그를 시도했습니다.
  • 위처럼 저는 빌드된 소스 코드를 기반으로 분석을 시도했기 때문에, 저의 해석 방식이 네이버나 카카오에서 실제로 의도한 방식과는 다소 차이가 있을 수 있습니다.
  • PASS 앱을 이용해서도 QR 체크인을 할 수 있지만, 웹으로 구현되었다는 정보를 확인할 수 없었습니다. 그래서 이번 비교 대상에서는 제외하였습니다.

네이버 뜯어보기

자, 서론이 길었습니다. 그럼 네이버에서 제공하고 있는 QR 체크인 페이지에 바로 들어가 봅시다. 접속하고 있는 브라우저에서 네이버 로그인이 되어있는 상태여야 합니다.

1 위 주소에 접속하여 개발자 도구로 DOM을 확인한 모습

코드를 까보자마자 처음으로 든 생각은 단순하네? 였습니다. 그대로 남아있는 주석, 단순명료한 ID와 클래스, 별도의 규칙 없는 CSS 네이밍들이 보였거든요. 아무래도 별도의 번들링 도구를 쓴 것 같지는 않고, 그냥 쌩 HTML에 작업을 한 게 아닐까 싶었습니다.

이제 서비스의 핵심이 되는, QR 코드 부분의 HTML을 까보도록 하겠습니다.

1 velid가 뭐지…? 혹시 valid 오타인가?

네이버에서는 QR 코드를 애초에 서버에서 Base64로 인코딩한 상태로 보내주는 것 같았습니다. 그리고 해당 이미지 소스를 <img> 태그로 렌더하고 있습니다.

15초가 지나면 현재 QR 코드가 만료되고, 두 번까지는 자동으로 새로고침이 되면서 갱신이 됩니다. 세 번째부터는 시간이 만료되면 불투명한 흰색 레이어로 QR 코드를 덮어버리고, 그 위에 있는 새로고침 버튼을 수동으로 눌러야만 합니다.

근데 이거 왠지 클래스 이름이 오타 같더라구요. velid라는 단어는 사전에 없던데… 실수겠죠?

1 접근성 관련한 처리가 눈에 띄었다

계속해서 HTML을 계속 둘러보고 있었는데, 접근성 관련해서 별도의 처리를 해준 코드가 눈에 띄었습니다. 저 blind 라는 클래스는 내부에 텍스트가 들어있음에도 불구하고 사용자에게 보이지는 않는데요, 바로 다음과 같은 스타일이 적용되어 있기 때문입니다.

.blind {
  position: absolute;
  overflow: hidden;
  clip: rect(0 0 0 0);
  width: 1px;
  height: 1px;
  margin: -1px;
}

즉 텍스트가 있긴 한데, widthheight을 고정하고 overflowhidden으로 주면서 안 보이게 숨긴 거죠. 아무래도 시각 장애인들을 위한 접근성 유틸 클래스인 듯합니다. 일반 사용자는 볼 일이 없는 텍스트이지만, 시각 장애인이 스크린 리더기로 웹 페이지에 들어오게 된다면 이미지나 아이콘들이 텍스트로 인식이 되어 읽힐 것입니다.

사실 저는 미처 생각하지 못했던 부분이었습니다. 위에서 이야기했던 것처럼 QR 체크인 페이지의 사용자는 전 국민이고, 그중에는 장애인도 포함이 되어 있다는 점을 간과했던 것이죠.

한편으로는 접근성은 보통 ARIA 어트리뷰트를 쓰는 것이 사실상 표준인데, 네이버는 왜 자체적으로 요런 유틸 클래스를 만들어 관리하는 것인지도 궁금해졌습니다.

아, 물론 aria- 어트리뷰트도 잘 쓰고 있긴 합니다. 특히 aria- 어트리뷰트와 CSS 선택자만을 이용해서, 별도의 JavaScript 없이도 툴팁을 구현한 것이 신기했습니다. 접근성과 시멘틱한 문법도 챙길 수 있으니, 일석이조네요.

1 아무래도 마지막 업데이트가 21년 4월 16일 버전인가보다

위는 스크립트 파일입니다. 주소 마지막에 v= 라는 쿼리스트링을 붙여서 페이지가 업데이트 되었을 때 캐싱된 스크립트를 읽어오지 않게 처리를 해주고 있네요. 위의 common이 붙어있는 스크립트는 통계나 간단한 유틸 함수들이 모여있는 스크립트인 것 같고, 아래쪽 privacyQR 스크립트가 이제 본격 비즈니스 로직을 담은 스크립트인 것 같습니다.

그리고 외부 스크립트를 <body> 태그의 제일 아래 부분에 몰아넣고 있습니다. 혹시라도 외부 스크립트를 <body> 태그의 끝에 몰아넣는 이유에 대해 잘 모르시는 분들은, 제가 이전에 작성했던 『스크립트의 실행 시점을 조절하는 Async와 Defer 속성』 포스트를 한 번 읽어보시기를 추천해 드립니다.

1 단 50KB!

다음으로는 개발자 도구의 네트워크 탭에 들어가 보았습니다. 코드가 단순해 보여서 그런지, 받아오는 데이터의 크기도 많지 않았습니다. 캐싱 기능을 끄고 새로고침을 해봤을 때, 네트워크로 전송받은 데이터가 약 50KB 정도 되네요. 덕분에 콘텐츠를 준비하는 데 걸리는 시간도 0.2 ~ 0.3초 정도로 아주 빠르게 유지가 되네요. 이 부분은 사용자 경험에서 좋은 영향을 줄 수 있을 것 같습니다.

1 나름 반응형 디자인도 지원한다

CSS 코드를 쭉 살펴봤는데 미디어 쿼리(@media)를 이용한 반응형 처리가 되어 있더라구요. 그래서 화면 사이즈를 늘렸다 줄였다 하면서 변화를 살펴보았습니다. 스마트폰을 가로로 눕히거나 태블릿, 웹에서 접근하면 저런 모양이 나옵니다.

1 ⎛⎝(•‿•)⎠⎞⎛⎝(•‿•)⎠⎞ EZ! ⎛⎝(•‿•)⎠⎞⎛⎝(•‿•)⎠⎞

전체적인 JavaScript 코드는 요렇게 생겼습니다. 전체 70라인짜리의 아주 간단한 코드입니다. 아무래도 본인 인증을 하고 그런 것들을 다른 페이지에서 별도로 처리해주기 때문에, QR 체크인 페이지에서는 단순히 QR 코드만 보여주면 되어서 프론트엔드의 로직이 간단해지지 않았나 싶네요.

위의 로직은 15초 동안 유효한 QR 코드를 띄워주고, 만료되면 두 번까지 자동 새로고침을 해주는 로직입니다.

1 묵시적 타입 변환에, 존재하지 않는 프로퍼티 접근까지… 어질어질하다 그죠?

근데 코드가 좀 뭐랄까… 이상했어요. 좋게 말해서 트리키하게 동작하고 있었고, 솔직하게 얘기하자면 좀 얼렁뚱땅 돌아가고 있었습니다. JavaScript에서 Number형 변수에 String을 더해서 묵시적인 타입 변환을 활용하고 있고, Number형 변수에서 length 속성을 찾으면서 런타임 에러가 아슬아슬하게 나지 않는 부분도 있습니다.

카운팅하는 횟수도 뭐 1, 2, 3 숫자로 세는 게 아니라 1, "11", "111" 이런 식으로 스트링을 뒤에 붙이고 그 길이를 세는 방식으로 이루어져 있었습니다. 따옴표 표시를 보시면 아시겠지만, 두 번째부터는 스트링으로 타입 변환이 일어난 걸 확인할 수 있습니다.

쿠키에 저장할 때는 타입이 String으로 바뀌는데, 처음에는 이 사실을 모르고 그냥 쓰고 있는 것인가 라는 생각이 들었습니다. 근데 그렇다기에는 스트링 길이를 체크하는 조건문으로 판단을 하고 있어서, 이게 의도된 것 같기도 하고… 쩝, 잘 모르겠더라구요.

var isNaverAndroidApp =
  navigator.userAgent.indexOf("NAVER(inapp; search") != -1 &&
  navigator.userAgent.indexOf("Android") != -1;

뭐 아무튼 계속해서 코드를 살펴보죠. 스크롤을 쭉 내리다가 웹과 앱을 식별하고 분기 처리를 하는 JavaScript 코드를 발견했습니다. 그래서 이번에는 안드로이드 앱을 이용해서 QR 체크인 페이지에 다시 접속해보았습니다. 그러자 웹에서는 볼 수 없었던 UI가 등장했습니다. 우측 상단의 홈 화면의 추가 버튼툴팁, 그리고 중앙의 홈 화면에 추가 버튼이 생겼네요.

역시, 네이버 앱에서도 유저 에이전트 뒤에 앱을 식별할 수 있는 스트링을 덧붙여서 넣어놓나 봅니다. 사실 예전 회사에서 웹뷰 개발을 할 때도 이런 방식으로 앱인지 아닌지를 구별했거든요. 그래서 개발자 도구에서 유저 에이전트를 조작한 후, 다시 새로 고침을 눌러보았습니다.

1 짜잔, 앱에서만 떴던 팝업을 웹에서도 뜨게 만들었다

역시, 앱과 똑같은 상태의 화면이 나오게 되었습니다. 그렇다면 웹에서 버튼을 누르면 어떻게 될까요? 사실 저 버튼을 누르면 아래의 URL로 리다이렉션이 됩니다.

if (isNaverAndroidApp) {
  location.href = "naversearchapp://addshortcut?url=...&icon=...&title=...";
}

스키마를 보아하니, 네이버 앱에서 지원하는 딥링크인 것 같네요. 저렇게 커스텀한 형태의 스키마를 만들어서, 웹에서 네이티브 앱으로 링크를 걸어줄 수 있습니다. 이때 넘겨주고 싶은 정보를 쿼리스트링으로 전달을 해주는 것 같네요.

웹에서는 저 버튼을 누르면 등록된 핸들러가 없어서 앱을 실행시킬 수 없다는 오류가 콘솔에 뜨고, 이동이 되지 않습니다.

이것 외에도 자잘한 몇 가지 기술들이 있습니다.

1 이미지를 묶어서

모든 에셋이 포함된 이미지를 하나만 불러오고, CSS의 background-image를 이용해 화면에 나타나게 하는 방식을 쓰고 있습니다. 보통 예전 웹사이트에서 리퀘스트 횟수를 줄이기 위해 사용되던 기술입니다.

혹시 검색엔진 규칙도 명세했는지 싶어서 robots.txt 파일을 확인해봤는데, disallow가 설정되어 있었습니다. 즉 네이버의 QR 체크인 페이지를 검색해서 들어올 수는 없게 되어 있습니다.

네이버 총평

네이버 쪽의 QR 체크인 페이지는 확실히 가볍고 단순하다 였습니다. 단순하게 QR 코드와 관련된 코드만 있고, 난독화도 되어 있지 않았습니다. 그래서 누가 보더라도 쉽게 코드의 구조를 파악할 수 있었을 것 같아요. 아마 서버 쪽에서 복잡한 비즈니스 로직들이 다 처리되고 있기 때문에 프론트엔드 쪽 코드가 간단해지지 않을까라는 생각을 했습니다.

다만, 이 프론트엔드 쪽 코드가 그렇게 잘 작성된 것은 아니었습니다. 별도의 외부 라이브러리를 따로 안 쓰고 자체적인 라이브러리만 사용해서 그런지 비효율적인 부분이 눈에 띄었습니다.

카카오 뜯어보기

다음으로는 카카오에서 제공하고 있는 QR 체크인 페이지를 뜯어보겠습니다. 카카오톡을 통해서도 접근할 수 있지만, 그러면 디버깅을 할 수 없으니 웹으로 접속을 시도해보겠습니다. 다음이나 카카오의 로그인 페이지를 이용해 미리 로그인된 상태여야 합니다.

1 5252, 돌아가라…

하지만 카카오는 애초에 인앱 웹뷰가 아니면 접근을 못 하게 막아놓았습니다. 하지만 어림도 없지! 바로 개발자 도구로 유저 에이전트를 손보고, 장벽을 뚫어냈습니다.

1 짠, 뚫었습니다

QR 코드가 잘 나오네요. 개발자 도구로 DOM을 보니, 네이버와 비교했을 때 확실히 복잡하고 뭐가 많다는 것을 알 수 있습니다. 접근성 관련해서도 aria- 태그가 나름 잘 활용이 되고 있었습니다.

특히 그중에서도 data-role 이라는 어트리뷰트가 눈에 띄어서, 해당 엘리먼트의 CSS 속성을 수정하면서 모두 렌더시켜보았습니다.

1 단순하게 QR 체크인 페이지만 있는게 아니었습니다!

위의 사진을 보시면 아시겠지만, QR 체크인 기능뿐만 아니라 약관동의와 본인인증을 비롯한 각종 페이지, 그리고 많은 UI 컴포넌트들이 이미 DOM에 그려진 상태였습니다. 이들을 단순하게 CSS로 숨기고, 상태에 따라 JavaScript로 조건부 렌더를 하는 것이었던 거죠.

1 좀 더 크다

그래서인지 확실히 네이버의 QR 체크인 페이지보다는 무거웠습니다. 캐싱을 끈 상태에서 네트워크로 사용한 데이터가 1.2MB나 되니, 네이버보다 약 25배 무거운 셈이네요.

다만 캐싱 기능이 있기 때문에, 처음으로 QR 체크인 페이지에 접속하는 게 아니라면 이 정도로 데이터가 많이 소모되지는 않습니다. 캐싱이 활성화되어 있는 상태에서는 새로고침을 하면 60KB만 소모되니, 큰 차이가 없습니다.

1 그 덕분에 훨씬 더 많은 정보를 제공할 수 있긴 하다

많은 기능이 한꺼번에 포함되어 있어서 무겁긴 하지만, 훨씬 구조적으로 작업이 되어있다는 것을 확인할 수 있었습니다. 그 뿐만 아니라 카카오에서 QR 체크인 페이지를 만들 때 고려하고 있는 모든 예외처리 사항들도 확인을 할 수 있었습니다.

1 어? 여기는 QR 코드가 캔버스로 그려지네?

이제 본격적으로 QR 코드 쪽을 살펴봅시다. 확실하게 눈에 띄는 차이점이 하나 보이네요. 바로 <canvas> 태그의 등장입니다. 네이버처럼 단순하게 이미지를 띄워도 상관 없을텐데, 캔버스를 이용해서 QR 코드를 그려야 하는 이유가 있을까요?

1 3번 갱신하는 로직이 네트워크 탭에 그대로 찍혀진 모습

그 이유는 바로 네트워크 로그에서 확인할 수 있었습니다. 15초 사이의 간격을 두고, 세 번의 JSON 파일을 요청하는 AJAX 요청이 보이시나요? 참고로 JSON 파일의 형태는 이렇게 생겼더라구요.

{
  "qr_code": "가00나00",
  "qr_data": "002|eyJ0eXAiOiJ...gzKOw_Nc", // JWT 토큰
  "status": 0
}

화면에 그려지는 정보들이 포함된 것을 확인할 수 있네요. 맞습니다, 바로 카카오는 서버에서 QR 코드를 직접 생성해주는 게 아니라, AJAX 방식으로 JWT 데이터를 받은 후 해당 정보를 이용해 프론트엔드에서 직접 QR 코드를 생성하기 때문이었습니다.

1 만료 후에는 아예 다른 더미 이미지가 그려진다

QR 코드가 만료될 때의 처리도 네이버와는 다른 방식으로 처리하고 있었습니다. QR 코드 HTML 위에 레이어를 씌우는 것이 아니라, 기존 QR 코드 영역을 display: none 으로 만들어버리고, 아예 더미 이미지를 대신 보여주게 하는 방식이었습니다.

1 더미 이미지가 실제 QR 코드인가 싶어서 찍어보니… 엥? 알리페이가 왜 나와

갑자기 이 더미 이미지가 실제 QR 코드인지 궁금해져서 인식을 시켜보았습니다. 그런데 식별이 되네요? 뜬금없게도 저장된 내용은 알리페이의 QR 코드였습니다. 무슨 상품과 연결된 것인지 궁금해져서 앱을 다운로드하고 회원가입까지 해서 찾아봤지만… 얼럿창이 뜨면서 따로 링크 되지는 않네요. 아마 지금은 없는 상품인 거 같습니다.

차라리 저 QR 코드가 카카오 개발자 채용 페이지로 연결되는 거였다면… 진짜 감동의 쓰나미였을 것 같았다는 아쉬움이 드네요.

1 읽을 수 없다? 아니, 읽기가 어려운 거다. 읽을 순 있다!

스크립트 코드도 살펴보았는데, 난독화가 되어 있어서 네이버처럼 아주 상세하게 비즈니스 로직을 뜯어보기에는 어려웠습니다. 아무래도 번들러를 쓴 것이 분명하군요. 브라우저에서 코드를 보기 쉽게 다듬어주는(beautify) 기능을 쓰면 읽기는 쉬워지지만, 약 3만 6천 줄이라는 방대한 양의 코드가 만들어집니다.

이는 처음부터 끝까지 모든 비즈니스 로직들이 번들링 되어 있는 스크립트 파일이라 그렇습니다. 쉽게 말해서 카카오는 QR 체크인 페이지를 통째로 하나의 웹 어플리케이션처럼 관리하고 있는 듯하네요.

1 beautify를 하고 찾아봤지만.. 찾기가 어려웠다

대신 일부분의 로직은 찾을 수 있었습니다. 위의 사진은 아마 QR 코드를 생성하기 위해 서버로부터 식별 정보를 담은 JSON 파일을 받기 위해 AJAX 요청을 시도하는 부분인 것 같네요.

1 카카오에서는 쿠키가 아니라 변수를 쓰고, 숫자값으로 비교를 하고 있다. 확실히 편-안!

카카오도 QR 코드 갱신 로직이 네이버와 동일합니다. 2번까지 자동 새로고침되고, 그 이후부터는 더 이상 자동으로 갱신되지 않죠.

다듬어진 코드에서 해당 로직을 찾았습니다. 아무래도 페이지를 새로고침 하는 것이 아니라 AJAX로 받은 정보를 적용하기만 하면 되기 때문에, 횟수 정보를 쿠키에 저장할 필요가 없습니다. 그래서 갱신 횟수를 저장하는 변수를 만들고 관리하는 모습이 보이네요. 확실히 아까와 비교했을 때 편안한 코드입니다.

1 구글에서 검색이 되는 모습. 물론 카카오톡 밖이라서 동작하지는 않아요~

카카오에서는 별도로 검색 엔진 규칙 robots.txt를 명시하지 않았더군요. 그래서 다음이든 구글이든 검색 엔진을 통해 접근을 할 수 있습니다.

그리고 카카오에서는 네이버처럼 이미지 에셋을 하나로 뭉쳐서 관리하지 않고, 그냥 assets에 별도 파일로 관리를 하고 있는 점도 달랐습니다.

한편 iOS에서 디바이스를 쉐이크할 때 webkit.messageHandlers 를 이용해서 네이티브 앱과 브릿지 통신을 하는 부분도 찾았는데, 제 스마트폰은 안드로이드여서… 이 부분은 제가 제대로 확인해보지 못했습니다.

카카오 총평

카카오 쪽의 QR 체크인 페이지는 확실히 구조화가 잘 되어 있다 는 느낌을 받았습니다. 모든 유저 플로우에서 발생할 수 있는 페이지 컴포넌트와 예외 처리를 위한 UI 컴포넌트가 잘 정리되어 있었기 때문입니다.

다만 네이버에 비해 훨씬 많은 기능들이 포함되어 있는 페이지를 그려야 하니, 이게 무겁게 느껴지기도 했습니다. 물론 캐싱 기능이 있기 때문에, 최초 접속이 아니라면 사용자가 데이터 소모량을 체감할 정도는 아니긴 합니다.

그렇다면 QR 코드에는 무슨 내용이 들어있나

참고로 QR 코드를 디코드했을 때 나오는 결과물은 다른 분께서 작성해주신 별도의 포스트에 잘 정리가 되어있었습니다.

// 카카오톡으로 발급한 QR 코드 정보
{
  "sub": "fa4a62024b......4dc3a15c8b91e",
  "iss": "kakaotalk",
  "exp": 1602049229,
  "version": "002"
}
// 네이버로 발급한 QR 코드 정보
{
  "version": "001",
  "exp": 1600257331,
  "sub": "8fe25f89.......8958e88d8",
  "iss": "NAVER"
}

순서가 다르긴 하지만, 표현하고자 하는 정보가 동일한 것을 알 수 있습니다.

각 프로퍼티를 좀 더 자세히 살펴보자면, 개인을 식별할 수 있는 고유값(sub), QR 코드 발급자(iss), 앱 버전(version), 만료일(exp)의 정보가 공통적으로 포함되어 있네요.

공통점

두 페이지 사이에서 비슷한 점을 요약하자면 아래와 같습니다.

  • 네이버와 카카오 모두 최대 두 번까지 자동 새로고침이 되고, 그 이후부터는 수동으로 새로고침 버튼을 눌러야 한다
  • 네이버와 카카오 모두 시각 장애인을 위한 접근성을 고려하면서 HTML 태그를 작성하고 있다
  • 네이버와 카카오 모두 QR 코드에 동일한 스키마의 정보를 싣고 있다

차이점

한편, 두 페이지 사이의 차이점을 요약하자면 아래와 같습니다.

  • 네이버는 QR 인증 관련된 정보만 실은 HTML 문서만 제공하고, 카카오에서는 거의 웹앱 하나를 통째로 리턴한다
  • 네이버는 용량이 50KB 정도로 가볍고, 카카오는 1.2MB(캐싱된 상태에서는 60KB) 정도로 무겁다
  • 네이버는 서버 쪽에서 Base64로 인코딩된 이미지를 HTML에 실어서 넘겨주고, 카카오는 AJAX 방식으로 얻어온 JWT 토큰을 이용해 브라우저 단에서 QR 코드를 생성한다
  • 네이버는 QR 코드 갱신할 때 페이지를 새로 불러오기 때문에 약간의 화면 번쩍임이 있지만, 카카오는 AJAX 요청으로 QR 코드를 갱신하기 때문에 훨씬 부드럽게 이미지가 전환된다
  • 네이버는 페이지 새로고침을 시도하기 때문에 최대 2번까지 자동 새로고침되는 기능을 구현하기 위해 쿠키를 쓰지만, 카카오는 AJAX 방식을 쓰기 때문에 JavaScript 변수로 저장한다
  • 네이버는 QR 코드를 렌더할 때 <img> 태그를 쓰고, 카카오에서는 <canvas> 태그를 쓴다
  • 네이버는 15초의 유효기간이 만료되면 반투명한 <div>를 덮어버리고, 카카오는 더미 이미지로 전환시켜 버린다
  • 네이버는 코드 난독화가 되어있지 않고, 카카오는 코드 난독화가 되어 있다
  • 네이버는 웹에서도 접근이 가능하지만, 카카오는 유저 에이전트를 조작하지 않는 이상 웹에서 접근할 수 없다
  • 네이버robots.txt로 크롤링 제한이 걸려있지만, 카카오는 별도의 크롤링 제한이 없다