프로젝트 생성

npx create-react-app toyproject

 

패키지 설치

npm install tailwindcss postcss-cli autoprefixer

 

css 설정파일 생성 ( tailwind.config.js)

npx tailwindcss init -p

 

PostCSS 설정 파일 생성

notepad postcss.config.js

 

내용 추가

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [],
  theme: {
    extend: {},
  },
  plugins: [],
}

 

index.css

@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';


body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}

 

tailwind.config.js

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./src/**/*.{js,ts,jsx,tsx}'],
  theme: {
      extend: {},
  },
  plugins: [],
};

 

추가적으로 만들기 전 라우터 사용하기 위해서 설치

npm install react-router-dom

 

// import logo from './logo.svg';
import './App.css';
import React from 'react';
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Main from './pages/main';
// import About from './pages/About';



function App() {
  return (
    <BrowserRouter>
      {/* <Header /> */}
      <Routes>
        <Route path="/" element={<Main />} />
      </Routes>
    </BrowserRouter>
  );
}

export default App;

 

만들어준 후 공통 header.tsx 작업을 들어간다.

 

일단 메인쪽에서 애니메이션 효과를 사용할 것이기에 해당 npm 을 설치한다.

npm install react-scroll framer-motion

 

import React from 'react';
import { Link } from 'react-router-dom';

const Header: React.FunctionComponent = () => {
  return (
    <header>
        <h2 className="dn none">상단메뉴</h2>
        <div>
            <button>견적문의</button>
            <p>000-0000-0000</p>
        </div>
      <nav>
        <ul>
          <li>
            <Link to="/">Main</Link>
          </li>
          <li>
            <Link to="/about">회사 소개</Link>
          </li>
          <li>
            <Link to="/about">사업 현황</Link>
          </li>
          <li>
            <Link to="/about">파트너사 문의</Link>
          </li>
        </ul>
      </nav>
    </header>
  );
};

export default Header;

헤더 부분 만들어주고 좀있다가 테일윈드css 를 적용해보겠다.

공식 문서 

https://tailwindcss.com/docs/installation

 

Installation - Tailwind CSS

The simplest and fastest way to get up and running with Tailwind CSS from scratch is with the Tailwind CLI tool.

tailwindcss.com

 

여기서 필요한 부분을 확인하면서 추가를 하도록 하겠다.

정렬을 위해서 해당 클래스를 넣어주면

 

이런형태로 들어간다.

 

 

이런식으로 적용이 되는걸 확인 할 수 있다. 

추후에 개인 프로젝트로 하나 만들예정이다. 

 

깃허브

https://github.com/asdf132645/finish

React.lazy() 와 Suspense 먼저 알아보겠다. (해당 기록은 학습한 내용을 전반적으로 기록하면서 정리한 내용이다.)

코드 분할과 지연된 컴포넌트 로딩은 초기 시간이 감소, 성능향상에 장점이 있다.

React.lazy()

React.lazy 함수를 사용하면 dynamic import를 사용해 컴포넌트를 렌더링할 수 있다. 

다이나믹 임포트는 spa 이므로 한번에  사용하지 않는 컴포넌트까지 불러오는 단점이 존재한다.

리엑트에서는 React.lazy 를 통하여 동적으로 import 즉 모듈등 페이지를 불어올 수 있도록 제공한다. 

이렇게 React.lazy를 사용하면 처음부터 불러오는게 아니라 동적으로 필요할때 불러오기 떄문에

초기 렌더링 지연시간을 어느정도 줄일 수 있다.

 

구문은

import Component from './Component';

const Component = React.lazy(() => import('./Component'));

 

 React.lazy로 감싼 컴포넌트는 단독으로 쓰일 수는 없고, React.suspense 컴포넌트의 하위에서 렌더링을 해야 한다.

 


React.Suspense

라우터로 분기가 나누어진 컴포넌트들을 위 코드처럼 lazy 를 통해 임폴트 하면 해당 path로 이동할때 컴포넌트를 불러온다 이 과정에서 로딩하는 시간이 생기게 된다.

 

서스펜스는 아직 렌더링이 준비되지 않은 컴포넌트가 있을 떄 로딩 화면을 보여주고, 로딩이 완료되면 렌더링이 준비된 컴포넌트를 보여주는 기능이다.

