[리팩토링]_성능 테스트 및 최적화하기

hanseungjune·2023년 7월 29일

리팩토링

목록 보기
6/25
post-thumbnail

성능 테스트를 왜 시도 했는가?

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

성능 테스트 이미지

작성 코드 및 설명

KioskHeader.js

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의 위치를 표시하며, 로고 이미지는 현재 위치가 홈 페이지인지를 판별하여 표시 여부를 결정합니다.

KioskAddress.js

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를 사용하여 서버로부터 해당 키오스크의 주소를 요청하고, 이를 화면에 표시합니다.

KioskHomeSection.js

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에서 대여 가능한 우산의 개수와 반환 가능한 우산의 개수를 표시합니다. 이 데이터는 서버에 요청을 보내 얻습니다.

KioskRentBtn.js

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: 이 컴포넌트는 대여 버튼을 관리합니다. 현재 대여 가능한 우산의 개수를 받아와서 이를 화면에 표시하고, 대여 가능한 우산이 없을 경우에는 대여 페이지로 이동하지 않고 대신 대화 상자를 통해 사용자에게 알립니다.

KioskReturnBtn.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: 이 컴포넌트는 반납 버튼을 관리합니다. 현재 반납 가능한 우산의 개수를 받아와서 이를 화면에 표시하고, 반납 가능한 우산이 없을 경우에는 반납 페이지로 이동하지 않고 대신 대화 상자를 통해 사용자에게 알립니다.

KioskWeather.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를 사용하여 불필요한 렌더링을 방지하고, 애플리케이션의 퍼포먼스를 향상시킵니다.

profile
필요하다면 공부하는 개발자, 한승준

0개의 댓글