Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
38 changes: 6 additions & 32 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-scripts": "5.0.1",
"styled-components": "^6.1.13"
"styled-components": "^6.1.18"
},
"scripts": {
"start": "react-scripts start",
Expand Down
24 changes: 12 additions & 12 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,30 @@ import CurrentWeather from "./components/CurrentWeather";
import HourlyForecast from "./components/HourlyForecast";
import DailyForecast from "./components/DailyForecast";

function App() {
function App() { //Api를 불러옴, 서울의 날짜, 날씨 데이터 요청
const API_URL =
"https://api.open-meteo.com/v1/forecast?latitude=37.566&longitude=126.9784&hourly=temperature_2m,weather_code&daily=weather_code,temperature_2m_max&timezone=Asia%2FTokyo&forecast_days=7";

const [weatherData, setWeatherData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [weatherData, setWeatherData] = useState(null); //날씨 데이터를 저장, 처음에는 null
const [isLoading, setIsLoading] = useState(true); //로딩 여부를 저장, 처음엔 true->데이터를 불러오는 중

useEffect(() => {
fetch(API_URL)
.then((response) => response.json())
fetch(API_URL) //컴포넌트가 처음 렌더링 될 때 API를 호출함
.then((response) => response.json()) //응답데이터를 json으로 변환함
.then((data) => {
setWeatherData(data);
setIsLoading(false);
})
setWeatherData(data); //날씨 데이터를 weatherData에 저장함
setIsLoading(false); //로딩 완료
})
.catch((error) => {
console.error("데이터 로드 실패:", error);
setIsLoading(false);
setIsLoading(false); //error가 발생해도 로딩이 종료됨
});
}, []);

return (
<Container>
<CurrentWeather weatherData={weatherData} isLoading={isLoading} />
{!isLoading && weatherData && (
<Container>
<CurrentWeather weatherData={weatherData} isLoading={isLoading} /> {/* 현재 날씨 컴포넌트 렌더링*/}
{!isLoading && weatherData && ( //로딩 끝나면, 데이터가 있으면 시간별, 일별 데이터 표시
<>
<HourlyForecast weatherData={weatherData} />
<DailyForecast weatherData={weatherData} />
Expand Down
28 changes: 25 additions & 3 deletions src/components/CurrentWeather.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,32 @@ import { getWeatherDescription } from "../utils/weather";

const CurrentWeather = ({ weatherData, isLoading }) => {
if (isLoading) {
return <div>채워주세요</div>;
return <div>불러오는 중...</div>;
}
if(!weatherData||!weatherData.hourly){
return <div>현재 날씨 데이터를 불러 올 수 없습니다.</div>
}

return <div>채워주세요</div>;
const time=weatherData.hourly.time;
const temperature_2m=weatherData.hourly.temperature_2m;
const weather_code=weatherData.hourly.weather_code;
const now= new Date();
let closestIndex=0;
for(let i=0; i<time.length; i++){
const forecastTime=new Date(time[i]);
if(forecastTime>now){ //예보 시간이 지금보다 클때, 미래일때
closestIndex=i; // 그 시간을 가장 가까운 시간으로 지정
break;
}
}
const temperature=temperature_2m[closestIndex];
const weathercode=weather_code[closestIndex];
console.log("현재 예보 시간: ", closestIndex);
return (
<CurrentWeatherWrapper>
<Temperature>{temperature}°C</Temperature>
<WeatherCode>{getWeatherDescription(weathercode)}</WeatherCode>
</CurrentWeatherWrapper>
);
};

export default CurrentWeather;
15 changes: 12 additions & 3 deletions src/components/DailyForecast.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@ import { DailyForecastWrapper, DailyItem } from "./styles/StyledComponents";
import { getWeatherDescription, formatDailyData } from "../utils/weather";

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

return <div>채워주세요</div>;
return (
<DailyForecastWrapper>
{dailyData.map((item,index)=>(
<DailyItem key={index}>
<p>{item.day}</p>
<p>{getWeatherDescription(item.weatherCode)}</p>
<p>{item.temperature}</p>
</DailyItem>
))}
</DailyForecastWrapper>
);
};

export default DailyForecast;
14 changes: 12 additions & 2 deletions src/components/HourlyForecast.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,19 @@ import { HourlyForecastWrapper, HourlyItem } from "./styles/StyledComponents";
import { getWeatherDescription, formatHourlyData } from "../utils/weather";

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

return <div>채워주세요</div>;
return (
<HourlyForecastWrapper>
{hourlyData.map((item,index)=>(
<HourlyItem key={index}>
<p>{item.hourLabel}</p>
<p>{item.temperature}</p>
<p>{getWeatherDescription(item.weatherCode)}</p>
</HourlyItem>
))}
</HourlyForecastWrapper>
)
};

export default HourlyForecast;
13 changes: 13 additions & 0 deletions src/components/styles/StyledComponents.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,22 @@ export const HourlyForecastWrapper = styled.div`
background: rgba(255, 255, 255, 0.2);
border-radius: 15px;
margin-top: auto;
&::-webkit-scrollbar {
height: 8px;
}

&::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 10px;
}

&::-webkit-scrollbar-track {
background: transparent;
}
`;

export const HourlyItem = styled.div`
white-space: nowrap;
display: flex;
flex-direction: column;
align-items: center;
Expand Down
59 changes: 55 additions & 4 deletions src/utils/weather.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,68 @@ export const getWeatherDescription = (code) => {
73: "보통 눈",
75: "강한 눈",
};
return weatherCodes[code] || "알 수 없음";
return weatherCodes[code] || "알 수 없음";
};

export const formatHourlyData = (weatherData) => {
if (!weatherData) return [];
// 밑에 코드 채워주세요
return [];
/*latitude, longitude: 서울 좌표 (37.566°N, 126.9784°E)
hourly: 시간별 날씨 데이터 포함:
temperature_2m: 섭씨 온도
weather_code: 날씨 상태 코드
daily: 일별 날씨 데이터 포함:
weather_code: 일별 날씨 상태 코드
temperature_2m_max: 일 최고 기온
timezone: Asia/Tokyo
forecast_days: 7일 예보 */
const time= weatherData.hourly.time; //시간
const temperature_2m=weatherData.hourly.temperature_2m; //섭씨 온도
const weather_code= weatherData.hourly.weather_code; //날시 상태 코드

const currentDayHour=new Date(); //현재 날짜, 시간

const parsed=[];
for (let i=0; i< time.length; i++){
const hour=new Date(time[i]);
if(currentDayHour<=hour&&parsed.length<12){
parsed.push({
hourLabel: `${hour.getHours()}시`, //시간
temperature: `${temperature_2m[i]}°C`,//섭씨 온도
weatherCode: weather_code[i], //날씨 설명
})
}
}
console.log("formatHourlyData: ", parsed);
console.log("현재 시각: ", currentDayHour);

return parsed;
};

export const formatDailyData = (weatherData) => {
if (!weatherData) return [];
if (!weatherData) return []; //날씨데이터가 없으면 빈 배열 반환
// 밑에 코드 채워주세요
return [];
const day= weatherData.daily.time; //날짜 배열
const temperature_2m_max=weatherData.daily.temperature_2m_max //최고 온도
const weather_code=weatherData.daily.weather_code //날씨 코드

return day.map((dateStr, index)=>{ //map 반복
const dayDate=new Date(dateStr);
const formattedDate=dayDate.toLocaleDateString("ko-KR",{ //한글
month: "long", //5월 16일로 출력
day:"numeric",
});
const weekOneChar=dayDate.toLocaleDateString("ko-KR",{
weekday:"long" //금요일

}).charAt(0); //금
return{
day: `${formattedDate} (${weekOneChar})`, //5월 16일 (금)
temperature: `${temperature_2m_max[index]}°C` ,
weatherCode: weather_code[index],
}



})
};