Notice
Recent Posts
Recent Comments
Link
«   2025/08   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Tags
more
Archives
Today
Total
관리 메뉴

개발일기

[내일배움캠프] 최종 프로젝트 - <picture>태그와 <source>태그를 활용한 이미지 최적화, uselocation hook을 이용한 페이지 이동 시 스크롤 탑 본문

카테고리 없음

[내일배움캠프] 최종 프로젝트 - <picture>태그와 <source>태그를 활용한 이미지 최적화, uselocation hook을 이용한 페이지 이동 시 스크롤 탑

코찡 2023. 9. 16. 16:00

1. <picture>태그와 <source>태그를 활용한 이미지 최적화

* 이미지 로딩 이슈(Layout Shift) 해결방법

1. 디바이스별 별도 이미지 사이즈 제공
2. <picture> 와 <source> 태그 (webp 지원 여부)
3. 이미지 변환 도구 - squoosh

 

-> 여기에서 사용해 볼 방법은 두 번째 방법 !

 

브라우저는 <picture> 태그 내에 선언된 <source> 태그 요소 중에서 srcset, media, type과 같이 선언된 속성(attibutes)들을 통해 기기에 적합한 이미지를 순차적으로 확인하고 노출하게 된다. 적합한 요소가 선언되어 있지 않거나, 브라우저가 picture를 지원하지 않을 때에는 <img> 태그에서 선언된 이미지가 노출된다.

 

정리하자면, <source> 태그는 브라우저가 렌더링해야 하는 파일과 크기를 결정하는 데 도움이 되는 정보를 제공하며, <img> 태그는 이미지의 크기와 대체 텍스트를 제공하고, <source> 태그가 적합한 이미지를 제공할 수 없는 경우에는 대체 이미지를 제공한다.

import mainBannerAvif from '../../../assets/imgs/partner/partner_post_banner.avif';
import mainBanner from '../../../assets/imgs/partner/partner_post_banner.png';
import mainBannerWebp from '../../../assets/imgs/partner/partner_post_banner.webp';
import * as St from './style';

function PartnerBanner() {
  return (
    <St.ImageWrapper $banner={`partner`}>
      <St.PartnerMainImageBox>
        <picture>
          <source srcSet={mainBannerAvif} type="image/avif" />
          <source srcSet={mainBannerWebp} type="image/webp" />
          <img src={mainBanner} alt="동행 찾기 메인 배너 이미지" />
        </picture>
      </St.PartnerMainImageBox>
    </St.ImageWrapper>
  );
}

export default PartnerBanner;

2. uselocation hook을 이용한 페이지 이동 시 스크롤 탑

1) ScrollToTop 컴포넌트 정의

// components 폴더 하위 common 폴더 하위 scrollToTop 폴더 하위 ScrollToTop.tsx

import { useEffect } from 'react';
import { useLocation } from 'react-router';

export default function ScrollToTop() {
  const { pathname } = useLocation();

  useEffect(() => {
    window.scrollTo(0, 0);
  }, [pathname]);
  return null;
}

2) ScrollToTop 컴포넌트 적용

(1) 모든 컴포넌트는 App 컴포넌트 아래 Router에서 관리하고 있습니다.

// root 경로에 App.tsx

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { Reset } from 'styled-reset';
import Router from './shared/Router';

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {},
  },
});

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Reset />
      <Router />
      <ReactQueryDevtools initialIsOpen={false} position="bottom-right" />
    </QueryClientProvider>
  );
}

export default App;

(2) 따라서 Router 상단에 ScrollToTop 컴포넌트 import해주면, 모든 컴포넌트 페이지 이동 시 스크롤 탑 구현 가능

// shared 폴더 하위 Router.ts

import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Layout from '../components/common/layout/Layout';
import ErrorPage from '../pages/ErrorPage';
import Intro from '../pages/Intro';
import LogIn from '../pages/LogIn';
import MyPage from '../pages/MyPage';
import Partner from '../pages/Partner';
import PartnerDetail from '../pages/PartnerDetail';
import PartnerWrite from '../pages/PartnerWrite';
import SignUp from '../pages/SignUp';
import SpotShare from '../pages/SpotShare';
import SpotShareDetail from '../pages/SpotShareDetail';
import SpotShareWrite from '../pages/SpotShareWrite';
import StyledWrapper from '../components/common/layout/StyleWrapper';
// ScrollToTop 임포트
import ScrollToTop from '../components/common/scrollToTop/ScrollToTop';

const Router: React.FC = () => {
  // ScrollToTop 컴포넌트 삽입
  return (
    <BrowserRouter>
      <ScrollToTop />
      <Routes>
        <Route path="/" element={<Intro />} />
        <Route path="*" element={<ErrorPage />} />

        <Route
          path="/login"
          element={
            <StyledWrapper>
              <LogIn />
            </StyledWrapper>
          }
        />
        <Route
          path="/signup"
          element={
            <StyledWrapper>
              <SignUp />
            </StyledWrapper>
          }
        />
        <Route
          path="/partner"
          element={
            <StyledWrapper>
              <Partner />
            </StyledWrapper>
          }
        />
        <Route
          path="/spotshare"
          element={
            <StyledWrapper>
              <SpotShare />
            </StyledWrapper>
          }
        />

        <Route element={<Layout />}>
          <Route path="/mypage" element={<MyPage />} />
          <Route path="/partner/detail/:postid" element={<PartnerDetail />} />
          <Route path="/partner/write" element={<PartnerWrite />} />
          <Route path="/partner/write/:postid" element={<PartnerWrite />} />
          <Route path="/spotshare/detail/:postid" element={<SpotShareDetail />} />
          <Route path="/spotshare/write" element={<SpotShareWrite />} />
          <Route path="/spotshare/write/:postid" element={<SpotShareWrite />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
};
export default Router;