Coding/Today I Learned (148)

05
01

볼떄마다 느끼는 참 개발자 스러운 곰

1. 개요

  • react의 전부라고 할 수 있는, 상태관리의 근본적인 원리도 모르고 쓰기는 react에게 미안하다.
  • 실무에서 느낀 상태관리의 의의에 대해 다시 생각해 보고 싶었다.

2. 상태?

 

Managing State – React

The library for web and native user interfaces

react.dev

  • react dev docs에 따르면, 직접적으로 명령에 대해 명시하지 않고, 시각적 상태에 따라 보고 싶은 UI를 설명한다고 한다.
  • 즉, UI 변경에 따른 결과를 말 그대로 '상태'라고 할 수있고, 그것을 저장, 변경, 컴포넌트 간 전달한다.
  • 이때, 상태는 UI 변경점에 따른 것이므로, 상태가 변하면 UI 도 자연스럽게 업데이트 될 것이다.

3. useState

  • 하루에도 수 십번 사용하는 근본 hook.
  • 상태를 변경하고, 사용할 수 있도록 한다.
const [data, setData] = useState([])
  • 첫 번째 인자로 상태값을, 두 번째 인자로 상태를 변경할 함수를 받아 구조 분해 할당으로 사용하게 된다.
  • 근본적 원리는, useState는 초기값을 처음 인자로 받아 disoatcher 인스턴스를 생성해, 유저가 원할 때마다 반환해 준다.
    • 특이점 :   우리는 처음 'const'로 useState를 구조분해할당 했기 때문에 data가 직접 바뀌지는 않는다.
    • 전역에 생성된 closuer가 useState가 실행될 때마다 초기값을 비교하고 할당해 준다.
    • setState가 실행되면 받은 인수로 closuer의 값을 변경시켜 주고, 리렌더링을 실행하며, 바뀐 값을 useState가 가져온다.

4. 전역 상태 관리

  • 결국, useState의 값은 한 컴포넌트 안에서만 사용가능하다.
  • 같은 상태를 위계가 다른 컴포넌트에서 사용하려고 하거나, 상태를 props 드릴링 하다보면 상태의 생명주기를 알 수 없게 되고, 무분별한 리렌더링이 일어난다.
  • 그래서 한번 선언한 상태를 다른 컴포넌트에서도 사용하기 위해 전역 수준에서 상태를 관리하기도 한다.
  • 기본적인 방법으로 Context API를 사용할 수 있다.
    • Context API는 '의존성 주입'을 위한 API 이다.
    • ContextProvuder 컴포넌트를 만들어 내부에 useState를 사용해 상태를 만들어주고 value를 props로 받는다.
    • value가 바뀔 때마다 상태를 변경해 주고 다시 바뀐 상태를 반환 한다.
    • 이후 ContextProvuder 아래의 children들은 useContext hook으로 상태에 접근이 가능하다.
    • 단점 :  props의 value가 바뀔 경우 모든 children들은 리렌더링 된다. 결국 상태이기 때문이다. 그래서 불필요한 리렌더링을 막기 위해 memoization이 필수적이다. 결국, 어디서 memoization이 일어나고, 상태가 변경하는지를 정확히 알지 못하게 되는 경우가 프로젝트 사이즈가 커지면 종종 일어나기 때문에 상태의 생명주기를 어지럽히는 요인이 된다.

5. Zustand

  • redux보다 간단하고, 필요 없는 렌더링을 조절하기가 간단한 전역 상태 관리 라이브러리이다.
 

Zustand

 

zustand-demo.pmnd.rs

import { create } from 'zustand'

const useStore = create((set) => ({
  count: 1,
  inc: () => set((state) => ({ count: state.count + 1 })),
}))

function Counter() {
  const { count, inc } = useStore()
  return (
    <div>
      <span>{count}</span>
      <button onClick={inc}>one up</button>
    </div>
  )
}
  • useState와 마찬가지로, 초기값, set 함수의 동작을 create 하고, hook으로 어디서든 접근이 가능하다.
  • 일반적으로 create로 만든 hook 들을 한 파일에서 관리하고, 사용해야 할 컴포넌트에서 가져와 사용한다.

