태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.

달력

12

« 2020/12 »

  •  
  •  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  •  
  •  
노무현 전 대통령의 서거 소식을 접한 후로 계속 가슴 답답한 주말을 보냈다.

지금도 정치에 크게 관심을 쏟는 것도 아니고 이전에는 아예 관심이 없었다고 표현하는 게 더 적절하겠다. 조금이나마 정치에 관심을 갖게 된 계기가 노무현이라는 정치인의 대선 출마였다. 투표권을 받고 처음으로 뽑은 대통령이었다. 그래서 몰상식한 탄핵에서 지켜야 한다고 생각했고, 나같이 충돌을 두려워하고 소심한 사람도 촛불을 들고 집회에 나갈 수 있다는 경험을 갖게 해준 대통령이었다. 자전거 타는 모습, 동네 수퍼에서 담배 피는 모습을 찍은 사진을 통해 흐뭇하게 웃음을 짓게 해준 사람이었다. 전직 대통령이면서도 동네 아저씨와 별 다를 바 없는 생활을 할 수 있다는 너무나 당연한 사실을 새롭게 깨우치게 해준 사람이었다.

마음은 답답한데, 잘못된 것은 저들이라고 말하고 싶은데, 왜 답답한지 설명하거나, 정확히 저들의 무엇이 잘못되었는지 지적할 만한 소양이 내게는 없다. 앵무새처럼 똑같은 소리만 떠들고 있는 TV 라디오를 붙잡고 있는 것보다 좀 더 도움이 되는 수단이 필요하다고 생각했다. 그래서 유시민의 후불제 민주주의를 집어들었다. 지금 같은 시기에 읽기에 좋은 책이라고 생각한다.

생존 경쟁에서 살아남기 위한 기술도 좋지만, 나와 내 주변 사람들이 좀 더 만족스러운 삶을 살기 위해서 무엇이 필요한지, 무엇을 갈고 닦아야 하는지 알고 싶다.
Posted by wafe

댓글을 달아 주세요

  1. jindog 2009.05.25 01:57  댓글주소  수정/삭제  댓글쓰기

    수능강사 이현 선생님의 강의를 lameproof에서 접했는데
    맞는 걸 맞다라고 표현하기 위해서는 때론 목숨을 걸어야 하고 틀린 걸 틀렸다라고 표현하기 위해서는 밥줄을 걸어야 한다 ... 이걸 고인이 그대로 몸으로 보여줘 버린 셈...

    • wafe 2009.05.25 02:25  댓글주소  수정/삭제

      이제사 이렇게 슬퍼하는 것도 사후약방문이라는 생각이 들기도 한다. 뉴스에서 소식을 접할 때는 '노무현도 어쩔 수 없었네' 라든가 '당신은 그래서 얼마나 깨끗한지 두고보자'하는 식의 단순한 반응 외에 좀 더 깊이 생각해보지는 못했거든.

      오래 살아서 더 많은 생각할 거리를 던져 주셨으면 더 좋았겠지만, 이번 일에서 얻게 될 교훈들을 잊지 않도록 애써야겠네...

2009. 5. 23. 14:16

삼가 고인의 명복을 빕니다. 카테고리 없음2009. 5. 23. 14:16


사진 from 자전거 타는 대통령
Posted by wafe

댓글을 달아 주세요

2009. 5. 23. 03:36

Google Test의 데이터 기반 테스트 카테고리 없음2009. 5. 23. 03:36

Google Test에서도 MBUnit의 RowTestNUnit의 TestCase 같은 데이터 기반 테스트라는 개념을 지원한다. Google Test에서는 이 개념을 Value-Parameterized Tests라고 부른다. 코드는 동일한데 사용하는 데이터 값 몇 개만 바뀌는 테스트 케이스가 있을 때, 코드 중복을 없애주면서도 어떤 테스트 케이스가 실패하고 성공하는지 정확하게 알려주는 유용한 기능이다.

 

