관리자 NFC 카드 운영 API
관리자가 NFC 재고를 등록하고, 회원에게 발급하고, 명함에 연결하거나 삭제하는 API 명세입니다.
공통 규칙
- Base path:
/v1/admin - 인증:
Authorization: Bearer {accessToken} - 권한:
ROLE_ADMIN - 일반 사용자는
403 DB0004를 반환합니다. - 시간은 기존 API와 동일한
LocalDateTimeISO-8601 문자열입니다. - NFC 카드는 발급 회원과 연결 명함을 별도로 기록합니다.
Member 1 ── N NfcCard N ── 0..1 BusinessCardNFC 카드 식별 필드는 uuid가 아니라 기존 백엔드 용어인 cardUid를 사용합니다. 신규 발급 시 값은 서버가 UUID v4로 생성합니다.
상태와 삭제
| 상태 | 설명 |
|---|---|
IN_STOCK | 시스템에 등록됐지만 회원에게 발급되지 않은 재고 카드 |
UNCONNECTED | 회원에게 발급됐지만 명함에 연결되지 않은 카드 |
CONNECTED | 명함에 연결되어 사용 가능한 카드 |
INACTIVE | 일시적으로 사용하지 않는 카드 |
LOST | 분실 신고된 카드 |
REVOKED | 폐기된 카드 |
관리자 삭제는 상태 변경과 별개인 논리 삭제입니다. 삭제된 카드는 DB에 남지만 기본 조회·집계·태그 resolve에서 제외됩니다.
1. 운영 요약 조회
GET /v1/admin/card-operations/summaryResponse 200
{
"totalUsers": 5,
"totalNfcCards": 7,
"connectedNfcCards": 2,
"unconnectedNfcCards": 2,
"attentionNfcCards": 5
}totalUsers:ACTIVE상태인 일반·관리자 회원 수totalNfcCards: 논리 삭제되지 않은 카드 수attentionNfcCards:CONNECTED가 아닌 카드 수
2. 사용자 카드 레지스트리 조회
GET /v1/admin/users?query={검색어}&nfcCardStatus={상태}검색 대상은 이름, 이메일, 명함 slug, 카드 이름, cardUid입니다.
카드 상태와 보유 수량은 businessCardId가 아니라 assignedMemberId를 기준으로 집계합니다. 따라서 명함이 없는 회원에게 발급한 UNCONNECTED 카드도 결과에 포함됩니다.
Response 200
{
"users": [
{
"memberId": "member_01",
"displayName": "김민서",
"email": "minseo.kim@taple.team",
"role": "USER",
"joinedAt": "2026-07-02T03:12:00",
"businessCard": {
"id": "business_card_01",
"slug": "minseo-kim",
"published": true
},
"cardStatus": "CONNECTED",
"cardStatuses": ["CONNECTED", "INACTIVE"],
"lastTaggedAt": "2026-07-15T14:32:00",
"nfcCardCount": 2
}
]
}대표 cardStatus 우선순위는 다음과 같습니다.
CONNECTED > LOST > INACTIVE > UNCONNECTED > REVOKED > NONE3. 회원별 NFC 카드 조회
GET /v1/admin/members/{memberId}/nfc-cards회원에게 발급된 모든 카드를 assignedMemberId 기준으로 반환합니다. businessCardId가 없는 UNCONNECTED 카드도 포함합니다.
Response 200
{
"nfcCards": [
{
"id": "nfc_01",
"cardUid": "d3357c7d-190c-4e64-9d8e-434e7aa8231e",
"label": "메인 비즈니스 카드",
"status": "CONNECTED",
"assignedMemberId": "member_01",
"assignedAt": "2026-07-03T02:10:00",
"businessCardId": "business_card_01",
"businessCardSlug": "minseo-kim",
"activatedAt": "2026-07-03T02:10:00",
"lastTaggedAt": "2026-07-15T14:32:00"
}
]
}기존 GET /v1/admin/business-cards/{businessCardId}/nfc-cards는 명함에 연결된 카드만 조회하는 호환용 계약으로 유지합니다.
4. NFC 카드 목록 조회
GET /v1/admin/nfc-cards?status=UNCONNECTED&query={검색어}&unassignedOnly=true| 파라미터 | 필수 | 설명 |
|---|---|---|
status | 아니요 | NFC 카드 상태 |
query | 아니요 | 카드 이름 또는 cardUid 부분 검색 |
unassignedOnly | 아니요 | true면 businessCardId가 없는 카드만 조회 |
응답 형식은 명함별 NFC 카드 조회와 동일합니다.
5. NFC 카드 발급
POST /v1/admin/nfc-cards
Content-Type: application/json미발급 재고 등록
{
"label": "미발급 재고 카드",
"businessCardId": null
}명함에 즉시 연결하여 발급
{
"label": "메인 비즈니스 카드",
"businessCardId": "business_card_01"
}검증 규칙:
label: 공백 제거 후 필수, 최대 30자businessCardId: 선택값. 지정하면 존재하는 명함이어야 함cardUid: 요청으로 받지 않고 서버가 UUID v4 생성businessCardId가 없으면IN_STOCK, 지정하면 명함 소유자에게 발급 후CONNECTED
Response 201
{
"id": "nfc_01",
"cardUid": "e7f08a26-929f-4a23-a685-e0d356ad938a",
"label": "메인 비즈니스 카드",
"status": "CONNECTED",
"assignedMemberId": "member_01",
"assignedAt": "2026-07-15T15:40:00",
"businessCardId": "business_card_01",
"businessCardSlug": null,
"activatedAt": "2026-07-15T15:40:00",
"lastTaggedAt": null
}6. 재고 NFC 카드를 회원에게 발급
POST /v1/admin/nfc-cards/{cardId}/assign
Content-Type: application/json{
"memberId": "member_01"
}- 대상 회원 행을 비관적 잠금으로 조회하고
ACTIVE상태인지 확인합니다. - 카드 행을 비관적 잠금으로 조회합니다.
IN_STOCK만 발급할 수 있습니다.- 성공하면
UNCONNECTED로 변경하고assignedMemberId,assignedAt을 기록합니다. - 대상 회원이 없거나 탈퇴했으면
404 US0001을 반환합니다. - 발급할 수 없는 카드 상태면
409 NF0003을 반환합니다.
Response 200
발급된 NFC 카드 단건 응답을 반환합니다.
7. 미연결 NFC 카드 연결
POST /v1/admin/nfc-cards/{cardId}/connect
Content-Type: application/json{
"businessCardId": "business_card_01"
}- 카드 행을 비관적 잠금으로 조회합니다.
IN_STOCK이면 명함 소유자에게 발급하고 즉시 연결합니다.UNCONNECTED이면 발급 회원과 명함 소유자가 같아야 합니다.- 성공하면
CONNECTED로 변경하고activatedAt을 기록합니다. - 이미 연결됐거나 연결할 수 없는 상태면
409 NF0002를 반환합니다.
Response 200
NFC 카드 단건 응답을 반환합니다.
8. NFC 카드 삭제
DELETE /v1/admin/nfc-cards/{cardId}Response 204
본문은 없습니다. 같은 카드를 다시 삭제해도 204를 반환합니다. 존재하지 않는 카드 ID는 404 NF0001입니다.
오류 응답
기존 공통 형식을 사용합니다.
{
"code": "NF0002",
"message": "NFC card is not in a connectable state",
"data": null
}| 코드 | HTTP | 설명 |
|---|---|---|
DB0003 | 400 | 파라미터·본문 검증 실패 |
DB0004 | 403 | 관리자 권한 없음 |
US0001 | 404 | 발급할 회원이 없거나 탈퇴함 |
BC0001 | 404 | 명함을 찾을 수 없음 |
NF0001 | 404 | NFC 카드를 찾을 수 없음 |
NF0002 | 409 | NFC 카드를 연결할 수 없는 상태 |
NF0003 | 409 | NFC 카드를 발급할 수 없는 상태 |
VALIDATION_FAILED | 422 | 필수 입력값이 없거나 잘못됨 |
프론트 URL 조립
백엔드는 프론트 도메인이 포함된 URL을 반환하지 않습니다.
NFC 주소 = {frontendOrigin}/nfc/{cardUid}
공개 주소 = {frontendOrigin}/card/{businessCardSlug}현재 구현 범위
- 목록 API는 현재 전체 목록을 반환하며 페이지네이션은 적용하지 않았습니다.
- 카드 등록·회원 발급·연결·삭제에 대한 별도 관리자 감사 로그는 기록하지 않습니다.
- 요청 재시도를 위한 idempotency key는 지원하지 않습니다.
- 카드 상태를 임의로 변경하는 관리자 API는 이번 범위에 포함하지 않았습니다.