˙Install Package
▷ npx react-create-app .
- npx는 외부 package에 포함된 항목(파일)들을 실행할 때에 사용
▷ npm install react-router --save
- react router(라우터) 설치 (WEB & APP)
▷ npm install react-router-dom --save
- react router(라우터) 설치 (WEB)
▷ npm install react-router-native --save
- react router(라우터) 설치 (APP)
▷ npm install axios --save
- HTTP 통신을 하는 데에 있어서 자주 사용되는 JavaScript Library
▷ npm install http-proxy-middleware --save
- 편리한 proxy 설정과 변경, 작업 등이 가능하도록 지원하는 Library
- 여러 가지의 proxy 등록이 가능하고, 옵션을 지정할 때 편리하게 사용할 수 있으며, 조금 더 자유롭게 사용이 가능
- Port 번호가 다른 server 간의 통신이 가능하도록 설정(통신 가능)
- const {createProxyMiddleware} = require('http-proxy-middleware');
▷ npm install concurrently –-save
- NodeJS server와 react server를 동시에 구동시킬 수 있도록 지원하는 Library
- root 디렉터리의 package.json script 항목에 사용할 명령어로 실행
▷ npm install antd –-save
- antd 디자인 적용 명령어
▷ npm install redux –-save
- redux 설치 명령어
▷ npm install react-redux –-save
- react-redux 설치 명령어
▷ npm install redux-promise –-save
redux 내의 store의 상태를 적용하기에는 적절하지 않은 형태로 promise 객체는 결과가 필요(본래는 action → reducer)하다. 따라서, middleware를 통과하지 않고 reducer에서 넘겨줄 수 없는데, redux-trunk를 이용하거나, redux-promise를 사용하는 방법이 존재(payload 되는 객체가 통신 결과를 기다린 후에 reducer에게 전달)한다.
▷ npm install redux-trunk –-save
액션 생성자가 반환하는 것을 객체가 아닌 함수를 사용하도록 지원한다. 함수가 반환하면 해당 함수에 대한 실행이 끝난 뒤의 값을 액션으로 넘기는 방식인데, 결론적으로 기존 액션 생성자가 반환하는 객체로는 처리하지 못하였던 비동기 작업을 일반 함수로 반환 함으로써 사용이 가능하다.
˙NPM vs NPX
▷ NPM : Package Manager
- Node.js 자동화된 의존성과 Package 관리를 위한 Manager (관리용)
- 프로젝트(또는 App) 내에 필요한 의존성 관련된 항목들은 package.json 내에서 지정
- npm install 명령어를 통하여 원하는 Package 항목을 node_modules(Local) 설치
초기 설치한 모듈을 지속적으로 사용하기 때문에 버전 관리가 어려울 수 있고, 동일 모듈을 사용하여도 프로젝트(또는 App)마다 다른 버전을 요구하는 경우 버전 문제가 발생할 수 있기 때문에, 최신 버전을 매번 설치해주어야 하는 이슈가 발생할 수 있음
▷ NPX : Package Runner
- Node.js Package 실행을 위한 Tool (실행용)
- Package 최신 버전을 설치하여 실행(존재한다면 미설치) 한 후에, 해당 Package 제거를 진행하는 방식
▷ NPX 사용 시에는 다음과 같은 경우에 효율적으로 사용이 가능
- npm run-script 명령어를 사용하지 않고 Local Package 사용을 목적으로 하는 경우
- 단일(일회) 성 Package 사용을 목적으로 하는 경우
- NPM 5.2.0 버전 이후부터 추가
▷ NPX를 사용하지 않고 NPM을 사용할 경우에는 번거로운 작업이 필요
Package 실행 시 Local Package가 위치한 경로로 실행시키거나, package.json의 script로 경로 지정이 필요하다. 또한, 업데이트(최신 모듈) 사용 또는 적용의 경우에도 주기적으로 업데이트가 필요(따라서, NPX 권장)하다.
˙Server Start (Documents Directory)
▷ npm run dev
- package.json → 'scripts' : {'dev': 'concurrently \"npm run backend\" \"npm run start --prefix client\'}
˙ React Redux
▷ Redux : JavaScript 상태 관리 Library로 본질은 Node.js 모듈
- UI → Actrion → Reducer → Store → UI (단방향 흐름)
- Redux 왜 사용할까? : https://velog.io/@youthfulhps/What-is-Redux-and-why-use-it
˙ React Hook
▷ Hook : React의 새로운 기능으로 16.8 버전 이후로 추가
- state, component 등에 대한 부분을 바꿈
- function component에서 state를 가질 수 있게 됨으로써, class component와 render 등을 하지 않아도 됨
- 즉, 모든 것들이 하나의 function(함수화)이 되는 개발이 가능
- Hook 자세히 알아보기 : https://velog.io/@velopert/react-hooks
∴ CODE LOGIC
// redux action
export const LOGIN_USER = 'login_user';
export const REGISTER_USER = 'register_user';
export const AUTH_USER = 'auth_user';
// redux action User_action(API)
import axios from 'axios';
import {LOGIN_USER, REGISTER_USER, AUTH_USER} from './types';
export function loginUser(dataToSubmit) {
const request = axios.post('/api/users/login', dataToSubmit)
.then(response => response.data)
return {type: LOGIN_USER, payload: request}
}
export function registerUser(dataToSubmit) {
const request = axios.post('/api/users/register', dataToSubmit)
.then(response => response.data)
return {type: REGISTER_USER, payload: request}
}
export function auth() {
const request = axios.get('/api/users/auth')
.then(response => response.data)
return {type: AUTH_USER, payload: request}
}
// redux reducer
import {combineReducers} from 'redux';
import user from './user_reducer';
const rootReducer = combineReducers({
user
})
export default rootReducer;
// redux reducer User_reducer
import {LOGIN_USER, REGISTER_USER, AUTH_USER} from '../_actions/types';
export default function (state = {}, action) {
switch (action.type) {
case LOGIN_USER:
return {...state, loginSuccess: action.payload}
// break;
case REGISTER_USER:
return {...state, register: action.payload}
// break;
case AUTH_USER:
return {...state, userData: action.payload}
// break;
default:
return state;
}
}
import React, {useEffect} from 'react';
import axios from 'axios';
import {withRouter} from 'react-router-dom';
function LandingPage(props) {
useEffect(() => {
axios.get('/api/hello')
.then(response => {console.log(response)})
}, [])
const onClickHandler = () => {
axios.get('/api/users/logout')
.then(response => {
// logout 성공 시에는 login 화면으로 이동
if (response.data.success) {
props.history.push('/login')
// logout 실패 시에는 Error 메시지 조회
} else {
alert('로그아웃 하는데 실패 했습니다.')
}
})
}
return (
<div style = {{
display: 'flex', justifyContent: 'center', alignItems: 'center',
width: '100%', height: '100vh'
}}>
<h2>시작 페이지</h2>
<button onClick={onClickHandler}>
로그아웃
</button>
</div>
)
}
export default withRouter(LandingPage)
import React, {useState} from 'react';
// import Axios from 'axios';
import {useDispatch} from 'react-redux';
import {loginUser} from '../../../_actions/user_action';
import {withRouter} from 'react-router-dom';
function LoginPage(props) {
const dispatch = useDispatch();
// 사용할 변수 state 선언
const [Email, setEmail] = useState('')
const [Password, setPassword] = useState('')
const onEmailHandler = (event) => {
setEmail(event.currentTarget.value)
}
const onPasswordHandler = (event) => {
setPassword(event.currentTarget.value)
}
const onSubmitHandler = (event) => {
// 매번 자동으로 새로고침 되어지는 event 방지
event.preventDefault();
let body = {
email: Email,
password: Password
}
dispatch(loginUser(body))
.then(response => {
// login 성공 시에는 메인 화면으로 이동
if (response.payload.loginSuccess) {
props.history.push('/')
// login 실패 시에는 Error 메시지 조회
} else {
alert('Error')
}
})
}
return (
<div style={{
display: 'flex', justifyContent: 'center', alignItems: 'center',
width: '100%', height: '100vh'
}}>
<form style={{display: 'flex', flexDirection: 'column'}}
onSubmit={onSubmitHandler}
>
<label>Email</label>
// email 입력 설정(login 입력 화면)
<input type='email' value={Email} onChange={onEmailHandler} />
<label>Password</label>
// password 입력 설정(login 입력 화면)
<input type='password' value={Password} onChange={onPasswordHandler} />
<br />
<button type='submit'>
Login
</button>
</form>
</div>
)
}
export default withRouter(LoginPage)
import React, {useState} from 'react';
// import Axios from 'axios';
import {useDispatch} from 'react-redux';
import {registerUser} from '../../../_actions/user_action';
import {withRouter} from 'react-router-dom';
function RegisterPage(props) {
const dispatch = useDispatch();
// 사용할 변수 state 선언
const [Email, setEmail] = useState('')
const [Name, setName] = useState('')
const [Password, setPassword] = useState('')
const [ConfirmPassword, setConfirmPassword] = useState('')
const onEmailHandler = (event) => {
setEmail(event.currentTarget.value)
}
const onNameHandler = (event) => {
setName(event.currentTarget.value)
}
const onPasswordHandler = (event) => {
setPassword(event.currentTarget.value)
}
const onConfirmPasswordHandler = (event) => {
setConfirmPassword(event.currentTarget.value)
}
const onSubmitHandler = (event) => {
event.preventDefault();
// 처음 입력한 password와 확인 입력한 password가 다를 경우
if (Password !== ConfirmPassword) {
return alert('비밀번호와 비밀번호 확인은 같아야 합니다.')
}
let body = {
email: Email,
password: Password,
name: Name
}
dispatch(registerUser(body))
.then(response => {
// 회원가입(register user) dispatch payload 정보 처리가 성공한 경우
if (response.payload.success) {
props.history.push('/login')
// 회원가입(register user) dispatch payload 정보 처리가 실패한 경우
} else {
alert('Failed to sign up')
}
})
}
return (
<div style={{
display: 'flex', justifyContent: 'center', alignItems: 'center',
width: '100%', height: '100vh'
}}>
<form style={{display: 'flex', flexDirection: 'column'}}
onSubmit={onSubmitHandler}
>
<label>Email</label>
// email 입력 설정(회원가입(register) 입력 화면)
<input type='email' value={Email} onChange={onEmailHandler} />
<label>Name</label>
// name 입력 설정(회원가입(register) 입력 화면)
<input type='text' value={Name} onChange={onNameHandler} />
<label>Password</label>
// password 입력 설정(회원가입(register) 입력 화면)
<input type='password' value={Password} onChange={onPasswordHandler} />
<label>Confirm Password</label>
// password(입력 확인 password) 입력 설정(회원가입(register) 입력 화면)
<input type='password' value={ConfirmPassword} onChange={onConfirmPasswordHandler} />
<br />
<button type='submit'>
회원 가입
</button>
</form>
</div>
)
}
export default withRouter(RegisterPage)
import React, {useEffect} from 'react';
// import Axios from 'axios';
import {useDispatch} from 'react-redux';
import {auth} from '../_actions/user_action';
export default function (SpecificComponent, option, adminRoute = null) {
function AuthenticationCheck(props) {
const dispatch = useDispatch();
useEffect(() => {
dispatch(auth()).then(response => {
// Auth 정보가 일치하지 않는 등 login 정보가 부합하지 않는 경우
if (!response.payload.isAuth) {
if (option) {
props.history.push('/login')
}
// Auth 정보가 일치 하는 등 login 정보가 부합하는 경우
} else {
if (adminRoute && !response.payload.isAdmin) {
props.history.push('/')
} else {
if (option === false)
props.history.push('/')
}
}
})
// }, [])
}, [dispatch, props.history])
return (
<SpecificComponent />
)
}
return AuthenticationCheck
}
import React from 'react';
import {BrowserRouter as Router, Switch, Route, /* Link */} from 'react-router-dom';
import LandingPage from './components/views/LandingPage/LandingPage'
import LoginPage from './components/views/LoginPage/LoginPage';
import RegisterPage from './components/views/RegisterPage/RegisterPage';
import Auth from './hoc/auth'
// 사용 될 API 항목들을 지정(Switch 문을 사용하여 요청받은 API 호출)
// component 정보에 따라 로그인(인증)된 사용자에 따라 접근가능/불가능/무관 페이지 처리
function App() {
return (
<Router>
<div>
{/*
A <Switch> looks through all its children <Route>
elements and renders the first one whose path
matches the current URL. Use a <Switch> any time
you have multiple routes, but you want only one
of them to render at a time
*/}
<Switch>
<Route exact path='/' component={Auth(LandingPage, null)} />
<Route exact path='/login' component={Auth(LoginPage, false)} />
<Route exact path='/register' component={Auth(RegisterPage, false)} />
</Switch>
</div>
</Router>
);
}
export default App;
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import 'antd/dist/antd.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {Provider} from 'react-redux';
import {applyMiddleware, createStore} from 'redux';
import promiseMiddleware from 'redux-promise';
import ReduxThunk from 'redux-thunk';
import Reducer from './_reducers';
const createStoreWithMiddleware = applyMiddleware(promiseMiddleware, ReduxThunk)(createStore)
ReactDOM.render(
<Provider
// redux 상태 모니터링 확장 프로그램(Chrome)
// ★★★ https://bigstar-vlog.tistory.com/47
store={createStoreWithMiddleware(Reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__()
)}
>
<App />
</Provider>
, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
// proxy 설정(NodeJS에서 사용된 server 및 port 정보를 사용)
const {createProxyMiddleware} = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api',
createProxyMiddleware({
target: 'http://localhost:5000',
changeOrigin: true,
})
);
};
'끄적대기' 카테고리의 다른 글
데이터베이스 정규화와 비정규화 (0) | 2022.04.19 |
---|---|
데이터베이스 ETL : Extract Transform Load (0) | 2022.04.18 |
[Chapter 1] NodeJS (0) | 2022.03.24 |
화이트박스 테스트와 블랙박스 테스트 (0) | 2022.03.24 |
Replication(리플리케이션) vs Clustering(클러스터링) (0) | 2022.03.24 |