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

์ด๋ฒˆ์— ๋งŒ๋“ค์–ด๋ณธ ์†Œ๋งˆ๋ฒ• ํ”„๋กœ์ ํŠธ๋Š” ๋กœ๋˜ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ, OOP study ๋ฏธ์…˜ ์ค‘ ํ•˜๋‚˜์ด๋‹ค. OOP ์™€ ์œ ๋‹›ํ…Œ์ŠคํŠธ์— ์ค‘์‹ฌ์„ ๋‘์–ด์„œ ์ฝ”๋“œ๋ฅผ ์งœ๋ณด์•˜๊ณ , ์“ฐ์ด๋Š” ํ•จ์ˆ˜๋“ค์€ ์ตœ๋Œ€ํ•œ ๋ชจ๋“ˆํ™”์‹œ์ผœ์„œ ์ฝ”๋“œ๋ฅผ ์งœ๋ณด์•˜๋‹ค.


#. Project Map


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

1. Layout

1-1. ๋ฉ”์ธํ™”๋ฉด

lotto1

๊ตฌ์ž… ๊ธˆ์•ก์œผ๋กœ ์‹œ์ž‘. (์ˆซ์ž์ž…๋ ฅ)

1-2. ๊ฒฐ๊ณผํ™”๋ฉด

lotto2

๋กœ๋˜ 1๋“ฑ์— ๋‹น์ฒจ๋˜์—ˆ๋‹ค..! ใ…Žใ…Ž

2. Code flow

๋กœ๋˜ ํ”„๋กœ๊ทธ๋žจ์˜ ๊ฒฝ์šฐ, ์ž…๋ ฅํผ submit ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•œ ํ›„, ์กฐ๊ฑด์— ๋งž๋Š” ๋‹ค์Œ ์ž…๋ ฅํผ์ด ๋‚˜์™€์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— react hooks๋ฅผ ์ด์šฉํ•ด์„œ ๊ฐ’์ด true๊ฐ€ ๋˜์—ˆ์„ ์‹œ, ๋‹ค์Œ ์ž…๋ ฅํผ์„ ์ƒ์„ฑํ•ด์ฃผ๋„๋ก ํ•ด์ฃผ์—ˆ๋‹ค.

const [gotALottoCount, setGotALottoCount] = useState(false)

const onSubmitPurchaseAmount = (e: any) => {
  e.preventDefault()
  setGotALottoCount(false)

  //...
  setGotALottoCount(true)
}

return
{
  gotALottoCount && (
    <form onSubmit={onSubmitManualLottoCount}>
      <label htmlFor="manualLottoCountInput">
        ์ˆ˜๋™์œผ๋กœ ๊ตฌ๋งคํ•  ๋กœ๋˜ ์ˆ˜๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.{' '}
      </label>
      <input
        id="manualLottoCountInput"
        type="text"
        value={manualLottoCount}
        onChange={onChangeManualLottoCount}
      ></input>
      <button type="submit">์ž…๋ ฅ</button>
    </form>
  )
}

์˜ˆ๋ฅผ ๋“ค์–ด, onSubmitPurchaseAmount ๊ฐ€ ๊ตฌ์ž…๊ธˆ์•ก ์ž…๋ ฅํผ์— ๋Œ€ํ•œ submit ์ด๋ฒคํŠธํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜์ธ๋ฐ, ํ•ด๋‹น ํ•จ์ˆ˜๊ฐ€ ๋๋‚˜๊ณ  setGotALottoCount(true) ๋ฅผ ํ†ตํ•ด gotALottoCount๋ฅผ true๋กœ ๋ฐ”๊ฟ” ์ฃผ์–ด์„œ, ๋‹ค์Œ form ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ ์ƒ์„ฑ๋˜๋„๋ก ํ•ด์ฃผ์—ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  submit ์ด๋ฒคํŠธํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜ ์ดˆ๋ฐ˜์— setGotALottoCount(false) ๋ผ๋Š” ์ดˆ๊ธฐ๊ฐ’ ๋ฆฌ์…‹์ฝ”๋“œ๋„ ๋„ฃ์–ด์ฃผ์—ˆ๋‹ค.

3. Validator

3-1. ์œ ํšจ์„ฑ๊ฒ€์‚ฌ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ

react hooks ๋ฅผ ํ†ตํ•ด, ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ์— ๋”ฐ๋ฅธ ์—๋Ÿฌ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ๋„ ํ•ด์ฃผ์—ˆ๋‹ค. ์—๋Ÿฌ๋ฉ”์‹œ์ง€์— ๋Œ€ํ•œ ๋„ค์ด๋ฐ์„ ํ•˜๋‹ค๋ณด๋‹ˆ ๋ณ€์ˆ˜๋ช…์ด 40์ž๊ฐ€ ๋„˜์–ด๊ฐ€๋Š” ๊ฒฝ์šฐ๊ฐ€ ํ—ˆ๋‹คํ–ˆ๋‹ค. ใ… ใ…  ๋ณ€์ˆ˜๋ช…์— ๋Œ€ํ•œ ๊ณ ๋ฏผ๋„ ๋‹ค์‹œ ํ•ด๋ด์•ผ๊ฒ ๋‹ค..

์—๋Ÿฌ๋ฉ”์‹œ์ง€์— ๋Œ€ํ•œ ๋ณ€์ˆ˜๋ช… ํ•ด๊ฒฐ! ํ•ด๊ฒฐํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ์ด๋™

const [purchaseAmountError, setPurchaseAmountError] = useState('')

return
{
  purchaseAmountError === 'PURCHASE_AMOUNT_IS_BLANK_ERROR' && (
    <div style={{ color: 'red' }}>๊ตฌ์ž… ๊ธˆ์•ก์„ ์ž…๋ ฅํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.</div>
  )
}
{
  purchaseAmountError === 'PURCHASE_AMOUNT_IS_NOT_NUMBER_ERROR' && (
    <div style={{ color: 'red' }}>๊ตฌ์ž… ๊ธˆ์•ก์€ ์ˆซ์ž๋กœ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.</div>
  )
}
{
  purchaseAmountError ===
    'PURCHASE_AMOUNT_IS_LESS_THAN_MINIMUM_AMOUNT_ERROR' && (
    <div style={{ color: 'red' }}>
      ๋กœ๋˜ ์ตœ์†Œ ๊ตฌ์ž… ๊ฐ€๊ฒฉ์€ {LOTTO_PRICE}์›์ž…๋‹ˆ๋‹ค.
    </div>
  )
}

3-2. Validator ๋ชจ๋“ˆ

Validator(์œ ํšจ์„ฑ๊ฒ€์‚ฌ)๋Š” ๋ชจ๋“ˆ๋กœ ๋”ฐ๋กœ ๊ด€๋ฆฌํ•ด์ฃผ์—ˆ๊ณ  ์—๋Ÿฌ๋ฐœ์ƒ์‹œ ์—๋Ÿฌ๋ฉ”์‹œ์ง€ ๋ฌธ์ž์—ด์„ ๋ฆฌํ„ดํ•˜๊ณ , ์—๋Ÿฌ๊ฐ€ ์—†์œผ๋ฉด ์ž…๋ ฅ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ๋ฆฌํ„ดํ•˜๋„๋ก ํ•ด์ฃผ์—ˆ๋‹ค.

// App.tsx

import { validatePurchaseAmountInput } from './modules/formValidator'

const onSubmitPurchaseAmount = (e: any) => {
  e.preventDefault()
  setPurchaseAmountError('')
  setGotALottoCount(false)

  const validatedPurchaseAmount = validatePurchaseAmountInput(
    purchaseAmount,
    LOTTO_PRICE
  )
  if (validatedPurchaseAmount === 'PURCHASE_AMOUNT_IS_BLANK_ERROR') {
    return setPurchaseAmountError('PURCHASE_AMOUNT_IS_BLANK_ERROR')
  }
  if (validatedPurchaseAmount === 'PURCHASE_AMOUNT_IS_NOT_NUMBER_ERROR') {
    return setPurchaseAmountError('PURCHASE_AMOUNT_IS_NOT_NUMBER_ERROR')
  }
  if (
    validatedPurchaseAmount ===
    'PURCHASE_AMOUNT_IS_LESS_THAN_MINIMUM_AMOUNT_ERROR'
  ) {
    return setPurchaseAmountError(
      'PURCHASE_AMOUNT_IS_LESS_THAN_MINIMUM_AMOUNT_ERROR'
    )
  }
  const _purchaseAmount = Number(validatedPurchaseAmount)
  const lottoCount = Math.floor(_purchaseAmount / LOTTO_PRICE)
  setLottoCount(lottoCount)
  setGotALottoCount(true)
}

