์ง๋์๊ฐ์ ๋ก๊ทธ์ธ์ ํด์ ํ ํฐ์ ๋ฐ์์ค๊ณ , ๋ก๊ทธ์ธ ์ฑ๊ณต ํ์ด์ง๋ก ์ด๋ํ๋ ๊ฒ๊น์ง ์ค์ต์ ํ๋ค. ํ์ง๋ง ์ฌ๊ธฐ์ ์๋ก๊ณ ์นจ์ ํ๋ฉด ํ ํฐ์ด ์ฌ๋ผ์ง๋ ๋ฌธ์ ์ ์ด ๋ฐ์ํ๋ค. ์ ์ด๋ฐ ๋ฌธ์ ์ ์ด ๋ฐ์ํ ๊น?
์๋ก๊ณ ์นจ์ ํ๋ฉด ์๋ก์ด html, css, js๋ฅผ ๋ค์ ๋ฐ์์ค๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฐ๋ผ์ ์ด์ ์ ๊ทธ๋ ธ๋ state ๋ณ์๋ค์ด ์ด๊ธฐํ๋์ด accessToken
์ด ์ฌ๋ผ์ง๋ ๊ฒ์ด๋ค.
ํด๊ฒฐ๋ฐฉ๋ฒ์ผ๋ก๋ ๋ญ๊ฐ ์์๊น?
refresh token
์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด ์๋ค! ํ์ง๋ง refresh token๋ ๋์ด๋๊ฐ ์๊ธฐ ๋๋ฌธ์ ๊ทธ ์ ์ localstorage
์ ํ ํฐ์ ์ ์ฅํด์ ๋ฌธ์ ์ ์ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์ ๋ค.
localstorage๋ ๋ธ๋ผ์ฐ์ ์ ์ฅ์ ์ค ํ๋์ด๋ค.
ํ ํฐ์ ๋ธ๋ผ์ฐ์ ์ ์ ์ฅํ๋ฉด ์๋ก๊ณ ์นจ์ ํด๋ ํ ํฐ์ด ๋ ์๊ฐ์ง ์๋๋ค.
localStorage.setItem(key, value)
๋ฅผ ์ด์ฉํด์ ๋ก๊ทธ์ธ ํ์ ๋ฐ์์จ accessToken์ ๋ธ๋ผ์ฐ์ ์ ์ ์ฅํด์ค๋ค!
import {useMutation,gql} from "@apollo/client"
import {ChangeEvent} from "react"
import { useRecoilState } from "recoil";
import {useRouter} from "next/router"
cosnt LOGIN_USER = gql`
mutation loginUser($email:String){
loginUser(email: $email, password: $password){
accessToken
}
}
`
export default function LoginPage(){
cosnt [accessToken,setaccessToken] = useRecoilState(accessTokenState)
const [email,setEmail]=useState("")
const [password,setPassword]=useState("")
const [loginUser] = useMutation<Pick<IMutation,'loginUser'>,IMutationLoginUserArgus>(LOGIN_USER)
const router = useRouter()
const onChangeEmail = (event:ChangeEvent<HTMLInputElement>)=>{
setEmail(event.target.value)
}
const onChangePassword = (event:ChangeEvent<HTMLInputElement>)=>{
setPassword(event.target.value)
}
const onClickLogin = async()=>{
try{
// 1. ๋ก๊ทธ์ธํด์ accessToken ๋ฐ์ค๊ธฐ
cosnt result = await loginUser({
variables:{
email : email,
password : password
}
})
const accessToken = result.data?.loginUser.accessToken
// 2. accessToken์ด ์๋ค๋ฉด global state์ ์ ์ฅ ํ localStorage์ ์ ์ฅํ๊ธฐ
if(accessToken){setAccessToken(accessToken || "" )
void router.push('/loginsuccess')
localStorage.setItem("accessToken",accessToken) // ์์๋ก ์ฌ์ฉ ๋์ค์ ์ง์ธ์์
}
}catch(error){
Modal.error({content : error.message})
}
}
return(
<div>
์ด๋ฉ์ผ : <input type="text" onchange={onChangeEmail}/> <br/>
๋น๋ฐ๋ฒํธ : <input type="password" onchange={onChangePassword}/>
<button onClick={onClickLogin}>๋ก๊ทธ์ธ</button>
</div>
)
}
์ค์ ๋ก accessToken์ด ์ ์ฅ๋๋ ๊ณณ์ recoil์ recoilState์ accessToken์ด๋ผ๋ ๋ณ์๋ค.
์๋ก๊ณ ์นจ์ ํ๊ฒ ๋๋ฉด accessToken ๋ณ์๊ฐ ์๋ก ๊ทธ๋ ค์ง๊ฒ ๋๋ค. ๋ฐ๋ผ์ login ํ์ด์ง์์ ๋ธ๋ผ์ฐ์ ์ ์ ์ฅํด๋ accessToken์ getItem
์ ํตํด ๊ฐ์ ธ์, setAccessToken
์ ๋ค์ ๋ฃ์ด์ฃผ์ด ๋ธ๋ผ์ฐ์ ์ ์ ์ฅ๋ ํ ํฐ์ผ๋ก ๋ฐ๊ฟ์ค์ผ ํ๋ค.
์ฐธ๊ณ ! localStorage ์ฌ์ฉ๋ฐฉ๋ฒ
์ ์ฅํ ๋: localstorage.setItem("key", value)
๊บผ๋ด์ฌ ๋: localstorage.getItem("key")
ํ์ง๋ง ์ฌ๊ธฐ์ localstorage is not defined
์๋ฌ๊ฐ ๋ฐ์ํ๋ค. next.js์ ๋ ๋๋ง ๋ฐฉ์ ๋๋ฌธ์ด๋ค!
๋ธ๋ผ์ฐ์ ์ ์ฃผ์๋ฅผ ์ ๋ ฅํด์ ์ ์ํ๋ฉด, ํ๋ก ํธ์๋ ์๋ฒ๋ก๋ถํฐ html, css, js์ ๋ฐ์์จ๋ค.
html์ด ๋จผ์ ๋ค์ด๋ฐ์์ค๊ณ , html์ด ๋ธ๋ผ์ฐ์ ์ ๊ทธ๋ ค์ง๋ฉด์ css, js๋ฅผ ๋ค์ด๋ฐ์์จ๋ค.
html ํ์ผ์ ๋ง๋ค์ด์ผ ํ๋๋ฐ, ์ฐ๋ฆฌ๋ jsํ์ผ๋ก๋ง ํ์ด์ง๋ฅผ ๋ง๋ค์๊ธฐ ๋๋ฌธ์ ๋ธ๋ผ์ฐ์ ๊ฐ ์๋ ํ๋ก ํธ ์๋ฒ์์ ๋จผ์ ๊ทธ๋ ค๋ณธ๋ค(prerendering
).
ํ๋ฆฌ๋ ๋๋ง ๊ฒฐ๊ณผ๋ฅผ ๋ธ๋ผ์ฐ์ ์์ ๊ทธ๋ฆฌ๊ณ , ๊ทธ ํ์ js ํ์ผ์ ์๋ฒ๋ก๋ถํฐ ๋ค์ด๋ฐ์์ค๊ณ , js ํ์ผ์ ๋ฐํ์ผ๋ก ๋ธ๋ผ์ฐ์ ์์ ๋ค์ ์คํ์์ผ๋ณด๋ฉฐ ๋จผ์ ๊ทธ๋ ธ๋ ๊ฒ๊ณผ ๋น๊ต(diffing
)ํด๋ณธ๋ค.
๋น๊ต(diffing
)ํ ํ์ ๋ค๋ฅธ ๋ถ๋ถ์ด ์๋ค๋ฉด ์ต์ข
์ ์ผ๋ก ๋ฐ์ํด์ ๋ ๋๋งํ๋ค. ์ฌ๊ธฐ์ onClick, onChange ๋ฑ ๊ธฐ๋ฅ๋ค์ ์ถ๊ฐํด์ฃผ๊ฒ ๋๋ค. ์ด ๊ณผ์ ์ hydration
์ด๋ผ๊ณ ํ๋ค.
์ฆ, 2๋ฒ ๊ทธ๋ฆฌ๊ฒ ๋๋ ๊ฒ์ด๋ค! html๋ง ๋น ๋ฅด๊ฒ ๋ฐ์์์ ๋ธ๋ผ์ฐ์ ์ ๊ทธ๋ ค์ฃผ๊ณ , ๊ทธ ํ์ js๋ก ํ ๋ฒ ๋ ๊ทธ๋ ค์ค๋ค(hydration: ๋ฌผ ์ฃผ๊ธฐ).
์ฌ๊ธฐ์ localstorage is not defined
์๋ฌ์ ์ด์ ๋ฅผ ์ ์ ์๋ค. localstorage๋ ๋ธ๋ผ์ฐ์ ์๋ง ์๋๋ฐ, ์๋ฒ์์ ๋จผ์ prerendering ํ๊ธฐ ๋๋ฌธ์ localstorage๋ ์คํ์ ์คํจํ๋ค.
useEffect
๋ฅผ ์ฌ์ฉํด์ ๋ ๋๋ง ์ดํ์ ์คํ๋๋๋ก ํ๋ฉด ๋๋ค.
// Apollo Setting
// ==import ๋ถ๋ถ ์๋ต==
export default function ApolloSetting(props) {
const [accessToken,setAccessToken] =useRecilState(accessTokenState)
useEffect(()=>{
if(localStorage.getItem("accessToken")){
setAccessToken(localStorage.getItem("accessToken")||"")
}
},[])
const uploadLink = createUploadLink({
uri : "๋ฐฑ์๋ ์ฃผ์",
headers : { Authorization : "Bearer ๋ฐ์์จ ํ ํฐ" }
})
// ====== return ๋ถ๋ถ ์๋ต =======
}
๋ก๊ทธ์ธ ์ธ์ฆ ์ดํ์๋ ๊ถํ ๋ถ๊ธฐ
๊ฐ ์ด๋ฃจ์ด์ง๋ค. ๋ก๊ทธ์ธ์ ํ ์ฌ๋๊ณผ ํ์ง ์์ ์ฌ๋์ผ๋ก ๋๋๊ธฐ๋ ํ๊ณ , ์ด์์๋ก ๋ก๊ทธ์ธ ํ ์ฌ๋, ํ๋งค์๋ก ๋ก๊ทธ์ธ ํ ์ฌ๋ ๋ฑ์ผ๋ก ๋ค์ํ๊ฒ ๊ถํ์ ๋ถ๋ฆฌํ ์๋ ์๋ค.
์คํ
์ ์ถ์
๊ตฌ๊ฐ ํ๋์ธ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ค.
๊ฐ์ฅ ์ฒ์์ ์
๋ ฅ๋ ํจ์๊ฐ ๊ฐ์ฅ ๋์ค์ ์คํ์ ๋น ์ ธ๋๊ฐ๋ค(FILO
: First In Last Out).
ํ
๋ ์๋ฐฉํฅ ์ถ์
์ด ๊ฐ๋ฅํ ํ์ดํ ํํ์ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ค.
๊ฐ์ฅ ๋จผ์ ์
๋ ฅ๋ ํจ์๊ฐ ๊ฐ์ฅ ๋จผ์ ๋น ์ ธ๋๊ฐ๋ค(First In First Out
).
์ค์ฝํ ์ฒด์ธ
์ด๋ ํด๋น ์ค์ฝํ์ ์์ผ๋ฉด ์์ ์ค์ฝํ๋ก ์ฐพ์ ์ฌ๋ผ๊ฐ๋ ๊ณผ์ ์ ์๋ฏธํ๋ค.
<!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>
script tag ์์ชฝ์ ๋ณด๋ฉด, aaa()์ bbb()๊ฐ ์ฝ์คํ์ ์์ด๊ณ , bbb()๊ฐ ์คํ์ ๊ฐ์ฅ ์์ ์๊ธฐ ๋๋ฌธ์ ๊ฐ์ฅ ๋จผ์ ์ฝ์คํ์ ๋น ์ ธ๋๊ฐ๋ค.
bbb ํจ์์์ apple์ด๋ผ๋ ๋ณ์๋ฅผ ์ฝ์์ฐฝ์ ๋์ฐ๋ ค๊ณ ํ์ง๋ง, bbb() ์์๋ apple์ด๋ผ๋ ๋ณ์๊ฐ ์๋ค. ๋ฐ๋ผ์ ์ค์ฝํ์ฒด์ธ์ด ์ผ์ด๋๊ณ apple ๋ณ์๋ฅผ ์ฐพ๊ธฐ ์ํด aaa()๋ผ๋ ์์ ์ค์ฝํ๋ก ์ฌ๋ผ๊ฐ๋ค.
์ฌ๊ธฐ์ aaa ํจ์๋ bbb์ closure
๊ฐ ๋๋ค. ์ฆ, ํด๋ก์ ๋ ์์ ํจ์์ ํด๋น ํจ์(bbbํจ์)๊ฐ ์ ์ธ๋ ์ค์ฝํ(์์ํจ์๋ฅผ ๋๋ฌ์ผ ํ๊ฒฝ)์ด ๋๋ค.
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
// 10
์์ ํจ์๋ ํ๋ผ๋ฏธํฐ
๋ฅผ ์ด์ฉํด ์กฐ๊ธ ๋ ๊ฐ๊ฒฐํ๊ฒ ๋ฐ๊ฟ ์ ์๋ค.
// ํจ์ ์ ์ธ์
function aaa(apple){
return function bbb(banana){
console.log(banana)
console.log(apple)
}
}
aaa(10)(5)
// ์คํ ๊ฒฐ๊ณผ
// 5 => bbb์ ๋ฃ์ ์ธ์๊ฐ
// 10 => aaa์ ๋ฃ์ ์ธ์๊ฐ
์ด์ ์์ ํจ์๋ฅผ ํ์ดํ ํจ์
๋ก ๋ฐ๊พธ๋ฉด ์๋์ ๊ฐ์ด ๋ํ๋ด์ค ์ ์๋ค.
// ํ์ดํ ํจ์๋ก ๋ณ๊ฒฝ
const aaa = (apple)=>{
return (banana)=>{
console.log(apple)
console.log(banana)
}
}
aaa(10)(5)
์ค๊ดํธ์ return ์ฌ์ด์ ์๋ฌด๊ฒ๋ ์๋ค๋ฉด ์ค๊ดํธ๋ฅผ ์๋ตํ ์ ์์๋ค. ๋ฐ๋ผ์ ๋ค์๊ณผ ๊ฐ์ด ๋ํ๋ผ ์ ์๋ค.
// ์ค๊ดํธ ์๋ต
const aaa = (apple)=>(banana)=>{
console.log(apple)
console.log(banana)
}
aaa(10)(5)
HOC
๋ ์์์ ์๋ ์ปดํฌ๋ํธ๋ก ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ณด๋ค ๋จผ์ ์คํ๋๋ ์ปดํฌ๋ํธ๋ค.
HOC๋ ํด๋์คํ ์ปดํฌ๋ํธ
์ ์ ํฉํ ๋ฐฉ์์ด๋ค. ํจ์ํ ์ปดํฌ๋ํธ์ ์ ํฉํ ๋ฐฉ์์ ๋์ค์ ๋ฐฐ์ธ ์์ ์ด๋ค.
๊ถํ์ ์ฒดํฌํ๋ HOC๋ฅผ ์ง์ ๋ง๋ค์ด๋ณด์๋ค.
export const withAuth = (Component:any)=>(props:any)=>{
const router = useRouter()
useEffect(()=>{
if(!localStorage.getItem("accessToken")){
alert("๋ก๊ทธ์ธ์ ๋จผ์ ํด์ฃผ์ธ์")
void router.push("/๋ก๊ทธ์ธ ํ์ด์ง")
}
},[])
return <Component {...props} />
}
์ด์ ๊ถํ ์ฒดํฌ ์ปดํฌ๋ํธ๋ฅผ ์ ์ฉํ๊ณ ์ถ์ ํ์ด์ง์ ์ ์ฉํด์ฃผ๋ฉด ๋๋ค.
// loginSuccessPage -> withAuth ์ ์ฉํ๊ธฐ
const LoginSuccessPage = ()=>{
const {data} = useQuery(FETCH_USER_LOGGED_IN)
return <div>{data?.fetchUserLoggedIn.name}๋ ํ์ํฉ๋๋ค.</div>
}
export default withAuth(LoginSuccessPage)
์์ ๊ฐ์ด withAuth(์ ์ฉํ๊ณ ์ถ์ ์ปดํฌ๋ํธ)
ํ์์ผ๋ก export ํด์ฃผ๋ฉด ๋๋ค!
์ด๋ ๊ฒ ํด์ฃผ๋ฉด ํด๋น ์ปดํฌ๋ํธ๊ฐ ์คํ๋๊ธฐ ์ ์ ๊ถํ์ฒดํฌ ์ปดํฌ๋ํธ๊ฐ ๋จผ์ ์คํ๋๋ค.