WPF Prism ์—ฐ๊ตฌ

WPF Prism ์—ฐ๊ตฌ


WPF ํ”„๋ ˆ์ž„์›Œํฌ ์ค‘ ๊ฐ€์žฅ ๋Œ€์ค‘์ ์ธ Prism์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์•Œ์•„๋ณด๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋ณธ ์‹œ๋ฆฌ์ฆˆ(Slog)์—์„œ๋Š” WPF Prism์˜ ๊ตฌ์กฐ๋ฅผ ์—ฐ๊ตฌ/๋ถ„์„ํ•˜๊ณ  ๋˜ ์ด์™€ ๊ด€๋ จ๋œ ์ด๋ก ์ด๋‚˜ ๊ฐœ๋…์„ ์—ฐ๊ฒฐ ์ง€์–ด ํ™•์žฅํ•ด ์•Œ์•„๋‚˜๊ฐ€ ๋ณด๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

์™„๋ฃŒ๋œ ์—ฐ๊ตฌ

8๊ฐœ์˜ ์ข‹์•„์š”

1. Prism-Sample-Wpf


Prism ๊ธฐ์ˆ ์„ ๋น ๋ฅด๊ฒŒ ๊ฒ€ํ† ํ•˜๊ณ  ๋„์ž…ํ•˜๊ธฐ ์œ„ํ•œ ํ›Œ๋ฅญํ•œ ๋ฐฉ๋ฒ•์ด ์—ฌ๊ธฐ ์žˆ์Šต๋‹ˆ๋‹ค.

PrismLibrary/Prism-Samples-Wpf
Brian Lagunas ๋‹˜์„ ๋น„๋กฏํ•œ ์—ฌ๋Ÿฌ Prism ๊ธฐ์—ฌ์ž๋“ค์ด GitHub๋ฅผ ํ†ตํ•ด ์ œ๊ณตํ•˜๊ณ  ์žˆ๋Š” ํ›Œ๋ฅญํ•œ ์ƒ˜ํ”Œ ์†Œ์Šค์ฝ”๋“œ ์ž…๋‹ˆ๋‹ค.

์ด ์ƒ˜ํ”Œ ํ”„๋กœ์ ํŠธ์˜ ํŠน์ง•์œผ๋กœ๋Š” ํŠนํžˆ Prism์˜ ์ฃผ์š” ๊ธฐ์ˆ ๋“ค์„ ๊ฐ๊ฐ์˜ ํ”„๋กœ์ ํŠธ๋กœ ๋‚˜๋ˆ„์–ด ์ œ๊ณตํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์Šคํ…์„ ์ˆœ์„œ๋Œ€๋กœ ํ•˜๋‚˜์”ฉ ๋ฐŸ์•„ ๋‚˜์•„๊ฐ€๊ธฐ์— ์•ˆ์„ฑ๋งž์ถค์ž…๋‹ˆ๋‹ค.

์ƒ˜ํ”Œ ๋ชฉ๋ก


(์ˆซ์ž ๋„˜๋ฒ„๋ง์ด ์ด์ƒํ•˜์ง€๋งŒ ์‹ค์ œ ํ”„๋กœ์ ํŠธ ์ด๋ฆ„๊ณผ ๋™์ผํ•˜๋ฉฐ ๊ฐฏ์ˆ˜๋Š” ์ด 29๊ฐœ ์ž…๋‹ˆ๋‹ค.)

5๊ฐœ์˜ ์ข‹์•„์š”

Prism GitHub Repository


PrismLibrary organization์—์„œ ๊ณต์‹์œผ๋กœ ์ œ๊ณตํ•˜๋Š” Prism์€ WPF, Xamarin Froms, Uno ๋“ฑ์˜ ํ”Œ๋ ›ํผ์„ ์ œ๊ณตํ•˜๋ฉฐ ํ•ต์‹ฌ ๊ณต์šฉ ๋ถ€๋ถ„์€ .NET Standard 2.0๋ฅผ ํ†ตํ•ด ๊ตฌํ˜„๋˜์–ด ์žˆ์œผ๋ฉฐ, ๊ทธ ์™ธ ํ”Œ๋žซํผ์— ํŠนํ™”๋œ ๊ธฐ๋Šฅ์€ ๊ฐ๊ฐ ๊ตฌํ˜„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

