Member Directory
관리자용 컴포넌트관리자(MASTER)만 쓰는 화면에 쓰입니다.
검색, 필터, 페이지네이션을 갖춘 관리자 멤버 목록입니다.
멤버가 많아질 때 필요한 검색, 필터, 페이지 나누기를 한 덩어리로 묶은 관리자 목록입니다.MemberTable(표) 위에 MemberFilters(검색·필터)와 Pagination(페이지 이동)을 더했고, /admin/members가 이것을 씁니다.
Preview
27명의 목 데이터로 직접 시험해 보세요. 검색어를 입력하고, 역할·상태를 고르고, 페이지를 넘겨 보세요.
| ID | 아이디 | 이름 | 이메일 | 역할 | 상태 | 가입일 | |
|---|---|---|---|---|---|---|---|
| 1 | user01 | 사용자01 | user01@example.com | MASTER | 활성 | 2026-10-02 | |
| 2 | user02 | 사용자02 | user02@example.com | WRITER | 활성 | 2026-10-03 | |
| 3 | user03 | 사용자03 | user03@example.com | OWNER | 활성 | 2026-10-04 | |
| 4 | user04 | 사용자04 | user04@example.com | VIEWER | 활성 | 2026-10-05 | |
| 5 | user05 | 사용자05 | user05@example.com | WRITER | 비활성 | 2026-10-06 | |
| 6 | user06 | 사용자06 | user06@example.com | OWNER | 활성 | 2026-10-07 | |
| 7 | user07 | 사용자07 | user07@example.com | VIEWER | 삭제됨 | 2026-10-08 | |
| 8 | user08 | 사용자08 | user08@example.com | WRITER | 활성 | 2026-10-09 | |
| 9 | user09 | 사용자09 | user09@example.com | OWNER | 활성 | 2026-10-10 | |
| 10 | user10 | 사용자10 | user10@example.com | VIEWER | 비활성 | 2026-10-11 |
전체 27명 중 1–10
tsx
<MemberDirectory
query={query}
onQueryChange={(patch) => setQuery((q) => ({ ...q, ...patch }))}
data={data} // { items, total, page, size }
loading={isFetching}
onRoleChange={(id, roles) => updateRoles.mutate({ id, roles })}
onRemove={(id) => removeMember.mutate(id)}
/>What users can do
| 기능 | 동작 |
|---|---|
| 검색 | 아이디, 이름, 이메일에서 찾습니다. 입력을 멈추고 0.3초 뒤에 반영되어 글자마다 서버를 부르지 않습니다. |
| 역할 필터 | MASTER, OWNER, VIEWER, WRITER 중 하나만 보거나 전체를 봅니다. |
| 상태 필터 | 활성, 비활성, 삭제됨 중 하나만 보거나 전체를 봅니다. |
| 페이지 이동 | 이전/다음 버튼과 페이지 번호. 페이지가 많으면 현재 주변만 보이고 나머지는 …으로 줄입니다. |
| 페이지당 개수 | 10, 20, 50개 중에서 고릅니다. |
| 초기화 | 필터가 걸려 있을 때만 나타나는 버튼. 검색어와 필터를 한 번에 지웁니다. |
- 검색어나 필터를 바꾸면 자동으로 1페이지로 돌아갑니다. 3페이지를 보다가 검색했는데 빈 화면이 나오는 일을 막습니다.
- 새 결과가 오는 동안 이전 목록을 흐리게 유지해서 화면이 깜빡이지 않습니다.
- 결과가 없으면 안내 문구와
필터 초기화버튼을 보여 줍니다. - 아래에는 "전체 27명 중 1–10"처럼 현재 범위를 표시합니다.
How data flows
MemberFilters (입력) ──► query { q, roleId, status, page, size }
│
▼
GET /users?q=&role_id=&status=&page=&size=
│
▼
{ items: User[], total, page, size } ──► MemberTable + Pagination조건(query)은 부모가 들고 있고, MemberDirectory는 값을 받아 보여 주고 바뀐 부분만 onQueryChange로 알려 줍니다. 서버 조회든 목 데이터든 상관없이 쓸 수 있습니다.
AdminMembersPage.tsx
const [query, setQuery] = useState(defaultMemberQuery);
const onQueryChange = useCallback(
(patch) => setQuery((q) => ({ ...q, ...patch })),
[],
);
const { data, isFetching } = useMembers(query);
<MemberDirectory query={query} onQueryChange={onQueryChange} data={data} loading={isFetching} ... />onQueryChange는 useCallback으로 감싸 같은 함수를 유지하세요. 매번 새 함수가 만들어지면 검색 입력의 지연 처리가 계속 초기화됩니다.API
GET /users
GET /users?q=kim&role_id=3&status=active&page=2&size=10
// response 200
{
"items": [ { "id": 11, "user_id": "user11", ... } ],
"total": 27,
"page": 2,
"size": 10
}| 파라미터 | 타입 | 설명 |
|---|---|---|
q | string | 아이디·이름·이메일에 포함된 글자. 비어 있으면 보내지 않습니다. |
role_id | number | 역할 필터. 전체면 보내지 않습니다. |
status | "active" | "inactive" | "deleted" | 상태 필터. 전체면 보내지 않습니다. |
page | number | 1부터 시작하는 페이지 번호. |
size | number | 페이지당 개수 (10, 20, 50). |
검색·필터·페이지 나누기는 서버가 해야 합니다. 전체를 받아 브라우저에서 거르면 멤버가 많아질 때 느려지고 불필요한 데이터가 노출됩니다. 위 미리보기의 브라우저 필터는 서버 없이 화면을 보여 주기 위한 용도입니다.
Pagination
페이지 이동 버튼만 따로 쓸 수도 있는 shadcn 스타일 컴포넌트입니다. 현재 페이지 주변만 보여 주고 나머지는 …로 줄입니다.
tsx
<Pagination page={page} pageCount={12} onPageChange={setPage} />- 7쪽 이하는 전부, 그보다 많으면
1 … 4 5 [6] 7 8 … 12형태로 보여 줍니다. - 첫/마지막 페이지에서는 이전/다음 버튼이 잠깁니다. 페이지가 1개뿐이면 아무것도 그리지 않습니다.
- 현재 페이지는
aria-current="page"로 표시되어 스크린리더가 알립니다.
Props
MemberDirectory props
| Prop | Type | 설명 |
|---|---|---|
query필수 | MemberQuery | 현재 조건 { q, roleId, status, page, size }. |
onQueryChange필수 | (patch: Partial<MemberQuery>) => void | 조건이 바뀌면 바뀐 부분만 전달합니다. |
data | Page<User> | 현재 조건에 맞는 한 페이지와 전체 개수 { items, total, page, size }. |
loading | boolean | 조회 중이면 이전 목록을 흐리게 보여 줍니다. |
onRoleChange필수 | (id, roleId) => void | 역할이 바뀔 때. |
onRemove필수 | (id) => void | 탈퇴 처리를 눌렀을 때. |
Pagination props
| Prop | Type | 설명 |
|---|---|---|
page필수 | number | 현재 페이지 (1부터). |
pageCount필수 | number | 전체 페이지 수. |
onPageChange필수 | (page: number) => void | 페이지를 고르면 호출합니다. |
Customization
- 검색 지연 시간은
<MemberFilters debounceMs={500} />처럼 바꿉니다 (기본 300ms). - 페이지당 개수 선택지는
admin/members-filter.ts의SIZE_OPTIONS입니다. - 상태 필터의 항목은 같은 파일의
STATUS_OPTIONS에서 고칩니다. - 컬럼을 더하려면
MemberTable에TableHead·TableCell한 쌍을 추가하세요.
Related API
이 컴포넌트가 호출하는 API입니다. 누르면 요청·응답 명세로 이동합니다.
