Skip to content

Commit 36bd981

Browse files
authored
Merge pull request #150 from ITZipProject/main
# 1/11 까지의 작업내용 머지
2 parents bda5eea + d8540b5 commit 36bd981

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+3058
-1643
lines changed

package-lock.json

Lines changed: 340 additions & 484 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,16 @@
3636
"framer-motion": "^11.11.11",
3737
"install": "^0.13.0",
3838
"iron-session": "^8.0.2",
39-
"jotai": "^2.9.0",
39+
"jotai": "^2.11.0",
4040
"js-cookie": "^3.0.5",
41+
"jwt-decode": "^4.0.0",
4142
"lodash": "^4.17.21",
4243
"lucide-react": "^0.446.0",
4344
"next": "^14.2.15",
4445
"npm": "^10.8.3",
4546
"prisma": "^5.16.1",
4647
"react": "^18",
48+
"react-cookie": "^7.2.2",
4749
"react-dom": "^18",
4850
"react-hook-form": "^7.52.1",
4951
"react-hot-toast": "^2.4.1",
@@ -87,7 +89,7 @@
8789
"eslint-plugin-prettier": "^5.2.1",
8890
"eslint-plugin-storybook": "^0.8.0",
8991
"eslint-plugin-tailwindcss": "^3.17.4",
90-
"msw": "^1.3.4",
92+
"msw": "^2.6.8",
9193
"postcss": "^8",
9294
"remark-gfm": "^4.0.0",
9395
"storybook": "^8.1.11",

public/mockServiceWorker.js

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
* - Please do NOT serve this file on production.
99
*/
1010

11-
const PACKAGE_VERSION = '2.4.9'
12-
const INTEGRITY_CHECKSUM = '26357c79639bfa20d64c0efca2a87423'
11+
const PACKAGE_VERSION = '2.6.8'
12+
const INTEGRITY_CHECKSUM = '00729d72e3b82faf54ca8b9621dbb96f'
1313
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
1414
const activeClientIds = new Set()
1515

