Django 소개 및 사용법

본 글은 http://tutorial.djangogirls.org/ko/ 의 튜토리얼 을 따라 블로그를 개발하고, 주요 정보들을 요약한 글입니다. 최종 결과물은 http://captainpangyo.pythonanywhere.com/ 에서 확인 가능합니다.

Django 란 무엇인가?

  • Django란 (/ˈdʒæŋɡoʊ/ jang-goh/쟁고/장고) 파이썬으로 만들어진 무료 오픈소스 웹 어플리케이션 프레임워크

Urlresolver

  • 역할 : 웹 서버에 요청이 오면 장고로 전달되고, 장고가 웹 페이지의 주소를 가져와 무엇을 할지 확인한다.
  • 각 URL 에 대해 일일이 확인하기는 비효율적이므로, 패턴으로 일치여부를 판단한다.
  • 일치하는 패턴의 경우 요청을 연관된 함수(view)에 넘긴다.
  • 결론 : urlresolver는 우체국의 우편 배달부 역할을 한다. (편지의 주소를 확인하고 맞으면 집에 전달해주는)

Django 설치

  • 가상환경 (Virtualenv) : 프로젝트 기초 전부를 Python/Django 와 분리시켜준다. 웹사이트가 변경되어도 개발 중인 것에 영향을 미치지 않는다.
  • 가상환경 만들기 : python3 -m venv myvenv myvenv 라는 가상환경을 생성
    • 명명법 : 소문자 / 공백은 없어야함 / 자주 입력해야 하므로 짧게
  • 가상환경 실행 명령어 : source myvenv/bin/activate
  • 가상환경에서 PIP로 장고 설치 : pip install django==1.8
  • 웹서버 실행 명령어 : python manage.py runserver
  • 장고 기본 Database : SQLite3
  • 파이썬 관습 : 던더(dunder; 더블 – 언더스코어 준말)
  • WSGI 프로토콜 : 파이썬을 이용한 웹사이트를 서비스 하기 위한 표준 (PythonAnywhere 에서 지원)

Django 프로젝트 생성

  • 가상환경에서 django-admin startproject mysite . 입력하여 장고 프로젝트 생성
  • 생성된 프로젝트의 초기 구조는
    djangogirls
    ├───manage.py
    └───mysite
          settings.py
          urls.py
          wsgi.py
          __init__.py
    
    • manage.py : 사이트 관리 및 웹 서버 실행
    • settings.py : 웹사이트 설정
    • urls.py : urlresolver 가 사용하는 패턴 목록 포함

Django 배포

  • Git 으로 커밋 및 배포
    [Local]
    - git status
    - git add --all .
    - git commit -m "First Commit."
    - git push
    
    [Server]
    - cd ~/my-first-blog
    - source myvenv/bin/activate
    - git pull
    - python manage.py collectstatic
    

Python 정규표현식

  • ^ : 문자열이 시작할 때
  • $ : 문자열이 끝날 때
  • \d : 숫자
  • + : 바로 앞에 나오는 항목이 계속 나올 때
  • () : 패턴의 부분을 저장할 때
  • 예를 들어, http://www.mysite.com/post/12345/ 와 같은 URL 이 있다고 하자.
  • 뷰마다 모든 글 번호를 작성하는 것은 현실적으로 힘들다.
  • 따라서 정규표현식 ^post/(\d+)/$. 을 이용하여 해당 글 번호를 가진 post 를 접근한다.
  • 위 정규표현식을 아래와 같이 부분 부분 나눠보면
    • ^post : url 시작점에 (오른쪽부터) post/ 가 있다.
    • (\d+) : 숫자가 1개 이상 있다.
    • / : / 뒤에 문자가 존재한다.
    • $ : URL 이 끝이 / 로 끝나야 매칭될 수 있다는 걸 의미
  • 파이썬에서 정규 표현식을 작성할 때는 항상 문자열 앞에 r을 붙입니다

Django View

  • 어플리케이션의 로직 을 넣는 곳
  • 모델의 정보를 받아와서 템플릿 에 전달하는 역할

템플릿 (HTML)

  • 서로 다른 정보를 일정한 형태로 표시할 수 있는 재사용이 가능한 파일
  • HTML : “Hyper Text Markup Language” 의 줄인 말.
  • 하이퍼텍스트(Hyper Text) : 페이지 간에 하이퍼링크를 담을 수 있는 텍스트
  • 마크업(Markup) : 브라우저 문서를 해석하는 표시

Django ORM & QuerySets

  • 쿼리셋 : 전달받은 모델의 객체 목록. 데이터베이스의 데이터를 읽어 필터 및 정렬 가능
  • 사용방법
    from blog.models import Post
    Post.objects.all()
    Post.objects.filter( title__contains = 'title' )
    
  • 쿼리셋은 연결(chaining) 도 가능
    Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
    

템플릿의 동적 데이터

  • from 다음의 마침표 .현재 디렉토리 또는 현재 어플리케이션 을 의미
    • from .models import Post : 현재 디렉토리의 models.py 를 접근 (.py 확장자는 붙이지 않아도 됌)
    from django.shortcuts import render
    from django.utils import timezone
    from .models import Post
    
    # `post_list` 라고 정의한 view 함수에서 QuerySet 으로 처리한 데이터를 `posts` 라는 변수로 받는다.
    def post_list(request):
    posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
    # QuerySet으로 받은 데이터를 posts 라는 이름의 변수에 태워 보낸다
    return render(request, 'blog/post_list.html', {'posts': posts})
    

