🔗 직접 작성한 코드 보기
🔗 강사 코드 보기
차후에 직접 작성한 코드는 수정이 있을 예정입니다!
import logo from "../asset/investment-calculator-logo.png";
export default function Header() {
return (
<header id="header">
<img src={logo} alt="logo image" />
<h1>Investment Calculator</h1>
</header>
);
}
import Header from "./components/Header.jsx";
function App() {
return <Header />;
}
export default App;

export default function UserInput() {
return (
<section id="user-input">
<div className="input-group">
<p>
<label htmlFor="initial-investment">Initial Investment</label>
<input type="number" id="initial-investment" required />
</p>
<p>
<label htmlFor="annual-investment">Annual Investment</label>
<input type="number" id="annual-investment" required />
</p>
</div>
<div className="input-group">
<p>
<label htmlFor="expected-investment">Expected Investment</label>
<input type="number" id="expected-investment" required />
</p>
<p>
<label htmlFor="duration">Duration</label>
<input type="number" id="duration" required />
</p>
</div>
</section>
);
}
import { useState } from "react";
export default function UserInput() {
const [userInput, setUserInput] = useState({
initialInvestment: 10000,
annualInvestment: 1200,
expectedInvestment: 6,
duration: 10,
});
}
...prevUserInput, [inputIdentifier]:newValue 부분에 대한 코드 작성에 익숙치 않아서 해당 부분을 떠올리지 못했다.function handleInputChange(inputIdentifier, newValue) {
setUserInput((prevUserInput) => {
return {
...prevUserInput,
[inputIdentifier]: newValue,
};
});
}
import { useState } from "react";
export default function UserInput() {
const [userInput, setUserInput] = useState({
initialInvestment: 10000,
annualInvestment: 1200,
expectedReturn: 6,
duration: 10,
});
function handleInputChange(inputIdentifier, newValue) {
setUserInput((prevUserInput) => {
return {
...prevUserInput,
[inputIdentifier]: newValue,
};
});
}
return (
<section id="user-input">
<div className="input-group">
<p>
<label htmlFor="initial-investment">Initial Investment</label>
<input
type="number"
id="initial-investment"
required
value={userInput.initialInvestment}
onChange={(e) =>
handleInputChange("initialInvestment", +e.target.value)
}
/>
</p>
<p>
<label htmlFor="annual-investment">Annual Investment</label>
<input
type="number"
id="annual-investment"
required
value={userInput.annualInvestment}
onChange={(e) =>
handleInputChange("annualInvestment", +e.target.value)
}
/>
</p>
</div>
<div className="input-group">
<p>
<label htmlFor="expected-investment">Expected Investment</label>
<input
type="number"
id="expected-investment"
required
value={userInput.expectedReturn}
onChange={(e) =>
handleInputChange("expectedReturn", +e.target.value)
}
/>
</p>
<p>
<label htmlFor="duration">Duration</label>
<input
type="number"
id="duration"
required
value={userInput.duration}
onChange={(e) => handleInputChange("duration", +e.target.value)}
/>
</p>
</div>
</section>
);
}
value와 onChange props를 추가했다.value={userInput.~}를 이용해서 업데이트된 상태를 반영할 수 있도록 했다.onChange에는 직접 inputIdentifier를 작성하고 화살표 함수를 이용해 이벤트 타겟의 값을 직접 전달했다.import Header from "./components/Header.jsx";
import UserInput from "./components/UserInput.jsx";
function App() {
return (
<>
<Header />
<UserInput />
</>
);
}
export default App;
export default function Results({ userInput }) {
console.log(userInput)
return <p>Results...</p>;
}
// 우선 정말 간단하게 틀만 잡는다.
// App.jsx
import { useState } from "react"; // 상태 끌어올리기
import Header from "./components/Header.jsx";
import UserInput from "./components/UserInput.jsx";
import Results from "./components/Result.jsx";
function App() {
// 상태 끌어올리기 - 상태 정의
const [userInput, setUserInput] = useState({
initialInvestment: 10000,
annualInvestment: 1200,
expectedReturn: 6,
duration: 10,
});
// 상태 끌어올리기 - 상태 업데이트 함수
function handleInputChange(inputIdentifier, newValue) {
setUserInput((prevUserInput) => {
return {
...prevUserInput,
[inputIdentifier]: newValue,
};
});
}
return (
<>
<Header />
{/* 상태 끌어올리기 - 상태 업데이트 함수, 상태를 UserInput에게 전달.*/}
<UserInput onChange={handleInputChange} userInput={userInput} />
{/* 상태 끌어올리기 - userInput을 통해서 결과를 계산 및 출력 */}
<Results userInput={userInput}/>
</>
);
}
export default App;
// 상태 끌어올리기 - App으로부터 받은 onChange함수와 userInput 받아오기
export default function UserInput({ onChange, userInput }) {
return (
<section id="user-input">
<div className="input-group">
<p>
<label htmlFor="initial-investment">Initial Investment</label>
<input
type="number"
id="initial-investment"
required
value={userInput.initialInvestment}
// onChange("initialInvestment", +e.target.value)로 함으로써 App에서 정의된 상태 업데이트 함수 동작
onChange={(e) => onChange("initialInvestment", +e.target.value)}
/>
</p>
<p>
<label htmlFor="annual-investment">Annual Investment</label>
<input
type="number"
id="annual-investment"
required
value={userInput.annualInvestment}
onChange={(e) => onChange("annualInvestment", +e.target.value)}
/>
</p>
</div>
<div className="input-group">
<p>
<label htmlFor="expected-investment">Expected Investment</label>
<input
type="number"
id="expected-investment"
required
value={userInput.expectedReturn}
onChange={(e) => onChange("expectedReturn", +e.target.value)}
/>
</p>
<p>
<label htmlFor="duration">Duration</label>
<input
type="number"
id="duration"
required
value={userInput.duration}
onChange={(e) => onChange("duration", +e.target.value)}
/>
</p>
</div>
</section>
);
}

