๐จ ์ค๋ณต ํ์นญ ๋ฌธ์ (FetchUserData.tsx ์คํจ์ฑ X)
๐จ provider๋ก children์ ๊ฐ์ธ์
๐จ useContext vs ์ปดํฌ๋ํธ ํธ๋ฆฌ ํ๋กญ์ค ์ ๋ฌ
๋ฐ์ดํฐ๊ฐ ์ฌ์ฉ๋๋ ๋ชจ๋ ์ปดํฌ๋ํธ์์ ๋งค๋ฒ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ ์์๊ณ ,
FetchUserData.tsx๋ ์ ์ด์ ์กด์ฌ ์ด์ ๋ ์์์์ ๊นจ๋ฌ์์ด์...
โญย ์ ๋ฌธ์ ๋ฅผ ๊ฐ์ ํ provider ๋ฐฉ์
๐ฅ๏ธย FetchUserDataProvider.tsx
'use client';
import { supabase } from '@/supabase/client';
import React, { useEffect } from 'react';
import useUserStore from '@/stores/user.store';
export const FetchUserDataProvider = ({ children }: { children: React.ReactNode }) => {
const { setUserId, setNickname, setLevelName, setAttendance, setEmail } = useUserStore((state) => state);
useEffect(() => {
const fetchUserData = async () => {
const { data: authData, error: authError } = await supabase.auth.getUser();
if (authError) {
console.error('Authentication user data fetch error:', authError);
return;
}
const user = authData?.user;
if (user) {
// console.log('Fetched user ID:', user.id); // ๋๋ฒ๊น
์ฝ๋ ์ถ๊ฐ
setUserId(user.id);
setEmail(user.email ?? null);
// TODO: supabase users ํ ๋ฒ๋ง ๊ฐ์ ธ์๋ ๋์ง ์์ต๋๊น? ์?
// TODO: ๋ง์ต๋๋ค...
const { data: nicknameData, error: nicknameError } = await supabase
.from('users')
.select('nickname')
.eq('id', user.id)
.single();
if (nicknameError) {
console.error('๋๋ค์ ๊ฐ์ ธ์ค๊ธฐ ์คํจ:', nicknameError);
} else {
setNickname(nicknameData.nickname ?? ''); // ์ ์ญ ์ํ์ ๋๋ค์ ์ค์
}
const { data: levelData, error: levelError } = await supabase
.from('users')
.select('level_id')
.eq('id', user.id)
.single();
if (levelError) {
console.error('๋ ๋ฒจ ๊ฐ์ ธ์ค๊ธฐ ์คํจ:', levelError);
} else if (levelData.level_id) {
const { data: levelNameData, error: levelNameError } = await supabase
.from('level')
.select('name')
.eq('id', levelData.level_id)
.single();
if (levelNameError) {
console.error('๋ ๋ฒจ ์ด๋ฆ ๊ฐ์ ธ์ค๊ธฐ ์คํจ:', levelNameError);
} else {
setLevelName(levelNameData.name); // ์ ์ญ ์ํ์ ๋ ๋ฒจ ์ด๋ฆ ์ค์
}
}
// users ํ
์ด๋ธ์์ ์ถ์ ํ์ ๊ฐ์ ธ์ค๊ธฐ
const { data: attendanceData, error: attendanceError } = await supabase
.from('users')
.select('attendance')
.eq('id', user.id)
.single();
if (attendanceError) {
console.error('์ถ์ ํ์ ๊ฐ์ ธ์ค๊ธฐ ์คํจ:', attendanceError);
} else {
setAttendance(attendanceData.attendance);
}
}
};
fetchUserData();
}, [setNickname, setLevelName, setUserId, setAttendance, setEmail]);
return <>{children}</>;
};
export default FetchUserDataProvider;
๋ฃจํธ ๋ ์ด์์์์ children์ provider๋ก ๊ฐ์ธ์ฃผ๋ฉด,
๊ฐ ์ปดํฌ๋ํธ์์๋ ์ํ๊ด๋ฆฌ๋ ๋ ๋๋ง๋ง ํ๋ฉด ๋๋ค..!
๋ฆฌํฉํ ๋ง ํน์ ์ฝ๋ ๋ผ์ธ ์ค์ด๋ฉด์ ๋ ๋ ํธ๋ค๊ฐ์ด
์ฌ์ค ๋ง์์ผ๋ก ์๋ฟ์ง ์์ ๋๊ฐ ๋ง์๋๋ฐ, ์ด๊ฑด ์ง์ง ์ข ์๋ฆ๋ต๋ค.
์๋๋ฉด ์๋ฌด๊ฒ๋ ๋ชจ๋ฅด๋ ๋ด ๋์๋ ์ปดํฌ๋ํธ๋ง๋ค ๋ฐ๋ณต๋๋ fetching์
์ ๋ง,, ๊ตฌ๋ ธ๊ฑฐ๋ ์ฉ...ใ
GrowthSummary๋ ์ ์ ๋ฐ์ดํฐ๊ฐ ์ฌ์ฉ๋๋ ์ฌ๋ฌ ์ปดํฌ๋ํธ๋ค ์ค ํ๋๋ก, ๋ฐ์ดํฐ๋ฅผ ์ ์ญ์ํ๋ก ๊ด๋ฆฌํ๊ณ ์ง์ ๋ ๋๋งํ๋ ์ญํ ์ ํ๋ค. provider ๋๋ถ์ ๋ฐ์ดํฐ fetching์ ์ํํ ํ์๊ฐ ์์ด์ก๋ค.
๐ ์์ ์
'use client';
import { supabase } from '@/supabase/client';
import React from 'react';
import { useEffect } from 'react';
import useUserStore from '@/stores/user.store';
export const FetchUserData = () => {
const { setUserId, setNickname, setLevelName, setAttendance, setEmail } = useUserStore((state) => state);
useEffect(() => {
const fetchUserData = async () => {
const { data: authData, error: authError } = await supabase.auth.getUser();
if (authError) {
console.error('Authentication user data fetch error:', authError);
return;
}
const user = authData?.user;
if (user) {
setUserId(user.id);
setEmail(user.email ?? null);
const { data: nicknameData, error: nicknameError } = await supabase
.from('users')
.select('nickname')
.eq('id', user.id)
.single();
if (nicknameError) {
console.error('๋๋ค์ ๊ฐ์ ธ์ค๊ธฐ ์คํจ:', nicknameError);
} else {
setNickname(nicknameData.nickname ?? '');
}
const { data: levelData, error: levelError } = await supabase
.from('users')
.select('level_id')
.eq('id', user.id)
.single();
if (levelError) {
console.error('๋ ๋ฒจ ๊ฐ์ ธ์ค๊ธฐ ์คํจ:', levelError);
} else if (levelData.level_id) {
const { data: levelNameData, error: levelNameError } = await supabase
.from('level')
.select('name')
.eq('id', levelData.level_id)
.single();
if (levelNameError) {
console.error('๋ ๋ฒจ ์ด๋ฆ ๊ฐ์ ธ์ค๊ธฐ ์คํจ:', levelNameError);
} else {
setLevelName(levelNameData.name);
}
}
const { data: attendanceData, error: attendanceError } = await supabase
.from('users')
.select('attendance')
.eq('id', user.id)
.single();
if (attendanceError) {
console.error('์ถ์ ํ์ ๊ฐ์ ธ์ค๊ธฐ ์คํจ:', attendanceError);
} else {
setAttendance(attendanceData.attendance);
}
}
};
fetchUserData();
}, [setNickname, setLevelName, setUserId, setAttendance, setEmail]);
return null;
};
๐ฅ๏ธย GrowthSummary.tsx
'use client';
import React, { useEffect, useState } from 'react';
// import FetchUserData from '@/lib/utils/FetchUserData';
import FetchMembershipDays from '@/lib/utils/FetchMembershipDays';
import useUserStore from '@/stores/user.store';
import ProfileStages from '../molecules/ProfileStages';
import { supabase } from '@/supabase/client';
import AttendanceCheck from '@/lib/utils/AttendanceCheck';
const GrowthSummary = () => {
const { nickname, levelName, membershipDays, userId, setLevelId, updatedLevelId } = useUserStore((state) => state);
useEffect(() => {
const fetchUserLevelId = async () => {
if (userId) {
const { data: user, error } = await supabase.from('users').select('level_id').eq('id', userId).single();
if (!error && user) {
setLevelId(user.level_id); // Global ์ํ ์
๋ฐ์ดํธ
} else {
console.error('์ฌ์ฉ์ ๋ ๋ฒจID ๊ฐ์ ธ์ค๊ธฐ ์คํจ: ', error);
}
}
};
fetchUserLevelId();
}, [userId, setLevelId]);
return (
<>
{/* <FetchUserData /> */}
<AttendanceCheck />
<FetchMembershipDays />
<div
className="flex flex-row w-[98rem] h-[14.8rem] sm:w-[38rem] sm:h-[9.6rem] pt-[3.2rem] pb-[2rem] sm:pt-[1.2rem] sm:pb-[1.2rem] bg-white
rounded-tr-[2rem] rounded-tl-[2rem]"
>
<div className="ml-[3.2rem] sm:ml-[2rem] w-[9.6rem] h-[9.6rem] sm:w-[7.2rem] sm:h-[7.2rem]">
{updatedLevelId && <ProfileStages size={96} />}
</div>{' '}
{/* updatedLevelId ์ฌ์ฉ */}
<div className="items-center ml-[1.6rem]">
<span className="font-semibold text-[2.6rem] sm:text-[1.8rem] text-[#008A02]">{levelName}</span>
<span className="font-semibold text-[2.6rem] sm:text-[1.8rem] ml-[1.6rem] sm:ml-[0.8rem]">
{nickname} ๋์ ์ ์
</span>
<div className="font-semibold text-[1.8rem] sm:text-[1.3rem] text-[#727272]">
<p>ํ๋ํธ๋ฆฌ์ ํจ๊ป {membershipDays}์ผ์งธ,</p>
<div className="flex justify-between">์ด์ฌํ ๋๋ฌด๋ฅผ ํค์ฐ๊ณ ๊ณ์๋ค์!</div>
</div>
</div>
</div>
</>
);
};
export default GrowthSummary;
๐จ useEffect ๋ด์์ ๋ฐ์ดํฐ๋ฅผ ํ์นญํ๋ ๊ฒ์ ์ํฐ ํจํด
๐จ ํ๋์ ํ
์์ ๋๋ฌด ๋ง์ ์์
์ด ์ด๋ฃจ์ด์ง๊ณ ์๋ค
๐จ ๋ก์ปฌ ์ ์ญ์ํ๊ด๋ฆฌ์ ๋ฐ์ดํฐ ํ์นญ๊ณผ ์ฐ๋ ์ํ๊ด๋ฆฌ๋ฅผ ๋ถ๋ฆฌํด์ผ ํจ์จ์
๐จ userId๊ฐ ์์ ๋๋ง ๋๋จธ์ง ์ ๋ณด๋ฅผ ๋ถ๋ฌ์ค๊ฒ enbled ์ต์
์ฌ์ฉ
๐จ ์ ์ฒด ์ ๋ณด ๋ ๋๋ง ์ปดํฌ๋ํธ & ๊ฐ๋ณ ์ ๋ณด ๋ ๋๋ง ์ปดํฌ๋ํธ
๊ธฐ๋ฅ : ๋ฐ์ดํฐ ํ์นญ, ๋ฐ์ดํฐ ๋ ๋๋ง, (์๋) ์
๋ฐ์ดํธ, ์กฐ๊ฑด๋ถ ๋ ๋๋ง
๋ฌธ์ : ์ค๋ณต ํ์นญ, ๊ฐ๋
์ฑ ์ ํ, ๋จ์ผ ํ
์ ์ฌ๋ฌ๊ฐ์ง ๊ธฐ๋ฅ, ๋ฎ์ ์ดํด๋
๋ชฉ์ : ๋ฐ์ดํฐ ํ์นญ ๋ก์ง ๊ฐ๊ฒฐํ, ์ ๋ฐ์ ์ธ ๊ฐ๋
์ฑ ํฅ์
์ผ์ชฝ์ ๊ฐ์ด null ๋๋ undefined์ผ ๊ฒฝ์ฐ, ์ค๋ฅธ์ชฝ์ ๊ฐ์ ๋ฐํ.
?.๋ ์ต์
๋ ์ฒด์ด๋ ์ฐ์ฐ์๋ก, user๊ฐ null ๋๋ undefined์ธ ๊ฒฝ์ฐ,
์๋ฌ ์์ด undefined๋ฅผ ๋ฐํํ๋ค.
์ฆ, user๊ฐ ์ ์๋์ด ์์ ๊ฒฝ์ฐ์๋ user.id๋ฅผ ๋ฐํํ๊ณ ,
๊ทธ๋ ์ง ์์ผ๋ฉด undefined๋ฅผ ๋ฐํ.
๐ฅ๏ธ const userId = user?.id ?? null;
user?.id์ ๊ฒฐ๊ณผ๊ฐ null ๋๋ undefined์ผ ๊ฒฝ์ฐ,
?? ์ฐ์ฐ์ ์ฐ์ธก์ ๊ฐ ์ฆ, null์ userId์ ํ ๋นํ๋ค.
๋ง์ฝ user?.id๊ฐ 0, false, ๋น ๋ฌธ์์ด("") ๊ฐ์ "falsy" ๊ฐ์ด์ด๋,
๊ทธ ๊ฐ ๊ทธ๋๋ก ๋ฐํ๋๋๋ฐ, ์ด๋ ๋
ผ๋ฆฌ์ OR (||)์์ ์ฐจ์ด์ ์ด๋ค.
|| ์ฐ์ฐ์๋ falsyํ ๊ฐ์ ๋ง๋๋ฉด ์ฐ์ธก ๊ฐ์ ๋ฐํํ๋ ๋ฐ๋ฉด,
??๋ null๊ณผ undefined๋ง ์ฒ๋ฆฌํ๋ค.
โก๏ธ user?.id๊ฐ null ๋๋ undefined์ด๋ฉด null์ ๋ฐํํ๊ณ ,
๊ทธ๋ ์ง ์์ผ๋ฉด user?.id์ ๊ฐ์ userId์ ํ ๋นํ๋ ์ฝ๋.