Django 템플릿

  • 데이터 표현을 위해 template tags 라는 내장된 기능을 사용한다.
  • 장고 템플릿 태그(Django template tags) : 파이썬을 HTML로 바꿔주어, 빠르고 쉽게 동적인 웹사이트를 만들 수 있다.
  • 위에서 정의한 view 함수에서 전달받은 posts 를 다음과 같이 템플릿에 적용하면 동적 데이터를 다룰 수 있다.
    <div>
      <h1><a href="/">Django Girls Blog</a></h1>
    </div>
    
    {% for post in posts %}
      <div>
          published: {{ post.published_date }}
          <h1><a href="">{{ post.title }}</a></h1>
          {{ post.text|linebreaksbr }}
      </div>
    {% endfor %}
    

템플릿 확장하기

  • 서로 다른 페이지에서 HTML의 일부를 동일하게 재사용이 가능
  • post_list.html 에서 base.html 를 참조하는 방법
    <!-- base.html -->
    <div class="row">
      <div class="col-md-8">
      {% block content %}
      {% endblock %}
      </div>
    </div>
    
    <!-- post_list.html -->
    {% extends 'blog/base.html' %}
    
    {% block content %}
      {% for post in posts %}
          <div class="post">
              <div class="date">
                  {{ post.published_date }}
              </div>
              <h1><a href="">{{ post.title }}</a></h1>
              {{ post.text|linebreaksbr }}
          </div>
      {% endfor %}
    {% endblock content %}
    
Advertisements

Web App Manifest 소개 및 사용법

왜 주목 받는가?

  • App Store 가 생긴 이후로 웹 개발자들은 어떻게 하면 Web Application 을 Mobile Application 의 느낌으로 보이게 할지 연구해왔다.
  • Apple, Chrome, IE (Microsoft) 등이 Web App Manifest 파일을 차례로 도입하면서, Mobile Icon과 Launch Screen (Splash Screen) 등을 Mobile 에 등록할 수 있게 하면서 Web Application 이 점점 Mobile Application 의 형태를 띄게 된다.

구현? 사용법?

  • Web App 에 Native 느낌을 더하기 위해 Apple 이 추가한 몇 가지 태그는 아래와 같다.

11

  • 그리고 이후에 Google 이 다음과 같은 태그를 추가한다.

22

33

Building Manifest File

  • 메니페스트 파일은 JSON 파일과 비슷한 형태를 갖고 있다.
{
"name": "Super Racer 2000",
"short_name": "Racer2K",
"icons": [{
"src": "icon/lowres.png",
"sizes": "64x64",
"type": "image/webp"
}, {
"src": "icon/hd_small.png",
"sizes": "64x64"
}, {
"src": "icon/hd_hi.png",
"sizes": "128x128",
"density": 2
}],
"scope": "/racer/",
"start_url": "/racer/start.html",
"display": "fullscreen",
"orientation": "landscape"
}
  • 위의 속성들을 설명하자면,
  • name : icon 에 표시되는 이름
  • short_name : Web Application 이름의 짧은 버전. 공간이 충분하지 않아 full name 이 나올 수 없을 때 사용된다.
  • start_url : 실행시에 시작되는 URL 주소
  • display : 앱이 어떤식으로 실행될지 정하는 속성 (옵션 : fullscreen, minimul-ui, standalone, browser)
  • orientation : 웹 어플리케이션의 화면 방향을 정의 (옵션 : any, landscape, portrait, …)

Icon

  • 아이콘은 앱을 표시하기 위한 이미지
  • 아이콘에는 앱 표시에 사용되는 여러가지의 이미지들의 특성이 포함되어 있다.
  • src : 이미지 위치를 가리킨다
  • type : 아이콘 파일 유형을 정한다
  • sizes : 이미지 크기를 정한다
  • density : 기기의 pixel density 에 맞춰 어떤 아이콘이 사용될지 정한다. (지정하지 않을 경우 default 값은 1.0)

Scope & Navigation Scope

  • Navigate outside the app : 앱 유효범위의 밖으로 이동하려고 하면 (a 태그 클릭시) 새로운 브라우저를 실행한다.
  • Navigate into the app : deep linking 이라고 불린다. 매니페스트 파일 내의 유효범위에 있는 URL로 이동하면, 앱 밖으로 벗어나지 않는다. 웹 페이지 뿐만 아니라 네이티브 앱에서도 웹 앱을 여는 것이 가능하다.

Lesson & Learned

  • iOS 에서 즐겨찾기로 추가한 아이콘으로 웹 앱을 실행하면, 웹 내에서 href 태그 접근 시 새로운 브라우저를 띄우면서 scope 이 바뀐다.
  • 해결책 : <a href="#"> 없앤다.

Reference

Understanding the Manifest for Web App

Javascript Pattern 리터럴 & 생성자

이 글은 JavaScript Patterns Build Better Applications with Coding and Design Patterns 책을 학습 후 중요 부분을 요약한 것입니다.

객체 리터럴

  • new Object() 보다는 객체 리터럴 var obj = {}; 을 활용한다.
  • 아래와 같이 생성자에 어떤 인자를 받느냐에 따라 다른 객체가 생성된다.
    // 일반 객체
    var o = new Object();
    console.log(o.constructor === Object); // true
    
    // 숫자 객체
    var o = new Object(1);
    console.log(o.constructor === Number); // true
    console.log(o.toFixed(2)); // &amp;quot;1.00&amp;quot;
    
    // 스트링 객체
    var o = new Object(&amp;quot;I am a strong&amp;quot;);
    console.log(o.constructor === String); // true
    console.log(typeof o.substring); // &amp;quot;function&amp;quot;
    
    // boolean 객체
    var o = new Object(true);
    console.log(o.constructor === Boolean); // true
    
  • 위와 같은 이유로, Object() 생성자는 런타임시 결정되는 동적인 값이 생성자에 인자로 전달된 경우 예기치 않은 결과가 반환될 수 있다.

사용자 정의 생성자 함수

  • 내장 생성자가 아니라 직접 사용자가 정의한 생성자 함수 호출은 다음과 같다.

    var Person =  function (name) {
    this.name = name;
    this.say = function () {
      return &amp;quot;I am &amp;quot; + this.name;
    };
    };
    
  • 위의 함수는 사실 아래와 같다.
    var Person =  function (name) {
    // var this = {};
    
    this.name = name;
    this.say = function () {
      return &amp;quot;I am &amp;quot; + this.name;
    };
    
    // return this;
    };
    
  • 따라서, 생성자 함수 내부의 마지막에 다른 객체가 명시적으로 반환되지 않으면, 내부적으로 this 로 참조된 객체가 반환된다.

  • 한가지 더 유의할 점은, 이 Person 함수의 say() 메서드는 객체 생성시에 항상 default로 따라 붙기 때문에 메모리 관점에서 비효율적이다. 그렇기 때문에 아래와 같이

    Person.prototype.say = function() {
    return &amp;quot;I am &amp;quot; + this.name;
    }
    
  • 처럼 필요한 경우에 메서드 멤버는 prototype에 추가하여 사용하는 것이 더 효율적이다.

생성자의 new 강제 패턴

  • 생성자의 첫 글자를 대문자로 쓰는 명명규칙을 사용하여 해당 함수는 늘 생성자로 생성한다.
  • 아래와 같은 that 사용 으로 해당 함수를 생성자로만 사용할 수 있다.

    function Waffle () {
    var that = {};
    that.tastes = &amp;quot;yummy&amp;quot;;
    return that;
    }
    
  • 위처럼 that 을 사용하게 되면 생성자 함수를 new 로 호출하지 않더라도, 항상 생성자 함수로서 역할을 한다.
    var first = new Waffle(),
      second = Waffle();
    console.log(first.tastes); // &amp;quot;yummy&amp;quot;
    console.log(second.tastes); // &amp;quot;yummy&amp;quot;
    
  • 주의 : 위와 같은 that 패턴은 프로토타입에 추가한 멤버를 사용할 수 없게 된다

해결방법

  • 위와 같은 생성자 강제 new 패턴 문제점을 해결하려면 아래와 같은 방식을 사용한다.

    function Waffle () {
    
    if (!(this instanceof Waffle)) {
      return new Waffle();
    }
    
    this.tastes = &amp;quot;yummy&amp;quot;;
    }
    Waffle.prototype.wantAnother = true;
    

배열 리터럴

  • 배열 또한 var a = new Array("1", "2"); 와 같은 형식보다는 var a = [1,2]; 배열 리터럴을 이용한다.
  • 해당 객체가 배열인지 판별하려면 .isArray([]);Object.prototpe.toString()를 사용한다.

JSON

  • JSON : JavaScript Object Notation 의 줄임말이다.
    var json = {
    &amp;quot;name&amp;quot; : &amp;quot;value&amp;quot;,
    &amp;quot;some&amp;quot; : [1, 2, 3]
    };
    

정규표현식 리터럴

  • 정규표현식 생성방법은 다음과 같다.
    • var re = /\\/gm;
    • var re = new RegExp("\\\\", "gm");
  • 문법
    • g : 전역 매칭
    • m : 여러줄 매칭
    • i : 대소문자 구분없이 매칭
    • 여러개 패턴 동시 사용 가능 var re = /pattern/gmi;

원시 데이터 타입 래퍼

  • 자바스크립트는 Number, String, Boolean, null, undefiend 5개의 원시 데이터 타입이 존재한다.

요약

  • 객체 리터럴 표기법 : 이름 – 값 쌍을 쉼표로 분리하고, 괄호로 감싸 객체를 생성한다.
  • 생성자 함수 : 내장 생성자 함수와 사용자 정의 생성자, 내장 생성자는 리터럴 표기가 낫다.
  • 생성자 new 강제 패턴 : 생성자 함수가 항상 new 로 호출한 것처럼 보장하는 패턴
  • 배열 리터럴 표기법 : var a = ["1", "2"];
  • 정규표현식 리터럴 : var a = new RegExp() 보다는 var a = /\\gm/gim; 리터럴 방식을 사용한다.

Javascript Pattern 기초

이 글은
JavaScript Patterns Build Better Applications with Coding and Design Patterns 책을 학습 후 주요 부분을 요약한 것입니다.

