Skip to content

styled components

aejijeon edited this page Apr 25, 2021 · 10 revisions

(์ž‘์„ฑ์ž: AEJIJEON)

styled-components๋ž€?

CSS-in-JS styling์„ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, ํ˜„์กดํ•˜๋Š” CSS in JS ๊ด€๋ จ ๋ฆฌ์•กํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ค‘ ๊ฐ€์žฅ ์ธ๊ธฐ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.(emotion, styled-jsx...)
cssํŒŒ์ผ์„ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ  javascript ์ฝ”๋“œ ๋‚ด์—์„œ ์ผ๋ฐ˜ css๋กœ ์ปดํฌ๋„ŒํŠธ์˜ ์Šคํƒ€์ผ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
styled-components ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋จผ์ € Tagged Template Literal์ด๋ผ๋Š” ๋ฌธ๋ฒ•์„ ์‚ดํŽด๋ณด์ž.

Template Literal๋ฌธ๋ฒ•์€ ๋ฌธ์ž์—ด ์กฐํ•ฉ์„ ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ES6 ๋ฌธ๋ฒ•์œผ๋กœ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ๋ฌธ๋ฒ•์ด๋‹ค.

console.log(`hello ${user}`);

์œ„์™€ ๊ฐ™์ด ๋ฐฑํ‹ฑ(``)์„ ์‚ฌ์šฉํ•ด์„œ ๋ฌธ์ž์—ด ์•ˆ์— javascript values(variable, object, function, etc.)๋ฅผ ํฌํ•จ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.
๊ทธ๋ ‡๋‹ค๋ฉด Tagged Template Literal ๋ฌธ๋ฒ•์€ ๋ฌด์—‡์ผ๊นŒ?
Tagged Template Literal์€ Template Literal์„ ์ด์šฉํ•˜์—ฌ ํ•จ์ˆ˜์˜ ์ธ์ž๋ฅผ parsing ํ•˜์—ฌ ๋„˜๊ฒจ์ฃผ๋Š” ๊ฒƒ์ด๋‹ค. Template Literal์„ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๋ฐฑํ‹ฑ ์•ˆ์— ๋„ฃ์–ด์ค€ javascript values๋ฅผ ์กฐํšŒํ•˜๊ณ  ์‹ถ์„ ๋•Œ Tagged Template Literal ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์˜ˆ์‹œ๋ฅผ ์‚ดํŽด๋ณด์ž.

const var1 = "first";
const var2 = "second";
function approachToVars(texts, ...values) {
  //ํŒŒ๋ผ๋ฏธํ„ฐ์˜ rest ๋ฌธ๋ฒ• ์‚ฌ์šฉ
  console.log(texts, values); // [ 'blabla ', ' and ', ' blabla' ] [ 'first', 'second' ]
}
approachToVars`blabla ${var1} and ${var2} blabla`; // Template Literal๋ฅผ ์ด์šฉํ•˜์—ฌ 
// ํ•จ์ˆ˜์˜ ์ธ์ž๋ฅผ parsing(${} ๋ถ€๋ถ„ ๋ถ„๋ฆฌ)ํ•˜์—ฌ ๋„˜๊ฒจ์คŒ
function sample(texts, ...ftns) {
  console.log(texts);
  console.log(ftns);
  ftns.forEach((ftn) => ftn());
}
//${}์„ ํ†ตํ•˜์—ฌ ํ•จ์ˆ˜๋ฅผ ๋„˜๊ฒจ์ฃผ๋ฉด ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ sampleํ•จ์ˆ˜์—์„œ ํ˜ธ์ถœ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Œ
sample`
  ์ œ๋ชฉ: ${() => console.log("์ œ๋ชฉ
  ๋‚ด์šฉ" >")}
  ๋‚ด์šฉ: ${() => console.log("๋‚ด์šฉ")}
`;
// ์ถœ๋ ฅ:
// ["\n  ์ œ๋ชฉ: ", "\n  ๋‚ด์šฉ: ", "\n"]
// [ [Function (anonymous)], [Function (anonymous)] ]
// ์ œ๋ชฉ
// ๋‚ด์šฉ

