angular

[Angular2] 지시자(Directive)

참고: angular2에 대한 연재는 여기를 참조해 주세요. 틀린 부분이나 의견, 피드백은 언제든지 환영합니다.

지시자에 대하여

오늘 다룰 개념은 Angularjs를 사용해 보신 분들은 적어도 한 번쯤은 들어보았을 지시자(Directive)입니다. 지시자에 대한 설명은 먼저 용어의 의미에서 출발해 보기로 합니다. 지시자 본래의 단어 Directive를 네이버에서 검색할 때 제일 처음 나오는 사전적 정의는 다음과 같습니다.

(공식적인) 지시[명령]

A directive is an official instruction that is given by someone in authority.

마찬가지로 네이버 한국어 사전에서 지시의 의미를 찾아보면 “1. 가리켜 보임, 2. 일러서 시킴. 또는 그 내용” 등으로 나오네요. 여기서 우리가 관심 갖는 의미는 2번째에 해당된다고 볼 수 있습니다.

정리하면, 어휘의 의미에서 접근했을 때 지시자라는 개념은 “어떠한 대상에게 일련의 명령을 수행하게 함”으로 다듬어 정의할 수 있습니다. 그리고 이 의미는 Angular 프레임워크 맥락에서도 동일합니다. Angular 프레임워크에서 의미하는 지시자는 “DOM을 다루기 위한 모든 것”이라 정의할 수 있습니다.

그럼 다음의 예를 통해서 조금 더 익숙한 곳에서부터 Angular가 수행하는 지시자의 역할을 살펴보기로 합니다.

지시자가 아닌 간단한 형태의 Component를 보여드렸습니다. Component 소스를 다시 꺼내 든 이유는 Component 역시 지시자이기 때문입니다. 위에서 정의한 지시자의 정의와 Component의 역할을 생각하면 Component가 왜 지시자인지 이해할 수 있습니다. Component도 화면의 렌더링과 사용자의 이벤트 처리 등 DOM을 다루기 때문에 지시자입니다.

Component는 Angular에서 중요한 컨셉 중 하나입니다. 그러므로 지시자의 일부로 다루기 보다는 독립적으로 구분해서 보는 이해하는 것이 좋습니다. 따라서 앞으로 지시자를 이야기 할 때 넓은 의미로는 Component를 포함하지만 협소한 의미로 이야기할 때는 Component를 제외하고 이야기 하도록 합니다.

그렇다면 좁은 의미로 볼 때, 지시자에는 어떤 것들이 있을까요? 구조 지시자(Structural directive)와 속성 지시자(Attribute directive)가 있습니다. 이 지시자들은 HTML의 태그 안에 속성에 선언하여 사용합니다. 세부적으로 각각 어떤 역할을 하는지 확인해 봅시다.

 

구조 지시자(Structural Directive)

구조 지시자는 DOM 요소를 추가하거나 삭제하는 등 화면의 구조를 변경할 때 사용하는 지시자입니다. 대표적인 구조 지시자에는 ngIf, ngFor가 있습니다. ngIf 지시자는 불리언 값을 입력 받아 true일 경우 ngIf가 선언된 DOM을 보여주고 false이면 제거해주는 용도의 지시자입니다. 반면 ngFor는 배열 형태의 모델을 반복해서 DOM에 표현할 때 사용하는 지시자입니다. 아래 공식매뉴얼의 간단한 예제를 살펴봅시다.

설명한 정의와 같이 구조 지시자는 DOM을 직접 조작하는데 사용하는 지시작입니다. angular에서 제공하는 기본 구조 지시자로도 어플리케이션 개발이 충분하지만, 필요에 따라 자신만의 구조지시자를 만들수도 있습니다. 이에 대해서는 나중에 다시 다루어 보도록 합니다.

angularjs에서는 지시자는 태그명, 속성, 클래스명, 주석까지 총 4가지로 표현되어 사용할 수 있었습니다. angularjs의 태그명을 사용한 지시자가 angular에서는 Component에 해당하고, angularjs의 속성 지시자가 angular에서는 협의의 의미의 지시자에 대응한다고 볼 수 있습니다.

 

속성 지시자(Attribute Directive)

속성지시자는 지시자가 선언된 DOM의 모습이나 동작을 조작하는데 사용하는 지시자입니다. 조작할 DOM에 미리 정의한 속성 지시자를 마킹하듯이 태그 안에 속성으로 선언하면 해당 DOM이 지시작에서 정의한 대로 동작하게 됩니다. 다음의 예를 한 번 봅시다.

이 예제는 DOM요소의 class에 ‘my-new-class’를 추가하는 ‘my-selector’라는 속성지시자를 선언한 것입니다. 따라서 필요에 따라 ‘my-new-class’란 클래스를 추가할 DOM에 다음과 같이 ‘my-directive’를 속성 중 하나로 선언해 주면 됩니다.

속성 지시자는 명칭에서 알 수 있듯이 DOM의 속성과도 직접 연관된 지시자입니다. 장황한 설명보다 다음의 예제가 그 역할을 충분히 설명할 수 있습니다.

예제를 보면 []를 사용하여 단방향 바인딩으로 우변의 모델 값을 직접 반영하고 있다는 것을 알 수 있습니다. 예제와 같이 기존 DOM의 속성에 접근하기 위한 용도에도 속성지시자가 사용되며 지난 시간에 설명한 바인딩도 속성 지시자의 일부가 됩니다.

 

Component와 지시자의 관계

오늘 소개했던 지시자와 Component는 Angular 프레임워크의 기둥과도 같은 중요한 개념입니다. 위에서 설명한 바와 같이 사실 Component도 지시자의 이야기 했습니다. 실제로 @Component 데코레이터는 @Directivetemplate이 포함된 확장된 형태로 구현되어 있습니다. 넓은 의미의 지시자는 DOM을 다루는 데 필요한 모든 것이기 때문에 우리가 만들 어플리케이션은 지시자들을 조합하여 구성하게 됩니다. HTML template을 포함한 Component와 구조/속성 지시자들을 활용하여 새로운 Component를 만들고, 또 이렇게 만들어진 Component 들을 조합하여 하나의 완성된 어플리케이션을 구축하는 것입니다. 따라서 지난 시간에 설명한 Component의 트리의 개념을 지시자를 포함해서 생각하면 다음과 같이 표현할 수 있습니다.

컴포넌트 트리 참조

위 그림을 보면 여러 지시자와 Component를 조합하여 새로운 Component를 부모로 가질 수 있지만 지시자는 부모가 될 수 없다는 점을 알 수 있습니다.

종합예제

그럼 한 번 지금까지 간단하게 살펴 보았던 컴포넌트, 데이터바인딩과 오늘 배운 내용들을 종합한 간단한 예제를 작성해 보기로 합니다. 예제는 초간단 Todo 리스트입니다. 먼저 작동 예제를 살펴봅시다.

예제의 시작점은 app.component.ts의 AppComponent 입니다.

AppComponent를 구성하기 위해서 TodoInputComponent와 TodoListComponent가 필요합니다. 따라서 13번 라인과 같이 metadata의 directives 에 해당 자식 컴포넌트들을 선언해야 합니다. 만약 자식 Component나 지시자를 사용하지만 directives안에 선언해 두지 않으면 어플리케이션 로딩 시 스크립트 오류가 발생합니다.

이번엔 한 번  TodoListComponent를 봅시다.

  • TodoListComponet도 metadata를 보면 TodoItemComponent를 자식 컴포넌트로 갖고 있음을 directives를 통해서 알 수 있습니다.
  • 10번 라인에서는 todos 배열의 값을 구조지시자를 활용하여 반복해서 todo-item을 표현하고 있습니다.

TodoItemComponent의 소스를 읽어 봅시다.

  • 8번 라인을 보면 PriorityDirective를 자식으로 포함하고 있음을 알 수 있습니다.
  • 7번 라인의 template  을 보면 todo-priority 라는 속성지시자를 추가했음을 확인할 수 있습니다.
  • 또한 7번 라인에서 todo 모델의 값을 사용자에게 보여주기 위해서 getTodoItemContent라는 함수를 사용했는데 다음에 “파이프”를 설명하면서 이를 좀 더 깔끔하게 처리하는 법을 소개하겠습니다.

본 예제의 PriorityDirective의 역할은 아주 간단합니다. 해당 속성지시자가 선언된 DOM의 요소에 ‘priority-grp’이라는 클래스를 추가하는 일을 합니다. 소스에서 확인해 볼까요?

소스를 보면 설명하지 않은 내용들이 많긴 하지만 일단은 무시하고 여기서는 9번라인이 todo-priority 속성 지시자가 선언된 DOM 요소에 ‘priority-grp’ 클래스를 넣는 부분이라고만 확인합시다.  지시자에 대한 자세한 활용은 다음 시간에 이야기 하기로 합니다.  (또한 constructor 안에 선언된 두 파라미터와 관련된 의존성 주입입니다. 역이 다음에 다루기로 합니다.)

 

정리하며

오늘 설명한 지시지에 대한 개념은 지난 시간의 Component와 함께 Angular 기반 어플리케이션의 뼈대를 세우는 중요한 역할을 합니다. 컴포넌트 관점에서 template이 있는 Component가 지시자와 다른 Componen를 자식으로 포함할 수 있지만 지시자는 다른 자식을 가질 수 없다는 것을 설명했습니다.

Angular2 글 목록보기

 

반응형 프로그래밍 분류

Angular2 코어 멤버인 victorsavkin의 글 “THE TAXONOMY OF REACTIVE PROGRAMMING“을 허락을 받고 번역 하였습니다. 번역에 대한 피드백이나 오류는 주저없이 트윗이나 메일로 알려주세요.

This post is the translation version of the original post “THE TAXONOMY OF REACTIVE PROGRAMMING” with allowed to translate by the author victorsavkin.]
우리는 모두 반응형 프로그램의 일부 요소를 활용하여 사용자 인터페이스를 구축합니다. 하나의 Todo 항목이 늘었났다고 해봅시다. 화면에 새 항목을 그려줘야 합니다. 이번엔 누군가가 Todo 항목의 제목을 변경했다면 어떨까요? 우리는 DOM의 텍스트 요소를 갱신해줘야 합니다. 이와 같은 일들을 도와줄 수십 종류의 라이브러리들이 이미 존재합니다. 이러한 라이브러리들은 비슷한 점도 있으면서도 각기 다른 점도 가지고 있습니다.

이 글에서 저는 반응형 프로그래밍의 각기 독립적인 4가지 측면을 설명할 것입니다.

  1. 이벤트와 상태 (events and state)
  2. 도출과 실행 (deriving and executing)
  3. 구체적 방식과 투명한 방식(reified and transparent)
  4. 자기 관찰과 외부 관찰 (self observation and external observation)

위 주제들에 대한 개념과 함께 4가지 측면이 각각 어떻게 쓰이는지를 설명할 예정입니다. 이를 통해 여러 라이브러리들을 좀 더 객관적이고 명확하게 바라보고 건설적인 토론을 이끌어 낼 수 있을 것입니다.

