-
15. Logout사이드프로젝트 2024. 7. 26. 01:36
https://www.youtube.com/watch?v=-xXASlyU0Ck&t=2007s
를 보며 정리하며 쓰는 글
---
저번시간에 이어 로그아웃을 만든다.
현재 로그아웃을 해도 로그아웃 버튼이 로그인 버튼으로 바뀌기만 할뿐, 나머지 화면은 그대로다. 이걸바꿔줘보자.
가장먼저 지난시간에 presence로 현재 접속중인 유저들이 몇명인지를 표시해줬는데, 로그아웃을 해도 상태가 그대로다.
이건 왜그러냐면 로그아웃을 하는 components/ChatHeader.tsx로 가보면,
await supabase.auth.sighOut()을 하고 router.refresh()를 해주고 있긴 하다.
하지만 현재 user의 상태는 zustand로 전역적으로 관리해주고 있다. 이거에 대한 별다른 조치를 취해주고 있지 않기때문에 여전히 유저가 있는 것처럼 나오는 것이다.
lib/store/user.ts 로 이동해서 user를 셋해주는 메서드를 하나 만들어주자.
// lib/store/user.ts import { User } from "@supabase/supabase-js"; import { create } from "zustand"; interface UserState { user: User | null; setUser: (user: User | null) => void; } export const useUser = create<UserState>()((set) => ({ user: null, // default state setUser: (newUser) => set((state) => ({ user: newUser })), }));
그리고 다시 components/ChatHeader.tsx로 이동해 로그아웃 할때 방금 만든 setUser로, 전역적으로 관리되고 있는 user객체를 null로 만들어 준다.
// components/ChatHeader.tsx "use client"; import React from "react"; import { Button } from "./ui/button"; import { createClient } from "@/utils/supabase/client"; import { User } from "@supabase/supabase-js"; import { useRouter } from "next/navigation"; // 주의할 점. next/navigation에서 임포트한다. import ChatPresence from "./ChatPresence"; import { useUser } from "@/lib/store/user"; const ChatHeader = ({ user }: { user: User | null }) => { const router = useRouter(); const setUser = useUser((state) => state.setUser); // setUser 메서드를 가져온다 ******* // props로 UserResponse를 받는다. const handleLoginWithGithub = () => { const supabase = createClient(); supabase.auth.signInWithOAuth({ provider: "github", options: { redirectTo: location.origin + "/auth/callback", }, }); }; const handleLogout = async () => { const supabase = createClient(); await supabase.auth.signOut(); // const { error } = await supabase.auth.signOut(); user = null; setUser(null); // user를 null로 만들어준다. ********************** router.refresh(); }; return ( <div className="h-20"> <div className="p-5 border-b flex items-center justify-between h-full"> <div> <h1 className="text-xl font-bold">Daily Chat</h1> <ChatPresence /> </div> {user ? ( // user가 있으면 (로그인했으면) 로그아웃 버튼 표시, 없으면 (로그인 안했으면) 로그인 버튼 표시 <Button onClick={handleLogout}>Logout</Button> ) : ( <Button onClick={handleLoginWithGithub}>Login</Button> )} </div> </div> ); }; export default ChatHeader;
그리고 나서 다시 로그아웃을 해봐도, 아직은 유저가 2명으로 유지되는 걸 볼 수 있는데, 만약 유저가 3명이고 2명이 로그아웃을 했다면 2명이고, 100명이고 99명이 로그아웃을 했어도 2명으로 유지될거다.
그 이유는 components/ChatPresence.tsx에서, 유저가 몇명인지 세고, 중복을 제거하는 부분에서 null이나 Undefined도 한자리를 차지하고 있기 때문이다. 이부분을 user_id가 false값이면 안들어가게끔 해준다.
components/ChatPresence.tsx channel .on("presence", { event: "sync" }, () => { // channel.presenceState()의 type은 '{ presence_ref: string; }'만 되어있기 때문에, 추가로 user_id도 타입으로 설정해준다. const newState: RealtimePresenceState<{ user_id: string }> = channel.presenceState(); console.log("sync : ", newState); const userIds = []; for (const item in newState) { const userId = newState[item][0].user_id; if (!userId) { continue; // userId가 없다면 넘겨준다. } userIds.push(userId); } console.log("userIDs : ", userIds); setOnlineUsers(new Set(userIds).size); })
이렇게 해주고 새로고침 하고 실험해보면, 로그인 된 유저수만 세는 걸 볼 수 있다.
이제 로그아웃 하면 화면 바꾸는 걸 해주자. (로그인이 안됬을 때 화면)
먼저 컴포넌트를 만들어준다.
components/ChatAbout.tsx
// components/ChatAbout.tsx import React from "react"; const ChatAbout = () => { return ( <div className="flex-1 flex items-center justify-center"> <div className="text-center space-y-5"> <h1 className="text-3xl font-bold">Welcome to Datil Chat</h1> <p className="w-96"> 환영합니다! Please Login to send Message. Powered by NextJs + Supabase </p> </div> </div> ); }; export default ChatAbout;
그리고 app/page.tsx로 이동해, 로그인한 유저 정보가 있다면 기존의 채팅 화면을, 없다면 위에서 만든 화면을 보여주도록 하자.
// app/page.tsx import ChatAbout from "@/components/ChatAbout"; import ChatHeader from "@/components/ChatHeader"; import { ChatInput } from "@/components/ChatInput"; import ChatMessages from "@/components/ChatMessages"; import ListMessages from "@/components/ListMessages"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import InitUser from "@/lib/store/InitUser"; import { createClient } from "@/utils/supabase/server"; // supabase 객체 불러오기. // 서버 컴포넌트니깐 서버에서 임포트해온다. import React from "react"; const page = async () => { const supabase = createClient(); // supabase 객체 불러오기. // const session = await supabase.auth.getSession(); // console.log(session.data.session?.user); const user = (await supabase.auth.getUser()).data.user; // console.log("user: ", user); return ( <> <div className="max-w-3xl mx-auto md:py-10 h-screen"> <div className="h-full border rounded-md flex flex-col relative"> <ChatHeader user={user} /> {user ? ( // 유저정보가 있다면 기존의 채팅화면을 <> <ChatMessages /> <ChatInput /> </> ) : ( // 없다면 이화면 <ChatAbout /> )} </div> </div> <InitUser user={user} /> {/* user state 관리를 시작할 수 있도록. */} </> ); }; export default page;
github : https://github.com/Wunhyeon/Next-Supabase-Chat/tree/15.Logout
'사이드프로젝트' 카테고리의 다른 글
17. 앞으로 개선사항 (0) 2024.07.29 16. 배포 (0) 2024.07.26 14. Presence (현재 접속자) (0) 2024.07.25 13. Pagination (0) 2024.07.24 한글 두번씩 입력되는 버그해결 (2) 2024.07.24