React 프로젝트 설치 및 설정 문제 해결

주의사항 (오류)

npm install -g cnpm --registry=https://registry.npm.taobao.org
'react-scripts'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다.

    // 다른 컴포넌트를 설치한 후 npm start 명령을 다시 실행할 때 위 오류가 발생할 수 있습니다.
    ## 해결 방법 1:
    npm install react-scripts
    설치 완료 후 npm start를 다시 실행합니다.
    ## 해결 방법 2:
    node_modules 폴더 삭제
    npm install
    설치 완료 후 npm start를 다시 실행합니다.

설치 명령어 약어

==> i = install (설치)
> -g = global (전역)

> -S = --save (개발 환경에 배포)

> -D = --save-dev (프로덕션 환경에 배포)

세 가지 라이브러리 파일

react.min.js - React의 핵심 라이브러리
react-dom.min.js - DOM 관련 기능 제공
babel.min.js - Babel은 ES6 코드를 ES5 코드로 변환하여, 아직 ES6를 지원하지 않는 브라우저에서도 React 코드를 실행할 수 있게 합니다. Babel은 JSX를 지원합니다. Babel과 babel-sublime 패키지를 함께 사용하면 소스 코드의 구문 강조를 한 차원 높일 수 있습니다.

공개 저장소 설치 (Redux)

npm i redux -S

store 생성 -> index.js, reducer.js, actionCreators.js, actionTypes.js

// index.js (디버깅 도구 > REDUX_DEVTOOLS 설치)

import { createStore } from 'redux';
import reducer from './reducer';

const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export default store;

// actionTypes.js  
export const CHANGE_INPUT_VALUE = 'CHANGE_INPUT_VALUE';

// actionCreators.js      
import { CHANGE_INPUT_VALUE } from './actionTypes';
export const getInputChangeAction = (value) => ({
    type: CHANGE_INPUT_VALUE,
    value
});

//  reducer.js
import { CHANGE_INPUT_VALUE } from './actionTypes';
const defaultState = {
    inputValue: '222',
    list: ['ewewee', 'ewewe']
};
export default (state = defaultState, action) => {
    if (action.type === CHANGE_INPUT_VALUE) {
        const newState = JSON.parse(JSON.stringify(state));
        newState.inputValue = action.value;
        return newState;
    }
    return state;
}


// 컴포넌트에서 호출

import store from './store/index'
import { getInputChangeAction } from './store/actionCreators';

constructor(props) {
    super(props);
    console.log(store.getState())
    this.state = { }
    this.handleStoreChange = this.handleStoreChange.bind(this)
    store.subscribe(this.handleStoreChange)
}

handleStoreChange() {
    console.log(store.getState())
}

// 데이터 가져오기
store.getState()

// 데이터 추가

handleInputValue(event) {
  const action = getInputChangeAction(event.target.value);
  store.dispatch(action);
};

캐싱

데이터 캐시에 저장
localStorage.setItem('user', values.username);

캐시에서 데이터 가져오기
localStorage.getItem('user');

플러그인

npm i react-redux -S

미들웨어

npm i redux-thunk -S

// index.js (디버깅 도구 > REDUX_DEVTOOLS 설치)
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducer';

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const enhancer = composeEnhancers(applyMiddleware(thunk));
const store = createStore(reducer, enhancer);
export default store;

// actionCreators.js
import { CHANGE_INPUT_VALUE } from './actionTypes';
import axios from 'axios';  

export const getInputChangeAction = (value) => ({
    type: CHANGE_INPUT_VALUE,
    value
});

export const getList = () => {
    return (dispatch) => {
        axios.get('/api/data', { params: { page: 1, limit: 10 } }).then((res) => {
            const data = res.data.list;
            const action = getInputChangeAction(data);
            dispatch(action);
        });
    };
};

// 컴포넌트에서 호출
import store from './store/index';
import { getList } from './store/actionCreators';

fetchData() {
    const action = getList();
    store.dispatch(action);
}

npm i redux-saga -S

라우터 설치 (react-router-dom)

npm i react-router-dom -S