이 글의 예제는 Angular2와 RxJS를 사용하여 작성하였습니다. 여기에 쓰인 예제를 포함하여 재사용 가능한 라이브러리들과 대부분의 기능을 제공하고 있는 여러 프레임워크를 비교하는 것이 좀더 명확해 질 것입니다. 동일한 개념을 사용하여 이 글에서 논의를 이어나갈 예정입니다. 따라서 이러한 비교논의가 의미 있을 것입니다.

그럼 이제 한번 들어가 봅시다.

이벤트와 상태 (Events and State)

우리는 반응성을 논할 때 이벤트와 이벤트 스트림을 이야기 하곤 합니다. 이벤트 스트림은 반응형 객체에서 중요한 부분을 차지합니다. 하지만 두번째로 중요한 상태에 대해서도 간과해서는 안됩니다. 각각의 속성을 한번 비교해 봅시다.

이벤트는 독립적인 것으로 결코 지나칠 수 없습니다. 모든 단일 이벤트는 발생된 순서를 바탕으로 신경써야 할 대상입니다. 그러므로 “가장 최근의 이벤트” 같은 것이 우리가 관심 가져야 하는 특별한 것이 아닙니다. 따라서 사용자에게 직접적으로 보여지는 이벤트는 거의 없다고 볼 수 있습니다.

반면에 상태는 연속적인 속성을 지니고 있기에 시간을 기준으로 특정한 때의 값을 정의할 수 있습니다. 우리는 보통 얼마나 많이 상태가 갱신되었는지에 관심을 갖지 않고 가장 최신의 값에만 신경을 씁니다. 상태는 종종 화면에 노출되거나 일련의 의미있는 형태로 있기도 합니다.

쇼핑카트를 예로 들어봅시다. 우리는 쇼핑카트의 내용을 변경하기 위해서 마우스 클릭 이벤트를 사용할 수 있습니다. 더하기 버튼을 클릭하면 제품의 수를 증가시켜줄 것이고, 마이너스 버튼을 클릭하면 제품의 수를 감소 시킬 것입니다. 결국, 클릭의 수가 중요하기 때문에 쇼핑카트의 내용을 유지하기 위해서 단 하나의 이벤트도 누락해서는 안됩니다. 그러나 우리는 마지막 이벤트를 반드시 점검해야 하거나 이를 사용자에게 노출시킬 일도 없습니다.

반면에 쇼핑카트의 전체 항목수는 상태에 해당합니다. 우리는 전체 항목수가 얼마나 갱신되었나를 보지 않고 쇼핑카트의 내용을 나타내는 가장 최신의 값만을 고려합니다.

정의

이벤트 스트림 은 특정한 시간 주기 동안 생성된 값들의 순차적인 나열입니다. 그리고 상태 는 시간에 따라 바뀔 수 있는 단일 값입니다.

한 번 예제를 살펴봅시다.

위 예제에서는 하나의 이벤트 흐름인 moves라는 observable을 가지고 있습니다. 사용자가 매번 마우스를 움직일 때마다, observable은 이벤트를 발산(emit)시킵니다. 우리는 상태값인 position subject를 생성하여 moves observable을 사용합니다. position값의 속성은 마우스의 현재 위치를 반환합니다. 이 예제는 이벤트와 상태가 무엇인지 묘사해줄 뿐 아니라 상태는 종종 이벤트의 순차적인 나열에서 생성된다는 것을 보여줍니다.

역자 주: RxJS가 익숙하지 않은 분들은 observable이 계속해서 발생하는 이벤트 흐름의 발생출처 라고 생각하시고 읽으시면 됩니다. 그래서 이름과 같이 관찰가능한 대상이기에 observable이라고 합니다. RxJS에 대해 자세히 알고 싶으신 분들은 rxmarble의 그림을 참조해 보세요.

이벤트와 상태라는 이분법이 단지 RxJS만의 산물이 아니라는 것을 보여주기 위해서 이번에는 Angular의 예제를 봅시다.

이 예제에서 employee 컴포넌트의 name 입력은 company 컴포넌트의 employees 프로퍼티에서 끄집어 낸 상태입니다. 주의할 점은 우리는 오로지 가장 최신의 이름 값에만 신경을 쓴다는 것입니다. 예를 들어 중도에 갖고 있던 이름 값을 지나쳐도 어떤 것에도 영향을 미치지 않습니다. 모든 단일한 값을 다루면서 발생된 순서정보까지 내포한 selected 이벤트의 순차적 나열과 비교해 보세요.

정리

반응형 프로그램의 세계 안에 이벤트 스트림과 상태라는 두 개의 카테고리가 있습니다. 이벤트 스트림은 사용자에게 노출되지 않고 시간에 따라 흐르는 독립적인 값의 순차적인 나열입니다. 상태는 사용자게에 종종 노출되기도 하고 의미있는 형태를 갖는 시간에 따라 변할 수 있는 값입니다.

도출과 실행 (deriving and executing)

먼저 마우스 클릭에 대한 RxJS observable이 다음과 같이 있다고 해봅시다.

이 observable로 무얼 할 수 있을까요? 당장 드는 생각은 좌표를 표시하는 것이네요.

나름 유용하지만, 콜백을 사용하는 것과 큰 차이가 없습니다.

마우스 클릭 observable의 진짜 가치는 새로운 observable을 도출할 수 있다는 점입니다.

map 연산이 제공한 것은 오로지 새로운 observable을 제공한다는 점입니다. 이로 인해 프로그램에 영향을 주는 것은 전혀 없기에, 이 프로그램은 부수효과를 갖지 않습니다.

역자 주: 이 번역에서 side effect는 부수효과로 통일합니다. 부수효과는 함수가 자신의 결과값 이에외 다른 상태에 영향을 주는 것을 말합니다. 예를 들면 console.log를 사용한 화면 출력도 마찬가지입니다.

이제, 조금 더 깊이 들어가서 몇가지 RxJS 연산을 사용하여 새로운 observable을 생성해 봅시다. 이번에 생성할 observable은 모든 요소가 tofrom의 좌표를 갖고 있습니다.

다시 한번, observable 사이의 관계만 집중해 봅시다. 도출이란 어떻게 서로 다른 존재(entity)가 어떻게 연관되어 있는지에 대한 모든 것입니다. 결론적으로는 여러분의 프로그램이 수행하게 될 연산을 표현해주는 하나의 observable 또는 여러 observable이 있게 됩니다. 이는 연산(computation) 그 자체가 아니라 연산의 표현입니다. 사용자에게 실제로 노출되는 것은 아무것도 없습니다.

사용자에게 무엇인가를 보여주기 위해서 (다른 말로 표현하자면, 부수효과를 실행하기 위해서는) 우리는 forEach함수의 호출이 필요하다.

RxJS만이 도출과 부수효과의 수행을 별개의 연산으로 분류하는 유일한 라이브러리가 아닙니다. Angular 역시 가능합니다.

name속성은 ceo 속성에 도출되어 ngOnChanges가 부수효과를 수행하는데 사용됩니다.

역자 주: Angular2에서는 컴포넌트가 뷰의 일부 요소가 되어 트리형태를 만듭니다. 따라서 부모 컴포넌트에서 자식 컴포넌트로 상태 값을 전달할 수 있고, 이 때 자식 컴포넌트에서는 값이 변결될 때 ngOnChanges 함수가 자동으로 실행됩니다. 위 예제는 간략하게 부모 컴포넌트의 ceo 상태가 변경되어 ceo 안에 name을 받는 자식 컴포넌트의 ngOnchange가 console.log로 부수효과를 일으키는 것을 보여줍니다.

Hot과 Cold. 그리고 프라미스에 대해서

흥미롭게도 RxJS의 observable은 차갑기 때문에, 라이브러리가 두 종류의 연산으로 구분되어 있어야만 합니다. forEach와 구독 개념과 같은 일련의 연산들을 제공해야 합니다. 그렇지 않으면 어떠한 부수효과도 실행되지 않을 것입니다.

반면에 프라미스는 뜨겁습니다. 뜨겁다는 의미는 프라미스를 제공하는 쪽에서 값이 정해지면, 모든 도출된 프라미스에게 전달이 됩니다. 왜냐하면 프라미스는 도출과 부수효과를 실행하는데 모두 사용될 수 있는 then 연산을 통해서 이를 해낼 수 있기 때문입니다.

hot/cold 주제는 미묘하지만 단순히 부수효과나 구독과 관련해서 영향을 주는 것이 아니라 취소와도 관련되어 있습니다. 더 자세한 내용은 Ben Lesh의 훌륭한 글을 참조하세요.

역자 주: Hot과 Cold Observable에 대한 차이는 Rx 방식의 개념에서 나온 것으로 자세한 내용은 위의 글을 참조해 주시기 바랍니다. 간단하게 Hot은 변경된 데이터가 있으면 Push 형태로 바로 전달해 주는 것이고, Cold는 변경된 데이터를 구독하는 리스너가 있기 전까지 실행되지 않는 Pull 형태라고 보시면 됩니다.

정리

시간에 따라 변하는 변수를 다루는 방법은 두 가지가 있습니다. 기존 변수로 부터 새로운 변수를 유도하거나 값이 변화할 때마다 부수효과를 실행하는 것입니다. 이 두 방법을 분리하는 것은 코드를 좀 더 구성 가능하고 리팩토링하기 쉽게 만들어 줍니다.

구체적 방식(REIFIED)과 투명한 방식(TRANSPARENT)

먼저, 예제를 살펴봅시다.

예제에서 우리는 두 개의 subjectceoname을 가지고 있습니다. 이 두 subject는 값의 변경이 일어나는 것을 관찰할 수 있도록 도와줍니다. 반응형 프로그램에서 이러한 타입을 우리는 구체적 방식(reified)이라고 호명합니다. 그 이유는 관찰행위를 표현하는 구체적인 객체에 접근할 수 있기 때문입니다. 또한 이러한 구체적인 객체의 소유는 이를 수정하고 다른 곳에 전달하고, 새로운 것을 구성할 수 있기에 강력한 특징을 갖고 있습니다.

name.valuename의 차이는 처음에 봤을때 미묘할 수도 있지만 핵심적인 부분입니다. name.valuename상태의 현재 값을 의미하지만 name은 그 자체로 다른 것입니다. name 시간에 따라 변화하는 값을 나타냅니다.

유사한 Angular 방식의 예제와 대조해 봅시다.

Angular는 시간에 따라 변하는 name을 표현하는 어떠한 형태의 객체도 제공하지 않습니다. 우리는 오로지 현재 값만을 갖고 있습니다. 반응형 프로그래밍에서 이러한 타입을 투명한 방식(transparent)라고 호명합니다. 왜냐하면 개발자들은 오로지 가장 최신의 값만을 다루기 때문입니다. 관찰행위는 프레임워크 안에서 이뤄지기에 알 수 없습니다.

구성

투명한 방식과 구체적 방식을 활용한 반응형 프로그래밍은 상태를 구성할 때 매우 다르게 작동합니다.

구체적 방식의 반응형 프로그램을 사용하면 우리는 다음과 같이 기존의 것에서 새로운 변수를 생성하는 특수한 연산을 사용해야만 합니다.

