생각기록

두번째 프로젝트 bandari / React 좋아요 기능 본문

강의 정리/React JS

두번째 프로젝트 bandari / React 좋아요 기능

끼록관 2023. 2. 22. 17:45

DB 구조

supplies 테이블의 likeCount는 모두의 찜 갯수 ( 거기에 유저아이디의 찜 +1 , -1 )

 

pick 찜 테이블

유저 아이디 / supplies의 pk 값을 가지고 있다.

 

 


순서

1. 찜 버튼을 누르면, 로그인이 되어있지 않으면 알림창으로 로그인을 하라고 한다.

2. 로그인이 되어있으면, likeState의 여부에 따라 바뀐다.

 

판매페이지에서 조인문으로 가져온 pick / supplies db 데이터를 { list } 로 받고 있다.

 

list.picks 모든 사람의 찜 데이터 값에   필터 를 걸었다  data.userId === isLoggedIn

좋아요 글의 유저 아이디와 로그인한 유저 아이디가 일치하는값

datas에는 내가 좋아요 한 찜 데이터들만 온다.

 

그 값이 존재하면, setLikeState( true) ; 값을 보낸다.

likeState 의 기본값은 false고, 내가 찜한 데이터가 있으면 true인 것  

 

확실히 콘솔을 찍어봐야 이해가 가긴 한다...

datas의 빈값들은 초기값대로 false / 값이 있는 얘들은 내가 좋아요한 찜 데이터 true

 

3. ( ! likeState  =>  내가 저장한 값이 없으면  false 기본값)

나는 aixos 요청하기 전에 스테이트 값을 변경했었는데 .. **** 그러면 안된다 / db 값이 바뀌고 ! state 설정을 해야 한다.

프론트

pick db에 post로 요청

supplies pk 아이디값을 id로  / 유저 아이디 : 로그인 된 아이디 값

 

 

서버 컨트롤러

응답을 받고 난후 state를 설정합니다.

  • setLikeCount ( likeCount + 1 ) ;      =>  likeCount는 db에서 받은 총 찜 카운터 수
  • setLikeState ( true ) ;   =>  내가 찜한 값이니 true로 바꾼다.

 

4. ( likeState 가 true => 내가 찜한 값이 0개 이상이여서 true )

해제 하시겠습니까?

ok 누르면, 

서버로 post 요청 보낸다 

 

일단, pick 테이블에 userId 값을 리덕스에 저장된  유저 아이디값인 > isLoggedIn 값으로 post 요청으로 추가했다.

 

프론트
서버 컨트롤러

마찬가지로  삭제까지 
 
 
 
db에 용품pk / userPK를 추가
// 판매글 좋아요 카운트, 상태
exports.postLikePlus = async (req, res) => {
  // console.log('좋아요 값', req.body);
  pick
    .create(
      {
        suppliesId: req.body.id,
        userId: req.body.userId,
      },
      { raw: true }
    )
    .then(
      // console.log('좋아요 추가')
      res.send(true)
    );
};


exports.postLikeminus = async (req, res) => {
  pick
    .destroy({
      where: { userId: req.body.userId, suppliesId: req.body.id },
      raw: true,
    })
    .then(
      res.send(true)
      // console.log('좋아요 해제'));
    );
};

 

 

import React, { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';

import styles from './css/Card.module.css';

import noneLike from '../assets/NoneClikeLikeButton.png';
import clickedLike from '../assets/ClikedLikeButton.png';

import axios from 'axios';
import { useNavigate } from 'react-router-dom';

const Card = ({ list }) => {
  // 개인 좋아요
  const [likeState, setLikeState] = useState(false);
  // 총 좋아요
  const [likeCount, setLikeCount] = useState(list.picks.length);
  // sold out 여부
  const [deal, setDeal] = useState('');

  const isLoggedIn = useSelector((state) => state.user.user.isLogin);
  const userId = useSelector((state) => state.user.user.userId);
  const navigate = useNavigate();

  useEffect(() => {
    //거래중
    if (list.deal) {
      setDeal('selling');
    } else {
      //거래완료
    }
  }, [list.deal]);

  useEffect(() => {
    if (!isLoggedIn) return false;
    const datas = list.picks.filter((data) => data.userId === isLoggedIn);
    if (datas.length > 0) setLikeState(true);
  }, []);
  console.log('용품글 아이디: ', list.id);

  // 찜 버튼
  const onLikeButton = (e) => {
    if (!isLoggedIn) {
      alert('로그인 하셔야 이용이 가능합니다.');
      return;
    }
    if (!likeState) {
      //찜 안된 상태

      if (window.confirm('찜을 하시겠습니까?')) {
        axios
          .post('pick/postLikePlus', {
            id: list.id,
            userId: isLoggedIn,
          })
          .then((res) => {
            setLikeCount(likeCount + 1);
            setLikeState(true);
            if (res.data === true) console.log('좋아요 성공');
          });
      }
    } else {
      // 찜한 상태
      if (window.confirm('찜을 해제 하시겠습니까?')) {
        axios
          .post('pick/postLikeminus', {
            id: list.id,
            userId: isLoggedIn,
          })
          .then((res) => {
            setLikeCount(likeCount - 1);
            setLikeState(false);
            if (res.data === true) console.log('좋아요 해제 성공');
          });
      }
    }
  };

  // 카드 컴포넌트 클릭 함수
  const goToDetail = () => {
    navigate('/salesDetail', { state: { ...list } });
    console.log('카드 클릭 해당 글 상세페이지 이동 정보:', { ...list });
  };

  const listData = list;
  return (
    <>
      <div
        className={styles.card}
        onClick={() => {
          goToDetail();
        }}
      >
        <div className={`${styles[`${deal}`]}`}>
          {list.deal ? '' : 'Soldout'}
        </div>
        <img
          src={`/uploadImg/${listData.cover}`}
          aria-label="cardImg"
          loading="lazy"
          alt="카드 이미지"
        />

        <div className={styles.cardFooter}>
          <h3>{listData.title}</h3>
          <p>{listData.price} 원</p>
          <p>
            {listData.location}
          </p>
        </div>
        <div className={styles.likeButton}>
          <span className={styles.likeCount}>{likeCount}</span>
          <img
            src={likeState ? clickedLike : noneLike}
            alt="찜"
            onClick={(e) => {
              e.stopPropagation();
              onLikeButton(setLikeState(!likeState));
            }}
          />
        </div>
      </div>
    </>
  );
};

export default Card;