/////
Search
➡️

flow 따라 해당하는 component 추가하기

태그
Vue.js
TypeScript
목차

만들어볼 페이지

위와같이 특정 선택에 따라 해당하는 컴포넌트가 나오고, 요청 데이터를 모두 모아 한번에 전송해 주어야 하는데요…  이를 어떻게 구현할 수 있을까요
다양하게 구현할 수 있겠지만, 저는 순서를 지정하는 방식으로 구현하였습니다.

코드로 알아보기

기능 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
복사

컴포넌트 체크하기

여러 컴포넌트를 한군데에서 보여주는 것이기 때문에 각각의 타입에 대한 컴포넌트들은 자식 컴포넌트라 할 수 있습니다. 자식 컴포넌트 에서 선택된 데이터를 부모 컴포넌트로 보내줘야 하기 때문에 emitsprops 를 사용해야 합니다.

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
복사