2023-09-14 16:46:13

 

 

Redux

Redux는 상태 관리를 위한 라이브러리로 주로 React와 함께 사용되며 React 뿐만 아니라 다른 프레임워크나 라이브러리와도 호환됩니다. 

Redux를 사용하는 주된 이유는 복잡한 애플리케이션의 상태를 예측 가능하고 효율적으로 관리하기 위함이지만 작은 애플리케이션 또는 단순한 상태 구조의 애플리케이션에서는 Redux의 도입이 부담이 될 수 있습니다. 따라서 상태 관리 필요성, 애플리케이션의 크기 및 복잡성에 따라 적절하게 사용하는 것이 좋습니다.

 

설치 방법

npm install redux

 

장점

예측 가능한 상태 관리 : Redux는 일관적으로 동작하고 서로 다른 환경에서도 실행 되며 테스트하기 쉬운 앱을 작성하도록 도와줍니다.

중앙화 : 앱의 상태와 로직을 중앙화하여 실행 취소, 다시 실행, 영속적인 상태 등의 강력한 기능을 사용할 수 있게 합니다.

쉬운 디버깅 : Redux DevTools는 앱의 상태, 액션 및 액션에 따른 상태 변경을 실시간으로 확인할 수 있습니다.

유연함 : Redux는 큰 커뮤니티 지원을 받고 있으며 다양한 미들웨어, 확장 및 플러그인이 있습니다.

 

중요 개념

Store : 애플리케이션의 상태를 저장하는 중앙 저장소로 Redux에서는 하나의 Store 만 존재 합니다.

State : 애플리케이션의 현재 상태를 나타내며 객체, 배열, 문자열, 정수 등 어떤 데이터 유형이라도 상태가 될 수 있습니다.

Action : 상태를 변경하기 위한 정보를 포함하는 객체로 모든 Action은 type 필드를 가지고 있으면 필요한 경우 데이터를 추가할 수 있습니다.

Reducer : Action을 통해 상태를 어떻게 변경할지 결정하는 순수 함수 입니다. Reducer는 현재 상태와 액션을 인수로 받아 새로운 상태를 반환 합니다.

Dispatch : Action을 Store에 전달하는 메소드로 Dispatch를 통해 상태가 변경됩니다.

 

 

Style Components

Style-components는 주로 React와 함께 사용되며 JavaScript 내에서 CSS를 스타일링 할 수 있게 도와주는 라이브러리입니다.

 

설치 방법

npm install styled-components

 

주요 특징

자동 중요 CSS : styled-components는 페이지에 렌더링된 컴포넌트를 추적하고 해당 스타일만 자동으로 주입하여 코드 분할과 결합을 하여 필요한 코드만 로드합니다.

클래스 이름 버그 없음 : styled-components는 스타일에 대한 고유한 클래스 이름을 생성하여 중복, 중첩 또는 철자 오류를 검사합니다.

CSS의 쉬운 삭제 : 클래스 이름이 코드베이스의 어디에 사용되는지 알기 어려울 수 있는데 styled-components는 모든 스타일링이 특정 컴포넌트와 연결되어 있기 때문에 알기 쉽습니다.

간단한 동적 스타일링 : 컴포넌트의 props나 전역 테마를 기반으로 스타일링을 조정하는 것이 수동으로 수십 개의 클래스를 관리할 필요 없고 간단하고 직관적입니다. 쉽게 말해 값에 변화가 필요한 스타일링을 해야 할때 styled-components에 속성값을 동적으로 입력할 수 있습니다.

수월한 유지보수 : 해당 컴포넌트에 적용되는 스타일링 파일을 찾기 쉬워 코드베이스 크기에 관계없이 유지보수가 쉽습니다.

자동 벤더 접두사 추가 : 현재 표준에 맞게 CSS를 작성하고 나머지는 styled-component가 알아서 해줍니다.

벤더 접두사는 CSS 속성 앞에 붙는, 브라우저 제조업체(prefix)를 나타내는 접두사를 의미

-webkit- : Chrome, Safari, newer versions of Opera 등 WebKit 엔진을 사용하는 브라우저용
-moz- : Firefox용
-o- : 구버전의 Opera용
-ms- : Internet Explorer 및 Edge용
// CSS
// CSS3의 transform 속성은 초기에 모든 브라우저에 지원되지않아 아래와 같이 여러 벤더 접두사를 사용해 스타일을 적용