그런데 아무래도 C# 보다는 유연성이 떨어지는(?) C++이라 쓰기가 마냥 쉽지만은 않아서 좀 정리를 해 둘 필요가 있다. 템플릿을 써서 구현한 기능이라 컴파일 에러라도 한 번 나면 좀 골치가 아프긴 하지만, Visual C++ 2008의 템플릿 관련 에러 메시지는 엄청나게 친절하기 때문에 미리 걱정할 필요는 없을 듯.

  1. #include "gtest/gtest.h"

    #include "gmock/gmock.h"

    #include <tuple>

     

    using testing::Return;

     

    namespace

    {

    // ParamType 구성

    // TestWithParam<T>가 받는 테스트 데이터는 T 타입의 값 하나이기 때문에, 데이터 여러 개를 넘기려고 tuple을 사용한다.

    typedef std::tr1::tuple<bool, MsoTriState, MsoAnimateByLevel, MsoTriState, MsoTriState, MsoAnimateByLevel> ParamType;

     

    // Value-Parameterized Tests를 위해서는 비어있는 Fixture라도 하나 만들어야한다.

    // TestWithParam<T>를 상속받는 것 외에는 일반적인 Test Fixture와 동일하다.

    class TestEffectDataSetEffectInformationFixture

        : public testing::TestWithParam<ParamType>

    {

    };

     

    // Value-Parameterized Tests를 위한 테스트 메서드

    // 여기서는 TEST_P 매크로를 쓴다. 일반적인 Fixture를 쓸 때에는 TEST_F 매크로를 쓰는 것과 다르다.

    TEST_P(TestEffectDataSetEffectInformationFixture, TestSetEffectInformation)

    {

        bool recheckRun;

        MsoTriState recheckRetValue;

        MsoAnimateByLevel levelEffectRetValue;

        MsoTriState animBgRetValue;

        MsoTriState animBgExpected;

        MsoAnimateByLevel levelEffectExpected;

     

        // GetParam() 함수를 써서 테스트 데이터를 얻는다.

        // 여기서는 데이터가 tuple 이라서 tie()를 써서 값을 얻는다.

        std::tr1::tie(recheckRun, recheckRetValue, levelEffectRetValue, animBgRetValue, animBgExpected, levelEffectExpected) = GetParam();

     

        MockEffectAdaptor ea;

        MockEffectInformationAdaptor eia;

     

        if (recheckRun) {

            EXPECT_CALL(ea, GetAnimateBackgroundRechecked())

                .WillOnce(Return(recheckRetValue));

        }

        else {

            EXPECT_CALL(ea, GetAnimateBackgroundRechecked())

                .Times(0);

        }

     

        EXPECT_CALL(eia, GetBuildByLevelEffect())

            .WillOnce(Return(levelEffectRetValue));

        EXPECT_CALL(eia, GetAnimateBackground())

            .WillOnce(Return(animBgRetValue));

     

        EffectData ed;

        ed.SetEffectInformation(ea, eia);

     

        ASSERT_EQ(animBgExpected, ed.AnimateBackground);

        ASSERT_EQ(levelEffectExpected, ed.BuildByLevelEffect);

    }

     

    // Value-Parameterized Tests에 테스트 데이터를 제공

    INSTANTIATE_TEST_CASE_P(Prefix, TestEffectDataSetEffectInformationFixture,

        testing::Values(

            ParamType(true, msoTrue, msoAnimateLevelNone, msoFalse, msoTrue, msoAnimateLevelNone),

            ParamType(true, msoFalse, msoAnimateLevelNone, msoFalse, msoFalse, msoAnimateLevelNone),

            ParamType(false, msoFalse, msoAnimateTextByAllLevels, msoFalse, msoFalse, msoAnimateTextByAllLevels),

            ParamType(false, msoFalse, msoAnimateLevelNone, msoTrue, msoTrue, msoAnimateLevelNone)

        ));

    } // anonymous namespace

 

테스트 실행 결과는 이런 식으로 나온다.

