Rev. 2.73

얼마 전 소개했던 PrismJS는 아직 HTML, CSS, JS만을 지원하고 있습니다. 지금 즈음 다른 언어를 지원하지 않을까 해서 살펴보니 역시나 있더군요. 제가 발견한 현재 확장 가능한 언어는 JAVA, PHP, Bash 그리고 모든 C 스타일 언어에 적용할 수 있는 Generic 등이 있습니다. 이 문법 강조 컴포넌트를 확장하려면 PrismJS를 포크한 각 개발자의 저장소에서 직접 가져와야 합니다. 몇몇 컴포넌트에서 사소한 문제를 발견했지만, 신속히 수정되고 있어 해결방법을 어렵지 않게 찾아낼 수 있었어요. 각 언어별 저장소 위치를 아래에 정리했습니다. PrismJS를 사용하는 분들은 참고하세요.

Bash - https://github.com/ktmud/prism/tree/gh-pages/components
JAVA - https://github.com/thomasklemm/prism/tree/gh-pages/components
Clojure - https://github.com/samflores/prism/tree/gh-pages/plugins
Vim - https://github.com/samflores/prism/tree/gh-pages/plugins
Ruby - https://github.com/samflores/prism/tree/gh-pages/plugins
PHP - https://github.com/Aaron3/prism/tree/php/components
Generic - https://github.com/Aaron3/prism/tree/generic/components
Okaidia(theme) - https://github.com/ocodia/prism/tree

언어를 추가하는 방법은 아주 간단합니다. 컴포넌트 또는 플러그인 폴더에 있는 해당언어의 소스를 prism.js에 추가해 주세요.

Comments

그동안 실험대상으로 사용해 오면서 이곳저곳이 얼룩져 더는 관리할 수 없을 정도로 버거워져 버린 정든 블로그 디자인을 버리고 새로운 모습으로 단장하였습니다. 작년 3월에 디자인 컨셉을 잡고 한참을 내버려뒀다가 최근에 마무리 지은 것입니다. 아직 모든 기능이 작동하지 않아 적용하는 것을 망설였지만, 이번에도 실패하면 또다시 방치할 것 같아서 내질러 버린 거에요. 일단 가장 치명적인 문제는 댓글 입력이 안 되는 것입니다. 하지만 트위터와 연동한 기능은 잘 작동하므로 당분간 트위터를 통해 피드백 주시면 감사하겠습니다. 자, 이제 기존과 다른 점들과 진행상황 들을 정리해 볼게요.

작업 환경

뒷단은 APM(Apache, PHP, MySQL)입니다. 이 블로깅 도구는 텍스트큐브의 초기 버전 격인 태터툴즈 클래식 버전을 가다듬은 것입니다. 왠지 개발자라면 직접 운영하는 서비스 하나 정도는 가지고 있어야 할 것 같은 이상한 기분이 들어서 손수 업데이트하고 있는 것이죠. 뭐, 사서 고생하는 것처럼 보일지도 모르겠지만 사실 얻는 게 더 많다고 우겨대고 싶습니다.

앞단은 산업표준처럼 되어버린 jQuery 따위는 가볍게 무시하고 Prototype.js를 씁니다. 이유를 굳이 만들자면, 기존 코드들이 모두 이 녀석으로 작성되었고 자바스크립트의 고유한 문법과 혼용할 수 있고, 왠지 jQuery만 쓰면 너무 편해서 바보가 되어 버릴 거라는 요상한 생각을 가지고 있습니다.

연도별 내비게이션

기존의 페이지 기반 내비게이션은 고유주소를 제공하지 못하는 등의 문제점을 가지고 있어 연도를 기반으로 정보를 분할해서 출력하도록 변경하였습니다. 연도가 적용되는 컨트롤러는 Archive, Tag, Search, Projects입니다. 상단에 한줄로 배치한 연도는 이 컨트롤러들과 And 조건으로 쿼리하기 때문에 마치 태그처럼 사용될 수 있습니다. 예를 들면, '브라우저' 태그와 2010년을 조합하거나 '자료' 카테고리와 2009년을 조합하여 정보 출력범위를 좁히는 것이죠. URL은 /tag/브라우저/2010과 같은 패턴이며, 연도가 생략되면 올해를 기본으로 합니다. 월 단위로 좁힐 수 있는 기능을 옵셔널하게 적용하려고 했는데, 요즘 활동이 영 불량한 관계로 데이터가 걸려들 확율이 너무 낮아져서 안 만들었습니다.

