블록체인 TIL-4Week-24Day

디오·2023년 4월 5일
0

오늘도 정신없이 시작하는 하루였다. 요즘 아침부터 저녁까지 계속 코딩만 하니 그냥 기계처럼 코딩만 하는 느낌이다. 물론 그게 나쁘다는건 아니다. 뭔가에 열중해서 빠져서 사는게 운동할때 이후로 오랜만이다보니 익숙하면서도 새로운 그런 느낌이다. 오늘도 새로운듯 새롭지 않은 React를 배웠고, 간단하게 오늘 배운 부분에 대한 정리와 4pm부터 있었던 우리 아기사자스터디팀에 대해 소소하게 적어볼까 한다.



☑️ 클래스형/함수형 컴포넌트.

이전에 간략하게 클래스형과 함수형 컴포넌트에 대해서 언급을 했었는데 강사님께서 오늘 디테일하게 어떤점이 다른지에 대해서 강의를 하셨다. 사실 강의를 했다기 보다는 가볍게 언급하신다고 하셨는데 강사님의 열정으로 보다 세세하게 들을 수 있었다.

<클래스형 컴포넌트>

import { Component } from "react";

class ClassComp extends Component {
  render() {
    return (
      <div className="bg-blue-100 w-1/2 min-h-screen flex justify-center items-center text-2xl">
        ClassComp
      </div>
    );
  }
}

export default ClassComp;
<함수형 컴포넌트>

function FunctionalComp() {
  return (
    <div className="bg-red-100 w-1/2 min-h-screen flex justify-center items-center text-2xl">
      FunctionalComp
    </div>
  );
}

export default FunctionalComp;
  • 클래스형 컴포넌트는 함수형과는 다르게 class로 시작하고 return위에 render로 감싸져있다.

  • 함수형 컴포넌트는 상단에 function으로 시작하고 render를 사용하지 않는다.

  • 결과를 봤을때 두 코드의 기능상의 차이는 보이지 않는다.



💠 예제를 통한 두 컴포넌트 비교.

<클래스형 컴포넌트>

import { Component } from "react";

class ClassComp extends Component {
  state = {
    count: 0,
  };

  render() {
    return (
      <div className="bg-blue-100 w-1/2 min-h-screen flex justify-center items-center text-2xl">
        <div>{this.state.count}</div>
      </div>
    );
  }
}

export default ClassComp;
<함수형 컴포넌트>

import { useState } from "react";

function FunctionalComp() {
  const [count, setCount] = useState(0);

  return (
    <div className="bg-red-100 w-1/2 min-h-screen flex justify-center items-center text-2xl">
      {count}
    </div>
  );
}

export default FunctionalComp;
  • 두 컴포넌트를 비교했을때 화면 또한 동일하게 나온다.

  • 클래스형 컴포넌트는 상태값과 라이프사이클을 가지고 있다.

    • state를 통해 상태를 선언하고 this키워드를 통해 상태 값에 접근할 수 있다.
  • 함수형 컴포넌트는 상태값과 라이프사이클을 가질 수 없다.

    • 다만, 리액트 16.8 이후 Hooks의 등장으로 함수형 컴포넌트에서도 상태 값과 라이프사이클을 가질 수 있게 되었다.

    • 업데이트 이후로 함수형 컴포넌트에서는 useState를 통해 상태값을 다룰 수 있다.



💠 두 컴포넌트의 상태값 업데이트 방법.

<클래스형 컴포넌트>

import { Component } from "react";

class ClassComp extends Component {
  state = {
    count: 0,
  };

  setCount(num) {
    this.setState({
      count: num,
    });
  }

  render() {
    return (
      <div className="bg-blue-100 w-1/2 min-h-screen flex flex-col justify-center items-center text-2xl">
        <div>{this.state.count}</div>
        <button
          className="bg-purple-300 px-4 py-2 rounded-md"
          onClick={() => this.setCount(this.state.count + 1)}

          +
        </button>
      </div>
    );
  }
}

export default ClassComp;
<함수형 컴포넌트>

import { useState } from "react";

function FunctionalComp() {
  const [count, setCount] = useState(0);

  return (
    <div className="bg-red-100 w-1/2 min-h-screen flex flex-col justify-center items-center text-2xl">
      {count}
      <button
        className="bg-purple-300 px-4 py-2 rounded-md"
        onClickAdd={() => setCount(count + 1)}

        +
      </button>
    </div>
  );
}

export default FunctionalComp;
  • 함수형 컴포넌트는 useState에 setCount를 사용하여 값을 업데이트 해준것을 확인할 수 있다.

  • 클래스형 컴포넌트는 class 내부에 setCount를 만들고 접근하여 값을 업데이트 해준것을 볼 수 있다.



🔰 this.

사실 this에 대해서는 내용이 너무 어렵다. 이해하기에는 아직 내가 많이 부족한가보다. 그래도 강사님께서 중요한 내용? 기술면접에서 나올 수 있다고 하셨기 때문에 정리를 해두고 차근차근 알아가는것이 좋다고 생각한다.

💡this에 대한 공식 문서

  • 다른 프로그래밍 언어와는 동작방식이 다르다.

  • 함수를 호출한 방법에 의해 결정된다.

    • 함수에 따라 호출이 다르다는 이야기는 this를 실행하는 시점마다 this가 가진 값이 달라진다는 말이다.



🔶Web browser, NodeJS에서의 this.

  • Web browser 자신인 Window 객체를 출력한다. window 객체는 우리가 사용했던 alert 기능을 포함한 객체다.

  • Node에서는 global 객체를 출력한다.

❗Web browser에서 실행했을 때랑, NodeJS에서 실행했을 때의 this는 서로 다른 값을 지칭한다.



🔶객체에서의 this.

const myProfile = {
  codeName: "h662",
  age: 18,
  whatIsThis: function () {
    console.log(this);
  },
};

myProfile.whatIsThis();
  • this가 객체 자신을 지칭하는 것을 확인할 수 있다.

  • 화살표 함수는 this를 가질 수 없다.



🔶재 할당 된 this.

const myProfile = {
  codeName: "h662",
  age: 18,
  whatIsThis: function () {
    console.log(this);
  },
};

myProfile.whatIsThis();

let abc = myProfile.whatIsThis;

abc();
  • 재 할당 된 abc는 다시 NodeJS 기준 global을 지칭하는 것을 확인할 수 있다.

  • this의 값은 호출이 되는 시점에 계산이 이뤄진다.



🔶this의 값을 묶어주는 bind.

const myProfile = {
  codeName: "h662",
  age: 18,
  whatIsThis: function () {
    console.log(this);
  },
};

myProfile.whatIsThis();

let abc = myProfile.whatIsThis;

let bindedAbc = abc.bind(myProfile);

bindedAbc();
  • bind의 키워드를 사용하면 this의 값을 묶어줄 수 있다.

  • abc의 this를 myProfile객체로 묶어주겠다 라는 뜻이다.



🪷useContext.

♾️ Components와 Props의 문제점.

  • 컴포넌트가 많아지고 컴포넌트 간의 데이터 전달이 많아질수록 데이터 전달이 힘들어지게 된다.

  • useContext를 사용하면 이 문제를 해결할 수 있다.



♾️ useContext 사용.

<App.jsx>


import { useState } from "react";
import Child from "./components/Child";
import ChildFriend from "./components/ChildFriend";
import { createContext } from "react";

export const AppContext = createContext();

function App() {
  const [gift, setGift] = useState(1000000);

  return (
    <AppContext.Provider value={{ gift, setGift }}>
      <div className="bg-red-100 min-h-screen flex justify-center items-center gap-8">
        <Child />
        <ChildFriend />
      </div>
    </AppContext.Provider>
  );
}

export default App;
<Child.jsx (Grand, SuperGrand 동일)>


import GrandChild from "./GrandChild";

const Child = () => {
  return (
    <div>
      <GrandChild />
    </div>
  );
};

export default Child;
<ChildFriend.jsx>


import { useContext } from "react";
import { AppContext } from "../App";

const ChildFriend = () => {
  const { gift } = useContext(AppContext);

  return (
    <div>
      <div>ChildFriend</div>
      {gift}
    </div>
  );
};

export default ChildFriend;
  • useContext를 사용하여 앱의 전체 상태를 관리할 수 있다.

  • App.jsx에서 다른 중간에 있는 많은 컴포넌트를 거치지 않고 마지막 컴포넌트에 원하는 값을 전달할 수 있게 된다.

⁉️ 보다 자세한 내용은 은찬매니저님이 알려주는 내용을 토대로 업데이트 하도록 하겠다.



✅Up&Down Game.

오늘은 Up&Down Game을 만들어보았다. 생각보다 어렵지 않게 이해가 되었는데 막상 TIL을 작성하기 위해서 코드를 보니 또 살짝 막히는? 부분이 없지않아 있다. 코드리뷰를 해보면서 다시 뜯어보는 시간을 가져보도록 하겠다.

🔘 App.jsx

import { useEffect } from "react";
import GameBoard from "./components/GameBoard";
import GameResult from "./components/GameResult";

function App() {
  useEffect(() => {
    let savedPoint = localStorage.getItem("point");

    if (!savedPoint) {
      localStorage.setItem("point", 0);
    }
  }, []);
  //savedPoint가 없는경우 강제적으로 0점을 부여한다.

  return (
    <div className="flex flex-col justify-center items-center min-h-screen">
      <GameResult />
      <GameBoard />
    </div>
  );
}

export default App;
  • 코드 상단에 useEffect와 GameBoard, GameResult 컴포넌트들을 불러오기 위해 import 문을 사용하였다.

  • App 컴포넌트 내용을 보면, useEffect 함수를 사용해서 localStorage에 저장된 point 값을 getItem을 사용해 가져오고, if문을 사용하여 !savedPoint가 없는 경우 localStorage에 point 값으로 0을 저장하는 작업을 진행했다.

    • !savedPoint 앞에 있는 "!" 는 부정의 의미? 아닐때 같은 의미를 가지고 있다.
  • return 문에서는 GameResult와 GameBoard 컴포넌트를 렌더링해 화면에 보여주는 역할을 부여했다.




🔘 GameResult.

const GameResult = () => {
  const point = localStorage.getItem("point") | 0;

  return (
    <div className="bg-green-300 w-full grow flex flex-col justify-end items-center text-white pb-8 shadow-green-300 shadow-lg">
      <div className="text-8xl font-black">Up & Down</div>
      <div className="text-2xl">현재 점수 : {point}</div>
    </div>
  );
};

export default GameResult;
  • GameResult 컴포넌트 내부 localStorage에서 point 값을 가져오고, 만약 값이 존재하지 않는 경우 0이 적용되도록 했다.

  • localStorage.getItem은 point 키에 해당하는 값을 반환하고, | 0; 연산자는 값이 존재하지 않는 경우 0으로 반환되도록 코드를 짰다.

  • return 문에서는 GameResult 부분에 대한 css적인 요소를 작성하였고, 특히 Up&Down과 현재점수 부분이 text 요소를 적용하여 화면에 출력되도록 하였다.

    • 여기서 현재 점수는 useState를 적용하여 {point}값을 가져오도록 하였다.




🔘 GameBoard.

import { useEffect, useState } from "react";

