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
  • ISet, IMultiSet

  • 더 많은 내용

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

 

원본 비디오 시청: https://delphicon.embarcadero.com/talks/introduction-to-spring4d-taking-delphi-development-to-the-next-level/

 

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

  • 인터페이스 기반이다.
    • 메모리 관리가 매우 쉽다
    • Collection를 함수에서 반환하고, 어느 곳에서든 사용하기만 하면 된다. 사용이 끝나면 자동 제거 된다.
  • IEnumberable<T>를 사용하여 API가 확장되었다.
    • 명확하게 표현되는 코드를 사용하도록 메소드가 제공된다.
    • 성가시게 루프를 사용할 필요가 적어진다.
  • Systems.Generics.Collections 대체한다.
    • 100%는 아니지만 왠만한 건 다 있고 델파이 컬렛션을 Spring.Collections로 마이그래이션도 쉽다.
  • TCollections와 IEnumberable에서 항상 팩토리 메소드를 사용한다.
    • (델파이 컬렉션과 달리) 클래스를 직접 사용하면 안된다. 제공되는 팩토리 메소드를 사용해야 한다.
    • Spring.Collection.* 유닛을 uses에 추가하면 해당 컬렉션을 사용할 수 있다.

Collections 인터페이스

  • IEnumberable<T>
    • (정렬되지 않은) 항목들의 나열을 제공하는 기반 타입
  • ICollection<T>, IReadOnlyCollection<T>
    • (인덱스 번호가 지정되지 않은) 항목들의 컬렉션, List의 기반 타입
  • IList<T>, IReadOnlyList<T>
    • (인덱스 기반의 메소드가 제공되는) 리스트
  • IMap<TKey, TValue>, IReadOnlyMap<TKey, TValue>
    • Dictionary와 MultiMap의 기반 타입
  • IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue>
  • IMultiMap<TKey, TValue>, IReadOnlyMultiMap<TKey, TValue>
  • ISet<T>
    • MultiSet의 기반 타입
  • IMultiSet<T>, IReadOnlyMultiSet<T>

읽기전용 vs 불변

  • Spring4D에는 불변 컬렉션이 없다. (추가/변경/삭제가 해당 컬렉션 안에서 발생한다)
  • 하지만, 모든 타입은 ReadOnly 버전이 제공된다. (AsReadOnly 메소드 사용), 멀티쓰레드에 안전하지 않지만, 컬렉션을 사용하는 곳에서 추가/변경/삭제를 할 수 없다.
  • 컬렉션을 읽기 전용 목적으로 전달할 때에는 IEnumberable<T> 또는 알맞은 ReadOnly 컬렉션을 사용하자.

IEnumberable<T>

  • (정렬되지 않고) (실현되지 않은, not materialized) 항목들의 나열을 제공하는 기반 타입
  • 모든 컬렉션 타입의 기반
  • 사용할 수 있는 메소드가 매우 많음
  • ‘(실현되지 않은, not materialized)’ 이란?
    • 쿼리문 이라고 생각하면 됨 (Where절에 명시한 조건에 따라 가져올 항목이 결정되는 SQL구문과 유사)
    • SQL문이 실행되어야 해당 데이터 집합이 생기는 것과 같은 방식 (실행 전에는 해당 조건에 해당하는 데이터 컬렉션이 없다)
    • 물론 어딘가에는 전체 데이터가 있다. 하지만, 내가 원하는 컬렉션 세트는 조건이 실행되어야 생긴다.

//// IEnumberable<T>예문 (주문 총액이 9,000 이상인 고객 목록)

customers.Where (


  function (const c : TCustomer): Boolean


  begin


    Result := c.OrderTotal > 9000; 


  end);

 

스트리밍과 지연 실행

  • IEnumberable<T>의 작동은 최대한 늦게 실행된다
    • 예를 들어, 
customer.Where(조건).Take(10);
에서 조건은, 항목 10개를 만들기 위해 필요한 만큼만 작동한다.
  • 즉, 작동이 되고 나서야 컬렉션이 만들어진다.

ICollection<T>

  • 정렬되지 않은 항목들의 (변경 가능한) 컬렉션
    • Add / Remove / Extract 메소드 제공
  • IEnumberable<T>와 달리 항상 항목을 가지고 있는 컬렉션
  • OnChange 이벤트가 제공되어서 어떠한 변경도 공지할 수 있음

