[MGS 3기 - 9일차] SCSS

박철연·2022년 4월 21일
0

MGS STFE 3기

목록 보기
9/35

오늘까지 CSS 심화 온라인 강의 일정이 잡혀있었습니다! 어제부로 전체 강의 분량을 모두 커버했기 때문에, 오늘은 SCSS 강의를 위주로 내용을 정리했습니다.

SCSS

SCSS의 개념

CSS는 HTML과 마찬가지로 접근하기가 쉽지만, 작업이 고도화될수록 예상치 못한 불편함이 생겨나게 됩니다.

프로젝트가 커질 수록, 불필요한 선택자가 많아지기도 하고, 연산 기능에도 한계가 있어 원하는 결과를 얻는게 복잡해집니다. 프로그래밍 언어처럼 구문이 없기 때문에 정교한 작업을 하는 것도 쉽지 않습니다.

그러나 웹 개발을 할 때에는, 표준 CSS만 브라우저에서 동작할 수 있기 때문에 다른 선택권이 사실상 없었습니다.

이를 해결하기 위해 나온것이 바로 SCSS, 즉 CSS 전(예비)처리기입니다.

쉽게 말해, CSS가 동작하기 전에 사용하는 기능들을 모아둔 확장 프로그램의 개념으로 볼 수 있습니다.

여전히 웹 상에서는 일반 CSS가 동작하지만, 실제로 코드를 작성하는 개발자는 SCSS의 도움을 받아 기존 CSS의 한계점을 보완할 수 있게 되는 것입니다.

SCSS의 적용

이미 언급한 것과 같이, SCSS로 작성된 파일이 직접 브라우저에서 읽히는 것은 아닙니다. 브라우저는 오로지 일반 CSS 파일만 읽어줍니다.

그래서, SCSS로 스타일링을 작성하고 나면, 반드시 컴파일 과정을 거쳐야만 합니다.

SCSS에도 여러 가지 종류가 존재하는데, 대부분 컴파일러와 함께 사용해서 실제 웹 개발에 적용할 수 있다고 볼 수 있겠습니다.

Why Sass?

여러 종류의 SCSS

일반적으로 자주 사용되는 전처리기에는 Less, Sass(SCSS), Stylus가 있습니다.

Less 같은 경우에는 진입 장벽이 매우 낮고, 많은 개발자들이 이미 사용하고 있어 관련 커뮤니티를 참고하기에도 좋지만, 다른 전처리기와 비교했을 때 기능적 한계가 어느 정도 있는 편입니다.

Stylus 같은 경우는 less와 비교했을 때 깔끔하고 좀 더 세련됐으며 기능도 다양하다고 할 수 있습니다.

하지만 비교적 늦게 나왔기 때문에 인지도가 낮아 사용하는 개발자들의 수가 많지 않고, 전처리기 자체의 성숙도도 다소 떨어집니다.

Sass(SCSS)는 언급한 두 가지 전처리기의 장점을 모두 가집니다.

문법은 Sass가 Stylus와 비슷하고, SCSS는 Less와 비슷하며, Sass와 SCSS는 하나의 컴파일러로 모두 컴파일 가능합니다.

또한, 2006년부터 시작하여 가장 오래된 CSS 확장 언어이며 그만큼 높은 성숙도와 많은 커뮤니티를 가지고 있고 기능도 훌륭합니다.

Sass와 SCSS의 차이점

Sass(Syntactically Awesome Style Sheets)의 3버전에서 새롭게 등장한 SCSS는 CSS 구문과 완전히 호환되도록 새로운 구문을 도입해 만든 Sass의 모든 기능을 지원하는 CSS의 상위집합(Superset) 입니다.

즉, SCSS는 CSS와 거의 같은 문법으로 Sass 기능을 지원한다고 볼 수 있겠습니다.

문법적으로 유사하나, 중괄호와 세미콜론의 사용 유무에서 차이가 납니다.

/* Sass */

.list
  width: 100px
  float: left
  li
    color: red
    background: url("./image.jpg")
    &:last-child
      margin-right: -10px

/* SCSS */

.list {
  width: 100px;
  float: left;
  li {
    color: red;
    background: url("./image.jpg");
    &:last-child {
      margin-right: -10px;
    }
  }
}

