[DelphiCon 요약] Spring4D 소개 – 델파이 개발을 한 수준 높이기


Spring4D 소개 – 델파이 개발을 한수준 높이기 (Introduction to Spring4D – Taking Delphi Development to the Next Level) 를 요약했습니다. (이 요약 번역은 원본 비디오와 내용이 일부 다르거나, Q&A등 일부 생략되었을 수 있습니다.)

이 스프링4D는 델파이 개발을 더 쉽고 더 견고하게 할 수 있도록 미리 구성된 라이브러리입니다. 많은 개발자들에게 필요했던 타입과 확장 기능이 구현되어 있습니다. 예를 들어, 스마트 포인터를 사용하면 메모리 누수 걱정없이 오브젝트를 만들어 쓸 수 있습니다. Spring4D의 컬렉션은 컬렉션이나 리스트 관리가 매우 간편합니다. (이런 라이브러리 또는 프레임워크를 사용하면) 코드는 더 짧아지고, 일관성과 품질은 더 높아집니다.

  • Spring4D란?
  • Spring4D를 받는 방법
  • Spring4D의 기본 구성
  • Spring4D 구성 요소: Spring.Base
  • Nullable<T>
  • Event<T>
  • 스마트 포인터 (Shared<T> 와 Weak<T>)
  • Collections
  • Collections 인터페이스
  • 읽기전용 vs 불변
  • IEnumberable<T>
  • 스트리밍과 지연 실행
  • ICollection<T>
  • IList<T>
  • IMap, IDictionary, IMultiMap
  • 더 많은 내용

