brunch

You can make anything
by writing

C.S.Lewis

by Hika Jan 15. 2016

Element의 attribute와 property

코딩생활의 소소한 뒤통수

DOM Element를 사용하는데 있어 굉장히 어려운 점은 하나의 객체에 필드를 나타내는 두 가지 방법이 있다는 것이다.

attribute는 getAttribute, setAttribute, removeAttribute, attributes 등의 기능들과 사용되는데 태그에도 직접 반영되고 동시에 당연하게도 문자열만 값이 될 수 있다.

property는 자바스크립트 오브젝트로서의 필드로 대괄호나 점구문을 통해 필드를 정의하고 값을 넣거나 조회하는 것으로 여기에는 다양한 객체를 할당할 수도 있다.

문제는 이 둘 사이의 동기화다.


대표적으로 많이 쓰는 input의 value를 생각해보자.

<input id="test" value="abc"/>

var input = document.getElementById('test');
console.log(input.value);
// 'abc'
console.log(input.getAttribute('value'));
// 'abc'

일견 평온해 보인다. 이후 attribute로 값을 바꿔보자

input.setAttribute('value', 'def');
console.log(input.getAttribute('value'));
// 'def'

위와 같이 바꾸면 실제 input박스의 값도 화면상에서 def가 되어있다. 즉 완전하다.

하지만 property를 이용하면 그때부터 헬게이트가 펼쳐진다. 일반적으로 알려진 value키의 특징은

  - attribute로 설정하면 value property도 자동으로 갱신되지만,

  - value property를 갱신한다고 자동으로 attrivute가 갱신되지는 않는다

라는 것이다. 코드로 표현하면 다음과 같다.

input.setAttribute('value', 'kkk');
console.log(input.value);
//'kkk'

input.value = 'ooo';
console.log(input.getAttribute('value');
//'kkk'

즉 attribue를 바꾸면 value키도 바뀌지만 value키를 바꾼다고 attribute가 바뀌지는 않는다는 것이다.
하지만 여기엔 굉장히 중요한 내용이 추가로 있어서 사실 온라인에 돌아다니는 저 내용은 틀렸다고 볼 수는 없지만 오직 처음에만 저렇고 이후는 더욱 굉장한 문제로 된다는 사실을 간과하고 있다.

일단 input.value를 통해 값을 바꾸면 화면상의 input박스의 값도 같이 바뀐다.


즉 엘리먼트의 실체는 실제 내부의 텍스트박스의 텍스트를 value키값에 의거하여 바꾼 것이다

헌데 attribute값은 그대로다.

...

이것이 무엇을 뜻하는가?

setAttribute는 다른 경우 값을 지정하면 태그 실체에 반영되고, 그 attribute에 의거 각 엘리먼트가 작동하는게 일반적이다.

하지만 input.value를 통해 한 번이라도 값을 정의하는 순간 태그상의 value attribute는 화면에 보이는 텍스트박스안의 텍스트와 맞지 않게 된다는 것이다. 이를 코드로 보면 다음과 같다.

<input id="test" value="abc">

var input = document.getElementByI('test');
input.value = "def";

console.log(input.getAttribute('value'));
//'abc'
console.log(input);
//<input id="test" value="abc">

또한 실제 화면의 input박스는 다음과 같은 모습이 된다.

def

즉 value를 통해 def로 바꾸면 화면의 input박스엔 실제로 def가 들어있지만 태그는 value="abc"상태가 되어버린다.

한 번 이렇게 어긋나면 그 이후엔 setAttribute를 해도 실제 input박스에 반영되지 않고 그저 attribute만 갱신되는 식으로 따로 놀게 되는 것이다.




물론 이 문제는 value의 attribute와 property 동기화정도의 문제가 아니라 input박스에서 텍스트를 입력해도 getAttribute('value')는 예전값을 반환하는게 근본적으로 원인을 제공하고 있다.


더 나아가 별도의 입출력을 제공하는 컴포넌트 요소들은 이러한 문제를 내포할 가능성이 있다.

select박스, 체크박스, 라디오박스 등도 사용자 조작을 통해 직접 값을 바꿀 수 있다.

여기에 영향을 받는 checked등의 값도 한번 value키를 통해 지정하거나 사용자입력으로 갱신되는 순간부터 attribute와의 동기화는 깨지고 따로 놀게 된다.


사실 모든 악의 근원은 스펙문서에 있다.

이 안에 보면 value에 대한 설명에 다음과 같은 내용이 있다.

Changing this attribute changes the contents of the form control, but does not change the value of the HTML value attribute of the element

즉 여기서 너무 한정적으로만 정의한 것이다. 그저 사용자 입력으로 내용을 바꿀경우 attribute는 안바꾼다는 내용정도만 정의한게 화근이다. 이 정의만으로는 두가지 구멍이 생기는 것이다.

1. 사용자 입력 전에 setAttribute는 value값과 동시에 컨트롤의 값을 바꿀 수 있는가(있게 구현됨)

2. value키 또는 사용자 입력으로 value를 갱신한 뒤 setAttribute는 컨트롤의 값을 바꿀 수 있는가(없게 구현됨)


즉 같은 setAttribute('value', 'aaa')의 의미가 사용자가 컨트롤을 조작하거나 value키에 값을 할당하는 시점을 기준으로 의미가 바뀐다는 것이다. 이전에는 컨트롤의 값도 같이 바뀌고 표시되지만 이후시점에는 attribute만 갱신된다.


게다가 value와 같은 이유로 checked도 동일한 사태가 벌어지는데 심지어 스펙문서에는 이에 대한 언급이 없다.


다행히 html5스펙에서는 보다 내용이 보강되었다.


The value content attribute gives the default   value of the input element. When the value content attribute is added, set,  or removed, if the control's dirty value flag  is false, the user agent must set the value of the element  to the value of the value content attribute, if there is  one, or the empty string otherwise, and then run the current value sanitization  algorithm, if one is defined.

이 내용에서는 위의 설명한 구조를 drty value flag 라는 내부 플래그가 존재하고 그 플래그에 따라 어찌 작동하는지를 보다 구체적으로 정의하고 있다.



이외에도 class/classname 의 문제 href의 문제등 attribute와 property간의 수많은 문제는 아예 그 스펙안에 내장되어 우리를 괴롭히고 있다.


>  다음글


 > 이전글  


브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari