JavaScript의 map() 메서드를 사용하여 배열 데이터를 순회하고, 각 항목을 JSX 엘리먼트로 변환하여 렌더링합니다. 이때 각 엘리먼트에는 React가 항목을 식별할 수 있도록 고유한 key prop을 반드시 지정해야 합니다.
{goalList.map(goal => (
<GoalItem key={goal.id} id={goal.id}>
{goal.text}
</GoalItem>
))}
새로운 항목을 목록에 추가할 때는, 기존 상태 배열을 복사한 새로운 배열을 만들고 그 앞에 새 항목을 추가합니다. 스프레드 문법(...)을 사용하면 이를 간결하게 처리할 수 있습니다.
핵심 원칙: 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];
});
};
목록에서 특정 항목을 삭제할 때는 JavaScript의 filter() 메서드를 사용하는 것이 가장 이상적입니다. filter()는 원본 배열을 변경하지 않고, 특정 조건을 만족하는 요소들로만 구성된 새로운 배열을 반환합니다.
const deleteItemHandler = goalId => {
setCourseGoals(prevGoals => {
// 삭제할 id와 일치하지 않는 항목들만 필터링하여 새로운 배열 생성
const updatedGoals = prevGoals.filter(goal => goal.id !== goalId);
return updatedGoals;
});
};
JSX의 style prop은 문자열이 아닌 JavaScript 객체를 받습니다. 이 객체의 속성값에 삼항 연산자 등을 사용하여 조건부로 다른 값을 할당할 수 있습니다.
특징:
background-color → backgroundColor).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>
);
조건에 따라 클래스 이름을 동적으로 추가하거나 제거하는 방식입니다. 일반적으로 가장 권장되는 방법으로, 스타일 로직을 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>
);
문제점: 일반 CSS는 전역 스코프(Global Scope)를 가지므로, 다른 컴포넌트에서 동일한 클래스 이름을 사용하면 스타일이 충돌할 수 있습니다.
해결책 (CSS Modules): CSS 파일을 컴포넌트 전용으로 만들어 스타일 충돌을 원천적으로 방지하는 기술입니다.
[ComponentName].module.css 형식으로 짓습니다.import styles from './MyComponent.module.css'; 와 같이 CSS 파일을 import합니다..form-control → MyComponent_form-control__a1B2c)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>
);
};
push, splice 대신, ... (스프레드 문법)이나 filter를 사용하여 항상 새로운 배열을 반환하는 방식으로 상태를 업데이트해야 합니다.