Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

해시뱅(#!)에 대해서...

해시뱅을 처음 본 것은 작년에 트위터가 새로운 웹사이트를 선보였을 때입니다.

트위터 접속 화면

제 트위터 계정인 http://twitter.com/outsideris로 접속하면 http://twitter.com/#!/outsideris로 리다이렉트 되는데 URL의 중간에 있는 #!를 해시뱅(HashBang)이라고 부릅니다.(저도 이번에 알게 되었네요.) 처음에 저 URL을 보았을때 상당히 찝찝했습니다. "이 보기 좋지 않은 URL은 무엇인가?"정도의 생각을 하면서 넘어갔던 같습니다.

저는 약간 웹 순수주의자에 가깝기 때문에 웹에 기본은 HTML이고 극단적으로 말하자면 요즘 같은 게임이나 지도같은 특이케이스 아니고 웹사이트라면 자바스크립트를 끈 채로도 동작할 수 있어야 한다고 생각합니다. 물론 그것을 지향점이라는 것이지 현실적인 어려움을 부정하는 것은 아닙니다. 여기서 현실적인 어려움이란 개발자나 관련자의 마인드나 기술 부족이나 일정관련된 문제등을 이야기 하는 것이고 위에 말한대로 다 하기 어려운 부분이 있지만 가능하다면 그렇게 되어야 된다고 생각합니다. 그런 부분에는 URL이 시맨틱해야 한다고 생각하는 것도 포함되어 있기 때문에 URL가운데 의미없는 #!가 들어가 있는 것은 눈에 영 거슬렸습니다. 그렇게 넘어갔다가 최근에 다른 글을 보다가 해시뱅에 대해서 많은 논의에 대한 글을 보게 되어서 관련 글들을 읽고보고 정리합니다.



해시뱅(#!)은 무엇인가?
해시뱅에 대해서 이야기 하기 전에 일반적인 웹사이트의 동작을 먼저 보겠습니다.

  1. 사용자가 브라우저에 http://twitter.com/outsideris를 입력합니다.
  2. 브라우저가 http://twitter.com/outsideris를 서버로 보냅니다.
  3. http://twitter.com가 가리키는 트위터서버로 이동이 됩니다.
  4. 서버는 /outsideris가 무엇인지 알기 때문에 적당한 HTML을 만들어서 클라이언트에게 리턴합니다.
이게 해시뱅을 사용하기 전의 트위터나 일반적인 웹사이트가 동작하는 구조입니다. 해시뱅을 이용한 현재의 트위터는 아래와 같이 동작합니다.

  1. 사용자가 브라우저에 http://twitter.com/#!/outsideris를 입력합니다.
  2. 브라우저가 #뒤에 부분인 !/outsideris를 로컬에 저장하고 http://twitter.com/를 서버에 보냅니다.
  3. http://twitter.com가 가리키는 트위터서버로 이동이 됩니다.
  4. 서버는 루트페이지의 HTML을 대량의 자바스크립트 링크와 함께 클라이언트에게 리턴합니다.
  5. 브라우저가 자바스크립트를 실행시키고 /outsideris를 파싱해서 oustideris에 대한 데이터를 서버에 요청합니다.
  6. 서버에서 받은 데이터를 화면에 적절하게 뿌려집니다.
위 동작 설명을 보면 알 수 있듯이 URL의 # 기능인 앵커(anchor)를 이용한 것입니다. 현재 페이지에서 #뒤에 있는 name으로 되어 있는 태그를 찾아서 그곳으로 이동되기 때문에 보통 가이드 문서에서 목차의 내용으로 바로 이동되는 데 사용되는 것이고 이것을 이용해서 #뒤에 !/outsideris를 붙혀서 마치 URL처럼 보이도록 한 것입니다. 일반적으로 이 방법을 사용할 때 #!라고 쓰기 때문에 해시뱅이라고 부릅니다.



왜 해시뱅이 필요한가?
해시뱅이 필요한 이유는 단일 페이지 웹애플리케이션을 만들기 위해서입니다. 해시뱅을 사용하는 사이트들은 전통적인 웹사이트가 아닌 웹애플리케이션이라고 보아야 합니다. 자바스크립트의 성능이 급격히 증가한 이후에 페이지의 전체를 로딩하는 대신에 페이지를 딱 한개만 두고 자바스크립트 만으로 모든 메뉴를 다루는 것에 대한 요구사항가 생겼고 그것을 구현하기 위해서는 페이지가 갱신되지 않는 것이 중요한데 현재 기술로는 페이지 갱신없이 URL을 변경할 수 있는 방법이 없습니다. 그렇기 때문에 페이지 갱신없이 URL이 변경되는 것처럼 보이도록 하기 위해서 해시뱅을 사용하는 것입니다. 해시뱅에서 #뒤에 이는 부분을 fragment identifier라고 부릅니다.

기존의 레가시를 위해서 웹애플리케이션도 북마크할 수 있고 링크를 공유하고 검색엔진에 인덱싱 될수 있으려면 현재는 해시뱅이 없이는 불가능합니다. 해시뱅을 사용한 것은 전통적인 웹사이트의 레가시를 깨뜨리지 않으려는 절충안이라고 볼 수 있습니다. 현재 기술로써는 웹애플리케이션과 웹사이트를 동시에 지원할 수는 없기 때문에 어느쪽이든 한쪽을 선택할 수밖에 없는 것이었습니다. 해시뱅을 사용하고 있는 곳에서도 pushState같은 기술이 완전히 보급되어 웹애플리케이션과 웹사이트를 동시 지원할 수 있게 되는 것이 가장 좋다는 데는 이견이 없습니다.(현재 사파리, 크롬, 파이어폭스가 지원하고 있습니다.)

이는 과도기적인 기술입니다. HTML5의 Histroy API인 pushState가 모든 브라우저에서 지원이 된다면 보기싫은 해시뱅은 사용하지 않아도 되지만 현재 pushState는 일부의 브라우저에서만 지원이 가능한 것이기 때문에 그때까지 이같은 요구사항을 이뤄내기 위해서 해쉬뱅이라는 것이 도입된 것입니다. 해시뱅을 사용하는 곳에서도 해시뱅이 별로 이쁘지 않고 완벽하지도 않다는 것은 동감하지만 현실적으로 유용하다는 것입니다.



해시뱅은 웹에 좋지 않습니다.
해시뱅의 문제점은 단순히 이쁘지 않은 URL의 문제가 아닙니다. 이는 웹페이지가 아닌 자바스크립트 웹애플리케이션이기 때문에 자바스크립트가 동작하지 않으면 사이트전체가 동작하지 않습니다. 단순히 자바스크립트를 사용할 수 없는 환경에 있는 사용자 뿐만 아니라 어떤 문제로든 자바스크립트 파일이 제대로 로드되지 않는다면 사용자는 사이트를 전혀 이용할 수가 없습니다.(인터넷이 좋지 않은 환경등에서는 얼마든지 발생할 수 있는 일입니다.)

자바스크립트 로드문제 뿐만 아니라 스크립트 오류 혹은 흔히 실수하는 JSON에서 마지막 엘리먼트뒤에 콤마(,)를 찍는 단순한 실수로도 전체 웹사이트를 망가뜨릴 수 있습니다. 더군다나 웹사이트에 광고를 게제하는 경우 광고가 포함하고 있는 자바스크립트는 일반적으로 품질이 좋지 않기 때문에 이 자바스크립트로 인하여 웹사이트가 망가질 수 있습니다. 이는 컨텐츠가 자바스크립트와 아주 강력하게 커플링 되었음을 의미하는데 프로그래밍에서 커플링은 항상 좋지 않은 것으로 인식되어 왔습니다.

국내에서는 유명하지 않지만 GwakerLifeHacker라는 사이트가 올 초에 해시뱅을 사용해서 리뉴얼하고 오픈하면서 이 잠재적인 문제로 사고가 크게 터졌었다고 합니다. 이전의 LifeHacker는 100만개의 페이지를 가진 사이트였지만 새로운 사이트는 100만개의 Fragment Identifier를 가진 하나의 페이지가 된 것입니다. (이유는 모르겠지만) 수시간동안 자바스크립트 파일이 로드되지 않았고 그 덕에 사용자들은 두 사이트의 어떤 컨텐츠에도 접속을 할 수 없었습니다. 이 장애가 해결되기 까지는 거의 하루가 걸렸던것 같습니다.  기존의 웹에 비해서 해시뱅이 깨지기 쉬운 웹이라는 것을 Gwaker와 LifeHacker가 몸소 보여준 격이 되었습니다.

또 다른 문제로는 크롤러의 문제가 있습니다. 크롤러는 검색엔진이 웹사이트의 내용을 알기 위해서 사이트의 컨텐츠를 긁어가는 봇인데 대부분의 크롤러는 HTTP 1.1과 URL 스펙(RFC-2396같은)을 따르고 있는데 이것을 따르는 크롤러는 자바스크립트를 실행시키지 않기 때문에 해시뱅으로 만들어진 사이트의 컨텐츠를 가져갈 수 없습니다. 크롤러가 보는 페이지는 내용이 없는 빈 페이지일 뿐입니다.

구글은 fragment idendifier URL을 변경하는 것으로 이 문제를 해결했습니다.  예를 들어 www.example.com/ajax.html#!key=value 와 같은 해시뱅 URL을 구글은 www.example.com/ajax.html?_escaped_fragment_=key=value와 같은 괴상한 URL로 변경이 합니다. 이렇게 변경된 URL은 실제 컨텐츠를 가리키는 URL이 되고(서비스 업체에서 그렇게 만들어야 겠죠.)구글을 이것을 색인합니다. 구글은 이것을 일반적인 앵커용도의 #과 웹애플리케이션에서 사용한 #를 구별하기 위해서 #뒤에 !를 붙혀서 웹애플리케이션 사이트들을 구별할 수 있도록 제시함으로써 해시뱅이 생기게 된 것입니다. 하지만 현재 구글봇만 색인이 가능하고 실제로 Gwaker와 LifeHacker가 문제가 생겼을때 구글 검색 및 여러 뉴스 사이트에서 Gwaker와 LiferHacker가 제외되었다고 합니다.

또한 이제 캐시를 제대로 할 수 없게 되었습니다. 중개서버가 이를 다룰 수 있는 방법이 없기 때문에 사이트는 모든 트래픽을 다루어야 합니다. 그리고 구글봇이나 웹브라우저 기반의 봇만 마이크로포맷의 데이터를 볼 수있기 때문에 마이크로 포맷을 비롯한 시맨틱 기술들이 잠재적으로 버려졌습니다. 페이스북의 Like 위젯같은 경우 페이지의 URL로 구분을 하기 때문에 글에 위젯을 달기 위해서는 추가적인 작업이 필요하게 되었습니다. 더군다나 이는 리퍼러도 깨뜨립니다. 리퍼러에는 fragment identifier가 넘어오지 않기 때문에 리퍼러를 보고 실제 방문자가 찾아온 원글을 찾아갈 수 없게 되었습니다.


결론
양쪽의 의견이 다 어느정도 합리적이라고는 보지만 저 같은 경우에는 해시뱅을 반대하는 입장입니다. 게임같은 경우가 아닌 트위터나 LifeHacker는(LifeHacker는 현재는 원래로 돌아간듯 합니다.) 페이지 설질상 애플리케이션보다는 컨텐츠의 성격이 강하기 때문에 애플리케이션을 선택한 것 자체가 그다지 좋지 않은 선택이었다고 생각하는 편입니다.

URL이 퍼머링크의 역할을 하는 것이나 검색의 중요성, 웹브라우저뿐만 아니라 모든 크롤러나 리더등도 내용을 문제 없이 읽을수 있어야 하는 것은 웹의 근본에 포함되어야 하는 것이기 때문에 설사 과도기적이라고 하더라도 이런 돌연변이 형태의 것이 생기는 것은 그다지 찬성할 수 없습니다.(각 서비스에 따라 다른 의견을 가질수는 있겠지만요.) 관련글들에서 본것 처럼 curl로 사이트의 정보를 얻을 수 없다면 그 사이트는 이미 깨진 것이라는 것에 동의하고 있습니다.

사실 구글도 해시뱅을 사용하고 있지는 않지만(아마 구글을 크롤링하는 곳은 없어서인지 몰라도) 그냥 #만을 사용해서 웹애플리케이션처럼 리뉴얼 되어있습니다. 그래서 현재 curl로 구글에 요청해서 검색결과를 받아올 수 없습니다. 얼마전에 검색결과를 가져와서 보여주는 예제 소스를 짜다가 이문제를 발견했었는데 그당시에는 원인도 자세히 몰라서 무척 고생한 기억이 납니다.

금융권이나 정부기관같은 한국형1 사이트들도 아닌 어느정도 웹의 사상등을 이해하고 있다고 생각하던 구글이나 트위터가 이런 선택을 했다는 것은 약간 실망스러운 부분입니다.


[참고 글]
Thoughts on the Hashbang
Breaking the Web with hash-bangs
Broken Links
Hash, Bang, Wallop.

  1. 여기서 한국형이란 웹의 사상을 전혀 이해하지 못한채 요구사항만으로 기술을 억지로 가져다 붙혀서 엉망으로 만들어버렸다는 의미로 사용했고 대부분의 한국형이란 말이 붙은 것들도 결과물을 보면 그다지 다르지 않다고 생각합니다. [Back]
2011/09/21 02:33 2011/09/21 02:33