커스텀 훅부터 나만의 useQuery를 만드려면 어떻게 해야할까?
이번주는 커스텀 훅을 가지고 훅스 패턴을 사용하고, 제너릭 타입을 배운다.
또 텍스트를 웹 에디터를 만들어보고, 이때 발생하는 보안문제같은 것들을 보게 된다.
실제로 결제기능도 구현해보고, 카카오 지도도 연동해보자.
오늘은 구조분해할당을 리뷰하고, rest-parameter로 객체에서 데이터를 삭제해보고, 커스텀 훅으로 폴더구조를 바꿔볼것이다.
객체가 담긴 변수 안에 키값들을 하나하나 변수로 만들기 보다는 한꺼번에 변수로 만들 수 있다. 이를 구조분해할당이라고 한다.
useQuery또한 함수를 실행시켰더니 리턴값이 객체였다. 그 리턴값이 데이터와 로딩이다.
**이때 객체 순서는 별로 중요하지 않고, 이름이 중요하다.
배열의 구조분해할당은 대괄호를 사용한다.
괄호는 useState에서도 볼 수 있다. 이 역시 구조분해할당이 되었다는 것
대신 배열은 순서가 매우 중요하다. 객체와 달리 키 이름은 상관없다. 키가 없기 때문에 마음대로 지어도 괜찮다.
다만 순서대로 들어오기 때문에 순서를 신경쓰자!
배열과 객체의 차이점
객체는이름이 중요
하고 순서가 중요하지 않았다.
배열은순서가 중요
하고 이름이 중요하지 않다.
<객체와 배열 구조분해할당 예시>
마찬가지로 useQuery와 useState도 만들어보자
useQuery라는 함수를 만들어 data와 refetch에 각각 실행할 실행문을 넣고 바로 아래줄처럼
const {data, refetch} = useQuery("FETCH_BOARD")
라고 부르면 data에서는 writer를 꺼낼 수 있고, refetch()하면 실행문이 실행된다.
직접 useQuery처럼 작동하는 함수를 만든것이다. (물론 아예 같지는 않다)
## useState 만들기
특정 객체에서 지우고 싶은 데이터를 delete를 사용해 지우면 원본을 건드리는 일이기 때문에 그리 좋지 않습니다. rest파라미터를 사용하면 원본을 건들이지 않고 삭제할 수 있습니다.
const { money, hobby, ...rest } = child
이러면 money와 hobby가 빠진채로 rest에 저장이 된다.
꼭 rest라고 써야하는 건 아니고 관례로 그렇게 사용한다.
<예시>
const {hobby, dream, ...newMe} = me //결과 // newMe = {name : "혜원", position : "mento"}
함수는 함순데 안에 use로 시작하는 함수를 사용하고 있다면 커스텀 훅이고 함수안에 use가 없다면 그냥 함수다.
일반적이지 않은 기능이 들어가 있기때문에 커스텀 훅이라고 부를 뿐 둘 다 함수이다.
eslint가 커스텀 훅을 use로 안감싸면 에러를 보여주지만 에러는 아님.
로그인이 필수인 곳에 들어가면 라우터로 로그인 페이지로 보내는 코드를 필요한 곳에서 import해서 사용할 수 있다.
import { useRouter } from "next/router"; import { useEffect } from "react"; export const useAuth = () => { const router = useRouter(); // 로그인 체크 useEffect(() => { if (localStorage.getItem("accessToken") === null) { alert("로그인 후 이용 가능합니다!!!"); void router.push("/23-03-login-check"); } }, []); };
프리보드에서 활용할 수 있는 방안은
router만 쓰는 것들 .
쿼리 타입 쓰기 싫다면 .
사용할 수 있다.
클래스 함수 쓸 때는 이런 커스텀한 훅을 쓸 수 없었다. 그렇지만 지금은 함수형 컴포넌트가 나오면서 커스텀 훅으로 바뀌어 분리할 수 있게 되었다.
우선 제너릭 타입 전에 타입에 대해 알아보자.
<타입의 종류>
import { useQuery } from "@apollo/client"; import { useState } from "react"; // 1. 문자/숫자/불린(primitive) 타입 const getPrimitive = (arg1: string, arg2: number, arg3: boolean): [boolean, number, string] => { return [arg3, arg2, arg1]; }; const result = getPrimitive("철수", 123, true); // 2. any 타입 => 그냥 자바스크립트랑 같음 const getAny = (arg1: any, arg2: any, arg3: any): [any, any, any] => { console.log(arg1 + 1000); return [arg3, arg2, arg1]; }; const result = getAny("철수", 123, true); // 3. unknown 타입 const getUnknown = (arg1: unknown, arg2: unknown, arg3: unknown): [unknown, unknown, unknown] => { if (typeof arg1 === "number") console.log(arg1 + 1000); return [arg3, arg2, arg1]; }; const result = getUnknown("철수", 123, true); // 4. generic 타입 - 1 function getGeneric<MyType1, MyType2, MyType3>(arg1: MyType1, arg2: MyType2, arg3: MyType3): [MyType3, MyType2, MyType1] { return [arg3, arg2, arg1]; } const result = getGeneric("철수", 123, true); // 5. generic 타입 - 2 function getGeneric2<T1, T2, T3>(arg1: T1, arg2: T2, arg3: T3): [T3, T2, T1] { return [arg3, arg2, arg1]; } const result = getGeneric2("철수", 123, true); // 6. generic 타입 - 3 function getGeneric3<T, U, V>(arg1: T, arg2: U, arg3: V): [V, U, T] { return [arg3, arg2, arg1]; } const result = getGeneric3("철수", 123, true); // 7. generic 타입 - 4 const getGeneric4 = <T, U, V>(arg1: T, arg2: U, arg3: V): [V, U, T] => { return [arg3, arg2, arg1]; }; const result = getGeneric4<string, number, boolean>("철수", 123, true);
제너릭은 타입을 추론해서 그자리에 넣어주는데 인자에 들어오는 타입을 그대로 사용할 수 있다. 실무에서는 보통 T, U, V 등 짧게 줄여 사용한다.
사용자 입장보다는 제공자 입장에서 많이 쓰게 된다. (useState or useQuery 같은 경우)
제너릭을 응용하는 방법은 아래와 같다.
import { useState } from "react"; // 제공자 export function useMyState<S>(qqq: S): [S, (value) => void] { const myState = qqq; const mySetState = (value) => { console.log(`${myState}에서 ${value}로 state를 변경하겠습니다!!!`); console.log(`변경된 ${value}를 사용해서 컴포넌트를 리렌더링 하겠습니다!!!`); }; return [myState, mySetState]; } // 사용자 const [count, setCount] = useMyState<number>(10);
generic HOF
// 1. HOF - 일반함수 function first<T>(arg1: T) { return function second<U>(arg2: U): [T, U] { return [arg1, arg2]; }; } const result = first("영희")(8); // 2. HOF - 화살표함수 // prettier-ignore const first2 = <T>(arg1: T) => <U>(arg2: U): [T, U] => { return [arg1, arg2]; }; const result = first2("영희")(8); // 2. HOF - 화살표함수 // prettier-ignore const withAuth = <C>(Component: C) => <P>(props: P): [C, P] => { return [Component, props]; }; const result = first2("영희")(8);
hof에도 쓸 수 있다면 hoc에도 똑같이 쓰인다.
generic HOC
import { useRouter } from "next/router"; import { ComponentType, useEffect } from "react"; // prettier-ignore export const withAuth = (ABC: ComponentType) => <P extends {}>(props: P) => { const router = useRouter(); // 로그인 체크 useEffect(() => { if (localStorage.getItem("accessToken") === null) { alert("로그인 후 이용 가능합니다!!!"); void router.push("/23-03-login-check"); } }, []); return <ABC {...props} />; };
저번주 이번주에 걸쳐 실무로 넘어왔다고 볼 수 있다.
그런데 내용이 이해가 안가...'
좀 더 복습에 치중하자.
중고마켓은 코드를 줄이고 컴포넌트를 따로 빼서 재활용을 많이 할 수 있는 방향으로 코드를 짜보자