Wednesday, July 30, 2014

면접에 대한 이야기 (5)

5. 보안 관련 외국계 대기업 회사

이제 조금 급할 때도 된 것 같긴 하지만
어쨌든 안정적이고 큰 회사로 가고 싶은 욕망 만큼은 아직 꺾이지 않은 시기였다.

광탈 연락을 받자마자 다시 job search에 들어갔는데 괜찮은 job을 발견했다.

우선 job description이 맞긴 맞았는데, 회사가 좀 먼데 있다는 게 단점이었다.
인천 송도.

거기에 뭔가 회사가 많이 유치되고 있다는 건 알고 있었는데
거기까지 출퇴근 하기에는 무리지 않을까 싶었지만,
예전에 인천-서울로 출퇴근 하는 거리를 체감해 봤던 터라 크게 거부감이 들지는 않았다.

일단 job description에 대한 게 상당히 맞았기 때문에 채용 공고를 올린 헤드헌터에게 연락을 했고 빠른 시간에 contact 할 수 있었다.

담당자와 얘기해봤을 때는 이 회사와 상당히 연관이 있는 것으로도 판단이 되고
거기 팀장과도 아는 사이라고 주장해서 그런가 보다 싶었다.
그리고 여기 헤드헌터 회사 이외에는 job description이 올라온 곳은 없었기 때문에 믿을 수 밖에 없었다.

특이한 점은 자기들은 사람을 보고 판단하기 때문에
직접 회사로 와서 방문하여 얘기를 나눠야 한다는 점이었다.

지금껏 여러 헤드헌터들을 겪어 봤지만 직접 만나야 한다는 조건을 건 헤드헌터는 처음이었기에 신선하기도 했고, 위치도 마포 쪽이어서 가깝기도 했기에 선뜻 응했다.

마포의 조그만 오피스텔에 헤드헌터 회사인데,
실제 담당 헤드헌터와 얘기 하는데 큰 문제는 없었다.
이력서 수정만 조금 잘 하면 될 거라고 했고 연봉도 자기들이 생각한 수준으로 얘기하면 될 거라고 해서 연봉도 정해줬다.
뭐 마지막 연봉보다야 높은 편이라 큰 불만 없이 받아들였고 순조롭게 진행되는 듯 하였으나...

그 회사 쪽에서 나 외에 대리급 개발자 한명 더 필요한데 이왕이면 같이 일해서 호흡을 맞춰 본 사람이면 좋겠다고 해서 다른 건 대충 얘기했는데 이 부분을 너무 강조해서 얘기했다. 생각나는 사람이 있긴 했지만 재직중이라 얘기하기도 껄끄럽고 어쨌든 안된다는 생각으로 찾아는 보겠다고 영혼없는 대답을 해 줬다.

마지막으로 또 하나는 외국계 회사다 보니 추천서를 받아와야 하는데, 전 직장에 아시는 분에게 부탁해 보라고 해서 뜻하지 않게 전 직장에 가야 할 상황이 왔다.
솔직히 전 직장은 가기 싫었다. 아직까지 부장님에 대한 불만의 앙금이 남아 있던 터라 전전 직장의 부장님을 찾아 갔고 오랜만에 또 만나서 이런 저런 얘기를 했다.

추천서는 귀찮았는지 포맷만 주고 알아서 하라고 던져 줘서 내가 대충 맞춰서 쓰면 될 것 같았고, 사람은 진짜 타이밍 좋게도 같이 일했던 개발자가 곧 퇴직한다 해서 그 친구를 추천해 봐야 겠다 생각했다.

집에 오는 길에 집에서 놀고 있는 그 친구에게 연락해서 내가 헤드헌터 통해 구직중인데 같은 직장에 지원해 보라는 얘기가 있으니 한번 같이 해보자 해서 그 친구도 이력서를 제출해서 같이 지원했다.

그렇게 별 문제 없이 이력서 지원을 했는데 생각보다 면접 연락이 오지 않았다. 서류 심사는 통과된 것 같은데 조금 더 기다려 보라고 한지 2주째.
드디어 면접의 날이 왔고 송도까지 머나먼 여행을 한 끝에 면접을 진행했다.

안내 데스크 옆에 넓은 대기실도 있었고 음료도 마실 수 있고 잡지, 신문 등도 있어서 지루하지 않게 기다릴 수 있었다. 드디어 내 면접 차례가 왔는데, 책상 위에 둔 면접 대상자 리스트를 곁눈질로 보게 되었다. 총 5명이었고 내가 마지막이었는데, 같이 지원했던 그 친구 이름은 보이지 않아서, 서류 심사에서 탈락했구나 생각했다.

면접은 생각보다 대충 보진 않았다. 중요한 기술적인 질문 몇 개를 했고 나름 잘 대답하긴 했으나, 면접 보고 나서 항상 드는 생각은 더 좋은 대답과 내가 알고 있는 대답이 있었음에도 불구하고 대답을 잘 못했다는 점 그게 아쉬운 부분이었던 것 같다.

C#의 메모리 해제 문제 (Weak reference)도 그랬고, UI 쪽 뿐 아니라 실제 설계하고 본인이 만들고 문제를 해결해 나갔던 그런 부분에 대한 질문에도 분명 형식적인 대답을 하지 않았는데도 면접관들은 내가 능력이 없어서 못한 것 처럼 생각하고 있었다. - 결국 본인이 해결한 건 없다는 뜻이네요. 라는 말까지 들었으니.

어쨌든 면접이 끝나고 나서 안될 것 같다 라는 걸 조금은 직감했다.
내가 분명 좋은 대답을 못한 것도 있지만, 이 회사에서 내가 필요로 하진 않는 것 같다는 느낌이 조금 더 강했던 것 같다.
면접 진행시에 면접관 한 명이 그냥 아무말 없이 나갔던 것도 그렇고, 그렇게 시계를 봐 가면서 짧게 끝낸 것도 그렇고... 느낌상 난 마지막에 헤드헌터가 억지로 끼워 넣어서 면접 진행했던 한 명이었던 것 같은 그런 느낌.

실제로 며칠 후에 결과도 그래서 크게 아쉽거나 그러진 않았다.
그 보다 더 아쉬웠던 건 도대체 어떤 개발자를 뽑아서 일을 시키려고 한 건지는 모르겠지만 이후에 최근까지 job search 했을 때도 이 회사는 사람을 뽑고 있었다. (적어도 올해 6월 까지는 그랬다.)

이렇게 해서 3월도 결국 이 회사 지원하기 위해서 썼던 시간이 되어 버렸다.
그런데 아쉽거나 그러지 않았다. 다음 회사 면접 진행을 위해 또 준비하면 되겠지 였고, 실제로 진행했으니까.

3월 말.
뜻하지 않게 전전 직장의 정말 개발 잘 하고 친했던 형이 이름만 대면 알만한 회사에서 구직한다는 걸 전해 들었는데 나를 포함하여 몇몇 개발자들에게 지원해 보라 했다.
내가 보기엔 그 형이 지원하는게 더 확실하고 좋을 것 같았는데, 그 형은 지금 회사에 눈치 보인다고 해서 거절했고, 내게 지원해 보라고 해서 또 쉴틈 없이 다음 면접을 위해 시간을 보내게 되는 스토리가 6번 얘기가 될 예정이다.

---

원래 시간 될 때 마다 면접 관련된 글을 쓰고 싶었는데
이런 저런 바쁘다는 핑계와 게으름 때문에 제대로 못하고 있는 것 같다.
4월 얘기면 벌써 3달전 얘긴데 기억이 가물가물 하여 빠른 시간에 모든 이야기를 마무리 지을까 한다.

Tuesday, July 22, 2014

데이트 통장 (혹은 커플 통장)에 대한 계속되는 나의 주장

