팔로우/언팔로우 - Part2 팔로워/팔로잉 리스트
ㆍProject Diary/React + MariaDB (PicShare WebApp)
사용자가 자신의 팔로워와 팔로잉 리스트를 확인할 수 있는 기능을 구현합니다. 이 기능은 팔로워 리스트와 팔로잉 리스트를 각각 별도의 컴포넌트로 분리하여 제공합니다.
프론트엔드
1. 팔로워 리스트 컴포넌트
FollowerListSection.jsx
1-1 팔로워 리스트 컴포넌트
> 상태 및 기본 설정
const FollowerListSection = () => {
const navigate = useNavigate();
const location = useLocation();
const { followers } = location.state || { followers: [] };
console.log("팔로워페이지의 팔로잉", followers);
// 뒤로 가기 버튼을 눌렀을 때 이전 페이지로 이동합니다.
const handleBack = () => {
navigate(-1);
};
- useLocation : 이전 페이지에서 팔로워 데이터를 받아옵니다.
- handleBack: 뒤로 가기 버튼을 눌렀을 때 이전 페이지로 이동합니다.
> 팔로워 목록 렌더링
return (
<FollowerListSectionBlock>
<div className="top">
<div className="tag">
<button onClick={handleBack}>
<IoIosArrowBack />
</button>
<h2>팔로워 목록</h2>
</div>
<div className="num">
{!followers.length ? (
<span>팔로워 0 명</span>
) : (
<span>팔로워 {followers.length}명</span>
)}
</div>
</div>
{!followers.length ? (
<div className="empty">
<p>당신을 팔로우하는 사람이 아직 없어요.</p>
<p>멋진 컨텐츠를 공유해보세요!</p>
</div>
) : (
<div className="followerListWrap">
{followers.map((user) => (
<FollowerList key={user.userNo}>
<Link to={`/personalpage/${user.userNo}`} className="imageBox">
<img
src={`${serverUrl}/uploads/${user.profilePicture}`}
alt={user.userNickname}
/>
<span>@{user.userNickname}</span>
</Link>
<FollowButton userNo={user.userNo} />
</FollowerList>
))}
</div>
)}
</FollowerListSectionBlock>
);
};
export default FollowerListSection;
- 팔로워 목록을 렌더링합니다
- 팔로워가 있을 경우 : 팔로워 목록을 렌더링합니다.
- 팔로워가 없을 경우 : 빈 상태 메시지를 렌더링합니다. - 각 팔로워 항목에 대해 FollowButton 컴포넌트를 렌더링하여 팔로우/언팔로우 기능을 제공합니다.
2. 팔로잉 리스트 컴포넌트 (FollowingListSection.jsx)
팔로잉 리스트 컴포넌트는 특정 사용자의 팔로잉 목록을 보여줍니다. 이 컴포넌트도 FollowButton 컴포넌트를 사용하여 팔로우/언팔로우 기능을 제공합니다.
2-1 팔로잉 리스트 컴포넌트
> 상태 및 기본 설정
const FollowingListSection = () => {
const navigate = useNavigate();
const location = useLocation();
const { following } = location.state || { following: [] };
console.log("팔로잉페이지의 팔로잉", following);
// 뒤로 가기 버튼을 눌렀을 때 이전 페이지로 이동합니다.
const handleBack = () => {
navigate(-1);
};
- useLocation: 이전 페이지에서 팔로잉 데이터를 받아옵니다.
- handleBack: 뒤로 가기 버튼을 눌렀을 때 이전 페이지로 이동합니다.
> 팔로잉 목록 렌더링
return (
<FollowingListSectionBlock>
<div className="top">
<div className="tag">
<button onClick={handleBack}>
<IoIosArrowBack />
</button>
<h2>팔로잉 목록</h2>
</div>
<div className="num">
{!following.length ? (
<span>팔로잉 0 명</span>
) : (
<span>팔로잉 {following.length}명</span>
)}
</div>
</div>
{!following.length ? (
<div className="empty">
<p>아직 팔로잉한 사람이 없습니다.</p>
<p>새로운 사람들을 팔로우하고 소식을 받아보세요!</p>
</div>
) : (
<div className="followingListWrap">
{following.map((user) => (
<FollowingList key={user.userNo}>
<Link to={`/personalpage/${user.userNo}`} className="imageBox">
<img
src={`${serverUrl}/uploads/${user.profilePicture}`}
alt={user.userNickname}
/>
<span>@{user.userNickname}</span>
</Link>
<FollowButton userNo={user.userNo} />
</FollowingList>
))}
</div>
)}
</FollowingListSectionBlock>
);
};
export default FollowingListSection;
- 팔로잉 목록을 렌더링합니다
- 팔로잉이 있을 경우 : 팔로잉 목록을 렌더링합니다.
- 팔로잉이 없을 경우 : 빈 상태 메시지를 렌더링합니다. - 각 팔로잉 항목에 대해 FollowButton 컴포넌트를 렌더링하여 팔로우/언팔로우 기능을 제공합니다.
3. 프로필 섹션 컴포넌트에서 팔로워/팔로잉 리스트로 이동 (ProfileSection.jsx)
이 컴포넌트는 사용자 개인 피드 페이지에서 팔로워/팔로잉 리스트 페이지로 이동하는 기능을 제공합니다.
3-1 프로필 섹션 컴포넌트
> 상태 및 기본 설정
const ProfileSection = ({ length }) => {
const navigate = useNavigate();
const dispatch = useDispatch();
const { userNo } = useParams(); // URL에서 유저 넘버 추출
const targetUserNo = parseInt(userNo); // URL에서 받아온 유저 넘버
const currentUser = useSelector((state) => state.members.user);
const currentUserNo = currentUser.userNo;
const [user, setUser] = useState();
const [loading, setLoading] = useState(true);
const [followers, setFollowers] = useState([]);
const [following, setFollowing] = useState([]);
- useParams : URL에서 유저 번호를 추출합니다.
- useSelector : Redux 스토어에서 현재 사용자 정보를 가져옵니다.
- useState : 사용자 정보와 로딩 상태를 관리합니다.
- useState : 팔로워와 팔로잉 목록을 관리합니다.
> 사용자 정보 및 팔로워/팔로잉 목록 로드
useEffect(() => {
if (currentUserNo) {
dispatch(fetchFollowingList(currentUserNo));
}
}, [dispatch, currentUserNo]);
useEffect(() => {
const fetchTargetUserData = (targetUserNo) => {
axios
.get(`${serverUrl}/auth/users?targetUserNo=${targetUserNo}`)
.then((res) => {
const data = res.data;
setUser(data);
setLoading(false);
})
.catch((err) => console.log(err));
};
fetchTargetUserData(targetUserNo);
// 팔로워 목록 가져오기
dispatch(fetchFollowerList(targetUserNo)).then((response) => {
setFollowers(response);
});
// 팔로잉 목록 가져오기
dispatch(fetchFollowingList(targetUserNo)).then((response) => {
setFollowing(response);
});
}, [userNo, targetUserNo, dispatch]);
- fetchTargetUserData: 서버에서 타겟 사용자 정보를 가져옵니다.
- axios.get : 서버에 사용자 정보 요청을 보냅니다.
- setUser(data) : 받아온 사용자 정보를 상태에 저장합니다.
- setLoading(false) : 로딩 상태를 해제합니다. - dispatch(fetchFollowerList(targetUserNo)) : 타겟 사용자의 팔로워 목록을 가져옵니다.
- dispatch(fetchFollowingList(targetUserNo)) : 타겟 사용자의 팔로잉 목록을 가져옵니다.
> 팔로워/팔로잉 리스트 페이지로 이동
// 팔로잉 리스트 페이지로 이동
const handleFollowingClick = (userNo) => {
navigate(`/followinglist/${userNo}`, {
state: { following },
});
};
// 팔로워 리스트 페이지로 이동
const handleFollowerClick = (userNo) => {
navigate(`/followerlist/${userNo}`, {
state: { followers },
});
};
- handleFollowingClick: 팔로잉 리스트 페이지로 이동합니다.
- handleFollowerClick: 팔로워 리스트 페이지로 이동합니다.
> 프로필 섹션 렌더링
if (loading) return <div>로딩중...</div>;
return (
<ProfileSectionBlock>
<div className="profile">
<img
src={`${serverUrl}/uploads/${user[0].profilePicture}`}
alt="프로필 사진"
/>
<div className="length">
<p>{length}</p>
<p>게시물</p>
</div>
<div
className="length"
onClick={() => handleFollowerClick(user[0].userNo)}
>
<p>{followers.length || 0}</p>
<p>팔로워</p>
</div>
<div
className="length"
onClick={() => handleFollowingClick(user[0].userNo)}
>
<p>{following.length || 0}</p>
<p>팔로잉</p>
</div>
</div>
<span className="nickname">{user[0].userNickname}</span>
<span className="modify">
{currentUserNo === targetUserNo && (
<Link to="/profilemodify">
<FaPen />
</Link>
)}
</span>
<div className="btn">
{currentUserNo !== targetUserNo && (
<FollowButton userNo={targetUserNo} />
)}
</div>
</ProfileSectionBlock>
);
};
export default ProfileSection;
- 팔로워/팔로잉 리스트 페이지 이동 기능: handleFollowingClick과 handleFollowerClick 함수를 통해 해당 리스트 페이지로 이동합니다.
- 팔로우 버튼: 현재 사용자가 본인이 아닌 경우에만 FollowButton을 렌더링합니다.
백엔드
1. 팔로잉 리스트 가져오기
> 팔로잉 리스트 엔드포인트
// 팔로잉 리스트 가져오기
followRouter.get("/followinglist", (req, res) => {
const { userNo } = req.query;
db.query(
"SELECT * FROM users WHERE userNo IN (SELECT followeeId FROM follows WHERE followerId = ?)",
[userNo],
(err, followinglistdata) => {
if (err) {
console.error("팔로잉리스트 에러", err);
return res.json({
message: "팔로잉리스트 불러오기 실패",
});
}
console.log("팔로잉리스트", followinglistdata);
res.send(followinglistdata);
}
);
});
sql 쿼리문
SELECT * FROM users WHERE userNo IN (SELECT followeeId FROM follows WHERE followerId = ?)
- 이 쿼리는 현재 사용자가 팔로우한 모든 사용자의 정보를 가져옵니다.
- SELECT followeeId FROM follows WHERE followerId = ? : 현재 사용자가 팔로우한 사용자의 번호를 가져옵니다.
- SELECT * FROM users WHERE userNo IN (...) : 해당 번호를 가진 사용자의 정보를 가져옵니다.
2. 팔로워 리스트 가져오기
> 팔로워 리스트 엔드포인트
// 팔로워 리스트 가져오기
followRouter.get("/followerlist", (req, res) => {
const { userNo } = req.query;
db.query(
"SELECT * FROM users WHERE userNo IN (SELECT followerId FROM follows WHERE followeeId = ?)",
[userNo],
(err, followerlistdata) => {
if (err) {
console.error("팔로워리스트 에러", err);
return res.json({
message: "팔로워리스트 불러오기 실패",
});
}
console.log("팔로워리스트", followerlistdata);
res.send(followerlistdata);
}
);
});
sql 쿼리문
SELECT * FROM users WHERE userNo IN (SELECT followerId FROM follows WHERE followeeId = ?)
- 이 쿼리는 현재 사용자를 팔로우한 모든 사용자의 정보를 가져옵니다.
- SELECT followerId FROM follows WHERE followeeId = ? : 현재 사용자를 팔로우한 사용자의 번호를 가져옵니다.
- SELECT * FROM users WHERE userNo IN (...) : 해당 번호를 가진 사용자의 정보를 가져옵니다.