컴포넌트 상태를 업데이트해 선택에 따라 컨트롤, 텍스트, 동작이 일관되게 유지되도록 하세요. 표현만 바꿀 때는 CSS를 재정의하세요. 이 가이드에서는 Mac의 Safari에서 로컬 타일 미리보기를 사용해 두 접근 방식을 선택하고 확인하는 방법을 보여 줍니다.
독자가 수행할 작업은 구체적입니다. 직접 작성한 작은 웹페이지를 수정해 모양 컨트롤이 동기화된 상태를 유지하고, 장식 스타일은 쉽게 편집할 수 있도록 합니다. 여기서 컴포넌트 상태란 페이지 자체의 업데이트 함수가 관리하는 JavaScript 객체를 뜻합니다. 프레임워크나 특정 제품의 편집 기능은 전제하지 않습니다.
어떤 변경을 컴포넌트 상태에 포함해야 하나요?
변경 후에 무엇이 서로 일치해야 하는지부터 생각하세요. 둥근 타일을 선택했을 때 체크박스, 미리보기, 설명 문구가 모두 업데이트되어야 한다면, 그 선택의 단일 원본을 상태에 두세요. 그런 다음 세 가지 결과를 모두 그 선택값에서 파생하세요. 이는 구현 권장 사항입니다. 선택과 화면에 표시되는 결과 사이의 관계를 명확하게 만들어 줍니다.
--tile-size 같은 CSS 사용자 지정 속성은 역할이 다릅니다. 이 속성은 var()을 사용해 참조하는 선언에 값을 제공합니다. 사용자 지정 속성은 캐스케이드에 참여하며 보통 상속됩니다. JavaScript가 설정하더라도 동작은 CSS 규칙을 따릅니다. CSS 사용자 지정 속성 명세에서 이 계약을 정의합니다.
편집하기 전에 다음 판단 기준을 사용하세요.
| 요청된 변경 | 권장 소유자 | 이 예시에서의 이유 |
|---|---|---|
| 독자가 둥근 타일이나 정사각형 타일을 선택함 | 컴포넌트 상태 | 체크박스, 설명, 모양이 서로 일치해야 함 |
| 독자가 타일 크기를 선택함 | 컴포넌트 상태, CSS로 렌더링 | 범위 컨트롤, 설명, 크기가 하나의 값을 공유함 |
| 모든 타일에 서로 다른 장식 색상을 적용함 | 스타일시트 선언 | 업데이트해야 할 선택값이나 텍스트가 없음 |
| 미리보기 주변에 더 많은 공간이 필요함 | 스타일시트 선언 | 타일 주변의 레이아웃이 바뀜 |
| 검사하는 동안 임시 크기가 필요함 | 로컬 CSS 재정의 | 나중에 제거할 시각적 실험임 |
구분 기준은 작업에 따라 달라집니다. 이 미리보기에서 색상은 고정된 장식이므로 CSS에 둡니다. 나중에 색상 선택기와 선택한 색상에 대한 설명을 추가한다면, 그 선택을 상태로 다루고 시각적 효과를 렌더링하세요. 같은 선택을 서로 독립적으로 편집 가능한 두 값으로 만들지 마세요.
기본 컨트롤에도 자체 상태가 있습니다. 체크박스의 경우 현재 checked 속성을 읽으세요. HTML checked 속성은 기본 체크 상태를 지정하며, 이후 사용자의 변경 사항을 추적하지 않습니다. 이 차이는 MDN의 체크박스 속성 참조에 설명되어 있습니다.
Mac에서 독립 실행형 미리보기 만들기
일반 텍스트 코드 편집기에서 다음 전체 문서를 tile-preview.html로 저장한 다음 Safari에서 여세요. .html 확장자를 유지하세요. 예시에는 CSS와 JavaScript가 포함되어 있고 시스템 글꼴을 사용하며, 외부 에셋 없이 단순한 기하학적 타일을 그립니다.
스타일시트는 문서의 head에 있습니다. HTML은 style 요소를 CSS 삽입 방법으로 정의하며, 자체 포함된 내부 스타일과 추가 리소스를 참조하는 스타일시트를 구분합니다. 이 예시를 자체 포함된 형태로 유지하면 스타일 입력값이 명확해집니다. HTML 표준의 style 및 scripting 섹션을 참조하세요.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Tile preview</title>
<style>
body {
font-family: system-ui, sans-serif;
margin: 2rem;
color: #222;
background: #fff;
}
label { display: block; margin-block: 1rem; }
.preview { padding: 24px; background: #eee; }
.tile {
width: 80px;
height: 80px;
border-radius: 0;
width: var(--tile-size, 80px);
height: var(--tile-size, 80px);
border-radius: var(--tile-radius, 0);
background-color: #547899;
}
</style>
</head>
<body>
<h1>Tile preview</h1>
<label>
<input id="round" type="checkbox"> Round tile
</label>
<label>
Tile size
<input id="size" type="range" min="40" max="160"
step="1" value="80">
</label>
<button id="reset" type="button">Reset choices</button>
<p id="summary"></p>
<div class="preview">
<div id="tile" class="tile" aria-hidden="true"></div>
</div>
<script>
const state = { rounded: false, size: 80 };
const round = document.getElementById('round');
const size = document.getElementById('size');
const summary = document.getElementById('summary');
const tile = document.getElementById('tile');
const reset = document.getElementById('reset');
function render() {
round.checked = state.rounded;
size.value = String(state.size);
tile.style.setProperty('--tile-size', `${state.size}px`);
tile.style.setProperty('--tile-radius', state.rounded ? '50%' : '0');
summary.textContent = `Tile A: ${state.rounded ? 'round' : 'square'}, ${state.size}px.`;
}
function update(patch) {
const next = { ...state, ...patch };
if (typeof next.rounded !== 'boolean' ||
!Number.isInteger(next.size) ||
next.size < 40 || next.size > 160) {
throw new RangeError('Use a boolean and a size from 40 to 160.');
}
Object.assign(state, next);
render();
}
round.addEventListener('change', () => {
update({ rounded: round.checked });
});
size.addEventListener('input', () => {
update({ size: Number(size.value) });
});
reset.addEventListener('click', () => {
update({ rounded: false, size: 80 });
});
render();
</script>
</body>
</html>
여기서 사용하는 스타일 인터페이스는 문서화되어 있습니다. element.style.setProperty()은 사용자 지정 속성을 포함한 인라인 선언을 작성합니다. 속성 이름은 --tile-size처럼 CSS 표기법을 사용합니다. MDN의 setProperty 참조를 확인하세요.
Round tile을 선택하고, Tile size를 다른 값으로 옮긴 다음, Reset choices를 선택해 보세요. 앞의 두 동작은 모양과 설명을 모두 업데이트해야 하며, 재설정하면 80픽셀의 정사각형으로 돌아가야 합니다. 이 프로그램에서는 state의 필드만 할당해도 render()가 호출되지 않습니다. 검증과 렌더링이 함께 실행되도록 update()을 통해 변경을 처리하세요.
업데이트 및 렌더링 로직은 Node에서 모의 컨트롤과 모의 스타일 객체를 사용해 초기값, 변경값, 유효하지 않은 크기, 재설정에 대해 확인했습니다. 확인은 통과했지만 브라우저 렌더링까지 입증한 것은 아닙니다. 위의 화면 확인 절차를 사용해 Safari에서 파일을 검증하세요.
CSS 선언은 언제 재정의해야 하나요?
지속적인 장식 변경은 저장된 스타일시트에서 편집하세요. 예를 들어 .tile의 background-color을 다른 CSS 색상으로 바꾸거나 .preview의 패딩을 조정할 수 있습니다. 이 프로그램에서는 어느 변경도 새 상태 필드가 필요하지 않습니다. 선택된 값을 나타내지 않기 때문입니다.
임시 실험을 하려면 Safari → Settings… → Advanced → Show features for web developers에서 Safari의 인스펙터를 활성화하세요. Develop → Show Web Inspector를 열거나 Option-Command-I를 누르세요. WebKit의 인스펙터 가이드에 설명된 Mac 단계입니다.
예시를 연 상태에서 다음 전체 콘솔 코드를 실행하면 타일 색상이 일시적으로 바뀌고, 그 재정의를 제거하는 함수가 제공됩니다.
const previewTile = document.getElementById('tile');
if (!previewTile) throw new Error('Open tile-preview.html first.');
previewTile.style.setProperty('background-color', '#8069a6');
function restoreTileColor() {
previewTile.style.removeProperty('background-color');
}
// Run restoreTileColor() when finished comparing colors.
이 인라인 선언을 제거하면 나머지 캐스케이드가 다시 색상을 결정합니다. 스타일시트의 색상 선언을 삭제하는 것은 아닙니다. 제거 방법은 MDN의 removeProperty 참조에 정의되어 있습니다. 이 예시에서 페이지를 새로고침한 뒤에도 선택한 색상을 유지하려면 CSS 소스를 편집하고 저장하세요.
렌더러가 소유하는 값은 임시로 재정의하지 마세요. --tile-size를 직접 바꾸면 설명에는 여전히 상태값이 표시되고, 다음 render() 호출에서 실험한 값이 덮어써집니다. 컴포넌트 전체가 일치하도록 하려면 컨트롤이나 업데이트 함수를 통해 크기를 변경하세요.
재정의가 아무 효과가 없어 보이는 이유는 무엇인가요?
선택자 특이성을 높이기 전에 값이 거치는 경로를 진단하세요. 선언이 유효하더라도 캐스케이드에서 우선순위를 얻지 못할 수 있고, 우선 적용된 선언이 더 이상 사용하지 않는 사용자 지정 속성을 대상으로 할 수도 있습니다.
- 대상을 확인하세요. 모양을 표시하는 요소를 선택했는지 확인하세요.
- 입력을 확인하세요. 이 예시에서 크기는 40에서 160 사이의 정수여야 합니다. 유효하지 않은 업데이트는 상태를 변경하기 전에 오류를 발생시킵니다.
- 사용처를 확인하세요. 타일 크기에는
var(--tile-size)를 사용합니다. 우선 적용되는 직접적인width선언은 너비에서 해당 값을 우회할 수 있지만, 높이는 계속 이 값을 사용합니다. - 우선순위를 확인하세요. 적용 가능한 선언, 중요도, 레이어, 특이성을 비교하세요.
- 다음 상호작용을 확인하세요. 컨트롤을 움직인 뒤 변경 사항이 사라지면
render()이 무엇을 쓰는지 확인하세요.
일반 인라인 선언은 일반 작성자 스타일시트 규칙보다 우선합니다. 하지만 중요도, 애니메이션, 전환은 더 넓은 우선순위 순서에 영향을 줍니다. 선언이 나중에 나왔다고 해서 자동으로 우선 적용되는 것은 아니며, 선택자 특이성이 더 높아도 모든 충돌을 해결할 수 있는 것은 아닙니다. MDN의 캐스케이드 가이드에서 이러한 조건을 설명합니다.
또한 인라인 값과 계산된 값을 구분하세요. element.style는 인라인 스타일을 노출하므로, 인라인 값이 비어 있다고 해서 요소에 스타일이 없다는 뜻은 아닙니다. getComputedStyle()은 활성 스타일시트를 적용한 뒤의 계산된 값을 읽습니다. MDN의 style 참조와 getComputedStyle 참조를 확인하세요.
예시 페이지의 콘솔에서 다음 전체 진단 코드를 실행하세요.
const inspectedTile = document.getElementById('tile');
if (!inspectedTile) throw new Error('Open tile-preview.html first.');
console.table({
inlineSizeToken: inspectedTile.style.getPropertyValue('--tile-size'),
inlineWidth: inspectedTile.style.getPropertyValue('width'),
resolvedWidth: getComputedStyle(inspectedTile).width,
resolvedHeight: getComputedStyle(inspectedTile).height
});
수정하지 않은 예시를 연 직후 예상되는 값은 인라인 크기 토큰 80px, 빈 인라인 너비, 계산된 크기 80px입니다. 이는 인라인 사용자 지정 속성이 인라인 width 선언을 만들지 않고도 스타일시트 선언에 값을 제공할 수 있음을 보여 줍니다.
브라우저 및 리소스의 어떤 제약이 중요한가요?
MDN은 CSS 사용자 지정 속성이 2017년 4월부터 브라우저 전반에서 널리 지원된다고 표시합니다. 그렇다고 모든 구형 브라우저의 지원이 보장되는 것은 아닙니다. 예시에는 var() 선언 앞에 일반 크기 선언이 포함되어 있으므로 사용자 지정 속성을 지원하지 않는 환경에서도 타일이 정적인 모양을 갖습니다. MDN의 사용자 지정 속성 참조를 확인하세요.
var(--tile-size, 80px) 안의 대체값은 사용자 지정 속성을 해석할 수 없는 브라우저와의 호환성을 제공하지 않습니다. 또한 입력된 모든 토큰을 검증하지도 않습니다. 예를 들어 blue처럼 정의된 값도 대체값이 있다는 이유만으로 유효한 너비가 되지는 않습니다. MDN의 사용자 지정 속성 가이드에서 이러한 한계를 설명합니다.
대상 구형 브라우저에 사용자 지정 속성 지원이 없다면, 렌더러를 변경해 일반 인라인 width, height, border-radius 선언을 setProperty()으로 작성하는 것이 지원되는 대안입니다. 상태 검증과 컨트롤 동기화는 동일하게 유지하세요. 이렇게 하면 렌더링 계약이 바뀌므로, 충돌하는 렌더러 쓰기를 제거하고 동일하게 지원될 것이라고 가정하지 말고 대상 브라우저에서 검증하세요.
이 로컬 미리보기에서는 선택이 명확합니다. 모양과 크기 선택에는 상태를 사용하고, 적은 수의 CSS 입력값을 통해 선택을 렌더링하며, 장식은 스타일시트에 두세요. 변경 후에는 컨트롤을 조작하고 재설정해 확인하세요. 미리보기, 컨트롤, 설명은 계속해서 동일한 선택값을 나타내야 합니다.
출처
- CSS Custom Properties for Cascading Variables Module Level 1 — W3C CSS Working Group
- HTML Standard: The style element — WHATWG
- HTMLInputElement: checked property — MDN Web Docs
- CSSStyleDeclaration: setProperty() method — MDN Web Docs
- Enabling Web Inspector — WebKit
- CSSStyleDeclaration: removeProperty() method — MDN Web Docs
- Introduction to the CSS cascade — MDN Web Docs
- HTMLElement: style property — MDN Web Docs
- Window: getComputedStyle() method — MDN Web Docs
- Custom properties (--*): CSS variables — MDN Web Docs
- Using CSS custom properties (variables) — MDN Web Docs