[패스트캠퍼스 수강 후기] 프론트엔드 인강 100% 환급 챌린지 34회차 미션 - 31강 Sass props 설정 적용하기

5 minute read

props 만들고 설정해주기

버튼에 color props 추가하기

기존에 우리가 만든 버튼은 파랑이었는데 이번에는 핑크, 그레이도 한번 추가해보도록 하자. 색상 선택이 고민될때는 open-color라는 사이트를 이용하면 밝기별로 색상표가 나와있어서 사용하기에 좋다.
자, 그럼 Button 컴포넌트에 아래와 같이 color props를 추가해보자.

Button.js

import React from "react";
import classNames from "classnames";
import "./Button.scss";

// size: large, medium, small
// color : blue, pink, gray
function Button({ children, size, color }) {
  return (
    <button className={classNames("Button", size, color)}>{children}</button>
  );
}

Button.defaultProps = {
  //가장정석적인 defualt설정
  size: "medium",
  color: "blue",
};
export default Button;

그리고 아래와 같이 color별 변수를 만들어주고 버튼의 컬러마다 각각의 스타일을 만들어주면 된다.

Button.scss

$blue: #228be6; //변수 생성
$gray: #495057;
$pink: #f06595;

.Button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-weight: bold;
  outline: none;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  padding-left: 1rem;
  padding-right: 1rem;
  transition: 0.2s all;
  &.large {
    height: 3rem;
    font-size: 1.25rem;
  }
  &.medium {
    height: 2.25rem;
    font-size: 1rem;
  }
  &.small {
    height: 1.75rem;
    font-size: 0.875rem;
  }

  //버튼 컬러별 스타일링
  &.blue {
    background: $blue;
    &:hover {
      background: lighten($blue, 10%);
      box-shadow: 2px 2px 2px rgba($color: #000000, $alpha: 0.2);
      transition: 0.2s all;
    }
    &:active {
      background: darken($blue, 10%);
      box-shadow: inset 2px 2px 5px rgba($color: #000000, $alpha: 0.1);
    }
  }

  &.pink {
    background: $pink;
    &:hover {
      background: lighten($pink, 10%);
      box-shadow: 2px 2px 2px rgba($color: #000000, $alpha: 0.2);
      transition: 0.2s all;
    }
    &:active {
      background: darken($pink, 10%);
      box-shadow: inset 2px 2px 5px rgba($color: #000000, $alpha: 0.1);
    }
  }

  &.gray {
    background: $gray;
    &:hover {
      background: lighten($gray, 10%);
      box-shadow: 2px 2px 2px rgba($color: #000000, $alpha: 0.2);
      transition: 0.2s all;
    }
    &:active {
      background: darken($gray, 10%);
      box-shadow: inset 2px 2px 5px rgba($color: #000000, $alpha: 0.1);
    }
  }

  & + & {
    margin-left: 1rem;
  }
}

그런데 위의 scss코드를 보면 색상별로 중복되는 코드가 좀 거슬린다. blue, pink, gray가 모두 비슷한 코드에 변수명만 다른것을 볼 수 있다. 이럴때는 scss의 mixin을 사용해주면 되는데 방법은 아래와 같다.

//자주 반복되는 구간을 복사한다. 그리고 믹스인을 새로 생성한다.
@mixin button-color($color) {
  //@mixin 이름( 파라미터 - 컬러이름을 받아올 곳)
  background: $color;
  &:hover {
    background: lighten($color, 10%);
    box-shadow: 2px 2px 2px rgba($color: #000000, $alpha: 0.2);
    transition: 0.2s all;
  }
  &:active {
    background: darken($color, 10%);
    box-shadow: inset 2px 2px 5px rgba($color: #000000, $alpha: 0.1);
  }
}

이렇게 중복되는 코드를 파라미터로 받아올 수 있게 만들어주고 원래 코드가 있던 부분에는 아래와 같이 include해준다.

&.blue {
  @include button-color(
    $blue
  ); //button-color라는 믹스인에 컬러변수를 파라미터로 보내줌.
}

&.pink {
  @include button-color($pink);
}

&.gray {
  @include button-color($gray);
}

이제 App.js로 돌아가서 우리가 만든 버튼 컴포넌트를 불러서 확인해보자.

App.js

import React from "react";
import Button from "./components/Button";
import "./App.scss";

function App() {
  return (
    <div className="App">
      <div className="buttons">
        <Button size="large">Button</Button>
        <Button>Button</Button>
        <Button size="small">Button</Button>
      </div>
      <div className="buttons">
        <Button size="large" color="gray">
          Button
        </Button>
        <Button color="gray">Button</Button>
        <Button size="small" color="gray">
          Button
        </Button>
      </div>
      <div className="buttons">
        <Button size="large" color="pink">
          Button
        </Button>
        <Button color="pink">Button</Button>
        <Button size="small" color="pink">
          Button
        </Button>
      </div>
    </div>
  );
}

export default App;

리액트화면이미지 위와 같이 잘 나오는 것을 볼 수 있다.
자 이제 나머지 props들도 함께 완성해보자.

outline, fullWidth props 추가하기

이번엔 버튼에 선만 있는 스타일인 outline과 버튼이 100% 꽉 차는 fullWidth의 prpos를 만들어 볼 것이다. 먼저 Button 컴포넌트에 두가지 props를 추가하자.

Button.js 안의 fuction Button 수정

function Button({ children, size, color, outline, fullWidth }) {
  return (
    <button
      className={classNames("Button", size, color, {
        //이 둘은 true or false만 있기때문에 객체형태로 받아와준다고함.
        outline,
        fullWidth,
      })}
    >
      {children}
    </button>
  );
}

그리고 scss에서 outline같은 경우는 컬러별로 만들어야 하니까 mixin안에 만들어주고 fullWidth는 전체적으로 적용하는 것이니 &.fullWidth로 만들어보자. 코드는 아래와 같다.

Button.scss

$blue: #228be6;
$gray: #495057;
$pink: #f06595;

@mixin button-color($color) {
  //@mixin 이름(파라미터)
  background: $color;
  &:hover {
    background: lighten($color, 10%);
    box-shadow: 2px 2px 2px rgba($color: #000000, $alpha: 0.2);
    transition: 0.2s all;
  }
  &:active {
    background: darken($color, 10%);
    box-shadow: inset 2px 2px 5px rgba($color: #000000, $alpha: 0.1);
  }
  //outline 추가
  &.outline {
    color: $color;
    background: none;
    border: 1px solid $color;
    &:hover {
      background: $color;
      color: #fff;
    }
  }
}

.Button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-weight: bold;
  outline: none;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  padding-left: 1rem;
  padding-right: 1rem;
  transition: 0.2s all;
  &.large {
    height: 3rem;
    font-size: 1.25rem;
  }
  &.medium {
    height: 2.25rem;
    font-size: 1rem;
  }
  &.small {
    height: 1.75rem;
    font-size: 0.875rem;
  }

  &.blue {
    @include button-color($blue);
  }

  &.pink {
    @include button-color($pink);
  }

  &.gray {
    @include button-color($gray);
  }

  & + & {
    //Button뒤에 Button이 올때만.
    margin-left: 1rem;
  }
  &.fullWidth {
    width: 100%;
    justify-content: center;
    & + & {
      margin-left: 0;
      margin-top: 1rem;
    }
  }
}

이제 App.js에서 우리가 만든 속성을 적용해서 불러와보자.

App.js

import React from "react";
import Button from "./components/Button";
import "./App.scss";

function App() {
  return (
    <div className="App">
      <div className="buttons">
        <Button size="large">Button</Button>
        <Button>Button</Button>
        <Button size="small">Button</Button>
      </div>
      <div className="buttons">
        <Button size="large" color="gray">
          Button
        </Button>
        <Button color="gray">Button</Button>
        <Button size="small" color="gray">
          Button
        </Button>
      </div>
      <div className="buttons">
        <Button size="large" color="pink">
          Button
        </Button>
        <Button color="pink">Button</Button>
        <Button size="small" color="pink">
          Button
        </Button>
      </div>
      <div className="buttons">
        <Button
          // outline={true} 혹은 outline={false}라고 써줘도 됨. outline이 있으면 true, 없으면 false 반환.
          size="large"
          outline
        >
          Button
        </Button>
        <Button color="pink" outline>
          Button
        </Button>
        <Button size="small" color="gray" outline>
          Button
        </Button>
      </div>
      <div className="buttons">
        <Button size="large" fullWidth>
          Button
        </Button>
        <Button size="large" color="pink" fullWidth>
          Button
        </Button>
        <Button size="large" color="gray" fullWidth>
          Button
        </Button>
      </div>
    </div>
  );
}

export default App;

리액트구동화면 잘 구동되는 것을 볼 수 있다.

버튼에 …rest props 전달하기

먼저 왜 …rest를 써야하는지 알아보자. 우리가 App.js에서 버튼에 onClick이나, onMouseMove라는 기능을 주었다고 가정했을때, 이 기능들을 Button.js에서 받아와서 적용해주어야 구동이 가능하다.

App.js 안에서의 button컴포넌트 일부

<Button
  size="large"
  color="gray"
  fullWidth
  onClick={() => {
    console.log("클릭");
  }}
  onMouseMove={() => {
    console.log("무브");
  }}
>
  Button
</Button>

이렇게 기능을 넣어주면 이걸 버튼 컴포넌트에서 받아와 연결해주는게 있어야 하는데 보통은 이런식으로 한다.

Button.js 안에서의 function button

function Button({
  children,
  size,
  color,
  outline,
  fullWidth,
  onClick,
  onMouseMove,
}) {
  return (
    <button
      className={classNames("Button", size, color, {
        outline,
        fullWidth,
      })}
      onClick={onClick} //이런식으로 연결
      onMouseMove={onMouseMove} //이런식으로 연결
    >
      {children}
    </button>
  );
}

위와 같이 Button.js에서도 onClick과 onMouseMove도 연결해주어야 한다. 그런데 매번 이렇게 기능을 추가할때마다 컴포넌트로 넘어와서 기능을 넣어주는 것이 불편하다면 아래와 같이 …rest로 쉽게 구현할 수 있다.

function Button({ children, size, color, outline, fullWidth, ...rest }) {
  return (
    <button
      className={classNames("Button", size, color, {
        //이 둘은 true or false만 있기때문에 객체형태로 받아와준다고함.
        outline,
        fullWidth,
      })}
      {...rest}
    >
      {children}
    </button>
  );
}

여기서 한가지만 더 추가하자면, App.js 내에서 버튼을 커스터마이징 할 경우를 위해서 className도 받아와주면 더 완벽해질 수 있다.

function Button({
  children,
  size,
  color,
  outline,
  fullWidth,
  className,
  ...rest
}) {
  //className 추가
  console.log(rest);
  return (
    <button
      className={classNames(
        "Button",
        size,
        color,
        {
          outline,
          fullWidth,
        },
        className //App에서 커스터마이징 클래스 이름이 생성될 경우 받아올 수 있도록 className 속성도 넣어주면 좋다.
      )}
      {...rest}
    >
      {children}
    </button>
  );
}

className이 겹치지 않게 작성하는 팁!

클래스네임작명 전체 프로젝트안에서 컴포넌트와 클래스명을 보여주면서 예시를 들어줬으면 더 좋았을 것 같은 개인적인 아쉬움이….

오늘은 여기까지..
시청 영상 31강 04~06까지

수강인증이미지

프론트엔드 개발 올인원 패키지 with React Online. 👉 https://bit.ly/31Cf1hp

Comments