styled-components를 사용하면서 겪었던 시행착오들을 케이스로 나누어 더 좋은 방법이라고 생각한 것들을 정리한 것들입니다. 혹시 더 좋은 방법이 있다면 알려주시면 감사하겠습니다.
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을 활용했다
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문을 사용한 것보다 더 깔끔한 형태로 코드가 작성되기 때문에 훨씬 괜찮다고 느꼈다.
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` ... `;