이번 포스팅은 스프링 부트 Polling App의 마지막 포스팅으로 그동안 작성했던 코드를 화면으로 띄워주도록 하겠습니다!!
(원본링크 https://www.callicoder.com/spring-boot-spring-security-jwt-mysql-react-app-part-1/)
본 포스팅을 따라하기 어렵거나, 결과물을 위해 빠르게 보셔야 하는 분을 위해 코드를 올려놓았습니다.
코드는 Github 저장소에 올려놓았습니다.
이전 포스팅은 여기 있습니다.
완성되고 실행이 정상적으로 이루어지면 아래와같이 실행됩니다 :)
Front-End 작성하기
설치 -> app 만들기 -> Dependencies 설치하기 -> Design 수정하기 -> 실행 의 과정이 있습니다!!
코드는 Github 저장소에 올려놓았습니다.
1. create-react-app 설치하기
npm install -g create-react-app
2. 사용자 app 만들기
create-react-app polling-app-client
3. Dependency 추가 설치하기
cd polling-app-client
npm install antd react-router-dom --save
npm install react-app-rewired babel-plugin-import react-app-rewire-less --save-dev
4. Ant 디자인 설정하기
package.json 안에 설정되어있던 react-scripts 를 react-app-rewired 로 바꾸어 주고 config-overrides.js 파일을 추가 해 줍니다.
package.json
{
"name": "polling-app-client",
"version": "0.1.0",
"private": true,
"dependencies": {
"antd": "^3.2.2",
"react": "^16.5.2",
"react-dom": "^16.5.2",
"react-router-dom": "^4.3.1",
"react-scripts": "1.1.5"
},
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test --env=jsdom",
"eject": "react-scripts eject"
},
"devDependencies": {
"babel-plugin-import": "^1.6.5",
"react-app-rewire-less": "^2.1.0",
"react-app-rewired": "^1.4.1"
}
}
config-overrides.js
const { injectBabelPlugin } = require('react-app-rewired');
const rewireLess = require('react-app-rewire-less');
module.exports = function override(config, env) {
config = injectBabelPlugin(['import', { libraryName: 'antd', style: true }], config);
config = rewireLess.withLoaderOptions({
modifyVars: {
"@layout-body-background": "#FFFFFF",
"@layout-header-background": "#FFFFFF",
"@layout-footer-background": "#FFFFFF"
},
javascriptEnabled: true
})(config, env);
return config;
};
5. Running the App
npm start
Front-end 코드
코드는 Github 저장소에 올려놓았습니다.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './app/App';
import registerServiceWorker from './registerServiceWorker';
import { BrowserRouter as Router } from 'react-router-dom';
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById('root')
);
registerServiceWorker();
src/app/App.js
import React, { Component } from 'react';
import './App.css';
import {
Route,
withRouter,
Switch
} from 'react-router-dom';
import { getCurrentUser } from '../util/APIUtils';
import { ACCESS_TOKEN } from '../constants';
import PollList from '../poll/PollList';
import NewPoll from '../poll/NewPoll';
import Login from '../user/login/Login';
import Signup from '../user/signup/Signup';
import Profile from '../user/profile/Profile';
import AppHeader from '../common/AppHeader';
import NotFound from '../common/NotFound';
import LoadingIndicator from '../common/LoadingIndicator';
import PrivateRoute from '../common/PrivateRoute';
import { Layout, notification } from 'antd';
const { Content } = Layout;
class App extends Component {
constructor(props) {
super(props);
this.state = {
currentUser: null,
isAuthenticated: false,
isLoading: false
}
this.handleLogout = this.handleLogout.bind(this);
this.loadCurrentUser = this.loadCurrentUser.bind(this);
this.handleLogin = this.handleLogin.bind(this);
notification.config({
placement: 'topRight',
top: 70,
duration: 3,
});
}
loadCurrentUser() {
this.setState({
isLoading: true
});
getCurrentUser()
.then(response => {
this.setState({
currentUser: response,
isAuthenticated: true,
isLoading: false
});
}).catch(error => {
this.setState({
isLoading: false
});
});
}
componentDidMount() {
this.loadCurrentUser();
}
handleLogout(redirectTo="/", notificationType="success", description="You're successfully logged out.") {
localStorage.removeItem(ACCESS_TOKEN);
this.setState({
currentUser: null,
isAuthenticated: false
});
this.props.history.push(redirectTo);
notification[notificationType]({
message: 'Polling App',
description: description,
});
}
handleLogin() {
notification.success({
message: 'Polling App',
description: "You're successfully logged in.",
});
this.loadCurrentUser();
this.props.history.push("/");
}
render() {
if(this.state.isLoading) {
return <LoadingIndicator />
}
return (
<Layout className="app-container">
<AppHeader isAuthenticated={this.state.isAuthenticated}
currentUser={this.state.currentUser}
onLogout={this.handleLogout} />
<Content className="app-content">
<div className="container">
<Switch>
<Route exact path="/"
render={(props) => <PollList isAuthenticated={this.state.isAuthenticated}
currentUser={this.state.currentUser} handleLogout={this.handleLogout} {...props} />}>
</Route>
<Route path="/login"
render={(props) => <Login onLogin={this.handleLogin} {...props} />}></Route>
<Route path="/signup" component={Signup}></Route>
<Route path="/users/:username"
render={(props) => <Profile isAuthenticated={this.state.isAuthenticated} currentUser={this.state.currentUser} {...props} />}>
</Route>
<PrivateRoute authenticated={this.state.isAuthenticated} path="/poll/new" component={NewPoll} handleLogout={this.handleLogout}></PrivateRoute>
<Route component={NotFound}></Route>
</Switch>
</div>
</Content>
</Layout>
);
}
}
export default withRouter(App);
src / common - 공통 구성 요소
-
AppHeader.js : 인증되지 않은 사용자에 대한 로그인 및 SignUp 버튼과 인증 된 사용자에 대한 홈, 프로필 및 폴링 버튼을 렌더링하는 헤더 구성 요소.
-
LoadingIndicator.js : API 호출이 진행되는 동안 다른 구성 요소에서 로딩 표시를 렌더링하는 데 사용됩니다.
-
NotFound.js : App현재 url과 일치하는 경로가 없으면 구성 요소에서 이것을 사용 하여 404 Not Found 페이지를 렌더링합니다.
-
PrivateRoute.js : /login사용자가 인증없이 보호 된 페이지에 액세스하려고하는 경우 리디렉션되는 메타 구성 요소입니다 .
-
ServerError.js : 다른 구성 요소는이 API를 사용하여 구성 요소가 처리 할 수없는 500 개의 오류로 API가 응답하는 경우 500 서버 오류 페이지를 표시합니다.
src/constants
src/constants/index.js
export const API_BASE_URL = process.env.REACT_APP_API_BASE_URL || 'http://localhost:8080/api';
export const ACCESS_TOKEN = 'accessToken';
export const POLL_LIST_SIZE = 30;
export const MAX_CHOICES = 6;
export const POLL_QUESTION_MAX_LENGTH = 140;
export const POLL_CHOICE_MAX_LENGTH = 40;
export const NAME_MIN_LENGTH = 4;
export const NAME_MAX_LENGTH = 40;
export const USERNAME_MIN_LENGTH = 3;
export const USERNAME_MAX_LENGTH = 15;
export const EMAIL_MAX_LENGTH = 40;
export const PASSWORD_MIN_LENGTH = 6;
export const PASSWORD_MAX_LENGTH = 20;
src / poll
-
NewPoll.js : 설문 조사 작성 양식을 표시
-
PollList.js :이 구성 요소는 투표 목록과 설문조사를 보여주는 데 사용되고, 사용자가 작성한 설문 목록과 해당 사용자가 투표 한 설문 목록을 렌더링합니다.
-
Poll.js : PollList구성 요소가 단일 폴을 렌더링하는 데 사용됩니다 .
src / user
-
login / Login.js : Login 구성 요소는 로그인 양식을 렌더링하여 사용자를 인증하는 로그인 API를 호출합니다.
-
signup / Signup.js : 등록 양식을 렌더링하고 클라이언트 측 유효성 검증을 포함합니다. React에서 폼 유효성 검사를 수행하는 방법을 배우고 싶은지 확인하는 것은 흥미로운 구성 요소입니다.
-
profile / Profile.js : 프로필 페이지는 사용자의 공개 프로필을 렌더링합니다. 사용자의 기본 정보, 사용자가 작성한 여론 조사 목록 및 사용자가 투표 한 여론 조사 목록을 표시합니다.
src / util
-
APIUtils.js :이 스크립트에는 모든 Rest API 호출이 기록됩니다. fetchAPI를 사용하여 백엔드 서버에 요청합니다.
-
Colors.js :이 유틸리티는 사용자의 아바타에서 사용할 임의의 색상을 얻는 데 사용됩니다.
-
Helpers.js : 날짜 형식을 지정하는 헬퍼 함수가 포함되어 있습니다.
앱 실행
cd polling-app-client
npm install && npm start
'Old Branch' 카테고리의 다른 글
장고(Django) 예제 / 북마크 프로젝트 - 1. 개발환경 세팅하기 (0) | 2019.07.22 |
---|---|
Django 구성 분석하기와 기본 세팅 (0) | 2019.07.20 |
스프링 부트(Spring Boot)와 Security, MySQL, React를 사용한 Spring Polling App (2) (2) | 2019.07.18 |
스프링 부트(Spring Boot)와 Security, MySQL, React를 사용한 Spring Polling App(3) (0) | 2019.07.18 |
스프링 부트(Spring Boot)와 Security, MySQL, React를 사용한 Spring Polling App (1) (1) | 2019.07.17 |