const GameBoard = () => {
  const [point, setPoint] = useState(5);
  //5점으로 점수 기준을 만든 코드
  const [randomNum, setRandomNum] = useState(Math.floor(Math.random() * 100));
  // (Math.floor(Math.random() * 100)) - 0이랑 100사이에 난수를 발생시킴. (랜덤함수사용)
  const [choiceNum, setChoiceNum] = useState("");
  //초기값 등록.
  const [hint, setHint] = useState("0 ~ 100 사이의 숫자를 맞춰보세요!");
  const onChangeChoice = (e) => {
    setChoiceNum(e.target.value);
  };
  // setChoiceNum(e.target.value) 이 코드로 고정되어있던 value={choiceNum} 을 변경할 수 있도록 만든다.
  const onClickCheck = () => {
    let checkNum = parseInt(choiceNum);

    if (isNaN(checkNum)) {
      setHint("숫자를 입력해주세요!");
      return;
    }
    if (0 > checkNum || checkNum >= 100) {
      setHint("숫자를 잘못 입력하셨습니다.");
      return;
    }
    // if을 이용하여 값을 비교하는데 사용.

    if (randomNum === checkNum) {
      setHint("정답입니다!!! 랜덤값을 초기화 합니다.");
      //정답 코드.
      if (point > 0) {
        let savedPoint = localStorage.getItem("point");
        // 기존 점수 불러오는 코드
        localStorage.setItem("point", parseInt(savedPoint) + point);
        //저장하는 코드
      }
      setRandomNum(Math.floor(Math.random() * 100));
      // 정답을 맞추고 나면 랜덤결과 값을 초기화 하는 코드
      setChoiceNum("");
      // 정답을 맞추고 나면 입력창을 초기화 하는 코드.
      setPoint(5);
      // 점수를 주고나서 초기화.
    } else if (randomNum > choiceNum) {
      setHint(`정답은 ${choiceNum}보다 높은 숫자입니다.`);
      setPoint(point - 1);
    } else if (randomNum < choiceNum) {
      setHint(`정답은 ${choiceNum}보다 낮은 숫자입니다.`);
      setPoint(point - 1);
    }
  };
  //1.문자입력 2.0~100이외의 숫자는 오류를 일으킬 수 있다.

  useEffect(() => console.log(`랜덤 숫자는 ${randomNum}입니다.`), [randomNum]);
  //[randomNum] - 추적할 값. / (`랜덤 숫자는 ${randomNum}입니다.`) - 추적해낸 값.

  useEffect(
    () => console.log(`당신이 선택한 숫자는 ${choiceNum}입니다.`),
    [choiceNum]
  );
  useEffect(() => console.log(`현재포인트 ${point}`), [point]);
  return (
    <div className=" w-full grow flex flex-col justify-center items-center">
      <div className="mb-4 text-xl font-bold">{hint}</div>
      <div>
        <input
          className="border-2 rounded-lg px-4 py-2 focus:outline-yellow-300 shadow-lg"
          type="text"
          value={choiceNum}
          onChange={onChangeChoice}
        />
        <button
          onClick={onClickCheck}
          className="px-4 py-2 ml-2 rounded-lg border-2 border-yellow-300 text-yellow-300 shadow-lg"

          확인
        </button>
      </div>
    </div>
  );
};

export default GameBoard;
  • 4가지의 useState 변수를 선언하였다. useState() 함수를 사용하여 각각 point, randomNum, choiceNum, hint 변수를 초기화하고 관리하도록 정리했다.

    • 먼저 const [point, setPoint] = useState(5);를 사용해 5점을 점수기준으로 하도록 설정했다.

    • const [randomNum, setRandomNum] = useState(Math.floor(Math.random() * 100)); 를 작성하여 랜덤함수를 사용해 0~100까지의 랜덤숫자가 나오도록 설정했다.

    • const [choiceNum, setChoiceNum] = useState(""); 를 작성하여 초기값이 나오도록 설정하였다.

    • const [hint, setHint] = useState("0 ~ 100 사이의 숫자를 맞춰보세요!"); 를 작성해 hint가 나오도록 설정했다.

❓ 개인적으로 이 코드가 잘 이해가 않갔는데 지선매니저님 덕분에 어느정도 과정을 생각할 수 있게 됬다.

