KEMP.Zipper란?
KEMP engine (Mars ver)에서 운용할 binary를 압축하는 application입니다.
본연의 압축 기능 이외에 지난 버전에서 문제된 Customizing의 어려움, serialize 요소 부재 등을 해결하기로 하였고,
지난 UWP project에서 활용한 MVVM pattern을 적용시켜보기 위해서, WinForm이 아닌 WPF platform의 practice를 만들어 보기로 하였습니다.
기능 개발 외적인 목표
새로운 platform으로 개발하는 만큼, 또한 TDD에 대한 관심과 경험이 어느 정도 쌓인 만큼, project진행 간 기능 개발 외적인 목표를 두었습니다.
1. TDD를 하자
2. WPF를 하자
3. CLI를 만들어 보자
4. Resource 통합 관리를 해보자
5. WPF application 개발 practice를 만들어 보자
결과적으로 WPF는 ModernUI(MUI) Template을 적용하는 등의 목표 대비 추가 달성을 하였으나,
UX 설계 및 테스트 과정에 아쉬움이 남는 project였습니다.
Solution overview
Project |
Description |
Remark |
KEMP.Zipper.App |
WPF app |
MVVM |
KEMP.Zipper.BetaApp |
WPF app (beta) |
개발자 UI |
KEMP.Zipper.CLI |
Command Line Interface |
Console에서 기동(미완) |
KEMP.Zipper.Configuration |
Define configuration |
AppSetting 대체 |
KEMP.Zipper.Core |
Zip, Validation |
압축 및 유효성 검증 로직 구현 |
KEMP.Zipper.Model |
Define model |
Model class 선언 |
KEMP.Zipper.Resources |
Define resource |
Resource 통합 관리 |
KEMP.Zipper.Test |
Test project |
|
KEMP.Zipper.App / BetaApp
과거에 사용 경험이 있던 MVVM light NuGet을 사용하였습니다.
전에는 단순 MVVM pattern 개발을 위해서 사용했는데, ViewModelLocator에서 IoC(Inversion of Control) 처리가 손쉽게 되어 ViewModel에 필요한 Service를 손쉽게 전달할 수 있었습니다.
MVVM light가 제공하는 ViewModelLocator에서 IoC처리에 대한 선언을 하고, ViewModel을 property로 노출시킨 다음, View에서는 ViewModelLocator에서 그 ViewModel의 property를 참조하여 Data binding을 수행합니다.
ViewModel의 생성자에서 필요로하는 service interface는 MVVM의 IoC를 통해서 제공됨으로써 별도의 처리가 필요치 않습니다.
Service와 ViewModel의 분리하여 테스트할 수 있는 장점이 생기게 됩니다.
Metro UI라고도 불리는 Modern UI Template을 적용하였습니다.
이 틀을 벗어나는 UI를 만들기 위해서 Template 자체의 source를 까서 수정해야 했다...
이 Template을 사용함으로써 기존의 Winform application과 시각적으로 큰 차별화를 이루었으나, UI Design이 꽤 제한되는 상황이라 이를 customize하느라고... 상당히 애를 먹었습니다 :-D
https://github.com/firstfloorsoftware/mui/wiki
KEMP.Zipper.Configuration
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="appStructureSection"
type="KEMP.Zipper.Configuration.AppStructureSection, KEMP.Zipper.Configuration" requirePermission="true"
restartOnExternalChanges="true"
allowLocation="true" />
</configSections>
<appStructureSection>
<angular>
<add rootFolder="client"
items="pages;app.js;route.json;index.html;" />
<add rootFolder="server"
items="BLP;Configuration;Configuration\AppSettings.xml;Metadata\...;" />
</angular>
<angular2>
<add rootFolder="client"
items="pages;app.js;route.json;index.html;" />
<add rootFolder="server"
items="BLP;Configuration;Configuration\AppSettings.xml;Metadata\...;" />
</angular2>
</appStructureSection>
</configuration>
자세한 내용은 아래의 link를 참고하시길 바랍니다.
요약하면 appStructureSection element에 대한 설계를 담고 있는 project이며, App.config로부터 해당 내용을 읽어들이는 logic을 포함하고 있습니다.
http://bandcy.tistory.com/entry/ConfigurationSection을-이용한-App-configuration
KEMP.Zipper.Core
public interface IZipper
{
event EventHandler<ProgressEventArgs> ProgressChanged;
void Zip();
void Validate();
string TargetPath { get; set; }
string ProjectPath { get; set; }
string ZipName { get; }
ValidationError[] Messages { get; }
void Serialize();
void Deserialize();
}
본 Application의 주가 되는 기능. 즉, Zip, Validation, Serialize/Deserialize 작업을 수행하는 project입니다.
또한, Zip Progress를 Event로 노출하여 progess의 진행률을 바깥 쪽에서 확인할 수 있도록 하였습니다.
IZipper를 상속받는 Zipper(AngularJs ver), Angular2Zipper(Angular2 ver)용을 구현하였습니다.
KEMP.Zipper.Model
ZipModel, ValidationError 등에 대한 선언을 담고 있습니다.
ZipModel은 사용자가 project path를 전달했을 때, 그 경로를 통해서 company, project 등의 기본적인 정보를 파악하는 기능을 가집니다.
보편적으로 DB작업을 하는 경우, Service, DAO tier에서 공통으로 사용하는 Model을 별도의 project로 분리하는 pattern을 자주 사용했던 패턴입니다.
습관적으로 본 project에서도 Model project를 별도로 분리하였으나, 완료한 현 시점 뒤돌아 보니 굳이 Core와 분리할 필요가 없었을 것 같습니다.
KEMP.Zipper.Resources
사용자에게 노출하는 문장을 통합 관리하기 위한 별도의 project
내부적으로 resource dictionary와 resource key를 인자로 sentence를 반환하는 기능만을 제공함. 이 key value pair를 MJ에게 전달하여 검수했으며, 그 결과를 손쉽게 적용할 수 있었습니다.
향후 다국어를 지원한다면, 당 project를 확장하여 제공하는 것으로 충분할 것으로 기대합니다.
KEMP.Zipper.Test
Project type은 Unit project이며, Test가 반복적으로 사용하는 상수는 별도의 Consts.cs 파일에 취합해 두고 참조하는 형태로 진행하였습니다.Model, Core, App 등의 project를 테스트하기 위한 목적의 project.
코드를 refactoring할 때마다, Run All 수행하면서 수정한 code의 side effect 여부를 체크하였습니다.
모두 녹색 불이 들어왔을 때의 짜릿한 기분이란!!
Slack의 활용
Project 진행 간 공지사항 및 issue 등을 별도의 #kempzipper라는 채널을 통해서 통합관리함으로써, 아 그게 뭐였던라…? 하던 궁금증을 손쉽게 찾아볼 수 있도록 하였습니다. 또한, 나중에 project에 참여한 member도 그 history를 손쉽게 파악 가능할 수 있었다고 생각합니다.
Members
@ally, @fireclon profile 사진 좀 바꿔요...
형상관리의 부재
이런 저런 이유로 형상 관리 시스템을 사용하지 않았는데, TDD에 의거하여 수시로 refactoring하다가 긴급하게 배포해야 할 타이밍에 배포할 수 없는 상황과 마주하게 되었습니다. 막판에 구두로 커뮤니케이션 한 부분에서 미스가 발생했습니다.
남은 시간 동안 마무리 할 수 없는 상황인지라, Beta라는 딱지를 달고 이전에 테스트했던 개발자 UI를 부랴부랴 복구하여 end user에게 전달했습니다.
Refactoring하면서 비난 받던 개발자 UI를 완전히 엎어버린게 얼마나 후회가 되던지...
잦은 refactoring을 수반하는 TDD는 형상관리가 필수적이다라는 것을 깨닫게 되었습니다.
개발자 UI라는 말은 듣기 싫지만, 이렇게 만드는 건 너무 편하다!!
회고
시간이 없어서 TDD를 못한다는 얘기는 자주 듣게되는 변명입니다.
그런데 저도 project 막바지에 App project 부분을 작업할 때, 시간이 없다는 핑계로 TDD를 못했습니다.
MVVM pattern에서 ViewModel에 대한 test practice를 좀 더 연구해 봐야겠습니다.
local file system에 대한 mock을 주입하는 것을 해 보지 못한 것도 매우 아쉬운 부분입니다.
이 화면을 만들기 위해서 나는 그렇게 고생을 했나 보다.
References
https://github.com/firstfloorsoftware/mui/wiki/Screenshots
https://mvvmlight.codeplex.com/