작년에 데이트 통장 관련된 글을 쓴 후 실제로 실천해 옮겨서 좋은 결과를 얻어 냈고
여자친구와 결혼해서도 가계부를 쓰는 걸로 업그레이드가 되서
서로 지출내역과 잔액 통장관리 등을 계속해서 해 나가고 있다.

데이트 통장 1부
데이트 통장 2부

이쯤되면 적어도 나와 와이프 사이에서는 데이트 통장이 없는 버전으로 잘 해냈다고 생각한다.

그러다가 문득 네이버에서 다음과 같은 걸 발견했다.

커플통장 만드는게 좋을까? 당신의 생각은?

커플통장에 대한 설문조사를 한 건데 아무래도 찬성의 의견이 조금 더 많은 듯 싶다.

문제는 반대표를 던진 사람들의 의견인데 대부분이 헤어졌을 때의 뒷처리 곤란 문제가 대다수이다.
내가 언급했던 문제점에 대한 글을 보고 보완한다면 좋을 방향으로 갈 수 있지 않을까 싶다.

실제 작성했던 스프레드시트는 공개가 불가능하지만 예제로 만든 시트는 보여줄 수 있다. 꼭 이렇게 하라는 건 아니고 단지 예시일 뿐이니 이정도로만 관리해 줘도 누가 얼마만큼 썼고 서로 얼마만큼의 돈으로 써야 하는지 관리가 된다.

2014 커플 통장 예시

사실 실물 통장을 만드는게 서로에게 더 물리적인 연결 고리를 만들어 주지 않을까 하는 생각에 내 방법이 마음에 들지 않을 수 있다.

하지만 그건 기우에 불과하며 통장 관리의 귀찮음보다는 실제 서로 얼마를 썼고 앞으로 어떻게 돈을 써야 하는지에 대한게 관심사이다 보니 내 방법으로 하는게 더 좋다고 자부한다.

내 블로그를 방문해서 이 글을 보는 커플들이 내가 제안한 방법을 써서 효과를 봤으면 좋겠다.

Monday, July 21, 2014

WPF globalization language, using singleton class indexer property (WPF 다국어 작업, 동적으로 해보자)

지난 포스트에 이어...

쉽게 하는 방법

지난 포스트는 쉽게 하는 방법에 대해 쓴 것이다.
단!
요구사항이 프로그램 실행중에 언어를 변경해도 런타임시에 변경되지 않아야 한다는 조건에서다.
왜냐하면 CurrentUICulture는 처음 실행될 때 세팅해 줄 수 있고, 그 이후에는 그 언어에 맞는 리소스 dll 파일을 가져와 바인딩 하기 때문에 런타임시에 변경이 불가능하기 때문이다.

그리고 이러한 요구사항은 아래와 같은 이유로 종종 타협이 되서 런타임시 언어 변경 기능은 빼는 경우가 있다.

1. 규모가 큰 프로그램의 경우 리소스를 사용하는 갯수가 수백 수천개이기 때문에, 일일이 리소스 별로 변경되는 걸 Notify 해 주기가 어렵다. (즉, 바인딩 된 언어 리소스의 Notify가 쉽지 않기 때문에) 라고 실드치는 경우. 이유는 명백하다, 처음부터 다국어를 고려하지 않고 만들었기 때문이다.

2. 크롬이나 기타 윈도우 프로그램 (심지어 윈도우 OS)의 경우도, 언어 변경 후 프로그램을 재 시작하라는 메시지와 함께 재시작 하는걸 당연하게 생각하는 사용자가 있기에 우리 프로그램도 런타임 시에 언어 변경이 고객의 필수(?) 요구사항이 아닌 이상 굳이 그렇게 까지 해야 하나? 라고 반문하는 경우.

그래서 첫 포스팅의 경우 처럼 개발자도 작업하기 쉽고, 언어 변경 후 프로그램 다시 재시작 하는게 크게 어려운 일이 아니기에 쉬운 방법으로 많이들 사용한다.

