(번역) angularjs 마법 이해하기: 기본 데이터 타입(primitive type)을 바인딩 하지 마라

원문: http://www.codelord.net/2014/05/10/understanding-angulars-magic-dont-bind-to-primitives/

angularjs를 사용하고 있다면 ‘기본 데이터 타입을 바인딩 하지 마라’라는 말을 들어본적이 있을 것이다. 이 글에서는 기본 데이터 타입을 바인딩하면 발생할 수 있는 문제를 예제를 통해 설명하겠다.

예제

책의 태그정보를 다루는 앱이 있다고 가정하자. 사용자가 책의 태그 정보를 수정할 수 있는 앱을 angularjs로 만들면 다음과 같다.

위 코드를 직접 확인해보자. input의 책 태그 정보를 수정해보자. input의 값은 문제없이 변경이 된다. 하지만 book.tags에 반영이 되진 않는다.

이 현상은 ng-repeat이 각 tag에 대해서 별도의 childe-scope을 만들기 때문에 input에서 변경한 값이 book.tags에 반영이 되지 않기 때문에 발생한다. 코드로 표현하자면 다음과 같다.

bookCtrl scope = { tags: [ 'foo', 'bar', 'baz' ] } ng-repeat child scopes = { tag: 'foo' }, { tag: 'bar' }, { tag: 'baz' }

child scope을 보면 book.tags 값과 양방향 데이터 바인딩 처리를 해주고 있지 않다. 그래서 child scope의 데이터를 담고 있는 input의 값을 변경해도book.tags의 값은 변하지 않는다.

양방향 바인딩이 되려면 $watch, $watchCollection 등의 처리를 해줘야 한다. (자세한건 여길 참고)

tag의 값을 기본형 데이터가 아닌 객체를 사용해 저장하면 위와 같은 문제는 해결이 된다. 하지만 book.tags의 데이터 타입을 변경하고 싶지 않을때는 어떻게 해야 할까?

잘못된 시도

다음과 같이 시도해볼수 있다.

ng-model이 child scope의 값 대신 book.tags를 바로 연결한다. 이제 input의 변경이 book.tags의 값에 반영된다. 데모를 확인해보자. 이게 뭔가 싶을거다. ㅋㅋ

ng-repeat이 이런 현상을 발생시키는 범인이다. ng-repeat은 리스트의 모든 값들을 감시하고 있다가 값이 변경되면 변경된 값을 해당 엘리먼트에 반영한다.

자바스크립트의 기본 데이터 타입은 불변(immutable)의 값이다. 값을 변경한다는 것은 실제로 가지고 있는 기존의 데이터를 버리고 새롭게 변경된 값을 갖는 객체를 생성해서 사용하는 것이다. 이러한 기본 데이터 타입의 변경은 ng-repeat이 기존 데이터를 바인딩하고 있던 input을 삭제하고 새롭게 생성된 데이터를 바인딩하는 input을 추가하는 것이다. 이게 input에서 값을 수정할때마다 포커싱을 잃는 이유다.

해결 방법

ng-repeat이 기본 데이터 타입 값을 감시하는게 아니라 리스트의 index를 이용하도록 하는 것이다. 어떻게하면 ng-repeat가 index를 감시 대상으로 설정할 수 있을까? 운좋게 angularjs 1.2 버전에서 도입된 track by 를 이용하면 된다.

결과를 보자

track by성능 향상에 더 유용하지만, 이러한 형태로 사용이 가능한걸 알고 있는것도 좋다. 이런 내부 동작을 이해하면 angularjs 마법을 이해하는데 도움이 된다.

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.

다음의 HTML 태그와 속성을 사용할 수 있습니다: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">