HTML&CSS

CSS 작업 방식에 대한 고찰 (feat. Styled-Components)

yoonjong Park 2022. 11. 22.

여러 자료들(블로그, 클론코딩)에서 하는 말이 

모두 다르다. 그래서 어떤 방식이 더 나은지 잘 모르겠다.

CSS module 방식도 있고, tailwind 도 있고, styled component 도 있다.  이 것 말고도 많은데, 주로 사용하는 것은 그래도 이정도니까... 

전반적으로 요즘은 styled component VS tailwind 정도로 압축되어가고 있는 것 같다.

 

css in js VS utility-first css 방식의 차이라서,

1. 렌더링 성능을 좀더 우선시 하는 사람이면, tailwind (순수한 css로 봐야 하기 때문에 렌더링 이후에 다시 js로 적용하는 방식에 비해서 속도가 빠르다.)

2. 개발자의 유지보수를 좀 더 우선시 하는 사람이면 Styled component (emotion 포함시키도록 하자. 얘도 영향 받았다고 공식문서에 써있다.)

 

Styled Components 의 작업방식

import styled from "styled-components";

const Father = styled.div`
  display: flex;
`;

const BoxOne = styled.div`
  background-color: teal;
  width: 100px;
  height: 100px;
`;

const BoxTwo = styled.div`
  background-color: tomato;
  width: 100px;
  height: 100px;
`;

const Text = styled.span`
  color: white;
`;

function App() {
  return (
    <Father>
      <BoxOne>
        <Text>Hello</Text>
      </BoxOne>
      <BoxTwo />
    </Father>
  );
}

export default App;

 

위와 같이 작업이 가능하다. Styled Components라는 말 그대로 Style을 다루는 컴포넌트를 만드는 것이다.

위 코드를 보면, 약간 비효율적으로 보이는 게 width, height 값, 그리고 background-color값을 먹이는 것에 대한 것에 대해 문제가 생긴다. 이 부분에 대해 계속 개선해보자.

그리고 immet 자동 완성 도구가 자꾸 JavaScript로 해줘서 Camel 형태로 된다는 점이 조금 짜증나긴 한다. 뭔가 방법이 있으려나...?

 

import styled from "styled-components";

const Father = styled.div`
  display: flex;
`;

const Box = styled.div`
  background-color: ${props => props.bgColor};
  width: 100px;
  height: 100px;
`;

const Text = styled.span`
  color: ${props => props.fontColor};
`;

function App() {
  return (
    <Father>
      <Box bgColor="teal">
        <Text fontColor="white">Hello</Text>
      </Box>
      <Box bgColor="tomato" />
    </Father>
  );
}

export default App;

개선하면 위와 같이 개선이 가능하다.

결국, 리액트의 Component는 Props에 대한 이해에서 모든 걸 출발한다.
Props를 이용해서 Style에 데이터를 넘겨주는 것이다.

 

이 걸 좀 더 효율적으로 사용하는 방법을 찾아보면,

결국 컴포넌트의 합성 및 추출 을 이용하는 방법이 있다. 그래서 아래와 같이 작업하게 된다.

import styled from "styled-components";

const Father = styled.div`
  display: flex;
`;

const Box = styled.div`
  background-color: ${props => props.bgColor};
  width: 100px;
  height: 100px;
`;

const Circle = styled(Box)`
  border-radius: 50px;
`;

const Text = styled.span`
  color: ${props => props.fontColor};
`;

function App() {
  return (
    <Father>
      <Box bgColor="teal">
        <Text fontColor="white">Hello</Text>
      </Box>
      <Circle bgColor="tomato" />
    </Father>
  );
}

export default App;

 

속성을 넣는 방법

const Input = styled.input.attrs({ required: true, maxLength: 10 })`
  background-color: tomato;
`;

function App() {
  return (
    <Father>
      <Input />
      <Input />
      <Input />
      <Input />
    </Father>
  );
}

위와 같이 input.attrs 에 매개변수로 객체를 넣어서 해결한다.

렌더 결과

스타일드 컴포넌트 자체가 스타일도 컴포넌트로 관리하자라는 것이다.

이 부분에 좀 더 집중해보자.

const roationAnimation = keyframes`
  0% {
    transform: rotate(0deg);
    border-radius: 0px;
  }
  50% {
    border-radius: 100px; 
  }
  100% {
    transform: rotate(360deg);
    border-radius: 0px;
  }
`;