투명한 방식의 반응형 프로그래밍을 활용 시 우리는 일반적인 자바스크립트 문법을 사용합니다.

상태를 다룰 때에, 우리는 위 두 타입의 반응형 프로그래밍을 효율적으로 사용할 수 있습니다. 투명한 반응형 프로그래밍이 좀 더 간단하고 성능에 우위가 있는 반면, 구체화한 반응형 프로그래밍은 좀 더 유연합니다.

이벤트를 다루는 경우에는 두 방식을 모두 사용할 수 없습니다. 왜 두 타입의 반응형 프로그래밍을 살펴볼 필요가 있는지 이해하려면 시간의 개념을 다뤄야 합니다.

암묵적 시간과 명시적 시간

투명한 방식의 반응형 프로그램을 사용 시, 우리는 시간을 명시적으로 다룰 수 없습니다. 상세히 설명하면 시간의 개념 자체를 실제로 정의하지 않습니다. 물론 name 속성이 시간의 흐름 속에 변할 수 있다는 것을 인지하고 있지만, 시간에 흐름에 기반하여 흥미로운 결정을 도출할 만한 어떠한 수단도 없습니다.

그래서 구체적 방식의 반응형 프로그램이 뜨기 시작한 겁니다. 구체적 방식의 반응형 프로그램은 시간의 흐름을 명시적으로 만들어 이를 활용하는 특수한 연산들을 제공하므로 새로운 것을 구성할 수 있게 도와줍니다. 예를 들면, 우리는 다음과 같이 name subject를 0.5초 지연시켜 갱신할 수 있습니다.

상태값을 활용할 때 시간을 직접 이용하는 것은 그다지 실용적이지 않겠지만 이벤트를 다룰 때는 흔한 방식입니다. (예를 들면 debouncing 같은 것이 있습니다.) 이러한 이유로, 지금까지 익숙했던 투명한 방식의 패러다임으로서 가장 최신의 값에 접근하는 것은 이벤트 처리에 있어서는 종종 충분하지 않을 때가 있습니다.

정리

투명한 방식의 반응형 프로그램은 시간에 따라 변하는 여러 변수들을 구성하는데 있어서 기존의 자바스크립트 문법을 사용하기 때문에 더 간단합니다. 그러나 이 방법은 오로지 변수의 가장 최신의 값만을 제공하며 명시적으로 시간이 흐름을 다룰 방법을 제공하지 않습니다. 결과적으로 상태 값을 도출하는 데는 유용하지만 이벤트를 조작하는데는 그렇지 않습니다.

자기관찰과 외부 관찰

정의

외부관찰(External observation)은 관찰 대상인 객체를 스스로 관찰해서 알아낼 수 없고 대신 어떤 외부 존재 (예를 들면, Angular 프레임워크)가 객체의 변화를 알아내는 방식입니다. 자기 관찰(Self observation)은 스스로 객체의 변화를 직접 관리하는 방식입니다.

다시 한번 예제를 보도록 합시다.

이 예제에서는 employee 컴포넌트의 fullName 속성을 company 컴포넌트의 ceo 속성에서 도출하고 있습니다. 다시 말하면, Angular에게 ceo 객체를 관찰하여 성이나 이름이바뀔 때마다 employee 컴포넌트를 갱신하라고 명령합니다. 여기서 흥미로운 점은 ceo 객체는 관찰에 필요한 어떠한 정보도 소유하고 있지 않고, 일련의 특수한 방식으로 표현되거나 생성된 것도 아닙니다.

이는 Angular가 대신 외부관찰을 하고 있기 때문입니다. Angular는 template1 안에 표현식을 계산하고 결과를 기억하고 있습니다. 다음 번에도 동일한 계산을 수행하면서 이전의 값과 새로운 값을 비교하게 됩니다. 이 때 만약 값이 변경되었을 경우, Angular 프레임워크에서 employee 컴포넌트를 갱신해 줍니다.

외부 관찰은 많은 이점을 갖고 있습니다. 먼저, 상당한 유연함을 가져다 줍니다. 여러분은 예제와 같이 {firstName: string, lastName: string} 인터페이스를 구현한 어떤 형태의 객체를 사용해도 됩니다. 서버로부터 전송된 json 일수도 있고 ImmutableJS 객체도 가능합니다. 이에 더하여, 외부 관찰은 강력하게 관찰의 순서를 보장하여 연속적인 갱신의 문제를 피할 수 있도록 실시합니다. 물론 불리한 측면은 실행할 코드가 프레임워크의 컨텍스트 안에서 관찰되어야 합니다. 그래서 Angular1이 $scope.apply를 API를 가지고 있으며, Angular2 에서 zone.js가 필요한 이유이기도 하니다.

객체의 변화를 스스로 인지하고 이를 구독하고 있는 대상 에게 알려주는 자기관찰과 대조해 보시기 바랍니다. MobX 또는 Knockout이 이러한 접근방식을 취하고 있는 라이브러리 입니다.

총정리

본 글에서, 반응형 프로그래밍에 대한 논의에 필수적인 4가지 측면들을 소개하고자 정리해 보았습니다. 제 바람은 이 글을 통해서 좀 더 명확하고 객관적으로 여러 라이브러리나 접근 방식을 비교하는 것입니다.

이제 여러분이 새로운 라이브러리를 접할 때 스스로에게 다음과 같은 4가지 질문을 던져 보시길 바랍니다.

  • 라이브러리가 이벤트를 다루는가? 상태를 다루는가?
  • 라이브러리가 값을 도출하는가? 부수효과를 실행하는가?
  • 라이브러리가 투명한 방식의 반응형 프로그램인가? 구체적 방식의 반응형 프로그램인가?
  • 라이브러리가 외부관찰인가? 자기관찰인가?

예를 들면, Angular에 대해서 논의할 때 우리는 상태를 도출하기 위하여 외부관찰을 통해 투명한 방식의 반응형 프로그램을 사용하거나 혹은 이벤트를 다루기 위해서 자기관찰을 통해 구체적 방식의 반응형 프로그램을 사용한다고 말할 수 있습니다. 그리고 더 바라기는 여러분은 Angular 개발팀이 이러한 선택지를 만든 이유를 알았으면 합니다. 다른 예로 자기관찰을 통해 투명한 방식의 반응형 프로그램을 사용하며 상태를 다루는 MobX가 있습니다. 또는 자기 관찰을 통해 구체적 방식의 반응형 프로그램을 지원하는 Cycle.js를 고려해 볼수도 있습니다.
모든 형태의 반응형 프로그램을 이 글을 통해서 알았을까요? 물론 아닙니다! 이 하나의 글을 통해서 반응형 프로그램에 대한 전체 분야를 다뤘다는 것은 순진한 생각입니다. 예를 들면, 저는 푸쉬나 풀 방식, 일급 및 고계함수의 FRP(Functional Reactive Programming), continus FRP, Observable/Iterable duality, back pressure, sampling 등 여러 다른 흥미롭고 중요한 주제를 다루지 않았습니다. 어쩌면 다음 블로그 포스팅의 주제가 될지도 모르겠습니다. 그러니 계속해서 주목해 주세요.

저자의 블로그: http://victorsavkin.com/


  1. http://www.notforme.kr/archives/1627#template 
angular

[Angular2] 데이터 바인딩

참고: angular2에 대한 연재는 여기를 참조해 주세요. 틀린 부분이나 의견, 피드백은 언제든지 환영합니다.

 

들어가기

오늘은 지난 시간에 설명한 컴포넌트의 상태를 어떻게 다룰지와 연관된 주제인 “데이터 바인딩“을 소개합니다. Angular 1을 사용해 보신 분들은 양방향 데이터 바인딩(2-way data binding)을 통해서 이미 “데이터 바인딩”이란 개념을 들어 보셨을 겁니다. 이번 글을 통해서 Angular2에서는 Angular1과 달리 어떻게 “데이터 바인딩” 기능을 제공하는지 알 수 있습니다.

 

데이터 바인딩?

먼저, 데이터 바인딩은 Angular에만 있는 특별한 개념이 아닙니다. 데스크톱 기반 앱의 UI 개발시에도 있었던 개념입니다. Java Swing을 예로 들어 볼까요? 사용자의 입력이 필요한 곳에 일반적으로 JTextField를 윈도우 창(Panel)에 배치 합니다. 그리고 버튼과 같은 요소에 리스너 메서드를 등록하여 사용자가 입력한 정보를 조회하여 비즈니스로직을 구현하는데요. 이 때 Swing에서는 사용자가 정보를 입력할 때 이를 JTextField 객체 내 상태에 바인딩합니다. 그리고 getText() API를 통해서 입력한 정보를 얻을 수 있습니다. 이처럼 기존 데스크톱 기반의 UI 라이브러리들은 사용자의 입력을 받을 수 있는 컴포넌트 안에 상태를 가지고 있고, 입력이벤트에 따른 상태 변경이 암묵적으로 자연스럽게 이루어집니다.

UI 프로그램의 트렌드가 데스크톱에서 웹으로 바뀌면서 기존에 데스크톱 기반 어플리케이션에서는 편리했던 입력 컴포넌트의 상태 정보 조회가 웹 기반 어플리케이션에서는 간단하지 않게 되었습니다. 이는 웹의 태생 자체가 사용자와의 양방향 상호작용이 많은 것을 가정한 것이 아니기 때문에 발생하는 불일치 입니다.

웹 어플리케이션에 어떻게 상태 정보를 조회하고 갱신하는지 확인해 봅시다. 보통 웹 어플리케이션에서는 DOM의 요소 안에 value나 checked 프로퍼티 등에 필요한 상태정보를 담고 있는 경우가 많습니다. 사용자가 입력한 정보를 얻기 위해서 웹에서는 DOM의 API나 jQuery를 사용해서 필요한 요소를 찾아내는 코드가 필수적으로 따라 붙게 됩니다. 아래는 jQuery의 val() API 문서에 있는 예제 소스 중 일부입니다.

script 태그 안에 있는 소스를 보면 input 태그의 “keyup” 이벤트가 발생할 때마다, 해당 input 요소(8번 줄의 this)에서 val() 메서드를 통해서 사용자가 입력한 값을 읽어서 다시 이를 p 요소를 조회한 후에 text 프로퍼티에 값으로 넣고 있습니다.

이러한 환경에서 웹기반 환경에서 사용자의 입력을 자동으로 반영해주는 것을 프레임워크 레벨에서 지원하기 시작했고, Angular 역시 이를 지원하는 프레임워크 중에 하나일 뿐입니다.

 

Angular2의 데이터 바인딩

Angular2에서는 화면에 UI역할을 하는 DOM과 Component 사이에서 총 4가지 방식의 데이터 바인딩 방법을 제공하고 있습니다. 아래는 Angular.io 매뉴얼에서 가져온 4가지 데이터 바인딩을 설명하는 그림입니다. 그림의 그려진 4개의 화살표가 데이터가 반영되는 방향을 의미하고 화살표 위에 내용이 실제 문법적 표현을 나타냅니다.

Angular2 데이터 바인딩 타입

첫 번째: 보간(interpolation)

