๐Ÿ”จ Vue `defineProps`์—์„œ ๋„˜๊ธด prop์ด `props`๊ฐ€ ์•„๋‹ˆ๋ผ `$attrs`๋กœ ๋น ์ง„ ๋ฌธ์ œ

ZYE KIMยท2026๋…„ 5์›” 11์ผ

Problem Solving

๋ชฉ๋ก ๋ณด๊ธฐ
7/7

ํŒŒ์ผ ์—…๋กœ๋“œ ์ปดํฌ๋„ŒํŠธ์— target ๊ฐ’์„ ๋„˜๊ฒจ์„œ ์—…๋กœ๋“œ API ์š”์ฒญ ์‹œ targetType์œผ๋กœ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ–ˆ๋‹ค.
๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ file-input ์ปดํฌ๋„ŒํŠธ์— target prop์„ ์ถ”๊ฐ€๋กœ ๋„˜๊ธฐ๊ณ  ์žˆ์—ˆ๋‹ค.

<file-input
  v-model="salesOrderDetail.fileList"
  :mode="'edit'"
  :target="'ORDER'"
/>

๊ณตํ†ต ์ปดํฌ๋„ŒํŠธ์ธ file-input.vue์—์„œ๋Š” props๋ฅผ ์ด๋ ‡๊ฒŒ ์„ ์–ธํ–ˆ๋‹ค.

const props = withDefaults(defineProps<IFileUploadProps>(), {
  module: "",
  fileCategory: "",
  target: "",
  mode: "edit",
  modelValue: () => [],
  clearable: false,
  multiple: true,
  accept: ".ppt, .pdf, .hwp, .docx, .xls, .xlsx, .png, .jpg, .jpeg",
  maxSize: 10,
  error: false,
  errorMessage: "",
  hintMessage: "",
  placeholder: "ํŒŒ์ผ์ฒจ๋ถ€"
});

๊ทธ๋ฆฌ๊ณ  ์—…๋กœ๋“œ ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์—ˆ๋‹ค.

await apiClient.file<IFileResponse>("/files/upload", {
  moduleCode: props.module,
  fileType: "FILE",
  targetType: props.target ?? null,
  file
});

๊ทธ๋Ÿฐ๋ฐ ์ด์ƒํ•˜๊ฒŒ๋„ props.target ๊ฐ’์ด ๋ณด์ด์ง€ ์•Š์•˜๋‹ค.

โ“ ์ฒ˜์Œ์—๋Š” ๋ถ€๋ชจ์—์„œ ๊ฐ’์„ ์ž˜๋ชป ๋„˜๊ธด ์ค„ ์•Œ์•˜๋‹ค.
๊ทธ๋ž˜์„œ ํ…Œ์ŠคํŠธ์šฉ์œผ๋กœ fileCategory๋„ ์ถ”๊ฐ€ํ•ด์„œ ๋„˜๊ฒจ๋ดค๋‹ค. โ“

<file-input
  v-model="salesOrderDetail.fileList"
  :mode="'view'"
  :target="'ORDER'"
  :fileCategory="'abc'"
/>

ํ•˜์ง€๋งŒ ์—ฌ์ „ํžˆ ์›ํ•˜๋Š” ๊ฐ’์ด props์—์„œ ํ™•์ธ๋˜์ง€ ์•Š์•˜๋‹ค.


์›์ธ ์ฐพ๊ธฐ

์ฝ˜์†”๋กœ ์•„๋ฌด๋ฆฌ ์ฐ์–ด๋ด๋„ ๋ณด์ด์ง€ ์•Š๊ธธ๋ž˜ ์ผ๋‹จ ์ƒ์œ„์— v-bind="attrs"์„ ์–ธํ•ด๋†“์•„์„œํ˜น์‹œ...๋ชฐ๋ผโ€˜propsโ€˜์™€โ€˜attrs"์„ ์–ธํ•ด ๋†“์•„์„œ ํ˜น์‹œ... ๋ชฐ๋ผ`props`์™€ `attrs`๋ฅผ ๊ฐ๊ฐ ํ™•์ธํ–ˆ๋‹ค.

(์–ธ์ œ๋‚˜ ํ•ญ์ƒ ํ˜น์‹œ๊ฐ€ ๋ฌธ์ œ์ง€..?)

const attrs = useAttrs();

console.log("props.target:", props.target);
console.log("attrs.target:", attrs.target);
console.log("attrs:", attrs);

๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์•˜๋‹ค.

props.target: undefined
attrs.target: ORDER
attrs: Proxy(Object) { target: 'ORDER' }

์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ์ ์€ ๋ถ€๋ชจ์—์„œ ๊ฐ’์ด ์•ˆ ๋„˜์–ด์˜จ ๊ฒŒ ์•„๋‹ˆ๋ผ๋Š” ๊ฒƒ์ด๋‹ค.

๋ถ€๋ชจ๊ฐ€ ๋„˜๊ธด target="ORDER"๋Š” ์‹ค์ œ๋กœ ์ž์‹๊นŒ์ง€ ๋„์ฐฉํ–ˆ๋‹ค.
๋‹ค๋งŒ Vue๊ฐ€ ์ด ๊ฐ’์„ props๋กœ ์ธ์‹ํ•˜์ง€ ๋ชปํ•ด์„œ $attrs๋กœ ๋„˜๊ฒจ๋ฒ„๋ฆฐ ๊ฒƒ์ด๋‹ค.

์ฆ‰ ๋ฌธ์ œ๋Š” ๊ณตํ†ต ์ปดํฌ๋„ŒํŠธ์˜ defineProps ์ฒ˜๋ฆฌ ์ชฝ์— ์žˆ์—ˆ๋‹ค.


ํƒ€์ž… ์„ ์–ธ ํ™•์ธ

์ฒ˜์Œ์—๋Š” IFileUploadProps ํƒ€์ž…์— target์ด ์—†๋Š” ์ค„ ์•Œ์•˜๋‹ค.

ํ•˜์ง€๋งŒ ํƒ€์ž… ํŒŒ์ผ์„ ํ™•์ธํ•ด๋ณด๋‹ˆ target์€ ๋ถ„๋ช…ํžˆ ์กด์žฌํ–ˆ๋‹ค.

interface IBaseUploadProps<T extends IFileResponse = IFileResponse> {
  modelValue: T[];
  mode?: "edit" | "view";
  required?: boolean;
  hint?: string;
  hintMessage?: string;
  readonly?: boolean;
  disabled?: boolean;
  persistentHint?: boolean;
  clearable?: boolean;
  multiple?: boolean;
  maxSize?: number;
  module: string;
  fileCategory?: string;
  target?: string;
}

export interface IFileUploadProps extends IBaseUploadProps<ISaveFile> {
  placeholder?: string;
  accept?: string;
  target?: string;
}

TypeScript ๊ธฐ์ค€์œผ๋กœ๋Š” ๋ฌธ์ œ๊ฐ€ ์—†์–ด ๋ณด์˜€๋‹ค.

IFileUploadProps๋Š” IBaseUploadProps<ISaveFile>๋ฅผ ์ƒ์†ํ•˜๊ณ  ์žˆ๊ณ ,
target?: string๋„ ์กด์žฌํ•œ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ๋Ÿฐํƒ€์ž„์—์„œ๋Š” props.target์ด ์•„๋‹ˆ๋ผ $attrs.target์œผ๋กœ ๋น ์ง€๊ณ  ์žˆ์—ˆ๋‹ค.


์ง„์งœ ์›์ธ

์›์ธ์€ defineProps<IFileUploadProps>()์— ๋„˜๊ธด ํƒ€์ž… ๊ตฌ์กฐ๊ฐ€ Vue SFC ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋Ÿฐํƒ€์ž„ props๋กœ ์•ˆ์ •์ ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ์—๋Š” ๋ณต์žกํ–ˆ๋˜ ๊ฒƒ์ด๋‹ค.
(๋ญ”๊ฐ€ doc์— ์žˆ์–ด๋ณด์—ฌ์„œ ์ด์šฉํ•˜๋ฉด ์™œ ์ด๋Ÿฐ๊ฒŒ ํ„ฐ์ง€๋Š”๊ฑฐ์ฃ ..?)

