• [아무튼 Sass] 4. 더 멋지게 활용하기 - 흐름제어, 내장 모듈, 함수

    2022. 11. 5.

    by. 나나 (nykim)

    320x100

     

     

    프롤로그

     

    백만 년 만의 업데이트! 이제 시리즈의 마지막 글입니다 😎

    흐름제어와 함수를 통해 Sass를 더 멋진 방법으로 활용해 봅니다.

     

    아무튼 Sass 시리즈
    1. 시작하기 - Sass 개념, 컴파일러 설치
    2. 기본 작성법 익히기 - 중첩, 참조, 변수, 보간
    3. 더 편하게 CSS 다루기 - mixin, extend, 모듈화(import/use)
    4. 더 멋지게 활용하기 - 흐름제어, 내장 모듈, 함수 👈 Here!

     

    이번 글에서 다루는 것:
    - @if, @each 등의 흐름제어문
    - list 변수와 map 변수
    - 내장 모듈이 제공하는 사용하기
    - @function 로 직접 함수 만들기

     

     


     

     

    1) 흐름 제어

     

    Sass의 기본 특징들은 다 살펴봤으니 이번에는 좀더 기깔나게 활용해 볼 차례입니다.

    먼저 흐름 제어문이라고도 부르는 @At-Rules들을 살펴봅니다.

     

    @if, @each, @for, @while

    이름만 봐도 어떻게 써야 할지 감이 오는데요 😉 Sass 내에서 조건문/반복문을 위해 사용합니다.

     

     

    @if, @else

    말그대로 조건에 따라 스타일을 반환합니다. 그래서 mixin과 찰떡궁합이에요!

    인자로 뭘 받아오느냐에 따라 분기할 수 있기 때문입니다.

     

    아래는 글자를 말줄임 처리하는 믹스인입니다. 상황에 따라 1줄 또는 그 이상으로 말줄임 처리합니다.

     

    @mixin ellipsis($lines: 1) {
      @if ($lines==1) {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      } @else {
        display: -webkit-box;
        overflow: hidden;
        text-overflow: ellipsis;
        -webkit-line-clamp: $lines;
        -webkit-box-orient: vertical;
      }
    }
    .text { @include ellipsis; }
    .long-text { @include ellipsis(3); }

     

    $lines라는 인자가 뚫려있고 그 값에 따라 다른 스타일을 적용해주고 있습니다.

    인자가 들어오지 않는다면 기본값인 1이 들어왔다고 칠 거고요.

    따라서 .text 는 첫 번째 조건(@if 아래)의 스타일을 적용받고, .long-text는 두 번째 조건(@else 아래)의 스타일을 적용받습니다.

     

    @else 문 없이 @if 문만 써도 되고, @else if로 추가 분기를 하는 것도 가능합니다.

     

     

    See the Pen Untitled by nanalike (@nykim_) on CodePen.

     

     

    물론 이렇게 쓰지 않고 믹스인을 두 개 만들어서 각각 호출해도 되긴 합니다.

    하지만 사실상 거의 유사한 스타일이기 때문에 같은 이름 아래에서 분기하는 게 좀 더 관리하기 편합니다.

    뭣보다... 믹스인 이름을 하나만 외워도 됩니다 😽

     

     

     

     

    @for

    특정 숫자 범위 내에서 반복시킬 때 사용합니다.

    현재 해당하는 인덱스(주로 $i로 표현)를 활용해야 할 때 요긴합니다.

     

    예를 들어 아이템 10개의 그림자가 겹쳐지는데 역순으로 z-index가 높아야 하는 상황입니다.

    .item:nth-child(1) { z-index: 10; }
    .item:nth-child(2) { z-index: 9; }
    .item:nth-child(3) { z-index: 8; }
    .item:nth-child(4) { z-index: 7; }
    .item:nth-child(5) { z-index: 6; }
    // ...

     

    이런 식으로 한땀한땀 정성스럽게 z-index를 지정해줘도 되지만 귀찮으니까 반복문을 돌려버리죠!

    nth-child가 1에서 하나씩 올라가고 그에 따라 z-index는 하나씩 내려가기 때문에 1부터 10까지 반복하면 됩니다.

    @for {반복자} from {시작} through {끝} 으로 사용합니다.

     

    .item {
      $_length: 10;
      $_color: #b2c0d5;
    
      @for $i from 1 through $_length {
        &:nth-child(#{$i}) {
          z-index: ($_length + 1) - $i;
          background-color: darken($_color, $i * 5%);
        }
      }
    }

     

    See the Pen Untitled by nanalike (@nykim_) on CodePen.

     

     

     

    :nth-child와 $i를 결합해 귀찮은 반복 노가다를 손쉽게 끝내버렸습니다 🤟

    그리고 color.scale()라는 걸 통해 각각의 배경색도 다르게 줬는데요, 이러한 Sass 함수에 대해서도 잠시 후 다뤄봅니다.

     

    @for 반복문에는 through 대신 to를 쓰는 방법도 있는데, 이는 끝-1 만큼만 반복합니다.
    1) @for $i from 1 through 5 = 1~5만큼 반복
    2) @for $i from 1 to 5 = 1~4만큼 반복

     

     

     

    @each

    주어진 목록 내에서 반복시킬 때 사용합니다. list, map 변수와 함께 쓰면 더욱 좋아요.

     

    Sass에서 변수는 $변수명: 값;으로 선언해 다양한 값을 담을 수 있다! 고 했는데요,

    그중에는 좀 특이하게 생긴 list와 map이 있습니다.

     

    우선 요 두 변수에 대해 짚고 넘어가 봅니다.

    먼저 list는 말그대로 하나의 변수에 여러 개의 값을 담고 있는 형태입니다. 배열이랑 비슷해요!

    // 리스트
    $list: facebook, youtube, instagram;

     

    대충 어떻게 써야할지 감이 오지 않나요?!

    안에 든 것들을 꺼내서 반복시키기 딱 좋게 생겼습니다 🤓

     

     

    주어진 목록 내에서 반복시킬 때는 @each를 사용한다고 했는데,

    @each {현재 요소를 가리키는 이름} in {변수 이름} 형태로 사용합니다.

     

    .sns {
      $_list: facebook, youtube, instagram;
    
      @each $sns in $_list {
        &.#{$sns} {
          background-image: url(./img/icon-#{$sns}.png);
        }
      }
    }
    
    /*
     * 출력결과:
     
     .sns.facebook { background-image: url(./img/icon-facebook.png); }
     .sns.youtube { background-image: url(./img/icon-youtube.png); }
     .sns.instagram { background-image: url(./img/icon-instagram.png); }
    
    */

     

     

    한편 map 변수는 어떻게 생겼을까요?

    얘는 key와 value가 한 쌍을 이루는 형태입니다. JSON이랑 비슷하죠!

     

    // 맵
    $_map: (
      'facebook': #d3e6f3,
      'instagram': #e9e9e9,
      'youtube': #ffe0e0
    );

     

    얘는 key 뿐만 아니라 value 값도 담고 있기 때문에 더 복잡한 활용도 가능해요.

    마찬가지로 @each문과 찰떡인데, value가 있기 때문에 @each {현재 요소}, {현재 요소의 값} in {변수} 형태로 사용합니다.

     

    .sns {
      $_map: (
        'facebook': #d3e6f3,
        'instagram': #e9e9e9,
        'youtube': #ffe0e0
      );
    
      @each $sns, $color in $_map {
        &.#{$sns} {
          background-color: $color;
        }
      }
    }
    
    /*
     * 출력결과:
     
     .sns.facebook { background-color: #d3e6f3; }
     .sns.instagram { background-color: #e9e9e9; }
     .sns.youtube { background-color: #ffe0e0; }
    
    */

     

     

     

    이렇게 Sass문을 쓰면 각각 하나씩 치지 않아도 되니 덜 귀찮죠!

    무엇보다 수정이 한 군데서 일어나기 때문에 관리하기 쉽다는 점이 매력적입니다. 나중에 변경사항이 생기면 위의 변수 부분만 수정해주면 되니까요 😚

     

     

     

     

     

    @while

    조건이 true인 동안 반복시킵니다. 하지만 다른 반복문인 @for나 @each가 더 직관적이기 때문에 거의 사용하지 않습니다. (=찬밥신세)

     

    아래 예제에서는 $idx라는 변수값을 계속 바꿀 수 있다는 점을 포인트로 봐주세요!

    $idx + 1처럼 숫자만큼 증가시키거나 calc()내에 넣어 계산된 값을 쓸 수도 있습니다.

     

    .item {
      $idx: 1;
      @while $idx < 5 {
        &:nth-child(#{$idx}) {
          filter: grayscale(1);
          font-size: calc(10vmin + #{$idx * 2}vmin);
        }
        $idx: $idx + 1;
      }
    }

    See the Pen Untitled by nanalike (@nykim_) on CodePen.

     

     

     

     


     

     

     

    2) 내장 함수

     

    Sass의 매력 중 하나는 '네가 필요할 것 같아서 미리 준비해왔어'라는 쎈스가 넘친다는 점 😘

    색상 값을 계산하거나 맵 변수의 특정 값을 알아내는 등, 미리 정의된 빌트-인 함수를 제공하고 있어 언제든 가져와 쓸 수 있어요!

     

     

    색상 함수

    버튼에 hover 했을 때 색을 좀 더 밝게 만들고자 합니다. 이때 색상값을 직접 계산하기 귀찮으므로 함수인 scale()을 써봅니다.

    이를 위해선 우선 sass:color라는 내장 모듈을 가져와야 하므로 @use 규칙으로 가져와 주세요.

     

    @use 'sass:color'; // 모듈 호출

     

    사용법은 간단해요!

    {네임스페이스}.함수() 형태에 인자를 넣어 사용하면 됩니다.

     

    @use 'sass:color'; // 모듈 호출
    $navy: #0a4069; // 변수 선언
    
    .btn {
      background-color: $navy; // 변수 그대로 사용
    
      &:hover {
        background-color: color.scale($navy, $lightness: 20%); // 색상값 변환 후 사용
      }
    }

     

     

    See the Pen Untitled by nanalike (@nykim_) on CodePen.

     

     

    예제에서 버튼에 hover 하면 배경이 기본 색상(#0a4069)에서 20% 만큼 밝아진 색상(#116cb1)이 됩니다.

    만약 $navy의 값이 달라지더라도 거기에 맞춰 20% 만큼 밝아질 테니 hover 색상을 따로 수정할 필요가 없어지죠.

     

    그리고 눈썰미 좋은 분은 scss 코드 내에 rgb()라는 함수와 color.red(), color.green(), color.blue() 라는 함수가 있는 걸 발견하셨을 텐데요 🦅👀

    이들도 Sass 내장 모듈이 제공하는 함수에요! 

    red(), green(), blue()로 r,g,b 값을 받아온 뒤 rgb()에 넣어 HEX 값을 얻었습니다. 

     

     

    색상 함수에는 이러한 것들이 있습니다. 더 자세한 내용은 문서를 참고해 주세요.

     

    scale() 색상의 rgb, hsl 등을 -100%에서 +100%까지 조절한 값을 리턴 color.scale(#998099, $alpha: -40%); // rgba(153, 128, 153, 0.6)
    mix() 색상끼리 혼합한 색상값을 리턴 color.mix(#036, #d2e1dd, 75%); // #355f84
    invert() 대비되는 색상값을 리턴 color.invert(black); // white
    grayscale() 동일한 밝기의 회색값을 리턴. color.change($color, $saturation: 0%)와 동일 color.grayscale(#6b717f); // #757575

     

     

    Sass에 모듈 시스템이 생기기 전에는 이런 빌트인 함수도 전역으로 사용했습니다.
    Sass 팀은 전역으로 쓰지 않는 것을 추천하지만, 필요하다면 호환성을 위해 네임스페이스 없이 전역으로 사용할 수 있습니다.
    다만 if(), hsl(), rgb()는 멤버명 없이 전역으로 사용할 수 있습니다.
    기존에 Sass를 쓰셨다면 색상을 밝게 만들 때 lighten() 함수가 더 익숙한 분들도 계실 거에요.
    하지만 lighten()이나 darken()은 종종 원하는 밝기가 아닌, 고정된 양만큼의 밝기를 변화시키는 문제가 있어 새 모듈에 포함되지 못했습니다.....
    호환성을 위해 해당 함수를 아직 사용할 수 있긴 하지만 더 정확한 값을 얻으려면 scale() 사용을 추천합니다.

     

     

     

     

     

    list, map 함수

    list와 map 변수를 인자로 받아 특정 값을 리턴합니다.

    얘도 정말 다양한 함수를 제공하는데, 저는 주로 nth()get() 함수를 사용해요.

     

     

    예를 들어 nth()는 n번째에 해당하는 list 변수의 값을 알려줍니다.

    아래는 3개의 값이 들어 있는 fontSize 변수에서 3번째 값을 가져와 사용한 예시입니다.

    사용하기 전 @use로 모듈 가져온 부분도 체크해 보세요! (하지만 네임스페이스 없이 쓰는 것도 가능해요)

     

    @use 'sass:list';
    
    $fontSize: 12px, 16px, 20px;
    
    .title {
      font-size: list.nth($fontSize, 3); //결과: 20px
    }

     

    이번엔 map.get()이란 함수를 써봅니다.

    font-weight의 값을 map 변수로 만든 상태인데요, 여기서 'bold'에 해당하는 값을 알아내기 위해 get()을 사용했습니다.

     

    @use 'sass:map';
    
    $weight: (
      'light': 300,
      'regular': 400,
      'bold': 700
    );
    
    .title {
      font-weight: map.get($weight, 'bold'); //결과: 700
    }

     

     

    이런 내장 함수는 반복문이나 조건문과 결합하면 효율을 더 극대화할 수 있어요!

    예를 들어 @for은 1씩 늘어나는 인덱스를 사용하니 list 내부를 돌면서 인덱스와 일치하는 값을 쓰도록 할 수 있습니다.

     

    @use 'sass:list';
    @use 'sass:color';
    
    $color: red, orange, yellow, green, blue;
    
    .lv {
      @for $i from 1 through list.length($color) {
        &:nth-child(#{$i}) {
          background-color: list.nth($color, $i);
          color: color.invert(list.nth($color, $i));
        }
      }
    }

     

     

    list.nth()함수를 통해 $i번째 $color값을 알아내 자동으로 넣어줬습니다.

    또한 반복문의 횟수를 length()로 지정했기 때문에 $color 길이가 늘거나 줄어도 신경쓸 필요가 없어요. 와! 진.짜.편.합.니.다

     

    하나 더. color 값을 보면 nth()를 invert()로 감싸고 있는 걸 볼 수 있습니다.

    리턴된 값을 알아서 잘 가져다 쓰기 때문에 이처럼 함수를 인자로 넣어 쓰는 것도 가능합니다. 물론 @if와 같은 조건문 안에 넣는 것도 가능하고요!

    다만 한번에 너무 많이 감싸는 경우 가독성이 떨어지므로 중간에 지역 변수로 잘라내는 것도 방법입니다.

     

     

    See the Pen Untitled by nanalike (@nykim_) on CodePen.

     

     

     

    nth() 리스트의 n번째에 해당하는 값을 리턴 (음수인 경우 뒤에서부터 계산) list.nth(10px 12px 16px, 2); // 12px
    length() 리스트의 길이를 리턴 list.length(10px 20px 30px); // 3
    set-nth() 리스트의 특정 n번째 요소를 교체하여 리턴  list.set-nth(10px 20px, 1, 1px); // 1px 20px

     

    get() 맵의 키와 연결된 값을 리턴 map.get($font-weights, "Bold");
    set() 맵의 키와 연결된 값을 수정하여 리턴 map.set($font-weights, "regular", 300);
    has-key() 맵의 키가 존재하는지 여부 map.has-key($font-weights, "semiBold");
    keys() 맵의 모든 키를 쉼표로 구분하여 리턴 map.keys($font-weights);
    values() 맵의 모든 값을 쉼표로 구분하여 리턴 map.values($font-weights);

     

    더 자세한 내용은 문서를 참고해 주세요.

     

     

     

    그 외

    그밖에 내장 모듈로는 숫자를 계산해주는 sass:math, 문자열을 다루는 sass:string, 다른 모듈을 평가할 수 있는 sass:meta, 선택자를 평가하는 sass:selecter가 있습니다.

    여기서부터 그 외로 묶이는 이유는... 저도 잘 안 써서 어색하기 때문... 사실 이 글 쓰면서 알게 된 함수가 더 많습니다 🙈

     

    @use 'sass:math';
    h1 {
     position: absolute;
     left: #{math.ceil(48.8)}px; // 올림처리 = 50px
    }
    @use 'sass:selector';
    
    #{selector.append(".button", ".active, .disabled")} { // 셀렉터 묶기
    	color: red;
    }
    @use 'sass:meta';
    
    @function add($a, $b) {
      @return $a + $b;
    }
    
    .title {
      @if meta.function-exists('add') { // 해당 함수가 존재하는지 판단
        font-size: #{add(50, 30)}px; // true
      } @else {
        font-size: 20px;
      }
    }

     

     

     


     

     

     

    3) 함수

     

    내장 함수도 좋지만 '아 뭔가 딱 나한테 맞는 기능의 함수가 없네...' 할 때! 직접 함수를 만들어 쓸 수도 있습니다.

    @function 으로 함수를 선언하고 @return 으로 리턴값을 설정해주면 됩니다.

     

    예제를 통해 익혀보죠! 이 포스팅에 나온 것처럼 html의 폰트 크기를 62.5%로 잡고 나머지를 rem 단위로 작업하려고 합니다.

    그런데 시안은 px 단위기 때문에 작성할 때 변환이 필요합니다. 5px → 0.5rem, 200px → 20rem처럼요.

    이걸 머리속에서 계산하고 써도 되지만, 좀 더 안전하게 작성하기 위해 함수를 쓰기로 합니다.

     

    @function rem($px) {
      @return #{calc($px / 10)}rem;
    }
    
    .logo {
      width: rem(70); // 70px = 7rem
      height: rem(30); // 30px = 3rem
    }

     

    인자로 넘어온 $px 값을 10으로 나눠 리턴하는 단순한 함수입니다. @return으로 리턴값을 명시해야 하는 것에 주의하세요!

    사용할 때는 내장 함수를 썼던 것처럼 호출해 주면 됩니다.

     

    단, 예제는 함수가 같은 파일 안에 있어서 네임스페이스가 없지만 함수가 다른 파일에 있다면 모듈을 불러와야 합니다.

     

    @use 'fn';
    
    .logo {
      width: fn.rem(70);
      height: fn.rem(30);
    }

     

     

    함수 내에서 다른 함수를 불러와 쓰는 것도 물론 가능합니다.

    아래는 SVG 아이콘을 변수에 저장한 다음 함수를 호출해 조합해서 사용하는 예제입니다.

    (백그라운드 이미지로 SVG 사용하는 방법은 요 포스팅을 참고하세요)

     

    @use 'sass:map';
    @use 'sass:string';
    
    $icons: (
      sun: 'M23.9922 38.4651C24.6394 38.4651 25.1717 38.957 25.2358 39.5873L25.2422 39.7151V42.7527C25.2422 43.443 24.6826 44.0027 23.9922 44.0027C23.345 44.0027 22.8127 43.5108 22.7487 42.8805L22.7422 42.7527V39.7151C22.7422 39.0248 23.3019 38.4651 23.9922 38.4651ZM35.8947 34.0978L35.9962 34.1889L38.1441 36.3368C38.6322 36.8249 38.6322 37.6164 38.1441 38.1046C37.6884 38.5602 36.9686 38.5905 36.4778 38.1957L36.3763 38.1046L34.2284 35.9567C33.7403 35.4685 33.7403 34.6771 34.2284 34.1889C34.684 33.7333 35.4039 33.7029 35.8947 34.0978ZM13.755 34.1889C14.2106 34.6445 14.241 35.3644 13.8461 35.8552L13.755 35.9567L11.6071 38.1046C11.119 38.5927 10.3275 38.5927 9.83937 38.1046C9.38376 37.6489 9.35339 36.9291 9.74825 36.4383L9.83937 36.3368L11.9872 34.1889C12.4754 33.7008 13.2668 33.7008 13.755 34.1889ZM23.9999 13.0805C30.0306 13.0805 34.9194 17.9693 34.9194 24C34.9194 30.0306 30.0306 34.9194 23.9999 34.9194C17.9693 34.9194 13.0805 30.0306 13.0805 24C13.0805 17.9693 17.9693 13.0805 23.9999 13.0805ZM23.9999 15.5805C19.35 15.5805 15.5805 19.3501 15.5805 24C15.5805 28.6499 19.35 32.4194 23.9999 32.4194C28.6499 32.4194 32.4194 28.6499 32.4194 24C32.4194 19.3501 28.6499 15.5805 23.9999 15.5805ZM42.7308 22.787C43.4212 22.787 43.9808 23.3467 43.9808 24.037C43.9808 24.6842 43.489 25.2166 42.8586 25.2806L42.7308 25.287H39.6933C39.0029 25.287 38.4433 24.7274 38.4433 24.037C38.4433 23.3898 38.9352 22.8575 39.5655 22.7935L39.6933 22.787H42.7308ZM8.30657 22.7287C8.99692 22.7287 9.55657 23.2884 9.55657 23.9787C9.55657 24.6259 9.06469 25.1582 8.43437 25.2223L8.30657 25.2287H5.26904C4.57869 25.2287 4.01904 24.6691 4.01904 23.9787C4.01904 23.3315 4.51092 22.7992 5.14124 22.7352L5.26904 22.7287H8.30657ZM11.5056 9.8043L11.6071 9.89542L13.755 12.0433C14.2432 12.5314 14.2432 13.3229 13.755 13.811C13.2994 14.2667 12.5796 14.297 12.0887 13.9022L11.9872 13.811L9.83937 11.6632C9.35122 11.175 9.35122 10.3836 9.83937 9.89542C10.295 9.43981 11.0148 9.40943 11.5056 9.8043ZM38.1441 9.89542C38.5997 10.351 38.63 11.0709 38.2352 11.5617L38.1441 11.6632L35.9962 13.811C35.508 14.2992 34.7166 14.2992 34.2284 13.811C33.7728 13.3554 33.7425 12.6356 34.1373 12.1448L34.2284 12.0433L36.3763 9.89542C36.8644 9.40727 37.6559 9.40727 38.1441 9.89542ZM24.0004 3.99731C24.6476 3.99731 25.18 4.48919 25.244 5.11951L25.2504 5.24731V8.28484C25.2504 8.97519 24.6908 9.53484 24.0004 9.53484C23.3532 9.53484 22.8209 9.04296 22.7569 8.41264L22.7504 8.28484V5.24731C22.7504 4.55696 23.3101 3.99731 24.0004 3.99731Z',
      moon: 'M9.66862399,33.0089622 C14.6391867,41.6182294 25.647814,44.5679822 34.2570813,39.5974194 C36.6016136,38.243803 38.5753268,36.4126078 40.0785961,34.229664 C40.5811964,33.4998226 40.256086,32.4918794 39.421758,32.193262 C32.6414364,29.766492 29.0099482,26.9542522 26.9026684,22.9317305 C24.6842213,18.6970048 24.110919,14.0582926 25.662851,7.69987534 C25.8774494,6.82064469 25.1829812,5.98348115 24.27924,6.03196802 C21.4771404,6.18230425 18.739608,6.98721743 16.2570813,8.42050489 C7.64781404,13.3910676 4.69806124,24.3996949 9.66862399,33.0089622 Z M24.6881449,24.0918536 C26.9913881,28.4884439 30.80035,31.5226926 37.1145781,33.998575 C35.9639388,35.3646621 34.5800621,36.524195 33.0070813,37.4323559 C25.5935456,41.7125627 16.1138943,39.1724978 11.8336875,31.7589622 C7.55348069,24.3454265 10.0935456,14.8657752 17.5070813,10.5855684 C19.0445047,9.69793654 20.6992772,9.08707059 22.4136896,8.76727896 L22.882692,8.68729053 C21.6894389,14.6550319 22.2911719,19.5163454 24.6881449,24.0918536 Z'
    );
    
    @function get-color($color) {
      @if $color != '' {
        @return if(
          string.index($color, '#'),
          '%23#{string.slice($color, 2)}',
          '%23#{$color}'
        );
      }
      @return 'none';
    }
    
    @function icon($name,  $fill: '') {
      $fill-color: get-color($fill);
      $icon: map.get($icons, $name);
      @if $icon {
        $svg: "%3Csvg viewBox='0 0 48 48' xmlns='http://www.w3.org/2000/svg'%3E";
        $path: "%3Cpath d='#{$icon}' fill='#{$fill-color}' /%3E";
        @return url('data:image/svg+xml;charset=utf8,#{$svg}#{$path}%3C/svg%3E');
      } @else {
        @error '#{$name}에 해당하는 아이콘이 존재하지 않습니다.';
      }
    }
    
    .icon {
      background-image: icon(sun, $fill: 'ff5a40');
    }
    
    .icon2 {
      background-image: icon(moon, $fill: '#ffdb34');
    }

     

    코드가 좀 복잡해졌지만 괜찮습니다, 우리가 다 스캔 끝낸 것들이니까요 😎

    먼저 맨 위에 @use로 필요한 모듈들을 가져왔고, $icons에 SVG 값을 담아두었습니다.

     

    @function get-color($color) {
      @if $color != '' {
        @return if(
          string.index($color, '#'),
          '%23#{string.slice($color, 2)}',
          '%23#{$color}'
        );
      }
      @return 'none';
    }

     

    get-color()는 SVG에 넣을 컬러값을 리턴하도록 제가 만든 커스텀 함수입니다.

    이 함수는 인자가 없다면 'none'을 리턴하고, 인자가 있다면 #을 떼고 리턴합니다.

    이렇게 해주는 이유는 색상코드에 #이 들어있든 아니든 에러 없이 적용하고 싶기 때문입니다.

    그래서 index()로 '#'이 있는지 판단해, slice()로 잘라주었습니다.

     

     

    @function icon($name,  $fill: '') {
      $fill-color: get-color($fill);
      $icon: map.get($icons, $name);
      @if $icon {
        $svg: "%3Csvg viewBox='0 0 48 48' xmlns='http://www.w3.org/2000/svg'%3E";
        $path: "%3Cpath d='#{$icon}' fill='#{$fill-color}' /%3E";
        @return url('data:image/svg+xml;charset=utf8,#{$svg}#{$path}%3C/svg%3E');
      } @else {
        @error '#{$name}에 해당하는 아이콘이 존재하지 않습니다.';
      }
    }

     

    icon() 함수는 $icons 변수에서 해당하는 d값을 가져오고, get-color() 함수로 계산해온 색상값을 적용해 줍니다.

    한번에 입력해도 되지만 가독성이 떨어져서 여러 개의 변수로 분리했습니다.

     

    또, 오타 등으로 존재하지 않는 아이콘을 쓰려고 했을 때 @error로 에러를 발생시키도록 했습니다.

    이러면 컴파일을 막아 '앗 실수!'를 개발 단계에서 바로 인지할 수 있습니다 🤟

     

     

    See the Pen Untitled by nanalike (@nykim_) on CodePen.

     

     

     

     


     

     

     

    4) 기타 팁

     

    이제 거의 끝입니다 (헉헉) 🏃‍♀️

    추가로 알아두면 좋은 @at-rules 몇 가지와 Dart Sass의 주요 변경사항에 대해 짧게 다뤄봅니다.

     

     

    @error, @warn

    인수가 들어가는 함수/믹스인에게 올바른 인수가 들어왔는지 확인하고 싶을 때 @error@warn을 사용할 수 있습니다.

    @error는 오류났다는 걸 알리며 컴파일을 중지하지만, @warn은 경고만 표시할 뿐 컴파일을 중지하지는 않습니다.

     

    @mixin lang($lang) {
      $langs: 'ja', 'en';
    
      @if not list.index($langs, $lang) {
        @if $lang == 'jp' or $lang == 'jap' {
          @warn '`#{$lang}`이 아니라 `ja` 입니다🤔 ';
          $lang: 'ja';
        } @else {
          @error '`#{$lang}`는 `#{$langs}`에 포함되어 있지 않습니다 🤔 ';
        }
      }
    
      :lang(#{$lang}) {
        @content;
      }
    }
    
    
    @include lang(jp) { // 경고 표시
      .logo {
        font-size: 18px;
      }
    }
    
    @include lang(asdf) { // 오류 표시 후 컴파일 중지
      .logo {
        font-size: 22px;
      }
    }

     

     

    @at-root

    이걸 쓰면 표현식을 잠깐 root 레벨로 보내버릴 수 있습니다.

     

    .header {
      .nav {
        .menu {
          .item {
            @at-root {
              .title {
                font-size: 30px;
              }
            }
          }
        }
      }
    }
    
    // 컴파일 결과: .title { font-size:30px; }

     

     

    @debug

    @debug는 변수나 표현식이 맞게 들어갔나... 긴가만가 할 때 씁니다.

    뭔가 달라지는 건 없고 컴파일 시 @debug 결과를 보여줍니다.

     

    $var1: 150;
    $var2: $var1 * 10;
    $var3: $var1 + $var2;
    
    @debug calc(100vw - #{$var3}px);

     

    음, 역시 내 계산대로군 😌

     

     

     

     

    Dart Sass의 변경사항

    Dart Sass는 주요 변경사항이 릴리즈 하기 전 경고를 표시하고 이후 릴리즈를 실시합니다.

    아래는 Dart Sass에서 바뀐 (또는 곧 바뀔) 변경사항 중 흥미로운 2가지를 긁어왔습니다. 자세한 내용은 문서를 참고하세요!

     

     

    사용자 정의 변수에 Sass 변수 사용

    :root {
      --color-accent-800: $color-accent; // not working
      --color-accent-800: #{$color-accent};
      --color-accent-200: #{rgba($color-accent, 0.2)};
    }

     

    사용자 정의 변수에 Sass 변수를 쓰려면 보간으로 감싸서 사용해야 제대로 컴파일 됩니다.

    왜 안 들어가는 거지?! 하면 당황하지 말고 감싸주세요! 감싼 후에는 함수 등으로 값을 조절하는 것도 가능합니다.

     

     

     

    슬래시로 나눗셈 연산

    $size: 200px;
    
    div {
      width: $size / 5; // warning
    }
    
    div {
      width: calc($size / 5); // ok
    }

     

    Sass에서 / 는 나눗셈 연산을 수행하기도 하지만 구분자 기호로도 사용됩니다.

    따라서 혼동을 막기 위해 나눗셈을 작성할 때는 calc()로 감싸거나, math.div() 함수를 써서 계산하는 것을 권장합니다.

     

     


     

     

     

    에필로그

     

    와 정말 길고 긴 시리즈 글! 야심찬 시리즈물도 이 글이 마지막으로 끝이네요.

    최대한 실무에서 사용할 만한 응용 가능한 예제를 많이 넣으려고 했는데, 익히는 데 도움이 됐다면 기쁩니다 :-D

     

    사실 CSS 작성에 있어 Sass만이 방법인 건 아니긴 합니다.
    JS랑 분리되어 있다보니 프롭만 받아오면 해결될 것을 굳이 클래스로 나눠야 하는 불편함,
    그냥 속성 하나만 주고 싶은데 굳이 마크업으로 나누고 셀렉팅해줘야 하는 불편함도 물론 있죠...

    그래도 믹스인, 함수 등 다양한 기능으로 활용도가 높고 CSS와 유사해 쉽게 쓸 수 있다는 점에서 참 좋아합니다 😘

     

     

    검색으로 들어오신 분도, 즐찾이나 구독으로 와주신 분(넘나 감사합니다)도 감사 인사드립니다 🙇‍♀️

    Sass와 함께 더 편한 CSS 라잎을 즐기자구요  \\ ٩( ᐛ )و //

    728x90

    댓글 1