본 포스트는 Udemy 리액트 완벽가이드 2024 를 듣고 정리한 내용입니다.
목차 🌳
1️⃣ Vinilla CSS 로 스타일링
2️⃣ CSS Modules를 통한 스코핑
3️⃣ Styled Components 사용하기
4️⃣ Tailwind CSS 로 스타일링
5️⃣ 정적, 동적으로 CSS 적용하기
vite를 통한 웹 번들링을 거치면, js로 css 파일을 import 할 수 있음.
vite는 import를 식별하고 css 파일이 내부에 주입됨
장점
1️⃣ CSS 를 담당하는 사람이 있어 분없이 가능하다면, 파일만 import 해주면 되기 때문에 간결하며, JSX 코드에 접근 및 수정을 최소화함.
2️⃣ 흔히 아는, CSS 코드를 사용할 수 있음
단점
1️⃣ CSS 규칙은 컴포넌트로 스코핑되어 있지 않음 (한 컴포넌트로 CSS 적용 범위가 정해져 있지 않음)
-> 다른 컴포넌트 간에 내부 className이 같을 경우, 스타일 충돌이 이루어질 수 있다.
Header.jsx
import logo from '../assets/logo.png';
import './Header.css';
export default function Header() {
return (
<header>
...
</header>
);
헤더에만 Header.css
를 import 한 경우에도, 그 파일에 CSS 규칙들은 Header에만 적용되지 않는다.
(하나의 컴포넌트에 대해서만 스코핑 적용 ❌)
클래스 선택자 혹은 html 태그가 전역 범위에 정의되어, 충돌이 일어날 수 있다💥 import 하지 않은 jsx 파일의 코드에도 적용이 된다는 말
ClassName을 통해 동적으로, 스타일 부여하기
<label className={`${emailNotValid ? 'invalid' : ''}`}>Email</label>
컴포넌트에 CSS를 스코핑 하기 위한 방법 중 하나로,
css 파일에 style을 적는 것이 아닌 ➡️ jsx 파일 내부에, 태그 안에 속성값으로style={{color: 'red'}}
와 같이 넣어주는 방법
이때, inline 스타일을 정의할 때는, {{}}
이중 중괄호를 사용하게 되는데,
그냥 key-value 형태의 스타일 값을 동적으로 할당해줘야 하기 때문에 중괄호가 두개 필요하구나~ 생각하면 되겠다. key-value에서 { }
, 동적 할당으로 인해 { }
key값인 스타일 속성은 카멜케이스로 작성!
export default function Header() {
return (
<header>
<p style={{
color: 'red',
textAlign: 'center'
}}>A community of artists and art-lovers.</p>
</header>
);
}
장점
1️⃣ 원하는 요소에만 스타일을 적용할 수 있다. (다른 요소에 적용 ❌ )
2️⃣ 동적 스타일링이 용이함
inline 동적 스타일링 예시
export default function AuthInputs() {
const emailNotValid = submitted && !enteredEmail.includes('@');
return (
<input
type="email"
style={{
backgroundColor: emailNotValid ? '#fed2d2' : '#d1d5db',
}}
/>
)
}
단점
1️⃣ 모든 요소를 개별적으로 다 스타일 적용해야 한다는 점, 무수한 중복 발생
-> 공통적으로 적용될 수 있는 스타일도 다 따로 작성해줘야 함 (수정도 마찬가지)
2️⃣ CSS 코드 JSX 코드에 구분이 없음
-> 가독성, 협업 bad
Vanilla css로 작성하되, 스코프도 지정할 수 있는 방법
방법
1️⃣ 파일이름:Header.css
➡️Header.module.css
으로 변경
2️⃣ import 하여 객체의 이름을 네이밍 해줘야 함
-> ex)import classes from ./Header.module.css
3️⃣ className으로 사용시, import한 곳에서의 class에 접근
-><p className={classes.paragraph}></p>
className 이 겹치더라도 빌드 과정에서 import 한 컴포넌트의 파일로 스코핑되도록 보장함!
장점
1️⃣ CSS 를 담당하는 사람이 있어 분없이 가능하다면, 파일만 import 해주면 되기 때문에 간결하며, JSX 코드에 접근 및 수정을 최소화함.
2️⃣ 흔히 아는, CSS 코드를 사용할 수 있음
3️⃣ import된 컴포넌트로 스코프되어, className이 겹쳐도 충돌되지 않음!
단점
1️⃣ 많은 css 파일을 만들어야 할 수 있음, 한 컴포넌트에 하나씩 타켓팅하면 (내부 양은 적지만, 파일 수는 많은 .. )
2️⃣ CSS 코드 JSX 코드에 구분이 없음
-> 가독성, 협업 bad
말그대로, CSS를 컴포넌트화 시켜주는 라이브러리
사용방법
1️⃣ styled-components 패키지에서 import 해주기
import { styled } from 'styled-components'
2️⃣ styled 객체에 알맞는 html 태그 속성을 넣어주고 (styled.div
), 백틱(tagged-template)으로 스타일 감싸주기const 변수 = styled.html요소` css 속성: css 내용, css 속성: css 내용, ... `
ex styled-component
const ControlContainer = styled.div`
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-bottom: 1.5rem;
`;
동적 스타일링 방법? 🤨
예 물론 있죠.
css 클래스를 따로 추가 하지 않고도, props를 추가적으로 만들어서 조건에 따라서 스타일을 부여할 수 있다 ! (styled components 도 컴포넌트이니까~)
props 넘겨주는 방법 예시
<Label $invalid={emailNotValid}>Email</Label>
props-> $invalid
에 따라서 true 혹은 false가 설정.const Label = styled.label` ... color: ${({ $invalid }) => ($invalid ? '#f87171' : '#6b7280')}; `;
styled components인 Label에서, 백틱으로 감싸진
${ ... }
에 화살표 함수를 작성하고, 그 안에 인자인 props의 상태에 따라 조건에 맞는 스타일 값을 동적으로 출력하게 된다.
근데 왜 styled component에서 사용하는 props에는 $를 붙여서 쓰나요? 🤨
왜냐면, styled component로 전달되는 props와 HTML 내장 속성 간의 충돌을 피하기 위해서 !
일부 props 가 HTML 속성으로 직접 전달되어 예상치 못한 동작을 발생시킬 수 있기 때문
따라서,$
를 붙여서 HTML 표준 속성과 충돌하지 않도록 관례로 정해졌다.
html 표준 속성 예시
<input type="checkbox" checked> // checked
<input type="text" required> //required
<form action="/submit" method="post"> // action
<input type="text" name="username"> //type name 등등
<button type="submit">Submit</button>
</form>
<header>
<img src={logo} alt="A canvas" />
<h1>ReactArt</h1>
<p>A community of artists and art-lovers.</p>
<header>
❌ 헤더 태그 내부에 위치하고 있는 요소들에 대해 각각 컴포넌트를 만들어, 스타일을 적용시키는 것이 아닌!
헤더에 대한 styled components 내부에 & 적용할 요소
와 같이 작성해주면 된다.
ex) nested styled components
const StyledHeader = styled.header`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 2rem;
margin-bottom: 2rem;
& img {
object-fit: contain;
margin-bottom: 2rem;
width: 11rem;
height: 11rem;
}
@media (min-width: 768px) {
& {
margin-bottom: 4rem;
}
& h1 {
font-size: 2.25rem;
}
}
`;
단, 부모요소 -> 자식에 대한 스타일 작성은 &(한칸공백)요소
로 작성해야 한다.
❌&p
❌
⭕️& p
⭕️
이처럼 styled components 사용시, 자식요소에 대한 style 추가(중첩)와, 미디어 쿼리 적용이 간단하다!
자식 요소에 대한 style 적용과 유사하되, &
뒤에 공백 없이 가상선택자
를 적어주면 된다.
const Button = styled.button`
padding: 1rem 2rem;
font-weight: 600;
...
&:hover {
background-color: #f0920e;
}
`;
장점
1️⃣ 리액트 컴포넌트 단위에서 생각하면 되기 때문에, 간단
2️⃣ 스타일이 필요한 컴포넌트로 스코핑 되기 때문에, css 충돌이 없음
단점
1️⃣ 상대적으로 많은 컴포넌트가 생겨날 수 있음 (작고, 그 수가 많아서, 헷갈릴 수 있음)
2️⃣ jsx와 css의 영역이 명확하게 구분되지 않음
-> 재사용되는 스타일 컴포넌트는 따로 관리하는게 맞음
(결국 스타일 컴포넌트도 리액트의 컴포넌트의 관점에서 생각하기)
스타일 라이브러리 html 요소에 유틸리티 클래스를 적용하여, css를 그려냄
css 프레임워크
Vite 환경에서 Tailwind CSS 설치 및 설정 방법
export default function Header() {
return (
<header className="flex flex-col items-center mt-8 mb-16">
<img src={logo} alt="A canvas" className="object-contain mb-8 w-44 h-44" />
<h1 className="text-4xl font-semibold tracking-widest text-center uppercase text-amber-800">
ReactArt
</h1>
</header>
);
}
이런식으로 bootstrap
쓰듯이 사용하면 된다.
더 많은 내용은 공식문서 참조
ex Flex 적용 방식
내가 원하는 스타일이 없으면 어쩌죠? Custom 하면 되죠~
index.css
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
background-color: #ffaa00;
background-image: url("data:image/svg+xml");
background-attachment: fixed;
background-size: cover;
}
Tailwind 지시어 추가해주고, 메인 스타일 파일에 css 로 만들어주기
tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {
fontFamily: {
title: ['"Pacifico"', 'cursive'],
},
},
},
plugins: [],
};
extend 부분에 폰트 추가해주기,
이때 title
은 새롭게 추가하고 싶은 폰트의 새로운 이름일 뿐임
(구글에서 불러온 폰트는 이중따옴표로 한번더 감싸주기!)
장점
1️⃣ CSS 를 잘 몰라도 쓸 수 있음
2️⃣ 다 만들어진 css를 사용하는거라 개발 속도가 더 빠르다고 함
3️⃣ 스타일 충돌이 없음
단점
1️⃣ 클래스가 너무 길어진다.
2️⃣ tailwind 만으로 끝나는게 아니라, 추가로 css 작업이 필요하기도 함
3️⃣ jsx와 css 코드 사이에 구분이 명확하지 않음
4️⃣ 내용의 양이 작고 또 그 수가 많은 wrapper 컴포넌트가 생김 (코드줄이 늘어남)