-
Notifications
You must be signed in to change notification settings - Fork 3
Feat/110/create crew api 연결 #124
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 23 commits
b32a804
d0be986
91249ed
e857a5d
2d9bb31
b206203
613be1a
7d062fb
8fef8ea
e739a1a
901ea86
74b19b6
d999f45
031c7ca
824ba7f
6e86fa5
99719e9
1666eb9
2425584
7a03e8d
a8db729
ef8fc4f
6d9c094
1bc4a75
69e8afc
2cc00f1
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,28 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { fetchApi } from '@/src/utils/api'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { CreateCrewRequestTypes, CreateCrewResponseTypes } from '@/src/types/create-crew'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function createCrew(data: CreateCrewRequestTypes) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response: { data: CreateCrewResponseTypes; status: number } = await fetchApi( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `/api/crews`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| method: 'POST', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| headers: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Content-Type': 'application/json', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| credentials: 'include', // Include authentication credentials | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body: JSON.stringify(data), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!response.data) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error('Failed to create crew: No data received'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return response.data; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // eslint-disable-next-line no-console | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error(error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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. 에러 처리 로직 개선 필요 현재 구현에는 다음과 같은 문제점들이 있습니다:
다음과 같이 수정을 제안드립니다: -export async function createCrew(data: CreateCrewRequestTypes) {
+export async function createCrew(data: CreateCrewFormTypes) {
+ // 입력값 검증
+ if (!data.title || !data.content) {
+ throw new Error('필수 입력값이 누락되었습니다.');
+ }
+
try {
const response: { data: CreateCrewResponseTypes; status: number } = await fetchApi(
`/api/crews`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include', // Include authentication credentials
body: JSON.stringify(data),
},
);
if (!response.data) {
- throw new Error('Failed to create crew: No data received');
+ throw new Error('크루 생성 응답 데이터가 없습니다.');
}
return response.data;
} catch (error) {
- // eslint-disable-next-line no-console
- console.error(error);
+ throw new Error(`크루 생성 중 오류가 발생했습니다: ${error instanceof Error ? error.message : '알 수 없는 오류'}`);
}
- return null;
}📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,42 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { fetchApi } from '@/src/utils/api'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import Toast from '@/src/components/common/toast'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { GetImageUrlResponseTypes } from '@/src/types/create-crew'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function getImageUrl( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| file: File | string | null, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'MEMBER' | 'CREW' | 'GATHERING', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+6
to
+9
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 함수 시그니처에 대한 타입 검증 개선이 필요합니다.
다음과 같이 타입 검증을 추가하는 것을 제안드립니다: +const VALID_TYPES = ['MEMBER', 'CREW', 'GATHERING'] as const;
+type ImageType = typeof VALID_TYPES[number];
+
export async function getImageUrl(
file: File | string | null,
- type: 'MEMBER' | 'CREW' | 'GATHERING',
+ type: ImageType,
) {
+ if (!VALID_TYPES.includes(type)) {
+ throw new Error(`유효하지 않은 이미지 타입입니다: ${type}`);
+ }📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const formData = new FormData(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (file instanceof File) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ALLOWED_TYPES = ['image/jpeg', 'image/png']; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (file.size > MAX_FILE_SIZE) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Toast({ message: '파일 크기는 5MB를 초과할 수 없습니다.', type: 'error' }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error('파일 크기 초과'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!ALLOWED_TYPES.includes(file.type)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Toast({ message: '지원하지 않는 파일 형식입니다.', type: 'error' }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error('지원하지 않는 파일 형식'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| formData.append('file', file); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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 상수 정의 위치 및 오류 메시지 개선이 필요합니다. 다음과 같은 개선사항을 제안드립니다: +const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
+const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'] as const;
+const FILE_TYPE_ERROR = '지원하지 않는 파일 형식입니다. (지원 형식: JPG, PNG, GIF, WebP)';
+const FILE_SIZE_ERROR = '파일 크기는 5MB를 초과할 수 없습니다.';
+
export async function getImageUrl(
file: File | string | null,
type: 'MEMBER' | 'CREW' | 'GATHERING',
) {
const formData = new FormData();
if (file instanceof File) {
try {
- const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
- const ALLOWED_TYPES = ['image/jpeg', 'image/png'];
-
if (file.size > MAX_FILE_SIZE) {
- Toast({ message: '파일 크기는 5MB를 초과할 수 없습니다.', type: 'error' });
- throw new Error('파일 크기 초과');
+ Toast({ message: FILE_SIZE_ERROR, type: 'error' });
+ throw new Error(`File size exceeds limit: ${file.size} bytes`);
}
if (!ALLOWED_TYPES.includes(file.type)) {
- Toast({ message: '지원하지 않는 파일 형식입니다.', type: 'error' });
- throw new Error('지원하지 않는 파일 형식');
+ Toast({ message: FILE_TYPE_ERROR, type: 'error' });
+ throw new Error(`Unsupported file type: ${file.type}`);
}📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response: { data: GetImageUrlResponseTypes } = await fetchApi( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `/api/images?type=${type}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| method: 'POST', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body: formData, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!response.data) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error('Failed to get image: No data received'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return response.data; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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 응답 처리 개선이 필요합니다. 응답 처리 시 더 구체적인 에러 타입과 메시지가 필요합니다. const response: { data: GetImageUrlResponseTypes } = await fetchApi(
`/api/images?type=${type}`,
{
method: 'POST',
body: formData,
},
);
- if (!response.data) {
- throw new Error('Failed to get image: No data received');
+ if (!response?.data?.imageUrl) {
+ throw new Error(
+ `이미지 업로드 실패: 서버 응답 누락 (type: ${type}, size: ${file.size})`
+ );
}
return response.data;📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // eslint-disable-next-line no-console | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error(error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,15 +1,16 @@ | ||||||
| import { Meta, StoryFn } from '@storybook/react'; | ||||||
| import { CreateCrewRequestTypes } from '@/src/types/create-crew'; | ||||||
| import CreateCrewForm, { CreateCrewFormTypes } from '.'; | ||||||
| import { CreateCrewFormTypes, CreateCrewRequestTypes } from '@/src/types/create-crew'; | ||||||
| import CreateCrewForm from '.'; | ||||||
|
|
||||||
| const initialValue: CreateCrewRequestTypes = { | ||||||
| title: '', | ||||||
| mainCategory: '', | ||||||
| subCategory: '', | ||||||
| imageUrl: null, | ||||||
| imageUrl: '', | ||||||
|
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 초기값 타입 일관성 개선 필요
다음과 같은 개선을 제안드립니다: - imageUrl: '',
+ imageUrl: null,또는 타입 정의에서 명시적으로 빈 문자열을 허용하도록 수정이 필요합니다. 📝 Committable suggestion
Suggested change
|
||||||
| mainLocation: '', | ||||||
| subLocation: '', | ||||||
| totalCount: 0, | ||||||
| introduce: '', | ||||||
| }; | ||||||
|
|
||||||
| export default { | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,22 +10,23 @@ import Button from '@/src/components/common/input/button'; | |
| import DropDown from '@/src/components/common/input/drop-down'; | ||
| import FileInputWrap from '@/src/components/common/input/file-input-wrap'; | ||
| import TextInput from '@/src/components/common/input/text-input'; | ||
| import { CreateCrewRequestTypes } from '@/src/types/create-crew'; | ||
| import ImgCrewSamples from '@/public/assets/images/crew-sample'; | ||
| import Textarea from '@/src/components/common/input/textarea'; | ||
| import { CreateCrewFormTypes } from '@/src/types/create-crew'; | ||
| import ImgCrewSampleUrls from '@/public/assets/images/crew-sample'; | ||
|
|
||
| export interface CreateCrewFormTypes { | ||
| data: CreateCrewRequestTypes; | ||
| export interface CreateCrewFormProps { | ||
| data: CreateCrewFormTypes; | ||
| isEdit?: boolean; | ||
| onEdit?: (data: CreateCrewRequestTypes) => void; | ||
| onSubmit?: (data: CreateCrewRequestTypes) => void; | ||
| onEdit?: (data: CreateCrewFormTypes) => void; | ||
| onSubmit?: (data: CreateCrewFormTypes) => void; | ||
| } | ||
|
|
||
| export default function CreateCrewForm({ | ||
| isEdit = false, | ||
| onEdit = () => {}, | ||
| onSubmit = () => {}, | ||
| data, | ||
| }: CreateCrewFormTypes) { | ||
| }: CreateCrewFormProps) { | ||
| const router = useRouter(); | ||
| const { | ||
| control, | ||
|
|
@@ -34,7 +35,7 @@ export default function CreateCrewForm({ | |
| trigger, | ||
| clearErrors, | ||
| formState: { errors, isValid, isSubmitting }, | ||
| } = useForm<CreateCrewRequestTypes>({ | ||
| } = useForm<CreateCrewFormTypes>({ | ||
| defaultValues: data, | ||
| mode: 'onBlur', | ||
| }); | ||
|
|
@@ -43,24 +44,24 @@ export default function CreateCrewForm({ | |
| const [regionIndex, setRegionIndex] = useState(0); | ||
|
|
||
| const title = useWatch({ control, name: 'title' }); | ||
| // mainCategory와 mainLocation 값의 변화를 감지하여 인덱스를 설정 | ||
| const mainCategory = useWatch({ control, name: 'mainCategory' }); | ||
| const mainLocation = useWatch({ control, name: 'mainLocation' }); | ||
| const introduce = useWatch({ control, name: 'introduce' }); | ||
|
|
||
| const handleMainCategoryChange = (newValue: string | null) => { | ||
| setValue('mainCategory' as const, newValue as CreateCrewRequestTypes['mainCategory']); | ||
| setValue('subCategory' as const, null as CreateCrewRequestTypes['subCategory']); | ||
| setValue('mainCategory', newValue || ''); | ||
| setValue('subCategory', null); | ||
|
Comment on lines
+52
to
+53
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 카테고리와 지역 선택 로직의 개선이 필요합니다. 현재 구현에서 몇 가지 개선이 필요한 부분이 있습니다:
다음과 같은 개선을 제안드립니다: const handleMainCategoryChange = (newValue: string | null) => {
- setValue('mainCategory', newValue || '');
+ setValue('mainCategory', newValue);
setValue('subCategory', null);
clearErrors('subCategory');
};
const handleMainLocationChange = (newValue: string | null) => {
- setValue('mainLocation', newValue || '');
+ setValue('mainLocation', newValue);
setValue('subLocation', null);
clearErrors('subLocation');
};
useEffect(() => {
- setCategoryIndex(categoryData.findIndex((category) => category.title.label === mainCategory));
- setRegionIndex(regionData.findIndex((region) => region.main.label === mainLocation));
+ const newCategoryIndex = categoryData.findIndex((category) => category.title.label === mainCategory);
+ const newRegionIndex = regionData.findIndex((region) => region.main.label === mainLocation);
+ setCategoryIndex(newCategoryIndex !== -1 ? newCategoryIndex : 0);
+ setRegionIndex(newRegionIndex !== -1 ? newRegionIndex : 0);
}, [mainCategory, mainLocation]);Also applies to: 59-60, 64-65 |
||
| clearErrors('subCategory'); | ||
| }; | ||
|
|
||
| const handleMainLocationChange = (newValue: string | null) => { | ||
| setValue('mainLocation' as const, newValue as CreateCrewRequestTypes['mainLocation']); | ||
| setValue('subLocation' as const, null as CreateCrewRequestTypes['subLocation']); | ||
| setValue('mainLocation', newValue || ''); | ||
| setValue('subLocation', null); | ||
| clearErrors('subLocation'); | ||
| }; | ||
| useEffect(() => { | ||
| setCategoryIndex(categoryData.findIndex((category) => category.title.value === mainCategory)); | ||
| setRegionIndex(regionData.findIndex((region) => region.main.value === mainLocation)); | ||
| setCategoryIndex(categoryData.findIndex((category) => category.title.label === mainCategory)); | ||
| setRegionIndex(regionData.findIndex((region) => region.main.label === mainLocation)); | ||
| }, [mainCategory, mainLocation]); | ||
|
|
||
| return ( | ||
|
|
@@ -162,24 +163,24 @@ export default function CreateCrewForm({ | |
| required: '이미지를 선택해주세요.', | ||
| validate: { | ||
| fileSize: (file) => | ||
| file && file instanceof File && file.size <= 5242880 | ||
| ? true | ||
| : '파일 크기는 5MB 이하여야 합니다.', | ||
| file && file instanceof File | ||
| ? file.size <= 5242880 || '파일 크기는 5MB 이하여야 합니다.' | ||
| : true, // 문자열인 경우 크기 검사를 건너뜁니다. | ||
| fileType: (file) => | ||
| file && | ||
| file instanceof File && | ||
| ['image/jpeg', 'image/jpg', 'image/png'].includes(file.type) | ||
| ? true | ||
| : 'JPG, PNG 파일만 업로드 가능합니다.', | ||
| file && file instanceof File | ||
| ? ['image/jpeg', 'image/jpg', 'image/png'].includes(file.type) || | ||
| 'JPG, PNG 파일만 업로드 가능합니다.' | ||
| : true, // 문자열인 경우 파일 타입 검사를 건너뜁니다. | ||
|
Comment on lines
+166
to
+173
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 validateFile = (file: File | string) => {
// 문자열(기존 URL)인 경우 검증 통과
if (typeof file === 'string') return true;
// 파일인 경우 상세 검증 수행
if (file instanceof File) {
// 파일 확장자 검증
const extension = file.name.split('.').pop()?.toLowerCase();
const validExtensions = ['jpg', 'jpeg', 'png'];
if (!extension || !validExtensions.includes(extension)) {
return 'JPG, PNG 파일만 업로드 가능합니다.';
}
// 파일 크기 검증
if (file.size > 5 * 1024 * 1024) {
return '파일 크기는 5MB 이하여야 합니다.';
}
// MIME 타입 검증
if (!['image/jpeg', 'image/jpg', 'image/png'].includes(file.type)) {
return '올바른 이미지 형식이 아닙니다.';
}
return true;
}
return '올바르지 않은 파일입니다.';
}; |
||
| }, | ||
| }} | ||
| render={({ field }) => ( | ||
| <FileInputWrap | ||
| {...field} | ||
| sample={ImgCrewSamples} | ||
| isEdit={isEdit} | ||
| sample={ImgCrewSampleUrls} | ||
| onChange={(newValue) => { | ||
| field.onChange(newValue); | ||
| if (newValue instanceof File) trigger('imageUrl'); | ||
| trigger('imageUrl'); | ||
| }} | ||
| /> | ||
| )} | ||
|
|
@@ -264,7 +265,28 @@ export default function CreateCrewForm({ | |
| )} | ||
| /> | ||
| </div> | ||
|
|
||
| <div className="flex flex-col gap-3"> | ||
| <div className="flex justify-between"> | ||
| <label htmlFor="gathering-introduce" className="text-base font-semibold text-gray-800"> | ||
| 크루 소개 | ||
| </label> | ||
| <span> | ||
| <span className="text-blue-500">{introduce.length}</span>/100 | ||
| </span> | ||
| </div> | ||
| <Controller | ||
| name="introduce" | ||
| control={control} | ||
| render={({ field }) => ( | ||
| <Textarea | ||
| {...field} | ||
| placeholder="크루 소개글을 100자 이내로 입력해주세요." | ||
| maxLength={100} | ||
| inputClassNames="h-40 py-2.5 px-4 bg-gray-100 placeholder:text-gray-400 font-pretendard text-base font-medium rounded-xl" | ||
| /> | ||
| )} | ||
| /> | ||
| </div> | ||
| <div className="flex justify-between gap-4 pt-18"> | ||
| <Button | ||
| type="submit" | ||
|
|
||
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
함수 시그니처 업데이트 필요
타입 이름 변경에 맞춰 함수 파라미터 타입을 업데이트해야 합니다.
📝 Committable suggestion