๐ Task TODOLIST
- [x] ์ฑํ ๋ฐฉ ๋๊ฐ๊ธฐ ๋ฒํผ
- [x] ์ฑํ ๋ฐฉ ๋๊ฐ ์ ์ฒ๋ฆฌ("chattng_room" ํ ์ด๋ธ ๋ณ๊ฒฝ, "room" ํ ์ด๋ธ ๋ณ๊ฒฝ, "participants" ํ ์ด๋ธ ๋ณ๊ฒฝ, "remember_last_msg" ํ ์ด๋ธ ๋ณ๊ฒฝ, storage์์ ํด๋น ์ ์ ์ ์ด๋ฏธ์ง๋ค ์ญ์ )
- [x] ๋จ์์๋ ์ฌ๋๋ค์ ๋ํ ์ฒ๋ฆฌ
โจ ๊ฐ๋ฐ ๋ด์ฉ
1. ์ฑํ ๋ฐฉ ๋๊ฐ ์ ์ฒ๋ฆฌ
- "chattng_room" ํ ์ด๋ธ ๋ณ๊ฒฝ
- "room" ํ ์ด๋ธ ๋ณ๊ฒฝ
- "participants" ํ ์ด๋ธ ๋ณ๊ฒฝ
- "remember_last_msg" ํ ์ด๋ธ ๋ณ๊ฒฝ
- storage์์ ํด๋น ์ ์ ์ ์ด๋ฏธ์ง๋ค ์ญ์
Why(์ด์ ) ?
ํ ๋ช ์ด ์ฑํ ๋ฐฉ์ ๋๊ฐ๋ฉด ํด์ผํ ์ฒ๋ฆฌ๋ค์ด ์ ๋ง ๋ง๋ค. ๋์ดํด๋ณด์๋ฉด
- "chatting_room" table์์ ํด๋น ์ฑํ ๋ฃธ์ isActive ์ํ๋ฅผ false๋ก ๋ง๋ค์ด์ค์ผ ํ๊ณ
- "room" table์์ ํด๋น ๋ฐฉ์ ๋ฆฌ๋ ๋ณ๊ฒฝ + room_status๋ฅผ "๋ชจ์ง์ค"์ผ๋ก ๋ณ๊ฒฝํด์ผํ๊ณ
- "participants" table์์ ๋๊ฐ๊ธฐ๋ฅผ ๋๋ฅธ ์ ์ ์ isDeleted ์์ฑ์ true๋ก ๋ณ๊ฒฝํ๊ณ
- ๊ทธ ๋ค์(๋น๋๊ธฐ ๋ก์ง์ ๋๊ธฐ์ฒ๋ฆฌ) ๋จ์์๋ ์ฌ๋๋ค์ด ๋๊ตฌ์ธ์ง ํ์ธํ๊ณ
- "remeber_last_msg" table์์ ๋๊ฐ๊ธฐ๋ฅผ ๋๋ฅธ ์ ์ ๊ฐ ์ฝ์ ํด๋น ์ฑํ ๋ฐฉ์ ๋ง์ง๋ง ๋ฉ์ธ์ง๋ฅผ ์ญ์ ํด์ผ ํ๊ณ
- storage์ ํด๋น ์ ์ ๊ฐ ์ฑํ ๋ฐฉ์ ์ฌ๋ ธ๋ ์ด๋ฏธ์ง๋ค์ ์ญ์ ํด์ผํ๋ค.(๋ฉ๋ชจ๋ฆฌ ์ต์ ํ)
How(๊ณผ์ ) ?
๊ฐ ๋ก์ง์ ๋ค์๊ณผ ๊ฐ๋ค.
// ์ฑํ
๋ฐฉ isActive ์ํ๋ฅผ false๋ก ๋ณ๊ฒฝ
const updateIsActiveFalse = async () => {
const { error: updateActiveErr } = await clientSupabase
.from('chatting_room')
.update({ isActive: false })
.eq('chatting_room_id', chatRoomId);
if (updateActiveErr) {
alert('์ฑํ
๋ฐฉ ๋นํ์ฑํ์ ์คํจํ์์ต๋๋ค.');
console.error(updateActiveErr.message);
}
};
// room ํ
์ด๋ธ์์ ๋ฆฌ๋ ๋ณ๊ฒฝ + room_status: "๋ชจ์ง์ค" + participants ํ
์ด๋ธ์์ ํด๋น ๋ฃธ์ ๋ํ ์ ์ ์ ๋ณด isDeleted: true๋ก
const getRidOfMe = async () => {
if (user?.user_id === leader_id) {
const { error: updateLeaderErr } = await clientSupabase
.from('room')
.update({
leader_id: participants?.find((person) => person.user_id !== user?.user_id)?.user_id,
room_status: '๋ชจ์ง์ค'
})
.eq('room_id', room_id);
if (updateLeaderErr) console.error('fail to update leader of room', updateLeaderErr.message);
}
const { error: deleteErr } = await clientSupabase
.from('participants')
.update({ isDeleted: true })
.eq('room_id', room_id)
.eq('user_id', user?.user_id!);
if (deleteErr) {
console.error(deleteErr.message);
alert('์ฑํ
๋ฐฉ ๋๊ฐ๊ธฐ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ์์ต๋๋ค.');
}
};
// ๋จ์์๋ ์ฌ๋์ธ์ง ๋๊ฐ์ฌ๋์ธ์ง isRest ์ํ๋ณ๊ฒฝ์ผ๋ก ํ๋ฉด ๋ ๋๋ง ๋ฐ๊พธ๋ ํจ์
const handleIsRest = async () => {
const { data: restOf, error: getPartErr } = await clientSupabase
.from('participants')
.select('user_id')
.eq('room_id', room_id)
.eq('isDeleted', false);
if (getPartErr) {
console.error(getPartErr.message);
alert('์ฐธ๊ฐ์๋ค ์ ๋ณด๋ฅผ ๋ถ๋ฌ์ค๋ ๋ฐ ์คํจํ์ต๋๋ค.');
} else {
const restArr = restOf.map((r) => r.user_id);
setisRest(restArr.includes(user?.user_id!) as boolean);
}
};
// ๋ง์ง๋ง ๋ฉ์ธ์ง ์ญ์
const deleteLastMsg = async () => {
const { error } = await clientSupabase.from('remember_last_msg').delete().eq('chatting_room_id', chatRoomId);
if (error) console.error('remember_last_msg table์ ํด๋น ์ฑํ
๋ฐฉ ๊ด๋ จ ์ ๋ณด ์ญ์ ์คํจ');
};
// ํด๋น ์ ์ ๊ฐ ๋จ๊ธด ์ฑํ
์ฐฝ์ ์ด๋ฏธ์ง๋ค ์ง์ฐ๊ธฐ
const deleteTheUserImgs = async () => {
const { error: imgStorageErr, data: usersAllImgList } = await clientSupabase.storage
.from('chatImg')
.list(`${chatRoomId}/${user?.user_id}`);
if (imgStorageErr) {
console.error('storage remove fail', imgStorageErr.message);
} else {
const filesToRemove = usersAllImgList.map((x) => `${chatRoomId}/${user?.user_id}/${x.name}`);
if (filesToRemove && filesToRemove.length) {
const { error: deleteFilesErr } = await clientSupabase.storage.from('chatImg').remove(filesToRemove);
deleteFilesErr && console.error('fail to delete list of the folder', deleteFilesErr.message);
const { error: deleteFolderErr } = await clientSupabase.storage
.from('chatImg')
.remove([`${chatRoomId}/${user?.user_id}`]);
deleteFolderErr && console.error("fail to delete the user's folder of storage", deleteFolderErr.message);
}
}
};
const getOutOfChatRoom = async () => {
const message = `ํ๋ช
์ด๋ผ๋ ๋๊ฐ๋ฉด ์ฑํ
๋ฐฉ์ด ์ข
๋ฃ๋ฉ๋๋ค.
ํ ๋ฒ ๋๊ฐ๋ฉด ๋ค์ ์
์ฅํ์ค ์ ์์ต๋๋ค.
๊ทธ๋๋ ๋๊ฐ์๊ฒ ์ต๋๊น?`;
openModal({
type: 'confirm',
name: '',
text: message,
onFunc: async () => {
closeModal();
await updateIsActiveFalse();
await getRidOfMe();
await handleIsRest();
deleteLastMsg();
deleteTheUserImgs();
queryClient.removeQueries({ queryKey: [MSGS_QUERY_KEY, chatRoomId], exact: true });
},
onCancelFunc: () => {
closeModal();
}
});
};
๋ก์ง ์ค์๋ ๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌ๋์ด์ผ ํ ๋ก์ง๋ค์ด ์์ด์ ํ์ํ ๋ก์ง์ await๋ก ์ฒ๋ฆฌํด์ฃผ์๋ค.
2. ๋๊ฐ ์ฌ๋๊ณผ, ๋จ์์๋ ์ฌ๋๋ค์ ๊ตฌ๋ถํด์ ๋ผ์ฐํธ ๋ณ๊ฒฝ
Why(์ด์ ) ?
๋๊ฐ ์ฌ๋๊ณผ ๋จ์์๋ ์ฌ๋์ ๋ํ ์ฒ๋ฆฌ๋ฅผ ๊ณ ๋ฏผํ๋ค.
์ฑํ ์ค ์ด๋ค ์ฌ๋์ด ๋๊ฐ๋๋ผ๋ ๋๊ฐ ์ฌ๋๋ง ๋นผ๊ณ ์ฑํ ๋ฐฉ์ ๊ณ์ ์ด์ด๋๊ฐ๊น?
ํ์ง๋ง, ๊ทธ๋ ๊ฒ ๋๋ค๋ฉด ๋ง์ฝ ์๋ก์ด ์ฌ๋์ด ๋ค์ด์ค๊ฒ ๋๋ฉด ๊ทธ ์ฌ๋์ ์ด์ ์ฑํ ๋ค์ ๋ค ๋ณผ ์ ์์ํ ๋ฐ ๋ญ๊ฐ ๋๊ฐ ์ฌ๋์ ๋ํ ๊ฐ์ธ์ ๋ณด๋ฅผ ๋ถํ์ํ๊ฒ ๋ด์ผํ ์๋ ์๊ณ , ๋์ฒด๋ ๋๋์ด๋ผ ๊ธฐ๋ถ์ด ๋์ ์๋ ์๊ณ , ๋ฌด์๋ณด๋ค ์ฌ๋์ด ๋๊ฐ๋๋ผ๋ ์ฑํ ๋ฐฉ์ด ๊ณ์ ์ ์ง๋๋ ๊ฒ์ด user๋ค์๊ฒ ์ ์ฉ๋ ์ ์๋ค๋ ์๊ฐ์
=> ํ ๋ช ์ด๋ผ๋ ๋๊ฐ๋ฉด ํด๋น ์ฑํ ๋ฐฉ์ ์ข ๋ฃํด๋ฒ๋ฆฌ์! ์๋ก ๋๊ตฐ๊ฐ ๋ค์ด์ค๋ฉด ์๋ก์ด ์ฑํ ๋ฐฉ์ ์์ํ์.
๋ผ๋ ๊ฒฐ๋ก ์ ๋๋ฌํ๋ค.
๊ทธ๋์ ์๊ฐํด๋ณด๋,
๋๊ฐ ์ฌ๋์ ๊ทธ๋ฅ ๋ก๋น๋ก ๊ฐ๋ฉด ๋๋๋ฐ, ๋จ์์๋ ์ฌ๋๋ค์ ๊ฐ์๊ธฐ ๋๊ฐ ๋๊ฐ๋ค๊ณ ๊ฐ์ด ๋ก๋น๋ก ํ๊ฒจ์ง๋ ๊ฒ UX ์ ๋๋ฌด ํฉ๋น๋นํ ๊ฒ ๊ฐ์๋ค.
How(๊ณผ์ ) ?
์ฑํ ๋ฐฉ์ ๋๋ฅธ ์ฌ๋์ธ์ง, ๋จ์์๋ ์ฌ๋์ธ์ง ๊ตฌ๋ถํ ํ์๊ฐ ์์๋ค.
ํ์ฌ, A๋ผ๋ room_id์ ๋ฐฉ์์ ๋๊ฐ๊ธฐ๋ฅผ ๋๋ฅธ ์ฌ๋์ "participants" table์์ isDeleted๋ฅผ true๋ก ๋ฐ๊พธ๋ ์์ฒญ์ ๋จผ์ ๋ณด๋ด๊ณ (์ ํ), ๋จ์์๋ ์ฌ๋๋ค์ด ๋๊ตฌ์ธ์ง๋ฅผ ์์๋ณด๋ ์์ฒญ์ ํ์ ๋ณด๋์ผ๋ก์จ(ํํ) ๋๊ฐ๊ธฐ๋ฅผ ๋๋ฅธ ์ฌ๋๊ณผ ๋๋จธ์ง ์ฌ๋๋ค์ ๊ตฌ๋ถํ๋ค.
๋ํ, ๊ทธ ๋ ๊ทธ๋ฃน์๊ฒ ๋ค๋ฅธ ๋ผ์ฐํธ ๋ณ๊ฒฝ์ ์ ๊ณตํ๊ธฐ ์ํด์ isRest ์ํ๋ฅผ ๋ง๋ค์๋ค.
๋จ์์๋ ์ฌ๋๋ค์ด ๋๊ตฌ์ธ์ง๋ฅผ ์์๋ณด๋ ์์ฒญ์ ๋ํ ์๋ต์ ์ด๋ฆ์ด ์๋ค๋ฉด isRest๋ฅผ true๋ก,
์ด๋ฆ์ด ์๋ค๋ฉด ๋๊ฐ๊ธฐ๋ฅผ ๋๋ฅธ ์ฌ๋์ด๋ฏ๋ก isRest๋ฅผ false๋ก set ํ๊ณ , ์ด ์ํ๋ฅผ ์์ ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ๊ธฐ ์ํด์ zustand store์ ์ ์ฅํ๋ค.
๊ทธ๋ฐ ๋ค์ chatPage์ ๊ฐ์ฅ ์๋จ์์ ํด๋น ์ฑํ ๋ฐฉ์ is_Active ์ํ๊ฐ ๋ณ๊ฒฝ๋๋ ๊ฒ์ supabase-realtime์ผ๋ก ์ค์๊ฐ ๊ตฌ๋ ํ ๋ค, is_Active๊ฐ false๋ผ๋ฉด, ๋ด๊ฐ ๋จ์์๋ ์ ์ ์ธ์ง ์์๋ณผ ์ ์๋ isRest ์ํ์ ๋ฐ๋ผ ๋ผ์ฐํธ๊ฐ ๋ณ๊ฒฝ๋๋๋ก ํ๋ค.
what(๊ฒฐ๊ณผ) ?
์์ ์ฝ๋์ ์ค๋ณต๋๋ ์ฝ๋์ธ๋ฐ, ๋ฐ๋ก ์ด๊ฒ์ ์ํ ์ฝ๋๋ค์ด์๊ณ
// room ํ
์ด๋ธ์์ ๋ฆฌ๋ ๋ณ๊ฒฝ + room_status: "๋ชจ์ง์ค" + participants ํ
์ด๋ธ์์ ํด๋น ๋ฃธ์ ๋ํ ์ ์ ์ ๋ณด isDeleted: true๋ก
const getRidOfMe = async () => {
if (user?.user_id === leader_id) {
const { error: updateLeaderErr } = await clientSupabase
.from('room')
.update({
leader_id: participants?.find((person) => person.user_id !== user?.user_id)?.user_id,
room_status: '๋ชจ์ง์ค'
})
.eq('room_id', room_id);
if (updateLeaderErr) console.error('fail to update leader of room', updateLeaderErr.message);
}
const { error: deleteErr } = await clientSupabase
.from('participants')
.update({ isDeleted: true })
.eq('room_id', room_id)
.eq('user_id', user?.user_id!);
if (deleteErr) {
console.error(deleteErr.message);
alert('์ฑํ
๋ฐฉ ๋๊ฐ๊ธฐ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ์์ต๋๋ค.');
}
};
// ๋จ์์๋ ์ฌ๋์ธ์ง ๋๊ฐ์ฌ๋์ธ์ง isRest ์ํ๋ณ๊ฒฝ์ผ๋ก ํ๋ฉด ๋ ๋๋ง ๋ฐ๊พธ๋ ํจ์
const handleIsRest = async () => {
const { data: restOf, error: getPartErr } = await clientSupabase
.from('participants')
.select('user_id')
.eq('room_id', room_id)
.eq('isDeleted', false);
if (getPartErr) {
console.error(getPartErr.message);
alert('์ฐธ๊ฐ์๋ค ์ ๋ณด๋ฅผ ๋ถ๋ฌ์ค๋ ๋ฐ ์คํจํ์ต๋๋ค.');
} else {
const restArr = restOf.map((r) => r.user_id);
setisRest(restArr.includes(user?.user_id!) as boolean);
}
};
// chatPage์ ๊ฐ์ฅ ์๋จ ์ปดํฌ๋ํธ InitChat.tsx
'use client';
const InitChat = ({ user, chatRoomId }: { user: User | null; chatRoomId: string }) => {
const { chatState, isRest, setChatState, setisRest, setChatRoomId, setHasMore } = chatStore((state) => state);
const {
room: { room_id, leader_id }
} = useRoomDataQuery(chatRoomId);
const router = useRouter();
const queryClient = useQueryClient();
const allMsgs = useMsgsQuery(chatRoomId);
useEffect(() => {
// ์ฑํ
๋ฐฉ isActive ์ํ ๊ตฌ๋
const channel = clientSupabase
.channel(`${chatRoomId}_chatting_room_table`)
.on(
'postgres_changes',
{ event: 'UPDATE', schema: 'public', table: 'chatting_room', filter: `chatting_room_id=eq.${chatRoomId}` },
(payload) => {
setChatState((payload.new as chatRoomType).isActive);
if (user?.id !== leader_id) {
queryClient.invalidateQueries({
queryKey: [CHATDATA_QUERY_KEY]
});
}
}
)
.subscribe();
return () => {
clientSupabase.removeChannel(channel);
};
}, []);
useEffect(() => {
// **์ฑํ
๋ฐฉ์ ์์์ง ๋ง์ง
if (!chatState) {
// ํ ๋ช
์ด ์ฑํ
๋ฐฉ์ ๋๊ฐ์ ์ฑํ
๋ฐฉ isActive๊ฐ false๊ฐ ๋๋ฉด,
if (isRest) {
// ๋ด๊ฐ ๋๊ฐ๊ธฐ๋ฅผ ๋๋ฅธ ์ฌ๋์ด ์๋๋ผ๋ฉด(๋จ์ ์ฌ๋์ด๋ฉด) ๋ค์ ์๋ฝ์ฐฝ์ผ๋ก
router.push(`/meetingRoom/${room_id}`);
} else {
// ๋ด๊ฐ ๋๊ฐ๊ธฐ๋ฅผ ๋๋ฅธ ์ฌ๋์ด๋ผ๋ฉด ์์ ๋ก๋น๋ก
router.push('/meetingRoom');
}
setChatState(true);
setisRest(true);
} else {
// **์ฑํ
๋ฐฉ์ ์๋๋ค๋ฉด
setChatRoomId(chatRoomId);
if (allMsgs) {
queryClient.setQueryData([MSGS_QUERY_KEY, chatRoomId], [...allMsgs].reverse());
setHasMore(allMsgs.length >= ITEM_INTERVAL + 1);
}
}
}, [chatState, isRest]);
return null;
};
export default InitChat;
๐ธ ์คํฌ๋ฆฐ์ท
- ์ฑํ ๋ฐฉ์ ๋๊ฐ ์ฌ๋: ๋ก๋น๋ก
- ์๋ฌด๊ฒ๋ ๋๋ฅด์ง ์๊ณ ์๋๋ฐฉ์ด ๋๊ฐ ๊ฒฝ์ฐ: ์๋ฝ์ฐฝ์ผ๋ก ์ด๋(์กฐ๊ธ ๋ณด๊ณ ๊ณ์๋ฉด ๋ณ๊ฒฝ๋ฉ๋๋ค)
๐จ TroubleShotting
payload์ ํ์ ์ง์ ์ ์ ๋ฅผ ๋จน์๋ค.
๋ด๊ฐ ๋ฐ์ payload๋ ์ฑํ ๋ฃธ์ด์์ด์ ๊ดํธ ํ ํ์ ์ง์ ์ ํด์ฃผ๋ ํด๊ฒฐ๋์๋ค.
useEffect(() => {
// ์ฑํ
๋ฐฉ isActive ์ํ ๊ตฌ๋
const channel = clientSupabase
.channel(`${chatRoomId}_chatting_room_table`)
.on(
'postgres_changes',
{ event: 'UPDATE', schema: 'public', table: 'chatting_room', filter: `chatting_room_id=eq.${chatRoomId}` },
(payload) => {
setChatState((payload.new as chatRoomType).isActive); ---> type ์ง์
...
๐ ๋ ํผ๋ฐ์ค
My head....?