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

Merged
merged 29 commits into from
Jul 16, 2023

Conversation

eunbeann
Copy link
Member

@eunbeann eunbeann commented May 12, 2023

✨ 구현 기능 명세

✅ 기본 과제

  • 기상 카드 🌩
    • 날씨에 따른 이미지
    • 온도
    • 체감 온도
    • 최저, 최고
    • 구름 %
  • 일간 / 주간 + 지역(영어) 검색 🔍
    • 날씨 검색 타입
      • 오늘 ⇒ 하루
      • 주간 ⇒ 5일 예보
    • 검색 기능 (uesParams)
      • /day/:area /week/:area 와 같이 Param로 검색한 지역 넘겨주기
      • 가져와서 오늘 / 주간 날씨 렌더링하기
  • Outlet + Route ⛓
    • Outlet과 Route를 활용하여 페이지 내의 컴포넌트 변경
      • 헤더 고정
        • 기상 정보 카드만 경로에 따라 변경
    • 에러 처리 페이지 만들기!

✅ 심화 과제

  • 스켈레톤 UI
    • 기상 정보 불러올 때 사용자에게 스켈레톤 UI 보여주기!!
  • 커스텀훅으로 데이터 받아오기!
    • isError, isLoading, data를 커스텀훅의 반환값으로 만들어서 이를 스켈레톤 UI랑 연결지었습니다!!!

🌼 PR Point

  • Outlet + Route ⛓

    <Routes>
    	<Route index element={<Main/>} />
    		<Route path="day" element={<DayList />}>
    		<Route path=":area" element={<DaySection />}/>
    		</Route>
    		<Route path="week" element={<WeekList />}>
    	    <Route path=":area" element={<WeekSection />}/>
    		</Route>
      <Route path="*" element={<Error/>} />
    </Routes>

    ‼ 처음에 day/week 도 param로 받아오고 싶어서 이런 저런 시도를 해봤는데, 생각만큼 잘 안돼서… ㅠㅠ 냅다 하드 코딩마냥 박았습니다. 그랬더니 코드가 지져분해 보이는 것 같아서 속상

    ⇒ day/week 코드에 신경쓸게 엄청 많아져서 와장창 꼬여버리는 이슈 발생!! 길을 잃음

    ⇒ customHooks써서 코드 정돈 되면 좀 수월하게 가능할 것 같은데 🤔

  • 일간 / 주간 + 지역(영어) 검색 🔍

Screen Shot 2023-05-12 at 18 23 06
- 하루 날씨

```jsx
try{
// 스켈레톤을 위한 화면 로딩
    setIsLoading(true);
    await axios
// 데이터 받아오기
        .get(`https://api.openweathermap.org/data/2.5/weather?q=${area}&appid=${import.meta.env.VITE_APP_WEATHER}&units=metric`)
        .then((res) => res.data)
        .then((data)=> {
            if(data.cod == 200) {
//하루 날씨 받아오기
                setDayWeather(
                    {
                    id: 0,
                    name: data.name,
                    weather_description: data.weather[0].description,
                    weather_img_url: WEATHER_TYPE.filter(
                        (weather) => weather.description === data.weather[0].description
                    )[0].imgURL,
                    temp: data.main.temp,
                    feels_like: data.main.feels_like,
                    temp_min: data.main.temp_min,
                    temp_max: data.main.temp_max,
                    clouds: data.clouds.all,
                    })
                }
            })
  } catch (err) {
    console.log(err);
  }
  setIsLoading(false);
  }
