-
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주차 기본/심화과제] ☀️승경씨의 날씨 예보☀️ #9
base: main
Are you sure you want to change the base?
Changes from all commits
0486170
430f344
4af015d
8e6ac56
cb2fe50
a5d8e40
0a368af
1e6a064
7aefe0c
d3554a4
d991359
2e85f6d
4ad08b5
20e0c04
7a46ef1
2891e3b
228c565
43c6726
f3be44b
3eecf79
449dd7c
b5c7e35
05d2c63
fdf8014
94ac54f
d187a31
7605e9c
8b3ae2a
ee7595a
961df4c
e5e006f
a024f46
f79ce94
2439bd5
dbac8d9
79ac0b8
41f8282
51bbef1
37266c1
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,14 @@ | ||
module.exports = { | ||
env: { browser: true, es2020: true }, | ||
extends: [ | ||
'eslint:recommended', | ||
'plugin:@typescript-eslint/recommended', | ||
'plugin:react-hooks/recommended', | ||
], | ||
parser: '@typescript-eslint/parser', | ||
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, | ||
plugins: ['react-refresh'], | ||
rules: { | ||
'react-refresh/only-export-components': 'warn', | ||
}, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# 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,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>Vite + React + TS</title> | ||
</head> | ||
<body> | ||
<div id="root"></div> | ||
<script type="module" src="/src/main.tsx"></script> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{ | ||
"name": "week4-project", | ||
"private": true, | ||
"version": "0.0.0", | ||
"type": "module", | ||
"scripts": { | ||
"dev": "vite", | ||
"build": "tsc && vite build", | ||
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", | ||
"preview": "vite preview" | ||
}, | ||
"dependencies": { | ||
"react": "^18.2.0", | ||
"react-dom": "^18.2.0" | ||
}, | ||
"devDependencies": { | ||
"@types/react": "^18.0.28", | ||
"@types/react-dom": "^18.0.11", | ||
"@typescript-eslint/eslint-plugin": "^5.57.1", | ||
"@typescript-eslint/parser": "^5.57.1", | ||
"@vitejs/plugin-react": "^4.0.0", | ||
"eslint": "^8.38.0", | ||
"eslint-plugin-react-hooks": "^4.6.0", | ||
"eslint-plugin-react-refresh": "^0.3.4", | ||
"typescript": "^5.0.2", | ||
"vite": "^4.3.2" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { useState } from "react"; | ||
import { ThemeProvider } from "styled-components"; | ||
import { useGetWeatherData } from "./lib/api/useGetWeatherData"; | ||
import WeatherPage from "./Pages/WeatherPage"; | ||
import Router from "./Router"; | ||
import GlobalStyle from "./style/globalStyle"; | ||
import theme from "./style/theme"; | ||
|
||
function App() { | ||
const [count, setCount] = useState(0); | ||
// const { weatherInfo } = useGetWeatherData("Seoul"); | ||
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. 주석아 내가 한 번 사라져볼게 얍! |
||
// console.log(weatherInfo); | ||
return ( | ||
<> | ||
<ThemeProvider theme={theme}> | ||
<GlobalStyle /> | ||
<Router /> | ||
</ThemeProvider> | ||
</> | ||
); | ||
} | ||
|
||
export default App; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import React from "react"; | ||
import styled from "styled-components"; | ||
|
||
const Error404 = () => { | ||
return <StErrorMsg>페이지가 존재하지 않습니다!</StErrorMsg>; | ||
}; | ||
|
||
const StErrorMsg = styled.h2` | ||
margin-left: 3rem; | ||
font-size: 3rem; | ||
`; | ||
Comment on lines
+8
to
+11
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. 오옹 이거어는 이렇게 스타일을 지정해준 이유가 있나요?? h2를 쓰고 거기에 마진과 사이즈를 재조정...특이한 스타일링이라 그냥 궁금해서! |
||
|
||
export default Error404; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { Outlet } from "react-router-dom"; | ||
import Header from "../components/atom/Header"; | ||
import SearchInput from "../components/atom/SearchInput"; | ||
|
||
const WeatherPage = () => { | ||
return ( | ||
<div> | ||
<Header /> | ||
<SearchInput /> | ||
<Outlet /> | ||
</div> | ||
Comment on lines
+7
to
+11
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. 혹시 여기 div 태그로 감싸져있는 이유가 뭔가요옹 |
||
); | ||
}; | ||
|
||
export default WeatherPage; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { BrowserRouter, Route, Routes } from "react-router-dom"; | ||
import WeatherSection from "./components/organism/WeatherSection"; | ||
import Error404 from "./Pages/Error404"; | ||
import WeatherPage from "./Pages/WeatherPage"; | ||
|
||
const Router = () => { | ||
return ( | ||
<> | ||
<BrowserRouter> | ||
<Routes> | ||
<Route path="/" element={<WeatherPage />}> | ||
<Route path="/:type/:area" element={<WeatherSection />} /> | ||
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. 나 요렇게 쓰고 싶던거 못 썼는데 성경이 코드 얌지게 보고 배워가야지 ✨✨✨✨ |
||
</Route> | ||
<Route path="*" element={<Error404 />} /> | ||
</Routes> | ||
</BrowserRouter> | ||
</> | ||
); | ||
}; | ||
|
||
export default Router; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import React from "react"; | ||
import styled from "styled-components"; | ||
|
||
const Header = () => { | ||
return ( | ||
<> | ||
<StHeader> | ||
<h1>☀️ 승경씨의 날씨 예보 ☀️</h1> | ||
</StHeader> | ||
</> | ||
); | ||
}; | ||
|
||
const StHeader = styled.header` | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: center; | ||
padding: 1rem; | ||
|
||
width: 100%; | ||
height: 8rem; | ||
|
||
background-color: ${({ theme }) => theme.colors.Weather_Header_Blue}; | ||
|
||
> h1 { | ||
font-size: 3.5rem; | ||
color: ${({ theme }) => theme.colors.Weather_Text_White}; | ||
} | ||
`; | ||
|
||
export default Header; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import React, { useRef, useState } from "react"; | ||
import { useNavigate } from "react-router-dom"; | ||
import styled from "styled-components"; | ||
|
||
const SearchInput = () => { | ||
const navigate = useNavigate(); | ||
const inputRef = useRef<HTMLInputElement>(null); | ||
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. 타스로햇고나...!! |
||
const [isDailyInfo, setIsDailyInfo] = useState<boolean>(true); | ||
|
||
const handleOptionChange = (e: React.ChangeEvent<HTMLSelectElement>) => { | ||
if (e.target.value === "daily") setIsDailyInfo(true); | ||
else setIsDailyInfo(false); | ||
}; | ||
|
||
const handleNavigate = (e: React.FormEvent<HTMLFormElement>) => { | ||
e.preventDefault(); | ||
const area = inputRef.current?.value; | ||
const type = isDailyInfo ? "day" : "week"; | ||
console.log(area, type); | ||
navigate(`/${type}/${area}`); | ||
}; | ||
Comment on lines
+15
to
+21
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 ( | ||
<> | ||
<StSearchForm onSubmit={(e) => handleNavigate(e)}> | ||
<StPeriodOption onChange={(e) => handleOptionChange(e)}> | ||
<option value="daily">오늘</option> | ||
<option value="weekly">주간</option> | ||
</StPeriodOption> | ||
|
||
<input placeholder="영어로 도시명 ex) seoul " ref={inputRef} /> | ||
<button type="submit">날씨 검색</button> | ||
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. 어차피 눌렀을 때 실제 form 을 submit 하는게 아니고 추가적인 로직들을 수행하도록 되어있는 거니까! type='button'으로 설정해두면 e.preventDefault를 할 필요도 없을 것 같아!! 사실 둘 중 뭐가 나은지는 모르겠는데...그런 방법도 있다! 는거죵~ 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. 하 형근아 혹시 녹음으로 코리했어? |
||
</StSearchForm> | ||
</> | ||
); | ||
}; | ||
|
||
const StSearchForm = styled.form` | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
gap: 1rem; | ||
margin-top: 2rem; | ||
margin-bottom: 2rem; | ||
|
||
width: 100%; | ||
|
||
> input { | ||
width: 30rem; | ||
height: 6rem; | ||
padding: 2rem; | ||
|
||
border-radius: 2rem; | ||
border: none; | ||
background-color: ${({ theme }) => theme.colors.Weather_Header_Blue}; | ||
|
||
font-size: 2.3rem; | ||
} | ||
|
||
> button { | ||
width: 10rem; | ||
height: 6rem; | ||
|
||
background-color: ${({ theme }) => theme.colors.Weather_Btn_Blue}; | ||
border: none; | ||
border-radius: 1.5rem; | ||
|
||
font-size: 2rem; | ||
color: ${({ theme }) => theme.colors.Weather_Text_White}; | ||
} | ||
`; | ||
|
||
const StPeriodOption = styled.select` | ||
width: 8rem; | ||
height: 3rem; | ||
|
||
border-radius: 3rem; | ||
border: 0.2rem solid gray; | ||
`; | ||
|
||
export default SearchInput; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import styled from "styled-components"; | ||
import { WeatherInfoToRender } from "../../types/weather"; | ||
|
||
export interface WeatherCardProps { | ||
weatherCardInfo: WeatherInfoToRender; | ||
isDaily: boolean; | ||
} | ||
|
||
const WeatherCard = (props: WeatherCardProps) => { | ||
const { | ||
weatherCardInfo: { | ||
name, | ||
main: { feels_like, temp, temp_min, temp_max }, | ||
clouds: { all }, | ||
weatherImg, | ||
dt_txt, | ||
}, | ||
isDaily, | ||
} = props; | ||
let [month, date]: [string, string] = ["", ""]; | ||
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. 요오기도 타입이 string이지만 보여지는 값은 숫자니까..! 이왕이면 요기 타입을 number로 지정하면 다른 이상한 문자열이 들어오는 경우를 방지할 수 있지 않을까! 그렇게 해두고 나중에 실제 값이 사용될 때 string으로 형변환을 해주면 되니까! 타스를 쓰니까 이왕이면 좀 더 좁고 엄격한(narrow) 타입을 사용해보면 좋을 것 같아용 |
||
if (dt_txt) { | ||
[month, date] = dt_txt.slice(5, 10).split("-"); | ||
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 ( | ||
<> | ||
<StCardWrapper> | ||
{isDaily ? ( | ||
<h2>{name}</h2> | ||
) : ( | ||
<time> | ||
{month}/{date} | ||
</time> | ||
)} | ||
|
||
<img src={weatherImg} /> | ||
|
||
<StWeatherInfoWrapper> | ||
<li> | ||
<p>체감 온도</p> | ||
<h2>{temp}</h2> | ||
</li> | ||
<li> | ||
<h2>체감 온도</h2> | ||
<p>{feels_like}</p> | ||
</li> | ||
<li> | ||
<h2>최저 / 최고</h2> | ||
<p> | ||
{temp_min}/{temp_max} | ||
</p> | ||
</li> | ||
<li> | ||
<h2>구름</h2> | ||
<p>{all}%</p> | ||
</li> | ||
</StWeatherInfoWrapper> | ||
</StCardWrapper> | ||
</> | ||
); | ||
}; | ||
|
||
const StCardWrapper = styled.article` | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
justify-content: center; | ||
padding: 2.5rem; | ||
gap: 1.3rem; | ||
|
||
width: 25rem; | ||
|
||
background-color: ${({ theme }) => theme.colors.Weather_Card_Blue}; | ||
border-radius: 4rem; | ||
|
||
> time, | ||
h2 { | ||
font-size: 2.5rem; | ||
} | ||
|
||
> img { | ||
width: 95%; | ||
height: 17rem; | ||
} | ||
`; | ||
|
||
const StWeatherInfoWrapper = styled.ul` | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
justify-content: center; | ||
gap: 0.8rem; | ||
|
||
width: 95%; | ||
|
||
> li { | ||
display: flex; | ||
justify-content: space-between; | ||
|
||
width: 100%; | ||
|
||
> p, | ||
h2 { | ||
font-size: 1.5rem; | ||
} | ||
} | ||
`; | ||
|
||
export default WeatherCard; |
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.
으이잉??? react-router-dom이랑 styled-components 어디 갔지..?
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.
오 그러게! 신기하다👀