🌳 κ³ κΈ‰ ν”„λ‘œμ νŠΈ : Decimal type

yeonjeongΒ·2025λ…„ 6μ›” 20일

μ΄λ²ˆμ— κ³ κΈ‰ ν”„λ‘œμ νŠΈλ₯Ό μ§„ν–‰ν•˜λ©΄μ„œ μƒˆλ‘œ μ•Œκ²Œλœ Type에 λŒ€ν•΄μ„œ λͺ‡ 쀄 μž‘μ„±ν•˜λ €κ³  ν•œλ‹€.

원화(β‚©)λŠ” 10원이 ν˜„κΈˆμœΌλ‘œ κ°€μž₯ μž‘μ€ λ‹¨μœ„μ΄κ³ , 인터넷 결제 ν˜Ήμ€ 이자λ₯Ό 받닀보면 1원 λ‹¨μœ„λ„ λ³Ό 수 μžˆλ‹€. ν•˜μ§€λ§Œ λ‹¬λŸ¬($)의 κ²½μš°μ—λŠ” λ‹¬λŸ¬λ₯Ό κΈ°μ€€μœΌλ‘œ $1.2 μ—¬κΈ°μ„œ μ†Œμˆ˜μ  λ‹¨μœ„λŠ” μ„ΌνŠΈμ΄λ‹€.

μ§€κΈˆ μš°λ¦¬λŠ” ν•œκ΅­μ—μ„œλ§Œ 결제λ₯Ό μœ λ„ν•˜λŠ” μ‹œμŠ€ν…œμ„ λ§Œλ“€μ§€λ§Œ,
λΉ„μ§€λ‹ˆμŠ€μ˜ μ˜μ—­μ΄ ν™•μž₯λ˜μ–΄μ„œ ν˜Ήμ€ ν•œκ΅­ 고객 뿐 μ•„λ‹ˆλΌ ν•΄μ™Έ 고객 μœ μΉ˜κ°€ κ°€λŠ₯ν•˜λ‹€λ©΄?
전세계 곡용으둜 ν†΅μš©λ˜λŠ” λ‹¬λŸ¬ 결제 μ‹œμŠ€ν…œμ„ μ€€λΉ„ν•΄μ•Ό ν•œλ‹€.

μ—¬κΈ°μ„œ μ μš©ν•΄ λ³Ό 수 μžˆλŠ” type이 λ°”λ‘œ Deciml이닀.

1. μ˜μ–΄λ‘œ λ³΄λŠ” 숫자 νƒ€μž…

μ •μˆ˜: integer(1,2,3,0,-1,-2)
μ‹€μˆ˜: real number(-2.5, 0, 3.14)
λΆ„μˆ˜: fraction(1/2, 3/4)
μ†Œμˆ˜: decimal(0.2, 2.718)
μžμ—°μˆ˜: natural number(1,2,3)

μœ„μ˜ λ‚΄μš©μ„ λ³΄μ•˜μ„ λ•Œ decimal은 0μ΄μƒμ˜ μ†Œμˆ˜λ₯Ό λ‚˜νƒ€λ‚Ό 수 μžˆλŠ” νƒ€μž…μ΄λΌλŠ” 것을 μ•Œ 수 μžˆλ‹€.

그러면 μ™œ μ†Œμˆ˜ 뿐 μ•„λ‹ˆλΌ 금육 데이터λ₯Ό λ‹€λ£° λ•ŒλŠ” decimal을 μ‚¬μš©ν•΄μ•Ό ν• κΉŒ?

일반적으둜 μ‚¬μš©ν•˜λŠ” 뢀동 μ†Œμˆ˜μ (float, double)은 계산 κ³Όμ •μ—μ„œ λ―Έμ„Έν•œ μ˜€νŒŒκ°€ λ°œμƒ ν•  수 μžˆμ–΄μ„œ 돈과 κ΄€λ ¨λœ κ³„μ‚°μ—λŠ” μ ν•©ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ΄λ‹€.

2. Decimal νƒ€μž…μ— λŒ€ν•œ 이해

DECIMAL(P,S) νƒ€μž…μ€ κ³ μ • μ†Œμˆ˜μ  숫자λ₯Ό μ €μž₯ν•  수 μžˆλ‹€.

  • P(Precision, 정밀도): 숫자의 총 자릿수λ₯Ό 의미. μ†Œμˆ˜μ  μ™Όμͺ½κ³Ό 였λ₯Έμͺ½μ„ λͺ¨λ‘ ν¬ν•¨ν•œ 자릿수 -> 1-65κΉŒμ§€ μ§€μ • κ°€λŠ₯ν•˜μ§€λ§Œ 데이터 λ² μ΄μŠ€λ§ˆλ‹€ λ‹€λ₯Ό 수 있음
  • S(Scale, μŠ€μΌ€μΌ): μ†Œμˆ˜μ  μ΄ν•˜μ˜ 자릿수λ₯Ό 의미. -> 0~PκΉŒμ§€ κ°€λŠ₯