import { calculateInvestmentResults, formatter } from "../util/investment.js";
export default function Results({ userInput }) {
// App으로부터 받아온 userInput을 가지고 계산 -> resultsData 배열 받아옴
const resultsData = calculateInvestmentResults(userInput);
// 초기 투자 금액 계산.
const initialInvestment =
resultsData[0].valueEndOfYear -
resultsData[0].interest -
resultsData[0].annualInvestment;
return (
<table id="result">
<thead>
<tr>
<th>Year</th>
<th>Investment Value</th>
<th>Interest (Year)</th>
<th>Total Interest</th>
<th>Capital</th>
</tr>
</thead>
<tbody>
{/* resultsData 배열을 가지고 map함수 -> 배열 요소 하나씩 계산 후 출력 */}
{resultsData.map((yearData) => {
// 총 이자.
const totalInterest =
yearData.valueEndOfYear -
yearData.annualInvestment * yearData.year -
initialInvestment;
// 투자한 총 금액
const totalAmountInvested = yearData.valueEndOfYear - totalInterest;
return (
<tr key={yearData.year}>
<td>{yearData.year}</td>
<td>{formatter.format(yearData.valueEndOfYear)}</td>
<td>{formatter.format(yearData.interest)}</td>
<td>{formatter.format(totalInterest)}</td>
<td>{formatter.format(totalAmountInvested)}</td>
</tr>
);
})}
</tbody>
</table>
);
}

function App() {
// 조건적 콘텐츠 - 에러메시지 출력 (duration >= 1 이어야 함)
const inputIsValid = userInput.duration >= 1;
return (
<>
...
{/* 조건적 콘텐츠 출력 */}
{!inputIsValid && <p className="center">기간은 0보다 커야합니다.</p>}
{inputIsValid && <Results userInput={userInput} />}
</>
);
}

굳이 필요하지 않다면 커스텀 컴포넌트로 인해서 프로젝트를 복잡하게 만들지 말자.
- 상태에 대한 초기값을
calculateInvestmentResults()함수 적용한 값으로 했다. 이는 프로젝트에 대한 스스로의 이해 부족으로 인해 생겼다. 이로 인해 프로젝트가 복잡하게 됨.- 상태 업데이트 함수가 복잡하다. 커스텀 컴포넌트, 상태 초기값의 잘못된 설정으로 인해서 상태 업데이트 함수가 꼬였다.
- 상태 업데이트 함수가 이전 값들을 반영해야한다는 것을 인지하고 있었으나 어떻게 해야 이전 값들을 반영할 수 있는지 방법을 몰랐고 미숙했다.
전반적으로 프로젝트에 대한 이해를 한 뒤에 어떻게 설계를 하면 좋을지 간략한 설계도를 고민해보면서 만드는 것이 좋을 것 같다. 또한 자바스크립트 문법에 대해서 아직 어려움을 가지고 있으니, 해당 부분은 더 공부가 필요하다.