์œ ํšจ์„ฑ๊ฒ€์‚ฌ ๋ชจ๋“ˆ formValidator.ts ํŒŒ์ผ ์•ˆ์—์„œ ํ•จ์ˆ˜ํ•˜๋‚˜ํ•˜๋‚˜ export ํ•ด์ฃผ์—ˆ๋‹ค.

// formValidator.ts

export const validatePurchaseAmountInput = (
  purchaseAmount: string,
  lottoPrice: number
) => {
  let _purchaseAmount = purchaseAmount.trim()

  if (_purchaseAmount.length === 0) {
    return 'PURCHASE_AMOUNT_IS_BLANK_ERROR'
  }

  const purchaseAmountHasString =
    _purchaseAmount && _purchaseAmount.match(/\D/g)
  if (purchaseAmountHasString !== null && purchaseAmountHasString.length >= 0) {
    return 'PURCHASE_AMOUNT_IS_NOT_NUMBER_ERROR'
  }
  if (_purchaseAmount.length < String(lottoPrice).length) {
    return 'PURCHASE_AMOUNT_IS_LESS_THAN_MINIMUM_AMOUNT_ERROR'
  }

  return _purchaseAmount
}

4. Lotto ๊ฐ์ฒด

๋กœ๋˜ ํ•œ์žฅ์„ ์˜๋ฏธํ•˜๋Š” Lotto ๊ฐ์ฒด๋ฅผ class ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•ด์„œ ๋งŒ๋“ค์–ด์ฃผ์—ˆ๋‹ค.

export class Lotto {
  numbers: number[] = []

  constructor(numbers: number[]) {
    this.numbers = numbers
  }
}

์•„๋ž˜ ์ฝ”๋“œ๋Š” ์ˆ˜๋™๋กœ๋˜ ๋ฒˆํ˜ธ๋ฅผ Lotto ๊ฐ์ฒด ๋ฐฐ์—ด ์•ˆ์— ๋„ฃ๋Š” ์˜ˆ์‹œ์ธ๋ฐ, ์ž…๋ ฅ๋ฐ›์€ ๋ฌธ์ž์—ด์„ ๋ถ„๋ฆฌํ•˜๊ณ  ์ˆซ์ž๋กœ ๋ณ€ํ™˜ํ•œ ํ›„ sorting๊นŒ์ง€ ํ•ด์„œ Lotto๊ฐ์ฒด์— ๋‹ด์•„์ฃผ์—ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  Lotto ๊ฐ์ฒด ๋ฐฐ์—ด์— push ํ•ด์ฃผ์—ˆ๋‹ค.

์ด ์—ญ์‹œ, ์ œ๋Œ€๋กœ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Validator(์œ ํšจ์„ฑ ๊ฒ€์‚ฌ)๋ฅผ ํ†ตํ•ด ๋ชจ๋“  ์—๋Ÿฌ๋ฅผ ์žก์•„์ฃผ๋Š” ๊ฒƒ์ด ์„ ๊ฒฐ์กฐ๊ฑด์ด์—ˆ๋‹ค.

const _myLottos: Lotto[] = []
for (let i = 0; i < Number(manualLottoCount); i++) {
  const _manualLotto = manualLottos[i].split(',')
  const manualLottoNumbers = _manualLotto.map(lottoNumber =>
    Number(lottoNumber)
  )
  const sortedManualLottoNumbers = manualLottoNumbers.sort((a, b) => a - b)
  _myLottos.push(new Lotto(sortedManualLottoNumbers))
}

5. WinningLotto ๊ฐ์ฒด

