원티드 프리온보딩 - 로그인 기능 구현하기 3 (실습)
원티드 프리온보딩 - 로그인 기능 구현하기 3강 실습
세션-쿠키를 통해 로그인 기능 구현하기
1. 로그인 실패시 함수 종료. 로그인 성공시 ‘/page-a’로 이동
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// pages/Login.js
const Login = () => {
const { routeTo } = useRouter();
const loginSubmitHandler = async (event) => {
event.preventDefault();
// FormData를 이용해서 로그인 시도
const formData = new FormData(event.currentTarget);
// TODO 3-1.: 로그인 실패시 함수 종료. 로그인 성공시 '/page-a'로 이동
const loginResult = await login({
username: formData.get("username"),
password: formData.get("password")
});
if (!loginResult) return;
routeTo("/page-a");
};
return (
<div className="non-logged-in-body">
<h1>로그인 페이지</h1>
<p>
로그인 성공시 Page A로 이동합니다.
<br />
실패시 alert를 띄웁니다.
</p>
<form onSubmit={loginSubmitHandler}>
<label>
Username:
<input type="text" name="username" />
</label>
<label>
Password:
<input type="password" name="password" />
</label>
<button type="submit" value="Submit">
submit
</button>
</form>
</div>
);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// api/login.js
export const login = async (args) => {
// TODO 3-1: POST, '/auth/login' 호출
// body에는 { username, password }가 들어가야 함
// header가 올바르게 추가된 경우 쿠키는 자동으로 함께 전송됨
const loginResponse = await axios.post(`${BASE_URL}/auth/login`, args, {
headers: { "Content-Type": "application/json", credentials: "include" }
});
if (!loginResponse) {
alert("로그인실패");
return;
}
return loginResponse;
};
2. 로그인 여부에 따라 보여질 Rotuer 설정하기
- 로그인(인증)을 해야 이동이 가능한 페이지는 GeneralLayout으로 감싸기
- 로그인(인증)을 해야 이동이 가능한 페이지만 사이드바에 표시할 수 있게 SidebarContent 만들기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// router.js
const routerData = [
{
id: 0,
path: "/",
label: "Home",
element: <Home />,
withAuth: false
},
{
id: 1,
path: "/login",
label: "로그인",
element: <Login />,
withAuth: false
},
{
id: 2,
path: "/page-a",
label: "page-a",
element: <PageA />,
withAuth: true
},
{
id: 3,
path: "/page-b",
label: "page-b",
element: <PageB />,
withAuth: true
},
{
id: 4,
path: "/page-c",
label: "page-c",
element: <PageC />,
withAuth: true
}
];
// GeneralLayout에는 페이지 컴포넌트를 children 으로 전달
export const routers = createBrowserRouter(
routerData.map((router) => {
if (router.withAuth) {
return {
path: router.path,
element: <GeneralLayout>{router.element}</GeneralLayout>
};
} else {
return {
path: router.path,
element: router.element
};
}
})
);
// TODO 3-2: 라우터 객체에서 인증이 필요한 페이지만 필터링해 사이드바에 전달
// id, path, label을 전달하여 Sidebar에서 사용
export const SidebarContent = routerData.reduce((prev, router) => {
if (!router.withAuth) return prev;
return [...prev, { id: router.id, path: router.path, label: router.label }];
}, []);
3. GeneralLayout 컴포넌트의 Sidebar에 SidebarContent 전달하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// layout/GeneralLayout.js
const GeneralLayout = ({ children }) => {
const [userProfile, setUserProfile] = useState(null);
const { routeTo } = useRouter();
// 중략...
if (!userProfile) return <div>loading...</div>;
return (
<div className="general-layout">
<Sidebar sidebarContent={SidebarContent} userProfile={userProfile} />
<div className="general-layout__body">{children}</div>
</div>
);
};
export default GeneralLayout;
4. 로그인페이지 접속 시 이미 로그인된 유저인지 판별하기
- 로그인된 유저: page-a 페이지로 이동
- 로그인 안된 유저: login page 렌더
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Pages/Login.js
// TODO 3-2.: 이미 로그인된 유저인지 판별
const isLoggedIn = async () => {
const userInfo = getCurrentUserInfo();
userInfo ? true : false;
return false;
};
const Login = () => {
const { routeTo } = useRouter();
// TODO 3-2.: 이미 로그인된 상태라면 page-a로 라우팅
useEffect(() => {
if (isLoggedIn()) {
routeTo("/page-a");
}
}, []);
const loginSubmitHandler = async (event) => {
event.preventDefault();
// FormData를 이용해서 로그인 시도
const formData = new FormData(event.currentTarget);
const loginResult = await login({
username: formData.get("username"),
password: formData.get("password")
});
if (!loginResult) return;
routeTo("/page-a");
};
return <div className="non-logged-in-body">// 중략...</div>;
};
4. 로그인이 필요한 페이지 접속 시 로그인 여부를 확인하기
1
2
3
4
5
6
7
8
9
10
11
12
13
// api/login.js
export const getCurrentUserInfo = async () => {
// TODO 3-2: GET, '/profile' 호출
// 호출 성공시 유저 정보 반환
// header가 올바르게 추가된 경우 쿠키는 자동으로 함께 전송됨
const userInfoResponse = await axios.get(`${BASE_URL}/profile`, {
headers: { "Content-Type": "application/json", credentials: "include" }
});
return userInfoResponse ? userInfoResponse.data : null;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// layout/GeneralLayout.js
const GeneralLayout = ({ children }) => {
const [userProfile, setUserProfile] = useState(null);
const { routeTo } = useRouter();
const fetchUserProfile = useCallback(async () => {
// TODO 3-2: 페이지 이동시 마다 로그인 여부를 확인하는 함수 구현
// 로그인 여부 확인 ('/profile' 호출 성공여부 확인)
// 로그인 성공시 userProfile 상태 업데이트
// 로그인 실패시 로그인 페이지로 이동 ('/login')
const userInfo = await getCurrentUserInfo();
if (!userInfo) {
routeTo("/login");
return;
}
setUserProfile(userInfo);
}, []);
useEffect(() => {
// TODO 3-2: 페이지 이동시 마다 로그인 여부를 확인하는 함수 실행
console.log("page changed!");
fetchUserProfile();
}, [children]);
if (!userProfile) return <div>loading...</div>;
return (
<div className="general-layout">
<Sidebar sidebarContent={SidebarContent} userProfile={userProfile} />
<div className="general-layout__body">{children}</div>
</div>
);
};
export default GeneralLayout;
This post is licensed under CC BY 4.0 by the author.