CSS 모듈 활용
module.css의 지역화된 클래스 이름으로 스타일 충돌을 막고 전역 CSS·SCSS와의 책임 범위를 구분합니다.
웹 애플리케이션의 화면 구조와 사용자 경험은 스타일링(styling) 방식에 영향을 받습니다. React 기반의 Next.js 애플리케이션에서 스타일을 적용하는 방법은 여러 가지이며, 그 가운데 CSS 모듈(CSS Modules)은 클래스 이름을 파일 단위로 지역화해 스타일 충돌을 줄입니다.
9장에서는 CSS 모듈, Sass, CSS-in-JS, Tailwind CSS 순서로 Next.js의 스타일링 방법을 살펴봅니다. 이 글에서는 CSS 모듈의 개념과 필요성, Next.js 프로젝트에서 활용하는 방법을 정리합니다.
CSS 모듈이란 무엇인가요?
CSS 모듈은 기본적으로 클래스 이름과 애니메이션 이름을 파일마다 고유한 이름으로 바꾸고, 컴포넌트가 그 이름을 가져와 쓰는 방식입니다. 그래서 서로 다른 모듈에서 같은 클래스 이름을 써도 이름 충돌을 줄일 수 있습니다.
먼저 세 가지를 구별해 두면 좋습니다. import styles from './Button.module.css'의 styles는 클래스 이름을 고르는 객체이고, className은 그 이름을 DOM에 붙이는 자리입니다. 그리고 브라우저가 최종 색을 정하는 단계에서는 일반 CSS의 선택자, 상속, 우선순위가 그대로 작동합니다. 이름이 지역화되어도 부모의 color 상속이나 전역 button 규칙이 사라지지는 않습니다.
예를 들어 모듈의 .card p는 카드 아래의 모든 p를 선택합니다. 그 p가 자식 React 컴포넌트가 돌려준 요소여도, 실제 DOM에서 카드의 후손이면 대상이 됩니다. React 컴포넌트를 파일로 나눈 것이 CSS 선택자가 닿는 범위를 나누지는 않으며, CSS 모듈은 Shadow DOM을 만들지 않습니다.
정적인 모듈 CSS를 import한다는 이유만으로 "use client"가 필요한 것은 아닙니다. 뒤에 나오는 버튼은 클릭 함수를 받을 수 있는 클라이언트 컴포넌트 예제입니다. CSS의 :hover 자체는 클릭 콜백이나 React 상태 없이 브라우저가 처리합니다. 일반 onClick 함수를 넘기는 호출부는 클라이언트 컴포넌트 안에서 준비해야 합니다.
CSS 모듈의 특징을 정리하면 다음과 같습니다. 서로 다른 모듈의 같은 클래스 이름이 서로 다른 출력 이름으로 연결되는 로컬 스코프(local scope)를 가지며, 생성된 클래스는 그것을 적용한 DOM 요소에 작용합니다. 출력 클래스 이름은 빌드 도구가 만들며, 구체적인 형식은 도구와 개발 모드, 운영 모드에 따라 달라질 수 있으므로 직접 적지 않고 styles.btn처럼 참조합니다. CSS 파일과 그것을 가져오는 컴포넌트의 의존 관계가 드러나며, 같은 모듈을 여러 컴포넌트가 함께 쓸 수도 있습니다. 그리고 CSS 파일을 JavaScript나 TypeScript 코드에서 직접 import해 씁니다.
왜 CSS 모듈을 사용해야 할까요?
전통적인 CSS 작성 방식에는 몇 가지 문제가 있었습니다. 첫째는 전역 스코프 오염(global scope pollution)입니다. 모든 CSS 클래스 이름은 기본적으로 전역 범위를 가지므로, 다른 컴포넌트나 페이지에서 의도치 않게 같은 클래스 이름을 쓰면 스타일이 덮어써지는 충돌(collision)이 생깁니다. 이는 특히 규모가 큰 프로젝트나 여러 개발자가 협업하는 환경에서 디버깅을 어렵게 하고 유지보수를 복잡하게 만듭니다.
둘째는 스타일 사이의 의존 관계를 관리하기 어렵다는 점입니다. 어떤 스타일이 어떤 HTML 요소에 적용될지 파악하기 어렵고, 컴포넌트를 지울 때 그 스타일도 안전하게 지울 수 있는지 확신하기 어려워 데드 코드가 남습니다.
CSS 모듈은 이 문제를 두 가지로 해결합니다. 다른 모듈의 클래스 이름을 전역에서 미리 차지할 필요가 없으므로 이름 충돌이 줄고, 컴포넌트 파일에서 CSS 파일을 직접 import하므로 스타일을 어디에서 쓰는지 추적하기 쉬워집니다.
Next.js에서 CSS 모듈 사용하기
Next.js는 CSS 모듈을 기본으로 지원하므로 추가 설정 없이 바로 쓸 수 있습니다. CSS 파일은 [name].module.css로 작성하며, .module.scss와 .module.sass는 뒤에서 설명하는 sass 패키지를 설치한 뒤에 쓸 수 있습니다.
실습은 앞 장에서 쓴 App Router 프로젝트에 src/app/css-modules 폴더를 만들고, 다음 page.tsx, page.module.css, StyledButton.tsx, StyledButton.module.css 네 파일을 제목의 경로대로 준비합니다. 페이지는 자신의 배치를 page.module.css에서, 버튼은 자신의 색상과 동작 상태를 StyledButton.module.css에서 가져옵니다. 파일 하나만 복사하면 import가 가리키는 나머지 파일이 없어 화면을 확인할 수 없습니다.
이 예제의 버튼은 선택한 색상과 hover, active 스타일을 확인하기 위한 것입니다. 페이지가 onClick을 넘기지 않으므로, 클릭한 뒤 데이터가 저장되거나 다른 페이지로 이동한다고 예상하지 않습니다. 이벤트 처리를 더하려면 그 함수를 정의할 클라이언트 컴포넌트를 따로 설계해야 합니다.
실습: 버튼 컴포넌트에 CSS 모듈 적용하기
간단한 버튼 컴포넌트를 만들고 CSS 모듈로 스타일을 적용해 봅시다. 먼저 src/app/css-modules/page.tsx를 만듭니다. 이 페이지는 서버 컴포넌트이며, 곧 만들 클라이언트 컴포넌트 StyledButton을 가져와 씁니다.
// src/app/css-modules/page.tsx
import StyledButton from './StyledButton'; // 클라이언트 컴포넌트 임포트
import styles from './page.module.css'; // 페이지 요소에 적용할 CSS 모듈 임포트
export default function CssModulesPage() {
return (
<div className={styles.container}>
<h1 className={styles.title}>CSS 모듈 사용 예제</h1>
<p className={styles.description}>
아래 버튼은 CSS 모듈을 사용하여 고유한 스타일을 가집니다.
</p>
<div style={{ display: 'flex', gap: '20px', marginTop: '30px' }}>
<StyledButton label="클릭하세요" />
<StyledButton label="다른 버튼" primary={true} />
</div>
</div>
);
}페이지는 styles.container처럼 import한 객체의 속성으로 클래스 이름을 지정합니다. 이 속성의 값은 빌드할 때 파일마다 고유하게 바뀐 이름이므로, className="container"처럼 문자열을 직접 적으면 모듈의 스타일이 적용되지 않습니다. src/app/css-modules/page.module.css에는 page.tsx에 적용할 기본 레이아웃 스타일을 정의합니다. 여기의 클래스 이름 container, title, description이 곧 styles 객체의 속성 이름이 됩니다.
/* src/app/css-modules/page.module.css */
.container {
padding: 40px;
max-width: 800px;
margin: 20px auto;
background-color: #f8f8f8;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
text-align: center;
}
.title {
color: #333;
margin-bottom: 15px;
font-size: 2.5em;
}
.description {
color: #666;
font-size: 1.1em;
line-height: 1.6;
}src/app/css-modules/StyledButton.tsx는 버튼 컴포넌트를 정의하는 클라이언트 컴포넌트입니다. primary가 true이면 공통 클래스에 강조 클래스를 더합니다.
"use client"; // 클라이언트 컴포넌트임을 명시
import React from 'react';
import buttonStyles from './StyledButton.module.css'; // 🚨 CSS 모듈 임포트
interface StyledButtonProps {
label: string;
primary?: boolean;
onClick?: () => void;
}
export default function StyledButton({ label, primary = false, onClick }: StyledButtonProps) {
// CSS 모듈의 클래스 이름을 조합합니다.
return (
<button
className={`${buttonStyles.btn} ${primary ? buttonStyles.primary : ''}`}
onClick={onClick}
>
{label}
</button>
);
}StyledButton은 클라이언트 컴포넌트이지만, CSS 모듈을 가져와 className에 넣는 방식은 서버 컴포넌트인 페이지와 같습니다. primary가 false이면 템플릿 문자열의 두 번째 자리가 빈 문자열이 되어 btn에 해당하는 이름 하나만 남습니다. 마지막으로 src/app/css-modules/StyledButton.module.css에 StyledButton에 적용할 스타일을 정의합니다. .btn은 모든 버튼에 붙는 공통 규칙이고, .primary는 강조 버튼에만 더해지는 규칙입니다.
.btn {
padding: 12px 25px;
font-size: 1.1em;
border: none;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.3s ease, transform 0.1s ease;
font-weight: bold;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.btn:hover {
transform: translateY(-2px);
}
.btn:active {
transform: translateY(0);
box-shadow: none;
}
/* 기본 버튼 스타일 */
.btn {
background-color: #007bff;
color: white;
}
/* primary prop이 true일 때 적용될 스타일 */
.primary {
background-color: #28a745;
color: white;
}
.primary:hover {
background-color: #218838;
}개발 서버(npm run dev)를 실행한 뒤 http://localhost:3000/css-modules에 접속하면, 두 버튼의 배경색이 서로 다른 것을 확인할 수 있습니다. 브라우저 개발자 도구의 Elements 탭에서는 버튼의 클래스 이름이 원래의 btn, primary와 어떻게 연결되는지 확인합니다. 구체적인 출력 이름은 실행 환경에 따라 달라질 수 있으며, page.module.css의 클래스 이름도 비슷하게 고유한 이름으로 바뀝니다.
CSS 모듈과 일반 CSS의 차이점
NEXT · 9.1 · CSS 모듈
CSS 모듈은 클래스 이름을 파일별로 지역화합니다
예제의 buttonStyles.btn과 buttonStyles.primary는 모듈이 만든 이름을 참조합니다. 클래스 이름, 불러오기, 적용 범위, 사용처를 각각 비교합니다.
| 비교 기준 | 일반 CSS | CSS 모듈 |
|---|---|---|
| 클래스 이름 | 작성한 전역 이름을 여러 파일이 함께 사용 | 파일별 고유 이름으로 변환하여 import 객체로 참조 |
| 불러오기 | App Router의 JS/TS import 또는 스타일시트 연결 | styles 객체를 import하고 className에 매핑된 이름 지정 |
| 적용 범위 | 일치하는 선택자에 따라 여러 화면에 영향 | 생성된 클래스를 적용한 요소에 영향; 상속과 전역 규칙은 남음 |
| 공유와 삭제 | 전역 선택자의 사용처 확인 | 같은 모듈을 여러 컴포넌트가 공유할 수 있으므로 import 사용처 확인 |
- 클래스 이름
- 일반 CSS: 작성한 전역 이름을 여러 파일이 함께 사용CSS 모듈: 파일별 고유 이름으로 변환하여 import 객체로 참조
- 불러오기
- 일반 CSS: App Router의 JS/TS import 또는 스타일시트 연결CSS 모듈: styles 객체를 import하고 className에 매핑된 이름 지정
- 적용 범위
- 일반 CSS: 일치하는 선택자에 따라 여러 화면에 영향CSS 모듈: 생성된 클래스를 적용한 요소에 영향; 상속과 전역 규칙은 남음
- 공유와 삭제
- 일반 CSS: 전역 선택자의 사용처 확인CSS 모듈: 같은 모듈을 여러 컴포넌트가 공유할 수 있으므로 import 사용처 확인
CSS 모듈은 Shadow DOM을 만들지 않습니다. 상속·전역 규칙의 영향은 남으며, 같은 모듈을 여러 컴포넌트가 공유할 수 있습니다.
위 그림은 일반 CSS와 CSS 모듈의 적용 범위를 비교합니다. 위쪽 흐름에서 모듈 CSS 소스의 btn, primary 선택자는 모듈 처리를 거쳐 두 가지 출력이 됩니다. 하나는 import 객체의 이름으로 React의 클래스 선택에 이름 매핑을 제공하고, 다른 하나는 생성된 CSS 규칙으로 브라우저에 전달됩니다. React의 primary props는 클래스 선택의 별도 입력이며, btn은 공통으로 넣고 true이면 primary를 더해 button의 className에 매핑된 이름을 지정합니다. 브라우저는 같은 요소의 규칙을 결합하면서 hover, 상속, 전역 CSS 조건도 함께 판단합니다. 그림의 설명처럼 props 입력과 CSS 출력은 차례로 실행되는 두 단계가 아니며, 빌드 도구의 세부 파일과 스타일시트 전송 단계는 생략했습니다.
아래쪽 표는 네 가지 기준으로 두 방식을 비교합니다. 일반 CSS는 작성한 전역 이름을 여러 파일이 함께 쓰지만, CSS 모듈은 파일마다 고유한 이름으로 바꾸어 import 객체로 참조합니다. 일반 CSS는 JS나 TS의 import 또는 스타일시트 연결로 불러오고, CSS 모듈은 styles 객체를 import해 className에 매핑된 이름을 지정합니다. 일반 CSS는 일치하는 선택자에 따라 여러 화면에 영향을 주고, CSS 모듈은 생성된 클래스를 적용한 요소에 영향을 주지만 상속과 전역 규칙은 남습니다. 공유와 삭제에서는 일반 CSS가 전역 선택자의 사용처를, CSS 모듈은 같은 모듈을 여러 컴포넌트가 함께 쓸 수 있으므로 import 사용처를 확인해야 합니다.
StyledButton의 primary가 false이면 buttonStyles.btn만 className에 들어가 기본 파란 배경을 씁니다. true이면 btn과 primary에 대응하는 두 출력 이름을 함께 넣습니다. 두 클래스가 같은 요소에 적용되므로 공통의 padding, transition, 변형 규칙은 그대로 유지되고, 뒤에 선언된 primary 규칙이 배경을 초록으로 정합니다. CSS 모듈이 DOM의 상속을 따로 끊어 내는 것은 아닙니다.
개발자 도구에서는 btn이라는 원래 문자열이 보이는지보다, JSX가 import 객체에서 얻은 이름을 실제 요소에 넣었는지와 그 이름에 해당하는 CSS 규칙이 로드되었는지를 차례로 확인하세요. 출력 이름의 구체적인 해시 형식은 실습의 정답 조건으로 삼지 않습니다.
클래스를 뒤에 적는 것과 규칙을 뒤에 선언하는 것은 다릅니다
className의 두 이름을 반대로 나열해도 같은 두 클래스가 붙습니다. primary 색이 선택되는 이유는 HTML 문자열에서 뒤에 적었기 때문이 아니라, 명시도(specificity)가 같은 .btn과 .primary의 배경 규칙 가운데 .primary 선언이 CSS에서 나중에 나오기 때문입니다. !important, 애니메이션, 서로 다른 출처나 cascade layer가 없는 같은 작성자의 규칙을 비교할 때, 명시도가 같아야 선언 순서로 결과가 정해집니다.
같은 조건에서 전역 .toolbar button은 클래스 하나와 요소 하나를 포함해 명시도가 0-1-1이고, 모듈의 .primary 출력 클래스는 0-1-0입니다. 세 숫자는 차례로 ID 선택자의 수, 클래스·속성 선택자와 가상 클래스의 수, 태그 선택자와 가상 요소의 수이며, 앞자리부터 비교하는 방법은 웹 입문의 선택자 실습에서 다룹니다. 그래서 버튼이 .toolbar 안에 있으면 전역 배경이 이길 수 있습니다. 모듈 이름을 제대로 넣었는데도 색이 다르다면, 개발자 도구의 계산된 스타일과 덮어쓴 규칙을 함께 확인하세요. Next.js CSS 안내도 전역 스타일의 영향과 CSS 순서를 구분해 설명합니다.
더 깊이: 서로 다른 CSS 파일의 선언 순서는 무엇이 정할까
한 파일 안의 선언 순서는 눈에 보이지만, 서로 다른 CSS 모듈 파일의 규칙이 최종 CSS에서 어떤 순서로 놓일지는 따로 정해집니다. Next.js는 프로덕션 빌드에서 스타일시트를 자동으로 나누고 합치며, 이때 CSS의 순서는 코드에서 스타일을 import한 순서를 따릅니다. 예를 들어 페이지가 BaseButton 컴포넌트를 먼저 import하고 그다음에 자기 page.module.css를 import하면, 버튼의 CSS가 페이지의 CSS보다 앞에 놓입니다. 그래서 명시도가 같은 두 모듈의 규칙이 부딪히면 나중에 import된 쪽이 이기며, import를 자동으로 정렬하는 린터나 포매터가 이 순서를 바꿔 결과를 뒤집을 수도 있습니다. 공식 문서도 ESLint의 sort-imports 같은 자동 정렬을 끄고, 전역 스타일은 애플리케이션의 루트에서 import하라고 권합니다.
개발 서버와 프로덕션의 차이도 알아 둡니다. 개발 중의 CSS 순서는 프로덕션과 다르게 나올 수 있으므로, 최종 순서는 next build의 결과로 확인해야 합니다. 또한 Next.js는 Suspense와 맞물리는 React의 스타일시트 기능을 쓰기 때문에, 레이아웃이나 페이지에서 import한 전역 CSS는 지금의 구현에서 다른 라우트로 이동해도 문서에서 제거되지 않습니다. 특정 페이지에서만 import한 전역 선택자가 이동한 뒤의 페이지에도 남아 충돌할 수 있으므로, 전역 CSS에는 정말로 전체에 적용할 규칙만 둡니다.
SCSS/SASS와 CSS 모듈 함께 사용하기
Next.js는 CSS 모듈과 함께 SCSS와 SASS도 기본으로 지원합니다. sass(Dart Sass) 패키지를 설치한 뒤, 파일 확장자를 .module.scss나 .module.sass로 바꾸어 쓰면 됩니다. 먼저 패키지를 설치합니다.
npm install sass
# 또는
yarn add sass설치한 뒤에는 따로 설정하지 않아도 .scss 파일을 import할 수 있습니다. 다음으로 StyledButton.module.css를 StyledButton.module.scss로 바꾸면 중첩이나 변수 같은 SCSS 문법을 쓸 수 있습니다. 아래 파일의 $primary-color 같은 $ 변수는 색상 값을 한곳에 모아 두는 SCSS 변수이고, &:hover의 &는 바깥 선택자를 가리켜 .btn:hover처럼 펼쳐집니다.
$primary-color: #28a745;
$default-color: #007bff;
.btn {
padding: 12px 25px;
font-size: 1.1em;
border: none;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.3s ease, transform 0.1s ease;
font-weight: bold;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
&:hover { // SCSS 중첩 문법
transform: translateY(-2px);
}
&:active {
transform: translateY(0);
box-shadow: none;
}
/* 기본 버튼 스타일 */
background-color: $default-color; // SCSS 변수 사용
color: white;
}
/* primary prop이 true일 때 적용될 스타일 */
.primary {
background-color: $primary-color;
color: white;
&:hover {
background-color: darken($primary-color, 10%); // SCSS 함수 사용
}
}이 코드는 darken()을 씁니다. 이 함수는 Dart Sass에서 폐기 예정이며, 새 코드에서 같은 HSL 명도 감소를 표현하려면 @use "sass:color"와 color.adjust($color, $lightness: -10%, $space: hsl)를 쓸 수 있습니다. 비례로 조정하는 color.scale()과는 계산 방식이 다릅니다. 또한 .btn 안에서 중첩 규칙(&:hover, &:active) 뒤에 background-color 같은 선언을 두는 배치는 Dart Sass 1.77.7부터 1.91까지 선언 순서에 관한 경고를 냈습니다. 1.92.0부터는 경고 없이 소스에 적은 순서대로 그 선언을 중첩 규칙 뒤의 별도 .btn 규칙으로 출력하며, 이 예제는 겹치는 속성이 없어 결과가 같습니다. 출력 순서를 짐작하기 쉽도록 새로 작성할 때는 선언을 중첩 규칙보다 앞에 두는 편이 좋습니다.
마지막으로 컴포넌트에서 import 경로를 바꿉니다. 다음 수정 블록은 기존 StyledButton.tsx의 import 한 줄을 바꾸는 부분이므로, 앞의 Props, 함수, return을 지우고 이 두 줄로 파일 전체를 덮어쓰지 마세요. CSS 방식과 SCSS 방식은 같은 버튼의 대체 스타일 파일이므로, SCSS 버전을 고를 때 기존 CSS import를 바꾸어 확인합니다.
import buttonStyles from './StyledButton.module.scss'; // 🚨 확장자를 .scss로 변경
// ...이제 SCSS의 변수, 중첩, 믹스인을 CSS 모듈의 파일 단위 스코프 안에서 쓸 수 있습니다.
정리
- CSS 모듈은 클래스 이름과 애니메이션 이름을 파일마다 고유한 이름으로 바꾸며, 컴포넌트는 import한
styles객체로 그 이름을 참조합니다. - 이름은 지역화되지만 선택자, 상속, 우선순위 같은 CSS 계산은 그대로이며, 후손 선택자는 자식 컴포넌트의 요소에도 닿습니다.
- 같은 요소에 붙은 두 클래스의 결과는
className의 나열 순서가 아니라 명시도와 CSS의 선언 순서로 정해집니다. - Next.js는
.module.css를 기본으로 지원하고,sass패키지를 설치하면.module.scss와.module.sass도 쓸 수 있습니다.
연습 문제
1. 이름 매핑과 최종 스타일을 구별하기
다른 전역 규칙이 없고 원래 CSS의 선언 순서가 유지된다고 가정합니다. primary=true인 버튼에 마우스를 올렸을 때 배경색과 세로 이동을 각각 설명해 보세요. 같은 요소의 className을 문자열 "btn primary"로 직접 바꾸면 기존 모듈 규칙을 계속 참조할까요? 같은 모듈을 import한 다른 버튼에도 같은 규칙을 적용할 수 있는지도 판단해 보세요.
해설 보기
true인 버튼은 btn과 primary의 출력 이름을 모두 가집니다. .primary:hover의 배경 #218838과 .btn:hover의 translateY(-2px)가 함께 적용되며, 서로 다른 속성이므로 초록색 규칙이 이동을 없애지 않습니다. 모듈의 btn과 primary는 출력 이름으로 바뀌므로, 문자 그대로 "btn primary"만 넣으면 그 두 매핑을 쓰지 않습니다. 우연히 같은 이름의 별도 전역 규칙이 있는 경우는 이 문제의 전제에서 뺐습니다.
같은 모듈을 여러 버튼이 import하면 같은 매핑과 규칙을 다시 쓸 수 있습니다. 그래서 버튼 컴포넌트 하나를 지웠다는 이유만으로 CSS 파일을 지우기 전에, 다른 import 사용처도 확인해야 합니다.
2. 클래스 나열 순서 바꾸기
외부 CSS가 없는 원래의 StyledButton.module.css와 primary=true를 기준으로 풉니다. btn과 primary의 출력 이름을 className에서 반대로 나열하면 기본 배경이 파랑으로 돌아갈까요? 두 선택자의 명시도와 선언 순서로 설명해 보세요.
해설 보기
초록 #28a745가 유지됩니다. HTML의 클래스 나열 순서는 우선순위를 만들지 않습니다. 두 클래스 선택자는 각각 0-1-0으로 같고, CSS에서 .primary의 배경 선언이 뒤에 있으므로 이깁니다.
3. hover와 active가 동시에 참일 때
같은 조건에서 마우스가 버튼 위에 있고 버튼을 누르고 있어 :hover와 :active가 동시에 참입니다. 배경색, 이동량, 그림자는 무엇일까요? transition이 끝난 값을 기준으로 판단합니다.
해설 보기
배경은 .primary:hover의 #218838입니다. 이동은 .btn:hover와 .btn:active가 각각 0-2-0으로 명시도가 같고 :active 선언이 뒤에 있으므로 translateY(0)이 됩니다. :active가 box-shadow: none을 선언하므로 그림자도 없습니다. 배경과 transform은 속성마다 따로 결정됩니다.
4. 자식 컴포넌트의 p 요소
모듈의 .card p { color: red; }를 카드 부모에 적용했고, 자식 컴포넌트가 별도의 color 규칙 없이 p를 돌려줍니다. 카드 아래의 이 p에도 빨강이 적용될까요? React 파일을 나눈 것이 결과를 바꾸는지 설명해 보세요.
해설 보기
빨강이 적용됩니다. 지역화된 card 클래스 아래의 p라는 후손 선택자가 실제 DOM의 해당 요소를 선택합니다. 자식 컴포넌트의 소스 파일이 따로 있어도, DOM을 새로 격리하지는 않습니다.
5. 전역 규칙과의 우선순위
같은 작성자, 같은 layer의 전역 .toolbar button { background-color: black; }이 모듈보다 먼저 로드되었고, 버튼은 .toolbar의 후손입니다. 마우스를 올리지 않은 primary 버튼의 배경은 무엇일까요? !important, 인라인 스타일, 애니메이션은 없다고 가정합니다.
해설 보기
검정입니다. 0-1-1이 0-1-0보다 높으므로, 이 비교에서는 먼저 로드되었어도 전역 규칙이 이깁니다. 다만 마우스를 올렸을 때의 .primary:hover는 다른 선택자이므로, 이 정지 상태의 결론을 그대로 넓혀 적용하지 않습니다.
다음 글 Sass 통합에서는 Sass의 변수, 믹스인, 모듈 시스템을 Next.js 프로젝트에 본격적으로 통합하는 방법을 알아봅니다.