Prism์€ ์†”๋ฃจ์…˜์ด ์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค.

image

๊ทธ๋ฆฌ๊ณ  Prism์˜ ์ฃผ์š” ๊ธฐ์ˆ ์ธ IoC Container ๊ธฐ์ˆ ์€ ๋ณ„๋„์˜ ํŒจํ‚ค์ง•์œผ๋กœ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. (WPF ๊ธฐ์ค€)

  • Prism.DryIoc
  • Prism.Unity

(7.0 ๋ฒ„์ „๋ถ€ํ„ฐ IoC ๊ด€๋ จ ๋ถ€๋ถ„์ด ๋ถ„๋ฆฌ๋˜์–ด์žˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.)

GitHub ๊ณต์‹ Repository

๊ทธ๋ฆฌ๊ณ  1.5k forks๋ฅผ ์ž๋ž‘ํ•˜๋Š” ์ด ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋Š” ํ™•์‹คํžˆ ํ”„๋ ˆ์ž„์›Œํฌ ์—ฐ๊ตฌ/๋ถ„์„์— ์žˆ์–ด์„œ ์•„์ฃผ ํ•ซํ•˜๊ณ  ๊ท€ํ•œ ์˜คํ”ˆ์†Œ์Šค๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค์–ด์š”. :slight_smile:

3๊ฐœ์˜ ์ข‹์•„์š”

์š”์ฆ˜ ์ œ์ผ ๊ธฐ๋Œ€ ๋˜๋Š” ์Šฌ๋กœ๊ทธ์ž…๋‹ˆ๋‹ค. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

3๊ฐœ์˜ ์ข‹์•„์š”

UnitTest: Prism.Core.dll


Prism ํ”„๋ ˆ์ž„์›Œํฌ๋Š” Unit Test๋ฅผ ์–ด๋–ป๊ฒŒ ํ–ˆ์„๊นŒ์š”? ๋‚ด๋ถ€์ ์œผ๋กœ ์œ ๋‹›ํ…Œ์ŠคํŠธ๋ฅผ ์–ด๋–ป๊ฒŒ ๊ตฌ์„ฑํ–ˆ๋Š”์ง€๋ฅผ ์•Œ๊ฒŒ ๋˜๋ฉด ๋‹จ์œ„ ๊ธฐ๋Šฅ๋“ค์„ ์—ญ์œผ๋กœ ๋ถ„์„ํ•˜๊ณ  ์ˆœ ๊ธฐ๋Šฅ์˜ ์˜๋„๋ฅผ ์ดํ•ดํ•˜๊ณ  ํŒŒ์•…ํ•˜๋Š”๋ฐ ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋งŒ๋“ค๊ณ ์ž ํ•˜๊ฑฐ๋‚˜ ์œ ๋‹›ํ…Œ์ŠคํŠธ๋ฅผ ๋„์ž…ํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด ์ข‹์€ ๋ ˆํผ๋Ÿฐ์Šค๊ฐ€ ๋  ์ˆ˜๋„ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. :smile:

์‚ฌ์‹ค Prism์€ ๋‹ค์–‘ํ•œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์œ ๋‹›ํ…Œ์ŠคํŠธ๊ฐ€ ์กด์žฌ ํ•˜์ง€๋งŒ ๊ทธ ์ค‘์—์„œ๋„ Prism.Core์— ๋Œ€ํ•œ ์œ ๋‹›ํ…Œ์ŠคํŠธ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋จผ์ € PrismLibrary_Core.sln ์†”๋ฃจ์…˜์„ ์‚ดํŽด๋ณด๋ฉด ๋‘๊ฐœ์˜ ํ”„๋กœ์ ํŠธ๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š”๋ฐ ํ•ญ๋ชฉ์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋‹จ ํ”„๋กœ์ ํŠธ์˜ ๋ฌผ๋ฆฌ์ ์ธ ์œ„์น˜๊ฐ€ ๊ฐ๊ฐ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ์•„๋ž˜ ๋งํฌ๋ฅผ ํ†ตํ•ด ์œ„์น˜๋ฅผ ํ™•์ธํ•ด๋ณด์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ Prism.Core.Tests๋Š” Prism.Core.dll ์–ด์…ˆ๋ธ”๋ฆฌ์˜ ์œ ๋‹›ํ…Œ์ŠคํŠธ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค. ๋จผ์ € ํ”„๋กœ์ ํŠธ ํด๋” ๊ตฌ์„ฑ์„ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์œ ๋‹›ํ…Œ์ŠคํŠธ ํด๋” ๊ตฌ์กฐโ€ฆ

