diff --git a/package.json b/package.json
index 043f07c..40feb01 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
     "next": "13.1.6",
     "react": "18.2.0",
     "react-dom": "18.2.0",
+    "react-toastify": "^9.1.1",
     "swr": "^2.0.3",
     "yup": "^1.0.0"
   },
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6d3cc25..56147d9 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -22,6 +22,7 @@ specifiers:
   prettier: 2.8.4
   react: 18.2.0
   react-dom: 18.2.0
+  react-toastify: ^9.1.1
   swr: ^2.0.3
   typescript: ^4.9.5
   yup: ^1.0.0
@@ -39,6 +40,7 @@ dependencies:
   next: 13.1.6_biqbaboplfbrettd7655fr4n2y
   react: 18.2.0
   react-dom: 18.2.0_react@18.2.0
+  react-toastify: 9.1.1_biqbaboplfbrettd7655fr4n2y
   swr: 2.0.3_react@18.2.0
   yup: 1.0.0
 
@@ -2638,6 +2640,17 @@ packages:
     resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
     dev: false
 
+  /react-toastify/9.1.1_biqbaboplfbrettd7655fr4n2y:
+    resolution: {integrity: sha512-pkFCla1z3ve045qvjEmn2xOJOy4ZciwRXm1oMPULVkELi5aJdHCN/FHnuqXq8IwGDLB7PPk2/J6uP9D8ejuiRw==}
+    peerDependencies:
+      react: '>=16'
+      react-dom: '>=16'
+    dependencies:
+      clsx: 1.2.1
+      react: 18.2.0
+      react-dom: 18.2.0_react@18.2.0
+    dev: false
+
   /react-transition-group/4.4.5_biqbaboplfbrettd7655fr4n2y:
     resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
     peerDependencies:
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index 3cd4df4..30cbcba 100644
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -4,6 +4,8 @@ import CssBaseline from '@mui/material/CssBaseline'
 import NextLink, { type LinkProps as NextLinkProps } from 'next/link'
 import { LinkProps } from '@mui/material/Link'
 import { ThemeProvider, createTheme } from '@mui/material'
+import { ToastContainer } from 'react-toastify'
+import 'react-toastify/dist/ReactToastify.css'
 import '@fontsource/roboto/300.css'
 import '@fontsource/roboto/400.css'
 import '@fontsource/roboto/500.css'
@@ -35,6 +37,15 @@ export default function App({ Component, pageProps }: AppProps) {
     <ThemeProvider theme={theme}>
       <CssBaseline />
       <Component {...pageProps} />
+      <ToastContainer
+        position="top-center"
+        autoClose={3000}
+        hideProgressBar
+        newestOnTop={false}
+        closeOnClick
+        pauseOnFocusLoss={false}
+        pauseOnHover
+      />
     </ThemeProvider>
   )
 }
diff --git a/src/utils/axios.ts b/src/utils/axios.ts
index be6639c..f65d575 100644
--- a/src/utils/axios.ts
+++ b/src/utils/axios.ts
@@ -2,6 +2,19 @@ import axios, { type AxiosError } from 'axios'
 import Router from 'next/router'
 import status from 'http-status'
 import * as api from '@/api'
+import { toast } from 'react-toastify'
+
+interface ErrorResponse {
+  statusCode: number
+  message: string
+}
+
+const throwError = (error: AxiosError<ErrorResponse>) => {
+  toast.error(
+    error.response?.data.message.toString() ?? error.response?.statusText,
+  )
+  return Promise.reject(error)
+}
 
 axios.interceptors.request.use(function (config) {
   const accessToken = localStorage.getItem('accessToken')
@@ -13,7 +26,7 @@ axios.interceptors.response.use(
   function (response) {
     return response
   },
-  async function (error: AxiosError) {
+  async function (error: AxiosError<ErrorResponse>) {
     // fail to refresh token
     if (
       error.config &&
@@ -21,7 +34,7 @@ axios.interceptors.response.use(
       error.config.method === 'put'
     ) {
       Router.push('/login')
-      return Promise.reject(error)
+      return throwError(error)
     }
     switch (error.response?.status) {
       case status.UNAUTHORIZED: {
@@ -36,7 +49,7 @@ axios.interceptors.response.use(
         return error.config && axios.request(error.config)
       }
       default: {
-        return Promise.reject(error)
+        return throwError(error)
       }
     }
   },