Jump to content
과거의 기술자료(읽기 전용): https://tech.devgear.co.kr ×
과거의 기술자료(읽기 전용): https://tech.devgear.co.kr

이 사이트 검색

검색 태그: '한국어 번역'.

  • 태그로 검색

    태그 사이를 쉼표(,)로 구분하세요.
  • 작성자로 검색

콘텐츠 유형


게시판

  • 엠바카데로 (Embarcadero) 개발도구: 델파이 (Delphi), C++빌더 (C++Builder), RAD 스튜디오 (RAD Studio)
    • [기술 Q&A 게시판] 델파이, C++빌더, RAD 스튜디오
    • [설치/등록 Q&A 게시판] 델파이, C++빌더, RAD 스튜디오
    • [기술 기고 게시판] 델파이, C++빌더, RAD 스튜디오
    • [포트폴리오 게시판] 내가 참여한 프로젝트/프로그램 소개
    • [구인 게시판] 개발자 채용/프로젝트 의뢰
  • 엠바카데로 (Embarcadero) DBMS: 인터베이스 (InterBase)
    • [기술 Q&A 게시판] 인터베이스
    • [설치/등록 Q&A 게시판] 인터베이스
    • [기술 기고 게시판] 인터베이스
  • 비주얼 스튜디오 (Visual Studio) 관련 도구
    • [기술 Q&A 게시판] 비주얼 어시스트
    • [설치/등록 Q&A 게시판] 비주얼 어시스트
    • [기술 기고 게시판] 비주얼 어시스트
  • 구록 (Gurock) 테스트도구: 테스트레일 (TestRail)
    • [기술 Q&A 게시판] 테스트레일
    • [설치/등록 Q&A 게시판] 테스트레일
    • [기술 기고 게시판] 테스트레일
  • 아이데라 (Idera) 데이터 도구: 아쿠아 데이터 스튜디오 (Aqua Data Studio), ER/Studio 등
    • ER스튜디오 (ER/Studio)
    • 아쿠아 데이터 스튜디오 (Aqua Data Studio)
  • API레이어 (Apilayer) 개발 도구: API레이어 (Apilayer)
    • [Q&A 게시판] API레이어 (Apilayer)
  • 이 사이트 이용 관련
    • [게시판] 이 사이트 관련 이용 팁과 Q&A

Categories

  • 이달의 기술자료: 엠바카데로
  • 비디오 세미나
    • UX Summit
    • DelphiCon
    • CodeRage
    • 데브기어 세미나
    • Skill Sprint
  • 기술백서(PDF)

Categories

  • 시작하기
  • 설치/등록/라이선스
  • 튜토리얼
  • 도서

Categories

  • RAD 스튜디오 역사관
  • 11 알렉산드리아
  • 10.4 시드니
  • 10.3 리오
  • 10.2 도쿄
  • 10.1 베를린
  • 10.0 시애틀
  • XE8~XE
  • 2010~6.0

...에서 결과 찾기

검색어 일치 조건


최초 작성일

  • Start

    End


최종 변경일

  • Start

    End


개수로 필터링...

가입

  • Start

    End


Group


자주 쓰는 도구

  1. << DelphiCon 2021 목록으로 이동 원본 비디오(YouTube) 보기 (30 min) DelphiCon 의 2021 시리즈 중, Things That You Don’t Know About JSON in Delphi - Dion Carlos Mai & Gustavo Mena Barreto (25 min) 의 한글 요약본입니다. 델파이에서 JSON을 다루는 방법, JSON 파서를 가장 생산적으로 사용하는 방법, JSON 직렬화 작동을 커스터마이징하고 자동화하는 옵션을 설명합니다. 발표자 Gustavo Mena Barreto는 Aquasoft 선임 개발자이며, Dion Carlos Mai은 Aquasoft 개발팀장입니다. 이 세션에서 사용된 전체 소스코드: https://github.com/gustavomenabarreto/delphicon2021 목차 델파이에서 JSON 다루기를 주제로 정한 이유 JSON을 다룰 때 델파이에서 선택할 수 있는 것들 이 세션에서 사용할 JSON 샘플 JSON을 사용하기 위해 필요한 델파이 유닛(들) 1. 가장 간단한 방법 JSON 읽기 (아래 코드와 주석 참조) JSON 쓰기 (아래 코드와 주석 참조) 2. JsonToDelphi JSON 직렬화 행위를 변경하는 옵션(들) 3. Super Object (오픈소스) 4. RESTResponseDataSetAdapter 컴포넌트 REST API를 통해서 JSON 데이터를 받아서 델파이 폼에 표현하기 5. TJSONMarshal 클래스 델파이에서 JSON 다루기를 주제로 정한 이유 요즘 개발자에게 JSON은 매우 중요한 자리를 차지하고 있다. 요즘은 API 통합이 너무나 많다. 심지어 NoSQL JSON 데이터베이스도 있다. JSON을 다루는 방법이 여러가지 라는 사실을 잘 모르는 개발자들이 있다. 이 세션에서는 JSON을 다루는 여러 가지 방법을 설명한다. JSON을 다룰 때 델파이에서 선택할 수 있는 것들 가장 간단한 방법 JsonToDelphi: JSON 오브젝트를 가지고 델파이 클래스를 자동 생성하는 웹사이트 Super Object (오픈소스): 델파이 6, 7 등 구버전에서 JSON을 다룰 수 있음 (최신 버전 사용자에게는 권장하지 않음) RESTResponseDataSetAdapter 컴포넌트: JSON을 기반으로 데이터셋을 자동 생성 TJSONMarshal 클래스: JSON 직렬화 처리를 커스터마이징할 때 사용 이 세션에서 사용할 JSON 샘플 그림. 이 세션의 코드는 비트코인 블록체인에서 사용되는 매우 구조가 큰 JSON에서 극히 일부분만 뽑아 놓은 샘플 JSON을 사용한다. JSON을 사용하기 위해 필요한 델파이 유닛(들) uses System.JSON, REST.Json; 1. 가장 간단한 방법 TJSONObject와 TJSONArray를 사용하는 방법 JSON 읽기와 쓰기 (아래 코드와 주석 참조) // [JSON 읽기] var objeto : TJSONObject; ObjetoArray: TJSONArray; I: Integer; begin // ParseJSONValue를 이용하면, 단 한줄로 JSONObject를 만들 수 있다. objeto := TJsonObject.ParseJSONValue(MemoJson.Lines.Text) as TJSONObject; // JSONObject의 Get과 GetValue 메소드를 이용하면, JSON 개체의 모든 데이터에 접근할 수 있다. ShowMessage(objeto.Get('block_hash').ToString); ShowMessage(objeto.GetValue('block_hash').Value); // 배열 형식의 JSON값은 TJSONArray로 받을 수 있다. ObjetoArray := objeto.Get('inputs').JsonValue as TJSONArray; // JSONArray 안에 있는 각 요소는 for 루프를 통해 접근할 수 있다. for I := 0 to ObjetoArray.Size - 1 do begin MemoResultado.Lines.Add( ObjetoArray.Get(i).ToString + ': ' + ObjetoArray.Get(i).Value); end; end; // [JSON 쓰기] var objeto : TJSONObject; ObjetoArray: TJSONArray; ObjetoInputs: TJSONObject; begin objeto := TJSONObject.Create; ObjetoArray := TJSONArray.Create; // JSONObject의 AddPair (필드 이름, 필드 값) 메소드를 사용하여 데이터 쓰기 // 필드 값에 문자열 넣기 objeto.AddPair('block_hash','123456789'); // 필드 값에 JSONObject의 일종인 JSONArray 넣기 Objeto.AddPair('Inputs',ObjetoArray); // 'Input' 필드 값인 ObjetoArray에 들어 갈 JSONObject 만들기 ObjetoInputs := TJSONObject.Create; // 위와 동일하게 AddPair를 사용하여 JSONObject에 데이터 쓰기 ObjetoInputs.AddPair('prev_hash','987654321'); ObjetoInputs.AddPair('script','1'); // JSON 배열인 ObjetoArray의 Add 메소드를 이용하여, 배열 안에 요소 넣기 ObjetoArray.Add(ObjetoInputs); MemoResultado.Lines.Text := objeto.ToString; end; 2. JsonToDelphi https://jsontodelphi.com는 JSON 샘플을 넣으면, 해당 JSON을 다룰 수 있는 델파이 클래스를 자동으로 받을 수 있는 웹사이트이다. SOAP / XML 파서와 사용 방식이 상당히 유사하지만, XML이 아니라 JSON을 다룬다. 우리가 직접 해당 클래스를 만들기 위해 수작업으로 코드를 작성할 수도 있지만, 많은 API들이 방대한 JSON을 사용하고 있는데, 그 JSON을 모두 클래스로 만들 기 위해 수작업으로 코드를 쓰려면, 시간도 많이 걸리고 너무 수고스럽다. 무료 서비스인 이 웹사이트를 이용하면 그 모든 수고를 덜 수 있다. 생성된 클래스의 전체 소스 코드를 다운로드 할 수 있으므로, 필요한 부분을 직접 변경할 수도 있다. // [JsonToDelphi에서 만들어 준 클래스 사용하여 읽기] // 제공한 JSON 샘플의 구조가 모두 반영된 클래스가 생성되므로, 그냥 사용하면 된다. var TesteJson: TRoot; Inputs: TInputs; begin // JsonToDelphi가 만들어준 클래스 안에 JSON을 읽어서 넣기 TesteJson := TJson.JsonToObject<TRoot>(MemoJson.Lines.Text); // 데이터 타입까지 이미 알맞게 지정된 해당 클래스의 프로퍼티를 통해 JSON 필드를 접근 ShowMessage('BlockHash - '+TesteJson.BlockHash); for Inputs in TesteJson.Inputs do begin Showmessage('ScripType - '+Inputs.ScriptType); end; end; // [JsonToDelphi에서 만들어 준 클래스 사용하여 쓰기] var TesteJson: TRoot; Inputs: TInputs; I: Integer; begin // JsonToDelphi에서 만들어 준 클래스를 생성하고 TesteJson := TRoot.Create; // 프로퍼티를 이용하여 값을 쓴다. TesteJson.BlockHash := 'www.aquasoft.com.br'; // 마찬가지 방법으로 배열을 돌면서 값을 쓸 수도 있다. for I := 0 to 2 do begin Inputs := TInputs.Create; Inputs.Age := 20; Inputs.Script := 'teste script'; TesteJson.Inputs.Add(Inputs); end; // 단 한줄로 JSON 데이터를 문자로 풀어낼 수 있다. MemoResultado.Lines.Text := //이 때 joIgnoreEmptyStrings 옵션을 사용하면, 값이 빈 문자열인 데이터는, JSON 필드를 만들 지 않고 건너뛴다. // joIgnoreEmptyStrings는 델파이의 TJsonOption의 일종이다 (아래 본문의 설명 참조) TJson.ObjectToJsonString(TesteJson,[joIgnoreEmptyStrings]); end; JSON 직렬화 행위를 변경하는 옵션(들) 델파이의 REST.Json.TJsonOption https://docwiki.embarcadero.com/Libraries/Sydney/en/REST.Json.TJsonOption JSON 생성 작동 방식 중 원하는 옵션을 선택할 수 있다. 예: 유닉스 날짜 포맷 사용하기, UTC 날짜 사용, 들여쓰기, 카멜 표기, 등등 델파이 버전이 최신일수록 선택할 수 있는 직렬화 옵션이 더 많다. 이전 버전이라서 필요한 옵션이 없다면, 원하는 작동을 하는 코드를 수작업으로 써 넣는다. [그림. 델파이 11.0 알렉산드리아의 TJsonOption (참고: 이전 버전에는 옵션이 이것처럼 많지 않다)] 3. Super Object (오픈소스) 델파이 6, 7 등 구버전에서 JSON을 다룰 수 있음 무료 오픈 소스: https://github.com/hgourvest/superobject 델파이가 오래된 버전이지만, API 연동을 위해 JSON을 다루어야 한다면 이런 역할을 하는 DLL 또는 패키지의 도움을 받을 수 있다. 하지만, 직접 코드로 구현하려면 Super Object를 사용할 수 있다. 주의! 앞에서 설명했듯이, 최신 버전에는 이미 이런 기능들이 들어있으므로, Super Object를 굳이 사용할 이유가 없음 // [SuperObject를 사용하여 읽기] var Objeto: ISuperObject; Inputs: ISuperObject; begin // SuperObject의 SO 메소드를 사용하여 오브젝트를 생성 Objeto := SO(MemoJson.Lines.Text); // S 메소드는 원하는 필드와 값을 마치 배열처럼 접근 ShowMessage(Objeto.S['block_hash']); ShowMessage(Objeto['block_hash'].AsString); // for 루프 사용 가능 (for in 루프는 델파이 7 같은 구버전에는 없지만, for 문으로 유사하게 구현하면 된다.) for Inputs in objeto['inputs'] do begin MemoResultado.Lines.Add(Inputs.AsString); end; end; // [델파이 구버전에서 SuperObject를 사용하여 쓰기] var Objeto: ISuperObject; Inputs: ISuperObject; begin // 오브젝트 생성 Objeto := SO; // 데이터 쓰기 역시 마치 배열을 다루듯이 사용 Objeto.S['block_hash'] := '123456789'; Objeto.O['inputs'] := SA([]); Inputs := SO; Inputs.S['script'] := 'Teste script 1'; Inputs.I['age'] := 30; // JSON 배열을 쓸 수도 있다. Objeto.A['inputs'].O[0] := Inputs; Inputs := SO; Inputs.S['script'] := 'Teste script 2'; Inputs.I['age'] := 60; Objeto.A['inputs'].O[1] := Inputs; // AsString 메소드를 사용하여 JSON으로 쓸 수도 있다. MemoResultado.Lines.Text := Objeto.AsString; end; 4. RESTResponseDataSetAdapter 컴포넌트 RESTResponse에 받은 JSON 데이터를 FDMemTable 등의 데이터셋에 넣어주는 컴포넌트 (델파이 최근 버전에서 추가됨) [그림. REST 디버거를 사용하면 더 쉽게 만들 수 있다] REST API를 통해서 JSON 데이터를 받아서 델파이 폼에 표현하기 REST 디버거 (REST API의 읽기와 쓰기를 테스트하는 무료 도구)를 실행한다. 테스트가 성공했다면, [Copy Components] 버튼을 클릭한다. 그러면, 해당 REST API를 다루도록 설정이 완료된 델파이 컴포넌트가 클립보드에 복사된다. RESTClient: REST 서비스 제공자에게 요청을하고 응답을 받는 사용자 오브젝트 RESTRequest: RESTClient가 REST 서비스 제공자에게 전달하는 요청 RESTResponse: RESTClient가 REST 서비스 제공자로부터 전달받은 응답 RESTResponseDataSetAdapter: 전달받은 응답을 FDMemTable 등의 데이터셋에 넣어주는 매개체 FDMemTable: 데이터셋 델파이 폼에서 붙여넣기를 하면, 복사된 컴포넌트가 모두 추가되고, 되델파이 프로젝트에서 바로 사용할 수 있다. 그 후에는, 일반 데이터와 마찬가지로 FDMemTable을 사용하면 된다. 함께 복사된 REST 관련 컴포넌트들이 자동으로 이 데이터셋에 JSON 데이터를 담는다. FDMemTable 이 가장 좋고 편한 데이터셋이기 때문에 사용한다 (원한다면 다른 데이터셋을 사용해도 된다) 예를 들어, 데이터가 표현될 그리드의 데이터소스 즉 DataSource 컴포넌트의 DataSet 프로퍼티에 FDMemTable를 지정하면 된다. 직접 작성해야할 코드는 2줄 뿐이다 (아래 코드 참조) procedure TForm1.Button5Click(Sender: TObject); begin // REST 요청을 한다 RESTRequest1.Execute; // RESTResponse에 받은 JSON 데이터를 FDMemTable 등의 데이터셋에 넣기 RESTResponseDataSetAdapter1.Active := True; end; 5. TJSONMarshal 클래스 JSON 데이터 직렬화 알고리즘을 커스터마이징 할 수 있다. RTTI를 직접 건드릴 필요가 없기 때문에 보다 안전하고 편하다. RTTI를 잘못 다루면 쓰레드 문제가 발생할 수 있다. JSON을 다룰 때는 쓰레드를 사용하고 JSON 구조를 알맞게 맞추어 쓰는 것이 좋다. 보다 자세한 내용은 사용자 오브젝트 직렬화에 대한 상세한 Docwiki 도움말을 참고하기 바란다. // [TJSONMarshal을 사용하여 맞춤 JSON 구현하기] var lMarshal : TJSONMarshal; TesteJson: TRoot; //JsonToDelphi에서 제공하는 SuperObject 타입을 사용 Inputs: TInputs; I: Integer; begin TesteJson := TRoot.Create; // 오브젝트 생성 (SuperObject) TesteJson.BlockHash := 'www.aquasoft.com.br'; for I := 0 to 2 do begin Inputs := TInputs.Create; Inputs.Age := 20; Inputs.Script := 'teste script'; TesteJson.Inputs.Add(Inputs); end; // JSONMarshal을 생성할 때 컨버터를 지정 (커스터마이징을 위해 컨버터를 이용하는 것은 뒤에서 설명) lMarshal := TJSONMarshal.Create (TJSONConverter.Create); // Marshal 메소드를 사용하면 컨버터에 지정된 대로 컨버전 된다. MemoResultado.Lines.Text := lMarshal.Marshal(TesteJson).ToString(); lMarshal.Free; end; JSON 커스터마이징 방식 1: JSONMarshalled 어트리뷰트 사용 [JSONMarshalled(False)] // 이 어트리뷰트에서 False로 지정된 것, 즉 FScript는 생성될 JSON의 필드에 들어가지 않는다. FScript: string; JSON 커스터마이징 방식 2: RegisterConverter 프로시저를 오버로딩하기 프로시저 오버로딩을 통해 파서인 TJSONMarshal 클래스의 기본 전환 방식을 덮어쓰는 방법 JSON의 특정 필드의 데이터 타입 바꾸기 등 얼마든지 처리 로직을 바꿀 수 있다. (굳이 RTTI를 건드리지 않아도 된다) 좀 복잡하긴 하지만, 필요하다면 이처럼 TJSONMarshal 클래스를 생성할 때 컨버터를 파라미터로 넣는 방식을 쓸 수 있다. << DelphiCon 2021 목록으로 이동
  2. << DelphiCon 2021 목록으로 이동 원본 비디오(YouTube) 보기 (30 min) DelphiCon 의 2021 시리즈 중, Leaving Delphi 7 - A Success Migration Case - Dion Carlos Mai & Rafael Pereira (30 min) 의 한글 요약본입니다. 20년이 넘었고, 델파이 7로 운영하던 대형 시스템을 델파이 10으로 마이그레이션한 프로젝트를 정리한 내용을 공유합니다. 발표자 Rafael은 이 소매점 시스템 제공자인 Stone Co의 개발자입니다. 목차 회사 및 프로젝트 소개 현대화 마이그레이션을 계획하게 된 동기와 결과 마이그레이션 프로젝트의 개요 마이그레이션 프로젝트의 진행 단계 1단계 (분석 단계) 2단계 (실행 단계) 3단계 (검증 단계) 4단계 (배포 단계) 프로젝트를 통해 배운 점 참고 자료 그림 자료 회사 및 프로젝트 소개 회사 소개: Stone Co Linx: 뉴욕 증시에 상장된 브라질의 소프트웨어 기업. 브라질의 소매점 소프트웨어 시장에서 점유율 45.6% Stone: 뉴욕 증시에 상장된 브라질의 Stone Co.의 핀테크 기업. 지불 관리 도구와 유연한 금융 상품을 제공 Stone Co: 2012년에 시작하여 브라질 전역을 커버하고 있다. 2020년 11월, Stone Co는 Linx를 인수 대상 시스템 소개: Linx Big 20년이 넘은 소매점 소프트웨어. 약국에서 주로 사용 현재 3,000개가 넘는 고객사와, 5,000개가 넘는 소매점에서 사용 중 (2개 이상을 사용하는 점포가 많으므로, 배포된 소프트웨어는 훨씬 더 많음) 현대화 마이그레이션을 계획하게 된 동기와 결과 윈도우10은 2021년 10월 현재 전세계 데스크탑 점유율의 80%이상을 차지하고 있고 계속 증가하는 추세임 (관련 통계 보기) 윈도우 10에 집중해야 하는 상황 윈도우 10의 새 운영체제 기능을 충분히 지원하는 IDE 기능이 필요 윈도우 10에 맞는 더 좋은 시각적인 UI가 필요 (그림 참조: 예전 화면과 새 화면 비교) 스타일, 대시보드, 그래프, 메뉴 등 더 풍부한 화면을 제공하여 외관상 완전히 다른 새 소프트웨어로 보임 (그림 참조: 새 화면) 델파이 10에 내장된 컴포넌트와 스타일만 사용하여 화면 개발 새 UI를 구현하기 위한 코드는 새로 작성 하지만, 모든 클래스와 소스 코드는 예전 델파이 7과 거의 같음 스레드가 필요 요즘 소프트웨어는 API 통합이 많다. 델파이 7에서도 스레드를 구현할 수 있다. 하지만, 델파이 7은 소프트웨어를 개발하기에 여전히 매우 훌륭한 도구이지만, 20년 전에 나온 오래된 버전이다. 델파이 최신 버전을 사용하지 않으면, API 통합을 모두 지원하거나 JSON 직렬화하기 등 새 기술을 반영하기가 어렵다. 기타 개선 델파이 10의 장점을 가질 수 있게 됨: 64비트 지원, 윈도우 10 지원, FireDAC으로 데이터 연결, 다른 운영체제에서 접근 등등 델파이 10 시애틀을 선택한 이유 당시 우리 개발자 대부분이 사용할 수 있는 라이선스를 이미 가지고 있었기 때문 마이그레이션 프로젝트의 개요 목표 델파이 7에서 델파이 10 시애틀로 이전 마리아DB 10.4.13과 MySQL 8.0.21 지원 개요 기간: 총 7 개월 투입 인원 외부: 3명 (아쿠아소프트 개발자, 마이그레이션에만 집중) 내부: 22명 (개발자, QA, 델파이 프로젝트 별 책임자) 마이그레이션 대상 (소매점 관리 시스템)의 규모 델파이 프로젝트: 65개 모듈: 194개 .PAS 파일: 6,000개 .DFM 파일: 2,000개 코드 줄 수: 2백만줄 써드 파티 컴포넌트 세트: 23가지 마이그레이션 프로젝트의 진행 단계 총 4단계로 나누고, 앞 단계 완료 시 다음 단계를 진행: 분석, 실행, 검증, 배포 1단계 (분석 단계) 개요 기간: 3.5개월 투입 인원 외부: 3명 (아쿠아소프트 개발자) 내부: 2명 (개발자 1명, QA 1명) 목표 측정 / 식별 / 문서화 델파이 10으로 마이그레이션이 끼칠 영향 새 데이터베이스와의 호환성 프로젝트로 인해 달라지는 점과 문제점 변경으로 인해 발생될 것으로 예상되는 문제 해소 자동 변환 도구 개발 작업 내용 외부 인력 (아쿠아소프트) 합류 전환할 컴포넌트를 3가지로 나누어 분석하고 대비 버전의 수명이 다한 컴포넌트 마이그레이션 시 문제가 많고, 작업도 많이 해야 하는 것들임 이 프로젝트에서 많이 사용될 컴포넌트 프로젝트에서 사용되는 비중이 크기 때문에, 그 만큼 프로젝트에 끼치는 영향도 큰 것들임 소스코드가 없는 컴포넌트 매우 이상하게 작동할 수 있고 마이그레이션 작업을 까다롭게 만들기 때문에 주의해야 할 것들임 예: Zeos (2003년부터 사용한 오픈 소스 데이터베이스 컴포넌트) [에러가 발생되는 코드] QPrincipal.FieldByName('datapre').AsDateTime :=now; 짐작하겠지만, 상업용 시스템에서 매우 많이 사용되는 코드이며, 모두 수정해야 하므로, 에러 해소 작업이 많다. 델파이 버전 간의 차이와 충돌을 식별하고 해소 유니코드 (영향 받는 dll이 있는 지까지도 파악할 필요가 있음) 자동 변환 도구 개발 ("마이그레이션 찾아 바꾸기"라고 이름 지음) 소스 변환 자동화 도구 개발을 이 분석 단계 안에서 완성 (그림 참조: 소스 변환 자동화 도구와 변환 결과) Zeos에서 UniDAC으로 마이그레이션하는 도구 마법사가 발생시키는 버그 수정 자동화 사용되는 모든 컴포넌트 마이그레이션 자동화 (많은 컴포넌트에서 일부 프로퍼티가 변경되었기 때문) 델파이 10과 델파이 7과의 차이로 인해 필요한 코드 변환 자동화 "모든" 소스를 변환할 수 있는 "맞춤" 도구 완성 소스 코드 동기화 소스 코드 동기화는 매우 중요! 마이그레이션 프로젝트 진행 중에도 전체 배포까지 완료되기 전까지는 전환해야 할 코드가 계속 변하기 때문이다. 분석 단계가 진행되는 중에도 내부 개발자 20명은 여전히 기존 코드나 화면을 변경하고 새 기능을 추가한다. 즉, 마이그레이션 대상인 델파이 7 소스 코드가 계속 변한다. 2단계 (실행 단계) 모든 코드를 변경하는 단계 개요 기간: 2일 (사전 준비가 완료되었으므로 가능) 참여 인원 외부: 1명 (아쿠아소프트 개발자) 내부: 1명 (개발자) 목표 코드 "전체"를 자동 변환 도구를 사용하여 델파이 7에서 델파이 10으로 변경 작업 내용 진행 전 실행 단계 직전에 기존 프로그램 변경을 금지 1일차 자동 변환 도구를 사용하여 변환 개발자 2명이 6시간 만에 완료 총 4,678개 소스 파일을 변경 2일차 컴포넌트 적합성, 데이터베이스 적합성이 파악된다. 모든 변경이 계획대로 되었는 지를 검증 3단계 (검증 단계) 개요 기간: 2.5 개월 참여 인원 외부: 3명 (아쿠아소프트 개발자) 내부: 22명 (개발자, QA, 델파이 프로젝트 별 책임자) 목표 모든 델파이 프로젝트와 모든 작동을 검증, 버그 파악 및 해소 팀 구성 개발자 (버그 픽스 전담) 그룹 개발자 (일반) 그룹 QA 그룹 델파이 프로젝트 별 책임자 그룹 작업 내용 검증 단계 시작 전 (즉, 실행 단계인 2일 간) 델파이 10 사용자 교육 이유: 모든 개발자는 이 검증 단계 시작을 기준으로, 그 이후에는 델파이 10만 사용 (그 이전에는 델파이 7만 사용) 내용: 델파이 10 사용법, 새 기능, 검증 단계 및 이후 유지 보수 시 작업 절차 결과: 개발자 등 모든 참여 인원이 검증 단계에 참여할 준비를 완료 검증 단계 시작 직후 버그가 매우 많이 보고되었고, 버그의 대부분을 신속하게 해소했다. 자동 변환 도구를 이용했기 때문이다. 버그 발생: 변환 도구에 버그가 있으면, 변환된 시스템에도 버그가 생긴다. 버그 해소: 변환 도구의 버그를 수정하면, 변환된 시스템의 해당 버그도 해소된다. 앞 단계에서 접근할 수 없었던 것들에서 발생되는 버그를 QA들이 찾아냈는데, 이런 검증은 도움이 되었다. 검증 결과 모든 기능 중 97% 검증 완료 나머지 3%는 외부 API라서 내부 테스트 불가 이유: 실제 운영 환경에서 테스트할 수 밖에 없는 외부 데이터) 대책: 외부 API 목록 작성과 운영 환경에서 완벽한 테스트를 할 수 있도록 미리 준비 (매우 중요) 이 단계에서는 버그 해소 작업에 큰 수고가 들지 않았다. 버그 등록이 지속적으로 줄어듬 종료 시점에 모두 해소 4단계 (배포 단계) RC (Release Candidate, 출시 후보) 버전 배포, 합격 판정 후 전체 배포 개요 기간: 5주 참여인원 내부: 2명 (QA 1명, 개발자 1명) 고객: 선정된 고객사 104곳 (사용 점포 브라질 18개 주에 있는 소매점에 총 184개 배포) 브라질 소매점 특성 상, "모든 지역"에서 문제가 없는 지 검증해야 함 (브라질은 각 주마다 세금과 규제가 다름) 작업 내용 4-1 단계: RC(Release Candidate, 출시 후보) 버전 배포 준비 RC (Release Candidate, 출시 후보) 버전 합격 기준 설정 중요도 0 (우회 해소 방법이 없는) 버그: 최대 5개 이내 중요도 1 (우회 해소 방법이 1가지인) 버그: 최대 20개 이내 위 기준 중 하나라도 통과하지 못하면 이 RC 버전은 불합격으로 판정하고 즉시 배포를 중단하기로 함 RC 버전 최대한 완벽한 버전을 준비 RC 버전을 배포 고객 "모든" 기능이 검증될 수 있도록 "세심하게" 고객을 선정 (고객마다 사용하는 기능, 방식, 환경이 제각각일 수 있다) 4-2 단계: RC 버전 배포 및 판정 배포 후 보고된 버그의 갯수 (모두 14개, 그림 참조: 버그 발견 및 해소 추이) 중요도 0: 1개 (기준치인 5개 이내, 통과) 중요도 1: 4개 (기준치인 20개 이내, 통과) 중요도 2: 7개 중요도 3: 2개 기준에 의거 성공 판정 4-3 단계: 전체 배포 100% 델파이 10으로 전환 완료 해소해야 할 마이그레이션 관련 버그가 없음 처음 계획한 데이터베이스를 지원 확인 현재, 99% 고객이 업데이트된 시스템을 사용 프로젝트를 통해 배운 점 분석 단계 (자동 변환 도구 개발 포함)에서 QA는 한두명만 참여하면 된다. 이 단계에서 QA가 많고, 버그를 많이 찾아낸다고 해서 무조건 좋은 것은 아니다. QA는 자동 변환 도구의 결과를 테스트 주의! 이 단계에 QA를 많이 투입할 것인지 아닌 지에 대한 결정은 프로젝트에 따라 다를 수 있으므로 매우 조심해야 한다. 써드-파티 컴포넌트는 분석이 매우 잘 되어야 한다. 심지어 프로젝트 전부터 미리 충분히 파악해야 한다. 마이그레이션 프로젝트가 시작되면 써드-파티 컴포넌트에 대한 작업과 부담이 많기 때문이다. 마이그레이션을 자동화하는 "맞춤" 변환 도구를 개발한다. 프로젝트가 많고 규모가 커도 처리할 수 있다. 검증 단계는 매우 중요하다. 충분한 테스트 시간이 필요하다. 검증 단계를 거치지 않고 바로 배포로 넘어가면 운영 시 문제가 많을 수 있다. 그러면 경영진은 이 프로젝트가 실패했다고 판정할 수 있다. 그러면, 검증 단계 하나의 실패로 끝나지 않는다. (우리는 검증 단계에 충분한 시간을 계획하였기 때문에 문제가 없었다) 프로젝트 중 소통이 매우 중요하다. 모든 팀원들이 진행 중인 작업을 항상 업데이트할 수 있는 절차와 환경이 구축되어야 한다. 그래야, 서로 다른 사람의 작업에 어떤 영향을 주거나 받는 지를 미리 파악할 수 있다. 프로젝트 평가 기준을 구체적이고 명확히 정하고 모니터링해야 한다. 계속 앞으로 갈 것인지 아니면 중단해야 하는 지에 대한 기준을 정하는 것은 매우 중요하다. 마이그레이션 목적이 분명해야 한다. 프로젝트 진행 중에 목적이 변경되면, 3-4개월 이상 지체될 수 있다. 목적과 목표가 분명해야 계획된 일정을 준수할 수 있다. 델파이 7은 매우 훌륭한 도구이지만, 거기에 머물고 있을 수 만은 없다. 머물러 있고 싶어도, 우리가 앞으로 가야할 시간은 언젠가 오게되어 있다. 20년 전에 출시된 델파이 7로는 지금 시점에서 필요한 기술 모두를 지원하지 못한다. 시스템 운영에 크리티컬할 기술에서도 그렇다. 구버전에서 벗어나서 최신 기술에 맞춰가자. 참고 자료 What's New in Delphi 10 Seattle https://www.embarcadero.com/products/delphi/whats-new Embarcadero Update Center https://www.embarcadero.com/rad-in-action/migration-upgrade-center Desktop Windows Version Market Share Worldwide http://gs.statcounter.com/windows-version-market-share/desktop/worldwide/#monthly-201509-202110 Delphi Unicode: https://www.embarcadero.com/images/dm/technical-papers/delphi-unicode-migration.pdf 그림 자료 마이그레이션 전과 후의 UI 비교: 로그인 화면 새 UI에서 사용자 테마 선택 지원: 설정 화면 새 UI에서 사용자 테마 선택 지원: 다크 테마 적용 시 화면 마이그레이션 전과 후의 UI 비교: 일반 화면 새 UI에서 대시보드 구현 새 UI에서 실시간 그래프 구현1 새 UI에서 실시간 그래프 구현2 "맞춤" 자동 변환 도구 예시: 써드 파티 컴포넌트의 경우 프로퍼티 변환이 필요함 3단계 (검증 단계)의 버그 발견 및 해소 추이 << DelphiCon 2021 목록으로 이동
  3. << DelphiCon으로 이동 엠바카데로 DelphiCon의 2021 시리즈 를 소개합니다. DelphiCon은 공식 "엠바카데로 델파이" 온라인 세미나입니다: 모든 세션 다시 보기 (아직 다시보기가 제공되지 않는 세션도 일부 있습니다.) (한글 요약이 필요하면 댓글로 요청하세요. 한글 요약본을 제공하겠습니다. 이 경우, 아래 목록에 제목이 한글로 표시됩니다) [2021. 11. 18] 델파이에서 JSON을 다룰 때 당신이 몰랐던 것들 - Dion Carlos Mai & Gustavo Mena Barreto (25 min) --APILayer: Features and Connecting with REST - Victory Fernandes ( min) --Building a Web Crawler with Delphi and Python - Maico Dal Ri & Dion Carlos Mai ( min) --Delphi Best Practices: The Top Seven Things you Should be Doing - Nick Hodges ( min) --Fintech on the Ethereum Blockchain with Delphi - Jim McKeeth & Stefan van As ( min) --FireDAC: Combining Power and Speed in Cross Platform Database Access (Live Panel) - Cary 𝔍ensen, Jens Fudge & Olaf Monien ( min) --Invoice Generation via Telegram Bot Using FastReport VCL and Delphi - Alexander Syrykh ( min) --Using C++Builder to Extend the Reach of Delphi - David Millington ( min) [2021. 11. 17] 델파이 7에서 벗어나기 - 마이그레이션 프로젝트 사례 - Dion Carlos Mai & Rafael Pereira (30 min) Control Arduino Manipulator with Delphi and Visuino over WiFi or Bluetooth - Boian Mitov ( min) Multi-Platform Explorations using Delphi, FMX, Feeds, REST and More - David Intersimone ( min) Smartwatch Android Meets Delphi - Controlling Devices - Sileide Campos & Victory Fernandes ( min) Delphi Does Low-Code: Cross-Platform REST Client in < 30 Minutes! - David Cornelius ( min) Castle Game Engine - Coming to Delphi! - Michalis Kamburelis ( min) Developing Applications for the Raspberry Pi with Delphi 11 - Dr. Holger Flick ( min) --Multi-Threaded Programming on Apple's Mac M1 vs Mac Core i7 vs Windows Core i7 - Olaf Monien ( min) [2021. 11. 16] Maximise Your Delphi Productivity - Alister Christie ( min) Why Does the Cloud Matter for a Delphi Developer? - Dion Carlos Mai ( min) Move Your UI to the 23rd Century - Building a Data Dashboard with Delphi and Skia. Engage! - Ian Barker ( min) --Thriller: A Delphi Web App in 5 Work Days - Bruno Fierens ( min) --Creating Custom Title Bars in VCL Apps - Ray Konopka ( min) 탭(Tab) 콘트롤은 데스크탑 UX를 어떻게 망가뜨리는가? - Ray Konopka (46 min) (이 세션은 UX Summit 2021에서도 진행되었으므로, 그 링크로 연결됨) 탭 콘트롤 관련 16가지 예를 매우 짧고 명확히 설명하고 탭 콘트롤 디자인 가이드라인을 제시한다. Entity Component Systems: A Different Approach to Coding - Erik van Bilsen ( min) --Convert your VCL Database Application to Mobile and Multiplatform - Miguel Angel Moreno ( min) Keynote - Beyond 10x - The Future of Development with Delphi - Marco Cantù ( min) Create Windows Installers in RAD Studio 11 Alexandria [On-Demand] - Tomas Slapka ( min) Tackling Localization with TsiLang Components Suite [On-Demand] - Gleb Sitikov ( min) Using Delphi as a No Code Development IDE [On-Demand] - Patrick Prémartin ( min) Using FireMonkey as a Game Engine [On-Demand] - Patrick Prémartin ( min) Designing Applications with Windows 11 [On-Demand] - Ziad Allaghi ( min) The SOLID Principles in Delphi - Write Better Code! [On-Demand] - Marco Geuze ( min) 델파이 또는 C++빌더에서 메모리 누수 탐지하기 [On-Demand] - Artem Razin (11 min, 한국어 더빙) (이 세션은 UX Summit 2021에서도 진행되었으므로, 그 링크로 연결됨) Beautifying IntraWeb apps with Bootstrap [On-Demand] - Jackson Gomes ( min) Delphi Modernization Made Easy! [On-Demand] - Oren Aviram ( min) << DelphiCon으로 이동 View full 엠바카데로 기술자료
  4. << DelphiCon으로 이동 엠바카데로 DelphiCon의 2021 시리즈 를 소개합니다. DelphiCon은 공식 "엠바카데로 델파이" 온라인 세미나입니다: 모든 세션 다시 보기 (아직 다시보기가 제공되지 않는 세션도 일부 있습니다.) (한글 요약이 필요하면 댓글로 요청하세요. 한글 요약본을 제공하겠습니다. 이 경우, 아래 목록에 제목이 한글로 표시됩니다) [2021. 11. 18] 델파이에서 JSON을 다룰 때 당신이 몰랐던 것들 - Dion Carlos Mai & Gustavo Mena Barreto (25 min) --APILayer: Features and Connecting with REST - Victory Fernandes ( min) --Building a Web Crawler with Delphi and Python - Maico Dal Ri & Dion Carlos Mai ( min) --Delphi Best Practices: The Top Seven Things you Should be Doing - Nick Hodges ( min) --Fintech on the Ethereum Blockchain with Delphi - Jim McKeeth & Stefan van As ( min) --FireDAC: Combining Power and Speed in Cross Platform Database Access (Live Panel) - Cary 𝔍ensen, Jens Fudge & Olaf Monien ( min) --Invoice Generation via Telegram Bot Using FastReport VCL and Delphi - Alexander Syrykh ( min) --Using C++Builder to Extend the Reach of Delphi - David Millington ( min) [2021. 11. 17] 델파이 7에서 벗어나기 - 마이그레이션 프로젝트 사례 - Dion Carlos Mai & Rafael Pereira (30 min) Control Arduino Manipulator with Delphi and Visuino over WiFi or Bluetooth - Boian Mitov ( min) Multi-Platform Explorations using Delphi, FMX, Feeds, REST and More - David Intersimone ( min) Smartwatch Android Meets Delphi - Controlling Devices - Sileide Campos & Victory Fernandes ( min) Delphi Does Low-Code: Cross-Platform REST Client in < 30 Minutes! - David Cornelius ( min) Castle Game Engine - Coming to Delphi! - Michalis Kamburelis ( min) Developing Applications for the Raspberry Pi with Delphi 11 - Dr. Holger Flick ( min) --Multi-Threaded Programming on Apple's Mac M1 vs Mac Core i7 vs Windows Core i7 - Olaf Monien ( min) [2021. 11. 16] Maximise Your Delphi Productivity - Alister Christie ( min) Why Does the Cloud Matter for a Delphi Developer? - Dion Carlos Mai ( min) Move Your UI to the 23rd Century - Building a Data Dashboard with Delphi and Skia. Engage! - Ian Barker ( min) --Thriller: A Delphi Web App in 5 Work Days - Bruno Fierens ( min) --Creating Custom Title Bars in VCL Apps - Ray Konopka ( min) 탭(Tab) 콘트롤은 데스크탑 UX를 어떻게 망가뜨리는가? - Ray Konopka (46 min) (이 세션은 UX Summit 2021에서도 진행되었으므로, 그 링크로 연결됨) 탭 콘트롤 관련 16가지 예를 매우 짧고 명확히 설명하고 탭 콘트롤 디자인 가이드라인을 제시한다. Entity Component Systems: A Different Approach to Coding - Erik van Bilsen ( min) --Convert your VCL Database Application to Mobile and Multiplatform - Miguel Angel Moreno ( min) Keynote - Beyond 10x - The Future of Development with Delphi - Marco Cantù ( min) Create Windows Installers in RAD Studio 11 Alexandria [On-Demand] - Tomas Slapka ( min) Tackling Localization with TsiLang Components Suite [On-Demand] - Gleb Sitikov ( min) Using Delphi as a No Code Development IDE [On-Demand] - Patrick Prémartin ( min) Using FireMonkey as a Game Engine [On-Demand] - Patrick Prémartin ( min) Designing Applications with Windows 11 [On-Demand] - Ziad Allaghi ( min) The SOLID Principles in Delphi - Write Better Code! [On-Demand] - Marco Geuze ( min) 델파이 또는 C++빌더에서 메모리 누수 탐지하기 [On-Demand] - Artem Razin (11 min, 한국어 더빙) (이 세션은 UX Summit 2021에서도 진행되었으므로, 그 링크로 연결됨) Beautifying IntraWeb apps with Bootstrap [On-Demand] - Jackson Gomes ( min) Delphi Modernization Made Easy! [On-Demand] - Oren Aviram ( min) << DelphiCon으로 이동
  5. << DelphiCon 2021 목록으로 이동 원본 비디오(YouTube) 보기 (30 min) DelphiCon 의 2021 시리즈 중, Things That You Don’t Know About JSON in Delphi - Dion Carlos Mai & Gustavo Mena Barreto (25 min) 의 한글 요약본입니다. 델파이에서 JSON을 다루는 방법, JSON 파서를 가장 생산적으로 사용하는 방법, JSON 직렬화 작동을 커스터마이징하고 자동화하는 옵션을 설명합니다. 발표자 Gustavo Mena Barreto는 Aquasoft 선임 개발자이며, Dion Carlos Mai은 Aquasoft 개발팀장입니다. 이 세션에서 사용된 전체 소스코드: https://github.com/gustavomenabarreto/delphicon2021 목차 델파이에서 JSON 다루기를 주제로 정한 이유 JSON을 다룰 때 델파이에서 선택할 수 있는 것들 이 세션에서 사용할 JSON 샘플 JSON을 사용하기 위해 필요한 델파이 유닛(들) 1. 가장 간단한 방법 JSON 읽기 (아래 코드와 주석 참조) JSON 쓰기 (아래 코드와 주석 참조) 2. JsonToDelphi JSON 직렬화 행위를 변경하는 옵션(들) 3. Super Object (오픈소스) 4. RESTResponseDataSetAdapter 컴포넌트 REST API를 통해서 JSON 데이터를 받아서 델파이 폼에 표현하기 5. TJSONMarshal 클래스 델파이에서 JSON 다루기를 주제로 정한 이유 요즘 개발자에게 JSON은 매우 중요한 자리를 차지하고 있다. 요즘은 API 통합이 너무나 많다. 심지어 NoSQL JSON 데이터베이스도 있다. JSON을 다루는 방법이 여러가지 라는 사실을 잘 모르는 개발자들이 있다. 이 세션에서는 JSON을 다루는 여러 가지 방법을 설명한다. JSON을 다룰 때 델파이에서 선택할 수 있는 것들 가장 간단한 방법 JsonToDelphi: JSON 오브젝트를 가지고 델파이 클래스를 자동 생성하는 웹사이트 Super Object (오픈소스): 델파이 6, 7 등 구버전에서 JSON을 다룰 수 있음 (최신 버전 사용자에게는 권장하지 않음) RESTResponseDataSetAdapter 컴포넌트: JSON을 기반으로 데이터셋을 자동 생성 TJSONMarshal 클래스: JSON 직렬화 처리를 커스터마이징할 때 사용 이 세션에서 사용할 JSON 샘플 그림. 이 세션의 코드는 비트코인 블록체인에서 사용되는 매우 구조가 큰 JSON에서 극히 일부분만 뽑아 놓은 샘플 JSON을 사용한다. JSON을 사용하기 위해 필요한 델파이 유닛(들) uses System.JSON, REST.Json; 1. 가장 간단한 방법 TJSONObject와 TJSONArray를 사용하는 방법 JSON 읽기와 쓰기 (아래 코드와 주석 참조) // [JSON 읽기] var objeto : TJSONObject; ObjetoArray: TJSONArray; I: Integer; begin // ParseJSONValue를 이용하면, 단 한줄로 JSONObject를 만들 수 있다. objeto := TJsonObject.ParseJSONValue(MemoJson.Lines.Text) as TJSONObject; // JSONObject의 Get과 GetValue 메소드를 이용하면, JSON 개체의 모든 데이터에 접근할 수 있다. ShowMessage(objeto.Get('block_hash').ToString); ShowMessage(objeto.GetValue('block_hash').Value); // 배열 형식의 JSON값은 TJSONArray로 받을 수 있다. ObjetoArray := objeto.Get('inputs').JsonValue as TJSONArray; // JSONArray 안에 있는 각 요소는 for 루프를 통해 접근할 수 있다. for I := 0 to ObjetoArray.Size - 1 do begin MemoResultado.Lines.Add( ObjetoArray.Get(i).ToString + ': ' + ObjetoArray.Get(i).Value); end; end; // [JSON 쓰기] var objeto : TJSONObject; ObjetoArray: TJSONArray; ObjetoInputs: TJSONObject; begin objeto := TJSONObject.Create; ObjetoArray := TJSONArray.Create; // JSONObject의 AddPair (필드 이름, 필드 값) 메소드를 사용하여 데이터 쓰기 // 필드 값에 문자열 넣기 objeto.AddPair('block_hash','123456789'); // 필드 값에 JSONObject의 일종인 JSONArray 넣기 Objeto.AddPair('Inputs',ObjetoArray); // 'Input' 필드 값인 ObjetoArray에 들어 갈 JSONObject 만들기 ObjetoInputs := TJSONObject.Create; // 위와 동일하게 AddPair를 사용하여 JSONObject에 데이터 쓰기 ObjetoInputs.AddPair('prev_hash','987654321'); ObjetoInputs.AddPair('script','1'); // JSON 배열인 ObjetoArray의 Add 메소드를 이용하여, 배열 안에 요소 넣기 ObjetoArray.Add(ObjetoInputs); MemoResultado.Lines.Text := objeto.ToString; end; 2. JsonToDelphi https://jsontodelphi.com는 JSON 샘플을 넣으면, 해당 JSON을 다룰 수 있는 델파이 클래스를 자동으로 받을 수 있는 웹사이트이다. SOAP / XML 파서와 사용 방식이 상당히 유사하지만, XML이 아니라 JSON을 다룬다. 우리가 직접 해당 클래스를 만들기 위해 수작업으로 코드를 작성할 수도 있지만, 많은 API들이 방대한 JSON을 사용하고 있는데, 그 JSON을 모두 클래스로 만들 기 위해 수작업으로 코드를 쓰려면, 시간도 많이 걸리고 너무 수고스럽다. 무료 서비스인 이 웹사이트를 이용하면 그 모든 수고를 덜 수 있다. 생성된 클래스의 전체 소스 코드를 다운로드 할 수 있으므로, 필요한 부분을 직접 변경할 수도 있다. // [JsonToDelphi에서 만들어 준 클래스 사용하여 읽기] // 제공한 JSON 샘플의 구조가 모두 반영된 클래스가 생성되므로, 그냥 사용하면 된다. var TesteJson: TRoot; Inputs: TInputs; begin // JsonToDelphi가 만들어준 클래스 안에 JSON을 읽어서 넣기 TesteJson := TJson.JsonToObject<TRoot>(MemoJson.Lines.Text); // 데이터 타입까지 이미 알맞게 지정된 해당 클래스의 프로퍼티를 통해 JSON 필드를 접근 ShowMessage('BlockHash - '+TesteJson.BlockHash); for Inputs in TesteJson.Inputs do begin Showmessage('ScripType - '+Inputs.ScriptType); end; end; // [JsonToDelphi에서 만들어 준 클래스 사용하여 쓰기] var TesteJson: TRoot; Inputs: TInputs; I: Integer; begin // JsonToDelphi에서 만들어 준 클래스를 생성하고 TesteJson := TRoot.Create; // 프로퍼티를 이용하여 값을 쓴다. TesteJson.BlockHash := 'www.aquasoft.com.br'; // 마찬가지 방법으로 배열을 돌면서 값을 쓸 수도 있다. for I := 0 to 2 do begin Inputs := TInputs.Create; Inputs.Age := 20; Inputs.Script := 'teste script'; TesteJson.Inputs.Add(Inputs); end; // 단 한줄로 JSON 데이터를 문자로 풀어낼 수 있다. MemoResultado.Lines.Text := //이 때 joIgnoreEmptyStrings 옵션을 사용하면, 값이 빈 문자열인 데이터는, JSON 필드를 만들 지 않고 건너뛴다. // joIgnoreEmptyStrings는 델파이의 TJsonOption의 일종이다 (아래 본문의 설명 참조) TJson.ObjectToJsonString(TesteJson,[joIgnoreEmptyStrings]); end; JSON 직렬화 행위를 변경하는 옵션(들) 델파이의 REST.Json.TJsonOption https://docwiki.embarcadero.com/Libraries/Sydney/en/REST.Json.TJsonOption JSON 생성 작동 방식 중 원하는 옵션을 선택할 수 있다. 예: 유닉스 날짜 포맷 사용하기, UTC 날짜 사용, 들여쓰기, 카멜 표기, 등등 델파이 버전이 최신일수록 선택할 수 있는 직렬화 옵션이 더 많다. 이전 버전이라서 필요한 옵션이 없다면, 원하는 작동을 하는 코드를 수작업으로 써 넣는다. [그림. 델파이 11.0 알렉산드리아의 TJsonOption (참고: 이전 버전에는 옵션이 이것처럼 많지 않다)] 3. Super Object (오픈소스) 델파이 6, 7 등 구버전에서 JSON을 다룰 수 있음 무료 오픈 소스: https://github.com/hgourvest/superobject 델파이가 오래된 버전이지만, API 연동을 위해 JSON을 다루어야 한다면 이런 역할을 하는 DLL 또는 패키지의 도움을 받을 수 있다. 하지만, 직접 코드로 구현하려면 Super Object를 사용할 수 있다. 주의! 앞에서 설명했듯이, 최신 버전에는 이미 이런 기능들이 들어있으므로, Super Object를 굳이 사용할 이유가 없음 // [SuperObject를 사용하여 읽기] var Objeto: ISuperObject; Inputs: ISuperObject; begin // SuperObject의 SO 메소드를 사용하여 오브젝트를 생성 Objeto := SO(MemoJson.Lines.Text); // S 메소드는 원하는 필드와 값을 마치 배열처럼 접근 ShowMessage(Objeto.S['block_hash']); ShowMessage(Objeto['block_hash'].AsString); // for 루프 사용 가능 (for in 루프는 델파이 7 같은 구버전에는 없지만, for 문으로 유사하게 구현하면 된다.) for Inputs in objeto['inputs'] do begin MemoResultado.Lines.Add(Inputs.AsString); end; end; // [델파이 구버전에서 SuperObject를 사용하여 쓰기] var Objeto: ISuperObject; Inputs: ISuperObject; begin // 오브젝트 생성 Objeto := SO; // 데이터 쓰기 역시 마치 배열을 다루듯이 사용 Objeto.S['block_hash'] := '123456789'; Objeto.O['inputs'] := SA([]); Inputs := SO; Inputs.S['script'] := 'Teste script 1'; Inputs.I['age'] := 30; // JSON 배열을 쓸 수도 있다. Objeto.A['inputs'].O[0] := Inputs; Inputs := SO; Inputs.S['script'] := 'Teste script 2'; Inputs.I['age'] := 60; Objeto.A['inputs'].O[1] := Inputs; // AsString 메소드를 사용하여 JSON으로 쓸 수도 있다. MemoResultado.Lines.Text := Objeto.AsString; end; 4. RESTResponseDataSetAdapter 컴포넌트 RESTResponse에 받은 JSON 데이터를 FDMemTable 등의 데이터셋에 넣어주는 컴포넌트 (델파이 최근 버전에서 추가됨) [그림. REST 디버거를 사용하면 더 쉽게 만들 수 있다] REST API를 통해서 JSON 데이터를 받아서 델파이 폼에 표현하기 REST 디버거 (REST API의 읽기와 쓰기를 테스트하는 무료 도구)를 실행한다. 테스트가 성공했다면, [Copy Components] 버튼을 클릭한다. 그러면, 해당 REST API를 다루도록 설정이 완료된 델파이 컴포넌트가 클립보드에 복사된다. RESTClient: REST 서비스 제공자에게 요청을하고 응답을 받는 사용자 오브젝트 RESTRequest: RESTClient가 REST 서비스 제공자에게 전달하는 요청 RESTResponse: RESTClient가 REST 서비스 제공자로부터 전달받은 응답 RESTResponseDataSetAdapter: 전달받은 응답을 FDMemTable 등의 데이터셋에 넣어주는 매개체 FDMemTable: 데이터셋 델파이 폼에서 붙여넣기를 하면, 복사된 컴포넌트가 모두 추가되고, 되델파이 프로젝트에서 바로 사용할 수 있다. 그 후에는, 일반 데이터와 마찬가지로 FDMemTable을 사용하면 된다. 함께 복사된 REST 관련 컴포넌트들이 자동으로 이 데이터셋에 JSON 데이터를 담는다. FDMemTable 이 가장 좋고 편한 데이터셋이기 때문에 사용한다 (원한다면 다른 데이터셋을 사용해도 된다) 예를 들어, 데이터가 표현될 그리드의 데이터소스 즉 DataSource 컴포넌트의 DataSet 프로퍼티에 FDMemTable를 지정하면 된다. 직접 작성해야할 코드는 2줄 뿐이다 (아래 코드 참조) procedure TForm1.Button5Click(Sender: TObject); begin // REST 요청을 한다 RESTRequest1.Execute; // RESTResponse에 받은 JSON 데이터를 FDMemTable 등의 데이터셋에 넣기 RESTResponseDataSetAdapter1.Active := True; end; 5. TJSONMarshal 클래스 JSON 데이터 직렬화 알고리즘을 커스터마이징 할 수 있다. RTTI를 직접 건드릴 필요가 없기 때문에 보다 안전하고 편하다. RTTI를 잘못 다루면 쓰레드 문제가 발생할 수 있다. JSON을 다룰 때는 쓰레드를 사용하고 JSON 구조를 알맞게 맞추어 쓰는 것이 좋다. 보다 자세한 내용은 사용자 오브젝트 직렬화에 대한 상세한 Docwiki 도움말을 참고하기 바란다. // [TJSONMarshal을 사용하여 맞춤 JSON 구현하기] var lMarshal : TJSONMarshal; TesteJson: TRoot; //JsonToDelphi에서 제공하는 SuperObject 타입을 사용 Inputs: TInputs; I: Integer; begin TesteJson := TRoot.Create; // 오브젝트 생성 (SuperObject) TesteJson.BlockHash := 'www.aquasoft.com.br'; for I := 0 to 2 do begin Inputs := TInputs.Create; Inputs.Age := 20; Inputs.Script := 'teste script'; TesteJson.Inputs.Add(Inputs); end; // JSONMarshal을 생성할 때 컨버터를 지정 (커스터마이징을 위해 컨버터를 이용하는 것은 뒤에서 설명) lMarshal := TJSONMarshal.Create (TJSONConverter.Create); // Marshal 메소드를 사용하면 컨버터에 지정된 대로 컨버전 된다. MemoResultado.Lines.Text := lMarshal.Marshal(TesteJson).ToString(); lMarshal.Free; end; JSON 커스터마이징 방식 1: JSONMarshalled 어트리뷰트 사용 [JSONMarshalled(False)] // 이 어트리뷰트에서 False로 지정된 것, 즉 FScript는 생성될 JSON의 필드에 들어가지 않는다. FScript: string; JSON 커스터마이징 방식 2: RegisterConverter 프로시저를 오버로딩하기 프로시저 오버로딩을 통해 파서인 TJSONMarshal 클래스의 기본 전환 방식을 덮어쓰는 방법 JSON의 특정 필드의 데이터 타입 바꾸기 등 얼마든지 처리 로직을 바꿀 수 있다. (굳이 RTTI를 건드리지 않아도 된다) 좀 복잡하긴 하지만, 필요하다면 이처럼 TJSONMarshal 클래스를 생성할 때 컨버터를 파라미터로 넣는 방식을 쓸 수 있다. << DelphiCon 2021 목록으로 이동 View full 엠바카데로 기술자료
  6. << DelphiCon 2021 목록으로 이동 원본 비디오(YouTube) 보기 (30 min) DelphiCon 의 2021 시리즈 중, Leaving Delphi 7 - A Success Migration Case - Dion Carlos Mai & Rafael Pereira (30 min) 의 한글 요약본입니다. 20년이 넘었고, 델파이 7로 운영하던 대형 시스템을 델파이 10으로 마이그레이션한 프로젝트를 정리한 내용을 공유합니다. 발표자 Rafael은 이 소매점 시스템 제공자인 Stone Co의 개발자입니다. 목차 회사 및 프로젝트 소개 현대화 마이그레이션을 계획하게 된 동기와 결과 마이그레이션 프로젝트의 개요 마이그레이션 프로젝트의 진행 단계 1단계 (분석 단계) 2단계 (실행 단계) 3단계 (검증 단계) 4단계 (배포 단계) 프로젝트를 통해 배운 점 참고 자료 그림 자료 회사 및 프로젝트 소개 회사 소개: Stone Co Linx: 뉴욕 증시에 상장된 브라질의 소프트웨어 기업. 브라질의 소매점 소프트웨어 시장에서 점유율 45.6% Stone: 뉴욕 증시에 상장된 브라질의 Stone Co.의 핀테크 기업. 지불 관리 도구와 유연한 금융 상품을 제공 Stone Co: 2012년에 시작하여 브라질 전역을 커버하고 있다. 2020년 11월, Stone Co는 Linx를 인수 대상 시스템 소개: Linx Big 20년이 넘은 소매점 소프트웨어. 약국에서 주로 사용 현재 3,000개가 넘는 고객사와, 5,000개가 넘는 소매점에서 사용 중 (2개 이상을 사용하는 점포가 많으므로, 배포된 소프트웨어는 훨씬 더 많음) 현대화 마이그레이션을 계획하게 된 동기와 결과 윈도우10은 2021년 10월 현재 전세계 데스크탑 점유율의 80%이상을 차지하고 있고 계속 증가하는 추세임 (관련 통계 보기) 윈도우 10에 집중해야 하는 상황 윈도우 10의 새 운영체제 기능을 충분히 지원하는 IDE 기능이 필요 윈도우 10에 맞는 더 좋은 시각적인 UI가 필요 (그림 참조: 예전 화면과 새 화면 비교) 스타일, 대시보드, 그래프, 메뉴 등 더 풍부한 화면을 제공하여 외관상 완전히 다른 새 소프트웨어로 보임 (그림 참조: 새 화면) 델파이 10에 내장된 컴포넌트와 스타일만 사용하여 화면 개발 새 UI를 구현하기 위한 코드는 새로 작성 하지만, 모든 클래스와 소스 코드는 예전 델파이 7과 거의 같음 스레드가 필요 요즘 소프트웨어는 API 통합이 많다. 델파이 7에서도 스레드를 구현할 수 있다. 하지만, 델파이 7은 소프트웨어를 개발하기에 여전히 매우 훌륭한 도구이지만, 20년 전에 나온 오래된 버전이다. 델파이 최신 버전을 사용하지 않으면, API 통합을 모두 지원하거나 JSON 직렬화하기 등 새 기술을 반영하기가 어렵다. 기타 개선 델파이 10의 장점을 가질 수 있게 됨: 64비트 지원, 윈도우 10 지원, FireDAC으로 데이터 연결, 다른 운영체제에서 접근 등등 델파이 10 시애틀을 선택한 이유 당시 우리 개발자 대부분이 사용할 수 있는 라이선스를 이미 가지고 있었기 때문 마이그레이션 프로젝트의 개요 목표 델파이 7에서 델파이 10 시애틀로 이전 마리아DB 10.4.13과 MySQL 8.0.21 지원 개요 기간: 총 7 개월 투입 인원 외부: 3명 (아쿠아소프트 개발자, 마이그레이션에만 집중) 내부: 22명 (개발자, QA, 델파이 프로젝트 별 책임자) 마이그레이션 대상 (소매점 관리 시스템)의 규모 델파이 프로젝트: 65개 모듈: 194개 .PAS 파일: 6,000개 .DFM 파일: 2,000개 코드 줄 수: 2백만줄 써드 파티 컴포넌트 세트: 23가지 마이그레이션 프로젝트의 진행 단계 총 4단계로 나누고, 앞 단계 완료 시 다음 단계를 진행: 분석, 실행, 검증, 배포 1단계 (분석 단계) 개요 기간: 3.5개월 투입 인원 외부: 3명 (아쿠아소프트 개발자) 내부: 2명 (개발자 1명, QA 1명) 목표 측정 / 식별 / 문서화 델파이 10으로 마이그레이션이 끼칠 영향 새 데이터베이스와의 호환성 프로젝트로 인해 달라지는 점과 문제점 변경으로 인해 발생될 것으로 예상되는 문제 해소 자동 변환 도구 개발 작업 내용 외부 인력 (아쿠아소프트) 합류 전환할 컴포넌트를 3가지로 나누어 분석하고 대비 버전의 수명이 다한 컴포넌트 마이그레이션 시 문제가 많고, 작업도 많이 해야 하는 것들임 이 프로젝트에서 많이 사용될 컴포넌트 프로젝트에서 사용되는 비중이 크기 때문에, 그 만큼 프로젝트에 끼치는 영향도 큰 것들임 소스코드가 없는 컴포넌트 매우 이상하게 작동할 수 있고 마이그레이션 작업을 까다롭게 만들기 때문에 주의해야 할 것들임 예: Zeos (2003년부터 사용한 오픈 소스 데이터베이스 컴포넌트) [에러가 발생되는 코드] QPrincipal.FieldByName('datapre').AsDateTime :=now; 짐작하겠지만, 상업용 시스템에서 매우 많이 사용되는 코드이며, 모두 수정해야 하므로, 에러 해소 작업이 많다. 델파이 버전 간의 차이와 충돌을 식별하고 해소 유니코드 (영향 받는 dll이 있는 지까지도 파악할 필요가 있음) 자동 변환 도구 개발 ("마이그레이션 찾아 바꾸기"라고 이름 지음) 소스 변환 자동화 도구 개발을 이 분석 단계 안에서 완성 (그림 참조: 소스 변환 자동화 도구와 변환 결과) Zeos에서 UniDAC으로 마이그레이션하는 도구 마법사가 발생시키는 버그 수정 자동화 사용되는 모든 컴포넌트 마이그레이션 자동화 (많은 컴포넌트에서 일부 프로퍼티가 변경되었기 때문) 델파이 10과 델파이 7과의 차이로 인해 필요한 코드 변환 자동화 "모든" 소스를 변환할 수 있는 "맞춤" 도구 완성 소스 코드 동기화 소스 코드 동기화는 매우 중요! 마이그레이션 프로젝트 진행 중에도 전체 배포까지 완료되기 전까지는 전환해야 할 코드가 계속 변하기 때문이다. 분석 단계가 진행되는 중에도 내부 개발자 20명은 여전히 기존 코드나 화면을 변경하고 새 기능을 추가한다. 즉, 마이그레이션 대상인 델파이 7 소스 코드가 계속 변한다. 2단계 (실행 단계) 모든 코드를 변경하는 단계 개요 기간: 2일 (사전 준비가 완료되었으므로 가능) 참여 인원 외부: 1명 (아쿠아소프트 개발자) 내부: 1명 (개발자) 목표 코드 "전체"를 자동 변환 도구를 사용하여 델파이 7에서 델파이 10으로 변경 작업 내용 진행 전 실행 단계 직전에 기존 프로그램 변경을 금지 1일차 자동 변환 도구를 사용하여 변환 개발자 2명이 6시간 만에 완료 총 4,678개 소스 파일을 변경 2일차 컴포넌트 적합성, 데이터베이스 적합성이 파악된다. 모든 변경이 계획대로 되었는 지를 검증 3단계 (검증 단계) 개요 기간: 2.5 개월 참여 인원 외부: 3명 (아쿠아소프트 개발자) 내부: 22명 (개발자, QA, 델파이 프로젝트 별 책임자) 목표 모든 델파이 프로젝트와 모든 작동을 검증, 버그 파악 및 해소 팀 구성 개발자 (버그 픽스 전담) 그룹 개발자 (일반) 그룹 QA 그룹 델파이 프로젝트 별 책임자 그룹 작업 내용 검증 단계 시작 전 (즉, 실행 단계인 2일 간) 델파이 10 사용자 교육 이유: 모든 개발자는 이 검증 단계 시작을 기준으로, 그 이후에는 델파이 10만 사용 (그 이전에는 델파이 7만 사용) 내용: 델파이 10 사용법, 새 기능, 검증 단계 및 이후 유지 보수 시 작업 절차 결과: 개발자 등 모든 참여 인원이 검증 단계에 참여할 준비를 완료 검증 단계 시작 직후 버그가 매우 많이 보고되었고, 버그의 대부분을 신속하게 해소했다. 자동 변환 도구를 이용했기 때문이다. 버그 발생: 변환 도구에 버그가 있으면, 변환된 시스템에도 버그가 생긴다. 버그 해소: 변환 도구의 버그를 수정하면, 변환된 시스템의 해당 버그도 해소된다. 앞 단계에서 접근할 수 없었던 것들에서 발생되는 버그를 QA들이 찾아냈는데, 이런 검증은 도움이 되었다. 검증 결과 모든 기능 중 97% 검증 완료 나머지 3%는 외부 API라서 내부 테스트 불가 이유: 실제 운영 환경에서 테스트할 수 밖에 없는 외부 데이터) 대책: 외부 API 목록 작성과 운영 환경에서 완벽한 테스트를 할 수 있도록 미리 준비 (매우 중요) 이 단계에서는 버그 해소 작업에 큰 수고가 들지 않았다. 버그 등록이 지속적으로 줄어듬 종료 시점에 모두 해소 4단계 (배포 단계) RC (Release Candidate, 출시 후보) 버전 배포, 합격 판정 후 전체 배포 개요 기간: 5주 참여인원 내부: 2명 (QA 1명, 개발자 1명) 고객: 선정된 고객사 104곳 (사용 점포 브라질 18개 주에 있는 소매점에 총 184개 배포) 브라질 소매점 특성 상, "모든 지역"에서 문제가 없는 지 검증해야 함 (브라질은 각 주마다 세금과 규제가 다름) 작업 내용 4-1 단계: RC(Release Candidate, 출시 후보) 버전 배포 준비 RC (Release Candidate, 출시 후보) 버전 합격 기준 설정 중요도 0 (우회 해소 방법이 없는) 버그: 최대 5개 이내 중요도 1 (우회 해소 방법이 1가지인) 버그: 최대 20개 이내 위 기준 중 하나라도 통과하지 못하면 이 RC 버전은 불합격으로 판정하고 즉시 배포를 중단하기로 함 RC 버전 최대한 완벽한 버전을 준비 RC 버전을 배포 고객 "모든" 기능이 검증될 수 있도록 "세심하게" 고객을 선정 (고객마다 사용하는 기능, 방식, 환경이 제각각일 수 있다) 4-2 단계: RC 버전 배포 및 판정 배포 후 보고된 버그의 갯수 (모두 14개, 그림 참조: 버그 발견 및 해소 추이) 중요도 0: 1개 (기준치인 5개 이내, 통과) 중요도 1: 4개 (기준치인 20개 이내, 통과) 중요도 2: 7개 중요도 3: 2개 기준에 의거 성공 판정 4-3 단계: 전체 배포 100% 델파이 10으로 전환 완료 해소해야 할 마이그레이션 관련 버그가 없음 처음 계획한 데이터베이스를 지원 확인 현재, 99% 고객이 업데이트된 시스템을 사용 프로젝트를 통해 배운 점 분석 단계 (자동 변환 도구 개발 포함)에서 QA는 한두명만 참여하면 된다. 이 단계에서 QA가 많고, 버그를 많이 찾아낸다고 해서 무조건 좋은 것은 아니다. QA는 자동 변환 도구의 결과를 테스트 주의! 이 단계에 QA를 많이 투입할 것인지 아닌 지에 대한 결정은 프로젝트에 따라 다를 수 있으므로 매우 조심해야 한다. 써드-파티 컴포넌트는 분석이 매우 잘 되어야 한다. 심지어 프로젝트 전부터 미리 충분히 파악해야 한다. 마이그레이션 프로젝트가 시작되면 써드-파티 컴포넌트에 대한 작업과 부담이 많기 때문이다. 마이그레이션을 자동화하는 "맞춤" 변환 도구를 개발한다. 프로젝트가 많고 규모가 커도 처리할 수 있다. 검증 단계는 매우 중요하다. 충분한 테스트 시간이 필요하다. 검증 단계를 거치지 않고 바로 배포로 넘어가면 운영 시 문제가 많을 수 있다. 그러면 경영진은 이 프로젝트가 실패했다고 판정할 수 있다. 그러면, 검증 단계 하나의 실패로 끝나지 않는다. (우리는 검증 단계에 충분한 시간을 계획하였기 때문에 문제가 없었다) 프로젝트 중 소통이 매우 중요하다. 모든 팀원들이 진행 중인 작업을 항상 업데이트할 수 있는 절차와 환경이 구축되어야 한다. 그래야, 서로 다른 사람의 작업에 어떤 영향을 주거나 받는 지를 미리 파악할 수 있다. 프로젝트 평가 기준을 구체적이고 명확히 정하고 모니터링해야 한다. 계속 앞으로 갈 것인지 아니면 중단해야 하는 지에 대한 기준을 정하는 것은 매우 중요하다. 마이그레이션 목적이 분명해야 한다. 프로젝트 진행 중에 목적이 변경되면, 3-4개월 이상 지체될 수 있다. 목적과 목표가 분명해야 계획된 일정을 준수할 수 있다. 델파이 7은 매우 훌륭한 도구이지만, 거기에 머물고 있을 수 만은 없다. 머물러 있고 싶어도, 우리가 앞으로 가야할 시간은 언젠가 오게되어 있다. 20년 전에 출시된 델파이 7로는 지금 시점에서 필요한 기술 모두를 지원하지 못한다. 시스템 운영에 크리티컬할 기술에서도 그렇다. 구버전에서 벗어나서 최신 기술에 맞춰가자. 참고 자료 What's New in Delphi 10 Seattle https://www.embarcadero.com/products/delphi/whats-new Embarcadero Update Center https://www.embarcadero.com/rad-in-action/migration-upgrade-center Desktop Windows Version Market Share Worldwide http://gs.statcounter.com/windows-version-market-share/desktop/worldwide/#monthly-201509-202110 Delphi Unicode: https://www.embarcadero.com/images/dm/technical-papers/delphi-unicode-migration.pdf 그림 자료 마이그레이션 전과 후의 UI 비교: 로그인 화면 새 UI에서 사용자 테마 선택 지원: 설정 화면 새 UI에서 사용자 테마 선택 지원: 다크 테마 적용 시 화면 마이그레이션 전과 후의 UI 비교: 일반 화면 새 UI에서 대시보드 구현 새 UI에서 실시간 그래프 구현1 새 UI에서 실시간 그래프 구현2 "맞춤" 자동 변환 도구 예시: 써드 파티 컴포넌트의 경우 프로퍼티 변환이 필요함 3단계 (검증 단계)의 버그 발견 및 해소 추이 << DelphiCon 2021 목록으로 이동 View full 엠바카데로 기술자료
  7. << DelphiCon 2020 목록으로 이동 DelphiCon 의 2020 시리즈 중, Introduction to Spring4D - Taking Delphi Development to the Next Level - Stefan Glienke 의 한글 요약본입니다. 오픈 소스인 스프링4D를 사용하면, 코드는 더 짧아지고, 일관성과 품질은 더 높아집니다. 예를 들어, 스마트 포인터를 사용하면 오브젝트를 만들어 쓸 때 메모리 누수를 걱정할 필요가 없습니다. Spring4D의 컬렉션은 컬렉션이나 리스트 관리가 매우 간편합니다. 발표자 (Stefan Glienke)는 델파이 경력 20년이 넘은 개발자이며, Spring4D 오픈소소의 개발 책임자입니다. 원본 비디오에는 아래 요약보다 자세한 데모와 설명이 있습니다. 목차 Spring4D 란 Spring4D를 받는 방법 Spring4D의 기본 구성 Spring4D 구성 요소: Spring.Base Nullable Event 스마트 포인터 (Shared 와 Weak) Collections: 가장 많이 활용되는 라이브러리 Collections 인터페이스 읽기전용 vs 불변 IEnumberable ICollection IList IMap, IDictionary, IMultiMap ISet, IMultiSet 더 많은 내용 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> 특정 데이터 타입을 유지하면서도, Null값을 가질 수 있도록 한다. 이제는 억지값 즉, Null (값이 없음)을 넣기 위해 아래와 같은 값이나 코드가 필요가 없다. 날짜 타입에 “날짜가 주어지지 않음”을 넣기 위해 -40000 이라는 가상의 값 넣기 Boolean에 “참인지 거짓인지 모름”을 넣기 위해 True/False/FileNotFound로 Inum을 직접 만들어 사용하기 Variant 타입과는 다름 Variant는 호환 가능한 타입 변환을 암시적으로 수행하므로 언제든 다른 타입으로 변환될 여지가 있다(type unsafe). 예를 들어 Variant에는 숫자 3과 문자열 ‘3’을 모두 넣을 수 있고, 상황에 맞게 반영된다. Nullable<T>는 타입을 유지하기 때문에, 정해진 타입만 담을 수 있다(type safe). Nullable<Integer>에는 정수 3만 넣을 수 있고 문자열 3을 넣을 수는 없다. // [Nullable<T> 예문] uses Spring; procedure … var n, n2: Nullable<TDateTime>; // Spring 유닛 필요 d: TDateTime; begin d := now; Log.Lines.Add(n.HasValue.ToString); //[결과: False]: 디펄트에서 Nullable.HasValue의 결과는 False Log.Lines.Add(n.ToString); //[결과: Null]:디펄트에서 Nullable.ToString의 결과는 ‘Null’(값없음) n := d; Log.Lines.Add(n.HasValue.ToString); //[결과: True]: 특정 시간을 넣으면, Nullable.HasValue의 결과는 True Log.Lines.Add(n.ToString); //[결과: 19.11.2020 18:11:51]: 독일 기준 날짜 형식 (발표자의 시간대가 독일로 되어 있음) n := nil; //n에 들어있는 값을 없앤다 Log.Lines.Add(n.HasValue.ToString); //[결과: False]: nil을 넣으면, Nullable.HasValue의 결과는 False Log.Lines.Add(n.GetValueOrDefault().ToString); //[결과: 30.12.1899] // Nullable.GetValueOrDefault().ToString의 결과는 값이 있으면 그 값을, 없으면 기본값인 30.12.1899을 출력 Log.Lines.Add((n=n2).ToString); //[결과: True]: n과 n2는 둘다 값이 없으므로, n과 n2를 비교하면 ‘같음’이 된다 n := d; Log.Lines.Add((n=n2).ToString); //[결과: False]: n에만 d를 넣으면, n과 n2를 비교하면 ‘다름’이 된다 n2 := d; Log.Lines.Add((n=n2).ToString); //[결과: True]: n과 n2는 둘다 d와 같은 값이므로, n과 n2를 비교하면 ‘같음’이 된다. end; 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; // 연산자 오버로딩 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; //Shared<T>가 아닌 일반 TStringList weakRef: Weak<TStrings>; //문자열을 Weak 참조하기로 한다. begin sl := TStringList.Create; weakRef := sl; // 연산자 오버로딩 sl.Add(‘델파이콘’); Log.Lines.Add(weakRef.IsAlive.ToString); //[결과: True]: 참조 타겟이 살아있다 Log.Lines.AddStrings(weakRef); //[결과: 델파이콘]: 참조 대상의 값, 연산자 오버로딩 Log.Lines.AddParagraph; sl.Free; //Share<T> 가 아니므로 직접 Free 했다. 그러면, Weak 참조하고 있는 곳에 모두 공지된다. // Weak<T>는 모든 플랫폼에서 작동된다. Log.Lines.Add(weakRef.IsAlive.ToString); //[결과: False]: 참조 대상이 제거되었음’을 알고 있음 Log.Lines.Add(Assigned(weakRef.Target).ToString); //[결과: False]: 참조 대상이 Assigned 되지 않았음’을 알고 있음, Weak<T>.Target와 Shared<T>.Value는 같은 역할 Log.Lines.Add(weakRef <> nil).ToString; //[결과: False]: 참조 대상이 nil 임’을 알고 있음 end; 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개를 뽑는 것이 아니라, 대상 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은 IDictionary와 IMultiMap의 기반 타입이다 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 … 더 많은 내용 오픈 소스 코드 살펴보기 (유닛 테스트 코드 역시 많이 있다) 코드에는 xml 문서도 많다. 도움말과 시작하기 문서 질문 또는 의견: https://groups.google.com/g/spring4d 버그 리포트: https://bitbucket.org/sglienke/spring4d/issues << DelphiCon 2020 목록으로 이동
  8. << DelphiCon 2020 목록으로 이동 DelphiCon 의 2020 시리즈 중, Functional Programming With Delphi - Nick Hodges 의 한글 요약본입니다. 프로그래밍 방식에 대한 새로운 관점을 가질 수 있는 세션입니다. 함수형 프로그래밍은 지금까지 일반적으로 해온 명령형 프로그래밍보다 더 간결 명료하고, 멀티 쓰레드에서도 안전한 코드를 실현합니다. 발표자 (Nick Hodges)는 델파이 개발자라면 한번쯤 들어본 저명한 델파이 전문가입니다. 저서 델파이로 코딩하기 (Coding in Delphi) 델파이로 코딩하기 속편 (More Coding in Delphi) 델파이에서 의존관계 인젝션 (Dependency Injection in Delphi) 원본 비디오에는 아래 요약보다 자세한 데모와 설명이 있습니다. 목차 상태의 실패(The Failure of State) 불변 (Immutable) 타입을 사용하자 함수형 프로그래밍과 명령형 프로그래밍 함수형(Functional) 프로그래밍이란? 명령형(Imperative) 프로그래밍이란? 함수형(Functional) 프로그래밍과 (우리에게 익숙한) 명령형(Imperative) 프로그래밍의 차이점 함수형 프로그래밍 이해하기 함수형 프로그래밍: 메소드/함수의 요건 함수형 프로그래밍: 함수의 특징 함수형 프로그래밍: 함수 (매우 중요!) 함수형 프로그램밍: 완성된 프로그램의 특징 함수형 프로그래밍 방식으로 생각하기 타협점 예문: 델파이 구현한 함수형 프로그래밍 1. 정해진 갯수까지 제곱값 구하기 2. 과잉수 (abundant number, 약수의 값이 자기 자신보다 큰 숫자) 구하기 요약 상태의 실패(The Failure of State) 함수형 프로그래밍 강연에서 Robert C. Martin는 "상태는 변하고, 이런 변화는 프로그램에 영향을 끼친다"고 설명한다. 변수의 값은 변한다. 변수 값의 변화를 추적할 수 없으면 코드를 이해하기 어렵다. 메소드 호출 사이의 관계를 파악하지 못하면 코드의 흐름을 따라가기가 어렵다. 특히, 멀티 쓰레드를 사용하는 경우, 상태가 변화가 발생하는 코드를 이해하거나 디버깅하기는 더 어렵다. 불변 (Immutable) 타입을 사용하자 불변 클래스는 (생성자에서) 한번만 값을 검증하면 된다. 이후에는 변경되지 않기 때문이다. 예를 들어, 이메일에 값이 들어있는지 아닌지 등을 확인할 때 생성자 호출 코드만 보면 된다. 불변 클래스는 한번 만들어지면 없어질 때까지 상태가 변하지 않으므로 상태 변경을 걱정할 필요가 없다. 서로 다른 쓰레드에서 함께 사용되어도 안전하다. 코드 읽기가 더 쉽다. 상태 변화를 추적하기 위해 메소드를 하나씩 추적할 필요가 없다. 가변 클래스와 불변 클래스 예시 // [1] 일반적인 클래스 타입 (내부 데이터 변경 허용, 멀티 쓰레드에서 안전하지 않음) type TMutablePerson = class private FName: string; Email: string; procedure SetName(const Value: string); procedure SetEmail(const Value: string); public constructor Create(const aName: string; const aEmail: string); property Name: string read Name write SetName; //프로퍼티에서 쓰기가 가능하므로 이름을 바꿀 수 있다 property Email: string read Email write SetEmail; //프로퍼티에서 쓰기가 가능하므로 이메일을 바꿀 수 있다 end; // [2] 불변 클래스 타입 (내부 데이터 변경 불가. 멀티 쓰레드에서도 안전) type TImmutablePerson = class private FName: string; Email: string; public constructor Create(const aName: string; const aEmail: string); property Name: string read Name; //생성된 후에는 외부에서 읽기만 가능 property Email: string read Email; //생성된 후에는 외부에서 읽기만 가능 end; // [3] 불변 클래스 타입 (내부 데이터 변경을 허용하지만, 실제로는 새 인스턴스를 제공하므로 멀티 쓰레드에서도 안전) type TPerson = class private FName: string; Email: string; public constructor Create(const aName: string; const aEmail: string); function ChangeName(const Value: string): TPerson; //데이터가 변하지만, 실제로 새 인스턴스를 반환 function ChangeEmail(const Value: string): TPerson; //데이터가 변하지만, 실제로 새 인스턴스를 반환 property Name: string read Name; //생성된 후에는 외부에서 읽기만 가능 property Email: string read Email; //생성된 후에는 외부에서 읽기만 가능 end; function TPerson.ChangeEmail (const aEmail: string) : TPerson; begin Result := TPerson.Create(FName, aEmail); //기존 Person의 이메일이 바뀌는 대신 새 Person이 생긴다. // 참고로, 델파이에는 가비지 컬렉션이 없으므로, 기존의 오브젝트는 제거하여 메모리 누수를 방지할 필요가 있다. end; 함수형 프로그래밍과 명령형 프로그래밍 함수형(Functional) 프로그래밍이란? 함수형 프로그래밍은 크게 주목받고 있다. Haskell, Scala, Clojure, F#, Erlang, Groovy 등이 모두 함수형 프로그래밍 언어이다. 함수형 프로그래밍으로 만든 프로그램에는 오로지 함수만 있다. 즉 모든 것이 함수이다. 급부상 하는 이유 저명한 프로그래머들이 함수형 프로그레밍을 하고 있다. (순수 함수형 프로그래밍은) 멀티 쓰레드에서 안전하다. 더 간결하고, 더 작성하기 쉽다. (간단한 함수 호출 코드로 기존의 긴 코드를 교체할 수 있다) 테스트와 디버깅이 더 쉽다. (상태 변화를 추적할 필요가 없기 때문에) 명령형(Imperative) 프로그래밍이란? 컴퓨터 프로그램 컴퓨터가 수행할 작업이 정의된 명령 세트 컴퓨터에게 작업 지시를 하면 컴퓨터는 지시받은 대로 수행한다. 우리가 코드를 작성하고 생각하는 익숙한 방식은 이른바 “명령형” 프로그래밍은 어떻게(HOW) 수행할 것인지를 정해주는 방식이다. “명령형” 프로그래밍은 상태를 변경하는 방식이다. 함수형(Functional) 프로그래밍과 (우리에게 익숙한) 명령형(Imperative) 프로그래밍의 차이점 명령형 (Imperative) 프로그래밍 함수형(Functional) 프로그래밍 코드의 목적 어떻게 (HOW) 하는 지를 정의 무엇을 (WHAT)을 하는 지를 정의 코드 작성 순서대로 명령을 실행하는 "과정" 중심 표현식의 "결과" 중심 문제 해결 방식(예시) 목록에서 다음 고객을 선택하고, 만약 청구할 금액이 있으면, 청구서를 발행한다. 만약 목록에 고객이 더 있으면, 첫단계로 가서 반복한다. 목록에서 청구할 금액이 있는 모든 고객에게 청구서를 발행한다. 함수형 프로그래밍 이해하기 먼저, 고정 관념에서 완전히 벗어날 필요가 있다. “객체 지향은 움직이는 부분을 캡슐화함으로써 코드를 이해할 수 있도록 한다. 함수형 프로그래밍은 부분의 움직임을 최소화함으로써 코드를 이해할 수 있도록 한다” - 마이클 페더스 (Michael Feathers) 디버깅을 해본 개발자라면, 움직이는 부분들이 너무 많고, 코드를 이해하려면 이 많은 움직임을 힘들게 쫓아다녀야 한다는 점을 잘 안다. 움직이는 부분이 최소화 된다면 훨씬 단순해 질 수 있다. 함수형 프로그래밍: 메소드/함수의 요건 함수형 프로그래밍에서 메소드/함수는 참조 투명성이 확보된다. (즉, 참조하는 쪽에서 볼 때 ‘항상’ 같은 결과를 확신할 수 있다) 즉, Method Signature가 언제나 정직하다. (즉, 인자가 같으면 항상 그 결과도 같다) 예를 들어 메소드에 A를 전달하면 결과는 항상 B이다. (B가 아닌 결과는 절대 나올 수 없다) [퀴즈] Assert.True (f(x), f(x))는 항상 ‘참’인가? 항상 ‘참’이라고 보장할 수 없다. 각 f(x)의 결과가 다를 수 있고, 그러면 ‘거짓’일 수도 있기 때문이다. 항상 ‘참’이라고 보장하려면, f(x)의 결과가 항상 같다고 보장되어야 한다. 즉 f(x)는 “참조 투명성”이 확보된 순수한 함수여서 상태가 전혀 변하지 않아야 한다. 참조 투명성이 확보된 함수와 그렇지 않은 함수의 차이 // 호출되는 시점에 따라 결과가 달라지는 함수 function GetElapseTime (const aThen: TDateTime): TTimeSpan; begin Result := Now - aThen; //같은 값을 받아도 호출되는 시점에 따라, Now의 값이 다르기 때문에, 결과도 달라진다. end; // 호출되는 시점과 관계없이 항상 결과가 같은 함수 function sGetElapseTime (const aThen, aNow: TDateTime): TTimeSpan; begin Result := aNow - aThen; //받은 값으로만 계산하므로, 같은 값을 전달하면 결과도 항상 같다. end; 함수형 프로그래밍: 함수의 특징 함수형 언어의 빌딩 블록이다. 완전히 캡슐화되어 있다. (일종의 블랙박스이다) 사전에 확실히 정해진다(순수하다) 참조 투명성이 보장된다 즉, 인자가 같으면 결과도 항상 같다. 상태 변경을 하지 않는다. 부작용이 없다. 전역 상태값을 참조하지 않는다. 교환 가능하다. 즉, 함수의 실행 순서가 바뀌어도, 결과가 바뀌지 않는다. 명령형 프로그래밍에서는 함수 간의 호출 순서가 중요해서 주석을 남기기도 하는데 그럴 필요 없다. 함수형 프로그래밍: 함수 (매우 중요!) 함수형 프로그래밍에서 함수는 가장 중요한 지위를 가진다. 추상화의 기본 단위 함수 자체가 인자(Parameter)가 되어 전달될 수 있다. 함수 자체가 결과값으로 반환될 수 있다. 결과를 해당 함수로 교체할 수 있다. 함수는 다른 언어 생성자가 보일 수 있는 곳이면 어디든지 보인다. (함수형 프로그래밍 언어에서 함수는 일종의 루트 타입이다) 부작용이 없다: 멀티 쓰레드에서 안전하고, 항상 결과가 정확하다. 함수형 프로그램밍: 완성된 프로그램의 특징 더 단순하다 (코드가 짧고, 변수의 상태 변화를 걱정하거나 확인할 필요가 없다) 코드 작성과 유지가 더 쉽다. (오직 함수만 만들어 쓰고 관리하면 된다) 시간적 결합 (Temporal Coupling) 문제가 없으므로, 호출 순서를 고민하지 않아도 부작용이 없다. 멀티 쓰레드에서 동시성 문제를 걱정하지 않아도 된다. 변수 값 변화를 확인하느라 애쓰지 않아도 된다. 함수형 프로그래밍 방식으로 생각하기 함수형 프로그래밍을 위한 새로운 생각 상태 변경이 아니라 불변 상태를 생각하자 즉, 변수나 오브젝트의 조건이나 상태를 확인하면서 지시하지 말고, 변하지 않는 것을 정의하고 조합하자. 단계가 아니라 결과를 생각하자 루프 없이 가능한가? 물론 가능하다. (아래 예문 참조) 구조가 아니라 조합을 생각하자 명령이 아니라 선언을 생각하자 원하는 것이 무엇인지를 작성하자. 어떻게 실행하는 지를 명령하지 말자. 함수형 프로그래밍에서 하지 말아야 할 것 변수값 변경 (X) 할당 연산자 (X): 할당문은 상태를 바꾸는 것이 목적이다. 루프 사용 (X): (IEnumerable로 대체하는 아래 예문 참조) 데이터 구조 변경 (X) 부작용이 있는 코드 (X) 생각할 점! GoTo문 사용 중지 운동 당시 많은 개발자들이 당황했었다. 하지만, 지금 우리 대부분은 GoTo 문 없이 코딩을 하고 있다! 타협점 WriteLn: 대부분의 프로그램은 사용자의 입력을 받을 필요가 있다. 사용자와 상호 작용을 위한 타협: 대부분의 프로그램은 결과 표시 등 사용자와 상호 작용을 한다. 예문: 델파이 구현한 함수형 프로그래밍 델파이로 함수형 프로그래밍을 하기 위해 필요한 것 함수형으로 생각하기 IEnumerable 사용 (오픈 소스 Spring4D 에서 제공) 익명 메소드 사용 for 루프를 쓰지 않기 1. 정해진 갯수까지 제곱값 구하기 // [1] 일반적인 델파이 코드 procedure PrintSquare1 (const aInteger: Integer); var i: Integer; begin for i := 1 to aInteger do // i의 값이 계속 바뀐다. begin WriteLn(i, ‘ - ‘, i * i); end; end; // [2] 상태 변경을 피하기 위해 재귀 호출 사용 // (할당문을 사용하는 상태 변경은 피했지만, 재귀 호출은 메모리 부하가 크고 매우 조심해서 사용해야 한다는 점은 다들 안다) procedure PrintSquare2 (const aInteger: Integer); begin if aInteger > 0 then begin PrintSquare2(aInteger - 1); //상태 변경으로 볼 수 있지만, 할당문은 아니다. WriteLn(aInteger, ‘ - ‘, aInteger * aInteger); end; end; // [3] TEnumerable (오픈 소스 Spring4D)와 익명 메소드 활용 uses Spring.Collections: //Spring4D … procedure PrintSquare3 (const aInteger: Integer); begin if aInteger > 0 then begin TEnumerable.Range(1, aInteger).ForEach(procedure(const aInt: Integer) //익명 메소드 begin WriteLn(aInt, ‘ - ‘, aInt * aInt); end; end; // [4] 함수를 반환하는 함수형 작성 type TPrintSquaresProc = reference to procedure (const aInteger: integer); … procedure PrintSquare4: TPrintSquaresProc; begin Result := procedure(const aInteger: integer) begin PrintSquare3(aInteger); end; end; // [5] 함수를 인자로 받는 순수한 함수형 작성 // 제곱을 어떻게 하는 지를 전혀 명령하지 않는다. 그냥 "제곱을 해라" 라고만 한다. type TPrintSquaresProc = reference to procedure (const aInteger: integer); … procedure PrintSquare5(const aInteger: integer; aProc: TPrintSquaresProc); begin aProc(aInteger); end; … //알맞은 함수만 인자로 전달하면 제곱 뿐만 아니라 무엇이든 해당 함수에 의한 출력을 수 있다. //여기서는 PrintSquare4 함수를 전달하여 제곱을 출력한다. PrintSquare5 (10, PrintSquare4()); 2. 과잉수 (abundant number, 약수의 값이 자기 자신보다 큰 숫자) 구하기 (역자 주: 이 요약에서는 일반적인 방식의 코드 설명 부분은 생략) 당연히, 꽤 길고 복잡하고 루프와 할당문이 많이 사용된다. 당연히 코드 작성도 오래걸리고, 변수의 값을 일일이 추적하면서 디버깅하는 것도 오래 걸린다. // 함수형 코드 uses Spring.Collections, uNumberClassificationTypes; type TSomewhatFunctionalNumberClassifier = class public // TSomewhatFunctionalNumberClassifiers는 클래스 함수로만 구성 class function IsFactor(aNumber: Integer; aPotentialFactor: Integer): Boolean; class function Factors(aNumber: Integer): ISet; class function IsAbundant(aNumber: Integer): Boolean; static; class function IsDeficient(aNumber: Integer): Boolean; static; class function IsPerfect(aNumber: Integer): Boolean; static; end; … function SomewhatFunctionalNumberClassifier(aNumber: Integer): TNumberClassifier; begin if TSomewhatFunctionalNumberClassifier.IsPerfect(aNumber) then begin Result := Perfect end else begin if TSomewhatFunctionalNumberClassifier.IsAbundant(aNumber) then begin Result := Abundant end else begin Result := Deficient end; end; end; // [1] 이 함수만 유일하게 상태가 변하고 루프를 사용한다. 이것은 뒤에서 다시 한번 더 함수형으로 개선하겠다. class function TSomewhatFunctionalNumberClassifier.Factors(aNumber: Integer): ISet; var LFactores : ISet; i : Integer; begin LFactores := Collections.CreateSet; for i := 1 to Round(Sqrt(aNumber)) do // 상태가 변하는 유일한 지점 begin if IsFactor(aNumber, i) then begin LFactores.Add(i); LFactores.Add(aNumber div i); end; end; Result := LFactores; end; // 위 [1]을 더 개선한 코드 (TEnumerable.Range와 익명 메소드 활용) class function TSomewhatFunctionalNumberClassifier.Factors(aNumber: Integer): ISet; begin Result := TEnumerable.Range(1, aNumber).Where( function(const aInteger: Integer) : Boolean begin Result := IsFactor(aNumber, aInteger); end); end; class function TSomewhatFunctionalNumberClassifier.IsFactor(aNumber: Integer): Boolean; begin Result := aNumber mod aPotentialFactor = 0; end; class function TSomewhatFunctionalNumberClassifier.IsPerfect(aNumber: Integer): Boolean; begin Result := Factors(aNumber).Sum - aNumber = aNumber; end; class function TSomewhatFunctionalNumberClassifier.IsAbundant(aNumber: Integer): Boolean; begin Result := Factors(aNumber).Sum - aNumber > aNumber; end; class function TSomewhatFunctionalNumberClassifier.IsDeficient(aNumber: Integer): Boolean; begin Result := Factors(aNumber).Sum - aNumber < aNumber; end; 위와 같이 ,함수로만 구성되고, 상태 변화가 없고, 함수가 파라미터로 전달되고, 이해하기 쉽고, 훨씬 단순한 코드 예문을 보았다. 이런 방식을 사용하면 델파이에서 함수형 프로그래밍을 할 수 있다. 요약 순수 함수는 부작용이 없다. 같은 입력을 받으면 항상 같은 출력을 한다. 함수형 프로그래밍은 상태 변경을 하지 않기 때문에 멀티 쓰레드 환경에서도 안전하다. 할당 연산자를 사용하지 않는다. 함수형 프로그래밍은 상태를 공유하지 않는다. << DelphiCon 2020 목록으로 이동
  9. Kori

    [DelphiCon 요약] 델파이 고성능 구현

    << DelphiCon 2020 목록으로 이동 DelphiCon 의 2020 시리즈 중, High Performance Delphi - Primož Gabrijelčič 의 한글 요약본입니다. 델파이 고성능 구현 ‘Delphi High Performance’ 도서 저자가 직접 성능 개선을 데모와 함께 설명합니다. 발표자 (Primož Gabrijelčič)는 1980년대 8비트 시절부터 파스칼 코드를 써오고 있습니다. 주로 방송용 고가용성 서버 프로그램을 개발하고 있으며, 수준높은 주제를 다루는 수많은 기고를 해오고 있습니다. (발표자의 웹페이지: http://thedelphigeek.com) 발표자가 저술한 도서 디자인 패턴을 델파이에서 실습하기 델파이 고성능 구현 (Delphi High Performance) 옴니쓰레드라이브러리를 활용한 병렬 프로그래밍 (Parallel programming with OmniThreadLibrary) 원본 비디오(YouTube) 보기 (56 min)를 권장합니다 목차 성능 향상 개요 알고리즘 향상 알고리즘 복잡도와 성능 빅오(Big-O) 표기법 알고리즘 별로 데이터 증가와 복잡도(성능 저하)의 관계를 파악 더 좋은 알고리즘으로 바꾸기 예제: 무작위 단어 검색 프로그램의 성능을 향상해보자 알고리즘 별로 성능 차이 비교 개선 전: 정렬되지 않은 TStringList 검색 개선 1: 정렬된 TStringList 검색 개선 2: TDictionary 검색 개선 3: 글자 수별로 TStringList를 따로 만들고, 이 TStringList를 다시 오브젝트 리스트에 모아두고 사용하기 코드가 지나치게 많이 실행되는 것을 방지하기 예제: 2GB 파일을 1KB 단위로 읽고, 진행률을 표시하는 프로그램을 개선해보자 개선 전(흔한 코드): “화면에 진행율을 표현하는 코드”가 성능을 크게 떨어뜨리고 있었다. 개선 후(화면 업데이트를 필요한 만큼만 실행): “화면에 진행율을 표현하는 코드”는 100번만 실행하면 충분하다. 예제: TListBox와 TMemo에 각각 항목을 10,000 개씩 넣는 프로그램을 개선해보자. 코드 개선 전/후의 소요 시간 비교표 개선 전: 10,000 항목 x 2개 컨트롤 = 20,000번 윈도우와 메시지 교환 개선 1: 루프 앞뒤에 BeginUpdate와 EndUpdate 호출하여 윈도우와 메시지 교환을 2번으로 줄인다. 개선 2.1-TMemo: 해당 루프 안에서는 항목을 화면을 사용하지 않는 오브젝트인 TStringList에 넣고, 루프 밖에서 TStringList를 TMemo에 바인딩하여 모든 항목을 한번에 넣기 개선 2.2-TListBox를 가상 모드로 사용하여 필요할 때 데이터를 추가하기 불필요한 코드 실행 없애기 예제: (캐싱) 지역변수를 활용한 캐싱 예제: 피노나치 수열 계산 프로그램 개선하기 개선 전 (기존 코드): 피보나치 수열을 계산 수학식을 그대로 코드에 넣으면 심각한 성능 문제가 발생 개선1(캐싱): 재귀 호출을 하되, 적어도 동일한 연산은 반복하지 않도록 한번 연산된 것은 결과를 캐시(저장)해 두었다가 재사용 하자. 개선2: 재귀 호출 알고리즘을 순차적 반복 호출 알고리즘으로 바꾸어서 해소 알고리즘 향상에 관심이 있는 개발자에게 권장하는 자료 성능 향상 개요 ‘성능 향상 작업’을 하는 순서 (’성능 향상’이 의미하는 바는 사람들마다 다를 수 있지만, 순서는 거의 같다.) 문제 식별 (‘측정’이 중요하다!) TStopwatch, 성능 카운터, GetTickCount 등을 활용하여 명시적으로 측정하자. AQTime 등 전문 프로파일링 도구를 사용하는 것도 좋다. ‘알고리즘 향상’: 가장 좋은 방법! 이 세션에서 다룰 내용이다! 세부적인 코드 튜닝 병렬 처리 적용 외부 라이브러리 사용 더 좋은 알고리즘을 제공하는 라이브러리를 도입하여 빠르게 문제 해결 가능. 주의할 점: 외부 라이브러리 제작자가 유지보수를 하지 않는 상황에 대한 대비가 필요 (성능 향상이 필요한 특정 부분을) 어셈블리어와 같은 저수준 언어로 대체 주의할 점: 10년 후에는 지금보다도 저수준 언어 개발자 찾기가 더 어려워 진다는 점을 고려해야 함 알고리즘 향상 알고리즘의 복잡도와 성능 빅오(Big-O) 표기법 알고리즘 별로 데이터 증가와 복잡도(성능 저하)의 관계를 파악 성능 향상 방법 더 좋은 알고리즘으로 바꾸기 코드가 불필요하게 많이 실행되는 상황을 찾아서, 실행 횟수를 줄이기 코드가 실행하지 않아도 되는 상황을 찾아서, 실행되지 않도록 하기 알고리즘 복잡도와 성능 빅오(Big-O) 표기법 컴퓨터 과학에서 빅 O 표기법은 입력 크기가 커짐에 따라 실행 시간 또는 공간 요구 사항이 어떻게 증가하는 지를 파악함으로써 알고리즘을 분류하는 데 사용된다. 데이터가 증가할 수록 프로그램의 속도가 얼마나 느려지는 지를 판단할 때에도 유용하다. 프로그램의 성능 문제는 데이터가 많아지면서 발생되는 경향이 크다. 관련 웹페이지 추천: 알고리즘 별 복잡도가 잘 정리된 웹 문서: http://bigocheatsheet.com 다양한 데이터 구조를 자세히 비교한 웹 문서: https://geeksforgeeks.org/data-structures 알고리즘 별로 데이터 증가와 복잡도(성능 저하)의 관계를 파악 O(1)은 데이터가 아무리 증가해도 성능이 저하되지 않는다. 반면에, 피보나치 수열 재귀호출은 복잡도 즉 성능이 급격하게 저하된다 (역자 주: 재귀 호출은 매우 강력하지만, 코딩에 적용하기 전에 주의해야 합니다. 뒤에서 재귀 호출과 관련된 예시와 개선 방향이 제시됩니다) 더 좋은 알고리즘으로 바꾸기 예제: 무작위 단어 검색 프로그램의 성능을 향상해보자 (비디오 13분 30초부터 보기) 프로그램: 글자수가 주어지면,그 글자수에 해당하는 영어 단어를 무작위로 만들고, 37만개 단어가 들어있는 목록 안에 있는 단어인지를 알려준다. 모든 목록은 동일한 파일(37만 단어)을 읽어서 만든다. 목록 생성은 한번만 실행되면 되지만, 검색은 훨씬 더 많이 실행된다는 점을 고려하자. 알고리즘 별로 성능 차이 비교 알고리즘 글자수 증가에 따른 결과 비고 일반적인 코드 정렬되지 않은 TStringList 검색 4글자 단어까지는 1초 이내 완료 5글자 단어는 1초 이상 소요 6글자 단어부터는 시간 초과 IndexOf 함수에서 RTL은 순차 탐색을 한다.: O(n)에 해당 최악의 경우, 37만번째 단어까지 찾아봐야 결과를 알 수 있다. 개선1 정렬된 TStringList 검색 6글자 단어까지는 1초 이내 완료 7글자 단어는 1초 이상 소요 8글자 단어부터는 시간 초과 IndexOf 함수에서 RTL은이분 탐색을 한다: O(log n)에 해당 정열된 StringList는 처음 만드는데 걸리는 시간은 더 길지만, 검색 속도는 더 빠르다. 개선2 TDictionary 사용 7글자 단어까지는 1초 이내 완료 8글자 단어는 1초 이상 소요 9글자 단어부터는 시간 초과 델파이 Disctionary의 ContainsKey 함수는 Array보다 검색이 빠르지는 않지만, 목록이 클 경우StringGrid의 IndexOf 함수보다는 검색이 빠르다. 개선3 글자 수 별로 TStringList를 따로 만들어서 사용 글자수와 관계없이 1초 이내 완료 성능 개선을 위한 알고리즘을 창의적으로 만들자. 검색 시에는 해당 글자수에 맞는 TStringList를 찾고 그 안에서만 검색한다. 개선 전: 정렬되지 않은 TStringList 검색 // 목록을 생성하는 코드 FWordsUnsorted := TStringList.Create; FWordsUnsorted.Assigned(wordList); … // 목록 안을 검색하는 코드 FindGoodWord ( function (const word: string): boolean begin Result := FWordsUnsorted.IndexOf(word) >= 0; end); 개선 1: 정렬된 TStringList 검색 // 목록을 생성하는 코드 FWordsSorted := TStringList.Create; FWordsSorted.Assigned(wordList); FWordsSorted.Sorted := True; … // 목록 안을 검색하는 코드 FindGoodWord ( function (const word: string): boolean begin Result := FWordsSorted.IndexOf(word) >= 0; end); 개선 2: TDictionary 검색 // 목록을 생성하는 코드 FWordsDictionary := TDictionary.Create(wordList.Count); for word in wordList do FWordsDictionary.Add(word, True); //Key에 단어를 넣고, Value에는 그냥 True로 모두 넣는다. … // 목록 안을 검색하는 코드 FindGoodWord ( function (const word: string): boolean begin Result := FWordsDictionary.ContainsKey(word) >= 0; end); 개선 3: 글자 수별로 TStringList를 따로 만들고, 이 TStringList를 다시 오브젝트 리스트에 모아두고 사용하기 // 목록을 생성하는 코드 FWordsLOL := TObjectList.Create; FWordsLOL.Add(nil); for word in wordList do begin while FWordsLOL.Count <= Length(word) do FWordsLOL.Add(TStringList.Create); FWordsLOL[Length(word)].Add(word); end; … // 목록 안을 검색하는 코드 wordLen := inpWordLength.Value; if (wordLen < 1) or (wordLen >= FWordsLOL.Count) then //글자수에 해당하는 목록 자체가 없으면 검색할 필요도 없다. idx := -1 else //글자수에 해당하는 목록이 있으면, 해당 검색할 문자를 만들고, 해당 글자수로 된 단어를 모아둔 리스트에서 검색한다. idx := Random(FWordsLOL[wordLen].Count); if (idx >= 0) and (idx < FWordsLOL[wordLen].Count) then lbWords.ItemIndex := lbWords.Items.Add(Format('%s (%d ms)', [FWordsLOL[wordLen][idx], time.ElapsedMilliseconds])) else lbWords.ItemIndex := lbWords.Items.Add(Format('No such word (%d ms)', [time.ElapsedMilliseconds])); 코드가 지나치게 많이 실행되는 것을 방지하기 (일반적인 GUI 프로그램에서는) 초당 수천번씩 화면을 업데이트하지 말자. 루프 앞뒤에 BeginUpdate와 EndUpdate 호출하여 윈도우와 메시지 교환을 최소화하자. (멀티 쓰레드 사용 시) 초당 수백만번 씩 운영체제에 메시지를 보내지 말자. 예제: 2GB 파일을 1KB 단위로 읽고, 진행률을 표시하는 프로그램을 개선해보자 개선 전(흔한 코드): “화면에 진행율을 표현하는 코드”가 성능을 크게 떨어뜨리고 있었다. ProgressBar1.Max := CFileSize; ProgressBar1.Position := 0; total := 0; while total < CFileSize do begin block := CFileSize - total; if block > 1024 then block := 1024; … // 해당 1KB짜리 'block’을 읽는 코드 Inc(total, block); ProgressBar1.Position := total; ProgressBar1.Update; //문제 지점!!!: 2백만번이나 화면을 업데이트하고 있었다 (여기를 주석처리하고 실행하면 훨씬 빨라진다) end; 개선 후(화면 업데이트를 필요한 만큼만 실행): “화면에 진행율을 표현하는 코드”는 100번만 실행하면 충분하다. ProgressBar1.Max := 100; ProgressBar1.Position := 0; lastPct := 0; total := 0; while total < CFileSize do begin block := CFileSize - total; if block > 1024 then block := 1024; … // 해당 1KB짜리 'block’을 읽는 코드 Inc(total, block); // 문제 지점을 해소하자. currPct := Round(total / CFileSize * 100); //백분율을 미리 계산하고 if currPct > lastPct then //1% 단위로만 (총 100번) 화면 업데이트 begin lastPct := currPct; ProgressBar1.Position := currPct; ProgressBar1.Update; end; end; 예제: TListBox와 TMemo에 각각 항목을 10,000 개씩 넣는 프로그램을 개선해보자. UI 컨트롤에 항목을 하나씩 추가한다는 것은 그만큼 프로그램과 윈도우 운영체제가 서로 메시지를 주고 받는다는 의미이다. 그 횟수를 줄이자. 코드 개선 전/후의 소요 시간 비교표 코드 (컨트롤에 10,000개 항목 추가) TListBox 실행 시간 TMemo 실행 시간 일반적인 코드 루프 안에서 컨트롤에 항목을 추가 7초 28초 개선1 해당 루프 앞뒤에 BeginUpdate와 EndUpdate 호출 0.5초 3초 개선2-TMemo 해당 루프 안에서는 화면을 사용하지 않는 TStringList에 항목을 여기에 넣고 루프 밖에서 TStringList를 컨트롤에 바인딩하여 모든 항목을 한번에 넣기 (해당 없음) 0.2초 개선2-TListBox 가상 리스트 박스 사용 (이것은 불필요한 코드 실행 없애기에서 따로 설명) 0.003초 (해당 없음) 개선 전: 10,000 항목 x 2개 컨트롤 = 20,000번 윈도우와 메시지 교환 for i := 1 to CNumLines do Memo1.Lines.Add('Line ' + IntToStr(i)); for i := 1 to CNumLines do ListBox1.Items.Add('Line ' + IntToStr(i)); 개선 1: 루프 앞뒤에 BeginUpdate와 EndUpdate 호출하여 윈도우와 메시지 교환을 2번으로 줄인다. 델파이는 (뭔가 변경을 시작할께요 라는 표시인) BeginUpdate 코드를 만나면 윈도우 운영체제에 메시지를 보내는 작업을 중단하고, (프로그램이 변경이 다 끝났어요 라는 표시인) EndUpdate를 만나면) 윈도우에 메시지를 보낸다. // [BeginUpdate, EndUpdate만 사용해도 성능이 개선된다] // TMemo는 (10,000 개 항목인 경우 기존 28초에서 3초로 속도가 향상됨) Memo1.Lines.BeginUpdate; for i := 1 to CNumLines do Memo1.Lines.Add('Line ' + IntToStr(i)); Memo1.Lines.EndUpdate; // TListBox는 (10,000 개 항목인 경우 기존 7초에서 0.5초로 속도가 향상됨) ListBox1.Items.BeginUpdate; for i := 1 to CNumLines do ListBox1.Items.Add('Line ' + IntToStr(i)); ListBox1.Items.EndUpdate; 개선 2.1-TMemo: 해당 루프 안에서는 항목을 화면을 사용하지 않는 오브젝트인 TStringList에 넣고, 루프 밖에서 TStringList를 TMemo에 바인딩하여 모든 항목을 한번에 넣기 (비디오 34분 45초부터 보기) // 루프 안에서는 TStringList에 항목을 넣는다. sl := TStringList.Create; for i := 1 to CNumLines do sl.Add('Line ' + IntToStr(i)); Memo1.Text := sl.Text; FreeAndNil(sl); // TMemo에는 루프 밖에서 한번에 넣는다. 개선 2.2-TListBox를 가상 모드로 사용하여 필요할 때 데이터를 추가하기 리스트박스에 항목이 10,000 개 있어도, 실제로 화면에서 한번에 표시되는 항목은 18개이다. 다른 항목을 보려면 스크롤해야 보인다. 따라서, 그렇다면 스크롤에 맞는 항목만 즉시 제공할 수 있다면, 10,000개 항목을 미리 가지고 있는 것과 사용자에게는 다를 바가 없다. 그렇게 한 결과, 같은 기능을 가진 리스트박스가 0.007초 만에 실행됨 가상 리스트박스 구현 방법 TListbox 설정 변경 프로퍼티: Style 프로퍼티를 lbVirtual로 변경 (기본값은 lbStandard) 이벤트: OnData, OnDataFind, OnDataObject 구현 OnData: 리스트박스에 값이 제공되는 이벤트 OnDataFind: Listbox.Items.IndexOf 함수가 사용될 때 호출되는 이벤트 OnDataObject: 리스트박스에 오브젝트가 들어가 있는 경우 사용한다. (지금은 생략) TListbox를 준비하는 코드와 OnData와 OnDataFind 이벤트를 처리하는 코드 // 10,000개 항목은 TStringList에만 넣어둔다. FList.Capacity := CNumLines; for i := 1 to CNumLines do FList.Add('Line ' + IntToStr(i)); //리스트박스의 항목 갯수는 전체 항목수인 10,000으로 지정한다. ListBox1.Count := CNumLines; //리스트박스는 항목을 표시하려고 할 때, 리스트박스는 OnData 이벤트가 발생하고 표시할 항목의 인덱스 번호를 전달한다. procedure TfrmVirtualListBox.ListBox1Data(Control: TWinControl; Index: Integer; var Data: string); begin Data := FList[Index]; //TStringList의 해당 인덱스에 해당하는 문자열을 반환하여 ListBox에 표현도록 한다. // 실제 호출되는 인덱스를 로그로 남겨서 확인해보자 // 처음에는 18개 항목만, 스크롤 하면 그에 맞는 항목만 가져오는 것을 알 수 있다. //OutputDebugString(PChar(Index.ToString)); //일단 주석처리 함 end; // DataFind 즉, (TListbox).Items.IndexOf([찾고자 하는 값]) 코드가 사용되는 경우를 대비한다. // 리스트박스에는 실제로 항목이 들어있지 않다는 점을 명심하자 function TfrmVirtualListBox.ListBox1DataFind(Control: TWinControl; FindString: string): Integer; begin Result := FList.IndexOf(FindString); //데이터를 실제 가지고 있는 곳인 TStringList의 IndexOf 결과를 반환한다. end; 팁! 이처럼 성능이 문제가 있을 때, 먼저 문제되는 코드를 아예 사용하지 않을 방법이 있는가를 생각해 보자. (이 예제에서는 10,000개 항목을 리스트박스에 넣는 부분을 아예 없애버렸다) 불필요한 코드 실행 없애기 예제: (캐싱) 지역변수를 활용한 캐싱 누구나 알고 있는데도, 실제로는 의외로 많은 코드에서 이런 부분이 간과되어 있는 방식이다. 특히, 값을 계산하거나 가져오는 시간이 오래 걸리는 것일 수록 지역변수를 활용한 캐시 효과는 크다. 앞서 사용한 FindGoodWord 함수에서 사용자가 지정한 단어의 글자수는 코드에서 여러번 사용된다. 개선 전: 글자 수를 매번 TSpinEdit에서 읽어온다 (그만큼 윈도우와 메시지를 교환하게 된다). 개선 후: 대신 한번만 읽고 WordLen이라는 지역변수에 넣고, 코드의 나머지 부분에서는 이 지역 변수를 사용하자. 예제: 피노나치 수열 계산 프로그램 개선하기 개선 전 (기존 코드): 피보나치 수열을 계산 수학식을 그대로 코드에 넣으면 심각한 성능 문제가 발생 피보나치 수열 계산 원리는 재귀 호출이다. 하지만, 프로그래밍에서 그대로 재귀 호출로 구현하는 것은 비효율적이다. 재귀 호출로 인한 성능 문제를 해소하기 위해서는 알고리즘을 개선하는 것이 좋다. // 문제점: (1) 재귀 호출이 되고, (2) 동일한 연산도 여러번 반복된다. function TfrmFibonacci.FibonacciRecursive(element: int64): int64; if element < 3 then Result := 1 else Result := FibonacciRecursive(element - 1) + FibonacciRecursive(element - 2); end; 개선1(캐싱): 재귀 호출을 하되, 적어도 동일한 연산은 반복하지 않도록 한번 연산된 것은 결과를 캐시(저장)해 두었다가 재사용 하자. (비디오 52분 17초부터 보기) 더좋은 알고리즘이 떠오르지 않을 때는 이처럼 캐싱을 검토해보는 것이 좋다. function TfrmFibonacci.FibonacciRecursiveMemorized(element: int64): int64; if FFibonicciTable[element] >= 0 then Result = FFibonicciTable[element] else begin if element < 3 then Result := 1 else Result := FibonacciRecursiveMemorized(element - 1) + FibonacciRecursiveMemorized(element - 2); FFibonicciTable[element+1] := Result; end; 개선2: 재귀 호출 알고리즘을 순차적 반복 호출 알고리즘으로 바꾸어서 해소 (비디오 54분 01초부터 보기) 재귀 호출은 대체로 순차적 반복 호출로 대체할 수 있다. 재귀 호출 시 같은 계산을 반복하지 않으면. 기하급수적인 복잡도 증가가 아니라, 순차적인 복잡도 증가 즉 O(1)이 된다. function TfrmFibonacci.FibonacciIterative(element: int64): int64; var a, b: int64; begin a := 1; b := 0; repeat if element = 1 then Exit(a); b := b + a; if element = 2 then Exit(b); a := a + b; Dec(element, 2); until false; end; GbLists.pas: O(1)을 구현한 캐싱 딕셔너리이며 사용하기도 쉽다: https://github.com/gabr42/GpDelphiUnits 에 있다. 알고리즘 향상에 관심이 있는 개발자에게 권장하는 자료 도서 Julian Bucknall, Algorithms and Data Structures PrimožGabrijelčič, DelphiHighPerformance RobertSedgewick&KevinWayne, Algorithms 웹 Algorithms, 4th Edition, https://algs4.cs.princeton.edu/home/ Udacity,edX,Udemy,Coursera... https:www.geeksforgeeks.org/data-structures/ https:www.geeksforgeeks.org/fundamentals-of-algorithms 포럼 Delphi-PRAXIS: en.delphipraxis.net << DelphiCon 2020 목록으로 이동
  10. << DelphiCon 2020 목록으로 이동 DelphiCon 의 2020 시리즈 중, Code Faster in Delphi - Alister Christie 의 한글 요약본입니다. 델파이 코딩 속도를 높여주는 신간 ‘Code Faster in Delphi’ 에 수록된 유용한 팁 중 몇가지를 데모와 함께 알려줍니다. 발표자 (Alister Christie)는 델파이를 배우려는 개발자에게 도움이 되는 LearnDelphi.tv를 운영하고 있습니다. 원본 비디오 시청을 권합니다. Alister의 비디오는 학습하기 좋게 편집되어서 제공됩니다. 이 글의 유용한 단축키가 손에 익고 나면, 더 많은 것도 활용해 보세요: 델파이와 RAD 스튜디오에서 활용할 수 있는 더 많은 단축키가 정리된 파일 다운로드 목차 코드 에디터 단축키 폼 디자이너에서 빠르게 작업하기 최신 RTL의 새 언어 기능을 이용하여, 오래된 코드를 현대화하기 정규식을 활용하여 웹페이지에 있는 모든 이미지의 url을 뽑아서 델파이로 가져오기 코드 에디터 단축키 (비디오 보기) 아래 단축키 중 혹시 사용하고 있지 않는 것이 있는 지 보고 이제부터라도 사용해보자. F12 (폼 디자이너와 코드 에디터 전환): 매주 자주하는 작업이다. 즉, 단축키를 쓰면 하루에도 엄청난 시간이 절약된다. Ctrl+Space와 방향키 (키워드를 빠르고 정확하게 입력하기): 키워드 앞글자 몇 자만 타이핑하고 Ctrl+Space를 사용하면 해당 키워드 목록이 표시된다. 이때, 방향키를 이용하여 원하는 키워드를 선택하면 자동 완성된다. Ctrl+왼쪽 방향키 (현재 줄에서 코드의 가장 앞쪽으로 커서를 이동) Ctrl+Shift+v (지역 변수 선언): 일단 변수명 부터 타이핑한다. 그리고 나서 Ctrl+Shift+v를 사용하면, 변수 선언을 바로 할 수 있다. Ctrl+y (현재 코드 줄을 한번에 삭제): 커서가 있는 곳의 코드 한 줄이 삭제된다. Ctrl+j (for 루프 코드를 빠르게 작성): 일단 for를 타이핑한다. 그리고 나서 Ctrl+j를 사용한다. Ctrl+Shift+c (구현부 코드 골격 자동 생성): 프로시저, 함수, 프로퍼티를 새로 만들 때는, 일단 선언부를 작성하고, Ctrl+Shift+c를 사용하면, 구현부 코드 골격이 자동 생성된다. 프로퍼티의 경우 선언된 Field와 Setter도 자동 생성된다. Ctrl+Shift+Alt+p (매개변수 동기화): 프로시저, 함수, 프로퍼티에서 매개 변수를 선언부( 또는 구현부에서) 변경하고, 그 자리에서 Ctrl+Shift+Alt+p를 사용하면 대응되는 구현부 (또는 선언부)의 매개 변수에도 똑 같이 반영되어 일치하게 된다. Ctrl+Shift+위/아래 방향키 (프로시저나 함수의 구현부와 선언부를 왔다갔다 하기) Ctrl+프로시저(또는 함수) 이름 클릭 (해당 프로시저나 함수의 내용으로 이동) Alt+좌/우 방향키 (뒤로 가기와 앞으로 가기: 이전 작업 위치로 가기 또는 다시 원래 위치로 되돌아 오기) Ctrl+Shif+1˜9 (코드에 북마크를 달기): 커서가 있는 줄에 해당 숫자가 지정된 북마크가 생긴다. 원하는 북마크로 이동하려면, Ctrl+1˜9를 사용한다. 참고: 겟잇 패키지 매니저에서 "bookmark"를 타이핑하고 검색하면, 보다 향상된 북마크를 설치할 수도 있다. 화면에서 코드를 한줄이라도 더 볼 수 있다: 단축키에 익숙해지면 툴바에서 해당 단축 아이콘 빼자. 툴바가 차지하는 영역을 줄일 수 있다. 예를 들어, 뒤로 가기와 앞으로 가기 아이콘을 드래그하여 툴바 바깥으로 꺼내면 툴바에서 없어진다 자주 쓰는 단축 아이콘으로 툴바를 구성하려면, 툴바에서 오른쪽 클릭 > Customize Ctrl+Shift+좌/우 방향키(코드 블록 지정) 후 시작하는 괄호 (선택된 곳의 앞뒤를 괄호로 감싸기): 대/중/소 괄호 모두 적용된다. 참고: 블록 선택 팁: And/Or 로 연결된 곳에서 Ctrl+w를 사용하면 선택되는 코드 블록이 한단위씩 늘어난다. Ctrl+/ (주석 처리): 해당 줄 앞에 //가 추가되어 주석으로 처리된다. (이미 //가 있는 주석에서 사용하면, //가 제거되어, 일반 코드가 된다) 여러 줄을 한번에 주석으로 바꾸려면, 해당 줄을 모두 선택한 상태에서 { 를 사용하여 감싸도 된다. 하지만, 각 줄 앞에 // 두는 주석 처리 방식이 더 일반적이다 Ctrl+q+b (선택 영역의 가장 앞으로 커서 이동) 후 Shift+위방향키 (앞에 있는 코드도 선택 영역에 넣기) Ctrl+q+k (선택 영역의 가장 뒤로 커서 이동) 후 Shift+아래 방향키 (뒤에 있는 코드도 선택 영역에 넣기) Shift+위/아래 방향키 (각 줄의 처음부터 끝까지를 모두 선택) Shift+Alt+위/아래 방향키 (각 줄에서 한칸만 선택): 예를 들어 모든 줄에서 한자씩 왼쪽으로 당기고 싶으며, 이 방법으로 선택하고 Delete키를 누른다 Ctrl+o+k (시작점에서 커서까지 사각형으로 코드 영역을 선택하기와 해당 줄 전체 선택하기를 선택하는 토글): 사각형으로 선택하기로 설정된 경에서는 Shift+위/아래/좌/우 방향키를 사용하여 코드의 일부 영역을 사각형으로 선택할 수 있다. 여러줄을 선택하고 Tab 또는 Shift+Tab (여러줄을 한번에 들여쓰기 또는 내어쓰기) Ctrl+Shift+I (또는 U)도 같은 기능 Ctrl+D (uses 절에 나열된 모든 유닛들을 한줄에 하나씩 배치) 주의! 이 단축키를 사용하려면, 먼저 Tools > Options > Language > Formatter > Delphi > Line Breaks 에서 Line Breaks in Uses clauses를 Yes로 설정해야 한다. 만약, Uses 절의 모든 유닛이 아니라, 일부 유닛 만 한 줄에 하나씩 배치하려면, 원하는 코드 영역을 선택한 후에 Ctrl+D를 사용한다. 폼 디자이너에서 빠르게 작업하기 (비디오 12분 15초부터 보기) TMemo를 폼에 추가할 때 바로 크기를 설정하기 툴 팔레트에서 TMemo를 클릭한다. 그리고 나서, 폼 위에서 TMemo가 위치할 왼쪽 위 지점에서 마우스 왼쪽 버튼을 누르고 그 상태에서 드래그하여 원하는 크기가 될 때까지 끌고 간 후에 마우스 버튼을 놓는다. TEdit 여러개를 폼에 한번에 추가하기 툴 팔레트에서 Shift+원하는 컴포넌트(TEdit)를 클릭하여 툴 팔레트에서 TEdit가 강조 표시되도록 한다. 그리고 나서, 폼 위에 원하는 갯수 만큼 마우스 왼쪽 버튼을 클릭한다. (툴 팔레트에 강조 표시된 컴포넌트를 풀려면, ESC를 클릭하거나 툴 팔레트에서 다른 컴포넌트를 선택한다.) 툴 팔레트 검색창에서 원하는 컨트롤을 타이핑하고 엔터키를 쳐서 바로 폼 위에 추가하기 툴 팔레트에서 Ctrl+Alt+p 설정을 한다 (툴 팔레트 > 오른쪽 클릭 > Properties > Persistent Search Filter 옵션을 활성화 하는 것과 같은 효과) TEdit를 폼 위에 추가하면, 해당 오브젝트 명이 Text 프로퍼티에 자동으로 들어간다. 이것을 지우기 해당 TEdit에서 오른쪽 클릭 > Clear Text (오브젝트 인스펙터에서 Text 프로퍼티를 찾아서 지우는 것과 같은 효과) 컨트롤의 Name, Text, Align 등을 변경하기 해당 컨트롤에서 오른쪽 클릭 > Quick Edit 뒤쪽에 가려져서 마우스로 선택하기 힘든 컨트롤의 위치/크기를 변경하기 스트럭처 창을 이용하여 해당 컨트롤을 선택 Ctrl+방향키 (위치 변경) 또는 Shift+방향키 (크기 변경) 팁! 덮고 있는 컨트롤에서 마우스 왼쪽 버튼을 누른 상태에서 ESC키를 사용한다 (그러면, 아래에 가려진 컨트롤이 선택된다). 그 상태에서 마우스를 드래그하여 뒤에 있는 컨트롤을 이동할 수 있다. 컨트롤 여러개의 위치를 한번에 정렬하기 스트럭처 창 또는 폼 에서 Shift+클릭으로 여러 컨트롤을 동시 선택하고 나서, 기준이 될 컨트롤을 마지막으로 한번 더 클릭한다 (기준 컨트롤만 모서리가 검게 표시되고, 나머지는 모소리가 회색이다). 그 상태에서, 오른쪽 클릭 > Position > Align에서 원하는 정렬을 선택한다. 컨트롤을 움직이지 않고 정렬션을 보기 컨트롤을 마우스 왼쪽 버튼으로 누른 채로 잠깐 기다린다. (또는 Shift키를 누른다) 참고로, VCL 폼 디자이너의 정렬선은,파랑색이 컨트롤 테두리 기준선이고, 핑크색은 컨트롤 안의 텍스트 기준선이다. 컨트롤을 다른 컨트롤 안에 넣기 스트럭처 창에서 컨트롤을 드래그하여 컨테이너가 될 컨트롤 안에 끌어 넣는다. 또는, 폼 위에서 컨트롤을 Ctrl+x하고 컨테이너가 될 컨트롤을 마우스로 선택한 후 Ctrl+v를 한다. TSpeedButton을 사용해야하는데 "실수로" TButton을 사용했다면 폼 화면에서 Alt+F12를 사용하여 폼 코드로 이동한다. (폼 디자니어 > 오른쪽 클릭 > View As Text 와 같은 효과) 폼 코드에서 해당 컨트롤 이름을 검색한 후, "TButton"을 "TSpeedButton"으로 변경한다. 경고 창이 나타나서 SpeedButton 은 Tab Order 프로퍼티가 없다고 해도 Ignore 버튼을 클릭한다. 오브젝트 인스펙터를 보면 TButton이 TSpeedButton으로 변경되었을 것이다. 하지만, 소스 코드에는 여전히 TButton으로 남아 있다. 따라서 컴파일을 시도한다. 그러면, 오류 창이 뜨면서 바꿀 것인지를 묻는 창이 뜬다. 이때, Yes를 클릭하면 소스 코드 역시 TSpeedButton으로 변경되고 컴파일이 진행된다. 이미 설정한 컨트롤을 기준으로 비슷한 것들을 추가하기 폼 디자이너에서 원하는 컨트롤을 선택하고 Ctrl+c 한 후에 메모장에서 Ctrl+v를 하면 해당 컨트롤을 구현하는 코드가 메모장에 복사되어 붙는다. (예를 들어, TButton을 TSpeedButton으로 변경하거나, Caption 프로퍼티의 값을 변경하는 등) 메모장에서 원하는 대로 코드를 바꾸고 나서, Ctrl+c 한다. 다시 폼 위에서 Ctrl+v를 하면 메모장에서 변경한 코드 대로 컨트롤이 추가된다. Ctrl+v를 3번 하면 해당 컨트롤 3개가 추가되므로,컨트롤 여러개를 추가할 때도 유용하다. 컨트롤 여러개에 공통으로 있는 프로퍼티를 한번에 변경하기 폼 디자이너에서 여러 컨트롤을 선택한 후 오브젝트 인스펙터의 검색창에서 원하는 프로퍼티를 타이핑하면, 해당하는 프로퍼티만 창에 표시되므로 해당는 프로퍼티의 값을 한번에 변경한다. 프로퍼티가 여러개 나타나는 경우, 화살표를 이용하여 원하는 프로퍼티를 선택한다. 최신 RTL의 새 언어 기능을 이용하여, 오래된 코드를 현대화하기 (비디오 20분 27초부터 보기) 오래전 부터 사용하던 아래의 코드를 보다 간결 명확한 현대식 코드로 바꿔보자. // 기존에 있던 코드 procedure TForm1.btnGetFileClick(Sender: Object); var F: TSearchRec; begin if FindFirst(‘c:\temp\*.*’, faAnyFile - faDirectory, F) = 0 then repeat Listbox1.Items.Add(F.Name); until FindNext(F) <> 0; end; 1단계: (TDirectory, TPath 등) System.IOUtils 유닛 을 사용하자. 보다 읽기 쉬운 코드가 된다 // [변경 1단계] IOUtils 유닛을 사용 // 개선된 점: 코드를 이해하기가 더 쉬워졌다 (더 짧아지지는 않았지만, 훨씬 읽기 쉬워졌다) uses System.IOUtils; procedure TForm1.btnGetFileClick(Sender: Object); var FilesNames: TArray; FilesName: string; begin FileNames: TArray := TDirectory.GetFiles(‘c:\temp’); for FileName in FilesNames do Listbox1.Items.Add(TPath.GetFileName(FileName)); end; 2단계: 인라인 변수를 사용하자. 현대식 문법이며, 읽기도 쉽고, 실행 효율성도 더 높다. // [변경 2단계] 인라인 변수를 사용 // 개선된 점1: 코드가 더 간결해졌다 // 개선된 점2: 인라인 변수는 해당 영역에서만 유효하다. // (코드에서 변수의 범위는 작을수록 더 좋다. 아래의 새 코드에서는 FileName 변수가 for 루프 안에서만 유효하므로, 바깥에서 사용하면 컴파일러 오류 발생) uses System.IOUtils; procedure TForm1.btnGetFileClick(Sender: Object); begin var FileNames: TArray<string> := TDirectory.GetFiles(‘c:\temp’); for var FileName: string in FilesNames do Listbox1.Items.Add(TPath.GetFileName(FileName)); end; 3단계: 타입 레퍼런스 기능(10.3버전에 추가됨)을 사용하자. 컴파일러가 이해할 수 있는 타입이라면, 이제 굳이 적지 않아도 된다. // [변경 3단계] 인라인 변수 선언 시 타입 지정 생략 // 개선된 점: 코드가 더 간결해졌다 uses System.IOUtils; procedure TForm1.btnGetFileClick(Sender: Object); begin var FileNames := TDirectory.GetFiles(‘c:\temp’); for var FileName in FilesNames do Listbox1.Items.Add(TPath.GetFileName(FileName)); end; 정규식을 활용하여 웹페이지에 있는 모든 이미지의 url을 뽑아서 델파이로 가져오기 (비디오 24분 30초부터 보기) 웹페이지에서 jpg 이미지 100개를 가져오는 작업을 해야하는 상황이라면? 각 이미지 경로를 웹브라우저에서 일일이 찾아서 메모장에 붙이는 수작업을 하지 말자. 아래와 같이 짧은 코드를 만들어서 TListBox에 표시하면, 훨씬 빠르고 정확히 가져올 수 있고, 다음에 또 쓸 수도 있다. // 아래의 버튼 클릭 이벤트 코드 // 단, 이 코드가 작동하려면, 먼저 NetHTTPRequest와 NetHTTPClient 컴포넌트를 폼 디자이너에 올리고 서로 연결을 해두어야 한다 uses System.RegularExpressions; procedure TForm1.Button1Click(Sender: Object); begin var Response := NetHTTPClient1.Get(‘….’); // ...에는 목표 웹페이지 주소 (예) https://www.trademe.co.nz/a/marketplace/gaming/nintendo-wii-u var WebText := Response.ContentAsString; var Matches := TRegEx.Matches(WebText, ‘https://[ˆ/\s]+/\S+?\.jpg'); for var Match in Matches do ListBox1.Items.Add(Match.Value); end; << DelphiCon 2020 목록으로 이동
  11. << DelphiCon 2020 목록으로 이동 DelphiCon 의 2020 시리즈 중, Leveraging High DPI in VCL Applications - Ray Konopka 의 한글 요약본입니다. 발표자 (Ray Konopka)는 CodeSite, Konopka Signature VCL로 유명한 Raize Software의 창업자이며, 월트 디즈니 어트렉션 테크놀러지의 수석 소프트웨어 엔지니어로서, 디즈니리조트, 디즈니랜드 등의 소프트웨어도 개발합니다. 도서 “델파이 컴포넌트 개발하기(Developing Custom Delphi Component)” 저자이기도 합니다. 델파이는 델파이로 만듭니다. 델파이는 11.0 버전에서 IDE 자체에 HighDPI를 적용하였습니다. 그 결과, 델파이 사용자들이 더 쾌적하게 개발할 수 있게 되었습니다. (모니터가 4K 이상이라면 더욱 효과가 큽니다) 여러분의 앱에서도 델파이 11.0과 같이 서로 다른 다중 모니터 특히 최근에 부쩍 많아진 High DPI 모니터 지원할 수 있습니다. 그 방법을 이 세션에서 설명합니다. 원본 비디오(YouTube) 보기 (54 min)를 권장합니다. 발표 자료도 참고하세요. 목차 High DPI 관련 핵심 개념 이제는 개발자가 '단 하나의 DPI' 를 기준으로 잡지 못할 만큼 모니터의 DPI 너무 다양해졌다. 개발자가 '단 하나의 DPI' 를 기준으로 정하기가 어려운 이유는? (데모) 윈도우 메모장을 통해 다중 모니터의 표현 이해하기 애플은 High DPI 처리가 단순 명확하다. 윈도우는 High DPI를 다루기가 어렵다. 윈도우에서 제공하는 DPI 확대/축소 개발한 윈도우용 앱이 High DPI 를 잘 지원하기 위해서, 개발자가 관여 해야 한다. 델파이의 DPI Awareness 옵션 5가지 (2020.11 현재) Unaware (DPI 인식 안함) System aware (시스템 DPI 반영) Per Monitor (모니터 별 DPI 반영) GDI Scaling (GDI 확대/축소) Per Monitor V2 (모니터 별 DPI 반영 버전 2) (데모) 델파이 프로젝트에서 지정한 DPI Awareness 옵션 별 결과 비교 Unaware System aware Per Monitor GDI Scaling Per Monitor V2 TImageCollection TVirtualImageList (데모) Per Monitor V2 옵션에서 TImageCollection와 TVirtualImageList를 사용하기 Per Monitor V2 옵션에서 UI 콘트롤을 동적으로 생성할 때 알맞게 표현하기 위한 VCL 함수들 (데모) Per Monitor V2 옵션에서, UI 콘트롤을 동적으로 생성할 때 알맞게 표현하기 High DPI 관련 핵심 개념 DPI (Dots Per Inch): 1인치에 들어가는 점(Dot)의 갯수 (역자 주: 점은 LCD모니터, 프린터 등 출력 장치의 물리적인 최소 표현 단위) PPI (Pixels Per Inch): 1인치에 들어가는 픽셀(Pixel)의 갯수 (역자 주: 픽셀은 Pics+Element의 복합어이며, 디지털 이미지를 만들 때 사용되는 논리적인 최소 표현 단위) 아주 오래전부터 “윈도우에서 표준 DPI는 96이다”라고 간주하면 안전했다. 그래픽의 사실 상 표준이었던 픽셀 기반은 VGA 부터 sVGA, XGA…로 이어져 오면서 지켜져왔다. DPI와 해상도(Resolution)가 서로 관련은 있지만, 항상 일치하는 것은 아니다. 지금은 더욱 그렇다. 예전에는, 대체로 차이가 거의 없었다. 얼마전까지는 대체로 모니터가 커지고 해상도가 높아져도, 이때에도 '점'의 크기 즉 DPI는 대체로 그대로 유지되면서 크기만 커졌다. 따라서 , 모니터에서 해상도가 커지는 비율 만큼 '점'의 갯수도 같은 비율로 많아지게 된다. 하지만 최근에는, High DPI 장비가 많아졌다 이제는 개발자가 '단 하나의 DPI' 를 기준으로 잡지 못할 만큼 모니터의 DPI 너무 다양해졌다. 화면 배율 (Display Scale Factor) 예전에는, 96 DPI 가 거의 모든 모니터에서 사실 상 표준으로 지켜졌으므로, 1024, 1440 등 해상도가 커져도 UI 작성에 영향이 없었다. 동일하게 96 DPI 인 경우,해상도가 커지면, 활용할 수 있는 면적이 그만큼 커진다는 점만 고려하면 충분했다. 16 x 16 아이콘을 어느 해상도에서 사용해도 큰 문제는 없었다. 이제는, High DPI 화면인 경우, 화면 요소들이 잘 보이도록하는 조치가 필요하다. 예: 226 DPI인 새 노트북에서는 16 x 16 아이콘은 실제 크기가 3mm이다. 너무 작아서 거의 보이지도 않는다. 스크린 해상도 DPI 해당 물리적 화면 크기 1024 x 769 96 10.7” x 8" 1440 x 900 96 15” x 9”.375 2560 x 1440 109 23.4” x 13.2” 3074 x 1920 226 (훨씬 높다) 13.6” x 8.”5 (얼마전에 구입한 노트북) 표1. 얼마 전에 구입한 노트북이 경우, 물리적인 화면의 크기는 더 작은데 DPI는 훨씬 높다. 개발자가 '단 하나의 DPI' 를 기준으로 정하기가 어려운 이유는? 여러 모니터를 동시에 사용하고, 각 모니터의 해상도가 다양해졌다. 사용자가 사용 중인 앱을 드래그하여 DPI가 다른 모니터로 옮기기도 한다. 이제는 다중 모니터 사용에 거의 제한이 없다. (오래 전의 윈도우는 미리 정의된 해상도에 맞는 모니터만 연결할 수 있었다) 사용자가 노트북이나 태블릿에 외부 모니터를 연결/해제하는 경우가 많다. 사용자가 원격 데스트탑을 통해 앱을 사용하기도 한다. 사용자가 컴퓨터 사용 중에 화면 배율(Display Scale Factor)을 변경하기도 한다. (데모) 윈도우 메모장을 통해 다중 모니터의 표현 이해하기 (비디오 7분 30초부터 보기) 윈도우 메모장의 250% 배율 화면: 메인 메뉴 사이의 간격이 매우 좁다. 윈도우 메모장의 일반 배율(100%) 화면 DPI, 해상도, 배율이 다른 모니터를 모두 잘 지원하기는 쉽지 않다. 윈도우 메모장을 보면, High DPI를 전반적으로 무난하게 지원하고 있다. 앱은 배율에 맞게 확대되면서도, 화면이 선명하고, 글자 폰트도 선명하다. 하지만, High DPI에서 여백도 다르고, 본문의 문장의 길이도 다르다. 무엇보다 메인 메뉴 간의 간격이 좁아져서 사용하기에 살짝 불편하다. High DPI에서 윈도우 앱은 "폰트" 확대/축소도 완벽하게 지원하기가 힘들다. "이미지"는 더욱 다루기가 어렵다. 애플은 High DPI 처리가 단순 명확하다. 애플은 모든 화면 요소에서 물리적 표현 단위인 "포인트"를 사용한다. 맥 OS와 iOS에서 PPI는 Point Per Inch를 의미한다. (Pixel Per Inch가 아니다) 애플의 표준은 1인치 당 72 포인트이고, 잘 지켜지고 있다. 따라서, 모니터에 맞게 확대/축소 시, 단순히 배율을 적용한다. 역사적 배경 그래픽 인쇄 분야에서는 DPI (인치당 표현되는 물리적인 점)이 중요하다. 애플은 오래 전부터 이 분야에서 독보적이었다. 윈도우는 High DPI를 다루기가 어렵다. (비디오 11분 47초부터 보기) 마이크로소프트는 애플과 달리 논리적인 단위인 "픽셀"을 사용한다. Dialog Unit (DLU) 마이크로소프트가 고안한 단위로써, 여러 디스플레이 장비를 다룰 수 있도록 윈도우를 설계하는 과정에 생겨났다. 1 Dialog Unit = 윈도우 시스템 폰트 너비 평균의 1/4 Dialog Unit는 논리적인 단위이므로, 물리적인 장비에 따라 달라진다. (장비 의존성이 없고, 물리적 면적에 맵핑되지 않는다) 수직 Dialog Unit와 수평 Dialog Unit가 일치하지 않을 수도 있다. 문제는 지금은 아무도 더이상 Dialog Unit을 사용하지 않는다는 점이다. 마이크로소프트의 개발도구 역시 마찬가지로, 닷넷, 윈폼, XAML 에서도 Dialog Unit을 사용하지 않는다. 요즘 개발도구는 대부분 픽셀(Pixel)을 사용한다. (마이크로소프트 개발도구도 마찬가지이다.) 픽셀은 논리적 단위이며, 물리적 크기가 고정되어있지 않다. 예전에는 거의 모든 모니터가 96 DPI 를 사용했다. 물리적인 단위가 변하지 않으므로, 그것에 기반한 논리적인 단위인 픽셀 역시 실제로 물리적 크기가 고정된 것과 같았다. 이제는 댜양한 DPI가 사용된다. High DPI인 4K 모니터와 일반 모니터는 물리적인 단위인 DPI가 다르기 때문에 그것에 기반한 픽셀의 물리적인 실제 크기 역시 서로 다르다. 윈도우에서는 논리적 크기인 픽셀과 화면의 물리적인 실제 크기를 연결할 방법이 있어야만, High DPI를 다룰 수 있다. 윈도우에서 제공하는 DPI 확대/축소 윈도우는 우리가 아무 조치를 하지 않아도 High DPI 화면에 알아서 표현한다. 윈도우 메모장 데모에서 본 것 처럼 꽤 괜찮지만 여전히 완벽하지는 않다. 개발한 윈도우용 앱이 High DPI 를 잘 지원하기 위해서, 개발자가 관여 해야 한다. 픽셀 크기의 기준을 정하고, 윈도우10 에서 제공하는 DPI 배율 확대/축소(Scaling) 기능을 활용하자. 윈도우는 최신 버전으로 오면서 High DPI 지원이 점점 좋아졌고, 윈도우10에서 상당히 향상되었다. 개발자는 DPI 인식 방식을 각 앱 별로 매니페스트(Manifest)에 기록하여 윈도우에게 알려줄 수 있다. 델파이의 DPI Awareness 옵션 5가지 (2020.11 현재) (비디오 18분 17초부터 보기) 위치: 델파이에서 Project > Options > Application > Manifest > DPI Awareness 옵션 Unaware (DPI 인식 안함) 방식: 윈도우 비트맵은 가장 일반적인 96 dpi를 기준으로 각 모니터에 맞게 확대/축소하여 렌더링한다. 장점 모든 화면 요소를 단순하게 확대/축소하므로, 요소의 위치나 크기가 잘못되지 않는다. 단점 화면이 선명하지 않다. 특히 해상도 낮은 이미지를 High DPI 모니터에서 보면 픽셀이 그대로 드러난다. 의견: 무난한 선택이지만, 보기에 좋지 않기 때문에 최고의 선택은 아니다. (데모 보기) System aware (시스템 DPI 반영) 방식:사용자가 윈도우를 시작할 때 로그인한 모니터의 DPI를 사용하여 윈도우가 알아서 확대/축소하여 렌더링한다. 장점: (언급되지 않음) 단점 사용자가 윈도우를 시작한 주 모니터에 따라, 렌더링 기준이 달라진다. 사용자가 폼을 다른 모니터로 옮기거나 화면 배율(확대/축소)을 변경하면, 화면이 그다지 좋지 않다. 의견: 권장하기 어렵다. (데모 보기) Per Monitor (모니터 별 DPI 반영) 방식: DPI 변경을 최상위 창에 공지하여 DPI가 다른 모니터에서 앱을 따로 다룰 수 있도록 한다. 마이크로소프트가 윈도우8.1부터 채택했다 장점: 윈도우가 모든 것을 임의로 확대/축소하지 않게 되었다. 단점: 실제로는 잘 지원하지 못하고 화면이 틀어진다. (뒤에서 살펴보겠다) 의견: 특별한 이유가 없다면, 사용하지 않는 것이 좋다. (데모 보기) GDI Scaling (GDI 확대/축소) 델파이 10.4에서 VCL용으로 추가되었다. (10.3에서도 가능하긴 했지만 사용하기 불편했다) 윈도우 10 Creators 업데이트 (1703)에서 도입된 기술이 반영된 것이다. 방식: 폰트와 기본 도형 등 모든 것을 GDI 프레임워크가 알아서 DPI에 맞게 확대/축소한다. 디스플레이 배율값과 동일하게 오브젝트를 확대/축소한다. 모니터에 맞게, GDI가 알아서 알맞은 폰트를 사용한다. 원래 10pt인 폰트에 만약 @200% 텍스트가 적용되면, 렌더링할 때 GDI는 실제로 20pt 폰트를 사용한다. 장점 이미지을 제외한 모든 화면 요소를 GDI가 알아서 깔끔하게 표현한다. 개발자가 가장 쉽고 빠르게 High DPI를 잘 지원하는 앱을 만들 수 있다. 단점 개발자가 각 앱별로 별도의 매니페스트를 변경/배포해야 한다. 한 단어 안에서 글자 간격이 불규칙하고 글자가 너무 가깝게 겹칠 수도 있다. 폰트를 잘 선택하여 해결할 수도 있다. 아이콘 등 이미지는 단순히 늘어나거나 줄어들기 때문에 High DPI에서 깨끗하게 표현되지 못한다. 개발자가 관여할 여지가 전혀 없으므로, High DPI 이슈를 개발자가 해소하지 못한다. 의견: 이미지에 색상이나 곡선이 많지 않거나 위의 단점이 문제 되지 않는다면, 최고는 아니지만 대안이 될 수 있다. (데모 보기) Per Monitor V2 (모니터 별 DPI 반영 버전 2) 델파이 10.4부터 제공된다. (역자 주: 델파이 10.3 에도 이 옵션이 있었다) GDI Scaling과 마찬가지로, 윈도우10 Creators 업데이트 (1703) 이후 버전에서 작동한다. 방식 자동 스케일링을 통해 해당 DPI에 맞게 이미지를 제외하고, 모든 것이 확대/축소된다. 또한, DPI 변경을 최상위 창에 공지하여 DPI가 다른 모니터에서 앱을 따로 다룰 수 있도록 한다. 장점: 앱에서 DPI 변경을 인지할 수 있어서, 개발자가 미리 대응할 수 있다. 자동으로 테마가 그리는 윈도우 비트맵은 모두 DPI에 맞게 확대/축소 된다. 비클라이언트 (NonClient) 영역까지도 해당된다. 체크 박스, 라디오 버튼등 공통 콘트롤, 파일 찾기, 폰트 설정 등 윈도우 대화상자 모두가 해당된다. 의견: High DPI를 가장 잘 다룰 수 있는 선택이다. (데모 보기) (데모) 델파이 프로젝트에서 지정한 DPI Awareness 옵션 별 결과 비교 Unaware (비디오 25분 40초부터 보기) High DPI 모니터 실제로 High DPI를 활용하지는 못한다. 앱에서는 폼의 크기는 425 x 405로 인식하고, 폼 전체가 배율에 맞게 단순하게 늘어난다. 모든 요소가 제자리에 있고, 모든 것이 일반 모니터에서와 동일하게 작동한다. 하지만, 폼 전체의 표현이 깔끔하지 않고, 글자와 이미지 곡선에서는 픽셀이 드러난다. 일반 모니터 (96 DPI) ( Unaware 설명 보기) System aware (비디오 28분 15초부터 보기) High DPI 모니터 (사용자가 윈도우 로그인을 한 주 모니터) 아이콘 이미지가 매우 작다 (High DPI에서도 여전히 16x16 크기로 표현되기 때문) 폰트 등 화면 요소들이 선명하게 표현되지는 않는다. 폼 크기는 1055x1002로 High DPI를 활용한다. 일반 모니터 (96 DPI) 96 DPI 모니터에서도 폼 크기는 1055x1002이다. 주모니터인 High DPI를 기준으로 96 DPI와 차이만큼 그대로 축소된다. 그 결과, 모든 것이 크기도 작아지고 선명하지 않다. (System aware 설명 보기) Per Monitor (비디오 30분 01초부터 보기) High DPI 모니터 High DPI에서 폰트 등 화면 요소들이 훨씬 깔끔하게 표현된다. 아이콘 이미지가 매우 작다 (High DPI에서도 여전히 16x16 크기로 표현된다) 폼 크기는 1055x1002로 High DPI를 활용한다. 일반 모니터 (96 DPI) HighDPI에서 잘 보이던 요소들이 96 DPI에서는 크기와 위치가 가 알맞게 적용되지 않는다. 스크롤 바 크기가 훨씬 굵어졌다. 체크 박스와 라디오 버튼 윗부분이 잘렸다. 툴바의 아이콘 간격이 틀어졌다. 이유는, DPI 변경을 앱의 폼에게 전달하지 못하기 때문이다. 스크롤 바와 같은 윈도우 공통 콘트롤에서도 변경전의 DPI를 그대로 반영된다. (Per Monitor 설명 보기) GDI Scaling (비디오 32분 11초부터 보기) High DPI 모니터 개발자가 앱이 GDI Scaling을 사용하도록 별도로 매니페스트를 변경/배포 함으로써, 가장 쉽고 빠르게 GDI를 지원할 수 있으므로 최고는 아니지만 대안이 될 수 있다. 폼은 자신의 크기가 425x405인 줄 알고 있다. 즉 96 DPI 인 줄 안다. 하지만, GDI가 알아서 모든 화면 요소를 High DPI에서 선명하게 표현한다. 폰트, 메뉴간의 간격, 윈도우 콘트롤 등 (단, 이미지는 제외) 폰트는 배율에 맞게 더 큰 폰트가 적용되어서 깨끗하게 표현된다. 아이콘 이미지가 선명하지 않다. (여기서는 아이콘의 16x16 이미지를 단순히 늘렸다) 이미지에 색상이나 곡선이 많지 않은 앱이라면 보기 좋지 않을 수 있다. 일반 모니터 (96 DPI) GDI Scaling은 완벽하지 않지만 멀티 모니터를 대체로 잘 지원한다. (GDI Scaling 설명 보기) Per Monitor V2 (비디오 35분 04초 부터 시청) High DPI 모니터 델파이 10.4부터 제공되며, 가장 완벽하게 High DPI를 지원할 수 있는 옵션이다. High DPI에서 화면 요소가 선명하다는 GDI Scaling의 장점을 그대로 가진다. 폰트, 배치, 윈도우 콘트롤 (스크롤 바 등)이 선명하다. 아이콘 이미지가 매우 작다. (High DPI에서도 여전히 16x16 크기로 표현된다) 하지만, 앱이 DPI 변경을 인지할 수 있으므로 해결할 수 있다 (뒤 데모에서 설명) UI 콘트롤을 동적으로 생성할 때에는 제대로 표현되지 않는다. 하지만, 앱이 DPI 변경을 인지할 수 있으므로 해결할 수 있다 (뒤 데모에서 설명) 일반 모니터 (96 DPI) (Per Monitor V2 설명 보기) TImageCollection 서로 다른 해상도를 가진 이미지들을 공유하는 저장소 예: 아이콘 이미지를 16x16, 32x32 등 여러 해상도에 맞게 보관할 수 있다. 비시각적 컴포넌트이므로 어디든지 넣고 공유할 수 있다. 데이터 모듈이나 메인 폼 등 원하는 곳에 올려두고 쓸 수 있다. 명명 규칙을 맞추어서 이미지 이름을 정하면, 알맞은 이미지가 자동으로 로딩된다. Size in the file name 옵션이 선택되어 있는 지 확인할 것 (ImageCollection 에디터) 다음 3가지 구분자 중 한가지 사용 가능 (-, _, @) 파일명 예시: File-Open-16.png , Edit-Paste-48.png TVirtualImageList 해당 폼의 DPI를 파악하여 각 폼에서 알맞은 이미지를 TImageCollection에서 가져와서 사용하도록 한다. 각 폼마다 각자 TVirtualImageList를 가지고 있어야 한다. 데이터 모듈이나 메인폼에 올려두고 공유하여 다른 폼에서 사용할 수는 없다. 각 폼은 다른 모니터에서 표현될 수 있고, 모니터에 따라 DPI가 다르게 적용될 수 있기 때문이다. AutoFill 프로퍼티 AutoFill을 True로 지정하면, TImageCollection 안의 모든 이미지 중에서 알맞은 것을 TVirtualImageList로 가져온다. 만약, TVirtualImageList에서 TImageCollection 안의 이미지 중 몇가지만 사용하고 싶으면, AutoFill을 False로 지정하고, 원하는 이미지만 선택할 수 있다. (데모) Per Monitor V2 옵션에서 TImageCollection와 TVirtualImageList를 사용하기 (영문 비디오 42분 01초부터 보기) 한국어 더빙 비디오에서 이부분 부터 재생되는 비디오 Per Monitor V2 옵션에서 UI 콘트롤을 동적으로 생성할 때 알맞게 표현하기 위한 VCL 함수들 VCL의 새 함수(들) 단, (윈도우 10 최근 버전의 API를 활용하므로) 윈도우 10 최근 버전 이후에서 만 작동한다. Vcl.Controls.GetSystemMetricsForWindow() 윈도우에 새로 추가된 API인 GetSystemMetrics()와 GetSystemMetricsForDPI()를 호출한다. 이전 버전에서 호출을 시도해도 안전하다. TControl.GetSystemMetrics() 개발자가 편하게 사용하도록 VCL에서 TControl에 GetSystemMetrics가 추가되었다. (DPI 확인은 대체로 콘트롤에서 필요하기 때문이다) 폼 등 모든 TControl이 GetSystemMetricsForWindow 를 호출할 수 있다. 혹시, 이미 윈도우 API에서 직접 GetSystemMetrics를 사용한 코드가 있다면, WinAPI.Windows.GetSystemMetrics로 변경하여 기존 유닛을 그대로 사용해도 된다. TControl.CurrentPPI 프로퍼티 (데모) Per Monitor V2 옵션에서, UI 콘트롤을 동적으로 생성할 때 알맞게 표현하기 (영문 비디오 50분 49초부터 보기) 한국어 더빙 비디오에서 이부분 부터 재생되는 비디오 // 처음 한번 알맞은 크기와 위치에 동적으로 생성되고나면, DPI가 다른 화면에서 표현될 때에도 알맞게 표현된다. procedure TFrmMain.btnAddClick(Sender: TObject); var X, Y, W, H: Integer; begin B := TButton.Create( Self ); B.Parent := pnlPreview; // (변경 전, 10 pixel로 지정) // X := 10; // (변경 후) // VCL.Controls 유닛에 있는 TControl.CurrentPPI 프로퍼티를 사용하여 해당 PPI를 가져와서, // 96 DPI일 때 10 pixel을 기준으로 하고, MulDiv 함수를 사용하여 해당 PPI에 맞게 다시 지정한다. // (참고: Muldiv는 앞의 두 숫자를 곱하고 이어서 세번째 숫자로 나눈 후에 Integer로 반올림하는 델파이 함수) // 일단 이렇게 생성되고 나면, 다른 콘트롤과 마찬가지로 DPI가 다른 모니터로 옮겨져도 알맞게 맞추어진다. X := MulDiv ( 10, CurrentPPI, 96 ); // = 10 * CurrentPPI / 96 Y := MulDiv ( 10, CurrentPPI, 96 ); W := MulDiv ( 75, CurrentPPI, 96 ); H := MulDiv ( 25, CurrentPPI, 96 ); B.SetBounds( X, Y, W, H ); end; Per Monitor V2 매니페스트와 추가된 API를 사용하여 High DPI에서 완벽하게 표현된 결과. end. << DelphiCon 2020 목록으로 이동
  12. << DelphiCon으로 이동 엠바카데로 DelphiCon의 2020 시리즈를 소개합니다. DelphiCon은 공식 "엠바카데로 델파이" 온라인 세미나입니다: : 유튜브에서 모든 비디오 보기 (한글 제목은 한글 요약본이 제공됩니다. 한글 요약본이 필요한 세션 또는 기타 의견이 있으면 댓글을 남기세요) High DPI 고해상도를 VCL에서 활용하기 Leveraging High DPI in VCL Applications - Ray Konopka (54 min) 여러분의 앱에서도 델파이 11.0과 같이 서로 다른 다중 모니터 특히 최근에 부쩍 많아진 High DPI 모니터 지원할 수 있습니다. 그 방법을 이 세션에서 설명합니다. Spring4D 소개 - 델파이 개발을 한수준 높이기 Introduction to Spring4D - Taking Delphi Development to the Next Level - Stefan Glienke (63 min) 델파이로 웹 다루기 (한글 요약 작성 중) Powering the Web with Delphi - Bruno Fierens, Dr. Holger Flick, Chad Hower & Daniele Teti (56 min) Delphi GUI Programming with FireMonkey - Andrea Magni (61 min) 델파이 코딩을 더 빠르게 하는 방법 Code Faster in Delphi - Alister Christie (69 min) 코드사이트 로깅 실전 활용 기법 Real-world CodeSite Logging Techniques - Bob Swart (58 min) 델파이로 함수형 프로그래밍하기 Functional Programming With Delphi - Nick Hodges (34 min) 델파이 고성능 구현 High Performance Delphi - Primož Gabrijelčič (56 min) Delphi at the University: Insights for Students and Teachers - Victory Fernandes (56 min) Opening Keynote: The State of Delphi - Marco Cantù (59 min) Delphi in Education Panel (67 min) FireMonkey and Multi-Device Panel (73 min) Upgrade and Migration Panel (64 min) FireDAC Database Access Panel (88 min) Windows VCL Panel (67 min) Delphi Product Management & Architect Panel (77 min) << DelphiCon으로 이동
  13. << UX Summit 2021 목록으로 이동 UX Summit 의 2021 시리즈 중, Find leaks without leaving RAD Studio - Artem Razin 의 한국어 더빙 비디오입니다. 발표자(Artem Razin)는 Softanics의 창업자이며, Deleaker, ArmDot, BoxedApp 등을 개발했다. 이 세션은 델파이와 C++빌더에서 Deleaker를 이용하여 메모리 누수를 찾고 해당 소스로 이동하는 방법을 짧고 명확히 시연한다. 이 짧은 비디오는 한국어로 더빙이 되어 있습니다: YouTube 비디오 보기 (11 min, 한국어 더빙) Deleaker는 델파이와 C++빌더에서 사용되는 메모리 누수 (Memory Leak) 탐지 도구들 중에서 전용 도구 중 유일한 전용 도구이다. 이 제품이 유료인 이유는 사용해보면 알 수 있다. 메모리 누수 감지를 위한 정말 좋은 기능들이 제공된다. Delphi 시연 부분 보기 C++빌더 시연 부분 (7분 32초 부터) 보기 참고: RAD 스튜디오 안에서 메모리 누수 탐지하기(한국어 더빙) 시 사용한 스크립트 [00:18] Deleaker는 RAD 스튜디오에 플러그인 되어 메모리 누수를 탐지하고 리소스 사용을 효율적으로 최적화합니다. [00:27] 이 비디오에서는 Deleaker가 RAD 스튜디오 10.4 시드니에 어떻게 통합되는지 그리고, 개발자가 어떻게 메모리 누수를 찾고 해소하는 지를 보여줍니다. [00:36] Deleaker 설치파일을 실행합니다. [00:50] 설치되어 있는 RAD 스튜디오의 버전이 표시됩니다. 여기에 Deleaker가 통합됩니다. [01:06] RAD 스튜디오 10.4 시드니에 Deleaker를 통합하겠습니다. [01:17] 준비되었습니다. [01:21] 이제 RAD 스튜디오를 시작하겠습니다. [01:55] Deleaker 창은 언제든 RAD 스튜디오의 Deleaker 메뉴에서 열 수 있습니다. [02:09] 새 윈도우 VCL 애플리케이션을 만들겠습니다. [02:32] 프로젝트를 빌드하고 실행합니다. [03:02] RAD 스튜디오로 돌아와서 Deleaker 창을 열고 [Snapshot]을 클릭합니다. [03:22] 현재 살아있는 오브젝트를 보겠습니다. 클래스 이름별로 그룹핑 되어 있습니다. [03:36] 여기에 MainForm과 다른 오브젝트들이 보입니다. [03:42] 각 오브젝트마다 해당 크기, 소스파일, 호출 스택이 표시됩니다. [03:58] 이제 애플리케이션를 종료하겠습니다. [04:01] 애플리케이션 프로세스가 종료되면 Deleaker는 스냅샵을 찍기 시작합니다. [04:08] 누수가 없습니다. 예상대로입니다. [04:21] 누수를 만들어보겠습니다. [04:25] 폼에 버튼을 추가합니다. [04:33] 이름을 적습니다. [04:54] 버튼을 더블 클릭하여 이벤트 핸들러를 추가합니다. [04:58] 메모리를 하나 할당하고, TStringList 오브젝트도 하나 생성하겠습니다. [05:31] 지금 만든 누수를 Deleaker가 찾아낼 것입니다. [05:43] 빌드하고 실행합니다. [05:58] 누수가 생기도록 버튼을 몇번 클릭합니다. [06:05] 애플리케이션을 종료하겠습니다. 그러면 Deleaker가 스냅샷을 준비합니다. [06:13] Deleaker가 메모리 누수를 발견했습니다. 각 누수마다 발생 횟수, 크기, 소스 파일명, 호출 스택이 표시됩니다. [06:27] 누수되는 오브젝트를 찾기 위해 [Delphi Objects]를 클릭합니다. [06:31] Deleaker가 해당 TStringList 오브젝트를 찾아냈습니다. [06:37] 여기에 호출 스택이 표시됩니다. [06:42] 해당 소스 코드로 이동하려면 호출 스택에서 마우스 오른쪽을 클릭하고 Show Source Code를 선택합니다. [06:50] Deleaker에 의해 소스 파일이 열리고, 누수되는 오브젝트가 있는 줄에 커서가 위치합니다. [06:56] Deleaker로 돌아와 Allocation에서 메모리 할당 중 하나를 선택해 보겠습니다. [07:00] 메모리를 할당하는 GetMem 함수가 있는 줄로 찾아갑니다. [07:06] 최종 스냅샷에는 누수된 메모리와 해당 오브젝트의 크기, Hit Count, 횟수, 값과 모듈 정보가 모두 들어있습니다. 소스코드에서 할당된 리소스의 위치를 찾아가기도 쉽습니다. [07:30] Deleaker와 프로젝트를 모두 종료하겠습니다. [07:32] 이번에는 C++빌더로 비슷한 애플리케이션을 만들겠습니다. [07:50] 프로젝트가 준비되었습니다. 빌드하고 실행합니다. [08:25] 애플리케이션을 종료하지 않은 상태에서 개발툴로 가서, Deleaker창을 열고 [Snapshot]을 클릭하겠습니다. [08:38] 메모리 할당이 많이 표시됩니다. [08:42] 현재 살아있는 오브젝트들도 보입니다. [08:45] 오브젝트들은 클래스 이름 별로 그룹핑되어 있습니다 [08:49] 각 오브젝트 마다 호출 스택을 살펴볼 수 있습니다. [08:54] Deleaker와 애플리케이션을 종료합니다. [09:02] Deleaker가 글로벌 오브젝트 2개를 찾았네요. 좋아요. [09:13] 메모리 누수를 만들어보겠습니다. [09:16] 화면에 버튼을 놓고 이름을 줍니다. [09:27] 버튼을 더블클릭하여 이벤트 핸들러를 엽니다. [09:32] 메모리 누수 두개를 만들겠습니다. [09:57] 디버깅을 시작하겠습니다. [10:12] 버튼을 몇번 클릭하여 누수를 만듭니다. [10:16] 화면을 종료하면 Deleaker가 스냅샷을 찍습니다. [10:23] 스냅샷이 준비되었습니다. [10:26] Deleaker는 누수되는 오브젝트를 찾았습니다. [10:32] 여기에 해당 호출 스택이 있습니다. [10:34] 이 호출 스택에서 마우스 오른쪽을 클릭하고, Show Source Code 를 선택하여 누수가 발생하는 소스로 이동합니다. [10:40] 코드 편집기가 열리고 정확한 위치에 커서가 놓여있습니다. 좋네요. [10:44] Allocations를 열어보면 new 연산자에서 누수가 생긴 다는 것을 알게 됩니다. [10:51] Allocations에서 오른쪽 클릭을 하고, Show Source Code를 선택하여 소스 코드로 이동합니다. [10:58] 네, 정확한 코드 위치로 이동하였습니다. [11:02] Deleaker는 델파이와 C++빌더에서 사용할 수 있는 메모리 프로파일러입니다. [11:06] 개발자는 메모리 손실을 고칠 수 있고 윈도우 핸들과 기타 리소스의 누수 역시 고칠 수 있습니다. [11:10] RAD 스튜디오에 밀접하게 통합되기 때문에,개발 환경을 벗어날 필요없이 누수가 있는 코드로 바로 이동할 수 있습니다. [11:18] 행복한 코딩을 하시기 바랍니다. << UX Summit 2021 목록으로 이동
  14. << UX Summit으로 이동 엠바카데로 UX Summit의 2021 시리즈를 소개합니다. 테마: Desktop First UX (데스크톱 애플리케이션 개발자를 위한 UX/UI 구축 세미나) (한글 제목은 한글 요약본이 제공됩니다. 한글 요약본이 필요한 세션 또는 기타 의견이 있으면 댓글을 남기세요) 탭(Tab) 콘트롤은 데스크탑 UX를 어떻게 망가뜨리는가? - Ray Konopka (46 min) 탭 콘트롤 관련 16가지 예를 매우 짧고 명확히 설명하고 탭 콘트롤 디자인 가이드라인을 제시한다. RAD 스튜디오 안에서 메모리 누수 탐지하기 - Artem Razin (11 min, 한국어 더빙) 한국어 더빙 비디오 제공. 델파이와 C++빌더 안에서 메모리 누수가 있는 소스를 찾는 방법을 짧고 명확히 시연한다. Designing Multi-Threaded UIs - Olaf Monien RAD Studio 11 Preview - Marco Cantu Introduction to Data Abstract for Delphi - Marc Hoffman Introduction to Data Abstract for .NET - Marc Hoffman Introduction to Remoting SDK for Delphi - Marc Hoffman Introduction to Remoting SDK for .NET - Marc Hoffman Rubicon Full Text Search - Ann Lynnworth Improving User Experience with Effective Feedback Gathering - Kevin McCoy Delphi and the System of Laboratories, Analysis and Medical Management - Ziad Allaghi Creating Project Using EhLib - Dmitriy Bolshakov Native Response Design: Laying out Component for Dynamic Screen Sizing - Stephen Ball Vector Graphics: Lottie and SVG with Skia - Jim McKeeth Building Maintainable UIs with FLUX - Jason Southwell Achieve Best User Experience in Desktop Database Applications with FireDAC - Miguel Angel Moreno Rich and Lite IDE: UltraEdit and RAD Studio - David Millington, Ben Schwenk, Kyle Wheeler Case Studies on the Artificial Intelligence Integrated Desktop Engineering Applications - Dr. Yilmaz Yoru How Artificial Intelligence can improve UI/UX - Wagner Landgraf REST Client applications techniques with TMS XData - Wagner Landgraf Dashboard Systems: Design, Concept, and Coding Tips - Diego Souza The Evolution of the Windows UI to Fluent UI and Windows 11 - Abdullah Leghari Creating Cross Platform Desktop Applications with Web Technology from Delphi - Bruno Fierens Windows 11 Panel Session - David Millington & Matteo Pagani Visual Assist and Game Development - Chris Gardner, David Millington, Kyle Wheeler Building Your Own Design System: The Why and How - Jason Beres Desktop Databases: Options and Best Practices - Sriram Balasubramanian & Mary Kelly Design to Code : Go from Desktop to Web in Seconds! - Jason Beres (53 min) Utility & Design: The Desktop IDE - David Millington No Code + RPA to Optimize the Desktop UX - Dr. Setrag Khoshafian Learning C++ on the Desktop - Dr. Yilmaz Yoru Business of Desktop UI Components - Bruno Fierens & Atanas Popov Revamp your VCL App with DevExpress - Don Wibier Eight Questions I Had Every Day As A Dev Team Lead - Nick Hodges Building modern, High-DPI-ready, responsive user-interfaces with the VCL and TMS VCL UI Pack - Dr. Holger Flick Monday Panel with Live Q&A - Ian Barker MVP, Nick Hodges, marc hoffman & Ray Konopka Rubicon Full Text Search - Recap with Q&A - Ann Lynnworth Day 1 Overview Day 2 Overview Day 3 Overview Day 4 Overview Day 5 Overview << UX Summit으로 이동
  15. << UX Summit으로 이동 엠바카데로 UX Summit의 2021 시리즈를 소개합니다. 테마: Desktop First UX (데스크톱 애플리케이션 개발자를 위한 UX/UI 구축 세미나) (한글 제목은 한글 요약본이 제공됩니다. 한글 요약본이 필요한 세션 또는 기타 의견이 있으면 댓글을 남기세요) 탭(Tab) 콘트롤은 데스크탑 UX를 어떻게 망가뜨리는가? - Ray Konopka (46 min) 탭 콘트롤 관련 16가지 예를 매우 짧고 명확히 설명하고 탭 콘트롤 디자인 가이드라인을 제시한다. RAD 스튜디오 안에서 메모리 누수 탐지하기 - Artem Razin (11 min, 한국어 더빙) 한국어 더빙 비디오 제공. 델파이와 C++빌더 안에서 메모리 누수가 있는 소스를 찾는 방법을 짧고 명확히 시연한다. Designing Multi-Threaded UIs - Olaf Monien RAD Studio 11 Preview - Marco Cantu Introduction to Data Abstract for Delphi - Marc Hoffman Introduction to Data Abstract for .NET - Marc Hoffman Introduction to Remoting SDK for Delphi - Marc Hoffman Introduction to Remoting SDK for .NET - Marc Hoffman Rubicon Full Text Search - Ann Lynnworth Improving User Experience with Effective Feedback Gathering - Kevin McCoy Delphi and the System of Laboratories, Analysis and Medical Management - Ziad Allaghi Creating Project Using EhLib - Dmitriy Bolshakov Native Response Design: Laying out Component for Dynamic Screen Sizing - Stephen Ball Vector Graphics: Lottie and SVG with Skia - Jim McKeeth Building Maintainable UIs with FLUX - Jason Southwell Achieve Best User Experience in Desktop Database Applications with FireDAC - Miguel Angel Moreno Rich and Lite IDE: UltraEdit and RAD Studio - David Millington, Ben Schwenk, Kyle Wheeler Case Studies on the Artificial Intelligence Integrated Desktop Engineering Applications - Dr. Yilmaz Yoru How Artificial Intelligence can improve UI/UX - Wagner Landgraf REST Client applications techniques with TMS XData - Wagner Landgraf Dashboard Systems: Design, Concept, and Coding Tips - Diego Souza The Evolution of the Windows UI to Fluent UI and Windows 11 - Abdullah Leghari Creating Cross Platform Desktop Applications with Web Technology from Delphi - Bruno Fierens Windows 11 Panel Session - David Millington & Matteo Pagani Visual Assist and Game Development - Chris Gardner, David Millington, Kyle Wheeler Building Your Own Design System: The Why and How - Jason Beres Desktop Databases: Options and Best Practices - Sriram Balasubramanian & Mary Kelly Design to Code : Go from Desktop to Web in Seconds! - Jason Beres (53 min) Utility & Design: The Desktop IDE - David Millington No Code + RPA to Optimize the Desktop UX - Dr. Setrag Khoshafian Learning C++ on the Desktop - Dr. Yilmaz Yoru Business of Desktop UI Components - Bruno Fierens & Atanas Popov Revamp your VCL App with DevExpress - Don Wibier Eight Questions I Had Every Day As A Dev Team Lead - Nick Hodges Building modern, High-DPI-ready, responsive user-interfaces with the VCL and TMS VCL UI Pack - Dr. Holger Flick Monday Panel with Live Q&A - Ian Barker MVP, Nick Hodges, marc hoffman & Ray Konopka Rubicon Full Text Search - Recap with Q&A - Ann Lynnworth Day 1 Overview Day 2 Overview Day 3 Overview Day 4 Overview Day 5 Overview << UX Summit으로 이동 View full 엠바카데로 기술자료
  16. << UX Summit 2021 목록으로 이동 UX Summit 의 2021 시리즈 중, Designing Multi-Threaded UIs - Olaf Monien - Ray Konopka 의 한글 요약본입니다. 발표자 (Ray Konopka)는 CodeSite, Konopka Signature VCL로 유명한 Raize Software의 창업자이며, 월트 디즈니 어트랙션 테크놀러지의 수석 소프트웨어 엔지니어로 일했습니다. 탭 콘트롤 관련 16가지 예를 매우 짧고 명확히 설명하고, 탭 콘트롤 디자인 가이드라인을 제시합니다. 원본 비디오(YouTube) 보기(46 min)를 권장합니다. 목차 1 탭 콘트롤 이해하기 2 탭(Tab)의 갯수와 배치는 어느 정도가 알맞을까? 3 많은 문서를 탭으로 나누어 다루려면 어떻게 하면 좋을까? 4 컨텍스트 유지하기 5 계층 구조는 탭보다는 트리뷰가 직관적이다 6 스타일링 7 메타포(탭이 상징하는 것) 유지하기 8 탭 콘트롤 관련 기타 고려 사항 9 탭 콘트롤 디자인 가이드라인 10 탭의 방향과 배치 변경 등을 시도하여 더 효과적인 사용성을 제공해보ㅈ 1 탭 콘트롤 이해하기 탭(Tab) 콘트롤이 널리 사용되는 주된 이유 3가지 (비디오 0분 55초부터 보기) 시각적으로 분명하다: 해당 탭이 무엇을 하는 곳인지 사용자가 즉시 파악할 수 있다. 공간 효율성이 좋다: 좁은 공간 안에서 여러 그룹이 배치되고 각 그룹별로 공간을 사용할 수 있다. 사용성이 좋다: 시각적으로 분명하기 때문에, 실생활에서도 많이 쓰는 표현 방식이므로 쉽게 사용할 수 있다. 탭(Tab) 콘트롤의 "역할": 해당되는 콘텐츠 관리 (비디오 2분 30초부터 보기) 원하는 콘텐츠로 찾아서 빠르게 이동한다: 엑셀 등 스프레드시트의 탭 관련된 콘텐츠끼리 "따로" 모아놓는다: 옵션 설정 대화상자 (수십년간 사용되고 있다. 하지만 뒤에서 설명하는 것처럼, 잘못 사용하면 사용자에게 혼란을 주기도 한다.) 콘텐츠(문서)를 정돈한다: 웹브라우저, IDE, 그래픽 디자인 도구 등에서도 탭 방식이 많이 사용된다. 탭(Tab) 콘트롤 UX 디자인 시 어려운 점 (비디오 5분 40초부터 보기) 탭의 갯수: 탭이 너무 많으면 복잡하다. 사용자는 종종 탭을 너무 많이 열기도 한다. 컨텍스트 유지하기: 탭과 해당 내용 간에는 컨텍스트가 연결되어야 하고 직관적이고 일관성이 있어야 한다. 탭의 모양 스타일: 탭을 예쁘게만 보이게 하다보면 일관성이 깨지고 의미하는 바가 더 모호해질 수 있다. 메타포(탭이 상징하는 것): 탭이 상징하는 바가 점차 여러 가지가 되다보면, 뒤섞이다보니 사용자의 생각과 단절이 생긴다. 2 탭(Tab)의 갯수와 배치는 어느 정도가 알맞을까? 탭은 10개 이하일 때 매우 효과적이다. (대체로 7개 정도가 알맞다) 탭을 많이 열면 탭의 폭이 좁아지므로 탭 제목이 짧을 수록 사용성이 좋다. 제목이 너무 길면 잘려서 뒷부분이 안보일 수 이거나 너무 길어서 공간을 지나치게 많이 차지할 수 있다. 탭은 한줄에 배치되어야 한다. 불가피하게 여러줄이 꼭 필요할 수도 있지만, 여러줄에 들어있으면 사용자가 혼란을 느끼고 성가시게 생각한다. 사례1. 스크래치패드 에디터의 옵션 화면의 탭 갯수와 배치 (비디오 9분 30초부터 보기) 그림1. 스크래치패드 에디터의 옵션 화면은 그룹이 잘 나누어져 있고, 탭이 한줄로 정리되었고, 직관적이고 사용하기 쉽다. 탭을 7개로 구성했는데, 만약 그룹핑을 보다 세밀하게 10개로 했다면 오히려 복잡해지고, 화면도 더 많이 필요했을 것이다. 사례2. 소스 인사이트 옵션 화면의 탭 갯수와 배치 (비디오 11분 09초부터 보기) 그림2. 소스 인사이트의 옵션 화면은 탭이 여러줄에 배치되어 있다. 더 많은 내용을 담을 수 있지만 사용성은 떨어진다. 위쪽의 탭을 클릭하면 선택한 탭이 아랫 줄로 내려고, 다른 탭들의 위치도 달라지기 때문에 사용자들이 혼란을 느낀다. 요즘은 내용이 많은 경우에 한줄 안에 탭을 모두 넣기 위해 세로로 된 탭을 사용하기도 한다. 아쉽지만 윈도우의 기본 탭 콘트롤은 세로 배치 즉 오른쪽이나 왼쪽 배치를 지원하지 않는다. 3 많은 문서를 탭으로 나누어 다루려면 어떻게 하면 좋을까? 얼마나 많은 탭을 사용할 것인지에 대한 제한은 없다. (여러분도 웹 브라우저에서 탭을 상당히 많이 열지 않는가?) 정답은 없고 다들 조금씩 스타일이 다를 뿐이다. 하지만 어떤 스타일이든 일관성이 있어야 한다. 사례3. 크롬에서 문서를 탭으로 다루는 방법 (비디오 15분 03초부터 보기) 그림3. 크롬에서 탭이 많아지면 탭의 폭이 줄어든다. 탭이 많아지면 탭의 제목이 잘려서 정확히 알 수 없다. 오른쪽의 드롭다운 화살표를 클릭하면, 긴 탭 제목이 수직으로펼쳐진다. 하지만 부가 정보도 있고, 공간을 많이 차지하여 스크롤이 생긴다. 게다가 위쪽 탭과 세로 탭의 순서가 다르고 사용자가 정렬기준을 이해하기도 힘들다. 결국 원하는 탭을 쉽게 찾기가 불편하다. 사례4. 엣지에서 문서를 탭으로 다루는 방법 (비디오 17분 44초부터 보기) 그림4. 엣지에는 크롬에 있는 드롭다운 화살표가 없다. 대신 왼쪽에 수직 탭 (Veritcal Tab) 버튼이 있다. 그다지 탭처럼 보이지는 않기 때문에, 그래서 탭을 사용하던 기존의 경험과 단절된 기분이 든다. 하지만, 크롬보다 수직 탭의 크기가 작아서 탭 수가 같아도 스크롤이 필요없다. 그리고 탭의 배치 순서가 위쪽의 탭과 일치한다. 그렇다고 해도, 원하는 탭을 훨씬 더 빠르게 열 수 있다고 하기는 힘들다. 사례5. 사파리에서 문서를 탭으로 다루는 방법 (비디오 19분 24초부터 보기) 그림5. 사파리는 오쪽에 있는 탭 오버뷰 보기를 클릭하면 화면에 모든 탭의 미리보기가 펼쳐진다. 꽤 좋아보이지만, 여전히 각 탭 제목의 뒷부분이 잘린다. 화면도 많이 차지하므로, 일부 탭은 스크롤을 해야 볼 수 있다. 또한, 탭의 폭이 최소가 되면 글자가 없어지고 아이콘만 표시된다. 이이콘 만으로는 어떤 의미인지 알 수가 없다. 사례6. 스크래치패드에서 문서를 탭으로 다루는 방법 (비디오 21분 01초부터 보기) 그림6. 문서가 많아지면, 오른쪽에 드롭다운 메뉴가 생기고, 클릭하면 알파벳 순서로 모든 탭이 펼쳐진다. 각 탭이 공간을 적게 차지하므로 더 많은 탭을 표시할 수 있다. 그리고 위쪽에 있는 탭은 폭이 줄어들지 않는다. 4 컨텍스트 유지하기 탭이 매우 효과적이려면 다루는 콘텐츠가 너무 많지 않아야 한다. 너무 많은 콘텐츠를 탭으로 다루면, 오히려 더 성가실 수 있다. 탭을 중첩하지 말자: 탭 안에 다른 탭 그룹을 넣으면 찾기가 쉽지 않고, 그 의미도 불분명해진다. 탭과 내용이 어떻게 연결되는 지 한눈에 파악될 수 있도록 컨텍스트의 일관성을 유지한다. 사례7. IsoBuster 옵션 화면 (비디오 23분 26초부터 보기) 그림7. IsoBuster 옵션 화면은 너무 많은 정보를 넣기 위해서 사용하기 불편하게 만든 사례이다. 탭이 3겹으로 중첩되어 있어서 한눈에 들어오지 않는다. 가장 상위의 탭들은 실제로 메인화면의 Options 아래에 있는 메뉴들인데 여기에 또 들어있다. 그리고 상위의 탭을 선택하면, 선택된 상위 탭에 속하는 탭들로 아랫 줄의 탭들이 교체된다. 카테고리 별로 그룹을 나누기 위해 이렇게 했다는 것을 이해할 수 있었다. 5 계층 구조는 탭보다는 트리뷰가 직관적이다 이렇게 카테고리가 계층 구조로 표현되어야 한다면, 탭보다는 트리뷰 형태가 더 적합하다. 트리뷰는 보는 순간 바로 계층 구조라는 것을 명확히 알 수 있고 필요한 곳으로 이동하기가 더 쉽다. 참고로, 트리뷰를 다룰 때는 트리뷰에서 선택된 노드가 그 화면에서 사용자가 작업하는 내용과 연결이 되어 있어야 한다. 자칫 트리뷰의 선택에 해당되는 내용이 나머지 화면에 표현되지 않고 따로 놀면 사용자가 혼란을 느낀다. 사례8. RAD 스튜디오에 내장된 TChart 에디터 (비디오 25분 56초부터 보기) 그림8. TChart 편집창 역시 탭을 남용하여서 사용하기 불편하다. 편집창의 왼쪽에 트리뷰가 계층 구조를 표현하고 있다. 하지만, 오른쪽 화면 안에는 탭이 너무 많고 탭의 계층이 중첩되어 있다. Border 탭 아래에는 Frame, Corners, Callout, Bevel 이라는 4개의 탭이 있는데, 해당 내용은 적다. 차라리 이 4개가 탭이 아니라 그룹 박스라면 한번에 4개 모두를 펼쳐서 보여줄 수 있어서, 탭을 열고 들어갈 필요도 없을 것이다. 즉, 결국 그룹핑이나 계층 구조에서는 계층의 깊이를 줄이는 노력이 필요하다. 6 스타일링 탭은 매우 효과적이다. 하지만, 그러기 위해서는면, 선택되지 않은 탭과 쉽게 구별되어야 한다. 탭이 어떤 내용과 연결되어 있는 지가 명확해야 한다. 탭은 사용자가 보는 순간 바로 탭이라고 알 수 있는 모습이어야 한다. 탭의 방향을 잘 고려해야 한다. 사례9. 워드의 폰트 옵션창과 엑셀의 시트 탭 (비디오 28분 59초부터 보기) 그림9-1.워드의 폰트 옵션창은 선택된 탭과 해당 영역 그리고 그 나머지 영역 간의 명도차가 적어서 그 차이가 "확연하지 않다" 그림9-2.엑셀은 선택된 시트 탭의 제목이 굵고 다른 색이고 강조하는 밑줄이 있어서 선택되지 않은 탭과 그 차이가 "확연하다". 선택되지 않은 영역과 명도차도 워드의 옵션창보다 더 크다. 사례10. RAD 스튜디오에 사용되는 많은 탭 (비디오 30분 50초부터 보기) 그림10. RAD 스튜디오는 탭과 해당 내용 사이에 단절이 있다. 선택된 유닛이 무엇인지는 확연히 눈에 띄지만, 유닛 탭과 유닛의 내용 사이에 굵은 경계선이 있다. Code, Design, History 탭은 탭이 아닌 상자처럼 보인다. 게다가 편집 화면과 연결되어 보이지 않는다. Project, Deta Explorer, Multi-Device Previ..도 그 위의 상자와 연관이 있는 탭인데 상자와 단절된 느낌이든다. 사례11. 마이크로소프트 뉴스 웹사이트 (비디오 32분 52초부터 보기) 그림11. 마이크로소프트 뉴스에서는 보기에는 멋지지만 사용자들은 관심있는 항목을 찾아서 열기가 쉽지 않다. 사용자가 탭을 선택하면, 그 탭이 가장 앞으로 이동하면서 앞에 있던 탭을 밀어낸다. 사용자는 선택하는 영역인 탭들이 움직이기 때문에 위치를 쉽게 찾지 못한다. 사례12. 예전의 원노트 (비디오 34분 30초부터 보기) 그림12-1. 예전의 원노트에 있는 오른쪽 탭은 직관적이고 찾아서 이동하기 좋다. 그림12-2. 예전의 원노트에서 위쪽 탭에서 펼처진 드롭다운 탭과 위쪽에 있는 탭과, 좌측의 탭 모두 순서가 맞지 않는다. (Tutorial 항목의 위치를 잘 살펴보면 알 수 있다) 사례13. 윈도우 10 용 원노트 (비디오 36분 12초부터 보기) 그림13. 윈도우 10 용 원노트에서는 이전 버전에 있던 위쪽 탭을 제거하고, 수직 탭으로 계층을 표현했다. 이전보다 아쉬운 점은 선택된 제목 탭과 본문이 연결되지 않는다는 점이다. (이전 버전의 오른쪽 탭에서는 본문과 같은 하얀색으로 연결되어서 연결성이 명확했다.) 7 메타포(탭이 상징하는 것) 유지하기 윈도우 리본 UI는 탭 콘트롤에 강화된 스타일이 적용된 것이다. 안타깝지만, 리본 UI에는 여러 메타포가 섞여있어서 일관성이 떨어진다. 사례14. 워드패드 리본 UI의 탭 (비디오 37분 40초부터 보기) 그림14-1. 워드패드의 리본 UI의 위쪽 탭 중 File이 눈에 띄는 색상이므로 선택된 것처럼 보이지만, 실제로 선택된 탭은 Home이다. 그림14-2. 워드패드의 리본 UI에서 File도 탭인데, 클릭하면 다른 탭과 달리 팝업이 표시된다. 즉 다른 탭과 메타포가 다르다. 이렇게 메타포가 섞이면 단절이 발생한다. 사례15. 워드 리본 UI의 탭 (비디오 38분 25초부터 보기) 그림15-1. 워드 리본 UI는 탭의 스타일을 마치 탭이 아닌 것처럼 보이도록 한다. 그림15-2. 워드 리본 UI File 탭은 다른 탭과 메타포가 다르다. 이렇게 메타포가 섞이면 역시 단절이 생긴다. 즉 일관성이 깨진다. File 탭을 클릭하면, 다른 탭과 달리 새 창이 표시되고 심지어 다른 탭을 포함하여 화면을 모두 덮어버린다. 게다가, File 화면은 단순히 File이라고 하기에는 너무 많은 것들이 들어가 있다. 8 탭 콘트롤 관련 기타 고려 사항 탭 이동은 이미 널리 사용된다. 웹 브라우저와 웹사이트에서 탭으로 이동하는 방식이 인기있게 된 것은 5~10년 전이다. 웹에서 사용되는 탭의 스타일이 일부 데스크탑 앱에 영향을 끼치고 있다. 마법사도 탭의 일종이다. 마법사는 하나의 영역마다 해당 설정을 하는 일종의 탭이며, 매우 효과적이다. 단, [다음] 버튼을 통해야 다음 내용으로 넘어갈 수 있도록 다른 탭들은 안보여야 한다. 탭은 순서를 의미하지 않는다 "탭의 언제든지 어느 탭이든지 선택하여 열수 있다"는 본연의 메타포를 지키는 것이 중요하다. 내용 비교가 필요한 문서들은 탭을 사용하지 말자. 탭을 선택하면 해당 내용만 볼 수 있어야 하기 때문이다. 탭이 연결된 내용을 사용자가 비교하지 못하고, 탭을 눌러서 내용을 계속 바꿔야 하고 동시에 비교할 수 없다. 9 탭 콘트롤 디자인 가이드라인 탭을 나열할 때 사용자가 이해하기 쉬운 순서로 나열한다. (가장 중요한 것이 가장 앞에 있어야 한다) 탭 제목은 짧게 한두단어로만 만든다. 첫글자만 대문자로 쓴다. (모두 대문자로 쓰지 않는다) 탭을 여러줄로 배치하지 않는다. 탭을 중첩하여 사용하지 않는다. 선택된 탭은 그렇지 않은 탭에 비해 확연히 눈에 띄도록 표시한다. 10 탭의 방향과 배치 변경 등을 시도하여 더 효과적인 사용성을 제공해보자 TRzPageControl 과 TRzTabControl (비디오 43분 04초부터 보기) (겟잇 패키지 매니저에서 받을 수 있는) Konopka Signature VCL 콘트롤 세트에 들어있다. RAD 스튜디오에 내장된 TPageControl 과 TTabControl보다 많은 기능이 추가되어 있다. 그림 16-1. TRzPageControl은 탭의 방향과 위치를 지정할 수 있다. 그림 16-2. Konopka Signature VCL 콘트롤 데모에서 Tabs를 선택하면 많은 기능을 확인할 수 있다. TRzPageControl은 탭이 많아져도 탭의 폭이 줄어들지 않는다. 공간이 부족하면 드롭다운을 통해 모든 탭이 알파벳 순서로 수직으로 정렬된다. << UX Summit 2021 목록으로 이동
  17. << UX Summit 2021 목록으로 이동 UX Summit 의 2021 시리즈 중, Designing Multi-Threaded UIs - Olaf Monien - Ray Konopka 의 한글 요약본입니다. 발표자 (Ray Konopka)는 CodeSite, Konopka Signature VCL로 유명한 Raize Software의 창업자이며, 월트 디즈니 어트랙션 테크놀러지의 수석 소프트웨어 엔지니어로 일했습니다. 탭 콘트롤 관련 16가지 예를 매우 짧고 명확히 설명하고, 탭 콘트롤 디자인 가이드라인을 제시합니다. 원본 비디오(YouTube) 보기(46 min)를 권장합니다. 목차 1 탭 콘트롤 이해하기 2 탭(Tab)의 갯수와 배치는 어느 정도가 알맞을까? 3 많은 문서를 탭으로 나누어 다루려면 어떻게 하면 좋을까? 4 컨텍스트 유지하기 5 계층 구조는 탭보다는 트리뷰가 직관적이다 6 스타일링 7 메타포(탭이 상징하는 것) 유지하기 8 탭 콘트롤 관련 기타 고려 사항 9 탭 콘트롤 디자인 가이드라인 10 탭의 방향과 배치 변경 등을 시도하여 더 효과적인 사용성을 제공해보ㅈ 1 탭 콘트롤 이해하기 탭(Tab) 콘트롤이 널리 사용되는 주된 이유 3가지 (비디오 0분 55초부터 보기) 시각적으로 분명하다: 해당 탭이 무엇을 하는 곳인지 사용자가 즉시 파악할 수 있다. 공간 효율성이 좋다: 좁은 공간 안에서 여러 그룹이 배치되고 각 그룹별로 공간을 사용할 수 있다. 사용성이 좋다: 시각적으로 분명하기 때문에, 실생활에서도 많이 쓰는 표현 방식이므로 쉽게 사용할 수 있다. 탭(Tab) 콘트롤의 "역할": 해당되는 콘텐츠 관리 (비디오 2분 30초부터 보기) 원하는 콘텐츠로 찾아서 빠르게 이동한다: 엑셀 등 스프레드시트의 탭 관련된 콘텐츠끼리 "따로" 모아놓는다: 옵션 설정 대화상자 (수십년간 사용되고 있다. 하지만 뒤에서 설명하는 것처럼, 잘못 사용하면 사용자에게 혼란을 주기도 한다.) 콘텐츠(문서)를 정돈한다: 웹브라우저, IDE, 그래픽 디자인 도구 등에서도 탭 방식이 많이 사용된다. 탭(Tab) 콘트롤 UX 디자인 시 어려운 점 (비디오 5분 40초부터 보기) 탭의 갯수: 탭이 너무 많으면 복잡하다. 사용자는 종종 탭을 너무 많이 열기도 한다. 컨텍스트 유지하기: 탭과 해당 내용 간에는 컨텍스트가 연결되어야 하고 직관적이고 일관성이 있어야 한다. 탭의 모양 스타일: 탭을 예쁘게만 보이게 하다보면 일관성이 깨지고 의미하는 바가 더 모호해질 수 있다. 메타포(탭이 상징하는 것): 탭이 상징하는 바가 점차 여러 가지가 되다보면, 뒤섞이다보니 사용자의 생각과 단절이 생긴다. 2 탭(Tab)의 갯수와 배치는 어느 정도가 알맞을까? 탭은 10개 이하일 때 매우 효과적이다. (대체로 7개 정도가 알맞다) 탭을 많이 열면 탭의 폭이 좁아지므로 탭 제목이 짧을 수록 사용성이 좋다. 제목이 너무 길면 잘려서 뒷부분이 안보일 수 이거나 너무 길어서 공간을 지나치게 많이 차지할 수 있다. 탭은 한줄에 배치되어야 한다. 불가피하게 여러줄이 꼭 필요할 수도 있지만, 여러줄에 들어있으면 사용자가 혼란을 느끼고 성가시게 생각한다. 사례1. 스크래치패드 에디터의 옵션 화면의 탭 갯수와 배치 (비디오 9분 30초부터 보기) 그림1. 스크래치패드 에디터의 옵션 화면은 그룹이 잘 나누어져 있고, 탭이 한줄로 정리되었고, 직관적이고 사용하기 쉽다. 탭을 7개로 구성했는데, 만약 그룹핑을 보다 세밀하게 10개로 했다면 오히려 복잡해지고, 화면도 더 많이 필요했을 것이다. 사례2. 소스 인사이트 옵션 화면의 탭 갯수와 배치 (비디오 11분 09초부터 보기) 그림2. 소스 인사이트의 옵션 화면은 탭이 여러줄에 배치되어 있다. 더 많은 내용을 담을 수 있지만 사용성은 떨어진다. 위쪽의 탭을 클릭하면 선택한 탭이 아랫 줄로 내려고, 다른 탭들의 위치도 달라지기 때문에 사용자들이 혼란을 느낀다. 요즘은 내용이 많은 경우에 한줄 안에 탭을 모두 넣기 위해 세로로 된 탭을 사용하기도 한다. 아쉽지만 윈도우의 기본 탭 콘트롤은 세로 배치 즉 오른쪽이나 왼쪽 배치를 지원하지 않는다. 3 많은 문서를 탭으로 나누어 다루려면 어떻게 하면 좋을까? 얼마나 많은 탭을 사용할 것인지에 대한 제한은 없다. (여러분도 웹 브라우저에서 탭을 상당히 많이 열지 않는가?) 정답은 없고 다들 조금씩 스타일이 다를 뿐이다. 하지만 어떤 스타일이든 일관성이 있어야 한다. 사례3. 크롬에서 문서를 탭으로 다루는 방법 (비디오 15분 03초부터 보기) 그림3. 크롬에서 탭이 많아지면 탭의 폭이 줄어든다. 탭이 많아지면 탭의 제목이 잘려서 정확히 알 수 없다. 오른쪽의 드롭다운 화살표를 클릭하면, 긴 탭 제목이 수직으로펼쳐진다. 하지만 부가 정보도 있고, 공간을 많이 차지하여 스크롤이 생긴다. 게다가 위쪽 탭과 세로 탭의 순서가 다르고 사용자가 정렬기준을 이해하기도 힘들다. 결국 원하는 탭을 쉽게 찾기가 불편하다. 사례4. 엣지에서 문서를 탭으로 다루는 방법 (비디오 17분 44초부터 보기) 그림4. 엣지에는 크롬에 있는 드롭다운 화살표가 없다. 대신 왼쪽에 수직 탭 (Veritcal Tab) 버튼이 있다. 그다지 탭처럼 보이지는 않기 때문에, 그래서 탭을 사용하던 기존의 경험과 단절된 기분이 든다. 하지만, 크롬보다 수직 탭의 크기가 작아서 탭 수가 같아도 스크롤이 필요없다. 그리고 탭의 배치 순서가 위쪽의 탭과 일치한다. 그렇다고 해도, 원하는 탭을 훨씬 더 빠르게 열 수 있다고 하기는 힘들다. 사례5. 사파리에서 문서를 탭으로 다루는 방법 (비디오 19분 24초부터 보기) 그림5. 사파리는 오쪽에 있는 탭 오버뷰 보기를 클릭하면 화면에 모든 탭의 미리보기가 펼쳐진다. 꽤 좋아보이지만, 여전히 각 탭 제목의 뒷부분이 잘린다. 화면도 많이 차지하므로, 일부 탭은 스크롤을 해야 볼 수 있다. 또한, 탭의 폭이 최소가 되면 글자가 없어지고 아이콘만 표시된다. 이이콘 만으로는 어떤 의미인지 알 수가 없다. 사례6. 스크래치패드에서 문서를 탭으로 다루는 방법 (비디오 21분 01초부터 보기) 그림6. 문서가 많아지면, 오른쪽에 드롭다운 메뉴가 생기고, 클릭하면 알파벳 순서로 모든 탭이 펼쳐진다. 각 탭이 공간을 적게 차지하므로 더 많은 탭을 표시할 수 있다. 그리고 위쪽에 있는 탭은 폭이 줄어들지 않는다. 4 컨텍스트 유지하기 탭이 매우 효과적이려면 다루는 콘텐츠가 너무 많지 않아야 한다. 너무 많은 콘텐츠를 탭으로 다루면, 오히려 더 성가실 수 있다. 탭을 중첩하지 말자: 탭 안에 다른 탭 그룹을 넣으면 찾기가 쉽지 않고, 그 의미도 불분명해진다. 탭과 내용이 어떻게 연결되는 지 한눈에 파악될 수 있도록 컨텍스트의 일관성을 유지한다. 사례7. IsoBuster 옵션 화면 (비디오 23분 26초부터 보기) 그림7. IsoBuster 옵션 화면은 너무 많은 정보를 넣기 위해서 사용하기 불편하게 만든 사례이다. 탭이 3겹으로 중첩되어 있어서 한눈에 들어오지 않는다. 가장 상위의 탭들은 실제로 메인화면의 Options 아래에 있는 메뉴들인데 여기에 또 들어있다. 그리고 상위의 탭을 선택하면, 선택된 상위 탭에 속하는 탭들로 아랫 줄의 탭들이 교체된다. 카테고리 별로 그룹을 나누기 위해 이렇게 했다는 것을 이해할 수 있었다. 5 계층 구조는 탭보다는 트리뷰가 직관적이다 이렇게 카테고리가 계층 구조로 표현되어야 한다면, 탭보다는 트리뷰 형태가 더 적합하다. 트리뷰는 보는 순간 바로 계층 구조라는 것을 명확히 알 수 있고 필요한 곳으로 이동하기가 더 쉽다. 참고로, 트리뷰를 다룰 때는 트리뷰에서 선택된 노드가 그 화면에서 사용자가 작업하는 내용과 연결이 되어 있어야 한다. 자칫 트리뷰의 선택에 해당되는 내용이 나머지 화면에 표현되지 않고 따로 놀면 사용자가 혼란을 느낀다. 사례8. RAD 스튜디오에 내장된 TChart 에디터 (비디오 25분 56초부터 보기) 그림8. TChart 편집창 역시 탭을 남용하여서 사용하기 불편하다. 편집창의 왼쪽에 트리뷰가 계층 구조를 표현하고 있다. 하지만, 오른쪽 화면 안에는 탭이 너무 많고 탭의 계층이 중첩되어 있다. Border 탭 아래에는 Frame, Corners, Callout, Bevel 이라는 4개의 탭이 있는데, 해당 내용은 적다. 차라리 이 4개가 탭이 아니라 그룹 박스라면 한번에 4개 모두를 펼쳐서 보여줄 수 있어서, 탭을 열고 들어갈 필요도 없을 것이다. 즉, 결국 그룹핑이나 계층 구조에서는 계층의 깊이를 줄이는 노력이 필요하다. 6 스타일링 탭은 매우 효과적이다. 하지만, 그러기 위해서는면, 선택되지 않은 탭과 쉽게 구별되어야 한다. 탭이 어떤 내용과 연결되어 있는 지가 명확해야 한다. 탭은 사용자가 보는 순간 바로 탭이라고 알 수 있는 모습이어야 한다. 탭의 방향을 잘 고려해야 한다. 사례9. 워드의 폰트 옵션창과 엑셀의 시트 탭 (비디오 28분 59초부터 보기) 그림9-1.워드의 폰트 옵션창은 선택된 탭과 해당 영역 그리고 그 나머지 영역 간의 명도차가 적어서 그 차이가 "확연하지 않다" 그림9-2.엑셀은 선택된 시트 탭의 제목이 굵고 다른 색이고 강조하는 밑줄이 있어서 선택되지 않은 탭과 그 차이가 "확연하다". 선택되지 않은 영역과 명도차도 워드의 옵션창보다 더 크다. 사례10. RAD 스튜디오에 사용되는 많은 탭 (비디오 30분 50초부터 보기) 그림10. RAD 스튜디오는 탭과 해당 내용 사이에 단절이 있다. 선택된 유닛이 무엇인지는 확연히 눈에 띄지만, 유닛 탭과 유닛의 내용 사이에 굵은 경계선이 있다. Code, Design, History 탭은 탭이 아닌 상자처럼 보인다. 게다가 편집 화면과 연결되어 보이지 않는다. Project, Deta Explorer, Multi-Device Previ..도 그 위의 상자와 연관이 있는 탭인데 상자와 단절된 느낌이든다. 사례11. 마이크로소프트 뉴스 웹사이트 (비디오 32분 52초부터 보기) 그림11. 마이크로소프트 뉴스에서는 보기에는 멋지지만 사용자들은 관심있는 항목을 찾아서 열기가 쉽지 않다. 사용자가 탭을 선택하면, 그 탭이 가장 앞으로 이동하면서 앞에 있던 탭을 밀어낸다. 사용자는 선택하는 영역인 탭들이 움직이기 때문에 위치를 쉽게 찾지 못한다. 사례12. 예전의 원노트 (비디오 34분 30초부터 보기) 그림12-1. 예전의 원노트에 있는 오른쪽 탭은 직관적이고 찾아서 이동하기 좋다. 그림12-2. 예전의 원노트에서 위쪽 탭에서 펼처진 드롭다운 탭과 위쪽에 있는 탭과, 좌측의 탭 모두 순서가 맞지 않는다. (Tutorial 항목의 위치를 잘 살펴보면 알 수 있다) 사례13. 윈도우 10 용 원노트 (비디오 36분 12초부터 보기) 그림13. 윈도우 10 용 원노트에서는 이전 버전에 있던 위쪽 탭을 제거하고, 수직 탭으로 계층을 표현했다. 이전보다 아쉬운 점은 선택된 제목 탭과 본문이 연결되지 않는다는 점이다. (이전 버전의 오른쪽 탭에서는 본문과 같은 하얀색으로 연결되어서 연결성이 명확했다.) 7 메타포(탭이 상징하는 것) 유지하기 윈도우 리본 UI는 탭 콘트롤에 강화된 스타일이 적용된 것이다. 안타깝지만, 리본 UI에는 여러 메타포가 섞여있어서 일관성이 떨어진다. 사례14. 워드패드 리본 UI의 탭 (비디오 37분 40초부터 보기) 그림14-1. 워드패드의 리본 UI의 위쪽 탭 중 File이 눈에 띄는 색상이므로 선택된 것처럼 보이지만, 실제로 선택된 탭은 Home이다. 그림14-2. 워드패드의 리본 UI에서 File도 탭인데, 클릭하면 다른 탭과 달리 팝업이 표시된다. 즉 다른 탭과 메타포가 다르다. 이렇게 메타포가 섞이면 단절이 발생한다. 사례15. 워드 리본 UI의 탭 (비디오 38분 25초부터 보기) 그림15-1. 워드 리본 UI는 탭의 스타일을 마치 탭이 아닌 것처럼 보이도록 한다. 그림15-2. 워드 리본 UI File 탭은 다른 탭과 메타포가 다르다. 이렇게 메타포가 섞이면 역시 단절이 생긴다. 즉 일관성이 깨진다. File 탭을 클릭하면, 다른 탭과 달리 새 창이 표시되고 심지어 다른 탭을 포함하여 화면을 모두 덮어버린다. 게다가, File 화면은 단순히 File이라고 하기에는 너무 많은 것들이 들어가 있다. 8 탭 콘트롤 관련 기타 고려 사항 탭 이동은 이미 널리 사용된다. 웹 브라우저와 웹사이트에서 탭으로 이동하는 방식이 인기있게 된 것은 5~10년 전이다. 웹에서 사용되는 탭의 스타일이 일부 데스크탑 앱에 영향을 끼치고 있다. 마법사도 탭의 일종이다. 마법사는 하나의 영역마다 해당 설정을 하는 일종의 탭이며, 매우 효과적이다. 단, [다음] 버튼을 통해야 다음 내용으로 넘어갈 수 있도록 다른 탭들은 안보여야 한다. 탭은 순서를 의미하지 않는다 "탭의 언제든지 어느 탭이든지 선택하여 열수 있다"는 본연의 메타포를 지키는 것이 중요하다. 내용 비교가 필요한 문서들은 탭을 사용하지 말자. 탭을 선택하면 해당 내용만 볼 수 있어야 하기 때문이다. 탭이 연결된 내용을 사용자가 비교하지 못하고, 탭을 눌러서 내용을 계속 바꿔야 하고 동시에 비교할 수 없다. 9 탭 콘트롤 디자인 가이드라인 탭을 나열할 때 사용자가 이해하기 쉬운 순서로 나열한다. (가장 중요한 것이 가장 앞에 있어야 한다) 탭 제목은 짧게 한두단어로만 만든다. 첫글자만 대문자로 쓴다. (모두 대문자로 쓰지 않는다) 탭을 여러줄로 배치하지 않는다. 탭을 중첩하여 사용하지 않는다. 선택된 탭은 그렇지 않은 탭에 비해 확연히 눈에 띄도록 표시한다. 10 탭의 방향과 배치 변경 등을 시도하여 더 효과적인 사용성을 제공해보자 TRzPageControl 과 TRzTabControl (비디오 43분 04초부터 보기) (겟잇 패키지 매니저에서 받을 수 있는) Konopka Signature VCL 콘트롤 세트에 들어있다. RAD 스튜디오에 내장된 TPageControl 과 TTabControl보다 많은 기능이 추가되어 있다. 그림 16-1. TRzPageControl은 탭의 방향과 위치를 지정할 수 있다. 그림 16-2. Konopka Signature VCL 콘트롤 데모에서 Tabs를 선택하면 많은 기능을 확인할 수 있다. TRzPageControl은 탭이 많아져도 탭의 폭이 줄어들지 않는다. 공간이 부족하면 드롭다운을 통해 모든 탭이 알파벳 순서로 수직으로 정렬된다. << UX Summit 2021 목록으로 이동 View full 엠바카데로 기술자료
  18. << UX Summit 2021 목록으로 이동 UX Summit 의 2021 시리즈 중, Find leaks without leaving RAD Studio - Artem Razin 의 한국어 더빙 비디오입니다. 발표자(Artem Razin)는 Softanics의 창업자이며, Deleaker, ArmDot, BoxedApp 등을 개발했다. 이 세션은 델파이와 C++빌더에서 Deleaker를 이용하여 메모리 누수를 찾고 해당 소스로 이동하는 방법을 짧고 명확히 시연한다. 이 짧은 비디오는 한국어로 더빙이 되어 있습니다: YouTube 비디오 보기 (11 min, 한국어 더빙) Deleaker는 델파이와 C++빌더에서 사용되는 메모리 누수 (Memory Leak) 탐지 도구들 중에서 전용 도구 중 유일한 전용 도구이다. 이 제품이 유료인 이유는 사용해보면 알 수 있다. 메모리 누수 감지를 위한 정말 좋은 기능들이 제공된다. Delphi 시연 부분 보기 C++빌더 시연 부분 (7분 32초 부터) 보기 참고: RAD 스튜디오 안에서 메모리 누수 탐지하기(한국어 더빙) 시 사용한 스크립트 [00:18] Deleaker는 RAD 스튜디오에 플러그인 되어 메모리 누수를 탐지하고 리소스 사용을 효율적으로 최적화합니다. [00:27] 이 비디오에서는 Deleaker가 RAD 스튜디오 10.4 시드니에 어떻게 통합되는지 그리고, 개발자가 어떻게 메모리 누수를 찾고 해소하는 지를 보여줍니다. [00:36] Deleaker 설치파일을 실행합니다. [00:50] 설치되어 있는 RAD 스튜디오의 버전이 표시됩니다. 여기에 Deleaker가 통합됩니다. [01:06] RAD 스튜디오 10.4 시드니에 Deleaker를 통합하겠습니다. [01:17] 준비되었습니다. [01:21] 이제 RAD 스튜디오를 시작하겠습니다. [01:55] Deleaker 창은 언제든 RAD 스튜디오의 Deleaker 메뉴에서 열 수 있습니다. [02:09] 새 윈도우 VCL 애플리케이션을 만들겠습니다. [02:32] 프로젝트를 빌드하고 실행합니다. [03:02] RAD 스튜디오로 돌아와서 Deleaker 창을 열고 [Snapshot]을 클릭합니다. [03:22] 현재 살아있는 오브젝트를 보겠습니다. 클래스 이름별로 그룹핑 되어 있습니다. [03:36] 여기에 MainForm과 다른 오브젝트들이 보입니다. [03:42] 각 오브젝트마다 해당 크기, 소스파일, 호출 스택이 표시됩니다. [03:58] 이제 애플리케이션를 종료하겠습니다. [04:01] 애플리케이션 프로세스가 종료되면 Deleaker는 스냅샵을 찍기 시작합니다. [04:08] 누수가 없습니다. 예상대로입니다. [04:21] 누수를 만들어보겠습니다. [04:25] 폼에 버튼을 추가합니다. [04:33] 이름을 적습니다. [04:54] 버튼을 더블 클릭하여 이벤트 핸들러를 추가합니다. [04:58] 메모리를 하나 할당하고, TStringList 오브젝트도 하나 생성하겠습니다. [05:31] 지금 만든 누수를 Deleaker가 찾아낼 것입니다. [05:43] 빌드하고 실행합니다. [05:58] 누수가 생기도록 버튼을 몇번 클릭합니다. [06:05] 애플리케이션을 종료하겠습니다. 그러면 Deleaker가 스냅샷을 준비합니다. [06:13] Deleaker가 메모리 누수를 발견했습니다. 각 누수마다 발생 횟수, 크기, 소스 파일명, 호출 스택이 표시됩니다. [06:27] 누수되는 오브젝트를 찾기 위해 [Delphi Objects]를 클릭합니다. [06:31] Deleaker가 해당 TStringList 오브젝트를 찾아냈습니다. [06:37] 여기에 호출 스택이 표시됩니다. [06:42] 해당 소스 코드로 이동하려면 호출 스택에서 마우스 오른쪽을 클릭하고 Show Source Code를 선택합니다. [06:50] Deleaker에 의해 소스 파일이 열리고, 누수되는 오브젝트가 있는 줄에 커서가 위치합니다. [06:56] Deleaker로 돌아와 Allocation에서 메모리 할당 중 하나를 선택해 보겠습니다. [07:00] 메모리를 할당하는 GetMem 함수가 있는 줄로 찾아갑니다. [07:06] 최종 스냅샷에는 누수된 메모리와 해당 오브젝트의 크기, Hit Count, 횟수, 값과 모듈 정보가 모두 들어있습니다. 소스코드에서 할당된 리소스의 위치를 찾아가기도 쉽습니다. [07:30] Deleaker와 프로젝트를 모두 종료하겠습니다. [07:32] 이번에는 C++빌더로 비슷한 애플리케이션을 만들겠습니다. [07:50] 프로젝트가 준비되었습니다. 빌드하고 실행합니다. [08:25] 애플리케이션을 종료하지 않은 상태에서 개발툴로 가서, Deleaker창을 열고 [Snapshot]을 클릭하겠습니다. [08:38] 메모리 할당이 많이 표시됩니다. [08:42] 현재 살아있는 오브젝트들도 보입니다. [08:45] 오브젝트들은 클래스 이름 별로 그룹핑되어 있습니다 [08:49] 각 오브젝트 마다 호출 스택을 살펴볼 수 있습니다. [08:54] Deleaker와 애플리케이션을 종료합니다. [09:02] Deleaker가 글로벌 오브젝트 2개를 찾았네요. 좋아요. [09:13] 메모리 누수를 만들어보겠습니다. [09:16] 화면에 버튼을 놓고 이름을 줍니다. [09:27] 버튼을 더블클릭하여 이벤트 핸들러를 엽니다. [09:32] 메모리 누수 두개를 만들겠습니다. [09:57] 디버깅을 시작하겠습니다. [10:12] 버튼을 몇번 클릭하여 누수를 만듭니다. [10:16] 화면을 종료하면 Deleaker가 스냅샷을 찍습니다. [10:23] 스냅샷이 준비되었습니다. [10:26] Deleaker는 누수되는 오브젝트를 찾았습니다. [10:32] 여기에 해당 호출 스택이 있습니다. [10:34] 이 호출 스택에서 마우스 오른쪽을 클릭하고, Show Source Code 를 선택하여 누수가 발생하는 소스로 이동합니다. [10:40] 코드 편집기가 열리고 정확한 위치에 커서가 놓여있습니다. 좋네요. [10:44] Allocations를 열어보면 new 연산자에서 누수가 생긴 다는 것을 알게 됩니다. [10:51] Allocations에서 오른쪽 클릭을 하고, Show Source Code를 선택하여 소스 코드로 이동합니다. [10:58] 네, 정확한 코드 위치로 이동하였습니다. [11:02] Deleaker는 델파이와 C++빌더에서 사용할 수 있는 메모리 프로파일러입니다. [11:06] 개발자는 메모리 손실을 고칠 수 있고 윈도우 핸들과 기타 리소스의 누수 역시 고칠 수 있습니다. [11:10] RAD 스튜디오에 밀접하게 통합되기 때문에,개발 환경을 벗어날 필요없이 누수가 있는 코드로 바로 이동할 수 있습니다. [11:18] 행복한 코딩을 하시기 바랍니다. << UX Summit 2021 목록으로 이동 View full 엠바카데로 기술자료
  19. << UX Summit으로 이동 엠바카데로 UX Summit의 첫번째 시리즈인 UX Summit 2020을 소개합니다. 테마: Desktop First UX (데스크톱 애플리케이션 개발자를 위한 UX/UI 구축 세미나) (한글 제목은 한글 요약본이 제공됩니다. 한글 요약본이 필요한 세션 또는 기타 의견이 있으면 댓글을 남기세요) Keynote: How Mobile First Ruined Desktop UX - Ray Konopka (56 min) 모바일 우선이 데스크탑 UX에 어떤 영향을 끼쳤는지, 데스크탑 UX에는 왜 데스크탑 우선이 알맞은지 등 관련된 재미있는 사례와 유익한 정보들을 제공한다. 훌륭한 UI에 적용된 과학 (25 min) | Science of Great UI - Mark Miller of DevExpress 사람의 생물학적 인지 능력을 바탕으로 과학적으로 UI를 접근하여 훌륭한 UI 설계의 방향을 제시한다. 구체적인 사례로, 버튼의 형태나 색상 등을 어떻게 디자인하면, 사용자가 보다 쉽고 편하게 사용하고 인지하는 지를 분석한다. 멀티플랫폼 앱에 가장 효과적인 UX 디자인 (24 min) | Effective UX Design for Multiplatform Apps - Miguel Angel Moreno 오른쪽 클릭은 옳다! (24 min) | Right Click is Right! - Roger Swann 오른쪽 클릭을 왜 해야하고,어떻게 구현하는 지를 명쾌하게 설명한다. 정말 멋진 윈도우 10 플루언트 UI를 델파이로 구현하기 (1편 52 min, 2편 47 min) 마이크로소프트의 새 표준 디자인 컨셉인 플루언트 UI의 핵심을 정리 및 시연 하고(1편), 윈도우10 데스크톱 UX를 델파이 만으로 구현하는 기술을 시연한다(2편). 레거시 데스크탑 앱 UI/UX 현대화 – 이론부터 실제까지 (67 min) | Legacy desktop apps UI & UX modernization - Serge Pilko 15년 전에는 괜찮은 UI였는데, 이제는 구식으로 보이는 앱이 있나요? 어떤 전략과 방안을 선택을 할 수 있고, UI 현대화 절차, 팁, 위험 관리 방안을 사례를 들어 설명한다. 데스크탑용 UX 구축/최적화 전략 (13 min) | Strategies for Building and Optimizing the Desktop UX - Mary Kelly 데스트탑 전용 UX 구축 전략을 위한 수립 절차, 방법, 기본틀, 진행 팁을 공유한다. 터치스크린 POS 화면 디자인하기 (55 min) | Point of Sale Screen Concept - Diego Souza 식당용 POS 화면 하나를 그대로 따라하면 완성할 수 있도록 처음부터 하나씩 빠짐없이 시연한다. 애플리케이션을 개발할 때 데스크탑부터 개발해야 하는 이유 (20 min) | Why Desktop First to Develop an Application - Yılmaz Yörü 데스크탑 전용, 모바일 전용, 데스크탑-모바일 겸용 중 하나를 선택을 해야할 때의 판단 기준을 제시하고 각 사례를 제시한다. 감지와 응답: 우리의 방식을 지속적으로 파악하여 더 좋은 성과에 도달하기 (29 min) | Sense & Respond: Continuously Learning Our Way to Better Outcomes - Jeff Gothelf (사용자들은 소프트웨어를 계획된 대로만 사용하지는 않는다는 사실을 인정하고) 더 좋은 소프트웨어 UX를 제공하기 위한 근본적인 접근 방식을 제시한다. "어떨 때" 모바일보다 데스크톱을 선택/집중하는 것이 좋을까? (10 min) | Mobile Second: When to Focus on Desktop First - Paul Shustak (10 min) 새 소프트웨어를 출시하고 초기 시장에 안착하기 위해, 데스트탑과 모바일 중 “상황 맞는 선택"을 하기 위한 기준을 제시하고, 데스크탑을 우선하여 좋은 결과를 낸 사례를 분석한다. UNTOLD AI by Chris Noessel (18 min) Speed redesigns are the future - Michał Malewicz (21 min) Redesigning a Modern Developer IDE - Antonio Gomez (35 min) Lost Art Tweaking the OS - Bruno Sonnino (44 min) Building Consistent UI - Holger Flick (29 min) Build amazing applications with the Fluent Design System - Matteo Pagani (34 min) Building a Health Services Desktop Application in Delphi VCL - Ziad Allaghi (33 min) How to Terrify Your Users (& Look Cool at Parties) - Ian Barker (76 min) Desktop Native or Web on Windows? - Marco Cantu (25 min) Using Adobe XD for UI Wireframes - Nabanita Das (20 min) Leveraging Users' Visual System - Billy Hollis (75 min) Automation & Industrial User Interfaces - Zach Briggs (30 min) Desktop UX Panel #1: 데스트톱 UX (88 min) Desktop UX Panel #2: 데스트톱 UX (95 min) Desktop UX Panel #3: 데스트톱 UX (85 min) << UX Summit으로 이동
  20. << UX Summit으로 이동 엠바카데로 UX Summit의 첫번째 시리즈인 UX Summit 2020을 소개합니다. 테마: Desktop First UX (데스크톱 애플리케이션 개발자를 위한 UX/UI 구축 세미나) (한글 제목은 한글 요약본이 제공됩니다. 한글 요약본이 필요한 세션 또는 기타 의견이 있으면 댓글을 남기세요) Keynote: How Mobile First Ruined Desktop UX - Ray Konopka (56 min) 모바일 우선이 데스크탑 UX에 어떤 영향을 끼쳤는지, 데스크탑 UX에는 왜 데스크탑 우선이 알맞은지 등 관련된 재미있는 사례와 유익한 정보들을 제공한다. 훌륭한 UI에 적용된 과학 (25 min) | Science of Great UI - Mark Miller of DevExpress 사람의 생물학적 인지 능력을 바탕으로 과학적으로 UI를 접근하여 훌륭한 UI 설계의 방향을 제시한다. 구체적인 사례로, 버튼의 형태나 색상 등을 어떻게 디자인하면, 사용자가 보다 쉽고 편하게 사용하고 인지하는 지를 분석한다. 멀티플랫폼 앱에 가장 효과적인 UX 디자인 (24 min) | Effective UX Design for Multiplatform Apps - Miguel Angel Moreno 오른쪽 클릭은 옳다! (24 min) | Right Click is Right! - Roger Swann 오른쪽 클릭을 왜 해야하고,어떻게 구현하는 지를 명쾌하게 설명한다. 정말 멋진 윈도우 10 플루언트 UI를 델파이로 구현하기 (1편 52 min, 2편 47 min) 마이크로소프트의 새 표준 디자인 컨셉인 플루언트 UI의 핵심을 정리 및 시연 하고(1편), 윈도우10 데스크톱 UX를 델파이 만으로 구현하는 기술을 시연한다(2편). 레거시 데스크탑 앱 UI/UX 현대화 – 이론부터 실제까지 (67 min) | Legacy desktop apps UI & UX modernization - Serge Pilko 15년 전에는 괜찮은 UI였는데, 이제는 구식으로 보이는 앱이 있나요? 어떤 전략과 방안을 선택을 할 수 있고, UI 현대화 절차, 팁, 위험 관리 방안을 사례를 들어 설명한다. 데스크탑용 UX 구축/최적화 전략 (13 min) | Strategies for Building and Optimizing the Desktop UX - Mary Kelly 데스트탑 전용 UX 구축 전략을 위한 수립 절차, 방법, 기본틀, 진행 팁을 공유한다. 터치스크린 POS 화면 디자인하기 (55 min) | Point of Sale Screen Concept - Diego Souza 식당용 POS 화면 하나를 그대로 따라하면 완성할 수 있도록 처음부터 하나씩 빠짐없이 시연한다. 애플리케이션을 개발할 때 데스크탑부터 개발해야 하는 이유 (20 min) | Why Desktop First to Develop an Application - Yılmaz Yörü 데스크탑 전용, 모바일 전용, 데스크탑-모바일 겸용 중 하나를 선택을 해야할 때의 판단 기준을 제시하고 각 사례를 제시한다. 감지와 응답: 우리의 방식을 지속적으로 파악하여 더 좋은 성과에 도달하기 (29 min) | Sense & Respond: Continuously Learning Our Way to Better Outcomes - Jeff Gothelf (사용자들은 소프트웨어를 계획된 대로만 사용하지는 않는다는 사실을 인정하고) 더 좋은 소프트웨어 UX를 제공하기 위한 근본적인 접근 방식을 제시한다. "어떨 때" 모바일보다 데스크톱을 선택/집중하는 것이 좋을까? (10 min) | Mobile Second: When to Focus on Desktop First - Paul Shustak (10 min) 새 소프트웨어를 출시하고 초기 시장에 안착하기 위해, 데스트탑과 모바일 중 “상황 맞는 선택"을 하기 위한 기준을 제시하고, 데스크탑을 우선하여 좋은 결과를 낸 사례를 분석한다. UNTOLD AI by Chris Noessel (18 min) Speed redesigns are the future - Michał Malewicz (21 min) Redesigning a Modern Developer IDE - Antonio Gomez (35 min) Lost Art Tweaking the OS - Bruno Sonnino (44 min) Building Consistent UI - Holger Flick (29 min) Build amazing applications with the Fluent Design System - Matteo Pagani (34 min) Building a Health Services Desktop Application in Delphi VCL - Ziad Allaghi (33 min) How to Terrify Your Users (& Look Cool at Parties) - Ian Barker (76 min) Desktop Native or Web on Windows? - Marco Cantu (25 min) Using Adobe XD for UI Wireframes - Nabanita Das (20 min) Leveraging Users' Visual System - Billy Hollis (75 min) Automation & Industrial User Interfaces - Zach Briggs (30 min) Desktop UX Panel #1: 데스트톱 UX (88 min) Desktop UX Panel #2: 데스트톱 UX (95 min) Desktop UX Panel #3: 데스트톱 UX (85 min) << UX Summit으로 이동 View full 엠바카데로 기술자료
  21. UX Summit 2020> 중, 을(를) 요약/번역 했습니다. 발표자 ()는 . 이 요약에 미처 담지 못한 좋은 내용은 원본을 보세요: YouTube 비디오 (총 56분) 보기 이 세션을 통해서 우리는, View full 아티클
  22. Kori

    [UX Summit 2020 요약] 훌륭한 UI에 적용된 과학

    << UX Summit 2020 목록으로 이동 UX Summit 의 2020 시리즈 중, Science of Great UI - Mark Miller 의 한글 요약본입니다. 발표자 (Mark Miller)는 16년간 훌륭한 UI를 연구해 오고 있으며, DevIQ에서 비디오 교육 과정도 제공하고 있습니다. 비디오 시청을 권장합니다. 쉽고 명확한 예시를 통해 좋은 화면과 그렇지 않은 것의 차이를 눈으로 쉽게 확인할 수 있습니다. 이 요약에 미처 담지 못한 좋은 내용은 원본을 보세요: YouTube 비디오 (25 min) 보기 이 세션에서는 매우 유용한 UI에 적용되는 과학적 원리와 응용된 팁을 배울 수 있습니다. 저는 개발 뿐만 아니라 문서 작성에도 이 원리를 적용하려고 합니다. 소개된 팁들 역시 바로 사용할 것입니다. 과학적 원리와 응용된 팁 과학적 원리 응용된 팁 중요도와 강조 대비가 클수록 강조도 커진다. 강조가 커지면, 구분도 명확해진다. 화면의 모든 요소가 정보이다. 하지만, 각 요소마다 중요도는 다르다 . 각 화면 요소에서 강조의 정도와 중요도를 일치시키자. 내용(주목적)을 강조하자. 형식(보조 수단)을 강조하지 말자. 대비와 가독성 명도 대비가 클 수록 가독성이 높다. 색상 대비는 가독성 향상 효과가 낮다. 가독성을 파악하려면, 흑백 필터를 적용하여 Brightness (밝기)를 비교한다. 글 상자의 여백 정답은 없지만, 알맞은 수준은 있다. 인간이 글을 인식하는 방식에 대한 실험 결과를 바탕으로 한다. 여백은 단어 사이 간격의 1.5배가 적당하다. 상자의 테두리 테두리는 선이 얇고 명도 대비가 낮을 때, 상자 안의 글자 인식을 덜 방해한다. 상자의 테두리 선은 명도 대비를 낮추고 얇은 선을 사용하자. (그러면, 여백을 충분히 줄여도 테두리가 내용을 방해하지 않는다.) 모서리 사람은 뾰족한 곳이 중요하다고 생각한다. 뾰족한 모서리는 시선을 끈다. 아이콘 등 작은 도형은 끝을 둥글게 만들자. 이유는, 같은 크기의 뾰족한 도형보다 더 많은 픽셀이 들어가므로 멀리서도 더 잘보이고, 더 커보인다. 뾰족한 지점이 만드는 불필요한 강조가 없어진다. 강조하고 싶은 곳만 뾰족한 모서리를 사용하자. 예: 연필 모양을 사용할 때, 연필 모양 끝 지점에 중요한 의미가 없다면, 그 지점도 둥글게 처리하자. 도형 안쪽 비우기 vs 채우기 (다음 표 참조)* 버튼을 강조하려면 버튼 색상을 채우자. 도형 안쪽 비우기와 채우기 비교 (사각형 버튼을 비교하기) 안쪽이 비어있고 테두리로 그려진 버튼 안쪽이 모두 채워진 버튼 (글자를 제외하고) 표시되는 픽셀수 1,000 개 10,000 개 사용된 사각형 개수 2개 (테두리 바깥쪽 사각형, 안쪽 사각형) 1개 모서리의 개수 8개 (테두리 바깥과 안) 4개 선명하지 않을 때의 전달력 시선을 끌지 못한다. 시선을 끈다. 시각적 메시지 강조되는 것은 테두리이다. (테두리에 뾰족한 모서리가 8개나 있다.) 강조되는 것은 도형 자체이다. UI 향상해보기 그림1. 왼쪽의 UI 보다 오른쪽 UI가 전달력이 높다. 위 그림의 왼쪽 UI를 오른쪽 UI로 바꾸기 위한 작업 각 도형의 불필요한 뾰족한 모서리 제거 그래프의 명도 대비를 확대 불필요하게 대문자로만 된 글자를 소문자로 변경 (모든 글자가 강조되어 서로 자기가 중요하다고 하는 것은 좋지 않다. 중요한 것만 강조하자.) 의미 없고 불필요한 색상을 제거하고 명도 변화만 살짝 적용 (중요하지 않은 곳이 시선을 끌지 않도록 하자.) (더 자세한 내용은 원본 비디오 참고) 기타 참고 스크린샷 (원본 비디오에는 더 많은 예시가 있습니다.) 그림2. 표의 테두리가 배경과 명도 대비가 낮고 얇을 수록 내용이 더 잘 들어온다. (표의 불필요한 여백도 줄일 수 있게 된다.) 그림3. 크기가 같은 도형이라도 모서리가 둥글면 도형에 사용되는 픽셀이 더 많아져서 더 커보이고 더 잘보인다. 그림4. 뾰족한 곳은 시선을 끈다. 중요한 곳을 강조하려면 상자에서 중요한 곳만 뾰족하게 하고 다른 모서리는 둥글게 처리한다. 그림5. 버튼 이미지는 채우는 것이 더 좋다. 그림6. 버튼의 모서리는 둥근 것이 더 좋다. << UX Summit 2020 목록으로 이동
  23. << UX Summit 2020 목록으로 이동 UX Summit 의 2020 시리즈 중, Science of Great UI - Mark Miller 의 한글 요약본입니다. 발표자 (Mark Miller)는 16년간 훌륭한 UI를 연구해 오고 있으며, DevIQ에서 비디오 교육 과정도 제공하고 있습니다. 비디오 시청을 권장합니다. 쉽고 명확한 예시를 통해 좋은 화면과 그렇지 않은 것의 차이를 눈으로 쉽게 확인할 수 있습니다. 이 요약에 미처 담지 못한 좋은 내용은 원본을 보세요: YouTube 비디오 (25 min) 보기 이 세션에서는 매우 유용한 UI에 적용되는 과학적 원리와 응용된 팁을 배울 수 있습니다. 저는 개발 뿐만 아니라 문서 작성에도 이 원리를 적용하려고 합니다. 소개된 팁들 역시 바로 사용할 것입니다. 과학적 원리와 응용된 팁 과학적 원리 응용된 팁 중요도와 강조 대비가 클수록 강조도 커진다. 강조가 커지면, 구분도 명확해진다. 화면의 모든 요소가 정보이다. 하지만, 각 요소마다 중요도는 다르다 . 각 화면 요소에서 강조의 정도와 중요도를 일치시키자. 내용(주목적)을 강조하자. 형식(보조 수단)을 강조하지 말자. 대비와 가독성 명도 대비가 클 수록 가독성이 높다. 색상 대비는 가독성 향상 효과가 낮다. 가독성을 파악하려면, 흑백 필터를 적용하여 Brightness (밝기)를 비교한다. 글 상자의 여백 정답은 없지만, 알맞은 수준은 있다. 인간이 글을 인식하는 방식에 대한 실험 결과를 바탕으로 한다. 여백은 단어 사이 간격의 1.5배가 적당하다. 상자의 테두리 테두리는 선이 얇고 명도 대비가 낮을 때, 상자 안의 글자 인식을 덜 방해한다. 상자의 테두리 선은 명도 대비를 낮추고 얇은 선을 사용하자. (그러면, 여백을 충분히 줄여도 테두리가 내용을 방해하지 않는다.) 모서리 사람은 뾰족한 곳이 중요하다고 생각한다. 뾰족한 모서리는 시선을 끈다. 아이콘 등 작은 도형은 끝을 둥글게 만들자. 이유는, 같은 크기의 뾰족한 도형보다 더 많은 픽셀이 들어가므로 멀리서도 더 잘보이고, 더 커보인다. 뾰족한 지점이 만드는 불필요한 강조가 없어진다. 강조하고 싶은 곳만 뾰족한 모서리를 사용하자. 예: 연필 모양을 사용할 때, 연필 모양 끝 지점에 중요한 의미가 없다면, 그 지점도 둥글게 처리하자. 도형 안쪽 비우기 vs 채우기 (다음 표 참조)* 버튼을 강조하려면 버튼 색상을 채우자. 도형 안쪽 비우기와 채우기 비교 (사각형 버튼을 비교하기) 안쪽이 비어있고 테두리로 그려진 버튼 안쪽이 모두 채워진 버튼 (글자를 제외하고) 표시되는 픽셀수 1,000 개 10,000 개 사용된 사각형 개수 2개 (테두리 바깥쪽 사각형, 안쪽 사각형) 1개 모서리의 개수 8개 (테두리 바깥과 안) 4개 선명하지 않을 때의 전달력 시선을 끌지 못한다. 시선을 끈다. 시각적 메시지 강조되는 것은 테두리이다. (테두리에 뾰족한 모서리가 8개나 있다.) 강조되는 것은 도형 자체이다. UI 향상해보기 그림1. 왼쪽의 UI 보다 오른쪽 UI가 전달력이 높다. 위 그림의 왼쪽 UI를 오른쪽 UI로 바꾸기 위한 작업 각 도형의 불필요한 뾰족한 모서리 제거 그래프의 명도 대비를 확대 불필요하게 대문자로만 된 글자를 소문자로 변경 (모든 글자가 강조되어 서로 자기가 중요하다고 하는 것은 좋지 않다. 중요한 것만 강조하자.) 의미 없고 불필요한 색상을 제거하고 명도 변화만 살짝 적용 (중요하지 않은 곳이 시선을 끌지 않도록 하자.) (더 자세한 내용은 원본 비디오 참고) 기타 참고 스크린샷 (원본 비디오에는 더 많은 예시가 있습니다.) 그림2. 표의 테두리가 배경과 명도 대비가 낮고 얇을 수록 내용이 더 잘 들어온다. (표의 불필요한 여백도 줄일 수 있게 된다.) 그림3. 크기가 같은 도형이라도 모서리가 둥글면 도형에 사용되는 픽셀이 더 많아져서 더 커보이고 더 잘보인다. 그림4. 뾰족한 곳은 시선을 끈다. 중요한 곳을 강조하려면 상자에서 중요한 곳만 뾰족하게 하고 다른 모서리는 둥글게 처리한다. 그림5. 버튼 이미지는 채우는 것이 더 좋다. 그림6. 버튼의 모서리는 둥근 것이 더 좋다. << UX Summit 2020 목록으로 이동 View full 아티클
  24. << UX Summit 2020 목록으로 이동 UX Summit 의 2020 시리즈 중, Effective UX Design for Multiplatform Apps - Miguel Angel Moreno 의 한글 요약본입니다. 발표자 (Miguel Angel Moreno)는 매우 넓고 풍부한 경험을 가진 엠바카데로 MVP 입니다. 이 요약에 미처 담지 못한 좋은 내용은 원본을 보세요: YouTube 비디오 (24 min) 보기 이 세션은 델파이 개발자가 데스크탑 앱을 개발할 때 유용한 UX 구현 가이드라인을 제시합니다. 이 세션을 통해 우리는, 모바일 UX와 데스크탑 UX가 어떻게 달라야 하는지 알 수 있고, UX와 UI를 더 이상 혼동하지 않을 수 있습니다. 목차 UI vs UX: 역설 내 UI는 이미 좋다. 그런데 왜 UX를 고려해야 하나? “모바일 우선” 접근 방식이 “데스크탑에는 최악”이 되는 경우 데스크탑은 리소스가 더 많다 데스크탑 UX 향상하기 1: 애플리케이션 설치 경험을 향상 데스크탑 UX 향상하기 2: 애플리케이션 시작 경험을 향상 데스크탑 UX 향상하기 3: 애플리케이션 안에서 화면 이동 경험을 향상 데스크탑 UX 향상하기 4: 다중 작업 능력을 활용 데스크탑 UX 향상하기 5: 데이터 확보 능력을 활용 데스크탑 UX 향상하기 6: 사용자 지원 능력을 향상 요약: 좋은 UX의 기본 요소 관련 추천 자료 UI vs UX: 역설 많은 개발자들이 UI와 UX를 바꿔쓰거나 부정확하게 사용한다. UX는 사용성(usability)이 아니다. 사용성은 UX의 일부일 뿐이다. 사용성은 UI의 성질 중의 하나로서 시스템이 배우기 쉬운지, 효율적으로 사용할 수 있는지, 사용할 때 즐거운지 등을 커버하는 UX의 한 부분이다. 소프트웨어 UX란 사용자가 소프트웨어를 고르고, 확보하고, 설치하고, 사용하고, 고객 지원을 받는 전 과정의 모든 것을 의미한다. UX란? UX(User eXperience) 라는 용어를 만든 Don Norman의 정의 (비디오 2분 08초부터의 내용) “사용자 경험 (UX, User Experience)는 최종 사용자가 "회사", "그 회사의 서비스", "그 회사의 제품"과 상호작용하는 모든 요소를 아우른다.” - Don Norman 그림1. Don Norman이 UX라는 용어를 만든 계기 - 그는 산업 디자인, 그래픽, 접점(Interface), 물리적 상호작용, 설명서 등 사람이 어느 시스템을 대하면서 경험하게 되는 모든 요소를 커버하는 용어를 원했는데 인간 접점(Human interface)이나 사용성(Usability)는 너무 좁은 의미만 담을 수 있었다. UX는 그 용어가 생기기 전부터 이미 있었다. (비디오 2분 30초부터의 내용) 그림2. UX는 그 용어가 생기기 전부터 이미 있었다. “디즈니 월드는 언제나 사람의 생활을 향상시킬 수 있는 최신 기술들이 모여있는 곳” - 월트 디즈니 명심할 점: 사용자들이 당신의 제품을 사용하면서 어떻게 느끼는가에 관한 것이 바로 UX이다. 내 UI는 이미 좋다. 그런데 왜 UX를 고려해야 하나? (비디오 3분 00초 부터의 내용) UI UX 대상 UI는 최종 사용자가 제품과 상호작용하는 성질을 대상으로 한다. UX는 제품의 목적과 기능을 대상으로 한다. 작업 UI 디자인은 최종 사용자가 보고, 듣고, 느끼는 것에 영향을 주는 작업이다. 제품의 디자인과 인터페이스에 연결된 미적인 구성 요소를 다룬다. UX 디자인은 시장 조사, 고객 니즈 파악을 위한 고객과의 커뮤니케이션 등 사회적인 구성 요소를 다루는 작업이다. 초점 UI는 최종 제품에 집중한다. (디자인 구성 요소를 이루는 기술 구성 요소를 주로 다룬다.) UX는 관리와 분석에 집중한다. (아이디어 창출, 개발, 전달까지 모든 프로젝트 단계를 다룬다.) 그림3. (완성된 제품인) 숟가락이 UI라면, (식사를 즐긴다는 과정, 즉) 숟가락을 사용하는 것은 UX 프로세스의 일부이다. 그림4. 더 좋은 UX를 제공하면 더 많은 고객이 관심을 가진다. (똑같은 케찹이지만, 사용 방식이 다르면 UX가 달라진다.) 그림5. 공사해서 만든 보도가 UI라면, 실제로 지나다니는 사람들이 만든 지름길은 UX이다. (보도가 지름길보다 보기에 더 멋지다. 즉, UI가 더 좋다. 하지만, 사용자에게 최고의 UX는 아니었기 때문에 사람들은 자신들의 UX를 만들었다.) 스마트폰이 널리 보급됨에 따라, “모바일 우선”현상이 심해지고 데스크탑의 UX는 지금 나쁘게 변해가고 있다. 사례 1: “모바일 우선” 접근 방식이 “데스크탑에는 최악”이 되는 경우 - 구글앱스 (단일화된 UI만 추구) (비디오 5분 23초부터의 내용) 그림6. 구글앱스의 데스크탑 UX는 "모바일 우선"이라는 원칙을 지키기 위해 데스크탑이 제공할 수 있는 장점을 희생했다. (구글의 결정에 따라) 구글앱스에서 작업을 시작하려면, 일단 "햄버거 아이콘"을 사용하여 서랍 메뉴를 열어야 한다. (그 결과 데스크탑에서도 클릭을 2회 이상 해야한다.) 메뉴와 기능이 항상 보인다면 클릭을 1회만 하는게 더 좋은 UX가 아닐까? 참고. 구글 CEO 에릭 슈미트의 모바일 퍼스트 철학: “모든 것에서 모바일 우선, 애플리케이션도 모바일 우선, 사람들이 사물을 사용하는 방식도 모바일 우선” 사례 2: “모바일 우선” 접근 방식이 “데스크탑에는 최악”이 되는 경우 - 윈도우 8 (비디오 6분 51초부터의 내용) 그림7. 윈도우 8 데스크탑 UX는 "터치 장비 사용자를 고려"했는데, 키보드와 마우스 사용자에게는 어색했다. 윈도우 8은 데스크탑의 커서 방식과 태블릿의 터치 방식을 모두 지원하도록 UI를 구현하였다. The Verge의 평가: “윈도우 8을 되돌아보면, 마이크로소프트가 당시에 잘못된 길을 선택했음을 쉽게 알 수 있다. 터치 기반 컴퓨팅에 크게 배팅했지만 결국 PC의 키보드와 마우스 사용이 어색하고, 불편하고 혼란스럽게 만들어버렸다.” 사례 3: “모바일 우선” 접근 방식이 “데스크탑에는 최악”이 되는 경우 - 웹사이트의 미니멀리즘 (비디오 7분 35초부터의 내용) 그림8. 미니멀리즘은 화면이 작은 모바일에서는 실용적이지만, 화면이 큰 데스크탑에서는 효율적이지 않다. 웹사이트가 점점 “모바일 미니멀리즘”에 맞추기 위해 새로 디자인된다. 그 결과 데스크탑의 장점을 희생한다. 데스크탑에서는 더 많은 내용을 한번에 표현할 수 있는데도 미니멀리즘을 추구하느라 많은 내용을 숨긴다. (예, 요소와 메뉴를 아이콘 안에 숨기기, 내용의 앞부분만 보여주기) 모바일 디자인이 미니멀리즘에 기초하는 이유는 모바일에서 잘 사용하기 위해 꼭 필요하기 때문이다. (데스크탑에서 사용할 때는 굳이 필요하지 않을 수도 있다.) 데스크탑은 모바일과 달리 리소스가 더 풍부하기 때문에, 더 수준 높은 것들을 해낼 능력이 있다. 데스크탑 컴퓨터는 모바일 장비보다 리소스가 풍부하다. 스크린의 면적이 넓고 해상도가 높다: 많은 그래픽 요소를 한번에 표현할 수 있다. 메모리와 저장공간이 크다: 방대한 데이터 덩어리를 빠르고 효율적으로 다룰 수 있다. 데스크탑 UX 향상하기 1: 애플리케이션 설치 경험을 향상 “앱스토어”를 사용하자. 앱스토어는 모바일 앱 배포 방식에서 성공했고, 이제 데스크탑 소프트웨어에서도 제공된다. 최종 사용자는 앱스토어에서 매우 편하고 효과적으로 애플리케이션을 찾아서 설치한다. 설치 미디어 제공과 관련된 불필요한 부담이 없어진다. (강력하고 안전한 디지털 서명은 악성 프로그램이나 앱 변조를 제거하므로) 앱스토어에서 앱을 획득하는 것이 더 안전하다. RAD 스튜디오는 (윈도우 스토어와 맥 앱스토어) 데스크탑용 양대 앱스토어 배포를 지원한다. (깃허브에 예제 코드 제공 예정) 데스크탑 UX 향상하기 2: 애플리케이션 시작 경험을 향상 애플리케이션을 작동시키는 경험은 누구나 가장 성가시게 느끼는 데스크탑 UX이다. 시간이 많이 걸리는 FormCreate 프로시저의 코드가 있으면, 시작되기 전에 대체로 모래시계를 표시한다. 최종 사용자는 혼란한 마음에 앱을 수차례 기동하고, 이로 인해 데이터가 꼬이기도 한다. (모래시계만 보이면, 데이터 로딩 중인지, 초기화 작업 중인지 등 앱이 실제로 무슨 작업을 하는 지 알 수 없기 때문이다.)
 이 문제를 해소하는 몇가지 코딩 패턴 스플래쉬 화면을 애플리케이션에 추가하기 (적어도 프로그램이 시작되고 있다는 것을 사용자가 알 수 있다.) 초기화 코드를 별도의 프로시저 안으로 옮기고, 기본 화면이 표시된 다음에 호출되도록 하기 그리고 화면에서 각 초기화 작업의 내용을 알려주기 (예: 데이터 로딩 현황 등을 표시하여 기다릴 수 있도록 안내한다.) 폼 생성 처리를 최소화하기 (폼이 많다면 기본 폼을 제외한 나머지 폼은 실제 사용될 때 생성하도록 한다.) 
 RAD스튜디오의 기본 설정(구동 시 모든 폼 생성)을 그대로 사용하면 폼이 많은 경우 구동 시간이 지연된다. (깃허브에 예제 코드 제공 예정) 데스크탑 UX 향상하기 3: 애플리케이션 안에서 화면 이동 경험을 향상 화면 이동은 데스크탑 앱에서 가장 중요한 UX 특성 중 하나이다. 가능하면 관련된 개체들을 한 화면에서 모두 볼 수 있도록, 화면 공간을 활용하자. (그리드, 리스트, 트리 등) 주의: 많은 것을 한번에 보여주려면 그만큼 화면 구성을 잘 해야 한다. 잘못하면 오히려 혼란에 빠지는 역효과가 날 수 있다. 정보를 파악하기 쉽도록, 서로 관련된 개체들은 페이지나 탭에 모아두고, 각 페이지와 탭은 명확히 분리하자. 이동 버튼과 이동 보조 기능이 언제나 제공되어야 한다. 버튼 뿐만 아니라 키보드로도 쉽게 이동할 수도 있도록 하자. (모바일 UX에는 없는 데스크탑 UX 만의 장점이다.) VCL과 FMX 모두 폼 디자이너와 컴포넌트를 통해 좋은 화면을 구성할 수 있다. (깃허브에 예제 코드 제공 예정) 데스크탑 UX 향상하기 4: 다중 작업 능력을 활용 데스크탑 앱은 멀티-코어 CPU와 풍부한 메모리를 사용한다. 대용량 데이터 로딩이나 집약된 수학 계산 등이 백그라운드에서 실행되도록 멀티 쓰레드와 병렬 처리 사용하자. UX를 훨씬 자연스럽게 해준다. RAD스튜디오의 기능 활용 작업 진행과 종료에 대한 명확한 시각적 표현과 공지를 제공할 수 있도록 다양한 시각적 컴포넌트가 제공된다. (데스크탑이기 때문에 제공할 수 있는 좋은 UX이다.) 멀티태스킹과 병렬 처리를 위한 라이브러리가 제공된다. 익명 메소드(Anonymous method)를 사용하면 멀티 태스킹 프로시저를 더 쉽게 작성하고 관리할 수 있다. (깃허브에 예제 코드 제공 예정) 데스크탑 UX 향상하기 5: 데이터 확보 능력을 활용 데스크탑 앱은 모바일 앱에 비해 훨씬 큰 메모리를 사용할 수 있다. 사용자가 데이터를 읽거나 계층형으로 관련 데이터를 파악할 때 화면 이동이 자연스럽고 즉각 반응하는 것이 좋은 UX이다. 데스크탑에서는, 필요한 기준 데이터를 앱 구동 시 한번에 모두 로딩해 둘 수 있기 때문에 느리게 반응하는 문제를 해소할 수 있다. 반면에 모바일 앱의 경우, 데이터를 보관할 메모리가 충분하지 않으므로, 필요할 때 필요한 것만 가져오고 다 사용하면 버리는 방식이다. (데이터 가져오기는 시간이 많이 걸리는 작업인데 필요할 때마다 가져와야 한다.) SQL 클라이언트가 없기 때문에 대체로 REST 클라이언트가 SOA(Service Oriented Architecture) 서비스 서버에서 데이터를 받아오는 방식 등 제한된 방식으로 데이터를 다루게 된다. RAD 스튜디오의 FireDAC (데이터 엔진 클라이언트 라이브러리) 활용 로컬과 원격 데이터를 모두 빠르고 효율적으로 로드할 수 있다. 데이터 집약적인 상황에서는, 사용자가 이동하는 레코드에 따라 데이터가 순차적으로 로딩할 수 있다. FireDAC은 데이터를 필요한 것부터 로드한다. 한번 로드한 데이터는 버리지 않고 증분 방식으로 계속 새 데이터를 읽어들일 수 있다. 모바일 앱보다 훨씬 부드러운 UX를 제공하기에 적합하다. (깃허브에 예제 코드 제공 예정) 데스크탑 UX 향상 6: 사용자 지원 능력을 향상 데스크탑 앱은 화면 공간이 충분하므로 메시지 창을 제공하여도 주요 화면을 가리지 않는다. 슬랙 등 원하는 메시징 플랫폼을 사용하여 고객이 원할 때 즉시, 실시간 고객 지원을 하도록 구현한다. 필요시 원격 데스크탑 기능 등을 연결하면 사용자의 화면을 직접 보면서 사용자의 문제를 바로 해결하도록 구현한다. 사용자가 전화하고, 기다릴 필요가 없다. 이메일이나 화면 스크린샷 등도 필요없다. 이와 같은 불필요한 과정을 제거하면 현격하게 훌륭한 UX를 제공한다. “온라인 지원” 요청 버튼을 언제든 쉽게 찾을 수 있도록 애플리케이션 안에 구현한다. 그러면 사용자들은 쉽게 그 자리에서 서비스를 받을 수 있다. RAD 스튜디오의 기능 활용 TCP 또는 UDP 프로토콜을 직접 사용하는 간단한 메시징 솔루션을 구현할 수 있도록 컴포넌트가 제공된다. 슬랙 등 널리 사용되는 메시징 플랫폼의 REST API에 쉽게 연결할 수 있도록 REST 클라이언트 컴포넌트가 제공된다. (깃허브에 예제 코드 제공 예정) 요약: 좋은 UX의 기본 요소 사용자를 이해하자 사업의 타겟 고객을 명확히하고, 그들이 원하는 것과 필요로 하는 것을 이해하는 것이 목표이다. UX에 가장 필요한 능력은 공감 능력이다. UX 디자이너는 최종 사용자들이 필요로 하는 것과 그들의 감정을 이해하는 능력이 뛰어나야 한다. 디자인 전략을 수립하자 디자인 전략 안에는 제품의 목적을 이해하고 논리적인 사용 흐름의 맵을 만드는 과정이 들어가야 한다. 상호작용 설계를 분석하자 사용자들이 제품을 어떻게 사용하는 지 분석 사용 습관, 개인적 선호도 분석 UI와 상호작용하면서 어떤 지름길(바로가기)를 사용하는지 분석 와이어 프레임이나 프로토타입을 작성하자 UX 디자이너는 디자인 팀에게 아이디어와 그 목적을 전달하기 위해 와이어 프레임이나 프로토타입을 작성한다. 델파이는 프로토타입을 빠르게 만들 수 있다. (델파이 사용자라면 누구나 아는 사실) 관련 추천 자료 “UX” 용어 이해 - Don Norman (영문 비디오, 2min) The Design of Everyday Things - Don Norman (도서) 비전부터 UX 디자인까지 (From Vision to UX Design) with RAD Studio - S.듀폰트, C.자브로키스 (영문 비디오, 23min) (훌륭한 UX를 갖춘) 훌륭한 UI를 디자인 하는 방법 - D.밀링턴 (영문 아티클, 3부작) 코드 예제 (추후 업로드 예정) UX라는 용어가 생기기 전의 역사 (짧은 블로그와 비디오, 역자 추천) << UX Summit 2020 목록으로 이동
  25. << UX Summit 2020 목록으로 이동 UX Summit 의 2020 시리즈 중, Effective UX Design for Multiplatform Apps - Miguel Angel Moreno 의 한글 요약본입니다. 발표자 (Miguel Angel Moreno)는 매우 넓고 풍부한 경험을 가진 엠바카데로 MVP 입니다. 이 요약에 미처 담지 못한 좋은 내용은 원본을 보세요: YouTube 비디오 (24 min) 보기 이 세션은 델파이 개발자가 데스크탑 앱을 개발할 때 유용한 UX 구현 가이드라인을 제시합니다. 이 세션을 통해 우리는, 모바일 UX와 데스크탑 UX가 어떻게 달라야 하는지 알 수 있고, UX와 UI를 더 이상 혼동하지 않을 수 있습니다. 목차 UI vs UX: 역설 내 UI는 이미 좋다. 그런데 왜 UX를 고려해야 하나? “모바일 우선” 접근 방식이 “데스크탑에는 최악”이 되는 경우 데스크탑은 리소스가 더 많다 데스크탑 UX 향상하기 1: 애플리케이션 설치 경험을 향상 데스크탑 UX 향상하기 2: 애플리케이션 시작 경험을 향상 데스크탑 UX 향상하기 3: 애플리케이션 안에서 화면 이동 경험을 향상 데스크탑 UX 향상하기 4: 다중 작업 능력을 활용 데스크탑 UX 향상하기 5: 데이터 확보 능력을 활용 데스크탑 UX 향상하기 6: 사용자 지원 능력을 향상 요약: 좋은 UX의 기본 요소 관련 추천 자료 UI vs UX: 역설 많은 개발자들이 UI와 UX를 바꿔쓰거나 부정확하게 사용한다. UX는 사용성(usability)이 아니다. 사용성은 UX의 일부일 뿐이다. 사용성은 UI의 성질 중의 하나로서 시스템이 배우기 쉬운지, 효율적으로 사용할 수 있는지, 사용할 때 즐거운지 등을 커버하는 UX의 한 부분이다. 소프트웨어 UX란 사용자가 소프트웨어를 고르고, 확보하고, 설치하고, 사용하고, 고객 지원을 받는 전 과정의 모든 것을 의미한다. UX란? UX(User eXperience) 라는 용어를 만든 Don Norman의 정의 (비디오 2분 08초부터의 내용) “사용자 경험 (UX, User Experience)는 최종 사용자가 "회사", "그 회사의 서비스", "그 회사의 제품"과 상호작용하는 모든 요소를 아우른다.” - Don Norman 그림1. Don Norman이 UX라는 용어를 만든 계기 - 그는 산업 디자인, 그래픽, 접점(Interface), 물리적 상호작용, 설명서 등 사람이 어느 시스템을 대하면서 경험하게 되는 모든 요소를 커버하는 용어를 원했는데 인간 접점(Human interface)이나 사용성(Usability)는 너무 좁은 의미만 담을 수 있었다. UX는 그 용어가 생기기 전부터 이미 있었다. (비디오 2분 30초부터의 내용) 그림2. UX는 그 용어가 생기기 전부터 이미 있었다. “디즈니 월드는 언제나 사람의 생활을 향상시킬 수 있는 최신 기술들이 모여있는 곳” - 월트 디즈니 명심할 점: 사용자들이 당신의 제품을 사용하면서 어떻게 느끼는가에 관한 것이 바로 UX이다. 내 UI는 이미 좋다. 그런데 왜 UX를 고려해야 하나? (비디오 3분 00초 부터의 내용) UI UX 대상 UI는 최종 사용자가 제품과 상호작용하는 성질을 대상으로 한다. UX는 제품의 목적과 기능을 대상으로 한다. 작업 UI 디자인은 최종 사용자가 보고, 듣고, 느끼는 것에 영향을 주는 작업이다. 제품의 디자인과 인터페이스에 연결된 미적인 구성 요소를 다룬다. UX 디자인은 시장 조사, 고객 니즈 파악을 위한 고객과의 커뮤니케이션 등 사회적인 구성 요소를 다루는 작업이다. 초점 UI는 최종 제품에 집중한다. (디자인 구성 요소를 이루는 기술 구성 요소를 주로 다룬다.) UX는 관리와 분석에 집중한다. (아이디어 창출, 개발, 전달까지 모든 프로젝트 단계를 다룬다.) 그림3. (완성된 제품인) 숟가락이 UI라면, (식사를 즐긴다는 과정, 즉) 숟가락을 사용하는 것은 UX 프로세스의 일부이다. 그림4. 더 좋은 UX를 제공하면 더 많은 고객이 관심을 가진다. (똑같은 케찹이지만, 사용 방식이 다르면 UX가 달라진다.) 그림5. 공사해서 만든 보도가 UI라면, 실제로 지나다니는 사람들이 만든 지름길은 UX이다. (보도가 지름길보다 보기에 더 멋지다. 즉, UI가 더 좋다. 하지만, 사용자에게 최고의 UX는 아니었기 때문에 사람들은 자신들의 UX를 만들었다.) 스마트폰이 널리 보급됨에 따라, “모바일 우선”현상이 심해지고 데스크탑의 UX는 지금 나쁘게 변해가고 있다. 사례 1: “모바일 우선” 접근 방식이 “데스크탑에는 최악”이 되는 경우 - 구글앱스 (단일화된 UI만 추구) (비디오 5분 23초부터의 내용) 그림6. 구글앱스의 데스크탑 UX는 "모바일 우선"이라는 원칙을 지키기 위해 데스크탑이 제공할 수 있는 장점을 희생했다. (구글의 결정에 따라) 구글앱스에서 작업을 시작하려면, 일단 "햄버거 아이콘"을 사용하여 서랍 메뉴를 열어야 한다. (그 결과 데스크탑에서도 클릭을 2회 이상 해야한다.) 메뉴와 기능이 항상 보인다면 클릭을 1회만 하는게 더 좋은 UX가 아닐까? 참고. 구글 CEO 에릭 슈미트의 모바일 퍼스트 철학: “모든 것에서 모바일 우선, 애플리케이션도 모바일 우선, 사람들이 사물을 사용하는 방식도 모바일 우선” 사례 2: “모바일 우선” 접근 방식이 “데스크탑에는 최악”이 되는 경우 - 윈도우 8 (비디오 6분 51초부터의 내용) 그림7. 윈도우 8 데스크탑 UX는 "터치 장비 사용자를 고려"했는데, 키보드와 마우스 사용자에게는 어색했다. 윈도우 8은 데스크탑의 커서 방식과 태블릿의 터치 방식을 모두 지원하도록 UI를 구현하였다. The Verge의 평가: “윈도우 8을 되돌아보면, 마이크로소프트가 당시에 잘못된 길을 선택했음을 쉽게 알 수 있다. 터치 기반 컴퓨팅에 크게 배팅했지만 결국 PC의 키보드와 마우스 사용이 어색하고, 불편하고 혼란스럽게 만들어버렸다.” 사례 3: “모바일 우선” 접근 방식이 “데스크탑에는 최악”이 되는 경우 - 웹사이트의 미니멀리즘 (비디오 7분 35초부터의 내용) 그림8. 미니멀리즘은 화면이 작은 모바일에서는 실용적이지만, 화면이 큰 데스크탑에서는 효율적이지 않다. 웹사이트가 점점 “모바일 미니멀리즘”에 맞추기 위해 새로 디자인된다. 그 결과 데스크탑의 장점을 희생한다. 데스크탑에서는 더 많은 내용을 한번에 표현할 수 있는데도 미니멀리즘을 추구하느라 많은 내용을 숨긴다. (예, 요소와 메뉴를 아이콘 안에 숨기기, 내용의 앞부분만 보여주기) 모바일 디자인이 미니멀리즘에 기초하는 이유는 모바일에서 잘 사용하기 위해 꼭 필요하기 때문이다. (데스크탑에서 사용할 때는 굳이 필요하지 않을 수도 있다.) 데스크탑은 모바일과 달리 리소스가 더 풍부하기 때문에, 더 수준 높은 것들을 해낼 능력이 있다. 데스크탑 컴퓨터는 모바일 장비보다 리소스가 풍부하다. 스크린의 면적이 넓고 해상도가 높다: 많은 그래픽 요소를 한번에 표현할 수 있다. 메모리와 저장공간이 크다: 방대한 데이터 덩어리를 빠르고 효율적으로 다룰 수 있다. 데스크탑 UX 향상하기 1: 애플리케이션 설치 경험을 향상 “앱스토어”를 사용하자. 앱스토어는 모바일 앱 배포 방식에서 성공했고, 이제 데스크탑 소프트웨어에서도 제공된다. 최종 사용자는 앱스토어에서 매우 편하고 효과적으로 애플리케이션을 찾아서 설치한다. 설치 미디어 제공과 관련된 불필요한 부담이 없어진다. (강력하고 안전한 디지털 서명은 악성 프로그램이나 앱 변조를 제거하므로) 앱스토어에서 앱을 획득하는 것이 더 안전하다. RAD 스튜디오는 (윈도우 스토어와 맥 앱스토어) 데스크탑용 양대 앱스토어 배포를 지원한다. (깃허브에 예제 코드 제공 예정) 데스크탑 UX 향상하기 2: 애플리케이션 시작 경험을 향상 애플리케이션을 작동시키는 경험은 누구나 가장 성가시게 느끼는 데스크탑 UX이다. 시간이 많이 걸리는 FormCreate 프로시저의 코드가 있으면, 시작되기 전에 대체로 모래시계를 표시한다. 최종 사용자는 혼란한 마음에 앱을 수차례 기동하고, 이로 인해 데이터가 꼬이기도 한다. (모래시계만 보이면, 데이터 로딩 중인지, 초기화 작업 중인지 등 앱이 실제로 무슨 작업을 하는 지 알 수 없기 때문이다.)
 이 문제를 해소하는 몇가지 코딩 패턴 스플래쉬 화면을 애플리케이션에 추가하기 (적어도 프로그램이 시작되고 있다는 것을 사용자가 알 수 있다.) 초기화 코드를 별도의 프로시저 안으로 옮기고, 기본 화면이 표시된 다음에 호출되도록 하기 그리고 화면에서 각 초기화 작업의 내용을 알려주기 (예: 데이터 로딩 현황 등을 표시하여 기다릴 수 있도록 안내한다.) 폼 생성 처리를 최소화하기 (폼이 많다면 기본 폼을 제외한 나머지 폼은 실제 사용될 때 생성하도록 한다.) 
 RAD스튜디오의 기본 설정(구동 시 모든 폼 생성)을 그대로 사용하면 폼이 많은 경우 구동 시간이 지연된다. (깃허브에 예제 코드 제공 예정) 데스크탑 UX 향상하기 3: 애플리케이션 안에서 화면 이동 경험을 향상 화면 이동은 데스크탑 앱에서 가장 중요한 UX 특성 중 하나이다. 가능하면 관련된 개체들을 한 화면에서 모두 볼 수 있도록, 화면 공간을 활용하자. (그리드, 리스트, 트리 등) 주의: 많은 것을 한번에 보여주려면 그만큼 화면 구성을 잘 해야 한다. 잘못하면 오히려 혼란에 빠지는 역효과가 날 수 있다. 정보를 파악하기 쉽도록, 서로 관련된 개체들은 페이지나 탭에 모아두고, 각 페이지와 탭은 명확히 분리하자. 이동 버튼과 이동 보조 기능이 언제나 제공되어야 한다. 버튼 뿐만 아니라 키보드로도 쉽게 이동할 수도 있도록 하자. (모바일 UX에는 없는 데스크탑 UX 만의 장점이다.) VCL과 FMX 모두 폼 디자이너와 컴포넌트를 통해 좋은 화면을 구성할 수 있다. (깃허브에 예제 코드 제공 예정) 데스크탑 UX 향상하기 4: 다중 작업 능력을 활용 데스크탑 앱은 멀티-코어 CPU와 풍부한 메모리를 사용한다. 대용량 데이터 로딩이나 집약된 수학 계산 등이 백그라운드에서 실행되도록 멀티 쓰레드와 병렬 처리 사용하자. UX를 훨씬 자연스럽게 해준다. RAD스튜디오의 기능 활용 작업 진행과 종료에 대한 명확한 시각적 표현과 공지를 제공할 수 있도록 다양한 시각적 컴포넌트가 제공된다. (데스크탑이기 때문에 제공할 수 있는 좋은 UX이다.) 멀티태스킹과 병렬 처리를 위한 라이브러리가 제공된다. 익명 메소드(Anonymous method)를 사용하면 멀티 태스킹 프로시저를 더 쉽게 작성하고 관리할 수 있다. (깃허브에 예제 코드 제공 예정) 데스크탑 UX 향상하기 5: 데이터 확보 능력을 활용 데스크탑 앱은 모바일 앱에 비해 훨씬 큰 메모리를 사용할 수 있다. 사용자가 데이터를 읽거나 계층형으로 관련 데이터를 파악할 때 화면 이동이 자연스럽고 즉각 반응하는 것이 좋은 UX이다. 데스크탑에서는, 필요한 기준 데이터를 앱 구동 시 한번에 모두 로딩해 둘 수 있기 때문에 느리게 반응하는 문제를 해소할 수 있다. 반면에 모바일 앱의 경우, 데이터를 보관할 메모리가 충분하지 않으므로, 필요할 때 필요한 것만 가져오고 다 사용하면 버리는 방식이다. (데이터 가져오기는 시간이 많이 걸리는 작업인데 필요할 때마다 가져와야 한다.) SQL 클라이언트가 없기 때문에 대체로 REST 클라이언트가 SOA(Service Oriented Architecture) 서비스 서버에서 데이터를 받아오는 방식 등 제한된 방식으로 데이터를 다루게 된다. RAD 스튜디오의 FireDAC (데이터 엔진 클라이언트 라이브러리) 활용 로컬과 원격 데이터를 모두 빠르고 효율적으로 로드할 수 있다. 데이터 집약적인 상황에서는, 사용자가 이동하는 레코드에 따라 데이터가 순차적으로 로딩할 수 있다. FireDAC은 데이터를 필요한 것부터 로드한다. 한번 로드한 데이터는 버리지 않고 증분 방식으로 계속 새 데이터를 읽어들일 수 있다. 모바일 앱보다 훨씬 부드러운 UX를 제공하기에 적합하다. (깃허브에 예제 코드 제공 예정) 데스크탑 UX 향상 6: 사용자 지원 능력을 향상 데스크탑 앱은 화면 공간이 충분하므로 메시지 창을 제공하여도 주요 화면을 가리지 않는다. 슬랙 등 원하는 메시징 플랫폼을 사용하여 고객이 원할 때 즉시, 실시간 고객 지원을 하도록 구현한다. 필요시 원격 데스크탑 기능 등을 연결하면 사용자의 화면을 직접 보면서 사용자의 문제를 바로 해결하도록 구현한다. 사용자가 전화하고, 기다릴 필요가 없다. 이메일이나 화면 스크린샷 등도 필요없다. 이와 같은 불필요한 과정을 제거하면 현격하게 훌륭한 UX를 제공한다. “온라인 지원” 요청 버튼을 언제든 쉽게 찾을 수 있도록 애플리케이션 안에 구현한다. 그러면 사용자들은 쉽게 그 자리에서 서비스를 받을 수 있다. RAD 스튜디오의 기능 활용 TCP 또는 UDP 프로토콜을 직접 사용하는 간단한 메시징 솔루션을 구현할 수 있도록 컴포넌트가 제공된다. 슬랙 등 널리 사용되는 메시징 플랫폼의 REST API에 쉽게 연결할 수 있도록 REST 클라이언트 컴포넌트가 제공된다. (깃허브에 예제 코드 제공 예정) 요약: 좋은 UX의 기본 요소 사용자를 이해하자 사업의 타겟 고객을 명확히하고, 그들이 원하는 것과 필요로 하는 것을 이해하는 것이 목표이다. UX에 가장 필요한 능력은 공감 능력이다. UX 디자이너는 최종 사용자들이 필요로 하는 것과 그들의 감정을 이해하는 능력이 뛰어나야 한다. 디자인 전략을 수립하자 디자인 전략 안에는 제품의 목적을 이해하고 논리적인 사용 흐름의 맵을 만드는 과정이 들어가야 한다. 상호작용 설계를 분석하자 사용자들이 제품을 어떻게 사용하는 지 분석 사용 습관, 개인적 선호도 분석 UI와 상호작용하면서 어떤 지름길(바로가기)를 사용하는지 분석 와이어 프레임이나 프로토타입을 작성하자 UX 디자이너는 디자인 팀에게 아이디어와 그 목적을 전달하기 위해 와이어 프레임이나 프로토타입을 작성한다. 델파이는 프로토타입을 빠르게 만들 수 있다. (델파이 사용자라면 누구나 아는 사실) 관련 추천 자료 “UX” 용어 이해 - Don Norman (영문 비디오, 2min) The Design of Everyday Things - Don Norman (도서) 비전부터 UX 디자인까지 (From Vision to UX Design) with RAD Studio - S.듀폰트, C.자브로키스 (영문 비디오, 23min) (훌륭한 UX를 갖춘) 훌륭한 UI를 디자인 하는 방법 - D.밀링턴 (영문 아티클, 3부작) 코드 예제 (추후 업로드 예정) UX라는 용어가 생기기 전의 역사 (짧은 블로그와 비디오, 역자 추천) << UX Summit 2020 목록으로 이동 View full 아티클
×
×
  • Create New...

중요한 정보

이용약관 개인정보보호정책 이용규칙 이 사이트가 더 잘 작동하기 위해 방문자의 컴퓨터에 쿠키가 배치됩니다. 쿠키 설정 변경에서 원하는 설정을 할 수 있습니다. 변경하지 않으면 쿠키를 허용하는 것으로 이해합니다.