const Box = styled.div`
  height: 200px;
  width: 200px;
  background-color: tomato;
  display: flex;
  justify-content: center;
  align-items: center;
  animation: ${roationAnimation} 1s linear infinite;
  span {  /* Box 컴포넌트 내부 span 태그 조작 */
    font-size: 36px;
    &:hover {
      /* & = span*/
      font-size: 98px;

    }
    &:active {
      opacity: 0;
  }
`;

function App() {
  return (
    <Wrapper>
      <Box>
        <span>😃</span>
      </Box>
    </Wrapper>
  );
}

위와 같은 방식의 조작도 가능하다.

keyframe 룰을 변수로 저장해두고 그걸 컴포넌트에 넣는다. (중요하진 않다. 스킵하자)

Box 컴포넌트 내부에서 span 태그를 조작가능해지게 된다. 이 부분이 매우 쿨한데, span 부분 또한 컴포넌트로 분리해보자. 이 게 좀 더 컴포넌트 단위로 세분화할 수 있게 된다.

const Emoji = styled.span`
  font-size: 36px;
`;

const Box = styled.div`
  height: 200px;
  width: 200px;
  background-color: tomato;
  display: flex;
  justify-content: center;
  align-items: center;
  animation: ${roationAnimation} 1s linear infinite;
  ${Emoji} {  /* 스타일 컴포넌트 자체를 조작 : 가장 쿨한 부분. */
    font-size: 36px;
    &:hover {
      /* & = span*/
      font-size: 98px;

    }
    &:active {
      opacity: 0;

  }
`;

function App() {
  return (
    <Wrapper>
      <Box>
        <Emoji>😃</Emoji>
      </Box>
      <Emoji>💗</Emoji>
    </Wrapper>
  );
}

그러면 위와 같이 조작이 가능해진다.

Box > Emoji 이런식으로 종속되게 구조화할 수 있게 되는 것이다.

당연히 Emoji (Text) 컴포넌트는 따로 사용도 가능하고, Box 내부에 사용될 때는 다른 방식으로 제어하는 것도 가능해진다.

태그만 바꾸어주는 방식

아래와 같이 태그만 바꾸어줄 수도 있다.

      <Box bgColor="green">
        <Emoji>😃</Emoji>
      </Box>

      <Box as="a" bgColor="green">

결과 : 클래스명이 동일하게 들어가는 것을 보면, 스타일은 동일하게 적용되고, 태그만 바뀐다는 것을 알 수 있다.

 

당연한 이야기지만, 컴포넌트 이기 때문에 export해서 가져다 사용하면 된다.
이 효용성 때문에 좀 더 JavaScript 개발자가 다루기 더 유용함.
이에 효율성이 좀 더 증가하는 듯.

 

 

// index.js

import React from "react";
import ReactDOM from "react-dom/client";
import { ThemeProvider } from "styled-components"; // ThemeProvider를 import 해줌
import App from "./App";

const root = ReactDOM.createRoot(document.getElementById("root"));

const darkTheme = { // added
  textColor: "whitesmoke",
  backgroundColor: "#111",
};

const lightTheme = { // added
  textColor: "#111",
  backgroundColor: "whitesmoke",
};

root.render(
  <React.StrictMode>
    <ThemeProvider theme={darkTheme}>  // App을 감싸준다.
      <App />
    </ThemeProvider>
  </React.StrictMode>
);

위와 같이 하고나면,

// App.js

import styled from "styled-components";

const Title = styled.h1`
  color: ${props => props.theme.textColor};  // props로 theme에 접근이 바로 가능해진다. Cool !!
`;

const Wrapper = styled.div`
  display: flex;
  width: 100vh;
  height: 100vh;
  justify-content: center;
  align-items: center;
  background-color: ${props => props.theme.backgroundColor};  // added
`;

function App() {
  return (
    <Wrapper>
      <Title>goodJob</Title>
    </Wrapper>
  );
}

export default App;

Index.js Theme Props를 변경해면 위와 같이 변경되는 것을 확인할 수 있다.

참고자료

1. 노마드코더

2. 드림코딩엘리

2. 

https://react.vlpt.us/styling/03-styled-components.html

 

3. styled-components · GitBook

03. styled-components 이번에 배워볼 기술은 CSS in JS 라는 기술입니다. 이 문구가 뜻하는 그대로, 이 기술은 JS 안에 CSS 를 작성하는 것을 의미하는데요, 우리는 이번 튜토리얼에서 해당 기술을 사용하

react.vlpt.us

 

 

 

댓글