컴포넌트/아바타, 페르소나

Persona (페르소나)

VanillaFront 2026. 9. 20. 16:25

Va.Persona — 아바타 + 이름·역할 조합 컴포넌트

Va.Avatar를 감싸 이름(title)과 부가 설명(detail)을 함께 표시하는 조합 컴포넌트입니다. "사용자 사진 + 이름 + 직책" 처럼 사람 정보를 한 단위로 표현하는 데 최적화되어 있으며, 텍스트 위치를 자유롭게 배치할 수 있습니다.

 

클래스 정보

Va.Persona extends Va.Component
tagName: 'persona' (실제 렌더링은 <div>)
isContainer: true
파일: va_avatar.js

기본 사용법

{
    tagName: 'persona',
    title: '홍길동',
    detail: '개발팀장',
    textPosition: 'right',
    avatar: {
        image: user.photoUrl,
        size: 'medium'
    }
}

또는 코드로:

let persona = new Va.Persona({
    title: '홍길동',
    detail: 'hong@example.com',
    avatar: { initial: 'HG', backgroundColor: '#4A90E2', color: 'white' }
});
parent.append(persona);

왜 별도 컴포넌트인가

Avatar + Title + SubTitle을 매번 조합해 만들 수도 있지만, Va.Persona는:

  • 표준 사람 표현 단위 — "누구" 를 한 컴포넌트로 명확히 표현
  • 위치 배치 자동  textPosition 하나로 top/bottom/left/right 자유 전환
  • Avatar 이벤트 프록시 — 아바타·뱃지 클릭 이벤트를 상위에서 통일 처리
  • 활성 상태 연동 — Persona의 actived가 내부 Avatar에 자동 반영

속성

속성타입기본값설명

title String '' 주 텍스트 (이름/직책)
detail String '' 부가 텍스트 (설명/역할)
textPosition String 'right' 텍스트 위치: 'top', 'bottom', 'left', 'right'
avatar Object 내부 Va.Avatar에 전달할 옵션
size String 'medium' 크기 (아바타에 자동 전파)
actived Boolean false 활성 상태 (아바타에 반영)
disabled Boolean false 비활성 상태
stopPropagation Boolean true 이벤트 전파 차단

textPosition별 레이아웃

값배치

'right' (기본) [👤] 홍길동 개발팀장 — 아바타 왼쪽
'left' 홍길동 개발팀장 [👤] — 아바타 오른쪽
'top' 홍길동<br>개발팀장<br>[👤] — 아바타 아래
'bottom' [👤]<br>홍길동<br>개발팀장 — 아바타 위 (프로필 카드 스타일)

이벤트

이벤트발생 시점콜백 인자

click Persona 전체 클릭 (sender, element, evt)
contextmenu 우클릭 (sender, element, evt)
avatarClick 내부 아바타만 클릭 (sender, element, evt)
avatarContextmenu 내부 아바타 우클릭 (sender, element, evt)
badgeClick 아바타 뱃지 클릭 (sender, element, evt)
badgeContextmenu 뱃지 우클릭 (sender, element, evt)

주목: click(전체)과 avatarClick(아바타만)이 별도로 dispatch되므로, "아바타 클릭 = 프로필 이동, 전체 클릭 = 상세 열기" 같은 분기 가능.

메서드

  • setTitle(title) — 주 텍스트 변경
  • setDetail(detail) — 부가 텍스트 변경
  • setAvatar(avatar) — 아바타 컴포넌트 교체
  • hideAvatar() / showAvatar() — 아바타 표시/숨김
  • active() / inactive() — 활성 상태 제어
  • focus() / blur() — 포커스 (내부 아바타에 위임)

내부 구조

<div class="va-persona medium right">
    <div class="persona-inner" elname="inner">
        <span class="va-avatar circular">...</span>  <!-- 내부 Avatar -->
        <div class="content" elname="content">
            <div class="title">홍길동</div>
            <div class="detail">개발팀장</div>
        </div>
    </div>
</div>

textPosition에 따라 avatar와 content의 순서가 재배열됩니다.

사용 시점 — 언제 쓰나

  • 사용자 리스트 — 팀원 목록, 회원 목록
  • 댓글/메시지 헤더 — 작성자 정보
  • 드롭다운 메뉴 헤더 — 로그인 사용자 표시
  • 협업 툴 참여자 — 프로젝트 멤버 카드
  • 프로필 카드 — 명함 스타일 UI

사용하지 말아야 할 때

  • 아바타만 필요  Va.Avatar 단독
  • 여러 명 콤팩트 표시  Va.AvatarGroup
  • 정형 사용자 테이블  Va.Grid
  • 텍스트 없는 순수 이미지  Va.Image

자주 쓰는 조합 예시

사용자 리스트 (오른쪽 텍스트)

{
    tagName: 'section',
    layout: 'ds-flex fd-column gap-s',
    tags: users.map(user => ({
        tagName: 'persona',
        title: user.name,
        detail: user.email,
        textPosition: 'right',
        size: 'medium',
        avatar: {
            image: user.photo,
            initial: user.initial
        },
        onClick: 'onClickUser'
    }))
}

