Skip to content

Conversation

@hanjiwon1108
Copy link
Contributor

๐Ÿ’ก ๊ฐœ์š”

bsmhub v2/ Inputs of Modal components ๊ตฌํ˜„

๐Ÿ“ƒ ์ž‘์—…๋‚ด์šฉ

์ถ”๊ฐ€ํ•˜๊ธฐ ๋ฒ„ํŠผ,
์ผ๋ฐ˜ ๋ฒ„ํŠผ(์ƒ‰๊น”๊ณผ text props),
๋‹ค์–‘ํ•œ ํ˜•ํƒœ์˜ Input type(text, date, lock, edit, search),
๋ผ๋ฒจ์žˆ๋Š” inpput(required ์œ ๋ฌด),
์‚ฌ์ง„ ์—…๋กœ๋“œ(๋ฏธ๋ฆฌ๋ณด๊ธฐ)

๐Ÿ”€ ๋ณ€๊ฒฝ์‚ฌํ•ญ

๐Ÿ“ธ ์Šคํฌ๋ฆฐ์ƒท

image

@insertjenkins
Copy link

insertjenkins bot commented Aug 27, 2025

๐Ÿš€ ๋ฐฐํฌ ์ค€๋น„์ค‘

์˜ˆ์ƒํฌํŠธ : 4005

Copy link
Contributor

Copilot AI left a comment

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 ${
Copy link

Copilot AI Aug 27, 2025

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.
];

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

Copilot AI Aug 27, 2025

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.
@hanjiwon1108 hanjiwon1108 changed the base branch from develop to release/v2 August 27, 2025 23:31
@insertjenkins
Copy link

insertjenkins bot commented Aug 27, 2025

๐Ÿš€ ๋ฐฐํฌ ์ค€๋น„์ค‘

์˜ˆ์ƒํฌํŠธ : 4005

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

insertjenkins bot commented Sep 6, 2025

๐Ÿš€ ๋ฐฐํฌ ์ค€๋น„์ค‘

์˜ˆ์ƒํฌํŠธ : 4005

Copy link
Contributor

@GAMZAMANDU GAMZAMANDU left a comment

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
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
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
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
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
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
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
Contributor

Choose a reason for hiding this comment

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

๊ณตํ†ต๋œ ํƒ€์ž… ์ •์˜ ์ข‹์Šต๋‹ˆ๋‹ค.

@GAMZAMANDU
Copy link
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
Copy link

insertjenkins bot commented Oct 8, 2025

๐Ÿš€ ๋ฐฐํฌ ์ค€๋น„์ค‘

์˜ˆ์ƒํฌํŠธ : 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 3 months 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.

3 participants