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"
- 좋아요 순으로 보겠다.
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에 "여행"이라고 포함되어있으면 가져와줘.
한줄이렇게 추가
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가 될때마다 발생할때마다 실행된다... "ㅇ", "여", "ㅎ", "해", "행"...
이렇게 되면 속도가 느려질수있다.
검색버튼없이 검색기능을 수행할떄
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="제목으로 검색"
/>
동적 라우팅
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
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..🌟