diff --git a/package.json b/package.json index bea73ed..ffaf871 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "@supabase/supabase-js": "^2.98.0", "@tailwindcss/vite": "^4.1.8", "@tanstack/react-query": "^5.90.8", - "axios": "^1.13.2", + "axios": "^1.15.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0f8c845..f1a77d1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,8 +36,8 @@ dependencies: specifier: ^5.90.8 version: 5.90.21(react@19.2.4) axios: - specifier: ^1.13.2 - version: 1.13.6 + specifier: ^1.15.0 + version: 1.15.0 class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -2156,12 +2156,12 @@ packages: possible-typed-array-names: 1.1.0 dev: true - /axios@1.13.6: - resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==} + /axios@1.15.0: + resolution: {integrity: sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==} dependencies: follow-redirects: 1.15.11 form-data: 4.0.5 - proxy-from-env: 1.1.0 + proxy-from-env: 2.1.0 transitivePeerDependencies: - debug dev: false @@ -3760,8 +3760,9 @@ packages: object-assign: 4.1.1 react-is: 16.13.1 - /proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + /proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} dev: false /punycode@2.3.1: diff --git a/src/_MainPage/mocks/marketIndexMock.ts b/src/_MainPage/mocks/marketIndexMock.ts index 27edfcb..d97947a 100644 --- a/src/_MainPage/mocks/marketIndexMock.ts +++ b/src/_MainPage/mocks/marketIndexMock.ts @@ -2,156 +2,116 @@ import type { MarketIndex } from "@/_MainPage/types/MarketIndexType"; export const MOCK_KOSPI: MarketIndex = { marketType: "KOSPI", - startDate: "2026-03-01", - endDate: "2026-03-08", + startDate: "2026-05-11", + endDate: "2026-05-15", data: [ { marketType: "KOSPI", - baseDate: "2026-03-02", - openPrice: 5320.15, - closePrice: 5345.82, - highPrice: 5368.4, - lowPrice: 5310.2, - changeAmount: 25.67, - changeRate: 0.48, + baseDate: "2026-05-11", + openPrice: 7775.31, + closePrice: 7822.24, + highPrice: 7899.32, + lowPrice: 7713.49, + changeAmount: 324.24, // 5월 8일 종가(7498.00) 대비 + changeRate: 4.32, }, { marketType: "KOSPI", - baseDate: "2026-03-03", - openPrice: 5345.82, - closePrice: 5412.3, - highPrice: 5425.1, - lowPrice: 5338.5, - changeAmount: 66.48, - changeRate: 1.24, + baseDate: "2026-05-12", + openPrice: 7953.41, + closePrice: 7643.15, + highPrice: 7999.67, + lowPrice: 7421.71, + changeAmount: -179.09, + changeRate: -2.29, }, { marketType: "KOSPI", - baseDate: "2026-03-04", - openPrice: 5412.3, - closePrice: 5389.75, - highPrice: 5430.0, - lowPrice: 5375.2, - changeAmount: -22.55, - changeRate: -0.42, + baseDate: "2026-05-13", + openPrice: 7513.65, + closePrice: 7844.01, + highPrice: 7855.47, + lowPrice: 7402.36, + changeAmount: 200.86, + changeRate: 2.63, }, { marketType: "KOSPI", - baseDate: "2026-03-05", - openPrice: 5389.75, - closePrice: 5456.2, - highPrice: 5470.8, - lowPrice: 5382.1, - changeAmount: 66.45, - changeRate: 1.23, + baseDate: "2026-05-14", + openPrice: 7873.91, + closePrice: 7981.41, + highPrice: 7991.04, + lowPrice: 7842.72, + changeAmount: 137.4, + changeRate: 1.75, }, { marketType: "KOSPI", - baseDate: "2026-03-06", - openPrice: 5456.2, - closePrice: 5523.48, - highPrice: 5540.3, - lowPrice: 5448.9, - changeAmount: 67.28, - changeRate: 1.23, - }, - { - marketType: "KOSPI", - baseDate: "2026-03-07", - openPrice: 5523.48, - closePrice: 5498.12, - highPrice: 5545.0, - lowPrice: 5480.5, - changeAmount: -25.36, - changeRate: -0.46, - }, - { - marketType: "KOSPI", - baseDate: "2026-03-08", - openPrice: 5498.12, - closePrice: 5567.35, - highPrice: 5580.2, - lowPrice: 5492.8, - changeAmount: 69.23, - changeRate: 1.26, + baseDate: "2026-05-15", + openPrice: 7951.75, + closePrice: 7493.18, + highPrice: 8046.78, + lowPrice: 7371.68, + changeAmount: -488.23, + changeRate: -6.12, }, ], }; export const MOCK_KOSDAQ: MarketIndex = { marketType: "KOSDAQ", - startDate: "2026-03-01", - endDate: "2026-03-08", + startDate: "2026-05-11", + endDate: "2026-05-15", data: [ { marketType: "KOSDAQ", - baseDate: "2026-03-02", - openPrice: 1085.3, - closePrice: 1092.45, - highPrice: 1098.7, - lowPrice: 1080.1, - changeAmount: 7.15, - changeRate: 0.66, - }, - { - marketType: "KOSDAQ", - baseDate: "2026-03-03", - openPrice: 1092.45, - closePrice: 1108.2, - highPrice: 1115.3, - lowPrice: 1090.8, - changeAmount: 15.75, - changeRate: 1.44, - }, - { - marketType: "KOSDAQ", - baseDate: "2026-03-04", - openPrice: 1108.2, - closePrice: 1095.6, - highPrice: 1112.4, - lowPrice: 1088.3, - changeAmount: -12.6, - changeRate: -1.14, + baseDate: "2026-05-11", + openPrice: 1212.88, + closePrice: 1207.34, + highPrice: 1212.89, + lowPrice: 1190.6, + changeAmount: -0.38, // 5월 8일 종가(1207.72) 대비 + changeRate: -0.03, }, { marketType: "KOSDAQ", - baseDate: "2026-03-05", - openPrice: 1095.6, - closePrice: 1118.35, - highPrice: 1125.0, - lowPrice: 1093.2, - changeAmount: 22.75, - changeRate: 2.08, + baseDate: "2026-05-12", + openPrice: 1214.9, + closePrice: 1179.29, + highPrice: 1225.29, + lowPrice: 1142.49, + changeAmount: -28.05, + changeRate: -2.32, }, { marketType: "KOSDAQ", - baseDate: "2026-03-06", - openPrice: 1118.35, - closePrice: 1135.8, - highPrice: 1142.5, - lowPrice: 1115.4, - changeAmount: 17.45, - changeRate: 1.56, + baseDate: "2026-05-13", + openPrice: 1176.43, + closePrice: 1176.93, + highPrice: 1183.07, + lowPrice: 1153.65, + changeAmount: -2.36, + changeRate: -0.2, }, { marketType: "KOSDAQ", - baseDate: "2026-03-07", - openPrice: 1135.8, - closePrice: 1128.92, - highPrice: 1140.2, - lowPrice: 1122.6, - changeAmount: -6.88, - changeRate: -0.61, + baseDate: "2026-05-14", + openPrice: 1187.02, + closePrice: 1191.09, + highPrice: 1194.03, + lowPrice: 1162.2, + changeAmount: 14.16, + changeRate: 1.2, }, { marketType: "KOSDAQ", - baseDate: "2026-03-08", - openPrice: 1128.92, - closePrice: 1148.5, - highPrice: 1155.3, - lowPrice: 1126.4, - changeAmount: 19.58, - changeRate: 1.73, + baseDate: "2026-05-15", + openPrice: 1197.23, + closePrice: 1129.82, + highPrice: 1197.23, + lowPrice: 1110.16, + changeAmount: -61.27, + changeRate: -5.14, }, ], }; diff --git a/src/lib/contexts/AuthContext.tsx b/src/lib/contexts/AuthContext.tsx index 5feeab0..98fe200 100644 --- a/src/lib/contexts/AuthContext.tsx +++ b/src/lib/contexts/AuthContext.tsx @@ -1,6 +1,6 @@ import { createContext, useEffect, useState } from "react"; import type { User } from "@supabase/supabase-js"; -import { supabase } from "@/lib/supabase"; +import { supabase, isSupabaseConfigured } from "@/lib/supabase"; interface AuthContextType { user: User | null; @@ -17,6 +17,11 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => { const [isLoading, setIsLoading] = useState(true); useEffect(() => { + if (!isSupabaseConfigured) { + setIsLoading(false); + return; + } + supabase.auth.getSession().then(({ data: { session } }) => { setUser(session?.user ?? null); setIsLoading(false); diff --git a/src/lib/error.ts b/src/lib/error.ts index 6570fe8..781b4d5 100644 --- a/src/lib/error.ts +++ b/src/lib/error.ts @@ -1,4 +1,5 @@ import { AuthError } from "@supabase/supabase-js"; +import { SupabaseConfigError } from "@/lib/supabase"; const AUTH_ERROR_MESSAGE_MAP: Record = { email_exists: "이미 사용 중인 이메일입니다.", @@ -22,6 +23,10 @@ const AUTH_ERROR_MESSAGE_MAP: Record = { }; export function generateErrorMessage(error: unknown) { + if (error instanceof SupabaseConfigError) { + return error.message; + } + if (error instanceof AuthError && error.code) { return ( AUTH_ERROR_MESSAGE_MAP[error.code] ?? diff --git a/src/lib/supabase.ts b/src/lib/supabase.ts index 31e474d..05fc244 100644 --- a/src/lib/supabase.ts +++ b/src/lib/supabase.ts @@ -1,10 +1,26 @@ -import { createClient } from "@supabase/supabase-js"; +import { createClient, type SupabaseClient } from "@supabase/supabase-js"; const supabaseUrl = import.meta.env.VITE_SUPABASE_URL; const supabaseKey = import.meta.env.VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY; -if (!supabaseUrl || !supabaseKey) { - throw new Error("Supabase 환경변수가 없거나 잘못되었습니다."); +export const isSupabaseConfigured = Boolean(supabaseUrl && supabaseKey); + +export class SupabaseConfigError extends Error { + constructor() { + super("인증 서비스 설정이 누락되었습니다. 잠시 후 다시 시도해주세요."); + this.name = "SupabaseConfigError"; + } +} + +function createUnconfiguredClient(): SupabaseClient { + const handler: ProxyHandler = { + get() { + throw new SupabaseConfigError(); + }, + }; + return new Proxy({}, handler) as SupabaseClient; } -export const supabase = createClient(supabaseUrl, supabaseKey); +export const supabase: SupabaseClient = isSupabaseConfigured + ? createClient(supabaseUrl, supabaseKey) + : createUnconfiguredClient(); diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx index 38461ba..01c87e3 100644 --- a/src/pages/LoginPage.tsx +++ b/src/pages/LoginPage.tsx @@ -41,6 +41,14 @@ const LoginPage = () => { } }; + const handleClickGitHubSignIn = async () => { + try { + await signInWithGitHub(); + } catch (error) { + toast.error(generateErrorMessage(error)); + } + }; + return (
@@ -88,7 +96,7 @@ const LoginPage = () => {