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주차 ☀️ 날씨웹 기본/심화 과제 ] #18

Merged
merged 66 commits into from
May 28, 2023

Conversation

Chanwoo-Jeong
Copy link
Member

🌈 구현 결과물

배포🔗 : https://search-weather-one.vercel.app/day

image

✨ 구현 기능 명세

  • 기본 과제

✅ 기상 카드✔️

  1. 날씨에 따른 이미지 ✔️
  2. 온도✔️
  3. 체감온도✔️
  4. 최저,최고✔️
  5. 구름 % ✔️

✅ 일간 / 주간 + 지역(영어)검색✔️

  1. 날씨 검색 타입✔️
    1. 오늘 → 하루 ✔️
    2. 주간 → 5일 예보✔️
  2. 검색 기능✔️
    1. /day/:area or /week/:area 와 같이 params로 검색한 지역을 넘겨줍니다. :: hint) useParams ✔️
    2. 이를 가져와 오늘/ 주간 날씨를 렌더링 ✔️

✅ Outlet + Route ✔️

  1. Outlet과 Route를 활용하여 페이지 내의 컴포넌트를 변경해주세요! ✔️
    1. 헤더는 고정 ✔️
    2. 기상 정보 카드들만 경로에 따라 변경됩니다. ✔️
  2. 에러페이지 처리도 필수! ✔️
const Router = () => {
  return (
    <>
      <BrowserRouter>
        <Routes>
          <Route path="/" element={<SearchWeather />}>
            <Route path=":period" element={<WeatherInfoSection />}>
              <Route path=":area" element={<WeatherCardSection />} />
            </Route>
          </Route>
          <Route path="*" element={<ErrorPage />} />
        </Routes>
      </BrowserRouter>
    </>
  );
};
  • 심화 과제

✅ 커스텀훅으로 데이터를 받아옵시다!

  • 저 같은 경우에는 isError, isLoading, data를 커스텀훅의 반환값으로 만들어서 이를 스켈레톤 UI랑 연결지었습니다!!!

/** 날씨 데이터 받아오는 Hook */
const WeatherHook = (period, area) => {
  const [data, setData] = useState({});
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  const getWeatherData = useCallback(async () => {
    setIsLoading(true);
    setIsError(false);
    let url;
    if (period === "week")
      url = `https://api.openweathermap.org/data/2.5/forecast?q=${area}&appid=${
        import.meta.env.VITE_APP_WEATHER
      }&units=metric`;
    if (period === "day")
      url = `https://api.openweathermap.org/data/2.5/weather?q=${area}&appid=${
        import.meta.env.VITE_APP_WEATHER
      }&units=metric`;

    try {
      const res = await axios.get(url);
      setData(res.data);
    } catch (err) {
      setIsError(true);
    }

    setIsLoading(false);
  });

  useEffect(() => {
    
    getWeatherData();

    return () => {};
  }, [period, area]);

  return { data, isLoading, isError };
};

export default WeatherHook;

✅ 스켈레톤 UI

  • 기상 정보를 불러올때, 사용자에게 스켈레톤 UI를 보이게 합니다!
    image

🌼 PR Point

1. 영어지역 자동 filter 기능을 구현했습니다. (알파벳 g가 포함된 도시들)

image

/** 저장해둔 도시들*/
export const cityList = [
  "Seoul",
  "Busan",
  "Daegu",
  "Incheon",
  "Gwangju",
  "Daejeon",
  "Ulsan",
  "Sejong",
  "Gyeonggi-do",
  "Gangwon-do",
  "Chungcheongbuk-do",
  "Chungcheongnam-do",
  "Jeollabuk-do",
  "Jeollanam-do",
  "Gyeongsangbuk-do",
  "Gyeongsangnam-do",
  "Jeju",
];

/** 도시 검색시 cityList 자동완성 검색 기능 */
const AutoSearch = () => {
  const [keyword, setKeyword] = useState("");
  const [autoSearchResult, setAutoSearchResult] = useState([]);

  useEffect(() => {
    const howManyalphabethave = (cityList, keyword) => {
      const lowwerKeyword = keyword.toLowerCase();

      const lowerCityList = cityList.map((cityName) => {
        return cityName.toLowerCase();
      });

      const matchList = lowerCityList.map((cityNameL, i) => {
        if (cityNameL.indexOf(lowwerKeyword) !== -1) return cityList[i];
      });

      const pureList = matchList.filter((cityName) => cityName !== undefined);

      return pureList;
    };

    const searchResult = howManyalphabethave(cityList, keyword);
    setAutoSearchResult(searchResult);

    return ()=>{}
  }, [keyword, cityList]);

  return {keyword , setKeyword, autoSearchResult };
};

export default AutoSearch;
 /** 자동완성 도시 리스트에서 클릭시 라우팅 */
  const handleClickArea = (cityName) => {
    navigate(`/${period}/${cityName}`);
    setKeyword(cityName);
  };

2. 검색버튼이 아닌 input 창에 도시를 넣고 enter 를 누르면 라우팅 처리가 되도록 구현했습니다.

/** 검색창 엔터 키 시 날씨정보 받아오는 곳으로 라우팅 */
  const handleKeyDown = (e) => {
    if (e.keyCode !== 13) return;
    const area = inputRef.current.value;
    navigate(`/${period}/${area}`);
  };

3. 주소창으로 검색하더라도 오류가 나지 않도록 로직을 처리해두었습니다.

  /** 처음 로딩시 자동으로 day(오늘로 설정) */
  useEffect(() => {
    if (period === undefined) return navigate(`/day`);
    if (area) setKeyword(area);
    return () => {};
  }, [period, area]);

  /** 오늘 혹은 주간 변경시 라우팅 이동 */
  const navigatePeriod = (e) => {
    const selectedPeriod = e.target.value;
    if (area) return navigate(`/${selectedPeriod}/${area}`);
    navigate(`/${selectedPeriod}`);
  };

4. data 가 없는 지역을 검색하거나 잘못 검색했을 경우 UI처리를 해두었습니다.

image


🥺 소요 시간, 어려웠던 점

  • 12h 시간 정도 소요했습니다. 그냥 구현자체는 뭔가 어렵지 않았는데 주소창으로 검색하거나 , 단순히 오늘 주간 필터만 바꾸거나 등등 사용자의 다양한 행동들을 시도해보고 오류나는 부분들을 처리하는라 시간을 소요한 것 같습니다. (빡센QA ㅎㅎㅎ)
  • pr commit log 이슈가 이번주에도 소량 발생했습니다. 이제 문제의 원인을 정확히 파악했습니다...ㅎㅎ (덕분에 3차 생각과제 머지를 ....ㅎ) 그리고 약간의 지난주 커밋도....

Chanwoo-Jeong and others added 30 commits April 28, 2023 23:54
Copy link

@lydiacho lydiacho left a comment

Choose a reason for hiding this comment

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

수고많았우다 진짜 혼자 실버 골드를 넘어서 플래티넘 찍는 수준 미쳤다 혼자만의 챌린지 끊임없이 해내는 찬우... 진실된 '금'잔디세여..
p.s. 코드리뷰어의 뻔뻔한 부탁 : 혹시 PR포인트 쓸때 코드 써준 부분 파일명 같이 써줄 수 잇슬까 ? 고러면 PR 포인트 보면서 리뷰같이 하기 조을듯 고마어 ~

import { useEffect, useState } from "react";
import { cityList } from "../assets/cityList";

/** 도시 검색시 cityList 자동완성 검색 기능 */

Choose a reason for hiding this comment

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

아니 진짜 심화과제를 넘어선 플래티넘과제까지 혼자 할래 자꾸? 팟장자리 그만넘봐

Choose a reason for hiding this comment

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

내 말이 그말이여~~

return pureList;
};

const searchResult = howManyalphabethave(cityList, keyword);

