, 원문 : How Selections Work
2014년 11월 11일, 번역 :     원문 함께 보기   한글만 보기

셀렉션 작동 원리

 

Any sufficiently advanced technology is indistinguishable from magic. –Arthur C. Clarke
충분히 발달한 기술은 마술과 구별이 안된다. –Arthur C. Clarke

In the past I have presented simplified descriptions of D3’s selections, providing only enough detail to get started. This article takes a more comprehensive approach; rather than saying how to use selections, I will explain how selections are implemented. This may take longer to read, but it should dispel any magic and help you master data-driven documents.

예전에 D3셀렉션에 대해, D3를 시작하는 데 필요한 정도로만 단순하게 설명프리젠테이션을 준비한 적이 있었다. 이 글에서는 단순히 셀렉션의 사용 방법을 다루기 보다는, 구현 방법을 포함하여 셀렉션을 더 종합적으로 접근해서 다루고자 한다. 그래서 읽는 데 오래 걸릴 수는 있겠지만, D3가 마술 같다는 느낌을 떨쳐 내고, 데이터 기반 문서(Data-Driven Documents, D3)를 제대로 마스터 하는 데 분명 도움이 될 것이다.

The structure of this article may at first seem arbitrary. It describes the internal workings of selections rather than the design motivations, so you may wonder why such mechanisms are needed. This is simply because it is easier to lay all the pieces out first before explaining how everything works in tandem. By the time you read to the end, the intent of the design as well as its function should be clear.

이 글의 구조는 처음보면 뭔가 정돈되지 않고 멋대로 나열되어 있는 것처럼 보일 수도 있을 것이다. 이 글은 셀렉션을 이렇게 설계한 이유에 대한 내용 보다는 셀렉션의 내부 작동 방식에 대해 설명하고 있기 때문에, 읽다보면 왜 이런 메커니즘이 필요했는 지 궁금할 수 있다. 먼저 필요한 요소들을 죄다 꺼내 놓은 다음에 각 요소가 어떻게 동작하는 지 설명하는 것이 더 쉬우므로, 끝까지 읽고 나면 작동 원리 뿐 아니라 설계의 의도까지도 명확하게 이해할 수 있을 것이다.

D3 is a visualization library, so this article incorporates visual explanations to accompany the text. In subsequent diagrams, the left side of the diagram will show the structure of selections, while the right side will show the structure of data:

D3는 시각화 라이브러리다. 따라서, 텍스트에 시각적인 설명을 포함해서 이야기를 풀어갈 것이다. 설명에 나오는 다이어그램에서, 아래와 같이 왼쪽은 셀렉션의 구조를 보여주고, 오른쪽은 데이터의 구조를 보여줄 것이다:

왼쪽은 셀렉션의 구조 연결부 오른쪽은 데이터의 구조

Rounded rectangles such as thing indicate JavaScript objects of various types, ranging from literal objects ({foo: 16}), primitive values ("hello"), arrays of numbers ([1, 2, 3]) to DOM elements. Certain special object types are colored, including selection, array, and element. References from one object to another are indicated with connecting lines (). For example, an array containing the number 42 looks like:

thing처럼 모서리가 둥근 네모로 표시된 것은 리터럴 객체({foo: 16}), 원시 데이터 값("hello"), 숫자 배열([1, 2, 3])에서 DOM 문서요소까지 다양한 자바스크립트의 객체를 나타낸다. 셀렉션, 배열, DOM요소 처럼 특별한 객체는 색깔을 넣어 표시했다. 한 객체에서 다른 객체로의 참조는 연결선으로 표시했다. 예를 들어, 42라는 숫자 하나를 원소로 가지는 배열은 다음과 같이 표시한다:

var array = [42];

Wherever possible, the code that generates the given selection appears immediately above the diagram. Opening your browser’s JavaScript console and creating selections interactively is a great way to test your understanding of the text!

설명에 나오는 셀렉션을 만들어내는 코드는 다이어그램의 바로 위에 배치했다. 브라우저의 자바스크립트 콘솔을 열고 직접 셀렉션을 생성하고 테스트 하면서 읽으면 훨씬 이해하기 쉬울 것이다!

Let’s begin.

이제 본격적으로 시작해 보자.

#배열의 서브클래스

