From 16a43aee69aaa774ec77bb07ba1d6106e2a122b3 Mon Sep 17 00:00:00 2001 From: hun Date: Tue, 10 Feb 2026 16:32:36 +0900 Subject: [PATCH 01/14] =?UTF-8?q?comment:=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/devices/DeviceSearchPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/devices/DeviceSearchPage.tsx b/src/pages/devices/DeviceSearchPage.tsx index 512420e..5b88f66 100644 --- a/src/pages/devices/DeviceSearchPage.tsx +++ b/src/pages/devices/DeviceSearchPage.tsx @@ -222,10 +222,10 @@ const DeviceSearchPage = () => { setShowSaveCompleteModal(false); setIsFadingOut(false); handleCloseModal(); - }, 200); // 0.2초 + }, 200); return () => clearTimeout(closeTimer); - }, 800); // 0.8초 + }, 800); return () => clearTimeout(holdTimer); } From 627351319632b07cb8e9e01abb1ef496e87269e8 Mon Sep 17 00:00:00 2001 From: hun Date: Wed, 11 Feb 2026 00:03:50 +0900 Subject: [PATCH 02/14] =?UTF-8?q?feat:=20=EA=B8=B0=EA=B8=B0=EA=B2=80?= =?UTF-8?q?=EC=83=89=20API=20=EC=97=B0=EB=8F=99=20(=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EC=9D=BC=EB=B6=80=20=EB=82=A8=EC=95=84?= =?UTF-8?q?=EC=9E=88=EC=9D=8C=20)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/devices/searchDevices.ts | 34 ++++++ src/components/ProductCard/ProductCard.tsx | 6 +- src/constants/queryKey.ts | 1 + src/hooks/useIntersectionObserver.ts | 38 +++++++ src/pages/devices/DeviceSearchPage.tsx | 125 +++++++++++++++++---- src/types/devices.ts | 36 ++++++ 6 files changed, 218 insertions(+), 22 deletions(-) create mode 100644 src/apis/devices/searchDevices.ts create mode 100644 src/hooks/useIntersectionObserver.ts diff --git a/src/apis/devices/searchDevices.ts b/src/apis/devices/searchDevices.ts new file mode 100644 index 0000000..b873558 --- /dev/null +++ b/src/apis/devices/searchDevices.ts @@ -0,0 +1,34 @@ +import { axiosInstance } from '@/apis/axios/axios'; +import type { + SearchDevicesParams, + GetDevicesSearchResponse, + DeviceSearchResult, +} from '@/types/devices'; +import { useInfiniteQuery } from '@tanstack/react-query'; +import { queryKey } from '@/constants/queryKey'; + +export const searchDevices = async ( + params: SearchDevicesParams +): Promise => { + const { data } = await axiosInstance.get( + '/api/devices/search', + { params } + ); + return data.result ?? { devices: [], nextCursor: null, hasNext: false }; +}; + +export const useSearchDevices = (params: Omit) => { + return useInfiniteQuery({ + queryKey: [queryKey.DEVICE_SEARCH, params], + queryFn: ({ pageParam }) => + searchDevices({ + ...params, + cursor: pageParam as string | undefined, + }), + initialPageParam: undefined as string | undefined, + getNextPageParam: (lastPage) => + lastPage?.hasNext ? lastPage.nextCursor : undefined, + enabled: true, + staleTime: 1000 * 60 * 5, + }); +}; diff --git a/src/components/ProductCard/ProductCard.tsx b/src/components/ProductCard/ProductCard.tsx index 4e79f2b..837f057 100644 --- a/src/components/ProductCard/ProductCard.tsx +++ b/src/components/ProductCard/ProductCard.tsx @@ -18,13 +18,15 @@ const ProductCard: React.FC = ({ product, onClick }) => {
{/* Name & Category */}
-

{product.name}

+

+ {product.name.length > 24 ? `${product.name.slice(0, 24)}...` : product.name} +

{product.category}

{/* Price */}

- {product.price.toLocaleString()} + {(product.price ?? 0).toLocaleString()}

{/* Color Chips */} diff --git a/src/constants/queryKey.ts b/src/constants/queryKey.ts index 46b3bec..97243d3 100644 --- a/src/constants/queryKey.ts +++ b/src/constants/queryKey.ts @@ -10,4 +10,5 @@ export const queryKey = { LIFESTYLE_DEVICE: 'lifestyle_device', BRANDS: 'brands', RECENTLY_VIEWED: 'recently_viewed', + DEVICE_SEARCH: 'device_search', } as const; diff --git a/src/hooks/useIntersectionObserver.ts b/src/hooks/useIntersectionObserver.ts new file mode 100644 index 0000000..b2c78c0 --- /dev/null +++ b/src/hooks/useIntersectionObserver.ts @@ -0,0 +1,38 @@ +import { useEffect, useRef, useState } from 'react'; + +interface UseIntersectionObserverOptions { + root?: Element | null; + rootMargin?: string; + threshold?: number | number[]; +} + +export const useIntersectionObserver = ( + options: UseIntersectionObserverOptions = {} +) => { + const [isIntersecting, setIsIntersecting] = useState(false); + const targetRef = useRef(null); + + useEffect(() => { + const target = targetRef.current; + if (!target) return; + + const observer = new IntersectionObserver( + ([entry]) => { + setIsIntersecting(entry.isIntersecting); + }, + { + root: options.root ?? null, + rootMargin: options.rootMargin ?? '0px', + threshold: options.threshold ?? 0, + } + ); + + observer.observe(target); + + return () => { + observer.disconnect(); + }; + }, [options.root, options.rootMargin, options.threshold]); + + return { targetRef, isIntersecting }; +}; diff --git a/src/pages/devices/DeviceSearchPage.tsx b/src/pages/devices/DeviceSearchPage.tsx index 5b88f66..4ba66fb 100644 --- a/src/pages/devices/DeviceSearchPage.tsx +++ b/src/pages/devices/DeviceSearchPage.tsx @@ -22,9 +22,10 @@ import { PRICE_OPTIONS, SCROLL_CONSTANTS, } from '@/constants/devices'; -import { MOCK_PRODUCTS } from '@/constants/mockData'; import { ROUTES } from '@/constants/routes'; -import { type ModalView } from '@/types/devices'; +import { type ModalView, type SearchDevice } from '@/types/devices'; +import { useSearchDevices } from '@/apis/devices/searchDevices'; +import { useIntersectionObserver } from '@/hooks/useIntersectionObserver'; import { useGetCombos } from '@/apis/combo/getCombos'; import { useGetCombo } from '@/apis/combo/getComboId'; import { usePostComboDevice } from '@/apis/combo/postComboDevices'; @@ -48,6 +49,27 @@ const getCategoryDeviceType = (categoryId: number | null): string | undefined => return mapping[categoryId]; }; +// sortOption을 API sortType으로 변환 +const getSortType = (sortOption: string) => { + const mapping: Record = { + 'latest': 'LATEST', + 'alphabetical': 'NAME_ASC', + 'price-low': 'PRICE_ASC', + 'price-high': 'PRICE_DESC', + }; + return mapping[sortOption] ?? 'LATEST'; +}; + +// SearchDevice를 Product 형식으로 변환 +const mapSearchDeviceToProduct = (device: SearchDevice) => ({ + id: device.deviceId, + name: `${device.brandName ?? ''} ${device.name ?? ''}`.trim(), + category: device.deviceType ?? '', + price: device.price ?? 0, + image: device.imageUrl ?? null, + colors: [] as string[], +}); + const DeviceSearchPage = () => { const [searchParams, setSearchParams] = useSearchParams(); const selectedProductId = searchParams.get('productId'); @@ -97,6 +119,44 @@ const DeviceSearchPage = () => { })); }, [brandsData]); + // 기기 검색 API 파라미터 구성 + const apiSearchParams = useMemo(() => { + const deviceType = getCategoryDeviceType(selectedCategory); + return { + keyword: searchQuery || undefined, + size: 24, + sortType: getSortType(sortOption), + deviceTypes: deviceType ? [deviceType] : undefined, + brandIds: selectedBrand ? [Number(selectedBrand)] : undefined, + }; + }, [searchQuery, selectedCategory, sortOption, selectedBrand]); + + // 기기 검색 API 호출 + const { + data: searchData, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + isLoading: isSearchLoading, + isError: isSearchError, + } = useSearchDevices(apiSearchParams); + + // 전체 기기 목록 (모든 페이지 결합) + const allDevices = useMemo(() => + searchData?.pages.flatMap(page => page.devices) ?? [], + [searchData] + ); + + // 무한 스크롤 트리거 + const { targetRef, isIntersecting } = useIntersectionObserver({ rootMargin: '100px' }); + + // 스크롤 감지 시 다음 페이지 로드 + useEffect(() => { + if (isIntersecting && hasNextPage && !isFetchingNextPage) { + fetchNextPage(); + } + }, [isIntersecting, hasNextPage, isFetchingNextPage, fetchNextPage]); + // 페이지 마운트 시 상단으로 스크롤 useEffect(() => { window.scrollTo(0, 0); @@ -108,9 +168,10 @@ const DeviceSearchPage = () => { }, [selectedCategory]); /* 선택된 제품 찾기 */ - const selectedProduct = selectedProductId - ? MOCK_PRODUCTS.find(p => p.id === Number(selectedProductId)) + const selectedDevice = selectedProductId + ? allDevices.find(d => d.deviceId === Number(selectedProductId)) : null; + const selectedProduct = selectedDevice ? mapSearchDeviceToProduct(selectedDevice) : null; /* 모달 닫기 */ const handleCloseModal = () => { @@ -372,7 +433,7 @@ const DeviceSearchPage = () => {
{/* Left side - Result count */}
-

