웹개발 (3)

ppparkta·2022년 11월 8일
1

웹개발

목록 보기
4/26

오늘 학습한 내용

  • useEffects
  • cleanup function
  • to do list 제작 (map,filter)
  • coin tracker 제작 (fetch)

effects

state가 변할 때 모든 컴포넌트가 다시 실행되고 모든 코드도 다시 실행된다. 그런데 예를들어 API를 불러오는 등의 작업은 한번만 필요하기 때문에 이 부분이 다시 실행되는 것을 예방하고 싶다. 그럴 때 쓰는 것이 Effects라고 한다.

memo와는 다른 개념인데, memo는 state가 변할 때만 코드를 실행시키고 다른 때에는 코드를 실행시키지 않는 것이다. effects가 필요할 때는 state가 변해도 코드가 실행되지 않았으면 할 때다.

useEffects

import { useEffect } from "react";

React 객체에서 어떤 특성을 사용하고 싶을 때는 이런식으로 받아와야 한다. useEffect라는 특성을 받아왔기 때문에 이제 js코드 내에서 객체를 통해 접근할 필요 없이 바로 사용할 수 있다.

문득 든 생각인데 import가 객체를 받아온다고 알고 있는데 그러면 위의 코드도 구조분해할당인가?

그 다음 코드를 작성할거다.

function App() {
  const [counter, setCounter] = useState(0);
  const onClick = () => {
    setCounter((current) => current + 1);
  };
  console.log("i run all the time");
  const iRunOnlyOnce = () => {
    console.log("i run only once.");
  };
  useEffect(iRunOnlyOnce, []);
  return (
    <div>
      <h1>{counter}</h1>
      <button onClick={onClick}>Click me</button>
    </div>
  );
}

코드를 살펴보면서 설명하겠다. state가 변할 때 리렌더링된다. 그런데 iRunOnlyOnce()는 이름에서도 알 수 있듯 한번만 실행해야 한다. 이럴 때 useEffect()를 사용하면 문제가 해결된다.

useEffect(iRunOnlyOnce, []);

첫번째 인자는 한번만 실행시켰으면 하는 함수, 두번째 인자는 뒤에서 설명하겠다.우선 두번째 인자는 빈 배열을 둔다.

이 코드를 추가하고 콘솔창을 키면 App컴포넌트가 리렌더링 될 때마다 "i run all the time"이라는 문장은 반복되는 반면 iRunOnlyOnce() 는 처음 렌더링 될 때 딱 한번만 실행되고 이후에는 실행되지 않는 것을 알 수 있다.

App 컴포넌트도 결국 ("index.js") 에서 사용되는 자식 컴포넌트이므로 리렌더링 될 때마다(이 상황에서는 버튼을 누를 때마다) 내부에 포함된 모든 코드가 실행되어야 하는데 이런 경우 코드가 한번만 실행된다.

그런데 예를들어 한 컴포넌트에 두개의 state(a,b)가 존재할 때,

a가 변할 때에만 코드가 실행됐으면 하는 경우도 있다.

즉 b가 변할 때에는 코드가 실행되지 않게 하고싶다.

이럴 때 사용하는게 useEffects() 의 두번째 인자다.

useEffect(()=>{
	console.log("search for", keyword);
}, [keyword]);

이런 식으로 비어뒀던 배열 안에 첫번째 함수가 실행될 수 있는 특수한 경우를 넣는 것이다. 앞선 빈 배열의 경우 처음 실행될 때 한번만 코드가 실행되는 이유이기도 하다. 두번째 인자를 Dependencies라고 한다.

dependencies는 react가 지켜봐야 할 state를 의미한다. 이 state들이 변할 때 useEffects는 첫번째 인자로 받은 함수를 실행시킨다.

즉, useEffect를 사용하면 특정 함수가 언제 실행될지 결정할 수 있다는 뜻이다.

useEffects(실행하려는 코드, Dependencies);

cleanup function

컴포넌트가 삭제될 때 뭔가 할 수 있게 해주는 함수.

useEffect에 들어갈 함수 안에 컴포넌트가 삭제될 때 실행시키고 싶은 함수를 리턴하면 됨.

function Hello() {
  function byFn(){
  	console.log("destroyed :(");
  }
  function hiFn(){
  	console.log("created :>");
    return byFn;
  }
  useEffect(hiFn,[]);
  return <h1>Hello</h1>;
}

