스프링 부트(Spring Boot)와 Security, MySQL, React를 사용한 Spring Polling App(4)
이번 포스팅은 스프링 부트 Polling App의 마지막 포스팅으로 그동안 작성했던 코드를 화면으로 띄워주도록 하겠습니다!!
(원본링크 https://www.callicoder.com/spring-boot-spring-security-jwt-mysql-react-app-part-1/)
본 포스팅을 따라하기 어렵거나, 결과물을 위해 빠르게 보셔야 하는 분을 위해 코드를 올려놓았습니다.
코드는 Github 저장소에 올려놓았습니다.
이전 포스팅은 여기 있습니다.
[Java/Spring-framework] - 스프링 부트(Spring Boot)와 Security, MySQL, React를 사용한 Spring Polling App (1)
스프링 부트(Spring Boot)와 Security, MySQL, React를 사용한 Spring Polling App (1)
이번 포스팅에서는 스프링 부트(Spring Boot )와 스프링 시큐리티(Spring Security)를 활용해서 Spring Polling App을 만들어 보도록 하겠습니다! 데이터베이스로는 MySQL를 사용 할 것이고, 프론트엔드로는 React..
woolbro.tistory.com
[Java/Spring-framework] - 스프링 부트(Spring Boot)와 Security, MySQL, React를 사용한 Spring Polling App (2)
스프링 부트(Spring Boot)와 Security, MySQL, React를 사용한 Spring Polling App (2)
이전포스팅에 이어서 작성하도록 하겠습니다. [Java/Spring-framework] - 스프링 부트(Spring Boot)와 Security, MySQL, React를 사용한 Spring Polling App (1) 스프링 부트(Spring Boot)와 Security, MySQL, Rea..
woolbro.tistory.com
[Java/Spring-framework] - 스프링 부트(Spring Boot)와 Security, MySQL, React를 사용한 Spring Polling App(3)
스프링 부트(Spring Boot)와 Security, MySQL, React를 사용한 Spring Polling App(3)
이전포스팅에 이어서 작성을 해 보도록 하겠습니다. 이번 포스팅의 코드는 Github 저장소에 있습니다. ...더보기 [Java/Spring-framework] - 스프링 부트(Spring Boot)와 Security, MySQL, React를 사용한 Spring..
woolbro.tistory.com
완성되고 실행이 정상적으로 이루어지면 아래와같이 실행됩니다 :)
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