블록체인 TIL-5Week-29Day

디오·2023년 4월 10일
2

오늘은 React에서 이전에 웹사이트 만들때 적용했었던 날씨와 chatGPT를 React를 이용해 만들어보는 수업을 받았다.
오늘은 차크라UI를 하는줄 알았는데 갑자기 방향이 틀어져 다시
테일윈드를 사용해 수업을 하게 되었고, 지난주와 마찬가지로 이번주도 힘든 한주가 되겠다는 생각을 할 수밖에 없었다. 그래도 복습은 해야하고 TIL도 작성해야하니.. 일단 이해되는부분이 없지만
뭐라도 작성해 보겠다.



✅ React Icons 정리.

React Icons를 정리하기에 앞서 App.jsx를 같이 사용하였기 때문에 기본적인 코드를 먼저 올리도록 하겠다.

import Chat from "./components/Chat";
import Weather from "./components/Weather";

function App() {
  return <Chat />;
}

export default App;
  • import로 "Chat"과 "Weather"을 불러왔고, 원하는 함수를 return안에 불러왔다.



🪷 Weather.

전체코드로 보기 불편해서 코드를 구분해서 작성해보도록 하겠다.

 import { useEffect, useState } from "react";
import {
  FaSun,
  FaCloudSun,
  FaCloud,
  FaCloudMeatball,
  FaCloudSunRain,
  FaCloudShowersHeavy,
  FaPooStorm,
  FaSnowflake,
  FaSmog,
} from "react-icons/fa";
import axios from "axios";
  • import { useEffect, useState } from "react";를 작성해 React의 useEffect와 useState hook을 가져왔다.

    • useEffect hook은 컴포넌트가 렌더링될 때마다 특정 작업을 수행하도록 한다.
    • useState hook은 컴포넌트의 상태를 관리하는 데 사용한다.
  • 두번째줄부터 시작되는 FaSun부터 FaSmog까지는 React Icons에서 사용할 아이콘을 가져오기 위해 작성됬다. 이 아이콘들은 날씨 정보를 표시하는 데 사용된다.

  • import axios from "axios";는 Axios 라이브러리를 가져오는 코드다. Axios는 HTTP 요청을 처리하는 라이브러리로, 이 코드에서는 OpenWeatherMap API로부터 날씨 정보를 가져오기 위해 사용했다.

❗ 이 코드에 대한 정체적인 정리를 해보자면 이 코드는 React Hooks를 사용하여 컴포넌트 기반으로 구현됐다. useEffect를 사용하여 컴포넌트가 렌더링될 때마다 axios를 사용하여 OpenWeatherMap API로부터 날씨 정보를 가져오도록 하였고, 이 정보는 useState를 사용하여 컴포넌트의 상태로 관리되도록 구성되어있다.
이후, 가져온 날씨 정보에 따라서 FaSun, FaCloudSun, FaCloud, FaCloudMeatball, FaCloudSunRain, FaCloudShowersHeavy, FaPooStorm, FaSnowflake, FaSmog 등의 아이콘 중 하나를 표시하도록 하였고, 이를 통해 웹 애플리케이션은 사용자에게 날씨 정보를 시각적으로 전달할 수 있도록 만들어졌다.



const weatherIcon = {
  "01": <FaSun size={96} />,
  "02": <FaCloudSun size={96} />,
  "03": <FaCloud size={96} />,
  "04": <FaCloudMeatball size={96} />,
  "09": <FaCloudSunRain size={96} />,
  10: <FaCloudShowersHeavy size={96} />,
  11: <FaPooStorm size={96} />,
  13: <FaSnowflake size={96} />,
  50: <FaSmog size={96} />,
};
  • 해당 코드는 날씨 정보를 나타내는 아이콘을 선택하기 위한 객체다.

  • 객체의 이름은 weatherIcon이며, 각 날씨 코드(예: "01", "02", "03" 등)를 key로 가지고 해당하는 아이콘(, 등)을 value로 가지고 있도록 하였다. 그리고 이 객체를 사용하면, 날씨 코드를 통해 적절한 아이콘을 선택할 수 있다.

    • 예를 들어, 날씨 코드가 "01"일 경우 weatherIcon["01"]을 호출하여 아이콘을 선택할 수 있도록 한다. 이를 통해 날씨 정보를 시각적으로 전달하는 데 사용할 수 있고, 이 코드는 이전 코드와 함께 사용되어, 날씨 정보를 가져온다. 또한 해당 정보를 통해 적절한 아이콘을 선택하여 표시하는 데 사용된다.



