Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 83 additions & 6 deletions package-lock.json

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

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
"imagemin-webp": "^8.0.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-router-dom": "^7.7.0",
"tailwindcss": "^4.1.11"
"react-router-dom": "^7.7.0"
},
"devDependencies": {
"@eslint/js": "^9.30.1",
Expand All @@ -29,10 +28,13 @@
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@vitejs/plugin-react": "^4.7.0",
"autoprefixer": "^10.4.21",
"eslint": "^9.30.1",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.3.0",
"postcss": "^8.5.6",
"tailwindcss": "^4.1.12",
"typescript": "~5.8.3",
"typescript-eslint": "^8.35.1",
"vite": "^7.0.4",
Expand Down
2 changes: 1 addition & 1 deletion src/api/PostOAuth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default function PostOAuth() {

const data: { accessToken: string; isOnboarded: boolean } = await res.json();

localStorage.setItem('accessToken', data.accessToken);
sessionStorage.setItem('accessToken', data.accessToken);

if (data.isOnboarded) {
navigate('/main', { replace: true });
Expand Down
2 changes: 1 addition & 1 deletion src/api/fetchChapters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Chapter } from '../types/@common/chapter';
import { transformChapters } from '../utils/transformChapter';

export default async function fetchChapters(): Promise<Chapter[]> {
const accessToken = localStorage.getItem('accessToken');
const accessToken = sessionStorage.getItem('accessToken');
try {
const response = await fetch(`https://grav-it.inuappcenter.kr/api/v1/learning/chapters`, {
method: 'GET',
Expand Down
2 changes: 1 addition & 1 deletion src/api/fetchMainInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ApiError } from '../types/@common/api';
import type MainPageResponse from '../types/api/main';

export default async function fetchMainInfo(): Promise<MainPageResponse> {
const accessToken = localStorage.getItem('accessToken');
const accessToken = sessionStorage.getItem('accessToken');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Guard sessionStorage for SSR

Avoid accessing Web APIs during SSR.

-    const accessToken = sessionStorage.getItem('accessToken');
+    const accessToken = typeof window !== 'undefined' ? sessionStorage.getItem('accessToken') : null;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const accessToken = sessionStorage.getItem('accessToken');
const accessToken = typeof window !== 'undefined'
? sessionStorage.getItem('accessToken')
: null;
🤖 Prompt for AI Agents
In src/api/fetchMainInfo.ts around line 6, the direct call to
sessionStorage.getItem('accessToken') runs during SSR and can crash; guard
access by checking that window (or sessionStorage) is defined before calling
getItem (e.g., if (typeof window !== 'undefined' && window.sessionStorage) { ...
}) and provide a safe fallback (null or an empty string) when unavailable so the
function behaves correctly both in browser and server environments.


try {
const response = await fetch(API_ENDPOINTS.main.base, {
Expand Down
28 changes: 28 additions & 0 deletions src/api/getMypage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { API_ENDPOINTS } from '../constants/api';

export const getMypage = async () => {
const accessToken = sessionStorage.getItem('accessToken');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Guard missing token; tighten headers

Avoid sending Authorization: Bearer null and prefer Accept for GET.

   const accessToken = sessionStorage.getItem('accessToken');
+  if (!accessToken) {
+    throw new Error('Missing access token');
+  }

   try {
     const response = await fetch(`${API_ENDPOINTS.users.base}/my-page`, {
       method: 'GET',
       headers: {
-        'Authorization': `Bearer ${accessToken}`,
-        'Content-Type': 'application/json',
+        Authorization: `Bearer ${accessToken}`,
+        Accept: 'application/json',
       },
       credentials: 'include', 
     });

Also applies to: 9-14

🤖 Prompt for AI Agents
In src/api/getMypage.ts around line 4 (and also lines 9-14), avoid sending
Authorization: Bearer null and use Accept for GET requests: first read the token
and guard it (if no token, handle unauthenticated flow or return early) and only
add the Authorization header when token is a non-empty string; for the GET fetch
call replace Content-Type with an Accept: application/json header and ensure the
headers object is built conditionally so Authorization is omitted when token is
missing.


try {
const response = await fetch(`${API_ENDPOINTS.users.base}/my-page`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
credentials: 'include',
});

if (!response.ok) {
const errorData = await response.json();
console.error('Error response:', response.status, errorData);
throw new Error(`HTTP error! status: ${response.status}`);
}

const data = await response.json();
return data;
} catch (error) {
console.error('Fetch error:', error);
throw error;
}
};
30 changes: 0 additions & 30 deletions src/api/getMypage.tsx

This file was deleted.

29 changes: 29 additions & 0 deletions src/api/getTierLeagues.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { API_ENDPOINTS } from '../constants/api';

export const getTierLeagues = async (id: number, num: number) => {
const accessToken = sessionStorage.getItem('accessToken');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Token guard + lean GET headers

Same fix as other APIs.

   const accessToken = sessionStorage.getItem('accessToken');
+  if (!accessToken) {
+    throw new Error('Missing access token');
+  }
@@
       headers: {
-        'Authorization': `Bearer ${accessToken}`,
-        'Content-Type': 'application/json',
+        Authorization: `Bearer ${accessToken}`,
+        Accept: 'application/json',
       },
       credentials: 'include', 

Also applies to: 9-14

🤖 Prompt for AI Agents
In src/api/getTierLeagues.ts around lines 4 and 9-14, add a guard for missing
accessToken and simplify the GET request headers: check
sessionStorage.getItem('accessToken') and if falsy return or throw a clear error
(e.g., throw new Error('No access token')), then call fetch with method: 'GET'
and a minimal headers object including Authorization: `Bearer ${accessToken}`
and Accept: 'application/json' (remove any unnecessary headers/body), ensuring
early exit on missing token to prevent unauthenticated requests.


try {
const response = await fetch(API_ENDPOINTS.leagues.tier(id, num), {
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
credentials: 'include',
});

if (!response.ok) {
const errorData = await response.json();
console.error('Error response:', response.status, errorData);
throw new Error(`HTTP error! status: ${response.status}`);
}

const data = await response.json();
console.log(data);
return data;
} catch (error) {
console.error('Fetch error:', error);
throw error;
}
};
29 changes: 29 additions & 0 deletions src/api/getUserLeagues.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { API_ENDPOINTS } from '../constants/api';

export const getUserLeagues = async (num: number) => {
const accessToken = sessionStorage.getItem('accessToken');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Token guard + lean GET headers

Avoid Bearer null and prefer Accept header.

   const accessToken = sessionStorage.getItem('accessToken');
+  if (!accessToken) {
+    throw new Error('Missing access token');
+  }
 
   try {
     const response = await fetch(API_ENDPOINTS.leagues.user(num), {
       method: 'GET',
       headers: {
-        'Authorization': `Bearer ${accessToken}`,
-        'Content-Type': 'application/json',
+        Authorization: `Bearer ${accessToken}`,
+        Accept: 'application/json',
       },
       credentials: 'include',
     });

Also applies to: 9-14

🤖 Prompt for AI Agents
In src/api/getUserLeagues.ts around lines 4 and also lines 9-14, the code reads
the accessToken from sessionStorage and may send an Authorization header
containing "Bearer null"; update the token guard so you check that accessToken
is truthy and not the string "null" before using it (return or throw / handle
unauthenticated case early), and when constructing the GET request headers
always include Accept: application/json and add Authorization: `Bearer
${accessToken}` only when the token passes the guard; ensure no Authorization
header is sent if token is missing.


try {
const response = await fetch(API_ENDPOINTS.leagues.user(num), {
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
credentials: 'include',
});

if (!response.ok) {
const errorData = await response.json();
console.error('Error response:', response.status, errorData);
throw new Error(`HTTP error! status: ${response.status}`);
}

const data = await response.json();
console.log(data);
return data;
} catch (error) {
console.error('Fetch error:', error);
throw error;
}
};
2 changes: 1 addition & 1 deletion src/api/patchOnBoarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { AxiosResponse } from 'axios';
import { API_ENDPOINTS } from '../constants/api';

export const PatchOnBoarding = async (nickname: string, profilePhotoNumber: number) => {
const accessToken = localStorage.getItem('accessToken');
const accessToken = sessionStorage.getItem('accessToken');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid sending “Bearer null” when token is missing.

Guard early and surface an actionable error (or redirect) instead of hitting the API with an invalid header.

-const accessToken = sessionStorage.getItem('accessToken');
+const accessToken = sessionStorage.getItem('accessToken');
+if (!accessToken) {
+  throw new Error('Unauthenticated: missing access token. Please log in again.');
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const accessToken = sessionStorage.getItem('accessToken');
const accessToken = sessionStorage.getItem('accessToken');
if (!accessToken) {
throw new Error('Unauthenticated: missing access token. Please log in again.');
}
🤖 Prompt for AI Agents
In src/api/patchOnBoarding.tsx around line 6, the code reads the accessToken
from sessionStorage but may send an Authorization header like "Bearer null" when
missing; change the flow to guard immediately after getting the token—if
accessToken is null/undefined/empty then either throw a clear error or redirect
to the login page and do not call the API; when building request headers only
include Authorization when a valid token exists (e.g., add header conditionally
or return early), and ensure any error paths surface an actionable message for
the caller.


try {
const response: AxiosResponse = await axios.patch(
Expand Down
Loading