브랜드메뉴, 햄버거메뉴에서 사용되는 관심브랜드 기능을 구현하는 과정에서 로그인한 사용자만 관심브랜드 기능을 이용할 수 있다. 때문에 api요청시 아래와 같이 headers
의 ‘Authorization’
값으로 accessToken
값을 전달줘야 했다.
...
const product = {
getInterestProduct: (queryString, accessToken) => {
if (accessToken != undefined) {
aaApi.defaults.headers.common['Authorization'] = 'Bearer ' + accessToken
}
return aaApi.get(...)
},
}
export default product
그래서 관심브랜드의 하트토글 버튼을 사용자가 클릭했을때 상태객체의 accessToken
이 null
이면, 로그인페이지로 리다이렉트하도록 했다.
// /hooks/useClickHeartToggle.ts
import {useAppSelector} from '../app/redux/hooks';
import {Brand} from '../types/brandTypes';
export default function useClickHeartToggle() {
const {accessToken} = useAppSelector((state) => state.user);
// 하트버튼 눌렀을때 관심브랜드 토글기능
const handleClickHeart = (brand: Brand) => {
if (!accessToken) {
alert('로그인이 필요한 기능입니다.');
window.location.href = url
} else {
console.log('brand', brand);
}
};
return {
handleClickHeart,
};
}
그런데 로그인하고 다시 검색페이지로 돌아와서 쿠키에서 ‘accessToken’
값을 얻어보려고 해도 undefined
만 찍혔다. 웹스토리지엔 ‘accessToken’
값이 잘 들어와 있는데도 말이다.
공식문서에서 설명하는 것처럼 cookies메서드를 사용하면, 서버 컴포넌트로부터 http 수신 요청 쿠키를 읽어들일 수 있다고 한다. 만약 이름을 찾을 수 없다면 undefined
를 반환한다. 그러나 undefined
가 찍혔다.
// server-component
import {cookies} from 'next/headers';
import BrandMenu from '../../components/brands/brandMenu';
// 페이지는 경로 세그먼트에 고유한 UI다.
export default function BrandsPage() {
const cookieStore = cookies();
const accessToken = cookieStore.get('accessToken');
// toDo 로그인을 해도 undefined가 찍힌다.
console.log('accessToken', accessToken);
return <BrandMenu />;
}
아무래도 도메인이 달라서 인 것 같다..?
‘use client’
키워드를 쓰는 클라이언트 컴포넌트에 아래와 같이 로직을 작성해주면, 해당 컴포넌트 생명주기가 시작될 때 setCookie
의 세번째 인자의 프로퍼티 중 domain
을 생략해주면 동일한 domain
으로 쿠키가 저장되어 콘솔값이 잘 찍히지만,
아래와 같이 특정 도메인을 지정하여 웹스토리지에 저장하면 콘솔값이 아무것도 찍히지 않는다.
'use client';
import {useCookies} from 'react-cookie';
export default function Header() {
const [cookies, setCookie, removeCookie] = useCookies(['accessToken']);
const setCookieHandler = () => {
setCookie('accessToken', 'dd', {
domain: './blabla.com',
path: '/',
});
};
useEffect(() => {
setCookieHandler();
console.log('Cookies: ', cookies);
}, [cookies]);
return (
...
);
}
근데 기존코드에서는 됐잖아? 그래서 기존 코드의 로직을 그대로 가져오려고했다. 그런데 ”getServerSideProps” is not supported in app/.
에러가 났다. 공식문서를 확인하니, pages router에서만 된단다. (즉, pages디렉터리에서만 된다는 뜻인 것 같음)
===Quoted===
Previous Next.js data fetching methods such as getServerSideProps, getStaticProps, and getInitialProps are not supported in the new App Router. However, you can still use them in the Pages Router.
===End===
그래서 .blabla
도메인이 일치하는 test.blabla
로 접속하니까 쿠키값이 문제없이 console.log
로 출력되는 것을 확인할 수 있었다! 결국 예상한대로 도메인이 달라서, 즉 localhost
와 blabla.com
도메인이 달라서 생기는 문제였다.
근데 왜일까? http쿠키(웹 쿠키, 브라우저 쿠키)는 서버가 사용자의 웹 브라우저에 전송하는 작은 데이터 조각이다. 즉, 쿠키는 두 요청이 동일한 브라우저에서 들어왔는지 아닌지를 판단할 때 주로 사용한다. 쿠키를 받는 과정은 아래와 같다.
1/ (클라이언트) ==http요청==> (서버a) : 서버가 클라이언트의 http요청을 수신
2/ (클라이언트) <==응답 + Set-Cookie헤더
== (서버a)
Set-Cookie: <cookie-name>=<cookie-value>
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
[page content]
3/ (클라이언트) ==Cookie헤더==> (서버a)
Cookie http헤더
안에 포함하여 전송한다.GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
여기까지 정리한 후에 다시 위의 이슈를 되짚어 보자면, 처음에 localhost
도메인에서 전역 상태 관리 객체를 확인해서 accessToken
값이 null
인경우, 로그인 페이지로 리다이렉션한다. 이때의 도메인 주소는 blabla.com
이다. 즉, 로그인시 해당 도메인(blabla.com
)에서 쿠키를 요청해서 서버로부터 쿠키를 저장하라고 Set-Cookie
헤더를 응답받게 되는데, localhost
도메인으로 돌아가서 아래 코드처럼 쿠키를 get
해오려고 해당 클라이언트에 요청을 해버리니까 서버에서 undefined
를 반환하는 것이다.
// server-component
import {cookies} from 'next/headers';
import BrandMenu from '../../components/brands/brandMenu';
// 페이지는 경로 세그먼트에 고유한 UI다.
export default function BrandsPage() {
const cookieStore = cookies();
const accessToken = cookieStore.get('accessToken');
// toDo 로그인을 해도 undefined가 찍힌다.
console.log('accessToken', accessToken);
return <BrandMenu />;
}
근데 여기서 undefined
를 반환하는 서버는 무엇일까? 아래와 같이 공식문서에서 cookies()
함수는 동적함수이고,
아래와 같이 공식문서에서 동적함수는 캐시된다는데 그럼 캐시서버일까?
앞서 정리했듯이, 서버와 통신해야 cookie
를 받을 수 있다. nextJS에선 서버 컴포넌트와 클라이언트 컴포넌트가 나누어져있다.
우선 ssr이란 서버 사이드에서 웹 문서를 만들어서 클라이언트로 서빙하는 것을 말한다. 즉, 서버 사이드에서 만들어진 문서는 javascript가 없이도 페이지를 렌더링할 수 있다. 하지만 정적으로만 화면을 구성할 수 있다. 그래서 화면의 레이아웃을 변경하려면 서버 사이드에서 html을 계속 만들어야하기 때문에 서버 리소스가 많이들게 된다.
그렇기 때문에 reactJS가 클라이언트 사이드(브라우저)에서 javascript로 이미 렌더링된 html페이지를 조작하는 것이다.
결국 nextJS를 사용하게 되면 nextJS가 주체로 html파일을 클라이언트 사이드에 서빙하고, 그 다음에 reactJS가 주체로 javascript파일을 클라이언트 사이드에 서빙한다. 이후 js코드들이 이전에 보내진 html dom요소에 매칭되게 되는데 이게 hydration이다.
다시 돌아와서 서버에서 제공하는 쿠키를 가져오려면 헤더를 통해 가져와야하는데, 이때는 서버 사이드, 클라이언트 사이드 모두 가능하지만 적어도 해당 서버를 거쳐서 받아와야만 한다. 즉 서버를 거치지 않고 다이렉트로 브라우저로 접근하면 서버에서는 undefined
를 반환할 수 밖에 없는 것이다.
여기서 서버는 nextJS이고, nextJS는 서버와 통신을 한 적이 없어서 쿠키의 존재를 모른다. 그런데 blabla.com
서버를 거쳐서 오게되면 해당서버에서 쿠키를 .blabla
도메인으로 공유하기 때문에 같은 도메인인 test.blabla
에서는 쿠키를 알 수 있게된다. 하지만 localhost
도메인에서는 이를 절대 알 수 없다. 그래서 undefined
를 반환했던 것이다.
https://blog.logrocket.com/guide-cookies-next-js/
https://developer.mozilla.org/ko/docs/Web/HTTP/Cookies
많은 도움이 되었습니다, 감사합니다.