You were probably told that selections are arrays of DOM elements. False. For one, selections are a subclass of array; this subclass provides methods to manipulate selected elements, such as setting attributes and styles. Selections inherit native array methods as well, such as array.forEach and array.map. However, you won’t often use native methods as D3 provides convenient alternatives, such as selection.each. (A few native methods are overridden to adapt their behavior to selections, namely selection.filter and selection.sort.)

여러분은 셀렉션이 DOM 문서요소의 배열이라는 말을 들어봤을 것이다. 한마디로 아니올시다. 셀렉션은 배열의 서브클래스다. 셀렉트된 요소의 속성이나 스타일을 설정할 수 있는 메서드를 이 서브클래스가 제공한다. 셀렉션은 array.forEacharray.map 과 같은 네이티브 메서드도 상속받아 지원하지만, D3가 selection.each 와 같은 편리한 대체 메서드를 제공하기 때문에, 네이티브 메서드를 직접 사용할 일은 별로 없을 것이다(selection.filterselection.sort 같은 몇 가지 네이티브 메서드는 재정의 되었다).

#문서요소 그룹핑

Another reason selections aren’t literally arrays of elements is that they are arrays of arrays of elements: a selection is an array of groups, and each group is an array of elements. For example, d3.select returns a selection with one group containing the selected element:

셀렉션이 문서요소의 배열이라는 말이 틀린 또 하나의 이유는, 셀렉션은 문서요소의 배열이 아니라 문서요소의 배열의 배열이라는 점이다: 셀렉션은 group의 배열인데, 각 group은 문서요소의 배열이다. 예를 들어, d3.select 는 선택된 문서요소를 원소로 하는 한 개의 group 배열을 원소로 하는 배열을 반환한다:

var selection = d3.select("body");

In the JavaScript console, try running this command and inspecting the group as selection[0] and the node as selection[0][0]. While accessing a node directly is supported by D3’s API, for reasons that will soon be apparent it is more common to use selection.node.

자바스크립트 콘솔에서 위의 명령을 실행하고 selection[0]으로 group을, selection[0][0]으로 노드를 검사해보자. D3 API로 노드에 직접 접근할 수도 있지만, selection.node를 사용하는 것이 훨씬 더 나은 이유를 곧 알게될 것이다.

Likewise, d3.selectAll returns a selection with one group and any number of elements:

마찬가지로, d3.selectAll은 여러 개의 문서요소를 원소로 하는 한 개의 group 배열을 원소로 하는 배열을 반환한다:

d3.selectAll("h2");

Selections returned by d3.select and d3.selectAll have exactly one group. The only way for you to obtain a selection with multiple groups is selection.selectAll. For example, if you select all table rows and then select the rows’ cells, you’ll get a group of sibling cells for each row:

d3.select나 d3.selectAll은 정확히 한 개의 group 배열을 반환한다. 여러 개의 group 배열을 반환하는 유일한 방법은 selection.selectAll을 사용하는 것이다. 예를 들어, 테이블의 모든 행을 셀렉트하고, 각 행의 셀을 셀렉트하면, 각 행의 셀을 원소로 하는 여러 개의 group 배열을 원소로 하는 배열을 반환 받게 된다:

d3.selectAll("tr").selectAll("td");

With selectAll, every element in the old selection becomes a group in the new selection; each group contains an old element’s matching descendant elements. So, if each table cell contained a span element, and you called selectAll a third time, you’d get a selection with sixteen groups:

selection.selectAll을 쓰면, selectAll 메서드를 호출하는 원래 셀렉션인 selection에 있던 모든 최말단 문서요소 들이 selection.selectAll에 의해 반환되는 새로운 셀렉션의 group 배열이 된다. 예를 들어, 만약 앞의 테이블 셀렉션 예제에서 각 셀이 span 문서요소를 가지고 있어서, 다음과 같이 selectAll을 한 번 더 호출하면, 두 번째 selectAll의 호출 결과로 나왔던 16개의 td 가 새로운 셀렉션에서는 아래와 같이 16개의 group 배열이 된다:

d3.selectAll("tr").selectAll("td").selectAll("span");

Each group has a parentNode property which stores the shared parent of all the group’s elements. The parent node is set when the group is created. Thus, if you call d3.selectAll("tr")​.selectAll("td"), the returned selection contains groups of td elements, whose parents are tr elements. For selections returned by d3.select and d3.selectAll, the parent element is the document element.

