Claude Code · claude-opus-5-5 · React (TSX)
자료실 목록도 같은 형태라 데이터만 바꿔 쓸 수 있습니다.
결과 코드펼치기
import { useMemo, useState, type FormEvent } from "react";
type Post = { id: number; title: string; date: string; views: number; pin?: boolean };
const POSTS: Post[] = [
{ id: 12, title: "가을 학기 클래스 일정 및 수강료", date: "2026.09.01", views: 341, pin: true },
{ id: 11, title: "11월 야외 출사 일정 안내", date: "2026.08.24", views: 158 },
{ id: 10, title: "수강생 사진전 출품작 접수", date: "2026.08.10", views: 203 },
{ id: 9, title: "스튜디오 대관 운영 시간 변경", date: "2026.07.18", views: 127 },
{ id: 8, title: "장비 대여 규정 안내", date: "2026.06.30", views: 189 },
{ id: 7, title: "여름 휴강 기간 안내", date: "2026.06.12", views: 142 },
];
const FONT = '"Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Malgun Gothic", sans-serif';
export default function NoticeBoard() {
const [draft, setDraft] = useState("");
const [keyword, setKeyword] = useState("");
const posts = useMemo(() => POSTS.filter((p) => (keyword ? p.title.toLowerCase().includes(keyword.toLowerCase()) : true)), [keyword]);
const onSearch = (e: FormEvent) => {
e.preventDefault();
setKeyword(draft.trim());
};
return (
<section className="w-full bg-white" style={{ fontFamily: FONT }}>
<div className="mx-auto w-full max-w-[1200px] px-4 py-12 sm:px-6 lg:py-16">
<div className="mb-5 flex flex-wrap items-center justify-between gap-4">
<p className="text-[15px] text-[#8a93a3]">
전체 <strong className="text-[#1B263B]">{posts.length}</strong>건
</p>
<form onSubmit={onSearch} role="search" className="flex w-full gap-1.5 rounded-full bg-[#f4f6f9] p-[5px] sm:w-auto">
<label htmlFor="notice-keyword" className="sr-only">
검색어
</label>
<input
id="notice-keyword"
type="search"
value={draft}
onChange={(e) => setDraft(e.target.value)}
placeholder="제목 검색"
className="h-[42px] min-w-0 flex-1 rounded-full bg-transparent px-4 text-[15px] outline-none focus:bg-white sm:w-60 sm:flex-none"
/>
<button type="submit" className="h-[42px] rounded-full bg-[#1B263B] px-[22px] font-bold text-white">
검색
</button>
</form>
</div>
{posts.length === 0 ? (
<p className="rounded-2xl bg-[#f4f6f9] py-20 text-center text-[#8a93a3]">등록된 글이 없습니다.</p>
) : (
<table className="w-full border-collapse tabular-nums">
<caption className="sr-only">공지사항 목록</caption>
<thead>
<tr className="text-sm font-bold text-white">
<th scope="col" className="hidden w-20 rounded-l-[10px] bg-[#1B263B] p-3.5 sm:table-cell">번호</th>
<th scope="col" className="rounded-l-[10px] bg-[#1B263B] p-3.5 sm:rounded-none">제목</th>
<th scope="col" className="w-[92px] rounded-r-[10px] bg-[#1B263B] p-3.5 sm:w-[130px] sm:rounded-none">작성일</th>
<th scope="col" className="hidden w-[90px] rounded-r-[10px] bg-[#1B263B] p-3.5 sm:table-cell">조회</th>
</tr>
</thead>
<tbody>
{posts.map((p, i) => {
const number = posts.filter((q) => !q.pin).length - posts.slice(0, i).filter((q) => !q.pin).length;
return (
<tr key={p.id} className={`border-b border-[#e4e7ed] text-center text-[15px] text-[#8a93a3] ${p.pin ? "bg-[#fff7dc]" : ""}`}>
<td className="hidden px-3.5 py-5 sm:table-cell">{p.pin ? "" : number}</td>
<td className="px-2 py-4 text-left sm:px-3.5 sm:py-5">
<a href={`/notice/${p.id}`} className="group flex flex-col items-start gap-1.5 sm:flex-row sm:items-center sm:gap-2.5">
{p.pin && <span className="inline-flex h-[26px] items-center rounded-full bg-[#F5C842] px-2.5 text-xs font-bold text-[#1B263B]">공지</span>}
<span className="text-base font-semibold text-[#172031] group-hover:text-[#3D64B1]" style={{ wordBreak: "keep-all" }}>
{p.title}
</span>
</a>
</td>
<td className="px-2 py-4 text-[13px] sm:px-3.5 sm:py-5 sm:text-[15px]">{p.date}</td>
<td className="hidden px-3.5 py-5 sm:table-cell">{p.views}</td>
</tr>
);
})}
</tbody>
</table>
)}
</div>
</section>
);
}
적용 사이트 0
이 블록이 실제로 쓰인 사이트입니다.
아직 등록된 사이트가 없습니다. 이 블록을 쓴 사이트가 있다면 알려 주세요.
재현 후기
v1 · 0개아직 이 버전의 프롬프트로 직접 만들어 본 사람이 없습니다. 첫 번째로 재현해 보세요.