팔로우/언팔로우 - 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 (...) : 해당 번호를 가진 사용자의 정보를 가져옵니다.

현재 로그인한 유저의 팔로잉/팔로우 리스트 보기
다른 유저의 팔로잉/팔로우 리스트 보기