각 group 배열은 group 배열 내의 모든 원소의 공통적인 부모 노드를 저장하는 parentNode 속성을 가지고 있다. 부모 노드는 group 배열이 생성될 때 설정된다. 그래서 d3.selectAll("tr")​.selectAll("td")를 호출하면 부모가 tr 인 td 문서요소들을 원소로 하는 group 배열의 배열이 셀렉션으로 반환된다. d3.select 와 d3.selectAll 에 의해 반환되는 셀렉션은 document 요소parentNode 속성으로 가지는 group 배열의 배열이다.

Most of the time, you can safely ignore that selections are grouped. When you use a function to define a selection.attr or selection.style, the function is called for each element; the main difference with grouping is that the second argument to your function (i) is the within-group index rather than the within-selection index.

대부분의 경우 셀렉션이 group 배열의 배열이라는 점을 인식하지 못해도 괜찮다. selection.attr 이나 selection.style 을 정의하기 위해 함수를 호출하면, 함수의 파라미터로 전달되는 내용은 group 배열이 아니라 원래 의도대로 셀렉션의 최말단 문서요소에 적용되기 때문이다. group 배열과 관련해 의식해야 할 것은, 함수의 두 번째 파라미터로 전달하는 (i)가 셀렉션 범위의 인덱스가 아니라 group 배열 내에서의 인덱스라는 점이다.

#그룹핑을 변경하지 않는 메서드들

Only selectAll has special behavior regarding grouping; select preserves the existing grouping. The select method differs because there is exactly one element in the new selection for each element in the old selection. Thus, select also propagates data from parent to child, whereas selectAll does not (hence the need for a data-join)!

그룹핑에 관해서는 selectAll만 특이하게 동작한다(역자 주 : selectAll만 원래의 셀렉션에 있는 group 배열의 parentNode 속성을 변경한다); select는 원래의 셀렉션에 있는 그룹핑을 변경하지 않고 그대로 보존한다. select 메서드는 원래의 셀렉션의 최말단 문서요소 각각에 대해 단 하나씩의 문서요소만을 원소로 취하는 단 하나의 group 배열을 반환하기 때문에 selectAll과 다르게 동작한다. select는 원래 셀렉션의 데이터를 새로운 셀렉션에 물려주지만, selectAll은 물려주지 않는다(그래서 데이터-조인이 필요하다!).

The append and insert methods are wrappers on top of select, so they also preserve grouping and propagate data. For example, given a document with four sections:

append 메서드와 insert 메서드는 select 메서드를 랩핑한 메서드로서, select 메서드와 마찬가지로 그룹핑을 그대로 보존하고 원래의 셀렉션에 있던 데이터를 새로운 셀렉션에 물려준다. 예를 들어, 4개의 section이 있는 문서의 경우:

d3.selectAll("section");

If you append a paragraph element to each section, the new selection likewise has a single group with four elements:

각 section에 paragraph를 append 하면, append 메서드가 반환하는 새로운 셀렉션은 네 개의 원소를 가지는 한 개의 group 배열을 갖게 된다.

d3.selectAll("section").append("p");

Note that the parentNode for this selection is still the document element because selection.selectAll has not been called to regroup the selection.

위에서 append 메서드가 반환하는 새로운 셀렉션에 있는 group 배열의 parentNode 속성은 여전히 document 임을 기억하자. 셀렉션의 그룹핑을 변경하는 selection.selectAll은 호출된 적이 없기 때문이다.

#Null 문서요소

Groups can contain nulls to indicate missing elements. Nulls are ignored for most operations; for example, D3 skips null elements when applying styles and attributes.

group 배열은 문서요소가 없는 상태를 나타내기 위해 null을 포함할 수 있다. null은 대부분의 처리에서 무시된다; 예를 들어, D3는 스타일이나 속성값을 적용할 때 null 문서요소는 무시하고 지나가 버린다.

Null elements can occur when selection.select cannot find a matching element for the given selector. The select method must preserve the grouping structure, so it fills the missing slots with null. For example, if only the last two sections have asides:

null 문서요소는 selection.select 메서드가 해당 셀렉터에 매칭되는 문서요소를 찾을 수 없을 때 발생할 수 있다. select 메서드는 그룹핑 구조를 그대로 보존해야 하므로, 매칭되는 짝이 없는 곳은 null로 채운다. 예를 들어, 앞의 예제에서 4개의 section 중에서 두 개만 aside 를 가지고 있다면:

d3.selectAll("section").select("aside");

As with grouping, you can usually ignore null elements, but note their use in preserving the grouped structure of a selection and its within-group index.

그룹핑에 대해서는 보통 null 문서요소를 무시할 수 있지만, 셀렉션의 group 배열의 구조를 보존하는 데 null 문서요소가 사용된다는 점과 group 배열 내에서의 인덱스에 대해서는 기억을 해두자.

#데이터 바인딩

Perhaps surprisingly, data is not a property of the selection, but a property of its elements. This means that when you bind data to a selection, the data is stored in the DOM rather than in the selection: data is assigned to the __data__ property of each element. If an element lacks this property, the associated datum is undefined. Data is therefore persistent while selections can be considered transient: you can reselect elements from the DOM and they will retain whatever data was previously bound to them.

놀랍겠지만, 데이터는 셀렉션의 속성이 아니고, 셀렉션의 최말단 문서요소들의 속성이다. 데이터를 셀렉션에 바인딩하면, 데이터는 셀렉션이 아니라 셀렉션의 최말단 DOM 문서요소에 저장된다는 말이다: 데이터는 각 문서요소의 __data__라는 속성에 할당된다. 어떤 문서요소가 __data__ 속성을 가지고 있지 않으면, 그 문서요소에는 바인딩 된 데이터가 정의되어 있지 않은 것이다. DOM에서 문서요소를 다시 셀렉트 할 수 있으며, 셀렉트 된 해당 DOM 문서요소는 이전에 바인딩 된 데이터를 여전히 유지하고 있다. 따라서, 데이터는 지속성이 있지만(persistent), 셀렉션은 지속성이 없다(transient).

Data is bound to elements one of several ways:

데이터는 아래와 같은 방법으로 문서요소에 바인딩 될 수 있다:

While there is no reason to set the __data__ property directly when you can use selection.datum, doing so illustrates how data binding is implemented:

selection.datum을 사용할 수 있다면, __data__ 속성에 직접 데이터를 할당할 일은 없겠지만, 데이터 바인딩이 어떻게 구현되었는지 설명하기 위해 직접 데이터를 할당하는 코드를 보면 다음과 같다:

document.body.__data__ = 42;

The D3-idiomatic equivalent is to select the body and call datum:

앞의 직접 데이터 할당과 동일한 D3의 구문은, body를 셀렉트 하고, datum을 호출하는 것이다:

d3.select("body").datum(42);

If we now append an element to the body, the child automatically inherits data from the parent:

body에 문서요소 하나를 추가하면, 추가된 문서요소는 body로 부터 자동으로 데이터를 물려 받는다:

d3.select("body").datum(42).append("h1");

And that brings us to the last method of binding data: the mysterious join! But before we can achieve enlightenment, we must answer a more existential question.

이것은 데이터 바인딩의 마지막 방식인 상속에 의한 방식이다. 데이터 바인딩에 대한 진정한 깨우침을 얻으려면, 좀 더 존재론적인 질문에 답을 할 수 있어야 한다.

#데이터란 무엇인가?

Data in D3 can be any array of values. For example, an array of numbers:

D3에서는 숫자의 배열처럼, 값을 가진 모든 배열이 D3의 데이터가 될 수 있다.

var numbers = [4, 5, 18, 23, 42];

Or an array of objects:

D3의 데이터는 객체의 배열일 수 있으며:

var letters = [
  {name: "A", frequency: .08167},
  {name: "B", frequency: .01492},
  {name: "C", frequency: .02780},
  {name: "D", frequency: .04253},
  {name: "E", frequency: .12702}
];

Even an array of arrays:

심지어 배열의 배열일 수도 있다:

var matrix = [
  [ 0,  1,  2,  3],
  [ 4,  5,  6,  7],
  [ 8,  9, 10, 11],
  [12, 13, 14, 15]
];

We can mirror the visual representation of selections to represent data. Here’s a plain array of five numbers:

지금까지 봐왔던 셀렉션의 시각적 표현과 대칭으로 데이터를 표시할 수 있다. 5개의 숫자로 이루어진 일반적인 배열을 생각해보자:

Just as selection.style takes either a constant string to define a uniform style property (e.g., "red") for every selected element, or a function to compute a dynamic style per-element (function(d) { return d.color; }), selection.data can accept either a constant value or a function.

