Jieunny의 블로그

[TS] 날씨 검색 서비스 구현하기 본문

CodeStates/TS 스터디

[TS] 날씨 검색 서비스 구현하기

Jieunny 2023. 3. 31. 16:17

📣  OpenWeather API를 이용해서 날씨 검색 서비스 구현하기

스터디에서 각자 타입스크립트를 이용한 미니 프로젝트를 해오기로 했는데, 나는 날씨 검색 서비스를 만들기로 했다.

🚨css 랑 기능 추가하면서 밑에 적은 코드랑 조금 달라졌다
 

🔗 파일 구조

    src
        ㄴ components 
            ㄴ PrintWeather.tsx : 날씨가 어떤지를 출력하는 컴포넌트
            ㄴ InsertCityName.tsx : 도시를 입력할 input, 검색버튼을 출력하는 컴포넌트 
        ㄴ img : 배경화면이 될 날씨 이미지를 가지고 있는 폴더
        ㄴ type : openWeather에서 제공해주는 데이터 타입을 담고있는 폴더
            ㄴ types.ts
       ㄴ App.tsx : api로 데이터를 받아오고, 그에 맞는 배경화면과 다른 컴포넌트들을 모두 출력하는 컴포넌트 
.env : 환경 변수를 담고 있는 파일
 

𝟭. OpenWeather API 이용하기

// .env
REACT_APP_API_URL = https://api.openweathermap.org/data/2.5/weather
REACT_APP_WEATHER_API_KEY = '내 API KEY'

➰ .env 파일을 생성해서 사용할 API 주소와 내 API KEY를 환경 변수로 저장한다.
 

// App.tsx

const api = {
  //.env 파일의 환경 변수 사용
  url: process.env.REACT_APP_API_URL,
  api_key: process.env.REACT_APP_WEATHER_API_KEY
}

const App = () => {
  const [cityName, setCityName] = useState('seoul');
  // InsertCityName 컴포넌트에 props로 전달할 변수
  const [weather, setWeather] = useState<WeatherData>();
  // PrintWeather 컴포넌트에 props로 전달할 변수
  const [weatherName, setWeatherName] = useState("");

  const getWeather = async() => {
    // 오픈 API에서 특정 도시 날씨 받아오기
    const response = await axios.get(`${api.url}?q=${cityName}&appid=${api.api_key}`);
    // cityName에 들어있는 도시의 날씨를 받아온다.
    setWeather(response.data);
    setWeatherName(response.data.weather[0].main);
    // console.log(response.data);
  }

  useEffect(() => {
    getWeather();
  }, [cityName])
  // cityName 변할 때마다 데이터 불러오기

➰ 새 변수를 선언해서, 환경 변수들을 불러와준다.
➰ axios를 사용해서 open api의 데이터를 불러와주는데, 이때 특정 도시의 날씨를 불러와야하므로 query를 이용한다.
➰ query로 전달할 도시 이름은 state로 선언해주고, 이 도시 이름은 내가 입력할 때마다 바뀌어야 하므로 InsertCityName 컴포넌트로 전달해준다.
 

𝟮. 불러온 데이터를 각 컴포넌트에 전달, 받아오기

➰ TS에서 props를 받아올 때는 타입까지 함께 적어주어야 한다.

// App.tsx

const weatherImg:{[key: string]: string} = {
  Clear: Clear,
  Clouds: Clouds,
  Drizzle: Drizzle,
  Rain: Rain,
  Snow: Snow,
  Thunderstorm: Thunderstorm,
  // 각각은 이미지를 컴포넌트로 불러온 것
}
// App.tsx의 Container 컴포넌트에 배경화면으로 전달할 props

interface ContainerProps {
  // Container의 props 타입 지정
  img: string | undefined;
}
// interface로 props의 타입을 지정해주고

const Container = styled.div<ContainerProps>`
  width: 100%;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background-image: url(${(props) => props.img});
  // 받아온 img props를 배경사진으로 설정해준다.
  // 위에 <ContainerProps>로 타입 지정을 해줘야 props로 받아올 수 있다. 안받아오면 에러뜸.
  transition: all 0.5s ease-in;
  background-size: cover;
  background-repeat: 100% 100%;
  background-position: center;
`
// 사용할 컴포넌트에서 타입까지 받아준다.

return (
    <Container img={weatherImg[weatherName]}>
      {/* weatherName에 해당하는 이름을 img에서 찾아서 img props로 전달 */}
      <Box>
        <ProjectTitle>{`오늘 '${weather?.name}'의 날씨는?`}</ProjectTitle>
        <PrintWeather weather={weather}/>
        <InsertCityName setCityName={setCityName}/>
      </Box>
    </Container>

  );

➰ 각 컴포넌트에 필요한 state를 props이름={props값} 으로 전달해준다.
 

// PrintWeather.tsx

interface Props {
  // props 타입 지정해주기
  weather: WeatherData | undefined;
}

const PrintWeather = ({weather} : Props) => {
  return(
    <Container>
      <WeatherInfo>{`${weather?.weather[0].description}`}</WeatherInfo>
    </Container>
  )
}

➰ 받아온 props의 타입을 지정해주고, 받아올 때 타입도 표기해준다.
 

// InsertCityName.tsx

interface Props {
  setCityName: React.Dispatch<React.SetStateAction<string>>;
  //useState 의 set 함수는 TS에서의 타입이 Dispatch<React.SetStateAction<string>> 이다.
}

const InsertCityName = ({setCityName} :Props) => {
  const [city, setCity] = useState('seoul');
  // input칸에 입력한 city이름 담을 변수

  const changeCityName = (e: React.ChangeEvent<HTMLInputElement>) => {
    setCity(e.target.value);
  }

➰ 받아온 props의 타입을 지정해주고, 받아올 때 타입도 표기해준다.


📣  실행 화면