안녕하세요, TypeScript를 활용하여 DOM(Document Object Model)을 효율적으로 다루는 방법에 대해 알아보겠습니다. TypeScript는 DOM 조작 시 코드의 안전성과 가독성을 높여주는 강력한 타입 시스템을 제공합니다. 이번 글에서는 TypeScript로 DOM을 조작하는 기본 방법부터 주의할 점, 그리고 실무에서 활용할 수 있는 패턴까지 다뤄보겠습니다.
DOM(Document Object Model)은 HTML, XML 문서의 구조화된 표현으로, JavaScript를 통해 문서의 콘텐츠, 구조, 스타일을 동적으로 변경할 수 있는 API입니다. DOM은 트리 구조로 표현되며, 각 요소는 노드(Node)로 구성됩니다.
TypeScript는 DOM API를 포함한 타입 선언 파일을 제공하기 때문에, DOM 조작 시 강력한 타입 체크와 코드 자동 완성을 지원합니다.
DOM 요소를 접근하려면 TypeScript의 document
객체를 사용할 수 있습니다. 대표적으로 getElementById
, querySelector
등을 활용합니다.
const heading = document.getElementById('heading');
console.log(heading?.textContent); // 요소의 텍스트 출력
TypeScript는 document.getElementById
의 반환 타입을 HTMLElement | null
로 추론합니다. 요소가 존재하지 않을 수 있기 때문입니다. 따라서 타입 단언을 통해 더 구체적인 타입을 명시해야 합니다.
const heading = document.getElementById('heading') as HTMLHeadingElement;
console.log(heading.textContent);
querySelector
와 제네릭querySelector
는 CSS 선택자를 사용해 요소를 찾을 수 있는 강력한 메서드입니다. TypeScript에서는 제네릭을 사용해 반환 타입을 지정할 수 있습니다.
const button = document.querySelector<HTMLButtonElement>('#submit-button');
button?.addEventListener('click', () => {
console.log('버튼 클릭!');
});
위 코드에서 HTMLButtonElement
타입을 명시함으로써 버튼 요소의 고유 메서드와 속성에 접근할 수 있습니다.
TypeScript를 사용하면 DOM 요소의 속성을 안전하게 조작할 수 있습니다.
const input = document.querySelector<HTMLInputElement>('#user-input');
if (input) {
input.value = 'Hello, TypeScript!';
}
위 코드에서 input
요소의 value
속성은 HTMLInputElement
에 정의되어 있어 타입 체크가 가능합니다.
classList
를 사용하면 요소의 클래스를 쉽게 추가하거나 제거할 수 있습니다.
const box = document.querySelector<HTMLDivElement>('.box');
box?.classList.add('highlight');
box?.classList.remove('hidden');
style
객체를 사용해 인라인 스타일을 변경할 수 있습니다.
const box = document.querySelector<HTMLDivElement>('.box');
if (box) {
box.style.backgroundColor = 'blue';
box.style.color = 'white';
}
TypeScript로 DOM 이벤트를 처리할 때, 이벤트 객체의 타입을 명시적으로 선언할 수 있습니다.
const button = document.querySelector<HTMLButtonElement>('#submit-button');
button?.addEventListener('click', (event: MouseEvent) => {
console.log('클릭 이벤트 발생!', event);
});
TypeScript를 사용하면 커스텀 이벤트도 안전하게 처리할 수 있습니다.
const customEvent = new CustomEvent('myEvent', { detail: { message: 'Hello, Custom Event!' } });
document.dispatchEvent(customEvent);
document.addEventListener('myEvent', (event: Event) => {
const custom = event as CustomEvent;
console.log(custom.detail.message);
});
DOM 조작을 반복적으로 수행할 경우, 유틸리티 함수를 사용하면 코드의 가독성과 재사용성을 높일 수 있습니다.
function createElement<K extends keyof HTMLElementTagNameMap>(tagName: K, options?: Partial<HTMLElementTagNameMap[K]>): HTMLElementTagNameMap[K] {
const element = document.createElement(tagName);
if (options) {
Object.assign(element, options);
}
return element;
}
const newDiv = createElement('div', { id: 'my-div', className: 'container' });
document.body.appendChild(newDiv);
위 함수는 태그 이름과 속성을 받아 DOM 요소를 생성하고 반환합니다. 제네릭을 사용하여 태그 이름에 따라 적절한 타입을 반환하도록 설계되었습니다.
TypeScript는 DOM 요소가 존재하지 않을 가능성을 항상 염두에 둡니다. 따라서 요소를 조작하기 전에 null
체크가 필요합니다.
const heading = document.getElementById('heading');
if (heading) {
heading.textContent = '안녕하세요!';
}
타입 단언(as
)은 컴파일러에게 타입 정보를 제공하지만, 실제 DOM 구조와 일치하지 않을 경우 런타임 오류가 발생할 수 있습니다. 가능하면 안전한 타입 가드를 사용하는 것이 좋습니다.
const heading = document.getElementById('heading');
if (heading instanceof HTMLHeadingElement) {
heading.textContent = '안녕하세요!';
}
TypeScript는 DOM 조작 시 타입 안전성을 제공하여 런타임 오류를 줄이고 코드 가독성을 높여줍니다.
TypeScript를 활용하여 더욱 견고하고 유지보수하기 쉬운 웹 애플리케이션을 만들어 보세요! 감사합니다. 🙌