php에서는 callable 이라는 타입 힌트를 제공한다. 이 타입 힌트는 말 그대로 호출이 가능한 클래스, 메소드, 또는 함수인 경우에 사용할 수 있다. php에서는 타입이 별도의 타입으로 존재하지 않는 대신에 문자열로 처리하고 있어서 다소 모호한 부분도 있다. callable을 타입 힌트로 사용했을 때 어떤 값을 넘길 수 있는지 명확히 알고 있어야 한다.

function callableOnly(callable $callable): void {
    // callable에 해당하면 다음처럼 호출할 수 있음
    call_user_func($callable);

    // 일부를 제외하고는 다음과 같이 호출 가능함
    $callable();
}

특히 callable은 명확한 제한 없이 열어두고 사용하면 보안 문제 등을 만들어낼 수 있기 때문에 유의해야 한다.

callable

다음은 callable에 해당하는 경우로 상당히 다양한 형태로 callable을 정의할 수 있다. 여기서는 callable인지 확인하는 is_callable() 함수를 사용했다.

함수

function sayHello() {
    echo 'Hello';
}
is_callable('sayHello'); // true

꼭 사용자 정의 함수가 아니더라도 이와 같이 사용할 수 있다. 다만 언어에서 제공하는 구조는 callable에 해당하지 않는다. 예를 들면 isset(), empty(), list()는 callable이 아니다.

is_callable('isset'); // false

익명 함수

$hello = function () {
    echo 'Hello';
};

is_callable($hello); // true

정적 메소드

class HelloWorld()
{
    static function say()
    {
        echo 'Hello World!';
    }
}
is_callable('HelloWorld::say'); // true
is_callable(['HelloWorld', 'say']); // true
is_callable([HelloWorld::class, 'say']); // true

::class 상수는 PHP 5.5.0 이후로 사용할 수 있는데 해당 클래스의 네임스페이스를 포함한 전체 클래스명을 반환한다. 문자열로 사용하는 경우에는 개발도구에서 정적분석을 수행하지 못하기 때문에 오류를 검출하기 어렵다. 대신에 이 상수를 사용하면 현재 맥락에서 해당 클래스가 선언되어 있는지 없는지 검사해주기 때문에 이런 방식으로 많이 작성한다.

주의할 점은 정적분석 기능이 없는 개발도구에서는 ::class를 사용해도 문자열을 사용하는 것과 차이가 없다. ::class는 실제로 해당 클래스로 인스턴스를 생성하거나 하지 않기 때문에 autoload와는 상관 없이 동작하기 때문이다.

echo SomethingNotDefined::class; // "SomethingNotDefined"

대신 런타임에서 is_callable을 사용하거나 callable로 넘겨주는 경우에 정적 메소드의 경우는 autoload를 통해 검사하는 식으로 동작한다.

클래스 인스턴스 메소드

class Person
{
    protected $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function getName(): string
    {
        return $this->name;
    }
}

$edward = new Person('Edward');
is_callable([$edward, 'getName']); // true

클래스의 스코프 해결 연산자를 이용한 메소드

스코프 해결 연산자(Scope Resolution Operator)를 callable에서도 사용할 수 있다. Paamayim Nekudotayim라고 부르는 ::를 의미한다.

class Animal
{
    public function getType()
    {
        echo 'Animal';
    }
}

class Dog extends Animal
{
    public function getType()
    {
        echo 'Dog';
    }
}

$dog = new Dog;
is_callable([$dog, 'parent::getType']); // true
call_user_func([$dog, 'parent::getType']); // Animal

$callable(); // 이 경우에는 이 방식으로 호출할 수 없음

구현 메소드 대신 부모 클래스의 메소드를 직접 호출할 수 있다. 관계를 뒤집는 좋지 않은 구현이므로 이런 방식에 의존적인 코드는 작성하지 않는다.

매직 메소드 __invoke()

__invoke() 매직 메소드가 구현된 클래스는 인스턴스를 일반 함수처럼 호출할 수 있다.

class PersonSay
{
    protected $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function __invoke()
    {
        echo "Hello, {$this->name} said.";
    }
}

$say = new PersonSay('Edward');
is_callable($say); // true
call_user_func($say); // Hello, Edward said.

익명 클래스의 경우도 호출 가능하다.

$say = new class {
    public function __invoke(string $name)
    {
        echo "What up, {$name}?";
    }
};
is_callable($say); // true
call_user_func($say, 'Edward'); // What up, Edward?

이 매직 메소드는 손쉽게 상태를 만들어낼 수 있어서 유용할 때가 종종 있다.


Iterator를 callable로 사용할 수 있을까?

Iterator를 넘기면 인스턴스를 넘긴 것으로 인식해서 __invoke() 구현을 확인한다. 즉, Iterator를 루프를 돌려서 사용하지는 않는다.

Closure vs callable vs 인터페이스

매개변수로 익명함수만을 받고 싶다면 Closure를 지정할 수 있다. 하지만 익명함수에도 use 키워드로 스코프를 집어 넣거나 global로 전역 변수에 접근하는 방식도 여전히 가능하기 때문에 callable이 아니더라도 callable 만큼 열려 있는 것이나 마찬가지라는 점에 유의해야 한다. 열려있는 것 자체는 문제가 아니지만 Closure, callable은 전달받은 함수가 사용하기 적합한지 판단해야 하는 경우가 생긴다. 예를 들면 매개변수의 숫자라든지, 타입이라든지 사용 전에 확인해야 하는 경우가 있다.

그래서 단순히 함수 기능이 필요하더라도 계약을 명확하게 들어내야 한다면 인터페이스를 활용하는게 바람직하다. 인터페이스를 사용하면 전통적인 방식대로 인터페이스를 구현해서 사용하면 되겠다. 물론 익명 클래스로 다음처럼 사용할 수 있다. 익명 함수에 비해서는 다소 장황하게 느껴질 수 있지만 사전조건으로 검증해야 하는 내용을 줄일 수 있다.

interface NamedInterface
{
    public function getName(): string;
}

function sayHello(NamedInterface $named): void {
    echo "Hello! {$named->getName()} said.";
}

sayHello(new class implements NamedInterface {
    public function getName(): string {
        return 'Edward';
    }
});

모든 방법에는 장단점이 있으므로 필요에 따라 어느 접근 방식을 택해야 할지 결정 해야겠다.

어제는 저스틴님네 놀러가서 맛밥 맛고기도 먹었다. 오랜만에 좀 먹먹했던 기분도 풀리고 좋은 시간을 보냈다. 감사하게 집까지 바래다 주셨고 오면서 이런 저런 이야기를 했다. 마지막에 했던 이야기가 계속 생각에 걸려서 짧게라도 글로 써보고 싶어졌다.

예전엔 뭔가 하게 되면 고민하는 단계 없이, 지체 없이 시작하는게 가능했는데 요즘은 그런 “그냥 하기”가 참 어렵다는 얘기였다. 요즘은 그게 잘 안된다. 복잡한 이유를 찾지 않고 내가 하고 싶은 일을 고민 없이 하는게 쉽지 않아졌다.

생각해보면 그게 가능했던 당시에는 왜 사람들이 그냥 하지 않을까 하면 되는데 하는 오만한 얘기도 많이 했다. 하고싶은 일을 큰 고민 없이 하는 것도 얼마나 많은 부분이 조화되어야 가능한 것인지 지금 와서야 많이 느낀다. 너무 작아서 미약하게 느껴지는 시간도 의자에 앉아서 꾸역꾸역하는 일도, 주변에 물어보거나 조언을 얻을 수 있는 사람들이 있는 것도, 흥미가 꺼지지 않고 계속 배우고 싶은 욕구가 생기는 일도. 이런 부분들을 생각하다보면 무엇 하나 시작하는 일이 얼마나 많은 지원과, 충분한 동기와 자원이 필요한 일인지 생각한다.

이런 답을 고민하기보다 그냥 하면 된다. 왜 안될까 고민하면 끝도 없고, 종이 뒤집듯 고민 없이 행동으로 옮기는 것이 더 빠르다. 그냥 시작하고 나서 처음에는 좀 꾸역꾸역이라도 근육이 생길 때까지는 해야 한다. 꾸역꾸역은 단순하게 익숙하지 않다는 증거다. 그 구간만 지나면 재밌어진다. 그런데 꾸역꾸역하는 지점까지 가는 것만으로도 대단하다고 박수치고 싶다.

예전에 비해서 생각해야 할 점도 많고 고민도 많은게 당연하긴 하지만 그게 뭔가를 뒤로 미루는 이유가 되는거는 스스로에게 실례라는 기분도 든다. 내 스스로에게 많은 이유를 붙여가며 안하는 것은 그냥 내 뇌가 나를 속이는 일 같다. 설거지 거리를 쌓는 일 자체는 잘못이 아니지만 언젠가는 정리해야 한다. 더 많은 에너지와 시간이 필요하다. 그걸 알면서도 쌓고 있지만…

예전처럼 그냥 했으면 좋겠다.

Mike McQuaid의 Open Source Maintainers Owe You Nothing를 번역한 글이다. 이 번역은 CC-BY-NC-SA를 준수한다.


오픈소스 메인테이너는 당신에게 빚진 적 없다

이 포스트는 지난 10년간 오픈소스 커뮤니티에 참여한 경험과 8년간 Homebrew의 (현재 시점에서 누구보다도 가장 오래 참여한) 메인테이너로서 겪은 경험에 따라서 작성했다.

