diff --git a/src/components/input/AccountInput.tsx b/src/components/input/AccountInput.tsx
new file mode 100644
index 0000000..97ec397
--- /dev/null
+++ b/src/components/input/AccountInput.tsx
@@ -0,0 +1,39 @@
+import { useId } from 'react';
+
+import Input from './Input';
+import { AccountInputProps } from './types/types';
+import styles from './styles/AccountInput.module.css';
+
+/**
+ * 프로필 페이지용 계정 정보 표시 컴포넌트.
+ * 이메일과 비밀번호를 읽기 전용으로 보여줍니다.
+ * children 슬롯에 변경하기 버튼 등을 주입할 수 있습니다.
+ */
+export default function AccountInput({ email, children }: AccountInputProps) {
+ const emailId = useId();
+ const passwordId = useId();
+
+ return (
+
+
+
+
+
+
+
+
+
+ {children &&
{children}
}
+
+ );
+}
diff --git a/src/components/input/ActionTextArea.tsx b/src/components/input/ActionTextArea.tsx
new file mode 100644
index 0000000..27652f3
--- /dev/null
+++ b/src/components/input/ActionTextArea.tsx
@@ -0,0 +1,59 @@
+'use client';
+
+import { useRef, useState } from 'react';
+import Image from 'next/image';
+import clsx from 'clsx';
+import TextArea from './TextArea';
+import { ActionTextAreaProps } from './types/types';
+import arrowActive from '@/assets/buttons/arrow/arrowUpActivedButton.svg';
+import arrowInactive from '@/assets/buttons/arrow/arrowUpNonActivedButton.svg';
+import styles from './styles/ActionTextArea.module.css';
+
+/**
+ * 전송 버튼이 포함된 텍스트 입력 컴포넌트.
+ * 텍스트를 입력하면 전송 버튼이 활성화되고, 높이가 내용에 맞게 자동 조절됩니다.
+ * CommentInput의 기반 컴포넌트로, 단독으로도 사용할 수 있습니다.
+ */
+export default function ActionTextArea({
+ onSubmit,
+ wrapperClassName,
+ className,
+ onChange,
+ ...props
+}: ActionTextAreaProps) {
+ const [hasValue, setHasValue] = useState(false);
+ const textareaRef = useRef(null);
+
+ return (
+
+
+ );
+}
diff --git a/src/components/input/ChangePassword.tsx b/src/components/input/ChangePassword.tsx
new file mode 100644
index 0000000..4b5af7a
--- /dev/null
+++ b/src/components/input/ChangePassword.tsx
@@ -0,0 +1,50 @@
+import { useId } from 'react';
+
+import Input from './Input';
+import { ChangePasswordProps } from './types/types';
+import styles from './styles/ChangePassword.module.css';
+
+/**
+ * 비밀번호 변경 폼 컴포넌트.
+ * 새 비밀번호 + 확인 입력 필드로 구성되며, isEditing이 false면 입력이 비활성화됩니다.
+ * children 슬롯에 변경하기/취소 버튼 등을 주입할 수 있습니다.
+ */
+export default function ChangePassword({
+ isEditing = false,
+ newPasswordProps,
+ confirmPasswordProps,
+ children,
+}: ChangePasswordProps) {
+ const newPasswordId = useId();
+ const confirmPasswordId = useId();
+
+ return (
+
+
+
+
+
+
+
+
+
+
{children}
+
+ );
+}
diff --git a/src/components/input/CommentInput.tsx b/src/components/input/CommentInput.tsx
new file mode 100644
index 0000000..8c7ef0c
--- /dev/null
+++ b/src/components/input/CommentInput.tsx
@@ -0,0 +1,19 @@
+import clsx from 'clsx';
+import ActionTextArea from './ActionTextArea';
+import { CommentInputProps } from './types/types';
+import styles from './styles/CommentInput.module.css';
+
+/**
+ * 댓글 입력 컴포넌트.
+ * ActionTextArea를 위아래 보더 스타일로 감싸서 댓글 영역에 맞는 디자인을 제공합니다.
+ * 전송 버튼과 높이 자동 조절은 ActionTextArea에서 상속됩니다.
+ */
+export default function CommentInput({ className, ...props }: CommentInputProps) {
+ return (
+
+ );
+}
diff --git a/src/components/input/Input.stories.tsx b/src/components/input/Input.stories.tsx
new file mode 100644
index 0000000..a14559a
--- /dev/null
+++ b/src/components/input/Input.stories.tsx
@@ -0,0 +1,89 @@
+import type { Meta, StoryObj } from '@storybook/nextjs-vite';
+
+import { fn } from 'storybook/test';
+
+import Input from './Input';
+
+const meta = {
+ title: 'Components/Input',
+ component: Input,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+ args: {
+ placeholder: '텍스트를 입력해 주세요.',
+ onChange: fn(),
+ },
+ argTypes: {
+ type: {
+ control: 'inline-radio',
+ options: ['text', 'email', 'password'],
+ },
+ errorMessage: {
+ control: 'text',
+ },
+ isError: {
+ control: 'boolean',
+ },
+ disabled: {
+ control: 'boolean',
+ },
+ },
+ decorators: [
+ (Story) => (
+
+
+
+ ),
+ ],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {};
+
+export const Email: Story = {
+ args: {
+ type: 'email',
+ placeholder: '이메일을 입력해 주세요.',
+ },
+};
+
+export const EmailWithError: Story = {
+ args: {
+ type: 'email',
+ placeholder: '이메일을 입력해 주세요.',
+ errorMessage: '유효한 이메일이 아닙니다',
+ },
+};
+
+export const ErrorBorderOnly: Story = {
+ args: {
+ isError: true,
+ placeholder: '보더만 빨간색',
+ },
+};
+
+export const Disabled: Story = {
+ args: {
+ disabled: true,
+ value: '비활성 상태',
+ },
+};
+
+export const Overview: Story = {
+ render: () => (
+
+
+
+
+
+
+
+ ),
+ parameters: {
+ controls: { disable: true },
+ },
+};
diff --git a/src/components/input/Input.tsx b/src/components/input/Input.tsx
new file mode 100644
index 0000000..3696db0
--- /dev/null
+++ b/src/components/input/Input.tsx
@@ -0,0 +1,32 @@
+'use client';
+
+import { useId } from 'react';
+import clsx from 'clsx';
+import { InputProps } from './types/types';
+import styles from './styles/Input.module.css';
+
+/**
+ * 범용 텍스트 입력 컴포넌트.
+ * 네이티브 ``의 모든 속성을 지원하며, errorMessage를 전달하면
+ * 빨간 테두리 + 하단 에러 텍스트가 자동으로 표시됩니다.
+ */
+export default function Input({ className, errorMessage, isError, ...props }: InputProps) {
+ const hasError = isError || !!errorMessage;
+ const errorId = useId();
+
+ return (
+ <>
+
+ {errorMessage && (
+
+ {errorMessage}
+
+ )}
+ >
+ );
+}
diff --git a/src/components/input/PasswordInput.stories.tsx b/src/components/input/PasswordInput.stories.tsx
new file mode 100644
index 0000000..09f8fc5
--- /dev/null
+++ b/src/components/input/PasswordInput.stories.tsx
@@ -0,0 +1,63 @@
+import type { Meta, StoryObj } from '@storybook/nextjs-vite';
+
+import { fn } from 'storybook/test';
+
+import PasswordInput from './PasswordInput';
+
+const meta = {
+ title: 'Components/PasswordInput',
+ component: PasswordInput,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+ args: {
+ placeholder: '비밀번호를 입력해 주세요.',
+ onChange: fn(),
+ },
+ argTypes: {
+ errorMessage: {
+ control: 'text',
+ },
+ disabled: {
+ control: 'boolean',
+ },
+ },
+ decorators: [
+ (Story) => (
+
+
+
+ ),
+ ],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {};
+
+export const WithError: Story = {
+ args: {
+ errorMessage: '비밀번호를 입력해주세요.',
+ },
+};
+
+export const Disabled: Story = {
+ args: {
+ disabled: true,
+ },
+};
+
+export const Overview: Story = {
+ render: () => (
+
+ ),
+ parameters: {
+ controls: { disable: true },
+ },
+};
diff --git a/src/components/input/PasswordInput.tsx b/src/components/input/PasswordInput.tsx
new file mode 100644
index 0000000..3932bf8
--- /dev/null
+++ b/src/components/input/PasswordInput.tsx
@@ -0,0 +1,53 @@
+'use client';
+
+import { useId, useState } from 'react';
+import Image from 'next/image';
+import clsx from 'clsx';
+import Input from './Input';
+import { PasswordInputProps } from './types/types';
+import visibilityTrue from '@/assets/icons/visibility/visibillityTrue.svg';
+import visibilityFalse from '@/assets/icons/visibility/visibillityFalse.svg';
+import styles from './styles/PasswordInput.module.css';
+
+/**
+ * 비밀번호 전용 입력 컴포넌트.
+ * 우측 눈 아이콘으로 비밀번호 표시/숨기기를 토글할 수 있습니다.
+ * 복사 방지가 기본 적용되어 있으며, errorMessage 전달 시 에러 상태를 표시합니다.
+ */
+export default function PasswordInput({ className, errorMessage, ...props }: PasswordInputProps) {
+ const [showPassword, setShowPassword] = useState(false);
+ const errorId = useId();
+
+ return (
+ <>
+
+ e.preventDefault()}
+ aria-describedby={errorMessage ? errorId : undefined}
+ {...props}
+ />
+
+
+ {errorMessage && (
+
+ {errorMessage}
+
+ )}
+ >
+ );
+}
diff --git a/src/components/input/TextArea.tsx b/src/components/input/TextArea.tsx
new file mode 100644
index 0000000..48e0e91
--- /dev/null
+++ b/src/components/input/TextArea.tsx
@@ -0,0 +1,12 @@
+import clsx from 'clsx';
+import { TextAreaProps } from './types/types';
+import styles from './styles/TextArea.module.css';
+
+/**
+ * 멀티라인 텍스트 입력 컴포넌트.
+ * 네이티브 `