본문 바로가기
리액트 공부와 함께 하는 일상/5주차

[TIL] 3. 권한분기와 관리자 / 클로저 / HOC & HOF

by fefe94 2022. 2. 15.

 

Today I Learned

  1. Next.js 렌더링에 이런 원리가 있다니! - Diffing/Hydration
  2. 잠깐! 이거 먼저 실행해줘! - Closure / HOC / HOF

 

 

새로고침해도
로그인한걸 어떻게 유지시킬까요?

 

지난 번 로그인의 역사 게시글에서

토큰을 발급하여 로그인 정보를 디비 접근 없이

redius를 활용하여 백엔드에서

바로 조회 가능한 방법이 있다고 했습니다.

 

이방식은 브라우저에 accessToken을 저장하기 때문에

브라우저를 새로고침하거나 페이지를 이동했을 경우

로그인 정보가 날아가게 됩니다.

(화면이 새로 그려지며 초기화 되기 때문입니다.)

 

이를 해결하려면 브라우저 저장소를 이용하면 됩니다.

(오늘 알아볼 것은 로그인 관련 내용을 임시로 저장하는 방식입니다.)

 

브라우저 저장소

브라우저 저장소는 

cookie,

localStorage (영구저장 - 데이터 안날아감),

sessionStorage (브라우저 종료 시 데이터 소멸)

이렇게 3가지가 있습니다.

 

localStorage와 sessionStorage는

자바스크립트 api가 완전히 동일한 형태입니다.

 

이 둘의 차이는

sessionStorage는 같은 사이트라도 여러개의 창이나 탭을 열면

각각 여러개의 sessionStorage로 분리되어

각 창, 탭을 닫으면 데이터가 소멸하는 반면

localStorage는 창을 닫아도 데이터가 그대로 남습니다.

( sessionStorage 는 세션이 끝나면 지워지지만

localStorage는 세션이 끝나도 데이터가 지워지지 않습니다.)

 

여기서는

보안상 좋지는 않지만 원활한 개발을 위해 임시로

localStorage에 저장시키는 방법에 대해 알아보겠습니다.

 

localStorage

브라우저 저장소는 기본적으로

키, 값의 쌍을 이루는 데이터를 저장합니다.

(해시 테이블의 자료구조를 생각하면 됩니다.)

 

로컬 스토어에 값을 넣고 불러오는 메서드는 다음과 같습니다.

 

localStorage.setItem( 키 , 값 ); // 로컬스토리지에 값 넣어줄 때
localStorage.getItem( 키 ); // 로컬스토리지에서 값 불러올 때

 

참고로 로컬스토리지는

오직 문자열 데이터 타입만 지원합니다.

 

다른 타입의 데이터를 넣는다면

자동으로 문자열로 변환해줍니다.

(String(1)이라고 저장하는 것과 동일한 격입니다.)

 

객체 형태의 데이터를 localStorage에 저장하고 불러오려면

 JSON.stringify()과  JSON.parse()를 사용하면 됩니다. 

 

 

 

