새로고침시 state가 사라지게된다.(비워져 초기 상태로 돌아감ㅁ-> html,css,js등을 다시그리게되기때문)(즉, state도 변수나 마찬가지인데, 새로고침하면 새로운 html을 받아와 그리기에 기존것이 날라가는것.
따라서 다시 만들어야하는데(refreshToken), 그 전까지는 임시로로컬 스토리지에 저장해보자.
(이 방법은 실무에서 사용하기에는 좋지 않다. 보안문제가 발생할 수 있는데, 이후에 수업이 진행될것이다.)
localStorage에 accessToken담기.
세션 -> 껐다키면 사라지게에 일단 로컬에....
+
nextjs 원리.
localStorage.setItem으로 로컬스토리지에 저장함.
그러나 저장만 한것이니 새로고침시에는 사라짐.
따라서 이것을 다시 recoil에 저장!!
저장한것을 다시 Athorization에 넣고 보내기
localStorage.getItem으로 꺼내오기
_app.tsx에 가서
const result = localStorage.getItem("accessToken"); console.log(result); setAccessToken(result ?? "");
이렇게 로컬스토리지에 저장된것을 꺼내 다시 recoil에(글로벌 state에)담는다.
그런데!
라는 오류가뜬다.
그런데 함수안에 넣고...=> 버튼을 클릭햇을때 console.log가 찍히게하면 문제 없이 찍힌다.
프론트 서버를 yarn dev로 키고, 브라우저에서 주소를 치면 이 서버에 접속하여 html,css,js를 받아오고, 이후 브라우저에서는 벡엔드에 쿼리, 뮤테이션을 날리고, 벡엔드는 api요청을 보낸것을 받아오는것인데...
이때 브라우저에서는 html을 먼저 받아온다.
(우리가 작성하는 코드들은 자체적으로 html로 바뀌어 실행되게됨(자동으로 next나 react가 만들어줌))
그다음 이 html을 실행하며 script태그를 만나게되면 다시 서버로 가서 다운받아오고,css, js도 받아온다.(css 가 없다면 emotion에서 inline형태로(태그안에 style적는 방식)바꿔주게된다.)
이 과정에서...
미리= 프리
이렇게 프리랜더링을 통해 html코드를 만들어주게되고, 이 코드를 브라우저로 전달해준다.
브라우저는 그것을 다운받아와 그리는데, (미리그려놓은것을화면에 먼저 그림 ->> 빠르게 그림만이라도 먼저 보여줌.)
이후에 js를 요청하고 받아온다.
그다음 이 js의 내용을 가지고 서버에서 받은내용과 다른지 보고, js를 통해 html을 다시그리며 js의 기능들을(onClick등) 적용시킨다.(js를 통해 html업데이트)
==> 이 과정에서 차이점을 비교한다.
1. 일단 껍데기를 먼저보여준다
2. js를 다운로드 받아오며 차이점이 있는지 비교하면서 기존의 껍데기(html)을 업데이트한다,
기능을 적용한다.(비교하면서 효율적으로 그리게된다)
=> 아무것도 없던 껍데기를 업데이트 하는것을 디핑 Diffing, 여기에 js등을 적용하는것을 하이드레이션 hydration이라고한다(수화작용-> 메마른 대지에 물을 줌)
---> 그럼 왜 위에 사진에 나온 오류가 나온것일까?
yarn dev에서 프리렌더링시 localStorage가 없기때문에 생긴에러이다.
window의 기능이기에 서버에서 막힌것이다.
(함수안에 넣었을때 가능한경우는 프리랜더링시에는 그냥 화면 그리기만 하기때문이다.)
alert또한 마찬가지이다.
어떻게 해결할까??
- 조건문으로 브라우저일때만 사용
- useEffect => html을 받아 화면이 다 그려진 후 실행되는것.
이렇게 두가지 방법이 있다.
여기서 조건문에서는 또 두가지로 나뉜다.
process.browser방법,
typeof window방법
if (process.browser) { alert("방가 . 지금은 브라우저~"); console.log("지금은 브라우저"); const result = localStorage.getItem("accessToken"); console.log(result); setAccessToken(result ?? ""); // result가 없으면 어쩔껀데? 없으면 빈문자열로!! // => 일단 로컬스토리지에 저장되는것은 객체여도 문자열로 저장됨 // 일단 accessToken을 로그인시 받으면 그것을 활용하고 로컬스토리지에 저장함, // 새로고침시 사라지는데 이때는 로컬스토리지의것을 꺼내 accessToken에 담고 그것을 다시 보내주어 받은 토큰이 사라지지않게함 } else { console.log("지금은 프론트엔드 서버다!!"); }
2.typeof window방법
>```
if(typeof window !=="undefined"){
console.log("지금은 브라우저");
}else{
console.log("지금은 프론트엔드 서버다!!");// 즉 yarn dev로 실행한 부분
}
useEffect(() => { // console.log("지금은 브라우저"); const result = localStorage.getItem("accessToken"); setAccessToken(result ?? ""); }, []);
useEffect의 경우 앞에서도 언급했듯 화면이 다 그려지고, 실행되기에 프리랜더링과는 연관이 없다.
useEffect(() => { if (localStorage.getItem("accessToken") === null) { // accessToken이 없으면.. // 없으면 null임. alert("로그인 후 이용 가능합니다"); void router.push(`/section23/23-03-login-check`); } }, []);
더 효율적인 방법:
closure 실습
stack => 먼저들어온게 마지막에 빠진다.(차례대로 쌓이다가 마지막게 먼저빠지게됨(실행됨)) ==> LIFO구조.(Last In First Out)
que => 먼저 들어온게 먼저나간다 => FIFO구조 => 후반에 이벤트 루프부분에서 다루어짐.
지금 scope에 Local이라고 되어있다 ==>
지금 현재 내가있는 영역 안에서라는 의미.
옆쪽에보면 call stack이라는 부분이 보인다. 실행된 순서대로 쌓이게되며 나중에쌓인것이 먼저 사라진다.
==> 이것이 스코프 체인!!
호이스팅?
미리 js에서 무엇이있는지 읽어보아 뭐가있는지 알고있는것이라고 할 수 있음.
다만, let, const는 선언만되었다고 인식만하지 실제로 선언되고 값이 할당되지 않아 접근이 불가한 반면(TDZ에 들어가있다) var은 접근이 가능해 문제가 될 수 있다
정확하게 호이스팅이라는것은... => 해당 실행 컨텍스트에서 존재(선언되는)하는 변수와 함수들을 끌어올리는것.
(함수 위의 함수)
// 1. 함수를 리턴하는 함수 // aaa를 실행하는순간 결과로 bbb가 나오고, 이 bbb를 실행하려면 aaa함수 옆에 소괄호 하나 더 붙이면 return 하는 bbb에 소괄호 붙이는ㄴ것과 마찬가지 function aaa() { const apple = 10; return function bbb() { const banana = 20; console.log(banana); console.log(apple); }; } aaa()(); // 어차피 실행하면 내부의 함수 이름은 나오지 않으니 이름 필요없음 // 2. 함수를 리턴하는 함수 -인자를 받아 사용 function aaa1(apple) { return function (banana) { console.log(banana); console.log(apple); }; } // undefined aaa1(100)(500); // 500 // 100 // 3. 화살표함수로 만들기. const aaa2 = (apple) => // { // 중괄호롸 retrun사이에 아무것도 없으니 생략가능 // return (banana) => { console.log(banana); console.log(apple); }; // } // undefined aaa2(100)(500); // 500 // 100 // ==> 생략한것 const aaa3 = (apple) => (banana) => { console.log(banana); console.log(apple); }; aaa3(100)(500); // 4. 인자가 여러개 const ccc = (tomato) => (apple) => (banana) => (orange) => { console.log(banana); console.log(apple); console.log(tomato); console.log(orange); }; // undefined ccc(50)(30)(10)(5); // 10 // 30 // 50 // 5
const onClickPage = (page: number) => (): void => { // void refetch({ page: Number(event.currentTarget.id) }); // 아래쪽에서 변수에 담거나 하는것이 없으니 async / await로 기다릴 필요는 없기에 void void refetch({ page }); // 아래쪽에서 변수에 담거나 하는것이 없으니 async / await로 기다릴 필요는 없기에 void }; return ( <div> {data?.fetchBoards.map((el) => ( <div key={el._id}> <span style={{ margin: "10px" }}>{el.title}</span> <span style={{ margin: "10px" }}>{el.writer}</span> </div> ))} {new Array(10).fill(1).map((_, index) => ( <span key={index + 1} // id={String(index + 1)} onClick={ onClickPage(index + 1) // id를 직접 함수로 넣어줌. // 원래는 여기에 소괗호 하나 더 있어야하지만 js에서 자동으로 event를 넣어주니 생략 // event는 뒤쪽에 들어옴!! } {index + 1} </span>
(상위 컴포넌트)
로그인체크를 분리하고, 마이페이지접속시에 로그인 체크 로직이 먼저 실행되는것.(로그인체크를 하고, 해당 페이지에 접속됨)
로그인 안한 사람도 접근 가능하게하려면 => 로그인체크로 감싼 부분을 빼면됨
export default 로그인체크(마이페이지) ==> export default 마이페이지
HOC => hof와 비슷하나 jsx를 리턴하는것
결국 app.tsx의 Component로 합쳐져 실행되고, 이 부분도 함수로 바꿀수 있으니 => => {Component({qqq:"철수"})}
이 부분이 각각 로그인체크 함수의 인자들로 각 자리에 들어가게되어 ===> 첫부분은 컴포넌트가, 두번째 매개변수에는 props가 들어감!!