이외에도, 나중에 소개할 Mixin 기능을 작성할 때에도 차이점을 보입니다.

결론적으로, Sass와 SCSS는 단순한 몇 가지를 제외하면 거의 차이가 없지만 복잡한 문장이 될 경우 여러 환경에 따른 장단점이 있을 수 있습니다.

Sass는 좀 더 간결하고 작성하기 편리하며, {}나 ;를 사용하지 않아도 되므로 코드가 훨씬 깔끔해집니다.

SCSS는 인라인 코드(한 줄 작성)를 작성할 수 있고, CSS와 유사한 문법을 가지기 때문에 코드 통합이 훨씬 쉽습니다.

이렇게 몇몇 장단점이 있기 때문에 회사나 팀에서 원하는 방식을 사용해야 하거나, 개인 취향에 따라서 선택할 수 있습니다.

Sass(SCSS) 사용법

Sass(SCSS)는 웹에서 직접 동작할 수 없습니다. 어디까지나 최종에는 표준 CSS로 동작해야 하며, 우리는 전처리기로 작성 후 CSS로 컴파일해야 합니다.

일반적인 자바스크립트 개발 환경(Node.js)에서 사용할 만한 몇가지 방법을 정리해보았습니다.

SassMeister

간단한 Sass 코드는 컴파일러를 설치하는게 부담될 수 있습니다. 그럴 경우에는 SassMeister를 사용할 수 있습니다.

페이지 접속 후 바로 Sass나 SCSS 문법으로 코딩하면 CSS로 실시간 변환됩니다.

HTML를 작성하여 적용된 결과를 보거나 Sass 버전 설정 등 여러 환경 설정들을 지원하기 때문에, Sass 학습에도 유용합니다.

node-sass

node-sass는 Node.js를 컴파일러인 LibSass에 바인딩한 라이브러리 입니다.

NPM으로 전역 설치하여 사용합니다.

$ node-sass [옵션] <입력파일경로> [출력파일경로]

위 명령어로 사용할 수 있습니다. 컴파일하려는 파일의 경로와 컴파일된 파일이 저장될 경로를 설정합니다.

옵션을 적용할 수도 있습니다.

예를 들어, 옵션으로 --watch 혹은 -w를 입력하면, 런타임 중 파일을 감시하여 저장 시 자동으로 변경 사항을 컴파일할 것입니다.

Parcel

웹 애플리케이션 번들러 Parcel은 굉장히 단순하게 컴파일할 수 있습니다. (물론 Webpack이나 snowpack과 같은 다른 번들러도 가능합니다.)

위에서 언급한 node-sass만 잘 설치되어 있다면, parcel을 써서 단지 HTML 문서에 sass 파일을 연결하는 것만으로 번들링을 마쳐줍니다.

컴파일된 sass 파일은 dist 폴더 내에서 확인할 수 있습니다.

SCSS 문법

주석

CSS 주석은 "/**/" 입니다. Sass(SCSS)는 JavaScript처럼 두 가지 스타일의 주석을 사용합니다.

// 컴파일되지 않는 주석
/* 컴파일되는 주석 */

단, Sass의 경우 컴파일되는 여러 줄 주석을 사용할 때 각 줄 앞에 을 붙여야 하고, 중요한 것은 의 라인을 맞춰줘야 합니다.

/* 컴파일되는
 * 여러 줄
 * 주석 */

// Error
/* 컴파일되는
* 여러 줄
    * 주석 */

중첩(Nesting)

Sass는 중첩 기능을 사용할 수 있습니다.

상위 선택자의 반복을 피하고 좀 더 편리하게 복잡한 구조를 작성할 수 있습니다.

.section {
  width: 100%;
  .list {
    padding: 20px;
    li {
      float: left;
    }
  }
}

위 코드 블럭은 아래와 같은 일반 CSS 코드로 컴파일됩니다. 한 번 비교해보세요.

.section {
  width: 100%;
}
.section .list {
  padding: 20px;
}
.section .list li {
  float: left;
}

Ampersand (상위 선택자 참조)

중첩 안에서 & 키워드는 상위(부모) 선택자를 참조하여 치환합니다.

