LocalStorage : 브라우저를 껐다켜도 안의 데이터가 남아있다. (직접 지워줘야한다)
SessionStorage : 브라우저를 껐다키면 안에 저장된것 사라진다.(단, 새로고침시에는 변수만 사라질뿐, 안에 저장된것이 사라지지는 않는다)
Cookie : 로컬스토리지와 비슷한데 여기에 저장된 데이터는 request보낼때마다 같이 자동으로 보내진다.
따라서 서버(벡엔드 의미)와 데이터를 주고받는 용도로 사용된다.
먼저 어제 정리해보았던 로컬스토리지, 세션스토리지, 쿠키의 각각의 특징들을 정리해보았다
그런데 우리가 accessToken은 보안이 중요하기때문에 변수에 저장된다.
따라서 새로고침시(주소치고 엔터를 쳐 다시 접속하는것과 같음) 다시 HTML, CSS, JS를 다시 받아오기에 즉, 다시 실행되기에 글로벌스테이트(변수)에 저장된 내용들이 다 사라진다는 의미다.
변수는 JS에서 만든것이라 새로고침하면 JS도 새것으로 받아오니 따라서 모든 데이터가 사라지는 것이다.
그러면 어디에 저장해야 로그인 사용자가 유지될까?
두가지 방법이 있다
- 로컬스토리지나 세셩스토리지, 쿠키 등에 다시 저장한다.
- 새로고침시마다 accessToken 새로 받아온다.
일반적으로 실제 실무(회사)에서는 둘 다 사용하고 있다고한다.
다만, 무엇을 중요시 하느냐에 따라 달려있다.
보안을 중시하면 새로고침때마다 새로 받아오는 방식을, 빠르게 하는 방식을 선택하면 로컬스토리지나 세션스토리지를 사용한다.
그러면 accessToken을 새롭게 받기 위해서는?
- 로그인을 다시해 새 토큰을 받아온다.
- accessToken을 재발급받는 방법 사용(만료시간에 따라 로그인 다시 안해도 받을 수 있게 한다. refreshToken을 사용한다)
2번방법을 사용한다면 새로고침시에도 refreshToken을 사용해 accessToken을 재발급받고, 토큰 만료시간이 지났을때도 refreshToken을 사용해 accessToken을 재발급받을 수 있다.
일단 오늘은 refreshToken에 대해서는 아직 수업하지 않았고, 임시로 localStorage에 저장해보았다.
기존방식
=> 로그인으로 이메일(아이디)와 비밀번호를 전송하면 ==> 벡엔드에서는 accessToken을 받아오고 (이 과정을 인증
이라고 함) result라는 변수에 저장한다. ==> 그리고 로그인한 사용자에대한 정보 등을 보기위해 API요청을 할때 이 토큰이 없으면 에러메세지가, 이 토큰이 있으면 setAccessToken이라는 RecoilState아는 글로벌 스테이트에 저장한다.
로컬스토리지에 저장하기.
localStorage.setItem("key","value") ==> 저장하기
localStorage.getItem("key") ==> 가져오기
localStorage.setItem("키명아무거나",accessToken)
을 기존거에 추가해줬다. 뒤에 accessToken은 받아온 결과를 저장해놓은 결과 즉 토큰을 저장해놓은 변수다
const accessToken = result.data?.loginUser.accessToken
새로고침시 로컬스토리지에 accessToken이 있으면 넣어주는 코드 만들기
리렌더: function 부분만 재실행됨.
새로고침: 아예 새로운 CSS,HTML,JS 를 받아노는 것이기에 전부 재실행된다.
기존에 따로 빼놓은 아폴로세팅에 const result = localStorage.getItem("accessToken")
즉, 로컬스토리지에 accessToken이라는 키를 가진 (토큰을 값으로 가지는 키) 것이 있으면 다시 RecoilState에 담아준다.
const result = localStorage.getItem("accessToken");
if (result) setAccessToken(result);
result 부분에 undefined 가 들어올 수도 있기에(다시말해 로컬스토리지에 없을수도 있기에) 앞에 조건을 붙여주었다
그대로 실행하게되면 localStorage is not defined가 뜨게된다.
분명 브라우저 console창에서는 잘 실행되었고, 버튼 테그를 눌렸을때 클릭함수에
localStorage.getItem("accessToken");
얘를 넣어주면 잘 실행된다.
이유가뭘까?
이것을 알기위해서는 next.js의 랜더링 원리에대해 알아야했다.
브라우저에서 홈페이지 접속시 HTML받아오기 전단계에서 프론트엔드 서버(yarn dev실행하는 프로그램) 에서 해당 HTML코드를 그려본다. 그 내용을 가지고 HTML을 보내고 ==> 브라우저에서 다시 실행되며 두개합쳐 최적화한 결과가 최종적으로 보여지게된다.
브라우저에서 그린 내용과 프론트엔드 서버에서 그려본(프리랜더링한)것을 비교해 차이를 보는 과정 ====> diffing(딥핑)
최종적으로 완벽하게 그려냄 ===> hydration(하이드레이션)
그런데! 서버에서 랜더린해보는 것에는 윈도우라는 개념이 없다.
무슨말인가 하면은,
윈도우 안의 기능(브라우저의 기능)은 실행되지 않는다는 것이다.
윈도우 안의 기능을 대표적으로 몇가지 꼽아보자면,
alert() == window.alert()
confirm() === window.confirm()
그리고 앞서 했던
localStorage.getItem(),== "
localStorage.setItem() == "
정리하자면 윈도우(브라우저)기능이기에 실행되지않는 프리랜더링에서 발생하는 문제 때문에 그러한 오류가 발생한것이었다.
그럼 앞서 말했던 클릭등으로 실행되는 경우는?
이벤트가 발생되어야 실행되니 애초부터 프리랜더링시가 아니라 브라우저에서 실행되는 것이라고 볼 수 있다.
따라서 브라우저에서 실행되는 코드인지, 프론트엔드서버에서 실행되는 코드인지 구분해줄 필요가 있었다.
process.browser 사용
if (process.browser) { console.log("지금은 브라우저"); const result = localStorage.getItem("accessToken"); if (result) setAccessToken(result); } else { console.log("지금은 프론트엔드 서버(yarn dev로 실행시킨 프로그램 내부다)"); }
if문을 사용해 브라우저일 경우에만 localStorage가 실행되게 한다.
typeof window 사용
if (typeof window !== "undefined") { console.log("지금은 브라우저"); const result = localStorage.getItem("accessToken"); if (result) setAccessToken(result); } else { console.log("지금은 프론트엔드 서버(yarn dev로 실행시킨 프로그램 내부다)"); }
useEffect 사용
useEffect(() => { console.log("지금은 브라우저"); const result = localStorage.getItem("accessToken"); if (result) setAccessToken(result); }, []);
useEffect는 돔이 만들어지고 나서 실행되어 서버에서는 실행이 안된다. 다시말해 브라우저가 그려진 후 마지막에 실행된다. 즉, 프리랜더링은 무시되고 브라우저에서만 사용된다는것이다.
그렇다면 가장 간편해보이는데 useEffect하나만 알고 사용하면 되지 않나?
다 알고있어야하는 이유는?
next.js의 코드를 이해하기 위해서 다른 사람의 코드를 보고 적용하기 위해서 알고있어야한다.
말그대로 권한에따라 나누는 것.
로그인 성공페이지에 로컬스토리지에 accessToken이 없다면 로그인을 하라고 알림.
useEffect(() => {
if (!localStorage.getItem("accessToken")) {
alert("로그인 후 이용 가능합니다");
void router.push("/login");
}
}, []);
다만 문제되는점! 권한확인하는 부분이 한두군데도 아닌데 이 부분을 모든곳에 다 붙여넣는다?
만일 로그인페이지가 아니라 다른페이지로 보내길 원한다면? 일일이 붙여준곳에 가서 고쳐야한다.
해결방법 두가지
- 공통로직을 Higher Order Component =HOC 로 만들어 사용하기. HOC는 먼저 실행되는 컴포넌트를 의미한다.
- Custom hooks 를 만들어 사용하기 = 커스텀훅은 내가 만든 훅을 말한다.
보통 1번의 경우는 클래스형에서 주로 사용하나 함수형에서도 사용가능하다.
2번의 경우가 함수형에서 사용하는 방식이다. 클래스형 컴포넌트에선 hooks가 안되어 HOC를 사용하는 것이다.
실습:
먼저 HOC를 적용해본다.
HOC를 커스텀훅으로 바꿀수 있어야하기에 먼저 수업하였다.
HOC를 하기전, 알아야할 사전지식이 두가지 있다.
먼저 cluser라는 것이다.
브라우저-검사에서 Source 부분을 확인해 어느 한부분을 선택하고 새로고침을 하면 breakpoint가 걸린다. 그리고 그부분부터 실행과정을 볼 수 있다.
call Stack==> 요청한 스택: 실행된 함수단위로 아래부터 쌓인다.
stack: 마지막에 들어온게 먼저나간다.(먼저들어온게 나중에 나간다)==>Last In First Out(LIFO 구조)
queue: 먼저 들어온게 먼저나간다. = First In First Out(FIFO 구조)
호이스팅: 선언전에 먼저 만들어진다. 안에것 변수 불러오기등이 가능하기도 하다.
주의:
const와 let은 호이스팅은 되나 안에 접근이 불가하다.
호이스팅시 undefind 가 나오지만 콘솔로 찍히지는 않는다. 접근 안되는 구간을 TDZ리고한다.
function, var은 호이스팅도 되고, 접근도 가능하다. 즉, 콘솔창에 찍힌다.
function aaa(){
const apple = 10
function bbb(){
const banana = 20
console.log(banana)
console.log(apple)
console.log(qqq)
}
const qqq = 3
bbb()
}
aaa()
함수 bbb안에서 보면 자신의 함수 안이 로컬스코프, 그 밖을 Closure스코프 라고 볼 수 잇다. 즉 위에있는 함수를 가져오는것을 의미한다.따라서 bbb안에서 qqq를 호출되도 실행이 가능하다
정리:
스코프 체인이란, 위에서부터 아래로 흐른다.
local부분부터 Closure부분으로 흘러내려간다.
먼저 local에서 찾다가 없다면 closure부분에서 찾고, 여기에도 없다면 global까지가서 찾는 방식이다.
사전지식2
함수를 리턴하는 함수
1. 함수를 리턴하는 함수
function aaa(){
const apple = 10
return function bbb(){
const banana = 20
console.log(banana)
console.log(apple)
}
}
aaa()
ƒ bbb(){
const banana = 20
console.log(banana)
console.log(apple)
}
aaa()()
2. 함수를 리턴하는 함수 - 인자넣어주기
function aaa(apple){
return function 이름은상관없음(banana){
console.log(banana)
console.log(apple)
}
}
aaa(10)(20)
3. 함수를 리턴하는 함수 - 화살표 함수로 바꾸기
const bbb = (apple) => (banana) => {
console.log(banana);
console.log(apple);
};
bbb(10)(20);
4. 함수를 리턴하는 함수 - 3개
const bbb = (apple) => (banana) => (tomato) => {
console.log(banana);
console.log(apple);
console.log(tomato);
};
bbb(10)(20)(30);
각각의 안쪽의 함수가 실행되기전 밖의 함수가 실행되는데, 안쪽 함수의 기준으로 바깥쪽함수를 HOF즉, Higher Order Function 이라고 한다.
실행방법은 먼저 바깥의 함수를 실행하고 리턴으로 받아오는 함수를실행하는 식이다.
만약 가장 바깥의 함수이름을 aaa라고 한다면
aaa()로 함수 실행, 거기에 리턴함수를 붙인 모양이 aaa()()이다.
aaa()() 여기 소괄호에 값을 넣으면 걔가 인자가되어 실제 함수의 매개변수에 들어간다.
용어정리:
의존성배열=dependency array
디버깅:
HTML과 CSS는 Element에서,
JS는 console에서,
통신은 네트워크에서
Source에서는 프로그램이 실행중인 소스를 보며어떤위치에서 어떻게 진행되는지 검증이 가능하다.
이런것들을 활용해 오류등을 확인하는 과정을 디버깅 이라고 한다
scop: 변수의 scop의미.
Global : 전역변수. 어디에서나 접근 가능
Local: 지역변수. 그 함수 안에서만 접근 가능