Learn 권한 분기, Closure, HOC&HOF

Junghan Lee·2023년 4월 13일
0

Learnd in Camp

목록 보기
32/48

index

권한분기(스택, 큐, 스코프 체인, 클로저, 호이스팅)
함수를 리턴하는 함수 HOF
HOC(Higher Order Component)&withAuth


권한 분기

로그인 인증 이후에는 이에 따른 권한 분기가 이루어지는데 작게는 로그인을 한 사람과 하지 않은 사람부터 로그인에 등급을 매기면 운영자로 로그인한 사람 판매자로 로그인한 사람 등 다양하게 권한을 분리할 수 있다.

이를 진행하기 위해서는 스택, 큐에 대한 지식이 필요하다.

스택이란?
출입구가 하나인 우물 형태의 데이터 구조

스택은 출입구가 하나이기 때문에 가장 처음에 입력된 함수가 가장 나중에 스택을 빠져나가게 되며 이는 First In Last Out(FILO)라고 한다.

큐?
양방향 출입이 가능한 파이프 형태의 데이터 구조


First In First Out(FIFO)

스코프 체인

// closure.html 파일
<!DOCTYPE html>
<html lang="ko">
	<head>
		<title>클로저 실습</title>
		<script>
			function aaa(){
				banana = 3
				console.log(banana)
			}
			aaa();
		</script>
	</head>
	<body>
		
	</body>
</html>


코드 실행 방식을 알아보기 위해 개발자 도구의 source를 이용, 실행하는 곳에서 브레이크 포인트(코드가 빠르게 실행되다 멈추고 싶은 코드에서 멈추기)를 걸어두고 시작
1번 : 실행하지 않고 뛰어넘기, 2번: 실행과정 하나하나 살펴보기
aaa()에 브레이크 포이트를 걸어두고 함수 안으로 하나씩 들어오니 callstack에 aaa 함수가 쌓여있다.

스코프의 글로벌 부분을 보면 바나나가 3으로 잘 들어오고 있다. local에 바나나가 있었다면 local에 바나나가 들어와 있었겠지만 없었기 때문에 global까지 찾아올라감, 이것을 보고 스코프 체인이라고 한다.즉, 해당 스코프에 없으면 상위 스코프로 찾아 올라가는 과정을 의미한다.

클로저(closure)

// closure.html 파일
<!DOCTYPE html>
<html lang="ko">
	<head>
		<title>클로저 실습</title>
		<script>
			function aaa(){
				const apple = 10

				function bbb(){
					console.log(apple)
				}
				bbb()
			}
			aaa();
		</script>
	</head>
	<body>
		클로저 실습
	</body>
</html>

콜스택에 aaa, bbb 생성되어 있다. bbb는 스택의 가장 위에 있기 때문에 가장 먼저 콜스택을 빠져나간다. 스코프 부분을 보면 local은 bbb함수를 의미한다.

bbb함수에서 콘솔로 apple이라는 변수를 찍어보려 하나 bbb 안에는 apple이 없고 자바스크립트는 이를 위해 상위 스코프로 올라간다. 그러나 closure??

closure를 보면 apple: 10이 적혀 있다.

클로저란 bbb 함수 스코프 안에 apple 변수가 없어 aaa라는 상위 함수의 스코프로 올라가 apple이라는 변수를 찾게 된다. aaa 함수는 bbb의 closure가 되며 bbb가 apple을 찾아 올라갈 수 있는 이유는 실행컨텍스트가 외부 환경 요소를 수집하기 때문이다. 즉, 클로저는 상위 함수와 해당함수(bbb)가 선언된 스코프, 즉 상위 함수를 둘러싼 환경이다.

bbb함수에서 aaa함수 스코프로 올라가는 과정에서 스코프 체인이 일어난다.

  • 클로저(closure) : 상위 함수 + 상위함수의 lexical enviroment(상위함수를 둘러싼 환경)
  • 스코프 체인(scope chain) : 바로위 함수 스코프 뿐만아니라 global 스코프 까지 찾아 올라가는 과정을 scope chain이라고 함

호이스팅?

// closure.html 파일
<!DOCTYPE html>
<html lang="ko">
	<head>
		<title>클로저 실습</title>
		<script>
			function aaa(){
				const apple = 10

				function bbb(){
					console.log(apple)
				}
				bbb()

				const qqq = 3
			}
			aaa();
		</script>
	</head>
	<body>
		
	</body>
</html>

