-
Notifications
You must be signed in to change notification settings - Fork 0
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
π λ°λν νΌ μ μ² νκΈ° μ μ #87
Conversation
Walkthroughμ΄ PRμ μ¬λ¬ μ»΄ν¬λνΈ, API, νμ λ° νν¬μ κ±Έμ³ λ€μν λ³κ²½μ¬νμ ν¬ν¨ν©λλ€. API λΌμ°νΈμμλ POST μλ΅μ HTTP μν μ½λλ₯Ό ν¬ν¨νλλ‘ μμ λκ³ , νΌ APIμ GET λ©μλκ° μΆκ°λμμ΅λλ€. λν νμ΄μ§ λ° Application μ»΄ν¬λνΈμ νλΌλ―Έν° νμ μ΄ numberμμ stringμΌλ‘ λ³κ²½λμμΌλ©°, μλ‘μ΄ UI μ»΄ν¬λνΈ(체ν¬λ°μ€, λλ‘λ€μ΄, λ€μ€ μ΅μ , μ΅μ 컨ν μ΄λ, λ¬Έμ₯ μ΅μ )κ° μΆκ°λκ³ κΈ°μ‘΄ μ»΄ν¬λνΈ λͺ κ°κ° μμ λμμ΅λλ€. 곡μ νμ λ° react-query κΈ°λ°μ νΌ μ‘°ν/μ μΆ νν¬λ μλ‘ λμ λμμ΅λλ€. Changes
Sequence Diagram(s)sequenceDiagram
participant User as μ¬μ©μ
participant AL as ApplicationLayout
participant PH as usePostApplication
participant API as postApplication API
participant Server as μλ²
participant Toast as ν μ€νΈ μλ¦Ό
User->>AL: νΌ μ μΆ
AL->>PH: onSubmit νΈμΆ
PH->>API: postApplication νΈμΆ (params, type, data)
API->>Server: HTTP POST μμ²
Server-->>API: μλ΅ (data, status)
API-->>PH: μλ΅ λ°ν
PH-->>AL: λ³μ΄ μ±κ³΅
AL->>Toast: μ±κ³΅ λ©μμ§ νμ λ° λ¦¬λ€μ΄λ νΈ
sequenceDiagram
participant Hook as useGetForm
participant GF as getForm
participant API as API μλν¬μΈνΈ
Hook->>GF: νΌ id λ° type μμ²
GF->>API: HTTP GET μμ² (/api/form/{id}?type={type})
API-->>GF: μλ΅ λ°ν (data)
GF-->>Hook: λ°μ΄ν° λ°ν
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
β¨ Finishing Touches
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? πͺ§ TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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.
Actionable comments posted: 8
π Outside diff range comments (2)
src/app/api/application/field/[expo_id]/route.ts (1)
Line range hint
1-35
: μ½λ μ€λ³΅ μ κ±° νμμ΄ νμΌμ μ½λκ° λ€λ₯Έ API λΌμ°νΈ νμΌλ€κ³Ό κ±°μ λμΌν©λλ€. κ³΅ν΅ λ‘μ§μ λ³λμ λ―Έλ€μ¨μ΄λ μ νΈλ¦¬ν° ν¨μλ‘ μΆμΆνλ κ²μ κΆμ₯ν©λλ€.
λ€μκ³Ό κ°μ λΆλΆλ€μ 곡ν΅νν μ μμ΅λλ€:
- μΈμ¦ λ‘μ§
- μλ¬ μ²λ¦¬
- μλ΅ ν¬λ§·ν
μμ 리ν©ν λ§:
// src/shared/middleware/withAuth.ts export const withAuth = (handler: NextApiHandler) => async (req: NextApiRequest, res: NextApiResponse) => { const cookieStore = cookies(); const accessToken = cookieStore.get('accessToken')?.value; const config = accessToken ? { headers: { Authorization: `Bearer ${accessToken}`, }, } : {}; try { return await handler(req, res, config); } catch (error) { const axiosError = error as AxiosError<{ message: string }>; const status = axiosError.response?.status; const message = axiosError.response?.data?.message; return NextResponse.json({ error: message }, { status }); } };src/app/api/application/pre-standard/[expo_id]/route.ts (1)
Line range hint
11-19
: 보μ κ°μ μ μνμ¬ μΈμ¦ ν ν°μ΄ μλ κ²½μ°μλ μμ²μ΄ μ²λ¦¬λ©λλ€. λ°λν μ μ²κ³Ό κ°μ μ€μν μμ μ μΈμ¦λ μ¬μ©μλ§ μνν μ μλλ‘ μ ννλ κ²μ΄ μ’μ΅λλ€.
const config = accessToken ? { headers: { Authorization: `Bearer ${accessToken}`, }, } - : {}; + : null; + + if (!config) { + return NextResponse.json( + { error: 'μΈμ¦μ΄ νμν©λλ€' }, + { status: 401 } + ); + }
π§Ή Nitpick comments (11)
src/widgets/application/ui/ApplicationLayout/index.tsx (4)
20-20
: useForm μ¬μ© μ κ²μ¦ λ‘μ§ κΆμ₯
useForm<ApplicationFormValues>()
λ₯Ό μ¬μ© μ€μ΄μ§λ§,resolver
(μ:yupResolver
) λ±μ μΆκ°νμ¬ νΌ λ°μ΄ν°μ λν μ€ν€λ§ κ²μ¦μ μ μ©νλ©΄ μ ν¨μ± κ²μ¬κ° λ κ²¬κ³ ν΄μ§ μ μμ΅λλ€.
22-22
: μλ² μλ΅ μλ¬ μ²λ¦¬ λ³΄κ° μ μ
useGetForm
μΌλ‘formList
λ₯Ό κ°μ Έμ¬ λ,isLoading
μΈμλ μ€λ₯ λ°μ μμ λμνκΈ° μνisError
λ±μ μ¬μ©νλ©΄ μ¬μ©μ μλ΄μ λμμ΄ λ©λλ€.
24-24
: μλ¬ μΈμ μ±κ³΅ λ©μμ§ κ²ν
usePostApplication
μ¬μ© μ, μ€ν¨ μμλ μλ¬ μ²λ¦¬λ₯Ό νλλΌλ, μ±κ³΅ μμ μλ΄(μ±κ³΅ ν μ€νΈ λ±)κ° νμν μ μμ΅λλ€. κ³ λ €ν΄λ³΄μλ©΄ μ’κ² μ΅λλ€.
26-28
: showError ν¨μμ μ²λ¦¬ λ²μ νλ μ μ
νμ¬toast.error
λ§ νΈμΆνλλ°, μλ¬ μ ν(μλ² λ€μ΄, κΆν λ¬Έμ λ±)μ λ°λΌ λ€λ₯Έ μ‘μ μ΄λ λ‘κ·Έλ₯Ό λΆκΈ°ν μλ μμ΅λλ€. μν©λ³ λμ λ‘μ§μ νμ₯νλ©΄ μ’κ² μ΅λλ€.src/shared/types/application/type.ts (1)
1-5
: νΌ νμ μ λν μΆκ° μ μ½ μ‘°κ±΄μ΄ νμν©λλ€.
DynamicFormItem
μΈν°νμ΄μ€μjsonData
νλκ° λ¨μ string νμ μΌλ‘ μ μλμ΄ μμ΅λλ€. JSON λ°μ΄ν°μ ꡬ쑰λ₯Ό λ λͺ ννκ² μ μνλ©΄ νμ μμ μ±μ λμΌ μ μμ΅λλ€.λ€μκ³Ό κ°μ κ°μ μ μ μλ립λλ€:
export interface DynamicFormItem { title: string; formType: 'SENTENCE' | 'CHECKBOX' | 'MULTIPLE' | 'DROPDOWN'; - jsonData: string; + jsonData: { + options?: string[]; + placeholder?: string; + required?: boolean; + }; }src/widgets/application/model/usePostApplication.ts (1)
7-21
: μλ¬ μ²λ¦¬λ₯Ό κ°μ νμΈμμλ¬ μ²λ¦¬κ° λ무 λ¨μνλμ΄ μμ΅λλ€. μ¬μ©μμκ² λ ꡬ체μ μΈ μλ¬ λ©μμ§λ₯Ό μ 곡νλ©΄ μ’μ κ² κ°μ΅λλ€.
λ€μκ³Ό κ°μ΄ κ°μ ν΄λ³΄μΈμ:
return useMutation({ mutationFn: (data: FormattedApplicationData) => postApplication(params, type, data), onSuccess: () => { toast.success('λ°λν μ μ²μ΄ μλ£λμμ΅λλ€.'); router.push('/'); }, - onError: () => { - toast.error('λ°λν μ μ²μ μ€ν¨νμ΅λλ€.'); - }, + onError: (error: any) => { + const errorMessage = error?.response?.data?.message || 'λ°λν μ μ²μ μ€ν¨νμ΅λλ€.'; + toast.error(errorMessage); + }, });src/app/api/form/[expo_id]/route.ts (1)
35-54
: GET λ©μλ ꡬνμ λν νΌλλ°±GET λ©μλ ꡬνμ΄ μ λ°μ μΌλ‘ μ λμ΄μμΌλ, λͺ κ°μ§ κ°μ μ΄ νμν©λλ€:
- νμ νλΌλ―Έν°κ° μλ κ²½μ°μ λν μ²λ¦¬κ° νμν©λλ€.
- μλ¬ μλ΅μ κΈ°λ³Έ λ©μμ§λ₯Ό μΆκ°νλ©΄ μ’μ κ² κ°μ΅λλ€.
λ€μκ³Ό κ°μ΄ μμ νλ κ²μ μ μν©λλ€:
export async function GET( request: NextRequest, { params }: { params: { expo_id: string } }, ) { const { expo_id } = params; const { searchParams } = new URL(request.url); const type = searchParams.get('type'); try { const response = await apiClient.get(`/form/${expo_id}`, { params: { type }, }); return NextResponse.json(response.data); } catch (error) { const axiosError = error as AxiosError<{ message: string }>; const status = axiosError.response?.status || 500; - const message = axiosError.response?.data?.message; + const message = axiosError.response?.data?.message || 'νΌμ λΆλ¬μ€λλ° μ€ν¨νμ΅λλ€'; return NextResponse.json({ error: message }, { status }); } }src/entities/application/ui/SentenceOption/index.tsx (2)
5-11
: Props μΈν°νμ΄μ€μ λν νΌλλ°±Props μΈν°νμ΄μ€κ° μ μ μλμ΄ μμΌλ, λͺ κ°μ§ κ°μ μ¬νμ΄ μμ΅λλ€:
- maxLengthμ rowμ λν κΈ°λ³Έκ° μ€μ μ΄ νμν©λλ€.
- Props μΈν°νμ΄μ€μ μ€λͺ μ μΈ JSDoc μ£Όμμ μΆκ°νλ©΄ μ’μ κ² κ°μ΅λλ€.
λ€μκ³Ό κ°μ΄ μμ νλ κ²μ μ μν©λλ€:
+/** + * SentenceOption μ»΄ν¬λνΈμ Props μΈν°νμ΄μ€ + * @property maxLength - ν μ€νΈ μμμ μ΅λ κΈΈμ΄ + * @property row - ν μ€νΈ μμμ κΈ°λ³Έ ν μ + * @property required - νμ μ λ ₯ μ¬λΆ + * @property register - react-hook-formμ register ν¨μ + * @property name - νΌ νλ μ΄λ¦ + */ interface Props { maxLength: number; row: number; required: boolean; register: UseFormRegister<ApplicationFormValues>; name: string; }
24-34
: handleChange ν¨μ κ°μ μ μhandleChange ν¨μμ ꡬνμ΄ μ λμ΄ μμΌλ, μ±λ₯ μ΅μ νλ₯Ό μν κ°μ μ΄ νμν©λλ€.
λ€μκ³Ό κ°μ΄ μμ νλ κ²μ μ μν©λλ€:
- const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { + const handleChange = React.useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => { if (textareaRef.current) { textareaRef.current.style.height = 'auto'; textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`; } setHasInput(e.target.value.length > 0); if (onChange) { onChange(e); } - }; + }, [onChange]);src/entities/application/ui/OptionContainer/index.tsx (2)
26-54
: switch λ¬Έ 리ν©ν λ§ μ μswitch λ¬Έμ μ¬μ©ν ꡬνμ΄ λ³΅μ‘νκ³ νμ₯μ±μ΄ μ νμ μ λλ€.
λ€μκ³Ό κ°μ΄ κ°μ²΄ 맀νμ μ¬μ©νμ¬ λ¦¬ν©ν λ§νλ κ²μ μ μν©λλ€:
- let inputComponent; - switch (formType) { - case 'SENTENCE': - inputComponent = ( - <SentenceOption - register={register} - name={title} - maxLength={1000} - row={1} - required={false} - /> - ); - break; - case 'CHECKBOX': - inputComponent = ( - <CheckBoxOption options={options} register={register} name={title} /> - ); - break; - case 'MULTIPLE': - inputComponent = ( - <MultipleOption options={options} register={register} name={title} /> - ); - break; - case 'DROPDOWN': - inputComponent = ( - <DropDownOption options={options} register={register} name={title} /> - ); - break; - } + const componentMap = { + SENTENCE: () => ( + <SentenceOption + register={register} + name={title} + maxLength={1000} + row={1} + required={false} + /> + ), + CHECKBOX: () => ( + <CheckBoxOption options={options} register={register} name={title} /> + ), + MULTIPLE: () => ( + <MultipleOption options={options} register={register} name={title} /> + ), + DROPDOWN: () => ( + <DropDownOption options={options} register={register} name={title} /> + ), + }; + + const inputComponent = componentMap[formType as keyof typeof componentMap]?.();
56-61
: 컨ν μ΄λ μ€νμΌλ§ κ°μ μ μborder-1 ν΄λμ€κ° Tailwind CSSμμ μ§μλμ§ μμ μ μμ΅λλ€.
λ€μκ³Ό κ°μ΄ μμ νλ κ²μ μ μν©λλ€:
- <div className="flex flex-col gap-[20px] rounded-sm border-1 border-solid border-gray-200 p-[18px]"> + <div className="flex flex-col gap-5 rounded-sm border border-solid border-gray-200 p-[18px]"> <p className="text-h4 text-black">{title}</p> <div className="space-y-2">{inputComponent}</div> </div>
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (27)
src/app/(pages)/application/[id]/[type]/page.tsx
(1 hunks)src/app/api/application/[expo_id]/route.ts
(1 hunks)src/app/api/application/field/[expo_id]/route.ts
(1 hunks)src/app/api/application/field/standard/[expo_id]/route.ts
(1 hunks)src/app/api/application/pre-standard/[expo_id]/route.ts
(1 hunks)src/app/api/form/[expo_id]/route.ts
(3 hunks)src/entities/application/index.tsx
(0 hunks)src/entities/application/ui/CheckBoxOption/index.tsx
(1 hunks)src/entities/application/ui/DropDownOption/index.tsx
(1 hunks)src/entities/application/ui/Input/index.tsx
(0 hunks)src/entities/application/ui/MultipleOption/index.tsx
(1 hunks)src/entities/application/ui/OptionContainer/index.tsx
(1 hunks)src/entities/application/ui/RadioGroup/index.tsx
(0 hunks)src/entities/application/ui/SentenceOption/index.tsx
(1 hunks)src/entities/application/ui/TrainingRadioGroup/index.tsx
(0 hunks)src/shared/types/application/type.ts
(1 hunks)src/views/application/ui/application/index.tsx
(1 hunks)src/widgets/application/api/formApi.ts
(0 hunks)src/widgets/application/api/getForm.ts
(1 hunks)src/widgets/application/api/postApplication.ts
(1 hunks)src/widgets/application/model/applicationFormHandler.ts
(0 hunks)src/widgets/application/model/useGetForm.ts
(1 hunks)src/widgets/application/model/usePostApplication.ts
(1 hunks)src/widgets/application/types/type.ts
(0 hunks)src/widgets/application/ui/ApplicationLayout/index.tsx
(1 hunks)src/widgets/application/ui/form/ApplicationForm/standard/index.tsx
(0 hunks)src/widgets/application/ui/form/ApplicationForm/trainee/index.tsx
(0 hunks)
π€ Files with no reviewable changes (9)
- src/entities/application/index.tsx
- src/widgets/application/types/type.ts
- src/entities/application/ui/Input/index.tsx
- src/entities/application/ui/RadioGroup/index.tsx
- src/widgets/application/ui/form/ApplicationForm/trainee/index.tsx
- src/widgets/application/api/formApi.ts
- src/widgets/application/ui/form/ApplicationForm/standard/index.tsx
- src/widgets/application/model/applicationFormHandler.ts
- src/entities/application/ui/TrainingRadioGroup/index.tsx
π Additional comments (11)
src/widgets/application/ui/ApplicationLayout/index.tsx (3)
3-11
: Import λ¬Έ νμΈ μλ£
react-hook-form, react-toastify λ± νμν λͺ¨λλ€μ΄ μ μμ μΌλ‘ importλμ΄ μμ΅λλ€. νμ¬λ‘μλ μμ‘΄μ± μΆ©λλ μκ³ λ¨μ μΉμΈ κ°λ₯ν©λλ€.
17-17
: νλΌλ―Έν° νμ λ³κ²½ νμΈ
params
λ₯Ό numberμμ stringμΌλ‘ λ³κ²½ν κ²μ λμ λΌμ°ν μ²λ¦¬ λ°©μκ³Όμ μΌκ΄μ± μΈ‘λ©΄μμ μ μ ν΄ λ³΄μ λλ€. λ€λ§, λ€λ₯Έ κ³³μμparams
λ₯Ό μ«μλ‘ κΈ°λνκ³ μμ§ μμμ§ νμΈμ΄ νμν©λλ€.
65-108
: TRAINEE μ‘°κ±΄λΆ λ λλ§ λ‘μ§ μ¬νμΈ
type === 'TRAINEE'
쑰건μ λ°λΌ 'μ°μμ μμ΄λ' κ΄λ ¨ νλλ₯Ό 보μ¬μ£Όλ λ‘μ§μ΄ μ μ ν λμν΄ λ³΄μ λλ€. λ€λ§, λ€λ₯Έ νμ΄μ§λ λ‘μ§μμtype
μ΄ λ³ν λ λ¬Έμ κ° μκΈ°μ§ μλμ§ μ¬λΆλ₯Ό νλ² λ κ²μ¦ν΄ 보μκΈΈ κΆμ₯λ립λλ€.src/app/(pages)/application/[id]/[type]/page.tsx (1)
4-4
: νλΌλ―Έν° νμ λ³κ²½ μ£Όμ
params.id
κ° stringμΌλ‘ λ³κ²½λμΌλ―λ‘, μ΄ κ°μ μλ²λ λ€λ₯Έ μ»΄ν¬λνΈμμ numberλ‘λ§ μΈμνλ κ²½μ° νμ λΆμΌμΉκ° λ°μν μ μμ΅λλ€. κΈ°μ‘΄ λ‘μ§κ³Ό μΆ©λμ΄ μλμ§ νμΈν΄λ³΄λ κ²μ΄ μ’κ² μ΅λλ€.src/widgets/application/model/useGetForm.ts (1)
6-11
: νλ₯ν ꡬνμ λλ€!
useQuery
λ₯Ό μ¬μ©ν λ°μ΄ν° νμΉ κ΅¬νμ΄ κΉλνλ©°, 쿼리 ν€κ° μ μ νκ² κ΅¬μ±λμ΄ μμ΅λλ€. νμ μμ μ±λ μ 보μ₯λμ΄ μμ΅λλ€.src/views/application/ui/application/index.tsx (1)
5-5
: νμ λ³κ²½μ΄ μ μ ν μ΄λ£¨μ΄μ‘μ΅λλ€!
params
μ νμ μnumber
μμstring
μΌλ‘ λ³κ²½ν κ²μ΄ μ μ ν©λλ€. μ»΄ν¬λνΈμ ꡬ쑰λ κΉλνκ³ λͺ¨λ°μΌ λ°μνλ μ ꡬνλμ΄ μμ΅λλ€.src/shared/types/application/type.ts (1)
17-23
: κ°μΈμ 보 νλμ λν μ ν¨μ± κ²μ¦μ΄ νμν©λλ€.
FormattedApplicationData
νμ μphoneNumber
μ κ°μ κ°μΈμ 보 νλμ λν νμ μ μ½μ΄ μμ΅λλ€.λ€μκ³Ό κ°μ μ κ·μ ν¨ν΄μ μ¬μ©νμ¬ μ νλ²νΈ νμμ κ²μ¦νλ κ²μ μΆμ²λ립λλ€:
export type PhoneNumber = string & { __brand: 'PhoneNumber'; }; export function validatePhoneNumber(phone: string): PhoneNumber { const pattern = /^01[0-9]-?[0-9]{4}-?[0-9]{4}$/; if (!pattern.test(phone)) { throw new Error('μ ν¨νμ§ μμ μ νλ²νΈ νμμ λλ€'); } return phone as PhoneNumber; } export type FormattedApplicationData = { trainingId?: string; name: string; phoneNumber: PhoneNumber; personalInformationStatus: boolean; informationJson: string; };src/app/api/application/[expo_id]/route.ts (2)
27-27
: μλ΅μ μν μ½λ ν¬ν¨ - μ’μ κ°μ μ¬νHTTP μν μ½λλ₯Ό μλ΅μ ν¬ν¨μν€λ κ²μ ν΄λΌμ΄μΈνΈ μΈ‘μμ μλ΅μ λ μ ννκ² μ²λ¦¬ν μ μκ² ν΄μ£Όλ μ’μ κ°μ μ¬νμ λλ€.
Line range hint
6-8
: νμ λΆμΌμΉ λ¬Έμ ν΄κ²° νμνμ΄μ§ μ»΄ν¬λνΈμμλ
expo_id
κ° λ¬Έμμ΄λ‘ μ²λ¦¬λλ λ°λ©΄, API λΌμ°νΈμμλ μ«μλ‘ μ²λ¦¬λκ³ μμ΅λλ€. νμ μΌκ΄μ±μ μν΄ μμ μ΄ νμν©λλ€.- { params }: { params: { expo_id: number } }, + { params }: { params: { expo_id: string } },src/app/api/application/field/standard/[expo_id]/route.ts (1)
Line range hint
1-35
: ν μ€νΈ μ½λ μμ± νμAPI μλν¬μΈνΈμ λν ν μ€νΈ μ½λκ° μμ΅λλ€. λ€μκ³Ό κ°μ ν μ€νΈ μΌμ΄μ€λ€μ΄ νμν©λλ€:
- μΈμ¦λ μμ² μ²λ¦¬
- μΈμ¦λμ§ μμ μμ² μ²λ¦¬
- μλͺ»λ μ λ ₯κ° μ²λ¦¬
- μλ² μλ¬ μ²λ¦¬
ν μ€νΈ μ½λ μμλ₯Ό μμ±ν΄λ릴κΉμ?
src/app/api/form/[expo_id]/route.ts (1)
23-24
: μλ΅ μ²λ¦¬ λ°©μμ΄ κ°μ λμμ΅λλ€.NextResponse.jsonμ μ¬μ©νμ¬ μλ΅κ³Ό μν μ½λλ₯Ό ν¨κ» λ°ννλλ‘ μμ λ κ²μ΄ μ’μ΅λλ€.
const onSubmit = (data: ApplicationFormValues) => { | ||
const formattedData: { | ||
trainingId?: string; | ||
name: string; | ||
phoneNumber: string; | ||
personalInformationStatus: boolean; | ||
informationJson: string; | ||
} = { | ||
name: String(data['μ΄λ¦μ μ λ ₯νμΈμ'] || ''), | ||
phoneNumber: String(data['ν΄λν° λ²νΈλ₯Ό μ λ ₯νμΈμ'] || ''), | ||
personalInformationStatus: true, | ||
informationJson: JSON.stringify( | ||
formList?.dynamicForm?.reduce<Record<string, string>>((acc, form) => { | ||
const value = data[form.title as keyof ApplicationFormValues]; | ||
if (form.formType === 'CHECKBOX') { | ||
acc[form.title] = Array.isArray(value) | ||
? value.join(', ') | ||
: String(value || ''); | ||
} else { | ||
acc[form.title] = String(value || ''); | ||
} | ||
return acc; | ||
}, {}), | ||
), | ||
}; | ||
|
||
if (type === 'TRAINEE') { | ||
formattedData.trainingId = String( | ||
data['μ°μμ μμ΄λλ₯Ό μ λ ₯νμΈμ'] || '', | ||
); | ||
} | ||
|
||
PostApplication(formattedData); | ||
}; |
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
νλμ½λ©λ νκΈ νλλͺ
κ΄λ¦¬ λ°©μ
onSubmit λ‘μ§μμ 'μ΄λ¦μ μ
λ ₯νμΈμ'
, 'ν΄λν° λ²νΈλ₯Ό μ
λ ₯νμΈμ'
λ± νκΈ λ¬Έμμ΄μ΄ μ§μ μ¬μ©λκ³ μμ΅λλ€. νλλͺ
μ΄ λ³κ²½λλ©΄ μ 체 μ½λ μμ μ΄ νμνλ―λ‘, μμλ enumμΌλ‘ κ΄λ¦¬νλ λ°©μμ΄ μ μ§λ³΄μμ μ 리ν©λλ€.
μμ μ½λλ₯Ό μ°Έκ³ ν΄ μ£ΌμΈμ:
const onSubmit = (data: ApplicationFormValues) => {
+ const FIELD_NAME_NAME = 'μ΄λ¦μ μ
λ ₯νμΈμ';
+ const FIELD_NAME_PHONE = 'ν΄λν° λ²νΈλ₯Ό μ
λ ₯νμΈμ';
const formattedData = {
- name: String(data['μ΄λ¦μ μ
λ ₯νμΈμ'] || ''),
- phoneNumber: String(data['ν΄λν° λ²νΈλ₯Ό μ
λ ₯νμΈμ'] || ''),
+ name: String(data[FIELD_NAME_NAME] || ''),
+ phoneNumber: String(data[FIELD_NAME_PHONE] || ''),
...
};
...
};
π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const onSubmit = (data: ApplicationFormValues) => { | |
const formattedData: { | |
trainingId?: string; | |
name: string; | |
phoneNumber: string; | |
personalInformationStatus: boolean; | |
informationJson: string; | |
} = { | |
name: String(data['μ΄λ¦μ μ λ ₯νμΈμ'] || ''), | |
phoneNumber: String(data['ν΄λν° λ²νΈλ₯Ό μ λ ₯νμΈμ'] || ''), | |
personalInformationStatus: true, | |
informationJson: JSON.stringify( | |
formList?.dynamicForm?.reduce<Record<string, string>>((acc, form) => { | |
const value = data[form.title as keyof ApplicationFormValues]; | |
if (form.formType === 'CHECKBOX') { | |
acc[form.title] = Array.isArray(value) | |
? value.join(', ') | |
: String(value || ''); | |
} else { | |
acc[form.title] = String(value || ''); | |
} | |
return acc; | |
}, {}), | |
), | |
}; | |
if (type === 'TRAINEE') { | |
formattedData.trainingId = String( | |
data['μ°μμ μμ΄λλ₯Ό μ λ ₯νμΈμ'] || '', | |
); | |
} | |
PostApplication(formattedData); | |
}; | |
const onSubmit = (data: ApplicationFormValues) => { | |
const FIELD_NAME_NAME = 'μ΄λ¦μ μ λ ₯νμΈμ'; | |
const FIELD_NAME_PHONE = 'ν΄λν° λ²νΈλ₯Ό μ λ ₯νμΈμ'; | |
const formattedData: { | |
trainingId?: string; | |
name: string; | |
phoneNumber: string; | |
personalInformationStatus: boolean; | |
informationJson: string; | |
} = { | |
name: String(data[FIELD_NAME_NAME] || ''), | |
phoneNumber: String(data[FIELD_NAME_PHONE] || ''), | |
personalInformationStatus: true, | |
informationJson: JSON.stringify( | |
formList?.dynamicForm?.reduce<Record<string, string>>((acc, form) => { | |
const value = data[form.title as keyof ApplicationFormValues]; | |
if (form.formType === 'CHECKBOX') { | |
acc[form.title] = Array.isArray(value) | |
? value.join(', ') | |
: String(value || ''); | |
} else { | |
acc[form.title] = String(value || ''); | |
} | |
return acc; | |
}, {}), | |
), | |
}; | |
if (type === 'TRAINEE') { | |
formattedData.trainingId = String( | |
data['μ°μμ μμ΄λλ₯Ό μ λ ₯νμΈμ'] || '', | |
); | |
} | |
PostApplication(formattedData); | |
}; |
import axios from 'axios'; | ||
|
||
export const getForm = async (id: string, type: string) => { | ||
const response = await axios.get(`/api/form/${id}?type=${type}`); | ||
return response.data; | ||
}; |
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
μλ¬ μ²λ¦¬λ₯Ό μν try-catch ꡬ문 κ³ λ €
λΉλκΈ° ν΅μ μ axios
μμ²μ΄ μ€ν¨νλ©΄ μλ¬λ₯Ό λ°νν ν
λ°, μ΄λ λ¨μν ν¨μ λ°μΌλ‘ μλ¬κ° μ νλ κ°λ₯μ±μ΄ μμ΅λλ€. νμνλ€λ©΄ λ΄λΆμ try-catch
ꡬ문μ μΆκ°νκ±°λ, axios
μΈν°μ
ν°λ₯Ό μ¬μ©νμ¬ κ³΅ν΅ μλ¬ μ²λ¦¬λ₯Ό μ μ©ν μ μμ΅λλ€.
μμ:
export const getForm = async (id: string, type: string) => {
- const response = await axios.get(`/api/form/${id}?type=${type}`);
- return response.data;
+ try {
+ const response = await axios.get(`/api/form/${id}?type=${type}`);
+ return response.data;
+ } catch (error) {
+ // μλ¬ μ²λ¦¬λ₯Ό μν λ‘μ§
+ throw error;
+ }
};
π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
import axios from 'axios'; | |
export const getForm = async (id: string, type: string) => { | |
const response = await axios.get(`/api/form/${id}?type=${type}`); | |
return response.data; | |
}; | |
import axios from 'axios'; | |
export const getForm = async (id: string, type: string) => { | |
try { | |
const response = await axios.get(`/api/form/${id}?type=${type}`); | |
return response.data; | |
} catch (error) { | |
// μλ¬ μ²λ¦¬λ₯Ό μν λ‘μ§ | |
throw error; | |
} | |
}; |
export const postApplication = async ( | ||
params: string, | ||
type: string, | ||
data: FormattedApplicationData, | ||
) => { | ||
const urlMap: Record<string, string> = { | ||
STANDARD: `/api/application/pre-standard/${params}`, | ||
DEFAULT: `/api/application/${params}`, | ||
}; | ||
|
||
const url = urlMap[type] || urlMap.DEFAULT; | ||
const response = await axios.post(url, data); | ||
return response.data; | ||
}; |
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
μλ¬ μ²λ¦¬μ URL ꡬμ±μ κ°μ ν΄ μ£ΌμΈμ.
λ€μ μ¬νλ€μ κ³ λ €ν΄ μ£ΌμκΈ° λ°λλλ€:
- API νΈμΆ μ€ν¨ μμ μλ¬ μ²λ¦¬κ° λλ½λμ΄ μμ΅λλ€.
- URL 맀νμ΄ νλμ½λ©λμ΄ μμ΄ μ μ§λ³΄μκ° μ΄λ €μΈ μ μμ΅λλ€.
λ€μκ³Ό κ°μ΄ κ°μ νλ κ²μ μ μλ립λλ€:
export const postApplication = async (
params: string,
type: string,
data: FormattedApplicationData,
) => {
const urlMap: Record<string, string> = {
STANDARD: `/api/application/pre-standard/${params}`,
DEFAULT: `/api/application/${params}`,
};
const url = urlMap[type] || urlMap.DEFAULT;
- const response = await axios.post(url, data);
- return response.data;
+ try {
+ const response = await axios.post(url, data);
+ return response.data;
+ } catch (error) {
+ if (axios.isAxiosError(error)) {
+ throw new Error(`μ μ²μ μ μΆ μ€ μ€λ₯κ° λ°μνμ΅λλ€: ${error.message}`);
+ }
+ throw error;
+ }
};
λν URL ꡬμ±μ μν μμλ₯Ό λ³λ νμΌλ‘ λΆλ¦¬νλ κ²μ κ³ λ €ν΄ λ³΄μΈμ.
π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export const postApplication = async ( | |
params: string, | |
type: string, | |
data: FormattedApplicationData, | |
) => { | |
const urlMap: Record<string, string> = { | |
STANDARD: `/api/application/pre-standard/${params}`, | |
DEFAULT: `/api/application/${params}`, | |
}; | |
const url = urlMap[type] || urlMap.DEFAULT; | |
const response = await axios.post(url, data); | |
return response.data; | |
}; | |
export const postApplication = async ( | |
params: string, | |
type: string, | |
data: FormattedApplicationData, | |
) => { | |
const urlMap: Record<string, string> = { | |
STANDARD: `/api/application/pre-standard/${params}`, | |
DEFAULT: `/api/application/${params}`, | |
}; | |
const url = urlMap[type] || urlMap.DEFAULT; | |
try { | |
const response = await axios.post(url, data); | |
return response.data; | |
} catch (error) { | |
if (axios.isAxiosError(error)) { | |
throw new Error(`μ μ²μ μ μΆ μ€ μ€λ₯κ° λ°μνμ΅λλ€: ${error.message}`); | |
} | |
throw error; | |
} | |
}; |
const DropDownOption = ({ options, register, name }: Props) => { | ||
return ( | ||
<select className="rounded border px-2 py-1" {...register(name)}> | ||
<option value="">μ ννμΈμ</option> | ||
{options.map((option) => ( | ||
<option key={option.value} value={option.label}> | ||
{option.label} | ||
</option> | ||
))} | ||
</select> | ||
); | ||
}; |
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
μ κ·Όμ±κ³Ό μ ν¨μ± κ²μ¬λ₯Ό κ°μ νμΈμ
νμ¬ κ΅¬νμλ λ€μκ³Ό κ°μ κ°μ μ΄ νμν©λλ€:
- νμ μ ν μ¬λΆ κ²μ¦μ΄ μμ΅λλ€
- μ κ·Όμ± μμ±μ΄ λλ½λμμ΅λλ€
λ€μκ³Ό κ°μ΄ κ°μ ν΄λ³΄μΈμ:
const DropDownOption = ({ options, register, name }: Props) => {
return (
<select
- className="rounded border px-2 py-1"
+ className="rounded border px-2 py-1"
+ aria-label={name}
+ required
{...register(name, { required: "μ΄ νλͺ©μ νμμ
λλ€" })}
>
<option value="">μ ννμΈμ</option>
{options.map((option) => (
<option key={option.value} value={option.label}>
{option.label}
</option>
))}
</select>
);
};
π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const DropDownOption = ({ options, register, name }: Props) => { | |
return ( | |
<select className="rounded border px-2 py-1" {...register(name)}> | |
<option value="">μ ννμΈμ</option> | |
{options.map((option) => ( | |
<option key={option.value} value={option.label}> | |
{option.label} | |
</option> | |
))} | |
</select> | |
); | |
}; | |
const DropDownOption = ({ options, register, name }: Props) => { | |
return ( | |
<select | |
className="rounded border px-2 py-1" | |
aria-label={name} | |
required | |
{...register(name, { required: "μ΄ νλͺ©μ νμμ λλ€" })} | |
> | |
<option value="">μ ννμΈμ</option> | |
{options.map((option) => ( | |
<option key={option.value} value={option.label}> | |
{option.label} | |
</option> | |
))} | |
</select> | |
); | |
}; |
const CheckBoxOption = ({ options, register, name }: Props) => { | ||
return ( | ||
<div> | ||
{options.map((option) => ( | ||
<div key={option.value} className="mb-2 flex items-center"> | ||
<input | ||
type="checkbox" | ||
value={option.label} | ||
className="h-4 w-4 accent-blue-500" | ||
{...register(name)} | ||
/> | ||
<label className="ml-2 text-sm">{option.label}</label> | ||
</div> | ||
))} | ||
</div> | ||
); | ||
}; |
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
체ν¬λ°μ€ κ·Έλ£Ήμ μ κ·Όμ±μ κ°μ νμΈμ
체ν¬λ°μ€ κ·Έλ£Ήμ λν μ μ ν HTML ꡬ쑰μ ARIA μμ±μ΄ λλ½λμμ΅λλ€.
λ€μκ³Ό κ°μ΄ κ°μ ν΄λ³΄μΈμ:
const CheckBoxOption = ({ options, register, name }: Props) => {
return (
- <div>
+ <fieldset>
+ <legend className="sr-only">{name}</legend>
+ <div role="group" aria-labelledby={`${name}-group`}>
{options.map((option) => (
<div key={option.value} className="mb-2 flex items-center">
<input
type="checkbox"
value={option.label}
className="h-4 w-4 accent-blue-500"
+ id={`${name}-${option.value}`}
{...register(name)}
/>
- <label className="ml-2 text-sm">{option.label}</label>
+ <label htmlFor={`${name}-${option.value}`} className="ml-2 text-sm">
+ {option.label}
+ </label>
</div>
))}
+ </div>
+ </fieldset>
- </div>
);
};
π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const CheckBoxOption = ({ options, register, name }: Props) => { | |
return ( | |
<div> | |
{options.map((option) => ( | |
<div key={option.value} className="mb-2 flex items-center"> | |
<input | |
type="checkbox" | |
value={option.label} | |
className="h-4 w-4 accent-blue-500" | |
{...register(name)} | |
/> | |
<label className="ml-2 text-sm">{option.label}</label> | |
</div> | |
))} | |
</div> | |
); | |
}; | |
const CheckBoxOption = ({ options, register, name }: Props) => { | |
return ( | |
<fieldset> | |
<legend className="sr-only">{name}</legend> | |
<div role="group" aria-labelledby={`${name}-group`}> | |
{options.map((option) => ( | |
<div key={option.value} className="mb-2 flex items-center"> | |
<input | |
type="checkbox" | |
value={option.label} | |
className="h-4 w-4 accent-blue-500" | |
{...register(name)} | |
id={`${name}-${option.value}`} | |
/> | |
<label htmlFor={`${name}-${option.value}`} className="ml-2 text-sm"> | |
{option.label} | |
</label> | |
</div> | |
))} | |
</div> | |
</fieldset> | |
); | |
}; |
const MultipleOption = ({ options, register, name }: Props) => { | ||
return ( | ||
<div> | ||
{options.map((option) => ( | ||
<div key={option.value} className="mb-2 flex items-center"> | ||
<input | ||
type="radio" | ||
value={option.label} | ||
className="h-4 w-4 accent-blue-500" | ||
{...register(name)} | ||
/> | ||
<label className="ml-2 text-sm">{option.label}</label> | ||
</div> | ||
))} | ||
</div> | ||
); | ||
}; |
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
λΌλμ€ λ²νΌ κ·Έλ£Ήμ μ κ·Όμ±μ κ°μ νμΈμ
λΌλμ€ λ²νΌ κ·Έλ£Ήμ λν μ μ ν HTML ꡬ쑰μ ARIA μμ±μ΄ λλ½λμμ΅λλ€.
λ€μκ³Ό κ°μ΄ κ°μ ν΄λ³΄μΈμ:
const MultipleOption = ({ options, register, name }: Props) => {
return (
- <div>
+ <fieldset>
+ <legend className="sr-only">{name}</legend>
+ <div role="radiogroup" aria-labelledby={`${name}-group`}>
{options.map((option) => (
<div key={option.value} className="mb-2 flex items-center">
<input
type="radio"
value={option.label}
className="h-4 w-4 accent-blue-500"
+ id={`${name}-${option.value}`}
{...register(name)}
/>
- <label className="ml-2 text-sm">{option.label}</label>
+ <label htmlFor={`${name}-${option.value}`} className="ml-2 text-sm">
+ {option.label}
+ </label>
</div>
))}
+ </div>
+ </fieldset>
- </div>
);
};
Committable suggestion skipped: line range outside the PR's diff.
<textarea | ||
ref={(el) => { | ||
textareaRef.current = el; | ||
ref(el); | ||
}} | ||
className="w-full resize-none overflow-hidden bg-transparent text-body4 text-black placeholder-transparent" | ||
rows={row} | ||
maxLength={maxLength} | ||
onChange={handleChange} | ||
{...rest} | ||
/> |
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
ν μ€νΈ μμ μ κ·Όμ± κ°μ νμ
ν μ€νΈ μμμ μ κ·Όμ±μ κ°μ νκΈ° μν μμ±λ€μ΄ λλ½λμ΄ μμ΅λλ€.
λ€μκ³Ό κ°μ΄ μμ νλ κ²μ μ μν©λλ€:
<textarea
ref={(el) => {
textareaRef.current = el;
ref(el);
}}
className="w-full resize-none overflow-hidden bg-transparent text-body4 text-black placeholder-transparent"
rows={row}
maxLength={maxLength}
onChange={handleChange}
+ aria-label={name}
+ aria-required={required}
{...rest}
/>
π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
<textarea | |
ref={(el) => { | |
textareaRef.current = el; | |
ref(el); | |
}} | |
className="w-full resize-none overflow-hidden bg-transparent text-body4 text-black placeholder-transparent" | |
rows={row} | |
maxLength={maxLength} | |
onChange={handleChange} | |
{...rest} | |
/> | |
<textarea | |
ref={(el) => { | |
textareaRef.current = el; | |
ref(el); | |
}} | |
className="w-full resize-none overflow-hidden bg-transparent text-body4 text-black placeholder-transparent" | |
rows={row} | |
maxLength={maxLength} | |
onChange={handleChange} | |
aria-label={name} | |
aria-required={required} | |
{...rest} | |
/> |
const options = jsonData | ||
? Object.entries(JSON.parse(jsonData)).map(([key, value]) => ({ | ||
value: key, | ||
label: value as string, | ||
})) | ||
: []; |
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.
JSON νμ± λ‘μ§ μμ μ± κ°μ νμ
JSON νμ± κ³Όμ μμ λ°μν μ μλ μ€λ₯μ λν μ²λ¦¬κ° λλ½λμ΄ μμ΅λλ€.
λ€μκ³Ό κ°μ΄ μμ νλ κ²μ μ μν©λλ€:
- const options = jsonData
- ? Object.entries(JSON.parse(jsonData)).map(([key, value]) => ({
- value: key,
- label: value as string,
- }))
- : [];
+ const options = useMemo(() => {
+ if (!jsonData) return [];
+ try {
+ const parsed = JSON.parse(jsonData);
+ return Object.entries(parsed).map(([key, value]) => ({
+ value: key,
+ label: typeof value === 'string' ? value : String(value),
+ }));
+ } catch (error) {
+ console.error('JSON νμ± μ€λ₯:', error);
+ return [];
+ }
+ }, [jsonData]);
π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const options = jsonData | |
? Object.entries(JSON.parse(jsonData)).map(([key, value]) => ({ | |
value: key, | |
label: value as string, | |
})) | |
: []; | |
const options = useMemo(() => { | |
if (!jsonData) return []; | |
try { | |
const parsed = JSON.parse(jsonData); | |
return Object.entries(parsed).map(([key, value]) => ({ | |
value: key, | |
label: typeof value === 'string' ? value : String(value), | |
})); | |
} catch (error) { | |
console.error('JSON νμ± μ€λ₯:', error); | |
return []; | |
} | |
}, [jsonData]); |
π‘ λ°°κ²½ λ° κ°μ
λ°λν νΌ μ μ² νκΈ° μ μ
π μμ λ΄μ©
πΈ κΈ°ν
Summary by CodeRabbit