Skip to content

Commit eddd447

Browse files
authored
Merge pull request #91 from EasyToFind-ETF/dev
5차 배포
2 parents 31c4fb7 + 22188fc commit eddd447

File tree

6 files changed

+345
-234
lines changed

6 files changed

+345
-234
lines changed

src/app/(pages)/find/page.tsx

Lines changed: 163 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
toggleFavorite as toggleFavoriteAPI,
99
fetchFavoriteEtfCodes,
1010
} from "@/services/etfFavoriteService";
11-
import { useRouter } from "next/navigation";
11+
import { useRouter, useSearchParams } from "next/navigation";
1212

1313
import FilterTabs from "@/components/blocks/ETFFind/FilterTabs";
1414
import FilterButtons from "@/components/blocks/ETFFind/FilterButtons";
@@ -18,10 +18,14 @@ import HoldingTable from "@/components/blocks/ETFFind/HoldingTable";
1818
import CompareModal from "@/components/blocks/ETFCompare/ETFComapreModal";
1919

2020
export default function FindPage() {
21+
const router = useRouter();
22+
const searchParams = useSearchParams();
23+
const initialQuery = searchParams.get("query") ?? "";
24+
2125
const [selectedTab, setSelectedTab] = useState("유형별");
2226
const [selectedType, setSelectedType] = useState("전체");
2327
const [selectedTheme, setSelectedTheme] = useState("전체");
24-
const [searchQuery, setSearchQuery] = useState("");
28+
const [searchQuery, setSearchQuery] = useState(initialQuery);
2529

2630
const [etfData, setEtfData] = useState<ETFView[]>([]);
2731
const [holdingsData, setHoldingsData] = useState<HoldingView[]>([]);
@@ -32,7 +36,8 @@ export default function FindPage() {
3236
const [modalVisible, setModalVisible] = useState(false);
3337
const [modalData, setModalData] = useState<any[]>([]);
3438
const [showPlaceholder, setShowPlaceholder] = useState(true);
35-
const router = useRouter();
39+
const [hasInitialized, setHasInitialized] = useState(false);
40+
3641
const tabList = ["유형별", "테마별", "관심별"];
3742
const assetFilters = [
3843
"전체",
@@ -117,9 +122,134 @@ export default function FindPage() {
117122
}
118123
};
119124

125+
// 검색 실행 함수 (URL 파라미터용)
126+
const executeSearchWithQuery = async (query: string) => {
127+
setIsLoading(true);
128+
129+
const params: any = {
130+
query: query,
131+
sort: viewMode === "ETF로 보기" ? "etf_code" : "weight_pct",
132+
};
133+
134+
if (selectedTab === "관심별") {
135+
params.isFavorite = true;
136+
} else {
137+
if (selectedType !== "전체") params.assetClass = selectedType;
138+
if (selectedTheme !== "전체") params.theme = selectedTheme;
139+
}
140+
141+
try {
142+
if (viewMode === "ETF로 보기") {
143+
const data: any[] = await fetchEtfData(params);
144+
const formatted = (data as any[]).map((etf) => ({
145+
name: etf.etf_name,
146+
etfCode: etf.etf_code,
147+
nav: etf.latest_price,
148+
week1: etf.week1 ?? "-",
149+
month1: etf.month1 ?? "-",
150+
month3: etf.month3 ?? "-",
151+
month6: etf.month6 ?? "-",
152+
year1: etf.year1 ?? "-",
153+
year3: etf.year3 ?? "-",
154+
inception: etf.inception ?? "-",
155+
}));
156+
setEtfData(formatted);
157+
158+
// 관심 ETF 세팅
159+
try {
160+
const favoriteCodes = await fetchFavoriteEtfCodes();
161+
setFavoriteEtfCodes(favoriteCodes);
162+
} catch (err: any) {
163+
console.warn(
164+
"💥 관심 ETF 목록 가져오기 실패 (비로그인일 수 있음)",
165+
err
166+
);
167+
}
168+
} else {
169+
const data: any[] = await fetchHoldingsData(params);
170+
const formatted = (data as any[]).map((holding) => ({
171+
etfName: holding.etf_name,
172+
etfCode: holding.etf_code,
173+
holdingName: holding.holding_name,
174+
weight: holding.weight_pct,
175+
}));
176+
setHoldingsData(formatted);
177+
}
178+
} catch (err) {
179+
console.error("데이터 불러오기 실패:", err);
180+
if (viewMode === "ETF로 보기") setEtfData([]);
181+
else setHoldingsData([]);
182+
} finally {
183+
setIsLoading(false);
184+
}
185+
};
186+
187+
// 검색 실행 함수
188+
const executeSearch = async () => {
189+
setIsLoading(true);
190+
191+
const params: any = {
192+
query: searchQuery,
193+
sort: viewMode === "ETF로 보기" ? "etf_code" : "weight_pct",
194+
};
195+
196+
if (selectedTab === "관심별") {
197+
params.isFavorite = true;
198+
} else {
199+
if (selectedType !== "전체") params.assetClass = selectedType;
200+
if (selectedTheme !== "전체") params.theme = selectedTheme;
201+
}
202+
203+
try {
204+
if (viewMode === "ETF로 보기") {
205+
const data: any[] = await fetchEtfData(params);
206+
const formatted = (data as any[]).map((etf) => ({
207+
name: etf.etf_name,
208+
etfCode: etf.etf_code,
209+
nav: etf.latest_price,
210+
week1: etf.week1 ?? "-",
211+
month1: etf.month1 ?? "-",
212+
month3: etf.month3 ?? "-",
213+
month6: etf.month6 ?? "-",
214+
year1: etf.year1 ?? "-",
215+
year3: etf.year3 ?? "-",
216+
inception: etf.inception ?? "-",
217+
}));
218+
setEtfData(formatted);
219+
220+
// 관심 ETF 세팅
221+
try {
222+
const favoriteCodes = await fetchFavoriteEtfCodes();
223+
setFavoriteEtfCodes(favoriteCodes);
224+
} catch (err: any) {
225+
console.warn(
226+
"💥 관심 ETF 목록 가져오기 실패 (비로그인일 수 있음)",
227+
err
228+
);
229+
}
230+
} else {
231+
const data: any[] = await fetchHoldingsData(params);
232+
const formatted = (data as any[]).map((holding) => ({
233+
etfName: holding.etf_name,
234+
etfCode: holding.etf_code,
235+
holdingName: holding.holding_name,
236+
weight: holding.weight_pct,
237+
}));
238+
setHoldingsData(formatted);
239+
}
240+
} catch (err) {
241+
console.error("데이터 불러오기 실패:", err);
242+
if (viewMode === "ETF로 보기") setEtfData([]);
243+
else setHoldingsData([]);
244+
} finally {
245+
setIsLoading(false);
246+
}
247+
};
248+
120249
const handleSearch = () => {
121250
if (searchQuery.trim()) {
122-
router.push(`/find?query=${encodeURIComponent(searchQuery)}`);
251+
// 현재 페이지에서 검색할 때는 URL을 업데이트하지 않고 바로 검색 실행
252+
executeSearch();
123253
}
124254
};
125255

@@ -139,71 +269,38 @@ export default function FindPage() {
139269
}
140270
};
141271

142-
// 🔥 API 요청 트리거
272+
// URL 파라미터에서 검색어 가져오기 및 초기 검색 실행
143273
useEffect(() => {
144-
const fetchData = async () => {
145-
setIsLoading(true);
274+
if (hasInitialized) return; // 이미 초기화된 경우 중복 실행 방지
146275

147-
const params: any = {
148-
query: searchQuery,
149-
sort: viewMode === "ETF로 보기" ? "etf_code" : "weight_pct",
150-
};
276+
const query = searchParams.get("query");
277+
if (query) {
278+
setSearchQuery(query);
279+
setShowPlaceholder(false);
151280

152-
if (selectedTab === "관심별") {
153-
params.isFavorite = true;
154-
} else {
155-
if (selectedType !== "전체") params.assetClass = selectedType;
156-
if (selectedTheme !== "전체") params.theme = selectedTheme;
157-
}
281+
// URL 파라미터에서 검색어가 있으면 자동으로 검색 실행
282+
setTimeout(() => {
283+
executeSearchWithQuery(query);
158284

159-
try {
160-
if (viewMode === "ETF로 보기") {
161-
const data: any[] = await fetchEtfData(params);
162-
const formatted = (data as any[]).map((etf) => ({
163-
name: etf.etf_name,
164-
etfCode: etf.etf_code,
165-
nav: etf.latest_price,
166-
week1: etf.week1 ?? "-",
167-
month1: etf.month1 ?? "-",
168-
month3: etf.month3 ?? "-",
169-
month6: etf.month6 ?? "-",
170-
year1: etf.year1 ?? "-",
171-
year3: etf.year3 ?? "-",
172-
inception: etf.inception ?? "-",
173-
}));
174-
setEtfData(formatted);
175-
176-
// 관심 ETF 세팅
177-
try {
178-
const favoriteCodes = await fetchFavoriteEtfCodes();
179-
setFavoriteEtfCodes(favoriteCodes);
180-
} catch (err: any) {
181-
console.warn(
182-
"💥 관심 ETF 목록 가져오기 실패 (비로그인일 수 있음)",
183-
err
184-
);
185-
}
186-
} else {
187-
const data: any[] = await fetchHoldingsData(params);
188-
const formatted = (data as any[]).map((holding) => ({
189-
etfName: holding.etf_name,
190-
etfCode: holding.etf_code,
191-
holdingName: holding.holding_name,
192-
weight: holding.weight_pct,
193-
}));
194-
setHoldingsData(formatted);
195-
}
196-
} catch (err) {
197-
console.error("데이터 불러오기 실패:", err);
198-
if (viewMode === "ETF로 보기") setEtfData([]);
199-
else setHoldingsData([]);
200-
} finally {
201-
setIsLoading(false);
202-
}
203-
};
285+
const newUrl = window.location.pathname;
286+
window.history.replaceState({}, "", newUrl);
287+
}, 100); // 약간의 지연을 주어 searchQuery가 설정된 후 실행
288+
} else {
289+
// 검색어가 없으면 모든 ETF 목록을 가져오기
290+
setTimeout(() => {
291+
executeSearchWithQuery("");
292+
}, 100);
293+
}
294+
setHasInitialized(true);
295+
}, [searchParams]);
204296

205-
fetchData();
206-
}, [searchQuery, selectedType, selectedTheme, viewMode, selectedTab]);
297+
// 🔥 API 요청 트리거 (필터 변경 시에만 실행)
298+
useEffect(() => {
299+
// 초기 로드 시에는 실행하지 않음 (URL 파라미터 처리에서 실행됨)
300+
if (searchQuery) {
301+
executeSearch();
302+
}
303+
}, [selectedType, selectedTheme, viewMode, selectedTab]);
207304