6. 적절한 상태관리란?

  • 상태의 '범위'를 알고 사용하는 것이 중요하다. 각 스코프에 맞는 사이즈의 상태를 만들고, 상태의 생명주기를 개발자가 마음대로 조절할 수 있어야 한다. 불필요하게 모든 상태를 전역으로 사용하려 한다거나, 무분별한 props 드릴링으로 상태를 컴포넌트 간에 전달하는 방향은 다른 개발자들을 괴롭히는 일이다.
  • 필요에 의한 상태만을 만든다. 상태로 관리해야 할 것은 'UI 변화에 따른 값'이다. 한번 선언해 놓고 더 이상 변하지 않는 값을 가진 경우나, 초기값만 이 중요한 경우라면 굳이, 상태로 만들지 않고 선언해서도 충분히 사용할 수 있다. 불필요한 리렌더링을 줄이는 것도 큰 의의가 있다.
  • 여러 가지 전역 상태관리 라이브러리를 혼용해서 사용하지 않는다. 상태의 생명주기를 더욱 따라가기 힘들게 할 뿐이다.
  • 의존적이지 않고, 결합적이지 않은 코드를 작성하기 위해서는 전역 상태 관리를 사용하지 않는 것이 좋다. 필요에 따라 사용할 수 도 있지만 분명, 재사용에 큰 제약을 만들 것이다.
COMMENT
 
04
27

0부터 1까지만 알아보자

1. 개요

react query를 왜 사용하는가? 에 대한 질문은 너무 쉽게 할 수 있다. 왜냐고? 캐싱, 최적화, 언제 어디서든 서버에서 최신의 상태를 가져다가 전역으로 사용할 수 있는 매우 편한 라이브러리가 아닐수 없다! 하지만 세상에 완벽한게 어디있나, 분명 그에도 단점은 존제 할 것이다. 그래서 단점을 찾기 전에 가장 근본적인 질문부터 하나하나 알아 보려고 한다.

2. Query

- 많은 프론트 개발자들은 query와 친하지 않다.(사실 뭔지도 잘 모르겠다. 하지만 직접 쿼리를 써보라고 하면 또 쓴다...!? 어째서..)

- query : 질의

- DB와 통신에 필요한 요청을 질의하는 것을 말한다.

- 이를 위해 만들어지는 질문을 SQL이라고 부른다.

3. react-qurey

- 서버 상태관리를 위한 라이브러리

- fetching, caching, synchronizing, updating을 간단하게 hook으로 할 수 있다.

- 기존의 미들웨어는 복잡한 로직으로 만드는 과정이 쉽지않고, 이를 위해 보일러 플레이트를 만들어 여러 프로젝트에 적용한다고 해도, 코드의 양도 많아진다. 이를 react query는 대체할 수 있다.
(하지만 여기서 단점, 이렇게 내부 로직을 잘 알고 쓴다면 상관없지만, 다양한 hook들이 어떠한 방식을 통해 데이터를 다루고 있으며 데이터의 흐름을 직접 제어할 수 없다는 점에서 단점이 있다!)

- QueryClientProvider가 React Context API를 사용해 가장 최상단에 존제하기 때문에, 전역으로 사용할 수 있는 서버의 상태가 된다.

4. react-qurey의 기본 동작 순서

  1. 상태 확인
    • 데이터의 상태를 확인한다.
    • 데이터 상태의 종류
      • fresh : 최신화 된 데이터. 혹은, 아직 staleTime이 지나지 않아서 개발자가 보장하는 최신화된 데이터
            -> 이미 최신화된 데이터라면, 중복으로 네트워크 요청을 막음
      • fetching : 서버와 데이터를 통신하는 중
      • stale : 최신화 되지 않은 옛날 데이터 -> react qurey는 이 경우 다시 fetching -> fresh한 데이터로 상태를 바꾸려 한다.
      • delete : 삭제된 데이터. 혹은, cacheTime이 지나 케시에도 없는 데이터
  2. 상태에 따라 서버와 통신

5.간단한 원리

- 서버와 통신하여 데이터가 최신인지를 확인한다.

- 이때, 최초의 통신이 발생한다. 그래서 데이터가 최신인지를 확인하게 된다. 그리고 이는 state로 관리된다. (useQurey)

- 최신이라면, 캐싱하고 알맞는 queryKey에 바인딩 된다.

