SASS에서의 상속
이 글은 SASS에서의 상속에 대해 설명합니다.
실제 예제를 통해 SASS의 상속 개념을 설명하겠습니다.
YouTube Video
SASS에서의 상속
SASS에서의 상속(@extend)은 한 셀렉터의 스타일을 중복 없이 다른 셀렉터에 적용할 수 있게 하는 메커니즘입니다. 동일한 스타일이 하나로 '결합'되어 여러 요소에 적용되므로 결과 CSS의 중복이 줄어듭니다. 하지만 잘못 사용할 경우 의도치 않은 셀렉터 결합이 발생할 수 있습니다.
기초: @extend 사용 방법
아래는 .btn--primary가 .btn의 스타일을 상속받는 기본 예시입니다. @extend는 대상 셀렉터를 확장하는 지시어입니다.
1// Base button styles
2.btn {
3 padding: 0.5rem 1rem;
4 border-radius: 4px;
5 border: 1px solid #ccc;
6 background: white;
7 color: #333;
8}
9
10/* Primary button extends the base .btn */
11.btn--primary {
12 @extend .btn;
13 background: #007bff;
14 color: white;
15}@extend를 사용하면.btn--primary가.btn의 기본 스타일을 상속하고 필요한 부분만 덮어쓸 수 있습니다.
생성된 CSS
1.btn, .btn--primary {
2 padding: 0.5rem 1rem;
3 border-radius: 4px;
4 border: 1px solid #ccc;
5 background: white;
6 color: #333;
7}
8
9.btn--primary {
10 background: #007bff;
11 color: white;
12}베스트 프랙티스: 플레이스홀더(%placeholder) 사용
플레이스홀더 셀렉터(%name)는 CSS로 출력되지 않는 셀렉터입니다. 여러 컴포넌트 간에 상속을 통해 공통 스타일을 안전하게 공유하고 싶을 때 특히 널리 사용됩니다.
1// %placeholder will not be output directly
2%card-base {
3 padding: 1rem;
4 border-radius: 6px;
5 box-shadow: 0 1px 3px rgba(0,0,0,0.08);
6 background: #fff;
7}
8
9/* Components extend the placeholder */
10.card {
11 @extend %card-base;
12 border: 1px solid #eee;
13}
14
15.panel {
16 @extend %card-base;
17 border: 1px solid #ddd;
18}.card와.panel모두%card-base에서 상속을 받아 공통 스타일을 공유하면서 각자의 차별화도 할 수 있습니다.
생성된 CSS
1.card, .panel {
2 padding: 1rem;
3 border-radius: 6px;
4 box-shadow: 0 1px 3px rgba(0,0,0,0.08);
5 background: #fff;
6}
7
8.card {
9 border: 1px solid #eee;
10}
11
12.panel {
13 border: 1px solid #ddd;
14}다중 상속 (여러 @extend 사용)
여러 개의 플레이스홀더나 클래스를 동시에 상속할 수 있습니다. 스타일의 재사용성이 향상되지만, 어떤 규칙이 어떤 셀렉터와 결합되는지 추적하는 것이 중요합니다.
1%btn-base {
2 display: inline-block;
3 padding: 0.5rem 1rem;
4 border-radius: 3px;
5}
6
7%btn-large {
8 padding: 0.75rem 1.5rem;
9 font-size: 1.125rem;
10}
11
12/* Composite button that extends both placeholders */
13.btn--lg {
14 @extend %btn-base;
15 @extend %btn-large;
16 background: #222;
17 color: #fff;
18}- 아래는 버튼이 'base'와 'size' 두 개의 플레이스홀더를 상속하는 예시입니다.
.btn--lg는%btn-base와%btn-large모두를 상속하여 기본 레이아웃과 큰 사이즈를 결합합니다.
생성된 CSS
1.btn--lg {
2 display: inline-block;
3 /* %btn-large overrides the padding from %btn-base */
4 padding: 0.75rem 1.5rem;
5 border-radius: 3px;
6 font-size: 1.125rem;
7 background: #222;
8 color: #fff;
9}@extend의 동작(병합 메커니즘) 및 '셀렉터 폭발' 주의점
@extend는 일치하는 모든 셀렉터를 병합하여 출력하기 때문에 때로는 의도하지 않은 셀렉터 조합이 생길 수 있습니다.
아래 예시는 동일한 기본 클래스가 여러 곳에서 확장될 때 출력이 어떻게 증가할 수 있는지 보여줍니다.
1/* Many components extend .utility */
2/* A generic utility class */
3.utility {
4 margin-bottom: 1rem;
5}
6
7/* Nested selectors that extend .utility */
8.header {
9 @extend .utility;
10 .title {
11 font-weight: bold;
12 }
13}
14
15.footer {
16 @extend .utility;
17 .note {
18 color: #888;
19 }
20}
21
22.article {
23 @extend .utility;
24 .content {
25 line-height: 1.6;
26 }
27}
28
29.sidebar {
30 @extend .utility;
31 .section {
32 padding: 1rem;
33 }
34}- 여러 컴포넌트에서
.utility를 상속하면 셀렉터가 하나로 병합되어, 대규모 프로젝트에서는 CSS가 비대해질 수 있습니다.
생성된 CSS
1.utility,
2.header,
3.footer,
4.article,
5.sidebar {
6 margin-bottom: 1rem;
7}
8
9.header .title {
10 font-weight: bold;
11}
12
13.footer .note {
14 color: #888;
15}
16
17.article .content {
18 line-height: 1.6;
19}
20
21.sidebar .section {
22 padding: 1rem;
23}@extend와 .class vs 요소(태그) 셀렉터 — 우선순위 및 부작용
@extend는 클래스뿐만 아니라 요소 셀렉터에도 적용할 수 있습니다. 하지만 요소를 확장하면 영향 범위가 넓어져, 규칙이 의도하지 않은 곳에 적용될 위험이 높아집니다.
아래는 요소 셀렉터를 확장했을 때 발생할 수 있는 영향을 보여주는 예시입니다.
1/* Extending an element selector (not recommended) */
2h1 {
3 font-size: 2rem;
4 margin-bottom: 0.5rem;
5}
6
7/* If you extend h1, the resulting selector will include your class with h1 */
8.title {
9 @extend h1;
10 color: #333;
11}
12
13/* Output becomes:
14h1, .title { font-size: 2rem; margin-bottom: 0.5rem; }
15*/- 이 예시에서는 요소 셀렉터인
h1을 상속하면.title과h1이 동일한 스타일로 병합됩니다. - 소규모의 경우에는 편리해 보일 수 있지만, 프로젝트가 커질수록
h1의 규칙이 예상치 못하게.title과 합쳐져 스타일이 복잡해지고 유지보수성이 떨어질 수 있습니다. 따라서 스타일을 주로 클래스와 플레이스홀더 중심으로 설계하면 유지관리가 쉬워집니다.
생성된 CSS
1h1,
2.title {
3 font-size: 2rem;
4 margin-bottom: 0.5rem;
5}
6
7.title {
8 color: #333;
9}@extend와 !optional의 사용 사례
@extend에 !optional을 지정하면, 상속 대상이 존재하지 않을 때 에러를 발생시키지 않을 수 있습니다. 이 기능은 라이브러리와 같은 코드나 플레이스홀더가 조건부로 정의되는 경우에 특히 유용합니다.
아래는 존재하지 않을 수 있는 클래스를 !optional로 안전하게 상속 시도하는 예시입니다.
1/* Try to extend a class that might not exist */
2.component {
3 @extend .maybe-existing !optional;
4 padding: 1rem;
5}.maybe-existing이 존재하지 않으면 아무 일도 일어나지 않고 건너뜁니다. 확장을 안전하게 시도하고 싶을 때 이 기능을 사용할 수 있습니다.
생성된 CSS
1.component {
2 padding: 1rem;
3}@extend와 믹스인(@mixin / @include) 비교
@extend와 @mixin은 목적이 겹칠 수 있으나 결과물과 사용법이 다릅니다.
-
@extend- 생성된 CSS는 셀렉터 병합으로 중복을 줄입니다.
- 생성 후 셀렉터가 병합되기 때문에 의도치 않은 조합이 발생할 수 있습니다.
- 파라미터를 전달할 수 없습니다 (플레이스홀더 조합으로 보완 가능).
-
@mixin/@include- 호출마다 스타일이 중복 출력됩니다 (중복된 결과 발생).
- 파라미터를 전달할 수 있고, 조건문이나 루프 등 로직도 포함할 수 있습니다.
- 출력은 더 예측 가능하지만 파일 크기가 증가합니다.
아래는 동일한 버튼 스타일을 @mixin과 @extend 모두로 구현한 비교 예시입니다.
1/* Mixin approach */
2@mixin btn-styles($bg, $color) {
3 display: inline-block;
4 padding: 0.5rem 1rem;
5 background: $bg;
6 color: $color;
7 border-radius: 4px;
8}
9
10/* Use mixin */
11.btn {
12 @include btn-styles(white, #333);
13}
14
15.btn--primary {
16 @include btn-styles(#007bff, white);
17}
18
19/* Extend approach (shared placeholder) */
20%btn-base {
21 display: inline-block;
22 padding: 0.5rem 1rem;
23 border-radius: 4px;
24}
25
26.btn2 {
27 @extend %btn-base;
28 background: white;
29 color: #333;
30}
31
32.btn2--primary {
33 @extend %btn-base;
34 background: #007bff;
35 color: white;
36}@mixin은 스타일을 유연하게 삽입할 수 있고,@extend는 출력을 효율적으로 결합하므로 상황에 맞게 적절한 방식을 사용하면 됩니다.
생성된 CSS
@mixin 결과 출력
1.btn {
2 display: inline-block;
3 padding: 0.5rem 1rem;
4 background: white;
5 color: #333;
6 border-radius: 4px;
7}
8
9.btn--primary {
10 display: inline-block;
11 padding: 0.5rem 1rem;
12 background: #007bff;
13 color: white;
14 border-radius: 4px;
15}@extend 결과 출력
1.btn2,
2.btn2--primary {
3 display: inline-block;
4 padding: 0.5rem 1rem;
5 border-radius: 4px;
6}
7
8.btn2 {
9 background: white;
10 color: #333;
11}
12
13.btn2--primary {
14 background: #007bff;
15 color: white;
16}실전 가이드라인
SASS의 상속 기능은 스타일 재사용성을 높이기 위한 강력한 기능입니다. 하지만 잘못 사용하면 스타일 병합이 복잡해져 유지보수성이 떨어질 수 있습니다. 아래는 상속을 안전하고 효율적으로 사용하기 위한 주요 포인트입니다.
- 구조나 레이아웃처럼 순수하게 공통적인 컴포넌트 스타일에는 플레이스홀더를 사용하세요. 또한, 동적으로 파라미터를 지정해야 한다면
@mixin을 사용할 수 있습니다. h1등 HTML 요소를 직접 상속하는 것은 피해야 합니다. 예기치 않은 셀렉터 조합이 발생하여, 예상치 못한 CSS가 생성될 수 있습니다.- BEM 같은 네이밍 또는 명확한 접두어로 플레이스홀더 용도를 표시하면 안전한 관리가 가능합니다.
@extend는 같은 파일 내에서 사용하는 것이 더 안전합니다. 특히 대규모 프로젝트에서는 상속 관계를 추적하기 쉽게 각 컴포넌트 범위 내에서 상속을 설계하는 것이 바람직합니다.
요약
SASS의 @extend 기능은 공통 스타일을 효율적으로 재사용하고, 디자인 일관성을 보장하는 편리한 방법입니다. 하지만 셀렉터 조합이 쉽게 복잡해질 수 있으므로, 이 기능을 신중하게 제한된 범위에서 사용하는 것이 필요합니다. 공유 스타일은 플레이스홀더 셀렉터(%placeholder)로 묶고, 동적 파라미터가 필요한 부분에는 @mixin을 사용하면, 간단하고 유지관리가 쉬운 디자인을 유지할 수 있습니다.
위의 기사를 보면서 Visual Studio Code를 사용해 우리 유튜브 채널에서 함께 따라할 수 있습니다. 유튜브 채널도 확인해 주세요.