프로필 카드 (아바타 위, 텍스트 아래)

{
    tagName: 'div',
    layout: 'ds-flex fd-row fw-wrap gap-m',
    tags: team.map(member => ({
        tagName: 'persona',
        title: member.name,
        detail: member.role,
        textPosition: 'bottom',
        size: 'large',
        avatar: {
            image: member.photo,
            shape: 'circular'
        },
        style: {
            padding: '15px',
            border: '1px solid var(--colorNeutralStroke)',
            borderRadius: 'var(--sizeRadiusM)',
            width: '180px',
            textAlign: 'center'
        }
    }))
}

드롭다운 메뉴 헤더 (로그인 사용자)

{
    tagName: 'menu',
    tags: [{
        tagName: 'persona',
        title: currentUser.name,
        detail: currentUser.email,
        textPosition: 'right',
        avatar: { image: currentUser.photo },
        style: { padding: '10px', borderBottom: '1px solid var(--colorNeutralStroke)' }
    },{
        tagName: 'menuItem', text: '내 프로필'
    },{
        tagName: 'menuItem', text: '설정'
    },{
        tagName: 'menuItem', text: '로그아웃'
    }]
}

Avatar와 전체 각각 다른 액션

{
    tagName: 'persona',
    title: '홍길동',
    detail: '개발팀',
    avatar: { image: user.photo },
    onClick: 'onClickPersona',           // 전체 클릭 = 상세 열기
    onAvatarClick: 'onClickAvatar'       // 아바타만 클릭 = 프로필로 이동
}

// View 안:
onClickPersona(sender){
    this.openUserDetail(sender.title);
}
onClickAvatar(sender){
    this.navigateToProfile(sender.title);
}

활성 상태 표시 (온라인 등)

{
    tagName: 'persona',
    title: user.name,
    detail: user.isOnline ? '온라인' : '오프라인',
    actived: user.isOnline,
    avatar: { image: user.photo }
}

뱃지 알림 카운트

{
    tagName: 'persona',
    title: user.name,
    detail: '메시지 3건',
    avatar: {
        image: user.photo,
        badgeVisible: true,
        badge: { text: '3', appearance: 'danger' }
    },
    onBadgeClick: 'onClickMessages'
}

실전 예시 — 팀 대시보드

config(){
    return {
        tagName: 'page',
        tags: [{
            tagName: 'panel',
            tags: [{
                tagName: 'title',
                innerHTML: '개발팀 (전체 12명)'
            },{
                tagName: 'div',
                layout: 'ds-flex fd-row fw-wrap gap-m',
                style: { marginTop: '20px' },
                tags: this.team.map(member => ({
                    tagName: 'persona',
                    title: member.name,
                    detail: `${member.role} · ${member.status}`,
                    textPosition: 'bottom',
                    size: 'large',
                    actived: member.isOnline,
                    avatar: {
                        image: member.photo,
                        initial: member.initial,
                        backgroundColor: member.color,
                        color: 'white'
                    },
                    style: {
                        padding: '20px',
                        width: '180px',
                        textAlign: 'center',
                        border: '1px solid var(--colorNeutralStroke)',
                        borderRadius: 'var(--sizeRadiusM)',
                        cursor: 'pointer'
                    },
                    onClick: 'onClickMember',
                    onAvatarClick: 'onClickMemberAvatar'
                }))
            }]
        }]
    };
}

주의사항

  • avatar 옵션은 객체로 전달 — 내부에서 Va.Avatar를 생성. 이미 만든 Avatar 인스턴스를 넣는 게 아님
  • setAvatar() 메서드 두 개 정의됨 — 소스에 오버라이드된 두 버전이 있음 (va_avatar.js:647, va_avatar.js:657). 뒤의 것이 실제 실행됨. 새 Avatar 인스턴스 세팅용
  • size 자동 전파 — Persona의 size가 내부 아바타에 자동 반영. 아바타 개별 size 지정 시엔 그 값 우선
  • 텍스트만 필요하면 Persona 부적합 — 아바타 없이는 의미가 없으므로 Va.Div + Va.Title/Va.SubTitle 조합 사용
  • stopPropagation 기본 true — 리스트 아이템으로 사용 시 부모 클릭도 필요하면 stopPropagation: false
  • 이벤트 분리 활용 — click(전체)과 avatarClick(아바타만) 분리 가능. UX 상 유용
  • actived는 아바타에만 반영 — Persona 자체의 시각적 활성 표시는 별도로 style에서 처리 필요
  • detail 여러 줄 — 자동 줄바꿈 안 됨. 필요 시 \n 대신 style로 whiteSpace: 'pre-line' 등 처리

대안 비교

상황추천

아바타 + 이름·역할 표시 Va.Persona
아바타 단독 Va.Avatar
여러 사용자 콤팩트 Va.AvatarGroup
정형 사용자 테이블 Va.Grid
이름만 필요 Va.Label
카드 스타일 자유 구성 Va.Div + Avatar + Title 조합

참고