styled-components์—์„œ๋Š” ์ด๋Ÿฌํ•œ Tagged Template Literal ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•ด์„œ javascript values์™€ ํ•จ๊ป˜ stylingํ•  ์ˆ˜ ์žˆ๊ณ , ๋‹ค์Œ ์•„๋ž˜ ์˜ˆ์‹œ ์ฝ”๋“œ์™€ ๊ฐ™์ด ์ปดํฌ๋„ŒํŠธ์˜ props๋ฅผ ์ฝ์–ด์™€์„œ styling ๊ฐ€๋Šฅํ•˜๋‹ค.

//component์˜ props๋ฅผ ์ฝ์–ด์˜ด
const styledDev = styled`
    background: ${(props) => props.color}; 
`;

styled-components ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜

npm install โ€“save styled-components

styled-components ์‚ฌ์šฉ ์˜ˆ์‹œ

  • ์ฝ”๋“œ
import React from "react";
import styled from "styled-components";

const Circle = styled.div`
  width: 10rem;
  height: 10rem;
  background: blue;
  border-radius: 50%;
`;

function App() {
  return <Circle />;;
}

export default App;

์œ„ ์ฝ”๋“œ์—์„œ์ฒ˜๋Ÿผ javascript ํŒŒ์ผ์—์„œ ์Šคํƒ€์ผ์„ ์ž…๋ ฅํ•จ๊ณผ ๋™์‹œ์— ํ•ด๋‹น ์Šคํƒ€์ผ์„ ๊ฐ€์ง„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ณ , styled.input, styled.button๊ณผ ๊ฐ™์ด html tag๋“ค์„ ๊ฐ€์ง€๊ณ  ์Šคํƒ€์ผ๋งํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ํ™”๋ฉด
    ์›

API Reference

styled-components ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ์—ฌ๋Ÿฌ api๋“ค์„ ์‚ดํŽด๋ณด์ž.

1. styled

์Šคํƒ€์ผ์„ ์ž…ํž tagname๋‚˜ component๋ฅผ ์ธ์ž๋กœ ์ „๋‹ฌ(optional)ํ•˜๋ฉด ์ผ๋ฐ˜ css์ฝ”๋“œ๋ฅผ ํฌํ•จํ•˜๋Š” Tagged Template literal์„ ๋ฐ›์•„์„œ StyledComponent๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ํ•จ์ˆ˜๋ฅผ return ํ•ด ์คŒ

  • ์ฝ”๋“œ
import React from "react";
import styled from "styled-components";

// styled.button์€ styled(button)์˜ ์•ฝ์–ด
// button tag๋ฅผ ๊ฐ€์ง€๊ณ  ์Šคํƒ€์ผ๋งํ•œ StyleComponent๋ฅผ ๋งŒ๋“ฆ
const Button = styled.button`
  background: palevioletred;
  border-radius: 3px;
  border: none;
  color: white;
`;
// ์œ„์—์„œ ๋งŒ๋“  Button ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ€์ง€๊ณ  ์Šคํƒ€์ผ๋ง
// ๊ธฐ์กด Button ์ปดํฌ๋„ŒํŠธ์˜ ์Šคํƒ€์ผ์„ ์ƒˆ๋กœ์šด ์Šคํƒ€์ผ๋กœ ๋ฎ์Œ
const RedButton = styled(Button)`
  background: red;
`;

function App() {
  return (
    <>
      <Button>Default Button</Button>

      <RedButton>Red Button</RedButton>
    </>
  );
}

export default App;
  • ํ™”๋ฉด
    ๋ฒ„ํŠผ

2. css

(case 1)styled ํ•จ์ˆ˜์—์„œ ๊ตฌํ˜„ํ•œ css์ฝ”๋“œ์—์„œ ${}์„ ์‚ฌ์šฉํ•ด์„œ props๋ฅผ ๋„˜๊ฒจ๋ฐ›์•„ ์Šคํƒ€์ผ๋งํ•˜๊ฑฐ๋‚˜ (case 2)css์ฝ”๋“œ๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ ๋ถ„๋ฆฌํ•œ ์ฝ”๋“œ๋ฅผ styledํ•จ์ˆ˜ ๋‚ด๋ถ€์— ${}์„ ์‚ฌ์šฉํ•ด์„œ ๋‹ค์‹œ ํฌํ•จ์‹œํ‚ฌ ๊ฒฝ์šฐ์— css function๋ฅผ ๋ถˆ๋Ÿฌ์™€์„œ ์‚ฌ์šฉ

  • example of case 1
