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)에서 확인할 수 있습니다.
무료 PDF: 5분 완성 Claude Code 치트시트
이메일 주소만 등록하시면 A4 한 장짜리 치트시트 PDF를 즉시 보내드립니다.
개인정보는 엄격하게 관리하며 스팸은 보내지 않습니다.
이 글을 작성한 사람
Masa
Claude Code를 적극 활용하는 엔지니어. 10개 언어, 2,000페이지 이상의 테크 미디어 claudecode-lab.com을 운영 중.
관련 글
Claude Code 다국어 글을 매일 발행하기 전에 확인할 7가지
누락된 언어, 깨진 CTA, 반영되지 않은 배포를 막기 위해 다국어 Claude Code 글을 매일 발행하기 전에 확인할 체크리스트입니다.
Codex Automations란? 잠자는 동안 AI가 콘텐츠 운영을 처리하게 하는 방법
Codex Automations로 트래픽 분석, 주제 선정, 글 작성, CTA 개선, 배포까지 자동화하는 실전 가이드.
Claude Code × GCP Cloud Functions 완전 가이드 | 서버리스 함수 초고속 개발
Claude Code로 GCP Cloud Functions를 효율화. HTTP/Pub/Sub/Firestore 트리거 구현부터 로컬 테스트·배포 자동화까지, Masa의 실무 경험을 토대로 실제 코드로 해설.