Git
์ ์ฌ์ฉํ ํ ํ์
๊ณผ์ squash, rebase
๋ฅผ ์ ์ฉํ์ฌ commit ๋ด์ญ ๊ด๋ฆฌTrello
๋ฅผ ํ์ฉํ ์คํฌ๋ผ ๋ฐฉ์ ์๋ ํ๋ก์ ํธ ์งํReact.js
๋ฅผ ์ด์ฉํ ํด๋ก CRA(create-react-app)
๋ฅผ ์ฌ์ฉํ ์ด๊ธฐ ์ธํ
styled-components
ํ์ฉRedux
๋ฅผ ์ด์ฉํ ์ ์ญ ์ํ ๊ด๋ฆฌHooks
๋ฅผ ์ด์ฉํ ์ปดํฌ๋ํธ ์ํ ๊ด๋ฆฌlocalStorage
์ token
์ ์ด์ฉํ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ ๊ตฌํreact-router, query string, path
๋ก ๋์ ๋ผ์ฐํ
๊ตฌํDjango
๋ฅผ ์ฌ์ฉํ ์ด๊ธฐ ์ธํ
Aquery
๋ฅผ ์ด์ฉํ ๋ชจ๋ธ๋งBcrypt
๋ฅผ ์ด์ฉํ ๋น๋ฐ๋ฒํธ ์ํธํ๋ก ํ์๊ฐ์
๊ธฐ๋ฅ ๊ตฌํPyJWT
Mysql
Front-endReact
JavaScript
styled-components
Redux, react-redux
Git
Back-endAqueryTool
Python
Django
MySQL
PyJWT / Bcrypt
Git
Redux ์ ์ญ ์ํ ๊ด๋ฆฌ
const ADD_TO_CART = 'cart/ADD_TO_CART';
const REMOVE_ITEM = 'cart/REMOVE_ITEM';
const RESET_CART_LIST = 'cart/RESET_CART_LIST';
const INCREASE_COUNT = 'cart/INCREASE_COUNT';
const DECREASE_COUNT = 'cart/DECREASE_COUNT';
const SET_TOTAL = 'cart/SET_TOTAL';
const GET_ITEMS = 'cart/GET_ITEMS';
export const addToCart = (item) => ({
type: ADD_TO_CART,
payload: item,
});
export const removeItem = (items) => ({
type: REMOVE_ITEM,
payload: items,
});
export const resetCartList = () => ({
type: RESET_CART_LIST,
});
export const increaseCount = (items) => ({
type: INCREASE_COUNT,
payload: items,
});
export const decreaseCount = (items) => ({
type: DECREASE_COUNT,
payload: items,
});
export const setTotal = () => ({
type: SET_TOTAL,
});
export const getItems = (items) => ({
type: GET_ITEMS,
payload: items,
});
const initialState = {
totalPrice: 0,
totalCount: 0,
items: [],
};
const cart = (state = initialState, { type, payload }) => {
switch (type) {
case ADD_TO_CART:
return updateItems(state, payload);
case REMOVE_ITEM:
return updateItems(state, payload);
case RESET_CART_LIST:
return {
...state,
totalPrice: 0,
totalCount: 0,
items: [],
};
case INCREASE_COUNT:
return updateItems(state, payload);
case DECREASE_COUNT:
return updateItems(state, payload);
case GET_ITEMS:
return updateItems(state, payload);
default:
return state;
}
};
const updateItems = (state, payload) => {
const { items } = state;
const isArray = Array.isArray(payload);
return isArray
? {
...state,
items: [...payload],
totalCount: payload.reduce((acc, { count }) => acc + count, 0),
totalPrice: payload.reduce(
(acc, { count, price }) => acc + count * price,
0,
),
}
: {
...state,
items: checkCartItems(items, payload),
totalCount: state.totalCount + 1,
};
};
const checkCartItems = (items, payload) => {
const isAddedToCart = items.find((item) => item.id === payload.id);
const AddedItem = (item) =>
item.id === payload.id ? { ...item, count: item.count + 1 } : item;
return isAddedToCart ? items.map(AddedItem) : [...items, payload];
};
export default cart;
๋ฉ์ธ, ๋ฆฌ๋ทฐ ํ์ด์ง - ์ ์ํ ์ด๋๊ธธ
AWS S3
์ react-aws-s3
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํ ์ด๋ฏธ์ง ์
๋ก๋ ๊ธฐ๋ฅ ๊ตฌํ// ReviewForm.js
import React, { useState, useRef } from 'react';
import S3 from 'react-aws-s3';
...
import {
accessKeyId,
secretAccessKey,
} from '../../../config';
export default function ReviewForm({ setReviewData }) {
...
const handleFileChange = (e) => {
const reader = new FileReader();
const file = e.target.files[0];
const newFileName = file.name;
const config = {
bucketName: 'uploadtestdb',
dirName: 'photos',
region: 'ap-northeast-2',
accessKeyId,
secretAccessKey,
};
const ReactS3Client = new S3(config);
reader.onloadend = () => {
setImagePreviewUrl(reader.result);
};
reader.readAsDataURL(file);
ReactS3Client.uploadFile(file, newFileName)
.then((data) => setImageUrl(data.location))
.catch((err) => console.error(err));
};
...
// ImageUploader.js
export default function ImageUploader({ handleFileChange, fileInput }) {
return (
<Form className="uploadSteps">
<File>
<FileInput
ref={fileInput}
onChange={handleFileChange}
type="file"
multiple
/>
</File>
</Form>
);
}
...
react-aws-s3
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํ์ฌ AWS S3
์ ์ด๋ฏธ์ง ์
๋ก๋ (์์ง AWS๊ฐ ์ด๋ ต๋คใ
ใ
)import React, { useState } from 'react';
import styled, { keyframes } from 'styled-components';
import { API, reviewsTestToken } from '../../config';
export default function Reviews() {
const [reviewData, setReviewData] = useState([]);
const getReviewData = async () => {
const requestOptions = {
method: 'GET',
headers: {
Authorization: reviewsTestToken,
},
};
const res = await fetch(`${API}/reviews`, requestOptions);
const { data } = await res.json();
setReviewData(data);
};
useEffect(() => {
getReviewData();
}, []);
return (
...
)
async, await
๋ฅผ ์ด์ฉํ ๋ฐ์ดํฐ ํต์