๐Ÿ”ฎ ์†Œ๋งˆ๋ฒ• ํ”„๋กœ์ ํŠธ -2 (calculator)

๋งจ์ฒ˜์Œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ TodoList ๋ณด๋‹ค ๋” ๊ธฐ๋ณธ์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋Š” ๊ณ„์‚ฐ๊ธฐ๋ฅผ ํƒํ–ˆ๋‹ค.

๋ชจ๋“  ์—ฐ์‚ฐ์€ ๋ง์…ˆ ๋บ„์…ˆ์œผ๋กœ ์ด๋ค„์ง„๋‹ค..!


#. Project Map


์ œ์ž‘๋…ธํŠธ ํ•œ๋ˆˆ์—๋ณด๊ธฐ[์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ]

1. ๋ ˆ์ด์•„์›ƒ

calculator

1-1. ๋ ˆ์ด์•„์›ƒ1 (flex)

์ „์ฒด container๋Š” pixel๋กœ ๊ณ ์ •์ ์ธ ํฌ๊ธฐ๋ฅผ ์žก์•„์ฃผ์—ˆ๊ณ 

์š”์†Œ ๋ฐฐ์น˜ ๋ ˆ์ด์•„์›ƒ์€ flex box๋กœ ์žก์•„๋ณด์•˜๋‹ค.

๋น ..๋„!

๐Ÿฅ ๋ ˆ์ด์•„์›ƒ์€ ๋‚ด๊ฐ€ ์›ํ•˜๋Š”๋Œ€๋กœ ์žกํ˜”๋Š”๋ฐ ๋ฐ‘์— 0, ., = ๋ฒ„ํŠผ์ด ์‚ด์ง ๋งž์ง€ ์•Š์•˜๋‹ค.

flex container์˜ ์ž์‹ ์†์„ฑ์œผ๋กœ ์ƒ๋Œ€์ ์ธ ํฌ๊ธฐ๋ฅผ ๋งž์ถ”๊ธฐ ์œ„ํ•ด ๋‚˜๋จธ์ง€ ๋ฒ„ํŠผ๋“ค์„ flex : 1๋กœ ์ฃผ๊ณ  0๋ฒ„ํŠผ๋งŒ flex : 2๋กœ ์ฃผ์—ˆ๋Š”๋ฐ ์š”์†Œ๋“ค์˜ ์ƒ๋Œ€์  ํฌ๊ธฐ์— margin๊ฐ’๋„ ํฌํ•จํ•˜๋ฉด์„œ ๋”ฑ๋งž๋Š” ๋ ˆ์ด์•„์›ƒ์ด ๋‚˜์˜ค์ง€ ์•Š์€ ๊ฒƒ ๊ฐ™๋‹ค. ๊ทผ๋ฐ ๋””์ž์ธ์ž์ฒด๋กœ ๊ทธ๋ ‡๊ฒŒ ๋‚˜์˜์ง€ ์•Š๋‹ค๋Š” ํ•ฉ๋ฆฌํ™”๋กœ ๊ทธ๋ƒฅ ๋‘์—ˆ๋‹ค.

๋ฒฝ๋Œ์‹.. ๋ ˆ์ด์•„์›ƒ.. masonry ๋””์ž์ธ์ด์—์š”..

1-2. ๋ ˆ์ด์•„์›ƒ2 (๊ฐ’์— ๋Œ€ํ•œ ๋ทฐ 2๊ฐœ!)

calculator2

์ผ๋ฐ˜์ ์ธ ๊ณ„์‚ฐ๊ธฐ๋Š” result ๊ฐ’๊ณผ ํ˜„์žฌ ์ž…๋ ฅ๊ฐ’์„ ๋ณด์—ฌ์ฃผ๋Š” display๊ฐ€ 1๊ฐœ์กด์žฌํ•œ๋‹ค.

์ฆ‰, ํ˜„์žฌ์ž…๋ ฅ๊ฐ’์„ ๋ณด์—ฌ์ค„ ๋•Œ๋Š” ํ˜„์žฌ ์ €์žฅ๋˜์–ด์žˆ๋Š” result๊ฐ’์„ ๋ณผ ์ˆ˜ ์—†๋‹ค๋Š” ๋ถˆํŽธํ•œ ์ ์ด ์žˆ์—ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๊ฐ’์— ๋Œ€ํ•œ display ๋ทฐ๋ฅผ

  1. ์ €์žฅ๋œ result๊ฐ’ + ์ž…๋ ฅํ•œ operator ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๋ทฐ
  1. ํ˜„์žฌ ์ž…๋ ฅ๊ฐ’ ํ˜น์€ ์ตœ์ข… ๊ฒฐ๊ณผ๊ฐ’์„ ๋ณด์—ฌ์ฃผ๋Š” ๋ทฐ

๋‘๊ฐœ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

1-3. โญ title View (display1)

Title์€ styled-component์ด๋‹ค.

<Title>
  {tempResult + (pressedOperator === 'equal' ? '' : pressedOperator) ||
    'Calculator'}
</Title>

tempResult๋Š” ์ €์žฅ๋œ ๊ฐ’์ด๊ณ , ์ดˆ๊ธฐ tempResult ๊ฐ’์€ โ€๋กœ, ์ €์žฅ๋œ ๊ฐ’์ด ์—†์„ ๊ฒฝ์šฐ โ€˜Calculatorโ€™๊ฐ€ ์ฐํžˆ๋„๋ก ํ•˜์˜€๋‹ค.

1-4. โญ result View (display2)

ResultContainer, ResultText, ResultNumber๋Š” styled-component์ด๋‹ค.

<ResultContainer>
  <ResultText>Result</ResultText>
  <ResultNumber>{result}</ResultNumber>
</ResultContainer>

๊ทธ๋ฆฌ๊ณ  ์ „์ฒด ๊ฒฐ๊ณผ๊ฐ’ ํ˜น์€ ํ˜„์žฌ ์ž…๋ ฅ๊ฐ’ result์˜ ๋ทฐ๋„ ๊ตฌ์„ฑํ•ด์ฃผ์—ˆ๋‹ค.

2. ์ƒํƒœ๊ด€๋ฆฌ

const data = useSelector(state => state.calculator)
const [result, setResult] = useState(data.result || '0')
const [tempResult, setTempResult] = useState(data.tempResult || '')
const [pressedOperator, setPressedOperator] = useState(
  data.pressedOperator || ''
)
const [isFirstNumberTyping, setIsFirstNumberTyping] = useState(
  data.isFirstNumberTyping || false
)

2-1. ๋ฆฌ์•กํŠธ hooks

์ปดํฌ๋„ŒํŠธ๋ฅผ class ๊ฐ€ ์•„๋‹Œ ํ•จ์ˆ˜ํ˜•์œผ๋กœ ๊ตฌ์„ฑํ•˜๊ณ  ํด๋กœ์ €, hooks๋ฅผ ์ด์šฉํ•ด์„œ ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ์˜ ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ํ•˜์˜€๋‹ค.

์ƒํƒœ๋ณ€์ˆ˜๋Š”

  1. result : ํ˜„์žฌ ์ž…๋ ฅ๊ฐ’ || ์ตœ์ข… ๊ฒฐ๊ณผ ๊ฐ’
  1. tempResult : ์ €์žฅ๋œ ๊ฒฐ๊ณผ๊ฐ’
  1. pressedOperator : ์ €์žฅ๋œ operator
  1. isFirstNumberTyping : operand๋ฅผ ์ฒ˜์Œ์น˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋ณ€์ˆ˜

2-2. โญ isFirstNumberTyping ๋ณ€์ˆ˜

๊ณ„์‚ฐ๊ธฐ์—์„œ number๋ฅผ ์ž…๋ ฅํ•˜๋ฉด string์„ ๋”ํ•˜๋“ฏ์ด ๋‚˜์™€์•ผํ•œ๋‹ค.

1 + 1 => 11

ํ•˜์ง€๋งŒ ์ฒ˜์Œ ์ดˆ๊ธฐ๊ฐ’์€ 0์ธ๋ฐ ์œ„์ฒ˜๋Ÿผ ์ž…๋ ฅํ•˜๋ฉด

0 + 1 => not 1 but 01

์ด ํ™”๋ฉด์— ์ถœ๋ ฅ๋œ๋‹ค.

์ด ํ˜„์ƒ์„ ๋ง‰๊ธฐ ์œ„ํ•ด์„œ isFirstNumberTyping ๋ณ€์ˆ˜๋ฅผ ๋‘๊ณ  ์ฒ˜์Œ ์ž…๋ ฅ์‹œ์—๋Š” ์ฒ˜์Œ ์ž…๋ ฅํ•œ number๊ฐ€ ๊ฒฐ๊ณผ๊ฐ’์— ๋“ค์–ด๊ฐ€๋„๋ก ์„ค์ •ํ•ด์ฃผ์—ˆ๋‹ค.

๋ฌผ๋ก  ์ฒซ ํƒ€์ดํ•‘ number๊ฐ€ 0์ผ ๊ฒฝ์šฐ์˜ ์กฐ๊ฑด์ฒดํฌ๋„ ํ•ด์คฌ๋‹ค.

2-3. ๋ฆฌ๋•์Šค hooks

์ฒ˜์Œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ตฌ์„ฑํ•  ๋•Œ, calculator reducer์˜ ์ดˆ๊ธฐ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋„๋ก ์„ค์ •ํ–ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋‹ค๋ฅธ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์“ฐ๊ธฐ ์œ„ํ•ด ์ด ๊ณ„์‚ฐ๊ธฐ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ž ์‹œ Docker์— ๋„ฃ์—ˆ์„ ๋•Œ, ํ˜„์žฌ ๋ฆฌ์•กํŠธ state๋ฅผ ๋ฆฌ๋•์Šค state๋กœ ์˜ฎ๊ธฐ๋„๋ก ์„ค์ •ํ–ˆ๋‹ค.

์ฆ‰, ํ•ด๋‹น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ state ์กฐ์ž‘์€ ๋ฆฌ์•กํŠธ๋กœ,

ํ•ด๋‹น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ state ์ž„์‹œ์ €์žฅ( Docker ๋„˜๊ธฐ๊ธฐ )์€ ๋ฆฌ๋•์Šค๋กœ ํ•˜์˜€๋‹ค.

3. ์ด๋ฒคํŠธ์ฒ˜๋ฆฌ

CaculatorButtonRow๋Š” styled-component์ด๋‹ค.

<CaculatorButtonRow>
  <button
    name="1"
    type="button"
    onClick={e => buttonClickHandler(e.target.name)}
  >
    1
  </button>
  <button
    name="2"
    type="button"
    onClick={e => buttonClickHandler(e.target.name)}
  >
    2
  </button>
  <button
    name="3"
    type="button"
    onClick={e => buttonClickHandler(e.target.name)}
  >
    3
  </button>
  <button
    className="rightButton"
    name="multiple"
    type="button"
    onClick={e => buttonClickHandler(e.target.name)}
  >
    *
  </button>
</CaculatorButtonRow>

3-1. click ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ (buttonClickHandler)

๊ณ„์‚ฐ๊ธฐ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์žˆ๋Š” ๋ชจ~๋“  ๋ฒ„ํŠผ์˜ click ์ด๋ฒคํŠธํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜๋Š” buttonClickHandler ํ•จ์ˆ˜๋กœ ํ†ต์ผ ์‹œ์ผœ์ฃผ์—ˆ๋‹ค.

๋ฒ„ํŠผ๋งˆ๋‹ค name์„ ์ ์–ด์ฃผ๊ณ  e.target.name์„ ํ•จ์ˆ˜์— ์ „๋‹ฌํ•ด์คŒ์œผ๋กœ์„œ, ํ•ด๋‹น ํ•จ์ˆ˜์—์„œ ์ „๋‹ฌ๋ฐ›์€ ๊ฐ’์— ๋”ฐ๋ผ ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๊ฒŒ๋” ํ•ด์ฃผ์—ˆ๋‹ค.

const buttonClickHandler = button_type => {
  if (button_type === 'reset') {
    setResult('0')
    setTempResult('')
    setPressedOperator('')
  } else if (button_type === 'delete') {
    if (result === '0') return
    result.length === 1
      ? (setResult('0'), setIsFirstNumberTyping(true))
      : setResult(result.substr(0, result.length - 1))
  } else if (button_type === 'plus') {
    const block_result = operating(pressedOperator)
    setTempResult(block_result)
    setPressedOperator('plus')
    setIsFirstNumberTyping(true)

    if (pressedOperator !== '') {
      setResult(block_result)
    }
  } else if (button_type === 'minus') {
    const block_result = operating(pressedOperator)
    setTempResult(block_result)
    setPressedOperator('minus')
    setIsFirstNumberTyping(true)

    if (pressedOperator !== '') {
      setResult(block_result)
    }
  } else if (button_type === 'multiple') {
    const block_result = operating(pressedOperator)
    setTempResult(block_result)
    setPressedOperator('multiple')
    setIsFirstNumberTyping(true)

    if (pressedOperator !== '') {
      setResult(block_result)
    }
  } else if (button_type === 'divide') {
    const block_result = operating(pressedOperator)
    setTempResult(block_result)
    setPressedOperator('divide')
    setIsFirstNumberTyping(true)

    if (pressedOperator !== '') {
      setResult(block_result)
    }
  } else if (button_type === 'equal') {
    const block_result = operating(pressedOperator)
    setTempResult(block_result)
    setPressedOperator('equal')
    setIsFirstNumberTyping(true)
    if (pressedOperator !== '') {
      setResult(block_result)
    }
  } else if (button_type === 'period') {
    if (result.indexOf('.') < 0) {
      setResult(result + '.')
    }
    setIsFirstNumberTyping(false)
  } else if (button_type === 'toggleSign') {
    setResult(result[0] === '-' ? result.substr(1) : '-' + result)
  } else if (
    button_type === '1' ||
    '2' ||
    '3' ||
    '4' ||
    '5' ||
    '6' ||
    '7' ||
    '8' ||
    '9' ||
    '0'
  ) {
    if (isFirstNumberTyping) {
      setResult(button_type)
      setIsFirstNumberTyping(false)
    } else {
      result[0] === '0'
        ? setResult(button_type)
        : setResult(result + button_type)
    }
  }
}

