서로 다른 css 파일에 선언했지만 class name이 같아서 예상치 못한 스타일이 적용된 적이 있다.
이를 피하기 위해 class name을 엄청 길게 지었던 기억이 있다.
이 단점들을 극복하며 또 다른 장점도 갖고 있는 scss / sass를 알아보고 싶어졌다.
css는 복잡한 언어는 아니지만 작업이 크고 고도화 될수록 유지보수에 어려움이 생긴다.
해석에서 엿볼 수 있듯이 css를 보완하기 위해 나온 것이 sass와 scss이다.
추가적으로, sass v3에서 scss가 생겨났다. 즉, scss가 sass보다 뒤에 나왔으며, scss가 좀 더 넓은 범용성과 css의 호환성을 가지고 있다.
공식 레퍼런스에서도 sass보다 scss를 사용하기를 권유하고 있다.
sass와 scss의 가장 큰 차이점은 들여 쓰기+줄 바꿈 형식이냐, 중괄호+세미콜론 형식이냐 이다.
// scss
.div-block {
color: rgb(250, 222, 120);
border-radius: 2px;
}
// sass
.div-block
color: rgb(250, 222, 120)
border-radius: 2px
sass와 scss에는 css에 없는 추가 기능이 있다.
sass와 scss는 비슷하므로 아래에 코드 예제에서는 css와 scss를 비교하겠다.
주석
css에서는 주석을 /* */
로만 사용할 수 있었다.
scss에서는 한 줄 주석인 //
을 지원한다.
다만 컴파일 했을 때 css 주석인 /* */
은 남아있지만, 한 줄 주석인 //
은 사라진다.
변수 사용
자주 사용하는 색이나 폰트 등등을 변수로 지정해 재사용할 수 있다.
/* css */
body {
font-size: 16rem;
font-family: Helvetica, sans-serif;
color: #000;
}
// scss
$primary-font: Helvetica, sans-serif;
$primary-color: #000;
body {
font-size: 16rem;
font-family: $primary-font;
color: $primary-color;
}
&
이 존재한다./* css */
nav ul {
list-style: none;
/* ... */
}
nav li {
color: #000;
/* .... */
}
nav li:last-child {
text-decoration: underline;
/* ..... */
}
// scss
nav {
ul {
list-style: none;
/* ... */
}
li {
color: #000;
/* .... */
&:last-child {
text-decoration: underline;
/* ..... */
}
}
}
@use
나 @import
를 사용하여 파일을 기능별로 분할하고 다른 파일에 선언된 내용을 가져다 쓸 수 있다./* css */
body {
font-size: 16rem;
font-family: Helvetica, sans-serif;
color: #000;
}
input {
backgroud-color: #fff;
padding: 4%;
}
// scss
// _main.scss
$primary-font: Helvetica, sans-serif;
$primary-color: #000;
body {
font-size: 16rem;
font-family: $primary-font;
color: $primary-color;
}
// _component.scss
@use 'main';
.mail-box {
font-size: 100%;
font-family: main.$primary-font;
}
@mixin center($size: 12px, $color: #000) {
display: flex;
justify-content: center;
align-items: center;
font-size: $size;
color: $color;
@content;
}
.container {
@include center($color: green);
.child {
@include center(16px) {
position: relative;
top: 0;
}
}
}
위 SCSS 코드는 아래와 같이 컴파일 됨.
.container {
display: flex;
justify-content: center;
align-items: center;
font-size: 12px;
color: green;
}
.container .child {
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
color: #000;
position: relative;
top: 0;
}
/* css */
.message,
.success,
.error,
.warning {
border: 1px solid #ccc;
padding: 10px;
color: #333;
}
.success {
border-color: green;
}
.error {
border-color: red;
}
.warning {
border-color: yellow;
}
// scss
/* This CSS will print because %message-shared is extended. */
%message-shared {
border: 1px solid #ccc;
padding: 10px;
color: #333;
}
/* This CSS won't print because %equal-heights is never extended. */
%equal-heights {
display: flex;
flex-wrap: wrap;
}
.success {
@extend %message-shared;
border-color: green;
}
.error {
@extend %message-shared;
border-color: red;
}
.warning {
@extend %message-shared;
border-color: yellow;
}
아래에서부터는 scss에 대한 예제만 존재한다.
// scss
@use 'sass:math';
span {
width: math.div(100px, 200px) * 100%;
}
// if(함수)
$default-width: 400px;
.info {
width: $default-width;
background-color: if($default-width > 500, #fff, #5f5f5f);
}
// @if(조건문)
$default-width: 400px;
//공통 CSS
.info {
width: $default-width;
@if ($default-width > 500) {
// ()은 생략 가능, 조건식 안에 and, or, not 등을 사용할 수 있음
background-color: coral;
} @else {
background-color: orange;
}
}
@media screen and (max-width: xx px)
을 수십 번씩 사용했는데, 훨씬 간편한 방법으로 만들 수 있는 기능이다.// @for(반복문)
// through: 시작부터 종료조건까지 반복
@for $변수 from 시작 through 종료 {
/* 반복내용 */
}
//1~3까지 반복
@for $i from 1 through 3 {
.info:nth-child(3n + #{$i}) {
width: 50px * $i;
}
}
// to: 시작부터 종료조건 직전까지 반복
@for $변수 from 시작 to 종료 {
/* 반복내용 */
}
//1~2까지 반복
@for $i from 1 to 3 {
.info:nth-child(3n + #{$i}) {
width: 50px * $i;
}
}
// @while 은 @if(조건문)과 문법이 동일하므로 생략.
// @each(js의 map과 유사)
.list-fruits {
@each $fruit in apple, banana, mango, orange {
li.#{$fruit} {
background: url('../img/#{fruit}.png');
}
}
}
// 또는 리스트를 변수로 저장해서 사용 가능
$fruits: apple, banana, mango, orange;
.list_fruits {
@each $fruit in $fruits {
li.#{$fruit} {
background: url('../img/#{fruit}.png');
}
}
}
scss를 사용하면서 느꼈던 불편함 중에 하나는 스타일 반영 속도가 느리다는 것이다.
처음에는 렌더링에 의한 문제인 줄 알고 최적화 방법이 있나 찾아봤지만, 컴파일에 의한 문제였다.
sass(scss)는 css pre-processor(전처리기)라고도 불리는 css 스크립팅 확장 언어이기 때문에
컴파일러를 통해서 일반 css 문법으로 바꾼 뒤 적용하는 과정이 필요하다.
순서도로 표현하자면 다음과 같다.
cf) sass(scss) 컴파일 방식에 대해서 궁금하다면 Ruby를 통한 scss 컴파일 방법을 참고하면 도움이 될 것 같다.
npm install --dev sass
또는 yarn add -D sass
을 실행한다.
node-sass
는 scss를 다룰 수 있는 node program인데 현재는 sass
에 있는 dart-sass라는 패키지 사용이 권장된다.
.css 파일을 .scss로 파일명 변경한다.
이렇게 해도 scss가 적용이 되지 않는다면(프레임워크를 사용한다면 이미 아래는 적용되어 있을 가능성이 있다) webpack을 변경해야 한다.
자세한 설정은 webpack 공식 사이트를 보면 알 수 있다.
npm install --dev sass-loader
또는 yarn add -D sass-loader
을 실행한다.
sass-loader
는 webpack에 필요한 loader이다.
webpack.config.js
를 수정한다.
module.exports = {
module: {
rules: [{
...
}, {
test: /\.s[ac]ss$/i, // 여기 부분
use: [
"style-loader",
"css-loader",
{
loader: "sass-loader",
options: {
// Prefer `dart-sass`
implementation: require("sass"),
},
},
]
}]
},
}
fibers라는 패키지를 설치하면 Dart Sass의 컴파일 속도를 2배로 올릴 수 있다.
node는 기본적으로 비동기 방식을 사용하지만 동기방식으로 전환하는 방식으로 동작한다.
webpack은 기본적으로 이 패키지가 설치된 것을 전제로 동작하기 때문에 특별한 설정은 필요없다.
만일 이 패키지가 동작하는 것을 원하지 않는다면 아래처럼 수정한다.
// ...
{
loader: "sass-loader",
options: {
implementation: require("sass"),
sassOptions: {
fiber: false,
},
},
},
// ...