-
Notifications
You must be signed in to change notification settings - Fork 3
[feature] 지원서 제작/수정/불러오기 API 연결하기 #499
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
db4cf77
9d9a8f0
54225d3
fa52de4
dfc491c
55361c0
00a8e33
62f4479
161deea
2fd8f14
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,33 @@ | ||||||||||
| import API_BASE_URL from '@/constants/api'; | ||||||||||
| import { secureFetch } from '@/apis/auth/secureFetch'; | ||||||||||
| import { ApplicationFormData } from '@/types/application'; | ||||||||||
|
|
||||||||||
| export const createApplication = async ( | ||||||||||
| data: ApplicationFormData, | ||||||||||
| clubId: string, | ||||||||||
| ) => { | ||||||||||
| try { | ||||||||||
| const response = await secureFetch( | ||||||||||
| `${API_BASE_URL}/api/club/${clubId}/application`, | ||||||||||
| { | ||||||||||
| method: 'POST', | ||||||||||
| headers: { | ||||||||||
| 'Content-Type': 'application/json', | ||||||||||
| }, | ||||||||||
| body: JSON.stringify(data), | ||||||||||
| }, | ||||||||||
| ); | ||||||||||
|
|
||||||||||
| if (!response.ok) { | ||||||||||
| throw new Error('지원서 제출에 실패했습니다.'); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| const result = await response.json(); | ||||||||||
| return result.data; | ||||||||||
|
Comment on lines
+25
to
+26
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainAPI 응답 구조 확인 필요
다음 스크립트를 실행하여 API 응답 구조를 확인해주세요: 🏁 Script executed: #!/bin/bash
# Description: API 응답 구조 확인을 위해 관련 코드 검색
# updateApplication과 getApplication의 응답 처리 방식 확인
echo "=== Checking response handling in other API files ==="
rg -A 5 "result\.data|response\.json\(\)" --glob "frontend/src/apis/application/*.ts"
# 백엔드 API 응답 구조 관련 타입 정의 확인
echo -e "\n=== Checking API response type definitions ==="
fd -e ts -e tsx . frontend/src/types | xargs rg -l "ApiResponse|Response" | head -5 | xargs catLength of output: 2025 🏁 Script executed: #!/bin/bash
# 1. List any type definition files under src
echo "=== Listing type definition files under src ==="
find frontend/src -type f \( -name "*type*.ts" -o -name "*types*.ts" \) | sed 's/^/FOUND: /'
# 2. Search for generic ApiResponse or Response interfaces
echo -e "\n=== Searching for ApiResponse or Response interface definitions ==="
rg -H "interface .*Response" frontend/src
rg -H "type .*Response" frontend/src
# 3. Search for ApiResponse<T> usage
echo -e "\n=== Searching for ApiResponse< usage ==="
rg -H "ApiResponse<" frontend/src
# 4. Search for 'statuscode' string in code to find sample payloads
echo -e "\n=== Searching for sample payloads with 'statuscode' ==="
rg -H "statuscode" -A2 frontend/srcLength of output: 1537 🏁 Script executed: #!/bin/bash
# Display contents of the mock API handlers to inspect response structures for createApplication
echo "=== Showing mock API handlers for application endpoints ==="
sed -n '1,200p' frontend/src/mocks/api/applyHandlers.test.tsLength of output: 4973 API 응답 구조에 맞춰 반환값 수정 필요
- const result = await response.json();
- return result.data;
+ const result = await response.json();
+ return result;동일한 패턴을 사용하는 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||
| } catch (error) { | ||||||||||
| console.error('지원서 제출 중 오류 발생:', error); | ||||||||||
| throw error; | ||||||||||
| } | ||||||||||
| }; | ||||||||||
|
|
||||||||||
| export default createApplication; | ||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import API_BASE_URL from '@/constants/api'; | ||
|
|
||
| const getApplication = async (clubId: string) => { | ||
| try { | ||
| const response = await fetch(`${API_BASE_URL}/api/club/${clubId}/apply`); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainAPI 엔드포인트 일관성 확인 필요
다음 스크립트로 API 엔드포인트 사용 패턴을 확인해주세요: 🏁 Script executed: #!/bin/bash
# API 엔드포인트 패턴 확인
echo "=== application 관련 API 엔드포인트 검색 ==="
rg "/api/club.*/(apply|application)" --type ts --type tsx -A 2 -B 2
echo "=== API 엔드포인트 상수 확인 ==="
rg "application|apply" frontend/src/constants/ --type tsLength of output: 397 아래 스크립트로 🏁 Script executed: #!/bin/bash
# updateApplication.ts 위치 찾기
echo "=== updateApplication.ts 파일 경로 ==="
update_file=$(fd updateApplication.ts)
echo "$update_file"
# 해당 파일에서 fetch 사용 부분 추출
echo "=== updateApplication.ts 내 fetch 호출 스니펫 ==="
rg "fetch" -n "$update_file"Length of output: 441 updateApplication.ts 파일에서 실제 사용 중인 API 엔드포인트를 확인하기 위해 아래 스크립트를 실행해주세요. 🏁 Script executed: #!/bin/bash
# updateApplication.ts에서 API 엔드포인트 확인
file="frontend/src/apis/application/updateApplication.ts"
echo "=== 파일 경로 ==="
echo "$file"
echo "=== '/api/club' 포함 라인 확인 ==="
rg "/api/club" -n "$file"Length of output: 404 API 엔드포인트 일관성 유지 필요
위 예시처럼 두 파일 모두 🤖 Prompt for AI Agents |
||
| if (!response.ok) { | ||
| throw new Error(`Failed to fetch: ${response.statusText}`); | ||
| } | ||
|
|
||
| const result = await response.json(); | ||
| return result.data; | ||
| } catch (error) { | ||
| // [x] FIXME: | ||
| // {"statuscode":"800-1","message":"지원서가 존재하지 않습니다.","data":null} | ||
| console.error('Error fetching club details', error); | ||
| throw error; | ||
| } | ||
| }; | ||
|
|
||
| export default getApplication; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import API_BASE_URL from '@/constants/api'; | ||
| import { secureFetch } from '@/apis/auth/secureFetch'; | ||
| import { ApplicationFormData } from '@/types/application'; | ||
|
|
||
| export const updateApplication = async ( | ||
| data: ApplicationFormData, | ||
| clubId: string, | ||
| ) => { | ||
| try { | ||
| const response = await secureFetch( | ||
| `${API_BASE_URL}/api/club/${clubId}/application`, | ||
| { | ||
| method: 'PUT', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| body: JSON.stringify(data), | ||
| }, | ||
| ); | ||
|
|
||
| if (!response.ok) { | ||
| throw new Error('지원서 수정에 실패했습니다.'); | ||
| } | ||
|
|
||
| const result = await response.json(); | ||
| return result.data; | ||
| } catch (error) { | ||
| console.error('지원서 수정 중 오류 발생:', error); | ||
| throw error; | ||
| } | ||
| }; | ||
|
|
||
| export default updateApplication; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import { useQuery } from '@tanstack/react-query'; | ||
| import getApplication from '@/apis/application/getApplication'; | ||
|
|
||
| export const useGetApplication = (clubId: string) => { | ||
| return useQuery({ | ||
| queryKey: ['applicationForm', clubId], | ||
| queryFn: () => getApplication(clubId), | ||
| retry: false, | ||
| }); | ||
| }; |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -25,7 +25,7 @@ export const applyHandlers = [ | |||||
| return HttpResponse.json( | ||||||
| { | ||||||
| clubId, | ||||||
| form_title: mockData.form_title, | ||||||
| form_title: mockData.title, | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. API 응답 구조 불일치 문제
다음과 같이 수정하여 일관성을 맞춰주세요: - form_title: mockData.title,
+ title: mockData.title,📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| questions: mockData.questions, | ||||||
| }, | ||||||
| { status: 200 }, | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,25 +1,36 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 지원서 제작하기 : 지원서 제작 컴포넌트 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 지원서 수정과 제작을 맡을 컴포넌트 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Todo: 질문 삭제 및 질문 추가 기능 구현 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useState, useEffect } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import QuestionBuilder from '@/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { QuestionType } from '@/types/application'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { Question } from '@/types/application'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { mockData } from '@/mocks/data/mockData'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { ApplicationFormData } from '@/types/application'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { PageContainer } from '@/styles/PageContainer.styles'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import * as Styled from './CreateApplicationForm.styles'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import INITIAL_FORM_DATA from '@/constants/INITIAL_FORM_DATA'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { QuestionDivider } from './CreateApplicationForm.styles'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useAdminClubContext } from '@/context/AdminClubContext'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useGetApplication } from '@/hooks/queries/application/useGetApplication'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import createApplication from '@/apis/application/createApplication'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import updateApplication from '@/apis/application/updateApplication'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const CreateApplicationForm = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [formData, setFormData] = useState<ApplicationFormData>( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockData ?? INITIAL_FORM_DATA, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { clubId } = useAdminClubContext(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!clubId) return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { data, isLoading, isError } = useGetApplication(clubId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+19
to
+20
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 로딩 및 에러 상태 처리 필요
다음과 같이 로딩 및 에러 상태를 처리하세요: const { data, isLoading, isError } = useGetApplication(clubId);
+ if (isLoading) {
+ return <div>로딩 중...</div>;
+ }
+
+ if (isError) {
+ return <div>지원서를 불러오는 중 오류가 발생했습니다.</div>;
+ }
+
const [formData, setFormData] =📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [formData, setFormData] = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useState<ApplicationFormData>(INITIAL_FORM_DATA); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (data) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setFormData(data); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [data]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [nextId, setNextId] = useState(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const questions = mockData?.questions ?? INITIAL_FORM_DATA.questions; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const questions = data?.questions ?? INITIAL_FORM_DATA.questions; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (questions.length === 0) return 1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const maxId = Math.max(...questions.map((q) => q.id)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const maxId = Math.max(...questions.map((q: Question) => q.id)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return maxId + 1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -29,6 +40,7 @@ const CreateApplicationForm = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| title: '', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: '', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'SHORT_TEXT', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| items: [], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| options: { required: false }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setFormData((prev) => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -61,7 +73,7 @@ const CreateApplicationForm = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleFormTitleChange = (value: string) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setFormData((prev) => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...prev, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| form_title: value, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| title: value, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -87,7 +99,7 @@ const CreateApplicationForm = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? q.items && q.items.length >= 2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? q.items | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : [{ value: '' }, { value: '' }] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : undefined, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : [], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -102,12 +114,37 @@ const CreateApplicationForm = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleSubmit = async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!clubId) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const reorderedQuestions = formData.questions.map((q, idx) => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...q, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: idx + 1, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+119
to
+122
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chain질문 ID 재정렬의 영향 확인 필요 저장할 때마다 질문 ID를 1부터 순차적으로 재정렬하고 있습니다. 다른 곳에서 이 ID를 참조하거나 백엔드에서 특정 ID 값에 의존하는 경우 문제가 발생할 수 있습니다. 다음 스크립트를 실행하여 질문 ID 사용 현황을 확인해주세요: 🏁 Script executed: #!/bin/bash
# Description: 질문 ID가 다른 곳에서 참조되는지 확인
# 질문 ID 참조 패턴 검색
echo "=== Searching for question ID references ==="
rg -A 3 "question\.id|q\.id|questionId" --glob "frontend/src/**/*.{ts,tsx}" | grep -v "CreateApplicationForm"
# 백엔드 API에서 질문 ID 처리 방식 확인
echo -e "\n=== Checking backend question ID handling ==="
fd -e ts -e js . backend/src | xargs rg -A 3 "question.*id|questionId" | head -20Length of output: 4395 질문 ID 재정렬 중단 및 안정적 ID 유지 필요 저장할 때마다 질문 ID를 1부터 다시 매기면, 아래 컴포넌트들이 기존 ID로 데이터를 조회·처리하기 때문에 의도치 않은 동작(답안 손실, 키 재생성 등)이 발생할 수 있습니다.
- const reorderedQuestions = formData.questions.map((q, idx) => ({
- ...q,
- id: idx + 1,
- }));
+ // ID 재정렬 제거: 기존 ID 유지
+ const reorderedQuestions = formData.questions;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const payload: ApplicationFormData = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...formData, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| questions: reorderedQuestions, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (data) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await updateApplication(payload, clubId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alert('지원서가 성공적으로 수정되었습니다.'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion alert() 대신 토스트 알림 사용 권장 브라우저의 토스트 알림 라이브러리를 사용하거나 커스텀 알림 컴포넌트를 구현하시겠습니까? Also applies to: 134-134, 137-137 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await createApplication(payload, clubId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alert('지원서가 성공적으로 생성되었습니다.'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alert('지원서 저장에 실패했습니다.'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error(error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+117
to
+140
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion API 호출 중 로딩 상태 추가 필요 API 호출 중에 로딩 상태가 없어 사용자가 여러 번 클릭하거나 중복 제출이 발생할 수 있습니다. 다음과 같이 로딩 상태를 추가하세요: + const [isSubmitting, setIsSubmitting] = useState(false);
+
const handleSubmit = async () => {
if (!clubId) return;
+ if (isSubmitting) return;
+
+ setIsSubmitting(true);
const reorderedQuestions = formData.questions.map((q, idx) => ({
...q,
id: idx + 1,
}));
const payload: ApplicationFormData = {
...formData,
questions: reorderedQuestions,
};
try {
if (data) {
await updateApplication(payload, clubId);
alert('지원서가 성공적으로 수정되었습니다.');
} else {
await createApplication(payload, clubId);
alert('지원서가 성공적으로 생성되었습니다.');
}
} catch (error) {
alert('지원서 저장에 실패했습니다.');
console.error(error);
+ } finally {
+ setIsSubmitting(false);
}
};그리고 버튼에 로딩 상태를 반영하세요: - <Styled.submitButton onClick={handleSubmit}>
- 저장하기
+ <Styled.submitButton onClick={handleSubmit} disabled={isSubmitting}>
+ {isSubmitting ? '저장 중...' : '저장하기'}
</Styled.submitButton>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <PageContainer> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Styled.FormTitle | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type='text' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value={formData.form_title} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value={formData.title} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onChange={(e) => handleFormTitleChange(e.target.value)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| placeholder='지원서 제목을 입력하세요' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ></Styled.FormTitle> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -135,7 +172,9 @@ const CreateApplicationForm = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 질문 추가 + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Styled.AddQuestionButton> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Styled.ButtonWrapper> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Styled.submitButton>제출하기</Styled.submitButton> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Styled.submitButton onClick={handleSubmit}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 저장하기 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Styled.submitButton> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Styled.ButtonWrapper> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </PageContainer> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,7 @@ export interface Question { | |
| options: { | ||
| required: boolean; | ||
| }; | ||
| items?: { value: string }[]; | ||
| items: { value: string }[]; | ||
| } | ||
|
|
||
| export interface QuestionBuilderProps extends Question { | ||
|
|
@@ -46,7 +46,7 @@ export interface ChoiceProps extends QuestionComponentProps { | |
| } | ||
|
|
||
| export interface ApplicationFormData { | ||
| form_title: string; | ||
| title: string; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chain프로퍼티 이름 변경 검증 필요
다음 스크립트로 모든 파일에서 🏁 Script executed: #!/bin/bash
# form_title 사용이 남아있는지 확인
echo "=== form_title 사용 검색 ==="
rg "form_title" --type ts --type tsx
echo "=== ApplicationFormData 타입 사용 검색 ==="
rg -A 3 -B 3 "ApplicationFormData" --type ts --type tsxLength of output: 290 다음 스크립트로 🏁 Script executed: #!/bin/bash
# form_title 사용 여부 확인
echo "=== form_title 검색 ==="
rg "form_title" -g '*.ts' -g '*.tsx'
# ApplicationFormData 타입 사용 여부 확인
echo "=== ApplicationFormData 검색 ==="
rg -A3 -B3 "ApplicationFormData" -g '*.ts' -g '*.tsx'Length of output: 5635 form_title 잔여 참조 수정 필요
• frontend/src/mocks/api/apply.ts
위 파일들 외에도 다른 mock/data나 테스트 코드에 남아있는지 한 번 더 확인 부탁드립니다. 🤖 Prompt for AI Agents |
||
| questions: Question[]; | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
입력 파라미터 검증 추가 필요
data와clubId파라미터에 대한 검증이 없습니다. 잘못된 데이터로 API 호출이 발생할 수 있습니다.다음과 같이 입력 검증을 추가하는 것을 권장합니다:
export const createApplication = async ( data: ApplicationFormData, clubId: string, ) => { + if (!clubId || !clubId.trim()) { + throw new Error('clubId가 필요합니다.'); + } + if (!data || !data.title || !data.questions) { + throw new Error('유효하지 않은 지원서 데이터입니다.'); + } try {📝 Committable suggestion
🤖 Prompt for AI Agents