코딩하는라민

[React] Styled Component (CSS-in-JS) 본문

Styling/Styled-Components

[React] Styled Component (CSS-in-JS)

코딩하는라민 2023. 3. 13. 00:40
728x90
반응형

[React] Styled Component (CSS-in-JS)

📌 CSS in JS?

CSS in JS는 기존의 CSS 스타일링의 방식과 다르게 스타일 정의를 별도의 CSS 파일이 아닌 JS 파일에 작성하는 스타일 기법을 말한다.

JS 파일에서 작성해서 바로 컴포넌트에 적용하기 때문에 손쉽게 스타일을 적용할 수 있다.

CSS in Js 라이브러리에는 Styled Components, emotion, styled-jsx 등이 있다.

이러한 CSS in JS 라이브러리 중에서 가장 많이 사용되는 것이 Styled Component 라이브러리이다.

 

📌 Styled Component

Styled Component 는 스타일링 파일을 따로 두지 않고 동일한 컴포넌트에서 컴포넌트 이름을 사용하듯 스타일을 지정할 수 있다.

클래스명을 자동으로 생성하기 때문에 CSS 스타일링이 중첩되지 않도록 사용할 수 있게 된다.

또한 JS 파일에서 사용함으로써 동적인 값들을 사용할 수 있게 된다는 장점이 있다.

 

📌 설치하기

npm i styled-components

 

📌 Styled Component 사용하기

✅ import

import styled from 'styled-components'

 

 

✅ 컴포넌트

export default function App() {
  return (
    <div>
      <Button>스타일 적용</Button>
    </div>
  );
}

위와 같은 버튼 컴포넌트에 styled component 스타일링을 적용해보자.

 

✅ 사용 방법

const [컴포넌트명] = styled.[태그명] `
    [스타일링]
`

컴포넌트 명으로 된 변수에 탬플릿 리터럴을 사용해 스타일링 코드를 적용해주는 방식으로 사용할 수 있다.

 

✅ 사용 예시

const Button = styled.button`
    display: inline-block;  
    padding: 10px 20px;
    color: #fff;
    font-weight: bold;
    background: darkblue;
    border: none;
    border-radius: 5px; 
`;

 

📌 props 적용

리액트의 컴포넌트는 props 을 전달받아 그 props 에 접근할 수 있다.

styled component 에서도 props 을 사용할 수 있는데, JS의 문법을 그대로 사용하면 된다.

 

✅ interpolation(보간법) 을 이용해 props 적용하기

export default function App() {
  return (
    <div>
      <Button offButton>취소</Button>
      <Button>확인</Button>
    </div>
  );
}
const Button = styled.button`
    display: inline-block;  
    padding: 10px 20px;
    border: none;
    border-radius: 5px;
    margin-right: 20px;
    color: #fff;
    font-weight: bold;
    background: ${ props => props.offButton ? 'gray' : 'darkblue' };
`

화살표 함수에 삼항 연산자를 이용해서 조건에 따른 스타일을 넣어준다.

props 에 offButton 이라는 속성이 있으면 배경색을 gray 로 하고, offButton 속성이 없다면 배경색을 darkblue 색상으로 한다.

 

📌 스타일 상속

styled component 를 사용해 정의된 컴포넌트를 전달하면 해당 컴포넌트 스타일을 상속해줄 수 있다.

const [컴포넌트명] = styled( [스타일컴포넌트명] )`
    [스타일링]
`

쉽게 말해서 [스타일컴포넌트] 의 스타일링을 해당 [컴포넌트] 에도 적용해주는 것이다.

그리고 나서 컴포넌트에만 적용될 고유의 [스타일링] 을 적용해주면 된다.

 

export default function App() {
  return (
    <div>
      <Button>취소</Button>
      <ButtonOrange>확인</ButtonOrange>
    </div>
  );
}

위와 같은 다크블루 색상의 두 개의 버튼이 있다고 하자.

이 버튼 중 아래쪽 버튼의 배경색으로 오렌지 색상을 적용해보자.

 

✅ 기존의 스타일링

const Button = styled.button`
    display: inline-block;  
    padding: 10px 20px;
    color: #fff;
    font-weight: bold;
    background: darkblue;
    border: none;
    border-radius: 5px;
    margin-right: 20px;
`

 

✅ 스타일 상속

const ButtonOrange = styled(Button)`
  background: orange
`

ButtonOrange 컴포넌트에 Button 컴포넌트의 속성을 적용해주고, (스타일링+버튼태그)

ButtonOrange 컴포넌트에는 배경색으로 orange 를 적용해준다.

 

📌 일반 컴포넌트를 상속받아 스타일 컴포넌트 생성

위와 같은 스타일드 컴포넌트를 상속해서 사용하는 것 뿐만 아니라 일반 컴포넌트도 상속해서 사용할 수 있다.

위의 방법과 비슷하지만 일반 컴포넌트에는 스타일드 컴포넌트로 생성되지 않은 컴포넌트여야 한다.