Prism.Core.Tests
  - Commands
  - Common
  - Events
  - Extensions
  - Ioc
  - Mocks
  - Mvvm

์—ฌ๊ธฐ์„œ Prism.Core ํ”„๋กœ์ ํŠธ์˜ ํด๋” ๊ตฌ์กฐ์™€ ๋น„๊ตํ•ด๋ณด๋ฉด ๋งค์šฐ ์œ ์‚ฌํ•˜๊ฒŒ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์œ ๋‹› ํ…Œ์ŠคํŠธ๊ฐ€ Core์˜ ์–ด๋–ค ๋ถ€๋ถ„์„ ํ…Œ์ŠคํŠธ ํ–ˆ๋Š”์ง€๋„ ์ง๊ด€์ ์œผ๋กœ ์‰ฝ๊ฒŒ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ์ด๋ฉฐ ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์—ญ์œผ๋กœ ๋ถ„์„ํ•ด ๋‚˜๊ฐ€๊ธฐ์—๋„ ์ข‹์Šต๋‹ˆ๋‹ค.

Prism.Core ๊ตฌ์กฐโ€ฆ

image

๊ทธ๋Ÿผ ์ด์ œ ์‹ค์ œ ๊ตฌํ˜„๋˜์–ด์žˆ๋Š” ์œ ๋‹›ํ…Œ์ŠคํŠธ ํ˜„์žฅ์„ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.


์œ ๋‹›ํ…Œ์ŠคํŠธ ์‚ดํŽด๋ณด๊ธฐ


Events/EventAggregatorFixture.cs ํŒŒ์ผ์— ๊ตฌ์„ฑ๋œ ์–ด๋Š ํ•œ [Fact] ์œ ๋‹›ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค.

using Prism.Events;
using Xunit;

namespace Prism.Tests.Events
{
    public class EventAggregatorFixture
    {
        [Fact]
        public void GetReturnsSingleInstancesOfSameEventType()
        {
            var eventAggregator = new EventAggregator();
            var instance1 = eventAggregator.GetEvent<MockEventBase>();
            var instance2 = eventAggregator.GetEvent<MockEventBase>();

            Assert.Same(instance2, instance1);
        }

        public class MockEventBase : EventBase { }
    }
}

Assert.same(instance2, instance1); ๋ฐ”๋กœ ์ด ๊ตฌ๋ฌธ์—์„œ ์œ ์ถ”ํ•  ์ˆ˜ ์žˆ๋“ฏ์ด .GetEvent<T> ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๋‚ด๋ถ€์ ์œผ๋กœ ์ตœ์ดˆ ํƒ€์ž…์ธ ๊ฒฝ์šฐ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ฃผ๊ธฐ๋ฅผ ์ง€์†์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋ฉฐ ์ดํ›„์— ํƒ€์ž…์ด ๋‹ค์‹œ ๋“ค์–ด์˜ฌ ๊ฒฝ์šฐ ์ด๋ฏธ ์ƒ์„ฑ๋œ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋˜์–ด ์žˆ๋Š” ๊ธฐ๋Šฅ(Fact)์„ ์‰ฝ๊ฒŒ ์ดํ•ดํ•˜๊ณ  ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋ฏ€๋กœ Assert.Same ๊ฒฐ๊ณผ๋ฅผ ํ†ตํ•ด ,

