Sass로 반응형 블로그 헤더 만들기
Sass 변수·믹스인·부분 파일·BEM을 조합해 반응형 블로그 헤더를 구현하고 컴파일 결과를 확인합니다.
실습: Sass로 반응형 블로그 헤더 만들기
이번 장에서 배운 Sass module, 변수, 믹스인과 BEM을 조합해 반응형 헤더 컴포넌트를 만들어 봅시다. 이 실습은 JavaScript 상호작용 없이, 화면 폭에 따라 메뉴 배치가 바뀌는 CSS 레이아웃에 집중합니다.
먼저 실습 폴더를 만들고 Sass를 프로젝트의 개발 의존성으로 설치합니다. 이후 npx sass는 이 로컬 버전을 사용하며, 생성된 lockfile이 실습 환경의 버전을 고정합니다.
mkdir web-dev-practice
cd web-dev-practice
npm init -y
npm install --save-dev sasssass_header.html 파일 작성<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sass 반응형 헤더 실습</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<header class="main-header">
<a href="#" class="main-header__logo">My Blog</a>
<nav class="main-nav" aria-label="주요 메뉴">
<ul class="main-nav__list">
<li class="main-nav__item"><a href="#" class="main-nav__link">홈</a></li>
<li class="main-nav__item"><a href="#" class="main-nav__link">카테고리</a></li>
<li class="main-nav__item"><a href="#" class="main-nav__link">글쓰기</a></li>
<li class="main-nav__item"><a href="#" class="main-nav__link">마이페이지</a></li>
</ul>
</nav>
</header>
<main class="content-wrapper">
<h1 class="page-title">반응형 웹과 Sass의 조화</h1>
<p class="content-copy">이 페이지는 Sass를 이용하여 헤더를 만들었습니다. 화면 크기를 줄여보세요!</p>
<p class="content-copy">변수, 얕은 중첩, 믹스인과 module로 역할과 의존성을 분명하게 관리할 수 있습니다.</p>
<div class="content-demo">
페이지 스크롤 영역
</div>
</main>
<footer class="site-footer">
<p>© 2026 Sass Practice.</p>
</footer>
</body>
</html>scss/style.scss 파일 작성 (entry 파일)// CSS를 출력하는 module만 순서대로 불러옵니다.
@use 'base';
@use 'layout';
@use 'header';@use로 불러온 변수와 믹스인은 현재 stylesheet 안에서만 보입니다. 따라서 style.scss가 모든 의존성을 전역으로 준비하는 대신, 각 partial이 자신에게 필요한 module을 직접 @use합니다.
style.scss
ENTRY · base, layout, header를 직접 @use
-
CSS를 출력하는 Sass module
_base.scss→_variables.scss_layout.scss→_variables.scss_header.scss→_variables.scss,_mixins.scss
-
공유 module
_mixins.scss→_variables.scss_variables.scss는 네 module이 직접 사용한다.
A → B는 A가 B를 직접 @use한다는 뜻이다. IN은 해당 module을 직접 사용하는 파일 수이며, Dart Sass는 entry인 style.scss를 css/style.css로 컴파일한다.scss/_variables.scss 파일 작성// _variables.scss
$primary-color: #3498db;
$text-color: #333;
$bg-color: #f0f2f5;
$surface-color: white;
$header-bg: #2c3e50;
$nav-link-color: white;
$nav-link-hover-color: #1abc9c; // 터쿼이즈
$mobile-breakpoint: 768px; // 767px 모바일 상한을 계산하는 기준값scss/_mixins.scss 파일 작성// _mixins.scss
@use 'variables' as vars;
$mobile-max:
vars.$mobile-breakpoint - 1px;
@mixin mobile-only {
@media screen and
(max-width: $mobile-max) {
@content;
}
}미디어 쿼리의 feature query에서는 Sass 변수와 연산식을 직접 사용할 수 있습니다. 여기서는 768px - 1px이 컴파일 시 767px이 되므로 별도의 인터폴레이션이 필요하지 않습니다.
아래 다이어그램은 Sass 믹스인이 @content와 함께 반응형 CSS로 펼쳐지는 흐름을 정리한 것입니다.
-
정의 ·
_mixins.scss@use 'variables' as vars; $mobile-max: vars.$mobile-breakpoint - 1px; @mixin mobile-only { @media screen and (max-width: $mobile-max) { @content; } }공통 미디어 조건 안에 호출 블록을 받을
@contentinsertion point를 둔다. -
호출 ·
_header.scss핵심 발췌@use 'mixins' as mix; @include mix.mobile-only { .main-header { flex-direction: column; } }@include에 전달한 같은 요소의 선언이 mixin의@content위치에 들어간다. -
출력 ·
css/style.css핵심 발췌@media screen and (max-width: 767px) { .main-header { flex-direction: column; } }변수, 연산, mixin 호출은 사라지고 브라우저가 해석할 표준 CSS만 남는다.
@content는 runtime callback이 아니라 호출 블록을 컴파일 시 삽입하는 자리다. 미디어 feature query는 SassScript 변수와 연산식을 직접 받으므로 이 예제에는 인터폴레이션이 필요하지 않다.scss/_base.scss 파일 작성// _base.scss
@use 'variables' as vars;
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: vars.$bg-color;
color: vars.$text-color;
line-height: 1.6;
}
a {
text-decoration: none;
color: inherit;
}
ul {
list-style: none;
}scss/_layout.scss 파일 작성// _layout.scss
@use 'variables' as vars;
.content-wrapper {
max-width: 1200px;
margin: 20px auto;
padding: 20px;
background-color: vars.$surface-color;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.page-title {
color: vars.$primary-color;
margin-bottom: 15px;
}
.content-copy {
margin-bottom: 10px;
}
.content-demo {
min-height: 32rem;
padding: 20px;
background-color: vars.$bg-color;
}
.site-footer {
text-align: center;
background-color: #ccc;
padding: 15px;
margin-top: 50px;
color: #555;
}scss/_header.scss 파일 작성 (BEM + Sass 기능)// _header.scss
@use 'variables' as vars;
@use 'mixins' as mix;
.main-header {
background-color: vars.$header-bg;
padding: 15px 20px;
display: flex;
justify-content: space-between;
align-items: center;
gap: 20px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
.main-header__logo {
color: vars.$nav-link-color;
font-size: 1.8em;
font-weight: bold;
transition: color 0.2s ease;
&:hover {
color: vars.$nav-link-hover-color;
}
}
.main-nav__list {
display: flex;
gap: 25px;
}
.main-nav__link {
display: block;
color: vars.$nav-link-color;
font-weight: 500;
padding: 8px 0;
transition: color 0.2s ease, background-color 0.2s ease;
&:hover {
color: vars.$nav-link-hover-color;
}
}
@include mix.mobile-only {
.main-header {
flex-direction: column;
align-items: stretch;
gap: 12px;
padding: 12px 20px;
}
.main-header__logo {
text-align: center;
}
.main-nav__list {
flex-direction: column;
gap: 0;
}
.main-nav__item {
border-top: 1px solid rgba(255, 255, 255, 0.1);
&:last-child {
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
}
.main-nav__link {
width: 100%;
padding: 12px 8px;
text-align: center;
&:hover {
background-color: rgba(255, 255, 255, 0.1);
}
}
}BEM class를 각각 최상위 규칙으로 두고, &:hover와 &:last-child처럼 상태가 분명한 경우에만 한 단계 중첩했습니다. 모바일에서도 링크는 숨기지 않고 세로로 쌓이므로 JavaScript 상태 없이 완전한 메뉴를 유지합니다.
- 프로젝트 루트 폴더(
web-dev-practice)에서 터미널/명령 프롬프트를 열고 다음 명령을 실행합니다.npx sass --watch scss/style.scss:css/style.css scss/style.scss및 관련 파일들을 저장할 때마다css/style.css파일이 자동으로 업데이트되는 것을 확인합니다.sass_header.html파일을 웹 브라우저에서 열어봅니다.- 브라우저 창의 너비를 조절해 보세요.
- 데스크톱/태블릿 크기에서는 내비게이션 메뉴가 가로로 보일 것입니다.
- 모바일 크기(
767px이하)에서는 헤더와 내비게이션 링크가 세로로 쌓이며, 모든 링크는 계속 표시됩니다.
마무리 점검
Sass 기능을 많이 사용하는 것보다 컴파일된 CSS가 예측 가능하고 관리 가능한지가 더 중요합니다. 실습을 마치기 전에 다음을 확인하세요.
- module 의존성: 각 stylesheet가 사용하는 변수와 믹스인을 자신의
@use로 명시했는가? - 선택자: BEM 규칙과 얕은 중첩이 짧고 의도한 specificity의 CSS로 컴파일되었는가?
- 출력 중복: 같은 선언이나 미디어 쿼리가 불필요하게 여러 번 생성되지 않았는가?
- 산출물: 브라우저가 SCSS가 아니라 갱신된
css/style.css를 불러오는가? - 화면 동작:
767px이하에서 헤더와 메뉴가 세로로 쌓이고, 모든 링크를 사용할 수 있는가?