const onChangeChoice = (e) => {
    setChoiceNum(e.target.value);
  };
  • onChangeChoice 함수는 입력창(input)의 값이 변경될 때마다 호출되는 함수.

  • setChoiceNum 함수를 호출하여 choiceNum 상태 값을 변경.

    • choiceNum은 사용자가 선택한 숫자를 담는 변수이고, 그 변수가 변할 때를 감지하는게 setChoiceNum이다!

    • (e) => {...} 이 부분에서 e는 이벤트를 뜻하고 html(화면)에서 이벤트를 받는다.(내 생각에 이벤트를 받는다는건 입력하는 값을 받는것을 뜻하지 않을까 생각한다.)

    • e.target.value는 html "input"태그에서 사용자가 입력한 이벤트에서 값을 받아온다.(위에 내가 생각했던 내용이 이말인것 같다.)

    • 즉, onChangeChoice 함수는 html "input"태그에서 onChange 안에 들어가서 사용자 입력을 감지해 값을 받아서 choiceNum변수에 넣어준다!



  • onClickCheck 함수는 확인 버튼을 클릭했을 때 호출되는 함수다. choiceNum의 값을 가져와 정수로 변환한 후 checkNum 변수에 저장한다.

    • choiceNum의 값을 가져와 정수로 변환한 checkNum 변수에 if문 그리고 isNaN 함수를 사용하여 checkNum이 NaN인지 검사한다. 그리고 숫자가 아닌 문자를 입력하여 NaN이 나오면 "숫자를 입력해주세요!" 메시지를 출력한다.

    • 또한, 0보다 작거나 100보다 큰 경우 "숫자를 잘못 입력하셨습니다." 메시지를 출력하도록 if문 코드를 작성했다.

    • 그다음 if문은 랜덤값과 선택한 값이 같을때 정답메세지를 송출하는 코드이다.

    • 그 아래로 기존 점수를 localStorage에서 불러오는 코드와 포인트를 저장하는 코드를 작성하였고, 정답을 맞추면 게임의 랜덤결과값이 초기화되고 다른 값이 나오도록 설정하는 코드를 작성했다.

    • 또한 정답을 맞추고나면 입력창을 초기화 하는 코드와 점수 5점을 부여하고 초기화되는 코드를 작성하였다.

  • else if문을 사용하여 만약 랜덤값이 선택한 값보다 크다면, "정답은 더 높은 숫자입니다"를 출력하도록 하고, 사용자의 점수 1점을 감소하도록 하였다.

    • 마찬가지로 else if문을 사용하여 만약 랜덤값이 선택한 값보다 작다면, "정답은 더 낮은 숫자입니다"를 출력하도록 하고, 사용자의 점수 1점을 감소하도록 하였다.
  • useEffect 훅을 사용하여 각 state 변수의 값이 변경될 때마다 콘솔에 로그를 출력하도록 코드를 짰다.

  • 그리고 return 내부에 있는 내용은 게임 화면에 대한 css적인 부분을 랜더링 하도록 코드가 짜여있다.



🌜 하루를 마치며..

아직까지 코드를 완벽하게 이해하기는 어렵다. 오늘도 강의를 들을때 up&down을 만들때는 이해가 어느정도 된다고 생각했는데 막상 저녁에 TIL을 작성하려고 하니 모르는 부분들이 생겨났다. 아직도 이해하기 어려운 부분들이 많아서 나중에 제대로 사용하지 못할까봐 걱정이 크다. 그렇지만 이 또한 시간이 지나면 조금씩 나아지지 않을까 안일하게 생각해보려고 한다.(내 성격과는 맞지 않지만..)

그리고 4pm부터 6pm까지 아기사자 스터디를 시작했다. 나는 초록팀에 배정되었고, 은찬매니저님이 멘토로 들어오시게 되어서 기분이 너무 좋았다. 아무래도 대화를 가장 많이해본분이라 낮가림이 있는 나로선 너무 편하고 좋았다. 팀원들 중에는 아는분들도 많았고 모르는분들도 계셨는데 다들 잘지내면서 스터디를 좋은 방향으로 적용할 수 있으면 좋겠다.

강사님이 과제도 주셨는데 물론 나도 과제를 하면서 여러가지 기능도 적용해보고 하고 싶지만 그건 어느정도 수업내용을 아는자들만의 특권이라고 생각한다. 수업을 이해하려고 노력해야하는 나같은 사람 입장에서는 오늘 수업에서 들은 내용에 대해 복습하고 정리하기에도 시간이 너무 부족하다. 그렇다고 늦게까지 더 하기엔 다음날 수업에도 지장을 줄 수 있어 여의치 않다. 다행히 이번주는 금요일이 휴강이라 조금 더 뭔가를 해볼 수 있겠지만 언제나 완벽한 계획은 없으니 차근차근 내가 할수 있는걸 해보려고 한다.

내일도 잘 버텨보자!

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

0개의 댓글