import styled, { css } from "styled-components";

const Circle = styled.div`
  width: 5rem;
  height: 5rem;
  background: red;
  border-radius: 50%;
  ${(props) =>
    props.huge &&
    css`
      width: 10rem;
      height: 10rem;
    `}
`;

function App() {
  return <Circle />;
}
export default App;

Circle component์— props๋ฅผ ๋„˜๊ฒจ์ฃผ์–ด props๋ฅผ ๊ฐ€์ง€๊ณ  ์Šคํƒ€์ผ๋งํ•ด์ฃผ์—ˆ๋‹ค. ์œ„์™€ ๊ฐ™์ด css ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ๊ทธ ์Šคํƒ€์ผ ๋‚ด๋ถ€์—์„œ๋„ ๋‹ค๋ฅธ props๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

  • example of case 2
import styled, { css } from "styled-components";

const colorStyles = css`
  ${({ color }) => {
    return css`
      background: ${color};
    `;
  }}
`;
const Circle = styled.div`
  width: 5rem;
  height: 5rem;
  ${colorStyles};
  border-radius: 50%;
  ${(props) =>
    props.huge &&
    css`
      width: 10rem;
      height: 10rem;
    `}
`;
  
function App() {
  return <Circle />;;
}
export default App;

3. ThemeProvider

styled-components์—์„œ ์ œ๊ณตํ•˜๋Š” ํ…Œ๋งˆ๋ฅผ ์œ„ํ•œ (helper) ์ปดํฌ๋„ŒํŠธ์ด๋‹ค. styled-components๋กœ ๋งŒ๋“œ๋Š” ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์—์„œ ์กฐํšŒํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ „์—ญ์ ์ธ ๊ฐ’(theme)์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ์˜ˆ์‹œ
function App() {
  return (
    <ThemeProvider
      theme={{
        palette: {
          blue: "#228be6",
          gray: "#495057",
          pink: "#f06595",
        },
      }}
    >
      BUTTON>
    </ThemeProvider>
  );
}

ThemeProvider ์ปดํฌ๋„ŒํŠธ์˜ theme props๋กœ object๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ThemeProvider ๋‚ด๋ถ€์— ๋ Œ๋”๋ง๋œ styled components์—์„œ ๋ Œ๋”๋ง ๋œ ์กฐํšŒํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ theme์„theme prop๊ฐ€ ๋‚ด๋ถ€์— prop์ด ๋ Œ๋”๋ง ๋œ ์ปดํฌ๋„ŒํŠธ๋“ค์—๊ฒŒ ์ž๋™์œผ๋กœ ์ „๋‹ฌ๋จ) *์ฃผ์˜ ThemeProvider ๋‚ด๋ถ€๋Š” ํ•˜๋‚˜์˜ ๋ฆฌ์•กํŠธ ์—˜๋ฆฌ๋จผํŠธ๋กœ ๊ฐ์‹ธ์ ธ ์žˆ์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‚ด๋ถ€์— ์—ฌ๋Ÿฌ components๋ฅผ ๋ Œ๋”๋ง ์‹œ <></> ์œผ๋กœ ๊ฐ์‹ธ์ฃผ์–ด์•ผ ํ•œ๋‹ค.

  • theme ์กฐํšŒ ์˜ˆ์‹œ
const StyledButton = styled.button`
/_ ํฌ๊ธฐ _/
/_ ์ƒ‰์ƒ _/
${(props) => {
  const selected = props.theme.palette.blue;
  return css`
    background: ${selected};
    &:hover {
      background: ${lighten(0.1, selected)};
    }
    &:active {
      background: ${darken(0.1, selected)};
    }
  `;
}}
/_ ๊ธฐํƒ€ _/
`;

ThemeProvider ๋‚ด๋ถ€๋Š” ํ•˜๋‚˜์˜ ๋ฆฌ์•กํŠธ ์—˜๋ฆฌ๋จผํŠธ๋กœ ๊ฐ์‹ธ์ ธ ์žˆ์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‚ด๋ถ€์— AppBlock ๊ณผ Dialog components๋ฅผ ๋ Œ๋”๋ง ์‹œ <> ์œผ๋กœ ๊ฐ์‹ธ์ฃผ์–ด์•ผ ํ•จ

4. keyframes