기본 문법

<Suspense fallback={<Spinner />}>
  <UserList />
</Suspense>

 

위와 같이 사용하여 감싸고 사용하며 비동기 동기(useEffect, useState)api 호출등으로 사이드 이펙트가 발생할때 그 부작용을 방지할 수 있으며 해결 방안이 된다.


 

React.lazy와 Suspense의 적용

앱에 코드 분할을 도입할 곳을 결정하는것은 사실 까다롭다. 중간에 적용시키는 것보다는 웹 페이지를 불러오고 진입하는 단계인 route 에 이 두기능을 적용하는게 좋다.

import { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';

const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));

const App = () => (
  <Router>
    <Suspense fallback={<div>Loading...</div>}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </Suspense>
  </Router>
);

 

라우터에 Suspense를 적용하는 것은 간단한 편이라고 한다.

라우터가 분기되는 컴포넌트에서 각 컴포넌트에 React.lazy를 사용하여 import한다.

그리고 Route 컴포넌트들을 Suspense로 감싼 후 로딩 화면으로 사용할 컴포넌트를 fallback 속성으로 설정해주면 된다.

초기 렌더링 시간이 줄어드는 분명한 장점이 있으나 페이지를 이동하는 과정마다 로딩 화면이 보여지기 때문에 서비스에 따라서 적용 여부를 결정해야 한다.

이번글은  깔끔하면서 체계적인 모듈화 및 코드를 작성할 수있도록 리액트 클린 아키텍처를 작성해보겠다.

 

아키텍처의 중요성은 로버트 c 마틴의 클린 아키텍처에서 서술하기를 기능은 구조에 의지한다는 것이다. 이 점을 이해하기위해서는 코딩이라는 행위에 담겨져 있는 내재적 관점을 살펴 봐야 한다. 우리가 회사에 들어가서 하는 행위는 코딩이며 목정적성을 생각하면 다음과 같다.

 

우리는 코드를 이해하고 읽으며 수정/ 추가 유지보수 하는 일을 많이 하게된다. 따라서 이런 행위를 우리는 제일 먼저 프로젝트의 구조를 파악하고 아키텍처와도 밀접한 관계성을 갖는다. 예를 들어 지저분한 방에서 물건을 찾을 때 와 정돈된 방에서 물건을 찾는 속도는 차이가 나듯이 프로젝트의 구조에서도 정돈된 구조는 개발자의 속도에도 영향을 끼친다.

 

회사의 입장에서는 클린한 코드로 최소한의 인력으로 최대의 이익을 창출해야 하며 이는 소프트웨어 아키텍처의 목표인 필요한 시스템을 만들고 유지보수하는데 투입되는 인력을 최소화 하는것과 상응하게 된다.

 

클린 아키텍처란?

로버트 가 제안한 방법으로 관심사를 확실히 분리하여 효율적으로 재사용이 높은 컴포넌트를 구현 할 수 있게끔 만들수 있는 아키텍처 이다. 최소한의 노력으로 최대의 효율을 얻게 해주는 것이다.

 

 

클린 아키텍처의 기본 이론

  • 체계적으로 계층이 분리되어야한다.
  • 각 계층은 서로 맡은 역할을 충실히 수행한다.
  • 계층간의 의존성 관계가 잘 이루어져야한다.

핵심 개념

  • 안쪽의 원(비즈니스 로직) 바깥의 원(UI나 data source)에 대해 전혀 알지 못한다.
  • 안쪽 영역으로 갈수록 추상화와 정책의 수준이 높아지며, 반대로 갈수록 구체적인 세부사항으로 구성된다.

클린아키텍처 구조의 이해

 

이 글에서는 Next.js 프로젝트를 대상으로 위의 규칙을 준수하여 구성해 보겠다.
Next.js의 많은 예제들의 수준과 비슷하게 한다면, 아래와 같은 계층을 가진 구조의 모양이 나올 수 있다.

Core

