diff --git a/src/components/list/TaskListItem.stories.tsx b/src/components/list/TaskListItem.stories.tsx
new file mode 100644
index 0000000..14951a1
--- /dev/null
+++ b/src/components/list/TaskListItem.stories.tsx
@@ -0,0 +1,136 @@
+import type { Meta, StoryObj } from '@storybook/nextjs-vite';
+
+import { useState } from 'react';
+import { useArgs } from 'storybook/preview-api';
+import { fn } from 'storybook/test';
+
+import TaskListItem from './TaskListItem';
+
+const meta = {
+ title: 'Components/TaskListItem',
+ component: TaskListItem,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+ args: {
+ title: '할일 제목',
+ date: '2024년 7월 29일',
+ checked: false,
+ isSelected: false,
+ onCheckedChange: fn(),
+ onKebabClick: fn(),
+ onFrequencyClick: fn(),
+ },
+ argTypes: {
+ checked: { control: 'boolean' },
+ isSelected: { control: 'boolean' },
+ isEditing: { control: 'boolean' },
+ frequency: { control: 'text' },
+ commentCount: { control: 'number' },
+ },
+ decorators: [
+ (Story) => (
+
+
+
+ ),
+ ],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+const ControlledTaskListItem: Story['render'] = (args) => {
+ const [{ checked }, updateArgs] = useArgs();
+
+ const handleCheckedChange = (nextChecked: boolean) => {
+ updateArgs({ checked: nextChecked });
+ args.onCheckedChange?.(nextChecked);
+ };
+
+ return ;
+};
+
+export const Default: Story = {
+ render: ControlledTaskListItem,
+};
+
+export const Selected: Story = {
+ render: ControlledTaskListItem,
+ args: {
+ isSelected: true,
+ },
+};
+
+export const Completed: Story = {
+ render: ControlledTaskListItem,
+ args: {
+ checked: true,
+ },
+};
+
+export const WithComments: Story = {
+ render: ControlledTaskListItem,
+ args: {
+ commentCount: 3,
+ },
+};
+
+export const WithFrequency: Story = {
+ render: ControlledTaskListItem,
+ args: {
+ frequency: '매일 반복',
+ },
+};
+
+const EditingTaskListItem: Story['render'] = (args) => {
+ const [title, setTitle] = useState('');
+
+ return (
+ alert(`입력 완료: ${title}`)}
+ />
+ );
+};
+
+export const Editing: Story = {
+ render: EditingTaskListItem,
+ args: {
+ date: '2024년 7월 29일',
+ frequency: '매일 반복',
+ },
+};
+
+export const Overview: Story = {
+ render: () => (
+
+
+
+
+
+
+
+
+
+ ),
+ parameters: {
+ controls: { disable: true },
+ },
+};
diff --git a/src/components/list/TaskListItem.tsx b/src/components/list/TaskListItem.tsx
new file mode 100644
index 0000000..62d9384
--- /dev/null
+++ b/src/components/list/TaskListItem.tsx
@@ -0,0 +1,131 @@
+'use client';
+
+import clsx from 'clsx';
+import Image from 'next/image';
+import type { KeyboardEvent } from 'react';
+
+import CheckBox from '@/components/checkbox/CheckBox';
+
+import styles from './styles/TaskListItem.module.css';
+import { TASK_LIST_ITEM_ICONS } from './constants/taskListItemConstants';
+import type { TaskListItemProps } from './types/types';
+
+/**
+ * 할일 목록 아이템 카드 컴포넌트.
+ * 체크박스 + 제목 + 케밥 메뉴가 상단에, 날짜 + 반복 정보가 하단에 표시됩니다.
+ * isEditing이 true이면 제목 영역이 인라인 텍스트 입력으로 전환됩니다.
+ */
+export default function TaskListItem({
+ title,
+ date,
+ checked = false,
+ isSelected = false,
+ isEditing = false,
+ placeholder,
+ frequency,
+ commentCount,
+ onCheckedChange,
+ onTitleChange,
+ onTitleSubmit,
+ onKebabClick,
+ onFrequencyClick,
+ className,
+}: TaskListItemProps) {
+ const handleKeyDown = (event: KeyboardEvent) => {
+ if (event.key === 'Enter') {
+ event.preventDefault();
+ onTitleSubmit?.();
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+ {isEditing ? (
+ onTitleChange?.(e.target.value)}
+ onKeyDown={handleKeyDown}
+ aria-label="할일 제목 입력"
+ />
+ ) : (
+ {title}
+ )}
+ {!isEditing && commentCount != null && commentCount > 0 && (
+
+
+ {commentCount}
+
+ )}
+
+
+
+
+
+
+ {date}
+ {frequency && (
+ <>
+ |
+
+ >
+ )}
+
+
+ );
+}
diff --git a/src/components/list/constants/taskListItemConstants.ts b/src/components/list/constants/taskListItemConstants.ts
new file mode 100644
index 0000000..1d1d152
--- /dev/null
+++ b/src/components/list/constants/taskListItemConstants.ts
@@ -0,0 +1,11 @@
+import calenderSmall from '@/assets/icons/calender/calenderSmall.svg';
+import repeatSmall from '@/assets/icons/repeat/repeatSmall.svg';
+import comment from '@/assets/icons/comment/comment.svg';
+import kebabSmall from '@/assets/icons/kebab/kebabSmall.svg';
+
+export const TASK_LIST_ITEM_ICONS = {
+ calender: calenderSmall,
+ repeat: repeatSmall,
+ comment,
+ kebab: kebabSmall,
+} as const;
diff --git a/src/components/list/index.ts b/src/components/list/index.ts
new file mode 100644
index 0000000..a184947
--- /dev/null
+++ b/src/components/list/index.ts
@@ -0,0 +1,2 @@
+export { default as TaskListItem } from './TaskListItem';
+export type { TaskListItemProps } from './types/types';
diff --git a/src/components/list/styles/TaskListItem.module.css b/src/components/list/styles/TaskListItem.module.css
new file mode 100644
index 0000000..434f559
--- /dev/null
+++ b/src/components/list/styles/TaskListItem.module.css
@@ -0,0 +1,198 @@
+.card {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ padding: 12px;
+ border-radius: 8px;
+ background-color: var(--color-background-inverse);
+ border: 1px solid var(--color-background-tertiary);
+ cursor: pointer;
+ transition:
+ background-color 0.15s,
+ border-color 0.15s;
+}
+
+.card:hover {
+ background-color: var(--color-brand-secondary);
+}
+
+.selected {
+ border-color: var(--color-icon-brand);
+}
+
+.completed {
+ background-color: var(--color-background-secondary);
+}
+
+.completed:hover {
+ background-color: var(--color-background-secondary);
+}
+
+.topRow {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.checkboxLarge {
+ display: flex;
+ align-items: center;
+}
+
+.checkboxSmall {
+ display: none;
+ align-items: center;
+}
+
+.titleGroup {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+}
+
+.title {
+ min-width: 0;
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 1.4;
+ color: var(--color-text-tertiary);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.titleInput {
+ width: 100%;
+ min-width: 0;
+ font-family: var(--font-pretendard), 'Pretendard', sans-serif;
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 1.4;
+ color: var(--color-text-tertiary);
+ background: transparent;
+ border: none;
+ outline: none;
+ padding: 0;
+}
+
+.titleInput::placeholder {
+ color: var(--color-text-default);
+}
+
+.titleCompleted {
+ color: var(--color-text-disabled);
+ text-decoration: line-through;
+}
+
+.commentCount {
+ display: inline-flex;
+ align-items: center;
+ gap: 2px;
+ flex-shrink: 0;
+ font-size: 12px;
+ color: var(--color-text-disabled);
+}
+
+.metaRow {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ font-size: 12px;
+ color: var(--color-text-disabled);
+}
+
+.metaIcon {
+ flex-shrink: 0;
+ display: block;
+ width: 12px;
+ height: 12px;
+}
+
+.separator {
+ margin: 0 8px;
+ color: var(--color-background-tertiary);
+}
+
+.frequencyButton {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+ padding: 0;
+ border: none;
+ background: none;
+ font-family: inherit;
+ font-size: inherit;
+ color: inherit;
+ cursor: pointer;
+}
+
+.frequencyButton:hover {
+ text-decoration: underline;
+}
+
+.kebab {
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 24px;
+ height: 24px;
+ padding: 0;
+ border: none;
+ background: none;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.kebab:hover {
+ background-color: var(--color-background-tertiary);
+}
+
+@media (max-width: 767px) {
+ .card {
+ padding: 10px;
+ }
+
+ .topRow {
+ gap: 6px;
+ }
+}
+
+@media (max-width: 375px) {
+ .card {
+ padding: 8px;
+ gap: 4px;
+ }
+
+ .topRow {
+ gap: 4px;
+ }
+
+ .checkboxLarge {
+ display: none;
+ }
+
+ .checkboxSmall {
+ display: flex;
+ }
+
+ .title,
+ .titleInput {
+ font-size: 13px;
+ }
+
+ .commentCount {
+ font-size: 11px;
+ }
+
+ .metaRow {
+ font-size: 11px;
+ }
+
+ .metaIcon {
+ width: 10px;
+ height: 10px;
+ }
+}
diff --git a/src/components/list/types/types.ts b/src/components/list/types/types.ts
new file mode 100644
index 0000000..f75e061
--- /dev/null
+++ b/src/components/list/types/types.ts
@@ -0,0 +1,30 @@
+export type TaskListItemProps = {
+ /** 할일 제목 텍스트 */
+ title: string;
+ /** 날짜 텍스트 (예: "2024년 7월 29일") */
+ date: string;
+ /** 체크(완료) 여부 */
+ checked?: boolean;
+ /** 선택 상태 (파란 테두리 표시) */
+ isSelected?: boolean;
+ /** 인라인 편집 모드 활성화 여부 */
+ isEditing?: boolean;
+ /** 편집 모드에서 빈 입력 시 표시할 안내 텍스트 */
+ placeholder?: string;
+ /** 반복 정보 텍스트 (예: "매일 반복"). 없으면 숨김 */
+ frequency?: string;
+ /** 댓글 수. 0이거나 없으면 숨김 */
+ commentCount?: number;
+ /** 체크 상태 변경 시 호출되는 콜백 */
+ onCheckedChange?: (checked: boolean) => void;
+ /** 편집 모드에서 제목 텍스트 변경 시 호출되는 콜백 */
+ onTitleChange?: (value: string) => void;
+ /** 편집 모드에서 Enter 입력 시 호출되는 콜백 */
+ onTitleSubmit?: () => void;
+ /** 케밥(⋮) 버튼 클릭 시 호출되는 콜백 */
+ onKebabClick?: () => void;
+ /** 반복 아이콘 클릭 시 호출되는 콜백 */
+ onFrequencyClick?: () => void;
+ /** 추가 CSS 클래스 */
+ className?: string;
+};