React Native를 이용하여 간단한 리셋, 더하기, 결과 계산기를 만들어 보겠습니다.
계산기 컴포넌트 자리 잡기(CSS)
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
backgroundColor: '#fff',
alignItems: 'stretch',
justifyContent: 'center',
},
text:{
fontSize: 60,
fontWeight: '700',
color: 'white',
paddingBottom: 30,
paddingRight: 30,
},
resultContainer: {
flex: 1,
backgroundColor: 'black',
justifyContent: 'flex-end',
alignItems: 'flex-end',
},
buttonContainer: {
flex: 1,
backgroundColor:'orange',
}
});
CSS 속성 체크
flexDirection: 'column'
column: 컬럼 형태 컴포넌트
column-reverse: 컬럼 형태 반대 컴포넌트
row: 로우 형태 컴포넌트
row-reverse: 로우 형태 반대 컴포넌트
alignItems: 'stretch'
flex-start: 시작점부터 정렬
flex-end: 끝에서부터 정렬
center: 중앙 정렬
stretch: flexDirection과 수직 방향으로 확장
baseline: 기준선에 맞춰 정렬
justifyContent: 'center'
flex-start: 시작점부터 정렬
flex-end: 끝에서부터 정렬
center: 중앙 정렬
space-between: 컴포넌트 사이의 공간을 같게 만들고 정렬
space-around: 컴포넌트 주변 공간을 같게 만들어서 정렬
space-evenly: 컴포넌트 사이와 양 끝 사이의 공간을 같게 만들어서 정렬
컴포넌트
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';
import {useState} from 'react';
const App = () => {
// useState Hook
const [result, setResult] = useState(0);
console.log('rendering', result);
return (
<View style={styles.container}>
<StatusBar style={'light'}/>
<View style={styles.resultContainer}>
<Text style={styles.text}>{result}</Text>
</View>
<View style={styles.buttonContainer}>
<Text>Button</Text>
</View>
</View>
);
};
Android 상태 바 수정 테스트
import { StatusBar, StyleSheet, Text, View } from 'react-native';
<StatusBar barStyle="light-content" backgroundColor={'red'}/>
기기별 화면 크기 확인 (Hook 이용)
const windowWidth = useWindowDimensions().width;
컴포넌트 위치 지정 체크
(화면 너비 - 여백 공간) / 4
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, useWindowDimensions, View } from 'react-native';
import Button, { ButtonTypes } from './components/Button';
import {useState} from 'react';
const App = () => {
// useState Hook
const [result, setResult] = useState(0);
const windowWidth = useWindowDimensions().width;
const width = (windowWidth - 5) / 4;
return (
<View style={styles.container}>
<StatusBar style={'light'}/>
<View style={styles.resultContainer}>
<Text style={styles.text}>{result}</Text>
</View>
<View style={styles.buttonContainer}>
<View style={styles.leftPad}>
{/* 숫자 버튼 */}
<View style={styles.number}>
{[1,2,3,4,5,6,7,8,9].map((num) => (
<Button
title={num.toString()}
onPress={ () => {}}
buttonStyle={{width, height: width, marginBottom: 1}}
/>
))}
</View>
<View style={styles.bottom}>
<Button
title="0"
onPress={ () => {}}
buttonType={ButtonTypes.NUMBER}
buttonStyle={{width: width * 2, height: width, marginTop: 1}}
/>
<Button
title="="
onPress={ () => {}}
buttonType={ButtonTypes.OPERATOR}
buttonStyle={{width, height: width, marginTop: 1}}
/>
</View>
</View>
<View>
<Button
title="C"
onPress={ () => {}}
buttonType={ButtonTypes.OPERATOR}
buttonStyle={{width, height: width, marginTop: 1}}
/>
<Button
title="-"
onPress={ () => {}}
buttonType={ButtonTypes.OPERATOR}
buttonStyle={{width, height: width, marginTop: 1}}
/>
<Button
title="+"
onPress={ () => {}}
buttonType={ButtonTypes.OPERATOR}
buttonStyle={{width, height: width * 2, marginTop: 1}}
/>
</View>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
backgroundColor: '#fff',
alignItems: 'stretch',
justifyContent: 'center',
},
text:{
fontSize: 60,
fontWeight: '700',
color: 'white',
paddingBottom: 30,
paddingRight: 30,
},
resultContainer: {
flex: 1,
backgroundColor: 'black',
justifyContent: 'flex-end',
alignItems: 'flex-end',
},
buttonContainer: {
backgroundColor:'white',
flexDirection: 'row',
justifyContent: 'space-evenly',
},
leftPad: {
width: '75%',
},
number: {
flexWrap: 'wrap-reverse',
flexDirection: 'row',
justifyContent: 'space-evenly',
},
bottom: {
flexDirection: 'row',
justifyContent: 'space-evenly',
},
});
export default App;
map( Array.map() ) 함수는
배열에 내장된 함수로 배열을 순환하며 원본 배열과 같은 크기의 새로운 배열을 만들어 반환하는 함수이다.
레이아웃 구성
로직 구현
// 함수
const onPressNumber = function(number) {
setResult(function(prev){
return prev * 10 + number;
});
};
// 람다식 변형
const onPressNumber = (number) => {
setResult((prev) => prev * 10 + number);
};
숫자에 콤마를 추가 해 주고 싶으면
// lib ver
{result.toLocaleString()}
// 정규식 ver
{result.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
정규식 표현을 권장함.
사용된 내장 함수
Array.pop 함수
[1,2,3,4].pop();
Array.pop: 배열의 마지막 인덱스에 있는 값을 반환하면서 원본 배열에서는 삭제하는 함수
비어있다면 undefined 반환
let last = [1,2,3,4].pop();
console.log(last); // 4
nullish 병합 연산자
value ?? 0
nullish 병합 연산자 : ??
value(좌측) 값이 null 또는 undefined이면 우측 값 반환 그렇지 않으면 좌측 값 반환
includes 함수
[1, 2].includes(value)
배열의 내장함수로 특정 값이 배열에 있는지 확인
이해를 위해 변수를 두개로 나누어 처리 (formula, formula2)
한번에 처리 하고 싶다면, 처리될 값을 array로 처리
ex) [ 100, '+', 200, '-', 50 ], array를 순서대로 계산 (곱셈이나 나눗셈 경우 조건 포함 해서 처리)
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, useWindowDimensions, View } from 'react-native';
import Button, { ButtonTypes } from './components/Button';
import {useState} from 'react';
const App = () => {
// useState Hook
const [result, setResult] = useState(0);
const [formula, setFormula] = useState([]);
const [formula2, setFormula2] = useState([]);
const windowWidth = useWindowDimensions().width;
const width = (windowWidth - 5) / 4;
const [isChk, setIsChk] = useState(0);
const Operators = {
CLEAR: 'C',
PLUS: '+',
MINUS: '-',
EQUAL: '=',
};
const calculate = () => {
let calculatedNumber = 0;
if(isChk == 1){
calculatedNumber = formula[formula.length - 1] + formula2[formula2.length - 1];
}
// 리렌더링 처리
setResult(calculatedNumber);
}
// 그 외 버튼 이벤트
const onPressOperator = (operator) => {
switch (operator) {
case Operators.CLEAR:
setFormula([]);
setFormula2([]);
setResult(0);
setIsChk(0);
return;
case Operators.EQUAL:
calculate();
return;
case Operators.PLUS:
setResult(formula[formula.length - 1]+"+");
setIsChk(1);
return;
}
return;
};
// 로직 함수 호출 후 리렌더링
const onPressNumber = (number) => {
const last = formula[formula.length - 1];
const last2 = formula2[formula2.length - 1];
if(isChk == 1){
if(isNaN(last2)){
setResult(last + "+" + number);
setFormula2((prev) => [prev, number]);
} else { // 이 후 셋팅
const newNumber = (last2 ?? 0) * 10 + number;
setResult(last + "+" + newNumber);
setFormula2((prev) => {
// 배열의 마지막 인덱스 값을 반환
prev.pop();
return [prev, newNumber];
});
}
} else {
if(isNaN(last)){ // 최초 셋팅
setResult(number);
setFormula((prev) => [prev, number]);
} else { // 이 후 셋팅
const newNumber = (last ?? 0) * 10 + number;
setResult(newNumber);
setFormula((prev) => {
// 배열의 마지막 인덱스 값을 반환
prev.pop();
return [prev, newNumber];
});
}
}
};
return (
<View style={styles.container}>
<StatusBar style={'light'}/>
<View style={styles.resultContainer}>
{/* 숫자에 콤마 추가 .toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') */}
<Text style={styles.text}>{result}</Text>
</View>
<View style={styles.buttonContainer}>
<View style={styles.leftPad}>
{/* 숫자 버튼 */}
<View style={styles.number}>
{[1,2,3,4,5,6,7,8,9].map((num) => (
<Button
title={num.toString()}
onPress={ () => onPressNumber(num)}
buttonStyle={{width, height: width, marginBottom: 1}}
/>
))}
</View>
<View style={styles.bottom}>
<Button
title="0"
onPress={ () => onPressNumber(0)}
buttonType={ButtonTypes.NUMBER}
buttonStyle={{width: width * 2, height: width, marginTop: 1}}
/>
<Button
title={Operators.EQUAL}
onPress={ () => onPressOperator(Operators.EQUAL)}
buttonType={ButtonTypes.OPERATOR}
buttonStyle={{width, height: width, marginTop: 1}}
/>
</View>
</View>
<View>
<Button
title={Operators.CLEAR}
onPress={ () => onPressOperator(Operators.CLEAR)}
buttonType={ButtonTypes.OPERATOR}
buttonStyle={{width, height: width, marginTop: 1}}
/>
<Button
title={Operators.MINUS}
onPress={ () => onPressOperator(Operators.MINUS)}
buttonType={ButtonTypes.OPERATOR}
buttonStyle={{width, height: width, marginTop: 1}}
/>
<Button
title={Operators.PLUS}
onPress={ () => onPressOperator(Operators.PLUS)}
buttonType={ButtonTypes.OPERATOR}
buttonStyle={{width, height: width * 2, marginTop: 1}}
/>
</View>
</View>
</View>
);
};
결과 예시:
이퀄 (=) 버튼 터치시