발표자 (Stefan Glienke)는 델파이 경력 20년이 넘은 개발자이며, Spring4D 오픈소소의 개발 책임자입니다. (웹페이지: https://delphisorcery.blogspot.com )

Spring4D 란

  • 델파이 오픈소스 라이브러리 (2010년에 출시된 XE와 그 이후 버전에서 사용 가능)
    • 상업용 무료 사용 (아파치 2.0 오픈 소스 라이선스)
    • 델파이 RTL 확장
    • 제네릭스(Generics)와 RTTI를 적극 활용
    • 계속 발전하는 중이고 상업용 소프트웨어 개발에 활용되고 있음
  • 원칙: “골라 쓰기” – 원하는 것만 골라서 사용할 수 있다. 강제하지 않는다.
  • 버전: 2020년 12월 현재 1.2.4 – 곧 2.0 발표 예정

Spring4D를 받는 방법

  • Git에서 내려받기 https://bitbucket.org/sglienke/spring4d.git (버전콘트롤을 사용하지 않는다면 전체 파일 다운로드)
  • Build.exe 를 실행하여 간편하게 설치/설정 가능 (또는 전체 파일을 직접 컴파일 하여 사용하기)
  • 코드를 모두 받아서 컴파일하여 사용할 수 있지만, 미리 컴파일된 .dcu를 사용하는 것이 편함 (단 몇초지만 매번 컴파일하는 시간도 아끼자)

Spring4D의 기본 구성

  • Base: RTL 확장, 컬렉션
  • Core: 의존성 주입(DI, Dependency Injection) 컨테이너, 인터셉션/목킹(Mocking)
  • Data: ObjectDataSet을 이용해 (TDBGrid 등) 데이터를 인식하는 UI 콘트롤에 연결
  • Persistence: ORM
  • Extensions: 암호화 및 기타 유틸리티 (암호화는 다른 암호화 전문 라이브러리보다 약함)

Spring4D 구성 요소: Spring.Base

  • Nullable<T>
  • Event<T> 멀티 캐스트 이벤트
  • 스마트 포인터 (Shared<T> 와 Weak<T>)
  • Collections: 가장 많이 활용되는 라이브러리
  • IEnumerable<T>
  • 리스트, 딕셔너리, 멀티맵, 세트, 큐

Nullable<T>

  • 특정 타입을 지키는 데이터 타입(Type safe date type) 이면서도 Null값을 가질 수 있다.
  • Null (값이 없음)을 넣기 위해 아래와 같이 억지로 만든 값을 사용할 필요가 없다.
    • 날짜 타입에 “날짜가 주어지지 않음”을 넣기 위해 -40000 이라는 가상의 값 넣기
    • Boolean에 “참인지 거짓인지 모름”을 넣기 위해 True/False/FileNotFound로 Inum을 직접 만들기
  • Variant 타입과는 다름
    • Variant는 호환 가능한 타입 변환을 암시적으로 수행하므로 언제든 다른 타입으로 변환될 여지가 있지만 Nullable<T>는 타입을 지키므로 정해진 타입 이외에는 담을 수 없다.(type safe)
    • 예를 들어 Variant에는 숫자 3과 문자열 ‘3’을 모두 넣을 수 있고, 상황에 따라 사용되지만, Nullable<Integer>에는 정수 3만 넣을 수 있고 문자열 3을 넣을 수는 없다.

//// Nullable<T> 예문

uses

  Spring;

procedure …

var

  n, n2: Nullable<TDateTime>; // uses Spring (Spring 유닛 사용) 필요

  d: TDateTime;

begin

  d := now;

  Log.Lines.Add(n.HasValue.ToString);

  Log.Lines.Add(n.ToString); // DataTime을 문자열로 출력할 때는 지역 설정 등 RTL에서 적용된 형식으로 출력

  Log.Lines.AddParagraph;

 

  n := d;

 

  Log.Lines.Add(n.HasValue.ToString); // 로컬 변수로 사용된 Nullable<T>은 할당을 하지 않고 바로 사용 가능

  Log.Lines.Add(n.ToString);

  Log.Lines.AddParagraph;

 

  n := nil; //n에 들어있는 값을 없앤다

 

  Log.Lines.Add(n.HasValue.ToString);

  Log.Lines.Add(n.GetValueOrDefault().ToString); // GetValueOrDefault() 값이 있으면 그 값을, 없으면 기본값을 출력

  Log.Lines.AddParagraph;

 

  Log.Lines.Add((n=n2).ToString);

  n := d;

  Log.Lines.Add((n=n2).ToString);

  n2 := d;

  Log.Lines.Add((n=n2).ToString);

end;

 

////결과 (및 해설)

False // 기본값이 적용되면, Nullable.HasValue의 결과는 False

Null // 기본값이 적용되면, Nullable.ToString의 결과는 ‘Null’(값없음)

 

True // 특정 시간을 넣으면, Nullable.HasValue의 결과는 True

19.11.2020 18:11:51 //독일 기준 날짜 형식 (발표자의 시간대가 독일로 되어 있음)

 

False // nil을 넣으면, Nullable.HasValue의 결과는 False

30.12.1899 // Nullable.GetValueOrDefault().ToString의 결과는 값없는 경우에 출력할 날짜 기본값

 

True // n과 n2는 둘다 값이 없으므로, n과 n2를 비교하면 ‘같음’이 된다

False // n에만 d를 넣고, n과 n2를 비교하면 ‘다름’이 된다

True // n과 n2는 둘다 d와 같은 값을 가지므로, n과 n2를 비교하면 ‘같음’이 된다.

Event<T>

  • (필요한 타입을 따로 만들 필요없어서 사용이 쉬운) Observer 패턴과 유사하다.
  • publish와 Subscribe를 구현하기 간단하다. 델파이에서 일반 이벤트 사용하는 것과 방식이 같다.
  • 제네릭 타입을 사용하므로 파라미터와 리턴 타입이 유연하다
    • <T>에 (TNotifyEvent 등) 원하는 타입을 지정하면 된다.
  • 빠르고 쓰레드에 안전하다

//// Event<T> 멀티 캐스트 이벤트 예문

uses

  Spring;

 

private fOnMouseMove: Event<TMouseMoveEvent>; // 델파이 기본 이벤트에서 마우스 상태값을 받아온다

 

// Event<TMouseMoveEvent>를 구독(subscribe)하는 함수

procedure …

var

  subscriber: TEventSubscriber; //구독자 변수

begin

  subscriber := TEventSubscriber.Create(Self); //구독자 생성

  fOnMouseMove.Add(subscriber.HandleMouseMove); //구독자가 잡은 마우스 이동 이벤트를 넣는다 (fOnMouseMove는 실제로 이벤트핸들러들의 목록이므로 Add 메소드를 가지도록 되어있다. 그 결과 한번 작동하면 목록에 있는 모든 이벤트핸들러가 작동한다.)

  subscriber.OnUpdate.Add(ChangeCaption); //구독자의 OnUpdate이벤트 발생시 실행할 이벤트핸들러 지정 (좌표값을 폼의 캡션에 찍도록 함)

  subscriber.OnUpdate.Add(ChangeColor); // (마우스 Y좌표가 300이상이면 빨강으로 바꾸고 아니면, 일반 윈도우 색을 유지하도록 함)

  Log.OnMouseMove := fOnMouseMove; //TMomo인 Log의 OnMouseMove 이벤트에 Event<TMouseMoveEvent>를 연결하여 포함된 이벤트핸들러가 모두 실행되도록 한다.

  fOnMouseMove.OnChange := NotifyEventChange; //Event<T>에 이벤트핸들러가 추가/삭제 되면 공지한다. (예를 들어 이 예문에서 폼을 종료하면, 이 이벤트가 작동된다. 그 이유는 TEventSubscriber.Create(Self)로 생성되었고, Self는 MainForm였다. 따라서 TEventSubscriber의 owner 역시 메인폼이 된다. 메인 폼이 제거되면, 이 구독자 역시 제거되고 등록된 이벤트핸들러 역시 제거된다.)

end;

스마트 포인터 (Shared<T> 와 Weak<T>)

  • 오브젝트와 기타 리소스의 생명주기가 관리 능력 추가
  • try finally 를 사용하여 명시적으로 오브젝트를 제거하지 않아도 된다. 이점은 여러 리소스 간에 오브젝트를 주고 받는 것 역시 간편하게 해준다.(어느 곳에서 제거해야 하는지를 걱정할 필요가 없다)
  • Weak<T>에서는 순환 참조 문제가 없다 (심지어 델파이에 [Weak] 속성이 추가되기 전부터 있었다)
  • 보너스: Weak<T>로 만든 오브젝트는 모든 플랫폼에서 작동한다. 또한 오브젝트가 제거될 때 알림을 받을 수 있다

//// 스마트 포인터 (Shared<T> 와 Weak<T>) 예문

// Shared<T>: 레코드 타입으로 사용할 때

procedure …

var

  sl: Shared<TStringList>;

begin

  sl := TStringList.Create; // operator overloading

  sl.Value.Add(‘델파이콘’); // 멤버에 직접 액세스하지 못하고 Value 속성을 사용하여 액세스한다

  Log.Lines.AddStrings(sl); //<T>에서 지정한 타입을 그냥 전달하는 것과 같은 방식으로 전달될 수 있다.

end;

 

// IShared<T>: 인터페이스 타입으로 사용할 때

procedure …

var

  sl: IShared<TStringList>; // 인터페이스 타입으로 사용할 때

begin

  sl := Shared.Make<TStringList>(TStringList.Create); // 직접 생성하지 않고 인터페이스에 맞추어야 한다.

  sl.Add(‘델파이콘’); // (이 인터페이스는 익명메소드처럼 지정된 타입을 반환하기 때문에) 멤버에 직접 액세스할 수 있다.

  Log.Lines.AddStrings(sl); //<T>에서 지정한 타입을 그냥 전달하는 것과 같은 방식으로 전달될 수 있다.

end;

 

// IShared<T>로 생성된 오브젝트를 직접 제거하고 싶으면, 파라미터로 익명메소드를 넣는다.

procedure …

var

  sl: IShared<TStrings>;

begin

  sl := Shared.Make<TStrings>(TStringList.Create,

    procedure(const s: TStrings)

    begin

      Log.Lines.AddStrings(sl); //TMemo인 Log에 ‘델파이콘’을 출력하고

      s.Free; //생성된 TStrings를 제거

    end);

  sl.Add(‘델파이콘’);

end;

 

// 스마트 포인터로 만든 오브젝트는 오브젝트 제거 코드를 쓰지 않아도. 자동 제거되므로 메모리 누수를 염려하지 않아도 된다.

// ReportMemoryLeakOnShutdown := True; //델파이 프로젝트에서 메모리 누수를 확인하는 코드

 

// Weak<T>

procedure …

var

  sl: TStringList; //Share<T>가 아닌 일반 TStringList

  weakRef: Weak<TStrings>; //문자열을 Weak 참조하기로 한다.

begin

  sl := TStringList.Create;

  weakRef := sl; // operator overloading

  sl.Add(‘델파이콘’);

 

  Log.Lines.Add(weakRef.IsAlive.ToString);

  Log.Lines.AddStrings(weakRef); // operator overloading

  Log.Lines.AddParagraph;

 

  sl.Free; //Share<T> 가 아니므로 직접 Free 했다. 그러면, Weak 참조하고 있는 곳에 모두 공지된다.

 

  // Weak<T>는 모든 플랫폼에서 작동된다.

  Log.Lines.Add(weakRef.IsAlive.ToString);

  Log.Lines.Add(Assigned(weakRef.Target).ToString); //Weak<T>.Target은 Shared<T>.Value와 같은 역할

  Log.Lines.Add(weakRef <> nil).ToString;

end;

 

////결과 (및 해설)

True // 참조 타겟이 살아있다

델파이콘 // 참조 타겟의 값

False // ‘참조 타겟이 제거되었음’을 알고 있음

False // ‘참조 타겟이 Assigned 되지 않았음’을 알고 있음

False // ‘참조 타겟이 nil 임’을 알고 있음

이 외에도 다음의 내용들을 다룹니다. 더 자세한 내용은 원본 비디오를 통해 확인해보세요:

  • Collections
  • Collections 인터페이스
  • 읽기전용 vs 불변
  • IEnumberable<T>
  • 스트리밍과 지연 실행
  • ICollection<T>
  • IList<T>
  • IMap, IDictionary, IMultiMap
  • 더 많은 내용


app benchmarks C++ consulting DelphiCon getit idea ideas market migration news newsletter online services planning portfolio promotion rad서버 RAD스튜디오 technical tip tips UIUX whitepaper 개발 개발 사례 개발사례 개발자 겟잇 교육 기술백서 데모 델파이 마이그레이션 멀티플랫폼 모바일 생산성 엔터프라이즈커넥터 윈도우 의료 출시 컨설팅 크로스플랫폼 프로그래밍 프로모션 프로젝트