사이드바 영역으로 건너뛰기

게시물에서 찾기컴퓨터 관련한 잡다한 것들을..

10개의 게시물을 찾았습니다.

  1. 2007/08/09
    Effective 정리는 Springnote로..
    로이쿤
  2. 2007/02/01
    Regular Expression
    로이쿤
  3. 2007/01/24
    삼항연산자와 TypeCasting
    로이쿤
  4. 2006/12/17
    투컴을 한손에!! Synerge And AutoExnt
    로이쿤
  5. 2006/07/13
    history관련 cvs protocol
    로이쿤
  6. 2006/07/13
    directory 관련 유용한 tips
    로이쿤
  7. 2006/07/13
    cvs명령어 import와 co(checkout)
    로이쿤
  8. 2006/07/13
    ls, rls를 지원하지 않는 cvs에서 모듈 목록 가져오기
    로이쿤
  9. 2006/07/13
    CVS 승인 과정
    로이쿤
  10. 2006/07/13
    PatchHelper 기획
    로이쿤

Effective 정리는 Springnote로..

진보블로그 공감 버튼트위터로 리트윗하기페이스북에 공유하기딜리셔스에 북마크

Regular Expression


우리가 검색을 할때 특정 string이 아니라 특정 pattern으로 검색하고 싶을 때가 있다.
예를들어 10-2번지 만이 아니라 10-3 10-4 10-5 .. 와 같은 경우들을 모두 찾고 싶은 경우...
이런 경우에 컴퓨터에서 사용하는 것이 바로 regular expression이다.

컴퓨터는 아주아주 깊게 들여다보면 정말 단순한 녀석이기 때문에..
똑똑한 결과를 만들어내기 위해서는 많은 삽질이 필요하다.
우리가 직접 Pattern으로 검색할 수 있는 프로그램을 짜기 위해서는..
이 단순한 녀석이 똑똑해지도록 정말 많은 노력이 필요한 것이다.

하지만 이러한 삽질을 누군가가 미리 해놓았다면..
우리는 이러한 삽질을 조금의 노력을 투자하여 가져와 사용할 수가 있다.
이러한 개념을 프로그래밍에서는 Library라고 하고..
아주 유용한 Library들이 세계 곧곧에 널려있다는 점은..
프로그래머에게는 상당히 매력적이지 않을 수 없다.
(삽질이 마구마구 줄어드니까..)

C++란 언어에서는 패턴검색을 위한 라이브러리를 기본적으로 제공하지는 않는다.
따라서 패턴검색을 하기 위해서는 라이브러리를 추가로 설치해야 하는데..
C++에서는 대표적으로 boost라이브러리에서 제공하는 Regex를 많이 사용한다.Regex를 사용하기 위해서는 boost라이브러리가 설치되어야 한다.

boost를 설치하는 과정은 꽤나 복잡하다.
실제적으로 library를 VC와 연동하기 위한 작업들이 있기 때문이다.
왜 굳이 VC의 외부에서 링크하는 방식이 아닌 내부에서 가져다 쓰게 했는지는
잘 모르겠지만 아무튼 가벼운 마음으로 가져다 쓸 library는 아니다.

Regex로 내가 하고 싶었던 것은
바로 주석 지우기와 한글 추출이었다.

1. 주석 지우기

주석을 지우기 위해서는 주석에 해당하는 Regular expression을 만들고
이것을 검색하여 ""와 replace를 시키면 된다.

주석에 해당하는 Regular Expression을 만들면 다음과 같다.

    string mcomm = "(/\*([^*]|(\*+[^*/]))*\*+/)";
    string comment = "//[^nr]*";

위의 것은 multi line comment  /* ...  */에 해당하는 내용이고
아래것은 one line comment  // ... 에 해당하는 내용이다.

one line은 딱 봐도 이해하기 쉽지만 multi line은 좀 골머리를 써야한다.
자세한 설명은
http://ostermiller.org/findcomment.html
을 참고하면 좋을 것이다.

약간 형태가 다른데.. 다른 이유는 regex의 특성이 있기 때문이다.
먼저 저기의 solution에 해당하는 구현과 regex에 해당하는 구현의 가장 큰 차이점은
.이 'n'을 제외하는가 아닌가의 차이이다.
regex는 제외하지 않고 저 위의 solution은 제외하는 구현의 regular expression이다
일반적으로 .은 n과 같은 문자는 제외한다고 하는데 regex는 포함시켰다.

