TIL - 20250827

juni·2025년 8월 27일

TIL

목록 보기
107/315

0827 React : 동적 리스트 관리와 스타일링


✅ 1. 동적 리스트 관리 (생성, 추가, 삭제)

  • React에서 목록 데이터는 불변성(Immutability)을 유지하며 관리하는 것이 매우 중요합니다. 즉, 기존 상태 배열을 직접 수정하는 대신, 항상 새로운 배열을 생성하여 상태를 업데이트해야 합니다.

➕ 1-1. 목록 동적 렌더링 (기본)

  • JavaScript의 map() 메서드를 사용하여 배열 데이터를 순회하고, 각 항목을 JSX 엘리먼트로 변환하여 렌더링합니다. 이때 각 엘리먼트에는 React가 항목을 식별할 수 있도록 고유한 key prop을 반드시 지정해야 합니다.

    {goalList.map(goal => (
      <GoalItem key={goal.id} id={goal.id}>
        {goal.text}
      </GoalItem>
    ))}

➕ 1-2. 목록 추가 기능

  • 새로운 항목을 목록에 추가할 때는, 기존 상태 배열을 복사한 새로운 배열을 만들고 그 앞에 새 항목을 추가합니다. 스프레드 문법(...)을 사용하면 이를 간결하게 처리할 수 있습니다.

  • 핵심 원칙: push()와 같이 원본 배열을 변경하는 메서드를 사용해서는 안 됩니다.

    const [courseGoals, setCourseGoals] = useState([
      { text: 'Do all exercises!', id: 'g1' },
      { text: 'Finish the course!', id: 'g2' }
    ]);
    
    const addGoalHandler = enteredText => {
      // 함수형 업데이트: 최신 상태(prevGoals)를 기반으로 새로운 배열을 생성
      setCourseGoals(prevGoals => {
        const newGoal = { text: enteredText, id: Math.random().toString() };
        // 새 항목을 배열의 맨 앞에 추가하고, 기존 항목들을 뒤에 이어붙임
        return [newGoal, ...prevGoals];
      });
    };

➕ 1-3. 목록 삭제 기능

  • 목록에서 특정 항목을 삭제할 때는 JavaScript의 filter() 메서드를 사용하는 것이 가장 이상적입니다. filter()는 원본 배열을 변경하지 않고, 특정 조건을 만족하는 요소들로만 구성된 새로운 배열을 반환합니다.

    const deleteItemHandler = goalId => {
      setCourseGoals(prevGoals => {
        // 삭제할 id와 일치하지 않는 항목들만 필터링하여 새로운 배열 생성
        const updatedGoals = prevGoals.filter(goal => goal.id !== goalId);
        return updatedGoals;
      });
    };

✅ 2. 동적 스타일링 기법

  • 컴포넌트의 상태(state)나 속성(props)에 따라 동적으로 스타일을 변경하여 사용자에게 시각적인 피드백을 줄 수 있습니다.

➕ 2-1. 동적 인라인 스타일 조작

  • JSX의 style prop은 문자열이 아닌 JavaScript 객체를 받습니다. 이 객체의 속성값에 삼항 연산자 등을 사용하여 조건부로 다른 값을 할당할 수 있습니다.

  • 특징:

    • CSS 속성은 카멜 케이스(camelCase)로 작성합니다 (e.g., background-colorbackgroundColor).
    • 매우 동적이고 구체적인 스타일에 적합하지만, 남용하면 가독성이 떨어질 수 있습니다.
    const [isValid, setIsValid] = useState(true);
    
    // ... 입력값 검증 로직 ...
    
    return (
      <FormControl>
        <label style={{ color: !isValid ? 'red' : 'black' }}>Course Goal</label>
        <input style={{ borderColor: !isValid ? 'red' : '#ccc' }} type="text" />
      </FormControl>
    );

➕ 2-2. 동적 클래스 조작

  • 조건에 따라 클래스 이름을 동적으로 추가하거나 제거하는 방식입니다. 일반적으로 가장 권장되는 방법으로, 스타일 로직을 CSS 파일에 유지할 수 있어 관리가 용이합니다.

  • 구현: 템플릿 리터럴 (백틱 `)을 사용하여 className 문자열을 조합합니다.

    // CSS 파일에 .invalid 클래스가 정의되어 있다고 가정
    // .form-control.invalid { border-color: red; }
    
    const [isValid, setIsValid] = useState(true);
    
    return (
      // isValid가 false일 때 'invalid' 클래스가 동적으로 추가됨
      <div className={`form-control ${!isValid ? 'invalid' : ''}`}>
        <label>Course Goal</label>
        <input type="text" />
      </div>
    );

➕ 2-3. CSS Modules

  • 문제점: 일반 CSS는 전역 스코프(Global Scope)를 가지므로, 다른 컴포넌트에서 동일한 클래스 이름을 사용하면 스타일이 충돌할 수 있습니다.

  • 해결책 (CSS Modules): CSS 파일을 컴포넌트 전용으로 만들어 스타일 충돌을 원천적으로 방지하는 기술입니다.

    1. CSS 파일 이름을 [ComponentName].module.css 형식으로 짓습니다.
    2. import styles from './MyComponent.module.css'; 와 같이 CSS 파일을 import합니다.
    3. React 빌드 과정에서 클래스 이름이 고유한 해시값으로 변환됩니다. (e.g., .form-controlMyComponent_form-control__a1B2c)
    4. className={styles['class-name']} 또는 className={styles.className} 형태로 고유한 클래스 이름을 적용합니다.
    // GoalInput.module.css
    .form-control { /* ... 기본 스타일 ... */ }
    .form-control.invalid label { color: red; }
    
    // GoalInput.js
    import React, { useState } from 'react';
    import styles from './GoalInput.module.css'; // CSS 모듈 import
    
    const GoalInput = props => {
      const [isValid, setIsValid] = useState(true);
    
      // ...
    
      return (
        // styles 객체를 통해 고유한 클래스 이름 적용
        <div className={`${styles['form-control']} ${!isValid ? styles.invalid : ''}`}>
          <label>Course Goal</label>
          <input type="text" />
        </div>
      );
    };

📌 요약

  • React에서 리스트 데이터를 추가/삭제할 때는 push, splice 대신, ... (스프레드 문법)이나 filter를 사용하여 항상 새로운 배열을 반환하는 방식으로 상태를 업데이트해야 합니다.
  • 동적 스타일링인라인 스타일보다 클래스 이름을 동적으로 조작하는 방식이 더 유지보수하기 좋습니다.
  • CSS Modules는 CSS 클래스 이름이 전역적으로 충돌하는 문제를 해결해주는 강력한 기능으로, 컴포넌트 기반 개발에서 스타일의 독립성과 예측 가능성을 보장합니다.

0개의 댓글