Skip to content

(i25 152) feat/inputs of modal PR - #55

Closed
hanjiwon1108 wants to merge 57 commits into
release/v2from
(I25-152)-feat/InputsOfModal
Closed

hanjiwon1108 wants to merge 57 commits into
release/v2from
(I25-152)-feat/InputsOfModal

Conversation

@hanjiwon1108

Copy link
Copy Markdown
Contributor

💡 개요

bsmhub v2/ Inputs of Modal components 구현

📃 작업내용

추가하기 버튼,
일반 버튼(색깔과 text props),
다양한 형태의 Input type(text, date, lock, edit, search),
라벨있는 inpput(required 유무),
사진 업로드(미리보기)

🔀 변경사항

📸 스크린샷

image

@insertjenkins

insertjenkins Bot commented Aug 27, 2025

Copy link
Copy Markdown

🚀 배포 준비중

예상포트 : 4005

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements inputs and modal components for the BSMHub v2 project, adding form elements and interface components for user interactions.

  • Adds various input types with different states (text, date, lock, edit, search)
  • Implements labeled inputs with required field support
  • Creates button components and picture upload functionality with preview

Reviewed Changes

Copilot reviewed 103 out of 138 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/app/components/inputsOfModal/ Core modal input components including buttons, inputs, labels, and picture upload
src/app/components/system/text.ts Typography system using styled-components
src/app/components/layout/Header.tsx Simplified header component with navigation
tailwind.config.ts Updated color scheme and utility classes
package.json Added dependencies for styled-components and icons

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