.btn {
  position: absolute;
  &.active {
    color: red;
  }
}

.list {
  li {
    &:last-child {
      margin-right: 0;
    }
  }
}

.active 부분을 살펴보면, ampersand 뒤에 선택자가 붙은 것이 보입니다. 이는 결과적으로 .btn.active 선택자에 해당하는 요소에 스타일링을 적용할 것입니다.

& 키워드가 참조한 상위 선택자로 치환되는 것이기 때문에 다음과 같이 응용할 수도 있습니다.

.fs {
  &-small { font-size: 12px; }
  &-medium { font-size: 14px; }
  &-large { font-size: 16px; }
}

@at-root (중첩 벗어나기)

중첩에서 벗어나고 싶을 때 @at-root 키워드를 사용합니다.

중첩 안에서 생성하되 중첩 밖에서 사용해야 경우에 유용합니다.

.list {
  $w: 100px;
  $h: 50px;
  li {
    width: $w;
    height: $h;
  }
  @at-root .box {
    width: $w;
    height: $h;
  }
}

위 코드 블럭은 아래와 같이 컴파일 됩니다.

.list li {
  width: 100px;
  height: 50px;
}
.box {
  width: 100px;
  height: 50px;
}

중첩된 속성

CSS 코드를 작성하다 보면 font-, border- 와 같이 중복되는 속성 이름이 반복해서 사용됩니다. 이를 동일한 네임 스페이스를 가진다고 합니다.

이러한 동일 네임스페이스들을 가진 속성들을, SCSS 문법으로 효율적으로 작성 가능합니다.

.box {
  font: {
    weight: bold;
    size: 10px;
    family: sans-serif;
  };
  margin: {
    top: 10px;
    left: 20px;
  };
  padding: {
    bottom: 40px;
    right: 30px;
  };
}

변수(Variables)

반복적으로 사용되는 값을 변수로 지정할 수 있습니다. 변수 이름 앞에는 항상 $를 붙입니다.

$color-primary: #e96900;
$url-images: "/assets/images/";
$w: 200px;

.box {
  width: $w;
  margin-left: $w;
  background: $color-primary url($url-images + "bg.jpg");
}

변수 유효범위(Variable Scope)

변수는 사용 가능한 유효범위가 있습니다. 일반적으로 선언된 블록({}) 내에서만 유효범위를 가집니다.

.box1 {
  $color: #111;
  background: $color;
}
// Error
.box2 {
  background: $color;
}

위 코드 블럭에서, 변수 $color는 .box1의 블록 안에서 설정되었기 때문에, 블록 밖의 .box2에서는 사용할 수 없습니다.

변수 재 할당(Variable Reassignment)

변수에 변수를 할당하는 것도 가능합니다.

$red: #FF0000;
$blue: #0000FF;

$color-primary: $blue;
$color-danger: $red;

.box {
  color: $color-primary;
  background: $color-danger;
}

!global (전역 설정)

!global 플래그를 사용하면 변수의 유효범위를 전역(Global)로 설정할 수 있습니다.

.box1 {
  $color: #111 !global;
  background: $color;
}
.box2 {
  background: $color;
}

다만, 변수 이름이 중복되는 경우에는 전역 설정을 한 변수가 동일한 이름을 가진 다른 변수를 덮어쓰게 됩니다.

!default (초깃값 설정)

!default 플래그는 할당되지 않은 변수의 초깃값을 설정합니다.

즉, 할당되어있는 변수가 있다면 변수가 기존 할당 값을 사용합니다.

$color-primary: red;

.box {
  $color-primary: blue !default;
  background: $color-primary;
}

뿐만 아니라, ‘변수와 값을 설정하겠지만, 혹시 기존 변수가 있을 경우는 현재 설정하는 변수의 값은 사용하지 않겠다’는 의미로 쓸 수 있습니다.

#{} (문자 보간)

#{}를 이용해서 코드의 어디든지 변수 값을 넣을 수 있습니다.

참고로 아래의 코드 블럭에서, unquote()는 SCSS의 내장 함수로 문자에서 따옴표를 제거합니다.

$family: unquote("Droid+Sans");
@import url("http://fonts.googleapis.com/css?family=#{$family}");

