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
33 changes: 13 additions & 20 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,33 @@
import React, { useState, useEffect } from "react";
import { Container } from "./components/styles/StyledComponents";
import React, { useEffect, useState } from "react";
import CurrentWeather from "./components/CurrentWeather";
import HourlyForecast from "./components/HourlyForecast";
import DailyForecast from "./components/DailyForecast";
import { Wrapper } from "./components/styles/StyledComponents";

function App() {
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 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";

function App() {
const [weatherData, setWeatherData] = useState(null);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
fetch(API_URL)
.then((response) => response.json())
.then((data) => {
setWeatherData(data);
setIsLoading(false);
})
.catch((error) => {
console.error("데이터 로드 실패:", error);
setIsLoading(false);
});
.then((res) => res.json())
.then((data) => setWeatherData(data))
.catch((err) => console.error("날씨 데이터 에러:", err));
}, []);

return (
<Container>
<CurrentWeather weatherData={weatherData} isLoading={isLoading} />
{!isLoading && weatherData && (
<Wrapper>
{weatherData && (
<>
<CurrentWeather weatherData={weatherData} />
<HourlyForecast weatherData={weatherData} />
<DailyForecast weatherData={weatherData} />
</>
)}
</Container>
</Wrapper>
);
}

export default App;
export default App;
23 changes: 12 additions & 11 deletions src/components/CurrentWeather.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import React from "react";
import {
CurrentWeatherWrapper,
Temperature,
WeatherCode,
} from "./styles/StyledComponents";
import { CenterText, TempText, DescText } from "./styles/StyledComponents";
import { getWeatherDescription } from "../utils/weather";

const CurrentWeather = ({ weatherData, isLoading }) => {
if (isLoading) {
return <div>채워주세요</div>;
}
const CurrentWeather = ({ weatherData }) => {
const currentTemp = weatherData.hourly.temperature_2m[0];
const currentCode = weatherData.hourly.weather_code[0];
const description = getWeatherDescription(currentCode);

return <div>채워주세요</div>;
return (
<CenterText>
<TempText>{currentTemp}°C</TempText>
<DescText>{description}</DescText>
</CenterText>
);
};

export default CurrentWeather;
export default CurrentWeather;
18 changes: 14 additions & 4 deletions src/components/DailyForecast.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import React from "react";
import { DailyForecastWrapper, DailyItem } from "./styles/StyledComponents";
import { DailyCard } from "./styles/StyledComponents";
import { getWeatherDescription, formatDailyData } from "../utils/weather";

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

return <div>채워주세요</div>;
return (
<DailyCard>
{daily.map((item, idx) => (
<React.Fragment key={idx}>
<div>{item.date}</div>
<div>{getWeatherDescription(item.code)}</div>
<div>{item.temp}</div>
</React.Fragment>
))}
</DailyCard>
);
};

export default DailyForecast;
export default DailyForecast;
18 changes: 14 additions & 4 deletions src/components/HourlyForecast.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import React from "react";
import { HourlyForecastWrapper, HourlyItem } from "./styles/StyledComponents";
import { HourlyCard, ForecastItem } from "./styles/StyledComponents";
import { getWeatherDescription, formatHourlyData } from "../utils/weather";

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

return <div>채워주세요</div>;
return (
<HourlyCard>
{hourly.map((item, idx) => (
<ForecastItem key={idx}>
<div>{item.time}</div>
<div>{item.temp}</div>
<div>{getWeatherDescription(item.code)}</div>
</ForecastItem>
))}
</HourlyCard>
);
};

export default HourlyForecast;
export default HourlyForecast;
86 changes: 36 additions & 50 deletions src/components/styles/StyledComponents.js
Original file line number Diff line number Diff line change
@@ -1,69 +1,55 @@
import styled from "styled-components";