- 만일 데이터가 stale하여 다시 서버와 통신이 일어나 데이터가 최신화 되면, state도 바뀐 것이므로 리렌더된다.

- 더 자세한 내용은 아래 글 참고

 

React Query의 구조와 useQuery 실행 흐름 살펴보기 | 카카오엔터테인먼트 FE 기술블로그

함성준(kevin) 개발에는 인생(喜怒哀樂)이 담겨있습니다. 커피 좋아합니다.

fe-developers.kakaoent.com

6. state와의 접점

- react query는 데이터 패칭 라이브러리가 아니다.

 

Thinking in React Query

In this talk, we will learn how a different mindset can help us understand React Query and work with it efficiently.

tkdodo.eu

 

[번역] React Query 적으로 사고하기

React Query Maintainer인 Tkdodo가 알려주는 리액트 쿼리적으로 사고할 수 있는 3가지 요소들을 다루어봅니다.

velog.io

- 비동기 상태 관리자 (Async State Manager) 라고 한다.

- 클라이언트 단에서는 네트워크를 통신해서 불러온 데이터도 '상태'로 관리하고 UI에 필요한 데이터도 '상태'로 관리하게 된다. 필요에 따라 스코프를 넓혀 '전역 상태'로도 관리하게 된다.

- 프로젝트의 사이즈에 따라 전역상태가 관리가 힘들정도로 많아 진다면 혹은, 필요없는 상태들까지 전역으로 관리하거나 하는 행동은 코드 가독성과 유지보수에 치명적이다. (진짜 고통스럽다. 기능 추가 혹은 수정에 시간을 오래 쓰기보다 상태들을 따라가 찾는데 오랜 시간을 쓰게 된다.)

- 그래서 일단, 서버 상태와 클라이언트 상태를 분리시키는데 의의가 있다.

- 그리고 서버 상태는 효율적으로 관리될 필요가 더욱 크다. 필요할때 데이터를 불러 상태를 변경해 최신화 하고, 이미 최신의 데이터를 유지하고 있다면 상태를 업데이트 할 필요가 없어진다.

- 이때, 데이터의 최신화 여부가 상태의 업데이트를 함께 다뤄준다는 점이  react query의 최고 장점 되시겠다.

COMMENT
 
08
12

작은 것들을 모아 정리 하는 느낌이랑 비슷한 느낌이라 가져온 짤입니다 틀니 딱딱딱! (윈도우98입니다)

1. 문제의 발단

  • RN 강의를 듣던 중 강사님이 참 폴더 구조들을 정리를 잘한다고 생각했다.
  • 지금 프로젝트도 나름 정리를 하여 사용한다고 생각했지만, 뭔가 10% 부족하다고 느꼈다. 폴더 구조에 대한 정답은 없기 때문이다.
  • 그래서 그나마 정답에 가까운 것을 찾아 보려고 한다.

2. Atomic Design Pattern?

  • React의 구조는 수많은 컴포넌트로 되어 있다. 그러므로 똑같은 코드를 여러 번 사용하는 극한의 재사용성을 보여줄 수 있다.
  • 하지만 아무리 유용한 컴포넌트라도 어디에 있는지 찾지 못 하면 낭패다. 마치 침대 위 TV리모컨처럼 말이다.
  • 그래서 유형별로, 혹은 특정 공통점을 기준으로 잘 정리해 두려고 한다. 이때 atomic design pattern은 '디자인'을 중점적으로 이용하여 정리하고 그를 먼저 만들고 조립하는 디자인 시스템을 뜻한다.

3. 그림으로 보는 Atomic Design Pattern

출처 https://uxdesign.cc/atomic-design-2022-what-we-can-learn-from-eames-and-other-design-giants-4d8e2f579daa

  • 이렇게 자동차가 만들어지는 것처럼 가장 작은 단위 (작은 나사, 철 프레임등)가 뭉쳐 하나의 기능을 하는 의미 있는 구성품이 되고, 그것들이 모여 기능을 하는 프로젝트를 구성하게 된다.

출처 : https://actgovproject.bitbucket.io/

  • 웹버전에서 보면 이런 그림이다.
  • 작은 기능을 하는 태그, 디자인에 필요한 아이콘 등이 합쳐저서 하나의 기능을 하는 작은 컴포넌트가 되고, 그들이 모여 큰 레이아웃을 형성하게 되면서, 데이터를 넣으면 큰 페이지가 된다.

