-
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주차 기본/심화/생각 과제] 보리몽의 기상 예보 #8
base: main
Are you sure you want to change the base?
Conversation
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.
js의 꼼꼼한 기능을 잘 사용했다는 생각이 들어 ~!
그리고 궁금한 게 주간/일간을 다르게 데이터를 받아와야해서 컴포넌트나 커스텀 훅을 다 분리한 것 같은데 !
이럴 때는 현수처럼 분리하는 게 나을지 아니면 한 파일로 관리하는 게 좋을지 궁금하다
수고했어 !!
const weather = WEATHER_TYPE.find( | ||
(item) => item.description === description | ||
); | ||
return weather?.imgURL; |
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.
옵셔널체이닝을 이렇게 활용하는 구나 ~!! 너무 꼼꼼해
return ( | ||
<St.CardContainer> | ||
{isLoading ? ( | ||
<St.SkeletonCard></St.SkeletonCard> |
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.
카드안에서 스켈레톤을 쓰니까 스타일드컴포넌트로 깔끔하게 구분해줄 수 있군 !!
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.
으잉 한달 전의 나는 이런 생각을 전혀못했군.. 완전 똑똑해 난 기존 코드 똑같이 또 복붙하고 className추가해서 구현했는데..!!!
try { | ||
setIsLoading(true); | ||
setIsError(false); | ||
await new Promise(resolve => setTimeout(() => resolve(), 500)); |
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.
이거 왜 resolve를 시간차를 두고 할당해줬는지 알 수 있을까?
서버에서 데이터 받아오는 시간인감?
} finally { | ||
setIsLoading(false); | ||
} |
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.
오호 ! 공통적으로 적용되어야하는 코드는 finally로 처리! 배우고 가요 ㅎㅎ
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.
아 나도 현수처럼 완전 도움되는 리뷰 하고싶은데... 또 감탄사만 연발하다 온...ㅠ
커스텀훅을 사용하는 방식이 잘 이해가 안됐었는데
데이터를 불러올때 사용하면 너무너무 유용하다는 걸 알아갑니당!!!
고생 많았어 ✨
export const pulseAnimation = keyframes` | ||
0% { | ||
background-color: #94a3b8; | ||
} | ||
50% { | ||
background-color: #cbd5e1; | ||
} | ||
100% { | ||
background-color: #94a3b8; | ||
} | ||
` |
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.
아니 스켈레톤 애니메이션 너무멋진데??? 🤩🤩🤩
CardContainer: styled.div` | ||
display: flex; | ||
justify-content: center; | ||
background-color: ${theme.colors.orange}; |
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.
아니 나 이거 맨날 ${({ theme }) => theme.colors.orange}; 이렇게 하느라 손가락 나가는줄알았는데 (스니펫도 왜인지 안먹음 ㅠ) 그냥 theme을 import하면 되는거엿..? 무쳤다진짜
return ( | ||
<St.CardContainer> | ||
{isLoading ? ( | ||
<St.SkeletonCard></St.SkeletonCard> |
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.
으잉 한달 전의 나는 이런 생각을 전혀못했군.. 완전 똑똑해 난 기존 코드 똑같이 또 복붙하고 className추가해서 구현했는데..!!!
<div> | ||
<span>온도</span> | ||
<span>{main?.temp}</span> | ||
</div> |
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.
요 반복되는 div를 styled-component로 정의해줘도 좋았겠다!! _
<St.SkeletonCard /> | ||
<St.SkeletonCard /> | ||
<St.SkeletonCard /> | ||
<St.SkeletonCard /> | ||
<St.SkeletonCard /> |
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.
아니 이렇게 하면되는구나진챸 너무 깔끔해
<>Not Found</> | ||
) : ( | ||
data | ||
?.filter((item, index) => index % 8 === 0) |
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.
모듈러 연산 참고해갑니다잇!!
.map(({ id, dt_txt, weather, main, clouds }) => { | ||
return ( | ||
<St.Card key={id}> | ||
<div>{dt_txt.slice(5, 10)}</div> | ||
<img | ||
src={getWeatherImg(weather?.[0].description)} | ||
alt="날씨 이미지" | ||
/> | ||
<main> | ||
<div> | ||
<span>온도</span> | ||
<span>{main?.temp}</span> | ||
</div> | ||
<div> | ||
<span>체감 온도</span> | ||
<span>{main?.feels_like}</span> | ||
</div> | ||
<div> | ||
<span>최저/최고</span> | ||
<span> | ||
{main?.temp_min}/{main?.temp_max} | ||
</span> | ||
</div> | ||
<div> | ||
<span>구름</span> | ||
<span>{clouds?.all}%</span> | ||
</div> | ||
</main> | ||
</St.Card> |
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.
데이터를 custom hook으로 가져오니까 data 사용이 무척 깔끔해진거같다...
const handleEnter = (e) => { | ||
if(e.key === "Enter") { | ||
searchBtnClick(); | ||
} | ||
} |
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.
아놔 이거까진 생각못했는데 너무 섬세하자나...?
아 근데 이거 보고 내가 한거 다시 봤는데 이거 따로 안해줘도 엔터 치면 submit버튼이 눌리는거같은....????
export default function useGetCards(props) { | ||
const { cityName } = props; | ||
const [data, setData] = useState(); | ||
const [isLoading, setIsLoading] = useState(true); | ||
const [isError, setIsError] = useState(false); | ||
|
||
const getCards = async () => { | ||
try { | ||
setIsLoading(true); | ||
setIsError(false); | ||
await new Promise((resolve) => setTimeout(() => resolve(), 500)); | ||
const response = | ||
await axios.get(`https://api.openweathermap.org/data/2.5/forecast?q=${cityName}&appid=${ | ||
import.meta.env.VITE_APP_WEATHER | ||
}&units=metric | ||
`); | ||
setData(response.data.list); | ||
} catch (error) { | ||
console.log(error); | ||
setIsError(true); | ||
} finally { | ||
setIsLoading(false); | ||
} | ||
}; |
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.
useGetCard와 useGetCards를 따로 분리한게 무척 인상적이야!!
import React from 'react' | ||
import { Outlet } from 'react-router-dom' | ||
|
||
export default function Week() { | ||
return ( | ||
<div> | ||
<Outlet /> | ||
</div> | ||
) | ||
} |
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.
보니깐 Day component랑 Week component랑 완전히 똑같이 생겼는데 따로 분리해서 써야하는 이유가 궁금하다...!
✨ 구현 기능 명세
🌈 구현사항
✅
기상 카드
V✅
일간 / 주간 + 지역(영어)검색
V✅
Outlet + Route
V🌈 심화과제
✅
스켈레톤 UI
V✅
커스텀훅으로 데이터를 받아옵시다!
V🌼 PR Point
✅ week4/weather_forecast/src/pages/Week.jsx
Outlet 을 이용해 페이지 내의 하위 컴포넌트들 간의 전환을 쉽게 해주었어요 :) {children} 과 비슷한 느낌!!
✅ week4/weather_forecast/src/components/Card.jsx
커스텀 훅을 사용해서 data를 가져와 주었구요, 구조 분해 할당으로 weather, main, clouds 만 뽑아와주었어요! 그리고 useParams 를 이용해 도시 이름을 가져와 커스텀훅에 인자로 넣어주었어요!
이 함수는 날씨 description 에 해당하는 날씨 이미지를 WHEATHER_TYPE 에서 찾아 image URL 을 반환해 주는 친구!
조건부 렌더링으로 에러 처리와 스켈레톤 ui 처리를 해 주었어요!
<span>{clouds?.all}%</span>
옵셔널 체이닝을 통해 undefined 를 참조하지 않게 처리해주었습니다!
✅ week4/weather_forecast/src/components/Header.jsx
아까 위에서 useParams 로 cityName 을 가져왔는데요, 이건 Header 에서 search 버튼 클릭 시, 라우팅을 해주었기 때문에 가능했어요! 그 부분은 요렇게 구현되어 잇습니당
이 부분은 week/day 를 변경하는 handler 함수!
✅ week4/weather_forecast/src/components/PageLayout.jsx
전체 레이아웃은 이렇게 Header 가 공통이니까, PageLayout 에 헤더를 넣고, PageLayout 으로 Route 들을 감싸서 모든 페이지가 공통 레이아웃을 가지게 해주었어요!!
✅ week4/weather_forecast/src/hooks/useGetCard.jsx
이 부분은 custom hook 부분!
데이터 상태들을 정의해주고
비동기 요청을 하는데, Loading 은 true 로 Error 는 false 로 시작해서, 에러가 캐치되면 setIsError 를 통해 true 로 변경해주었고, finally 를 이용해 요청이 완료되면 로딩은 false 로 바꾸어주었어요!
await new Promise(resolve => setTimeout(() => resolve(), 500));
그리고 로딩을 위해서 setTimeout 시간 동안 기다리는 잉여 Promise 를 만들어 주었습니다!
cityName 이 바뀌면 비동기요청을 다시 해야 하기 때문에, 비동기 요청을 하는 함수와 전체 함수를 분리해서 작성해두었는데요! 이렇게 해도 되고 혹시 나중에 다른 파일에서 필요하다면? getCard 를 data, isLoading, isError 와 함께 담아서 return 해 주면 되어요!!
🥺 소요 시간, 어려웠던 점
3h
🌈 구현 결과물
도시명 검색 시 카드 렌더링, 도시명을 설정하지 않은 경로에 대한 에러
없는 도시명 검색 시 에러