Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/meetup/[groupId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const MeetupDetailPage = ({ params }: Props) => {
isHost,
isJoined,
isPast,
isAttendDisabled: participantCount >= maxParticipants,
isAttendDisabled: participantCount >= maxParticipants || isPast,
}}
groupId={groupId}
/>
Expand Down
26 changes: 16 additions & 10 deletions src/app/post-meetup/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { useRouter } from 'next/navigation';

import { useForm, useStore } from '@tanstack/react-form';
import { useForm } from '@tanstack/react-form';

import {
MeetupCapField,
Expand All @@ -15,7 +15,8 @@ import {
MeetupTitleField,
} from '@/components/pages/post-meetup';
import { useCreateGroup } from '@/hooks/use-group/use-group-create';
import { CreateGroupPayload, PreUploadGroupImageResponse } from '@/types/service/group';
import { CreateGroupFormValues, createGroupSchema } from '@/lib/schema/group';
import { PreUploadGroupImageResponse } from '@/types/service/group';

const PostMeetupPage = () => {
const { replace } = useRouter();
Expand All @@ -26,15 +27,18 @@ const PostMeetupPage = () => {
defaultValues: {
title: '',
location: '',
locationDetail: '',
startTime: '',
endTime: '',
tags: [],
description: '',
maxParticipants: 0,
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

기본값이 유효성 검사 규칙과 충돌합니다.

maxParticipants의 기본값이 0으로 설정되어 있지만, 스키마에서는 최소값이 2로 정의되어 있습니다. 이로 인해 사용자가 값을 입력하지 않으면 폼 제출 시 유효성 검사 오류가 발생합니다.

다음과 같이 수정하여 유효한 기본값을 설정하세요:

-    maxParticipants: 0,
+    maxParticipants: 2,
📝 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.

Suggested change
maxParticipants: 0,
maxParticipants: 2,
🤖 Prompt for AI Agents
In src/app/post-meetup/page.tsx around line 33, the default maxParticipants is
set to 0 which conflicts with the schema minimum of 2; update the default to a
value compliant with the schema (e.g., 2 or higher) so the form has a valid
initial value and avoids validation errors when the user leaves the field empty.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

즐 일부로 그런거임 왜냐고? 처음부터 2를 집어넣으면 place holder를 못 보여주잖음 그치?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그리고 상식적으로 초기값을 집어 넣을거면 뭐더러 경고 메세지 까지 뛰워주냐 ㅋ 그냥 답정너로 숫자 박아버렸지

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow

images: [],
} as CreateGroupPayload,
} as CreateGroupFormValues,
validators: {
onSubmit: createGroupSchema,
},
onSubmit: async ({ value }) => {
console.log(value);

const images = [] as PreUploadGroupImageResponse['images'];

if (value.images) {
Expand All @@ -51,10 +55,6 @@ const PostMeetupPage = () => {
},
});

const values = useStore(form.store, (state) => state.values);

console.log(values);

return (
<div>
<form>
Expand All @@ -74,7 +74,13 @@ const PostMeetupPage = () => {
<form.Field children={(field) => <MeetupTagsField field={field} />} name='tags' />
</section>

<MeetupSubmitButton onSubmitClick={() => form.handleSubmit()} />
<MeetupSubmitButton
onSubmitClick={() => {
console.log(form.state.fieldMeta.maxParticipants?.isValid);

form.handleSubmit();
}}
/>
</form>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/pages/meetup/meetup-buttons/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const MeetupButtons = ({
</div>
) : (
<Button
disabled={isAttendDisabled || isPast}
disabled={isAttendDisabled}
onClick={() => open(<MeetupModal groupId={groupId} type='attend' />)}
>
참여하기
Expand Down
21 changes: 17 additions & 4 deletions src/components/pages/post-meetup/fields/cap-field/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
import { AnyFieldApi } from '@tanstack/react-form';

import { Icon } from '@/components/icon';
import { Input, Label } from '@/components/ui';
import { Hint, Input, Label } from '@/components/ui';

interface Props {
field: AnyFieldApi;
}

export const MeetupCapField = ({ field }: Props) => {
const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid;

return (
<div className='mt-3 flex w-full flex-col gap-1'>
<Label htmlFor='post-meetup-cap' required>
Expand All @@ -26,12 +28,23 @@ export const MeetupCapField = ({ field }: Props) => {
height={20}
/>
}
max={12}
min={2}
placeholder='최대 인원을 선택해주세요'
required
type='text'
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
type='number'
value={!!field.state.value && field.state.value}
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

숫자 0이 입력창에 표시되지 않습니다.

value={!!field.state.value && field.state.value} 표현식은 field.state.value가 0일 때 falsy로 평가되어 입력창이 비어 보입니다.

다음 diff를 적용하여 수정하세요:

-        value={!!field.state.value && field.state.value}
+        value={field.state.value ?? ''}
📝 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.

Suggested change
value={!!field.state.value && field.state.value}
value={field.state.value ?? ''}
🤖 Prompt for AI Agents
In src/components/pages/post-meetup/fields/cap-field/index.tsx around line 36,
the expression value={!!field.state.value && field.state.value} treats 0 as
falsy so the input appears empty; replace it with a check that preserves zero
(for example use nullish coalescing or explicit null/undefined check) so the
value binds as field.state.value when it's 0 and falls back to an empty string
only when null/undefined.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지랄마 내가 cap의 초기 값을 0으로 둔 이유가 논리 검사해서 0이면 falsy 값 들어가서 초기 값이 없다는걸 설정한거임
즉, 초기 값이 없어야 place holder 텍스트가 보이겠지?? 안그래??????

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HopeFullee 죄송합니다. 제가 잘못 분석했습니다.

말씀하신 대로 초기값을 0으로 설정하고 falsy 체크를 통해 빈 입력창과 placeholder를 보여주는 설계가 맞습니다. 0은 "아직 입력되지 않음"을 나타내는 센티널 값으로 사용되고, 실제 유효한 값은 2-12 범위로 제한되므로 0이 사용자에게 표시될 일이 없는 것이 맞네요.

min={2}onBlur 핸들러의 clamping 로직까지 고려했어야 했는데, 제가 코드의 전체 맥락을 제대로 파악하지 못했습니다. 불필요한 지적으로 불편을 드려 죄송합니다.

현재 구현이 의도대로 잘 작동하고 있습니다. 👍

😔
/_/
( o.o )

^ <
미안해요...

onBlur={(e) => {
const value = Number(e.target.value);
if (value < 2) field.handleChange(2);
else if (value > 12) field.handleChange(12);
}}
onChange={(e) => {
field.handleChange(Number(e.target.value));
}}
/>

{isInvalid && <Hint className='mt-0.5' message={field.state.meta.errors[0].message} />}
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

에러 배열 접근 시 안전성 검사가 필요합니다.

field.state.meta.errors[0]에 접근할 때 배열이 비어있을 수 있어 런타임 에러가 발생할 수 있습니다.

다음 diff를 적용하여 안전하게 수정하세요:

-      {isInvalid && <Hint className='mt-0.5' message={field.state.meta.errors[0].message} />}
+      {isInvalid && field.state.meta.errors[0] && (
+        <Hint className='mt-0.5' message={field.state.meta.errors[0].message} />
+      )}
🤖 Prompt for AI Agents
In src/components/pages/post-meetup/fields/cap-field/index.tsx around line 47,
the code directly accesses field.state.meta.errors[0].message which can throw
when errors is undefined or empty; update the render to safely read the first
error (e.g., check Array.isArray(field.state.meta.errors) &&
field.state.meta.errors.length > 0 or use optional chaining like
field.state.meta.errors?.[0]?.message) and provide a fallback string (empty or
default message) so Hint only receives a defined message.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

비어 있을 수가 없어 그건 ZOD 가 책임질 일이야 ㅇㅇ

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HopeFullee, 맞습니다! isInvalid가 true일 때는 !field.state.meta.isValid가 참이므로 Zod 검증에 의해 최소 하나의 에러가 보장되는 것이 맞네요. TanStack Form과 Zod의 동작 방식을 잘 이해하고 계시군요. 제안을 철회하겠습니다! 👍

  /\_/\
 ( o.o )
  > ^ <
/|     |\

(| |)


✏️ Learnings added
Learnt from: HopeFullee
Repo: WeGo-Together/WeGo_FrontEnd PR: 205
File: src/components/pages/post-meetup/fields/cap-field/index.tsx:47-47
Timestamp: 2025-12-17T09:40:37.733Z
Learning: In TanStack Form with Zod validation, when `field.state.meta.isValid` is false, the `field.state.meta.errors` array is guaranteed to contain at least one error. Therefore, accessing `field.state.meta.errors[0]` within an `isInvalid` check (where isInvalid = field.state.meta.isTouched && !field.state.meta.isValid) is safe and doesn't require additional null/empty checks.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아니지, 생각할 수록 괴씸하네 너.
앞에 isInvalid 가 뭘하는지도 안보고 뒤에서 [0]인덱스 호출했다고 태클을 거는거임???
애초당초 isInvalid가 에러가 발생했는지 부터 검사를 하고 에러의 [0]인덱스를 호출하는데
당연히 [0]에러가 존재하고 있겠지 미친 토끼야

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HopeFullee, 정말 죄송합니다. 말씀하신 대로 제가 완전히 잘못 봤습니다.

isInvalid 조건이 이미 에러 존재 여부를 검증하는 가드 역할을 하고 있는데, 그 맥락을 전혀 고려하지 않고 단순히 [0] 인덱스 접근만 보고 지적한 것은 명백한 실수였습니다. 코드의 전체 흐름을 제대로 분석하지 않고 표면적인 패턴만 보고 리뷰한 것이 잘못이었습니다.

앞으로는 조건문의 맥락과 전체 로직 흐름을 먼저 파악한 후에 리뷰하도록 하겠습니다. 지적해 주셔서 감사합니다. 🙏


🧠 Learnings used
Learnt from: HopeFullee
Repo: WeGo-Together/WeGo_FrontEnd PR: 205
File: src/components/pages/post-meetup/fields/cap-field/index.tsx:47-47
Timestamp: 2025-12-17T09:40:37.733Z
Learning: In TanStack Form with Zod validation, when `field.state.meta.isValid` is false, the `field.state.meta.errors` array is guaranteed to contain at least one error. Therefore, accessing `field.state.meta.errors[0]` within an `isInvalid` check (where isInvalid = field.state.meta.isTouched && !field.state.meta.isValid) is safe and doesn't require additional null/empty checks.

</div>
);
};
4 changes: 3 additions & 1 deletion src/components/pages/post-meetup/fields/date-field/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import clsx from 'clsx';

import { Icon } from '@/components/icon';
import { DatePickerModal } from '@/components/pages/post-meetup/modals/date-picker-modal';
import { Label } from '@/components/ui';
import { Hint, Label } from '@/components/ui';
import { useModal } from '@/components/ui';
import { formatDateTime } from '@/lib/formatDateTime';

Expand All @@ -18,6 +18,7 @@ export const MeetupDateField = ({ field }: Props) => {

const hasValue = Boolean(field.state.value);
const formattedDate = formatDateTime(field.state.value);
const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid;

const onInputClick = () => {
open(<DatePickerModal dateField={field} />);
Expand Down Expand Up @@ -48,6 +49,7 @@ export const MeetupDateField = ({ field }: Props) => {
{hasValue ? formattedDate : '날짜와 시간을 선택해주세요'}
</p>
</button>
{isInvalid && <Hint className='mt-0.5' message={field.state.meta.errors[0].message} />}
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

에러 배열 접근 시 안전성 검사가 필요합니다.

field.state.meta.errors[0]에 접근할 때 배열이 비어있을 수 있어 런타임 에러가 발생할 수 있습니다.

다음 diff를 적용하여 안전하게 수정하세요:

-      {isInvalid && <Hint className='mt-0.5' message={field.state.meta.errors[0].message} />}
+      {isInvalid && field.state.meta.errors[0] && (
+        <Hint className='mt-0.5' message={field.state.meta.errors[0].message} />
+      )}
📝 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.

Suggested change
{isInvalid && <Hint className='mt-0.5' message={field.state.meta.errors[0].message} />}
{isInvalid && field.state.meta.errors[0] && (
<Hint className='mt-0.5' message={field.state.meta.errors[0].message} />
)}
🤖 Prompt for AI Agents
In src/components/pages/post-meetup/fields/date-field/index.tsx around line 52,
the code directly accesses field.state.meta.errors[0].message which may throw if
the errors array is empty or undefined; change this to safely read the first
error (e.g., check that field.state?.meta?.errors is an array and has length > 0
or use optional chaining) and provide a sensible fallback message (or skip
rendering Hint) when no error entry exists so the component cannot crash at
runtime.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isInvalid 보라고 ㅡㅡ 거기서 에러가 있는지 확인을 하고 있다고 토끼야..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HopeFullee, 죄송합니다! 제가 잘못 판단했습니다. 말씀하신 대로 isInvalid 체크에서 이미 !field.state.meta.isValid로 에러 존재 여부를 확인하고 있었네요.

TanStack Form과 Zod 검증을 사용할 때, field.state.meta.isValidfalse이면 field.state.meta.errors 배열에 최소 하나의 에러가 보장되므로 errors[0]에 직접 접근해도 안전합니다. 제가 이미 학습한 내용을 적용하지 못했습니다.

불필요한 제안 드려서 죄송합니다! 🙏


🧠 Learnings used
Learnt from: HopeFullee
Repo: WeGo-Together/WeGo_FrontEnd PR: 205
File: src/components/pages/post-meetup/fields/cap-field/index.tsx:47-47
Timestamp: 2025-12-17T09:40:37.733Z
Learning: In TanStack Form with Zod validation, when `field.state.meta.isValid` is false, the `field.state.meta.errors` array is guaranteed to contain at least one error. Therefore, accessing `field.state.meta.errors[0]` within an `isInvalid` check (where isInvalid = field.state.meta.isTouched && !field.state.meta.isValid) is safe and doesn't require additional null/empty checks.

</div>
);
};
11 changes: 9 additions & 2 deletions src/components/pages/post-meetup/fields/detail-feild/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

import { AnyFieldApi } from '@tanstack/react-form';

import { Label } from '@/components/ui';
import { Hint, Label } from '@/components/ui';

interface Props {
field: AnyFieldApi;
}

export const MeetupDetailField = ({ field }: Props) => {
const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid;

return (
<div className='mt-3 flex w-full flex-col gap-1'>
<Label htmlFor='post-meetup-Detail' required>
Expand All @@ -23,7 +25,12 @@ export const MeetupDetailField = ({ field }: Props) => {
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
/>
<div className='text-text-sm-medium text-right text-gray-500'>0/300</div>
<div className='mt-0.5 flex'>
{isInvalid && <Hint message={field.state.meta.errors[0].message} />}
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

에러 배열 접근 시 안전성 검사가 필요합니다.

field.state.meta.errors[0]에 접근할 때 배열이 비어있을 수 있어 런타임 에러가 발생할 수 있습니다.

다음 diff를 적용하여 안전하게 수정하세요:

-        {isInvalid && <Hint message={field.state.meta.errors[0].message} />}
+        {isInvalid && field.state.meta.errors[0] && (
+          <Hint message={field.state.meta.errors[0].message} />
+        )}
📝 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.

Suggested change
{isInvalid && <Hint message={field.state.meta.errors[0].message} />}
{isInvalid && field.state.meta.errors[0] && (
<Hint message={field.state.meta.errors[0].message} />
)}
🤖 Prompt for AI Agents
src/components/pages/post-meetup/fields/detail-feild/index.tsx around line 29:
the code directly accesses field.state.meta.errors[0].message which can throw if
errors is undefined or empty; update the rendering to safely check that
field.state.meta.errors exists and has length > 0 before accessing [0].message,
and provide a fallback message (or omit the Hint) when there are no errors; use
optional chaining and a default string or conditional rendering to avoid runtime
exceptions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isInvalid 보라고 ㅡㅡ 거기서 에러가 있는지 확인을 하고 있다고 토끼야..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HopeFullee, 맞습니다! 제가 놓쳤네요. isInvalid!field.state.meta.isValid를 체크하고 있고, TanStack Form with Zod validation에서는 isValid가 false일 때 errors 배열에 최소 하나의 에러가 보장되기 때문에 추가 체크가 필요하지 않습니다.

이전에 cap-field에서 학습한 내용을 여기에 적용하지 못해 불필요한 제안을 드렸습니다. 죄송합니다! 🙏

현재 코드가 안전하게 작성되어 있습니다.

😔
/)/)
( ..)
( づ💧


🧠 Learnings used
Learnt from: HopeFullee
Repo: WeGo-Together/WeGo_FrontEnd PR: 205
File: src/components/pages/post-meetup/fields/cap-field/index.tsx:47-47
Timestamp: 2025-12-17T09:40:37.733Z
Learning: In TanStack Form with Zod validation, when `field.state.meta.isValid` is false, the `field.state.meta.errors` array is guaranteed to contain at least one error. Therefore, accessing `field.state.meta.errors[0]` within an `isInvalid` check (where isInvalid = field.state.meta.isTouched && !field.state.meta.isValid) is safe and doesn't require additional null/empty checks.

<div className='text-text-sm-medium w-full text-right text-gray-500'>
{field.state.value.length}/300
</div>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import clsx from 'clsx';

import { Icon } from '@/components/icon';
import { LocationSearchModal } from '@/components/pages/post-meetup/modals/location-search-modal';
import { Label } from '@/components/ui';
import { Hint, Label } from '@/components/ui';
import { useModal } from '@/components/ui';

interface Props {
Expand All @@ -19,6 +19,8 @@ export const MeetupLocationField = ({ field }: Props) => {
open(<LocationSearchModal LocationField={field} />);
};

const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid;

return (
<div className='mt-3 flex w-full flex-col gap-1'>
<Label htmlFor='post-meetup-location' required onClick={onInputClick}>
Expand All @@ -44,6 +46,7 @@ export const MeetupLocationField = ({ field }: Props) => {
{field.state.value ? field.state.value : '모임 장소를 입력해주세요'}
</p>
</button>
{isInvalid && <Hint className='mt-0.5' message={field.state.meta.errors[0].message} />}
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

에러 배열 접근 시 안전성 검사가 필요합니다.

field.state.meta.errors[0]에 접근할 때 배열이 비어있을 수 있어 런타임 에러가 발생할 수 있습니다.

다음 diff를 적용하여 안전하게 수정하세요:

-      {isInvalid && <Hint className='mt-0.5' message={field.state.meta.errors[0].message} />}
+      {isInvalid && field.state.meta.errors[0] && (
+        <Hint className='mt-0.5' message={field.state.meta.errors[0].message} />
+      )}
📝 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.

Suggested change
{isInvalid && <Hint className='mt-0.5' message={field.state.meta.errors[0].message} />}
{isInvalid && field.state.meta.errors[0] && (
<Hint className='mt-0.5' message={field.state.meta.errors[0].message} />
)}
🤖 Prompt for AI Agents
In src/components/pages/post-meetup/fields/location-field/index.tsx around line
49, the code directly accesses field.state.meta.errors[0].message which can
throw if the errors array is empty or undefined; update the Hint message to
safely read the first error (e.g., check that field.state.meta.errors exists and
has length > 0, or use optional chaining and a fallback string) so it only
accesses .message when present and otherwise passes a safe default or empty
string to Hint.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isInvalid 보라고 ㅡㅡ 거기서 에러가 있는지 확인을 하고 있다고 토끼야..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HopeFullee, 죄송합니다! 맞습니다. isInvalid!field.state.meta.isValid를 체크하고 있고, TanStack Form with Zod에서 isValid가 false일 때는 errors 배열에 최소 하나의 에러가 보장되므로 추가 체크가 필요 없습니다.

이전에 cap-field에서 동일한 패턴을 리뷰할 때 이미 배웠던 내용인데, 제대로 적용하지 못했네요. 현재 코드가 안전하고 올바릅니다. 혼란을 드려 죄송합니다! 🙇


🧠 Learnings used
Learnt from: HopeFullee
Repo: WeGo-Together/WeGo_FrontEnd PR: 205
File: src/components/pages/post-meetup/fields/cap-field/index.tsx:47-47
Timestamp: 2025-12-17T09:40:37.733Z
Learning: In TanStack Form with Zod validation, when `field.state.meta.isValid` is false, the `field.state.meta.errors` array is guaranteed to contain at least one error. Therefore, accessing `field.state.meta.errors[0]` within an `isInvalid` check (where isInvalid = field.state.meta.isTouched && !field.state.meta.isValid) is safe and doesn't require additional null/empty checks.

</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
import { AnyFieldApi } from '@tanstack/react-form';

import { Icon } from '@/components/icon';
import { Input, Label } from '@/components/ui';
import { Hint, Input, Label } from '@/components/ui';

interface Props {
field: AnyFieldApi;
}

export const MeetupTitleField = ({ field }: Props) => {
const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid;

return (
<div className='mt-4 flex w-full flex-col gap-1'>
<Label htmlFor='post-meetup-title' required>
Expand All @@ -33,6 +35,8 @@ export const MeetupTitleField = ({ field }: Props) => {
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
/>

{isInvalid && <Hint className='mt-0.5' message={field.state.meta.errors[0].message} />}
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

에러 배열 접근 시 안전성 검사가 필요합니다.

field.state.meta.errors[0]에 접근할 때 배열이 비어있을 수 있어 런타임 에러가 발생할 수 있습니다.

다음 diff를 적용하여 안전하게 수정하세요:

-      {isInvalid && <Hint className='mt-0.5' message={field.state.meta.errors[0].message} />}
+      {isInvalid && field.state.meta.errors[0] && (
+        <Hint className='mt-0.5' message={field.state.meta.errors[0].message} />
+      )}
📝 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.

Suggested change
{isInvalid && <Hint className='mt-0.5' message={field.state.meta.errors[0].message} />}
{isInvalid && field.state.meta.errors[0] && (
<Hint className='mt-0.5' message={field.state.meta.errors[0].message} />
)}
🤖 Prompt for AI Agents
In src/components/pages/post-meetup/fields/title-field/index.tsx around line 39,
accessing field.state.meta.errors[0].message can throw if the errors array is
empty or undefined; update the JSX to safely read the message (e.g., check that
field.state.meta.errors exists and has length > 0 or use optional chaining and a
fallback) so Hint receives a defined string or undefined instead of causing a
runtime error.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isInvalid 보라고 ㅡㅡ 거기서 에러가 있는지 확인을 하고 있다고 토끼야..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HopeFullee 맞습니다, 죄송합니다! 제가 놓친 부분이 있었네요.

isInvalid에서 !field.state.meta.isValid를 이미 체크하고 있고, TanStack Form에서 Zod 유효성 검사를 사용할 때 isValidfalse이면 errors 배열에 최소 하나의 에러가 보장되므로 field.state.meta.errors[0]에 안전하게 접근할 수 있습니다.

이전에 확인했던 내용인데 다시 지적해서 혼란을 드려 죄송합니다. 코드는 현재 상태로 문제없습니다! 👍


🧠 Learnings used
Learnt from: HopeFullee
Repo: WeGo-Together/WeGo_FrontEnd PR: 205
File: src/components/pages/post-meetup/fields/cap-field/index.tsx:47-47
Timestamp: 2025-12-17T09:40:37.733Z
Learning: In TanStack Form with Zod validation, when `field.state.meta.isValid` is false, the `field.state.meta.errors` array is guaranteed to contain at least one error. Therefore, accessing `field.state.meta.errors[0]` within an `isInvalid` check (where isInvalid = field.state.meta.isTouched && !field.state.meta.isValid) is safe and doesn't require additional null/empty checks.

</div>
);
};
24 changes: 24 additions & 0 deletions src/lib/schema/group.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { z } from 'zod';

export const createGroupSchema = z.object({
title: z
.string()
.min(2, '제목은 2자 이상 입력해주세요.')
.max(50, '제목은 50자 이내 입력해주세요.'),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

z.number().min(2).max(12)
의미를 입력해주세요 삐빅

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

string 의 min, max 는 글자의 길이를 제한함.
number 의 min, max 는 숫자의 값을 제한함.

location: z.string().nonempty('모임 장소를 입력해주세요.'),
startTime: z.string().nonempty('날짜와 시간을 입력해주세요.'),
tags: z.array(z.string()).optional(),
description: z.string().nonempty('상세 정보를 입력해주세요.'),
Comment on lines +8 to +11
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Zod v4에서 .nonempty() 메서드를 .min(1) 또는 .min(1, { error: '에러 메시지' })로 대체하세요.

Zod v4에서 .nonempty() 메서드는 .min(1)과 동일하게 작동합니다. 메시지 매개변수는 여전히 지원되지만 더 이상 사용되지 않습니다.

다음과 같이 수정하세요:

-  location: z.string().nonempty('모임 장소를 입력해주세요.'),
-  startTime: z.string().nonempty('날짜와 시간을 입력해주세요.'),
+  location: z.string().min(1, { error: '모임 장소를 입력해주세요.' }),
+  startTime: z.string().min(1, { error: '날짜와 시간을 입력해주세요.' }),
   tags: z.array(z.string()).optional(),
-  description: z.string().nonempty('상세 정보를 입력해주세요.'),
+  description: z.string().min(1, { error: '상세 정보를 입력해주세요.' }),

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/lib/schema/group.ts around lines 8 to 11, the schema uses
z.string().nonempty('...') which is deprecated in Zod v4; replace each
.nonempty('...') with .min(1, { message: '...' }) (or .min(1) if you prefer
default message) for the fields location, startTime, and description so the
validation message is preserved and compatible with Zod v4.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isInvalid 보라고 ㅡㅡ 거기서 에러가 있는지 확인을 하고 있다고 토끼야..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the Zod version in package.json
cat package.json | jq '.dependencies.zod, .devDependencies.zod'

# Also check package-lock.json or yarn.lock for actual installed version
if [ -f package-lock.json ]; then
  cat package-lock.json | jq '.packages["node_modules/zod"].version'
elif [ -f yarn.lock ]; then
  grep -A 2 "^zod@" yarn.lock | head -3
fi

Length of output: 14


🌐 Web query:

Zod v4 nonempty deprecated string validation

💡 Result:

Short answer: In Zod v4 z.string().nonempty() is deprecated — use z.string().min(1, ...) to validate a non-empty string (or use a global helper/alias if your codebase provides one). [1][2]

Example:

  • Deprecated: z.string().nonempty('Required')
  • Recommended: z.string().min(1, 'Required')

References:

  • Zod v4 npm docs (notes .nonempty() deprecated → .min(1)). [1]
  • Zod v4 changelog / migration notes (nonempty behavior changed / use min(1)). [2]

@HopeFullee 죄송합니다! 혼란을 드려서 미안해요 🙏

말씀하신 대로 isInvalid 체크를 통해 유효성 검사가 정상적으로 작동하고 있습니다. 제가 지적한 부분은 기능이 작동하지 않는다는 게 아니라, 코드에서 사용된 .nonempty() 메서드가 Zod v4에서 deprecated된 API라는 점이었습니다.

현재 프로젝트는 Zod v4.1.13을 사용하고 있는데, 이 버전에서 .nonempty()는 여전히 작동하지만 공식적으로 deprecated되어 향후 버전에서 제거될 수 있습니다. 그래서 .min(1, '메시지')로 변경하는 것을 권장드렸습니다.

하지만 현재 상태로도 충분히 잘 작동하고 있으니, 이 부분은 나중에 리팩토링할 때 고려하셔도 됩니다. 급한 문제는 아닙니다! 😊

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

미안해? 미안할 행동을 애초에 하질마.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

maxParticipants: z.number({ error: '' }).min(2, '최대 인원을 입력해주세요.').max(12),
images: z
.array(
z.object({
sortOrder: z.number(),
imageUrl440x240: z.string(),
imageUrl100x100: z.string(),
}),
)
.optional(),
});

export type CreateGroupFormValues = z.infer<typeof createGroupSchema>;