WinningLotto ๊ฐ์ฒด๋Š” ์šฐ์Šน๋กœ๋˜์— ๋Œ€ํ•œ ์ •๋ณด์™€ ์œ ์ €๋กœ๋˜์™€ ๋น„๊ตํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ๊ฐ–๋Š” ๊ฐ์ฒด๋กœ, ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ class ๋ฌธ๋ฒ•์„ ์ด์šฉํ•ด์„œ ๋งŒ๋“ค์–ด์ฃผ์—ˆ๋‹ค.

import { Lotto } from './Lotto'

export class WinningLotto {
  lotto: Lotto
  bonusNo: number = 0

  constructor(lotto: Lotto, bonusNo: number) {
    this.lotto = lotto
    this.bonusNo = bonusNo
  }

  match(userLotto: Lotto) {
    let count = 0
    let bonusCount = 0
    this.lotto.numbers.map(number => {
      userLotto.numbers.indexOf(number) >= 0 && count++
    })
    userLotto.numbers.indexOf(this.bonusNo) >= 0 && bonusCount++
    if (count === 6) return 'FIRST'
    if (count === 5 && bonusCount) return 'SECOND'
    if (count + bonusCount === 5) return 'THIRD'
    if (count + bonusCount === 4) return 'FOURTH'
    if (count + bonusCount === 3) return 'FIFTH'
    return 'MISS'
  }
}

match ๋ฉ”์†Œ๋“œ๋Š” ๋งž์€ ๋กœ๋˜ ๊ฐœ์ˆ˜์— ๋”ฐ๋ฅธ ๋“ฑ์ˆ˜๋ฅผ ๋ฆฌํ„ดํ•˜๋ฏ€๋กœ ์ด๋Ÿฐ์‹์œผ๋กœ ์‚ฌ์šฉํ•ด์ฃผ์—ˆ๋‹ค.

const RANKS = {
  FIRST: 0,
  SECOND: 0,
  THIRD: 0,
  FOURTH: 0,
  FIFTH: 0,
  MISS: 0,
}

for (let i = 0; i < myLottos.length; i++) {
  RANKS[winningLotto.match(myLottos[i])]++
}

6. LottoFunctions ๋ชจ๋“ˆ

๋กœ๋˜์— ๋Œ€ํ•œ ํ•จ์ˆ˜๋ชจ๋“ˆ์„ ๋ชจ์•„๋‘๋Š” lottoFunctions.ts ๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์—ˆ๋‹ค. ์ด ํŒŒ์ผ ๋‚ด์—์„œ๋Š” ๋žœ๋ค ๋กœ๋˜๋„˜๋ฒ„๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜์™€, ๊ทธ์— ๋งž๋Š” ๋กœ๋˜ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์„œ ๋กœ๋˜ ๊ฐ์ฒด๋ฐฐ์—ด์„ ๋ฆฌํ„ดํ•˜๋Š” ํ•จ์ˆ˜ ๋‘๊ฐœ๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์—ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋กœ๋˜ ๊ทœ์น™์ด ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์œผ๋‹ˆ, ์ƒ์ˆ˜๋˜ํ•œ ์™ธ๋ถ€์—์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก LOTTO_NUMBERS (๋ชจ๋“  ๋กœ๋˜๋ฒˆํ˜ธ๊ฐ€ ๋“ค์–ด์žˆ๋Š” ๋ฐฐ์—ด : [1,2,3โ€ฆ45]), LOTTO_COUNT (๋กœ๋˜๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š” ํšŸ์ˆ˜ : 6) ์„ parameter๋กœ ๋„ฃ๊ฒŒ ํ•ด์ฃผ์—ˆ๋‹ค.

// lottoFunctions.ts
import { Lotto } from './Lotto'

export const makeAutomaticLotto = (
  lottoCount: number,
  LOTTO_NUMBERS: number[],
  LOTTO_COUNT: number
) => {
  const lottos: Lotto[] = []
  for (let i = 0; i < lottoCount; i++) {
    lottos.push(
      new Lotto(
        setRandomNumbers(LOTTO_NUMBERS, LOTTO_COUNT).sort(
          (a: number, b: number) => a - b
        )
      )
    )
  }
  return lottos
}

