icon
4장 : CSS 고급 기법

CSS 전처리기 (Sass) 입문

우리는 지금까지 순수 CSS(Vanilla CSS)만으로 웹 페이지를 스타일링하고, 고급 기법들을 통해 코드의 효율성을 높이는 방법을 학습했습니다. CSS 변수와 calc() 함수는 CSS의 유연성을 크게 향상시켰지만, 여전히 순수 CSS만으로는 대규모 프로젝트에서 반복적인 작업을 줄이거나 복잡한 로직을 처리하는 데 한계가 있습니다. 예를 들어, 반복되는 CSS 속성을 줄이거나, 함수처럼 재사용 가능한 스타일 묶음을 만들거나, 선택자를 중첩하여 부모-자식 관계를 시각적으로 표현하는 것은 순수 CSS에서는 불가능합니다.

여기서 등장하는 것이 바로 CSS 전처리기(CSS Preprocessor) 입니다. CSS 전처리기는 CSS를 더욱 강력하게 만들어주는 스크립트 언어로, 우리가 작성한 코드를 컴파일(Compile)하여 브라우저가 이해할 수 있는 표준 CSS로 변환해 줍니다. 이를 통해 변수, 중첩, 믹스인(함수), 상속 등의 프로그래밍 개념을 CSS에 도입할 수 있어, 개발 생산성을 크게 높이고 유지보수성을 향상시킬 수 있습니다.

가장 널리 사용되는 CSS 전처리기에는 Sass(Syntactically Awesome Style Sheets), Less, Stylus 등이 있습니다. 이 장에서는 Sass를 중심으로, 그 중에서도 표준 CSS와 가장 유사한 문법을 가진 SCSS(Sassy CSS) 를 사용하여 CSS 전처리기의 핵심 기능을 학습할 것입니다.


전처리기란 무엇이며 왜 필요한가?

CSS 전처리기(CSS Preprocessor): CSS를 확장하는 스크립트 언어입니다. 개발자가 전처리기 문법으로 코드를 작성하면, 전처리기가 이를 일반적인 CSS 코드로 변환(컴파일)해 줍니다. 브라우저는 이 변환된 CSS 파일을 해석하여 웹 페이지를 렌더링합니다.

필요성

  1. 변수(Variables): 색상, 폰트 크기 등 자주 사용되는 값을 변수로 정의하여 한 곳에서 관리할 수 있습니다. (순수 CSS 변수보다 기능이 강력하고 이전 버전의 브라우저도 지원)
  2. 중첩(Nesting): HTML 구조처럼 CSS 선택자를 중첩하여 작성할 수 있어 코드의 가독성이 향상됩니다.
  3. 믹스인(Mixins): 반복적으로 사용되는 CSS 속성 묶음을 함수처럼 정의하여 필요할 때마다 호출하여 사용할 수 있습니다.
  4. 상속(Inheritance) / 확장(Extend): 특정 선택자의 스타일을 다른 선택자에 상속시켜 코드 중복을 줄일 수 있습니다.
  5. 부분 파일(Partials) 및 임포트(Import): CSS 파일을 여러 작은 파일로 분리하고, 필요한 곳에서 @import 규칙으로 불러와 관리할 수 있습니다. 이는 SMACSS와 같은 CSS 아키텍처를 구현하는 데 매우 유용합니다.
  6. 연산(Operations): 사칙연산은 물론, 색상 함수 등 다양한 함수를 사용하여 복잡한 계산을 수행할 수 있습니다.

Sass (SCSS 문법) 설치 및 사용 준비

Sass는 Ruby 또는 Node.js 환경에서 실행됩니다. 현대 웹 개발에서는 Node.js 기반의 node-sass 또는 sass 패키지를 사용하는 것이 일반적입니다.

설치 과정 (Node.js 및 npm 필요)

  1. Node.js 설치 확인: 터미널/명령 프롬프트에서 node -vnpm -v를 입력하여 버전이 표시되는지 확인합니다. (설치되어 있지 않다면 Node.js 공식 홈페이지에서 설치)
  2. 프로젝트 폴더 생성 및 이동:
    mkdir my-sass-project
    cd my-sass-project
  3. npm 프로젝트 초기화:
    npm init -y
    (package.json 파일이 생성됩니다.)
  4. Sass 컴파일러 설치:
    npm install -g sass # 전역 설치 (어디서든 sass 명령 사용 가능)
    # 또는
    npm install --save-dev sass # 프로젝트 내 설치 (프로젝트에서만 사용)
    여기서는 편의상 전역 설치를 권장합니다.

