Front-End/React, React Native

React Native 간단한 계산기 만들기

개발자 DalBy 2024. 5. 14. 15:54
반응형

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() ) 함수는 

배열에 내장된 함수로 배열을 순환하며 원본 배열과 같은 크기의 새로운 배열을 만들어 반환하는 함수이다.

 

레이아웃 구성

계산기 app 화면
계산기 app

 

 

 

 

 

로직 구현

// 함수
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>
  );
};

 

 

 

 

결과 예시:

계산기 app 테스트 10 + 25
계산기 app 테스트

 

이퀄 (=) 버튼 터치시 

결과 35
계산기 app 테스트 결과

 

 



반응형