Skip to content

Commit fd55fd6

Browse files
authored
Merge pull request #255 from THIP-TextHip/develop
develp branch ์ž‘์—…๋‚ด์šฉ ๋จธ์ง€ : develop -> main
2 parents d06496b + 50ec1d7 commit fd55fd6

14 files changed

Lines changed: 268 additions & 64 deletions

File tree

โ€Žsrc/api/auth/getToken.tsโ€Ž

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ export interface GetTokenResponse {
99
code: number;
1010
message: string;
1111
data: {
12-
token: string; // ํ† ํฐ
12+
token: string; // ํ† ํฐ (์ž„์‹œ ๋˜๋Š” ์•ก์„ธ์Šค)
13+
isNewUser: boolean; // true๋ฉด ํšŒ์›๊ฐ€์ž… ํ•„์š” (์ž„์‹œ ํ† ํฐ), false๋ฉด ์•ก์„ธ์Šค ํ† ํฐ
1314
};
1415
}
1516

โ€Žsrc/api/index.tsโ€Ž

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,35 @@ export const apiClient = axios.create({
1111
'Content-Type': 'application/json',
1212
},
1313
});
14-
// Request ์ธํ„ฐ์…‰ํ„ฐ: localStorage์˜ ํ† ํฐ์„ ํ—ค๋”์— ์ž๋™ ์ถ”๊ฐ€
14+
// Request ์ธํ„ฐ์…‰ํ„ฐ: ํ† ํฐ ๋ถ€์žฌ ์‹œ ๋น„๊ณต๊ฐœ API ์š”์ฒญ์„ ์„ ์ œ ์ฐจ๋‹จ(๋ฆฌ๋‹ค์ด๋ ‰ํŠธ + ์š”์ฒญ ์ทจ์†Œ)
1515
apiClient.interceptors.request.use(
1616
config => {
17-
// localStorage์—์„œ ํ† ํฐ ํ™•์ธ
1817
const authToken = localStorage.getItem('authToken');
18+
const preAuthToken = localStorage.getItem('preAuthToken');
19+
// ๊ณต๊ฐœ API(์™„์ „ ๊ณต๊ฐœ)
20+
const publicPaths = ['/auth/token'];
21+
// ํšŒ์›๊ฐ€์ž… ์ง„ํ–‰ ์ค‘ ํ•„์š”ํ•œ ๊ฒฝ๋กœ(์ž„์‹œ ํ† ํฐ ํ—ˆ์šฉ)
22+
const signupPaths = ['/users/nickname', '/users/signup'];
23+
const isPublic = publicPaths.some(path => config.url?.startsWith(path));
24+
const isSignupPath = signupPaths.some(path => config.url?.startsWith(path));
25+
26+
if (!authToken && !isPublic && !(preAuthToken && isSignupPath)) {
27+
console.log('โŒ ํ† ํฐ ์—†์Œ: ์š”์ฒญ์„ ์ทจ์†Œํ•˜๊ณ  ํ™ˆ์œผ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.');
28+
window.location.href = '/';
29+
// ์š”์ฒญ ์ž์ฒด๋ฅผ ์ทจ์†Œํ•˜์—ฌ ๋ถˆํ•„์š”ํ•œ ๋„คํŠธ์›Œํฌ ์™•๋ณต ๋ฐฉ์ง€
30+
return Promise.reject(new Error('Request cancelled: missing auth token'));
31+
}
1932

2033
if (authToken) {
2134
config.headers.Authorization = `Bearer ${authToken}`;
22-
} else {
23-
console.log('โŒ localStorage์— ํ† ํฐ์ด ์—†์Šต๋‹ˆ๋‹ค.');
35+
} else if (preAuthToken && isSignupPath) {
36+
// ํšŒ์›๊ฐ€์ž… ๊ฒฝ๋กœ์—์„œ๋Š” ์ž„์‹œ ํ† ํฐ์„ ์‚ฌ์šฉ
37+
config.headers.Authorization = `Bearer ${preAuthToken}`;
2438
}
2539

2640
return config;
2741
},
28-
error => {
29-
return Promise.reject(error);
30-
},
42+
error => Promise.reject(error),
3143
);
3244

3345
// Response ์ธํ„ฐ์…‰ํ„ฐ: 401 ์—๋Ÿฌ ์‹œ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { apiClient } from '../index';
2+
3+
export interface NotificationItem {
4+
notificationId: number;
5+
title: string;
6+
content: string;
7+
isChecked: boolean;
8+
notificationType: string;
9+
postDate: string;
10+
}
11+
12+
export interface GetNotificationsResponse {
13+
isSuccess: boolean;
14+
code: number;
15+
message: string;
16+
data: {
17+
notifications: NotificationItem[];
18+
nextCursor: string;
19+
isLast: boolean;
20+
};
21+
}
22+
23+
export interface GetNotificationsParams {
24+
cursor?: string | null;
25+
type?: 'feed' | 'room';
26+
}
27+
28+
export const getNotifications = async (
29+
params?: GetNotificationsParams,
30+
): Promise<GetNotificationsResponse> => {
31+
const response = await apiClient.get<GetNotificationsResponse>('/notifications', {
32+
params,
33+
});
34+
return response.data;
35+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { apiClient } from '../index';
2+
3+
export interface DeleteUsersResponse {
4+
isSuccess: boolean;
5+
code: number;
6+
message: string;
7+
data: null;
8+
}
9+
10+
export const deleteUsers = async (): Promise<DeleteUsersResponse> => {
11+
const response = await apiClient.delete<DeleteUsersResponse>('/users');
12+
return response.data;
13+
};

โ€Žsrc/components/group/CompletedGroupModal.tsxโ€Ž

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ import { Modal, Overlay } from './Modal.styles';
88
import { getMyRooms, type Room } from '@/api/rooms/getMyRooms';
99
import { getMyProfile } from '@/api/users/getMyProfile';
1010
import { colors, typography } from '@/styles/global/global';
11+
import { useNavigate } from 'react-router-dom';
1112

1213
interface CompletedGroupModalProps {
1314
onClose: () => void;
1415
}
1516

1617
const CompletedGroupModal = ({ onClose }: CompletedGroupModalProps) => {
18+
const navigate = useNavigate();
1719
const [rooms, setRooms] = useState<Room[]>([]);
1820
const [isLoading, setIsLoading] = useState(false);
1921
const [error, setError] = useState<string | null>(null);
@@ -29,9 +31,14 @@ const CompletedGroupModal = ({ onClose }: CompletedGroupModalProps) => {
2931
coverUrl: room.bookImageUrl,
3032
deadLine: '',
3133
isOnGoing: false,
34+
type: room.type,
3235
};
3336
};
3437

38+
const handleGroupCardClick = (group: Group) => {
39+
navigate(`/group/detail/joined/${group.id}`);
40+
};
41+
3542
useEffect(() => {
3643
const fetchCompletedRooms = async () => {
3744
try {
@@ -84,7 +91,14 @@ const CompletedGroupModal = ({ onClose }: CompletedGroupModalProps) => {
8491
) : error ? (
8592
<ErrorMessage>{error}</ErrorMessage>
8693
) : convertedGroups.length > 0 ? (
87-
convertedGroups.map(group => <GroupCard key={group.id} group={group} type={'modal'} />)
94+
convertedGroups.map(group => (
95+
<GroupCard
96+
key={group.id}
97+
group={group}
98+
type={'modal'}
99+
onClick={() => handleGroupCardClick(group)}
100+
/>
101+
))
88102
) : (
89103
<EmptyState data-empty="true">
90104
<EmptyTitle>์™„๋ฃŒ๋œ ๋ชจ์ž„๋ฐฉ์ด ์—†์–ด์š”</EmptyTitle>
@@ -116,6 +130,11 @@ const Content = styled.div<{ isEmpty?: boolean }>`
116130
@media (min-width: 584px) {
117131
grid-template-columns: 1fr 1fr;
118132
}
133+
134+
//ํ•ญ๋ชฉ์ด ํ•˜๋‚˜์ผ ๋•Œ๋Š” ์ „์ฒด ์—ด์„ ์‚ฌ์šฉํ•˜์—ฌ 2์—ด ๊ทธ๋ฆฌ๋“œ์ฒ˜๋Ÿผ ๋ณด์ด์ง€ ์•Š๋„๋ก ์ฒ˜๋ฆฌ
135+
& > *:only-child {
136+
grid-column: 1 / -1;
137+
}
119138
`;
120139

121140
const LoadingMessage = styled.div`

โ€Žsrc/components/group/GroupCard.tsxโ€Ž

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,16 @@ export const GroupCard = forwardRef<HTMLDivElement, Props>(
3535
<p>{group.participants}</p>
3636
<MaximumParticipants>/ {group.maximumParticipants}๋ช…</MaximumParticipants>
3737
</Participant>
38-
{isOngoing === true ? (
39-
<RecruitingDeadline isRecommend={isRecommend}>
40-
{group.deadLine} ์ข…๋ฃŒ
41-
</RecruitingDeadline>
42-
) : (
43-
<OngoingDeadline isRecommend={isRecommend}>
44-
{group.deadLine} ๋ชจ์ง‘ ๋งˆ๊ฐ
45-
</OngoingDeadline>
46-
)}
38+
{(type !== 'modal' || group.type !== 'expired') &&
39+
(isOngoing === true ? (
40+
<RecruitingDeadline isRecommend={isRecommend}>
41+
{group.deadLine} ์ข…๋ฃŒ
42+
</RecruitingDeadline>
43+
) : (
44+
<OngoingDeadline isRecommend={isRecommend}>
45+
{group.deadLine} ๋ชจ์ง‘ ๋งˆ๊ฐ
46+
</OngoingDeadline>
47+
))}
4748
</Bottom>
4849
</Info>
4950
</Card>

โ€Žsrc/components/group/MyGroupBox.tsxโ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export interface Group {
2222
genre?: string;
2323
isOnGoing?: boolean;
2424
isPublic?: boolean;
25+
type?: string;
2526
}
2627

2728
const convertJoinedRoomToGroup = (room: JoinedRoomItem): Group => ({

โ€Žsrc/hooks/useSocialLoginToken.tsโ€Ž

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,23 @@ export const useSocialLoginToken = () => {
2323
console.log('๐Ÿ”‘ ์†Œ์…œ ๋กœ๊ทธ์ธ ํ† ํฐ ๋ฐœ๊ธ‰ ์š”์ฒญ');
2424
console.log('๐Ÿ“‹ loginTokenKey:', loginTokenKey);
2525

26-
// /auth/token API ํ˜ธ์ถœํ•˜์—ฌ ํ† ํฐ ๋ฐœ๊ธ‰ (์ž„์‹œ ํ† ํฐ ๋˜๋Š” access ํ† ํฐ)
26+
// /auth/token API ํ˜ธ์ถœํ•˜์—ฌ ํ† ํฐ ๋ฐœ๊ธ‰ (์ž„์‹œ ํ† ํฐ)
2727
const response = await getToken({ loginTokenKey });
2828

2929
if (response.isSuccess) {
30-
const { token } = response.data;
30+
const { token, isNewUser } = response.data;
3131

32-
// ํ† ํฐ์„ localStorage์— ์ €์žฅ (request header์— ์‚ฌ์šฉ)
33-
localStorage.setItem('authToken', token);
34-
35-
console.log('โœ… Access ํ† ํฐ ๋ฐœ๊ธ‰ ์„ฑ๊ณต (๋ฐ”๋กœ ํ™ˆ ํ™”๋ฉด)');
32+
if (isNewUser) {
33+
// ํšŒ์›๊ฐ€์ž… ์ง„ํ–‰์šฉ ์ž„์‹œ ํ† ํฐ ์ €์žฅ
34+
localStorage.setItem('preAuthToken', token);
35+
localStorage.removeItem('authToken');
36+
console.log('โœ… ์‹ ๊ทœ ์‚ฌ์šฉ์ž: ์ž„์‹œ ํ† ํฐ ์ €์žฅ (ํšŒ์›๊ฐ€์ž… ์ง„ํ–‰)');
37+
} else {
38+
// ๊ธฐ์กด ์‚ฌ์šฉ์ž: ์•ก์„ธ์Šค ํ† ํฐ ์ €์žฅ
39+
localStorage.setItem('authToken', token);
40+
localStorage.removeItem('preAuthToken');
41+
console.log('โœ… ๊ธฐ์กด ์‚ฌ์šฉ์ž: ์•ก์„ธ์Šค ํ† ํฐ ์ €์žฅ');
42+
}
3643

3744
// URL์—์„œ loginTokenKey ํŒŒ๋ผ๋ฏธํ„ฐ ์ œ๊ฑฐ
3845
const newUrl = window.location.pathname;
@@ -53,7 +60,7 @@ export const useSocialLoginToken = () => {
5360
// ํ† ํฐ ๋ฐœ๊ธ‰ Promise๋ฅผ ์ €์žฅ
5461
tokenPromise.current = handleSocialLoginToken();
5562
}
56-
}, [location.pathname]);
63+
}, [location.pathname, location.search]);
5764

5865
// ํ† ํฐ ๋ฐœ๊ธ‰ ์™„๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ํ•จ์ˆ˜ ๋ฐ˜ํ™˜
5966
const waitForToken = useCallback(async (): Promise<void> => {

โ€Žsrc/pages/feed/Feed.tsxโ€Ž

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ const Feed = () => {
5353
navigate('/feed/search');
5454
};
5555

56+
const handleNoticeButton = () => {
57+
navigate('/notice');
58+
};
59+
5660
// ์ „์ฒด ํ”ผ๋“œ ๋กœ๋“œ ํ•จ์ˆ˜
5761
const loadTotalFeeds = useCallback(async (_cursor?: string) => {
5862
try {
@@ -175,7 +179,11 @@ const Feed = () => {
175179

176180
return (
177181
<Container>
178-
<MainHeader type="home" leftButtonClick={handleSearchButton} />
182+
<MainHeader
183+
type="home"
184+
leftButtonClick={handleSearchButton}
185+
rightButtonClick={handleNoticeButton}
186+
/>
179187
<TabBar tabs={tabs} activeTab={activeTab} onTabClick={setActiveTab} />
180188
{initialLoading || tabLoading ? (
181189
<LoadingSpinner size="large" fullHeight={true} />

โ€Žsrc/pages/group/Group.tsxโ€Ž

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ const Group = () => {
9393
navigate('/group/search');
9494
};
9595

96+
const handleNoticeButton = () => {
97+
navigate('/notice');
98+
};
99+
96100
const handleAllRoomsClick = () => {
97101
navigate('/group/search', {
98102
state: {
@@ -105,7 +109,11 @@ const Group = () => {
105109
<Wrapper>
106110
{isMyGroupModalOpen && <MyGroupModal onClose={closeMyGroupModal} />}
107111
{isCompletedGroupModalOpen && <CompletedGroupModal onClose={closeCompletedGroupModal} />}
108-
<MainHeader type="group" leftButtonClick={openCompletedGroupModal} />
112+
<MainHeader
113+
type="group"
114+
leftButtonClick={openCompletedGroupModal}
115+
rightButtonClick={handleNoticeButton}
116+
/>
109117
<SearchBar placeholder="๋ชจ์ž„๋ฐฉ ์ฐธ์—ฌํ•  ์‚ฌ๋žŒ!" onClick={handleSearchBarClick} />
110118
<MyGroupBox onMyGroupsClick={openMyGroupModal}></MyGroupBox>
111119
<Blank height={'10px'} margin={'32px 0'}></Blank>

0 commit comments

Comments
ย (0)