가져오기(Import)

@import로 외부에서 가져온 Sass 파일은 모두 단일 CSS 출력 파일로 병합됩니다.

또한, 가져온 파일에 정의된 모든 변수 또는 Mixins 등을 주 파일에서 사용할 수 있습니다.

Sass @import는 기본적으로 Sass 파일을 가져오는데, CSS @import 규칙으로 컴파일되는 몇 가지 상황이 있습니다. 다음을 참고하세요.

  • 파일 확장자가 .css일 때
  • 파일 이름이 http://로 시작하는 경우
  • url()이 붙었을 경우
  • 미디어쿼리가 있는 경우

파일 분할(Partials)

프로젝트 규모가 커지면 파일들을 header나 side-menu 같이 각 기능과 부분으로 나눠 유지보수가 쉽도록 관리하게 됩니다.

이 경우 파일이 점점 많아지는데, 모든 파일이 컴파일 시 각각의 ~.css 파일로 나눠서 저장된다면 관리나 성능 차원에서 문제가 될 수 있겠죠.

그래서 Sass는 Partials 기능을 지원합니다.

파일 이름 앞에 _를 붙여(_header.scss와 같이) @import로 가져오면 컴파일 시 ~.css 파일로 컴파일하지 않습니다.

연산(Operations)

상대적 단위 연산

일반적으론 절댓값을 나타내는 px 단위로 연산을 하지만, 상대적 단위(%, em, vw 등)의 연산의 경우 CSS calc()로 연산해야 합니다.

width: 50% - 20px;  // 단위 모순 에러(Incompatible units error)
width: calc(50% - 20px);  // 연산 가능

나누기 연산의 주의사항

CSS는 속성 값의 숫자를 분리하는 방법으로 /를 허용하기 때문에 /가 나누기 연산으로 사용되지 않을 수 있습니다.

예를 들어, font: 16px / 22px serif; 같은 경우 font-size: 16px와 line-height: 22px의 속성값 분리를 위해서 /를 사용합니다.

아래 코드를 보면 나누기 연산자만 연산 되지 않고 그대로 컴파일됩니다.

div {
  width: 20px + 20px;  // 더하기
  height: 40px - 10px;  // 빼기
  font-size: 10px * 2;  // 곱하기
  margin: 30px / 2;  // 나누기
}

따라서 /를 나누기 연산 기능으로 사용하려면 다음과 같은 조건을 충족해야 합니다.

  • 값 또는 그 일부가 변수에 저장되거나 함수에 의해 반환되는 경우
  • 값이 ()로 묶여있는 경우
  • 값이 다른 산술 표현식의 일부로 사용되는 경우

재활용(Mixins)

Sass Mixins는 스타일 시트 전체에서 재사용 할 CSS 선언 그룹 을 정의하는 기능입니다. 약간의 Mixin(믹스인)으로 다양한 스타일을 만들어낼 수 있습니다.

선언하기(@mixin)와 포함하기(@include)의 구조로 작성합니다.

@mixin

기본적인 Mixin 선언은 아주 간단합니다. @mixin 지시어를 이용하여 스타일을 정의합니다.

자세한 문법은 아래 코드 블럭을 참조하세요.

// SCSS
@mixin 믹스인이름 {
  스타일;
}

// Sass
=믹스인이름
  스타일

@include

선언된 Mixin을 사용(포함)하기 위해서는 @include가 필요합니다. 먼저 mixin 예시를 하나 준비해보았습니다.

// SCSS
@mixin large-text {
  font-size: 22px;
  font-weight: bold;
  font-family: sans-serif;
  color: orange;
}

// Sass
=large-text
  font-size: 22px
  font-weight: bold
  font-family: sans-serif
  color: orange

위에서 선언한 Mixin을 사용해 보겠습니다.

// SCSS
h1 {
  @include large-text;
}
div {
  @include large-text;
}

// Sass
h1
  +large-text
div
  +large-text

@content

선언된 Mixin에 @content이 포함되어 있다면 해당 부분에 원하는 스타일 블록 을 전달할 수 있습니다.

이 방식을 사용하여 기존 Mixin이 가지고 있는 기능에 선택자나 속성 등을 추가할 수 있습니다.

