[새싹] 현대IT&E 231124 기록 - React

최정윤·2023년 11월 24일
0

새싹

목록 보기
29/67

리액트 장점

  • 컴포넌트를 활용한 코드 재활용성

  • 가상돔을 이용한 최적화 가능

  • 안드로이드 + 웹 동시에 하고 싶으면 하이브리드로 진행.


✅ React Router DOM

기본 컴포넌트

Path Pattern

Star Segment

  • "*" 를 이용하여 지정한다. 지정된 하위 모든 경로 패턴을 매칭시킨다.
path="/article/*"
/article
/article/1/2/3
/article/list/123

Optional Segment

  • 모든 세그먼트는 필수이나, 생략 가능한 세그먼트를 지정한다.
path="/:articleId?/list"
/1/list
/2/list
/list

React URL 파라미터(URL Parameter, Dynamic Segment)

  • URL을 이용하여 부가 정보를 보내는 방법이다.
  • ":" 로 시작되며 파라미터(parameter)명을 지정한다.
path="/board/list/:boardId"
path="/board/:boardCode/list/:boardId"
path="/:articleId"

Hooks

  • 리액트 라우터는 상태 공유를 위해 다양한 Hooks를 제공한다. Hooks에 대한 자세한 내용은 다음에 자세히 알아본다.

useNavigate

  • v6 이전에 사용되었던 useHistory를 대치한다.
  • Navigate 컴포넌트와 기능과 사용법은 동일하다.

useLocation

  • 현재 URL정보를 가지고 있는 location 객체를 반환한다.
  • QueryString 데이터 처리를 위해 useSearchParams와 같이 사용되는 경우가 많다.
  • 대표적인 속성은 다음과 같다.

pathname: QueryString을 제외한 현재 URL 경로를 받는다.
search: QueryString으로 전달된 파라미터를 처리한다.
state: Link 나 navigate가 전달한 state 값을 받는다.

useParams

  • React URL 파라미터로 넘어오는 값을 반환한다.
import { useParams } from 'react-router-dom'

useSearchParams

  • QueryString으로 넘어온 데이터에 대한 처리를 한다.
  • useState와 유사하게 배열 형태로 상태값과 변경 함수를 반환한다.
import { useSearchParams } from 'react-router-dom';
  • 주요 메서드

    get(key): key에 해당하는 값을 1개 반환한다. key가 같은 경우 처음 1개만 반환된다.
    getAll(key): key에 해당하는 값을 모두 반환한다.
    toString(): QueryString을 반환한다.
    set(key, value): key 값을 value로 수정한다.
    append(key, value): key를 이용하여 값을 추가한다.
    delete(key): key에 해당하는 값을 삭제한다.

useRoutes

  • Routes 컴포넌트가 아닌 JavaScript 객체로 Routes를 생성할 때 이용한다. 기존에는 reactrouter-config를 이용하였다.
  • 외부에 JavaScript으로 라우트를 구성하고, 불러 들여서 사용할 수 있다.

✅ Styled Components

스타일드 컴포넌트(Styled Components)

  • CSS 정의를 JavaScript으로 기술된 컴포넌트에 적용하는 방법을 CSS-in-JS라고한다.
  • CSS-in-JS는 JavaScript에서 직접 사용 가능하기 때문에 동적인 환경에도 쉽게 대응할 수 있다.
  • CSS-in-JS 중 인기가 있는 라이브러리는 중 하나는 Styled Components 이다.
  • Styled Components을 이용하면 다음과 같은 특징이 있다.
    • 리액트 컴포넌트 별로 재사용 가능한 자체 스타일을 만들 수 있다.
    • 스타일 속성을 자바스크립트 스타일이 아닌 css 스타일로 적용할 수 있다.
    • 내부적으로 class가 자동으로 생성되어 적용되므로 스타일명으로 간단히 관리할 수 있다.
  • 리액트 컴포넌트에 CSS를 적용한 뒤 리액트 요소를 반환한다.
  • CSS의 class 보다 Tag 위주로 구현한다.
  • 스타일드 컴포넌트를 사용 전에는 다음과 같이 CSS 적용한다.

