[ react, MYSQL ] 각 게시물에 대한 이미지 가져오기

Suji Kang·2023년 10월 30일
0
post-custom-banner

🐾 각 게시물에 대한 이미지 가져오기

app.js

 //각 게시물에 대한 이미지 가져오기
        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); // img_url의 새로운 키값이 만들어짐. 배열을 img에 대입.
        }
        console.log(results); //img_url 추가된 results
  • 원래 이렇게 img_url없었는데,

    이렇게해서, img_url을 추가시킴.

activitySection.js

   <ActivityBody>
                {
                    activityList.map((el) =>
                        <ActivityCard
                            key={el.id}
                            activity={el}
                        />
                    )
                }
  </ActivityBody>

👉 각 el을 ActivityCard로 넘겨줌


activityCard.js
console.log(props.activity)
👇

activityCard.js

const ActivityCard = (props)=>{
 //console.log(props.activity);

    return (
	<CardImg imgURL = { props.activity.img_url[0] }>
            <CardLikeButton onClick={()=>{setIsLiked(!isLiked)}}>
                {isLiked ? <FavoriteIcon style={ {color:'red'} }/> : <FavoriteBorderIcon/>}
            </CardLikeButton>
 	</CardImg>

props.activity.img_url[0]
👇

activityCard.styles.js

export const CardImg = styled.div`
    background-image: url(${(props)=> props.imgURL === undefined ? 'activity-default.jpg' : props.imgURL});
 `;

👉 사진이 undefined이면 기본값이미지 들어가고, 아니면 img_url로 들어간다.


🐾 현재 페이지 저장 하기 /오래된순, 최신순, 좋아요순 ..

activitySection.styles.js

const ActivitySection = () => {
    
   return (
 			<ActivityFooter>
                <Pagination
                    page={3} // page가 3에 있다. 번호로 설정.
                    count={totalPage} />
            </ActivityFooter>
     );
}

page 안에 숫자는 넣는대로 페이지가 바뀐다. (3이면 3페이지, 2면 2페이지)


currentPage로 설정하면 페이지가 바뀌는데로 1,2,3 바뀜
onChange함수setCurrentPage(value)를 저장해서 숫자를 누르는데로 저장할수있게 해준다.

import { useState } from "react";

const ActivitySection = () => {
    const [currentPage, setCurrentPage] = useState(1); //현재 페이지 저장 하기 위한 state
  //기본값으로는 1번째 페이지로 설정되어있음 
  
 const onPageChange = async (e, value) => {//매개변수두개, (이벤트, 클릭한값)
        setCurrentPage(value);
    };

   return (
 			<ActivityFooter>
                <Pagination
                    onChange={onPageChange}
                    page={currentPage} //currentPage로 설정하면  페이지가 바뀌는데로 저장
                    count={totalPage} />
            </ActivityFooter>
     );
}

express 한테 요청해야함. page={currentPage} 요청후, setActivityList(res.data.activityList);로 받아저장.

import { useEffect, useState } from "react";

const ActivitySection = () => {
    const [currentPage, setCurrentPage] = useState(1); //현재 페이지 저장 하기 위한 state
    const [order, setOrder] = useState('dateDesc'); //날짜 내림차순으로

 useEffect(() => {
        let tmp = async () => {
            try {
                let res = await axios.get(`/api/activities?order=${order}&limit=${cntPerPage}&page=${currentPage}`);
              //🌟1.order=날짜 내림차순으로, cntPerPage=4개,  currentPage=1page에, 가져와줘. 
              
                //res.data.toatal_cnt --> 전체 게시물 갯수 --> 계산 총 필요한 페이지 갯수
                // 전체게시물갯수   한페이지당게시물갯수         총페이지
                //  10                  3                   4
                //  10                  2                   5
                // 총페이지 갯수 = 올림(전체게시물갯수 / 한페이지당게시물갯수)
                setTotalPage(Math.ceil(res.data.total_cnt / cntPerPage));
                setActivityList(res.data.activityList);//🌟2.data 받은것, 여기에 설정됨.
            } catch (err) {
                console.log(err);
                alert('잠시 게시글을 불러오다 문제가 발생했습니다');
            }
        }
        tmp();
    }, [currentPage, order]); //currentPage,order 바꼇을때 다시 실행 

 return(
 		<ActivityBody>
                {
                    activityList.map((el) =>
                        <ActivityCard
                            key={el.id}
                            activity={el}
                        />
                        //🌟3.ActivityCard에 그려진다.
                    )
                }
        </ActivityBody>
   )
}

날짜내림차순대신, 좋아요순으로 바꾸고싶으면 ?

이렇게 계속 바꿀순없으니,
select 태그 설정을 해줘야겠다.

  • value="view" -조회수 순으로 보겠다.
  • value="like" - 좋아요 순으로 보겠다.
  • 우리가 만든 order 사용 (state변수.)
import { useState } from "react";

	const ActivitySection = () => {
	const [order, setOrder] = useState('dateDesc'); //기본값으로는 최신순

	const onOrderChange = (e) => {
        setOrder(e.target.value); //클릭되는 순, (최신순 누르면 최신순, 오래된순 누르면 오래된순...)
    }
 
 return(
	 <ActivitySelect
                    value={order}
                    onChange={onOrderChange}>
                    <option value="dateDesc">최신순</option>
                    <option value="dateAsc">오래된순</option>
                    <option value="like">좋아요순</option>
                    <option value="view">조회수순</option>
    </ActivitySelect>
   )

🐾 제목으로 검색하기

const ActivitySection = () => {
    const [searchText, setSearchText] = useState('');
  
  return(
   			<ActivitySectionHeader>
                <input onChange={tmp} placeholder="연습용 검색" />
                <ActivityInput
                    onChange={onSearchChange}
                    placeholder="제목으로 검색"
                />
                <ActivitySelect
                    value={order}
                    onChange={onOrderChange}>
                    <option value="dateDesc">최신순</option>
                    <option value="dateAsc">오래된순</option>
                    <option value="like">좋아요순</option>
                    <option value="view">조회수순</option>
                </ActivitySelect>
                <ActivityWriteBtn>글 쓰기</ActivityWriteBtn>
            </ActivitySectionHeader>
    )

김철수라고 쓴적이 없는데 value="김철수"라고 써서 기본값으로 써진다.

 <ActivityInput
    vlaue={searchText}
    placeholder="제목으로 검색"
 />

그런데❗️ 글씨를 아무리 써도 안바뀜. 왜냐면? 비어있는 input 태그가 그려있다고 생각하니깐.

그래서 onChange함수사용

 const onSearchChange = debounce((e) => {
        setSearchText(e.target.value);
    }, 500);
    
   return(
     <ActivityInput
       onChange={onSearchChange}
       vlaue={searchText}
       placeholder="제목으로 검색"
     />
 )

이렇게 입력될때마다, input태그가 변경됨

activitySection.js
&q=${searchText}도 받아온다.

useEffect(() => {
        let tmp = async () => {
            try {
                let res = await axios.get(
                    `/api/activities?order=${order}&limit=${cntPerPage}&page=${currentPage}&q=${searchText}`,
                  } catch (err) {
                console.log(err);
                alert('잠시 게시글을 불러오다 문제가 발생했습니다');
            }
        }

        tmp();
    }, [currentPage, order, searchText]);

q도 express에서 받아온다.
app.js

app.get('/api/activities', async (req, res) => {
    console.log(req.query);// {order:'', limit:'', page:'', q:''}
    let { order, limit, page, q } = req.query;
    limit = Number(limit);
    page = Number(page);
    // order "dateDesc"'dateAsc''like' 'view'

    // sql
    let sql = `
  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
  where title like ? 
`;

title에 "여행"이라고 포함되어있으면 가져와줘.

한줄이렇게 추가

🐾 SQL LIKE

1:53

  • %: "~"로 해석
  • -: 자릿수로 해석

--A로 시작하는 문자를 찾기--
SELECT 컬럼명 FROM 테이블 WHERE 컬럼명 LIKE 'A%'

--A로 끝나는 문자 찾기--
SELECT 컬럼명 FROM 테이블 WHERE 컬럼명 LIKE '%A'

--A를 포함하는 문자 찾기--
SELECT 컬럼명 FROM 테이블 WHERE 컬럼명 LIKE '%A%'

--A로 시작하는 두글자 문자 찾기--
SELECT 컬럼명 FROM 테이블 WHERE 컬럼명 LIKE 'A_'

--첫번째 문자가 'A''가 아닌 모든 문자열 찾기--
SELECT 컬럼명 FROM 테이블 WHERE 컬럼명 LIKE'[^A]'

--첫번째 문자가 'A'또는'B'또는'C'인 문자열 찾기--
SELECT 컬럼명 FROM 테이블 WHERE 컬럼명 LIKE '[ABC]'
SELECT 컬럼명 FROM 테이블 WHERE 컬럼명 LIKE '[A-C]'


 // sql
    let sql = `
  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
  where title like ? 
`;

  try {
        let [results] = await pool.query(sql, [`%${q}%`, limit, limit * (page - 1)]);
        console.log(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 });

근데 한번만 적어서 요청하면되는데, 지금 change가 될때마다 발생할때마다 실행된다... "ㅇ", "여", "ㅎ", "해", "행"... 이렇게 되면 속도가 느려질수있다.

🐾 추가적인 라이브러리(디바운싱 Lodash)를 사용해보자.

검색버튼없이 검색기능을 수행할떄
change 이벤트가 연이어 발생하기 떄문에, 비효율적으로 여러번 서버쪽으로 요청을 하는 문제가 발생.

Debounce
👉 반복적인 동작을 강제적으로 대기하는것

const tmp = (e) => {
	console.log(e.target.value)
  }
    return(
    <input onChange={tmp} placeholder="연습용 검색" />
    )

6번이나 출력이 되었다.

그래서 좀 기다렸으면 좋겠어.
👇
먼저 ❗️npm install lodash 하고,

import { debounce } from 'lodash';

  const tmp = debounce((e) => {
       console.log(e.target.value);
   }, 500); //0.5초 기다려
   
 //그러면이제 한번에 전송가능
 
 <input onChange={tmp} placeholder="연습용 검색" />

const onSearchChange = debounce((e) => {
    setSearchText(e.target.value);
    }, 500);
    
    
 <ActivityInput
      onChange={onSearchChange}
      placeholder="제목으로 검색"
  />

🐾 상세페이지보기 2:46

동적 라우팅

App.js

 {path:'/activity/:id', element : <ActivityDetailPage/>},

activityDeatil.js

import { useParams } from "react-router-dom";

const ActivityDetailPage = ()=>{
   const params = useParams();
   console.log(params);
   return(
       <h1>{params.id}번 게시글 상세 페이지</h1>
   );
}

export default ActivityDetailPage;

url에 쓰는대로 {params.id}출력됨.

activityCard.js

import { useNavigate } from "react-router-dom";

  const navigate = useNavigate();

return(
   <button onClick={() => {
         navigate(`/activity/${props.activity.id}`);
     }}>자세히 보기
   </button>
)

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

useAuth(); 사용.

target="활동게시판" 을 해줘야 클릭했을때, 화면에 색칠이 됨.

activity.js

import { useAuth } from "../../components/hooks/hooks";

const ActivityPage = ()=>{
  useAuth();
  return(
    <DashboardLayout target="활동게시판">
      <h1>활동 게시판 페이지 입니다</h1>
      <p>다양한 사람들의 다양한 활동을 경험해 보세요~</p>
      <ActivitySection />
    </DashboardLayout>
  );
}

📝 하트를 누가 눌렀는지에대한 정보를 가져와야한다.

2:59
app.js

//console.log(results);
 // 로그인 한 사람의 이메일 정보
    const token = req.headers.authorization.replace('Bearer ', '');
    //console.log(token);
    const dddd = jwt.verify(token, process.env.JWT_SECRET);

    // 가져온 게시물들에 대해 지금로그인 한 사람이 좋아요를 눌렀는지 여부
    for(let i = 0; i < results.length; i++){

    }
  console.log(token); //값은? null

📌 리액트에서 넘겨줘야한다(accessToken)

activitySection.js

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

const ActivitySection = () => {
const {accessToken} = useContext(UserContext);

  useEffect(() => {
        let tmp = async () => {
            try {
                let res = await axios.get(
                    `/api/activities?order=${order}&limit=${cntPerPage}&page=${currentPage}&q=${searchText}`,
                    {headers : {Authorization : `Bearer ${accessToken}`}}
                );
}

to be continued..🌟

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

0개의 댓글