function Weather() {
  const [lat, setLat] = useState();
  const [lon, setLon] = useState();
  const [weatherInfo, setWeatherInfo] = useState();

  const getGeolocation = () => {
    navigator.geolocation.getCurrentPosition(
      (position) => {
        setLat(position.coords.latitude);
        setLon(position.coords.longitude);
      }, 
      () => {
        alert("위치 정보에 동의 해주셔야 합니다.");
      } 
    );
  };
  • 해당 코드는 현재 위치의 위도와 경도를 가져오기 위한 함수인 getGeolocation()을 정의한다.

  • useState()를 사용하여 lat, lon, 그리고 weatherInfo라는 3개의 상태 값을 초기화한다. lat과 lon은 위치 정보를 저장하고, weatherInfo는 날씨 정보를 저장할 예정이다.

  • getGeolocation() 함수는 navigator.geolocation.getCurrentPosition() 메서드를 호출한다. 이 메서드는 사용자의 현재 위치를 가져오는 브라우저 기능인데 성공적으로 위치 정보를 가져오면, 콜백 함수가 호출되어 position.coords.latitude와 position.coords.longitude를 통해 위도와 경도를 설정한다. 그렇지 않은 경우, 오류 콜백 함수가 호출되어 "위치 정보에 동의 해주셔야 합니다." 라는 메세지를 출력한다.

❗이 함수는 이전 코드에서 useEffect()와 함께 사용되어, 컴포넌트가 렌더링될 때 한 번 호출되어 위치 정보를 가져오고, 해당 위치를 사용하여 API 요청을 보내어 날씨 정보를 가져오는 데 사용된다.



const getWeatherInfo = async () => {
    try {
      const response = await axios.get(
       
        `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${process.env.REACT_APP_WEATHER_API}&units=metric`
      ); 

      if (response.status !== 200) {
        alert("날씨 정보를 가져오지 못했습니다.");

        return;
      }
      console.log(response.data);
      setWeatherInfo(response.data); 

    } catch (error) {
      console.error(error); 
    } 
  };
  • 해당 코드는 OpenWeatherMap API를 사용하여 현재 위치의 날씨 정보를 가져오기 위한 함수인 getWeatherInfo()를 정의하는 코드다.

  • axios.get() 메서드를 사용하여 OpenWeatherMap API에 GET 요청을 보낸다. 이 때, lat과 lon 변수는 getGeolocation() 함수에서 설정된 현재 위치의 위도와 경도를 사용한다. process.env.REACT_APP_WEATHER_API는 API 키를 저장하는 환경 변수다. units=metric은 섭씨로 온도를 표시하도록 요청하는 코드다.

  • try-catch 문을 사용하여 비동기 함수를 처리했다. 성공적으로 API 요청이 완료되면, 반환된 response 객체의 status 속성을 확인하여 요청이 성공적으로 처리되었는지 확인한다. 그리고 요청이 성공하면, console.log() 함수를 사용하여 응답 데이터를 콘솔에 출력하고, setWeatherInfo() 함수를 사용하여 weatherInfo 상태 값을 설정한다.

  • 만약 요청이 실패하면, catch 블록이 실행되고 error 객체를 출력한다.

❗getWeatherInfo() 함수는 useEffect()와 함께 사용되어, lat과 lon이 변경될 때마다 호출되어 API 요청을 보내어 날씨 정보를 업데이트한다.

useEffect 안에서 비동기함수를 사용하기 위해서는 코드를 만들어야 비동기함수를 사용할 수 있다.



useEffect(() => {
    getGeolocation();
  }, []);
  useEffect(() => {
   
    if (!lat || !lon) return; 

    getWeatherInfo();
  }, [lat, lon]);
  useEffect(() => console.log(lat), [lat]);
  useEffect(() => console.log(lon), [lon]);
  useEffect(() => console.log(process.env.REACT_APP_WEATHER_API), []);
  • 해당 코드는 useEffect()를 사용하여 컴포넌트가 마운트될 때, 현재 위치 정보를 가져오기 위한 getGeolocation() 함수를 호출하고, lat과 lon 상태 값이 변경될 때마다 getWeatherInfo() 함수를 호출하는 코드다.

  • 첫번째 useEffect()는 빈 배열 [ ]을 의존성 배열로 갖고 있으므로, 컴포넌트가 마운트될 때 한 번만 실행된다. 이 때, getGeolocation() 함수를 호출하여 현재 위치 정보를 가져온다.

  • 두번째 useEffect()는 lat과 lon을 의존성 배열로 갖고 있다. 그래서 lat과 lon이 변경될 때마다 getWeatherInfo() 함수를 호출하여 현재 위치의 날씨 정보를 가져온다. 이 때, if (!lat || !lon) return;을 사용하여 lat과 lon 상태 값이 둘중 하나라도 초기값인 undefined인 경우, getWeatherInfo() 함수를 호출하지 않고 빠르게 반환하도록 한다.

  • 세번째,네번째 useEffect()는 각각 lat 상태 값이 변경될 때 또는 lon 상태 값이 변경될 때마다, lat 값과 lon 값을 콘솔에 출력한다.

  • 다섯번째 useEffect()는 빈 배열 []을 의존성 배열로 갖고 있으므로, 컴포넌트가 마운트될 때 한 번만 실행된다. 이 때, process.env.REACT_APP_WEATHER_API 값을 콘솔에 출력한다. 이 값은 API 요청에 필요한 API 키를 저장하는 환경 변수인데 따로 .env파일을 만들어 그 안에 필요한 API 키를 미리 받아 만들어둔다.



return (
    <div className="bg-red-100 min-h-screen flex justify-center items-center">
      {weatherInfo ? (
        <div className="flex flex-col justify-center items-center">
          {weatherIcon[weatherInfo.weather[0].icon.substring(0, 2)]}
         
          <div className="mt-8 text-2xl">
            {weatherInfo.name},{" "}
            {weatherInfo.main.temp.toString().substring(0, 4)} ℃
           
          </div>
        </div>
      ) : (
        "날씨 정보를 로딩중입니다 ..." 
      )}
    </div>
  );
}
  • 해당 코드는 화면에 날씨 정보를 출력하는 React 컴포넌트의 반환 값을 나타낸다.

  • 컴포넌트는 weatherInfo의 존재 여부에 따라 렌더링된다. weatherInfo가 존재하는 경우, 아이콘과 도시 이름, 그리고 온도가 포함된 div 태그를 반환한다. 이때, 아이콘은 weatherIcon 객체에서 해당 weatherInfo의 아이콘 코드(weatherInfo.weather[0].icon)를 가져와서 일치하는 아이콘 컴포넌트를 렌더링하도록 만들었다.

  • weatherInfo의 main.temp 속성에서 가져온 온도 값은 문자열로 변환되어 반환되었다. 이 문자열은 substring() 메소드를 사용하여 처음 네 자리만 자르도록 설정해 소수점 첫째자리까지만 표시되도록 짜여졌다.

  • weatherInfo가 존재하지 않는 경우, "날씨 정보를 로딩중입니다 ..."라는 문자열이 반환되도록 하였다.