오픈소스 소프트웨어 메인테이너에게 번아웃은 큰 문제다. 이 문제는 피할 수 있다. 메인테이너는 오픈소스 프로젝트를 즐겁고 건강하게, 오랜 기간동안 생산적으로 참여할 수 있다. 어떻게 가능할까? 바로 다른 메인테이너, 기여자, 또는 소프트웨어를 사용하는 사용자에게 의무감을 갖지 않는 것이다. 이 프로젝트가 개인적으로 이득을 보고 있는 상황도 상관 없다. (예를 들어 자기 홍보 가치가 있다거나 기부를 받고 있는 상황에도.)

메인테이너가 의무를 갖지 않는다는 근거를 명시한 경우가 있는가? 실제로 존재한다. 오픈소스 라이센스 자체에 말이다. 먼저 깃헙에서 가장 인기있는 오픈소스 라이센스MIT 라이센스를 보자.

이 소프트웨어는 상품성, 특정 목적 적합성, 그리고 비침해에 대한 보증을 포함한 어떠한 형태의 보증도 명시적이나 묵시적으로 설정되지 않은 “있는 그대로의” 상태로 제공된다. 소프트웨어를 개발한 프로그래머나 저작권자는 어떠한 경우에도 소프트웨어나 소프트웨어의 사용 등의 행위와 관련하여 일어나는 어떤 요구사항이나 손해 및 기타 책임에 대해 계약상, 불법행위 또는 기타 이유로 인한 책임을 지지 않는다.

이 난해한 법률 용어를 평범한 문장으로 살펴보자. (참고로 나는 변호사가 아니다):

  • 지금 제공하는 소프트웨어는 전부 메인테이너가 제공한다는 점에 동의한 것이다 (즉, 버그를 포함한 모든 것을 뜻한다)
  • 메인테이너는 모든 사용자와 사용 사례에서 동작하는 것을 보장하지 않는다 (문서화되어 있더라도 말이다)
  • 메인테이너는 어떤 소프트웨어 사용에서든 발생하는 어떤 문제에도 법적인 책임이 없다. (당신이 입은 피해를 고치기 위해 돈을 지불하는 경우에도)
  • 소프트웨어를 어떤 방식으로든 사용하려면 위 내용에 대해 필수적으로 동의해야 한다.

이 내용은 단순히 MIT 라이센스만 그런 것이 아니다. BSD 2-Clause, GPLv3, MPL v2.0, Apache v2.0, Unlicense 그리고 거의 대부분의 다른 오픈소스 라이센스에도 법적 책임에 대한 한계를 두고 어떤 보증도 명시적으로 제공하지 않는다. 요약하면, 어떤 오픈소스 소프트웨어를 사용했고 이 소프트웨어가 실서버의 모든 데이터를 지워버렸다고 해도 어느 누구에게도 소송을 걸 수 없다. 소프트웨어를 사용하는 것으로 어떤 부정적 결과도 개인의 책임이라고 동의했기 때문이다.

하지만 실제로는 어떤 문제가 발생하면 메인테이너는 문제를 빠르게 해결해주기도 하고 마치 회사처럼 사과를 하기도 한다. 이 부분이 번아웃의 가장 큰 원인이다. 대다수의 오픈소스 소프트웨어는 자유 시간의 자원 봉사로 개발된다. 하지만 오픈소스 메인테이너와 사용자가 그 관계를 회사/고객 같은 관계로 생각한다면 이는 지속하기 어렵다.

대다수의 메인테이너가 오픈소스 소프트웨어에 참여하기 시작한 이유는 재미도 있고 자신의 문제를 해결하고 있기 때문이다. 재미 대신 말도 안되는 의무만 지속되는 상황에서, 돈도 못버는데 지나치게 시간을 쏟고 재미 없는 일만 증가한다면 그건 메인테이너를 갈아넣는 일이다. 이런 상황에서 논쟁이 있을 만한 결정을 내리거나 그런 과정에서 모욕적인 얘기를 들으면 그 친구나 가족은 분명 “오픈 소스가 이런 고통을 감내할 만큼 가치있는 일이냐”고 질문할 것이다.

이 문제를 어떻게 고칠까? 오픈소스 생태계에 있는 모두가 오픈소스의 법적 현실을 수용하는 것이 답이다. 이 답을 실제로 적용하면 이런 의미다.

  • 메인테이너: 프로젝트에서 더이상 즐거움을 느끼지 못한다면 더 이상 하지 않는다. 사용자가 프로젝트에 대해 이야기할 때 무례하게 군다면 접근을 막아버리고 그 사람들이 요청하는 것을 하지 않는다. 버그를 만들었다고 미안해 하지 않는다. 모든 소프트웨어에는 결함이 있기 마련이다. 세상에 무언가를 내놓고도 어떤 댓가도 바라지 않았다는 점에서 스스로를 자랑스러워 하자.
  • 기여자: 결정은 메인테이너가 내리도록 하고 모든 관련 기여 문서를 읽었는지 확인한다. 메인테이너는 프로젝트를 운영하는 사람이고 궁극적으로 그들의 단어가 남게 된다. 어떤 식으로 프로젝트가 동작하는지 (사실, 무엇이든지) 당신에게 가르치기 위해서 메인테이너가 존재하는 것이 아니다.
  • 사용자: 프로젝트에 이슈를 생성하거나, 풀리퀘스트를 열거나 덧글을 작성할 때는 사람들이 자신들의 시간을 써서 소프트웨어를 만들고 무료로 사용하게 했다는 점에 감사하는 마음을 담아야 한다. 불만이든 어떻게 할 수 없는 부정적인 것들은 본인의 몫이다. (적어도 보이는 곳에서 말하지 말아야 한다.) 본인 스스로에게 더 시간을 들여 문제를 풀지 않으면서 다른 사람에게 물어서 해결하려고만 한다면 다른 사람이 그 문제를 해결해줄 거라고 기대하지 말아라. 즉, 어떤 도움이든 물어보기 전에 모든 문서를 읽어보고 스스로 문제를 해결하려고 노력하라는 의미다.

이런 역할을 모두 지킨다면, 메인테이너가 실제로 있지도 않은 의무에 치여서 프로젝트가 접히는 경우가 더 적어질 것이다. 오픈소스 생태계에서 본인이 어떤 역할로 참여하고 있는지 잘 인지한다면 더 행복한 메인테이너, 기꺼이 돕는 기여자와 감사하는 사용자가 될 수 있을 것이다.

최근 emacs에 대한 유튜브를 보는데 전혀 IT쪽 일을 하지 않는 사람도 org-mode 때문에 emacs를 사용한다는 얘기를 듣고 문서를 찾아보게 되었다.

emacs는 아예 처음 사용해보는데 이 문서에 emacs 기능도 간략하게 설명하고 있어서 org-mode의 기능을 살펴보는데 불편함이 없었다. 각각 기능도 강력하고 일반 텍스트를 이렇게 멋지게 조작 가능한게 놀랍다. markdown이나 html로 내보내는 기능도 멋지다. 내보내는 포맷은 플러그인으로 추가도 가능하다. 익숙해지면 정말 유용할 것 같다.

이 번역 문서는 GNU 자유 문서 사용 허가서 버전 1.3 또는 그 이후의 버전이 적용된다. 코드 예제는 GNU GPL 버전 3 또는 이후 버전이 적용된다. 원본은 Org mode beginning at the basics 페이지에서 찾을 수 있다.


Org-mode 기초부터 시작하기

Org-mode는 공식 웹페이지에서 설명하는 것과 같이 노트와 할 일 목록을 관리하고 프로젝트 계획을 작성하거나 저작하는데 사용할 수 있는, 빠르고 효과적인 플레인 텍스트 시스템입니다. Emacs 22.2와 XEmacs 22.1부터 지원하기 시작했습니다. 아래 내용은 간단한 튜토리얼로 Emacs와 org-mode를 사용하는 방법을 설명합니다.

Emacs에 대해 알아야 할, 정말 최소한의 지식

무엇이든지 하고싶은 일을 하기 위해서 Emacs에서 필수적으로 알아야 할 최소 지식은 다른 애플리케이션에서 알아야 하는 양보다 많습니다. 하지만 이런 비교는 일반적인 장난감과 레고를 비교하는 것과 같습니다. 레고는 시작하기 어렵지만 (작은 플라스틱 조각이 가득한 상자에서 시작합니다) 장기적으로 봤을 때는 더 많은 것을 만들 수 있습니다.

Emacs는 단축키가 풍부합니다. 처음 시작할 때는 이런 특징에 짜증날지 모르지만 시간이 흐를수록 마우스를 점점 적게 사용하게 되고 실제로 더 빠르게 작업을 할 수 있게 될겁니다.

기본적인 모든 동작은 마우스를 사용할 수 있습니다. 파일을 열거나 저장하는 등의 작업은 메뉴에서 모두 가능한 동작입니다. 하지만 키보드에서 손을 때고 마우스를 잡는 방법보다 단축키를 사용하는 방식이 훨씬 빠르다는 것을 알게될 것입니다.

Emacs는 이중 단축키를 많이 사용합니다. 대부분의 애플리케이션처럼 Alt-F 나 Alt-S 대신에 Control-X Control-FControl-X Control-S 를 사용합니다. 처음에는 생산성에 반하는 것처럼 느껴질지 몰라도 금방 익숙해질 겁니다.

노트: 키 축약 표현

  • M – Alt (고대 키보드에서는 Meta 키였습니다.)
  • C – Control
  • S – Shift
  • C-x f – 이 표기는 Control x를 누른 후, 둘 다 손을 땐 다음에 f를 누른다는 의미입니다.

어느 버전의 Emacs를 사용해야 하나요?

어느 버전이든 다 똑같이 느껴진다면 XEmacs보다 Emacs를 선택하기 바랍니다. (이 말에 동의하지 못한다면 이미 이 문장을 넘겨도 될 만큼 알고 있다는 의미입니다.) 다음 링크가 도움이 될 겁니다.

리눅스에서는 패키지 매니저를 사용해서 Emacs를 설치합니다. 데비안이라면 다음처럼 설치합니다.

sudo apt-get install emacs

설정하기

Emacs를 시작할 때 가장 큰 고통은 바로 설정하기에 있습니다. 설정을 위한 메뉴도 없고 텍스트 파일을 수정해야 합니다. (메뉴가 있다고 얘기하긴 하는데 그건 그냥 순수한 사람들을 낚는 겁니다.) 설정 파일의 위치는 (심지어 이름까지도) 어떤 운영체제를 사용하느냐에 따라 다릅니다. 하지만 플랫폼에 상관없이 그 내용은 거의 일치합니다. 대다수 사람들은 동일한 설정 파일을 다른 운영체제서 사용합니다. 장기적으로 보면 최고의 선택이나 마찬가지죠!

설정 파일의 위치는 다음과 같습니다.

org-mode 시작하기

이 챕터에서 사용하는 새로운 단축키는 다음과 같습니다.

  • C-x s – 문서 저장하기
  • C-x f – 문서 열기

첫 org-mode 문서

이제 첫 org-mode 문서를 시작하기 위해서 알아야 할 모든 지식을 습득했습니다. Emacs를 시작합니다. 완전히 새로 설치한 Emacs라면 Emacs의 스플래시 화면을 볼 수 있을 겁니다. 이 화면에는 Emacs 튜토리얼과 여러 문서를 볼 수 있는 바로가기가 있지만 지금은 건너뛰고 다음으로 넘어갑니다.

C-x f 단축키를 사용해서 새 문서를 시작합니다. 이 단축키는 문서를 열기 위한 기능을 제공합니다. (Emacs에서는 버퍼라고 말합니다.) 여기에 *1.org*라고 입력합니다. 이제 새로운 빈 문서가 화면에 나타납니다.

이 문서를 저장하기 위해서 저장 아이콘을 누르거나 C-x s 단축키를 누르고 1.org를 입력합니다.

Emacs는 org-mode 문서를 편집하려고 한다는 점을 아직 이해하지 못합니다. 현재 문서에서 org-mode를 활성화하려면 다음 명령을 입력합니다.

M-x org-mode

이 명령으로 현재 문서에서 org-mode를 활성화 할 수 있습니다.

Emacs가 org-mode 문서를 인식하도록 다음 내용을 문서 상단 에 추가합니다.

MY PROJECT -*- mode: org -*-

여기에 사용한건 뺄셈 기호며 밑줄이 아닙니다. MY PROJECT는 문서의 제목이며 마음대로 지정할 수 있습니다.

이 한 줄의 내용이 이 문서에서 org-mode를 활성화합니다. 이 내용을 넣으면 파일 확장자와 상관없이 org-mode가 동작합니다.

org 파일에서 항상 org-mode를 활성화하려면 Emacs 설정을 수정해야 합니다. 수정하는 방법은 다음 섹션에서 설명합니다.

Emacs 설정 처음으로 수정하기

Emacs 설정 파일을 엽니다. Emacs에서 이 파일을 열기 위해서 C-x f 를 사용합니다. (설정 파일의 경로는 설정을 참고합니다.) 그리고 다음 내용을 추가합니다.

;; -*- mode: elisp -*-

;;스플래시 화면을 끔 (다시 켜려면 t를 0으로 변경)
(setq inhibit-splash-screen t)

;;문법 강조를 활성화
(global-font-lock-mode t)
(transient-mark-mode 1)

;;;;org-mode 설정
;;org-mode 활성화
(require 'org)
;;org-mode를 .org로 끝나는 파일에서 활성화
(add-to-list 'auto-mode-alist '("\\.org$" . org-mode))

Emacs를 재시작합니다.

노트: 앞에서 추가했던 mode 행은 1) Emacs의 설정에 지정한 org-mode 확장자와 다른 확장자를 사용하는 경우 (예로 myfile.txt) 2) auto-mode-alist 행을 설정에 추가하지 않은 경우에만 필요합니다.

목록과 노트 관리하기

이 장에서 사용하는 단축키입니다.

  • TAB / S-TAB – 접기/펴기
  • M-up/down – 제목행 위/아래로 이동하기
  • M-left/right – 제목행 수준 높이기/낮추기
  • M-RET – 새 제목행 추가하기
  • C-x s – 파일 저장하기
  • C-h t – Emacs 튜토리얼

이제 org-mode 문서를 사용할 수 있도록 설정한 Emacs가 있으니 이제 시작하기만 하면 됩니다. org-mode를 시작하는데 도움이 될 개요를 적는 것으로 시작합니다. 새 문서를 열고 (C-x b) 2.org를 입력한 다음에 아래 내용을 복사해서 붙여넣습니다.

#-*- mode: org -*-
#+STARTUP: showall

* org-mode 시작을 환영합니다
  Org-mode에 오신 것을 환영하고 감사드립니다. org에서 개요를 작성하기는 매우 간편합니다.
  그냥 텍스트거든요! 그저 입력하면 됩니다.

* 제목행은 하나 이상의 별 문자로 시작합니다.
  제목은 별 하나, 부제목은 별 두 개 방식으로 숫자를 늘려갑니다.

* 목록 작성하기
** 개요 이동하기
** 제목행 이동하기

파일을 2.org로 저장합니다. (C-x s) 저장하면 문법 강조가 켜져있어서 색상이 변경되는 것을 확인할 수 있습니다. Emacs가 org-mode에서 작업중인 것을 알고 있기 때문입니다.

이제 정말로 org-mode를 시작할 준비가 되었습니다!

목록 작업하기

목록은 브레인스토밍과 모든 항목을 관리하는데 뛰어난 방식입니다. 목록을 사용하면 기록을 하는 동안 큰 그림 그리기에 집중할 수 있도록 돕습니다.

가장 먼저 할 일은 접기입니다. 특히 문서가 길어질 때는 이 기능이 유용합니다. 예제 문서에서 첫 제목행인 이동합니다. (방향키를 사용하세요.) org-mode 시작을 환영합니다 에 커서를 위치한 후에 TAB 을 눌러봅니다. 그리고 S-TAB 도 사용해봅니다. Tab 은 현재 부분을 접거나 펼 수 있습니다. 시프트 키와 함께 누르면 전체 문서를 접고 펼 수 있습니다.

브레인스토밍의 기본적인 아이디어는 항목을 목록으로 적는 것입니다. 적은 후에 항목의 순서를 중요도 순서로 다시 정렬하고 싶을 것입니다. 제목행을 위로, 또는 아래로 이동하기 위해서 제목행에서 M-up/down 을 사용할 수 있습니다. 목록을 모두 접어서 제목만 보이는 상태는 전체 개요를 파악하는데 도움됩니다. 동시에 세부적인 내용도 잃어버리지 않고 잘 보존하고 있습니다.

다음으로 제목 계층을 올리거나 낮출 수 있습니다. 예를 들어 제목행은 하나 이상의 별 문자로 시작합니다. 행을 목록 작성하기 의 부제로 만들고 싶다면 제목의 위치를 아래로 이동한 후에 M-right 으로 계층을 낮출 수 있습니다.

마지막으로 새 제목행을 추가하기 위해서는 M-RET 을 사용합니다.

목록은 순서 없는 목록과 순서 있는 목록이 있습니다. 다음을 확인하세요.

** 반지의 제왕
   가장 좋아하는 장면 (순서대로)
   1. 로히림 전투
   2. 에오윈과 마술사왕의 싸움
      + 이 내용은 이미 책에서도 가장 좋아하는 장면
      + 미란다 오토를 정말 좋아함
   3. 레골라스에게 화살 맞는 피터 잭슨
      - DVD에만 있음
      화살 맞을 때 정말 웃긴 표정이었음.
   하지만 결과적으로 각각의 장면이 아니라 영화 전체가 좋았다.
   영화에서 중요했던 배우:
   - 일라이저 우드 :: 프로도 역
   - 숀 애스틴 :: 샘 역, 프로도 친구로 나옴. 여전히 그를 구니스에서 나온 미키 월쉬로
     잘 기억하고 있음.

순서 없는 목록은 -, + 또는 \*로 시작합니다. 순서 있는 목록은 숫자와 점으로 시작합니다. 설명은 ::이 붙습니다.

더 보기: 이 스크린캐스트 에서는 일반 목록의 몇 가지 기능을 설명합니다. 이 내용은 메뉴얼 에서도 볼 수 있습니다.

기록하기

내용을 작성할 때 쓸 수 있는 몇 가지 표준적인 마크업이 있습니다. 다음과 같은 마크업을 사용할 수 있습니다.

단어에 *굵게*, /기울임꼴/, _밑줄_, =코드=, ~요약~ 등의 마크업을 쓸 수 있다. 꼭 필요한 경우 +삭제선+도 가능하다.

다음처럼 표현됩니다.

단어에 굵게, 기울임꼴, 밑줄, 코드, 요약 등의 마크업을 쓸 수 있다. 꼭 필요한 경우 삭제선 도 가능하다.

여기까지 봤다면 Emacs 튜토리얼을 살펴보는 것도 좋습니다. C-h t 로 Emacs에 내장되어 있는 튜토리얼을실행할 수 있습니다. 튜토리얼은 몇 가지 Emacs 단축키와 함께 어떻게 문서를 이동하는지 알려줄 것입니다.

