+
+ {card.title}
+
+
+ {/* 카드 우측 상단의 케밥 버튼, X 버튼 */}
+
}
>
- {/* 드롭다운 내부 메뉴 아이템 */}
-
setOpenModifyCard(true)}
+ {/* 트리거의(케밥) 드롭다운 - 수정하기, 삭제하기 */}
+
-
+
+
+
-
{
+ {/* X 버튼 (카드 닫기)*/}
+
+ {/* 카드 상단 */}
- 그리드로 관리하기 / 컬럼명은 따로 컴포넌트로 만들어 빼기
-
{column.title}
-
-
설명~~~~~~~~
-
-
{assignee.nickname}
-
{dueDate}
+
+ {/* 컬럼명 / 태그 // 설명 // 이미지 */}
+
+
+ {card.description && (
+
+ {card.description}
+
+ )}
+ {card.imageUrl && (
+
+ )}
+
+ {/* 담당자 / 마감일 박스 */}
+
+
+
+
+ 담당자
+
+ {card.assignee && (
+
+ <>
+
+
+ {card.assignee.nickname}
+
+ >
+
+ )}
+
+
+
+ 마감일
+
+ {card.dueDate && (
+
+ {card.dueDate}
+
+ )}
+
+
+
+
-
이미지 있으면 이미지
-
댓글 컴포넌트
- {/* 카드 수정 모달 */}
- {openModifyCard && (
-
- setOpenModifyCard(false)}
- // columnId={column.id}
- currentColumn={currentColumn}
- card={card}
- />
-
- )}
- >
+
+
+
)
}
diff --git a/src/app/features/dashboard_Id/Card/cardModal/CardContentCopy.tsx b/src/app/features/dashboard_Id/Card/cardModal/CardContentCopy.tsx
new file mode 100644
index 0000000..933b48c
--- /dev/null
+++ b/src/app/features/dashboard_Id/Card/cardModal/CardContentCopy.tsx
@@ -0,0 +1,110 @@
+// import { useModalStore } from '@store/useModalStore'
+// import Image from 'next/image'
+// import { useState } from 'react'
+
+// import Dropdown from '@/app/shared/components/common/Dropdown/Dropdown'
+
+// import { Card } from '../../type/Card.type'
+// import { Column } from '../../type/Column.type'
+// import CreateCardModal from '../cardFormModals/CreateCardModal'
+// import ModifyCardForm from '../cardFormModals/ModifyCardForm'
+// import Tags from '../Tags'
+// interface CardContentProps {
+// onClose: () => void
+// card: Card
+// column: Column
+// }
+// export default function CardContent({
+// onClose,
+// card,
+// column,
+// }: CardContentProps) {
+// const { id, imageUrl, title, tags, dueDate, assignee } = card
+// const [openModifyCard, setOpenModifyCard] = useState(false)
+// const { title: columnTitle, id: columnId } = column
+// const currentColumn = { columnTitle, columnId }
+// const { modalType, closeModal } = useModalStore()
+
+// const handleBackdropClick = (e: React.MouseEvent
) => {
+// if (e.target === e.currentTarget) {
+// closeModal()
+// }
+// }
+
+// if (!modalType) return null
+
+// return (
+// <>
+//
+//
+//
초대하기
+
+//
{title}
+//
+//
+// }
+// >
+// {/* 드롭다운 내부 메뉴 아이템 */}
+//
setOpenModifyCard(true)}
+// >
+// 수정하기
+//
+//
+// 삭제하기
+//
+//
+//
{
+// onClose()
+// console.log('sdfadfdsgreggsfds')
+// }}
+// />
+//
+
+//
+// 그리드로 관리하기 / 컬럼명은 따로 컴포넌트로 만들어 빼기
+//
{column.title}
+//
+//
설명~~~~~~~~
+//
+//
{assignee.nickname}
+//
{dueDate}
+//
+//
+//
이미지 있으면 이미지
+//
댓글 컴포넌트
+
+// {/* 카드 수정 모달 */}
+// {openModifyCard && (
+//
+// setOpenModifyCard(false)}
+// // columnId={column.id}
+// currentColumn={currentColumn}
+// card={card}
+// />
+//
+// )}
+//
+//
+// >
+// )
+// }
diff --git a/src/app/features/dashboard_Id/Card/cardModal/CardModal.tsx b/src/app/features/dashboard_Id/Card/cardModal/CardModal.tsx
index 9f8b32b..11b3353 100644
--- a/src/app/features/dashboard_Id/Card/cardModal/CardModal.tsx
+++ b/src/app/features/dashboard_Id/Card/cardModal/CardModal.tsx
@@ -1,15 +1,20 @@
import { createPortal } from 'react-dom'
+import { useLockBodyScroll } from '@/app/shared/hooks/useLockBodyScroll'
+
interface ModalProps {
children: React.ReactNode
}
export default function CardModal({ children }: ModalProps) {
const modalRoot = document.getElementById('modal-root')
+
+ useLockBodyScroll()
+
if (!modalRoot) return null
return createPortal(
-
,
diff --git a/src/app/features/dashboard_Id/Card/cardModal/Comment.tsx b/src/app/features/dashboard_Id/Card/cardModal/Comment.tsx
new file mode 100644
index 0000000..a681885
--- /dev/null
+++ b/src/app/features/dashboard_Id/Card/cardModal/Comment.tsx
@@ -0,0 +1,58 @@
+import { format } from 'date-fns'
+import { useState } from 'react'
+
+import { Avatar } from '@/app/shared/components/common/Avatar'
+import { useIsMobile } from '@/app/shared/hooks/useIsmobile'
+
+import { useDeleteCommentMutation } from '../../api/useDeleteCommentMutation'
+import { Comment as CommentType } from '../../type/Comment.type'
+import CommentModifyForm from './CommentModifyForm'
+
+export default function Comment({ comment }: { comment: CommentType }) {
+ const isMobile = useIsMobile()
+ const [modifyComment, setModifyComment] = useState(false)
+ const { mutate: deleteComment, isPending } = useDeleteCommentMutation()
+
+ return (
+
+ {isMobile ? (
+
+ ) : (
+
+ )}
+
+
+
+ {comment.author.nickname}
+
+
+ {format(new Date(comment.createdAt), 'yyyy.MM.dd HH:mm')}
+
+
+ {modifyComment ? (
+
setModifyComment(false)}
+ commentId={comment.id}
+ content={comment.content}
+ />
+ ) : (
+ {comment.content}
+ )}
+ {!modifyComment && (
+
+
+
+
+ )}
+
+
+ )
+}
diff --git a/src/app/features/dashboard_Id/Card/cardModal/CommentForm.tsx b/src/app/features/dashboard_Id/Card/cardModal/CommentForm.tsx
new file mode 100644
index 0000000..08cc66f
--- /dev/null
+++ b/src/app/features/dashboard_Id/Card/cardModal/CommentForm.tsx
@@ -0,0 +1,69 @@
+import { useForm } from 'react-hook-form'
+
+import { usePostCommentMutation } from '../../api/usePostCommentMutation'
+import { CommentFormData } from '../../type/CommentFormData.type'
+
+export default function CommentForm({
+ cardId,
+ columnId,
+ dashboardId,
+}: {
+ cardId: number
+ columnId: number
+ dashboardId: number
+}) {
+ const {
+ register,
+ handleSubmit,
+ reset,
+ formState: { errors, isValid, isSubmitting },
+ } = useForm
({
+ mode: 'onChange',
+ })
+
+ // 폼 제출 핸들러 함수
+ const { mutate: createComment, isPending } = usePostCommentMutation()
+
+ function onSubmit(data: CommentFormData) {
+ const payload = {
+ ...data,
+ cardId,
+ columnId,
+ dashboardId,
+ }
+ console.log(payload)
+ createComment(payload)
+ reset()
+ }
+
+ return (
+
+ )
+}
diff --git a/src/app/features/dashboard_Id/Card/cardModal/CommentModifyForm.tsx b/src/app/features/dashboard_Id/Card/cardModal/CommentModifyForm.tsx
new file mode 100644
index 0000000..379012b
--- /dev/null
+++ b/src/app/features/dashboard_Id/Card/cardModal/CommentModifyForm.tsx
@@ -0,0 +1,66 @@
+import { useForm } from 'react-hook-form'
+
+import { usePutCommentMutation } from '../../api/usePutCommentsMutation'
+import { PutCommentForm } from '../../type/CommentFormData.type'
+
+export default function CommentModifyForm({
+ onClose,
+ commentId,
+ content,
+}: {
+ onClose: () => void
+ commentId: number
+ content: string
+}) {
+ const {
+ register,
+ handleSubmit,
+
+ formState: { errors, isValid, isSubmitting },
+ } = useForm({
+ defaultValues: {
+ content: content,
+ },
+ })
+
+ // 폼 제출 핸들러 함수
+ const { mutate: createComment, isPending } = usePutCommentMutation()
+
+ function onSubmit(data: PutCommentForm) {
+ console.log(data)
+ console.log(commentId)
+ createComment({ payload: data, commentId }) //useMutation은 하나의 인자만 허용. 객체 하나로 묶어서 전달하는거 자꾸 까먹음..
+ onClose()
+ }
+
+ return (
+
+ )
+}
diff --git a/src/app/features/dashboard_Id/Card/cardModal/Comments.tsx b/src/app/features/dashboard_Id/Card/cardModal/Comments.tsx
new file mode 100644
index 0000000..5456354
--- /dev/null
+++ b/src/app/features/dashboard_Id/Card/cardModal/Comments.tsx
@@ -0,0 +1,25 @@
+import { format } from 'date-fns'
+import { useState } from 'react'
+import { toast } from 'sonner'
+
+import { Avatar } from '@/app/shared/components/common/Avatar'
+
+import useCommentsQuery from '../../api/useCommentsQuery'
+import { Comment as CommentType } from '../../type/Comment.type'
+import Comment from './Comment'
+
+export default function Comments({ cardId }: { cardId: number }) {
+ const { data, isLoading, error } = useCommentsQuery(cardId)
+ const comments = data?.comments
+
+ if (error) {
+ toast.error(error?.message || '문제가 발생했습니다.')
+ }
+ return (
+
+ {comments?.map((comment: CommentType) => (
+
+ ))}
+
+ )
+}
diff --git a/src/app/features/dashboard_Id/api/deleteCard.ts b/src/app/features/dashboard_Id/api/deleteCard.ts
new file mode 100644
index 0000000..e7cb706
--- /dev/null
+++ b/src/app/features/dashboard_Id/api/deleteCard.ts
@@ -0,0 +1,11 @@
+import api from '@/app/shared/lib/testAxios'
+
+// import api from '@/app/shared/lib/axios'
+import { ApiResponse } from '../type/ApiResponse'
+
+export async function deleteCard(cardId: number): Promise {
+ const res = await api.delete(
+ `/${process.env.NEXT_PUBLIC_TEAM_ID}/cards/${cardId}`,
+ )
+ return res.data
+}
diff --git a/src/app/features/dashboard_Id/api/deleteComment.ts b/src/app/features/dashboard_Id/api/deleteComment.ts
new file mode 100644
index 0000000..edb504e
--- /dev/null
+++ b/src/app/features/dashboard_Id/api/deleteComment.ts
@@ -0,0 +1,11 @@
+import api from '@/app/shared/lib/testAxios'
+
+// import api from '@/app/shared/lib/axios'
+import { ApiResponse } from '../type/ApiResponse'
+
+export async function deleteComment(commentId: number): Promise {
+ const res = await api.delete(
+ `/${process.env.NEXT_PUBLIC_TEAM_ID}/comments/${commentId}`,
+ )
+ return res.data
+}
diff --git a/src/app/features/dashboard_Id/api/fetchComments.ts b/src/app/features/dashboard_Id/api/fetchComments.ts
new file mode 100644
index 0000000..dfb5499
--- /dev/null
+++ b/src/app/features/dashboard_Id/api/fetchComments.ts
@@ -0,0 +1,12 @@
+import api from '@/app/shared/lib/testAxios'
+
+// import api from '@/app/shared/lib/axios'
+import { CommentsResponse } from '../type/Comment.type'
+
+export async function fetchComments(cardId: number): Promise {
+ const res = await api.get(
+ `/${process.env.NEXT_PUBLIC_TEAM_ID}/comments?size=10&cardId=${cardId}`,
+ )
+
+ return res.data
+}
diff --git a/src/app/features/dashboard_Id/api/postComment.ts b/src/app/features/dashboard_Id/api/postComment.ts
new file mode 100644
index 0000000..0a9417f
--- /dev/null
+++ b/src/app/features/dashboard_Id/api/postComment.ts
@@ -0,0 +1,14 @@
+// import api from '@/app/shared/lib/axios'
+import api from '@/app/shared/lib/testAxios'
+
+import { CommentResponse, PutCommentForm } from '../type/CommentFormData.type'
+
+export async function postComment(
+ payload: PutCommentForm,
+): Promise {
+ const res = await api.post(
+ `/${process.env.NEXT_PUBLIC_TEAM_ID}/comments`,
+ payload,
+ )
+ return res.data
+}
diff --git a/src/app/features/dashboard_Id/api/putCard.ts b/src/app/features/dashboard_Id/api/putCard.ts
index 3cd2f65..af7da3f 100644
--- a/src/app/features/dashboard_Id/api/putCard.ts
+++ b/src/app/features/dashboard_Id/api/putCard.ts
@@ -1,12 +1,9 @@
import api from '@/app/shared/lib/testAxios'
+import { ApiResponse } from '../type/ApiResponse'
// import api from '@/app/shared/lib/axios'
import { CardModifyFormData } from '../type/CardFormData.type'
-interface ApiResponse {
- message: string
-}
-
export async function putCard(
payload: CardModifyFormData,
cardId: number,
diff --git a/src/app/features/dashboard_Id/api/putComment.ts b/src/app/features/dashboard_Id/api/putComment.ts
new file mode 100644
index 0000000..c4b742b
--- /dev/null
+++ b/src/app/features/dashboard_Id/api/putComment.ts
@@ -0,0 +1,15 @@
+import api from '@/app/shared/lib/testAxios'
+
+// import api from '@/app/shared/lib/axios'
+import { CommentResponse, PutCommentForm } from '../type/CommentFormData.type'
+
+export async function putComment(
+ payload: PutCommentForm,
+ commentId: number,
+): Promise {
+ const res = await api.put(
+ `/${process.env.NEXT_PUBLIC_TEAM_ID}/comments/${commentId}`,
+ payload,
+ )
+ return res.data
+}
diff --git a/src/app/features/dashboard_Id/api/useCommentsQuery.ts b/src/app/features/dashboard_Id/api/useCommentsQuery.ts
new file mode 100644
index 0000000..2a1c28f
--- /dev/null
+++ b/src/app/features/dashboard_Id/api/useCommentsQuery.ts
@@ -0,0 +1,11 @@
+import { useQuery } from '@tanstack/react-query'
+
+import { CommentsResponse } from '../type/Comment.type'
+import { fetchComments } from './fetchComments'
+
+export default function useCommentsQuery(cardId: number) {
+ return useQuery({
+ queryKey: ['comments', cardId],
+ queryFn: () => fetchComments(cardId),
+ })
+}
diff --git a/src/app/features/dashboard_Id/api/useDeleteCardMutation.ts b/src/app/features/dashboard_Id/api/useDeleteCardMutation.ts
new file mode 100644
index 0000000..dd6f8db
--- /dev/null
+++ b/src/app/features/dashboard_Id/api/useDeleteCardMutation.ts
@@ -0,0 +1,20 @@
+import { useMutation, useQueryClient } from '@tanstack/react-query'
+import { toast } from 'sonner'
+
+import { deleteCard } from './deleteCard'
+
+export function useDeleteCardMutation() {
+ const queryClient = useQueryClient()
+
+ return useMutation({
+ mutationFn: (cardId: number) => deleteCard(cardId),
+ onSuccess: () => {
+ toast.success('카드가 삭제되었습니다')
+ queryClient.invalidateQueries({ queryKey: ['columnId'] }) // 카드 목록 캐시 무효화
+ },
+ onError: (error) => {
+ toast.error('카드 삭제를 실패했습니다.')
+ console.error('카드 삭제 실패:', error)
+ },
+ })
+}
diff --git a/src/app/features/dashboard_Id/api/useDeleteCommentMutation.ts b/src/app/features/dashboard_Id/api/useDeleteCommentMutation.ts
new file mode 100644
index 0000000..921b199
--- /dev/null
+++ b/src/app/features/dashboard_Id/api/useDeleteCommentMutation.ts
@@ -0,0 +1,17 @@
+import { useMutation, useQueryClient } from '@tanstack/react-query'
+
+import { deleteComment } from './deleteComment'
+
+export function useDeleteCommentMutation() {
+ const queryClient = useQueryClient()
+
+ return useMutation({
+ mutationFn: (commentId: number) => deleteComment(commentId),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['comments'] }) // 댓글 리스트 캐시 무효화
+ },
+ onError: (error) => {
+ console.error('댓글 삭제 실패:', error)
+ },
+ })
+}
diff --git a/src/app/features/dashboard_Id/api/usePostCard.ts b/src/app/features/dashboard_Id/api/usePostCard.ts
index 81f982e..ec809e4 100644
--- a/src/app/features/dashboard_Id/api/usePostCard.ts
+++ b/src/app/features/dashboard_Id/api/usePostCard.ts
@@ -1,5 +1,6 @@
import { useMutation, useQueryClient } from '@tanstack/react-query'
import axios from 'axios'
+import { toast } from 'sonner'
import { postCard } from './postCard'
@@ -10,9 +11,12 @@ export function usePostCard() {
return useMutation({
mutationFn: postCard,
onSuccess: () => {
+ toast.success('할 일이 추가되었습니다.')
queryClient.invalidateQueries({ queryKey: ['columnId'] }) //'columnId' 쿼리 invalidate - 카드 리스트가 stale 상태임을 알리고 다시 fetch 하도록 유도함
},
onError: (error) => {
+ toast.error('카드 생성 실패')
+
if (axios.isAxiosError(error)) {
const message = error.response?.data?.message
console.error('카드 생성 실패:', message ?? '알 수 없는 에러')
diff --git a/src/app/features/dashboard_Id/api/usePostCommentMutation.ts b/src/app/features/dashboard_Id/api/usePostCommentMutation.ts
new file mode 100644
index 0000000..ebb51d7
--- /dev/null
+++ b/src/app/features/dashboard_Id/api/usePostCommentMutation.ts
@@ -0,0 +1,24 @@
+import { useMutation, useQueryClient } from '@tanstack/react-query'
+import axios from 'axios'
+
+import { postComment } from './postComment'
+
+// ✅ 댓글 생성 컴포넌트에서 사용 (CommentForm.tsx)
+export function usePostCommentMutation() {
+ const queryClient = useQueryClient()
+
+ return useMutation({
+ mutationFn: postComment,
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['comments'] }) // 댓글 목록 캐시가 stale 상태임을 알리고 다시 fetch 하도록 유도함
+ },
+ onError: (error) => {
+ if (axios.isAxiosError(error)) {
+ const message = error.response?.data?.message
+ console.error('댓글 생성 실패:', message ?? '알 수 없는 에러')
+ } else {
+ console.error('댓글 생성 실패:', error)
+ }
+ },
+ })
+}
diff --git a/src/app/features/dashboard_Id/api/usePutCardMutation.ts b/src/app/features/dashboard_Id/api/usePutCardMutation.ts
index 0eb536a..cd1ec3f 100644
--- a/src/app/features/dashboard_Id/api/usePutCardMutation.ts
+++ b/src/app/features/dashboard_Id/api/usePutCardMutation.ts
@@ -1,11 +1,12 @@
import { useMutation, useQueryClient } from '@tanstack/react-query'
import axios from 'axios'
+import { toast } from 'sonner'
import { CardModifyFormData } from '../type/CardFormData.type'
import { postCard } from './postCard'
import { putCard } from './putCard'
-// ✅ 카드 생성 모달에서 사용 (CreateCardForm.tsx)
+// ✅ 카드 수정 모달에서 사용 (CreateCardForm.tsx)
export function usePutCardMutation() {
const queryClient = useQueryClient()
@@ -18,14 +19,16 @@ export function usePutCardMutation() {
payload: CardModifyFormData
}) => putCard(payload, cardId),
onSuccess: () => {
- queryClient.invalidateQueries({ queryKey: ['cardId'] }) //'columnId' 쿼리 invalidate - 카드가 stale 상태임을 알리고 다시 fetch 하도록 유도함
+ toast.success('할 일 카드가 수정되었습니다.')
+ queryClient.invalidateQueries({ queryKey: ['columnId'] }) //'columnId' 쿼리 invalidate - 카드가 stale 상태임을 알리고 다시 fetch 하도록 유도함
},
onError: (error) => {
+ toast.error('카드 수정 실패')
if (axios.isAxiosError(error)) {
const message = error.response?.data?.message
- console.error('카드 생성 실패:', message ?? '알 수 없는 에러')
+ console.error('카드 수정 실패:', message ?? '알 수 없는 에러')
} else {
- console.error('카드 생성 실패:', error)
+ console.error('카드 수정 실패:', error)
}
},
})
diff --git a/src/app/features/dashboard_Id/api/usePutCommentsMutation.ts b/src/app/features/dashboard_Id/api/usePutCommentsMutation.ts
new file mode 100644
index 0000000..3d93c48
--- /dev/null
+++ b/src/app/features/dashboard_Id/api/usePutCommentsMutation.ts
@@ -0,0 +1,30 @@
+import { useMutation, useQueryClient } from '@tanstack/react-query'
+import axios from 'axios'
+
+import { CommentResponse, PutCommentForm } from '../type/CommentFormData.type'
+import { putComment } from './putComment'
+
+interface PutCommentArgs {
+ commentId: number
+ payload: PutCommentForm
+}
+
+// ✅ 카드 생성 모달에서 사용 (CreateCardForm.tsx)
+export function usePutCommentMutation() {
+ const queryClient = useQueryClient()
+
+ return useMutation({
+ mutationFn: ({ payload, commentId }) => putComment(payload, commentId),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['comments'] }) //'columnId' 쿼리 invalidate - 카드가 stale 상태임을 알리고 다시 fetch 하도록 유도함
+ },
+ onError: (error) => {
+ if (axios.isAxiosError(error)) {
+ const message = error.response?.data?.message
+ console.error('댓글 수정 실패:', message ?? '알 수 없는 에러')
+ } else {
+ console.error('댓글 수정 실패:', error)
+ }
+ },
+ })
+}
diff --git a/src/app/features/dashboard_Id/api/useUploadCardImage.ts b/src/app/features/dashboard_Id/api/useUploadCardImage.ts
index 580923b..4c1074f 100644
--- a/src/app/features/dashboard_Id/api/useUploadCardImage.ts
+++ b/src/app/features/dashboard_Id/api/useUploadCardImage.ts
@@ -10,10 +10,12 @@ export function useUploadCardImage() {
mutationFn: ({ columnId, file }: { columnId: number; file: File }) =>
postCardImages(columnId, file),
onError: (error: AxiosError<{ message: string }>) => {
+ toast.error(
+ '이미지 업로드 중 오류가 발생했습니다. 크기가 작은 파일을 시도해주세요.',
+ )
const message =
error?.response?.data?.message ?? // 서버가 준 에러메세지
'이미지 업로드 중 오류가 발생했습니다. 크기가 작은 파일을 시도해주세요.' // 없으면 이 내용으로
- toast.error(message)
},
})
}
diff --git a/src/app/features/dashboard_Id/hooks/useInfiniteScroll.ts b/src/app/features/dashboard_Id/hooks/useInfiniteScroll.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/features/dashboard_Id/type/ApiResponse.ts b/src/app/features/dashboard_Id/type/ApiResponse.ts
new file mode 100644
index 0000000..d6b3593
--- /dev/null
+++ b/src/app/features/dashboard_Id/type/ApiResponse.ts
@@ -0,0 +1,3 @@
+export interface ApiResponse {
+ message: string
+}
diff --git a/src/app/features/dashboard_Id/type/Comment.type.ts b/src/app/features/dashboard_Id/type/Comment.type.ts
new file mode 100644
index 0000000..96e597d
--- /dev/null
+++ b/src/app/features/dashboard_Id/type/Comment.type.ts
@@ -0,0 +1,17 @@
+export interface Auth {
+ profileImageUrl: string
+ nickname: string
+ id: number
+}
+export interface Comment {
+ id: number
+ content: string
+ createdAt: string
+ updatedAt: string
+ cardId: number
+ author: Auth
+}
+export interface CommentsResponse {
+ cursorId: number | null
+ comments: Comment[] | []
+}
diff --git a/src/app/features/dashboard_Id/type/CommentFormData.type.ts b/src/app/features/dashboard_Id/type/CommentFormData.type.ts
new file mode 100644
index 0000000..2176eb8
--- /dev/null
+++ b/src/app/features/dashboard_Id/type/CommentFormData.type.ts
@@ -0,0 +1,20 @@
+import { Auth } from './Comment.type'
+
+export interface CommentFormData {
+ content: string
+ cardId: number
+ columnId: number
+ dashboardId: number
+}
+export interface CommentResponse {
+ id: number
+ content: string
+ createdAt: number
+ updatedAt: number
+ cardId: number
+ author: Auth
+}
+
+export interface PutCommentForm {
+ content: string
+}
diff --git a/src/app/globals.css b/src/app/globals.css
index 71a8f3e..e4d5a47 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -70,7 +70,7 @@ body {
@apply border border-[#D9D9D9] dark:border-[#747474];
}
.Border-section {
- @apply border border-[#D9D9D9] dark:border-[#000000];
+ @apply border border-[#D9D9D9] dark:border-[#454545];
}
.Border-column {
@apply border-r border-[#EEEEEE] dark:border-[#262626];
diff --git a/src/app/shared/components/common/Avatar.tsx b/src/app/shared/components/common/Avatar.tsx
index 056e2ae..eceb44a 100644
--- a/src/app/shared/components/common/Avatar.tsx
+++ b/src/app/shared/components/common/Avatar.tsx
@@ -51,7 +51,7 @@ export function Avatar({ size = 36, name, imageUrl }: AvatarProps) {
return profileImageUrl.current ? (
) : (
{
+ if (typeof window !== 'undefined') {
+ return window.innerWidth <= breakpoint
+ }
+ return false
+ })
+
+ useEffect(() => {
+ if (typeof window === 'undefined') return
+
+ function handleResize() {
+ setIsMobile(window.innerWidth <= breakpoint)
+ }
+
+ handleResize() // 초기 실행
+ window.addEventListener('resize', handleResize)
+ return () => window.removeEventListener('resize', handleResize)
+ }, [breakpoint])
+
+ return isMobile
+}
diff --git a/src/app/shared/hooks/useLockBodyScroll.ts b/src/app/shared/hooks/useLockBodyScroll.ts
new file mode 100644
index 0000000..4f5c82c
--- /dev/null
+++ b/src/app/shared/hooks/useLockBodyScroll.ts
@@ -0,0 +1,16 @@
+import { useEffect } from 'react'
+
+// 모달 띄웠을때 배경 스크롤 막기
+// 사용법: 모달 생성 컴포넌트에 가서, useLockBodyScroll()
+// overflow: hidden을 적용해서 바디 스크롤 막음
+// 컴포넌트 언마운트 시 원래 상태로 복원
+export function useLockBodyScroll() {
+ useEffect(() => {
+ const originalStyle = window.getComputedStyle(document.body).overflow
+ document.body.style.overflow = 'hidden'
+
+ return () => {
+ document.body.style.overflow = originalStyle
+ }
+ }, [])
+}