Icons
Interactive UI Explorer
아래에서 각 컴포넌트의 다양한 Variants와 States를 문서화된 형태로 테스트해볼 수 있습니다.
Live Preview
Icons
Standardized SVG icons as reusable components.
General Icons
Frequently used icons across the application.
Source Code
PlusIcon
SearchIcon
TrashIcon
EyeIcon
HeartIcon
InfoIcon
XIcon
MenuIcon
ArrowRightIcon
ChevronLeftIcon
ChevronRightIcon
PencilSquareIcon
Live Preview
Implementation
제작 코드
이 컴포넌트가 실제로 어떻게 구현되어 있는지 — 본체 .tsx 파일을 그대로 보여줍니다. variant 매핑·접근성 처리·forwardRef 패턴 등 디테일을 그대로 확인할 수 있어요.
Generaltypescript
import { cn } from "@/lib/cn";
interface IconProps {
className?: string;
strokeWidth?: number;
}
export const PlusIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
<svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M12 4v16m8-8H4" />
</svg>
);
export const SearchIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
<svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
);
export const TrashIcon = ({ className, strokeWidth = 2 }: IconProps) => (
<svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
);
/** 클립보드 복사 (URL 등) */
export const ClipboardIcon = ({ className, strokeWidth = 2 }: IconProps) => (
<svg
className={cn("h-4 w-4", className)}
fill="none"
stroke="currentColor"
strokeWidth={strokeWidth}
viewBox="0 0 24 24"
aria-hidden
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
/>
</svg>
);
/** 새 탭 / 외부 링크 열기 */
export const ArrowTopRightOnSquareIcon = ({ className, strokeWidth = 2 }: IconProps) => (
<svg
className={cn("h-4 w-4", className)}
fill="none"
stroke="currentColor"
strokeWidth={strokeWidth}
viewBox="0 0 24 24"
aria-hidden
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
/>
</svg>
);
export const EyeIcon = ({ className, strokeWidth = 2 }: IconProps) => (
<svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path strokeLinecap="round" strokeLinejoin="round" d="M2.458 12C3.732 7.943 7.522 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.478 0-8.268-2.943-9.542-7z" />
</svg>
);
export const HeartIcon = ({ className, strokeWidth = 2 }: IconProps) => (
<svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
</svg>
);
/** 댓글 수 등 (쇼케이스·블로그 카드) */
export const ChatBubbleIcon = ({ className, strokeWidth = 2 }: IconProps) => (
<svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24" aria-hidden>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
/>
</svg>
);
export const InfoIcon = ({ className, strokeWidth = 2 }: IconProps) => (
<svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
);
export const XIcon = ({ className, strokeWidth = 2 }: IconProps) => (
<svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
);
export const MenuIcon = ({ className, strokeWidth = 2 }: IconProps) => (
<svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M4 6h16M4 12h16M4 18h16" />
</svg>
);
export const ArrowRightIcon = ({ className, strokeWidth = 2 }: IconProps) => (
<svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M14 5l7 7m0 0l-7 7m7-7H3" />
</svg>
);
/** 이전 페이지 (Pagination 등) */
export const ChevronLeftIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
<svg
className={cn("h-4 w-4", className)}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<path d="M15 18l-6-6 6-6" />
</svg>
);
/** 다음 페이지 (Pagination 등) */
export const ChevronRightIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
<svg
className={cn("h-4 w-4", className)}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<path d="M9 18l6-6-6-6" />
</svg>
);
/** 아코디언·드롭다운 펼침 등 */
export const ChevronDownIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
<svg
className={cn("h-4 w-4", className)}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<path d="M19 9l-7 7-7-7" />
</svg>
);
/** 다시 시도 / 새로고침 (퀴즈 다시 풀기 등) */
export const ArrowPathIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
<svg
className={cn("h-4 w-4", className)}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<path d="M4 4v5h5M20 20v-5h-5" />
<path d="M19 9a8 8 0 00-14.32-2.5M5 15a8 8 0 0014.32 2.5" />
</svg>
);
/** 공유하기 (노드 연결 형태) */
export const ShareIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
<svg
className={cn("h-4 w-4", className)}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<circle cx="18" cy="5" r="3" />
<circle cx="6" cy="12" r="3" />
<circle cx="18" cy="19" r="3" />
<path d="M8.59 13.51l6.83 3.98M15.41 6.51l-6.82 3.98" />
</svg>
);
/** 드래그 손잡이 (세로 그립 점) */
export const GripVerticalIcon = ({ className, strokeWidth = 2 }: IconProps) => (
<svg
className={cn("h-4 w-4", className)}
viewBox="0 0 24 24"
fill="currentColor"
stroke="none"
strokeWidth={strokeWidth}
aria-hidden
>
<circle cx="9" cy="6" r="1.6" />
<circle cx="9" cy="12" r="1.6" />
<circle cx="9" cy="18" r="1.6" />
<circle cx="15" cy="6" r="1.6" />
<circle cx="15" cy="12" r="1.6" />
<circle cx="15" cy="18" r="1.6" />
</svg>
);
/** 홈 / 메인으로 */
export const HomeIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
<svg
className={cn("h-4 w-4", className)}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<path d="M3 10.5L12 3l9 7.5" />
<path d="M5 9.5V20a1 1 0 001 1h12a1 1 0 001-1V9.5" />
</svg>
);
/** 목록 / 문항 수 (퀴즈 문항 등) */
export const QueueListIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
<svg
className={cn("h-4 w-4", className)}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<path d="M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" />
</svg>
);
/** 시계 / 시간 제한 */
export const ClockIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
<svg
className={cn("h-4 w-4", className)}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<circle cx="12" cy="12" r="9" />
<path d="M12 7v5l3 2" />
</svg>
);
/** 물음표 원형 / 객관식·도움말 */
export const QuestionMarkCircleIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
<svg
className={cn("h-4 w-4", className)}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<circle cx="12" cy="12" r="9" />
<path d="M9.5 9.5a2.5 2.5 0 014.9.7c0 1.7-2.4 2.1-2.4 3.8" />
<path d="M12 17h.01" />
</svg>
);
/** 새싹 / 초급 (난이도) */
export const SproutIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
<svg
className={cn("h-4 w-4", className)}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<path d="M12 22V11" />
<path d="M12 11C12 7.5 9.5 5 5 5c0 4 2.5 6.5 7 6.5z" />
<path d="M12 9.5C12 6.5 14.2 4 18.5 4c0 3.5-2.2 6-6.5 6z" />
</svg>
);
/** 로켓 / 중급 (난이도) */
export const RocketIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
<svg
className={cn("h-4 w-4", className)}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 00-2.91-.09z" />
<path d="M12 15l-3-3a22 22 0 014-6.5C15.5 2.5 18 2 22 2c0 4-.5 6.5-3.5 9.5A22 22 0 0112 15z" />
<path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0" />
<path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5" />
</svg>
);
/** 불꽃 / 상급 (난이도) */
export const FireIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
<svg
className={cn("h-4 w-4", className)}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<path d="M8.5 14.5A2.5 2.5 0 0011 12c0-1.38-.5-2-1-3-1.07-2.14-.22-4.05 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 11-14 0c0-1.15.43-2.29 1-3a2.5 2.5 0 002.5 2.5z" />
</svg>
);
/** 수정 / 편집 (연필·문서, Heroicons 스타일) */
export const PencilSquareIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
<svg
className={cn("h-4 w-4", className)}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<path d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125" />
</svg>
);