์๋ก
JWT ํ ํฐ.. ๋ง ํ๋ก์ ํธ๋ฅผ ์ ํ๋ 1-2ํ๋ ํ๋ถ์ ์์ ์ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ ์ด๊ฑธ๋ก ๊ตฌํํ๊ณ ์ถ์ด์ ๋๋๋๋์ ์ด ์๋ค.
๊ทธ๋๋ ์ด๊ฒ ๋ฌด์จ๋ง์ด์ง ๋์ ํ ์ดํด๋ฅผ ๋ชปํ๊ณ ๊ตฌํ์ ํฌ๊ธฐํ์๋๋ฐ,
์ง๊ธ์ ์ด๊ฑธ ์ดํดํ๊ณ ํ๋ก์ ํธ์ ์ ์ฉ ์์ผฐ๋ค๋ ์ ๊ธฐํ ์ผ์ด๋ค. ๋ง์ด ์ฑ์ฅํ์์ ๋๋๋ค.
๋ถ๋๋ฝ์ง๋ง RhythMeet ๊ธฐ์กด์ ๋ก๊ทธ์ธ ์ธ์ฆ ๋ฐฉ์์ ๋ฌธ์ ๊ฐ ์ข ๋ง์๋ค.
๋ฐํ ์๊ฐ์ ๋ง์ถฐ์ ํ๋ฌ์ด๋ผ๋ ์งง์ ์๊ฐ ์์ ์ต๋ํ ๊ธฐ๋ฅ์ ๊ตฌํํ์ด์ผ ํ๋ ๋ฌธ์ ๋๋ฌธ๋ ์์๊ฒ์ด๋ค.
์ด์จ๋ , ์ด๋ฒ ๋ก๊ทธ์ธ ์ธ์ฆ ๋ฐฉ์์ ๋ด๊ฐ ๋ฆฌํฉํ ๋งํ๊ธฐ๋ก ํ๋ค.
์ ๋๋ก ๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ ๊ตฌํํด๋ณด๊ณ ์ถ์๋๋ฐ ๋์ผ ๋ญํค๋นํค๋ค๐

๊ทธ๋ฌ๋ฉด ์ด๋ป๊ฒ ํด๊ฒฐํ๋์ง ํฌ์คํ ํด๋ณด๋๋ก ํ๊ฒ ๋ค.
๋ก๊ทธ์ธ ์ธ์ฆ ๊ตฌ์กฐ
์ฐ์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ก์ธ์ค ํ ํฐ / ๋ฆฌํ๋ ์ ํ ํฐ ํ ์ธํธ๊ฐ ์๊ณ
๋ฆฌํ๋ ์ ํ ํฐ์ ์ด์ฉํด ์ก์ธ์ค ํ ํฐ์ ๊ฐฑ์ ํ๋ค๋ ๊ฒ์ด ๊ธฐ๋ณธ์ ์ธ ์ด๋ก ์ด๋ค.
๊ทธ๋ฆฌ๊ณ JWT ํ ํฐ์ ์ฟ ํค์ ์ ์ฅํด์ผ ๋ณด์์ฑ์ด ์ข๋ค๋๊ฒ๋,
(์ด์ ๋๋ ์๊ณ ์์๋ค)
๊ทธ๋ฌ๋ฉด ๊ธฐ์กด์ ์ฝ๋๋ ์ด๋ป๊ฒ ๊ตฌํ๋์์๊น? ์ด๋ค ๋ฌธ์ ๊ฐ ์์์๊น?
๋ฌธ์ ์ํฉ
๊ธฐ์กด์ ์๋น์ค ๋ก๊ทธ์ธ ์ธ์ฆ ๊ตฌ์กฐ์๋ ๋ค์๊ณผ ๊ฐ์ ๋ฌธ์ ๊ฐ ์์๋ค.
1. Access ํ ํฐ๊ณผ Refresh ํ ํฐ์ ๋ชจ๋ ์๋ต Body๋ก ์ ๋ฌ๋ฐ๊ณ ์์๋ค.
2. ๊ทธ๋ฆฌ๊ณ ์ด๋ ๊ฒ ์ ๋ฌ๋ฐ์ ํ ํฐ์ ํ๋ก ํธ์์ ๋ก์ปฌ์คํ ๋ฆฌ์ง์ ์ ์ฅํ๊ณ ์์๋ค.
3. RefreshToken์ ์ ํจ๊ธฐ๊ฐ์ด ์ง๋๋ฉด ๋ฌดํ ๋ก๋ฉ ๋ฐ ๋ก๊ทธ์์์ด ๋ถ๊ฐํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
4. ๋ฌผ๋ก ๋ณด์์์ ์ด์๋ ์์๋ค.
์ด๋ฌํ ๋ฌธ์ ๊ฐ ์์ด ์๋น์ค ์์ฒด ์ฌ์ฉ์๋ ๋ฌธ์ ๊ฐ ์๋ ์ํฉ์ด์๋ค.
(๋ฌดํ๋ก๋ฉ..)
๋ฌธ์ ํด๊ฒฐ ๊ณผ์
1. ๋ฉํ ๋๊ป ์ง๋ฌธ๋๋ฆฌ๊ธฐ
ํ ํฐ์ ์ฟ ํค์ ์ ์ฅํ๊ณ ์ถ์๋ค. ์ด๋ด ๊ฒฝ์ฐ ๋ฐฑ์๋์ ๊ตฌ์กฐ๋ ๋ฐ๊ฟ์ผ ํ๋๊ฒ์ผ๋ก ์๊ณ ์๊ธฐ์,
์ด๋ป๊ฒ ๋ณ๊ฒฝํ๋ฉด ์ข์์ง ๋ถํธ์บ ํ ์์ ๋ฉํ ๋๊ป ์ฐ๋ฝ์ ๋๋ ธ๋ค.