IList<T>

  • 정렬된 항목들의 (변경 가능한) 리스트
  • 인덱스를 통해 각 항목에 접근 가능
  • 정렬 기반 작동 기능 제공 (Insert, Delete, IndexOf, Sort,…)

//// IEnumberable<T> 예문

procedure …

var

  numbers: IEnumberable<Integer>;

  i: Integer; 

  oddNumbers: IEnumberable<Integer>;

begin

  numbers := TEnumberable.Range(1,10);

  for i in numbers do

    Log.Lines.Add(i.ToString); // 1˜10까지 숫자 출력

    Log.Lines.AddParagraph;

 

  oddNumbers := numbers.Where(

    function(const n: Integer): Boolean

    begin

      Result := Odd(n);

    end);

  for i in oddNumbers do

    Log.Lines.Add(i.ToString); // 1~10 중 홀수만 출력

    Log.Lines.AddParagraph;

    Log.Lines.Add(oddNumbers.Last.ToString); // 9 출력

end;

 

//// IList<T> 예문

// IList<T>에는 인덱스 위치에 삽입이 가능하다.

procedure …

var

  list: IList<Integer>;

  i: Integer;

  oddNumbers: IEnumberable<Integer>;

begin

  list := TCollection.CreateList<Integer>([4,5,6]);

  list.AddRange([7,8,9]);

  list.InsertRange(0, [1,2,3]);

 

  for i in list do

    Log.Lines.Add(i.ToString); // 1˜9까지 순서대로 숫자가 출력

    Log.Lines.AddParagraph;

 

  oddNumbers := list.Where(

    function(const n: Integer): Boolean

    begin

      Result := Odd(n);

    end);

 

  Log.Lines.Add(’숫자 11까지’);

  list.Add(11); // 뒤늦게 숫자 11을 List에 추가

  for i in oddNumbers do // oddNumbers는 정의된 조건에 맞는 컬렉션을 이 시점에서 다시 제공

    Log.Lines.Add(i.ToString); // 1~11까지 중 홀수만 출력 (11도 포함된다)

    Log.Lines.AddParagraph;

end;

 

IMap, IDictionary, IMultiMap

  • IMap은 딕셔너리와 멀티맵의 기반 타입이다
    • Add, Remove, ContainKey 등등의 메소드가 제공된다.
  • IDictionary는 ‘키-값’으로 구성된 쌍들이 모여있는 컬렉션 (키는 고유해야 함)
  • IMultiMap는 일종의 딕셔너리이며 각 값에 리스트가 들어간다 (즉, 키 하나에 여러 항목이 들어갈 수 있다)
    • 값에 들어 있는 컬렉션을 다룰 수 있도록 여러 가지가 제공된다.

//// IMultiMap<TKey, TValue> 예문

// IMultiMap<TKey, TValue> 에는 키 하나에 여러 값이 들어갈 수 있다.

procedure …

var

  words: IMultiMap<Integer, string>;

  s: string;

  i: Integer;

  oddNumbers: IEnumberable<Integer>;

begin

  words := TCollection.CreateMap<Integer, string>;

 

  for s in LorenIpsum do // LorenIpsum은 저자가 만든 무작위 글자 생성기

    words.Add(s.Length, s); //키에는 단어 수를, 값에는 해당 단어를 넣어 추가한다.

 

  for i in words.Keys.Ordered do

  begin

    Log.Lines.Add(‘글자 길이가 %d자인 단어:’, [i]);

    for s in words[i] do

      Log.Lines.Add(s);

  end;

end;

 

////결과 (및 해설)

// 키(글자 수)는 정렬되었지만

// 값(해당 글자)는 정렬되지 않고, 중복되기도 한다.

글자 길이가 2자인 단어:

ut

et

At

et

et

글자 길이가 3자인 단어:

sit

sed

sed

eos

글자 길이가 … …

 

// words := TCollection.CreateMap<Integer, string>; 대신

// words := TCollection.CreateHashMap<Integer, string>;을 사용하면

// 값(해당 글자)의 중복이 제거된다.

//중복된 값은 받지 않기 때문이다.

글자 길이가 2자인 단어:

ut

et

At

글자 길이가 3자인 단어:

sit

sed

eos

글자 길이가 …

 

// 이 때, 값을 출력하는 루프에서 값도 정렬하고 싶다면,

// for s in words[i] do를

// for s in words[i].Ordered do로 변경하면 된다 

// 다른 방법으로는 (더 간단하고 성능이 좋은 방법으로는),

// words := TCollection.CreateTreeMap<Integer, string>;을 사용하면

