๐Ÿ”ฎ ์†Œ๋งˆ๋ฒ• ํ”„๋กœ์ ํŠธ -5 (today)

Date ๊ฐ์ฒด์™€ **๋‚ ์”จ API **๋ฅผ ์ด์šฉํ•œ ์˜ค๋Š˜์˜ ๋‚ ์งœ, ์‹œ๊ณ„, ๋‚ ์”จ ์•ฑ์„ ๋ณผ ์ˆ˜ ์žˆ๋Š” ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜.

์‹œ๊ณ„๋Š” ๋งค์ดˆ๋งˆ๋‹ค ๋ฆฌ๋กœ๋“œ๋˜๋ฉฐ, ๋‚ ์”จ๋Š” ์ดˆ๊ธฐ๋กœ๋”ฉ ๋•Œ๋งŒ ๋กœ๋“œ๋˜๊ฒŒ ์„ค์ •ํ•ด์ฃผ์—ˆ๋‹ค.

์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…


#. Project Map


์ œ์ž‘๋…ธํŠธ ํ•œ๋ˆˆ์—๋ณด๊ธฐ[์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ]

1. ๋ ˆ์ด์•„์›ƒ

today

์œ„์—์„œ๋ถ€ํ„ฐ ๋‚ ์งœ, ์‹œ๊ฐ„, ๋‚ ์”จ ์ˆœ..

2. ์‹œ๊ณ„ (Clock)

2-1. ์ƒํƒœ๊ด€๋ฆฌ (state)

const weekArr = ['์ผ', '์›”', 'ํ™”', '์ˆ˜', '๋ชฉ', '๊ธˆ', 'ํ† ']

const [year, setYear] = useState('')
const [month, setMonth] = useState('')
const [day, setDay] = useState('')
const [week, setWeek] = useState('')

const [hours, setHours] = useState('')
const [minutes, setMinutes] = useState('')
const [seconds, setSeconds] = useState('')

๋…„, ์›”, ์ผ, ์š”์ผ, ์‹œ๊ฐ„, ๋ถ„, ์ดˆ๋ฅผ ๊ฐ๊ฐ hooks ๋กœ ๋งŒ๋“ค์–ด์ฃผ์—ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์š”์ผ์„ ํ•œ๊ธ€๋กœ ์ถœ๋ ฅํ•ด์ค„ ๋ฐฐ์—ด์„ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด์ฃผ์—ˆ๋‹ค.

2-2. ๋™์ž‘ (setInterval)

useEffect(() => {
  clock()
  setInterval(clock, 1000)
})

const clock = () => {
  const date = new Date()

  // setTodayDate(date)
  setYear(date.getFullYear())
  setMonth(date.getMonth())
  setDay(date.getDate())
  setWeek(date.getDay())

  setHours(date.getHours())
  setMinutes(date.getMinutes())
  setSeconds(date.getSeconds())
}

๋งˆ์šดํŠธ๊ฐ€ ๋๋‚˜๊ณ  useEffect ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•  ๋•Œ, clock ํ•จ์ˆ˜๋ฅผ ํ•œ๋ฒˆ ์‹คํ–‰ํ•˜๊ฒŒ ํ•˜๊ณ 

๊ทธ๋‹ค์Œ๋ถ€ํ„ฐ๋Š” setInterval ํ•จ์ˆ˜๋กœ clockํ•จ์ˆ˜๋ฅผ ์ฃผ๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋˜๋„๋ก ํ•ด์ฃผ์—ˆ๋‹ค.

2-3. ๋ทฐ (View)

<>
  <div style={{ marginTop: '5px', fontSize: '18px', fontFamily: 'escore7' }}>
    {year}๋…„ {month + 1}์›” {day}์ผ {weekArr[week]}์š”์ผ
  </div>
  <div style={{ marginTop: '10px', fontSize: '60px', color: 'orange' }}>
    {hours < 10 ? `0${hours}` : hours}:{minutes < 10 ? `0${minutes}` : minutes}:
    {seconds < 10 ? `0${seconds}` : seconds}
  </div>
</>

1.

ํƒœ๊ทธ๋“ค์ด ๋ณต์žกํ•œ ๊ตฌ์กฐ๊ฐ€ ์•„๋‹ˆ์—ฌ์„œ, styled Component ๋ง๊ณ  style ์†์„ฑ์„ ์‚ฌ์šฉํ–ˆ๋‹ค.

2.

์‹œ๊ฐ„, ๋ถ„, ์ดˆ๊ฐ€ ํ•œ์ž๋ฆฌ์ผ ๊ฒฝ์šฐ ์•ž์— 0์„ ๋ถ™์—ฌ ์–ด์ƒ‰ํ•˜์ง€ ์•Š๊ฒŒ ํ•ด์ฃผ์—ˆ๋‹ค.

์‚ผํ•ญ ์—ฐ์‚ฐ์ž์™€ ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด์„ ์‚ฌ์šฉํ•ด๋ณด์•˜๋‹ค.

3. ๋‚ ์”จ (Weather)

3-1. ์ƒํƒœ๊ด€๋ฆฌ (state)

const [temp, setTemp] = useState('')
const [humidity, setHumidity] = useState('')
const [weather, setWeather] = useState('')
const [weatherIcon, setWeatherIcon] = useState('')
const [country, setCountry] = useState('')
const [city, setCity] = useState('')
const [cloud, setCloud] = useState('')

๋‚ ์”จ์˜ ์ƒํƒœ (state) ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ hooks๋ฅผ ์ด์šฉํ–ˆ๋‹ค.

๊ฐ๊ฐ, ๊ธฐ์˜จ, ์Šต๋„, ๋‚ ์”จ, ๋‚ ์”จ์•„์ด์ฝ˜, ๋‚˜๋ผ, ๋„์‹œ, ๊ตฌ๋ฆ„์˜ ์–‘ ์ด๋‹ค.

3-2. ๋™์ž‘ (openweathermap API)

useEffect(() => {
  weather_clock()
})

const weather_clock = () => {
  const url =
    'https://api.openweathermap.org/data/2.5/weather?q=Seoul&appid=5a852cca928001166e0c28dca72c5987'
  fetch(url)
    .then(res => {
      return res.json()
    })
    .then(json => {
      setTemp(Math.round(json.main.temp - 273.15))
      setHumidity(json.main.humidity)
      setWeather(json.weather[0].main)
      setWeatherIcon(json.weather[0].icon)
      setCountry(json.sys.country)
      setCity(json.name)
      setCloud(json.clouds.all + '%')
    })
}

๋‚ ์”จ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ useEffect๋ฅผ ์ด์šฉํ•ด์„œ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰์‹œ์ผœ ๋™์ž‘ํ•˜๋„๋ก ํ•˜์˜€๋‹ค.

api๋กœ๋Š” openweathermap API ๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.

https://openweathermap.org/

AJAX ๋Š” fetch ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค.

3-3. ๋ทฐ (View)

<>
  <div
    style={{
      marginTop: '4px',
      fontFamily: 'escore8',
      fontSize: '16px',
      textShadow: '2px 2px #ccc',
    }}
  >
    {weather}
  </div>
  <img src={`http://openweathermap.org/img/w/${weatherIcon}.png`} />
  <div
    style={{
      fontSize: '10px',
      fontFamily: 'escore9',
    }}
  >
    {country}, {city},
  </div>
  <div style={{ marginTop: '20px', fontSize: '14px' }}>ํ˜„์žฌ์˜จ๋„ : {temp}โ„ƒ</div>
  <div style={{ marginTop: '10px', fontSize: '14px' }}>
    ํ˜„์žฌ์Šต๋„ : {humidity}%
  </div>
  <div style={{ marginTop: '10px', fontSize: '14px' }}>
    ๊ตฌ &nbsp; &nbsp; &nbsp; ๋ฆ„ : {cloud}
  </div>
</>

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ styled component๊ฐ€ ์•„๋‹Œ style ์†์„ฑ์„ ์ด์šฉํ–ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋‚ ์”จ ์ด๋ฏธ์ง€ ์•„์ด์ฝ˜๋„ openweathermap API ์—์„œ ์ œ๊ณต์„ ํ•ด์ค˜์„œ ๊ฐ™์ด ์‚ฌ์šฉํ•˜์˜€๋‹ค.

4. ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…

4-1. today > layout.js

// today > layout.js
import React from 'react'
import styled from 'styled-components'
import Weather from './Weather'
import Clock from './Clock'
import ContentsMenubar from '../ContentsMenubar'

const BackgroundContainer = styled.div`
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 100vw;
  height: 100vh;
  background: #f5f6f7;
`

const TodayContainer = styled.div`
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  border-radius: 3px;
  box-shadow: -4px -2px 4px 0px white, 4px 2px 6px 0px #dfe4ea;
  padding: 0px 40px 60px 40px;
`

const Layout = () => {
  return (
    <>
      <BackgroundContainer>
        <TodayContainer>
          <ContentsMenubar name="today" />
          <Clock />
          <Weather />
        </TodayContainer>
      </BackgroundContainer>
    </>
  )
}

export default Layout

4-2. Error code : 429 (Too many Request)

์ฒ˜์Œ์—๋Š” Date ๊ฐ์ฒด๋กœ ์ƒํƒœ๋ฅผ ๊ณ„์† ์—…๋ฐ์ดํŠธํ•ด์ฃผ๋Š” ์ฝ”๋“œ์™€ ๋‚ ์”จ api๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญ, ์‘๋‹ตํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ํ•œ ๊ณณ์— ๋‘์—ˆ์—ˆ๋‹ค.

๊ทผ๋ฐ, ์“ธ๋ฐ์—†์ด ์‹œ๊ฐ„์„ ์นด์šดํŠธํ•˜๋ฉด์„œ ๊ณ„์† ๋‚ ์”จ api ์š”์ฒญ๋„ ๊ณ„์† ๋ณด๋‚ด์„œ ๋‚˜์ค‘์—๋Š” api ์„œ๋ฒ„์—์„œ ์—๋Ÿฌ(429 Too many Request)๋ฅผ ๋ฑ‰์–ด๋ƒˆ๋‹ค.

๋‚ด๊ฐ€ ์‚ฌ์šฉํ•œ api๋Š” ๋ฌด๋ฃŒ์˜€๊ณ , ์ดˆ๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๋”๋งŽ์ด ์š”์ฒญํ•˜๋ ค๋ฉด ์œ ๋ฃŒ๋กœ ์ „ํ™˜ํ•ด์•ผํ•จ.

๊ทธ๋ž˜์„œ ์ดˆ๋‹น ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•œ Clock ์ปดํฌ๋„ŒํŠธ์™€ ๊ธฐ๋Šฅ๋“ค,

๊ทธ๋ฆฌ๊ณ  ์ดˆ๊ธฐ ๋กœ๋”ฉ ๋•Œ๋งŒ ์š”์ฒญํ•ด๋„ ๊ดœ์ฐฎ์€ Weather ์ปดํฌ๋„ŒํŠธ์™€ ๊ธฐ๋Šฅ๋“ค์„ ๋”ฐ๋กœ ๋นผ์ฃผ์—ˆ๋‹ค.

api๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, ์š”์ฒญ์˜ ์–‘์ด ์ปค์ง์— ๋”ฐ๋ผ ๋น„์šฉ๋„ ์ปค์ง„๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค.

5. ๊ฐœ์ธ์ ์ธ ํ”ผ๋“œ๋ฐฑ

5-1. ์„œ๋ฒ„๋ฆฌ์Šค

๊ณต๊ณต๋ฐ์ดํ„ฐํฌํ„ธ์˜ open API ์ง€์นจ์„œ ์ค‘..

crossdomain2

์›๋ž˜๋Š” ๊ธฐ์ƒ์ฒญ API ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค ํ–ˆ์œผ๋‚˜ ๋ธŒ๋ผ์šฐ์ € ๋‚ด javascript ์ฝ”๋“œ๋กœ api์— ์ ‘๊ทผํ•  ๊ฒฝ์šฐ CORS ์ด์Šˆ๊ฐ€ ์žˆ๋‹ค๋Š” ์ด์•ผ๊ธฐ๋ฅผ ๋“ฃ๊ณ  ๋‹ค๋ฅธ api๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.

์ด์ „์˜ ์†Œ๋งˆ๋ฒ• ํ”„๋กœ์ ํŠธ - 4 (crawling)ํฌ๋กค๋ง ์•ฑ์—์„œ๋„ ๋ฐœ์ƒํ–ˆ์—ˆ๋Š”๋ฐ CORS๋ฌธ์ œ๋ฅผ ์ž˜ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์„œ๋ฒ„๋ฅผ ํ•˜๋‚˜ ๋‘๊ณ  ์„œ๋ฒ„์™€ ํ”„๋ก ํŠธ์˜ AJAX, ๊ทธ๋ฆฌ๊ณ  ์„œ๋ฒ„์™€ api ํ†ต์‹  ์ด๋Ÿฐ์‹์œผ๋กœ ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

์‚ฌ์‹ค nodeJS ์—์„œ ๋”ฐ๋กœ api ํ†ต์‹ ์„ ํ•ด๋ณด์ง€๋Š” ์•Š์•˜์ง€๋งŒ ๋‚˜์ค‘์— ๊ผญ ๋™์ผํ•œ api ํ†ต์‹ ์„ ๊ฐ€์ง€๊ณ  ์›น ๋ธŒ๋ผ์šฐ์ €์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ apiํ†ต์‹ , ์„œ๋ฒ„์˜ nodeJS์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ apiํ†ต์‹  ๋‘˜๋‹ค ํ•˜๋ฉด์„œ ๋น„๊ตํ•ด๋ด์•ผ๊ฒ ๋‹ค.

์†Œ๋งˆ๋ฒ• ํ”„๋กœ์ ํŠธ - 4 (crawling)์—์„œ๋„ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ฐœ์ƒํ–ˆ๋˜ CORS ์ด์Šˆ๊ฐ€ nodeJS ์—์„œ๋Š” ๋ฐœ์ƒํ•˜์ง€ ์•Š์•˜๋‹ค.


Written by@taenyKim
๋ฐฐ์šฐ๋ฉฐ ์„ฑ์žฅํ•˜๊ณ  ๊ธฐ๋กํ•˜๊ธฐ #FE #UI #๊ฐœ๋ฐœ #life

GitHubFacebook