[ react, MYSQL ] 좋아요 누르기 & 각 post의 상세페이지

Suji Kang·2023년 11월 2일
0
post-custom-banner

🐾 로그인한사람만 이용가능하게 하기

accessToken 이 null값이면 바로 종료;

useEffect(() => {
        let tmp = async () => {
            try {
                if(accessToken === null){
                    return; //accessToken 이 null값이면 바로 종료;
                }
                let res = await axios.get(
                    `/api/activities?order=${order}&limit=${cntPerPage}&page=${currentPage}&q=${searchText}`,
                    {headers : {Authorization : `Bearer ${accessToken}`}}
                );
 }
        tmp();
    }, [currentPage, order, searchText, accessToken]);

select *
from (select a.id, 
    a.title,
      a.content,
      a.writer_email,
      a.created_date,
      a.updated_date,
      a.activity_view,
      IFNULL(b.like, 0) "activity_like"
    from tbl_activities a left outer join (
      select activity_id, count(*) "like"
      from tbl_activity_like
      group by activity_id
    ) b
on a.id = b.activity_id) c left outer join (select * from tbl_activity_like
   where email = '1234@test.com') d
on c.id = d.activity_id;

🐾 SQL - 테이블 두개 하나로 한번에 합치기

로그인 한 사람의 이메일 정보

app.js

app.get('/api/activities', async (req, res) => {
 // sql
    let sql = `
    select c.id ,
     c.title,
     c.content,
     c.writer_email,
     c.created_date,
     c.updated_date,
     c.activity_view,
     c.activity_like,
     if(d.email is null, 'no', 'yes') "liked"
 from (select a.id, 
     a.title,
       a.content,
       a.writer_email,
       a.created_date,
       a.updated_date,
       a.activity_view,
       IFNULL(b.like, 0) "activity_like"
     from tbl_activities a left outer join (
       select activity_id, count(*) "like"
       from tbl_activity_like
       group by activity_id
     ) b
 on a.id = b.activity_id) c left outer join (select * from tbl_activity_like
    where email = ?) d
 on c.id = d.activity_id
  where title like ? 
`;

  try {
        // 로그인 한 사람의 이메일 정보
        const token = req.headers.authorization.replace('Bearer ', '');
        // console.log(token);
        const user = jwt.verify(token, process.env.JWT_SECRET);
        let [results] = await pool.query(sql, [user.email, `%${q}%`, limit, limit * (page - 1)]);
        // console.log(results);

        //각 게시물에 대한 이미지 가져오기
        sql = `
        select img_url
        from tbl_activity_img
        where activity_id = ?
        `
        //각각의 게시물 이미지 url을 각 객체속에 추가 
        for (let i = 0; i < results.length; i++) {
            let [imgs] = await pool.query(sql, [results[i].id])
            console.log(imgs);
            results[i].img_url = imgs.map((el) => el.img_url);
        }
        console.log(results); //img_url 추가된 results
        // 전체게시물 갯수
        sql = `
        select count(*) "total_cnt" 
        from tbl_activities
        where title like ?
      `
        const [results2] = await pool.query(sql, [`%${q}%`]);
        console.log(results2); // [ {total_cnt: 5} ]
        res.json({ total_cnt: results2[0].total_cnt, activityList: results });
    } catch (err) {
        console.log(err);
        res.status(500).json('오류발생했음');
    }
});

하트가 이미 눌렸으면, 새로고침해도 하트가 눌린채 가져온다.

const [isLiked, setIsLiked] = useState(props.activity.liked === 'yes'); //좋아요 여부 

activityCard.js

import { UserContext } from "../../App";
import { useContext, useState } from "react";
import axios from "axios";

const ActivityCard = (props) => {
    console.log(props.activity);
    const [isLiked, setIsLiked] = useState(props.activity.liked === 'yes'); //좋아요 여부
  
   const { accessToken } = useContext(UserContext);
  
   const onLikeClick = async () => {
        if (accessToken === null) {
            alert('로그인 후 이용해주세요');
            navigate('/login');
            return;
        }
        if (isLiked === false) {//🌟하트가 안눌린 상태에서 하트를 누르면
            //지금 로그인 한 사람이, 해당 게시물에 하트를 누른것 --> 데이터 베이스 테이블에 추가
            try{
                await axios.post('/api/like', 
                    {id:props.activity.id} , 
                    {headers:{Authorization:`Bearer ${accessToken}`}}
                );
                setIsLiked(true);
            }catch(err){
                console.log(err);
                alert('현재 서버에 문제가 있어요 잠시후 다시 시도하세요');
            }
        }else{ //🌟하트가 눌린 상태에서 하트를 해제한것
            //지금 로그인한 사람이 해당 게시물 하트 해제한것 -->테이블 삭제
            try{
                await axios.delete('/api/like', {
                    data : {id:props.activity.id}, 
                    headers:{Authorization:`Bearer ${accessToken}`} 
                });
                // setIsLiked(false);
            }catch(err){
                console.log(err);
                alert('잠시후 다시 실행하세요');
            }
        }
    }
  
   return (
         <CardLikeButton onClick={onLikeClick}>
              {isLiked ? <FavoriteIcon style={{ color: 'red' }} /> : <FavoriteBorderIcon />}
         </CardLikeButton>
     //like버튼이 클릭되면,
);

//🌟좋아요 테이블에 추가 - 필요한 정보 ( 리액트가 줘야하는건 게시물 id, 로그인한사람의 email)
app.post('/api/like', async (req, res) => {
    const id = req.body.id //body 안에 id가 들어있음/ 게시물 id
    const token = req.headers.authorization.replace('Bearer ', '');
    const user = jwt.verify(token, process.env.JWT_SECRET);
    //user.email --> 로그인한 사람의 email

    let sql = `
     insert into tbl_activity_like
      values (?, ?);
    `
    try {
        await pool.query(sql, [id, user.email]);
        res.json('추가 성공!');
    } catch (err) {
        console.log(err);
        res.status(500).json('오류발생했음');
    }
})

//🌟좋아요 테이블에서 삭제 ( 리액트가 줘야하는건 게시물 id, 로그인한사람의 email)
app.delete('/api/like', async (req, res) => {
    const id = req.body.id //body 안에 id가 들어있음/ 게시물 id
    const token = req.headers.authorization.replace('Bearer ', '');
    const user = jwt.verify(token, process.env.JWT_SECRET);
    //user.email --> 로그인한 사람의 email

    let sql = `
     delete from tbl_activity_like
      where activity_id = ? and email = ?;
    `;
    try {
        await pool.query(sql, [id, user.email]);
        res.json('삭제 성공!');
    } catch (err) {
        console.log(err);
        res.status(500).json('오류발생했음');
    }
})

🐾 각 post의 상세페이지

activityDetail.js

import { useParams } from "react-router-dom";
import DashboardLayout from "../../components/common/layout";
import { useAuth } from "../../components/hooks/hooks";
import ActivityDetailSection from "../../components/activity/activityDetailSection";

const ActivityDetailPage = ()=>{
    useAuth();  //로그인한후 사용가능
    const params = useParams();
    // console.log(params);
    return(
        <DashboardLayout target="활동게시판">
            <h1>{params.id}번 게시글 상세 페이지</h1>
            <ActivityDetailSection activityId={params.id}/>
        </DashboardLayout>
    );
}

export default ActivityDetailPage;

📝 activityDetailSection 컴포넌트를 만든다.

📌 먼저 UI를 만든다.
activityDetailSection.js

import { BoardContent, BoardDetailWrap, BoardInfoWrap, BoardTitle, WriteBtn } from "../../pages/dashboard/activityDetail.styles";
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
import FavoriteIcon from '@mui/icons-material/Favorite';

const ActivityDetailSection = (props) => {
    return (
        <section>
            <BoardDetailWrap>
                <BoardTitle>
                    제주도 여행 후기
                    <div>
                        <span>좋아요:57</span>
                        <span onClick={onLikeClick}>
                            {isLiked ? <FavoriteIcon style={{ color: 'red' }} /> : <FavoriteBorderIcon />}
                        &</span>
                    </div>
                </BoardTitle>
                <BoardInfoWrap>
                    <p>작성자</p>
                    <p>123@test.com</p>
                    <p>작성일</p>
                    <p>2021-09-09</p>
                </BoardInfoWrap>
                <BoardInfoWrap>
                    <p>작성일자</p>
                    <p>2021-09-20</p>
                    <p>수정일자</p>
                    <p>2021-09-09</p>
                </BoardInfoWrap>
                <BoardInfoWrap>
                    <p>좋아요</p>
                    <p>40</p>
                </BoardInfoWrap>
                <BoardContent>
                    게시물 내용
                </BoardContent>
                <div style={{
                    alignSelf: 'flex-end',
                    display: 'flex',
                    columnGap: '10px'
                }}>
                    <WriteBtn>수정</WriteBtn>
                    <WriteBtn style={{ backgroundColor: 'red' }}>삭제</WriteBtn>
                </div>
            </BoardDetailWrap>
        </section>
    )
}
export default ActivityDetailSection;

activityDetail.styles.js

import styled from "@emotion/styled";

export const BoardDetailWrap = styled.div`
  display: flex;
  flex-direction: column;
  margin: 0 auto;
`;

export const BoardTitle = styled.div`
  border-top: 1px solid black;
  border-bottom: 1px solid silver;
  font-size: 18px;
  padding: 20px;
  background-color: #e9e9e9;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
    &  span{
        font-size: 14px;  
    }
`;

export const BoardInfoWrap = styled.div`
  display: flex;
  border-bottom: 1px solid silver;
  &>p{
    padding: 20px;
  }

  & > p:nth-of-type(odd){
    width: 20%;
    text-align: center;
    border-right: 0.5px solid silver;
    border-left: 0.5px solid silver;
    
  }
  & > p:nth-of-type(even){
    width: 40%;
  }
`;

export const BoardContent = styled.div`

  padding: 40px 20px;
  border-bottom:1px solid silver;
`;

export const WriteBtn = styled.button`
  align-self:flex-end;
  margin-top: 20px;
  border-radius: 0;
  padding: 6px 20px;
  border: none;
  background-color: #e9e9e9;
  cursor: pointer;
`;

📝 클릭한 id게시물을 가져와야한다. (최초로 로딩될때 페이지가 실행된다.)

🔎activityDetail.js에서 activityId={params.id}props로 전달받았다.

activityDetailSection.js

import { useState, useEffect, useContext } from "react";
import { BoardContent, BoardDetailWrap, BoardInfoWrap, BoardTitle, WriteBtn } from "../../styles/dashboard/activityDetail.styles";
import axios from "axios";
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
import FavoriteIcon from '@mui/icons-material/Favorite';
import { UserContext } from "../../App";


const ActivityDetailSection = (props) => { //props받고 (activityDetail에서)
  // const params = useParams();
    const [isLiked, setIsLiked] = useState(false); //좋아요 여부

    const { accessToken } = useContext(UserContext);
    
    useEffect(() => {
        let tmp = async () => {
            if(accessToken === null) return;
            let res = await axios.get(`/api/activities/${props.activityId}`, //아이디에다르게 가져다줘 activityDetail에서 사용한 params.id를 props로 받아와서 사용.
                { headers: { Authorization: `Bearer ${accessToken}` } }
            );
            console.log(res.data);
        }
        tmp();
    }, [props.activityId, accessToken]);

app.js

app.get('/api/activities/:id', async (req, res) => {
    const id = req.params.id; // 리액트가 준 게시물 id
    // console.log(id);
    const token = req.headers.authorization.replace('Bearer ', '');
    const user = jwt.verify(token, process.env.JWT_SECRET);

    let sql = `
    select * from
    tbl_activities
    where id = ?
    `; //조회수까지나옴

    try {
        let [ result1 ] = await pool.query(sql, [id]); // result = id에 조회수까지 
        res.json('ddd');
    } catch {
        res.status(500).json('오류발생했음');
    }
});

속도는 이게 더 느림 (하는방식만 알면됨)

let sql = `
    select * from
    tbl_activities
    where id = ?
    `;//아이디에서 조회수까지

👇

app.js

app.get('/api/activities/:id', async (req, res) => {
    const id = req.params.id; // 리액트가 준 게시물 id
    // console.log(id);
    const token = req.headers.authorization.replace('Bearer ', '');
    const user = jwt.verify(token, process.env.JWT_SECRET);
  
   let sql = `
    select * from
    tbl_activities
    where id = ?
    `;//아이디에서 조회수까지

 try {
        let [ result1 ] = await pool.query(sql, [id]);
    
        sql = `
          select count(*) "activity_like"
          from tbl_activity_like
          where activity_id = ?;
        `; //좋아요갯수
   
        let [result2] = await pool.query(sql, [id]);
    
        sql = `
          select * from tbl_activity_like
          where activity_id = ? and email = ?;
        `; //하트를 누른지 안누른지 확인여부 & 로그인한 사람이 있다면, 결과가 있고, 아니면 없을것이다.
   
        let [result3] = await pool.query(sql, [id, user.email]);
    
        sql = `
          select * from tbl_activity_img
          where activity_id = ? 
        `; //이미지url
   
        let [result4] = await pool.query(sql, [id]);
   //🌟 다 하나하나 만들고 이제, 하나로 합친다.
   
        result1[0].activity_like = result2[0].activity_like; //result2는 좋아요갯수를 가져옴. 가져온것을 result1안에 넣어준다. 
        result1[0].liked = result3.length === 0 ? 'no' : 'yes'; //좋아요눌렀는지 안눌렀는지 여부는 result3에.
        result1[0].img_url = result4.map((el)=> el.img_url)//이미지 url
    
        console.log(result1[0]);
        res.json(result1[0]); // 모두다 result1[0] 에 넣어주고 리액트로 보낸다. 
      }catch(err){
        res.status(500).json('오류발생');
      }
});
 console.log(result1[0]); //안에 다 들어감 

to be continued..🌟

profile
나를위한 노트필기 📒🔎📝
post-custom-banner

0개의 댓글