목록 화면 개선

목록화면에 더 많은 정보가 노출되도록 개선하였습니다. 게시판처럼 제목만 떡하니 노출되는 불친절함을 해결하고 싶었거든요. 그래서 본문에 작성된 내용을 가져와 코드, 이미지, HTML 등의 영역을 제거하여 서두 일부분을 노출한 것입니다. 그리고 이미지가 첨부된 포스트인 경우, 가장 처음에 사용된 이미지의 축소판(Thumbnail)을 실시간으로 생성하고 출력하는 기능이 추가되었습니다. 이는 목록 형식으로 출력되는 태그, 아카이브, 검색 등에 일괄 적용됩니다.

프로젝트 섹션 확장

이 웹 사이트에서 가장 주력으로 다루고 내세울 만한 컨텐츠들을 모아서 제공하는 것에 목적을 두고 프로젝트 섹션을 개선하였습니다. 프로젝트 섹션에서는 참여하거나 개인적으로 진행한 프로젝트의 결과물이나 기술 등을 다룹니다. 그리고 가장 빈번하게 다루는 기술들인 자바스크립트, HTML, CSS로 구분하여 추가로 컨텐츠에 접근할 수 있도록 했습니다.

태그, 사진 목록 그리고 방명록 제거

우후죽순으로 늘어나는 태그 이름들만 주르륵 나열된 목록을 제공하거나 랜덤하게 뿌리는 짓이 어느 순간 바보스럽게 느껴졌습니다. 방분자는 자신이 찾고자 하는 정보 이외의 것에는 눈곱만큼도 관심이 없기 때문이죠. 같은 맥락에서, 지극히 개인적인 취미 생활인 사진들만 모아 놓는 페이지를 정보에 목마른 방문객이 잠시 들러서 감상해 줄 것이라고는 도저히 상상이 되지 않아요. 무슨 카페도 아니고 말이죠. 방명록은 그야말로 개인 홈페이지가 한참 성행하던 십수 년 전에나 어울릴만한 의사소통 장치라고 생각됩니다. 내친김에 댓글 검색, 잠다 한 외부링크들도 함께 싸그리 제거했습니다.

진행 상황

앞서 말했듯이, 댓글 입력 기능이 작동하지 않습니다. 그리고 본문에는 포스트 타입에 따른 기본기능과 연관된 스크립트들이 있는데 아직 작업 전이라 갤러리, 이미지 확대 등의 기능이 정상적으로 작동하지 않습니다. 기본 기능에 해당하는 모든 기능이 완료되고 나면, 크로스-브라우저 작업을 거쳐 기존 디자인에서 쓸만하다고 생각되는 컴포넌트들은 부담 없는 선에서 모두 가져올 생각입니다. 작업이 진행되는 동안 다소 불편하시겠지만 양해 부탁합니다.

  • TODO - 서버사이트 스크립트 사용 여부 판단 및 Lazier Image Load 적용
  • 2012-10-29 - 쿼리 결과가 10개 미만이면 한 페이지에 모두 출력
  • 2012-09-30 - 아카이브, 쿼리 결과 없음 메시지 추가
  • 2012-09-25 - Minimap, Aspect Ratio Crop 모드 지원 및 기존 Scale모드 자동 전환, IE9 지원
  • 2012-09-22 - Sound Effect, Anywhere, Tooltip 컴포넌트 활성
  • 2012-09-22 - History/State API 적용 및 문서와 스크립트 구조 변경
  • 2012-09-21 - 아카이브, 동영상이 첨부된 경우 프리뷰 이미지 추가
  • 2012-09-21 - 아카이브, 쿼리 결과에 따라 최근 연도 자동 선택 및 연도 링크 활성, 인코딩 문제 수정
  • 2012-09-19 - 트위터 연동, Shorten URL 역추적 후 비교하여 DB에 쑤셔 박음
  • 2012-09-18 - 아카이브, 프리뷰 이미지가 없고 예제 코드가 있는 경우 아이콘 출력
  • 2012-09-17 - 댓글 기능 및 트위터 연동 위젯 활성(일부)
  • 2012-09-16 - 사용되지 않는 파셜 함수 및 변수 제거(일부)
  • 2012-09-14 - 사이트 미니맵 실험적/제한적 활성
  • 2012-09-11 - 자바스크립트, 스타일시트 동적 병합 및 순수 CSS에 의해서만 레이아웃 갱신
  • 2012-09-11 - 아카이브, 이미지 출력 개선 및 리엑션 카운트 수정
  • 2012-09-10 - 이전 컴포넌트 및 위젯 일부 적용
  • 2012-09-10 - CSS3 패턴 배경을 적용했으나 화면갱신 성능이 현저히 저하되어 이미지로 대체
  • 2012-09-09 - HTML5 microdata를 이용한 About the author 섹션 추가(다국어 지원 예정)
  • 2012-09-08 - Archive, Tag, Search, Entries 링크 레이블 그리고 탭 디스플레이 오류 수정
  • 2012-09-07 - 미디어 쿼리를 이용한 멀티플 사이즈 스크린 지원
  • 2012-09-07 - 목록에 출력될 미리보기 이미지 실시간 생성
  • 2012-09-06 - 검색 기능 활성화
  • 2012-09-06 - Reactions Tab 스타일링 및 몇몇 CSS 이슈 수정

