Post

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 되지 않는 것이다. 🤔 이걸 어떡하지 하는차에 너무 쉽게 해결방법을 찾았다.

widthheight에 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/
This post is licensed under CC BY 4.0 by the author.