Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(app): 회원 가입 완료시 JWT 토큰을 쿠키로 저장하고 인증이 필요한 요청에 Bearer 헤더를 설정하도록 합니다. #140

Merged
merged 6 commits into from
Sep 21, 2023
20 changes: 20 additions & 0 deletions .pnp.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
Binary file not shown.
2 changes: 2 additions & 0 deletions apps/react-world/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
"@tanstack/react-query-devtools": "^4.35.3",
"@types/axios": "^0.14.0",
"axios": "^1.5.0",
"js-cookie": "^3.0.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.15.0",
"zustand": "^4.4.1"
},
"devDependencies": {
"@types/js-cookie": "^3",
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^6.4.1",
Expand Down
19 changes: 13 additions & 6 deletions apps/react-world/src/apis/apiInstances.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { AxiosRequestConfig } from 'axios';
import axios from 'axios';
import Cookies from 'js-cookie';

// TODO: 향후 .env 파일로 분리
const BASE_URL = 'https://api.realworld.io/api';
Expand All @@ -12,14 +13,20 @@ const axiosApi = (url: string, options?: AxiosRequestConfig) => {

// POST, DELETE 등 요청 시 인증이 필요한 경우
const axiosAuthApi = (url: string, options?: AxiosRequestConfig) => {
const jwtToken = '토큰 값'; // TODO: 향후 개발
const instance = axios.create({
baseURL: url,
headers: { Authorization: 'Bearer ' + jwtToken },
...options,
});
const instance = axios.create({ baseURL: url, ...options });
return instance;
};

export const api = axiosApi(BASE_URL);
export const authApi = axiosAuthApi(BASE_URL);

// 요청 전 JWT 쿠키 유무를 확인 후 Bearer 헤더 설정
authApi.interceptors.request.use(config => {
const jwtToken = Cookies.get('jwtToken');

if (jwtToken) {
config.headers.Authorization = 'Bearer ' + jwtToken; // 토큰이 있을 때 헤더에 추가
}

return config;
});
2 changes: 2 additions & 0 deletions apps/react-world/src/apis/register/RegisterService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ class RegisterService {
return response.data;
} catch (error) {
if (isAxiosError(error) && error.response) {
console.error('Axios error occurred:', error.response.data);
throw error.response.data;
}
console.error('An unexpected error occurred:', error);
throw error;
}
}
Expand Down
11 changes: 7 additions & 4 deletions apps/react-world/src/hooks/useRegister.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
RegisterUserResponse,
} from '../apis/register/RegisterService.types';
import RegisterService from '../apis/register/RegisterService';
import { saveTokenToCookie } from '@utils/jwtUtils';

const useRegister = () => {
const [userData, setUserData] = useState<RegisterUserParams>({
Expand All @@ -31,14 +32,14 @@ const useRegister = () => {
const response = await RegisterService.registerUser(userData);
console.log('response: ' + JSON.stringify(response, null, 2));

// TODO: 임시로 로컬 스토리지에 토큰 저장
localStorage.setItem('jwtToken', response.user.token);
// JWT 토큰을 쿠키에 저장
if (response && response.user && response.user.token) {
saveTokenToCookie(response.user.token);
}

setIsLoading(false);
return response;
} catch (error) {
setIsLoading(false);

// TODO: 로그 제거
console.log('error: ' + JSON.stringify(error, null, 2));
if (error && typeof error === 'object' && 'errors' in error) {
Expand All @@ -47,6 +48,8 @@ const useRegister = () => {
console.error('An unexpected error occurred:', error);
}
return null;
} finally {
setIsLoading(false);
}
};

Expand Down
8 changes: 8 additions & 0 deletions apps/react-world/src/utils/jwtUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Cookies from 'js-cookie';

export const saveTokenToCookie = (token: string) => {
Cookies.set('jwtToken', token, {
secure: true, // HTTPS에서만 쿠키 전송
sameSite: 'lax', // CSRF 공격 방지를 위한 설정
});
};
16 changes: 16 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,7 @@ __metadata:
"@tanstack/react-query": ^4.35.3
"@tanstack/react-query-devtools": ^4.35.3
"@types/axios": ^0.14.0
"@types/js-cookie": ^3
"@types/react": ^18.2.15
"@types/react-dom": ^18.2.7
"@typescript-eslint/eslint-plugin": ^6.4.1
Expand All @@ -826,6 +827,7 @@ __metadata:
eslint-plugin-prettier: ^5.0.0
eslint-plugin-react: ^7.33.2
eslint-plugin-react-hooks: ^4.6.0
js-cookie: ^3.0.5
react: ^18.2.0
react-dom: ^18.2.0
react-router-dom: ^6.15.0
Expand Down Expand Up @@ -916,6 +918,13 @@ __metadata:
languageName: node
linkType: hard

"@types/js-cookie@npm:^3":
version: 3.0.4
resolution: "@types/js-cookie@npm:3.0.4"
checksum: 46ac93974776a256f3cedadf60b45ded4d905a5e69986882d8c17baa351cb2e81a691864a1f19c3ca90eaa2cb3eeb7cb5426416b487a7d54cf5ff278d39d7870
languageName: node
linkType: hard

"@types/json-schema@npm:^7.0.12":
version: 7.0.12
resolution: "@types/json-schema@npm:7.0.12"
Expand Down Expand Up @@ -3107,6 +3116,13 @@ __metadata:
languageName: node
linkType: hard

"js-cookie@npm:^3.0.5":
version: 3.0.5
resolution: "js-cookie@npm:3.0.5"
checksum: 2dbd2809c6180fbcf060c6957cb82dbb47edae0ead6bd71cbeedf448aa6b6923115003b995f7d3e3077bfe2cb76295ea6b584eb7196cca8ba0a09f389f64967a
languageName: node
linkType: hard

"js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0":
version: 4.0.0
resolution: "js-tokens@npm:4.0.0"
Expand Down