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
- oraclecloude
- Markdown
- Nextjs
- stepzen
- shadcn-ui
- svelte
- springboot
- dokerfile
- WSLHostPatcher
- beautifullsoup
- Book
- reactnative
- bpl
- AWS
- Module
- wsl2
- docker
- flyio
- react
- edgestore
- Expo
- kidznote
- tessrect
- FastAPI
- Delphi
- spring
- java
- TypeScript
- vercel
- superbase
Archives
- Today
- Total
Blog
[Delphi] 데이터 전달 관리(ModuleLink) 본문
델파이(Delphi) 개발 시 화면(Form) 간에 독립성을 유지하면서 복잡한 데이터를 주고받거나, 함수의 매개변수가 5개, 10개 이상으로 늘어나는 '롱 파라미터 리스트(Long Parameter List)' 현상은 유지보수를 어렵게 만드는 대표적인 원인임.
이를 해결하기 위해 윈도우 메시지를 이용한 폼 간 비결합 통신은 물론, 일반 함수 호출 시에도 수십 개의 인자를 단 하나로 압축할 수 있는 타입 명시형 TModuleLink 클래스의 설계와 두 가지 실무 활용 예제를 정리함.
1. 아키텍처 개념 및 데이터 흐름
인자가 추가될 때마다 함수의 선언부를 고치거나 화면 간에 유닛을 직접 참조(uses)하는 방식은 결합도를 높여 관리가 힘들어짐.
- 해결책: 모든 파라미터를
TModuleLink라는 단 하나의 객체에 키-밸류(Key-Value) 형태로 담고, 이 객체의 메모리 주소(포인터)만 전달하는 '파라미터 객체 패턴(Parameter Object Pattern)'을 적용함. - 메모리 효율: 아무리 많은 데이터를 담아도 전달되는 것은 오직 4바이트 또는 8바이트의 주소값 딱 하나이므로 메모리 복사 오버헤드가 없어 실행 속도가 극도로 빠름.
2. [공통 유닛] 타입 명시형 TModuleLink 소스코드 (uCommonLink.pas)
내부적으로 TStrings 파싱 엔진을 사용하되, 외부에는 타입을 명시한 메서드만 노출하여 안정성과 코드 자동완성 편의성을 확보함.
unit uCommonLink;
interface
uses
System.Classes, System.SysUtils, System.Generics.Collections, Winapi.Messages, Winapi.Windows;
const
// 객체 간 통신에 사용할 사용자 정의 윈도우 메시지 ID
WM_CUSTOM_COMMUNICATION = WM_USER + 2001;
type
TModuleLink = class
private
FParams: TStrings;
public
constructor Create;
destructor Destroy; override;
// 1. 타입 명시형 데이터 저장 (Write)
procedure WriteStr(const AKey, AValue: string);
procedure WriteInt(const AKey: string; AValue: Integer);
procedure WriteBool(const AKey: string; AValue: Boolean);
procedure WriteFloat(const AKey: string; AValue: Double);
procedure WriteDateTime(const AKey: string; AValue: TDateTime);
// 2. 타입 명시형 데이터 추출 (Read - 안전 방어코드 포함)
function ReadStr(const AKey: string; const ADefault: string = ''): string;
function ReadInt(const AKey: string; const ADefault: Integer = 0): Integer;
function ReadBool(const AKey: string; const ADefault: Boolean = False): Boolean;
function ReadFloat(const AKey: string; const ADefault: Double = 0.0): Double;
function ReadDateTime(const AKey: string; const ADefault: TDateTime = 0): TDateTime;
// 3. 데이터 관리 기능
procedure DeleteParam(const AKey: string);
function ContainsKey(const AKey: string): Boolean;
// 4. 메시지 전송 및 수신 헬퍼 엔진 (윈도우 메시지용)
function SendSingle(ATargetHWnd: HWND): Boolean;
procedure SendBroadcast(const ATargetList: TList<HWND>);
class function ReceiveLink(var AMessage: TMessage): TModuleLink;
property Params: TStrings read FParams;
end;
implementation
{ TModuleLink }
constructor TModuleLink.Create; begin inherited; FParams := TStringList.Create; end;
destructor TModuleLink.Destroy; begin FParams.Free; inherited; end;
{ --- Write Methods --- }
procedure TModuleLink.WriteStr(const AKey, AValue: string); begin FParams.Values[AKey] := AValue; end;
procedure TModuleLink.WriteInt(const AKey: string; AValue: Integer); begin FParams.Values[AKey] := IntToStr(AValue); end;
procedure TModuleLink.WriteBool(const AKey: string; AValue: Boolean); begin FParams.Values[AKey] := BoolToStr(AValue, True); end;
procedure TModuleLink.WriteFloat(const AKey: string; AValue: Double); begin FParams.Values[AKey] := FloatToStr(AValue); end;
procedure TModuleLink.WriteDateTime(const AKey: string; AValue: TDateTime); begin FParams.Values[AKey] := DateTimeToStr(AValue); end;
{ --- Read Methods --- }
function TModuleLink.ReadStr(const AKey: string; const ADefault: string): string;
begin
Result := FParams.Values[AKey]; if Result = '' then Result := ADefault;
end;
function TModuleLink.ReadInt(const AKey: string; const ADefault: Integer): Integer;
begin
Result := StrToIntDef(FParams.Values[AKey], ADefault);
end;
function TModuleLink.ReadBool(const AKey: string; const ADefault: Boolean): Boolean;
begin
Result := StrToBoolDef(FParams.Values[AKey], ADefault);
end;
function TModuleLink.ReadFloat(const AKey: string; const ADefault: Double): Double;
begin
Result := StrToFloatDef(FParams.Values[AKey], ADefault);
end;
function TModuleLink.ReadDateTime(const AKey: string; const ADefault: TDateTime): TDateTime;
begin
Result := StrToDateTimeDef(FParams.Values[AKey], ADefault);
end;
{ --- Management Methods --- }
procedure TModuleLink.DeleteParam(const AKey: string);
var Idx: Integer;
begin
Idx := FParams.IndexOfName(AKey); if Idx >= 0 then FParams.Delete(Idx);
end;
function TModuleLink.ContainsKey(const AKey: string): Boolean;
begin
Result := FParams.IndexOfName(AKey) >= 0;
end;
{ --- Transmission Methods --- }
function TModuleLink.SendSingle(ATargetHWnd: HWND): Boolean;
begin
Result := False; if ATargetHWnd = 0 then Exit;
Result := (Winapi.Windows.SendMessage(ATargetHWnd, WM_CUSTOM_COMMUNICATION, 0, LPARAM(Self)) = 1);
end;
procedure TModuleLink.SendBroadcast(const ATargetList: TList<HWND>);
var TargetHWnd: HWND;
begin
if not Assigned(ATargetList) then Exit;
for TargetHWnd in ATargetList do
if (TargetHWnd <> 0) and IsWindow(TargetHWnd) then
Winapi.Windows.SendMessage(TargetHWnd, WM_CUSTOM_COMMUNICATION, 0, LPARAM(Self));
end;
class function TModuleLink.ReceiveLink(var AMessage: TMessage): TModuleLink;
begin
Result := nil; if AMessage.Msg = WM_CUSTOM_COMMUNICATION then Result := TModuleLink(AMessage.LParam);
end;
end.
3. 실무 활용 예제 예시
예제 A. 윈도우 메시지를 이용한 폼(Form)간 데이터 전송
두 폼이 서로의 존재를 직접 알지 못해도(uses 참조가 없어도), 핸들(HWND)만 알면 안전하게 복합 데이터를 비결합 통신으로 주고받을 수 있음.
1) 데이터를 보내는 쪽 (자식 폼 또는 팝업 폼)
procedure TChildForm.BtnSendToParentClick(Sender: TObject);
var
Link: TModuleLink;
begin
Link := TModuleLink.Create;
try
// 키-밸류 형식으로 전송할 데이터를 세팅
Link.WriteStr('Command', 'POPUP_SELECTION');
Link.WriteInt('SelectedID', 1024);
Link.WriteStr('ItemName', '아메리카노');
Link.WriteBool('IsTakeOut', True);
// 기억해 둔 부모 폼의 핸들(FParentHandle)로 링크 전송
Link.SendSingle(FParentHandle);
finally
Link.Free; // 동기 전송(SendMessage)이므로 완료 후 즉시 해제
end;
end;
2) 데이터를 받는 쪽 (부모 폼 또는 메인 폼)
// 부모 폼의 private 또는 protected 영역에 메시지 핸들러 선언 필요
// procedure WMCustomCommunication(var Message: TMessage); message WM_CUSTOM_COMMUNICATION;
procedure TParentForm.WMCustomCommunication(var Message: TMessage);
var
Link: TModuleLink;
begin
// 메시지 매개변수로부터 Link 객체 주소 복원
Link := TModuleLink.ReceiveLink(Message);
if not Assigned(Link) then Exit;
if Link.ReadStr('Command') = 'POPUP_SELECTION' then
begin
// 타입별 함수를 사용해 캐스팅 없이 값 추출
ShowMessage(Format('선택된 상품: [%d] %s (포장여부: %s)',
[Link.ReadInt('SelectedID'),
Link.ReadStr('ItemName'),
BoolToStr(Link.ReadBool('IsTakeOut'), True)]));
Message.Result := 1; // 성공 피드백 반환
end;
end;
예제 B. 함수 매개변수(Parameter) 단일화를 통한 데이터 전달
함수의 요구 인자가 계속 늘어나는 '롱 파라미터 리스트' 현상을 방지함. 데이터가 추가되거나 삭제되어도 함수의 선언부(틀)를 변경할 필요가 없어 하위 호환성이 100% 유지됨.
1) 함수를 호출하는 쪽
procedure TMainForm.BtnSubmitClick(Sender: TObject);
var
UserParam: TModuleLink;
begin
UserParam := TModuleLink.Create;
try
// 함수에 보낼 수십 개의 파라미터를 Link 객체 하나에 축약
UserParam.WriteStr('ID', 'delphi_master');
UserParam.WriteStr('Name', '홍길동');
UserParam.WriteInt('Age', 29);
UserParam.WriteBool('IsActive', True);
// 비즈니스 고도화로 새로 추가된 파라미터 (함수 선언은 고치지 않음)
UserParam.WriteStr('UserGrade', 'VIP');
// 인자 딱 하나만 넘겨서 비즈니스 함수 호출
RegistUser(UserParam);
finally
UserParam.Free;
end;
end;
2) 함수를 제공하는 쪽 (구현부)
// 파라미터 항목이 증감해도 프로시저의 선언부 틀은 평생 고칠 필요가 없음
procedure TMainForm.RegistUser(ALink: TModuleLink);
var
sID, sName, sGrade: string;
nAge: Integer;
begin
if not Assigned(ALink) then Exit;
// 타입 명시형 함수로 안전하게 데이터 획득
sID := ALink.ReadStr('ID');
sName := ALink.ReadStr('Name');
nAge := ALink.ReadInt('Age', 20); // 데이터 누락 시 기본값 20세 방어코드 작동
// 나중에 추가된 파라미터도 함수 내부에서 유연하게 대응 가능
sGrade := ALink.ReadStr('UserGrade', 'General');
// 이후 회원 등록 DB 로직 처리 진행...
end;
결론 및 정리
- 폼간 통신 효율성: 유닛 소스코드 결합을 완전히 배제하고 오직 윈도우 핸들과 통신용 메시지만으로 유연한 데이터 제어가 가능함.
- 함수 관리 효율성: 파라미터 추가 시 유발되는 도미노 식의 코드 수정 및 오버로딩(
overload) 노가다를 차단하여 하위 호환성을 완벽하게 보장함. TModuleLink클래스는 프로젝트 전반에서 화면 간의 비결합 통신 매개체이자, 복잡한 비즈니스 데이터의 DTO(Data Transfer Object) 역할까지 동시에 수행할 수 있는 델파이 표준 범용 데이터 구조체임.