[TIL 0410] 이미지 등록하기

zitto·2023년 4월 10일
0

TIL

목록 보기
37/77
post-thumbnail

// BoardWrite.container.tsx
const [fileUrls, setFileUrls] = useState(["", "", ""]);
// BoardWrite.presenter.tsx
<S.ImageWrapper>
  <S.Label>사진첨부</S.Label>
  {props.fileUrls.map((el, index) => (
    <Uploads01
      key={uuidv4()}
      index={index}
      fileUrl={el}
      onChangeFileUrls={props.onChangeFileUrls}
    />
  ))}
</S.ImageWrapper>

3개의 이미지 첨부 버튼이 화면에 노출되도록 하려면,
map을 실행하는 배열의 길이가 일정하게 3이어야 한다.
이를 위해 fileUrls라는 state의 초기값에 빈 문자열이 3개 들어가 있는 배열을 넣어준다.
빈문자열 3개를 props로 전달해줘서 map으로 3개가 그려지고
각 빈문자열이 el이 된다.

이제 화면에는 Uploads01이라는 컴포넌트가 배열의 길이 수 만큼 그려진다
또한, Uploads01 컴포넌트의 key에는 uuid를 이용하여 고유한 id 값을 넣어준다.

uuid Docs : https://www.npmjs.com/package/uuid


  • Uploads.01.container.js
import { useMutation } from "@apollo/client";
import { useRef } from "react";
import type { ChangeEvent } from "react";
import { checkValidationImage } from "./Uploads01.validation";
import Uploads01UI from "./Uploads01.presenter";
import type { IUploads01Props } from "./Uploads01.types";
import { UPLOAD_FILE } from "./Uploads01.queries";
import { Modal } from "antd";
export default function Uploads01(props: IUploads01Props): JSX.Element {
  const fileRef = useRef<HTMLInputElement>(null);
  const [uploadFile] = useMutation(UPLOAD_FILE);
  // onClickUpload를 실행하면 fileRef가 click 된다
  const onClickUpload = (): void => {
    fileRef.current?.click();
  };
  // input에 파일이 첨부될 경우 작동하는 함수
  const onChangeFile = async (
    event: ChangeEvent<HTMLInputElement>
  ): Promise<void> => {
    // 이미지 적합성 체크 (Uploads01.validation.ts에서 함수 import)
    const file = event.target.files?.[0];
    const isValid = checkValidationImage(file);
    if (!isValid) return;
    try {
      // 첨부된 파일을 구글 스토리지에 업로드 후 url를 반환받는다.
      const result = await uploadFile({ variables: { file } });
      // BoardWrite에서 props로 받아온 onChangeFileUrls 함수에 스토리지에 업로드가 완료된 file의 url을 넘겨준다.
      props.onChangeFileUrls(result.data.uploadFile.url, props.index);
    } catch (error) {
      if (error instanceof Error) Modal.error({ content: error.message });
    }
  };
  return (
    <Uploads01UI
      fileRef={fileRef}
      fileUrl={props.fileUrl}
      defaultFileUrl={props.defaultFileUrl}
      onClickUpload={onClickUpload}
      onChangeFile={onChangeFile}
    />
  );
}


onChangeFile 작동해서 백엔드에 전달
백엔드는 클라우드에 전달
백엔드로부터 URL 받음
트라이앵글 구조!

그렇게 받아온 props.fileurl을 다시 fileurl에 담아서 넘겨줌.

  • Uploads.01.presenter.js
import {
  UploadButton,
  UploadFileHidden,
  UploadImage,
} from "./Uploads01.styles";
import type { IUploads01UIProps } from "./Uploads01.types";
export default function Uploads01UI(props: IUploads01UIProps): JSX.Element {
  return (
    <>
      {props.fileUrl !== "" ? (
        <UploadImage
          onClick={props.onClickUpload}
          src={`https://storage.googleapis.com/${props.fileUrl}`}
        />
      ) : (
        <UploadButton onClick={props.onClickUpload}>
          <>+</>
          <>Upload</>
        </UploadButton>
      )}
      <UploadFileHidden
        type="file"
        ref={props.fileRef}
        onChange={props.onChangeFile}
      />
    </>
  );
}

사진을 바꾸게 될 수도 있으니
버튼에도 onClickUpload 해주는 것임
하나하나당 container와 presenter가 있는 것임

  • BoardWrite.container.tsx

    BoardWrite에서 버튼을 클릭했을 때, 해당 버튼의 index를 props로 함께 넘겨준다.
    그리고 onChangeFileUrls 함수는, 배열로 이루어진 fileUrls의 몇 번째 index를 교체할 것 인지를 매개변수로 받아오게 된다.
fileUrls[index] = fileUrl
setFileUrls(fileUrls)

이렇게 실행하면 작동 X !!
얕은복사로 가능하게 한다.
여기서부터 드릴링이 시작된 것임

이렇게 하면, 파일 업로드 시 FileUrls라는 state의 특정 index 값구글 스토리지에 올라간 이미지 파일의 url로 바뀌게 된다.
그리고 변경 완료된 fileUrls를 useMutation 요청 시 함께 보내면
게시글 등록 시 첨부된 이미지의 url도 함께 DB에 저장된다.

  • BoardDetail.presenter.tsx
    받아온 images 배열 데이터를 filter와 map을 이용하여 페이지에 출력한다.
    이 때, el이 빈 값이 아닌 경우에만 이미지가 그려지도록 filter 조건을 설정해줌.


    fileUrls[index] = url
    주소가 같기때문에 얕은 복사가 필요하다.

    state 바뀌었으니 리렌더링 되고
    fileUrls가 presenter로 넘어가게 되고
    presenter도 리렌더되면서

    그걸 바탕으로 업로드가 실행됨.
    이게 Upload.container / presenter 로 날아간다.
    한페이지 말고 분리해서 하므로 나중에 재사용이 가능함!

수정 시에는 이미지에 defalutvalue를 어떻게 줄 것인가?
인풋타입이 아니기 때문에 defalutvalue라는 속성은 없음
따라서 이미 초기값은 빈문자열로 만들어져 있기 때문에
data에 담아서 넣어도 안바뀜
useEffect 사용!
의존성배열에 넣어서 데이터가 바뀌었다면
거기서 이미를 뽑아서 가져오고
추가적인 (한 번더) 리렌더가 발생한다.

profile
JUST DO WHATEVER

0개의 댓글