[번역] CSS를 넘어선 반응형 디자인: JavaScript로 진정한 적응형 인터페이스 만들기
Soshy·
원문: Responsive Design Beyond CSS: Using JavaScript to Build Truly Adaptive Interfaces
반응형 디자인은 흔히 CSS 문제로 소개됩니다. 미디어 쿼리와 유연한 레이아웃으로 해결하는 것이라고요. 하지만 그건 불완전한 시각입니다. 현대의 반응형 디자인은 레이아웃만큼이나 동작(behavior)에 관한 것이기도 하고, 동작 측면에서는 JavaScript가 필수적인 도구가 됩니다. 반응형을 단순히 "화면 크기"의 문제가 아니라 "맥락(context)"의 문제로 바라보기 시작하면, JavaScript는 인터페이스를 지능적으로 적응시키는 핵심 도구로 떠오릅니다.
가장 기본적인 의미에서 반응형 디자인이란 UI가 다양한 뷰포트 크기에 맞게 조정되는 것을 말합니다. JavaScript는 이러한 변화를 실시간으로 감지하고 반응할 수 있습니다. 가장 직접적인 방법은 리사이즈 이벤트를 수신하고 그에 맞게 응답하는 것입니다.
function handleResize() {
const width = window.innerWidth;
if (width < 768) {
document.body.style.backgroundColor = "#f4f4f4";
} else {
document.body.style.backgroundColor = "#ffffff";
}
}
window.addEventListener("resize", handleResize);
handleResize();
이 예시는 핵심 아이디어를 보여줍니다. 변화를 감지하고 그에 맞는 동작을 적용하는 것입니다. 다만 실제로는 리사이즈 이벤트가 매우 빠르게 발생해 성능이 저하될 수 있습니다. 보다 현실적인 접근법은 스로틀링(throttling) 을 도입해 UI가 초당 수백 번 업데이트를 시도하지 않도록 하는 것입니다.
function throttle(fn, delay) {
let lastCall = 0;
return function () {
const now = Date.now();
if (now - lastCall >= delay) {
lastCall = now;
fn();
}
};
}
const optimizedResize = throttle(() => {
console.log("Resized to:", window.innerWidth);
}, 200);
window.addEventListener("resize", optimizedResize);
반응형 디자인은 크기 변화에 반응하는 것뿐만 아니라, 조건에 맞추는 것이기도 합니다. 브라우저는 이를 위한 강력한 API를 이미 제공합니다. 바로 matchMedia입니다. 너비를 수동으로 확인하는 대신, CSS 미디어 쿼리를 그대로 반영하는 조건을 정의하고 JavaScript에서 이에 반응할 수 있습니다.
const mobileQuery = window.matchMedia("(max-width: 768px)");
function handleDeviceChange(e) {
if (e.matches) {
console.log("Mobile view");
} else {
console.log("Desktop view");
}
}
mobileQuery.addEventListener("change", handleDeviceChange);
handleDeviceChange(mobileQuery);
이 방식은 JavaScript 로직을 CSS와 일치시켜 불일치를 줄여줍니다. 브레이크포인트를 따로 추측할 필요 없이 CSS와 공유하는 것입니다.
반응형은 레이아웃뿐 아니라 콘텐츠 적응도 포함합니다. 예를 들어, 작은 화면에서는 더 가벼운 이미지를 로드하고 싶을 수 있습니다. JavaScript의 조건부 로딩을 활용하면 사용자가 불필요한 리소스를 내려받지 않아도 됩니다.
function loadImageBasedOnScreen() {
const img = document.querySelector("#hero");
if (window.innerWidth < 768) {
img.src = "small.jpg";
} else {
img.src = "large.jpg";
}
}
window.addEventListener("resize", loadImageBasedOnScreen);
loadImageBasedOnScreen();
더 발전된 방식으로는 Intersection Observer API를 활용할 수 있습니다. 단순한 크기가 아닌 가시성(visibility)을 기준으로 반응하며, 반응형 디자인이 성능 중심으로 발전하는 지점이기도 합니다.
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
document.querySelectorAll("img[data-src]").forEach(img => {
observer.observe(img);
});
이 패턴에서 이미지는 화면에 나타나려는 시점에만 로드됩니다. 레이아웃은 CSS로 반응하지만, 로딩 전략은 JavaScript로 반응하는 것입니다.
또 하나 간과되기 쉬운 영역은 입력 방식 반응성입니다. 터치 기반 레이아웃은 마우스 기반과 다르게 동작해야 합니다. JavaScript는 입력 기능을 감지하고 인터랙션을 조정할 수 있습니다.
function isTouchDevice() {
return "ontouchstart" in window || navigator.maxTouchPoints > 0;
}
if (isTouchDevice()) {
document.body.classList.add("touch");
} else {
document.body.classList.add("mouse");
}
이를 통해 인터랙션 패턴을 동적으로 변경할 수 있습니다. 호버 효과, 드래그 동작, 제스처 처리 모두 화면 크기가 아닌 사용자 기기에 맞게 조정할 수 있습니다.
반응형 내비게이션도 JavaScript가 핵심적인 역할을 하는 영역입니다. 공간에 따라 전체 메뉴와 접을 수 있는 메뉴를 전환하는 패턴이 일반적입니다.
const menuButton = document.querySelector("#menu-btn");
const nav = document.querySelector("#nav");
menuButton.addEventListener("click", () => {
nav.classList.toggle("open");
});
단순해 보이는 코드지만, 핵심 아이디어를 담고 있습니다. 반응형 디자인은 종종 상태(state)를 수반한다는 것입니다. 인터페이스는 단순히 재배치되는 것이 아니라 모드가 바뀝니다. JavaScript가 그 상태를 관리합니다.
최근에는 뷰포트 기반 논리 대신 컨테이너 기반 반응형으로의 전환이 늘고 있습니다. CSS 컨테이너 쿼리가 레이아웃을 처리하는 동안, JavaScript는 ResizeObserver를 사용해 요소의 크기를 직접 관찰할 수 있습니다.
const box = document.querySelector(".card");
const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
const width = entry.contentRect.width;
if (width < 300) {
entry.target.classList.add("small");
} else {
entry.target.classList.remove("small");
}
}
});
resizeObserver.observe(box);
이 기법은 컴포넌트를 진정으로 재사용 가능하게 만들어줍니다. 전체 화면에 의존하는 대신, 각 컴포넌트가 자신의 크기와 맥락에 반응하게 됩니다.
반응형 디자인은 궁극적으로 적응성에 관한 것입니다. CSS는 구조와 레이아웃을 우아하게 처리하지만, JavaScript는 타이밍, 동작, 리소스 관리에 대한 제어권을 줍니다. 이 둘을 고려하며 결합시키면, 빠르고 직관적이며 사용자 환경에 맞게 조율된 인터페이스를 만들 수 있습니다.
진정한 변화는 "모바일에서 어떻게 보이는가?"라는 질문을 멈추고 "이 상황에서 어떻게 동작해야 하는가?"라는 질문을 시작할 때 일어납니다. JavaScript는 그 질문에 정확하게 답할 수 있게 해주는 도구입니다.