Basics

  • 전역변수 사용 최소화
  • var 선언 1회
  • 루프내 length 캐쉬 사용
  • 코딩 규칙 준수

전역변수 사용 최소화

  • 네임스페이스 패턴 & 즉시 실행함수 사용
  • 아래와 같은 안티패턴은 피한다.
    function foo () {
    var a = b = 0;
    }
    

    위는 이렇게 바꿔야 한다.

    function foo () {
    var a, b;
    a = b = 0;
    }
    
  • 전역객체 접근방법
    var global = (function() {
    return this; // new 와 생성자를 이용하지 않고 호출하면 this는 항상 전역객체를 가리킨다.
    }());
    
  • 단일 var 패턴
    function func () {
    var a = 1,
        b = 2,
        sum = a + b,
        myobject = {},
        i,
    
        // 변수를 선언할 때는 항상 초기값을 같이 지정하는 습관을 들인다.
    }
    

    위와 같은 코드 기법은 코드 작성량과 전송량이 모두 줄어드는 이점이 있다.

For 루프

  • 아래의 코드는 루프 순회시마다 배열의 length에 접근하는 문제점이 있다 (일반적으로 length 접근은 비용이 크다)

    for (var i = 0; i &lt; array.length; i++) {
    //
    }
    

    위 코드를 아래와 같이 바꾼다.

    for (var i = 0, var max = array.length; i &lt; max; i++) {
    //
    }
    

    또한 이를 단일 var 패턴과 조합하면,

    function looper () {
    var i = 0,
        max,
        array = [];
    
    for (i = 0, max = array.length; i &lt; max; i++) {
    
    }
    }
    

    가 될 수 있다.

For-in 루프

  • 객체를 순회할 때 사용하는 함수

    var man = {
    hands: 2,
    legs: 2,
    heads: 1
    };
    
    if (typeof Object.prototype.clone === &quot;undefined&quot;) {
    Object.prototype.clone = function () {};
    }
    
  • 위 코드의 경우 for-in 루프를 사용할 때 주의할 점은
    // 안티패턴
    for (var variable in object) {
    console.log(i, &quot;:&quot;, man[i]);
    }
    // 콘솔 출력 내용
    // hands : 2
    // legs : 2
    // heads : 1
    // clone function()
    
  • 프로토타입 체인에 따라 상속받은 메서드를 의도치 않게 출력하였다. 따라서 이는 아래와 같이
    // 올바른 패턴
    for (var i in man) {
    if (man.hasOwnProperty(i)) {
      console.log(i, &quot;:&quot;, man[i]);
    }
    }
    // 콘솔 출력 내용
    // hands : 2
    // legs : 2
    // heads : 1
    

Switch

  • 아래의 패턴으로 가독성을 향상시킬 수 있다.
    var inspect_me = 0,
      result = '';
    
    switch (inspect_me) {
    case 0:
      result = &quot;zero&quot;;
      break; // 각 case문에 break 반드시 포함
    case 1:
      result = &quot;one&quot;;
      break;
    default: // switch 문 안에 default 는 반드시 포함
      result = &quot;unknown&quot;;
    }
    

