From 7dd6165d8285373cb68e20f0a85adc7991ba86f7 Mon Sep 17 00:00:00 2001 From: 0011810 Date: Mon, 13 Jul 2026 14:43:02 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8/?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EA=B5=AC=ED=98=84=20(#43)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/images/auth/icon-github.svg | 3 + public/images/auth/icon-google.svg | 6 + public/images/auth/login-symbol.png | Bin 0 -> 72979 bytes src/app/auth/callback/route.ts | 35 ++ src/app/login/page.tsx | 10 + src/app/signup/page.tsx | 5 + src/middleware.ts | 10 + src/shared/lib/middleware.ts | 21 +- src/views/login/index.ts | 1 + src/views/login/ui/LoginView.tsx | 222 ++++++++++ src/views/signup/index.ts | 1 + src/views/signup/ui/SignupView.tsx | 384 ++++++++++++++++++ .../landing-header/ui/LandingHeader.tsx | 2 +- 13 files changed, 692 insertions(+), 8 deletions(-) create mode 100644 public/images/auth/icon-github.svg create mode 100644 public/images/auth/icon-google.svg create mode 100644 public/images/auth/login-symbol.png create mode 100644 src/app/auth/callback/route.ts create mode 100644 src/app/login/page.tsx create mode 100644 src/app/signup/page.tsx create mode 100644 src/middleware.ts create mode 100644 src/views/login/index.ts create mode 100644 src/views/login/ui/LoginView.tsx create mode 100644 src/views/signup/index.ts create mode 100644 src/views/signup/ui/SignupView.tsx diff --git a/public/images/auth/icon-github.svg b/public/images/auth/icon-github.svg new file mode 100644 index 0000000..4188a28 --- /dev/null +++ b/public/images/auth/icon-github.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/auth/icon-google.svg b/public/images/auth/icon-google.svg new file mode 100644 index 0000000..cfb057b --- /dev/null +++ b/public/images/auth/icon-google.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/auth/login-symbol.png b/public/images/auth/login-symbol.png new file mode 100644 index 0000000000000000000000000000000000000000..5a7fccc49186d60169a7dfc1e353a4f20675b740 GIT binary patch literal 72979 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

&$DWJ}cPfLc5zZEzqbiFpFiiCnar-GCx< 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 n && n !== '-') ?? + data.user.email!.split('@')[0]; + + const { error: upsertError } = await supabase.from('profiles').upsert({ + id: data.user.id, + email: data.user.email!, + real_name: realName, + avatar_url: meta.avatar_url ?? null, + }); + + if (upsertError) { + return NextResponse.redirect(`${origin}/login?error=auth_failed`); + } + + return NextResponse.redirect(`${origin}${next}`); + } + } + + return NextResponse.redirect(`${origin}/login?error=auth_failed`); +} diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx new file mode 100644 index 0000000..225c5e3 --- /dev/null +++ b/src/app/login/page.tsx @@ -0,0 +1,10 @@ +import { Suspense } from 'react'; +import { LoginView } from '@/views/login'; + +export default function LoginPage() { + return ( + + + + ); +} diff --git a/src/app/signup/page.tsx b/src/app/signup/page.tsx new file mode 100644 index 0000000..7b542f7 --- /dev/null +++ b/src/app/signup/page.tsx @@ -0,0 +1,5 @@ +import { SignupView } from '@/views/signup'; + +export default function SignupPage() { + return ; +} diff --git a/src/middleware.ts b/src/middleware.ts new file mode 100644 index 0000000..b54c291 --- /dev/null +++ b/src/middleware.ts @@ -0,0 +1,10 @@ +import { updateSession } from '@/shared/lib/middleware'; +import { type NextRequest } from 'next/server'; + +export async function middleware(request: NextRequest) { + return await updateSession(request); +} + +export const config = { + matcher: ['/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)'], +}; diff --git a/src/shared/lib/middleware.ts b/src/shared/lib/middleware.ts index 2a14462..d0ca5dc 100644 --- a/src/shared/lib/middleware.ts +++ b/src/shared/lib/middleware.ts @@ -38,14 +38,21 @@ export async function updateSession(request: NextRequest) { const { data } = await supabase.auth.getClaims(); const user = data?.claims; - if ( - !user && - !request.nextUrl.pathname.startsWith('/login') && - !request.nextUrl.pathname.startsWith('/auth') - ) { - // no user, potentially respond by redirecting the user to the login page + const PUBLIC_PATHS = ['/', '/login', '/signup', '/auth']; + const isPublic = PUBLIC_PATHS.some( + (p) => request.nextUrl.pathname === p || request.nextUrl.pathname.startsWith(p + '/'), + ); + + if (!user && !isPublic) { + const url = request.nextUrl.clone(); + url.pathname = '/login'; + return NextResponse.redirect(url); + } + + const AUTH_PAGES = ['/login', '/signup']; + if (user && AUTH_PAGES.includes(request.nextUrl.pathname)) { const url = request.nextUrl.clone(); - url.pathname = '/auth/login'; + url.pathname = '/workspaces'; return NextResponse.redirect(url); } diff --git a/src/views/login/index.ts b/src/views/login/index.ts new file mode 100644 index 0000000..6942ed1 --- /dev/null +++ b/src/views/login/index.ts @@ -0,0 +1 @@ +export { default as LoginView } from './ui/LoginView'; diff --git a/src/views/login/ui/LoginView.tsx b/src/views/login/ui/LoginView.tsx new file mode 100644 index 0000000..ac0e47a --- /dev/null +++ b/src/views/login/ui/LoginView.tsx @@ -0,0 +1,222 @@ +'use client'; + +import Image from 'next/image'; +import Link from 'next/link'; +import { useRouter, useSearchParams } from 'next/navigation'; +import { useState } from 'react'; +import { Eye, EyeOff, Loader2 } from 'lucide-react'; + +import { createClient } from '@/shared/lib/client'; +import { Input } from '@/shared/ui/input'; + +const AUTH_ERROR_MAP: Record = { + 'Invalid login credentials': '이메일 또는 비밀번호가 올바르지 않아요', + 'Email not confirmed': '이메일 인증이 필요해요. 메일함을 확인해주세요', + 'User already registered': '이미 가입된 이메일이에요', + 'Password should be at least 6 characters': '비밀번호는 6자 이상으로 입력해주세요', + 'Unable to validate email address: invalid format': '올바른 이메일 형식이 아니에요', + 'signup requires a valid password': '비밀번호를 입력해주세요', + 'Email rate limit exceeded': '잠시 후 다시 시도해주세요', + over_email_send_rate_limit: '잠시 후 다시 시도해주세요', + 'too many requests': '잠시 후 다시 시도해주세요', +}; + +function toKoreanError(message: string): string { + const matched = Object.entries(AUTH_ERROR_MAP).find(([key]) => + message.toLowerCase().includes(key.toLowerCase()), + ); + return matched ? matched[1] : '로그인 중 오류가 발생했어요. 다시 시도해주세요'; +} + +const CALLBACK_ERROR_MAP: Record = { + auth_failed: '소셜 로그인에 실패했어요. 다시 시도해주세요', + session_exchange_failed: '인증 처리 중 오류가 발생했어요. 다시 시도해주세요', +}; + +const SAVED_EMAIL_KEY = 'syncly_saved_email'; + +export default function LoginView() { + const supabase = createClient(); + const router = useRouter(); + const searchParams = useSearchParams(); + const callbackError = searchParams.get('error'); + const [email, setEmail] = useState(() => + typeof window !== 'undefined' ? (localStorage.getItem(SAVED_EMAIL_KEY) ?? '') : '', + ); + const [password, setPassword] = useState(''); + const [rememberEmail, setRememberEmail] = useState(() => + typeof window !== 'undefined' ? !!localStorage.getItem(SAVED_EMAIL_KEY) : false, + ); + const [emailError, setEmailError] = useState(null); + const [passwordError, setPasswordError] = useState(null); + const [error, setError] = useState( + callbackError ? (CALLBACK_ERROR_MAP[callbackError] ?? '로그인 중 오류가 발생했어요') : null, + ); + const [loading, setLoading] = useState(false); + const [showPassword, setShowPassword] = useState(false); + + const signInWithGoogle = async () => { + await supabase.auth.signInWithOAuth({ + provider: 'google', + options: { redirectTo: `${window.location.origin}/auth/callback` }, + }); + }; + + const signInWithGitHub = async () => { + await supabase.auth.signInWithOAuth({ + provider: 'github', + options: { redirectTo: `${window.location.origin}/auth/callback` }, + }); + }; + + const signInWithEmail = async () => { + setEmailError(null); + setPasswordError(null); + setError(null); + + if (!email) { + setEmailError('이메일을 입력해주세요'); + return; + } + if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { + setEmailError('올바른 이메일 형식이 아니에요'); + return; + } + if (!password) { + setPasswordError('비밀번호를 입력해주세요'); + return; + } + + if (rememberEmail) { + localStorage.setItem(SAVED_EMAIL_KEY, email); + } else { + localStorage.removeItem(SAVED_EMAIL_KEY); + } + + setLoading(true); + const { error } = await supabase.auth.signInWithPassword({ email, password }); + if (error) { + setError(toKoreanError(error.message)); + } else { + router.push('/workspaces'); + } + setLoading(false); + }; + + return ( +

+
+
+ Syncly +
+

Syncly에 오신 걸 환영해요

+

소규모 팀을 위한 가벼운 협업 공간

+
+
+ +
+ + + + +
+
+ 또는 +
+
+ +
{ + e.preventDefault(); + signInWithEmail(); + }} + noValidate + className="flex flex-col gap-3" + > +
+ { + setEmail(e.target.value); + setEmailError(null); + }} + className={`bg-brand-secondary text-brand-ink placeholder:text-brand-ink/50 h-11 rounded-[18px] border-2 px-4 text-sm transition-colors focus-visible:ring-0 ${emailError ? 'border-red-400 focus-visible:border-red-400' : 'focus-visible:border-brand border-transparent'}`} + /> + {emailError &&

{emailError}

} +
+
+
+ { + setPassword(e.target.value); + setPasswordError(null); + }} + className={`bg-brand-secondary text-brand-ink placeholder:text-brand-ink/50 h-11 w-full rounded-[18px] border-2 px-4 pr-11 text-sm transition-colors focus-visible:ring-0 ${passwordError ? 'border-red-400 focus-visible:border-red-400' : 'focus-visible:border-brand border-transparent'}`} + /> + +
+ {passwordError &&

{passwordError}

} +
+ + + + {error &&

{error}

} + +
+
+ +
+ + ← 홈으로 + +

+ 아직 회원이 아니신가요?{' '} + + 가입하기 + +

+
+
+
+ ); +} diff --git a/src/views/signup/index.ts b/src/views/signup/index.ts new file mode 100644 index 0000000..71261bc --- /dev/null +++ b/src/views/signup/index.ts @@ -0,0 +1 @@ +export { default as SignupView } from './ui/SignupView'; diff --git a/src/views/signup/ui/SignupView.tsx b/src/views/signup/ui/SignupView.tsx new file mode 100644 index 0000000..00c602b --- /dev/null +++ b/src/views/signup/ui/SignupView.tsx @@ -0,0 +1,384 @@ +'use client'; + +import Image from 'next/image'; +import Link from 'next/link'; +import { useEffect, useRef, useState } from 'react'; + +import { Eye, EyeOff, Loader2 } from 'lucide-react'; + +import { createClient } from '@/shared/lib/client'; +import { Input } from '@/shared/ui/input'; + +const INPUT_CLASS = + 'h-11 rounded-[18px] border-2 border-transparent bg-brand-secondary px-4 text-sm text-brand-ink placeholder:text-brand-ink/50 transition-colors focus-visible:border-brand focus-visible:ring-0'; + +type Step = 'email' | 'otp' | 'info' | 'done'; + +const STEP_TITLE: Record = { + email: 'Syncly 시작하기', + otp: '인증번호 입력', + info: '사용자 정보 입력', + done: '가입이 완료됐어요', +}; + +const OTP_SECONDS = 180; + +const AUTH_ERROR_MAP: Record = { + 'User already registered': '이미 가입된 이메일이에요', + 'Password should be at least 6 characters': '비밀번호는 6자 이상으로 입력해주세요', + 'Unable to validate email address': '올바른 이메일 형식이 아니에요', + 'Token has expired or is invalid': '인증번호가 만료됐거나 올바르지 않아요', + 'Email rate limit exceeded': '잠시 후 다시 시도해주세요', + 'too many requests': '잠시 후 다시 시도해주세요', + over_email_send_rate_limit: '잠시 후 다시 시도해주세요', +}; + +function toKoreanError(message: string): string { + const matched = Object.entries(AUTH_ERROR_MAP).find(([key]) => + message.toLowerCase().includes(key.toLowerCase()), + ); + return matched ? matched[1] : '오류가 발생했어요. 다시 시도해주세요'; +} + +function formatTimer(seconds: number) { + const m = Math.floor(seconds / 60); + const s = seconds % 60; + return `${m}:${String(s).padStart(2, '0')}`; +} + +export default function SignupView() { + const supabase = createClient(); + + const [step, setStep] = useState('email'); + const [email, setEmail] = useState(''); + const [otp, setOtp] = useState(''); + const [name, setName] = useState(''); + const [password, setPassword] = useState(''); + const [showPassword, setShowPassword] = useState(false); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(false); + const [timer, setTimer] = useState(OTP_SECONDS); + + const timerRef = useRef | null>(null); + + const startTimer = () => { + setTimer(OTP_SECONDS); + if (timerRef.current) clearInterval(timerRef.current); + timerRef.current = setInterval(() => { + setTimer((prev) => { + if (prev <= 1) { + clearInterval(timerRef.current!); + return 0; + } + return prev - 1; + }); + }, 1000); + }; + + useEffect( + () => () => { + if (timerRef.current) clearInterval(timerRef.current); + }, + [], + ); + + const signInWithGoogle = async () => { + await supabase.auth.signInWithOAuth({ + provider: 'google', + options: { redirectTo: `${window.location.origin}/auth/callback` }, + }); + }; + + const signInWithGitHub = async () => { + await supabase.auth.signInWithOAuth({ + provider: 'github', + options: { redirectTo: `${window.location.origin}/auth/callback` }, + }); + }; + + const handleEmailNext = async () => { + setError(null); + setLoading(true); + const { error } = await supabase.auth.signInWithOtp({ + email, + options: { shouldCreateUser: true }, + }); + if (error) { + setError(toKoreanError(error.message)); + } else { + setStep('otp'); + startTimer(); + } + setLoading(false); + }; + + const handleOtpVerify = async () => { + setError(null); + setLoading(true); + const { error } = await supabase.auth.verifyOtp({ + email, + token: otp, + type: 'email', + }); + if (error) { + setError(toKoreanError(error.message)); + } else { + setStep('info'); + } + setLoading(false); + }; + + const handleResendOtp = async () => { + setError(null); + setLoading(true); + const { error } = await supabase.auth.signInWithOtp({ + email, + options: { shouldCreateUser: true }, + }); + if (error) { + setError(toKoreanError(error.message)); + } else { + startTimer(); + } + setLoading(false); + }; + + const handleInfoSubmit = async () => { + setError(null); + setLoading(true); + const { error } = await supabase.auth.updateUser({ + password, + data: { full_name: name }, + }); + if (error) { + setError(toKoreanError(error.message)); + } else { + const { + data: { user }, + } = await supabase.auth.getUser(); + if (user) { + await supabase.from('profiles').upsert({ + id: user.id, + email: user.email!, + real_name: name, + avatar_url: null, + }); + } + setStep('done'); + } + setLoading(false); + }; + + return ( +
+
+
+ Syncly +
+

{STEP_TITLE[step]}

+

+ {step === 'email' && '소규모 팀을 위한 가벼운 협업 공간'} + {step === 'otp' && ( + <> + {email}로
+ 인증번호를 보냈어요 + + )} + {step === 'info' && email} + {step === 'done' && '이제 워크스페이스를 만들어보세요'} +

+
+
+ + {step === 'email' && ( +
+
{ + e.preventDefault(); + handleEmailNext(); + }} + className="flex flex-col gap-3" + > + setEmail(e.target.value)} + required + className={INPUT_CLASS} + /> + {error &&

{error}

} + +
+ +
+
+ 또는 +
+
+ + + +
+ )} + + {step === 'otp' && ( +
+
{ + e.preventDefault(); + handleOtpVerify(); + }} + noValidate + className="flex flex-col gap-3" + > +
+
+ setOtp(e.target.value.replace(/\D/g, '').slice(0, 6))} + required + className={`${INPUT_CLASS} pr-16`} + /> + + {formatTimer(timer)} + +
+ +
+ {error &&

{error}

} + +
+
+ )} + + {step === 'info' && ( +
+
{ + e.preventDefault(); + handleInfoSubmit(); + }} + noValidate + className="flex flex-col gap-3" + > + setName(e.target.value)} + required + className={INPUT_CLASS} + /> +
+ setPassword(e.target.value)} + required + minLength={6} + className={`${INPUT_CLASS} pr-11`} + /> + +
+ {error &&

{error}

} + +
+
+ )} + + {step === 'done' && ( + + 시작하기 + + )} + + {step !== 'done' && ( +
+ {step === 'email' && ( + + ← 홈으로 + + )} + {(step === 'otp' || step === 'info') && ( + + )} +

+ 이미 계정이 있으신가요?{' '} + + 로그인 + +

+
+ )} +
+
+ ); +} diff --git a/src/widgets/landing/landing-header/ui/LandingHeader.tsx b/src/widgets/landing/landing-header/ui/LandingHeader.tsx index 8e4ba37..ecf0192 100644 --- a/src/widgets/landing/landing-header/ui/LandingHeader.tsx +++ b/src/widgets/landing/landing-header/ui/LandingHeader.tsx @@ -44,7 +44,7 @@ export default function LandingHeader() { 로그인 무료로 시작하기 From f5ae24020c98e29df7e285b54dc58f57bbe4d72b Mon Sep 17 00:00:00 2001 From: 0011810 Date: Mon, 13 Jul 2026 16:44:15 +0900 Subject: [PATCH 2/6] =?UTF-8?q?fix:=20=EC=BD=94=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81=20(#43)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/auth/callback/route.ts | 29 +++++++++++----- src/shared/lib/middleware.ts | 6 ++-- src/views/login/ui/LoginView.tsx | 35 +++++++++++-------- src/views/signup/ui/SignupView.tsx | 55 ++++++++++++++++++------------ 4 files changed, 78 insertions(+), 47 deletions(-) diff --git a/src/app/auth/callback/route.ts b/src/app/auth/callback/route.ts index f06c8d6..84b46d8 100644 --- a/src/app/auth/callback/route.ts +++ b/src/app/auth/callback/route.ts @@ -4,24 +4,37 @@ import { createClient } from '@/shared/lib/server'; export async function GET(request: NextRequest) { const { searchParams, origin } = new URL(request.url); const code = searchParams.get('code'); - const next = searchParams.get('next') ?? '/workspaces'; + const rawNext = searchParams.get('next') ?? '/workspaces'; + let next = '/workspaces'; + try { + const parsed = new URL(rawNext, origin); + if (parsed.origin === origin) next = parsed.pathname + parsed.search; + } catch { + // invalid URL, keep default + } if (code) { const supabase = await createClient(); const { data, error } = await supabase.auth.exchangeCodeForSession(code); if (!error && data.user) { + if (!data.user.email) { + return NextResponse.redirect(`${origin}/login?error=email_missing`); + } const meta = data.user.user_metadata ?? {}; const realName = [meta.full_name, meta.name, meta.user_name].find((n) => n && n !== '-') ?? - data.user.email!.split('@')[0]; + data.user.email.split('@')[0]; - const { error: upsertError } = await supabase.from('profiles').upsert({ - id: data.user.id, - email: data.user.email!, - real_name: realName, - avatar_url: meta.avatar_url ?? null, - }); + const { error: upsertError } = await supabase.from('profiles').upsert( + { + id: data.user.id, + email: data.user.email, + real_name: realName, + avatar_url: meta.avatar_url ?? null, + }, + { onConflict: 'id', ignoreDuplicates: true }, + ); if (upsertError) { return NextResponse.redirect(`${origin}/login?error=auth_failed`); diff --git a/src/shared/lib/middleware.ts b/src/shared/lib/middleware.ts index d0ca5dc..e907527 100644 --- a/src/shared/lib/middleware.ts +++ b/src/shared/lib/middleware.ts @@ -1,6 +1,9 @@ import { createServerClient } from '@supabase/ssr'; import { NextResponse, type NextRequest } from 'next/server'; +const PUBLIC_PATHS = ['/', '/login', '/signup', '/auth']; +const AUTH_PAGES = ['/login', '/signup']; + export async function updateSession(request: NextRequest) { let supabaseResponse = NextResponse.next({ request, @@ -38,7 +41,6 @@ export async function updateSession(request: NextRequest) { const { data } = await supabase.auth.getClaims(); const user = data?.claims; - const PUBLIC_PATHS = ['/', '/login', '/signup', '/auth']; const isPublic = PUBLIC_PATHS.some( (p) => request.nextUrl.pathname === p || request.nextUrl.pathname.startsWith(p + '/'), ); @@ -46,10 +48,10 @@ export async function updateSession(request: NextRequest) { if (!user && !isPublic) { const url = request.nextUrl.clone(); url.pathname = '/login'; + url.search = new URLSearchParams({ redirect: request.nextUrl.pathname }).toString(); return NextResponse.redirect(url); } - const AUTH_PAGES = ['/login', '/signup']; if (user && AUTH_PAGES.includes(request.nextUrl.pathname)) { const url = request.nextUrl.clone(); url.pathname = '/workspaces'; diff --git a/src/views/login/ui/LoginView.tsx b/src/views/login/ui/LoginView.tsx index ac0e47a..a8b4766 100644 --- a/src/views/login/ui/LoginView.tsx +++ b/src/views/login/ui/LoginView.tsx @@ -31,6 +31,7 @@ function toKoreanError(message: string): string { const CALLBACK_ERROR_MAP: Record = { auth_failed: '소셜 로그인에 실패했어요. 다시 시도해주세요', session_exchange_failed: '인증 처리 중 오류가 발생했어요. 다시 시도해주세요', + email_missing: 'OAuth 계정에서 이메일 정보를 확인할 수 없어요. 다른 방법으로 로그인해 주세요', }; const SAVED_EMAIL_KEY = 'syncly_saved_email'; @@ -55,18 +56,17 @@ export default function LoginView() { const [loading, setLoading] = useState(false); const [showPassword, setShowPassword] = useState(false); - const signInWithGoogle = async () => { - await supabase.auth.signInWithOAuth({ - provider: 'google', - options: { redirectTo: `${window.location.origin}/auth/callback` }, - }); - }; - - const signInWithGitHub = async () => { - await supabase.auth.signInWithOAuth({ - provider: 'github', + const handleOAuthSignIn = async (provider: 'google' | 'github') => { + setError(null); + setLoading(true); + const { error } = await supabase.auth.signInWithOAuth({ + provider, options: { redirectTo: `${window.location.origin}/auth/callback` }, }); + if (error) { + setError(toKoreanError(error.message)); + setLoading(false); + } }; const signInWithEmail = async () => { @@ -98,7 +98,8 @@ export default function LoginView() { if (error) { setError(toKoreanError(error.message)); } else { - router.push('/workspaces'); + const redirect = searchParams.get('redirect'); + router.push(redirect ?? '/workspaces'); } setLoading(false); }; @@ -116,16 +117,18 @@ export default function LoginView() {
+ {oauthError &&

{oauthError}

} +
또는 diff --git a/src/views/signup/ui/SignupView.tsx b/src/views/signup/ui/SignupView.tsx index cd68852..f9f5d4c 100644 --- a/src/views/signup/ui/SignupView.tsx +++ b/src/views/signup/ui/SignupView.tsx @@ -2,11 +2,13 @@ import Image from 'next/image'; import Link from 'next/link'; +import { useRouter } from 'next/navigation'; import { useEffect, useRef, useState } from 'react'; import { Eye, EyeOff, Loader2 } from 'lucide-react'; import { createClient } from '@/shared/lib/client'; +import { useOAuthSignIn } from '@/shared/lib/use-oauth-sign-in'; import { Input } from '@/shared/ui/input'; const INPUT_CLASS = @@ -48,6 +50,7 @@ function formatTimer(seconds: number) { export default function SignupView() { const supabase = createClient(); + const router = useRouter(); const [step, setStep] = useState('email'); const [email, setEmail] = useState(''); @@ -60,6 +63,25 @@ export default function SignupView() { const [timer, setTimer] = useState(OTP_SECONDS); const timerRef = useRef | null>(null); + const { signInWithOAuth, oauthError, oauthLoading } = useOAuthSignIn(); + + useEffect(() => { + const checkSession = async () => { + const { data: { user } } = await supabase.auth.getUser(); + if (!user) return; + const { data: profile } = await supabase + .from('profiles') + .select('real_name') + .eq('id', user.id) + .maybeSingle(); + if (profile?.real_name) { + router.push('/workspaces'); + } else { + setStep('info'); + } + }; + checkSession(); + }, []); const startTimer = () => { setTimer(OTP_SECONDS); @@ -82,19 +104,6 @@ export default function SignupView() { [], ); - const handleOAuthSignIn = async (provider: 'google' | 'github') => { - setError(null); - setLoading(true); - const { error } = await supabase.auth.signInWithOAuth({ - provider, - options: { redirectTo: `${window.location.origin}/auth/callback` }, - }); - if (error) { - setError(toKoreanError(error.message)); - setLoading(false); - } - }; - const handleEmailNext = async () => { setError(null); setLoading(true); @@ -232,21 +241,22 @@ export default function SignupView() {
+ {oauthError &&

{oauthError}

}
)} From c589c1da67b96841f28b14f790b571ae439e2dfa Mon Sep 17 00:00:00 2001 From: 0011810 Date: Mon, 13 Jul 2026 17:23:55 +0900 Subject: [PATCH 4/6] =?UTF-8?q?style:=20OTP/=EC=A0=95=EB=B3=B4=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=20=EB=8B=A8=EA=B3=84=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=9C=A0=EB=8F=84=20=EB=B2=84=ED=8A=BC=20=EC=88=A8=EA=B9=80=20?= =?UTF-8?q?(#43)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/signup/ui/SignupView.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/views/signup/ui/SignupView.tsx b/src/views/signup/ui/SignupView.tsx index f9f5d4c..11b1060 100644 --- a/src/views/signup/ui/SignupView.tsx +++ b/src/views/signup/ui/SignupView.tsx @@ -391,12 +391,14 @@ export default function SignupView() { ← 이전 )} -

- 이미 계정이 있으신가요?{' '} - - 로그인 - -

+ {step === 'email' && ( +

+ 이미 계정이 있으신가요?{' '} + + 로그인 + +

+ )}
)}
From f5b51c88e269f1388d21ea2d30c35c64731c0ba6 Mon Sep 17 00:00:00 2001 From: 0011810 Date: Mon, 13 Jul 2026 17:29:28 +0900 Subject: [PATCH 5/6] =?UTF-8?q?style:=20OTP/=EC=A0=95=EB=B3=B4=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=20=EB=8B=A8=EA=B3=84=20=EC=9D=B4=EC=A0=84=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EA=B0=80=EC=9A=B4=EB=8D=B0=20=EC=A0=95=EB=A0=AC=20?= =?UTF-8?q?(#43)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/signup/ui/SignupView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/signup/ui/SignupView.tsx b/src/views/signup/ui/SignupView.tsx index 11b1060..0f6b7b7 100644 --- a/src/views/signup/ui/SignupView.tsx +++ b/src/views/signup/ui/SignupView.tsx @@ -371,7 +371,7 @@ export default function SignupView() { )} {step !== 'done' && ( -
+
{step === 'email' && ( Date: Mon, 13 Jul 2026 17:53:02 +0900 Subject: [PATCH 6/6] =?UTF-8?q?fix:=20=EC=84=B8=EC=85=98=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8=20=EC=A4=91=20email=20=ED=8F=BC=20=EC=88=9C=EA=B0=84?= =?UTF-8?q?=20=EB=85=B8=EC=B6=9C=20=EB=B0=A9=EC=A7=80=20(#43)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/signup/ui/SignupView.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/views/signup/ui/SignupView.tsx b/src/views/signup/ui/SignupView.tsx index 0f6b7b7..cf6abc3 100644 --- a/src/views/signup/ui/SignupView.tsx +++ b/src/views/signup/ui/SignupView.tsx @@ -61,6 +61,7 @@ export default function SignupView() { const [error, setError] = useState(null); const [loading, setLoading] = useState(false); const [timer, setTimer] = useState(OTP_SECONDS); + const [checkingSession, setCheckingSession] = useState(true); const timerRef = useRef | null>(null); const { signInWithOAuth, oauthError, oauthLoading } = useOAuthSignIn(); @@ -68,7 +69,10 @@ export default function SignupView() { useEffect(() => { const checkSession = async () => { const { data: { user } } = await supabase.auth.getUser(); - if (!user) return; + if (!user) { + setCheckingSession(false); + return; + } const { data: profile } = await supabase .from('profiles') .select('real_name') @@ -78,6 +82,7 @@ export default function SignupView() { router.push('/workspaces'); } else { setStep('info'); + setCheckingSession(false); } }; checkSession(); @@ -186,6 +191,8 @@ export default function SignupView() { setLoading(false); }; + if (checkingSession) return null; + return (