Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions src/components/CurrentWeather.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import React from "react";
import {
CurrentWeatherWrapper,
Temperature,
WeatherCode,
} from "./styles/StyledComponents";
import { getWeatherDescription } from "../utils/weather";
import { CurrentWeatherWrapper, Temperature, WeatherCode } from "./styles/StyledComponents";
import { getWeatherDescription, formatCurrentData } from "../utils/weather";
import { DEGREE_CELSIUS, LOADING } from "../constants/uiConstants";

const CurrentWeather = ({ weatherData, isLoading }) => {
const currentData = formatCurrentData(weatherData);

if (isLoading) {
return <div>채워주세요</div>;
return <div>{LOADING}</div>;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Loading 대신 스피너나 간단한 스켈레톤 UI를 추가해보는 것도 좋을 것 같아요!

https://blog.makerjun.com/2f7a9818-df45-4eab-9768-b79d61b4d0ec

Copy link
Copy Markdown
Author

@onegood07 onegood07 May 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import { SpinnerWrapper } from "./styles/StyledComponents";
import { ClockLoader } from "react-spinners";

const Spinner = () => {
  return (
    <SpinnerWrapper>
      <ClockLoader color="white" size={150} />
    </SpinnerWrapper>
  );
};

export default Spinner;

찾아보니 스피너 라이브러리가 있어서 사용해보았습니다!

참고자료
https://www.davidhu.io/react-spinners/

https://choijying21.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A1%9C%EB%94%A9%ED%99%94%EB%A9%B4-%EC%8A%A4%ED%94%BC%EB%84%88-%EB%A7%8C%EB%93%9C%EB%8A%94-%EC%97%AC%EB%9F%AC%EA%B0%80%EC%A7%80-%EB%B0%A9%EB%B2%95gif-React-spinners

}

return <div>채워주세요</div>;
return (
<CurrentWeatherWrapper>
<Temperature>
{currentData.temperature}
{DEGREE_CELSIUS}
</Temperature>
<WeatherCode>{getWeatherDescription(currentData.weatherCode)}</WeatherCode>
</CurrentWeatherWrapper>
);
};

export default CurrentWeather;
17 changes: 15 additions & 2 deletions src/components/DailyForecast.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import React from "react";
import { DailyForecastWrapper, DailyItem } from "./styles/StyledComponents";
import { getWeatherDescription, formatDailyData } from "../utils/weather";
import { DEGREE_CELSIUS } from "../constants/uiConstants";

const DailyForecast = ({ weatherData }) => {
const dailyData = formatDailyData(weatherData);

return <div>채워주세요</div>;
return (
<DailyForecastWrapper>
{dailyData.map((data) => (
<DailyItem key={data.timeString}>
<div>{data.date}</div>
<div>{getWeatherDescription(data.weatherCode)}</div>
<div>
{data.temperature}
{DEGREE_CELSIUS}
</div>
</DailyItem>
))}
</DailyForecastWrapper>
);
};

export default DailyForecast;
20 changes: 18 additions & 2 deletions src/components/HourlyForecast.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
import React from "react";
import { HourlyForecastWrapper, HourlyItem } from "./styles/StyledComponents";
import { getWeatherDescription, formatHourlyData } from "../utils/weather";
import { DEGREE_CELSIUS, HOUR_SUFFIX } from "../constants/uiConstants";

const HourlyForecast = ({ weatherData }) => {
const hourlyData = formatHourlyData(weatherData);

return <div>채워주세요</div>;
return (
<HourlyForecastWrapper>
{hourlyData.map((data) => (
<div key={data.timeString}>
<HourlyItem>
{data.hour}
{HOUR_SUFFIX}
</HourlyItem>
<HourlyItem>
{data.temperature}
{DEGREE_CELSIUS}
</HourlyItem>
<HourlyItem>{getWeatherDescription(data.weatherCode)}</HourlyItem>
</div>
))}
</HourlyForecastWrapper>
);
};

export default HourlyForecast;
38 changes: 17 additions & 21 deletions src/constants/exampleResponse.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,29 +183,25 @@ const exampleResponse = {
"2024-12-17T23:00",
],
temperature_2m: [
0.9, 0.6, 0.1, -0.3, -0.8, -0.6, -1.3, -1.4, -1.5, -1.0, 0.1, 1.6, 2.7,
4.2, 5.3, 5.7, 5.7, 4.8, 3.6, 2.8, 1.8, 0.8, 0.0, -0.6, -1.0, -1.6, -2.1,
-2.6, -3.1, -3.5, -3.8, -4.2, -4.7, -3.9, -2.5, -1.0, 0.3, 1.4, 2.0, 2.2,
2.0, 1.3, 0.6, 0.1, -0.3, -0.6, -0.9, -1.0, -1.3, -1.4, -1.5, -1.8, -1.8,
-2.0, -2.1, -2.1, -2.1, -1.4, -0.2, -0.8, -0.6, 0.3, 0.9, 1.5, 1.4, 0.9,
0.4, 0.3, 0.0, -0.9, -1.5, -2.2, -2.8, -3.3, -4.0, -4.7, -5.2, -5.7, -6.1,
-6.3, -6.5, -5.9, -4.8, -3.4, -2.2, -1.1, -0.5, -0.1, 0.4, -0.2, -0.9,
-1.3, -1.9, -2.6, -3.1, -3.4, -3.7, -3.8, -3.9, -4.0, -4.1, -4.1, -4.1,
-3.9, -3.7, -3.0, -1.6, 0.2, 1.8, 2.9, 3.7, 4.0, 3.7, 2.9, 2.1, 1.3, 0.6,
-0.1, -0.7, -1.3, -1.8, -2.3, -2.7, -3.1, -3.7, -4.3, -4.6, -4.8, -4.6,
-3.9, -2.1, 0.3, 2.1, 2.7, 2.8, 2.8, 2.0, 1.0, 0.2, -0.3, -0.6, -0.8,
-1.0, -1.1, -1.4, -1.8, -2.4, -2.9, -3.4, -3.8, -4.1, -4.2, -4.0, -3.5,
-2.3, -0.7, 0.7, 1.5, 2.0, 2.2, 1.7, 0.9, 0.2, -0.1, -0.3, -0.5, -0.8,
-1.1,
0.9, 0.6, 0.1, -0.3, -0.8, -0.6, -1.3, -1.4, -1.5, -1.0, 0.1, 1.6, 2.7, 4.2, 5.3, 5.7, 5.7,
4.8, 3.6, 2.8, 1.8, 0.8, 0.0, -0.6, -1.0, -1.6, -2.1, -2.6, -3.1, -3.5, -3.8, -4.2, -4.7,
-3.9, -2.5, -1.0, 0.3, 1.4, 2.0, 2.2, 2.0, 1.3, 0.6, 0.1, -0.3, -0.6, -0.9, -1.0, -1.3, -1.4,
-1.5, -1.8, -1.8, -2.0, -2.1, -2.1, -2.1, -1.4, -0.2, -0.8, -0.6, 0.3, 0.9, 1.5, 1.4, 0.9,
0.4, 0.3, 0.0, -0.9, -1.5, -2.2, -2.8, -3.3, -4.0, -4.7, -5.2, -5.7, -6.1, -6.3, -6.5, -5.9,
-4.8, -3.4, -2.2, -1.1, -0.5, -0.1, 0.4, -0.2, -0.9, -1.3, -1.9, -2.6, -3.1, -3.4, -3.7, -3.8,
-3.9, -4.0, -4.1, -4.1, -4.1, -3.9, -3.7, -3.0, -1.6, 0.2, 1.8, 2.9, 3.7, 4.0, 3.7, 2.9, 2.1,
1.3, 0.6, -0.1, -0.7, -1.3, -1.8, -2.3, -2.7, -3.1, -3.7, -4.3, -4.6, -4.8, -4.6, -3.9, -2.1,
0.3, 2.1, 2.7, 2.8, 2.8, 2.0, 1.0, 0.2, -0.3, -0.6, -0.8, -1.0, -1.1, -1.4, -1.8, -2.4, -2.9,
-3.4, -3.8, -4.1, -4.2, -4.0, -3.5, -2.3, -0.7, 0.7, 1.5, 2.0, 2.2, 1.7, 0.9, 0.2, -0.1, -0.3,
-0.5, -0.8, -1.1,
],
weather_code: [
2, 2, 2, 2, 2, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 2, 3, 2, 2,
2, 2, 1, 1, 2, 2, 1, 1, 2, 73, 75, 51, 51, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 2, 2, 2, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 2, 3, 2, 2, 2, 2, 1, 1, 2, 2, 1, 1, 2, 73, 75,
51, 51, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2,
],
},
daily_units: { time: "iso8601", weather_code: "wmo code" },
Expand Down
6 changes: 6 additions & 0 deletions src/constants/uiConstants.js
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Units
export const DEGREE_CELSIUS = "°C";
export const HOUR_SUFFIX = "시";

