Skip to content

Commit bd20cf6

Browse files
committed
refactor: 로그인 대규모 리팩토링
- 타입 추가 - react query 사용 - api가 be에서 받은 error를 던지고 LoginForm 띄우도록 수정
1 parent 126b642 commit bd20cf6

6 files changed

Lines changed: 113 additions & 61 deletions

File tree

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,38 @@
1-
export async function login({
2-
username,
3-
password,
4-
}: { username: string; password: string }) {
1+
import type {
2+
LoginRequest,
3+
LoginResponse,
4+
RegisterRequest,
5+
RegisterResponse,
6+
} from '../model/auth.types';
7+
8+
export async function register(
9+
request: RegisterRequest,
10+
): Promise<RegisterResponse['data']> {
11+
const res = await fetch('/api/auth/register', {
12+
method: 'POST',
13+
headers: { 'Content-Type': 'application/json' },
14+
body: JSON.stringify(request),
15+
});
16+
17+
const result: RegisterResponse = await res.json();
18+
if (!result.success) {
19+
throw new Error(result.error.message);
20+
}
21+
return result.data;
22+
}
23+
24+
export async function login(
25+
request: LoginRequest,
26+
): Promise<LoginResponse['data']> {
527
const res = await fetch('/api/auth/login', {
628
method: 'POST',
729
headers: { 'Content-Type': 'application/json' },
8-
body: JSON.stringify({ username, password }),
30+
body: JSON.stringify(request),
931
});
10-
if (!res.ok) throw new Error('로그인 실패');
11-
return res.json(); // { token, user }
32+
33+
const result: LoginResponse = await res.json();
34+
if (!result.success) {
35+
throw new Error(result.error.message);
36+
}
37+
return result.data;
1238
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useMutation } from '@tanstack/react-query';
2+
import { useNavigate } from 'react-router-dom';
3+
import { login } from '../api/authApi';
4+
import type { LoginRequest, LoginResponse } from '../model/auth.types';
5+
6+
export function useLogin(onSuccess?: (data: LoginResponse['data']) => void) {
7+
const navigate = useNavigate();
8+
const { mutate, isPending, isSuccess, isError, data, error, reset } =
9+
useMutation({
10+
mutationFn: (payload: LoginRequest) => login(payload),
11+
onSuccess: (data) => {
12+
if (data?.accessToken) {
13+
localStorage.setItem('token', data.accessToken);
14+
navigate('/');
15+
}
16+
onSuccess?.(data);
17+
},
18+
});
19+
20+
return {
21+
mutate,
22+
isPending,
23+
isSuccess,
24+
isError,
25+
data,
26+
error,
27+
reset,
28+
};
29+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// 회원가입 요청
2+
export interface RegisterRequest {
3+
username: string;
4+
email: string;
5+
imageUrl: string;
6+
password: string;
7+
}
8+
9+
// 회원가입 응답
10+
export interface RegisterResponse {
11+
success: boolean;
12+
data: string | null;
13+
error: {
14+
message: string;
15+
code: number;
16+
};
17+
}
18+
19+
// 로그인 요청
20+
export interface LoginRequest {
21+
email: string;
22+
password: string;
23+
}
24+
25+
// 로그인 응답
26+
export interface LoginResponse {
27+
success: boolean;
28+
data: {
29+
accessToken: string;
30+
};
31+
error: {
32+
message: string;
33+
code: number;
34+
};
35+
}

frontend/src/features/auth/model/useAuth.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

frontend/src/features/auth/ui/LoginForm.tsx

Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,51 @@
11
import { Input } from '@/shared/ui/Input';
22
import { Button } from '@/shared/ui/button';
33
import { useState } from 'react';
4-
import { useAuth } from '../model/useAuth';
4+
import { useLogin } from '../hooks/useLogin';
55

66
export function LoginForm() {
7-
const { login } = useAuth();
8-
const [username, setUsername] = useState('');
7+
const { mutate, isPending, isError, error } = useLogin();
8+
const [email, setEmail] = useState('');
99
const [password, setPassword] = useState('');
10-
const [error, setError] = useState<string | null>(null);
11-
const [loading, setLoading] = useState(false);
1210

13-
const handleSubmit = async (e: React.FormEvent) => {
11+
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
1412
e.preventDefault();
15-
setError(null);
16-
setLoading(true);
17-
try {
18-
await login(username, password);
19-
// 성공시 라우팅 처리 필요하면 여기서!
20-
} catch (err) {
21-
setError('아이디 또는 비밀번호가 올바르지 않습니다.');
22-
} finally {
23-
setLoading(false);
24-
}
13+
mutate({ email, password });
2514
};
2615

2716
return (
2817
<form className='flex flex-col gap-4 w-full' onSubmit={handleSubmit}>
2918
<Input
30-
label='아이디'
31-
placeholder='아이디'
32-
value={username}
33-
onChange={(e) => setUsername(e.target.value)}
34-
minLength={6}
35-
maxLength={16}
19+
label='이메일'
20+
placeholder='이메일'
21+
value={email}
22+
onChange={(e) => setEmail(e.target.value)}
3623
required
37-
autoComplete='username'
24+
autoComplete='email'
3825
/>
3926
<Input
4027
label='비밀번호'
4128
placeholder='비밀번호'
4229
value={password}
4330
onChange={(e) => setPassword(e.target.value)}
44-
minLength={6}
45-
maxLength={20}
31+
type='password'
4632
required
4733
autoComplete='current-password'
4834
/>
49-
{error && (
35+
{isError && error instanceof Error && (
5036
<div className='text-[var(--danger-text-default)] font-display-medium-16 px-2'>
51-
{error}
37+
{error.message}
5238
</div>
5339
)}
5440
<Button
5541
variant='contained'
5642
type='submit'
5743
size='lg'
58-
disabled={loading || !username || !password}
44+
disabled={isPending || !email || !password}
5945
className='w-full'
6046
>
6147
<span className='font-available-medium-20'>
62-
{loading ? '로그인 중...' : '아이디로 로그인'}
48+
{isPending ? '로그인 중...' : '이메일로 로그인'}
6349
</span>
6450
</Button>
6551
</form>

frontend/src/features/auth/ui/LogoutButton.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import { LogOut } from 'lucide-react';
22
import { useNavigate } from 'react-router-dom';
3-
import { useAuth } from '../model/useAuth';
43

54
export function LogoutButton() {
6-
const { logout } = useAuth();
75
const navigate = useNavigate();
86

97
const handleLogout = () => {
10-
logout();
8+
localStorage.removeItem('token');
119
navigate('/login', { replace: true });
1210
};
1311

0 commit comments

Comments
 (0)