example.
DECIMAL(10, 2): 총 10자리 숫자 쀑 μ†Œμˆ˜μ  μ΄ν•˜ 2자리λ₯Ό μ €μž₯ν•©λ‹ˆλ‹€. ν‘œν˜„ κ°€λŠ₯ν•œ λ²”μœ„λŠ” -99999999.99λΆ€ν„° 99999999.99κΉŒμ§€μž…λ‹ˆλ‹€. (총 8자리 μ •μˆ˜λΆ€ + 2자리 μ†Œμˆ˜λΆ€)

3. Prisma 적용 및 ν™œμš©

model Order {
  id        String   @id @default(uuid())
  userId    String
  totalAmount Decimal @db.Decimal(19, 4)
  createdAt DateTime @default(now())
}
  • Decimal: Prisma λ‚΄λΆ€μ—μ„œ μ‚¬μš©ν•˜λŠ” νƒ€μž… 이름
  • @db.Decimal(P, S): λ°μ΄ν„°λ² μ΄μŠ€μ— μ‹€μ œ 생성될 컬럼의 νƒ€μž…μ„ μ§€μ •
  • P (Precision): 숫자의 총 자릿수 (μ†Œμˆ˜μ  μ™Όμͺ½ + μ†Œμˆ˜μ  였λ₯Έμͺ½).
  • S (Scale): μ†Œμˆ˜μ  μ΄ν•˜ 자릿수.
    이 값듀은 μ‚¬μš©ν•˜λ €λŠ” λ°μ΄ν„°λ² μ΄μŠ€μ˜ DECIMAL νƒ€μž… κ·œμΉ™μ„ λ”°λ₯΄κΈ°(예: MySQL은 μ΅œλŒ€ 65, 30)
  1. νƒ€μž…μŠ€ν¬λ¦½νŠΈ Type μ§€μ •
    PrismaλŠ” Decimal νƒ€μž… ν•„λ“œλ₯Ό μ²˜λ¦¬ν•  λ•Œ, JavaScript의 κΈ°λ³Έ number νƒ€μž…μ΄ μ•„λ‹Œ, Decimal.js 라이브러리의 Decimal 객체λ₯Ό μ‚¬μš©ν•˜λ„λ‘ νƒ€μž… μ •μ˜λ₯Ό μƒμ„±ν•œλ‹€.. λ”°λΌμ„œ TypeScript μ½”λ“œμ—μ„œλŠ” Decimal.js의 Decimal νƒ€μž…μ„ import.

1) decimal 라이브러리 이용

a) decimal.js 라이브러리 μ„€μΉ˜

pm install decimal.js
npm install -D @types/decimal.js

b) TypeScript μ½”λ“œμ—μ„œ μ‚¬μš©ν•˜κΈ°
Decimal νƒ€μž…μ˜ 값을 λ‹€λ£° λ•ŒλŠ” 항상 Decimal.js 객체둜 μƒμ„±ν•˜κ³ , κ³„μ‚°ν•˜λ©°, λ¬Έμžμ—΄λ‘œ λ³€ν™˜ν•˜μ—¬ μ‚¬μš©

import { PrismaClient, Prisma } from '@prisma/client';
import Decimal from 'decimal.js'; // Decimal.js 라이브러리 μž„ν¬νŠΈ


const prisma = new PrismaClient();

