Skip to main content

크로미움으로 새로운 브로우저 만들기

핵심 강의

동영상 준비 중

강의 개요

크로미움을 이용해서 자신만의 브로우져를 만드는 방법을 설명합니다. 하이브리드 방식으로 어플리케이션을 개발할 때 활용할 수 있습니다.

아래는 이 강의에서 사용하는 방법으로 만든 프로그램의 예입니다.

강의 전 준비 사항

이 강의에서 다룰 내용

  • 크로미움을 이용한 자신만의 브로우저 만들기
  • 크로미움 사용 시 주의해야 할 것들
  • 자바스크립트 연동하기

프로젝트 시작하기

프로젝트 파일 코드 설명

크로미움 콤포넌트를 사용하기 위해서는 프로젝트 파일의 소스를 수정해야 합니다.

program cef;

uses
uCEFApplication,
uCEFv8Handler,
Vcl.Forms,
_fmMain in '_fmMain.pas' {fmMainOfCEF},
Core in 'Core\Core.pas',
View in 'Core\View.pas',
JavaScript in 'Core\JavaScript.pas',
Globals in 'Globals.pas';

{$R *.res}

begin
GlobalCEFApp := TCefApplication.Create;
GlobalCEFApp.EnableGPU := true;
GlobalCEFApp.UserAgent := GlobalCEFApp.UserAgent + 'AsomeCodeApp';
GlobalCEFApp.DeleteCache := false;
GlobalCEFApp.DeleteCookies := false;
GlobalCEFApp.OnWebKitInitialized :=
procedure ()
begin
TCefRTTIExtension.Register('App', TJSExtension);
end;
if not GlobalCEFApp.StartMainProcess then Exit;
///
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TfmMainOfCEF, fmMainOfCEF);
Application.Run;
//
DestroyGlobalCEFApp;
end.
  • 4-5: 라인과 같이 uCEFApplication, uCEFv8Handler 유닛 두 개를 uses 절에 추가합니다.
  • 16-26: 라인처럼 크로미움 어플리케이션 객체 생성 및 설정 코드를 삽입합니다.
  • 33: 라인처럼 크로미움 라이브러리의 리소스를 해제하는 코드를 삽입합니다.
  • 2-25: 라인은 자바스킙트를 연동하기 위해 삽입된 코드입니다.
  • 24: 라인에서 자바스크립트 핸들러를 "App"이라는 이름으로 등록합니다. TJSExtension는 여러분들이 직접 만들어야하는 클래스입니다. 예제 소스의 JavaScript.pas 유닛을 참고하시기 바랍니다.
    • html에서 자바스크립트로 "App.test()"라고 호출하면 TJSExtension의 test() 메소드를 찾아서 호출하게 됩니다. 즉, 웹에서 자바스크립트 함수 "App.test()" 호출하면 실제 코드는 델파이 쪽에서 실행됩니다.

메인폼 코드 설명

아래는 메인폼의 소스를 일부분 가져와서 설명한 내용입니다.

소스코드 원본 보기

interface 영역 설명

unit _fmMain;

interface

uses
JsonData, Disk,
uCEFApplication, uCEFInterfaces, uCEFTypes, uCEFStringVisitor, uCEFCookieManager,
....;

type
TfmMainOfCEF = class(TForm)
...
private
procedure WMCopyData(var Msg:TWMCopyData); message WM_COPYDATA;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
procedure rp_GoHome(AJsonData:TJsonData);
end;

var
fmMainOfCEF: TfmMainOfCEF;
  • 6: 라인
    • JsonData: json 파싱을 위해서 필요합니다.
    • Disk: 디스크 입출력 및 기타 정보를 제공합니다.
  • 7: 크로미움을 이용하기 위해서 필요한 유닛들입니다. 자동으로 삽입되지 않는 유닛들은 직접 입력해야 합니다.
  • 14: 자바스크립로 델파이 함수를 호출할 때 크로미움의 프로세스에서 호출이 됩니다. 따라서 자바스크립트에서 호출된 함수에서 메인 스레드 또는 스레드 세이프하지 않은 코드를 실행하는 것은 위험합니다. 그래서 WM_COPYDATA를 이용하여 메인 스레드로 메시지를 전달하여 사용하는 방식을 사용하고 있습니다. 스레드 세이프한 작업은 자바스크립트가 호출한 함수에서 직접 처리해도 됩니다.
  • 19: RyuLib에서 제공하는 방식으로 WM_COPYDATA 메시지를 수신하면 메시지 안에 표기된 함수 이름으로 함수 자동으로 찾고 호출하는 방식입니다. 반드시 published 영역에 있어야 합니다. 자세한 설명은 동영상에서 다루도록 하겠습니다.

구현 코드 설명

constructor TfmMainOfCEF.Create(AOwner: TComponent);
begin
inherited;

Chromium.Options.FileAccessFromFileUrls := STATE_ENABLED;
Chromium.Options.WebSecurity := STATE_DISABLED;

TCore.Obj.View.Add(Self);
end;

destructor TfmMainOfCEF.Destroy;
begin
TCore.Obj.View.Remove(Self);

inherited;
end;
  • 5: 로컬 하드디스크의 html 파일 등을 읽을 수 있도록 합니다.
  • 6: CORS(Cross-Origin Resource Sharing)을 무시하도록 합니다. 로컬 html 파일에서 전혀 다른 도메인의 REST api를 호출 할 수 있도록 합니다.
  • 8: 메시지 수신만으로 원하는 메소드를 실행 할 수 있도록 이(Self) 객체를 등록합니다.
  • 13: 더 이상 메시지 수신으로 메소드를 실행하지 않습니다.
procedure TfmMainOfCEF.FormShow(Sender: TObject);
begin
Chromium.CreateBrowser(CEFWindowParent);
tmStart.Enabled := true;
end;

procedure TfmMainOfCEF.tmStartTimer(Sender: TObject);
begin
tmStart.Enabled := false;
rp_GoHome(nil);
end;

procedure TfmMainOfCEF.rp_GoHome(AJsonData: TJsonData);
begin
Chromium.LoadURL(GetExecPath + 'index.html');
end;
  • 3: 메인 폼이 화면에 보이기도 전에 크로미움이 초기화되면 오류가 날 수 있습니다. 따라서 메인 폼의 OnShow 이벤트에서 크로미움을 초기화합니다.
  • 4: 초기화하고 곧바로 크로미움으로 웹 페이지를 오픈하면 오류가 날 수 있습니다. 타이머를 이용하여 약간의 간격을 두고 페이지 오픈을 실행하도록 합니다.
  • 9: 타이머가 반복해서 실행되지 않도록 합니다.
  • 10: 메인 페이지를 브라우져에 표시합니다.
procedure TfmMainOfCEF.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caNone;
Hide;
tmBye.Enabled := true;
end;
  • 프로그램을 곧바로 중료하면 병목을 일으여 멈추는 등의 문제가 있다면 tmBye.OnTimer 이벤트 핸들러 안에서 처리하면 됩니다. 필자는 사용 중인 스레드 종료처리 등으로 프로그램 종료가 오래 결리는 경우에 이 방법을 많이 사용합니다.
  • 3: 메인 폼이 닫겨도 프로그램이 종료되지 않도록합니다.
  • 4: 메인 폼은 감춰서 프로그램 종료가 오래 걸려서 답답함을 느끼지 않도록 합니다.
  • 5: 간격을 두고 종료처리가 될 수 있도록 타이머를 작동시킵니다.
procedure TfmMainOfCEF.tmByeTimer(Sender: TObject);
begin
Application.Terminate;
end;
  • tmBye 타이머가 1초 후에 작동하면 프로그램을 완전히 종료합니다. 시간 간격은 필요한만큼 지정하시면 됩니다.
procedure TfmMainOfCEF.WMCopyData(var Msg: TWMCopyData);
var
text : ansistring;
begin
text := PAnsiChar(Msg.CopyDataStruct.lpData);
TCore.Obj.View.AsyncBroadcast(text);
end;
  • 자바스크립트에서 델파이 함수를 호출했을 때 직접 처리하지 않고 WM_COPYDATA 메세지를 거쳐서 델파이의 메인 스레드와의 충돌 문제를 해결합니다.

JavaScript 유닛 설명

unit JavaScript;

interface

uses
HandleComponent,
Windows, Messages, SysUtils, Classes, Dialogs;

type
TJSExtension = class
class function version:string;
class procedure Command(text:string);
end;

TJavaScript = class (THandleComponent)
private
procedure WMCopyData(var Msg:TWMCopyData); message WM_COPYDATA;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;

class function Obj:TJavaScript;

procedure SendString(const AText:ansistring);
end;

...

class function TJSExtension.version: string;
begin
Result := '1.0';
end;

class procedure TJSExtension.Command(text:string);
begin
TJavaScript.Obj.SendString(text);
end;

procedure TJavaScript.SendString(const AText: ansistring);
var
receiverHandle : THandle;
copyDataStruct : TCopyDataStruct;
begin
receiverHandle := FindWindow('TfmMainOfCEF', nil);
if receiverHandle = 0 then Exit;

copyDataStruct.dwData := 0;
copyDataStruct.cbData := 1 + Length(AText);
copyDataStruct.lpData := PAnsiChar(AText);
SendMessage(receiverHandle, WM_COPYDATA, integer(Handle), Integer(@copyDataStruct));
end;

...
  • 자바스크립트에서 "App.함수명()" 형식으로 델파이 코드를 호출 할 수 있도록 합니다. 대부분 코드는 그대로 사용하시면 되기 때문에 수정해야 할 부분들만 설명하도록 하겠습니다.
  • 10-13: 자바스크립트에서 호출할 함수를 정의할 클래스입니다. 모든 함수는 class 메소드로 작성하여야 합니다. 11-12: 처럼 클래스 내에 메소드를 정의하면 자동으로 자바스크립트 쪽에서 호출할 수 있게 됩니다.
  • 15-25: 자바스크립트에서 함수를 호출하면 델파이 메인 스레드로 메시지를 보내서 호출 내용을 델파이 메인 스레드에서 처리하는데 필요한 클래스입니다.
  • 29-32: 자바스크립트로 문자열을 리턴하는 함수의 예입니다.
  • 34-37: 자바스크립트에서 전송한 문자열을 델파이 메인 스레드에 전달하는 함수의 예입니다.
  • 44: 자바스크립트에서 함수를 호출하면 델파이와는 전혀 다른 프로세스에서 처리되기 때문에 윈도우 메시지를 전송해서 처리하고 있습니다. 이때 메인 폼에서 이 메시지를 수신하는데요, 여러분들이 사용하는 메인 폼이 다른 프로그램의 메인 폼과 클래스 이름이 다르게 하셔야 합니다. 44: 라인에서는 여러분들이 메시지를 수신하고 싶은 폼의 클래스 이름을 입력해주시면 됩니다.