// index.js (엔트리 파일)
import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import Index from './view/common/index';
import { BrowserRouter } from 'react-router-dom';

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

// router/index.js
import React from 'react';
import { Switch, Route, Redirect } from 'react-router-dom';
import Index from '../view/index/index';

export default class Router extends React.Component {
    render() {
        return (
            <Switch>
                <Route path="/" exact render={() => <Redirect to="/index" />} />
                <Route path="/index" component={Index} />
            </Switch>
        );
    }
}

프로그래매틱 내비게이션:
this.props.history.push('/download');

외부 링크로 이동:
window.location.href = 'https://your-url';

데이터 요청 (axios)

npm i axios -S

package.json 설정:
"proxy": "http://api.example.com"

import React from 'react';
import { Table } from 'antd';
import axios from 'axios';

export default class UserList extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            data: []
        };
    }

    componentDidMount() {
        this.fetchData();
    }

    fetchData() {
        axios.get('/backend/account/index', { params: { page: 1, limit: 10, token: 'your-token' } })
            .then((res) => {
                this.setState({ data: res.data.data.list });
            })
            .catch((error) => {
                console.error(error);
            });
    }

    render() {
        return (
            <Table dataSource={this.state.data} rowKey="id" />
        );
    }
}

Ant Design UI 설치

npm i antd -S

공식 문서: https://ant.design/docs/react/use-with-create-react-app-cn

개발 환경 설정

Ant Design에 맞게 설정 변경 (공식 문서 참고)
npm i react-app-rewired -D
npm i babel-plugin-import -D  // 코드 스플리팅 (필요한 모듈만 로드)

기본 문법

// 최상위 태그 대체 플레이스홀더
<Fragment>

// 생성자
constructor(props) {
    super(props);
    this.state = { // 컴포넌트 상태
    };
}

// 전개 연산자를 사용한 상태 업데이트
this.setState({
    list: [...this.state.list, this.state.inputVal],
    inputVal: ''
});

// 불변성 (Immutable)
// state는 직접 수정할 수 없습니다.

// HTML 이스케이프 해제
dangerouslySetInnerHTML={{ __html: item }}

// label 태그 사용
<label htmlFor="inputField">입력 내용</label>
<input id="inputField" className="input" value={this.state.inputVal} onChange={this.handleInputChange.bind(this)} />
<button onClick={this.handleSubmit.bind(this)}>제출</button>

코드 최적화

// 생성자에서 메서드 바인딩
this.handleSubmit = this.handleSubmit.bind(this);

// 리스트 렌더링을 위한 별도 메서드
renderList() {
    return this.state.list.map((item, index) => {
        return (
            <ListItem
                key={index}
                content={item}
                index={index}
                onDelete={this.handleDelete}
            />
        );
    });
}

// 자식 컴포넌트
render() {
    const { content } = this.props;
    return (
        <div onClick={this.handleClick}>
            {content}
        </div>
    );
}

handleClick() {
    const { onDelete, index } = this.props;
    onDelete(index);
}

// ES6를 사용한 상태 업데이트
this.setState((prevState) => ({
    list: [...prevState.list, prevState.inputVal],
    inputVal: ''
}));

handleDelete(index) {
    this.setState((prevState) => {
        const list = [...prevState.list];
        list.splice(index, 1);
        return { list };
    });
}

Props 타입 검증

npm install --save prop-types
import PropTypes from 'prop-types';

ListItem.propTypes = {
    test: PropTypes.string.isRequired, // 필수 전달
    test: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // 여러 타입 허용
    content: PropTypes.string,
    onDelete: PropTypes.func,
    index: PropTypes.number
};

// 기본값 설정
ListItem.defaultProps = {
    test: 'hello'
};

디버깅 도구

React Developer Tools 및 Redux DevTools 설치 및 사용

Chrome 브라우저 => 설정 => 확장 프로그램 => Chrome 웹 스토어 열기 => "React Developer Tools" 검색 => 설치
=> 브라우저에서 개발자 도구 열기 => React 탭 확인

태그: React redux React Router axios antd

6월 25일 01:46에 게시됨