할 일 항목 사용하기

이 챕터에서 사용하는 새 단축키입니다.

  • S-left/right – 워크플로를 변경
  • C-c C-v – 현재 문서에 있는 할 일 목록 보기

기본 할 일 기능

org-mode를 사용하는 가장 큰 이유는 할 일을 관리하는데 사용하기 위해서 입니다. 먼저 할 일 기능을 사용하기 위해서는 특별히 해야 하는 작업 없이 TODO 키워드를 제목행에 추가하면 됩니다.

** TODO 비행기 구입하기

할일 목록을 빠르게 사용하려면 다음 단축키를 사용합니다.

  • S-left/right

이 단축키는 TODO 에서 DONE 까지, 그리고 빈 칸을 순환하며 상태를 변경합니다.

큰 문서를 작업하고 있고 문서 곳곳에 할 일 목록이 흩어져 있는 상황에서는 C-c / t 로 현재 할 일 항목만 남기고 나머지는 모두 접을 수 있습니다.

할 일 설정하기

  • 파일 내에서 설정하기

    Org-mode 파일에서는 워크플로 상태를 파일 시작 위치에서 설정할 수 있습니다. 다음과 같이 파일 시작부에 작성합니다.

    #+TODO: TODO IN-PROGRESS WAITING DONE
    

    이 행은 파일 상단에 위치해야 하며 상단과 #+TODO 행 사이에 빈 행이 없어야 합니다.

    새 워크플로를 활성화하기 위해서는 파일을 새로 열거나 파일 최상단에 #로 시작하는 행으로 이동한 다음에 C-c C-c 를 입력합니다.

    워크플로를 복사해서 테스트 파일인 1.org에 붙여놓고 차이를 확인해봅시다.

  • Emacs 설정 파일에서

    워크플로 상태를 모든 org 파일에 직접 추가하는 방법은 번거롭습니다. 어디서나 사용할 수 있도록 설정하려면 설정 파일에 추가합니다. 다음 내용은 (require 'org) 행 이후에 추가합니다.

    (setq org-todo-keywords
      '((sequence "TODO" "IN-PROGRESS" "WAITING" "DONE")))
    

    워크플로 상태를 활성화하기 위해서 Emacs를 재시작합니다.

아젠다

이 장에서 사용하는 단축키입니다.

  • C-c a – 아젠다
  • C-c [ – 아젠다 파일 목록에 문서를 추가
  • C-c ] – 아젠다 파일 목록에서 문서를 제거
  • C-c . – 일자 추가
  • C-u C-c . – 일자와 시각 추가
  • C-g – 하던 일을 멈추고 벗어남

아젠다(agenda)라는 단어의 기본적인 의미는 완료 해야 할 것 으로 라틴어인 agendum 에서 왔습니다. Org-mode는 다양한 종류의 아젠다, 일감 목록을 만들고 하나 또는 여러 org 문서에서 이런 일감을 수집하기 매우 좋습니다.

활성화된 모든 일감 목록 생성하기

1.org를 기본 아젠다 파일로 사용하고 나중에 Emacs의 설정 파일에서 어떻게 동작하는지 살펴보겠습니다.

1.org를 엽니다. C-c a 를 눌러서 아젠다를 엽니다. 아젠다는 다음처럼 나옵니다.

Press key for an agenda command
-------------------------------
a Agenda for the current week or day
t List of all TODO entries

위 내용은 일부 내용이고 실제로는 더 많은 내용이 출력될 것입니다.

아쉽게도 위 두 항목은 빈 목록을 보여줄 것입니다. (직접 눌러서 확인해볼 수 있습니다.) 그러므로 이 상태에서 C-g 를 눌러 빠져 나옵니다. 이제 1.org를 아젠다 파일로 추가하기 위해서 C-c [ 를 사용합니다. 이제 아젠다 메뉴로 가서 (C-c a) t 를 누르면 모든 할 일 항목 목록을 확인합니다.

할 일 항목 사용하기에서 설명한 방식대로 더 적절한 워크플로를 추가하는 과정을 했다면 DONE을 제외한 모든 항목이 모두 나타나는 것을 볼 수 있습니다.

이 과정은 여러 문서에서 반복할 수 있습니다. 아젠다는 할 일 전체 목록을 제공할 것입니다. 만약 문서를 아젠다 파일 목록에서 빼고 싶다면 C-c ] 를 사용하면 됩니다.

약속과 마감 일시

일반적으로 시간 설정이 필요한 일감은 달력에 표시합니다. org-mode는 이 방식도 지원합니다. 아젠다는 모든 할 일을 시간 기반 목록으로 볼 수 있습니다. 다음 내용을 참고하기 바랍니다.

1.org에 새 (부)제목을 프레드에게 전화하기 라고 추가합니다. (M-RET프레드에게 전화하기) 다 입력한 후에 C-c . 를 입력합니다. 이 명령을 입력하면 화면 밑에 일자 선택지가 표시됩니다. 직접 손으로 입력할 수도 있고 S-left/right 으로 선택할 일자를 변경할 수 있습니다. 만약 일자 외에 시간도 추가하고 싶다면 C-c . 대신에 C-u C-c . 를 입력합니다.

이제 아젠다 (C-c a)로 이동해서 a 를 누르면 아젠다 항목을 확인할 수 있습니다.

더 읽을 거리:

Emacs 설정 파일에서 아젠다 설정하기

C-c [ 을 사용해서 아젠다 목록에 추가한 후에 Emacs의 설정 파일을 확인하면 다음 내용을확인할 수 있습니다.

(custom-set-variables
  ;; custom-set-variables was added by Custom.
  ;; If you edit it by hand, you could mess it up, so be careful.
  ;; Your init file should contain only one such instance.
  ;; If there is more than one, they won't work right.
 '(org-agenda-files (quote ("~/Documents/Projects/org4beginners/2.org"
 "~/Documents/Projects/org4beginners/1.org"))))
(custom-set-faces
  ;; custom-set-faces was added by Custom.
  ;; If you edit it by hand, you could mess it up, so be careful.
  ;; Your init file should contain only one such instance.
  ;; If there is more than one, they won't work right.
 )

Emacs lisp의 세계에 오신 것을 환영합니다. Emacs가 설정 파일을 변경한 경우에 이런 방식으로 기록됩니다. (참고: Aquamacs 에서는 customizations.el이라는 별도 파일에 저장됩니다.)

여기서 중요한 내용은 중간에 있는데 (5, 6행) org-agenda-files 내용을 확인할 수 있습니다. 아젠다 목록을 만들기 위해 사용하는 아젠다 파일의 목록이 여기에 지정되어 있습니다. 지금은 일단 그대로 둡니다. 다음에 설정 파일을 살펴볼 일이 있다면 적어도 이게 무슨 기능을 하는지 알 수 있을 것입니다.

더 읽을 거리: 사용자 정의 아젠다 명령

GTD

이 챕터에서 사용하는 단축키입니다.

  • C-c C-c – 태그 추가

Getting things done 은 유명한 시간 관리 방법 중 하나로 구글에서 검색하면 1억 5천 여 항목에 이릅니다. 태그를 사용하면 org mode에서도 비슷한 방식으로 할 일 관리를 할 수 있습니다.

태그는 다른 종류의 할일 목록을 조직화하는데 사용합니다. 예를 들면 전화 일정, 읽을 책 목록, 장바구니 목록을 묶기 위해 씁니다.

태그를 추가하기 위해서 다음 설정을 문서 상단에 추가합니다.

#+TAGS: { @OFFICE(o) @HOME(h) } COMPUTER(c) PHONE(p) READING(r)

문서를 다시 불러오거나 #으로 시작하는 곳에서 C-c C-c 를 입력합니다.

이제 문서 어느 행에서든 하나 또는 그 이상의 태그를 등록할 수 있습니다. C-c C-c 를 누르면 다음과 같은 팝업이 나타납니다.

Inherited:
Current:
{ [o] @OFFICE     [h] @HOME    }
  [C] COMPUTER   [p] PHONE   [r] READING

문서 서두에 정의한 태그를 사용할 수 있는 바로가기입니다. 첫 두 태그는 (OFFICE와 HOME)은 상호배타적이라 둘 중 하나만 선택할 수 있지만 나머지는 자유롭게 추가할 수 있습니다.

GTD 설정의 좋은 예제로는 이 글을 참고하세요: Emacs에 Org-mode를 사용해서 GTD 구현하기

Emacs 설정 파일에 태그 추가하기

Emacs 설정 파일에 태그를 추가하려면 다음처럼 설정에 입력합니다.