❗위 코드에는 삼항연산자를 사용하여 해당 코드가 맞으면 짜여져있는 코드를 반환하도록 하고, 다르다면 로딩중을 표시하도록 하였다. 또한 icon을 날씨 상태에 따라 보여줄 수 있도록 만들어줬고, substring을 통해 밤,낮 D,N로 표현되는것을 방지하도록 설정하였다.



🪷 chat.

import axios from "axios";
import { useState } from "react";
  • 해당 코드는 axios와 useState 모듈을 가져오고, 이를 이용하여 HTTP 요청을 보내거나 함수형 컴포넌트에서 상태를 관리할 수 있는 준비를 하는 것이다.

    • axios 모듈은 HTTP 요청을 보내고 받기 위한 라이브러리다. 웹 애플리케이션에서 서버와 통신을 할 때 많이 사용된다.

    • useState는 함수형 컴포넌트에서 상태를 관리하기 위해 사용된다. useState를 사용하면 컴포넌트 내에서 상태를 변경하고, 변경된 상태를 렌더링할 수 있다.



const Chat = () => {
  const [question, setQuestion] = useState("");
  const [content, setContent] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  • 해당 코드는 React에서 함수형 컴포넌트를 정의하고, 상태를 초기화하는 코드다.

  • const Chat = () => {}는 Chat이라는 이름으로 내보내지는데 함수형 컴포넌트를 정의한다.

  • const [question, setQuestion] = useState("");는 문자열 타입의 question이라는 상태를 초기화하고, 이 상태를 변경하는 함수 setQuestion을 생성한다. 그리고 useState 함수의 인자로 전달된 ""는 question의 초기값을 의미한다.

  • const [content, setContent] = useState("");는 문자열 타입의 content라는 상태를 초기화하고, 이 상태를 변경하는 함수 setContent를 생성한다. 그리고 위와 마찬가지로 useState 함수의 인자로 전달된 ""는 content의 초기값을 의미한다.

  • const [isLoading, setIsLoading] = useState(false);는 isLoading이라는 상태를 초기화하고, 이 상태를 변경하는 함수 setIsLoading을 생성한다. 그리고 useState 함수의 인자로 전달된 false는 isLoading의 초기값을 뜻한다.



const onSubmitChat = async (e) => {
    try {
      e.preventDefault();

      if (isLoading) {
        alert("검색중입니다...");

        return;
      }
      if (!question) {
        alert("질문을 입력해주세요.");

        return;
      }

      setIsLoading(true);
      setContent("");
  • 해당 코드는 onSubmitChat이라는 함수를 정의하고, 이 함수를 이벤트 핸들러(e)로 사용하는 코드다. 이 함수는 사용자가 검색을 할 때 실행된다.

  • async 키워드는 onSubmitChat 함수가 비동기 함수라는것을 나타낸다.

  • try-catch는 예외 처리를 위한 문법이다. 이 블록 내에서 예외가 발생하면 catch 블록이 실행된다.

  • e.preventDefault()는 검색시 기본 동작인 페이지 새로고침을 막는 역할을 한다.

  • if문을 사용하여 isLoading 상태가 true이면 alert를 통해 검색 중임을 알려준다.

  • !question은 question 상태에 값이 없을 때를 나타낸다. 이 경우 마찬가지로 if문을 사용하여 alert를 통해 "질문을 입력해주세요."라는 메시지를 출력하도록 한다.

  • setIsLoading(true)는 isLoading 상태를 true로 변경한다. 그리고 그렇게 되면 검색중임을 나타내게 된다.

  • setContent("")는 content 상태를 빈 문자열로 초기화한다. 이렇게 되면 채팅 내용을 초기화하게 된다.

❗따라서, 위 코드는 사용자가 검색을 하면, 검색 중임을 나타내는 isLoading 상태를 변경하고, content 상태를 초기화하는 함수를 실행한다. 또한, question 상태에 값이 없으면 질문을 입력하라는 메시지를 출력한다.



 const response = await axios.post(
        "https://holy-fire-2749.fly.dev/chat",
        {
          question, 
        },
        {
          headers: {
            Authorization: "Bearer BLOCKCHAINSCHOOL3",
          },
        }
      );

      if (response.status !== 200) {
        alert("오류가 발생했습니다.");
        setIsLoading(false);

        return;
      }

      console.log(response);
      setContent(response.data.choices[0].message.content);

      setIsLoading(false);
    } catch (error) {
      console.error(error);

      setIsLoading(false);
    }
  };
  • 해당 코드는 axios를 사용하여 API 서버로 POST 요청을 보내는 코드다.

  • await axios.post()는 axios를 사용하여 POST 요청을 보내고, API 서버에서 반환한 응답을 기다리도록 한다.

  • "https://holy-fire-2749.fly.dev/chat"은 API 서버의 URL을 나타내는데 이 URL은 POST 요청을 받아 챗봇과의 대화를 진행하고 그 결과를 반환하도록 한다.

  • question은 사용자가 입력한 질문을 나타낸다. 그리고 입력한 값이 question에 저장된다.

  • headers는 HTTP 요청 헤더를 설정한다. 여기서 Authorization 헤더에 "Bearer BLOCKCHAINSCHOOL3" 값을 설정하도록 한다.

  • if문을 사용하여 응답 status가 200이 아닐 경우, "오류가 발생했습니다."라는 메시지를 출력한다.

    • 응답 data 속성에서 choices 배열의 첫 번째 요소([0])의 message 속성에서 content 값을 가져와 setContent를 통해 content 상태를 업데이트한다.
  • setIsLoading(false)는 isLoading 상태를 false로 변경한다. 그리고 로딩이 중복으로 실행되지 않게끔하는 역할을 한다.

  • catch 블록은 예외 처리를 위한 문법이다. console.error를 호출하여 오류를 출력하고, setIsLoading(false)를 호출하여 isLoading 상태를 false로 변경한다. 역시 중복으로 실행되지 않게끔 하는 역할을 한다.

❗따라서, 위 코드들은 API 서버로 POST 요청을 보내고, 그 결과를 기다린 다음, 요청이 성공했을 경우 응답에서 대화 내용(content)을 추출하여 setContent를 통해 업데이트한다. 요청이 실패했을 경우는, 오류 메시지를 출력하도록 한다.



return (
    <div className="bg-black min-h-screen flex flex-col justify-center items-center text-white">
      <form onSubmit={onSubmitChat}>
        <input
          className="text-black"
          type="text"
          value={question}
          onChange={(e) => setQuestion(e.target.value)} 
        />{" "}
      
        <input type="submit" value="검 색" />
      </form>
      {content && <div className="mt-4 px-16">{content}</div>}
    </div>
  );
};
  • 첫 div는 css적요소를 나타낸다.

  • form onSubmit={onSubmitChat}>은 onSubmitChat 함수를 검색 제출 핸들러로 등록한다. 검색할 때 onSubmitChat 함수가 호출된다.

  • input className="text-black" type="text" value={question} onChange={(e) => setQuestion(e.target.value)} />은 사용자가 질문을 입력할 수 있는 검색창 나타낸다.

    • value 속성은 question 상태를 사용하여 현재 입력된 값을 표시하고, onChange 이벤트 핸들러는 사용자가 입력한 값을 setQuestion 함수를 통해 question 상태에 업데이트하도록 한다.
  • input type="submit" value="검 색" />은 "검색" 버튼을 나타낸다.

  • {content && div className="mt-4 px-16">{content}/div>}은 content 상태가 비어 있지 않으면 and문을 사용해 ({content &&}) 대화 내용(content)을 표시하도록 한다. 이를 위해 className 속성을 사용하여 상단 여백(mt-4)과 가로 여백(px-16)을 추가한 div>를 만들었다.



