let aaa = "철수"
let bbb = aaa
console.log(aaa) // 철수
console.log(bbb) // 철수

객체, 배열 같은 참조 타입의 경우 변수에 지정해 주면(복사를 하면) 그 값이 담기는 것이 아닌 값이 담겨있는 주소값이 변수에 담김
따라서 복사를 하게 되면 주소값이 담기고, 이는 곧 주소를 공유하는 것이기 때문에 복사본의 값을 바꾸면 원본의 값에도 영향을 주어 값이 바뀌게 되는 것
let Obj2 = [...Obj] 같이 spread 연산자를 이용하면 객체 값을 복사 후 값이 바뀌어도 원본에는 영향을 주지않음 (배열도 똑같은 방식으로 복사 가능) let profile1 = {
name: "철수",
age: 8,
school: "공룡초등학교",
hobby: {
first: "수영",
second: "프로그래밍"
}
}
JSON.stringify 로 객체를 문자열로 바꾼 다음, JSON.parse 로 다시 문자열을 객체로 바꿔주면 깊은 복사가 됨+a) 기능 라이브러리 lodash
- 객체를 복사할 때마다 깊은 복사를 따로 해주기는 번거롭고 느림
- 복사 같은 관련 작업을 도와주는 기능 모음 라이브러리 lodash
ex._.cloneDeep(value)=> 복사 기능


import { gql, useQuery } from "@apollo/client";
import { useState } from "react";
const FETCH_BOARDS = gql`
query fetchBoards($page: Int) {
fetchBoards(page: $page) {
_id
writer
title
}
}
`;
export default function PaginationPage() {
const { data } = useQuery(FETCH_BOARDS, { variables: { page: 1 } });
const [myIndex, setMyIndex] = useState([
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
]);
const onClickEdit = (event: MouseEvent<HTMLButtonElement>): void => {
const temp = [...myIndex];
temp[Number(event.currentTarget.id)] = true;
setMyIndex(temp);
};
return (
<div>
{data?.fetchBoards.map((el, index) =>
!myIndex[index] ? (
<div key={el._id}>
<span>{el.title}</span>
<span>{el.writer}</span>
<button onClick={onClickEdit} id={String(index)}>
수정하기
</button>
</div>
) : (
<input type="text" key={el._id}></input>
)
)}
</div>
);
}
onClickedit 함수가 실행되고, event로 가져온 id 값(index)를 temp(원본 배열을 복사한 값)에서 해당 index로 조회(temp(index))하여 그 값을 true로 바꿈setMyIndex( ) 로 myIndex에 재할당각 Item 들을 컴포넌트로 분리하기
- 위 코드의 경우 Boards를 조회하는 것이라 10개의 false 가 담긴 배열을 setState를 통해 map 함수 내에서 조건부 렌더링으로 활용 가능
- 하지만 comments(댓글) 을 조회한다면 그 list 수가 가변적이라 배열을 미리 지정하기 어렵고, 그 수가 많아지면 state 초기값으로 배열을 담아두는 것이 비효율적임
*효율적인 방법 => comments(댓글) 을 컴포넌트로 분리하자!
1. 목록 컴포넌트
// 목록
import { useQuery, gql } from "@apollo/client";
import {
IQuery,
IQueryFetchBoardsArgs,
} from "../../../src/commons/types/generated/types";
import CommentItem from "../../../src/components/units/16-comment-item";
const FETCH_BOARDS = gql`
query {
fetchBoards {
_id
writer
title
}
}
`;
export default function StaticRoutingMovedPage(): JSX.Element {
const { data } = useQuery<Pick<IQuery, "fetchBoards">, IQueryFetchBoardsArgs>(
FETCH_BOARDS
);
return (
<div>
{data?.fetchBoards.map((el) => (
<CommentItem key={el._id} el={el} />
))}
</div>
);
}
각각의 fetch로 조회될 목록들을 map 함수 안에 <CommentItem> 이란 컴포넌트로 지정
이에 따라 data에 생성되는 목록들 수 만큼만 렌더링되어 화면에 표출 됨
*이 때 el = {el} 로 data 배열 안의 각 객체정보를 props 형태로CommentItem에 전달
2. Item 컴포넌트
// item 컴포넌트
import { useState } from "react";
import { IBoard } from "../../../commons/types/generated/types";
interface ICommentItemProps {
el: IBoard;
}
export default function CommentItem(props: ICommentItemProps): JSX.Element {
const [isEdit, setIsEdit] = useState(false);
const onClickEdit = (): void => {
setIsEdit(true);
};
return (
<>
{!isEdit ? (
<div>
<span>{props.el.title}</span>
<span>{props.el.writer}</span>
<button onClick={onClickEdit}>수정하기</button>
</div>
) : (
<input type="text"></input>
)}
</>
);
}
props로 넘긴 el 은 CommentItem 안에서 props.el 로 활용하여 화면에 표출
각각의 CommentItem은 독립적인 isEdit을 가지게 되고, 조건부렌더링을 통해 각각의 개별적 요소들만 isEdit 값에 따라 기존의 요소 or input 요소를 렌더링하게 됨
*렌더링 되는 결과는 똑같으나 컴포넌트를 분리하여 좀 더 효율적인 관리가 가능
유튜브 또는 페이스북과 같이, 페이지를 아래로 스크롤 하다가 종단점에 도달하면
새로운 데이터가 계속해서 추가되는 방식의 페이지 처리 방법을 무한스크롤 방식이라고 함


