2025 / 03 / 26 ~ 27
오늘 수업 시간에는 CSS도 컴포넌트화 하는 방법을 배웠습니다.
배울 때는 딱히 좋은 점을 모르겠었는데, 실습을 해보니까 확실히 어떤 점이 좋은지 알 수 있었습니다. 파일을 외부에서 사용할 수 있도록 export하는 것에는 이젠 익숙해졌는데.. 아직 props를 전달하고 받는 부분이 헷갈리는건지.. 딱히 뭐라 말할 순 없는데 이상합니다. 배운 내용과 함께 헷갈린 부분을 중점적으로 정리해보도록 하겠습니다.
- CSS-in-JS 라이브러리입니다.
- 스타일을 자바스크립트 코드 안에 직접 작성할 수 있게 해줍니다.
- 컴포넌트 별로 스타일을 모듈화하고, 스타일과 로직을 함께 관리할 수 있습니다.
- 리액트에서 UI 컴포넌트를 작성할 때, 스타일을 별도로 작성하는 대신 컴포넌트 자체에 스타일을 정의할 수 있어서 매우 직관적이고 관리하기 쉽습니다.
방법 1
npm install styled-components
방법 2
yarn add styled-components
- styled 객체를 사용하여, HTML 태그에 스타일을 적용한 컴포넌트를 생성합니다.
import styled from 'styled-components'; // 스타일이 적용된 버튼 컴포넌트 생성 const Button = styled.button` background-color: #4CAF50; color: white; padding: 10px 20px; border: none; border-radius: 5px; &:hover { background-color: #45a049; } `; // 리액트 컴포넌트에서 사용 function App() { return ( <Button>Click Me</Button> ); }
1. 템플릿 리터럴 사용
- styled-components는 템플릿 리터럴( ` ) 문법을 사용하여 CSS 스타일을 정의합니다.
- 코드 안에서 스타일을 작성할 수 있기 때문에, 컴포넌트가 렌더링될 때 스타일도 함께 적용됩니다.
2. 컴포넌트화된 스타일
- styled-components로 만든 스타일은 styled component로 불립니다.
- HTML 요소처럼 사용할 수 있습니다.
- ex) Button은 단순한 button 태그지만 스타일이 적용된 리액트 컴포넌트입니다.
3. 유니크 클래스 이름 생성
- 각 styled-component는 자동으로 고유한 클래스 이름을 생성합니다.
- 이 클래스 이름은 글로벌 네임스페이스와 충돌하지 않도록 보장됩니다.
1. CSS와 JS의 분리
- 스타일을 자바스크립트 파일 내에 포함시켜, CSS 파일을 따로 관리할 필요가 없습니다.
- 컴포넌트의 스타일을 한 곳에서 모두 관리할 수 있습니다.
2. 동적 스타일링
- props, 상태에 따라 동적으로 변경할 수 있어, 다양한 스타일링이 가능합니다.
3. 조건부 스타일링
- 조건에 따라 스타일을 다르게 적용할 수 있어 유연합니다.
4. 컴포넌트화된 스타일
- 스타일이 컴포넌트와 결합되어 있기 때문에, 재사용성이 높고 컴포넌트 기반으로 관리할 수 있습니다.
5. 자동 클래스 이름 생성
- 고유한 클래스 이름을 자동으로 생성하므로, 스타일 충돌을 걱정할 필요가 없습니다.
6. 미디어 쿼리 지원
- CSS에서 사용하는 미디어 쿼리를 직접 작성할 수 있습니다.
1. 성능 문제
- 매 렌더링마다 스타일을 동적으로 생성하므로, 많은 스타일을 사용하거나 복잡한 스타일링을 사용할 경우 성능에 영향을 줄 수 있습니다.
2. 스타일 인라인화
- 모든 스타일이 자바스크립트 파일 안에 존재하기 때문에, 이 파일이 커지면 페이지 로드 시 성능에 영양이 있을 수 있습니다.
3. 디버깅 어려움
- 스타일이 자바스크립트 코드로 작성되기 떄문에 CSS 파일에서와 같은 방식으로 디버깅을 하기가 어려울 수 있습니다.
앱 컴포넌트
- 리액트는 컴포넌트 기반 개발을 통해 UI를 관리합니다.
- 각 컴포넌트를 독립적이고 재사용 가능함으로써, 유지보수성이 좋아지고 코드가 깔끔해집니다.
import AuthInputs from "./components/AuthInputs"; import Header from "./components/Header"; export default function App() { return ( <> <Header /> <main> <AuthInputs /> </main> </> ); }
1. 컴포넌트 기반 구조
- 이 코드에서는 리액트의 컴포넌트 기반 구조를 활용하고 있습니다.
- App은 부모 컴포넌트이고, Header, AuthInputs는 자식 컴포넌트입니다.
- 독립적인, 재사용 가능한 부분으로 나누어져 각 부분을 독립적으로 개발하고 관리할 수 있습니다.
2. JSX
- JSX 문법을 사용하여 리액트 컴포넌트를 HTML과 비슷한 방식으로 작성하고 있습니다.
<>,</>는 React Fragment로, 불필요한 DOM 요소를 추가하지 않고 여러 자식 컴포넌트를 반환할 수 있게 합니다.
<Header />
<AuthInputs />
헤더 컴포넌트
- 스타일 컴포넌트화는 유지보수성과 재사용성을 높여줍니다.
- 반응형 디자인을 통해 다양한 화면 크기에서 앱이 잘 보이도록 만들 수 있습니다.
- 이는 다양한 디바이스에서 사용자 경험을 향상시키는 데 중요합니다.
import logo from "../assets/logo.png"; import styled from "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; } & h1 { font-size: 1.5rem; font-weight: 600; letter-spacing: 0.4em; text-align: center; text-transform: uppercase; color: #9a3412; font-family: "Pacifico", cursive; margin: 0; } & p { text-align: center; color: #a39191; margin: 0; } @media (min-width: 768px) { margin-bottom: 4rem; & h1 { font-size: 2.25rem; } } `; export default function Header() { return ( <StyledHeader> <img src={logo} alt="A canvas" /> <h1>Welcome to the App</h1> <p>Let's get started</p> </StyledHeader> ); }
1. 스타일 컴포넌트화 (styled-components)
- 스타일을 자바스크립트 파일 안에 작성하여, 한 곳에서 관리하는 방법입니다.
- 컴포넌트와 스타일이 연결되어 코드의 관리가 용이합니다.
2. 반응형 디자인
- 미디어 쿼리를 사용하여 다양한 화면 크기에 맞는 스타일을 적용합니다.
- 모바일, 태블릿, 데스크탑 화면 크기에 따라 UI가 달라지도록 만드는 반응형 디자인입니다.
StyledHeader
미디어 쿼리
입력 필드 컴포넌트
- 동적 스타일링은 사용자 입력 값에 따라 실시간으로 반응할 수 있게 합니다.
- 유효하지 않은 입력에 대해 실시간 피드백을 제공하는 것은 사용자 경험을 향상시킵니다.
import styled from "styled-components"; const Label = styled.label` display: block; margin-bottom: 0.5rem; font-size: 0.75rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: ${({ $invalid }) => ($invalid ? "#f87171" : "#6b7280")}; `; const Input = styled.input` width: 100%; padding: 0.75rem 1rem; line-height: 1.5; border-color: ${({ $invalid }) => ($invalid ? "#f73f3f" : undefined)}; color: ${({ $invalid }) => ($invalid ? "#ef4444" : "#374151")}; background-color: ${({ $invalid }) => ($invalid ? "#fed2d2" : "#d1d5db")}; border: 1px solid; border-radius: 0.25rem; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); `; export default function CustomInput({ label, invalid, ...props }) { return ( <p> <Label $invalid={invalid}>{label}</Label> <Input $invalid={invalid} {...props} /> </p> ); }
1. Props
- 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 때 사용됩니다.
- invalid라는 props를 통해 유효성 검사를 자식 컴포넌트로 전달하고 있습니다.
2. 동적 스타일링
- styled-components에서 조건부로 스타일을 적용하는 방식입니다.
- invalid라는 상태에 따라 입력 필드의 색상, 배경, 경계선이 달라집니다.
- 사용자가 실수를 했을 때 이를 시각적으로 나타낼 수 있습니다.
동적 스타일링
$invalid
로그인 폼 컴포넌트
- 상태 관리를 통해 컴포넌트가 동적인 데이터를 다룰 수 있게 됩니다.
- 리액트에서 상태는 UI의 변화를 제어하는 핵심 개념입니다.
- 유효성 검사는 사용자 입력에 따라 피드백을 제공하며, 사용자 경험을 향상시킵니다.
- 잘못된 입력에 대해 즉시 알림을 제공하는 것이 중요합니다.
import { useState } from "react"; import styled from "styled-components"; import CustomInput from "./CustomInput"; const ControllContainer = styled.div` display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 1.5rem; `; export default function AuthInputs() { const [enteredEmail, setEnteredEmail] = useState(""); const [enteredPassword, setEnteredPassword] = useState(""); const [submitted, setSubmitted] = useState(false); function handleInputChange(identifier, value) { if (identifier === "email") { setEnteredEmail(value); } else { setEnteredPassword(value); } } function handleLogin() { setSubmitted(true); } const emailNotValid = submitted && !enteredEmail.includes("@"); const passwordNotValid = submitted && enteredPassword.trim().length < 6; return ( <div id="auth-inputs"> <ControllContainer> <CustomInput label="email" invalid={emailNotValid} type="email" onChange={(event) => handleInputChange("email", event.target.value)} /> <CustomInput label="password" invalid={passwordNotValid} type="password" onChange={(event) => handleInputChange("password", event.target.value)} /> </ControllContainer> <div className="actions"> <button type="button" className="text-button"> Create a new account </button> <button className="button" onClick={handleLogin}> Sign In </button> </div> </div> ); }
1. 상태 관리 (useState)
- useState 훅을 통해 각 입력 필드의 값을 상태로 관리하고 있습니다.
- 컴포넌트의 재렌더링을 유발하며, 입력값에 따라 동적으로 업데이트할 수 있게 해줍니다.
2. 이벤트 핸들러
- onChange 이벤트 핸들러를 사용하여 입력 값이 변할 때마다 상태를 업데이트합니다.
- handleLogin 함수는 로그인 버튼 클릭 시 제출 상태를 갱신합니다.
3. 조건부 렌더링
- 유효성 검사 결과에 따라 스타일을 동적으로 변경합니다.
- invalid 값에 따라 CustomInput 컴포넌트에 전달되는 스타일이 달라집니다.
useState
상태 관리
유효성 검사
- styled-components에서 컴포넌트 자신을 참조하는 특별한 선택자입니다.
- &는 현재 컴포넌트 또는 현재 선택된 요소를 참조합니다.
- 주로 중첩된 스타일링에서 유용하게 사용됩니다.
- & h1은 StyledHeader라는 컴포넌트 내에서 h1 태그에 스타일을 적용합니다.
- StyledHeader는
<header>태그로 변환되므로, & h1은<header>안에 있는 h1 태그를 선택하는 것입니다.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; } & h1 { font-size: 1.5rem; font-weight: 600; letter-spacing: 0.4em; text-align: center; text-transform: uppercase; color: #9a3412; font-family: "Pacifico", cursive; margin: 0; } & p { text-align: center; color: #a39191; margin: 0; } `;
1. 중첩된 스타일링
- 중첩된 스타일을 적용할 때, 현재 컴포넌트 내에서 특정 요소를 선택할 수 있습니다.
- 구조적으로 명확하고, 더 읽기 쉬운 스타일을 만들 수 있게 도와줍니다.
2. 컴포넌트에 스타일 적용
- &는 해당 스타일이 특정 컴포넌트에만 적용되도록 하는 중요한 역할을 합니다.
- 다른 요소에 영향을 주지 않고 컴포넌트 내부 요소에만 스타일을 적용할 수 있습니다.
- styled-components에서 props 이름을 구분하기 위해 사용되는 특별한 접두사입니다.
- 기능을 가지지는 않지만, props를 직관적이고 명확하게 관리할 수 있도록 합니다.
- styled-components에서는 props 이름에 아무런 제한이 없지만, $ 접두사를 사용하는 것이 일반적인 규칙이나 스타일 가이드에서 권장되는 방법입니다.
ex) $invalid
- $invalid는 단순히 invalid라는 props를 styled-components의 스타일 컴포넌트에서 사용할 때, $를 붙여 구분하는 방법입니다.
- $는 스타일링에 특별한 영향을 주지는 않지만, props를 스타일에 적용할 때 어떤 props가 외부에서 전달되는지 구분하기 위해 사용됩니다.
- 일반적인 props 이름과 구별하기 위함입니다.
1. 가독성 향상
- 외부에서 전달되는 props와 스타일링을 위한 props를 쉽게 구분할 수 있습니다.
- ex) invalid는 논리적 상태를 나타내는 props이고, $invalid는 스타일을 적용할 때 사용되는 props라는 것을 명확히 알 수 있습니다.
2. 명확한 구분
- styled-components에서 props가 스타일을 제어하는 용도일 때, $ 접두사를 사용하면 해당 props가 스타일링에만 관련 있다는 것을 명확히 구분할 수 있습니다.
3. 스타일링 가이드라인
- 일부 스타일 가이드에서는 스타일링을 위한 props에는 $를 붙일 것을 권장합니다.
- $invalid는 invalid라는 prop을 전달할 때, 스타일을 동적 변경에 사용됩니다.
- $invalid를 사용하여 유효성 검사 결과에 따라 스타일을 조건부로 적용합니다.
- ex) $invalid가 true일 경우 색상이나 배경색, 테두리 색상이 다르게 설정됩니다.
const Label = styled.label` display: block; margin-bottom: 0.5rem; font-size: 0.75rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: ${({ $invalid }) => ($invalid ? "#f87171" : "#6b7280")}; `; const Input = styled.input` width: 100%; padding: 0.75rem 1rem; line-height: 1.5; border-color: ${({ $invalid }) => ($invalid ? "#f73f3f" : undefined)}; color: ${({ $invalid }) => ($invalid ? "#ef4444" : "#374151")}; background-color: ${({ $invalid }) => ($invalid ? "#fed2d2" : "#d1d5db")}; border: 1px solid; border-radius: 0.25rem; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); `; export default function CustomInput({ label, invalid, ...props }) { return ( <p> <Label $invalid={invalid}>{label}</Label> <Input $invalid={invalid} {...props} /> </p> ); }
56일차 후기
- CSS를 모듈화 하다가 이번에는 styled-components를 사용하여 한 페이지에서 모두 다루니까 좀 신기하기도 하고, CSS 파일이 필요없어진다는게 조금 오묘했습니다.
- 처음 보는 선택자를 사용한 부분이 있어서 반복 학습을 해야할 것 같습니다.
- CSS 구문에 삼항 연산자를 사용해서 사용자의 입력값에 따라 동적으로 업데이트할 수 있다는 것이 신기했고, if문이 아닌게 아직은 조금 어색한 것 같습니다.
- Props를 넘겨 받을 때, 객체의 형태로 넘겨 받는데.. 여기서 구조분해를 했을 때와 하지 않았을 때의 차이를 잘 몰랐었는데 이번 기회에 확실히 할 수 있었습니다.
- 점점 날이 갈수록 내용이 깊어지고 있는 건지.. 할 수 있을까.. 걱정입니다. ٩(๑˃̌ۿ˂̌๑)۶
- 그래도 벨로그에 꾸준히 정리하고 복습하면 괜찮겠죠...? ⁽⁽ ◟( ・o˙ )◞