💡 URECA/🗒️ 스터디 노트

[URECA] Day54 리액트 lazy, suspense, axios

코딩하세현 2025. 4. 15. 16:43
728x90

💡 Lighthhouse 

F12 → 개발자 도구 → Lighthouse

 

Lighthouse는 무엇인가? 🤔

 

  • 웹 성능 최적화 도구 
  • 총 4가지( performace, accessibilty, best practices, seo )로  검사

주의사항

  • 테스트 환경에 따라 점수가 달라짐
  • 네트워크 상태, 시스템 성능 등 환경 요소가 결과에 여향
  • 비즈니스 우선순위와 균형 유지
  • 사용자 경험 개선이 목표

💡성능 향상을 위해 검토할 내용 → 자바스크립트 최적화 → 라우터 구성에서 lazy 컴포넌트를 Suspense로 감싸기

🔍 lazy()에 대해서 알아보자!

const MainPage = lazy(() => import('./pages/MainPage'))
const AboutPage = lazy(() => import('./pages/AboutPage'))

import구문 말고 변수로 선언해서 lazy하는 이유는 뭐야?

정적vs동적 임포트 차이때문이다.

 

A. import구문은 정적이다.

import AboutPage from './pages/AboutPage'
  • 앱이 시작될때 무조건 로딩된다.
  • 컴파일(빌드) 시점에 딱 고정되어 있다.
  • 코드 스플리팅이 안된다.

 

B. lazy(()=>import(...))는 동적 import

const AboutPage = lazy(() => import('./pages/AboutPage'))
  • 실행 중 필요한 순간에만 로딩된다.
  • 웹팩이 이걸 보고 chunk로 분리해서 따로 로딩한다.
  • 코드 스프리팅이 가능해진다. 
이유 설명
import 처음부터 모든 짐을 한 번에 가방에 넣고 출발 (무겁고 느림)
lazy(() => import()) 필요한 짐만 그때그때 배달받음 (가볍고 빠름)

 

🤔 그렇다면 왜 변수로 선언해야할까❓

lazy()는 React.Component타입을 반환한다. 그래서 JSX에서 <AboutPage />처럼 사용하려면 변수에 담아야한다.

만일 그냥 import만 쓰면 함수 반환값이라 JSX에서 사용하지 못한다. 

 

🤔 코드 스플리팅이란 무엇일까❓

React.lazy()는 필요한 순산에만 컴포넌트를 불러오는 방식이다. 이를 지연로딩(Lazy Loading) 또는 코드 스플리팅이라고 한다.

 

✅ 요약

이유 설명
import 처음부터 모든 짐을 한 번에 가방에 넣고 출발 (무겁고 느림)
lazy(() => import()) 필요한 짐만 그때그때 배달받음 (가볍고 빠름)

🔍 Suspense에 대해 알아보자

import React from 'react'
...
import { Outlet } from 'react-router-dom'
import { Suspense } from 'react'

const Default = () => {
  return (
    <>
  ...
      <Suspense fallback={<div>Loading...</div>}>
        <Outlet />
      </Suspense>
   ...
    </>
  )
}

export default Default

 

🤔 왜 <Outlet />을 <Suspense>로 감싸❓

 

<Outlet />은 react-router-dom에서 중첩 라우팅을 구현할때 자식 컴포넌트가 표시되는 자리이다. 

 

Outlet에 대한 설명 ⬇️

근데 자식 컴포넌트들은 React.lazy()로 동적 import 한다.

const MainPage = lazy(() => import('./pages/MainPage'))

 

이렇게 lazy로 불러온 컴포넌트는 비동기 로딩이라, 렌더링 전에 준비가 안되어있을 수 있다.

 

비동기 대한 설명⬇️

그런 경우에는 React는 컴포넌트로 바로 렌더 못하고 대기 상태에 빠지게 된다. 

 

대기 상태에 대한 설명 ⬇️ 

더보기

리액트가 어떤 컴포넌트를 렌더링하려고 하는데, 그 컴포넌트가 아직 준비되지 않아서 기다리는 상태

const AboutPage = lazy(() => import('./pages/AboutPage'));

 lazy로 불러온 컴포넌트는 실제로 비동기로 불러온다. 그러면 React는 AboutPage를 렌더링하기 전에 기다려야 한다.

 

그런데 만일..! Suspense가 없으면?

React는 컴포넌트를 기다릴 준비가 안 되어 있어서 에러 발생

