[Vue] map 함수와 Vue3의 반응형 시스템
서버에서 데이터를 받아오고, 받아온 데이터를 v-for 반복문을 사용해서 렌더링한다고 생각해보자.
<thead v-for="(subject, index) in subjects" :key="index" class="table-element">
<tr>
<th class="text-left table-header">
<div>
<span>{{ subject.subject_no }}</span>
<!-- <span class="text-weight-bold">성별</span>
<span v-if="subject.gender == 'Female'">여성</span>
<span v-if="subject.gender == 'Male'">남성</span>
<span v-if="subject.gender == ''"></span>
<span class="text-weight-bold">연령(개월)</span>
<span>{{ subject.age_month }}</span> -->
</div>
</th>
</tr>
<tr>
<th class="text-left table-header">
<div class="th-container">
<div>
<span class="text-weight-bold">성별</span>
<span v-if="subject.gender == 'Female'">여성</span>
<span v-if="subject.gender == 'Male'">남성</span>
</div>
<q-btn
size="xs"
padding="xs"
color="blue"
icon="edit"
@click="editSubject(subject, index)"
/>
</div>
</th>
<td class="text-center flex-container">
<span class="q-gutter-sm checkbox-list">
<q-checkbox dense v-model="subject.a" color="blue" label="a" :disable="subject.resultModel.value !== 'new'"/>
<q-checkbox dense v-model="subject.b" color="blue" label="b" :disable="subject.resultModel.value !== 'new'"/>
<q-checkbox dense v-model="subject.c" color="blue" label="c" :disable="subject.resultModel.value !== 'new'"/>
<q-checkbox dense v-model="subject.d" color="blue" label="d" :disable="subject.resultModel.value !== 'new'"/>
</span>
<span class="button-list">
<q-btn color="blue" label="검사 시작 " :disable="subject.resultModel.value !== 'new'" @click="startNewExamine(subject)"/>
<q-btn color="blue" label="재검사" :disable="subject.resultModel.value == 'new' || subject.resultModel.value == ''"/>
<q-btn color="blue" label="결과 보기" :disable="subject.resultModel.value == 'new' || subject.resultModel.value == ''" @click="showResult(subject)"/>
</span>
</td>
</tr>
<tr class="tr-margin"></tr>
</thead>
각 subject에 대해 watch 등 비즈니스 로직을 추가하려면 서버로부터 subjects를 받아올 때 map, forEach 등 반복문을 사용해 각각의 subject에 로직을 추가하는 방식으로 작업하면 된다.
여기서 map을 사용할 때는 주의할 점이 하나 더 있다.
Vue3 에서는 reactive 또는 ref로 감싼 객체만이 반응형으로 동작해 데이터의 변경을 감지하고 해당 Vue 컴포넌트를 동적으로 업데이트 할 수 있다.
(ref는 단일 값을 반응형으로 만들 때 사용하고 reactive는 객체를 반응형으로 만들 때 사용한다. ref는 .value로 값에 접근할 수 있고 reactive는 일반 객체처럼 직접 값에 접근할 수 있다)
function getAllSubject() {
api.get('/subject/get-all-subject')
.then(response => {
subjects.value = response.data.map(subject => {
watch(() => subject.resultModel, (newValue) => {
if(newValue == 'new') {
subject.a = false;
subject.b = false;
subject.c = false;
subject.d = false;
}
if(newValue != 'new') {
api.get('/subject/get-result', {
params: {
subject_no: subject.subject_no,
examine_date: subject.resultModel.value
}
})
.then(response => {
subject.a = response.data.selected.aa;
subject.b = response.data.selected.bb;
subject.c = response.data.selected.cc;
subject.d = response.data.selected.dd;
})
.catch(error => {
console.error("결과 불러오기 오류", error);
})
}
});
if(subject.result_list && subject.result_list.length > 0) {
subject.result_list.sort((a, b) => {
const formatA = a.replace(/-/g, "/").replace(/(\d{4})\/(\d{2})\/(\d{2})\/(\d{2})\/(\d{2})\/(\d{2})/, "$1/$2/$3 $4:$5:$6");
const formatB = b.replace(/-/g, "/").replace(/(\d{4})\/(\d{2})\/(\d{2})\/(\d{2})\/(\d{2})\/(\d{2})/, "$1/$2/$3 $4:$5:$6");
return new Date(formatB) - new Date(formatA);
});
}
return {
...subject,
result_list: subject.result_list.sort((a, b) => new Date(b) - new Date(a)),
resultModel: {
value: '',
label: '',
},
a: false,
b: false,
c: false,
d: false,
}
})
})
.catch(error => {
console.error("subject/get-all-subject 오류", error)
})
}
이렇게 map 함수를 사용해서 각 subject에 watch를 추가하면 제대로 작동할까?
자바스크립트의 map 함수는 배열의 각 요소에 대해 주어진 함수를 실행하고 그 결과를 새로운 배열으로 반환하는 함수로 원본 배열을 변경하지 않고 각 요소에 대해 적용된 함수의 결과로 새로운 배열을 생성하는 함수이다.
위의 예시에서는 map 함수 내에서 새로 생성된 객체들이 Vue의 반응형 시스템에 추적되지 않는다.
따라서 map 함수 내에서 새롭게 생성하는 각 객체들을 reactive로 명시적으로 감싸 반응형으로 만들어야 한다.
함수를 수정해보자.
function getAllSubject() {
api.get('/subject/get-all-subject')
.then(response => {
subjects.value = response.data.map(subjectData => {
const subject = reactive({
...subjectData,
resultModel: {
value: '',
label: ''
},
a: false,
b: false,
c: false,
d: false,
});
watch(() => subject.resultModel.value, (newValue) => {
if(newValue == 'new') {
subject.a = false;
subject.b = false;
subject.c = false;
subject.d = false;
}
if(newValue != 'new') {
api.get('/subject/get-result', {
params: {
subject_no: subject.subject_no,
examine_date: subject.resultModel.value
}
})
.then(response => {
subject.a = response.data.selected.aa;
subject.b = response.data.selected.bb;
subject.c = response.data.selected.cc;
subject.d = response.data.selected.dd;
})
.catch(error => {
console.error("결과 불러오기 오류", error);
})
}
});
if(subject.result_list && subject.result_list.length > 0) {
subject.result_list.sort((a, b) => {
const formatA = a.replace(/-/g, "/").replace(/(\d{4})\/(\d{2})\/(\d{2})\/(\d{2})\/(\d{2})\/(\d{2})/, "$1/$2/$3 $4:$5:$6");
const formatB = b.replace(/-/g, "/").replace(/(\d{4})\/(\d{2})\/(\d{2})\/(\d{2})\/(\d{2})\/(\d{2})/, "$1/$2/$3 $4:$5:$6");
return new Date(formatB) - new Date(formatA);
});
}
return subject;
})
})
.catch(error => {
console.error("subject/get-profile 오류", error)
})
}
subjectData 객체는 api 응답으로부터 받은 원시 데이터를 가지고 있고, 이 데이터를 바탕으로 새로운 반응형 객체인 subject를 정의한다,
watch 함수로 데이터의 변화를 감지하고 지정된 콜백 함수를 실행시킨다.
예시에서는 resultModel.value가 변경될 때 마다 콜백 함수가 호출되고, subject의 다른 속성 값을 변경해 반응형을 구현한다.
요점은 map 함수 내부에서 reactive 객체를 반환한다는 점이다.
map 함수 사용 시 컴포넌트를 반응형으로 반환하는 부분을 빼먹는 경우가 많으니.. 주의하자.
'Solutions' 카테고리의 다른 글
[SQL Server] 지원하지 않는 TLS 버전 설정 (1) | 2024.06.05 |
---|---|
대용량 파일 업로드 처리 (30GB) (1) | 2023.12.03 |
[JavaScript] Shadow DOM 다루기 (0) | 2023.11.22 |
[Git] Git 협업 시 충돌 해결 (1) | 2023.11.14 |
디렉토리 구조 탐색과 이미지 메타데이터 저장 (0) | 2023.09.17 |
댓글
이 글 공유하기
다른 글
-
대용량 파일 업로드 처리 (30GB)
대용량 파일 업로드 처리 (30GB)
2023.12.03 -
[JavaScript] Shadow DOM 다루기
[JavaScript] Shadow DOM 다루기
2023.11.22 -
[Git] Git 협업 시 충돌 해결
[Git] Git 협업 시 충돌 해결
2023.11.14 -
디렉토리 구조 탐색과 이미지 메타데이터 저장
디렉토리 구조 탐색과 이미지 메타데이터 저장
2023.09.17