Comments

HTML5의 History/State API를 이용하여 브라우저의 히스토리를 조작하는 방법에 대해 알아보겠습니다. 그동안 location.hash로 꾸리 하게 밖에 구현할 수 없었던 브라우저 히스토리를 대체할 수 있는 명세로서, Ajax로 호출한 컨텐츠가 드로잉 된 페이지를 조회한 후 브라우저에서 "뒤로 가기" 버튼을 눌러 다시금 이전 위치로 이동하고 해당 컨텐트를 조회할 수 있게 하는 목적으로 사용할 수 있습니다. 알다시피, 이와 같은 뒤치다꺼리를 하지 않으면 방문객은 우리의 웹사이트를 무심결에 떠나 버리는 불상사가 발생할 수 있지요. 얼마 전에 소개한 Customized JS Bin에 이 명세를 적극적(대부분의 요청에 사용)으로 반영하면서 알아낸 몇 가지 사실들에 대하여 정리해보겠습니다.

비동기(동적으)로 컨텐츠를 갱신할 때 매번 마음 한구석에 남는 응어리는 바로 시멘틱 웹입니다. Ajax로 불러들여 진 컨텐츠가 정적이어(검색되어)야 할 필요성 가장 먼저 판단해야 합니다. 왜냐하면, 이에 따른 추가작업이 필요하거나 구조가 달라지기 때문이죠. 만약 시멘티컬한 환경을 고려한다면 서버-사이드에서 하나의 URL을 각기다른 형식의 데이터로 응답할 필요가 있습니다. 비동기 요청에는 레이아웃을 제외한 갱신영역이나 내용만을 담은 작은 HTML 조각 혹은 JSON과 같은 응답형태를 가지고, 일반적인 요청에는 레이아웃을 포함한 완벽하게 파싱된 정적인 HTML 컨텐트를 응답하는 두 가지를 모두 준비하는 것이 좋습니다.

A 요소에 href 속성에는 페이지가 갱신될 주소를 반드시 기입해서 웹 크롤러들이 컨텐츠를 샅샅이 긁어갈 수 있게 하고 클릭을 자바스크립트로 제어하여 Ajax로 요청 및 갱신하는 것이 일반적인 패턴입니다. 하나의 주소에서 응답형태가 다른 컨텐트를 제공하는 것이 마음에 안들지도 모릅니다. 물론 개별적으로 응답형태에 따른 주소를 제공하거나 파라미터를 이용할 수도 있겠지만, 개인적으로 선호하지 않는 방법이기 때문에 개별 주소를 사용하는 방법에 대해서는 생략하겠습니다. 대부분의 서버-사이드 언어는 Ajax 요청인지를 구분할 수 있으며, PHP는 다음과 같은 방법으로 알아냅니다.

<?php
  $ajax = isset($_SERVER['HTTP_X_REQUESTED_WITH'])
      || (isset($_SERVER['HTTP_ORIGIN']) && $_SERVER['REQUEST_METHOD'] == 'GET');

  if ($ajax) {
    header("Content-Type: application/json; charset=utf-8");
?>
{
  "title": "Fire JS Bin",
  "url": "http://jsbin.firejune.com",
  "text": "Collaborative playground for web developers."
}
<?php
  } else {
    header('Content-type: text/html; charset=utf-8');
?>
<html>
  <head>
    <title>Fire JS Bin</title>
  </head>
  <body>
    <div id="content">Collaborative playground for web developers.</div>
  </body>
</html>
<?php
}
?>

