Skip to content

Commit 476ea1a

Browse files
committedFeb 22, 2024·
Squashed commit of the following:
commit c941d1c Author: Jeon Jinho <gino9940@gmail.com> Date: Fri Feb 23 01:46:01 2024 +0900 디자인 불일치 대거 수정 (#229) * feat: add comment box * fix: comment chip icon * fix: remove meatball button from a/b topic card * chore: add @toss/utils * feat: add formatToKoreanNumber * fix: RadioInput visibility * refactor(topic-card): separate header from comment box * refactor(comment-box): replace comment preview api * fix(topic-swiper): useSwiperSlide not working * fix(home): get b side topic only * feat: add width prop to Flex * feat: add isBig prop to commentBox * feat: add ellipsis prop to Text * feat: show CTA if comments empty * fix: add key prop to SwiperSlide * fix: add exception handling for comments by topic creator commit aa91526 Author: CHAEYOUNG SONG <77428876+chaeyoung103@users.noreply.github.com> Date: Wed Feb 21 17:01:02 2024 +0900 mypage 퍼블리싱 및 기능 구현 (#228) * fix: 사파리 이슈 textcolor 지정 * fix: 서버에서 응답이 없는경우 에러방지 * fix: 디자인과 다른 input ui 수정 * feat: 내정보수정 페이지로 이동 로직 추가 * style: 버튼 이름 변경 * feat: modifyprofile route 추가 * design: 디자인과 다른 ui 수정 * feat: 내정보수정 api 생성 * feat: 내정보 수정 페이지 퍼블리싱 및 api 연동 * feat: import ModifyProfile with lazy --------- Co-authored-by: 전진호 <gino9940@gmail.com> commit 6dd66e4 Author: 전진호 <gino9940@gmail.com> Date: Mon Feb 19 21:34:07 2024 +0900 fix: remove duplicated identifier commit 5ec1eb5 Merge: b1350d9 4ed3857 Author: Jeon Jinho <gino9940@gmail.com> Date: Mon Feb 19 21:28:53 2024 +0900 Merge branch 'main' into dev commit b1350d9 Author: Jeon Jinho <gino9940@gmail.com> Date: Mon Feb 19 16:11:17 2024 +0900 comment에 choice 추가해서 선택지 반영 (#222) commit 093fdbf Author: Jeon Jinho <gino9940@gmail.com> Date: Mon Feb 19 16:11:05 2024 +0900 알림 화면에 뒤로가기 버튼 추가 (#220) commit 581907a Author: Jeon Jinho <gino9940@gmail.com> Date: Mon Feb 19 16:09:47 2024 +0900 fix: 유저가 작성한 토픽인 경우 조회 가능하도록 수정 (#219) * fix: get method to throw error * fix: check if member created topic * fiX: remove console.log * refactor: remove another console.logs commit 7664e48 Author: Jeon Jinho <gino9940@gmail.com> Date: Mon Feb 19 16:09:35 2024 +0900 A 사이드 토픽 카드 텍스트 영역 및 소수점 이슈 해결 (#218) * fix: round percentage * fix: make progress content hidden with ellipsis * fix: integrate to topic * fix: show toast on vote my topic commit 112de6d Author: Jeon Jinho <gino9940@gmail.com> Date: Mon Feb 19 13:59:24 2024 +0900 fix: 토픽 작성자는 투표 불가 (#217) * chore: add react-toastify * feat: add zIndex type to theme * feat: Toast container * feat: show toast and reset slide * fix: make handleVote return Promise<boolean> commit d101c47 Merge: ef9eaee 0292560 Author: CHAEYOUNG SONG <77428876+chaeyoung103@users.noreply.github.com> Date: Mon Feb 19 00:14:32 2024 +0900 Merge pull request #215 from team-offonoff/feat/admin-login fix: admin login button commit 0292560 Author: 전진호 <gino9940@gmail.com> Date: Mon Feb 19 00:10:32 2024 +0900 feat: add admin login button commit adf85b8 Author: 전진호 <gino9940@gmail.com> Date: Sun Feb 18 23:41:07 2024 +0900 feat: replace to useOAuth commit 1fade9e Author: 전진호 <gino9940@gmail.com> Date: Sun Feb 18 23:40:53 2024 +0900 fix: redirect user to login page on fail commit 196a011 Author: 전진호 <gino9940@gmail.com> Date: Sun Feb 18 23:38:36 2024 +0900 feat: add useOAuth to authroize commonly commit 5b20728 Author: 전진호 <gino9940@gmail.com> Date: Sun Feb 18 23:38:08 2024 +0900 fix: separate login url retrieve to function commit ef9eaee Author: Jeon Jinho <gino9940@gmail.com> Date: Sat Feb 17 21:10:24 2024 +0900 fix: signup 화면 디자인 불일치 수정 (#213) * fix: make DefaultButton extends ButtonHTMLAttributes * feat: replace to DefaultButton * fix: radio button selected style * feat: navigate to home after consent to term commit 89f4201 Author: 전진호 <gino9940@gmail.com> Date: Sat Feb 17 20:58:26 2024 +0900 fix: google redirect url commit ca79bec Author: Jeon Jinho <gino9940@gmail.com> Date: Thu Feb 15 15:29:05 2024 +0900 Theme Color를 Layout에서 동적으로 수정 (#210) * chore: add react-helmet * feat: change theme color in layout commit 03d663e Merge: 370b532 25634ba Author: Jeon Jinho <gino9940@gmail.com> Date: Wed Feb 14 20:06:01 2024 +0900 Merge pull request #205 from team-offonoff/feat/mypage 마이페이지 ui 및 기능 추가 commit 25634ba Merge: b4efd5b 370b532 Author: Jeon Jinho <gino9940@gmail.com> Date: Wed Feb 14 20:05:42 2024 +0900 Merge branch 'dev' into feat/mypage commit b4efd5b Author: chaeyoung103 <thdcodud103@naver.com> Date: Wed Feb 14 20:01:52 2024 +0900 feat: 프로필 이미지 삭제하기 기능추가 delete api 연동 및 삭제하기 모달 추가 commit ecff063 Author: chaeyoung103 <thdcodud103@naver.com> Date: Wed Feb 14 20:01:12 2024 +0900 fix: 삭제하기 액션모달 사용을 위한 수정 commit 370b532 Merge: 360a128 da57fb0 Author: Jeon Jinho <gino9940@gmail.com> Date: Wed Feb 14 19:44:54 2024 +0900 Merge pull request #203 from team-offonoff/feat/sentry-logging feat: add @sentry/react commit 360a128 Merge: d7e66db 3a3c64c Author: Jeon Jinho <gino9940@gmail.com> Date: Wed Feb 14 19:44:42 2024 +0900 Merge pull request #202 from team-offonoff/feat/b-side-detail-page B 사이드 상세 페이지 구현 commit da57fb0 Author: 전진호 <gino9940@gmail.com> Date: Wed Feb 14 19:37:34 2024 +0900 feat: thrwo Error for testing commit de3c598 Author: 전진호 <gino9940@gmail.com> Date: Wed Feb 14 19:37:19 2024 +0900 feat: init sentry commit 5fbe1d6 Author: 전진호 <gino9940@gmail.com> Date: Wed Feb 14 19:37:12 2024 +0900 feat: add @sentry/react commit 3a3c64c Author: 전진호 <gino9940@gmail.com> Date: Wed Feb 14 19:29:44 2024 +0900 feat: navigate to detail page on click card commit 85379ef Author: 전진호 <gino9940@gmail.com> Date: Wed Feb 14 19:29:16 2024 +0900 feat: add B side detail page commit dd5380f Author: 전진호 <gino9940@gmail.com> Date: Wed Feb 14 19:27:57 2024 +0900 fix: make choiceSlide box-sizing to content-box commit 7fc2960 Author: 전진호 <gino9940@gmail.com> Date: Wed Feb 14 19:27:34 2024 +0900 feat: add BackButton component commit ce9ae78 Author: 전진호 <gino9940@gmail.com> Date: Wed Feb 14 18:41:15 2024 +0900 feat: separate loading component commit d7e66db Merge: e5d10fa 88040c3 Author: Jeon Jinho <gino9940@gmail.com> Date: Wed Feb 14 18:36:34 2024 +0900 Merge pull request #201 from team-offonoff/feat/loading-screen Loading 화면 분리 commit 90fc26b Author: chaeyoung103 <thdcodud103@naver.com> Date: Wed Feb 14 16:13:58 2024 +0900 feat: 프로필 이미지 s3 업로드 및 프로필 이미지 변경 api 연동 commit 88040c3 Author: 전진호 <gino9940@gmail.com> Date: Wed Feb 14 15:50:49 2024 +0900 feat: separate loading component commit e5d10fa Author: 전진호 <gino9940@gmail.com> Date: Wed Feb 14 15:16:35 2024 +0900 fix: routes missing commit 564d19c Merge: 5ff3a6e 22d7eb1 Author: Jeon Jinho <gino9940@gmail.com> Date: Wed Feb 14 15:15:14 2024 +0900 Merge pull request #199 from team-offonoff/feat/lazy-routes feat: load routes lazily commit 22d7eb1 Merge: b01f5a7 5ff3a6e Author: Jeon Jinho <gino9940@gmail.com> Date: Wed Feb 14 15:14:43 2024 +0900 Merge branch 'dev' into feat/lazy-routes commit 5ff3a6e Merge: 4f28590 566d213 Author: Jeon Jinho <gino9940@gmail.com> Date: Wed Feb 14 15:14:10 2024 +0900 Merge pull request #198 from team-offonoff/feat/b-side-page feat: B 사이드 토픽 홈 화면 구현 commit b01f5a7 Author: 전진호 <gino9940@gmail.com> Date: Tue Feb 13 22:30:19 2024 +0900 feat: load routes lazily commit 566d213 Author: 전진호 <gino9940@gmail.com> Date: Tue Feb 13 22:08:37 2024 +0900 fix: render BTopicCard commit ed0f863 Author: 전진호 <gino9940@gmail.com> Date: Tue Feb 13 21:58:44 2024 +0900 feat: implements BTopicCard commit 853df2a Author: 전진호 <gino9940@gmail.com> Date: Tue Feb 13 21:58:21 2024 +0900 feat: implements BTopicChoice commit c1b28ef Author: 전진호 <gino9940@gmail.com> Date: Tue Feb 13 21:54:23 2024 +0900 feat: add backgroundColor as optional commit 54875a5 Author: 전진호 <gino9940@gmail.com> Date: Tue Feb 13 20:08:58 2024 +0900 fix: remove transition from body commit 4ef34b5 Author: 전진호 <gino9940@gmail.com> Date: Tue Feb 13 19:59:11 2024 +0900 fix: separate onVote commit 2c0c540 Author: 전진호 <gino9940@gmail.com> Date: Tue Feb 13 17:49:04 2024 +0900 feat: add b topics background color commit 3977e6d Author: 전진호 <gino9940@gmail.com> Date: Tue Feb 13 17:48:27 2024 +0900 fix: b nav item to commit 67bd612 Author: 전진호 <gino9940@gmail.com> Date: Tue Feb 13 17:14:37 2024 +0900 feat: rename ATopics and add BTopics commit d0bec20 Merge: 72aaca6 4f28590 Author: chaeyoung103 <thdcodud103@naver.com> Date: Tue Feb 13 16:34:58 2024 +0900 Merge remote-tracking branch 'origin/dev' into feat/mypage commit 72aaca6 Author: chaeyoung103 <thdcodud103@naver.com> Date: Mon Feb 12 20:26:12 2024 +0900 feat: mypage 모달 생성, 앨범 기능 추가 commit 1936187 Merge: 629e617 413afd3 Author: chaeyoung103 <thdcodud103@naver.com> Date: Mon Feb 12 17:44:21 2024 +0900 Merge remote-tracking branch 'origin/dev' into feat/mypage commit 629e617 Merge: 55d5c17 b77acf8 Author: chaeyoung103 <thdcodud103@naver.com> Date: Fri Feb 9 22:46:07 2024 +0900 Merge remote-tracking branch 'origin/dev' into feat/mypage commit 55d5c17 Merge: f5be359 55e8442 Author: chaeyoung103 <thdcodud103@naver.com> Date: Fri Feb 9 22:38:11 2024 +0900 Merge remote-tracking branch 'origin/dev' into feat/mypage commit f5be359 Merge: 39fc1c1 3a24467 Author: chaeyoung103 <thdcodud103@naver.com> Date: Fri Feb 9 20:56:42 2024 +0900 Merge remote-tracking branch 'origin/dev' into feat/mypage commit 39fc1c1 Author: chaeyoung103 <thdcodud103@naver.com> Date: Fri Feb 9 17:57:44 2024 +0900 feat: mypage ui 생성 commit 682c926 Merge: 7e36b4e da45f52 Author: chaeyoung103 <thdcodud103@naver.com> Date: Fri Feb 9 16:18:26 2024 +0900 Merge remote-tracking branch 'origin/dev' into feat/mypage commit 7e36b4e Merge: b532fb5 2624766 Author: chaeyoung103 <thdcodud103@naver.com> Date: Thu Feb 8 21:53:48 2024 +0900 Merge remote-tracking branch 'origin/dev' into feat/mypage commit b532fb5 Merge: 185fc29 0d1fdb5 Author: chaeyoung103 <thdcodud103@naver.com> Date: Thu Feb 8 21:32:20 2024 +0900 Merge remote-tracking branch 'origin/dev' into feat/mypage commit 185fc29 Merge: d892fd5 2232cb4 Author: chaeyoung103 <thdcodud103@naver.com> Date: Thu Feb 8 21:29:12 2024 +0900 Merge remote-tracking branch 'origin/dev' into feat/mypage commit d892fd5 Merge: ac72441 8c74cf2 Author: chaeyoung103 <thdcodud103@naver.com> Date: Thu Feb 8 21:26:21 2024 +0900 Merge remote-tracking branch 'origin/dev' into feat/mypage
1 parent 49d4ba6 commit 476ea1a

38 files changed

+727
-371
lines changed
 

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"@sentry/react": "^7.101.0",
1818
"@tanstack/react-query": "^5.4.3",
1919
"@toss/date": "^1.1.8",
20+
"@toss/utils": "^1.4.6",
2021
"@types/styled-components": "^5.1.28",
2122
"framer-motion": "^10.16.4",
2223
"react": "^18.2.0",

‎src/apis/comment/useComment.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
useQueryClient,
77
} from '@tanstack/react-query';
88

9-
import { CommentReaction, CommentResponse } from '@interfaces/api/comment';
9+
import { CommentReaction, CommentResponse, LatestComment } from '@interfaces/api/comment';
1010

1111
import { PagingDataResponse } from '@interfaces/api';
1212

@@ -20,6 +20,10 @@ const getComments = ({ topicId, page, size }: { topicId: number; page: number; s
2020
);
2121
};
2222

23+
const getCommentPreview = (topicId: number) => {
24+
return client.get<LatestComment>(`/topics/${topicId}/comment`);
25+
};
26+
2327
const createComments = ({ topicId, content }: { topicId: number; content: string }) => {
2428
return client.post<CommentResponse>({
2529
path: `/comments`,
@@ -47,10 +51,10 @@ const useComments = (topicId: number) => {
4751
});
4852
};
4953

50-
const useLatestComment = (topicId: number, enabled: boolean) => {
54+
const usePreviewComment = (topicId: number, enabled: boolean) => {
5155
return useQuery({
5256
queryKey: [COMMENT_KEY, 'latest', topicId],
53-
queryFn: () => getComments({ topicId: topicId, page: 0, size: 1 }),
57+
queryFn: () => getCommentPreview(topicId),
5458
enabled: enabled,
5559
});
5660
};
@@ -96,4 +100,4 @@ const useReactComment = (topicId: number, commentId: number) => {
96100
});
97101
};
98102

99-
export { COMMENT_KEY, useComments, useCreateComment, useReactComment, useLatestComment };
103+
export { COMMENT_KEY, useComments, useCreateComment, useReactComment, usePreviewComment };

‎src/apis/fetch.ts

+11-3
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,21 @@ class Fetch {
7474
body: JSON.stringify(body),
7575
});
7676

77-
const data = await response.json();
78-
7977
if (!response.ok) {
78+
const data = await response.json();
8079
throw new ResponseError(data);
8180
}
8281

83-
return data as TData;
82+
// 응답 본문이 있는지 확인
83+
const text = await response.text();
84+
if (text) {
85+
// 응답 본문이 있으면 JSON으로 파싱
86+
const data = JSON.parse(text);
87+
return data as TData;
88+
} else {
89+
// 응답 본문이 없으면 null 반환
90+
return null;
91+
}
8492
}
8593

8694
async delete<T>(path: string, body?: object): Promise<T> {

‎src/apis/profile/useProfile.ts

+23-11
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
11
import { useMutation } from '@tanstack/react-query';
22

3-
import { PresignedURLResponse, ProfileResponse } from '@interfaces/api/profile';
3+
import {
4+
ModifyProfileRequestDTO,
5+
PresignedURLResponse,
6+
ProfileResponse,
7+
} from '@interfaces/api/profile';
48

59
import client from '@apis/fetch';
610

7-
// export interface profileRequestDTO {
8-
// status?: 'VOTING' | 'CLOSED';
9-
// keyword_id?: number;
10-
// page?: number;
11-
// size?: number;
12-
// sort?: string;
13-
// side?: 'TOPIC_A' | 'TOPIC_B';
14-
// }
15-
1611
const getProfile = () => {
1712
return client.get<ProfileResponse>('/members/profile');
1813
};
@@ -35,6 +30,13 @@ const updateProfileImgURL = (profileImgURL: string) => {
3530
});
3631
};
3732

33+
const modifyProfile = (req: ModifyProfileRequestDTO) => {
34+
return client.put({
35+
path: `/members/profile/information`,
36+
body: req,
37+
});
38+
};
39+
3840
const deleteProfileImg = () => {
3941
return client.delete(`/members/profile/image`);
4042
};
@@ -51,4 +53,14 @@ const useDeleteProfileImg = () => {
5153
return useMutation({ mutationFn: () => deleteProfileImg() });
5254
};
5355

54-
export { getProfile, useGetPresignedURL, useUpdateProfileImgURL, useDeleteProfileImg };
56+
const useModifyProfile = () => {
57+
return useMutation({ mutationFn: modifyProfile });
58+
};
59+
60+
export {
61+
getProfile,
62+
useGetPresignedURL,
63+
useUpdateProfileImgURL,
64+
useDeleteProfileImg,
65+
useModifyProfile,
66+
};

‎src/assets/icons/comment-box.svg

+21
Loading

