Skip to content

Commit fb02e1e

Browse files
committed
feat: Implement WalletContext for real-time portfolio updates and refactor AuthUserProvider for improved state management.
1 parent 8384c2f commit fb02e1e

File tree

9 files changed

+349
-113
lines changed

9 files changed

+349
-113
lines changed

frontend/package-lock.json

Lines changed: 137 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"react-hook-form": "^7.60.0",
5757
"react-resizable-panels": "^2.1.7",
5858
"recharts": "2.15.4",
59+
"socket.io-client": "^4.8.1",
5960
"sonner": "^1.7.4",
6061
"tailwind-merge": "^3.3.1",
6162
"tailwindcss-animate": "^1.0.7",

frontend/src/app/(auth)/login/page.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,12 @@ function LoginPage() {
4545

4646
if (response.ok) {
4747
const data = await response.json();
48-
// Context 업데이트 (로컬 스토리지는 Provider가 처리)
49-
if (data.access_token) {
50-
setAccessToken(data.access_token);
48+
if (data.accessToken) {
49+
setAccessToken(data.accessToken);
5150
}
5251
if (data.user) {
5352
setUser(data.user);
5453
}
55-
// 메인 페이지로 이동
5654
router.push("/");
5755
} else {
5856
const data = await response.json();

frontend/src/app/account/page.tsx

Lines changed: 5 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,62 +4,27 @@ import SettingCard from "@/components/SettingCard";
44
import ThemeToggle from "@/components/ThemeToggle";
55
import { Button } from "@/components/ui/button";
66
import { useAuth, useUser } from "@/context/AuthUserProvider";
7-
import { fetchClient } from "@/lib/api/fetchClient";
7+
import { useWallet } from "@/context/WalletContext";
88
import { useTheme } from "@/lib/utils/theme-context";
99
import Link from "next/link";
1010
import { useRouter } from "next/navigation";
11-
import { useEffect, useState } from "react";
12-
13-
interface Portfolio {
14-
balance: number;
15-
holdings: {
16-
coinId: string;
17-
amount: number;
18-
averagePrice: number;
19-
}[];
20-
}
2111

2212
function Account() {
23-
const { user, clearUser } = useUser();
24-
const { accessToken, clearAccessToken } = useAuth();
13+
const { user } = useUser();
14+
const { clearAccessToken } = useAuth();
15+
const { clearUser } = useUser();
2516
const { theme, toggleTheme } = useTheme();
2617
const router = useRouter();
2718
const isDark = theme === "dark";
2819

29-
const [portfolio, setPortfolio] = useState<Portfolio | null>(null);
30-
31-
const fetchPortfolio = async () => {
32-
try {
33-
const response = await fetchClient("/wallet/portfolio", {
34-
headers: {
35-
Authorization: `Bearer ${accessToken}`,
36-
"Content-Type": "application/json",
37-
},
38-
credentials: "include",
39-
method: "GET",
40-
});
41-
if (response.ok) {
42-
const data = await response.json();
43-
setPortfolio(data);
44-
}
45-
} catch (err) {
46-
console.error("Failed to fetch portfolio:", err);
47-
}
48-
};
20+
const { portfolio } = useWallet();
4921

50-
5122
const handleLogout = () => {
5223
clearAccessToken();
5324
clearUser();
5425
router.push("/login");
5526
};
5627

57-
useEffect(() => {
58-
if (accessToken) {
59-
fetchPortfolio();
60-
}
61-
}, [accessToken, fetchPortfolio]);
62-
6328
return (
6429
<div
6530
className={`min-h-screen flex flex-col justify-center items-center ${

frontend/src/app/layout.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { Header } from "@/components/main/Header";
44
import { AuthUserProvider } from "@/context/AuthUserProvider";
5+
import { WalletProvider } from "@/context/WalletContext";
56
import ThemeProvider from "@/lib/utils/theme-context";
67
import type { Metadata } from "next";
78
import { Geist, Geist_Mono } from "next/font/google";
@@ -45,8 +46,10 @@ export default function RootLayout({
4546
>
4647
<ThemeProvider>
4748
<AuthUserProvider>
48-
<Header />
49-
{children}
49+
<WalletProvider>
50+
<Header />
51+
{children}
52+
</WalletProvider>
5053
</AuthUserProvider>
5154
</ThemeProvider>
5255
</body>

frontend/src/components/trade/Panel.tsx

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,21 @@
11
"use client";
22

3-
import { useAuth } from "@/context/AuthUserProvider";
3+
import { useWallet } from "@/context/WalletContext";
44
import { fetchClient } from "@/lib/api/fetchClient";
55
import { useTheme } from "@/lib/utils/theme-context";
66
import { useEffect, useState } from "react";
77
import { Button } from "../ui/button";
88

9-
interface Portfolio {
10-
balance: number;
11-
holdings: {
12-
coinId: string;
13-
amount: number;
14-
averagePrice: number;
15-
}[];
16-
}
17-
189
export default function TradePanel({ symbol }: { symbol: string }) {
1910
const [price, setPrice] = useState<number | null>(null);
2011
const [inputQty, setInputQty] = useState("");
21-
const [portfolio, setPortfolio] = useState<Portfolio | null>(null);
2212
const [loading, setLoading] = useState(false);
2313
const [error, setError] = useState("");
2414
const { theme } = useTheme();
25-
const { accessToken } = useAuth();
15+
const { portfolio, refreshPortfolio } = useWallet();
2616
const isDark = theme === "dark";
2717

2818
useEffect(() => {
29-
if (accessToken) {
30-
fetchPortfolio();
31-
}
3219
const wsUrl = `wss://stream.binance.com:9443/ws/${symbol.toLowerCase()}@trade`;
3320
const ws = new WebSocket(wsUrl);
3421

@@ -42,25 +29,6 @@ export default function TradePanel({ symbol }: { symbol: string }) {
4229
};
4330
}, [symbol]);
4431

45-
const fetchPortfolio = async () => {
46-
if (!accessToken) return;
47-
try {
48-
const response = await fetchClient("/wallet/portfolio", {
49-
headers: {
50-
Authorization: `Bearer ${accessToken}`,
51-
"Content-Type": "application/json",
52-
},
53-
credentials: "include", // 쿠키 포함 (필요 시)
54-
});
55-
if (response.ok) {
56-
const data = await response.json();
57-
setPortfolio(data);
58-
}
59-
} catch (err) {
60-
console.error("Failed to fetch portfolio:", err);
61-
}
62-
};
63-
6432
const handleTrade = async (type: 'BUY' | 'SELL') => {
6533
if (!inputQty || !price) return;
6634

@@ -90,9 +58,7 @@ export default function TradePanel({ symbol }: { symbol: string }) {
9058
method: "POST",
9159
headers: {
9260
"Content-Type": "application/json",
93-
Authorization: `Bearer ${accessToken}`,
9461
},
95-
credentials: "include",
9662
body: JSON.stringify({
9763
coinId: symbol,
9864
amount: amount,
@@ -103,7 +69,7 @@ export default function TradePanel({ symbol }: { symbol: string }) {
10369
if (response.ok) {
10470
alert(`${type === 'BUY' ? "매수" : "매도"} 성공!`);
10571
setInputQty("");
106-
fetchPortfolio(); // 잔액 갱신
72+
refreshPortfolio(); // 잔액 갱신
10773
} else {
10874
const data = await response.json();
10975
setError(data.message || `${type === 'BUY' ? "매수" : "매도"} 실패`);

0 commit comments

Comments
 (0)