화살표 함수를 사용하면 더 짧게 작성할 수 있다.

useEffect(function () {
    console.log("hi");
    return function () {
      console.log("bye");
    };
  }, []);

중간점검

여태까지 배운 핵심 내용들을 정리하면 이렇다

  • 리액트를 사용하는 이유 :
    • 최소 단위의 렌더링을 위해
  • useState() :
    • 변수, 변수를 제어하는 함수로 구성되며 변하는 값을 제어, 해당 부분의 리렌더링을 함
  • props :
    • 태그의 속성 값을 함수의 아규먼트 처럼 컴포넌트에 값을 전달해준다.
  • useEffect() :
    • 코드의 실행 시점을 관리할 수 있는 선택권을 얻는 방어막 같은 존재, 디펜던시가 없을 경우 최초 1회 실행, 있을 경우 해당 값이 변할 경우 실행한다. 이 때 디펜던시는 여러개 입력이 가능하다.

실습 to do list 만들기

state 만들면 직접 수정이 불가능하다. to do list만들려면 기본적으로 목록을 담을 배열이 필요한데, ustState에 배열을 사용할 때 어떻게 작업해야 하는지 정리함.

const [toDos, setTodos]=useState([]);
const onSubmit = (event) => {
    event.preventDefault();
    if (toDo === "") return;
    setTodos((current) => [...current, toDo]);
    setTodo("");
  };

바닐라 자바스크립트에서 배열에 값을 추가하기 위해 .push()라는 속성을 사용했다. 그러나 state는 직접 수정이 불가능하므로 새로운 배열을 만들어서 setTodos를 이용해 값을 리턴하는 과정이 필요하다.

함수의 인자를 배열에 추가하여 새로운 함수로 만들려면 배열 안에 인자를 넣고 , ...배열명 을 붙이면 된다.

setTodos((current) => [...current, toDo]);

이 과정에만 유의하면 된다. setState의 매개변수는 변수도 들어갈 수 있고 함수도 들어갈 수 있는데 함수도 결국은 어떤 값을 리턴하여 변수를 반환하기 때문에 어떤 형식이든 새롭게 state값을 설정할 수 있는 변수를 리턴하면 된다.

map

배열에 to do list가 추가되었다면, 배열을 화면에 출력하는 작업도 필요하다. 이건 jsx와 map을 이용하면 간단하게 구현할 수 있다.

map함수는 배열의 모든 항목에 임의의 함수를 적용하는 메소드이다.

  • map의 첫번째 인자: 현재 배열 인덱스의 값
  • map의 두번째 인자: 현재 배열의 인덱스

단순히 모든 배열을 출력하기 위해 이렇게 작성할 수 있다.

{toDos.map((item)=>{
	<li>
    	{item}
  	</li>
})}

새로 만들 배열 안에 아예 jsx의 태그와 변수를 담는다. 이러면 별도로 페인트 함수를 만들지 않아도 된다. 다만 li태그 특성때문에 오류가 날 수 있어서 다음과 같이 수정한다.

{toDos.map((item, index)=>{
	<li key={index}>
    	{item}
  	</li>
})}

지금 to do list는 항목을 추가하는 기능만 구현됐지만 항목을 삭제하는 기능도 구현하고 싶다. localStorage 기반으로 만든게 아니므로

  • 리스트 생성 시 삭제 버튼 추가
  • 삭제 버튼 클릭 시 배열에서 항목 제거
  • 리렌더링

이런 과정으로 만들고 싶었다. 강의에 달린 댓글들 참고하여 삭제 기능도 구현했다.

const onClickDelete=(index)=>{
	setTodos(toDos.filter((item, todosIndex) => index !== todosIndex));
};
{toDos.map((item, index)=>{
	<li key={index}>
    	{item}
    <button onClick={()=>{onClickDelete(index)}}>Delete</button>
  	</li>
})}

filter

오랜만에 등장한 filter! 자바스크립트 공부한 첫 주에 알게된 함수다.

filter함수는 어떤 배열의 각 항목마다 boolean함수를 적용해서 true일 때는 새로 만들어질 배열에 추가하고 false일 때제외하는 함수이다.

인자는 map과 같이 첫번째 인자는 현재 인덱스의 값, 두번째 인자는 현재 인덱스를 의미한다.

투두리스트 삭제 기능에서 사용한 filter는 삭제하고자 하는 배열의 인덱스와 모든 배열의 인덱스를 비교하여 다르면 true, 같으면 false를 반환하게 했다.

