컴포넌트/패널 외 영역 (Div, Span)

SearchPanel (조회패널)

VanillaFront 2026. 9. 18. 20:32

Va.SearchPanel — 조회 조건 전용 패널 컨테이너

목록/그리드 화면 상단의 "조회 조건 입력 영역"을 위한 전용 패널입니다. Va.Semantic을 상속받아 <section> 태그로 렌더링되며, va-search-panel 클래스가 부여되어 테두리·상단 강조선·hover 그림자 등 검색 폼 전용 시각 스타일이 자동 적용됩니다.

클래스 정보

Va.SearchPanel extends Va.Semantic extends Va.Component
tagName: 'searchPanel' (실제 렌더링은 <section>)
isContainer: true

기본 사용법

{
    tagName: 'searchPanel',
    layout: 'ds-flex fd-row ai-center gap-s',
    tags: [
        { tagName: 'inputField', label: '이름', ref: 'refName' },
        { tagName: 'inputField', label: '부서', ref: 'refDept' },
        { tagName: 'div', style: { flex: 1 } },
        { tagName: 'button', text: '조회', appearance: 'primary', onClick: 'onClickSearch' }
    ]
}

왜 별도 컴포넌트인가

동일한 결과를 Va.Panel이나 Va.Div로도 만들 수 있지만, Va.SearchPanel은:

  • 일관된 시각적 신호 — 상단에 primary 색 강조선(2px), 테두리, 배경색이 자동 부여되어 "이 영역은 조회 조건이다"라는 신호를 명확히 전달
  • hover 인터랙션 — 마우스 올리면 그림자가 부드럽게 나타남 (transition 포함)
  • 테마 대응 — Light/Dark/Paper 등 모든 테마에서 검색 패널 전용 스타일 자동 적용
  • 의미 전달 — 코드만 봐도 "여기가 검색 조건 영역"임이 명확
  • B2B 화면 표준화 — CRUD 화면에서 검색 폼 위치·룩앤필이 프로젝트 전체에서 일관됨

자동 적용되는 CSS

.va-search-panel {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    border: 0.1rem solid var(--colorPanelBorder);
    border-radius: var(--sizeBorderM);
    background-color: var(--colorPanel);
}

/* light 테마 확장 */
.va-search-panel {
    border-top: 2px solid var(--colorPrimary);  /* 상단 강조선 */
    transition: box-shadow var(--lt-duration-slow) var(--lt-easing);
}
.va-search-panel:hover {
    box-shadow: var(--lt-shadow-1);
}

기본은 세로 방향 (flex-direction: column)이므로 가로 정렬을 원하면 layout: 'ds-flex fd-row ...'로 override 필요.

속성

Va.Semantic으로부터 상속:

속성타입설명

text String 순수 텍스트 (자동 이스케이프)
innerHTML String HTML 문자열
layout, style, class, hidden, attributes 공통 속성

이벤트

Va.Semantic으로부터 상속 (기본 DOM 이벤트).

메서드

  • setText(text) — 텍스트 변경 후 업데이트
  • setInnerHTML(html) — HTML 변경 후 업데이트
  • Va.Component / Va.Semantic 공통 메서드

내부 구조

<section class="va-search-panel">
    <!-- containerElement에 필드 + 버튼 등 -->
</section>

사용 시점 — 언제 쓰나

  • CRUD 화면의 조회 조건 영역 — 목록/그리드 위쪽의 검색 폼
  • 필터 폼 — 상세 검색, 다중 조건 필터링
  • 리포트 조회 조건 — 날짜 범위, 분류, 조건 선택 후 조회

사용하지 말아야 할 때

  • 일반적인 폼 입력  Va.Panel 또는 Va.Div
  • 화면 최상위  Va.Page
  • 탭 인터페이스  Va.Tab
  • 입력 폼 자체 → SearchPanel은 컨테이너, 필드는 Va.InputField 

자주 쓰는 조합 예시

단일 행 검색 폼

{
    tagName: 'searchPanel',
    layout: 'ds-flex fd-row ai-center gap-s',
    style: { padding: '10px' },
    tags: [
        { tagName: 'inputField', label: '이름', ref: 'refName' },
        { tagName: 'comboboxField', label: '부서', ref: 'refDept', items: [/* ... */] },
        { tagName: 'div', style: { flex: 1 } },
        { tagName: 'button', text: '조회', appearance: 'primary', onClick: 'onClickSearch' },
        { tagName: 'button', text: '초기화', onClick: 'onClickReset' }
    ]
}

다중 행 검색 폼

{
    tagName: 'searchPanel',
    style: { padding: '15px' },
    tags: [{
        tagName: 'div',
        layout: 'ds-flex fd-row gap-s',
        tags: [
            { tagName: 'inputField', label: '이름', ref: 'refName' },
            { tagName: 'inputField', label: '이메일', ref: 'refEmail' }
        ]
    },{
        tagName: 'div',
        layout: 'ds-flex fd-row gap-s',
        style: { marginTop: '10px' },
        tags: [
            { tagName: 'dateField', label: '가입일 시작', ref: 'refStartDate' },
            { tagName: 'dateField', label: '가입일 종료', ref: 'refEndDate' },
            { tagName: 'div', style: { flex: 1 } },
            { tagName: 'button', text: '조회', appearance: 'primary', onClick: 'onClickSearch' }
        ]
    }]
}

