<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><atom:link rel="hub" href="http://tumblr.superfeedr.com/" xmlns:atom="http://www.w3.org/2005/Atom"/><description>About Development &amp; Productivity</description><title>Daily Upgrade</title><generator>Tumblr (3.0; @fallroot)</generator><link>http://dailyupgrade.me/</link><item><title>Ruby on Rails - full_messages 한국어 처리 | Ruby on Rails - full_messages for Korean</title><description>&lt;p&gt;이 글은 액티브 레코드를 검증할 때 제공하는 문구를 한국어로 바꾸는 방법에 대한 내용입니다.&lt;/p&gt;

&lt;p&gt;먼저 진행을 위해 예제 프로젝트를 만듭니다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ rails new example
$ cd example
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;간단하게 로그인 아이디, 이메일 필드를 갖는 사용자 모델을 만듭니다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ rails g scaffold user login:string email:string
$ rake db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;&lt;code&gt;app/models/user.rb&lt;/code&gt;&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;class User &amp;lt; ActiveRecord::Base
  validates :login, :presence   =&amp;gt; true,
                    :uniqueness =&amp;gt; true,
                    :length     =&amp;gt; {:in =&amp;gt; 3..20}

  validates :email, :presence   =&amp;gt; true,
                    :uniqueness =&amp;gt; true
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;여기까지 코드를 적고 서버를 올린 후 필드를 채우지 않고 새 사용자를 만들어 봅니다.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_ln6rx6Xwt11qzzaw8.jpg" alt=""/&gt;&lt;/p&gt;

&lt;p&gt;화면에 나오는 오류 문구는 &lt;code&gt;ActiveModel::Errors#full_messages&lt;/code&gt;를 이용해서 만들어집니다.&lt;/p&gt;

&lt;h3&gt;&lt;code&gt;app/views/users/_form.html.erb&lt;/code&gt;&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;% @user.errors.full_messages.each do |msg| %&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;%= msg %&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이제 한국어 사용을 위해 아래와 같이 설정하고 서버를 다시 올립니다.&lt;/p&gt;

&lt;h3&gt;&lt;code&gt;config/application.rb&lt;/code&gt;&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;config.i18n.default_locale = :ko
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;한국어 문구를 위해 새로 파일을 작성합니다.&lt;/p&gt;

&lt;h3&gt;&lt;code&gt;config/locales/ko.yml&lt;/code&gt;&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;ko:
  attributes:
    login: 아이디
    email: 이메일
  errors:
    messages:
      blank: 에 내용을 입력해 주세요
&lt;/code&gt;&lt;/pre&gt;

&lt;div class="tips"&gt;

&lt;h3&gt;레일즈에서 기본적으로 제공하는 문구&lt;/h3&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href="https://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml"&gt;actionpack/lib/action_view/locale/en.yml&lt;/a&gt;

&lt;ul&gt;&lt;li&gt;숫자, 날짜, 시간, 폼&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rails/rails/blob/master/activemodel/lib/active_model/locale/en.yml"&gt;activemodel/lib/active_model/locale/en.yml&lt;/a&gt;

&lt;ul&gt;&lt;li&gt;액티브 모델 오류&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rails/rails/blob/master/activerecord/lib/active_record/locale/en.yml"&gt;activerecord/lib/active_record/locale/en.yml&lt;/a&gt;

&lt;ul&gt;&lt;li&gt;액티브 레코드 오류&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml"&gt;activesupport/lib/active_support/locale/en.yml&lt;/a&gt;

&lt;ul&gt;&lt;li&gt;날짜/시간 표시&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;위 영문에 대응하는 한국어 문구를 작성하면 됩니다.&lt;/p&gt;

&lt;/div&gt;

&lt;h2&gt;문제&lt;/h2&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_ln6s4wcIS81qzzaw8.jpg" alt=""/&gt;&lt;/p&gt;

&lt;p&gt;한국어 문구를 보여줬을 때 문제점이 몇 가지 있습니다.&lt;/p&gt;

&lt;h3&gt;필드 이름과 오류 문구 사이의 공백&lt;/h3&gt;

&lt;p&gt;이 공백은 &lt;a href="https://github.com/rails/rails/blob/master/activemodel/lib/active_model/locale/en.yml"&gt;activemodel/lib/active_model/locale/en.yml&lt;/a&gt; 파일에 &lt;code&gt;errors.format&lt;/code&gt; 값이 &amp;#8220;&lt;code&gt;%{attribute} %{message}&lt;/code&gt;&amp;#8220;로 설정되어 있기 때문에 생깁니다.&lt;/p&gt;

&lt;h3&gt;받침에 따른 조사&lt;/h3&gt;

&lt;p&gt;한국어는 체언의 받침 유무에 따라서 조사가 달라지는 경우가 많습니다. &amp;#8220;이/가, 은/는, 을/를, 과/와&amp;#8221; 등이 예가 되겠습니다.&lt;/p&gt;

&lt;h3&gt;필드 이름의 위치&lt;/h3&gt;

&lt;p&gt;위에서 얘기한 것처럼 &lt;code&gt;errors.format&lt;/code&gt; 값이 &amp;#8220;&lt;code&gt;%{attribute} %{message}&lt;/code&gt;&amp;#8220;이기 때문에 주어나 목적어가 반드시 처음에 나와야 합니다. 그래서 &amp;#8220;다른 사람이 쓰고 있는 아이디입니다.&amp;#8221; 형태의 문장을 만들 수 없습니다.&lt;/p&gt;

&lt;h2&gt;구현&lt;/h2&gt;

&lt;p&gt;위 문제를 모두 고치기 위해 아래와 같은 방법을 생각했습니다.&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;&lt;code&gt;errors.format&lt;/code&gt; 값을 &lt;code&gt;%{message}&lt;/code&gt;로 바꿉니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;activerecord.errors.messages&lt;/code&gt; 안의 각 문구 안에 &lt;code&gt;%{attribute}&lt;/code&gt;를 적절한 위치에 넣습니다.&lt;/li&gt;
&lt;li&gt;문구 안에 조사를 같이 넣어줍니다.&lt;/li&gt;
&lt;li&gt;체언에 따라 조사를 바꿉니다.&lt;/li&gt;
&lt;/ol&gt;&lt;h3&gt;&lt;code&gt;config/locales/ko.yml&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;문구에 조사를 구분하기 위해서 괄호 두 개로 감싸줍니다. 받침이 있는 형태이든 없는 형태이든 상관없습니다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ko:
  attributes:
    login: 아이디
    email: 이메일
  errors:
    format: %{message}
    messages:
      blank: 헉! %{attribute}((을)) 입력해 주세요
&lt;/code&gt;&lt;/pre&gt;

&lt;div class="tips"&gt;

&lt;h3&gt;레일즈 오류 문구 검색 순서&lt;/h3&gt;

&lt;p&gt;특정 모델 또는 특정 필드에 다른 문구를 보여 주려면 적절하게 설정하면 됩니다.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;code&gt;activerecord.errors.models.[model_name].attributes.[attribute_name]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;activerecord.errors.models.[model_name]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;activerecord.errors.messages&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;errors.attributes.[attribute_name]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;errors.messages&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;

&lt;p&gt;한국어 처리를 위해 새 파일을 작성합니다.&lt;/p&gt;

&lt;h3&gt;&lt;code&gt;config/initializers/korean_full_messages.rb&lt;/code&gt;&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;# encoding: utf-8

class Korean
  # 받침에 따른 알맞은 조사를 구한다.
  PROPOSITIONS = {'이' =&amp;gt; '가', '은' =&amp;gt; '는', '을' =&amp;gt; '를', '과' =&amp;gt; '와'}

  def self.get_proposition word, proposition
    # 한국어만 처리한다.
    return '' if word.size == word.bytesize

    # 받침이 없을 때
    if word.mb_chars.last.decompose[2].nil?
      PROPOSITIONS[proposition] || proposition
    # 받침이 있을 때
    else
      PROPOSITIONS.invert[proposition] || proposition
    end
  end
end

module ActiveModel
  class Errors
    def korean_full_messages
      # 한국어 설정이 아니면 full_messages를 돌려준다.
      return full_messages unless I18n.locale == :ko

      full_messages.map do |message|
        # 오류 문구에서 ((조사)) 형태를 찾아 받침에 맞는 조사로 바꿔 준다.
        message.gsub(/(?&amp;lt;=(.{1}))\(\(([이가은는을를과와])\)\)/) do
          Korean.get_proposition $1, $2
        end
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;&lt;code&gt;app/views/users/_form.html.erb&lt;/code&gt;&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;  &amp;lt;% @user.errors.korean_full_messages.each do |msg| %&amp;gt;
    &amp;lt;li&amp;gt;&amp;lt;%= msg %&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_ln7yeim8b91qzzaw8.jpg" alt=""/&gt;&lt;/p&gt;

&lt;h2&gt;참고&lt;/h2&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href="https://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml"&gt;actionpack/lib/action_view/locale/en.yml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rails/rails/blob/master/activemodel/lib/active_model/locale/en.yml"&gt;activemodel/lib/active_model/locale/en.yml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rails/rails/blob/master/activerecord/lib/active_record/locale/en.yml"&gt;activerecord/lib/active_record/locale/en.yml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml"&gt;activesupport/lib/active_support/locale/en.yml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/ko.yml"&gt;rails/locale/ko.yml&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><link>http://dailyupgrade.me/post/6806676778</link><guid>http://dailyupgrade.me/post/6806676778</guid><pubDate>Thu, 23 Jun 2011 08:27:00 +0900</pubDate><category>ruby on rails</category></item><item><title>크롬 확장 프로그램 개발 - 페이지 액션 | Chrome Extension Development - Page Actions</title><description>&lt;h2&gt;개요&lt;/h2&gt;

