diff --git a/src/app/board/[id]/page.tsx b/src/app/board/[id]/page.tsx
index be0fbf9..9bb2063 100644
--- a/src/app/board/[id]/page.tsx
+++ b/src/app/board/[id]/page.tsx
@@ -16,6 +16,7 @@ import CommentList from '@/components/board/CommentList'
import { useUserInfo } from '@/service/user/useUserService'
import { useQueryClient } from '@tanstack/react-query'
import { queryKeys } from '@/service/board/BoardQueries'
+import { useToast } from '@/hooks/useToast'
const BoardDetail = () => {
const { id } = useParams()
@@ -24,6 +25,8 @@ const BoardDetail = () => {
const queryClient = useQueryClient()
+ const { showToast } = useToast()
+
const { data: userInfo } = useUserInfo()
const { data: boardDetail } = useBoardDetail(boardId)
const { mutate: postBoardLike } = usePostBoardLike()
@@ -44,7 +47,12 @@ const BoardDetail = () => {
}, [boardDetail])
const handleLikeToggle = () => {
+ if (!userInfo) {
+ showToast('로그인이 필요한 서비스입니다')
+ return
+ }
if (userInfo && userInfo.id === boardDetail?.memberSimpleInfo.id) {
+ showToast('본인 게시글에는 좋아요를 누를 수 없습니다')
return
}
postBoardLike(boardId, {
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 93b7008..84debe5 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,9 +1,10 @@
+import dynamic from 'next/dynamic'
import type { Metadata } from 'next'
import '../styles/globals.css'
import Recoil from '@/recoil/Recoil'
import localFont from 'next/font/local'
-import dynamic from 'next/dynamic'
import ReactQueryProviders from '@/hooks/useReactQuery'
+import Toaster from '@/components/common/Toaster'
const pretendard = localFont({
src: '../../public/fonts/PretendardVariable.woff2',
@@ -42,6 +43,7 @@ export default function RootLayout({
{children}
+
diff --git a/src/app/signin/info/page.tsx b/src/app/signin/info/page.tsx
index 5e3214b..aff0f3b 100644
--- a/src/app/signin/info/page.tsx
+++ b/src/app/signin/info/page.tsx
@@ -59,12 +59,6 @@ const Info = () => {
/>
-
-
- 당신의 이메일은 {email}입니다.
-
-
-
당신의 MBTI는 무엇인가요?
diff --git a/src/components/auth/Login.stories.tsx b/src/components/auth/Login.stories.tsx
index 52bbba6..ad3c176 100644
--- a/src/components/auth/Login.stories.tsx
+++ b/src/components/auth/Login.stories.tsx
@@ -1,18 +1,19 @@
-import Login from '@/components/auth/Login'
-import { User } from '@/model/User'
+import Login, { LoginProps } from '@/components/auth/Login'
import { Meta, StoryFn } from '@storybook/react'
export default {
title: 'Auth/Login',
component: Login,
-} as Meta
+} as Meta
-const Template: StoryFn = (args: User) =>
+const Template: StoryFn = (args: LoginProps) =>
export const Primary = Template.bind({})
Primary.args = {
- profileImgUrl: '/images/common/default.svg',
- nickName: '유보라',
- mbti: 'ENFP',
- badge: '엠비티어른',
+ user: {
+ profileImgUrl: '/images/common/default.svg',
+ nickName: '유보라',
+ mbti: 'ENFP',
+ badge: '엠비티어른',
+ },
}
diff --git a/src/components/auth/Login.tsx b/src/components/auth/Login.tsx
index 06ef6e9..921d40c 100644
--- a/src/components/auth/Login.tsx
+++ b/src/components/auth/Login.tsx
@@ -4,26 +4,31 @@ import { User } from '@/model/User'
import { useRouter } from 'next/navigation'
import Profile from '../common/Profile'
-const menuItems = [
- { id: 'chat', label: 'M쌤 채팅', path: '/chat' },
- { id: 'notification', label: '알림', path: '/notifications' },
- { id: 'activity', label: '활동', path: '/activity' },
- { id: 'profile', label: '프로필 설정', path: '/profile-settings' },
-]
+export interface LoginProps {
+ user: User
+}
-const Login = ({ profileImgUrl, nickName, mbti, badge }: User) => {
+const Login = ({ user }: LoginProps) => {
const router = useRouter()
+ const { id, profileImgUrl, nickName, mbti, badge } = user
+
+ const menuItems = [
+ { id: 'chat', label: 'M쌤 채팅', path: '/chatting' },
+ { id: 'notification', label: '알림', path: '/#' },
+ { id: 'activity', label: '활동', path: '/#' },
+ { id: 'profile', label: '프로필 설정', path: `/user/${id}` },
+ ]
return (
-
+
-
+
@@ -40,8 +45,4 @@ const Login = ({ profileImgUrl, nickName, mbti, badge }: User) => {
)
}
-Login.defaultProps = {
- badge: undefined,
-}
-
export default Login
diff --git a/src/components/board/BoardCreatePage.tsx b/src/components/board/BoardCreatePage.tsx
index 6ae27f2..8b3c948 100644
--- a/src/components/board/BoardCreatePage.tsx
+++ b/src/components/board/BoardCreatePage.tsx
@@ -16,12 +16,13 @@ import { useQueryClient } from '@tanstack/react-query'
import { queryKeys } from '@/service/board/BoardQueries'
import { useUserInfo } from '@/service/user/useUserService'
import { MBTI } from '@/types/mbtiTypes'
+import { useToast } from '@/hooks/useToast'
const BoardCreatePage = () => {
const router = useRouter()
const queryClient = useQueryClient()
-
const editorRef = useRef
(null)
+ const { showToast } = useToast()
const [title, setTitle] = useState('')
const [content, setContent] = useState('')
@@ -101,10 +102,10 @@ const BoardCreatePage = () => {
const handleSubmit = () => {
if (!title) {
- alert('제목을 입력해주세요.')
+ showToast('제목을 입력해주세요.')
return
} else if (!content) {
- alert('내용을 입력해주세요.')
+ showToast('내용을 입력해주세요.')
return
}
postBoard(formData, {
@@ -116,7 +117,7 @@ const BoardCreatePage = () => {
}
return (
-
+
diff --git a/src/components/board/BoardPage.tsx b/src/components/board/BoardPage.tsx
index 5c18fb0..29a65ef 100644
--- a/src/components/board/BoardPage.tsx
+++ b/src/components/board/BoardPage.tsx
@@ -10,6 +10,8 @@ import SearchBar from '@/components/common/SearchBar'
import { useBoardList } from '@/service/board/useBoardService'
import { useEffect, useState } from 'react'
import { BoardI } from '@/model/Board'
+import { useToast } from '@/hooks/useToast'
+import { useUserInfo } from '@/service/user/useUserService'
const BoardPage = () => {
const router = useRouter()
@@ -22,6 +24,9 @@ const BoardPage = () => {
const pageSize = 6
const { data: boardList } = useBoardList(mbti, page - 1, pageSize)
+ const { data: userInfo } = useUserInfo()
+
+ const { showToast } = useToast()
const handlePageChange = (newPage: number) => {
setPage(newPage)
@@ -36,6 +41,14 @@ const BoardPage = () => {
}
}, [mbtiQuery])
+ const handleWriteClick = () => {
+ if (!userInfo) {
+ showToast('로그인이 필요한 서비스입니다')
+ return
+ }
+ router.push('/board/create')
+ }
+
return (
<>
@@ -48,9 +61,7 @@ const BoardPage = () => {
text="글 쓰기"
color="PURPLE"
size="small"
- onClick={() => {
- router.push('/board/create')
- }}
+ onClick={handleWriteClick}
/>
diff --git a/src/components/board/BoardUpdatePage.tsx b/src/components/board/BoardUpdatePage.tsx
index ffb8b65..4e07bf0 100644
--- a/src/components/board/BoardUpdatePage.tsx
+++ b/src/components/board/BoardUpdatePage.tsx
@@ -16,6 +16,7 @@ import MbtiSelect from '@/components/board/MbtiSelect'
import { useQueryClient } from '@tanstack/react-query'
import { queryKeys } from '@/service/board/BoardQueries'
import { MBTI } from '@/types/mbtiTypes'
+import { useToast } from '@/hooks/useToast'
const BoardUpdatePage = () => {
const router = useRouter()
@@ -24,6 +25,7 @@ const BoardUpdatePage = () => {
const boardId = Number(id)
const editorRef = useRef(null)
+ const { showToast } = useToast()
const [mbti, setMbti] = useState(null)
const [title, setTitle] = useState('')
@@ -114,10 +116,10 @@ const BoardUpdatePage = () => {
const handleSubmit = () => {
if (!title) {
- alert('제목을 입력해주세요.')
+ showToast('제목을 입력해주세요.')
return
} else if (!content) {
- alert('내용을 입력해주세요.')
+ showToast('내용을 입력해주세요.')
return
}
updateBoard(
diff --git a/src/components/board/Comment.tsx b/src/components/board/Comment.tsx
index da6abb2..a83f309 100644
--- a/src/components/board/Comment.tsx
+++ b/src/components/board/Comment.tsx
@@ -8,6 +8,8 @@ import {
} from '@/service/comment/useCommentService'
import Image from 'next/image'
import { useParams } from 'next/navigation'
+import { useToast } from '@/hooks/useToast'
+import { useUserInfo } from '@/service/user/useUserService'
import Profile from '../common/Profile'
export interface CommentProps {
@@ -32,10 +34,20 @@ const Comment = ({ comment, onClick, refetchComments }: CommentProps) => {
const [liked, setLiked] = useState(comment.isLiked)
const [likeCount, setLikeCount] = useState(comment.likeCount)
+ const { data: userInfo } = useUserInfo()
+ const { showToast } = useToast()
const isDeleted = content === '삭제된 댓글입니다.'
const handleToggleLike = () => {
+ if (!userInfo) {
+ showToast('로그인이 필요한 서비스입니다')
+ return
+ }
+ if (userInfo && userInfo.id === memberSimpleInfo.id) {
+ showToast('본인 댓글에는 좋아요를 누를 수 없습니다')
+ return
+ }
if (!isEditAllowed) {
setLiked(!liked)
setLikeCount((prevCount) => (liked ? prevCount - 1 : prevCount + 1))
@@ -82,8 +94,7 @@ const Comment = ({ comment, onClick, refetchComments }: CommentProps) => {
{/* mobile */}
@@ -70,7 +81,7 @@ const Header = () => {
handleClick(category.path)}
+ onClick={() => handleCategoryClick(category.path)}
className={`cursor-pointer relative hover:text-main1 transition-all ${
selected === category.path
? 'text-main1 font-bold after:content-[""] after:absolute after:bottom-[-10px] after:left-0 after:w-full after:h-[2px] after:bg-main1 after:opacity-100'
diff --git a/src/components/common/Toast.tsx b/src/components/common/Toast.tsx
new file mode 100644
index 0000000..aad6e05
--- /dev/null
+++ b/src/components/common/Toast.tsx
@@ -0,0 +1,20 @@
+import React from 'react'
+
+interface ToastProps {
+ show: boolean
+ children: React.ReactNode
+}
+
+const Toast = ({ show, children }: ToastProps) => {
+ return (
+
+ {children}
+
+ )
+}
+
+export { Toast }
diff --git a/src/components/common/Toaster.tsx b/src/components/common/Toaster.tsx
new file mode 100644
index 0000000..102497c
--- /dev/null
+++ b/src/components/common/Toaster.tsx
@@ -0,0 +1,22 @@
+'use client'
+
+import { useToast } from '@/hooks/useToast'
+import { Toast } from './Toast'
+
+const Toaster = () => {
+ const { toasts } = useToast()
+
+ return (
+ <>
+ {toasts.map((toast) => {
+ return (
+
+ {toast.message}
+
+ )
+ })}
+ >
+ )
+}
+
+export default Toaster
diff --git a/src/components/home/HotThreeBox.tsx b/src/components/home/HotThreeBox.tsx
index 7102e46..4880428 100644
--- a/src/components/home/HotThreeBox.tsx
+++ b/src/components/home/HotThreeBox.tsx
@@ -1,35 +1,68 @@
'use client'
import { useHotThree } from '@/service/home/useHomeService'
+import { useUserInfo } from '@/service/user/useUserService'
import HotThree from './HotThree'
import NotLogin from '../auth/NotLogin'
+import Login from '../auth/Login'
const HotThreeBox = () => {
const { data: hotThree } = useHotThree()
+ const { data: userInfo } = useUserInfo()
return (
<>
{/* mobile */}
-
- {hotThree && (
+ {userInfo ? (
<>
-
-
-
+
+ {hotThree && (
+ <>
+
+
+
+ >
+ )}
+ >
+ ) : (
+ <>
+
+ {hotThree && (
+ <>
+
+
+
+ >
+ )}
>
)}
{/* desktop */}
- {hotThree && (
+ {userInfo ? (
+ <>
+ {hotThree && (
+ <>
+
+
+
+ >
+ )}
+
+ >
+ ) : (
<>
-
-
-
+ {hotThree && (
+ <>
+
+
+
+ >
+ )}
+
>
)}
-
>
)
diff --git a/src/hooks/useToast.tsx b/src/hooks/useToast.tsx
new file mode 100644
index 0000000..ca63a29
--- /dev/null
+++ b/src/hooks/useToast.tsx
@@ -0,0 +1,74 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+
+const TOAST_REMOVE_DELAY = 2000
+
+interface Toast {
+ id: string
+ message: string
+ show: boolean
+}
+
+let toasts: Toast[] = []
+let count = 0
+
+const genId = (): string => {
+ count = (count + 1) % Number.MAX_SAFE_INTEGER
+ return count.toString()
+}
+
+const timeouts = new Map>()
+const listeners: Array<(toasts: Toast[]) => void> = []
+
+const showToast = (message: string) => {
+ const id = genId()
+
+ if (!toasts.find((toast) => toast.id === id)) {
+ toasts = [...toasts, { id, message, show: true }]
+ }
+
+ listeners.forEach((listener) => {
+ listener(toasts)
+ })
+
+ const timeout = setTimeout(() => {
+ toasts = toasts.map((toast) =>
+ toast.id === id ? { ...toast, show: false } : toast,
+ )
+
+ listeners.forEach((listener) => {
+ listener(toasts)
+ })
+
+ const maxTimeoutId = Array.from(timeouts.entries()).reduce((a, b) =>
+ b[1] > a[1] ? b : a,
+ )[0]
+
+ if (maxTimeoutId === id) {
+ toasts = []
+ timeouts.clear()
+ }
+ }, TOAST_REMOVE_DELAY)
+
+ timeouts.set(id, timeout)
+}
+
+export const useToast = () => {
+ const [state, setState] = useState([...toasts])
+
+ useEffect(() => {
+ listeners.push(setState)
+ return () => {
+ const index = listeners.indexOf(setState)
+ if (index > -1) {
+ listeners.splice(index, 1)
+ }
+ }
+ }, [])
+
+ return {
+ showToast,
+ toasts: state,
+ }
+}
diff --git a/tailwind.config.ts b/tailwind.config.ts
index 01542ef..bc8f2bf 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -96,6 +96,7 @@ const config: Config = {
'38.5': '154px',
'68': '272px',
'95': '380px',
+ '4%': '4%',
'5%': '5%',
'6%': '6%',
'7%': '7%',