디자인 시안을 웹으로 옮기는 순서: 시맨틱 HTML·토큰·컴포넌트
디자인 시안을 웹으로 옮길 때 핵심은 픽셀을 그대로 복사하는 것이 아니라, 콘텐츠의 의미와 반복 규칙을 브라우저가 이해할 수 있는 구조로 번역하는 것이다. 이 글에서는 시맨틱 HTML, 디자인 토큰, 컴포넌트, 반응형 규칙을 이용해 시안을 유지보수 가능한 코드로 구현하는 순서를 살펴본다.
웹사이트 구축 시리즈 2/3
목표와 페이지 구조가 아직 정리되지 않았다면 웹사이트 구축 전 기획부터 시작하자.
1. 구현 전에 시안을 ‘재료와 규칙’으로 분해한다
시안을 페이지별 이미지로만 보면 비슷한 UI를 매번 새로 만들게 된다. 코드를 쓰기 전, 화면에서 반복되는 재료를 찾아 세 목록으로 분리한다.
콘텐츠
- 제목, 본문, 목록, 이미지, 날짜, 가격처럼 의미가 있는 정보
- CMS나 API에서 바뀔 수 있는 값
- 짧아지거나 길어지고, 없을 수도 있는 값
시각 규칙
- 색, 글자 크기, 행간, 간격, 모서리, 그림자
- 컨테이너 최대 너비와 여백
- 모바일·태블릿·데스크톱에서 달라지는 배치
상호작용 상태
- 기본, hover, focus, active, disabled, loading, error, empty
- 메뉴 열림, 아코디언 펼침, 모달 표시
- 성공 또는 실패 후 사용자가 받는 피드백
이 분해를 해두면 “이 화면과 똑같이”라는 요청을 “같은 콘텐츠 구조와 토큰, 상태 규칙을 사용한다”로 바꿀 수 있다.
2. CSS보다 먼저 문서 구조를 작성한다
HTML은 화면의 의미와 순서를 표현하고, CSS는 그 구조를 배치하고 꾸민다. MDN의 시맨틱 HTML 과정은 올바른 구조가 접근성, 검색, 브라우저 기본 기능의 바탕이 된다고 설명한다.
예를 들어 서비스 상세 페이지의 골격은 다음처럼 시작할 수 있다.
<body>
<header>
<a href="/" aria-label="Duolabs 홈">Duolabs</a>
<nav aria-label="주요 메뉴">...</nav>
</header>
<main>
<article>
<header>
<p>웹사이트 구축</p>
<h1>아이디어를 운영 가능한 웹사이트로 만듭니다</h1>
<p>기획부터 디자인, 개발, 배포까지 필요한 범위를 함께 정합니다.</p>
</header>
<section aria-labelledby="scope-title">
<h2 id="scope-title">제공 범위</h2>
...
</section>
<section aria-labelledby="process-title">
<h2 id="process-title">진행 과정</h2>
...
</section>
</article>
</main>
<footer>...</footer>
</body>header, nav, main, article, section, footer를 무조건 많이 쓰는 것이 목표는 아니다. MDN의 시맨틱 용어 설명처럼 콘텐츠의 역할을 가장 잘 나타내는 요소를 고르는 것이 핵심이다.
구조를 검토할 때 CSS를 잠시 끄고 다음을 확인해 보자.
- DOM 순서만으로 내용을 읽어도 자연스러운가
- 페이지의 주 제목이 분명한가
- 제목 단계가 내용을 논리적으로 나누는가
- 링크와 버튼이 역할에 맞게 사용됐는가
- 목록인 콘텐츠가 실제
ul,ol,li로 표현됐는가 - 입력 필드에 연결된
label이 있는가
div에 클릭 이벤트를 붙여 버튼처럼 만드는 대신 실제 button을 사용하면 키보드 동작과 역할을 브라우저에서 기본으로 얻는다. 접근성은 추가 장식보다 올바른 기본 요소에서 시작한다.
3. 페이지 뼈대부터 구현한다
세부 컴포넌트보다 먼저 전체 레이아웃의 큰 규칙을 잡는다.
:root {
--page-gutter: clamp(1rem, 4vw, 2.5rem);
--content-max: 72rem;
--reading-max: 44rem;
}
.page-shell {
width: min(100% - (var(--page-gutter) * 2), var(--content-max));
margin-inline: auto;
}
.reading-column {
width: min(100%, var(--reading-max));
}이 단계에서는 헤더 높이, 본문 최대 너비, 섹션 간격, 주요 그리드만 확인한다. 카드 그림자나 아이콘 위치 같은 세부 표현부터 만지면 큰 구조가 바뀔 때 수정 비용이 커진다.
웹 디자인 레이아웃의 기본에서 다룬 것처럼 콘텐츠 흐름, 컨테이너, Grid와 Flexbox의 역할을 먼저 정한다. Flexbox는 한 축의 정렬에, Grid는 행과 열의 관계가 중요한 배치에 사용하면 의도가 코드에 잘 드러난다.
4. 디자인 값을 토큰으로 번역한다
시안의 색상 코드와 간격 값을 컴포넌트마다 복사하지 말고, 의미 있는 이름의 토큰으로 정리한다. MDN의 CSS 사용자 정의 속성 가이드는 반복 값을 한 곳에서 정의하고 재사용해 변경 비용과 중복을 줄이는 방법을 설명한다.
:root {
/* primitive */
--blue-600: #2563eb;
--slate-950: #0f172a;
--slate-600: #475569;
--white: #ffffff;
/* semantic */
--color-text: var(--slate-950);
--color-text-muted: var(--slate-600);
--color-surface: var(--white);
--color-action: var(--blue-600);
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 0.75rem;
--space-4: 1rem;
--space-6: 1.5rem;
--space-8: 2rem;
--space-12: 3rem;
--radius-control: 0.625rem;
--radius-card: 1rem;
}원시 토큰은 실제 값의 단계이고, 의미 토큰은 사용 목적이다. 컴포넌트가 --blue-600보다 --color-action을 사용하면 브랜드 색이나 다크 모드가 바뀌어도 컴포넌트를 일일이 수정할 필요가 없다.
토큰은 시안에 보이는 모든 숫자를 등록하는 창고가 아니다. 반복되고, 이름을 붙일 수 있고, 함께 변경될 가능성이 있는 값부터 만든다. 예외가 반복되기 시작할 때 새 토큰이 필요한지 검토한다.
5. 컴포넌트 경계는 모양보다 책임으로 정한다
둥근 사각형이 보인다고 모두 Card 하나에 넣으면 속성이 끝없이 늘어난다. 컴포넌트는 다음 조건을 기준으로 분리한다.
- 여러 위치에서 같은 의미와 동작으로 반복된다.
- 독립적인 상태나 접근성 규칙을 가진다.
- 데이터 구조가 명확하다.
- 한 문장으로 책임을 설명할 수 있다.
예를 들어 ServiceCard는 서비스 제목, 요약, 링크라는 콘텐츠 구조를 가진다. Accordion은 펼침 상태와 aria-expanded, 키보드 포커스라는 상호작용 책임을 가진다. 둘은 사각형이라는 외형이 비슷해도 다른 컴포넌트다.
type ServiceCardProps = {
title: string;
description: string;
href: string;
icon?: ReactNode;
};API는 실제 콘텐츠 모델을 반영해야 한다. showBlueLine, smallTitle, moveButtonUp처럼 시안의 임시 위치를 속성으로 노출하면 변형 조합이 빠르게 복잡해진다. tone="featured", size="compact"처럼 의미 있는 제한된 변형을 사용한다.
6. 대표 컴포넌트 하나를 끝까지 만든다
모든 페이지를 얕게 구현하기보다 버튼, 입력, 카드처럼 반복되는 대표 컴포넌트 하나를 먼저 완성한다. 다음 상태를 한 번에 확인하자.
| 상태 | 확인할 것 |
|---|---|
| 기본 | 콘텐츠와 계층이 명확한가 |
| hover | 포인터 사용자에게 반응이 보이는가 |
| focus-visible | 키보드 포커스가 충분히 뚜렷한가 |
| active | 누르는 순간의 피드백이 있는가 |
| disabled | 비활성 이유와 상태가 구별되는가 |
| loading | 중복 실행을 막고 진행 상태를 알리는가 |
| error | 문제와 해결 방법이 함께 표시되는가 |
.button:focus-visible {
outline: 3px solid color-mix(in srgb, var(--color-action) 35%, transparent);
outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
scroll-behavior: auto !important;
transition-duration: 0.01ms !important;
animation-duration: 0.01ms !important;
}
}한 컴포넌트에서 토큰, 상태, 접근성, 반응형 규칙을 검증하면 나머지 구현의 기준이 생긴다. 이것이 작은 디자인 시스템의 시작이다.
7. 반응형은 화면 크기보다 콘텐츠가 깨지는 지점에서 결정한다
시안에 375px, 768px, 1440px 세 장이 있어도 그 사이의 모든 폭에서 사이트가 동작해야 한다. 브레이크포인트는 특정 기기 이름보다 콘텐츠가 더 이상 자연스럽게 배치되지 않는 지점에서 추가한다.
.feature-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
gap: clamp(1rem, 2vw, 1.5rem);
}구현 중에는 최소한 다음 폭을 연속적으로 드래그해 확인한다.
- 매우 좁은 모바일에서 가로 스크롤이 생기지 않는가
- 메뉴와 긴 제목이 두 줄 이상일 때도 안전한가
- 카드 수가 홀수거나 설명 길이가 달라도 흐름이 자연스러운가
- 큰 화면에서 읽기 폭이 지나치게 넓어지지 않는가
- 200% 확대 시 콘텐츠와 기능이 사라지지 않는가
이미지는 고정 높이에 억지로 맞추기보다 aspect-ratio, object-fit, 반응형 소스를 콘텐츠 성격에 맞게 사용한다. 이미지가 중요한 정보를 포함한다면 작은 화면에서 잘려도 되는지 먼저 판단해야 한다.
8. 실제 콘텐츠와 실패 조건으로 검증한다
Lorem ipsum과 완벽한 샘플 데이터는 문제를 숨긴다. 다음 데이터를 넣어 보자.
- 제목 1자와 60자
- 본문 없음과 매우 긴 본문
- 이미지 없음, 세로형 이미지, 느린 이미지
- 카드 1개, 2개, 7개
- 한글·영문·숫자가 섞인 긴 문자열
- 서버 오류, 빈 검색 결과, 권한 없음
그리고 마우스를 치우고 키보드만으로 메뉴, 폼, 모달을 사용한다. 브라우저 확대, 다크 모드, 동작 줄이기 설정도 확인한다. 자동 검사기는 빠른 신호를 주지만, W3C의 접근성 평가 개요가 설명하듯 도구 하나만으로 접근성을 판정할 수는 없으므로 사람의 검토가 함께 필요하다.
구현 순서 요약
- 시안에서 콘텐츠·시각 규칙·상태를 추출한다.
- CSS 없이 의미와 읽기 순서가 맞는 HTML을 작성한다.
- 페이지 컨테이너와 큰 레이아웃을 구현한다.
- 반복 값을 디자인 토큰으로 정리한다.
- 책임과 상태가 분명한 컴포넌트를 만든다.
- 대표 컴포넌트 하나를 접근성까지 완성한다.
- 콘텐츠가 깨지는 지점에 반응형 규칙을 추가한다.
- 실제·극단·실패 데이터로 페이지를 검증한다.
시안을 정확히 옮긴다는 말은 한 화면 캡처와 픽셀이 같다는 뜻만은 아니다. 다양한 콘텐츠, 화면, 입력 방식에서도 디자인의 의도와 정보의 우선순위가 유지되어야 비로소 웹 구현이 완성된다.
구축 시리즈 이어 읽기
- 웹사이트 구축 전 기획: 목표·콘텐츠·정보 구조를 정리하는 방법
- 현재 글: 디자인 시안을 웹으로 옮기는 순서
- 웹사이트 구축 후 운영 설계: 측정·콘텐츠·보안·백업
참고 자료
함께 읽기
- 스크롤바가 없는데 화면 오른쪽에 빈 여백이 생기는 이유웹 앱의 화면 가장자리는 평소에는 눈에 잘 들어오지 않습니다. 콘텐츠와 배경이 자연스럽게 이어지면 굳이 볼 이유가 없기 때문입니다. 그런데 여러 화면을 확인하던 중 오른쪽 끝에 비어 보이는 세로 띠가 계속 눈에 걸렸습니다. 어떤 화면에서는 나타나고, 어떤 화면에서는 덜 보였습니다.
- Google 번역을 켰더니 React 앱이 오류 화면으로 바뀐 이유한국어, 영어, 일본어, 중국어를 직접 제공하는 React 랜딩 페이지에서 예상하지 못한 문제가 생겼습니다. Chrome이 띄운 "이 페이지를 번역하시겠습니까?" 제안을 수락하면 번역이 시작되는 듯하다가, 잠시 뒤 사이트의 "일시적인 오류가 발생했습니다" 화면으로 바뀌었습니다.
- 데스크톱 AI 화면을 모바일에서 과감히 제거한 이유데스크톱에서 잘 작동하는 AI 인터페이스를 모바일 화면에 그대로 줄이면 기능은 남지만 경험은 쉽게 무너집니다. 사이드바, 대화, 참고 문헌을 동시에 보여주는 3단 구조는 넓은 화면에서는 강력하지만 작은 화면에서는 탐색과 스크롤이 서로 경쟁합니다.
- 디자인 시스템을 씌우기 전에 레이아웃 틀부터 맞춰야 하는 이유같은 디자인 시스템을 사용하는데도 어떤 화면은 정돈되어 보이고, 어떤 화면은 미묘하게 어긋나 보일 때가 있습니다. 색상과 버튼 모양이 같아도 페이지 폭, 여백, 제목 위치, 입력 필드의 구성이 제각각이면 전체 인상은 쉽게 흐트러집니다.
- 디자인 시스템을 복제하지 않는 방법: Polaris 구조와 CSS 테마로 다시 설계한 DEUX디자인 시스템이 두세 개로 늘어나면 가장 먼저 보이는 차이는 색상과 모서리입니다. 그래서 새로운 시스템을 만들 때 기존 버튼과 카드의 색만 바꾸는 일로 시작하기 쉽습니다. 하지만 시간이 지나면 각 시스템 폴더에 Button, Input, Dialog, Chart 구현이 하나씩 생깁니다. 처음에는 독립성이 높아 보이지만,…