Page > SearchPanel + Grid 표준 CRUD

{
    tagName: 'page',
    tags: [{
        tagName: 'searchPanel',
        layout: 'ds-flex fd-row ai-center gap-s',
        style: { padding: '10px' },
        tags: [
            { tagName: 'inputField', label: '키워드', ref: 'refKeyword' },
            { tagName: 'div', style: { flex: 1 } },
            { tagName: 'button', text: '조회', appearance: 'primary', onClick: 'onClickSearch' }
        ]
    },{
        tagName: 'panel',
        style: { flex: 1 },
        tags: [{
            ref: 'refGrid',
            tagName: 'grid',
            columns: [ /* ... */ ],
            onRowClick: 'onRowClick'
        }]
    }]
}

collapsible한 고급 검색 (Details와 조합)

{
    tagName: 'searchPanel',
    tags: [{
        tagName: 'div',
        layout: 'ds-flex fd-row gap-s',
        style: { padding: '10px' },
        tags: [
            { tagName: 'inputField', label: '키워드' },
            { tagName: 'div', style: { flex: 1 } },
            { tagName: 'button', text: '조회', appearance: 'primary' }
        ]
    },{
        tagName: 'details',
        title: '고급 검색',
        open: false,
        tags: [
            { tagName: 'dateField', label: '시작일' },
            { tagName: 'dateField', label: '종료일' },
            { tagName: 'comboboxField', label: '상태', items: [ /* ... */ ] }
        ]
    }]
}

실전 예시 — 사용자 목록 화면

export default class UserList extends Va.View {
    constructor(){ super(arguments); }
    mounted(){ this.loadUsers(); }
    onClickSearch(){
        let name = this.getRef('refName').getValue();
        let dept = this.getRef('refDept').getValue();
        UserService.search(this, { name, dept }, (res) => {
            this.getRef('refGrid').setData(res.data);
        });
    }
    onClickReset(){
        this.getRef('refName').setValue('');
        this.getRef('refDept').setValue('');
    }
    config(){
        return {
            tagName: 'page',
            tags: [{
                tagName: 'searchPanel',
                layout: 'ds-flex fd-row ai-center gap-s',
                style: { padding: '10px' },
                tags: [
                    { tagName: 'inputField', label: '이름', ref: 'refName' },
                    { tagName: 'comboboxField', label: '부서', ref: 'refDept', items: [
                        { code: 'DEV', name: '개발' },
                        { code: 'DES', name: '디자인' }
                    ]},
                    { tagName: 'div', style: { flex: 1 } },
                    { tagName: 'button', text: '조회', appearance: 'primary', onClick: 'onClickSearch' },
                    { tagName: 'button', text: '초기화', onClick: 'onClickReset' }
                ]
            },{
                tagName: 'panel',
                style: { flex: 1 },
                tags: [{
                    ref: 'refGrid',
                    tagName: 'grid',
                    columns: [
                        { key: 'name', title: '이름', width: 100 },
                        { key: 'dept', title: '부서', width: 100 },
                        { key: 'email', title: '이메일', fillRatio: 1 }
                    ]
                }]
            }]
        };
    }
}
Va.registerView('/view/user/UserList', UserList);

주의사항

  • 기본 방향은 세로(column) — 가로 배치를 원하면 명시적으로 layout: 'ds-flex fd-row ...' 지정
  • 자체 padding 없음 — 내용을 넣을 때 style: { padding: '10px' } 등을 직접 지정하는 편이 편함
  • 상단 primary 강조선은 테마 확장 CSS에서 — light/dark 테마에만 있음. paper 테마에서는 다른 스타일 적용
  • hover 시 그림자 자동 — 정적 화면이 아니라 인터랙션 UI라는 시각적 힌트
  • Page > SearchPanel + Panel(그리드) 조합이 표준 CRUD 스캐폴드 — Panel과 동급으로 취급하여 Page 자식으로 나란히 놓기
  • Panel과 혼동 주의 — Panel은 범용, SearchPanel은 검색 전용. 서로 다른 시각 스타일

대안 비교

상황추천

조회 조건 입력 영역 Va.SearchPanel
일반 섹션 컨테이너 Va.Panel
순수 그루핑 Va.Div
접기/펼치기 폼 Va.Details + SearchPanel 조합
폼 액션 바 Va.ActionBar
페이지 최상위 Va.Page

참고

'컴포넌트 > 패널 외 영역 (Div, Span)' 카테고리의 다른 글

TitleBar (타이틀바)  (0) 2026.09.18
SubTitle (서브타이틀)  (0) 2026.09.18
Title (타이틀)  (0) 2026.09.18
ActionBar (액션바)  (0) 2026.09.18
Panel (패널)  (0) 2026.09.18