그럼에도 불구하고!
런타임시에 언어 변경이 아주 불가능한건 아니기 때문에 이번에 한번 소개해 보고자 한다.
이건 c#의 indexer를 PropertyChanged 이벤트를 줄 수 있기 때문에 착안한 것이며, 이걸 Singleton 패턴으로 instance를 관리하여 작성하였다.

프로젝트 환경은 다음과 같다.
OS: Windows 8.1
Framework: .NET Framework 4.5
Development Tool: Visual Studio Ultimate 2013
Language: C#
Project: WPF Class LIbrary


* Source description

1. Single instance (Singleton)
한 언어별 리소스들을 일괄로 관리하기 위해서 Singleton 패턴을 사용하여 하나의 인스턴스로 관리한다. 나중에 일괄로 PropertyChanged할 때 용이하다.


private static volatile LanguageResources instance;
private static object syncRoot = new Object();


public static LanguageResources Instance
{
    get
    {
        if (instance == null)
        {
            lock (syncRoot)
            {
                if (instance == null)
                {
                    instance = new LanguageResources();
                }
            }
        }
        return instance;
    }
}


2. Managed dictionary collection
내부적으로 key-value로 관리할 dictionary 클래스를 사용한다. 리소스 자체가 key-value pair이기 때문에 이만한 collection이 없다. 그리고 dictionary가 변경될 때 마다 PropertyChanged를 걸어준다. "item[]"(Binding.Indexer)을 PropertyName으로 주면 indexer에 바인딩이 걸린 모든 Binding path에 notify가 간다. 5번에서도 설명될 것이지만 각 ViewModel의 PropertyChagned도 여기서 모두 걸어준다.


private Dictionary<stringstring> _resourceDictionary;
public Dictionary<stringstring> ResourceDictionary
{
    set
    {
        _resourceDictionary = value;
        if (_resourceDictionary != null && PropertyChanged != null)
        {
            // Set property name "Binding.IndexerName" for PropertyChanged event
            PropertyChanged(thisnew PropertyChangedEventArgs("Item[]"));
            // call PropertyChanged in registered viewmodels implement INotifyPropertyChanged interface
            foreach (var item in NotifyPropertyChangedDictoionary)
            {
                if (item.Key != null && item.Value != null)
                {
                    foreach (string propertyname in item.Value)
                    {
                        PropertyChanged(item.Key, new PropertyChangedEventArgs(propertyname));
                    }
                }
            }
        }
    }
    get
    {
        return _resourceDictionary;
    }
}

3. Using indexer
인덱서의 경우는 c# 기본적인 것이니 자세한 설명은 생략하고, Singleton 객체에 indexer property를 노출시킨다. 직관적으로 key값을 주면 내부 dictionary에서 검색하여 value를 리턴하는 식으로 만든다. code behind에서 동적으로 세팅해 주는게 편할 수도 있지만, xaml 코드에서 정적으로 binding 하는게 더 강력하다. 왜냐하면 이 indexer에 PropertyChanged가 걸리기 때문이다.


public string this[string key]
{
    get
    {
        string value = key == null ? "" : key;
        if (ResourceDictionary != null && ResourceDictionary.ContainsKey(key) == true)
        {
            value = ResourceDictionary[key];
        }
        else
        {
            value = string.Format(Settings.RESOURCE_NOT_FOUND_MESSAGE, key);
        }
        return value;
    }
}

4. Load resource file (DataContractJsonSerializer)
JSON 포맷으로 되어있는 텍스트 파일을 읽어와서, 최종적으로 dictionary의 key, value collection 형태로 만든다. 단, Serialize 가능하게 만드는 것이 file read, write 할때도 직관적이면서 쉽다. 예제의 경우에는 JSON 포맷으로 텍스트 파일을 만들었으며, DataContractJsonSerializer 클래스를 사용하여 serialize 했다.