날것의 페이지 완성ㅋㅋㅋㅋㅋ!


실습 coin tracker 만들기

이번에는 coinpaprika API 사용해서 실시간으로 코인 정보를 받아오는 웹페이지를 만들것이다. 지난주 바닐라 자바스크립트를 학습하며 fetch에 대해 전혀 이해하지 못했기 때문에 오늘은 fetch가 무엇인지, 어떻게 사용하는 것인지, 실습할 때 쓴 코드는 어떤 원리로 작동되는 것인지 이해하며 학습하는 것이 목표이다.

fetch

어렴풋이 이 함수는 외부 API를 주소 형태로 받아오는 함수라고 생각하고 있었다. 개념을 찾아보니 다음과 같았다.

fetch함수는 원격 API를 간편하게 호출할 수 있도록 브라우저에서 제공하는 함수이다.

이 함수의 첫번째 인자는 URL, 두번째 인자는 옵션 객체를 입력받고 Promise 타입의 객체를 반환한다. Promise 객체는 이 글을 보고 이해했다.

  • promise : 비동기 코드가 끝나면 반환된다. 비동기 처리의 상태와 데이터를 담게 되어 있다.
  • .then : 비동기 처리가 끝난 다음에 처리할 일을 정의할 수 있다.
  useEffect(() => {
    fetch("https://api.coinpaprika.com/v1/tickers")
      .then((response) => response.json());
  }, []);

위의 코드는 실습하며 작성한 코드인데, promise라는 비동기코드가 끝나고 반환되는 객체를 리턴하기 때문에 .then을 사용해서 다음에 할 일을 지정해 주는 것이라고 이해했다.

받아온 response.json()을 처리할건데, 배열을 담는 state를 만들어서 이 json파일을 저장했다.

이렇게 받아온 json 객체를 화면에 출력하기 위해 다시 map 함수를 사용했는데, 코드는 다음과 같다.

  useEffect(() => {
    fetch("https://api.coinpaprika.com/v1/tickers")
      .then((response) => response.json())
      .then((json) => {
        setCoins(json);
      });
  }, []);
<select>
	{coins.map((coin, index) => {
		return (
        	<option key={index}>
            {coin.name}({coin.symbol}): {coin.quotes.USD.price.toFixed(2)}
            USD
            </option>
		);
	})}
</select>

나중에 코인 종류를 선택하면 내가 입력한 돈으로 해당 코인을 몇개까지 구매 가능한지 알아보기 위해서 select태그를 선택했다.

영상을 보고 따라한건 여기까지고 이 다음부터는 선택한 코인을 내가 가진 돈과 비교해 최대 몇개의 코인을 가질 수 있는지 출력하였다.

아래의 money는 내가 가진 돈, need는 코인의 가격이다. 각각 state를 만들고 관련 태그에 붙일 이벤트리스너 함수를 작성했다.

  const [money, setMoney] = useState(0);
  const [need, setNeed] = useState(0);
  const onChange = (event) => {
    setMoney(event.target.value);
  };
  const selectCoin = (event) => {
    setNeed(event.target.value);
  };

select태그에 onChange함수를 넣었는데 값이 선택되지 않는 문제가 있었다.

  const selectCoin = (event) => {
    setNeed(event.target.value);
  };

🙄문제발생

처음 코드를 작성할 때 이벤트 타겟의 밸류에 접근하는데 내가 만든 태그에는 value가 설정돼 있지 않았다. 그래서 내가 원하는 값이 아니라 undefined가 뜨는 문제가 있었다.

뒤늦게 undefined를 발견하고 왜 이렇게 뜰까 고민하다가 태그 속성을 발견했고, <option key={index} value={coin.quotes.USD.price}> select-option 태그 속성을 다음과 같이 변경했다. 문제 해결! >_<

  • 코인 선택 시 need 세팅
  • input에 돈 입력 시 money 세팅
  • 입력받은 needmoney 나눈 값 화면에 출력

체크리스트 모두 구현하고 몇가지 디테일도 손봐서 파일을 완성했다. App컴포넌트에 쓰면 이전에 만든 to do list를 지워야 해서 그냥 새로운 파일을 만들고 App컴포넌트 안에 붙여줬다.

날것의 22222
3000달러 있으면 2.4이더리움 가능하다고 한다.

ㅋㅋㅋ;

profile
겉촉속촉

0개의 댓글