TIL : useEffect 자세히 알아보기, Authentication

엉썬·2022년 4월 15일

Needog

목록 보기
6/8

TIL-04-08

Authentication vs. Authorization

사용자 인증을 따로 떼어서 관리하는 폴더를 만드려다가 인증을 영어로 뭐라고 써야할지 헷갈렸다. AuthenticationAuthorization이 일단은 떠올랐는데, 과거에 어느 글에서 둘의 의미가 상이하다는 말을 보아서 다시 한번 찾아보게 되었다.

사용자 인증의 의미로는 Authentication이 사용되는 것 같다.
권한에 대해 (확인된) 부여를 Authorization으로 표현하는 듯 하다.

그러니까 통상적으로는 Authentication이 일어난 뒤에 Authorization이 되는 셈이 아닐까 싶다.

참고


useEffect by Dan Abramov

Dan Abramov가 때때로 리액트 강의를 듣다보면 꼭 나오시는 분이라고는 알고 있었다. 프로젝트를 구현하다가 useEffect에 대해서 조금 미흡하게 이해하는 것 같아 블로그를 뒤지다가 다시 그의 이름을 우연히 발견하게 되었다.

그 블로그의 이름은 조금 웃기게도 overreacted였다. 사실 블로그를 처음 읽을 때는 너무나도 자세한 설명에 놀랐는데 나중에서야 가 썼다는 사실을 알고 새삼 이름이 오르내리는 이유를 알 수 있었다. 리액트 공식 문서에서도 찾기 힘들었던 hook의 자세한 동작 원리라던가 개념적 바탕에 대해서도 너무 친절하게 썼있었다. 차라리 백날 강의를 듣거나 공식 문서를 읽기 전에 이 블로그를 먼저 봤더라면 하는 허무함이 조금 들 정도였다. 심지어 한국어로 번역까지 되어 있었다! 정말 리액트에 대해 과도할 정도로 (그러니까 overreact하게) 적혀 있는 블로그였다.

처음에는 useEffect만 궁금했는데 보다보니 리액트 전반에 걸친 원리에 대한 심화된 내용을 학습할 수 있었다.

아래는 해당 블로그에서 발췌한 내용과 그에 개인적인 사족을 덧붙인 내용이다. 잊혀질만하면 다시 찾아서 종종 들여다 봐야겠다.

// JSX는 아래 오브젝트를 만들기 위한 편의구문입니다.
// <dialog>
//   <button className="blue" />
//   <button className="red" />
// </dialog>
{
  type: 'dialog',
  props: {
    children: [{
      type: 'button',
      props: { className: 'blue' }
    }, {
      type: 'button',
      props: { className: 'red' }
    }]
  }
}
  • 뷰랑 별 다를 게 없구나. JSX가 단지 편의성을 주는 거구나.
    이래서 프론트 하나 하면 다른 거 하기 쉽다고 하는 거구나.

  • 리액트가 DOM을 렌더링 하는 방식이 호스트 객체를 통해서 만드는 것이므로 리렌더링 때 미리 구조를 만들어 놓고 null일 경우 렌더링을 안하는 식으로 되는 거구나.
    그 이후에 다른 DOM이 만들어 지면 그 부분만 고치는 거고 아닌 것은 재활용 하는 셈이고?! 그래서 동적 리스트(map을 사용하는 등)의 경우에는 key값을 통해서 해당 객체인지 아닌지를 판단하는 거였구나
    ㅜㅜ 내가 이걸 먼저 봤더라면… 그래서 index로 key값을 주면 제대로 재사용이 안되는 거구나. Array의 요소들이 변화하면 순서가 변화할 수 있어서 결과적으로 재사용의 효율성에 문제가 있으니까.

컴포넌트로 작성하면 React가 호출 시점을 결정할 수 있게 해 줍니다.

  • 리액트가 메모이제이션을 하는 방식도 항상 궁금했는데 이런 식으로 객체화 시켜서 트리를 관리한다면 그렇게 큰 비용이 들지 않겠구나 싶다. 그리고 메모이제이션이 어떤 식으로 작동하는지 알 수 있었다.

부모 컴포넌트가 setState를 통해 갱신을 예약하면 React는 기본적으로 전체 하위 트리를 재조정합니다. React는 부모의 갱신이 어떤 자식에게 영향을 주는지 알 수 없기 때문에 일관성을 위해 모두 갱신합니다. 굉장히 비용이 클 것 같지만 실제로는 중소형 하위 트리에서는 문제가 되지 않습니다.

트리가 너무 깊게 갱신된다면 React에 하위 트리를 메모이제이션 해서 얕은 props 비교를 통해 이전 렌더링 결과를 재사용할 수 있습니다.