들여쓰기

  • 중괄호 { } 의 안에 있으면 들여쓴다.
  • 중괄호 { 시작의 위치는 개발자마다 아래와 같이 2가지로 분류된다.
    // (1)
    if (true) {
    // body
    }
    // (2)
    if (true)
    {
    // body
    }
    
  • 위의 (2) 경우가 아래와 같은 문제를 만들 수 있다.
    function func () {
    return
    {
        name : &quot;Bat&quot;
    };
    } // 결과값 : undefined
    
  • 자바스크립트는 행 종료시 자동으로 세미콜론을 추가하기 때문에 위의 코드는 결국 아래와 같다.
    function func () {
    return undefined;
    {
        name : &quot;Bat&quot;
    };
    } // 결과값 : undefined
    
  • 결론 : 여는 중괄호 { 는 항상 선행하는 명령문과 동일한 라인에 두어야 한다.
    function func () {
    return {
        name : &quot;Bat&quot;
    };
    } // 결과값 : {name : &quot;Bat&quot;}
    

공백

  • 문어체 영어는 쉼표마침표 뒤에 공백을 둔다.
    // (1)
    for (var i = 0, max = 10; i &lt; max; i += 1) {
    
    }
    // (2)
    var a = [1, 2, 3];
    // (3)
    var o = {a: 1, b: 2};
    // (4)
    myFunc (a ,b ,c)
    // (5)
    function myFunc() {}
    // (6)
    var myFunc = function () {};
    
  • 공백을 많이 사용하여 코드의 가독성을 높이면, 코드의 Byte 양이 늘어나는 부작용이 있다.

  • 이는 compression을 이용하여 해결한다. (빌드용 & 배포용 나눌 것)

명명규칙

  • 생성자 함수의 첫 글자는 대문자로
  • 카멜 표기법 (camel case) : 각 단어의 첫 글자만 대문자 firstName
  • 언더스코어 표기법 (underscore) : 단어를 _로 잇는다. first_name

주석작성

  • 코드내 주석으로 API 문서를 자동화 해주는 툴은 jsdocyuidoc 이 있다.

코드압축

  • 압축도구는 아래와 같은 작업으로 코드 Byte 양을 줄인다.
    1. 공백, 줄바꿈, 주석 등을 제거한다.
    • 매개변수 길이 를 줄인다.
    • 전역변수 를 바꾸는 경우 코드를 망가뜨릴 수 있으므로, 보통 지역변수 를 바꾼다.
  • 미리부터 압축된 코드를 작성하려는 것은 잘못된 생각!!

요약

  • 전역변수 사용 최소화
  • 함수 내 var 선언은 1회
  • 내장 생성자의 프로토타입은 확장하지 X
  • 공백, 중괄호 규칙 준수
  • 명명 규칙 준수

크롬 확장프로그램 개발 II (Overview)

기본

  • 확장프로그램은 HTML / CSS / Javascript / Images / etc 파일들을 압축해서 묶어놓은 프로그램이다.
  • 브라우저가 웹 페이지에 제공하는 모든 API 를 웹 페이지와 동일하게 사용할 수 있다.
  • Content Scriptscross-origin XMLHttpRequests 를 이용하여 웹 페이지나 서버와 인터랙션 할 수 있다.
  • tabsbookmarks 처럼 브라우저 기능을 사용할 수도 있다.

Extension UI

  • browser action 또는 page action 형태로 확장 프로그램을 사용할 수 있다.
  • 확장 프로그램이 거의 모든 페이지에 연관이 있을 때는 browser action 을 사용
  • 확장 프로그램이 페이지에 따라 활성화 / 비활성화 해야될 때는 page action을 사용

Files

  • 모든 확장프로그램은 다음과 같은 파일들이 있어야 한다.
    • manifest file 1개
    • HTML files 1개 이상
    • Javascript (옵션)
    • 기타 파일 (옵션)
  • 확장 프로그램을 배포할 때는 .crx 라는 확장자 명의 zip 파일로 배포하기 때문에, 한개의 폴더에 잘 정리한다.

  • Chrome Developer Dashboard 를 이용하여 확장 프로그램을 Chrome App Store에 배포하려면 초기 등록비 $5 가 필요하다.

파일 참조

  • 일반 HTML 페이지와 같은 방식을 파일을 참조하면 된다. <img src="images/myimage.png">
  • 디버깅 시에는 확장프로그램의 모든 파일은 다음과 같은 경로로 접근이 가능하다 chrome-extension:///
  • chrome://extensions 에서 extensionIDpathToFile 에 대한 정보를 확인할 수 있다.

The Manifest File

  • 확장 프로그램에 관한 전반적인 정보를 갖고 있다. 간단한 예시는 다음과 같다.

    {
    "name": "My Extension",
    "version": "2.1",
    "description": "Gets information from Google.",
    "icons": { "128": "icon_128.png" },
    "background": {
      "persistent": false,
      "scripts": ["bg.js"]
    },
    "permissions": ["http://*.google.com/", "https://*.google.com/"],
    "browser_action": {
      "default_title": "",
      "default_icon": "icon_19.png",
      "default_popup": "popup.html"
    }
    }
    

Architecture

  • 대다수의 확장 프로그램은 주 로직을 포함하고 보이지 않는 background page 를 갖고 있다.
  • 확장 프로그램의 UI 를 나타내는 다른 페이지도 포함하고 있다.
  • 기존에 확장 프로그램에 포함된 페이지 이외에 사용자가 로딩하는 다른 페이지들을 조작하기 위해서는 content script가 있어야 한다.

The background page

  • 백그라운드 페이지에는 persistent backgroundevent 두 종류의 페이지가 있다.
  • persistent background 페이지 는 항상 열려 있다.
  • event 페이지 는 필요에 따라 페이지가 열고 닫힌다.
  • 항상 background 페이지를 사용해야 하는 상황이 아니면 가급적 event 페이지를 사용한다.

UI 페이지

  • 확장프로그램은 UI를 표시하는 일반 HTML 파일을 포함하고 있다.
  • 표시하는 방식에는 popup, options, override 등이 있다.
  • window.open()tabs.create 등을 이용하여 확장프로그램 안에 있는 다른 HTML 파일들도 표시할 수 있다.
  • 확장프로그램 안의 HTML 파일 간에는 서로의 DOM 접근이 가능하고, function 호출도 가능하다.

Content Scripts

  • content scripts : 브라우저에 로딩된 페이지의 컨텍스트에서 실행되는 javascript
  • 확장프로그램의 일부가 아니라 브라우저에 로딩된 페이지의 일부라고 생각하면 된다.
  • 확장프로그램을 이용하여 웹 페이지와 인터랙션할 때 사용한다.
  • 브라우저가 로딩하는 페이지의 정보들에 접근이 가능하고, 페이지를 변경할 수 있다.
  • 로딩된 페이지의 DOM 조작은 가능하나, 확장프로그램의 background 페이지의 DOM 조작은 불가능하다.
  • 한가지 주의할 점은, content script확장프로그램 간의 소통이 아예 안되는 것은 아니다. 예를 들어, 브라우저 페이지에서 RSS feed를 찾을 떄 마다 content script에서 메시지를 보낸다고 하자. 그러면, background 페이지에서 content script 에 브라우저 페이지 모양을 바꿀지 묻는게 가능하다.

Chrome.* API 사용하기

  • 웹 페이지와 앱이 사용할 수 있는 모든 API 뿐만 아니라, chrome.* APIs 라고 불리는 크롬 전용 API에도 접근이 가능하다.
  • 예를 들어, window.open() 등의 웹앱 API 사용시에, 어느 탭을 열지 구체적으로 정하고 싶다면 Chrome의 tabs.create API를 사용할 수 있다.
  • 대부분의 chrome.* API비동기 방식이다. string chrome.runtime.getURL() 같은 일부 API는 동기 방식이다.

페이지 간의 통신

  • 페이지 간의 통신이 필요한 경우, 확장프로그램 안의 모든 페이지는 같은 쓰레드의 같은 프로세스에서 실행되기 때문에 상호 페이지 간에 직접 함수 호출이 가능하다.
  • getViews()getBackgroundPage() 같은 크롬 API로 해당 페이지를 참조하고 나면, 자유롭게 호출이 가능하다.

데이터 저장과 익명(incognito) 모드

  • 확장프로그램은 storage API, HTML5 web storage API 등을 이용하여 데이터 저장이 가능하다.
  • 데이터 저장시에 확장프로그램은 디폴트 값으로 익명 윈도우를 사용하지 않는다는 것을 고려해야 한다.
  • 익명 모드를 확인하는 샘플코드는 다음과 같다.
    function saveTabData(tab, data) {
    if (tab.incognito) {
      chrome.runtime.getBackgroundPage(function(bgPage) {
        bgPage[tab.url] = data;      // Persist data ONLY in memory
      });
    } else {
      localStorage[tab.url] = data;  // OK to store data
    }
    }
    

크롬 확장프로그램 개발 I (Intro)

개요

  • browser action 을 이용해서 URL 주소창 옆의 확장 프로그램 아이콘을 생성할 수 있다.
  • 확장 프로그램을 만들기 위해서는 manifest.json 파일이 필요한데, 이 매니페스트 파일 안에 확장 프로그램의 이름, 설명, 버전 정보 등을 설정할 수 있다.

Browser Action

  • Google Chrome Toolbar 에 아이콘을 등록하고 아이콘에 tooltip, badge, popup 같은 동작을 추가할 수 있다.
  • "browser_action": {...}
  • manifest.json 파일을 다음과 같이 등록할 수 있다.
    {
     &quot;name&quot;: &quot;My extension&quot;,
     ...
     &quot;browser_action&quot;: {
       &quot;default_icon&quot;: {                    // optional
         &quot;19&quot;: &quot;images/icon19.png&quot;,           // optional
         &quot;38&quot;: &quot;images/icon38.png&quot;            // optional
       },
       &quot;default_title&quot;: &quot;Google Mail&quot;,      // optional; shown in tooltip
       &quot;default_popup&quot;: &quot;popup.html&quot;        // optional
     },
     ...
    }
    

Page Action

  • Browser Action 과 마찬가지로 주소창 옆에 아이콘을 추가할 수 있다.
  • 현재 페이지에서만 작동할 수 있는 동작들을 정의한다. (모든 페이지에 적용되지 않음)

Manifest File Format

  • 매니페스트 파일은 아래와 같은 JSON 형태로 구현한다.
    {
    // Required
    &quot;manifest_version&quot;: 2,
    &quot;name&quot;: &quot;My Extension&quot;,
    &quot;version&quot;: &quot;versionString&quot;,
    
    // Recommended
    &quot;default_locale&quot;: &quot;en&quot;,
    &quot;description&quot;: &quot;A plain text description&quot;,
    &quot;icons&quot;: {...},
    
    // Pick one (or none)
    &quot;browser_action&quot;: {...},
    &quot;page_action&quot;: {...},
    
    // Optional
    &quot;author&quot;: ...,
    &quot;automation&quot;: ...,
    &quot;background&quot;: {
      // Recommended
      &quot;persistent&quot;: false
    },
    &quot;background_page&quot;: ...,
    &quot;chrome_settings_overrides&quot;: {...},
    &quot;chrome_ui_overrides&quot;: {
      &quot;bookmarks_ui&quot;: {
        &quot;remove_bookmark_shortcut&quot;: true,
        &quot;remove_button&quot;: true
      }
    },
    &quot;chrome_url_overrides&quot;: {...},
    &quot;commands&quot;: {...},
    &quot;content_capabilities&quot;: ...,
    &quot;content_scripts&quot;: [{...}],
    &quot;content_security_policy&quot;: &quot;policyString&quot;,
    &quot;converted_from_user_script&quot;: ...,
    &quot;copresence&quot;: ...,
    &quot;current_locale&quot;: ...,
    &quot;devtools_page&quot;: &quot;devtools.html&quot;,
    &quot;event_rules&quot;: [{...}],
    &quot;externally_connectable&quot;: {
      &quot;matches&quot;: [&quot;*://*.example.com/*&quot;]
    },
    &quot;file_browser_handlers&quot;: [...],
    &quot;file_system_provider_capabilities&quot;: {
      &quot;configurable&quot;: true,
      &quot;multiple_mounts&quot;: true,
      &quot;source&quot;: &quot;network&quot;
    },
    &quot;homepage_url&quot;: &quot;http://path/to/homepage&quot;,
    &quot;import&quot;: [{&quot;id&quot;: &quot;aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&quot;}],
    &quot;incognito&quot;: &quot;spanning or split&quot;,
    &quot;input_components&quot;: ...,
    &quot;key&quot;: &quot;publicKey&quot;,
    &quot;minimum_chrome_version&quot;: &quot;versionString&quot;,
    &quot;nacl_modules&quot;: [...],
    &quot;oauth2&quot;: ...,
    &quot;offline_enabled&quot;: true,
    &quot;omnibox&quot;: {
      &quot;keyword&quot;: &quot;aString&quot;
    },
    &quot;optional_permissions&quot;: [&quot;tabs&quot;],
    &quot;options_page&quot;: &quot;options.html&quot;,
    &quot;options_ui&quot;: {
      &quot;chrome_style&quot;: true,
      &quot;page&quot;: &quot;options.html&quot;
    },
    &quot;permissions&quot;: [&quot;tabs&quot;],
    &quot;platforms&quot;: ...,
    &quot;plugins&quot;: [...],
    &quot;requirements&quot;: {...},
    &quot;sandbox&quot;: [...],
    &quot;short_name&quot;: &quot;Short Name&quot;,
    &quot;signature&quot;: ...,
    &quot;spellcheck&quot;: ...,
    &quot;storage&quot;: {
      &quot;managed_schema&quot;: &quot;schema.json&quot;
    },
    &quot;system_indicator&quot;: ...,
    &quot;tts_engine&quot;: {...},
    &quot;update_url&quot;: &quot;http://path/to/updateInfo.xml&quot;,
    &quot;version_name&quot;: &quot;aString&quot;,
    &quot;web_accessible_resources&quot;: [...]
    }
    

    참고

Chrome V8 엔진의 자바스크립트 처리 성능 향상 기법

개요

  • 웹 어플리케이션 성능 튜닝을 위한 기본적인 절차
    1. 문제가 발생하기 전에 준비
    2. 문제 발생시 식별하고 이해
    3. 문제를 해결
  • V8 엔진이 어떻게 JS를 최적화하는지 이해하는 것이 가장 중요하다.

Hidden Classes

  • Javascript는 컴파일시에 사용하는 타입정보에 대해 제한적이다.
  • Javascript는 런타임시에 데이터 타입을 변경할 수 있다.
  • V8은 런타임시에 객체 처리를 위해 내부적으로 hidden class를 만들어서 사용한다.
  function Point(x, y) {
    this.x = x;
    this.y = y;
  }

  var p1 = new Point(11, 22);
  var p2 = new Point(33, 44);
  // 여기에서 p1과 p2는 hidden class를 공유합니다.
  p2.z = 55;
  // 경고! p1과 p2는 이제 다른 hidden class를 갖습니다.
  • p2에서 z속성을 추가하기 전까지는 같은 hidden class 를 갖지만, z가 추가되고 나면 다른 hidden class를 갖기 때문에 성능에 악영향을 미친다.
  • [결론]
    • 모든 객체 멤버를 생성자 함수 안에서 초기화 (나중에 멤버 타입이 변하지 않음)
    • 항상 같은 순서로 멤버를 초기화

Numbers

  • V8은 데이터 타입 변환시 값을 효율적으로 나타내는 태그 사용
  • 사용자가 사용하는 값을 통해서 number타입 추론
  • 데이터 타입은 동적으로 변할 수 있기 때문에, 효율적으로 값을 나타내는 태그 사용
  • number타입을 지속적으로 쓰는 것이 중요
  var i = 42;  // 31비트 부호있는 정수입니다.
  var j = 4.2;  // 이 값은 double 타입의 부동 소수점 숫자 데이터입니다.
  • [결론]
    • 31비트 부호있는 정수를 사용

Normal Arrays

  • 큰 배열 처리를 위해 두가지 유형의 내부 배열 저장소가 존재한다.
    • Fast Elements : 키 값이 순서대로 채워진 경우 사용되는 선형 저장소
    • Dictionary Elements : 위 경우가 아닐 때 사용하는 해쉬 테이블 저장소
  • 배열 저장소가 한 유형에서 다른 유형으로 변경되지 않는것이 중요
  • [결론]
    • 인덱스 0부터 시작하는 연속키 사용
    • 배열 선언시 최대사이즈를 할당하지 말고 ( > 64K 원소), 사용하면서 크기를 늘려간다
    • 숫자 배열의 요소를 삭제하지 않는다
    • 초기화 안한 요소는 호출하지 않는다 (아래 코드 참조)
  a = new Array();
  for (var b = 0; b &lt; 10; b++) {
    a[0] |= b;  // 안 좋아요!
  }
  //vs.
  a = new Array();
  a[0] = 0;
  for (var b = 0; b &lt; 10; b++) {
    a[0] |= b;  // 훨씬 좋습니다. 2배 더 빨라요.
  }

Double Arrays

  • double 타입 배열이 일반 배열보다 빠른 이유 : 배열의 hidden class는 일반적으로 요소의 타입을 검사하여 hidden class를 변경하는 작업이 있으나 double의 경우 여기서 제외된다.
  • 아래와 같이 부주의한 배열요소 변경은 할당과 변환이라는 추가 작업들을 만든다.
  // 비효율적인 코드
  var a = new Array();
  a[0] = 77;   // 할당
  a[1] = 88;
  a[2] = 0.5;   // 할당, 배열 타입 변환 (일반배열 -&gt; double 배열)
  a[3] = true; // 할당, 배열 타입 변환 (double 배열 -&gt; 일반배열)

  // 효율적인 코드
  var a = [77, 88, 0.5, true];
  • 일반 배열 선언후 a[2] 요소에 double 형태를 할당하면, 일반 배열에서 double 배열 형태의 배열 형태가 바뀐다.
  • 그리고 나서 a[3] 요소에 다시 일반 배열을 할당하면, double 배열에서 일반배열로 다시 전환된다.
  • 이런 추가 작업들이 결국 성능에 영향을 미치게 되기 때문에 var a = [77, 88, 0.5, true]; 형태로 한번에 선언하면 컴파일러가 요소의 타입을 모두 알고 hidden class를 미리 결정할 수 있다.
  • 따라서 중간에 배열의 타입을 바꾸는데 생기는 불필요한 전환 작업들을 줄일 수 있다.
  • [결론]
    • 초기화시 배열 리터럴을 이용하여 배열 크기를 작게 고정
    • 사용하기 전 크기에 맞게 배열 사이즈를 할당 – 배열의 크기가 작으면 작은 크기의 배열에 (<64K) 에 할당
    • 숫자 배열에 비숫자 값 사용 자제
    • 리터럴 사용하지 않고 배열 초기화 할 때 작은 배열의 재변환 되지 않도록 주의

Javascript 컴파일

  • Javascript 는 동적인 언어이고 원래는 인터프리터 방식으로 구현
  • 최근 Javascript 런타임 엔진은 컴파일을 이용
  • V8 (Chrome browser's Javascript Engine) 에는 두가지 Just-In-Time(JIT) 컴파일러가 있다.
    • Full Compiler : 일반적인 Javascript를 좋은 코드로 변환
    • Optimizing Compiler : 대부분의 Javascript들을 뛰어난 코드로 변환하지만 시간 더 오래걸린다

The Full Compiler

  • 모든 코드에서 동작하고, 최대한 빨리 코드를 실행시키지만 뛰어난 코드보다는 일반적으로 좋은 코드들을 만든다.
  • 컴파일 시점에서 데이터 타입에 대한 가정을 하지 않는다 (변수의 데이터 타입이 런타임시에 변경된다고 간주)
  • Full Compiler 가 생성한 코드는 인라인 캐시 (ICs)를 사용하여 프로그램 실행시에 타입에 대한 정보를 구체화한다.
  • 인라인 캐시는 타입 의존적인 코드들을 캐싱하여 타입을 효과적으로 처리한다.
  • 코드가 실행시에, 타입 추정이 유효한지 확인한다. 그 후 인라인 캐시를 이용하여 동작을 단순화 한다.
  • 여러 데이터 타입 처리시에는 성능이 떨어질 수 있음.
  • [결론]
    • 다형적(polymorphic)연산 보다는 단형적(monomorphic)연산을 사용한다.
    • 단형적 연산 : hidden class 가 항상 같다.
    • 다형적 연산 : 그렇지 않고 값이 변한다.
  • 아래 예제를 확인해보자

  function add(x, y) {
    return x + y;
  }

  add(1, 2);      // add 함수의 더하기는 단형적 연산입니다.
  add(&quot;a&quot;, &quot;b&quot;);  // add 함수의 더하기는 다형적 연산이 됩니다.
  • 위의 x, y를 보면 첫번째 add(1,2);기본 number 타입에서 두번째 add(&quot;a&quot;,&quot;b&quot;);에서 일반 object 타입으로 변한다.
  • 이 경우 같은 함수에 대한 두번의 호출이 동일한 hidden class를 쓰는 것이 아니고, 달라지기 때문에 이건 다형적 연산이 된다.

The Optimizing Compiler

  • Full Compiler와는 병렬로 처리된다.
  • V8 엔진이 hot function(자주 실행되는 함수)을 Optimizing Compiler로 재컴파일 한다.
  • 타입 피드백을 이용하여 컴파일된 코드를 빠르게 한다. (인라인캐쉬 (ICs)에서 얻은 타입을 사용한다.)
  • 컴파일러 연산은 추론을 통해서 인라인 된다. (호출된 곳에 코드 위치)
  • 이렇게 해서 실행속도를 빠르게 하고 다른 최적화도 가능하다.
  • 단형적 함수와 생성자들은 완전히 인라인 될 수 있다 (단형적 연산이 V8에서 좋은 이유)
  • 최적화 컴파일러는 현재 try {} catch {} 구문을 처리하지 않는다.
  • 최적화 코드 로그 보려면 V8 엔진에서 d8 --trace-opt primes.js 플래그 사용
  • [결론]
    • try {} catch {} 사용시 성능에 민감한 코드는 아래 예제처럼 내장함수에 적용한다.
  function perf_sensitive() {
    // Do performance-sensitive work here
  }

  try {
    perf_sensitive()
  } catch (e) {
    // Handle exceptions here
  }

De-optimization

  • 이 컴파일러는 추론을 이용하여 최적화를 한다.
  • 최적화된 코드를 버리고, Full Compiler 코드의 맞는 위치에서 실행을 재개
  • 최적화된 코드의 hidden class 를 변경시에 역최적화가 일어난다.
  • 역최적화 코드 보려면 V8에서 d8 --trace-deopt primes.js 플래그 사용
  • [결론]
    • 함수가 최적화 된 이후에 hidden class 변경을 자제한다