nailedReact / bokgungom-market

멋쟁이사자처럼 프론트엔드 스쿨 3기 15조 득근득근 복근곰마켓 레포지토리
https://bokgungom-market.vercel.app/
6 stars 5 forks source link

[FEED] 프로필 이미지 아이콘 포커스 상태로 리사이즈 경우 데스크탑에서도 bottom 50% #338

Closed SEMINSEMINSEMIN closed 1 year ago

SEMINSEMINSEMIN commented 1 year ago

https://user-images.githubusercontent.com/104843477/210065526-fbdd37bd-de38-415d-9d11-f86a8af6e1a2.MOV

라이브러리 사용하고 싶다..

SEMINSEMINSEMIN commented 1 year ago

https://stackoverflow.com/questions/47798279/jquery-mobile-how-to-detect-if-mobile-virtual-keyboard-is-opened

SEMINSEMINSEMIN commented 1 year ago

https://stackoverflow.com/questions/47841986/detecting-the-opening-or-closing-of-a-virtual-keyboard-on-a-touchscreen-device

SEMINSEMINSEMIN commented 1 year ago

if ('visualViewport' in window) { const VIEWPORT_VS_CLIENT_HEIGHT_RATIO = 0.75; window.visualViewport.addEventListener('resize', function (event) { if ( (event.target.height * event.target.scale) / window.screen.height < VIEWPORT_VS_CLIENT_HEIGHT_RATIO ) console.log('keyboard is shown'); else console.log('keyboard is hidden'); }); }

github-actions[bot] commented 1 year ago

Branch issue-338 created for issue: [FEED] 프로필 이미지 아이콘 텍스트에리어 길어질 경우 위치 이상함 + 포커스 상태로 리사이즈 경우 데스크탑에서도 bottom 50%

