제가 아는 인터넷 앨범 프로그램에 가장 많이 쓰이는 것은 2가지 있습니다.

1) Gallery (http://gallery.menalto.com)
Gallery - YOUR PHOTOS ON YOUR WEBSITE
2) album.pl (http://perl.bobbitt.ca/album)



입니다. 두개 중에 우열을 가린다면 Gallery 가 압승입니다. album.pl 은 perl 로 만들어져 있는데 아무래도 php 로 만들어진 gallery 보다 느립니다. 서버에 부하도 많이 줍니다.

제가 사용하는 gallery 는 ubuntu linux 에서 자동 업데이트 되는 버젼으로 사용합니다. 아무래도 apt-get(우분투 프로그램 업데이터) 으로 업데이트 하는 것이 편하니까요.

제 홈페이지(http://whria.net) 와 보시면 photography 부분이 있습니다. 이것은 gallery 로 만들어져 있습니다. 매우 짜임새있고 좋습니다. 단점이라면 댓글 다는 부분이 조금 부족합니다.
,


유니 코드 (UTF-8 or UTF-16LE) 를 local (아스키 또는 각자의 codepage) 로 변환시키는 문제는 유니코드 지원 프로그래밍을 위해서는 매우 복잡한 문제이다.

먼저 보통 말하는 local codepage 랑 아스키랑 같은 것이라는 것을 알자. 나는 처음에 이게 차이가 있을 까봐 정말 머리가 아팠다.

결국...

UTF8 <-> UTF16LE
UTF16LE <-> UTF8
UTF8 <-> local
UTF16LE <-> local

이렇게 4 가지 조합만 바꿀 수 있으면 unicode 를 완벽하게 지원하는 프로그램을 짤 수 있다.

c++ 에서 사용할 수 있는 locale 을 바꿔 주는 library 가 몇가지 있는데...

1) iconv
2) boost
3) qt 의 qstring
4) 그리고 피부미인의 codechanger ㅋㅋㅋ

1 번은 가장 많이 쓰는데 static 으로 compile 할라면 머리아프다. 나는 DLL 을 정말 싫어한다.
2 번은 사람들이 잘 모르는데 boost 안에 일부 function 이 unicode 프로그래밍의 중요한 clue 를 제공한다. 여기서 개발된 것이 4 번의 피부미인의 codechanger 이다.
3 번 qt 는 일단 install 할라면 너무 머리아프다. build 하다가 다른 library 랑 부딛치면 돌아버린다.

4 번 피부미인의 codechanger 는 내가 medicalphoto 라는 프로젝트를 하면서 정말정말 어렵게 만든겁니다.  여기에만 특별히 공개하겠습니다. ^^g 이걸 쓰려면 boost library 를 설치해야합니다. 아니면 utf8-codecvt_facet.hpp 에 있는 boost/config.hpp 나 boost/detail/workaround.hpp 등만 copy 해서 사용해도 됩니다.


사용법은 아래와 같다.

MCodeChanger::_CCL("unicode letters") = "local code letters"
MCodeChanger::_CCU("local code letters") = "unicode letters"



1. codechanger.h

////////////////////////////////////////////////////////////////////////////////
// Copyright : Han Seung Seog
// It was so damn hard to make this library
// http://prettygom.com
// http://sshan.net
// 2008. 8. 1
////////////////////////////////////////////////////////////////////////////////

#pragma once

#include "../boost.h"
#include <string>
#include <boost/format.hpp>
#include "tchar.h"
#include "utf8_codecvt_facet.hpp"
#include "unicode.h"

#ifdef _UNICODE
    #define _CCL U_W
    #define _CCU W_U
    #define _CCW mbs_to_wcs
    #define _CCN wcs_to_mbs
#else
    #define _CCL U_L
    #define _CCU L_U
    #define _CCW LocaltoLocal
    #define _CCN LocaltoLocal
#endif // _UNICODE

class MCodeChanger
{
public:
    static tstring LocaltoLocal(const tstring& str)
    {
        return str;
    }

    static std::string L_U(const std::string& str)
    {
        std::locale local(std::locale(""),new utf8_codecvt_facet);
        return wcs_to_mbs(mbs_to_wcs(str),local);
    }
    static std::string U_L(const std::string& str)
    {
        std::locale local(std::locale(""),new utf8_codecvt_facet);
        return wcs_to_mbs(mbs_to_wcs(str,local));
    }
    static std::string W_U(const std::wstring& str)
    {
        std::locale local(std::locale(""),new utf8_codecvt_facet);
        return wcs_to_mbs(str,local);
    }
    static std::wstring U_W(const std::string& str)
    {
        std::locale local(std::locale(""),new utf8_codecvt_facet);
        return mbs_to_wcs(str,local);
    }

static std::wstring
mbs_to_wcs(std::string const& str, std::locale const& loc = std::locale(""))
{
    typedef std::codecvt<wchar_t, char, std::mbstate_t> codecvt_t;
    codecvt_t const& codecvt = std::use_facet<codecvt_t>(loc);
    std::mbstate_t state = 0;
    std::vector<wchar_t> buf(str.size() + 1);
    char const* in_next = str.c_str();
    wchar_t* out_next = &buf[0];
    codecvt_t::result r = codecvt.in(state,
        str.c_str(), str.c_str() + str.size(), in_next,
        &buf[0], &buf[0] + buf.size(), out_next);
    return std::wstring(&buf[0]);
}
 
static std::string
wcs_to_mbs(std::wstring const& str, std::locale const& loc = std::locale(""))
{
    typedef std::codecvt<wchar_t, char, std::mbstate_t> codecvt_t;
    codecvt_t const& codecvt = std::use_facet<codecvt_t>(loc);
    std::mbstate_t state = 0;
    std::vector<char> buf((str.size() + 1) * codecvt.max_length());
    wchar_t const* in_next = str.c_str();
    char* out_next = &buf[0];
    codecvt_t::result r = codecvt.out(state,
        str.c_str(), str.c_str() + str.size(), in_next,
        &buf[0], &buf[0] + buf.size(), out_next);
    return std::string(&buf[0]);
}
};

2. [ utf8_codesvt_facet.hpp ]

// Copyright ?2001 Ronald Garcia, Indiana University (garcia@osl.iu.edu)
// Andrew Lumsdaine, Indiana University (lums@osl.iu.edu).
// Distributed under the Boost Software License, Version 1.0. (See accompany-
// ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#pragma once

// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// utf8_codecvt_facet.hpp

// This header defines class utf8_codecvt_facet, derived fro
// std::codecvt<wchar_t, char>, which can be used to convert utf8 data in
// files into wchar_t strings in the application.
//
// The header is NOT STANDALONE, and is not to be included by the USER.
// There are at least two libraries which want to use this functionality, and
// we want to avoid code duplication. It would be possible to create utf8
// library, but:
// - this requires review process first
// - in the case, when linking the a library which uses utf8
//   (say 'program_options'), user should also link to the utf8 library.
//   This seems inconvenient, and asking a user to link to an unrevieved
//   library is strange.
// Until the above points are fixed, a library which wants to use utf8 must:
// - include this header from one of it's headers or sources
// - include the corresponding .cpp file from one of the sources
// - before including either file, the library must define
//   - BOOST_UTF8_BEGIN_NAMESPACE to the namespace declaration that must be used
//   - BOOST_UTF8_END_NAMESPACE to the code to close the previous namespace
//   - declaration.
//   -  -- to the code which must be used for all 'exportable'
//     symbols.
//
// For example, program_options library might contain:
//    #define BOOST_UTF8_BEGIN_NAMESPACE <backslash character>
//             namespace boost { namespace program_options {
//    #define BOOST_UTF8_END_NAMESPACE }}
//    #define  BOOST_PROGRAM_OPTIONS_DECL
//    #include "../../detail/utf8/utf8_codecvt.cpp"
//
// Essentially, each library will have its own copy of utf8 code, in
// different namespaces.

// Note:(Robert Ramey).  I have made the following alterations in the original
// code.
// a) Rendered utf8_codecvt<wchar_t, char>  with using templates
// b) Move longer functions outside class definition to prevent inlining
// and make code smaller
// c) added on a derived class to permit translation to/from current
// locale to utf8

//  See http://www.boost.org for updates, documentation, and revision history.

// archives stored as text - note these ar templated on the basic
// stream templates to accommodate wide (and other?) kind of characters
//
// note the fact that on libraries without wide characters, ostream is
// is not a specialization of basic_ostream which in fact is not defined
// in such cases.   So we can't use basic_ostream<OStream::char_type> but rather
// use two template parameters
//
// utf8_codecvt_facet
//   This is an implementation of a std::codecvt facet for translating
//   from UTF-8 externally to UCS-4.  Note that this is not tied to
//   any specific types in order to allow customization on platforms
//   where wchar_t is not big enough.
//
// NOTES:  The current implementation jumps through some unpleasant hoops in
// order to deal with signed character types.  As a std::codecvt_base::result,
// it is necessary  for the ExternType to be convertible to unsigned  char.
// I chose not to tie the extern_type explicitly to char. But if any combination
// of types other than <wchar_t,char_t> is used, then std::codecvt must be
// specialized on those types for this to work.