208305
// 비교하기 버튼 클릭 핸들러
209306
const handleCompareClick = async () => {
@@ -273,7 +370,7 @@ export default function FindPage() {
273370
style={{ caretColor: "white" }}
274371
placeholder={
275372
showPlaceholder
276-
? "상품명 혹은 종목명으로 원하는 ETF를 검색해보세요"
373+
? "상품명 혹은 증권코드로 원하는 ETF를 검색해보세요"
277374
: ""
278375
}
279376
value={searchQuery}
@@ -334,7 +431,7 @@ export default function FindPage() {
334431
count={
335432
viewMode === "ETF로 보기" ? etfData.length : holdingsData.length
336433
}
337-
selectedTab={selectedTab}
434+
selectedTab={""}
338435
/>
339436

340437
{isLoading ? (

src/app/(pages)/goal/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export default function GoalPlannerpage() {
5959
<div>
6060
<div
6161
className="bg-white rounded-3xl p-8 shadow-lg"
62-
// style={{ borderRadius: "4rem" }}
62+
style={{ borderRadius: "2.5rem" }}
6363
>
6464
<div className="flex items-center justify-center gap-4">
6565
<Loader2 className="w-8 h-8 text-blue-600 animate-spin" />

src/components/blocks/goal/EtfCandidateCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ export const EtfCandidateCard = ({
157157

158158
return (
159159
<div
160-
className="bg-white rounded-3xl w-full px-16 py-16 shadow-lg"
160+
className="bg-white rounded-3xl w-full px-16 py-16 shadow-lg transition-transform duration-300 hover:scale-105"
161161
style={{ borderRadius: "4rem" }}
162162
>
163163
{/* ETF 기본 정보 */}

src/components/blocks/goal/GoalPlannerForm.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ export const GoalPlannerForm = ({ planner }: GoalPlannerFormProps) => {
8888
id="targetAmount"
8989
name="targetAmount"
9090
type="number"
91-
value={input.targetAmount || ""}
91+
value={input.targetAmount === 0 ? "" : input.targetAmount}
9292
onChange={handleInputChange}
9393
min={10000}
9494
className="bg-white border-gray-300 text-gray-900 placeholder:text-gray-400 text-lg font-bold h-20 rounded-3xl px-4 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
@@ -119,7 +119,7 @@ export const GoalPlannerForm = ({ planner }: GoalPlannerFormProps) => {
119119
id="targetYears"
120120
name="targetYears"
121121
type="number"
122-
value={input.targetYears || ""}
122+
value={input.targetYears === 0 ? "" : input.targetYears}
123123
onChange={handleInputChange}
124124
min={1}
125125
max={5}
@@ -166,7 +166,7 @@ export const GoalPlannerForm = ({ planner }: GoalPlannerFormProps) => {
166166
id="initialAmount"
167167
name="initialAmount"
168168
type="number"
169-
value={input.initialAmount || ""}
169+
value={input.initialAmount === 0 ? "" : input.initialAmount}
170170
onChange={handleInputChange}
171171
min={0}
172172
className="bg-white border-gray-300 text-gray-900 placeholder:text-gray-400 text-lg font-bold h-20 rounded-3xl px-4 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
@@ -197,7 +197,7 @@ export const GoalPlannerForm = ({ planner }: GoalPlannerFormProps) => {
197197
id="monthlyContribution"
198198
name="monthlyContribution"
199199
type="number"
200-
value={input.monthlyContribution || ""}
200+
value={input.monthlyContribution === 0 ? "" : input.monthlyContribution}
201201
onChange={handleInputChange}
202202
min={0}
203203
className="bg-white border-gray-300 text-gray-900 placeholder:text-gray-400 text-lg font-bold h-20 rounded-3xl px-4 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"

0 commit comments

Comments
 (0)