40

+

{allDevices.length}

개 결과

@@ -387,18 +448,42 @@ const DeviceSearchPage = () => { {/* Product Grid */}
-
- {MOCK_PRODUCTS.map((product) => ( - { - searchParams.set('productId', product.id.toString()); - setSearchParams(searchParams); - }} - /> - ))} -
+ {isSearchLoading ? ( +
+

로딩 중...

+
+ ) : isSearchError ? ( +
+

검색 결과를 불러오는데 실패했습니다.

+
+ ) : allDevices.length === 0 ? ( +
+

검색 결과가 없습니다.

+
+ ) : ( +
+ {allDevices.map((device) => ( + { + searchParams.set('productId', device.deviceId.toString()); + setSearchParams(searchParams); + }} + /> + ))} +
+ )} + + {/* 무한 스크롤 트리거 */} +
+ + {/* 로딩 인디케이터 */} + {isFetchingNextPage && ( +
+

더 불러오는 중...

+
+ )}
{/* Top Button - 3행이 보일 때만 표시 */} @@ -457,7 +542,7 @@ const DeviceSearchPage = () => {

{selectedProduct.name}

-

{selectedProduct.price.toLocaleString()}

+

{(selectedProduct.price ?? 0).toLocaleString()}

@@ -482,7 +567,7 @@ const DeviceSearchPage = () => {

브랜드

-

Apple

+

{selectedDevice?.brandName ?? '-'}

색상

@@ -491,7 +576,7 @@ const DeviceSearchPage = () => {

가격

-

{selectedProduct.price.toLocaleString()}

+

{(selectedProduct.price ?? 0).toLocaleString()}

diff --git a/src/types/devices.ts b/src/types/devices.ts index 2fb9252..d6564be 100644 --- a/src/types/devices.ts +++ b/src/types/devices.ts @@ -1,4 +1,5 @@ import { type CombinationName, type CombinationStatus } from '@/constants/combination'; +import type { CommonResponse } from '@/types/common'; export type AuthStatus = 'logout' | 'login'; export type ModalView = 'device' | 'combination' | 'combinationDetail'; @@ -27,3 +28,38 @@ export type UserCombination = { createdAt?: string; tags: CombinationTagType[]; }; + +// 기기 검색 API 파라미터 +export interface SearchDevicesParams { + keyword?: string; + cursor?: string; + size?: number; + sortType?: 'LATEST' | 'NAME_ASC' | 'PRICE_ASC' | 'PRICE_DESC'; + deviceTypes?: string[]; + minPrice?: number; + maxPrice?: number; + brandIds?: number[]; +} + +// 검색 결과 기기 +export interface SearchDevice { + deviceId: number; + deviceType: string; + brandName: string; + name: string; + price: number; + priceCurrency: string; + imageUrl: string; + releaseDate: string; + specifications: Record; +} + +// 검색 결과 +export interface DeviceSearchResult { + devices: SearchDevice[]; + nextCursor: string | null; + hasNext: boolean; +} + +// API 응답 +export type GetDevicesSearchResponse = CommonResponse; From 5d0d042908ef9d462a82de61f54f98f3ff783630 Mon Sep 17 00:00:00 2001 From: hun Date: Wed, 11 Feb 2026 20:19:03 +0900 Subject: [PATCH 03/14] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=ED=95=A0=20?= =?UTF-8?q?=EB=95=8C=20=EC=83=88=20=EB=8D=B0=EC=9D=B4=ED=84=B0=EA=B0=80=20?= =?UTF-8?q?=EC=A4=80=EB=B9=84=EB=90=98=EB=A9=B4=20=EB=A1=9C=EB=94=A9?= =?UTF-8?q?=EB=90=98=EB=8F=84=EB=A1=9D=20=EA=B5=AC=ED=98=84,=20(=EA=B9=9C?= =?UTF-8?q?=EB=B9=A1=EA=B1=B0=EB=A6=BC=20=20=ED=95=B4=EA=B2=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ProductCard/ProductCard.tsx | 2 +- src/pages/devices/DeviceSearchPage.tsx | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/ProductCard/ProductCard.tsx b/src/components/ProductCard/ProductCard.tsx index 837f057..dada95d 100644 --- a/src/components/ProductCard/ProductCard.tsx +++ b/src/components/ProductCard/ProductCard.tsx @@ -19,7 +19,7 @@ const ProductCard: React.FC = ({ product, onClick }) => { {/* Name & Category */}

- {product.name.length > 24 ? `${product.name.slice(0, 24)}...` : product.name} + {product.name.length > 21 ? `${product.name.slice(0, 21)}...` : product.name}

{product.category}

diff --git a/src/pages/devices/DeviceSearchPage.tsx b/src/pages/devices/DeviceSearchPage.tsx index 4ba66fb..2052989 100644 --- a/src/pages/devices/DeviceSearchPage.tsx +++ b/src/pages/devices/DeviceSearchPage.tsx @@ -448,11 +448,12 @@ const DeviceSearchPage = () => { {/* Product Grid */}
- {isSearchLoading ? ( + {/* 초기 로딩: 데이터가 없고 로딩 중일 때만 로딩 메시지 표시 */} + {isSearchLoading && allDevices.length === 0 ? (

로딩 중...

- ) : isSearchError ? ( + ) : isSearchError && allDevices.length === 0 ? (

검색 결과를 불러오는데 실패했습니다.

From 861fea57cea3c1b170dcae9b88d36bcaa15b9852 Mon Sep 17 00:00:00 2001 From: hun Date: Wed, 11 Feb 2026 20:21:44 +0900 Subject: [PATCH 04/14] =?UTF-8?q?feat:=20=EB=B8=8C=EB=9E=9C=EB=93=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EC=A4=91=EB=B3=B5=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/devices/DeviceSearchPage.tsx | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/pages/devices/DeviceSearchPage.tsx b/src/pages/devices/DeviceSearchPage.tsx index 2052989..87b31da 100644 --- a/src/pages/devices/DeviceSearchPage.tsx +++ b/src/pages/devices/DeviceSearchPage.tsx @@ -61,14 +61,24 @@ const getSortType = (sortOption: string) => { }; // SearchDevice를 Product 형식으로 변환 -const mapSearchDeviceToProduct = (device: SearchDevice) => ({ - id: device.deviceId, - name: `${device.brandName ?? ''} ${device.name ?? ''}`.trim(), - category: device.deviceType ?? '', - price: device.price ?? 0, - image: device.imageUrl ?? null, - colors: [] as string[], -}); +const mapSearchDeviceToProduct = (device: SearchDevice) => { + const brandName = device.brandName ?? ''; + const deviceName = device.name ?? ''; + + // device.name이 이미 brandName으로 시작하면 중복 방지 + const fullName = deviceName.startsWith(brandName) + ? deviceName + : `${brandName} ${deviceName}`.trim(); + + return { + id: device.deviceId, + name: fullName, + category: device.deviceType ?? '', + price: device.price ?? 0, + image: device.imageUrl ?? null, + colors: [] as string[], + }; +}; const DeviceSearchPage = () => { const [searchParams, setSearchParams] = useSearchParams(); From 866978c12d761528f0835d9c7d32e035c909880c Mon Sep 17 00:00:00 2001 From: hun Date: Wed, 11 Feb 2026 20:23:38 +0900 Subject: [PATCH 05/14] =?UTF-8?q?feat:=20=EC=A0=9C=ED=92=88=20=EC=82=AC?= =?UTF-8?q?=EC=A7=84=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ProductCard/ProductCard.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/ProductCard/ProductCard.tsx b/src/components/ProductCard/ProductCard.tsx index dada95d..78aae38 100644 --- a/src/components/ProductCard/ProductCard.tsx +++ b/src/components/ProductCard/ProductCard.tsx @@ -12,7 +12,15 @@ const ProductCard: React.FC = ({ product, onClick }) => { onClick={onClick} > {/* Image - 정사각형 */} -
+
+ {product.image ? ( + {product.name} + ) : null} +
{/* Content */}
From 944eca77a06a9e8de8b7f8bd92c995f46154d406 Mon Sep 17 00:00:00 2001 From: hun Date: Wed, 11 Feb 2026 21:47:59 +0900 Subject: [PATCH 06/14] =?UTF-8?q?fix:=20=EC=B6=A9=EC=A0=84=EA=B8=B0=20(=20?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B8=ED=95=B4=20=EB=84=88=EB=B9=84=EA=B0=80=20?= =?UTF-8?q?=EB=84=93=EA=B2=8C=20=EC=9E=A1=ED=98=80=EC=84=9C,=2019=EB=A1=9C?= =?UTF-8?q?=20=EC=A4=84=EC=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ProductCard/ProductCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ProductCard/ProductCard.tsx b/src/components/ProductCard/ProductCard.tsx index 78aae38..0d3d5e8 100644 --- a/src/components/ProductCard/ProductCard.tsx +++ b/src/components/ProductCard/ProductCard.tsx @@ -27,7 +27,7 @@ const ProductCard: React.FC = ({ product, onClick }) => { {/* Name & Category */}

- {product.name.length > 21 ? `${product.name.slice(0, 21)}...` : product.name} + {product.name.length > 19 ? `${product.name.slice(0, 19)}...` : product.name}

{product.category}

From 9b4318810cf97c4beb701855060ef3ad659620b9 Mon Sep 17 00:00:00 2001 From: hun Date: Wed, 11 Feb 2026 22:05:15 +0900 Subject: [PATCH 07/14] =?UTF-8?q?design:=20RootLayout=EA=B3=BC=EC=9D=98=20?= =?UTF-8?q?css=20=EB=AC=B8=EC=A0=9C=EB=A5=BC=20=EB=B7=B0=ED=8F=AC=ED=8A=B8?= =?UTF-8?q?=20=EC=9D=B4=EC=9A=A9=ED=95=98=EC=97=AC=20=EB=B0=98=EC=9D=91?= =?UTF-8?q?=ED=98=95=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/devices/DeviceSearchPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/devices/DeviceSearchPage.tsx b/src/pages/devices/DeviceSearchPage.tsx index 87b31da..7ce757c 100644 --- a/src/pages/devices/DeviceSearchPage.tsx +++ b/src/pages/devices/DeviceSearchPage.tsx @@ -358,7 +358,7 @@ const DeviceSearchPage = () => { }; return ( -
+
{/* Main Content */} From f06c1527e3665830c095a9c373850c8d86d439a2 Mon Sep 17 00:00:00 2001 From: hun Date: Wed, 11 Feb 2026 22:08:04 +0900 Subject: [PATCH 08/14] =?UTF-8?q?design:=20spinner=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/devices/DeviceSearchPage.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/pages/devices/DeviceSearchPage.tsx b/src/pages/devices/DeviceSearchPage.tsx index 7ce757c..4a789b7 100644 --- a/src/pages/devices/DeviceSearchPage.tsx +++ b/src/pages/devices/DeviceSearchPage.tsx @@ -7,6 +7,7 @@ import CombinationDeviceCard from '@/components/Combination/CombinationDeviceCar import ProductLife from '@/components/ProductCard/ProductLife'; import FilterDropdown from '@/components/Filter/FilterDropdown'; import SortDropdown from '@/components/Filter/SortDropdown'; +import LoadingSpinner from '@/components/LoadingSpinner'; import SearchIcon from '@/assets/icons/search.svg?react'; import FilterIcon from '@/assets/icons/filter.svg?react'; import TopIcon from '@/assets/icons/top.svg?react'; @@ -458,11 +459,9 @@ const DeviceSearchPage = () => { {/* Product Grid */}
- {/* 초기 로딩: 데이터가 없고 로딩 중일 때만 로딩 메시지 표시 */} + {/* 초기 로딩: 데이터가 없고 로딩 중일 때만 로딩 스피너 표시 */} {isSearchLoading && allDevices.length === 0 ? ( -
-

로딩 중...

-
+ ) : isSearchError && allDevices.length === 0 ? (

검색 결과를 불러오는데 실패했습니다.

@@ -492,7 +491,7 @@ const DeviceSearchPage = () => { {/* 로딩 인디케이터 */} {isFetchingNextPage && (
-

더 불러오는 중...

+
)}
From 7fad2c3dc0fcc34dde3d205a6193210bedba3230 Mon Sep 17 00:00:00 2001 From: hun Date: Wed, 11 Feb 2026 22:33:41 +0900 Subject: [PATCH 09/14] =?UTF-8?q?feat:=20=EA=B8=B0=EA=B8=B0=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=EB=B3=B4=EA=B8=B0=20=EC=8A=A4=ED=8E=99:=20=EC=83=89?= =?UTF-8?q?=EC=83=81=20=EC=82=AD=EC=A0=9C,=20=EC=B6=A9=EC=A0=84=EB=B0=A9?= =?UTF-8?q?=EC=8B=9D=20=EC=B6=9C=EC=8B=9C=EC=9D=BC=20API=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/devices/DeviceSearchPage.tsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/pages/devices/DeviceSearchPage.tsx b/src/pages/devices/DeviceSearchPage.tsx index 4a789b7..4281423 100644 --- a/src/pages/devices/DeviceSearchPage.tsx +++ b/src/pages/devices/DeviceSearchPage.tsx @@ -491,7 +491,7 @@ const DeviceSearchPage = () => { {/* 로딩 인디케이터 */} {isFetchingNextPage && (
- +

더 불러오는 중...

)}
@@ -579,10 +579,6 @@ const DeviceSearchPage = () => {

브랜드

{selectedDevice?.brandName ?? '-'}

-
-

색상

-

내추럴 티타늄

-

가격

@@ -592,11 +588,19 @@ const DeviceSearchPage = () => {

충전방식

-

USB-C

+

+ {selectedDevice?.specifications?.chargingPort + ? String(selectedDevice.specifications.chargingPort).replace('_', '-') + : '-'} +

출시일

-

2023년 9월

+

+ {selectedDevice?.releaseDate + ? new Date(selectedDevice.releaseDate).toLocaleDateString('ko-KR', { year: 'numeric', month: 'long' }) + : '-'} +

From 49d71d219dc55c8974fe2b1e842bc917c895d217 Mon Sep 17 00:00:00 2001 From: hun Date: Wed, 11 Feb 2026 23:01:39 +0900 Subject: [PATCH 10/14] =?UTF-8?q?fix:=20=EA=B8=B0=EA=B8=B0=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=EB=AA=A8=EB=8B=AC=EC=97=90=EC=84=9C=20=EC=82=AC?= =?UTF-8?q?=EC=A7=84=20=EB=B3=B4=EC=9D=B4=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/devices/DeviceSearchPage.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/pages/devices/DeviceSearchPage.tsx b/src/pages/devices/DeviceSearchPage.tsx index 4281423..8848db0 100644 --- a/src/pages/devices/DeviceSearchPage.tsx +++ b/src/pages/devices/DeviceSearchPage.tsx @@ -561,6 +561,13 @@ const DeviceSearchPage = () => {
{/* Image */}
+ {selectedProduct.image ? ( + {selectedProduct.name} + ) : null}
{/* Right Section - Specs */} From b488c98ade2f5d941ce38ec0d5c26a13b4dff5d1 Mon Sep 17 00:00:00 2001 From: hun Date: Wed, 11 Feb 2026 23:09:10 +0900 Subject: [PATCH 11/14] =?UTF-8?q?design:=20=EA=B8=B0=EA=B8=B0=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=EB=B3=B4=EA=B8=B0=20=EB=AA=A8=EB=8B=AC=20=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=ED=94=84=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/devices/DeviceSearchPage.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/pages/devices/DeviceSearchPage.tsx b/src/pages/devices/DeviceSearchPage.tsx index 8848db0..3a7bf8a 100644 --- a/src/pages/devices/DeviceSearchPage.tsx +++ b/src/pages/devices/DeviceSearchPage.tsx @@ -611,11 +611,6 @@ const DeviceSearchPage = () => {
- {/* Hashtags */} -
- - -
From f9e5ff7402a35cef446b83c0c7d1b469692ea49f Mon Sep 17 00:00:00 2001 From: hun Date: Wed, 11 Feb 2026 23:11:45 +0900 Subject: [PATCH 12/14] =?UTF-8?q?feat:=20=EA=B8=B0=EA=B8=B0=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=EB=B3=B4=EA=B8=B0=20=EC=8A=A4=ED=8E=99=20=EB=84=98?= =?UTF-8?q?=EC=96=B4=EC=98=A4=EC=A7=80=20=EC=95=8A=EB=8A=94=20=ED=96=89=20?= =?UTF-8?q?hidden?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/devices/DeviceSearchPage.tsx | 42 ++++++++++++++------------ 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/pages/devices/DeviceSearchPage.tsx b/src/pages/devices/DeviceSearchPage.tsx index 3a7bf8a..6165e8d 100644 --- a/src/pages/devices/DeviceSearchPage.tsx +++ b/src/pages/devices/DeviceSearchPage.tsx @@ -582,10 +582,12 @@ const DeviceSearchPage = () => {

카테고리

{selectedProduct.category}

-
-

브랜드

-

{selectedDevice?.brandName ?? '-'}

-
+ {selectedDevice?.brandName && ( +
+

브랜드

+

{selectedDevice.brandName}

+
+ )}

가격

@@ -593,22 +595,22 @@ const DeviceSearchPage = () => {

-
-

충전방식

-

- {selectedDevice?.specifications?.chargingPort - ? String(selectedDevice.specifications.chargingPort).replace('_', '-') - : '-'} -

-
-
-

출시일

-

- {selectedDevice?.releaseDate - ? new Date(selectedDevice.releaseDate).toLocaleDateString('ko-KR', { year: 'numeric', month: 'long' }) - : '-'} -

-
+ {selectedDevice?.specifications?.chargingPort && ( +
+

충전방식

+

+ {String(selectedDevice.specifications.chargingPort).replace('_', '-')} +

+
+ )} + {selectedDevice?.releaseDate && ( +
+

출시일

+

+ {new Date(selectedDevice.releaseDate).toLocaleDateString('ko-KR', { year: 'numeric', month: 'long' })} +

+
+ )}
From f2395dfe73b0fa2eb1701838eb6859a3b1e9c547 Mon Sep 17 00:00:00 2001 From: hun Date: Wed, 11 Feb 2026 23:18:06 +0900 Subject: [PATCH 13/14] =?UTF-8?q?feat:=20=EA=B8=B0=EA=B8=B0=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=EB=B3=B4=EA=B8=B0=20=EC=8A=A4=ED=8E=99=20=EC=9D=B8?= =?UTF-8?q?=EC=B9=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/devices/DeviceSearchPage.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/pages/devices/DeviceSearchPage.tsx b/src/pages/devices/DeviceSearchPage.tsx index 6165e8d..c55d7e4 100644 --- a/src/pages/devices/DeviceSearchPage.tsx +++ b/src/pages/devices/DeviceSearchPage.tsx @@ -595,6 +595,14 @@ const DeviceSearchPage = () => {

+ {selectedDevice?.specifications?.screenInch && ( +
+

인치

+

+ {selectedDevice.specifications.screenInch} +

+
+ )} {selectedDevice?.specifications?.chargingPort && (

충전방식

From 7e9e42ef37ac0cada532673d8a38357304e47434 Mon Sep 17 00:00:00 2001 From: hun Date: Wed, 11 Feb 2026 23:21:26 +0900 Subject: [PATCH 14/14] =?UTF-8?q?fix:=20=EB=B9=8C=EB=93=9C=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/devices/DeviceSearchPage.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/pages/devices/DeviceSearchPage.tsx b/src/pages/devices/DeviceSearchPage.tsx index c55d7e4..ed978b6 100644 --- a/src/pages/devices/DeviceSearchPage.tsx +++ b/src/pages/devices/DeviceSearchPage.tsx @@ -4,7 +4,6 @@ import GNB from '@/components/Home/GNB'; import ProductCard from '@/components/ProductCard/ProductCard'; import PrimaryButton from '@/components/Button/PrimaryButton'; import CombinationDeviceCard from '@/components/Combination/CombinationDeviceCard'; -import ProductLife from '@/components/ProductCard/ProductLife'; import FilterDropdown from '@/components/Filter/FilterDropdown'; import SortDropdown from '@/components/Filter/SortDropdown'; import LoadingSpinner from '@/components/LoadingSpinner'; @@ -595,22 +594,22 @@ const DeviceSearchPage = () => {

- {selectedDevice?.specifications?.screenInch && ( + {selectedDevice?.specifications?.screenInch ? (

인치

- {selectedDevice.specifications.screenInch} + {String(selectedDevice.specifications.screenInch)}

- )} - {selectedDevice?.specifications?.chargingPort && ( + ) : null} + {selectedDevice?.specifications?.chargingPort ? (

충전방식

{String(selectedDevice.specifications.chargingPort).replace('_', '-')}

- )} + ) : null} {selectedDevice?.releaseDate && (

출시일