์•„GetEvent(); ์ฒ˜๋ฆฌ๊ฐ€ ๋‚ด๋ถ€์ ์œผ๋กœ typeof(T)๋ฅผ Key๋กœ ํ•˜๋Š” Dictionary๋ฅผ ์‚ฌ์šฉํ•˜๊ฒ ๊ตฌ๋‚˜โ€ฆ

์ด๋Ÿฐ ์‹์œผ๋กœ ์˜๋„๋ฅผ ์•Œ์•„๊ฐ€๊ณ  ๊ธฐ๋Šฅ ์ฒ˜๋ฆฌ ๋ฐฉ์‹ ๋“ฑ์„ ์œ ์ถ”ํ•˜๊ฑฐ๋‚˜ ๋˜ ์ง์ ‘์ ์ธ ํ™•์ธํ•˜๊ธฐ๊นŒ์ง€ ์†์‰ฝ๊ฒŒ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
์‹ค์ œ GetEvent ์†Œ์Šค์ฝ”๋“œ๋„ ํ•œ๋ฒˆ ์‚ดํŽด๋ณผ๊นŒ์š”? ์—ฌ๊ธฐ

์ค€๋น„๋œ ์œ ๋‹›ํ…Œ์ŠคํŠธ๋ฅผ ์ž˜ ํ™œ์šฉํ•œ๋‹ค๋ฉด ์žฌ๋ฏธ์žˆ๋Š” ์—ฐ๊ตฌ ๋ฐฉ๋ฒ•์ด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋˜ ๋ฆฌ๋ทฐ ํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์€ ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด ๊ฐ€๋”์”ฉ ๋‚ด์šฉ์„ ์ถ”๊ฐ€ํ•ด๋‚˜๊ฐˆ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.
:smile:

4๊ฐœ์˜ ์ข‹์•„์š”

Singleton Management


Prism์€ IContainerRegistry๋ฅผ ํ†ตํ•ด Singleton ์ธ์Šคํ„ด์Šค ๊ฐ์ฒด๋ฅผ ์ง€์† ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. IContainerRegistry๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ PrismApplication ํด๋ž˜์Šค๋กœ ๋ถ€ํ„ฐ ๊ฐ•์ œ ๊ตฌํ˜„๋˜๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋ ค์›€ ์—†์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (abstract)

override ํƒ€์ž…์ธ RegisterTypes ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด IContainerRegister๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

internal class App : PrismApplication
{
    ...
    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        // ์—ฌ๊ธฐ!!
    }
}

ํ—ˆ๋‚˜! ๊ธฐ์กด Application์„ ์œ ์ง€ํ•˜๋ฉด์„œ PrismBootstrapper๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ๋„ ์žˆ๊ฒ ์ฃ . ์ด ๊ฒฝ์šฐ PrismBootstrapper ์—์„œ๋„ ๋˜ํ•œ RegisterTypes ๋ฉ”์„œ๋“œ๋ฅผ overrideํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋™์ผํ•˜๋‹ค ์ƒ๊ฐํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ํฌ๊ฒŒ ๋‹ค๋ฅผ ๊ฒƒ์ด ์—†๊ธฐ์—, ์ด ๋ถ€๋ถ„์€ ๋‚˜์ค‘์— ์ข€ ๋” ๋‹ค๋ค„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

IContainerRegistry

์ด์ œ Singleton ์ธ์Šคํ„ด์Šค๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ํ•ด๋‹น ๊ฐ์ฒด๋ฅผ ๋“ฑ๋กํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฐ”๋กœ RegisterSingleton ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ RegisterSingleton ๋ฉ”์„œ๋“œ๋ฅผ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.


๋ฐ”๋กœ์ƒ์„ฑํ•˜๊ธฐ

Application ์ƒ์„ฑ ๊ณผ์ •์—์„œ ๋ฐ”๋กœ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ์‹ค์ œ new() ์ƒ์„ฑ์ด ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— Singleton์„ ๋“ฑ๋กํ•˜๋Š” ๊ณผ์ •์—์„œ ๊ตณ์ด ์ƒ์„ฑํ•  ํ•„์š”๊ฐ€ ์—†๊ฑฐ๋‚˜ ํ•˜์ง€ ๋ง์•„์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ์ž˜ ํŒ๋‹จํ•ด ๋ณด๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

