이 글은 Udemy의 【한글자막】 React 완벽 가이드 2025 with React Router & Redux 를 수강하고 정리한 내용입니다.
💡 프로젝트 개요
자세한 코드는 여기로 👉 https://github.com/An0401na/React_Study/tree/main/01-starting-project
이번 프로젝트에서는 React를 활용한 투자 계산기를 개발했다.
사용자가 입력한 데이터를 기반으로 연간 투자 금액, 예상 수익률, 투자 기간을 계산하여
매년 누적된 투자 금액과 이자를 시각적으로 보여주는 기능을 구현했다.
- React: 컴포넌트 기반 UI 개발
- useState: 사용자 입력값을 상태로 관리
- map(): 배열 데이터를 테이블 형식으로 렌더링
- Intl.NumberFormat: 숫자를 화폐 단위로 변환
🏗️ 구현 과정
1️⃣ 📌 상태 관리 & 입력값 처리 (App.jsx)
사용자의 입력값을 useState로 관리하고, 변경될 때마다 상태를 업데이트하도록 했다.
또한 duration 값이 1년 이상인지 확인하여 유효한 경우에만 결과를 출력하도록 처리했다.
const [inputs, setInputs] = useState({
initialInvestment: 10000, // 초기 투자금
annualInvestment: 1200, // 연간 투자금
expectedReturn: 6, // 예상 수익률 (%)
duration: 10 // 투자 기간 (년)
});
const inputIsValid = inputs.duration >= 1;
function handleInputValueChange(inputIdentifier, newValue) {
setInputs(prevInputs => ({
...prevInputs,
[inputIdentifier]: +newValue // 문자열 → 숫자로 변환
}));
}
✅ 배운 점
- React에서 상태(state)를 객체로 관리할 때, 기존 데이터를 보존하면서 업데이트하는 패턴을 익혔다.
- 조건부 렌더링 (&&)을 활용해 특정 조건에서만 UI를 보여주는 방법을 활용했다.
- +newValue를 사용하여 문자열을 숫자로 변환하여 상태를 업데이트하는 방식에 대해 알게 되었다.
2️⃣ 🎛️ 입력 폼 컴포넌트 (Input.jsx)
컴포넌트 재사용성을 고려하여 입력 폼을 Input 컴포넌트로 분리했다.
onChange 이벤트를 활용해 부모 컴포넌트(App.jsx)의 상태를 업데이트한다.
export default function Input({ label, inputIdentifier, value, onChangeValue }) {
function handleChange(event) {
onChangeValue(inputIdentifier, event.target.value);
}
return (
<p>
<label>{label}</label>
<inputtype="number"
required
value={value}
onChange={handleChange}
/>
</p>
);
}
✅ 배운 점
- props를 활용한 컴포넌트 재사용성 향상
- onChange 이벤트 핸들러를 부모로 전달하는 리프팅(lifting state up) 패턴 적용
3️⃣ 📊 투자 결과 테이블 렌더링 (Result.jsx)
입력된 값들을 기반으로 투자 결과를 연도별로 계산하여 테이블로 출력했
금액은 Intl.NumberFormat을 사용하여 화폐 형식으로 변환했다.
const results = calculateInvestmentResults(input);
let totalInterest = 0;
let investedCapital = input.initialInvestment;
return (
<table id="result">
<thead>
<tr>
<th>Year</th>
<th>Investment Value</th>
<th>Interest (Year)</th>
<th>Total Interest</th>
<th>Invested Capital</th>
</tr>
</thead>
<tbody>
{results.map((result) => {
totalInterest += result.interest;
investedCapital += input.annualInvestment;
return (
<tr key={result.year}>
<td>{result.year}</td>
<td>{formatter.format(result.valueEndOfYear)}</td>
<td>{formatter.format(result.interest)}</td>
<td>{formatter.format(totalInterest)}</td>
<td>{formatter.format(investedCapital)}</td>
</tr>
);
})}
</tbody>
</table>
);
✅ 배운 점
- .map()을 사용해 배열 데이터를 동적으로 테이블에 렌더링
- 누적 값 (totalInterest)을 매년 업데이트하면서 계산하는 패턴
🛠️ 트러블슈팅
❌ 1. Hydration Error: In HTML, whitespace text nodes cannot be a child of <tr>
발생 원인: JSX에서 <tr> 내부에 {/* 주석 */}을 사용하면서 불필요한 공백이 발생했기 때문
✅ 해결 방법:
JSX 내부에서는 주석을 <tr> 내부가 아닌 바깥쪽에 배치해야 한다.
{/* 각 연도의 투자 결과 표시 */}
<tr key={result.year}>
<td>{result.year}</td>{/*태그 내(HERE) 주석 작성시 오류*/}
<td>{formatter.format(result.valueEndOfYear)}</td>
<td>{formatter.format(result.interest)}</td>
<td>{formatter.format(totalInterest)}</td>
<td>{formatter.format(investedCapital)}</td>
</tr>
❌ 2. useState 상태 업데이트 문제
🚨 문제 원인: useState의 불변성 위반
const [inputs, setInputs] = useState({INPUT});
function handleInputValueChange(input, newValue){
setInputs(prevInputs => {
console.log("Inputvalue change:", input, ":", newValue);
let current = prevInputs;
current.INPUT.initialInvestment = newValue; // ❌ 상태 직접 변경 (잘못된 코드)
return current;
})
}
💡 문제 설명:
- prevInputs를 직접 변경하면 React가 상태 변경을 감지하지 못해 컴포넌트가 리렌더링되지 않음.
- React 상태는 불변성(immutable)을 유지해야 함.
✅ 해결 방법:
새로운 객체를 반환해야 함!
const [inputs, setInputs] = useState(INPUT);
function handleInputValueChange(input, newValue) {
setInputs(prevInputs => {
console.log("Input value change:", input, ":", newValue);
return {
...prevInputs, // 기존 상태 복사
[input]: newValue, // 변경된 값 적용
};
});
}
❌ 3. 변경된 값이 console.log에 찍히지 않는 문제
🚨 문제 원인: 잘못된 참조 사용
console.log("initialInvestment : "+INPUT.initialInvestment);
💡 문제 설명:
- INPUT은 useState로 관리하는 상태(state)가 아니고 단순한 변수이므로 상태 변경이 반영되지 않음.
- console.log("initialInvestment : " + INPUT.initialInvestment);는 상태 업데이트 전 값을 참조함.
✅ 해결 방법:
상태 값인 inputs를 사용해야 변경된 값을 확인할 수 있음.
console.log("initialInvestment : " + inputs.initialInvestment);
🎯 프로젝트 결과물
📌 배운 점 & 느낀 점
✅ useState를 활용한 상태 관리의 중요성을 다시 한 번 깨달았다.
✅ 컴포넌트 재사용성을 고려해 작은 단위로 분리하는 것이 유지보수에 도움이 된다.
✅ JSX에서 불필요한 공백을 조심해야 한다는 점을 배웠다.
✅ React에서 데이터 흐름을 관리하는 패턴 (props 전달, lifting state up 등)을 실습했다.
✅ +newValue를 사용하여 문자열을 숫자로 변환하여 상태 관리
이번 프로젝트를 통해 React의 기본 개념을 정리하고, 실제로 적용할 수 있었다! 🚀
더 나은 사용자 경험을 위해 차트 라이브러리와 애니메이션을 추가하는 것도 흥미로울 것 같다. 🎨
'Programming > React' 카테고리의 다른 글
[React] React 완벽 가이드 - Refs(참조) & Potals(포탈) 활용하기 (0) | 2025.03.23 |
---|---|
[React] 컴포넌트 속성에 컴포넌트 전달하기 (0) | 2025.03.18 |
[React]스프레드 연산자를 통한 속성 전달 방식- id 전달 (0) | 2025.03.12 |
[React] 함수형 컴포넌트의 반환 return 값 조건 & Fragment(프래그먼트) <>...</> (0) | 2025.03.11 |
[React] Element와 Componet의 차이 (0) | 2025.03.11 |