Component의 상태를 그대로 화면에 노출 시킬 때 사용하는 방법입니다. 예를 들어 아래와 같이 MyComponent라는 Component에 myData라는 멤버 변수를 상태로 갖고 있다고 해봅시다.

이러한 경우 MyComponent를 표현할 template 에서 “{{ myData }}” 와 같이 MyComponent의 멤버변수를 중괄호 2개를 중첩하여 감싸면 myData안의 값이 자연스럽게 화면에 반영됩니다. 위 예제에서는 div태그 안에 myData 변수를 보간하였으므로 실제 html 소스로는 “<div>날 화면에 그려줘!</div>”로 바뀌게 됩니다. 따라서 그림의 첫 번째 화살표와 같이 Component 안의 데이터가 DOM에 반영된다고 볼 수 있습니다.

 

두 번째: 프로퍼티 바인딩

프로퍼티 바인딩은 Component의 상태 값을 DOM에 프로퍼티에 직접 반영하는데 쓰입니다. 문법은 해당 DOM의 프로퍼티를 대괄호([])로 감싸고 우측에 바인딩할 대상을 넣어주면 됩니다. 예를 들어, HTML의 input 태그는 메모리 상에 HTMLInputElement로 DOM에 반영되는데요. input 태그의 프로퍼티 중 하나인 disabled에 다음과 같은 방식으로 직접 Component의 상태를 반영하거나, 간단한 expression을 사용할 수 있습니다.

 

세 번째: 이벤트 바인딩

DOM의 요소들은 모두 특정 이벤트에 대한 리스너를 등록할 수 있습니다. 버튼에 onClick으로 이벤트를 직접 지정하기도 하지만 보통은  jQuery를 사용해서 이벤트를 등록합니다. Angular2에서는 DOM의 요소에 이벤트 리스너를 Component에 정의한 함수로 정의할 수 있게 해주면 이를 이벤트 바인딩이라고 합니다. 이벤트 바인딩하는 방법은 바인딩할 이벤트명을 소괄호( () )로 감싸고 우측에 메서드명을 적으면 됩니다.

아래는 button 요소에 “click”이벤트를, div 요소에 “mousemove” 이벤트를 바인딩한 예제입니다.

예제의 소스를 보면 다음과 같습니다.

소스의 내용을 살펴봅시다. 24번 라인에 먼저 button 요소에 소괄호를 사용하여 click 이벤트에 AppComponent의 “clickedButton” 메서드를 바인딩했습니다. 25번 라인에서는 div 요소의 “mousemove” 이벤트를 “printMousePosition” 메서드에 바인딩했습니다. 이벤트 바인딩으로 선언한 메서드의 파라미터로 $event는 DOM의 이벤트 객체가 자동으로 들어갑니다.

따라서 화면 상의 버튼을 클릭하면 clickedButton 메서드가 실행되면서 alert창이 뜨게 되고, div요소 위에 마우스를 움직일 때마다 printMousePosition메서드가 실행되면서, 해당 이벤트 발생 시점의 마우스 포지션을 positions 배열에 넣고 화면에 자동으로 출력합니다.

 

네 번째: 양방향 바인딩

마지막으로 양방향(two way) 바인딩입니다. 양방향 바인딩은 프로퍼티 바인딩과 이벤트 바인딩을 동시에 적용하는 방법입니다. 문법은 [(ngModel)]= “바인딩할 대상” 으로 해당 요소의 속성으로 넣어주면 됩니다. input, select 박스 등에 사용하면 바인딩한 Component에 상태가 화면의 View에도 반영이 되고, 반대로 사용자가 View에서 입력할 때 Component의 상태에도 자동으로 데이터가 반영이 됩니다. 아래 예제 input에 한번 입력해 보시기 바랍니다.

아래 소스를 살펴볼까요. 8번 라인에 ngModel로 AppComponent의 value를 양방향 바인딩하고, 이를 바로 p 태그 안에 보간하여 사용자가 입력한 값을 바로 보여줄 수 있게 처리 했습니다. 양방향 바인딩을 이용할 경우 손 쉽게 서버에서 보내는 데이터를Component의 모델에 저장하면 뷰에 반영이 되고 동시에 사용자가 뷰에서 수정한 값을 다시 Component의 모델에 반영할 수도 있습니다.

양방향 바인딩의 동작원리는 내부적으로 Change Detector에 의해서 이루어지는데요. 브라우저에서 발생 가능한 비동기 이벤트(XHR, setTimeout, Event)에 Change Detector가 루트 컴포넌트부터 Depth First Search 방식으로 데이터의 변경사항을 감지하여 양방향 바인딩을 가능하게 한다는 점만 가볍게 설명하고 넘어가겠습니다. 자세한 내용은 나중에 다시 다루도록 하겠습니다.

 

정리하며

오늘은 데이터 바인딩과 Angular2가 지원하는 4가지 방식의 데이터 바인딩에 대해서 살펴 보았습니다. 실상 Angular1에서는 양방향 바인딩이 기본이고 다른 바인딩은 아예 지원하지 않았습니다. 양방향 바인딩을 구현하기 위해서 Angular가 (내부적으로 $digest라는 사이클을 돌 때마다) 모든 변경사항을 체크해야 해서 퍼포먼스의 결함이 있었습니다.

Angular2에서는 양방향 바인딩 자체에도 완전히 새로운 알고리즘으로 퍼포먼스의 향상이 있었을 뿐 아니라 다른 바인딩 옵션을 제공하여 조금더 최적화딘 어플리케이션 개발을 돕습니다. 데이터 바인딩의 활용으로 불필요하게 늘어나는 스크립트 코드들이 줄어드니 사용하지 않을 이유가 없겠지요? 오늘은 여기서 이만 글을 마무리 합니다.

Angular2 글 목록보기

angular

jQuery vs Angular 2 코드 비교

참고: angular2에 대한 연재는 여기를 참조해 주세요. 틀린 부분이나 의견, 피드백은 언제든지 환영합니다. 

들어가기 전에

이 포스팅은 지난 2015 나는 프로그래머다 컨퍼런스 중 React 라이브 코딩에서 보고 알게 된 jQuery versus React.js thinking 글에서 출발하여 (원저자의 허락을 받아) Angular2 버전으로 쓴 것입니다.

This post is the result affected by original post “jQuery versus React.js thinking” that I found on React.js live coding session in “I am programmer” conference. I wrote this post with the permission of original blog post’s author.

이 글에서는 아주 간단한 예제를 jQuery와 Angular2로 각각 구현하여 어떤 차이점이 있는지 확인해 보려고 합니다. jQuery와 Angular2의 직접적인 비교라기 보다 최근의 프론트엔드 프레임워크가 어떤 방식으로 어플리케이션을 구성하는지 확인하기 위한 목적의 글입니다.

 

예제 설명

예제는 jQuery versus React.js thinking과 동일하며 아래와 같은 기능으로 이루어져 있습니다.

  • 아이템의 목록이 있으며 아이템은 detail 정보를 기본적으로 드러내지 않음
  • 사용자가 아이템 클릭 시에 해당 아이템의 detail 정보가 펼쳐짐
  • 이 때 클릭 안 한 아이템들은 grey로 색 변경
  • 클릭한 아이템을 재클릭 시, detail 정보는 다시 감추고 원래 상태로 돌아감
  • 클릭해서 펼쳤던 아이템 외에 다른 아이템 클릭 시 펼쳤던 아이템은 닫히고 클릭한 아이템을 펼침

 

jQuery 방식

jQuery 방식의 소스는 원 글의 소스와 동일합니다.

라이브 예제
JS Bin on jsbin.com

jQuery 방식의 구현은 먼저 아이템 정보와 상태가 그대로 html 파일 내 DOM에 분산되어 있습니다. 또한 데이터와 프레젠테이션 로직이 한 곳에 뭉쳐 있습니다. 각 상태를 변경하기 위해서는 “li”, “:hidden”과 같은 셀렉터를 사용해서 접근해야 합니다. 기능이 복 잡해 질수록 DOM에 접근하는 코드로 인해 리팩토링과 개선이 쉽지 않습니다.

Angular 2 방식

Angular2는 컴포넌트 기반으로 앱을 구성하게 되어 있습니다. 예제가 간단하기 때문에 하나의 파일 안에서 모든 기능을 구현해 넣을 수 있습니다. 하지만 가장 Angular 2 스타일 다운 특징을 보여주고자 각 요소를 최대한 기능에 맞게 나누었습니다. 또한 예제는 Javascript에 타입 등의 syntax가 추가된 Typescript로 구현되어 있습니다.

라이브 예제

product.component

Component 클래스 안에 컴포넌트를 구성하는 핵심 정보들이 다 있습니다. 먼저 6번 라인의 templateUrl을 통해서 해당 컴포넌트의 뷰를 “products.component .html”에 정의하고, 7번 라인에  styleUrls에서 해당 컴포넌트를 위한 스타일 파일을 선언해 두었습니다.

아이템 데이터는 ProductService라는 클래스에 있습니다. Angular2에서는 실제 비즈니스 로직은 Service 클래스에 담도록 안내하고 있습니다. 그리고 Service를 Component에서 가져다 쓸 때에는 간단한 선언을 통해서 자동으로 Service객체를 주입받습니다. 8번 라인이 Component에서 사용할 의존성을 선언한 것입니다.  8번 라인과 같이 providers에 의존성 선언 후에는 construtor 메서드 안에 파라미터로 해당 의존성을 선언해 주면 의존성 주입이 자동으로 이루어집니다.

product.service

서비스 클래스는 아이템 데이터를 반환하는 getProducts 메서드 하나 만을 갖고 있습니다. ES6에 추가된 const 키워드를 사용하여 상수로 mock 데이터를 선언하여 메서드에서 반환합니다. 실제 상황에서는 getProducts 메서드 안에서 ajax 호출을 하도록 구현합니다.

2번 라인에서 import한 Product와 12번 라인에 메서드의 반환타입으로 Product[]로 되어 있는 것은 아이템 데이터의 타입을 명시한 Product 클래스 정보입니다. Typescript는 반드시 타입을 필요로 하기에 다음과 같이 모델 클래스를 선언하여 사용합니다.(자세한 내용은 연재 중인 Angular.io 글 목록을 참고해 주세요.)

 

Angular2 방식에서 눈에 띄는 점은 jQuery방식과 달리 명시적으로 파일단위 관심사의 분리가 이루어진다는 점입니다. Angular2는 다음과 같은 요소들로 관심사를 나눕니다.

  • template: 뷰 역할
  • component: 뷰와 모델, 상태 관리
  • service: 비즈니스 로직

jQuery 방식에 비해 파일 개수도 늘어나고, 코드의 양도 늘어나지만 Angular2 방식이 기능 개선 및 유지보수에 훨씬 더 효율적인 방식입니다. 또한 Angular2는 독립적인 컴포넌트 방식으로 개발하기에 재사용에도 용이하며, 컴포넌트별 독립적인 테스트까지 가능하여 jQuery 방식에 비해 이점을 갖는 부분이 많습니다.

결론

웹 어플리케이션의 복잡도가 증가하면서 지속적인 개선 및 유지보수를 고려할 때, 일정 규모 이상의 웹 어플리케이션 개발 시 프레임워크 사용은 더 이상 선택사항이 아니라고 생각합니다.

