26일) 그놈의 generic이 뭐에요ㅠ typescript 심화/Cookie/LocalStorage/SessionStorage / 장바구니를 브라우저 안에 담아줘보자 ! Code Camp FE 6기

kimareum·2022년 4월 19일
1

코드캠프6기

목록 보기
26/36
post-thumbnail

저번주 금요일에 하루종일 중간평가를 보고 너무 체력이 떨어진걸 느낀다...
( 그 전에 너무 긴장했고 평가를 보면서 마음이 급해서 아무것도 먹지 못했고 ... ㅜ )
막상 하고나니 CRUD와 무한스크롤, 퍼블리싱이 전부여서 수업만 잘 따라가면 잘 할수 있었던 시험이었지만...맨땅에 폴더 만드는것부터 CRUD를 해보는건 아주 복습하기에 좋은 과정이었지만,,,
이번주에 뭔가 너무 힘이 없고 체력이 떨어진걸 느낀다!! ㅠㅠ 운동을 못해서 그렁가
샤워전에 스쿼트 좀 때리고... 홍삼도 잘 챙겨먹고..틈틈히 잠 보충하고
이제 얼마 안남았으니까 지치지 말자 !!!!!!!!

오늘의 요약 !
오늘은 앞서 알아 보았던 타입스크립트와 브라우저 저장소에 관해서 좀 더 세부적으로 알아봤죠!


우선 타입들을 조금 더 심도 있게 알아보는 시간을 가졌습니다!
any와 unknown를 먼저 알아봤죠!


any는 우리가 알지 못하는 타입을 표현할때 자주 사용했습니다! 이 경우 타입 검사를 하지 않게 되는데 타입의 일부만 알고 전체에 대한 타입을 알지 못할때 유용했습니다!


unknown은 any와 비슷하면서도 조금 달랐죠! any와 unknown 모두 매개변수(arg)로 뭘 넣어주든 상관없었습니다,
하지만 unknown은 뭘 넣어 주든 뭔지는 모르지만 개발자에게 조금 더 안전하게 타입을 지정할 수 있도록 경고해 줄 수 있다는 차이가 있었습니다!


알지 못하는 타입이 생겨 any나 unknown을 사용하지 않고 우리가 직접 타입을 만들어주었던 것 기억나시나요?
( (매개변수) 앞 <>를 사용해 함수에 타입 변수를 추가했죠! )
generic 또한 any와 비슷했어요! 하지만 any는 실제로 함수 반환 시 어떤 타입인지에 대한 정보는 잃게 되어 string을 넣어도 any타입이 반환되는 반면, generic은 입력 값에 따라 내부 타입이 정의가 되었습니다. 즉, 단일 타입이 아닌 다양한 타입에서 작동하게 작성할 수 있었습니다!
이 generic은 useState,useMutation 등을 만들어 사용할 경우에 자주 쓰인다 하였습니다!


함수에서 함수를 return하는 클로저에서 사용하는 generic 또한 알아봤습니다!
const aaa = <>()=><>():반환타입(return 있을경우)=>{} 의 형태가 되었습니다!


HOC에서 사용할때는 어땠죠? 가장 큰 차이는 매개변수와 반환되는 형태가 컴포넌트라는 점이였습니다!
따라서 Component부분은 ComponentType으로, props부분은 {}객체로써 지정하였습니다!
우리는 강의를 진행하며 매개변수에 대한 타입을 지정하는것은 여러번 해주어 어느정도 익숙해졌습니다!
그렇다면 return 타입은 어떻게 적어줬는지 기억 나시나요? : 을 통해 return type을 지정해 줄 수 있었습니다!
Ex) fuction aaa():string{return “망고”}
변수에 데이터를 넣어 두면, 새로고침 시 저장된 데이터가 날아가는 현상이 있죠?!
이는 HTML, CSS, JS 를 다시 다운로드 받아서 화면에 새로 그리기 때문이였습니다!


따라서, 데이터를 유지하기 위해 브라우저 저장소에 저장했습니다.
브라우저 저장소는 크게 쿠키(cookie), 로컬스토리지(localStorage), 세션스토리지(sessionStorage)가 있었습니다.
사용은 공통적으로 간단했습니다! 쿠키는 document.cookie, 로컬스토리지와 세션스토리지는 .setItem() .getItem() 을 사용했습니다!


각각의 특징들이 있었는데 기억나시나요!?


- 쿠키: 저장된 데이터가 Backend-API 요청시에 자동으로 함께 보내짐, 백엔드와 브라우저 간 데이터를 공유할때 유용하지만 너무 많은 데이터를 담아두면 효율성이 떨어지게됨, 만료 시간이 존재하기에 브라우저를 종료해도 만료시간까지 살아있음, 주로 보안과 관련된 정보(httpOnly를 사용해 보안 옵션을 걸어)를 주고 받을때 사용
- 로컬스토리지: 브라우저를 껐다가 다시 켜도 저장 정보가 남아있음, 주로 가벼운 정보들 저장
- 세션스토리지: 브라우저를 껐다가 다시 켜면 저장 정보가 사라짐


비교적 간단했습니다! 하지만 쿠키를 조회 할때 특징이 있었습니다! 쿠키에 담긴 모든 내용을 구분없이 하나의 문자열로 가져왔었죠! 따라서 우리는 쿠키 내용의 구분점인 ; 를 기준으로 잘라주어야했습니다!
그러기 위해 document.cookie.split(‘; ’)을 사용해주었죠! 그 안에서 우리가 필요한 내용을 뽑아주기 위해 filter()를 사용했고 필요한 내용을 찾아내기 위해 filter()안에서 startWith()를 사용했고 추출된 결과 data를 우리의 필요에 의해 다시 한번 가공(key,value형태를 value만 남을수 있게 replace 등등)해주었습니다! 이 순서를 기억하시길 바랍니다!


우리는 브라우저 저장소를 활용해서 비회원 장바구니와 오늘 본 상품 리스트를 만들 수 있었습니다!
비회원은 아직 로그인 하기 전이므로, Backend에서 어떤 유저가 장바구니에 무엇을 담고 있는지 기록하기가 조금 애매했죠! 그렇기에 쿠키보다는 로컬스토리지에 임시로 저장해 놓고, 나중에 로그인에 성공했을 때 미리 담아 놓은 장바구니를 쉽게 가져올 수 있다 했습니다!
하나 포인트가 있었죠! object로 받게 되는 value를 JSON.stringify로 감싸주어야 문자열로 담겼던 점! 기억하시죠!?
이렇게 담아주는 정보에서 불필요한 부분들은 REST 파라미터를 사용해 제외해주었습니다!
하지만 장바구니인 만큼 우리가 본 상품들은 계속 쌓여야하는데 새로 갱신되는 문제가 있었습니다! 이부분은 JSON.parse()를 사용해 LocalStorage의 basket을 객체로 만들어 변수에 담아주고 이 변수에 push를 통해 새로운 상품들을 담아주는 형태로 만들어주었죠!
장바구니에 중복 상품이 들어가면 안되겠죠! 이 부분의 처리는 push() 해주는 시점에서 해주었습니다! el의 id와 baskets안에 담겨있는 id를 비교했습니다!!


장바구니의 내용을 활용해보면 오늘 본 상품 역시, 클릭했던 상품들만 로컬스토리지에 저장해 놓으면 되겠죠?! 물론, 브라우저를 껐다가 다시 들어올 수도 있으므로 세션스토리지보다는 로컬스토리지가 알맞을 것 같습니다!

- 타입스크립트 타입 심화 genirc

  • 우리가 배웠던 자바스크립트같은 any타입 ! 아무거나 넣어줘!
const getAny = (args: any)=>{
    return args + 2
}
const result = getAny("철수")
  • 비슷한.. 나는 모르겠어! 라고 말하는 unknown 타입
const getUnknown = (args:unknown)=>{
    return args + 2 
}
//이러면 args에 오류가 뜬다 
const result2 = getUnknown("철수")

둘다 아무거나 들어갈수 있지만 , unknown은 어떠한 행위를 했을때 조건에 따라 뭐해줘라 !
라고 좀더 지정을 해줘야한다
타입스크립트를 쓸꺼면 any보단 상황에 따라 타입 지정해주는 unknown이 낫겠지!
그래서 조건별로 타입을 지정해주면 unknown은 이렇게

const getUnknown = (args:unknown)=>{
    if(typeof args === "number"){
        return args + 2 
    }else {
        return " 숫자를 넣어주세요"
    }    
}
const result2 = getUnknown("철수")

이렇게 unknown을 사용하면은! 결과 예측도 가능하다.
개발자에게 안전하게 코딩하도록 유도한다! (숫자라고 했응께 숫자만 넣어줘요)

  • generic이 뭔지 알아보는 과정
  • 뭔지 지정은 안해줬으니 들어온 타입 그대로 같은 타입으로 사용할게 ! 라는 의미이다
function getGeneric<Mytype>(arg : Mytype) : Mytype{
    return arg
} 
const aaa: string = "철수"
const bbb: number = 8
const ccc: boolean = true
const result4_1 = getGeneric(aaa)
const result4_2 = getGeneric(bbb)
const result4_3 = getGeneric(ccc)

any와 generic의 차이점
일단 들어오고 나면 거기에 해당하는 애들은 다 그 타입으로 지정해준다
그래서 나오는 값을 예측할 수 있다



- HOF에서 일반 타입과 제네릭의 차이!
제네릭 썼을때 묶어주는것 잊지말자

// 1.HOF - 일반타입 
function firstFun1(arg1: string){
    return function secondFunc1(arg2: number): [string,number]{
        return [arg1, arg2]
    }
}
const result1 = firstFun1("영희")(8)
// 2.HOF - any타입
function firstFun2(arg1: any){
    return function secondFunc1(arg2: any): [any,any]{
        return [arg1, arg2]
    }
}
const result2 = firstFun2("영희")(8)
// 3. HOF - generic 타입
function firstFun3<T>(arg1: T){
    return function secondFunc1<U>(arg2: U): [T,U]{
        return [arg1, arg2]
    }
}
const result3 = firstFun3("영희")(8)

화살표 함수에서 생략 가능한것을 생략한 부분 이해하기 !

const firstFun4 = <T>(arg1: T)=>{
    return const secondFunc1 = <U>(arg2: U): [T,U]=>{
        return [arg1, arg2]
    }
}
const result4 = firstFun4("영희")(8)
// 이렇게 바꿀수 있다
const firstFun4 = <T>(arg1: T)=> <U>(arg2: U): [T,U]=>
{return [arg1, arg2]}
const result4 = firstFun4("영희")(8)

- 변수 저장 말구, 브라우저 저장소 Cookie/LocalStorage/SessionStorage

  • 로컬스토리지랑 세션스토리지는 비슷하게 생겼었고, 둘다 키와 벨류로 저장이 되었었어!

  • 세션스토리지는 브라우저 껐다키면 사라지게 되고 , 로컬스토리지는 계속 저장되는 공간이었다!
    -> 비회원 장바구니는 로컬스토리지가 낫겠지 ?

  • 쿠키는 많은 정보들이 들어있는데, Expires가 만료시간..! 만료되면 해당 쿠키는 사라지게 된다.
    • httponly는 자바스크립트로 접근할수 있게 할것인가 말것인가를 정하는 부분
    • true인 경우 자바스크립트에서 쿠키의 정보를 가져올수 없다.
      (api요청으로만 백엔드로 보낼수 있고 자바스크립트로 조작을 할수는 없다)

    • api요청을 할때 쿠키에 있는 아이들이 같이 백엔드로 전송이 된다
    • https( http secure의 약자로 , 안전한 사이트를 의미해서 자물쇠모양이 걸려있지 ! )
      -쿠키의 secure부분은 체크가 되어있는(true) https일때만 데이터를 보낼것이다
      -api요청을 하고 response를 받을때, 백엔드에서 response에 데이터를 넣어줄수도 있음


      -쿠키는 단순 저장소 느낌이 아니라 백엔드에서 정보를 전송할때, 프론트에서도 정보를 보낼때 사용할 수 있는 공간이다 !!


      브라우저 끄면 사라지면 안돼 - 로컬 스토리지
      브라우저에서만 보여주면 돼 - 세션 스토리지
      -> 나중에 웹에디터와 리프레쉬토큰을 배울때 지금 임시로 로컬스토리지에 저장했던 accesstoken을 다시 저장하는 법을 배우겠다


      - 로컬 스토리지, 세션 스토리지, 쿠키에 데이터를 저장하고 꺼내오는 부분
      쿠키에서 원하는 정보만 가져오는 부분

- 비회원 전용 장바구니가 있다구 ?

