Angular ngFor Directive 만들어보기

라코마코·2021년 9월 18일
2
post-thumbnail

" 개구리를 해부만 하지 말고, 개구리를 만들어라 "

부스트캠프 시절에 마스터님께 들은 말인데 해부 하며 구조를 공부하는것보다 직접 만들어 보며 왜 이렇게 동작하는지 파악하는게 더 효율적이다 라는 의미입니다.

이번 포스팅에서는 Angular에서 자주 쓰이는 ngFor Directive를 만들어 보며 Structural Directive에 대해서 학습해볼 예정입니다.

꽤 어려울줄 알았는데 엄청 간단한 문제였습니다. 일단 소스코드 먼저 투척하겠습니다.

import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[appCustomFor]',
})
export class CustomForDirective {
  @Input()
  appCustomForOf: Array<any>;
  constructor(
    private vcr: ViewContainerRef,
    private tpl: TemplateRef<{
      $implicit: number;
      index: number;
    }>
  ) {}
  ngOnInit() {
    this.appCustomForOf.forEach((data: any, index) => {
      this.vcr.createEmbeddedView(this.tpl, {
        $implicit: data,
        index,
      });
    });
  }
}

useCase

<!-- datas: [1,2,3,4,5,6,7,8,9] -->
<ng-container *appCustomFor="let data of datas; let i = index">
  <p>Test {{ data }} index:{{ i }}</p>
</ng-container>

화면

해석

Structural Directive를 자세히 모른다면 외계 코드같은것들로 다가올겁니다. ($implicit 같은...) 알고 보면 굉장히 쉬운 문제입니다. 우선 HTML 코드를 보며 하나씩 해석해봅시다.

<ng-container *appCustomFor="let data of datas; let i = index">

Structural Directive는 다른 Directive와 달리 * 으로 시작합니다. 따라서 *appCustomFor을 사용해서 디렉티브를 선언한것이죠. 우항에는 Structural Directive 실행을 위한 Angular만의 syntax들이 들어가있습니다.

자세한 설명은 위 공식링크를 통해서 참고해주세요.

이 syntax들은 Angular에서 컴파일되면서 아래와 같은 형태로 변경되게 됩니다.

<ng-container appCustomFor let-data [appCustomForOf]="datas" let-i="index">

Structural Directive Syntax

앵귤러 공식문서 가이드를 보면 대략 이런식으로 Syntax가 사용된다고 합니다.

*:prefix="( :let | :expression ) (';' | ',')? ( :let | :as | :keyExp )*"

(이게... 먼소리고...)

위의 제가 쓴 syntax에 대입해보며 하나씩 천천히 살펴보도록 합시다.

문법내용
:prefixHTML attribute key
:keyHTML attribute key
:local템플릿에서 사용할 local 변수
:export디렉티브에서 내보내는 변수명
:expressionAngular에서 사용하는 표현식
keyExp = :key ":"? :expression ("as" :local)? ";"?
let = "let" :local "=" :export ";"?
as = :export "as" :local ";"?

우선 prefix부터 먼저 봅시다. 저희가 Structural Directive를 사용할때 *appCustomFor을 사용한것이 기억나시나요? 이 부분이 *을 사용하였기에 prefix로 볼 수 있습니다.

let data of datas

이 문항은 어떻게 볼 수 있을까요?

let data는 let Expression으로 볼 수 있어 아래와 같이 해석됩니다.

let-data = "$implicit"

이때 :export를 따로 지정하지 않아 directive의 기본 값인 $implicit가 할당됩니다.

of datas는 keyExp으로 해석됩니다.

keyExp의 경우 prefix(appCustomFor) 값이 자동으로 붙기 때문에 아래와 같이 해석됩니다.

[appCustomForOf]="datas" // [of]="datas"가 아님 prefix가 무조건 붙어 해석됨
let i = "index"

위 부분은 let Expression으로 볼 수 있어 아래 처럼 번역됩니다.

let-i = "index";

그 외에도 위 코드에서 사용하지 않았지만 as의 문법은 (:export as :local) 이렇게 사용하고 이를 let으로 치환한다고 합니다. (let :local "=" :export ";"?)

let i = "index" as babo

let-i="index" let-babo="index"

다시 코드로

이제 다시 코드를 봅시다. 지금 제가 쓴 directive syntax는 아래처럼 해석되었습니다.

<ng-container appCustomFor let-data="$implicit" [appCustomForOf]="datas" let-i="index">

우선 appCustomForOf를 통해서 순회할 데이터가 들어오니 Input Decorator를 만들어줍시다.

  @Input()
  appCustomForOf: Array<any>;

이제 들어온 데이터만큼 순회하면서 화면을 그려주면됩니다. Structural Directive는 화면을 변경시킬 ViewContainerRef와 TemplateRef 객체를 Di 해줍니다. 이를 사용해 화면을 렌더링해주면 됩니다.

  ngOnInit() {
    this.appCustomForOf.forEach((data: any, index) => {
      this.vcr.createEmbeddedView(this.tpl);
  }

짠~! 이제 data와 i local 변수에 값을 바인딩 해줘야 합니다.

이 2 변수는 Directive에서 내보내는 (:export) 값을 참조하고 있는데요.

값들은 모두 createEmbeddedView의 2번째 매개변수로 객체로 export 하는것이 가능합니다.

  ngOnInit() {
    this.appCustomForOf.forEach((data: any, index) => {
      this.vcr.createEmbeddedView(this.tpl, {
        $implicit: data,
        index,
      });
    });
  }

생각보다 쉽죠~? Angular Directive Syntax가 첫번째 난관인데요. 어느정도 익숙해지면 전체적인 흐름이 눈에 팍 하고 이해가 되실겁니다.

Directive는 많은 가능성을 가지고 있습니다. Directive를 사용하면 관심사를 쉽게 분리할 수 있고, 또 Directive에서 값을 export하는것도 가능하기 때문에 함수로써의 기능도 수행할 수 있죠.

host Elements에 Directive를 붙이는 기능도 현재 개발중이라고 하는데요. 이 기능이 완성되면 앵귤러 컴포넌트간에 Directive를 통한 기능 조합이 가능해져서 가능성이 무궁무진하다고 합니다.

이상 포스팅을 마치겠습니다

0개의 댓글