이 글은 jQuery와 Angular2와 모습을 직접적으로 비교한 것은 아닙니다. 그보다 구현하려는 대상과 목적에 따라 적절한 선택이 필요함을 보여주려고 한 것입니다. 여러 프론트엔드 프레임워크 중에서 Angular2 역시 앞으로 주목하고 관심가질 만한 좋은 기술이라는 것을 알려드리면서 글을 마칩니다.

참고: jquery-versus-react-thinking

Angular2 글 목록보기

 

angular

[Angular2] 상태 다루기

참고: angular2에 대한 연재는 여기를 참조해 주세요. 틀린 부분이나 의견, 피드백은 언제든지 환영합니다. 

들어가기

이번 글에서부터 지난 시간에 설명한 Component에 살을 붙이기 시작합니다. 오늘은 컴포넌트의 상태값의 개념을 이해해보고 Angular2에서는 어떻게 상태를 관리하는지 간략하게 설명합니다. 이를 위해 Component 클래스에 첫 변수와 메서드를 정의해 봅니다.

 

상태(State)에 대해서

웹사이트에서 상품을 구매할 때 우리는 그림에서 보는 바와 같이 “… 동의”라는 체크박스를 흔하게 접하게 됩니다. 이 때 체크박스는 사용자의 클릭 행위에 따라서 상기의 내용에 동의하는지 여부를 확인하는 용도로 쓰입니다.  그리고 체크박스는 다음의 두가지 의미 중 하나를 반드시 갖게 됩니다.

# 체크박스 상태 의미
1 체크함 내용에 동의
2 체크 안 함 내용을 안 읽었거나 동의하지 않음

따라서 HTML 요소 들이 사용자의 행위에 따른 상태를 가질 수 있습니다. 보통은 예제의 체크박스를 포함하여 Form을 구성하는 input 요소나 셀렉트박스, 라디오박스도 상태를 가집니다.

 

상태정보를 다루는 방식

사용자의 상태 정보들이 결국 우리가 개발하는 웹 어플리케이션의 주요한 관심사이면서 또한 비즈니스 로직에 깊은 연관을 갖게 됩니다. 그렇다면 Angular2가 어떻게 상태정보를 다루는지와 비교하기 위해서 기존에는 HTML요소의 상태 정보를 어떻게 접근해서 활용해 왔는지를 확인해 봅시다.

위의 예제를 그대로 이어서 사용자가 상품 구매를 진행하기 위해서 모든 “…동의” 항목에 체크 했다고 생각해 봅시다. 보통 우리는 사용자가 상품의 구매를 위해서 반드시 “…동의”체크박스를 눌러야 한다는 입력정보의 검증로직을 작성합니다. 이 때 일반적인 웹 어플리케이션의 접근 방식은 체크박스의 “checked” property 상태 정보를 얻기 위해서 DOM API나 jQuery를 사용합니다. 아래 예제를 통해서 확인해 봅시다.

결국 DOM API나 jQuery 둘 중 어느 방식이든지 상태 정보가 필요할 때에 1. 해당 DOM에 접근한 후, 2. DOM의 상태정보를 읽어서, 3. 상태정보에 따라 해당 로직을 수행하는 절차를 거칩니다.

Angular2에서는 위 방식과는 달리 “선언적인” 방식으로 상태정보를 다룰 수 있게 해줍니다. 먼저 실제 라이브 예제를 살펴 봅시다.

이 예제는 왼쪽의 체크박스의 체크 상태 여부를 오른쪽의 버튼을 클릭할 때 alert창을 통해서 확인하는 것입니다. 체크박스의 상태를 변경해 가면서 오른쪽의 버튼을 눌러 정상적으로 체크박스의 상태를 가져오는지 확인해 보세요. 그리고나서 예제의 컴포넌트의 소스를 살펴봅시다.

먼저 위 예제는 javascript가 아닌 Typescript로 작성되어 있고 앞으로도 Typescript를 사용합니다. Typescript에서는 변수에 타입을 선언해 줘야 하는데 타입의 선언은 변수명 뒤에 콜론과 함께 선언해야 합니다. (앞으로 Typescript 문법설명이 필요한 부분이 있을 때 마다 설명합니다. 자세한 설명은 “Typescript 문법 소개(미정)”나, Typescript 매뉴얼을 참고해 주세요)

각 코드에 대한 의미는 아래와 같습니다.

  • 첫 번째 줄: 클래스에 angular에게 Component라는 것을 알려주기 위한 Decorator를 사용하기 위해 import 한 것입니다.
  • 5~8번째 줄:은 Component의 뷰를 구성하는 마크업 정보입니다.
  • 11번째 줄: 클래스에 멤버 변수로 isChecked라는 변수를 선언했습니다.
  • 12~14번째 줄: 클래스의 constructor 메서드 안에 isChecked를 true로 초기화 했습니다.
  • 16~18번째 줄: 버튼의  click 이벤트 시 호출할 메서드를 선언하였습니다.

Component에 template에 익숙하지 않은  [(ngModel)], (click)이 눈에 거슬릴 텐데요. angular1을 경험해 보신 분들은 two-way binding을 알고 계셔서 눈치를 채셨겠지만, 데이터 바인딩에 대해서는 다음 글(데이터 바인딩)에서 설명 하기로 합니다.

이 예제가 보여주는 핵심은 Angular2에서는 상태정보를 Component의 멤버변수로 선언하여 이를 체크박스의 체크 상태와 자동으로 연결해 준다는 것입니다. 이로 인해 우리는 DOM API나, jQuery와 같이 직접 DOM에 접근해서 상태정보를 읽는 노력을 할 필요가 없습니다. Component안에서 상태정보를 직접 관리할 수 있습니다.

이 방식의 목적은 단순히 angular2가 사용자의 행위에 의해 변경된 HTML요소의 상태를 자동으로 Component의 멤버변수에 반영해 주어 편의를 제공해 주는 것에 있는 것이 아닙니다. 비즈니스 로직에 필요한 상태정보를 얻기 위해 비본질적인 DOM의 접근하는 로직은 프레임워크에게 위임하고 Component 안에서 뷰의 비즈니스로직 처리에만 집중하게 해주는 것이 좀 더 근본적인 목표입니다.

물론 “[(ngModel)]”과 같이 자동으로 데이터를 반영해 주기 위한 최소한의 정보를 마크업에 담아야 하기 때문에 완벽하게 뷰와 Component간의 종속성을 끊을 수는 없습니다. 또한 실제 어플리케이션을 개발하다 보면 직접 DOM에 접근해야할 일도 있고 jQuery를 쓰게 될 수도 있습니다.

하지만, 최대한 뷰에 종속되는 로직을 줄이거나 한군데에 모으고 Component의 역할을 비즈니스로직 처리와 이벤트 핸들링에 초점을 맞추는 것이 중요한 접근 방식입니다. (참고: 추후에 Service와 의존성 주입을 설명하면서 비즈니스로직은 Service로 빠지게 됩니다.)

 

정리

웹어플리케이션에서 HTML요소들 중에 사용자의 행위에 따라 상태정보를 가지는 것들이 있습니다. 이 상태정보는 기존 방식에서는 DOM에 접근해야만 얻을 수 있었습니다. Angular2에서는 Component는 순수하게 상태정보를 다루고 사용하는 역할에만 집중하고 DOM에 접근하는 역할은 angular 프레임워크 내부의 동작에 위임합니다. 다음 글에서 angular가 자동으로 DOM의 상태를 Component 내부의 상태에 반영해 주는 원리인 “데이터 바인딩”에 대해서 살펴보기로 합니다.

Angular2 글 목록보기

angular

[Angular2] 예제 프로그램 및 프로젝트 셋팅

참고: angular2에 대한 연재는 여기를 참조해 주세요. 틀린 부분이나 의견, 피드백은 언제든지 환영합니다.  

회원관리 프로그램

예제로 사용할 웹 어플리케이션은 CRUD 기능이 있는 간단한 회원관리 앱입니다.  간단하게 아래와 같이 기능을 나열해 볼 수 있습니다.

  • 로그인/로그아웃 기능: 등록한 사용자로 로그인
  • 회원 등록 기능: id, 비번, 이름, 사진, 소속, 성별, 나이 등의 정보 입력 가능
  • 회원정보 수정 기능: 로그인한 회원 본인의 정보만 수정 및 삭제
  • 관리자 기능: 전체 회원의 목록 조회, 회원 상세 정보조회 및 수정 가능

위의 기능을 포함해서 검색 등의 추가적인 기능들을 점진적으로 만들어 볼 예정입니다.

이제 프로젝트 셋팅을 하기로 합니다. 현재 프로젝트의 기본 템플릿은 quickstart 예제의 구조와 큰 차이는 없습니다. 이미 생성해 놓은 프로젝트 템플릿 압축 파일을 받아서 작업할 디렉토리에 풀면 됩니다.

압축을 풀고 에디터로 열어서 왼쪽에 다음과 같은 파일 목록이 나오면 정상입니다.

프로젝트 폴더가 위치한 곳에서 터미널창을 열고 다음과 같은 명령어를 칩니다.

[NPM]에서 설명한 것과 같이 package.json에 이미 lite-server  실행 스크립트를 두었기 때문에 위 명령어를 실행하면 바로 localhost:3000으로 웹서버가 실행됩니다. 웹서버 실행 후 브라우저 창에 “회원정보 관리”가 뜨면 정상적으로 프로젝트 초기 환경 셋팅이 끝난 것입니다.

Angular2 글 목록보기

D 프로젝트 1차 회고

프로젝트 개요

  • 일정: 2016년 1월 중순부터 5월 중순까지 약 4개월 진행, 6월부터 2차 진행 중
  • 주요 사용 기술:
    • Backend는  Spring mvc 4 기반 사내 프레임워크. Java8의 Feature들을 적극 사용하려고 노력, 리파지토리는 jOOQ를 사용
    • Frontend는 Angular2 베타에서부터 시작해서 rc.1에 이르면서 1차 마무리. Angular2와 함께 rxjs, 일부 immutablejs 활용 및 라이브러리로 lodash 사용, Angular2 개발언어로 자바스크립트가 아닌 Typescript 사용, 패키징에 Webpack 적용
  • 첫 프로젝트
  • 주로 작업한 내역:
    • 프로젝트 초기 환경셋팅
    • front 앱 템플릿 코드 셋 작성 및 패키징 환경 설정 구성 (Angular2-webpack-starter를 우리 상황에 맞게 변형)
    • D 상세 등록 서버-프론트 부분 작업
    • 프로젝트 내 이벤트 비동기 호출 처리 서비스
    • Angular2를 열심히 파 본 덕에 angular.io 매뉴얼 소소한 2건의 commit 기여

 

느낀 점1: DDD 맛보기

  • 설계 시간이 거의 없어 충분히 고민할 시간이 부족했었음
  • 그래도 욕심을 좀 부려 DDD를 공부하면서 프로젝트에 이를 접목해 보고 싶었음
  • 자료 검색 중 DDD를 맛 보게 해준 자료: 토비님의 “스프링 프레임워크와 DDD
  • 글을 읽고 찾은 적용 포인트 하나! 가능하면 빈약한 도메인 모델은 만들지 말자.
  • 내가 맡은 코드의 서비스 레이어는 애플리케이션의 공통적이고 부가적인 지원성 성격의 기능(로깅, 트랜잭션, 에러처리 등)만 다루고 비즈니스 로직을 도메인에 담아보기로 결정
  • 비즈니스 로직의 8~90%가 CRUD와 Validation이었는데 CRUD 때문에 도메인 클래스 안에 리파지토리를 넣는 코드로 구현하게 됨
  • 하나의 메서드에 여러 개의 리파지토리 구현체를 파라미터로 넘기는 것이 맞는지 의문이 들었음
  • 차라리 도메인 클래스 안에 리파지토리를 멤버로 갖고 있어 복수의 리파지토리를 파라미터로 받지 않는 것이 더 낫지 않을까 생각
  • 그러나 이건 함께 하는 팀원과 상의가 안된 부분이고 뭔가 아닌 것 같아 완전 롤백.
  • 결국 전통적인 방식으로 서비스 레이어가 커지는 스타일로 CRUD 처리.
    • 나중에 알게 되었지만 위와 같은 스타일이 Active Record(활성레코드) 패턴

 

  • 원점에서 DDD는 도대체 어떤식으로 코드를 짜는 건지 샘플이 보고 싶어서 여러 차례 구글링 해봤지만 마땅한 예제 소스를 발견 못함
  • 그러다가 참고할 자료로”엔터프라이즈 애플리케이션 아키텍처 패턴” 책 읽기 시작
  • 트랜잭션 스크립트, 도메인 모델, 활성 레코드 개념을 알게 되고 차이를 이해하게 됨
  • 이어서 도메인 주도 설계 책을 읽기 시작 했으나 개념이 쉽게 이해되지 않고 코드 예가 부족해서 머리에 잘 안들어왔음
  • 다시 원점에서 자료 조사를 하다 발견한 자료: 조영호님의 블로그
  • 그리고 작년에 있었던 KSUG의 세미나 영상
  • 두 개의 영상을 보고 플젝에 DDD를 어떻게 적용할지 정리를 함
  • 결론적으로 트랜잭션 스크립트, 활성레코드 스타일 대신에 가능하면 Validation, 객체 조합 및 생성 등의 비즈니스 로직을 도메인 객체로 이관
  • DDD를 살짝 맛보면서 좋았던 점
    • 서비스레이어에서 비즈니스 로직을 테스트 했으면 스프링 컨텍스트 안에서 돌려야 하니 무거운데 도메인 객체는 POJO로서 테스트 케이스가 가벼워짐
    • 서비스 레이어의 메서드의 길이가 줄고 비즈니스 로직이 메서드로 추출되면서 가독성이 향상 됨

 

느낀점 2: Java8

  • 플젝 이전의 경험은 간단한 형태의 filter나 predicate 후 collect하는 수준
  • 플젝하면서 좀 더 적극적으로 Java8의 기능을 적용해 보려고 했음

 

  • 첫 욕심: Optional을 쓰자, 쥬니어다운 “예외가 없는 프로그램이 완벽하지!”  설익은 발상으로 리파지토리의 인터페이스 반환 값을 모두 Optional로 감싸자고 제안
  • 무식하게 모델 단 건 뿐아니라  Collection까지 다 Optional로 묶음
  • Optional 변수 네이밍을 고민하다 구글링 끝에 가장 마음에 드는 것으로 maybeXX를 결정하고 네이밍
  • Optional을 쓰고 보니 모든 코드에 다음과 같은 스타일의 코드가 붙음

  • 리파지토리 조회 코드에 모두 저런 식의 코드가 붙다보니 나중에 귀찮아서 그냥 maybeXXX.get()을 바로 해서 사용하는 경우가 생겨 Optional 사용의미가 떨어짐
  • 불편함을 느낀 와중에 Collection 타입의 반환은 빈 Collection을 주면 될 것을 뒤늦게 깨닫고 Collection 반환시에는 Optional을 걷어 냄
  • 이런 경험을 하고 나서 그럼 Optional은 어떨때 사용하면 좋은지 찾아보니 주로 라이브러리 개발 등 타인에게 API 제공 시 활용하면 좋다고 함
  • jOOQ와 함께 쓰면 단 건 모델의 경우 다음과 같이 코드를 짤 수가 있어서 Optional활용이 편해짐

  • 두번 째 욕심: Java8의 꽃은 역시 스트림이지 하면서 가능한 for문에 스트림 적용
  • 다만 일부 케이스에서 오히려 스트림이 로직을 한번에 이해하기 어려웠음
  • 한 예로 복수의 데이터를 사용자로부터 입력 받아 DB에 있는 데이터와 비교해서 입력된 데이터에서 빠진 것은 삭제하고 신규로 추가된 것은 insert하는 코드
  • 여러 번의 코드 리팩토링과 스트림 내의 다른 연산을 통해서 조금 더 가독성 확보

 

  • 스트림을 쓰면서 좋은 점 중 하나인 parallelStream!
  • 독립된 복수 연산이나 API 콜 및 db insert/update에 적용
  • 혹시나 하는 마음에 parallelStream과 관련해서 조사 중 다음과 같은 글들 발견
  • 내용 골자는 parallelStream이 공통의 스레드 풀을 사용한다는 것. 그래서 Hang이 걸릴 수 있는 부분에는 별도의 스레드 풀로 돌려야 함.
  • 그래서 API 콜 및 db insert/update 부분에서 parallelStream을 쓴 경우에는 ForkJoin으로 감싸서 처리

 

느낀점 3:  jOOQ

  • 리파지토리에 JPA를 적용해 볼 수도 있으나, 기존 db 스키마를 고려했을 때 JPA보단 직접 쿼리를 짜는 것에 대한 오너십이 필요하다고 생각
  • 그렇다고 if~ else 분기처리를 포함한 쿼리 템플릿을 xml에 담아서 마이바티스를 쓰는 것은 지양하고 싶다는 것이 팀원간의 공통된 의견
  • 그래서 jOOQ를 쓰기로 함.
  • jOOQ는 queryDSL과 유사하지만 좀 더 쿼리 키워드와 가까운 메서드로 쿼리를 읽는 것 같은 코드를 작성하게 해주는 라이브러리임
  • 예를 들면, insert나 select 코드는 다음과 같은 모양을 갖고 있음

  • 위의 코드와 같이 메서드 자체가 쿼리 키워드와 동일하고, 실제 생성되는 쿼리도 메서드와 동일함
  • 사전에 테이블 스키마를 클래스로 자동 생성해 주어 insert 하거나 select 할 때 바인딩할 값들은 반드시 테이블의 컬럼 타입과 동일해야 함.
  • 즉 컴파일 타임에 db에 넣는 값의 타입 안정성 보장
  • 단점이라고 한다면 쿼리를 코드로 짜다보니 큰 실수를 하기 쉬움.
  • where 절을 빼서 전체 테이블을 업데이트 했던 아찔한 순간이 있었음
  • 나중에 파트장님이  jOOQ에서 제공하는 쿼리 실행 전후 리스너에 where절이 없을 경우 쿼리 실행을 막도로 추가해 줬음

 

느낀점 4: 예외처리는 힘들어

  • 첫 프로젝트이다 보니 RunTimeException을 어떻게 처리할 것인가 고민이 많았음
  • 기본적으로 Controller를 통해서 유저뷰까지 예외를 내리고, UI에서 해당 예외를 적절하게 처리하는 것이 옳다고 생각함
  • 다만 비즈니스 로직 상 예측 가능한  NPE 같은 것들까지 일일이 UI애서 처리하기 보다 에러코드로 추상화해서 UI로 내려주는 것으로 결론을 지음
  • 사용자가 쓰는 서비스이다 보니 에러 발생 시 원인 파악을 위해서 에러코드를 관리하는 것이 더 좋을 것 같다고 판단했음
  • 그럼에도 불구하고 예외를 어떻게 관리하고 처리할 것인가는 고민이 필요한 이슈
  • 다행히 사내 기술 공유 메일에서 이와 관련된 논의가 있어서 참고하고 통찰을 얻을 수 있었음
    • 간단한 형태의 비즈니스 로직을 예외로 던지면 스택 트레이스 오버헤드 등 있을 수 있음
    • 비즈니스 로직 중 간단하게 처리하기 어려운 경우에 한해서 예외로 던지는 것은 의미가 있음 등

 

느낀점 5: UI 개발은 어려워

  • 어드민 성격이지만 사용자가 쓰는 서비스이기에 UI쪽 공수가 많은 프로젝트
  • UI에 어떤 기술을 쓸지 상당히 중요한 요소 였음
  • 종래와 같이 jQuery로 할 경우 코드가 장황하게 나열될 위험이 높아 향후 유지보수의 어려움이 따를 것 같아 프레임워크를 쓰기로 했고 다소 과감하지만 Angular2를 사용하기로 함
  • 사용자가 쓰는 서비스다보니 다소 강도높은 QA를 하면서 실제 개발 시간의 70%가 UI개발에 쓰인 것 같음
  • Angular2 학습 비용도 있었지만, Typescript, ECMA6, rxjs등 다른 내용에 대한 적응도 필요 했음

 

느낀점 6: 애플리케이션과 SPA

  • Angluar2를 쓰면서 SPA(Single Page Application)에 대해서 다시 생각해 보게 됨
  • SPA에 대한 기존의 이해도는 하나의 페이지 안에서 원하는 모든 것을 다 할 수 있는 것 정도.
  • gmail을 이미 익숙하게 써왔으니 이정도로만 생각
  • 하지만 이번 플젝하면서 SPA는 일반적인 웹페이지와 철학 자체가 다르고 따라서 개발도 기존의 웹앱을 개발하는 방식의 관점으로 접근하면 안된다는 것을 깨달음.
  • 먼저, 이해한 바를 바탕으로 개념을 분리할 필요를 느낌
    • 홈페이지, 웹사이트 등은 하나의 큰 주제를 공유하는 웹페이지들이 고정된 URL을 가지고 있는 것
    • SPA은 하나의 서비스 또는 일련의 목적을 달성하기 위한 기능들의 묶음으로 이루어진 애플리케이션이고 다만 구동되는 호스트가 브라우저 임
    • 웹 애플리케이션은 SPA나 홈페이지, 웹사이트등 브라우저를 호스트로 해서 구동하는 애플리케이션을 말함
  • 위와 같은 개념으로 볼 때 이번 플젝의 결과물은 이론의 여지 없이 SPA임
  • 따라서 UI개발을 웹페이지 개발방식으로 접근하면 안되고 델파이나  C++(MFC)로 만드는 기존의 데스크톱 애플리케이션 관점으로 접근하는 것이 더 적합하다고 생각
  • 정작 데스크톱 앱은 Java(Swing)을 이용해 본 경험이 전부였음
  • 그래도 컴포넌트 기반 Angular2의 아키텍쳐를 보면서 컴포넌트 기반 개발 방법론에 대한 이해도를 얻을 수 있었음
  • 부트스트랩이나  angular material등의 css 프레임워크가 뜨고 있는 것도 위와 같은 맥락 안에서 웹용 UI컴포넌트로서 인기를 얻는 것이라고 볼 수도 있음
  • 즉, 현재 애플리케이션의 흐름이 기존의 OS에서 직접 구동되는 네이티브 애플리케이션에서 브라우저 위에서 동작하는 웹 애플리케이션으로 변화되었고 이러한 배경하에 SPA가 나온 것임
  • 물론 SPA가 나오기 위해서 ajax 기술 부터 Shadow dom, Web Component까지 html의 스펙도 진화한 것임
  • 거기다 최근에는 오히려 웹앱개발의 언어가 네이티브와 모바일에서까지 동작하게 만드는 추세로 발전한 것임(Electron, React Native)

