Next.js Image
를 사용하면 처음 이미지를 불러오는 코드를 작성할 때부터 이미지의 크기를 지정해주어야 한다.
따라서 화면 크기에 따라 이미지의 사이즈를 동적으로 바꿀 수 없다는 문제가 발생한다.
생각한 방법은 아래 두 가지 방법이다.
나는 두번 째 방법을 사용하여 이 문제를 해결했다.
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
const [margin, setMargin] = useState(0);
useEffect(() => {
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
if (windowWidth > windowHeight) {
setHeight(windowHeight * 0.8);
setWidth((windowHeight * 0.8) / 1.414);
setMargin(windowHeight * 0.1);
} else {
setWidth(windowWidth * 0.8);
setHeight(windowWidth * 0.8 * 1.414);
setMargin((windowHeight - windowWidth * 0.8 * 1.414) / 2);
}
}, []);
내가 띄우려는 모든 사진의 비율은 1:sqrt(2) 이다. 따라서 그냥 1.414를 곱하거나 나누어주는 식으로 너비와 높이를 계산해버렸다. 또한 화면 가운데에 모달 이미지가 위치하도록 만들기 위해서 margin-top을 위 코드와 같이 새로운 변수에 할당하여 만들어주었다.
그런데 여기서 문제는 useEffect로 화면이 처음 렌더링될 때 높이와 너비를 한 번만 불러오는 방식이기 때문에 사용자가 새로고침 없이 화면의 크기만 바꾼다면 이미지의 비율이 맞지 않게 된다.
앞서 언급한 문제를 해결하고자 다음 방법을 고안했다.
window.addEventListener('resize', callback);
을 사용하고 useEffect
에서 이 값이 변할때마다 가로 세로 계산이 실행되도록 하는 것이다.
주의: removeEventListener
무조건 해줘야함!!
작성한 코드는 다음과 같다.
const modalSizing = () => {
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
if (windowWidth > windowHeight) {
setHeight(windowHeight * 0.8);
setWidth((windowHeight * 0.8) / 1.414);
setMargin(windowHeight * 0.1);
} else {
setWidth(windowWidth * 0.8);
setHeight(windowWidth * 0.8 * 1.414);
setMargin((windowHeight - windowWidth * 0.8 * 1.414) / 2);
}
};
useEffect(() => {
modalSizing();
window.addEventListener("resize", modalSizing);
return () => {
window.removeEventListener("resize", modalSizing);
};
}, []);
이렇게 하면 화면 크기가 바뀌더라도 이미지의 사이즈를 동적으로 변화시킬 수 있다.
정석이라고 하기에는 애매하지만 원하는 기능을 구현하긴 했으니 다행이라고 생각한다...ㅎ
아래는 Modal Component 관련 코드입니다.
./components/modal.tsx
import Image from "next/image";
import styles from "../pages/modules/Modal.module.css";
import React, { useEffect, useState } from "react";
interface ModalProps {
url: string;
onClose: () => void;
visible: boolean;
}
export default function Modal({ url, onClose, visible }: ModalProps) {
const handleCloseClick = (e: any) => {
e.preventDefault();
onClose();
};
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
const [margin, setMargin] = useState(0);
const modalSizing = () => {
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
if (windowWidth > windowHeight) {
setHeight(windowHeight * 0.8);
setWidth((windowHeight * 0.8) / 1.414);
setMargin(windowHeight * 0.1);
} else {
setWidth(windowWidth * 0.8);
setHeight(windowWidth * 0.8 * 1.414);
setMargin((windowHeight - windowWidth * 0.8 * 1.414) / 2);
}
};
useEffect(() => {
modalSizing();
window.addEventListener("resize", modalSizing);
return () => {
window.removeEventListener("resize", modalSizing);
};
}, []);
return (
<>
<div className={styles.modal} style={{ display: `${visible ? "block" : "none"}` }}>
<div className={styles.modal_close} onClick={handleCloseClick}>
창 닫기 ☓
</div>
<div className={styles.modal_body}>
<Image src={`/sessions/${url}.webp`} alt="modal-img" width={width} height={height} className={styles.modal_img} style={{ marginTop: margin }} />
</div>
</div>
</>
);
}
./pages/modules/Modal.module.css
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.4);
z-index: 100;
margin: auto;
}
.modal_body {
position: absolute;
top: 50%;
left: 50%;
padding: 40px;
text-align: center;
background-color: transparent;
border-radius: 10px;
transform: translateX(-50%) translateY(-50%);
animation: showModal 0.5s;
height: 100%;
width: 100%;
box-shadow: none;
}
@keyframes showModal {
0% {
opacity: 0;
transform: translateX(-50%) translateY(-60%);
}
100% {
opacity: 1;
transform: translateX(-50%) translateY(-50%);
}
}
.modal_close {
position: fixed;
top: 20px;
right: 20px;
width: 100px;
height: 40px;
font-size: 17px;
background-color: #fff;
border-radius: 35px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
margin: 0;
color: #222;
z-index: 100;
}
.modal_img {
border-radius: 10px;
box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);
object-fit: contain;
}