// Message
export const LOADING = "Loading...";
23 changes: 23 additions & 0 deletions src/utils/validators.js
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

데이터 검증 로직 좋습니다 😁

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export const isToDay = (dateInData, currentDate) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export const isToDay = (dateInData, currentDate) => {
export const isToday = (dateInData, currentDate) => {

return (
dateInData.getFullYear() === currentDate.getFullYear() &&
dateInData.getMonth() === currentDate.getMonth() &&
dateInData.getDate() === currentDate.getDate()
);
};

export const isWithin12Hours = (dateInData, currentDate) => {
const diff = (dateInData - currentDate) / (1000 * 60 * 60);
return diff >= 0 && diff <= 12;
};

export const isInCurrentTime = (dateInData) => {
const now = new Date();

return (
dateInData.getFullYear() === now.getFullYear() &&
dateInData.getMonth() === now.getMonth() &&
dateInData.getDate() === now.getDate() &&
dateInData.getHours() === now.getHours()
);
};
56 changes: 52 additions & 4 deletions src/utils/weather.js
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

전체적으로 안에 포매팅 기능을 하는 부분들이 많은데, 이를 별도의 util 함수로 분리하면 좋겠습니다!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// weather.js -> fetchWeatherData.js
export const fetchCurrentData = (weatherData) => {
  if (!weatherData) {
    errorLog(fetchCurrentData.name, FAILED_LOAD_DATA);
    return;
  }

  const index = weatherData.hourly.time.findIndex((time) => isInCurrentTime(new Date(time)));

  return {
    time: new Date(weatherData.hourly.time[index]),
    temperature: weatherData.hourly.temperature_2m[index],
    weatherCode: weatherData.hourly.weather_code[index],
  };
};

fetch로 시작하는 함수들로 변경하여 최대한 데이터 원본 형태로 불러오도록 수정했습니다.

Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { isToDay, isWithin12Hours, isInCurrentTime } from "./validators";
import { getWeekdayName } from "./weeks";

export const getWeatherDescription = (code) => {
const weatherCodes = {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기도 constants.js로 분리하면 어떨까요?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constants로 분리 후, 아래와 같이 변경했습니다!

// weather.js
export const getWeatherDescription = (code) => {
  return WEATHER_CODE[code] || "알 수 없음";
};

0: "맑음",
Expand All @@ -19,14 +22,59 @@ export const getWeatherDescription = (code) => {
return weatherCodes[code] || "알 수 없음";
};

export const formatCurrentData = (weatherData) => {
if (!weatherData) return;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

만약 문제가 발생해서 return했다면 console에 log라도 찍어주면 개발할 때 빠르게 어느 부분에서 문제가 생겼는지 확인할 수 있을 것 같아요!

Copy link
Copy Markdown
Author

@onegood07 onegood07 May 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// logger.js
export const errorLog = (component, message) => {
  console.log(`[${component}] ${message}`);
};

// weather.js
export const formatCurrentData = (weatherData) => {
if (!weatherData) {
    errorLog(formatCurrentData.name, FAILED_LOAD_DATA);
    return;
  }
}

다음과 같이 작성했습니다!


const index = weatherData.hourly.time.findIndex((time) => isInCurrentTime(new Date(time)));
const date = new Date(weatherData.hourly.time[index]);

return {
time: date.toISOString(),
temperature: Math.floor(weatherData.hourly.temperature_2m[index]),
weatherCode: weatherData.hourly.weather_code[index],
};
};

export const formatHourlyData = (weatherData) => {
if (!weatherData) return [];
// 밑에 코드 채워주세요
return [];
const currentDate = new Date();
const result = [];

weatherData.hourly.time.forEach((time, index) => {
const hourlyDate = new Date(time);

if (
isToDay(hourlyDate, currentDate) &&
isWithin12Hours(hourlyDate, currentDate) &&
result.length < 12
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이것도 validate 로직이니까 따로 분리하면 좋을 것 같네요!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// validators.js
export const isValidHourlyData = (hourlyDate, currentDate, collectedForecasts) => {
  return (
    isToday(hourlyDate, currentDate) &&
    isWithin12Hours(hourlyDate, currentDate) &&
    collectedForecasts.length < 12
  );
};

기존에 result였던 배열명도 collectedForecasts로 변경하고 validators 안에 분리했습니다!

) {
result.push({
timeString: hourlyDate.toISOString(),
hour: hourlyDate.getHours(),
temperature: Math.floor(weatherData.hourly.temperature_2m[index]),
weatherCode: weatherData.hourly.weather_code[index],
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

포매팅 함수도 분리해 봅시다!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// formatWeatherData.js
export const formatHourlyData = (weatherData) => {
  if (!weatherData) {
    errorLog(formatHourlyData.name, FAILED_LOAD_DATA);
    return null;
  }

  return weatherData.map(({ time, temperature, weatherCode }) => {
    const date = new Date(time);
    return {
      time: date.toISOString(),
      hour: date.getHours(),
      temperature: Math.floor(temperature),
      weatherCode: getWeatherDescription(weatherCode),
    };
  });
};
  • fetchWeatherData.js와 formatWeatherData.js로 분리했습니다~

});
}
});

return result;
};

export const formatDailyData = (weatherData) => {
if (!weatherData) return [];
// 밑에 코드 채워주세요
return [];

const result = weatherData.daily.time.map((time, index) => {
const dailyDate = new Date(time);

return {
timeString: dailyDate.toISOString(),
date: `${dailyDate.getMonth() + 1}월 ${dailyDate.getDate()}일 (${getWeekdayName(
dailyDate.getDay()
)})`,
temperature: Math.floor(weatherData.daily.temperature_2m_max[index]),
weatherCode: weatherData.daily.weather_code[index],
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

포매팅!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// formatWeatherData.js
export const formatDailyData = (weatherData) => {
  if (!weatherData) {
    errorLog(formatDailyData.name, FAILED_LOAD_DATA);
    return null;
  }

  return weatherData.map(({ time, temperature, weatherCode }) => {
    const date = new Date(time);

    return {
      time: date.toISOString(),
      date: `${date.getMonth() + 1}월 ${date.getDate()}일 (${getWeekdayDescription(
        date.getDay()
      )})`,
      temperature: Math.floor(temperature),
      weatherCode: getWeatherDescription(weatherCode),
    };
  });
};
  • fetchWeatherData.js와 formatWeatherData.js로 분리하여 포매팅했습니다!

};
});

return result;
};
13 changes: 13 additions & 0 deletions src/utils/weeks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const getWeekdayName = (dayIndex) => {
const weeksCodes = {
0: "일",
1: "월",
2: "화",
3: "수",
4: "목",
5: "금",
6: "토",
};

return weeksCodes[dayIndex];
};