&lt;p&gt;브라우저 액션과 페이지 액션 비교는 &lt;a href="http://dailyupgrade.me/post/5320852086/chrome-extension-development-browser-actions"&gt;크롬 확장 프로그램 개발 - 브라우저 액션&lt;/a&gt; 글에서 적었는데요. 한 번 더 말씀드리면 아이콘을 감추고 싶으면 페이지 액션을 그 외에는 브라우저 액션을 쓰는 것을 추천합니다.&lt;/p&gt;

&lt;p&gt;페이지 액션으로 구현하는 확장 프로그램은 대개 아래와 같은 절차로 구현합니다.&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;컨텐트 스크립트를 통해 페이지를 살펴봅니다.&lt;/li&gt;
&lt;li&gt;실행 조건을 만족하면 주소창에 페이지 액션 아이콘을 보여 줍니다.&lt;/li&gt;
&lt;li&gt;아이콘을 클릭했을 때 프로그램을 실행합니다.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;페이지 액션 아이콘을 보여주는 함수는 &lt;code&gt;chrome.pageAction.show(integer tabId)&lt;/code&gt;입니다. &lt;a href="http://dailyupgrade.me/post/5759139172/chrome-extension-development-content-scripts"&gt;크롬 확장 프로그램 개발 - 컨텐트 스크립트&lt;/a&gt; 글에서 얘기한 것처럼 컨텐트 스크립트는 일부 &lt;code&gt;chrome.extension.*&lt;/code&gt;를 제외한 다른 API를 사용할 권한이 없습니다. 그래서 백그라운드 페이지에 관련 함수를 넣고 컨텐트 스크립트에서 호출하도록 합니다.&lt;/p&gt;

&lt;h2&gt;메시지 전달 (Message Passing)&lt;/h2&gt;

&lt;p&gt;컨텐트 스크립트에서 백그라운드 페이지와 통신하는 방법을 메시지 전달이라고 합니다. 이 예제에서는 &lt;code&gt;chrome.extension.sendRequest&lt;/code&gt;, &lt;code&gt;chrome.tabs.sendRequest&lt;/code&gt; 함수로 메시지를 보내고 &lt;code&gt;chrome.extension.onRequest.addListener&lt;/code&gt; 함수를 통해서 메시지를 받도록 합니다.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;컨텐트 스크립트 → 백그라운드 페이지

&lt;ul&gt;&lt;li&gt;&lt;code&gt;chrome.extension.sendRequest&lt;/code&gt; → &lt;code&gt;chrome.extension.onRequest.addListener&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;백그라운드 페이지 → 컨텐트 스크립트

&lt;ul&gt;&lt;li&gt;&lt;code&gt;chrome.tabs.sendRequest&lt;/code&gt; → &lt;code&gt;chrome.extension.onRequest.addListener&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;백그라운드 페이지는 확장 프로그램의 기본 페이지입니다. 컨텐트 스크립트는 웹 페이지에 포함되고 이 웹 페이지는 탭에 포함됩니다. 그래서 백그라운드 페이지를 호출하려면 &lt;code&gt;extension&lt;/code&gt;을 컨텐트 스크립트를 호출하려면 &lt;code&gt;tabs&lt;/code&gt;를 사용한다고 이해하면 될 것 같습니다.&lt;/p&gt;

&lt;h2&gt;플래시 극장&lt;/h2&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_lmxe5gVYDy1qzzaw8.jpg" alt=""/&gt;&lt;/p&gt;

&lt;p&gt;먼저 웹 페이지에 플래시가 있으면 배경을 흐리게 만들어서 집중할 수 있게 하는 확장 프로그램을 만들어 보겠습니다. 이미 &lt;a href="https://chrome.google.com/webstore/detail/bfbmjmiodbnnpllbbbfblcplfjjepjdn"&gt;Turn Off the Lights&lt;/a&gt;라는 좋은 확장 프로그램이 있으니 참고하시고 예제에서는 아주 단순하게 구현하겠습니다.&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;컨텐트 스크립트에서 웹 페이지에 플래시가 있는지 검사합니다.&lt;/li&gt;
&lt;li&gt;플래시가 있으면 페이지 액션 아이콘을 보여줍니다.&lt;/li&gt;
&lt;li&gt;아이콘을 클릭하면 컨텐트 스크립트를 통해 오버레이를 만듭니다.&lt;/li&gt;
&lt;li&gt;아이콘을 다시 클릭하면 오버레이를 감춰서 원래 화면을 보여줍니다.&lt;/li&gt;
&lt;/ol&gt;&lt;div class="tips"&gt;

&lt;h3&gt;&lt;code&gt;wmode&lt;/code&gt; 속성&lt;/h3&gt;

&lt;p&gt;플래시는 &lt;code&gt;&amp;lt;object&amp;gt;&lt;/code&gt; 또는 &lt;code&gt;&amp;lt;embed&amp;gt;&lt;/code&gt; 태그를 이용해서 문서에 들어갑니다. &lt;code&gt;wmode&lt;/code&gt; 속성이 &lt;code&gt;transparent&lt;/code&gt;가 아니면 플래시는 CSS의 &lt;code&gt;z-index&lt;/code&gt; 영향을 받지 않습니다. 그래서 화면을 덮는 오버레이를 만들 때는 플래시가 들어 있는 &lt;code&gt;&amp;lt;embed&amp;gt;&lt;/code&gt; 태그의 스타일을 &lt;code&gt;display: none&lt;/code&gt; 또는 &lt;code&gt;visibility: hidden&lt;/code&gt;으로 바꾸어서 처리합니다.&lt;/p&gt;

&lt;p&gt;또한 &lt;code&gt;wmode&lt;/code&gt; 속성은 자바스크립트를 사용해서 동적으로 설정할 수 없습니다. 이를 구현하려면 &lt;code&gt;innerHTML&lt;/code&gt; 등을 이용해서 새롭게 태그를 만들어야 합니다.&lt;/p&gt;

&lt;/div&gt;

&lt;h3&gt;manifest.json&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;{
  "name": "Flash Theater",
  "version": "0.1.0",
  "background_page": "background.html",
  "content_scripts": [{
    "js": ["contentscript.js"],
    "matches": ["http://*/*", "https://*/*"]
  }],
  "icons": {
    "16": "icon-16.png",
    "48": "icon-48.png"
  },
  "page_action": {
    "default_icon" : "icon-16.png",
    "default_title": "Flash Theater"
  },
  "permissions": ["tabs"]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;페이지 액션 선언이 추가되었습니다.&lt;/p&gt;

&lt;h3&gt;background.html&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;title&amp;gt;&amp;lt;/title&amp;gt;
&amp;lt;script&amp;gt;
// 컨텐트 스크립트에서 요청이 오면 처리합니다.
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
  // 페이지 액션 아이콘을 클릭했을 때 실행할 이벤트 핸들러를 등록합니다.
  chrome.pageAction.onClicked.addListener(function(tab) {
    chrome.tabs.getSelected(null, function(tab) {
      // 컨텐트 스크립트에 배경 처리를 요청합니다.
      chrome.tabs.sendRequest(tab.id, {});
    });
  });
  // 페이지 액션 아이콘을 보여줍니다.
  chrome.pageAction.show(sender.tab.id);
});
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;contentscript.js&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;// 웹 페이지에 플래시가 있을 때만 실행합니다.
if (document.querySelector('[type="application/x-shockwave-flash"]')) {
  // 백그라운드 페이지를 호출합니다.
  chrome.extension.sendRequest({});

  // 페이지 액션 아이콘을 클릭했을 때 요청을 받습니다.
  chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
    var overlay = document.getElementById('theater-overlay');

    if (overlay) {
      // 오버레이를 토글합니다.
      overlay.style.display = overlay.style.display == 'none' ? 'block' : 'none';
    } else {
      overlay = document.createElement('div');
      overlay.id = 'theater-overlay';

      var styles = {
        backgroundColor: 'rgba(0, 0, 0, 0.9)',
        height         : '100%',
        left           : 0,
        position       : 'fixed',
        width          : '100%',
        top            : 0,
        zIndex         : 10000
      }

      for (var style in styles) {
        overlay.style[style] = styles[style];
      }

      document.body.appendChild(overlay);
    }
  });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;결과&lt;/h3&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_lmxf4ew3GY1qzzaw8.jpg" alt=""/&gt;&lt;/p&gt;

&lt;h2&gt;사진 갤러리&lt;/h2&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_lmxe567JlS1qzzaw8.jpg" alt=""/&gt;&lt;/p&gt;

&lt;p&gt;이번에는 웹 페이지에 사진을 팝업 페이지로 보여주는 확장 프로그램을 만들어 보겠습니다.&lt;/p&gt;

&lt;h3&gt;manifest.json&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;{
  "name": "Photo Gallery",
  "version": "0.1.0",
  "background_page": "background.html",
  "content_scripts": [{
    "js": ["contentscript.js"],
    "matches": ["http://*/*", "https://*/*"]
  }],
  "icons": {
    "16": "icon-16.png",
    "48": "icon-48.png"
  },
  "page_action": {
    "default_icon" : "icon-16.png",
    "default_popup": "popup.html",
    "default_title": "Photo Gallery"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;페이지 액션 선언에 팝업 페이지가 들어가 있습니다.&lt;/p&gt;

&lt;h3&gt;contentscript.js&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;var photos = [];

// document.images는 HTMLCollection입니다.
// Array.prototype.slice를 이용해 Array로 바꾼 후 forEach 문을 사용합니다.
Array.prototype.slice.call(document.images).forEach(function(image) {
  // Image.naturalWidth로 사진의 원래 너비를 구합니다.
  if (image.naturalWidth &amp;gt;= 150) {
    photos.push(image.src);
  }
});

// 너비가 150픽셀이 넘는 사진이 있으면 실행합니다.
if (photos.length &amp;gt; 0) {
  chrome.extension.sendRequest(photos);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;gallery.css&lt;/h3&gt;

&lt;p&gt;불투명한 오버레이와 그 위의 사진들을 위한 CSS입니다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#photo-gallery {
  background-color: rgba(0, 0, 0, 0.9);
  height: 100%;
  left: 0;
  padding: 20px;
  position: fixed;
  top: 0;
  width: 100%;
  z-index: 10000;
}
#photo-gallery img {
  border: 5px solid #fff;
  border-radius: 3px;
  height: 150px;
  margin: 10px;
  width: 150px;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;background.html&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;title&amp;gt;&amp;lt;/title&amp;gt;
&amp;lt;script&amp;gt;
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
  // 페이지 액션 아이콘을 클릭하면 컨텐트 스크립트를 통해 갤러리를 보여줍니다.
  chrome.pageAction.onClicked.addListener(function(tab) {
    chrome.tabs.getSelected(null, function(tab) {
      chrome.tabs.sendRequest(tab.id, {});
    });
  });

  // 페이지 액션 아이콘을 보여줍니다.
  chrome.pageAction.show(sender.tab.id);
});
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;popup.html&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;title&amp;gt;&amp;lt;/title&amp;gt;
  &amp;lt;style&amp;gt;
    body, img, p {
      margin: 0;
      padding: 0;
    }
    body {
      min-width: 240px;
    }
    img {
      height: 50px;
      margin: 5px;
      width: 50px;
    }
    p {
      text-align: right;
    }
  &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;div id="photos"&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;p&amp;gt;
    &amp;lt;button id="close"&amp;gt;Close&amp;lt;/button&amp;gt;
  &amp;lt;/p&amp;gt;
&amp;lt;script&amp;gt;
  var photos = document.getElementById('photos');

  // 백그라운드 페이지에서 사진 주소를 가져옵니다.
  chrome.extension.getBackgroundPage().photos.forEach(function(photo) {
    var img = document.createElement('img');
    img.src = photo;
    photos.appendChild(img);
  });

  // 팝업을 닫습니다.
  document.getElementById('close').addEventListener('click', function(event) {
    window.close();
  });
&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;결과&lt;/h3&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_lmxf4pPiPj1qzzaw8.jpg" alt=""/&gt;&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;

&lt;p&gt;지금까지 두 예제를 통해 페이지 액션과 팝업 페이지를 구현해봤습니다. 컨텐트 스크립트와 백그라운드 페이지, 팝업 페이지 사이의 통신이 크롬 확장 프로그램 개발의 핵심이라고 생각합니다.&lt;/p&gt;

&lt;p&gt;다음 글에서는 &lt;a href="https://chrome.google.com/webstore/detail/jhnmcbbbdgknomnkigcmflfcahgblhdk"&gt;한국어 맞춤법 검사기&lt;/a&gt; 예제를 구현하면서 보조 메뉴, 알림창에 대해서 얘기하겠습니다.&lt;/p&gt;

&lt;h2&gt;참고&lt;/h2&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href="http://code.google.com/chrome/extensions/pageAction.html"&gt;Page Actions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/chrome/extensions/tabs.html"&gt;Tabs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/chrome/extensions/extension.html"&gt;chrome.extension&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chrome.google.com/webstore/detail/bfbmjmiodbnnpllbbbfblcplfjjepjdn"&gt;Turn Off the Lights&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;h2&gt;지난 연재&lt;/h2&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href="http://dailyupgrade.me/post/5176583468/chrome-extension-development-overview"&gt;크롬 확장 프로그램 개발 - 개요&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://dailyupgrade.me/post/5320852086/chrome-extension-development-browser-actions"&gt;크롬 확장 프로그램 개발 - 브라우저 액션&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://dailyupgrade.me/post/5759139172/chrome-extension-development-content-scripts"&gt;크롬 확장 프로그램 개발 - 컨텐트 스크립트&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><link>http://dailyupgrade.me/post/6707468769</link><guid>http://dailyupgrade.me/post/6707468769</guid><pubDate>Mon, 20 Jun 2011 10:13:06 +0900</pubDate><category>chrome</category><category>extension</category></item><item><title>크롬 확장 프로그램 개발 - 컨텐트 스크립트 | Chrome Extension Development - Content Scripts</title><description>&lt;h2&gt;개요&lt;/h2&gt;

&lt;p&gt;컨텐트 스크립트란 웹 페이지 안에서 동작하는 자바스크립트와 스타일시트를 말합니다. 페이지를 불러온 후 북마클릿을 실행하는 것처럼 페이지의 내용을 읽어올 수도 있고 마음대로 바꿀 수도 있습니다.&lt;/p&gt;

&lt;p&gt;물론 보안상 몇 가지 제한사항이 있습니다. 공식 페이지에서는 아래와 같이 얘기합니다.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;code&gt;chrome.extension.*&lt;/code&gt; 을 제외한 API를 사용할 수 없습니다.&lt;/li&gt;
&lt;li&gt;확장 프로그램 페이지에 선언한 변수, 함수를 사용할 수 없습니다.&lt;/li&gt;
&lt;li&gt;웹 페이지나 다른 컨텐트 스크립트에 선언한 변수, 함수를 사용할 수 없습니다.&lt;/li&gt;
&lt;li&gt;크로스 사이트 AJAX 통신을 할 수 없습니다.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;컨텐트 스크립트는 격리된 공간에서 실행됩니다. 예를 들어 jQuery를 사용하는 여러 확장 프로그램을 설치하더라도 충돌을 일으키지 않는다는 말입니다. 물론 같은 내용을 여러 번 호출하는 낭비는 있겠지만, 보안상 어쩔 수 없는 일이라고 봅니다.&lt;/p&gt;

&lt;p&gt;컨텐트 스크립트는 브라우저/페이지 액션에서 웹 페이지를 제어하려고 할 때 같이 사용하는 것이 대부분이지만 액션 없이 컨텐트 스크립트만 사용하는 예제를 먼저 만들어 봅니다.&lt;/p&gt;

&lt;h2&gt;무지개 링크&lt;/h2&gt;

&lt;p&gt;무지개 링크는 워드프레스나 텍스트큐브 등 설치형 블로그 서비스에서 제공하던 플러그인에서 착안했습니다. 커서를 올리면 링크 색깔이 다채롭게 변하는 기능인데요. 보통 자바스크립트를 이용해서 구현하던 것을 CSS3 애니메이션으로 만들어 보겠습니다.&lt;/p&gt;

&lt;h4&gt;manifest.json&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;{
  "name": "Rainbow Link",
  "version": "0.1.0",
  "content_scripts": [{
    "matches": ["http://*/*", "https://*/*"],
    "css": ["rainbow.css"]
  }]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;컨텐트 스크립트를 사용하기 위해서는 &lt;code&gt;manifest.json&lt;/code&gt; 파일에 &lt;code&gt;content_scripts&lt;/code&gt; 항목으로 사용할 파일과 적용할 주소를 등록해야 합니다.&lt;/p&gt;

&lt;div class="tips"&gt;

&lt;h3&gt;매치 패턴&lt;/h3&gt;

&lt;p&gt;매치 패턴은 확장 프로그램의 권한을 등록하는 &lt;code&gt;permission&lt;/code&gt; 항목이나 컨텐트 스크립트를 등록하는 &lt;code&gt;content_script&lt;/code&gt; 항목에서 적용할 웹 페이지 주소를 정할 때 사용하는 규칙입니다.&lt;/p&gt;

&lt;p&gt;와일드카드로 &lt;code&gt;*&lt;/code&gt; 문자를 사용할 수 있습니다. 자세한 규칙은 &lt;a href="http://code.google.com/chrome/extensions/match_patterns.html"&gt;홈페이지&lt;/a&gt;를 참고하시고 여기에서는 몇 가지 예만 적겠습니다.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;code&gt;http://*/*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http://*/foo*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://*.google.com/foo*bar&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&lt;a href="http://127.0.0.1/*"&gt;http://127.0.0.1/*&lt;/a&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*://mail.google.com/*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;all_urls&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;

&lt;h4&gt;rainbow.css&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;@-webkit-keyframes rainbow {
  0%   {color: #f00;}
  17%  {color: #f90;}
  33%  {color: #ff0;}
  50%  {color: #0f0;}
  67%  {color: #00f;}
  83%  {color: #60f;}
  100% {color: #90f;}
}

a:hover {
  -webkit-animation: rainbow infinite 3s;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;확장 프로그램을 설치하고 마우스 커서를 링크 위에 올려두면 휘황찬란한 결과를 확인할 수 있습니다. :) 무지개 링크라고 정말 일곱 가지 색만 넣었는데 색을 더 추가하면 보기 좋을 것 같습니다.&lt;/p&gt;

&lt;div class="tips"&gt;

&lt;h3&gt;&lt;code&gt;-webkit-animation&lt;/code&gt; 단축 형식&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;-webkit-animation: name duration timing_function delay iteration_count direction
&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.w3.org/TR/css3-animations/#the-animation-shorthand-property-"&gt;The ‘animation’ Shorthand Property&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.smashingmagazine.com/2011/05/17/an-introduction-to-css3-keyframe-animations/"&gt;An Introduction To CSS3 Keyframe Animations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;

&lt;h2&gt;페이지 이동 버튼&lt;/h2&gt;

&lt;p&gt;이번에는 자바스크립트 파일만 사용해서 웹 페이지의 맨 위와 아래로 이동하는 기능을 만들어봅니다.&lt;/p&gt;

&lt;h3&gt;manifest.json&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;{
  "name": "Go To",
  "version": "0.1.0",
  "content_scripts": [{
    "matches": ["http://*/*", "https://*/*"],
    "js": ["goto.js"]
  }]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;goto.js&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;(function() {
  var top    = document.createElement('a'),
      topImg = document.createElement('img');

  topImg.src = chrome.extension.getURL('top.png');
  top.addEventListener('click', function() {
    window.scrollTo(0, 0);
  });
  top.appendChild(topImg);

  var bottom    = document.createElement('a'),
      bottomImg = document.createElement('img');

  bottomImg.src = chrome.extension.getURL('bottom.png');
  bottom.addEventListener('click', function() {
    window.scrollTo(0, document.body.scrollHeight);
  });
  bottom.appendChild(bottomImg);

  var wrap = document.createElement('div');
  wrap.style.position = 'fixed';
  wrap.style.right    = 0;
  wrap.style.top      = 0;
  wrap.style.zIndex   = 10000;

  wrap.appendChild(top);
  wrap.appendChild(bottom);

  document.body.appendChild(wrap);
})();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;버튼 2개를 만들어 하나는 웹 페이지의 맨 위로 하나는 맨 아래로 이동하게 합니다. 그리고 버튼을 감싸는 &lt;code&gt;div&lt;/code&gt; 태그를 만들어 웹 페이지의 오른쪽 위에 고정합니다.&lt;/p&gt;

&lt;div class="tips"&gt;

&lt;h3&gt;&lt;code&gt;chrome.extension.getURL&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;컨텐트 스크립트에서 확장 프로그램에 등록된 파일에 접근하려고 할 때 사용합니다. 확장 프로그램을 등록할 때 사용하는 폴더를 기준으로 상대경로를 인자로 넘기면 됩니다.&lt;/p&gt;

&lt;p&gt;스타일시트에서는 자바스크립트를 사용할 수 없어서 확장 프로그램에 등록된 파일의 경로를 가져올 방법이 없으니 주의하시기 바랍니다.&lt;/p&gt;

&lt;/div&gt;

&lt;h2&gt;정리&lt;/h2&gt;

&lt;p&gt;간단하게 스타일시트와 자바스크립트 파일을 다루는 컨텐트 스크립트에 대해서 얘기했습니다. &lt;code&gt;chrome.extension.getURL&lt;/code&gt; 함수를 빼고는 원래 하던 대로 웹 페이지를 다루면 되므로 따로 설명할 말이 별로 없었습니다. :)&lt;/p&gt;

&lt;p&gt;다음 글에서는 페이지 액션을 다루도록 하겠습니다. 개발 방법은 브라우저 액션과 대부분 같아서 아직 설명하지 않은 팝업 페이지와 컨텐트 스크립트와의 연동을 중심으로 얘기하겠습니다.&lt;/p&gt;

&lt;h2&gt;참고&lt;/h2&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href="http://code.google.com/chrome/extensions/content_scripts.html"&gt;Content Scripts - Google Chrome Extensions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/chrome/extensions/match_patterns.html"&gt;Match Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/chrome/extensions/extension.html#method-getURL"&gt;chrome.extension.getURL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.w3.org/TR/css3-animations/#the-animation-shorthand-property-"&gt;The ‘animation’ Shorthand Property&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.smashingmagazine.com/2011/05/17/an-introduction-to-css3-keyframe-animations/"&gt;An Introduction To CSS3 Keyframe Animations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><link>http://dailyupgrade.me/post/5759139172</link><guid>http://dailyupgrade.me/post/5759139172</guid><pubDate>Mon, 23 May 2011 14:07:55 +0900</pubDate><category>chrome</category><category>extension</category></item><item><title>크롬 확장 프로그램 개발 - 브라우저 액션 | Chrome Extension Development - Browser Actions</title><description>&lt;p&gt;&lt;a href="https://chrome.google.com/webstore"&gt;크롬 웹 스토어&lt;/a&gt;에 있는 확장 프로그램들을 설치해 보면 대부분 브라우저 액션입니다. 페이지 액션이 조금 있고, 옴니박스는 몇 개 되지 않고 보조 메뉴나 컨텐트 스크립트는 보통 액션에 부가된 기능으로 들어가 있습니다.&lt;/p&gt;

&lt;h2&gt;브라우저 액션 對 페이지 액션&lt;/h2&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_lklz6kK9Zn1qzzaw8.jpg" alt="브라우저 액션, 페이지 액션"/&gt;&lt;/p&gt;

&lt;p&gt;그렇다면 브라우저 액션과 페이지 액션은 각각 언제 써야 할까요? 먼저 각 액션에서 지원하는 API를 살펴보겠습니다.&lt;/p&gt;

&lt;p&gt;먼저 브라우저 액션에서 사용하는 &lt;code&gt;chrome.browserAction&lt;/code&gt; API입니다.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;메소드

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;setBadgeBackgroundColor&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;setBadgeText&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setIcon&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setPopup&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setTitle&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;이벤트

&lt;ul&gt;&lt;li&gt;&lt;code&gt;onClicked&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;다음은 페이지 액션에서 사용하는 &lt;code&gt;chrome.pageAction&lt;/code&gt; API입니다.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;메소드

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;hide&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setIcon&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setPopup&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setTitle&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;show&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;이벤트

&lt;ul&gt;&lt;li&gt;&lt;code&gt;onClicked&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;브라우저 액션에는 뱃지에 대한 함수가, 페이지 액션에는 액션을 보이고 감추는 함수가 있다는 점을 빼면 모두 같습니다. 다른 점이 하나 더 있는데 아이콘 위치와 크기가 다르다는 것입니다. 브라우저 액션은 주소창 오른쪽에 있는 도구 모음에, 페이지 액션은 주소창 안에 아이콘이 보입니다. 브라우저 액션은 19픽셀, 페이지 액션은 16픽셀입니다.&lt;/p&gt;

&lt;p&gt;특별한 제한이 있는 것이 아니라서 사실 어떤 액션을 사용하더라도 상관없습니다. 페이지 액션의 대표적인 확장 프로그램인 RSS 구독을 예를 들면 아이콘이 숨겨져 있다가 RSS 피드가 웹 페이지에 있으면 아이콘을 보여줍니다. 이를 브라우저 액션으로 구현한다면 RSS 피드 유무에 따라서 아이콘을 바꿔서 보여 주는 방법을 쓸 수 있겠죠.&lt;/p&gt;

&lt;p&gt;공식 홈페이지에서는 대부분 페이지에서 실행하면 브라우저 액션을 쓰고 실행할 페이지가 적으면 브라우저 액션을 쓰라고 권합니다. 저는 아이콘을 감추고 싶으면 페이지 액션을 그 외에는 브라우저 액션을 쓰는 것을 추천합니다.&lt;/p&gt;

&lt;h2&gt;구현&lt;/h2&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_lkrmd1Q1K51qzzaw8.jpg" alt="Simple Pomodoro Timer"/&gt;&lt;/p&gt;

&lt;p&gt;요즘 많이 회자하는 시간 관리법인 &lt;a href="http://www.pomodorotechnique.com/"&gt;뽀모도로 테크닉&lt;/a&gt;에 써먹을 간단한 타이머를 브라우저 액션으로 만들려고 합니다. 뽀모도로를 잘 모르시는 분은 25분을 기본으로 하는 타이머를 만든다고 생각하시면 됩니다. 브라우저 액션만의 기능인 뱃지를 써서 남은 시간을 보여 주려고 합니다.&lt;/p&gt;

&lt;p&gt;확장 프로그램 개발을 할 때 가장 먼저 해야 할 일은 &lt;code&gt;manifest.json&lt;/code&gt; 파일을 작성하는 것입니다. 이번에는 브라우저 액션을 사용할 테니 &lt;code&gt;browser_action&lt;/code&gt; 필드를 추가합니다. 또 아이콘을 지정하기 위해 &lt;code&gt;icons&lt;/code&gt; 필드를, 뽀모도로 로직을 담을 &lt;code&gt;background_page&lt;/code&gt; 필드를 추가합니다.&lt;/p&gt;

&lt;h3&gt;manifest.json&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;{
  "name": "Simple Pomodoro Timer",
  "version": "0.1.0",
  "icons": {
    "48": "icon-48.png"
  },
  "browser_action": {
    "default_icon" : "icon-19.png",
    "default_title": "Simple Pomodoro Timer"
  },
  "background_page": "background.html"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;아이콘은 구글에서 Pomodoro로 검색한 아이콘을 크기만 조절해서 19, 48픽셀 두 가지를 사용합니다.&lt;/p&gt;

&lt;div class="tips"&gt;

&lt;h3&gt;아이콘 크기&lt;/h3&gt;

&lt;p&gt;해당 크기의 아이콘이 없으면 브라우저가 알아서 조절하지만 사용하는 용도에 맞는 모든 크기의 아이콘을 제공하는 것이 좋겠죠.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;favicon, 인포바&amp;#160;: 16픽셀&lt;/li&gt;
&lt;li&gt;주소창에 보이는 페이지 액션 아이콘&amp;#160;: 16픽셀&lt;/li&gt;
&lt;li&gt;도구 모음에 보이는 브라우저 액션 아이콘&amp;#160;: 19픽셀&lt;/li&gt;
&lt;li&gt;확장 프로그램 목록&amp;#160;: 48픽셀&lt;/li&gt;
&lt;li&gt;웹 스토어 소개 페이지&amp;#160;: 128픽셀&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;

&lt;h3&gt;API&lt;/h3&gt;

&lt;p&gt;아이콘을 클릭하면 뽀모도로를 실행해서 남은 시간을 보여 주고 한 번 더 클릭하면 정지하는 구조로 만들겠습니다. 이 프로그램에서 사용할 API는 두 가지입니다.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href="http://code.google.com/chrome/extensions/browserAction.html#event-onClicked"&gt;onClicked&lt;/a&gt;

&lt;ul&gt;&lt;li&gt;&lt;code&gt;chrome.browserAction.onClicked.addListener(function(Tab tab) {...})&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;브라우저 액션 아이콘을 클릭하면 발생하는 이벤트입니다. 처리할 함수를 인자로 넣습니다.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/chrome/extensions/browserAction.html#method-setBadgeText"&gt;setBadgeText&lt;/a&gt;

&lt;ul&gt;&lt;li&gt;&lt;code&gt;chrome.browserAction.setBadgeText(object details)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;뱃지에 단어를 적습니다. 아이콘이 19픽셀이라 네 글자 정도 넣을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;기능이 조금 심심한 것 같아서 하나만 더 추가해 보도록 하겠습니다.&lt;/p&gt;

&lt;h3&gt;&lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;혹시 &lt;a href="http://www.ommwriter.com/"&gt;OmmWriter&lt;/a&gt;라는 프로그램을 아시나요? 글쓰기에 집중할 수 있게 배경화면과 배경음을 제공하는 편집기입니다. 또 &lt;a href="http://www.rainymood.com/"&gt;RainyMood&lt;/a&gt; 같은 사이트를 가보신 적 있으신가요? 빗소리를 들려주는 사이트인데요. 저는 이렇게 마음을 차분하게 해 주는 배경음이 참 좋더라고요. :)&lt;/p&gt;

&lt;p&gt;기왕 크롬을 사용하는 마당에 HTML5 기능 하나는 넣을 겸 뽀모도로를 시작하면 빗소리를 배경음으로 넣겠습니다. &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; 태그를 추가하고 자바스크립트에서 제어하는 코드를 작성합니다.&lt;/p&gt;

&lt;p&gt;자세한 오디오 태그의 내용은 아래 사이트를 참고하세요.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href="http://html5doctor.com/native-audio-in-the-browser/"&gt;Native Audio in the browser - HTML5 Doctor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.w3schools.com/html5/tag_audio.asp"&gt;HTML5 audio tag - W3Schools&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;h3&gt;background.html&lt;/h3&gt;

&lt;p&gt;이제 완성된 코드는 아래와 같습니다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset="utf-8"&amp;gt;
    &amp;lt;title&amp;gt;&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;audio id="audio" src="rain.mp3" loop&amp;gt;&amp;lt;/audio&amp;gt;
    &amp;lt;script&amp;gt;
      var Pomodoro = {
        // 기본 뽀모도로 시간을 25분으로 합니다.
        POMODORO_TIME: 25 * 60 * 1000,

        // 뽀모도로를 시작합니다.
        start: function() {
          this.active = true;
          this.startedAt = new Date();
          this.interval = setInterval(this.countDown, 1000);
          document.getElementById('audio').play();
        },

        // 뽀모도로를 중지합니다.
        stop: function() {
          document.getElementById('audio').pause();
          clearInterval(this.interval);
          this.active = false;
        },

        // 1초마다 남은 시간을 계산해서 뱃지에 표시합니다.
        countDown: function() {
          var timeLeft = Pomodoro.POMODORO_TIME - (new Date() - Pomodoro.startedAt);

          if (timeLeft &amp;lt;= 0) {
            Pomodoro.stop();
            return;
          }

          var minutes = Math.floor(timeLeft / 60000),
              seconds = Math.floor((timeLeft - minutes * 60000) / 1000);

          chrome.browserAction.setBadgeText({
            text: minutes + ':' + (seconds &amp;lt; 10 ? '0' : '') + seconds
          });
        }
      };

      // 아이콘 클릭 이벤트를 설정합니다.
      chrome.browserAction.onClicked.addListener(function(tab) {
        // 클릭할 때마다 뽀모도로를 켰다 껐다 합니다.
        Pomodoro[Pomodoro.active ? 'stop' : 'start']();
      });
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;전체 소스는 &lt;a href="https://github.com/fallroot/chrome-extensions"&gt;GitHub&lt;/a&gt;에 올려 두었습니다.&lt;/p&gt;

&lt;p&gt;확장 프로그램을 브라우저에 등록하는 방법을 한 번 더 말씀드리자면&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;주소창에 &lt;code&gt;chrome://extensions&lt;/code&gt;을 입력하거나 &lt;strong&gt;도구 &amp;gt; 확장 프로그램&lt;/strong&gt; 메뉴를 선택합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;개발자 모드&lt;/strong&gt;를 열고 &lt;strong&gt;압축해제된 확장 프로그램 로드&amp;#8230;&lt;/strong&gt; 버튼을 클릭합니다.&lt;/li&gt;
&lt;li&gt;작업 파일들을 저장한 폴더를 선택합니다.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_lkrmdthDiZ1qzzaw8.jpg" alt="확장 프로그램 등록"/&gt;&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;

&lt;p&gt;지금까지 브라우저 액션을 사용해서 뽀모도로 타이머를 만들어 봤습니다. 나중에 팝업 페이지나 설정 페이지를 추가해서 좀 더 그럴듯하게 만들 수도 있을 것 같습니다. 저는 뽀모도로를 GTD(Getting Things Done)와 결합해서 웹 앱으로 만들어 보는 건 어떨까 생각만 하고 있습니다. :)&lt;/p&gt;

&lt;p&gt;브라우저 / 페이지 액션 아이콘은 &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;를 이용해서 만들 수 있다고 합니다. 약간의 애니메이션 효과를 넣을 수 있겠네요.&lt;/p&gt;

&lt;p&gt;브라우저 액션은 이번 예제처럼 웹 페이지와 상관없이 구현된 것들도 많이 있습니다. 메일이나 페이스북 등의 서비스에 주기적으로 접근해 새 소식을 알려 주는 프로그램들이 좋은 예가 되겠습니다.&lt;/p&gt;

&lt;p&gt;공식 홈페이지에 있는 &lt;a href="http://code.google.com/chrome/extensions/browserAction.html"&gt;API 문서&lt;/a&gt;와 &lt;a href="http://code.google.com/chrome/extensions/samples.html#chrome.browserAction"&gt;예제 프로그램들&lt;/a&gt;도 반드시 확인해 보시기 바랍니다.&lt;/p&gt;

&lt;p&gt;다음 글에서는 페이지 액션을 구현하기 전에 컨텐트 스크립트를 사용해서 웹 페이지를 제어하는 방법에 대해서 먼저 알아보도록 하겠습니다. 컨텐트 스크립트를 익힌 다음에는 웹 페이지를 사용하는 브라우저 액션이나 페이지 액션을 구현할 수 있을 겁니다.&lt;/p&gt;

&lt;h2&gt;참고&lt;/h2&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href="http://code.google.com/chrome/extensions/browserAction.html"&gt;Browser Actions - Google Chrome Extensions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/chrome/extensions/samples.html#chrome.browserAction"&gt;Samples - Google Chrome Extensions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.pomodorotechnique.com/"&gt;뽀모도로 테크닉&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.ommwriter.com/"&gt;OmmWriter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.rainymood.com/"&gt;RainyMood&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://html5doctor.com/native-audio-in-the-browser/"&gt;Native Audio in the browser - HTML5 Doctor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.w3schools.com/html5/tag_audio.asp"&gt;HTML5 audio tag - W3Schools&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><link>http://dailyupgrade.me/post/5320852086</link><guid>http://dailyupgrade.me/post/5320852086</guid><pubDate>Mon, 09 May 2011 10:00:05 +0900</pubDate><category>chrome</category><category>extension</category></item><item><title>크롬 확장 프로그램 개발 - 개요 | Chrome Extension Development - Overview</title><description>&lt;p&gt;크롬 확장 프로그램은 구글 크롬 브라우저에 설치해서 사용하는 부가 기능을 말합니다. &lt;a href="https://chrome.google.com/webstore"&gt;크롬 웹 스토어&lt;/a&gt;에서 내려받을 수 있고 개발한 확장 프로그램을 올릴 수도 있습니다.&lt;/p&gt;

&lt;h3&gt;웹킷 엔진에서 실행됩니다.&lt;/h3&gt;

&lt;ul&gt;&lt;li&gt;HTML5, CSS3 지원이 뛰어납니다.&lt;/li&gt;
&lt;li&gt;같은 엔진을 사용하는 iOS, 안드로이드에 응용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;&lt;h3&gt;HTML, 자바스크립트, 스타일시트 파일로 이루어져 있습니다.&lt;/h3&gt;

&lt;ul&gt;&lt;li&gt;웹 개발에 필요한 지식을 그대로 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;XUL을 써야 하는 파이어폭스에 비해 개발하기 편합니다.&lt;/li&gt;
&lt;/ul&gt;&lt;h2&gt;확장 프로그램 형태&lt;/h2&gt;

&lt;p&gt;크롬 확장 프로그램은 여러 가지 형태로 개발할 수 있습니다. 외관상으로 구별되는 점도 있고 사용하는 API가 다르기도 합니다. 어떤 형태로 개발할 수 있는지 하나씩 살펴보겠습니다.&lt;/p&gt;

&lt;h3&gt;브라우저 액션, 페이지 액션 (Browser Actions, Page Actions)&lt;/h3&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_lklz6kK9Zn1qzzaw8.jpg" alt="브라우저 액션, 페이지 액션"/&gt;&lt;/p&gt;

&lt;p&gt;가장 일반적인 형태의 확장 프로그램입니다. 브라우저 액션은 주소창 오른쪽에 페이지 액션은 주소창 안에 아이콘이 나타납니다. 둘 다 팝업 페이지를 가질 수 있습니다.&lt;/p&gt;

&lt;h3&gt;보조 메뉴 (Context Menus)&lt;/h3&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_lklz71Dc8T1qzzaw8.jpg" alt="보조 메뉴"/&gt;&lt;/p&gt;

&lt;p&gt;마우스 오른쪽 클릭을 하면 생기는 보조 메뉴에 나타납니다. 특정한 종류의 요소를 클릭했을 때만 동작하게 할 수 있습니다.&lt;/p&gt;

&lt;h3&gt;옴니박스 (Omnibox)&lt;/h3&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_lklz7g58NG1qzzaw8.jpg" alt="옴니박스"/&gt;&lt;/p&gt;

&lt;p&gt;특정 검색어를 주소창에 입력하면 나타납니다. 주로 검색과 관련된 확장 프로그램에 적합합니다.&lt;/p&gt;

&lt;h3&gt;오버라이드 페이지 (Override Pages)&lt;/h3&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_lklz8djibT1qzzaw8.jpg" alt="오버라이드 페이지"/&gt;&lt;/p&gt;

&lt;p&gt;크롬에서 기본적으로 제공해 주는 즐겨찾기, 방문 기록, 새 탭 페이지를 바꿉니다.&lt;/p&gt;

&lt;h3&gt;앱 (Apps)&lt;/h3&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_lklz7u3Ih31qzzaw8.jpg" alt="앱"/&gt;&lt;/p&gt;

&lt;p&gt;크롬 브라우저의 기능을 확장하려는 것이 아니라 독립적인 탭에서 동작하는 웹 앱입니다.&lt;/p&gt;

&lt;h3&gt;테마 (Themes)&lt;/h3&gt;

&lt;p&gt;크롬 브라우저의 외관을 바꿉니다. 연재에서 다루지 않습니다.&lt;/p&gt;

&lt;h2&gt;확장 프로그램 페이지&lt;/h2&gt;

&lt;p&gt;확장 프로그램을 어떤 형태로 개발할지 결정했다면 이번에는 어떤 페이지를 작성해야 하는지 알아봅니다. 반드시 작성해야 하는 페이지는 없습니다. 만들려는 확장 프로그램 형태에 따라서 필요한 페이지를 추가하면 됩니다.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_lkmatjDAo41qzzaw8.jpg" alt="확장 프로그램 페이지"/&gt;&lt;/p&gt;

&lt;h3&gt;백그라운드 페이지 (Background Pages)&lt;/h3&gt;

&lt;p&gt;확장 프로그램 개발의 기본이 되는 HTML 파일입니다. 확장 프로그램 실행에 필요한 주 알고리즘을 이 페이지에 넣는 것이 일반적입니다. 사용자에게 보이지 않습니다.&lt;/p&gt;

&lt;h3&gt;컨텐트 스크립트 (Content Scripts)&lt;/h3&gt;

&lt;p&gt;웹 페이지에 끼워 넣을 수 있는 자바스크립트와 스타일시트 파일을 일컫습니다. 웹 페이지의 DOM(Document Object Model)을 살펴보거나 수정하기 위해 사용합니다.&lt;/p&gt;

&lt;h3&gt;팝업 페이지 (Popup Pages)&lt;/h3&gt;

&lt;p&gt;브라우저 액션, 페이지 액션에서 사용할 수 있는 HTML 파일입니다. 아이콘을 클릭하면 팝업 페이지를 보여줍니다.&lt;/p&gt;

&lt;h3&gt;설정 페이지 (Options Pages)&lt;/h3&gt;

&lt;p&gt;확장 프로그램에서 사용하는 값들을 사용자가 선택할 수 있게 하고 싶을 때 제공하는 HTML 파일입니다. 보통 &lt;code&gt;localStorage&lt;/code&gt;를 이용해서 값들을 저장합니다.&lt;/p&gt;

&lt;h2&gt;확장 프로그램 등록&lt;/h2&gt;

&lt;ol&gt;&lt;li&gt;&lt;p&gt;편집기를 열고 아래 내용을 입력한 후 &lt;code&gt;manifest.json&lt;/code&gt; 이름으로 저장합니다. 키와 문자열은 반드시 겹따옴표로 둘러 싸야 합니다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
    "name": "첫 번째 확장 프로그램",
    "version": "0.1.0"
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;주소창에 &lt;code&gt;chrome://extensions&lt;/code&gt;을 입력하거나 &lt;strong&gt;도구 &amp;gt; 확장 프로그램&lt;/strong&gt; 메뉴를 선택합니다.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;개발자 모드&lt;/strong&gt;를 열고 &lt;strong&gt;압축해제된 확장 프로그램 로드&amp;#8230;&lt;/strong&gt; 버튼을 클릭합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;manifest.json&lt;/code&gt; 파일을 저장한 폴더를 선택합니다.&lt;/li&gt;
&lt;li&gt;파일을 추가하거나 수정하면 &lt;strong&gt;새로고침&lt;/strong&gt; 버튼을 누르면 됩니다.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_lklz60WEDg1qzzaw8.jpg" alt="크롬 확장 프로그램 등록"/&gt;&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;

&lt;p&gt;크롬 브라우저에서만 실행할 수 있다는 분명한 단점이 있지만 다른 브라우저를 전혀 신경 쓰지 않고 마음껏 HTML5, CSS3를 실험할 수 있습니다. 웹 개발자에게는 학습 난도가 낮아서 크롬 브라우저를 주로 사용한다면 장난삼아(?) 한 번 손댈만한 것 같습니다.&lt;/p&gt;

&lt;p&gt;다음 글부터 브라우저 액션과 페이지 액션을 시작으로 여러 예제를 만들어 보면서 확장 프로그램의 형태와 페이지에 대해 자세하게 살펴보겠습니다.&lt;/p&gt;

&lt;h2&gt;참고&lt;/h2&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href="http://code.google.com/chrome/extensions/index.html"&gt;Google Chrome Extensions - Google Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chrome.google.com/webstore"&gt;Chrome Web Store - Apps, Extensions and Themes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><link>http://dailyupgrade.me/post/5176583468</link><guid>http://dailyupgrade.me/post/5176583468</guid><pubDate>Wed, 04 May 2011 10:00:00 +0900</pubDate><category>chrome</category><category>extension</category></item><item><title>DEVONthink에서 마크다운 사용하기 | Using Markdown in DEVONthink</title><description>&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_lk74zpGJnf1qzzaw8.png" alt="DEVONthink"/&gt;&lt;/p&gt;

&lt;p&gt;DEVONthink에서 바로 만들 수 있는 문서 파일은 Plain Text, Rich Text, Script, HTML, XML, Property List 입니다. 마크다운을 지원하지는 않지만 밖에서 만든 마크다운 파일을 임포트해서 사용할 수 있습니다. 이 과정을 애플스크립트를 써서 자동화도록 하겠습니다.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://dailyupgrade.me/post/4913748629/using-markdown-in-mac"&gt;이전 글&lt;/a&gt;에서 말한 대로 한글 마크다운을 제대로 훑어보기 위해서 최상단에 &lt;code&gt;chartset: utf-8&lt;/code&gt;을 적어줘야 합니다. 매번 적어주는 일이 귀찮을 테니 마크다운 문서를 만들 때 이 문장을 미리 적은 파일을 임포트합니다.&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;&lt;p&gt;편집기를 열고 아래 문장을 입력합니다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;charset: utf-8
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/tmp&lt;/code&gt; 폴더에 &lt;code&gt;import.markdown&lt;/code&gt; 이름으로 저장합니다.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;애플스크립트 편집기를 열어서 아래 코드를 입력합니다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;do shell script "touch /tmp/import.markdown"

tell application id "com.devon-technologies.thinkpro2"
    import "/tmp/import.markdown" name "Change this title"
end tell
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;스크립트 파일을 &lt;code&gt;~/Library/Application Support/DEVONthink Pro 2/Scripts/Data&lt;/code&gt; 폴더에 &lt;code&gt;New Markdown&lt;/code&gt;이라는 이름으로 저장합니다.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;시스템 설정 &amp;gt; 키보드 &amp;gt; 단축키 메뉴로 들어가서 DEVONthink 어플리케이션에 &lt;code&gt;New Markdown&lt;/code&gt; 제목으로 단축키를 지정합니다. 저는 &lt;code&gt;⌘⌃M&lt;/code&gt;을 사용합니다.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;스크립트 파일 이름과 저장 폴더는 취향대로 선택하시면 되지만 파일 이름과 단축키 제목은 같아야 합니다.&lt;/p&gt;</description><link>http://dailyupgrade.me/post/4942981795</link><guid>http://dailyupgrade.me/post/4942981795</guid><pubDate>Tue, 26 Apr 2011 10:00:05 +0900</pubDate><category>markdown</category><category>mac</category><category>devonthink</category></item><item><title>맥에서 마크다운 사용하기 | Using Markdown in Mac</title><description>&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_lk06ataIeq1qzzaw8.jpg" alt=""/&gt;&lt;/p&gt;

&lt;p&gt;이 글은 확장 마크다운인 &lt;a href="http://fletcherpenney.net/multimarkdown/"&gt;MultiMarkdown&lt;/a&gt;을 사용하는 것을 기본으로 합니다. OS X 10.6.7 영문 상위에서 시험했습니다.&lt;/p&gt;

&lt;h2&gt;마크다운 실행파일&lt;/h2&gt;

&lt;p&gt;가장 먼저 마크다운의 기본이 되는 실행 파일을 설치합니다.&lt;/p&gt;

&lt;h3&gt;설치&lt;/h3&gt;

&lt;ol&gt;&lt;li&gt;브라우저를 열고 &lt;a href="https://github.com/fletcher/peg-multimarkdown/downloads/"&gt;MultiMarkdown 저장소&lt;/a&gt;로 갑니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MultiMarkdown-Mac-x.x.x.pkg.zip&lt;/code&gt; 파일을 받습니다.&lt;/li&gt;
&lt;li&gt;압축을 풀고 패키지 파일을 실행합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/usr/local/bin&lt;/code&gt; 위치에 &lt;code&gt;multimarkdown&lt;/code&gt; 파일과 &lt;code&gt;mmd&lt;/code&gt;로 시작하는 실행 파일들이 설치됩니다.&lt;/li&gt;
&lt;/ol&gt;&lt;h2&gt;마크다운 훑어보기&lt;/h2&gt;

&lt;p&gt;훑어보기(Quick Look)는 맥 파인더에서 해당 프로그램을 실행하지 않고 스페이스 키를 눌러서 파일의 내용을 미리 보는 기능입니다. 기본적으로 지원하는 파일 종류가 다양한데다 다른 사용자들이 개발한 플러그인을 설치해서 확장할 수도 있습니다. 이 플러그인은 &lt;code&gt;.qlgenerator&lt;/code&gt; 확장자를 갖습니다.&lt;/p&gt;

&lt;h3&gt;훑어보기 플러그인 사이트&lt;/h3&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.quicklookplugins.com/"&gt;QuickLook Plugins&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.qlplugins.com/"&gt;QLPlugins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;MultiMarkdown 역시 훑어보기 플러그인을 제공합니다.&lt;/p&gt;

&lt;h3&gt;설치&lt;/h3&gt;

&lt;ol&gt;&lt;li&gt;브라우저를 열고 &lt;a href="https://github.com/fletcher/peg-multimarkdown/downloads/"&gt;MultiMarkdown 저장소&lt;/a&gt;로 갑니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;QLMultiMarkdown.qlgenerator.zip&lt;/code&gt; 파일을 받아 압축을 풉니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;QLMultiMarkdown.qlgenerator&lt;/code&gt; 파일이 나옵니다.&lt;/li&gt;
&lt;li&gt;모든 사용자에게 적용하려면 &lt;code&gt;/Library/QuickLook&lt;/code&gt; 위치에, 현재 사용자에게만 적용하려면 &lt;code&gt;~/Library/QuickLook&lt;/code&gt; 폴더에 이 파일을 옮깁니다.&lt;/li&gt;
&lt;li&gt;파인더에서 마크다운 파일을 선택하고 스페이스 키를 눌러서 정상적으로 동작하는지 확인합니다.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;동작하지 않으면 터미널을 열고 아래 명령어를 입력한 후 다시 해봅니다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;qlmanage -r
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;h3&gt;삭제&lt;/h3&gt;

&lt;p&gt;훑어보기 플러그인 파일을 넣은 폴더에서 &lt;code&gt;.qlgenerator&lt;/code&gt; 확장자를 갖는 플러그인 파일을 삭제합니다.&lt;/p&gt;

&lt;h2&gt;한글 마크다운 파일 훑어보기&lt;/h2&gt;

&lt;p&gt;한글이 포함된 마크다운 파일을 훑어보기하면 한글이 깨져서 보입니다. 기술적으로 이해하시려는 분은 &lt;a href="https://github.com/fletcher/peg-multimarkdown/wiki/How-do-I-create-a-MultiMarkdown-document%3F"&gt;How do I create a MultiMarkdown document? - GitHub&lt;/a&gt; 문서의 Metadata 부분을 보시기 바랍니다. 가장 쉬운 해결 방법은 문서 첫 줄에 아래 문장을 넣는 것입니다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;charset: utf-8
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;매번 이 문장을 넣는 것이 귀찮겠지만 나중에 해결하도록 하고 일단 다음으로 넘어가겠습니다.&lt;/p&gt;

&lt;h2&gt;마크다운 미리보기&lt;/h2&gt;

&lt;p&gt;작성된 마크다운 파일을 보는 것은 훑어보기로 쉽게 할 수 있지만 문서를 작성 중이라면 훑어보기보다는 미리보기 기능이 필요할 겁니다. 이 기능을 지원하는 &lt;a href="http://www.panic.com/coda/"&gt;Coda&lt;/a&gt;, &lt;a href="http://macromates.com/"&gt;TextMate&lt;/a&gt; 등의 편집기들이 있지만 어떤 프로그램을 사용하든 작성하는 사이사이에 쉽게 미리보기가 가능하도록 하고 싶습니다. 오토메이터로 간단하게 서비스를 만들어 보도록 하겠습니다.&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;오토메이터를 실행합니다.&lt;/li&gt;
&lt;li&gt;템플릿에서 서비스를 선택합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Replaces selected text&lt;/code&gt; 체크박스를 선택 해제합니다.&lt;/li&gt;
&lt;li&gt;왼쪽 라이브러리에서 &lt;code&gt;Run Shell Script&lt;/code&gt;를 선택해서 오른쪽 화면으로 끌어다 놓습니다.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;아래 문장을 입력합니다. 사파리는 &lt;code&gt;Safari&lt;/code&gt;, 파이어폭스는 &lt;code&gt;Firefox&lt;/code&gt;로 바꿔서 입력합니다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/usr/local/bin/multimarkdown &amp;gt; /tmp/mmd.html
open -a "Google Chrome" /tmp/mmd.html
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;제목을 &lt;code&gt;Preview MultiMarkdown&lt;/code&gt;로 정하고 저장하면 &lt;code&gt;~/Library/Services&lt;/code&gt; 위치에 저장됩니다.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;편집기에서 문장을 선택하고 마우스 오른쪽 버튼을 누릅니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Services &amp;gt; Preview MultiMarkdown&lt;/code&gt; 메뉴를 선택하면 마크다운이 HTML로 변환되서 브라우저에서 보입니다.&lt;/li&gt;
&lt;/ol&gt;&lt;h3&gt;단축키 지정&lt;/h3&gt;

&lt;ol&gt;&lt;li&gt;시스템 설정을 실행합니다.&lt;/li&gt;
&lt;li&gt;키보드 메뉴를 선택합니다.&lt;/li&gt;
&lt;li&gt;키보드 단축키 탭을 선택합니다.&lt;/li&gt;
&lt;li&gt;왼쪽에서 서비스를 선택합니다.&lt;/li&gt;
&lt;li&gt;텍스트 카테고리 밑에 &lt;code&gt;Preview MultiMarkdown&lt;/code&gt;을 찾아서 더블클릭합니다.&lt;/li&gt;
&lt;li&gt;원하는 단축키를 지정합니다.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;이렇게 단축키를 지정하면 어떤 프로그램을 사용하든 문장을 선택한 후 지정한 단축키를 누르면 미리보기가 가능합니다. 이 방법이 마음에 안 들어서 특정 프로그램에서만 단축키가 동작하게 하고 싶으면 아래와 같이 합니다.&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;시스템 설정을 실행합니다.&lt;/li&gt;
&lt;li&gt;키보드 메뉴를 선택합니다.&lt;/li&gt;
&lt;li&gt;키보드 단축키 탭을 선택합니다.&lt;/li&gt;
&lt;li&gt;왼쪽에서 응용 프로그램 단축키를 선택합니다.&lt;/li&gt;
&lt;li&gt;오른쪽 더하기 버튼을 누릅니다.&lt;/li&gt;
&lt;li&gt;원하는 프로그램을 선택하고 메뉴 제목에 &lt;code&gt;Preview MultiMarkdown&lt;/code&gt;를 입력합니다.&lt;/li&gt;
&lt;li&gt;원하는 단축키를 지정합니다.&lt;/li&gt;
&lt;/ol&gt;&lt;h3&gt;삭제&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;~/Library/Services&lt;/code&gt;에 있는 &lt;code&gt;Preview MultiMarkdown&lt;/code&gt; 파일을 삭제합니다.&lt;/p&gt;

&lt;h2&gt;마크다운 링크&lt;/h2&gt;

&lt;p&gt;마크다운 문서를 작성하면서 가장 많이 하는 일 가운데 하나는 웹 페이지를 모으는 것입니다. 브라우저에서 주소를 복사해서 옮겨 적는 일이 많은데요. 저는 &lt;code&gt;&amp;lt;http://dailyupgrade.me/&amp;gt;&lt;/code&gt; 형식으로 주소만 적는 것보다 &lt;code&gt;[Daily Upgrade](&lt;a href="http://dailyupgrade.me"&gt;http://dailyupgrade.me&lt;/a&gt;)&lt;/code&gt; 형식을 좋아하는데 매번 이렇게 만드는 것이 귀찮더라고요. 그래서 구글 크롬 확장 프로그램으로 간단하게 만들어 봤습니다. :)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://chrome.google.com/webstore/detail/nckkdgnncmnkpgjliombbmadaiejdckd"&gt;여기&lt;/a&gt;에서 설치하시면 되고요. 브라우저에서 아이콘을 클릭하면 문서 제목과 주소가 마크다운 형식으로 클립보드에 복사됩니다. 바로 마크다운 문서에 붙여서 사용하시면 됩니다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;

&lt;p&gt;여기까지 맥에서 마크다운을 쓰기 위한 여러 가지 방법들을 적어 봤습니다. 다음에는 제가 맥에서 가장 많이 사용하는 &lt;a href="http://www.devon-technologies.com/products/devonthink/"&gt;DEVONthink&lt;/a&gt; 프로그램에서 마크다운을 사용하는 방법을 얘기하려고 합니다.&lt;/p&gt;</description><link>http://dailyupgrade.me/post/4913748629</link><guid>http://dailyupgrade.me/post/4913748629</guid><pubDate>Mon, 25 Apr 2011 10:31:05 +0900</pubDate><category>markdown</category><category>multimarkdown</category><category>mac</category></item><item><title>마크다운이란? | About Markdown</title><description>&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_ljzn4tf9XE1qzzaw8.jpg" alt="마크다운"/&gt;&lt;/p&gt;

&lt;h2&gt;소개&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;Markdown is a text-to-HTML conversion tool for web writers. Markdown allows you to write using an easy-to-read, easy-to-write plain text format, then convert it to structurally valid XHTML (or HTML).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;위 문장은 마크다운을 만든 John Gruber의 &lt;a href="http://daringfireball.net/projects/markdown/"&gt;홈페이지&lt;/a&gt;에서 가져온 문장입니다. 이 짧은 문장에 마크다운의 핵심 철학이 모두 들어 있습니다.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;읽기 쉽다&lt;/li&gt;
&lt;li&gt;쓰기 쉽다.&lt;/li&gt;
&lt;li&gt;일반 텍스트 파일이다.&lt;/li&gt;
&lt;li&gt;HTML 형식으로 변환한다.&lt;/li&gt;
&lt;/ul&gt;&lt;h3&gt;읽기 쉽다&lt;/h3&gt;

&lt;p&gt;마크다운 형식으로 쓴 글을 보면 종이에 적은 것과 비슷한 느낌을 받습니다. 제목, 목록과 같은 형식을 쉽게 알아 볼 수 있습니다.&lt;/p&gt;

&lt;h3&gt;쓰기 쉽다&lt;/h3&gt;

&lt;p&gt;마크다운 문법은 아주 쉽습니다. HTML 문법에 비하면 학습 난이도에서 비교가 되지 않습니다. 생각의 흐름을 방해하지 않고 직관적으로 글을 쓸 수 있습니다. 쓰기 쉽다는 장점은 특히 모바일 시대에 더 두드러집니다. 많은 사람들이 즐겨 사용하는 &lt;a href="http://www.evernote.com/"&gt;Evernote&lt;/a&gt;, &lt;a href="http://springpadit.com/"&gt;Springpad&lt;/a&gt; 같은 메모 프로그램에서도 약간의 수고로 메모를 좀 더 직관적이고 구조적으로 만들어 줍니다.&lt;/p&gt;

&lt;h3&gt;일반 텍스트 파일이다&lt;/h3&gt;

&lt;p&gt;파일을 작성하기 위해서 오피스 프로그램을 실행하지 않아도 됩니다. 낯선 곳에서 아래한글이 설치되어 있지 않아서 발을 동동 구를 필요도 없습니다. 메모장에서 적어도 되고 스마트폰을 꺼내서 작성해도 됩니다.&lt;/p&gt;

&lt;h3&gt;HTML 형식으로 변환한다&lt;/h3&gt;

&lt;p&gt;작성이 끝난 글은 HTML 형식으로 바꿔서 블로그를 비롯한 온라인 서비스에 쉽게 올릴 수 있습니다. 글의 외관을 꾸미는 일은 HTML로 바꾼 다음에 할 일이고 마크다운에서는 글 쓰는 것에 집중할 수 있습니다.&lt;/p&gt;

&lt;p&gt;더 좋은 내용을 보시려면 아래 링크를 참고하시기 바랍니다.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href="http://offree.net/entry/Markdown-Formatter"&gt;HTML을 모르면? 마크다운을 사용하자! - 도아&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://hi8ar.net/entry/%EB%B8%94%EB%A1%9C%EA%B7%B8-HTML-%EA%B8%80%EC%93%B0%EA%B8%B0%EC%9D%98-%EA%B8%B0%EC%88%A0"&gt;블로그, HTML 글쓰기의 기술. - hi8ar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;h2&gt;지원&lt;/h2&gt;

&lt;h3&gt;서비스형 블로그&lt;/h3&gt;

&lt;p&gt;마이크로블로그로 유명한 &lt;a href="http://www.tumblr.com"&gt;Tumblr&lt;/a&gt;, &lt;a href="http://posterous.com"&gt;Posterous&lt;/a&gt;에서는 마크다운을 지원합니다.&lt;/p&gt;

&lt;h3&gt;설치형 블로그&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://wordpress.org/"&gt;WordPress&lt;/a&gt;, &lt;a href="http://www.textcube.org/"&gt;TextCube&lt;/a&gt;에서는 플러그인을 설치해서 마크다운으로 글을 쓸 수 있습니다.&lt;/p&gt;

&lt;h3&gt;편집기&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://macromates.com/"&gt;TextMate&lt;/a&gt;, &lt;a href="http://www.panic.com/coda/"&gt;Coda&lt;/a&gt; 등의 편집기에서도 미리보기, HTML로 변환하기 등의 기능이 플러그인 설치로 가능합니다.&lt;/p&gt;

&lt;h2&gt;기본 문법&lt;/h2&gt;

&lt;p&gt;마크다운에서 제공하는 문법을 간략하게 적어 보면 아래와 같습니다.&lt;/p&gt;

&lt;h3&gt;강조&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;*강조*
**강한 강조**
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;코드&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;`한 줄 코드`
(탭 또는 공백4개 이상) 여러 줄 코드 
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;목록&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;* 순서 없는 목록
* 순서 없는 목록

1. 순서 있는 목록
2. 순서 있는 목록 
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;제목&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;# 제목 1
## 제목 2
### 제목 3
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;인용문&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; 인용문
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;링크&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;[구글](&lt;a href="http://google.com/"&gt;http://google.com/&lt;/a&gt;)
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;이미지&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;![위키피디아 로고](&lt;a href="http://upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png"&gt;http://upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png&lt;/a&gt;)
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;수평선&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;***
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;자세한 문법은 아래 링크를 참고하시기 바랍니다.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href="http://daringfireball.net/projects/markdown/syntax"&gt;Markdown Syntax Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Markdown"&gt;Markdown - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://stackoverflow.com/editing-help"&gt;Markdown Editing Help - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://hw.libsyn.com/p/8/3/3/8339a864bb8faa83/Markdown_Cheat_Sheet.pdf"&gt;Markdown Syntax Cheat Sheet - HOWTUBE&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;h2&gt;확장 마크다운&lt;/h2&gt;

&lt;p&gt;마크다운이 제공하는 기능은 그 문법만큼이나 단순합니다. 그 부족한 기능을 보강하려는 움직임들이 아래의 확장 마크다운을 만들었습니다.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href="http://michelf.com/projects/php-markdown/extra/"&gt;Markdown Extra&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://fletcherpenney.net/multimarkdown/"&gt;MultiMarkdown&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;어떤 것이 먼저 만들어져서 영향을 주었는지는 모르겠지만 두 확장 마크다운의 기능은 매우 비슷합니다. Markdown Extra는 PHP로 만들어졌기 때문에 웹 서비스에 적용하기 좋을 것 같고 저는 맥에서 사용하는 것을 우선으로 하기 때문에 MultiMarkdown을 선택했습니다.&lt;/p&gt;

&lt;h2&gt;확장 마크다운 문법&lt;/h2&gt;

&lt;p&gt;두 확장이 공통적으로 지원하는 문법 위주로 간략하게 적도록 하겠습니다.&lt;/p&gt;

&lt;h3&gt;인라인 HTML&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;p&amp;gt;인라인 HTML&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;HTML 안에서 마크다운 사용&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;div markdown="1"&amp;gt;
마크다운 안에 *HTML* 블록이 들어갑니다.
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;제목 아이디 속성&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;# 제목 1 {#header1}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;코드 블럭&lt;/h3&gt;

&lt;p&gt;MultiMarkdown, Tumblr에서 지원하지 않습니다. 개발자 입장에서 가장 아쉬운 점입니다. :(&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;~~~~~~~~
코드 블럭
~~~~~~~~
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;테이블&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;헤더 1 | 헤더 2
------|-------
셀 1 | 셀 2
셀 3 | 셀 4
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;정의 목록&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;마크다운
:   간단한 마크업 언어로, 일반 텍스트로 문장 구조를 표기하던 관례를 규칙으로 만든 문법이다.
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;각주&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;이 문장에는 각주가 들어갑니다.[^1]

[^1]: 첫 번째 각주입니다.
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;약어&lt;/h3&gt;

&lt;p&gt;MultiMarkdown, Tumblr에서 지원하지 않습니다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;*[HTML]: Hyper Text Markup Language
*[W3C]:  World Wide Web Consortium
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;인용&lt;/h3&gt;

&lt;p&gt;Markdown Extra, Tumblr에서 지원하지 않습니다. 논문을 많이 다루시는 분들에게 필수적인 기능일 것 같습니다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;자유시장에서 우리의 선택은 얼마나 자유로운가?[p. 143][#Justice:2010].

[#Justice:2010]: 마이클 샌델. 김영사. 2010.
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;정리&lt;/h2&gt;

&lt;p&gt;이것으로 마크다운과 확장 마크다운에 대한 짧은 소개와 문법 설명을 마칩니다. 마크다운의 철학이 널리 퍼지길 바라면서 다음 글에서는 맥에서 마크다운을 활용하는 법에 대해 적도록 하겠습니다.&lt;/p&gt;</description><link>http://dailyupgrade.me/post/4821686285</link><guid>http://dailyupgrade.me/post/4821686285</guid><pubDate>Fri, 22 Apr 2011 10:00:06 +0900</pubDate><category>markdown</category><category>multimarkdown</category></item></channel></rss>