설치 및 설정

create-react-app react-styled-components
  • 리액트의 기본 라이브러리가 아닌 스타일드 컴포넌트는 사용하기 위해서 추가적인 패키지 설치가 필요하다.
npm install styled-components
  • 스타일드 컴포넌트를 사용하기 전에 참조가 필요하다.
import styled from 'styled-components';
  • 스타일드 컴포넌트는 템플릿 문자열(Template literals)을 기반으로 한 태그드 템플릿 문자열(Tagged Template Literals)을 이용한다.
    • 백틱( )을 이용하여 생성하고 내부에서 줄 바꾸기를 허용한다.
    • ${ ~ } 를 이용한 표현식(Expression)을 삽입할 수 있다.
    • 표현식 내부에 조건(삼항)연산자를 사용할 수 있고, 중첩 가능하다.
    • 표현식 내에서 자바스크립트에서 사용 가능한 거의 대부분의 자료형을 사용할 수 있다.

코드

import logo from './logo.svg';
import './App.css';
import styled from 'styled-components';
const SimpleButton = styled.button`
  color: white;
  background-color: green;
`;
const LargeButton = styled(SimpleButton)`
  font-size: 50px;
`;
const ReactButton = props => {
  console.log('props', props);
  return <button className={props.className}>{props.children}</button>
}
const ReactLargeButton = styled(ReactButton)`
  font-size: 50px;
`;
const PrimaryButton = styled.button`
  color: ${ props => props.primary ? 'white' : 'black'};
  background-color: ${ props => props.primary ? 'blue' : 'gray'};
`;

function App() {
  return (
    <div>
      <SimpleButton>Simple</SimpleButton>
      <LargeButton>Large</LargeButton>
      <ReactButton>React</ReactButton>
      <ReactLargeButton>React Large</ReactLargeButton>
      <PrimaryButton>Normal</PrimaryButton>
      <PrimaryButton primary>Primary</PrimaryButton>
    </div>
  );
}

export default App;

✅ React Context API

리액트 컨텍스트(React Context)

  • 부모 자식을 기준으로 상태를 전달하는 props는 다양한 컴포넌트들 전체에 상태를 전달하기에는 한계가 있다. 컨텍스트 API는 컴포넌트 전체에서 데이터를 전달할 수 있다.
  • props는 부모와 자식의 단계가 깊어 질수록 전달의 경로 관리가 복잡해 지는데 이를 Props Drilling이라고 한다. 이런 경우 props에 의존성이 생겨 수정할 때도 연결된 모든 props을 수정해야 하고 중간에 있는 컴포넌트를 수정해도 많은 부분을 수정해야 한다.
  • 리액트는 Context 사용을 위해 Context API를 제공한다. Context API는 createContext, Provider, Consumer등의 기능을 제공한다.
  • Content를 제공하는 제공자(Provider)와 구독하는 구독자(Consumer)구조를 제공한다.

Context API

  • React.createContext
    • Context 객체를 생성하여 반환한다. 필요할 경우 하나 이상 생성할 수 있다.
    import { createContext } from 'react';
    const DataContext = createContext(defaultValue);
    • defaultValue는 컴포넌트가 상위 경로에서 Context를 제공하는 Provider 찾지 못하는 경우 사용된다. defaultValue는 한번 설정되면 변경되지 않는다.
    • context 객체의 데이터는 스스로 변경을 할 수 없다.
  • Provider Component
    • 지정된 Context의 value props 값을 하위 자손 컴포넌트에게 전달하는 구독 서비스를 제공하고, 자손 컴포넌트의 수는 제한이 없다.
    • context의 변화가 있으면 구독 컴포넌트에게 전파를 한다.
    • Context별 Provider 중첩을 허용하고, 동일 Provider가 중첩된 경우 consumer 컴포넌트에서 가장 가까운 Provider value 값이 우선한다.
    • Provider 의 value props 값이 변경될 때마다 구독 중인 컴포넌트들은 re-render된다. 따라서 잦은 value 값 변경은 빈번한 re-render를 발생시킨다.
  • Consumer Component
    • context 변화를 구독하는 컴포넌트이다.
    • Consumer 컴포넌트는 내에 content는 함수를 이용하여 구현한다.
    • 최근에는 useContext hook를 이용하여 구독을 한다.

코드

import React, { createContext, useContext } from 'react';
import './style.css';
const themeDefault = {border: '10px solid red'};
const themeContext = createContext(themeDefault);

function App() {
  const theme = useContext(themeContext);
  console.log('theme', theme);
  return (
    <themeContext.Provider value={{border: '10px solid blue'}}>
      <div className="root" style={theme}>
        <h1>Hello World!</h1>
        <Sub1/>
      </div>
    </themeContext.Provider>
  );
}

function Sub1() {
  const theme = useContext(themeContext);
  return (
    <themeContext.Provider value={{border: '10px solid green'}}>
      <div style={theme}>
        <h1>Sub1</h1>
        <Sub2/>
      </div>
    </themeContext.Provider>
  );
}

function Sub2() {
  const theme = useContext(themeContext);
  return (
    <div style={theme}>
      <h1>Sub2</h1>
      <Sub3/>
    </div>
  );
}

function Sub3() {
  const theme = useContext(themeContext);
  return (
    <div style={theme}>
      <h1>Sub3</h1>
    </div>
  );
}

export default App;

✅ useReducer

  • 리액트가 제공하는 기본 Hook 중의 하나이다.
  • useState와 비슷하게 상태관리에 이용된다. 하지만 useState 처럼 setXXX()에 Callback 함수를 전달하는 방법을 사용하지 않는다.
  • useReducer는 Dispatch를 이용해 처리방법이 기술된 Reducer를 호출하면서 상태를 전달하여 실행하는 대리자 구조로 운영된다.
  • 컴포넌트에서 상태 처리에 관련된 코드를 Reducer를 이용하여 분리할 수 있고, 컴포넌트에서는 상태 값만 전달하면 되어 구조가 단순해 지고, 재활용성이 높아진다.
  • 단순한 구조의 state 관리를 할 경우 useState 이용을 권장한다. 복잡한 구조의 state를 이용하는 경우에는 reducer함수를 이용할 수 있는 useReducer 사용을 권장한다.

초기화 및 생성

  • reducer
    • 순수 함수로 state와 action을 매개변수로 한다. 매개변수로 넘어온 state를 변경한 뒤 반환한다.
    • state 와 action을 매개변수로 한다.
  • initialArg
    • 초기 상태이다. 만약 init이 지정되지 않으면 initialArg로 초기화 된다.
    • 초기화를 하는 함수를 지정할 수 있고 선택 사항이다. initalArg를 매개변수로 사용한다. init 함수가 지정되면 init(initialArg)의 결과로 초기화된다.
  • state
    • 현재 상태이다. init(initialArg) 또는 initialArg에 의해 결정된다.
  • dispatch
    • 실행할 정보를 가지고 있는 action과 state를 reducer에 전달한다.

✅ useEffect

  • 리액트가 제공하는 기본 Hook 중 하나이다.
  • 함수형 컴포넌트의 라이프 사이클(Life Cycle)별 트리거(Trigger)로 동작하여 작업을 Side Effect를 이용하여 수행할 수 있게 한다.
  • Side Effect
    • 부수효과, 부작용이라고 한다. 함수지향형의 순수 함수의 정의와 관련된 현상이다.
    • 리액트에서는 컴포넌트와 외부 사이에 일어나는 비동기 적인 작업을 의미한다.
    • 주로 서버 API통신이나 리액트 컴포넌트 DOM 수동 수정 또는 브라우저 API등을 이용할 때 필요하다.
    • 데이터 로딩과 렌더링을 순차적으로 진행하면 데이터 로딩 시간 때문에 렌더링이 지연되는 문제가 있다. 렌더링을 먼저하고 비동기로 데이터가 필요할 때 하면 효율적으로 실행할 수 있다.

선언 및 생성

  • 다음과 같이 참조 후 생성 라이프 사이클에 따라 생성한다.
