[개발 공부]/[AngularJS]

[AngularJS] ng-repeat & track by / 더블콜론(이중콜론) ::

wild keyboardist 2021. 12. 15. 15:28

[AngularJS] ng-repeat 과 track by / 더블콜론(이중콜론) ::

 

 

 

  • 더블콜론 ( :: ) 을 사용하는 이유? 

 

시작하기 전 간단하게 더블콜론(double colon) 에 대해서 짚고 넘어가자.

ng-repeat 에서 {{ :: friends.age }} 이런식으로 표현하게되면,

해당 요소는 최초 한번만 바인딩 하기로 제한하는 것이다. (one-time binding)

 

=> 해당 요소가 다시 렌더링되지 않는 이상, 값의 변화는 화면에 반영되지 않는다!

 

 

 

 

 

 

이제 본문으로 들어가서,

 

ng-repeat 은 $watchCollection 이란 친구로 컬렉션 내의 변화를 감지한다.

(여기선 $watchCollection 이라는 것이 있다더라...하고 넘어가면 된다)

 

 

  • 순회하는 collection 의 요소가 추가되거나,
  • 순회하는 collection 의 요소가 없어지거나,
  • 순회하는 collection 의 요소의 순서가 재배열되거나,

 

할 때에, ng-repeat 은 해당 변화를 DOM 에 즉각적으로 반영해주는데,

하지만 이렇게 변화가 있을때마다 화면에 보이는 모든 DOM 들을

전부 다시 그려준다면 해도해도 이런 낭비가 없다.

(전부 렌더링해주는데 사용되는 메모리며, 속도문제며 등등....)

 

 

 

이를 방지하기 위해 "keep track"이라는 기능을 사용하여,

이미 렌더링 되어있는 DOM 은 다시 렌더링 하지않고, 새로 업데이트 된 부분만

추가로 렌더링해주어 렌더링 퍼포먼스를 향상시키는데,

이를 "DOM 의 재사용"이라고 한다.

 

 

 

오, 센스있는데? 라고 할 수 있지만,

그러나, DOM 을 재사용하기 때문에 일어나는 문제가 있다. 아래의 예시를 보자.

 

 

 


[예시]

 

$scope.friends = [

       {name:'John', age:25},

       {name:'Mary', age:40},

       {name:'Peter', age:85}

]

 

 

(A) <li ng-repeat="friend in friends">

=> {{friend.name}} is {{friend.age}} years old.   

 

 

(B) <li ng-repeat="friend in friends">

=> {{friend.name}} is {{ :: friend.age}} years old.  

 

 

(C) <li ng-repeat="friend in friends track by friend.name">

=> {{friend.name}} is {{ :: friend.age}} years old. 

 

 

(D) <li ng-repeat="friend in friends track by $index">

=> {{friend.name}} is {{ :: friend.age}} years old. 

 


 

1) $scope.friends.age 에 전부 +5 씩 더해주는 함수를 실행했을 때:

 

오직 (A)만 아래와 같이 age+5 가 즉시 바인딩되어 나타날 것이다.

  • John is 30 years old.
  • Mary is 45 years old.
  • Peter is 90 years old.

 

(B), (C), (D) 는 최초 한번만 바인딩되기 때문에 age+5 는 화면에 변화가 반영되지 않는다.

  • John is 25 years old.
  • Mary is 40 years old.
  • Peter is 85 years old.

 

 

=> (B)(C)(D) 는 더블콜론( :: friends.age ) 을 사용하여,

one-time binding, 즉, 처음 한번만 바인딩하기로 제한을 두었기 때문이다.

화면이 그려진 이후, 값의 변화가 생겼어도 최초 한번만 바인딩되기 때문에

그 이후의 변화는 화면에 반영되지 않는다.

 

 

 

 

2) 위에서 5살씩 더해진 $scope.friends 를 원본에 덮어쓰는 함수를 실행했을 때 (원본이 변경):

 

(A)는 그대로 age+5, 이제 (B)에서도 $scope.friends 원본의 변화가 반영되어 재 렌더링되므로 age+5 되어 나타난다.

  • John is 30 years old.
  • Mary is 45 years old.
  • Peter is 90 years old.

 

(C)(D)는 원본이 변했더라도 각각 name 과 $index 만을 tracking 하므로 age+5 의 변화는 감지하지 못한다.

  • John is 25 years old.
  • Mary is 40 years old.
  • Peter is 85 years old.

 

 

=> 둘 다 $scope.friends.age 의 변화를 감지하지 못하므로, DOM 도 다시 바인딩되지 않는다.

재 렌더링되지 않으므로, 화면에도 변화는 없다.

 

 

 

 

3) $scope.friends 에서 첫번째 요소를 shift() 함수를 사용하여 remove 했을 때:

 

(A), (B)는 첫번째 요소인 John 이 없어져서 반영된다.

  • John is 30 years old.
  • Mary is 45 years old.
  • Peter is 90 years old.

 

(C) 또한 마찬가지로 첫번째 요소가 없어져서 반영된다. (age+5 는 여전히 반영되지않음)

  • John is 25 years old.
  • Mary is 40 years old.
  • Peter is 85 years old.

 

(D)는 Mary와 Peter의 age가 잘못되어있다.

  • John is 25 years old.
  • Mary is 25 years old. (25는 John의 age)
  • Peter is 40 years old. (40은 Mary의 age)

 

 

=> (D)는 $index를 tracking 하는데, $index 의 값은 언제나 0, 1, 2... 와 같다.

즉, tracking 중인 $index의 값은 변함이 없다고 판단되므로,

DOM 은 다시 바인딩되지 않고 "DOM이 재사용" 된다.

 

또한, 더블콜론( :: friends.age ) 을 사용하였으므로,

처음 한번만 바인딩하기로 제한을 두어, age는 그대로 동일하게 남아있게된다.

결과적으로 화면상 age가 밀려나게된 것처럼 보인다.

 

track by $index 사용은 AngularJS 에서도 공식적으로 추천하지 않는다.