- Next.js / MySQL / AWS ์คํ์ ์ฌ์ฉํ์ฌ ์ฌ์ฉํ SNS ์๋น์ค์ ์ผ๋ถ ์๋น์ค๋ฅผ ์ ๊ณต ์ฌ์ดํธ
- 2021-01-29 ~ 2021-03-02 (์ธ๋ถ UI๋ฐ ๊ธฐ๋ฅ ์์ ์งํ ์ค)
- Next.js (Create-next-app)
- antd (CSS ํ๋ ์์ํฌ)
- styled-componenets (CSS ์คํ์ผ ๊ด๋ฆฌ)
- axios (์๋ฒ๊ฐ ๋ฐ์ดํฐ ์ก์์ )
- immer (๋ฐ์ดํฐ ๋ถ๋ณ์ฑ ๊ด๋ฆฌ)
- moment (๋ ์ง ๊ด๋ฆฌ)
- react-slick (์ด๋ฏธ์ง ์บ๋ฃจ์ )
- swr (์ํ ๊ด๋ฆฌ)
- redux (๋น๋๊ธฐ ์ฒ๋ฆฌ)
- redux-saga (๋น๋๊ธฐ ์ฒ๋ฆฌ +)
- redux-devtools-extension (ํฌ๋กฌ ๋ธ๋ผ์ฐ์ ์ธ๋ถ ์๋น์ค ์ฌ์ฉํ์ฌ ์ํ ๊ด๋ฆฌ, ์ถ์ )
- node.js
- nodemon
- express
- express-session
- sequelize
- mysql2
- multer
- cors
- cookie-parser
- passport
- multer-s3
- hpp
- helmet
- cross-env
- dotenv
- pm2
- SSR๊ณผ ๋ฆฌ๋์ค-์ฌ๊ฐ๋ฅผ ์ด์ฉํ ๋น๋๊ธฐ์ฒ๋ฆฌ
์ฒ์ ๋น ๊ฒ์๋ฌผ์์ SSR์ ํตํด ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์ฑ์๋๋๋ค.
export const getServerSideProps = wrapper.getServerSideProps(
async (context) => {
const cookie = context.req ? context.req.headers.cookie : "";
axios.defaults.headers.Cookie = "";
if (context.req && cookie) {
axios.defaults.headers.Cookie = cookie;
}
context.store.dispatch({
type: LOAD_POSTS_REQUEST,
});
context.store.dispatch(END);
await context.store.sagaTask.toPromise();
}
);
- useDispatch() ํ ํจ์๋ฅผ ํ์ฉํด LOAD_POSTS_REQUEST์ ๊ฐ์ ์ก์ ์ด ํธ์ถ๋๋ฉด ๋ฆฌ๋์ค-์ฌ๊ฐ์์ ์ด๋ฅผ ์ฒ๋ฆฌํ๊ณ ๊ฒฐ๊ณผ๊ฐ์ LOAD_POSTS_SUCCESS / LOAD_POSTS_FAILURE ๋ก ๋ฐํํ๋ค.
unction loadPostsAPI(lastId) {
return axios.get(`/posts?lastId=${lastId || 0}`);
}
function* loadPosts(action) {
try {
const result = yield call(loadPostsAPI, action.lastId);
yield put({
type: LOAD_POSTS_SUCCESS,
data: result.data,
});
} catch (err) {
console.error(err);
yield put({
type: LOAD_POSTS_FAILURE,
error: err.response.data,
});
}
}
function* watchloadPosts() {
yield takeLatest(LOAD_POSTS_REQUEST, loadPosts);
}
export default function* postSaga() {
yield all([
fork(watchloadPosts),
....
])
}
- ๋ฆฌ๋์ค-์ฌ๊ฐ์์ ์ป์ ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ๋ฆฌ๋์์์ ์ฒ๋ฆฌ
const reducer = (state = initialState, action) => {
return produce(state, (draft) => {
switch (action.type) {
case LOAD_POSTS_REQUEST:
draft.loadPostsLoading = true;
draft.loadPostsDone = false;
draft.loadPostsError = null;
break;
case LOAD_POSTS_SUCCESS:
draft.loadPostsLoading = false;
draft.loadPostsDone = true;
draft.mainPosts = draft.mainPosts.concat(action.data);
draft.hasMorePosts = action.data.length === 10;
break;
case LOAD_POSTS_FAILURE:
draft.loadPostsLoading = false;
draft.loadPostsError = action.error;
break;
...
default:
break;
}
});
};
- ๋ฆฌ๋์์์ ์ฒ๋ฆฌ๋ ๊ฐ์ ๋ฐํ์ผ๋ก Virtual DOM์ด ์ด๋ฅผ ๋น๊ตํ๊ต ๋ ๋๋งํ๋ค. ํ์ ์ํฉ์ ์๋ ๊ฒฐ๊ณผ๋ฅผ ์ฐธ์กฐ
์ฑ ์ ํตํด ์ฒ์ javascript๋ฅผ ๋ฐฐ์ธ ๋ promise๋, async await์ ๊ฐ์ ๋น๋๊ธฐ ๋ฌธ๋ฒ๋ค์ ๋จ์ง ์ฑ ์์ ๋ฌธ์๋ค๋ก๋ง ๋์ด๋์ด์์ด์ ์ฝ๊ฒ ์๋ซ์ง ์์๋ค. ํ์ง๋ง, ์ฝ๋๋ฅผ ์์ฑํ๋ฉด์ ์ค์ ๋ก ๊ฒฝํํด๋ณด๋ ๋ฐ์ดํฐ ์ก์์ ์ ์์ด์ ๋น๋๊ธฐ์ฒ๋ฆฌ๊ฐ ์ ๋ ๋น ์ง๋ฉด ์๋๋ค๋ ๊ฒ์ ์๊ฒ ๋์๋ค. ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ์๋ค๋ฉด, ๋ฐฑ์๋ ์๋ฒ์์ ์ํต์ด ์ผ๋ฐฉํฅ์ ์ผ๋ก ์ ๋ฌ๋๊ณ ๊ฒฐ๊ณผ๊ฐ ๋๋ ์๋ฌ๊ฐ์ ๋ง์๋๋ก ๋ฝ์์ฌ์ง๋ ๋ชจ๋ฅธ๋ค.(๋ฆฌํด์ ์ ๋๋ก ๋ฐ์ง ๋ชป ํ ๊ฒ์ด๋ค.) ์๋ฒฝํ์ง ์์ง๋ง, ๋ค๋ฅธ ํ ์ดํ๋ก์ ํธ๋ฅผ ๋ ์งํํ๋ฉด์ redux, redux-saga์ ๋น๋๊ธฐ ์ฒ๋ฆฌ ํจ์ ๋ฟ๋ง ์๋๋ผ js์ ๊ธฐ๋ณธ ๋ฌธ๋ฒ์์ ์ ๊ณตํ๋ ๋น๋๊ธฐ ์ฒ๋ฆฌ ํจ์์ ๋ํด ๊ณต๋ถํด์ผ๊ฒ ๋ค
ํ์คํ์ผ๋ก ๊ฐ์๋ฅผ ๋ค์ผ๋ฉฐ ๋ค์ ๋ง๋ค์ด๋ณด๋ฉด์, CORS ๋ฌธ์ ๋ฅผ ๋๋ฌด ๋ง์ด ๊ฒช์๋ค. ์๋ง์กด ์น ์๋น์ค์ ๋ฐฐํฌ๋ฅผ ํ๋ ๊ณผ์ ์์๋ ๋ฆฌ๋ ์ค ๋ช ๋ น์ด๋ฅผ ํตํด ๋ฆฌ๋ ์ค ์๋ฒ์์ ์๋กญ๊ฒ pull ์ ํด์ผ ํ๊ณ , axios๋ก ์ฌ์ฉ์ ์์ฒญ์ ๋ฐฑ์๋ ์๋ฒ์ ๋๊ฒจ์ค ๋๋ ์๋ชป ์ค์ ํ๋ค๋ฉด CORS ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ธฐ ๋ค๋ฐ์ฌ์๋ค. (์ปค๋ฐ ํธ์ฌ์ ํ์ ๋ฌดํ ๋ฐ๋ณต...) ์๋์ ์ผ๋ก ํ๋ก ํธ์์๋ ์ค์ ํด์ผํ ๊ฒ์ด ์ ์ง๋ง, ๋ด๊ฐ ์ฒ๋ฆฌํด ๋ณธ ์ฌํญ ์ธ์๋ ๋ฐ์ํ ์ค๋ฅ๋ค์ ๋๋นํด ์กฐ๊ธ ๋ ๋ฅํ ๊ณต๋ถ์ ํ์์ฑ์ ๋๊ผ๋ค.
CSS ํ๋ ์์ํฌ๋ ์ ๋ง ๋ ์ด ๋ ์ฑ๋ฐฐ์ด๋ค. ์ง๊ธ ๋ฆฌ์กํธ์ ๋ฅ์คํธ์์๋ ํจ์ํ ์ปดํฌ๋ํธ๋ฅผ ํตํด ์์ฑํ์ง๋ง, CSS FRAMEWORK์ ์์ ๋ค์ ํด๋์คํ ์ปดํฌ๋ํธ๊ฐ ์์ง๋ ๋ง์ด ์กด์ฌํ๋ค. ๋ค์ ๋ฐ๊ฟ์ ์ฌ์ฉํ๋ ๋ฒ๋ ๊ณต๋ถ๋ฅผ ํด์ผ ํ๊ณ , 'antd'์ ๊ธ๋ก๋ฒ ์คํ์ผ๋ง ๋ฑ ์ธ๋ถ์ ์ผ๋ก ์ ๊ทผํ์ฌ ์์์ ์คํ์ผ๋ง์ ์ฃผ๊ธฐ๊ฐ ๋งค์ฐ ๊น๋ค๋ก์ ๋ค. (๋ชจ๋ฐ์ผ ๋ฒ์ ์ ์ํด ๋ฏธ๋์ด ์ฟผ๋ฆฌ๋ฅผ ์ฐ๋ ๋ถ๋ถ์ ์์ง ๋งค์ฐ ๋ง์ด ๋ถ์กฑํ๋ฏ ํ๋ค...) ๊ฐํธํ๊ฒ ์ปดํฌ๋ํธ ui๋ฅผ ์ ๊ณต๋ฐ๋ ์ ์ฅ์ด๊ธฐ ๋๋ฌธ์ ํ๋ก ํธ ๊ฐ๋ฐ์ ์ ์ฅ์์๋ ๋์น๋ฉด ์๋๋ ๋ถ๋ถ์ธ ๊ฒ ๊ฐ๋ค.
์ฌํ๊น์ง์ ๊ณต๋ถ ๋ฐฉํฅ์ด javascript์ event Listener, DOM, BOM ์์ฃผ๋ก ๊ณต๋ถ๋ฅผ ํ๊ณ ๋ฆฌ์กํธ์์ ๋ฅ์คํธ๋ก ๋์ด์๋ค. ๊ทธ๋์ ์ธ์ง, ๋ฐฐ์ด์ ํ๋กํ ํ์ ํจ์๋ค(push, filter, concat, find) ๋ฑ ์ฐ๊ณ ์ถ์ ๋ ์ ๋๋ก ๋ชป์ฐ๋ ์ํฉ์ด ๋ฐ์ํ๋ค. ๋ด๊ฐ ์๊ฐํ๋ ํ๋ก ํธ ๊ฐ๋ฐ์์ ๊ฐ์ฅ ํฐ ๋๋ชฉ์ '๋จธ๋ฆฟ์์ผ๋ก ๊ตฌํํ ๋ก์ง์ ์ฝ๋๋ฅผ ํตํด ๋ํ๋ผ ์ ์๋๋'์ธ ๊ฒ ๊ฐ๋ค. react์ next๋ฅผ ํตํด ํ ์ดํ๋ก์ ํธ๋ฅผ ์งํํ๊ธฐ ์ ์, js์ ๋ํ ์ข ๋ ํํํ ์ค๋ ฅ์ ๊ฐ์ถ ํ์ ํ๋ ์์ํฌ๋ฅผ ๊ณต๋ถํ๋ค๋ฉด ํจ์ฌ ๋ ๊ฐ๊ฒฐํ๊ณ ๊ฐ๋ ์ฑ ์ข์ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ ๊ฒ์ด๋ค.
์ ๋ง ์ ์ฆ์ ์๋ง์กด ์น ์๋น์ค์ด๋ค. ํ๋ก ํธ๋ง ๊ฐ์ง๊ณ ํ๋ฐ๋๋ ๋ฉ๋ชจ๋ฆฌ๋ ์ข์ง๋ง, DB๋ฅผ ํฌํจํ์ฌ ์กฐ๊ธ ๋ ์์ฑ๋ ํ๋ก์ ํธ๋ฅผ ๋ค๋ฅธ ์ฌ๋๋ค์๊ฒ ๋ณด์ฌ์ฃผ๊ณ ์ถ์ด์ ์ด๋ป๊ฒ ๋๊น์ง ๋์ง ์๊ณ AWS๋ฅผ ํตํด ๋ฐฐํฌํ์๋ค. ๊ธฐ์กด ๊ฐ์๋ฅผ ๋ฐํ์ผ๋ก ๋ฐ๋ผ๋ง ์น๋๋ฐ๋, ๊ฐ์์๋ ๋ค๋ฅด๊ฒ ํฐ๋ฏธ๋์์ ๋๋ค๋ฅธ ์๋ฅ๋ค์ด ๋ฐ์ํ๊ธฐ๋ ํ์๊ณ , ๊ทธ ์ค๋ฅ๋ค์ ๊ตฌ๊ธ๋งํ๋๋ฐ๋ง ํด๋ ๋ฉฐ์น ์ ์์ ๋ถ์ ๊ฒ ๊ฐ์ง๋ง, ํด๋๊ณ ๋ณด๋ ๋งค์ฐ ๋ฟ๋ฏํ๋ค. ์ด๋ ๋ค ํ ๊ฒฐ๊ณผ๋ฌผ์ ๋ด ๋ณธ ์ ์์ด, ๊ณ์ ์ธํฐ๋ท ๊ฐ์๋ค์ course๋ค์ ํตํด์ ๊ณต๋ถํ๋ค๊ฐ ์ด๋ฒ์์ผ ๋ง๋ก ํฐ ํ๋ก์ ํธ๋ฅผ ๋ง๋ค๊ฒ ๋์๋ค. ํ๋ก ํธ์ ๋ฐฑ์๋๋ฅผ ๋ชจ๋ ๋ค๋ค ๋ณด๋ฉด์, ํ ๊ณณ์๋ง ํธํฅ๋ ์ ์๋ ์์ผ๋ฅผ ์ด๋์ ๋ ๋ฒ์ด๋ฌ๋ค๊ณ ์๊ฐํ๋ค. ๋ฐฑ์๋ ํ๋ก๊ทธ๋๋จธ์ ์ํตํ๊ธฐ ์ํด api ์ฒ๋ฆฌ๋ฅผ ์ด๋ป๊ฒ RestFulํ๊ฒ ์ฒ๋ฆฌํ ์ง, ๋๋ DB๋ฅผ ๋ง๋ค ๋ ์ด๋ค ๊ด๊ณ๋ก ๋ง๋ค์ด์ผ ํ๋ก ํธ๋ ๋ฐฑ์์ ์ํตํ ๋ ํธํ ์ง์ ๋ํ ๊ณ ๋ฏผ์ด ๋ง์ด ๋ค์ด ๊ฐ ๊ฒ ๊ฐ๋ค.
๋๋ฉ์ธ์ ์ฌ์ ์ฐ๊ฒฐํ๊ณ , lambda ์ฒ๋ฆฌ๋ฅผ ํตํด ์ด๋ฏธ์ง๋ฅผ ๋ฆฌ์ฌ์ด์งํ๊ณ , ํ๋ ฅ์ IP๋ฅผ ๋ฐ์์ ๋๋ฉ์ธ์ ์ฐ๊ฒฐํ๋ ์์ ๋ค์ด ๋๋ฌด ์๋กญ๊ณ ์ฌ๋ฐ์๋ค. ์ด๋ฐ ๋ค์ํ ๊ฒฝํ๋ค์ด ๋์ค์ ๋์ ๋ฐ์ ์ ํฐ ๋ฌด๊ธฐ๊ฐ ๋๋ฆฌ๋ผ ๋ฏฟ์ด ์์ฌ์น ์๋๋ค.