import React, { useEffect } from 'react';
  • 설정
    useEffect(callback[, dependencies]);
    • callback
      • 컴포넌트 렌더링이 끝난 뒤 호출되는 callback 함수이다. 필수이다.
    • dependencies
      • 종속성 배열(dependency array)이라고 한다. useEffect에서 관리되는 모든 상태를 등록한다.
  • 라이프 사이클
    • useEffect의 dependencies 사용 여부와 반환(return) 여부에 따라 함수 컴포넌트의 생명주기에 맞추어 실행이 가능하다.
    • Mount
      • 처음 컴포넌트 렌더링이 완료된 후 실행된다.
      • 콜백함수와 초기값이나 빈 dependencies가 설정되면 실행한다
      • Class Component의 ComponentDidMount에 해당한다.
      useEffect(() => { console.log('mount'); }, []);
    • Update
      • dependencies가 변경되면 실행된다.
      • Class Component의 ComponentDidMount, ComponentDidUpdate에 해당한다.
    • unmount
      • dependencies가 비어 있고, 컴포넌트가 사라지면 Cleanup 함수를 호출한다.
      • Cleanup함수는 unmount, update 전에 실행하는 작업을 지정한다. 계속되는 이벤트 호출이 발생되는 경우 이벤트 해지를 위해서도 사용된다.
      • Class Component의 ComponentWillUnmount에 해당한다.
      useEffect(() => {
      return () => { console.log('unmount'); }
      }, []);
    • dependencies 값이 없을 경우
      • 리 렌더링마다 실행된다.
      useEffect(() => { console.log('rerendering'); });

코드

import logo from './logo.svg';
import './App.css';
import React, { useReducer, useState } from 'react';

function App() {
  function countReducer(oldCount, action) {
    if (action.type === 'UP') {
      return oldCount + action.number;
    } else if (action.type === 'DOWN') {
      return oldCount - action.number;
    } else if (action.type === 'RESET') {
      return 0;
    }
  }
  const [number, setNumber] = useState(10);
  const [count, countDispatch] = useReducer(countReducer, 0);
  function down() {
    countDispatch({ type: 'DOWN', number: number });
  }
  function reset() {
    countDispatch({ type: 'RESET', number: number });
  }
  function up() {
    countDispatch({ type: 'UP', number: number });
  }
  function changeNumber(event) {
    setNumber(Number(event.target.value));
  }
  return (
    <div className="App">
      <input type="button" value="-" onClick={down} />
      <input type="button" value="0" onClick={reset} />
      <input type="button" value="+" onClick={up} />
      <input type="text" value={number} onChange={changeNumber} />
      <span>{count}</span>
    </div>
  );
}

export default App;

✅ React-Redux

리덕스(Redux)

  • Flux 디자인 패턴을 응용한 App 전역 상태(State)관리 자바스크립트 라이브러리이다. 하지만 리덕스에서는 Flux의 Dispatcher 내부적으로 이용한다.
  • Context API가 제공되기 이전부터 사용되었다.
  • 다양한 리듀서(Reducer)가 업데이트를 진행하기 전에 추가 작업을 할 수 있는 리덕스 미들웨어(Redux Middleware)를 이용할 수 있다.
  • 리덕스는 다음 3가치 원칙을 기초로 한다.
    • Single source of truth
      • App단위의 전역 상태는 하나의 Store에 하나의 Object Tree로 저장된다.
      • Redux Store라고 하며 단일 저장소는 상태 값 관리 및 변화 추적 등 디버깅에 유리하다.
    • State is read-only
      • 상태 갱신(Update) 정보를 가지고 있는 Action 객체를 통해서만 State를 변경할 수 있다.
      • 복잡한 상태 갱신 발생 상황을 제한하여 단순화시키면 상태 변화 추적 및 관리에 유리하다.
    • Changes are made with pure functions
      • Action 정보를 이용해서 State Tree를 갱신하는 작업은 순수 함수(Pure Function)인 Reducer를 생성해서 처리해야 한다.
      • 동일한 이전 상태와 Action을 받으면 항상 동일한 새로운 상태 객체를 생성해서 반환하여 결과를 예측 가능하게 한다.

