[TIL]20250415

김민석·2025년 4월 15일
post-thumbnail

오늘 목표

  • 수업 전 7시 기상 후 운동(O)
  • 수업 내용 모르는 거 및 공부내용 정리(O)
  • JS 강의 듣기(O)

공부내용

Lighhouse

Lighthouse는 Google에서 개발한 오픈소스 웹 성능 분석 도구로 F12 개발자 도구를 열어 사용이 가능하다. 분석하려는 카테고리를 선택하고 분석을 누르면 해당 웹페이지의 성능,접근성,SEO 등을 검사하고 점수를 매겨줍니다. 또한 어떠한 점이 부족한지 문제점과 개선 방안을 따로 분석해 글로 남겨줍니다.

주요 측정 영역

  1. 성능(Performance): 로딩 속도, 상호작용 지연 시간, 시각적 안정성 등이 있고
  • FCP (First Contentful Paint) 개선
    • FCP는 브라우저가 첫 번째 텍스트나 이미지 등 “보여지는 콘텐츠”를 화면에 그리는 시점까지의 시간 백엔드 API 응답 속도 개선 CDN 사용 불필요한 CSS/JS 파일 제거
  • LCP (Largest Contentful Paint) 개선
    • LCP는 화면에 표시되는 가장 큰 콘텐츠 요소가 렌더링되는 시간 이미지 최적화를해 개선이 가능하다
  • TBT (Total Blocking Time) 줄이기
    • TBT는 브라우저가 메인 스레드를 점유해서 사용자 입력에 반응하지 못하는 시간의 총합 사용자의 필요에 따라 지연 로딩 (lazy loading)을 하면 개선이 가능하다. 또한 코드 분할을 통해 JavsScript payload 감소시키면 좋다.
  • CLS (Cumulative Layout Shift) 최소화
    • CLS는 페이지 로딩 중 요소가 갑자기 움직이는 현상 이미지에 width 및 height 명시한다면 개선이 가능함.
  1. 접근성(Accessibility): 다양한 사용자(장애인 포함)가 콘텐츠에 접근할 수 있는 정도
  • 대체 텍스트 추가 (Alt Text)
    + alt 속성은 이미지의 의미나 목적을 설명해야 한다 꼭 alt는 건너뛰지 말고 작성을 해주자 ex)title 및 descript
    대체 폰트 및 텍스트를 추가해야한다.
  • 키보드 탐색 지원
    • Tab 키만으로 모든 인터랙션이 가능하게 해야한다. div, span 같은 비인터랙티브 요소에 클릭 기능을 부여하려면tabindex="0"과 키보드 이벤트도 함께 처리해야 합니다.

<div role="button" tabindex="0" onclick="doSomething()" onkeydown="if(event.key==='Enter'){doSomething()}">
  클릭
</div>
  • ARIA 속성 올바르게 사용
    • HTML만으로 표현이 어려운 동작이나 역할을 aria-label과 같은 것으로 알려줘야한다 예로는 아이콘만 있는 경우 그것이 어떠한 역할을 하는지 모를 수 있기 때문에 작성을 해주는것이 접근서에 좋다.
<button aria-label="검색하기">
  <svg>...</svg>
</button>

<a href="contact.html" aria-label="회사 연락처 페이지로 이동">
  <img src="contact-icon.png" alt="">
</a>
  1. SEO(검색 엔진 최적화): 검색 엔진이 페이지를 크롤링하고 인덱싱하기 쉬운 정도
    +메타 태그 개선
    • 메타 태그는 페이지의 정보를 브라우저와 검색엔진에 전달하는 중요한 요소
<!-- index.html -->
<head>
  <meta charset="UTF-8" />
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>SHOPIFY - 최고의 쇼핑 경험</title>
  <meta name="description" content="당신을 위한 프리미엄 쇼핑몰, 다양한 상품과 특별한 할인 혜택을 만나보세요." />
  <meta name="keywords" content="쇼핑, 온라인쇼핑, 패션, 의류, 액세서리" />
  <meta name="author" content="SHOPIFY" />
  <!-- Open Graph / 소셜 미디어 -->
  <meta property="og:title" content="SHOPIFY - 최고의 쇼핑 경험" />
  <meta property="og:description" content="당신을 위한 프리미엄 쇼핑몰" />
  <meta property="og:image" content="/og-image.jpg" />
  <meta property="og:url" content="https://your-domain.com" />
  <meta property="og:type" content="website" />
  <!-- 기존 코드 유지 -->
