오늘 배운 것
1. 구조분해할당 (= 비구조화할당 / Destructure)
구조분해할당이란 객체나 배열을 단어 그대로 '분해'해서 사용할 수 있는 문법이다. ES6에 추가되었다. 간단한 예시를 통해 객체와 배열 두 가지 케이스의 구조분해할당을 살펴보자.
객체의 경우
const child = {
name: "철수",
age: 13,
school: "다람쥐 초등학교"
}
// case1
const name = child.name
const age = child.age
const school = shild.school
child라는 객체에서 원하는 값만 뽑아오고 싶을 때, child.name과 같은 문법으로도 해당 값을 뽑아올 수 있다. 하지만 이렇게 되면 객체에서 받아오고자 하는 데이터의 양이 많아질수록 코드가 쓸 데 없이 길어진다. 코드가 길어진다는 말은 곧 성능이 저하된다는 뜻. 이 때 필요한 것이 바로 구조분해할당이다.
const child = {
name: "철수",
age: 13,
school: "다람쥐 초등학교"
}
// case2
const {name, age} = child
console.log(name) // "철수"
console.log(age) // 13
중괄호 안에 뽑아오고자 하는 데이터의 key를 넣으면 해당 데이터를 받아올 수 있다. 예시처럼 원하는 일부 데이터만 받아오는 것도 가능하다.
배열의 경우
const classmates = ["철수", "영희", "훈이"]
// case1
const child1 = classmates[0]
const child2 = classmates[1]
const child3 = classmates[2]
// case2
const [child1, child2, child3] = classmates
배열의 경우 index를 기준으로 값을 불러온다. 그렇기 때문에 변수명은 마음대로 지정이 가능하다. (useState에서 사용하고 있는 것이 배열의 구조분해할당이다)
// 객체 구조분해할당 예시
const { data, loading } = useQuery(FETCH_BOARDS)
// 배열 구조분해할당 예시
const [state, setState] = useState("")
Rest 파라미터 (객체에서 원하는 데이터만 삭제)
객체에서 일부 데이터만 삭제하길 원할 때에도 구조분해할당을 활용해서 간편하게 처리할 수 있다.
const child = {
name: "철수",
age: 8,
school: "다람쥐초등학교",
money: 2000,
hobby: "수영"
}
// child 객체에서 money, hobby만 지워보자
const {money, hobby, ...rest} = child
console.log(rest) // { name: '철수', age: 8, school: '다람쥐초등학교' }
이렇게 하면 rest라는 이름으로 money, hobby 값이 제거된 새로운 객체를 만들 수 있다.
여기서 rest는 변수명이기에 원하는대로 지정해줘도 된다.
delete 메소드를 사용해서 삭제하는것도 물론 가능하지만, 데이터 원본을 수정하는 것은 서비스의 다른 부분에서 예기치 않은 오류를 발생시킬 위험이 있으니 최대한 지양하는 것이 좋다.
배우고 나서 보니, 사실 나는 이미 구조분해할당을 적극 활용하고 있었다. 비록 그 원리는 몰랐지만 말이다.. 예시를 사용하는 방법을 이미 익힌 상태에서 작동 원리에 대한 설명을 들으니 이해가 빨랐다. 역시 실습 위주의 공부가 효율적이다.
2. custom hook
지난번에 배웠던 HOC(고차 컴포넌트) 방식은 사실 클래스형 컴포넌트를 위한 것이라고 한다. 함수형 컴포넌트에서도 물론 사용은 가능하지만, 특정 기능을 함수로 만들어 공유하기에는 Custom hook이라고 하는 방식이 훨씬 깔끔하고 효율적이라고. 오늘은 기존에 만들었던 withAuth 라는 HOC를 useAuth라는 이름의 Custom hook으로 바꾸는 실습을 했다. 그리고 추가로 페이지 정보를 받아 라우팅하는 hook도 만들어보았음.
isLoading이라는 이름의 boolean state를 만들어서 값이 제대로 넘어오는 것도 확인했다. 구조분해할당을 이용해서 데이터를 자유롭게 전달할 수 있는 것도 끝내주는 장점!
useMoveToPage는 함수의 인자로 page라는 문자열을 받아서 해당 url로 라우팅해준다. 라우팅 함수, 이미지 검증 함수, 로그인 인증 여부 확인 함수 등은 Custom Hook으로 만들어 관리하는 것이 압도적으로 편리해 보인다. 초기 제작 시간은 좀 더 늘어나겠지만.. 원활한 서비스 운영을 위한 기회비용이라고 생각한다.
3. writeQuery / readQuery
게시글 목록을 불러오는 API를 뿌려놓은 페이지에서 게시글 삭제나 등록을 실행할 경우, mutation한 뒤 수정된 목록 데이터를 다시 불러와서 실시간 업데이트 해주어야 한다. 지금까지는 그러한 작용을 위해 refetch를 이용했다. 하지만 refetch는 단어 그대로 매번 데이터 전체를 새로 fetch하는 것이기 때문에, 서비스 규모가 커질수록 한 번 한 번의 동작에 소비되는 메모리가 커진다. 그러한 점은 곧 서비스의 성능 저하로 이어진다.
그래서 필요한 것이 바로 Apollo-Client에서 지원하는 Apollo-Cache. Apollo-Cache는 Apollo-Client를 이용해서 읽어온 데이터를 저장하는 일종의 글로벌 스테이트로, DB에서 전체 데이터를 다시 fetch하는 것이 아니라 Apollo-Cache에 임시 저장된 데이터를 수정한 후 다시 불러오는 방식으로도 활용이 가능하다. 이 과정에 필요한 cache.modify, readField 등의 기능은 Apollo-Client에서 대부분 지원하고 있으니 공식 문서를 참고하자.
오늘 했던 리팩토링 작업의 전 후 코드도 올려둔다.
// refetchQueries를 이용하는 경우
const onClickDelete = (boardId: string) => async () => {
// 삭제하기 로직
await deleteBoard({
variables: { boardId },
refetchQueries: [
{
query: FETCH_BOARDS,
},
]
});
};
// Apollo-Cache를 이용하는 경우
const onClickDelete = (boardId: string) => async () => {
// 삭제하기 로직
await deleteBoard({
variables: { boardId },
update(cache, { data }) {
const deletedId = data.deleteBoard;
cache.modify({
fields: {
fetchBoards: (prev, { readField }) => {
// prev 안에 기존 30개 데이터가 존재 => 29개로 변경해야 함
const filteredPrev = prev.filter(
(el) => readField("_id", el) !== deletedId
);
return [...filteredPrev];
},
},
});
},
});
};
4. 포트폴리오 작업
파일 업로드, 태그 등록, button, Alert Bar 등 페이지 전체에서 쓰일 공통 컴포넌트들을 분리하고, 각각의 컴포넌트에 데이터를 전달하는 작업을 끝냈다. 특히 뿌듯한 건 캡쳐한 Alert Bar 부분.. mui에서 끌어온 디자인인데, 컴포넌트 하나로 분리한 뒤 지정된 함수의 인자로 바 안에 들어갈 내용과 Alert의 종류까지 지정할 수 있도록 만들었다. 이제 필요한 부분에서 일일이 mui를 import할 필요 없이 지정 함수만 호출하면 깔끔하고 예쁜 Alert Bar를 띄울 수 있다.
비록 컴포넌트 분리 작업에 예상보다 시간이 많이 걸리는 바람에 products 게시판의 삭제 기능 구현까지는 손도 못 댔지만👻, 상세 페이지 컴포넌트를 만들고 데이터 받아오는 작업까지는 완료했다. 내일이랑 주말에 부지런하게 움직여서 다음 주 월요일 전에는 제품 등록 게시판 기본 기능들을 다 만들도록 하자.
앞으로 할 것
- 구조분해할당 블로그 포스팅 작성하며 내용 정리하기
- products 게시판 상세페이지 완성 및 수정/삭제 기능 구현하기
- reviews 게시판의 목록/등록 만들기 : 리뷰는 굳이 상세페이지까지 넘어가지 않고 목록에서 상세 내용까지 볼 수 있도록 만들어 주는 것이 유저의 편의성을 높이는 방향이라고 생각한다. 그래서 아코디언 토글을 활용해 목록 페이지에서 리뷰 내용까지 다 확인할 수 있도록 할 생각이다. (리뷰 등록도 별도의 페이지로 넘어가지 않고, List 페이지에 등록 컴포넌트를 넣어서 작동하게 할 생각.)
'Journal > Today I Learned' 카테고리의 다른 글
TIL - 2022.02.22 화요일 (0) | 2022.02.22 |
---|---|
TIL - 2022.02.18 금요일 (0) | 2022.02.18 |
TIL - 2022.02.16 수요일 (0) | 2022.02.16 |
TIL - 2022.02.15 화요일 (0) | 2022.02.15 |
TIL - 2022.02.14 월요일 (0) | 2022.02.14 |