Build your component library - shadcn/ui
A set of beautifully-designed, accessible components and a code distribution platform. Works with your favorite frameworks. Open Source. Open Code.
ui.shadcn.com
샤드씨엔은 매우 간편한 라이브버리로, 위 공식문서에 나와있는대로 세팅하면 됩니다.
지난 인턴생활하면서 가장 많이 활용한 라이브러리인데, 정리를 해볼까 합니다.
React vite typescript 기반 개발을 할 때에 ui요소 중 Input, Button, Form, FormItem등 사용해봤지만 재미있던 건 Accordion입니다. 아래에 나와있는 링크에 들어가보면 어떻게 활용해야하는지 코드로 나와있습니다.
https://ui.shadcn.com/docs/components/accordion
Accordion
A vertically stacked set of interactive headings that each reveal a section of content.
ui.shadcn.com
제가 쓴 패키지매니저로는 pnpm이며 명령어는 아래와 같습니다.
pnpm dlx shadcn@latest add accordion
이러면 ui/common에 자동으로 생성이 됩니다.
자동생성된 코드를 보면 아래와 같습니다.
'use client'
import * as AccordionPrimitive from '@radix-ui/react-accordion'
import { cn } from '@ui/common/lib/utils'
import { ChevronDownIcon } from 'lucide-react'
import * as React from 'react'
function Accordion({ ...props }: React.ComponentProps<typeof AccordionPrimitive.Root>) {
return <AccordionPrimitive.Root data-slot="accordion" {...props} />
}
function AccordionItem({ className, ...props }: React.ComponentProps<typeof AccordionPrimitive.Item>) {
return (
<AccordionPrimitive.Item
data-slot="accordion-item"
className={cn('border-b last:border-b-0', className)}
{...props}
/>
)
}
function AccordionTrigger({ className, children, ...props }: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
return (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
data-slot="accordion-trigger"
className={cn(
'flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180',
className
)}
{...props}
>
{children}
<ChevronDownIcon className="pointer-events-none size-4 shrink-0 translate-y-0.5 text-muted-foreground transition-transform duration-200" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
)
}
function AccordionContent({ className, children, ...props }: React.ComponentProps<typeof AccordionPrimitive.Content>) {
return (
<AccordionPrimitive.Content
data-slot="accordion-content"
className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
{...props}
>
<div className={cn('pt-0 pb-4', className)}>{children}</div>
</AccordionPrimitive.Content>
)
}
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
공식문서에서는 아래와 같이 사용하면 된다고 나와있습니다.
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion"
<Accordion type="single" collapsible>
<AccordionItem value="item-1">
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>
Yes. It adheres to the WAI-ARIA design pattern.
</AccordionContent>
</AccordionItem>
</Accordion>
위 코드를 참고하여 입맛에 맛게 조리해주면 됩니다.
아래 코드는 제가 적용한 실제 코드입니다.
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@ui/common/components/accordion'
export default function FaqList({
filteredItems,
}: {
filteredItems: { id: number; category: string; title: string; answer: string }[]
}) {
return (
<div className="w-full max-w-(--content-width) rounded-lg border border-gray-06">
{filteredItems.map((faq) => {
return (
<div key={faq.id} className="flex flex-col border-b border-gray-05 px-6 py-4">
<Accordion type="single" className="border-0" collapsible>
<AccordionItem value="item-1">
<div className="flex items-center justify-between">
<div className="flex text-body-01 font-semibold">
<p className="mr-2 text-gray-03">[{faq.category}]</p>
<p>{faq.title}</p>
</div>
<AccordionTrigger></AccordionTrigger>
</div>
<AccordionContent>
<div className="mt-5 flex text-body-02 text-gray-03">
<p className="mr-2 text-body-01 font-bold text-key">A.</p>
{faq.answer}
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
)
})}
</div>
)
}
이밖에 써먹은 shadcn - Form, FromItem
import { Button, Input, LineTabs } from '@/shared/components'
import { useNavigate } from '@tanstack/react-router'
import { useState } from 'react'
import { useForm } from 'react-hook-form'
import { z } from 'zod'
import { zodResolver } from '@hookform/resolvers/zod'
import { Form, FormItem } from '@ui/common/components/form'
const findSchema = z.object({
name: z.string().nonempty('이름을 입력해주세요'),
number: z.string().nonempty('휴대폰 번호를 입력해주세요'),
})
type FormValues = z.infer<typeof findSchema>
export function AccountFindForm() {
const [activeTab, setActiveTab] = useState('일반회원')
const navigate = useNavigate()
const mockAccounts = [{ id: 'ACEID', name: '홍길동', phone: '01012345678', value: '일반회원' }]
const form = useForm<FormValues>({
resolver: zodResolver(findSchema),
defaultValues: { name: '', number: '' },
mode: 'onChange',
})
const onSubmit = (data: FormValues) => {
const found = mockAccounts.find(
(acc) => acc.name === data.name && acc.phone === data.number && acc.value === activeTab
)
if (found) {
navigate({
to: '/auth/find-account/complete',
state: ((prev) => ({ id: found.id })) as any,
})
}
}
return (
<section>
<div className="flex flex-col items-center justify-center">
<h1 className="mb-2 text-title-01 font-semibold">아이디 찾기</h1>
<h6 className="mb-4 text-body-02">
이름과 {activeTab === '일반회원' ? '휴대폰 번호' : '사업자등록번호'}를 입력해주세요
</h6>
</div>
<div className="mb-7">
<LineTabs
tabs={[
{ label: '일반회원', value: '일반회원' },
{ label: '기업회원', value: '기업회원' },
]}
activeTab={activeTab}
setActiveTab={setActiveTab}
/>
</div>
<Form form={form} onSubmit={onSubmit} className="space-y-7">
<FormItem
name="name"
label="이름"
labelClassName="mb-2 text-body-02 font-semibold"
messageClassName="mt-1 text-body-02 text-[#F24B4E]"
circleX={false}
>
<Input placeholder="이름을 입력해주세요" />
</FormItem>
<FormItem
name="number"
label={activeTab === '일반회원' ? '휴대폰 번호' : '사업자등록번호'}
labelClassName="mb-2 text-body-02 font-semibold"
messageClassName="mt-1 text-body-02 text-[#F24B4E]"
circleX={false}
>
<Input type="number" placeholder="숫자만 입력 가능" />
</FormItem>
<Button className="bg-key" type="submit" disabled={!form.formState.isValid}>
확인
</Button>
</Form>
</section>
)
}
'프론트엔드 역량 > CS 기초, 알고리즘, 자료구조, 시스템 디자인' 카테고리의 다른 글
[CS 기초] 1티어_완전 기초 간단 정리! (0) | 2025.04.02 |
---|---|
[HTML5] 시맨틱 태그란? (0) | 2025.04.02 |
[라이브러리] tanstack-react-router (0) | 2025.04.02 |
[CS 기초] 기본 재귀 (Recursion) (0) | 2025.04.02 |
[CS 기초] 정렬 및 탐색 알고리즘 (0) | 2025.04.02 |