우선적으로 추가적으로 데이터를 불러오기 위해 onloadMore 란 함수를 작성하고 해당 함수 안에 Query 에서 fetchMore(추가로 데이터를 더 fetch)를 뽑아내서 위와 같이 작성
*[const {data, fetchMore} = useQuery(FetchBoard) 로 data 외에 fetchMore 도 뽑아올 수 있음!]
fetchMore 내 변수(variables)의 값으로 현재 fetch 된 글 수를 10으로 나눈 값에 1을 더한 값으로 설정
*[원리]
1. 해당 fetchBoard API는 한 번 fetch 시 열 개의 글들을 불러옴
2. 즉 1페이지에 10개의 글을 불러오는 것이고, page 수가 늘어날 때마다(더 많이 fetch할 때마다) 10개 씩 글 목록 수가 증가
3. fetchMore는 현재 글 수보다 더 많은 data를 불러오는 것이기에 그 다음 page의 글들을 불러와야 함(변수의 page 값이 다음 페이지로 지정되야 함)
if문을 추가하여 fetchMoreResult 가 없다면 (추가적으로 불러올 데이터가 없다면) fetchBoardComments 는 현재의 값으로 리턴하고, 그렇지 않다면 이전의 fetch된 Comments에 추가적으로 불러온 Comments들을 spread 연산자로 합한 결과를 현재 값으로 리턴

<InfiniteScroll> 태그로 감싸 줌loadMore 라는 태그 내 속성의 값으로 넣어 줌import { useQuery, gql } from "@apollo/client";
import type {
IQuery,
IQueryFetchBoardsArgs,
} from "../../../src/commons/types/generated/types";
import InfiniteScroll from "react-infinite-scroller";
const FETCH_BOARDS = gql`
query fetchBoards($page: Int) {
fetchBoards(page: $page) {
_id
writer
title
contents
}
}
`;
export default function StaticRoutingMovedPage(): JSX.Element {
const { data, fetchMore } = useQuery<
Pick<IQuery, "fetchBoards">,
IQueryFetchBoardsArgs
>(FETCH_BOARDS);
const onLoadMore = (): void => {
if (data === undefined) return;
void fetchMore({
variables: { page: Math.ceil((data?.fetchBoards.length ?? 10) / 10) + 1 },
updateQuery: (prev, { fetchMoreResult }) => {
if (fetchMoreResult.fetchBoards === undefined) {
return {
fetchBoards: [...prev.fetchBoards],
};
}
return {
fetchBoards: [...prev.fetchBoards, ...fetchMoreResult.fetchBoards],
};
},
});
};
return (
<div>
<InfiniteScroll pageStart={0} loadMore={onLoadMore} hasMore={true}>
{data?.fetchBoards.map((el) => (
<div key={el._id}>
<span style={{ margin: "10px" }}>{el.title}</span>
<span style={{ margin: "10px" }}>{el.writer}</span>
</div>
)) ?? <div></div>}
</InfiniteScroll>
</div>
);
}