React.lazy()는 내부적으로 "Promise"를 던짐 (throw Promise) → React는 깜짝 놀란다😲

 

그래서 Suspense가 필요한 것이다.

🗣️"야! 얘 로딩 안됐을 수도 있으니까 기다리는 동안 fallback 보여줄게"

 

🔄 흐름 상태

1. lazy 컴포넌트가 로딩되면

2. Promise를 던지고 → React가 "대기 상태"로 진입

3. 이때 Suspense가 감싸고 있으면

4. fallback UI를 보여주고 → 컴포넌트가 다 불러와지면

5. 진짜 컴포넌트를 다시 렌더링 

그래서

 

Suspense

lazy 컴포넌트를 기다리는 동안 보여줄 UI를 지정하는 역할을 한다. 

<Suspense fallback={<div>Loading...</div>}>
  <Outlet />
</Suspense>

 

👉🏻 Outlet안에 있는 컴포넌트가 lazy하게 로딩되는 동안, <div> Loading... </div>이걸 보여준다 는 의미

 

fallback에 대한 설명 ⬇️ 

더보기

fallback은 lazy 컴포넌트가 로딩될 때 보여줄 임시 컴포넌트

 

예: 로딩 스피너, 메시지, 스켈레톤 UI 등

<Suspense fallback={<MySkeleton />}> <Outlet /> </Suspense>

💡React에서 API 데이터를 가져오는 방법

🤔 Axios를 설치하는 이유는 뭘까? 

npm i axios
  • axios는 HTTP 요청을 간편하게 보낼 수 있게 도와주는 라이브러리이다.
  • fetch보다 코드가 간결하고, interceptors 같은 기능도 있어 확장성이 좋다.
  • get, post, put, delete 같은 REST API 요청을 쉽게 다를 수 있다. 

🤔 api/bannerApi.js를 분리하는 이유는 뭘까?

  • API 호출 로직을 한 곳에 모아두면 재사용하기 쉽고 유지보수가 편해진다.

📃 주요 코드 설명 - bannerApi.js

import axios from 'axios'
const BASE_URL = 'http://localhost:3000/banners/'

export const getBannerData = async () => {
  try {
    const res = await axios.get(`${BASE_URL}`)
    // console.log('res----', res)
    return res.data
  } catch (err) {
    console.log('err----', err)
    // throw err
  }
}

 

하나하나 코드를 이해봅시당!

 

import axios from 'axios'
  • axios라는  외부 라이브러리를 가져온다.
  • 서버와 통신을 하려면 fetch같은걸 사용해야하는데, axios는 이걸 더 쉽게 해주는 도구이다.

👉🏻 axios를 파일에서 사용할 수 있게 준비하자! 라는 의미이다. 

 

const BASE_URL = 'http://localhost:3000/banners/'
  • 서버에 있는 데이터를 가져올 주소(URL)를 변수로 저장한 것
  • 이 주소는 JSON Server 경로이다.

 

try { ... } catch (err) { ... }
  • 에러가 나도 앱이 멈추지 않게 하기 위한 보호막
  • try안에 있는 코드에서 문제가 생기면 catch에서 대신 처리해준다.

 

const res = await axios.get(${BASE_URL})
  • 서버에 데이터를 요청하는 코드
  • axios.get(...)은 해당 주소에서 데이터를 가져오는 명령이고,
  • await는 "서버가 응답할 때까지 기다려줘"라는 의미
  • res는 서버의 응답 전체를 담은 객체이다.

 

return res.data
  • res.data는 서버가 보내준 실제 데이터 부분만 추출한 것이다.
더보기

개발자 도구에는 정말 무궁무진한 기능들이 숨어 있다는 걸 새삼 느꼈다.
성능 최적화 도구까지 포함되어 있다니, 놀라울 따름이다!

또한 이번 작업을 통해, 컴포넌트는 렌더링만 담당하고, API 호출이나 비즈니스 로직은 별도로 분리해야 유지보수성이 높아진다는 걸 깨달았다. 
앞으로는 API 관련 코드는 무조건 api 디렉토리에 정리하는 습관을 들이기로 다짐했다.

이런 웹사이트를 구현하는 개발자들은 얼마나 밤을 새워가며 고생했을까 생각이 들었다. 

물론 워라벨이 중요한 삶이면 그 반대일 수도?

내가 해야할 개발공부는 스택처럼 쌓여간다. 

728x90