Choose a reason for hiding this comment

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

함수 네이밍 폼 미쳤닼ㅋㅋㅋㅋㅋ 구석구석 세심함이 드러납니다요

Comment on lines +10 to +24
const howManyalphabethave = (cityList, keyword) => {
const lowwerKeyword = keyword.toLowerCase();

const lowerCityList = cityList.map((cityName) => {
return cityName.toLowerCase();
});

const matchList = lowerCityList.map((cityNameL, i) => {
if (cityNameL.indexOf(lowwerKeyword) !== -1) return cityList[i];
});

const pureList = matchList.filter((cityName) => cityName !== undefined);

return pureList;
};

Choose a reason for hiding this comment

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

이건 지짜 궁금해서 하는 질문인데!! 이런 함수들은 useEffect 내에서 정의해주는게 좋아?? 밖에서 함수 정의해놓고 useEffect 내부에서는 실행만 시키면 안되나???
useEffect(() => { setAutoSearchResult(howManyalphabethave(cityList,keyword)},[keyword,cityList]); 걍 이러케!

Comment on lines +45 to +46
imgURL:
"https://media.istockphoto.com/id/1392278078/ko/%EB%B2%A1%ED%84%B0/%EB%B9%97%EB%B0%A9%EC%9A%B8%EC%9D%B4%EC%9E%88%EB%8A%94-%ED%83%9C%EC%96%91%EA%B3%BC-%EA%B5%AC%EB%A6%84-%EB%82%A0%EC%94%A8-%EA%B0%9C%EB%85%90-3d-%EB%B2%A1%ED%84%B0-%EC%95%84%EC%9D%B4%EC%BD%98%EC%9E%85%EB%8B%88%EB%8B%A4-%EB%A7%8C%ED%99%94-%EC%B5%9C%EC%86%8C-%EC%8A%A4%ED%83%80%EC%9D%BC.jpg?b=1&s=170667a&w=0&k=20&c=J9W7kGm54XRYABT4VTfEFC3Kb60_3ROZRL9WbeVFEYc=",

Choose a reason for hiding this comment

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

우~ light rain 이미지 노잼 ~ 킬링포인트 넣는 센스 부타ㄱ드려여;;

Comment on lines +13 to +15
<Route path=":period" element={<WeatherInfoSection />}>
<Route path=":area" element={<WeatherCardSection />} />
</Route>

Choose a reason for hiding this comment

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

와 여기 :period :area 로 나눠서 라우트 뎁스 하나 더 내려가는거 설계ㅍㅁㅊㄷ

Choose a reason for hiding this comment

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

진짜 ㄹㅈㄷ라고 생각해용..

Comment on lines +27 to +29
<div>날씨를 불러올 수 없습니다 😭</div>
<div>지역(영어) 를 다시 확인해보세요!</div>
<div>혹은 url확인 , 새로고침(F5)을 시도해보세요!</div>

Choose a reason for hiding this comment

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

어이어이 div 멈추라고 ~ 1주차 기억 안나 ? 초심잃지 말자 ~

Choose a reason for hiding this comment

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

div 지옥~

Comment on lines +96 to +101
/** 검색창 엔터 키 시 날씨정보 받아오는 곳으로 라우팅 */
const handleKeyDown = (e) => {
if (e.keyCode !== 13) return;
const area = inputRef.current.value;
navigate(`/${period}/${area}`);
};

Choose a reason for hiding this comment

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

13번.. 엔터키.. 메모..

Comment on lines +54 to +60
/** 오늘일 경우]날짜세팅 로직 */
const today = new Date();
const year = today.getFullYear();
let month = String(today.getMonth() + 1);
let date = String(today.getDate());
if (month.length === 1) month = "0" + month;
if (date.length === 1) date = "0" + date;

Choose a reason for hiding this comment

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

kiya 예제대로면 그냥 dt_txt stlice 갈겨주면 되는데 직접 Date 객체 불러와서 파싱하는 클라스 미쳤다

Choose a reason for hiding this comment

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

진짜 레전드다 나는 그냥 문자열 대츙 슬라이싱했는데, 이렇게 객체 선언까지 하는게 넘 최고에요!

/>
);
})
)}

Choose a reason for hiding this comment

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

삼향연산자 쾌감 ㅠㅠ 잘쓴다 잘써

@Chanwoo-Jeong Chanwoo-Jeong merged commit 0893441 into main May 28, 2023
Chanwoo-Jeong added a commit that referenced this pull request May 28, 2023
Copy link

@seojisoosoo seojisoosoo left a comment

Choose a reason for hiding this comment

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

역시 넘 멋져요....ㅠㅠ 최고 차누!!!!!!!

@@ -0,0 +1 @@
VITE_APP_WEATHER='b1581c63833eb15d2ba7179d359626a0'

Choose a reason for hiding this comment

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

오엠지!! env 파일은 깃허브에 올라오지 않도록 .gitignore에 꼭 추가해주세요!!

import { useEffect, useState } from "react";
import { cityList } from "../assets/cityList";

/** 도시 검색시 cityList 자동완성 검색 기능 */

Choose a reason for hiding this comment

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

내 말이 그말이여~~

const searchResult = howManyalphabethave(cityList, keyword);
setAutoSearchResult(searchResult);

return ()=>{}

Choose a reason for hiding this comment

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

오홍 이 return문은 무엇인가효??

Comment on lines +4 to +5
/** 날씨 데이터 받아오는 Hook */
const WeatherHook = (period, area) => {

Choose a reason for hiding this comment

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

커스텀 훅도 결국 보통의 함수와 유사하지만, 공식문서를 살펴보면 훅은 반드시 use로 시작되는 이름이어야한다고 나와있네용! 훅들은 use로 시작될 수 있도록 네이밍하는 게 좋을 것 같아요!

스크린샷 2023-06-08 오후 10 19 18

커스텀 훅 공식문서

url = `https://api.openweathermap.org/data/2.5/forecast?q=${area}&appid=${
import.meta.env.VITE_APP_WEATHER
}&units=metric`;
if (period === "day")

Choose a reason for hiding this comment

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

else if를 쓰는 것은 어떨까용


font-size: 5rem;

background-color: #1a73e8;;;

Choose a reason for hiding this comment

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

;;;;;;;;?ㅋㅋㅋㅋㅋㅋㅋ

Comment on lines +86 to +90
<header></header>
<div>
<div></div>
<span></span>
</div>

Choose a reason for hiding this comment

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

안에 내용이 없는 경우는

이렇게 써줘도 좋을 것 같구, 이렇게 태그만 보았을 때에는 이게 무엇을 의미하는지 알 수 없자나용?! 그래서 선택자보다는 스타일드컴포넌트로 의미를 담은 명칭을 주는 것은 어떨까용??


const WeatherCard = (props) => {
const { period } = useParams();
const { feelsLike, temp, maxTemp, minTemp, weatherImg, castDate, clouds } =

Choose a reason for hiding this comment

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

구조분해할당 칭찬해

Comment on lines +54 to +60
/** 오늘일 경우]날짜세팅 로직 */
const today = new Date();
const year = today.getFullYear();
let month = String(today.getMonth() + 1);
let date = String(today.getDate());
if (month.length === 1) month = "0" + month;
if (date.length === 1) date = "0" + date;

Choose a reason for hiding this comment

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

진짜 레전드다 나는 그냥 문자열 대츙 슬라이싱했는데, 이렇게 객체 선언까지 하는게 넘 최고에요!

return (
<Wrapper>
<header>
{period === "day" ? `${year} - ${month} - ${date}` : dateOnly}

Choose a reason for hiding this comment

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

보니까 period === "day" 이거가 계속 사용되는 것 같아용! 함수로 따로 빼서 export하고 재활용하면 어떨까요??

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.

3 participants