๋น์ ํผ์ UI ๋์์ธ์ด ๋ค ๊ฐ์ ์์ ๋ชจ๋ฌ์ด์๊ณ , ์ ์ญ์์ ์ํ๋ฅผ ๊ด๋ฆฌํ ์ ์๋ ํ๋ก์ฐ์ฌ์ redux-toolkit์ ๊ฒฝํํด๋ณด๊ณ ์ ๋์ ํ๋ ค๊ณ ํ์๋ค. ์๋๊น์ง ์์ฑ ํ์ UI ๋์์ธ์ด ์ ๋ฉด์ ์ผ๋ก ๋ฐ๋์ด์ ํ์ด์ง ๊ด๋ฆฌํ๋ ๋ฐฉํฅ์ผ๋ก ๋ณ๊ฒฝํ๊ฒ ๋์๋๋ฐ ๋น์ ์์ฑํ ๋ด์ฉ์ ๊ธฐ๋กํ๊ณ ์ดํ ์จ๋จน๊ธฐ ์ํด ์ ์ฅํ๋ค~
์ฌ์ฉํ๊ณ ์๋ Next.js ์คํ์ v14 app router, ๋ฃจํธ ๋๋ ํ ๋ฆฌ๋ src์ด๋ค.
src ๋ด์ ๋ค์๊ณผ ๊ฐ์ ํด๋, ํ์ผ์ ๋ง๋ค๊ณ ๊ทธ ์์ ํ์ํ slice๋ฅผ ์กฐ๊ฐ์กฐ๊ฐ ์์ฑํ๋ค.
// src/lib/slices/modalSlice.js
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
isOpen: false,
modalType: null,
modalProps: {},
};
const modalSlice = createSlice({
name: "modal",
initialState,
reducers: {
openModal: (state, action) => {
state.isOpen = true;
state.modalType = action.payload.modalType;
state.modalProps = action.payload.modalProps || {};
},
closeModal: (state) => {
state.isOpen = false;
state.modalType = null;
state.modalProps = {};
},
},
});
export const { openModal, closeModal } = modalSlice.actions;
export const selectModalState = (state) => state.modal;
export default modalSlice.reducer;
lib ํด๋์ store.js๋ฅผ ๋ง๋ค๊ณ ๊ทธ ์์ ๋ง๋ค์ด๋ ์ฌ๋ผ์ด์ค๋ฅผ ๋ฃ์ด์ค๋ค.
// src/lib/store.js
import { configureStore } from "@reduxjs/toolkit";
import modalSlice from "./slices/modalSlice";
export const store = configureStore({
reducer: {
modal:modalSlice
},
});
๋ค๋ฅธ ์ปดํฌ๋ํธ์ Provider๋ก ๊ฐ์ธ๊ณ export ํด์ค ๋ค์, ์ด๊ฑธ๋ก root์ layout๋ฅผ ๊ฐ์ธ์ค์ผํ๋ค.
Provider๋ฅผ ๋ฐ๋ก root layout์ ์ ์ฉ์ํค๋ ค๊ณ ํ๋ฉด client ์ด์ฉ๊ณ .. ๋ ๋๋ง ์๋ฌ๊ฐ ๋๋ค.
๊ทธ๋ฆฌ๊ณ ์๋ ์ฝ๋์์ ModalManager๋ผ๊ณ ์ปดํฌ๋ํธ๋ฅผ import ํด์๋๋ฐ, ์ด๊ฑด ๋ชจ๋ฌ ํ์ ์ ์ ์ํด์ ๋ชจ์๋ ์ปดํฌ๋ํธ์ด๋ค.
์์ธํ๊ฑด ์ ๋ผ๊ณ ์ผ๋จ ์จ์ค๋ค
// src/lib/Provider.js
"use client";
import { Provider } from "react-redux";
import { store } from "./store";
import ModalManager from "@/components/common/Modal";
export default function ReduxProvider({ children }) {
return (
<Provider store={store}>
{children}
<ModalManager />
</Provider>
);
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>
/* <๋ฐฐ๊ฒฝ ์ปดํฌ๋ํธ /> */
<ReduxProvider>
{children}
<ModalManager />
</ReduxProvider>
</body>
</html>
);
}
๋น์ ์๊ฐํ ๋ชจ๋ฌ์ 2๊ฐ์ง์๋ค
- ์ฐ์ปด ๋ชจ๋ฌ : ์ต์ด ๋ก๊ทธ์ธ ์์๋ง ๋ณด์ผ ๊ฒ, ์ฌ๋ผ์ด๋ ์์
- ์ด๋ฒคํธ ๋ฑ๋ก ๋ชจ๋ฌ
1) ์ฌ๊ฑด ๋ฑ๋ก ํผ
2) ์ํ ๋ฑ๋ก
2-1) ๊ฒ์์ฐฝ
2-2) ๊ฒ์์ฐฝ์์ ์ํ ์ ํ์ ์ํ ๋ฐ์ดํฐ form์ ์๋ ์ธํ
// src/components/common/Modal.js
"use client";
import { useSelector, useDispatch } from "react-redux";
import { closeModal, openModal, selectModalState } from "@/lib/slices/modalSlice";
import { useState } from "react";
import welcomeStep1 from "....";
import welcomeStep2 from "....";
import welcomeStep3 from "....";
import welcomeStep4 from "....";
const WelcomeModal = ({ onClose }) => {
const image = [welcomeStep1, welcomeStep2, welcomeStep3, welcomeStep4];
const content = ["A", "B", "C", "D"];
const [slidepage, setSlidePage] = useState(0);
const prevSlide = () => {
if (slidepage > 0) {
setSlidePage(slidepage - 1);
}
};
const nextSlide = () => {
if (slidepage < content.length - 1) {
setSlidePage(slidepage + 1);
}
};
return (
<div>
<div>
<div>
{slidepage > 0 && <button onClick={prevSlide}>์ด์ ์ฌ๋ผ์ด๋</button>}
{slidepage < content.length - 1 ? (
<button onClick={nextSlide}>๋ค์ ์ฌ๋ผ์ด๋</button>
) : (
<button onClick={onClose}>X</button>
)}
</div>
<h2>{`Step ${slidepage + 1}`}</h2>
<img src={image[slidepage].src} alt={`welcome modal image ${slidepage}`} />
<p>{content[slidepage]}</p>
</div>
</div>
);
};
์ฌ๊ธฐ์๋ ์ํ๋ ๋ชจ๋ฌ์ ๋ชจ์์ ๋ง๋ค์๋ค.
๋ ์ด๋ฐ ์์ผ๋ก ๋ง๋ค์๋๋ฐ ํ์ด์ง ๋๊ธฐ๋ ๋ฐฉ์์ ์ด์ ์ ์บ๋ฆฐ๋ ๋ง๋ค์๋ ๊ฑฐ๋ ๋น์ทํ๊ฒ ํ๋ค.
๋์ค์ ์ฌ๋ผ์ด๋์ฒ๋ผ ๋์ด๊ฐ๊ฒ ํ๊ณ ์ถ์๋ฐ ์ด ๋ถ๋ถ์ ํผ๋ธ๋ฆฌ์ฑํ๋ฉด์ ์์ ํ ์์ ์ด๊ณ ์ผ๋จ ๊ธฐ๋ฅ๋ง ๋์๊ฐ๊ฒ ํด๋๋ค.
์ ์ฝ๋์ฒ๋ผ ๋ชจ๋ฌ๋ค์ ์ธ๋ถ ๋ด์ฉ์ ์์ฑํ๊ณ , ์ด์ ๋ฐ์ ModalManager๋ผ๋ ํจ์๋ฅผ ์์ฑํ๋ค.
const ModalManager = () => {
const dispatch = useDispatch();
const { isOpen, modalType, modalProps } = useSelector(selectModalState);
if (!isOpen) return null;
const renderModalContent = () => {
switch (modalType) {
case "WELCOME_MODAL":
return <WelcomeModal {...modalProps} onClose={() => dispatch(closeModal())} />;
{/* ์ด์ธ์ case๋ค์ ์ถ๊ฐํ๋ฉด ๋๋ค */}
default:
return null;
}
};
return (
<div>
<div>{renderModalContent()}</div>
</div>
);
};
export default ModalManager;
renderModalContent ํจ์๋ฅผ ๋ณด๋ฉด, ์์์ modalSlice์์ ์์ฑํ๋ openModal๋ก ์ด ๋ ๋ชจ๋ฌ์ ๊ตฌ๋ถํด์ค ๋ค์ํ(modalType)์ ๋ฌ์์ค ๋ค์, ์ด ๋ชจ๋ฌ์ ์ฌ์ฉํ ํ์ด์ง์ ๊ฐ์ modalType์ ํค๋ก, ์ ๋ค์ํ์ ๊ฐ์ผ๋ก dispatch์ ํธ์ถํด์ฃผ๋ฉด ๋๋ค.
export default function Page() {
const dispatch = useDispatch();
const welcomeModalTester = () => {
dispatch(openModal({ modalType: "WELCOME_MODAL" }));
};
return (
<div>
<button onClick={welcomeModalTester}>์ด๊ฑด ์ฐ์ปด ๋ชจ๋ฌ</button>
</div>
)
};
์ ๋ ๊ฒ ์ ๋ฌํ๋ฉด modalType์ ํด๋นํ๋ ๋ชจ๋ฌ์ ๋์์ฃผ๊ฒ ๋๋ค
์ฌ๊ธฐ์๋ modalProps์ ๊ฐ์ ์ฃผ์ง ์์์ง๋ง ์ด๋ฒคํธ ๋ชจ๋ฌ์ ๋ค๋ฃจ๋ ๋ถ๋ถ์์ ์ฌ์ฉํ ์์ ์ด๋ค.
์์ง ๋ก๊ทธ์ธ ๊ด๋ จํด์ ํ์ํ ๋ถ๋ถ์ด ๋์ค์ง ์์์ ์ดํ์ ๋ก๊ทธ์ธ ๋ก์ง ์ถ๊ฐํ ๋ ์ฒ์์ ๋ก๊ทธ์ธ์ ์ฑ๊ณตํ๋ฉด localStorage์ ์ต์ด ๋ก๊ทธ์ธ ๊ธฐ๋ก์ ํ์ธํด์
1) ์์ผ๋ฉด ์ฐ์ปด ๋ชจ๋ฌ์ ๋ณด์ฌ์ฃผ๋ฉด์ ์ต์ด ๋ก๊ทธ์ธ ๊ธฐ๋ก์ ๋จ๊ธด๋ค (ํ ์ด๋ธ์ true/false๋ก ์ปฌ๋ผ์ ๋ง๋ค์ด์ผ ํ ๋ฏ)
2) ์์ผ๋ฉด 1์ ์ ๋ผ๊ณ ํ์ด์ง์ ๋ด์ฉ๋ง ๋ณด์ฌ์ค๋ค.
(+ ๊ทธ๋ฆฌ๊ณ ๋งํฌ ๋ณต์ฌํด์ ์ ์ ๊ฐ ์๋ ํ์ธ์๊ฒ ํ์ด์ง๋ฅผ ๋ณด์ฌ์ค ๋๋ view๋ง ํ์ฉํด์ผํ๋ค๋ ์ค์ )
'What I Learn' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
OSSCA: customize prompt & set a completionOptions in Continue (2) | 2024.10.02 |
---|---|
OSSCA : setting the Continue at vscode & Overview (0) | 2024.09.23 |
์ฌ์ฉ์ ์ ์ ์ ๋๋ฉ์ด์ ์ผ๋ก ๋ณ์ด ๋ฐ์งโจ๋ฐฐ๊ฒฝ ๋ง๋ค๊ธฐ (tailwindCSS) (1) | 2024.09.14 |
Make Calendar without Calendar library (@date-fns) (0) | 2024.09.13 |
Separate Next.js Root Layout from 404 Page Layout (0) | 2024.09.01 |