파이어베이스 storage 이미지 업로드 적용, Next.js에서 2번씩 렌더링 되는 이유

Lekosk.dev·2023년 2월 10일
0
post-thumbnail

01. 구현

✅ 결과.

  1. 파이어베이스의 스토리지를 통해 이미지를 업로드 할 수 있다.
  2. 업로드한 이미지의 url을 다운로드하여, 게시글 안에 이미지를 노출시킬 수 있다.
  3. Next.js에서 head(title)를 설정할 수 있다.
  4. 라우트의 isReady를 사용하여, 라우팅이 전부 불러오기 전까지 빈 컴포넌트를 반환할 수 있다.

✅ 스크린샷.

02. 파이어베이스 storage

✅ 이미지 업로드하고 url 받아오기.

export const storage = getStorage(app)

import { storage } from '@/pages/_app'
import { getDownloadURL, ref, uploadBytes } from 'firebase/storage'

    const [imageUrl, setImageUrl] = useState('')

    const onChangeUpload = (event: ChangeEvent<HTMLInputElement>) => {
        if (event.target.files === null) return;
        const imageRef = ref(storage, `images/${event.target.files[0].name}`)
        uploadBytes(imageRef, event.target.files[0])
            .then((snapshot) => {
                getDownloadURL(snapshot.ref)
                    .then((url: string) => {
                        setImageUrl(url)
                    });
            });
    };
    
    <Input type='file' onChange={onChangeUpload} />

공식문서
https://firebase.google.com/docs/storage/web/upload-files?hl=ko&authuser=0

  1. 파이어베이스에서 스토리지를 시작한다.
  2. storage 만들기 : getStorage() 함수를 파이어베이스에서 불러와 내 앱을 인자로 넣는다.
  3. 인풋 만들기 : 이미지를 넣을 인풋의 타입을 파일로 설정한다.
  4. 온체인지 핸들러 만들기 : 이벤트.타겟에 파일이 들어오지 않으면 return 하게 만든다.
  5. 이미지참조 만들기 : ref() 함수에 인자로 내 스토리지, 내가 업로드할 파일의 이름을 넣는다(경로 포함).
  6. 업로드 하기 : uploadBytes() 함수에 내가 방금 만든 참조와 파일 이름을 넣어 업로드한다.
  7. 업로드한 파일의 url을 받기 : getDownloadURL()을 통해 url을 받아 스테이트를 변경해준다.
  8. 썸네일 노출 : 이제 이 스테이트에 담긴 url을 통해 이미지 태그에 src로 썸네일을 노출 시킬수 있다.
  9. 글 작성 : onClcikSubmit 핸들러에 글 작성 시 url도 포함하여 제출한다.

✅ Next.js에서 2번씩 렌더링 되는 이유.

아래의 포스트를 참고하여 해결하였다.

https://velog.io/@rssungjae/React-%EB%91%90%EB%B2%88%EC%94%A9-%EB%A0%8C%EB%8D%94%EB%A7%81%EB%90%98%EB%8A%94-%EA%B2%BD%EC%9A%B0

  • StrictMode 모드 사용.
  • route.query를 사용.
    특히 Next.js는 route를 사용하게 되면 첫 렌더 시 query가 undefinded 상태로 마운트되고, 그 후 query에 값이 채워져서 다시 렌더링된다. isReady를 사용해서 isReady가 false라면 빈 컴포넌트를 반환하게 만들어 query 값이 채워지기 전까지 렌더링을 늦출 수 있었다.

이때 if( !route.isReady ){ return (<></>) } 코드는 useState()보다 하단에 있어야한다.
그렇지 않으면 "React has detected a change in the order of Hooks called" 라는 에러가 뜨게된다.

03. 느낀 점

✅ 인풋 버튼 스타일 변경. (useRef or antd)

파일을 올리는 인풋의 스타일이 마음에 들지 않기 때문에, 기존의 인풋 엘리먼트를 숨기고 useRef 훅스를 사용하여 다른 엘리먼트로 대체하던가, antd를 사용해야겠다.

✅ 썸네일에 대한 궁금증.

글 작성 시, 이미지를 업로드하는 인풋이 바뀔때마다 온체인지 핸들러에 의해 이미지를 서버에 저장하고, 그 url을 받아오게된다. 글을 작성하면서 이미지를 계속 변경하게 되면 계속 서버에 버려지는 이미지 데이터가 누적이 될 텐데, 이를 실무에서 어떻게 관리하는지 궁금하다. 내가 내린 생각은 그냥 누적되게 두는 것같다.

✅ 다음 적용사항.

기능구현

  • 글 작성 시 사진 업로드 후, 썸네일 이미지 보여주기
  • 글 수정, 글 삭제 기능 추가.
  • 피드 페이지에서 검색 기능 추가.(디바운싱 이용)
  • 피드 페이지 무한 스크롤 추가.
  • 통계, 설정 페이지 구현.
  • 날짜 페이지에 공휴일 정보를 표기.(api 이용)
  • 앱 소개(안내) 페이지 추가.
  • 달력 페이지에서 2개 이상 작성된 날짜에 마우스 오버 시, 글 갯수 보여주는 팝업 표시.

스타일

  • 업로드 버튼 교체.
  • 날짜 페이지에서 오늘 날짜, 전날, 다음날로 이동하는 버튼 추가.
  • 오른쪽 어사이드에 과거와 현재, 미래에 해당하는 블럭 컬러 구분.
  • 전체적인 스타일링 개선 (현재는 너무 antd 바닐라 => 영감을 받은 쿠르츠게작트 감성을 살리고 싶다).
  • 스타일링 컨셉 기획 후 다크모드 구현.
  • 로딩시, 로딩 컴포넌트 삽입.
  • 모바일 환경에서 레이아웃 @media 반응형 구현.

리팩토링

  • 홈, 피드, 작성 페이지 상단 UI가 통일되었다. 이 컴포넌트를 분리하여 앞으로 남은 페이지에서도 계속 사용할 수 있다.
  • 현재 컴포넌트는 라우팅(페이지)+기능+UI+타입으로 코드가 합쳐져 있으나, 차후 이를 분리할 예정.
profile
[ 자아 · 관계 · 우주 ] 를 사랑하는 개발자가 되자.

0개의 댓글