async function createAndGetProduct() {
  // 1. Prisma에 데이터 생성/μ—…λ°μ΄νŠΈ μ‹œ Decimal κ°’ 처리
  // Decimal 값을 λ„˜κ²¨μ€„ λ•ŒλŠ” λ¬Έμžμ—΄ λ˜λŠ” Decimal.js 객체둜 λ„˜κ²¨μ€˜μ•Ό ν•©λ‹ˆλ‹€.
  const newProduct = await prisma.product.create({
    data: {
      name: 'Smart Watch',
      price: new Decimal('199.99'), // Decimal.js 객체둜 μƒμ„±ν•˜μ—¬ 전달 (κ°€μž₯ μ•ˆμ „)
      // price: '199.99', // λ¬Έμžμ—΄λ‘œ 전달해도 Prismaκ°€ Decimal.js 객체둜 λ³€ν™˜ν•΄μ€λ‹ˆλ‹€.
    },
  });

  console.log('Created Product:', newProduct);
  // newProduct.priceλŠ” Decimal.js의 Decimal 객체 νƒ€μž…μž…λ‹ˆλ‹€.
  // console.log(typeof newProduct.price); // object
  // console.log(newProduct.price instanceof Decimal); // true

  // 2. Prismaμ—μ„œ Decimal 값을 가져왔을 λ•Œ
  const product = await prisma.product.findUnique({
    where: { id: newProduct.id },
  });

  if (product) {
    // product.priceλŠ” Decimal.js의 Decimal κ°μ²΄μž…λ‹ˆλ‹€.
    console.log('Product Price (Decimal object):', product.price);

    // 3. Decimal κ°’μœΌλ‘œ 계산 μˆ˜ν–‰ μ‹œ (λ°˜λ“œμ‹œ Decimal.js λ©”μ„œλ“œ μ‚¬μš©)
    const taxRate = new Decimal('0.10'); // 10%
    const priceWithTax = product.price.t
```imes(taxRate).plus(product.price);

    console.log('Price with Tax (Decimal object):', priceWithTax);

    // 4. Decimal 값을 λ¬Έμžμ—΄λ‘œ λ³€ν™˜ν•˜μ—¬ 좜λ ₯ν•˜κ±°λ‚˜ API μ‘λ‹΅μœΌλ‘œ 보낼 λ•Œ
    console.log('Price with Tax (string, 2 decimal places):', priceWithTax.toFixed(2)); // "219.99"
    console.log('Price with Tax (full string):', priceWithTax.toString()); // "219.989" (μ •ν™•ν•œ κ°’)

    // 5. 숫자둜 λ³€ν™˜ν•΄μ•Ό ν•  경우 
    // ν•„μš”ν•œ κ²½μš°κ°€ μ•„λ‹ˆλΌλ©΄ Number()둜 λ³€ν™˜ν•˜μ§€ λ§ˆμ„Έμš”.
    // console.log('Price with Tax (number - potentially imprecise):', Number(priceWithTax.toString()));
  }
}

2) prisma - decimal λŒμ–΄μ˜€κΈ°

import { Decimal } from "@prisma/client/runtime/library";

export const buyerUser = {
 id: "user_buyer_001",
 email: "buyer@example.com",
 name: "νŒŒλŠ”μ‚¬λžŒ",
 password: "password1234",
 type: UserType.BUYER,
 point: 0,
 totalAmount: new Decimal(0),
 createdAt: new Date(),
 updatedAt: new Date(),
};

-> prisma 라이브러리λ₯Ό ν™œμš©ν•œ Decimal νƒ€μž…μ„ μ°Έμ‘°

(1) Prisma의 νƒ€μž… μ •μ˜μ™€ 일치
PrismaλŠ” schema.prisma νŒŒμΌμ— Decimal νƒ€μž…μœΌλ‘œ μ •μ˜λœ ν•„λ“œλ₯Ό κ°€μ Έμ˜€κ±°λ‚˜ 생성할 λ•Œ, JavaScript의 κΈ°λ³Έ number νƒ€μž…μ΄ μ•„λ‹Œ decimal.js 라이브러리의 Decimal 객체둜 λ³€ν™˜ν•˜μ—¬ 처리
-> totalAmount: new Decimal(0)은 Prismaκ°€ μ˜ˆμƒν•˜λŠ” νƒ€μž…(Decimal.js의 Decimal 객체)에 맞좰 데이터λ₯Ό μ œκ³΅ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

(2) 뢀동 μ†Œμˆ˜μ  였차 λ°©μ§€
JavaScript의 κΈ°λ³Έ number νƒ€μž…μ€ 64λΉ„νŠΈ 뢀동 μ†Œμˆ˜μ (float)으둜 ν‘œν˜„λ˜κΈ° λ•Œλ¬Έμ—, 0.1 + 0.2κ°€ 0.30000000000000004와 같이 λ―Έμ„Έν•œ 였차λ₯Ό κ°€μ§ˆ 수 μžˆλ‹€. 돈과 κ΄€λ ¨λœ κ³„μ‚°μ—μ„œλŠ” μ΄λŸ¬ν•œ μž‘μ€ μ˜€μ°¨λ„ ν—ˆμš©λ˜μ§€ μ•ŠκΈ°μ— new Decimal(0)을 μ‚¬μš©ν•¨μœΌλ‘œμ¨ 0μ΄λΌλŠ” 값을 μ •ν™•ν•œ μ‹­μ§„μˆ˜λ‘œ ν‘œν˜„ν•˜κ³ , μ•žμœΌλ‘œ 이 totalAmount ν•„λ“œμ— λŒ€ν•œ λͺ¨λ“  계산(λ§μ…ˆ, λΊ„μ…ˆ λ“±)도 Decimal.js의 λ©”μ„œλ“œ(plus(), minus(), times() λ“±)λ₯Ό μ‚¬μš©ν•˜μ—¬ 였차 없이 이루어지도둝 μ€€λΉ„ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

(3) μΌκ΄€λœ 데이터 처리

λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ Decimal νƒ€μž…μœΌλ‘œ κ΄€λ¦¬ν•˜κΈ°λ‘œ ν–ˆλ‹€λ©΄, μ• ν”Œλ¦¬μΌ€μ΄μ…˜ κ³„μΈ΅μ—μ„œλ„ μΌκ΄€λ˜κ²Œ 관리해야 ν•˜λŠ”λ° new Decimal(0)은 μ΄λŸ¬ν•œ μΌκ΄€λœ 처리 λ°©μ‹μ˜ μ‹œμž‘μ 

profile
Backend 개발자둜 κ±°λ“­λ‚˜λŠ” κ·Έ λ‚ κΉŒμ§€ !

0개의 λŒ“κΈ€