// Ordered 없이도 값(해당 글자)의 중복이 제거될 뿐만 아니라 값도 정렬된다.

글자 길이가 2자인 단어:

At

ea

et

no

ut

글자 길이가 3자인 단어:

duo

eos

est

sea

sed

sit

글자 길이가 …

 

ISet, IMultiSet

  • Enum 세트와 유사하다. 하지만 enum 이외의 어떤 타입도 가능하다
  • 고유한 항목들로만 구성된 컬렉션이다 (마치 딕셔너리에서 값이 없는 키들의 집합과 같다.)
    • 내부적으로 hashtable 또는 tree를 통해 구현된다.
      • HashTable은 델파이 해시테이블과 달리 순서에 맞게 삽입한다.
      • Tree는 비교 기준에 맞추어 알맞은 순서로 저장한다.
  • IMultiSet은 Dictionary<T, Integer>이며 숫자 즉 갯수는 추가/삭제에 따라 증가/감소한다.
    • 갯수 파악이 매우 쉽다.

//// IMultiSet 예문

procedure …

var

  wordCounts: IMultiSet<string>;

begin

  wordCounts := TCollection.CreateMultiSet<string>(LorenIpsum); //LorenIpsum은 문자열 배열을 반환

 

  for var entry s in wordCounts.Entries do

    Log.Lines.Add(‘%s %d, [entry.Item, entry.Count]);

  end;

end;

////결과 (및 해설)

// 각 항목 별 갯수가 표시된다

Lorem 4

ipsum 4

elitr 2

sed 4

labore 2

et 8

// 만약 글자수 순으로 정렬되도록 하려면

// for var entry s in wordCounts.Entries do를

// for var entry s in wordCounts.OrderedByCount.Entries do 로 바꾸면 된다.

et 8

Lorem 4

ipsum 4

dolor 4

sit 4

amet 4

consetetur 2

sadipscing 2 …

 

더 많은 내용

 


 

번호 제목 글쓴이 날짜 조회 수
공지 [DelphiCon 요약] 코드사이트 로깅 실전 활용 기법 (Real-world CodeSite Logging Techniques) 관리자 2021.01.19 22591
공지 [UX Summit 요약] 오른쪽 클릭은 옳다 (Right Click is Right) 관리자 2020.11.16 21024
공지 [10.4 시드니] What's NEW! 신기능 자세히 보기 관리자 2020.05.27 23082
공지 RAD스튜디오(델파이,C++빌더) - 고객 사례 목록 관리자 2018.10.23 28880
공지 [데브기어 컨설팅] 모바일 앱 & 업그레이드 마이그레이션 [1] 관리자 2017.02.06 30050
공지 [전체 목록] 이 달의 기술자료 & 기술레터 관리자 2017.02.06 25397
공지 RAD스튜디오(델파이, C++빌더) - 시작하기 [1] 관리자 2015.06.30 46347
공지 RAD스튜디오(델파이,C++빌더) - 모바일 앱 개발 사례 (2020년 11월 업데이트 됨) 험프리 2014.01.16 182316
1397 N 윈도우와 맥 개발 시작을 위한 파이어몽키 코스북: 무료 다운로드 제공(385페이지) 관리자 2013.04.05 152367
1396 ComPort(시리얼 통신) 컴포넌트 설치안내 [11] file 험프리 2013.12.04 112779
1395 [REST API] REST 기반 파일 업로드와 다운로드 구현하기 험프리 2020.08.31 84735
1394 델파이 튜토리얼 자습서 이용 안내 관리자 2014.09.01 71988
1393 이 달의 기술자료 - 2014년 11월 험프리 2014.10.13 54176
1392 이 달의 기술자료 - 2014년 6월 file 험프리 2014.06.05 50404
1391 Find the O/S Language Type c2design 2014.07.30 48421
1390 RAD Studio Resource Center 박병일 2012.01.26 46644
1389 CD-ROM 열고 닫기 박병일 2011.12.22 44787
1388 [Android] 폰번호 가져오기 [1] 타락천사 2014.09.05 38644
1387 이 달의 기술자료 - 2014년 12월 file 험프리 2014.11.26 32514
1386 RAD Studio XE6 Update1 발표 [1] Humphery 2014.06.20 29499
1385 델파이XE2 파이어몽키 기반 아이폰앱 개발에서 제스춰를 인식시키는 방법 박병일 2012.01.25 23342
1384 [10.4 시드니 신기능] 새로운 VCL TEdgeBrowser 컴포넌트 험프리 2020.05.18 23197