TabControl의 탭 컨텐츠 렌더링 가이드

탭 컨텐츠 렌더링

TabControl을 포함하는 화면을 로딩할 경우, 첫 번째 탭은 항상 화면에 표시됩니다. TabControl이 여러 개의 탭을 포함할 경우, 첫 번째 탭을 제외한 나머지 탭들은 사용자가 열 수도 있고 그렇지 않을 수도 있습니다. 즉, 탭이 4개 있을 때 사용자가 모든 탭을 열어보지 않는 경우가 존재할 수 있습니다. 혹은 탭이 20개 있을 경우, 대부분의 경우 일부 탭만 열어서 사용하고 20개 탭을 모두 여는 경우는 극히 적을 것입니다.

TabControl이 포함하고 있는 모든 탭을 초기에 렌더링할 경우 성능에 부담을 줄 수 있습니다. 초기 데이터 적재 측면에서도 사용하지 않을지도 모르는 데이터를 가져오기 위해 네트워크 대역폭을 낭비하고 로딩 시간을 크게 늘릴 수도 있기 때문입니다. 즉, 탭의 개수가 많고 탭 컨텐츠가 복잡할 경우, 사용하지 않을지도 모르는 탭들의 컨텐츠에 포함된 컴포넌트들을 대량으로 로딩하고 렌더링하고 이는 곧 성능 관련 문제로 이어질 수 있습니다.

특히 대량의 데이터를 사용하는 GridView를 포함하는 탭이 존재하거나, UDC 및 Generator 등 렌더링 비용이 상대적으로 높은 컴포넌트를 탭 컨텐츠가 포함할 경우, 렌더링으로 인한 부하는 더욱 증가합니다.

alwaysDraw

alwaysDraw 속성을 사용하면 TabControl이 포함하는 탭 컨텐츠의 렌더링 시점을 결정할 수 있습니다. TabControl이 포함된 화면이 처음 로딩될 경우, TabControl의 모든 탭 컨텐츠를 렌더링(alwaysDraw="true")하거나 혹은 첫 번째 탭 컨텐츠만 렌더링(alwaysDraw="false")할 수 있습니다. 첫 번째 탭 컨텐츠만 렌더링하는 경우, 나머지 탭 컨텐츠는 사용자 이벤트 혹은 스크립트 이벤트로 각 탭이 선택되는 시점에 해당 탭 컨텐츠가 렌더링됩니다.

frameMode 속성


TabControl의 <content> 태그의 frameMode 속성 설정에 따라 렌더링 방식이 일부 변경됩니다.

alwaysDraw="true"

다음은 TabControl에 alwaysDraw="true" 설정을 적용하고, TabControl의 <content> 태그에 alwaysDraw="true" 및 frameMode="wframe" 설정을 적용한 경우입니다.

적용 설정


alwaysDraW

frameMode

TabControl

"true"


TabControl<content>

"true"

"wframe"

$r_title(alwaysDraw="true" frameMode="wframe")

<w2:tabControl id="tac_main" alwaysDraw="true" class="wq_tab">
    <w2:tabs id="tabs1" label="탭1" />
    <w2:tabs id="tabs2" label="탭2" />
    <w2:tabs id="tabs3" label="탭3" />
    <w2:tabs id="tabs4" label="탭4" />
    <w2:content id="content1" 
        alwaysDraw="true" frameMode="wframe" src="/…/tab1.xml" />
    <w2:content id="content2" 
        alwaysDraw="true" frameMode="wframe" src="/…/tab2.xml" />
    <w2:content id="content3" 
        alwaysDraw="true" frameMode="wframe" src="/…/tab3.xml" />
    <w2:content id="content4" 
        alwaysDraw="true" frameMode="wframe" src="/…/tab4.xml" />
</w2:tabControl>

위의 코드를 적용하여 예제 페이지를 로딩한 직후, 아무런 추가 작업도 수행하지 않고 F12 개발자 도구를 열어보겠습니다. Elements 탭을 확인하면, alwaysDraw="true" 설정을 적용했기 때문에 다음과 같이 모든 탭 컨텐츠가 렌더링 된 것을 확인할 수 있습니다.

