저는 C++ 임베디드 프로젝트를 C#으로 마이그레이션하고 있으며, 팀은 UI를 위해 WPF를 사용할 것을 고려하고 있습니다. 조사하는 동안 많은 개발자들이 WPF에서 MVVM 패턴을 사용한다는 것을 알게 되었습니다. 하지만 코드-비하인드 방식으로도 WPF를 사용할 수 있다는 것도 알게 되었습니다.
임베디드 프로젝트에서 어떤 접근 방식이 더 일반적으로 사용되고 더 효율적인지 궁금합니다. 제 초기 인상은 MVVM 패턴이 주로 웹 애플리케이션에 사용된다는 것입니다. 우리 팀에서는 한 사람이 UI와 백엔드 코드를 모두 처리하게 될 가능성이 큽니다.
우리는 현대적이고 미적인 UI를 원하기 때문에(WinForms는 구식으로 보입니다), 어떤 접근 방식이 더 나을까요? WPF에서 MVVM 패턴을 사용하는 것과 코드-비하인드 방식을 사용하는 것 중 어떤 것이 더 좋을지에 대한 조언이나 추천을 부탁드립니다. 감사합니다!
MVVM 은 학습장벽이 있을 수 있어요. 단순히 디자인만 원하신다면 xaml 로 디자인 + 코드 비하인드로 액션 처리 정도만 해도 쓸만합니다.
그리고 무럭무럭 코드가 자라나서 데이터 핸들링이나 view 가 섞여들어간 로직 관리에 어려움을 느끼기 시작하면 MVVM 을 적용해볼까… 하는 시기가 올 거구요.
그 때가서 하나씩 바꿔나가도 됩니다.(쉽다고는 안 했…)
덧,
MVVM 의 원조는 WPF 라고 보는 게 맞을 거 같구용.
웹에서 많이 사용된다는 얘기는 저는 자주 못들어본 거라… 대부분 웹 프론트엔드 프레임워크는 자체적인 철학들이 있던데 MVVM 은 잘 없더군요. knockout.js 정도?
안드로이드나 아이폰 어플에서 가끔 쓰는 정도는 들어봤어요. ㅇㅅㅇ!
특별한 상황이 아니라면 대부분 MVVM을 쓰시는 것이 단기적으로나 장기적으로나 유지보수하는데 도움이 됩니다.
그러나 정말 특수한 상황이라 임베디드에서 실시간으로 데이터를 그리는 것이 필요하시다면 MVVM처럼 여러번 라우팅을 거쳐서 돌아가는 메커니즘 대신, Direct2D 같은 렌더링 기술을 검토해보실 수 있을 것 같습니다. (다만 짐작하시겠지만 MVVM과는 완전히 다른 로우레벨 기술입니다.)
예를 들어 SharpDX와 Windows Forms Rendering Area를 통해 다음과 같은 식으로 코드 구현을 시작할 수 있겠습니다.
using System;
using System.Windows.Forms;
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.DXGI;
using SharpDX.Direct3D11;
using SharpDX.Mathematics.Interop;
using Device = SharpDX.Direct3D11.Device;
using Factory = SharpDX.Direct2D1.Factory;
using FeatureLevel = SharpDX.Direct3D.FeatureLevel;
using SwapChain = SharpDX.DXGI.SwapChain;
using SwapChainDescription = SharpDX.DXGI.SwapChainDescription;
public class Direct2DForm : Form
{
private Factory _factory;
private Device _device;
private SwapChain _swapChain;
private RenderTarget _renderTarget;
private SolidColorBrush _brush;
public Direct2DForm()
{
InitializeDirect2D();
}
private void InitializeDirect2D()
{
_factory = new Factory();
var swapChainDesc = new SwapChainDescription()
{
BufferCount = 1,
ModeDescription = new ModeDescription(ClientSize.Width, ClientSize.Height, new Rational(60, 1), Format.R8G8B8A8_UNorm),
Usage = Usage.RenderTargetOutput,
OutputHandle = Handle,
SampleDescription = new SampleDescription(1, 0),
IsWindowed = true
};
Device.CreateWithSwapChain(SharpDX.Direct3D.DriverType.Hardware, DeviceCreationFlags.BgraSupport, swapChainDesc, out _device, out _swapChain);
using (var backBuffer = _swapChain.GetBackBuffer<SharpDX.Direct3D11.Texture2D>(0))
{
var surface = backBuffer.QueryInterface<Surface>();
_renderTarget = new RenderTarget(_factory, surface, new RenderTargetProperties(new PixelFormat(Format.Unknown, AlphaMode.Premultiplied)));
}
_brush = new SolidColorBrush(_renderTarget, new RawColor4(1, 0, 0, 1));
}
protected override void OnPaint(PaintEventArgs e)
{
_renderTarget.BeginDraw();
_renderTarget.Clear(new RawColor4(0, 0, 0, 1));
_renderTarget.DrawEllipse(new Ellipse(new RawVector2(ClientSize.Width / 2, ClientSize.Height / 2), 100, 100), _brush, 10);
_renderTarget.EndDraw();
_swapChain.Present(1, PresentFlags.None);
base.OnPaint(e);
}
protected override void OnResize(EventArgs e)
{
_renderTarget.Resize(new Size2(ClientSize.Width, ClientSize.Height));
base.OnResize(e);
}
protected override void Dispose(bool disposing)
{
_brush.Dispose();
_renderTarget.Dispose();
_swapChain.Dispose();
_device.Dispose();
_factory.Dispose();
base.Dispose(disposing);
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Direct2DForm());
}
}
현대적이고 미적인 UI의 의미가 좀 넓어보이지만, UI의 반영이 동적으로 이뤄지면서 관리도 편하게 하고 싶다면 MVVM을 추천드립니다.
제가 본 바로는, 근래에 WPF 관련 대형 오픈소스 라이브러리나 앱들의 코드를 살펴보면 MVVM 구조에서 동작하는 걸 상정한 경우가 많았습니다. 구글에서 UI/UX 관련 정보를 찾아다녀도 MVVM에서의 해결방법이 많구요.
MVVM을 쓰느냐는 “임베디드” 같이 분야와는 별로 관련이 없을 듯 합니다. 그 대신, View에서 다룰 데이터가 많고, 그 데이터들의 변화를 반영하는 작업이 앱에서 많은 비중을 차지하고 있느냐로 판단하는게 좋지 않을까 생각합니다.
(다만 임베디드 앱이라면 분명 장치 제어 같은걸 하는걸텐데, 그러면 MVVM 기반으로 개발하는게 편하지 않을까 싶네요)
중요한 점은, MVVM의 learning-curve는 확실히 가파르고, 개발에 참여하는 사람이 모두 MVVM 패턴을 이해해야 합니다.
그런 점에서 본 포럼에서 연재되었던 MVVM 관련 포스트를 참고하는걸 추천드립니다. 저도 이걸로 많은 공부가 되었습니다.