diff --git a/src/app/(auth)/layout.tsx b/src/app/(auth)/layout.tsx
new file mode 100644
index 0000000..c8cc154
--- /dev/null
+++ b/src/app/(auth)/layout.tsx
@@ -0,0 +1,13 @@
+export default function DashboardLayout({
+ children,
+}: {
+ children: React.ReactNode
+}) {
+ return (
+
+ )
+}
diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx
index 73a37b2..dbb675e 100644
--- a/src/app/(auth)/login/page.tsx
+++ b/src/app/(auth)/login/page.tsx
@@ -1,11 +1,26 @@
'use client'
+import Link from 'next/link'
+import { useEffect, useState } from 'react'
+import AuthLogo from '@/app/features/auth/components/AuthLogo'
import LoginForm from '@/app/features/auth/components/LoginForm'
export default function Login() {
+ const [mounted, setMounted] = useState(false)
+ useEffect(() => setMounted(true), [])
+ if (!mounted) {
+ return null
+ }
return (
<>
+
+
+ 회원이 아니신가요?{' '}
+
+ 회원가입하기
+
+
>
)
}
diff --git a/src/app/features/.gitkeep b/src/app/features/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/src/app/features/auth/api/authApi.ts b/src/app/features/auth/api/authApi.ts
index 4902afa..b5203fc 100644
--- a/src/app/features/auth/api/authApi.ts
+++ b/src/app/features/auth/api/authApi.ts
@@ -1,8 +1,9 @@
import api from '@/app/shared/lib/axios'
-import { AUTH_ENDPOINT } from './authEndpoint'
-import { LoginRequest, SignupRequest, LoginResponse } from '../types/auth.type'
import { User as SignupResponse } from '@/app/shared/types/user.type'
+import { LoginRequest, LoginResponse, SignupRequest } from '../types/auth.type'
+import { AUTH_ENDPOINT } from './authEndpoint'
+
export const login = async (data: LoginRequest): Promise => {
const response = await api.post(AUTH_ENDPOINT.LOGIN, data)
return response.data
diff --git a/src/app/features/auth/components/LoginForm.tsx b/src/app/features/auth/components/LoginForm.tsx
index 5b1fe8b..4c178d3 100644
--- a/src/app/features/auth/components/LoginForm.tsx
+++ b/src/app/features/auth/components/LoginForm.tsx
@@ -4,7 +4,7 @@ import Input from '@components/Input'
import { cn } from '@lib/cn'
import { useForm } from 'react-hook-form'
-import { useLoginSubmit } from '../hooks/useLoginSubmit'
+import { useLoginMutation } from '../hooks/useLoginMutation'
import { loginValidation } from '../schemas/loginValidation'
import { LoginRequest } from '../types/auth.type'
@@ -12,7 +12,7 @@ export default function LoginForm() {
const {
register,
handleSubmit,
- formState: { errors, isSubmitting, isValid },
+ formState: { errors, isValid },
} = useForm({
mode: 'onChange',
defaultValues: {
@@ -21,19 +21,20 @@ export default function LoginForm() {
},
})
- const { submit } = useLoginSubmit()
- const showEmailError = !!errors.email
- const showPasswordError = !!errors.password
+ const { mutate: loginMutate, isPending } = useLoginMutation()
return (
-
)
diff --git a/src/app/features/auth/hooks/useAuth.ts b/src/app/features/auth/hooks/useAuth.ts
index d4d1c1e..ecda0ec 100644
--- a/src/app/features/auth/hooks/useAuth.ts
+++ b/src/app/features/auth/hooks/useAuth.ts
@@ -1,12 +1,13 @@
-import { login as loginApi, signup as signupApi } from '../api/authApi'
+import { User } from '@/app/shared/types/user.type'
+
+import { signup as signupApi } from '../api/authApi'
import { useAuthStore } from '../store/useAuthStore'
-import { LoginRequest, SignupRequest } from '../types/auth.type'
+import { SignupRequest } from '../types/auth.type'
export function useAuth() {
const { setAccessToken, setUser, clearAuthState } = useAuthStore()
- async function login(data: LoginRequest) {
- const response = await loginApi(data)
+ function updateAuthState(response: { accessToken: string; user: User }) {
const { accessToken, user } = response
if (!accessToken || !user) {
@@ -27,7 +28,7 @@ export function useAuth() {
}
return {
- login,
+ updateAuthState,
signup,
logout,
}
diff --git a/src/app/features/auth/hooks/useLoginMutation.ts b/src/app/features/auth/hooks/useLoginMutation.ts
new file mode 100644
index 0000000..c7bfeee
--- /dev/null
+++ b/src/app/features/auth/hooks/useLoginMutation.ts
@@ -0,0 +1,35 @@
+import { showError, showSuccess } from '@lib/toast'
+import { useMutation } from '@tanstack/react-query'
+import type { AxiosError } from 'axios'
+import axios from 'axios'
+import { useRouter } from 'next/navigation'
+
+import { login } from '../api/authApi'
+import { LoginRequest, LoginResponse } from '../types/auth.type'
+import { useAuth } from './useAuth'
+
+export function useLoginMutation() {
+ const router = useRouter()
+ const { updateAuthState } = useAuth()
+
+ return useMutation({
+ mutationFn: login,
+ onSuccess: async (response) => {
+ updateAuthState(response)
+ showSuccess('로그인에 성공하셨습니다!')
+ await new Promise((resolve) => setTimeout(resolve, 400))
+ router.push('/mydashboard')
+ },
+ onError: (error) => {
+ if (axios.isAxiosError(error)) {
+ const severMessage = (
+ error.response?.data as { message?: string } | undefined
+ )?.message
+ const fallback = error.message || '로그인 실패'
+ showError(severMessage ?? fallback)
+ } else {
+ showError('알 수 없는 에러 발생')
+ }
+ },
+ })
+}
diff --git a/src/app/features/auth/hooks/useLoginSubmit.ts b/src/app/features/auth/hooks/useLoginSubmit.ts
deleted file mode 100644
index da476f9..0000000
--- a/src/app/features/auth/hooks/useLoginSubmit.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import axios from 'axios'
-import { useRouter } from 'next/navigation'
-import { toast } from 'sonner'
-
-import { LoginRequest } from '../types/auth.type'
-import { useAuth } from './useAuth'
-
-export function useLoginSubmit() {
- const { login } = useAuth()
- const router = useRouter()
-
- async function submit(data: LoginRequest) {
- try {
- await login(data)
- toast.success('로그인 성공')
- router.push('/mydashboard')
- } catch (e: unknown) {
- if (axios.isAxiosError(e)) {
- const message = e.response?.data?.message
- toast.error(message ?? '로그인 실패')
- } else {
- toast.error('알 수 없는 에러 발생')
- }
- }
- }
-
- return { submit }
-}
diff --git a/src/app/globals.css b/src/app/globals.css
index f03d8e3..c024759 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -67,7 +67,9 @@ body {
.BG-ThemeToggleButton {
@apply bg-[#FFFFFF] dark:bg-[#D3D3D3];
}
-
+.Text-violet {
+ @apply text-[#228DFF];
+}
.BG-drag-hovered {
@apply bg-blue-100;
}