만들어볼 페이지
위와같이 특정 선택에 따라 해당하는 컴포넌트가 나오고, 요청 데이터를 모두 모아 한번에 전송해 주어야 하는데요… 이를 어떻게 구현할 수 있을까요
다양하게 구현할 수 있겠지만, 저는 순서를 지정하는 방식으로 구현하였습니다.
코드로 알아보기
기능 flow 체크하기
기능은 아래와 같은 순서로 동작 되며, 괄호안의 숫자로 번호를 지정 해 주었습니다.
1.
날짜 지정하기 (1)
2.
검색 타입 선택하기 (2)
3.
해당 타입에 맞춰 컴포넌트 반환하기
•
일반 검색 (3)
•
카테고리 검색 (4)
•
금액 범위 검색 (5)
Api 체크하기
타입에 따라 request 가 다릅니다.
import axiosInstance from './interceptor/RequestInterceptor.ts';
export const searchWithCategory = async (expenditureData: {
startDate: string;
endDate: string;
category: string;
}) => {
const response = await axiosInstance.post(
'/expenditures/search/category',
expenditureData,
);
console.log(response.data);
return response;
};
export const searchWithMoneyRange = async (expenditureData: {
startDate: string;
endDate: string;
minMoney: number;
maxMoney: number;
}) => {
const response = await axiosInstance.post(
'/expenditures/search/money-range',
expenditureData,
);
console.log(response.data);
return response;
};
export const normalSearch = async (expenditureData: {
startDate: string;
endDate: string;
}) => {
const response = await axiosInstance.post(
'/expenditures/search',
expenditureData,
);
console.log(response.data);
return response;
};
TypeScript
복사
컴포넌트 체크하기
여러 컴포넌트를 한군데에서 보여주는 것이기 때문에 각각의 타입에 대한 컴포넌트들은 자식 컴포넌트라 할 수 있습니다. 자식 컴포넌트 에서 선택된 데이터를 부모 컴포넌트로 보내줘야 하기 때문에 emits 과 props 를 사용해야 합니다.
emits 은 언제 쓰나요?
자식 컴포넌트의 데이터를 부모 컴포넌트로 보낼 때 사용합니다. 첫번째로 날짜를 지정해야 하는 자식 컴포넌트가 나오는데요, 이때 지정한 날짜를 부모 컴포넌트로 보낼 때 사용합니다.
<script setup lang="ts">
import { ref } from 'vue';
const startDate = ref('');
const endDate = ref('');
const emits = defineEmits(['update-date']);
function submitDate() {
emits('update-date', startDate.value, endDate.value);
}
</script>
<template>
<div class="container-col">
<form class="content-col">
<div class="form-col">
<div class="font_title">조회 기간 선택하기</div>
<div class="font-label">시작날짜와 끝 날짜를 선택해주세요.</div>
<input
class="input"
type="date"
v-model="startDate"
style="width: 180px"
/>
<input
class="input"
type="date"
v-model="endDate"
style="width: 180px"
/>
<div class="content-row">
<div type="submit" class="button-blue" @click="submitDate">
선택완료
</div>
</div>
</div>
</form>
</div>
</template>
HTML
복사
이걸 부모 컴포넌트에서 어떻게 받아오면 되나요?
해당 자식 컴포넌트를 부모 컴포넌트에 불러오고, @[자식 컴포넌트에서 지정한 키] = “부모 컴포넌트에서 지정한 함수명” 을 넣어주면 됩니다.
<script setup lang="ts">
import { ref } from 'vue';
import SelectDate from './search/SelectDate.vue';
const currentStep = ref(1);
...
function handleUpdateDate(sDate, eDate) {
startDate.value = sDate;
endDate.value = eDate;
currentStep.value = 2;
}
</script>
<template>
<div class="container-col">
<SelectDate v-if="currentStep === 1" @update-date="handleUpdateDate" />
...
</template>
HTML
복사
그렇다면 props 는 언제 쓰나요?
부모 컴포넌트에서 받아온 응답 데이터를 반환할 컴포넌트가 있을 수 있습니다. 이런식으로 부모 컴포넌트가 자식 컴포넌트에 데이터를 전달해야 할 경우 사용하게 됩니다.
아래와 같이 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달합니다.
<script setup lang="ts">
import SearchResult from './search/SearchResult.vue';
const searchResults = ref(null);
async function submitSearch() {
try {
let response;
const searchData = {
startDate: startDate.value,
endDate: endDate.value,
};
if (searchType.value == 'category') {
response = await searchWithCategory({
...searchData,
category: selectedCategory.value,
});
if (response.status == 200) {
currentStep.value = 5;
}
} else if (searchType.value == 'range') {
response = await searchWithMoneyRange({
...searchData,
minMoney: moneyRange.value.min,
maxMoney: moneyRange.value.max,
});
if (response.status == 200) {
currentStep.value = 5;
}
} else if (searchType.value == 'general') {
response = await normalSearch(searchData);
if (response.status == 200) {
currentStep.value = 5;
}
}
searchResults.value = response.data; // 요청으로 받은 데이터를 저장 합니다.
console.log(response.data);
} catch (error) {
console.error(error);
}
}
</script>
<template>
<div class="container-col">
...
<SearchResult
v-if="searchResults && currentStep == 5"
:results="searchResults"
/>
</div>
</template>
HTML
복사
그 후 자식 컴포넌트 에서 형식에 맞춰 데이터를 받으면 됩니다.
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
results: Object,
});
</script>
<template></template>
HTML
복사
전체 코드
<script setup lang="ts">
import { ref } from 'vue';
import SelectDate from './search/SelectDate.vue';
import SelectSearchType from './search/SelectSearchType.vue';
import SelectCategory from './search/SelectCategory.vue';
import SelectMoneyRange from './search/selectMoneyRange.vue';
import {
normalSearch,
searchWithCategory,
searchWithMoneyRange,
} from '../../api/ExpenditureApi.ts';
import SearchResult from './search/SearchResult.vue';
const currentStep = ref(1);
const searchType = ref('general');
const startDate = ref('');
const endDate = ref('');
const selectedCategory = ref('');
const moneyRange = ref({ min: 0, max: 0 });
const searchResults = ref(null);
function handleUpdateDate(sDate, eDate) {
startDate.value = sDate;
endDate.value = eDate;
currentStep.value = 2;
}
function handleUpdateType(type) {
searchType.value = type;
if (type === 'general') {
submitSearch();
} else {
currentStep.value = type === 'category' ? 3 : 4;
}
}
function handleUpdateCategory(category) {
selectedCategory.value = category;
submitSearch();
}
function handleUpdateRange(range) {
moneyRange.value = range;
submitSearch();
}
async function submitSearch() {
try {
let response;
const searchData = {
startDate: startDate.value,
endDate: endDate.value,
};
if (searchType.value == 'category') {
response = await searchWithCategory({
...searchData,
category: selectedCategory.value,
});
if (response.status == 200) {
currentStep.value = 5;
}
} else if (searchType.value == 'range') {
response = await searchWithMoneyRange({
...searchData,
minMoney: moneyRange.value.min,
maxMoney: moneyRange.value.max,
});
if (response.status == 200) {
currentStep.value = 5;
}
} else if (searchType.value == 'general') {
response = await normalSearch(searchData);
if (response.status == 200) {
currentStep.value = 5;
}
}
searchResults.value = response.data;
console.log(response.data);
} catch (error) {
console.error(error);
}
}
</script>
<template>
<div class="container-col">
<SelectDate v-if="currentStep === 1" @update-date="handleUpdateDate" />
<SelectSearchType
v-if="currentStep === 2"
@update-type="handleUpdateType"
/>
<SelectCategory
v-if="currentStep === 3 && searchType === 'category'"
@update-category="handleUpdateCategory"
/>
<SelectMoneyRange
v-if="currentStep === 4 && searchType === 'range'"
@update-range="handleUpdateRange"
/>
<SearchResult
v-if="searchResults && currentStep == 5"
:results="searchResults"
/>
</div>
</template>
<style lang="scss">
@import '../../assets/styles/common/container';
@import '../../assets/styles/color/color';
.container-col {
@include container(column, flex-start, flex-start, 100%, auto);
}
.content-col {
@include container(column, center, center, 100%, auto);
}
</style>
HTML
복사