diff --git a/src/LecueNote/api/getPresignedUrl.ts b/src/LecueNote/api/getPresignedUrl.ts new file mode 100644 index 00000000..e232a2e6 --- /dev/null +++ b/src/LecueNote/api/getPresignedUrl.ts @@ -0,0 +1,8 @@ +import { api } from '../../libs/api'; + +const getPresignedUrl = async () => { + const { data } = await api.get('/api/images/note'); + return data; +}; + +export default getPresignedUrl; diff --git a/src/LecueNote/api/postLecueNote.ts b/src/LecueNote/api/postLecueNote.ts new file mode 100644 index 00000000..d23b5a36 --- /dev/null +++ b/src/LecueNote/api/postLecueNote.ts @@ -0,0 +1,38 @@ +// import { useNavigate } from 'react-router-dom'; + +import { api } from '../../libs/api'; +import { postLecueNoteProps } from '../type/lecueNoteType'; + +const postLecueNote = ({ + contents, + color, + fileName, + bgColor, +}: postLecueNoteProps) => { + // const navigate = useNavigate(); + + const response = api + .post( + '/api/notes', + { + bookId: 1, + content: contents, + textColor: color, + background: fileName ? fileName : bgColor, + }, + { + headers: { + Authorization: `Bearer ${import.meta.env.VITE_APP_TOKEN}`, + }, + }, + ) + .then((res) => { + console.log(res); + // 나중에 주석코드를 활성화시킬 예정! + // navigate(`lecue-book/${res.data.data.bookUuid}`); + }); + + return response; +}; + +export default postLecueNote; diff --git a/src/LecueNote/api/putPresignedUrl.ts b/src/LecueNote/api/putPresignedUrl.ts new file mode 100644 index 00000000..f09f1c8e --- /dev/null +++ b/src/LecueNote/api/putPresignedUrl.ts @@ -0,0 +1,18 @@ +import { api } from '../../libs/api'; +import { putPresignedUrlProps } from '../type/lecueNoteType'; + +const putPresignedUrl = ({ + presignedUrl, + binaryFile, + fileType, +}: putPresignedUrlProps) => { + const response = api.put(presignedUrl, binaryFile, { + headers: { + 'Content-Type': fileType, + }, + }); + + return response; +}; + +export default putPresignedUrl; diff --git a/src/LecueNote/components/CreateNote/index.tsx b/src/LecueNote/components/CreateNote/index.tsx index 67c5591c..67b42f52 100644 --- a/src/LecueNote/components/CreateNote/index.tsx +++ b/src/LecueNote/components/CreateNote/index.tsx @@ -1,47 +1,22 @@ -import { useState } from 'react'; - -import { - BG_COLOR_CHART, - CATEGORY, - TEXT_COLOR_CHART, -} from '../../constants/colorChart'; import { CreateNoteProps } from '../../type/lecueNoteType'; import SelectColor from '../SelectColor'; import WriteNote from '../WriteNote'; import * as S from './CreateNote.style'; function CreateNote({ + clickedCategory, + clickedBgColor, + clickedTextColor, + isIconClicked, contents, + setFileName, handleChangeFn, + handleClickCategory, + handleClickedColorBtn, + handleClickedIcon, imgFile, uploadImage, }: CreateNoteProps) { - const [clickedCategory, setclickedCategory] = useState(CATEGORY[0]); - const [clickedTextColor, setClickedTextColor] = useState(TEXT_COLOR_CHART[0]); - const [clickedBgColor, setclickedBgColor] = useState(BG_COLOR_CHART[0]); - const [isIconClicked, setIsIconClicked] = useState(false); - - const handleClickCategory = ( - e: React.MouseEvent, - ) => { - setclickedCategory(e.currentTarget.innerHTML); - }; - - const handleClickedColorBtn = ( - e: React.MouseEvent, - ) => { - if (clickedCategory === '텍스트색') { - setClickedTextColor(e.currentTarget.id); - } else { - setclickedBgColor(e.currentTarget.id); - setIsIconClicked(false); - } - }; - - const handleClickedIcon = () => { - setIsIconClicked(true); - }; - return ( { + postMutation.mutate({ + contents: contents, + color: textColor, + fileName: fileName, + bgColor: bgColor, + }); + }; + return ( - diff --git a/src/LecueNote/components/SelectColor/index.tsx b/src/LecueNote/components/SelectColor/index.tsx index 78216575..8163a7e1 100644 --- a/src/LecueNote/components/SelectColor/index.tsx +++ b/src/LecueNote/components/SelectColor/index.tsx @@ -12,6 +12,7 @@ function SelectColor({ clickedCategory, clickedTextColor, clickedBgColor, + setFileName, handleCategoryFn, handleColorFn, handleIconFn, @@ -39,6 +40,7 @@ function SelectColor({ isIconClicked={isIconClicked} colorChart={TEXT_COLOR_CHART} state={clickedTextColor} + setFileName={setFileName} uploadImage={uploadImage} handleFn={handleColorFn} handleIconFn={handleIconFn} @@ -48,6 +50,7 @@ function SelectColor({ isIconClicked={isIconClicked} colorChart={BG_COLOR_CHART} state={clickedBgColor} + setFileName={setFileName} uploadImage={uploadImage} handleFn={handleColorFn} handleIconFn={handleIconFn} diff --git a/src/LecueNote/components/ShowColorChart/index.tsx b/src/LecueNote/components/ShowColorChart/index.tsx index bbd2fdf0..4a68a0e1 100644 --- a/src/LecueNote/components/ShowColorChart/index.tsx +++ b/src/LecueNote/components/ShowColorChart/index.tsx @@ -1,7 +1,9 @@ -import { useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { IcCameraSmall } from '../../../assets'; import { BG_COLOR_CHART } from '../../constants/colorChart'; +import useGetPresignedUrl from '../../hooks/useGetPresignedUrl'; +import usePutPresignedUrl from '../../hooks/usePutPresignedUrl'; import { ShowColorChartProps } from '../../type/lecueNoteType'; import * as S from './ShowColorChart.style'; @@ -9,11 +11,16 @@ function ShowColorChart({ isIconClicked, colorChart, state, + setFileName, uploadImage, handleFn, handleIconFn, }: ShowColorChartProps) { const imgRef = useRef(null); + const [presignedUrl, setPresignedUrl] = useState(''); + const { data } = useGetPresignedUrl(); + // 함수 컴포넌트 내에서 커스텀 훅 호출 시, 에러발생 + const putMutation = usePutPresignedUrl(); const handleImageUpload = () => { const fileInput = imgRef.current; @@ -21,17 +28,37 @@ function ShowColorChart({ if (fileInput && fileInput.files && fileInput.files.length > 0) { const file = fileInput.files[0]; - const reader = new FileReader(); - reader.readAsDataURL(file); + // reader1: 파일을 base64로 읽어서 업로드 + const reader1 = new FileReader(); + reader1.readAsDataURL(file); + reader1.onloadend = () => { + if (reader1.result !== null) { + uploadImage(reader1.result as string); + } + }; - reader.onloadend = () => { - if (reader.result !== null) { - uploadImage(reader.result as string); + // reader2: 파일을 ArrayBuffer로 읽어서 PUT 요청 수행 + const reader2 = new FileReader(); + reader2.readAsArrayBuffer(file); + reader2.onloadend = () => { + if (reader2.result !== null && presignedUrl) { + putMutation.mutate({ + presignedUrl: presignedUrl, + binaryFile: reader2.result, + fileType: file.type, + }); } }; } }; + useEffect(() => { + if (data !== undefined) { + setPresignedUrl(data.data.url); + setFileName(data.data.fileName); + } + }, [data]); + return ( {colorChart === BG_COLOR_CHART && ( diff --git a/src/LecueNote/hooks/useGetPresignedUrl.tsx b/src/LecueNote/hooks/useGetPresignedUrl.tsx new file mode 100644 index 00000000..29b62bc2 --- /dev/null +++ b/src/LecueNote/hooks/useGetPresignedUrl.tsx @@ -0,0 +1,14 @@ +import { useQuery } from 'react-query'; + +import getPresignedUrl from '../api/getPresignedUrl'; + +const useGetPresignedUrl = () => { + const { isLoading, error, data } = useQuery({ + queryKey: ['get-presigned-url'], + queryFn: () => getPresignedUrl(), + }); + + return { isLoading, error, data }; +}; + +export default useGetPresignedUrl; diff --git a/src/LecueNote/hooks/usePostLecueNote.ts b/src/LecueNote/hooks/usePostLecueNote.ts new file mode 100644 index 00000000..0ac36e70 --- /dev/null +++ b/src/LecueNote/hooks/usePostLecueNote.ts @@ -0,0 +1,22 @@ +import { AxiosError } from 'axios'; +import { useMutation } from 'react-query'; + +import postLecueNote from '../api/postLecueNote'; +import { postLecueNoteProps } from '../type/lecueNoteType'; + +const usePostLecueNote = () => { + const mutation = useMutation({ + mutationFn: ({ + contents, + color, + fileName, + bgColor, + }: postLecueNoteProps) => { + return postLecueNote({ contents, color, fileName, bgColor }); + }, + onError: (err: AxiosError) => console.log(err), + }); + return mutation; +}; + +export default usePostLecueNote; diff --git a/src/LecueNote/hooks/usePutPresignedUrl.ts b/src/LecueNote/hooks/usePutPresignedUrl.ts new file mode 100644 index 00000000..83903085 --- /dev/null +++ b/src/LecueNote/hooks/usePutPresignedUrl.ts @@ -0,0 +1,21 @@ +import { AxiosError } from 'axios'; +import { useMutation } from 'react-query'; + +import putPresignedUrl from '../api/putPresignedUrl'; +import { putPresignedUrlProps } from './../type/lecueNoteType'; + +const usePutPresignedUrl = () => { + const mutation = useMutation({ + mutationFn: ({ + presignedUrl, + binaryFile, + fileType, + }: putPresignedUrlProps) => { + return putPresignedUrl({ presignedUrl, binaryFile, fileType }); + }, + onError: (err: AxiosError) => console.log(err), + }); + return mutation; +}; + +export default usePutPresignedUrl; diff --git a/src/LecueNote/page/LeceuNotePage/index.tsx b/src/LecueNote/page/LeceuNotePage/index.tsx index 5974abc4..d9ba845c 100644 --- a/src/LecueNote/page/LeceuNotePage/index.tsx +++ b/src/LecueNote/page/LeceuNotePage/index.tsx @@ -3,12 +3,28 @@ import { useState } from 'react'; import Header from '../../../components/common/Header'; import CreateNote from '../../components/CreateNote'; import Footer from '../../components/Footer'; +import { + BG_COLOR_CHART, + CATEGORY, + TEXT_COLOR_CHART, +} from '../../constants/colorChart'; import * as S from './LecueNotePage.style'; function LecueNotePage() { const MAX_LENGTH = 1000; const [contents, setContents] = useState(''); const [imgFile, setImgFile] = useState(''); + const [clickedCategory, setclickedCategory] = useState(CATEGORY[0]); + const [clickedTextColor, setClickedTextColor] = useState(TEXT_COLOR_CHART[0]); + const [clickedBgColor, setclickedBgColor] = useState(BG_COLOR_CHART[0]); + const [isIconClicked, setIsIconClicked] = useState(false); + const [fileName, setFileName] = useState(''); + + const handleClickCategory = ( + e: React.MouseEvent, + ) => { + setclickedCategory(e.currentTarget.innerHTML); + }; const handleChangeContents = (e: React.ChangeEvent) => { setContents(e.target.value); @@ -18,16 +34,44 @@ function LecueNotePage() { } }; + const handleClickedColorBtn = ( + e: React.MouseEvent, + ) => { + if (clickedCategory === '텍스트색') { + setClickedTextColor(e.currentTarget.id); + } else { + setclickedBgColor(e.currentTarget.id); + setIsIconClicked(false); + } + }; + + const handleClickedIcon = () => { + setIsIconClicked(true); + }; + return (
setImgFile(file)} + setFileName={setFileName} + handleChangeFn={handleChangeContents} + handleClickCategory={handleClickCategory} + handleClickedColorBtn={handleClickedColorBtn} + handleClickedIcon={handleClickedIcon} + /> +