현재 열려있는 브라우저 페이지에 또 다른 브라우저 페이지를 띄우는 것
일반 팝업처럼 새로운 윈도우를 여는 것이 아닌 현재 윈도우의 레이어를 쌓아서 보여주는 형식의 팝업을 의미
공통점
차이점
import React from "react";
import "./Modal.css";
const Modal = ({ isOpen, onClose, type = "A", children, onConfirm }) => {
if (!isOpen) return null; // 모달이 열리지 않은 경우 렌더링하지 않음
return (
<div className="modal-overlay" >
<div className="modal-content" >
{type === "A" && (
<>
<div className="div-alert">Alert</div>
<p>{children}</p>
<button className="btn-ok" onClick={() => { onConfirm?.(); onClose(); }}>OK</button>
</>
)}
{type === "C" && (
<>
<div className="div-confirm">Confirm</div>
<p>{children}</p>
<button className="btn-ok" onClick={() => { onConfirm?.(); onClose(); }}>Yes</button>
<button className="btn-cancle" onClick={onClose}>No</button>
</>
)}
{type === "E" && (
<>
<div className="div-error">Error</div>
<p>{children}</p>
<button className="btn-cancle" onClick={() => { onConfirm?.(); onClose(); }}>OK</button>
</>
)}
</div>
</div>
);
};
export default Modal;
Modal.css
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal-content {
background: white;
border-radius: 8px;
max-width: 500px;
width: 100%;
position: relative;
text-align: center;
}
.modal-content .div-alert {
border-radius: 5px;
background: gainsboro;
}
.modal-content .div-confirm {
border-radius: 5px;
background: gainsboro;
}
.modal-content .div-error {
border-radius: 5px;
background: red;
}
.modal-content p {
padding: 20px;
margin: 10px 0 20px;
}
.modal-content button {
margin: 5px;
padding: 8px 15px;
border: none;
border-radius: 5px;
cursor: pointer;
}
.modal-content .btn-ok {
background-color: #0800ff;
color: white;
}
.modal-content .btn-cancle {
background-color: #f44336;
color: white;
}
import React, { createContext, useContext, useState } from "react";
import Modal from "../components/modal/Modal";
const ModalContext = createContext();
let globalOpenModal = null; // 전역 모달 호출 함수
export const ModalProvider = ({ children }) => {
const [isModalOpen, setIsModalOpen] = useState(false);
const [modalType, setModalType] = useState(null); // alert, confirm, error 등
const [modalContent, setModalContent] = useState(null);
const [onConfirm, setOnConfirm] = useState(null); // confirm 모달용 콜백
const openModal = (type, content, onConfirmCallback) => {
setModalType(type);
setModalContent(content);
setOnConfirm(() => onConfirmCallback || null);
setIsModalOpen(true);
};
const closeModal = () => {
setIsModalOpen(false);
setModalType(null);
setModalContent(null);
setOnConfirm(null);
};
// 전역에서 사용할 수 있도록 설정
globalOpenModal = openModal;
//prop으로 { openModal, closeModal } 객체를 전달하여 하위 컴포넌트에서 이 값을 사용할 수 있게 한다
return (
<ModalContext.Provider value={{ openModal, closeModal }}>
{children}
{/* 모달 렌더링 */}
<Modal
isOpen={isModalOpen}
onClose={closeModal}
type={modalType}
onConfirm={onConfirm}
>
{modalContent}
</Modal>
</ModalContext.Provider>
);
};
//useModal은 커스텀 Hook으로, useContext를 사용하여 ModalContext를 가져온다. 이를 통해 하위 컴포넌트에서 Modal관련 정보에 쉽게 접근할 수 있다
export const useModal = () => useContext(ModalContext);
// 전역 모달 호출 함수
export const getGlobalModal = () => globalOpenModal;
import React from 'react';
import { BrowserRouter, Route, Routes, Navigate } from 'react-router-dom';
import CombinedProvider from './context/CombinedProvider';
import MainPage from './pages/MainPage';
function App() {
return (
<CombinedProvider>
<BrowserRouter>
<Routes>
<Route path="/main" element={<MainPage />} />
</Routes>
</BrowserRouter>
</CombinedProvider>
);
}
export default App;
생성한 Context를 애플리케이션의 최상위 컴포넌트로 설정하여 하위 컴포넌트가 Modal관련 정보에 쉽게 접근하고 사용할 수 있음
Context 여러개를 구현한 후 한 개의 Provider로 관리한다.
import React from "react";
import { AuthProvider } from "./AuthContext";
import { ModalProvider } from "./ModalContext";
const CombinedProvider = ({ children }) => {
return (
<ModalProvider>
<AuthProvider>
{children}
</AuthProvider>
</ModalProvider>
);
};
export default CombinedProvider;
import axios from 'axios';
import { getGlobalModal } from '../context/ModalContext';
// Axios 인스턴스 생성
const axiosInstance = axios.create({
...
});
// 요청 인터셉터
axiosInstance.interceptors.request.use(
...
);
// 공통 요청 함수
const axiosRequest = async (method, url, data = null, fn_callback, bResponseOnlyData = true) => {
try {
...
} catch (error) {
fn_axiosError(error);
}
};
const fn_axiosError = async(error) => {
const openModal = getGlobalModal();
const originalRequest = error.config;
// 기타 에러 처리
console.log(error);
openModal("E", `${error.message}`);
}
export default axiosRequest; // 기본 익스포트 사용
Loading Modal은 화면에서 API 요청을 보낸 이후 API 요청을 받기 전까지 화면에서 동작하지 못하도록 사용자를 제어할 수 있다.
React-Spinners 라이브러리를 사용하여 쉽게 구현할 수 있다.
npm install react-spinners
https://www.davidhu.io/react-spinners/
React Spinners
yarn add react-spinners
www.davidhu.io
원하는 모양을 선택
import React from 'react';
import "./LoadingModal.css";
import { RingLoader } from "react-spinners";
const LoadingModal = () => (
<div className="modal-overlay">
<RingLoader color="#36D7B7"
size={50}
/>
</div>
);
export default LoadingModal;
LoadingModal.css
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
import React, { createContext, useContext, useState } from "react";
import Modal from "../components/modal/Modal";
import LoadingModal from "../components/modal/LoadingModal";
const ModalContext = createContext();
let globalOpenModal = null; // 전역 모달 호출 함수
let globalLoadingModal = null; // 전역 로딩 모달 호출 함수
export const ModalProvider = ({ children }) => {
...
const [isLoadingVisible, setIsLoadingVisible] = useState(false); // 로딩 모달 상태
const openModal = (type, content, onConfirmCallback) => {
...
};
const closeModal = () => {
...
};
const applyLoadingModal = (isOpen) => {
setIsLoadingVisible(isOpen);
};
// 전역에서 사용할 수 있도록 설정
globalOpenModal = openModal;
globalLoadingModal = applyLoadingModal;
return (
<ModalContext.Provider value={{ openModal, closeModal }}>
...
{/* 로딩 모달 렌더링 */}
{isLoadingVisible && <LoadingModal />}
</ModalContext.Provider>
);
};
export const useModal = () => useContext(ModalContext);
// 전역 모달 호출 함수
export const getGlobalModal = () => globalOpenModal;
// 전역 로딩 모달 제어 함수
export const getGlobalLoadingModal = () => globalLoadingModal;
import { getGlobalModal, getGlobalLoadingModal } from '../context/ModalContext';
...
// 공통 요청 함수
const axiosRequest = async (method, url, data = null, fn_callback, bResponseOnlyData = true) => {
const loadingModal = getGlobalLoadingModal();
loadingModal(true);
try {
...
} catch (error) {
fn_axiosError(error);
}
loadingModal(false);
};
const fn_axiosError = async(error) => {
const loadingModal = getGlobalLoadingModal();
...
loadingModal(false);
}
api 호출 전후로 loadingModal 관리 함수 호출로직 추가
Alert Modal과 Loading Modal을 구현하였다. 이제부터는 javascript에서 제공하는 alert가 아닌 개발한 Modal을 사용도록 하자.
이전글
다음 글
React 로그인 구현 (JWT) (2) | 2025.02.09 |
---|---|
React axios 모듈화 (0) | 2025.02.07 |
React-Router-Dom 정리 (0) | 2025.02.06 |
React Hook 기본 정리 / Custom Hook (1) | 2025.02.05 |
React CORS 설정 - 프록시 (http-proxy-middleware) (7) | 2025.01.31 |
댓글 영역