diff --git a/public/images/boardThemas/B-0.png b/public/images/boardThemas/B-0.png new file mode 100644 index 0000000..8d03af8 Binary files /dev/null and b/public/images/boardThemas/B-0.png differ diff --git a/public/images/boardThemas/B-1.png b/public/images/boardThemas/B-1.png new file mode 100644 index 0000000..8e0b413 Binary files /dev/null and b/public/images/boardThemas/B-1.png differ diff --git a/public/images/boardThemas/B-2.png b/public/images/boardThemas/B-2.png new file mode 100644 index 0000000..80f2357 Binary files /dev/null and b/public/images/boardThemas/B-2.png differ diff --git a/public/images/boardThemas/B-3.png b/public/images/boardThemas/B-3.png new file mode 100644 index 0000000..1dfaed9 Binary files /dev/null and b/public/images/boardThemas/B-3.png differ diff --git a/public/images/polaroidFrames/F-10.svg b/public/images/polaroidFrames/F-10.svg new file mode 100644 index 0000000..bb3df43 --- /dev/null +++ b/public/images/polaroidFrames/F-10.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/polaroidFrames/F-11.svg b/public/images/polaroidFrames/F-11.svg new file mode 100644 index 0000000..622f898 --- /dev/null +++ b/public/images/polaroidFrames/F-11.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/polaroidFrames/F-9.svg b/public/images/polaroidFrames/F-9.svg new file mode 100644 index 0000000..1021001 --- /dev/null +++ b/public/images/polaroidFrames/F-9.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/polaroidThemas/F-10.png b/public/images/polaroidThemas/F-10.png new file mode 100644 index 0000000..2a0dbe9 Binary files /dev/null and b/public/images/polaroidThemas/F-10.png differ diff --git a/public/images/polaroidThemas/F-11.png b/public/images/polaroidThemas/F-11.png new file mode 100644 index 0000000..7fbe881 Binary files /dev/null and b/public/images/polaroidThemas/F-11.png differ diff --git a/public/images/polaroidThemas/F-9.png b/public/images/polaroidThemas/F-9.png new file mode 100644 index 0000000..57672c3 Binary files /dev/null and b/public/images/polaroidThemas/F-9.png differ diff --git a/src/__tests__/hooks/useBoardName.spec.ts b/src/__tests__/hooks/useBoardName.spec.ts new file mode 100644 index 0000000..9839f72 --- /dev/null +++ b/src/__tests__/hooks/useBoardName.spec.ts @@ -0,0 +1,47 @@ +import { act, renderHook } from '@testing-library/react' +import { useBoardName } from '@/hooks/useBoardName' + +describe('useBoardName()', () => { + it('description', () => { + // Given + const { result } = renderHook(() => useBoardName()) + + // When + act(() => { + result.current.setBoardName('board name') + }) + + // Then + expect(result.current.description).toEqual('10/15자') + }) + + describe('errorMessage', () => { + it('should return error message on empty string', () => { + // Given + const { result } = renderHook(() => useBoardName()) + + // When + act(() => { + result.current.setBoardName('') + }) + + // Then + expect(result.current.errorMessage).toEqual( + '최소 한글자 이상 입력해주세요', + ) + }) + + it('should return error message on value longer than max length', () => { + // Given + const { result } = renderHook(() => useBoardName()) + + // When + act(() => { + result.current.setBoardName('abcdefghijklmnop') + }) + + // Then + expect(result.current.errorMessage).toEqual('15자 이내로 입력 가능해요') + }) + }) +}) diff --git a/src/__tests__/hooks/useInputValidation.spec.ts b/src/__tests__/hooks/useInputValidation.spec.ts new file mode 100644 index 0000000..6bbcf28 --- /dev/null +++ b/src/__tests__/hooks/useInputValidation.spec.ts @@ -0,0 +1,182 @@ +import { act, renderHook } from '@testing-library/react' +import { useInputValidation, Validation } from '@/hooks/useInputValidation' + +describe('useInputValidation()', () => { + describe('value', () => { + it('should return initialValue as value', () => { + // Given + const initialValue = 'initialValue' + + // When + const { result } = renderHook(() => useInputValidation(initialValue)) + + // Then + expect(result.current.value).toEqual(initialValue) + }) + + it('should set value to the new value when setValue is called', () => { + // Given + const initialValue = 'initialValue' + const { result } = renderHook(() => useInputValidation(initialValue)) + + // When + const newValue = 'newValue' + act(() => { + result.current.setValue(newValue) + }) + + // Then + expect(result.current.value).toEqual(newValue) + }) + }) + describe('isDirty', () => { + it('should set isDirty to false before the value is changed', () => { + // Given + const initialValue = 'initialValue' + + // When + const { result } = renderHook(() => useInputValidation(initialValue)) + + // Then + expect(result.current.isDirty).toBeFalsy() + }) + + it('should set isDirty to true when the value is changed', () => { + // Given + const initialValue = 'initialValue' + const { result } = renderHook(() => useInputValidation(initialValue)) + + // When + const newValue = 'newValue' + act(() => { + result.current.setValue(newValue) + }) + + // Then + expect(result.current.isDirty).toBeTruthy() + }) + + it('should set isDirty to false when the value is not changed', () => { + // Given + const initialValue = 'initialValue' + const { result } = renderHook(() => useInputValidation(initialValue)) + + // When + const newValue = 'initialValue' + act(() => { + result.current.setValue(newValue) + }) + + // Then + expect(result.current.isDirty).toBeFalsy() + }) + }) + + describe('isInvalid', () => { + it('should set isInvalid to true when a validation fails', () => { + // Given + const initialValue = 'initialValue' + const validations: Validation[] = [ + { + validator: () => false, + errorMessage: 'error message1', + }, + { + validator: () => true, + errorMessage: 'error message2', + }, + ] + + // When + const { result } = renderHook(() => + useInputValidation(initialValue, validations), + ) + + // Then + expect(result.current.isInvalid).toBeTruthy() + }) + + it('should set isInvalid to false when all validations pass', () => { + // Given + const initialValue = 'initialValue' + const validations: Validation[] = [ + { + validator: () => true, + errorMessage: 'error message1', + }, + { + validator: () => true, + errorMessage: 'error message2', + }, + { + validator: () => true, + errorMessage: 'error message3', + }, + ] + + // When + const { result } = renderHook(() => + useInputValidation(initialValue, validations), + ) + + // Then + expect(result.current.isInvalid).toBeFalsy() + }) + }) + + describe('errorMessage', () => { + it('should set errorMessage to the first validation that fails ', () => { + // Given + const initialValue = 'initialValue' + const validations: Validation[] = [ + { + validator: () => false, + errorMessage: 'error message1', + }, + { + validator: () => false, + errorMessage: 'error message2', + }, + { + validator: () => false, + errorMessage: 'error message3', + }, + ] + + // When + const { result } = renderHook(() => + useInputValidation(initialValue, validations), + ) + + // Then + expect(result.current.errorMessage).toEqual('error message1') + }) + + it('should set errorMessage to empty string when all validations pass', () => { + // Given + const initialValue = 'initialValue' + const validations: Validation[] = [ + { + validator: () => true, + errorMessage: 'error message1', + }, + { + validator: () => true, + errorMessage: 'error message2', + }, + { + validator: () => true, + errorMessage: 'error message3', + }, + ] + + // When + const { result } = renderHook(() => + useInputValidation(initialValue, validations), + ) + + // Then + expect(result.current.errorMessage).toEqual('') + }) + }) +}) diff --git a/src/__tests__/lib/utils/clipboard.spec.ts b/src/__tests__/lib/utils/clipboard.spec.ts new file mode 100644 index 0000000..cbe3abb --- /dev/null +++ b/src/__tests__/lib/utils/clipboard.spec.ts @@ -0,0 +1,39 @@ +import { copyToClipboard } from '@/lib/utils' + +describe('lib/utils/clipboard', () => { + describe('copyToClipboard()', () => { + beforeEach(() => { + Object.assign(navigator, { + clipboard: { + writeText: jest.fn().mockImplementation(() => Promise.resolve()), + }, + }) + }) + + afterEach(() => { + jest.restoreAllMocks() + }) + + it('Should Copy target string to clipboard', async () => { + // Given + const target = 'copy target string' + + // When + await copyToClipboard(target) + + // Then + expect(navigator.clipboard.writeText).toHaveBeenCalledWith(target) + }) + + it('Should return resolved promise object when it is done', () => { + // Given + const target = 'copy target string' + + // When + const result = copyToClipboard(target) + + // Then + expect(result).toEqual(Promise.resolve()) + }) + }) +}) diff --git a/src/app/(home)/_components/CopyLinkBtn.tsx b/src/app/(home)/_components/CopyLinkBtn.tsx index 2a72ac4..12d7a71 100644 --- a/src/app/(home)/_components/CopyLinkBtn.tsx +++ b/src/app/(home)/_components/CopyLinkBtn.tsx @@ -1,20 +1,19 @@ 'use client' import LinkIcon from 'public/icons/linkcopy.svg' -import TwoPolaroidsIcon from 'public/icons/twopolaroids.svg' -import Modal from '@/components/Modal' import { useState } from 'react' +import LinkCopiedModal from '@/app/(home)/_components/LinkCopiedModal' +import { copyToClipboard } from '@/lib/utils' const CopyLinkBtn = () => { - const [showLinkCopyModal, setShowLinkCopyModal] = useState(false) + const [isLinkCopiedModalOpen, setIsLinkCopiedModalOpen] = useState(false) - const closeModal = () => setShowLinkCopyModal(false) + const openLinkCopiedModal = () => setIsLinkCopiedModalOpen(true) + const closeLinkCopiedModal = () => setIsLinkCopiedModalOpen(false) - const copyLink = () => { + const copyCurrentUrl = () => { const currentURL = window.location.href - return navigator.clipboard.writeText(currentURL).then(() => { - setShowLinkCopyModal(true) - }) + return copyToClipboard(currentURL).then(openLinkCopiedModal) } return ( @@ -26,17 +25,14 @@ const CopyLinkBtn = () => { type="button" className="rounded-[30px] bg-gray-100 p-3 shadow-[0_4px_8px_0_rgba(0,0,0,0.15)]" aria-label="copy link" - onClick={copyLink} + onClick={copyCurrentUrl} > - - }> - 링크가 복사되었습니다! - {'POLABO를\n 지인들에게도 알려주세요!'} - - - + ) } diff --git a/src/app/(home)/_components/CreateBoardBtn.tsx b/src/app/(home)/_components/CreateBoardBtn.tsx index d40fe03..50baa20 100644 --- a/src/app/(home)/_components/CreateBoardBtn.tsx +++ b/src/app/(home)/_components/CreateBoardBtn.tsx @@ -8,15 +8,15 @@ import GoToLoginModal from './GoToLoginModal' const CreateBoardBtn = () => { const router = useRouter() - const [loginModalOpen, setLoginModalOpen] = useState(false) + const [isLoginModalOpen, setIsLoginModalOpen] = useState(false) const { status } = useSession() - const handleClick = () => { + const handleCreateButtonClick = () => { if (status === 'authenticated') { router.push('/board/create') } else { - setLoginModalOpen(true) + setIsLoginModalOpen(true) } } @@ -25,13 +25,13 @@ const CreateBoardBtn = () => { setLoginModalOpen(false)} + isOpen={isLoginModalOpen} + onClose={() => setIsLoginModalOpen(false)} /> ) diff --git a/src/app/(home)/_components/LinkCopiedModal.tsx b/src/app/(home)/_components/LinkCopiedModal.tsx new file mode 100644 index 0000000..1483958 --- /dev/null +++ b/src/app/(home)/_components/LinkCopiedModal.tsx @@ -0,0 +1,25 @@ +import React from 'react' +import Modal from '@/components/Modal' +import TwoPolaroidsIcon from 'public/icons/twopolaroids.svg' + +interface LinkCopiedModalProps { + isOpen: boolean + onClose: () => void +} + +const LinkCopiedModal = ({ + isOpen, + onClose: handleCloseModal, +}: LinkCopiedModalProps) => { + return ( + + }> + 링크가 복사되었습니다! + {'POLABO를\n 지인들에게도 알려주세요!'} + + + + ) +} + +export default LinkCopiedModal diff --git a/src/app/(onboarding)/signup/_components/steps/BirthDtForm.tsx b/src/app/(onboarding)/signup/_components/steps/BirthDtForm.tsx index 25a70d2..c031e01 100644 --- a/src/app/(onboarding)/signup/_components/steps/BirthDtForm.tsx +++ b/src/app/(onboarding)/signup/_components/steps/BirthDtForm.tsx @@ -41,7 +41,7 @@ const BirthDtForm = ({ {'님의\n생일을 입력해주세요!'}

- 추가 정보를 입력하시면 나에게 딱 맞는 보드 주제를 추천해드려요 :) + 추가 정보를 입력하시면 나에게 딱 맞는 보드 이름을 추천해드려요 :)

diff --git a/src/app/(onboarding)/signup/_components/steps/GenderForm.tsx b/src/app/(onboarding)/signup/_components/steps/GenderForm.tsx index b7c9b76..db60cff 100644 --- a/src/app/(onboarding)/signup/_components/steps/GenderForm.tsx +++ b/src/app/(onboarding)/signup/_components/steps/GenderForm.tsx @@ -37,7 +37,7 @@ const GenderForm = ({ {'을 \n 입력해주세요!'}

- 추가 정보를 입력하시면 나에게 딱 맞는 보드 주제를 추천해드려요 :) + 추가 정보를 입력하시면 나에게 딱 맞는 보드 이름을 추천해드려요 :)

diff --git a/src/app/board/[boardId]/_components/Tutorial/Tooltips.tsx b/src/app/board/[boardId]/_components/Tutorial/Tooltips.tsx index 0c7cdb8..61ea64f 100644 --- a/src/app/board/[boardId]/_components/Tutorial/Tooltips.tsx +++ b/src/app/board/[boardId]/_components/Tutorial/Tooltips.tsx @@ -33,7 +33,7 @@ export const Step2Tooltip = () => { trianglePos="-bottom-[0%] translate-y-[20%]" > - 보드 주제와 맞는 사진 + 보드 이름과 맞는 사진 {`을 올려 \n 보드를 꾸며주세요!`} diff --git a/src/app/board/[boardId]/page.tsx b/src/app/board/[boardId]/page.tsx index 52b8de5..4d8617b 100644 --- a/src/app/board/[boardId]/page.tsx +++ b/src/app/board/[boardId]/page.tsx @@ -56,9 +56,14 @@ const BoardPage = async ({ params }: BoardPageProps) => { const session = await auth() + const background = `/images/boardThemas/${board.options.THEMA}.png` + return ( -
+
@@ -76,6 +81,8 @@ const BoardPage = async ({ params }: BoardPageProps) => { ) } + className="bg-transparent" + shadow={false} /> {board.items.length === 0 ? ( diff --git a/src/app/board/create/_components/BackButton.tsx b/src/app/board/create/_components/BackButton.tsx new file mode 100644 index 0000000..077e3a2 --- /dev/null +++ b/src/app/board/create/_components/BackButton.tsx @@ -0,0 +1,18 @@ +'use client' + +import React from 'react' +import ArrowBackIcon from 'public/icons/arrow_back_ios.svg' +import { useRouter } from 'next/navigation' + +const BackButton = () => { + const router = useRouter() + + return ( + router.back()} + /> + ) +} + +export default BackButton diff --git a/src/app/board/create/_components/BoardAvailabilityCheckModal.tsx b/src/app/board/create/_components/BoardAvailabilityCheckModal.tsx index cfc5864..d9a19ec 100644 --- a/src/app/board/create/_components/BoardAvailabilityCheckModal.tsx +++ b/src/app/board/create/_components/BoardAvailabilityCheckModal.tsx @@ -1,20 +1,13 @@ 'use client' -import { useEffect, useState } from 'react' -import { getBoardAvailableCount } from '@/lib' import Modal from '@/components/Modal' import TwoPolaroidsIcon from 'public/icons/twopolaroids.svg' import { useRouter } from 'next/navigation' +import { useBoardAvailability } from '@/hooks' const BoardAvailabilityCheckModal = () => { - const [showModal, setShowModal] = useState(false) const router = useRouter() - - useEffect(() => { - getBoardAvailableCount().then((availableBoardCount) => { - setShowModal(availableBoardCount === 0) - }) - }, []) + const isBoardAvailable = useBoardAvailability() const redirectToHome = () => { router.replace('/') @@ -23,8 +16,8 @@ const BoardAvailabilityCheckModal = () => { return ( setShowModal(false)} + isOpen={!isBoardAvailable} + onClose={() => {}} > }> diff --git a/src/app/board/create/_components/BoardNameForm.tsx b/src/app/board/create/_components/BoardNameForm.tsx index 3794f8c..6a897ad 100644 --- a/src/app/board/create/_components/BoardNameForm.tsx +++ b/src/app/board/create/_components/BoardNameForm.tsx @@ -2,41 +2,38 @@ import Button from '@/components/Button' import TextInput from '@/components/TextInput' -import { useState, ReactNode } from 'react' - -const MAX_BOARD_NAME_LENGTH = 15 +import { ReactNode } from 'react' +import { useBoardName } from '@/hooks/useBoardName' +import { useRouter } from 'next/navigation' interface BoardNameFormProps { children: ReactNode - createBoard: (title: string) => void } -const BoardNameForm = ({ children, createBoard }: BoardNameFormProps) => { - const [title, setTitle] = useState('') - const [hasError, setHasError] = useState(false) - const isEmpty = title.length === 0 +const BoardNameForm = ({ children }: BoardNameFormProps) => { + const { + boardName, + setBoardName, + isDirty, + isInvalid, + errorMessage, + description, + } = useBoardName() - const onInput = (value: string) => { - setTitle(value) - if (value.length > MAX_BOARD_NAME_LENGTH) { - setHasError(true) - } else { - setHasError(false) - } - } + const router = useRouter() return ( <>
- 보드 주제를 정해주세요! + 보드 이름을 정해주세요!
{children} @@ -44,10 +41,10 @@ const BoardNameForm = ({ children, createBoard }: BoardNameFormProps) => { type="submit" size="lg" className="mb-12" - disabled={hasError || isEmpty} - onClick={() => createBoard(title)} + disabled={isInvalid} + onClick={() => router.push(`/board/create/theme?title=${boardName}`)} > - 완료 + 다음 ) diff --git a/src/app/board/create/page.tsx b/src/app/board/create/page.tsx index 6ed9d51..9a0f496 100644 --- a/src/app/board/create/page.tsx +++ b/src/app/board/create/page.tsx @@ -1,33 +1,21 @@ +import BackButton from '@/app/board/create/_components/BackButton' +import BoardNameRecommendations from '@/app/board/create/_components/BoardNameRecommendations' import Image from 'next/image' import PolaboLogo from 'public/images/polabo_logo.png' -import { postBoard } from '@/lib' -import { revalidateTag } from 'next/cache' -import { redirect } from 'next/navigation' -import BoardNameRecommendations from '@/app/board/create/_components/BoardNameRecommendations' import BoardAvailabilityCheckModal from './_components/BoardAvailabilityCheckModal' import BoardNameForm from './_components/BoardNameForm' const CreateBoardPage = () => { - const createBoard = async (title: string) => { - 'use server' - - const boardId = await postBoard({ - title, - userId: null, - }) - - revalidateTag('myBoard') - redirect(`/board/${boardId}`) - } return ( -
+
+ logo - +
diff --git a/src/app/board/create/theme/_components/ThemaSelect.tsx b/src/app/board/create/theme/_components/ThemaSelect.tsx new file mode 100644 index 0000000..8fda49d --- /dev/null +++ b/src/app/board/create/theme/_components/ThemaSelect.tsx @@ -0,0 +1,88 @@ +'use client' + +import Button from '@/components/Button' +import { BOARDTHEMAS } from '@/lib/constants/boardConfig' +import { BoardThemaKeyType } from '@/types' +import Image from 'next/image' +import CheckIcon from 'public/icons/check.svg' +import { useState } from 'react' +import { twMerge } from 'tailwind-merge' + +interface ThemaSelectItemProps { + themaType: BoardThemaKeyType + isCurrentThema: boolean + setCurrentThema: (thema: BoardThemaKeyType) => void +} + +const ThemaSelectItem = ({ + themaType, + isCurrentThema, + setCurrentThema, +}: ThemaSelectItemProps) => ( +
+ {isCurrentThema && ( + + )} + +
+ polabo setCurrentThema(themaType)} + objectFit="contain" + /> +
+ + {BOARDTHEMAS[themaType].title} + +
+) + +interface ThemaSelectProps { + boardName: string + createBoard: (boardName: string, boardThema: BoardThemaKeyType) => void +} + +const ThemaSelect = ({ createBoard, boardName }: ThemaSelectProps) => { + const [currentThema, setCurrentThema] = useState('B-0') + + return ( +
+
+ {Object.entries(BOARDTHEMAS).map(([key]) => ( + + ))} +
+ +
+ ) +} + +export default ThemaSelect diff --git a/src/app/board/create/theme/page.tsx b/src/app/board/create/theme/page.tsx new file mode 100644 index 0000000..7868046 --- /dev/null +++ b/src/app/board/create/theme/page.tsx @@ -0,0 +1,48 @@ +import Header from '@/components/Header' +import { postBoard } from '@/lib' +import { revalidateTag } from 'next/cache' +import { redirect } from 'next/navigation' +import { BoardThemaKeyType } from '@/types' +import ThemaSelect from './_components/ThemaSelect' + +interface PageProps { + searchParams: { + title: string + } +} + +const CreateBoardThemePage = ({ searchParams }: PageProps) => { + const { title } = searchParams + + const createBoard = async ( + boardName: string, + boardThema: BoardThemaKeyType, + ) => { + 'use server' + + const boardId = await postBoard({ + title: boardName, + userId: null, + options: { + THEMA: boardThema, + }, + }) + + revalidateTag('myBoard') + + redirect(`/board/${boardId}`) + } + + return ( +
+
} + shadow={false} + /> + +
+ ) +} + +export default CreateBoardThemePage diff --git a/src/app/mypage/boards/_components/BoardList/BoardEditPopup.tsx b/src/app/mypage/boards/_components/BoardList/BoardEditPopup.tsx index c286ff2..68f479f 100644 --- a/src/app/mypage/boards/_components/BoardList/BoardEditPopup.tsx +++ b/src/app/mypage/boards/_components/BoardList/BoardEditPopup.tsx @@ -21,7 +21,7 @@ const BoardEditPopup = ({ className="border-b border-b-gray-300 py-2 pl-[22px] text-sm text-gray-950" onClick={clickChangeName} > - 보드 주제 수정하기 + 보드 이름 수정하기
}> - 보드 주제 수정 + 보드 이름 수정
{ return ( <>
{leftButton}
-
+
{title}
diff --git a/src/components/Polaroid/PolaroidCard/index.tsx b/src/components/Polaroid/PolaroidCard/index.tsx index cb71631..895b1cb 100644 --- a/src/components/Polaroid/PolaroidCard/index.tsx +++ b/src/components/Polaroid/PolaroidCard/index.tsx @@ -4,6 +4,7 @@ import PolaroidFrame from '@/components/Polaroid/Base/PolaroidFrame' import PolaroidDescription from '@/components/Polaroid/Base/PolaroidDescription' import PolaroidMessage from '@/components/Polaroid/Base/PolaroidMessage' import PolaroidNickname from '@/components/Polaroid/Base/PolaroidNickname' +import { getPolaroidStyle } from '@/lib/utils/polaroid' interface PolaroidCardProps { polaroid: Polaroid @@ -18,7 +19,7 @@ function PolaroidCard({ polaroid, onClick = () => {} }: PolaroidCardProps) { themaKey={polaroid.options.THEMA} fontKey={polaroid.options.FONT} > -
+
diff --git a/src/components/Polaroid/PolaroidDetail/PolaroidItem.tsx b/src/components/Polaroid/PolaroidDetail/PolaroidItem.tsx index 4a88e9a..c5cc5fe 100644 --- a/src/components/Polaroid/PolaroidDetail/PolaroidItem.tsx +++ b/src/components/Polaroid/PolaroidDetail/PolaroidItem.tsx @@ -1,4 +1,5 @@ import { Polaroid } from '@/types' +import { getPolaroidStyle } from '@/lib/utils/polaroid' import PolaroidDescription from '../Base/PolaroidDescription' import PolaroidFrame from '../Base/PolaroidFrame' import PolaroidImage from '../Base/PolaroidImage' @@ -16,12 +17,15 @@ const PolaroidItem = ({ polaroid }: PolaroidItemProps) => { themaKey={polaroid.options.THEMA} fontKey={polaroid.options.FONT} > -
+
) => void + changeMessage: (e: React.ChangeEvent) => void } const PolaroidMessageInput = ({ @@ -11,17 +12,29 @@ const PolaroidMessageInput = ({ maxLength, changeMessage, }: PolaroidMessageInputProps) => { + const ref = useRef(null) + + useEffect(() => { + if (!ref.current) { + return + } + + ref.current.style.height = 'auto' // 높이를 초기화하여 기존 높이를 제거 + ref.current.style.height = `${ref.current.scrollHeight}px` + }, [message]) + return ( -
- +