๋ฌธ์ œ๊ฐ€ ๋œ ๊ตฌ์กฐ๋Š” ๋‹ค์Œ ์กฐํ•ฉ์ด์—ˆ๋‹ค.

interface IBaseUploadProps<T extends IFileResponse = IFileResponse> {
  ...
  target?: string;
}

export interface IFileUploadProps extends IBaseUploadProps<ISaveFile> {
  ...
  target?: string;
}

์ฆ‰,

  • imported interface
  • extends
  • generic
  • ์ค‘๋ณต ์„ ์–ธ๋œ prop

์ด ์กฐํ•ฉ์ด ๋“ค์–ด๊ฐ€ ์žˆ์—ˆ๋‹ค.

TypeScript๋Š” ์ด ํƒ€์ž…์„ ์ •์ƒ์ ์œผ๋กœ ์ดํ•ดํ•œ๋‹ค.

ํ•˜์ง€๋งŒ Vue์˜ defineProps<T>()๋Š” ์™„์ „ํ•œ TypeScript ํƒ€์ž… ์ฒด์ปค์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค.
Vue SFC ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ํƒ€์ž… ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋Ÿฐํƒ€์ž„ prop ์˜ต์…˜์„ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š”๋ฐ, ์ด ๊ณผ์ •์€ ์ œํ•œ์ ์ธ AST ๋ถ„์„์— ๊ฐ€๊น๋‹ค.

๊ทธ๋ž˜์„œ TypeScript ์ž…์žฅ์—์„œ๋Š” target์ด ์กด์žฌํ•˜์ง€๋งŒ, Vue ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋Ÿฐํƒ€์ž„ props ๋ชฉ๋ก์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์—์„œ target์„ ๋ˆ„๋ฝํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ ๊ฒฐ๊ณผ Vue๋Š” ๋ถ€๋ชจ์—์„œ ์ „๋‹ฌ๋ฐ›์€ target์„ ์ด๋ ‡๊ฒŒ ํŒ๋‹จํ•œ๋‹ค.

target? ๋‚ด๊ฐ€ ์•„๋Š” prop ๋ชฉ๋ก์— ์—†๋„ค.
๊ทธ๋Ÿผ props๊ฐ€ ์•„๋‹ˆ๋ผ attrs๋กœ ๋„˜๊ธด๋‹ค.

๊ทธ๋ž˜์„œ ์ตœ์ข… ๊ฒฐ๊ณผ๊ฐ€ ์ด๋ ‡๊ฒŒ ๋œ ๊ฒƒ์ด๋‹ค.

props.target // undefined
attrs.target // "ORDER"

ํ•ด๊ฒฐ๋ฒ•

๊ฐ€์žฅ ํ™•์‹คํ•œ ํ•ด๊ฒฐ๋ฒ•์€ defineProps์— ์‚ฌ์šฉํ•˜๋Š” ํƒ€์ž…์„ flatํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด๋‹ค.

์ฆ‰ extends + generic ๊ตฌ์กฐ๋ฅผ ํ”ผํ•˜๊ณ , ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” props๋ฅผ ์ง์ ‘ ๋ช…์‹œํ•œ๋‹ค.

interface FileInputProps {
  modelValue: ISaveFile[];
  mode?: "edit" | "view";
  required?: boolean;
  hint?: string;
  hintMessage?: string;
  readonly?: boolean;
  disabled?: boolean;
  persistentHint?: boolean;
  clearable?: boolean;
  multiple?: boolean;
  maxSize?: number;
  module: string;
  fileCategory?: string;
  target?: string;
  placeholder?: string;
  accept?: string;
  error?: boolean;
  errorMessage?: string;
}