#include <locale>
// for mbstate_t
#include <wchar.h>
// for std::size_t
#include <cstddef>

#include <boost/config.hpp>
#include <boost/detail/workaround.hpp>

namespace std {
    #if defined(__LIBCOMO__)
        using ::mbstate_t;
    #elif defined(BOOST_DINKUMWARE_STDLIB) && !defined(__BORLANDC__)
        using ::mbstate_t;
    #elif defined(__SGI_STL_PORT)
    #elif defined(BOOST_NO_STDC_NAMESPACE)
        using ::mbstate_t;
        using ::codecvt;
    #endif
} // namespace std

#if !defined(__MSL_CPP__) && !defined(__LIBCOMO__)
    #define BOOST_CODECVT_DO_LENGTH_CONST const
#else
    #define BOOST_CODECVT_DO_LENGTH_CONST
#endif

// maximum lenght of a multibyte string
#define MB_LENGTH_MAX 8

struct  utf8_codecvt_facet :
    public std::codecvt<wchar_t, char, std::mbstate_t> 
{
public:
    explicit utf8_codecvt_facet(std::size_t no_locale_manage=0)
        : std::codecvt<wchar_t, char, std::mbstate_t>(no_locale_manage)
    {}
protected:
    virtual std::codecvt_base::result do_in(
        std::mbstate_t& state,
        const char * from,
        const char * from_end,
        const char * & from_next,
        wchar_t * to,
        wchar_t * to_end,
        wchar_t*& to_next
    ) const;

    virtual std::codecvt_base::result do_out(
        std::mbstate_t & state, const wchar_t * from,
        const wchar_t * from_end, const wchar_t*  & from_next,
        char * to, char * to_end, char * & to_next
    ) const;

    bool invalid_continuing_octet(unsigned char octet_1) const {
        return (octet_1 < 0x80|| 0xbf< octet_1);
    }

    bool invalid_leading_octet(unsigned char octet_1)   const {
        return (0x7f < octet_1 && octet_1 < 0xc0) ||
            (octet_1 > 0xfd);
    }

    // continuing octets = octets except for the leading octet
    static unsigned int get_cont_octet_count(unsigned   char lead_octet) {
        return get_octet_count(lead_octet) - 1;
    }

    static unsigned int get_octet_count(unsigned char   lead_octet);

    // How many "continuing octets" will be needed for this word
    // ==   total octets - 1.
    int get_cont_octet_out_count(wchar_t word) const ;

    virtual bool do_always_noconv() const throw() { return false; }

    // UTF-8 isn't really stateful since we rewind on partial conversions
    virtual std::codecvt_base::result do_unshift(
        std::mbstate_t&,
        char * from,
        char * /*to*/,
        char * & next
    ) const
    {
        next = from;
        return ok;
    }

    virtual int do_encoding() const throw() {
        const int variable_byte_external_encoding=0;
        return variable_byte_external_encoding;
    }

    // How many char objects can I process to get <= max_limit
    // wchar_t objects?
    virtual int do_length(
        BOOST_CODECVT_DO_LENGTH_CONST std::mbstate_t &,
        const char * from,
        const char * from_end,
        std::size_t max_limit
#if BOOST_WORKAROUND(__IBMCPP__, BOOST_TESTED_AT(600))
        ) const throw();
#else
        ) const;
#endif

    // Largest possible value do_length(state,from,from_end,1) could return.
    virtual int do_max_length() const throw () {
        return 6; // largest UTF-8 encoding of a UCS-4 character
    }
};


3. [utf8_codecvt_facet.cpp]

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// utf8_codecvt_facet.cpp

// Copyright ?2001 Ronald Garcia, Indiana University (garcia@osl.iu.edu)
// Andrew Lumsdaine, Indiana University (lums@osl.iu.edu).
// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

// Please see the comments in <boost/detail/utf8_codecvt_facet.hpp> to
// learn how this file should be used.
#include "stdafx.h"
#include "utf8_codecvt_facet.hpp"

#include <cstdlib> // for multi-byte converson routines
#include <cassert>

#include <boost/limits.hpp>
#include <boost/config.hpp>

// If we don't have wstring, then Unicode support
// is not available anyway, so we don't need to even
// compiler this file. This also fixes the problem
// with mingw, which can compile this file, but will
// generate link error when building DLL.
#ifndef BOOST_NO_STD_WSTRING

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// implementation for wchar_t

// Translate incoming UTF-8 into UCS-4
std::codecvt_base::result utf8_codecvt_facet::do_in(
    std::mbstate_t& /*state*/,
    const char * from,
    const char * from_end,
    const char * & from_next,
    wchar_t * to,
    wchar_t * to_end,
    wchar_t * & to_next
) const {
    // Basic algorithm:  The first octet determines how many
    // octets total make up the UCS-4 character.  The remaining
    // "continuing octets" all begin with "10". To convert, subtract
    // the amount that specifies the number of octets from the first
    // octet.  Subtract 0x80 (1000 0000) from each continuing octet,
    // then mash the whole lot together.  Note that each continuing
    // octet only uses 6 bits as unique values, so only shift by
    // multiples of 6 to combine.
    while (from != from_end && to != to_end) {

        // Error checking   on the first octet
        if (invalid_leading_octet(*from)){
            from_next = from;
            to_next = to;
            return std::codecvt_base::error;
        }

        // The first octet is   adjusted by a value dependent upon
        // the number   of "continuing octets" encoding the character
        const   int cont_octet_count = get_cont_octet_count(*from);
        const   wchar_t octet1_modifier_table[] =   {
            0x00, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc
        };

        // The unsigned char conversion is necessary in case char is
        // signed   (I learned this the hard way)
        wchar_t ucs_result =
            (unsigned char)(*from++) - octet1_modifier_table[cont_octet_count];

        // Invariants   :
        //   1) At the start of the loop,   'i' continuing characters have been
        //    processed
        //   2) *from   points to the next continuing character to be processed.
        int i   = 0;
        while(i != cont_octet_count && from != from_end) {

            // Error checking on continuing characters
            if (invalid_continuing_octet(*from)) {
                from_next   = from;
                to_next =   to;
                return std::codecvt_base::error;
            }

            ucs_result *= (1 << 6);

            // each continuing character has an extra (10xxxxxx)b attached to
            // it that must be removed.
            ucs_result += (unsigned char)(*from++) - 0x80;
            ++i;
        }

        // If   the buffer ends with an incomplete unicode character...
        if (from == from_end && i   != cont_octet_count) {
            // rewind "from" to before the current character translation
            from_next = from - (i+1);
            to_next = to;
            return std::codecvt_base::partial;
        }
        *to++   = ucs_result;
    }
    from_next = from;
    to_next = to;

    // Were we done converting or did we run out of destination space?
    if(from == from_end) return std::codecvt_base::ok;
    else return std::codecvt_base::partial;
}

std::codecvt_base::result utf8_codecvt_facet::do_out(
    std::mbstate_t& /*state*/,
    const wchar_t *   from,
    const wchar_t * from_end,
    const wchar_t * & from_next,
    char * to,
    char * to_end,
    char * & to_next
) const
{
    // RG - consider merging this table with the other one
    const wchar_t octet1_modifier_table[] = {
        0x00, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc
    };

    wchar_t max_wchar = (std::numeric_limits<wchar_t>::max)();
    while (from != from_end && to != to_end) {

        // Check for invalid UCS-4 character
        if (*from  > max_wchar) {
            from_next = from;
            to_next = to;
            return std::codecvt_base::error;
        }

        int cont_octet_count = get_cont_octet_out_count(*from);

        // RG  - comment this formula better
        int shift_exponent = (cont_octet_count) *   6;

        // Process the first character
        *to++ = static_cast<char>(octet1_modifier_table[cont_octet_count] +
            (unsigned char)(*from / (1 << shift_exponent)));

        // Process the continuation characters
        // Invariants: At   the start of the loop:
        //   1) 'i' continuing octets   have been generated
        //   2) '*to'   points to the next location to place an octet
        //   3) shift_exponent is   6 more than needed for the next octet
        int i   = 0;
        while   (i != cont_octet_count && to != to_end) {
            shift_exponent -= 6;
            *to++ = static_cast<char>(0x80 + ((*from / (1 << shift_exponent)) % (1 << 6)));
            ++i;
        }
        // If   we filled up the out buffer before encoding the character
        if(to   == to_end && i != cont_octet_count) {
            from_next = from;
            to_next = to - (i+1);
            return std::codecvt_base::partial;
        }
        *from++;
    }
    from_next = from;
    to_next = to;
    // Were we done or did we run out of destination space
    if(from == from_end) return std::codecvt_base::ok;
    else return std::codecvt_base::partial;
}

