styled-components 활용팁

dev_hobin·2021년 12월 5일
0


styled-components를 사용하면서 겪었던 시행착오들을 케이스로 나누어 더 좋은 방법이라고 생각한 것들을 정리한 것들입니다. 혹시 더 좋은 방법이 있다면 알려주시면 감사하겠습니다.

styled-components를 사용하면서 불편했던 것들

1. styled-component는 semantic 태그를 알아보기 힘들다

const Post = ({ title, nickname, date, children }) => {
  return (
    <Container>
      <Title>{title}</Title>
      <Content>{children}</Content>
      <InfoContainer>
	<RegistrationDate>{date}</RegistrationDate>
	<Writer>{nickname}</Writer>
      </InfoContainer>
    </Container>
  );
};

const Container = styled.article` ... `;
const Title = styled.article` ... `;
const Content = styled.article` ... `;
const InfoContainer = styled.div` ... `;
const RegistrationDate = styled.aside` ... `;
const Writer = styled.aside` ... `;

태그들을 모두 styled-component로 관리하면 스타일을 태그별로 세세하게 나눠서 관리할 수 있지만 위와 같은 코드를 봤을 때 어떤 semantic 태그를 썻는지 알아보기 힘든게 나에게는 불편하게 느껴졌고 이미 컴포넌트로 나뉘어져 있는데 그 안의 태그 하나하나 styled-component로 만드는 과정도 귀찮게 느껴졌다.

그래서 semantic 태그는 최대한 살리고 스타일을 위한 태그들만 styled-component로 관리하는 방법을 생각해보았다

const Post = ({ title, nickname, date, children }) => {
  return (
    <StyledArticle> // semantic 태그이기 때문에 article 태그인 것을 명시해주었다
      <h2 className="title">{title}</h2>
      <p className="content">{children}</p>
      <InfoContainer>
	<aside className="registration-date">{date}</aside>
	<aside className="nickname">{nickname}</aside>
      </InfoContainer>
    </StyledArticle>
  );
};

const InfoContainer = styled.div` ... `;
const StyledArticle = styled.article`
  .title { ... }
  ${InpoContainer} { ... }
  .registration-date { ... }
  .nickname { ... }
`;

위와 같이 semantic 태그를 최대한 살렸을 경우 나에겐 이 컴포넌트의 핵심적인 내용이 무엇인지 더 잘 보이게 느껴졌다.

StyledArticle 컴포넌트 같은 경우에는 semantic 태그지만 하위 컴포넌트의 스타일을 관리할 부모 컴포넌트 역할을 할 것이기 때문에 styled-component로 만들어주었고 semantic 태그의 스타일 관리는 className을 활용했다

2. 한 컴포넌트의 스타일이 3가지 이상으로 나뉠 때 스타일을 분기하여 적용하는 것이 까다롭다

const Button = ({ type, $type, onClick, children }) => {
  return (
    <StyledButton type={type || 'button'} $type={$type} onClick={onClick}>
      {children}
    </StyledButton>
  );
};

const StyledButton = styled.button`
  background: ${({ $type }) => $type === 'PRIMARY' && 'palevioletred'};
  background: ${({ $type }) => $type === 'SECONDARY' && 'white'};
  background: ${({ $type }) => $type === 'ANOTHER' && 'coral'};

  color: ${({ $type }) => $type === 'PRIMARY' && 'white'};
  color: ${({ $type }) => $type === 'SECONDARY' && 'palevioletred'};
  color: ${({ $type }) => $type === 'ANOTHER' && 'white'};

  border: ${({ $type }) => $type === 'PRIMARY' && '2px solid palevioletred'};
  border: ${({ $type }) => $type === 'SECONDARY' && '2px solid palevioletred'};
  border: ${({ $type }) => $type === 'ANOTHER' && '2px solid coral'};

  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border-radius: 3px;
`;

지금보면 상당히 이상하게 짠 코드지만 처음 styled-component를 사용할 때는 컴포넌트가 받은 prop을 그대로 사용하거나 true, false로 구분하여 스타일을 다르게주는 방식에 갇혀있었기 때문에 이런 식으로 작성했었다.

분명히 더 좋은 방법이 있을것이라고 생각하여 공식 문서를 찾아보니 바로 해결할 수 있는 방법이 있었다.