localStorage 응용 Basket

  • 코드를 보고 이해하자
    • fetch map으로 뿌려주는 부분
      import { gql, useQuery } from "@apollo/client";
      import ProductList from "../../src/components/units/27-01-basket";
      const FETCH_BOARDS = gql`
        query fetchBoards{
            fetchBoards{
                _id
                writer
                title
                contents
            }
        }
      `
      export default function BasketPage(){
        const {data} = useQuery(FETCH_BOARDS)
        console.log(data)
            return (
                <>
                <div>
                {data?.fetchBoards.map((el) => (
                    <ProductList key={el._id}  
                                el={el}
                    />
                    ))}
              </div>
                    </>
            )
      }```  
    • ProductList 컴포넌트 부분
      장바구니에 있으면 버튼 바꾸어주는 부분 잘 확인하자 !
      import styled from "@emotion/styled";
      import { useState } from "react";
      import { IBoard } from "../../../commons/types/generated/types";
      import { getDate } from "../../utility";
      const MyRow = styled.div`
      display: flex;
      flex-direction: row;
      width: 550px;
      font-size: 20px;
      height: 70px;
      `;
      const MyColumn = styled.div`
      width: 150px;
      font-size: 20px;
      `;
      const Wrapper = styled.div`
      display: flex;
      flex-direction: row;
      `
      const Left = styled.div`
      `
      const Right = styled.div`
      font-size: 15px;  
      `
      export default function ProductList(props){
        const data = getDate(new Date)  
        const[inbasket,setInbasket] = useState(false)   
        const onClickBasket = (el : IBoard) => ()=>{
            console.log(el);
            const baskets = JSON.parse(localStorage.getItem("baskets") || "[]");
            // 2. 이미 담겼는지 확인하기
            const temp = baskets.filter((basketEl: IBoard) => basketEl._id === el._id);
            if (temp.length === 1) {
              const newbaskets = baskets.filter((basketEl: IBoard) => basketEl._id !== el._id);          localStorage.setItem("baskets",JSON.stringify(newbaskets))
              setInbasket(false)
              return;
            }
            // 3. 장바구니에 담기
            const { __typename, ...newEl } = el;
            baskets.push(newEl);       
            localStorage.setItem("baskets", JSON.stringify(baskets));
            localStorage.setItem("date",data)
            setInbasket(true)
                 }
            console.log(data)
        return(
            <Wrapper>         
              <Left>
                    <MyRow key={props.el._id}>
                    <MyColumn>{props.el.writer}</MyColumn>
                    <MyColumn>{props.el.title}</MyColumn>
                    <button onClick={onClickBasket(props.el)}>{ inbasket ? "장바구니 취소" : "장바구니 담기"}</button>
                  </MyRow>
              </Left>
              <Right>
                {inbasket ?  <MyColumn> {data}에 장바구니에 담은 상품 입니다 
      </MyColumn> : <MyColumn></MyColumn>           
              </Right>            
      </Wrapper>
        )
      } ```

-오늘 나의 알고리즘 시도

-세준쌤의 풀이 (나의 시도와 정석풀이가 접근이 비슷해서 기분이 좋았음! 좀 더 생각하는 능력을 기르자)

function solution(n, lost, reserve) { 
   const losted = [...lost] //lost데이터가 filter되기 이전의 데이터를 저장한다 
   lost = lost.filter(student => !reserve.includes(student)).sort((a,b)=>a-b)
   reserve = reserve.filter(student => !losted.includes(student)).sort()
       let answer = n- lost.length;
   for(let i =0; i< lost.length; i++){
       //내 앞번호 학생이 여벌 체육복을 가지고 있는지 검사 
       if(reserve.includes(lost[i] -1)){
           reserve.splice(reserve.indexOf(lost[i]-1),1)
           answer++
           
           //뒷번호 검사 
       } else if(reserve.includes(lost[i]+1)){
           reserve.splice(reserve.indexOf(lost[i]+1),1)
           answer ++
       }
   } return answer }```
                                

                                
                                
                                ```
                             reduce 풀이법도 알아놓자 ..!              
                                                             
profile
카지노 딜러에서 프론트엔드 개발자로 변신하는 중입니다! 코드캠프 프론트엔드 6기 과정을 수강중이며, 노원두 멘토님의 지도아래 컴맹을 열심히 탈출하는 중입니다.다소 주관적이나 코드캠프의 커리큘럼과 하루일정을 자세히 포스팅중입니다! mollyddong@gmail.com

0개의 댓글