Implementing Currency Formatting: Claude Code 활용 가이드
implementing currency formatting: Claude Code 활용. 실용적인 코드 예시를 포함합니다.
通貨フォーマットをClaude Code로올바르게구현する
ECサイト、会計앱、国際대응サービスでは、通貨の正確なフォーマットが필수적입니다。小数点、桁区切り、通貨記号のルールは国・通貨ごとに異なるため、自前でフォーマットすると不具合の温床になります。Claude Code를 활용하면 Intl.NumberFormatや専用라이브러리を활용した堅牢な구현を효율적으로구축할 수 있습니다。
Intl.NumberFormatの활용
> 多通貨대응の通貨フォーマットユーティリティを作って。
> 日本円、USドル、ユーロに대응して。
> ロケール別표시、為替変換、金額の유효성 검사も含めて。
// src/lib/currency.ts
export type CurrencyCode = 'JPY' | 'USD' | 'EUR' | 'GBP' | 'CNY' | 'KRW';
interface FormatOptions {
currency: CurrencyCode;
locale?: string;
compact?: boolean; // "1.2万" 와 같은短縮표시
showSign?: boolean; // +/- 符号を표시
}
/**
* 通貨をフォーマットする
*/
export function formatCurrency(
amount: number,
options: FormatOptions
): string {
const { currency, locale = 'en-US', compact = false, showSign = false } = options;
const formatter = new Intl.NumberFormat(locale, {
style: 'currency',
currency,
notation: compact ? 'compact' : 'standard',
signDisplay: showSign ? 'exceptZero' : 'auto',
// 小数通貨は小数点2桁、JPY/KRWは0桁(自動判定)
});
return formatter.format(amount);
}
// Usage example
formatCurrency(1234567, { currency: 'JPY' }); // "¥1,234,567"
formatCurrency(1234.56, { currency: 'USD' }); // "$1,234.56"
formatCurrency(1234.56, { currency: 'EUR', locale: 'de-DE' }); // "1.234,56 €"
formatCurrency(1234567, { currency: 'JPY', compact: true }); // "¥123万"
通貨計算の주의点
// src/lib/currency-math.ts
/**
* 浮動小数点の誤差を避けるため、整数(最小通貨単位)で計算する
* JPY: 1円 = 1 | USD: 1セント = 1 | EUR: 1セント = 1
*/
export class Money {
constructor(
private readonly amountInMinorUnit: number,
private readonly currency: CurrencyCode
) {}
// セント/円の小数桁数
private static decimals(currency: CurrencyCode): number {
return ['JPY', 'KRW'].includes(currency) ? 0 : 2;
}
static fromMajorUnit(amount: number, currency: CurrencyCode): Money {
const decimals = Money.decimals(currency);
const minor = Math.round(amount * Math.pow(10, decimals));
return new Money(minor, currency);
}
toMajorUnit(): number {
const decimals = Money.decimals(this.currency);
return this.amountInMinorUnit / Math.pow(10, decimals);
}
add(other: Money): Money {
this.assertSameCurrency(other);
return new Money(this.amountInMinorUnit + other.amountInMinorUnit, this.currency);
}
subtract(other: Money): Money {
this.assertSameCurrency(other);
return new Money(this.amountInMinorUnit - other.amountInMinorUnit, this.currency);
}
multiply(factor: number): Money {
return new Money(Math.round(this.amountInMinorUnit * factor), this.currency);
}
format(locale?: string): string {
return formatCurrency(this.toMajorUnit(), { currency: this.currency, locale });
}
private assertSameCurrency(other: Money) {
if (this.currency !== other.currency) {
throw new Error(`通貨が異なります: ${this.currency} と ${other.currency}`);
}
}
}
// Usage example
const price = Money.fromMajorUnit(1980, 'JPY');
const tax = price.multiply(0.1);
const total = price.add(tax);
console.log(total.format()); // "¥2,178"
為替レート変換
// src/lib/exchange.ts
interface ExchangeRates {
base: CurrencyCode;
rates: Record<CurrencyCode, number>;
updatedAt: string;
}
let cachedRates: ExchangeRates | null = null;
export async function getExchangeRates(base: CurrencyCode = 'JPY'): Promise<ExchangeRates> {
// 캐시が1시간以内なら再利用
if (cachedRates && Date.now() - new Date(cachedRates.updatedAt).getTime() < 3600000) {
return cachedRates;
}
const res = await fetch(`https://api.exchangerate-api.com/v4/latest/${base}`);
const data = await res.json();
cachedRates = {
base,
rates: data.rates,
updatedAt: new Date().toISOString(),
};
return cachedRates;
}
export async function convertCurrency(
amount: number,
from: CurrencyCode,
to: CurrencyCode
): Promise<{ converted: number; rate: number }> {
const rates = await getExchangeRates(from);
const rate = rates.rates[to];
return {
converted: Math.round(amount * rate * 100) / 100,
rate,
};
}
通貨입력컴포넌트
// src/components/CurrencyInput.tsx
'use client';
import { useState } from 'react';
interface Props {
value: number;
currency: CurrencyCode;
onChange: (value: number) => void;
}
export function CurrencyInput({ value, currency, onChange }: Props) {
const [displayValue, setDisplayValue] = useState(value.toLocaleString());
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const raw = e.target.value.replace(/[^0-9.-]/g, '');
setDisplayValue(e.target.value);
const num = parseFloat(raw);
if (!isNaN(num)) onChange(num);
};
const handleBlur = () => {
setDisplayValue(value.toLocaleString());
};
const symbol = new Intl.NumberFormat('en-US', {
style: 'currency',
currency,
currencyDisplay: 'narrowSymbol',
}).formatToParts(0).find((p) => p.type === 'currency')?.value || '';
return (
<div className="relative">
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400">{symbol}</span>
<input
type="text"
value={displayValue}
onChange={handleChange}
onBlur={handleBlur}
className="w-full border rounded-lg pl-8 pr-4 py-2 text-right"
inputMode="decimal"
/>
</div>
);
}
浮動小数点の落とし穴
JavaScriptで0.1 + 0.2 === 0.30000000000000004となる問題は有名です。通貨計算では必ず最小単位(セント/円)の整数で扱い、표시時にのみ小数に変換するのが鉄則です。
関連글
날짜のフォーマットは날짜・시간の扱い、国際化全般はi18n구현가이드를 참고하세요.
Intl.NumberFormatの상세仕様はMDN(developer.mozilla.org/Intl/NumberFormat)에서 확인할 수 있습니다.
Claude Code 워크플로우를 한 단계 업그레이드하세요
지금 바로 Claude Code에 복사해 쓸 수 있는 검증된 프롬프트 템플릿 50선.
이 글을 작성한 사람
Masa
Claude Code를 적극 활용하는 엔지니어. 10개 언어, 2,000페이지 이상의 테크 미디어 claudecode-lab.com을 운영 중.
관련 글
Claude Code로 리팩토링을 자동화하는 방법
Claude Code를 활용해 코드 리팩토링을 효율적으로 자동화하는 방법을 알아봅니다. 실전 프롬프트와 구체적인 리팩토링 패턴을 소개합니다.
Claude Code로 사이드 프로젝트 개발 속도를 극대화하는 방법 [예제 포함]
Claude Code를 활용해 개인 프로젝트 개발 속도를 획기적으로 높이는 방법을 알아봅니다. 실전 예제와 아이디어부터 배포까지의 워크플로를 포함합니다.
Complete CORS Configuration Guide: Claude Code 활용 가이드
complete cors configuration guide: Claude Code 활용. 실용적인 팁과 코드 예시를 포함합니다.