🌜하루를 마치며..

이것저것 찾아보며 간신히 오늘 배웠던 부분을 정리하기는 했는데 정확하게 작성된건지 사실 잘 모르겠다. 이해하지 못하고 작성한 부분이라 틀린부분이 있을수도 있겠지만 그래도 역시 정리를 하고나니 그래도 어떤부분이 어떻게 사용되었는지에 대해서는 그래도 생각을 하는 시간을 갖을 수 있었다. 역시나 오늘도 정신적으로 힘든 하루였고, 다행이라면 다행일지 모르겠지만 어렵고 힘든과정이 계속 반복되다보니 그냥 어려운게 점점 당연하다는 느낌이 자리잡고 있어서 이걸 다행이라고 해야할지 아니라고 해야할지 모르겠다. 어차피 수업중에도 이해 못할테니 수업끝나고 내가 혼자 공부해야지 이런 마음이 자리잡다보니 집중력도 떨어지는것 같고.. 사실 현재 내 상황에 어떤게 맞는지 모르겠다. 일단은 내가 할 수있는 선에서 열심히는 하겠지만 이제는 나도 잘 모르겠다.

💠PS. 백엔드에 대한 부분도 배웠는데 음.. 잘모르겠다. 거기다 여러도구? 여기저기 들어가서 하다보니 헷갈리고 뭘 한건지도 잘 모르겠다;

profile
개발자가 되어가는 개린이"

0개의 댓글