SEMINSEMINSEMIN commented 1 year ago
import { useState, useRef, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import TopBar from "../../components/TopBar";
import useAuth from "../../hook/useAuth";
import { PostEditWrapper } from "../../components/postEditWrapper.style";
import { ProductImgSetCont } from "../../components/ProductImageSet/productImageSet.style";
import { ImgUploadIcon } from "../../components/ImageUpload/imageUpload.style";
import { UserProfileImg } from "../../components/postEditUserProfile.style";
import Textarea from "../../components/Textarea/Textarea";
import { Contentimg } from "../../components/postEditContentImg.style";
import basicImg from "../../assets/basic-profile-img.png";
import deleteIcon from "../../assets/icon/icon-delete.png";
import useWindowSizeCustom from "../../hook/windowSize";
import NavBar from "../../components/NavBar/NavBar";
import Toast from "../../components/Toast";

let fileUrls = [];

export default function UploadPost() {
    const [isBtnDisable, setIsBtnDisable] = useState(true);
    const [showImages, setShowImages] = useState([]);
    const [isFocused, setIsFocused] = useState();
    const imagePre = useRef(null);
    const textarea = useRef();
    const fileInpRef = useRef(null);
    const navigate = useNavigate();
    const data = useAuth();
    const toastRef = useRef(null);
    const fileLabelRef =useRef();

    // 화면 사이즈 변경 훅
    const { width } = useWindowSizeCustom();

    // 뒤로 가기, 또는 페이지 전환시 혹시라도 남아있을 fileURL, fileInpRef.current.value 제거 위해
    useEffect(() => {
        fileInpRef.current.value = null;
        fileUrls = [];
    }, []);

    useEffect(() => {
        const handleResize = (e) => {
            if (window.innerWidth < 768) {
                console.log(`window.innerHeight: ${window.innerHeight}`);
                console.log(`window.visualViewport: ${e.target.height}`);
                if (window.innerHeight !== e.target.height) {
                    fileLabelRef.current.style.bottom = "50%";
                } else {
                    fileLabelRef.current.style.bottom = "5.51%";
                }
                // if (isFocused) {
                //     fileLabelRef.current.style.bottom = "50%";
                // } else {
                //     fileLabelRef.current.style.bottom = "8.51%";
                // }
            }
        };

        window.visualViewport.addEventListener("resize", handleResize)

        return () => window.visualViewport.removeEventListener("resize", handleResize);
    }, [])

    const handleFocus = () => {
        setIsFocused(true);
    }

    const handleBlur = () => {
        setIsFocused(false);
    }

    // textarea 자동 높이 조절
    const handleTextarea = (e) => {
        textarea.current.style.height = "auto";
        textarea.current.style.height = textarea.current.scrollHeight + "px";
        if (e.target.value.length === 0 && showImages.length === 0) {
            setIsBtnDisable(true);
        } else {
            setIsBtnDisable(false);
        }
        // 글자 수 제한 테스트 중입니다.
        // let text = e.target.value;
        // let text_length = text.length;
        // setContentText(text);

        // let max_length = 100;
        // if (text_length > max_length) {
        //     text = text.substring(0, 100);
        //     alert(100 + "자 이상 작성할 수 없습니다.");
        // }
    };

    // const [visualViewport, setVisualViewport] = useState();

    // 이미지 미리보기
    let previewUrl = [];
    const handleAddImages = (event) => {
        if (
            fileUrls.length + 
            fileInpRef.current.files.length <=
            3
        ) {
            const imageFiles = [...fileInpRef.current.files];
            fileUrls.push(...imageFiles);

            for (let i = 0; i < fileUrls.length; i++) {
                let file = fileUrls[i];
                const fileReader = new FileReader();
                fileReader.onload = () => {
                    previewUrl.push(fileReader.result);
                    setShowImages([...previewUrl]);
                };
                fileReader.readAsDataURL(file);
            }
            setIsBtnDisable(false);
            fileInpRef.current.value = null;
        } else {
            alert("이미지는 3개까지 업로드 할 수 있습니다.");
        }
    };

    // 이미지 미리보기 삭제
    const handleDeleteImage = (id) => {
        setShowImages(showImages.filter((_, index) => index !== id));

        if (!textarea.current.value && showImages.length === 1) {
            setIsBtnDisable(true);
        };

        fileInpRef.current.value = null;

        fileUrls = fileUrls.filter((_, index) => index !== id);
    };

    // const postImgName = [];
    // 이미지 서버에 전송
    const uploadImg = async (file) => {
        const formData = new FormData();
        formData.append("image", file);

        try {
            const res = await fetch(
                "https://mandarin.api.weniv.co.kr/image/uploadfiles",
                {
                    method: "POST",
                    body: formData,
                }
            );
            const json = await res.json();
            console.log(json);

            const postImgName = json[0].filename;
            return postImgName;
        } catch (error) {
            console.error(error);
        }
    };

    // 저장 버튼 클릭 시 텍스트, 이미지 값 서버에 전송. 이미지는 서버에 있는 데이터를 가져와서 전송.
    const CreatePost = async function (e) {
        e.preventDefault();
        const url = "https://mandarin.api.weniv.co.kr/post";
        const imgUrls = [];

        try {
            for (const file of fileUrls) {
                imgUrls.push(
                    "https://mandarin.api.weniv.co.kr/" +
                        (await uploadImg(file))
                );
            }

            const productData = {
                post: {
                    content: textarea.current.value,
                    image: imgUrls.join(","),
                },
            };

            const response = await fetch(url, {
                method: "POST",
                headers: {
                    Authorization: localStorage.getItem("Authorization"),
                    "Content-type": "application/json",
                },
                body: JSON.stringify(productData),
            });
            const json = await response.json();

            console.log(json);
            console.log("게시글 등록 완료");

            // 게시글이 없다면 오류 alert
            if (json.message) {
                alert(json.message);
            } else {
                // 게시글 등록 성공하면 본인 프로필 페이지로 이동
                handleShowToast();
                setTimeout(function(){
                    navigate(`/account/profile/${json.post.author.accountname}`);
                }, 1000)
            }
        } catch (error) {
            console.error(error);
        }
    };

    const handleShowToast = () => {
        toastRef.current.style.transform = "scale(1)";
        setTimeout(function(){
            toastRef.current.style.transform = "scale(0)";
        }, 3000)
        return;
    }

    return (
        <>
            <TopBar
                type="A4"
                right4Ctrl={{ form: "postUpload", isDisabled: isBtnDisable }}
            />
            <Toast ref={toastRef} msg="게시글이 업로드 되었습니다!"/>
            <PostEditWrapper>
                <UserProfileImg
                    src={data ? data.image : basicImg}
                    alt="게시글 작성자 프로필 사진"
                />
                <form
                    style={{ flexBasis: "304px", height: "100%"}}
                    action=""
                    id={"postUpload"}
                    onSubmit={CreatePost}
                >
                    <ProductImgSetCont htmlFor="productImg">
                        {/* <ExReport/> */}
                        <Textarea
                            placeholder="게시글 입력하기..."
                            onChange={handleTextarea}
                            ref={textarea}
                            onFocus={handleFocus}
                            onBlur={handleBlur}
                            rows={1}
                        />
                        {/* 이미지 표시하는게 label 안에 있어도 되나? */}
                        {showImages.map((image, id) => (
                            <div className="each-image-cont" key={id}>
                                <Contentimg
                                    src={image}
                                    alt={`${image}-${id}`}
                                    ref={imagePre}
                                />
                                <button
                                    className="delete-btn"
                                    type="button"
                                    onClick={() => handleDeleteImage(id)}
                                >
                                    <img src={deleteIcon} alt="이미지 삭제" />
                                </button>
                            </div>
                        ))}
                    </ProductImgSetCont>
                    <ImgUploadIcon className={"orange small location "} ref={fileLabelRef}>
                        <span className="ir">이미지 첨부</span>
                        <input
                            multiple
                            className="ir"
                            ref={fileInpRef}
                            type="file"
                            accept="image/jpg, image/gif, image/png, image/jpeg, image/bmp, image/tif, image/heic"
                            onChange={handleAddImages}
                        />
                    </ImgUploadIcon>
                </form>
            </PostEditWrapper>
            {width >= 768 ? <NavBar /> : <></>}
        </>
    );
}
SEMINSEMINSEMIN commented 1 year ago

VisualViewport resize시 텍스트에리어의 높이를 조정하는 방법을 썼을 때 아이콘은 잘 움직이긴 하는데 이제 긓 입력하고 다시 수정하려고 클릭하면 페이지 전체가 올라가버림

그리고 이 방법의 단점은 스크롤이 두 개 생긴다는 거

SEMINSEMINSEMIN commented 1 year ago

https://stackoverflow.com/questions/60797340/ios-safari-prevent-or-control-scroll-on-input-focus

SEMINSEMINSEMIN commented 1 year ago

아니면 키보드 업일때 position static으로 하기...???

SEMINSEMINSEMIN commented 1 year ago

문서에 스크롤이 생길 경우 스크롤 탑을 0으로

동작은 하긴 하는데 유저 입장에서 불편할거 같다(왜냐하면 텍스트에리어 외의 다른 영역 스크롤시 스크롤 탑이 자꾸 바뀌니까)

import { useState, useRef, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import TopBar from "../../components/TopBar";
import useAuth from "../../hook/useAuth";
import { PostEditWrapper } from "../../components/postEditWrapper.style";
import { ProductImgSetCont } from "../../components/ProductImageSet/productImageSet.style";
import { ImgUploadIcon } from "../../components/ImageUpload/imageUpload.style";
import { UserProfileImg } from "../../components/postEditUserProfile.style";
import Textarea from "../../components/Textarea/Textarea";
import { Contentimg } from "../../components/postEditContentImg.style";
import basicImg from "../../assets/basic-profile-img.png";
import deleteIcon from "../../assets/icon/icon-delete.png";
import useWindowSizeCustom from "../../hook/windowSize";
import NavBar from "../../components/NavBar/NavBar";
import Toast from "../../components/Toast";

let fileUrls = [];

export default function UploadPost() {
    const [isBtnDisable, setIsBtnDisable] = useState(true);
    const [showImages, setShowImages] = useState([]);
    const [isFocused, setIsFocused] = useState();
    const [visualVh, setVisualVh] = useState();
    const imagePre = useRef(null);
    const textarea = useRef();
    const fileInpRef = useRef(null);
    const navigate = useNavigate();
    const data = useAuth();
    const toastRef = useRef(null);
    const fileLabelRef =useRef();

    // 화면 사이즈 변경 훅
    const { width } = useWindowSizeCustom();

    // 뒤로 가기, 또는 페이지 전환시 혹시라도 남아있을 fileURL, fileInpRef.current.value 제거 위해
    useEffect(() => {
        fileInpRef.current.value = null;
        fileUrls = [];
    }, []);

    useEffect(() => {
        const handleResize = (e) => {
            // if (window.innerWidth < 768) {
            //     if (window.innerHeight !== e.target.height) {
            //         fileLabelRef.current.style.bottom = "50%";
            //     } else {
            //         fileLabelRef.current.style.bottom = "5.51%";
            //     }
            //     // if (isFocused) {
            //     //     fileLabelRef.current.style.bottom = "50%";
            //     // } else {
            //     //     fileLabelRef.current.style.bottom = "8.51%";
            //     // }
            // }
            setVisualVh(e.target.height);
        };

        window.visualViewport.addEventListener("resize", handleResize)

        return () => window.visualViewport.removeEventListener("resize", handleResize);
    }, [])

    const handleFocus = (e) => {
        setIsFocused(true);
        // setIsFocused(true);
        // const end = textarea.current.value.length;
        // textarea.setSelectionRange(end, end);
        // textarea.focus();
        // e.target.scrollTop = e.target.scollHeight;
    }

    useEffect(() => {
        const scrollHandler = () => {
            console.log(document.documentElement.scrollTop);
            document.documentElement.scrollTop = 0;
        };

        document.addEventListener("scroll", scrollHandler);

        return () => document.removeEventListener("resize", scrollHandler);
    }, []);

    const handleBlur = () => {
        setIsFocused(false);
    }

    // textarea 자동 높이 조절
    const handleTextarea = (e) => {
        // textarea.current.style.height = "auto";
        // textarea.current.style.height = textarea.current.scrollHeight + "px";
        // console.log(window.visualViewport);
        // console.log(textarea.current.style.height);
        if (e.target.value.length === 0 && showImages.length === 0) {
            setIsBtnDisable(true);
        } else {
            setIsBtnDisable(false);
        }
        // 글자 수 제한 테스트 중입니다.
        // let text = e.target.value;
        // let text_length = text.length;
        // setContentText(text);

        // let max_length = 100;
        // if (text_length > max_length) {
        //     text = text.substring(0, 100);
        //     alert(100 + "자 이상 작성할 수 없습니다.");
        // }
    };

    // const [visualViewport, setVisualViewport] = useState();

    // 이미지 미리보기
    let previewUrl = [];
    const handleAddImages = (event) => {
        if (
            fileUrls.length + 
            fileInpRef.current.files.length <=
            3
        ) {
            const imageFiles = [...fileInpRef.current.files];
            fileUrls.push(...imageFiles);

            for (let i = 0; i < fileUrls.length; i++) {
                let file = fileUrls[i];
                const fileReader = new FileReader();
                fileReader.onload = () => {
                    previewUrl.push(fileReader.result);
                    setShowImages([...previewUrl]);
                };
                fileReader.readAsDataURL(file);
            }
            setIsBtnDisable(false);
            fileInpRef.current.value = null;
        } else {
            alert("이미지는 3개까지 업로드 할 수 있습니다.");
        }
    };

    // 이미지 미리보기 삭제
    const handleDeleteImage = (id) => {
        setShowImages(showImages.filter((_, index) => index !== id));

        if (!textarea.current.value && showImages.length === 1) {
            setIsBtnDisable(true);
        };

        fileInpRef.current.value = null;

        fileUrls = fileUrls.filter((_, index) => index !== id);
    };

    // const postImgName = [];
    // 이미지 서버에 전송
    const uploadImg = async (file) => {
        const formData = new FormData();
        formData.append("image", file);

        try {
            const res = await fetch(
                "https://mandarin.api.weniv.co.kr/image/uploadfiles",
                {
                    method: "POST",
                    body: formData,
                }
            );
            const json = await res.json();
            console.log(json);

            const postImgName = json[0].filename;
            return postImgName;
        } catch (error) {
            console.error(error);
        }
    };

    // 저장 버튼 클릭 시 텍스트, 이미지 값 서버에 전송. 이미지는 서버에 있는 데이터를 가져와서 전송.
    const CreatePost = async function (e) {
        e.preventDefault();
        const url = "https://mandarin.api.weniv.co.kr/post";
        const imgUrls = [];

        try {
            for (const file of fileUrls) {
                imgUrls.push(
                    "https://mandarin.api.weniv.co.kr/" +
                        (await uploadImg(file))
                );
            }

            const productData = {
                post: {
                    content: textarea.current.value,
                    image: imgUrls.join(","),
                },
            };

            const response = await fetch(url, {
                method: "POST",
                headers: {
                    Authorization: localStorage.getItem("Authorization"),
                    "Content-type": "application/json",
                },
                body: JSON.stringify(productData),
            });
            const json = await response.json();

            console.log(json);
            console.log("게시글 등록 완료");

            // 게시글이 없다면 오류 alert
            if (json.message) {
                alert(json.message);
            } else {
                // 게시글 등록 성공하면 본인 프로필 페이지로 이동
                handleShowToast();
                setTimeout(function(){
                    navigate(`/account/profile/${json.post.author.accountname}`);
                }, 1000)
            }
        } catch (error) {
            console.error(error);
        }
    };

    const handleShowToast = () => {
        toastRef.current.style.transform = "scale(1)";
        setTimeout(function(){
            toastRef.current.style.transform = "scale(0)";
        }, 3000)
        return;
    }

    return (
        <>
            <TopBar
                type="A4"
                right4Ctrl={{ form: "postUpload", isDisabled: isBtnDisable }}
            />
            <Toast ref={toastRef} msg="게시글이 업로드 되었습니다!"/>
            <PostEditWrapper                             visualVh={visualVh ? visualVh + "px" : "100vh"}>
                <UserProfileImg
                    src={data ? data.image : basicImg}
                    alt="게시글 작성자 프로필 사진"
                />
                <form
                    style={{ flexBasis: "304px", height: "100%"}}
                    action=""
                    id={"postUpload"}
                    onSubmit={CreatePost}
                >
                    <ProductImgSetCont htmlFor="productImg">
                        {/* <ExReport/> */}
                        <Textarea
                            placeholder="게시글 입력하기..."
                            onChange={handleTextarea}
                            ref={textarea}
                            onFocus={handleFocus}
                            onBlur={handleBlur}
                            rows={1}
                        />
                        {/* 이미지 표시하는게 label 안에 있어도 되나? */}
                        {showImages.map((image, id) => (
                            <div className="each-image-cont" key={id}>
                                <Contentimg
                                    src={image}
                                    alt={`${image}-${id}`}
                                    ref={imagePre}
                                />
                                <button
                                    className="delete-btn"
                                    type="button"
                                    onClick={() => handleDeleteImage(id)}
                                >
                                    <img src={deleteIcon} alt="이미지 삭제" />
                                </button>
                            </div>
                        ))}
                    </ProductImgSetCont>
                    <ImgUploadIcon className={"orange small location "} ref={fileLabelRef}>
                        <span className="ir">이미지 첨부</span>
                        <input
                            multiple
                            className="ir"
                            ref={fileInpRef}
                            type="file"
                            accept="image/jpg, image/gif, image/png, image/jpeg, image/bmp, image/tif, image/heic"
                            onChange={handleAddImages}
                        />
                    </ImgUploadIcon>
                </form>
            </PostEditWrapper>
            {width >= 768 ? <NavBar /> : <></>}
        </>
    );
}
SEMINSEMINSEMIN commented 1 year ago
import { useState, useRef, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import TopBar from "../../components/TopBar";
import useAuth from "../../hook/useAuth";
import { PostEditWrapper } from "../../components/postEditWrapper.style";
import { ProductImgSetCont } from "../../components/ProductImageSet/productImageSet.style";
import { ImgUploadIcon } from "../../components/ImageUpload/imageUpload.style";
import { UserProfileImg } from "../../components/postEditUserProfile.style";
import Textarea from "../../components/Textarea/Textarea";
import { Contentimg } from "../../components/postEditContentImg.style";
import basicImg from "../../assets/basic-profile-img.png";
import deleteIcon from "../../assets/icon/icon-delete.png";
import useWindowSizeCustom from "../../hook/windowSize";
import NavBar from "../../components/NavBar/NavBar";
import Toast from "../../components/Toast";

let fileUrls = [];

export default function UploadPost() {
    const [isBtnDisable, setIsBtnDisable] = useState(true);
    const [showImages, setShowImages] = useState([]);
    const [isFocused, setIsFocused] = useState();
    const [visualVh, setVisualVh] = useState();
    const imagePre = useRef(null);
    const textarea = useRef();
    const fileInpRef = useRef(null);
    const navigate = useNavigate();
    const data = useAuth();
    const toastRef = useRef(null);
    const fileLabelRef =useRef();

    // 화면 사이즈 변경 훅
    const { width } = useWindowSizeCustom();

    // 뒤로 가기, 또는 페이지 전환시 혹시라도 남아있을 fileURL, fileInpRef.current.value 제거 위해
    useEffect(() => {
        fileInpRef.current.value = null;
        fileUrls = [];
    }, []);

    useEffect(() => {
        const handleResize = (e) => {
            // if (window.innerWidth < 768) {
            //     if (window.innerHeight !== e.target.height) {
            //         fileLabelRef.current.style.bottom = "50%";
            //     } else {
            //         fileLabelRef.current.style.bottom = "5.51%";
            //     }
            //     // if (isFocused) {
            //     //     fileLabelRef.current.style.bottom = "50%";
            //     // } else {
            //     //     fileLabelRef.current.style.bottom = "8.51%";
            //     // }
            // }
            setVisualVh(e.target.height);
        };

        window.visualViewport.addEventListener("resize", handleResize)

        return () => window.visualViewport.removeEventListener("resize", handleResize);
    }, [])

    const handleFocus = (e) => {
        setIsFocused(true);
        // setIsFocused(true);
        // const end = textarea.current.value.length;
        // textarea.setSelectionRange(end, end);
        // textarea.focus();
        // e.target.scrollTop = e.target.scollHeight;
    }

    useEffect(() => {
        const scrollHandler = () => {
            if (window.visualViewport.height < window.innerHeight) {
                console.log(document.documentElement.scrollTop);
                document.documentElement.scrollTop = 0;
            }
        };

        document.addEventListener("scroll", scrollHandler);

        return () => document.removeEventListener("resize", scrollHandler);
    }, []);

    const handleBlur = () => {
        setIsFocused(false);
    }

    // textarea 자동 높이 조절
    const handleTextarea = (e) => {
        // textarea.current.style.height = "auto";
        // textarea.current.style.height = textarea.current.scrollHeight + "px";
        // console.log(window.visualViewport);
        // console.log(textarea.current.style.height);
        if (e.target.value.length === 0 && showImages.length === 0) {
            setIsBtnDisable(true);
        } else {
            setIsBtnDisable(false);
        }
        // 글자 수 제한 테스트 중입니다.
        // let text = e.target.value;
        // let text_length = text.length;
        // setContentText(text);

        // let max_length = 100;
        // if (text_length > max_length) {
        //     text = text.substring(0, 100);
        //     alert(100 + "자 이상 작성할 수 없습니다.");
        // }
    };

    // const [visualViewport, setVisualViewport] = useState();

    // 이미지 미리보기
    let previewUrl = [];
    const handleAddImages = (event) => {
        if (
            fileUrls.length + 
            fileInpRef.current.files.length <=
            3
        ) {
            const imageFiles = [...fileInpRef.current.files];
            fileUrls.push(...imageFiles);

            for (let i = 0; i < fileUrls.length; i++) {
                let file = fileUrls[i];
                const fileReader = new FileReader();
                fileReader.onload = () => {
                    previewUrl.push(fileReader.result);
                    setShowImages([...previewUrl]);
                };
                fileReader.readAsDataURL(file);
            }
            setIsBtnDisable(false);
            fileInpRef.current.value = null;
        } else {
            alert("이미지는 3개까지 업로드 할 수 있습니다.");
        }
    };

    // 이미지 미리보기 삭제
    const handleDeleteImage = (id) => {
        setShowImages(showImages.filter((_, index) => index !== id));

        if (!textarea.current.value && showImages.length === 1) {
            setIsBtnDisable(true);
        };

        fileInpRef.current.value = null;

        fileUrls = fileUrls.filter((_, index) => index !== id);
    };

    // const postImgName = [];
    // 이미지 서버에 전송
    const uploadImg = async (file) => {
        const formData = new FormData();
        formData.append("image", file);

        try {
            const res = await fetch(
                "https://mandarin.api.weniv.co.kr/image/uploadfiles",
                {
                    method: "POST",
                    body: formData,
                }
            );
            const json = await res.json();
            console.log(json);

            const postImgName = json[0].filename;
            return postImgName;
        } catch (error) {
            console.error(error);
        }
    };

    // 저장 버튼 클릭 시 텍스트, 이미지 값 서버에 전송. 이미지는 서버에 있는 데이터를 가져와서 전송.
    const CreatePost = async function (e) {
        e.preventDefault();
        const url = "https://mandarin.api.weniv.co.kr/post";
        const imgUrls = [];

        try {
            for (const file of fileUrls) {
                imgUrls.push(
                    "https://mandarin.api.weniv.co.kr/" +
                        (await uploadImg(file))
                );
            }

            const productData = {
                post: {
                    content: textarea.current.value,
                    image: imgUrls.join(","),
                },
            };

            const response = await fetch(url, {
                method: "POST",
                headers: {
                    Authorization: localStorage.getItem("Authorization"),
                    "Content-type": "application/json",
                },
                body: JSON.stringify(productData),
            });
            const json = await response.json();

            console.log(json);
            console.log("게시글 등록 완료");

            // 게시글이 없다면 오류 alert
            if (json.message) {
                alert(json.message);
            } else {
                // 게시글 등록 성공하면 본인 프로필 페이지로 이동
                handleShowToast();
                setTimeout(function(){
                    navigate(`/account/profile/${json.post.author.accountname}`);
                }, 1000)
            }
        } catch (error) {
            console.error(error);
        }
    };

    const handleShowToast = () => {
        toastRef.current.style.transform = "scale(1)";
        setTimeout(function(){
            toastRef.current.style.transform = "scale(0)";
        }, 3000)
        return;
    }

    return (
        <>
            <TopBar
                type="A4"
                right4Ctrl={{ form: "postUpload", isDisabled: isBtnDisable }}
            />
            <Toast ref={toastRef} msg="게시글이 업로드 되었습니다!"/>
            <PostEditWrapper                             visualVh={visualVh ? visualVh + "px" : "100vh"}>
                <UserProfileImg
                    src={data ? data.image : basicImg}
                    alt="게시글 작성자 프로필 사진"
                />
                <form
                    style={{ flexBasis: "304px", height: "100%"}}
                    action=""
                    id={"postUpload"}
                    onSubmit={CreatePost}
                >
                    <ProductImgSetCont htmlFor="productImg">
                        {/* <ExReport/> */}
                        <Textarea
                            placeholder="게시글 입력하기..."
                            onChange={handleTextarea}
                            ref={textarea}
                            onFocus={handleFocus}
                            onBlur={handleBlur}
                            rows={1}
                        />
                        {/* 이미지 표시하는게 label 안에 있어도 되나? */}
                        {showImages.map((image, id) => (
                            <div className="each-image-cont" key={id}>
                                <Contentimg
                                    src={image}
                                    alt={`${image}-${id}`}
                                    ref={imagePre}
                                />
                                <button
                                    className="delete-btn"
                                    type="button"
                                    onClick={() => handleDeleteImage(id)}
                                >
                                    <img src={deleteIcon} alt="이미지 삭제" />
                                </button>
                            </div>
                        ))}
                    </ProductImgSetCont>
                    <ImgUploadIcon className={"orange small location "} ref={fileLabelRef}>
                        <span className="ir">이미지 첨부</span>
                        <input
                            multiple
                            className="ir"
                            ref={fileInpRef}
                            type="file"
                            accept="image/jpg, image/gif, image/png, image/jpeg, image/bmp, image/tif, image/heic"
                            onChange={handleAddImages}
                        />
                    </ImgUploadIcon>
                </form>
            </PostEditWrapper>
            {width >= 768 ? <NavBar /> : <></>}
        </>
    );
}