들어가기
이번 프로젝트에서는 styled-components를 사용했다.
이전에도 사용해본 적 있는 스타일링 라이브러리지만, 실질적으로 어떻게 동작하는지는 모르고 있었다. 😅
(그저 CSS-in-JS라는 것과 ``으로 감싸 스타일링 하면 된다는 매우 단순한 기본 문법만 알고 있었다.)
이번 기회에 내가 왜 많은 스타일링 프레임워크 중 styled-components를 골랐는지,
이 기술의 장단점과 동작 원리에 대해 알아보았다.
왜 도입했는가?
먼저 이세계 MBTI를 만들기 전, 곰곰 다이어리를 만들 때에도 진행바를 구현한 적이 있었다.
진행바를 동적으로 스타일링 해야 했는데, CSS module을 사용하고 있어 인라인 태그로 스타일링을 구현할 수 밖에 없었다.
인라인 스타일을 지양해야 하는 이유는 가독성이 떨어진다. 그로 인해 유지 보수가 어려워진다.
이게 가장 보편적인 이유인데, 2번째 이유도 있었다. 바로 리렌더링 문제였다.
{} === {}는 false라 리렌더링을 할 때마다 style이 들어간 객체를 이전과 다르다고 판단해 불필요한 리렌더링을 야기하기 때문이다.
물론 이것이 React의 성능에 영향을 끼치는 것은 미미하며, 상황에 맞춰 개발해야 하기 때문에 인라인 스타일을 그냥 사용했는데,
이를 계기로 해결책 중 하나였던 styled-components를 학습해보면 좋겠다고 생각이 들어 도입하게 되었다.
styled-components의 장단점
styled-components는 CSS-in-JS라는 기술을 사용하는 스타일링 라이브러리다.
CSS-in-JS는 뒤에 후술하겠지만 간단히 말해 JS 안에 CSS를 작성하는 기술이다.
이 기술에 해당되는 라이브러리로는 가장 인기가 많은 styled-components와 더불어 emotion과 steyld-jsx가 있다.
장점
1. 위에서 서술한 것과 같이 JS 표현식을 사용해 동적으로 스타일링을 할 수 있다.
이는 조건부나 컴포넌트의 어떤 특정 데이터(진행바 같은)에 기반한 스타일링을 하는데 유용하다.
2. 컴포넌트 기반 스타일링으로 가독성, 유지보수성, 재사용성이 높다.
styled-components는 컴포넌트 기반 스타일링이다. 즉 컴포넌트와 같이 스타일을 정의한다.
각 컴포넌트가 자체적인 스타일을 갖고 있어 코드와 스타일링을 이해하기 쉽고, 유지보수와 재사용에 용이해진다.
3. 스타일 간 충돌을 방지한다.
styled-components는 렌더링 시 자동으로 고유한 className을 생성, 사용한다.
따라서 복잡한 스타일링 중복 및 충돌을 방지할 수 있다.
단점
주로 나오는 이야기가 동적 스타일링으로 인한 성능 오버 헤드, 번들 크기 증가, 러닝 커브 3가지다.
1번은 런타임에 스타일을 생성하기 때문이고, 2번은 순수 CSS, CSS module과 달리 라이브러리를 설치해야 하기 때문이다.
3번은 본인이 체감해서 인정한다. (🥹ㅋ)
동작 원리
그래서 왜 도입했고 어떤 장단점이 있는지 알아봤다.
이제 styled-components가 어떻게 동작하는지 알아보기 전, 템플릿 리터럴을 이해하고 가자.
템플릿 리터럴은 JS에서 문자열을 표현하는 방식 중 하나로, 백틱 기호를 사용해 문자열 안에 변수나 표현식을 삽입한다.
// 기존 문자열 연결
const name = 'John';
const greeting = 'Hello, ' + name + '!';
// 템플릿 리터럴 사용
const templateLiteralGreeting = `Hello, ${name}!`;
console.log(greeting); // 기존 방식
console.log(templateLiteralGreeting); // 템플릿 리터럴 사용
const name = "react";
const message = `hello ${name}`;
console.log(message); // "hello react"
//${} 안에 일반 문자열/숫자가 아닌 객체를 넣을 경우
const obj = { a: 1 };
const text = `${obf}`;
console.log(text); // "[Object object]"
// ${} 안에 일반 문자열/숫자가 아닌 함수를 넣을 경우
const func = () => true;
const msg = `${func}`;
console.log(msg); // "() => true"
템플릿 리터럴에서 더 발전된 형태가 tagged template literal, 태그드 템플릿 리터럴인데
styled-components는 내부적으로 태그드 템플릿 방식으로 작동한다.
태그드 템플릿 리터럴은 템플릿 리터럴 문법을 사용해 문자열을 동적으로 생성할 때,
해당 문자열을 처리하기 위한 특별한 함수를 적용해 문자열 안에 들어가는 값들을 원하는대로 가공할 수 있고,
템플릿 리터럴 앞에 함수 이름(태그 함수)를 두어 해당 함수를 호출하는 형태다.
function myTaggedFunction(strings, ...values) {
console.log(strings); // 문자열 부분 (3) ['안녕하세요, 제 이름은 ', '이고, 나이는 ', '살입니다.', raw: Array(3)]
console.log(values); // 변수 값 부분 (2) ['John', 30]
}
const name = 'John';
const age = 30;
myTaggedFunction`안녕하세요, 제 이름은 ${name}이고, 나이는 ${age}살입니다.`;
styled-components를 사용할 때 먼저 템플릿 리터럴 문법을 이용해 스타일을 정의한다.
import styled from 'styled-components';
const StyledDiv = styled.div`
color: blue;
font-size: 16px;
`;
// const StyledDiv = styled.div`color: blue; font-size: 16px;`;
이 코드는 styled 함수에 .div를 전달해 div 태그를 스타일링하는 함수를 반환한다.
해당 함수는 백틱으로 감싸진 템플릿 리터럴을 인자로 받아 스타일을 정의하고,
해당 스타일이 적용된 새로운 컴포넌트를 생성한다.
styled 함수는 styled-components에서 제공하는 핵심 함수 중 하나다.
const StyledComponent = styled.elementType`css styles`;
동적으로 스타일링은 어떻게 할까?
바로 props를 전달하거나 삼항 연산자를 사용해 조건부로 스타일링 할 수 있다.
primary는 JSX 속성으로 StyledButton 컴포넌트에 전달되는 값, props라 해당 컴포넌트 내부에서 객체로 사용된다.
그래서 props.primary ? 'white' : 'black'에서 color: white가 되는 것이다.
import styled from 'styled-components';
const StyledButton = styled.button`
color: ${(props) => (props.primary ? 'white' : 'black')};
background-color: ${(props) => (props.primary ? 'blue' : 'lightgray')};
font-size: 16px;
padding: 10px;
`;
const PrimaryButton = <StyledButton primary>Primary Button</StyledButton>;
const SecondaryButton = <StyledButton>Secondary Button</StyledButton>;
import styled from 'styled-components';
const StyledDiv = styled.div`
color: ${(props) => props.color || 'black'};
background-color: ${(props) => props.backgroundColor || 'lightgray'};
font-size: 16px;
padding: 10px;
`;
const MyComponent = () => {
return (
<div>
<StyledDiv>Default Style</StyledDiv>
<StyledDiv color="blue" backgroundColor="yellow">
Custom Style
</StyledDiv>
</div>
);
};
적용하기
내 경우에 버튼을 눌러 질문을 넘길 때마다 진행바가 동적으로 차오르게 해야 했다.
const [width, setWidth] = useState(0);
...
// 앞으로 진행할 때마다
const handleNextQ = type => {
if (curQIdx + 1 < questionData.length) {
setCurQIdx(prevIdx => prevIdx + 1);
setWidth(((curQIdx + 2) / questionData.length) * 250);
// 뒤로 돌아갈 때마다
const handlePrevQ = () => {
if (curQIdx > 0) {
setCurQIdx(prevIdx => {
setWidth(((prevIdx - 1) / questionData.length) * 250);
return prevIdx - 1;
});
}
};
이런 식으로 width의 값을 바꿔줬고 이를 스타일링에 적용시켰다.
<ProgressBar>
<Progress width={width}>
{width === 0 ? '' : ((width / 250) * 100).toFixed(0) + '%'}
</Progress>
</ProgressBar>
const Progress = styled.div`
width: ${props => props.width}px;
height: 20px;
background-color: ${themes.purpleColor};
border-radius: 50px;
display: flex;
text-align: center;
align-items: center;
justify-content: center;
`;
Progress에 준 width는 props로 전달되었다. 이 width에 따라 width 스타일링이 진행된다.
width가 0이면 0%지만 width가 0이 아니라면 해당 숫자를 250(전체 진행바 너비)로 나누고 100을 곱해 %를 만든다.
모르고 썼을 땐 마냥 어렵게 느껴졌는데 이 스타일링 라이브러리는 왜 백틱을 쓰고, 왜 CSS-in-JS라고 하는지 이해할 수 있었다!
다음 글에선 사용 방법과 스타일링 확장, 전역 스타일링하는 방법을 정리해보겠다.
**참고
CSS의 진화 과정
CSS -> SCSS -> BEM -> CSS Modules -> Styled Components
blog.toycrane.xyz
Template literals - JavaScript | MDN
템플릿 리터럴은 내장된 표현식을 허용하는 문자열 리터럴입니다. 여러 줄로 이뤄진 문자열과 문자 보간기능을 사용할 수 있습니다. 이전 버전의 ES2015사양 명세에서는 "template strings" (템플릿 문
developer.mozilla.org
3. styled-components · GitBook
03. styled-components 이번에 배워볼 기술은 CSS in JS 라는 기술입니다. 이 문구가 뜻하는 그대로, 이 기술은 JS 안에 CSS 를 작성하는 것을 의미하는데요, 우리는 이번 튜토리얼에서 해당 기술을 사용하
react.vlpt.us
The magic behind 💅 styled-components
Never seen that magic backtick styled.div`` notation? It's actually just JavaScript, no fancy transpiler needed! What that is, how it works and what it does? Let's find out!
mxstbr.blog
'🛠️ 프로젝트 > ⭐️ 이세계 테스트' 카테고리의 다른 글
[이세계 MBTI 테스트] Audio 음악 객체 알아보기 (0) | 2024.03.08 |
---|---|
[이세계 MBTI 테스트] React SPA로 만드는 MBTI 테스트 (7) | 2024.02.28 |
[이세계 MBTI 테스트] lighthouse로 웹사이트 성능 측정 및 개선하기 (0) | 2024.02.17 |
[이세계 MBTI 테스트] 웹페이지에 광고 붙이기 : 카카오 애드핏 + 커스텀 훅 + React script 태그 (0) | 2024.02.16 |
[이세계 MBTI 테스트] 리액트 로딩 스피너 라이브러리 react-spinner (0) | 2024.02.04 |