24. 06. 11 TIL 뉴스피드 프로젝트 정리
이번 팀 프로젝트는 뉴스피드 페이지를 제작하는 프로젝트였다.
우리 팀은 코딩 질문 답변, 정보 공유 사이트를 만들기로 계획했고, 나는 마이 페이지를 맡았다.
이번엔 처음으로 수파베이스를 사용해서 프로젝트를 진행했는데 처음이다 보니 어려움도 많고, 오류가 진짜 많이 생겼었다.
수파베이스는 다른 팀원분이 맡아서 기본 세팅을 해주신 덕분에 코드만 짜면 됐었다.
const MyPage = () => {
const [postList, setPostList] = useState([]);
const [user, setUser] = useState(null);
const [userName, setUserName] = useState('');
const [nameModal, setNameModal] = useState(false);
const navigate = useNavigate();
나의 경우는 state를 이렇게 4개 만들었다.
마이 페이지에서는 작성한 글을 불러와야 하므로 게시물 목록을 저장한 postList,
사용자 정보를 저장한 user,
닉네임 변경, 프로필 사진 변경을 맡았었는데 사용자의 닉네임을 저장하는 userName,
나는 닉네임 변경 모달을 따로 만들었기 때문에, 이 모달의 표시 여부를 제어하는 nameModal 이렇게다.
그리고 내가 추가한 건 아니지만 경로를 변경하기 위해 navigate도 추가했다.
const getUserNickname = useCallback(async (userId) => {
if (!userId) return; // userId가 없으면 함수 종료
const { data: userNickName, error: userNickNameError } = await supabase
.from('users')
.select('nickname')
.eq('id', userId)
.single();
if (userNickNameError) {
console.error('Error fetching nickname:', userNickNameError);
} else {
setUserName(userNickName.nickname);
}
}, []);
그리고 그 다음 코드인데, 사용자의 닉네임을 데이터베이스에서 가져오는 코드다.
useCallback은 제대로 배우진 않았는데, 함수를 메모리에 저장해서 필요할 때마다 다시 만들지 않도록 하는 훅이다.
이 함수는 비동기 함수(함수 앞에 async를 붙임) 로 만들어졌는데, 비동기 함수는 실행되는 동안 다른 작업도 동시에 할 수 있게 해준다.
그러나 userId가 없으면 함수를 바로 종료시킨다.
또 await 키워드(비동기 함수 안에서 사용, 특정 작업이 끝날 때까지 기다리게 해줌, 비동기 작업이 완료될 때까지 기다리기 위해 사용됨)는 데이터 베이스 조회가 완료될 때까지 기다리게 해준다.
즉, 필요한 곳에서는 기다리고 그 외의 코드들은 계속해서 실행되게 해준다.
우리 조는 auth가 아닌 users 테이블을 따로 만들어서, 회원가입시 그 테이블에 가입 정보가 들어가게 했는데,
수파베이스 users 테이블을 조회해서, 닉네임을 가져오고, id가 userId와 같은 사용자만 찾고, 하나의 사용자만 찾겠다는 뜻이다.
이렇게 가져온 데이터는 userNickName에 저장하고, 오류가 있으면 userNickNameError에 저장한다.
그리고 오류가 있으면 콘솔에 오류 메세지를 출력하도록 하고, 없으면 userName에 저장한다.
마지막 빈 배열은 처음 렌더링될 때 한 번만 생성되도록 하는 것이다.
useEffect(() => {
const fetchUser = async () => {
const { data: authData } = await supabase.auth.getUser();
if (authData?.user) {
setUser(authData.user);
}
};
fetchUser();
}, []);
useEffect는 특정 작업을 컴포넌트가 렌더링된 후에 실행하고 싶을 때 사용한다. 이 훅은 화면에 한 번 실행된다.
그리고 async 사용, 수파베이스에서 사용자 인증 정보를 가져오고, await로 작업이 완료될 때까지 기다린다.
이 가져온 데이터는 authData에 저장된다.
그래서 조건문으로 authData(사용자 정보)가 있으면 setUser에 사용자 정보를 저장한다.
그리고 fetchUser 함수를 호출해서 사용자 정보를 가져온다. 이 훅은 한 번만 실행된다.
useEffect(() => {
if (user) {
const fetchPostsAndNickname = async () => {
const userId = user.id;
// 사용자 닉네임 가져오기
getUserNickname(userId);
// 게시물 가져오기
const { data: posts, error } = await supabase
.from('posts')
.select('id, title, created_at, content, user_id')
.eq('user_id', userId)
.order('created_at', {
ascending: false
});
if (error) {
console.error('Error fetching posts', error);
} else {
setPostList(posts);
}
};
fetchPostsAndNickname();
}
}, [user, getUserNickname]);
user라는 변수가 존재하면 (로그인된 상태면)
fetch~ 라는 비동기 함수를 정의해 사용자 닉네임, 게시물을 가져온다.
먼저 사용자 아이디를 userId에 저장하고, getUserNickname(userId) 함수를 호출해 사용자 닉네임을 가져온다.
그 다음 수파베이스에서 posts 테이블을 조회해서 user_id가 userId와 일치하는 게시물만 선택한다.
그리고 최신 게시물이 먼저 오도록 정렬하고, 조회 결과를 posts에 저장한다.
이 훅은 user, getUserNickname이 변경될때마다 실행된다.
const changeName = () => {
setNameModal(true);
};
이 함수는 닉네임 변경 모달을 열기 위해 사용되며, nameModal을 true로 바꾼다.
const handleItemSelect = (id) => {
const item = postList.find((item) => item.id === id);
navigate('/detailpage', { state: { item } });
};
이 함수는 역시 내가 만든 게 아니고 팀원분이 추가해주셨다.
이 함수는 게시물 id를 매개변수로 받아 postList에서 id가 매개변수로 받은 id와 일치하는 게시물을 찾아 item 변수에 저장한다.
그리고 navigate 함수를 사용해 /detailpage 경로로 이동한다. 또, item 변수를 상태로 전달하는데,
이렇게 하면 상세 페이지에서 선택한 게시물의 정보를 사용할 수 있다.