JS Bin의 컨텐츠는 코드이기 때문에 검색으로 접근될 확률이 낮습니다만, 기본 설계가 정적인 컨텐츠를 제공하는 형태로 만들어져 있기 때문에 시멘틱한 모습으로 방향을 잡았습니다. History API를 지원하는 브라우저에서는 동일한 주소에서 JSON형식의 데이터만 수신하고 그렇지 않으면 주소창의 주소가 변경되면서 이동합니다. 이제 브라우저 히스토리를 조작해 봅시다.

  function drawHTML(data, state) {
    if (state) history.pushState(data, data.title, data.url);
    $('#content').html(data.text);
  }

  function requestError(req, state) {
    alert('WTF? ' + state);
    //location.href = this.url;
  }

  $('a').click(function() {
    if (history.pushState) {
      $.getJSON(this.href).success(drawHTML).error(requestError);
      return false;
    }
    return true;
  });

범국민 라이브러리인 jQuery를 이용한 간단한 예입니다. A 요소의 클릭을 감시하는 함수는 지원 여부를 구분하여 href에 명시된 주소로 JSON 요청을 날린후 이벤트 진행을 중단하며 그렇지 않은 경우 A 요소가 정상적으로 클릭 되어 일반적인 주소 이동이 발생하도록 내버려 둡니다. drawHTML 함수에는 패이지가 갱신될 코드와 history.pushState 메서드를 호출하는 코드로 구성되어 있습니다. 이 메서드는 3개의 인자를 받는데, 해당 페이지의 상태를 보존하기 위한 데이터와 문서 제목 그리고 해당 페이지의 URL입니다. drawHTML 함수의 두 번째 state 인자가 유효한 때에만 실행되며 호출되는 즉시 브라우저의 주소창이 바뀌고 히스토리 스택이 쌓입니다. 여기까지는 쌓기만 한 것입니다. 이제 히스토리가 변경되면 발생하는 이벤트인 window.onpopstate를 작성해 봅시다.

  $(window).bind("popstate", function(event) {
    var data = event.originalEvent.state;
    if (data) drawHTML(data, false);
  });

이때 보존해 두었던 데이터를 그대로 캐시처럼 활용함으로써 두 번 다시 서버를 힘들게 하지 않아도 되는 장점이 있습니다. 그리고 drawHTML의 두번째 인자를 false로 넘겨 주는 것은 히스토리를 더이상 쌓지 않게 하기 위함입니다. 이미 쌓여있는 녀석을 또 쌓을 필요는 없으니까요. 이외에도 history.replaceState라는 메서드가 있는데, 해당 주소의 컨텐트가 갱신된 경우에 사용하는 것입니다. 이게 전부에요. 간단하죠? 해시가 아닌 URL이 변경되는 레알 히스토리이면서 Ajax로 신규 컨텐트만 갱신하고 캐시효과도 누릴수 있기 때문에 매우 빠릅니다. 우리가 잘 알고있는 GitHub에서도 슬라이드 효과를 동반한 소스코드 살펴보기 그리고 개시물을 조회하는 곳곳에 이 API를 사용하고 있습니다.

음... History/State API를 지원하지 않는 브라우저에서는 종전과 같이 location.hash트릭으로 비스무리한 기능을 부가적으로 제공할 수 있습니다. HTML5 History/State API를 모든 브라우저에서 사용 가능하게 하는 History.js도 좋은 대안이 될 수 있을 것아서 적용했다가, JS Bin은 이미 이 해시를 이용하여 에디터의 뷰 활성 상태를 관리하기 때문에 충돌이 발생했고 또한 다소 무거운 느낌이 들어서 바로 제거했습니다. jsbin.firejune.com으로 이동해서 컨텐츠와 주소창이 변경되는 모습을 직접 확인해보세요. 참 그리고 커스터마이즈한 JS Bin도 많이 진척되었습니다. 관심 있으신 분들은 릴리즈 노트도 함께 봐주세요. 소개글 이후 자세한 변동내역이 모두 기록되어 있습니다.

Comments