PowerBuilder 보고서 아키텍처 개요
엔터프라이즈 환경에서 데이터 기반 의사결정을 위해서는 유연한 보고서 체계가 필수적이다. PowerBuilder는 DataWindow 기술을 중심으로 시각적 개발과 복잡한 데이터 처리를 동시에 지원하며, 이를 통해 개발자는 비즈니스 요구사항에 맞춘 맞춤형 보고서를 구축할 수 있다.
보고서 핵심 구성요소
보고서 체계는 데이터 획득, 가공, 시각화의 세 단계로 구성된다. 데이터 획득 단계에서는 다양한 저장소로부터 원본 데이터를 수집하고, 가공 단계에서는 필터링, 집계, 변환을 수행하며, 시각화 단계에서는 최종 사용자가 해석하기 용이한 형태로 정보를 전달한다. 맞춤형 보고서의 가치는 정확한 데이터 분석을 통해 기업의 전략적 결정을 뒷받침하는 데 있다.
DataWindow 객체 심층 분석
DataWindow는 PowerBuilder 애플리케이션의 핵심 데이터 컨테이너로, SQL 실행 결과를 시각적 요소로 변환하는 역할을 한다. 단순한 표 형태의 나열부터 복잡한 마스터-디테일 구조까지 다양한 형태의 데이터 표현이 가능하다.
DataWindow 유형 및 특성
DataWindow는 시각적 편집이 가능한 그래픽 타입과 코드 중심의 스크립트 타입으로 구분된다. 주요 속성으로는 데이터 소스 정의, 표시 스타일, 갱신 모드, 그룹화 기준 등이 있으며, 이들은 DataWindow Painter에서 설정하거나 런타임에 동적으로 변경할 수 있다.
동적 스타일 적용 예제
// DataWindow 동적 속성 변경
dw_report.SetTransObject(sqlca)
// 현재 배경색 확인 및 반전
long ll_currentColor, ll_newColor
ll_currentColor = Long(dw_report.Describe("DataWindow.Color"))
ll_newColor = ll_currentColor XOR 16777215
// Modify 함수를 통한 속성 적용
string ls_modify
ls_modify = "DataWindow.Color=" + String(ll_newColor)
dw_report.Modify(ls_modify)위 예제에서는 Describe로 속성을 읽고 Modify로 적용하는 패턴을 보여준다. XOR 연산을 통해 색상을 반전시키는 방식은 시각적 대비 효과를 을 수 있다.
런타임 DataWindow 생성 패턴
// 동적 DataWindow 생성 및 데이터 소스 연결
string ls_syntax, ls_errors
// SQL 문법에서 DataWindow 문법 생성
ls_syntax = sqlca.SyntaxFromSQL("SELECT emp_id, emp_name, dept_code FROM employee", "", ls_errors)
IF Len(ls_errors) > 0 THEN
MessageBox("오류", "문법 생성 실패: " + ls_errors)
RETURN
END IF
// 동적 생성 및 데이터 바인딩
dw_dynamic.Create(ls_syntax)
dw_dynamic.SetTransObject(sqlca)
dw_dynamic.Retrieve()반응형 레이아웃 구현
다양한 해상도와 디바이스에 대응하기 위한 레이아웃 전략이 필요하다. PowerBuilder 2017 이후 버전에서는 CSS 기반 스타일링을 지원하여 유연한 디자인이 가능해졌다.
/* 모바일 최적화 스타일 */
@media screen and (max-width: 768px) {
.dw-container { width: 100%; overflow-x: auto; }
.dw-header { font-size: 0.9em; display: none; }
.dw-row { display: block; margin-bottom: 1em; border: 1px solid #ccc; }
.dw-cell { display: block; padding: 0.5em; }
.dw-cell::before {
content: attr(data-label);
font-weight: bold;
display: inline-block;
width: 40%;
}
}스크립트 기반의 동적 레이아웃 조정도 여전히 유효하다. Resize 이벤트에서 컨트롤 위치와 크기를 재계산하여 화면 크기 변화에 대응할 수 있다.
데이터 연결 및 SQL 최적화
프로그래밍적 연결 설정
// 트랜잭션 객체 구성
n_tr lnv_conn
lnv_conn = CREATE n_tr
lnv_conn.DBMS = "SNC"
lnv_conn.LogId = "app_user"
lnv_conn.LogPass = "secure_password"
lnv_conn.ServerName = "dbserver.production.local"
lnv_conn.Database = "ERPDB"
lnv_conn.AutoCommit = False
// 연결 시도 및 예외 처리
IF lnv_conn.Connect() <> 0 THEN
MessageBox("연결 실패", "데이터베이스 접속 오류: " + lnv_conn.SQLErrText)
DESTROY lnv_conn
RETURN
END IF
// DataWindow에 트랜션 객체 할당
dw_report.SetTransObject(lnv_conn)고성능 쿼리 설계
대용량 데이터 처리 시 쿼리 성능이 보고서 응답 시간을 좌우한다. 다음 원칙을 적용하면 효율적인 데이터 조회가 가능하다.
- 필요한 컬럼만 명시적으로 지정하여 네트워크 부하 감소
- 적절한 인덱스 활용으로 풀 스캔 방지
- 파라미터화된 쿼리로 실행 계획 캐싱 활용
- 페이지네이션으로 초기 로딩 시간 단축
// 매개변수 기반 페이징 쿼리
string ls_baseSQL
ls_baseSQL = "SELECT emp_id, emp_name, hire_date, salary " + &
"FROM ( " + &
" SELECT emp_id, emp_name, hire_date, salary, " + &
" ROW_NUMBER() OVER (ORDER BY emp_id) AS rn " + &
" FROM employee " + &
" WHERE dept_code = :dept_param " + &
") ranked " + &
"WHERE rn BETWEEN :start_row AND :end_row"
dw_report.SetSQLSelect(ls_baseSQL)계산 열과 비즈니스 로직 분리
클라이언트 측 계산 구현
DataWindow 내에서 Computed Field를 활용하면 데이터베이스 부담 없이 클라이언트에서 값을 산출할 수 있다. 복잡한 비즈니스 규칙은 사용자 정의 함수로 모듈화한다.
// 사용자 정의 함수: 성과 등급별 보너스 계산
// 글로벌 함수: f_calc_bonus
long f_calc_bonus(decimal adc_baseSalary, string as_rating)
decimal ldc_multiplier
long ll_bonus
CHOOSE CASE as_rating
CASE "A"
ldc_multiplier = 2.5
CASE "B"
ldc_multiplier = 1.8
CASE "C"
ldc_multiplier = 1.2
CASE "D"
ldc_multiplier = 0.8
CASE ELSE
ldc_multiplier = 0
END CHOOSE
ll_bonus = Long(adc_baseSalary * ldc_multiplier)
RETURN ll_bonus이 함수는 DataWindow의 Computed Field 표현식에서 호출할 수 있다.
// Computed Field 표현식 예시
// f_calc_bonus(salary, performance_grade)이벤트 기반 상호작용 설계
사용자 동작 처리 패턴
// DataWindow 클릭 이벤트: 상세 정보 팝업
long ll_clickedRow
string ls_empID
ll_clickedRow = dw_report.GetClickedRow()
IF ll_clickedRow > 0 THEN
ls_empID = dw_report.GetItemString(ll_clickedRow, "emp_id")
// 상세 뷰 오픈
nvo_empDetail lnv_detail
lnv_detail = CREATE nvo_empDetail
lnv_detail.of_ShowDetail(ls_empID)
DESTROY lnv_detail
END IF드래그 앤 드롭 기반 동적 필터
// 드래그 시작: 컬럼 더에서
// DragDrop 이벤트: 필터 영역으로
string ls_columnName, ls_filterValue, ls_currentFilter
// 드된 컬럼 별
ls_columnName = String(dwo.Name)
// 필터 조건 구성
IF ls_columnName = "dept_code" THEN
ls_filterValue = dw_report.GetItemString(row, "dept_code")
ls_currentFilter = dw_report.Describe("DataWindow.Table.Filter")
IF Len(ls_currentFilter) > 0 THEN
ls_currentFilter += " AND "
END IF
ls_currentFilter += "dept_code = '" + ls_filterValue + "'"
dw_report.SetFilter(ls_currentFilter)
dw_report.Filter()
END IF출력 및 배포 기능
프린터 설정 세부 제어
// 사용자 정의 프린터 설정
PrintJobDefinition lpr_job
lpr_job = CREATE PrintJobDefinition
lpr_job.Printer = "\\\\printserver\\FinancePrinter"
lpr_job.Copies = 2
lpr_job.PageRange = "1-5,8,10-12"
lpr_job.Orientation = Landscape!
// 인쇄 실행
dw_report.Print(lpr_job)
DESTROY lpr_job다중 형식 내보내기
// 내보내기 형식 선택 대화상자
string ls_path, ls_ext
int li_format
li_format = Integer(MessageBox("형식 선택", "1-PDF 2-Excel 3-XML", Question!, YesNoCancel!, 1))
CHOOSE CASE li_format
CASE 1
ls_ext = "pdf"
ls_path = "C:\Reports\monthly_report.pdf"
dw_report.SaveAs(ls_path, PDF!, True)
CASE 2
ls_ext = "xlsx"
ls_path = "C:\Reports\monthly_report.xlsx"
dw_report.SaveAs(ls_path, Excel!, True)
CASE 3
ls_ext = "xml"
ls_path = "C:\Reports\monthly_report.xml"
dw_report.SaveAs(ls_path, XML!, True)
END CHOOSE오류 처리 및 안정성 확보
// 중앙집중식 오류 처리 루틴
integer li_errorCode
string ls_errorContext
li_errorCode = dw_report.Retrieve()
IF li_errorCode < 0 THEN
ls_errorContext = "보고서 데이터 조회 실패" + "~r~n" + &
"객체: " + dw_report.ClassName() + "~r~n" + &
"SQLCA 코드: " + String(sqlca.SQLDBCode) + "~r~n" + &
"메시지: " + sqlca.SQLErrText
// 로깅
nvo_logger lnv_log
lnv_log.of_WriteErrorLog(ls_errorContext)
// 사용자 알림
MessageBox("처리 오류", "보고서를 생성할 수 없습니다. 관리자에게 문의하세요.", StopSign!)
END IF