export const setRandomNumbers = (
  LOTTO_NUMBERS: number[],
  LOTTO_COUNT: number
) => {
  let takenLottoNumbers = [...LOTTO_NUMBERS]
  const resultNumbers: number[] = []
  for (let j = 0; j < LOTTO_COUNT; j++) {
    const randomNumber = Math.floor(
      Math.random() * (LOTTO_NUMBERS[LOTTO_NUMBERS.length - 1] - j)
    )
    const chosen = takenLottoNumbers.splice(randomNumber, 1)[0]
    resultNumbers.push(Number(chosen))
  }
  return resultNumbers
}

7. UnitTest

์œ ๋‹›ํ…Œ์ŠคํŠธ๋Š” given, when, then 3๊ฐ€์ง€ ๋‹จ๊ณ„๋กœ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ๊ฒฝ์šฐ๋ฅผ (์ตœ๋Œ€ํ•œ ใ… ) ํ…Œ์ŠคํŠธ ํ•ด์ฃผ์—ˆ๋‹ค. ์•„๋ž˜๋Š” WinningLotto์˜ match ๋ฉ”์†Œ๋“œ์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ์˜ ์˜ˆ์ด๋‹ค.

// WinningLotto.test.ts

import { WinningLotto } from './WinningLotto'
import { Lotto } from './Lotto'

describe('WinningLotto๊ฐ์ฒด match๋ฉ”์†Œ๋“œ', () => {
  // given
  const FirstLotto = new Lotto([1, 2, 3, 4, 5, 6])
  const winningLotto = new WinningLotto(FirstLotto, 7)
  it('WinningLotto๊ฐ์ฒด์— 6๊ฐœ์˜ ์ˆซ์ž ๋ชจ๋‘ ๋งž์„ ๊ฒฝ์šฐ, 1๋“ฑ์„ ๋ฆฌํ„ดํ•˜๋Š”์ง€ ํ™•์ธ', () => {
    // when
    const myLotto = new Lotto([1, 2, 3, 4, 5, 6])
    const result = winningLotto.match(myLotto)
    // then
    expect(result).toStrictEqual('FIRST')
  })
  it('WinningLotto๊ฐ์ฒด์— 5๊ฐœ์˜ ์ˆซ์ž, 1๊ฐœ์˜ ๋ณด๋„ˆ์Šค๋ณผ์ด ๋งž์„ ๊ฒฝ์šฐ, 2๋“ฑ์„ ๋ฆฌํ„ดํ•˜๋Š”์ง€ ํ™•์ธ', () => {
    // when
    const myLotto = new Lotto([1, 2, 3, 4, 5, 7])
    const result = winningLotto.match(myLotto)
    // then
    expect(result).toStrictEqual('SECOND')
  })
  it('WinningLotto๊ฐ์ฒด์— 5๊ฐœ์˜ ์ˆซ์ž๋งŒ ๋งž์„ ๊ฒฝ์šฐ, 3๋“ฑ์„ ๋ฆฌํ„ดํ•˜๋Š”์ง€ ํ™•์ธ', () => {
    // when
    const myLotto = new Lotto([1, 2, 3, 4, 5, 8])
    const result = winningLotto.match(myLotto)
    // then
    expect(result).toStrictEqual('THIRD')
  })
  it('WinningLotto๊ฐ์ฒด์— 4๊ฐœ์˜ ์ˆซ์ž๋งŒ ๋งž์„ ๊ฒฝ์šฐ, 4๋“ฑ์„ ๋ฆฌํ„ดํ•˜๋Š”์ง€ ํ™•์ธ', () => {
    // when
    const myLotto = new Lotto([1, 2, 3, 4, 8, 9])
    const result = winningLotto.match(myLotto)
    // then
    expect(result).toStrictEqual('FOURTH')
  })
  it('WinningLotto๊ฐ์ฒด์— 3๊ฐœ์˜ ์ˆซ์ž๋งŒ ๋งž์„ ๊ฒฝ์šฐ, 5๋“ฑ์„ ๋ฆฌํ„ดํ•˜๋Š”์ง€ ํ™•์ธ', () => {
    // when
    const myLotto = new Lotto([1, 2, 3, 8, 9, 10])
    const result = winningLotto.match(myLotto)
    // then
    expect(result).toStrictEqual('FIFTH')
  })
  it('WinningLotto๊ฐ์ฒด์— ๋งž๋Š” ์ˆซ์ž๊ฐ€ 3๊ฐœ๋„ ์—†์„ ๊ฒฝ์šฐ MISS๋ฅผ ๋ฆฌํ„ดํ•˜๋Š”์ง€ ํ™•์ธ', () => {
    // when
    const myLotto = new Lotto([8, 9, 10, 11, 12, 13])
    const result = winningLotto.match(myLotto)
    // then
    expect(result).toStrictEqual('MISS')
  })
})

ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๋ฉด์„œ ๋ฌด์กฐ๊ฑด ์—๋Ÿฌ๊ฐ€ ๋‚˜์ง€ ์•Š๊ฒ ์ง€? ์ƒ๊ฐํ–ˆ์ง€๋งŒ ์‹ค์ˆ˜๋Š” ๊ณณ๊ณณ์— ์ˆจ๊ฒจ์ ธ ์žˆ์—ˆ๋‹ค. ใ…Žใ…Ž;; ์ด๋ฒˆ์— ํ…Œ์ŠคํŠธ์˜ ํ•„์š”์„ฑ์„ ํ™•์‹คํžˆ ๋Š๋ผ๊ฒŒ ๋˜์—ˆ๋˜ ๊ฒƒ ๊ฐ™๋‹ค.

