-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ 4주차 기본/심화 과제 ] 🌞수수의 기상예보⛱️ #10
base: main
Are you sure you want to change the base?
Changes from all commits
47347ae
af7b4b4
cca13e8
4431bee
162ad80
c47d280
dda3e7a
8c8655e
ad6d563
ca3f9f0
9b5121f
bd830e6
5fb3b32
29aa6b3
a1b2e9e
5e63ff1
a577eaa
72e756f
035a345
3d1d64a
5862c67
1e3acb5
2a39e06
e4ddb49
44ebbc5
c5e4d6a
827b174
cbda21e
6022d6b
44ce321
7eea641
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
module.exports = { | ||
parser: "@typescript-eslint/parser", | ||
plugins: ["@typescript-eslint", "prettier"], | ||
extends: [ | ||
"airbnb", | ||
"plugin:import/errors", | ||
"plugin:import/warnings", | ||
"plugin:prettier/recommended", | ||
"plugin:@typescript-eslint/recommended", | ||
"prettier/@typescript-eslint", | ||
], | ||
rules: { | ||
"linebreak-style": 0, | ||
"import/prefer-default-export": 0, | ||
"prettier/prettier": 0, | ||
"import/extensions": 0, | ||
"import/no-unresolved": 0, | ||
"import/no-extraneous-dependencies": 0, // 테스트 또는 개발환경을 구성하는 파일에서는 devDependency 사용을 허용 | ||
"react/prop-types": 0, | ||
"react/jsx-filename-extension": [2, { extensions: [".js", ".jsx", ".ts", ".tsx"] }], | ||
"jsx-a11y/no-noninteractive-element-interactions": 0, | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
.env | ||
# Logs | ||
logs | ||
*.log | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
pnpm-debug.log* | ||
lerna-debug.log* | ||
|
||
node_modules | ||
dist | ||
dist-ssr | ||
*.local | ||
|
||
# Editor directories and files | ||
.vscode/* | ||
!.vscode/extensions.json | ||
.idea | ||
.DS_Store | ||
*.suo | ||
*.ntvs* | ||
*.njsproj | ||
*.sln | ||
*.sw? |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"semi": true, | ||
"tabWidth": 2, | ||
"printWidth": 120, | ||
"trailingComma": "all", | ||
"bracketSameLine": true, | ||
"endOfLine": "auto" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>수수의 기상예보</title> | ||
</head> | ||
<body> | ||
<div id="root"></div> | ||
<script type="module" src="/src/main.jsx"></script> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
{ | ||
"name": "weather", | ||
"private": true, | ||
"version": "0.0.0", | ||
"type": "module", | ||
"scripts": { | ||
"dev": "vite", | ||
"build": "vite build", | ||
"lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0", | ||
"preview": "vite preview" | ||
}, | ||
"dependencies": { | ||
"axios": "^1.4.0", | ||
"react": "^18.2.0", | ||
"react-dom": "^18.2.0", | ||
"react-loading-skeleton": "^3.3.1", | ||
"react-query": "^3.39.3", | ||
"react-router-dom": "^6.11.1", | ||
"styled-components": "^6.0.0-rc.1" | ||
}, | ||
"devDependencies": { | ||
"@types/react": "^18.0.28", | ||
"@types/react-dom": "^18.0.11", | ||
"@vitejs/plugin-react": "^4.0.0", | ||
"eslint": "^8.38.0", | ||
"eslint-plugin-react": "^7.32.2", | ||
"eslint-plugin-react-hooks": "^4.6.0", | ||
"eslint-plugin-react-refresh": "^0.3.4", | ||
"vite": "^4.3.2" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import React from "react"; | ||
import { styled } from "styled-components"; | ||
|
||
export default function Header() { | ||
return ( | ||
<HeaderWrapper> | ||
<Title>🌞수수의 기상예보⛱️</Title> | ||
</HeaderWrapper> | ||
); | ||
} | ||
|
||
const HeaderWrapper = styled.header` | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
|
||
width: 100%; | ||
padding: 2rem; | ||
|
||
background-color: ${({ theme }) => theme.colors.blue}; | ||
`; | ||
|
||
const Title = styled.h1` | ||
${({ theme }) => theme.fonts.header}; | ||
color: ${({ theme }) => theme.colors.black}; | ||
cursor: pointer; | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import React from "react"; | ||
import { styled } from "styled-components"; | ||
import Header from "./header"; | ||
|
||
export default function PageLayout(props) { | ||
const { children } = props; | ||
return ( | ||
<PageWrapper> | ||
<Header /> | ||
{children} | ||
</PageWrapper> | ||
); | ||
} | ||
|
||
const PageWrapper = styled.div` | ||
display: flex; | ||
flex-direction: column; | ||
justify-items: center; | ||
align-items: center; | ||
|
||
width: 100%; | ||
height: 100%; | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import React, { useState } from "react"; | ||
|
||
export default function Search() { | ||
return ( | ||
<> | ||
<input placeholder="영어로 도시명 ex) seoul" /> | ||
<button type="button">날씨 검색</button> | ||
</> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import React, { useEffect, useState } from "react"; | ||
import { styled } from "styled-components"; | ||
import { WEATHER_TYPE } from "../../core/weatherImg"; | ||
|
||
export default function WeatherCard(props) { | ||
const { weatherData, title } = props; | ||
const { main, clouds } = weatherData; | ||
const [weatherImg, setWeatherImg] = useState(""); | ||
|
||
useEffect(() => { | ||
weatherData?.weather && | ||
setWeatherImg(WEATHER_TYPE.filter((w) => w.description === weatherData?.weather[0]?.description)[0]?.imgURL); | ||
Comment on lines
+11
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 크... 옵셔널체이닝 폼 미쳤다 필터도 넘 깔끄매요 |
||
}, [weatherData]); | ||
|
||
return ( | ||
<WeatherCardWrapper> | ||
<h1>{title}</h1> | ||
<img src={weatherImg} alt={title} /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 리액트하면서 자꾸 alt 까먹게 되더라 잊지않고 꼬박꼬박 쓰는거 조하욥 |
||
<p>온도 | {main?.temp}</p> | ||
<p>체감 온도 | {main?.feels_like}</p> | ||
<p> | ||
최저/최고 | {main?.temp_min}/{main?.temp_max} | ||
</p> | ||
<p>구름 | {clouds?.all}</p> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. | 이거 이런식으로 하는구나! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오오 이거 좋다!! 오빠는 before after 활용을 참 잘하는 것 같아용! |
||
</WeatherCardWrapper> | ||
); | ||
} | ||
|
||
const WeatherCardWrapper = styled.article` | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: center; | ||
align-items: center; | ||
|
||
margin: 1rem; | ||
padding: 2rem; | ||
|
||
border: 0.3rem solid ${({ theme }) => theme.colors.blue}; | ||
border-radius: 1rem; | ||
background-color: ${({ theme }) => theme.colors.skyblue}; | ||
${({ theme }) => theme.fonts.title} | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import React from "react"; | ||
import { keyframes, styled } from "styled-components"; | ||
|
||
export default function SkeletonTemplate() { | ||
return ( | ||
<SkeletonBoxWrapper> | ||
<Title></Title> | ||
<Img></Img> | ||
<Text></Text> | ||
Comment on lines
+7
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 완전 맞아요!! 그렇게 고쳐야징 |
||
</SkeletonBoxWrapper> | ||
); | ||
} | ||
|
||
const loading = keyframes` | ||
0% { | ||
transform: translateX(0); | ||
} | ||
50%, | ||
100% { | ||
transform: translateX(45rem); | ||
} | ||
`; | ||
|
||
const SkeletonBoxWrapper = styled.section` | ||
width: 28rem; | ||
height: 40rem; | ||
|
||
margin: 1rem; | ||
|
||
background-color: #ddd; | ||
border-radius: 1rem; | ||
|
||
overflow: hidden; | ||
`; | ||
|
||
const Title = styled.div` | ||
width: 25rem; | ||
height: 3rem; | ||
|
||
margin: 1.5rem 0 0.5rem 0; | ||
|
||
background-color: rgba(145, 145, 145, 0.1); | ||
|
||
animation: ${loading} 4s infinite; | ||
`; | ||
|
||
const Img = styled.div` | ||
width: 25rem; | ||
height: 25rem; | ||
|
||
margin: 0.5rem; | ||
|
||
background-color: rgba(145, 145, 145, 0.1); | ||
|
||
animation: ${loading} 4s infinite; | ||
`; | ||
|
||
const Text = styled.div` | ||
width: 25rem; | ||
height: 8rem; | ||
|
||
margin: 0.5rem; | ||
|
||
background-color: rgba(145, 145, 145, 0.1); | ||
|
||
animation: ${loading} 4s infinite; | ||
`; |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,25 @@ | ||||||||||||||||||||
import React, { useEffect, useState } from "react"; | ||||||||||||||||||||
import { useParams } from "react-router-dom"; | ||||||||||||||||||||
import WeatherCard from "../common/weatherCard"; | ||||||||||||||||||||
import useWeather from "../../hooks/useWeather"; | ||||||||||||||||||||
import ErrorPage from "../../@pages/errorPage"; | ||||||||||||||||||||
import SkeletonTemplate from "../skeleton/skeletonTemplate"; | ||||||||||||||||||||
|
||||||||||||||||||||
export default function DetailWeatherInfo() { | ||||||||||||||||||||
const { area } = useParams(); | ||||||||||||||||||||
const { fetchTodayWeatherInfo, isError, todayData, isLoading } = useWeather(); | ||||||||||||||||||||
|
||||||||||||||||||||
useEffect(() => { | ||||||||||||||||||||
fetchTodayWeatherInfo(area); | ||||||||||||||||||||
}, []); | ||||||||||||||||||||
|
||||||||||||||||||||
useEffect(() => { | ||||||||||||||||||||
fetchTodayWeatherInfo(area); | ||||||||||||||||||||
}, [area]); | ||||||||||||||||||||
|
||||||||||||||||||||
if (isError) { | ||||||||||||||||||||
return <ErrorPage />; | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
return <>{isLoading ? <SkeletonTemplate /> : <WeatherCard weatherData={todayData} title={todayData.name} />}</>; | ||||||||||||||||||||
} | ||||||||||||||||||||
Comment on lines
+20
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오오오 여기 리턴으로 isError 조건까지 한번에 처리해줄 수 잇지 아늘까?!
Suggested change
요런 늑낌스로.. 소심한 제안 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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,45 @@ | ||
import React, { useEffect, useState } from "react"; | ||
import { useParams } from "react-router-dom"; | ||
import WeatherCard from "../common/weatherCard"; | ||
import { styled } from "styled-components"; | ||
import useWeather from "../../hooks/useWeather"; | ||
import ErrorPage from "../../@pages/errorPage"; | ||
import SkeletonTemplate from "../skeleton/skeletonTemplate"; | ||
|
||
export default function WeekWeatherInfo() { | ||
const { area } = useParams(); | ||
const { fetchWeekWeatherInfo, isError, weekDatas, isLoading } = useWeather(); | ||
|
||
useEffect(() => { | ||
fetchWeekWeatherInfo(area); | ||
}, []); | ||
|
||
useEffect(() => { | ||
fetchWeekWeatherInfo(area); | ||
}, [area]); | ||
|
||
if (isError) { | ||
return <ErrorPage />; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 뭔가 이렇게 미리 돌리는게 좋은가?! isError ? : isLoading ? : 약간 이런식으로 했거든! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오호! 둘 다 좋은 방법인 것 같긴 해용! 공식 문서들에서 보통 저렇게 미리 돌리길래 따라해봐쎠! |
||
|
||
return ( | ||
<CardWrapper> | ||
{weekDatas.list?.slice(0, 5).map((weatherData, i) => ( | ||
<> | ||
{isLoading ? ( | ||
<SkeletonTemplate /> | ||
) : ( | ||
<WeatherCard key={i} weatherData={weatherData} title={weatherData?.dt_txt?.split(" ")[0]} /> | ||
)} | ||
</> | ||
))} | ||
</CardWrapper> | ||
); | ||
} | ||
|
||
const CardWrapper = styled.section` | ||
display: flex; | ||
flex-wrap: wrap; | ||
|
||
width: 100rem; | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import React from "react"; | ||
import { styled } from "styled-components"; | ||
|
||
export default function ErrorPage() { | ||
return ( | ||
<ErrorPageWrapper> | ||
<ErrorMessage>Error</ErrorMessage> | ||
</ErrorPageWrapper> | ||
); | ||
} | ||
|
||
const ErrorMessage = styled.h1` | ||
${({ theme }) => theme.fonts.header} | ||
`; | ||
|
||
const ErrorPageWrapper = styled.div` | ||
width: 100%; | ||
height: 100vh; | ||
|
||
background-color: ${({ theme }) => theme.fonts.blue}; | ||
`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
font도 theme으로 적용! 깰꼬매요