Nextjs13 + React-Swiper 이미지 로드안됨

|

브라우저 창 크기를 한번 조정해줘야

Nextjs에서 Image태그를 사용해줬는데, 브라우저 창 크기를 한번 조정해줘야 이미지가 로드되었다.
새로고침을 해도 무조건 브라우저 창 크기를 한번 조정해줘야 이미지가 로드되었다.
(img태그도 시도해봤는데 똑같음)

원인은 뭐였을까?

원인은 React-Swiper의 버전 문제였다.
9버전에서 10버전으로 바뀌면서 코드가 좀 바뀐거였는데 나는 다른 사람의 프로젝트를 참고하면서 작업을 하다보니, 버전 문제일거라는 생각을 1도 못했다.(초보특)

아니 근데 다시 생각해보니 React-Swiper 메인페이지에 나와있는거 복붙해서 했었는데?

해결책

결론부터 말하자면

<div class='swiper-wrapper'></div>

이 코드를 안에 넣어줘야한다.

React-Swiper 메인 홈페이지에는

// Import Swiper React components
import { Swiper, SwiperSlide } from 'swiper/react';

// Import Swiper styles
import 'swiper/css';

export default () => {
  return (
    <Swiper
      spaceBetween={50}
      slidesPerView={3}
      onSlideChange={() => console.log('slide change')}
      onSwiper={(swiper) => console.log(swiper)}
    >
      <SwiperSlide>Slide 1</SwiperSlide>
      <SwiperSlide>Slide 2</SwiperSlide>
      <SwiperSlide>Slide 3</SwiperSlide>
      <SwiperSlide>Slide 4</SwiperSlide>
      ...
    </Swiper>
  );
};

이런식으로 되어있고 나도 이걸 그대로 가져다 썻는데.. Image는 또 다른 영역인가보다(이렇게 생각하는게 편함)

이곳을 가보면 v9에서 v10으로 가면서 요소 내부 레이아웃이 변했으니 몇가지 트릭을 수행하는 경우 새로운 레이아웃을 고려해보라고 한다.

나는 이렇게 했다.

import { Swiper, SwiperSlide } from 'swiper/react';
import { Keyboard, Navigation } from 'swiper/modules';
import Image from 'next/image';

import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';

...

<div className='relative w-screen h-screen bg-black z-50'>
  <Swiper
    slidesPerView={1}
    spaceBetween={30}
    loop={true}
    keyboard=
    pagination=
    navigation={true}
    modules={[Navigation, Keyboard]}
    className='mySwiper'
  >
    {dummyData.map((data, index) => (
      <SwiperSlide key={data.id}>
        <div className='swiper-wrapper'>
          {/* 포스팅 사진 */}
          <Image src={data.imgUrl} alt='이미지' fill objectFit='cover' />
          {/* 포스팅 내용 */}
          ...
        </div>
      </SwiperSlide>
    ))}
  </Swiper>
</div>

Nextjs 핫 리로드 안될때

|

핫 리로드란?

개발중인 프로젝트 앱을 실행하고 변경되는 사항을 빠르게 반영하기 위해 지원 하는 기능인데, 이게 안되면 개발자가 너무 불편해진다.
html,css를 변경할 때 마다 새로고침을 해줘야하기 때문이다.

핫 리로드가 안될 때

핫 리로드가 안될 때 의심해봐야하는건 node 버전이다. node를 최신버전으로 업데이트 해주자.

ContextAPI

|

ContextAPI란

React가 자체적으로 제공하는 상태관리 방법이다.
데이터를 공유하기 위한 방법으로 사용한다.
Ex)로그인한 사용자 정보, 테마,언어 설정 등등

장점

  • props Drilling를 피하고 컴포넌트 간 상태를 쉽게 공유 가능하다.
  • 추가적인 라이브러리 설치가 필요없다.

단점

  • 복잡한 상태관리는 어려울 수 있다.
  • 너무 많은 context를 사용하면 컴포넌트 재사용성이 떨어진다.(컴포넌트가 거기에 의존하게되서)

사용예시

  • 로그인 유저 정보가져오기

index.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { BrowserRouter as Router } from 'react-router-dom';
import { AuthContextProvider } from 'context/AuthContext';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <AuthContextProvider>
    <Router>
      <App />
    </Router>
  </AuthContextProvider>
);

AuthContext.tsx

import { ReactNode, createContext, useState, useEffect } from 'react';
import { User, getAuth, onAuthStateChanged } from 'firebase/auth';
import { app } from 'firebaseApp';

interface AuthProps {
  children: ReactNode;
}
// 전역 상태 값
const AuthContext = createContext({
  user: null as User | null,
});

export const AuthContextProvider = ({ children }: AuthProps) => {
  const auth = getAuth(app);

  const [currentUser, setCurrentUser] = useState<User | null>(null);

  useEffect(() => {
    onAuthStateChanged(auth, (user) => {
      if (user) {
        setCurrentUser(user);
      } else {
        setCurrentUser(user);
      }
    });
  }, [auth]);

  return (
    <AuthContext.Provider value=>
      {children}
    </AuthContext.Provider>
  );
};

