ECMAScript 6 에서 추가되는 많은 새로운 기능들이 기대가 되면서도 아직까지 직접 사용해보지 못했었다. 최근에 JavaScript 관련 컨퍼런스 영상 뿐만 아니라 대부분의 포스트도 최신 문법으로 작성되는 경우가 많아 살펴보게 되었다.

ES5 표준은 2009년에 표준화되어 점진적으로 반영되고 있지만 ECMAScript 6는 2015년 6월 승인을 목표로 작성되고 있는 새 ECMAScript 표준이다. Prototype 기반의 객체 지향 패턴을 쉽게 사용할 수 있도록 돕는 class의 추가, => 화살표 함수 표현, 템플릿 문자열, generatoryield 등 다른 언어에서 편리하게 사용하던 많은 기능들이 추가될 예정이다.

현재 나와있는 JS 엔진에는 극히 일부만 실험적으로 적용되어 있어서 실제로 사용하게 될 시점은 까마득한 미래와 같이 느껴진다. 하지만 현재에도 기존 JavaScript와 다른 문법을 사용할 수 있도록 돕는 transform compiler가 존재한다.

TypeScript, CoffeeScript는 JavaScript 문법이 아닌 각각의 문법으로 작성된 코드를 JavaScript에서 동작 가능한 코드로 변환한다. 이와 같은 원리로 ECMAScript 6 문법으로 작성된 파일을 변환-컴파일하는 구현이 존재한다. 이 포스트에서 소개하려는 라이브러리, babel이 바로 그 transcompiler 중 하나다.

Babel 사용하기

다른 라이브러리와 같이 npm으로 설치 가능하다.

$ npm install --global babel

ES6로 작성한 파일로 js 컴파일한 결과를 확인하려면 다음 명령어를 사용할 수 있다.

$ babel script.js

파일로 저장하기 위해 --out-file, 변경할 때마다 저장하도록 하려면 --watch 플래그를 활용할 수 있다. 파일 대신 경로도 사용할 수 있다.

$ babel ./src --watch --out-file script-compiled.js

babel을 설치하면 node.js의 CLI와 같이 사용할 수 있는 babel-node 라는 CLI를 제공한다. node처럼 REPL나 직접 파일을 실행할 때 사용할 수 있다. 직접 실행해서 확인할 때 편리하다.

$ babel-node # REPL 실행 시
$ babel-node app.js

자세한 내용은 babel CLI 문서에서 확인할 수 있다.

다른 도구와 함께 Babel 사용하기

Babel은 다양한 usage에 대한 예시를 제공하고 있다. Babel의 Using Babel을 확인하면 현재 사용하고 있는 도구에 쉽게 접목할 수 있다.

Meteor는 다음 패키지를 설치하면 바로 사용할 수 있다. 이 패키지를 설치하면 .es6.js, .es6, .es, .jsx 파일을 자동으로 컴파일 한다.

$ meteor add grigio:babel

Webpack을 사용하고 있다면 babel-loader를 설치한 후 webpack.config.js에 해당 loader를 사용하도록 설정하면 끝난다.

Webpack을 사용해보지 않았다면 다음 순서대로 시작할 수 있다. Webpack은 모듈을 하나의 파일로 묶어주는 module bundler의 역할을 하는 도구다. 먼저 CLI를 설치한다.

$ npm install --global webpack

프로젝트에서 babel을 사용할 수 있도록 babel-loader를 추가한다.

$ npm install babel-loader --save-dev

webpack.config.js 파일을 다음과 같이 작성한다.

