Next.js는 이미지 최적화를 위해 Image 컴포넌트를 제공합니다.
Eslint 설정에 따라 Next.js 프로젝트에서 Image 컴포넌트가 아닌 일반 img 태그를 사용할 경우 다음과 같은 Eslint 경고 메시지가 나타날 수 있습니다.
관련 링크
Image
컴포넌트의 필수 속성Image 컴포넌트에는 다음과 같은 필수 속성들이 있습니다.
width
, height
속성은 정적 이미지거나 fill
속성을 사용했을 경우를 제외하고 필수로 입력해야 합니다. 다음 예시 코드처럼 작성하면 Image 컴포넌트를 간단하게 사용할 수 있습니다.
import Image from 'next/image'
const Page = () => {
return (
<Image
src={src}
alt={alt}
width={500}
height={500}
/>
)
}
export default Page;
width
, height
에는 원본 이미지의 픽셀 값을 숫자로 입력하면 됩니다.
만약 이미지의 크기를 정확히 알 수 없는 경우 어떻게 해야할까요? 그리고 반응형을 고려해야한다면 어떻게 해야할까요?
fill
속성fill
속성을 사용하면 이미지가 부모 요소를 채우도록 동작합니다. 이미지의 너비와 높이를 설정하는 대신 이미지를 부모 요소에 맞게 확장할 수 있습니다.
fill
속성을 적용하면 width
, height
를 설정할 수 없습니다.
만약 fill
과 width
, height
를 함께 적용하면 에러가 발생합니다.
<Image
src={src}
alt={alt}
width={500}
height={500}
fill
/>
style로 직접 width
, height
를 지정해도 에러가 발생합니다.
<Image
src={src}
alt={alt}
fill
style={{ width: '500px', height: '500px' }}
/>
fill
속성을 사용하면 다음과 같은 스타일이 기본으로 적용됩니다.
<img
style="position:absolute; height:100%; width:100%; left:0; top:0; right:0; bottom:0; color:transparent"
...
/>
개발자 도구 > Elements
탭에서 확인할 수 있습니다.
img
요소에 position: "absolute"
가 자동으로 지정되기 때문에 부모 요소에 position: "relative"
, position: "fixed"
, 또는 position: "absolute"
를 지정해야 합니다.
부모 요소에 position을 지정하지 않으면 다음과 같이 나타납니다.
개발자 도구 > Layers
탭에서 확인해보니 상단 header(z-index
를 설정하지 않은 상태)를 덮어버렸네요.
피드 형식으로 게시글이 나타나는 페이지인데 모든 이미지가 body를 기준으로 position: "absolute"
된 상태입니다.
부모 요소에 position: "relative"
를 적용하고 너비와 높이를 지정해보겠습니다.
<Container>
<Image
src={src}
alt={alt}
fill
/>
</Container>
// Emotion을 이용한 스타일 코드 작성
const Container = styled.div`
position: relative;
width: 100%;
height: 200px;
`;
부모 요소의 크기에 맞춰 이미지가 적용되었습니다.
예시 코드에서는 height: 200px;
로 높이 값을 직접 지정했습니다. 높이 값을 지정하지 않는다면, 부모 요소의 크기를 기반으로 이미지가 나타나므로 이미지도 높이 값이 height: 0
이 됩니다.
fill
fill
속성을 이용해서 반응형을 고려한 Image 컴포넌트를 적용해보겠습니다.
기본으로 적용되어 있는 스타일을 상쇄하기 위해 CSS 선택자 우선순위가 가장 높은 !important
를 사용할 수 있습니다.
Image 컴포넌트 자체에 다음과 같이 스타일을 직접 적용합니다.
<Container>
<StyledImage
src={src}
alt={alt}
fill
/>
</Container>
const Container = styled.div`
position: relative;
`;
const StyledImage = styled(Image)`
position: relative !important;
height: unset !important;
`;
먼저, position: relative !important;
를 적용하여 문서의 흐름에 맞게 배치될 수 있도록 합니다.
다음으로 height: unset !important;
를 적용했습니다.
이 때 unset
속성은 부모로부터 상속할 값이 존재하면 상속값(inherit
으로 동작)을, 그렇지 않다면 초깃값(initial
로 동작)을 사용합니다.
부모 요소에 높이 값을 상속하지 않으면 초깃값인 이미지의 높이 값이 적용됩니다.
height: initial !important;
을 적용해도 이미지의 높이 값에 맞춰 나타납니다.
프로젝트 상황에 맞게 선택해주세요 :)
width
, height
, sizes
이 방법은 훨씬 간단합니다.
<StyledImage
src={src}
alt={alt}
width={0}
height={0}
sizes="100vw"
/>
const StyledImage = styled(Image)`
width: 100%;
height: auto;
`;
fill
속성을 사용하지 않기 때문에 position: "absolute"
로 인해 부모 요소에 position을 지정하지 않아도 됩니다.
sizes="100vw"
를 지정하지 않으면 이미지가 상당히 깨져서 나타납니다. 그 이유는 Elements
탭에서 확인할 수 있습니다.
sizes="100vw"
를 지정하지 않았을 경우 적용되는 코드sizes="100vw"
를 지정했을 경우 적용되는 코드쨔쟌! header에 z-index
를 설정하지 않아도 이미지가 header를 가리지 않게 되었습니다.
저는 반응형 이미지를 적용할 수 있는 컴포넌트를 생성해서 적용했습니다.
import styled from '@emotion/styled';
import Image from 'next/image';
interface ResponsiveImageStyleProps {
aspectRatio?: number | 'auto';
}
interface ResponsiveImageProps extends ResponsiveImageStyleProps {
src: string;
alt: string;
quality?: number;
}
const ResponsiveImage = ({
src,
alt,
quality,
aspectRatio = 'auto',
}: ResponsiveImageProps) => {
return (
<ImageContainer aspectRatio={aspectRatio}>
<StyledImage src={src} alt={alt} quality={quality ?? 100} fill priority />
</ImageContainer>
);
};
export default ResponsiveImage;
const StyledImage = styled(Image)`
position: relative !important;
height: unset !important;
object-fit: cover;
`;
const ImageContainer = styled.div<ResponsiveImageStyleProps>`
position: relative;
${StyledImage} {
aspect-ratio: ${({ aspectRatio }) => aspectRatio};
}
`;
저는 첫 번째 방법을 이용하여 적용했습니다.
(header에 z-index
도 적용했습니다.)
추가로 object-fit: cover;
를 적용하여 이미지가 크기에 꽉 차게 설정하고, 상황에 따라 aspect-ratio
를 적용하기 위해 aspectRatio
값을 전달받을 수 있도록 했습니다.
aspectRatio
를 Image 컴포넌트에 직접 props로 전달하여 적용하지 않은 이유는 Image 컴포넌트에 지정된 속성 이외의 속성을 설정할 수 없기 때문입니다. 따라서, 부모 요소에서 aspectRatio
값을 받아 그 값을 Image 컴포넌트로 전달할 수 있도록 했습니다.
또는 부모 요소에서 aspectRatio
값으로 aspect-ratio
를 적용할 수 있습니다. 만약 부모 요소에 적용한다면 overflow: hidden;
도 함께 적용해주세요.
Next.js가 13버전으로 업데이트 되면서 Image 컴포넌트의 몇몇 속성들이 레거시가 되었습니다. 13버전 이 전 버전의 예시들은 찾기 쉬웠는데 13버전 예시는 비교적 찾기 어려워서 글을 작성하게 되었습니다.
fill
속성을 사용해본 적이 없다가 이번 기회에 사용하면서 많은 공부가 되었습니다.
!important
는 최대한 사용을 피하고자 하는데 fill
속성을 이용하고 싶어서 같이 사용했습니다. 하핫
unset
에 대해서도 스치듯 인지하고 있던 속성이었는데 이번에 다시 한 번 자세히 공부할 수 있어서 좋았습니다.
이 글이 많은 분들에게 도움이 되길 바랍니다 :)
참고