containerRegistry.RegisterSingleton(new AvatarView());

์•„๋งˆ๋„ ๋ณ€์ˆ˜๊ฐ€ ํ•„์š”ํ•œ ์ƒ์„ฑ์ž ์ด๊ฑฐ๋‚˜ ๋ฌด์–ธ๊ฐ€ ์…‹ํŒ…์ด ํ•„์š”ํ•˜๊ฑฐ๋‚˜, ์—ฌ๋Ÿฌ ์‹œ์  ์ƒ์˜ ์ด์œ ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ๊ฒ ์ฃ .
๊ทธ๋ฆฌ๊ณ  ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฌธ์ž์—ด๋กœ ์ ‘๊ทผํ•  ๋•Œ ์›ํ•˜๋Š” ์ด๋ฆ„์œผ๋กœ ์ฐพ์„ ์ˆ˜ ์žˆ๋„๋ก ๋ถ€์—ฌํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. (overriding)

containerRegistry.RegisterSingleton(new BlogView(), "BlogPage");
containerRegistry.RegisterSingleton<AvatarView>(new AvatarView(), "BlogPage");

๋‘๋ฒˆ์งธ ์ค„์€ <T> ํƒ€์ž… ๋‹จ์ˆœํ™”๊ฐ€ ๊ฐ€๋Šฅํ•˜์ฃ !

์‚ฌ์‹ค ๊ธฐ๋ณธ์ ์œผ๋กœ ์ด๋ฆ„์„ ์ž‘์„ฑํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๊ฐ์ฒด ์ด๋ฆ„๊ณผ ๋™์ผํ•œ AvatarView ๋ฌธ์ž์—ด์ด ๋ถ€์—ฌ๋˜์ง€๋งŒ, ์ด๋ ‡๊ฒŒ ์ธ์Šคํ„ด์Šค๋ช…๊ณผ ๋‹ค๋ฅด๊ฒŒ ๋“ฑ๋กํ•  ์ˆ˜๋„ ์žˆ๊ฒ ์Šต๋‹ˆ๋‹ค. ์•„๋งˆ๋„ ๋ณดํ†ต ์ ‘๋‘ ๋‹จ์–ด๋ฅผ ๋ถ™์ด๊ฑฐ๋‚˜ ํ•  ๋•Œ ์‚ฌ์šฉ๋˜๊ฒ ์ฃ .

ํƒ€์ž…๋“ฑ๋กํ•˜๊ธฐ

๋ฐ”๋กœ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ๋Š” ๋‹ฌ๋ฆฌ Singleton ์ธ์Šคํ„ด์Šค๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๊ณผ์ •์—์„œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ํ•„์š”๊ฐ€ ์—†๊ฑฐ๋‚˜ ViewModel ์˜์กด์„ฑ ์ฃผ์ž…, Resolve, Region ๋“ฑ์—์„œ ํ˜ธ์ถœ์ด ๋  ๋•Œ ์ƒ์„ฑ๋˜์–ด์•ผ ํ•  ๊ฒฝ์šฐ์—๋Š” ๋ฐ”๋กœ ์ด ๋ฐฉ๋ฒ•์— ํ•ด๋‹นํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก  ์‹œ์  ์ƒ ๋” ๋‹ค์–‘ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์žˆ๊ฒ ์Šต๋‹ˆ๋‹ค. :joy:

container.Registry.RegisterSingleton(typeof(BlogView));

์•ž์„œ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ๊ณผ๋Š” ๋‹ฌ๋ฆฌ <T> ํƒ€์ž…์„ ๋„˜๊ฒจ์คŒ์œผ๋กœ์จ ๋จผ ํ›—๋‚ โ€ฆ BlogView๋ฅผ ์ฐพ๋Š” ์ƒํ™ฉ์ด ์ƒ๊ธธ ๋•Œ Singleton ๊ด€๋ฆฌ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๋ฒˆ์—๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ name์„ ์›ํ•˜๋Š” ๋ฌธ์ž์—ด๋กœ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

