16 메인 페이지에 실제 데이터 띄워보기 - gatsby-plugin-image 라이브러리로 최적화된 썸네일 사진 띄워주기
source: categories/study/gatsby/gatsby_9-07.md
gatsby-plugin-image 라이브러리로 최적화된 썸네일 사진 띄워주기
gatsby-plugin-image 라이브러리는 왜 사용할까?
저번 챕터에서는 GraphQL Query를 통해 이미지 링크를 받아와 이를 통해 썸네일 이미지를 띄워주도록 구현했습니다.
하지만 이 방식은 이미지가 완전히 로딩되기 전까지는 화면에 나타나지 않아 좋지 못한 사용자 경험을 제공할 가능성이 크고, 이는 이미지 사이즈와 해상도 조절이 불가능하기 때문에 파일 용량이 클 경우에 더 심해질 수 있습니다.
이를 위해 사용하는 라이브러리가 gatsby-plugin-image
라이브러리입니다.
gatsby-plugin-image
라이브러리는 Gatsby의 기본적인 이미지 처리 과정의 효율성을 이용해 더욱 높은 사용자 경험을 제공합니다.
강의 업데이트 전, 기존에는 gatsby-image 라이브러리를 통해 이미지를 띄워주었습니다.
하지만 해당 라이브러리가 Deprecated 되어 gatsby-plugin-image 라이브러리로 마이그레이션하는 것을 권장드립니다.
gatsby-plugin-image 라이브러리 세팅 및 사용 방법
먼저, 아래 커맨드를 입력해 필요한 라이브러리를 설치해줍니다.
yarn add gatsby-plugin-image gatsby-plugin-sharp gatsby-transformer-sharp
기존 강의를 따라 gatsby-image
라이브러리를 설치하신 분은 아래 커맨드를 통해 해당 라이브러리를 삭제해주시기 바랍니다.
yarn remove gatsby-image
그 다음, 아래와 같이 설치한 라이브러리를 gatsby-config.js
파일 내 플러그인 옵션에 추가해줍니다.
module.exports = {
plugins: [
// ...,
{
resolve: `gatsby-plugin-sharp`,
options: {
defaults: {
formats: ['auto', 'webp'],
quality: 100,
placeholder: 'blurred',
}
}
},
`gatsby-transformer-sharp`,
`gatsby-plugin-image`,
// ...
],
}
그럼 gatsby-plugin-image
라이브러리를 어떻게 사용하는지 알아봅시다.
import { GatsbyImage } from 'gatsby-plugin-image'
export default ({ data }) => (
<div>
<h1>Hello gatsby-image</h1>
<GatsbyImage
image={data.file.childImageSharp.gatsbyImageData}
alt="Gatsby Image"
/>
</div>
)
export const imageQuery = graphql`
{
file {
childImageSharp {
gatsbyImageData(width: 700)
}
}
}
`
기본적인 구조는 위의 코드와 같습니다.
지난 챕터에서 설치한 이미지 처리 라이브러리에 의해 내부적으로 이미지가 가공이되어,
GraphiQL 상에서 이미지 프로퍼티를 선택하면 위와 같이 childImageSharp
프로퍼티가 존재하고,
또 해당 프로퍼티 안에는 gatsbyImageData
프로퍼티가 있습니다.
바로 이 gatsbyImageData
프로퍼티가 gatsby-plugin-image
라이브러리를 위한 이미지 데이터입니다.
마찬가지로 allMarkdownRemark
스키마 내의 thumbnail
프로퍼티를 살펴보면 동일한 프로퍼티가 존재한다는 것을 확인하실 수 있습니다.
gatsby-plugin-image 라이브러리의 도입으로 코드 수가 많이 줄어들었습니다. 그에 따라 기존 강의 내용 일부인 Fragment 관련 내용이 삭제됐습니다.
메인 페이지 컴포넌트에서 썸네일 이미지 데이터 쿼리하기
그럼 이제 메인 페이지에서 썸네일 이미지를 불러올 수 있도록 쿼리문을 수정해봅시다.
아래와 같이 쿼리문을 수정해주세요.
src/pages/index.tsx
export const getPostList = graphql`
query getPostList {
allMarkdownRemark(
sort: { order: DESC, fields: [frontmatter___date, frontmatter___title] }
) {
edges {
node {
id
frontmatter {
title
summary
date(formatString: "YYYY.MM.DD.")
categories
thumbnail {
childImageSharp {
gatsbyImageData(width: 768, height: 400)
}
}
}
}
}
}
}
`
이렇게 쿼리문이 변경됨에 따라 IndexPage 컴포넌트의 Props 타입도 변경해주어야 하는데, 해당 부분은 PostItem.types.ts
파일에 존재합니다.
따라서 아래와 같이 코드를 변경해주어야 합니다.
src/types/PostItem.types.tsx
import { IGatsbyImageData } from 'gatsby-plugin-image'
export type PostFrontmatterType = {
title: string
date: string
categories: string[]
summary: string
thumbnail: {
childImageSharp: {
gatsbyImageData: IGatsbyImageData
}
}
}
export type PostListItemType = {
node: {
id: string
frontmatter: PostFrontmatterType
}
}
PostItem 컴포넌트 수정하기
위와 같이 PostItem 컴포넌트가 받는 데이터의 구조가 달라졌기 때문에 이에 맞춰 Props 타입을 수정해야 하므로 아래와 같이 코드를 변경해주세요.
src/components/Main/PostItem.tsx
// ...
import { GatsbyImage } from 'gatsby-plugin-image'
import { PostFrontmatterType } from 'types/PostItem.types'
type PostItemProps = PostFrontmatterType & { link: string }
// ...
이제 타입은 제대로 수정했으니, 이미지 데이터를 받고 출력해보는 부분을 구현해봅시다.
여기에서는 위에서 본 것처럼 gatsby-plugin-image
라이브러리에서 제공하는 GatsbyImage 컴포넌트를 사용해야 합니다.
GatsbyImage 컴포넌트를 사용해 그대로 출력해도 무방하지만, 저희가 기존에 설정했던 스타일이 있기 때문에 이를 활용해보도록 하겠습니다.
src/components/Main/PostItem.tsx
// ...
const ThumbnailImage = styled(GatsbyImage)`
width: 100%;
height: 200px;
border-radius: 10px 10px 0 0;
`
// ...
기존에는 img 요소에 대한 스타일을 지정한 Styled Component였지만, 저희는 GatsbyImage 컴포넌트를 활용할 것이기 때문에 해당 컴포넌트를 넘겨주었습니다.
그리고 기본적으로 object-fit 속성은 gatsby-config.js
파일에서 플러그인을 추가할 때에 설정했기 때문에 지워주도록 하겠습니다.
이렇게 스타일을 매핑한 요소가 변경되었기 때문에 기존 img 태그를 사용하던 방식에서 GatsbyImage 컴포넌트를 사용하는 방식으로 변경해야합니다.
따라서 다음과 같이 해당 부분을 변경해줍시다.
src/components/Main/PostItem.tsx
// ...
const PostItem: FunctionComponent<PostItemProps> = function ({
title,
date,
categories,
summary,
thumbnail: {
childImageSharp: { gatsbyImageData },
},
link,
}) {
return (
<PostItemWrapper to={link}>
<ThumbnailImage image={gatsbyImageData} alt="Post Item Image" />
<PostItemContent>
<Title>{title}</Title>
<Date>{date}</Date>
<Category>
{categories.map(item => (
<CategoryItem key={item}>{item}</CategoryItem>
))}
</Category>
<Summary>{summary}</Summary>
</PostItemContent>
</PostItemWrapper>
)
}
export default PostItem
여기까지 이상없이 잘 따라왔다면 로컬 서버를 실행하여 Lazy Loading이 잘 적용되었는지 확인해주세요.
프로필 이미지 Lazy Loading 적용하기
저희는 메인 페이지 상단의 Introduction 컴포넌트에서 프로필 이미지를 사용했습니다.
이 또한 마찬가지로 gatsby-plugin-image
를 통해 Lazy Loading 기능을 적용해줄 수 있습니다.
먼저, 루트 디렉토리에 static 디렉토리를 생성해준 후, 확장자는 유지한 채로 profile-image
라는 이름으로 이미지를 저장해주세요.
그 다음, gatsby-source-filesystem
라이브러리를 통해 static 디렉토리 내의 파일도 GraphQL 상으로 처리할 수 있게 설정을 추가해주겠습니다.
gatsby-config.js
module.exports = {
siteMetadata: { ... },
plugins: [
// ...,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `contents`,
path: `${__dirname}/contents`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/static`,
},
},
// ...
],
};
그럼 이제 메인 페이지에서 profile-image
라는 이름의 이미지 파일을 Query하는 코드를 다음과 같이 추가해주세요.
그리고 Introduction 컴포넌트에 profileImage
라는 이름으로 Props를 전달해주세요.
src/pages/index.tsx
// ...
import { IGatsbyImageData } from 'gatsby-plugin-image'
import { PostListItemType } from 'types/PostItem.types'
type IndexPageProps = {
data: {
allMarkdownRemark: {
edges: PostListItemType[]
}
file: {
childImageSharp: {
gatsbyImageData: IGatsbyImageData
}
}
}
}
// ...
const IndexPage: FunctionComponent<IndexPageProps> = function ({
data: {
allMarkdownRemark: { edges },
file: {
childImageSharp: { gatsbyImageData },
},
},
}) {
return (
<Container>
<GlobalStyle />
<Introduction profileImage={gatsbyImageData} />
<CategoryList selectedCategory="Web" categoryList={CATEGORY_LIST} />
<PostList posts={edges} />
<Footer />
</Container>
)
}
export default IndexPage
export const getPostList = graphql`
query getPostList {
allMarkdownRemark(
sort: { order: DESC, fields: [frontmatter___date, frontmatter___title] }
) {
edges {
node {
id
frontmatter {
title
summary
date(formatString: "YYYY.MM.DD.")
categories
thumbnail {
childImageSharp {
gatsbyImageData(width: 768, height: 400)
}
}
}
}
}
}
file(name: { eq: "profile-image" }) {
childImageSharp {
gatsbyImageData(width: 120, height: 120)
}
}
}
`
위의 코드와 같이 profile-image
라는 이름을 가진 이미지 파일 데이터를 불러와 Introduction 컴포넌트에 전달해주었으니, Introduction 컴포넌트에도 변경 사항을 적용해줍시다.
src/components/Main/Introduction.tsx
import React, { FunctionComponent } from 'react'
import styled from '@emotion/styled'
import { IGatsbyImageData } from 'gatsby-plugin-image'
import ProfileImage from 'components/Main/ProfileImage'
type IntroductionProps = {
profileImage: IGatsbyImageData
}
// ...
const Introduction: FunctionComponent<IntroductionProps> = function ({
profileImage,
}) {
return (
<Background>
<Wrapper>
<ProfileImage profileImage={profileImage} />
<div>
<SubTitle>Nice to Meet You,</SubTitle>
<Title>I'm Junior Frontend Developer Hyun.</Title>
</div>
</Wrapper>
</Background>
)
}
export default Introduction
이제 ProfileImage 컴포넌트에서 gatsby-plugin-image
라이브러리의 GatsbyImage 컴포넌트를 사용해 기존 이미지 태그를 바꿔줍시다.
src/components/Main/ProfileImage.tsx
import React, { FunctionComponent } from 'react'
import styled from '@emotion/styled'
import { GatsbyImage, IGatsbyImageData } from 'gatsby-plugin-image'
type ProfileImageProps = {
profileImage: IGatsbyImageData
}
const ProfileImageWrapper = styled(GatsbyImage)`
width: 120px;
height: 120px;
margin-bottom: 30px;
border-radius: 50%;
@media (max-width: 768px) {
width: 80px;
height: 80px;
}
`
const ProfileImage: FunctionComponent<ProfileImageProps> = function ({
profileImage,
}) {
return <ProfileImageWrapper image={profileImage} alt="Profile Image" />
}
export default ProfileImage
여기까지 한 후, 메인 페이지를 새로고침해보세요.
Lazy Loading이 잘 적용되었나요?