-webkit-transform: rotate(30deg);
-moz-transform: rotate(30deg);
-ms-transform: rotate(30deg);
-o-transform: rotate(30deg);
transform: rotate(30deg);
// styled-components는 자동 벤더 접두사 추가가 되기 때문에 개발자가 수동으로 접두사를 추가할 필요가 없음
const RotatedBox = styled.div`
  transform: rotate(30deg);
`;

 

예시 코드

import styled from 'styled-components';

const ButtonTest = styled.button`
  background-color: blue;
  color: white;
  padding: 10px 20px;
`;

<ButtonTest>Click Me</ButtonTest>

 

 

Redux-Thunk

Redux-Thunk는 Redux 미들웨어의 일종으로 비동기 작업이나 부수 효과가 있는 Action을 처리하는데 사용됩니다.

일반적인 Redux의 Action은 객체를 반환 하지만 객체 뿐만 아니라 함수도 Dispatch 할 수 있습니다.

 

설치 방법

npm install redux-thunk

 

주요 특징

함수 반환 액션 생성자 : 기본적인 Redux Action 생성자는 Action 객체를 반환하는데 Redux-Thunk를 사용하면 함수도 반환 할 수 있습니다.

비동기 작업 처리 : Redux-Thunk는 API 호출, 데이터베이스 쿼리와 같은 비동기 작업을 처리하는데 유용하며 비동기 작업의 시작, 서공, 실패 등 다양한 Action을 Dispatch 할 수 있습니다.

조건부 액션 Dispatch : getState를 사용하여 현재 상태를 확인 및 특정 조건에 따라 Action을 Dispatch 하거나 무시할 수 있습니다.

복잡한 로직 캡슐화 : Redux의 기본 원칙 중 하나는 Reducer가 순수 함수이어야 한다는 것입니다. Reducer는 외부 상태나 변수에 의존하지 않고 부수 효과를 발생시키지 않아야 하는데 실제 애플리케이션에서는 API 호출, 타이머 설정, 로컬 스토리지 액세스 등의 부수 효과가 필요한 경우가 많습니다. Redux-Thunk는 이러한 복잡한 로직과 부수 효과를 Action 생성자 내에 캡슐화하게 해주어 Reducer를 순수하게 유지할 수 있게 도와줍니다.

간단한 미들웨어 체인 : Redux의 미들웨어 시스템은 여러 미들웨어를 체인으로 연결할 수 있게 해주는데 이 체인 내에서 각 미들웨어는 Action을 가로채서 추가적인 요청을 처리 할 수 있게 해줍니다.

확장성 :  Redux-Thunk는 비동기 작업을 처리하는 가장 기본적인 방법을 제공하여 특정 프로젝트의 요구사항에 따라 다양한 비동기 처리 패턴이나 추가적인 로직을 쉽게 구현 할 수 있습니다.

 

예시 코드

// Action 생성자
const fetchData = () => {
  return (dispatch) => {
    dispatch({ type: 'FETCH_DATA_START' });
    
    fetch('/api/data')
      .then(response => response.json())
      .then(data => {
        dispatch({ type: 'FETCH_DATA_SUCCESS', payload: data });
      })
      .catch(error => {
        dispatch({ type: 'FETCH_DATA_FAILURE', payload: error });
      });
  };
};

// store에서 사용
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';

const store = createStore(reducer, applyMiddleware(thunk));

 

도시의 날짜 정보 만들어오는 프로그램 만들어보기 

정말 간단한 프로그램이기 때문에 정확한 도시가 아니면 터지고 난리 나니 어떤 구조로 돌아가는지 이해용으로 좋습니다.

 

준비물 : openweather API Key

https://openweathermap.org/ 페이지 로그인 후 아이디 부분 클릭 하여 My API keys 에 있는 API Key 복사

이미지 좌측 하단 Key 부분의 key 값 필요

 

프로젝트 구조

 

사용 모듈

npx create-react-app TestWeatherAPI
npm install redux
npm install react-redux
npm install style-components
npm install redux-thunk

 

index.js

Redux store를 사용하기 위한 설정 

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
// Redux를 사용하기 위해 Provider를 최상단에서 사용
import { Provider } from "react-redux";
import { store } from "./redux/store";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <Provider store={store}>
    <App />
  </Provider>
);

