Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

개발을 하기는 합니다만

[Angular][06](컴포넌트와 템플릿) 템플릿 문법 본문

Angular/Angular Official Guide

[Angular][06](컴포넌트와 템플릿) 템플릿 문법

jaeyoung-lee 2020. 3. 17. 20:07

본 글은 앵귤러 공식 홈페이지의 DOC 항목을 한국어로 (공부 목적으로) 요약(?)정리(?)한 글임을 밝힙니다.

템플릿 문법

앵귤러 애플리케이션은 컴포넌트 클래스 인스턴스와 사용자가 마주하는 템플릿의 상호작용을 통해서 사용자가 보는 것과 할 수 있는 것들을 관리합니다.

Model Vice Controller(MVC)dhk model-view-viewmodel(MVVM)을 통해 이미 컴포넌트와 템플릿 이중 구조에 이미 익숙할 수도 있습니다. 앵귤러에서, 컴포넌트는 컨트롤러/뷰모델의 역할을하고 템플릿은 뷰를 담당합니다.

이번장은 앵귤러 템플릿 언어에 대한 포괄적이고 기술적인 설명을 제공합니다. 기본적인 템플릿 언어의 원리와 다른 장에서 접하게 될 대부분의 문법을 설명합니다.

이번 장에서 개념들을 설명하게 될 예시 코드는 템플릿 문법 예시코드 실행/다운로드에서 자세히 볼 수 있습니다.

템플릿에서의 HTML

HTML은 앵귤러 템플릿이 사용하는 언어입니다. 거의 모든 HTML 문법이 템플릿 문법에서도 유효합니다. 그 중 script 엘리먼트는 예외입니다 - 스크립트 주입(script injection) 공격의 위험성을 배제하기 위해서입니다. 실제로 script 는 무시되며 브라우저 콘솔에 경고문을 띄웁니다. 자세한 사항은 보안 페이지를 확인해주세요.

 

몇 가지 추가적인 HTML 문법이 템플릿 문법에서는 사용될 수 없습니다. html, body, base 엘리먼트가 그 예이며, 실제로 템플릿 문법에서 아무런 동작을 하지 않습니다. 다른 거의 모든 것들은 템플릿 문법에서도 사용 가능합니다.

 

컴포넌트와 디렉티브를 통해서 새로운 앨리먼트나 어트리뷰를 추가함으로써 HTML 문법을 다채롭게 할 수 있습니다. 다음 섹션에서는 데이터 바인딩을 통해서 동적으로 DOM(document object model)의 밸류를 가져오고 바꾸는 법에 대해 알려드리겠습니다.

 

데이터 바인딩의 한 형식인 문자열 바인딩을 통해 HTML 템플릿이 얼마나 다채로워 질 수 있는지 확인해봅시다.


문자열 바인딩과 템플릿 표현식

문자열 바인딩은 HTML 엘리먼트 태그 안과 어트리뷰트 값 할당란 안에 문자열을 조합하여 넣을 수 있게 해줍니다. 템플릿 표현식은 문자열을 조합할 수 있게 해주는 방법입니다.

 

이번 장에서 소개될 예시는 문자열 바인딩 라이브 예제 / 다운로드를 통해 확인할 수 있습니다.

문자열 바인딩 {{ ... }}

문자열 바인딩은 문자열 안에 포함된 표현식을 말합니다. 기본적으로, 문자열 바인딩은 이중 중괄호 {{, }}를 사용합니다.

다음 코드에서 {{ curentCustomer }}은 문자열 바인딩입니다.

<!-- src/app/app.component.html -->

<h3>Current customer: {{ currentCustomer }}</h3>

이중 중괄호 안의 문자는 보통 컴포넌트 프로퍼티의 이름입니다. 앵귤러는 이중 중괄호 내부의 문자를 해당 문자에 상응하는 컴포넌트 프로퍼티가 가지는 값으로 대체합니다.

<!-- src/app/app.component.html -->

<p>{{title}}</p>
<div><img src="{{itemImageUrl}}"></div>

위의 예제에서, 앵귤러는 title과 itemImageUrl 프로퍼티를 찾아내고 비어있는 템플릿을 이들로 채우기 때문에, 제목 문자와 이미지를 표시할 수 있습니다.

