리액트 숙련 - Action Creator , Payload, Duck 패턴, react-router-dom, Dynamic Route 동기적 vs. 비동기적 프로그래밍, REST API 그리고 JSON

새벽로즈·2023년 11월 9일
1

TIL

목록 보기
42/72
post-thumbnail

Action Creator

  • 액션 객체를 생성하는 함수
  • 액션 객체의 value를 변경할 때, 문자열이 아닌 상수를 사용하여 유지보수와 가독성을 향상됨

Action Creator 만들기

  • 액션 value를 상수로 정의
  • 액션 객체를 반환하는 함수를 만들어 Action Creator를 생성
//[ Counter.js ]
//초기값
const initialState = {
  number: 0,
};
// const [number, setNumber] = usestate(0)과 같은 것

export const PLUS_ONE = "counter/PLUS_ONE";
export const MINUS_ONE = "counter/MINUS_ONE";

// 리듀서 = state에 변화를 일으키는 함수
// 변화 : state를 action의 type에 따라 변경하는 함수
// input : state와 action
const counter = (state = initialState, action) => {
  switch (action.type) {
    case PLUS_ONE:
      return { number: state.number + 1 };
    case MINUS_ONE:
      return { number: state.number - 1 };
    default:
      return state;
  }
};

export default counter;

Action Creator 사용하기

  • Action Creator를 import하여 컴포넌트에서 사용
  • dispatch에 대신 함수를 전달하여 액션 객체를 생성
//[ App.jsx ]
import logo from "./logo.svg";
import "./App.css";
import { useDispatch, useSelector } from "react-redux"; //
import { PLUS_ONE, MINUS_ONE } from "./redux/modules/counter";
function App() {
  const counter = useSelector((state) => {
    return state.counter;
  });

  const dispatch = useDispatch();
  return (
    <div className="App">
      <div>현재 카운트 : {counter.number}</div>
      <button
        onClick={() => {
          dispatch({
            type: PLUS_ONE,
          });
        }}
      >
        +
      </button>
      <button
        onClick={() => {
          dispatch({
            type: MINUS_ONE,
          });
        }}
      >
        -
      </button>
    </div>
  );
}

export default App;

Action Creator를 사용하면 좋은점

  1. 휴먼에러(오타) 방지
    액션 value를 상수로 사용하면 오타로 인한 에러방지
  2. 유지보수의 효율성 증가
    하나의 수정으로 여러 곳에 반영이 가능하므로 유지보수가 편리함
  3. 코드 가독성
    Action Creator를 통해 액션들을 목록화하여 코드의 가독성을 높임
  4. 리덕스 공식 문서 권장
    리덕스 공식 문서에서도 Action Creator 사용을 권장함

Payload

1) 이전에 뭘 했고 지금은 뭘 할 것인가?
+1과 -1버튼을 각각 누르면 증가되거나 감소하는 카운터를 만들었고
만약 사용자가 어떤 input에 5를 입력하고 버튼 누르면 5 증가하고 10을 입력하면 10이 증가하는 프로그램이 되도록 하는 것

2) Payload란 무엇인가?

  • 액션 객체에 함께 전달되는 데이터를 의미
  • 액션 객체에는 type 외에도 payload라는 속성을 추가하여 데이터를 담아 전달함.
  • Payload는 커뮤니티에서의 규칙이 정해져 있지 않으며, 주로 payload라는 이름을 사용함.
// payload가 추가된 액션객체

{type: "ADD_NUMBER", payload: 10}
// type뿐만 아니라 payload라는 key와 value를 같이 담는다.

payload 이용 해서 기능 구현하기

1) 사용자가 입력한 값을 받을 input 구현:

// src/App.js
import React from "react";
import { useState } from "react";

const App = () => {
  const [number, setNumber] = useState(0);

  const onChangeHandler = (event) => {
    const { value } = event.target;
    setNumber(+value);
  };

  console.log(number);

  return (
    <div>
      <input type="number" onChange={onChangeHandler} />
      <button>더하기</button>
      <button>빼기</button>
    </div>
  );
};