export default AuthContext;

이제 유저정보 가져올때는 이렇게 사용해주면된다.

import AuthContext from 'context/AuthContext';
import { useContext } from 'react';

const { user } = useContext(AuthContext);

Firestore

|

FireStore란?

Firebase에서 제공하는 NoSQL 형식의 클라우드 데이터베이스다.

간단 요약:

  • 데이터를 저장하고 불러와주는 일을 쉽게 도와준다.

  • 실시간 데이터 동기화를 지원해주며, 웹,안드로이드,ios에서 데이터를 저장하고 동기화 할 수 있다.

  • 데이터는 문서(document)와 컬렉션(collection)의 형태로 저장되며, 효율적인 쿼리작성이 가능하다.

  • 오프라인 지원 제공

장점

  • 실시간 데이터 동기화

    • 실시간 채팅 및 데이터 분석 등 실시간 기능 애플리케이션 개발 가능
  • 구조화된 데이터

    • 문서 - 컬렉션 형태로 데이터 저장 / 구조화된 데이터 쉽게 저장하고 불러올 수 있다.
  • 보안

    • 사용자 기반의 보안규칙 설정이 가능하다.

FireStore 사용방법

  • Firebase 프로젝트 생성 & Firebase SDK 앱 추가

  • Firestore 인스턴스 가져오기

  • firebaseApp.tsx

import { initializeApp, FirebaseApp, getApp } from 'firebase/app';
import {getFirestore} from 'firebase/firestore';
import 'firebase/auth';

export let app: FirebaseApp;

const firebaseConfig = {
  ...
};

try {
  app = getApp('app');
} catch (e) {
  app = initializeApp(firebaseConfig);
}

// Initialize Firebase
const firebase = initializeApp(firebaseConfig);
// FireStore NoSQL database
export const db = getFirestore(app);

export default firebase;
  • FireStore 서비스 사용하기
import { db } from 'firebaseApp';

const onSubmit = async (e:any ) => {
  e.preventDefault();

  try{
    // firestore db에 저장
    await addDoc(collection(db,'posts'),{
      ...
    });
  } catch (e) {
    console.log(e)
  }
}

그런데..

파이어베이스 홈페이지에서 해당 프로젝트 들어가서
파이어스토어 생성 먼저 해주는게 좋다.

  • 클라우드 파이어스토어 탭에서 파이어스토어 생성
  • 규칙 탭에서 문서보기를 클릭하면 여러 보안 규칙이 나오는데, 참고해서 보안을 작성해주면 된다.

예를들면 뭐 게시글 수정할때 작성자와 로그인한사람의 uid가 같지않으면 접근이 불가능하게 해준다던가,
인증된 모든 사용자는 접근할 수 있게 해준다던가 하는 식의 보안 규칙 말이다.

Firebase SDK 리팩토링

|

리팩토링 전 코드

import { initializeApp } from 'firebase/app';
import { getAnalytics } from 'firebase/analytics';

// Your web app's Firebase configuration
const firebaseConfig = {
  apiKey: process.env.REACT_APP_API_KEY,
  authDomain: process.env.REACT_AUTH_DOMAIN,
  projectId: process.env.REACT_APP_PROJECT_ID,
  storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_MESSAGE_SENDER_ID,
  appId: process.env.REACT_APP_ID,
  measurementId: process.env.REACT_APP_MEASUREMENTID,
};

// Initialize Firebase
const firebase = initializeApp(firebaseConfig);
const analytics = getAnalytics(firebase);

export default firebase;

리팩토링 후 코드

import { initializeApp, FirebaseApp, getApp } from 'firebase/app';
import 'firebase/auth';

export let app: FirebaseApp;

const firebaseConfig = {
  apiKey: process.env.REACT_APP_API_KEY,
  authDomain: process.env.REACT_AUTH_DOMAIN,
  projectId: process.env.REACT_APP_PROJECT_ID,
  storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_MESSAGE_SENDER_ID,
  appId: process.env.REACT_APP_ID,
  measurementId: process.env.REACT_APP_MEASUREMENTID,
};

try {
  // 초기화가 되었는지 확인
  app = getApp('app');
} catch (e) {
  // 초기화
  app = initializeApp(firebaseConfig);
}

// Initialize Firebase
const firebase = initializeApp(firebaseConfig);

export default firebase;

이렇게 작성한 이유는 앱을 초기화 할 때마다, initializeApp하는건 비효율적이라서다.

getApp()을 파이어베이스 공홈에서 찾아보면,

  • 앱 이름이 제공되면 해당 이름에 해당하는 앱이 반환된다.
  • 검색 중인 앱이 아직 초기화되지 않은 경우 ‘예외’가 발생한다. (공홈 getApp설명)

라고 명시되어있다.

즉 위의 코드는 app이 초기화가 안되어있다면 예외를 발생시켜 초기화 시킨다.