보통, 이중 중괄호 내부에 있는 텍스트는 템플릿 표현식(template expression)으로, 앵귤러는 이들을 먼저 평가(evalutae)레서 문자열로 바꾸어줍니다. 다음의 문자열 바인딩은 두 수를 더함으로써 이를 보여줍니다.

<!-- src/app/app.component.html -->

<p>The sum of 1 + 1 is {{1 + 1}}.</p>
<!-- 출력: "The sum of 1 + 1 is 2" -->

이러한 표현식은 호스트 컴포넌트의 메소드 또한 불러올 수 있습니다. 하단의 예제에서는 getVal()메서드를 불러와 사용했습니다.

<!-- src/app/app.component.html -->

<p>The sum of 1 + 1 is not {{1 + 1 + getVal()}}.</p>
<!-- 출력: "The sum of 1 + 1 is not 4" -->

앵귤러는 이중 중괄호 내부의 모든 표현들을 평가하고, 표현식의 결과를 문자열로 변환하며, 이 문자열을 이웃하는 문자열과 연결시킵니다. 최종적으로는, 완성된 문자열을 엘리먼트나 디렉티브 프로퍼티에 지정시킵니다.

이러한 작용은 엘리먼트 태그 사이에 결과값을 넣거나 어트리뷰트에 지정하는 것처럼 보입니다. 하지만 문자열 바인딩은 프로퍼티 바인딩을 사용하는 앵귤러의 특별한 문법일 뿐입니다.

Component 메타데이터의 interpolation 옵션을 변경하면 {{ }} 대신 다른 표기법으로 문자열 바인딩을 할 수 있습니다

템플릿 표현식(template expression)

템플릿 표현식은 결괏값을 만들어내고 이중 중괄호 {{ }} 내부에 나타냅니다. 앵귤러는 표현식을 실행하고 바인딩 타켓의 프로퍼티에 지정합니다 - 타켓은 HTML 엘리먼트, 컴포넌트, 혹은 디렉티브가 될 수도 있습니다.

문자열 바인딩 {{ 1 + 1 }}은 1+1이라는 템플릿 표현식을 이중 중괄호로 감싼 형태입니다. 문자열 바인딩에서, 템플릿 표현은 = 표현 오른쪽의 겹따옴표 " " 안에 위치하게 됩니다 - [property] = "expression" 처럼 말이죠.

문법적 측면에서, 템플릿 표현은 JavaScript와 유사합니다. 많은 JavaScript 표현이, 몇가지 예외만 제외하면, 템플릿 표현에서도 유효합니다.

예를들어, 다음과 같은 자바스크립트 표현식은 부작용이나 에러를 일으킬 수 있습니다.

  • 값 할당 ( =, +=, -=, ... )
  • new, typeof, instanceof 연산자
  • ; 나 , 로 나타내는 체이닝 표현식
  • 증감연산자 --, ++
  • ES2015 이루 도입된 연산자 중 일부

자바스크립트와 템플릿 문법은 다음 측면에서도 차이를 보입니다

  • 템플릿 문법은 | 나 & 같은 비트연산자를 지원하지 않습니다.
  • 템플릿 표현식을 위한 연산자도 있습니다: |, ?, !

표현식의 컨텍스트

표현식의 컨텍스트(expression context)는 대게 컴포넌트 인스턴스와 범위를 공유합니다. 다음 코드에서 {{ }} 내부의 recommended와 " " 속의 itemImageUrl2는 AppComponent의 프로퍼티를 가리킵니다.

<!-- src/app/app.component.html -->

<h4>{{recommended}}</h4>
<img [src]="itemImageUrl2">

템플릿 표현식은 또한 템플릿 인풋 변수(template input variable - 하단 예시에서는 let customer)나 템플릿 참조 변수(template reference variable - 하단 예시에서는 #customerInput)와 같은 템플릿 컨텍스트 내부의 프로퍼티 또한 가리킬 수 있습니다.

<!-- src/app/app.component.html (템플릿 입력 변수) -->

<ul>
  <li *ngFor="let customer of customers">{{customer.name}}</li>
</ul>
<!-- src/app/app.component.html (템플릿 참조 변수) -->

<label>Type something:
  <input #customerInput>{{customerInput.value}}
</label>

템플릿 표셕식 내부의 컨텍스트 범위는 템플릿 변수, 디렉티브의 context 객체(만약 있다면), 그리고 컴포넌트의 멤버가 조합된 범위입니다. 이 중 두 군데 이상에 존재하는 변수 명을 참조한다면 우선숭위는 다음과 같습니다 - 템플릿 변수 > 디렉티브의 context 객체 > 컴포넌트 멤버.

이전의 예시는 이와 같은 상황을 잘 보여줍니다. 템플릿 안에서 컴포넌트의 customer 프로퍼티와 *ngFor가 정의한 customer 템플릿 변수가 충돌하고, 우선순위가 높은 템플릿 변수로 customer가 할당됩니다.

{{ customer.name }}에서 customer는 컴포넌트 프로퍼티가 아닌 템플릿 인풋 변수입니다.

템플릿 표현식은 undefined 를 제외하고는 전역 범위에 있는 객체를 사용할 수 없습니다. window나 document를 지정할 수 없다는 말입니다. 추가적으로, console.log() 나 Math.max()와 같은 함수를 호출할 수도 없습니다.

템플릿 표현식 가이드라인

템플릿 표현식을 사용할 때는 다음 가이드라인을 준수하는 것이 좋습니다.

  • 간단하게
  • 빠르게 실행되도록
  • 외부 영향은 최소화

간단하게

물론 복잡한 템플릿 표현식을 쓰는 것이 불가능한 것은 아니지만, 이를 지양하는 것이 좋습니다.

프로퍼티의 이름이나 메소드 실행을 간단히 쓰는 것이 좋고 필요에 따라 boolean 부정인 !를 사용해도 좋습니다. 하지만 컴포넌트나 비즈니스 로직은 개발과 테스트가 편해지도록, 템플릿이 아닌 컴포넌트에 넣는 것이 좋습니다.

빠르게 실행 되도록

앵귤러는 변화감지 싸이클마다 템플릿 표현식을 실행시킵니다. 변화감지 사이클은 Promise 결과값 반환, HTTP 결괏값, 타이머 이벤트, 키 입렵, 마우스 이동 등의 다양한 비동기(asynchornous) 동작에 의해 작동됩니다.

그래서 표현식은 되도록 빨리 종료되어야 합니다. 그렇지 않으면 사용자(특히 느린 기기를 사용하는)는 실증을 느낄테니까요. 연산이 많이 필요하다면 캐싱을 사용하는 것도 고려해보세요.

외부 영향 최소화

템플릿 표현식은 타켓 프로퍼티의 값을 제외하고는 어떠한 애플리케이션의 상태도 변경하면 안됩니다.

 

이 규칙은 앵귤러의 "단방향 데이터 흐름(unidirectional data flow)" 원칙에서도 매우 중요합니다. 컴포넌트의 프로퍼티를 읽어오는 동안 다른 표시 결과들이 바뀔 염려를 할 일이 없어야합니다. 단일 렌더링 과정에서 뷰는 안정적(타 컴포넌트의 템플릿 표현식에 의해 변화 되어서는 안된다)이여야 합니다.

 

이러한 부작용을 최소화하고 앵귤러의 변화 감지 기능을 향상시키기 위해서 "멱등적(idempotent)"인 표현식을 사용하는 것이 이상적입니다. 앵귤러에서 멱등적이란, 특정한 값을 기준으로 표현식을 작성했을 때, 이 값이 바뀌기 전까지 표현식이 항상 동일한 결괏갑을 배출해야 한다는 것을 의미합니다.

 

그리고 여기서 특정한 값은 이벤트 루프가 한 번 실행되는 동안 바뀌어서는 안됩니다. 만약 멱등적 표현식이 문자열이나 숫자를 반환하고 이 표현식이 두 번 연속 실행될 때, 이 표현식은 동일한 문자열이나 숫자를 반환해야합니다. 만약 멱등적 표현식이 배열과 같은 객체를 반환한다면, 몇 번이 실행되더라고 같은 객체를 참조해야 합니다.

이러한 규칙에서 예외로 작용하는 것이 *ngFor 입니다. *ngFor 는 trackBy라는 기능이 있는데, 이 기능은 순회과정에서 이전과 다른 객체를 참조하더라도 같은 객체를 참조하는 것으로 간주할 수 있습니다. 자세한 사항은 *ngFor with trackBy를 참고하세요.


템플릿 실행문(template statements)

템플릿 실행문은 엘리먼트, 컴포넌트, 디렉티브와 같은 바인딩된 타겟이 이벤트를 일으킬 때 반응합니다. 이벤트 바인딩 섹션에서, 템플릿 실행문이 = 표시의 우측에 나타나는 것을 확인 할 수 있습니다. 다음과 같이 말이죠 - (event) = "statement".

<!-- src/app/app.component.html -->

<button (click)="deleteHero()">Delete hero</button>

 템플릿 실행문은 변화를 발생시킵니다. 변화를 발생시킨다는 것이 이벤트의 핵심입니다. 이 것이 사용자의 행동으로부터 애플리케이션의 상태를 업데이트하는 방법입니다.

 

이벤트에 반응하는 것은 앵귤러의 "단방향 데이터 흐름"의 한 측면이라고 볼 수 있습니다. 해당 이벤트 루프 턴에서 객 체 내의 어디에 있는 어떠한 값이든 자유롭게 바꿀 수 있습니다.

 

템플릿 표현식처럼, 템플릿 실행문은 자바스크립트처럼 생긴 문법을 사용합니다. 템플릿 실행문 파서는 템플릿 표현식 파서와 다르며, 템플릿 실행문 파서는 기본적인 값 할당(=)과 체이닝 표현식(;)을 사용할 수 있습니다.

 

그러나 다음과 같은 자바스크립트 문법은 템플릿 실행문에서 사용할 수 없습니다.

  • new 키워드
  • 증감연산자 ++와 --
  • 연산 할당자 += 와 -=
  • 비트 연산자 | 와  &
  • 파이프 연산자

 

템플릿 실행문의 컨텍스트

템플릿 표현식과 같이, 템플릿 실행문은 이벤트 핸들링 함수나 컴포넌트 인스턴스와 같이 템플릿 실행문의 컨텍스트의 범위 내에 있는 것들만 사용 가능합니다.

 

템플릿 실행문의 컨텍스트는 흔이 컴포넌트 인스턴스의 범위와 같습니다. 예를들어 (click)= "deleteHero()" 의 deleteHero 메소드는 데이터를 처리하는 컴포넌트 내의 메서드입니다.

<!-- src/app/app.component.html -->

<button (click)="deleteHero()">Delete hero</button>

템플릿 실행문의 컴포넌트는 또한, 템플릿 고유의 컨텍스트 내에 존재하는 프로퍼티에 접근 할 수 있습니다. 아래 예제에서, 템플릿의 $event 객체, 템플릿 인풋 변수(let hero) 와 템플릿 참조 변수(#heroForm)은 컴포넌트의 이벤트 핸들링 메소드로 전달 됩니다.

<!-- src/app/app.component.html -->

<button (click)="onSave($event)">Save</button>
<button *ngFor="let hero of heroes" (click)="deleteHero(hero)">{{hero.name}}</button>
<form #heroForm (ngSubmit)="onSubmit(heroForm)"> ... </form>

템플릿 컨텍스트에 정의된 항목이름과 컴포넌트 컨텍스트에 정의된 항목 이름이 중복된다면, 템플릿 컨텍스트 내의 항목이름이 우선적으로 처리됩니다. 위의 deleteHero(hero) 내에서 hero는 컴포넌트의 hero 프로퍼티가 아닌 템플릿 인풋 변수인 hero 입니다.

 

템플릿 실행문 가이드라인

템플릿 실행문은 템플릿 표현식처럼 전역 공간에 접근할 수 없습니다. window나 document에 접근할 수 없으며 console.log나 Math.max 함수를 호출할 수 없습니다.

 

템플릿 표현식과 바찬가지로, 복잡한 템플릿 실행문을 작성하는 것을 피해야합니다. 메소드를 호출하거나 간단한 프로퍼티 값 할당을 하는 것이 보통입니다.

 


바인딩 문법: 개요

데이터 바인딩은 애플리케이션의 데이터의 값에 따라 사용자에게 보여지는 것들을 관리하는 기능입니다. HTML에 직접 데이터를 넣거나 직접 HTML로부터 값을 받아 올 수 있지만, 데이터 바인딩 프레임워크를 통해서 데이터를 읽고, 쓰고, 관리하는 것이 훨씬 편리합니다. 단순하게 바인딩할 값의 소스와 타겟 HTML 엘리먼트 사이의 바인딩을 정의하면, 프레임워크가 나머지 작업을 실행해줍니다.

Comments