@@ -62,7 +62,12 @@ self.addEventListener('message', async function (event) {
6262

6363
sendToClient(client, {
6464
type: 'MOCKING_ENABLED',
65-
payload: true,
65+
payload: {
66+
client: {
67+
id: client.id,
68+
frameType: client.frameType,
69+
},
70+
},
6671
})
6772
break
6873
}
@@ -155,6 +160,10 @@ async function handleRequest(event, requestId) {
155160
async function resolveMainClient(event) {
156161
const client = await self.clients.get(event.clientId)
157162

163+
if (activeClientIds.has(event.clientId)) {
164+
return client
165+
}
166+
158167
if (client?.frameType === 'top-level') {
159168
return client
160169
}
@@ -183,12 +192,26 @@ async function getResponse(event, client, requestId) {
183192
const requestClone = request.clone()
184193

185194
function passthrough() {
186-
const headers = Object.fromEntries(requestClone.headers.entries())
195+
// Cast the request headers to a new Headers instance
196+
// so the headers can be manipulated with.
197+
const headers = new Headers(requestClone.headers)
198+
199+
// Remove the "accept" header value that marked this request as passthrough.
200+
// This prevents request alteration and also keeps it compliant with the
201+
// user-defined CORS policies.
202+
const acceptHeader = headers.get('accept')
203+
if (acceptHeader) {
204+
const values = acceptHeader.split(',').map((value) => value.trim())
205+
const filteredValues = values.filter(
206+
(value) => value !== 'msw/passthrough',
207+
)
187208

188-
// Remove internal MSW request header so the passthrough request
189-
// complies with any potential CORS preflight checks on the server.
190-
// Some servers forbid unknown request headers.
191-
delete headers['x-msw-intention']
209+
if (filteredValues.length > 0) {
210+
headers.set('accept', filteredValues.join(', '))
211+
} else {
212+
headers.delete('accept')
213+
}
214+
}
192215

193216
return fetch(requestClone, { headers })
194217
}

src/api/algorithm/fetchCheckSolvedAcUser.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
/* eslint-disable */
22

3+
import { tokenAtom } from '@/store/useTokenStore';
34
import axios, { AxiosError } from 'axios';
45
import { useAtom } from 'jotai';
56
import { useEffect, useState } from 'react';
67

7-
import { accessTokenAtom } from '@/store/useTokenStore';
8-
98
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
109

1110
export const useSolvedacLinkStatus = () => {
1211
const [isSolvedacLinked, setIsSolvedacLinked] = useState<boolean | null>(null);
1312
const [loading, setLoading] = useState<boolean>(true);
1413
const [error, setError] = useState<string | null>(null);
15-
const [accessToken] = useAtom(accessTokenAtom);
14+
const [token] = useAtom(tokenAtom);
1615

1716
useEffect(() => {
18-
if (!accessToken) {
17+
if (!token.accessToken) {
1918
setIsSolvedacLinked(null);
2019
setLoading(false);
2120
return;
@@ -27,7 +26,7 @@ export const useSolvedacLinkStatus = () => {
2726
const response = await axios.get('algorithm/user', {
2827
baseURL: apiUrl,
2928
headers: {
30-
Authorization: `Bearer ${accessToken}`,
29+
Authorization: `Bearer ${token.accessToken}`,
3130
},
3231
});
3332
console.log('response: ', response);
@@ -54,7 +53,7 @@ export const useSolvedacLinkStatus = () => {
5453
};
5554

5655
checkSolvedacLinkStatus();
57-
}, [accessToken]);
56+
}, [token.accessToken]);
5857

5958
return { isSolvedacLinked, loading, error };
6059
};

src/api/algorithm/fetchLinkSolvedAcUser.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
/* eslint-disable */
2+
import { tokenAtom } from '@/store/useTokenStore';
23
import axios, { AxiosError } from 'axios';
34
import { useAtom } from 'jotai';
45
import { useState } from 'react';
56

6-
import { accessTokenAtom } from '@/store/useTokenStore';
7-
87
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
98

109
export const useSolvedacLink = (username: string | null) => {
1110
const [loading, setLoading] = useState<boolean>(false);
1211
const [error, setError] = useState<string | null>(null);
13-
const [accessToken] = useAtom(accessTokenAtom);
12+
const [token] = useAtom(tokenAtom);
1413

1514
const linkSolvedAcUser = async () => {
16-
if (!username || !accessToken) {
15+
if (!username || !token.accessToken) {
1716
setError('아이디를 입력하거나 로그인 상태를 확인해주세요.');
1817
return;
1918
}
@@ -26,7 +25,7 @@ export const useSolvedacLink = (username: string | null) => {
2625
{
2726
baseURL: apiUrl,
2827
headers: {
29-
Authorization: `Bearer ${accessToken}`,
28+
Authorization: `Bearer ${token.accessToken}`,
3029
},
3130
params: { username }, // 쿼리 파라미터로 username 전달
3231
},

src/api/auth/auth.action.ts

Lines changed: 111 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,130 @@
1+
import { LoginResponse } from '@/types/auth';
2+
13
import instance from '../axiosInstance';
24

5+
// 중첩된 data 구조를 반영하는 API 응답 타입
6+
interface ApiResponse<T> {
7+
data: {
8+
data: T;
9+
message?: string;
10+
status?: number;
11+
accessToken?: string;
12+
refreshToken?: string;
13+
};
14+
}
15+
16+
// 이메일 중복 체크 (인증 없이 요청)
317
export const checkEmail = async (email: string) => {
4-
const result = await instance.get('/user/checkDuplicateEmail', {
5-
params: {
6-
email,
7-
},
8-
headers: {
9-
'No-Auth': true,
10-
},
11-
});
12-
return result;
18+
try {
19+
const res = await instance.get<ApiResponse<boolean>>('/user/checkDuplicateEmail', {
20+
params: { email },
21+
headers: { noAuth: true },
22+
});
23+
return {
24+
success: true,
25+
data: res.data.data,
26+
};
27+
} catch (err) {
28+
return {
29+
success: false,
30+
error: err instanceof Error ? err.message : '이메일 중복 체크 오류',
31+
};
32+
}
1333
};
1434

35+
// 인증 코드 전송 (인증 없이 요청)
1536
export const sendCode = async (email: string) => {
16-
const result = await instance.post(
17-
'/user/authEmail',
18-
{ email },
19-
{
20-
headers: {
21-
'No-Auth': true,
37+
try {
38+
const res = await instance.post<ApiResponse<{ message: string }>>(
39+
'/user/authEmail',
40+
{ email },
41+
{
42+
headers: { noAuth: true },
2243
},
23-
},
24-
);
25-
return result;
44+
);
45+
return {
46+
success: true,
47+
data: res.data.data,
48+
};
49+
} catch (err) {
50+
return {
51+
success: false,
52+
error: err instanceof Error ? err.message : '인증 코드 전송 오류',
53+
};
54+
}
2655
};
2756

57+
// 인증 코드 확인 (인증 없이 요청)
2858
export const checkCode = async (email: string, authCode: string) => {
29-
const result = await instance.get('/user/authEmail', {
30-
params: {
31-
email,
32-
authCode,
33-
},
34-
headers: {
35-
'No-Auth': true,
36-
},
37-
});
38-
return result;
59+
try {
60+
const res = await instance.get<ApiResponse<boolean>>('/user/authEmail', {
61+
params: { email, authCode },
62+
headers: { noAuth: true },
63+
});
64+
console.log('checkCode___', res);
65+
return {
66+
success: true,
67+
data: res.data.data,
68+
};
69+
} catch (err) {
70+
return {
71+
success: false,
72+
error: err instanceof Error ? err.message : '인증 코드 확인 오류',
73+
};
74+
}
3975
};
4076

41-
export const createAccount = async (
77+
// 계정 생성 (인증 없이 요청)
78+
export const joinAction = async (
4279
email: string,
4380
password: string,
4481
password_check: string,
4582
auth_code: string,
4683
) => {
47-
const result = await instance.post(
48-
'/user/join',
49-
{
50-
email,
51-
password,
52-
password_check,
53-
auth_code,
54-
},
55-
{
56-
headers: {
57-
'No-Auth': true,
84+
try {
85+
const res = await instance.post<ApiResponse<boolean>>(
86+
'/user/join',
87+
{ email, password, passwordCheck: password_check, authCode: auth_code },
88+
{
89+
headers: { noAuth: true },
90+
},
91+
);
92+
return {
93+
success: true,
94+
data: res.data.data,
95+
};
96+
} catch (err) {
97+
return {
98+
success: false,
99+
error: err instanceof Error ? err.message : '계정 생성 오류',
100+
};
101+
}
102+
};
103+
104+
// 로그인 요청 (인증 없이 요청)
105+
export const loginAction = async (email: string, password: string) => {
106+
try {
107+
const res = await instance.post<ApiResponse<LoginResponse>>(
108+
'/user/login',
109+
{ email, password },
110+
{ headers: { noAuth: true } },
111+
);
112+
if (!res.data.data.accessToken || !res.data.data.refreshToken) {
113+
throw new Error('토큰이 없습니다');
114+
}
115+
116+
return {
117+
success: true,
118+
data: {
119+
accessToken: res.data.data.accessToken,
120+
refreshToken: res.data.data.refreshToken,
58121
},
59-
},
60-
);
61-
return result;
122+
status: res.status,
123+
};
124+
} catch (err) {
125+
return {
126+
success: false,
127+
error: err instanceof Error ? err.message : '로그인 오류',
128+
};
129+
}
62130
};

0 commit comments

Comments
 (0)