56일차[styled-components]

진하의 메모장·2025년 3월 28일
1

공부일기

목록 보기
59/66
post-thumbnail

2025 / 03 / 26 ~ 27

오늘 수업 시간에는 CSS도 컴포넌트화 하는 방법을 배웠습니다.
배울 때는 딱히 좋은 점을 모르겠었는데, 실습을 해보니까 확실히 어떤 점이 좋은지 알 수 있었습니다. 파일을 외부에서 사용할 수 있도록 export하는 것에는 이젠 익숙해졌는데.. 아직 props를 전달하고 받는 부분이 헷갈리는건지.. 딱히 뭐라 말할 순 없는데 이상합니다. 배운 내용과 함께 헷갈린 부분을 중점적으로 정리해보도록 하겠습니다.



💌 styled-components

  • CSS-in-JS 라이브러리입니다.
  • 스타일을 자바스크립트 코드 안에 직접 작성할 수 있게 해줍니다.
  • 컴포넌트 별로 스타일을 모듈화하고, 스타일과 로직을 함께 관리할 수 있습니다.
  • 리액트에서 UI 컴포넌트를 작성할 때, 스타일을 별도로 작성하는 대신 컴포넌트 자체에 스타일을 정의할 수 있어서 매우 직관적이고 관리하기 쉽습니다.


1. 설치 방법

방법 1

npm install styled-components

방법 2

yarn add styled-components
  • 설치 후, 리액트 컴포넌트에서 styled-components를 사용할 수 있습니다.


2. 사용 방법

  • 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>
   );
}


3. 작동 원리

1. 템플릿 리터럴 사용

  • styled-components는 템플릿 리터럴( ` ) 문법을 사용하여 CSS 스타일을 정의합니다.
  • 코드 안에서 스타일을 작성할 수 있기 때문에, 컴포넌트가 렌더링될 때 스타일도 함께 적용됩니다.

2. 컴포넌트화된 스타일

  • styled-components로 만든 스타일은 styled component로 불립니다.
  • HTML 요소처럼 사용할 수 있습니다.
  • ex) Button은 단순한 button 태그지만 스타일이 적용된 리액트 컴포넌트입니다.

3. 유니크 클래스 이름 생성

  • 각 styled-component는 자동으로 고유한 클래스 이름을 생성합니다.
  • 이 클래스 이름은 글로벌 네임스페이스와 충돌하지 않도록 보장됩니다.


4. 장점

1. CSS와 JS의 분리

  • 스타일을 자바스크립트 파일 내에 포함시켜, CSS 파일을 따로 관리할 필요가 없습니다.
  • 컴포넌트의 스타일을 한 곳에서 모두 관리할 수 있습니다.

2. 동적 스타일링

  • props, 상태에 따라 동적으로 변경할 수 있어, 다양한 스타일링이 가능합니다.

3. 조건부 스타일링

  • 조건에 따라 스타일을 다르게 적용할 수 있어 유연합니다.

4. 컴포넌트화된 스타일

  • 스타일이 컴포넌트와 결합되어 있기 때문에, 재사용성이 높고 컴포넌트 기반으로 관리할 수 있습니다.

5. 자동 클래스 이름 생성

  • 고유한 클래스 이름을 자동으로 생성하므로, 스타일 충돌을 걱정할 필요가 없습니다.

6. 미디어 쿼리 지원

  • CSS에서 사용하는 미디어 쿼리를 직접 작성할 수 있습니다.


5. 주의할 점

1. 성능 문제

  • 매 렌더링마다 스타일을 동적으로 생성하므로, 많은 스타일을 사용하거나 복잡한 스타일링을 사용할 경우 성능에 영향을 줄 수 있습니다.

2. 스타일 인라인화

  • 모든 스타일이 자바스크립트 파일 안에 존재하기 때문에, 이 파일이 커지면 페이지 로드 시 성능에 영양이 있을 수 있습니다.

3. 디버깅 어려움

  • 스타일이 자바스크립트 코드로 작성되기 떄문에 CSS 파일에서와 같은 방식으로 디버깅을 하기가 어려울 수 있습니다.


💌 App 컴포넌트

앱 컴포넌트

  • 리액트는 컴포넌트 기반 개발을 통해 UI를 관리합니다.
  • 각 컴포넌트를 독립적이고 재사용 가능함으로써, 유지보수성이 좋아지고 코드가 깔끔해집니다.
import AuthInputs from "./components/AuthInputs";
import Header from "./components/Header";

export default function App() {
   return (
      <>
         <Header />
         <main>
            <AuthInputs />
         </main>
      </>
   );
}


1. 적용된 리액트 개념

1. 컴포넌트 기반 구조

  • 이 코드에서는 리액트의 컴포넌트 기반 구조를 활용하고 있습니다.
  • App은 부모 컴포넌트이고, Header, AuthInputs는 자식 컴포넌트입니다.
  • 독립적인, 재사용 가능한 부분으로 나누어져 각 부분을 독립적으로 개발하고 관리할 수 있습니다.

2. JSX

  • JSX 문법을 사용하여 리액트 컴포넌트를 HTML과 비슷한 방식으로 작성하고 있습니다.
  • <>, </>는 React Fragment로, 불필요한 DOM 요소를 추가하지 않고 여러 자식 컴포넌트를 반환할 수 있게 합니다.


2. 코드 분석

<Header />

  • Header는 상단에 로고나 제목을 표시하는 역할을 합니다.
  • 이 컴포넌트는 App의 루트 컴포넌트 내에서 렌더링됩니다.

<AuthInputs />

  • 로그인 폼을 담당하는 컴포넌트입니다.
  • 사용자가 이메일과 비밀번호를 입력하는 필드를 제공하고, 로그인 버튼을 클릭하면 처리됩니다.


💌 Header 컴포넌트

헤더 컴포넌트

  • 스타일 컴포넌트화는 유지보수성과 재사용성을 높여줍니다.
  • 반응형 디자인을 통해 다양한 화면 크기에서 앱이 잘 보이도록 만들 수 있습니다.
  • 이는 다양한 디바이스에서 사용자 경험을 향상시키는 데 중요합니다.
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. 적용된 리액트 개념

1. 스타일 컴포넌트화 (styled-components)

  • 스타일을 자바스크립트 파일 안에 작성하여, 한 곳에서 관리하는 방법입니다.
  • 컴포넌트와 스타일이 연결되어 코드의 관리가 용이합니다.

2. 반응형 디자인

  • 미디어 쿼리를 사용하여 다양한 화면 크기에 맞는 스타일을 적용합니다.
  • 모바일, 태블릿, 데스크탑 화면 크기에 따라 UI가 달라지도록 만드는 반응형 디자인입니다.


2. 코드 분석

StyledHeader

  • styled-components를 사용하여 header에 스타일을 적용합니다.
  • 스타일은 템플릿 리터럴로 작성됩니다.
  • header 태그에 스타일을 적용하는 StyledHeader라는 이름의 컴포넌트를 생성합니다.

미디어 쿼리

  • 화면 크기가 768px 이상일 경우 h1의 폰트 크기를 조정하는 미디어 쿼리(@media)를 사용하여 반응형 디자인을 구현하고 있습니다.


💌 CustomInput 컴포넌트

입력 필드 컴포넌트

  • 동적 스타일링은 사용자 입력 값에 따라 실시간으로 반응할 수 있게 합니다.
  • 유효하지 않은 입력에 대해 실시간 피드백을 제공하는 것은 사용자 경험을 향상시킵니다.
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. 적용된 리액트 개념

1. Props

  • 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 때 사용됩니다.
  • invalid라는 props를 통해 유효성 검사를 자식 컴포넌트로 전달하고 있습니다.

2. 동적 스타일링

  • styled-components에서 조건부로 스타일을 적용하는 방식입니다.
  • invalid라는 상태에 따라 입력 필드의 색상, 배경, 경계선이 달라집니다.
  • 사용자가 실수를 했을 때 이를 시각적으로 나타낼 수 있습니다.


2. 코드 분석

동적 스타일링

  • styled-components의 동적 스타일링을 활용합니다.
  • invalid prop을 사용하여 입력 값이 유효하지 않을 경우 스타일을 조건부로 변경합니다.
  • ex) invalid가 true일 때 입력 필드는 빨간색 경계선과 배경색을 가집니다.

$invalid

  • props로 전달된 값에 따라 스타일이 동적으로 결정됩니다.
  • styled-components에서는 props를 통해 스타일을 제어할 수 있습니다.
  • $는 스타일링에서 props 이름과 구별하기 위해 사용됩니다.


💌 AuthInputs 컴포넌트

로그인 폼 컴포넌트

  • 상태 관리를 통해 컴포넌트가 동적인 데이터를 다룰 수 있게 됩니다.
  • 리액트에서 상태는 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. 적용된 리액트 개념

1. 상태 관리 (useState)

  • useState 훅을 통해 각 입력 필드의 값을 상태로 관리하고 있습니다.
  • 컴포넌트의 재렌더링을 유발하며, 입력값에 따라 동적으로 업데이트할 수 있게 해줍니다.

2. 이벤트 핸들러

  • onChange 이벤트 핸들러를 사용하여 입력 값이 변할 때마다 상태를 업데이트합니다.
  • handleLogin 함수는 로그인 버튼 클릭 시 제출 상태를 갱신합니다.

3. 조건부 렌더링

  • 유효성 검사 결과에 따라 스타일을 동적으로 변경합니다.
  • invalid 값에 따라 CustomInput 컴포넌트에 전달되는 스타일이 달라집니다.


2. 코드 분석

useState

  • 이메일, 비밀번호, 제출 여부 상태를 관리하기 위해 useState 훅을 사용합니다.
  • 이 훅은 리액트에서 컴포넌트 내에서 상태를 관리할 수 있게 해줍니다.

상태 관리

  • enteredEmail, enteredPassword, submitted는 각각의 입력값과 폼이 제출된 상태를 추적합니다.
  • 상태가 변할 때마다 컴포넌트는 리렌더링됩니다.

유효성 검사

  • 이메일과 비밀번호가 유효하지 않으면 입력 필드에 스타일을 다르게 적용합니다.
  • CustomInput 컴포넌트에서 invalid prop을 통해 조건부로 스타일을 다르게 적용하는 방식입니다.


💌 &

  • styled-components에서 컴포넌트 자신을 참조하는 특별한 선택자입니다.
  • &는 현재 컴포넌트 또는 현재 선택된 요소를 참조합니다.
  • 주로 중첩된 스타일링에서 유용하게 사용됩니다.


1. 사용 예시

  • & 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;
   }
`;


2. 사용 이유

1. 중첩된 스타일링

  • 중첩된 스타일을 적용할 때, 현재 컴포넌트 내에서 특정 요소를 선택할 수 있습니다.
  • 구조적으로 명확하고, 더 읽기 쉬운 스타일을 만들 수 있게 도와줍니다.

2. 컴포넌트에 스타일 적용

  • &는 해당 스타일이 특정 컴포넌트에만 적용되도록 하는 중요한 역할을 합니다.
  • 다른 요소에 영향을 주지 않고 컴포넌트 내부 요소에만 스타일을 적용할 수 있습니다.


💌 $

  • styled-components에서 props 이름을 구분하기 위해 사용되는 특별한 접두사입니다.
  • 기능을 가지지는 않지만, props를 직관적이고 명확하게 관리할 수 있도록 합니다.
  • styled-components에서는 props 이름에 아무런 제한이 없지만, $ 접두사를 사용하는 것이 일반적인 규칙이나 스타일 가이드에서 권장되는 방법입니다.


1. 정확한 역할

ex) $invalid

  • $invalid는 단순히 invalid라는 props를 styled-components의 스타일 컴포넌트에서 사용할 때, $를 붙여 구분하는 방법입니다.
  • $는 스타일링에 특별한 영향을 주지는 않지만, props를 스타일에 적용할 때 어떤 props가 외부에서 전달되는지 구분하기 위해 사용됩니다.
  • 일반적인 props 이름과 구별하기 위함입니다.


2. 사용 이유

1. 가독성 향상

  • 외부에서 전달되는 props와 스타일링을 위한 props를 쉽게 구분할 수 있습니다.
  • ex) invalid는 논리적 상태를 나타내는 props이고, $invalid는 스타일을 적용할 때 사용되는 props라는 것을 명확히 알 수 있습니다.

2. 명확한 구분

  • styled-components에서 props가 스타일을 제어하는 용도일 때, $ 접두사를 사용하면 해당 props가 스타일링에만 관련 있다는 것을 명확히 구분할 수 있습니다.

3. 스타일링 가이드라인

  • 일부 스타일 가이드에서는 스타일링을 위한 props에는 $를 붙일 것을 권장합니다.


3. 예시 코드

  • $invalidinvalid라는 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˙ )◞
profile
૮꒰ ྀི〃´꒳`〃꒱ა

0개의 댓글