4. 어떻게 진행 되는가

1. Atoms : 가장 작은 디자인 요소의 단위
2. Molecules : 적어도 하나의 기능은 할 수 있는 컴포넌트 단위
3. Organisms : 하나의 기능을 하는 요소들을 합쳐 디자인적 요소까지 더한 유저들에게 의미를 부여한 단위
4. Template : 데이터를 연결하기 전의 레이아웃 단위
5. Page : 유저들이 보는 단위

5. 실전편

📦atomic-design-pattern-test
 ┣ 📂components
 ┃ ┣ 📂atoms
 ┃ ┣ 📂moecules
 ┃ ┣ 📂organisms
 ┃ ┗ 📂templates
 ┗ 📂pages
  • 정말 직관적인 네이밍
  • 가끔 헷갈리는 부분이 있다. 무엇이 molecules이고, 무엇이 organisms 일까??
    • 나는 너무 고민하지 않기로 했다. 그저 '우리'가 보기 편하면 모든 것이 완벽하다고 생각했기 때문에 기획자와 디자이너와 적당한 합의 후에 디자인 시스템부터 나누어 생각했다.
    • 우리가 이 컴포넌트는 molecules라고 말하면 molecules인 것이고, organisms이면 organisms인 것이다. 그래서 우리들만의 규칙을 만들어 나누어 주었다. (ex: 결제나 비즈니스 로직이 들어간 컴포넌트면 organisms로 보자)

6. 결론

  • 정답은 없다. 이것 또한 개발을 그리고, 구조를 편하게 정리하고 보기 위해 만드는 디자인 패턴일 뿐이니까 말이다.
  • 결국 우리가 선택한 구조는 페이지를 기준으로 기능적인 부분을 때서 나눠놓은 패턴이 되었다(아, 정리했는데 ㅎㅎ)
  • 프로젝트마다 어떤 디자인 페턴을 선택하는지가 이후에 프로젝트의 규모가 거대해졌을 때도 큰 영향을 미치니, 최대한 확장성과 정확한 규칙을 토대로 만들어 가는 것이 좋을 것 같다.
COMMENT
 
07
27

한 번 사용한 최초문자

문자열에서 한번만 사용한 문자를 찾으려고 합니다. 매개변수 s에 문자열이 주어지면 한번만 사용한 문자 중 문자열에서 가장 먼저 나타난 문자의 인덱스 번호를 반환하는 프로그램을 작성하세요. 인덱스는 1부터 시작합니다. 한번만 사용한 문자가 없을 경우 -1를 반환하세요.

s answer

"statitsics" 3
"aabb" -1
"stringshowtime" 3
"abcdeabcdfg" 5

제한사항: • 문자열 s의 길이는 100을 넘지 않습니다. • 문자열은 소문자로만 이루어져 있습니다.

입력예제 1 설명 : 한번만 사용한 문자는 a, c이고, 문자열에서 먼저 나타난 것은 a이고 인덱스는 3입니다.

const func = (string) => {
  // 주어진 문자열을 각 문자로 분리하여 배열로 만든다.
  const strArr = string.slice().split('');
  // 결과를 저장할 객체를 생성한다.
  const result = {};

  // strArr 배열의 각 문자에 대해 처리,
  for (let i = 0; i < strArr.length; i++) {
    const currentChar = strArr[i];
    // 현재 문자가 result 객체의 프로퍼티로 존재하는지 반복문으로 확인한다.
    const existingValue = result[currentChar];

    // 현재 문자의 값이 존재하는지 확인한다.
    // 존재하는 경우 해당 값을 1 증가시킨다.
    // 존재하지 않는 경우(빈 값인 경우) 새로운 키를 생성하고 값을 1 준다.
    result[currentChar] = existingValue ? existingValue + 1 : 1;
  }

  // 0일 경우 -1이기 때문에 중간에 한번 걸러준다.
  const filterFunc = (result) => {
    return result === 0 ? -1 : result;
  }
  return filterFunc(Object.values(result).findIndex((el) => el === 1) + 1);
}
COMMENT