diff --git a/public/icons/camera.svg b/public/icons/camera.svg
new file mode 100644
index 00000000..cc6a70ac
--- /dev/null
+++ b/public/icons/camera.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/features/study/group/ui/group-study-thumbnail-input.tsx b/src/features/study/group/ui/group-study-thumbnail-input.tsx
new file mode 100644
index 00000000..319193e2
--- /dev/null
+++ b/src/features/study/group/ui/group-study-thumbnail-input.tsx
@@ -0,0 +1,136 @@
+'use client';
+
+import Image from 'next/image';
+import { useState, DragEvent, ChangeEvent, useRef } from 'react';
+import Button from '@/shared/ui/button';
+
+const inputStyles = {
+ base: 'rounded-100 flex w-full flex-col items-center justify-center rounded-lg border-2 p-500',
+ dragging: 'border-border-brand bg-fill-brand-subtle-hover',
+ notDragging: 'border-gray-300 border-gray-300 border-dashed',
+};
+
+export default function GroupStudyThumbnailInput({
+ image,
+ onChangeImage,
+}: {
+ image?: string;
+ onChangeImage: (image?: string) => void;
+}) {
+ const fileInputRef = useRef(null);
+
+ const handleOpenFileDialog = () => {
+ fileInputRef.current?.click();
+ };
+
+ const [isDragging, setIsDragging] = useState(false);
+
+ // 영역 안에 드래그 들어왔을 때
+ const handleDragEnter = (e: React.DragEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ setIsDragging(true);
+ };
+ // 영역 밖으로 드래그 나갈 때
+ const handleDragLeave = (e: React.DragEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ console.log('drag leave');
+ setIsDragging(false);
+ };
+ // 영역 안에서 드래그 중일 때
+ const handleDragOver = (e: DragEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ console.log('drag over');
+
+ if (e.dataTransfer.files) {
+ setIsDragging(true);
+ }
+ };
+ // 영역 안에서 drop 했을 때
+ const handleDrop = (e: DragEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ console.log('drop');
+ setIsDragging(false);
+
+ const file = e.dataTransfer.files[0]; // 1장만 허용
+
+ if (file && file.type.startsWith('image/')) {
+ onChangeImage(URL.createObjectURL(file));
+ }
+ };
+
+ // 파일 업로드 버튼으로 파일 선택했을 때 preview 설정
+ const handleFileChange = (e: ChangeEvent) => {
+ const file = e.target.files?.[0];
+
+ if (file && file.type.startsWith('image/')) {
+ onChangeImage(URL.createObjectURL(file));
+ }
+ };
+
+ const handleRemove = () => {
+ onChangeImage(undefined);
+ };
+
+ return (
+
+ {!image ? (
+
+
+
+
+ 드래그하여 파일 업로드
+
+
+
+
+
+ ) : (
+
+
+
+
+ )}
+
+ );
+}
diff --git a/src/stories/ui/group-study-thumbnail-input.stories.tsx b/src/stories/ui/group-study-thumbnail-input.stories.tsx
new file mode 100644
index 00000000..948fd35d
--- /dev/null
+++ b/src/stories/ui/group-study-thumbnail-input.stories.tsx
@@ -0,0 +1,29 @@
+import { Meta, StoryObj } from '@storybook/react';
+import { useState } from 'react';
+import GroupStudyThumbnailInput from '@/features/study/group/ui/group-study-thumbnail-input';
+
+const meta: Meta = {
+ component: GroupStudyThumbnailInput,
+ argTypes: {
+ image: {
+ description:
+ '이미지 URL 상태 입니다. (undefined인 경우 이미지가 없는 상태를 의미합니다.)',
+ },
+ onChangeImage: {
+ description:
+ '이미지 URL 상태를 변경하는 함수입니다. 파라미터는 이미지 URL 또는 undefined 입니다. (undefined인 경우 이미지가 제거된 상태를 의미합니다.)',
+ },
+ },
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: () => {
+ const [image, setImage] = useState(undefined);
+
+ return ;
+ },
+};