Skip to content

[Feat] 회원 전자 명함 기능 구현 #1129

Description

@kyeoungwoon

어떤 기능이 필요한가요?

모바일 앱의 전자 명함 기능을 member 도메인 내부 기능으로 구현해야 합니다.

전자 명함은 회원의 공개 프로필, 링크, 교환 요청, 저장/수집 이력, 오프라인 동기화를 다루므로 별도 최상위 businesscard 도메인으로 빼기보다 member 도메인의 profile 확장 영역으로 두는 것이 자연스럽습니다. 다만 기존 MemberProfile에 단순 필드만 추가하지 말고, 1인 1장 제약과 교환/가시성 정책을 별도 aggregate로 분리합니다.

🔧 TODO

  • member 도메인 안에 전자 명함 모델을 추가한다.
    • 예시: MemberBusinessCard, MemberBusinessCardFieldVisibility, MemberBusinessCardExchange, MemberBusinessCardCollection
    • cross-domain 참조는 memberId, scheduleId, activityId 같은 ID만 저장한다.
    • 기존 Member, MemberProfile entity에 @OneToMany 컬렉션을 추가하지 않는다.
  • 1인 1장 정책을 DB와 도메인에서 보장한다.
    • member_business_card.member_id unique index
    • 최초 생성과 수정 command 분리
    • 삭제 대신 비활성화/숨김이 필요한지 결정
  • 명함 필드와 가시성 정책을 설계한다.
    • 기본 필드: 이름, 닉네임, 학교, 파트, 기수, 프로필 이미지, 한 줄 소개, 링크 목록
    • 확장 필드: 이메일, 전화번호, 소속, 관심사 등 민감도 높은 필드는 별도 동의/노출 정책 필요
    • visibility enum: PUBLIC, EXCHANGE_ONLY, PRIVATE
    • 서버 응답에서 요청자 관계에 따라 필드를 필터링한다.
  • 명함 CRUD API를 추가한다.
    • GET /api/v1/members/me/business-card
    • POST /api/v1/members/me/business-card
    • PATCH /api/v1/members/me/business-card
    • GET /api/v1/members/business-cards/{memberId} 또는 username 기반 public path
    • response DTO는 entity를 직접 노출하지 않는다.
  • 공개 웹 명함 URL 정책을 정한다.
    • username/publicSlug unique constraint
    • GET /card/{username}을 서버가 직접 렌더링할지, API만 제공하고 프론트 라우팅으로 처리할지 결정
    • private field가 public URL에 절대 노출되지 않도록 query/service test 추가
  • 양방향 명함 교환 요청을 구현한다.
    • POST /api/v1/members/business-cards/exchanges
    • PATCH /api/v1/members/business-cards/exchanges/{exchangeId} accept/reject/cancel
    • GET /api/v1/members/business-cards/exchanges?status=
    • 만료 시간, 중복 요청, 차단/거절 후 재요청 정책
  • 활동 참여자 일괄 명함 받기 기능을 구현한다.
    • schedule/activity 참여자 목록은 schedule/organization/project Query UseCase로 조회한다.
    • POST /api/v1/members/business-cards/collections/bulk
    • 이미 저장한 명함은 idempotent하게 처리한다.
  • 오프라인 큐잉/동기화 API를 구현한다.
    • POST /api/v1/members/business-cards/sync/flush
    • client operation idempotency key 필수
    • BLE 교환 후 온라인 복귀 시 교환 이력 업로드 가능
    • 같은 상대와 중복 저장/교환되는 케이스를 서버에서 병합
  • 알림과 연결한다.
    • 교환 요청/수락/만료 시 notification 이벤트 발행
    • FCM payload type 예시: BUSINESS_CARD_EXCHANGE_REQUESTED, BUSINESS_CARD_EXCHANGE_ACCEPTED
  • 보안/개인정보 정책을 반영한다.
    • 본인 명함 전체 조회와 타인 명함 조회 response를 분리
    • EXCHANGE_ONLY 필드는 교환 완료 관계에서만 노출
    • block/mute 정책이 도입되면 교환 요청 생성 차단
    • audit log가 필요한 command 범위 결정
  • 테스트와 문서를 추가한다.
    • 1인 1장 unique 정책
    • visibility filtering
    • 교환 요청 상태 전이
    • 만료/중복/idempotency
    • public URL 개인정보 비노출
    • bulk save/offline flush
    • RestDocs/OpenAPI

📎 참고 자료

  • 모바일 신기능 계획서: 2기 방학 중 앱 신기능 개발
  • 현재 서버 상태: member에는 MemberProfile이 있지만, 전자 명함/교환/수집 이력 모델은 없습니다.
  • 구현 원칙: 최상위 businesscard 도메인이 아니라 member 도메인 내부에 둡니다.

✅ 제출 전 확인사항

  • 동일하거나 유사한 이슈가 없는지 확인했습니다.
  • 적절한 라벨과 Assignee를 설정했습니다.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions