herekim / payssion

Payment๐Ÿ’ณ + Mission๐ŸŽฏ = Payssion๐Ÿ”ฅ

Home Page:https://www.npmjs.com/package/payssion

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ

NEXTSTEP ํŒŒ์ด๋„ ๋ฏธ์…˜์ธ ํŽ˜์ด๋จผ์ธ  ์•ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, Payssion์ž…๋‹ˆ๋‹ค.
Payment๐Ÿ’ณ + Mission๐ŸŽฏ = Payssion๐Ÿ”ฅ

NEXTSTEP <TDD, ํด๋ฆฐ ์ฝ”๋“œ with React> 2๊ธฐ์— ์ฐธ์—ฌํ•ด ์ง„ํ–‰ํ•œ ๋ฏธ์…˜ํ˜• ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค. ๋ฏธ์…˜์˜ ์ตœ์ข… ๋ชฉํ‘œ๋Š” ํŽ˜์ด๋จผ์ธ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ NPM์— ๋ฐฐํฌํ•˜๋Š” ๊ฒƒ์ด์—ˆ์œผ๋ฉฐ, ๊ทธ ๊ณผ์ •์—์„œ ์‚ฌ์šฉ์ž์—๊ฒŒ ํŽธ๋ฆฌํ•œ UI/UX ์ œ๊ณต, ํ•จ๊ป˜ ๊ฐœ๋ฐœํ•˜๋Š” ๋™๋ฃŒ๋ฅผ ์œ„ํ•œ ํด๋ฆฐํ•œ ์ฝ”๋“œ ์ž‘์„ฑ, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ DX ์ œ๊ณต์„ ๊ณ ๋ คํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ํ”„๋กœ์ ํŠธ๋Š” ๋ฐฐํฌ์— ์„ฑ๊ณตํ•ด ํ•จ๊ป˜ ๋ฏธ์…˜์— ์ฐธ์—ฌํ•œ ๋™๋ฃŒ ๋ถ„๋“ค์ด ์‚ฌ์šฉ์ค‘์ด๋ฉฐ, ์‹ค์ œ ์‚ฌ์šฉ์— ๋Œ€ํ•œ ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ”ํƒ•์œผ๋กœ ๊พธ์ค€ํžˆ ๊ฐœ์„ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ๋ฒ•

  1. ์„ค์น˜ํ•˜๊ธฐ

    // npm
    npm i payssion
    
    // yarn
    yarn add payssion
  2. ์ค€๋น„ํ•˜๊ธฐ (PayssionProvider)

    import { PayssionProvider } from 'payssion'
    
    function App() {
      return (
        <BrowserRouter>
          <PayssionProvider>
            <Routes>
              {routes.map((route) => (
                <Route key={route.path} path={route.path} element={route.element} />
              ))}
            </Routes>
          </PayssionProvider>
        </BrowserRouter>
      )
    }
  3. ๊ฒฐ์ œ ์‹œ์ž‘ํ•˜๊ธฐ (initiatePayment)

  • amount, onSuccessAction๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.

  • initiatePayment ํƒ€์ž…

    type InitiatePaymentParams = {
      amount: number
      onSuccessAction: () => void
    }
  • ์˜ˆ์‹œ ์ฝ”๋“œ

    import { usePayssion } from 'payssion'
    
    const PaymentComponent = () => {
      const [amount, setAmount] = useState(0)
      const onSuccessAction = () => {
        // ์„ฑ๊ณต ์‹œ ๋™์ž‘ํ•  ์ฝ”๋“œ
      }
      const { initiatePayment } = usePayssion()
      return (
        <>
          <SomeComponent />
          <PaymentButton onClick={() => initiatePayment({ amount, onSuccessAction })} />
        </>
      )
    }
  1. ๊ฒฐ์ œ ๋ชจ๋“ˆ ์—ด๊ธฐ

    import { Payssion, isOpen } from 'payssion'
    
    const SomeComponent = () => {
      const { isOpen } = usePayssion()
    
      return (
        <>
          <Header />
          <Description />
          {isOpen && <Payssion />}
        </>
      )
    }

์‚ฌ์šฉ ์˜ˆ์‹œ

2023-05-06.16.37.11.mov

๊ณ ๋ฏผ์˜ ํ”์ ๋“ค

์ปดํฌ๋„ŒํŠธ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•œ ๊ณ ๋ฏผ

๊ธฐ์กด ๊ฐœ๋ฐœ ๋ฐฉ์‹์€ ํŽ˜์ด์ง€๋ฅผ ๊ธฐ์ค€์œผ๋กœ Top-Down ๋ฐฉ์‹์œผ๋กœ ์ž‘์—…์„ ํ–ˆ๋‹ค๋ฉด, ์ด ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— ๋ฐ˜๋ณต ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํŒŒ์•…ํ•˜๊ณ  ์ž‘์€ ์ปดํฌ๋„ŒํŠธ๋ถ€ํ„ฐ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๊ฒŒ ์Œ“์•„ ์˜ฌ๋ฆฌ๋ฉด์„œ Bottom-Up ๋ฐฉ์‹์œผ๋กœ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. Thinking in react๋ฌธ์„œ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ 1๋‹จ๊ณ„: UI๋ฅผ ์ปดํฌ๋„ŒํŠธ ๊ณ„์ธต์œผ๋กœ ๋‚˜๋ˆ„๊ธฐ, 2๋‹จ๊ณ„: React๋กœ ์ •์ ์ธ ๋ฒ„์ „ ๋งŒ๋“ค๊ธฐ, 3๋‹จ๊ณ„: UI state์— ๋Œ€ํ•œ ์ตœ์†Œํ•œ์˜ (ํ•˜์ง€๋งŒ ์™„์ „ํ•œ) ํ‘œํ˜„ ์ฐพ์•„๋‚ด๊ธฐ, 4๋‹จ๊ณ„: State๊ฐ€ ์–ด๋””์— ์žˆ์–ด์•ผ ํ•  ์ง€ ์ฐพ๊ธฐ ์ด๋ ‡๊ฒŒ 4๊ฐ€์ง€ ๋‹จ๊ณ„๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„ค๊ณ„ํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

ํŠนํžˆ ํŽ˜์ด๋จผ์ธ  ์•ฑ ํŠน์„ฑ์ƒ ํผ ์ œ์–ด๋ฅผ ํ•ด์•ผํ•˜๋Š” ์ƒํ™ฉ์ด ๋งŽ์•˜๋Š”๋ฐ, CDD(Component Driven Development)์— ๊ธฐ๋ฐ˜ํ•ด UI๋ฅผ ๊ตฌ์„ฑํ•˜๋ฉด์„œ Input ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ชจ๋“  ํผ์—์„œ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. CDD ๊ธฐ๋ฐ˜ ์ ‘๊ทผ๋ฒ•์€ ์ดˆ๊ธฐ์— ์‹œ๊ฐ„์ด ์†Œ์š”๋˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋Š๊ปด์กŒ์ง€๋งŒ ์‹œ๊ฐ„์ด ์ง€๋‚ ์ˆ˜๋ก ์ปดํฌ๋„ŒํŠธ์˜ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋ฐ”ํƒ•์œผ๋กœ ํ™•์žฅ์„ฑ์ด ๋†’์•„์ง„๋‹ค๊ณ  ๋Š๊ผˆ์Šต๋‹ˆ๋‹ค. ํŠน์ • ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ข…์†ํ•˜์ง€ ์•Š์œผ๋ฉด์„œ ๋…๋ฆฝ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ํ•ฉ์„ฑ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋„์ž…ํ•  ์ˆ˜๋„ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ฐ ์ปดํฌ๋„ŒํŠธ๋Š” ์žฌ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•œ ๋…๋ฆฝ์ ์ธ ๋ชจ๋“ˆ๋กœ ๋™์ž‘ํ–ˆ์œผ๋ฉฐ ์ถ”ํ›„์— ํ…Œ์ŠคํŠธ๋ฅผ ๋„์ž…ํ•  ๋•Œ์—๋„ ์ด์ ์ด ์žˆ์„ ๊ฒƒ์ด๋ผ ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.

์ปดํฌ๋„ŒํŠธ ๊ณ„์ธต ๋ถ„๋ฆฌ ์˜ˆ์‹œ

์˜ˆ์‹œ ์ฝ”๋“œ

// InputTitle
const InputTitle = ({ children }: PropsWithChildren) => {
  return <StyledInputTitme>{children}</StyledInputTitme>
}

// Input.tsx
const Input = forwardRef(({ type = 'text', css, ...props }: InputProps, ref: ForwardedRef<HTMLInputElement>) => {
  return <InputBasic ref={ref} type={type} css={{ ...css }} {...props} />
})

// InputBox.tsx
const InputBox = ({ children, css }: PropsWithChildren<InputBoxProps>) => {
  return <StyledInputBox css={{ ...css }}>{children}</StyledInputBox>
}

// CardExpiredDate.tsx
const CardExpiredDate = ({ expiredDateRef }: CardExpiredDateProps) => {
  const { handleInputChange } = useCardExpiredDate()

  return (
    <InputContainer>
      <InputTitle>๋งŒ๋ฃŒ์ผ</InputTitle>
      <InputBox css={{ width: '50%' }}>
        <Input ref={expiredDateRef.first} placeholder="MM" data-name="MM" onInput={handleInputChange} maxLength={2} />
        <Input
          ref={expiredDateRef.second}
          placeholder="YY"
          data-name="YY"
          onInput={handleInputChange}
          maxLength={2}
        />
      </InputBox>
    </InputContainer>
  )
}

// CardAdd.tsx (CardForm ์—ญํ• )
const CardAdd = () => {
  const {
    //...
  } = useCardAdd()

  return (
    <PayssionApp>
      //...
      <CardForm>
        <CardForm.CardNumbers />
        <CardForm.CardExpiredDate />
        <CardForm.CardOwner />
        <CardForm.CardSecurityCode />
        <CardForm.CardPassword />
      </CardForm>
      //...
    </PayssionApp>
  )
}

๊ผญ ํ•„์š”ํ•œ ์ƒํƒœ์ธ๊ฐ€?

์ƒํƒœ๊ฐ€ ๋งŽ์•„์ง„๋‹ค๋Š” ๊ฒƒ์€ ๊ด€๋ฆฌ ํฌ์ธํŠธ๊ฐ€ ๋Š˜์–ด๋‚˜๋Š” ๊ฒƒ์ด๋ผ๋Š” ์ƒ๊ฐ์„ ๋ฐ”ํƒ•์œผ๋กœ ๊ธฐ๋Šฅ์ด ๋™์ž‘ํ•˜๋Š” '์ตœ์†Œํ•œ์˜ ์ƒํƒœ'๋กœ ๋งŒ๋“ค๊ณ ์ž ํ–ˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด ์•„๋ž˜ ๊ฐ€์ƒ ํ‚ค๋ณด๋“œ์˜ ๊ฒฝ์šฐ ๋ชจ๋‹ฌ ์ข…๋ฃŒ๋ฅผ ์œ„ํ•œ ref ์ƒํƒœ๋ฅผ ์ œ์™ธํ•˜๊ณ , ์–ด๋–ค ์ƒํƒœ๋„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  props๋กœ ์ „๋‹ฌ๋ฐ›๋Š” onKeyPress, ๋„๋ฉ”์ธ ํ•จ์ˆ˜์ธ getRandomVirtualDigits ๋“ฑ์˜ ๊ฐ’์„ ์ด์šฉํ•ด ์ƒํƒœ๋Š” ์ตœ์†Œํ™”ํ•˜๋ฉด์„œ ๊ธฐ๋Šฅ์ด ๋™์ž‘ํ•˜๋„๋ก ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ปดํฌ๋„ŒํŠธ์˜ ์žฌ์‚ฌ์šฉ์„ฑ ์ฆ๊ฐ€๋ผ๋Š” ์ถ”๊ฐ€ ์žฅ์ ์„ ์–ป์„ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

const VirtualKeyboard = ({ onKeyPress }: VirtualKeyboardProps) => {
  const { modalRef } = useVirtualKeyboard()

  return (
    <div ref={modalRef}>
      <BottomSheetContainer>
        <DigitButtonContainer>
          {getRandomVirtualDigits().map((digit) => (
            <DigitButton key={digit} onClick={() => onKeyPress(String(digit))}>
              {digit}
            </DigitButton>
          ))}
        </DigitButtonContainer>
      </BottomSheetContainer>
    </div>
  )
}

ํšจ์œจ์ ์ธ UI ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ์Šคํ† ๋ฆฌ๋ถ ์‚ฌ์šฉ

์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜์˜ ๊ฐœ๋ฐœ ๋ฐฉ์‹์„ ์ ์šฉํ•˜๋ฉฐ, ์Šคํ† ๋ฆฌ๋ถ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ์˜ ๋…๋ฆฝ์„ฑ์„ ํ™•๋ณดํ•˜๊ณ , ์‹œ๊ฐ์ ์œผ๋กœ ํ…Œ์ŠคํŠธํ•˜๊ณ  ๋ฌธ์„œํ™”ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด, ๊ฐ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐœ๋ณ„์ ์œผ๋กœ ๊ธฐ๋Œ€ํ•œ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์œผ๋ฉฐ, ๋””๋ฒ„๊น… ๊ณผ์ •๋„ ๋‹จ์ˆœํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๊ฐ ์ปดํฌ๋„ŒํŠธ์˜ ๋‹ค์–‘ํ•œ ์ƒํƒœ๋ฅผ ์†์‰ฝ๊ฒŒ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•  ์ˆ˜ ์žˆ์–ด, UI์˜ ๊ฐ ์š”์†Œ๊ฐ€ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ๋ฐฉ์‹์„ ๋ช…ํ™•ํ•˜๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๊ฐœ๋ณ„์ ์ธ ์ปดํฌ๋„ŒํŠธ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์กฐํ•ฉํ•œ ํŽ˜์ด์ง€ ์„น์…˜์„ ์ œ๊ณตํ•˜์—ฌ, ํŽ˜์ด์ง€ ๋‹จ์œ„์—์„œ ์–ด๋–ป๊ฒŒ UI๊ฐ€ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํฌ๋กœ๋งˆํ‹ฑ์„ ์‚ฌ์šฉํ•ด์„œ ๋ฐฐํฌํ•จ์œผ๋กœ์จ ์‚ฌ์šฉ์ž๊ฐ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์ „์— ๋ฏธ๋ฆฌ ๋™์ž‘ ๋ฐฉ์‹์„ ์‰ฝ๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

ํ™•์žฅ์„ฑ์„ ๊ณ ๋ คํ•œ ์ปดํฌ๋„ŒํŠธ ํŒจํ„ด ๋„์ž…

Form ์ปดํฌ๋„ŒํŠธ๋Š” ์—ฌ๋Ÿฌ๊ณณ์—์„œ ์žฌ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์–ธ์ œ๋“  ์š”๊ตฌ ์‚ฌํ•ญ์— ๋”ฐ๋ผ ๋ณ€ํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ Prop์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋„˜๊ฒจ์ฃผ๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด, ์š”๊ตฌ ์‚ฌํ•ญ์ด ๋ณ€ํ™”ํ•  ๋•Œ๋งˆ๋‹ค ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€ ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ , Prop์— ๋„˜๊ฒจ์ฃผ๋Š” ๋ฐ์ดํ„ฐ๋„ ์ถ”๊ฐ€๋˜๋Š” ๋“ฑ์˜ ๋ถˆํŽธํ•จ์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ํ•ฉ์„ฑ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•ด Form ๋‚ด๋ถ€์— ๋“ค์–ด๊ฐ€๋Š” ์ฝ”๋“œ๋ฅผ ์ปดํฌ๋„ŒํŠธ ์‚ฌ์šฉ ๋ถ€๋ถ„์—์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด Form์˜ ์š”๊ตฌ ์‚ฌํ•ญ์ด ๋ณ€๊ฒฝ๋˜๋”๋ผ๋„, ์ปดํฌ๋„ŒํŠธ์˜ ๋‚ด๋ถ€ ๊ตฌํ˜„์ด ์•„๋‹ˆ๋ผ ์‚ฌ์šฉ ๋ถ€๋ถ„์—์„œ ์ˆ˜์ •์„ ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์‹œ ์ฝ”๋“œ

// ์‚ฌ์šฉ ๋ถ€๋ถ„
    <CardDetailsForm>
      <CardDetailsForm.PageTitle buttonElement={<BackButton />} title="์นด๋“œ ๋ชฉ๋ก์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ" />
      <CardDetailsForm.BigCard
        onClickDeleteButton={onClickDeleteButton}
        cardNumbers={cardNumbers}
        cardName={cardName}
        cardOwner={cardOwner}
        cardExpiredDate={cardExpiredDate}
        cardType={cardType}
      />
      <CardDetailsForm.CardAliasInput inputRef={nicknameRef} defaultValue={cardNickname} />
      <CardDetailsForm.NavigationButton
        additionalClassNames="mt-50"
        onBeforeNavigate={handlePreNavigation}
        to="CardList"
        text="๋‹ค์Œ"
      />
    </CardDetailsForm>
    
// ๋‚ด๋ถ€ ๊ตฌํ˜„
const CardDetailsForm = ({ children }: PropsWithChildren) => {
  const bigCard = getCardDetailsFormSubElement(children, BigCard)
  const pageTitle = getCardDetailsFormSubElement(children, PageTitle)
  const NavigationButton = getCardDetailsFormSubElement(children, NavigationTextButton)
  const cardAliasInput = getCardDetailsFormSubElement(children, CardAliasInput)
  
  return (
    <CustomPayssionApp>
      <Title>{pageTitle}</Title>
      {bigCard}
      <InputContainer
        css={{
          flexCenter: 'center',
          width: '100%',
        }}
      >
        {cardAliasInput}
      </InputContainer>
      {NavigationButton}
    </CustomPayssionApp>
  )
}

CardDetailsForm.PageTitle = PageTitle
CardDetailsForm.BigCard = BigCard
CardDetailsForm.NavigationButton = NavigationTextButton
CardDetailsForm.CardAliasInput = CardAliasInput

Context API ์‚ฌ์šฉ ์‹œ ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง ์ตœ์ ํ™”

ํŽ˜์ด์ง€ ๊ฐ„์˜ ๊ณต์œ ๋˜์–ด์•ผ ํ•˜๋Š” ๋ช‡ ๊ฐ€์ง€ ์ƒํƒœ๋“ค์ด ์กด์žฌํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋ชจ๋‹ฌ, ์นด๋“œ, ์นด๋“œ ๋ฆฌ์ŠคํŠธ ์ƒํƒœ๊ฐ€ ์—ฌ๋Ÿฌ ํŽ˜์ด์ง€์—์„œ ๊ณต์œ ๋˜์–ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ ๋„๊ตฌ๊ฐ€ ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค. ์„œ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ผ๋Š” ์„ ํƒ์ง€๋„ ์กด์žฌํ–ˆ์ง€๋งŒ, Context API + useReducer๋ฅผ ํ™œ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” ๊ด€๋ฆฌ๋˜์–ด์•ผ ํ•˜๋Š” ์ƒํƒœ์˜ ์ˆ˜์™€ ๋ณต์žก๋„๊ฐ€ ๋†’์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋ฌด๋ถ„๋ณ„ํ•˜๊ฒŒ ์„œ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ๋  ๊ฒฝ์šฐ ์ถ”ํ›„์— ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด ํ•„์š”ํ•œ ์ƒํ™ฉ์ด๋‚˜ ์œ ์ง€๋ณด์ˆ˜๋ฅผ ํ•˜๋Š” ์ƒํ™ฉ์—์„œ ๋”์šฑ ์–ด๋ ค์›€์ด ์žˆ์„ ๊ฒƒ์ด๋ผ๊ณ  ํŒ๋‹จํ•˜๊ณ  ๋ฆฌ์•กํŠธ๋งŒ์„ ์‚ฌ์šฉํ•ด์„œ ํ•ด๊ฒฐํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

Context API๋ฅผ ์ด์šฉํ•ด ์ „์—ญ์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ์นด๋“œ ์ƒํƒœ์— ๋Œ€ํ•ด ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์˜ ์ตœ์ ํ™”๋ฅผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์นด๋“œ ์ƒํƒœ๋ฅผ ์ด์šฉํ•˜๋Š” ์นด๋“œ ํ”„๋ฆฌ๋ทฐ ์ปดํฌ๋„ŒํŠธ์™€ ์นด๋“œ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ํผ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์นด๋“œ์˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•  ๋•Œ๋งˆ๋‹ค ํผ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•˜๋Š” ์ƒํ™ฉ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ƒํƒœ ์ปจํ…์ŠคํŠธ์™€ ๋ณ€๊ฒฝ ์ปจํ…์ŠคํŠธ๋กœ ๋ถ„๋ฆฌํ•ด Provider๋ฅผ ํ†ตํ•ด ๊ฐ๊ฐ ์ œ๊ณตํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์žฌ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋ถ„๋ฆฌํ–ˆ์„ ๊ฒฝ์šฐ, ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋Š” ํ”„๋ฆฌ๋ทฐ ์ปดํฌ๋„ŒํŠธ๋งŒ ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•˜๊ณ , ํผ ์ปดํฌ๋„ŒํŠธ๋Š” Reducer ํ•จ์ˆ˜๋ฅผ ์ฐธ์กฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ปจํ…์ŠคํŠธ์˜ Value๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•˜๋‹ค๊ณ  ํŒ๋‹จํ•ด ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด, ์ปดํฌ๋„ŒํŠธ ๊ฐ„์˜ ์ƒํƒœ ๊ณต์œ ์™€ ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ์ƒํƒœ ๊ด€๋ฆฌ ์ „๋žต์˜ ๊ฐœ์„ ์„ ํ†ตํ•ด ์ปดํฌ๋„ŒํŠธ ๊ฐ„์˜ ์˜์กด์„ฑ์„ ์ค„์ด๊ณ , ํšจ์œจ์ ์ธ ์ปดํฌ๋„ŒํŠธ ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒ์— ๋” ๊นŠ์€ ์ดํ•ด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์ปค์Šคํ…€ ํ›…์„ ํ™œ์šฉํ•œ ์ฝ”๋“œ์˜ ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ

์ฝ”๋“œ์˜ ๊ด€์‹ฌ์‚ฌ๋ฅผ ๋ถ„๋ฆฌํ•˜๊ณ  ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ์ปค์Šคํ…€ ํ›…์„ ์ ๊ทน์ ์œผ๋กœ ํ™œ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ํผ, ๋ชจ๋‹ฌ, API ํ˜ธ์ถœ ๋“ฑ ๋ฐ˜๋ณต์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๋กœ์ง์— ๋Œ€ํ•ด ์ปค์Šคํ…€ ํ›…์„ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ UI๋ผ๋Š” ๋‹จ์ผ ์ฑ…์ž„์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ์œผ๋ฉฐ, ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ˆจ๊น€์œผ๋กœ์จ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์ด ์ฆ๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ hook ํด๋”๋ฅผ ์ปดํฌ๋„ŒํŠธ์™€ Co-Location ๋˜๋„๋ก ์œ„์น˜์‹œ์ผฐ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์œ ์ง€๋ณด์ˆ˜์— ํ•„์š”ํ•œ ์ฝ”๋“œ๋ฅผ ์‰ฝ๊ฒŒ ์ฐพ์„ ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ปค์Šคํ…€ ํ›… ์˜ˆ์‹œ ์ฝ”๋“œ

const CardAdd = () => {
  const {
    numbersRef,
    passwordRef,
    expiredDateRef,
    ownerRef,
    securityCodeRef,
    openValidToast,
    setOpenValidToast,
    onBeforeNavigate,
    isValidCardInfo,
  } = useCardAdd()

  return (
    //...
  )
}

Co-Location ์˜ˆ์‹œ

CardAdd
โ”ฃ components
โ”ƒ โ”ฃ CardForm
โ”ƒ โ”ƒ โ”ฃ components
โ”ƒ โ”ƒ โ”ฃ hooks --> CardForm ๋‚ด๋ถ€ ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•œ ์ปค์Šคํ…€ ํ›…
โ”ƒ โ”ƒ โ”ฃ CardForm.tsx
โ”ƒ โ”— index.ts
โ”ฃ hooks --> CardAdd ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•œ ์ปค์Šคํ…€ ํ›…
โ”ฃ CardAdd.tsx

๋น„์ œ์–ด ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ™œ์šฉํ•œ ๋ณต์žกํ•œ Form ๊ด€๋ฆฌ

์นด๋“œ ์ถ”๊ฐ€ ํŽ˜์ด์ง€์—์„œ ์ „์ฒด ์ œ์–ด ์ปดํฌ๋„ŒํŠธ -> ๋ถ€๋ถ„ ๋น„์ œ์–ด ์ปดํฌ๋„ŒํŠธ -> ์ „์ฒด ๋น„์ œ์–ด ์ปดํฌ๋„ŒํŠธ๋กœ ๋ฆฌํŒฉํ† ๋งํ–ˆ์Šต๋‹ˆ๋‹ค. ์ดˆ๊ธฐ์—๋Š” ๊ฐ€์žฅ ํŽธํ•˜๊ฒŒ ์ ‘ํ–ˆ๋˜ ์ œ์–ด ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ–ˆ์œผ๋‚˜, ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•˜๋ฉด์„œ ๋ณด์•ˆ์ฝ”๋“œ์™€ ์นด๋“œ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ์— ๋Œ€ํ•ด์„œ๋Š” ์นด๋“œ ์ปดํฌ๋„ŒํŠธ์— ์ฆ‰๊ฐ์ ์œผ๋กœ ๋ฐ˜์˜์ด ๋˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒƒ์„ ํŒŒ์•…ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ํ•ด๋‹น ์ž…๋ ฅ์— ๋Œ€ํ•ด์„œ๋Š” ๋น„์ œ์–ด ์ปดํฌ๋„ŒํŠธ๋กœ ๋™์ž‘ํ•˜๋„๋ก ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ดํ›„ ์นด๋“œ์˜ ์ƒํƒœ๊ฐ€ ๋ณด์—ฌ์ง€๋Š” ์นด๋“œ ์ปดํฌ๋„ŒํŠธ์™€ ์นด๋“œ์˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ํผ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ถ„๋ฆฌ๋˜์–ด์žˆ๊ณ , ํผ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ฆ‰๊ฐ์ ์ธ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ํŒŒ์•…ํ–ˆ์Šต๋‹ˆ๋‹ค. Context API์™€์˜ ์กฐํ•ฉ์œผ๋กœ ์ „์ฒด ์ž…๋ ฅ์— ๋Œ€ํ•ด์„œ ๋น„์ œ์–ด ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ œ์–ด ์ปดํฌ๋„ŒํŠธ์™€ ๋น„์ œ์–ด ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•ด์„œ ๊นŠ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์—ˆ์œผ๋ฉฐ ์ถ”ํ›„ ํผ ์ œ์–ด์˜ ๋ฐฉํ–ฅ์„ฑ์„ ์žก๋Š” ๋ฐ์— ๊ธฐ์ค€์ ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ๊ฒฝํ—˜์„ ๋ธ”๋กœ๊ทธ๋กœ ์ •๋ฆฌํ•ด์„œ ๊ณต์œ  ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํžˆ

All ์ œ์–ด ์ปดํฌ๋„ŒํŠธ

  • ํผ ์ œ์–ด๊ฐ€ ํ•„์š”ํ•œ Input์€ ์นด๋“œ ๋ฒˆํ˜ธ, ๋งŒ๋ฃŒ์ผ, ์นด๋“œ ์†Œ์œ ์ž, ๋ณด์•ˆ์ฝ”๋“œ, ๋น„๋ฐ€๋ฒˆํ˜ธ ๋‹ค์„ฏ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค.
  • ์ฒ˜์Œ์—๋Š” ์ต์ˆ™ํ–ˆ๋˜ ์ œ์–ด ์ปดํฌ๋„ŒํŠธ๋กœ ๋ชจ๋“  ํผ ์ œ์–ด๋ฅผ ์™„๋ฃŒํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ถ€๋ถ„์  ๋น„์ œ์–ด ์ปดํฌ๋„ŒํŠธ

  • ๋ฆฌํŒฉํ† ๋ง์„ ์ง„ํ–‰ํ•˜๋ฉฐ ๋ณด์•ˆ ์ฝ”๋“œ, ๋น„๋ฐ€๋ฒˆํ˜ธ ์ƒํƒœ๊ฐ’์€ ๋‹ค์Œ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅผ ๋•Œ์—๋งŒ ํ•„์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์ธ์ง€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ณด์•ˆ ์ฝ”๋“œ, ๋น„๋ฐ€๋ฒˆํ˜ธ์— ๋Œ€ํ•ด์„œ๋Š” ๋น„์ œ์–ด ์ปดํฌ๋„ŒํŠธ๋กœ ๊ด€๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.

All ์ œ์–ด ์ปดํฌ๋„ŒํŠธ

  • ์นด๋“œ ๋ฒˆํ˜ธ, ๋งŒ๋ฃŒ์ผ, ์นด๋“œ ์†Œ์œ ์ž ์ •๋ณด ๋˜ํ•œ ์ƒํƒœ๊ฐ’์ด ํผ ๋‚ด๋ถ€์—์„œ ํ•„์š”ํ•œ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์™ธ๋ถ€ ์ปดํฌ๋„ŒํŠธ(์นด๋“œ)์—์„œ ํ•„์š”ํ•˜๋‚˜๋Š” ๊ฒƒ์„ ์ธ์ง€ํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ๋ชจ๋“  ํผ ์ œ์–ด๋ฅผ ๋น„์ œ์–ด๋กœ ์‹œ๋„ํ•˜๊ธฐ ์œ„ํ•ด Context API์™€ ๊ฒฐํ•ฉํ•ด ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ์•„๋ž˜ ์ฝ”๋“œ๋Š” CardExpiredDate์—์„œ Context API์™€ ๋น„์ œ์–ด๋ฅผ ์‚ฌ์šฉํ•œ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.

    • CardExpiredDate ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉ๋˜๋Š” handleInputChange๋Š” ๊ฒฐ๊ตญ useCardInfo์—์„œ ์ œ๊ณตํ•˜๋Š” handleExpiredDate ํ•จ์ˆ˜์ด๋ฉฐ, ์ด ํ•จ์ˆ˜๋Š” cardDispatch๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    • ์ด๋ฅผ ํ†ตํ•ด ์ „์—ญ Context์— ๋งŒ๋ฃŒ์ผ ์ƒํƒœ๊ฐ’์„ ์ €์žฅํ•˜๋ฉฐ, Dispatch ์ปจํ…์ŠคํŠธ๋งŒ ์†Œ๋น„ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฆฌ๋ Œ๋”๋ง์€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
    // CardExpiredDate.tsx
    const CardExpiredDate = ({ expiredDateRef }: CardExpiredDateProps) => {
      const { handleInputChange } = useCardExpiredDate()
    
      return (
        <InputContainer>
          <InputTitle>๋งŒ๋ฃŒ์ผ</InputTitle>
          <InputBox css={{ width: '50%' }}>
            <Input ref={expiredDateRef.first} placeholder="MM" data-name="MM" onInput={handleInputChange} maxLength={2} />
            <Input
              ref={expiredDateRef.second}
              placeholder="YY"
              data-name="YY"
              onInput={handleInputChange}
              maxLength={2}
            />
          </InputBox>
        </InputContainer>
      )
    }
    
    // useCardExpiredDate.ts
    const useCardExpiredDate = () => {
      const { handleExpiredDate } = useCardInfo()
      const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
        const {
          dataset: { name },
          value,
        } = event.target
    
        switch (name) {
          case 'YY':
            if (value.length === 2 && !isYearValid(Number(value))) {
              alert('์œ ํšจ๊ธฐ๊ฐ„์ด ๋งŒ๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.')
              event.target.value = ''
              return
            }
            handleExpiredDate({ value, yymm: name })
            break
          case 'MM':
            if (Number(value) > 12 || Number(value) < 0) {
              alert('์›”์€ 1์ด์ƒ 12์ดํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.')
              event.target.value = ''
              return
            }
            handleExpiredDate({ value, yymm: name })
            break
        }
      }
    
      return { handleInputChange }
    }
    
    // useCardInfo.ts
    const useCardInfo = () => {
      const cardDispatch = useContext(CardDispatchContext)
    
      const handleExpiredDate = ({ value, yymm }: HandleExpiredDateProps) => {
        if (!isNumber(value)) return
        switch (yymm) {
          case 'YY':
            cardDispatch({
              type: 'SET_EXPIRED_YEAR',
              payload: value,
            })
            break
          case 'MM':
            cardDispatch({
              type: 'SET_EXPIRED_MONTH',
              payload: value,
            })
            break
        }
      }
    
      // ...
    
      return {
        handleExpiredDate,
        // ...
      }
    }