module.exports = {
  entry: "./app.js",
  output: {
    path: __dirname,
    filename: "bundle.js"
  },
  module: {
    loaders: [
      { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" }
    ]
  }
}

위 설정은 node_modules 디렉토리를 제외한, 프로젝트 내에 있는 모든 *.js를 babel로 변환 후 묶어준다. 각각 세부적인 옵션은 webpack 문서에서 살펴볼 수 있다.


매번 비슷하면서도 전혀 새로운 라이브러리가 많이 나와 때로는 따라가기 버겁다는 생각이 들 때도 있지만 찬찬히 들여다보면 그 새로움에 자극을 받게 된다. (부지런한 사람들 같으니!) 다음 세대 ECMAScript를 준비하는 마음으로 새로운 문법도 꼼꼼히 봐야겠다. Babel, Webpack 등 최근 나오는 라이브러리는 문서화가 잘 되어있는 편이라 금방 배우기 쉬운 편이니 각 문서를 확인해보자.

더 읽을 거리

지난 2월에 한국에서 구입했는데 다시 호주로 오기 전까지 배송이 안되서 결국 들고 오질 못했었는데 집에서 택배와 함께 보내줘서 이제야 개봉해보고 살펴보게 되었다.

라즈베리 파이는 영국의 Rasberry Pi Foundation에서 교육 용도로 개발한, 신용카드 사이즈의 컴퓨터로 USD 35달러라는 저렴한 가격에 판매하고 있다. 교육용 외에도 다양한 분야에서 활용하고 있어 보급형 개발보드의 새로운 세계를 열었다는 좋은 평을 받고 있다.

raspberry pi 2 model B

구입한 라즈베리 파이 2 Model B는 2015년 2월에 출시한 보드로 전 모델에 비해 업그레이드 된 모델이다. 사양은 다음과 같다.

  • Quad core broadcom BCM2836 CPU with 1GB RAM
  • 40pin extended GPIO
  • Micro SD slot
  • Multiple Ports: 4 USB ports, Full size HDMI, 4 pole Stereo output and Composite video port, CSI camera port & DSI display port
  • Micro USB power source

라즈베리파이 웹사이트 문서를 참고해 설치했는데 교육용 개발보드답게 문서화가 아주 잘되어 있어서 환경 구축에 큰 어려움이 없었다.

라즈베리파이 자체로는 사실 할 수 있는게 없어 몇 가지 부속이 더 필요하다. (요리사가 집 냉장고에서 대충 재료를 꺼내는 느낌으로) 내 부품 잡동사니에서 다음과 같은 부품을 찾았다. 다 가지고 있으면 본체만 구입하면 되겠지만 없다면 Starter Kit 같은 것도 판매하고 있다.

라즈베리 파이 2 Starter Kit 구입 링크

  • 8GB Micro SD, Micro SD reader (SKY라고 써져 있는데 이제 더이상 볼 수 없는 회사가 되어버렸다…)
  • iptime wifi USB (호주 처음 올 떄 가져왔으니 적어도 5년은 된 동글)
  • Micro USB 케이블
  • 흔한 이더넷 케이블 (랜선이라 부르는)
  • 애플 USB 충전기

다음은 라즈베리파이 웹사이트 문서에서 확인할 수 있는 내용으로 자세하게 보고 싶다면 문서를 참고하자. 이미지 세팅에는 mac 환경에서, 그 외에는 terminal SSH를 통해 라즈베리 파이에 접속해서 진행했다.

이미지 설치하기

먼저 사용하고 싶은 이미지를 라즈베리파이 웹사이트 다운로드 페이지에서 내려 받는다. 여기서는 Raspbian 이미지를 사용했다. zip 파일 압축을 해제하면 img 파일이 나오는데 이 이미지를 micro SD에 풀어준다. 그 img를 그대로 넣는게 아니라 명령어를 사용해서 넣어야 한다.

먼저 Micro SD를 SD 카드 어뎁터나 micro SD 리더기 등을 통해 맥에 연결한다. 그리고 터미널을 열어 diskutil list로 어느 경로로 마운트 되었는지 확인한다.

$ diskutil list
/dev/disk0
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *1.0 TB     disk0
   1:                        EFI EFI                     209.7 MB   disk0s1
   2:          Apple_CoreStorage                         999.7 GB   disk0s2
   3:                 Apple_Boot Recovery HD             650.0 MB   disk0s3
/dev/disk1
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:                  Apple_HFS Macintosh HD           *999.4 GB   disk1
                                 Logical Volume on disk0s2
                                 Unencrypted
/dev/disk2
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *2.0 TB     disk2
   1:               Windows_NTFS My Passport             2.0 TB     disk2s1
/dev/disk3
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *8.1 GB     disk3
   1:             Windows_FAT_32 Sky                     8.1 GB     disk3s1

내 경우는 /dev/disk3 인 것을 확인할 수 있었다. 이제 마운트되어 있는 디스크를 언마운트해야 한다.

$ diskutil unmountDisk /dev/<disk# from diskutil>
# 본인의 disk 번호를 확인 후 그 값을 넣어야 한다
# 예를 들면 diskutil unmountDisk /dev/disk3

그리고 이미지가 있는 경로에서 다음 dd 명령어로 이미지를 넣어준다.

sudo dd bs=1m if=/path/to/image.img of=/dev/<rdisk# from diskutil>
# 경로를 상황에 맞게 수정해야 한다
# 내 경우는 sudo dd bs=1m if=~/Downloads/2015-05-05-raspbian-wheezy.img of=/dev/rdisk3

원래 문서에서는 of=/dev/disk3로 입력하도록 하는데 disk는 버퍼로 인해 속도가 느리다고 한다. disk로 한참 기다려도 생각보다 오래 걸려 찾아보니 맥에서 disk는 정말 느리므로 rdisk를 활용하라는 글을 찾을 수 있었다. 입력이 다 끝났으면 micro SD를 라즈베리 파이에 연결한다.

라즈베리 파이 접속하기

모니터가 있으면 더 편하게 했겠지만 본체만 가지고도 충분히 기본 설정이 가능하게 잘 구성되어 있다. 라즈베리 파이에 Micro SD, 무선 랜카드, 그리고 초기 설정을 위해 사용할 이더넷 케이블(랜선)을 연결한다. 케이블 한쪽은 라즈베리 파이 본체에 반대쪽은 맥에 직접 연결(Direct Connection)한다. 그러고 나서 전원 usb를 꼽으면 초록색 빨간색 LED가 빤짝이며 부팅된다. 빨간색이 오래 켜져있으면 부팅이 완료된 것이다.

맥에서 터미널을 열어 ssh로 로그인한다.

$ ssh pi@raspberrypi.local
# password는 raspberry

기본적인 메시지와 함께 쉘이 뜨는 것을 확인할 수 있다. 다음 명령어로 환경 설정을 시작한다.

$ sudo raspi-config

1번으로 파일 시스템을 재설정하고 2번으로 사용자 비밀번호를 변경한다. 변경이 완료되었으면 Tab을 누르면 Finish를 선택할 수 있다.

wifi 설정하기

라즈베리 파이를 유선랜으로 연결해두는 환경이면 좋겠지만 내 방은 공유기랑 멀어서 wifi를 기본적으로 사용하도록 설정해야 한다. 내 wifi usb 동글은 충분히 낡아서 그런지 드라이버 설정 없이 바로 인식했다. 먼저 스캔을 한다.

$ sudo iwlist wlan0 scan

이제 랜카드가 스캔한 AP가 모두 출력되는데 연결하고자 하는 SSID를 확인하자. 그리고 /etc/wpa_supplicant/wpa_supplicant.conf에 ssid와 AP 접속할 때 사용하는 비밀번호를 입력한다. vim은 없고 vi가 있는데 nano를 써도 무관하다. 취향에 따라 사용하고 여기서는 nano로 적어둔다.

$ sudo nano /etc/wpa_supplicant/wpa_supplicant.conf

이제 내용을 입력할 수 있다. ssid에는 AP명, psk에는 AP 비밀번호를 입력한다.

network={
    ssid="HARUAIR_AP"
    psk="goawayfreerider"
}

입력을 완료했으면 ctrl + x, y 그리고 엔터를 누른다. 이러면 자동으로 변경을 확인해서 wifi에 연결된다. 안되면 무선랜을 sudo ifdown wlan0으로 내렸다가 sudo ifup wlan0으로 다시 켜준다. 인터넷 연결 확인은 간단하게 ping을 활용할 수 있다. ping google.com을 입력했을 때 resolving host problem 같은게 나지 않고 정상적으로 핑을 주고 받으면 연결이 된 상태다. 이제 vim을 설치할 수 있다. 😀

wlan0이 연결이 되었으면 이제 이더넷 케이블은 제거해도 된다.

라즈베리파이 기본 호스트명 변경하기

만약 여러대의 라즈베리파이를 사용할 예정이라면 편의를 위해 호스트명을 변경해두는 것이 편리하다. 위에서 환경설정에 사용했던 raspi-config에서 8 Advanced Options를 선택하면 A2 Hostname 항목에서 변경할 수 있다. 다음은 이 간편한 방법을 두고 복잡한 과정으로 변경하는 방법이다.

두 군데를 수정하면 되는데 /etc/hostname 파일과 /etc/hosts 파일이다. 각각 파일의 역할이 궁금하면 구글에서 찾아보자.

$ sudo nano /etc/hostname

rassberrypi를 원하는 호스트명으로 변경한다. (변경 후에 저장하려면 ctrl + x, y 그리고 엔터.)

$ sudo nano /etc/hosts

가장 마지막 줄에 있는 127.0.1.1 rassberrypi에서 뒷 호스트명을 변경한 호스트명으로 교체하고 저장한다. 이제 다음 명령어로 변경사항을 반영하고 재부팅한다.

$ sudo /etc/init.d/hostname.sh
$ sudo reboot

재부팅하면 변경된 호스트명으로 접속할 수 있다.

new hostname

라즈베리 파이를 직접 사용해보니 그냥 일반 컴퓨터와 크게 다르지 않았다. 사양도 높은 편이라서 하드만 연결하면 NAS 용도로 충분히 사용할 수 있을 정도인데 왜 좋은 평을 받는지 충분히 이해가 되었다. 최근 IoT의 열풍도 그냥 나타난 것이 아니라 이런 작고 강력한 하드웨어의 든든한 지원 덕분이라는 것을 실감할 수 있었다.

어떻게 활용하면 오래, 재미있게 가지고 놀 수 있을지 생각해봤는데 요즘 Azure에서 IoT와 관련해 여러 서비스가 나오고 있어서 센서를 구입해 이 서비스를 이용해볼 생각이고 그리고 손으로 만질 수 있는 개발에 대한 로망이 있어 확장 실드와 모터를 구입해 servo control을 해보려고 한다. 웹과 연동되는 장난감을 만들 생각에 벌써 신난다. 😀

PHP에서 데이터를 json 문자열로 변환할 때 json_encode(mixed $value) 함수를 사용하게 된다. 이 함수를 이용해 개체를 변환할 때에도 활용할 수 있다. 기본적으로는 클래스에서 public인 프로퍼티에 대해서만 json으로 반환한다. protected나 private, 또는 데이터를 가공해 json으로 반환해야 한다면 해당 클래스에서 JsonSerializable 인터페이스를 구성해 어떤 형태로 변환할 것인지 정의할 수 있다. 이 인터페이스는 PHP 5.4.0 이상, PHP 7 에서 지원하고 있다.

<?php
class Student {
    public $first_name;
    public $last_name;
    protected $school;

    public function __construct($first_name, $last_name, $school) {
        $this->first_name = $first_name;
        $this->last_name = $last_name;
        $this->school = $school;
    }
}

$haruair = new Student("Edward", "Kim", "WeirdSchool");
print $haruair;


// result :
// {
//   "first_name": "Edward",
//   "last_name": "Kim"
// }
?>

public 프로퍼티만 필요로 한 경우라면 별도의 인터페이스 구성 없이도 사용할 수 있다. 다만 대부분의 라이브러리에서 protected 또는 private으로 프로퍼티를 작성하고 __get(), __set() 매직 메소드를 구현해 사용하고 있고 또 권장하고 있기 때문에 그런 경우엔 다음과 같이 JsonSerializable 인터페이스를 활용할 수 있다.

<?php
class Student implements JsonSerializable {
    public $first_name;
    public $last_name;
    protected $school;

    public function __construct($first_name, $last_name, $school) {
        $this->first_name = $first_name;
        $this->last_name = $last_name;
        $this->school = $school;
    }

    public function jsonSerialize() {
        return [
            'full_name' => "{$this->first_name}, {$this->last_name}",
            'first_name' => $this->first_name,
            'last_name' => $this->last_name,
            'school' => $this->school,
        ];
    }
}

$haruair = new Student("Edward", "Kim", array("Jeju Univ", "Weird School"));
print $haruair;


// result :
// {
//   "full_name": "Edward, Kim",
//   "first_name": "Edward",
//   "last_name": "Kim",
//   "school": [
//       "Jeju Univ",
//       "Weird School"
//   ]
// }
?>

변환하는 과정에서 json 문자열을 추가하거나 데이터의 구조를 변환하는 등 다양한 형태로 활용할 수 있다.

더 읽을 거리

Micro-framework의 전성기라고 할 만큼 다양한 환경과 언어로 프레임워크가 쏟아지고 있다. PHP에도 micro-framework가 많이 나와 있는데1 최근 Laravel에서 Lumen을 발표했다. 발표 자료에서는 symfony2 기반인 silex보다 1.9배 빠르다고 하는데 문법적으로는 Silm과 상당히 유사한 느낌도 든다. 기존에 나왔던 프레임워크와 엄청나게 큰 구조 차이를 가지고 있는 것은 아니지만 Laravel과의 호환을 염두한 부분도 많다는 느낌을 받았다. 또한 구조적으로도 silex나 여타 기존에 나온 micro-framework 보다 훨씬 깔끔하고 미려하다는 느낌을 받았다.

lumen logo

이 포스트는 lumen 문서에서 쉽게 볼 수 있는 부분만 다뤘고 더 깊은 내용을 보고 싶다면 Lumen 공식 문서를 보는게 도움이 된다. Lumen는 PHP >= 5.4를 요구하며 Mcrypt, OpenSSL, mbstring, tokenizer 확장을 필요로 한다.

Lumen 설치

lumen을 설치하기 위해서는 composer가 설치되어 있어야 한다.

lumen을 사용해 프로젝트를 시작하는 방법은 lumen installer를 사용하는 방법과 composer의 create-project로 생성하는 방법이 있다. 결과물은 동일한데 installer 속도가 더 빠르다.

composer.json, phpunit.xml 등 단순한 스캐폴딩을 함께 제공한다.

Lumen Installer

다음 명령어로 Lumen Installer를 설치한다. 이 installer는 커맨드라인 환경에서 lumen 프로젝트를 시작할 수 있도록 기능을 제공한다.

composer global require "laravel/lumen-installer=~1.0"

설치가 완료되면 lumen new 명령어로 프로젝트를 생성할 수 있다. 여기서 helloworld라는 이름으로 프로젝트를 생성했다.

lumen new helloworld

다음과 같이 프로젝트가 생성된 것을 확인할 수 있다.

lumen init

composer

위 인스톨러를 사용할 수 없다면 composer create-project 생성할 수 있다.

composer create-project laravel/lumen --prefer-dist

설정하기

lumen은 laravel과 다르게 모든 설정을 .env에 저장한다. 쉽게 활용할 수 있도록 .env.example 템플릿이 제공된다. 데이터베이스, 캐시, 큐, 세션과 관련한 설정값을 지정할 수 있다. 설정에서 가장 먼저 할 일로 해당 템플릿을 열어 APP_KEY에 32자 무작위 문자열을 입력한 후 .env로 저장한다.

URL 설정

Apache 환경을 사용하고 있다면 public/.htaccess를 활용할 수 있고 Nginx에서는 다음과 같이 설정할 수 있다.

location / {
    try_files $uri $uri/ /index.php?$query_string;
}

HTTP 라우팅

라우팅 기초

route는 app/Http/routes.php에 작성한다. 여타 micro-Framework과 크게 다르지 않은 문법이다.

$app->get('/', function() {
  return 'Hello World';  
});
$app->post('foo/bar', function() {
  // do something
});
$app->patch('foo/bar', function() {
  // do something
});
$app->put('foo/bar', function() {
  // do something
});
$app->delete('foo/bar', function() {
  // do something
});

이렇게 생성한 route에서 URL을 다른 곳에서 사용하고 싶다면 url 헬퍼를 쓴다.

$list_link = url('foo');

router에서 파라미터는 다음과 같이 사용한다.

$app->get('user/{id}', function($id) {
  return 'User '.$id;
});

$app->get('user/{name:[A-Za-z]+}', function($name) {
  // do something
  // 이 문법은 라라벨과 호환되지 않는다.
});

컨트롤러와도 쉽게 연결할 수 있다.

$app->get('user/{id}', 'UserController@showProfile');

위 route와 같이 복잡한 URL 구조를 가지고 있다면 route에 as로 별칭을 지정해 쉽게 활용할 수 있다.

$app->get('user/profile', ['as' => 'profile', function() {
  // show user profile
}]);

$app->get('user/dashboard', [
  'as' => 'dashboard',
  'uses' => 'UserController@showDashboard'
]);

이제 위 route는 profile이라는 이름으로 활용할 수 있다. 파라미터가 있는 경우는 두번째 파라미터에 array로 값을 넣으면 된다.

$url = route('profile');
$redirect = redirect()->route('profile');

$profile_url = route('profile', ['id' => 1]);

라우팅 그룹 묶기

route를 그룹으로 묶어 미들웨어나 네임스페이스를 지정할 수 있다. 여기에서 $app->group()를 활용한다.

Closure를 기반으로 한 미들웨어는 다음과 같이 사용할 수 있다.

$app->group(['middleware' => 'foolbar'], function($app) {
  $app->get('/', function() {
    // do something
  });
  $app->get('user/profile', function() {
    // do something
  });
});

namespace로 특정 네임스페이스에 있는 컨트롤러를 불러 활용할 수 있다.

$app->group(['namespace' => 'Admin'], function() {
  // "App\Http\Controllers\Admin" 네임스페이스에 있는 컨트롤러
});

HTTP 예외 발생하기

abort 헬퍼를 이용한다. 응답을 같이 보내줄 수도 있다.

abort(404);
abort(403, 'Unauthorised action.');

이 헬퍼는 상태 코드와 함께 Symfony\Component\HttpFoundation\Exception\HttpException 예외를 던진다.

뷰는 resources/views에 php 파일로 저장한다.

<!-- View stored in resources/views/greeting.php -->

<!doctype html>
<html>
    <head>
        <title>Welcome!</title>
    </head>
    <body>
        <h1>Hello, <?php echo $name; ?></h1>
    </body>
</html>

다음과 같이 사용할 수 있다.

$app->get('/', function() {
  return view('greeting', ['name' => 'James']);
});

$app->get('/admin', function() {
  /* ... */

  // resources/views/admin/dashboard.php
  return view('admin.dashboard', $data);
});

데이터 바인딩을 배열로 넘길 수 있지만 with 메소드나 매직 메소드를 활용할 수도 있다.

$view = view('greeting')
          ->with('name', 'Edward')
          ->with('age', 20)
          ->withLocation('Jeju'); // 매직 메소드

컨트롤러

규모가 커지면 routes.php 파일 하나로만 로직을 다루는 것보다 컨트롤러를 활용해서 구조화하는 것이 낫다. 컨트롤러로 HTTP 요청을 조작하는 로직을 쉽게 묶을 수 있다. 컨트롤러는 app/Http/Conterollers에 저장한다.

컨트롤러는 App\Http\Conterollers\Controller 기초 클래스를 필수적으로 상속해야 한다. 기본적인 컨트롤러는 다음과 같다.

<?php namespace App\Http\Controllers;

use App\User;
use App\Http\Controllers\Controller;

class UserController extends Controller {

    /**
     * Show the profile for the given user
     *
     * @param  int  $id
     * @return Response
     */
    public function showProfile($id)
    {
        return view('user.profile', ['user' => User::findOrFail($id)]);
    }

}

UserController를 라우터에서 다음과 같이 연결할 수 있다.

$app->get('user/{id}', 'App\Http\Controllers\UserController@showProfile');

의존성 주입

Lumen과 Laravel의 서비스 컨테이너는 타입 힌트를 통해 의존성을 해결해준다. 생성자 주입, 메소드 주입 둘 다 사용 가능하다. 다음 코드는 생성자의 타입 힌트 UserRepository, store 메소드의 타입힌트 Request로 의존성이 주입되는 예제다.

<?php namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Repositories\UserRepository;
use Illuminate\Http\Request;

class UserController extends Controller {

    /**
     * The user repository instance.
     */
    protected $users;

    /**
     * Create a new controller instance.
     *
     * @param  UserRepository  $users
     * @return void
     */
    public function __construct(UserRepository $users)
    {
        $this->users = $users;
    }

    /**
     * Store a new user.
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        $name = $request->input('name');
    }

}

미들웨어

HTTP 미들웨어는 HTTP 요청과 응답을 제어할 수 있도록 돕는 구조로 micro-Framework에서는 흔히 사용되고 있다. (Python의 uWSGI, .Net의 OWIN) 미들웨어는 app/Http/Middleware에 위치한다.

미들웨어를 활용하기 위해 구성해야 하는 메소드는 handle이다.

<?php namespace App\Http\Middleware;

class OldMiddleware {

    /**
     * Run the request filter.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if ($request->input('age') < 200) {
            return redirect('home');
        }

        return $next($request);
    }

}

미들웨어로 HTTP 요청과 응답을 모두 제어할 수 있는데 $next($request)를 기준으로 그 앞에서는 요청을 제어하고 뒤에서는 응답을 제어할 수 있다.

다음은 요청을 제어하는 미들웨어로 이 내용을 실행한 후 어플리케이션에 접근하게 된다.

<?php namespace App\Http\Middleware;

class BeforeMiddleware implements Middleware {

    public function handle($request, Closure $next)
    {
        // Perform action

        return $next($request);
    }
}

다음은 응답을 제어하는 미들웨어 예시로 어플리케이션에서 처리가 끝난 후 클라이언트에게 전달되는 응답을 제어할 수 있다.

<?php namespace App\Http\Middleware;

class AfterMiddleware implements Middleware {

    public function handle($request, Closure $next)
    {
        $response = $next($request);

        // Perform action

        return $response;
    }
}

Service Provider와 Service Container

Lumen은 Service ProviderService Container로 기초를 구성하고 있다. Service Provider는 어플리케이션을 시작하기 전에 준비해야 할 작업을 처리할 수 있다. bootstrap/app.php를 열어보면 $app->register() 메소드를 확인할 수 있는데 이 메소드를 통해 추가적인 service provider를 등록할 수 있다.

Service Container는 클래스 간의 의존성을 해결하기 위한 도구로 생성자 또는 “setter” 메소드를 통해 의존성을 주입해준다. $app->bind(), $app->singleton()을 통해 resolver를 등록할 수 있다. 컨테이너에서 의존성을 주입 받기 위해서는 $foobar = $app->make('FooBar'); 방식으로 make 메소드를 사용하는 방법이 있고, 앞서 살펴본 방식인 생성자나 개별 메소드에서 타입 힌팅을 이용해 의존성을 주입할 수 있다. 자세한 내용은 각 문서를 참고하자.


Micro-framework지만 그 말이 무색할 만큼 현대적인 PHP 개발에서 필요한 필수적인 요소는 모두 포함된 강력함을 보여주고 있다. 이 포스트에서 자세하게 다루진 않았지만 많은 서비스도 제공하고 있으며 기존에 laravel을 사용하고 있다면 더 간편하게 사용할 수 있을 것이라 본다. 지금까지 나온 micro-framework 중에서는 가장 마음에 드는 구성이라 차기 프로젝트에서 사용하는 것을 생각해보고 있다. 앞으로 기대가 많이 되는 프레임워크다.

  • PHP 기반의 Micro Frameworks 정리 
  • PHP 5.3에서 새로운 기능으로 네임스페이스가 추가되었다. (= 이미 오래된 기능이다.) 많은 현대 언어는 이미 이 기능을 추가한지 오래지만 PHP는 조금 늦게 추가되었다. 최근에 개발되는 대다수의 PHP 라이브러리는 네임스페이스로 패키징해 composer, League 등을 통해 제공되고 있어 현대 PHP를 사용하려고 한다면 필수적으로 알아야 하는 기능이다.

    PHP에서는 같은 이름을 가진 두 클래스를 동시에 사용할 수 없다. 클래스는 항상 유일해야만 한다. 이 제한으로 인해 서드파티 라이브러리에서 User라는 클래스명을 사용하고 있을 때는 User라는 클래스명을 사용할 수 없었다. 이렇게 간단한 클래스명을 사용하지 못하는건 불편하다.

    PHP 네임스페이스는 위와 같은 클래스명 중복 문제를 해결한다. 그 뿐 아니라 코드를 패키징하거나 벤더명을 지정해 소유권을 표시하는데도 사용할 수 있다.

    전역 네임스페이스

    여기 간단한 클래스가 있다.

    <?php
    class Edward
    {
    
    }
    

    별로 특별한 부분이 없다. 다음과 같이 사용할 수 있다.

    <?php
    
    $edward = new Edward();
    

    이 상황에서 이 클래스는 전역 네임스페이스를 가졌다고 볼 수 있다. 즉 이 클래스는 네임스페이스 없이 존재한다. 그냥 일반 클래스와 같다.

    단순한 네임스페이스

    이제 네임스페이스 밑으로 클래스를 만들어보자.

    <?php
    namespace Haruair;
    
    class Edward
    {
    
    }
    

    위에서 만든 클래스와 유사하지만 작은 차이가 있다. namespace 라는 지시문이 추가되었다. namespace Haruair;는 여기서 작성한 모든 PHP 코드가 Haruair 네임스페이스와 관련이 되어 있음을 뜻하고 이 파일에서 생성한 클래스가 모두 Haruair 네임스페이스에 포함되어 있음을 뜻한다. 네임스페이스를 통해 클래스를 생성하면 다음과 같이 사용할 수 있다.

    <?php
    $edward = new Haurair\Edward();
    

    위 코드와 같이 네임스페이스와 함께 클래스를 선언할 수 있다. 네임스페이스와 클래스 사이에는 백슬래시()로 구분이 된다. 위와 같은 방법으로 네임스페이스로 클래스를 다룰 수 있다.

    이와 같은 방법으로 여러 단계의 위계를 활용하고 있는 경우를 많이 볼 수 있다.

    This\Namespace\And\Class\Combination\Is\Silly\But\Works
    

    의존성 원칙

    PHP는 현재의 namespace에 따라 상대적으로 동작한다.

    <?php
    namespace Haruair;
    
    $edward = new Edward();
    

    Haruair 네임스페이스 내에서 개체를 생성했다. 동일한 네임스페이스에 속해 있는 상황이기 때문에 Haurair\EdwardEdward로 호출해 사용할 수 있다.

    이런 상황에서 반대로 생각해볼 수 있는 부분은 네임스페이스 내부에서 상위 또는 최상위에 있는 네임스페이스나 클래스는 어떻게 접근할 지에 대해서다.

    PHP는 클래스명 앞에 백스래시()를 넣어 전역 클래스 또는 글로벌 네임스페이스를 사용하고 있음을 명시적으로 선언할 수 있다.

    <?php
    $edward = new \Edward();
    

    만약 다른 네임스페이스에 속한 클래스인 Drink\CokeHaruair 네임스페이스 내에서 사용한다면 앞서 예제와 같이 작성할 수 있다.

    <?php
    namespace Haruair;
    
    $coke = new \Drink\Coke();
    

    매번 전체 위계를 입력하는 것이 번거롭다면 use를 활용할 수 있다.

    <?php
    namespace Haruair;
    
    use Drink\Coke;
    
    $coke = new Coke();
    

    use를 활용하면 다른 네임스페이스에 있는 하나의 클래스를 현재 네임스페이스 내에서 사용할 수 있게 해준다. 동일한 클래스명을 불러오게 되는 경우가 온다면 다음과 같이 활용할 수 있다.

    <?php
    namespace Haruair;
    
    use Drink\Pepsi as BlueCoke;
    
    $pepsi = new BlueCoke();
    

    위와 같이 as 키워드를 쓰면 Drink\Pepsi 클래스에 별칭 BlueCoke를 지정해 사용할 수 있다. 같은 이름의 클래스 여럿을 동시에 사용한다 해도 문제 없다.

    <?php
    namespace Facebook;
    
    use Twitter\User as TwitterUser;
    
    class User {}
    
    $twitter_user = new TwitterUser();
    $facebook_user = new User();
    

    Twitter 네임스페이스에 있는 UserTwitterUser 별칭으로 불러오면서 충돌을 회피했다. 이와 같이 충돌을 피하고 의도와 필요에 따라 기능을 모아서 사용할 수 있다.

    use는 필요한 만큼 넣어서 사용할 수 있다.

    <?php
    namespace Haruair;
    
    use Twitter\Follower;
    use Facebook\WallPost;
    use Cyworld\WallPost as CyPost;
    

    구조

    네임스페이스는 단순히 충돌을 피하기 위해서만 사용하는 것이 아니라 조직이나 소유권을 표기하기 위해 사용하기도 한다.

    오픈소스 라이브러리를 만든다고 가정하자. 내가 만든 코드를 다른 사람이 사용한다면 분명 좋을 것이다. 다만 내 코드를 사용하는 사람들에게 불편함을 주지 않았으면 좋겠다. 클래스명이 충돌하게 되면 엄청나게 불편할 것이 확실하다. 그래서 다음과 같이 네임스페이스를 구분하기로 했다.

    Haruair\Blog\Content\Post
    Haruair\Blog\Content\Page
    Haruair\Blog\Tag
    

    여기서 내 아이디를 사용해서 이 코드가 누가 만들었는지 표시하는 것과 동시에 내 라이브러리 안에 만들어 코드를 사용하고자 하는 사람의 코드와 충돌하지 않도록 돕는다. 내 기초 네임스페이스에 여러개의 서브 네임스페이스를 만들어 내부 구조를 잡았다.

    Composer를 사용하면 PSR-0, PSR-4를 통해 정해진 규칙에 따라 네임스페이스를 통해 클래스 정의를 자동으로 불러오는 등의 작업을 할 수 있다. 위 두 문서에서 이 유용한 방식을 확인해보는 것을 강력하게 추천한다.

    제한

    PHP가 제공하는 네임스페이스에는 한계가 있다. 다른 언어들에서의 구현과는 거의 유사하지만 약간 다른 점이 존재한다. Java의 경우, wildcard(*)를 이용해 해당 네임스페이스에 속해 있는 모든 클래스를 한번에 불러올 수 있다. 또한 Java에서의 import는 앞서 살펴 본 use와 같은 역할을 해서 패키지나 네임스페이스 내에 있는 클래스를 쉽게 이용할 수 있게 돕는다. 다음은 Java의 예시다.

    import haruair.blog.*;
    

    위와 같은 코드로 haruair.blog의 모든 패키지를 로드할 수 있다.

    PHP는 이와 같은 방법으로 불러올 수 없다. 대신 상위의 네임스페이스를 use로 불러와 유사하게 사용할 수 있다.

    <?php
    namespace weirdmeetup;
    
    use Haruair\Blog as Cms;
    
    $post = new Cms\Content\Post;
    $page = new Cms\Content\Page;
    $tag = new Cms\Tag;
    

    한 네임스페이스에서 많은 클래스를 동시에 사용할 때 위 방법이 도움이 된다.

    더 읽을 거리

    msdn 블로그에 게시된 New Features in C# 6포스트를 요약했다. C# 6는 VS 2015 프리뷰와 함께 제공된 버전으로 여러가지 문법 특징이 추가되었다. 이 포스트는 요약이라 내용이 좀 부실할 수 있는데 상세한 내용은 위 포스트를 참고하자.

    표현식

    nameof

    새로운 형태의 문자열로 프로그램 요소의 이름을 간혹 알아내야 할 상황을 마주하는데 그때 사용할 수 있는 표현식이다. 점 표기법(dot notation)을 사용한 경우 가장 마지막 식별자를 반환한다.

    if (x == null) throw new ArgumentNullException(nameof(x));
    WriteLine(nameof(person.Address.ZipCode)); // prints "ZipCode"
    

    person이 스코프 내에서 타입이 아닌 변수라면 두번째 코드는 허용되지 않는다.

    문자열 인터폴레이션

    번거로웠던 String.Format()을 간편하게 작성할 수 있다.

    var s = String.Format("{0} is {1} year{{s}} old", p.Name, p.Age);
    s = "\{p.Name} is \{p.Age} year{s} old";
    s = "\{p.Name,20} is \{p.Age:D3} year{s} old"; // 형식 지정
    s = "\{p.Name,20} is \{p.Age:D3} year{(p.Age == 1 ? "" : "s")} old"; // 표현식도 쓸 수 있음
    

    Note. 프리뷰 이후 문법이 변경되었다. s = $"{p.Name,20} is {p.Age:D3} year{{s}} old";

    Null 조건부 연산자

    체이닝 등 호출이 연속적으로 이뤄지는 상황에서 null 확인을 더 쉽게 만드는 연산자다.

    int? length = customers?.Length; // customers가 null이면 null 반환
    Customer first = customers?[0]; // customers가 null이면 null 반환
    
    // null 병합 연산자인 ??와 함께 사용. customers가 null 일 때, 값은 0
    int length = customers?Length ?? 0;
    
    // 뒤에 따라오는 멤버 접근, 엘리먼트 접근 등은 customers가 null이 아닐 때만 호출
    int? first = customers?[0].Orders.Count();
    
    // 위와 동일한 표현
    int? first = (customers != null) ? customers[0].Orders.Count() : null;
    
    int? first = customers?[0].Orders?.Count(); // 연속으로 사용 가능
    

    다만 ?을 사용한 직후에는 문법적으로 모호함이 있어서 바로 호출을 할 수 없다. 그래서 바로 대리자를 호출할 경우 다음과 같이 작성해야 한다.

    if(predicate?(e) ?? false) { ... } // 에러 발생
    if(predicate?.Inkobe(e) ?? false) { ... }
    

    이벤트를 작동할 때 다음과 같이 작성할 수 있다.

    PropertyChanged?.Invoke(this, args);
    

    이 문법은 스레드 안정성을 보장하는데 좌측을 평가한 후 값을 임시로 저장하기 때문이다.

    인덱스 이니셜라이저

    개체 이니셜라이저를 확장에 다음과 같이 인덱스를 넣을 수 있게 되었다. 표현식 하나로 JSON 개체를 만들 때 유용하다.

    var numbers = new Dictionary<int, string> {
      [7] = "seven",
      [9] = "nine",
      [13] = "thirteen",
    };
    

    확장 Add 메소드

    확장 Add 메소드를 컬렉션 이니셜라이저에서 사용할 수 있다. 예전엔 인스턴스 메소드만 Add를 호출할 수 있었다.

    오버로드 향상

    오버로드 확인(resolution)이 향상되었다. 상세 내역은 소개되지 않았고 nullable 값 타입을 받을지 말지, 메소드 그룹을 대리자로 받는 등의 향상이 있을 것이라고 한다.

    표현문

    예외 필터

    CLR 호환으로 VB와 F#에만 있던 기능이 이제 추가되었다.

    try { ... }
    catch (MyException e) if (myfilter(e))
    {
      ...
    } // if 문이 참일 때만 `catch` 블럭이 실행된다.
    

    필터는 일반적이고 수용할 수 있는 형태로 “오용”을 할 수 있다. 파생작업을 위해 다음과 같이 사용할 수 있다.

    private static bool Log(Exception e) { /* log it */; return false; }
    ...
    try { ... }
    catch (Exception e) if (Log(e)) {}
    

    catch/finally 블럭에서의 Await

    Resource res = null;
    try
    {
        res = await Resource.OpenAsync( ... ); // 원래 되던 부분
    }
    catch(ResourceException e)
    {
      await Resource.LogAsync(res, e); // 이제 가능한 부분
    }
    finally
    {
      if (res != null) await res.ColseAsync(); // 여기서도 가능
    }
    

    맴버 선언

    자동 프로퍼티 이니셜라이저

    필드에 이니셜라이져 하는 것과 비슷하다. 이 방법으로 이니셜라이징 하면 setter를 거치지 않고 내부 형식 바로 저장이 된다.

    public class Customer
    {
      public string First { get; set; } = "Jane";
      public string Last { get; set; } = "Doe";
    }
    

    Getter only 자동 프로퍼티

    setter 없이 자동 프로퍼티를 사용하는게 허용된다. 이 방식은 readonly로 암묵적인 선언이 된다.

    public class Customer
    {
      public string First { get; } = "Jane";
      public string Last { get; } = "Doe";
    }
    

    위와 같이 초기화 하지 않는 경우에는 다음과 같이 타입 생성자에서 선언하면 값이 내부 형식에 바로 저장된다.

    public class Customer
    {
      public string Name { get; };
      public Customer(string first, string last)
      {
        Name = first + " " + last;
      }
    }
    

    이 문법은 표현 타입을 더 간소하게 만든다. 하지만 변형 가능한 타입과 불변 타입의 차이가 없어진다. 변형 가능한 클래스도 상관이 없다면 자동 프로퍼티를 기본으로 사용하는 것도 좋다. 여기다 getter only 자동 프로퍼티는 변형 가능한 타입과 불변 타입을 더 비슷하게 만든다.

    표현-본문 함수 멤버

    표현-본문 함수 멤버는 메소드와 프로퍼티, 다른 종류의 함수 멤버들의 본문을 블럭이 아닌 람다처럼 표현식을 바로 쓸 수 있도록 지원한다. 실제로 람다처럼 람다 화살표로 작성한다. 블럭 본문에 단일 반환값을 가진 형태와 동일하다.

    public Point Move(int dx, int dy) => new Point(x + dx, y + dy);
    public static Complex operator +(Complex a, Complex b) => a.Add(b);
    public static implicit operator string(Person p) => $"{p.First} {p.Last}";
    

    void를 반환하는 메소드, Task를 반환하는 비동기 메소드에서도 동일하게 사용할 수 있지만, 이 경우에는 람다에서와 같이 꼭 문 표현식(statement expression)이 사용해야 한다.

    public void Print() => Console.WriteLine(First + " " + Last);
    

    프로퍼티와 인덱서도 다음과 같이 쓸 수 있다. get 키워드가 없는 대신 표현식 본문 문법에 따라 암묵적으로 사용된다.

    public string Name => First + " " + Last;
    public Customer this[long id] => store.LookupCustomer(id);
    

    파라미터 없는 구조체 생성자

    파라미터 없는 구조체 생성자가 허용되었다. 다음 구조체에서 new Person()은 선언된 생성자에 따라 기본값을 제공한다. default(Person)로 기본값을 사용하거나 new Person[...] 형태로 배열을 사용하면 해당 생성자는 실행되지 않는다. 명시적으로 구조체 타입과 함께 new 키워드를 사용했을 때만 해당된다.

    struct Person
    {
      public string Name { get; }
      public int Age { get; }
      public person(string name, int age) { Name = name; Age = age; }
      public Person() : this("Jane Doe", 37){ }
    }
    

    임포트

    using static

    using 으로 정적 멤버 타입을 스코프에서 직접 사용할 수 있다. 프리뷰에서는 정적 클래스의 멤버만 불러올 수 있다.

    using System.Console;
    using System.Math;
    
    class Program
    {
      static void Main()
      {
        WriteLine(Sqrt(3*3 + 4*4));
      }
    }
    

    Note. 다음과 같이 디자인이 변경되었다.

    1. using에서 using static으로 변경
    2. 구조체나 enum과 같은 멤버의 비정적 타입을 임포트 할 수 있음
    3. VB 모듈의 멤버나 F#의 최상위 함수를 임포트 할 수 있음
    4. 최상위 스코프에서 확장 메소드는 임포트 할 수 없음. 확장을 해온 원 클래스는 불러오지 않고 확장 메소드만 불러오면 안되기 때문.

    위 모든 기능들은 VS 2015 프리뷰에서 확인할 수 있다.


    이 글을 뒤늦게 확인하고 C#6의 문법적인 변경점을 살펴봤다. (아직 기본적인 문법도 잘 모르긴 하지만.) C# 개발은 얼마 해보지 못했는데 문자열 인터폴레이션만 봐도 편리한 기능들이 많이 나오는구나 느낄 수 있었다. 이번 달 아니면 다음 달 내로 베타가 시작될 것 같은데 기대된다.

    반년 만에 블로그 테마를 변경했다. 고해상도 디바이스가 많아져서 그런지 요즘 대부분의 블로그 테마들이 큰 서체 사이즈와 넓은 레이아웃으로 많이 나오고 있다. 반년 정도 사용한 hamingway 테마로 변경했을 때에도 비슷한 테마를 여럿 적용해봤는데 그나마 한국어 컨텐츠가 괜찮은 가독성으로 보여 hamingway 테마를 선택했었다.

    이 테마를 오래 사용하다보니 불편한 점도 있었다. 대다수의 테마에서 공통적으로 느껴지는 부분인데 스크롤을 내려가며 컨텐츠를 읽다보면 sidebar가 있던 공간이 계속 공백으로 남아있기 때문에 균형감 없는 레이아웃으로 글을 읽게 된다. 또한 영어로 작성된 컨텐츠는 모든 브라우저에서 단어를 분리해 행 전환이 되는 단어 분리(word-break)가 발생하지 않지만 아직까지 CJK언어권의 단어 분리 옵션은 일부 브라우저만 지원한다.1 이런 상황과 맞물려 좁은 컬럼과 큰 서체의 사용은 한국어 문서의 가독성을 많이 떨어뜨리게 된다.

    WordPress haruair theme

    이번 테마는 roots.io의 Sage를 기반 테마로 사용해서 레이아웃, 서체, 행간과 자간 등을 변경해서 만들었다. 이 기반 테마는 bootstrap으로 구성되어 있는데 dist 되어있는 minified css를 활용하는 것이 아니라 bower로 전체 코드를 받아와 less를 직접 사용할 수 있는 장점이 있다. 테마의 구조가 직관적인 편이고 gulp를 이용한 빌드 및 watch 설정이 이미 다 되어 있기 때문에 기존에 있던 많은 starter theme과는 다르게 번거로운 작업이 필요하지 않았다.

    • one column으로 강제되어 있지만 2-column으로도 사용 가능
    • 영문은 ubuntu, 국문은 나눔바른고딕 기본으로 사용
    • 기본 서체의 크기를 키우고 행간과 자간을 한국어에 맞게 조절
    • 포스트 제목 아래에 headline을 삽입할 수 있도록 custom field 추가
    • 포스트 하단에 author box 추가
    • 테블릿 이하 해상도에서 서체 크기 조정

    Ubuntu는 웹폰트로 추가되어 있는 반면 나눔고딕은 그렇지 않다. 나눔바른고딕은 웹폰트로 사용하기에 아직 무거운 느낌이고 기본 서체를 사용해도 그렇게 못봐주는 느낌은 아니라서 해당 폰트가 설치되어 있을 때만 나오고 그 외에는 애플고딕이나 바른고딕으로 출력된다.

    아직 어색한 부분이나 변경해야 하는 부분이 많이 있지만 차차 수정할 생각으로 일단 적용했다. 해당 테마는 GitHub에서 받을 수 있으며 테마를 설치할 때에도 의존성 설치 및 빌드 과정이 요구되어 bower, npm 등을 구동할 수 있어야 한다. 로컬에서 작업한 후 gulp build로 모든 파일을 컴파일해서 올리는 방법도 가능하다. 자세한 설치 방법은 Sage README에서 확인할 수 있다.

  • Firefox는 CJK 언어를 위해 word-break: keep-all 을 지원하며 IE도 비슷한 방식으로 단어 분리를 방지할 수 있다. 
  • 요즘 Microsoft Virtual Academy 를 통해 제공되는 여러 강의를 듣고 있다. 모든 강의가 영어로 제공되어 있어 아쉽긴 하지만 우린 직접 하는 그림(?)을 보고 따라 할 수 있으니까 만약 관심이 있다면 살펴보는 것도 좋겠다.

    C#을 가장 처음 접한 때는 @justinchronicle님이 운영한 .Net MVC Web Frameworks 스터디 였는데 그 경험으로 회사에서도 C#으로 프로젝트를 진행해보는 등 많은 도움이 되었다. 스터디가 프레임워크 위주라서 C# 자체에 대해 너무 피상적으로만 알고 있다는 생각이 들었던 터라 요 얼마간은 제대로 공부해야겠다 마음 먹고 MVA에서 C# 관련 강의를 보고 있다.

    C# 개발을 하면서 Visual Studio를 사용해왔긴 했지만 너무 기본적인 기능만 활용하고 있다는 생각이 들어 MVA에 올라온 강의 중 What’s New In Visual Studio 2013 in JumpStart를 잠깐 보게 되었는데 유용한 기능들이 많아 간단하게 정리했다.

    VS 테마 변경하기

    개발 도구를 사용하다보면 생산성을 높이기 위해서(가독성이나… 기분을 전환하거나ㅎ) 테마를 변경하게 되는 경우가 종종 있다. Visual Studio에서도 Color Theme을 변경할 수 있는 기능이 있다.

    Tools > Options 에서 Environment 섹션을 클릭하면 Color Theme을 선택할 수 있는 옵션이 있다. 기본적으로 제공되는 테마는 Black, White, Blue 세가지인데 Visual Studio를 설치하는 과정에서 선택하게 되어 있기 때문에 직접 변경하게 되는 일은 거의 없다.

    이 외에 새로운 테마를 사용하고 싶다면 Visual Studio Gallery에서 제공하는 Visual Studio 2013 Color Theme Editor를 활용할 수 있다. 해당 확장을 설치하면 기본적으로 추가적인 테마가 제공되며 또 자신의 취향에 맞는 테마를 쉽게 만들어 낼 수 있다.

    다음과 같이 추가된 테마를 확인할 수 있다.

    Color Theme Options

    Solarized (Dark)를 적용했다.

    Theme Changed

    행 번호 표시하기

    이미 많은 분들이 켜서 사용하고 있는 옵션인데 Tools > Options > Text Editor 에서 활성화 하고 싶은 언어의 General 섹션을 클릭하면 Line Numbers 옵션이 있다.

    스크롤바 맵모드 사용하기

    Sublime Text에서 제공되는 것과 같은 스크롤바 맵모드를 활용할 수 있다. Tools > Options > Text Editor 에서 활성화 하고 싶은 언어의 Scroll Bars 섹션을 클릭하면 Behavior에서 스크롤 모드를 변경할 수 있다. 여기서 map 모드로 변경하고 적용하면 맵모드를 확인할 수 있다.

    Scroll Map Mode Options

    활성화 된 스크롤맵에 마우스 커서를 올리면 코드를 바로 확인할 수 있는 Preview Tooltip 도 지원한다.

    주석 켜고 끄기

    해당 코드를 주석으로 바꾸고 싶으면 영역을 선택하고 Ctrl + K, Ctrl + C 로 주석을 달 수 있다. 반대로 주석을 없에고 싶으면 Ctrl + K, Ctrl + U.

    문서 포메팅하기, 들여쓰기 자동 지정하기

    VS는 코드 작성할 때 기본적으로 설정된 코딩 컨벤션에 맞게 잘 동작하지만 가끔 애매하게 제대로 되지 않을 때가 있다. 그럴 때 Format Document를 사용할 수 있다. 단축키는 Ctrl + K, Ctrl + D. 문서 전체가 아닌 선택된 영역만 하고 싶다면 Ctrl + K, Ctrl + F.

    Format Document

    현재 행을 다른 행으로 이동하기

    코드를 작성하다가 코드의 위치를 옮기고 싶다면 복사 붙여넣기를 해도 되지만 간단하게 Alt + 위, 아래 단축키를 사용할 수 있다. 여러 행을 선택하면 한번에 이동도 가능하다.

    Peek Definition

    C#은 여러 클래스가 많은 파일에 산재되어 있어 직접 작성한 코드라도 해당 위치를 찾을 때 불편한 경우가 종종 있다. 이럴 때 Peek Definition을 활용할 수 있다. 현재 작성된 메소드나 인스턴스, 클래스에서 Alt + F12를 누르면 작은 창으로 해당 코드를 바로 열어볼 수 있다.

    Pick Definition

    열린 작은 창 안에서도 해당 단축키로 계속 탐색이 가능하고 그 자리에서 바로 코드를 수정 및 저장하는 것도 가능하다. 창을 닫으려면 ESC를 누르면 된다.

    이전에 복사한 내용 붙여넣기

    내용을 복사해 붙여넣는 도중에 다른 내용을 복사하면 이전에 가지고 있던 내용을 잃어버리게 된다. 만약 그 이전에 있던 내용을 붙여넣고 싶다면 Ctrl + Shift + V 를 반복해서 입력하면 된다.


    Visual Studio는 알아가면 알수록 강력한 기능이 많아 사용하는 재미가 있는 도구인 것 같다. 조만간 나올 VS 2015도 기대되고, 더욱 자유자재로 쓸 수 있도록 부지런히 공부해야겠다.

    마이크로소프트에서 제공하는 IDE인 Visual Studio는 다양한 언어과 강력한 기능을 제공하고 있다. 이 IDE는 상황에 맞게 구입해서 사용할 수 있도록 다양한 버전으로 제공되고 있었는데 예전부터 Visual Studio Express 라는 이름으로 기능이 다소 제한된 무료 버전을 제공하고 있었다. (DreamSpark를 통해서 사용 할 수 있던 버전이 바로 이 버전.)

    가장 최근 Express 버전인 Visual Studio 2013 Express도 각 타겟 플랫폼에 따라 for Web, for Windows, for Windows Desktop로 분류해 제공해왔는데, 2014년 11월, 대부분의 제약 사항이 없어진 Visual Studio 2013 Community가 공개되었다.

    • Express 버전에서 제한 되었던 IDE 확장 기능을 Visual Studio Gallery를 통해 사용할 수 있음
    • 크로스 플랫폼 지원
    • 교육용 및 개인 개발자 상용으로 사용 가능
    • 기업도 PC 250대 이상 또는 연매출 미화 1백만 달러 이상이 아닌 경우 5명까지 사용 가능

    이 버전의 VS는 Visual Studio 2013 Community 안내 페이지에서 받을 수 있다.

    요즘은 생산성 좋은 IDE도 많이 생겨서 선택의 폭이 많이 넓어졌긴 했지만 그래도 VS의 강력하고 안정적인 기능들은 언제 봐도 든든한 기분이 든다. C#, VB 외에도 PHP, JavaScript 등 다양한 언어에 대한 지원도 많아졌고 npm, bower, composer 등을 편리하게 사용할 수 있는 확장도 있어 새로운 개발도구를 찾고 있는 윈도 사용자라면 사용해보는 것도 좋겠다.

    윈도 환경의 개발 커뮤니티도 하루가 다르게 변화하고 있고(MS에서 github에 코드를 공개해놓고 작업한다던가) 게다가 Windows 10와 VS2015 릴리즈가 올해 예정되어 있어 예전에 비해 체감하게 되는 온도가 많이 다른 것 같다. 더 재미있는 도구들이 많이 나왔으면 하는 기대감이 든다.

    워드프레스에서 유지보수를 위해 사이트를 일시적으로 차단할 경우가 있다면 유지보수 모드(Maintenance mode)를 활용할 수 있다.

    사용자로서 워드프레스를 이용하게 될 때에는 이 모드를 보게 되는 일이 거의 없다. 만약 보게 된다면 워드프레스 플러그인, 테마, 코어를 업데이트 하는 도중에 사이트에 접속하게 되는 경우다. 설치 과정을 눈여겨 본 사용자라면 유지보수 모드를 켜고 설치 과정이 진행된 후 완료 되면 다시 유지보수 모드가 해제되는 것을 확인할 수 있다.

    만약 업데이트 중에 페이지를 벗어나게 된다면 유지보수 모드가 해제되지 않아 사이트가 닫힌 상태로 유지된다. 이럴 때에는 직접 유지보수 모드를 직접 해제해야 한다.

    maintenance mode

    워드프레스 웹사이트에서 다음과 같은 화면을 본다면 유지보수 모드가 켜져있는 상태다.

    유지보수 모드 켜고 끄기

    유지보수 모드는 워드프레스 최상위 폴더에 .maintenance라는 이름으로 빈 텍스트 파일을 생성하면 활성화 된다. 유지보수 모드를 끄려면 이 파일을 지우면 된다.

    .Maintenance file

    유지보수 페이지 변경하기

    유지보수 페이지를 좀 더 친절하게 변경하고 싶다면 wp-content/maintenance.php 파일을 만들어 해당 내용을 작성하면 된다. 기본적으로 출력되는 유지보수 페이지는 503 Service Unavailable 를 반환한다는 사실을 참고하자.

    <?php
      $protocol = $_SERVER["SERVER_PROTOCOL"];
      if ( 'HTTP/1.1' != $protocol && 'HTTP/1.0' != $protocol )
        $protocol = 'HTTP/1.0';
      header( "$protocol 503 Service Unavailable", true, 503 );
      header( 'Content-Type: text/html; charset=utf-8' );
      header( 'Retry-After: 600' );
    ?>
    <!DOCTYPE html>
    <html lang="ko">
    <head>
      <meta charset="utf-8">
      <title>Haruair.com 점검중</title>
    </head>
    <body>
      <h1>현재 웹사이트 점검중입니다. 잠시 후에 다시 접속해주시기 바랍니다.</h1>
    </body>
    </html>
    

    웹사이트 설정

    웹페이지 색상을 선택하세요

    Darkreader 플러그인으로 선택한 색상이 제대로 표시되지 않을 수 있습니다.