매듭 짓기

  • Backend 개발을 하면서는 모델링의 중요성과 DDD를 어떻게 하면 잘 적용할 수 잇을지에 대한 경험을 쌓을 수 있었음
  • Frontend 개발을 하면서는 Typescript 덕분에 ECMA6를 다뤘던 점이 좋았고, 웹기술의 변천사의 흐름을 이해할 수 있었음

[Tip] Fork한 Github 소스 원래 소스와 싱크 하기

관심갖고 Fork 한 저장소(Repository)의 커밋 내역을 원래 저장소의 최신 커밋 내역으로 바꾸는 방법은 간단합니다.

먼저 Fork한 자신의 로컬 저장소에 remote로 원래 저장소를 등록해야 합니다. 등록하기 전에 현재 원격 저장소가 무엇이 있는지 확인해 봅니다.

현재는 Github에 있는 내 저장소만 등록되어 있을 겁니다.  이제 아래와 같이 원격 저장소에 “upstream”이란 이름을 주고 원래 소스 저장소를 추가합니다.  (이름은 사실 상관 없습니다.)

이제 다시 등록된 원격 저장소를 확인합니다.

정상적으로 “upstream”이란 명칭으로 원격 저장소가 추가 되었습니다.

다음은 추가한 “upstream” 저장소를 “fetch”합니다. fetch와 pull의 차이는 가져와서 머지까지 자동을 해주느냐 안 해주느냐의 차이이고 “fetch”는 머지는 안 해 줍니다.

대략 위와 같은 결과를 확인할 수 있으면 정상적으로 “upstream”이란 저장소의 “master” 브랜치를 가져온 것을 확인할 수 있습니다.

이제 그럼 원래 자신의 로컬 저장소의 master를 checkout합니다.

fetch 해두었던 “upstream/master”를 체크아웃했던 “master” 브랜치로 머지합니다.

머지하고 Github에도 반영하려면 git push 하시면 됩니다.

이 내용은 Github에 있던 아래 두 링크 내용입니다.

  • https://help.github.com/articles/configuring-a-remote-for-a-fork/
  • https://help.github.com/articles/syncing-a-fork/
angular_io
angular

Angular2: QuickStart 따라하기

참고: angular2에 대한 연재는 여기를 참조해 주세요. 틀린 부분이나 의견, 피드백은 언제든지 환영합니다. 

들어가기

지난번 에서는 Angular2를 사용하기 위해서 필요한 최소한의 배경 개념을 다루었습니다. 이제 Angular2가 어떻게 생겼는지 눈과 손으로 확인을 해 볼 차례입니다. 첫 예제는 Angular.io 공식 매뉴얼에 있는  5Min QUICKSTART입니다. Angular2 개발환경 셋팅 후 “Hello World” 보는 것이 목표라고 생각하시면 됩니다.

 

매뉴얼대로 묻지도 따지지도 않고 따라하면 정말 5분 안에 뭔가 나오기는 합니다만, 내가 뭘 했는지 이해하기 쉽지 않습니다. 따라서 본 글에서는 퀵스타트 예제와 동일하게 내용을 다루면서도 조금 더 이해하기 쉽도록 내용을 풀어보고자 합니다. 일단은 있는 그대로 따라하며 브라우저에 화면을 띄워 본 후에 다시 처음으로 돌아와 설명을 읽어보시기를 권장합니다.

 

에디터 선택

아무리 퀵스타트라지만 개발을 하려면 연장이 필요하겠지요? 필요한 도구는 Typescript를 지원하는 IDE나 에디터 하나만 있으면 됩니다. 대부분의 유명한 에디터나 IDE가 Typescript용 플러그인을 제공하고 있으므로, 가장 손에 익숙한 것을 사용하셔도 됩니다. 본 연재에서는 ATOM 에디터를 사용합니다. 공식 사이트에서 OS에 맞게 install 파일을 받으시고 정상적으로 설치가 완료되면 아래와 같은 화면을 만날 수 있습니다.

 

first_atom

 

그리고 우측 두번째 버튼 “Install a Package”을 눌러 “atom-typescript“만 설치해 주시면 됩니다.

 

typescript_atom

 

개발환경 구성

개발환경 구성은 다음의 순서로 이루어집니다.

  1. npm 활용을 위한 node 설치
  2. 프로젝트용 빈 폴더 생성
  3. tsconfig.json 파일 생성
  4. typings.json 파일 생성
  5. package.json 파일 생성
  6. npm, typings 패키지 설치

 

Node 설치

공식 사이트에 들어가서 자신의 OS에 맞는 installer를 받아서 설치합니다. node가 정상적으로 설치되면 터미널에서 node와 npm이 정상적으로 설치되었는지 확인합니다.

 

 

node_js

 

폴더 생성

퀵스타트 앱을 작성할 폴더를 하나 생성합니다. 이 글에서는 “follow-quickstart”라는 이름으로 폴더를 생성했습니다. 생성한 폴더를 에디터에서 엽니다.

 

기본 설정 파일 생성

생성한 프로젝트 폴더 follow-quickstart에 tsconfig.json 파일을 만들고 다음의 내용을 복사해서 붙여넣습니다.

 

 

tsconfig.json은 Typescript를 Javascript로 컴파일할 때 적용할 옵션설정을 명시해 둔 파일입니다. 컴파일 할 때 터미널에서 “tsc” 명렁어를 사용해서 컴파일을 하는데 만약 ES5 기준으로 컴파일 하고 싶을 경우 “tsc –target es5″를 입력하면 되는데요. 반복적인 컴파일 옵션은 tsconfig.json에 등록해 두면 터미널에서 불필요하게 파라미터를 추가할 필요가 없습니다. 자세한 컴파일 옵션과 관련해서는 아래의 링크를 참조하면 됩니다.

  • https://www.typescriptlang.org/docs/handbook/compiler-options.html (컴파일 옵션)
  • http://www.typescriptlang.org/docs/handbook/tsconfig.json.html (tsconfig.json 정의)

 

이어서 typings.json 파일을 만들고 역시 다음의 내용을 복사해서 붙여넣습니다.

 

typings는 Typescript를 사용할 때 따라 붙는 필수 툴입니다.  typings에서 대해서 설명하려면 지난 시간에 설명한 Typescript 정의에 대해서 복기해 볼 필요가 있습니다. 지난 글에서 Typescript는 ES5 + ES6 + 타입 등의 추가 Syntax로 이루어진 스크립트 언어라고 설명했습니다. 이 정의에서 중요한 부분은 ECMA에 없는 바로 타입입니다.

 

자 그럼, 질문을 하나 드려보겠습니다. 기존의 Javascript는 타입이 없는데 Typescript로 개발 시 어떻게 jQuery 등의 라이브러리를 어떻게 사용할 수 있을까요? 아니 이 질문 전에 Typescript로 개발하면 기존에 Javascript로 작성된 외부 라이브러리나 코드를 사용할 수가 있는건가요? 질문에 대한 대답은 당연이 “예”입니다. 다만 한 가지 추가적인 작업이 필요합니다. 바로 타입 정보인데요. 만약 우리 소스에 jQuery를 추가로 사용하기로 했는데 타입 정보가 주어지지 않는다면 여러분의 에디터에서는 가차없이 빨간 줄을 jQuery를 사용한 코드에 뿌려줄 겁니다. 아니면 컴파일 시점에 Typescript 컴파일러가 해당 라이브러리를 인식하지 못해 에러를 여러분에게 보여줄 겁니다.

 

Typescript로 개발하는 가장 큰 장점은 정적인 코드 분석 등 IDE와 같은 도구의 지원으로 배포 및 런타임 전에 사전의 발생할 버그나 오류를 잡을 수 있다는 것입니다. 어찌보면 타입이 있는 컴파일 언어에서 당연한 소리이겠지만, 타입이 없었던 Javascript 세상에서는 분명 새로운 이야기임에 틀림 없습니다.

 

처음에 던졌던 질문으로 돌아와서 빨간 줄을 없애기 위해서 추가로 해야할 일이 있다고 말씀 드렸는데요. 그 일은 바로 해당 라이브러리에 타입 정보를 직접 추가해 주는 것입니다. 예를 들어 jQuery의 datepicker를 쓴 다고 가정해 봅시다. 그러면 우리는 아래와 같은 type 정보를 어딘가 선언해 두어야 합니다.

 

 

겨우 하나의 메서드를 선언하는데는 큰 무리가 없지만 jQuery 하나만 해도 적지 않은 메서드가 있는데 일일이 타입정보를 만들려면 힘들겠지요. 다행히도 오픈소스 생태계에서 이러한 작업(이라고 하고 노가다…)를 함께 공유하는 생태계가 있으니 그것이 바로 typings 입니다. 사전 설명이 길었는데 typings는 기존 Javascript 라이브러리들의 타입 정보만을 선언한 파일들을 내려받을 수 있도록 도와주는 툴입니다.

 

NPM을 통해서 우리가 필요한 외부 라이브러리를 쉽게 내려받고 package.json으로 관리하듯이 우리가 필요한 외부 라이브러리의 타입 정보가 필요하다면 “typings install dt~jQuery –save –global” 명령어 하나면 알아서 타입 정보가 담긴 파일을 내려받게 됩니다. –global 옵션은 일단 무시해 주세요. 본 quickstart 예제에서는 typings를 실제로 쓸 일이 없어 다음에 좀 더 자세하게 설명하기로 합니다.

 

마지막으로 package.json 파일을 만들고 다음의 내용을 복사해서 붙여넣습니다.

 

필수 패키지 설치

지난 글에서 설명한 대로 NPM을 통해서 Angular2에 필요한 패키지 목록을 다운받을 수 있도록 package.json에 dependencies와 devDependencies가 선언되어 있습니다. package.json에 라이브러리가 선언되어 있으면 install 명령어로 간단하게 위에 열거된 라이브러리들을 설치할 수 있습니다. 명령을 실행하면 package.json에 등록된 패키지와 각 패키지가 의존하는 패키지까지 설치가 자동으로 진행이 됩니다.

 

 

매뉴얼에도 적혀 있지만 install 명령 시에 터미널에서 “npm WARN”은 무시해도 개발에 문제가 없지만 “npm ERR!”은 보이면 안되는 결과입니다. npm install을 할 때 모두에게 평화가 있기를 바랍니다. 정상적으로 설치가 되었다면 현재 폴더 기준으로 node_module이란 폴더가 생성되고 이 폴더 안에 설치된 패키지들이 위치해야 합니다. 만약 패키지가 로컬 폴더에 정상적으로 설치되었는지 확인하고 싶다면 다음의 명령어로 현재 로컬 폴더에서 사용하는 패키지 목록을 확인할 수 있습니다.

 

 