또 일반 컴포넌트에는 className 속성을 전달받도록 설정해야 스타일의 상속이 가능하다.

 

1️⃣ 

function Green({className, children}){
    return <button className={className}>{children}</button>
}

2️⃣ 

const ButtonGreen = styled(Green)`
    background: green;
    display: inline-block;  
    padding: 10px 20px;
    color: #fff;
    font-weight: bold;
    border: ${props => props.border || "5px solid red"};
    border-radius: 5px;
    margin-right: 20px;
`

3️⃣ 

export default function App() {
    return (
      <div>
        <ButtonGreen border="4px solid darkblue">수정</ButtonGreen>
        <Button>취소</Button>
        <ButtonOrange>확인</ButtonOrange>
      </div>
    );
}

스타일은 1️⃣→2️⃣→3️⃣ 순서로 적용된 것을 볼 수 있다.

 

ButtonGreen 컴포넌트의 border 속성이 없다면 2️⃣ 컴포넌트의 스타일이 반영된다.

export default function App() {
  return (
    <div>
      <ButtonGreen>수정</ButtonGreen>
      <Button>취소</Button>
      <ButtonOrange>확인</ButtonOrange>
    </div>
  );
}

 

📌 컴포넌트 스타일 래퍼

스타일드 컴포넌트와 컴포넌트를 구분하지 않고 컴포넌트 내부에 스타일드 컴포넌트를 적용하면 관리하기가 편해진다.

export default function App() {
    const Button = styled.button`
        display: inline-block;  
        padding: 10px 20px;
        color: #fff;
        font-weight: bold;
        background: darkblue;
        border: none;
        border-radius: 5px;
        margin-right: 20px;
    `

    return (
      <div>
        <Button>버튼</Button>
      </div>
    );
}

하지만 스타일드 컴포넌트가 많아질 경우에는 별도의 파일에 분리해서 사용하는 것이 좋다.

왜냐하면 이러한 방법은 컴포넌트가 렌더링 될 때마다 컴포넌트 스타일를 재정의하기 때문이다.

따라서 이렇게 사용하는 것은 좋지 못한 방법이다.

 

📌 중첩 규칙

다음과 같이 중첩된 태그가 있다고 하자.

export default function App() {
  return (
    <>
      <Container>
        <p>중첩 스타일링</p>
      </Container>
    </>
  );
}

스타일 미적용

Sass 에서처럼 중첩해서 스타일링을 지정해줄 수 있다.

 

✅ Container 컴포넌트 안에 있는 p 태그에 스타일 적용

const Container = styled.div`
    p{
      font-size: 20px;
      font-weight: bold;
    }
`

스타일 적용 후

 

📌 가상 셀렉터 적용

위의 경우와 마찬가지로 Sass 에서처럼 가상 셀렉터 또한 적용이 가능하다.

& 는 Sass, SCSS 에서 부모 선택자를 가리킨다.

const Container = styled.div`
    // Container 스타일 적용
    width: 250px;
    padding: 10px;
    background: yellow;

    // p 스타일 적용
    p{
      width: 150px;
      font-size: 20px;
      font-weight: bold;
      background: gray;
      color: white;
      text-align: center;
      margin: 0 auto;
        // p 에 hover 시 적용될 스타일
        &:hover{
          background: darkblue;
        }
    }

    // Container 에 hover 시 적용될 스타일
    &:hover{
      background: orange;
    }
`;

export default function App() {
  return (
    <div>
      <Container>
        <p>가상 셀렉터</p>
      </Container>
    </div>
  );
}

hover 전 스타일
hover 시 적용될 스타일

 

📌 가상 요소 적용

const Container = styled.div`
  &::before{
    content: '✅ Container 시작';
  }
  &::after{
    content: '⬆️ Container 끝';
  }

  p::before{
    content: '💅🏻';
  }
  p::after{
    content: '💅🏻';
  }
`;

export default function App() {
  return (
    <div>
      <Container>
        <p>가상 요소</p>
      </Container>
    </div>
  );
}

가상 요소 적용

 

📌 가상 클래스 적용

const Container = styled.div`
  width: 200px;
  text-align: center;
  &.border{
    border: 2px solid #000;
    border-radius: 15px;
  }
`;

export default function App() {
  return (
    <div>
      <Container className={"border"}>
        <p>가상 클래스</p>
      </Container>
    </div>
  );
}

가상 클래스 적용

 

📌 attrs

attrs 생성자를 이용하면 정적, 동적 props 를 정의할 수 있다.

이 방법을 사용하면 코드가 짧아진다.

const InputStyle = styled.input.attrs(({ size, primary }) => ({
    // 정적 코드
    className: 'Input',
    
    // 동적 코드
    color: primary || 'green',
    padding: size || '16px',
    margin: size || '16px'
  }) 
)` 
  display: block;
  
  border: 2px solid ${ ({color})=>color };
  padding: ${ ({size})=>size };
  margin: ${ ({size})=>size };
  border-radius: ${ ({size})=>size };
`

