diff --git a/src/api/accountApi.js b/src/api/accountApi.js
index a461886..b25b5b4 100644
--- a/src/api/accountApi.js
+++ b/src/api/accountApi.js
@@ -31,6 +31,10 @@ const realApi = {
parseText({ title, text } = {}) {
return http.post('/account/entries/parse', { title, text })
},
+ // 환율 조회 — 외화 결제 입력 시 통화→원화 환율 자동 채움
+ fxRate(from, to = 'KRW') {
+ return http.get('/fx/rate', { params: { from, to } })
+ },
// 자주 쓰는 내역(빠른 등록 템플릿)
quickEntries() {
diff --git a/src/views/account/AccountView.vue b/src/views/account/AccountView.vue
index 85bd044..901ed30 100644
--- a/src/views/account/AccountView.vue
+++ b/src/views/account/AccountView.vue
@@ -84,7 +84,42 @@ function resetFilters() {
// 추가/수정 모달
const formOpen = ref(false)
const editId = ref(null)
-const form = reactive({ entryDate: '', type: 'EXPENSE', category: '', amount: null, memo: '', walletKind: '', walletId: '', toWalletKind: '', toWalletId: '', principal: null, interest: null, annualFee: null, installmentMonths: '' })
+const form = reactive({ entryDate: '', type: 'EXPENSE', category: '', amount: null, memo: '', walletKind: '', walletId: '', toWalletKind: '', toWalletId: '', principal: null, interest: null, annualFee: null, installmentMonths: '', currency: 'KRW', foreignAmount: null, rate: null })
+
+// ===== 외화 결제 =====
+const CURRENCIES = ['KRW', 'USD', 'JPY', 'EUR', 'CNY', 'GBP', 'AUD', 'CAD', 'HKD', 'SGD', 'THB', 'VND', 'TWD', 'PHP', 'MYR']
+const isForeign = computed(() => form.currency && form.currency !== 'KRW')
+// JPY 등은 원본이 정수 위주라 100단위 표기가 흔하지만, 환율은 1단위 기준으로 통일
+const convertedKrw = computed(() =>
+ isForeign.value ? Math.round((Number(form.foreignAmount) || 0) * (Number(form.rate) || 0)) : null,
+)
+// 외화면 환산 원화를 amount 에 반영(저장·검증·표시는 원화 기준)
+watch([() => form.currency, () => form.foreignAmount, () => form.rate], () => {
+ if (isForeign.value) form.amount = convertedKrw.value
+})
+const fxLoading = ref(false)
+async function fetchRate() {
+ if (!isForeign.value || fxLoading.value) return
+ fxLoading.value = true
+ try {
+ const res = await accountApi.fxRate(form.currency, 'KRW')
+ if (res?.rate != null) form.rate = Number(res.rate)
+ else formInfo.value = '환율을 가져오지 못했어요. 직접 입력해 주세요.'
+ } catch {
+ formInfo.value = '환율 조회에 실패했어요. 직접 입력해 주세요.'
+ } finally {
+ fxLoading.value = false
+ }
+}
+function onCurrencyChange() {
+ if (isForeign.value) {
+ if (form.foreignAmount == null && form.amount != null) form.foreignAmount = form.amount // 원화→외화 전환 시 초기값
+ fetchRate()
+ } else {
+ form.foreignAmount = null
+ form.rate = null
+ }
+}
const isRepayment = computed(() => form.type === 'REPAYMENT')
// 상환 대상이 카드면 연회비 입력 노출(대출은 연회비 없음)
const repayTargetIsCard = computed(() => {
@@ -452,7 +487,7 @@ function todayStr() {
function openCreate() {
editId.value = null
editingPending.value = false
- Object.assign(form, { entryDate: todayStr(), type: 'EXPENSE', category: '', amount: null, memo: '', walletKind: 'BANK', walletId: '', toWalletKind: '', toWalletId: '', principal: null, interest: null, annualFee: null, installmentMonths: '' })
+ Object.assign(form, { entryDate: todayStr(), type: 'EXPENSE', category: '', amount: null, memo: '', walletKind: 'BANK', walletId: '', toWalletKind: '', toWalletId: '', principal: null, interest: null, annualFee: null, installmentMonths: '', currency: 'KRW', foreignAmount: null, rate: null })
selectedTagIds.value = []
syncCategoryMajor()
cancelAddCategory()
@@ -481,6 +516,9 @@ function openEdit(e) {
interest: null,
annualFee: null,
installmentMonths: e.installmentMonths || '',
+ currency: e.currency && e.currency !== 'KRW' ? e.currency : 'KRW',
+ foreignAmount: e.currency && e.currency !== 'KRW' ? Number(e.originalAmount) : null,
+ rate: e.currency && e.currency !== 'KRW' ? Number(e.exchangeRate) : null,
})
// 태그 이름 → id 매핑 (현재 태그 목록 기준)
const nameToId = {}
@@ -559,6 +597,7 @@ async function submit() {
}
}
submitting.value = true
+ const foreign = isForeign.value
const payload = {
entryDate: form.entryDate,
type: form.type,
@@ -569,6 +608,10 @@ async function submit() {
toWalletId: form.type === 'TRANSFER' ? form.toWalletId || null : null,
installmentMonths: showInstallment.value && Number(form.installmentMonths) >= 2 ? Number(form.installmentMonths) : null,
tagIds: selectedTagIds.value,
+ // 외화 결제: amount 는 환산 원화, 원본은 별도 보존
+ currency: foreign ? form.currency : null,
+ originalAmount: foreign ? Number(form.foreignAmount) : null,
+ exchangeRate: foreign ? Number(form.rate) : null,
}
try {
if (editId.value) {
@@ -947,7 +990,8 @@ onMounted(async () => {