์• ๋‹ˆ๋ฉ”์ด์…˜์— ๋Œ€ํ•œ keyframes๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” method์ด๋‹ค.
keyframes๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š” TaggedTemplateLiteral์„ ์ธ์ž๋กœ ๋ฐ›์•„์„œ Keyframes model์„ returnํ•ด์ค€๋‹ค.

๋‹ค์Œ ์˜ˆ์‹œ๋ฅผ ์‚ดํŽด๋ณด์ž.

import styled from "styled-components";

const FadeInButton = styled.button`
  @keyframes fadeIn {
    0% {
      opacity: 0;
    }
    100% {
      opacity: 1;
    }
  }
  animation: 5s fadeIn ease-out;
  width: 100px;
  height: 20px;
`;

function App() {
  return <FadeInButton />;;
}
export default App;

์ด์ฒ˜๋Ÿผ keyframes method๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ถ”๊ฐ€ํ•  ์ปดํฌ๋„ŒํŠธ์˜ TaggedTemplateLiteral์— ์ผ๋ฐ˜ css ์ฝ”๋“œ๋กœ keyframes๋ฅผ ์ •์˜ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
ํ•˜์ง€๋งŒ ๋™์ผํ•œ keyframes๋ฅผ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ๋„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ์ค‘๋ณต๋œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.

const fadeIn = css`
  @keyframes identifier {
    0% {
      opacity: 0;
    }
    100% {
      opacity: 1;
    }
  }
`;
const FadeInButton = styled.button`
  ${fadeIn}
  animation: 5s identifier ease-out;
  width: 100px;
  height: 20px;
`;

์ด ์ฝ”๋“œ์—์„œ๋Š” fadeIn์ด๋ผ๋Š” css ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค์–ด์„œ ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์— ํฌํ•จ์‹œ์ผœ์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ animation property์— ํ•ด๋‹น keyfremes์˜ identifier์„ ๋”ฐ๋กœ ๋ช…์‹œํ•ด์ค˜์•ผ ํ•œ๋‹ค.

import styled, { keyframes } from "styled-components";

const fadeIn = keyframes`
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
`;

const FadeInButton = styled.button`
  animation: 1s ${fadeIn} ease-out;
`;

function App() {
  return <FadeInButton />;
}
export default App;

์ด์™€ ๊ฐ™์ด keyframes method๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด keyframes model์„ ์—ฌ๋Ÿฌ styled components์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ  ์ด์ „ ์˜ˆ์‹œ์™€ ๊ฐ™์ด keyframes์˜ identifier์„ ๋”ฐ๋กœ ๋ช…์‹œํ•ด์ค„ ํ•„์š”๊ฐ€ ์—†๊ฒŒ ๋œ๋‹ค.

๊ธฐํƒ€

styled components ์•ˆ์— ์žˆ๋Š” h3, p์™€ ๊ฐ™์€ ํƒœ๊ทธ๋“ค์„ ์Šคํƒ€์ผ๋งํ•  ๋•Œ ๋”ฐ๋กœ ๋”ฐ๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด์„œ ์Šคํƒ€์ผ๋ง ํ•˜์ง€ ์•Š๊ณ  styled-components์—์„œ Nested CSS ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

const Block = styled.div`
  width: 100rem;
  height: 100rem;
  //    {} ์•ˆ์— styling ์ถ”๊ฐ€...
  h3 {
  }
  p {
  }
`;

๋งˆ๋ฌด๋ฆฌ

styled-components ์‚ฌ์šฉ ์˜ˆ์ œ ๋ณด๋Ÿฌ๊ฐ€๊ธฐ(codesandbox)
(์ฝ”๋“œ์— ์ฃผ์„ ๋‹ฌ์•„๋†จ์Šต๋‹ˆ๋‹ค. ์ฒœ์ฒœํžˆ ์ฝ์–ด๋ณด๋ฉด styled-components์— ๋Œ€ํ•ด ์ž˜ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์„ ๊ฒ๋‹ˆ๋‹ค.)

references

"๋ฒจ๋กœํผํŠธ์™€ ํ•จ๊ป˜ํ•˜๋Š” ๋ชจ๋˜ ๋ฆฌ์•กํŠธ", 2021๋…„ 04์›” 25์ผ ์ ‘์†, https://react.vlpt.us/styling/03-styled-components.html
https://styled-components.com/docs/api#primary