-
Notifications
You must be signed in to change notification settings - Fork 1
Feat/70 체험 등록/수정페이지 로직 및 레이아웃 1차 구현 #73
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 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,79 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'use client'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import Modal from '@/components/Modal'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import DaumPostcode from 'react-daum-postcode'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import Input from '@/components/Input'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import Button from '@/components/Button'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interface AddressInputProps { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onAddressChange: (address: string) => void; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| address: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interface PostcodeData { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| address: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| addressType: 'R' | 'J'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bname: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| buildingName: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| zonecode: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| userSelectedType: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default function AddressInput({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onAddressChange, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| address, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }: AddressInputProps) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [isOpen, setIsOpen] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleComplete = (data: PostcodeData) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let fullAddress = data.address; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let extraAddress = ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (data.addressType === 'R') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (data.bname !== '') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| extraAddress += data.bname; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (data.buildingName !== '') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| extraAddress += | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| extraAddress !== '' ? `, ${data.buildingName}` : data.buildingName; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fullAddress += extraAddress !== '' ? ` (${extraAddress})` : ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onAddressChange(fullAddress); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setIsOpen(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+29
to
+46
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. 🧹 Nitpick (assertive)
매 렌더마다 새로운 함수가 생성되어 - const handleComplete = (data: PostcodeData) => {
+ const handleComplete = useCallback((data: PostcodeData) => {
…
- };
+ }, [onAddressChange]);불필요한 재생성을 줄여 컴포넌트 트리 성능을 개선할 수 있습니다. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Input | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| label='주소' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id='address' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value={address} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => setIsOpen(true)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| readOnly | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+51
to
+56
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. 🧹 Nitpick (assertive)
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Modal isOpen={isOpen} onOpenChange={setIsOpen}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Modal.Content> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Modal.Header> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Modal.Title>주소 검색</Modal.Title> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Modal.Close /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Modal.Header> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Modal.Item> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <DaumPostcode onComplete={handleComplete} /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Modal.Item> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Modal.Footer> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| variant='primary' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className='py-8' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => setIsOpen(false)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 닫기 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Modal.Footer> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Modal.Content> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Modal> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,37 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||
| interface CategoryProps { | ||||||||||||||||||||||||||||||||||||||||||||||||
| category?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| onCategoryChange: (value: string) => void; | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| export default function CategoryInput({ | ||||||||||||||||||||||||||||||||||||||||||||||||
| category, | ||||||||||||||||||||||||||||||||||||||||||||||||
| onCategoryChange, | ||||||||||||||||||||||||||||||||||||||||||||||||
| }: CategoryProps) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||
| <div> | ||||||||||||||||||||||||||||||||||||||||||||||||
| <label | ||||||||||||||||||||||||||||||||||||||||||||||||
| htmlFor='category' | ||||||||||||||||||||||||||||||||||||||||||||||||
| className='font-regular flex flex-col text-lg text-black' | ||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||
| 카테고리 | ||||||||||||||||||||||||||||||||||||||||||||||||
| </label> | ||||||||||||||||||||||||||||||||||||||||||||||||
| <div> | ||||||||||||||||||||||||||||||||||||||||||||||||
| <select | ||||||||||||||||||||||||||||||||||||||||||||||||
| id='category' | ||||||||||||||||||||||||||||||||||||||||||||||||
| className='w-full rounded-md border border-gray-800 bg-white px-20 py-17 placeholder-gray-600' | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
| <label | |
| htmlFor='category' | |
| className='font-regular flex flex-col text-lg text-black' | |
| > | |
| 카테고리 | |
| </label> | |
| <div> | |
| <select | |
| id='category' | |
| className='w-full rounded-md border border-gray-800 bg-white px-20 py-17 placeholder-gray-600' | |
| <label | |
| className='font-regular flex flex-col text-lg text-black' | |
| htmlFor='category' | |
| > | |
| 카테고리 | |
| </label> | |
| <div> | |
| <select | |
| className='w-full rounded-md border border-gray-800 bg-white px-20 py-17 placeholder-gray-600' | |
| id='category' | |
| value={category} | |
| onChange={(e) => onCategoryChange(e.target.value)} | |
| > |
🧰 Tools
🪛 GitHub Actions: CI
[warning] 15-22: react/jsx-sort-props: Props should be sorted alphabetically.
🤖 Prompt for AI Agents
In src/app/(with-header)/myactivity/components/CategoryInput.tsx around lines 13
to 22, fix the props sorting by arranging the JSX attributes in a consistent
order, typically starting with id, then className, followed by other props.
Also, review the option elements inside the select to ensure that the option
values and their displayed text are consistent and correctly matched. Adjust the
code to apply these changes for better readability and correctness.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| import type React from 'react'; | ||
|
|
||
| interface FormSectionProps { | ||
| title: string; | ||
| children: React.ReactNode; | ||
| description?: string; | ||
| } | ||
|
|
||
| export function FormSection({ | ||
| title, | ||
| children, | ||
| description, | ||
| }: FormSectionProps) { | ||
| return ( | ||
| <div className='space-y-6'> | ||
| <div> | ||
| <h2 className='border-b border-gray-200 pb-2 text-xl font-semibold text-gray-900'> | ||
| {title} | ||
| </h2> | ||
| {description && ( | ||
| <p className='mt-2 text-sm text-gray-600'>{description}</p> | ||
| )} | ||
| </div> | ||
| {children} | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,39 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'use client'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import IconClose from '@assets/svg/close'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interface ImagePreviewProps { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| image: File | string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onRemove: () => void; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alt: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export function ImagePreview({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| image, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onRemove, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alt, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className = '', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }: ImagePreviewProps) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const src = typeof image === 'string' ? image : URL.createObjectURL(image); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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 메모리 누수 방지를 위한 URL cleanup 필요
다음과 같이 useEffect를 사용하여 cleanup을 구현하세요: 'use client';
+import { useEffect } from 'react';
import IconClose from '@assets/svg/close';
// ... in component
export function ImagePreview({
image,
onRemove,
alt,
className = '',
}: ImagePreviewProps) {
const src = typeof image === 'string' ? image : URL.createObjectURL(image);
+ useEffect(() => {
+ return () => {
+ if (typeof image !== 'string') {
+ URL.revokeObjectURL(src);
+ }
+ };
+ }, [src, image]);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className={`group relative ${className}`}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className='aspect-square w-full overflow-hidden rounded-lg'> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <img | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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. 요 부분은 Image 태그말고 img를 쓰신 이유가 있으신가요?
Contributor
Author
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. 리팩토링 과정에서 변경하도록하겠습니다! |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| src={src || '/placeholder.svg'} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className='h-full w-full object-cover' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alt={alt} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+23
to
+27
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. 🧹 Nitpick (assertive) Next.js Image 컴포넌트 사용 권장 파이프라인에서 지적한 대로 +import Image from 'next/image';
- <img
+ <Image
src={src || '/placeholder.svg'}
className='h-full w-full object-cover'
alt={alt}
+ fill
+ sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
- />
+ />🧰 Tools🪛 GitHub Actions: CI[warning] 23-23: @next/next/no-img-element: Using 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type='button' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={onRemove} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className='absolute top-2 right-2 rounded-full bg-gray-600 p-1 text-white transition-colors hover:bg-gray-700' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| aria-label='이미지 삭제' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <IconClose /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| 'use client'; | ||
|
|
||
| import { MainImageSelect } from './MainImageSelect'; | ||
| import { SubImageSelect } from './SubImageSelect'; | ||
|
|
||
| interface ImagesSectionProps { | ||
| mainImage: string | File | null; | ||
| subImage: (string | File)[]; | ||
| onMainImageSelect: (file: File) => void; | ||
| onMainImageRemove: () => void; | ||
| onSubImageAdd: (files: File[]) => void; | ||
| onSubImageRemove: (index: number) => void; | ||
| } | ||
|
Comment on lines
+6
to
+13
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. 🧹 Nitpick (assertive) 타입 이름과 컴포넌트 이름 불일치 -interface ImagesSectionProps {
+interface ImageSectionProps {
...
-}: ImagesSectionProps) {
+}: ImageSectionProps) {
🤖 Prompt for AI Agents |
||
|
|
||
| export function ImageSection({ | ||
| mainImage, | ||
| subImage, | ||
| onMainImageSelect, | ||
| onMainImageRemove, | ||
| onSubImageAdd, | ||
| onSubImageRemove, | ||
| }: ImagesSectionProps) { | ||
| return ( | ||
| <div className='space-y-8'> | ||
| <MainImageSelect | ||
| mainImage={mainImage} | ||
| onImageSelect={onMainImageSelect} | ||
| onImageRemove={onMainImageRemove} | ||
| /> | ||
|
|
||
| <SubImageSelect | ||
| subImage={subImage} | ||
| onImagesAdd={onSubImageAdd} | ||
| onImageRemove={onSubImageRemove} | ||
| /> | ||
| </div> | ||
|
Comment on lines
+24
to
+36
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. 🧹 Nitpick (assertive) ESLint 경고: prop 정렬 🧰 Tools🪛 GitHub Actions: CI[warning] 28-34: react/jsx-sort-props: Props should be sorted alphabetically. 🤖 Prompt for AI Agents |
||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| 'use client'; | ||
|
|
||
| import type React from 'react'; | ||
|
|
||
| interface ImageUploadProps { | ||
| onImageSelect: (file: File) => void; | ||
| multiple?: boolean; | ||
| className?: string; | ||
| children?: React.ReactNode; | ||
| } | ||
|
|
||
| export function ImageUpload({ | ||
| onImageSelect, | ||
| multiple = false, | ||
| className = '', | ||
| children, | ||
| }: ImageUploadProps) { | ||
| const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
| const file = e.target.files?.[0]; | ||
| if (file) { | ||
| onImageSelect(file); | ||
| } | ||
| }; | ||
|
Comment on lines
+18
to
+23
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 multiple prop과 파일 처리 로직 불일치
다음 중 하나를 선택하여 수정하세요: 옵션 1: multiple 지원하지 않음 (현재 사용 패턴에 맞음) interface ImageUploadProps {
onImageSelect: (file: File) => void;
- multiple?: boolean;
className?: string;
children?: React.ReactNode;
}
export function ImageUpload({
onImageSelect,
- multiple = false,
className = '',
children,
}: ImageUploadProps) {옵션 2: multiple 완전 지원 - onImageSelect: (file: File) => void;
+ onImageSelect: (files: File[]) => void;
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
- const file = e.target.files?.[0];
- if (file) {
- onImageSelect(file);
- }
+ const files = Array.from(e.target.files || []);
+ if (files.length > 0) {
+ onImageSelect(multiple ? files : [files[0]]);
+ }
};
🤖 Prompt for AI Agents |
||
|
|
||
| return ( | ||
| <label className={`group cursor-pointer ${className}`}> | ||
| <div className='flex aspect-square w-full flex-col items-center justify-center rounded-lg border-2 border-dashed border-gray-300 transition-colors hover:border-green-400 hover:bg-green-50'> | ||
| {children || ( | ||
| <> | ||
| <h2 className='text-xl text-gray-600 group-hover:text-green-600'> | ||
| + | ||
| </h2> | ||
| <span className='text-center text-xs font-medium text-gray-600 group-hover:text-green-600'> | ||
| 이미지 | ||
| <br /> | ||
| 등록 | ||
| </span> | ||
| </> | ||
| )} | ||
| </div> | ||
| <input | ||
| type='file' | ||
| accept='image/*' | ||
| multiple={multiple} | ||
| className='hidden' | ||
| onChange={handleFileChange} | ||
| /> | ||
| </label> | ||
| ); | ||
| } | ||
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.
🧹 Nitpick (assertive)
userSelectedType타입을 구체화하세요userSelectedType역시'R' | 'J'로 고정된 값만 들어오므로string대신 동일한 유니온 타입을 부여하면 타입 안정성이 한층 올라갑니다.📝 Committable suggestion
🤖 Prompt for AI Agents