Skip to content
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

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
0486170
init : 초기세팅
pinktopaz May 10, 2023
430f344
style : globalStyle 세팅
pinktopaz May 10, 2023
4af015d
chore : 날씨 데이터 타입 지정
pinktopaz May 10, 2023
8e6ac56
chore : 날씨 이미지 상수 데이터 지정
pinktopaz May 10, 2023
cb2fe50
style : 헤더 컴포넌트 뷰 구현
pinktopaz May 10, 2023
a5d8e40
style : 폰트 적용
pinktopaz May 10, 2023
0a368af
style : 헤더 색상 수정
pinktopaz May 10, 2023
1e6a064
style : 날씨 카드 뷰 구현
pinktopaz May 10, 2023
7aefe0c
style : 상단 옵션 및 검색창 뷰 구현
pinktopaz May 10, 2023
d3554a4
feat : 라우터 세팅
pinktopaz May 10, 2023
d991359
feat : 검색어와 옵션 받아서 링크 이동하기 구현
pinktopaz May 10, 2023
2e85f6d
chore : 상수 데이터 파일명 변경
pinktopaz May 10, 2023
4ad08b5
feat : 하루 날씨 조회 커스텀훅 구현
pinktopaz May 10, 2023
20e0c04
feat : axios 인스턴스 만들기
pinktopaz May 10, 2023
7a46ef1
feat : 하루 날씨 데이터 받아와 뷰에 뿌려주기
pinktopaz May 10, 2023
2891e3b
chore : 주간 날씨 페이지 파일명 변경
pinktopaz May 10, 2023
228c565
chore : 인터페이스 상속으로 정리
pinktopaz May 10, 2023
43c6726
feat : 날씨 카드에서 props 데이터 받아서 보여주기
pinktopaz May 10, 2023
f3be44b
chore : Header 컴포넌트 코드 정리
pinktopaz May 10, 2023
3eecf79
chore : 충돌 발생으로 인한 재커밋
pinktopaz May 10, 2023
449dd7c
chore : 라우터 수정
pinktopaz May 10, 2023
b5c7e35
chore : 충돌로 인해 중복으로 들어온 파일 삭제
pinktopaz May 10, 2023
05d2c63
style : 단일 날씨 카드중앙정렬
pinktopaz May 10, 2023
fdf8014
fix : 처음 검색하면 week로 연결되는 문제해결
pinktopaz May 10, 2023
94ac54f
chore : 지역 이름 옵셔널로 변경
pinktopaz May 10, 2023
d187a31
feat : 주간 정보 받아오는 커스텀훅 완성
pinktopaz May 10, 2023
7605e9c
chore : 커스텀 훅 하나로 합치기
pinktopaz May 10, 2023
8b3ae2a
feat : 스켈레톤 뷰 추가
pinktopaz May 12, 2023
ee7595a
style : 404 페이지 추가
pinktopaz May 12, 2023
961df4c
feat : 메인페이지 구성
pinktopaz May 12, 2023
e5e006f
chore :날씨 카드 섹션 컴포넌트 하나로 합치기
pinktopaz May 12, 2023
a024f46
feat : api 불러올 때 초기화
pinktopaz May 12, 2023
f79ce94
chore : 코드 정리
pinktopaz May 12, 2023
2439bd5
chore : env 실수로 올린 거 내리기
pinktopaz May 12, 2023
dbac8d9
fix : 에러 발생 후 api 연결 오류 해결
pinktopaz May 12, 2023
79ac0b8
Delete week4/node_modules directory
pinktopaz May 15, 2023
41f8282
Delete package.json
pinktopaz May 15, 2023
51bbef1
Delete yarn.lock
pinktopaz May 15, 2023
37266c1
Delete .env
pinktopaz May 15, 2023
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
14 changes: 14 additions & 0 deletions week4/week4-project/.eslintrc.cjs
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',
},
}
24 changes: 24 additions & 0 deletions week4/week4-project/.gitignore
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?
13 changes: 13 additions & 0 deletions week4/week4-project/index.html
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>
28 changes: 28 additions & 0 deletions week4/week4-project/package.json
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"
Comment on lines +13 to +14
Copy link

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 어디 갔지..?

Copy link

Choose a reason for hiding this comment

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

오 그러게! 신기하다👀

},
"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"
}
}
1 change: 1 addition & 0 deletions week4/week4-project/public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions week4/week4-project/src/App.tsx
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");
Copy link
Member

Choose a reason for hiding this comment

The 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;
13 changes: 13 additions & 0 deletions week4/week4-project/src/Pages/Error404.tsx
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
Copy link

Choose a reason for hiding this comment

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

오옹 이거어는 이렇게 스타일을 지정해준 이유가 있나요??

h2를 쓰고 거기에 마진과 사이즈를 재조정...특이한 스타일링이라 그냥 궁금해서!


export default Error404;
15 changes: 15 additions & 0 deletions week4/week4-project/src/Pages/WeatherPage.tsx
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
Copy link

Choose a reason for hiding this comment

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

혹시 여기 div 태그로 감싸져있는 이유가 뭔가요옹

);
};

export default WeatherPage;
21 changes: 21 additions & 0 deletions week4/week4-project/src/Router.tsx
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 />} />
Copy link

Choose a reason for hiding this comment

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

이렇게 한 번에 라우터 설정할 수 있었구나! 나는 더 어렵게 생각해서 라우팅 설정에서 시간 많이 썼었는데 배워가요!

Copy link
Member

Choose a reason for hiding this comment

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

나 요렇게 쓰고 싶던거 못 썼는데 성경이 코드 얌지게 보고 배워가야지 ✨✨✨✨

</Route>
<Route path="*" element={<Error404 />} />
</Routes>
</BrowserRouter>
</>
);
};

export default Router;
31 changes: 31 additions & 0 deletions week4/week4-project/src/components/atom/Header.tsx
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;
81 changes: 81 additions & 0 deletions week4/week4-project/src/components/atom/SearchInput.tsx
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);
Copy link
Member

Choose a reason for hiding this comment

The 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
Copy link
Member

Choose a reason for hiding this comment

The 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>
Copy link

Choose a reason for hiding this comment

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

어차피 눌렀을 때 실제 form 을 submit 하는게 아니고 추가적인 로직들을 수행하도록 되어있는 거니까!

type='button'으로 설정해두면 e.preventDefault를 할 필요도 없을 것 같아!!

사실 둘 중 뭐가 나은지는 모르겠는데...그런 방법도 있다! 는거죵~

Copy link
Member

Choose a reason for hiding this comment

The 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;
109 changes: 109 additions & 0 deletions week4/week4-project/src/components/atom/WeatherCard.tsx
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] = ["", ""];
Copy link

Choose a reason for hiding this comment

The 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("-");
Copy link

Choose a reason for hiding this comment

The 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;
Loading