NPM ๋ฐฐํฌ ์‹œ ๋ฐœ์ƒํ•œ ๋ฌธ์ œ ํ•ด๊ฒฐ

NPM ๋ฐฐํฌ๋ฅผ ์œ„ํ•œ ๋นŒ๋“œ ํŒŒ์ผ ์ƒ์„ฑ ๊ณผ์ •์—์„œ Rollup์„ ํ™œ์šฉํ•˜์—ฌ ESM, CJS ๋ชจ๋“ˆ ํ™˜๊ฒฝ์„ ๊ณ ๋ คํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ TypeScript๋ฅผ ์ง€์›ํ–ˆ์Šต๋‹ˆ๋‹ค. Create React App(CRA)๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ ์ ˆ๋Œ€ ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด Craco๋ฅผ ํ†ตํ•ด ์˜ค๋ฒ„๋ผ์ด๋“œํ•˜๋Š” ๋“ฑ์˜ ์„ค์ •์„ ์ถ”๊ฐ€ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์—, ์ดˆ๊ธฐ์— ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์šฉ ๋นŒ๋“œ ํŒŒ์ผ ์ƒ์„ฑ ๊ณผ์ •์— ์–ด๋ ค์›€์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ Rollup์„ ์‚ฌ์šฉํ•ด ์ ˆ๋Œ€ ๊ฒฝ๋กœ๋ฅผ Craco์˜ ์„ค์ •๊ณผ ๋งž์ถฐ์ฃผ๊ณ , ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ ํ™˜๊ฒฝ์„ ๋Œ€์‘ํ•˜๋Š” ๋นŒ๋“œ ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ์ž์˜ ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ์•Œ๋งž์€ ๋ชจ๋“ˆ ํ™˜๊ฒฝ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์—ˆ์œผ๋ฉฐ, ์ •์  ํƒ€์ž… ์ œ๊ณต์„ ํ†ตํ•ด ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฐฐํฌ๋ฅผ ์œ„ํ•œ ๋‹ค์–‘ํ•œ ์„ค์ •์— ๋Œ€ํ•ด ๋ฐฐ์šฐ๊ณ  ์ด๋ฅผ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•˜๋Š” ๊ฒฝํ—˜์„ ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

CSS ํด๋ž˜์Šค ์ด๋ฆ„ ์ค‘๋ณต ๋ฌธ์ œ๋กœ ์ธํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ ์‹œ UI๊ฐ€ ๊นจ์ง€๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฒ˜์Œ์—๋Š” CSS ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•ด์„œ ํ•ด๊ฒฐํ•˜๊ณ ์ž ํ–ˆ์œผ๋‚˜ ๋ชจ๋“ˆ ํ™˜๊ฒฝ ์„ค์ •, ๊ฐ ์ปดํฌ๋„ŒํŠธ ์ฝ”๋“œ ์ˆ˜์ • ๋“ฑ์ด ํ•„์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ํŒŒ์•…ํ•ด์„œ CSS-In-JS๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๋„์ž… ๋น„์šฉ์ด ํฌ๊ฒŒ ๋‹ค๋ฅด์ง€ ์•Š๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ Near Zero Runtime ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ Stitches๋ฅผ ์ด์šฉํ•ด ํ•ด์‹œ๋œ ํด๋ž˜์Šค ์ด๋ฆ„์„ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž์—๊ฒŒ ์–ด๋–ค ๊ฐ’์„ ์ œ๊ณตํ• ์ง€์— ๋Œ€ํ•œ ๊ณ ๋ฏผ

issues-1

  • PayssionProvider, Payssion, usePayssion ์„ธ ๊ฐ€์ง€๋กœ ๋ถ„๋ฆฌํ•ด์„œ ๊ฐ๊ฐ์„ ์œ ์ €์—๊ฒŒ ์ „๋‹ฌํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • PayssionProvider๋Š” ํŽ˜์ด๋จผ์ธ  ์•ฑ์„ ์‚ฌ์šฉํ•  ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์‹ธ๋Š” Provider ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
  • Payssion์€ ์‹ค์ œ ์‚ฌ์šฉ ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค.
  • usePayssion์€ PayssionProvider์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ฐ’ ์ค‘์— ์œ ์ €๊ฐ€ ์‹ค์ œ ํ•„์š”ํ•œ ๊ฐ’๋งŒ ์ œ๊ณตํ•˜๋Š” ์ปค์Šคํ…€ ํ›…์ž…๋‹ˆ๋‹ค. initiatePayment, isOpen, closePayment ์„ธ ๊ฐ€์ง€์˜ ๊ฐ’์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

About

Payment๐Ÿ’ณ + Mission๐ŸŽฏ = Payssion๐Ÿ”ฅ

https://www.npmjs.com/package/payssion


Languages

Language:TypeScript 97.2%Language:JavaScript 2.3%Language:HTML 0.5%