‎src/assets/icons/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import CameraIcon from './camera.svg?react';
1515
import CheckIcon from './check.svg?react';
1616
import ClockIcon from './clock.svg?react';
1717
import CloseIcon from './close.svg?react';
18+
import CommentBoxIcon from './comment-box.svg?react';
1819
import CommentIcon from './comment.svg?react';
1920
import DefaultProfileIcon from './default-profile.svg?react';
2021
import DeleteIcon from './delete.svg?react';
@@ -60,6 +61,7 @@ export {
6061
ClockIcon,
6162
CloseIcon,
6263
CommentIcon,
64+
CommentBoxIcon,
6365
DefaultProfileIcon,
6466
DownChevronIcon,
6567
GoogleIcon,

‎src/components/A/ATopicCard.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import { TopicResponse } from '@interfaces/api/topic';
1212

1313
import { colors } from '@styles/theme';
1414

15+
import { MeatballIcon } from '@icons/index';
16+
1517
import { getDateDiff } from '@utils/date';
1618

1719
interface AlphaTopicCardProps {
@@ -34,6 +36,8 @@ const AlphaTopicCard = React.memo(({ topic, onVote, chip }: AlphaTopicCardProps)
3436
onVote(topic.topicId, side);
3537
};
3638

39+
const handleOptionClick = () => {};
40+
3741
return (
3842
<>
3943
<Col padding={'20px'}>
@@ -46,7 +50,10 @@ const AlphaTopicCard = React.memo(({ topic, onVote, chip }: AlphaTopicCardProps)
4650
<Text size={18} weight={500} color={colors.white}>
4751
{topic.topicTitle}
4852
</Text>
49-
<button> - </button>
53+
{/* TBD: 1차 스펙 아웃 */}
54+
{/* <button onClick={handleOptionClick}>
55+
<MeatballIcon fill={colors.white_60} />
56+
</button> */}
5057
</Row>
5158
<Col gap={5} style={{ marginBottom: 14 }}>
5259
<ProgressBar

‎src/components/B/BTopicCard.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { TopicResponse } from '@interfaces/api/topic';
88

99
import { colors } from '@styles/theme';
1010

11+
import { MeatballIcon } from '@icons/index';
12+
1113
import { getDateDiff } from '@utils/date';
1214

1315
import { CardContainer, CardFooter } from './BTopicCard.styles';
@@ -24,6 +26,8 @@ const BTopicCard = ({ topic }: BTopicCardProps) => {
2426
navigate(`/topics/b/${topic.topicId}`, { state: { topic } });
2527
};
2628

29+
const handleOptionClick = () => {};
30+
2731
return (
2832
<>
2933
<CardContainer onClick={handleCardClick}>
@@ -57,7 +61,10 @@ const BTopicCard = ({ topic }: BTopicCardProps) => {
5761
<Text size={18} weight={600} color={colors.white}>
5862
{topic.topicTitle}
5963
</Text>
60-
<button>-</button>
64+
{/* TBD: 1차 스펙 아웃 */}
65+
{/* <button onClick={handleOptionClick}>
66+
<MeatballIcon fill={colors.white_60} />
67+
</button> */}
6168
</Row>
6269
<Row gap={5}>
6370
<BTopicChoice

‎src/components/Home/Comment/Comment.tsx

+10-2
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,18 @@ const Comment = React.memo(({ comment, choices }: CommentProps) => {
8383
</Row>
8484
<Text
8585
size={14}
86-
color={comment.writersVotedOption === 'CHOICE_A' ? colors.A_60 : colors.B_60}
86+
color={
87+
comment.writersVotedOption
88+
? comment.writersVotedOption === 'CHOICE_A'
89+
? colors.A_60
90+
: colors.B_60
91+
: colors.purple
92+
}
8793
weight={600}
8894
>
89-
{choices[comment.writersVotedOption === 'CHOICE_A' ? 0 : 1].content.text}
95+
{comment.writersVotedOption
96+
? choices[comment.writersVotedOption === 'CHOICE_A' ? 0 : 1].content.text
97+
: '작성자'}
9098
</Text>
9199
</Col>
92100
</Row>

‎src/components/Home/CommentBox/CommentBox.styles.tsx

+4-31
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,6 @@ export const CommentContainer = styled.div`
99
align-items: center;
1010
justify-content: flex-start;
1111
width: 100%;
12-
padding: 41px 20px 0;
13-
`;
14-
15-
export const CommentHeader = styled.div`
16-
display: flex;
17-
align-items: center;
18-
justify-content: space-between;
19-
width: 100%;
20-
height: 22px;
21-
`;
22-
23-
export const KeywordContainer = styled.div`
24-
display: flex;
25-
gap: 6px;
26-
align-items: center;
27-
justify-content: flex-start;
2812
`;
2913

3014
export const CommnetBodyContainer = styled.div`
@@ -41,8 +25,7 @@ export const CommentInfoContainer = styled.div`
4125
align-items: center;
4226
justify-content: flex-start;
4327
width: 100%;
44-
height: 40px;
45-
padding: 0 16px;
28+
padding: 10px 16px;
4629
margin-top: 11px;
4730
background-color: #64519b;
4831
border-radius: 10px 10px 0 0;
@@ -54,46 +37,36 @@ export const Comment = styled.div`
5437
display: flex;
5538
gap: 10px;
5639
align-items: center;
57-
justify-content: flex-start;
40+
justify-content: center;
5841
width: 100%;
59-
height: 57px;
60-
padding: 0 16px;
6142
background-color: ${colors.navy2};
6243
border-radius: 0 0 10px 10px;
6344
`;
6445

6546
export const Blur = styled('div')<{ $voted: boolean }>`
6647
box-sizing: border-box;
67-
display: flex;
68-
gap: 10px;
69-
align-items: center;
70-
justify-content: flex-start;
7148
width: 100%;
72-
height: 29px;
7349
background-color: transparent;
74-
filter: ${(props) => (!props.$voted ? 'blur(2px)' : 'blur(0px)')};
50+
filter: ${(props) => (!props.$voted ? 'blur(2px)' : 'unset')};
7551
`;
7652

7753
export const HighlightText = styled.span`
7854
color: white;
7955
`;
8056

8157
export const CommentButton = styled.button`
82-
position: absolute;
83-
top: 50%;
84-
left: 50%;
8558
display: inline-flex;
8659
align-items: center;
8760
justify-content: center;
8861
width: 140px;
8962
height: 30px;
9063
padding: 4px 14px;
64+
margin: 14px 0;
9165
font-size: 1.5rem;
9266
font-weight: 700;
9367
line-height: 0%;
9468
color: ${colors.white};
9569
text-align: center;
9670
background-color: ${colors.purple};
9771
border-radius: 91px;
98-
transform: translate(-50%, -50%);
9972
`;

‎src/components/Home/CommentBox/CommentBox.tsx

+38-88
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
1-
import React from 'react';
1+
import { formatToKoreanNumber } from '@toss/utils';
22

3-
import useReportTopic from '@apis/topic/useReportTopic';
4-
import { Col } from '@components/commons/Flex/Flex';
5-
import ActionModalButton from '@components/commons/Modal/ActionModalButton';
3+
import { Row } from '@components/commons/Flex/Flex';
4+
import ProfileImg from '@components/commons/ProfileImg/ProfileImg';
65
import Text from '@components/commons/Text/Text';
7-
import { UserProfileImage } from '@components/Home/TopicCard/TopicCard.styles';
8-
import useModal from '@hooks/useModal/useModal';
96
import { LatestComment } from '@interfaces/api/comment';
10-
import { TopicResponse } from '@interfaces/api/topic';
117

128
import { colors } from '@styles/theme';
139

14-
import { HideIcon, MeatballIcon, RefreshIcon, ReportIcon } from '@icons/index';
15-
1610
import {
1711
CommentContainer,
18-
CommentHeader,
19-
KeywordContainer,
2012
CommentInfoContainer,
2113
Comment,
2214
HighlightText,
@@ -27,104 +19,62 @@ import {
2719

2820
interface CommentBoxProps {
2921
onClick: () => void;
30-
topicId: number;
3122
hasVoted: boolean;
32-
side: TopicResponse['topicSide'];
33-
keyword: TopicResponse['keyword'];
3423
commentCount: number;
3524
voteCount: number;
36-
latestComment: LatestComment | undefined;
25+
previewComment: LatestComment | undefined;
26+
isBig?: boolean;
3727
}
3828

3929
const CommentBox = ({
4030
onClick,
41-
topicId,
42-
side,
43-
keyword,
4431
commentCount,
4532
voteCount,
46-
latestComment,
33+
previewComment,
4734
hasVoted,
35+
isBig = false,
4836
}: CommentBoxProps) => {
49-
const { Modal, toggleModal } = useModal('action');
50-
const reportMutation = useReportTopic(topicId);
51-
52-
const handleOnClickCommentMenu = () => {
53-
toggleModal();
54-
};
55-
56-
const handleHideTopic = () => {};
57-
58-
const handleReportTopic = () => {
59-
reportMutation.mutate();
60-
toggleModal();
61-
};
62-
63-
const handleRevoteTopic = () => {
64-
throw new Error('투표 다시하기 기능을 사용할 수 없습니다.');
65-
};
66-
6737
return (
6838
<CommentContainer>
69-
<CommentHeader>
70-
<KeywordContainer>
71-
<Text size={13} weight={'regular'} color={colors.purple}>
72-
{side === 'TOPIC_A' ? 'A' : 'B'} 사이드
73-
</Text>
74-
{keyword && (
75-
<>
76-
{' '}
77-
<Text size={14} weight={'regular'} color={colors.white_20}>
78-
|
79-
</Text>
80-
<Text size={13} weight={'regular'} color={colors.white_60}>
81-
{keyword.keywordName}
82-
</Text>
83-
</>
84-
)}
85-
</KeywordContainer>
86-
<button onClick={handleOnClickCommentMenu}>
87-
<MeatballIcon fill={colors.white_60} />
88-
</button>
89-
</CommentHeader>
9039
<CommnetBodyContainer onClick={onClick}>
9140
<CommentInfoContainer>
41+
{previewComment && (
42+
<Text size={14} weight={600} color={colors.white_60}>
43+
<HighlightText>{formatToKoreanNumber(commentCount)}</HighlightText>의 댓글
44+
</Text>
45+
)}
9246
<Text size={14} weight={600} color={colors.white_60}>
93-
<HighlightText>{commentCount}천개</HighlightText>의 댓글
94-
</Text>
95-
<Text size={14} weight={600} color={colors.white_60}>
96-
<HighlightText>{voteCount}</HighlightText>이 선택했어요
47+
<HighlightText>{formatToKoreanNumber(voteCount)}</HighlightText>이 선택했어요
9748
</Text>
9849
</CommentInfoContainer>
9950
<Comment>
100-
<Blur $voted={hasVoted}>
101-
<UserProfileImage src={latestComment?.writer?.profileImageUrl || ''} />
102-
<Text size={15} weight={'regular'} color={colors.white}>
103-
{latestComment?.content || ''}
104-
</Text>
105-
</Blur>
106-
{!hasVoted && <CommentButton>선택하고 댓글 보기</CommentButton>}
51+
{hasVoted ? (
52+
<Blur $voted={hasVoted}>
53+
{previewComment ? (
54+
<Row
55+
gap={10}
56+
alignItems="center"
57+
margin={isBig ? '25px 16px' : '18px 16px'}
58+
width={'unset'}
59+
>
60+
<ProfileImg url={previewComment.writer?.profileImageUrl} size={22} />
61+
<Text size={15} weight={'regular'} color={colors.white} align="left" ellipsis={2}>
62+
{previewComment.content || ''}
63+
</Text>
64+
</Row>
65+
) : (
66+
<Row justifyContent="center" margin={isBig ? '35px 0' : '18px 0px'}>
67+
<Text size={14} color={colors.white_60}>
68+
선택 후 가장 먼저 댓글을 작성해 보세요!
69+
</Text>
70+
</Row>
71+
)}
72+
</Blur>
73+
) : (
74+
<CommentButton>선택하고 댓글 보기</CommentButton>
75+
)}
10776
</Comment>
10877
</CommnetBodyContainer>
109-
<Modal>
110-
<Col padding={'36px 24px'} gap={20}>
111-
<ActionModalButton
112-
handleClick={handleHideTopic}
113-
Icon={() => <HideIcon />}
114-
label={'이런 토픽은 안볼래요'}
115-
/>
116-
<ActionModalButton
117-
handleClick={handleReportTopic}
118-
Icon={() => <ReportIcon />}
119-
label={'신고하기'}
120-
/>
121-
<ActionModalButton
122-
handleClick={handleRevoteTopic}
123-
Icon={() => <RefreshIcon />}
124-
label={'투표 다시 하기'}
125-
/>
126-
</Col>
127-
</Modal>
12878
</CommentContainer>
12979
);
13080
};

‎src/components/Home/TopicCard/TopicCard.styles.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ export const TopicCardContainer = styled.div`
99
display: flex;
1010
flex-direction: column;
1111
align-items: center;
12-
margin-bottom: 100px;
1312
overflow: hidden;
1413
`;
1514

@@ -91,3 +90,9 @@ export const SelectTextContainer = styled.div<{ $voted: boolean }>`
9190
margin: 4px 0 0;
9291
visibility: ${(props) => (props.$voted ? 'hidden' : 'visible')};
9392
`;
93+
94+
export const TopicFooter = styled.div`
95+
box-sizing: border-box;
96+
width: 100%;
97+
padding: 50px 20px 20px;
98+
`;

‎src/components/Home/TopicCard/TopicCard.tsx

+94-40
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
import React, { useEffect, useRef, useState } from 'react';
1+
import React, { useEffect, useRef } from 'react';
22
import { useSearchParams } from 'react-router-dom';
33
import { useSwiperSlide } from 'swiper/react';
44

5-
import { useLatestComment } from '@apis/comment/useComment';
5+
import { usePreviewComment } from '@apis/comment/useComment';
6+
import useReportTopic from '@apis/topic/useReportTopic';
67
import useVoteTopic from '@apis/topic/useVoteTopic';
8+
import { Col, Row } from '@components/commons/Flex/Flex';
9+
import ActionModalButton from '@components/commons/Modal/ActionModalButton';
710
import ProfileImg from '@components/commons/ProfileImg/ProfileImg';
811
import Text from '@components/commons/Text/Text';
912
import { Toast } from '@components/commons/Toast/Toast';
@@ -12,14 +15,21 @@ import CommentBox from '@components/Home/CommentBox/CommentBox';
1215
import Timer from '@components/Home/Timer/Timer';
1316
import VoteCompletion from '@components/Home/VoteCompletion/VoteCompletion';
1417
import useBottomSheet from '@hooks/useBottomSheet/useBottomSheet';
15-
import { LatestComment } from '@interfaces/api/comment';
18+
import useModal from '@hooks/useModal/useModal';
1619
import { Choice, TopicResponse } from '@interfaces/api/topic';
1720

1821
import { useAuthStore } from '@store/auth';
1922

2023
import { colors } from '@styles/theme';
2124

22-
import { LeftDoubleArrowIcon, RightDoubleArrowIcon } from '@icons/index';
25+
import {
26+
HideIcon,
27+
LeftDoubleArrowIcon,
28+
MeatballIcon,
29+
RefreshIcon,
30+
ReportIcon,
31+
RightDoubleArrowIcon,
32+
} from '@icons/index';
2333

2434
import { ResponseError } from '@apis/fetch';
2535

@@ -32,6 +42,7 @@ import {
3242
UserInfoContainer,
3343
TopicCardContainer,
3444
SelectTextContainer,
45+
TopicFooter,
3546
} from './TopicCard.styles';
3647

3748
interface TopicCardProps {
@@ -45,44 +56,49 @@ const TopicCard = ({ topic }: TopicCardProps) => {
4556

4657
const swiperSlide = useSwiperSlide();
4758
const { BottomSheet: CommentSheet, toggleSheet } = useBottomSheet({});
48-
const voteMutation = useVoteTopic();
49-
const { data: latestCommentData, isSuccess } = useLatestComment(
59+
/** Home의 useTopics에서 사용한 req와 동일하게 할것 */
60+
const voteMutation = useVoteTopic({
61+
status: 'VOTING',
62+
side: 'TOPIC_B',
63+
size: 10,
64+
});
65+
const { data: previewComment } = usePreviewComment(
5066
topic.topicId,
5167
topic.selectedOption !== null || isMyTopic
5268
);
53-
const [latestComment, setLatestComment] = useState<LatestComment | undefined>();
69+
const { Modal, toggleModal } = useModal('action');
70+
const reportMutation = useReportTopic(topic.topicId);
5471

5572
const containerRef = useRef<HTMLDivElement | null>(null);
5673

57-
useEffect(() => {
58-
if (swiperSlide.isActive) {
59-
setSearchParams((searchParams) => {
60-
searchParams.set('topicId', topic.topicId.toString());
61-
return searchParams;
62-
});
63-
}
64-
}, [swiperSlide]);
74+
const handleHideTopic = () => {};
6575

66-
useEffect(() => {
67-
if (isSuccess) {
68-
setLatestComment(latestCommentData.data[0] as LatestComment);
69-
}
70-
}, [isSuccess]);
76+
const handleReportTopic = () => {
77+
reportMutation.mutate();
78+
toggleModal();
79+
};
80+
81+
const handleRevoteTopic = () => {
82+
throw new Error('투표 다시하기 기능을 사용할 수 없습니다.');
83+
};
7184

7285
const handleOnClickCommentBox = () => {
7386
if (isMyTopic || topic.selectedOption !== null) {
7487
toggleSheet();
7588
}
7689
};
7790

91+
const handleOnClickCommentMenu = () => {
92+
toggleModal();
93+
};
94+
7895
const handleOnVote = async (choiceOption: Choice['choiceOption']) => {
7996
try {
80-
const data = await voteMutation.mutateAsync({
97+
await voteMutation.mutateAsync({
8198
topicId: topic.topicId,
8299
choiceOption: choiceOption,
83100
votedAt: new Date().getTime() / 1000,
84101
});
85-
setLatestComment(data.latestComment);
86102
return true;
87103
} catch (error) {
88104
if (error instanceof ResponseError) {
@@ -96,14 +112,7 @@ const TopicCard = ({ topic }: TopicCardProps) => {
96112

97113
return (
98114
<React.Fragment>
99-
<TopicCardContainer
100-
ref={containerRef}
101-
style={{
102-
marginBottom: containerRef.current
103-
? window.innerHeight - containerRef.current.scrollHeight + 60
104-
: 0,
105-
}}
106-
>
115+
<TopicCardContainer ref={containerRef}>
107116
<BestTopicCotainer>
108117
<Text size={18} color={colors.purple}>
109118
실시간 인기 토픽
@@ -140,20 +149,65 @@ const TopicCard = ({ topic }: TopicCardProps) => {
140149
</Text>
141150
<RightDoubleArrowIcon />
142151
</SelectTextContainer>
143-
<CommentBox
144-
side={topic.topicSide}
145-
hasVoted={topic.selectedOption !== null || isMyTopic}
146-
topicId={topic.topicId}
147-
commentCount={0}
148-
voteCount={0}
149-
keyword={topic.keyword}
150-
latestComment={latestComment}
151-
onClick={handleOnClickCommentBox}
152-
/>
152+
<TopicFooter>
153+
<Row>
154+
<Row gap={6}>
155+
<Text size={13} weight={'regular'} color={colors.purple}>
156+
{topic.topicSide === 'TOPIC_A' ? 'A' : 'B'} 사이드
157+
</Text>
158+
{topic.keyword && (
159+
<>
160+
<Text size={14} weight={'regular'} color={colors.white_20}>
161+
|
162+
</Text>
163+
<Text size={13} weight={'regular'} color={colors.white_60}>
164+
{topic.keyword.keywordName}
165+
</Text>
166+
</>
167+
)}
168+
</Row>
169+
<button onClick={handleOnClickCommentMenu}>
170+
<MeatballIcon fill={colors.white_60} />
171+
</button>
172+
</Row>
173+
<CommentBox
174+
hasVoted={topic.selectedOption !== null || isMyTopic}
175+
commentCount={topic.commentCount}
176+
voteCount={topic.voteCount}
177+
previewComment={previewComment}
178+
onClick={handleOnClickCommentBox}
179+
/>
180+
</TopicFooter>
153181
</TopicCardContainer>
182+
<div
183+
style={{
184+
height: containerRef.current
185+
? window.innerHeight - containerRef.current.scrollHeight + 80
186+
: 0,
187+
}}
188+
/>
154189
<CommentSheet>
155190
<TopicComments topic={topic} />
156191
</CommentSheet>
192+
<Modal>
193+
<Col padding={'36px 24px'} gap={20}>
194+
<ActionModalButton
195+
handleClick={handleHideTopic}
196+
Icon={() => <HideIcon />}
197+
label={'이런 토픽은 안볼래요'}
198+
/>
199+
<ActionModalButton
200+
handleClick={handleReportTopic}
201+
Icon={() => <ReportIcon />}
202+
label={'신고하기'}
203+
/>
204+
<ActionModalButton
205+
handleClick={handleRevoteTopic}
206+
Icon={() => <RefreshIcon />}
207+
label={'투표 다시 하기'}
208+
/>
209+
</Col>
210+
</Modal>
157211
</React.Fragment>
158212
);
159213
};

‎src/components/Home/TopicComments/TopicComments.styles.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ export const CommentsContainer = styled.div`
1515
overflow-y: auto;
1616
`;
1717

18+
export const EmptyCommentContainer = styled.div`
19+
display: flex;
20+
flex: 1;
21+
align-items: center;
22+
justify-content: center;
23+
`;
24+
1825
export const CommentInputContainer = styled.div`
1926
width: 100%;
2027
padding: 10px 20px 32px;

‎src/components/Home/TopicComments/TopicComments.tsx

+19-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import React, { memo, useLayoutEffect, useState } from 'react';
1+
import { formatToKoreanNumber } from '@toss/utils';
2+
import React, { memo, useState } from 'react';
23

34
import { useComments, useCreateComment } from '@apis/comment/useComment';
45
import { Row } from '@components/commons/Flex/Flex';
@@ -13,6 +14,7 @@ import {
1314
CommentInput,
1415
CommentInputContainer,
1516
CommentsContainer,
17+
EmptyCommentContainer,
1618
TopicCommentsContainer,
1719
TopicCommentsHeader,
1820
} from './TopicComments.styles';
@@ -26,9 +28,7 @@ const TopicComments = memo(({ topic }: TopicCommentsProps) => {
2628
const commentMutation = useCreateComment(topic.topicId);
2729
const [newComment, setNewComment] = useState('');
2830

29-
const commentCount = data?.pages.reduce((acc, page) => {
30-
return acc + page.data.length;
31-
}, 0);
31+
const commentCount = data?.pages[0].pageInfo.total;
3232

3333
const comments = React.useMemo(() => data?.pages.flatMap((page) => page.data), [data]);
3434

@@ -37,18 +37,27 @@ const TopicComments = memo(({ topic }: TopicCommentsProps) => {
3737
<TopicCommentsHeader className="draggable">
3838
<Row className="draggable">
3939
<Text className="draggable" size={18} weight={500} color={colors.black}>
40-
{commentCount}
40+
{formatToKoreanNumber(commentCount || 0)}
4141
</Text>
4242
<Text className="draggable" size={18} weight={500} color={colors.black_40}>
4343
의 댓글
4444
</Text>
4545
</Row>
4646
</TopicCommentsHeader>
47-
<CommentsContainer>
48-
{comments?.map((comment) => (
49-
<Comment key={comment.commentId} comment={comment} choices={topic.choices} />
50-
))}
51-
</CommentsContainer>
47+
{commentCount !== 0 ? (
48+
<CommentsContainer>
49+
{comments?.map((comment) => (
50+
<Comment key={comment.commentId} comment={comment} choices={topic.choices} />
51+
))}
52+
</CommentsContainer>
53+
) : (
54+
<EmptyCommentContainer>
55+
<Text size={16} color={colors.black_80}>
56+
가장 먼저 댓글을 작성해 보세요!
57+
</Text>
58+
</EmptyCommentContainer>
59+
)}
60+
5261
<CommentInputContainer>
5362
<CommentInput
5463
type="text"

‎src/components/Home/TopicSwiper/TopicSlide.tsx

-87
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11
import React, { useRef, useState } from 'react';
2+
import styled from 'styled-components';
23
import SwiperCore from 'swiper';
34
import { Navigation } from 'swiper/modules';
4-
import { Swiper } from 'swiper/react';
5+
import { Swiper, SwiperSlide } from 'swiper/react';
56

6-
import TopicSlide from './TopicSlide';
7+
import { TopicResponse } from '@interfaces/api/topic';
8+
9+
import { colors } from '@styles/theme';
10+
11+
import { RightChevronIcon } from '@icons/index';
12+
13+
import TopicCard from '../TopicCard/TopicCard';
714

815
SwiperCore.use([Navigation]);
916

1017
interface TopicSwiperProps {
11-
children: JSX.Element[];
18+
topics: TopicResponse[];
1219
fetchNextPage: () => void;
1320
hasNextPage: boolean;
1421
}
1522

16-
const TopicSwiper = ({ children, fetchNextPage, hasNextPage }: TopicSwiperProps) => {
23+
const TopicSwiper = ({ topics, fetchNextPage, hasNextPage }: TopicSwiperProps) => {
1724
const swiperRef = useRef<SwiperCore>();
1825
const [init, setInit] = useState(true);
1926
const [prevDisabled, setPrevDisabled] = useState(false);
@@ -30,7 +37,7 @@ const TopicSwiper = ({ children, fetchNextPage, hasNextPage }: TopicSwiperProps)
3037
fetchNextPage();
3138
}
3239

33-
if (!hasNextPage && children.length - swiper.realIndex === 1) {
40+
if (!hasNextPage && topics.length - swiper.realIndex === 1) {
3441
setNextDisabled(true);
3542
}
3643
};
@@ -42,25 +49,42 @@ const TopicSwiper = ({ children, fetchNextPage, hasNextPage }: TopicSwiperProps)
4249
modules={[Navigation]}
4350
spaceBetween={0}
4451
slidesPerView={1}
45-
style={{ height: '100%' }}
52+
style={{ height: '100%', overflowY: 'auto' }}
4653
onBeforeInit={(swiper) => (swiperRef.current = swiper)}
4754
onSlideChange={handleSlideChange}
4855
onReachBeginning={handleReachBeginning}
4956
observer={true}
57+
observeSlideChildren={true}
58+
observeParents={true}
5059
>
51-
{children.map((child) => {
60+
{topics.map((topic) => {
5261
return (
53-
<TopicSlide
54-
key={child.key}
55-
init={init}
56-
prevDisabled={prevDisabled}
57-
swiperRef={swiperRef}
58-
setNextDisabled={setNextDisabled}
59-
nextDisabled={nextDisabled}
60-
setPrevDisabled={setPrevDisabled}
61-
>
62-
{child}
63-
</TopicSlide>
62+
<SwiperSlide key={topic.topicId}>
63+
<SlideContainer>
64+
<PrevButton
65+
disabled={init || prevDisabled}
66+
onClick={() => {
67+
swiperRef.current?.slidePrev();
68+
setNextDisabled(false);
69+
}}
70+
>
71+
<RightChevronIcon
72+
style={{ transform: 'rotate(180deg)' }}
73+
stroke={colors.white_40}
74+
/>
75+
</PrevButton>
76+
<TopicCard topic={topic} />
77+
<NextButton
78+
disabled={nextDisabled}
79+
onClick={() => {
80+
swiperRef.current?.slideNext();
81+
setPrevDisabled(false);
82+
}}
83+
>
84+
<RightChevronIcon stroke={colors.white_40} />
85+
</NextButton>
86+
</SlideContainer>
87+
</SwiperSlide>
6488
);
6589
})}
6690
</Swiper>
@@ -69,3 +93,35 @@ const TopicSwiper = ({ children, fetchNextPage, hasNextPage }: TopicSwiperProps)
6993
};
7094

7195
export default TopicSwiper;
96+
97+
const SlideButton = styled.button<{ disabled: boolean }>`
98+
position: absolute;
99+
top: 63px;
100+
z-index: 100;
101+
width: 32px;
102+
height: 32px;
103+
padding: 4.8px 10.4px;
104+
cursor: pointer;
105+
background-color: transparent;
106+
107+
${(props) => props.disabled && `display: none;`}
108+
`;
109+
110+
const PrevButton = styled(SlideButton)`
111+
left: 20px;
112+
`;
113+
114+
const NextButton = styled(SlideButton)`
115+
right: 20px;
116+
`;
117+
118+
const SlideContainer = styled.div`
119+
overflow-y: auto;
120+
121+
&::-webkit-scrollbar {
122+
display: none;
123+
}
124+
125+
-ms-overflow-style: none; /* IE and Edge */
126+
scrollbar-width: none; /* Firefox */
127+
`;

‎src/components/commons/Chip/CommentChip.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import styled from 'styled-components';
22

33
import { colors } from '@styles/theme';
44

5-
import { CommentIcon } from '@icons/index';
5+
import { CommentBoxIcon, CommentIcon } from '@icons/index';
66

77
import Text from '../Text/Text';
88

@@ -19,7 +19,7 @@ const CommentChip = ({ count, onClick, backgroundColor }: CommentChipProps) => {
1919
onClick={onClick}
2020
style={{ backgroundColor: backgroundColor || colors.black_40 }}
2121
>
22-
<CommentIcon width={18} height={18} />
22+
<CommentBoxIcon width={18} height={18} />
2323
<Text size={13} weight={400} color={colors.white}>
2424
댓글
2525
</Text>

‎src/components/commons/Flex/Flex.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ interface FlexProps extends React.HTMLAttributes<HTMLDivElement> {
99
padding?: React.CSSProperties['padding'];
1010
margin?: React.CSSProperties['margin'];
1111
borderRadius?: React.CSSProperties['borderRadius'];
12+
width?: React.CSSProperties['width'];
1213
children: React.ReactNode;
1314
}
1415

@@ -52,6 +53,7 @@ interface StyledFlexProps {
5253
padding?: React.CSSProperties['padding'];
5354
margin?: React.CSSProperties['margin'];
5455
gap?: React.CSSProperties['gap'];
56+
width?: React.CSSProperties['width'];
5557
}
5658

5759
const Flex = styled.div<StyledFlexProps>`
@@ -72,7 +74,11 @@ const Flex = styled.div<StyledFlexProps>`
7274
css`
7375
gap: ${gap}px;
7476
`}
75-
width: 100%;
77+
${({ width = '100%' }) =>
78+
width &&
79+
css`
80+
width: ${width};
81+
`}
7682
${({ padding }) =>
7783
padding &&
7884
css`

‎src/components/commons/Modal/ActionModalButton.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import React from 'react';
22

3+
import { colors } from '@styles/theme';
4+
35
import { Row } from '../Flex/Flex';
46
import Text from '../Text/Text';
57

@@ -14,7 +16,7 @@ const ActionModalButton = ({ handleClick, Icon, label }: ActionModalButtonProps)
1416
<button onClick={handleClick}>
1517
<Row alignItems={'center'} gap={14}>
1618
<Icon />
17-
<Text size={16} weight={500}>
19+
<Text size={16} weight={500} color={colors.black}>
1820
{label}
1921
</Text>
2022
</Row>

‎src/components/commons/ProfileImg/ProfileImg.tsx

+5-7
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,11 @@ interface ProfileImgProps {
1111
const ProfileImg = ({ url, size = '100%', rounded = true }: ProfileImgProps) => {
1212
if (!url) {
1313
return (
14-
<div>
15-
<DefaultProfileIcon
16-
width={size}
17-
height={size}
18-
style={rounded ? { borderRadius: '50%' } : {}}
19-
/>
20-
</div>
14+
<DefaultProfileIcon
15+
width={size}
16+
height={size}
17+
style={rounded ? { borderRadius: '50%' } : {}}
18+
/>
2119
);
2220
}
2321

‎src/components/commons/RadioInput/RadioInput.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ const RadioLabel = styled.label<{ checked: boolean }>`
2828
font-weight: 700;
2929
color: ${({ theme }) => theme.colors.white};
3030
text-align: center;
31-
background-color: ${({ checked, theme }) => (checked ? theme.colors.purple : theme.colors.navy2)};
31+
background-color: ${({ checked, theme }) => (checked ? theme.colors.navy2 : 'transparent')};
32+
border: ${({ checked, theme }) => (checked ? 'unset' : `1px solid ${theme.colors.white_20}`)};
3233
border-radius: 10px;
3334
`;
3435

‎src/components/commons/SelectInput/SelectInput.tsx

+7-4
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,11 @@ const StyledSelect = styled.select<{ selected: boolean }>`
2929
font-size: 1.4rem;
3030
font-weight: 600;
3131
line-height: 1.4;
32-
color: ${({ theme }) => theme.colors.purple};
32+
color: ${({ selected, theme }) => (selected ? theme.colors.white : theme.colors.purple_60)};
3333
appearance: none;
34-
background-color: #342b52;
34+
background-color: ${({ theme }) => theme.colors.navy2_40};
3535
border: none;
3636
border-radius: 10px;
37-
opacity: 0.6;
3837
3938
&:focus {
4039
outline: none;
@@ -61,7 +60,11 @@ const SelectInput = (props: SelectInputProps) => {
6160
<SelectLabel htmlFor={id}>
6261
<DownChevronIcon />
6362
</SelectLabel>
64-
<StyledSelect {...register(id, options)} selected={watch(id) !== undefined} id={id}>
63+
<StyledSelect
64+
{...register(id, options)}
65+
selected={watch(id) !== undefined && watch(id) !== ''}
66+
id={id}
67+
>
6568
<option value="" disabled selected style={{ display: 'none' }}>
6669
{placeholder}
6770
</option>

‎src/components/commons/Text/Text.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface TextProps extends React.HTMLAttributes<HTMLDivElement> {
1010
align?: React.CSSProperties['textAlign'];
1111
lineHeight?: React.CSSProperties['lineHeight'];
1212
noWrap?: boolean;
13+
ellipsis?: number;
1314
}
1415

1516
const getFontSize = (sizeInPx: number) => `${sizeInPx / 10}rem`;
@@ -25,6 +26,16 @@ const Text = React.memo((props: TextProps) => {
2526
});
2627

2728
const StyledText = styled('div')<TextProps>`
29+
${({ ellipsis }) =>
30+
ellipsis &&
31+
css`
32+
display: -webkit-box;
33+
height: 100%;
34+
overflow: hidden;
35+
word-wrap: break-word;
36+
-webkit-line-clamp: ${ellipsis};
37+
-webkit-box-orient: vertical;
38+
`}
2839
${({ lineHeight = 1.4 }) =>
2940
lineHeight &&
3041
css`

‎src/components/commons/TextInput/TextInput.styles.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { TextInputTheme } from './theme';
44

55
const StyledInput = styled.input<{ inputTheme: TextInputTheme }>`
66
width: 100%;
7-
font-size: ${({ inputTheme }) => inputTheme.placeholderSize};
7+
font-size: ${({ inputTheme }) => inputTheme.fontSize};
88
font-weight: ${({ inputTheme }) => inputTheme.fontWeight};
99
line-height: 1.4;
1010
color: ${({ theme }) => theme.colors.white};

‎src/components/commons/TextInput/theme.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ export const theme1: TextInputTheme = {
1818
fontSize: '1.4rem',
1919
fontWeight: 600,
2020
backgroundColor: colors.navy2_40,
21-
placeholderColor: colors.purple,
21+
placeholderColor: colors.purple_60,
22+
placeholderSize: '1.4rem',
2223
};
2324

2425
export const theme2: TextInputTheme = {
@@ -28,6 +29,7 @@ export const theme2: TextInputTheme = {
2829
fontWeight: 500,
2930
backgroundColor: colors.navy2_40,
3031
placeholderColor: colors.purple,
32+
placeholderSize: '1.6rem',
3133
};
3234

3335
export const theme3: TextInputTheme = {

‎src/interfaces/api/comment.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ export interface CommentResponse {
22
commentId: number;
33
topicId: number;
44
writer: Writer;
5-
writersVotedOption?: 'CHOICE_A' | 'CHOICE_B';
5+
writersVotedOption: 'CHOICE_A' | 'CHOICE_B' | null;
66
content: string;
77
commentReaction: CommentReaction;
88
createdAt: number;

‎src/interfaces/api/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export interface PagingDataResponse<T> {
66
interface PageInfo {
77
page: number;
88
size: number;
9+
total: number;
910
empty: boolean;
1011
last: boolean;
1112
}

‎src/interfaces/api/profile.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,9 @@ interface PresignedURLResponse {
1010
presignedUrl: string;
1111
}
1212

13-
export type { ProfileResponse, PresignedURLResponse };
13+
interface ModifyProfileRequestDTO {
14+
nickname: string;
15+
job: string;
16+
}
17+
18+
export type { ProfileResponse, PresignedURLResponse, ModifyProfileRequestDTO };

‎src/routes/Auth/signup/Signup.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ const Signup = () => {
103103
options={CONFIG.NICKNAME.options}
104104
placeholder={'한글, 영문, 숫자 최대 8자'}
105105
right={() => (
106-
<Text size={14} weight={700} color={colors.purple}>
106+
<Text size={14} weight={700} color={colors.purple_60}>
107107
{nicknameProgress}
108108
</Text>
109109
)}

‎src/routes/B/BTopic.tsx

+103-35
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,117 @@
1-
import React, { useState } from 'react';
1+
import React from 'react';
22
import { useLocation } from 'react-router-dom';
33

4-
import { useLatestComment } from '@apis/comment/useComment';
4+
import { usePreviewComment } from '@apis/comment/useComment';
5+
import useReportTopic from '@apis/topic/useReportTopic';
56
import useVoteTopic from '@apis/topic/useVoteTopic';
7+
import { Col, Row } from '@components/commons/Flex/Flex';
68
import BackButton from '@components/commons/Header/BackButton/BackButton';
79
import Layout from '@components/commons/Layout/Layout';
10+
import ActionModalButton from '@components/commons/Modal/ActionModalButton';
811
import ProfileImg from '@components/commons/ProfileImg/ProfileImg';
912
import Text from '@components/commons/Text/Text';
13+
import { Toast } from '@components/commons/Toast/Toast';
1014
import ChoiceSlider from '@components/Home/ChoiceSlider/ChoiceSlider';
1115
import CommentBox from '@components/Home/CommentBox/CommentBox';
1216
import Timer from '@components/Home/Timer/Timer';
1317
import {
1418
TopicCardContainer,
15-
BestTopicCotainer,
1619
TopicContainer,
1720
Topic,
18-
UserInfoContainer,
1921
SelectTextContainer,
22+
TopicFooter,
2023
} from '@components/Home/TopicCard/TopicCard.styles';
2124
import TopicComments from '@components/Home/TopicComments/TopicComments';
2225
import VoteCompletion from '@components/Home/VoteCompletion/VoteCompletion';
2326
import useBottomSheet from '@hooks/useBottomSheet/useBottomSheet';
24-
import { LatestComment } from '@interfaces/api/comment';
27+
import useModal from '@hooks/useModal/useModal';
2528
import { Choice, TopicResponse } from '@interfaces/api/topic';
2629

30+
import { useAuthStore } from '@store/auth';
31+
2732
import { colors } from '@styles/theme';
2833

29-
import { LeftDoubleArrowIcon, RightDoubleArrowIcon } from '@icons/index';
34+
import {
35+
HideIcon,
36+
LeftDoubleArrowIcon,
37+
MeatballIcon,
38+
RefreshIcon,
39+
ReportIcon,
40+
RightDoubleArrowIcon,
41+
} from '@icons/index';
42+
43+
import { ResponseError } from '@apis/fetch';
3044

3145
interface BTopicProps {
3246
topic: TopicResponse;
3347
}
3448

3549
const BTopic = () => {
3650
const location = useLocation();
37-
const { topic } = location.state as BTopicProps;
3851
const { BottomSheet: CommentSheet, toggleSheet } = useBottomSheet({});
39-
const voteMutation = useVoteTopic();
40-
const { data: latestCommentData, isSuccess } = useLatestComment(
52+
const { Modal, toggleModal } = useModal('action');
53+
const memberId = useAuthStore((store) => store.memberId);
54+
const { topic } = location.state as BTopicProps;
55+
const isMyTopic = topic.author.id === memberId;
56+
57+
const { data: previewComment } = usePreviewComment(
4158
topic.topicId,
42-
topic.selectedOption !== null
59+
topic.selectedOption !== null || isMyTopic
4360
);
44-
const [latestComment, setLatestComment] = useState<LatestComment | undefined>();
61+
const voteMutation = useVoteTopic();
62+
const reportMutation = useReportTopic(topic.topicId);
4563

4664
const handleVote = async (choiceOption: Choice['choiceOption']) => {
47-
const data = await voteMutation.mutateAsync({
48-
topicId: topic.topicId,
49-
choiceOption: choiceOption,
50-
votedAt: new Date().getTime() / 1000,
51-
});
52-
setLatestComment(data.latestComment);
53-
return true;
65+
try {
66+
await voteMutation.mutateAsync({
67+
topicId: topic.topicId,
68+
choiceOption: choiceOption,
69+
votedAt: new Date().getTime() / 1000,
70+
});
71+
return true;
72+
} catch (error) {
73+
if (error instanceof ResponseError) {
74+
if (error.errorData.abCode === 'VOTED_BY_AUTHOR') {
75+
Toast.error('토픽을 작성한 사람은 투표할 수 없어요');
76+
}
77+
}
78+
return false;
79+
}
5480
};
5581

5682
const handleCommentBoxClick = () => {
57-
if (topic.selectedOption !== null) {
83+
if (isMyTopic || topic.selectedOption !== null) {
5884
toggleSheet();
5985
}
6086
};
6187

88+
const handleTopicOptionClick = () => {
89+
toggleModal();
90+
};
91+
92+
const handleHideTopic = () => {};
93+
94+
const handleReportTopic = () => {
95+
reportMutation.mutate();
96+
toggleModal();
97+
};
98+
99+
const handleRevoteTopic = () => {
100+
throw new Error('투표 다시하기 기능을 사용할 수 없습니다.');
101+
};
102+
62103
return (
63104
<React.Fragment>
64105
<Layout hasBottomNavigation={false} HeaderLeft={<BackButton />}>
65106
<TopicCardContainer>
107+
<Text size={18} color={colors.purple}>
108+
{topic.keyword?.keywordName}
109+
</Text>
66110
<TopicContainer>
67111
<Topic style={{ width: 170, wordBreak: 'keep-all', overflowWrap: 'break-word' }}>
68112
{topic.topicTitle}
69113
</Topic>
70114
</TopicContainer>
71-
<UserInfoContainer>
72-
<ProfileImg url={topic.author.profileImageUrl} size={20} />
73-
<Text size={14} weight={'regular'} color={colors.white_60}>
74-
{topic.author.nickname}
75-
</Text>
76-
</UserInfoContainer>
77115
{topic.selectedOption !== null ? (
78116
<VoteCompletion
79117
side={topic.selectedOption === 'CHOICE_A' ? 'A' : 'B'}
@@ -94,21 +132,51 @@ const BTopic = () => {
94132
</Text>
95133
<RightDoubleArrowIcon />
96134
</SelectTextContainer>
97-
<CommentBox
98-
side={topic.topicSide}
99-
hasVoted={topic.selectedOption !== null}
100-
topicId={topic.topicId}
101-
commentCount={0}
102-
voteCount={0}
103-
keyword={topic.keyword}
104-
latestComment={latestComment}
105-
onClick={handleCommentBoxClick}
106-
/>
135+
<TopicFooter>
136+
<Row>
137+
<Row gap={8} alignItems="center">
138+
<ProfileImg url={topic.author.profileImageUrl} size={'22px'} />
139+
<Text size={13} color={colors.white_60}>
140+
{topic.author.nickname}
141+
</Text>
142+
</Row>
143+
<button onClick={handleTopicOptionClick}>
144+
<MeatballIcon fill={colors.white_60} />
145+
</button>
146+
</Row>
147+
<CommentBox
148+
hasVoted={topic.selectedOption !== null || isMyTopic}
149+
commentCount={topic.commentCount}
150+
voteCount={topic.voteCount}
151+
previewComment={previewComment}
152+
onClick={handleCommentBoxClick}
153+
isBig={true}
154+
/>
155+
</TopicFooter>
107156
</TopicCardContainer>
108157
</Layout>
109158
<CommentSheet>
110159
<TopicComments topic={topic} />
111160
</CommentSheet>
161+
<Modal>
162+
<Col padding={'36px 24px'} gap={20}>
163+
<ActionModalButton
164+
handleClick={handleHideTopic}
165+
Icon={() => <HideIcon />}
166+
label={'이런 토픽은 안볼래요'}
167+
/>
168+
<ActionModalButton
169+
handleClick={handleReportTopic}
170+
Icon={() => <ReportIcon />}
171+
label={'신고하기'}
172+
/>
173+
<ActionModalButton
174+
handleClick={handleRevoteTopic}
175+
Icon={() => <RefreshIcon />}
176+
label={'투표 다시 하기'}
177+
/>
178+
</Col>
179+
</Modal>
112180
</React.Fragment>
113181
);
114182
};

‎src/routes/Home/Home.tsx

+10-7
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ import 'swiper/css/scrollbar';
66
import useTopics from '@apis/topic/useTopics';
77
import NotificationButton from '@components/commons/Header/NotificationButton/NotificationButton';
88
import Layout from '@components/commons/Layout/Layout';
9-
import TopicCard from '@components/Home/TopicCard/TopicCard';
109
import TopicSwiper from '@components/Home/TopicSwiper/TopicSwiper';
1110

1211
import { Container } from './Home.styles';
1312

1413
const Home = () => {
15-
const { data, fetchNextPage, hasNextPage } = useTopics();
14+
const { data, fetchNextPage, hasNextPage } = useTopics({
15+
status: 'VOTING',
16+
side: 'TOPIC_B',
17+
size: 10,
18+
});
1619

1720
const topics = data?.pages.flatMap((page) => page.data);
1821

@@ -26,11 +29,11 @@ const Home = () => {
2629
<Layout HeaderRight={<NotificationButton />}>
2730
<Container>
2831
{topics && (
29-
<TopicSwiper fetchNextPage={handleFetchNextPage} hasNextPage={hasNextPage}>
30-
{topics.map((topic) => (
31-
<TopicCard topic={topic} key={topic.topicId} />
32-
))}
33-
</TopicSwiper>
32+
<TopicSwiper
33+
topics={topics}
34+
fetchNextPage={handleFetchNextPage}
35+
hasNextPage={hasNextPage}
36+
/>
3437
)}
3538
</Container>
3639
</Layout>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { color } from 'framer-motion';
2+
import { styled } from 'styled-components';
3+
4+
import { colors } from '@styles/theme';
5+
6+
export const Container = styled.div`
7+
position: relative;
8+
display: flex;
9+
flex-direction: column;
10+
align-items: flex-start;
11+
justify-content: flex-start;
12+
width: 100%;
13+
height: 100%;
14+
padding: 24px 20px 0;
15+
background-color: ${(props) => props.theme.colors.navy};
16+
`;
17+
export const BackButton = styled.button`
18+
width: 24px;
19+
height: 24px;
20+
padding: 3.6px 7.8px;
21+
`;
22+
23+
export const Divider = styled.div`
24+
display: flex;
25+
align-items: center;
26+
justify-content: center;
27+
width: 100%;
28+
height: 1px;
29+
background-color: ${colors.white_20};
30+
`;
31+
32+
export const SubmitButton = styled.div`
33+
position: absolute;
34+
bottom: 48px;
35+
left: 50%;
36+
justify-content: center;
37+
width: 100%;
38+
padding: 0 20px;
39+
transform: translateX(-50%);
40+
`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import React, { useEffect, useRef, useState } from 'react';
2+
import { FormProvider, useForm } from 'react-hook-form';
3+
import { useLocation, useNavigate } from 'react-router-dom';
4+
5+
import { useModifyProfile } from '@apis/profile/useProfile';
6+
import DefaultButton from '@components/commons/Button/DefaultButton';
7+
import { Col, Row } from '@components/commons/Flex/Flex';
8+
import InputField from '@components/commons/InputField/InputField';
9+
import Layout from '@components/commons/Layout/Layout';
10+
import SelectInput from '@components/commons/SelectInput/SelectInput';
11+
import Text from '@components/commons/Text/Text';
12+
import TextInput from '@components/commons/TextInput/TextInput';
13+
import { theme1, theme2 } from '@components/commons/TextInput/theme';
14+
import { Toast } from '@components/commons/Toast/Toast';
15+
import { ModifyProfileRequestDTO } from '@interfaces/api/profile';
16+
17+
import { CONFIG, INPUT_TYPE } from '@constants/form';
18+
import { JOBS } from '@constants/signup';
19+
20+
import { colors, theme } from '@styles/theme';
21+
22+
import { RightChevronIcon } from '@icons/index';
23+
24+
import { ResponseError } from '@apis/fetch';
25+
26+
import { BackButton, Container, Divider, SubmitButton } from './ModifyProfile.styles';
27+
28+
interface ModifyProfileProps {
29+
birth: string;
30+
gender: string;
31+
}
32+
33+
const MAX_NICKNAME_LENGTH = 8;
34+
35+
const ModifyProfile = () => {
36+
const navigate = useNavigate();
37+
const location = useLocation();
38+
const { birth, gender } = location.state as ModifyProfileProps;
39+
40+
const methods = useForm<ModifyProfileRequestDTO>({ mode: 'onChange' });
41+
const modifyProfileMutation = useModifyProfile();
42+
43+
const nicknameProgress = methods.watch(INPUT_TYPE.NICKNAME)
44+
? `${methods.watch(INPUT_TYPE.NICKNAME)?.length}/${MAX_NICKNAME_LENGTH}`
45+
: '';
46+
47+
const handleSubmitForm = async () => {
48+
const data = methods.getValues();
49+
try {
50+
const res = await modifyProfileMutation.mutateAsync({
51+
nickname: data.nickname,
52+
job: data.job,
53+
});
54+
console.log(res);
55+
navigate(-1);
56+
} catch (error) {
57+
console.log(error);
58+
if (error instanceof ResponseError) {
59+
Toast.error(error.errorData.errorContent.message);
60+
}
61+
}
62+
};
63+
64+
return (
65+
<Layout
66+
hasBottomNavigation={false}
67+
HeaderLeft={
68+
<BackButton onClick={() => navigate(-1)}>
69+
<RightChevronIcon style={{ transform: 'rotate(180deg)' }} stroke={colors.white} />
70+
</BackButton>
71+
}
72+
HeaderCenter={
73+
<Text size={20} weight={600} color={colors.white}>
74+
내 정보 수정
75+
</Text>
76+
}
77+
>
78+
<FormProvider {...methods}>
79+
<Container>
80+
<Col gap={24} alignItems="flex-start">
81+
<Col gap={20} alignItems="flex-start">
82+
<Row gap={12} alignItems="center">
83+
<Text size={14} weight={600} color={colors.white_40}>
84+
생년월일
85+
</Text>
86+
<Text size={14} weight={400} color={colors.white_60}>
87+
{birth}
88+
</Text>
89+
</Row>
90+
<Row gap={12} alignItems="center">
91+
<Text size={14} weight={600} color={colors.white_40}>
92+
성별
93+
</Text>
94+
<Text size={14} weight={400} color={colors.white_60}>
95+
{gender}
96+
</Text>
97+
</Row>
98+
</Col>
99+
<Divider />
100+
<Col gap={40} alignItems="flex-start" justifyContent="space-between">
101+
<InputField label="변경할 닉네임을 입력해 주세요">
102+
<TextInput
103+
id={INPUT_TYPE.NICKNAME}
104+
theme={theme1}
105+
options={CONFIG.NICKNAME.options}
106+
placeholder={'한글, 영문, 숫자 최대 8자'}
107+
right={() => (
108+
<Text size={14} weight={700} color={colors.purple_60}>
109+
{nicknameProgress}
110+
</Text>
111+
)}
112+
/>
113+
</InputField>
114+
<InputField label="변경할 직업을 입력해 주세요">
115+
<SelectInput
116+
id={INPUT_TYPE.JOB}
117+
options={CONFIG.JOB.options}
118+
placeholder="직업 선택하기"
119+
selectOptions={JOBS}
120+
/>
121+
</InputField>
122+
</Col>
123+
</Col>
124+
<SubmitButton>
125+
<DefaultButton
126+
title={'변경하기'}
127+
onClick={handleSubmitForm}
128+
disabled={!methods.formState.isValid}
129+
/>
130+
</SubmitButton>
131+
</Container>
132+
</FormProvider>
133+
</Layout>
134+
);
135+
};
136+
137+
export default ModifyProfile;

‎src/routes/MyPage/MyPage.styles.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const BackButton = styled.button`
2727
padding: 3.6px 7.8px;
2828
`;
2929

30-
export const MyInfoUpdateButton = styled.button`
30+
export const ModifyProfileButton = styled.button`
3131
display: flex;
3232
align-items: center;
3333
justify-content: center;

‎src/routes/MyPage/MyPage.tsx

+32-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useEffect, useRef, useState } from 'react';
22
import { set } from 'react-hook-form';
3-
import { useNavigate } from 'react-router-dom';
3+
import { useLocation, useNavigate } from 'react-router-dom';
44

55
import {
66
getProfile,
@@ -25,7 +25,7 @@ import {
2525
Divider,
2626
ImageInput,
2727
ModalDivider,
28-
MyInfoUpdateButton,
28+
ModifyProfileButton,
2929
PhotoButton,
3030
ProfileImgContainer,
3131
} from './MyPage.styles';
@@ -37,6 +37,8 @@ const MyPage = () => {
3737

3838
const [profileImg, setProfileImg] = useState<string | null>(null);
3939
const [nickName, setNickName] = useState<string>('');
40+
const [birth, setBirth] = useState<string>('');
41+
const [gender, setGender] = useState<string>('');
4042
const [fileName, setFileName] = useState<string>('');
4143
const [file, setFile] = useState<File>();
4244
const [presignedURL, setpresignedURL] = useState<string>('');
@@ -117,6 +119,10 @@ const MyPage = () => {
117119
toggleModal();
118120
};
119121

122+
const handleOnClickModifyProfile = () => {
123+
navigate('/mypage/modify-profile', { state: { birth, gender } });
124+
};
125+
120126
useEffect(() => {
121127
const fetchProfile = async () => {
122128
try {
@@ -125,6 +131,8 @@ const MyPage = () => {
125131
setProfileImg(response.profileImageUrl);
126132
}
127133
setNickName(response.nickname);
134+
setBirth(response.birth.replace(/-/g, '/'));
135+
setGender(response.gender === 'FEMALE' ? '여자' : '남자');
128136
} catch (error) {
129137
console.error(error);
130138
}
@@ -168,15 +176,26 @@ const MyPage = () => {
168176
</Col>
169177
<Col gap={32} alignItems="flex-start">
170178
<Row padding="0 7px" gap={3} alignItems="center">
171-
<Text size={16} weight={400} color={colors.white}>
179+
<Text
180+
size={16}
181+
weight={400}
182+
color={colors.white}
183+
onClick={handleOnClickModifyProfile}
184+
>
172185
내 정보 수정
173186
</Text>
174-
<MyInfoUpdateButton>
187+
<ModifyProfileButton onClick={handleOnClickModifyProfile}>
175188
<RightChevronIcon stroke={colors.white_40} />
176-
</MyInfoUpdateButton>
189+
</ModifyProfileButton>
177190
</Row>
178191
<Divider />
179-
<Text style={{ padding: '0 7px' }} size={16} weight={400} color={colors.white}>
192+
<Text
193+
style={{ padding: '0 7px' }}
194+
size={16}
195+
weight={400}
196+
color={colors.white}
197+
align="start"
198+
>
180199
약관
181200
</Text>
182201
<Row padding="0 7px" gap={10} alignItems="center">
@@ -188,7 +207,13 @@ const MyPage = () => {
188207
</Text>
189208
</Row>
190209
<Divider />
191-
<Text style={{ padding: '0 7px' }} size={16} weight={400} color={colors.white_40}>
210+
<Text
211+
style={{ padding: '0 7px' }}
212+
size={16}
213+
weight={400}
214+
color={colors.white_40}
215+
align="start"
216+
>
192217
로그아웃
193218
</Text>
194219
</Col>

‎src/routes/index.tsx

+5-3
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,20 @@ import Loading from '@components/commons/Loading/Loading';
1212

1313
import { useAuthStore } from '@store/auth';
1414

15-
1615
const Home = lazy(() => import('./Home/Home'));
1716
const ATopics = lazy(() => import('./A/ATopics'));
1817
const BTopic = lazy(() => import('./B/BTopic'));
1918
const BTopics = lazy(() => import('./B/BTopics'));
2019
const TopicSideSelection = lazy(() => import('./Topic/TopicSideSelection'));
2120
const TopicCreate = lazy(() => import('./Topic/Create/TopicCreate'));
2221
const MyPage = lazy(() => import('./MyPage/MyPage'));
22+
const ModifyProfile = lazy(() => import('./MyPage/ModifyProfile/ModifyProfile'));
2323
const Notification = lazy(() => import('./Notification/Notification'));
2424
const Login = lazy(() => import('./Auth/login/Login'));
2525
const KakaoLogin = lazy(() => import('./Auth/kakao/KakaoLogin'));
2626
const GoogleLogin = lazy(() => import('./Auth/google/GoogleLogin'));
2727
const Signup = lazy(() => import('./Auth/signup/Signup'));
2828

29-
3029
const AuthRoute = () => {
3130
const reLogin = useAuthStore((store) => store.reLogin);
3231

@@ -72,7 +71,10 @@ const Router = () => {
7271
<Route path="create" element={<TopicSideSelection />} />
7372
<Route path="create/:topicSide" element={<TopicCreate />} />
7473
</Route>
75-
<Route path="mypage" element={<MyPage />} />
74+
<Route path="mypage">
75+
<Route index element={<MyPage />} />
76+
<Route path="modify-profile" element={<ModifyProfile />} />
77+
</Route>
7678
<Route path="notifications" element={<Notification />} />
7779
</Route>
7880
<Route path="login">

‎yarn.lock

+15
Original file line numberDiff line numberDiff line change
@@ -1746,6 +1746,13 @@
17461746
dependencies:
17471747
regenerator-runtime "^0.14.0"
17481748

1749+
"@babel/runtime@^7.14.8":
1750+
version "7.23.9"
1751+
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7"
1752+
integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==
1753+
dependencies:
1754+
regenerator-runtime "^0.14.0"
1755+
17491756
"@babel/template@^7.22.15", "@babel/template@^7.22.5":
17501757
version "7.22.15"
17511758
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38"
@@ -3537,6 +3544,14 @@
35373544
dependencies:
35383545
date-fns "^2.25.0"
35393546

3547+
"@toss/utils@^1.4.6":
3548+
version "1.4.6"
3549+
resolved "https://registry.yarnpkg.com/@toss/utils/-/utils-1.4.6.tgz#5f2db113253ad2d44a4e746f34a07ed4e7076d9e"
3550+
integrity sha512-MXLAL44Q6HU3fZ8v0DxgMiVqLLwSxegqPd7OsOsV5/tSJL5Ti6zFZRDMidIi73wp7IE3VAa4YswSEiuXbEwHZA==
3551+
dependencies:
3552+
"@babel/runtime" "^7.14.8"
3553+
date-fns "^2.25.0"
3554+
35403555
"@types/aria-query@^5.0.1":
35413556
version "5.0.2"
35423557
resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.2.tgz#6f1225829d89794fd9f891989c9ce667422d7f64"

0 commit comments

Comments
 (0)
Please sign in to comment.