Sass 컴파일 명령

SCSS 파일(*.scss)을 표준 CSS 파일(*.css)로 컴파일하는 기본적인 명령입니다.

sass input.scss output.css

자동 컴파일 (Watch Mode): 파일을 저장할 때마다 자동으로 컴파일되도록 watch 모드를 사용하는 것이 개발에 훨씬 편리합니다.

sass --watch input.scss:output.css
# 예시: sass --watch scss/main.scss:css/style.css

이 명령을 실행하면 터미널은 계속 대기 상태가 되고, input.scss 파일에 변경이 감지될 때마다 output.css를 자동으로 업데이트합니다.


Sass (SCSS) 핵심 기능

이제 SCSS의 주요 기능들을 살펴봅시다.

변수 (Variables)

CSS 변수와 유사하지만, Sass 변수는 $foo: value; 형식으로 선언하며, 컴파일 시점에 값이 결정되어 브라우저 호환성을 신경 쓸 필요가 없습니다.

/* _variables.scss (보통 별도의 파일로 관리) */
$primary-color: #3498db;
$text-color: #333;
$font-stack: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
$base-padding: 15px;

/* style.scss */
body {
    font-family: $font-stack;
    color: $text-color;
    background-color: #f0f2f5;
}

.button {
    background-color: $primary-color;
    padding: $base-padding;
    color: white;
    border: none;
    border-radius: 5px;
}

중첩 (Nesting)

HTML 구조처럼 CSS 선택자를 중첩하여 작성하여 코드의 계층 구조를 명확하게 보여줍니다.

/* SCSS */
nav {
    ul {
        margin: 0;
        padding: 0;
        list-style: none;

        li {
            display: inline-block;
            margin-right: 10px;

            a {
                display: block;
                padding: 5px 10px;
                text-decoration: none;
                color: #333;

                &:hover { /* 부모 선택자(&) */
                    background-color: lightgray;
                }
            }
        }
    }
}

컴파일된 CSS:

/* CSS */
nav ul {
    margin: 0;
    padding: 0;
    list-style: none;
}
nav ul li {
    display: inline-block;
    margin-right: 10px;
}
nav ul li a {
    display: block;
    padding: 5px 10px;
    text-decoration: none;
    color: #333;
}
nav ul li a:hover {
    background-color: lightgray;
}
  • & (부모 선택자 참조): 중첩된 규칙 내에서 부모 선택자를 참조할 때 사용합니다. :hover, ::before 등 가상 클래스/요소나 클래스 조합 시 유용합니다.

믹스인 (Mixins)

재사용 가능한 CSS 코드 블록을 정의하는 기능입니다. 함수처럼 인자를 받아 동적인 스타일을 생성할 수도 있습니다. @mixin으로 정의하고, @include로 사용합니다.

/* SCSS */
@mixin flex-center {
    display: flex;
    justify-content: center;
    align-items: center;
}

@mixin border-radius($radius) {
    -webkit-border-radius: $radius;
    -moz-border-radius: $radius;
    border-radius: $radius;
}

.container {
    @include flex-center; /* 믹스인 사용 */
    min-height: 200px;
    background-color: #f9f9f9;
}

.card {
    width: 200px;
    height: 150px;
    background-color: white;
    @include border-radius(10px); /* 인자와 함께 믹스인 사용 */
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}

컴파일된 CSS:

/* CSS */
.container {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 200px;
    background-color: #f9f9f9;
}