```

- 주간 날씨

```jsx
const getWeekWeather = async() => {
  let WeatherData = []; 

  try {

    setIsLoading(true);

    await axios
      .get(`https://api.openweathermap.org/data/2.5/forecast?q=${area}&appid=${import.meta.env.VITE_APP_WEATHER}&units=metric`)
      .then((res) => res.data)
      .then((data)=> {
        if(data.cod == 200) {
          data.list
          // 5일 간의 오후 3시 날씨
          .filter((item, idx) => [2, 10, 18, 26, 34].includes(idx))
          .map((data, idx) => {
            //   날짜 세팅
            //UTC와 대한민국 표준 시간 차 초단위로 빼기 (9 * 60 * 60)
            const date = new Date((data.dt - 9 * 60 * 60) * 1000);
            const month = date.getMonth()+1;
            const day = date.getDate();
            const dateString = `${month}월 ${day}일`;
            WeatherData.push({
              id: idx,
              date: dateString,
              weather_description: data.weather[0].description,
              weather_img_url: WEATHER_TYPE.filter(
                (weather) => weather.description === data.weather[0].description
              )[0].imgURL,
              temp: data.main.temp,
              feels_like: data.main.feels_like,
              temp_min: data.main.temp_min,
              temp_max: data.main.temp_max,
              clouds: data.clouds.all,
            });
          });

          setWeekWeather(WeatherData);
        }
      })
} catch (err) {
  console.log(err)
}
  setIsLoading(false);
}
```
  • 기상 카드 🌩

    • 일간카드
    <St.MockCard>
                <h3>{dayWeather.name}</h3>
                <img src={dayWeather.weather_img_url} alt={dayWeather.weather_description} />
                <div>
                    <span> 온도 </span>
                    <p> {dayWeather.temp} </p>
                </div>
                <div>
                    <span> 체감 온도 </span>
                    <p> {dayWeather.feels_like}</p>
                </div>
                <div>
                    <span> 최저 / 최고 </span>
                    <p> {dayWeather.temp_max} / {dayWeather.temp_min} </p>
                </div>
                <div>
                    <span> 구름 </span>
                    <p> {dayWeather.clouds}% </p>
                </div>
            </St.MockCard>
    • 주간 카드
    {weekWeather.map((data, idx) => (
          <St.MockCard key={idx}>
            <h3>{data.date}</h3>
            <img src={data.weather_img_url} alt={data.weather_description} />
            <div>
                <span> 온도 </span>
                <p> {data.temp} </p>
            </div>
            <div>
                <span> 체감 온도 </span>
                <p> {data.feels_like}</p>
            </div>
            <div>
                <span> 최저 / 최고 </span>
                <p> {data.temp_max} / {data.temp_min} </p>
            </div>
            <div>
                <span> 구름 </span>
                <p> {data.clouds}% </p>
            </div>
          </St.MockCard>  
          ))}
    • 필요한 데이터 카드만 받아서 set 해주기
    • 일간 카드는 지역명을, 주간 카드는 날짜명을 기입
  • 심화과제

    • Skeleton
    {isLoading?(
        <>
            <St.MockCard>
            <h3 className='skeleton-title'></h3>
              <img className='skeleton-img'/>
              <div className='skeleton-div'>
                  <span className='skeleton-span'>  </span>
                  <p className='skeleton-p'> </p>
              </div>
              </St.MockCard>
        </>
            ):(
        <>
            <St.MockCard>
                <h3>{dayWeather.name}</h3>
                <img src={dayWeather.weather_img_url} alt={dayWeather.weather_description} />
                <div>
                    <span> 온도 </span>
                    <p> {dayWeather.temp} </p>
                </div>
                <div>
                    <span> 체감 온도 </span>
                    <p> {dayWeather.feels_like}</p>
                </div>
                <div>
                    <span> 최저 / 최고 </span>
                    <p> {dayWeather.temp_max} / {dayWeather.temp_min} </p>
                </div>
                <div>
                    <span> 구름 </span>
                    <p> {dayWeather.clouds}% </p>
                </div>
            </St.MockCard>
        </>
            )}
    • isLoading 값으로 로딩 여부 확인 후 안되었을 경우 스켈레톤 보여주기
    @keyframes skeleton-gradient {
      0% {
        background-color: rgba(165, 165, 165, 0.1);
      }
      50% {
        background-color: rgba(165, 165, 165, 0.3);
      }
      100% {
        background-color: rgba(165, 165, 165, 0.1);
      }
    }
      .skeleton-title {
        width: 12rem;
        height: 4rem;
        background: ${(props) => props.theme.lightgrey};
        overflow: hidden;
        position: relative;
      }
    
      .skeleton-title::before {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      animation: skeleton-gradient 1.5s infinite ease-in-out;
    }
    
      .skeleton-img {
        width: 14rem;
        height: 14rem;
        background: ${(props) => props.theme.lightgrey};
        margin: 2rem 2rem;
        overflow: hidden;
        position: relative;
      }
    
      .skeleton-img::before {
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        animation: skeleton-gradient 1.5s infinite ease-in-out;
      }
    
      .skeleton-div {
        width: 14rem;
        height: 14rem;
        background: ${(props) => props.theme.lightgrey};
        overflow: hidden;
        position: relative;
      }
    
      .skeleton-div::before {
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        animation: skeleton-gradient 1.5s infinite ease-in-out;
      }
    • CSS로 애니메이션 적용

🥺 어려웠던 점

  • 스켈레톤 UI!!!!!!!!!!!

    • 도통 애니메이션 먹히질 않고 긴 코드를 파일 하나 안에서 관리하려니까 죽을 맛 🥲
    • 시작하기 전에 뭔가 가벼운 마음으로 (왜인지 내가 할 수 있을 것 같았음) 스타트했어서 더 힘들었던 것 같네융…
  • useState의 비동기 처리

    • 데이터를 끌어오고 상태를 업데이트하고 하는 모든 것들에서
      내가 원하는 순간에 동작하지 않아서 몇 번을 뜯었는지 ㅜㅜ 리액트의 상태 변화를 더 공부해야겠습니다 + useEffect 깔끔하게 쓰기

    +useEffect로 dayWeek, area 넣어주고
    +초기값 설정해줘서 해결


🌈 구현 결과물

ezgif-1-5dea5be1fe

@eunbeann eunbeann self-assigned this May 12, 2023
@@ -0,0 +1,2 @@
# VITE_APP_WEATHER='587bd6b133db966f6015b13ee1898879';//언니꺼
VITE_APP_WEATHER='fb02f9333a202901a648da747fc19542' #내꺼
Copy link
Member Author

Choose a reason for hiding this comment

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

뭐야 이거 왜 올라가잇어

Copy link

Choose a reason for hiding this comment

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

ㅋㅋㅋㅋㅋgitignore에 얼른 추가해줘

@@ -0,0 +1,2 @@
# VITE_APP_WEATHER='587bd6b133db966f6015b13ee1898879';//언니꺼
VITE_APP_WEATHER='fb02f9333a202901a648da747fc19542' #내꺼
Copy link

Choose a reason for hiding this comment

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

ㅋㅋㅋㅋㅋgitignore에 얼른 추가해줘

Error 404
</h1>
<p>
주소가 잘못되진 않았나요?
Copy link

Choose a reason for hiding this comment

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

너무 친절하고 귀엽다ㅎㅎ

border-radius: 1rem;
background-color: ${(props)=>props.theme.lightGreen};

&::placeholder { // 앞에 & 꼭...
Copy link

Choose a reason for hiding this comment

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

귀여운 주석 발견!ㅎㅎ 가끔 이런 거 빼먹어서 헤맬 때 있지ㅠ

@eunbeann eunbeann merged commit 1c1f088 into main Jul 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants