From 058f4a3b36f792c4fe142bf83e31f4d263971c2e Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 12 Oct 2025 03:39:07 +0900 Subject: [PATCH 01/42] =?UTF-8?q?feat:=20=EB=8B=A4=EC=A4=91=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=EC=84=9C=20=EB=AA=A8=EB=8B=AC=EC=9A=A9=20=EC=98=B5?= =?UTF-8?q?=EC=85=98=20=ED=83=80=EC=9E=85=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/types/application.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/src/types/application.ts b/frontend/src/types/application.ts index 710beea93..267ebb78f 100644 --- a/frontend/src/types/application.ts +++ b/frontend/src/types/application.ts @@ -56,3 +56,9 @@ export interface AnswerItem { id: number; value: string; } + +export interface ApplicationOption { + id: number; + name: string; + url?: string; // 존재하면 외부 링크, 없으면 내부 +} \ No newline at end of file From 7c94b70f1d4e45dac1aadc23fb7f054e6253bec0 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 12 Oct 2025 04:15:24 +0900 Subject: [PATCH 02/42] =?UTF-8?q?feat:=20=EB=8B=A4=EC=A4=91=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=EC=84=9C=20=EC=98=B5=EC=85=98=20=EB=AA=A9=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/mocks/data/mockData.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/frontend/src/mocks/data/mockData.ts b/frontend/src/mocks/data/mockData.ts index 3a246039b..1f076e5ff 100644 --- a/frontend/src/mocks/data/mockData.ts +++ b/frontend/src/mocks/data/mockData.ts @@ -1,4 +1,4 @@ -import { ApplicationFormData } from '@/types/application'; +import { ApplicationFormData, ApplicationOption } from '@/types/application'; type QuestionType = | 'CHOICE' @@ -121,3 +121,16 @@ export const mockData: ApplicationFormData = { }, ], }; + +export const mockOptions: Record = { + /*보블리스*/ + '67e54ae51cfd27718dd40bea': [ + { id: 101, name: '개발자로 지원하기' }, + { id: 102, name: '디자이너로 지원하기', url: 'https://docs.google.com/forms/d/e/1FAIpQLSeL0p3y7Z2YFymaPThXv5dYBU-wVIKqr3TrQlwHLyJneE6CcA/viewform' }, + ], + /*IVF*/ + '67ee2ca3b35e3c267e3c248d': [ + { id: 201, name: '선수로 지원하기' }, + { id: 202, name: '매니저로 지원하기' }, + ], +}; \ No newline at end of file From 7dcb97b516d81b3c18057120e52a608940288d65 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 12 Oct 2025 04:16:35 +0900 Subject: [PATCH 03/42] =?UTF-8?q?feat:=20=EB=8B=A4=EC=A4=91=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=EC=84=9C=20=EC=98=B5=EC=85=98=20=ED=95=B8=EB=93=A4?= =?UTF-8?q?=EB=9F=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/mocks/api/apply.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/frontend/src/mocks/api/apply.ts b/frontend/src/mocks/api/apply.ts index 56f6a4eee..63194476c 100644 --- a/frontend/src/mocks/api/apply.ts +++ b/frontend/src/mocks/api/apply.ts @@ -1,5 +1,5 @@ import { http, HttpResponse } from 'msw'; -import { mockData } from '../data/mockData'; +import { mockData, mockOptions } from '../data/mockData'; import { API_BASE } from '../constants/clubApi'; import { validateClubId } from '../utils/validateClubId'; import { ERROR_MESSAGE } from '../constants/error'; @@ -69,4 +69,16 @@ export const applyHandlers = [ { status: 200 }, ); }), + + http.get(`${API_BASE}/:clubId/applications`, ({ params }) => { + const clubId = String(params.clubId); + if (!validateClubId(clubId)) { + return HttpResponse.json( + { message: ERROR_MESSAGE.INVALID_CLUB_ID }, + { status: 400 }, + ); + } + const list = mockOptions[clubId] ?? []; + return HttpResponse.json({data: list}, {status: 200}); + }), ]; From fef1c3e4d2c1352f8ab6bec68f638e09b82dc7fb Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 12 Oct 2025 04:31:52 +0900 Subject: [PATCH 04/42] =?UTF-8?q?feat:=20=EB=8B=A4=EC=A4=91=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=EC=84=9C=20=EC=98=B5=EC=85=98=20=EB=AA=A9=EB=A1=9D=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apis/application/getApplicationOptions.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 frontend/src/apis/application/getApplicationOptions.ts diff --git a/frontend/src/apis/application/getApplicationOptions.ts b/frontend/src/apis/application/getApplicationOptions.ts new file mode 100644 index 000000000..af949c4b2 --- /dev/null +++ b/frontend/src/apis/application/getApplicationOptions.ts @@ -0,0 +1,18 @@ +import API_BASE_URL from "@/constants/api"; + +const getApplicationOptions = async (clubId: string) => { + try { + const response = await fetch(`${API_BASE_URL}/api/club/${clubId}/applications`); + if (!response.ok) { + console.error(`Failed to fetch options: ${response.statusText}`); + throw new Error((await response.json()).message); + } + const result = await response.json(); + return result.data; + } catch (error) { + console.error('지원서 옵션 조회 중 오류가 발생했습니다.', error); + throw error; + } +}; + +export default getApplicationOptions; \ No newline at end of file From d762de67233b5198958b3f25214d13beff351dc9 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 12 Oct 2025 07:56:44 +0900 Subject: [PATCH 05/42] =?UTF-8?q?fix:=20mock=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=20=ED=82=A4=20=EB=B0=8F=20=EC=9D=91=EB=8B=B5=20=EC=8A=A4?= =?UTF-8?q?=ED=82=A4=EB=A7=88=20=ED=98=95=EC=8B=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/mocks/api/apply.ts | 11 ++++++++--- frontend/src/mocks/data/mockData.ts | 5 +++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/frontend/src/mocks/api/apply.ts b/frontend/src/mocks/api/apply.ts index 63194476c..7e080e685 100644 --- a/frontend/src/mocks/api/apply.ts +++ b/frontend/src/mocks/api/apply.ts @@ -24,9 +24,14 @@ export const applyHandlers = [ return HttpResponse.json( { - clubId, - form_title: mockData.title, - questions: mockData.questions, + status: '200', + message: 'OK', + data: { + clubId, + title: mockData.title, + description: mockData.description, + questions: mockData.questions, + }, }, { status: 200 }, ); diff --git a/frontend/src/mocks/data/mockData.ts b/frontend/src/mocks/data/mockData.ts index 1f076e5ff..4823d02c7 100644 --- a/frontend/src/mocks/data/mockData.ts +++ b/frontend/src/mocks/data/mockData.ts @@ -1,4 +1,5 @@ import { ApplicationFormData, ApplicationOption } from '@/types/application'; +import { CLUB_BOB, CLUB_IVF } from '../constants/clubApi'; type QuestionType = | 'CHOICE' @@ -124,12 +125,12 @@ export const mockData: ApplicationFormData = { export const mockOptions: Record = { /*보블리스*/ - '67e54ae51cfd27718dd40bea': [ + [CLUB_BOB]: [ { id: 101, name: '개발자로 지원하기' }, { id: 102, name: '디자이너로 지원하기', url: 'https://docs.google.com/forms/d/e/1FAIpQLSeL0p3y7Z2YFymaPThXv5dYBU-wVIKqr3TrQlwHLyJneE6CcA/viewform' }, ], /*IVF*/ - '67ee2ca3b35e3c267e3c248d': [ + [CLUB_IVF]: [ { id: 201, name: '선수로 지원하기' }, { id: 202, name: '매니저로 지원하기' }, ], From 8bba6d7309471029705c598899a784daa88dbf57 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 12 Oct 2025 07:57:55 +0900 Subject: [PATCH 06/42] =?UTF-8?q?refacter:=20mock=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EC=83=81=EC=88=98=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/mocks/constants/clubApi.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/src/mocks/constants/clubApi.ts b/frontend/src/mocks/constants/clubApi.ts index 99a936f02..e8d5c689d 100644 --- a/frontend/src/mocks/constants/clubApi.ts +++ b/frontend/src/mocks/constants/clubApi.ts @@ -1,3 +1,6 @@ export const API_BASE = 'http://localhost/api/club'; export const CLUB_ID = '67e54ae51cfd27718dd40be6'; + +export const CLUB_IVF = '67ee2ca3b35e3c267e3c248d'; +export const CLUB_BOB = '67e54ae51cfd27718dd40bea'; \ No newline at end of file From 87d175727c9499bf0fa5a2d9d9d24b57716d1a05 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 12 Oct 2025 12:45:47 +0900 Subject: [PATCH 07/42] =?UTF-8?q?feat:=20=EB=AA=A8=EB=8B=AC=20=EA=B3=B5?= =?UTF-8?q?=ED=86=B5=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/common/Modal/Modal.tsx | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 frontend/src/components/common/Modal/Modal.tsx diff --git a/frontend/src/components/common/Modal/Modal.tsx b/frontend/src/components/common/Modal/Modal.tsx new file mode 100644 index 000000000..44a4ae312 --- /dev/null +++ b/frontend/src/components/common/Modal/Modal.tsx @@ -0,0 +1,29 @@ +import { ReactNode} from "react"; +import * as Styled from './Modal.styles'; + +export interface ModalProps { + isOpen: boolean; + onClose: () => void; + title: string; + description?: string; + children?: ReactNode; +} + +const Modal = ({ isOpen, onClose, title, description, children }: ModalProps) => { + if (!isOpen) return null; + + return ( + + event.stopPropagation()}> + + {title && {title}} + + + {description && {description}} + {children} + + + ); +} + +export default Modal; \ No newline at end of file From 4440bb8547415e7514af8ab7b494e459f3ff2c15 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 12 Oct 2025 13:04:36 +0900 Subject: [PATCH 08/42] =?UTF-8?q?chore:=20react-remove-scroll=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/package-lock.json | 128 +++++++++++++++++++++++++++++++++++++ frontend/package.json | 1 + 2 files changed, 129 insertions(+) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 73176c5f2..422d755ae 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -23,6 +23,7 @@ "react-datepicker": "^8.1.0", "react-dom": "^19.0.0", "react-markdown": "^9.0.3", + "react-remove-scroll": "^2.7.1", "react-router-dom": "^7.1.1", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", @@ -7872,6 +7873,12 @@ "dev": true, "license": "MIT" }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, "node_modules/detect-port": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-2.1.0.tgz", @@ -10098,6 +10105,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -16342,6 +16358,53 @@ "node": ">=0.10.0" } }, + "node_modules/react-remove-scroll": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", + "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-router": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.1.tgz", @@ -16382,6 +16445,28 @@ "react-dom": ">=18" } }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -18899,6 +18984,49 @@ "dev": true, "license": "MIT" }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", diff --git a/frontend/package.json b/frontend/package.json index 919f90011..f77cf6748 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,6 +34,7 @@ "react-datepicker": "^8.1.0", "react-dom": "^19.0.0", "react-markdown": "^9.0.3", + "react-remove-scroll": "^2.7.1", "react-router-dom": "^7.1.1", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", From 01d9bc3aee00eb459a5b80e3f1d598980b8dedf1 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 12 Oct 2025 13:09:02 +0900 Subject: [PATCH 09/42] =?UTF-8?q?feat:=20RemoveScroll=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/common/Modal/Modal.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/common/Modal/Modal.tsx b/frontend/src/components/common/Modal/Modal.tsx index 44a4ae312..35ee2a591 100644 --- a/frontend/src/components/common/Modal/Modal.tsx +++ b/frontend/src/components/common/Modal/Modal.tsx @@ -1,5 +1,6 @@ -import { ReactNode} from "react"; +import { MouseEvent, ReactNode } from "react"; import * as Styled from './Modal.styles'; +import { RemoveScroll } from "react-remove-scroll"; export interface ModalProps { isOpen: boolean; @@ -10,11 +11,12 @@ export interface ModalProps { } const Modal = ({ isOpen, onClose, title, description, children }: ModalProps) => { - if (!isOpen) return null; + if (!isOpen) return null; - return ( + return ( - event.stopPropagation()}> + + ) => e.stopPropagation()}> {title && {title}} @@ -22,6 +24,7 @@ const Modal = ({ isOpen, onClose, title, description, children }: ModalProps) => {description && {description}} {children} + ); } From 62aeef40214551ace7d0ac1ee6af43bd61ade844 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 12 Oct 2025 13:11:54 +0900 Subject: [PATCH 10/42] =?UTF-8?q?style:=20=EA=B3=B5=ED=86=B5=20=EB=AA=A8?= =?UTF-8?q?=EB=8B=AC=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/common/Modal/Modal.styles.ts | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 frontend/src/components/common/Modal/Modal.styles.ts diff --git a/frontend/src/components/common/Modal/Modal.styles.ts b/frontend/src/components/common/Modal/Modal.styles.ts new file mode 100644 index 000000000..cfb82f4a8 --- /dev/null +++ b/frontend/src/components/common/Modal/Modal.styles.ts @@ -0,0 +1,58 @@ +import styled from 'styled-components'; + +export const Overlay = styled.div<{ isOpen: boolean }>` + position: fixed; + inset: 0; + z-index: 1000; + background: rgba(0,0,0, ${({ isOpen }) => (isOpen ? 0.45 : 0)}); + display: grid; + place-items: center; + padding: 24px; + transition: background-color .2s ease; +`; + +export const Container = styled.div<{ isOpen: boolean }>` + min-width: 500px; + max-height: 90vh; + background: #fff; + border-radius: 10px; + overflow: hidden; + box-shadow: ${({ isOpen }) => (isOpen ? '0 18px 44px rgba(0,0,0,.22)' : 'none')}; + transition: transform .2s ease, box-shadow .2s ease; +`; + +export const Header = styled.div` + padding: 30px; + border-bottom: 1px solid #DCDCDC; + display: flex; + align-items: center; +`; + +export const Title = styled.h3` + font-size: 20px; + font-weight: 800; + flex: 1; + text-align: left; +`; + +export const IconButton = styled.button` + border: none; + background: transparent; + font-size: 20px; + font-weight: 800; + color: #9D9D9D; + line-height: 1; + cursor: pointer; +`; + +export const Description = styled.p` + padding: 20px 32px 0px; + text-align: left; + color: #9D9D9D; + font-weight: 600; +`; + +export const Body = styled.div` + padding: 16px 30px 30px; + overflow: auto; +`; From 53ff2b307164be14391a3a7c7720fbcff495993a Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 12 Oct 2025 15:18:43 +0900 Subject: [PATCH 11/42] =?UTF-8?q?feat:=20=EC=A7=80=EC=9B=90=EC=84=9C=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=EB=AA=A8=EB=8B=AC=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modals/ApplicationSelectModal.tsx | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 frontend/src/components/application/modals/ApplicationSelectModal.tsx diff --git a/frontend/src/components/application/modals/ApplicationSelectModal.tsx b/frontend/src/components/application/modals/ApplicationSelectModal.tsx new file mode 100644 index 000000000..17b1b35eb --- /dev/null +++ b/frontend/src/components/application/modals/ApplicationSelectModal.tsx @@ -0,0 +1,36 @@ +import Modal from "@/components/common/Modal/Modal"; +import * as Styled from './ApplicationSelectModal.styles'; +import { ApplicationOption } from "@/types/application"; +import { useNavigate, useParams } from "react-router-dom"; + +export interface ApplicationSelectModalProps { + isOpen: boolean; + onClose: () => void; + options: ApplicationOption[]; + onSelect: (option: ApplicationOption) => void; +} + +const ApplicationSelectModal = ({ isOpen, onClose, options, onSelect }: ApplicationSelectModalProps) => { + return ( + + {options.length === 0 ? ( + 지원 가능한 분야가 없습니다. + ) : ( + + {options.map((option) => ( + onSelect(option)}> + {option.name} + + ))} + + )} + + ); +}; + +export default ApplicationSelectModal; From 40a5556590f06777355475c6245ef7db06a9d136 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 12 Oct 2025 16:19:44 +0900 Subject: [PATCH 12/42] =?UTF-8?q?style:=20=EC=A7=80=EC=9B=90=EC=84=9C=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=EB=AA=A8=EB=8B=AC=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modals/ApplicationSelectModal.styles.ts | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 frontend/src/components/application/modals/ApplicationSelectModal.styles.ts diff --git a/frontend/src/components/application/modals/ApplicationSelectModal.styles.ts b/frontend/src/components/application/modals/ApplicationSelectModal.styles.ts new file mode 100644 index 000000000..59d8fc345 --- /dev/null +++ b/frontend/src/components/application/modals/ApplicationSelectModal.styles.ts @@ -0,0 +1,31 @@ +import styled from 'styled-components'; + +export const EmptyMessage = styled.div` + padding: 16px 8px; + color: #9D9D9D; + text-align: center; + font-weight: 600; +`; + +export const List = styled.div` + display: grid; + gap: 16px; +`; + +export const OptionButton = styled.button` + width: 100%; + padding: 18px 20px; + border-radius: 10px; + border: 1px solid #DCDCDC; + background: #fff; + font-weight: 600; + font-size: 16px; + cursor: pointer; + transition: background-color .15s ease, color .15s ease, border-color .15s ease; + + &:hover { + background: #ff7a00; + color: #fff; + border-color: #ff7a00; + } +`; From 402e906cf292e0f442fa69571a00ec222744d2b9 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 12 Oct 2025 16:20:32 +0900 Subject: [PATCH 13/42] =?UTF-8?q?chore:=20=EB=AA=A9=20=EC=84=9C=EB=B2=84?= =?UTF-8?q?=20=EC=A3=BC=EC=86=8C=EB=A1=9C=20=EC=9E=84=EC=8B=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/apis/application/getApplication.ts | 3 ++- frontend/src/apis/application/getApplicationOptions.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/apis/application/getApplication.ts b/frontend/src/apis/application/getApplication.ts index 6598ce899..9410c9637 100644 --- a/frontend/src/apis/application/getApplication.ts +++ b/frontend/src/apis/application/getApplication.ts @@ -1,8 +1,9 @@ import API_BASE_URL from '@/constants/api'; +import { API_BASE } from '@/mocks/constants/clubApi'; const getApplication = async (clubId: string) => { try { - const response = await fetch(`${API_BASE_URL}/api/club/${clubId}/apply`); + const response = await fetch(`${API_BASE}/${clubId}/apply`); if (!response.ok) { console.error(`Failed to fetch: ${response.statusText}`); throw new Error((await response.json()).message); diff --git a/frontend/src/apis/application/getApplicationOptions.ts b/frontend/src/apis/application/getApplicationOptions.ts index af949c4b2..72d5520d3 100644 --- a/frontend/src/apis/application/getApplicationOptions.ts +++ b/frontend/src/apis/application/getApplicationOptions.ts @@ -1,8 +1,9 @@ import API_BASE_URL from "@/constants/api"; +import { API_BASE } from "@/mocks/constants/clubApi"; const getApplicationOptions = async (clubId: string) => { try { - const response = await fetch(`${API_BASE_URL}/api/club/${clubId}/applications`); + const response = await fetch(`${API_BASE}/${clubId}/applications`); if (!response.ok) { console.error(`Failed to fetch options: ${response.statusText}`); throw new Error((await response.json()).message); From 97cd642ceab460fd10c8cb5b79f5ce8c4da33d63 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 12 Oct 2025 16:20:48 +0900 Subject: [PATCH 14/42] =?UTF-8?q?feat:=20=EB=AA=A9=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/mocks/constants/clubApi.ts | 4 +++- frontend/src/mocks/data/mockData.ts | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/frontend/src/mocks/constants/clubApi.ts b/frontend/src/mocks/constants/clubApi.ts index e8d5c689d..9c6d54d6f 100644 --- a/frontend/src/mocks/constants/clubApi.ts +++ b/frontend/src/mocks/constants/clubApi.ts @@ -3,4 +3,6 @@ export const API_BASE = 'http://localhost/api/club'; export const CLUB_ID = '67e54ae51cfd27718dd40be6'; export const CLUB_IVF = '67ee2ca3b35e3c267e3c248d'; -export const CLUB_BOB = '67e54ae51cfd27718dd40bea'; \ No newline at end of file +export const CLUB_BOB = '67e54ae51cfd27718dd40bea'; +export const CLUB_BACK = '67e54ae51cfd27718dd40bd8'; +export const CLUB_TEST = '67ebf9f75c8623081055881c'; \ No newline at end of file diff --git a/frontend/src/mocks/data/mockData.ts b/frontend/src/mocks/data/mockData.ts index 4823d02c7..ec2032150 100644 --- a/frontend/src/mocks/data/mockData.ts +++ b/frontend/src/mocks/data/mockData.ts @@ -1,5 +1,5 @@ import { ApplicationFormData, ApplicationOption } from '@/types/application'; -import { CLUB_BOB, CLUB_IVF } from '../constants/clubApi'; +import { CLUB_BACK, CLUB_BOB, CLUB_IVF, CLUB_TEST } from '../constants/clubApi'; type QuestionType = | 'CHOICE' @@ -128,10 +128,17 @@ export const mockOptions: Record = { [CLUB_BOB]: [ { id: 101, name: '개발자로 지원하기' }, { id: 102, name: '디자이너로 지원하기', url: 'https://docs.google.com/forms/d/e/1FAIpQLSeL0p3y7Z2YFymaPThXv5dYBU-wVIKqr3TrQlwHLyJneE6CcA/viewform' }, + { id: 103, name: '기획자로 지원하기' }, ], /*IVF*/ [CLUB_IVF]: [ { id: 201, name: '선수로 지원하기' }, { id: 202, name: '매니저로 지원하기' }, ], + /*백경예술연구회*/ + [CLUB_BACK]: [ + { id: 301, name: '백경예술연구회 지원하기' }, + ], + /*테스트*/ + [CLUB_TEST]: [], }; \ No newline at end of file From df2de81f1de272abf3256ea76ba46b718f6635ee Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 12 Oct 2025 16:34:37 +0900 Subject: [PATCH 15/42] =?UTF-8?q?feat:=20=EB=8F=99=EC=95=84=EB=A6=AC=20?= =?UTF-8?q?=EC=A7=80=EC=9B=90=ED=95=98=EA=B8=B0=20=EB=B2=84=ED=8A=BC?= =?UTF-8?q?=EC=97=90=20=EC=A7=80=EC=9B=90=EC=84=9C=20=EA=B0=9C=EC=88=98?= =?UTF-8?q?=EB=B3=84=20=EB=8F=99=EC=9E=91=20=EB=B6=84=EA=B8=B0=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ClubApplyButton/ClubApplyButton.tsx | 65 ++++++++++++++++--- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx b/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx index 4916b7024..b4318271a 100644 --- a/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx +++ b/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx @@ -5,6 +5,10 @@ import getApplication from '@/apis/application/getApplication'; import useMixpanelTrack from '@/hooks/useMixpanelTrack'; import { EVENT_NAME } from '@/constants/eventName'; import ShareButton from '@/pages/ClubDetailPage/components/ShareButton/ShareButton'; +import { useState } from 'react'; +import { ApplicationOption } from '@/types/application'; +import getApplicationOptions from '@/apis/application/getApplicationOptions'; +import ApplicationSelectModal from '@/components/application/modals/ApplicationSelectModal'; interface ClubApplyButtonProps { deadlineText?: string; @@ -19,10 +23,25 @@ const ClubApplyButton = ({ deadlineText }: ClubApplyButtonProps) => { const { clubId } = useParams<{ clubId: string }>(); const navigate = useNavigate(); const trackEvent = useMixpanelTrack(); - const { data: clubDetail } = useGetClubDetail(clubId!); - if (!clubId || !clubDetail) return; + // 모달 옵션 상태 + const [isOpen, setIsOpen] = useState(false); + const [options, setOptions] = useState([]) + + const openByOption = (option?: ApplicationOption) => { + if (!clubId) return; + if (option?.url) { + // 외부 폼 + window.open(option.url, '_blank'); + } else { + // 내부 폼 + navigate(`/application/${clubId}`); + } + setIsOpen(false); + }; + + if (!clubId || !clubDetail) return null; const handleClick = async () => { trackEvent(EVENT_NAME.CLUB_APPLY_BUTTON_CLICKED); @@ -33,18 +52,38 @@ const ClubApplyButton = ({ deadlineText }: ClubApplyButtonProps) => { } try { - await getApplication(clubId); - navigate(`/application/${clubId}`); - } catch (err: unknown) { - const externalFormLink = clubDetail.externalApplicationUrl?.trim(); + const list = await getApplicationOptions(clubId); + + if (list.length === 1) { + openByOption(list[0]); + return; + } - if (!externalFormLink) { - alert('동아리 모집 정보를 확인해주세요.'); + if (list.length >= 2) { + setOptions(list); + setIsOpen(true); return; } - window.open(externalFormLink, '_blank', 'noopener,noreferrer'); + setOptions([]); + setIsOpen(true); + return; + + } catch { + try { + await getApplication(clubId); + navigate(`/application/${clubId}`); + setIsOpen(false); + } catch { + const externalForm = clubDetail.externalApplicationUrl?.trim(); + if (externalForm) { + window.open(externalForm, '_blank'); + setIsOpen(false); + } else { + setOptions([]); + } + } } - }; + }; const renderButtonContent = () => { if (deadlineText === RECRUITMENT_STATUS.CLOSED) { @@ -70,6 +109,12 @@ const ClubApplyButton = ({ deadlineText }: ClubApplyButtonProps) => { {renderButtonContent()} + setIsOpen(false)} + options={options} + onSelect={openByOption} + /> ); }; From a5f1853fc8e78d13ba7a7e476d62c51547dd493f Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 12 Oct 2025 17:18:52 +0900 Subject: [PATCH 16/42] =?UTF-8?q?chore:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20import=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/application/modals/ApplicationSelectModal.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/components/application/modals/ApplicationSelectModal.tsx b/frontend/src/components/application/modals/ApplicationSelectModal.tsx index 17b1b35eb..328b670c5 100644 --- a/frontend/src/components/application/modals/ApplicationSelectModal.tsx +++ b/frontend/src/components/application/modals/ApplicationSelectModal.tsx @@ -1,7 +1,6 @@ import Modal from "@/components/common/Modal/Modal"; import * as Styled from './ApplicationSelectModal.styles'; import { ApplicationOption } from "@/types/application"; -import { useNavigate, useParams } from "react-router-dom"; export interface ApplicationSelectModalProps { isOpen: boolean; From e52fee6e2003bad454bf06775af57c3da98881ae Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 2 Nov 2025 14:29:57 +0900 Subject: [PATCH 17/42] =?UTF-8?q?fix:=20=EC=84=9C=EB=B2=84=20=EC=A3=BC?= =?UTF-8?q?=EC=86=8C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/apis/application/getApplication.ts | 3 +-- frontend/src/apis/application/getApplicationOptions.ts | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/frontend/src/apis/application/getApplication.ts b/frontend/src/apis/application/getApplication.ts index 9410c9637..6598ce899 100644 --- a/frontend/src/apis/application/getApplication.ts +++ b/frontend/src/apis/application/getApplication.ts @@ -1,9 +1,8 @@ import API_BASE_URL from '@/constants/api'; -import { API_BASE } from '@/mocks/constants/clubApi'; const getApplication = async (clubId: string) => { try { - const response = await fetch(`${API_BASE}/${clubId}/apply`); + const response = await fetch(`${API_BASE_URL}/api/club/${clubId}/apply`); if (!response.ok) { console.error(`Failed to fetch: ${response.statusText}`); throw new Error((await response.json()).message); diff --git a/frontend/src/apis/application/getApplicationOptions.ts b/frontend/src/apis/application/getApplicationOptions.ts index 72d5520d3..af949c4b2 100644 --- a/frontend/src/apis/application/getApplicationOptions.ts +++ b/frontend/src/apis/application/getApplicationOptions.ts @@ -1,9 +1,8 @@ import API_BASE_URL from "@/constants/api"; -import { API_BASE } from "@/mocks/constants/clubApi"; const getApplicationOptions = async (clubId: string) => { try { - const response = await fetch(`${API_BASE}/${clubId}/applications`); + const response = await fetch(`${API_BASE_URL}/api/club/${clubId}/applications`); if (!response.ok) { console.error(`Failed to fetch options: ${response.statusText}`); throw new Error((await response.json()).message); From 73f4db115fd0b5ced5580ed90002b3801d85eeb8 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sat, 8 Nov 2025 03:57:51 +0900 Subject: [PATCH 18/42] =?UTF-8?q?feat:=20=ED=83=80=EC=9E=85=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/types/application.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend/src/types/application.ts b/frontend/src/types/application.ts index 267ebb78f..d4e30bdbe 100644 --- a/frontend/src/types/application.ts +++ b/frontend/src/types/application.ts @@ -57,8 +57,7 @@ export interface AnswerItem { value: string; } -export interface ApplicationOption { - id: number; - name: string; - url?: string; // 존재하면 외부 링크, 없으면 내부 +export interface ApplicationForm { + id: string; + title: string; } \ No newline at end of file From ab9d09b91a6d2a5e74fa99eaab8d9a36e4a8d100 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sat, 8 Nov 2025 03:59:15 +0900 Subject: [PATCH 19/42] =?UTF-8?q?feat:=20=EC=A3=BC=EC=86=8C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=B0=8F=20=EB=B0=98=ED=99=98=20=ED=98=95=ED=83=9C?= =?UTF-8?q?=20=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apis/application/getApplicationForms.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 frontend/src/apis/application/getApplicationForms.ts diff --git a/frontend/src/apis/application/getApplicationForms.ts b/frontend/src/apis/application/getApplicationForms.ts new file mode 100644 index 000000000..9a0a336f5 --- /dev/null +++ b/frontend/src/apis/application/getApplicationForms.ts @@ -0,0 +1,22 @@ +import API_BASE_URL from "@/constants/api"; + +const getApplicationForms = async (clubId: string) => { + try { + const response = await fetch(`${API_BASE_URL}/api/club/${clubId}/apply`); + if (!response.ok) { + console.error(`Failed to fetch options: ${response.statusText}`); + throw new Error((await response.json()).message); + } + const result = await response.json(); + const forms = Array.isArray(result?.data?.forms) ? result.data.forms : []; + return forms.map((form: { id: string; title: string }) => ({ + id: form.id, + title: form.title, + })); + } catch (error) { + console.error('지원서 옵션 조회 중 오류가 발생했습니다.', error); + throw error; + } +}; + +export default getApplicationForms; \ No newline at end of file From 92221f4b8eced4059dfebc5aa3cdd4019c55846b Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sat, 8 Nov 2025 03:59:38 +0900 Subject: [PATCH 20/42] =?UTF-8?q?feat:=20=EC=A3=BC=EC=86=8C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=B0=8F=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/apis/application/getApplication.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/apis/application/getApplication.ts b/frontend/src/apis/application/getApplication.ts index 6598ce899..ba8186faa 100644 --- a/frontend/src/apis/application/getApplication.ts +++ b/frontend/src/apis/application/getApplication.ts @@ -1,8 +1,8 @@ import API_BASE_URL from '@/constants/api'; -const getApplication = async (clubId: string) => { +const getApplication = async (clubId: string, applicationFormId: string) => { try { - const response = await fetch(`${API_BASE_URL}/api/club/${clubId}/apply`); + const response = await fetch(`${API_BASE_URL}/api/club/${clubId}/apply/${applicationFormId}`); if (!response.ok) { console.error(`Failed to fetch: ${response.statusText}`); throw new Error((await response.json()).message); From f390eb0f52eab47bf69ae00eadc6ecbb8bfb289e Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sat, 8 Nov 2025 04:00:52 +0900 Subject: [PATCH 21/42] =?UTF-8?q?feat:=20=EB=8B=A4=EC=A4=91=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=EC=84=9C=20api=20=ED=8C=8C=EC=9D=BC=EB=AA=85=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apis/application/getApplicationOptions.ts | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 frontend/src/apis/application/getApplicationOptions.ts diff --git a/frontend/src/apis/application/getApplicationOptions.ts b/frontend/src/apis/application/getApplicationOptions.ts deleted file mode 100644 index af949c4b2..000000000 --- a/frontend/src/apis/application/getApplicationOptions.ts +++ /dev/null @@ -1,18 +0,0 @@ -import API_BASE_URL from "@/constants/api"; - -const getApplicationOptions = async (clubId: string) => { - try { - const response = await fetch(`${API_BASE_URL}/api/club/${clubId}/applications`); - if (!response.ok) { - console.error(`Failed to fetch options: ${response.statusText}`); - throw new Error((await response.json()).message); - } - const result = await response.json(); - return result.data; - } catch (error) { - console.error('지원서 옵션 조회 중 오류가 발생했습니다.', error); - throw error; - } -}; - -export default getApplicationOptions; \ No newline at end of file From 66eea33fde710a515e4ddff5a7d9c22268ac5177 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sat, 8 Nov 2025 04:17:29 +0900 Subject: [PATCH 22/42] =?UTF-8?q?feat:=20=EC=98=88=EC=99=B8=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EB=B3=B4=EA=B0=95=20=EB=B0=8F=20=EC=82=BC=ED=95=AD?= =?UTF-8?q?=20=EC=97=B0=EC=82=B0=EC=9E=90=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/apis/application/getApplicationForms.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/frontend/src/apis/application/getApplicationForms.ts b/frontend/src/apis/application/getApplicationForms.ts index 9a0a336f5..4a922c83f 100644 --- a/frontend/src/apis/application/getApplicationForms.ts +++ b/frontend/src/apis/application/getApplicationForms.ts @@ -4,11 +4,20 @@ const getApplicationForms = async (clubId: string) => { try { const response = await fetch(`${API_BASE_URL}/api/club/${clubId}/apply`); if (!response.ok) { - console.error(`Failed to fetch options: ${response.statusText}`); - throw new Error((await response.json()).message); + let message = response.statusText; + try { + const errorData = await response.json(); + if (errorData?.message) message = errorData.message; + } catch {} + console.error(`Failed to fetch options: ${message}`); + throw new Error(message); } + const result = await response.json(); - const forms = Array.isArray(result?.data?.forms) ? result.data.forms : []; + let forms: Array<{ id: string; title: string }> = []; + if (result && result.data && Array.isArray(result.data.forms)) { + forms = result.data.forms; + } return forms.map((form: { id: string; title: string }) => ({ id: form.id, title: form.title, From c3c6d4aacdb6061187cbbd1bbca828963df7066a Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sat, 8 Nov 2025 04:20:42 +0900 Subject: [PATCH 23/42] =?UTF-8?q?feat:=20=EC=98=88=EC=99=B8=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/apis/application/getApplication.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frontend/src/apis/application/getApplication.ts b/frontend/src/apis/application/getApplication.ts index ba8186faa..ffda83da9 100644 --- a/frontend/src/apis/application/getApplication.ts +++ b/frontend/src/apis/application/getApplication.ts @@ -4,8 +4,13 @@ const getApplication = async (clubId: string, applicationFormId: string) => { try { const response = await fetch(`${API_BASE_URL}/api/club/${clubId}/apply/${applicationFormId}`); if (!response.ok) { - console.error(`Failed to fetch: ${response.statusText}`); - throw new Error((await response.json()).message); + let message = response.statusText; + try { + const errorData = await response.json(); + if (errorData?.message) message = errorData.message; + } catch {} + console.error(`Failed to fetch: ${message}`); + throw new Error(message); } const result = await response.json(); From 919bf616e52ed2a17ceb455c008db71498921056 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sat, 8 Nov 2025 13:52:18 +0900 Subject: [PATCH 24/42] =?UTF-8?q?chore:=20=ED=8C=8C=EC=9D=BC=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{getApplicationForms.ts => getApplicationOptions.ts} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename frontend/src/apis/application/{getApplicationForms.ts => getApplicationOptions.ts} (91%) diff --git a/frontend/src/apis/application/getApplicationForms.ts b/frontend/src/apis/application/getApplicationOptions.ts similarity index 91% rename from frontend/src/apis/application/getApplicationForms.ts rename to frontend/src/apis/application/getApplicationOptions.ts index 4a922c83f..9c602ea2c 100644 --- a/frontend/src/apis/application/getApplicationForms.ts +++ b/frontend/src/apis/application/getApplicationOptions.ts @@ -1,6 +1,6 @@ import API_BASE_URL from "@/constants/api"; -const getApplicationForms = async (clubId: string) => { +const getApplicationOptions = async (clubId: string) => { try { const response = await fetch(`${API_BASE_URL}/api/club/${clubId}/apply`); if (!response.ok) { @@ -28,4 +28,4 @@ const getApplicationForms = async (clubId: string) => { } }; -export default getApplicationForms; \ No newline at end of file +export default getApplicationOptions; \ No newline at end of file From b3d860ec8aa56730c827f925dc28ac1b65a4e7f0 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sat, 8 Nov 2025 14:34:24 +0900 Subject: [PATCH 25/42] =?UTF-8?q?feat:=20=ED=8C=8C=EB=9D=BC=EB=AF=B8?= =?UTF-8?q?=ED=84=B0=20=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/modals/ApplicationSelectModal.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/application/modals/ApplicationSelectModal.tsx b/frontend/src/components/application/modals/ApplicationSelectModal.tsx index 328b670c5..4c54e859c 100644 --- a/frontend/src/components/application/modals/ApplicationSelectModal.tsx +++ b/frontend/src/components/application/modals/ApplicationSelectModal.tsx @@ -1,12 +1,12 @@ import Modal from "@/components/common/Modal/Modal"; import * as Styled from './ApplicationSelectModal.styles'; -import { ApplicationOption } from "@/types/application"; +import { ApplicationForm } from "@/types/application"; export interface ApplicationSelectModalProps { isOpen: boolean; onClose: () => void; - options: ApplicationOption[]; - onSelect: (option: ApplicationOption) => void; + options: ApplicationForm[]; + onSelect: (option: ApplicationForm) => void; } const ApplicationSelectModal = ({ isOpen, onClose, options, onSelect }: ApplicationSelectModalProps) => { @@ -23,7 +23,7 @@ const ApplicationSelectModal = ({ isOpen, onClose, options, onSelect }: Applicat {options.map((option) => ( onSelect(option)}> - {option.name} + {option.title} ))} From 7fd2b26791c81cc33133dbc06b1cde7710801e2c Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sat, 8 Nov 2025 14:34:52 +0900 Subject: [PATCH 26/42] =?UTF-8?q?feat:=20=EB=8B=A4=EC=A4=91=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=EC=84=9C=20=EB=B6=84=EA=B8=B0=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ClubApplyButton/ClubApplyButton.tsx | 62 ++++++++----------- 1 file changed, 25 insertions(+), 37 deletions(-) diff --git a/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx b/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx index b4318271a..2e24b6747 100644 --- a/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx +++ b/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx @@ -6,9 +6,10 @@ import useMixpanelTrack from '@/hooks/useMixpanelTrack'; import { EVENT_NAME } from '@/constants/eventName'; import ShareButton from '@/pages/ClubDetailPage/components/ShareButton/ShareButton'; import { useState } from 'react'; -import { ApplicationOption } from '@/types/application'; +import { ApplicationForm } from '@/types/application'; import getApplicationOptions from '@/apis/application/getApplicationOptions'; import ApplicationSelectModal from '@/components/application/modals/ApplicationSelectModal'; +import { set } from 'date-fns'; interface ClubApplyButtonProps { deadlineText?: string; @@ -27,21 +28,25 @@ const ClubApplyButton = ({ deadlineText }: ClubApplyButtonProps) => { // 모달 옵션 상태 const [isOpen, setIsOpen] = useState(false); - const [options, setOptions] = useState([]) + const [options, setOptions] = useState([]); - const openByOption = (option?: ApplicationOption) => { - if (!clubId) return; - if (option?.url) { - // 외부 폼 - window.open(option.url, '_blank'); - } else { - // 내부 폼 - navigate(`/application/${clubId}`); + if (!clubId || !clubDetail) return null; + + const goWithForm = async (formId: string) => { + try { + const formDetail = await getApplication(clubId, formId); + navigate(`/application/${clubId}/${formId}`, { state: { formDetail } }); + setIsOpen(false); + } catch (error) { + console.error('지원서 조회 중 오류가 발생했습니다', error); + alert('지원서 정보를 불러오는 중 오류가 발생했습니다. 다시 시도해주세요.'); } - setIsOpen(false); }; - if (!clubId || !clubDetail) return null; + const openByOption = (option?: ApplicationForm) => { + if (!option) return; + void goWithForm(option.id); + }; const handleClick = async () => { trackEvent(EVENT_NAME.CLUB_APPLY_BUTTON_CLICKED); @@ -54,34 +59,17 @@ const ClubApplyButton = ({ deadlineText }: ClubApplyButtonProps) => { try { const list = await getApplicationOptions(clubId); - if (list.length === 1) { - openByOption(list[0]); - return; - } - - if (list.length >= 2) { + if (list.length <= 0) { + alert('현재 지원 가능한 지원서가 없습니다.'); + } else if (list.length === 1) { + await goWithForm(list[0].id); + } else { setOptions(list); setIsOpen(true); - return; - } - setOptions([]); - setIsOpen(true); - return; - - } catch { - try { - await getApplication(clubId); - navigate(`/application/${clubId}`); - setIsOpen(false); - } catch { - const externalForm = clubDetail.externalApplicationUrl?.trim(); - if (externalForm) { - window.open(externalForm, '_blank'); - setIsOpen(false); - } else { - setOptions([]); - } } + } catch (e) { + console.error('지원서 옵션 조회 중 오류가 발생했습니다.', e); + alert('지원서 정보를 불러오는 중 오류가 발생했습니다. 다시 시도해주세요.'); } }; From e7bfaa15d582bdf780bf7064c8c093747cc384a4 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 9 Nov 2025 10:22:13 +0900 Subject: [PATCH 27/42] =?UTF-8?q?feat:=20applicationFormId=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/context/AdminClubContext.tsx | 11 ++++++++++- .../queries/application/useGetApplication.ts | 8 ++++---- .../ApplicantDetailPage/ApplicantDetailPage.tsx | 6 +++--- .../ApplicationEditTab/ApplicationEditTab.tsx | 6 +++--- .../ApplicationFormPage/ApplicationFormPage.tsx | 16 ++++++++-------- 5 files changed, 28 insertions(+), 19 deletions(-) diff --git a/frontend/src/context/AdminClubContext.tsx b/frontend/src/context/AdminClubContext.tsx index aaf87f2cc..3c9c40449 100644 --- a/frontend/src/context/AdminClubContext.tsx +++ b/frontend/src/context/AdminClubContext.tsx @@ -6,6 +6,8 @@ interface AdminClubContextType { setClubId: (id: string | null) => void; applicantsData: ApplicantsInfo | null; setApplicantsData: (data: ApplicantsInfo | null) => void; + applicationFormId: string | null; + setApplicationFormId: (id: string | null) => void; } const AdminClubContext = createContext( @@ -19,9 +21,16 @@ export const AdminClubProvider = ({ }) => { const [clubId, setClubId] = useState(null); const [applicantsData, setApplicantsData] = useState(null); + const [applicationFormId, setApplicationFormId] = useState(null); return ( - + {children} ); diff --git a/frontend/src/hooks/queries/application/useGetApplication.ts b/frontend/src/hooks/queries/application/useGetApplication.ts index c376b9f7f..70f666d64 100644 --- a/frontend/src/hooks/queries/application/useGetApplication.ts +++ b/frontend/src/hooks/queries/application/useGetApplication.ts @@ -1,11 +1,11 @@ import { useQuery } from '@tanstack/react-query'; import getApplication from '@/apis/application/getApplication'; -export const useGetApplication = (clubId: string) => { +export const useGetApplication = (clubId: string, applicationFormId: string) => { return useQuery({ - queryKey: ['applicationForm', clubId], - queryFn: () => getApplication(clubId), + queryKey: ['applicationForm', clubId, applicationFormId], + queryFn: () => getApplication(clubId, applicationFormId), retry: false, - enabled: !!clubId, + enabled: !!clubId || !!applicationFormId, }); }; diff --git a/frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx b/frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx index 405358c5d..aeaf8f846 100644 --- a/frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx +++ b/frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx @@ -38,9 +38,9 @@ const ApplicantDetailPage = () => { const [applicantStatus, setApplicantStatus] = useState( ApplicationStatus.SUBMITTED, ); - const { applicantsData, clubId } = useAdminClubContext(); + const { applicantsData, clubId, applicationFormId } = useAdminClubContext(); - const { data: formData, isLoading, isError } = useGetApplication(clubId!); + const { data: formData, isLoading, isError } = useGetApplication(clubId!, applicationFormId!); const { mutate: updateApplicant } = useUpdateApplicant(clubId!); const applicantIndex = @@ -75,7 +75,7 @@ const ApplicantDetailPage = () => { }, ]); }, 400), - [clubId, questionId], + [clubId, questionId, updateApplicant], ); if (!applicantsData) { diff --git a/frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx b/frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx index bc5476820..0b0ee7a95 100644 --- a/frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx +++ b/frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx @@ -15,10 +15,10 @@ import CustomTextArea from '@/components/common/CustomTextArea/CustomTextArea'; import { APPLICATION_FORM } from '@/constants/APPLICATION_FORM'; const ApplicationEditTab = () => { - const { clubId } = useAdminClubContext(); - if (!clubId) return null; + const { clubId, applicationFormId } = useAdminClubContext(); + if (!clubId || !applicationFormId) return null; - const { data, isLoading, isError } = useGetApplication(clubId); + const { data, isLoading, isError } = useGetApplication(clubId, applicationFormId); const [formData, setFormData] = useState(INITIAL_FORM_DATA); diff --git a/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx b/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx index 43a03a438..19a1f8cb1 100644 --- a/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx +++ b/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx @@ -18,28 +18,28 @@ import useMixpanelTrack from '@/hooks/useMixpanelTrack'; import { EVENT_NAME } from '@/constants/eventName'; const ApplicationFormPage = () => { - const { clubId } = useParams<{ clubId: string }>(); + const { clubId, applicationFormId } = useParams<{ clubId: string; applicationFormId: string }>(); const navigate = useNavigate(); const questionRefs = useRef>([]); const [invalidQuestionIds, setInvalidQuestionIds] = useState([]); const trackEvent = useMixpanelTrack(); - const { data: clubDetail, error: clubError } = useGetClubDetail(clubId!); + if (!clubId || !applicationFormId) return null; + + const { data: clubDetail, error: clubError } = useGetClubDetail(clubId); const { data: formData, isLoading, isError, error: applicationError, - } = useGetApplication(clubId!); + } = useGetApplication(clubId, applicationFormId); useTrackPageView( 'ApplicationFormPage', clubDetail?.name ?? `club:${clubId ?? 'unknown'}`, ); - if (!clubId) return null; - - const STORAGE_KEY = `applicationAnswers_${clubId}`; + const STORAGE_KEY = `applicationAnswers_${clubId}_${applicationFormId}`; const saved = localStorage.getItem(STORAGE_KEY); const initialAnswers = saved ? JSON.parse(saved) : []; @@ -104,12 +104,12 @@ const ApplicationFormPage = () => { await applyToClub(clubId, answers); localStorage.removeItem(STORAGE_KEY); alert( - `"${clubDetail.name}" 동아리에 성공적으로 지원되었습니다.\n좋은 결과 있으시길 바랍니다🤗`, + `"${clubDetail.name}" 동아리에 성공적으로 지원되었습니다.\n좋은 결과 있으시길 바랍니다`, ); navigate(`/club/${clubId}`, { replace: true }); } catch (error) { alert( - '⚠️ 답변 제출에 실패했어요.\n네트워크 상태를 확인하거나 잠시 후 다시 시도해 주세요.', + '답변 제출에 실패했어요.\n네트워크 상태를 확인하거나 잠시 후 다시 시도해 주세요.', ); } }; From 4108f889317d921d601adef40602400a70e89048 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 9 Nov 2025 10:42:11 +0900 Subject: [PATCH 28/42] =?UTF-8?q?fix:=20=EB=9D=BC=EC=9A=B0=ED=84=B0=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0=20=EB=B0=8F=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20?= =?UTF-8?q?import=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/App.tsx | 2 +- frontend/src/hooks/queries/application/useGetApplication.ts | 2 +- frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx | 2 +- .../components/ClubApplyButton/ClubApplyButton.tsx | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index ef5af5fe9..3025dd911 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -77,7 +77,7 @@ const App = () => { } /> } /> } /> diff --git a/frontend/src/hooks/queries/application/useGetApplication.ts b/frontend/src/hooks/queries/application/useGetApplication.ts index 70f666d64..c82d728f8 100644 --- a/frontend/src/hooks/queries/application/useGetApplication.ts +++ b/frontend/src/hooks/queries/application/useGetApplication.ts @@ -6,6 +6,6 @@ export const useGetApplication = (clubId: string, applicationFormId: string) => queryKey: ['applicationForm', clubId, applicationFormId], queryFn: () => getApplication(clubId, applicationFormId), retry: false, - enabled: !!clubId || !!applicationFormId, + enabled: !!clubId && !!applicationFormId, }); }; diff --git a/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx b/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx index 19a1f8cb1..51c40fb52 100644 --- a/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx +++ b/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx @@ -51,7 +51,7 @@ const ApplicationFormPage = () => { useEffect(() => { localStorage.setItem(STORAGE_KEY, JSON.stringify(answers)); - }, [answers]); + }, [answers, clubId, applicationFormId]); if (isLoading) return ; if (isError || clubError) { diff --git a/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx b/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx index 2e24b6747..e1e096a52 100644 --- a/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx +++ b/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx @@ -9,7 +9,6 @@ import { useState } from 'react'; import { ApplicationForm } from '@/types/application'; import getApplicationOptions from '@/apis/application/getApplicationOptions'; import ApplicationSelectModal from '@/components/application/modals/ApplicationSelectModal'; -import { set } from 'date-fns'; interface ClubApplyButtonProps { deadlineText?: string; From c6709b00efe1b20896bf3c4c79dda6318ae4eee4 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 9 Nov 2025 10:54:19 +0900 Subject: [PATCH 29/42] =?UTF-8?q?fix:=20=EB=AA=A9=EC=97=85=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=9E=84=EC=8B=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/mocks/data/mockData.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/frontend/src/mocks/data/mockData.ts b/frontend/src/mocks/data/mockData.ts index ec2032150..73b1c72f3 100644 --- a/frontend/src/mocks/data/mockData.ts +++ b/frontend/src/mocks/data/mockData.ts @@ -1,4 +1,4 @@ -import { ApplicationFormData, ApplicationOption } from '@/types/application'; +import { ApplicationFormData, ApplicationForm } from '@/types/application'; import { CLUB_BACK, CLUB_BOB, CLUB_IVF, CLUB_TEST } from '../constants/clubApi'; type QuestionType = @@ -123,21 +123,20 @@ export const mockData: ApplicationFormData = { ], }; -export const mockOptions: Record = { +export const mockOptions: Record = { /*보블리스*/ [CLUB_BOB]: [ - { id: 101, name: '개발자로 지원하기' }, - { id: 102, name: '디자이너로 지원하기', url: 'https://docs.google.com/forms/d/e/1FAIpQLSeL0p3y7Z2YFymaPThXv5dYBU-wVIKqr3TrQlwHLyJneE6CcA/viewform' }, - { id: 103, name: '기획자로 지원하기' }, + { id: 'string101', title: '개발자로 지원하기' }, + { id: 'string103', title: '기획자로 지원하기' }, ], /*IVF*/ [CLUB_IVF]: [ - { id: 201, name: '선수로 지원하기' }, - { id: 202, name: '매니저로 지원하기' }, + { id: 'string201', title: '선수로 지원하기' }, + { id: 'string202', title: '매니저로 지원하기' }, ], /*백경예술연구회*/ [CLUB_BACK]: [ - { id: 301, name: '백경예술연구회 지원하기' }, + { id: 'string301', title: '백경예술연구회 지원하기' }, ], /*테스트*/ [CLUB_TEST]: [], From 0d39d6b38a5c69c6544814e83dcac991a43eeb42 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 9 Nov 2025 11:53:43 +0900 Subject: [PATCH 30/42] =?UTF-8?q?feat:=20=EB=AA=A8=EB=8B=AC=20onBackdropCl?= =?UTF-8?q?ick=20=EC=86=8D=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/modals/ApplicationSelectModal.tsx | 3 ++- frontend/src/components/common/Modal/Modal.tsx | 11 +++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/application/modals/ApplicationSelectModal.tsx b/frontend/src/components/application/modals/ApplicationSelectModal.tsx index 4c54e859c..422332a46 100644 --- a/frontend/src/components/application/modals/ApplicationSelectModal.tsx +++ b/frontend/src/components/application/modals/ApplicationSelectModal.tsx @@ -16,13 +16,14 @@ const ApplicationSelectModal = ({ isOpen, onClose, options, onSelect }: Applicat onClose={onClose} title="지원 분야 선택" description="지원할 분야를 선택해주세요" + onBackdropClick={() => {return false;}} > {options.length === 0 ? ( 지원 가능한 분야가 없습니다. ) : ( {options.map((option) => ( - onSelect(option)}> + {onSelect(option); onClose();}}> {option.title} ))} diff --git a/frontend/src/components/common/Modal/Modal.tsx b/frontend/src/components/common/Modal/Modal.tsx index 35ee2a591..166e8dd0b 100644 --- a/frontend/src/components/common/Modal/Modal.tsx +++ b/frontend/src/components/common/Modal/Modal.tsx @@ -8,13 +8,20 @@ export interface ModalProps { title: string; description?: string; children?: ReactNode; + onBackdropClick?: () => boolean | void; } -const Modal = ({ isOpen, onClose, title, description, children }: ModalProps) => { +const Modal = ({ isOpen, onClose, title, description, children, onBackdropClick }: ModalProps) => { if (!isOpen) return null; + const handleOverlayClick = () => { + const result = onBackdropClick?.(); + if (result === false) return; + onClose(); + }; + return ( - + ) => e.stopPropagation()}> From ca1462ae273335a46324fa9151fc127b228c8e94 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 9 Nov 2025 14:44:27 +0900 Subject: [PATCH 31/42] =?UTF-8?q?fix:=20=EC=82=BC=ED=95=AD=EC=97=B0?= =?UTF-8?q?=EC=82=B0=EC=9E=90=20=EA=B4=80=EC=8B=AC=EC=82=AC=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modals/ApplicationSelectModal.tsx | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/application/modals/ApplicationSelectModal.tsx b/frontend/src/components/application/modals/ApplicationSelectModal.tsx index 422332a46..ab664d42b 100644 --- a/frontend/src/components/application/modals/ApplicationSelectModal.tsx +++ b/frontend/src/components/application/modals/ApplicationSelectModal.tsx @@ -9,6 +9,27 @@ export interface ApplicationSelectModalProps { onSelect: (option: ApplicationForm) => void; } +interface OptionsListProps { + options: ApplicationForm[]; + onSelect: (option: ApplicationForm) => void; +} + +const OptionsList = ({ options, onSelect }: OptionsListProps) => { + if (options.length === 0) { + return 지원 가능한 분야가 없습니다.; + } + + return ( + + {options.map((option) => ( + {onSelect(option);}}> + {option.title} + + ))} + + ) +}; + const ApplicationSelectModal = ({ isOpen, onClose, options, onSelect }: ApplicationSelectModalProps) => { return ( {return false;}} > - {options.length === 0 ? ( - 지원 가능한 분야가 없습니다. - ) : ( - - {options.map((option) => ( - {onSelect(option); onClose();}}> - {option.title} - - ))} - - )} + ); }; From ad58cc7e42f4d84c3e44afa7e8f53a6b33fdcfb6 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 9 Nov 2025 15:49:06 +0900 Subject: [PATCH 32/42] =?UTF-8?q?fix:=20handleOverlayClick=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/modals/ApplicationSelectModal.tsx | 11 +++++++++-- frontend/src/components/common/Modal/Modal.tsx | 8 +------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/application/modals/ApplicationSelectModal.tsx b/frontend/src/components/application/modals/ApplicationSelectModal.tsx index ab664d42b..30d07fa24 100644 --- a/frontend/src/components/application/modals/ApplicationSelectModal.tsx +++ b/frontend/src/components/application/modals/ApplicationSelectModal.tsx @@ -7,6 +7,7 @@ export interface ApplicationSelectModalProps { onClose: () => void; options: ApplicationForm[]; onSelect: (option: ApplicationForm) => void; + onBackdropClick?: () => boolean | void; } interface OptionsListProps { @@ -30,14 +31,20 @@ const OptionsList = ({ options, onSelect }: OptionsListProps) => { ) }; -const ApplicationSelectModal = ({ isOpen, onClose, options, onSelect }: ApplicationSelectModalProps) => { +const ApplicationSelectModal = ({ isOpen, onClose, options, onSelect, onBackdropClick }: ApplicationSelectModalProps) => { + const handleOverlayClick = () => { + const result = onBackdropClick?.(); + if (result === false) return; + onClose(); + }; + return ( {return false;}} + onBackdropClick={handleOverlayClick} > diff --git a/frontend/src/components/common/Modal/Modal.tsx b/frontend/src/components/common/Modal/Modal.tsx index 166e8dd0b..a688ef6e8 100644 --- a/frontend/src/components/common/Modal/Modal.tsx +++ b/frontend/src/components/common/Modal/Modal.tsx @@ -14,14 +14,8 @@ export interface ModalProps { const Modal = ({ isOpen, onClose, title, description, children, onBackdropClick }: ModalProps) => { if (!isOpen) return null; - const handleOverlayClick = () => { - const result = onBackdropClick?.(); - if (result === false) return; - onClose(); - }; - return ( - + ) => e.stopPropagation()}> From f446b1b857b637b8aca5b2abd338ad5f25a18a15 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 9 Nov 2025 19:50:42 +0900 Subject: [PATCH 33/42] =?UTF-8?q?fix:=20=EB=9D=BC=EC=9D=B4=EB=B8=8C?= =?UTF-8?q?=EB=9F=AC=EB=A6=AC=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20useEffect?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/common/Modal/Modal.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/common/Modal/Modal.tsx b/frontend/src/components/common/Modal/Modal.tsx index a688ef6e8..d167a5a3f 100644 --- a/frontend/src/components/common/Modal/Modal.tsx +++ b/frontend/src/components/common/Modal/Modal.tsx @@ -1,4 +1,4 @@ -import { MouseEvent, ReactNode } from "react"; +import { MouseEvent, ReactNode, useEffect } from "react"; import * as Styled from './Modal.styles'; import { RemoveScroll } from "react-remove-scroll"; @@ -11,9 +11,19 @@ export interface ModalProps { onBackdropClick?: () => boolean | void; } + const Modal = ({ isOpen, onClose, title, description, children, onBackdropClick }: ModalProps) => { if (!isOpen) return null; + useEffect(() => { + if (isOpen) { + document.body.style.overflow = 'hidden'; + } + return () => { + document.body.style.overflow = 'auto'; + }; + }, [isOpen]); + return ( From c159a23cba348b8413db92883a895c8f2c2f9ebe Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 9 Nov 2025 19:58:50 +0900 Subject: [PATCH 34/42] =?UTF-8?q?fix:=20=EB=AA=A8=EB=8B=AC=20=EB=8B=AB?= =?UTF-8?q?=ED=9E=88=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/application/modals/ApplicationSelectModal.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/src/components/application/modals/ApplicationSelectModal.tsx b/frontend/src/components/application/modals/ApplicationSelectModal.tsx index 30d07fa24..8950f058e 100644 --- a/frontend/src/components/application/modals/ApplicationSelectModal.tsx +++ b/frontend/src/components/application/modals/ApplicationSelectModal.tsx @@ -33,9 +33,7 @@ const OptionsList = ({ options, onSelect }: OptionsListProps) => { const ApplicationSelectModal = ({ isOpen, onClose, options, onSelect, onBackdropClick }: ApplicationSelectModalProps) => { const handleOverlayClick = () => { - const result = onBackdropClick?.(); - if (result === false) return; - onClose(); + return false; }; return ( From 428b637a379e98d1fe023e1fc4070cf846f9ba58 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 9 Nov 2025 20:13:00 +0900 Subject: [PATCH 35/42] =?UTF-8?q?fix:=20react-remove-scroll=20=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/package-lock.json | 128 ------------------ frontend/package.json | 1 - .../src/components/common/Modal/Modal.tsx | 3 - 3 files changed, 132 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 422d755ae..73176c5f2 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -23,7 +23,6 @@ "react-datepicker": "^8.1.0", "react-dom": "^19.0.0", "react-markdown": "^9.0.3", - "react-remove-scroll": "^2.7.1", "react-router-dom": "^7.1.1", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", @@ -7873,12 +7872,6 @@ "dev": true, "license": "MIT" }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "license": "MIT" - }, "node_modules/detect-port": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-2.1.0.tgz", @@ -10105,15 +10098,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -16358,53 +16342,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-remove-scroll": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", - "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", - "license": "MIT", - "dependencies": { - "react-remove-scroll-bar": "^2.3.7", - "react-style-singleton": "^2.2.3", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.3", - "use-sidecar": "^1.1.3" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-remove-scroll-bar": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", - "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", - "license": "MIT", - "dependencies": { - "react-style-singleton": "^2.2.2", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/react-router": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.1.tgz", @@ -16445,28 +16382,6 @@ "react-dom": ">=18" } }, - "node_modules/react-style-singleton": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", - "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", - "license": "MIT", - "dependencies": { - "get-nonce": "^1.0.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -18984,49 +18899,6 @@ "dev": true, "license": "MIT" }, - "node_modules/use-callback-ref": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", - "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-sidecar": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", - "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", - "license": "MIT", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", diff --git a/frontend/package.json b/frontend/package.json index f77cf6748..919f90011 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,7 +34,6 @@ "react-datepicker": "^8.1.0", "react-dom": "^19.0.0", "react-markdown": "^9.0.3", - "react-remove-scroll": "^2.7.1", "react-router-dom": "^7.1.1", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", diff --git a/frontend/src/components/common/Modal/Modal.tsx b/frontend/src/components/common/Modal/Modal.tsx index d167a5a3f..0440917ad 100644 --- a/frontend/src/components/common/Modal/Modal.tsx +++ b/frontend/src/components/common/Modal/Modal.tsx @@ -1,6 +1,5 @@ import { MouseEvent, ReactNode, useEffect } from "react"; import * as Styled from './Modal.styles'; -import { RemoveScroll } from "react-remove-scroll"; export interface ModalProps { isOpen: boolean; @@ -26,7 +25,6 @@ const Modal = ({ isOpen, onClose, title, description, children, onBackdropClick return ( - ) => e.stopPropagation()}> {title && {title}} @@ -35,7 +33,6 @@ const Modal = ({ isOpen, onClose, title, description, children, onBackdropClick {description && {description}} {children} - ); } From bca239a24a5b4e99dcf31fbf6619c8c9b8a5df82 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 9 Nov 2025 20:39:43 +0900 Subject: [PATCH 36/42] =?UTF-8?q?fix:=20=EB=AA=A8=EB=8B=AC=20=EC=97=B4?= =?UTF-8?q?=EB=A6=BC=20=ED=9B=84=20=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EC=95=88?= =?UTF-8?q?=20=EB=90=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/common/Modal/Modal.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/common/Modal/Modal.tsx b/frontend/src/components/common/Modal/Modal.tsx index 0440917ad..9723424fe 100644 --- a/frontend/src/components/common/Modal/Modal.tsx +++ b/frontend/src/components/common/Modal/Modal.tsx @@ -12,17 +12,17 @@ export interface ModalProps { const Modal = ({ isOpen, onClose, title, description, children, onBackdropClick }: ModalProps) => { - if (!isOpen) return null; - useEffect(() => { if (isOpen) { document.body.style.overflow = 'hidden'; } return () => { - document.body.style.overflow = 'auto'; + document.body.style.overflow = ''; }; }, [isOpen]); + if (!isOpen) return null; + return ( ) => e.stopPropagation()}> From bba7f92f7a1d0be71726003cd7918cd8fc5b9943 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 9 Nov 2025 21:37:29 +0900 Subject: [PATCH 37/42] =?UTF-8?q?fix:=20=EC=99=B8=EB=B6=80=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=EC=84=9C=20=EB=B6=84=EA=B8=B0=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ClubApplyButton/ClubApplyButton.tsx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx b/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx index e1e096a52..e7fde42db 100644 --- a/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx +++ b/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx @@ -31,6 +31,8 @@ const ClubApplyButton = ({ deadlineText }: ClubApplyButtonProps) => { if (!clubId || !clubDetail) return null; + + // 내부 폼 이동 const goWithForm = async (formId: string) => { try { const formDetail = await getApplication(clubId, formId); @@ -42,6 +44,7 @@ const ClubApplyButton = ({ deadlineText }: ClubApplyButtonProps) => { } }; + // url 존재 시 외부, 내부 지원서 옵션에 따른 처리 const openByOption = (option?: ApplicationForm) => { if (!option) return; void goWithForm(option.id); @@ -59,14 +62,23 @@ const ClubApplyButton = ({ deadlineText }: ClubApplyButtonProps) => { const list = await getApplicationOptions(clubId); if (list.length <= 0) { - alert('현재 지원 가능한 지원서가 없습니다.'); - } else if (list.length === 1) { + return; + } + + if (list.length === 1) { await goWithForm(list[0].id); } else { setOptions(list); setIsOpen(true); } } catch (e) { + const externalApplicationUrl = clubDetail.externalApplicationUrl?.trim(); + if (externalApplicationUrl) { + window.open(externalApplicationUrl, '_blank'); + return; + } + setOptions([]); + setIsOpen(true); console.error('지원서 옵션 조회 중 오류가 발생했습니다.', e); alert('지원서 정보를 불러오는 중 오류가 발생했습니다. 다시 시도해주세요.'); } From ce3f273b9821aa47c24f585b01536ac03a3ee3d8 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Sun, 9 Nov 2025 21:53:45 +0900 Subject: [PATCH 38/42] =?UTF-8?q?fix:=20early=20return=20=EB=B0=A9?= =?UTF-8?q?=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/ClubApplyButton/ClubApplyButton.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx b/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx index e7fde42db..0673fa00d 100644 --- a/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx +++ b/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx @@ -67,10 +67,10 @@ const ClubApplyButton = ({ deadlineText }: ClubApplyButtonProps) => { if (list.length === 1) { await goWithForm(list[0].id); - } else { - setOptions(list); - setIsOpen(true); + return; } + setOptions(list); + setIsOpen(true); } catch (e) { const externalApplicationUrl = clubDetail.externalApplicationUrl?.trim(); if (externalApplicationUrl) { From e642dacce8695264751ffaf6ebb76834d67fda66 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Mon, 10 Nov 2025 02:41:48 +0900 Subject: [PATCH 39/42] =?UTF-8?q?fix:=20=ED=9B=85=20=EA=B7=9C=EC=B9=99=20?= =?UTF-8?q?=EC=9C=84=EB=B0=98=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/hooks/queries/application/useGetApplication.ts | 4 ++-- .../tabs/ApplicationEditTab/ApplicationEditTab.tsx | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/frontend/src/hooks/queries/application/useGetApplication.ts b/frontend/src/hooks/queries/application/useGetApplication.ts index c82d728f8..81f98bebf 100644 --- a/frontend/src/hooks/queries/application/useGetApplication.ts +++ b/frontend/src/hooks/queries/application/useGetApplication.ts @@ -1,10 +1,10 @@ import { useQuery } from '@tanstack/react-query'; import getApplication from '@/apis/application/getApplication'; -export const useGetApplication = (clubId: string, applicationFormId: string) => { +export const useGetApplication = (clubId?: string | null, applicationFormId?: string | null) => { return useQuery({ queryKey: ['applicationForm', clubId, applicationFormId], - queryFn: () => getApplication(clubId, applicationFormId), + queryFn: () => getApplication(clubId!, applicationFormId!), retry: false, enabled: !!clubId && !!applicationFormId, }); diff --git a/frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx b/frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx index 0b0ee7a95..29a7da418 100644 --- a/frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx +++ b/frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx @@ -16,12 +16,8 @@ import { APPLICATION_FORM } from '@/constants/APPLICATION_FORM'; const ApplicationEditTab = () => { const { clubId, applicationFormId } = useAdminClubContext(); - if (!clubId || !applicationFormId) return null; - const { data, isLoading, isError } = useGetApplication(clubId, applicationFormId); - - const [formData, setFormData] = - useState(INITIAL_FORM_DATA); + const [formData, setFormData] = useState(INITIAL_FORM_DATA); useEffect(() => { if (data) { @@ -36,6 +32,8 @@ const ApplicationEditTab = () => { return maxId + 1; }); + if (!clubId || !applicationFormId) return null; + const addQuestion = () => { const newQuestion: Question = { id: nextId, From a70dbbf0d25c5aee9f69dcc24017fdcc79e3bd61 Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Mon, 10 Nov 2025 03:40:16 +0900 Subject: [PATCH 40/42] =?UTF-8?q?fix:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20alert=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/ClubApplyButton/ClubApplyButton.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx b/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx index 0673fa00d..65ffb1f82 100644 --- a/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx +++ b/frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx @@ -80,7 +80,6 @@ const ClubApplyButton = ({ deadlineText }: ClubApplyButtonProps) => { setOptions([]); setIsOpen(true); console.error('지원서 옵션 조회 중 오류가 발생했습니다.', e); - alert('지원서 정보를 불러오는 중 오류가 발생했습니다. 다시 시도해주세요.'); } }; From 2014acbeca71d7e9c97537ad24fc3eb4cd80829d Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Mon, 10 Nov 2025 03:54:48 +0900 Subject: [PATCH 41/42] =?UTF-8?q?fix:=20=ED=99=94=EB=A9=B4=20=ED=81=AC?= =?UTF-8?q?=EA=B8=B0=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=AA=A8=EB=8B=AC=20?= =?UTF-8?q?=ED=81=AC=EA=B8=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/common/Modal/Modal.styles.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/common/Modal/Modal.styles.ts b/frontend/src/components/common/Modal/Modal.styles.ts index cfb82f4a8..455b90623 100644 --- a/frontend/src/components/common/Modal/Modal.styles.ts +++ b/frontend/src/components/common/Modal/Modal.styles.ts @@ -1,3 +1,4 @@ +import { media } from '@/styles/mediaQuery'; import styled from 'styled-components'; export const Overlay = styled.div<{ isOpen: boolean }>` @@ -12,7 +13,8 @@ export const Overlay = styled.div<{ isOpen: boolean }>` `; export const Container = styled.div<{ isOpen: boolean }>` - min-width: 500px; + max-width: 500px; + width: 100%; max-height: 90vh; background: #fff; border-radius: 10px; From e20db39b074acce1c6bf5d3e36efc675d59a97bb Mon Sep 17 00:00:00 2001 From: suhyun113 <163711629+suhyun113@users.noreply.github.com> Date: Mon, 10 Nov 2025 10:18:54 +0900 Subject: [PATCH 42/42] =?UTF-8?q?fix:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EB=A7=A4=ED=95=91=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/apis/application/getApplicationOptions.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frontend/src/apis/application/getApplicationOptions.ts b/frontend/src/apis/application/getApplicationOptions.ts index 9c602ea2c..cd9ed2baa 100644 --- a/frontend/src/apis/application/getApplicationOptions.ts +++ b/frontend/src/apis/application/getApplicationOptions.ts @@ -18,10 +18,7 @@ const getApplicationOptions = async (clubId: string) => { if (result && result.data && Array.isArray(result.data.forms)) { forms = result.data.forms; } - return forms.map((form: { id: string; title: string }) => ({ - id: form.id, - title: form.title, - })); + return forms; } catch (error) { console.error('지원서 옵션 조회 중 오류가 발생했습니다.', error); throw error;