리액트 스터디 3

41606 단어 React리액트React

SPA

SPA는 Single page Application의 약자로 서버에서 단 한장의 HTML을 제공하는 방식을 의미한다. SPA 이전의 웹사이트는 화면의 UI에 따라 여러장의 HTML을 가졌는데 사용성이 떨어지는 단점이 있었다.

  • 장점
    단순한 요청에서도 화면이 새로 그려지므로 데이터의 상태를 일관되게 유지하는데 많은 코드가 필요하다. 또한, 화면의 일부만 변경하는 경우도 전체를 다시 그려서 자원이 낭비된다. 동기 방식의 통신에서 페이지가 가지고 있던 데이터가 전부 사라진다. 이러한 경우를 사용성이 떨어진다고 표현하는데 SPA는 한장의 HTML안에서 컴포넌트를 조립하여 화면 전체에 대한 갱신을 방지하여 이러한 부분을 보완해준다.

  • 단점
    한번에 정적자원을 내려받기 때문에 대량의 컴포넌트 즉, 많은 화면을 표현해야 하는 웹사이트에 경우 첫 로딩이 느려질 수 있다. 따라서 웹 사이트의 성격에 맞게 SPA 채용 여부를 고려해야 한다.

router

리액트에서는 일반적으로 페이지의 이동을 위해 router를 사용한다. 대표적인 라이브러리 중 하나인 react-route-dom를 사용해보자.

yarn add react-route-dom

위와 같이 bash를 통해 설치한다.

다음은 공식 문서에서 제공하는 quik start 가이드의 코드이다. App.js에 작성하면 간단한 router 구현이 가능하다.

import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";

export default function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about">About</Link>
            </li>
            <li>
              <Link to="/users">Users</Link>
            </li>
          </ul>
        </nav>

        {/* A <Switch> looks through its children <Route>s and
            renders the first one that matches the current URL. */}
        <Switch>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/users">
            <Users />
          </Route>
          <Route path="/">
            <Home />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

function Home() {
  return <h2>Home</h2>;
}

function About() {
  return <h2>About</h2>;
}

function Users() {
  return <h2>Users</h2>;
}


위와 같이 생성된 화면을 통해 기존의 네비게이션을 대체하는 라우터를 이해할 수 있다.

parameter 전달

index.js<App />을 불러온 <BrowserRouter>로 감싸준다.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
);

reportWebVitals();
	import { Route, Link } from 'react-router-dom';
	<Route exact path="/" component={Home} />
        <Route path="/user/:user_name" component={User} />
        <Route path="/about" component={About} />

페이지 이동 뿐만 아니라 값도 전달할 수 있다. 첫 줄의 /에 할당된 exact 옵션은 path가 정확히 일치할때만 컴포넌트를 불러오는 옵션으로 /user/about/가 포함된 path를 가진 화면을 호출 되었을 때 /의 반응을 방지한다.

localhost:3000/user/johndoe로 호출 한다고 가정했을 때 :user_name의 자리에 johndoe 가 전달된다.

return (
        <div>
            my name is {props.match.params.user_name}
        </div>
    )

User.js 에서는 위와 같이 꺼내서 사용할 수 있다.

history

브라우저가 제공하는 history를 사용할 수도 있다. 뒤로 가기 역시 간단하게 구현 할 수 있다.

<button onClick={() => {
          this.props.history.push('/user/johndoe');
        }}>user</button>
<button onClick={() => {
          this.props.history.goBack();
        }}>back</button>

404

Not Found 일명 404를 간단하게 처리 할 수 있다. NotFound.js를 만들었다고 가정하고 react-router-dom에서 Switch를 가져온다.

import { Route, Switch } from "react-router-dom";

그리고 다른 <Route>들을 <Switch>로 감싸고 path가 없는 Route를 추가하면 path가 정의 되지 않은 주소를 요청했을 때 지정된 컴포넌트인 NotFound.js로 이동된다.

<Switch>
  <Route component={NotFound} />
</Switch>

전술한 history를 이용해서 뒤로 가기 역시 구현할 수도 있다.

<Route render={(props) => (<NotFound history={props.history} />)} />

Redux

데이터의 전역적인 상태 관리를 위해, stateprops를 보완하기 위해 redux(공식 문서)를 사용해보자.

yarn add redux react-redux

스탠드 얼론인redux와 리액트에서 리덕스를 쓰기 위한 react-redux를 함께 설치하자.

  • State : 딕셔너리 형태로 데이터를 보관한다.
{[key]:value}
  • Action : 데이터에 변경이 발생할 때 발생하는 객체로 다음과 같은 형태이다. 스토어 내부의 데이터는 일관성과 불변성을 유지하기 위해 오직 액션을 통해서만 변경하는 것이 원칙이다.
{type: 'CHANGE_STATE', data: {...}}
  • ActionCreator : 액션 생성 함수는 액션을 생성하여 반환해준다.
const changeState = (new_data) => {
  return {
    type: 'CHANGE_STATE',
    data: new_data
  }
}
  • reducer : 리덕스에 저장된 데이터를 변경하는 함수이며 흐름은 다음과 같다.
    액션 생성 함수 호출액션 생성리듀서(데이터, 액션 객체)새로운 데이터 반환 리듀서는 순수 함수로 인수부를 제외하고 외부와의 의존관계를 맺지 않는다. 외부의 상태와 상관없이 항상 같은 동작을 보장해야 한다.

  • store : 리덕스를 적용하기 위해 사용하는 저장소 개념으로 리듀서, 상태, 액션 호출 등을 위한 내장 함수가 포함되어 있다. 단일 스토어 규칙에 따라 하나의 스토어만 사용한다.

  • dispatch : 액션 생성 함수를 호출하는 함수이다.

ducks

일반적으로 리덕스의 파일 구조는 확장성을 위해 action, action creator reducer끼리 역할을 기준으로 모아 작성한다. 반면 ducks 구조는 기능을 기준으로 그 기능 해당하는 action, action creator reducer를 한 파일안에 작성하는 방식이다. 작은 규모의 프로젝트나 학습을 위해 이러한 구조를 많이 사용하는 편이고 본 포스트의 목적에 부합한다.

src 폴더 하위에 redux/modules 폴더를 추가하고 다음과 같이 src/redux/configStore.jssrc/redux/modules/bucket.js를 만든다.

ducks 구조에 따라 bucket.jsActions, Action Creators, Reducer를 작성한다.

// bucket.js

// Actions
const LOAD = "bucket/LOAD";
const CREATE = "bucket/CREATE";

const initialState = {
    list: ['영화관 가기', '매일 책읽기', '수영 배우기'],
};

// Action Creators
export const loadBucket = (bucket) => {
    return { type: LOAD, bucket };
}

export const createBucket = (bucket) => {
    return { type: CREATE, bucket };
}

// Reducer
export default function reducer(state = initialState, action = {}) {
    switch (action.type) {
        // do reducer stuff
        case "bucket/LOAD": {
            return state;
        }
        case "bucket/CREATE": {
            const new_bucket_list = [...state.list, action.bucket];
            return { list: new_bucket_list };
        }
        default: return state;
    }
}

store 역시 작성해준다.

import { createStore, combineReducers } from "redux";
import bucket from "./modules/bucket";
import { createBrowserHistory } from "history";

export const history = createBrowserHistory();

const rootReducer = combineReducers({ bucket });

const store = createStore(rootReducer);

export default store;

index.jsProvider를 추가하고 라우터를 감싸준 뒤 store를 연결해준다.

import { Provider } from "react-redux";
import store from "./redux/configStore";

ReactDOM.render(
  <Provider store={store}>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </Provider>,
  document.getElementById('root')
);

connect

이제 스토어와 어플리케이션을 연결해보자. App.js에서 connectbucket을 불러오고 mapStateToProps, mapDispachToProps를 만든다.

import { connect } from "react-redux";
import { loadBucket, createBucket } from "./redux/modules/bucket";

const mapStateToProps = (state) => {
  return { bucket_list: state.bucket.list };
}

const mapDispachToProps = (dispatch) => {
  return {
    load: () => {
      dispatch(loadBucket());
    },
    create: (bucket) => {
      dispatch(createBucket(bucket));
    }
  };
}

export 부분을 수정해서 connect로 감싸준다.

export default connect(mapStateToProps, mapDispachToProps)(withRouter(App));

이제 componentDidMount에서 bucket_list를 확인해 볼 수 있다.

componentDidMount() {
    console.log(this.props);
  }


이제 기존의 setState를 주석하고 create를 사용해보자.

 this.props.create(new_item);
 //this.setState({ list: [...list, new_item] });

useSeletor을 이용해서 Hook도 사용 할 수 있다.

import { useSelector, useDispatch } from "react-redux";
const bucket_list = useSelector(state => state.bucket.list);

좋은 웹페이지 즐겨찾기