reportWebVitals();

 

App.js

import "./App.css";
import WeatherBox from "./components/layout/weatherBox";

function App() {
  return (
    <div className="App">
      {/* <WeatherBox /> */}
      <WeatherBox></WeatherBox>
      {/*  */}
    </div>
  );
}

export default App;

 

components/layout/weatherBox/index.jsx

날씨 관련 페이지 레이아웃 및 입력 받은 input의 value를 dispatch를 통해 weatherAction으로 매개변수를 보낸 후 결과 값을 받아와 출력

import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { weather } from "../../../middleware/weatherAction";
import { WeatherWrap, WeatherBottom } from "./weather.styled";

const WeatherBox = () => {
  const dispatch = useDispatch();

  const [name, setName] = useState("");
  const weatherData = useSelector((state) => state.weather.weatherData);

  const getWeather = () => {
    dispatch(weather.getWeather(name));
  };

  useEffect(() => {
    console.log(weatherData);
  }, [weatherData]);

  return (
    <WeatherWrap>
      <div className="weatherTop">
        <label>city name : </label>

        <input
          onChange={(e) => {
            setName(e.target.value);
          }}
        ></input>

        <button onClick={() => getWeather()}>search weather</button>
      </div>

      <WeatherBottom>
        <span> city : {weatherData && weatherData.data?.name}</span>
        <span>
          {" "}
          weather :{weatherData && weatherData.data?.weather[0]?.main}{" "}
        </span>
      </WeatherBottom>
    </WeatherWrap>
  );
};

export default WeatherBox;

 

component/layout/weatherBox/weather.styled.js

WeatherBox 컴포넌트에 스타일링 적용

import styled from "styled-components";

export const WeatherBottom = styled.div`
  display: flex;
  flex-direction: column;
  align-items: flex-start;
`;

export const WeatherWrap = styled.div`
  margin: 10px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;

  // 이런식으로도 쓸 수 있다는 걸 보여주기위해 사용
  & ${WeatherBottom} {
  }
`;

 

middleware/weatherAction.js

WeatherBox에서 넘어온 상태값을 axios를 활용하여 API 요청 후 반환 dispatch를 통해 store의 상태값 전달

Action과 Reducer 로직을 따로 관리하기 하여, 즉 코드의 분리 관리를 위해 middleware를 사용

// axios를 활용하여 요청에 대한 응답 처리
import axios from "axios";

function getWeather(name) {
  return async (dispatch) => {
    const data = await axios.get(
      `https://api.openweathermap.org/data/2.5/weather?q=${name}&appid=[자기의 weather API Key를 입력]`
    );
    dispatch({ type: "GET_WEATHER", payload: data });
  };
}

export const weather = { getWeather };

 

redux/reducer/index.js

실습에선 1개의 reducer를 사용했지만 여러개의 reducer를 사용하게 되면

combineReducers({ weather }) <- 이부분에 객체를 여러개 넣어서 사용하면됩니다.

// combineReducers() : reducer 함수를 합쳐주는 redux 라이브러리에서 제공되는 함수 사용
import { combineReducers } from "redux";
import weather from "./weather";

const rootReducer = combineReducers({ weather });
// 여러개의 reducer가 있을 경우
// const rootReducer = combineReducers({ weather, test1, test2, test3 });
export default rootReducer;

 

redux/reducer/weather.js

넘어온 날씨 데이터를 상태값에 저장 후 반환

let init = {
  weatherData: {},
};

function reducer(state = init, action) {
  const { type, payload } = action;
  switch (type) {
    case "GET_WEATHER":
      return { ...state, weatherData: payload };

    default:
      return state;
  }
}

export default reducer;

 

redux/store.js

import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import rootReducer from "./reducer";

// 미들웨어 추가
// applyMiddleware 함수로 미들웨어 추가 반환되는 객체로 추가

// 미들웨어로 thunk를 추가 하는 방법
// applyMiddleware(thunk) 함수에 매개변수로 사용할 미들웨어 전달

export const store = createStore(rootReducer, applyMiddleware(thunk));

 

구현 화면

city name에 도시이름을 입력 후 search weather 버튼을 누르면 그 도시의 날씨를 알려줍니다.

728x90