header์ ๋ด์์ ๋ณด๋ด๋๋ก ์์ ์ ํด์ค์ผ ํ๋ค๊ณ ํ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ฆฌํ๋ ์ํ ํฐ์ ํ๋ก ํธ๋ก ์ ๋ฌ์ ํด์ฃผ์ง ์๊ณ , ๋ฐฑ์๋ ์ชฝ์์๋ง ๊ฐ์ง๊ณ ์์ด์ผ ํ๋ค๊ณ ๋ต๋ณ์ฃผ์ จ๋ค.
๋๊ฐ ๋ค ์ฃผ๋๊ฒ์ ๊ด๋ฆฌ์ ๊ถํ์ ์ฃผ๋๊ฒ๊ณผ ๋์ผํ๋ค๊ณ ..
https://olrlobt.tistory.com/98
์ฐธ๊ณ ์๋ฃ๋ ์ฃผ์ จ๋ค


๊ทธ๋ ๋ค. ๋๋ค body๋ก ์ ๋ฌํ๋ฉด ์๋๋๊ฒ์ด์๋ค.
๊ทธ๋์ ๋ก๊ทธ์ธ ๋ฆฌํฉํ ๋ง ๋ด๋น ๋ฐฑ์๋ ํ์์๊ฒ ์ด ์๊ธฐ๋ฅผ ์ ๋ฌํ๊ณ ๋ ผ์๋ฅผํ์๋ค.
2. ์๋ฒ ๋ก์ง ๊ฐ์
ํ์ฌ ๋ฐฑ์๋ ํ์์ด ๋ก๊ทธ์ธ ๋ก์ง ๊ตฌ์กฐ๋ฅผ ๊ฐ์ ํด์ ์ค์ จ๋ค.
๋ก๊ทธ์ธ์ Access ํ ํฐ๊ณผ Refresh ํ ํฐ ๋ชจ๋ Body ๋ก ์๋ต
-> ๋ก๊ทธ์ธ์ Refresh ํ ํฐ์ ์ฟ ํค, Access ํ ํฐ์ ํค๋๋ก ๋ฃ์ด์ ์๋ต
์ด์ ํ๋ก ํธ๋ฅผ ๊ฐ์ ํ๋ฉด ๋๋ค.
3. ๊ธฐ์กด (๋ณ๊ฒฝ์ ) ๊ตฌ์กฐ
๋ด๊ฐ ๋ถ์ํ ๊ธฐ์กด์ ๊ตฌ์กฐ๋ฅผ ์์ฝํ๋ฉด ์ด๋ ๋ค.
RefreshToken์ ๋ก์ปฌ์คํ ๋ฆฌ์ง์ ์ ์ฅํ๊ณ ,
// callback.tsx
localStorage.setItem("accessToken", accessToken);
localStorage.setItem("refreshToken", refreshToken);
AccessToken ๋ง๋ฃ์ ๋ก์ปฌ ์คํ ๋ฆฌ์ง์ ์ ์ฅ๋์๋ RefreshToken์ ์ด์ฉํด์ ์ฌ์์ฒญ์ ๋ณด๋ธ๋ค.
// utill.ts
const refreshToken = localStorage.getItem("refreshToken");
if (!refreshToken) return Promise.reject(error);
try {
const response = await api.post<
AxiosResponse<ApiResponse<RefreshTokenResponse>>
>(ApiEndpotins.REFRESH_TOKEN, {
refreshToken,
});
๊ทธ ํ ๋ค์ ๋ฐ์์จ ํ ํฐ ์๋ต๋ค์ ๋ค์ ๋ก์ปฌ ์คํ ๋ฆฌ์ง์ ์ ์ฅํ๋ ๋ฐฉ์์ด์๋ค.
const { accessToken: newAccessToken, refreshToken: newRefreshToken } =
response.data.data;
localStorage.setItem("accessToken", newAccessToken);
localStorage.setItem("refreshToken", newRefreshToken);
๊ทธ๋ฌ๋ RefreshToken์ ์ ํจ๊ธฐ๊ฐ์ด ์ง๋๋ RefreshToken์ ๊ฐฑ์ ํ ๋ฐฉ๋ฒ์ด ์๋ค.
๊ณ์ ์ฃฝ์ ํ ํฐ์ ์ด์ฉํด ์์ธ์ค ํ ํฐ ์์ฒญ์ ๋ณด๋ด๋๊ฑฐ๋ค.
๋ก์ปฌ์คํ ๋ฆฌ์ง๋ ๋ธ๋ผ์ฐ์ ๋ฅผ ๋ซ๊ฑฐ๋ ์ฅ์น๋ฅผ ์ฌ๋ถํ ํด๋ ๋ฐ์ดํฐ๊ฐ ์ ์ง๋๊ธฐ์
์บ์๋ฅผ ์ง์ฐ์ง ์๋ ํ ์ ๋ฐ์ดํธ๊ฐ ๋์ง ์์๋ค.
๊ทธ๋ ๊ธฐ์ ๋ก๊ทธ์์๋ ๋ถ๊ฐํ ๋ฌดํ๋ก๋ฉ์ด ๋ฐ์ํ๋ค.
4. ๋ณ๊ฒฝํ ์ฝ๋ ๊ตฌ์กฐ
๋ฌผ๋ก ์ด ๋ถ๊ธฐ๋ฅผ ์ฌ์ ๋นํด์ ๋ก์ปฌ์คํ ๋ฆฌ์ง์์ ๊ด๋ฆฌํ ์ ์๋ ๋ฐฉ๋ฒ์ด ์๊ฒ ์ผ๋,
์ ์ ๊ฐ ์๋ ์๋น์ค์ ๋ก์ปฌ์คํ ๋ฆฌ์ง์ ๋ ํ ํฐ์ด ๋ ธ์ถ๋๋ ๋ฐฉ์์ด ๋ณด์์ ์ผ๋ก ๋ง์กฑ์ค๋ฝ์ง ์์๋ค.
(๊ทธ๋์ ์ฒ์์๋ ๋ฆฌํ๋ ์ ํ ํฐ์ ์ธ์
์ผ๋ก ์ฎ๊ฒจ์ ์์ ๋ธ๋ผ์ฐ์ ๋ซ์๋๋ง๋ค ๋ ๋ฆด๊น.. ํ๋ ๋ฐฉ์๋ ์๊ฐ์ํ๋ค)
๋ณด์์ด ํ์ํ๋ค๊ณ ๋๊ปด ์์ ์ฟ ํค ๊ด๋ฆฌ ๋ฐฉ์์ ์ ์ฉํด๋ณด๊ธฐ๋ก ํ์๋ค.
๋ฐฑ์๋ ํ์๋ ํ์พํ ใ ใ ๋ฅผ ์ธ์ณ์ฃผ์ ์ ๋์ฑ ๊ฐ์ฌํ๋ค.
// utill.ts
const axiosInstance = axios.create({
baseURL: domain,
timeout: 8000, // ์๋ต 8์ด ๋์ผ๋ฉด ์ค๋ฅ
withCredentials: true, // RefreshToken ์ฟ ํค ์๋ ์ ์ก
headers: {
},
});
์ฐ์ ๊ธฐ๋ณธ ์๋ต ์ธ์คํด์ค๋ฅผ ์์ ํ๋ค.
withCredentials: true ๋ฅผ ์ถ๊ฐํด์ ์ฟ ํค๋ฅผ ์ฌ์ฉํ๋๋ก ํ๋ค.
+) withCredentials: true๋?
Axios ์์ฒญ์์ ์ธ์ฆ ๊ด๋ จ ์ฟ ํค, ์๊ฒฉ ์ ๋ณด๋ฅผ ํจ๊ป ์ ์กํ๋๋ก ํ๋ ์ต์ .
HttpOnly๋ ์๋ฒ์์ ์ฟ ํค๋ฅผ๋ฐ๊ธํ ๋ ๋ฐ๋ก ์ค์ ํด์ฃผ์ด์ผํ๋ค.
// ์์
res.cookie("refreshToken", token, {
httpOnly: true, // ์๋ฐ์คํฌ๋ฆฝํธ์์ ์ ๊ทผ ๋ถ๊ฐ
secure: true, // HTTPS ํ๊ฒฝ์์๋ง ์ ์ก
sameSite: "none",
});
res.send({ success: true });
๊ทธ๋์ ์๋ฒ์์ HttpOnly๋ฅผ ์ค์ ํด์ฃผ๊ณ ,
ํด๋ผ์ด์ธํธ์์ withCredentials๋ฅผ ์ค์ ํด์ฃผ์ด์ผ ์์ ํ ์ฟ ํค ์ ์ก์ด ๊ฐ๋ฅํ๋ค.
๊ทธ ํ, ํค๋๋ก ๋ฐ์ ์ก์ธ์ค ํ ํฐ๋ง ๋ก์ปฌ์คํ ๋ฆฌ์ง์ ์ ์ฅํ๋๋ก ํ์๋ค.
// utill.ts
const newAccessToken = response.headers["accesstoken"];
if (newAccessToken) {
localStorage.setItem("accessToken", newAccessToken);
}
๊ทธ๋ฆฌ๊ณ ์ก์ธ์ค ํ ํฐ์ด ๋ง๋ฃ๋์์ ๊ฒฝ์ฐ ใ ก
์ฟ ํค์ ์๋ ๋ฆฌํ๋ ์ ํ ํฐ์ผ๋ก ์ก์ธ์ค ํ ํฐ์ ์ฌ์์ฒญํ๋ค.
// utill.ts - AccessToken ๋ง๋ฃ์ refresh ์์ฒญ
if (
errorMessage === "์ ํจํ์ง ์์ ํ ํฐ์
๋๋ค." &&
error.config.url !== ApiEndpotins.REFRESH_TOKEN
) {
try {
const refreshRes = await axiosInstance.post(
ApiEndpotins.REFRESH_TOKEN,
{ withCredentials: true }
);
๊ทธํ ์ด ์ฌ๋ฐ๊ธ ๋ฐ์ ์ก์ธ์ค ํ ํฐ์ ๋ค์ ์ ์ฅํ์ฌ
๋ค์ ์คํจํ API ์์ฒญ์ ์ฌ์๋ํ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ง์ฝ ๋ฆฌํ๋ ์ ํ ํฐ ๋ง์ ๋ง๋ฃ๋๋ค๋ฉด
catch (err) {
localStorage.removeItem("accessToken");
toast.showToast(
"error",
"์ธ์
์ด ๋ง๋ฃ๋์์ต๋๋ค. ๋ค์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.",
"auth"
);
return Promise.reject(error);
}
์ก์ธ์ค ํ ํฐ์ ์ง์ฐ๊ณ , ํ ์คํธ ๋ฉ์ธ์ง๋ฅผ ๋์ด๋ค.
๊ทธ๋ฆฌ๊ณ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ํธํ๋ค.
+)
๊ตฌํ์ ํ๋ ๊ณผ์ ์์ ์ฐพ์๋ณด๋ ๋ฆฌํ๋ ์ ํ ํฐ๋ ์ฐ์ฅํ๋ ๊ฐ๋ ์ด ์๋๋ฏ ํ๋ค.
ํ์ง๋ง ์ฐ๋ฆฌ๊ฐ ๊ตฌํํ๊ฑด ๋ฆฌํ๋ ์ ํ ํฐ์ด ๋ง๋ฃ๋๋ฉด ์๋ก ์ฌ๋ฐ๊ธํ๋ ๋ฐฉ์์ด๊ธฐ์
๊ทธ๋ฅ ๋ง๋ฃ์ ๋ค์ ๋ก๊ทธ์ธ์ ํจ์ผ๋ก์จ ์ฌ๋ฐ๊ธ ํ ์ ์๋๋ก ํด๋ผ์ด์ธํธ์์ ๊ตฌํ์ํ๋ค.
5. ๋ ๋ค๋ฅธ ๋ฌธ์
๊ทธ๋ฐ๋ฐ ๋ก๊ทธ์ธ์ ํ๋ฉด ๋ฐ๋ก ์ธ์ ์ด ๋ง๋ฃ๋์๋ค๋ฉด์ ๋ก๊ทธ์ธ์ฐฝ์ผ๋ก ๋์ด๊ฐ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
์ด์ ๊ฐ ๋ญ์ธ๊ณ , ํ๋
๋ก๊ทธ์ธ์ ํ๊ณ ์ฑ๊ณตํ ๊ฒฝ์ฐ -> ํ ํฐ์ ๊ฐ๊ฐ ์ฟ ํค์ ์คํ ๋ฆฌ์ง์ ์ ์ฅํ๋๋ฐ
๊ทธ ์คํ ์์ ๊ณผ
๋ก๊ทธ์ธ ์ฑ๊ณตํ ์ ์ ์ ๋ณด๋ฅผ ๋ถ๋ฌ์ค๋ ์์ฒญ์ ๋์์ ๋ณด๋ด๋ค๋ณด๋
์ ์ ์ ๋ณด์ ์์ฒญ์์ ํ ํฐ์ด ์์ด์ ๊ฑฐ๋ถ(!) 401 ๋นํ๋ฉด์ ๋ค์ ๋ก๊ทธ์ธ ํ๋ฉด์ผ๋ก ๋์์ค๋๊ฒ์ด์๋ค.
if (
errorMessage === "์ ํจํ์ง ์์ ํ ํฐ์
๋๋ค." &&
error.config.url !== ApiEndpotins.REFRESH_TOKEN &&
!error.config.url.includes("/auth/login") // โ
๋ก๊ทธ์ธ ์์ฒญ์ refresh ์๋ ๊ธ์ง
) {
๊ทธ๋์ utill.ts์ ์ด๋ฌํ ์ฝ๋๋ฅผ ํ ์ค ์ถ๊ฐํด์ฃผ๊ณ ,
if (isRegistered) {
getMe();
}
์ด๋ ๊ฒ ์ ์ ์ ๋ณด๋ฅผ ๋ฐ๋ก ๋ถ๋ฌ์ค๋ ๊ธฐ์กด์ ์ฝ๋๋ฅผ
// Callback.tsx
if (isRegistered) {
setTimeout(() => {
getMe();
}, 150);
}
์ด๋ ๊ฒ ์ฝ๊ฐ์ ๋๊ธฐ ํ ๋ถ๋ฌ์ฌ ์ ์๋๋ก ์์ ํ๋ค.
๊ทธ๋์ ์ง๊ธ์ ์ ์๋๋๋ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ!^_^
์ธ์ ๋ง๋ฃ ์๋ ๋ก๊ทธ์์๋ ์ ์๋ํ๋ค.

์ด๋ ๊ฒ ์ ์ฅ๋ ์ฟ ํค์ ๋ณด์ ์ฒดํฌ๋ ์ ๋๋ค.
๊ฐฑ์ ๋ก์ง ํ๋ฆ
1. AccessToken ๋ง๋ฃ ์
Axios ์ธํฐ์
ํฐ, 401 ์๋ต(์ ํจํ์ง ์์ ํ ํฐ์
๋๋ค.) ๊ฐ์ง
2. /auth/refresh ์์ฒญ ์ ์ก
(์ฟ ํค์ ๋ด๊ธด RefreshToken์ด ์๋ฒ๋ก ์๋ ์ ์ก)
3. ์๋ฒ, RefreshToken ๊ฒ์ฆ
์ ํจํ๋ฉด ์ AccessToken์ ์๋ต ํค๋(accesstoken)๋ก ์ ์ก
4. ํ๋ก ํธ, ์ AccessToken์ localStorage์ ์ ์ฅ
์คํจํ๋ API ์์ฒญ์ ์๋์ผ๋ก ์ฌ์๋
5. if RefreshToken์ด ๋ง๋ฃ๋ ๊ฒฝ์ฐ
/auth/refresh ์์ฒญ ์์ฒด๊ฐ 401 / 403์ผ๋ก ๊ฑฐ๋ถ
ํ๋ก ํธ, ํ ํฐ ์ญ์ ๋ฐ ๋ก๊ทธ์์ ์ฒ๋ฆฌ๋ก ์ ํ