로그인 인증을 마쳤으면 이에 따른 권한 분기
가 이루어진다.
배달의 민족 페이지를 예로 들면
1) 사용자가 접속하는 페이지,
2) 관리자가 접속하는 페이지,
3) 사장이 접속하는 페이지
등 다양하게 권한을 분리할 수 있다.
이러한 권한에 따른 페이지를 보여주기 및 권한이 없을 때 처리해주는 과정이 바로 권한분기이다.
이 부분이 가능하려면 로그인 했을 때 어떻게 권한을 주는지에 대한 내용이 밑바탕이 되어야한다.
권한을 구분하는 방법으로 useEffect
에서 accessToken
이 없으면 "/login"
화면으로 페이지를 이동시킨다.
import { useQuery, gql } from "@apollo/client";
import { useRouter } from "next/router";
import { useEffect } from "react";
import { useRecoilState } from "recoil";
import { accessTokenState } from "../../src/commons/store/index";
const FETCH_USER_LOGGED_IN = gql`
query fetchUserLoggedIn {
fetchUserLoggedIn {
email
name
}
}
`;
export default function LoginSuccessPage() {
const { data } = useQuery(FETCH_USER_LOGGED_IN);
const router = useRouter();
const [accessToken, setAccessToken] = useRecoilState(accessTokenState);
useEffect(() => {
if (!accessToken) {
alert("로그인하고 사용해라");
router.push("/login");
}
}, [accessToken]);
return <div>{data?.fetchUserLoggedIn.name}님 환영합니다!!!</div>;
}
권힌분기를 하려면 로그인 권한을 부여하는 페이지에 모두 위의 코드를 입력해야하는 버거로움이 있다. 이 문제를 해결하기 위해 HOC
을 사용한다. HOC
을 공부하기 앞서 먼저 클로저
부터 알아야한다. 왜냐하면 HOC
은 클로저로부터 확장된 개념이기 때문이다.
클로저는 내부 함수에서 외부 함수의 지역변수에 접근하는 것을 의미한다.
function A(){
const 지역변수 = 123
return funtion B(){
console.log(지역변수) //123
}
}
스코프 체인 즉 Local->Closure(가장 가까운 외부 함수)->Global 순으로 찾아 간다
내부에 있는 함수 B가 외부에 있는 함수 A에 있는 지역변수에 접근할 수 있다. 이 개념을 응용해서 HOC을 만든다.
HOC을 알기 앞서 HOF도 알아야하는데 이 둘의 차이는 Componenet와 Function의 차이라는 것을 명심하자. JSX(React의 HTML)를 return 하면 Component, 그렇지 않으면 Function !
HOC은 페이지의 권한을 처리할 때 사용하며 특정 컴포넌트를 실행하기 전에 상위 컴포넌트를 먼저 실행시켜 준다. HOC를 하나 만들어 놓고, 로그인이 필요한 컴포넌트 앞에 HOC만 붙여주면 간단하게 권한처리가 끝난다.
HOC는 다른 컴포넌트와 함께 실행되는 고차 컴포넌트이므로 with를 이름을 앞에 붙여야한다. ex) withAuth
, withApollo
import { useRouter } from "next/router";
import { useEffect } from "react";
export const withAuth = (Componenet) => (props) => {
const router = useRouter();
// 권한분기 로직 추가하기
// 토큰체크
useEffect(() => {
if (!localStorage.getItem("accessToken")) {
alert("로그인 후 이용 가능합니다!!!");
router.push("/login");
}
}, []);
return <Componenet {...props} />;
};
import { withAuth } from "../../src/components/commons/hocs/withAuth";
import { userInfoState } from "../../src/commons/store";
import { useRecoilState } from "recoil";
function LoginSuccessPage() {
const [userInfo] = useRecoilState(userInfoState);
return (
<div>
<div>{userInfo.name}님 환영합니다</div>
</div>
);
}
export default withAuth(LoginSuccessPage);
HOF 역시 HOC 와 비슷하다. 기존 material-ui, ant-design 등의 컴포넌트를 이용하면 id값이 날라가는 현상이 있었다. HOF의 경우 event.target.id 라고 직접 입력하지 않아 코드가 짧아진다는 장점이 있다.
또한, id는 전체 태그에서 고유해야하기 때문에, id가 남용되면 대규모 서비스에서 예기치 못한 상황이 발생할 수 있는데 HOF를 사용하면 이러한 점들을 사전에 방지해 줄 수 있고, 사용이 더욱 편리하다.
export default function Aaa(){
const onClickButton = (event) => {
console.log(event.target.id)
}
return <button id={123} onClick={onClickButton}>클릭</button>
}
export default function Bbb(){
const onClickButton = (id) => (event) => {
console.log(id)
}
return <button onClick={onClickButton(123)}>클릭</button>
}
권한분기는 정말 중요하니까 반복 복습 복습 ! 하자!
그리고 짧고 편한 코드를 작성하게 해주는 HOC
와 HOF
. 이 것들이 가능한 이유는 Javascript의 클로저
덕분이라는 것을 명심하자.