컴포넌트의 setState 호출은 즉시 렌더링을 발생시키지 않습니다. React는 모든 이벤트 핸들러를 실행시킨 다음 모든 변경사항을 한 번에 다시 렌더링 합니다.

  //결과적으로 1
  const [count, setCounter] = useState(0);

  function increment() {
    setCounter(count + 1);
  }

  function handleClick() {
    increment();
    increment();
    increment();
  }
	///결과적으로 3
  const [count, setCounter] = useState(0);

  function increment() {
    setCounter(c => c + 1);
  }

  function handleClick() {
    increment();
    increment();
    increment();
  }
  • hook은 지역에 한정적인 함수이므로( 예를 들면 useState는 지역의 상태를 관리) 컴포넌트 밖에서는 여느 지역인지 특정할 수 없다는 논리적인 이유로 사용이 불가능.

내부적으로 훅은 연결 리스트 로 구현됩니다.

  • 읽어보니까 컴포넌트는 함수라고 생각하는 게 나을 거 같다

명심하셔야 할 점은 여느 특정 랜더링 시 그 안에 있는 count 상수는 시간이 지난다고 바뀌는 것이 아니라는 것입니다. 컴포넌트가 다시 호출되고, 각각의 랜더링마다 격리된 고유의 count 값을 “보는” 것입니다.

ref는 클래스의 인스턴스 영역과 같은 역할 을 수행한다.

  • ref.current로 값을 불러오는 이유가 함수로 각자의 scope를 갖기 때문에 현제가 각기 달라지는데 ref를 통해서 현재를 공유하기 때문인듯!

저는 훅이 자바스크립트 클로저에 아주 많이 의존하고 있다는게 아이러니하다고 생각합니다. 이에 반해 클래스 컴포넌트는 전통적으로 타임아웃에서 잘못된 값을 가져오는 문제에 시달리고 있는데 이 문제는 주로 클로저와 연관이 있지요.

리액트는 브라우저가 페인트 하고 난 뒤에야 이펙트를 실행합니다. 이렇게 하여 대부분의 이펙트가 스크린 업데이트를 가로막지 않기 때문에 앱을 빠르게 만들어줍니다. 마찬가지로 이펙트의 클린업도 미뤄집니다. 이전 이펙트는 새 prop과 함께 리랜더링 되고 난 뒤에 클린업됩니다.

그렇게는 안되겠네요. 리액트는 함수를 호출해보지 않고 함수가 어떤 일을 하는지 알아낼 수 없습니다. (저 코드는 특정한 값을 포함하고 있는 것이 아니라 name prop에 있는 것을 담아왔을 뿐입니다.)
그래서 특정한 이펙트가 불필요하게 다시 실행되는 것을 방지하고 싶다면 의존성 배열을(“deps” 라고 알려진 녀석이죠) useEffect 의 인자로 전달할 수 있는 것입니다.

하지만 의존성 배열이 리액트에게 어떤 랜더링 스코프에서 나온 값 중 이펙트에 쓰이는 것 전부를 알려주는 힌트라고 인식한다면 말이 됩니다. count 를 사용하지만 deps 를 [] 라고 정의하면서 거짓말을 했지요. 이 거짓말 때문에 버그가 터지는 것은 시간 문제입니다!

useEffect와 관련된 프로젝트 이슈

처음에는 dependency array를 아예 생략하고 했더니 계속 useEffect가 실행되는 오류가 있었다. 그래서 dependency에 [setUser]을 추가하니 사라짐. 그런데 굳이 setUser을 추가하지 않아도 괜찮은 것이었다.

stackoverflowoverreact에서 배운 내용을 바탕으로 확인한 결과, 아예 deps 를 추가하지 않는다면 useEffect 는 매번 실행된다고 한다.
deps 인자를 주었고 useEffect 내부에서 참고하는 다른 대상이 없는데도 warning이 발생하는 이유는 eslint가 아직 그걸 판단할 정도의 지능은 없어서 그렇다고 한다.

  const [user, setUser] = useRecoilState(userAtom);

	useEffect(() => {
    async function getUserWithSession() {
      try {
        const { data } = await axios.get("/confirm", { withCredentials: true });
        const { userId, nickname, snsId } = data;
        setUser(() => ({
          userId,
          nickname,
          snsId,
        }));
      } catch (err) {
        console.log(err);
      }
    }
    getUserWithSession();
  }, []);

//여기서 자꾸 dependency를 비웠다는 경고가 발생
//eslint가 충분히 똑똑하지 않아서
// 예방차원에서 생기는 문제라고 함

참조

profile
하던 일부터 끝내자

0개의 댓글