입력과 출력으로부터 거리가 멀기 때문에 고수준이라고 볼 수 있는 계층이다.
앞서 말한 원칙에서 세부적인 도메인 로직이 아닌, 웹 애플리케이션 로직과 추상화된 코드들이 들어간다.
예를 들면 아래와 같은 성격의 코드들이 들어갈 수 있다.

  • 애플리케이션이 외부와의 통신을 위해 필요한 구현체
  • 도메인과 연관이 없는 고수준의 유틸리티
  • 재사용가능한 컴포넌트
    • ex) Button, Input, Select
  • 중요도가 높은 외부 모듈(라이브러리)의 어댑터

Core 계층에 있는 코드들은 절대 Core 원의 외부에 있는 계층(lib/components, pages)을 참조하면 안 된다.

Components / Lib

이 계층은 도메인에 종속적이며, Core 계층의 코드들을 참조할 수 있고, 아래 예시와 같은 코드들이 들어간다.
( 주문이라는 도메인이 속한 프로젝트라고 가정. )

 

- src
     - lib
         - order
             - constatns
             - hooks
             - mutations
             - queries
             - utils
             - ....
 
 - src
     - components
         - order
             - ItemList.tsx
             - Price.tsx
             - Payment.tsx
             - ...

 

  • Lib
    • 도메인에 관련되어 있는 로직들.
    • components 계층과 pages 계층에서 참조되어진다.
  • Components
    • 도메인에 관련되어 있는 컴포넌트.
    • 도메인에 국한되어 재사용성은 낮다.
    • pages 계층에서 참조되어진다.

Pages

도메인에 의존도가 높은 계층. 입력/출력과 밀접해있으며, 가장 저수준이라고 볼 수 있다.
Next.js의 기본 설정 값인 파일 시스템 기반으로 라우팅 처리를 하며, Components Lib 계층의 코드들을 참조하여 구성할 수 있다.

- src
   - pages
     - api
       - order
         - [...slug].tsx  // src/lib/order/.. 참조
         - ...
     - order
       - [id].tsx // src/components/order/.. 참조
       - _middleware.tsx // src/lib/order/.. 참조
       - ...

 

Dependency diagram

위의 구조와 규칙을 가지고 간략히 의존성 그래프로 표현하면 아래와 같은 모양이 될 것이다.

의존성은 모두 단방향으로만 흘러가고, 역으로 참조해서는 안 된다.

예를 들어, core 계층에 API 통신을 위한 구현체가 있다고 가정해 보자.
이 구현체는 UI의 형태 혹은 상태, 세부적인 도메인 로직를 알아서는 안되며, 알아도 좋을 게 없다.
마찬가지로 도메인에 종속적인 코드들 또한, 이 구현체가 비동기 통신을 위하여 어떤 객체를 사용하여 구현이 되었는지, 어떻게 API 서버와 통신을 하는지 전혀 알 필요가 없다.

이러한 관심사의 분리로 인해 각 모듈은 여러 책임에서 벗어나기 쉽고, 테스트하기도 더 쉬워지며, 유지 보수 비용도 줄어들 것이다.

 

아래는 내가 직접 응용한 구조이다. 

 

 

core -> common에 api 를 담은 폴더와 타입들을 지정해둔 각 서비스 마다 dto 폴더를 생성하여 정의하였고

useCasese -> 각 서비스 아래에 폴더이른과매칭되는 ts 파일로 어플리케이션 비즈니스 로직을 수행하는 영역으로 api 통신에 있어 중간다리하는 역할 하는 부분으로 구분을 지었다. 

controller -> 부분은 pages 폴더 추후에 추가할 custom hook 폴더를 추가할 예정이다.

 

결론

실무에서 업무를 하다보면 직관적이지 못한 프로젝트 구조들로 인해 많은 시간을 낭비하였다. 좋은코드구조는 남들이보기에 한눈에 들어오는 구조이며 누구나 이해할 수 있는 영역이라고 생각한다.그런 구조를 생각하면서 구조를 짜고 코드를 생성 한다면 개발자의 시간 등이 많이 아껴지리라고 본다.

 

 

깃허브 https://github.com/asdf132645/reactToyProject

 

node express 에 대한 개념 설명과 개념을 정리 해보겠다.

 

1. 미들웨어 스택