Fulx Pattern

  • MVC 패턴의 문제점인 데이터가 Controller 집중하여 관리가 힘들다는 문제에 대안으로 제시된 디자인 패턴이다.
  • 데이터를 한 방향으로 전달되게 하여 복잡성을 줄이는 디자인 패턴이다.
  • useReducer의 상태관리의 기본 패턴이다.

설치

  • 리액트의 기본 라이브러리가 아닌 리덕스를 사용하기 위해서 redux, react-redux 패키지 설치가 필요하다.
npm install redux react-redux

리덕스 상태 흐름

  • 리덕스를 구성하는 구성 요소 사이의 흐름은 다음과 같다.

구성 요소

스토어(Store)

  • App 전체에 하나만 만들 수 있는 상태 트리(Sate Tree)로 Redux 상태를 관리하는 저장 공간이다.
  • 기본적으로 루트 상태 트리에 대응하는 루트 리듀서(Root Reducer)를 제공한다.
    • 관리할 상태가 복잡해지면 하위 상태 트리(domain State Tree)를 추가하고 대응하는 리듀서를 생성한 뒤 combineReducers Helper 함수를 이용하여 하나로 만들어 Store에 전달한다.
  • Action에 의해서만 변경될 수 있는 자바스크립트 객체이다.
  • 기본 내장 함수
  • createStore(reducer[, preloadedState][, enhancer])
    • Redux Store를 생성하여 반환한다. 공식적으로는 Redux Toolkit 의 configureStore사용을 권장하고 있다.
    • reducer : 리듀서를 지정한다.
    • preloadedState : 초기 상태를 지정한다. 이전 상태로 초기화할 때 이용된다.
    • enhancer : 미들웨어 등 Store에 외부 확장 기능을 추가할 경우 지정한다. Redux 기반은 applyMiddleware()가 있다.
  • getSate()
    • 현재 상태를 트리로 반환한다.
  • dispatch(action)
    • 리듀서에게 현재 상태(getSate())와 액션을 전달하여 생태변경을 한다.
  • subscribe(listener)
  • 상태 트리가 변경될 때 실행할 작업을 listener를 생성하여 전달하는 순수 함수이다.
  • listener에서 dispatch를 잘못할 사용할 경우 무한 루프에 빠질 수 있어 조심해야 한다.
  • 직접 사용보다는 useSelect Hook등 다른 방법을 사용하는 것을 권장한다.

액션

  • 순수 자바스크립트 객체로 이전 상태를 갱신하기 위한 상태 정보를 가지고 있다.
  • Reducer 실행할 기능 식별자를 전달하는 type과 처리할 데이터인 data로 구성된다.
  • Action를 생성하는 자바스크립트 함수를 액션 생성자(Action Creator)라고 한다.

리듀서(Reducer)

  • 리듀서로 전달된 현재 State와 Action을 기반으로 새로운 State를 생성하여 반환한다.
  • 새로운 상태 객체를 생성하면 변경 이력(History) 생성과 이전 State의 불변성(Immutable)을 유지하는 불변적 갱신(Immutable Update)을 할 수 있다.
  • combineReducers()
    • createStore는 한 개의 Reducer 만 지정할 수 있다.
    • 관리적인 목적으로 Reducer를 분류하여 다수 개를 분리 생성()한 뒤 하나로 통합해 주는 helper이다.

기타 기능

Provider

  • Redux Store에 있는 상태 값에 리액트의 Context API처럼 바로 접근할 수 있게 하는 컴포넌트이다.

useSelector

  • Redux Store의 자식 노드를 선택할 때 이용하는 Hook이다.
  • SelectorFunction
    • 순수 함수로 루트 상태를 매개변수로 선택 조건을 지정하는 함수이다.
  • options
    • 선택사항으로 EqualityFn은 동등성 비교(Equality Comparisons)에 사용되는 함수이다.
    • EqualityFn의 경우 두 값을 비교하여 false가 나오면 리렌더링을 한다. 기본값은 Type까지 검사하는 엄격한 동등성(Strict equality) 검사를 수행한다.

