728x90
- Vue.js에서 그리드 데이터와 임시 데이터를 이동하는 방법
-
Vue.js 그리드에서 현재 커서 위치 갱신 및 롤백 구현하기
-
Vue.js의 $set과 $ 접두사의 의미 완벽 정리
Vue.js에서 그리드의 데이터를 다루는 예제를 보여드리겠습니다. 여기서는 한 행(row)의 데이터를 임시 데이터로 이동하고, 반대로 임시 데이터를 그리드 행으로 이동하는 예제를 작성해보겠습니다.
<template>
<div>
<!-- 그리드 테이블 -->
<table>
<thead>
<tr>
<th>ID</th>
<th>이름</th>
<th>나이</th>
<th>액션</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in gridData" :key="item.id">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.age }}</td>
<td>
<button @click="moveToTemp(index)">임시로 이동</button>
</td>
</tr>
</tbody>
</table>
<!-- 임시 데이터 영역 -->
<div v-if="tempRow" class="temp-area">
<h3>임시 데이터</h3>
<p>ID: {{ tempRow.id }}</p>
<p>이름: {{ tempRow.name }}</p>
<p>나이: {{ tempRow.age }}</p>
<button @click="moveToGrid">그리드로 이동</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
// 그리드 데이터
gridData: [
{ id: 1, name: '김철수', age: 25 },
{ id: 2, name: '이영희', age: 30 },
{ id: 3, name: '박민수', age: 35 }
],
// 임시로 저장될 row 데이터
tempRow: null
}
},
methods: {
// 그리드에서 임시로 데이터 이동
moveToTemp(index) {
// 선택한 행을 임시 데이터로 저장
this.tempRow = { ...this.gridData[index] };
// 그리드에서 해당 행 제거
this.gridData.splice(index, 1);
},
// 임시 데이터를 그리드로 이동
moveToGrid() {
if (this.tempRow) {
// 임시 데이터를 그리드에 추가
this.gridData.push({ ...this.tempRow });
// 임시 데이터 초기화
this.tempRow = null;
}
}
}
}
</script>
<style scoped>
table {
border-collapse: collapse;
width: 100%;
margin-bottom: 20px;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
.temp-area {
border: 1px solid #ccc;
padding: 15px;
margin-top: 20px;
}
button {
padding: 5px 10px;
cursor: pointer;
}
</style>
이 예제의 주요 특징:
-
데이터 구조:
-
gridData: 그리드에 표시될 데이터 배열
-
tempRow: 선택된 행을 임시로 저장하는 객체
-
-
메서드:
-
moveToTemp(index):
-
그리드에서 선택한 행을 임시 데이터로 이동
-
spread 연산자(...)를 사용해 객체 복사
-
splice로 원본 배열에서 제거
-
-
moveToGrid():
-
임시 데이터를 그리드로 이동
-
데이터 이동 후 임시 데이터 초기화
-
-
-
템플릿:
-
v-for로 그리드 데이터 렌더링
-
v-if로 임시 데이터 영역 조건부 렌더링
-
버튼 클릭 이벤트로 데이터 이동 처리
-
사용 방법:
-
그리드에서 "임시로 이동" 버튼을 클릭하면 해당 행이 임시 영역으로 이동
-
임시 영역에서 "그리드로 이동" 버튼을 클릭하면 다시 그리드로 복귀
추가 개선 제안:
-
데이터 정렬 기능 추가
// gridData에 추가 후 정렬
this.gridData.push({ ...this.tempRow });
this.gridData.sort((a, b) => a.id - b.id);
-
중복 체크 로직 추가
moveToGrid() {
if (this.tempRow && !this.gridData.some(item => item.id === this.tempRow.id)) {
this.gridData.push({ ...this.tempRow });
this.tempRow = null;
}
}
이 코드를 실제 프로젝트에 적용할 때는 필요한 비즈니스 로직과 스타일링을 추가하여 사용하면 됩니다.
아래는 임시 데이터를 그리드의 현재 커서 위치에 갱신하고, 수정 취소 시 롤백 기능을 포함한 Vue.js 예제입니다. 커서 위치를 추적하고 원본 데이터를 백업하여 롤백할 수 있도록 구현했습니다.
<template>
<div>
<!-- 그리드 테이블 -->
<table>
<thead>
<tr>
<th>ID</th>
<th>이름</th>
<th>나이</th>
<th>액션</th>
</tr>
</thead>
<tbody>
<tr
v-for="(item, index) in gridData"
:key="item.id"
:class="{ 'selected': index === currentIndex }"
@click="selectRow(index)"
>
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.age }}</td>
<td>
<button @click.stop="moveToTemp(index)">임시로 이동</button>
</td>
</tr>
</tbody>
</table>
<!-- 임시 데이터 영역 -->
<div v-if="tempRow" class="temp-area">
<h3>임시 데이터</h3>
<p>ID: {{ tempRow.id }}</p>
<input v-model="tempRow.name" placeholder="이름 수정" />
<input v-model.number="tempRow.age" type="number" placeholder="나이 수정" />
<button @click="updateGrid">현재 위치에 갱신</button>
<button @click="rollback">취소 (롤백)</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
// 그리드 데이터
gridData: [
{ id: 1, name: '김철수', age: 25 },
{ id: 2, name: '이영희', age: 30 },
{ id: 3, name: '박민수', age: 35 }
],
// 임시로 저장될 row 데이터
tempRow: null,
// 원본 데이터 백업 (롤백용)
originalRow: null,
// 현재 선택된 행 인덱스
currentIndex: null
}
},
methods: {
// 행 선택
selectRow(index) {
this.currentIndex = index;
},
// 그리드에서 임시로 데이터 이동
moveToTemp(index) {
this.currentIndex = index;
// 원본 데이터 백업
this.originalRow = { ...this.gridData[index] };
// 선택한 행을 임시 데이터로 복사
this.tempRow = { ...this.gridData[index] };
},
// 임시 데이터를 현재 커서 위치에 갱신
updateGrid() {
if (this.tempRow && this.currentIndex !== null) {
// 현재 위치의 데이터 갱신
this.$set(this.gridData, this.currentIndex, { ...this.tempRow });
// 갱신 후 임시 데이터 초기화
this.tempRow = null;
this.originalRow = null;
}
},
// 수정 취소 및 롤백
rollback() {
if (this.originalRow && this.currentIndex !== null) {
// 원본 데이터로 롤백
this.$set(this.gridData, this.currentIndex, { ...this.originalRow });
// 임시 데이터 초기화
this.tempRow = null;
this.originalRow = null;
}
}
}
}
</script>
<style scoped>
table {
border-collapse: collapse;
width: 100%;
margin-bottom: 20px;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
.selected {
background-color: #f0f0f0;
cursor: pointer;
}
.temp-area {
border: 1px solid #ccc;
padding: 15px;
margin-top: 20px;
}
button {
padding: 5px 10px;
margin-right: 5px;
cursor: pointer;
}
input {
margin: 5px 0;
padding: 5px;
}
</style>
주요 특징:
-
데이터 구조:
-
gridData: 그리드 데이터 배열
-
tempRow: 임시로 편집할 행 데이터
-
originalRow: 롤백을 위한 원본 데이터 백업
-
currentIndex: 현재 선택된 행의 인덱스
-
-
메서드:
-
selectRow(index): 클릭한 행을 선택하여 커서 위치 설정
-
moveToTemp(index):
-
선택한 행의 원본을 백업하고 임시 데이터로 복사
-
커서 위치 업데이트
-
-
updateGrid():
-
임시 데이터를 현재 커서 위치의 그리드 행에 갱신
-
$set을 사용해 반응형 업데이트 보장
-
-
rollback():
-
원본 데이터로 롤백
-
임시 데이터 초기화
-
-
-
템플릿:
-
행 클릭 시 선택 상태 표시 (selected 클래스)
-
임시 데이터 영역에서 이름과 나이 수정 가능 (v-model 사용)
-
"현재 위치에 갱신"과 "취소 (롤백)" 버튼 제공
-
사용 방법:
-
그리드에서 행을 클릭하여 선택 (배경색으로 선택 상태 표시)
-
"임시로 이동" 버튼 클릭 시 해당 행 데이터가 임시 영역으로 이동
-
임시 영역에서 데이터를 수정
-
"현재 위치에 갱신" 클릭 시 수정된 데이터가 그리드의 선택된 위치에 반영
-
"취소 (롤백)" 클릭 시 원본 데이터로 복원
추가 개선 제안:
-
유효성 검사:
updateGrid() {
if (this.tempRow && this.currentIndex !== null) {
if (!this.tempRow.name || this.tempRow.age < 0) {
alert('이름과 나이를 올바르게 입력하세요');
return;
}
this.$set(this.gridData, this.currentIndex, { ...this.tempRow });
this.tempRow = null;
this.originalRow = null;
}
}
-
선택 해제 기능:
deselectRow() {
this.currentIndex = null;
}
이 예제는 현재 커서 위치에 데이터를 갱신하고 롤백 기능을 제공하며, Vue의 반응형 시스템을 활용해 UI를 효율적으로 업데이트합니다.
Vue.js에서 $set과 $의 의미, 그리고 관련 메서드에 대해 설명드리겠습니다.
$set이란?
$set은 Vue.js에서 객체 또는 배열의 반응형 속성을 동적으로 추가하거나 업데이트할 때 사용하는 메서드입니다. Vue는 초기 렌더링 시 데이터 객체를 감지하여 반응형으로 만드는데, 나중에 추가된 속성이나 배열 요소는 기본적으로 반응형으로 감지되지 않습니다. 이를 해결하기 위해 $set을 사용합니다.
사용법
this.$set(target, propertyName/index, value)
-
target: 반응형으로 만들 대상 (객체 또는 배열)
-
propertyName/index: 속성 이름(객체) 또는 인덱스(배열)
-
value: 설정할 값
예제
-
객체에 새 속성 추가
data() {
return {
user: {
name: '김철수'
}
}
},
methods: {
addAge() {
// 일반 추가: 반응형으로 감지되지 않음
// this.user.age = 25;
// $set 사용: 반응형으로 감지됨
this.$set(this.user, 'age', 25);
}
}
-
배열 요소 업데이트
data() {
return {
items: ['a', 'b', 'c']
}
},
methods: {
updateItem() {
// 일반 할당: 반응형으로 감지되지 않음
// this.items[0] = 'x';
// $set 사용: 반응형으로 감지됨
this.$set(this.items, 0, 'x');
}
}
왜 필요한가?
Vue는 Object.defineProperty (Vue 2) 또는 Proxy (Vue 3)를 사용해 데이터의 반응성을 관리합니다. 하지만 객체에 새 속성을 추가하거나 배열의 인덱스를 직접 수정하면 Vue가 이를 감지하지 못합니다. $set은 내부적으로 Vue의 반응형 시스템을 업데이트하여 UI가 반영되도록 보장합니다.
$의 의미
Vue에서 $는 Vue 인스턴스에 붙어 있는 특수 속성 또는 메서드를 나타냅니다. $ 접두사는 개발자가 정의한 일반 변수/메서드와 구분하기 위해 Vue 프레임워크 자체에서 사용하는 naming convention입니다.
$가 붙은 주요 속성/메서드 예시
-
속성:
-
this.$data: 컴포넌트의 데이터 객체
-
this.$props: 전달받은 props
-
this.$el: 컴포넌트의 DOM 요소
-
this.$refs: 템플릿 내 ref 속성으로 참조된 요소/컴포넌트
-
-
메서드:
-
this.$set: 반응형 데이터 설정
-
this.$watch: 데이터 변경 감시
-
this.$emit: 커스텀 이벤트 발생
-
this.$nextTick: DOM 업데이트 후 콜백 실행
-
$는 Vue 내부 API임을 나타내며, 사용자 정의 변수/메서드와 충돌을 방지합니다.
$set 외에 참고할 사항
Vue에서 반응형 데이터를 다룰 때 $set 외에도 몇 가지 대안과 관련 메서드가 있습니다.
1. 배열의 반응형 메서드
Vue는 배열의 특정 메서드를 감지하여 반응형으로 처리합니다. $set 없이도 아래 메서드는 반응형으로 동작합니다:
-
push()
-
pop()
-
shift()
-
unshift()
-
splice()
-
sort()
-
reverse()
예:
this.items.push('d'); // 반응형으로 감지됨
this.items.splice(0, 1, 'x'); // 반응형으로 감지됨
2. 객체 전체 갱신
객체에 여러 속성을 추가해야 할 경우, 새 객체로 교체하는 방법도 반응형을 유지합니다.
this.user = { ...this.user, age: 25, city: '서울' };
3. Vue.set (글로벌 메서드)
this.$set은 인스턴스 메서드지만, Vue.set은 전역 메서드로 동일한 기능을 제공합니다.
import Vue from 'vue';
Vue.set(this.user, 'age', 25);
4. Vue 3에서의 차이
Vue 3에서는 Proxy를 사용하므로 객체에 새 속성을 추가해도 반응형으로 감지됩니다. 하지만 배열 인덱스 직접 수정은 여전히 $set이 필요할 수 있습니다.
// Vue 3
this.user.age = 25; // 반응형으로 동작
this.$set(this.items, 0, 'x'); // 여전히 필요
5. $delete
$set의 반대 개념으로, 반응형 객체/배열에서 속성/요소를 삭제할 때 사용합니다.
this.$delete(this.user, 'age'); // 속성 삭제
this.$delete(this.items, 0); // 배열 요소 삭제
6. $watch
데이터 변경을 감시하며, $set으로 설정한 값의 변화를 추적할 때 유용합니다.
this.$watch('user.age', (newValue) => {
console.log('나이 변경:', newValue);
});
요약
-
$set: 반응형 데이터 추가/업데이트를 위한 메서드
-
$: Vue 인스턴스의 특수 속성/메서드임을 나타냄
-
대안: 배열 메서드, 객체 교체, Vue.set, $delete 등
-
Vue 3: Proxy로 객체 반응성 개선, 배열은 여전히 주의 필요
궁금한 점이 더 있다면 말씀해주세요!
originalRow 란 게 필요가 없잖아.. 생각해봐 그리드에서 하나를 가지고 와서 수정이 확정되면 그리드에 덮어쓰고 99%를 하더라도 취소하면 temp를 다시 채우면 되잖아.......
전체를 다시 구현해야 될것 같은데..
맞습니다. 말씀하신 대로 다시 생각해보니 originalRow가 필요 없을 수 있습니다. 질문의 핵심을 다시 정리하면:
-
그리드에서 데이터를 tempRow로 가져와서 수정
-
수정이 확정되면(updateGrid) 그리드에 덮어씀
-
수정 중(99%라도) 취소하면(rollback) tempRow를 다시 원래 데이터로 채움
이 경우 originalRow를 별도로 저장할 필요 없이, tempRow만 관리하면 됩니다. 취소 시에는 그리드의 원래 데이터를 다시 tempRow로 복원하면 충분합니다. 전체 코드를 새로 설계해보겠습니다.
개선된 전체 코드
vue
<template>
<div>
<table>
<thead>
<tr>
<th>ID</th>
<th>이름</th>
<th>나이</th>
<th>액션</th>
</tr>
</thead>
<tbody>
<tr
v-for="(item, index) in gridData"
:key="item.id"
:class="{ 'selected': index === currentIndex }"
@click="selectRow(index)"
>
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.age }}</td>
<td>
<button @click.stop="moveToTemp(index)">임시로 이동</button>
</td>
</tr>
</tbody>
</table>
<div v-if="tempRow" class="temp-area">
<h3>임시 데이터</h3>
<p>ID: {{ tempRow.id }}</p>
<input v-model="tempRow.name" placeholder="이름 수정" />
<input v-model.number="tempRow.age" type="number" placeholder="나이 수정" />
<button @click="updateGrid">현재 위치에 갱신</button>
<button @click="rollback">취소</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
gridData: [
{ id: 1, name: '김철수', age: 25 },
{ id: 2, name: '이영희', age: 30 },
{ id: 3, name: '박민수', age: 35 }
],
tempRow: null,
currentIndex: null
}
},
methods: {
selectRow(index) {
this.currentIndex = index;
},
moveToTemp(index) {
this.currentIndex = index;
this.tempRow = { ...this.gridData[index] }; // 그리드에서 데이터 가져오기
},
updateGrid() {
if (this.tempRow && this.currentIndex !== null) {
this.$set(this.gridData, this.currentIndex, { ...this.tempRow }); // 그리드에 덮어쓰기
this.tempRow = null; // 임시 데이터 초기화
}
},
rollback() {
if (this.currentIndex !== null) {
this.tempRow = { ...this.gridData[this.currentIndex] }; // 그리드의 현재 데이터로 tempRow 복원
}
}
}
}
</script>
<style scoped>
table {
border-collapse: collapse;
width: 100%;
margin-bottom: 20px;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
.selected {
background-color: #f0f0f0;
cursor: pointer;
}
.temp-area {
border: 1px solid #ccc;
padding: 15px;
margin-top: 20px;
}
button {
padding: 5px 10px;
margin-right: 5px;
cursor: pointer;
}
input {
margin: 5px 0;
padding: 5px;
}
</style>
동작 시나리오
-
초기 상태:
-
그리드: { id: 1, name: '김철수', age: 25 }
-
tempRow: null
-
-
moveToTemp 호출:
-
tempRow: { id: 1, name: '김철수', age: 25 }
-
그리드는 그대로 유지
-
-
수정 (99% 진행):
-
tempRow: { id: 1, name: '김철수2', age: 30 }
-
-
updateGrid 호출:
-
그리드: { id: 1, name: '김철수2', age: 30 }
-
tempRow: null
-
-
수정 중 rollback 호출 (99% 상태에서):
-
tempRow: { id: 1, name: '김철수', age: 25 } (그리드의 현재 데이터로 복원)
-
그리드는 변경 없음
-
개선된 점
-
originalRow 제거: 불필요한 백업 데이터 삭제
-
rollback 단순화: 그리드의 현재 데이터를 tempRow로 복원만 하면 됨
-
직관성: 수정 취소 시 tempRow가 원래 상태로 돌아가며, 그리드는 수정되지 않음
확인 질문
-
이 구현이 "수정 99% 하다가 취소하면 tempRow를 다시 채우는" 의도에 맞나요?
-
추가로 조정할 부분이 있으면 말씀해주세요!
이제 코드가 더 간결하고 의도에 맞게 동작할 겁니다. 피드백 부탁드립니다!
728x90
'Vue.js 를 배워보자' 카테고리의 다른 글
Vue.js로 실시간 판매량 그리드에 부드러운 숫자 애니메이션 구현하기 (0) | 2025.03.14 |
---|---|
Vue.js로 장바구니 수량 관리 기능 만들기 외 2개 예제 (0) | 2025.03.14 |
Vue.js와 Axios로 데이터셋을 그리드로 로딩하고 클릭 시 Input Text에 자동 바인딩하기 (0) | 2025.03.04 |
Vue.js에서 JSON Server 포트 변경하기 (0) | 2025.03.02 |
Vue.js에서 Prop과 Emit으로 팝업 데이터 주고받기 예제 (0) | 2025.03.02 |