@mixin icon($url) {
  &::after {
    content: $url;
    @content;
  }
}
.icon1 {
  // icon Mixin의 기존 기능만 사용
  @include icon("/images/icon.png");
}
.icon2 {
  // icon Mixin에 스타일 블록을 추가하여 사용
  @include icon("/images/icon.png") {
    position: absolute;
  };
}

함수(Functions)

SCSS에서는 자신의 함수를 정의하여 사용할 수 있습니다. 함수와 Mixins은 거의 유사하지만 반환되는 내용이 다릅니다.

Mixin은 위에서 살펴본 대로 지정한 스타일(Style)을 반환하는 반면,
함수는 보통 연산된(Computed) 특정 값을 @return 지시어를 통해 반환합니다.

뿐만 아니라, Mixin은 @include 지시어를 사용하는 반면, 함수는 함수이름으로 바로 사용합니다.

아래와 같은 방식으로 작성합니다.

// Mixins
@mixin 믹스인이름($매개변수) {
  스타일;
}

// Functions
@function 함수이름($매개변수) {
  @return 값
}

조건과 반복

함수도 가능하니까 조건문과 반복문도 쓸 수 있지 않을까요?

맞습니다. SCSS에서는 조건문과 반복문도 다양한 형태로 지원합니다.

if (함수)

조건의 값(true, false)에 따라 두 개의 표현식 중 하나만 반환합니다.

조건부 삼항 연산자(conditional ternary operator)와 비슷합니다.

조건의 값이 true이면 표현식 1을, 조건의 값이 false이면 표현식 2를 실행합니다.

$width: 555px;
div {
  width: if($width > 300px, $width, null);
}

변수 $width가 300px보다 크면, width 값을 유지하고, 그렇지 않으면 null 값, 즉 따로 지정하지 않습니다. 결과적으로 div의 width는 555px이 찍히겠죠?

@if (지시어)

@if 지시어는 조건에 따른 분기 처리가 가능하며, 자바스크립트의 if 문(if statements)과 유사합니다.

같이 사용할 수 있는 지시어는 @else, if가 있습니다.

참고로, 조건에 ()는 생략이 가능합니다.

// @if
@if (조건) {
  /* 조건이 참일 때 구문 */
}

// @if @else
@if (조건) {
  /* 조건이 참일 때 구문 */
} @else {
  /* 조건이 거짓일 때 구문 */
}

// @if @else if
@if (조건1) {
  /* 조건1이 참일 때 구문 */
} @else if (조건2) {
  /* 조건2가 참일 때 구문 */
} @else {
  /* 모두 거짓일 때 구문 */
}

좀 더 구체적인 예시를 코드로 작성해보았습니다.

@function limitSize($size) {
  @if $size >= 0 and $size <= 200px {
    @return 200px;
  } @else {
    @return 800px;
  }
}

div {
  width: limitSize(180px);
  height: limitSize(340px);
}

@for

@for는 스타일을 반복적으로 출력합니다. for 문과 유사합니다.

@for는 through를 사용하는 형식과 to를 사용하는 형식으로 나뉩니다.
두 형식은 종료 조건이 해석되는 방식이 다릅니다.

먼저 through를 사용하는 경우입니다.

// 1부터 3번 반복
@for $i from 1 through 3 {
  .through:nth-child(#{$i}) {
    width : 20px * $i
  }
}

// 1부터 3 직전까지만 반복(2번 반복)
@for $i from 1 to 3 {
  .to:nth-child(#{$i}) {
    width : 20px * $i
  }
}

to를 사용하는 경우는 주어진 값 직전까지만 반복해야할 경우 유용할 수 있습니다.

:nth-child()에서 특히 유용하게 사용되는 @for는 일반적으로 through를 사용하는 것이 권장됩니다.

@each

@each는 List와 Map 데이터를 반복할 때 사용합니다. for in 문과 유사합니다.

// List Data
$fruits: (apple, orange, banana, mango);

.fruits {
  @each $fruit in $fruits {
    li.#{$fruit} {
      background: url("/images/#{$fruit}.png");
    }
  }
}
profile
Frontend Developer

0개의 댓글