CSS 강좌 / 큰 스타일시트 정리하기: 파일 구성·중첩·레이어
큰 스타일시트를 정리할 때는 공통 값·기본 규칙·레이아웃·구성 요소·작은 보조 규칙의 책임부터 나눕니다. CSS 중첩은 관련 선택자를 가까이 두고, 캐스케이드 레이어(@layer)는 규칙 그룹의 우선순위를 정하는 데 사용합니다. 파일을 많이 만드는 것보다 변경할 위치와 충돌 원인을 쉽게 찾는 것이 목표입니다.
중복을 줄이고 파일의 책임 정하기
먼저 같은 색과 간격이 복사된 부분, 더 이상 사용하지 않는 선택자, 거의 같은 버튼 규칙을 찾습니다. 역할이 같은 값은 변수로 묶고, 서로 다른 버튼은 공통 규칙과 필요한 차이를 나눕니다. 모든 중복을 제거하려고 복잡한 조건을 만드는 것보다 독립적인 구성 요소 두 개를 유지하는 편이 나을 수도 있습니다. 공통 값을 묶을 때는 공통 색과 간격을 CSS 변수로 관리하기에서 변수의 범위와 상속 조건을 확인하세요.
/* 정리 전: 같은 값이 여러 곳에 반복 */
.lesson-button { padding: .65rem 1rem; color: #166b92; }
.notice-button { padding: .65rem 1rem; color: #166b92; }
/* 정리 후: 공통 모양과 역할을 나눔 */
.button { padding: .65rem 1rem; color: var(--accent); }
.button.text-alert { color: #9d2632; }
위 비교는 정리 방향을 설명하는 부분 코드입니다. 실제 HTML에도 공통 button 클래스를 붙여야 하며, CSS만 이름을 바꾸고 HTML을 그대로 두면 모양이 사라질 수 있습니다. 아래 전체 실습은 이 구성을 파일과 레이어로 나눠 실행합니다.
| 파일 | 책임 | 수정할 때의 판단 |
|---|---|---|
| tokens.css | 공통 색과 간격 | 여러 구성 요소가 공유하는 값인가? |
| base.css | 글꼴·기본 여백·초점 표시 | 문서 전체의 기본 동작인가? |
| layout.css | 내용 영역과 배치 | 구성 요소의 위치 관계인가? |
| components.css | 카드·버튼의 모양과 상태 | 한 구성 요소에 묶이는가? |
| utilities.css | 작고 명시적인 보조 클래스 | 여러 곳에 제한적으로 적용할 예외인가? |
작은 문서는 한 파일 안에 주석 구역만 나눠도 충분합니다. 여러 사람이 다른 구성 요소를 수정하거나 재사용 범위가 커졌을 때 파일 분리를 검토하세요. 파일명은 탐색을 위한 구분이고, 파일을 나눈 것만으로 선택자 충돌이 사라지지는 않습니다. HTML 연결 경로와 캐스케이드도 함께 관리해야 합니다.
중첩에서 같은 요소와 자손을 구분하기
네이티브 CSS 중첩은 브라우저가 직접 해석합니다. Sass 같은 전처리기가 빌드 단계에서 다른 CSS를 만들어 내는 방식과는 다릅니다. 아래 코드는 .card의 제목과 카드 자체의 상태를 가까이 배치합니다.
.card {
& h2 { color: var(--accent); }
&:hover { border-color: var(--accent); }
}
& h2는 카드 안의 h2, &:hover는 카드 자체의 호버를 나타냅니다. & :hover처럼 공백을 넣으면 호버된 자손을 선택하므로 결과가 달라집니다. 같은 요소의 보조 클래스를 선택하는 &.warning과 자손을 선택하는 & .warning도 구분하세요.
/* 위의 단순 중첩을 펼친 형태 */
.card h2 { color: var(--accent); }
.card:hover { border-color: var(--accent); }
이 단일 부모 선택자 예제는 펼친 규칙으로 쉽게 대응됩니다. 여러 부모 선택자를 쉼표로 섞으면 중첩 선택자의 우선순위는 목록 중 가장 높은 우선순위의 영향을 받으므로, 복잡한 중첩을 단순 문자열 조합으로만 이해하지 않습니다. 필요 이상으로 깊게 중첩하면 특정 HTML 구조에 의존하게 됩니다. MDN 중첩 사용 안내와 중첩과 우선순위 설명을 참고하세요.
지원해야 하는 브라우저의 중첩 지원을 확인하고, 지원하지 않는 환경이 필요하다면 위처럼 펼친 CSS나 적절한 빌드 도구를 선택합니다. 새로운 구문을 썼다는 이유만으로 모든 방문자에게 적용된다고 가정하지 않습니다.
레이어가 선택자 우선순위보다 먼저 비교되는 경우
같은 작성자 스타일의 일반 선언에서는 레이어 밖 선언이 레이어 안 선언보다 우선합니다. 레이어 안에서는 미리 정한 순서에서 뒤 레이어가 앞 레이어보다 우선합니다. 따라서 뒤 레이어의 클래스 규칙이 앞 레이어의 ID 규칙을 이길 수 있습니다. 같은 레이어·중요도 등 다른 비교 조건도 같다면 선택자 우선순위, 스코프의 근접성, 선언 순서 같은 조건을 이어서 판단합니다.
@layer tokens, base, layout, components, utilities;
@layer base { #layer-target { color: #183044; } }
@layer utilities { .text-alert { color: #9d2632; } }
예제 버튼에는 두 선택자가 모두 일치합니다. 일반 선언에서는 utilities가 뒤이므로 빨간색이 적용됩니다. 버튼의 ID 우선순위가 높다는 사실만으로 결과를 예측하면 틀릴 수 있습니다. 레이어의 순서는 이름이 처음 등록된 순서에서 정해지며, 나중에 같은 레이어를 다시 열어도 그 레이어가 맨 뒤로 이동하지 않습니다.
!important 선언은 일반 선언보다 우선하며, 중요한 선언끼리는 레이어 순서가 반대입니다. 앞 레이어의 important가 뒤 레이어의 important보다 우선하고, 레이어 안의 important는 레이어 밖 important보다 우선합니다. 이 예제는 같은 작성자 CSS와 해당 속성의 레이어 비교에 범위를 한정합니다. 사용자·브라우저 스타일, 인라인 선언, 실행 중인 transition 등 캐스케이드의 다른 조건까지 “레이어만 보면 된다”고 확대하지 않습니다. MDN layer 문서에서 전체 조건을 확인할 수 있습니다.
전체 실습: HTML과 다섯 CSS 파일 연결하기
빈 폴더에 아래 구조를 만드세요. 모든 파일은 UTF-8로 저장합니다. index.html에서 상대 경로 styles/...를 사용하므로 CSS 파일은 styles 하위 폴더에 있어야 합니다. 별도 서버나 외부 라이브러리 없이 index.html을 브라우저로 열 수 있습니다. 파일을 읽지 못한다면 외부 CSS 파일 연결과 경로로 파일 위치와 연결 주소를 비교하세요.
stylesheet-project/
index.html
styles/
tokens.css
base.css
layout.css
components.css
utilities.css
index.html에는 실제 링크 태그와 클래스가 들어 있습니다. 비교 버튼은 동작을 실행하기 위한 버튼이 아니라 적용된 글자색과 초점을 확인하는 실습용입니다.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>역할별 CSS 파일과 레이어</title>
<link rel="stylesheet" href="styles/tokens.css">
<link rel="stylesheet" href="styles/base.css">
<link rel="stylesheet" href="styles/layout.css">
<link rel="stylesheet" href="styles/components.css">
<link rel="stylesheet" href="styles/utilities.css">
</head>
<body>
<main class="demo">
<h1>작은 스타일시트 정리 실습</h1>
<article class="card">
<h2>중첩으로 카드 범위 제한</h2>
<p>카드 제목에는 강조색이 적용됩니다. 카드 밖의 제목과 비교하세요.</p>
<button class="button" type="button">일반 버튼</button>
</article>
<section><h2>카드 밖의 제목</h2><p>기본 글자색을 사용합니다.</p></section>
<button id="layer-target" class="button text-alert" type="button">레이어 비교 버튼</button>
</main>
</body>
</html>
styles/tokens.css는 레이어 순서를 먼저 등록하고 공통 값을 선언합니다. 다른 파일은 같은 이름의 레이어를 열어 규칙을 추가합니다.
@layer tokens, base, layout, components, utilities;
@layer tokens {
:root { --ink: #183044; --accent: #166b92; --space-card: 1.25rem; }
}
styles/base.css에는 기본 문서 모양과 초점 표시를 둡니다. ID 규칙은 레이어가 우선순위에 미치는 영향을 비교하려고 넣은 장치입니다.
@layer base {
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; padding: 1rem; color: var(--ink); font-family: sans-serif; line-height: 1.6; }
h1 { font-size: 1.5rem; } h2 { font-size: 1.2rem; }
button { font: inherit; cursor: pointer; }
:focus-visible { outline: 3px solid #164f73; outline-offset: 3px; }
#layer-target { color: #183044; }
}
styles/layout.css는 내용 영역과 구역 간 간격을 관리합니다.
@layer layout {
.demo { display: grid; gap: 1rem; max-width: 44rem; margin-inline: auto; }
.demo > h1 { margin-block: 0 .5rem; }
.demo > button { justify-self: start; }
}
styles/components.css는 카드와 버튼을 관리합니다. 제목 색은 카드 안에만 적용되고, 카드에 호버하거나 내부 버튼에 초점을 두면 테두리가 바뀝니다.
@layer components {
.card {
padding: var(--space-card); border: 2px solid #90a4b2; border-radius: .5rem;
& h2 { margin-top: 0; color: var(--accent); }
&:hover, &:focus-within { border-color: var(--accent); }
}
.button {
padding: .65rem 1rem; color: var(--accent); background: #eaf4fa;
border: 1px solid #166b92; border-radius: .3rem;
}
}
styles/utilities.css는 비교 버튼의 보조 클래스를 관리합니다. 이 파일의 일반 선언이 앞 레이어의 ID·버튼 규칙보다 우선합니다.
@layer utilities {
.text-alert { color: #9d2632; }
}
파일·중첩·레이어를 하나씩 비교하고 복원하기
- 기본 화면에서 카드 제목과 일반 버튼은 파란색, 카드 밖 제목은 기본색, 비교 버튼은 빨간색인지 확인합니다. Tab으로 버튼에 초점 표시가 생기는지도 봅니다.
- components.css의 카드 안
& h2규칙만 주석 처리합니다. 카드 제목도 기본색으로 돌아갑니다. 확인 후 주석을 해제합니다. - HTML의 두 버튼 모두에 text-alert 클래스를 붙여 봅니다. 카드 내부 버튼도 빨간색이 됩니다. 내부 버튼의 추가 클래스만 제거해 복원합니다.
- components.css 끝에서
@layer블록 밖에.button { color: #28633b; }만 추가합니다. 두 버튼은 초록색이 됩니다. 레이어 순서보다 레이어 밖 일반 선언이 앞서는 결과입니다. 확인 후 새 규칙을 삭제합니다. - base.css의 ID color 선언에만 important를 추가합니다. 비교 버튼은 기본색으로 바뀝니다. 그 상태에서 utilities.css의 color에도 important를 추가하면 앞 레이어의 기본색이 유지됩니다. 비교 후 두 important를 모두 제거합니다.
- HTML에서 components.css 링크만 잠시 제거합니다. 카드·버튼의 구성 요소 모양이 사라집니다. 경로를 원래대로 연결하고 새로고침해 복원합니다.
스타일이 적용되지 않을 때 확인하는 순서
파일 분리 문제와 선택자 충돌을 따로 확인합니다. 브라우저 개발자 도구의 Network나 Sources에서 파일이 실제로 로드됐는지 먼저 보고, 요소를 선택해 Styles에서 규칙이 일치하는지와 어떤 선언이 덮어쓰였는지 살펴봅니다. 파일이 안 보이면 상대 경로·파일명·확장자를, 규칙이 안 보이면 클래스와 구문 지원을 확인합니다.
일치하는 규칙이 있어도 취소선이 생기면 레이어·important·선택자·스코프·순서를 비교합니다. 공통 변수가 예상과 다르면 변수 선언 범위도 함께 봅니다. 분리 후에는 주요 화면과 초점·호버를 다시 점검하고, 불필요한 중요한 선언을 계속 추가하는 대신 충돌을 일으킨 책임 구분과 선택자를 고치는 편이 유지보수에 도움이 됩니다.










