포스트

2024.07.18 프로그래머스 - React 동적 UI 개발 5

React 기반 동적 UI 개발 - 5

로그인 flow

로그인-flow.png

zustand를 통한 전역 상태 관리

설치

1
npm install zustand

authStore 작성

  • src / store / authStore.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { create } from "zustand";
import { devtools, StateStorage } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";

interface StoreState {
  isLoggedIn: boolean;
}

interface StoreAction {
  storeLogin: (token: string) => void;
  storeLogout: () => void;
}

export const useAuthStore = create<StoreState & StoreAction>()(
  devtools(
    immer((set) => ({
      isLoggedIn: false,
      storeLogin: (token: string) => set((state: StoreState) => {state.isLoggedIn = true}),
      storeLogout: () => set((state: StoreState) => {state.isLoggedIn = false}),
    })),
    { name: "authStore" }
  )
);

  • devtools 를 통해 Redux-develope 개발자 도구에서 store를 확인할 수 있다.
  • immer를 통해 불변성 유지를 통한 state 로직 간소화를 진행할 수 있다.

쿼리 스트링 사용하기

React-router의 useSearchParams 훅을 사용하면, 현재 client 환경에서 현재 접속? 중인 url 쿼리 스트링을 수신할 수 있다.

이를 기반으로 기타 다른 이벤트 없이 쿼리스트링을 통해 조건부 렌더링을 수행 할 수 있다.

  • 도서 카테고리 선택 코드 (BooksFilter.tsx)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    
       import React from "react";
      import styled from "styled-components";
      import { useCategory } from "../../../hooks/useCategory";
      import Button from "../../../shared/components/Button";
      import { useSearchParams } from "react-router-dom";
      import { QUERYSTRING } from "../../../shared/constants/queryString";
        
      function BooksFilter() {
        const { categories } = useCategory();
        const [searchParams, setSearchParams] = useSearchParams();
        
        const handleCategory = (id: number | null) => {
          const newSearchParams = new URLSearchParams(searchParams);
          console.log(newSearchParams);
          if (id === null) {
            newSearchParams.delete(QUERYSTRING.CATEGORY_ID);
          } else {
            newSearchParams.set(QUERYSTRING.CATEGORY_ID, id.toString());
            newSearchParams.delete(QUERYSTRING.PAGE);
          }
        
          setSearchParams(newSearchParams);
        };
        
        const handleNews = () => {
          const newSearchParams = new URLSearchParams(searchParams);
        
          if (newSearchParams.get(QUERYSTRING.NEWS)) {
            newSearchParams.delete(QUERYSTRING.NEWS);
          } else {
            newSearchParams.set(QUERYSTRING.NEWS, "true");
          }
          setSearchParams(newSearchParams);
        };
        
        return (
          <BooksFilterStyle>
            <div className="category">
              {categories.map((item) => (
                <Button
                  size="medium"
                  scheme={item.isActive ? "primary" : "normal"}
                  key={item.id}
                  onClick={() => handleCategory(item.id)}
                >
                  {item.name}
                </Button>
              ))}
            </div>
            <div className="new">
              <Button
                size="medium"
                scheme={searchParams.get(QUERYSTRING.NEWS) ? "primary" : "normal"}
                onClick={handleNews}
              >
                신간
              </Button>
            </div>
          </BooksFilterStyle>
        );
      }
        
      const BooksFilterStyle = styled.div`
        display: flex;
        gap: 24px;
        
        .category {
          display: flex;
          gap: 8px;
        }
      `;
        
      export default BooksFilter;
        
    

useSearchParams 는 배열을 반환한다.

  • 첫번째 반환 값 : 현재 쿼리스트링 상태? 값
  • 두번째 반환 값 : 쿼리스트링 setter

여기서 중요한점은 new URLSearchParams 를 통해 useSearchParams로 반환받은

현재 searchParams 값을 기반으로 쿼리스트링 객체를 생성 할 수 있다는 점이다.

URLSearchParams 인터페이스는 URL의 쿼리 문자열을 대상으로 작업할 수 있는 유틸리티 메서드를 정의합니다.

-MDN-

해당 method를 통해 useLocation으로 반환받은 location.search 와 같은 값 또한

객체 형태로의 간편한 사용이 가능해진다.

오늘 수강중 느낀점

확실히 구조를 이해하면서 하니까 어렵지만 프로젝트가 머리속에 들어온다.

이전에 강의에서 진행한 BE 코드를 같이 사용하는데,

나는 그 때 내 멋대로 작성해서, sql query 수정만 한 세월 한 것 같다.

풀스택 역량을 갖추는게 왜 어려운지 확연히 느낀 하루 였다.

그래도 한단계 또 성장한거 같아서 아주 이득이다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.