이전 글에서 이어집니다. :-) 2. 닷넷은 Pointer 환경이다? (닷넷에는 Pointer밖에 없다?)2.1. 닷넷은 값 형식을 제외하곤 모두 Pointer이다."닷넷에는 포인터가 없다" - 또는 - "일부 언어에서만 호환성을 유지하기 위하여 포인터를 남겨놓았을 뿐 포인터는 자바와 마찬가지로 사용하지 않는다"라고 닷넷의 장점이라고 생각하는 것은 입문자도 외우고 있다. 하지만 이 부분은 의외로 닷넷을 혼란스럽게 하는 주범이라고 생각한다. 닷넷에 포인터가 없기는 커녕 값 형식 (Primitive (int, short, char, long, ...), 구조체, 나열 상수 (enum))을 제외하면 "포인터 밖에 없는 환경이다"라는 명제가 성립되게 된다. 사실 여기서 포인터라고 함은 C의 그것과는 조금 다른 reference(참조)로 보는 것이 온당하지만... "즉, 닷넷의 클래스형의 변수는 모두 포인터이다." 덧. System.IntPtr, System.UIntPtr은 정수형 포인터를 가리키는 형식으로 자바가 정확하게 지원해주지 않는 C 언어의 포인터를 닷넷 환경에서는 이들 형식을 이용하여 다룰 수 있다. 그러나 이 글에서 이야기하는 개념과는 거리가 먼 상호 운용성에 관한 토픽이므로 큰 관련성이 없다. 2.2 null은 객체인가?닷넷에서 공참조 (힙에 실제로 참조되는 object가 없는 참조)의 경우는 당연히 객체가 붙어있지 않다. 이러한 상태에서 다음의 MSIL 명령어를 호출하는 동작을 수행할 경우 NullReferenceException이 발생한다고 닷넷 SDK 문서 상에는 기술되어있다.
위에서 강조 표시한 항목들은 실제 C#, VB.NET 환경에서 만날 수 있는 사항들과 관련이 있는 것을 열거한 것이다. 여기서 논점이 되는 것은 null을 개체로 볼것인가 아닌가에 대한 문제이다. 공참조는 어떤 객체도 참조하고 있지 않는다고 단정하고 있다. 하지만 '==' 연산에 있어 두개의 객체가 모두 null이거나 동일한 객체 또는 배열 참조의 경우 true라고 되어있는것으로 봐서 서로 다른 두 객체가 동일한 null을 참조하고 있으므로 true가 된것이 아닌가 하는 생각을 할 수 있다. 즉, null이 Object의 instance 형태는 아니지만 개념적으로 봤을 때 null도 object라고 봐야 하지 않을까? 덧. C# 2.0부터 새롭게 소개된 Nullable 형식에서 사용하는 null 키워드는 그 의미가 조금 다르다. 앞서 값 형식은 참조 형식과는 달리 값 그 자체를 취급한다고 하였는데, 데이터베이스 환경에서는 이런 설정과는 또 다르게 값 형식에도 null 상태를 허용하고 있다. 이런 불일치성을 해결하기 위하여 등장한 것이 Nullable 형식인데, 특별히 이 형식에 대해서는 Nullable 형식이 클래스 형식이 아님에도 불구하고 null을 값을 지정하지 않고 Nullable 개체를 새로 만든 것으로 대체하여 처리한다. int? aa = null;
System.Nullable<int> aa = null; int? aa = new System.Nullable<int>(); System.Nullable<int> aa = new System.Nullable<int>(); // 값이 지정되지 않은 int 형식을 만들기 위하여 위의 네 구문은 모두 같습니다. int? bb = 3; bool b = aa.HasValue; // 값이 지정되었는지 아닌지를 판단하기 위하여 위의 두 구문은 모두 같다. object o = (aa.HasValue ? aa.Value : null);
object o = aa ?? null; // 값이 있는지 없는지에 따라 선택적으로 값을 가져오게 하기 위한 방안으로 위의 구문은 모두 같다. 2.3 String에 대하여String Object에 대한 생각. string str = "111222";
string a = "111"; string b = "222"; string c = "111"; string d = b; string t = str.Substring(0, 3); // 111 string u = b.Clone().ToString(); // 222 위의 소스를 보고 다음이 참인지 생각해 보자. (== 연산자나 Equals 메서드는 값을 비교하는 것이므로, ReferenceEquals 메서드를 이용하여 같은 개체들인지 확인해보기로 한다.)
2.4 객체 지향의 캡슐화 파괴 주의"object pointer를 반환하는 getter method는 객체 지향의 캡슐화가 파괴될 가능성이 있다." 이는 object형의 field (member variable)의 getter에서 이 object를 그냥 반환하면 이 object를 받은 쪽이나 참조하고 있는 다른쪽에서 이 object의 내용을 변경하게 되므로 사실 캡슐화 (은닉)는 이루어지지 않았다고 보는 것이 정확하다. "이럴 경우 object를 clone(복제)하여 반환하지 않아도 되는지를 반드시 생각해 본다." object의 복사에는 shallow copy와 deep copy가 있다. // (참고) Member에는 두 개의 field (Identity Class 형의 id와 Family Class 형의 family)가 있다.
// Shallow Copy Example public Member ShallowCopy() { Member newer = new Member(); newer.id = this.id; newer.family = this.family; return newer; } // Deep Copy Example public Member DeepCopy() { Member newer = new Member(); newer.id = new Identity(this.id.Identity, this.id.Name); newer.family = new Family(this.family.FamilyName, this.family.FamilyInfo); return newer; } 위 소스에서 보듯이 Shallow Copy는 object를 복사하여 반환한 것 처럼 보이지만, 사실은 Member Object만 새로 생성되었을 뿐 Member의 field는 newere와 this 둘다 서로 같은 힙의 id와 family를 참조한다. 하지만 두 번째 method인 Deep Copy의 경우 member field를 새로 생성하여 복사하므로 서로 다른 id와 family이다. 클래스를 직접 구현하고 있을 동안 System.Object 클래스의 protected 메서드 (외부에서는 사용할 수 없지만 내부적으로 상속되어져 내려오는 메서드) 중 하나인 MemberwiseClone 메서드를 이용하여 자기 자신의 단순 복사본을 생성할 수 있다. 하지만 Deep Copy를 구현하려면 ICloneable 인터페이스를 이용하여 별도로 Clone 메서드를 통하여 구현하도록 하는 것이 바람직하다. 하지만 ICloneable 인터페이스를 구현했다고 해서 이 개체가 반드시 Deep Copy를 지원하는 것이라고는 볼 수 없다.* (참고) object를 immutable (변하지 않는, 불변의 객체)로 만드는 요령
즉, 값을 변경하기 위해서는 object를 다시 만들어야만 하는 불편은 있지만 안전하게 사용하고자할 때 유용한 방법이다. 2.5 배열에 대하여2.5.1 배열은 object인가?CLR에서 배열은 object로 취급되어 object와 같이 기술된다. 클래스의 상속도로 보았을 때에도 모든 배열은 암시적으로 System.Array를 부모 클래스로 하며, System.Array는 다시 System.Object를 부모 클래스로 한다. 그리고 int[] iarr = new int[10]; 구문에서처럼 new 연산자로 Heap 영역에 object를 생성하므로 object임을 알 수 있다. 2.5.2. 배열의 length는 왜 Java와 달리 프로퍼티인가?Java와는 달리 닷넷의 배열은 길이를 가져오기 위한 방법으로 getter property를 이용하는데, Java이든 닷넷이든 이 과정은 컴파일러의 트릭과 함께 CLR의 협조를 기반으로 이루어지는 특수한 논리가 숨어있다. 어찌되었든 간에 배열의 길이를 조사하는 것 자체는 필드도 아니고 메서드도 아니고, 프로퍼티도 아닌 단순한 기계 명령어 하나로 통일된다고 봐야 한다. (이전 섹션에서 언급한 ldlen 명령 참고) 그러나 이런 식의 문법적 요소를 남겨두는 이유는, 언어 호환성을 위한 것으로 컴파일러 제작자가 아닌 일반 소프트웨어 개발자들에게는 피상적인 의미 그 이상이 되지는 않는다. 2.5.3. readonly와 배열에 대하여...우리가 흔히 앞에서도 나온바 있지만 readonly는 값을 변경할 수 없는 것이라고만 생각하지 object로 되어있을 경우 그 object는 변경 가능하다는 것을 잊곤한다. 배열도 object이므로 마찬가지다. readonly int[] iarr = new int[5]; 일 경우 iarr = null; 은 오류로 취급되지만, iarr[3] = 5;는 오류가 발생하지 않는다. 즉, readonly로 지정되어있는것은 iarr이지 iarr이 가리키는 곳의 배열의 요소들은 아니다. 2.5.4. 닷넷에서의 다차원 배열은 논리적인 개념일 뿐이다.가령 2차원 배열처럼 보이는 int[][] iarr 또는 int[,] iarr은 논리적으로는 행열 구조처럼 보일 수 있지만 실제 컴퓨터 메모리 배치는 그렇지 않다. 두개의 배열이 각각 구분되어있는 상태에서 서로 유기적으로 통합된 상태이거나, 지정된 크기만큼의 메모리 공간을 사용하는 1차원 배열이 재구성된 것으로 이해하는 것이 올바르다. 2.6. 인수 (parameter/argument) 전달의 개념2.6.1. 닷넷에서 parameter (argument) 전달은 무조건 'call by value'이다.값 형식의 경우 호출한 쪽의 변수값은 호출 받은 method 내에서 값이 변경되어도 변경되지 않는다. 참조 형식의 경우도 참조되는 object에 대해서는 함께 변경되지만 참조 포인터는 call by value이다. object를 가리키는 pointer는 call by value로 변경되지만 Heap의 실제 object 내용은 변경되지 않는다. 2.6.2. C와 같은 언어는 static linking이지만, 닷넷은 dynamic linking이다.따라서 닷넷은 클래스가 처음에 한꺼번에 메모리에 로드되는것이 아니라 런타임시에 그것이 필요해 졌을 때 로드되고 링크된다. static field의 영역도 클래스가 로드되는 시점에서야 비로소 확보된다. 이렇게 되면 최초 가동 시간이 단축되고 끝까지 사용하지 않는 클래스의 경우 신경 쓸 필요가 없어지게 된다. 따라서 static field는 프로그램이 시작되어 해당 클래스가 필요해 졌을 때 CLR이 알아서 load/link 해준다. 즉, static field는 프로그램이 실행되기 시작할 때 부터 끝날 때 까지 계속해서 존재하는 것이라고 보면 된다. (참고) linking의 의미link된다는 것은 클래스가 memory에 loading될 때 특정 메모리 번지에 loading되는 데 이 메모리 번지는 loading될 때 마다 다른 번지 수에 loading된다. 이 때의 메모리 주소값 (Java에서는 실제 메모리 값이 아닐 수 있다)을 현재 실행 중인 프로그램에서 알 수 있도록 하여 해당 클래스에 대한 참조가 가능하도록 연결하는 과정이다. 정적 (static) link라는 것은 이러한 메모리에 대한 주소 정보를 컴파일 시에 compiler가 미리 결정하는 것이고, 동적 (dynamic) link라는 것은 프로그램 수행 중 결정되는 것을 의미한다. 정적인 link의 경우 직접적으로 메모리의 번지값이 할당되는 것이 아니라 offset 값 (기준 위치로부터의 index 값)으로 연결시킨다. 2.7 GC에 대하여 잠깐!2.7.1. Garbage Collection은 만능이 아니다.닷넷에는 free가 없다. GC가 알아서 해준다. 하지만 GC 수행 중에는 프로그램의 퍼포먼스가 크게 떨어질 수 있기 때문에 GC가 자주 발생하지 않도록 프로그램을 설계하는 것이 좋다. 서비스 되고 있는 시스템에서도 가끔 시스템이 응답이 늦어지는 시점이 있는데, 이는 GC가 수행되고 있기 때문일 가능성이 높다. 그렇다면 GC가 자주 발생하지 않도록 해야 하는데 가장 좋은 방법은 무엇일까? 그것은 바로 불필요한 객체를 생성하지 않는 것이 아닐까? 닷넷에 free가 없다는 것은 매력적이다. 그 이유는 두 개의 변수가 heap 내에서 하나의 object를 reference하고 있을 경우 실수로 하나의 변수만 free해버리면 나머지 하나는 dangling pointer라하여 reference pointer가 모르는 사이에 사라져버려 곤경에 처하는 것을 예방해 주기 때문이다. 참고로 System.Object 클래스에는 Finalize 메서드가 있어 GC 수행 시점에 호출되는 메서드가 있지만 이것은 GC가 언제 수행될지 알 수 없으므로 과신하면 안된다. 덧. 상호 연동성 프로그래밍을 많이 하는 경우, 소멸자와 함께 반드시 생각해야 할 것이 IDisposable 인터페이스의 구현이다. 이 인터페이스를 구현하여, 언제 호출될지 알 수 없는 소멸자가 아닌 명시적으로 호출 가능한 소거 함수를 배치하여 필요없을 때 외부 리소스 (플랫폼 호출을 통하여 부른 malloc 같은 함수들의 메모리 블럭)들을 미리 제거할 수 있다. 그리고 소멸자나 Finalize 메서드와도 Dispose 메서드를 연결시켜서 Dispose가 이루어지지 않아서 발생하는 리소스 누수를 예방해야 한다. 2.8 닷넷 Pointer 결론2.8.1 결국 닷넷에는 포인터가 있는 것인가, 없는 것인가?닷넷은 Heap 내의 Object를 참조(reference)하고 있고, 참조는 결국 개념이 포인터와 유사한 것이므로, 닷넷에 포인터가 없다는 것은 어불성설이다. 주. 이 부분에 대해 Object를 이해하시면 족히 이런 문제는 사라질 것으로 봅니다. 클래스에 대한 인스턴스 (object)들은 reference로 밖에 가질(참조될)수 없기 때문입니다. 컴파일러 입장이 아닌 기반 언어들 (C#, VB.NET, J#, C++ CLR 등)의 사상을 가지고 이해하는 것이 좋을듯 합니다. java.sun.com의 Java programmer's FAQ에 이런 질문 글이 올라온 적이 있다. "Java에는 pointer가 없다고 하는데, linked list는 어떻게 만들어야 하는가?"라는 질문이었는데 이에 대한 답이 참 명쾌하다. 이것은 비슷한 메카니즘을 가지는 다른 GC 환경 플랫폼에서도 동일하게 적용되는 것이다. Java에 관한 많은 오해 중에서 이것이 가장 심각한 것이다. 포인터가 없기는 커녕 Java에 있어 객체지향 프로그래밍은 오로지 Pointer에 의해 행해진다. 다시 말해 객체는 항상 포인터를 경유해서만 access가 이루어지며 결코 직접적으로 access 되지 않는다. pointer는 reference (참조)라고 불리며 당신을 위해 자동으로 참조된다. 닷넷에 포인터가 없다고 주장하는 모든 서적과 글들의 내용은 혼란을 야기할 가능성이 크다. 이러한 주장은 GC 플랫폼 환경을 정확히 숙지하지 않은 사람의 서술일 가능성이 높으므로 재고해보아야 할 표현이다. |
'닷넷'에 해당되는 글 15건
- 2008/08/18 남정현 개발자가 놓치기 쉬운 닷넷의 기본 원리 #2: 닷넷은 Pointer 환경이다? (닷넷에는 Pointer밖에 없다?)
- 2008/08/18 남정현 개발자가 놓치기 쉬운 닷넷의 기본 원리 #1: 객체 지향의 구멍 static
- 2008/07/20 남정현 리눅스에서 닷넷하기: MonoDevelop에서 만드는 Hello World
- 2008/06/17 남정현 HwpCtrl ActiveX 컨트롤에 관한 문제점 해결 방안 두 가지
- 2007/12/14 남정현 Mono 1.2.6 런칭!
- 2007/11/22 남정현 Known Issue: printf와 Visual Studio HOST 디버깅
- 2007/10/25 남정현 C#의 알려지지 않은 비공식 키워드들 (2)
- 2007/08/10 남정현 Delphi 2007과 MSBUILD
- 2007/08/05 남정현 삽질 하나: rundll32.exe와 ~RunDLL 시리즈 함수들
- 2007/08/03 남정현 C# 3.0 Preview: Extension Method와 나머지 (2)
개발자가 놓치기 쉬운 닷넷의 기본 원리 #2: 닷넷은 Pointer 환경이다? (닷넷에는 Pointer밖에 없다?)
Software Development/.NET Framework | 2008/08/18 15:03 |트랙백을 보내세요
'개발자가 놓치기 쉬운 자바의 기본 원리' 라는 글의 내용을 보고 같은 내용을 닷넷을 기준으로 서술한다면 재미있을 것 같다는 생각에 곧바로 글을 옮겨적어봅니다. 좋은 소스를 제공해주신 다음 커뮤니티본부 커뮤니티개발1팀의 전성호님께 감사드립니다. 이 글이 닷넷에 입문하시는 분들이나 기본적인 원리때문에 고민하셨던 분들께 좋은 역할을 해주기를 바랍니다. 출처: http://dna.daum.net/technote/java/PrincipleOfJavaInternalForDeveloperEasyToLost 요약: 개발자가 놓치기 쉬운 닷넷의 기본 원리에 대하여 기본적이긴 하지만 개발하면서 느끼고 경험한 내용을 정리하였다. 1. 객체 지향의 구멍 static1.1 닷넷은 객체 지향 환경이다?"닷넷은 자바와 마찬가지로 객체지향 언어이다" 라는 주장을 자주 접하게 된다. 만일 이것이 사실이라면 닷넷을 사용하는 한 "기존의 절차 지향 프로그래밍을 전혀 할 수 없을게 아닌가?" 라는 생각이 들지만 사실은 그렇지 않다. static을 이용하면 비 객체지향 언어처럼 코딩할 수 있다. static method, static property, static event는 instance가 아닌 클래스에 속하는 method로, class method, class property, class event라고 부른다. 반대로 static이 아닌 method, property, event은 instance method, instance property, instance event라고 부른다. static method, static property에는 this가 없다. instance method, property에는 숨겨진 파라미터로 this가 건네진다. (아래 "객체지향에 흔히 있는 오해" 참고) 하지만 static method, static property는 절차 지향의 함수와 동일하므로 숨겨진 파라미터 this는 없다. 그래서 static method, static property에서는 전달한 this가 없으므로 instance method, instance property를 호출하거나 instance field를 참조할 수 없는 것이다. (참고) 객체지향에 흔히 있는 오해
1.2 전역 변수static에서 public field는 전역 변수 (global variable, 글로벌 변수)이다. 여기서 "글로벌 변수는 왜 안되는가"에 대해 잠깐 생각해 본다. 우리는 흔히 "글로벌 변수는 될수있는한 사용하지 않는 것이 좋다"라고 한다. 그 이유는 글로벌 변수는 어디서든 참조할 수 있고 값을 변경할 수 있기 때문이다. 또한 파라미터나 리턴값으로 교환해야할 정보를 글로벌 변수를 경유(사용)하여 건네주면 함수의 역할이 불분명해지고 흐름도 애매해진다. 마지막 이유로는 "글로벌 변수는 하나밖에 없다"는 것이다. 이는 어디서 이 값을 변경했는지 알 수 없게 하는 지름길이고 실무에서도 간혹 발생하긴 하지만, 이 하나밖에 없는 변수가 버전업으로 두개가 필요하게 되었을 때 확장도 대형 프로젝트에서는 힘들어진다. 따라서 static에서 public은 readonly나 const를 붙여 상수나 읽기 전용 변수로 사용해야지 그 외의 용도는 자제하는 것이 좋을 것이다. (참고) readonly 초기화에서의 주의점. 예를 들어 다음과 같은 코드를 보았을 때 우려되는 점은 무엇인가? public static readonly System.Drawing.Color White = new System.Drawing.Color(255, 255, 255);
readonly 변수는 인라인 문장과 생성자 권역 내에서 한 번 초기화되면 변경이 불가능한데 object로 초기화할 경우 WHITE라는 필드가 변경될 수 없는 것이지 그것이 가리키는 객체는 아니라는 점이다. 과거 신규 서비스 개발시 readonly 변수 필드에 설정파일을 읽어 cache하는 singleton class의 특정 member를 이용하여 초기화할 경우 이 멤버값이 변경되면 readonly 변수의 값이 변경되었는데 프로그램에서는 이상한 짓을 하는 원인을 찾기가 상당히 어려웠던 경험을 하고난 후 부터 이런 코드는 냄새나는 코드로 여겨지게 되었다. static은 글로벌 변수와 동일하므로 남발해서는 안된다. static을 사용할 경우 다음 두 가지는 최소한 기억한다.
public class T
{ private int a; private int b; private int c; private int Calc() { c = a + b; return c * c; } // ....other method or getter/setter... } 위의 클래스 T의 경우 내부에서는 Calc라는 instance 함수를 사용하게 되면 c의 값이 매번 변하게 된다. 이는 무심코 하는 실수로 클래스 내에서 private method는 모든 멤버 instance 변수에 접근 가능하게 되면서 발생하게 된다. c의 값이 변하지 않기를 바랄 수 있다. 이 때 안전한 방법은 다음과 같이 Calc 하청 메서드를 static method로 수정하면 안전하다. private static int Calc(int a, int b)
{ int c = a + b; return c * c; } 여기서 a와 b는 멤버 변수를 접근할 수 없어 전달해야 한다. (static에는 this가 없어 instance field를 참조할 수 없다는 것은 이미 위에서 설명했다.) 또한 c도 같은 이유로 사용할 수 없어 로컬 변수로 선언하여 사용하고 있다. 이럴 경우 메서드가 약간 커질 수 있지만 instance member 변수를 안전하게 사용할 수 있다는 장점이 있다. 이것은 static을 다시한번 생각하게 하는 좋은 예가 되었을 것이다. |
트랙백을 보내세요
댓글을 달아 주세요
이번 강좌에서는 Mono를 이용하여 Hello World 콘솔 응용프로그램을 만들어보고 MonoDevelop와 Visual Studio의 차이점을 살펴보기로 하겠습니다. Visual Studio가 Windows용 응용프로그램을 위하여 최적화된 환경을 제공한다면 MonoDevelop는 Linux 응용프로그램, 정확하게는 Gnome Desktop을 위하여 최적화된 환경을 제공합니다. 이번 강좌에서는 지난번에 이어 VMware 기반 Mono 패키지를 이용합니다. 다음의 웹 사이트에서 Mono 패키지와 VMware Player를 무료로 다운로드받으실 수 있습니다. 새 프로젝트 만들기 VMware 패키지를 부팅한 이후에 바탕 화면에 MonoDevelop 아이콘이 있을 것입니다. 이것을 더블 클릭하면 MonoDevelop IDE가 시작됩니다. 위의 화면에서 Start New Solution을 클릭하면 아래와 같은 대화 상자가 나타납니다. Visual Studio의 새 프로젝트 만들기 대화 상자와 거의 비슷합니다. 하지만 Visual Studio와는 달리 Boo와 같은 신흥 닷넷 기반 스크립트 언어와 MSIL에 대한 직접 지원 (ILAsm), Java 프로그래밍, NUnit 프로젝트 등을 지원합니다. 패키징 프로젝트는 Visual Studio가 제공하는 설치 프로젝트의 성격을 가지지만 오픈 소스 프로젝트를 진행하는 사례가 많음을 감안하여 소스 코드 패키징까지 지원합니다. Packaging C# Console 프로젝트를 선택하고 프로젝트 이름을 HelloWorld로 입력하여 프로젝트 생성을 시작하면 새 프로젝트에 대한 상세 설정 대화 상자가 나타납니다. 크게 세 가지 설정이 존재하는데 일단 Packaging 설정부터 살펴보기로 하겠습니다. Archive of Sources 프로젝트 소스 코드를 패키징하는 방법을 정의합니다. 오픈 소스 프로젝트를 많이 진행하는 경우를 감안한 MonoDevelop만의 독특한 설정입니다.
Archive of Binaries
Tarball: 관련 소스 코드와 바이너리를 타르볼 파일 (.tar 시리즈)로 압축합니다. Unix Integration Generate Launch Script: PE 기반 MSIL 혼용 이미지를 만들어내는 것이므로 사실 만들어진 exe 파일 자체는 여전히 mono 런타임에 의하여 별도로 취급되어야 합니다. 이 과정을 단순화하기 위하여 별도의 스크립트를 생성할 수 있는 데 이 작업을 자동화해줍니다. 프로젝트 이름과 다르게 스크립트 이름을 지정할 수 있습니다. Generate .desktop File: GNOME 노틸러스를 위한 단축 아이콘 파일 (Windows의 Explorer Shell과 대응됩니다. Windows의 단축 아이콘이 .lnk 확장자를 가지는 것과 같습니다.)을 생성하도록 할 수 있습니다. 이 설정 또한 스크립트를 만드는 것과 같은 용도이지만 GNOME 노틸러스를 위한 부가 설정이 포함됩니다. Translation Mono는 다른 Linux 기반 응용프로그램들과 같은 방법으로 지역화 기능을 제공합니다. Microsoft .NET Framework가 사용하는 지역화와는 다른 방법으로 프로그램과 함께 배포되는 PO (Portable Object) 파일을 i18n 라이브러리로 가져오는 방식입니다. 이 설정을 통하여 국적 및 국가 코드 별로 PO 파일을 미리 구성할 수 있습니다. ![]() 언어와 국적 코드를 설정하는 단계에서 User Defined Locale 설정을 이용하여 단순한 언어 번역을 위한 용도 외에도 사용자 정의 메시지 테이블을 구성할 수 있습니다. Gtk# Support 프로젝트에 Gtk# 설정을 추가하고 대상 라이브러리 버전을 지정할 수 있습니다. 별도로 Mono SVN Repository에서 소스 코드를 체크아웃하여 가져오거나 Night Build (개발자에 의하여 짧은 시간 내에 Commit되는 검증 수준이 약한 최신 빌드)를 설치한 경우가 아니면 버전이 보통 한 개입니다. 여러 설정이 있었지만 우리는 여기서 아무런 설정을 택하지 않고 프로젝트를 만들어보기로 합니다. 하나씩 하나씩 다음 Chapter에서 짚어보기로 합시다. 첫 프로젝트 프로그래밍 우리가 예상하던것과 다르지 않게 프로젝트가 만들어지고, 편집과 테스트가 가능한 환경이 생성되었습니다. 리눅스 환경이라고는 해도 C# 코드를 보니 별로 어색하지 않습니다. Visual Studio와 유사한 Keyboard Scheme를 제공하므로 Console.WriteLine 메서드에 출력할 메시지를 Hello World!로 바꾸고 F5키를 눌러보기로 하겠습니다. Visual Studio와는 조금 다르게 하단에 콘솔 프로그램의 실행 결과가 표시됩니다. GNOME의 경우 Windows에서처럼 기본 콘솔 인터프리터가 통일되지 않습니다. 하나도 설치되지 않았을 수도 있고, 수십 개가 설치된 상태일 수도 있습니다. 그래서 기본 설정은 이와 같이 표준 출력과 표준 오류 출력 스트림 (stdout, stderr)을 리디렉션하여 출력 결과를 보여주는 방식을 사용합니다. 프로젝트 디렉터리 구성 살펴보기 그러면 우리가 만든 프로젝트 파일들의 구성이 어떻게 되는지 한 번 살펴보기로 하겠습니다. 프로젝트 디렉터리는 위와 같이 사용자 프로파일 디렉터리 (이 가상 머신의 경우 /home/linux입니다.)에 솔루션 이름과 동일한 디렉터리가 생성된 것을 볼 수 있습니다. mds 파일이 보입니다. 이 파일은 Visual Studio의 sln 파일과 같은 것이고 같은 솔루션 파일로 여러 프로젝트에 대한 정보가 mds 파일에 서술됩니다. 그리고 하위 프로젝트 디렉터리들도 이 디렉터리 아래에 생성되겠지요. 우리가 만든 C# 프로젝트가 저장되어있는 디렉터리로 들어가보면 이번엔 mdp 파일이 보입니다. Visual Studio의 csproj, vbproj, vdproj와 대응되는 것이 mdp 파일입니다. AssemblyInfo.cs, Main.cs 파일도 보이는군요. bin 폴더에 우리가 컴파일한 바이너리가 들어있을 것입니다. Debug, Release 디렉터리가 마찬가지로 존재합니다. 디버그 모드와 릴리즈 모드를 프로젝트 작업 도중 IDE 도구 모음에서 설정할 수 있으며 여기에 맞추어 빌드하고 테스트할 수 있습니다. Debug 폴더의 경우 exe 파일 외에 exe.mdb 파일 혹은 dll.mdb 파일이 생성될 수 있습니다. 이것은 Microsoft .NET Framework 환경에서의 pdb 파일과 같은 용도로 디버거가 문맥을 탐색할 수 있도록 정보를 저장한 Program Database 파일이지만 Mono Debugger를 위하여 생성된 것입니다. 요약 오늘 단원에서는 MonoDevelop로 프로젝트를 만들었을 때의 실제 디렉터리 및 파일의 배치, 소스 코드의 특성, 새 프로젝트를 만들 때 사용할 수 있는 설정들에 대하여 간략히 알아보았습니다. 다음 단원에서는 패키징 개념에 대하여 살펴보기로 하겠습니다. |





댓글을 달아 주세요