// How many char objects can I process to get <= max_limit
// wchar_t objects?
int utf8_codecvt_facet::do_length(
    BOOST_CODECVT_DO_LENGTH_CONST std::mbstate_t &,
    const char * from,
    const char * from_end,
    std::size_t max_limit
#if BOOST_WORKAROUND(__IBMCPP__, BOOST_TESTED_AT(600))
) const throw()
#else
) const
#endif
{
    // RG - this code is confusing!  I need a better way to express it.
    // and test cases.

    // Invariants:
    // 1) last_octet_count has the size of the last measured character
    // 2) char_count holds the number of characters shown to fit
    // within the bounds so far (no greater than max_limit)
    // 3) from_next points to the octet 'last_octet_count' before the
    // last measured character. 
    int last_octet_count=0;
    std::size_t char_count = 0;
    const char* from_next = from;
    // Use "<" because the buffer may represent incomplete characters
    while (from_next+last_octet_count <= from_end && char_count <= max_limit) {
        from_next += last_octet_count;
        last_octet_count = (get_octet_count(*from_next));
        ++char_count;
    }
    return static_cast<int>(from_next-from_end);
}

unsigned int utf8_codecvt_facet::get_octet_count(
    unsigned char   lead_octet
){
    // if the 0-bit (MSB) is 0, then 1 character
    if (lead_octet <= 0x7f) return 1;

    // Otherwise the count number of consecutive 1 bits starting at MSB
//    assert(0xc0 <= lead_octet && lead_octet <= 0xfd);

    if (0xc0 <= lead_octet && lead_octet <= 0xdf) return 2;
    else if (0xe0 <= lead_octet && lead_octet <= 0xef) return 3;
    else if (0xf0 <= lead_octet && lead_octet <= 0xf7) return 4;
    else if (0xf8 <= lead_octet && lead_octet <= 0xfb) return 5;
    else return 6;
}

namespace {
template<std::size_t s>
int get_cont_octet_out_count_impl(wchar_t word){
    if (word < 0x80) {
        return 0;
    }
    if (word < 0x800) {
        return 1;
    }
    return 2;
}

// note the following code will generate on some platforms where
// wchar_t is defined as UCS2.  The warnings are superfluous as
// the specialization is never instantitiated with such compilers.
template<>
int get_cont_octet_out_count_impl<4>(wchar_t word){
    if (word < 0x80) {
        return 0;
    }
    if (word < 0x800) {
        return 1;
    }
    if (word < 0x10000) {
        return 2;
    }
    if (word < 0x200000) {
        return 3;
    }
    if (word < 0x4000000) {
        return 4;
    }
    return 5;
}

} // namespace anonymous

// How many "continuing octets" will be needed for this word
// ==   total octets - 1.
int utf8_codecvt_facet::get_cont_octet_out_count(
    wchar_t word
) const {
    return get_cont_octet_out_count_impl<sizeof(wchar_t)>(word);
}


#endif



,


원래 컴퓨터 문자의 시초는 아스키 코드다. 아스키 코드에서는 1 문자는 1 byte 로 이루어져 있다.

하지만 이것으로는 모든 문자를 표현하는 것이 불가능하다. 요즘처럼 글로벌 시대에 다국어를 표현하려면 1 byte 는 많이 부족하다 특히 한글은 전세계 언어중에서 가장 큰 다양성을 가지고 있는데 모두 다 조합하면 다른 언어 다 합친것의 절반이상의 용량을 차지한다. 세종대왕님 감사합니다. ^^

다국어 뿐만 아니라 특수 문자 문제도 있기 때문에 적어도 2 byte 의 길이를 가진 code set 이 필요하게 되었다.

하지만 컴퓨터는 미국에서 개발되었고 걔네들은 2byte 쓸 이유가 없다. 특히 램값이 금값인 시절에 문자열 하나에는 1 byte 이상 차지하는 건 사치다. 그래서 1 byte = 1 문자로 최근까지 이어져왔다. 하지만 우리나라 같은 곳에서는 어쩔 수 없이 편법을 써서라도 한글을 표현해야 했고, 이를 극복하기 위해서 쓰는 대표적인 개념이 codepage 라는 개념이다.

데이터는 고정된 상태에서 codepage 에 따라 보이는 모양이 변한다. 예전에 일본 게임을 한국 윈도우에서 실행하면 메뉴의 글이 깨지는 것을 볼 수 있다. code page 가 일본으로 설정되어 있어야 제대로 보이기 때문이다. 하지만 일본 게임의 일본어를 보기위해 기본 code page 를 일본으로 설정하면 한글 윈도우 내의 다른 모든 한글이 엉망이 되버리는 문제가 있다.


이러한 문제로 개발 된 것이 unicode 이다.
모든 문자셋 + 기호를 지원하기 위해 2 byte 이상의 용량을 차지하는 문자셋을 개발한 것이다.

윈도우도 windows 2000 부터는 문자 set 으로 unicode 가 사용되었다. 기존의 아스키 코드가 1 byte 라면 window 에서 사용하는 unicode 는 2 byte 로 고정되어 있는 UTF16LE (little edition) 을 사용한다. 참고로 대부분의 unix 계열은 UTF-8 을 사용한다.

나도 처음에 unicode 하면 UTF16LE 인줄 알았다. 근데 잘 보니 unicode 에 종류가 무척 많다. 다 기억은 못하지만 UTF16BE (big edition) 도 있다. 다 알것 없고 unicode 는 2가지를 많이 쓴다고만 알면 된다.

1) UTF16LE --> windows 2000, winxp, vista, windows 7 등의 unicode / 2 byte 고정
2) UTF-8 --> unix/linux 계열에서 사용 / mysql 등의 database 에서 사용 / web 에서 표준 / 1 byte ~ 4 byte 가변
 
즉 많이 쓰는 것은 UTF16LE 와 UTF-8 두가지다.
 
UTF-8 이 참 재미있는 녀석인데 이놈은 개발 당시부터 아스키를 기준으로 만들어진 기존 프로그램을 그대로 이용하기 위해서 만들어졌다. 따라서 아스키 문자열과 호환이 된다. 하지만 1 byte 인 아스키 문자열이 커버하지 못하는 부분을 1 byte ~ 4 byte 까지 더 확장해서 표현한다. 그리고 문자열 중간에 null code 가 없기 때문에 기존 아스키 프로그램에 잘 돌아간다. 이런 이유로 unix 계열 / web / database 에서 unicode 하면 대부분 UTF-8 이다.

UTF16LE 는 마이크로 소프트 윈도우즈에서 사용되는 2 byte 문자셋이다. 장점은 문자열 길이 잴때 편하다(무조건 2로 나누면 되니깐... UTF-8 은 한문자가 몇바이트인지 앞에서부터 세보지 않으면 알 수 없다.)는 것 빼고는 다른 면에서 UTF-8 보다 뭐가 좋은지 잘 모르겠다. 결정적으로 기존 아스키 프로그램에 호환이 안되기 때문에 프로그램을 다시 짜야한다.

함수를 모조리 바꿔야 하는데 이게 보통 머리아픈게 아니다. 윈도우 내장 API 함수를 보면 MessageBoxA / MessageBoxW 이렇게 2가지가 있는데 A 로 끝나는 것은 기존의 아스키 함수 / W 는 Wide Character 를 쓰는 유니코드 함수이다. MFC 같은 라이브러리에서는 MessageBox 라고 하면 셋팅을 보고 알아서  MessageBoxA / MessageBoxW 중에 한놈으로 바꿔준다.


 


 
,



MedicalPhoto 는 의료용 사진관리 프로그램이다. 현재 의료용 사진관리 프로그램은 제대로 된 게 없다. 2004 년에 사진관리하기가 귀찮아서 만들었고 현재는 서울아산병원, 중앙대 병원 피부과에서 사용중이다. 무료 software 이다.

전세계인이 쓸 수 있는 사용하기 편한 의료용 사진관리 프로그램을 위해 개발되었다.

의사 입장에서 좋은 점은

1) 피부과 진단 코드가 거의다 들어있다. 거의 모든 textbook 에 있는 진단 코드와 ICD-10 코드 포함
2) 네트워크 지원으로 진료방에서 볼 수 있다.
3) 큰 화면을 지원하며 인터페이스가 직관적이라 사용이 편하다.
4) 자동 업데이트 지원

이 프로그램은 개발하면서 정말 오랜 시간이 걸렸다. 버그를 없애기 위해 엄청난 노력을 기울였다. 프로그래밍 입장에서 발전한 점은

1) 인스톨러 (ci installer 사용) & 웹 자동 업데이트
2) 서버-클라이언트 자동 업데이트
3) unicode 지원 (UTF-16LE & UTF-8)
4) Sqlite 데이터베이스로 사용
5) Joomla 로 홈페이지 작성 - maxmind 의 geographic tool 이용
6) GPL 라이센스를 따른다. - SVN 으로 코드 관리 중이다.
7) sourceforge.net 에 등록
8) pdf 토큐먼트 제공
9) 다중 모니터 지원


내가 지금까지 써본 상용 의료용 사진 관리 프로그램보다 훨씬 빠르고 강력하다. 프로그램은 installer 가 5 메가 정도로 작고 메모리도 순간 최고 20 메가 정도 밖에 안 차지한다.

http://medicalphoto.org




,


내가 2004 년 울산대학교에 파견 근무를 나가서 만든 프로그램이다.

이 프로그램을 만들면서 SQL 프로그래밍을 익혔다. sql 서버는 mysql 로 동작하고 프로그램은 mysql++ 를 이용해서 코딩하였다.

mysql++ 는 아래에서 받을 수 있다.
http://tangentsoft.net/mysql++/

mysql 을 사용한다면 mysql++ 로 코딩하게 되겠지만 대부분 이렇게 큰 DB 를 사용할 필요는 없어 보인다.

이경우 sqlite 가 좋은 대안이다. 작년에 완성된 Medicalphoto (http://medicalphoto.org) 는 Sqlite (http://sqlite.org) 를 DB 로 사용한다. cppsqlite (http://www.codeproject.com/KB/database/CppSQLite.aspx)라는 좋은 라이브러리가 있고 유니코드도 UTF-8 범위에서 지원한다.

아래는 dermastat 화면이다. ^^

소스가 필요한 사람은 별도로 메세지를 주세요.


,


FEDORA 나 UBUNTU 를 설치하면 한글 / 영문 버젼을 선택해서 설치해야합니다.

문제는 한글을 사용하면서 display 되는 메뉴나 메세지는 영문으로 받고 싶은 상황입니다.

대부분의 문서가 영문으로 된 메세지, 메뉴를 기준으로 하기 때문입니다.

아래는 fedora 3 에서 영문 linux 상태에서 한글 입력하는 방법입니다.

리눅스는 locale 설정은

# locale

로 확인이 가능합니다. 여기서 보이는 LC_MESSAGES 가 한글이면 한글로 보이고 영문이면 영문으로 보입니다.

가장 중요한 포인트는

export LC_MESSAGES=en_US.UTF-8          ( 또는 UTF-8 유니코드가 아닌 다른 영문 locale )

로 설정하면 터미널에서 메세지가 영문으로 나옵니다.

ubuntu 도 기본적인 틀은 동일합니다.

가장 편리한 방법은 처음에 install 할 때 영문 linux 로 인스톨하고 한글을 쓸 수 있게 설정하는 것입니다.

ubuntu 의 경우 이렇게 install 하면 그놈 메뉴까지 영문으로 보입니다.



아래는 영문으로 설치한 fedora 3 에서 한글 사용법입니다.


fedora 에서 언어 설정을 한글로 하면 다 좋은데 터미널에서 아래 밑줄 (_) 이 보이지 않고 아무래도 한글이 가독성이 떨어지기 때문에 영문 메뉴에 비해서 눈에 확 안들어옵니다. 그래서 영문 fedora 상태에서 한글을 입력할 수 있나 한참을 삽질한 끝에 방법을 알아냈습니다. ^^



1. nabi 0.14 버젼을 받습니다.

http://nabi.kldp.net/

에서 받으면 됩니다.
설치를 하려면 GTK 2.4 버젼이 필요한데,

이는 fedora 인스톨 시에 GENOME 개발툴 안에 들어있습니다. 처음 fedora 설치시에 GENOME 개발툴 등을 설치해야 GTK 를 설치하는 수고를 덜 수 있습니다.

나비 홈페이지에 있는대로 설치를 합니다.

2.

나비가 어디에 설치되었는지 알 필요가 있습니다.
터미널에서

whereis nabi

를 쳐서 어디에 설치되었는지 확인합니다. 참고로 fedora 2 의 경우에는 한글로 설치하면 nabi 0.11 버젼이 설치됩니다. whereis 명령으로 위치를 확인 후에 nabi 를 실행해 보아서 어떤 것이 nabi 0.14 버젼인지 확인하세요.

3.

서비스에서 시스템 셋팅 -> 서비스 셋팅으로 들어가 보면
LLIM 이 부팅할 때 서비스로 시작되도록 되어 있는데 이를 해지해 줍니다. 해지후 SAVE

4.

/etc/sysconfig 에 있는 i18n 파일을 엽니다.

이것을 다음과 같이 고칩니다.

LANG="en_US.UTF-8"
SUPPORTED="en_US.UTF-8:en_US:en:ja_JP.UTF-8:ja_JP:ja:ko_KR.UTF-8:ko_KR:ko"
SYSFONT="latarcyrheb-sun16"
export LANG=en_US.UTF-8
export XIM_PROGRAM=/usr/local/bin/nabi
export XMODIFIERS="@im=nabi"
export GTK_IM_MODULE=xim

여기에서 /usr/local/bin/nabi 는 나비가 설치된 곳이 /usr/local/bin/ 일 경우에 해당합니다. 아까 whereis 에서 확인한 폴더를 여기에 적습니다.

5. 리부팅

리부팅하면 display 는 영문인 상태로 한글을 쓸 수 있습니다.

2004.10.06 한승석 http://whria.net
,


내가 만든 최초의 공개 프로그램 "천타를 꿈꾸며" ...

때는 1997 년. 당시에는 hetel, 나우, 천리안과 같은 pc 통신이 있었고 윈도우는 win95 가 많이 쓰이기 시작한 시기였다. 하지만 상당수의 사용자는 DOS 를 사용하고 있었다. 그리고 컴퓨터 교육이 붐이 일어서 많은 사람들이 타자연습부터 열공하고 있었다.

당시 가장 많이 사용된 프로그램은 "한메 타자 연습" 프로그램이었다. 그런데 이 프로그램에 치명적인 문제점이 있었으니 DOS 에서만 작동한다는 것이었다. win95 나 win98 과 같은 환경에서는 조금 안 이쁘게 동작하였다.

당시 방학을 맞아 뭔가 할 거리를 찾던 도중에 타자 프로그램을 만들기로 하였다.

지금은 reference 가 많이 공개되어 만들기 쉽지만 당시에 윈도우용 한글 타자 연습 만들기는 쉽지 않았다. 몇가지 문제가 있었는데...

1) 한글 IME 제어 문제 (최고 난이도 문제... backspace 누를 때마다 변하는 한글 code 를 잡아내는 문제등은 지금도 복잡한 문제이다.)
2) 한글 조합형 / 완성형 제어 문제 (당시 자료는 조합형 자료가 만아 convert 시키는 문제가 복잡했음)
3) 타자 결과를 www 으로 전송하는 문제 (당시 get / post 로 정보를 전달하는 법에 대한 자료가 정말 부족했다.)