useDispatch

  • Redux Store에서 dispatch 함수의 참고할 때 이용하는 Hook이다. 리듀서의 액션을 key를 이용하여 실행한다.
  • options
    • 선택사항으로 EqualityFn은 동등성 비교(Equality Comparisons)에 사용되는 함수이다.
    • EqualityFn의 경우 두 값을 비교하여 false가 나오면 리렌더링을 한다. 기본값은 Type까지 검사하는 엄격한 동등성(Strict equality) 검사를 수행한다.

applyMiddleware(...middleware)

  • action이 Reducer로 보내지기 전에 실행할 작업이 있을 경우 사용된다. 잘못 지정하는 경우 실행속도가 느려지는 경우가 생길 수 있다.
  • 고차함수로 middleware(store(next(action))) 로 표현된다.
  • 로그에 사용하는 redux-logger, 비동기 처리하는 Redux-thunk, 동기식 처리를 하는 Reduxpromise-middleware 등이 있다.

코드

import React, {useState} from "react";
import './App.css';
import {createStore} from 'redux';
import {Provider, useSelector, useDispatch, connect} from 'react-redux';

const store = createStore();
function App() {
  const [number, setNumber] = useState(1);
  return (
    <div id="container">
      <h1>Root : {number}</h1>
      <div id="grid">
        <Provider store={store}>
          {/* <Left1 number={number}></Left1>
          <Right1
          onIncrease={() => {
            setNumber(number + 1);
          }}></Right1> */}
          <Left1></Left1>
          <Right1></Right1>
        </Provider>
      </div>
    </div>
  );
}

function Left1(props) {
  return (
    <div>
      {/* <h1>Left1 : {props.number}</h1>
      <Left2 number={props.number}></Left2> */}
      <h1>Left1</h1>
      <Left2></Left2>
    </div>
  )
}

function Left2(props) {
  return (
    <div>
      {/* <h1>Left2 : {props.number}</h1>
      <Left3 number={props.number}></Left3> */}
      <h1>Left2</h1>
      <Left3></Left3>
    </div>
  )
}

function Left3(props) {
  console.log('3');
  const number = useSelector((state) => state.number);
  return (
    <div>
      {/* <h1>Left3: {props.number}</h1> */}
      <h1>Left3 : {number}</h1>
    </div>
  )
}

function Right1(props) {
  return (
    <div>
      {/* <h1>Right1</h1>
      <Right2
      onIncrease={() => {
        props.inIncrease();
      }}></Right2> */}
      <h1>Right1</h1>
      <Right2></Right2>
    </div>
  )
}

function Right2(props) {
  return (
    <div>
      <h1>Right2</h1>
      {/* <Right3
      onIncrease={() => {
        props.onIncrease();
        }}></Right3> */}
        <Right3></Right3>
    </div>
  )
}

function Right3(props) {
  const dispatch = useDispatch();
  return (
    <div>
      <h1>Right3</h1>
      {/* <input type="button" value="+" onClick={() =>{
        props.onIncrease();
      }}></input> */}
      <input type="button" value="+" onClick={() => {
        dispatch({ type: 'PLUS' });
      }}></input>
    </div>
  )
}

export default App;

과제하기

문제

1. 계산기 프로젝트

  • 두 값을 입력받아 사칙연산 (+, -, *, /)하는 계산기를 제작하시오.
  • 숫자만 입력 받아 계산한다.
  • 결과, 초기화 기능이 있다.
  • 정수 연산만 가능하다.
  • 연산 결과는 결과 항목으로 출력한다.
  • Redux Toolkit을 이용한다.

2. 제출방법

  • hihelloastro@naver.com
  • "역할.txt"를 생성하여 성명과 역할 기입
  • node_modules를 제외한 나머지를 압축하여 제출
  • 파일명 7조.zip

풀이

npm install @reduxjs/toolkit react-redux
npm install --save-dev @babel/plugin-proposal-private-property-in-object
profile
개발 기록장

0개의 댓글