-
Notifications
You must be signed in to change notification settings - Fork 1
Fix(client): 온보딩 토큰 저장 로직 수정 #224
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
Changes from all commits
7604fb4
e7af0c2
bb5130a
ae7f5e8
231a7b4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,7 +26,14 @@ const GoogleCallback = () => { | |
| if (isUser) { | ||
| if (accessToken) { | ||
| localStorage.setItem('token', accessToken); | ||
|
|
||
| if (typeof chrome !== 'undefined' && chrome.storage?.local) { | ||
| chrome.storage.local.set({ token: accessToken }, () => { | ||
| console.log('Token saved to chrome storage'); | ||
| }); | ||
| } | ||
|
Comment on lines
+30
to
+34
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 크게 console이 필요없다면 지워도 될 것 같아요! |
||
| } | ||
|
|
||
| navigate('/'); | ||
| } else { | ||
| navigate('/onboarding?step=ALARM'); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -80,6 +80,11 @@ export const usePostSignUp = () => { | |
| }; | ||
| if (newToken) { | ||
| localStorage.setItem('token', newToken); | ||
| if (typeof chrome !== 'undefined' && chrome.storage?.local) { | ||
| chrome.storage.local.set({ token: newToken }, () => { | ||
| console.log('Token saved to chrome storage'); | ||
| }); | ||
| } | ||
|
Comment on lines
81
to
+87
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
스프린트 작업 대충 끝나면 Storage를 class형태나 util로 빼서 재사용 하도록 만드는 것이 좋을 것 같은데 어떻게 생각하시나요? |
||
| sendTokenToExtension(newToken); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -8,55 +8,11 @@ const apiRequest = axios.create({ | |||||||||||||||||||
| }, | ||||||||||||||||||||
| }); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| const refreshToken = async (email: string) => { | ||||||||||||||||||||
| try { | ||||||||||||||||||||
| const response = await axios.get( | ||||||||||||||||||||
| `${import.meta.env.VITE_BASE_URL}/api/v1/auth/token`, | ||||||||||||||||||||
| { | ||||||||||||||||||||
| params: { email }, | ||||||||||||||||||||
| } | ||||||||||||||||||||
| ); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| const newToken = response.data.data?.token || response.data.token; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if (newToken) { | ||||||||||||||||||||
| localStorage.setItem('token', newToken); | ||||||||||||||||||||
| return newToken; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| throw new Error('토큰 재발급 실패'); | ||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||
| console.error('토큰 재발급 실패:', error); | ||||||||||||||||||||
| throw error; | ||||||||||||||||||||
| } | ||||||||||||||||||||
| }; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // 요청 인터셉터 | ||||||||||||||||||||
| apiRequest.interceptors.request.use(async (config) => { | ||||||||||||||||||||
| const noAuthNeeded = [ | ||||||||||||||||||||
| '/api/v1/auth/token', | ||||||||||||||||||||
| '/api/v2/auth/signup', | ||||||||||||||||||||
| '/api/v2/auth/google', | ||||||||||||||||||||
| ]; | ||||||||||||||||||||
| const isNoAuth = noAuthNeeded.some((url) => config.url?.includes(url)); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if (!isNoAuth) { | ||||||||||||||||||||
| let token = localStorage.getItem('token'); | ||||||||||||||||||||
| const email = localStorage.getItem('email'); | ||||||||||||||||||||
| if (email) { | ||||||||||||||||||||
| try { | ||||||||||||||||||||
| token = await refreshToken(email); | ||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||
| console.error('요청 인터셉터에서 토큰 재발급 실패:', err); | ||||||||||||||||||||
| localStorage.removeItem('token'); | ||||||||||||||||||||
| window.location.href = '/onboarding'; | ||||||||||||||||||||
| throw err; | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } else { | ||||||||||||||||||||
| console.error('토큰이 없습니다. 온보딩을 먼저 완료해주세요.'); | ||||||||||||||||||||
| throw new Error('토큰이 없습니다. 온보딩을 먼저 완료해주세요.'); | ||||||||||||||||||||
| } | ||||||||||||||||||||
| const token = localStorage.getItem('token'); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if (token) { | ||||||||||||||||||||
| config.headers.Authorization = `Bearer ${token}`; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
@@ -68,11 +24,13 @@ apiRequest.interceptors.response.use( | |||||||||||||||||||
| (response) => response, | ||||||||||||||||||||
| async (error) => { | ||||||||||||||||||||
| const originalRequest = error.config; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| const noAuthNeeded = [ | ||||||||||||||||||||
| '/api/v1/auth/token', | ||||||||||||||||||||
| '/api/v2/auth/signup', | ||||||||||||||||||||
| '/api/v2/auth/google', | ||||||||||||||||||||
| ]; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| const isNoAuth = noAuthNeeded.some((url) => | ||||||||||||||||||||
| originalRequest.url?.includes(url) | ||||||||||||||||||||
| ); | ||||||||||||||||||||
|
|
@@ -85,25 +43,10 @@ apiRequest.interceptors.response.use( | |||||||||||||||||||
| ) { | ||||||||||||||||||||
| originalRequest._retry = true; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| try { | ||||||||||||||||||||
| const email = localStorage.getItem('email'); | ||||||||||||||||||||
| localStorage.removeItem('token'); | ||||||||||||||||||||
| window.location.href = '/onboarding?step=SOCIAL_LOGIN'; | ||||||||||||||||||||
|
Comment on lines
+46
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Chrome storage도 함께 정리해야 합니다. 이 PR은 온보딩 시 토큰을 localStorage와 chrome.storage 양쪽에 저장하도록 변경합니다. 하지만 인증 오류(401/403) 발생 시 localStorage만 삭제하고 chrome.storage는 정리하지 않아 두 저장소 간 불일치가 발생합니다. 🔎 chrome.storage도 정리하는 수정안 originalRequest._retry = true;
localStorage.removeItem('token');
+ if (typeof chrome !== 'undefined' && chrome.storage?.local) {
+ chrome.storage.local.remove('token', () => {
+ console.log('Token removed from chrome storage');
+ });
+ }
window.location.href = '/onboarding?step=SOCIAL_LOGIN';
return Promise.reject(error);또는 이전 리뷰 코멘트에서 제안된 중앙화된 유틸리티 함수를 사용하는 것을 권장합니다: // shared/utils/storage.ts에 추가
export const removeToken = async (): Promise<void> => {
localStorage.removeItem('token');
if (typeof chrome !== 'undefined' && chrome.storage?.local) {
try {
await chrome.storage.local.remove('token');
console.log('Token removed from chrome storage');
} catch (error) {
console.error('Chrome storage 삭제 실패:', error);
}
}
};그 다음 이 파일에서: originalRequest._retry = true;
- localStorage.removeItem('token');
+ await removeToken();
window.location.href = '/onboarding?step=SOCIAL_LOGIN';
return Promise.reject(error);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
|
|
||||||||||||||||||||
| if (email) { | ||||||||||||||||||||
| const newToken = await refreshToken(email); | ||||||||||||||||||||
| originalRequest.headers.Authorization = `Bearer ${newToken}`; | ||||||||||||||||||||
| return apiRequest(originalRequest); | ||||||||||||||||||||
| } else { | ||||||||||||||||||||
| console.error( | ||||||||||||||||||||
| '사용자 이메일이 없습니다. 온보딩을 다시 완료해주세요.' | ||||||||||||||||||||
| ); | ||||||||||||||||||||
| localStorage.removeItem('token'); | ||||||||||||||||||||
| window.location.href = '/onboarding'; | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } catch (refreshError) { | ||||||||||||||||||||
| console.error('토큰 재발급 실패:', refreshError); | ||||||||||||||||||||
| localStorage.removeItem('token'); | ||||||||||||||||||||
| window.location.href = '/onboarding'; | ||||||||||||||||||||
| } | ||||||||||||||||||||
| return Promise.reject(error); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| return Promise.reject(error); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,75 +7,64 @@ const apiRequest = axios.create({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const fetchToken = async (email?: string) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = await axios.get( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `${import.meta.env.VITE_BASE_URL}/api/v1/auth/token`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params: { email }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const newToken = response.data.data.token; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| chrome.storage.local.set({ token: newToken }, () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log('Token re-saved to chrome storage'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return newToken; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| apiRequest.interceptors.request.use(async (config) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const noAuthNeeded = ['/api/v1/auth/token', '/api/v1/auth/signup']; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isNoAuth = noAuthNeeded.some((url) => config.url?.includes(url)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (isNoAuth) return config; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const email = await new Promise<string | undefined>((resolve) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| chrome.storage.local.get('email', (result) => resolve(result.email)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let token = await new Promise<string | undefined>((resolve) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const getTokenFromStorage = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return new Promise<string | undefined>((resolve) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| chrome.storage.local.get('token', (result) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| resolve(result.token); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+10
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 런타임 안전성 검사 및 에러 핸들링이 누락되었습니다. PR objectives에서 명시한 대로 🔎 제안하는 수정안 const getTokenFromStorage = () => {
return new Promise<string | undefined>((resolve, reject) => {
+ if (typeof chrome === 'undefined' || !chrome.storage?.local) {
+ resolve(undefined);
+ return;
+ }
+
- chrome.storage.local.get('token', (result) => {
+ chrome.storage.local.get('token', (result) => {
+ if (chrome.runtime.lastError) {
+ reject(chrome.runtime.lastError);
+ return;
+ }
resolve(result.token);
});
});
};🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!isNoAuth) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (email) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| token = await fetchToken(email); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error('요청 인터셉터에서 토큰 재발급 실패:', err); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| localStorage.removeItem('token'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| window.location.href = '/onboarding'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw err; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error('토큰이 없습니다. 온보딩을 먼저 완료해주세요.'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| apiRequest.interceptors.request.use(async (config) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const token = await getTokenFromStorage(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (token) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| config.headers.Authorization = `Bearer ${token}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return config; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: 환경변수로 분리 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // eslint-disable-next-line turbo/no-undeclared-env-vars | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const onboardingUrl = import.meta.env.DEV | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? 'http://localhost:5173/onboarding?step=SOCIAL_LOGIN' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : 'https://pinback.today/onboarding?step=SOCIAL_LOGIN'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let isRedirecting = false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 리다이렉트 플래그에 잠재적 경쟁 조건이 있습니다. 모듈 레벨의 💡 개선 제안-let isRedirecting = false;
+let redirectPromise: Promise<void> | null = null;그리고 응답 인터셉터에서: - if (!isRedirecting) {
- isRedirecting = true;
+ if (!redirectPromise) {
+ redirectPromise = new Promise((resolve) => {
+ chrome.storage.local.remove(['token', 'email'], () => {});
- chrome.storage.local.remove(['token', 'email'], () => {});
-
- chrome.tabs.create({ url: onboardingUrl }, () => {
- setTimeout(() => {
- isRedirecting = false;
- }, 2000);
- });
+ chrome.tabs.create({ url: onboardingUrl }, () => {
+ redirectPromise = null;
+ resolve();
+ });
+ });
}
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| apiRequest.interceptors.response.use( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (response) => response, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async (error) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const originalRequest = error.config; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const noAuthNeeded = ['/api/v1/auth/token', '/api/v1/auth/signup']; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const noAuthNeeded = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| '/api/v1/auth/token', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| '/api/v1/auth/signup', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| '/api/v2/auth/google', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isNoAuth = noAuthNeeded.some((url) => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| originalRequest.url?.includes(url) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error.response && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (error.response.status === 401 || error.response.status === 403) && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !originalRequest._retry && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !isNoAuth | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| originalRequest._retry = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const newToken = await fetchToken('[email protected]'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| originalRequest.headers.Authorization = `Bearer ${newToken}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return apiRequest(originalRequest); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!isRedirecting) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isRedirecting = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| chrome.storage.local.remove(['token', 'email'], () => {}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| chrome.tabs.create({ url: onboardingUrl }, () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setTimeout(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isRedirecting = false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, 2000); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+55
to
+65
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Chrome API 호출에 에러 핸들링과 런타임 검사가 누락되었습니다.
🔎 제안하는 수정안 if (!isRedirecting) {
isRedirecting = true;
- chrome.storage.local.remove(['token', 'email'], () => {});
+ if (typeof chrome !== 'undefined' && chrome.storage?.local) {
+ chrome.storage.local.remove(['token', 'email'], () => {
+ if (chrome.runtime.lastError) {
+ console.error('토큰 제거 실패:', chrome.runtime.lastError);
+ }
+ });
+ }
- chrome.tabs.create({ url: onboardingUrl }, () => {
- setTimeout(() => {
- isRedirecting = false;
- }, 2000);
- });
+ if (typeof chrome !== 'undefined' && chrome.tabs?.create) {
+ chrome.tabs.create({ url: onboardingUrl }, () => {
+ if (chrome.runtime.lastError) {
+ console.error('탭 생성 실패:', chrome.runtime.lastError);
+ }
+ setTimeout(() => {
+ isRedirecting = false;
+ }, 2000);
+ });
+ } else {
+ isRedirecting = false;
+ }
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Promise.reject(error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
크론도 따로 추가해줘야하는지 몰랐어요..반성하겠습니다