[15-1] 객체 / 배열 복사
[15-2] 댓글 수정하기
[15-3] 무한스크롤
javascript에서 데이터를 복사할 때, 두 가지 개념이 존재합니다.
얕은 복사(Shallow Copy)
와 깊은 복사(Deep Copy)
다.
📂 데이터 복사
let aaa = "철수" let bbb = aaa console.log(aaa) // 철수 console.log(bbb) // 철수
복사본의 값을 변경해서 재할당
bbb = "영희" console.log(aaa) // 철수 console.log(bbb) // 영희
📂 객체 복사
let child1 = { name: "철수", age: 8, school: "다람쥐초등학교" } let child2 = child1 child2 // {name: '철수', age: 8, school: '다람쥐초등학교'}
복사본 child2 객체의 name 값을 변경
child2.name = "영희" child1 // {name: '영희', age: 8, school: '다람쥐초등학교'} child2 // {name: '영희', age: 8, school: '다람쥐초등학교'}
child2의 name 값만 변경했는데, child1의 name값도 변경됨
자바스크립트의 데이터 타입 특징 때문에 생기는 문제
String
, Number
, Boolean
타입 데이터의 경우 변수에 값을 할당하면 값 자체가 저장된다.
하지만 객체와 배열의 경우, 그 값 자체가 아닌 주소
가 저장된다.
두 객체가 같은 주소를 가리키고 있기 때문에, child2의 값을 변경하면 child1의 값도 동일하게 변경됨
객체 복사라는 것은 존재하지 않는다.
원본 객체와 같은 값을 가진 객체를 새로 만들 수 있을 뿐이다.
// child3에 child2 복사 let child3 = { name: child2.name, age: child2.age, school: child2.school } child3 // {name: '영희', age: 8, school: '다람쥐초등학교'}
// child3에 child2 복사 let child3 = { name: child2.name, age: child2.age, school: child2.school } child3 // {name: '영희', age: 8, school: '다람쥐초등학교'}
📂 스프레드 연산자
객체를 복사할 때마다 원본 객체의 모든 값을 따로따로 가져오는 것은 번거로운 작업이다.
객체에 들어가 있는 데이터의 양이 많아지면 실질적으로 불가능에 가까워 진다.
그 때 사용할 수 있는 것이 스프레드 연산자
let child4 = { ...child2 } child4 // {name: '영희', age: 8, school: '다람쥐초등학교'}
📂 중첩 객체 복사 _ 스프레드 연산자
스프레드 연산자 역시 객체 안에 객체가 값으로 들어가 있는 경우에는 제대로 복사가 되지 않는다.
스프레드 연산자를 이용한 복사를 얕은 복사(Shallow-Copy)라고 한다.
얕은 복사는 depth 1의 깊이를 가진 데이터까지는 복사할 수 있지만, depth 2 이상의 깊이를 가진 데이터는 복사하지 못한다.
📂 깊은 복사
JSON.stringify
와 JSON.parse
라는 메소드를 이용하면 객체/배열을 문자열로, 그리고 문자열을 객체/배열로 바꾸어 줄 수 있다.💡JSON
JavaScript Object Notation의 약자.
Javascript 객체 문법으로 구조화된 데이터를 표현하기 위한 문자 기반의 표준 포맷
JSON.stringify
JSON.stringify(profile1) // '{"name":"철수","age":8,"school":"공룡초등학교","hobby":{"first":"수영","second":"프로그래밍"}}'
JSON.parse
JSON.parse(JSON.stringify(profile1)) // {name: '철수', age: 8, school: '공룡초등학교', hobby: {first: '수영', second: '프로그래밍'}}
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> ); }
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> ); }