아무리 잘 지은 시스템도 나 혼자 쓰면 시스템이 아니라 개인 취향입니다. 여러 사람이 같은 토큰과 컴포넌트를 자연스럽게 쓰게 만드는 건, 규칙을 말로 부탁하는 게 아니라 코드 구조로 유도하는 일입니다. 팀이 함께 쓰게 만드는 실제 코드 규칙을 보여드리겠습니다.
45.1 하드코딩 대신 토큰 강제
팀이 흔들리는 첫 지점은 각자 눈대중으로 색과 간격을 박아 넣는 것입니다. 같은 파랑인데 사람마다 값이 조금씩 다른 참사가 여기서 생깁니다.
/* 나쁜 예: 사람마다 제각각 박은 색과 간격 */
.header-a { color: #2563eb; padding: 15px; }
.header-b { color: #2560ea; padding: 16px; }
.header-c { color: #2a63ec; padding: 14px; }
얼핏 같아 보이지만 값이 다 달라, 나중에 브랜드 색을 바꾸려면 이 세 곳을 일일이 찾아 고쳐야 합니다. 규칙은 하나뿐이어야 합니다.
/* 좋은 예: 모두 같은 토큰만 바라본다 */
.header-a,
.header-b,
.header-c {
color: var(--color-primary);
padding: var(--space-4);
}
이제 색을 바꿀 자리는 토큰 한 곳뿐이고, 누가 짜도 결과가 같습니다. 임의의 #hex 나 생짜 px 대신 var(--token) 을 쓰자는 약속이 팀의 첫 규칙입니다.
45.2 공용 토큰 파일 한 곳
토큰을 강제하려면 그 토큰이 사는 집이 한 곳이어야 합니다. 저는 tokens.css 한 파일에 모아 두고 모든 화면이 이걸 불러 쓰게 합니다.
/* tokens.css - 시스템의 유일한 진실 */
:root {
--color-primary: #2563eb;
--color-text: #111827;
--space-2: 8px;
--space-4: 16px;
--radius: 6px;
}
각 화면 파일은 맨 위에서 이 한 곳을 불러오기만 하면 됩니다.
/* header.css */
@import "tokens.css";
.header {
color: var(--color-text);
padding: var(--space-4);
border-radius: var(--radius);
}
토큰이 여기저기 흩어져 있으면 저마다 다른 값을 들고 살지만, 한 파일로 모으면 팀 전체가 같은 우물에서 물을 긷습니다. 값을 바꿀 때도 이 파일만 열면 됩니다.
45.3 유틸 클래스로 진입장벽 낮추기
토큰을 쓰라고만 하면 매번 CSS를 새로 짜야 해서 사람들이 귀찮아합니다. 자주 쓰는 조합을 유틸 클래스로 미리 만들어 두면 마크업에서 골라 쓰기만 하면 됩니다.
/* utilities.css - 토큰을 감싼 손잡이들 */
.u-text-primary { color: var(--color-primary); }
.u-p-4 { padding: var(--space-4); }
.u-gap-2 { gap: var(--space-2); }
.u-radius { border-radius: var(--radius); }
이제 새 요소를 만들 때 CSS를 안 짜고 마크업에서 조립합니다.
<div class="u-p-4 u-radius">
<span class="u-text-primary">저장됨</span>
</div>
토큰을 직접 외우지 않아도 손에 잡히는 이름의 클래스만 고르면 되니, 시스템을 처음 쓰는 사람도 금세 올라탑니다. 쉬운 길이 곧 옳은 길이 되게 만드는 것이지요.
45.4 공용 컴포넌트 재사용
같은 버튼을 사람마다 새로 짜면 미묘하게 다른 버튼이 열 개 생깁니다. 공용 컴포넌트 클래스 하나를 두고 모두가 그걸 가져다 쓰게 합니다.
/* 나쁜 예: 화면마다 버튼을 새로 그림 */
.login-submit {
background: #2563eb; color: #fff;
padding: 11px 15px; border-radius: 5px;
}
.signup-submit {
background: #2563eb; color: #fff;
padding: 10px 16px; border-radius: 6px;
}
두 버튼은 형제인데 값이 어긋나 화면마다 다르게 생겼습니다. 공용 클래스 하나로 합칩니다.
/* 좋은 예: 공용 .btn 하나를 재사용 */
.btn {
background: var(--color-primary);
color: #fff;
padding: var(--space-2) var(--space-4);
border-radius: var(--radius);
}
<button class="btn">로그인</button>
<button class="btn">회원가입</button>
두 버튼이 이제 한 부모에서 나와 완전히 같은 모습입니다. 버튼 모양을 바꿀 일이 생겨도 .btn 한 곳만 고치면 온 사이트의 버튼이 함께 바뀝니다.
45.5 네이밍 컨벤션 합의
여럿이 이름을 붙이다 보면 같은 것을 다르게 부르기 시작합니다. 이름 짓는 규칙을 코드로 정해 두면 새 클래스도 자연히 한 가족처럼 보입니다.
/* 나쁜 예: 규칙 없이 제각각 */
.PrimaryBtn { }
.btn_secondary { }
.danger-button { }
/* 좋은 예: 블록__요소--변형 한 규칙 */
.btn { }
.btn__icon { }
.btn--secondary { }
.btn--danger { }
규칙이 정해지면 .btn--danger 라는 이름만 보고도 이게 버튼의 위험 변형임을 누구나 압니다. 이름이 스스로 설명하니 문서를 덜 뒤지고, 새 사람이 지은 이름도 기존과 어긋나지 않습니다.
45.6 임의 값 못 쓰게 막기
합의를 말로만 두면 급할 때 무너집니다. 검사 규칙을 코드로 심어 두면 토큰을 벗어난 생짜 값이 들어올 때 도구가 대신 잡아 줍니다.
/* .stylelintrc 규칙 예: 색은 토큰으로만 */
{
"rules": {
"color-no-hex": true,
"declaration-property-value-disallowed-list": {
"/^padding|^margin/": ["/\\d+px/"]
}
}
}
이제 누군가 color: #2563eb 나 padding: 15px 를 박으면 검사에서 걸려 병합 전에 되돌아옵니다. 규율을 사람의 의지가 아니라 도구의 문턱으로 바꿔 두면, 바쁜 날에도 시스템이 새지 않습니다. 리뷰에서 사람이 매번 잔소리하지 않아도 되니, 지적은 도구에 맡기고 사람은 더 중요한 흐름을 봅니다.
/* 검사에 걸리는 코드 예 */
.card { color: #2563eb; } /* color-no-hex 위반 */
.card { padding: 15px; } /* px 직접값 위반 */
/* 통과하는 코드 */
.card { color: var(--color-primary); padding: var(--space-4); }
45.7 정리
팀 도입의 핵심은 옳게 쓰는 길을 가장 쉬운 길로 만드는 것입니다. 토큰을 한 파일에 모아 유일한 진실로 삼고, 유틸과 공용 컴포넌트로 진입장벽을 낮추고, 네이밍 규칙과 검사 도구로 이탈을 막습니다. 그러면 굳이 부탁하지 않아도 사람들이 자연스럽게 같은 부품을 재사용하고, 시스템은 비로소 개인의 것이 아니라 팀의 것이 됩니다.