Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
Tags
- WSLHostPatcher
- bpl
- Delphi
- stepzen
- vercel
- shadcn-ui
- wsl2
- oraclecloude
- edgestore
- reactnative
- FastAPI
- Expo
- TypeScript
- Module
- java
- springboot
- dokerfile
- docker
- Markdown
- react
- superbase
- kidznote
- Book
- svelte
- Nextjs
- beautifullsoup
- flyio
- spring
- AWS
- tessrect
Archives
- Today
- Total
Blog
[Delphi] Bpl을 활용한 패키지 아키텍처 본문
1. 아키텍처 전체 구조 레이아웃
전체적인 의존성 구조는 아래와 같이 철저히 단방향 하향 참조로 구성됨.
BaseCommon.bpl(레이어1): 서드파티 컴포넌트, 공통 유틸리티, DB 연결 정보, 그리고 모듈 간 통신을 위한 인터페이스(Interface)를 포함함.Main.exe(레이어2): 구체적인 기능 화면을 직접 들고 있지 않는 가벼운 껍데기임. 화면을 언제 띄울지 제어하는 엔진 역할만 수행함.A.bpl,B.bpl(레이어3): 실제 업무 화면(매출관리, 고객관리 등)과 상세 로직이 포함됨. 모듈 간에는 서로 절대 참조하지 않음.
2. 각 요소별 핵심 역할 및 내부 구현 방식
① BaseCommon.bpl (모든 패키지의 기반)
모든 컴포넌트(DevExpress, TMS 등)에 대한 참조 정보와 공통 기능을 가짐. 이 패키지가 깨지면 상위 모든 패키지가 작동하지 않으므로 변경 시 주의가 필요함.
- 컴포넌트 참조 통합: 개별 BPL마다 서드파티 컴포넌트를 각각 포함하면 용량이 중복으로 커짐.
BaseCommon프로젝트 옵션의Requires에 서드파티 패키지(예:dxCore.dcp,cxLibrary.dcp)를 등록하여 한곳에서 참조를 관리함. - 공통 인터페이스(Interface) 선언:
A.bpl과B.bpl이 서로 소통하거나Main.exe가 이들을 제어할 때 구체적인 폼 클래스 명을 쓰면 안 됨. 이를 위해 추상화된 약속(인터페이스)을 선언함.
// BaseCommon.bpl 내부 uInterfaces.pas
unit uInterfaces;
interface
uses System.Classes, Vcl.Forms;
type
// 모든 업무 BPL 화면들이 반드시 구현해야 하는 공통 규격
IBplForm = interface
['{7D4B6C1E-2A3A-4B5C-6D7E-8F9A0B1C2D3E}']
procedure InitModule(const AUserData: string); // 모듈 초기화 데이터 전달
function GetFormObject: TForm; // 폼 객체 자체를 반환받기 위함
end;
implementation
end.② A.bpl, B.bpl (독립된 비즈니스 모듈)
실제 UI 화면과 비즈니스 로직을 담고 있음. 프로젝트 설정의 Requires에 반드시 BaseCommon.dcp가 등록되어 있어야 함.
- 구현 방식:
BaseCommon에 선언된 인터페이스를 상속받아 화면을 구현하고, 메인에서 호출할 수 있도록 팩토리(Factory) 함수를exports함.
// A.bpl 내부 uFormA.pas
unit uFormA;
interface
uses
System.Classes, Vcl.Forms, Vcl.StdCtrls, uInterfaces; // BaseCommon의 인터페이스 참조
type
TFormA = class(TForm, IBplForm)
Button1: TButton;
public
procedure InitModule(const AUserData: string);
function GetFormObject: TForm;
end;
// 외부(Main.exe)에서 이 BPL을 호출할 창구 함수
function OpenModuleA(AOwner: TComponent): IBplForm; stdcall;
implementation
{$R *.dfm}
function OpenModuleA(AOwner: TComponent): IBplForm; stdcall;
begin
Result := TFormA.Create(AOwner); // 인터페이스 형태로 반환
end;
exports
OpenModuleA;
{ TFormA 구현 }
function TFormA.GetFormObject: TForm;
begin
Result := Self;
end;
procedure TFormA.InitModule(const AUserData: string);
begin
// 메인에서 넘겨준 로그인 유저 등의 데이터 처리
Caption := AUserData + '님의 업무 화면 A';
end;
end.③ Main.exe (중앙 통제 및 패키지 실행 엔진)
BaseCommon.dcp를 참조함. 실행 관련 핵심 로직(앞서 언급한 패키지 매니저 등)을 내장하고 있어, 사용자가 메뉴를 클릭할 때 해당하는 BPL을 동적으로 로드함.
- 동작 매커니즘:
Main.exe는A.bpl이나B.bpl이 프로젝트 내에 연결되어 있지 않음(uses 안 함). 오직 파일명과 함수명 문자열만 가지고 동적으로 호출함.
// Main.exe 내부 코드 일부
uses uInterfaces, uPackageManager; // BaseCommon의 인터페이스와 패키지 매니저만 참조
type
TOpenModuleFunc = function(AOwner: TComponent): IBplForm; stdcall;
procedure TMainForm.MenuAClick(Sender: TObject);
var
OpenFunc: TOpenModuleFunc;
BplModule: IBplForm;
begin
try
// 1. 패키지 매니저를 통해 A.bpl에서 함수 포인터 획득
@OpenFunc := TPackageManager.GetInstance.GetBplProc('A.bpl', 'OpenModuleA');
if Assigned(OpenFunc) then
begin
// 2. 함수를 실행하여 인터페이스를 얻음
BplModule := OpenFunc(Application);
// 3. 인터페이스 규격대로 데이터 전달 및 공통 제어
BplModule.InitModule('홍길동');
// 4. 실제 Form 객체로 변환하여 화면에 출력
BplModule.GetFormObject.Show;
end;
except
on E: Exception do ShowMessage(E.Message);
end;
end;3. 이 구조가 주는 강력한 이점 (왜 이렇게 해야 하는가?)
- 완벽한 순환 참조(Circular Reference) 방지: *
A.bpl에서B.bpl의 특정 기능이나 팝업이 필요할 때, 직접uses하면 결합도가 꼬임.
- 이때 직접 참조하지 않고
BaseCommon에 인터페이스나 이벤트를 중재자(Mediator)로 등록해 두고 통신하면 패키지 간의 커플링이 완전히 해소됨.
- 배포 효율 극대화(부분 패치):
A.bpl화면의 디자인이나 버그를 수정했을 때, 메인 프로그램 전체를 빌드하거나 배포할 필요가 없음.- 오직
A.bpl파일(몇 백 KB ~ 몇 MB 수준)만 빌드해서 사용자 PC에 덮어쓰면 패치가 완료됨.
- 초기 구동 속도 및 메모리 최적화:
- 메인 프로그램 실행 시에는
Main.exe와BaseCommon.bpl만 메모리에 올라감. - 사용자가 'A 업무 메뉴'를 누를 때 비로소
A.bpl이 메모리에 로드되므로 시스템 자원을 효율적으로 사용함.
- 대규모 개발 팀 협업 용이:
- 공통 파트 팀원은
BaseCommon.bpl만 관리하고, 영업 파트 개발자는A.bpl, 인사 파트 개발자는B.bpl만 독립적으로 개발하여 배포하므로 소스 충돌이 최소화됨.