(setq org-tag-alist '(("@work" . ?w) ("@home" . ?h) ("laptop" . ?l)))

설정 파일에서 상호배타적인 태그 그룹을 만들기 위해서는 메뉴얼 내용을 참고합니다.

이 설정은 문서 상단에 추가한 내용으로 덮어쓰는 것이 가능합니다. 그래서 각 문서에 개별적인 워크플로와 태그를 직접 설정해서 사용할 수 있습니다.

태그를 활발히 활용하는 예제는 여기서 확인할 수 있습니다.

내보내기

여기서 사용하는 단축키는 다음과 같습니다.

  • C-c C-e – 내보내기 메뉴

org-mode에서만 문서 작업을 한다면 큰 문제가 없습니다. 하지만 간혹 다른 포맷으로 문서를 내보내야 할 필요가 있습니다.

예를 들어 현재 문서를 내보내는데 html로 내보내려고 합니다. C-c C-e 를 누른 후에 h o 를 순서대로 누릅니다. 이 방법은 문서를 html로 내보낸 후, 그 내보낸 파일을 브라우저로 열게 됩니다.

더 읽을 거리: html 출판 튜토리얼을 보면 여기서 설명한 내용보다 더 상세하게 다룹니다. 이 방법으로 완전한 웹사이트를 출판하는 것도 가능합니다. 그리고 메뉴얼에서 html, latex, pdf 그리고 다른 포맷으로 내보내는 방법을 설명합니다.

org-mode에 능숙해지기

효율적인 도구를 사용해서 시간을 아끼려면 그 도구를 잘 알아야 합니다. org-mode를 잘 알기 위해서는 메뉴얼을 읽고 사용하는게 중요합니다. Org-mode는 문서화가 잘 되어 있습니다. 가장 빠르게 org-mode 문서를 Emacs에서 읽는 방법으로는 info browser를 사용할 수 있습니다.

이 창을 호출하기 위해서는 C-h i를 입력합니다. 그리고 링크 간 이동하기 위해서 TAB 키를 사용합니다.

info-browser를 이동할 때는 다음 키를 쓸 수 있습니다.

  • u – 위로(up)
  • n – 다음(next)
  • p – 이전(previous)

org-mode 메뉴얼 다음으로는 worg 웹사이트가 있습니다. 여러 재밌는 아이디어와 튜토리얼을 찾을 수 있습니다.

기능을 빠르게 참고할 수 있는 org-mode 치트시트와 emacs 치트시트가 있습니다. 이 두 문서는 귀찮은 단축키를 기억하는데 도움이 될 것입니다.

기초를 넘어서

Geek 유머에 "여기에 용이 있어요!"가 있습니다. 여기서부터는 org-mode를 자유롭지만 스스로 책임져야 하는 사용법을 설명합니다. 대부분 다음 내용은 실제로 정말 어렵거나 한 것은 아니지만 적어도 중요한 데이터는 백업을 해두시기 바랍니다. 만약 다음 내용에서 궁금한 부분이 있다면 메뉴얼과 질문 답변을 확인합니다. 또한 IRC (freenode의 #orgmode)에서 질문하는 것도 좋은 방법입니다.

TODO Quickly adding tasks with remember

(역주: 아직 내용이 존재하지 않는 항목입니다.)

최신 버전 org-mode 사용하기

여기서 사용하는 명령입니다.

  • M-x org-reload – 업데이트 후 org-mode를 다시 불러오기
  • M-x org-version – org-mode 버전 확인하기

Emacs가 업데이트 되는 속도보다 org-mode가 더 빠르게 개발되는 것을 아마 알 수 있을겁니다. 게다가 매일 org-mode의 개발 버전을 받아서 사용하는 것도 가능합니다.

어떻게 가능할까요?

  1. git 설치하기 org-mode 튜토리얼에서 다룰 부분은 아니지만 다음 내용을 참고하세요.
    sudo apt-get install git
  2. org-mode의 코드를 어디에 저장할지 결정합니다. 여기서는 ~/Build/Emacs/org-mode 에 저장하지만 어디 하나 별 차이가 없으니 편한 위치에 저장하기 바랍니다.
  3. org-mode의 최신 버전을 받습니다.
    mkdir ~/Build/Emacs
    cd ~/Build/Emacs
    git clone git://orgmode.org/org-mode.git
    cd org-mode && make && make doc
    
  4. Emacs-init 파일에 다음 내용을 추가합니다.
    (setq load-path (cons "~/Build/Emacs/org-mode/lisp" load-path))
    (setq load-path (cons "~/Build/Emacs/org-mode/contrib/lisp"
    load-path))
    

    (require 'org-install)

    중요! 일반 버전의 org-mode라면 다음을 사용합니다.

    :(require 'org) 다음을 사용한다면 위 행은 반드시 제거해야 합니다.

    :(require 'org-install)

  5. 최신 org-mode를 유지하려면 다음 명령을 사용합니다.
    cd ~/Build/Emacs/org-mode
    git pull && make clean && make && make doc
    
  6. org-mode를 다시 불러옵니다. M-x org-reload 또는 Emacs를 재시작하세요.

어떤 버전의 org-mode를 사용하고 있는지 확인하려면 M-x org-version 을 입력합니다.

얼마 전에 중고 Dell Latitude E7240을 100달러 주고 구입했었다. cpu는 i3지만 램도 넉넉하고 ssd도 달려 있어서 그런지 개발 장비로도 큰 무리 없을 것 같아 장만했다. 리눅스 머신을 갖고 싶었는데 너무 적절한 노트북이였다.

디스플레이가 TN패널인 게 단점이었다. 검색해보니 시중에 IPS 모델도 있기도 했고 그러면 IPS 패널만 사다가 교체하면 되는 것 아닐까 싶었다. 찾아보니 실제로 구입해서 교체한 글도 찾을 수 있었다.

문제는 그 검색했던 스레드에 페이지네이션이 너무나도 작았고 그 뒤에 더 많은 이야기가 있다는 점이었다. 그 사실을 알리익스프레스에서 구입하고 패널을 받고 교체하기 위해 노트북을 다 뜯고나서, 패널이 맞지 않는다는 사실을 알고 나서야 알게 되었다. 세상에. 그래서 다른 패널을 또 다시 주문했고 오늘 드디어 도착해서 교체했다.

새로 구입한 패널은 LP125WH2-SPM1 이다. 이 패널도 완전하게 100% 맞지는 않는다. 원래 패널보다 몇 mm 정도 넓어서 상판 지지대 같이 생긴 부분에 닿는다. 프레임 커버가 엄청 헐렁한 플라스틱 소재고 조립해도 크게 문제가 생기지 않을 것 같아서 그대로 두고 조립했다.

일단 IPS라서 시야각이 전혀 없다시피 하고 밝기도 훨씬 개선되었다. 이제는 유튜브 틀어도 전혀 거슬리지 않는다. 기존 TN 패널보다 한 2mm 정도 padding이 있어서 프레임에 딱 맞지 않는다. 워낙 화면이 좋으니까 별로 문제 없다.

뜯는 방법은 유튜브 영상을 보고 했다. 후면만 뜯으면 패널을 쉽게 교체할 수 있다.

구입은 알리익스프레스에서 했고 패널 정보는 panelook.com에서 확인했다. 구입 전에 패널 이름, 디스플레이 실측 크기와 edp의 lane을 잘 확인하자. 패널 이름이 일반적으로 규격 자체를 의미하는 경우가 많은 것 같다. 그리고 edp의 lane이 다른 경우에는 edp 케이블도 구입해야 하고 뜯어야 하는 범위도 커지니 주의하자. 그리고 가장 큰 복병이 패널 하단에 있는 PCB다. 사진 보면 검정 테이프로 처리되어 있는데 이 폭이 달라서 장비에 안맞을 수 있으니 구입 전에 잘 본다. 그리고 나사 구멍 수가 다르거나 위치가 다를 수 있으니 잘 확인하자.

패널을 두 차례 구입했어도 총 지출은 노트북 포함 270달러 정도다. 예전에 사용하던 인스피론 모델 최하위 사양이 300달러였던걸 생각하면 뿌듯하다. 이제 열심히 쓰는 일만 남았다!

나도 신입으로 일을 시작했을 때 혼자 인터넷 검색창을 붙들고 코드와 씨름한 경험이 있었다. 작은 회사에서 유일한 개발자라 물어볼 선임도 없었고, 문제는 어떻게든 기간 내에 해결해야 하는 상황이 많았다. 모두가 신입 시절을 거치는 동안 그런 벽을 마주할 때가 있을 것 같다. 넘어야 할 산은 높고 나는 너무나 작게만 느껴지는 그런 경험을 거치고서 각자 지금의 자리에 있지 않나, 생각한다. <바쁜 팀장님 대신 알려주는 신입 PHP 개발자 안내서>라는 책 제목을 보는 순간에 그 신입 당시의 기억이 먼저 떠올랐다.

바쁜 팀장님 대신 알려주는 신입 PHP 개발자 안내서 표지

개발을 코드를 작성해서 실행하는 일이라고 단순하게 설명할 수 있지만, 현실에서는 그렇게 간단하지 않다. 코드를 어떻게 잘 작성하는가도 중요한 주제지만 그만큼 코드를 잘 관리하는 일도 중요하다. 게다가 코드가 실행되는 환경을 이해하는 과정도 필요하다. 환경도 여러 계층에 걸쳐 있다면 두루두루 살펴봐야 한다. 언어도, 환경도 서로 쉽게 이해하고 공유할 수 있도록 다양한 규칙과 규약을 정리해놓고 있고 이런 부분도 숙지해야 한다. 개발이라고 말하기에는 다각적으로 알아야 하는 부분이 많다.

신입으로 첫날 출근하면 이런 지식에 압도당한다. 신입으로 시작할 때 가장 막막한 점은 단순히 언어에 대한 이해를 넘어서 이런 다양한 지식을 한꺼번에 흡수해야 한다는 점이다. 순식간에 넓은 분야를 깊이 있게 이해하는 일은 생각만으로도 벅차다. 알아야 할 모든 내용을 단번에 이해하면 좋겠지만 절대 쉬운 일이 아니다. 그런 막막한 상황에서 가장 중요한 것은 알아야 할 내용의 키워드를 습득하는 것이다. 키워드를 알면 그 키워드를 중심으로 아는 범위를 쉽게 늘릴 수 있다. 그런 점에서 이 책은 폭 넓으면서도 중요한 키워드를 모두 포함하고 있어서 학습에 좋은 가이드가 된다.

일단 PHP를 기준으로 설명하기 때문에 PHP를 사용하는 사람이라면 꼭 알아야 할 내용을 잘 다루고 있다. 그리고 많은 내용을 PHP에 할애하고 있긴 하지만 거기에 덧붙여 지금 시대에 웹개발을 한다면 필수적으로 알아야 할 다양한 키워드를 장마다 풀고 있다. 또한, 각 문제와 주제에 대해 어떤 식으로 접근해야 하는지 복잡하지 않게 설명한다. 실제로 문제를 마주하게 될 때 어떤 식으로 생각하고 찾아봐야 하는지 그 방법도 잘 전달하고 있다.

  • 저장소가 뭔가요? (버전 관리 시스템)
  • 저장소의 소스코드를 받았는데 왜 안되죠? (컴포저)
  • 제 컴퓨터에서는 잘 되는데요? (가상 머신을 이용한 개발 환경 구축)
  • 어떤 파일을 고쳐야 할 지 모르겠어요 (프런트 컨트롤러 패턴과 MVC 패턴)
  • GET, POST는 알겠는데 PUT, DELETE는 뭔가요? (HTTP와 REST)
  • 그렇게까지 해야 하나요? (시큐어 코딩)
  • 그냥 제 스타일대로 하면 안되나요? (코딩 컨벤션과 PHP 표준 권고)
  • MySQLi는 나쁜건가요? (PDO와 ORM)
  • 메모장에 코딩하면 안되나요? (통합 개발 환경)

책을 읽기 시작하자마자 끝까지 고개를 끄덕이며 읽었다. 내가 신입으로 들어갔을 때 이런 책이 있었으면 얼마나 수월하게 배우기 시작했을까. 주변에 PHP를 사용하는 사람이 있다면 꼭 알려주자. 신입 PHP 개발자에게는 어떻게 시작해야 하는지 좋은 가이드가 되고 선임이나 팀장급 이상이라면 어떤 내용을 신입에게 가르쳐야 하는지 명확한 지침이 되는 책이다.

그동안 트위터 공웹을 사용했었는데 트윗덱으로 바꿨다.

  • 공앱이랑 디자인이 거의 비슷하다. 그냥 공앱인데 다른 버전 쓰는 느낌 정도다.
  • 공앱 같은데 광고가 (아직) 없다. 최고.
  • Home을 숨기고 리스트만 보이게 만들었다. 그동안 단축키로 리스트를 왔다갔다 했는데(G-i) 리스트 오가는 귀찮음이 줄었다.
  • 키워드 뮤트도 가능하고, 각 리스트마다 별도의 exclude를 지정할 수 있어서 추가적으로 더 걸러낼 수 있다.
  • 순서라든지 크기라든지 리스트, 노티, 검색결과 등 개인화 할 수 있는 부분이 많아 좋다.
  • 다른 계정 같이 사용하는 경우에 편하다.
  • Ready to Tweet? 체크박스를 활성화해서 쓰는데 쓰기 전에 맞춤법도 다시 보고 트윗 계정이 맞는지도 보기 쉽고 좋다.
  • 의외로 리트윗 숫자랑 마음 숫자가 바로 보이는 것이 정보를 읽는데 있어서 편향을 만드는 것 같다. 별거 아닌데도 숫자가 크면 왠지 읽어야만 할 것 같은 기분도 들고 그러는데 트윗덱은 그런 표시가 목록서는 보이지 않는다.
  • 예약 트윗이 가능하다. 아직 쓰진 않았지만 이거 하려고 buffer 썼던거 생각하면 편리하다.

계정 표시 방법도 3개나 제공하고.

얼마 전에 트위터가 처음으로 흑자 냈다는 얘기가 있었는데 계속 잘 되었음 좋겠다.

최근 php로 cli 도구를 만드는 프로젝트를 했다. 타겟 서버에 접속하는 권한이 제한적이고 나도 프로젝트 스코프 내에서만 접근 가능한 상황이라 ci를 돌리기 좀 애매해서 bash로만 작성해뒀다. 총 12개의 물리 서버에 도구를 배포하는데 배포가 완료되면 notification을 띄우고 싶어서 찾아봤다.

특별한건 아니지만 그냥 보기 좋으니까. ?

osascript 사용하기

가장 간단한 방법이다. osascript로 노티를 띄울 수 있다. 장점은 1줄이면 된다는 점이다. 단점은 클릭하면 script editor가 열리는 점, 아이콘 변경이 안되는 점이다. bash에서 스크립트를 다음처럼 실행하면 된다.

osascript -e 'display notification "노티 내용" with title "타이틀" sound name "Basso"'

소리명은 ~/Library/Sounds, /System/Library/Sounds에서 찾을 수 있다.

terminal-notifier 사용하기

terminal-notifier를 설치해서 쓰는 방법이다. 아이콘이라든지 마음대로 다 변경할 수 있지만 따로 설치해야 한다. homebrew로 설치 가능하다.

terminal-notifier -title "Hello" -subtitle "코드 배포" -message "배포가 완료되었습니다" -appIcon https://haruair.com/logo.png

node-notifier 사용하기

node로 작성되어 있다면 node-notifier를 사용하면 된다. 멀티플랫폼을 지원한다. mac은 terminal-notifier를 포함해서 배포하기 때문에 terminal-notifier의 기능을 전부 사용할 수 있고 js에서 간단하게 불러낼 수 있다.

const notifier = require('node-notifier');

notifier.notify({
    title: "Hello",
    message: "Hello World!",
});

리액트의 Advanced guides 페이지를 따라하면서 노트한 내용이다. 가이드 쪽은 옴니버스 같은 기분이라서 반반으로 나눠 읽기로 했다. 기록하고 싶은 부분만 남겼기 때문에 자세한 내용은 각 페이지를 참고한다.

Reconciliation

React는 선언형 API를 사용하고 있어서 변경에 대해 일일이 신경쓰지 않아도 된다. 이 가이드에서는 React가 어떤 비교 알고리즘을 사용해서 고성능을 내는지 설명한다.

모든 컴포넌트를 다 새로 그리면 O(n3)인데 다음 두 가정으로 발견적(휴리스틱, heuristic) O(n) 알고리즘을 사용한다. 대부분의 경우는 이 가정에 문제가 없다.

  1. 다른 타입의 두 엘리먼트는 다른 트리를 만듬
  2. 개발자가 key로 힌트를 제공해서 자식 엘리먼트가 반복되는 렌더링에서 안정된 상태인걸 확인할 수 있음

엘리먼트가 갱신될 때, 어떤 식으로 갱신이 발생하는가는 다음과 같다.

  • 다른 타입의 엘리먼트인 경우, 트리 전체를 다시 그림 (언마운트 && 마운트 발생)
  • 동일 타입의 DOM 엘리먼트의 경우, 어트리뷰트만 갱신함, 어트리뷰트 일부만 갱신된 경우 변경된 부분만 갱신 (e.g. stylefontWeight)
  • 동일 타입의 컴포넌트 엘리먼트의 경우, 인스턴스가 유지되며 state도 보존됨. 대신 하위 컴포넌트에는 변경 사항을 componentWillReceiveProps()componentWillUpdate()로 전파함.

자식노드가 갱신될 때는 신경써야 한다.

// 1.
<ul>
  <li>Edward</li>
</ul>

// 2. 뒤로 추가되는 경우에는 기존 엘리먼트가 유지됨
<ul>
  <li>Edward</li>
  <li>Mindy</li>
</ul>

// 3. 앞으로 추가되는 경우에는 노드 전체를 다시 그림
//    당연히 성능 하락 발생하며 컴포넌트 엘리먼트 경우
//    언마운트 마운트하게 된다
<ul>
  <li>Mindy</li>
  <li>Edward</li>
</ul>

// 4. 앞서의 가정 2에 따라서 `key`를 제공하면
//    새로 그리지 않고 반영할 수 있게 됨
<ul>
  <li key="1029">Edward</li>
</ul>

<ul>
  <li key="2012">Mindy</li>
  <li key="1029">Edward</li>
</ul>

id가 없다면 적당히 hash를 생성해서 쓴다. 동일 계층에서만 유일값을 가지면 된다. 최후의 수단은 배열의 index인데 배열 순서가 바뀌지 않는다는 가정이 있어야 한다. 순서가 바뀌면 key를 써도 느리다. 별로 권장하지 않는다.

최종적인 결과는 동일하지만 어떻게 구현되어 있는지 아는 것으로 성능 향상을 할 수 있다. 휴리스틱에 기반한 알고리즘이라서 다음 경우엔 좋지 않다.

  1. 하위 트리의 컴포넌트가 일치하는지 검사하지 않는다. 두 컴포넌트가 비슷한 결과를 낸다면 하나로 만드는걸 고려한다.
  2. 키는 안정적이고 예측 가능하며 유일해야 한다. 이 규칙을 지키지 않으면 성능 하락이나 자식 컴포넌트가 state를 잃어버리는 경우가 발생한다.

Context

props로 일일이 내려주기 번거로울 때 contextTypes로 지정하면 하위 트리에 전역으로 전달된다. prop-types가 필요하다. 흑마법이므로 사용하지 말 것. 실험적인 API로 차후 문제가 될 가능성이 높다. 문서 내내 쓰지 말라는 말이 반복된다.

Fragments

엘리먼트를 반환할 때 컨테이너 역할을 하는 <div> 등을 쓰기 마련인데 테이블 같은 걸 조립할 때는 마크업에 맞지 않다. 이런 경우를 위해 정말 컨테이너 역할만 하는 <React.Fragment>를 사용할 수 있다.

class Columns extends React.Component {
  render() {
    return (
      <React.Fragment>
        <td>Hello</td>
        <td>World</td>
      </React.Fragment>
    );
  }
}

약식 표기로 <>, </>를 사용할 수 있지만 현재 툴에서 지원하지 않을 수도 있으니 위 방식으로 사용한다.

<React.Fragment>로 묶을 때 key도 지정할 수 있다.

function TodoList(props) {
  return (
    <dl>
    {props.items.map(item => (
      <React.Fragment key={item.id}>
        <dt>{item.thing}</dt>
        <dd>{item.done}</dd>
      </React.Fragment>
    ))}
    </dl>
  );
}

Portals

컴포넌트를 부모 컴포넌트의 DOM 트리 바깥에 붙일 때 사용하는 방법이다. 전체화면에 모달 띄우기 이런 동작 필요할 때 쓴다. 아래 코드에서 child는 렌더링 가능한 리액트 엘리먼트고 container는 DOM 엘리먼트다.

ReactDOM.createPortal(child, container)

DOM에서 이벤트 전파(Event bubbling)는 실제 노드를 타고 올라가는 식이지만 포털은 다른 DOM 노드에 위치하고 있더라도 React 컴포넌트의 노드 트리를 타고 전파된다. DOM의 바인딩만 트리 바깥에서 일어나고 실제 모든 컴포넌트는 기존과 동일한 방식으로 동작한다.

에러 바운더리

기존엔 에러가 발생하면 컴포넌트 트리가 멍텅구리 되었는데 에러 바운더리를 사용해서 해결할 수 있다. 오류가 발생했을 때 동작을 componentDidCatch(error, info)에 선언한다. error는 발생한 오류고 infocomponentStack 키를 포함한 객체로 오류 발생 시, 스택 정보를 제공한다.

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  componentDidCatch(error, info) {
    // 오류 발생 UI 표시
    this.setState({ hasError: true });
    // 로그로 남김
    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}
<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>

에러 바운더리는 다음처럼 컨텍스트 바깥에서 발생하는 오류는 잡지 못한다.

  • 이벤트 핸들러
  • 비동기 코드
  • 서버측 렌더링
  • 하위 컴포넌트가 아닌 에러 바운더리 자체에서 발생한 오류

react 16부터는 에러 바운더리로 에러가 잡히지 않았을 때 리액트 컴포넌트 트리 전체를 언마운트한다. 문제 있는 UI를 그대로 두면 사용자의 잘못된 조작을 야기할 수 있기 때문이라고 한다.

create react app 사용하면 스택 추적에서 어디서 오류가 났는지 명확히 보인다. create react app을 사용하지 않는다면 플러그인을 설치하면 된다. 다만 프로덕션에서는 꺼야 한다.

try ... catch는 명령행 코드에서만 동작한다. 이벤트 핸들러의 에러는 이벤트 바운더리에서 잡지 못한다. 이벤트 핸들러의 에러는 try ... catch를 사용한다.

15에서는 unstable_handleError였다고 한다.

웹 컴포넌트

웹 컴포넌트를 react 내에서 사용할 경우에는 일반 DOM 컴포넌트를 사용하는 것처럼 쓸 수 있다. 웹 컴포넌트는 명령형 API를 쓰는 경우가 종종 있는데 ref로 참조를 받아와 DOM 노드를 직접 호출해야 할 수도 있다. 또한 웹 컴포넌트에서 발생한 이벤트가 리액트 컴포넌트에 제대로 전이되지 않을 수 있으므로 직접 핸들러를 연결해야 할 수도 있다.

웹 컴포넌트에서 React 컴포넌트를 사용하려면 ReactDOM.render직접 렌더링 해야한다.

고차 컴포넌트

고차 컴포넌트는 컴포넌트를 입력 받아 새로운 컴포넌트를 반환하는 함수를 의미한다. (고차 함수와 같은 접근 방식이다.) Redux의 connect, Relay의 createFragmentContainer도 동일한 방식이다.

function withSubscription(WrappedComponent, selectedData) {
  return class extends React.Component {
    // ...
  }
}

고차 컴포넌트 내에서 기존 컴포넌트를 변경하지 않도록 주의한다. 고차 컴포넌트 내에서 기존 컴포넌트를 변경하면 추상성이 무너진다. 순수 함수처럼 작성해야 한다. 이 접근 방식은 책임을 분리한다는 점에서 컨테이너 컴포넌트와 유사한 점이 있다.

render() {
  // 불필요한 prop을 제거하거나 추가적으로 필요한 prop을 전달한다.
  const { extraProp, ...passThroughProps } = this.props;
  const injectedProp = someStateOrinstanceMethod;
  return (
    <WrappedComponent
      injectedProp={injectedProp}
      {...passThroughProps}
    />
  );
}

compose 같은 합성 함수를 사용하면 편리하다. lodashRedux, Ramda에서도 제공한다.

고차 컴포넌트에서 반환하기 전에 displayName을 추가하면 디버그를 쉽게 할 수 있다.

function withSubscription(WrappedComponent) {
  class WithSubscription extends React.Component { /* ... */ };
  WithSubscription.displayName = `WithSubscription(${getDisplayName(WrappedComponent)})`;
  return WithSubscription;
}

function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}

고차 컴포넌트에서 지켜야 할 사항이 있다.

  1. render() 내에서는 고차 컴포넌트를 사용하지 않는다. 렌더링 할 때마다 새로운 컴포넌트를 만들고 최악의 경우 노드의 모든 상태를 잃게 된다.
  2. 고차 컴포넌트를 만들 때는 정적 메소드도 직접 복사해야 한다. hoist-non-react-statics 같은 패키지를 사용해도 된다.
  3. 고차 컴포넌트는 ref를 전달하지 못한다. ref는 일반 prop이 아니기 때문인데 부모 컴포넌트에 ref 노출하는 식으로 별도의 prop을 만들어서 전달해야 한다. 깔끔한 해결책은 아니다.

Render Props

Render props은 prop에 엘리먼트를 반환하는 함수를 전달해서 재사용성을 높이는 방법이다. React Routerdownshift에서 사용하는 방식이라고 한다.

<Mouse render={mouse => (
  <Cat mouse={mouse} />
)}/>

구체적으로 구현하는 방식보다 동작을 사용자에게 위임하는 방식으로 구현하는 접근법으로 Mouse 커서 위치를 전달하는 예를 들었다.

이런 방식으로 사용하는걸 render props라고 하지만 꼭 props가 render일 필요는 없다. 패턴 이름일 뿐이다.

설명에는 ShallowEquals 때문에 React.PureComponent에서는 익명함수가 계속 새로 생성된다고 나오는데 내가 테스트를 제대로 못하는건지 React.Component에서 하는거랑 차이가 없어 보인다. 여튼 익명함수를 반복해서 생성하고 싶지 않다면 익명함수 대신 선언한 메소드를 전달해주는 방식으로 해결할 수 있다.

constructor(props) {
  super(props);
  this.renderTheCat = this.renderTheCat.bind(this);
}
// ...
renderTheCat(mouse) {
  return <Cat mouse={mouse} />;
}
// ...
return <Mouse render={this.renderTheCat} />;

다른 라이브러리와 함께 사용하기

DOM을 직접 제어하는 플러그인과 함께 사용하려면 ref로 DOM 엘리먼트를 노출하고 직접 제어한다.

componentDidMount() {
  this.$el = $(this.el);
  this.$el.chosen();
}

componentWillUnmount() {
  this.$el.chosen('destroy');
}
render() {
  return (<select ref={el => this.el = el}>{this.props.children}</select>);
}

다른 뷰 라이브러리와 연동하기에서는 리액트로 포팅하는 방법이랑 Backbone.View에 리액트 컴포넌트를 어떻게 넣는지 설명한다.

React state, flux, redux를 권하긴 하지만 모델 레이어와도 통합이 가능하다.

Backbone의 모델을 컴포넌트에서 사용하려면 backbone에서 사용하는 방식대로 사용하면 되고 Backbone의 모델에서 데이터를 가져오는 방식으로는 고차 컴포넌트 형태로 활용할 수 있다.

설명은 Backbone으로 했지만 여기서 사용한 기법 자체는 제한적이지 않다.

접근성

WAI-ARIA 적용, 시멘틱 HTML, 폼 접근성, 포커스 컨트롤 등 접근성 관련된 내용을 설명하는데 읽어봐야 하는 문서를 전부 나열하고 있다. 리액트에 특정한 부분이 아니라서 각 링크는 본문을 확인한다.

// for 대신 htmlFor를 사용
<label htmlFor="namedInput">Name:</label>
<input id="namedInput" type="text" name="name"/>

포커스 제어는 ref로 직접 DOM을 받아서 처리한다.

코드 분할

import() 등 nodejs에서 사용하는 일반적인 코드 분할 기법을 설명한다.

리액트의 Advanced guides 페이지를 따라하면서 노트한 내용이다. 가이드 쪽은 옴니버스 같은 기분이라서 반반으로 나눠 읽기로 했다. 기록하고 싶은 부분만 남겼기 때문에 자세한 내용은 각 페이지를 참고한다.

JSX in Depth

리액트 엘리먼트 타입 정의

JSX는 React.createElement(component, props, ...children)의 편의 문법이다. 그래서 JSX를 사용할 때는 스코프 내에 React가 꼭 필요하다.

다음처럼 점 표기법을 사용할 수 있다. 한번에 여러 컴포넌트 내보낼 때 편리하다.

const MyComponents = {
  DatePicker: function DatePicker(props) {
    return <div>Imagine a {props.color} datepicker here.</div>;
  }
}

function BlueDatePicker() {
  return <MyComponents.DatePicker color="blue" />;
}

사용자 정의 컴포넌트는 꼭 Capitalized 되어야 한다. 소문자로 된 컴포넌트라면 사용하기 전에 Capitalized 하는 방식으로 사용할 수 있다. 동적으로 사용할 때도 이런 방식으로 사용한다.

const components = {
  photo: PhotoStory,
  video: VideoStory
};

function Story(Props) {
  // Wrong
  return <components[props.storyType] story={props.story} />;

  // Correct
  const SpecificStory = components[props.storyType];
  return <SpecificStory story={props.story} />;
}

Props

아래는 각각 동일한 표현이다.

// 문자열 리터럴
<MyComponent message="hello world" />
<MyComponent message={'hello world'} />

// 문자열 리터럴은 HTML-unescaped로 처리됨
<MyComponent message="<3" />
<MyComponent message={'<3'} />

// Prop의 기본 값은 `True`
<MyComponent autocomplete />
<MyComponent autocomplete={true} />

Spread Attribute로 간편하게 표현할 수 있다.

<Greeting firstName="John" lastName="Dorian" nickName="Bambi" />

const props = {firstName: 'John', lastName: 'Dorian', nickName: 'Bambi'};
<Greeting {...props} />

const { nickName, ...other } = props;
const nick = nickName === 'Bambi' ? 'Newbie' : 'Scooter';
<button nickName={nick} {...other} />

자식 노드

문자열은 문자열로 처리되고 개행은 공백으로 처리된다.

render()에서 배열로 반환하면 합쳐서 렌더링한다.

JS 표현식도 자식 노드에 사용할 수 있다. 배열도 렌더링 하기 때문에 다음처럼 쓸 수 있다.

<ul>
  {todos.map((message) => <Item key={message} message={message} />)}
</ul>

children에 함수도 전달할 수 있다. Lifting state up이랑 비슷한 느낌이다. 세부 구현을 사용자에게 위임할 수 있을 것 같다.

function Repeat(props) {
  let items = [];
  for (let i = 0; i < props.numTimes; i++) {
    items.push(props.children(i));
  }
  return <div>{items}</div>;
}

function TodoList() {
  const todos = ['finish doc', 'submit pr', 'review'];
  return (
  <Repeat numTimes={10}>
    {(index) => <div key={index}>This is item {index} in the list</div>}
  </Repeat>
  );
}

Boolean, null, undefined는 화면에 렌더링하지 않는다.

// 조건부 표현
{showHeader && <Header />}

// false가 아닌 falsy한 값을 반환하는 경우에는 렌더링되는 점을 주의, 명확하게 boolean으로 반환할 것
{props.messages.length > 0 && <MessageList messages={props.messages} />}

Boolean, null, undefined를 표시하려면 {String(value)} 식으로 작성한다.

PropTypes로 타입 확인하기

정적 타입을 사용하지 않는다면 사용할 만한 검증 라이브러리다. 원래는 React에 포함되어 있다가 분리된 모양이다. 개발 모드에서만 값을 검사한다. 자세한 사용법은 prop-types 참고한다.

class Greeting extends React.Component {
  // ...
}

Greeting.propTypes = {
  name: PropTypes.string,
  nicknames: PropTypes.arrayOf(PropTypes.string),
  children: PropTypes.element.isRequired
};

Greeting.defaultProps = {
  name: 'Stranger'
};

클래스 프로퍼티 문법으로도 사용할 수 있다.

정적 타입 검사

FlowTypeScript를 설정하고 사용하는 방법을 설명한다.

코틀린도 js를 타겟 플랫폼으로 사용 가능하다고 한다. Kotlin Wrappers, create-react-kotlin-app을 참고한다.

Refs와 DOM

일반적으로 데이터 흐름은 props를 사용하게 되어 있지만 몇몇 경우에는 이런 방식에 적합하지 않다.

  • 커서 위치, 텍스트 선택, 미디어 재생
  • 애니메이션 처리
  • 서드파티 DOM 라이브러리와 연동

선언적으로 해결할 수 있는 부분에서는 ref를 쓰지 않는 것을 권한다. 예를 들면 Dialog 컴포넌트에 open(), close() 메소드를 만드는 것보다 isOpen prop을 넘겨주는 식으로 처리한다. 안되는걸 되게 하려고 ref를 쓸 수는 있지만 쓰기 전에 컴포넌트 위계를 보고 상태를 어디에 위치해야 하는지 잘 고려해야 한다. ref를 쓰는 방식보다 상위 계층에 상태가 위치하는게 더 적절하다면 Lifting State Up 방식을 적용해서 해결한다.

Ref는 DOM 컴포넌트와 클래스 컴포넌트에서만 사용할 수 있다. 컴포넌트 자체를 레퍼런스로 넘기 때문인데 함수형 컴포넌트 내에서 DOM 컴포넌트나 클래스 컴포넌트에는 사용할 수 있다.

마운트 될 때는 인자에 해당 엘리먼트를 전달하고 언마운트에는 null을 전달한다. 이 refcomponentDidMount, componentDidUpdate 전에 호출된다.

일반적으로 DOM 엘리먼트에 접근해야 할 일이 있을 때 많이 쓴다. ref={input => this.textInput = input}

class AutoFocusTextInput extends React.Component {
  componentDidMount() {
    this.textInput.focusTextInput();
  }

  render() {
    return <CustomTextInput
      ref={(input) => { this.textInput = input; }}/>;
  }
}

하위 엘리먼트의 DOM ref를 상위에서 사용하려면 props 체인을 따라서 함수를 내려주면 된다. 여기서 inputRef는 일반 prop을 정의해서 쓴 것이지 ref처럼 특별한 기능이 있는 prop이 아니다.

function CustomTextInput(props) {
  return <div><input ref={props.inputRef} /></div>;
}

function FormLayout(props) {
  return (
    <div>
      Name: <CustomTextInput inputRef={props.inputRef} />
    </div>
  );
}

class AwesomePage extends React.Component {
  render() {
    return <FormLayout inputRef={el => this.inputElement = el} />;
  }
}

가능하면 DOM을 노출해서 사용하지 않는 것이 좋다고 한다. 어쩔 수 없이 필요할 때만 사용하고 극단적으로는 findDOMNode()라는 흑마법도 존재한다고.

ref가 두 차례씩 호출되는 것(마운트 && 언마운트)은 null을 전달해서 기존에 연결된 레퍼런스를 지우는 역할도 겸하고 있기 때문이다. (DOM 레퍼런스를 냅두면 DOM은 해제되어도 GC가 지우지 않고 남겨둔다. 그래서 복잡한거 하지 않도록 간단한 함수 형태로만 소개하는 것 같다.)

Uncontrolled 컴포넌트

폼 데이터를 React 컴포넌트에서 다루는 controlled 컴포넌트와 반대로 DOM 자체에서 다루도록 하는 방식의 컴포넌트를 뜻한다.

DOM 엘리먼트를 사용하면 내장된 동작을 그대로 사용할 수 있는 특징이 있다. Controlled and uncontrolled form inputs in React don’t have to be complicated 글에서 비교 도표를 볼 수 있다.

<input defaultValue="Bob" type="text" ref={(input) => this.input = input} />
// checkbox, radio는 defaultChecked, 그 외는 defaultValue

input[type="file"]은 읽기 전용으로 항상 uncontrolled 컴포넌트다. ref를 사용해서 DOM을 직접 다룬다.

성능 최적화

프로덕션 빌드를 사용한다.

  • Create React App (이래서 다 이거 얘기하는듯)
  • 단일 파일로 빌드
  • Brunch -p 옵션 빌드
  • Browserify의 envify, uglifyify, uglify-js
  • Rollup의 replace, commonjs, uglify
  • webpack

크롬 개발자 도구의 성능 탭에서 컴포넌트를 프로파일링한다. 프로파일링 전에 크롬 확장과 React DevTool 끄는 것 잊지 않는다. 성능 테스트 과정 참조.

긴 목록은 한번에 로드하지 말고 동적으로 처리해야 성능이 좋다. react virtualized 같은 패키지가 있고 Virtualize, windowing가 검색 키워드.

React devtool에서 Highlight updates 기능으로 불필요하게 렌더링이 되는 지점을 찾아서 수정한다.

shouldComponentUpdate()의 반환값으로 렌더링 여부를 수동으로 제어할 수 있다. 이 부분을 직접 작성하는 것보다 React.PureComponent를 상속받는게 낫다. PureComponent는 기존 Component 구현에 prop과 state의 shallow 비교를 포함하고 있다. shouldComponentUpdate() 메소드의 동작 방식과 구현은 본문을 읽는다. 갱신이 필요하면 노드를 타고 올라가서 모두 갱신하게 만든다.

불필요한 갱신은 가변 데이터에서 주로 나타나기 때문에 불변 데이터를 사용하면 이 문제를 쉽게 피할 수 있다. Array.push()로 기존 배열을 조작하는 것보다 Array.concat(), [...words, 'new data']을 사용해서 원 데이터가 변형되지 않도록 한다. Immutable.js을 써도 된다.

ES6 없이 React

ES6 없이 쓸 일이 있을 때 읽는다.

JSX 없이 React

JSX 없이 쓸 일이 있을 때 읽는다.


가이드 나머지 다 보고 나면 몇 가지 먼저 만들려고 한다. 그리고나서 enzyme이랑 상태 관리하는 패키지 redux랑 mobx? 찾아서 볼 생각이다. 많은 분들이 열심히 쓰고 있어서 주워들은 것만 공부해도 좀 걸릴 것 같다.

색상을 바꿔요

눈에 편한 색상을 골라보세요 :)

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