name={name}
required={required}
readOnly={type === 'lock'}
className={`flex w-full h-[3.3125rem] py-1 ${

Copilot AI Aug 27, 2025

Copy link

Choose a reason for hiding this comment

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

This complex template literal with conditional classes should be refactored using a utility function or clsx library for better readability and maintainability.

Copilot uses AI. Check for mistakes.
Comment thread src/app/components/inputsOfModal/buttons/Buttons.tsx Outdated
Comment thread src/app/components/layout/Header.tsx Outdated
];

return (
<header className="flex-center p-white-space-margin h-9 border-b border-[light-gray-outline]">

Copilot AI Aug 27, 2025

Copy link

Choose a reason for hiding this comment

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

The Tailwind class 'border-[light-gray-outline]' should use the proper color syntax 'border-light-gray-outline' to reference the custom color defined in the config.

Suggested change
<header className="flex-center p-white-space-margin h-9 border-b border-[light-gray-outline]">
<header className="flex-center p-white-space-margin h-9 border-b border-light-gray-outline">

Copilot uses AI. Check for mistakes.
Comment thread src/app/components/inputsOfModal/pictureUpload/PictureUpload.tsx Outdated
@hanjiwon1108
hanjiwon1108 changed the base branch from develop to release/v2 August 27, 2025 23:31
@insertjenkins

insertjenkins Bot commented Aug 27, 2025

Copy link
Copy Markdown

🚀 배포 준비중

예상포트 : 4005

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: LEE JEONGHYEOK <157395300+GAMZAMANDU@users.noreply.github.com>
@insertjenkins

insertjenkins Bot commented Sep 6, 2025

Copy link
Copy Markdown

🚀 배포 준비중

예상포트 : 4005

@GAMZAMANDU GAMZAMANDU left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

피드백 반영해주세요.

import React from 'react';

interface ButtonsProps {
color?: 'black' | 'blue' | 'red' | 'green' | 'gray';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

이 색깔들은 어디에 쓰이는 건가요?

Comment on lines +15 to +19
black: 'bg-black',
blue: 'bg-blue-500',
red: 'bg-red-500',
green: 'bg-green-500',
gray: 'bg-gray-500',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

색깔들은 웬만하면 tailwind.config.ts에 정의된 클래스를 사용해주세요.

Comment on lines +28 to +31
<button
className={`flex w-full h-[3.25rem] py-2 px-[1.375rem] justify-center items-center gap-1 shrink-0 rounded-full ${bgClass} text-white text-base font-bold leading-5`}
onClick={onClick}
>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

flex, justify-center, items-center를 모두 합쳐둔 flex-center가 정의되어 있으니 확인 부탁드립니다..
또한 모든 글씨체는 tailwind.config.ts에 정의된 것을 사용해주세요.

placeholder = 'Placeholder',
value,
onChange,
name,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

name보단 label이 더 좋을 것 같습니다.

Comment on lines +49 to +51
['lock', 'edit', 'search', 'date'].includes(type) ? 'pr-10' : 'px-2.5'
} pl-2.5 items-center gap-2.5 shrink-0 rounded-md text-gray-base text-base font-normal leading-6 tracking-[0.0057rem] ${
type !== 'lock' ? 'bg-light-gray-outline' : ''

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

불필요한 속성이 다수 사용된 것 같습니다.

return (
<div className="inline-block h-[5.875rem]">
<div
className="h-full aspect-1 flex flex-col items-center justify-center rounded-md bg-light-gray-outline cursor-pointer overflow-hidden relative"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

둥근 회색깔 사각형이 자주 사용되고 있는데 이러한 부분을 레이아웃화 하셔야합니다.

Comment on lines +1 to +17
import { ChangeEvent } from 'react';

export type InputType = 'text' | 'lock' | 'date' | 'edit' | 'search';

export interface BaseInputProps {
type?: InputType;
placeholder?: string;
value?: string | number;
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
name?: string;
required?: boolean;
id?: string;
}

export interface LabelInputsProps extends BaseInputProps {
label?: string;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

공통된 타입 정의 좋습니다.

@GAMZAMANDU

Copy link
Copy Markdown
Contributor
image 켈린더 인풋도 다음과 같이 수정해주세요. 피그마에서 오류가 있었네요.

- react-hook-form v7.64.0 추가
- 폼 상태 관리 및 검증을 위한 의존성
- MultiInput 컴포넌트 사용으로 여러 필드 동시 관리
- onlyOne 옵션으로 단일/다중 입력 제어
- InputConfig 타입에 onlyOne 속성 추가
- 추가 버튼에 type='button' 추가하여 폼 제출 방지
- onInputsChange 콜백으로 외부 상태 관리 지원
- config 기반 동적 입력 생성
- FormFieldConfig: label과 InputListProvider 쌍 설정
- FormConfig: 전체 폼 설정 타입
- InputConfig import 추가
- React Hook Form 통합을 위한 타입 구조
- React Hook Form 통합으로 폼 상태 관리
- config 기반 동적 폼 필드 생성
- label과 InputListProvider 자동 페어링
- 필수 필드 자동 검증 및 에러 메시지 표시
- title prop으로 폼 제목 커스터마이징
- Buttons 컴포넌트 통합 제출 버튼
- InputListProvider.md: 컴포넌트 사용법 및 API 문서
- InputListProvider-with-ReactHookForm.md: React Hook Form 통합 가이드
- InputOfModal.md: config 기반 폼 생성 컴포넌트 문서
- 각 문서에 사용 예제, 타입 정의, 트러블슈팅 포함
- onlyOne prop 업데이트 반영
- PictureUpload 코드 간소화 (59줄 → 36줄)
- aspectRatio prop 추가로 다양한 비율 지원 (1:1, 3:4, 16:9 등)
- InputOfModal에서 picture 타입으로 PictureUpload 사용 가능
- 타입 안전성 개선: aspectRatio는 picture 타입 전용으로 제한
- ModalContext: 전역 모달 상태 관리 (Context API + useCallback)
- Modal 컴포넌트: UI 렌더링 및 배경 클릭으로 닫기
- 모달 스타일: 부드러운 애니메이션 및 backdrop blur 효과
- body 스크롤 방지 기능
- inputsOfModal 컴포넌트들을 modal 폴더로 이동
- 타입 시스템 개선 (extends 방식으로 통일)
- 사용 가이드 문서 작성
- inputsOfModal 폴더 삭제 (modal/inputsOfModal로 이동 완료)
- @utils alias 추가 (tsconfig.json)
- import 경로를 @components, @utils alias로 통일
- useInputList의 MultiInputItem 타입을 MultiInput.tsx에서 import
- layout.tsx와 page.tsx의 import 경로 수정
- write/edit/read 3가지 모드로 구분된 SkillTag 컴포넌트 추가
- 여러 스킬 태그를 관리하는 SkillTagProvider 구현
- react-input-autosize를 사용한 자동 너비 조절 인풋
- 한글 IME 입력 처리 추가
- 태그 생성/수정/삭제 기능 및 상태 관리
- 인풋 포커스 제거를 위한 CSS 유틸리티 추가
- 체크박스 인풋 컴포넌트 추가
- 모달 인풋 시스템에 체크박스 타입 지원
- inputsOfModal 폴더를 inputs로 리네이밍
- FormFieldConfig 타입을 discriminated union으로 개선
- SkillTag와 Checkbox를 InputOfModal에 통합
- Config 구조 단순화 (타입별 필요한 필드만 정의)
- import 경로 정리 및 통일
- common.css 중복 제거
- InputType에서 'lock' 타입 제거
- BaseInputPropsCommon에 readOnly prop 추가
- InputListProvider에서 isLocked → isReadOnly로 변경
- SingleInput에서 type 기반 readonly → readOnly prop으로 변경
- 일관된 readOnly 패턴으로 API 단순화
- 초기 태그 배열을 설정할 수 있는 initialTags prop 추가
- useEffect로 초기 태그 자동 설정 로직 구현
- 읽기 전용 모드에서 초기값 표시 가능
- inputsOfModal → modal/inputs로 폴더명 변경
- layout.tsx, page.tsx의 import 경로 업데이트
- 테스트 코드에 SkillTag 및 Checkbox 예제 추가
- import 경로 수정 (inputsOfModal → modal/inputs)
- 입력 잠금 → 입력 읽기 전용으로 용어 변경
- InputType에서 'lock' 제거, readOnly prop 추가
- readOnly prop 사용법 설명 추가
- React Hook Form 통합 가이드 경로 업데이트
- Discriminated Union 타입 구조 설명 추가
- InputListFieldConfig
- SkillTagFieldConfig
- CheckboxFieldConfig
- 필드 타입별 상세 설명 섹션 추가
- 각 필드 타입의 데이터 형식 명시
- 실전 예제 (프로필 작성 폼) 추가
- SkillTag, Checkbox 렌더링 로직 설명
- import 경로 업데이트
- SkillTag 컴포넌트 전용 상세 문서 작성
- write/edit/read 모드 설명
- Discriminated Union Props 구조
- 한글 IME Composition 처리 원리
- 자동 너비 조정 (react-input-autosize)
- 이벤트 버블링 방지 로직
- SkillTagProvider 사용법
- InputOfModal 통합 예제
- 실전 사용 사례 및 트러블슈팅
- 성능 최적화 팁
문제:
- defaultValues에서 모든 필드를 빈 배열([])로 초기화
- JS에서 []는 truthy 값이므로 checked={git diff src/app/components/modal/inputs/InputOfModal.tsxvalue}가 true가 됨

해결:
- Checkbox 타입은 false로, 나머지는 []로 초기화
- 삼항 연산자로 간결하게 처리
- validation 로직 간소화 (배열 타입만 검증)
- InputMode (write/edit/read) 도입으로 상태와 타입 분리
- Picture를 독립적인 필드 타입으로 추가
- Checkbox 비제어/제어 컴포넌트 모두 지원
- SkillTag autoFocus 제거로 포커스 관리 개선
- SingleInput에 icon prop 추가 (check/search/calendar)
- AutosizeInput 컴포넌트에 autoFocus prop 추가
- 태그 작성 시 자동으로 포커스 설정
- Checkbox.md 문서 생성
- Controlled/Uncontrolled 모드 설명
- React Hook Form 통합 예제
- InputOfModal에서 사용법
- 검증 로직 설명
- InputOfModal에 Checkbox required 검증 추가
- PictureUpload value 타입 any로 변경하여 호환성 개선
- onTagsChange 콜백을 useRef로 관리하여 의존성 무한 루프 방지
- InputOfModal의 Checkbox onChange 단순화
- PictureUpload value 타입 체크 명확화
- useEffect 의존성 배열 최적화
@insertjenkins

insertjenkins Bot commented Oct 8, 2025

Copy link
Copy Markdown

🚀 배포 준비중

예상포트 : 4000

>
{preview ? (
// eslint-disable-next-line @next/next/no-img-element
<img src={preview} alt="업로드된 이미지" className="object-cover w-full h-full" />

Check warning

Code scanning / CodeQL

DOM text reinterpreted as HTML Medium

DOM text
is reinterpreted as HTML without escaping meta-characters.

Copilot Autofix

AI about 1 year ago

To fix the problem, we must ensure that only safe and intended values are assigned to the src attribute of the <img> element. Specifically, when the value prop is a string (possibly attacker-supplied), we should validate that it is a safe image URL before setting it as preview. The best approach is to only allow URLs that either start with blob:, are from trusted domains, or are recognized safe protocols (e.g., https://, data:image/, etc.), and explicitly reject javascript:, data:text/html;, or other dangerous schemes.

Required changes:

  • Implement a function (e.g., isSafeImageSrc) that validates the string is a Blob URL or trusted image resource (starts with http://, https://, or data:image/).
  • In both the useEffect where preview is set from a string value, and the rendering where preview is used as img.src, use this validator to block unsafe URLs.
  • Optionally, fallback to not showing a preview if validation fails.

Add the function to the same file, and update the logic in the relevant lines.

Suggested changeset 1
src/app/components/modal/inputs/PictureUpload.tsx

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/app/components/modal/inputs/PictureUpload.tsx b/src/app/components/modal/inputs/PictureUpload.tsx
--- a/src/app/components/modal/inputs/PictureUpload.tsx
+++ b/src/app/components/modal/inputs/PictureUpload.tsx
@@ -8,6 +8,16 @@
   onFileChange?: (file: File | null) => void; // 파일 변경 콜백
 }
 
+function isSafeImageSrc(src: string): boolean {
+  // allow blob:, http(s), and data:image/* only
+  return (
+    src.startsWith('blob:') ||
+    src.startsWith('https://') ||
+    src.startsWith('http://') ||
+    src.startsWith('data:image/')
+  );
+}
+
 function PictureUpload({ aspectRatio = '1:1', value, onFileChange }: PictureUploadProps) {
   const [preview, setPreview] = useState<string>();
   const inputRef = useRef<HTMLInputElement>(null);
@@ -15,7 +25,11 @@
   // value prop이 string URL일 때만 preview 업데이트 (초기 로드용)
   useEffect(() => {
     if (typeof value === 'string') {
-      setPreview(value);
+      if (isSafeImageSrc(value)) {
+        setPreview(value);
+      } else {
+        setPreview(undefined);
+      }
     }
   }, [value]);
 
@@ -43,7 +57,7 @@
       className="overflow-hidden input-common flex-col !justify-center cursor-pointer"
       style={{ height: '5.875rem', width: getWidth(aspectRatio) }}
     >
-      {preview ? (
+      {(preview && isSafeImageSrc(preview)) ? (
         // eslint-disable-next-line @next/next/no-img-element
         <img src={preview} alt="업로드된 이미지" className="object-cover w-full h-full" />
       ) : (
EOF
@@ -8,6 +8,16 @@
onFileChange?: (file: File | null) => void; // 파일 변경 콜백
}

function isSafeImageSrc(src: string): boolean {
// allow blob:, http(s), and data:image/* only
return (
src.startsWith('blob:') ||
src.startsWith('https://') ||
src.startsWith('http://') ||
src.startsWith('data:image/')
);
}

function PictureUpload({ aspectRatio = '1:1', value, onFileChange }: PictureUploadProps) {
const [preview, setPreview] = useState<string>();
const inputRef = useRef<HTMLInputElement>(null);
@@ -15,7 +25,11 @@
// value prop이 string URL일 때만 preview 업데이트 (초기 로드용)
useEffect(() => {
if (typeof value === 'string') {
setPreview(value);
if (isSafeImageSrc(value)) {
setPreview(value);
} else {
setPreview(undefined);
}
}
}, [value]);

@@ -43,7 +57,7 @@
className="overflow-hidden input-common flex-col !justify-center cursor-pointer"
style={{ height: '5.875rem', width: getWidth(aspectRatio) }}
>
{preview ? (
{(preview && isSafeImageSrc(preview)) ? (
// eslint-disable-next-line @next/next/no-img-element
<img src={preview} alt="업로드된 이미지" className="object-cover w-full h-full" />
) : (
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
@GAMZAMANDU GAMZAMANDU closed this Oct 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants