Skip to content

Commit

Permalink
Merge pull request #113 from boostcamp-2020/develop
Browse files Browse the repository at this point in the history
[All] 3주차 배포
  • Loading branch information
enhakkore authored Nov 13, 2020
2 parents f095ae3 + e89920c commit 884a012
Show file tree
Hide file tree
Showing 92 changed files with 9,115 additions and 3,802 deletions.
1 change: 1 addition & 0 deletions client/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module.exports = {
env: {
browser: true,
es2021: true,
jest: true,
},
extends: ['plugin:react/recommended', 'airbnb'],
parserOptions: {
Expand Down
10,409 changes: 7,229 additions & 3,180 deletions client/package-lock.json

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@
"author": "",
"license": "ISC",
"dependencies": {
"@emotion/styled": "^10.0.27",
"@testing-library/jest-dom": "^5.11.5",
"@testing-library/react": "^11.1.1",
"prop-types": "^15.7.2",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0"
"react-router-dom": "^5.2.0",
"socket.io-client": "^3.0.1"
},
"devDependencies": {
"@babel/cli": "^7.12.1",
Expand All @@ -37,6 +41,7 @@
"eslint-plugin-react": "^7.21.5",
"eslint-plugin-react-hooks": "^4.2.0",
"html-webpack-plugin": "^4.5.0",
"jest": "^26.6.3",
"style-loader": "^2.0.0",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.12",
Expand Down
38 changes: 10 additions & 28 deletions client/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,20 @@ import {
BrowserRouter as Router,
Switch,
Route,
Redirect,
} from 'react-router-dom';

import StoreWrapper from './stores/StoreWrapper';

import Header from './components/Header';
import DetailMain from './components/issueDetail/IssueDetailPage';
import IssueMain from './components/issueList/IssueMain';
import NewIssueMain from './components/newIssue/NewIssueMain';
import LoginPage from './components/login/LoginPage';
import AuthCallback from './components/login/AuthCallback';
import RouteIf from './routes/RouteIf';

export default function App() {
return (
<>
<StoreWrapper>
<Router>
<Switch>
<Route exact path="/">
<Header />
<IssueMain />
</Route>
<Route path="/new-issue">
<Header />
<NewIssueMain />
</Route>
<Route path="/detail/:issueId">
<Header />
<DetailMain />
</Route>
<Redirect path="*" to="/" />
</Switch>
</Router>
</StoreWrapper>
</>
<Router>
<Switch>
<Route path="/login" component={LoginPage} />
<Route path="/callback" component={AuthCallback} />
<RouteIf path="*" />
</Switch>
</Router>
);
}
37 changes: 37 additions & 0 deletions client/src/Socket.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useEffect, useContext } from 'react';
import io from 'socket.io-client';
import { IssuesContext } from './stores/IssueStore';
import { LabelsContext } from './stores/LabelStore';
import { UsersContext } from './stores/UserStore';

const socket = io(process.env.SOCKET_URL);

export default function Socket() {
const { dispatch: issueDispatch } = useContext(IssuesContext);
const { dispatch: labelDispatch } = useContext(LabelsContext);
const { dispatch: userDispatch } = useContext(UsersContext);

useEffect(() => {
socket.on('label', ({ type, payload }) => {
labelDispatch({ type, payload: { ...payload, id: +payload.id } });
});

socket.on('user', ({ type, payload }) => {
userDispatch({ type, payload: { ...payload, id: +payload.id } });
});

socket.on('issue', ({ type, payload }) => {
if (type === 'ADD' || type === 'UPDATE') {
issueDispatch({ type, payload });
return;
}

if (type === 'UPDATE:isClosed') {
const { issueIds, isClosed } = payload;
issueIds.forEach((issueId) => issueDispatch({ type: 'UPDATE', payload: { id: issueId, isClosed } }));
}
});
}, []);

return null;
}
195 changes: 121 additions & 74 deletions client/src/apis/api.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,46 @@
import { removeUserInfo } from '../utils/utils';

const baseURL = process.env.BASE_URL || 'http://localhost:3000';

const getFreshAccessToken = async () => {
const url = `${baseURL}/auth/fresh`;
const request = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${localStorage.getItem('refreshToken')}`,
},
};

const result = await fetch(url, request);
const status = await result.status;

if (status === 401) {
alert('세션이 만료되어 로그아웃 되었습니다.');
removeUserInfo();
return status;
}
if (status >= 500) throw new Error('Server error');
if (status >= 400) throw new Error('Client error');

const { accessToken } = await result.json();
localStorage.setItem('accessToken', accessToken);

return status;
};

const customFetch = async (url, request) => {
try {
const res = await fetch(url, request);
const status = await res.status;
let res = await fetch(url, request);
let { status } = res;
if (status === 401) {
const freshStatus = await getFreshAccessToken();
if (freshStatus < 400) {
request.headers.Authorization = `Bearer ${localStorage.getItem('accessToken')}`;
res = await fetch(url, request);
status = res.status;
}
}
if (status >= 500) throw new Error('Server error');
if (status >= 400) throw new Error('Client error');
return res.json();
Expand All @@ -10,37 +49,71 @@ const customFetch = async (url, request) => {
}
};

const baseURL = process.env.BASE_URL || 'http://localhost:3000';
const createData = async (path, data) => {
const url = `${baseURL}/${path}`;
const request = {
method: 'POST',
headers: {
Authorization: `Bearer ${localStorage.getItem('accessToken')}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
};
const result = await customFetch(url, request);
return result;
};

const readAllData = async (path) => {
const url = `${baseURL}/${path}`;
const request = {
method: 'GET',
headers: { Authorization: `Bearer ${localStorage.getItem('accessToken')}` },
};
const result = await customFetch(url, request);
return result;
};

const updateData = async (path, data) => {
const url = `${baseURL}/${path}`;
const request = {
method: 'PATCH',
headers: {
Authorization: `Bearer ${localStorage.getItem('accessToken')}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
};
const result = await customFetch(url, request);
return result;
};

const deleteData = async (path, id) => {
const url = `${baseURL}/${path}`;
const request = {
method: 'DELETE',
headers: {
Authorization: `Bearer ${localStorage.getItem('accessToken')}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ id }),
};
const result = await customFetch(url, request);
return result;
};

export const issueAPI = {
async readAll() {
const url = `${baseURL}/issue`;
const request = {
method: 'GET',
headers: { Authorization: 'Bearer ~' },
};
const issues = await customFetch(url, request);
return issues;
return readAllData('issue');
},
async update(data) {
const url = `${baseURL}/issue`;
const request = {
method: 'PATCH',
headers: {
Authorization: 'Bearer ~',
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
};
const result = await customFetch(url, request);
return result;
return updateData('issue', data);
},
async markAll(isClosed, issueIds) {
const url = `${baseURL}/issue/markall`;
const request = {
method: 'PATCH',
headers: {
Authorization: 'Bearer ~',
Authorization: `Bearer ${localStorage.getItem('accessToken')}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ isClosed, issueIds }),
Expand All @@ -49,88 +122,62 @@ export const issueAPI = {
return result;
},
async create(newIssue) {
const url = `${baseURL}/issue`;
const request = {
method: 'POST',
headers: {
Authorization: 'Bearer ~',
'Content-Type': 'application/json',
},
body: JSON.stringify(newIssue),
};
const result = await customFetch(url, request);
return result;
return createData('issue', newIssue);
},
};

export const milestoneAPI = {
async readAll() {
const url = `${baseURL}/milestone`;
const request = {
method: 'GET',
headers: { Authorization: 'Bearer ~' },
};
const milestones = await customFetch(url, request);
return milestones;
return readAllData('milestone');
},
};

export const labelAPI = {
async create(newLabel) {
return createData('label', newLabel);
},
async readAll() {
const url = `${baseURL}/label`;
const request = {
method: 'GET',
headers: { Authorization: 'Bearer ~' },
};
const labels = await customFetch(url, request);
return labels;
return readAllData('label');
},
async update(data) {
return updateData('label', data);
},
async remove(id) {
return deleteData('label', id);
},
};

export const userAPI = {
async readAll() {
const url = `${baseURL}/user`;
const request = {
method: 'GET',
headers: { Authorization: 'Bearer ~' },
};
const users = await customFetch(url, request);
return users;
return readAllData('user');
},
};

export const commentAPI = {
async create(data) {
const url = `${baseURL}/comment`;
const request = {
method: 'POST',
headers: {
Authorization: 'Bearer ~',
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
};
const result = await customFetch(url, request);
return result;
async create(newComment) {
return createData('comment', newComment);
},
async readByIssue(issueId) {
const url = `${baseURL}/comment?issueId=${issueId}`;
const request = {
method: 'get',
headers: { Authorization: 'Bearer ~' },
headers: { Authorization: `Bearer ${localStorage.getItem('accessToken')}` },
};
const comments = await customFetch(url, request);
return comments;
},
async update(data) {
const url = `${baseURL}/comment`;
return updateData('comment', data);
},
};

export const oauthAPI = {
async getAccessToken(code) {
const url = `${baseURL}/auth`;
const request = {
method: 'PATCH',
headers: {
Authorization: 'Bearer ~',
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code }),
};
const result = await customFetch(url, request);
return result;
Expand Down
Loading

0 comments on commit 884a012

Please sign in to comment.