[강의] Styled Components

김하은·2023년 12월 2일
0

코드잇 강의 정리

목록 보기
55/60

기존 방식의 문제점

CSS 클래스 이름이 겹치는 문제

  • 클래스 이름이 전역적인 특성을 가지고 있음
  • 클래스 이름이 전역적이라는 건 한 컴포넌트에서 사용한 클래스 이름을 다른 모든 컴포넌트에서도 사용할 수 있다는 뜻
  • import 해오지 않은 CSS 파일에서도 같은 클래스 이름이 사용된 부분이 있으면 그곳의 스타일이 함께 적용됨
  • 의도하지 않은 방식으로 스타일이 적용되는 걸 막기 위해 클래스 이름은 겹치지 않도록 조심해야 함

    Styled Components는 클래스 이름을 아예 쓰지 않음
    CSS 코드로 React 컴포넌트를 바로 만드니까 애초에 클래스 이름이 겹칠 일이 없음

const StyledApp = styled.div`
  background-color: #000000;
`;

const Dashboard = styled.div`
  font-size: 16px;
`;

function App() {
  return (
    <StyledApp>
      <Dashboard> ... </Dashboard>
    </StyledApp>
  );
}

재사용하는 CSS 코드를 관리하기 어렵다

  • shadow20만을 보고는 이게 어디에서 사용되는 스타일인지 알기 어렵다는 단점이 있음
  • 스타일이 재사용 되는 곳이 점점 더 많아질수록 코드를 유지 보수 할 때 관리가 더 힘들어짐

    Styled Components에서는 스타일 재사용이 필요한 상황에서 클래스가 아니라 JavaScript 변수를 만듦
    JavaScript라서 언제 어디서 쓰고 있는지 에디터를 통해 확인하기 쉽고, 이름을 바꾸거나 삭제를 하는 것도 코드 에디터를 통해 쉽게 할 수 있음

// src/shadows.js
import { css } from 'styled-components';

const shadow20 = css`
  box-shadow: 0 10px 15px rgba(0, 0, 0, 0.2);
`;

const shadow40 = css`
  box-shadow: 0 10px 15px rgba(0, 0, 0, 0.4);
`;

// src/Card.js

import { shadow20 } from '../shadows';

const Card = styled.div`
  ${shadow20}
  ...(다른 CSS 코드)
`;

export default Card;

Hello Styled

설치

  • 생성한 프로젝트 폴더 안에서 아래 명령어로 Styled Components를 설치해야 함
    npm install styled-components

styled 불러오기

  • styled-components의 default import로 styled를 가져오됨
  • 대부분의 작업은 styled 함수를 사용함
import styled from 'styled-components';

컴포넌트 만들기

  • Styled Components에서는 클래스 대신에 컴포넌트를 만듦
  • 아래 코드는 <button> 태그에 스타일을 지정한 컴포넌트를 만드는 코드임
  • styled.tagnametagname 부분에는 스타일을 적용할 HTML 태그 이름을 쓰고, 바로 뒤에 템플릿 리터럴 문법으로 CSS 코드를 적음
    • styled.tagname으로 만든 컴포넌트는 일반적인 React 컴포넌트처럼 JSX로 사용하면 됨
  const Button = styled.button`
  background-color: #6750a4;
  border: none;
  color: #ffffff;
  padding: 16px;
`;

Nesting 문법

  • Nesting은 CSS 규칙 안에서 CSS 규칙을 만드는 걸 말함
  • Nesting을 활용하는 두 가지 방법인 & 선택자와 컴포넌트 선택자에 대해 알아보겠음

& 선택자

  • Nesting에서 &는 부모 선택자를 의미함
  • 버튼 컴포넌트가 .Button이라는 클래스 이름을 쓸 때 &:hover.Button:hover와 같은 의미임

컴포넌트 선택자

  • 컴포넌트를 선택자로 쓰고 싶을 때는 ${Icon}같이 컴포넌트 자체를 템플릿 리터럴 안에 넣어주면 됨
import styled from 'styled-components';
import nailImg from './nail.png';

const Icon = styled.img`
  width: 16px;
  height: 16px;
`;

const StyledButton = styled.button`
  background-color: #6750a4;
  border: none;
  color: #ffffff;
  padding: 16px;

  & ${Icon} {
    margin-right: 4px;
  }

  &:hover,
  &:active {
    background-color: #463770;
  }
`;

function Button({ children, ...buttonProps }) {
  return (
    <StyledButton {...buttonProps}>
      <Icon src={nailImg} alt="nail icon" />
      {children}
    </StyledButton>
  );
}

export default Button;
  • &와 자손 결합자를 사용하는 경우에는 &를 생략할 수 있음
  • ${Icon}만 써도 똑같이 동작함
  ${Icon} {
    margin-right: 4px;
  }
  • Nesting은 여러 겹으로 할 수도 있는데 예를 들어서 아래 코드처럼 Nesting된 규칙 안에서 규칙을 만들 수도 있음
const StyledButton = styled.button`
  ...
  &:hover,
  &:active {
    background-color: #7760b4;

    ${Icon} {
      opacity: 0.2;
    }
  }
`;

다이나믹 스타일링

  • 표현식 삽입법: 템플릿 리터럴 안에는 달러와 중괄호${ ... }를 사용해서 JavaScript 코드를 집어넣는 것
  • 표현식 삽입법을 사용하면 Styled Components에서 Prop에 따라 컴포넌트의 스타일을 다르게 보여줄 수 있음

${ ... } 안에 값(변수) 사용하기

  • 가장 기본적인 사용법은 JavaScript 변수를 그대로 넣는 방식임
  • 우리가 평소에 템플릿 문자열을 만들 때 사용하는 방식이라고 생각하면 됨
const SIZES = {
  large: 24,
  medium: 20,
  small: 16
};

const Button = styled.button`
  ...
  font-size: ${SIZES['medium']}px;
`;

${ ... } 안에 함수 사용하기

  • Prop에 따라 스타일을 다르게 적용하는 함수를 넣을 수 있음
  • 함수의 파라미터로는 Props를 받고, 리턴 값으로는 스타일 코드를 리턴하면 됨
  • 참고로 이건 템플릿 리터럴의 기능이 아니라 Styled Components가 내부적으로 처리해 주는 것임
const SIZES = {
  large: 24,
  medium: 20,
  small: 16
};

const Button = styled.button`
  ...
  font-size: ${(props) => SIZES[props.size]}px;
`;
  • 만약에 구조 분해(Destructuring)하면 아래처럼 쓸 수도 있음
font-size: ${({ size }) => SIZES[size]}px;
  • font-size: px 같은 잘못된 CSS 코드를 방지하기 위해서 가능하면 기본 값을 정해주는 게 좋음
  • 아래와 같은 식으로 널 병합 연산자(Nullish coalescing operator)를 사용할 수도 있음
font-size: ${({ size }) => SIZES[size] ?? SIZES['medium']}px;

논리 연산자 사용하기

const Button = styled.button`
  ...
  ${({ round }) => round && `
      border-radius: 9999px;
    `}
`;

삼항 연산자 사용하기

border-radius: ${({ round }) => round ? `9999px` : `3px`};

스타일 재사용: 상속

styled() 함수

  • 상속: 다른 컴포넌트의 스타일을 가져와서 원하는 대로 사용할 수 있는 것
  • Styled Components로 만들어진 컴포넌트를 상속하려면 styled() 함수를 사용하면 됨
// src/Button.js
import styled from 'styled-components';

const SIZES = {
  large: 24,
  medium: 20,
  small: 16,
};

const Button = styled.button`
  background-color: #6750a4;
  border: none;
  color: #ffffff;
  font-size: ${({ size }) => SIZES[size] ?? SIZES['medium']}px;
  padding: 16px;

  ${({ round }) =>
    round
      ? `
      border-radius: 9999px;
    `
      : `
      border-radius: 3px;
    `}

  &:hover,
  &:active {
    background-color: #463770;
  }
`;

export default Button;

// src/App.js
import styled from 'styled-components';
import Button from './Button';

const SubmitButton = styled(Button)`
  background-color: #de117d;
  display: block;
  margin: 0 auto;
  width: 200px;

  &:hover {
    background-color: #f5070f;
  }
`;

function App() {
  return (
    <div>
      <SubmitButton>계속하기</SubmitButton>
    </div>
  );
}

export default App;

JSX로 직접 만든 컴포넌트에 styled() 사용하기

  • styled.tagname으로 만든 컴포넌트는 바로 styled() 함수를 사용할 수 있지만, 그렇지 않은 컴포넌트는 따로 처리가 필요함
  • 예를 들어, 약관을 보여주는 JSX 문법을 직접 사용해서 바로 컴포넌트가 만들어진TermsOfService라는 컴포넌트가 있다고 할 때
function TermsOfService() {
  return (
    <div>
      <h1>㈜코드잇 서비스 이용약관</h1>
      <p>
        환영합니다.
        <br />
        Codeit이 제공하는 서비스를 이용해주셔서 감사합니다. 서비스를
        이용하시거나 회원으로 가입하실 경우 본 약관에 동의하시게 되므로, 잠시
        시간을 내셔서 주의 깊게 살펴봐 주시기 바랍니다.
      </p>
      <h2>1  (목적)</h2>
      <p>
        본 약관은 ㈜코드잇이 운영하는 기밀문서 관리 프로그램인 Codeit에서
        제공하는 서비스를 이용함에 있어 이용자의 권리, 의무 및 책임사항을
        규정함을 목적으로 합니다.
      </p>
    </div>
  );
}

export default TermsOfService;
  • styled()로 지정한 스타일이 적용되지 않음
  • JSX 문법으로 직접 만든 컴포넌트는 styled() 함수가 적용될 className에 대한 정보가 없기 때문임
import styled from 'styled-components';
import Button from './Button';
import TermsOfService from './TermsOfService';

const StyledTermsOfService = styled(TermsOfService)`
  background-color: #ededed;
  border-radius: 8px;
  padding: 16px;
  margin: 40px auto;
  width: 400px;
`;

const SubmitButton = styled(Button)`
  background-color: #de117d;
  display: block;
  margin: 0 auto;
  width: 200px;

  &:hover {
    background-color: #f5070f;
  }
`;

function App() {
  return (
    <div>
      <StyledTermsOfService />
      <SubmitButton>계속하기</SubmitButton>
    </div>
  );
}

export default App;
  • Styled Components를 사용하지 않고 직접 만든 컴포넌트는 className 값을 Prop으로 따로 내려줘야 styled() 함수를 사용할 수 있음
function TermsOfService({ className }) {
  return (
    <div className={className}>
      ...
    </div>
  );
}
  • 직접 만든 컴포넌트에 className Prop을 따로 내려주는 건 syled() 함수가 적용될 부분의 className을 별도로 정해주는 거라고 이해하면 됨
  • 위 코드의 경우엔, <div> 태그에 className을 내려줬기 때문에 styled(TermsOfService)에서 작성한 코드는 TermsOfService 안에 있는 <div> 태그에 적용됨

    스타일 상속을 하려면 styled() 함수를 사용하면 되는데, styled.tagname으로 만든 컴포넌트는 styled() 함수로 바로 상속하면 되고, Styled Components를 사용하지 않고 직접 만든 컴포넌트에는 클래스 이름을 내려준 후에 styled() 함수로 상속해야 함


스타일 재사용: css 함수

  • 중복되는 CSS 코드들을 변수처럼 저장해서 여러 번 다시 사용하고 싶을 때 주로 사용됨
// 똑같은 코드를 두번 작성하고 있음
import styled from 'styled-components';

const SIZES = {
  large: 24,
  medium: 20,
  small: 16
};

const Button = styled.button`
  ...
  font-size: ${({ size }) => SIZES[size] ?? SIZES['medium']}px;
`;

const Input = styled.input`
  ...
  font-size: ${({ size }) => SIZES[size] ?? SIZES['medium']}px;
`;

// css 함수 사용
import styled, { css } from 'styled-components';

const SIZES = {
  large: 24,
  medium: 20,
  small: 16
};

const fontSize = css`
  font-size: ${({ size }) => SIZES[size] ?? SIZES['medium']}px;
`;

const Button = styled.button`
  ...
  ${fontSize}
`;

const Input = styled.input`
  ...
  ${fontSize}
`;
  • 일반적인 템플릿 리터럴을 쓰는 게 아니라 css라는 태그 함수를 붙여서 씀
profile
아이디어와 구현을 좋아합니다!

0개의 댓글