게시판 글 작성및 조회수 증가시 페이지 reload 없이 데이터를 갱신해줘야 했다.
해당 기능을 제공하기 위해서
context로 선언한 데이터에서
useEffect()에 데이터가 변경되었을시 ==>
export const BoardProvider = ({ children }) => {
const [boardData, setBoardData] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const aixosData = async () => {
try {
const response = await axios.get('/api/board/list');
setBoardData(response.data);
console.log('게시판 데이터가 성공적으로 로드되었습니다:', response.data);
} catch (error) {
console.error('게시판 데이터 로드 중 오류 발생:', error);
} finally {
setLoading(false);
}
};
aixosData();
}, [setBoardData]);
이렇게 작성하여 데이터 갱신시 다시 요청을 하고
글을 작성하는 form태그의 onSubmit 에 적용되는 handelSubmit에
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await axios.post('/api/board/create', {
bdContent,
userEmail
});
setBoardWriteData(response.data);
setBoardData((prevData) => [response.data, ...prevData]); // 기존 데이터 앞에 새로운 데이터 추가
console.log('글 작성이 성공했습니다:', response.data);
formRef.current.reset();
} catch (error) {
console.error('글 작성 중 오류 발생:', error);
}
};
해당 코드를 추가하여 데이터를 앞에 쌓아주었다
게시판을 구현하는데 masonry를 통해 구현한 게시판이어서 페이징보다 무한스크롤을 통해 컨텐츠를 출력해주는게 더 적합하다고 판단하여 변경하기위해 찾아보았다.
해당방법으로 채용한건
react-intersection-observer 라이브러리를 사용하는것
https://github.com/thebuilder/react-intersection-observer#readme
GitHub - thebuilder/react-intersection-observer: React implementation of the Intersection Observer API to tell you when an eleme
React implementation of the Intersection Observer API to tell you when an element enters or leaves the viewport. - GitHub - thebuilder/react-intersection-observer: React implementation of the Inter...
github.com
해당기능을 통해서 데이터를 불러오기 위해
export function BoardSNS() {
const { loading } = useBoardContext();
const { boardViewData, loadingViews } = useBoardViewContext();
const [columns, setColumns] = useState(3);
const [itemsPerPage, setItemsPerPage] = useState(10);
const [modalOn, setModalOn] = useState(false);
const [selectedItem, setSelectedItem] = useState(null);
const [page, setPage] = useState(-1);
const [ref, inView] = useInView();
const [boardData, setBoardData] = useState([]);
// 무한 스크롤
// 지정한 타겟 div가 화면에 보일 때 마다 서버에 요청을 보냄
const productFetch = () => {
axios
.get(`/api/board/list?page=${page + 1}&pageSize=${itemsPerPage}`)
.then((res) => {
console.log(res.data);
setBoardData((prevData) => [...prevData, ...(res.data)]);
setPage((prevPage) => prevPage + 1);
})
.catch((err) => { console.log(err) });
};
useEffect(() => {
if (inView && !loading) {
console.log(inView, '무한 스크롤 요청💫');
productFetch();
}
}, [inView, loading]);
useEffect(() => {
const handleResize = () => {
const newColumns = window.innerWidth <= 700 ? 1 : window.innerWidth <= 1100 ? 2 : 3;
setColumns(newColumns);
};
handleResize();
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
// DB 게시판데이터 변동시 재요청
useEffect(() => {
const axiosData = async () => {
try {
const response = await axios.get('/api/board/list');
setBoardData(response.data);
console.log('게시판 갱신 데이터가 성공적으로 로드되었습니다:', response.data);
} catch (error) {
console.error('게시판 갱신 데이터 로드 중 오류 발생:', error);
}
};
axiosData();
}, [setBoardData]);
const handleModal = async (item) => {
setSelectedItem(item);
setModalOn(!modalOn);
// API 호출 등을 통해 viewCount를 1 증가시키는 작업 수행
try {
const response = await axios.get(`/api/board/detail/${item.bdIdx}`);
setNewsData(response.data);
console.log('데이터가 성공적으로 로드되었습니다:', response.data);
} catch (error) {
console.error('데이터 로드 중 오류 발생:', error);
}
};
// 가져온 데이터를 사용하여 UI를 렌더링
const boardItems = boardData && boardData.map((item, index) => {
// Moment.js를 사용하여 날짜 포맷 변경
const formattedDate = moment(item.createdAt).format('YYYY-MM-DD HH:mm');
return (
<>
<Item key={item.id} onClick={() => handleModal(item)}>
{/* 이미지 추가시 들어갈 코드 */}
{/* {imageUrl[item.id] && <ItemImage src={imageUrl[item.id]} />} */}
<ItemTextBox>
<TextDate>{formattedDate}</TextDate>
<TextContent>{item.bdContent}</TextContent>
{item.bdUrl && <TextUrl>{item.bdUrl}</TextUrl>}
<LikeBox>
{/* 댓글추가시 댓글 카운트해서 넣을것 */}
<Comments src={Comment} /> 10
<Likes src={Like} /> {item.bdLikes}
<Views src={ViewsLogo} /> {item.bdViews}
</LikeBox>
<UserBox>
{/* 유저 프로필사진 들어가기 */}
<UserBoxImage></UserBoxImage>
{/* 유저이름 출력 / 아직 시큐리티 적용안되서 null 받는중 */}
<UserBoxName>{item.userName}</UserBoxName>
</UserBox>
</ItemTextBox>
</Item>
</>
);
});
return (
<Wrapper>
<Masonry columns={columns} spacing={2} defaultHeight={150} defaultColumns={1} defaultSpacing={2}>
{loading ? (
<LoadingScreen />
) : (
boardItems
)}
</Masonry>
<BoardBottomBox ref={ref}></BoardBottomBox>
<BoardModalPortal>
{modalOn && <BoardModal item={selectedItem} onClose={() => setModalOn(false)} />}
</BoardModalPortal>
</Wrapper>
);
};
이렇게 코드를 작성하였다. 하지만 문제점으로 데이터가 새로 로드가 될때
스크롤이 위로 올라갔다 내려오는 현상이 있었다.
모달창을통해 북마크기능을 만들기위해서
const Modal = ({ onClose, item }) => {
const [bookMarkData, setBookMarkData] = useState([]);
const [loading, setLoading] = useState(true);
const [isBookmarked, setIsBookmarked] = useState(false);
const [isLogined, setIsLogined] = useState(false);
let userData;
let userEmailData;
const userDataString = sessionStorage.getItem('userData');
if (userDataString) {
userData = JSON.parse(userDataString);
userEmailData = userData.userEmail;
setIsLogined(true);
} else {
console.error('세션스토리지에 userData가 존재하지 않습니다.');
setIsLogined(false);
}
// 아이디로 북마크여부 조회
useEffect(() => {
const fetchBookMarkData = async () => {
try {
// GET 요청으로 북마크 데이터 조회
const getResponse = await axios.get(`/api/news/bookmark/${userEmail}`);
setBookMarkData(getResponse.data);
// 북마크 데이터가 있으면 즉, 길이가 1 이상이면 북마크가 되어 있다고 판단
setIsBookmarked(getResponse.data.length > 0);
console.log('북마크 조회 완료:', getResponse.data);
} catch (error) {
console.error('북마크 조회 중 오류 발생:', error);
} finally {
setLoading(false);
}
};
fetchBookMarkData();
}, [userEmailData]);
// 모달이 열릴 때 body에 스타일을 추가하여 스크롤을 막음
useEffect(() => {
document.body.style.overflow = "hidden";
return () => {
document.body.style.overflow = "visible";
};
}, []);
// 모달 내부의 클릭 이벤트일 경우 이벤트 전파 중지
const handleModalClick = (e) => {
e.stopPropagation();
};
const createBookMark = async () => {
try {
// POST 요청으로 북마크 생성
const createResponse = await axios.post(`/api/news/bookmark/create`, {
newsObjectId: 'yourNewsObjectIdValue',
userEmail: userEmailData,
});
setBookMarkData(createResponse.data);
setIsBookmarked(true); // 북마크가 생성되었으므로 true로 설정
console.log('북마크 등록 완료:', createResponse.data);
} catch (error) {
console.error('북마크 등록 중 오류 발생:', error);
} finally {
setLoading(false);
}
};
const deleteBookMark = async (bookmarkIdx) => {
try {
// DELETE 요청으로 북마크 삭제
const deleteResponse = await axios.delete(`/api/news/bookmark/delete/${bookmarkIdx}/${userEmail}`);
setBookMarkData(deleteResponse.data);
setIsBookmarked(false); // 북마크가 삭제되었으므로 false로 설정
console.log('북마크 삭제 완료:', deleteResponse.data);
} catch (error) {
console.error('북마크 삭제 중 오류 발생:', error);
} finally {
setLoading(false);
}
};
// 북마크 여부에 따라 북마크를 추가하거나 삭제하는 로직을 작성
const handleBookmarkClick = (e) => {
e.stopPropagation();
if (isBookmarked) {
deleteBookMark();
const firstBookmarkIdx = bookMarkData.length > 0 ? bookMarkData[0].idx : null;
if (firstBookmarkIdx) {
deleteBookMark(firstBookmarkIdx);
}
} else {
createBookMark();
}
}
return (
<BackgroundNews onClick={onClose}>
<Content onClick={handleModalClick}>
<BookmarkButton onClick={handleBookmarkClick}>
{isBookmarked ?
<BookmarkButtonImage src={BookmarkOn} /> :
<BookmarkButtonImage src={Bookmark} />
}
</BookmarkButton>
<CloseButton onClick={onClose}>
<CloseButtonImage src={Close} />
</CloseButton>
<HeadBox>
<HeadTitle>{item.title}</HeadTitle>
</HeadBox>
<DateBox>
<Media>{item.press}</Media>
<Date>{item.articleWriteTime}</Date>
</DateBox>
<ImageBox>
<ImageUrl src={item.picture} />
</ImageBox>
<ContentBox>
<ContentText>{item.summary}</ContentText>
<NewsUrlBox>
기사 원문 :
<NewsUrl href={item.url} target="_blank">{item.url}</NewsUrl>
</NewsUrlBox>
</ContentBox>
</ Content>
</BackgroundNews>
);
};
해당 코드를 작성했는데
Too many re-renders. React limits 에러가 발생하였다.
위 코드에서 로그인여부 검증을위한 if문이 실행되어서 Modal 자체에서 계속 set을 진행하여 나타난 에러
해당코드를 삭제하니 정상적으로 작동하였다.
북마크 개별조회 및 등록은되는데 삭제가 되지않았다.
해당 문제를 해결하기위해서
const [currentBookmarkIdx, setCurrentBookmarkIdx] = useState(null);
현재 클릭한 뉴스의 모달창에서 bookmarkIdx를 관리해주는 state를 사용하였다.
해당코드를 통해
// 아이디로 북마크여부 조회
useEffect(() => {
const fetchBookMarkData = async () => {
try {
// GET 요청으로 북마크 데이터 조회
const getResponse = await axios.get(`/api/news/bookmark/${userEmailData}`);
setBookMarkData(getResponse.data);
// 해당 아이템에 대한 북마크 여부를 설정
const isBookmarkedItem = getResponse.data.some((bookmark) => bookmark.newsObjectId === item.id);
setIsBookmarked(isBookmarkedItem);
// 현재 모달에 대한 bookmarkIdx 설정
const modalBookmark = getResponse.data.find((bookmark) => bookmark.newsObjectId === item.id);
setCurrentBookmarkIdx(modalBookmark ? modalBookmark.bookmark_idx : null);
console.log('북마크 조회 완료:', getResponse.data);
} catch (error) {
console.error('북마크 조회 중 오류 발생:', error);
} finally {
setLoading(false);
}
};
fetchBookMarkData();
}, [userEmailData, item.id]);
데이터조회를 할 때 상태를 관리해주었다.
이후 해당 상태를 통해서
const handleBookmarkClick = (e) => {
e.stopPropagation();
if (isBookmarked) {
deleteBookMark(currentBookmarkIdx);
} else {
createBookMark();
}
}
온클릭 핸들러에서 북마크가 되어있다면 현재 북마크를 선택해서 지우게 작성하였다.
가져온 content데이터가 \n을 포함하고있으면 개행이 되지않아서 br로 바꿔주다 적용이 되지않아서
white-space: pre-wrap;
해당방법을 이용해서 해결하였음