From 119bfe0b21d761d03a029b45d53cc73c2e931a83 Mon Sep 17 00:00:00 2001 From: seonju21 Date: Fri, 8 Aug 2025 23:04:06 +0900 Subject: [PATCH 01/42] =?UTF-8?q?=E2=99=BB=20refactor:=20=EB=A7=A4?= =?UTF-8?q?=EB=AC=BC=20=EB=A9=94=EC=9D=B8=ED=8E=98=EC=9D=B4=EC=A7=80=20API?= =?UTF-8?q?=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../homes/homelist/FilterSidebar.vue | 48 +--------- src/pages/homes/HomeListPage.vue | 94 +++++++------------ 2 files changed, 37 insertions(+), 105 deletions(-) diff --git a/src/components/homes/homelist/FilterSidebar.vue b/src/components/homes/homelist/FilterSidebar.vue index 12059f0..adabcec 100644 --- a/src/components/homes/homelist/FilterSidebar.vue +++ b/src/components/homes/homelist/FilterSidebar.vue @@ -1,6 +1,5 @@ - - diff --git a/src/components/homes/homecreate/Step2PriceInfo.vue b/src/components/homes/homecreate/Step2PriceInfo.vue index fbd6ea3..2c72425 100644 --- a/src/components/homes/homecreate/Step2PriceInfo.vue +++ b/src/components/homes/homecreate/Step2PriceInfo.vue @@ -128,13 +128,12 @@ diff --git a/src/components/homes/homecreate/Step3DetailInfo.vue b/src/components/homes/homecreate/Step3DetailInfo.vue index 6f356ba..2947232 100644 --- a/src/components/homes/homecreate/Step3DetailInfo.vue +++ b/src/components/homes/homecreate/Step3DetailInfo.vue @@ -165,8 +165,12 @@
diff --git a/src/pages/homes/HomeCreatePage.vue b/src/pages/homes/HomeCreatePage.vue index dc6cb4f..e112bdd 100644 --- a/src/pages/homes/HomeCreatePage.vue +++ b/src/pages/homes/HomeCreatePage.vue @@ -15,7 +15,7 @@ diff --git a/src/components/homes/homecreate/Step3DetailInfo.vue b/src/components/homes/homecreate/Step3DetailInfo.vue index 2947232..7f45158 100644 --- a/src/components/homes/homecreate/Step3DetailInfo.vue +++ b/src/components/homes/homecreate/Step3DetailInfo.vue @@ -90,8 +90,8 @@ type="number" min="0" class="border rounded p-2 pr-12 w-full no-spin" - :value="form.bathroomCount" - @input="handleChange('bathroomCount', $event.target.valueAsNumber)" + :value="form.bathroomCnt" + @input="handleChange('bathroomCnt', $event.target.valueAsNumber)" required placeholder="0" /> @@ -170,7 +170,7 @@ isPet: form.isPet, isParking: form.isParking, }" - @update:modelValue="(val) => updateForm(val)" + @update:modelValue="updateForm" /> @@ -197,6 +197,13 @@ const handleChange = (key, value) => { }) } +const updateForm = (updatedFields) => { + emit('update:form', { + ...props.form, + ...updatedFields, + }) +} + const homeDirectionOptions = [ { label: '남향', value: 'S' }, { label: '동향', value: 'E' }, diff --git a/src/components/homes/homedetails/RoomDetails.vue b/src/components/homes/homedetails/RoomDetails.vue index e1b9bea..e041be2 100644 --- a/src/components/homes/homedetails/RoomDetails.vue +++ b/src/components/homes/homedetails/RoomDetails.vue @@ -71,7 +71,7 @@
관리비 포함 항목
diff --git a/src/pages/homes/HomeCreatePage.vue b/src/pages/homes/HomeCreatePage.vue index e112bdd..8de1de3 100644 --- a/src/pages/homes/HomeCreatePage.vue +++ b/src/pages/homes/HomeCreatePage.vue @@ -55,7 +55,7 @@ const form = reactive({ supplyArea: 0, exclusiveArea: 0, roomCnt: 0, - bathroomCount: 0, + bathroomCnt: 0, homeFloor: 0, buildingTotalFloors: 0, buildDate: '', @@ -123,13 +123,13 @@ const handleSubmit = async () => { supplyArea: safeNumber(rawForm.supplyArea), exclusiveArea: safeNumber(rawForm.exclusiveArea), roomCnt: safeNumber(rawForm.roomCnt), - bathroomCount: safeNumber(rawForm.bathroomCount), + bathroomCnt: safeNumber(rawForm.bathroomCnt), homeFloor: safeNumber(rawForm.homeFloor), buildingTotalFloors: safeNumber(rawForm.buildingTotalFloors), buildDate: rawForm.buildDate, homeDirection: rawForm.homeDirection, isPet: rawForm.isPet, - isParkingAvailable: rawForm.isParking, + isParking: rawForm.isParking, area: safeNumber(rawForm.area), landCategory: rawForm.landCategory, facilityItemIds: rawForm.facilityItemIds, From 61e78f6f2ef7e8212cdfb067a1293f8dd60d852e Mon Sep 17 00:00:00 2001 From: seonju21 Date: Wed, 13 Aug 2025 15:55:55 +0900 Subject: [PATCH 22/42] =?UTF-8?q?=E2=99=BB=20refactor:=20=EB=A7=A4?= =?UTF-8?q?=EB=AC=BC=20=EB=93=B1=EB=A1=9D=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=8B=9C=EC=84=A4=ED=95=AD=EB=AA=A9=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/listing.js | 15 ++++++++--- .../homes/homecreate/Step2PriceInfo.vue | 27 ++++++++++--------- src/pages/homes/HomeCreatePage.vue | 7 +++-- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/apis/listing.js b/src/apis/listing.js index 445a266..b11b5a0 100644 --- a/src/apis/listing.js +++ b/src/apis/listing.js @@ -44,24 +44,33 @@ export async function fetchListingById(id) { } } +// apis/listing.js 파일의 createListing 함수 export async function createListing(listingData, images) { try { const formData = new FormData() + // listingData의 모든 필드를 formData에 추가 for (const key in listingData) { if (Object.prototype.hasOwnProperty.call(listingData, key)) { const value = listingData[key] - if (Array.isArray(value)) { - value.forEach((item) => { - formData.append(key, item) + if (key === 'maintenanceFees' && Array.isArray(value)) { + // ⭐ maintenanceFees 배열의 각 객체를 Spring DTO 바인딩 규칙에 맞게 추가 + value.forEach((feeItem, index) => { + formData.append(`maintenanceFees[${index}].maintenanceId`, feeItem.maintenanceId) + formData.append(`maintenanceFees[${index}].fee`, feeItem.fee) }) + } else if (Array.isArray(value)) { + // 다른 배열은 일반적인 방식으로 추가 + value.forEach((item) => formData.append(key, item)) } else if (value !== null && value !== undefined) { + // 그 외의 필드는 그대로 추가 formData.append(key, value) } } } + // 이미지 파일들을 formData에 추가 images.forEach((image) => { formData.append('images', image) }) diff --git a/src/components/homes/homecreate/Step2PriceInfo.vue b/src/components/homes/homecreate/Step2PriceInfo.vue index 2c72425..8e90481 100644 --- a/src/components/homes/homecreate/Step2PriceInfo.vue +++ b/src/components/homes/homecreate/Step2PriceInfo.vue @@ -49,7 +49,7 @@ :label="item.maintenanceName" :modelValue="isChecked(item.maintenanceId)" @update:modelValue=" - (checked) => toggleMaintenanceItem(item.maintenanceId, item.maintenanceName, checked) + (checked) => toggleMaintenanceItem(item.maintenanceId, item.fee, checked) " />
@@ -117,7 +117,7 @@ :label="item.maintenanceName" :modelValue="isChecked(item.maintenanceId)" @update:modelValue=" - (checked) => toggleMaintenanceItem(item.maintenanceId, item.maintenanceName, checked) + (checked) => toggleMaintenanceItem(item.maintenanceId, item.fee, checked) " /> @@ -157,27 +157,30 @@ function displayValue(field) { } const maintenanceItems = [ - { maintenanceId: 3, maintenanceName: '가스료' }, - { maintenanceId: 2, maintenanceName: '수도료' }, - { maintenanceId: 4, maintenanceName: '인터넷' }, - { maintenanceId: 1, maintenanceName: '전기료' }, - { maintenanceId: 5, maintenanceName: '청소비' }, + { maintenanceId: 3, maintenanceName: '가스료', fee: 0 }, + { maintenanceId: 2, maintenanceName: '수도료', fee: 0 }, + { maintenanceId: 4, maintenanceName: '인터넷', fee: 0 }, + { maintenanceId: 1, maintenanceName: '전기료', fee: 0 }, + { maintenanceId: 5, maintenanceName: '청소비', fee: 0 }, ] -function toggleMaintenanceItem(id, name, checked) { - let newItems = [...(props.form.maintenanceFeeItems || [])] +function toggleMaintenanceItem(id, fee, checked) { + let newItems = [...(props.form.maintenanceFees || [])] if (checked) { if (!newItems.some((item) => toRaw(item).maintenanceId === id)) { - newItems.push({ maintenanceId: id, itemName: name }) + newItems.push({ maintenanceId: id, fee: fee }) } } else { newItems = newItems.filter((item) => toRaw(item).maintenanceId !== id) } - emit('update:form', { maintenanceFeeItems: toRaw(newItems) }) + emit('update:form', { + ...props.form, + maintenanceFees: toRaw(newItems), + }) } function isChecked(id) { - return (props.form.maintenanceFeeItems || []).some((item) => toRaw(item).maintenanceId === id) + return (props.form.maintenanceFees || []).some((item) => toRaw(item).maintenanceId === id) } diff --git a/src/pages/homes/HomeCreatePage.vue b/src/pages/homes/HomeCreatePage.vue index 8de1de3..cc1c4f6 100644 --- a/src/pages/homes/HomeCreatePage.vue +++ b/src/pages/homes/HomeCreatePage.vue @@ -61,7 +61,7 @@ const form = reactive({ buildDate: '', homeDirection: '', facilityItemIds: [], - maintenanceFeeItems: [], + maintenanceFees: [], description: '', images: [], isPet: false, @@ -112,6 +112,8 @@ const handleSubmit = async () => { } // 다른 필수 필드들에 대해서도 여기에 유효성 검사를 추가할 수 있습니다. + const rawForm = toRaw(form) + const payload = { addr1: rawForm.addr1, addr2: rawForm.addr2, @@ -133,12 +135,13 @@ const handleSubmit = async () => { area: safeNumber(rawForm.area), landCategory: rawForm.landCategory, facilityItemIds: rawForm.facilityItemIds, - maintenanceFeeItems: rawForm.maintenanceFeeItems, + maintenanceFees: rawForm.maintenanceFees, } console.log('📦 최종 제출 데이터 (payload):', payload) console.log('🖼️ 업로드할 이미지 파일:', rawForm.images) + // ⭐ createListing 함수에 DTO 객체와 이미지 배열을 분리해서 전달 const response = await createListing(payload, rawForm.images) const homeId = response From f0ddaf63a679cc46fe69c95cfa908edeb2793edd Mon Sep 17 00:00:00 2001 From: seonju21 Date: Wed, 13 Aug 2025 16:07:10 +0900 Subject: [PATCH 23/42] =?UTF-8?q?=E2=99=BB=20refactor:=20=EB=A7=A4?= =?UTF-8?q?=EB=AC=BC=20=EB=93=B1=EB=A1=9D=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=EB=B9=84=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/homes/homedetails/RoomDetails.vue | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/components/homes/homedetails/RoomDetails.vue b/src/components/homes/homedetails/RoomDetails.vue index e041be2..13bb251 100644 --- a/src/components/homes/homedetails/RoomDetails.vue +++ b/src/components/homes/homedetails/RoomDetails.vue @@ -51,7 +51,7 @@ 방 / 욕실 수
- {{ listing.roomCnt }}개 / {{ listing.bathroomCnt }}개 + {{ listing.roomCnt }}개 / {{ listing.bathroomCount }}개
@@ -63,7 +63,7 @@
월 관리비
- {{ listing.maintenaceFee ?? 0 }}만원 + {{ listing.maintenanceFee ?? 0 }}만원
@@ -71,7 +71,7 @@
관리비 포함 항목
@@ -89,7 +89,7 @@

건물 시설

@@ -135,14 +135,12 @@ - - diff --git a/src/pages/homes/HomeDetailsPage.vue b/src/pages/homes/HomeDetailsPage.vue index 0316416..bc1a583 100644 --- a/src/pages/homes/HomeDetailsPage.vue +++ b/src/pages/homes/HomeDetailsPage.vue @@ -13,7 +13,7 @@ @@ -41,7 +41,7 @@ import RoomDetails from '@/components/homes/homedetails/RoomDetails.vue' import BaseButton from '@/components/common/BaseButton.vue' import TravelMap from '@/components/travel/TravelMap.vue' -import { fetchListingById } from '@/apis/listing.js' // API 함수 import +import { fetchListingById } from '@/apis/listing.js' const route = useRoute() const router = useRouter() @@ -49,7 +49,8 @@ const id = Number(route.params.no) const listing = ref(null) const images = ref([]) -const isFavorite = ref(false) // 찜 상태를 서버에서 받아와 초기화 +const isFavorite = ref(false) +const processedAddress = ref('') onMounted(async () => { try { @@ -59,8 +60,14 @@ onMounted(async () => { if (data) { listing.value = data images.value = data.imageUrls || [] - // 서버에서 찜 상태를 확인하는 API가 있다면 여기서 호출하여 isFavorite 초기화 - // 예를 들어, isFavorite.value = await fetchHomeLikeStatus(id) + + if (data.addr1) { + processedAddress.value = data.addr1 + } else { + processedAddress.value = data.addr2 || '주소정보 없음' + } + + console.log('최종 가공된 주소:', processedAddress.value) } } catch (err) { console.error('매물 조회 실패:', err) From aee5f47d1fdaad4d756256a6bb2b513be8640c1a Mon Sep 17 00:00:00 2001 From: seonju21 Date: Wed, 13 Aug 2025 17:11:58 +0900 Subject: [PATCH 26/42] =?UTF-8?q?=E2=99=BB=20refactor:=20=EB=A7=A4?= =?UTF-8?q?=EB=AC=BC=20=EC=83=81=EC=84=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=A7=80=EB=8F=84=20=EC=9C=84=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/homes/HomeCreatePage.vue | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/pages/homes/HomeCreatePage.vue b/src/pages/homes/HomeCreatePage.vue index cc1c4f6..18115d0 100644 --- a/src/pages/homes/HomeCreatePage.vue +++ b/src/pages/homes/HomeCreatePage.vue @@ -89,7 +89,7 @@ const handleSubmit = async () => { const rawForm = toRaw(form) - // ⭐ 필수 필드에 대한 유효성 검사 로직 추가 + // ⭐ 필수 필드에 대한 유효성 검사 로직 if (!rawForm.residenceType) { alert('매물 종류를 선택해주세요.') goToStep(1) @@ -110,9 +110,7 @@ const handleSubmit = async () => { goToStep(3) return } - // 다른 필수 필드들에 대해서도 여기에 유효성 검사를 추가할 수 있습니다. - - const rawForm = toRaw(form) + // 다른 필수 필드들에 대한 유효성 검사를 여기에 추가할 수 있습니다. const payload = { addr1: rawForm.addr1, @@ -141,7 +139,7 @@ const handleSubmit = async () => { console.log('📦 최종 제출 데이터 (payload):', payload) console.log('🖼️ 업로드할 이미지 파일:', rawForm.images) - // ⭐ createListing 함수에 DTO 객체와 이미지 배열을 분리해서 전달 + // createListing 함수에 DTO 객체와 이미지 배열을 분리해서 전달 const response = await createListing(payload, rawForm.images) const homeId = response From d59fb72bbc3bf32b76cb087244912de8dd0f82cd Mon Sep 17 00:00:00 2001 From: seonju21 Date: Thu, 14 Aug 2025 12:56:39 +0900 Subject: [PATCH 27/42] =?UTF-8?q?=E2=99=BB=20refactor:=20=EB=A7=A4?= =?UTF-8?q?=EB=AC=BC=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=9B=90=20=EB=8B=A8?= =?UTF-8?q?=EC=9C=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../homes/homecreate/Step2PriceInfo.vue | 8 ++-- .../homes/homedetails/ListingBasicInfo.vue | 41 +++++++++---------- .../homes/homedetails/RoomDetails.vue | 28 ++++++++++++- src/components/homes/homelist/ListingCard.vue | 40 +++++++++--------- 4 files changed, 70 insertions(+), 47 deletions(-) diff --git a/src/components/homes/homecreate/Step2PriceInfo.vue b/src/components/homes/homecreate/Step2PriceInfo.vue index 8e90481..cd9bf52 100644 --- a/src/components/homes/homecreate/Step2PriceInfo.vue +++ b/src/components/homes/homecreate/Step2PriceInfo.vue @@ -19,7 +19,7 @@ required /> 만원
@@ -36,7 +36,7 @@ placeholder="0" /> 만원
@@ -70,7 +70,7 @@ required /> 만원원 @@ -87,7 +87,7 @@ placeholder="0" /> 만원원 diff --git a/src/components/homes/homedetails/ListingBasicInfo.vue b/src/components/homes/homedetails/ListingBasicInfo.vue index 64db7b5..dcfbb2b 100644 --- a/src/components/homes/homedetails/ListingBasicInfo.vue +++ b/src/components/homes/homedetails/ListingBasicInfo.vue @@ -11,7 +11,7 @@
- 관리비 {{ formatNumber(listing.maintenaceFee) }}만원 + 관리비 {{ formatNumber(listing.maintenaceFee) }}원
@@ -26,26 +26,25 @@ defineProps({ function formatNumber(value) { if (typeof value === 'number') { - // 억 단위 처리 주석 처리 - // if (value >= 100000000) { - // const billion = Math.floor(value / 100000000) - // const remainder = value % 100000000 - // if (remainder > 0) { - // return `${billion}억 ${formatNumber(remainder)}` - // } else { - // return `${billion}억` - // } - // } - // 만 단위 처리 주석 처리 - // if (value >= 10000) { - // const tenThousand = Math.floor(value / 10000) - // const remainder = value % 10000 - // if (remainder > 0) { - // return `${tenThousand}만 ${formatNumber(remainder)}` - // } else { - // return `${tenThousand}만` - // } - // } + if (value >= 100000000) { + const billion = Math.floor(value / 100000000) + const remainder = value % 100000000 + if (remainder > 0) { + return `${billion}억 ${formatNumber(remainder)}` + } else { + return `${billion}억` + } + } + + if (value >= 10000) { + const tenThousand = Math.floor(value / 10000) + const remainder = value % 10000 + if (remainder > 0) { + return `${tenThousand}만 ${formatNumber(remainder)}` + } else { + return `${tenThousand}만` + } + } return value.toLocaleString() } return value ?? '0' diff --git a/src/components/homes/homedetails/RoomDetails.vue b/src/components/homes/homedetails/RoomDetails.vue index 827a79d..ac3bd80 100644 --- a/src/components/homes/homedetails/RoomDetails.vue +++ b/src/components/homes/homedetails/RoomDetails.vue @@ -63,7 +63,7 @@
월 관리비
- {{ listing.maintenaceFee ?? 0 }}만원 + {{ formatNumber(listing.maintenaceFee) }}원
@@ -248,6 +248,32 @@ const getIcon = (itemName) => { return iconMap[itemName] || null } +function formatNumber(value) { + if (typeof value === 'number') { + if (value >= 100000000) { + const billion = Math.floor(value / 100000000) + const remainder = value % 100000000 + if (remainder > 0) { + return `${billion}억 ${formatNumber(remainder)}` + } else { + return `${billion}억` + } + } + + if (value >= 10000) { + const tenThousand = Math.floor(value / 10000) + const remainder = value % 10000 + if (remainder > 0) { + return `${tenThousand}만 ${formatNumber(remainder)}` + } else { + return `${tenThousand}만` + } + } + return value.toLocaleString() + } + return value ?? '0' +} + const categorizedFacilities = computed(() => { if (!listing.facilities || !Array.isArray(listing.facilities)) { return {} diff --git a/src/components/homes/homelist/ListingCard.vue b/src/components/homes/homelist/ListingCard.vue index 4d0e0fe..7f30087 100644 --- a/src/components/homes/homelist/ListingCard.vue +++ b/src/components/homes/homelist/ListingCard.vue @@ -81,27 +81,25 @@ defineProps({ }) function formatNumber(value) { - if (typeof value === 'number') { - // 억 단위 처리 - // if (value >= 100000000) { - // const billion = Math.floor(value / 100000000) - // const remainder = value % 100000000 - // if (remainder > 0) { - // return `${billion}억 ${remainder.toLocaleString()}` - // } else { - // return `${billion}억` - // } - // } - // 천만 단위 처리 - // if (value >= 10000) { - // const tenThousand = Math.floor(value / 10000) - // const remainder = value % 10000 - // if (remainder > 0) { - // return `${tenThousand}만 ${remainder.toLocaleString()}` - // } else { - // return `${tenThousand}만` - // } - // } + if (typeof value === 'number' && value > 0) { + if (value >= 100000000) { + const billion = Math.floor(value / 100000000) + const remainder = value % 100000000 + return remainder > 0 ? `${billion}억 ${formatNumber(remainder)}` : `${billion}억` + } + + if (value >= 10000) { + const tenThousand = Math.floor(value / 10000) + const remainder = value % 10000 + return remainder > 0 ? `${tenThousand}만 ${formatNumber(remainder)}` : `${tenThousand}만` + } + + if (value >= 1000) { + const thousand = Math.floor(value / 1000) + const remainder = value % 1000 + return remainder > 0 ? `${thousand}천 ${remainder.toLocaleString()}` : `${thousand}천` + } + return value.toLocaleString() } return value ?? '0' From 46ebea51bcc52ef50652d3e4b8bac8b5f633f574 Mon Sep 17 00:00:00 2001 From: seonju21 Date: Thu, 14 Aug 2025 15:28:02 +0900 Subject: [PATCH 28/42] =?UTF-8?q?=E2=99=BB=20refactor:=20=EB=A7=A4?= =?UTF-8?q?=EB=AC=BC=20=EB=A9=94=EC=9D=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/listing.js | 2 +- .../homes/homelist/FilterSidebar.vue | 84 +++++++++++++--- src/pages/homes/HomeListPage.vue | 99 +++++++++++++++---- 3 files changed, 153 insertions(+), 32 deletions(-) diff --git a/src/apis/listing.js b/src/apis/listing.js index b11b5a0..aca6d7c 100644 --- a/src/apis/listing.js +++ b/src/apis/listing.js @@ -27,7 +27,7 @@ async function uploadImageToS3(file) { export async function fetchListings(params = {}) { try { const response = await api.get(API_BASE_URL, { params }) - return response.data.data + return response.data } catch (error) { console.error('매물 리스트 조회/검색 실패', error) throw error diff --git a/src/components/homes/homelist/FilterSidebar.vue b/src/components/homes/homelist/FilterSidebar.vue index 105700e..5e0c32d 100644 --- a/src/components/homes/homelist/FilterSidebar.vue +++ b/src/components/homes/homelist/FilterSidebar.vue @@ -1,4 +1,3 @@ -// @@ -47,16 +79,21 @@ const filters = ref({ city: '전체', district: '전체', houseType: '전체', - dealType: '월세', - depositRange: 500, - monthlyRange: 50, - leaseRange: 10000, - area: 30, // 평 단위 + dealType: '전체', + depositRange: 0, + monthlyRange: 0, + leaseRange: 0, + area: 0, direction: null, floor: null, conditions: [], }) +const page = ref(1) +const size = ref(20) // 초기 로딩 시 모든 매물을 가져오기 위해 충분히 큰 값으로 설정 +const totalItems = ref(0) +const totalPages = computed(() => Math.ceil(totalItems.value / size.value)) + const selectedGu = computed(() => { return filters.value.district !== '전체' && filters.value.district !== undefined ? filters.value.district @@ -64,15 +101,27 @@ const selectedGu = computed(() => { }) const otherCount = computed(() => { + if (!Array.isArray(listings.value) || listings.value.length <= 1) { + return 0 + } const uniqueGus = new Set(listings.value.map((listing) => listing.addr1)) return uniqueGus.size > 1 ? uniqueGus.size - 1 : 0 }) function onFilterChange(newFilters) { filters.value = { ...newFilters } + page.value = 1 + size.value = 20 // 필터링 시 페이지네이션을 위해 size를 20으로 변경 loadListings() } +function changePage(newPage) { + if (newPage > 0 && newPage <= totalPages.value) { + page.value = newPage + loadListings() + } +} + function goCreatePage() { router.push('/homes/create') } @@ -84,17 +133,11 @@ function goDetailPage(id) { async function loadListings() { try { const params = { + page: page.value, + size: size.value, residenceType: filters.value.houseType !== '전체' ? filters.value.houseType : undefined, leaseType: filters.value.dealType !== '전체' ? filters.value.dealType : undefined, - maxDepositPrice: - filters.value.dealType === '월세' - ? filters.value.depositRange * 10000 - : filters.value.dealType === '전세' - ? filters.value.leaseRange * 10000 - : undefined, - maxMonthlyRent: - filters.value.dealType === '월세' ? filters.value.monthlyRange * 10000 : undefined, - maxSupplyArea: filters.value.area * 3.30578, + maxSupplyArea: filters.value.area > 0 ? filters.value.area * 3.30578 : undefined, addr1: filters.value.city !== '전체' ? filters.value.district !== '전체' @@ -103,13 +146,31 @@ async function loadListings() { : undefined, } - const result = await fetchListings(params) - listings.value = result + if (filters.value.dealType === '월세' && filters.value.depositRange > 0) { + params.maxDepositPrice = filters.value.depositRange * 10000 + } + if (filters.value.dealType === '월세' && filters.value.monthlyRange > 0) { + params.maxMonthlyRent = filters.value.monthlyRange * 10000 + } + if (filters.value.dealType === '전세' && filters.value.leaseRange > 0) { + params.maxDepositPrice = filters.value.leaseRange * 10000 + } + + const response = await fetchListings(params) + + if (response && response.content) { + listings.value = response.content + totalItems.value = response.totalElements || response.data.length + } else { + listings.value = [] + totalItems.value = 0 + } - // ✨ 여기에 콘솔 로그를 추가하면 됩니다. console.log('✅ API 응답으로 받아온 매물 목록:', listings.value) } catch (err) { console.error('목록 조회 실패:', err) + listings.value = [] + totalItems.value = 0 } } From 858a67889bca5afa81685d9757db92a452f09cce Mon Sep 17 00:00:00 2001 From: seonju21 Date: Thu, 14 Aug 2025 15:39:26 +0900 Subject: [PATCH 29/42] =?UTF-8?q?=E2=99=BB=20refactor:=20=EB=A7=A4?= =?UTF-8?q?=EB=AC=BC=20=EB=A9=94=EC=9D=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/homes/HomeListPage.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/homes/HomeListPage.vue b/src/pages/homes/HomeListPage.vue index ae0e92a..2b46468 100644 --- a/src/pages/homes/HomeListPage.vue +++ b/src/pages/homes/HomeListPage.vue @@ -90,7 +90,7 @@ const filters = ref({ }) const page = ref(1) -const size = ref(20) // 초기 로딩 시 모든 매물을 가져오기 위해 충분히 큰 값으로 설정 +const size = ref(21) // 초기 로딩 시 모든 매물을 가져오기 위해 충분히 큰 값으로 설정 const totalItems = ref(0) const totalPages = computed(() => Math.ceil(totalItems.value / size.value)) @@ -111,7 +111,7 @@ const otherCount = computed(() => { function onFilterChange(newFilters) { filters.value = { ...newFilters } page.value = 1 - size.value = 20 // 필터링 시 페이지네이션을 위해 size를 20으로 변경 + size.value = 21 loadListings() } From 11fceecefa1769a605c3f950ebe4f644a55af145 Mon Sep 17 00:00:00 2001 From: seonju21 Date: Thu, 14 Aug 2025 17:59:16 +0900 Subject: [PATCH 30/42] =?UTF-8?q?=E2=99=BB=20refactor:=20=EB=A7=A4?= =?UTF-8?q?=EB=AC=BC=20=EB=A9=94=EC=9D=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=ED=95=84=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/listing.js | 2 +- .../homes/homelist/FilterSidebar.vue | 105 +----------------- src/pages/homes/HomeListPage.vue | 56 ++++++++-- 3 files changed, 49 insertions(+), 114 deletions(-) diff --git a/src/apis/listing.js b/src/apis/listing.js index aca6d7c..7d44f78 100644 --- a/src/apis/listing.js +++ b/src/apis/listing.js @@ -26,7 +26,7 @@ async function uploadImageToS3(file) { export async function fetchListings(params = {}) { try { - const response = await api.get(API_BASE_URL, { params }) + const response = await api.get(API_BASE_URL + '/search', { params }) return response.data } catch (error) { console.error('매물 리스트 조회/검색 실패', error) diff --git a/src/components/homes/homelist/FilterSidebar.vue b/src/components/homes/homelist/FilterSidebar.vue index 5e0c32d..15f8538 100644 --- a/src/components/homes/homelist/FilterSidebar.vue +++ b/src/components/homes/homelist/FilterSidebar.vue @@ -236,115 +236,16 @@ function resetFilters() { conditions: [], } districtList.value = [] + // 가공되지 않은 초기화된 filters 객체를 통째로 전달 emit('filter-change', filters.value) } function emitFilterChange() { - const searchParams = {} - - if (filters.value.houseType !== '전체') { - searchParams.residenceType = filters.value.houseType - } - - if (filters.value.dealType !== '전체') { - searchParams.leaseType = filters.value.dealType - if (filters.value.dealType === '월세') { - if (filters.value.depositRange > 0) { - searchParams.maxDepositPrice = filters.value.depositRange * 10000 - } - if (filters.value.monthlyRange > 0) { - searchParams.maxMonthlyRent = filters.value.monthlyRange * 10000 - } - } else if (filters.value.dealType === '전세') { - if (filters.value.leaseRange > 0) { - searchParams.maxDepositPrice = filters.value.leaseRange * 10000 - } - } - } - - if (filters.value.area > 0) { - searchParams.maxSupplyArea = filters.value.area * 3.30578 - } - - if (filters.value.city !== '전체') { - searchParams.addr1 = - filters.value.district !== '전체' ? filters.value.district : filters.value.city - } - - if (filters.value.direction) { - searchParams.homeDirection = filters.value.direction - } - - if (filters.value.floor) { - if (filters.value.floor === '반지하') { - searchParams.minFloor = -1 - searchParams.maxFloor = -1 - } else if (filters.value.floor === '1층') { - searchParams.minFloor = 1 - searchParams.maxFloor = 1 - } else if (filters.value.floor === '2~5층') { - searchParams.minFloor = 2 - searchParams.maxFloor = 5 - } else if (filters.value.floor === '6~9층') { - searchParams.minFloor = 6 - searchParams.maxFloor = 9 - } else if (filters.value.floor === '10층 이상') { - searchParams.minFloor = 10 - searchParams.maxFloor = 9999 - } - } - - if (filters.value.conditions.includes('반려동물 가능')) { - searchParams.isPet = true - } - if (filters.value.conditions.includes('주차 가능')) { - searchParams.isParking = true - } - - emit('filter-change', searchParams) + // 가공하지 않고, filters.value 객체 자체를 전달합니다. + emit('filter-change', filters.value) } const directions = ['남향', '동향', '서향', '북향', '남동향', '남서향', '북동향', '북서향'] const floors = ['반지하', '1층', '2~5층', '6~9층', '10층 이상'] const conditions = ['주차 가능', '반려동물 가능', '엘리베이터'] - - diff --git a/src/pages/homes/HomeListPage.vue b/src/pages/homes/HomeListPage.vue index 2b46468..d89d18f 100644 --- a/src/pages/homes/HomeListPage.vue +++ b/src/pages/homes/HomeListPage.vue @@ -66,7 +66,7 @@ diff --git a/src/pages/homes/HomeListPage.vue b/src/pages/homes/HomeListPage.vue index d89d18f..b3e12ed 100644 --- a/src/pages/homes/HomeListPage.vue +++ b/src/pages/homes/HomeListPage.vue @@ -66,7 +66,7 @@ From f960c49e6bc26c0478cd51160f5822e544fd40d5 Mon Sep 17 00:00:00 2001 From: seonju21 Date: Sat, 16 Aug 2025 14:30:07 +0900 Subject: [PATCH 33/42] =?UTF-8?q?=E2=99=BB=20refactor:=20=EB=A7=A4?= =?UTF-8?q?=EB=AC=BC=20=EB=A9=94=EC=9D=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=ED=95=84=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../homes/homelist/FilterSidebar.vue | 137 ++++++++++++++++-- src/pages/homes/HomeListPage.vue | 24 ++- 2 files changed, 144 insertions(+), 17 deletions(-) diff --git a/src/components/homes/homelist/FilterSidebar.vue b/src/components/homes/homelist/FilterSidebar.vue index fa88bff..ea48c14 100644 --- a/src/components/homes/homelist/FilterSidebar.vue +++ b/src/components/homes/homelist/FilterSidebar.vue @@ -66,9 +66,10 @@ type="range" v-model="filters.depositRange" min="0" - max="50000" + max="30000" step="10" - class="w-full custom-range" + class="w-full custom-range-deposit" + ref="depositRangeInput" />
최대: {{ filters.depositRange }}만원
@@ -79,7 +80,8 @@ min="0" max="500" step="5" - class="w-full custom-range" + class="w-full custom-range-monthly" + ref="monthlyRangeInput" />
최대: {{ filters.monthlyRange }}만원
@@ -92,7 +94,8 @@ min="0" max="80000" step="10" - class="w-full custom-range" + class="w-full custom-range-lease" + ref="leaseRangeInput" />
최대: {{ filters.leaseRange }}만원
@@ -106,7 +109,8 @@ min="0" max="70" step="1" - class="w-full custom-range" + class="w-full custom-range-area" + ref="areaRangeInput" />
최대: {{ filters.area }}평
@@ -159,7 +163,7 @@ + + diff --git a/src/pages/homes/HomeListPage.vue b/src/pages/homes/HomeListPage.vue index b3e12ed..a223ca0 100644 --- a/src/pages/homes/HomeListPage.vue +++ b/src/pages/homes/HomeListPage.vue @@ -109,6 +109,7 @@ const otherCount = computed(() => { }) function onFilterChange(newFilters) { + // Directly use the payload received from the sidebar filters.value = { ...newFilters } page.value = 1 size.value = 21 @@ -146,14 +147,18 @@ async function loadListings() { : undefined, } - if (filters.value.dealType === '월세' && filters.value.depositRange > 0) { + // Handle lease-specific price filtering + if (filters.value.dealType === 'WOLSE' && filters.value.depositRange > 0) { params.maxDepositPrice = filters.value.depositRange * 10000 } - if (filters.value.dealType === '월세' && filters.value.monthlyRange > 0) { + if (filters.value.dealType === 'WOLSE' && filters.value.monthlyRange > 0) { params.maxMonthlyRent = filters.value.monthlyRange * 10000 } - if (filters.value.dealType === '전세' && filters.value.leaseRange > 0) { + if (filters.value.dealType === 'JEONSE' && filters.value.leaseRange > 0) { + // 전세는 보증금 필터를 사용하며, leaseRange 값을 maxDepositPrice에 매핑 params.maxDepositPrice = filters.value.leaseRange * 10000 + // 전세의 경우 월세 필터는 제외 + delete params.maxMonthlyRent } if (filters.value.direction) { @@ -179,11 +184,14 @@ async function loadListings() { } } - if (filters.value.conditions.includes('반려동물 가능')) { - params.isPet = true - } - if (filters.value.conditions.includes('주차 가능')) { - params.isParking = true + // 조건 필터링 + if (Array.isArray(filters.value.conditions)) { + if (filters.value.conditions.includes('반려동물 가능')) { + params.isPet = true + } + if (filters.value.conditions.includes('주차 가능')) { + params.isParking = true + } } const response = await fetchListings(params) From c494ffb9e3b93428fbaf943977d21ee78ed47b99 Mon Sep 17 00:00:00 2001 From: seonju21 Date: Sat, 16 Aug 2025 18:42:49 +0900 Subject: [PATCH 34/42] =?UTF-8?q?=E2=99=BB=20refactor:=20=EB=A7=A4?= =?UTF-8?q?=EB=AC=BC=20=EC=88=98=EC=A0=95=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=96=B4=EC=B9=B4=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/listing.js | 10 +- .../homes/homeupdate/BasicInfoForm.vue | 134 +++++++- .../homes/homeupdate/FacilityInfoForm.vue | 290 +++++++++--------- .../homes/homeupdate/ImageUploader.vue | 149 ++++----- .../homes/homeupdate/PriceInfoForm.vue | 250 ++++++++++----- .../homes/homeupdate/RoomInfoForm.vue | 245 +++++++++++---- src/pages/homes/HomeUpdatePage.vue | 170 +++++++--- 7 files changed, 806 insertions(+), 442 deletions(-) diff --git a/src/apis/listing.js b/src/apis/listing.js index 7d44f78..fe906ad 100644 --- a/src/apis/listing.js +++ b/src/apis/listing.js @@ -44,7 +44,6 @@ export async function fetchListingById(id) { } } -// apis/listing.js 파일의 createListing 함수 export async function createListing(listingData, images) { try { const formData = new FormData() @@ -88,10 +87,13 @@ export async function createListing(listingData, images) { } } -export async function updateListing(id, updatedData) { +// 수정된 updateListing 함수: createListing과 유사한 방식으로 FormData를 사용 +export async function updateListing(id, formData) { try { - const response = await api.put(`${API_BASE_URL}/${id}`, updatedData, { - headers: { 'Content-Type': 'application/json' }, + const response = await api.put(`${API_BASE_URL}/${id}`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, }) return response.data.data } catch (error) { diff --git a/src/components/homes/homeupdate/BasicInfoForm.vue b/src/components/homes/homeupdate/BasicInfoForm.vue index 7db2a9e..16bfade 100644 --- a/src/components/homes/homeupdate/BasicInfoForm.vue +++ b/src/components/homes/homeupdate/BasicInfoForm.vue @@ -1,27 +1,129 @@ diff --git a/src/components/homes/homeupdate/FacilityInfoForm.vue b/src/components/homes/homeupdate/FacilityInfoForm.vue index 8d2ea21..9b365b9 100644 --- a/src/components/homes/homeupdate/FacilityInfoForm.vue +++ b/src/components/homes/homeupdate/FacilityInfoForm.vue @@ -1,182 +1,172 @@ diff --git a/src/components/homes/homeupdate/ImageUploader.vue b/src/components/homes/homeupdate/ImageUploader.vue index b21f6a0..bc7110e 100644 --- a/src/components/homes/homeupdate/ImageUploader.vue +++ b/src/components/homes/homeupdate/ImageUploader.vue @@ -2,29 +2,39 @@

매물 이미지

-
- - +
+ 기존 이미지 + +
- - 업로드된 이미지 + class="relative w-24 h-24 rounded overflow-hidden border border-gray-300 shadow-sm" + > + 업로드된 이미지 + +
-
- - -
- - - -
- + diff --git a/src/components/homes/homeupdate/PriceInfoForm.vue b/src/components/homes/homeupdate/PriceInfoForm.vue index 7b4072f..34afd27 100644 --- a/src/components/homes/homeupdate/PriceInfoForm.vue +++ b/src/components/homes/homeupdate/PriceInfoForm.vue @@ -1,92 +1,194 @@ diff --git a/src/components/homes/homeupdate/RoomInfoForm.vue b/src/components/homes/homeupdate/RoomInfoForm.vue index 8108576..f9c2fd9 100644 --- a/src/components/homes/homeupdate/RoomInfoForm.vue +++ b/src/components/homes/homeupdate/RoomInfoForm.vue @@ -1,94 +1,205 @@ + + diff --git a/src/pages/homes/HomeUpdatePage.vue b/src/pages/homes/HomeUpdatePage.vue index ea1f3dc..73fcf8c 100644 --- a/src/pages/homes/HomeUpdatePage.vue +++ b/src/pages/homes/HomeUpdatePage.vue @@ -1,6 +1,5 @@ @@ -59,7 +78,6 @@ import PriceInfoForm from '@/components/homes/homeupdate/PriceInfoForm.vue' import RoomInfoForm from '@/components/homes/homeupdate/RoomInfoForm.vue' import FacilityInfoForm from '@/components/homes/homeupdate/FacilityInfoForm.vue' import ImageUploader from '@/components/homes/homeupdate/ImageUploader.vue' - import { fetchListingById, updateListing } from '@/apis/listing.js' const route = useRoute() @@ -69,13 +87,18 @@ const listingId = route.params.id const listing = ref({}) const originalListing = ref({}) const isLoading = ref(false) +const isSaving = ref(false) const error = ref(null) +const newImages = ref([]) +const deletedImageIds = ref([]) + onMounted(async () => { try { isLoading.value = true - listing.value = await fetchListingById(listingId) - originalListing.value = JSON.parse(JSON.stringify(listing.value)) + const response = await fetchListingById(listingId) + listing.value = response + originalListing.value = JSON.parse(JSON.stringify(response)) } catch (err) { error.value = '데이터를 불러오는 데 실패했습니다.' console.error('조회 실패:', err) @@ -84,17 +107,78 @@ onMounted(async () => { } }) +const handleImageDelete = (imageId) => { + const numericId = parseInt(imageId, 10) + if (!isNaN(numericId) && !deletedImageIds.value.includes(numericId)) { + deletedImageIds.value.push(numericId) + } +} + const updateListingData = async () => { try { - isLoading.value = true + isSaving.value = true error.value = null - await updateListing(listingId, listing.value) + + const formData = new FormData() + + // 백엔드 DTO에 존재하는 필드만 FormData에 개별적으로 추가 + const fieldsToAppend = { + addr1: listing.value.addr1, + addr2: listing.value.addr2, + residenceType: listing.value.residenceType, + leaseType: listing.value.leaseType, + depositPrice: listing.value.depositPrice ?? 0, + monthlyRent: listing.value.monthlyRent ?? 0, + maintenanceFee: listing.value.maintenanceFee ?? 0, + roomCnt: listing.value.roomCnt ?? 0, + supplyArea: listing.value.supplyArea ?? 0.0, + exclusiveArea: listing.value.exclusiveArea ?? 0.0, + buildDate: listing.value.buildDate, + homeFloor: listing.value.homeFloor ?? 0, + buildingTotalFloors: listing.value.buildingTotalFloors ?? 0, + homeDirection: listing.value.homeDirection, + bathroomCnt: listing.value.bathroomCnt ?? 0, + isPet: listing.value.isPet, + isParking: listing.value.isParking, + } + + // 모든 필드를 FormData에 직접 추가 + for (const key in fieldsToAppend) { + if (Object.prototype.hasOwnProperty.call(fieldsToAppend, key)) { + const value = fieldsToAppend[key] + if (value !== null && typeof value !== 'undefined') { + formData.append(key, value) + } + } + } + + // 배열 필드들을 명시적으로 FormData에 추가 + ;(listing.value.facilityItemIds || []).forEach((id) => { + formData.append('facilityItemIds', id) + }) + ;(listing.value.maintenanceFees || []).forEach((feeItem, index) => { + formData.append(`maintenanceFees[${index}].maintenanceId`, feeItem.maintenanceId) + formData.append(`maintenanceFees[${index}].fee`, feeItem.fee ?? 0) + }) + ;(deletedImageIds.value || []).forEach((id) => { + formData.append('deleteImageIds', id) + }) + + // 파일 필드 추가 + if (newImages.value && newImages.value.length > 0) { + newImages.value.forEach((img) => { + formData.append('newImages', img.file) + }) + } + + await updateListing(listingId, formData) router.push('/homes') } catch (err) { - error.value = '저장 중 오류가 발생했습니다.' + const errorMessage = err.response?.data?.message || '저장 중 오류가 발생했습니다.' + error.value = errorMessage console.error('저장 실패:', err) } finally { - isLoading.value = false + isSaving.value = false } } From 0f187a4a8ca6ea6d35820aa62c3853d83fb2653f Mon Sep 17 00:00:00 2001 From: seonju21 Date: Sat, 16 Aug 2025 19:45:27 +0900 Subject: [PATCH 35/42] =?UTF-8?q?=E2=99=BB=20refactor:=20=EB=A7=A4?= =?UTF-8?q?=EB=AC=BC=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=AA=A8=EB=B0=94?= =?UTF-8?q?=EC=9D=BC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/icons/PetA.vue | 29 ++++ .../homes/homecreate/Step1BasicInfo.vue | 138 +++++++++--------- .../homes/homecreate/Step2PriceInfo.vue | 4 +- .../homes/homecreate/Step3DetailInfo.vue | 16 +- .../homecreate/StepProgressIndicator.vue | 15 +- .../homes/homedetails/RoomDetails.vue | 20 +-- .../homes/homelist/FilterSidebar.vue | 66 ++++++--- src/pages/homes/HomeCreatePage.vue | 3 +- src/pages/homes/HomeDetailsPage.vue | 4 +- src/pages/homes/HomeListPage.vue | 66 +++++++-- 10 files changed, 229 insertions(+), 132 deletions(-) create mode 100644 src/assets/icons/PetA.vue diff --git a/src/assets/icons/PetA.vue b/src/assets/icons/PetA.vue new file mode 100644 index 0000000..8066c9a --- /dev/null +++ b/src/assets/icons/PetA.vue @@ -0,0 +1,29 @@ + + + diff --git a/src/components/homes/homecreate/Step1BasicInfo.vue b/src/components/homes/homecreate/Step1BasicInfo.vue index d18919c..a445a2e 100644 --- a/src/components/homes/homecreate/Step1BasicInfo.vue +++ b/src/components/homes/homecreate/Step1BasicInfo.vue @@ -1,69 +1,3 @@ - - + + diff --git a/src/components/homes/homecreate/Step2PriceInfo.vue b/src/components/homes/homecreate/Step2PriceInfo.vue index cd9bf52..290af5a 100644 --- a/src/components/homes/homecreate/Step2PriceInfo.vue +++ b/src/components/homes/homecreate/Step2PriceInfo.vue @@ -42,7 +42,7 @@

관리비 포함 항목

-
+

관리비 포함 항목

-
+
-
+

상세 정보

전용면적과 건물 정보를 입력해주세요.

-
+
-
+
- +
-
+
-
+
@@ -145,7 +145,7 @@
방향 -
+