때문에오픈소스 JSon 관련파서를찾기시작했고(자바용), 그래두역시구글님이만들어놓으신 Gson 이라는라이브러리를찾을 수 가있었다. Gson에서는다양한기능을제공하지만위에서정의한요구사항에부합하는기능을쓰는것은매우간단하다. 아래의코드를보자.
public class Person
{
String name = new String();
int age;
String currentjob = new String();
ArrayList jobs = new ArrayList();
boolean isExistGirlFriend = false;
}
먼저데이터를넣고뺴고할때담을그릇, 클래스를만들자. Person 이라는예제클래스를만들었다. 다음과같이간단하게사람의여러가지특징에대해서설명할수있는변수들을지정했다. 그리고나서 JSon 파일을읽어오는 readJsonFile() 함수와 Person 객체에서 JSon파일로데이터를넣을수있는 writeJsonFile을작성하였다.
public static void readJsonFile(String path) throws IOException
{
StringBuffer sb = readTextInFile(path);
System.out.println(sb.toString());
Gson gson = new Gson();
Person person = gson.fromJson(sb.toString(), Person.class);
}
public static StringBuffer readTextInFile(String file_path) throws IOException
{
int i;
StringBuffer full_text = new StringBuffer();
try
{
FileReader in = new FileReader(file_path);
while ((i = in.read()) != -1)
{
char ch = (char) i;
full_text.append(ch);
}
}
catch (IOException e)
{
e.printStackTrace();
}
return full_text;
}
public static void writeJsonFile(String path, Object obj) throws IOException
{
Gson gson = new Gson();
String json = gson.toJson(obj);
FileWriter writer = new FileWriter(path);
writer.write(json);
writer.close();
}
최근에 라이브러리를 만들면서 오픈 소스 형식으로 여러 사람들이 볼수 있는 위키 혹은 개발 문서 사이트를 만들고 싶었다. 처음에 택한 도구는 당연 트랙(Trac)이었다. 테터툴즈라는 좋아라하는 오픈소스 프로젝트의 도구로 쓰기도 했었고, 이전에 다녔던 회사에서도 사용했던 도구여서 괜찮을 거라 생각했다. 하지만 Trac의 단점은 위키부분만 분리해서 쓰기에는 조금 불편하다는 점이었다.(적어도 나에게는)
위키피디아
두번째로 생각한 것은 위키(WiKi) 사이트이다. 위키피디아의 시스템도 개발 문서 및 오픈 소스 프로젝트 형식으로 괜찮을 것이라는 생각이 들었다. mediawiki 나 여타의 다른 위키를 찾아 봤지만, 가장 큰 문제는 서버에 대한 임대 문제이다. 회사에 설치 하자니 좀 캥기는 것도 있고, 왠지 모르게 호스팅 업체를 이용하자니 비용적인 부분이 부담이 되었다.
그러던 중 찾은 것이 구글 사이트 도구이다.
일단 로그인을 하고 사이트 도구를 누르면 바로 이런 화면이 나온다.
DocumentExtractor는 이미 작업중인 개발관련 위키이다.
일단 만들기른 눌러보자. 만들기를 누르면 아래와 같이 템플릿과 이름을 작성할수 있는 부분이 나오게 된다. 먼저 템플릿 부터 보자. 템플릿은 가장 중요하다. 왜냐하면 기본적인 사이트의 레이아웃을 정하기 때문이다. 다양한 형식의 템플릿이 있지만, 우리의 목적인 위키이다. 검색 하다 "Wiki"
다양한 템플릿이 존재한다.
다양한 형식의 위키 사이트를 볼 수가 있다. 일단은 가장 복잡해 보이지 않는 "Simple Wiki"를 선택해보자. 해당 위키 템플릿을 선택하면 미리보기를 해 볼수도 있고, 또 바로 선택할 수도 있다. 일단 해당 템플릿을 선택하고 나면 이름을 정해야 한다.
여기서는 BaboWiki 라는 이름으로 만들어 보도록 하자. 일단 입력하면 소문자의 형태로 url 이 자동적으로 생성된다. 물론 본인이 원하는 다른 url을 지정 할 수 있다. 이 이름은 겹치면 안되기 때문에 잘 선택해서 쓰자. (원래는 TestWiki라고 지었으나 같은 이름이 있어서 거절되었다.)
이름을 입력하고 나서는 테마를 정할수도 있다. 그리고 기타 옵션이 있는데 해당 사이트에 대한 공개 및 보안 설정을 할 수가 있으며, 사이트에 대한 설명을 쓸 수도 있다. 그리고 성인 콘텐츠가 포함되어 있는지의 여부 역시 체크할수 있다.
짜잔!!
자, 이제 사이트를 만들었다.
다음 시간에는 본격적으로 메뉴사용법과 개발 문서를 담을 수 있는 위키를 만들어 보도록 하자.
단위 테스트를 어떻게 구성해야 할까에 대해서 생각해 볼 경우가 종종 있는것 같다. 실제로 필자가 속한 조직에서는 아직 단위테스트에 대한 필요성을 인지 하지 못한것인지는 모르겠지만, 아무튼 새롭게 프로토타입(Prototype)을 구성중인 자바 기반의 문서 이미지 추출 프로젝트에서는 클린코드와 단위테스트를 필수로 포함시키기로 하였다.
컴퓨터 프로그래밍에서유닛 테스트란 소스 코드의 특정 모듈이 의도된 대로 정확히 작동하는지 검증하는 절차다. 즉, 모든 함수와 메소드에 대한 테스트 케이스(Test case)를 작성하는 절차를 말한다. 이를 통해서 언제라도 코드 변경으로 인해 문제가 발생할 경우, 단시간 내에 이를 파악하고 바로 잡을 수 있도록 해준다. 이상적으로, 각 테스트 케이스는 서로 분리되어야 한다. 이를 위해 가짜 객체(Mock object)를 생성하는 것도 좋은 방법이다. 유닛 테스트는 (일반적인 테스트와 달리) 개발자(developer) 뿐만 아니라 보다 더 심도있는 테스트를 위해 테스터(tester)에 의해 수행되기도 한다.
단위 테스트에 대해서 막연히 알고 있었고, 필요하다고는 느끼고 있었지만 처음 도입해 보는 과정에서 여러가지 시행 착오가 있었다.
가장 큰 문제는 단위테스트 코드는 어디에 위치하는것 인가? 하는 부분이었다. main함수에서 해야하는 것인지(이 경우, 객체를 생성해야 하고 publc 함수 밖에 테스팅 할수 밖에 없다.) , 해당 클래스 내 함수가 있는 위치에서 따로 뽑아서 해야하는것인지(이럴경우, 객체를 만들 필요가 없고, private 함수까지 테스팅을 할 수 있다.) 고민되는 부분이 있었다.
또 다른 문제는 명명법에 대한 문제인다. 같은 함수내에 단위 테스트에 대한 코드를 둔다면, 실제 실행 함수와 어떻게 구별할 것인가? 함수 앞에 test_ 접두어를 붙일수도 있지만, 더 큰 문제는 나 외에 다른 동료들도 이러한 내용을 강제하고 지키도록 동의를 해야한다는 것이다. 그렇지 않는다면, 소스코드는 실제 사용하는 부분과 그리고 테스트 코드 부분을 구분 할 수 없게 되는 문제가 있다.
처음에는 main() 함수가 있는 클래스에 static 함수로 다른 클래스의 실제 사용하는 함수를 테스팅 하는 코드를 넣는 방식으로 하였다. 이 경우 문제점은 여러가지 함수를 테스팅하기 위해서는 main()에서 다양한 static 테스트 함수들을 써야 한다는 점이다. 그래서 좀 불편한것 같았다.
그러던 중 JUnit 에 대해서 알게되었다. 일단 써보면서 특징을 설명하겠다.
일단 JUnit을 사용하기 전에,
빌드 패스(Build Path)에서 추가 라이브러리(Add Library)를 눌러서 JUnit을 추가하도록 하자. 필자는 JUnit3를 설치하였다. 자 그러면, 이제 JUnit 을 본격적으로 사용해 보자.
빌드경로에서 라이브러리 추가
JUnit3 선택
이클립스의 File의 New를 눌러서 JUnit Test Case를 누르면 테스트 케이스를 만들수 있다. 아래의 화면과 같이 나오게 되면 일단 하단에서 New JUnit3 Test Case를 선택한다. 우리는 앞에서 JUnit3 라이브러리를 추가했기 때문에 이것을 선택해 주면된다. 그리고 나서 되도록 현재 만드는 단위 테스트가 속할 패키지를 지정해 준다. 되도록이면 지정하는 것이 좋다. 왜냐하면 JUnit Test Case 역시 하나의 클래스 파일이기 때문에 패키지로 구분해 주는 것이 가독성 측면에서 좋다.
JUnit Test Case 추가
패키지 지정을 했으면, 이름(name)을 지정해 주자. 일반적인 클래스 이름이라고 생각하시면 되는데 이때에 일반 클래스를 대상으로 테스트 하는 것이기 때문에 이름에서 어떤 클래스를 테스트 하고 있음을 나타내는 동시에, 이 클래스가 유닛테스트 클래스라는 것을 알려주자. 예를 들어, 내가 ExtractorImage 라는 클래스에 대한 유닛테스트를 만든다고 하면, UnitTest_ExtractorImage 이런식으로 UnitTest_ 라는 접두어를 통해서 해당 클래스가 유닛테스트 클래스 임을 알려주자.
마지막으로는 어떤 클래스에 대한 유닛테스트 인지를 설정하는 부분으로 제일 아래에 있는 class under test 에서 browse를 눌러서 테스트 하려는 클래스를 적어주면 알아서 찾아준다. 자, 입력을 다 했으면, 이제 Next를 누르면 앞에서 입력한 테스트 대상 클래스의 함수들이 나온다. 보시면 알겟지만, Public 함수에 대해서만 테스트를 할 수가 있다. 이 단계에서 테스트를 할 함수를 체크를 한다. 체크를 다했으면 기본적은 단위 테스트의 구성이 끝나고 해당 단위테스트 클래스가 만들어 지는것을 볼 수가 있다.
테스트 대상 클래스 지정
테스트 할 함수 지정
만들어진 단위 테스트 클래스의 내부를 보자.
JUnit 테스트 결과
우리가 앞에서 지정했던 함수에 대해서 미리 만들어져 있다. 그런데 형태를 보면, public void testExtractorManager() 이런 형태로 원래 함수 test라는 접두어를 붙여서 표시를 하였다. 일단 한번 테스팅을해 보자 테스팅을 하면 이전과 다르게 오른쪽에 JUnit 테스트에 대한 결과를 보여주는 창이 나온다. 보시면 Runs, Errors, Fails 가 있는데 처음 생성을 하면 아래처럼 나오기 때문에 현재는 Fails:4 라고 표시가 되어 있다.
public void testExtractText()
{
fail("Not yet implemented");
}
이제 구현을 해 보자. 일단 각각의 테스트 함수에다가 필요한 테스트 코드들을 넣어준다. 만약 테스트 함수 외의 함수를 넣고 싶다면, test 접두어를 붙이지 않은채 코드를 넣으면 된다. 예를 들어 testExtractImage() 함수에서 특정 디렉토리 경로를 가져오는 함수를 호출해야 한다면,
private String GetTestDocPath()
{
File dir = new File("");
return dir.getAbsolutePath() + File.separator + test_document;
}
public void testExtractImage()
{
String doc_dir_path = GetTestDocPath();
}
이런식으로 GetTestDocPath()를 써주면 된다. 대신에 이 부분은 test접두어로 시작되지 않기 때문에 JUnit test를 실행 시킬때 결과에 표시되지 않는다. 그렇다면, 검사 대상 클래스의 내용이 추가 된다면 어떻게 해야 할까? 다시 앞의 단계를 반복해야할까?
수동으로 JUnit 테스트의 결과에 포함되는 테스트 함수를 추가 시키는 방법은 간단하다. 어떤 테스트 하려는 함수를 만들고 그 함수이름의 제일 앞에 test 접두어를 붙여주면 된다. 예를들어, testExtractImage1() 을 만들어 주고 테스트를 돌려보면 아래와 같은 결과를 얻을 수가 있다.
수동 테스트 함수 추가 결과
추가된 함수가 결과에 포함된 것을 볼수가 있다. 이렇게 하면 대상 클래스의 내용이 변하더라도 능동적으로 테스트 코드에 추가할 수가 있다.
자, 이제 다 구현을 하고 JUnit을 돌려보자. 필자가 생각하는 JUnit의 가장 큰 장점은 해당 함수에 대한 실행 시간을 표시해 줄수 있다는 것이다. 사실 프로그래머는 성능 측정에 대해서 자유로울 수가 없는데 JUnit 에서는 이런 부분을 쉽게 해결해 준다. 하나의 함수에 대한 실행 시간을 알려줌으로써, 어떤 부분이 병목을 가지고 있는지 테스트 케이스를 구성함으로써, 쉽게 알수가 있다. C 언어에서 처럼 시간을 측정하는 부분을 넣을 필요가 없다.(사실 이 부분은 소스코드를 더럽히기도 하고, 다른 여러가지 툴이 있을것이라 생각된다.)
매일매일 개발하는 소스코드를 올리고 있습니다. 원래는 프로젝트가 끝나고 올리려고 했으나, 그러다 보니 까먹는 경우가 있어서 이렇게 매일매일 올리는 소스코드 입니다. 제가 쓴 소스코드의 문제 혹은 개선점이 있으면 언제든지 댓글 달아 주세요
자바 프로그래머도 아니면서 자꾸 자바 소스코드를 내놓게 되는데 사실 환경만 구축되어 있으면, 이것만큼 쉽게 프로토타입핑을 쉽게 해 볼수 있는 언어도 드문것 같다. 오늘 소개한 코드는 일명 로컬캐쉬(LocalCache) 라는 것이다. 만든 취지는 원래 회사에서 캐쉬관련 모듈이 있는데, 캐쉬라는것은 한 마디로 미리 저장해 놓는 개념이라고 볼수 있다. 그래서 자바의 HastTable 을 이용해서 로컬캐쉬 즉, 프로그램 내에서 싱글턴의 형태로 존재하면서 무엇인가를 저장하고 있다가 요청하게 되면 반환해 주는 것이다. 여러가지로 활용될수 있는 여지가 있다고 생각되어 진다. 특정 API 관련된 결과를 다시 호출할 필요없이 일정기간 내에서는 로컬 캐쉬내에서 찾아서 보여준다면 빠르지 않을까?
import java.util.*;
public class LocalCache
{
private Hashtable local_hashtable = new Hashtable();
private static LocalCache shared_object = new LocalCache();
private int limit_chche_entry = 10000;
public Timer timer = new Timer();
boolean isMutexOpen = true;
private LocalCache()
{
// Do not use Constructor() because of Singleton instance
}
public static LocalCache getInstance()
{
return shared_object;
}
public void startTimer(int limit_mili_sec)
{
timer.schedule(new CleanCacheTimerTask(), limit_mili_sec);
}
public void stopTimer()
{
timer.cancel();
}
public void setLimitEntry(int limit_number)
{
limit_chche_entry = limit_number;
}
public int getLimitEntry()
{
return this.limit_chche_entry;
}
public boolean setCache(String key, Object value)
{
boolean is_added = false;
for (;;)
{
if (isMutexOpen == false)
{
if (isExistKey(key) == true)
{
is_added = false;
break;
}
}
else
{
isMutexOpen = false;
if (this.countCache() >= limit_chche_entry)
{
System.out.println("limit number : "
+ this.limit_chche_entry);
is_added = false;
}
else
{
this.local_hashtable.put(key, value);
is_added = true;
}
isMutexOpen = true;
break;
}
}
return is_added;
}
public Object getCache(String key)
{
return this.local_hashtable.get(key);
}
public boolean isExistKey(String key)
{
return this.local_hashtable.containsKey(key);
}
public void clearCache()
{
this.local_hashtable.clear();
}
public void delKeyInCache(String key)
{
if (this.local_hashtable.containsKey(key) == true)
{
this.local_hashtable.remove(key);
}
}
public int countCache()
{
return this.local_hashtable.size();
}
}
class CleanCacheTimerTask extends TimerTask
{
public void run()
{
LocalCache cache = LocalCache.getInstance();
cache.clearCache();
cache.timer.cancel();
}
}
위의 소스코드를 보시면 알겠지만, 단순하게 HashTable을 이용하고 있다. 그리고 몇가지 장치를 두었는데 :
1. 용량제한 장치
- 사실 엄청 많은 데이터를 넣을수는 있지만, 메모리 부족 문제가 생길수 있다. 그리고 해쉬테이블 상 Java 의 Object 형을 인자로 받기 때문에 모든 형들이 다 들어갈수 있다. 원래 취지는 데이터 사이즈를 고려해서 특정 데이터 사이즈 이상은 넣지 않는 것으로 할려 했으나, C에서 처럼 sizeof() 함수가 없기 때문에 건수 자체(limit_chche_entry )를 지정하는 방식으로 되어 있다.
2. 타이머 기능
- 타이머 기능은 당연히, 특정 시간 동안에만 캐쉬를 유지하는 기능이다. 너무 오랫동안 유지할 경우, 메모리 부족의 문제도 있기 때문에 시간을 둔것이다. 사용자가 설정한 시간이 지나면 저절로 해쉬테이블 내의 모든 데이터를 초기화 시킨다.
3. 멀티스레드 기능
- 당연히 멀티스레드를 생각했었고, 처음부터 멀티유저 동시 접속시의 성능향상을 위해서 만든 것이다. 때문에 멀티스레드를 위한 다양한 장치를 했는데 우선 본 클래스의 인스턴스 생성은 아예 한번만 이루어지고, 해당 인스턴스를 빌려서 쓰게 만드는 방식을 사용했다. 사실은 getInstance() 함수에서 Null 인지를 체크하고 널인 경우에만 인스턴스를 생성해서 반환하는 방식을 사용했는데 멀티쓰레드의 보장 문제가 테스트 할때 발견이 되어서 급히 수정했다. 그리고 또 한가지는 바로 setCache() 함수시의 내부에서 선 진입스레드가 수행하고 후 진입 스레드는 계속 대기(waiting)하는 것이 아니라, 대기하면서 계속 해쉬테이블 안에 키가 있는지 확인하는 과정을 거치게 만들었다. 선진입한 스레드와 후 진입한 스레드가 동일한 데이터를 쓰려고 하면 문제가 발생하는것은 아니지만, 후 진입한 스레드의 입장에서는 시간낭비이기 떄문이다.
사실 이 로컬캐쉬 코드와 쌍으로 이루어서 쓸수 있는것은 전에 포스팅에서 했던 MD5를 만드는 자바코드이다. 사실 해쉬테이블은 키-값, 키벨류 방식의 사전(Dictionary) 형태이기 때문에 하나의 데이터를 넣기 위해서는 고유한 키가 필요하다. 때문에 MD5 코드를 통해서 특정한 string 값을 입력하면 MD5의 형태로 문자열을 출력하고, 그 문자열을 해쉬테이블의 키 값으로 사용하는 것이다. MD5가 완벽하게 확실한 키 값을 추출해 준다고 말할수는 없겠지만, 테스트 결과 같은 문자열을 넣으면, 같은 MD5 키를 추출해 주었다.
최근에 해쉬테이블의 키 값을 만들기 위해서 입력 문자열에 대한 MD5를 추출하는 자바 소스를 인터넷에서 구했는데, 입력 문자열에 따라서 MD5 로 추출되는 자릿수가 달라서 해쉬테이블의 키 값으로 쓰기에는 조금 불편한 점이 있어서 자릿수 맞추는 부분을 추가하였다. 코드는 간단하다. 해당 클래스 객체를 만들고 자릿수를 지정해 주면 알아서 채우거나 빼준다. 빼는 것은 뒷 자리부터 빼고, 채우는 것은 0~9 까지의 랜덤함수를 통해서 채우도록 하였다. 키 값이 100% 겹치지 않는다고 단언 할 수는 없지만, 테스트 단계에서는 쓸만할듯.
import java.security.*;
import java.util.*;
public class MD5
{
int limit_position_number;
public MD5()
{
limit_position_number = 30;
}
public int getLimitPositionNumber()
{
return this.limit_position_number;
}
public void setLimitPositionNumber(int limit_postion_num)
{
this.limit_position_number = limit_postion_num;
}
public String encryptData(String str)
{
StringBuilder sb = new StringBuilder();
try
{
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(str.getBytes());
byte[] md5encrypt = md5.digest();
for (int i = 0; i < md5encrypt.length; i++)
{
sb.append(Integer.toHexString((int) md5encrypt[i] & 0xFF));
}
}
catch (Exception e)
{
e.printStackTrace();
}
return assemlyMD5(sb);
}
public String assemlyMD5(StringBuilder ori_md5)
{
for (;;)
{
int current_md5_len = ori_md5.toString().length();
if (current_md5_len > this.limit_position_number)
{
int last_index = current_md5_len - 1;
ori_md5.deleteCharAt(last_index);
}
else if (current_md5_len < this.limit_position_number)
{
Random random = new Random();
int gen_rand_vaule = random.nextInt(10);
ori_md5.append(gen_rand_vaule);
}
else
{
break;
}
}
return ori_md5.toString();
}
}
svn: Commit failed (details follow): svn: Could not use external editor to fetch log message; consider setting
the $SVN_EDITOR environment variable or using the --message (-m) or --file (-F) options svn: None of the environment variables SVN_EDITOR, VISUAL or EDITOR is set, and
no 'editor-cmd' run-time configuration option was found
이번 프로젝트가 언어 및 문자에 관련된 것이라서 조합자 중에서 특히 우리나라 말의 경우 초성 중성 종성이 있는데 그것들이 실제로 프로그래밍의 어떤 컨트롤 상에서 제공이 제대로 되는 경우가 없습니다. 사용자의 키보드 입력은 제대로 처리 하지만, 마우스로 버튼을 눌러서 한글의 제대로된 입력을 하려고 한다면 초성 중성 종성 조합 알고리즘을 만들어야 하는데. 그와 관련된 자료 및 소스 코드 입니다.
MixString 라이브러리로 처음에 PC 버전을 개발 했는데, 약간 수정해서 개발을 했습니다. 수정은 동료가. 제가 저 라이브러리를 찾았거든요. 그리고 C# 기반의 모바일 프로그램에 해당 라이브러리를 쓰려고 하니까 데스크톱 프로젝트라서 만들 수 없다고 하더군요. 도저히 개발할 시간은 없구.. 해서 찾은게 두번째 라이브러리 입니다. 두번째 라이브러리에서는 특이하게 자판에 기초하고 있어서, 영문자를 Input() 함수에 넣어야 한글이 나오는 방식이더라구요. 예를 들면 "a" 를 입력하면 "ㅁ" 이 나오는 방식입니다. 만약 r 대신에 R을 누르면 "ㄲ" 이 나오는 방식입니다.
점점 느끼는 것이지만, 역시 개발자들이 여러가지 결과물들을 블로그에 올리는 것이 좋다는 생각이 든다. 저작권 같은 개념도 중요하긴 하지만, 개발자는 자신이 개발해 봐야 안다. 그런데 모든것을 다 해볼 수는 없고, 책에서는 이론만 알려주기 떄문에 오픈소스, 커뮤니티, 블로그 등을 통해서 자신이 올린 소스를 보여주고 공유하는게 맞는것 같다.
제가 올린 것 역시, 제가 만든건 아니지만 그래두 반드시 필요한 사람이 있을거라 믿기에.^^
으로 이루어져있다고 합니다. 그렇기 때문에 실질적으로 개발자가 위치정보중에 GPS 만 혹은 WPS 만 사용하겠다고 설정할 수 없게 되어 있다고 합니다. 참, 어이가 없죠. 애플은 참 비밀이 많은 기업이긴 합니다만, 개발하는 입장에서 가져다 쓰기만 하면 편하긴 합니다만. 아무튼, 그래서 위치정보를 끄지 않는 이상 외국에서 데이터 로밍 차단만 하면 사진을 찍을때, 당연히 데이터가 소비되는 것입니다.
문제는 데이터 로밍을 켜고 쓰는 데이터 양은 문자 메시지로 KT에서 알려주는데요, 이렇게 사용된 데이터양은 절대 알수가 없습니다. 때문에 조심해야 합니다. 해외 나가실때에는 아쉽지만, 위치정보는 꺼 두심이 좋을것 같습니다.^^
PS) 다 아는 내용일지 몰라도, 오히려 IT 분야에 일하시는 분들이 위치정보 = GPS 라고 생각하시는 분이 많더라구요.
트위터에 아는 형님이 추천한 동영상인데, 이민석이라는 분, 찾아보니 한성대 컴퓨터 공학과 교수님인데 굉장히 말을 잘하시는것 같다. 약간의 쇼맨십도 있는것 같고 재밌게 잘 설명해 주시는 동영상인듯. 정말 동감하는 부분은 '신념에 기반한 개발' 약간은 고개숙이게 만드는 동영상인듯. 소스 코드를 읽어야 한다는 것은 한번 해 봐야 겠다는 생각이 든다. 여러가지 내 코드에 대한 분석 툴을 사용해 봐야 겠다는 생각이 든다.
이번 시간에는 폰트를 어떻게 개발에 이용할 것인가에 대해서 좀더 프로그래밍적인 부분에 대해서 이야기 해보고자
합니다. 일단, 폰트를 이용하는 부분은 다양하겠지만 레이블, 텍스트박스, 콤보 박스, 리스트박스
등의 텍스트 데이터가 쓰여지는 컨트롤에서 광범위하게 적용 할 수 있다고 봅니다.
그렇다면, 어떻게 그러한 컨트롤들에 폰트를 적용시킬 수 있을까요?
먼저 폰트를 윈도우 시스템에 등록을 해야 합니다. 물론 시스템에 등록하지
않고 사용할 수 있는 방법도 있는데, 그 방법에 대해서는 차후에 설명하도록 하겠습니다. 시스템에 등록한다는 것은 어렵게 설명한 것이지만, 쉽게 설명하면
만들어진 폰트를Windows/Fonts 폴더에 추가하면 되는 것입니다.
이렇게 Windows 시스템에 폰트를 추가하면, 자연스럽게 MS 워드 혹은 한글(hwp)의
폰트 창에서 확인 할 수가 있습니다. 왜냐하면 해당 오피스 프로그램들의 폰트는 윈도우 시스템에 추가된
폰트들을 기반으로 하고 있기 때문입니다.
개발자가 개발하는 프로그램의 컨트롤에 폰트를 적용하기 위해서는 다음의 코드를 사용해야 합니다.
float FontSize = 24.0f;
string
FontName = "AppleGothic";
publicvoid ApplyFont()
{
Font Apple_Font = newFont(FontName, FontSize);
txt_display.Font = UIC_Font;
}
위의 코드를 보시면, Font 클래스의 객체를 만들어서 “AppleGothic” 폰트를 24pt 의 크기로 하여 텍스트 박스에
해당 폰트정보를 적용하는 것을 나타내고 있습니다. 이 과정에서의 전제 조건은 윈도우 시스템에 등록되어
있어야 한다는 것이겠지요.
그렇다면, 앞에서 언급한대로 윈도우 시스템에 등록하지 않으면 쓸 수
없을까요?
닷넷에서는 Font 외에도 FontCollection
이라는 추상 클래스(abstract class)를 가지고 있는데, msdn을 살펴보면, 설치된 글꼴 컬렉션과 전용 글꼴 컬렉션의 기본
클래스를 제공한다고 나와 있습니다. 좀 말이 어렵지요. 간단히
설명하자면,
FontCollection (Abstract Class)
- InstalledFontCollection
-
PrivateFontCollection
InstalledFontCollection 은 시스템에 설치된 폰트를
나타내는 클래스입니다. 실질적으로 Font 클래스와 별반
다를 게 없다고 보시면 될 것 같습니다. Families 속성을 통해서 시스템에 있는 폰트를 이름기반으로
검색할 수가 있습니다.
PrivateFontCollection 은 한마디로 시스템에 설치하지
않고 응용프로그램에서 쓸 경우, 폰트를 적용할 때 사용할 수 있는 클래스 입니다. MSDN에 보면 시스템에 있는 Arial 글꼴을 전용버전으로 만들
수 있다고 하는데, 그 부분에 대해서는 좀더 MSDN을 참고하시고, 설치 안 해도 되는 폰트를 적용하는 법에 대해서 설명하도록 하겠습니다.
Font App_Font = newFont(PF.Families[0], FontSize, FontStyle.Regular, GraphicsUnit.Point);
txt_display.Font = App_Font;
기존의 Font 클래스를 사용하는 것은 마찬가지이나, Font 클래스의 객체를 만들 때에는 Family 이름을 넣어야
하기 때문에 PrivateFontCollection 객체를 이용해서 그 작업을 해주는 것입니다. 객체를 일단 만드신 후에 AddFontFile() 함수를 통해 해당
폰트파일의 주소(File Path)를 넣어주시고Font 객체에
생성자에 해당 Family 이름을 전달해 주시면 됩니다.
참고로, 이러한 개발에 적용 가능한 폰트의 형태는 트루타입의 폰트만
가능합니다.
최근 타이포그라피에 관심이 가는 필자.
이번 포스팅에서는, 시스템에 폰트를 등록하는 방법과 등록된 폰트를
개발 프로그램에 적용하는 방법 그리고 등록되지 않은 폰트를 개발 프로그램에 적용하는 방법에 대해서 알아보았습니다.
사실 저 역시 이번 과제를 통해서 폰트와 유니코드와의 관계 그리고 실제 폰트를 개발한다는 것에 대해서 또한 개발된 폰트를 프로그램에
적용하는 과정에 대해서 공부하게 되었던 계기였습니다.
폰트에 대해서는 타이포그래피(Typography) 하시는 분들이 더
잘 알거라 생각됩니다. 아랍어 같은 경우에는 가운데 혹은 마지막에 글자가 위치하면 변형되기도 하고 글자끼리
필기체처럼 이어지는 부분이 있어서, 폰트를 만들기 어렵더군요. 새삼
폰트디자이너 분들의 수고를 알게 되었습니다.