export const Container = styled.div`
export const Wrapper = styled.div`
background: linear-gradient(to bottom, #a1c4fd, #c2e9fb);
min-height: 100vh;
background: linear-gradient(to bottom, #87ceeb, #4682b4);
padding: 20px;
padding: 40px 20px;
color: #fff;
font-family: 'Helvetica Neue', sans-serif;
`;

export const CurrentWeatherWrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 60vh;
color: white;
export const CenterText = styled.div`
text-align: center;
margin-bottom: 50px;
`;

export const Temperature = styled.h1`
font-size: 4rem;
export const TempText = styled.h1`
font-size: 56px;
margin: 0;
`;

export const WeatherCode = styled.p`
font-size: 1.5rem;
margin: 10px 0;
export const DescText = styled.p`
font-size: 24px;
margin: 8px 0 0;
`;

export const HourlyForecastWrapper = styled.div`
export const HourlyCard = styled.div`
background-color: rgba(255, 255, 255, 0.3);
border-radius: 16px;
padding: 20px;
margin: 0 auto 40px;
max-width: 768px;
display: flex;
justify-content: space-between;
gap: 10px;
overflow-x: auto;
padding: 20px;
background: rgba(255, 255, 255, 0.2);
border-radius: 15px;
margin-top: auto;
text-align: center;
font-size: 16px;
color: #fff;
`;

export const HourlyItem = styled.div`
display: flex;
flex-direction: column;
align-items: center;
color: white;
padding: 10px;
min-width: 100px;
export const ForecastItem = styled.div`
flex: 1;
`;

export const DailyForecastWrapper = styled.div`
display: flex;
flex-direction: column;
gap: 10px;
export const DailyCard = styled.div`
background-color: rgba(255, 255, 255, 0.3);
border-radius: 16px;
padding: 20px;
background: rgba(255, 255, 255, 0.2);
border-radius: 15px;
margin-top: 20px;
`;

export const DailyItem = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
color: white;
padding: 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.2);

&:last-child {
border-bottom: none;
}
`;
margin: 0 auto;
max-width: 768px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
row-gap: 16px;
font-size: 16px;
color: #fff;
text-align: center;
`;
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
);
66 changes: 42 additions & 24 deletions src/utils/weather.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,50 @@
const weatherCodes = {
0: "맑음",
1: "대체로 맑음",
2: "부분적으로 흐림",
3: "흐림",
45: "안개",
48: "짙은 안개",
51: "약한 이슬비",
53: "보통 이슬비",
55: "강한 이슬비",
61: "약한 비",
63: "보통 비",
65: "강한 비",
71: "약한 눈",
73: "보통 눈",
75: "강한 눈",
};

export const getWeatherDescription = (code) => {
const weatherCodes = {
0: "맑음",
1: "대체로 맑음",
2: "부분적으로 흐림",
3: "흐림",
45: "안개",
48: "짙은 안개",
51: "약한 이슬비",
53: "보통 이슬비",
55: "강한 이슬비",
61: "약한 비",
63: "보통 비",
65: "강한 비",
71: "약한 눈",
73: "보통 눈",
75: "강한 눈",
};
return weatherCodes[code] || "알 수 없음";
};

export const formatHourlyData = (weatherData) => {
if (!weatherData) return [];
// 밑에 코드 채워주세요
return [];
const { time, temperature_2m, weather_code } = weatherData.hourly;
const now = new Date();
const currentHour = now.getHours();
const startIdx = time.findIndex((t) => new Date(t).getHours() === currentHour);

return Array.from({ length: 6 }, (_, i) => ({
time: `${(currentHour + i) % 24}시`,
temp: `${temperature_2m[startIdx + i]}°C`,
code: weather_code[startIdx + i],
}));
};

export const formatDailyData = (weatherData) => {
if (!weatherData) return [];
// 밑에 코드 채워주세요
return [];
};
const { time, temperature_2m_max, weather_code } = weatherData.daily;

return time.map((dateStr, idx) => {
const date = new Date(dateStr);
const options = { month: "numeric", day: "numeric", weekday: "short" };
const formatted = date.toLocaleDateString("ko-KR", options);

return {
date: formatted,
code: weather_code[idx],
temp: `${temperature_2m_max[idx]}°C`,
};
});
};