.card {
    width: 200px;
    height: 150px;
    background-color: white;
    -webkit-border-radius: 10px;
    -moz-border-radius: 10px;
    border-radius: 10px;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
  • @mixin은 특히 벤더 프리픽스(vendor prefix)가 필요한 속성(transform, transition, animation 등)을 효율적으로 관리하는 데 매우 유용합니다.

상속/확장 (@extend)

하나의 선택자의 CSS 속성을 다른 선택자에게 "상속"시키는 기능입니다. 코드 중복을 줄이는 데 사용됩니다.

/* SCSS */
.message {
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
}

.message--success {
    @extend .message; /* .message의 모든 스타일을 상속받음 */
    background-color: #d4edda;
    color: #155724;
    border-color: #c3e6cb;
}

.message--error {
    @extend .message;
    background-color: #f8d7da;
    color: #721c24;
    border-color: #f5c6cb;
}

컴파일된 CSS

/* CSS */
.message, .message--success, .message--error { /* 선택자들이 그룹화됨 */
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
}

.message--success {
    background-color: #d4edda;
    color: #155724;
    border-color: #c3e6cb;
}

.message--error {
    background-color: #f8d7da;
    color: #721c24;
    border-color: #f5c6cb;
}
  • @extendplaceholder 선택자(%placeholder-name)와 함께 사용될 때 더 강력합니다. placeholder@extend될 때만 CSS로 컴파일되어 불필요한 클래스를 생성하지 않습니다.

    %common-button-style {
        padding: 10px 15px;
        border-radius: 5px;
        cursor: pointer;
    }
    
    .btn-submit {
        @extend %common-button-style;
        background-color: blue;
        color: white;
    }

부분 파일 (Partials) 및 임포트 (@import)

CSS 파일을 여러 개의 작은 모듈로 분리하여 관리하고, @import 규칙을 사용하여 다른 SCSS 파일로 가져올 수 있습니다. 부분 파일은 파일명 앞에 언더스코어(_)를 붙여서 명명합니다 (예: _variables.scss). 언더스코어는 해당 파일이 독립적으로 CSS로 컴파일되지 않고, 다른 SCSS 파일에 임포트될 용도로 사용됨을 의미합니다.

/* _variables.scss */
$font-size-base: 16px;
$color-main: #3498db;

/* _mixins.scss */
@mixin button-style {
    padding: 10px 20px;
    border-radius: 5px;
}

/* style.scss (메인 파일) */
@import 'variables'; /* _variables.scss 파일을 가져옴 (언더스코어 생략 가능) */
@import 'mixins';   /* _mixins.scss 파일을 가져옴 */

body {
    font-size: $font-size-base;
    color: $color-main;
}

.my-button {
    @include button-style;
    background-color: $color-main;
    color: white;
}

이러한 모듈화는 SMACSS와 같은 CSS 아키텍처를 구현하는 데 매우 효과적입니다.

연산 (Operations)

Sass는 숫자, 색상 등 다양한 타입의 값에 대한 산술 연산을 지원합니다.

/* SCSS */
$base-font-size: 16px;
$line-height: 1.5;
$margin-unit: 10px;

body {
    font-size: $base-font-size;
    line-height: $line-height;
}

.box {
    width: 100px + 50px; /* 150px */
    height: 200px / 2; /* 100px */
    padding: $margin-unit * 2; /* 20px */
    margin: $margin-unit - 5px; /* 5px */
    font-size: $base-font-size * 1.2; /* 19.2px */
}

/* 색상 연산 */
$color-primary: #3498db;
$color-darker: darken($color-primary, 10%); /* 더 어두운 색상 */
$color-lighter: lighten($color-primary, 15%); /* 더 밝은 색상 */

.header {
    background-color: $color-darker;
    color: white;
}
.footer {
    background-color: $color-lighter;
}

실습: Sass로 반응형 블로그 헤더 만들기

이전 장에서 학습한 아키텍처와 방법론, 그리고 이번 장에서 배운 Sass 기능을 활용하여 반응형 헤더 컴포넌트를 만들어 봅시다.

  1. 프로젝트 폴더 구조

    `web-dev-practice/
    ├── sass_header.html
    └── scss/
        ├── _variables.scss
        ├── _mixins.scss
        ├── _base.scss
        ├── _layout.scss
        ├── _header.scss
        └── style.scss (메인 SCSS 파일)
    └── css/
        └── style.css (컴파일될 파일)
  2. sass_header.html 파일 작성

    sass_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">
            <div class="main-header__logo">
                <a href="#">My Blog</a>
            </div>
            <nav class="main-header__nav main-nav">
                <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>
                <button class="main-nav__toggle">
                    <span></span>
                    <span></span>
                    <span></span>
                </button>
            </nav>
        </header>
    
        <main class="content-wrapper">
            <h1>반응형 웹과 Sass의 조화</h1>
            <p>이 페이지는 Sass를 이용하여 헤더를 만들었습니다. 화면 크기를 줄여보세요!</p>
            <p>Sass의 변수, 중첩, 믹스인 등의 기능으로 더욱 효율적인 CSS 개발이 가능합니다.</p>
            <div style="height: 1000px; background-color: #ecf0f1; padding: 20px;">
                페이지 스크롤 영역
            </div>
        </main>
        <footer>
            <p>&copy; 2025 Sass Practice.</p>
        </footer>
    </body>
    </html>
  3. scss/style.scss 파일 작성 (메인 SCSS 파일)

    scss/style.scss
    // style.scss (메인 파일)
    
    // 1. 변수 정의
    @import 'variables';
    
    // 2. 믹스인 정의
    @import 'mixins';
    
    // 3. 기본 스타일 (Base)
    @import 'base';
    
    // 4. 레이아웃 스타일 (Layout)
    @import 'layout';
    
    // 5. 모듈/컴포넌트 스타일 (Header 모듈)
    @import 'header';
  4. scss/_variables.scss 파일 작성

    scss/_variables.scss
    // _variables.scss
    
    $primary-color: #3498db;
    $text-color: #333;
    $bg-color: #f0f2f5;
    $header-bg: #2c3e50;
    $nav-link-color: white;
    $nav-link-hover-color: #1abc9c; // 터쿼이즈
    
    $mobile-breakpoint: 768px; // 모바일에서 태블릿으로 넘어가는 기준
    $tablet-breakpoint: 1024px; // 태블릿에서 데스크톱으로 넘어가는 기준
  5. scss/_mixins.scss 파일 작성

    scss/_mixins.scss
    // _mixins.scss
    
    @mixin desktop-only {
        @media screen and (min-width: #{$tablet-breakpoint}) {
            @content; // 이 믹스인이 사용되는 곳의 CSS 내용을 포함
        }
    }
    
    @mixin tablet-and-up {
        @media screen and (min-width: #{$mobile-breakpoint}) {
            @content;
        }
    }
    
    @mixin mobile-only {
        @media screen and (max-width: #{$mobile-breakpoint - 1}) {
            @content;
        }
    }
    
    // Flexbox 중앙 정렬 믹스인
    @mixin flex-center {
        display: flex;
        justify-content: center;
        align-items: center;
    }
    • #{$변수이름}: 미디어 쿼리 등에서 변수 값을 문자열로 사용해야 할 때 (인터폴레이션) 사용합니다.
  6. scss/_base.scss 파일 작성

    scss/_base.scss
    // _base.scss
    
    * {
        box-sizing: border-box;
        margin: 0;
        padding: 0;
    }
    
    body {
        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        background-color: $bg-color;
        color: $text-color;
        line-height: 1.6;
    }
    
    a {
        text-decoration: none;
        color: $text-color;
    }
    
    ul {
        list-style: none;
    }
    
    img {
        max-width: 100%;
        height: auto;
        display: block;
    }
  7. scss/_layout.scss 파일 작성

    scss/_layout.scss
    // _layout.scss
    
    .content-wrapper {
        max-width: 1200px;
        margin: 20px auto;
        padding: 20px;
        background-color: white;
        border-radius: 8px;
        box-shadow: 0 2px 10px rgba(0,0,0,0.1);
    }
    h1 {
        color: $primary-color;
        margin-bottom: 15px;
    }
    p {
        margin-bottom: 10px;
    }
    
    footer {
        text-align: center;
        background-color: #ccc;
        padding: 15px;
        margin-top: 50px;
        color: #555;
    }
  8. scss/_header.scss 파일 작성 (BEM + Sass 기능)

    scss/_header.scss
    // _header.scss
    
    .main-header {
        background-color: $header-bg;
        padding: 15px 20px;
        display: flex;
        justify-content: space-between;
        align-items: center;
        box-shadow: 0 2px 5px rgba(0,0,0,0.2);
    
        &__logo { // .main-header__logo
            a {
                color: $nav-link-color;
                font-size: 1.8em;
                font-weight: bold;
                transition: color 0.2s ease;
                &:hover {
                    color: $nav-link-hover-color;
                }
            }
        }
    
        &__nav { // .main-header__nav (BEM 요소인 동시에 .main-nav 모듈의 시작)
            .main-nav__list {
                display: flex; // 기본은 가로 메뉴
                gap: 25px;
    
                .main-nav__item { // .main-nav__item
                    .main-nav__link { // .main-nav__link
                        color: $nav-link-color;
                        font-weight: 500;
                        padding: 8px 0;
                        transition: color 0.2s ease;
                        &:hover {
                            color: $nav-link-hover-color;
                        }
                    }
                }
            }
    
            .main-nav__toggle { // .main-nav__toggle (모바일 햄버거 메뉴 버튼)
                display: none; // 기본은 숨김
                background: none;
                border: none;
                cursor: pointer;
                padding: 10px;
                span {
                    display: block;
                    width: 25px;
                    height: 3px;
                    background-color: $nav-link-color;
                    margin: 5px 0;
                    transition: all 0.3s ease;
                }
            }
        }
    
        // 모바일 사이즈 이하에서 내비게이션 변경
        @include mobile-only { // 믹스인 사용
            flex-direction: column; // 세로 정렬
            align-items: flex-start;
            padding: 10px 20px;
    
            .main-header__logo {
                width: 100%;
                text-align: center;
                margin-bottom: 10px;
            }
    
            .main-header__nav {
                width: 100%;
                text-align: center;
    
                .main-nav__list {
                    flex-direction: column; // 메뉴 세로 정렬
                    width: 100%;
                    gap: 0;
                    // display: none; /* JavaScript로 토글될 예정 */
    
                    .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 {
                            padding: 12px 0;
                            width: 100%;
                            &:hover {
                                background-color: rgba(255, 255, 255, 0.1);
                            }
                        }
                    }
                }
    
                .main-nav__toggle {
                    display: block; // 햄버거 메뉴 버튼 표시
                    position: absolute;
                    right: 20px;
                    top: 15px;
                }
            }
        }
    
        // 특정 조건에서 토글 메뉴 활성화 (JavaScript와 연동)
        &.nav-active {
            .main-header__nav {
                .main-nav__list {
                    display: flex; // 모바일에서 숨겨진 메뉴를 보이도록
                }
                .main-nav__toggle {
                    span:nth-child(1) { transform: translateY(8px) rotate(45deg); }
                    span:nth-child(2) { opacity: 0; }
                    span:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }
                }
            }
        }
    }
    • 주의: JavaScript로 .main-nav__listdisplay: none;을 토글하는 기능을 추가해야 모바일 햄버거 메뉴가 작동합니다. 이 예시에서는 CSS만으로 반응형 레이아웃 변화를 보여주는 데 중점을 둡니다.
  9. Sass 컴파일 및 결과 확인

    • 프로젝트 루트 폴더(my-sass-project)에서 터미널/명령 프롬프트를 열고 다음 명령을 실행합니다:
      sass --watch scss/style.scss:css/style.css
    • scss/style.scss 및 관련 파일들을 저장할 때마다 css/style.css 파일이 자동으로 업데이트되는 것을 확인합니다.
    • sass_header.html 파일을 웹 브라우저에서 열어봅니다.
    • 브라우저 창의 너비를 조절해 보세요.
      • 데스크톱/태블릿 크기에서는 내비게이션 메뉴가 가로로 보일 것입니다.
      • 모바일 크기(767px 이하)에서는 헤더가 세로로 정렬되고, 내비게이션 메뉴가 사라지며 (현재 CSS 상으로는 보이지만, 실제로는 JavaScript로 숨김 처리), 햄버거 메뉴 버튼이 나타나는 것을 볼 수 있습니다.

이번 장에서는 CSS 코드의 생산성과 유지보수성을 혁신적으로 향상시키는 도구인 CSS 전처리기(Preprocessor) 에 대해 학습했습니다. 특히 가장 널리 사용되는 Sass(SCSS 문법) 를 중심으로 다음 핵심 기능들을 익혔습니다.

  • $변수: 재사용 가능한 값을 정의하여 일관된 디자인과 손쉬운 스타일 변경을 가능하게 합니다.
  • 중첩 ({} 내부 선택자, & 부모 참조): HTML 구조처럼 CSS 선택자를 중첩하여 작성하여 코드의 가독성을 높입니다.
  • @mixin@include: 반복적으로 사용되는 CSS 코드 블록을 함수처럼 정의하고 재사용하여 코드 중복을 줄입니다.
  • @extend: 특정 선택자의 스타일을 다른 선택자에 상속시켜 코드 효율성을 높입니다.
  • 부분 파일 (_파일.scss)과 @import: CSS 파일을 모듈화하여 관리하고, 필요한 곳에서 불러와 체계적인 프로젝트 구조를 만듭니다.
  • 연산: 숫자 및 색상 값에 대한 다양한 연산을 통해 동적인 스타일을 생성합니다.

이러한 Sass의 기능들은 순수 CSS의 한계를 극복하고, 대규모 프로젝트에서 CSS를 더 체계적이고 효율적으로 작성할 수 있도록 돕습니다. 이제 여러분은 CSS 개발 워크플로우를 한 단계 더 발전시킬 준비가 되었습니다.

이것으로 4장 "CSS 고급 기법"의 모든 내용을 마칩니다. 여러분은 이제 CSS를 이용해 동적인 효과를 부여하고, 변수와 계산식을 활용하며, 체계적인 방법론과 전처리기를 적용하여 더 효율적이고 관리하기 쉬운 CSS 코드를 작성할 수 있게 되었습니다.