const props = withDefaults(defineProps<FileInputProps>(), {
  module: "",
  fileCategory: "",
  target: "",
  mode: "edit",
  modelValue: () => [],
  clearable: false,
  multiple: true,
  accept: ".ppt, .pdf, .hwp, .docx, .xls, .xlsx, .png, .jpg, .jpeg",
  maxSize: 10,
  error: false,
  errorMessage: "",
  hintMessage: "",
  placeholder: "ํŒŒ์ผ์ฒจ๋ถ€"
});

์ด๋ ‡๊ฒŒ ๋ณ€๊ฒฝํ•˜๋ฉด Vue๊ฐ€ target์„ ์ •์ƒ์ ์œผ๋กœ prop์œผ๋กœ ์ธ์‹ํ•œ๋‹ค.

์ดํ›„ API ์š”์ฒญ์—์„œ๋„ ์ •์ƒ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

await apiClient.file<IFileResponse>("/files/upload", {
  moduleCode: props.module,
  fileType: "FILE",
  targetType: props.target || null,
  fileCategory: props.fileCategory || null,
  file
});

์—ฌ๊ธฐ์„œ ?? null๋ณด๋‹ค || null์„ ์‚ฌ์šฉํ•œ ์ด์œ ๋Š” ๊ธฐ๋ณธ๊ฐ’์ด ๋นˆ ๋ฌธ์ž์—ด์ผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

props.target ?? null

์ด๋ ‡๊ฒŒ ์“ฐ๋ฉด props.target์ด ""์ผ ๋•Œ ๊ทธ๋Œ€๋กœ ๋นˆ ๋ฌธ์ž์—ด์ด ๋“ค์–ด๊ฐ„๋‹ค.

๋ฐ˜๋ฉด,

props.target || null

์ด๋ ‡๊ฒŒ ์“ฐ๋ฉด ๋นˆ ๋ฌธ์ž์—ด์ผ ๊ฒฝ์šฐ null๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.


๋‹ค๋ฅธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์—๋Š”...์ด๋Ÿฐ๊ฒŒ ์žˆ๋Œ€์š”

๊ณตํ†ต ํƒ€์ž…์„ ๊ผญ ์žฌ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด generic์„ ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ๋„ ๋ฐฉ๋ฒ•์ด๋‹ค.

export interface IBaseUploadProps {
  modelValue: ISaveFile[];
  mode?: "edit" | "view";
  required?: boolean;
  hint?: string;
  hintMessage?: string;
  readonly?: boolean;
  disabled?: boolean;
  persistentHint?: boolean;
  clearable?: boolean;
  multiple?: boolean;
  maxSize?: number;
  module: string;
  fileCategory?: string;
  target?: string;
}

export interface IFileUploadProps extends IBaseUploadProps {
  placeholder?: string;
  accept?: string;
  error?: boolean;
  errorMessage?: string;
}

๋‹ค๋งŒ ๊ฐ€์žฅ ์•ˆ์ „ํ•œ ๋ฐฉ๋ฒ•์€ defineProps์— ๋“ค์–ด๊ฐ€๋Š” ํƒ€์ž…๋งŒํผ์€ ์ตœ๋Œ€ํ•œ ๋‹จ์ˆœํ•˜๊ณ  ๋ช…ํ™•ํ•˜๊ฒŒ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.


์ž„์‹œ๋ฐฉํŽธ

๋‹น์žฅ ๊ตฌ์กฐ๋ฅผ ๋ฐ”๊พธ๊ธฐ ์–ด๋ ต๋‹ค๋ฉด $attrs์—์„œ ๊ฐ’์„ ๊บผ๋‚ด ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

const attrs = useAttrs();

const uploadTarget = computed(() => {
  return props.target || String(attrs.target || "");
});

๊ทธ๋ฆฌ๊ณ  ์—…๋กœ๋“œ ์š”์ฒญ์—์„œ๋Š” ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•œ๋‹ค.

await apiClient.file<IFileResponse>("/files/upload", {
  moduleCode: props.module,
  fileType: "FILE",
  targetType: uploadTarget.value || null,
  file
});

ํ•˜์ง€๋งŒ ์ด ๋ฐฉ๋ฒ•์€ ๊ทผ๋ณธ์ ์ธ ํ•ด๊ฒฐ์ฑ…์€ ์•„๋‹ˆ๋‹ค.

target์€ ๋ช…ํ™•ํžˆ ์ปดํฌ๋„ŒํŠธ์˜ prop์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ฐ’์ด๊ธฐ ๋•Œ๋ฌธ์— $attrs์—์„œ ๊บผ๋‚ด ์“ฐ๋Š” ๊ฒƒ๋ณด๋‹ค defineProps์—์„œ ์ •์ƒ์ ์œผ๋กœ ์ธ์‹๋˜๋„๋ก ๊ณ ์น˜๋Š” ๊ฒƒ์ด ๋งž๋‹ค.


๋‹ค์Œ์— ์กฐ์‹ฌํ•  ๊ฒƒ

defineProps<T>()์— ํƒ€์ž…์„ ๋„ฃ์„ ๋•Œ๋Š” TypeScript์—์„œ ๋ฌธ์ œ๊ฐ€ ์—†๋‹ค๊ณ  ํ•ด์„œ Vue ๋Ÿฐํƒ€์ž„์—์„œ๋„ ํ•ญ์ƒ ์ •์ƒ ๋™์ž‘ํ•œ๋‹ค๊ณ  ๋ฏฟ์œผ๋ฉด ์•ˆ ๋œ๋‹ค.

ํŠนํžˆ ์•„๋ž˜์™€ ๊ฐ™์€ ํƒ€์ž… ๊ตฌ์กฐ๋Š” ์กฐ์‹ฌํ•ด์•ผ ํ•œ๋‹ค.

interface BaseProps<T> {
  value: T;
}

interface ChildProps extends BaseProps<SomeType> {
  target?: string;
}

์ด๋Ÿฐ ๊ตฌ์กฐ๋Š” TypeScript์—์„œ๋Š” ์ •์ƒ์ด์–ด๋„ Vue SFC ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋Ÿฐํƒ€์ž„ props๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์—์„œ ์ผ๋ถ€ prop์„ ๋ˆ„๋ฝํ•  ์ˆ˜ ์žˆ๋‹ค.

์•ž์œผ๋กœ๋Š” ๋‹ค์Œ ๊ธฐ์ค€์„ ์ง€ํ‚ค๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

1. defineProps์— ๋„ฃ๋Š” ํƒ€์ž…์€ ์ตœ๋Œ€ํ•œ flatํ•˜๊ฒŒ ์ž‘์„ฑํ•˜๊ธฐ

interface ComponentProps {
  modelValue: SomeType[];
  target?: string;
  mode?: "edit" | "view";
}

2. props๊ฐ€ ์•ˆ ์žกํžˆ๋ฉด $attrs๋ฅผ ๋ฐ”๋กœ ํ™•์ธํ•˜๊ธฐ

const attrs = useAttrs();

console.log("props.target:", props.target);
console.log("attrs.target:", attrs.target);

๋งŒ์•ฝ props์—๋Š” ์—†๊ณ  $attrs์— ์žˆ๋‹ค๋ฉด ๋ถ€๋ชจ ์ „๋‹ฌ ๋ฌธ์ œ๋ผ๊ธฐ๋ณด๋‹ค ์ž์‹์˜ prop ์„ ์–ธ ๋ฌธ์ œ์ผ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๋‹ค.

3. console.log(props)๋งŒ ๋ฏฟ์ง€ ์•Š๊ธฐ

Vue์˜ props๋Š” reactive proxy์ด๊ธฐ ๋•Œ๋ฌธ์— ์ฝ˜์†”์—์„œ ํŽผ์ณ ๋ณด๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋Š” ํ—ท๊ฐˆ๋ฆด ์ˆ˜ ์žˆ๋‹ค.

