
LightHouse라는 확장프로그램을 통해서 렌더링 관련 브라우저 성능 테스트를 할 수 있다. 이러한 프로그램을 통해서 프로젝트의 퀄리티를 판단 할 수 있기 때문에 이를 고려해서 해야하지 않을까 하는 생각이 들어서 진행하게 되었다.

const KioskHeader = () => {
const { id } = useParams();
const [isHome, setIsHome] = useState(false);
const setIsHomeBtn = useCallback(() => {
if (window.location.pathname === `/kiosk/${id}`) {
setIsHome(true);
} else {
setIsHome(false);
}
}, [id]);
useEffect(() => {
setIsHomeBtn();
}, [isHome]);
return (
<div css={KioskHeaderStyle}>
<div css={KioskLogo}>
{isHome ? (
<img src={KioskLogoimg} alt="키오스크로고" />
) : (
<KioskHomeBtn />
)}
</div>
<div css={KioskLocation}>
<KioskAddress />
</div>
</div>
);
};
export default KioskHeader;
KioskHeader.js: 이 파일은 헤더 컴포넌트를 담당합니다. 이 컴포넌트는 로고 이미지와 Kiosk의 위치를 표시하며, 로고 이미지는 현재 위치가 홈 페이지인지를 판별하여 표시 여부를 결정합니다.
const KioskAddress = () => {
const { id } = useParams();
const [address, setAddress] = useState("");
const HTTP_API = process.env.REACT_APP_API_MAIN_KEY;
const getAddress = useCallback(async () => {
try {
const response = await axios({
url: `${HTTP_API}/kiosk/${id}/address`,
method: "GET",
});
setAddress(response.data.address);
} catch (error) {
console.log(error)
}
}, [id]);
useEffect(() => {
getAddress();
}, [getAddress]);
return (
<div css={addressDiv}>
<div className="addressIcon">
<LocationOnIcon color="action" fontSize="large" />
</div>
<span css={addressFont}>{address}</span>
</div>
);
};
export default React.memo(KioskAddress);
KioskAddress.js: 이 컴포넌트는 Kiosk의 주소 정보를 관리합니다. 각 키오스크의 고유 ID를 사용하여 서버로부터 해당 키오스크의 주소를 요청하고, 이를 화면에 표시합니다.
const KioskHomeSection = () => {
const { id } = useParams();
const [rentCnt, setRentCnt] = useState(0);
const [returnCnt, setReturnCnt] = useState(0);
const HTTP_API = process.env.REACT_APP_API_MAIN_KEY;
const ShowCnt = useCallback(async (id) => {
try {
if (id) {
const response = await axios({
method: "GET",
url: `${HTTP_API}/kiosk/home/brolly/${id}`,
});
setRentCnt(response.data.brollyCnt);
setReturnCnt(response.data.emptyCnt);
}
} catch (error) {
console.log(error);
}
}, []);
useEffect(() => {
if (id) {
ShowCnt(id);
}
}, [id]);
return (
<div css={KioskSectionStyle}>
<div css={KioskButtons}>
<KioskRentBtn rentCnt={rentCnt} />
<KioskRemoveEventListener />
<KioskReturnBtn returnCnt={returnCnt} />
</div>
<KioskWeather />
</div>
);
};
export default React.memo(KioskHomeSection);
KioskHomeSection.js: 이 컴포넌트는 Kiosk의 홈 페이지를 관리합니다. 현재 Kiosk에서 대여 가능한 우산의 개수와 반환 가능한 우산의 개수를 표시합니다. 이 데이터는 서버에 요청을 보내 얻습니다.
const KioskRentBtn = (rentCnt) => {
const { id } = useParams();
const [open, setOpen] = useState(false);
const navigate = useNavigate();
const KioskRentMove = React.useCallback(() => {
if (rentCnt.rentCnt === 0) {
setOpen((open) => !open);
} else {
setOpen(false);
navigate(`/kiosk/${id}/rent`);
}
}, [id, navigate, rentCnt.rentCnt]);
return (
<div css={KioskRentBtnDiv} onClick={KioskRentMove}>
<img css={RentBtnStyle} src={RentBtn} alt="RentBtn" />
<div css={RentBtnTextBox}>
<p className="title">대여</p>
<p className="content">(대여가능 : {rentCnt.rentCnt}개)</p>
</div>
<DialogBox open={open} handleToggle={KioskRentMove} />
</div>
);
};
export default React.memo(KioskRentBtn);
KioskRentBtn.js: 이 컴포넌트는 대여 버튼을 관리합니다. 현재 대여 가능한 우산의 개수를 받아와서 이를 화면에 표시하고, 대여 가능한 우산이 없을 경우에는 대여 페이지로 이동하지 않고 대신 대화 상자를 통해 사용자에게 알립니다.
const KioskReturnBtn = (returnCnt) => {
const { id } = useParams();
const [open, setOpen] = useState(false);
const navigate = useNavigate();
const KioskReturnMove = useCallback(() => {
if (returnCnt.returnCnt === 0) {
setOpen((prevOpen) => !prevOpen);
} else {
setOpen(false);
navigate(`/kiosk/${id}/return`);
}
}, [navigate, returnCnt.returnCnt, id]);
return (
<div css={KioskReturnBtnDiv} onClick={KioskReturnMove}>
<img css={ReturnBtnStyle} src={ReturnBtn} alt="ReturnBtn" />
<div css={ReturnBtnTextBox}>
<p className="title">반납</p>
<p className="content">(반납가능 : {returnCnt.returnCnt}개)</p>
</div>
<CustomDialog
open={open}
onClose={KioskReturnMove}
title={"우산을 반납할 공간이 없습니다."}
description={"다른 키오스크를 찾아주세요!"}
buttonLabel={"닫기"}
/>
</div>
);
};
export default React.memo(KioskReturnBtn);
KioskReturnBtn.js: 이 컴포넌트는 반납 버튼을 관리합니다. 현재 반납 가능한 우산의 개수를 받아와서 이를 화면에 표시하고, 반납 가능한 우산이 없을 경우에는 반납 페이지로 이동하지 않고 대신 대화 상자를 통해 사용자에게 알립니다.
const KioskWeather = () => {
const [weatherData, setWeatherData] = useState({
celsius: 0,
windspeed: 0,
weatherIcon: "",
weatherDescription: "",
});
const { id } = useParams();
const API_key = process.env.REACT_APP_WEATHER_API_KEY;
const getWeatherInfo = useCallback(async (id) => {
let lat, lon;
if (Number(id) === 1) {
lat = "36.10720148408362";
lon = "128.41786543716924";
} else if (Number(id) === 2) {
lat = "36.1028517393142";
lon = "128.3827474679339";
} else if (Number(id) === 3) {
lat = "36.10280020225937";
lon = "128.3823329267556";
}
try {
const response = await axios({
url: `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${API_key}&units=metric`,
method: "GET",
});
if (response) {
setWeatherData({
celsius: response.data.main.temp,
windspeed: response.data.wind.speed,
weatherIcon: response.data.weather[0].icon,
weatherDescription: response.data.weather[0].description,
});
}
} catch (error) {
console.error('Error occurred while fetching weather data: ', error);
}
}, []);
useEffect(() => {
getWeatherInfo(id);
}, [id, getWeatherInfo]);
return (
<div css={KioskHomeWeatherStyle}>
<div css={KioskHomeWeatherImg}>
<img
css={weatherImg}
src={`http://openweathermap.org/img/w/${weatherData.weatherIcon}.png`}
alt="weatherImage"
/>
<div css={weatherDescriptionStyle}>{weatherData.weatherDescription}</div>
</div>
<div css={KioskHomeWeatherTextBox}>
<p className="celsius">
현재 기온 {Math.round(weatherData.celsius).toFixed(1)}
<span>⁰</span>
</p>
<p className="windspeed">풍속 : {weatherData.windspeed}(m/s)</p>
</div>
</div>
);
};
export default KioskWeather;
KioskWeather.js: 이 컴포넌트는 현재 Kiosk의 위치에 대한 날씨 정보를 보여줍니다. 각 Kiosk의 위치에 따라 openweathermap API를 사용하여 날씨 정보를 가져오고 이를 화면에 표시합니다.
각 컴포넌트는 React Hooks(특히 useState, useEffect, useCallback)를 사용하여 상태 관리 및 사이드 이펙트를 처리합니다. 그리고 필요한 데이터를 서버에서 요청하여 가져오는 작업을 수행하며, 이 과정에서 axios 라이브러리를 사용합니다. React.memo를 사용하여 불필요한 렌더링을 방지하고, 애플리케이션의 퍼포먼스를 향상시킵니다.