express 서버 코드의 핵심은 클라이언트의 각 요청을 처리하는 라우팅 로직을 구현하는것이고, 라우팅 로직을 구현하는 것은 곧 미들웨어 스택을 구출을 의미한다. 미들웨어 스택은 클라이언트의 요청을 처리하는 함수들이 설정된 순서대로 저장되어있는 구조를 의미한다. 이는 미들웨어, 라우터 핸들러, 에러 핸들러로 구성되어있다. 라우터라는 단위로 묶을 수 있다.

 

1. 애플리케이션 레벨 미들웨어

애플리케이션 레벨이란 아래서 살펴볼 express()로 생성할 수 있는 app 객체의 app.use()나 app.METHOD()(ex. app.get, app.post) 함수를 이용해 미들웨어를 app 인스턴스에 바인딩하는 미들웨어이다. 마운트 경로가 없는 미들웨어 함수는 앱이 요청을 수신할 때마다 실행하게 된다.

 

app.get('/pages/', (req, res, next) => {
  console.log('Time : ', Date.now());
  next();
});

app.get((req, res, next) => {
  console.log('Not Found');
});

 

하나의 경로에 app 메서드 여러개가 묶일 수 있는데 다음예시를 주의깊게 보자

 

app.get('/pages/:id', (req, res, next) => {
  if (req.params.id == 0) next('route');
  else next();
}, (req, res, next) => {
  res.send('regular');
}
});

app.get('/pages/:id', (req, res, next) => {
  res.send('special');
}

 

위에 경우는 2개묶인 app 메서드가 위에 있으므로 special 보다 먼저 실행된다. next() 는 지금 라우터 미들웨어 스택을 벗어난 다음 라우터로 제어가 넘어가게 하는것이다. 라우터 미들워ㅔ어 스택이란 하나의 app.use()나 app.메서드 에 묵인 라우팅 미들웨어들을 말한다.

여기서 그냥 next()는 regular로 넘어가게 된다. id가 0이 아니라서 regular로 넘어간다면, 거기서 next()를 호출하지 않았으므로 special은 호출되지 않는다. 

next() 안에 인자가 들어가는 경우는 아마 next('route')와 next(err)뿐일 것 같다. next 안에 뭔가 인자가 들어가면 express 앱은 그것을 오류 발생이라고 보고 오류 처리와 관련없는 다른 모든 미들웨어를 다 건너뛰고 오류 처리(error handling) 라우터로 넘어간다. 단 하나 'route'만 빼고 말이다. 'route'는 현재 메소드를 벗어나 path에 해당하는 다음 라우터로 제어가 넘어가는 것이다.

 

2. 라우터 레벨 미들웨어

라우터 레벨은 express.Router()로 생성할 수 있는 router 인스턴스에 미들웨어가 바인딩되는 것이다. 그것 외에는 애플리케이션 레벨 미들웨어가 차이가 없다. router.use()나 router.METHOD() 함수를 이용해 로드할 수 있다.

express.Router() 로 router 객체를 생성할 수 있는데, 미들웨어와 HTTP 메소드 라우트를 router 객체에 붙일 수 있다.

 

//app.js
const express = require('express');
const app = express();
const pageRouter = ('./routes/pages');
app.use('/pages', pageRouter);
//pages.js
const express = require('express');
const router = express.Router();

router.get('/pages/:id', (req, res, next) => {
  //pages id가 0이면 'regular'가 아닌 'special'로 넘어감
  if (req.params.id == 0) next('route');
  //pages id가 0이 아니라면 'regular'로 넘어감
  else next();
}, (req, res, next) => {
  res.send('regular');
}
});

