diff --git "a/.github/ISSUE_TEMPLATE/\360\237\220\236-\341\204\207\341\205\245\341\204\200\341\205\263-\341\204\205\341\205\265\341\204\221\341\205\251\341\204\220\341\205\263-\341\204\220\341\205\246\341\206\267\341\204\221\341\205\263\341\206\257\341\204\205\341\205\265\341\206\272.md" "b/.github/ISSUE_TEMPLATE/\360\237\220\236-\341\204\207\341\205\245\341\204\200\341\205\263-\341\204\205\341\205\265\341\204\221\341\205\251\341\204\220\341\205\263-\341\204\220\341\205\246\341\206\267\341\204\221\341\205\263\341\206\257\341\204\205\341\205\265\341\206\272.md"
new file mode 100644
index 00000000..286df978
--- /dev/null
+++ "b/.github/ISSUE_TEMPLATE/\360\237\220\236-\341\204\207\341\205\245\341\204\200\341\205\263-\341\204\205\341\205\265\341\204\221\341\205\251\341\204\220\341\205\263-\341\204\220\341\205\246\341\206\267\341\204\221\341\205\263\341\206\257\341\204\205\341\205\265\341\206\272.md"
@@ -0,0 +1,25 @@
+---
+name: "\U0001F41E 버그 리포트 템플릿"
+about: 프로젝트에서 발생하는 버그에 대해 명세합니다.
+title: "[Bug] Issue title"
+labels: "\U0001F41E BugFix"
+assignees: ""
+---
+
+## 💬 버그 설명
+
+문제가 생긴 상황을 간단히 설명해주세요.
+
+## 🔁 재현 방법
+
+문제가 어떻게 발생했는지 순서대로 작성해주세요.
+
+## ⚙️ 기대 동작
+
+정상적으로 어떤 동작을 기대했는지 설명해주세요.
+
+## 📸 스크린샷 (선택사항)
+
+## 📄 추가 정보
+
+기타 참고할만한 정보나 자료가 있다면 자유롭게 작성해주세요.
diff --git "a/.github/ISSUE_TEMPLATE/\360\237\220\236-\353\262\204\352\267\270-\353\246\254\355\217\254\355\212\270-\355\205\234\355\224\214\353\246\277.md" "b/.github/ISSUE_TEMPLATE/\360\237\220\236-\353\262\204\352\267\270-\353\246\254\355\217\254\355\212\270-\355\205\234\355\224\214\353\246\277.md"
index e668ddcc..286df978 100644
--- "a/.github/ISSUE_TEMPLATE/\360\237\220\236-\353\262\204\352\267\270-\353\246\254\355\217\254\355\212\270-\355\205\234\355\224\214\353\246\277.md"
+++ "b/.github/ISSUE_TEMPLATE/\360\237\220\236-\353\262\204\352\267\270-\353\246\254\355\217\254\355\212\270-\355\205\234\355\224\214\353\246\277.md"
@@ -3,20 +3,23 @@ name: "\U0001F41E 버그 리포트 템플릿"
about: 프로젝트에서 발생하는 버그에 대해 명세합니다.
title: "[Bug] Issue title"
labels: "\U0001F41E BugFix"
-assignees: ''
-
+assignees: ""
---
## 💬 버그 설명
+
문제가 생긴 상황을 간단히 설명해주세요.
## 🔁 재현 방법
+
문제가 어떻게 발생했는지 순서대로 작성해주세요.
## ⚙️ 기대 동작
+
정상적으로 어떤 동작을 기대했는지 설명해주세요.
## 📸 스크린샷 (선택사항)
## 📄 추가 정보
+
기타 참고할만한 정보나 자료가 있다면 자유롭게 작성해주세요.
diff --git a/.storybook/main.ts b/.storybook/main.ts
index aaa61f6f..c53b1e01 100644
--- a/.storybook/main.ts
+++ b/.storybook/main.ts
@@ -1,4 +1,5 @@
import type { StorybookConfig } from "@storybook/nextjs";
+
const config: StorybookConfig = {
stories: ["../src/**/*.stories.@(js|jsx|mdc|ts|tsx)"],
addons: ["@storybook/addon-a11y", "@storybook/addon-docs"],
@@ -11,6 +12,7 @@ const config: StorybookConfig = {
},
},
},
+ staticDirs: ["../public"],
typescript: {
check: false,
reactDocgen: "react-docgen-typescript",
@@ -36,4 +38,5 @@ const config: StorybookConfig = {
return config;
},
};
+
export default config;
diff --git a/public/images/aroma/aroma-apple.jpg b/public/images/aroma/aroma-apple.jpg
new file mode 100644
index 00000000..27d4cc35
Binary files /dev/null and b/public/images/aroma/aroma-apple.jpg differ
diff --git a/public/images/aroma/aroma-berry.jpg b/public/images/aroma/aroma-berry.jpg
new file mode 100644
index 00000000..1cf6eff4
Binary files /dev/null and b/public/images/aroma/aroma-berry.jpg differ
diff --git a/public/images/aroma/aroma-cherry.jpg b/public/images/aroma/aroma-cherry.jpg
new file mode 100644
index 00000000..5ef1bf5b
Binary files /dev/null and b/public/images/aroma/aroma-cherry.jpg differ
diff --git a/public/images/aroma/aroma-chocolate.jpg b/public/images/aroma/aroma-chocolate.jpg
new file mode 100644
index 00000000..2c6cb53b
Binary files /dev/null and b/public/images/aroma/aroma-chocolate.jpg differ
diff --git a/public/images/aroma/aroma-citrus.jpg b/public/images/aroma/aroma-citrus.jpg
new file mode 100644
index 00000000..add34b7e
Binary files /dev/null and b/public/images/aroma/aroma-citrus.jpg differ
diff --git a/public/images/aroma/aroma-coconut.jpg b/public/images/aroma/aroma-coconut.jpg
new file mode 100644
index 00000000..b0f81bd9
Binary files /dev/null and b/public/images/aroma/aroma-coconut.jpg differ
diff --git a/public/images/aroma/aroma-flower.jpg b/public/images/aroma/aroma-flower.jpg
new file mode 100644
index 00000000..f9f4dd65
Binary files /dev/null and b/public/images/aroma/aroma-flower.jpg differ
diff --git a/public/images/aroma/aroma-grass.jpg b/public/images/aroma/aroma-grass.jpg
new file mode 100644
index 00000000..a44d1675
Binary files /dev/null and b/public/images/aroma/aroma-grass.jpg differ
diff --git a/public/images/aroma/aroma-herb.jpg b/public/images/aroma/aroma-herb.jpg
new file mode 100644
index 00000000..dc1ffb9d
Binary files /dev/null and b/public/images/aroma/aroma-herb.jpg differ
diff --git a/public/images/aroma/aroma-mineral.jpg b/public/images/aroma/aroma-mineral.jpg
new file mode 100644
index 00000000..1c37a0c8
Binary files /dev/null and b/public/images/aroma/aroma-mineral.jpg differ
diff --git a/public/images/aroma/aroma-no-image.jpg b/public/images/aroma/aroma-no-image.jpg
new file mode 100644
index 00000000..d109a3b4
Binary files /dev/null and b/public/images/aroma/aroma-no-image.jpg differ
diff --git a/public/images/aroma/aroma-oak-cask.jpg b/public/images/aroma/aroma-oak-cask.jpg
new file mode 100644
index 00000000..7922cef1
Binary files /dev/null and b/public/images/aroma/aroma-oak-cask.jpg differ
diff --git a/public/images/aroma/aroma-peach.jpg b/public/images/aroma/aroma-peach.jpg
new file mode 100644
index 00000000..a8c9971b
Binary files /dev/null and b/public/images/aroma/aroma-peach.jpg differ
diff --git a/public/images/aroma/aroma-toast.jpg b/public/images/aroma/aroma-toast.jpg
new file mode 100644
index 00000000..a748d4ff
Binary files /dev/null and b/public/images/aroma/aroma-toast.jpg differ
diff --git a/public/images/aroma/aroma-tropical.jpg b/public/images/aroma/aroma-tropical.jpg
new file mode 100644
index 00000000..cf0a58d4
Binary files /dev/null and b/public/images/aroma/aroma-tropical.jpg differ
diff --git a/public/images/aroma/aroma-wet-soil.jpg b/public/images/aroma/aroma-wet-soil.jpg
new file mode 100644
index 00000000..4d2e1ae4
Binary files /dev/null and b/public/images/aroma/aroma-wet-soil.jpg differ
diff --git a/public/images/test/test_chip.jpg b/public/images/test/test_chip.jpg
new file mode 100644
index 00000000..566c46f7
Binary files /dev/null and b/public/images/test/test_chip.jpg differ
diff --git a/src/app/example/page.tsx b/src/app/example/page.tsx
index 89ae8e82..757d267d 100644
--- a/src/app/example/page.tsx
+++ b/src/app/example/page.tsx
@@ -1,7 +1,6 @@
"use client";
-import { SelectType } from "@/components";
-import Header from "@/components/header/Header";
+import { Chip, DropdownMenu, Flavor, Header, SelectType } from "@/components";
import React, { ChangeEvent } from "react";
const Page = () => {
@@ -9,13 +8,69 @@ const Page = () => {
console.log(e.target.value);
};
return (
-
+
+
+ console.log("로그아웃") },
+ ]}
+ />
+ console.log("임시") },
+ ]}
+ />
+ console.log("수정하기") },
+ { label: "삭제하기", onClick: () => console.log("삭제하기") },
+ ]}
+ />
+ console.log("버튼클릭") },
+ ]}
+ />
+
+
+
+
+
+
+
+
diff --git a/src/app/globals.css b/src/app/globals.css
index b758fb7d..ecf1d3fd 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -51,3 +51,13 @@ body {
/* stroke: currentColor; */
}
}
+
+/* 스크롤바 숨기기 */
+.scrollbar-hide {
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+}
+
+.scrollbar-hide::-webkit-scrollbar {
+ display: none;
+}
diff --git a/src/components/chip/Chip.stories.tsx b/src/components/chip/Chip.stories.tsx
new file mode 100644
index 00000000..bc211433
--- /dev/null
+++ b/src/components/chip/Chip.stories.tsx
@@ -0,0 +1,41 @@
+import { Meta, StoryObj } from "@storybook/nextjs";
+import Chip from "./Chip";
+
+const meta: Meta = {
+ title: "Components/Chip",
+ parameters: {
+ layout: "centered",
+ },
+ component: Chip,
+ tags: ["autodocs"],
+ decorators: [
+ (Story) => (
+
+
+
+ ),
+ ],
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {
+ label: "후추",
+ },
+};
+
+export const WithImage: Story = {
+ args: {
+ label: "후추",
+ img: "/images/test/test_chip.jpg",
+ },
+ decorators: [
+ (Story) => (
+
+
+
+ ),
+ ],
+};
diff --git a/src/components/chip/Chip.tsx b/src/components/chip/Chip.tsx
new file mode 100644
index 00000000..02a5d545
--- /dev/null
+++ b/src/components/chip/Chip.tsx
@@ -0,0 +1,44 @@
+"use client";
+
+import { cn } from "@/lib/utils";
+import Image from "next/image";
+import { useState } from "react";
+
+interface ChipProps {
+ img?: string;
+ label: string;
+}
+
+const Chip = ({ img, label }: ChipProps) => {
+ const [selected, setSelected] = useState(false);
+
+ return (
+
+ );
+};
+
+export default Chip;
diff --git a/src/components/dropdown-menu/DropdownMenu.stories.tsx b/src/components/dropdown-menu/DropdownMenu.stories.tsx
new file mode 100644
index 00000000..80822b97
--- /dev/null
+++ b/src/components/dropdown-menu/DropdownMenu.stories.tsx
@@ -0,0 +1,37 @@
+import type { Meta, StoryObj } from "@storybook/nextjs";
+import DropdownMenu from "./DropdownMenu";
+
+const meta = {
+ title: "Components/DropdownMenu",
+ component: DropdownMenu,
+ tags: ["autodocs"],
+ parameters: {
+ layout: "centered",
+ },
+ argTypes: {
+ items: { control: "object" },
+ },
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {
+ items: [
+ { label: "마이페이지", href: "/my-page" },
+ { label: "로그아웃", onClick: () => console.log("로그아웃") },
+ ],
+ },
+};
+
+export const EditDelete: Story = {
+ parameters: { nextjs: { navigation: { pathname: "/write" } } },
+ args: {
+ items: [
+ { label: "수정하기", onClick: () => console.log("수정하기") },
+ { label: "삭제하기", onClick: () => console.log("삭제하기") },
+ ],
+ },
+};
diff --git a/src/components/dropdown-menu/DropdownMenu.tsx b/src/components/dropdown-menu/DropdownMenu.tsx
new file mode 100644
index 00000000..d78ce962
--- /dev/null
+++ b/src/components/dropdown-menu/DropdownMenu.tsx
@@ -0,0 +1,52 @@
+"use client";
+
+import Link from "next/link";
+import { usePathname } from "next/navigation";
+import { cn } from "@/lib/utils";
+
+interface DropdownMenuProps {
+ items: { label: string; href?: string; onClick?: () => void }[];
+}
+
+const style = {
+ itemBase:
+ "flex-center h-full w-full rounded-md text-[16px] leading-[26px] text-gray-800 transition-colors hover:bg-gray-200",
+};
+
+function DropdownMenu({ items }: DropdownMenuProps) {
+ const pathname = usePathname();
+
+ const isActive = (href?: string) => Boolean(href && pathname === href);
+
+ return (
+
+ {items.map(({ label, href, onClick }) =>
+ href ? (
+
+ {label}
+
+ ) : (
+
+ )
+ )}
+
+ );
+}
+
+export default DropdownMenu;
diff --git a/src/components/flavor/Flavor.stories.tsx b/src/components/flavor/Flavor.stories.tsx
new file mode 100644
index 00000000..f3c531be
--- /dev/null
+++ b/src/components/flavor/Flavor.stories.tsx
@@ -0,0 +1,45 @@
+import { Meta, StoryObj } from "@storybook/nextjs";
+import Flavor from "./Flavor";
+import { AromaKey } from "@/types/AromaType";
+
+const meta: Meta = {
+ title: "Components/Flavor",
+ component: Flavor,
+ parameters: {
+ layout: "centered",
+ },
+ tags: ["autodocs"],
+ argTypes: {
+ count: { control: "number", description: "참여 인원 수 표시" },
+ items: {
+ control: "object",
+ description: "향 아이템 키 목록 (AromaKey[])",
+ },
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+const ITEMS: AromaKey[] = ["CHERRY", "PEPPER", "EMPTY"];
+
+export const Default: Story = {
+ args: {
+ count: 12,
+ items: ITEMS.slice(0, 1),
+ },
+};
+
+export const ThreeItems: Story = {
+ args: {
+ count: 3,
+ items: ITEMS,
+ },
+};
+
+export const Empty: Story = {
+ args: {
+ count: 0,
+ items: [],
+ },
+};
diff --git a/src/components/flavor/Flavor.tsx b/src/components/flavor/Flavor.tsx
new file mode 100644
index 00000000..8b6c1d9a
--- /dev/null
+++ b/src/components/flavor/Flavor.tsx
@@ -0,0 +1,53 @@
+import { AromaKey } from "@/types/AromaType";
+import Image from "next/image";
+import { aromaMap } from "./aroma-map";
+
+interface FlavorItemProps {
+ aroma: AromaKey;
+}
+
+const FlavorItem = ({ aroma }: FlavorItemProps) => {
+ const { img, label } = aromaMap[aroma];
+ return (
+
+
+ {label}
+
+ );
+};
+
+interface FlavorProps {
+ count: number;
+ items: AromaKey[];
+}
+
+const Flavor = ({ count, items }: FlavorProps) => {
+ return (
+
+
+
+ 어떤 향이 있나요?
+
+
+ ({count}명 참여)
+
+
+
+
+
+ {items.map((item, index) => (
+
+ ))}
+
+
+
+ );
+};
+
+export default Flavor;
diff --git a/src/components/flavor/aroma-map.ts b/src/components/flavor/aroma-map.ts
new file mode 100644
index 00000000..4e52bfeb
--- /dev/null
+++ b/src/components/flavor/aroma-map.ts
@@ -0,0 +1,94 @@
+import { AromaKey } from "@/types/AromaType";
+
+type AromaInfo = {
+ img: string;
+ label: string;
+};
+
+export const aromaMap: Record = {
+ CHERRY: {
+ img: "/images/aroma/aroma-cherry.jpg",
+ label: "체리",
+ },
+ BERRY: {
+ img: "/images/aroma/aroma-berry.jpg",
+ label: "베리",
+ },
+ OAK: {
+ img: "/images/aroma/aroma-oak-cask.jpg",
+ label: "오크",
+ },
+ VANILLA: {
+ // 이미지 없음
+ img: "/images/aroma/aroma-no-image.jpg",
+ label: "바닐라",
+ },
+ PEPPER: {
+ img: "/images/aroma/aroma-herb.jpg",
+ label: "후추",
+ },
+ BAKING: {
+ img: "/images/aroma/aroma-toast.jpg",
+ label: "베이킹",
+ },
+ GRASS: {
+ img: "/images/aroma/aroma-grass.jpg",
+ label: "풀",
+ },
+ APPLE: {
+ img: "/images/aroma/aroma-apple.jpg",
+ label: "사과",
+ },
+ PEACH: {
+ img: "/images/aroma/aroma-peach.jpg",
+ label: "복숭아",
+ },
+ CITRUS: {
+ img: "/images/aroma/aroma-citrus.jpg",
+ label: "시트러스",
+ },
+ TROPICAL: {
+ img: "/images/aroma/aroma-coconut.jpg",
+ label: "트로피컬",
+ },
+ MINERAL: {
+ img: "/images/aroma/aroma-mineral.jpg",
+ label: "미네랄",
+ },
+ FLOWER: {
+ img: "/images/aroma/aroma-flower.jpg",
+ label: "꽃",
+ },
+ TOBACCO: {
+ // 이미지 없음
+ img: "/images/aroma/aroma-no-image.jpg",
+ label: "담배",
+ },
+ EARTH: {
+ img: "/images/aroma/aroma-wet-soil.jpg",
+ label: "흙",
+ },
+ CHOCOLATE: {
+ img: "/images/aroma/aroma-chocolate.jpg",
+ label: "초콜릿",
+ },
+ SPICE: {
+ // 이미지 없음
+ img: "/images/aroma/aroma-no-image.jpg",
+ label: "향신료",
+ },
+ CARAMEL: {
+ // 이미지 없음
+ img: "/images/aroma/aroma-no-image.jpg",
+ label: "카라멜",
+ },
+ LEATHER: {
+ // 이미지 없음
+ img: "/images/aroma/aroma-no-image.jpg",
+ label: "가죽",
+ },
+ EMPTY: {
+ img: "/images/aroma/aroma-no-image.jpg",
+ label: "-",
+ },
+};
diff --git a/src/components/header/Header.stories.tsx b/src/components/header/Header.stories.tsx
index 07e39816..f8cda29c 100644
--- a/src/components/header/Header.stories.tsx
+++ b/src/components/header/Header.stories.tsx
@@ -29,7 +29,7 @@ export const Default: Story = {
};
export const ManyReviews: Story = {
- name: "리뷰가 많은 경우",
+ name: "Many Reviews ",
args: {
review: "12,345",
title: "도멘 드 라 로마네 콩티",
@@ -39,7 +39,7 @@ export const ManyReviews: Story = {
};
export const LongText: Story = {
- name: "제목/설명이 긴 경우",
+ name: "Long Text ",
args: {
review: "87",
title:
diff --git a/src/components/index.ts b/src/components/index.ts
index fbe8e787..43059486 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -1,5 +1,8 @@
export { default as Gnb } from "./gnb/Gnb";
export { default as Header } from "./header/Header";
+export { default as DropdownMenu } from "./dropdown-menu/DropdownMenu";
+export { default as Chip } from "./chip/Chip";
+export { default as Flavor } from "./flavor/Flavor";
export { TextInput, ModalTextInput } from "./text-input/text-input";
export { default as SelectType } from "./select-type/select-type";
export { default as Icon } from "./icon/Icon";
diff --git a/src/types/AromaType.ts b/src/types/AromaType.ts
new file mode 100644
index 00000000..dfcd4be4
--- /dev/null
+++ b/src/types/AromaType.ts
@@ -0,0 +1,21 @@
+export type AromaKey =
+ | "CHERRY"
+ | "BERRY"
+ | "OAK"
+ | "VANILLA"
+ | "PEPPER"
+ | "BAKING"
+ | "GRASS"
+ | "APPLE"
+ | "PEACH"
+ | "CITRUS"
+ | "TROPICAL"
+ | "MINERAL"
+ | "FLOWER"
+ | "TOBACCO"
+ | "EARTH"
+ | "CHOCOLATE"
+ | "SPICE"
+ | "CARAMEL"
+ | "LEATHER"
+ | "EMPTY";
diff --git a/tailwind.config.ts b/tailwind.config.ts
index ace6cba2..7c53b3ef 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -13,6 +13,14 @@ export default {
colors: {
black: "#1A1918",
white: "#FFFFFF",
+ gray100: "#FAFAFA",
+ gray300: "#D1D1D1",
+ gray600: "#8C8C8B",
+ primary: "#1A1918",
+ default: "#31302F",
+ secondary: "#A3A3A3",
+ neutral200: "#F2F2F2",
+ neutral400: "#BABABA",
gray: {
100: "#fafafa",
150: "#f7f7f7",