8. Self-feedback

8-1. ๋ณ€์ˆ˜๋ช… ์ง“๊ธฐ

์—๋Ÿฌ ์ถœ๋ ฅ์— ๋Œ€ํ•œ ๋ณ€์ˆ˜๋ช…์ด ๊ธฐ๋ณธ 30์ž์—์„œ 40์ž๊นŒ์ง€ ๋„˜์–ด๊ฐ€๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์—ˆ๋‹ค. ์ตœ๋Œ€ํ•œ ๊ธฐ๋Šฅ์„ ์•Œ์•„๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ์“ฐ๋ คํ–ˆ์ง€๋งŒ ๋„ˆ๋ฌด ๊ธธ์–ด๋„ ํ•œ๋ˆˆ์— ํŒŒ์•…ํ•˜๊ธฐ๊ฐ€ ์‰ฝ์ง€ ์•Š์•˜๋‹ค. ์ด๋Ÿด ๊ฒฝ์šฐ๋Š” ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ์ข‹์„ ์ง€ ์ข€ ๋” ๊ณ ๋ฏผ ํ•ด๋ด์•ผํ•  ๊ฒƒ ๊ฐ™๋‹ค.

์ข‹์€ ๋ฐฉ๋ฒ•์ด ์ƒ๊ฐ๋‚˜์„œ ๋ณ€์ˆ˜๋ช… ๊ธธ์ด๋ฅผ ์ค„์—ฌ๋ณด์•˜๋‹ค.

๊ธฐ์กด์˜ ์ฝ”๋“œ๋Š” ์—๋Ÿฌ๋ฉ”์‹œ์ง€์˜ ๋‚ด์šฉ์— ๊ด€๋ จ๋œ ๋ณ€์ˆ˜๋ฅผ ํ•˜๋‚˜ํ•˜๋‚˜ ๋”ฐ๋กœ ๋‘ฌ์„œ, boolean ๊ฐ’์ด ๋ฐ”๋€Œ๋ฉด ํ•ด๋‹น ์—๋Ÿฌ๋ฉ”์‹œ์ง€ ๋‚ด์šฉ์„ ์ถœ๋ ฅํ•˜๋„๋ก ํ–ˆ์—ˆ๋‹ค.

๊ธฐ์กด์˜ ์ฝ”๋“œ badโ€ฆ

// App.tsx

import { validatePurchaseAmountInput } from './modules/formValidator'

const [purchaseAmountIsBlankError, setPurchaseAmountIsBlankError] = useState(
  false
)
const [
  purchaseAmountIsNotNumberError,
  setPurchaseAmountIsNotNumberError,
] = useState(false)
const [
  purchaseAmountIsLessThanMinimumAmountError,
  setPurchaseAmountIsLessThanMinimumAmountError,
] = useState(false)

const onSubmitPurchaseAmount = (e: any) => {
  e.preventDefault()
  setPurchaseAmountIsBlankError(true)
  setPurchaseAmountIsNotNumberError(true)
  setPurchaseAmountIsLessThanMinimumAmountError(true)

  const validatedPurchaseAmount = validatePurchaseAmountInput(
    purchaseAmount,
    LOTTO_PRICE
  )
  if (validatedPurchaseAmount === 'PURCHASE_AMOUNT_IS_BLANK_ERROR') {
    return setPurchaseAmountIsBlankError(true)
  }
  if (validatedPurchaseAmount === 'PURCHASE_AMOUNT_IS_NOT_NUMBER_ERROR') {
    return setPurchaseAmountIsNotNumberError(true)
  }
  if (
    validatedPurchaseAmount ===
    'PURCHASE_AMOUNT_IS_LESS_THAN_MINIMUM_AMOUNT_ERROR'
  ) {
    return setPurchaseAmountIsLessThanMinimumAmountError(true)
  }
}

์—๋Ÿฌ ๋ณ€์ˆ˜๋ฅผ ์—๋Ÿฌ๋‚ด์šฉ์— ๋”ฐ๋ผ ์ง€์–ด์ฃผ๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ purchaseAmountError ๋กœ๋งŒ ๋‘๊ณ  ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ์— ๋”ฐ๋ผ ๊ฐ’๋งŒ ๋ฐ”๊ฟ”์ฃผ์—ˆ๋”๋‹ˆ ์“ธ๋ฐ ์—†์ด ๊ธด ๋ณ€์ˆ˜๋ช…๋„ ์•ˆ์ƒ๊ธฐ๊ณ , ๋ณ€์ˆ˜ ํ•˜๋‚˜๋กœ ํ†ตํ•ฉํ•ด์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์„œ ์ข‹์•˜๋‹ค.

์ˆ˜์ •ํ•œ ์ฝ”๋“œ good!! ๐Ÿ‘

// App.tsx

import { validatePurchaseAmountInput } from './modules/formValidator'

const [purchaseAmountError, setPurchaseAmountError] = useState('')

const onSubmitPurchaseAmount = (e: any) => {
  e.preventDefault()
  setPurchaseAmountError('')

  const validatedPurchaseAmount = validatePurchaseAmountInput(
    purchaseAmount,
    LOTTO_PRICE
  )
  if (validatedPurchaseAmount === 'PURCHASE_AMOUNT_IS_BLANK_ERROR') {
    return setPurchaseAmountError('PURCHASE_AMOUNT_IS_BLANK_ERROR')
  }
  if (validatedPurchaseAmount === 'PURCHASE_AMOUNT_IS_NOT_NUMBER_ERROR') {
    return setPurchaseAmountError('PURCHASE_AMOUNT_IS_NOT_NUMBER_ERROR')
  }
  if (
    validatedPurchaseAmount ===
    'PURCHASE_AMOUNT_IS_LESS_THAN_MINIMUM_AMOUNT_ERROR'
  ) {
    return setPurchaseAmountError(
      'PURCHASE_AMOUNT_IS_LESS_THAN_MINIMUM_AMOUNT_ERROR'
    )
  }
}

8-2. ์ปดํฌ๋„ŒํŠธ ์Šคํ”Œ๋ฆฌํŒ…

์ด๋ฒˆ lotto ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋”ฐ๋กœ ๋ถ„๋ฆฌํ•˜์ง€ ์•Š๊ณ  App.tsx ์—์„œ ๋ชจ๋“  ๋กœ์ง์„ ์‹คํ–‰ํ–ˆ๋‹ค. ์ƒํƒœ๊ด€๋ฆฌ๊ฐ€ ์‰ฝ๋‹ค๋Š” ์  ๋•Œ๋ฌธ์ด์—ˆ๋Š”๋ฐ, ๋‚˜์ค‘์— ํ•œ๋ฒˆ form ํƒœ๊ทธ๋“ค์„ ๋ถ„๋ฆฌํ•ด๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ์—ฐ์Šต์ด ๋  ๊ฒƒ ๊ฐ™๋‹ค.


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

GitHubFacebook