</head>
  • 검색엔진 친화적인 URL 구조 사용
    • 하이픈(-)으로 단어를 구분하는 것이 SEO에 더 좋습니다.

Lighthouse는 웹 개발 초기 단계부터 배포 후 유지보수까지 전체 개발 생명주기에서 유용한 도구이므로 정기적으로 활용하여 웹사이트의 품질을 지속적으로 개선해 나가는 것이 좋다. 추후에 프로젝트나 웹사이트를 개발한다면 사용하여 개선해보자!!

lazy

lazy는 코드 스플러팅을 구현하는 함수이다. 이 기능은 실제로 필요한 시점에 컴포넌트를 불러와 초기 로딩시간을 줄이고 성능을 향상 시킬 수 있습니다. 코드 스플릿팅은 큰 자바스크립트 파일을 작은 파일들로 분할하는 기술

Suspense

Suspense는 컴포넌트가 로딩될 때 까지 대기상태를 처리하는 React 컴포넌트이다.
Suspense를 사용하면 로딩 상태를 선언적으로 처리해 사용자 경험 향상시킨다.

React에서 lazy와 Suspense 장점 및 활용 방법

  1. 성능 최적화
    • 초기 로딩 시간 단축
    • 필요한 시점에만 코드 로드
    • 사용자 경험 향상
  2. 선언적 로딩 상태 처리
    • 복잡한 로딩 로직을 간소화
    • 컴포넌트 트리 전체에 대한 로딩 상태 관리 가능
    • 중첩된 Suspense 경계를 통한 로딩 UI의 세분화
  3. 코드 분할(Code Splitting) 용이
    • 큰 번들을 작은 청크로 분할
    • 라우트별, 기능별 코드 분할 구현 가능
    • 사용자가 필요로 하는 코드만 다운로드
  4. 데이터 페칭과의 통합
    • React 19에서는 데이터 페칭을 위한 Suspense 지원 강화
    • 데이터 로딩과 UI 로딩을 통합하여 일관된 사용자 경험 제공
    • 워터폴 현상 최소화 및 병렬 데이터 로딩 지원

대규모 SPA에서 성능과 사용자 경험 동시에 챙기기

1.라우트별 코드 스플리팅 (Route-based Code Splitting)

목적

초기 로딩을 빠르게 하고, 필요한 페이지만 동적으로 로딩하여 불필요한 리소스 낭비를 막음.

💡 왜 중요한가요?

SPA는 기본적으로 모든 JS 코드를 한 번에 불러오는데, 페이지 수가 많아질수록 초기 로딩 속도(FCP, TTI)가 느려집니다.
→ 라우트 단위로 코드를 분리하면,방문한 페이지의 코드만 로딩하므로 성능이 훨씬 좋아집니다.

2.페이지 전환 시 부드러운 로딩 경험 제공

목적

페이지 전환 시 깜빡임, 멈춤, 빈 화면 없이 연속적인 사용자 경험을 제공하는 것.

Skeleton사용하기

페이지 전환 시, 서버나 클라이언트 데이터가 로딩되는 동안 비어 있지 않게 미리보기 UI를 보여줍니다.

<Suspense fallback={<SkeletonPage />}>
  <Routes>
    <Route path="/" element={<Home />} />
  </Routes>
</Suspense>

React19 에서의 Suspence 뭐가 바뀌었을까?

use() 훅 등장

use는 기존의 useContext를 대체할 수 있다.
useContext는 컴포넌트의 최상위 레벨에서 호출해야 하는 반면,
use는 if와 같은 조건문이나 for와 같은 반복문 내부에서도 호출할 수 있다.
즉, use는 useContext보다 더 유연하게 사용될 수 있다.

강사님께 질문

lazy가 필요한 시점에만 로드 되도록 해서 초기 로딩시간을 줄어들게 하는거라면 aboutpage에서 초기값으로 dashboard가 되어있어서 초기로 렌더링 되는 것 같은데 dashboard도 lazy로 불러오는게 더 좋은건가요??

