Frontend
DOM 속성에 데이터를 저장하는 것에 대해서
twitchTV의 사용자 지정 속성 사용
프론트엔드 개발을 배우고 난 후부터 내가 주로 접속하는 웹 서비스의 프론트엔드가 어떻게 구성되어 있는지 알아보고자 개발자 도구를 확인하는 습관이 생겼다.
이번에는 자주 시청하는 인터넷 방송 (라이브 스트리밍 서비스) 플랫폼이 어떻게 구현되었는지 알아보고자 했다. 방송의 채팅 기능은 주로 웹 소켓(WebSocket) 혹은 서버 전송 이벤트(SSE)를 사용할 거라고 예상했고, 알아보니 얼추 맞았다. 클라이언트에서는 사용자가 채팅 메시지를 입력하고 전송하고, 이를 웹 소켓 연결을 통해 서버로 전송하면서 서버와 클라이언트 간 실시간 데이터 통신을 진행하는 방식이다. 채팅의 총 개수는 브라우저 성능을 고려하여 표시되는 메시지 수에 제한을 두고, HTML 요소에서 오래된 DOM 요소를 제거하고 새 DOM을 업데이트하는 방식으로 최적화하고 있었다.
그런데 트위치 서비스의 DOM 구조의 속성에 조금 다른 점을 발견했는데,
data-*
속성이었다..chat-line__message
는 라이브 스트리밍 방송의 개별 채팅 메시지를 의미한다.
- 추가 속성으로
data-a-target
,data-a-user
를 통해 타겟과 유저 닉네임을 저장하고 있다.
- 이외에도 트위치 서비스의 HTML에는 굉장히 많은 데이터 속성을 사용하고 있다.
아래는 치지직 서비스의 채팅 UI를 보여주는 HTML이다.
- 따로 사용자 지정 속성을 사용하고 있는 것 같지 않다.
아래는 아프리카TV 서비스의 채팅 UI를 보여주는 HTML이다.
user-type
과 같은 비표준 속성을 사용하고 있다.
HTML 데이터(data-*) 속성에 대해서
데이터 속성
data-*
속성정의된 의미를 갖지 않는 데이터에 대한 확장성을 고려하여 설계된 HTML 속성으로, 표준이 아닌 속성이지만 HTML 요소에 추가적인 데이터를 저장할 수 있도록 하는 속성이다.
왜 데이터 속성을 사용할까?
data 속성은
data-*
로 시작해야 하는 사용자 정의 속성이라고 할 수 있다. 자바스크립트에서는 dataset
이라는 객체를 통해 쉽게 접근할 수 있고*, CSS에서는 attr()
함수 또는 속성 선택자를 통해 쉽게 접근할 수 있다. DOM 요소에 데이터를 직접 저장하는 것으로, HTML에 사용하는 변수 데이터인 셈이다. 방식이 직관적이고, 하나의 요소에 데이터 속성을 동시에 사용할 수 있어 확장성이 좋다는 장점이 있다.데이터 속성을 사용하는 목적과 이유
- 페이지 또는 앱에 대한 사용자 지정 ‘데이터’, ‘상태’, ‘주석’ 등을 저장하기 위함
- 더 이상 적절한 표준 속성이나 요소가 없을 때 사용하는 HTML 표준화 속성
- 엄밀한 표준이 아니지만, 표준화된 방법으로 커스텀 데이터를 추가하기 위해 설계됨 (즉, 따로 뻘짓하지 말고 차라리 데이터 속성을 사용하라는 의미에서 고안됨)
- 주로 자바스크립트나 CSS에서 해당 속성을 활용하여 동적 기능을 추가하거나 스타일을 지정하는 데 사용됨
데이터 속성을 잘 활용한 사례를 살펴보자.
<div class="tweet" data-tweet-id="1234567890"> <p>Here is a tweet content.</p> <button class="like-button">Like</button> <button class="retweet-button">Retweet</button> </div>
위 코드는 트위터에서
data-*
속성을 사용해서 각 트윗마다 고유한 id를 부여한 케이스다. 이 id는 트윗 요소에 저장되어 있고, 사용자가 트윗과 관련된 작업을 할 때 이 id를 통해 서버와 통신하여 작업을 수행한다. 트윗의 고유 값을 쉽게 가져올 수 있기 때문에 이벤트 처리가 간편하고, 복잡한 상태 관리나 DOM 탐색 없이 간단하게 처리할 수 있는 장점이 있다.데이터 속성을 사용할 때의 주의할 점
‘보여야 하고 접근 가능한 내용은 데이터 속성에 저장하지 마세요. 접근 보조 기술이 접근할 수 없기 때문입니다. 또한 검색 크롤러가 데이터 속성의 값을 인덱싱하지 못할 수도 있습니다.’ - MDN
즉, 사용자에게 보여야 하고 접근이 가능해야 하는 콘텐츠에 데이터 속성을 활용하는 것은 사용자 경험과 접근성을 떨어뜨릴 수 있다. 시각 장애인을 위한 스크린 리더가 접근할 수 없으며, 검색 엔진에서 파악하지 못할 수도 있다.
이외에도 인터넷 익스플로러 11+ 이전 버전에서는
dataset
을 지원하지 않아서 getAttribute()
메서드를 통해 속성에 접근해야 한다. 또한, 데이터 속성 읽기의 성능은 자바스크립트 데이터 저장소에 저장하는 것과 비교하여 성능이 저조하다.<div class="product" data-product-id="12345" data-price="99.99" data-discount="20" data-stock="15"> <p>Product Name</p> <button class="add-to-cart">Add to Cart</button> </div>
위 코드는 특정 쇼핑몰 사이트에서 사용자 장바구니에 추가된 상품들에 대한 데이터를 관리하기 위해 데이터 속성을 사용한 사례다. 각 상품 요소에 상품의 id, 가격, 할인 정보, 재고 상태 등 다양한 데이터를 데이터 속성으로 저장했다. 데이터 속성을 이렇게 활용하면 무슨 문제가 생길까?
- 이러한 데이터를 서버와 자바스크립트에서 상태로 관리하면서 동시에 HTML 속성에도 저장하는 것은 데이터 중복을 초래할 수 있다. 재고 상태가 서버에서 업데이트되었는데, 클라이언트 측의
data-stock
속성이 갱신되지 않으면 사용자에게 잘못된 정보가 표시될 가능성이 있다.
- 상품에 추가되는 정보가 많을수록 관리해야 할 데이터 속성의 종류와 개수가 늘어나고 코드가 복잡해진다. 이는 코드 가독성을 떨어뜨리고, 유지보수를 어렵게 만든다.
- 많은 양의 데이터를 데이터 속성으로 저장할 경우, DOM의 크기가 커지며 데이터에 접근하거나 조작할 때 성능 저하가 발생할 수 있다.
DOM을 데이터베이스나 로컬 스토리지처럼 사용하지 말자
데이터 속성은 엄밀히 HTML의 표준이 아니다. 데이터 속성을 과도하게 사용하여 DOM을 데이터베이스처럼 취급할 경우 발생할 수 있는 문제점들을 알아보자.
데이터 일관성 문제
DOM에 데이터를 저장하면 자바스크립트에서 관리하는 상태(state)와 DOM에 저장된 데이터를 매번 동기화시켜야 한다. 이는 코드가 복잡해질 수 있으며, 혹여나 데이터가 동기화되지 않으면 데이터 일관성이 깨진다.
중복된 상태 관리
상태를 자바스크립트 코드와 DOM의 데이터 속성 두 곳에서 관리하면 동일한 데이터를 두 번 관리하게 된다. 상태 관리의 핵심은 단일 출처(Single Source of Truth)를 유지하는 것인데, DOM을 데이터 저장소로 사용하면 이 원칙을 위반하게 된다.
예를 들어 React에서는 컴포넌트 상태나 전역 상태 관리 라이브러리를 사용해 데이터를 관리한다. 하지만, 데이터 속성으로도 데이터를 관리하려고 하면 두 개의 출처가 생기며, 어느 쪽에서 데이터를 갱신해야 할지 혼란스러워질 수 있다. 이는 버그를 유발하는 주요 원인이 된다.
퍼포먼스 문제
DOM은 기본적으로 구조화된 문서 트리로, 주로 UI 요소를 관리하기 위해 설계되었다. 데이터를 대량으로 데이터 속성에 저장하게 되면, DOM이 불필요하게 커지고 복잡해지며, 브라우저가 이를 처리하는 데 더 많은 리소스를 소비하게 된다. 특히, 대규모의 DOM 조작이 필요할 때 성능 저하가 두드러지게 나타날 수 있다.
수백 개의 테이블 행에 데이터를 데이터 속성을 저장하고 이를 반복적으로 읽고 쓴다면, DOM 접근 성능이 저하될 수 있다. 특히나 성능이 중요한 모바일 환경에서 문제가 될 가능성이 높다.
보안 문제
데이터 속성은 클라이언트에서 누구나 접근할 수 있기 때문에 민감한 데이터를 저장하는 것은 보안상 매우 위험한 행위다.
HTML 구조에 대한 과도한 의존성
데이터 속성에 데이터를 저장하면 해당 데이터가 HTML 구조에 강하게 결합된다. HTML 구조가 변경되면 이와 관련된 데이터를 관리하는 코드도 함께 수정해야 한다. 이는 코드 유지보수성을 떨어뜨리고, 의도치 않은 버그를 유발할 수 있다.
데이터 저장소로서의 부적합성
DOM은 데이터 저장소로 설계되지 않았다. 데이터베이스나 로컬 스토리지처럼 사용하기에는 구조적인 한계가 있다. DOM은 상태를 영구적으로 저장하지 않으며, 페이지가 리로드되면 DOM에 저장된 모든 데이터는 사라진다.
트위치에서는 왜 데이터 속성을 사용했을까?
트위치와 같은 대규모 웹 애플리케이션에서 데이터 속성을 많이 사용한 것에 대해서 그 자체로 ‘좋지 않은 패턴’이라고 단정 짓기는 어렵다. 서비스의 아키텍처와 요구 사항에 따라 다르게 평가될 여지가 있는데, 트위치가 데이터 속성을 많이 사용하는 이유와 그 적절성을 판단하려면 몇 가지 측면을 고려해야 한다.
아키텍처 및 요구사항에 따른 판단
트위치는 실시간 스트리밍, 채팅, 사용자 인터랙션 등의 복잡한 기능을 지원하는 플랫폼이다. 다양한 기능과 데이터를 관리하기 위해 데이터 속성을 사용하는 것은 특정 상황에서 매우 유용할 수 있다.
예시로 동적 콘텐츠 관리를 꼽을 수 있다. 스트리밍 페이지에서는 수많은 동적 요소인 채팅 메시지, 사용자 상태, 스트림 정보 등이 실시간으로 업데이트 된다. 이러한 데이터를 자바스크립트로 효율적으로 접근하고 관리하기 위해 데이터 속성을 사용했다고 판단할 수 있다.
또 다른 예시로 많은 DOM 요소에 사용자 인터랙션을 기반으로 이벤트를 연결해야 할 때, 데이터 속성을 사용해 요소에 관련 메타데이터를 저장하고, 이를 기반으로 다양한 작업을 처리할 수 있게 했다고 판단할 수 있다.
의도적인 선택일까?
트위치 같은 대규모 플랫폼에서는 성능 최적화와 유지보수를 고려해 아키텍처와 코드를 설계했을 것이다. 데이터 속성의 다양한 사용이 의도적일 가능성이 높다는 것이다. 어떤 이유들이 있을까? 예상해보자.
백엔드와의 간단한 데이터 연동
데이터 속성에 저장된 데이터는 쉽게 서버로 전송되거나 서버에서 전달한 데이터를 DOM 요소에 할당할 수 있다. 이는 빠른 프로토타이핑과 유지보수에 유리할 수도 있다.
컴포넌트 간의 데이터 전달
프론트엔드 컴포넌트가 서로 데이터를 전달하거나, 서버에서 제공하는 데이터를 특정 DOM 요소에 바인딩해야 하는 경우, 데이터 속성을 통해 이를 쉽게 관리할 수 있다.
뭐, 요약하자면… 저렇게 큰 규모의 서비스에서 데이터 속성을 사용하는 것에는 특정한 이유가 있지 않을까? 넘겨 짚는 내용이다. “대협에게는 대의가 있겠지…”하는 것이다. 트위치의 웹 서비스 개발자에게 직접 물어보고 싶다.
React에서는 권장하지 않는다
리액트 JSX에서의 데이터 속성 사용에 대해 자료를 찾아보다가,
리액트 교과서
라는 책에서 아래와 같은 글을 발견했다.‘React에서는 주로 HTML 비표준 속성을 컴포넌트에 추가하면 신경도 쓰지 않고 무시한다. 이 점은 JSX가 아닌 네이티브 자바스크립트로 작성해도 소용이 없다. React의 동작 원리이기 때문이다.’ (React 16 이후에는 무시하지 않는다! *)
‘그렇지만 가끔 DOM 노드에 추가 데이터를 전달해야 하는 경우가 있다. 이것은 안티패턴으로, DOM을 데이터베이스나 로컬 스토리지처럼 사용하지 않아야 한다.’
적힌 그대로 리액트에서는 DOM 노드에 추가 데이터를 전달하는 방식을 안티 패턴으로 취급한다. 리액트를 학습하면서도 데이터 속성을 사용했던 경우는 거의 없었는데, 왜 그럴까?
리액트의 설계 철학과 상태 관리 방식
리액트는 상태를 관리하기 위해
useState
, useReducer
또는 상태 관리 라이브러리를 사용한다. 상태는 컴포넌트의 렌더링 로직과 밀접하게 연결되어 있으며, 리액트의 상태 관리 시스템은 데이터의 변경을 자동으로 반영하여 UI를 업데이트 한다. 이와 달리 데이터 속성은 DOM의 속성이므로, 상태 업데이트가 자동으로 UI에 반영하지 않는다.또한, 리액트는 가상 DOM을 사용하여 UI를 업데이트한다. 상태 변경 시 가상 DOM에서의 비교를 통해 실제 DOM을 최소한으로 업데이트한다.
dataset
을 DOM에서 직접 사용하는 경우, 리액트의 가상 DOM과 실제 DOM 간의 차이로 인해 성능 문제가 발생할 수 있다. 즉, 리액트의 최적화 이점을 충분히 활용하지 못하게 된다.리액트는 선언적 UI를 지향한다. 컴포넌트의 상태에 따라 UI가 자동으로 업데이트되도록 설계되어 있다. 데이터 속성을 사용하면 UI와 데이터 간의 명시적 연결이 부족할 수 있으며, 리액트의 철학에 어긋나는 방식이 될 수 있다.
리액트의 사용자 정의 속성
리액트 16 버전부터 비표준 DOM 속성을 무시하지 않게 되었다. 기존에는 표준 DOM 속성만 지원하고 비표준 속성은 렌더링 시에 무시하여서 일관성과 표준을 준수하려고 했다. 그러나 아래와 같은 이유로 비표준 속성을 지원하게 된다.
React가 비표준 속성을 지원하게 된 이유
- 사용자 정의 속성의 필요
많은 앱에서 비표준 속성을 사용자 정의 데이터나 메타데이터로 사용한다. 예를 들어,
data-*
속성 외에도 사용자 정의 속성을 많이 사용하는 경우가 있었다.- CSS-in-JS 및 스타일 라이브러리 지원
CSS-in-JS 라이브러리나 스타일링 도구는 종종 DOM 요소에 추가적인 속성을 사용하여 스타일을 적용하거나 관리한다. React의 속성 필터링 방식은 이러한 도구들과의 호환성 문제를 일으켰다.
즉, 아래와 같은 코드를 이제는 리액트에서 무시하지 않는다는 것이다.
<div data-foo="42" /> <button aria-label="Close" onClick={onClose} /> /* 위 속성들은 웹 접근성을 위해 기존에도 지원했음 */ <div mycustomattribute="something" /> /* React 15의 결과 : <div /> React 16의 결과 : <div mycustomattribute="something" /> */
그러나 리액트는 데이터를 속성에 저장하지 않을 것을 권장한다.
data-
속성을 사용하는 방식 또한 불가피한 경우가 아니라면 사용하지 않는 것을 권장한다.Should I Keep Data in Custom Attributes?
‘No. We don’t encourage you to keep data in DOM attributes. Even if you have to,
data-
attributes are probably a better approach, but in most cases data should be kept in React component state or external stores.However, the new feature is handy if you need to use a non-standard or a new DOM attribute, or if you need to integrate with a third-party library that relies on such attributes.’
사용자 정의 속성에 데이터를 저장해야 하나요?
아니요. DOM 속성에 데이터를 보관하는 것을 권장하지 않습니다. 불가피한 경우 데이터 속성(
data-
)이 더 나은 접근 방식일 수 있지만, 대부분의 경우 데이터는 React 컴포넌트 상태 또는 외부 저장소에 보관되어야 합니다. 그러나, 비표준 또는 새로운 DOM 속성을 사용해야 하거나 그러한 속성을 사용하는 타사 라이브러리와 통합해야 하는 경우에 이 기능이 유용합니다.
결론과 느낀 점
설계 철학에 따라 활용하자
리액트에서는 그 설계 철학으로 인해 DOM 구조에 데이터를 저장하지 않기를 권장한다. 상태와 UI를 컴포넌트의 상태와 props를 통해 관리하기를 권장하며, DOM 속성에 비표준 데이터나 메타 데이터를 저장하는 것을 지양하는 경향이 있다.
Vue.js에서는 사용자 지정 속성이나
data-*
속성의 사용이 리액트에 비해 더 유연하게 허용된다. Vue의 철학은 데이터와 DOM의 관계를 선언적으로 관리하며, 필요한 경우 사용자 지정 속성을 활용할 수 있는 자유를 제공한다.내가 어떤 프론트엔드 설계 철학을 따르고 있고, 어떤 프론트엔드 기술을 사용하는가에 따라 DOM을 어떻게 활용해야 할 지 나뉠 수 있을 것이다.
웹은 정말 다양한 기술로 개발됨
모든 웹이 모던한 기술들로 개발되는 것이 아니다. 아프리카TV 웹 서비스는 apache HTTP 웹 서버와 php 언어, RequireJS, jQuery를 사용하고 있는 것으로 보이고, 트위치와 치지직은 React를 사용하여 개발된 것으로 보인다. 네이버 공식 메인 페이지는 jQuery를 사용했다고 나와 있고, 쿠팡 플레이는 Next.js로 개발되었다. ―
wappalyzer
결과결국 어떤 기술들을 활용해서 웹앱이 개발되었냐 보다는 웹 기술의 근간을 통해 기본기를 다지는게 중요하다는 점을 또 한 번 느낀다. 자바스크립트, HTML, CSS, HTTP/HTTPS, 브라우저 동작 등 착실히 기본기를 쌓아서 어떤 기술을 쓰더라도 흔들리지 않는 점이 중요하다는 점이다.