export default function App() {
  return (
    <div>
      <InputStyle type="text" size="20px" placeholder="텍스트를 입력하세요." />
      <InputStyle type="text" size="15px" primary="plum" placeholder="텍스트를 입력하세요..." />
    </div>
  );
}

이 방법을 이용하면 props 에 따라 스타일링이 달라진다.

 

📌 mixin

mixin 은 css 에서 공통 클래스를 통해서 공통 스타일을 적용하는 방식과 같다.

css 함수를 통해서 공통 스타일 속성을 적용해줄 수 있다.

import styled, {css} from 'styled-components';

// boxContainer 라는 클래스를 가진 Box 컴포넌트에 적용할 스타일
const Box = styled.div`
  &.boxContainer{
    width: 250px;
    background: orange;
    padding: 10px;
  }
`

// mixin 할 스타일
// 해당 변수를 인터폴레이션하면 스타일이 공통으로 적용된다
const mixinStyle = css`
  width: 100px;
  padding: 20px 10px;
  color: white;
  margin: 10px auto;
  text-align: center;
  border-radius: 20px;
`

// BlueBox에 적용될 스타일
// mixinStyle 삽입
const BlueBox = styled.div`
  ${mixinStyle}
  background: blue;
`

// GrrenBox에 적용될 스타일
// mixinStyle 삽입
const GrrenBox = styled.div`
  ${mixinStyle}
  background: green;
`

export default function App() {
  return (
    <div>
      <Box className="boxContainer">
        <BlueBox>blue</BlueBox>
        <GrrenBox>green</GrrenBox>
      </Box>
    </div>
  );
}

 

📌 애니메이션

styled component 에서 애니메이션을 사용하려면 keyframs 를 import 해서 사용해야한다.

import styled, {keyframs} from 'styled-components';

 

const scaleHeart = keyframes`
  0%{ transform: translateX(0) }
  50%{ transform: translateX(100px) }
  100%{ transform: translateX(0) }
`;

const Heart = styled.p`
  font-size: 100px;
  animation: ${scaleHeart} 5s infinite ;
`;

export default function App() {
  return (
    <div>
      <Heart>💕</Heart>
    </div>
  );
}

왔다 갔다~

 

📌 전역 스타일

전역 스타일 지정은 styled component 의 createGlobalStyle 함수를 import 해줌으로써 사용할 수 있다.

이렇게 생성된 전역 스타일 컴포넌트를 적용하고자 하는 컴포넌트의 최상위에 추가해주면 하위에 있는 모든 컴포넌트에  지정해준 전역 스타일이 일괄 적용된다.

 

📄 GlobalStyle.js

import { createGlobalStyle } from 'styled-components';

const GlobalStyle = createGlobalStyle`
    * {
        font-family: 'Roboto', sans-serif;
        margin: 20px;
        padding: 0;
    }

    body {
        box-sizing: border-box;
        background: gray;
    }
`;

export default GlobalStyle;

 

📄 App.js

글로벌 스타일 컴포넌트를 루트 최상위에 넣어주면 하위의 컴포넌트들에게 글로벌 스타일이 적용된다.

import styled from 'styled-components';
import GlobalStyle from './GlobalStyles';

export default function App() {
  return (
    <>
      <GlobalStyle />      
      <Button>취소</Button>
      <ButtonOrange>확인</ButtonOrange>
    </>
  );
}

전역 스타일 적용

버튼 스타일링 코드

더보기
const Button = styled.button`
  display: inline-block;
  padding: 10px 20px;
  color: #fff;
  font-weight: bold;
  background: ${(props) => (props.offButton ? 'gray' : 'darkblue')};
  border: none;
  border-radius: 5px;
  margin-right: 10px;
`;

const ButtonOrange = styled(Button)`
  background: orange
`;

 

하지만,

💡 참고 💡 
게시글 작성일 기준으로 글로벌(전역) 스타일링은 v5 부터 적용되지 않는 이슈가 있다.
v3, v4 는 정상적으로 적용됨

 

📌 테마 디자인 적용하기

업데이트 예정...

 

 

 

 

📌 관련 확장 프로그램

✅ vscode-styled-components

스타일드 컴포넌트 내부 css 스타일 자동완성 기능!

 

vscode-styled-components - Visual Studio Marketplace

Extension for Visual Studio Code - Syntax highlighting for styled-components

marketplace.visualstudio.com

 

 

 

 


참고 : 멋쟁이사자처럼 4기 yamoo9 강의 자료, 스타일드 컴포넌트 공식 문서
  등을 공부하고, 간단하게 정리한 내용입니다. 잘못된 부분이나 문제되는 점이 있으면 댓글 부탁드립니다.

728x90
반응형

'Styling > Styled-Components' 카테고리의 다른 글

[React+TS] Styled-components 전역 theme 적용하기  (47) 2024.04.24