그다음은 wild character에 대한 escape를 하기 위해서 들어가야 하는
back slash의 갯수가 한개인가 두개인가의 차이점이다.
이것은 regex보다는 VC++의 컴파일러가 가려내는 것 때문이다.

2. 한글 추출

이 부분이 참 아스트랄 한데.. 한번 보자..

    string jamo = "[\x{3131}-\x{318E}]";
    string syllable = "[\x{AC00}-\x{D7A3}]";
    string not_syllable = "[^\x{AC00}-\x{D7A3}]";

    string paren = """;
    string not_paren_all = "[^"+paren+"]*";

    string const_str = paren + not_paren_all + syllable + not_paren_all + paren;
    string const_str2 =
            paren + not_paren_all + not_syllable + not_paren_all + paren;


일단 위의 4가지는 준비 단계에 해당하는 것이다.
자음모음은 unicode에서 0x3131 ~ 0x318E 까지 이다.
이를 string에 넣기 위해서는 꼭 가 2개 들어가야 한다는 사실!!
안그러면 에러나요~;;;

음절은 0xAC00 ~ 0xD7A3 까지 이다.
paren과 not_paren_all은 우리가 프로그램에서 보통 string을 "로 묶어서
쓰기때문에 들어가 있는 것이다.

첫번째 const_str은 "...한글..." 과 match되는 regular expression이다
하지만 그렇지 않다는 거!!! regex에서만 그런지 아님 다른 곳에서도 그런지 모르겠지만..
이것은 한글이 아닌 것들만 쫙 골라내게 된다;;

그래서 내린 특단의 조치가 바로 const_str2이다
이것은 사실 "...한글 아닌 것..."과 match되어야 하지만..
바로 이것이 "... 한글..."과 match되는 것이 아닌가..
여기 저기 찾아보았지만 만족할 만한 대답을 보지 못했다..

혹 누가 아신다면 알려주세요..ㅜ.ㅜ

3. Multi line 주석 지우기의 한계..

안타깝게도 1번의 comment는 짧은 line에서는 가능하지만..
긴 line에서는 에러를 뱉는다.. 이유는 메모리 한계를 벗어났다는 것!
아마도 설정으로 이러한 것을 더 길게 해줄 수 있겠지만..(찾아보지는 않았다;;)
애초에 별로 효율적이지 못하다는 이야기다..

사실 regular라는 것이 굉장히 일반적인 코딩이기 때문에..
특정 상황에서는 최적이 아닐 수 있다..
따라서 이것은 차라리 다음과 같은 코드로 마무리하였다..

bool ReplaceMultiLineComment(string& src)
{
    int nSize = src.size();
    bool bStart = false;
    int nStartIndex = 0, nEndIndex = 0;
    for(int i=0;i     {
        char c = src[i];
        if(!bStart)
        {
            if(c == '/')
            {
                if(src[i+1] == '*')
                {
                    nStartIndex = i;
                    bStart = true;
                }
            }
        }
        else
        {
            if(c == '*')
            {
                if(src[i+1] == '/')
                {
                    nEndIndex = i + 2;
                    src = src.substr(0, nStartIndex) + src.substr(nEndIndex);
                    return true;
                }
            }
        }
    }
    return false;
}

별로 효율적인 코드는 아님.;;



진보블로그 공감 버튼트위터로 리트윗하기페이스북에 공유하기딜리셔스에 북마크

삼항연산자와 TypeCasting

이 글은 http://j.strane.net/dokuwiki/doku.php/blog:2007:c_labyrinth_1 에서 제시된 문제에 제가 조금 더 생각해 본 것입니다.

#include "StdAfx.h"
#include
#include
using namespace std;

class B;

class A
{
public:
    A (){}
    A (const B& temp){}

    operator string() { return "It's A!"; };
};
 
class B
{
public:
    B (){}
    B (const A& temp){}
 
    operator string() { return "It's B!"; };
};
 
int main()
{
    A a;
    B b;

    string tTemp = (true ? a : b);
    cout << "Result : " << tTemp << endl;
    return 0;
}

위 예제를 다음과 같이 돌려 보았습니다.

1. A와 B에서 casting을 위한 생성자를 모두 제거합니다.
->  complie error!! : B를 A로 casting할 수가 없다는 error

2. B에서 A로의 생성자만 있는 경우

-> "It's A!"


3. A에서 B로의 생성자만 있는 경우
->
"It's B!"

4. 둘다 있는 경우
-> "It's A!"


삼항연산자 ? : 은 (이걸 뭐라하지;;)
Compile 시간에 결과인 두 항을 서로 Type casting이 가능한지 살핀다.
먼저 왼쪽의 항을 오른쪽으로 바꿀 수 있는 지 살펴본다. 가능하면 컴파일 성공!
이게 불가능하면 두번째로 오른쪽 항을 왼쪽항으로 바꿀 수 있는 지 살펴본다.
만일 이거마저 불가능하면 컴파일 error! 내용은 두번째가 불가능하다는 내용..

왜 이런 일을 하는 것일까요?
C++와 같은 언어는 Strongly-typed 언어이기 때문에...
컴파일러는 Type에 관한 checking을 수행해야 하는데..
이런 Check과정은 보통 Expression( a= b 따위의..) 단위로 이루어지게 됩니다.

대부분의 Expression은 한번의 TypeChecking으로 끝나게 되므로..
삼항연산자가 있는 Expression의 Type을 check할때..
이것을 예외적으로 처리할 수가 없어서..
컴파일러 단에서 일단 Type Cast를 시켜서
하나의 Type으로 맞추어 놓고 Check를 수행하기 때문으로 보입니다.

그 와중에 Casting을 위한 코드를 삽입하게 되고..
만일 이에 대한 생성자가 정의되어 있지 않다면..
Compile error가 날 수 밖에 없는 것이죠.

따라서 왠간히 복잡한 형태를 다루고 있다면..
이런 암묵적 casting은 위험할 수도 있으며..
쓸데없는 생성자까지 만들어줘야하는 불편함이 있으니..
복잡하지 않은 곳에만 삼항연산자를 쓰도록 하는 것이 좋겠습니다.

삼항연산자는 코드를 간결하게는 할 수 있지만 readability가 떨어지게 되니..
가급적 간단한 곳에만 사용하는게 좋겠습니다.


진보블로그 공감 버튼트위터로 리트윗하기페이스북에 공유하기딜리셔스에 북마크

투컴을 한손에!! Synerge And AutoExnt


현재 나는 회사에서 컴퓨터를 두개 쓰고 있다..
원활한 테스팅때문에 한번 썼다가..
계속 내가 쓰게 되었다..^^

아무래도 속도의 제약이 적어지니까 좋긴 하지만..
두개여서 불편한 점도 있다..

사실 본체야 저 멀리 아무대나 두어도 상관없고..
모니터야 두개 붙여 놓으면 되지만...
내가 항상 써야하는 키보드와 마우스가 두개이면 여간 불편한 것이 아니다..

하나의 키보드와 하나의 마우스로 두개의 컴퓨터를 조작하는 것!
그것이 내가 바라는 것이였다..

대부분 이런 경우를 위하여 하드웨어적인 장치를 사용하여
컴퓨터를 왔다갔다 할 수 있는 하드웨어도 있다..
하지만 그것은 왔다갔다 할때마다 눌러줘야하는 불편함이 있기에 패스..

검색을 하다가 발견한 아주 속 시원한 프로그램이 있었으니..
이름하야 Synerge!!
OS에 종속되지 않으면서..
완벽하게 두 컴퓨터의 키보드와 마우스를 공유할 수 있으며..
거기에다가 OpenSource라는 사실!!

투컴이지만 마치 원컴에 투 모니터처럼 사용할 수 있다..
게다가 놀라지 마시라... 클립보드 공유까지 된다는 사실...

하지만 사용하다보니 불편한 점이 하나 있었으니..
일단 서버역할을 하는 컴이 꺼진 경우...
어쩔 수 없이 클라이언트 쪽 컴은 사용할 수가 없다..
이를 위해서는 어쩔 수 없이 하드웨어를 사용해야 한다..ㅜ.ㅜ

그 반대의 경우에는 어떨까?
클라이언트가 꺼지고 다시 로그인 할때까지 서버는 문제없이 사용가능하다..
하지만!! 클라이언트가 부팅된 후에...
로그인을 해야만 Synerge가 실행되므로... 로그인 할때...
안타깝게도 이때도 하드웨어를 사용해야한다..

근데 만일 윈도우 로그인 전에 Synerge가 실행된다면?? 얼마나 좋을까?
그럼 서버 인풋머신들을 이용해서 클라이언트로 로그인이 가능하다..

그래서 찾게 된것이
윈도우 서비스 프로그램에 쉽게 유저프로그램을 등록시킬 수 있는 방법!!
역시 ... 세상은 넓고 프로그램은 많다..
AutoExnt가 바로 그것!!

원리는 간단하다.. 서비스 프로그램에 AutoExnt라는 프로그램을 등록시키고..
이 프로그램이 실행시킬 프로그램을 리스트에 적어만 놓으면
알아서 로그인전에 실행시켜주는 것이다~~~ 와~~
그래서 지금은 아주 효율적인 싱크를 잘 즐기고 있다..

참 세상에는 유용한 프로그램들이 많이 있다..
아주 작고 간단한 기능이지만.. 사람들이 유용하게 쓸수 있는 프로그램들..
OpenSource의 매력은 그게 아닐 런지..
또한 이런 작은 기능 하나하나가 모이면...
완벽한 큰 것 하나를 만들 수도 있다..
그래서 OpenSource의 발전 가능성은 무한하다..

앞으로 꼭 OpenSource개발에 한번 참여하고 싶다..
그전에 능력을 갖춰야...ㅜ.ㅜ
일단.. 블로그를... 좀 이쁘게 만들기 위한
Ajax공부를 하려고 한다..
이제 나와의 싸움이 시작된다..;;

진보블로그 공감 버튼트위터로 리트윗하기페이스북에 공유하기딜리셔스에 북마크

history관련 cvs protocol

 

다음을 통해 history를 얻어올 수 있다

 

 sendCommand("Directory ."); // local dir
 sendCommand(m_CVSRoot); // serv dir ??
 sendCommand("Argument -x"); // report형식
 sendCommand("Argument O"); // check out만 뽑아옴
 sendCommand("Argument -a"); // all user
 sendCommand("Argument -p"); // rep 지정
 sendCommand("Argument " + m_CVSRoot); // rep 이름
 sendCommand("history ");

 

맨 위의 두 문장이 들어가야 명령이 먹힌다는 것에 주의!

잘못된 옵션을 입력해보면 각 옵션에 대한 설명을 얻을 수 있음.

옵션은 cvs 프로그램의 옵션과 거의 같음.(틀린건 아직 못봤음..)

진보블로그 공감 버튼트위터로 리트윗하기페이스북에 공유하기딜리셔스에 북마크

directory 관련 유용한 tips

 

dir 생성

 

 CreateDirectory("dir이름",0);

 

dir 제거

 

 removeDirectory는 파일이 있거나 하위폴더가 있는 dir을 지우지 못함. system call이 강력.

 

 string command = "rd /S /Q "";
 string rd = "dir이름";
 string sysComm = command + rd;
 system(sysComm.c_str());

 

dir 이동과 복귀

 

 char cwd[1024];
 
 if ( GetCurrentDirectory(1024, (LPTSTR)cwd) != 0)
 {
  if ( SetCurrentDirectory(".\temp") )
  {

 

   //... 이동후 할일들

 

    SetCurrentDirectory(cwd);
  }
 }

진보블로그 공감 버튼트위터로 리트윗하기페이스북에 공유하기딜리셔스에 북마크

cvs명령어 import와 co(checkout)

각 명령어의 형식을 알아보면

 

import

 

   ss << "cvs -d:pserver:"<< m_Id << "@" << m_ServerName << ":" << m_CVSRoot // server
    << "import " // command
    << "-m "for PatchHelper"" // message
    << "-- PHStd avendor arelease"; // module이름과 추가로 주어야 하는것들...

 

check out

 

   ss << "cvs -d:pserver:"<< m_Id << "@" << m_ServerName << ":" << m_CVSRoot // server
    << "co " // command
    << "-P -- PHStd"; // -P는 Prune empty directory -- 다음에는 module이름

 

 

진보블로그 공감 버튼트위터로 리트윗하기페이스북에 공유하기딜리셔스에 북마크

ls, rls를 지원하지 않는 cvs에서 모듈 목록 가져오기

 

사실 co -c 가 있는데 이역시 서버 관리자가 모듈을 module/ 안에 넣지 않았으면 사용할 수 없다.

따라서 이런 경우를 위해서 많은 방법을 생각해보았는데 결론은 root의 history를  parsing하는 것이었다. 모듈이 import되어서 버젼관리가 되기 위해서는 반드시 check out 되어야 하는데, import는 남지 않지만 check out은 history에 남게 되어있다.

따라서 이러한 check out정보를 parsing하면 모듈들의 목록을 얻을 수 있다.

 

단 history정보를 서버가 보낼때 각 element의 구분을 space로 하는 문제가 있다.

따라서 파일이나 폴더의 이름에 공백이 들어가는 경우 많은 예외의 경우가 생기게된다.

 

하지만 root의 history는 모듈의 history와 조금 다르다.

root의 경우에는 좋은 점이 폴더 이름의 앞뒤에 '='를 넣어서 보내준다.

따라서 이를 기준으로 모듈 이름을 얻을 수가 있다.

하지만 파일이름에 '='들어가는 경우를 생각하면 이조차 예외가 생긴다.

 

이러한 경우를 해결하기 위해서 두가지 점에 초점을 맞춘다.

1. 폴더이름의 앞뒤에 ' ='를 넣어준다는 점

2. 줄을 맞추기 위한 space를 넣어준다는 점

 

이중에 중요한 것은 바로 2번인데 줄을 맞춘다는 것은 각 line에서 각 element의 시작 위치가 동일하다는 것을 의미하기 때문이다. 따라서 우리가 확신할 수 있는 이름의 모듈을 알고 그것을 기준으로 시작위치를 찾아서 파싱하면 정확한 파싱이 가능하다.

 

이를 위해서는 의미없는 모듈을 import하고 check out할 필요가 있다.

그리고 이 이름에 해당하는 data는 반드시 '='가 2개가 들어간 것중에서 찾아야 완벽히 정확하다.

이러한 까다로운 조건을 모두 만족시키면 완벽한 모듈이름 찾기가 가능하다.

진보블로그 공감 버튼트위터로 리트윗하기페이스북에 공유하기딜리셔스에 북마크

CVS 승인 과정

승인 과정은 다음과 같다

 

 //승인 시작
 sendCommand("BEGIN AUTH REQUEST");

 //CVS루트 입력
 sendCommand("/cvs");

 // user name 입력
 sendCommand(id);
 // password 입력
 sendCommand(getEncPasswd(passwd));

 // 승인끝
 sendCommand("END AUTH REQUEST");

 

암호는 암호화를 해야하는데 아주 단순한 알고리즘이다.

아래 글을 열면 암호화 테이블이 있는데 각 문자마다 고유한 int가 존재하는데

그 int값에 해당하는 char로 치환해주고 앞에 'A'만 붙이면 된다.



3. How to Connect to and Authenticate Oneself to the CVS server

Connection and authentication occurs before the CVS protocol itself is started. There are several ways to connect.

 

server
If the client has a way to execute commands on the server, and provide input to the commands and output from them, then it can connect that way. This could be the usual rsh (port 514) protocol, Kerberos rsh, SSH, or any similar mechanism. The client may allow the user to specify the name of the server program; the default is cvs. It is invoked with one argument, server. Once it invokes the server, the client proceeds to start the cvs protocol.

 

kserver
The kerberized server listens on a port (in the current implementation, by having inetd call "cvs kserver") which defaults to 1999. The client connects, sends the usual kerberos authentication information, and then starts the cvs protocol. Note: port 1999 is officially registered for another use, and in any event one cannot register more than one port for CVS, so GSS-API (see below) is recommended instead of kserver as a way to support kerberos.

 

pserver
The name pserver is somewhat confusing. It refers to both a generic framework which allows the CVS protocol to support several authentication mechanisms, and a name for a specific mechanism which transfers a username and a cleartext password. Servers need not support all mechanisms, and in fact servers will typically want to support only those mechanisms which meet the relevant security needs.

The pserver server listens on a port (in the current implementation, by having inetd call "cvs pserver") which defaults to 2401 (this port is officially registered). The client connects, and sends the following:

 

  • the string `BEGIN AUTH REQUEST', a linefeed,
  • the cvs root, a linefeed,
  • the username, a linefeed,
  • the password trivially encoded (see 4. Password scrambling algorithm), a linefeed,
  • the string `END AUTH REQUEST', and a linefeed.

The client must send the identical string for cvs root both here and later in the Root request of the cvs protocol itself. Servers are encouraged to enforce this restriction. The possible server responses (each of which is followed by a linefeed) are the following. Note that although there is a small similarity between this authentication protocol and the cvs protocol, they are separate.

 

I LOVE YOU
The authentication is successful. The client proceeds with the cvs protocol itself.

 

I HATE YOU
The authentication fails. After sending this response, the server may close the connection. It is up to the server to decide whether to give this response, which is generic, or a more specific response using `E' and/or `error'.

 

E text
Provide a message for the user. After this reponse, the authentication protocol continues with another response. Typically the server will provide a series of `E' responses followed by `error'. Compatibility note: CVS 1.9.10 and older clients will print unrecognized auth response and text, and then exit, upon receiving this response.

 

error code text
The authentication fails. After sending this response, the server may close the connection. The code is a code describing why it failed, intended for computer consumption. The only code currently defined is `0' which is nonspecific, but clients must silently treat any unrecognized codes as nonspecific. The text should be supplied to the user. Compatibility note: CVS 1.9.10 and older clients will print unrecognized auth response and text, and then exit, upon receiving this response. Note that text for this response, or the text in an E response, is not designed for machine parsing. More vigorous use of code, or future extensions, will be needed to prove a cleaner machine-parseable indication of what the error was.

If the client wishes to merely authenticate without starting the cvs protocol, the procedure is the same, except BEGIN AUTH REQUEST is replaced with BEGIN VERIFICATION REQUEST, END AUTH REQUEST is replaced with END VERIFICATION REQUEST, and upon receipt of I LOVE YOU the connection is closed rather than continuing.

Another mechanism is GSSAPI authentication. GSSAPI is a generic interface to security services such as kerberos. GSSAPI is specified in RFC2078 (GSSAPI version 2) and RFC1508 (GSSAPI version 1); we are not aware of differences between the two which affect the protocol in incompatible ways, so we make no attempt to specify one version or the other. The procedure here is to start with `BEGIN GSSAPI REQUEST'. GSSAPI authentication information is then exchanged between the client and the server. Each packet of information consists of a two byte big endian length, followed by that many bytes of data. After the GSSAPI authentication is complete, the server continues with the responses described above (`I LOVE YOU', etc.).

 

future possibilities
There are a nearly unlimited number of ways to connect and authenticate. One might want to allow access based on IP address (similar to the usual rsh protocol but with different/no restrictions on ports < 1024), to adopt mechanisms such as Pluggable Authentication Modules (PAM), to allow users to run their own servers under their own usernames without root access, or any number of other possibilities. The way to add future mechanisms, for the most part, should be to continue to use port 2401, but to use different strings in place of `BEGIN AUTH REQUEST'.

자료 얻은 곳 .. cvs protocol에 대한 자세한 설명을 볼 수 있음.

http://www.delorie.com/gnu/docs/cvs/cvsclient_3.html


진보블로그 공감 버튼트위터로 리트윗하기페이스북에 공유하기딜리셔스에 북마크

PatchHelper 기획

회사에서 큰 프로젝트는 cvs나 svn과 같은 버젼 관리시스템을 쓰기 마련이다.

cvs보다는 svn이 개정된 것이라고는 하지만

두 관리시스템의 구조가 너무나 차이가나서

cvs에서 원래 작업을 하던사람들은 쉽게 svn으로 옮기지를 못한다.

 

현재 Theos는 cvs를 통한 버젼관리를 하고 있는 상황이다.

svn이었으면 더 좋았겠지만 아무튼 cvs라는 상황에서 시작된 프로젝트이다.

 

이 프로젝트은 cvs에서 수정된 파일들을 원하는 형식으로 자동으로 압축해주는 프로그램이다.

현재 Theos를 비롯한 아라마루 게임들은 넷마블에서 퍼블리싱을 하는데, 넷마블로 수정본을 배포를 하는 경우에 정해진 형식으로 압축파일을 만들어줄 필요가 있다.

이러한 것을 손으로 하는 것이 아닌 프로그램으로 한다면 패치를 하는데 많은 편함이 있을것이다.

이 프로젝트를 진행하면 cvs의 사용에 대한 여러가지 이해, MFC의 활용에 대한 공부도 될 것이다.

진보블로그 공감 버튼트위터로 리트윗하기페이스북에 공유하기딜리셔스에 북마크