private void LoadResource()
{
    string fileStream = "";
    try
    {
        string filepath = string.Format(Settings.LANGUAGE_FILE_PATH, CultureName);
        if (DesignerProperties.GetIsInDesignMode(new DependencyObject()) == false)
        {
            fileStream = File.ReadAllText(filepath);
        }
        else
        {
            fileStream = File.ReadAllText(Settings.TARGET_PROJECT_NAME + Settings.OUTPUT_PATH + filepath);
        }
        DataContractJsonSerializer dcjs = new DataContractJsonSerializer(typeof(Dictionary<stringstring>));
        byte[] fileByte = Encoding.UTF8.GetBytes(fileStream);
        MemoryStream ms = new MemoryStream(fileByte);
        if (ResourceDictionary != null)
        {
            ResourceDictionary.Clear();
            ResourceDictionary = null;
        }
        ResourceDictionary = dcjs.ReadObject(ms) as Dictionary<stringstring>;
    }
    catch
    {
        Debug.Assert(false);
    }
}

5. Support ViewModel binding
사실 언어 리소스는 xaml에 정적 바인딩 해 두고 indexer에서 PropertyChanged로 변경 가능하게 하는게 가장 정석이지만, MVVM으로 만들어 둔 ViewModel의 Property에 PropertyChanged가 될 때 각 Property의 get에서 뭔가 string format 처리라던지 분기 처리를 통한 적절한 리소스를 보여주고 싶을 때가 있을 때도 지원 가능하도록 처리를 해 두었다.


#region NotifyPropertyChangedDictoionary
private Dictionary<INotifyPropertyChangedstring[]> _NotifyPropertyChangedDictoionary = null;
private Dictionary<INotifyPropertyChangedstring[]> NotifyPropertyChangedDictoionary
{
    get
    {
        if (_NotifyPropertyChangedDictoionary == null)
        {
            _NotifyPropertyChangedDictoionary = new Dictionary<INotifyPropertyChangedstring[]>();
        }
        return _NotifyPropertyChangedDictoionary;
    }
    set
    {
        _NotifyPropertyChangedDictoionary = value;
    }
}
#endregion
 
#region SetRegisterNotifyPropertyChanged
public void SetRegisterNotifyPropertyChanged(INotifyPropertyChanged sender, params string[] propertynames)
{
    if (NotifyPropertyChangedDictoionary != null)
    {
        NotifyPropertyChangedDictoionary.Add(sender, propertynames);
    }
}

#endregion


어쨌든 이런 식으로 리소스를 관리하면 런타임시에 언어 변경이 된다.
다만 언어별 텍스트 read, write시에 JSON notation error나
중복된 key 입력 정도만 조심하면 된다.

JSON notation error는 웹 상의 json viewer 같은 걸로 체크해 주면 되고
Dictionary class를 deseriazlize 하기 때문에 언어 파일에 중복키가 들어가게 되면 예외가 발생하게 된다. 이 부분을 조심하자.

* Benefit

1. Language resources changed on run time.

손쉽게 런타임 시에 언어 리소스 변경이 가능하다. 애초에 이 목적으로 만들어졌기 때문에 당연히 장점이 된다.

2. Can view design time.

디자인 타임에 리소스를 확인해 볼 수 있는 기능은 어찌 보면 별거 아니지만
실제 사용해 보면 상당히 편하고 좋은 기능임을 알 수 있다.

3. One time called all PropertyChanged event by indexer

Singleton instance에 CultureName이 변경되면 즉시 설정된 리소스 파일을 로딩하게 되고 그 과정에서 PropertyChanged 이벤트가 걸리게 되기 때문에 indexer를 사용하는 모든 binding된 property는 변경이 (저절로 되는 것처럼) 된다. 자세한 내용은 파일 다운로드해서 보면 알 수 있다.


* How to use LocalizationResource project library

1. Set output directory

Project properties -> Build -> Output
여기에 리소스를 사용할 프로젝트의 output 경로를 적는다.



<당연한 얘기지만 리소스를 빌드하면 dll 파일이 나오고 이걸 사용할 프로젝트에 출력 경로로 세팅해 주는 것이다>
 
2. Add reference library to target project.

다국어를 사용할 프로젝트에 이 라이브러리 프로젝트를 참조시킨다.

 
<역시 당연한 얘기지만 참조를 추가해 주는 건 기본>

3. Edit language resource files.

ko-KR, en-US가 기본으로 있으며 테스트용으로 key-value 값이 몇 개 들어 있다.
사용하고 싶은 key-value 값을 두 파일에 동일하게 추가해서 리소스 파일을 만들어 간다.


<Resources.en-US.txt>
<Resources.ko-KR.txt>
4. Use resource to xaml. You can see resource values design time.

디자인 타임에 실시간으로 리소스의 키를 입력했을 때 값이 확인되는 건 더할 나위 없이 좋은 기능임을 강조하고 싶다. 실제 쓸 때도 static binding으로 세팅만 해 두면 나중에 언어가 변경되더라도 PropertyChanged가 걸려서 언어에 맞는 리소스로 변경되서 보여진다.

<디자인타임에도 리소스가 보이는 걸 볼 수 있다.>
5. To see other property binding, download sample zip file.

Static binding 뿐 아니라 MVVM의 ViewModel binidng 까지 예제가 있으니 다운로드 해서 확인해 보면 된다.

LocalizationResources sample project file

<한국어>

파일 다운로드 방법
- 링크를 눌러 새 창이 뜨면 Ctrl+S 를 누른다.
- 아니면 메뉴에 파일 > 다운로드를 클릭한다.

<English>

How to download zip file.
- Link click, new tab opened -> press key 'Ctrl+s'
- or File > Download menu click.

Friday, July 18, 2014

WPF globalization language, using resx file (WPF 다국어 작업, 쉬운 방법)

WPF로 다국어 작업을 해야 하는데 이것저것 검색해 보면 다음과 같은 것들을 접할 수 있다.

Resgen.exe
Al.exe
LocBaml.exe
msbuild.exe
uid

이것저것 검색해 봐도 쉽고 직관적으로 할 수 있는 방법은 없는 것 같고
뭔가 command line 명령으로 파일 생성하고 수정하고 해서 로딩해 오는 방법을 써야 한다.

결국 이런 방법들의 공통점은 아래와 같다.
1. resource 가 있는 파일이 있고
2. 내가 원하는 언어를 선택하여 언어에 맞게 resource를 로딩해 오면
3. 그걸 사용하는 것이다.
4. 그것도 정적으로!

이게 무엇인고 하니 시작하기 전에 System.Threading.Thread.CurrentUICulture를 세팅해서 쓰는 건 뭔 짓을 해도 공통이라는 것이다.

런타임시에 동적으로 언어 변경하는 건 다음 포스팅에서 설명하기로 하고
우선 정적으로라도 하는 방법, 그것도 쉽게 하는 방법을 써보자.

이것 만큼 쉽게 하는 방법은 없다고 본다.
(만약 있다면 제보 부탁!)

WPF이니 Window일 것이고 내 개발 환경은 다음과 같다.
OS: Windows 8.1
Framework: .NET Framework 4.5
Development Tool: Visual Studio Ultimate 2013
Language: C#
Project: WPF Application

<그냥 WPF 프로젝트 만들면 볼 수 있는 친숙한 화면>

이름을 ResourcesSample로 생성하면 기본적으로 위와 같은 화면이 나올 것이다.
여기서 부터 순서대로 해보면,

1. Add resource value in Resources.resx
이 파일을 열면 기본 리소스를 관리할 수 있는 엑셀 파일 같은게 나오는데 두 개를 추가해 본다.
String1=야옹, String2=멍멍

<Resource.resx 파일을 열면 리소스 관리가 가능하다>


2. Static binding text in MainWindow.xaml
리소스를 사용해 본다.
xmlns:res="clr-namespace:ResourcesSample.Properties"를 추가해 주고
TextBlock에 Text="{x:Static res:Resources.String1}"을
쓰면 디자인 타임에도 볼 수 있듯이 "야옹"이라는 글자가 잘 나온다.