container.Registry.RegisterSingleton(typeof(BlogView), "BlogViewPage");

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด Type์„ ํ†ตํ•ด Singleton ๊ฐ์ฒด๋ฅผ ๋“ฑ๋ก๋งŒ ํ•ด์„œ ์–ด๋””์„ ๊ฐ€ ์‚ฌ์šฉ๋˜๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

Singleton ์‚ฌ์šฉ

๊ทธ๋Ÿผ ์ด์ œ ViewModel์ด๋‚˜ Resolve<T>๋ฅผ ํ†ตํ•ด ๋“ฑ๋ก๋œ Singleton ๊ฐ์ฒด๋ฅผ ๊ฐ€์ ธ์™€๋ณผ๊นŒ์š”?


ViewModel์—์„œ IoC Container ์ฃผ์ž…์„ ํ†ตํ•œ ์‚ฌ์šฉ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

public class JamesViewModel(BlogView blogView)
{

}

Resolve๋ฅผ ํ†ตํ•œ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•๋„ ํฌ๊ฒŒ ๋‹ค๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

BlogView blogView = _container.Resolve<BlogView>("BlogViewPage");
BlogView galleryView = _container.Resolve<GalleryView>();
BlogView aboutView = _container.Resolve<AboutView>();

Region์— ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ๋ชฉ์ ์— ๋งž๊ฒŒ ๋‹ค์–‘ํ•˜๊ฒŒ ํ™œ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. (Singleton ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ View์ธ ๊ฒฝ์šฐ ์žฌ๋ฏธ์žˆ๊ฒŒ ์‘์šฉํ•  ์ˆ˜ ์žˆ์ฃ .)

Interface ํ™œ์šฉ

์‚ฌ์‹ค Singleton์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋“ฑ๋ก์— ์‚ฌ์šฉ๋œ ๊ฐ์ฒด ํƒ€์ž…์„ ์•Œ๊ณ  ์žˆ์–ด์•ผ๋งŒ ํ•˜๋Š” ๊ตฌ์กฐ์ ์ธ ๋ถˆํŽธํ•จ์ด ์ƒ๊น๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ์ƒ, ๋ณ„ ์ƒ๊ด€์—†๋Š” ๊ฒฝ์šฐ๋„ ์žˆ๊ฒ ์ง€๋งŒ ๋ณต์žกํ•˜๊ฒŒ ์—ฌ๋Ÿฌ ํ”„๋กœ์ ํŠธ๋ฅผ ์ฐธ์กฐํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์ด ํ™œ์šฉ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.


๊ธฐ์กด Singleton ๋ฐฉ์‹๊ณผ ๊ฑฐ์˜ ๋™์ผ ํ•˜๋ฉด์„œ ๋Œ€ํ‘œ ์ธํ„ฐํŽ˜์ด์Šค(๋˜๋Š” ํด๋ž˜์Šค)๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

container.Registry.RegisterSingleton<IViewable, BlogView>("Blog");
container.Registry.RegisterSingleton<IViewable, GalleryView>("Gallery");
container.Registry.RegisterSingleton<IViewable, AboutView>("About");

์ด๋ ‡๊ฒŒ ์ธํ„ฐํŽ˜์ด์Šค ๋˜๋Š” ์ƒ์œ„ ์ƒ์†ํด๋ž˜์Šค๋ฅผ ๋Œ€ํ‘œ ํƒ€์ž…์œผ๋กœ ์ •์˜ํ•œ๋‹ค๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ์–ด๋– ํ•œ ์ฐธ์กฐ ์—†์ด๋„ ๋Œ€ํ‘œ ํƒ€์ž…์„ ํ†ตํ•ด ๊ฐ์ฒด๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

_container.Resolve<IViewable>("Blog");
_container.Resolve<IViewable>("Gallery");
_container.Resolve<IViewable>("About");

์ด๋ฅผ ํ†ตํ•ด ํ”„๋กœ์ ํŠธ๊ฐ„ ์ฐธ์กฐ ๊ด€๊ณ„์™€ ์ƒ๊ด€ ์—†์ด๋„ Singleton์„ ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. :smile:

4๊ฐœ์˜ ์ข‹์•„์š”

AutoWireViewModelChanged


