From 83fa628e77d943edd4f9aa17e3d49d030753f2eb Mon Sep 17 00:00:00 2001 From: JeongHoon <113702672+jeonghoon11@users.noreply.github.com> Date: Tue, 9 Sep 2025 22:40:22 +0900 Subject: [PATCH 1/6] =?UTF-8?q?Feat(bds):=20LikeButton=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84=20(#376)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: LikeButton 컴포넌트 추가 및 스토리북 작성 * feat: LikeButton 컴포넌트의 상태 프로퍼티를 'liked'에서 'isActive'로 변경 * feat: LikeButton 컴포넌트의 aria-label 프로퍼티를 동적으로 변경 * fix: 코드리뷰 반영 * fix: 코드리뷰 반영 * fix: LikeButton 컴포넌트 사이즈 Props 수정 및 스토리북 수정 --- packages/bds-ui/src/components/index.ts | 1 + .../components/like-button/like-button.css.ts | 22 +++ .../like-button/like-button.stories.tsx | 139 ++++++++++++++++++ .../components/like-button/like-button.tsx | 35 +++++ 4 files changed, 197 insertions(+) create mode 100644 packages/bds-ui/src/components/like-button/like-button.css.ts create mode 100644 packages/bds-ui/src/components/like-button/like-button.stories.tsx create mode 100644 packages/bds-ui/src/components/like-button/like-button.tsx diff --git a/packages/bds-ui/src/components/index.ts b/packages/bds-ui/src/components/index.ts index 556ee69d..be983cba 100644 --- a/packages/bds-ui/src/components/index.ts +++ b/packages/bds-ui/src/components/index.ts @@ -6,6 +6,7 @@ export { default as Content } from './content/content'; export { default as Floating } from './floating/floating'; export { default as Indicator } from './indicator/indicator'; export { default as Input } from './input/input'; +export { default as LikeButton } from './like-button/like-button'; export { default as Modal } from './modal/modal'; export { default as ModalContainer } from './modal/modal-container'; export * from './modal/store/modal-store'; diff --git a/packages/bds-ui/src/components/like-button/like-button.css.ts b/packages/bds-ui/src/components/like-button/like-button.css.ts new file mode 100644 index 00000000..076a6741 --- /dev/null +++ b/packages/bds-ui/src/components/like-button/like-button.css.ts @@ -0,0 +1,22 @@ +import { recipe } from '@vanilla-extract/recipes'; + +import { themeVars } from '../../styles'; + +export const iconVariants = recipe({ + variants: { + isActive: { + true: { color: themeVars.color.error }, + false: { color: themeVars.color.gray600 }, + }, + size: { + sm: { + width: '1.6rem', + height: '1.6rem', + }, + md: { + width: '2.4rem', + height: '2.4rem', + }, + }, + }, +}); diff --git a/packages/bds-ui/src/components/like-button/like-button.stories.tsx b/packages/bds-ui/src/components/like-button/like-button.stories.tsx new file mode 100644 index 00000000..6e0a4603 --- /dev/null +++ b/packages/bds-ui/src/components/like-button/like-button.stories.tsx @@ -0,0 +1,139 @@ +import { useState } from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; + +import LikeButton from './like-button'; + +const meta: Meta = { + title: 'Common/LikeButton', + component: LikeButton, + parameters: { + layout: 'centered', + docs: { + description: { + component: ` +**LikeButton** 컴포넌트는 '좋아요' 토글을 위한 디자인 시스템 컴포넌트입니다. +상태는 외부에서 관리하도록 설계하였습니다. + +## 설계 고려사항 +- 접근성을 고려해 설계했습니다. +- 크기는 \`size\` variant('sm' | 'md')로 관리합니다. sm: '1.6rem', md: '2.4rem' + +## 접근성 +- \`aria-label\`은 동작(눌렀을 때의 행동)을 설명합니다. 사용자로부터 \`ariaLabelWhenActive\` / \`ariaLabelWhenInActive\`를 입력받아 사용합니다. + +## Props 요약 +- **size**: 아이콘 크기 프리셋 ('sm' | 'md') +- **isActive**: 현재 좋아요 상태 (boolean) +- **onToggle?**: 클릭 시 호출되는 콜백 (상태 토글은 상위에서 처리; 맨 아래 \`Interactive\`에서 테스트) +- **ariaLabelWhenActive?**: 활성화(좋아요됨) 상태일 때 스크린리더 라벨 +- **ariaLabelWhenInActive?**: 비활성화(좋아요 안됨) 상태일 때 스크린리더 라벨 + `, + }, + }, + }, + argTypes: { + size: { + control: { type: 'radio' }, + options: ['sm', 'md'], + description: '아이콘 크기 프리셋', + table: { type: { summary: "'sm' | 'md'" } }, + }, + isActive: { + control: { type: 'boolean' }, + description: '좋아요 상태를 나타냅니다.', + table: { type: { summary: 'boolean' } }, + }, + onToggle: { + description: + '버튼 클릭 시 호출됩니다. 상태 변경은 상위에서 처리하면 됩니다.', + table: { type: { summary: '() => void' } }, + }, + ariaLabelWhenActive: { + control: { type: 'text' }, + description: '활성화(좋아요됨) 상태에서의 aria-label.', + table: { type: { summary: 'string' } }, + }, + ariaLabelWhenInActive: { + control: { type: 'text' }, + description: '비활성(좋아요 안됨) 상태에서의 aria-label.', + table: { type: { summary: 'string' } }, + }, + }, + tags: ['autodocs'], +}; + +export default meta; + +type Story = StoryObj; + +// Storybook 전용 Wrapper +function SizeWrapper({ + size, + children, +}: { + size: 'sm' | 'md'; + children: React.ReactNode; +}) { + const className = `sb-like-size-${size}`; + const css = ` + .${className} svg { + width: ${size === 'sm' ? '1.6rem' : '2.4rem'} !important; + height: ${size === 'sm' ? '1.6rem' : '2.4rem'} !important; + } + `; + return ( +
+ + {children} +
+ ); +} + +export const Default: Story = { + args: { + size: 'md', + isActive: false, + ariaLabelWhenActive: '좋아요 취소', + ariaLabelWhenInActive: '좋아요', + }, + render: (args) => ( + + + + ), +}; + +export const Liked: Story = { + args: { + size: 'md', + isActive: true, + ariaLabelWhenActive: '좋아요 취소', + ariaLabelWhenInActive: '좋아요', + }, + render: (args) => ( + + + + ), +}; + +export const Interactive: Story = { + args: { + size: 'sm', + isActive: false, + ariaLabelWhenActive: '좋아요 취소', + ariaLabelWhenInActive: '좋아요', + }, + render: (args) => { + const [liked, setLiked] = useState(Boolean(args.isActive)); + return ( + + setLiked(!liked)} + /> + + ); + }, +}; diff --git a/packages/bds-ui/src/components/like-button/like-button.tsx b/packages/bds-ui/src/components/like-button/like-button.tsx new file mode 100644 index 00000000..a8df38fa --- /dev/null +++ b/packages/bds-ui/src/components/like-button/like-button.tsx @@ -0,0 +1,35 @@ +import { Icon } from '@bds/ui/icons'; + +import { iconVariants } from './like-button.css'; + +interface LikeButtonProps { + size: 'sm' | 'md'; + onToggle?: () => void; + isActive: boolean; + ariaLabelWhenActive?: string; + ariaLabelWhenInActive?: string; +} + +const LikeButton = ({ + size, + onToggle, + isActive, + ariaLabelWhenActive, + ariaLabelWhenInActive, +}: LikeButtonProps) => { + return ( + + ); +}; + +export default LikeButton; From ae2a46a6adc42482689eb73cc11250cff8959851 Mon Sep 17 00:00:00 2001 From: 1jiwoo27 <101466143+1jiwoo27@users.noreply.github.com> Date: Wed, 10 Sep 2025 00:52:20 +0900 Subject: [PATCH 2/6] =?UTF-8?q?Refactor(client):=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20api=20=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81=20(#380)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: import문 변경 * refactor: 로그인 api 리팩터링 --- .../login-fallback/login-fallback-page.tsx | 2 +- apps/client/src/shared/types/schema.d.ts | 677 +++++++++++++++--- .../login-fallback/hooks/use-social-login.ts | 16 +- 3 files changed, 586 insertions(+), 109 deletions(-) diff --git a/apps/client/src/pages/login-fallback/login-fallback-page.tsx b/apps/client/src/pages/login-fallback/login-fallback-page.tsx index 9b857333..b3a006cb 100644 --- a/apps/client/src/pages/login-fallback/login-fallback-page.tsx +++ b/apps/client/src/pages/login-fallback/login-fallback-page.tsx @@ -1,5 +1,5 @@ import { useEffect } from 'react'; -import { useLocation } from 'react-router-dom'; +import { useLocation } from 'react-router'; import { useSocialLogin } from '@widgets/login-fallback/hooks/use-social-login'; diff --git a/apps/client/src/shared/types/schema.d.ts b/apps/client/src/shared/types/schema.d.ts index 0884a5eb..d62c02b7 100644 --- a/apps/client/src/shared/types/schema.d.ts +++ b/apps/client/src/shared/types/schema.d.ts @@ -56,6 +56,30 @@ export interface paths { patch?: never; trace?: never; }; + '/posts/{post-id}/likes': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * 게시글 좋아요 생성 + * @description 유저가 커뮤니티 게시글에 좋아요를 생성합니다. + */ + post: operations['createPostLike']; + /** + * 게시글 좋아요 삭제 + * @description 유저가 커뮤니티 게시글에 생성했던 좋아요를 삭제합니다. + */ + delete: operations['deletePostLike']; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; '/posts/{post-id}/comments': { parameters: { query?: never; @@ -97,6 +121,47 @@ export interface paths { patch?: never; trace?: never; }; + '/oauth/kakao/logout': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** 로그아웃 */ + post: operations['logout']; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/oauth/kakao/login': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * 카카오 로그인 + * @description 카카오 API를 통해 로그인합니다. + */ + get: operations['kakaoCallback_1']; + put?: never; + /** + * 카카오 로그인 - POST + * @description 카카오 API를 통해 로그인합니다. POST 요청을 사용하여 Body에 필요한 데이터를 받아옵니다. + */ + post: operations['kakaoCallback']; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; '/insurances/reports': { parameters: { query?: never; @@ -117,6 +182,26 @@ export interface paths { patch?: never; trace?: never; }; + '/files/upload': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * 이미지 업로드 API + * @description mediaType을 통해 PresignedUrl을 발급받습니다. + */ + post: operations['createdUrls']; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; '/users/me/report-summary': { parameters: { query?: never; @@ -257,26 +342,6 @@ export interface paths { patch?: never; trace?: never; }; - '/oauth/kakao/login': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * 카카오 로그인 - * @description 카카오 API를 통해 로그인합니다. - */ - get: operations['kakaoCallback']; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; '/insurances/reports/{insurance-report-id}': { parameters: { query?: never; @@ -449,6 +514,15 @@ export interface components { */ postId?: number; }; + BaseResponseVoid: { + /** + * Format: int32 + * @example 200 + */ + code?: number; + message?: string; + data?: Record; + }; CommentCreateRequest: { /** * @description 댓글 내용 @@ -471,6 +545,40 @@ export interface components { /** @description 리프레쉬 토큰 */ refreshToken?: string; }; + BaseResponseString: { + /** + * Format: int32 + * @example 200 + */ + code?: number; + message?: string; + data?: string; + }; + OAuthLoginRequest: { + code: string; + redirectUrl?: string; + }; + BaseResponseKaKaoLoginResponse: { + /** + * Format: int32 + * @example 200 + */ + code?: number; + message?: string; + data?: components['schemas']['KaKaoLoginResponse']; + }; + KaKaoLoginResponse: { + /** + * Format: int64 + * @description 유저 PK + * @example 1 + */ + userId?: number; + /** @description 액세스 토큰 */ + accessToken?: string; + /** @description 리프레쉬 토큰 */ + refreshToken?: string; + }; InsuranceReportRequest: { /** * @description 실명 @@ -593,6 +701,22 @@ export interface components { /** Format: uuid */ insuranceReportId?: string; }; + PresignedUrlRequest: { + /** @description 파일 형식 */ + mediaType?: string[]; + }; + BaseResponsePresignedUrlResponse: { + /** + * Format: int32 + * @example 200 + */ + code?: number; + message?: string; + data?: components['schemas']['PresignedUrlResponse']; + }; + PresignedUrlResponse: { + presignedUrls?: string[]; + }; BaseResponseInsuranceReportSummaryResponse: { /** * Format: int32 @@ -972,27 +1096,6 @@ export interface components { /** @description 마지막 페이지 여부 */ isLast?: boolean; }; - BaseResponseKaKaoLoginResponse: { - /** - * Format: int32 - * @example 200 - */ - code?: number; - message?: string; - data?: components['schemas']['KaKaoLoginResponse']; - }; - KaKaoLoginResponse: { - /** - * Format: int64 - * @description 유저 PK - * @example 1 - */ - userId?: number; - /** @description 액세스 토큰 */ - accessToken?: string; - /** @description 리프레쉬 토큰 */ - refreshToken?: string; - }; BaseResponseInsuranceReportResponse: { /** * Format: int32 @@ -1035,7 +1138,13 @@ export interface components { }; SectionData: { additionalInfo?: string; - statuses?: components['schemas']['ShowCoverageStatus'][]; + resource?: string; + statuses?: components['schemas']['ShowCoverageStatusDetail'][]; + }; + ShowCoverageStatusDetail: { + target?: string; + status?: string; + queryParamValue?: string; }; BaseResponseSurgerySection: { /** @@ -1127,15 +1236,6 @@ export interface components { hyphenCase?: string; coverage?: components['schemas']['CompareCoverage']; }; - BaseResponseVoid: { - /** - * Format: int32 - * @example 200 - */ - code?: number; - message?: string; - data?: Record; - }; }; responses: never; parameters: never; @@ -1508,12 +1608,9 @@ export interface operations { }; }; }; - getComments: { + createPostLike: { parameters: { - query?: { - cursor?: number; - size?: number; - }; + query?: never; header?: never; path: { 'post-id': number; @@ -1528,7 +1625,7 @@ export interface operations { [name: string]: unknown; }; content: { - '*/*': components['schemas']['BaseResponseSliceResponseCommentResponseLong']; + '*/*': components['schemas']['BaseResponseVoid']; }; }; 400: { @@ -1589,7 +1686,7 @@ export interface operations { }; }; }; - createComment: { + deletePostLike: { parameters: { query?: never; header?: never; @@ -1598,11 +1695,7 @@ export interface operations { }; cookie?: never; }; - requestBody: { - content: { - 'application/json': components['schemas']['CommentCreateRequest']; - }; - }; + requestBody?: never; responses: { /** @description OK */ 200: { @@ -1610,7 +1703,7 @@ export interface operations { [name: string]: unknown; }; content: { - '*/*': components['schemas']['BaseResponsePostCreateResponse']; + '*/*': components['schemas']['BaseResponseVoid']; }; }; 400: { @@ -1663,11 +1756,16 @@ export interface operations { }; }; }; - reissue: { + getComments: { parameters: { - query?: never; + query?: { + cursor?: number; + size?: number; + }; header?: never; - path?: never; + path: { + 'post-id': number; + }; cookie?: never; }; requestBody?: never; @@ -1678,7 +1776,7 @@ export interface operations { [name: string]: unknown; }; content: { - '*/*': components['schemas']['BaseResponseTokenReissueResponse']; + '*/*': components['schemas']['BaseResponseSliceResponseCommentResponseLong']; }; }; 400: { @@ -1721,6 +1819,14 @@ export interface operations { 'application/json': unknown; }; }; + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; 500: { headers: { [name: string]: unknown; @@ -1731,16 +1837,18 @@ export interface operations { }; }; }; - issueReport: { + createComment: { parameters: { query?: never; header?: never; - path?: never; + path: { + 'post-id': number; + }; cookie?: never; }; requestBody: { content: { - 'application/json': components['schemas']['InsuranceReportRequest']; + 'application/json': components['schemas']['CommentCreateRequest']; }; }; responses: { @@ -1750,7 +1858,7 @@ export interface operations { [name: string]: unknown; }; content: { - '*/*': components['schemas']['BaseResponseIssueInsuranceReportResponse']; + '*/*': components['schemas']['BaseResponsePostCreateResponse']; }; }; 400: { @@ -1803,7 +1911,7 @@ export interface operations { }; }; }; - getMyLastInsuranceReportSummary: { + reissue: { parameters: { query?: never; header?: never; @@ -1818,7 +1926,7 @@ export interface operations { [name: string]: unknown; }; content: { - '*/*': components['schemas']['BaseResponseInsuranceReportSummaryResponse']; + '*/*': components['schemas']['BaseResponseTokenReissueResponse']; }; }; 400: { @@ -1871,11 +1979,10 @@ export interface operations { }; }; }; - getMyPosts: { + logout: { parameters: { query?: { - cursorId?: number; - size?: number; + 'redirect-url'?: string; }; header?: never; path?: never; @@ -1889,9 +1996,10 @@ export interface operations { [name: string]: unknown; }; content: { - '*/*': components['schemas']['BaseResponseSliceResponseMyPostSummaryResponseLong']; + '*/*': components['schemas']['BaseResponseString']; }; }; + /** @description 경로 변수 값이 누락되었습니다. */ 400: { headers: { [name: string]: unknown; @@ -1900,6 +2008,7 @@ export interface operations { 'application/json': unknown; }; }; + /** @description 유효하지 않은 JWT입니다. */ 401: { headers: { [name: string]: unknown; @@ -1908,6 +2017,7 @@ export interface operations { 'application/json': unknown; }; }; + /** @description 권한이 없습니다. */ 403: { headers: { [name: string]: unknown; @@ -1916,6 +2026,7 @@ export interface operations { 'application/json': unknown; }; }; + /** @description 지원하지 않는 URL입니다. */ 404: { headers: { [name: string]: unknown; @@ -1924,6 +2035,7 @@ export interface operations { 'application/json': unknown; }; }; + /** @description 유효하지 않은 Http 메서드입니다. */ 405: { headers: { [name: string]: unknown; @@ -1932,6 +2044,7 @@ export interface operations { 'application/json': unknown; }; }; + /** @description 외부 서버 오류입니다. */ 500: { headers: { [name: string]: unknown; @@ -1942,11 +2055,11 @@ export interface operations { }; }; }; - getMyComments: { + kakaoCallback_1: { parameters: { - query?: { - cursorId?: number; - size?: number; + query: { + code: string; + 'redirect-url'?: string; }; header?: never; path?: never; @@ -1960,7 +2073,7 @@ export interface operations { [name: string]: unknown; }; content: { - '*/*': components['schemas']['BaseResponseSliceResponseMyCommentSummaryResponseLong']; + '*/*': components['schemas']['BaseResponseKaKaoLoginResponse']; }; }; 400: { @@ -2013,14 +2126,18 @@ export interface operations { }; }; }; - getInfo: { + kakaoCallback: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + 'application/json': components['schemas']['OAuthLoginRequest']; + }; + }; responses: { /** @description OK */ 200: { @@ -2028,7 +2145,7 @@ export interface operations { [name: string]: unknown; }; content: { - '*/*': components['schemas']['BaseResponseUserProfileResponse']; + '*/*': components['schemas']['BaseResponseKaKaoLoginResponse']; }; }; 400: { @@ -2081,14 +2198,18 @@ export interface operations { }; }; }; - getJobs: { + issueReport: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + 'application/json': components['schemas']['InsuranceReportRequest']; + }; + }; responses: { /** @description OK */ 200: { @@ -2096,10 +2217,9 @@ export interface operations { [name: string]: unknown; }; content: { - '*/*': components['schemas']['BaseResponseJobResponses']; + '*/*': components['schemas']['BaseResponseIssueInsuranceReportResponse']; }; }; - /** @description 경로 변수 값이 누락되었습니다. */ 400: { headers: { [name: string]: unknown; @@ -2108,7 +2228,6 @@ export interface operations { 'application/json': unknown; }; }; - /** @description 유효하지 않은 JWT입니다. */ 401: { headers: { [name: string]: unknown; @@ -2117,7 +2236,6 @@ export interface operations { 'application/json': unknown; }; }; - /** @description 권한이 없습니다. */ 403: { headers: { [name: string]: unknown; @@ -2126,7 +2244,6 @@ export interface operations { 'application/json': unknown; }; }; - /** @description 지원하지 않는 URL입니다. */ 404: { headers: { [name: string]: unknown; @@ -2135,7 +2252,6 @@ export interface operations { 'application/json': unknown; }; }; - /** @description 유효하지 않은 Http 메서드입니다. */ 405: { headers: { [name: string]: unknown; @@ -2144,7 +2260,6 @@ export interface operations { 'application/json': unknown; }; }; - /** @description 외부 서버 오류입니다. */ 500: { headers: { [name: string]: unknown; @@ -2155,7 +2270,79 @@ export interface operations { }; }; }; - getDiagnosedDisease: { + createdUrls: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['PresignedUrlRequest']; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + '*/*': components['schemas']['BaseResponsePresignedUrlResponse']; + }; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + }; + }; + getMyLastInsuranceReportSummary: { parameters: { query?: never; header?: never; @@ -2170,7 +2357,285 @@ export interface operations { [name: string]: unknown; }; content: { - '*/*': components['schemas']['BaseResponseDiagnosedDiseaseResponses']; + '*/*': components['schemas']['BaseResponseInsuranceReportSummaryResponse']; + }; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + }; + }; + getMyPosts: { + parameters: { + query?: { + cursorId?: number; + size?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + '*/*': components['schemas']['BaseResponseSliceResponseMyPostSummaryResponseLong']; + }; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + }; + }; + getMyComments: { + parameters: { + query?: { + cursorId?: number; + size?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + '*/*': components['schemas']['BaseResponseSliceResponseMyCommentSummaryResponseLong']; + }; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + }; + }; + getInfo: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + '*/*': components['schemas']['BaseResponseUserProfileResponse']; + }; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': unknown; + }; + }; + }; + }; + getJobs: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + '*/*': components['schemas']['BaseResponseJobResponses']; }; }; /** @description 경로 변수 값이 누락되었습니다. */ @@ -2229,7 +2694,7 @@ export interface operations { }; }; }; - getCoverageSelect: { + getDiagnosedDisease: { parameters: { query?: never; header?: never; @@ -2244,7 +2709,7 @@ export interface operations { [name: string]: unknown; }; content: { - '*/*': components['schemas']['BaseResponseCoveragePreferenceResponses']; + '*/*': components['schemas']['BaseResponseDiagnosedDiseaseResponses']; }; }; /** @description 경로 변수 값이 누락되었습니다. */ @@ -2303,11 +2768,9 @@ export interface operations { }; }; }; - kakaoCallback: { + getCoverageSelect: { parameters: { - query: { - code: string; - }; + query?: never; header?: never; path?: never; cookie?: never; @@ -2320,9 +2783,10 @@ export interface operations { [name: string]: unknown; }; content: { - '*/*': components['schemas']['BaseResponseKaKaoLoginResponse']; + '*/*': components['schemas']['BaseResponseCoveragePreferenceResponses']; }; }; + /** @description 경로 변수 값이 누락되었습니다. */ 400: { headers: { [name: string]: unknown; @@ -2331,6 +2795,7 @@ export interface operations { 'application/json': unknown; }; }; + /** @description 유효하지 않은 JWT입니다. */ 401: { headers: { [name: string]: unknown; @@ -2339,6 +2804,7 @@ export interface operations { 'application/json': unknown; }; }; + /** @description 권한이 없습니다. */ 403: { headers: { [name: string]: unknown; @@ -2347,6 +2813,7 @@ export interface operations { 'application/json': unknown; }; }; + /** @description 지원하지 않는 URL입니다. */ 404: { headers: { [name: string]: unknown; @@ -2355,6 +2822,7 @@ export interface operations { 'application/json': unknown; }; }; + /** @description 유효하지 않은 Http 메서드입니다. */ 405: { headers: { [name: string]: unknown; @@ -2363,6 +2831,7 @@ export interface operations { 'application/json': unknown; }; }; + /** @description 외부 서버 오류입니다. */ 500: { headers: { [name: string]: unknown; diff --git a/apps/client/src/widgets/login-fallback/hooks/use-social-login.ts b/apps/client/src/widgets/login-fallback/hooks/use-social-login.ts index d0c9282d..7d9c92c1 100644 --- a/apps/client/src/widgets/login-fallback/hooks/use-social-login.ts +++ b/apps/client/src/widgets/login-fallback/hooks/use-social-login.ts @@ -2,24 +2,32 @@ import ky from '@toss/ky'; import { useNavigate } from 'react-router'; import { tokenService } from '@shared/auth/services/token-service'; +import { appConfig } from '@shared/configs/app-config'; import { routePath } from '@shared/router/path'; import { paths } from '@shared/types/schema'; export const useSocialLogin = () => { const navigate = useNavigate(); type KakaoResponse = - paths['/oauth/kakao/login']['get']['responses']['200']['content']['*/*']['data']; + paths['/oauth/kakao/login']['post']['responses']['200']['content']['*/*']['data']; + + const getRedirectUrl = () => + import.meta.env.MODE === 'development' + ? appConfig.auth.kakaoLocalRedirectUrl + : appConfig.auth.kakaoProdRedirectUrl; const kakaoLogin = async (code: string) => { if (!code) { throw new Error('코드가 존재하지 않습니다.'); } + const redirectUrl = getRedirectUrl(); + try { const response = await ky - .get( - `${import.meta.env.VITE_API_BASE_URL}/oauth/kakao/login?code=${code}`, - ) + .post(`${appConfig.api.baseUrl}/oauth/kakao/login`, { + json: { code, redirectUrl }, + }) .json(); const { accessToken, refreshToken } = response.data; From 5467a5934e9c30103203e1bf0536d4b8427a17e1 Mon Sep 17 00:00:00 2001 From: Minjeong Kim <88662427+minjeoong@users.noreply.github.com> Date: Wed, 10 Sep 2025 17:38:50 +0900 Subject: [PATCH 3/6] =?UTF-8?q?Refactor(client):=20chip=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=A6=AC=ED=8E=99?= =?UTF-8?q?=ED=86=A0=EB=A7=81=20(#369)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: report chip -> label 로 변경 * fix: homechip -> homecard 네이밍 변경 * feat: 다시 label to chip * Delete packages/bds-ui/src/styles/reset.css.ts * Revert "Delete packages/bds-ui/src/styles/reset.css.ts" This reverts commit 3d831b1c6b39dd04527f21ff213565ce19c6f594. * feat: 롤백 * fix: home-chip -> home-card --- .../home-chip.css.ts => home-card/home-card.css.ts} | 0 .../{home-chip/home-chip.tsx => home-card/home-card.tsx} | 6 +++--- .../widgets/home/components/info-section/info-section.tsx | 4 ++-- .../components/info-section/recommended-info-section.tsx | 6 +++--- .../src/widgets/report/components/accordion/accordion.tsx | 3 ++- apps/client/src/widgets/report/components/chip/chip.tsx | 2 +- 6 files changed, 11 insertions(+), 10 deletions(-) rename apps/client/src/widgets/home/components/{home-chip/home-chip.css.ts => home-card/home-card.css.ts} (100%) rename apps/client/src/widgets/home/components/{home-chip/home-chip.tsx => home-card/home-card.tsx} (83%) diff --git a/apps/client/src/widgets/home/components/home-chip/home-chip.css.ts b/apps/client/src/widgets/home/components/home-card/home-card.css.ts similarity index 100% rename from apps/client/src/widgets/home/components/home-chip/home-chip.css.ts rename to apps/client/src/widgets/home/components/home-card/home-card.css.ts diff --git a/apps/client/src/widgets/home/components/home-chip/home-chip.tsx b/apps/client/src/widgets/home/components/home-card/home-card.tsx similarity index 83% rename from apps/client/src/widgets/home/components/home-chip/home-chip.tsx rename to apps/client/src/widgets/home/components/home-card/home-card.tsx index 73f6b729..08585e26 100644 --- a/apps/client/src/widgets/home/components/home-chip/home-chip.tsx +++ b/apps/client/src/widgets/home/components/home-card/home-card.tsx @@ -2,7 +2,7 @@ import { ReactNode } from 'react'; import { StatusType } from '@shared/types/type.ts'; -import * as styles from './home-chip.css.ts'; +import * as styles from './home-card.css.ts'; const statusMap = { 충분: 'enough', @@ -16,7 +16,7 @@ interface ChipProps { status?: StatusType; } -const HomeChip = ({ icon, title, status }: ChipProps) => { +const HomeCard = ({ icon, title, status }: ChipProps) => { const internalStatus = status ? statusMap[status] : undefined; return ( @@ -34,4 +34,4 @@ const HomeChip = ({ icon, title, status }: ChipProps) => { ); }; -export default HomeChip; +export default HomeCard; diff --git a/apps/client/src/widgets/home/components/info-section/info-section.tsx b/apps/client/src/widgets/home/components/info-section/info-section.tsx index 25fe1c71..77e58fb8 100644 --- a/apps/client/src/widgets/home/components/info-section/info-section.tsx +++ b/apps/client/src/widgets/home/components/info-section/info-section.tsx @@ -6,7 +6,7 @@ import { Swiper, SwiperSlide } from 'swiper/react'; import { Button } from '@bds/ui'; import { Icon } from '@bds/ui/icons'; -import HomeChip from '@widgets/home/components/home-chip/home-chip.tsx'; +import HomeCard from '@widgets/home/components/home-card/home-card.tsx'; import { homeChipConfig } from '@widgets/home/configs/home-chip-config.ts'; import InsuranceTitle from '@shared/components/insurance-title/insurance-title.tsx'; @@ -54,7 +54,7 @@ export const InfoSection = () => { > {homeChipConfig.map((chip, index) => ( - { return ( - } title={chip.title} status={chip.status as StatusType} @@ -124,7 +124,7 @@ export const RecommendedInfoSection = ({ const iconName = targetToIconMap.get(chip.target || ''); return ( - Date: Sat, 13 Sep 2025 01:40:17 +0900 Subject: [PATCH 4/6] =?UTF-8?q?CICD(*):=20=EB=A6=B4=EB=A6=AC=EC=A6=88=20?= =?UTF-8?q?=EB=85=B8=ED=8A=B8=20=EC=9E=90=EB=8F=99=ED=99=94=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20(#386)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: GitHub Release Drafter 설정 추가 * fix: release-drafter.yml에서 GITHUB_TOKEN을 RELEASE_DRAFTER_GITHUB_TOKEN으로 변경 --- .github/release-drafter.yml | 38 +++++++++++++++++++++++++++ .github/workflows/release-drafter.yml | 21 +++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/release-drafter.yml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 00000000..1f3debe6 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,38 @@ +name-template: 'v$RESOLVED_VERSION' +tag-template: 'v$RESOLVED_VERSION' +commitish: main + +categories: + - title: '🚀 Feat' + labels: + - '💡 Feature' + - '🎨 Style' + - '🔨 Refactor' + - title: '🐛 Fix' + labels: + - '💥 Fix' + - '🚨 HOTFIX' + - title: '🧰 Init' + labels: + - '🚀 Deploy' + - '🧹 Chore' + - '🤖 CI/CD' + +change-template: '- $TITLE @$AUTHOR (#$NUMBER)' +change-title-escapes: '\<*_&' + +version-resolver: + major: + labels: + - '🚀 Major' + minor: + labels: + - '🛥️ Minor' + patch: + labels: + - '🚘 Patch' + default: 🚘 Patch + +template: | + ## ✨ Changes + $CHANGES diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 00000000..cdae45a9 --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,21 @@ +name: Release Drafter + +on: + push: + branches: + - main + +permissions: + contents: write + pull-requests: write + +jobs: + draft-release: + runs-on: ubuntu-latest + steps: + - name: Run Release Drafter + uses: release-drafter/release-drafter@v6 + with: + config-name: release-drafter.yml + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_DRAFTER_GITHUB_TOKEN }} From 85cd6b41cf87e8c3e31ceb45a8cf77e4eba4c4ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B3=BD=EC=A7=80=EC=9A=B1=28Hanji=29?= <99489686+gwagjiug@users.noreply.github.com> Date: Sat, 13 Sep 2025 01:58:32 +0900 Subject: [PATCH 5/6] =?UTF-8?q?Chore(*):=20release-drafter.yml=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EA=B8=B0=EB=B3=B8=20=ED=8C=A8=EC=B9=98=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EC=88=98=EC=A0=95=20(#389)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore: release-drafter.yml에서 기본 패치 레이블 수정 --- .github/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 1f3debe6..3e8b2294 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -31,7 +31,7 @@ version-resolver: patch: labels: - '🚘 Patch' - default: 🚘 Patch + default: patch template: | ## ✨ Changes From 9eb7d7eab4534a1feb5cf95faf853c430e9e40d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B3=BD=EC=A7=80=EC=9A=B1=28Hanji=29?= <99489686+gwagjiug@users.noreply.github.com> Date: Sat, 13 Sep 2025 02:15:51 +0900 Subject: [PATCH 6/6] =?UTF-8?q?Chore(*):=20release-drafter.yml=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EB=B3=80=EA=B2=BD=20=EC=82=AC=ED=95=AD=20=EC=A0=95?= =?UTF-8?q?=EB=A0=AC=20=EB=B0=8F=20=ED=95=84=ED=84=B0=EB=A7=81=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=B6=94=EA=B0=80=20(#392)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore: release-drafter.yml에서 변경 사항 정렬 및 필터링 설정 추가 --- .github/release-drafter.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 3e8b2294..ca41faf8 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -33,6 +33,10 @@ version-resolver: - '🚘 Patch' default: patch +sort-by: merged_at +sort-direction: ascending +filter-by-commitish: false + template: | ## ✨ Changes $CHANGES