SVG 이미지 - 반응형 / 화면 모드 대응하기
📌시작하며
프로젝트에서 dark/light 모드를 지원하면서 문득, 사용자가 이미지를 업로드 하지 않았을 때 보여지는 이미지가…너무 밝지 않나?! 라는 생각을 하게 됐다. light 모드에는 괜찮은데 dark 모드라면 사용자가 불편할 수 있겠다는 생각을 하게 되었고, 각 모드별로 대응 되는 이미지로 변경하기로 결정했다!
생각했던 첫 번째 방식은 default Image를 2가지 버전으로 만들어서 각각의 src에 넣어주는 거다.
1
2
3
4
5
6
<Image
src={jpthumbnail ? jpthumbnail : "/default_card.png"}
fill={true}
sizes="(max-width: 768px) 100vw, (max-width: 1023px) 704px, 400px"
alt={"음악 썸네일"}
/>
현재는 다음과 같은 방식으로 작성되어 있는데, 이 코드에서, thumbnail이 있는지 확인하고, mode까지 한 번 더 확인해서 src에 값을 주는 방식이다.
그렇지만 코드가 복잡해질 것 같다는 생각을 했고, 현재 다른 컴포넌트들의 경우 transition
이 적용되어서 모드를 부드럽게 전환하기 때문에 이미지만 딱딱하게 바뀌는 것이 아쉬웠다.
그래서, 두 번재 방식인 svg 이미지를 사용해보는 것으로 결정!
✅SVG 사용하기
➡️SVG 컴포넌트 만들기
일단 default_Image 자체는 Figma에서 작업했기 때문에 svg로 쉽게 export 할 수 있었다.
이 svg파일을 가지고 DefaultImage.tsx
컴포넌트를 만들어 주었다.
1
2
3
4
5
6
7
8
9
10
11
12
13
/* DefaultImage*/
<div className="h-auto w-full">
<svg
width="350"
height="350"
viewBox="0 0 350 350"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="350" height="350" fill="white" />
{/* 생략 */}
</svg>
</div>
➡️부모 요소에 맞추기
이렇게 했더니 문제가 발생했다! 현재 svg의 viewBox가 0 0 350 350 으로 지정되어 있어, 해당 내용이 고정되어 부모 컴포넌트에 적절히 cover 되지 않는 것이다. 🤔 이걸 어떡하지 하는차에 너무 쉽게 해결방법을 찾았다.
width
와 height
에 350이란 고정된 숫자값이 아니라 initial
을 지정해주기만 하면 된다!
1
2
3
4
5
6
7
8
9
10
11
12
13
/* DefaultImage*/
<div className="h-auto w-full">
<svg
width="initial"
height="initial"
viewBox="0 0 350 350"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="350" height="350" fill="white" />
{/* 생략 */}
</svg>
</div>
➡️각각의 모드 대응하기
이제 svg가 각 모드에 따라 컬러를 변경할 수 있도록 기존에 Tailwind를 사용하던 방식 그대로 지정해주면 된다. 예를 들어 <rect>
에 지정되어 있던 fill='white'
를 삭제하고, className="fill-[#ffffff] dark:fill-[#9A9EAD]"
과 같이 지정해주면 된다.
만약 fill이 아니라 stroke 였다면, 이 또한 동일한 방식으로 기존 stroke='white'
를 지우고 stroke-[#ffffff] dark:stroke-[#9A9EAD]
와 같이 지정해주면 된다.
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
/* DefaultImage*/
<div className="h-auto w-full">
<svg
width="initial"
height="initial"
viewBox="0 0 350 350"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="26"
y="91.2886"
width="7.02564"
height="47.4231"
rx="3.51282"
className="fill-[#ffffff] dark:fill-[#9A9EAD]"
/>
<circle
cx="175"
cy="115"
r="76.0128"
className="fill-[#E5E8EA] stroke-[#ffffff] dark:fill-[#3D4353] dark:stroke-[#9A9EAD]"
stroke-width="8"
/>
{/* 생략 */}
</svg>
</div>
➡️기존에 사용되던 이미지 대체하기
이제 사용자가 업로드한 thumbnail이 없는경우 만들어 둔 DefaultImage
컴포넌트를 보여주어야 하므로 아래와 같이 코드를 수정했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div
className={cn("relative aspect-square w-full", {
hidden: selectLang === "jp",
block: selectLang === "ko",
})}
>
{selectLang === "ko" &&
(kothumbnail ? (
<Image
src={kothumbnail}
fill={true}
sizes="(max-width: 768px) 100vw, (max-width: 1023px) 704px, 400px"
alt={"음악"}
/>
) : (
<DefaultImage />
))}
</div>
✅결과
결론적으로 Tailwind를 활용해 dark/light 모드 적절히 대응하는 이미지 컴포넌트를 만들어 적용할 수 있었다! 확실히 dark모드에서는 더 어두운 default Image를 보여줄 수 있어서 사용성 측면에서 더 나은 선택이었다고 생각한다.🥰
🗂️참고 사이트
- https://www.reddit.com/r/webflow/comments/rg84yx/how_do_i_make_this_svg_responsive/