๋ค๋ฅธ ํ์ ๋นํด ๋ณผ๋ฅจ์ ๋ฐฐ๋ก ํฐ ์ํฉ์ด์์ง๋ง ์ ์ผ ์ฒ์ฒํ ์ง๋๋ฅผ ๋๊ฐ๋ค. ํ ์ ์๋ ๊ณ ๋ฏผ์ ๋ค ํ๊ณ ์์ํ์ง๋ง, ๊ทธ๋๋ ๊ฐ๋ฐ์ ํ๋ค๋ณด๋ฉด ์ด๊ธ๋๋๊ฒ ์๊ธด๋ค. ์ด๋ฒ ๊ธ์ ๋ฑ
ํค์ฆ ํ๋ก์ ํธ์ ์ด๊ธฐ ์ธํ
์ ๋ํ ๊ธฐ๋ก. ํ ํ๋ก์ ํธ ์ง์ ์ ๋ง์ง๋ง์ผ๋ก ํ๋ ํ ํ๋ ์คํฐ๋์์ ์ฌ์ฉํ๋ ์ฝ๋์, ์ด์ ์ ๊ณ ํฐ์ผ ํ์ ์์ ์ฌ์ฉํ๋ ์คํ๋ค์ด ๋ง์ ๋์์ด ๋์๋ค. ์ฌ๊ธฐ์ ์ธํ
ํ๋ฉด์ ๋ฐฐ์ด ๊ฒ๋ค์ ์ด์ ๋ง ์์ํ ๋๋ฒ์งธ ๊ณ ํฐ์ผ ํ๋ก์ ํธ์์ ๋ ์๊ธดํ ์ฌ์ฉํ ์์ .
๋ค์์ ์ด ๊ธ์์ ์ธ๊ธํ ๋ด์ฉ๋ค์ด๋ค.
- ๋ฆฌ๋์ค ํดํท
- ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ
- ๊นํ๋ธ ์ก์ ๊ณผ ๋์ปค๋ฅผ ์ด์ฉํ ์๋ ๋ฐฐํฌ ์ธํ
1. ๋ฆฌ๋์ค ํดํท
์ํ๊ด๋ฆฌ๋ฅผ ์ํด ์ด๋ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ ์ง์ ๋ํ ์ด์ผ๊ธฐ๊ฐ ๋ง์๋ค. ๋ฆฌ๋์ค ๋ตํฌ, ๋ฆฌ์ฝ์ผ ๋ฑ๋ฑ ๊ณ ๋ คํ์ง๋ง RTK๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ๊ฒฐ์ . ์ฌ์ค ๋ช์ฃผ์ ์ ํ๋ธ์์ ๋ณธ ์ฐ์ํ ์ ํ๋ธ ์ด์ฉ๊ตฌ์์ ๋ฆฌ์ฝ์ผ๊ณผ ๋ฆฌ์กํธ ์ฟผ๋ฆฌ๋ฅผ ์ด์ฉํด ์๋ฒ ์ํ์ ํด๋ผ์ด์ธํธ ์ํ๋ฅผ ๋ถ๋ฆฌํ๋ค๋ ์์์ ๋ณด๊ณ ๋์, ๋ฆฌ์ฝ์ผ์ ํ๋ฒ ์จ๋ณด๊ณ ์ถ์๋ค. ์ค์ ๋ก ๋งค์ฃผ ์งํํ๋ ๋์๋ฆฌ ์คํฐ๋ ๊ณผ์ ์ ๋ฆฌ์ฝ์ผ์ ๋์ ํด์ ์ฌ์ฉํด๋ณด์๋ค. ๋ฌธ์๋ณด๊ณ ๊ธฐ์ด์ ์ผ๋ก ๋ฐ๋ผํ๋ ์ ๋์ ๊ทธ์ณค์ง๋ง ๊ฝค ํธํ๋ค. ํ์ง๋ง ํ์๊ณผ ์ด์ผ๊ธฐํ ๊ฒฐ๊ณผ, ์์ง ํ์ ์์ ๋ ๋ง์ด ์ฌ์ฉํ๋ RTK๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ํ๋ค. RTK๋ ์ฒ์ ์ฌ์ฉํด๋ณด์ง๋ง ์์ ์ฝ๋๋ฅผ ๋ณด๋ ์๋ ์ฐ๋ ๋ฆฌ๋์ค์ ๋ฌ๋ฆฌ ๊ฝค ๊ฐ๊ฒฐํด์ง๊ณ ์ง๊ด์ ์ด์ด์ ์ข์๋ค.
createSlice
export const challengePayloadSlice = createSlice({
name: 'challengePayload',
initialState,
reducers: {
dispatchParent(state, action: PayloadAction<boolean>) {
state.challenge.isMom = action.payload;
},
/* ...์๋ต */
},
extraReducers: (builder) => {
builder
.addCase(postChallenge.pending, (state) => {
state.status = 'loading';
})
.addCase(postChallenge.fulfilled, (state, action) => {
state.status = 'succeeded';
state.response = action.payload;
})
.addCase(postChallenge.rejected, (state, action) => {
state.status = 'failed';
console.log(action.payload);
});
},
RTK์ ํต์ฌ์ด๋ค. ๊ธฐ์กด์ ๊ทธ๋ฅ ๋ฆฌ๋์ค๋ง์ ์ฌ์ฉํ ๋ ์ก์
์ type, ์์ฑ ํจ์, ๋ฆฌ๋์ ๋ฑ์ ์ง์ ์์ฑํด์ผ ํ๋๋ฐ, createSlice
๋ฅผ ์ด์ฉํด ์ด๋ฅผ ํ๋ฐฉ์ ํด๊ฒฐํ ์ ์๋ค. (๋ด๊ฐ ๋ด๋ ์์ํ ๋ค์ด๋ฐ์ ๊ณง ์์ ํ ์์ ..) ๋จ์ํ ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ๋ฆฌ๋์ ํจ์๋ค์ slice ๋ด๋ถ์ reducers์์ ์์ฑํ ์ ์๋ค. extraReducers๋ ๋ฐ์์ ์ณ๋ค๋ณด๊ฒ ์.
export const {
dispatchParent,
dispatchItemName,
...
} = challengePayloadSlice.actions;
์ด๋ ๊ฒ export ํ๋ฉด ์ธ๋ถ ์ปดํฌ๋ํธ์์ dispatch(dispatchParent);
์ ๊ฐ์ด ์ฌ์ฉํด ์ก์
์ ์คํ ํ ์ ์๋ค.
createAsyncThunk
๊ธฐ๋ณธ์ ์ผ๋ก redux thunk๋ฅผ ๋ด์ฅํ๊ณ ์๋ค.
// POST: ํ๋กํ ์ ๋ณด๊ฐ ์๋ ํ์์ ๋ํด ์
๋ ฅ๋ฐ์ ํ๋กํ ์ ๋ณด ์ ์ก
export const postChallenge = createAsyncThunk(
'challengePayload/postChallenge',
async (axiosPrivate: AxiosInstance, { getState, rejectWithValue }) => {
try {
const { challengePayload } = getState() as RootState;
const response = await axiosPrivate.post(
'/challenge',
challengePayload.challenge,
);
return response.data;
} catch (err) {
if (axios.isAxiosError(err)) {
return rejectWithValue(err);
}
}
},
);
์์๋ก, ์ฑ๋ฆฐ์ง๋ฅผ ๋ฑ๋กํ๋ ๋ด์ฉ์ ์ฝ๋์ด๋ค. rtk์์ ์ ๊ณตํ๋ createAsyncThunk
๋ฅผ ํตํด ๋น๋๊ธฐ ์ก์
์ ๋ง๋ค๊ณ ๋ฆฌ๋์์ ๋ฑ๋กํ ์ ์๋ค. ์ก์
ํ์
๋ฌธ์์ด๊ณผ ํ๋ก๋ฏธ์ค๋ฅผ ๋ฐํํ๋ ์ฝ๋ฐฑ ํจ์๋ฅผ ์ธ์๋ก ๋ฐ์์ ์ฃผ์ด์ง ์ก์
ํ์
์ ์ ๋์ด๋ก ์ฌ์ฉํ๋ ํ๋ก๋ฏธ์ค ์๋ช
์ฃผ๊ธฐ ๊ธฐ๋ฐ์ ์ก์
ํ์
์ ์์ฑํฉ๋๋ค. ๋ผ๊ณ ํจ.
์๊น ์์์ slice์ ์ฝ๋๋ฅผ ๋ณด๋ฉด extraReducers ๋ถ๋ถ์ด ์๋ค. ํ๋ก๋ฏธ์ค๊ฐ ๋ฐํํ๋ pending, fulfilled, rejected์ ๋ฐ๋ฅธ ์ก์
์ ๋ฑ๋กํ ์ ์๋ค. ์ฌ๊ธฐ์ ์ค์ํ๊ฒ. Thunk ์์์ try-catch๋ก ์๋ฌ๋ฅผ ๋ฐ๋ก ์ก์๋ฒ๋ฆฌ๋ฉด ์ก์
์ ๋ฌด์กฐ๊ฑด fulfilled๋ก ๋์ด๊ฐ๋ค. (๋ผ๊ณ ๊ฒฝํํ๋๋ฐ, ๋ฌธ์์๋ ์คํจํ ์์ฒญ์ด๋ thunk ์ค๋ฅ๋ rejected ํ๋ก๋ฏธ์ค๋ฅผ ๋ฐํํ์ง ์๋๋ค. dispatch์ result๋ฅผ ์ฌ์ฉํ์ง ์์ ๋ uncaught ๋๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํด์ ๋ผ๊ณ ์จ์์. ์กฐ๊ธ ๋ ๊ณต๋ถ๊ฐ ํ์ํ๋ค).
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ rejectWithValue
๋ฅผ ์ด์ฉํ๊ฑฐ๋, ์๋๋ฉด .unwrap()
๋ฅผ ์ด์ฉํ๊ฑฐ๋. ๋๊ฐ์ง ๊ฒฝ์ฐ์ ๋ํด์ ๋ณด๊ฒ ๋ค.
ํ๋ก๋ฏธ์ค๋ฅผ ๋ฐํํ๋ ์ฝ๋ฐฑํจ์์ ๋๋ฒ์งธ ์ธ์๋ก ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ ๊ณตํ๋ thunkApi๊ฐ ๋ค์ด๊ฐ๋ค. thunkApi.rejectWithValue
๋ฅผ ์ด์ฉํ๋ฉด ์๋ฌ๋ฅผ ์ก์์ ์ ์์ ์ผ๋ก rejected๋ก ๋ณด๋ธ๋ค. ์ปดํฌ๋ํธ์์๋ ์คํ ์ด์์ status๋ฅผ ๋ฐ์์ ์ํ์ ๋ฐ๋ผ ๋ ๋๋ง์ ๋ค๋ฅด๊ฒ ํ๋ค๋ ์ง ๋ฑ์ ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๋ค. ๋ฌธ์
๋๋ฒ์งธ๋ก ์๋ฌ๋ฅผ ์ก์ง ์๊ณ ๋ณด๋ธ ํ์ .unwrap()
๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด ์๋ค. ๋ฐํ๋๋ ํ๋ก๋ฏธ์ค๋ฅผ ์ง์ ๊น์ฃผ๋๊ฑด๊ฐ๋ด. ๋ฌธ์
useEffect(() => {
async function processLogin() {
try {
await dispatch(login({ code })).unwrap();
navigate('/');
} catch (error: any) {
console.error(error.message);
}
}
processLogin();
}, []);
์ด๋ฐ ์์ผ๋ก dispatch ์ดํ์ ๋ฐ๋ก ์๋ฌ๋ฅผ ํ์ธํ๋ค. ๊ทธ๊ฑธ ์บ์นํด์ ์ฒ๋ฆฌํ ์ ์์. ๋ฌธ์์๋ unwrap์ thunk์ ๊ฒฐ๊ณผ๋ฅผ ํธ๋ค๋ง, rejectWithValue๋ ์๋ฌ๋ฅผ ํธ๋ค๋งํ๋ค๊ณ ์ฐ์ฌ์์. ๋ ์ค์ ์ด๋ค๊ฒ ๋ ์ข์ ๋ฐฉ๋ฒ์ธ์ง๋ ์กฐ๊ธ ๋ ๊ณ ๋ฏผํด ๋ณด๊ณ ์ฌ์ฉํ ํ์๊ฐ ์๊ฒ ๋ค.
2. ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ
์ด๊ธฐ์ ์ธํ ํ๋ ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ๋ค.
โโโ .github # PRํฌํ๋ฆฟ, ์ก์
์ํฌํ๋ก์ฐ ๊ด๋ จ
โโโ .storybook # ์คํ ๋ฆฌ๋ถ ์ธํ
โโโ public
โโโ src
โ โโโ assets # ์์ด์ฝ, ์ด๋ฏธ์ง, ํฐํธ ํ์ผ ๋ฑ
โ โโโ components # ์ปดํฌ๋ํธ ๊ด๋ จ ํ์ผ
โ โ โโโ common
โ โ โโโ [...]
โ โโโ hooks # ์ปค์คํ
ํ
โ โ โโโ api
โ โ โโโ common
โ โ โโโ [...]
โ โโโ lib
โ โ โโโ api # axios ์ค์
โ โ โโโ constants # ๋๋ฉ์ธ, ํค ๋ฑ์ ์์
โ โ โโโ styles # GlobalStyle, ThemeProvider ๊ด๋ จ
โ โ โโโ types # type, interface ๊ด๋ จ
โ โ โโโ utils # ์ ํธ ํจ์ ๊ด๋ จ
โ โโโ pages # ํ์ด์ง ๊ด๋ จ ํ์ผ
โ โโโ store
โ โ โโโ app # store ์ธํ
โ โ โโโ slices # RTK slice ํ์ผ ์์ฑ
โ โโโ App.tsx
โ โโโ index.tsx
โโโ ๊ฐ์ข
์ธํ
ํ์ผ๋ค๊ณผ ๋ฆฌ๋๋ฏธ ํ์ผ
ํฌ๊ฒ ๊ตฌ์ฑํ ๋ ์ด๋ ต์ง ์์์ง๋ง, ํ์ ๋๋ ํ ๋ฆฌ ๊ตฌ์ฑ์ด ์ด๋ ค์ ๋ค. assets๋ค์ ์ด๋ป๊ฒ ๋ถ๋ฅํ ์ง, ์ปดํฌ๋ํธ๋ค.. ํ์
๋ค์ ์ด๋ป๊ฒ ๊ด๋ฆฌํ ์ง ๋ฑ๋ฑ. ๊ฐ๋ฐ์ ํ๋ ์ค๊ฐ์๋ ์๋ฒ์ฉ ๋ฐ๊ฟจ๋ ๊ฒฝํ์ด ์๋ค. ์ ๋ฆฌ๋ ํ์ํ์ง๋ง ๋์ค๊ฐ ๋๋ฌด ๊น์ด์ง๋๊ฑธ ๋ณ๋ก ์์ข์ํด์ ๋จธ๋ฆฌ๊ฐ ์ํ ๋ค. ๊ทธ๋๋ ๊ฝค ๋ณด๊ธฐ์ข๊ฒ ์ ๋ฆฌ ๋ ๊ฒ ๊ฐ๋ค.
3. ๋์ปค ์๋ ๋ฐฐํฌ ์ธํ
์ด์ ๊น์ง ๋ฐ๋ธ์ต์ค์ ๊ดํด์ ์์ ํ ๋ฌธ์ธํ์ด์๋ค. ๋ค๋ฅธ ๋๊ตฐ๊ฐ๊ฐ ์ธํ ํด์ค๋๋ก ์ฐ๊ฑฐ๋, ๋งค๋ฒ ์ง์ ๋ฐฐํฌ๋ฅผ ํ๊ฑฐ๋. ์ด๋ฒ์ ์ง์ CICD ์ธํ ์ ํด๋ณด๊ณ ์ถ์ ์์ฌ์ด ์์๋ค. ๊ฐ๋ฐ ์์ฒด์ ์๊ฐ๋ ๋ถ์กฑํ๋ฐ ๋งค๋ฒ ๋ฐฐํฌ์ ํ ์คํธ๋ฅผ ์ํด ์๊ฐ์ ์ฐ๋๊ฒ๋ ์๋ ์ ์๋๋ก.
[React] Docker + Nginx + Github Actions ํ๋ก์ ํธ ๋ฐฐํฌํ๊ธฐ (1)
๋ฆฌ์กํธ ํ๋ก์ ํธ๋ฅผ ๊นํ๋ธ ์ก์ ์ ํตํด ๋์ปค ํ๋ธ์ ํธ์ฌํ๋ ๊ณผ์ ์ด๋ค. Docker, Nginx, Github Actions๋ฅผ ์ด์ฉํ๋ค.
[React] Docker + Nginx + Github Actions ํ๋ก์ ํธ ๋ฐฐํฌํ๊ธฐ (2)
deploy ๋ ํฌ์งํ ๋ฆฌ์ ์ก์ ์ด ํธ๋ฆฌ๊ฑฐ๋๋ฉด ๋์ปค ์ปดํฌ์ฆ๋ฅผ ์ด์ฉํด ๋ฐฑ์๋, ํด๋ผ์ด์ธํธ, nginx ์ธ๊ฐ์ ์ด๋ฏธ์ง๋ฅผ ๋ฐ์์ ์คํํ๋ค.
์์ธํ ๊ธฐ๋ก์ ์์ ๊ธฐ๋ก์ผ๋ก.
4. ์ ๋ ๊ฒฝ๋ก ์ค์
ํ๋ก์ ํธ๊ฐ ๊ท๋ชจ๊ฐ ์ปค์ง๊ณ ๋์ค๊ฐ ๊น์ด์ง๋ฉด์ ํ์ผ์ import ํด์ฌ๋๋ง๋ค ๋ฒ๊ฑฐ๋ก์์ก๋ค. ํ์ ์คํฌ๋ฆฝํธ ๋ฆฌ์กํธ ํ๊ฒฝ์์ ์ ๋๊ฒฝ๋ก๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก ์ธํ ์ ํด๋ณด์๋ค.
[React] ์ ๋๊ฒฝ๋ก ์ค์ ํ๊ธฐ with TS, Storybook, CRA, Craco
Craco๋ฅผ ํตํด webpack config๋ฅผ ๊ฑด๋๋ฆด ์ ์๋ค.
์ถ๊ฐ ์ฝ์ง
craco๋ฅผ ์ฌ์ฉํ๋ฉด์ npm์ด ์๋ yarn์ ์ด์ฉํด ๋น๋๋ฅผ ํด์ผํ๋๋ฐ ์๊พธ๋ง ๋น๋ ์๋ฌ๊ฐ ๋ฌ๋ค.
Package.lock.json ๋์ yarn.lock๋ฅผ ๊ฐ์ ธ์ค๊ณ ,npm ci
๋์ yarn install --frozen-lockfile
์ ์คํํ๋ค.
๊ทธ ์ด์ ์ npm install yarn --global --force
๋ก yarn์ ์ค์นํด์ค.
์๊พธ yarn์ ์ค์นํ๋๋ฐ์์ ์ค๋ฅ๊ฐ ๋ฌ๋ค. ์ด๋ฏธ yarn์ด ๊น๋ ค์๋๋ฐ overwrite๋๋ค๊ณ ํจ. ๊ทผ๋ฐ ๊ทธ๋ ๋ค๊ณ ์๊น๋ฉด ์ปค๋งจ๋ ์คํ์๋๋ค๊ตฌ ์ด์ฉ๊ตฌ... ๋ฒ ์ด์ค๋ก ๊ฐ์ ธ์ค๋ node์ ๋ฒ์ ์ด ๋ฌธ์ ์๋ค. 14๋ฒ์ ์ด์๋๋ฐ 16์ผ๋ก ์ฌ๋ ค์ฃผ๋๊น ํด๊ฒฐ. ์ฐธ๊ณ ํ๋ ํ์ผ์์ ์คํํ๋ ์ปค๋งจ๋๋ค์ ์์ ํ ์๊ฐ์ ํ๋๋ฐ, ๋งจ ์์ค์ ๊ฑด๋๋ฆด ์๊ฐ์ ๋ชปํ๋ค. ๋ฉ์ถฉ๋ฉ์ถฉ. ์ง๋ฌธํ๋ ์น๊ตฌ์๊ฒ "๋ ์ ๋ ๊ฒฝํ์ด๋ฉด ์ด์ ๋๋ ํ ์ค ์์์ผ ํ๋ค.. ๋๋ ์ด์ ๋
๋ฆฝํด์ผ ๋ ๋๋ค.." ๋ผ๋ ์ด์๋ฆฌ ๋ค์๋ค.