๊ฐ€๋Šฅํ•˜๋ฉด ๊ฐœ๋ณ„ ๊ฐ’์„ ์ง์ ‘ ์ฐ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

console.log("target:", props.target);
console.log("module:", props.module);
console.log("mode:", props.mode);

4. ๊ณตํ†ต ํƒ€์ž… ์žฌ์‚ฌ์šฉ๋ณด๋‹ค ์ปดํฌ๋„ŒํŠธ ์•ˆ์ •์„ฑ์„ ์šฐ์„ ํ•˜๊ธฐ

๊ณตํ†ต ํƒ€์ž…์„ ์žฌ์‚ฌ์šฉํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ๊น”๋”ํ•ด ๋ณด์ด์ง€๋งŒ, defineProps์—์„œ๋Š” ์˜คํžˆ๋ ค ๋Ÿฐํƒ€์ž„ ๋ณ€ํ™˜ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ๋‹ค.

ํŠนํžˆ ๊ณตํ†ต ํƒ€์ž…์ด generic, extends, imported type์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค๋ฉด props ์„ ์–ธ์—๋Š” flat type์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์•ˆ์ „ํ•˜๋‹ค.


์ •๋ฆฌ

์ด๋ฒˆ ๋ฌธ์ œ๋Š” ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ prop์„ ์ž˜๋ชป ๋„˜๊ธด ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ์—ˆ๋‹ค.

๋ถ€๋ชจ์—์„œ ๋„˜๊ธด ๊ฐ’์€ ์ž์‹๊นŒ์ง€ ์ •์ƒ์ ์œผ๋กœ ๋„์ฐฉํ–ˆ๋‹ค.
๋‹ค๋งŒ Vue๊ฐ€ ํ•ด๋‹น ๊ฐ’์„ prop์œผ๋กœ ์ธ์‹ํ•˜์ง€ ๋ชปํ•ด์„œ $attrs๋กœ ๋„˜๊ฒจ๋ฒ„๋ฆฐ ๊ฒƒ์ด ๋ฌธ์ œ์˜€๋‹ค.

ํ•ต์‹ฌ ์›์ธ์€ defineProps<IFileUploadProps>()์— ์‚ฌ์šฉํ•œ ํƒ€์ž…์ด extends + generic ๊ตฌ์กฐ์˜€๊ณ , Vue SFC ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ด๋ฅผ ๋Ÿฐํƒ€์ž„ props๋กœ ์™„์ „ํžˆ ๋ณ€ํ™˜ํ•˜์ง€ ๋ชปํ•œ ๊ฒƒ์ด์—ˆ๋‹ค.

ํ•ด๊ฒฐ์€ defineProps์— ์‚ฌ์šฉํ•˜๋Š” ํƒ€์ž…์„ flatํ•˜๊ฒŒ ๋ฐ”๊พธ๋Š” ๊ฒƒ.

interface FileInputProps {
  modelValue: ISaveFile[];
  module: string;
  target?: string;
  fileCategory?: string;
  mode?: "edit" | "view";
}

์ด๋ ‡๊ฒŒ ๋‹จ์ˆœํ•˜๊ฒŒ ์„ ์–ธํ•˜๋ฉด props.target์œผ๋กœ ์ •์ƒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋ฒˆ ์ผ์„ ํ†ตํ•ด ์•Œ๊ฒŒ ๋œ ์ ์€ ํ•˜๋‚˜๋‹ค.

TypeScript๊ฐ€ ์ดํ•ดํ•˜๋Š” ํƒ€์ž…๊ณผ Vue๊ฐ€ ๋Ÿฐํƒ€์ž„ props๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋Š” ํƒ€์ž…์€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ๋‹ค.

profile
์ฃผ๋‹ˆ์–ด ํ”„๋ก ํŠธ์—”๋“œ๊ฐœ๋ฐœ์ž

0๊ฐœ์˜ ๋Œ“๊ธ€