export default App;

☞ Input과 더하기/빼기 버튼을 구현하고, 입력값을 상태로 관리함.

2) Counter.js 모듈 작성: Action Creator:

// src/redux/modules/counter.js
const ADD_NUMBER = "ADD_NUMBER";

export const addNumber = (payload) => ({
  type: ADD_NUMBER,
  payload,
});

☞ Action Value와 Action Creator를 작성하고, payload를 받아와 액션 객체를 생성함.

3) Counter.js 모듈 작성 : Initial State, Reducer, 내보내기:

// src/redux/modules/counter.js
const initialState = {
  number: 0,
};

const counter = (state = initialState, action) => {
  switch (action.type) {
    case ADD_NUMBER:
      return {
        number: state.number + action.payload,
      };
    default:
      return state;
  }
};

export default counter;

☞ Initial State와 Reducer의 기본 형태를 작성하고, 더하기 기능의 로직을 추가함.

4) 구현된 기능 테스트

// src/App.js
import React from "react";
import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { addNumber } from "./redux/modules/counter";

const App = () => {
  const dispatch = useDispatch();
  const [number, setNumber] = useState(0);
  const globalNumber = useSelector((state) => state.counter.number);

  const onChangeHandler = (event) => {
    const { value } = event.target;
    setNumber(+value);
  };

  const onClickAddNumberHandler = () => {
    dispatch(addNumber(number));
  };

  return (
    <div>
      <div>{globalNumber}</div>
      <input type="number" onChange={onChangeHandler} />
      <button onClick={onClickAddNumberHandler}>더하기</button>
      <button>빼기</button>
    </div>
  );
};

export default App;

☞ useSelector로 Store의 값을 조회하여 화면에 렌더링하고, useDispatch를 사용하여 Action Creator를 호출하여 기능을 테스트함

Duck 패턴

  • 리덕스 모듈을 효율적으로 구성하기 위한 패턴
  • Reducer 함수와 Action Creator 함수들을 한 파일에 작성하고 내보내는 방식
  • Action Type은 app/reducer/ACTION_TYPE 형태로 작성하는 규칙을 따름
  • 현재 코드 작성하는 방식이 이미 Duck패턴을 따르고 있었음
  • Action Type, Action Creator, Reducer를 한 파일에 작성하여 구조를 간결하게 유지함.

react-router-dom

  • react-router-dom은 React 앱에서 다중 페이지 네비게이션을 구현하기 위한 패키지.
  • 단일 페이지 애플리케이션(SPA)에서 여러 페이지로 이동하는 기능을 제공

react-router-dom 설치

  1. 패키지 설치:
    yarn add react-router-dom 명령으로 패키지 설치.

2 . react-router-dom 사용하기
1. 페이지 컴포넌트 생성
2. Router.js 생성 및 route 설정 코드 작성
3. App.js에서 Router.js import 및 적용
4. 페이지 이동 테스트

Router.js 설정:

// src/shared/Router.js

import React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Home from '../pages/Home';
import About from '../pages/About';
import Contact from '../pages/Contact';
import Works from '../pages/Works';
import Layout from './Layout';

const Router = () => {
  return (
    <BrowserRouter>
      <Layout>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="about" element={<About />} />
          <Route path="contact" element={<Contact />} />
          <Route path="works" element={<Works />} />
        </Routes>
      </Layout>
    </BrowserRouter>
  );
};

export default Router;

☞ BrowserRouter, Route, Routes 등을 이용하여 페이지 간 이동을 설정

react-router-dom Hooks

  1. useNavigate:
// src/pages/Home.js

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

const Home = () => {
  const navigate = useNavigate();

  return (
    <button
      onClick={() => {
        navigate('/works');
      }}
    >
      Works 페이지로 이동
    </button>
  );
};

export default Home;

☞ 다른 페이지로 이동할 때 사용하는 Hook.
☞ 버튼 클릭 등의 이벤트 핸들러에서 useNavigate를 이용해 페이지 이동 구현

  1. useLocation:
// src/pages/Works.js

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

const Works = () => {
  const location = useLocation();

  return (
    <div>
      <div>{`현재 페이지: ${location.pathname.slice(1)}`}</div>
      <Link to="/contact">Contact 페이지로 이동</Link>
    </div>
  );
};

export default Works;

☞ 현재 페이지에 대한 정보를 얻을 때 사용하는 Hook.
☞ 현재 URL과 관련된 정보를 객체로 제공.

  1. Link:
// src/pages/Works.js

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

const Works = () => {
  const location = useLocation();

  return (
    <div>
      <div>{`현재 페이지: ${location.pathname.slice(1)}`}</div>
      <Link to="/contact">Contact 페이지로 이동</Link>
    </div>
  );
};

export default Works;

☞ a 태그의 역할을 하는 컴포넌트.
☞ 페이지 이동 시 새로고침을 방지하고, SPA의 장점을 유지.

Children 활용

  • 미리 예상할 수 없는 자식 엘리먼트를 사용할 때 활용.
  • Layout 컴포넌트를 통해 Header, Footer 등을 Composition하여 페이지 레이아웃 구현.

Dynamic Route

  • path에 유동적인 값을 넣어서 특정 페이지로 이동하게끔 구현하는 방법
  • 이로 인해 Works 페이지에 여러 작업이 있고 각 작업마다 독립적인 페이지를 가질 수 있음

Dynamic Route 설정

  • Router.js에서 Dynamic Routes를 설정
  • Works 페이지에 여러 작업이 있고, 각각의 작업을 클릭하면 해당 작업의 상세 페이지로 이동하도록 구현
  • Dynamic Routes는 path에 works/:id와 같이 설정하며, :id는 동적인 값을 받겠다는 의미임

페이지 이동 테스트

  • 페이지 이동을 테스트하여 확인함
  • home → works → works/1 → home → works/100 순서대로 이동하여 각각의 상세 페이지가 잘 작동하는지 확인함
  • useParams 훅을 사용하여 Dynamic Routes에서 path에 입력된 id 값을 가져옴

동기적 vs. 비동기적 프로그래밍

동기적 프로그래밍: 코드는 순차적으로 실행되며, 앞선 코드가 완료되어야 다음 코드가 실행됨.
비동기적 프로그래밍: 코드의 실행이 완료 여부와 관계없이 다음 코드로 즉시 넘어감. 대표적으로 setTimeout, 이벤트 처리, 서버 통신과 관련된 비동기 코드가 있음.

###콜백지옥 (Callback Hell)
콜백지옥이 발생 하면?
비동기 코드 중첩이 깊어져 들여쓰기가 많아지고, 코드가 가독성이 떨어지고 수정이 어려워짐

해결책: ES6에서 소개된 Promise 객체를 사용하여 비동기 코드를 더 효율적으로 관리.

  1. Promise 객체
    비동기 작업의 약속(약속에 따라 동작)을 표현하는 객체.

1) 상태:
대기 (pending): 작업이 아직 성공 또는 실패하지 않은 상태.
이행 (fulfilled): 작업이 성공적으로 완료된 상태.
거부 (rejected): 작업이 실패한 상태.

2) 핸들링 방법:
then ~ catch: Promise 객체의 상태에 따라 적절한 처리를 수행.
async / await: Promise 객체를 더 간결하게 다루는 방법으로, 비동기 함수 내에서 동작.

3) then ~ catch 활용:

axios.get('http://api.naver.com/weather/today')
  .then(response => {
    console.log('정상처리 되었습니다: ' + response);
  })
  .catch(error => {
    console.log('오류가 발생하였습니다: ' + error);
  })
  .finally(() => {
    console.log('항상 실행되는 부분입니다!');
  });

4) async / await 활용:

const getWeather = async () => {
  try {
    const response = await axios.get('http://api.naver.com/weather/today');
    console.log('정상처리 되었습니다: ' + response);
  } catch (error) {
    console.log('오류가 발생하였습니다: ' + error);
  }
}

REST API

  • Representational State Transfer(REST)는 CRUD 기능을 HTTP Method(GET, POST, PUT, DELETE)를 통해 수행하는 아키텍처
  • 자원은 URI로 표현되고 행위는 HTTP Method로 정의함
  1. 예시: GET /users/3/profile은 "user 중에서 ID가 3인 사용자의 프로필을 요청"하는 것으로 자원과 행위를 명확히 표현함

  2. REST API 규칙 및 예시
    1) 명명 규칙:

  • URI는 명사를 사용하고 소문자로 작성되며 복수형을 사용함
  • URI의 마지막에는 /를 포함하지 않음
  • 파일 확장자를 표시하지 않음

2) 예시
http://example.com/posts (O)
http://example.com/post (X)
http://example.com/post/assets/example (O)

RESTful한 API

RESTful한 API는 위의 규칙을 따라 명확하고 사용하기 쉬운 API를 의미함
장점은 이해하기 쉽고 일관된 디자인으로 다양한 클라이언트가 활용할 수 있음

RESTful하지 못한 상황

  • 모든 CRUD 기능을 POST 메서드로만 이용하는 경우
  • URI에 행위(method)에 대한 부분이 들어가는 경우(/classes/createPeople)

Path Variable vs Query Parameter

REST 개발 시, 데이터를 가져올 때 주로 사용되는 두 가지 방법

Path Variable

경로 자체에 변수를 사용하여 리소스를 특정하는 방법
예시: /users/10
특히, 전체 데이터 또는 특정 데이터를 식별할 때 사용

Query Parameter

URI에 변수를 추가하여 데이터를 정렬하거나 필터링하는 방법
예시: /users?user_id=10
주로 데이터를 정렬하거나 필터링할 때 사용

JSON

  • JavaScript Object Notation의 약자
  • 자바스크립트 객체 문법을 기반으로 하는 문자 기반의 데이터 교환 형식.
  1. JSON 구조
    일반적인 JSON 구조는 자바스크립트 객체 리터럴 작성법을 따르며, 문자열, 숫자, 불리언 등의 원시 자료형과 중첩된 계층 구조를 가질 수 있음.
{
  "squadName": "Super hero squad",
  "homeTown": "Metro City",
  "formed": 2016,
  "secretBase": "Super tower",
  "active": true,
  "members": [
    {
      "name": "Molecule Man",
      "age": 29,
      "secretIdentity": "Dan Jukes",
      "powers": [
        "Radiation resistance",
        "Turning tiny",
        "Radiation blast"
      ]
    },
  ]
}

JSON 메서드

  1. stringify(): 자바스크립트 객체를 JSON 문자열로 변환하여 네트워크를 통해 전송할 때 주로 사용함
console.log(JSON.stringify({ x: 5, y: 6 })); // "{"x":5,"y":6}"
  1. parse(): JSON 문자열을 자바스크립트 객체로 변환하여 프로그램 내부에서 사용할 때 활용함
const json = '{"result":true, "count":42}';
const obj = JSON.parse(json);
console.log(obj.count); // 42

JSONPlaceholder

  • 가짜 서버 또는 mock API server로, 아래 API를 통해 JSON 기반의 DB 통신을 시뮬레이션할 수 있음.
import logo from "./logo.svg";
import "./App.css";
import { useEffect, useState } from "react";

function App() {
  const [data, setData] = useState([]);
  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/posts")
      .then((response) => response.json())
      .then((json) => {
        setData([...json]);
        return console.log(json);
      });
  }, []);
  return (
    <div className="App">
      <h3>jsonplaceholder Data</h3>
      {data.map(function (item) {
        return (
          <div>
            <ul>
              <li>{item.userID}</li>
              <li>{item.id}</li>
              <li>{item.title}</li>
              <li>{item.body}</li>
            </ul>
          </div>
        );
      })}
    </div>
  );
}

export default App;

오늘의 한줄평 : 리덕스가 조금 어려웠다. 와 복습도 열심히 해야겠다...

profile
귀여운 걸 좋아하고 흥미가 있으면 불타오릅니다💙 최근엔 코딩이 흥미가 많아요🥰

0개의 댓글