// App.tsx
import Calculator from "./components/Calculator";
export default function App () {
return (
<>
<Calculator />
</>
);
}
// src/components/Calculator.tsx
import { useState } from "react";
import CalculatorButton from "./CalculatorButton";
const performCalculation = (
prev: number,
current: number,
operation: string,
) => {
switch (operation) {
case "+":
return prev + current;
case "-":
return prev - current;
case "*":
return prev * current;
case "/":
return prev / current;
default:
return current;
}
};
const initialData: CalculatorState = {
currentNumber: "0", // 현재 입력/표시되는 숫자
previousNumber: "", // 이전에 입력된 숫자
operation: null, // 현재 선택된 연산자 ("+", "-", "/", "*")
isNewNumber: true, // 새로운 숫자 입력 여부
}
export default function Calculator() {
const [calculatorState, setCalculatorState] = useState<CalculatorState>(initialData);
// "C" 클릭됐을 때 실행되는 함수 (초기화 함수)
const handleClear = () => {
setCalculatorState(initialData);
};
// "+", "-", "*", "/", "-" 클릭됐을 때 실행되는 함수
const handleOperator = (
e: React.MouseEvent<HTMLInputElement, MouseEvent>,
) => {
const operator = e.currentTarget.value;
setCalculatorState((calculatorState) => {
if (calculatorState.currentNumber === "" && operator) { // currentNumber(새로 입력된 숫자)가 없지만 연산자 기호를 누른 경우
return calculatorState;
}
const current = parseFloat(calculatorState.currentNumber); // 현재 입력한 값(문자열)을 숫자로 변환시켜 대입
if (calculatorState.previousNumber && calculatorState.operation) { // 이전의 값이 있고, operation을 입력받았다면
const prev = parseFloat(calculatorState.previousNumber);
const result = performCalculation( // 연산 결과 대입
prev,
current,
calculatorState.operation,
);
return operator === "=" ? { // 등호 기호를 누른 경우
currentNumber: result.toString(),
previousNumber: "",
operation: null,
isNewNumber: true
} : { // 등호 기호가 아닌 다른 기호를 누른 경우
currentNumber: "",
previousNumber: result.toString(),
operation: operator,
isNewNumber: true
};
} else if (operator === "=") { // 이전의 값이 없지만, operation이 "="이라면
return { ...calculatorState, isNewNumber: true };
} else {
return {
currentNumber: "",
previousNumber: current.toString(), // 사용자가 연산자를 누르기 전에 입력한 값을 문자열로 변환시켜 대입
operation: operator,
isNewNumber: true,
};
}
});
};
// 0~9 클릭됐을 때 실행되는 함수
const handleNum = (e: React.MouseEvent<HTMLInputElement, MouseEvent>) => {
console.log(e.currentTarget.value);
const value = e.currentTarget.value; // 문자열
setCalculatorState((calculatorState) => ({
...calculatorState,
currentNumber: calculatorState.isNewNumber
? value // 새로운 숫자라면 그대로
: calculatorState.currentNumber + value, // 새로운 숫자가 아니라면 이전에 것과 더함 (문자열끼리 연결)
isNewNumber: false,
}));
};
// . 클릭됐을 때 실행되는 함수
const handleDot = () => {
setCalculatorState((calculatorState) => {
if (calculatorState.currentNumber.includes(".")) { // 현재 입력 값에 소수점이 포함되어 있는 경우
return calculatorState;
}
// 그렇지 않다면
return {
...calculatorState,
currentNumber: calculatorState.currentNumber + ".",
isNewNumber: false
}
})
};
const buttonConfigs: ButtonConfigs[] = [
// 객체 형태이기 때문에 중괄호
{ value: "C", className: "calc-clear", onClick: handleClear },
{ value: "/", className: "calc-operator", onClick: handleOperator },
{ value: "1", className: "calc-num", onClick: handleNum },
{ value: "2", className: "calc-num", onClick: handleNum },
{ value: "3", className: "calc-num", onClick: handleNum },
{ value: "*", className: "calc-operator", onClick: handleOperator },
{ value: "4", className: "calc-num", onClick: handleNum },
{ value: "5", className: "calc-num", onClick: handleNum },
{ value: "6", className: "calc-num", onClick: handleNum },
{ value: "+", className: "calc-operator", onClick: handleOperator },
{ value: "7", className: "calc-num", onClick: handleNum },
{ value: "8", className: "calc-num", onClick: handleNum },
{ value: "9", className: "calc-num", onClick: handleNum },
{ value: "-", className: "calc-operator", onClick: handleOperator },
{ value: ".", className: "calc-dot", onClick: handleDot },
{ value: "0", className: "calc-num", onClick: handleNum },
{ value: "=", className: "calc-result", onClick: handleOperator },
];
return (
<>
<div className="bg-[#1f1f1f] flex justify-center items-center h-screen">
<article className="w-70.5 border border-[#333] bg-[#ccc] p-1">
<form className=" grid grid-cols-[repeat(4, 65px)] auto-rows-[65px] gap-1">
<input
type="text"
className="calc-input"
readOnly
value={calculatorState.currentNumber}
/>
{buttonConfigs.map((button) => (
<CalculatorButton key={button.value} {...button} />
))}
</form>
</article>
</div>
</>
);
}
// src/components/CalculatorButton.tsx
export default function CalculatorButton({
value,
className,
onClick,
}: ButtonConfigs) {
return (
<>
<input type="button" className={className} value={value} onClick={onClick} />
</>
);
}
/* src/styles/index.css */
@import "./tailwind.css";
/* src/styles/tailwind.css */
@import "tailwindcss";
@utility calc-btn {
@apply border-2 border-[#333] cursor-pointer text-lg hover:shadow-[1px_1px_2px_#333]
}
@layer components {
.calc-input {
@apply bg-white col-span-4 text-right calc-btn px-2
}
.calc-clear {
@apply bg-[red] col-span-3 calc-btn
}
.calc-operator {
@apply bg-[orange] calc-btn
}
.calc-num {
@apply bg-white calc-btn
}
.calc-dot {
@apply bg-[green] calc-btn
}
.calc-result {
@apply col-span-2 calc-btn
}
}
// src/types/props.d.ts
type ButtonConfigs = {
value: string;
className: string;
onClick: (e: React.MouseEvent<HTMLInputElement, MouseEvent>) => void;
};
type CalculatorState = {
currentNumber: string;
previousNumber: string;
operation: null|string;
isNewNumber: boolean;
};
화살표 함수에서의 객체 반환 문법
자바스크립트에서 중괄호의 용도
1. 코드 블록
2. 객체 리터럴
자바스크립트는 화살표 함수 바로 뒤에 나오는 중괄호를 객체가 아닌 함수의 실행 블록으로 해석
--> 객체로 해석하게 하기 위해서는 중괄호를 소괄호로 감싸서 값임을 명시해야 함
컴포넌트 외부 정의와 내부 정의
| 컴포넌트 외부 | 컴포넌트 내부 | |
| 특징 | 컴포넌트가 리렌더링되어도 단 한 번만 생성 | 리렌더링될 때마다 매번 새로 생성 |
| 활용 데이터 | 컴포넌트의 state나 props를 참조하지 않는 경우 | 컴포넌트의 state나 props를 직접 참조하는 경우 |
| 주요 대상 | 변하지 않는 상수, 범용 유틸리티 함수, 정적 데이터 | 이벤트 핸들러, 상태 업데이트 로직, 파생 변수 |
'React > 타입스크립트로 배우는 리액트(React.js) : 기초부터 최신 기술까지' 카테고리의 다른 글
| 섹션 11. 할 일 관리 앱 (TODO LIST) (0) | 2026.05.14 |
|---|---|
| 섹션 10. 폼 다루기 (0) | 2026.05.07 |
| 섹션 8. 컴포넌트 스타일링 (2) (0) | 2026.04.21 |
| 섹션 8. 컴포넌트 스타일링 (1) (0) | 2026.04.16 |
| 섹션 7. 반복 렌더링과 조건부 렌더링 (0) | 2026.04.09 |