selection.style이 셀렉트 된 모든 문서요소에 단일 스타일 속성을 정의하기 위해 문자열 상수(예를 들면, "red")를 사용하거나, 문서요소 별로 동적인 스타일을 계산하기 위해 함수(function(d) { return d.color; })를 사용하는 것과 마찬가지로, selection.data도 상수값이나 함수를 인자로 사용할 수 있다.

However, unlike the other selection methods, selection.data defines data per-group rather than per-element: data is expressed as an array of values for the group, or a function that returns such an array. Thus, a grouped selection has correspondingly grouped data!

하지만 다른 selection 메서드와 달리, selection.data는 데이터를 문서요소 단위로 정의하지 않고 group 배열 단위로 정의한다: 데이터는 group 배열에 상응하는 배열로 표현되거나, 그런 배열을 반환하는 함수로 표현된다. 결국 셀렉션의 group 배열은 그에 상응하는 데이터 배열을 갖게 된다.

The blue lines in the diagram indicate that the data function returns the linked array. Your data function is passed the datum of the group’s parentNode (d) and the group’s index (i), and returns whatever array of data you want to join to that group. Thus, data is typically expressed as a function of parent data, facilitating the creation of hierarchical DOM elements from hierarchical data.

다이어그램에서 파란선은 data 함수가 연결 배열(linked array)을 반환하는 것을 보여주고 있다. data 함수(역자 주 : selection.data 함수가 아니라 바인딩 할 데이터를 반환하는 함수. 즉, selection.data의 인자로 전달되는 함수)는 group 배열의 parentNode의 데이터(d)와 group 배열의 인덱스(i)를 인수로 전달 받아서, 그 group 배열에 조인하고자 하는 데이터 배열을 반환한다. 따라서, 데이터는 부모 노드 데이터의 함수로 표현되어, 계층적인 데이터로부터 계층적인 DOM 문서요소를 쉽게 생성할 수 있다.

For selections with only a single group, you can pass the corresponding single array to selection.data directly; you only need a function when binding different data to different groups.

단 하나의 group 배열을 가지는 셀렉션에 대해서는, 그에 상응하는 한 개의 배열을 selection.data에 직접 전달할 수도 있다; 각기 다른 데이터를 각기 다른 group 배열에 바인딩 할 때만 함수가 필요하다.

#깨우침을 향한 열쇠

To join data to elements, we must know which datum should be assigned to which element. This is done by pairing keys. A key is simply an identifying string, such as a name; when the key for a datum and an element are equal, the datum is assigned to that element.

데이터를 문서요소에 조인 시킬 때는 어떤 데이터가 어떤 문서요소에 할당되는 지 알아야 한다. 이를 위해 키(key)가 필요하다. 는 이름처럼 식별자로 사용되는 단순한 문자열이다; 데이터는 자신의 키 값과 같은 키 값을 가진 문서요소에 할당된다.

The simplest method of assigning keys is by index: the first datum and the first element have the key “0”, the second datum and element have the key “1”, and so on. Joining an array of numbers to a matching array of paragraph elements therefore looks like this, with keys shown in green:

키를 할당하는 가장 쉬운 방법은 인덱스를 이용하는 것이다: 첫 번째 데이터와 첫 번째 문서요소의 인덱스 값은 모두 "0"이고, 두 번째 데이터와 두 번째 문서요소의 키 값은 "1", 세 번째는 "2" ... 와 같은 식이다. 숫자 배열을 인덱스를 기준으로 매칭되는 단락 문서요소의 배열에 조인하는 것이 바로 이 방식이다. 아래 그림에서 키는 녹색으로 표시되어있다.

var numbers = [4, 5, 18, 23, 42];

The resulting selection now has elements bound to data:

아래와 같이 데이터 조인한 셀렉션에 있는 문서요소에는 데이터가 바인딩 되어 있다:

d3.selectAll("div").data(numbers);

Joining by index is convenient if your data and elements are in the same order. However, when orders differ, joining by index is insufficient! In this case, you can specify a key function as the second argument to selection.data. The key function returns the key for a given datum or element. For example, if your data is an array of objects, each with a name property, your key function can return the associated name:

데이터와 문서요소가 같은 순서로 되어 있다면, 인덱스에 의한 조인은 아주 편리하다. 하지만, 둘의 순서가 다를 경우 인덱스만으로는 부족하다! 이럴 때는 키 함수를 selection.data 의 두 번째 인자로 넘겨줘야 한다. 키 함수는 주어진 데이터나 문서요소의 키를 반환한다. 예를 들어, 데이터가 name이라는 속성을 가진 객체의 배열이면, 키 함수는 배열의 각 원소 객체의 name 을 반환한다:

var letters = [
  {name: "A", frequency: .08167},
  {name: "B", frequency: .01492},
  {name: "C", frequency: .02780},
  {name: "D", frequency: .04253},
  {name: "E", frequency: .12702}
];

function name(d) {
  return d.name;
}

Again, the selected elements are now bound to data. The elements have also been reordered within the selection to match the data:

셀렉트된 문서요소가 데이터에 바인딩 되었다: 문서요소들은 조인된 데이터에 맞게 셀렉션 내부에서 재정렬되었다.

d3.selectAll("div").data(letters, name);

This process can be quite complicated for large grouped selections, but is simplified somewhat because each group is joined independently. Thus, you only need to worry about unique keys within a group, not across the entire selection.

이런 절차는 대규모의 group 배열이 포함된 셀렉션에서는 매우 복잡하게 진행되지만, 각 group 배열 단위로 독립적으로 조인되기 때문에 단순하게 볼 수도 있다. 그래서 키는 전체 셀렉션 내에서 유일할 필요가 없으며, group 배열 내에서만 유일성이 보장 되면 된다.

The above examples assume an exact 1:1 match between data and elements. But what happens when there’s no matching element for a given datum, or no matching datum for a given element?

앞의 예제는 데이터와 문서요소 사이에 정확히 1:1 관계가 성립한다. 하지만 주어진 데이터에 매칭되는 문서요소가 없거나, 반대로 주어진 문서요소에 매칭되는 데이터가 없는 경우에는 어떻게 처리될까?

#Enter, Update, Exit

When joining elements to data by key, there are three possible logical outcomes:

키를 기준으로 문서요소를 데이터에 조인할 때는 논리적으로 다음과 같이 3가지 경우의 수가 있다:

These are the three selections returned by selection.data, selection.enter and selection.exit, respectively. To illustrate, imagine you had a bar chart of the first five letters of the alphabet (ABCDE), and you want to transition to your five favorite vowels (YEAOI). You can use a key function to maintain association of letters to bars across the transition, resulting in the following data join:

위에서 나열한 3가지 셀렉션은 각각 selection.data, selection.enter and selection.exit에 의해 반환된다. 설명을 위해, 알파벳의 첫 다섯 글자(ABCDE)로 된 막대 차트가 있다고 상상해보자. 이 차트를 다섯 개의 모음(YEAOI)으로 전이(transition, 역자 주 : 화면 상의 변화를 애니메이션을 통해 표시) 시키고 싶다고 하자. 이럴 때 키 함수를 사용해서 전이하는 동안 글자와 막대의 관계를 유지할 수 있다. 조인 결과는 다음과 같다:

Two of the previously-displayed letters (A and E) are vowels. These bars are therefore placed in the update selection, in order of the new data:

첫 다섯 글자 중에서 두 개(A와 E)는 모음이다. A와 E에 해당하는 막대는 매칭되는 짝이 있으므로 조인한 데이터의 순서에 맞게 update 셀렉션에 들어간다:

var div = d3.selectAll("div").data(vowels, name);

The other three displayed letters (B, C and D) are consonants, and thus have no corresponding data in the new dataset. These elements are therefore placed in the exit selection. Note that the exit selection preserves the order of the original selection, which is sometimes useful when animating prior to removal:

나머지 세 개의 글자(B, C, D)는 자음이고, 새로운 데이터셋에 매칭되는 데이터가 없다. 세 개의 글자에 해당하는 문서요소들은 exit 셀렉션에 들어간다. exit 셀렉션은 원래 셀렉션의 순서를 그대로 보존한다는 점을 기억하자. exit 셀렉션에 있는 문서요소를 애니메이션 효과를 주면서 삭제할 때 유용하다:

div.exit();

Lastly, three of the vowels (Y, O and I) were not previously displayed, and thus have no corresponding element. These form the enter selection:

마지막으로 세 개의 모음(Y, O, I)는 화면에 표시되지 않는데, 매칭되는 문서요소가 없기 때문이다. 이 셋은 enter 셀렉션으로 들어간다:

div.enter();

While update and exit are normal selections, enter is a subclass of selection. This is necessary because it represents elements that do not yet exist. An enter selection contains placeholders rather than DOM elements; these placeholders are simply objects with a __data__ property. The implementation of enter.select is then specialized such that nodes are inserted into the group’s parent, replacing the placeholder. This is why it is critical to call selection.selectAll prior to a data join: it establishes the parent node for entering elements.

update 셀렉션과 exit 셀렉션이 보통 셀렉션이지만, enter 셀렉션은 셀렉션의 서브클래스다. enter 셀렉션은 아직 존재하지도 않는 문서요소를 나타내기 때문에 보통 셀렉션과는 다를 수 밖에 없다. enter 셀렉션은 DOM 문서요소가 아니라 자리채우미(placeholder)를 포함한다; 자리채우미는 __data__ 속성만을 가지는 단순 객체다. enter.select은 노드가 자리채우미를 대체하면서 group 배열의 부모로 삽입되도록 특별하게 구현되어 있다. 바로 이 점이 데이터 조인을 하기 전에 selection.selectAll을 호출해야 하는 이유다: selection.selectAll은 새로운 enter 셀렉션의 parentNode를 정확하게 설정한다.

#Enter와 Update 합치기

The general update pattern with a data join appends entering elements and removes exiting elements, while modifying dynamic attributes, styles and other properties of updating elements. Often, there’s overlap between properties of updating and entering elements.

데이터 조인에 관한 update 일반 패턴은 update 셀렉션에 있는 문서요소의 속성, 스타일 등을 수정하고, enter 셀렉션에 있는 문서요소를 추가하고, exit 셀렉션에 있는 문서요소를 제거한다. update 셀렉션에 있는 문서요소의 속성과 entering 셀렉션에 있는 문서요소의 속성이 겹치는 경우도 발생할 수 있다.

To reduce duplicate code, enter.append has a convenient side-effect: it replaces null elements in the update selection with the newly-created elements from the enter selection. Thus, after enter.append, the update selection is modified to contain both entering and updating elements. The update selection subsequently contains all currently-displayed elements:

코드 중복을 줄이기 위해, enter.append 에는 편리한 기능이 포함되어 있다: enter.append 는 update 셀렉션에 있는 null 문서요소를 enter 셀렉션에 있는 새로 생성된 문서요소로 대체한다. 그래서 enter.append 를 실행하고 나면, update 셀렉션은 enter 셀렉션에 있는 문서요소와 update 셀렉션에 있는 문서요소를 모두 포함하게 된다. 결과적으로 update 셀렉션은 현재 표시되는 모든 문서요소를 포함하게 된다:

With the selection once again consistent with the document, the life-cycle of the data join is complete.

셀렉션과 문서의 일관성이 다시 한 번 맞춰지면서, 데이터 조인의 생명주기가 완성된다.

도움 주신 분들

Thank you to Anna Powell-Smith, Scott Murray, Nelson Minar, Tom Carden, Shan Carter, Jason Davies, Tom MacWright and John Firebaugh for reviewing and providing feedback to improve this article.

이 글을 리뷰해주시고 개선을 위한 값진 피드백을 보내주신 Anna Powell-Smith, Scott Murray, Nelson Minar, Tom Carden, Shan Carter, Jason Davies, Tom MacWright and John Firebaugh에게 감사의 말씀을 전한다.

더 읽을거리

If you found this article informative, if you found parts unclear or confusing, or if you have followup questions or feedback, please let me know via Twitter or Hacker News. To continue learning about selections, reading D3’s source is a rigorous way to test your understanding. And here are several excellent talks and tutorials by others:

이 글이 유익하다고 생각되거나, 명확하지 않거나 혼동스러운 부분이 있거나, 추가 질문이나 피드백이 있다면, 트위터Hacker News를 통해 연락주기 바란다. 셀렉션에 대해 계속 공부하고 싶다면, D3 소스 보기 - 셀렉션을 보면 셀렉션에 대해 얼마나 이해하고 있는 지 엄격하게 테스트 해 볼 수 있다. D3에 대해 다른 사람들이 작성한 훌륭한 이야기거리와 튜토리얼은 아래와 같다:

0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15