<Design time에도 방금 추가한 리소스가 잘 바인딩이 되는 걸 볼 수 있다>

3. Change access modifier
그런데 실제 빌드해 보고 실행하려 하면 오류가 난다. 왜냐하면 String1은 internal 한정자라 public으로 바꿔줘야 하는데 아래 처럼 엑세스 한정자를 public으로 바꿔준다. String1뿐 아니라 Resource내에 모든 리소스가 public으로 변경 된다. 일단 실행은 잘 된다.

<엑세스 한정자를 public으로 변경한다. 그래야 String1의 키 값이 public으로 변경되고 접근할 수 있게 된다.>

<진짜 별거 없지만, 바인딩 된 "야옹" 텍스트가 잘 나오는게 확인 된다>


4. Add other Resource file
지금은 한국어용 리소스를 넣고 테스트 한 것이고, 영어 리소스를 넣어야 한다.
여기서 주의해야 할 것은 리소스 파일 이름을 CultureInfo 클래스의 Name대로 이름을 명명해야 한다는 것이다. 관련된 건 구글 좀 찾아보면 나오니 자세한 설명은 생략하겠다. 기존에 Resources.resx 파일과 같은 namespace 상에 있어야 하기 때문에 Resources.resx 파일을 Ctrl+C, Ctrl+V해서 추가한 뒤에 이름을 Resources.en-US.resx로 바꿔준다. 그리고 같은 String1, String2라는 이름을 가진 값을 "Yaong", "MeongMeong"으로 추가해 본다. 

<추가한 "Resources - 복사본.resx" 파일을 "Resources.en-US.resx"로 변경한다.>

5. Confirm output folder after build.
그리고 빌드를 해서 output 폴더(프로젝트 폴더의 bin/Debug/)를 보면 "en-US"라는 폴더가 생기고 그 안에 ResourcesSample.resources.dll 파일이 있는 걸 볼 수 있다.

<드디어 영어 리소스 파일이 생긴 것이다>
6. Set CultureInfo
그럼 마지막으로 App에 아래와 같은 코드를 추가한다.
이건 프로그램이 시작하기 전에 CultureInfo를 세팅하는 것인데 "en-US" 리소스를 추가했기 때문에 "en-US"로 생성한다.
그리고 CurrentUICulture에 세팅하면 모든 리소스가 우리가 추가한 언어의 리소스로 변경이 된다.
System.Threading.Thread.CurrentThread.CurrentUICulutre = new CultureInfo("en-US");

<CurrentUICulture에 "en-US"의 CultureInfo 객체를 세팅하면 끝이다>

<확인해 보면 별거 없지만, CurrentUICulture에 세팅한 대로 "Yaong" 텍스트가 나온다>

WPF 한 사람 치고 이 정도 했는데 다국어 못하겠다고 한 사람은 없을 것이다.
이후 작업은 이름-값에 해당하는 리소스들을 계속 추가해 주고 쓰는 것 뿐이다. 시간 싸움이라는 뜻이기도 하다.

그런데 다들 이런 식 -> 이런 식이라 함은 프로그램 시작시 리소스를 로딩해 와서 바인딩이 되는 식이라는 뜻이다. 런타임 시에 동적으로 되는 예제가 몇 있긴 한데 그것도 역시 리소스 파일을 가져와서 바꿔치기 하는 식이고 리소스 key-value를 메모리에 올려서 쓰겠다는 뜻이기도 하다.

다음 포스트에는 런타임시에 다국어 적용에 대해 써볼까 한다.


<한국어>
파일 다운로드 방법
- 링크를 눌러 새 창이 뜨면 ctrl+s를 누른다.
- 아니면 메뉴에 파일->다운로드를 클릭한다.

<English>
How to download to zip file.
- Link click, new tab opened -> press key 'ctrl+s'
- or File -> Download menu click.