Day 31

김정동·2021년 12월 13일
0

이제 기본기능 + 성능개선 부분을 배우면 된다.

방문한 페이지 기능

import { useRouter } from "next/router";
import { useState } from "react";

type IPage = "/boards" | "/useditems" | "/mypage";

export function useMoveToPage() {
  const router = useRouter();
  const [visitedPage, setVisitedPage] = useState("/");

  const moveToPage = (page: IPage) => () => {
    setVisitedPage(page);
    router.push(page);
  };

  return {
    moveToPage,
    visitedPage,
  };
}

게시판, 중고마켓, 마이페이지를 들어갔을 경우 저장하는 기능을 이렇게 만들 수 있고
타입은 이렇슴

Generic

function getString(arg :string) {
    return arg
}
const result1 = getString("철수")
// getString(3);(number' 형식의 인수는 'string' 형식의 매개 변수에 할당될 수 없습니다.)

getString에 철수를 담았고 result1안에 그 값을 담은 것.

result1의 타입까지 설정하고싶다면

function getString(arg :string): string {
    return arg
}
const result1 = getString("철수")

이렇게하면 리턴값의 타입을 설정(아규먼트가 바로나옴) 그걸 가지고 나온 result1에도 넣을 수 있다.

function getNumber(arg: number): number{
    return arg
}
const result2 = getNumber(8)

숫자도 똑같음

any를하면?

function getAny(arg: any): any {
  return arg;
}
const result31 = getAny(8);
const result32 = getAny("안녕하세요");
const result33 = getAny(true);

물론 다되서 좋지만 이렇게 하면 타입이 뭔지 모르니(number?? string?? boolean??) 좋은 표현은 아님

Generic
뜻 : 일반적인 타입이다, 내 타입을 지정할 수가 있음

function getGeneric<MyType>(arg: MyType): MyType {
  return arg;
}
const result41 = getGeneric(8);
const result42 = getGeneric("안녕하세요");
const result43 = getGeneric(true);

이렇게 any 라고 덜렁 나오지 않고 값이 나온다
MyType 으로 해서 들어간것과 나온것이 같다고 지정해둔 것(T로 엄청짧게해도된다)

Generic 응용

function getReverse(arg1: any, arg2: any, arg3: any): [any, any, any,]{
    return[arg3, arg2, arg1]
}
const result51 = getReverse("철수", "다람쥐초등학교", 8)
const result52 = getReverse(13, "영희", "토끼초등학교");
const result53 = getReverse(100, 200, "토끼초등학교");

any를 넣어서 값의 타입이 바뀌어도 사실 모르고 [any, any, any]를 number로 바꾸면 오류가 나게 된다.

그럼 제네릭을 써보면?

function getReverseType<MyType1, MyType2, MyType3>(
  arg1: MyType1,
  arg2: MyType2,
  arg3: MyType3
): [MyType3, MyType2, MyType1] {
  return [arg3, arg2, arg1];
}
const result61 = getReverseType("철수", "다람쥐초등학교", 8);
const result62 = getReverseType(13, "영희", "토끼초등학교");
const result63 = getReverseType(100, 200, "토끼초등학교");

이렇게 myType1,2,3으로 해놓으면 각 결과값의 타입을 볼 수 있다 😮 😮 😮
리턴값의 순서를 잘 맞추자

제네릭의 장점

any로 사용하면 뭐나오는지 모르는데 제네릭 사용하니까 타입을 알 수 있음

근데 하나하나 MyType 1,2,3 이럴수 없으니까 줄여보자

function getReverseT<T1, T2, T3>(arg1: T1, arg2: T2, arg3: T3): [T3, T2, T1] {
  return [arg3, arg2, arg1];
}
const result71 = getReverseT("철수", "다람쥐초등학교", 8);
const result72 = getReverseT(13, "영희", "토끼초등학교");
const result73 = getReverseT(100, 200, "토끼초등학교");

이름만 T로 바꾸면 좀 짧아보인다.

근데 더 줄일 수 있다??!

function getReverseTUV<T, U, V>(arg1: T, arg2: U, arg3: V): [V, U, T] {
  return [arg3, arg2, arg1];
}
const result81 = getReverseTUV("철수", "다람쥐초등학교", 8);
const result82 = getReverseTUV(13, "영희", "토끼초등학교");
const result83 = getReverseTUV(100, 200, "토끼초등학교");

이렇게 T,U,V 세가지로 넣을 수 있다.

이제 클로저를 응용해서 써보자

function firstFunc(arg1: string) {
  return function secondFunc(arg2: number): [string, number] {
    return [arg1, arg2];
  };
}
// firstFunc("")  // secondFunc 전체가 나옴

const resultClosure1 = firstFunc("영희")(20);

함수 안에 함수를 넣은 클로저함수다, 타입도 이렇게 지정해줄 수 있다.

애니를 넣으면?

function firstFunc2(arg1: any) {
  return function secondFunc2(arg2: any): [any, any] {
    return [arg1, arg2];
  };
}
const resultClosure2 = firstFunc2("영희")(20);

any로만 나오고 알 수 없다.

화살표함수를 한번 써보자

function firstFunc3<T>(arg1: T) {
  return function secondFunc3<U>(arg2: U): [T, U] {
    return [arg1, arg2];
  };
}
const resultClosure3 = firstFunc3("영희")(20);

이렇게 작성해보고 화살표함수로 바꾸면 이렇게된다

const firstArrow = <T>(arg1: T) => <U>(arg2 : U) : [T, U]=> {
  return [arg1, arg2];
};
const resultClosure4 = firstArrow("영희")(20);

근데 또 개선할 수 있다.???

const firstArrow2 =
  <C>(arg1: C) =>
  <P>(arg2: P): [C, P] => {
    return [arg1, arg2];
  };
const resultClosure5 = firstArrow2("영희")(20);

아직 생긴건 같지만 Component의 C, Props의 P를 넣었다.

프롭스는 사실 객체기 때문에

const resultClosure5 = firstArrow2("Presenter")({aaa: "철수"});

이런느낌이다 그래서 extends를 사용해줘야함

const firstArrow2 =
  <C, P extends { aaa: string }>(component: C) =>
  <P>(props: P): [C, P] => {
    return [component, props];
  };
const resultClosure5 = firstArrow2("Presenter")({ aaa: "철수" })

hoc를 만들었을 때 사용했던파일을 생각해보면

import { useRouter } from "next/router";
import { ComponentType, useEffect } from "react";

export const withAuth =
  <P extends {}>(Component: ComponentType<P>) =>
  (props: P) => {
    const router = useRouter();

    useEffect(() => {
      if (!localStorage.getItem("accessToken")) {
        alert("로그인 한 사람만 입장 가능합니다!!! 로그인을 먼저 해주세요!!");
        router.push("/23-04-login");
      }
    }, []);

    return <Component {...props} />;
  };

extends로 {} 배열이라고 설정해놓고 Props의 P를 설정해준다. ....??!? 이게 이렇게?!?!

Callback

콜백함수가 그래서 뭐임??

인자로 들어가는 함수

aaa 에 알맞은 인자값을 넣어줄 수 있다. 근데 aaa안에 함수를 넣는다면?

인자 안에 함수가 들어간다 - 이게 콜백함수다. 화살표 함수로 바꾸면

aaa(() => {
console.log("1231231")
})

어디서 많이 본 것 같다.

[1,2,3,4].map((el) => el+ "어린이")

map 도 같은 방식으로 적용된것 ????

다른 예시를 보자

function aaa(arg1){
    // 이런저런 로직
    // 외부에 데이터 등록하기
    arg1()
}

그리고 함수안에서 이런저런 로직을 사용하고나서 특정 함수를 사용하고 싶다면??

aaa(function()){
console.log("등록에 성공했습니다.")
}

사용하고나서 등록에 성공했다고 메세지를 보내게된다.
근데 함수들을 실행시키고 나서 순서대로 해줘 - asnync/await쓰면 되지만
그게 없을때는 arg1이렇게 밑에 넣어서 실행시켰음.
한번 실제로 얼마나 불편한지 보자

onClickCallback부분 완성

/* eslint-disable no-unused-vars */
import { useState } from "react";

export default function CallbackPromiseAsyncAwaitPage() {
  const [myCallback, setMyCallback] = useState([]);
  const [myPromise, setMyPromise] = useState([]);
  const [myAsyncAwait, setMyAsyncAwait] = useState([]);

  function onClickCallback() {
    const ccc = new XMLHttpRequest();
    ccc.open(
      "get",
      "http://numbersapi.com/random?min=1&max=200"
    )
    ccc.send()
    ccc.addEventListener("load", (res) => {
      // 다 끝나면 실행할꺼
      console.log(res)
    })
  }

  function onClickPromise() {}

  function onClickAsyncAwait() {}

  return (
    <>
      <h1>콜백과 친구들</h1>
      <div>
        결과:
        {myCallback.map((el: any) => (
          <div key={el.title}>{el.title}</div>
        ))}
      </div>
      <button onClick={onClickCallback}>Callback 요청하기!!</button>
      <div>
        결과:
        {myCallback.map((el: any) => (
          <div key={el.title}>{el.title}</div>
        ))}
      </div>
      <button onClick={onClickPromise}>Promise 요청하기!!</button>
      <div>
        결과:
        {myCallback.map((el: any) => (
          <div key={el.title}>{el.title}</div>
        ))}
      </div>
      <button onClick={onClickAsyncAwait}>AsyncAwait 요청하기</button>
    </>
  );
}

저렇게 open으로 열고 send로 보낸다음 addEventListener를 사용하여 콜백함수를 썼다

요청한 주소는 랜덤한 숫자를 받아오는거다. progressEvent - target 안에서 어떤 숫잔지 볼 수 있다.

그럼 임의의 순자를 받아오고 나서 그 숫자에 맞는 koreanjson의 해당하는 게시글 불러오기, 그 게시물을 쓴 작성한 작성자의 다른 게시물 보기 까지 응용해보자, 3가지 api를 요청해야한다.

게시물불러오기는 주소+받아온숫자
받아온 숫자는

     const num = res.target.response.split(" ")[0]

주소 + 받아온 숫자를 요청하려면

      const aaa = new XMLHttpRequest();
      aaa.open("get", `http://koreanjson.com/posts/${num}`);
      aaa.send();
      aaa.addEventListener("load", (res: any) => {});

그 이후 그 번호에 맞는 글을 보려면

      const aaa = new XMLHttpRequest();
      aaa.open("get", `http://koreanjson.com/posts/${num}`);
      aaa.send();

작성자의 게시물은 /posts?userId={id} (koreanjson을 보면된다)

      aaa.addEventListener("load", (res: any) => {
        const userId = JSON.parse(res.target.response).UserId;
        const aaa2 = new XMLHttpRequest();
        aaa2.open("get", `http://koreanjson.com/posts?userId=${userId}`);
        aaa2.send();
        aaa2.addEventListener("load", (res) => {
          const result = JSON.parse(res.target.response);
          setMyCallback(result);

이후 setMyCallback에 담았다

전체 코드를 보면

function onClickCallback() {
    const ccc = new XMLHttpRequest();
    ccc.open("get", "http://numbersapi.com/random?min=1&max=200");
    ccc.send();
    ccc.addEventListener("load", (res: any) => {
      // 다 끝나면 실행할꺼
      const num = res.target.response.split(" ")[0];

      const aaa = new XMLHttpRequest();
      aaa.open("get", `http://koreanjson.com/posts/${num}`);
      aaa.send();
      aaa.addEventListener("load", (res: any) => {
        const userId = JSON.parse(res.target.response).UserId;
        const aaa2 = new XMLHttpRequest();
        aaa2.open("get", `http://koreanjson.com/posts?userId=${userId}`);
        aaa2.send();
        aaa2.addEventListener("load", (res) => {
          const result = JSON.parse(res.target.response);
          setMyCallback(result);
        });
      });
    });
  }

이렇다.... 끔찍하게 길다 이런걸 콜백지옥이라고한다.

Promise

axios.get으로 받아온다음. then()써서 받아온 다음에 이렇게 한다고 쓴다.

  function onClickPromise() {
    console.log("이것은 1번입니다.");
    axios
      .get("http://numbersapi.com/random?min=1&max=200")
      .then((res) => {
        console.log("이것은 3번입니다.");
        const num = res.data.split(" ")[0];
        return axios.get(`http://koreanjson.com/posts/${num}`);
      })
      .then((res) => {
        const userId = res.data.UserId;
        return axios.get(`http://koreanjson.com/posts?userId=${userId}`);
      })
      .then((res) => {
        setMyPromise(res.data);
      })
      .catch((error) => {
        console.log(error);
      });
    console.log("이것은 2번입니다.");
  }

(순서를 보기위해 1,2,3을 써놓음, 이런 순서로 실행된다.)
콜백지옥보다는 낫다. 체인처럼 계속 내려오는 모습을 보이는데 Promise Chaning이라고 함. 이게 콜백 지옥을 해결한 첫번째 방법 axios는 Promise를 지원한 방법이다
이런식으로 promise를 만들 수도 있다.

  new Promise((resolve, reject) => {
    // 외부에 요청하기 또는 비동기작업하기
    if (성공) resolve(실행);
    if (실패) reject(실행);
  });

콜백지옥을 해결하긴 했는데 순서가 왔다갔다하는 것 같다.
그럼 그걸 해결한 Async/Await를 보자

Async/Await

  async function onClickAsyncAwait() {
    // prettier-ignore
    const res1 = await axios.get("http://numbersapi.com/random?min=1&max=200")
    const num = res1.data.split(" ")[0];
    const res2 = await axios.get(`http://koreanjson.com/posts/${num}`);
    const userId = res2.data.UserId;
    const res3 = await axios.get(
      `http://koreanjson.com/posts?userId=${userId}`
    );
    setMyAsyncAwait(res3.data);
  }

같은 방식인데 훨씬 깨끗하다?!
(물론 지금은 하나씩 받고 요청해야되서 하나씩 기다려야된다, 이거는 promise all 을 배워야한다, 여기서는 어차피 요청 안에 요청, 그 안에 요청이라 그건 필요없음)

any 넣으면 뭔지모르는데 genaric넣으면 그대로 나온다 그래서 props 타입을 지정해줘야하는데

프롭스를 줄 때 withauth를 줄 때 하나도 안변하고 Presetner로 넘겨도되는데
props를 그대로 넘기면 좋으니까 제네릭 사용

로직 사용하다가 필요할때 인자를 받아서 함수 실행시키는게 콜백함수임
근데 계속 반복되면서 들어가는 콜백지옥을 보여줌, 그걸 고치기위해 promise가 생김

근데 순서가 왔다갔다해서 보기가 힘들어서 async await로 나옴

이런 역사를 통해서 이게 생긴것

profile
개발자 새싹🌱 The only constant is change.

0개의 댓글