답변

router.jsx 에서 AboutPage.jsx를 이미 lazy()를 활용해서 호출했는데
AboutPage.jsx 의 내부의 컨포넌트도 lazy,Suspance를 사용할 필요가 있는가라는 질문을 하신 것 같습니다
고민했기 때문에 할 수 있는 질문이네요~ ^^

사용자가 처음 앱에 접속할 때는 AboutPage 자체가 로드되지 않고, AboutPage로 이동할 때 AboutPage의 기본 구조만 로드됩니다. 그 후 사용자가 특정 탭을 선택할 때 해당 탭 컴포넌트만 추가로 로드됩니다.

사용자가 AboutPage에 접근했을 때, 모든 탭 컴포넌트를 한꺼번에 로드하는 대신 초기 선택된 대시보드 탭만 로드하고 다른 탭은 필요할 때 로드합니다.
즉, AboutPage의 lazy 로딩은 "페이지를 방문할 때까지 페이지 로드를 지연"시키고, 하위 컴포넌트의 lazy 로딩은 "해당 탭을 클릭할 때까지 탭 컴포넌트 로드를 지연"시킵니다.

AboutPage의 lazy 로딩과 그 하위 컴포넌트의 lazy 로딩은 서로 다른 목적을 가지며, 중복이 아닌 보완적인 관계입니다. 두 수준의 lazy 로딩을 모두 유지하는 것이 코드 분할과 성능 최적화 측면에서 좋은 접근법입니다~

나의 질문 요점 은 AboutPage에서 기본 탭이 dashboard라서 dashboard 컴포넌트가 처음에 렌더링 되어서 Lazy로 불러오는 것이 의미가 있나 라는 질문이었다. 강사님의 설명의 어느정도 이해가 되었지만 GPT에게 한번 더 물어보았다. 아래는 내가 왜 그런가 생각한 코드이다.

import React, { Suspense, lazy, useState } from 'react'
import '../assets/lazyText.css'
// lazy를 사용하여 컴포넌트 동적 로딩
const LazyComponent = lazy(() => import('../lazy_test/LazyComponent'))
const LazyDashboard = lazy(() => import('../lazy_test/Dashboard'))
const LazyAnalytics = lazy(() => import('../lazy_test/Analytics'))

const AboutPage = () => {
  const [tab, setTab] = useState('dashboard')

  // 탭 변경 핸들러
  const handleTabChange = selectedTab => {
    setTab(selectedTab)
  }

  return (
    <main>
      <h2>AboutPage</h2>
      <h3>React 19 Lazy & Suspense 데모</h3>
      <div className="app-container">
        {/* 탭 내비게이션 */}
        <div className="tabs">
          <button
            onClick={() => handleTabChange('dashboard')}
            className={tab === 'dashboard' ? 'active' : ''}
          >
            대시보드
          </button>
          <button
            onClick={() => handleTabChange('analytics')}
            className={tab === 'analytics' ? 'active' : ''}
          >
            분석
          </button>
          <button
            onClick={() => handleTabChange('lazy')}
            className={tab === 'lazy' ? 'active' : ''}
          >
            지연 로딩 컴포넌트
          </button>
        </div>

        {/* Suspense를 사용한 로딩 상태 처리 */}
        <div className="content">
          <Suspense fallback={<div className="loading">로딩 중...</div>}>
            {tab === 'dashboard' && <LazyDashboard />}
            {tab === 'analytics' && <LazyAnalytics />}
            {tab === 'lazy' && <LazyComponent />}
          </Suspense>
        </div>
      </div>
    </main>
  )
}

export default AboutPage

여기서 AboutPage가 처음 렌더링될 때에도 dashboard 전체 코드를 바로 가져오는게 아니라
tab==="dashboard"가 되기 전까지는 브라우저로 안불러와짐 만약에 대시보드의 코드가 무겁다면 바로 불러오게 된다면 로딩이 느려진다. lazy를 쓰면? 유저가 aboutpage에 들어오더라도 dashboard는 그 전에는 브라우저에 존재하지 않다가 렌더링 시점에서 import 발생하면 그때서야 들어와서 lazy를 쓰는게 효과가 있다!!

profile
나만의 기록장

0개의 댓글