const StyledButton = styled.button`
  ${({ $type }) => {
    switch ($type) {
      case 'PRIMARY':
        return css`
          background: palevioletred;
          color: white;
          border: 2px solid palevioletred;
        `;
      case 'SECONDARY':
        return css`
          background: white;
          color: palevioletred;
          border: 2px solid palevioletred;
        `;
      case 'ANOTHER':
        return css`
          background: coral;
          color: white;
          border: 2px solid coral;
        `;
      default:
        return css`
          background: palevioletred;
          color: white;
          border: 2px solid palevioletred;
        `;
    }
  }}
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border-radius: 3px;
`;

switch문을 사용하여 더 괜찮은 코드를 작성 할 수 있었고 switch 문 자체가 약간 복잡하게 코드를 작성하게 되는거 같아서 더 깔끔하게 코드를 작성할 수 없을까 찾다가 열거형을 쓰는 방법을 찾았다

const BUTTON_TYPE = {
  PRIMARY: css`
    background: palevioletred;
    color: white;
    border: 2px solid palevioletred;
  `,
  SECONDARY: css`
    background: white;
    color: palevioletred;
    border: 2px solid palevioletred;
  `,
  ANOTHER: css`
    background: coral;
    color: white;
    border: 2px solid coral;
  `,
};

const Button = ({ type, $type, onClick, children }) => { ... };

const StyledButton = styled.button`
  ${({ $type }) => BUTTON_TYPE[$type]}

  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border-radius: 3px;
`;

위의 코드가 컴포넌트 파일에 들어왔을 때 어떤 타입이 있는지, 어떤 스타일이 적용되는지 바로 한눈에 볼 수 있고 아래에 styled-component에서 switch문을 사용한 것보다 더 깔끔한 형태로 코드가 작성되기 때문에 훨씬 괜찮다고 느꼈다.

3. 중첩된 컴포넌트들간의 스타일을 적용하기 까다롭다

const App = () => {
  return (
    <Link href="#">
      <svg viewBox="0 0 20 20" className="icon">
        <path d="M10 15h8c1 0 2-1 2-2V3c0-1-1-2-2-2H2C1 1 0 2 0 3v10c0 1 1 2 2 2h4v4l4-4zM5 7h2v2H5V7zm4 0h2v2H9V7zm4 0h2v2h-2V7z" />
      </svg>
      <Label className="label">Hovering my parent changes my style!</Label>
    </Link>
  );
};

const Link = styled.a`
  display: flex;
  align-items: center;
  padding: 5px 10px;
  background: papayawhip;
  color: palevioletred;
	
  .icon {
    flex: none;
    transition: fill 0.25s;
    width: 48px;
    height: 48px;
  }

  :hover {
    .icon {
      fill: rebeccapurple;
    }
  }
`;

const Label = styled.span` ... `;

위의 코드는 styled-component에서 다른 컴포넌트를 참조하는 방법을 모를 경우 스타일을 어떻게 줄 지 고민하다가 컴포넌트들에 className을 주어 스타일을 주는 방식으로 문제를 해결하게 되는 코드이다. 틀렸다고 볼 수는 없지만 라이브러리에서 제공하는 훨씬 더 우아한 방법이 있다

const App = () => {
  return (
    <Link href="#">
      // 다른 컴포넌트를 참조하는 기능을 활용하기 위해 styled-component로 변경
      <Icon viewBox="0 0 20 20">
        <path d="M10 15h8c1 0 2-1 2-2V3c0-1-1-2-2-2H2C1 1 0 2 0 3v10c0 1 1 2 2 2h4v4l4-4zM5 7h2v2H5V7zm4 0h2v2H9V7zm4 0h2v2h-2V7z" />
      </Icon>
      <Label>Hovering my parent changes my style!</Label>
    </Link>
  );
};

const Link = styled.a`
  display: flex;
  align-items: center;
  padding: 5px 10px;
  background: papayawhip;
  color: palevioletred;
`;

const Icon = styled.svg`
  flex: none;
  transition: fill 0.25s;
  width: 48px;
  height: 48px;
  // 상위 컴포넌트를 참조하여 자기 자신의 스타일을 관리
  ${Link}:hover & { 
    fill: rebeccapurple;
  }
`;

const Label = styled.span` ... `;
profile
무엇을 기억할지 고민하는 것이 공부다

0개의 댓글