npm_list

 

이후에 jQuery 등과 같은 외부 패키지가 필요할 때에 NPM을 통해서 패키지 관리가 가능합니다.  다음 기회에 필요한 패키지 설치하는 법을 설명하도록 하겠습니다.

 

Hello Angular2

개발환경 설정에 생각보다 긴 시간이 흘렀습니다. 지금까지 제대로 따라오셨다면 ATOM 에디터에 다음과 같은 폴더 구조가 보여야 합니다.

 

dev_env

 

드디어 Angular2를 만나볼 차례입니다. 위에서 생성했던 폴더 follow-quickstart에 스크립트 소스가 자리할 “app” 폴더를 만들고, 여기에 app.component.ts 란 이름의 파일을 생성 후 아래 코드를 붙여 넣습니다. app.component.ts 파일의 확장자 ts는 Typescript 파일을 의미합니다. 드디어 우리는 첫 번째 Tyescript로 만들어진 Angular2 소스를 만나게 되었습니다.

 

 

ATOM 에디터 설치 후 “atom-typescript” 패키지가 정상 설치가 되었다면 에디터에서 자동으로 ts파일을 컴파일 하여 app.component.ts외에 app.component.js와 app.component.map 파일을 생성해 줍니다. 어떻게 ts 확장자의 스크립트 소스가 js로 변환되는지 궁금하신 분들은 한번 확인해 보셔도 됩니다. 우리에게 익숙한 ES5형태의 자바스크립트 소스를 만날 수 있습니다. (map 파일은 디버깅 용도를 위한 추가 정보로 필수 요건은 아닙니다.)

 

first_compile

 

우리는 방금 Typescript로 짠 최초의 Angular2 컴포넌트를 만들었습니다. Angular 앱은 기본적으로 Tree 구조를 갖고 있으며 따라서 반드시 적어도 하나의 최상위 부모 컴포넌트가 있어야만 합니다. 방금 여러분이 작성한 소스가 이 부모 컴포넌트에 해당합니다. 컴포넌트는 웹앱 개발 뷰 템플릿을 관리하는 하나의 독립된 정도로 생각하면 좋을 것 같습니다. “컴포넌트”의 의미에 대해서는 추후에 다시 설명하도록 하겠습니다.

 

첫 컴포넌트 작성에 사용된 소스를 한 번 살펴보도록 하겠습니다. 먼저 관례적으로 최상위 컴포넌트를 AppComponent라고 이름 짓습니다. 소스 내용을 보면 생소한 부분들이 많이 있는데요. 여기서 주의하셔야 할 부분이 위 소스의 대부분이 이번에 ES6에 새로 추가된 자바스크립트 문법 이라는 점입니다.  Typescript나 Angular2에서만 문법이 아니라는 뜻입니다.

 

먼저 첫 번째 줄의 import는 ES6에서 추가된 대표적인 문법으로, Javascript 모듈을 불러올 때 사용합니다. Javascript 진영에서 모듈화와 관련된 여러 개별적인 움직임들이 있어 왔는데요. 드디어 언어적으로는 ES6에서 공식 지원이 됩니다.  Angular 역시 모든 요소들이 모듈화 되어 있고, 필요한 것은 반드시 import한 후에 사용이 가능합니다. Javascript의 모듈과 관련한 자세한 내용은 이후에 다루도록 합니다.

 

@Component는 데코레이터라고 하며, 앞으로 Angular 앱 개발 시 자주 보게 될 부분입니다. @Component안에 정의된 객체가 Angular에게 어떤 뷰 템플릿을 사용할지, 어떻게 컴포넌트를 생성할 지 등 메타 정보를 담고 있습니다. 위에서 selector의 의미는 이 컴포넌트가 갖게될 tag의 이름을 지칭하고, template은 유추할 수 있듯이 실제 컴포넌트가 렌더링 될 템플릿 정보입니다.

 

마지막으로  export class AppComponent를 한 번 분석해 봅시다. 이 문장에 총 3개의 단어가 있는데요. 뒤에서 부터 다음과 같이 읽을 수 있습니다.

  1.  “AppComponent”라는 이름의
  2. 클래스를 선언하고
  3. 이 클래스를 외부에 모듈로 공개

클래스 역시 ES6에 정식으로 추가된 것입니다. 눈치가 빠르신 분들은 아실 수도 있겠지만 클래스 선언 앞에 export가 붙는 순간 다른 파일(모듈)에서 위에서 설명한 import가 가능합니다. 자 그럼 한 번 AppComponent를 import 해볼까요?

 

Bootstraping

위에서 생성한 AppComponent는 우리가 QuickStart에서 사용할 루트 컴포넌트임을 설명했습니다. 그런데 어떻게 이 루트 컴포넌트를 실행시킬까요? Angular의 최초 진입점이 어디일까요? 이제 우리는 진입점 역할을 하는 소스가 하나 더 필요합니다. 진입점 역할을 하는 파일 main.ts를 만들어 봅시다.

 

 

처음 AppComponent 소스 보다는 눈에 좀 더 들어오지 않나요? Angular에서는 최초 실행을 위해서 bootstrap 메서드를 제공하고 있고, 이를 사용하기 위해서 역시 bootstrap을 import하고 있는게 눈에 들어오네요. import의 출처를 보면 ‘@angular/platform-browser-dynamic’으로 되어 있는데요. 이는 angular2가 bootstrap 될 환경이 browser에만 국한되지 않는다는 것을 보여줍니다.즉  bootstrap될 환경에 따라 다른 bootsrap 메서드를 import할 수가 있습니다. 예를 들면, 현재 Angular2는 네이티브 앱 및 서버 렌더링까지 지원을 가지고 있습니다.

 

Index.html

마지막으로 지금까지 작성한 두 소스를 실제 웹페이지에서 실행해 봅시다. 우리가 작성한 AppComponent 실행을 위해 다음의 index.html 파일을 프로젝트 폴더에 하나 생성합니다. (app 폴더 밑이 아닙니다.)

 

 

주석에 이미 적혀 있듯 index.html 안에도 설명이 필요한 부분이 세가지가 있습니다.  먼저 Angular2에서 필요로하는 의존 스크립트를 로드하는 것입니다. 각각의 용도는 다음과 같습니다.

  • es6-shim: 지난 시간에 설명하였던 내용으로 브라우저에서 지원하지 않는 es6 기능 지원을 위한 스크립트
  • zone.js: Angular2 에서 이벤트 detecet를 위해 사용하는 필수 라이브러리, 추후에 자세히 설명
  • Reflect.js: Angular2 에서 사용하는 @(데코레이터)를 위한 필수 라이브러리
  • System.js: Angluar2 모듈 로더

 

System.js

System.js는 우리가 app 폴더 하위에 작성한 Angular2 앱 모듈을 브라우저에서 실제 로딩하기 위한 툴로 쓰입니다. Javascript에서 모듈의 개념은 AMD, CommonJS과 같은 방식으로 이미 있던 개념이지만 위에서 잠깐 맛 본 import, export와 함께 ES6를 통해서 이제 언어에서 공식적으로 지원하게 되었습니다. Angular2 에서는 특정 모듈 로더를 사용하도록 제한하고 있지 않지만 간단한 예제용으로  System.js를 사용하고 있습니다. 본 연재에서는 추후에 System.js대신 webpack을 설명할 예정입니다.

 

일단 모듈 로더가 정상적으로 동작하기 위해서도 필요한 설정이 있습니다. 이를 위해 프로젝트 루트 폴더에 systemjs.config.js  파일을 생성 후에 아래 내용을 붙여 넣습니다. systemjs.config.js 파일의 내용을 보면 결국 사용할 패키지 선언과 실행할 앱을 설정하는 내용에 불과합니다. 지금은 자세한 내용을 이해하려 하기 보다는 모듈을 로드하기 위한 설정이 필요하다는 점만 인식합니다.

 

지금까지 index.html의 1,2번 주석에 대한 내용을 설명하였고 이제 마지막으로 다음의 내용만 확인하면 됩니다.

 

 

body 태그 안에 “my-app”이란 태그는 우리가 제일 처음 작성한 AppComponent의 “selector”에서 선언한 태그명과 동일하다는 것을 알고 있습니다. 여러분이 예측하시는 것과 같이 my-app이란 태그는 Angular2는 selector에 있는 태그명과 연결되어 실제 렌더링 시에는 AppComponent의 템플릿이 my-app 태그 위치에 자리하게 됩니다.

 

마지막으로 index.html에 styles.css가 import되어 있는데, styles.css 파일을 생성 후 공식 사이트에 있는 css를 그대로 옮겨 놓습니다.

 

 

이제 모든 작업이 끝났습니다! 터미널 창에서 다음과 같은 명령어를 통해서 간단한 http 서버를 띄워서 결과를 확인할 수 있습니다.

 

 

package.json에 “scripts” 속성에 이미 “start”라는 명령 스크립트가 선언되어 있습니다. 이 명령은 “tsc” 명령으로 타입스크립트를 컴파일하면서 동시에 “concurrently” 명령을 통해서 “npm run tsc:w”와  “npm run lite”명령까지 병렬적으로 실행합니다.

“tsc:w”는 타입스크립트 컴파일을 watch 모드로 계쏙 실행하는 코드이고, “lite” 명령은 devDependency에 선언되어 있는 “lite-server”  http 서버를 실행하는 명령어 입니다. 따라서 명령을 수행하면 자동으로 http서버가 실행되고 매뉴얼에서 볼 수 있는 것과 동일한  화면을 만날 수 있습니다.

 

 

한번 AppComponent 안에 template을 여러분의 기호에 따라 변경해 보시고 실제 브라우저에 반영되는지 확인해 보시기 바랍니다.

여기까지 따라오셨으면 이 예제의 폴더 구조는 다음과 같을 것입니다.

first_app_structure

 

정리하기

오늘은 Angular2 공식 예제 첫 번째인 Quickstart를 함께 따라해 보았습니다. 영어가 편한 분들은 사실 직접 매뉴얼을 읽으셔도 무방합니다. 그러나 이 연재의 서두에서 말씀드렸던 것처럼 다양한 개념들이 한 꺼번에 쏟아져 나오기에 읽어도 내용이 한 눈에 들어오지 않을 수 있습니다. 따라서 본 글에서는 각 내용을 너무 깊이 다루지는 않으면서도 최소한 예제를 이해하는데 도움을 줄 수 있는데까지 설명하는 것을 목적으로 하고자 노력했습니다.

 

워낙 여러 개념들이 쓰이다 보니 부족한 부분이나 잘못된 부분이 있을수도 있습니다. 또한 현재 angular.io 사이트의 가이드라인도 수시로 업데이트 되고 있어 본 포스팅의 내용과 달라진 것도 있을 수 있기에 의견 및 질문은 언제든 환영입니다.

Angular2 글 목록보기