반응형
이 코드에서 orderAmount는 Vue의 계산된 속성(computed property)으로, get()과 set() 메서드를 가진 객체를 사용해 정의되었습니다. 이 설계는 사용자 입력값과 표시값을 다르게 처리하기 위한 중요한 패턴입니다.
get() 메서드 — 값을 읽을 때 호출됩니다.
get() {
return orderAmountRaw.value ? orderAmountRaw.value.toLocaleString() : '';
}
- 역할: 화면에 값을 '표시'할 때 사용됩니다.
- 동작 방식:
- orderAmountRaw.value가 존재하면 숫자에 콤마를 추가하여 가독성 있게 표시(toLocaleString())
- 값이 없으면 빈 문자열('')을 반환
- 사용 시점: 템플릿에서 {{ orderAmount }}나 v-model="orderAmount"로 값을 읽을 때 호출됩니다.
set() 메서드 — 값을 변경할 때 호출됩니다.
set(val) {
const onlyNumber = String(val).replace(/[^0-9]/g, '');
orderAmountRaw.value = onlyNumber ? parseInt(onlyNumber, 10) : 0;
}
- 역할: 사용자가 입력한 값을 '처리'할 때 사용됩니다.
- 동작 방식:
- 입력값을 문자열로 변환(String(val))
- 정규식으로 숫자가 아닌 모든 문자 제거(replace(/[^0-9]/g, ''))
- 숫자만 남은 문자열을 정수로 변환(parseInt())
- 결과값을 실제 데이터인 orderAmountRaw에 저장
- 사용 시점: v-model="orderAmount"가 적용된 입력 필드에 사용자가 값을 입력할 때 호출됩니다.
orderAmount.value = '200000';
이런 식으로 코드에서 직접 값을 설정할 때도 set()이 실행됩니다.
이 패턴의 장점
- 데이터 표시와 저장 분리:
- 표시: 사용자에게는 콤마가 포함된 읽기 쉬운 형태(예: "1,000,000")
- 저장: 내부적으로는 계산에 적합한 숫자 형태(예: 1000000)
- 자동 데이터 정제:
- 사용자가 "1,000,000", "1000000", "1,000,000원" 등 다양한 형태로 입력해도
- 항상 숫자만 추출하여 일관된 형태로 저장
- 양방향 바인딩 활용:
- v-model 디렉티브로 입력과 출력을 모두 관리
- 사용자 입력 → set() → 데이터 저장 → get() → 화면에 표시되는 사이클 자동화
이 구현은 특히 통화, 수량 등의 숫자 입력에서 유용하며, 사용자 경험을 향상시키면서도 내부 로직에서는 일관된 데이터 형식을 유지할 수 있게 해줍니다.
전체소스
<template>
<div>
<h2>바로환전</h2>
<!-- 탭 버튼 -->
<div>
<button :class="{ active: selectedTab === 'market' }" @click="selectedTab = 'market'">시장가</button>
<button :class="{ active: selectedTab === 'limit' }" @click="selectedTab = 'limit'">지정가</button>
<button :class="{ active: selectedTab === 'auto' }" @click="selectedTab = 'auto'">자동주문</button>
</div>
<!-- 시장가 내용 -->
<div v-if="selectedTab === 'market'">
<h3>시장가</h3>
<p>
{{ isTokenOrder ? 'KRW 금액' : `${exchangeData.baseCurrency} 팔기` }}
<input type="text" v-model="orderAmount" placeholder="주문 금액 입력" class="order-input" />
<button @click="toggleOrderType" :class="{ on: isTokenOrder, off: !isTokenOrder }">
{{ isTokenOrder ? 'ON' : 'OFF' }}
</button>
</p>
<p>출금가능금액: {{ (exchangeData.availableBalance / 10000).toLocaleString() }}만원</p>
<div>
<button @click="setMax">최대</button>
<button @click="addAmount(1000000)">+1M</button>
<button @click="addAmount(100000)">+0.1M</button>
</div>
<div class="conversion-info">
<p>USD 환산: <span>USD 0.00</span></p>
<p>KRW 환산: <span>0원</span></p>
</div>
<button @click="order">주문하기</button>
</div>
<!-- 지정가/자동주문 등도 동일하게 확장 가능 -->
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
const { exchangeData } = defineProps({
exchangeData: Object,
});
const emit = defineEmits(['order']);
const selectedTab = ref('market');
const isTokenOrder = ref(false);
// 연산용 실제 값(숫자)
const orderAmountRaw = ref(0);
// 화면 표시용(콤마 포함)과 연산용(숫자)을 캡슐화
const orderAmount = computed({
get() {
// 화면에 표시할 때 콤마 추가
return orderAmountRaw.value ? orderAmountRaw.value.toLocaleString() : '';
},
set(val) {
// 입력값에서 숫자만 추출해서 저장
const onlyNumber = String(val).replace(/[^0-9]/g, '');
orderAmountRaw.value = onlyNumber ? parseInt(onlyNumber, 10) : 0;
}
});
// input에 v-model로 바로 사용
// <input type="text" v-model="orderAmount" ... />
const toggleOrderType = () => {
isTokenOrder.value = !isTokenOrder.value;
};
const setMax = () => {
orderAmountRaw.value = 1000000;
};
const addAmount = amount => {
orderAmountRaw.value += amount;
};
orderAmount.value = '2000000';
const order = () => {
emit('order', {
type: selectedTab.value,
amount: orderAmountRaw.value, // 연산용 값만 사용
isTokenOrder: isTokenOrder.value,
});
};
</script>
반응형
'프로그래밍 > Js' 카테고리의 다른 글
Vue + Vite 멀티 모듈 프로젝트 구조 완벽 가이드 (0) | 2025.05.20 |
---|---|
Vue 3 vite 다중 앱 구성 가이드: 단일 프로젝트에서 여러 애플리케이션 관리하기 (0) | 2025.05.19 |
TODO 관리 시스템 (0) | 2025.03.18 |
console.log 출력 내용을 <div>에 표시하는 방법 (0) | 2025.02.28 |
Promise.all 모든 비동기 작업이 끝난후 작업 실행 (0) | 2024.10.25 |