3>[ RUN      ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/0
3>[       OK ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/0
3>[ RUN      ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/1
3>[       OK ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/1
3>[ RUN      ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/2
3>[       OK ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/2
3>[ RUN      ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/3
3>[       OK ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/3

 

이 예제 코드에서는 테스트 데이터를 만들 때 그냥 Values()만 썼는데, Combine() 같은 걸 쓰면 주어진 값의 모든 조합을 생성해서 테스트해 준다거나 하는 기능은 그냥 코드 중복을 막는다는 차원을 넘어서는 멋진 기능이라고 생각된다.

 

그 외에 다른 사용법은 공식 문서를 보면 되는데, 실제 사용하면서 생기는 궁금증을 해결하기 위해서는 소스 코드에 주석 형태로 들어있는 사용 예제들을 보는게 좋다.

이 글은 스프링노트에서 작성되었습니다.

Posted by wafe

댓글을 달아 주세요

지난 번에 번역한 YouCard Re-visited : Implementing the ViewModel pattern이라는 글에 이어지는 YouCard Re-visited: Implementing Dependency Injection in Silverlight라는 글을 번역한 것이다.


이번 주에 올린 글에서 나는 YouCard 어플레케이션에서 Model-View-ViewModel 패턴을 구현한 방법에 대해서 썼다. 이번 글에서 나는 Inversion of Control 컨테이너와 Dependency Injection 패턴을 구현하는 방법에 대해 쓰려고 한다. 의존성 주입에 관한 글은 매우 많이 있으므로, 이 글에서는 Silverlight에서 의존성 주입을 사용하는 방법에 대해 집중하려고 한다. 특히, Ninject 프레임워크를 사용하는 방법에 대해 쓸 것이다. 이 패턴에 대해서 아주 잘 설명하고 있는 마틴 파울러의 에세이를 읽어보기를 강력하게 추천한다.

 

의존성 주입을 써보려고 할 때, 여러분은 이 패턴을 구현하고 있는 수많은 프레임워크들에 질려버릴지도 모른다. 그래서 마치 의존성 주입이라는 개념이 아주 크고 복잡한, 여러분이 소프트웨어를 만드는 방식에 엄청난 변화를 가져오는 개념이라고 생각하게 될지도 모른다. 큰 변화가 생기는 것이 사실일 수는 있다. 그러나 그것은 프레임워크를 사용하기 때문에 생기는 변화가 아니라 의존성 주입이라는 패턴을 도입함으로써 얻을 수 있는 유연성 때문에 생기는 변화일 것이다. 간단히 표현하자면 의존성 주입은 외부의 의존성을 어떻게 여러분의 어플리케이션 클래스에 제공할 것인가 하는 내용이다. 이전의 블로그 글에서 나는 Twitter와 Flickr 연동 부분을, 인터페이스를 구현하는 클래스들로 리팩터링했다. 코드가 Blend에서 실행되는지 웹 브라우저에서 실행되는지에 따라 실제 구현코드가 사용되기도 하고, 디자인 타임에 몇몇 더미 데이터를 제공하는 가짜(mock) 구현코드가 사용되기도 한다.

ninject-code1_749fbf00-74da-40f9-a91c-a5ee715c2b83.gif

위에 있는 코드의 문제점은 YouCardData 클래스와 IMicroBlog, IPhotoService 인터페이스를 구현하는 네 개의 클래스 사이에 결합 관계가 있다는 점이다. 뷰-모델(YouCardData)과 그것의 외부적인 의존물들(Twitter, Flickr) 사이에 있는 높은 결합 관계 때문에 코드의 유연성이 떨어진다. 인터페이스를 사용하도록 만들었기 때문에 Picasa 사진 서비스나 Tumblr 마이크로 블로그 서비스 같은 다른 서비스를 사용하는 기능을 구현할 수 있게 되기는 했다. 하지만 새롭게 구현한 서비스 클래스를 YouCardData 클래가 사용하도록 할 방법이 없다. 그리고 외부 서비스들을 가짜로 구현한 클래스를 제공할 수 없으므로 단위 테스트를 하기도 힘들다. 그래서 우리는 아래처럼 의존성 있는 개체를 생성자에 인자로 전달할 수 있도록 리팩터링할 수가 있다.

ninject-code2_13eb1037-d8c3-4fad-9b30-474da32f553f.gif

이렇게 함으로써 YouCardData를 사용하는 쪽에서는 생성자를 통해 의존성을 주입할 수 있게 된다. 이러한 방식을 '생성자를 이용한 의존성 주입' (constructor based dependency injection)이라고 부르는데, 여기서는 사용하는 코드 쪽에서 직접 의존성을 주입하는 식이다. 이렇게 변경함으로써, 다른 서비스 구현을 사용하도록 하거나, 단위 테스트를 하거나, 새로운 기능을 제공함에 있어서 아주 큰 유연성을 얻게 되었다. 하지만 이 코드에는 한 가지 문제가 있다. 데이터 컨텍스트를 생성하거나 하려면 XAML에서 이 클래스 인스턴스를 생성할 필요가 있는데, 이 때에는 파라미터가 없는 생성자가 필요하다. 아래와 같은 생성자를 하나 더 만들어서 이 문제를 해결할 수 있다.

ninject-code3_05e7555e-81a7-436b-9c08-4bf6b3c500d5.gif

이렇게 하여 XAML에서 선언적인(declarative) 방식으로 생성되는 인스턴스는 Twitter와 Flickr 서비스 구현을 사용하게 되고, 단위 테스트에서는 가짜 구현 개체를 사용할 수 있게 되었다. 하지만 여전히 완벽하지는 않다. 처음 만들었던 생성자에서는 YouCardData 클래스가 Blend에서 사용되고 있는 것인지 아닌지를 체크하도록 했었다. 현재 상태 그대로 유지할 수도 있겠지만 여기서 '제어 역전' (Inversion of Control) 컨테이너의 도움을 받아 보도록 하자.

 

Ninject

근래에 UI 프레임워크들과 패턴에 대해서 조사를 좀 해보았는데, 그들중 많은 수가 의존성 주입 프레임워크를 쓰고 있었다. Prism-AG 프레임워크Composite WPF 프레임워크를 Silverlight로 포팅한 것인데 DLLite라고 하는 작은 의존성 주입 프레임워크를 사용한다. Silverlight로 포팅된 또다른 의존성 주입 프레임워크에는 Unity가 있는데, Microsoft patterns and practices 그룹에서 내놓은 프레임워크 중 하나다. 하지만 내가 가장 좋아하는 것은 Ninject인데 의존성 주입 프레임워크 계의 닌자라고 할 수 있다. AltDotNet 팟캐스트의 5, 6번 에피소드에서 Nate Kohari의 이야기를 듣고 이 프레임워크에 관심을 가지게 되었다. Ninject는 스스로를 다음과 같이 설명하고 있다.

Ninject는 번개같이 빠르고, 엄청나게 가벼운 .NET 기반의 의존성 주입 프레임워크이다. 여러분의 어플리케이션을 느슷하게 결합된, 높은 응집도를 지닌 조각들로 나눈 후, 유연한 방법으로 다시 엮을 수 있는 방법을 제공해주는 프레임워크이다.  Ninject를 사용함으로써 여러분의 소프트웨어 아키텍처와 코드는 작성하기 쉽고, 재사용하기 쉬우며, 테스트하고 수정하기에 용이하게 될 것이다.

ninject-ninja1_56631962-4fcc-4af5-89a7-42765f316ce2.jpg

이 프레임워크의 컴파일된 크기는 110KB 밖에 되지 않고, Ninject.Core.dll만 참조에 추가하면 대부분의 사용 시나리오를 만족시킨다. Ninject의 강점은 XML이 아니라 코드로 설정을 한다는 점이다. 대부분의 IoC 컨테이너는 개체간의 의존성을 설정하는데 XML을 사용한다는 것을 강조하고 있다. 여러분의 소프트웨어 배포 환경이 설치할 때마다 많이 바뀌는 경우에는 XML을 사용하는 것이 이점으로 작용하지만, 많은 경우 아래와 같은 설정상의 부담이 따른다.

  1. 모든 개체 타입에 대해서 전체 어셈블리 이름(fully qualified assembly name)을 써야하기 때문에 설정 내용이 쓸데없이 많아지고 복잡하게 된다.
  2. 단순한 오타 때문에 어플리케이션이 제대로 동작하지 않게 되기 십상이다.
  3. 개체를 구성하는 규칙이 복잡해질수록 XML을 통해서 규칙을 표현한다는 것이 힘들어질 것이다.

그에 반해서 Ninject는 가끔은 "내장된 특수 분야 언어(embedded domain-specific language)"라고 불리기도 하는 늘어놓는 인터페이스(Fluent interface)를 사용한다. 이런 방식은 어플리케이션을 구성하는 데에 프로그래밍 언어의 힘을 빌릴 수 있다는 장점을 갖는다. 복잡하게 얽힌 클래스 간의 연결 관계를 표현할 수 있고, 사용자의 설정에 영향을 받는 클래스 연관 관계가 있을 경우 설정 파일을 읽어들여서 적용하는 방식을 방해하는 것도 전혀 없다.

... 코드를 이용해서 어플리케이션을 조립하는 편이 더 쉬운 경우가 있다. 배포할 때마다 별로 변하는 것이 없는 단순한 어플리케이션을 만드는 경우가 한 가지 예다. 이런 경우에는 별도의 XML 파일보다 몇 줄의 코드가 더 명확할 수 있다.

이와 대조적인 경우가, 조립 방법이 매우 복잡하고 조건에 따른 선택적인 과정이 섞여있는 경우이다. 이렇게 프로그래밍 언어와 비슷해지기 시작하면 XML은 무너지기 시작한다. 명료한 프로그램을 작성하기 위한 문법을 가진 실제 언어를 사용하는 편이 좋다.

... 이런 경우의 내 충고는, 항상 코드를 통해서 모든 설정을 할 수 있는 방법을 제공하고 나서, 분리된 설정 파일은 선택적인 기능으로 생각하라는 것이다. - 마틴 파울러

"… There are cases where it’s easier to use program code to do the assembly. One case is where you have a simple application that’s not got a lot of deployment variation. In this case a bit of code can be clearer than separate XML file.

A contrasting case is where the assembly is quite complex, involving conditional steps. Once you start getting close to programming language then XML starts breaking down and it’s better to use a real language that has all the syntax to write a clear program.

…My advice here is to always provide a way to do all configurations easily with a programmatic interface, and then treat separate configuration file as an optional feature. – Martin Fowler" (Inversion of Control Containers and the Dependency Injection pattern)

Ninject의 또다른 멋진 부분이 시작부터 Silverlight를 위해서 만들어졌다는 점이다. 즉, 원래 존재하던 더 큰 프레임워크를 싹둑 잘라서 Silverlight로 포팅한 것이 아니라는 말이다. 일단 Ninject는 작고 가볍다.

 

Dependency Injection with Ninject

Ninject를 사용하는 일은 아주 쉽다. 우선 우리가 해야할 일은 의존성 주입을 받는 생성자를 찾아서 간단한 어트리뷰트를 추가하는 일이다.

ninject-code4_07f0fc16-e98c-4ba5-bce8-3c45f0cb4159.gif

또, 화면에 보이는 모든 카드를 제어하는 메인 페이지의 뷰-모델에도 영속성 서비스를 위해서 의존성 주입을 사용하고 싶다.

ninject-code5_45dbc2c1-7873-4f65-bc1b-90099bdeab22.gif

Twitter 서비스를 구현한 클래스를 쓸 때에는 프록시 URL을 주입시키고 싶을 것이다. Twitter는 크로스도메인 접근을 허용하지 않기 때문에 우리가 운영하는 서버를 통해서 요청을 보내도록 해야한다.

ninject-code6_7099e6ad-f73c-48c9-8ae9-2904425bbcb6.gif

다음으로 우리가 해야할 일은 뷰-모델과 서비스들을 연결하는 작업이다. 많은 IoC 컨테이너에서 이 작업은 XML 설정을 통해 이뤄지지만 Ninject에서는 늘어놓은 인터페이스 스타일의 코드를 이용해서 작업이 이뤄진다. Ninject는 모듈이라는 개념을 사용해서 연결 작업을 수행한다. 하나의 커널(컨테이너)이 여러 모듈을 책임질 수 있다. YouCard 어플리케이션에서는 하나의 모듈이면 충분하다.

ninject-code7_f6257796-a955-47b3-b655-d42d4d74d7fe.gif

늘어놓는 인터페이스는 코드의 가독성을 높여준다. 그래서 굳이 위에 있는 클래스를 한 줄 한 줄 설명하는 데에 시간을 낭비할 필요는 없을 것 같다. 이 클래스는 StandardModule을 상속해서 Load 메서드를 재정의 하고 있다. Load 메서드에서는 서비스들과 그 서비스의 구체(concrete) 타입을 바인딩하고 있다. 바인딩 코드 한 줄 한 줄을 영어 문장이라고 생각하고 왼쪽에서 오른쪽으로 읽어보면 바인딩 규칙들이 아주 쉽게 이해된다. 또 이 코드가 Blend에서 실행되는 경우에 어떤 구현을 사용할 지 결정하는 부분에서 코드를 통한 설정 방식의 장점이 잘 드러나고 있음을 눈여겨 볼 필요가 있다. 웹 브라우저에서 실행되는 경우에는 Twitter 서비스를 사용하고 Blend에서 실행되는 경우에는 가짜로 구현된 서비스를 사용하도록 조건을 추가해두었다.

 

이 모듈을 사용하려면 간단히 Ninject 커널을 생성해서 모듈에 전달하면 된다.

ninject-code8_87031a87-c2c0-4cd3-a83c-63329429929f.gif

YouCardData 개체를 어떻게 생성할 지 걱정할 필요가 없다는 사실을 알아두기 바란다. Ninject 커널이 개체 생성을 모두 책임지고 YouCardModule에 정의된 바인딩 규칙에 따라 의존성을 주입해 준다.

 

Data binding to objects built by Ninject

여기까지 우리는 Ninject 커널을 설정했다. 다음 과제는 커널에서 얻은 뷰-모델 개체를 뷰에 넣어주는 것이다. 물론 Blend에서의 디자인 타임 경험을 유지하면서 말이다. 코드를 이용해서 데이터 컨텍스트를 세팅하는 것은 아주 간단하다. Application 클래스에서 커널을 생성하도록 하여 아래와 같은 일을 할 수 있다.

ninject-code9_1f612bd4-d89c-4f8b-bbb8-57e2cfc90072.gif

이렇게 하면 어플리케이션에 단 하나의 커널만 존재하게 할 수 있다. 하지만 여전히 어떻게 하면 선언적인 XAML 코드를 사용해서 Ninject가 생성해준 뷰-모델을 사용자 컨트롤의 데이터 컨텍스트에 설정할 수 있는가 하는 문제는 풀지 못했다. 며칠 간을 고민하고, 여러 가지 접근 방법을 시험해보고, Nate Kohari와 의논도 해본 후에 얻은 해답이 Service Locator였다. 이것은 IoC를 구현하는 또다른 패턴이다. 아마 여러분은 서비스 로케이터를 쓸 것인지 의존성 주입을 쓸 것인지 결정할 일이 자주 있을 것이다. 이 패턴에 대해 잘 설명하고 있는 마틴 파울러의 에세이 중 Service Locator vs Dependency Injection 부분을 읽어 보기를 추천한다.

 

서비스 로케이터를 간단히 설명하자면, 어플리케이션에서 사용하는 모든 서비스들의 참조를 가지고 있는 등록부라고 할 수 있다. YouCard에서라면 뷰-모델 같은 클래스는 IPhotoService나 ITwitterService의 구현체를 얻기 위해서 서비스 로케이터에 문의할 수 있다는 말이다. 서비스 로케이터와 의존성 주입 사이의 큰 차이는, 서비스 로케이터를 쓸 때는 여러분의 클래스가 구현체를 요청 하지만 의존성 주입을 쓸 때는 구현체를 클래스에 넣어준다는 점이다(pull vs. push). Ninject와 서비스 로케이터 패턴을 어떤 식으로 잘 섞어 쓸 수 있는지 Nate가 블로그에 글을 올린 것이 있다.

나는 서비스 로케이터와 의존성 주입 패턴이 서로 배타적이라고 생각하지 않는다. 사실 나는 Ninject 같은 의존성 주입 프레임워크와 서비스 로케이터 패턴을 같이 썼을 때 아주 효과적일 수 있다는 것을 발견했다. - Nate Kohari

 

YouCard에서 내가 만든 서비스 로케이터는 아주 간단하다.

ninject-code10_d7ea9906-616b-4de9-9965-81c3520711a9.gif

많은 경우 서비스 로케이터는 정적 클래스로 구현된다. 하지만 Silverlight 2에서는 정적 클래스에 대한 데이터 바인딩은 지원되지 않기 때문에 그 방식은 쓸 수 없다. 그래서 정적 생성자를 만들고, 사적인 정적 kernel 필드를 만드는 방식으로 처리했다. 정적 생성자는 다른 모든 생성자들 보다 먼저 클래스에서 가장 처음 실행된다. 정적 생성자에서는 커널이 있는지 확인해서 없으면 우리가 바인딩 설정을 하면서 만든 모듈을 사용하는 커널 인스턴스를 만든다. 커널 필드를 정적으로 만들었기 때문에, 어플리케이션에 커널 인스턴스가 하나만 존재함을 보장받을 수 있다. 클래스에는 뷰-모델을 커널로부터 얻어내는 두 개의 인스턴스 속성과, 모든 타입의 개체를 컨테이너로부터 얻을 수 있는 제네릭 인스턴스 메서드가 있다.

 

의존성 주입 개념을 도입하기 전에는 뷰와 뷰-모델을 이런 식으로 연결했다.

ninject-code11_7bcce970-b825-4ac6-8b78-13bf2cca1b85.gif

서비스 로케이터를 도입한 후에는 몇 가지 바꿀 것이 있다. 첫 번째로는 서비스 로케이터를 어플리케이션 수준의 리소스로 생성시킨다.

ninject-code12_b6ac62b6-557c-443d-aee3-410457b139a3.gif

그 후에 각각의 뷰에서 데이터 컨텍스트를 얻는 방법을 변경한다.

ninject-code13_d2368669-5217-4421-a6e3-be171a1ae90e.gif

각각의 뷰는 해당하는 뷰-모델을 서비스 로케이터의 속성에 데이터 바인딩하여 얻는다. (역자 첨언 : 위에서 YouCardViewModel은 더 위의 ServiceLocator 클래스 구현에서는 YouCardData라고 정의되어 있으므로, YouCardData라고 쓰는 게 맞는 것 같다.) 이 접근 방식의 가장 멋진 부분은, Blend의 디자인 타임을 완벽하게 지원하고 있다는 것과 디자인 타임에 가짜 구현 서비스를 통해 샘플 데이터를 제공한다는 것이다.

 

Conclusion

Silverlight에서 의존성 주입을 사용하는 것은 다른 .NET 어플리케이션에 쓰는 것보다 절대 어렵지 않다. 프레임워크를 가볍고 빠르게 유지하는 데 중점적인 노력을 쏟고 있다는 점 때문에 Ninject는 Silverlight에 잘 어울린다. 그러나 이렇게 간단한 샘플 프로젝트에서 Ninject를 사용하는 것은 쓸데없는 짓이라고 생각하는 사람도 있을 것이다. 의존성 주입 코드를 서비스 로케이터 안에 직접 작성할 수가 있고, 그렇게 해서 Ninject에 대한 의존성을 없애면서도 여전히 유연성은 유지할 수 있다. 반면, Ninject는 110KB (XAP 파일에 넣어서 압축하기 전 크기) 밖에 안되어서 어플리케이션 크기에 별로 영향을 주지 않는다. 100KB면 이 블로그 글에 사용한 이미지 크기의 절반도 되지 않는다.

ninject-ninja2_1c42192e-53fd-4a9d-b74d-e1ceda6e9dd0.jpg

의존성 주입을 사용함으로써 여러분은 코드를 더 유연하고 테스트하기 쉽게 유지할 수 있다. Silverlight 어플리케이션에서는 특히 이런 점들이 중요하다. 여러분이 Silverlight의 강력함을 알아감에 따라 UI 요구사항도 많이 바뀌어 갈 것이기 때문이다. 나는 Blend를 사용하는 디자이너에게 최상의 경험을 제공하는 것이 중요하다고 확신하고 있고, 의존성 주입이 그것을 바꿀 이유는 없다.

이 글은 스프링노트에서 작성되었습니다.

Posted by wafe

댓글을 달아 주세요

Mac OS X에서는 유닉스/리눅스 계열에서 흔히 쓰는 cron 대신 launchd라는 것으로 주기적으로 실행되는 명령을 콘트롤한다. launchd에 대한 일반적인 사항은 Mac에서 정해진 시간에 자동으로 프로그램 실행하는 법이라는 포스트를 보면 알 수 있다.

 

소개한 포스트에서 볼 수 있듯이, 직접 plist 파일을 수정하는 것보다는 Lingon이라는 오픈소스 도구를 사용하는 것이 편리하다. 그런데 cron에서는 쉽게 설정할 수 있는 "매주 월, 화, 수요일 오전 7시에 실행"이라는 방식의 스케줄을 launchd와 Lingon을 통해서는 만들어낼 수가 없다. launchd 문서에도 설명이 없어서, 작년에 똑같은 작업을 할 때에는 한참을 찾아 헤매다가 결국 비활성화되어 있는 cron을 활성화시켜서 일정을 지정했다. 이번에 OS를 재설치하고 다시 설정을 해야해서 한 번 찾아봤는데, 이번에는 방법을 발견했다.

 

사실 알고 보니 개념적으로 간단한 건데, 날짜를 배열로 지정해주면 되는 거였다. 매주 월,화 오전 7시 19분이라는 스케줄 설정은 이렇게 하면 된다.

   <key>StartCalendarInterval</key>
   <array>
       <dict>
           <key>Hour</key>
           <integer>7</integer>
           <key>Minute</key>
           <integer>19</integer>
           <key>Weekday</key>
           <integer>1</integer>
       </dict>
       <dict>
           <key>Hour</key>
           <integer>7</integer>
           <key>Minute</key>
           <integer>19</integer>
           <key>Weekday</key>
           <integer>2</integer>
       </dict>
   </array>

 

이것은 Lingon을 통해서는 만들어낼 수 없는 형식이라서, 일단 기본적인 뼈대만 Lingon의 Basic Mode에서 만들고, Expert Mode로 바꿔서 XML을 편집하는 식으로 설정해야 한다. Expert Mode에서 Basic Mode에서는 할 수 없는 설정을 추가한 후 Basic Mode로 다시 돌아가 내용을 바꾸면, Expert Mode에서 설정한 부분은 날아가버리기 때문에 주의해야 한다.

이 글은 스프링노트에서 작성되었습니다.

TAG Mac OS X
Posted by wafe

댓글을 달아 주세요

2009. 5. 11. 00:41

온라인 상의 개인 정보 카테고리 없음2009. 5. 11. 00:41

Exposed-Blog Confidential « 스튜디오 판타지아 2.0 (꽤 긴 글이라 읽으려면 어느 정도 시간을 투자할 마음의 준비가 필요할 듯 싶다)

미투데이를 쓰다보면 스스로에 대해서 뭔가 더 얘기하고 싶은 일도 있고, 온라인을 통해서 사람들의 반응을 듣고 싶을 때가 있다. 기본적으로는 친구들, 오프라인에서 알고 지내는 지인들을 주요 독자로 생각하고 있고, 미투데이를 통해서 자주 만나지 않고도 관계를 좀 더 강화할 수 있기를 기대하기 때문이다.

하지만 이미 너무 많은 정보가 노출되어 있는 것 같다. 온라인에 공개된 정보는 한 번 공개된 이상 개인의 의지로 완벽하게 통제할 수 없음은 이미 너무나 잘 알려져 있다.
Posted by wafe

댓글을 달아 주세요

  1. U.Seung 2009.05.11 02:16  댓글주소  수정/삭제  댓글쓰기

    오.. 엄청난 블로그네요.
    기술관련 블로그만 보는데 ..
    저런 블로그도 좀 읽어줘야 겠어요.. ;;

    • wafe 2009.05.11 11:46  댓글주소  수정/삭제

      RSS 구독해놓고 즐겨보는 블로그인데 좋은 포스팅이 많은 거 같다. 구글 리더에 별표 찍어놓은 글이 많네 ㅎㅎ

      이분 블로그 사이드바에 "내가 읽는 블로그"라는 링크들이 있는데 그쪽 블로그들도 들러볼만함.

나는 블로그에 사이드바가 있는 것을 별로 좋아하지 않는다. 정확히는 블로그 포스트 하나만 보여주는 페이지에 글 외에 다른 내용들이 섞이는 걸 그다지 좋게 보지 않는다. 그래서 워드프레스에서 블로그 포스트 페이지만 따로 분리되어 있는 설계를 좋아하고 (워드프레스를 안 쓴지 몇 년이나 되어서 지금은 어떤지 모르겠지만 그 때는 그랬다), 태터툴즈(이제는 텍스트큐브)의 덜렁 HTML 한 장짜리 설계를 별로 좋아하지 않는다. (근데 여러 가지 다른 편의성을 고려하다보니 계속 태터툴즈 계열만 쓰게 되긴 한다)

 

이것은 다분히 블로그 운영자로서의 입장보다는 검색 엔진 사용자의 입장에 가까운 것이다. 포스트 하나만 보여주는 페이지에 최근 댓글이니 최근 포스트니 태그 구름이니 하는 글 내용과 하등 관계없고 시간이 지남에 따라 계속 바뀌는 정보가 섞이면 검색 결과가 왜곡된다. 특정 키워드로 검색해서 나온 페이지인데도 검색어와 전혀 상관없는 블로그 포스트가 나오는 경우가 많다.

 

물론 검색 결과에 나온 글 하나 때문에 어떤 블로그에 들렀다가, 최근 포스트나 태그 구름 등 각종 부가 정보 중에서 흥미있는 정보(어쩌면 원래 검색해서 들어온 목적과는 아무런 관련이 없을수도 있는)를 발견하고 블로그를 둘러보다가 구독하게 되는 경우도 가끔 있기는 하다. 그러나 티스토리나 텍스트큐브 닷컴의 사용 통계를 보았을 때 적어도 내 블로그에서는 검색 엔진을 통해 들어온 사용자 대부분은 검색 결과에 걸린 페이지만 슬쩍 훑어보고 나가버리기 때문에 사용자를 유혹하는 데 큰 효용성은 없다.

 

어쨌거나 이런 두 가지 욕구를 다 만족시키기 위해서는 검색 엔진의 크롤러가 들렀을 때에는 포스트 내용만 보여주고, 방문자가 볼 때에는 부가 정보까지 같이 보여주는 방식을 생각해 볼 수 있겠다.

 

구현 가능성은? 글쎄... 포스트 내용은 static 하게 뿌리고 그 외 잡다한 내용들은 javascript AJAX를 써서 비동기적으로 보여주도록 하면 되려나? 구현 방법에는 여러 가지가 있을 듯 하다.

 

구현 가능성보다도, 다른 사람들은 대부분 이런 기능의 중요성을 그다지 높게 치지 않아서 이렇게 되어있는 거겠지...

 

이 글은 스프링노트에서 작성되었습니다.

TAG SEO
Posted by wafe

댓글을 달아 주세요

  1. U.Seung 2009.05.06 00:18  댓글주소  수정/삭제  댓글쓰기

    그러게요.
    저도 이번에 블로그 디자인 개편하면서 사이드바를 거의다 쳐내 버렸어요ㅋ 괜히 거추장스럽고, 잘 쓰지도 않는다는 생각이 들어서 (조만간 다시 띄울 계획이 있긴 하지만.ㅎㅎ)

    Crawler를 똑똑하게 만든다면..
    여러번 Crawl했을 때, 바뀌는 정보나 해당 블로그의 특성을 뽑아내서 알아서 잘 해줄 수 있지 않을까 생각도 들어요.

    • wafe 2009.05.06 21:43  댓글주소  수정/삭제

      오호, 깔끔해졌네. 나도 정리를 좀 해야겠다. 다시 보니 몇 개 정리해도 될 만한게 눈에 띄는구만. 포기할 수 없는 걸 고르라면 최근 댓글 정도? ㅎㅎ

MS Windows에서 맥으로 옮기면서 불편했던 것 중에 하나가 UI를 키보드로 사용하기가 상대적으로 어렵다는 점이었다. 왜 그런지 모르겠지만 나는 마우스를 사용하는 작업이 상당히 피곤하게 느껴진다. 마우스 버튼을 클릭할 때 손가락 관절이나 집게 손가락 끝부분에 좀 거북한 느낌을 받는 경우가 많고, 키보드에서 마우스로 손을 옮기는 행동도 귀찮다.

 

Windows에서 키보드로 UI를 사용할 때 가장 많이 쓰는 키들이 메뉴를 띄우는 Alt, 콘트롤들(버튼, 텍스트 상자 등) 사이를 이동하는 Tab, 화살표키, 엔터, 스페이스바다. 대화 상자나 입력 폼 같은 게 뜨면 탭 키로 콘트롤들을 이동해 다니면서 스페이스 바 키나 엔터 키로 버튼을 눌러서 다음 단계로 이동하는 사용 패턴이 가장 흔하다고 생각된다.

 

그런데 맥오에스에서 기본적으로는 탭 키로 버튼을 선택할 수가 없다. 무언가 의도가 있는 UI 디자인이겠지만 내 입장에서는 불편하기 그지없다. 다행히 시스템 환경설정에서 조정할 수 있게 되어 있다. "시스템 환경설정"에 들어가서 "키보드 & 마우스"로 들어간 뒤 다시 "키보드 단축키" 탭을 선택하면 창 하단에 "전체 키보드 접근"이라는 설정 항목이 따로 나와있다. 여기서 "모든 컨트롤"을 선택해주면 맥오에스에서도 탭 키로 버튼을 선택하는 것이 가능해진다.

mac_key_access.png

 

 

이 글은 스프링노트에서 작성되었습니다.

TAG Mac OS X
Posted by wafe

댓글을 달아 주세요

2009. 4. 28. 11:17

Google Test의 문자열 비교 Assertion 카테고리 없음2009. 4. 28. 11:17

UnitTest++과 비교했을 때 상대적으로 Google Test가 좋은 점 중에 한 가지는 문자열 비교이다. Google Test는 wide character 문자열도 잘 비교해주기 때문에 다음과 같은 문자열 비교 assertion을 써서 실제 값과 다른 경우에는 아래와 같이 문자열 값을 실패 메시지에 포함시켜서 보여준다.

  1. ASSERT_STREQ(_T("-111"), xe->GetProperty(_T("Canvas.Left")));

(노파심에 프로젝트명은 지움)

GoogleTestStringComp.png

 

Google Test의 문자열 비교 assertion에는 이런 것들이 있다.

Fatal assertion Nonfatal assertion
ASSERT_STREQ(expected_str, actual_str); EXPECT_STREQ(expected_str, actual_str);
ASSERT_STRNE(str1, str2); EXPECT_STRNE(str1, str2);
ASSERT_STRCASEEQ(expected_str, actual_str); EXPECT_STRCASEEQ(expected_str, actual_str);
ASSERT_STRCASENE(str1, str2); EXPECT_STRCASENE(str1, str2);

이 글은 스프링노트에서 작성되었습니다.

Posted by wafe

댓글을 달아 주세요

2009. 4. 23. 23:00

shared_ptr의 custom deleter 사용법 카테고리 없음2009. 4. 23. 23:00

내가 처음 custom deleter를 봤을 때에는, shared_ptr 생성자에서 한 번 custom deleter를 지정해주면 그 다음부터 reset()을 하더라도 항상 처음에 지정했던 deleter를 불러줄 거라는 기대가 있었다. 그래서 테스트를 해봤더니 그런게 아니네. custom deleter를 유지하고 싶다면 reset()을 할 때 deleter도 같이 넘겨줘야 한다.

 

아래 테스트 코드는 custom deleter를 지정하지 않고 reset()한 경우에는 custom deleter가 호출되지 않는다는 걸 Google Test를 이용해서 보여주는 코드이다.

 

  1. class TestSharedPtrFixture : public testing::Test
    {
    public:
       std::stringstream ss;

       TestSharedPtrFixture()
           : ss(std::stringstream::out | std::stringstream::out)
       {
       }

       void Deleter(int* p)
       {
           ss << "dtor()" << std::endl;
           delete p;
       }
    };

    // shared_ptr 기능을 제대로 알기 위한 테스트
    // shared_ptr reset() 메서드에서 dtor를 별도로 지정하는 경우 reset() 메서드를 실행할 때마다 dtor를 지정해줘야 함
    TEST_F(TestSharedPtrFixture, TestReset)
    {
       ss.str("");

       std::tr1::shared_ptr<int> sp;
       ASSERT_EQ(NULL, sp.get());

       sp.reset(new int(9), std::bind1st(std::mem_fun1(&TestSharedPtrFixture::Deleter), this));
       sp.reset(new int(8));
       sp.reset();

       ASSERT_EQ("dtor()\n", ss.str());
    }

    // shared_ptr 생성자에서 dtor를 별도로 지정하는 경우 reset() 메서드를 실행할 때마다 dtor를 지정해줘야 함
    TEST_F(TestSharedPtrFixture, TestCtor)
    {
       ss.str("");

       std::tr1::shared_ptr<int> sp(new int(9), std::bind1st(std::mem_fun1(&TestSharedPtrFixture::Deleter), this));
       ASSERT_EQ(9, *sp.get());

       sp.reset(new int(8));
       sp.reset();

       ASSERT_EQ("dtor()\n", ss.str());
    }

 

이 글은 스프링노트에서 작성되었습니다.

Posted by wafe

댓글을 달아 주세요