์•ˆ๋…•ํ•˜์„ธ์š”. ์ด์žฌ์›…์ž…๋‹ˆ๋‹ค.

์ด ๊ธด ์ด๋ฆ„์„ ๊ฐ€์ง„ ๋ฉ”์„œ๋“œ๋Š” ViewModelLocationProvider๋ฅผ ํ†ตํ•ด ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๋ฆ„์—์„œ ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด Prism ์„ค์ •์— ์˜ํ•ด View๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ๋Œ€์ƒ ViewModel์„ Injection ์ฃผ์ž…์„ ํ†ตํ•ด ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ๋Š” object ํƒ€์ž…์˜ View์™€ object ํƒ€์ž…์˜ ViewModel์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

Prism์—์„œ๋Š” ViewModelLocationProvider ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด ํ•œ ๊ฐ€์ง€ ์œ ์šฉํ•œ ์ด๋ฒคํŠธ(์ฝœ๋ฐฑํ•จ์ˆ˜ ํ˜ธ์ถœ ๋ฐฉ์‹)๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ ๋ฐฉ๋ฒ•์€ ์‹ฌํ”Œํ•ฉ๋‹ˆ๋‹ค. ๋จผ์ € ์ด๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ„๋‹จํ•œ UI ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“  ์ด์œ ๋Š” ๋ชจ๋“  View์— ๊ณตํ†ต์œผ๋กœ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค.

public class SmartView : ContentControl 
{
}

๊ทธ ๋‹ค์Œ ViewModelLocationProvider๋ฅผ ํ†ตํ•œ AutoWireViewModelChanged ๋ฉ”์„œ๋“œ์™€ ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

public class SmartView : ContentControl
{
    public SmartView()
    {
        ViewModelLocationProvider
            .AutoWireViewModelChanged(this, AutoWireViewModelChanged);
    }
    private void AutoWireViewModelChanged(object view, object dataContext)
    {
        // Prism์„ ํ†ตํ•ด ์ƒ์„ฑ๋œ Injection ์ฃผ์ž…๋œ ViewModel์ด ์ƒ์„ฑ๋  ๋•Œ ๋งˆ๋‹ค
        // ์ด ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค!
    }
}

์ด ๊ธฐ๋Šฅ์„ ํ†ตํ•ด Prism View์™€ ViewModel์˜ ์ƒ์„ฑ๊ณผ ํ๋ฆ„์„ ์ข€ ๋” ๋ช…ํ™•ํžˆ ์ธ์ง€ํ•˜๊ณ , View์™€ ViewModel์ด ๋ชจ๋‘ ์ƒ์„ฑ๋œ ์ด ์‹œ์ ์—์„œ ๋‹ค์–‘ํ•œ ํ™•์žฅ ์‘์šฉ ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ๋Š” ํƒ€์ด๋ฐ์„ ํ™•๋ณดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ™œ์šฉํ•œ ์ด๋Ÿฐ ์ž‘์—…๋„ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ฐ€๋Šฅํ•˜๊ฒ ์ฃ ? (์˜ˆ๋ฅผ ๋“ค์–ด!!!, ์—ฌ๋Ÿฌ๋ถ„์˜ MVVM ๊ทœ์น™์— ๋งž๊ฒŒโ€ฆ)

public class SmartView : ContentControl
{
    public SmartView()
    {
        ViewModelLocationProvider
            .AutoWireViewModelChanged(this, AutoWireViewModelChanged);
    }
    private void AutoWireViewModelChanged(object view, object dataContext)
    {
        if (dataContext is IViewCreatable ui) 
        {
            ui.OnInitialized();
        }
    } 
}

๊ทธ ๋ฐ–์—๋„ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋‚˜ ํŠน์„ฑ์— ๋งž๊ฒŒ ๋‹ค์–‘ํ•œ ๋ชฉ์ ๊ณผ ์˜๋„๋กœ AutoWireViewModelChanged ๋ฉ”์„œ๋“œ๋ฅผ ๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ œ๊ฐ€ ์ค€๋น„ํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ๊นŒ์ง€ ์ž…๋‹ˆ๋‹ค.
์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. :smile:

2๊ฐœ์˜ ์ข‹์•„์š”