초기 렌더링 결과

또한 F12 개발자 도구에서 Network 탭을 확인해보면 다음과 같이 동시에 관련된 모든 JS 파일들이 다운로드 되는 것을 확인할 수 있습니다. (SP5 기준)

다운로드된 JS 파일 목록

하지만 실제 프로젝트에서는 위의 예제처럼 탭 컨텐츠가 소수의 단순한 컴포넌트만 포함하는 경우는 별로 없습니다. 수많은 컴포넌트들이 대량의 데이터와 함께 복잡하게 구성되어 있거나, WFrame이 여러 단계에 걸쳐 네스팅되어 존재하는 경우도 많습니다

alwaysDraw="false" (기본 값)

alwaysDraw="false" 설정을 적용할 경우, 첫 번째 탭 컨텐츠만 렌더링되고 나머지 탭의 컨텐츠들은 사용자 이벤트나 스트립트 이벤트로 해당 탭을 선택할 경우 렌더링됩니다. 또한 TabControl의 <content> 태그의 frameMode 속성 설정에 따라 렌더링 방식이 추가로 변경됩니다.

TabControl의 <content> 태그의 frameMode 속성으로 아래와 같은 세 가지 값을 지정할 수 있습니다.

frameMode 옵션 설명

옵션

사용 프레임

비고

"iframe"

IFrame

비권장.

"wframe"

WFrame

alwaysDraw 속성 설정에 따라 탭 컨텐츠의 렌더링 동작이 결정됨.

"wframePreload"

WFrame

초기 화면 로딩 시, 첫 번째 탭을 제외한 나머지 탭의 컨텐츠에 속하는 JS 파일을 로딩하고 개체도 생성.

단, 렌더링은 수행하지 않음.

(주의! 탭 컨텐츠 scwin.onpageload() 이벤트 핸들러는 실행되지 않음.)

WFrameMode


SP4 이후 부터는 탭 내부의 구성이 극히 단순한 경우를 제외하면, <content> 태그의 frameMode 속성은 대부분의 경우 "wframe" 또는 "wframePreload"로 설정합니다.

frameMode="wframe"

다음은 TabControl에 alwaysDraw="false" 설정을 적용하고, TabControl의 <content> 태그에 alwaysDraw="false" 및 frameMode="wframe" 설정을 적용한 경우입니다.

적용 설정


alwaysDraWFrameMode

frameMode

TabControl

"false"


TabControl<content>

"false"

"wframe"

$r_title(alwaysDraw="false" frameMode="wframe")
<w2:tabControl id="tac_main" alwaysDraw="false" class="wq_tab">
    <w2:tabs id="tabs1" label="탭1" />
    <w2:tabs id="tabs2" label="탭2" />
    <w2:tabs id="tabs3" label="탭3" />
    <w2:tabs id="tabs4" label="탭4" />
    <w2:content id="content1" 
        alwaysDraw="false" frameMode="wframe" src="/…/tab1.xml" />
    <w2:content id="content2" 
        alwaysDraw="false" frameMode="wframe" src="/…/tab2.xml" />
    <w2:content id="content3" 
        alwaysDraw="false" frameMode="wframe" src="/…/tab3.xml" />
    <w2:content id="content4" 
        alwaysDraw="false" frameMode="wframe" src="/…/tab4.xml" />
</w2:tabControl>

위의 코드를 적용하여 예제 페이지를 로딩한 직후, 아무런 추가 작업도 수행하지 않고 F12 개발자 도구를 열어보겠습니다. Elements 탭을 확인하면, alwaysDraw="false" 설정을 적용했기 때문에 첫 번째 탭 컨텐츠만 렌더링 된 것을 확인할 수 있습니다. 두 번째 이후의 탭의 내용들은 모두 비어 있습니다.

초기 렌더링 결과

또한 F12 개발자 도구에서 Network 탭을 확인해보면 TabControl이 포함된 페이지와 첫 번째 탭의 JS 파일들만 다운로드된 것을 확인할 수 있습니다.

다운로드된 JS 파일 목록

마우스로 네 번째 탭을 클릭한 후 (즉, 네 번째 탭을 최초로 선택한 후), 다시 F12 개발자 도구의 Elements 탭을 확인해 보겠습니다. 방금 선택한 네 번째 탭이 추가적으로 렌더링 된 것을 볼 수 있습니다.

네 번째 탭 선택 후 화면 렌더링 결과

또한 F12 개발자 도구의 Network 탭을 확인하면 네 번째 탭의 JS 파일이 다운로드 된 것을 확인할 수 있습니다.

다운로드된 JS 파일

frameMode="wframePreload"

다음은 TabControl에 alwaysDraw="false" 설정을 적용하고, TabControl의 <content> 태그에 alwaysDraw="false" 및 frameMode="wframePreload" 설정을 적용한 경우입니다.

적용 설정


alwaysDraWFrameMode

frameMode

TabControl

"false"


TabControl<content>

"false"

"wframePreload"

$r_title(alwaysDraw="false" frameMode="wframePreload")
<w2:tabControl id="tac_main" alwaysDraw="false" class="wq_tab">
    <w2:tabs id="tabs1" label="탭1" />
    <w2:tabs id="tabs2" label="탭2" />
    <w2:tabs id="tabs3" label="탭3" />
    <w2:tabs id="tabs4" label="탭4" />
    <w2:content id="content1" 
        alwaysDraw="false" frameMode="wframePreload" 
        src="/…/tab1.xml" />
    <w2:content id="content2" 
        alwaysDraw="false" frameMode="wframePreload" 
        src="/…/tab2.xml" />
    <w2:content id="content3" 
        alwaysDraw="false" frameMode="wframePreload" 
        src="/…/tab3.xml" />
    <w2:content id="content4" 
        alwaysDraw="false" frameMode="wframePreload" 
        src="/…/tab4.xml" /></w2:tabControl>

위의 코드를 적용하여 예제 페이지를 로딩한 직후, 아무런 추가 작업도 수행하지 않고 F12 개발자 도구를 열어보겠습니다. Elements 탭을 확인하면, alwaysDraw="false" 설정을 적용했기 때문에 첫 번째 탭 컨텐츠만 렌더링 된 것을 확인할 수 있습니다. 두 번째 이후의 탭의 내용들은 모두 비어 있습니다.

초기 렌더링 결과

그런데 F12 개발자 도구에서 Network 탭을 확인해보면 동시에 관련된 모든 JS파일들이 다운로드된 것을 확인할 수 있습니다. 즉, 아직 렌더링되지 않은 탭 컨텐츠의 JS 파일들이 다운로드되었스니다.

다운로드된 JS 파일 목록

탭 컨텐츠 렌더링과 객체 접근

frameMode="wframePreload"로 설정할 경우, 각 탭 컨텐츠는 아래와 같이 접근할 수 있습니다.

컴포넌트

접근 가능.

단, (id 호출 등을 통해) 컴포넌트에 직접적으로 접근하여 렌더링 작업을 할 수는 없음.

DataCollection

접근 가능.

컬럼 값을 설정할 수 있음.

탭을 선택할 경우, 탭이 렌더링되고, ref 속성으로 바인딩된 컴포넌트에 데이터가 정상적으로 출력됨.

scwin 객체 및 scwin 객체의 속성

접근 가능.

각 컴포넌트와 바인딩된 이벤트 핸들러 및 메소드도 접근 가능.

단, 이벤트 핸들러 내부에 컴포넌트에 직접적으로 접근하는 코드가 존재할 경우, 이벤트 핸들러 및 메소드를 호출하면 오류가 발생.

frameMode="wframe"

탭의 컨텐츠가 렌더링되지 않은 경우, 해당 탭의 컨텐츠를 구성하고 있는 컴포넌트 등에 접근할 수 없습니다.

예제 페이지에 다음과 같이 두 개의 버튼을 구성하여 테스트 해보겠습니다. 이 버튼들을 클릭하면 탭1탭2에 존재하는 InputBox 컴포넌트에 임의의 값이 설정됩니다.

탭1탭2에 포함된 컴포넌트에 접근하기 위한 예제

두 버튼의 onclick 이벤트 핸들러를 아래와 같이 구현합니다.

$r_title(탭1과 탭2에 포함된 컴포넌트에 접근하기 위한 코드)
// 탭1-키1 값 설정 버튼 클릭 이벤트 핸들러
scwin.btn_tab1SetVal_onclick = function (e) {
    const tabs1 = tac_main.getWindow(tac_main.getTabIndex("tabs1"));
    tabs1.ipt_key1.setValue("탭1-키1 값을 설정합니다.");
};


// 탭2-키1 값 설정 버튼 클릭 이벤트 핸들러
scwin.btn_tab2SetVal_onclick = function (e) {
    const tabs2 = tac_main.getWindow(tac_main.getTabIndex("tabs2"));
    tabs2.ipt_key1.setValue("탭2-키1 값을 설정합니다.");
};

예제 페이지를 새로 고침한 후, 아무런 작업을 수행하지 않고 두 버튼을 차례로 클릭하면 alwaysDrawframeMode 설정에 따라 아래와 같이 동작합니다.

alwaysDraw="true" frameMode="wframe"

탭1-키1 값 설정탭2-키1 값 설정 두 버튼 모두 정상적으로 동작합니다.

alwaysDraw="false" frameMode="wframe"

탭1-키1 값 설정 버튼은 정상적으로 동작하지만, 탭2-키1 값 설정 버튼을 클릭할 경우 다음과 같은 오류가 발생합니다.

탭2-키1 값 설정 버튼을 클릭할 경우

위의 오류가 발생하는 이유는 두 번째 탭 컨텐츠가 아직 렌더링되지 않았고, 그 결과 탭 컨텐츠를 구성하는 컴포넌트들의 인스턴스도 아직 생성되지 않았기 때문입니다. 즉, setValue() 메소드를 제공해야 할 ipt_key1 컴포넌트가 아직 인스턴스화 되지 않은 상태(undefined)이므로 위와 같은 오류가 발생하게 됩니다.

하지만, 두 번째 탭을 클릭한 후, 탭2-키1 값 설정 버튼을 클릭하면 오류가 나타나지 않습니다.

frameMode="wframePreload"

다음은 TabControl에 alwaysDraw="false" 설정을 적용하고, TabControl의 <content> 태그에 alwaysDraw="false" 및 frameMode="wframePreload" 설정을 적용한 경우입니다.

적용 설정


alwaysDraWFrameMode

frameMode

TabControl

"false"


TabControl<content>

"false"

"wframePreload"

$r_title(alwaysDraw="false" frameMode="wframePreload")
<w2:tabControl id="tac_main" alwaysDraw="false" class="wq_tab">
    <w2:tabs id="tabs1" label="탭1" />
    <w2:tabs id="tabs2" label="탭2" />
    <w2:tabs id="tabs3" label="탭3" />
    <w2:tabs id="tabs4" label="탭4" />
    <w2:content id="content1" 
        alwaysDraw="false" frameMode="wframePreload" 
        src="/…/tab1.xml" />
    <w2:content id="content2" 
        alwaysDraw="false" frameMode="wframePreload" 
        src="/…/tab2.xml" />
    <w2:content id="content3" 
        alwaysDraw="false" frameMode="wframePreload" 
        src="/…/tab3.xml" />
    <w2:content id="content4" 
        alwaysDraw="false" frameMode="wframePreload" 
        src="/…/tab4.xml" /></w2:tabControl>

예제 페이지에 다음과 같이 두 개의 버튼을 구성하여 테스트 해보겠습니다. 이 버튼들을 클릭하면 탭1탭2에 존재하는 InputBox 컴포넌트에 임의의 값이 설정됩니다.

탭1탭2에 포함된 컴포넌트에 접근하기 위한 예제

두 버튼의 onclick 이벤트 핸들러를 아래와 같이 구현합니다.

$r_title(탭1과 탭2에 포함된 컴포넌트에 접근하기 위한 코드)
// 탭1-키1 값 설정 버튼 클릭 이벤트 핸들러
scwin.btn_tab1SetVal_onclick = function (e) {
    const tabs1 = tac_main.getWindow(tac_main.getTabIndex("tabs1"));
    tabs1.ipt_key1.setValue("탭1-키1 값을 설정합니다.");
};


// 탭2-키1 값 설정 버튼 클릭 이벤트 핸들러
scwin.btn_tab2SetVal_onclick = function (e) {
    const tabs2 = tac_main.getWindow(tac_main.getTabIndex("tabs2"));
    tabs2.ipt_key1.setValue("탭2-키1 값을 설정합니다.");
};

탭2-키1 값 설정’ 버튼을 클릭해보면 다음과 같이 frameMode="wframe"에서 발생했던 것보다 더 긴 내용의 오류가 발생합니다.

탭2-키1 값 설정 버튼을 클릭할 경우

단, 다음과 같이 DataCollection에 접근하여 컬럼 값을 설정하면 오류가 발생하지 않습니다.

$r_title(탭1과 탭2에 포함된 컴포넌트에 접근하기 위한 코드 (변경 후))
// 탭1-키1 값 설정 버튼 클릭 이벤트 핸들러
scwin.btn_tab1SetVal_onclick = function (e) {
    const tabs1 = tac_main.getWindow(tac_main.getTabIndex("tabs1"));
    tabs2.dma_map.set("key1", "탭2-키1 값을 설정합니다.");
};


// 탭2-키1 값 설정 버튼 클릭 이벤트 핸들러
scwin.btn_tab2SetVal_onclick = function (e) {
    const tabs2 = tac_main.getWindow(tac_main.getTabIndex("tabs2"));
    tabs2.dma_map.set("key1", "탭2-키1 값을 설정합니다.");
};

탭 컨텐츠는 렌더링되지 않았지만 컴포넌트에 접근은 가능한 상황

컴포넌트 자체에 접근은 가능하지만 렌더링에 관련된 작업은 불가능한 상태를 살펴보겠습니다. 다음과 같이 ‘탭2-키1 값 설정’ 버튼 클릭 이벤트 핸들러 코드를 수정하여 F12 개발자 도구의 Console 탭을 살펴보겠습니다.

// 탭2-키1 값 설정 버튼 클릭 이벤트 핸들러
scwin.btn_tab2SetVal_onclick = function (e) {
    const tabs2 = tac_main.getWindow(tac_main.getTabIndex("tabs2"));
    console.log(tabs2.ipt_key1);
};

아래와 같은 출력이 나타나고, 이는 두 번째 탭의 ipt_key1 컴포넌트의 인스턴스는 정상적으로 생성되었다는 것을 의미합니다. 즉, 두 번째 탭이 렌덩링되기 전이지만, 두 번째 컴포넌트에 포함된 컴포넌트에 접근은 가능합니다.

options 및 render 속성

위 결과 이미지에서 optionsrender 속성을 볼 수 있습니다.

비공개 속성


optionsrender 속성은 비공개입니다. 공통 개발자와 같이 핵심적인 WebSquare 사용자들에게 보다 자세한 설명을 드리기 위한 목적으로만 언급됩니다.

options 속성에는 개발자가 컴포넌트를 작성하면서 지정한 각종 속성 값들이 저장됩니다. 위의 이미지를 살펴보면 frameMode="wframePreload"로 설정된 상태에서도 options 속성이 그 내용과 함께 정상적으로 생성된 것을 확인할 수 있습니다.

현재 render 속성에는 null 참조가 할당되어 있기 때문에 컴포넌트 자체에 접근은 가능하지만 렌더링에 관련된 작업은 불가능합니다. 즉, 내부적으로 options 속성에 접근하는 기능들은 정상적으로 동작하지만, render 속성에 접근하는 기능들은 대부분 오류가 발생합니다.

정상적인 경우, render 속성에는 해당 WebSquare 컴포넌트에 대응하는 실제 DOM 요소에 대한 참조가 저장됩니다. 다음과 같이 <input> 태그로 생성된 DOM 요소에 대한 참조가 설정되어 있어야 합니다.

render 속성이 정상적으로 설정된 상태

예외 상황

렌더링이 되지 않은 상태에서 DOM 개체에 접근할 경우 오류가 발생합니다. 결국 null 상태인 DOM의 속성 등에 접근하는 셈이기 때문입니다.

하지만 오류는 발생하지 않지만 정상 동작하는 경우가 존재합니다.

$r_title(오류 발생하지만 정상 동작)
const tabs2 = tac_main.getWindow(tac_main.getTabIndex("tabs2"));
console.log(tabs2.ipt_key1.getValue());
tabs2.ipt_key1.setValue("탭2-키1 값을 설정합니다.")
tabs2.ipt_key1.setReadOnly(true);  // 오류는 발생하지만 정상 동작함.
tabs2.ipt_key1.setDisabled(true);  // 오류는 발생하지만 정상 동작함.
tabs2.ipt_key1.focus();
…등…

또한, 오류는 발생하지 않지만 정상 동작하지 않는 경우도 존재합니다.

$r_title(오류 없지만 정상 동작하지 않음)
const tabs2 = tac_main.getWindow(tac_main.getTabIndex("tabs2"));
console.log(tabs2.ipt_key1.getMandatory());
console.log(tabs2.ipt_key1.getDisplayFormat());
console.log(tabs2.ipt_key1.getAllowChar());
console.log(tabs2.ipt_key1.getReadOnly());
console.log(tabs2.ipt_key1.getRef());
console.log(tabs2.ipt_key1.getDisabled());
console.log(tabs2.ipt_key1.getID());
tabs2.ipt_key1.setMandatory(true);
tabs2.ipt_key1.show(“”);    // 오류는 없지만 정상 동작 안 함.
tabs2.ipt_key1.hide();      // 오류는 없지만 정상 동작 안 함.
tabs2.ipt_key1.setStyle("display", "none");  // 오류는 없지만 정상 동작 안 함.
…등…

대안

현재 주어진 리소스를 최대한 활용하여 유효성 검사 등의 상황에서 활용할 수 있는 여지는 존재합니다. 예를 들어서, frameMode="wframePreload" 설정을 적용한 상태에서 아래의 방법으로 ipt_key1 컴포넌트의 유효성 검사를 수행할 수 있습니다.

  1. 탭의 ipt_key1 컴포넌트에 접근합니다.

  2. ipt_key1.getMandatory() 메소드로 필수 입력 컴포넌트 여부를 확인합니다.

  3. 만약 ipt_key1이 필수 입력 컴포넌트라면, 값이 입력되어 있는지 확인해야 합니다.

  4. 그러나 ipt_key1.getValue() 메소드는 사용할 수 없습니다. (탭이 아직 렌더링 되어 있지 않을 수도 있기 때문입니다.)

  5. 이 경우, ipt_key1.getRef() 메소드로 바인딩 된 DataCollection 정보를 가져옵니다.

  6. DataCollection 정보를 활용해서 값이 입력되었는지 여부를 확인합니다.

한계점


실제 프로젝트 현장에서 이런 로직을 구현하려면 전제 조건도 많고, WRM 등의 기존 공통 라이브러리가 이런 기능을 지원하는지 확인해야 합니다.

권장


가령 필수적으로 모든 컴포넌트와 DataCollection을 바인딩하여 데이터를 관리하는 것을 권장합니다. 비록 단 한 건의 항목이라도 이런 방식으로 관리하는 것이 복잡한 프로젝트일수록, 또 프로젝트 후반으로 갈수록 유리합니다. (최근 시작되는 신규 프로젝트에서는 모든 컴포넌트를 필수적으로 DataCollection과 바인딩하여 사용하는 추세입니다. )

주의할 점

TabControl에 존재하는 탭의 개수, 각 탭들의 내용을 구성하는 수많은 컴포넌트들의 복잡도, 페이지 로딩 시 화면 구성을 위해 필요한 데이터 조회량 등을 감안하지 않고, 무조건 alwaysDraw="true" 설정을 적용하는 것을 지양해야 합니다. TabControl에 존재하는 탭의 개수가 많고, 각 탭들을 구성하는 컴포넌트들의 복잡도가 높은 경우가 많습니다. 이 같은 경우 alwaysDraw="true" 설정을 적용하면 구현은 간단하지만 성능이 저하될 수 있습니다.

사례 1

상황

TabControl를 포함하고 있는 페이지를 로딩하면서 해당 페이지 및 TabControl의 각 탭을 구성하는 모든 컴포넌트들을 함께 초기화하고 기본 데이터를 동시에 적재하고자 하지만, 메인 영역과 특정 탭 간에 또는 다수의 탭 간에 UI/데이터 적인 상호관계가 존재하는 경우.

화면 예시

일반적으로 SP4/SP5 기반의 WebSquare 화면 개발 시에는 페이지 로딩 시 호출되는 scwin.onpageload() 이벤트 핸들러에서 컴포넌트들의 구성을 초기화하고 기본 데이터를 적재하게 됩니다. 페이지 내부에 다수의 WFrame가 포함되어 있는 경우에도, 특별한 상황이 아니라면 부모 페이지에서 자식 WFrame 페이지의 컴포넌트 구성 등을 초기화 하기보다는(비록 불가능한 것은 아니지만 않지만), 가급적 각 WFrame 페이지 자체의 scwin.onpageload() 이벤트 핸들러에서 스스로 필요한 컴포넌트들의 구성을 초기화하고 기본 데이터를 적재하는 경우가 많습니다. 원칙적으로는 본문에서 살펴본 TabControl의 각 탭들도 WFrame의 한 형태이므로 위와 같은 방식으로 각 탭들이 자체적인 scwin.onpageload() 이벤트 핸들러에서 필요한 컴포넌트들의 구성을 초기화하고 기본 데이터를 적재하면 됩니다. 그러나, 즉 렌더링 시점을 예상할 수 없는 상황에서는, 언급한 방식의 컴포넌트 구성 초기화가 곤란한 경우도 있습니다.

건설사에서 전자계약 업무에 사용되는 계약 상세 관리 페이지가 존재한다고 가정해보겠습니다. 이 페이지에는 주요 계약 정보를 담고 있는 메인 영역과 alwaysDraw="false" 설정이 적용된 10여개의 탭으로 이루어진 TabControl가 구성되어 있습니다. 계약 상세 정보를 편집하기 위해 특정 계약 건을 선택해 이 페이지를 열면 우선적으로 메인 영역 및 TabControl의 첫 번째 탭에 대한 컴포넌트 구성 및 기본 데이터 적재 작업이 수행될 것입니다. 그런데 하필이면 메인 영역의 ‘외주/자재’ Radio 컴포넌트의 선택 값에 따라, 두 번째 탭의 ‘계약유형’ SelectBox 컴포넌트의 선택 목록 데이터가 변경되고, 이 SelectBox 컴포넌트의 선택 값에 따라 세 번째 탭의 테이블 레이아웃에서 ‘보증증권’ 관련 TR의 출력 토글된다고 가정해보도록 하겠습니다.

이런 경우 (아래의 사례는 구현 방식이나 프로젝트 내부에서 결정된 DataCollection 사용 표준 등에 따라 결과가 달라질 수는 있지만, 본문의 목적은 문제가 발생하는 상황을 이해하기 위한 것이므로 무시합니다.)

사례 2

상황

프로젝트에서 합의된 UI/UX 표준에서 유효성 검사 수행 표준이 TabControl에 친화적이지 않은 경우.

건설사에서 전자계약 업무에 사용되는 계약 상세 관리 페이지가 존재한다고 가정해보겠습니다. 이 페이지에는 주요 계약 정보를 담고 있는 메인 영역과 alwaysDraw="false" 설정이 적용된 10여개의 탭으로 이루어진 TabControl가 구성되어 있습니다. 그리고 이번에도 메인 영역의 ‘외주/자재’ Radio 컴포넌트의 선택 값에 따라, 두 번째 탭의 ‘계약유형’ SelectBox 컴포넌트의 선택 목록 데이터가 변경되고, 이 SelectBox 컴포넌트의 선택 값에 따라 세 번째 탭의 테이블 레이아웃에서 ‘보증증권’ 관련 TR의 출력이 토글된다고 가정해보도록 하겠습니다. 만약 프로젝트 내부 UI/UX 표준에서 ‘저장’ 버튼이 TabControl의 각 탭마다 별도로 존재하는 것으로 합의되었다면 상대적으로 수월하게 유효성 검사를 수행할 수 있습니다. 특정 탭의 유효성 검사 수행 시, 다른 탭이나 메인 영역의 데이터를 검증하거나 컴포넌트에 접근할 필요성이 상대적으로 적기 때문입니다.

화면 예시

그러나 이런 경우에도 본문의 예제와 같은 경우 세 번째 탭의 유효성 검사를 수행하려면 두 번째 탭의 컴포넌트 또는 데이터에 접근해야 할 수도 있습니다. 또한, 업무 개발자 측의 요구사항 중 두 번째 탭의 ‘계약유형’ 값이 지정되지 않은 경우 [‘보증증권’ 정보를 설정하려면 ‘계약유형’ 값을 지정해야 합니다.]라는 메시지를 출력한 다음, 자동으로 두 번째 탭으로 이동하고 ‘계약유형’ SelectBox 컴포넌트에 포커스를 지정해야 하는 등의 요건이 존재한다면 난이도가 더욱 복잡해집니다.

화면 예시

보통 프로젝트에서는 공통 개발자가 이런 유효성 검사 로직에 자동으로 대응할 수 있는 공통 유효성 검사 메소드를 요구 받는 경우가 많습니다. 물론 업무 개발자가 직접 대응한다고 하더라도 수월한 작업이 아닌 것은 당연합니다.

더 난감한 경우는 다음과 같이 프로젝트 내부 UI/UX 표준에서 ‘저장’ 버튼이 메인 영역에 한 개만 존재하는 것으로 합의된 경우입니다.

화면 예시

이에 대응하는 유효성 검사를 수행하려면 ‘저장’ 버튼 클릭 시, 메인 영역과 10개의 모든 탭에 대해 유효성 검사를 수행해야 하는데, 이번에도 문제는 각각의 탭들이 이미 렌더링 완료된 상태인지, 또는 일부 탭만 렌더링이 완료된 상태인지, 그도 아니면 오직 첫 번째 탭만 렌더링이 완료된 상태인지 불분명하다는 점입니다. 결과적으로 유효성 검사 코드를 구현하기가 쉽지 않습니다. 다른 탭이나 메인 영역과 상호 작용이 필요한 일부 탭들은 여전히 사례 1에서 업급한 문제점까지 해결해야 함은 물론입니다. 극단적인 경우로 ‘저장’ 버튼이 TabControl의 각 탭마다 별도로 존재하는 것으로 합의된다고 하더라도, 특정 탭 내부에 또 다시 자식 TabControl가 존재할 수 있고, 이는 결과적으로 ‘저장’ 버튼이 메인 영역에 한 개만 존재하는 것으로 합의된 경우와 동일합니다.

화면 예시

결론

특정 상황에서 TabControl의 alwaysDraw="false" 설정을 적용할 경우, 명백한 성능 향상 효과와 메모리 사용량 저하 효과를 기대할 수 있습니다. 그러나 실제 프로젝트 환경에서 극단적으로 복잡한 화면 구성을 구현할 때 alwaysDraw="false" 설정을 적용하면, 업무 개발자들의 구현 난이도를 크게 높이고, 심지어는 구현이 불가능 할 때도 있습니다. 따라서alwaysDraw="false" 설정은 아래 내용을 기준으로 주의해서 사용할 것을 권장합니다.