위 코드에 qqq변수를 추가해 다시 aaa()에 브레이크 포인트를 걸어 실행 후 aaa함수로 들어가 보면... 스코프의 local을 보니 아직 실행되지도 않았는데 qqq = undefined? ; 이는 호이스팅 때문...

var였다면 접근도 가능했겠으나 let, const는 Temporal Dead Zone(TDZ)에 들어가 있기 때문에 접근은 불가하다.

또 클릭을 하게 되면 스코프와 콜스택의 상태가 변화하는데 이를 실행 컨텍스트라고 한다.

함수를 리턴하는 함수 : HOF

HOC를 사용하기 위한 함수를 리턴하는 함수

function aaa(){
	console.log("저는 aaa예요")

	return function bbb(){
		console.log("저는 bbb예요")
	}
}

콘솔에는 저는 aaa에요만 출력이 되고 반환값으로 bbb 함수가 있다.
콘솔에 저는 bbb에요를 출력하려면? bbb함수를 호출해주면 된다.

aaa()()로 적어주면 됨.

function aaa(){
	const apple = 10

	return function bbb(){
		const banana = 5
		console.log(banana)
		console.log(apple)
	}
}

aaa()()

// 실행 결과
// 5
// 6

위의 함수를 파라미터를 사용해 간결하게 바꾸면

// 함수 선언식
function aaa(apple){

	return function bbb(banana){
		console.log(banana)
		console.log(apple)
	}
}

aaa(2)(3)

// 실행 결과
// 2 => aaa에 넣은 인자값
// 3 => bbb에 넣은 인자값

각 변수를 함수의 파라미터로 넘기게 되면 실행시 넘겨준 인자값으로 콘솔 출력..
bbb에 apple이 없음에도 받아오는 이유 : 클로져

// 화살표 함수로 변경
const aaa = (apple)=>{
	return (banana)=>{
				console.log(apple)
				console.log(banana)
		}
}

aaa(2)(3)

최종적인 모양

// 중괄호 생략
const aaa = (apple)=>(banana)=>{
				console.log(apple)
				console.log(banana)
}

aaa(2)(3)

High Order Component (HOC)

이는 클로저부터 확장된 개념으로
다른 컴포넌트보다 먼저 실행되는 상위의 컴포넌트라고 생각하면 된다.

aaa컴포넌트의 HOC(bbb)({qqq:"철수"})를 보면 함수를 리턴하는 함수의 호출방법과 비슷하다.

bbb부분에는 apple이 들어왔었고, {qqq:"철수"} 부분에는 banana가 들어왔었다.

HOC함수의 매개변수 부분에 component가 들어와 있는데 component파라미터에 들어올 컴포넌트는 bbb컴포넌트이다.

HOC함수에서 리턴되고 있는 함수를 보면
리턴 함수의 매개변수로 props가 들어가고 있고 해당 props에는 함수 호출 시 넣어준 인자값인 {qqq:"철수"}가 들어오게 된다. 밑으로 내려와 익명함수의 리턴값을 보면 내부에 컴포넌트가 없음에도 불구하고 컴포넌트를 리턴하는데 이는 클로저로 외부의 컴포넌트를 가지고 올 수 있기 때문이다.

withAuth만들어 보기

권한을 체크하는 Hoc를 직접 만들어 보면..

// src/components/commons/hoc/withAuth.tsx 파일

export const withAuth = (Component:any)=>(props:any)=>{
	const router = useRouter()

	//loginCheckSuccess 파일에 있는 useEffect를 가지고 오시면 됩니다. 
	useEffect(()=>{
		if(!localStorage.getItem("accessToken")){
			alert("로그인을 먼저 해주세요")
			void router.push("/로그인 페이지")
		}
	},[])

	return <Component {...props} />
}

eslint 문제 발생 시) "react/display-name":"off"

component&props 부분의 에러는 generic을 통해 해결

권한체크 Hoc을 만들었다면 적용하고 싶은 페이지에 적용하면 됨!

// loginSuccessPage -> withAuth 적용하기 

const LoginSuccessPage = ()=>{
	const {data} = useQuery(FETCH_USER_LOGGED_IN)

	return <div>{data?.fetchUserLoggedIn.name}님 환영합니다.</div>
}

export default withAuth(LoginSuccessPage)

이러면 컴포넌트가 실행되기 전에 권한 체크 컴포넌트가 먼저 실행된다!

profile
Strive for greatness

0개의 댓글