하여튼 우여곡절 끝에 1.0 버젼을 만들었는데 반응이 폭발적이었다. 사진은 hitel 사진이다. 나우랑 천리안은 망해서 스크랩을 못했다.



당시 나우누리에서 3만회 이상 download 를 기록했던 것으로 기억한다. 다운수로 sort 했을 때 나우 역사 top 10 안에 들었다. 하지만 나는 방학때밖에 관리 할 수가 없었고 이후 번개손 등의 프로그램에 밀려서 현재는 아무도 안쓴다. ㅋㅋ


월간 천리안 1월호에 실리고 당시 모 컴터 잡지사 부록으로도 나갔다.


천타를 꿈꾸며를 통해 얻은 교훈은 ???

1) 남들이 하나도 안한 분야는 빨리 선점하라!!! 그러면 내가 대장이 된다.
2) 지속적으로 관리 안하면 무너진다.

,


 

추억의 MSX 기억하시나요?

제가 어렸을 때 열심히 BASIC 공부하다가 의욕이 넘쳐서 샀던 책입니다.

코아스 출판사의 기계어 입문이네요.



첫장이군요. 일본 사람 책을 번역한 거네요.

일본에서 온 형이 추천해 주었던 책이었죠.


 

머리말입니다. 마지막에 MSX 기계가 친근하게 느껴진다던데.. 당시에는 이해가 안가서 기계가 어렵게만 느껴졌죠.






BASIC 이 정말 느리긴 느렸습니다.




당시 5학년 4반이었군요. 1985 년이네요.

지금은 공학이랑 관계 없는 분야로 진로를 결정했지만

당시에 친구들이랑 프로그램 만들고 오락하는게 인생의 낙이었습니다.

BASIC 의 한계를 깨닳은 이후에 저는 이 책만 읽으면

사라만다, 그라디우스, 마성전설 같은 게임을 만들 수 있는 줄 믿고 용돈을 쏟아 부었죠.

지금 생각하면 절대 불가능한 일이죠. ^^
 


제 첫번째 컴퓨터였던 DPC-200 입니다. 아이큐 1000 이라는 모델이죠. 파란색 키보드를 게임(올림픽이라는 게임이 있었습니다.)하느라 5개쯤 뽀개먹은 기억이 생생합니다. 메가롬팩(당시 4만원쯤했죠)을 사고 싶어서 부모님께 조르던 기억... 성능이 좋은 아이큐 2000 을 가지고 싶었는데 못가졌던 아픈 기억도 있습니다. ^^
,