//pages id가 0일 때 넘어올 미들웨어
router.get('/pages/:id', (req, res, next) => {
  res.send('special');
}

module.exports = router;

 

3. 오류 처리 미들웨어

오류 처리 미들웨어는 (err, req, res, next)를 인자로 받는 것이다. 항상 4개의 매개변수가 필요하다. 이게 오류 처리 미들웨어의 시그니처다. err.stack으로 에러 메시지를 볼 수 있다.

 

//오류의 종류에 상관없이 모든 오류를 잡는 미들웨어
app.get((err, req, res, next) => {
  console.log(err.stack);
  res.status(500).send('Something broke!');
});

주의할 점은 오류 처리 미들웨어는 app.use() 및 라우트 호출을 정의한 뒤 거의 코드의 맨 끝에 정의해야 한다는 점이다. 

위의 경우 모든 오류를 잡는 미들웨어 하나만 만들었는데, 에러마다 다른 오류 처리 미들웨어 함수를 정의할 수도 있다. 이 경우 catch-all 에러 핸들러는 그 오류 처리 미들웨어들 중에서도 가장 아래 있어야 한다.

오류 처리 미들웨어는 다음과 같이 부를 수 있다.

 

4. 기본 제공 미들웨어

기본 제공은 정적 리소스를 제공할 루트 디렉토리를 정하는 express.static 같은 것이 있다. 현재 문서에는 빌트인 미들웨어는 express.static 뿐이라고 되어 있다. 정적 파일을 전달해주는데, 여기서는 /public 디렉토리가 정적 파일들이 모여 있는 루트 디렉토리가 된다. 아래서 더 자세히 설명하겠다.

app.use(express.static(__dirname + '/public'));

 

 

5. 써드파티 미들웨어

마지막으로 써드파티 미들웨어는 npm 에서 설치한 helmet이나 cookie-parser 같은 모듈들이 해당이 된다. 쉽게 말해 express 자체적으로 제공하지 않고 따로 설치해야 하는 것들은 다 써드파티라고 보면 된다.

npm i cookie-parser
const express = require('express');
const app = express();
const cookieParser = require('cookie-parser');

app.use(cookieParser());

이 때 cookieParser()를 하면 미들웨어를 반환한다. 

Redux

1. 스토어 (Store): Redux에서는 애플리케이션의 상태를 담고 있는 단일 객체인 스토어를 사용한. 스토어는 애플리케이션의 전역 상태를 관리하며, 모든 상태 변경은 스토어를 통해 이루어진다.

2. 액션 (Action): 상태 변경을 위한 명령어를 나타내는 객체입니다. 액션은 반드시 type 속성을 가지고 있어야 한다.

3. 리듀서 (Reducer): 현재 상태와 액션을 받아 새로운 상태를 반환하는 순수 함수입니다. 애플리케이션의 전역 상태가 어떻게 변경되는지를 정의 한다.

4. 디스패치 (Dispatch): 액션을 리듀서로 보내 상태를 변경하는 것을 의미합니다. store.dispatch(action) 형태로 사용된다.

5. 컨테이너 컴포넌트 (Container Components): Redux와 연결되어 상태를 관리하는 리액트 컴포넌트이다.

 

리덕스 동작 원리

  • 리덕스의 스토어는 Context로 구성되어 Provider 컴포넌트의 value props로 전달된다.
  • 해당 Context는 구체적으로 { store, subscription }과 같은 형태를 띠고 있다.
  • connect() 함수에 의해 만들어진 컴포넌트나 useSelector() 함수를 사용한 컴포넌트는 액션이 디스패치 된 후 (어떠한 구독 메커니즘에 의해) 강제적으로 리렌더링이 유발된다. 이때 해당 컴포넌트는 Provider 컴포넌트가 제공해주는 리덕스 스토어의 상태를 읽는다.
  • 참고로 스토어는 늘 참조값이 같은 가변 객체에 해당하며, Provider 컴포넌트는 중간에 스토어가 다른 걸로 바뀌지 않는 이상 늘 같은 참조값을 가지는 객체를 Context로 구성하도록 구현된다. 따라서 액션을 디스패치 하든 무엇을 하든, Provider 컴포넌트에 의해 자식 컴포넌트들이 전부 리렌더링 될 일은 없다. 즉, 리렌더링은 오로지 구독 메커니즘에 의존하여 발생할 뿐이다.


Redux-Thunk

1. Thunk: Redux-Thunk은 비동기 작업을 처리하기 위해 사용되는 미들웨어 중 하나입니다. Thunk는 함수 형태로 감싸진 액션을 다룰 수 있게 해준다.

2. 비동기 작업 처리: Thunk를 이용하면 액션 생성자에서 비동기 작업을 수행하고, 작업이 완료된 후에 액션을 디스패치할 수 있다.

3. 단점: 복잡한 애플리케이션에서 비동기 코드가 늘어날수록 코드의 복잡성이 증가할 수 있다.

 

 

 


Redux-Saga 사가 (= 제네레이터 함수)

1. Generator 함수: Redux-Saga는 Generator 함수를 이용하여 복잡한 비동기 흐름을 제어하는 미들웨어이다.

2. Side Effects 처리: Redux-Saga를 사용하면 애플리케이션의 사이드 이펙트(네트워크 요청, 로깅 등)를 쉽게 다룰 수 있다.

3. 비동기 제어: 액션을 모니터링하고, 특정 액션이 발생할 때 비동기 작업을 시작하거나 중단할 수 있다.

4. 장점: 코드의 가독성이 뛰어나며, 비동기 작업을 더 선언적이고 효과적으로 관리할 수 있다.

이렇게 Redux, Redux-Thunk, 그리고 Redux-Saga는 각각의 특징을 가지고 있으며, 상황에 따라 선택하여 사용하면 된다. Redux는 상태 관리의 기본이 되는 라이브러리이며, Thunk와 Saga는 비동기 작업을 다루는데에 있어 각각의 장단이 있다.


이펙트

  • 사가를 실행하는 실행부에게 어떠한 동작을 수행해야 할지 알려주는 일반 객체(Plain Object)이다.
  • 일반적으로 put(), call(), select() 등의 헬퍼 함수를 호출함으로써 해당 이펙트 객체를 생성한다.
  • 이펙트 자체는 단순 객체이므로 아무런 사이드 이펙트를 발생시키지 않는다. 따라서 테스트가 용이하다.

 

주요 이펙트

  • call(fn, ...args) : (비동기 혹은 동기) 함수 fn을 호출한다.
  • select(selector) : selector를 이용하여 리덕스 스토어의 상태를 읽어온다. (= store.getState() 함수)
  • put(action) : action을 디스패치 한다. (= store.dispatch() 함수)
  • take(actionType) : actionType의 액션이 디스패치 될 때까지 기다린다.
  • fork(saga, ...args) : 새로운 실행 맥락으로 saga를 실행한다.
  • takeEvery(actionType, saga, ...args) : 사가를 하나 fork 하는 헬퍼 함수이다. 해당 사가는 actionType의 액션을 기다렸다가 saga를 fork 하는 작업을 무한히 반복한다.




Atom: 상태의 기본 단위
리액트 리코일에서는 Atom이라는 개념이 핵심입니다. 각 Atom은 고유한 키와 초기값을 가지고 있어 해당 키를 통해 상태에 접근할 수 있다. 아래는 간단한 Atom의 예시다.

 

import { atom } from 'recoil';

export const myState = atom({
  key: 'myState', // 고유한 키
  default: '', // 초기값
});

 

Selector: 파생된 상태 계산
Selector는 Atom에서 파생된 값을 계산하거나 다른 Selector에서 파생된 값을 계산하는 데 사용된다. 이를 통해 복잡한 상태 로직을 간단하게 처리할 수 있다.

import { selector } from 'recoil';
import { myState } from './atoms';

export const myDerivedState = selector({
  key: 'myDerivedState',
  get: ({ get }) => {
    const originalState = get(myState);
    // 상태에 대한 계산 로직
    return transform(originalState);
  },
});

 

RecoilRoot: 전역 상태 관리
RecoilRoot은 Recoil 애플리케이션의 상태를 관리하기 위한 컨테이너 역할을 한다. 애플리케이션 최상위 컴포넌트에서 RecoilRoot을 사용하여 전역 상태를 관리한다.

import { RecoilRoot } from 'recoil';

function App() {
  return (
    <RecoilRoot>
      {/* 애플리케이션 컴포넌트들 */}
    </RecoilRoot>
  );
}

 

사용 예시: 간편한 상태 관리

import { useRecoilState, useRecoilValue } from 'recoil';
import { myState, myDerivedState } from './atoms';

function MyComponent() {
  const [state, setState] = useRecoilState(myState);
  const derivedState = useRecoilValue(myDerivedState);

  const handleChange = (e) => {
    setState(e.target.value);
  };

  return (
    <div>
      <input type="text" value={state} onChange={handleChange} />
      <p>Derived State: {derivedState}</p>
    </div>
  );
}

 

이렇게 리액트 리코일을 사용하면 복잡한 상태 관리도 간편하게 처리할 수 있다.

 

공식 문서: https://recoiljs.org/

+ Recent posts