β νλ‘ νΈμλ μμ μ¬ν π±
- λ‘κ·ΈμΈ μλ΅ κ΅¬μ‘° λ³κ²½ β νμ
μ΄μ : { "userId": 12345, "jwtToken": "eyJhbG...", "isNewUser": false, "isOnboardingCompleted": true }
λ³κ²½ ν: { "userId": 12345, "accessToken": "eyJhbG...", "refreshToken": "eyJhbG...", "isNewUser": false, "isOnboardingCompleted": true }
μμ νμ:
- jwtToken β accessToken + refreshToken μΌλ‘ λ³κ²½
- λ‘컬 μ€ν 리μ§/AsyncStorageμ λ κ°μ ν ν° λͺ¨λ μ μ₯
// Before localStorage.setItem('token', response.jwtToken);
// After localStorage.setItem('accessToken', response.accessToken); localStorage.setItem('refreshToken', response.refreshToken);
- ν ν° μ¬λ°κΈ λ‘μ§ μΆκ° β νμ
μλ‘μ΄ μλν¬μΈνΈ: POST /v1/auth/refresh
Request: { "refreshToken": "eyJhbG..." }
Response: { "accessToken": "eyJhbG...", // νμ μλ‘ λ°κΈ "refreshToken": "eyJhbG..." // 7μΌ μ΄ν λ¨μΌλ©΄ μλ‘ λ°κΈ, μλλ©΄ null }
ꡬν λ°©λ² (Axios μΈν°μ ν° μμ): // API μΈν°μ ν° μ€μ axios.interceptors.response.use( response => response, async error => { const originalRequest = error.config;
// 401 μλ¬μ΄κ³ , μ¬μλκ° μλ κ²½μ°
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
const refreshToken = localStorage.getItem('refreshToken');
// 리νλ μ ν ν°μΌλ‘ μ μ‘μΈμ€ ν ν° λ°κΈ
const response = await axios.post('/v1/auth/refresh', {
refreshToken
});
const { accessToken, refreshToken: newRefreshToken } = response.data.data;
// μ μ‘μΈμ€ ν ν° μ μ₯
localStorage.setItem('accessToken', accessToken);
// 리νλ μ ν ν°λ κ°±μ λμμΌλ©΄ μ μ₯
if (newRefreshToken) {
localStorage.setItem('refreshToken', newRefreshToken);
}
// μλ μμ² μ¬μλ
originalRequest.headers.Authorization = `Bearer ${accessToken}`;
return axios(originalRequest);
} catch (refreshError) {
// 리νλ μ μ€ν¨ β λ‘κ·ΈμΈ νμ΄μ§λ‘
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
window.location.href = '/login';
return Promise.reject(refreshError);
}
}
return Promise.reject(error);
}
);
- λ‘κ·Έμμ λ‘μ§ μΆκ° β νμ
μλ‘μ΄ μλν¬μΈνΈ: POST /v1/auth/logout (μΈμ¦ νμ)
Request: μμ (Authorization ν€λλ§ νμ)
ꡬν: const logout = async () => { try { const accessToken = localStorage.getItem('accessToken');
// μλ²μ λ‘κ·Έμμ μμ²
await axios.post('/v1/auth/logout', {}, {
headers: {
Authorization: `Bearer ${accessToken}`
}
});
} catch (error) {
console.error('λ‘κ·Έμμ μ€ν¨:', error);
} finally {
// λ‘컬 ν ν° μμ (μλ² μμ² μ€ν¨ν΄λ μμ )
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
// λ‘κ·ΈμΈ νμ΄μ§λ‘ μ΄λ
window.location.href = '/login';
}
};
- API μμ² μ ν€λ λ³κ²½
λ³κ²½ μ :
headers: {
Authorization: Bearer ${localStorage.getItem('token')}
}
λ³κ²½ ν:
headers: {
Authorization: Bearer ${localStorage.getItem('accessToken')}
}
μμ μ°μ μμ
π΄ νμ (λΉμ₯ μμ νμ)
- β λ‘κ·ΈμΈ μλ΅μμ jwtToken β accessToken, refreshToken λΆλ¦¬
- β λ ν ν° λͺ¨λ μ μ₯νλλ‘ μμ
- β API μμ² μ accessToken μ¬μ©
π‘ μ€μ (λΉ λ₯΄κ² μΆκ° κΆμ₯)
- β ν ν° μ¬λ°κΈ μΈν°μ ν° κ΅¬ν
- β λ‘κ·Έμμ API νΈμΆ
π’ μ ν (μΆν κ°μ )
- ν ν° λ§λ£ μκ° μ²΄ν¬ ν μ¬μ κ°±μ (UX κ°μ )
- 리νλ μ ν ν°λ λ§λ£λλ©΄ "μΈμ λ§λ£" λ©μμ§ νμ
React Native μμ (AsyncStorage)
import AsyncStorage from '@react-native-async-storage/async-storage';
// λ‘κ·ΈμΈ const handleLogin = async (response) => { await AsyncStorage.setItem('accessToken', response.accessToken); await AsyncStorage.setItem('refreshToken', response.refreshToken); };
// API νΈμΆ
const accessToken = await AsyncStorage.getItem('accessToken');
axios.defaults.headers.common['Authorization'] = Bearer ${accessToken};
// λ‘κ·Έμμ await AsyncStorage.multiRemove(['accessToken', 'refreshToken']);