3-2. โญ operating ํ•จ์ˆ˜

const operating = operator_type => {
  if (operator_type === 'plus')
    return String(Number(tempResult) + Number(result))
  else if (operator_type === 'minus')
    return String(Number(tempResult) - Number(result))
  else if (operator_type === 'multiple')
    return String(
      tempResult === '' ? Number(result) : Number(tempResult) * Number(result)
    )
  else if (operator_type === 'divide')
    return String(
      tempResult === '' ? Number(result) : Number(tempResult) / Number(result)
    )
  else return String(Number(result))
}

๊ณ„์‚ฐ๊ธฐ ํ”„๋กœ๊ทธ๋žจ์€ number๋ฅผ ์ž…๋ ฅํ•˜๊ณ  operator๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ๋‹ค์Œ number๋ฅผ ์ž…๋ ฅํ•˜๋Š” ์‹์œผ๋กœ ๊ฐ’์„ ๊ณ„์‚ฐํ•œ๋‹ค.

1 + 3 * 2 ์‹์œผ๋กœ ๋ˆ„๋ฅด๋ฉด 7์ด ์•„๋‹ˆ๋ผ, 8์ด ๋‚˜์˜ด.

์ฆ‰, 1 + 3 ๊นŒ์ง€ ๋ˆ„๋ฅด๊ณ  *๋ฅผ ๋ˆŒ๋ €์„ ๋•Œ, ํ”„๋กœ๊ทธ๋žจ์€ *๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ +๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ  *๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ณ„์‚ฐํ•ด์•ผํ•œ๋‹ค.

๊ทธ๋ž˜์„œ e.target.name์— operator๊ฐ€ ๋“ค์–ด์˜ค๋ฉด ์ €์žฅ๋œ operator๋ฅผ ํ†ตํ•ด์„œ operating ํ•จ์ˆ˜๋ฅผ ๋จผ์ € ์‹คํ–‰ํ•˜๊ฒŒ๋” ํ•˜์˜€๋‹ค.

4. ๊ฐœ์ธ์ ์ธ ํ”ผ๋“œ๋ฐฑ

4-1. flex layout

์œ„์—์„œ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด, 0๋ฒ„ํŠผ์ด ๋ฒ„ํŠผ ๋‘๊ฐœ์˜ ํฌ๊ธฐ๋งŒํผ ๋“ค์–ด๋งž์ง€๋Š” ์•Š๋Š”๋‹ค.

4-2. , ์ฝค๋งˆ

์ˆซ์ž๊ฐ€ ์„ธ์ž๋ฆฌ์ˆ˜๋ฅผ ์ดˆ๊ณผํ•  ๋•Œ๋งˆ๋‹ค ,๋ฅผ ์ฐ๋Š” ๊ธฐ๋Šฅ์ด ์—†๋‹ค.

ex) 1,000,000

๊ณ„์‚ฐ๊ธฐ์˜ period(์ ), ๋ถ€ํ˜ธ ๋“ฑ์€ ๋ชจ๋‘ ๊ฐ’์˜ length ๋ณ€ํ™”๋ฅผ ์ค€๋‹ค. ํ•˜์ง€๋งŒ ,(์ฝค๋งˆ)๋ฅผ ๋„ฃ์œผ๋ ค๋ฉด ์ด๋Ÿฐ length ๋ณ€ํ™”์— ๋”ฐ๋ฅธ ์ฒ˜๋ฆฌ๋ฅผ ๋‹ค์‹œ ํ•ด์ค˜์•ผํ•˜๋Š”๋ฐ ํ•˜์ง€ ์•Š์•˜๋‹ค.


Written by@taenyKim
์›น ํ”„๋ก ํŠธ์—”๋“œ ๊ณต๋ถ€ ๋ธ”๋กœ๊ทธ / Learn in Public

GitHubFacebook