JSON.stringify(JSON형식의 객체

: 객체 => 문자열로 변환 : 직렬화(serialize)

 

 JSON.parse(JSON형식의 문자열

: 문자열 => 객체로 변환 : 역직렬화(deserialize)

 

localStorage.setItem('json', JSON.stringify({a:1,b:2}))
// undefined
JSON.parse(localStorage.getItem('json'))
// {a: 1, b: 2}

 

number 타입도 이를 활용하여 

넣어주고 불러와 줄 수 있습니다.

 

localStorage.setItem('nums',JSON.stringify([1,2,3,4,5]))
// undefined
JSON.parse(localStorage.getItem('nums'))
// (5) [1, 2, 3, 4, 5]

 

 

localStorage에 저장 시켜 놓은 accessToken을

setAccessToken을 사용해 넣어주면

만료시간까지 유지 가능해집니다.

 

localStorage is not defined

 

그런데 이렇게까지만

처리를 해주고 실행시켜 보니

아래와 같은 에러가 발생합니다.

 

 

여기서 Nest.js의 렌더링에 대한 이해가 필요합니다.

 

Next.js 렌더링

 

 

Browser에서 요청이 오면 

frontend서버에서 미리 그려본 후 (Pre-rendering) 

브라우저에 그려준 뒤, 비교(diffing)하고

변경사항을 적용(hydration)해줍니다.

 

이때, frontend서버는 

로컬 저장소가 없기 때문에 저런 에러가 뜨는 것입니다.

(로컬저장소는 브라우저에 있는 저장소입니다.)

 

따라서 조건식을 통해

로컬스토리지에 저장할 때를 

따로 처리해줘야 하며 해당 코드는

총 3가지로 아래와 같습니다.

 

_ app.tsx

// 방법1) 
if (process.browser) {
    // 지금 브라우저에서 실행중이라면
    if (localStorage.getItem("accessToken")) {
      setAccessToken(localStorage.getItem("accessToken") || "");
    }
  }
  // 브라우저라고 하면 윈도우 객체가 항상 있다.

// 방법2)
  if(typeof window !== "undefined"){// 윈도우가 있을 때(브라우저) 실행해줘!
    if (localStorage.getItem("accessToken")) {
      setAccessToken(localStorage.getItem("accessToken") || "");
    }
  } // 이걸로 실행해도 됨
  
// 방법3)  
  useEffect(() => {
    // 위 방식은 다시 그려지기 때문에 좋은 방법이 아니다.
    // useEffect는 서버에서 실행되지 않음.
    if (localStorage.getItem("accessToken")) {
      setAccessToken(localStorage.getItem("accessToken") || "");
    }
  }, []);

 

방법1,2는 화면이 다시 그려지면서

setAccessToken이 반복되므로

한번 그려진뒤 실행되어 안정적인

useEffect를 사용하는 것이 효율적입니다.

(다른 방법들은 다른 분들 코드를 볼 때

알아볼 수 있도록 참고해둡니다.)

 

 

로그인 관련 권한분기

 

권한분기는 큰 그림으로 봤을 때 

사용자 프론트엔드 서버 / 관리자 프론트엔드 서버

구매자 프론트엔드 서버 / 판매자 프론트엔드 서버 / 중개자 프론트엔드 버서

등등 다양하게 존재할 수 있습니다.

 

여기서 다룰 권한분기는

로그인 프로세스 관련

권한 분기로서 사용자 서버 안에서

로그인한 유저 / 로그인 안한 유저를

구분하는 권한 분기를 말합니다.

 

이를 위해

useEffect에서 accessToken이 없으면

login화면으로 이동시켜 주는 식을 이용했습니다.

 

문제는 로그인 권한이 필요한 모든 페이지에

위 로직을 입력해 주어야 한다는 것입니다.

 

이를 위해 HOC를 사용할 수 있습니다.

HOC & HOF

 

HOC는 Higher Order Component,

HOF는 Higher Order Function 이라고 합니다.

이 두 개념은 위의 클로저로부터 확장된 개념입니다.

 

클로저(closure)

 

외부 함수에 접근할 수 있는 내부 함수를 말합니다.
(일반적으로 어떤 함수가
외부에서 선언된 변수에 접근하는 것을 뜻합니다.)

 

return과 함수시작 사이 코드가 없으면 = () => () 식으로 줄여 줄 수 있음

 

클로저 내부로 들어가보면

스택구조임을 알수 있는데

스택은 먼저 들어간 것이

나중에 나오는 First-in/Last-out (FILO) 구조입니다.

 

여기서 스코프 개념도 알 수 있습니다.

스코프 체인이 되는 것인데

local

-> closure(가장 가까운 외부 함수)

-> global

순으로 찾아갑니다.

 

 

(자바스크립트에서의 클로져.

클로져랑 처음 실행할 때

브레이크 포인트 잡는 법에서

콜스택에 이어서 각각 함수마다

부분이 달라지는데 

그안의 스코프라는 창이 달라집니다.

로컬 -> 클로져 -> 글로벌

순으로 스코프가 체인됩니다.

찾고 없으면 다음에서 찾고 없으면

또 그 다음에서 찾는 식입니다.)

 

HOC ( HighOrderComponent )

 

가장 대표적으로 HOC 가 사용되는 부분은

페이지의 권한을 처리할 때 사용게 됩니다.

 

특정 컴포넌트를 실행하기 전에

상위 컴포넌트를 먼저 실행시켜 주는 것입니다.

이렇게 되면 HOC를 하나 만들어 놓고,

로그인이 필요한 컴포넌트 앞에 HOC만 붙여주면

간단하게 권한처리가 끝납니다. 

또한, HOC는 다른 컴포넌트와 함께 실행되므로

with라는 이름을 앞에 붙여줍니다.

(withAuth, withApollo 등이 그 예입니다.)

 

HOF ( HighOrderFunction )

 

HOF 역시 HOC와 비슷합니다.

단지 최종 return 값이 JSX인지, JSX가 아닌지로 구분됩니다.

Component대신 Function이려면 최종으로

return 되는 값이 JSX가 아닌 일반 함수이면 되겠죠?

그동안 특정 버튼을 클릭했을 때, id 값을 넘겨주기 위해

event.target.id를 사용하곤 했습니다.

하지만, 이는 고유한 id를 태그에 입력하게 되므로,

예기치 못하게 id가 중복되어 작성되는 경우에

오작동을 할 가능성이 있습니다.

이러한 이유로 HOF를 사용하게 됩니다.

(또한, HOF를 사용하면 기존에 UI프레임워크를

사용하면서 발생했던 문제(id가 사라지는 문제)도 해결됩니다.

 

HOC와 HOF 차이

Componenet와 Function의 차이입니다.

JSX(React의 HTML)를 return 하면 Component,

그렇지 않으면 Function 입니다.

(JSX는 html과 똑같아 보이지만 엄밀히 말하면 js입니다.)

 

 

.

.

.

(이렇게 편하게 해주는

HOC와 HOF 사용이 가능한 것은

Javascript의 클로저 덕분입니다.)

 

 

 

위 사진의 왼쪽이 _app.tsx, 가운데가 withAuth에 해당한다.

함수에서 함수를 리턴하더라도

최종적으로 hoc같은 모양을 만들 수 있습니다.

hoc를 활용해서 두개 컴포넌트 사이에서

먼저 실행해오는 컴포넌트를 만들 수 있습니다.

 

 

 


Reference

https://curryyou.tistory.com/359 

 

[자바스크립트] JSON형식 직렬화/역직렬화 방법

# JSON(Javascript Object Notation) : 클라이언트와 서버 간의 HTTP 통신을 위한 텍스트 데이터 포맷. : 자바스크립트의 객체처럼 키:값으로 구성되며, 각 객체를 배열로 묶을 수도 있다. : 문자열로된 키와

curryyou.tistory.com

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

 

JSON.stringify() - JavaScript | MDN

JSON.stringify() 메서드는 JavaScript 값이나 객체를 JSON 문자열로 변환합니다. 선택적으로, replacer를 함수로 전달할 경우 변환 전 값을 변형할 수 있고, 배열로 전달할 경우 지정한 속성만 결과에 포함

developer.mozilla.org

 

댓글