Study - Programming/C/C++2009. 2. 4. 08:38
BOOL CreateProcess(
  LPCTSTR lpApplicationName,
  LPTSTR lpCommandLine,
  LPSECURITY_ATTRIBUTES lpProcessAttributes,
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  BOOL bInheritHandles,
  DWORD dwCreationFlags,
  LPVOID lpEnvironment,
  LPCTSTR lpCurrentDirectory,
  LPSTARTUPINFO lpStartupInfo,
  LPPROCESS_INFORMATION lpProcessInformation
);

MSDN 참고 주소 : http://msdn.microsoft.com/en-us/library/ms682425.aspx (닷넷 기준이라 조금 모양이 다릅니다.)

자식 프로세스를 생성한다. 이때 이 함수를 호출한 프로세스는 부모 프로세스(Parent Process), 함수 호출로 생성된 프로세스를 자식 프로세스(Child Process)라고 한다.


  LPCTSTR lpApplicationName : 생성할 프로세스의 실행파일 이름.
  LPTSTR lpCommandLine : 생성할 프로세스의 파라미터 문자열

  LPSECURITY_ATTRIBUTES lpProcessAttributes : 프로세스의 보안 속성
  LPSECURITY_ATTRIBUTES lpThreadAttributes : 스레드를 전달할 경우 보안 속성
  BOOL bInheritHandles : 자식프로세스의 부모 프로세스의 핸들 소유 여부
  DWORD dwCreationFlags : 생성하는 프로세스의 프로세스 특성
  LPVOID lpEnvironment : 프로세스가 실행헤 필요한 문자열 저장. Enviroment Block 지정
  LPCTSTR lpCurrentDirectory : 자식 프로세스의 현재 디렉터리 설정
  LPSTARTUPINFO lpStartupInfo : 자식 프로세스의 속성이 담긴 구조체의 주소값
  LPPROCESS_INFORMATION lpProcessInformation : 자식 프로세스의 정보를 저장할 구조체 주소값


그리고 다음은 LPSTARTUPINFO의 구조이다.

typedef struct _STARTUPINFO {
  DWORD  cb;
  LPTSTR lpReserved;
  LPTSTR lpDesktop;
  LPTSTR lpTitle;
  DWORD  dwX;
  DWORD  dwY;
  DWORD  dwXSize;
  DWORD  dwYSize;
  DWORD  dwXCountChars;
  DWORD  dwYCountChars;
  DWORD  dwFillAttribute;
  DWORD  dwFlags;
  WORD   wShowWindow;
  WORD   cbReserved2;
  LPBYTE lpReserved2;
  HANDLE hStdInput;
  HANDLE hStdOutput;
  HANDLE hStdError;
} STARTUPINFO,
*LPSTARTUPINFO;

특별히 중요한 요소가 있다기보다는 이런게 있다라는 전체적인 것을 아는 것이 좋다. 자세한 것은 MSDN(http://msdn.microsoft.com/en-us/library/ms686331(VS.85).aspx) 을 참조하자.

다음은 LPPROCESS_INFORMATION 의 구조이다.
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId; } PROCESS_INFORMATION,
*LPPROCESS_INFORMATION;
자세한 사항은 좀 더 공부를 해 보아야겠다.

그리고 주의해야 할 점이 유니코드 기반에서 함수 실행 시 lpCommandLine인자에 바로 _T("abc.exe")형식으로 문자열을 넣으면 런타임에러가 난다. 함수 내부적으로 문자열에 변화를 가하기 때문이라고 한다. 그러므로 넘길때는 반드시 따로 문자열 변수를 만들어 넘기자.



Posted by 머리
Study - Programming/C/C++2008. 12. 15. 03:42


달팽이 모양 수열
한번 만들어 봤다.

오랫만에 하니까 영 하는게 어색하다. 아..잠자야 하는데..

Posted by 머리
Study- MSC/Computer2008. 10. 24. 19:48
  이번 포스팅에서는 동적할당을 객체의 생성쪽으로 확장해 보고자 한다. 먼저 말해두지만 이 포스팅은 포인터나 객체, 클래스를 설명하기 위한 포스팅이 아니다. 어디까지나 이런 사항에 대한 기본적인 지식은 알고 있다고 가정하고 진행하겠다. 우선 C++에서 Example이라는 클래스가 있고, 그것에 대한 객체를 생성해 보자.

Example ex;

  자. ex라는 객체가 생성되었다. 아마 특별한 문제 없이 제대로 돌아갈 것이다. 그리고 파괴도 잘 될 것이다. 하지만 이런 방식의 객체 생성은 권하고 싶지 않다. 대신 이런 객체의 생성을 권한다.

Example* pEx = new Example();

  위 방식은  Example라는 클래스 형식으로 메모리 공간에 할당을 한 후 그 주소를 pEx 포인터 변수에 넣어서 사용하게 하는 방식이다. 앞선 포스팅에서, 메모리의 어딘가에(HEAP) 공간을 할당한다고 했는데 위 방식도 바로 이런 방식이다. 이렇게 해 놓으면 이어서 이렇게도 가능하다

Example *pEx = new Example();
pEx = new Example();

  잘 이해가 가지 않을것이다. 무슨 이야기냐면 한 객체를 메모리에 생성하여 한 포인터에 주소를 기억시켰는데, 그 포인터를 재사용하여 또다른 객체를 또 생성하여 그 주소를 넣을 수 있다는 이야기이다. 물론 위 코드는 첫 객체의 해제에서 문제가 발생하지만 이런식으로 좀 더 코드에 유연성을 줄 수 있다. 그렇지 않으면 Example ex1,ex2; 등의 방식으로 객체를 생성해야 하는 문제가 발생한다.
  앞서 말한 유연성 외에도 이런 방식의 동적 할당을 통한 객체 생성은 맨 처음 말한 동적할당의 장점을 최대한 살릴 수가 있게 된다. 객체는 일반 변수보다 여기 저기서 사용될 가능성이 많다. 무슨 말이냐면 지역 변수 이상의 용도로 사용될 가능성이 많다는 이야기이다. 만약에 게임을 만들때 적을 표현하는 클래스가 있고 그 객체를 생성한다고 하면 상황에 따라 계속해서 적을 만들어 내야 하므로 동적 할당이 필요 할 수 밖에 없다.(int형으로 적을 표현할수는 없지 않은가?)
  이제까지는 객체를 동적할당 할 때 포인터를 이용하였다. 그 이유는 C++은 기본적으로 Call by Value 방식이기 때문이기 때문인데 이는 변수 및 객체의 사용을 한정적인 위치에서만 사용할 수 밖에 없게 만든다. 이 틀을 극복하기 위해서 메모리상의 주소로 읽기 위한 포인터가 사용되는데, 여기서 한단계 더 나가서 레퍼런스라는 개념이 나온다. 적어도 여기서는 Call by Reference와 Call by Address의 개념을 다르게 두자. Call by Address는 결론적으로는 포인터의 값을 이용한 Call by Value로 볼 수 있다. 엄밀하게 말하면 Call by Reference와는 다르다.(물론 내부적으로 이놈도 포인터를 쓰긴 쓴다.)
  말이 샜는데, C++ 이후의 객체 지향 언어는 기본 참조 방식을 Call by Value가 아니라 Call by Reference를 채택한 경우가 많다. JAVA가 그렇고, C#이 그러며, 심지어 Visual Basic도 Call by Reference가 기본이다.
  무슨 말이냐면 만약에

Example ex;

라는 코드가 있으면 C++에서는 ex가 객체 그 자체이지만 JAVA나 C#등에서는 저것은 그냥 참조를 위한 레퍼런스에 불과하지 객체 자체는 아니다. 즉 아무것도 못한다. 레퍼런스는 포인터처럼 메모리 상의 어떤 것을 가리켜야 그 의미가 있다. 즉

Example ex = new Example();

  와 같이 객체를 생성하고 그것을 참조하여야 한다.
  말이 많았다. 정작 하고 싶은 말은 '동적할당은 메모리 어딘가에 공간을 할당하고 포인터를 이용하여 그것을 참조한다, 그리고 이는 객체에서도 마찬가지이며 좀더 나아가 레퍼런스는 참조 없이는 아무런 의미가 없다'였는데 이것을 표현하기 위해 쓸데없는 말이 너무 많았던 것 같다.
  아무쪼록 이 글을 읽고 뭔가 '아!'하고 깨달음(?)을 얻는 프로그래머 지망생이 있으면 좋겠다. 그렇게 기원하면서 이상 3회에 걸친 포스팅을 마치고자 한다.
Posted by 머리
Study - Programming/C/C++2008. 10. 21. 19:37
  요즘 '조엘 온 소프트웨어'라는 책을 읽고 있는데 그 책에서 문자열 관련 이야기가 나오기에 그 내용을 참고하여 한번 이야기 하고자 한다.

  C의 문자열 구조는 먼저 문자열 내용이 있고 그 다음 '널 문자'가 들어가는 형식이다. 즉 "Hello"라는 문자열을 표현하려면 

'H' 'e' 'l' 'l'o' '\0' (단,\0 은 Null Characher)

  이렇게 6글자(sizeof(char) == 1이므로 6Byte)를 차지하게 된다. 이 Null Character의 사용 용도는 문자열의 끝(End of String)을 나타내기 위한 방법인데, 적어도 C에서는 이 문자가 없다면 메모리 어느 부분에 우연히 이 문자가 있는 것을 발견할때까지 계속해서 읽게 되어 엉뚱한 문자열이 나오게 된다.

  언뜻 보면 무슨 내용이 들어 있을 지 모르는 메모리에 이런 방식으로 문자열을 담는 것은 꽤 괜찮은 방법처럼 보인다. 하지만 조금 만 더 생각해보면 이것보다 좋은 대안을 찾을 수 있다.
  무엇보다 위 방식의 가장 큰 단점은 무슨 작업을 하려면 반드시 문자열 처음부터 끝까지 탐색을 하러 가야 한다는 것이다.
  예를 들어보자. 문자열의 길이를 알아내는 함수(strlen)를 구현해 보자고 한다면 문자열의 첫 포인터에서 Null Character을 찾을 때까지 포인터를 이동시켜야 한다.
  그런데 만약에 이 문자열의 길이가 100이라면? 그리고 1000이라면? 이런식으로 가면 문자열이 길면 길수록 그 처리 속도는 길어질 것이다. 다른 예로, 두 문자열을 잇는 함수(strcat)를 구현하자고 하면 두 문자열의 길이를 알아서 한 문자열에 할당된 크기를 다시 합친 크기로 재할당 해야 하고 문자열의 끝으로 이동해서 복사하는 과정을 수행해야 한다. 이것 역시 문자열의 길이를 구하는 과정에서부터 속도의 문제가 발생한다.
  이런 것을 해결하기 위한 방법으로는 뭐가 있을까? 이런 방법이 있을 수 있다.

'5' 'H' 'e' 'l' 'l' 'o'

  즉, 문자열의 첫 바이트에 문자열의 길이를 넣는 것이다. 이러면 굳이 끝까지 가서 널 문자를 찾을 필요 없이 길이를 첫 바이트에서 알 수 있으므로 그 크기 만큼 포인터를 이동해서 해결 할 수 있다. 여기서 주의해야 할 점은 끝부분에 Null Character을 넣는 방식은 하지 말라는 것이다. 그렇게 하면 정말 이도 저도 아닌 형편없는 문자열 표현 방식이 된다.
  다른 방법으로는 String Class를 구현하는 것인데, 이미 객체지향 프로그래밍 언어나 좀더 고급언어인 경우에는 지원되는 경우가 많다. 클래스의 멤버로 다음과 같은 예를 들 수 있다.

1. 문자열의 저장할 변수
2. 문자열의 길이를 저장할 변수
3. 기타 문자열 관련 함수

  위 방법으로는 효과적이고 자기 나름대로의 알고리즘으로 효율적으로 문자열을 관리 할 수 있을 것이다. 그리고 나 같으면 문자열을 저장 할 때 더이상 Null Character은 넣지 않도록 구현 할 것이다. 그리고 C++같은 경우라면 Operator Overloading등의 기능을 통해서 출력 및 입력, 기타 연산등의 방법도 구현해 보겠다.다만 위의 방법은 각자의 클래스 구현으로 표준이 규정되어 있지 않다면 호환성에서 문제가 발생 할 수 있겠고 조금 무거울 것이다.

  이외에도 많은 방법이 있을 것이다. 중요한 것은 이런 기본 데이터 타입에 만족하지 않고, 뭔가 다른 방식을 조금씩 연구하고 구현해 본다면 많은 도움이 될 것이다.
Posted by 머리
Study- MSC/Computer2008. 10. 1. 21:27
  예전에 비트맵을 분석해보려고 이것 저것 소스를 보다가 재밌는 코드를 본 적이 있다.
  비트맵을 파일 입출력으로 직접 읽어서 분석하는게 아니라 메모리를 할당한 후 파일에서 메모리로 전체를 복사시킨 후 파일은 닫고 메모리의 내용만 읽는 코드였다.
  흥미로운 방식이였다. 그러면서 든 생각이 그럼 바로 파일 입출력을 하는 것과 메모리에 올려서 하는 방식과 무엇이 다를까? 하는 것이였다. 그리고 이번 휴가를 통해서 바로 시험해 보았다.

실험 방법 : 어느정도 용량이 되는 TEXT 파일을 스트림에서 직접 읽는 방식과 메모리에 올려서 읽는 방식의 속도를 비교

소스 코드는 다음과 같다.


#include <stdio.h>
#include <stdio.h>
#include <windows.h>//GetTickTIme 호출

#define COUNT 1000000

//메모리를 통해서 파일을 읽는 것과 직접 FIle Stream에서 읽어 오는 것의 속도 비교

int main(void)
{

    FILE* fp;//파일을 읽을 파일 포인터
    char read[100];
    char a;
    char* str;

    unsigned int FileSize;
    int StreamTime;//스트림으로 읽었을 때의 시간
    int MemoryTime;//메모리로 했을 때의 시간

    //읽을 파일을 만드는 부분
    unsigned long i;

    if(fp = fopen("TestFile.txt","w"))
    {
        for(i = 0 ; i < COUNT ; i++)
        {
            fputs("TestFileText\n",fp);
        }

        fclose(fp);
    }


    //File Stream에서 읽기
    if(fp = fopen("TestFIle.txt","r"))
    {
        StreamTime = GetTickCount();  //시간 재기
        while(!feof(fp))
        {
            fgets(read,80,fp);
            printf("%s",read);
        };
        StreamTime = GetTickCount() - StreamTime;
        fclose(fp);
    }

    //Memory에 올린 후 읽기
    if(fp = fopen("TestFIle.txt","r"))
    {

        MemoryTime = GetTickCount();//재기 시작

        fseek(fp,0L,SEEK_END);
        FileSize = ftell(fp);//파일의 크기를 알기 위해 끝까지 이동 후 크기 저장

       
        str = malloc(sizeof(char) * FileSize);//크기 만큼 메모리 할당
        memset(str,0,FileSize);
        fseek(fp, 0L, SEEK_SET);//파일의 처음으로 이동
        fread(str,sizeof(char)*FileSize,1,fp);//파일을 메모리에 복사

        fclose(fp);//다 읽었으니 파일을 닫는다.

        printf("%s",str);//읽은 내용을 출력

        MemoryTime = GetTickCount() - MemoryTime;//측정 끝
        printf("소요 시간 : %d Tick\n",MemoryTime);

        free(str);//메모리 해제
    }

    printf("Stream : %d\nMemory : %d\n",StreamTime,MemoryTime);

    return 0;
}

실험 결과

내용 출력 하는 시간 포함해서 결과는

스트림 직접 읽기 : 45542 Tick
메모리 올려서 읽기 : 10656 Tick

약 23%정도 메모리로 읽는 방식이 더 빨랐다.
프린트 시간을 뺴서 계산 하면 더 정확한 결과를 얻을 수 있었을 것이다.

이번 실험으로 역시 메모리에 올려 놓고 돌리는 것이 속도 면에서는 더 효과적이라는 걸 직접 볼 수 있었다. 이런 것을 이용하면 Progress를 계산한다던가, 리소스 같은 것을 올려 놓고 읽으면 Loading TIme을 줄일 수 있을 것이다.
 
이렇게 메모리에 올리는 방식을 사용하는 예로 더블 버퍼링을 예로 들 수 있을 것이다. 더블 버퍼링도 메모리에 뿌릴 내용을 미리 그려 놓고 화면에 한꺼번에 뿌리는 방식을 이용한다.


그런데 의문점이 있어. 저렇게 하면 효과는 있지만 실제로 메모리에 대용량으로 할당하는 식으로는 잘 안할 거라고 생각해.
malloc()이런 걸로 몇 MB나 하는 걸 할당하는것도 무식하다고 생각하고 요즘 프로그램들 용량 엄청나잖아. 그런걸 보조하기 위해서 페이징같은 것이 있는거고

그래도 위 방법은 어느 정도 규모의 리소스 파일들을 읽을 때는 유용한 방법이 될 거라고 생각하는데, malloc같은 할당 함수 위에 다른 방법은 없는건지, 그리고 HEAP 영역 말고 이런 상황같은 것을 위한 다른 할당 가능 공간은 없는 건지 궁금하다. 한번 알아 봐야 겠다.


Posted by 머리
Study - Programming/C/C++2008. 6. 8. 02:32
음.. 근무할 때 핑 테스트한다고 창을 열댓개를 띄워 놓고 그러니까

작업 표시줄이 너무 복잡하더라

그래서 어떻게 할 방법이 없을까 생각하다가 아예 실행 중인 프로그램을 관리하는 프로그램을 만들자고 생각해서 만들어 보았다
사용자 삽입 이미지

생각보다 간단히 만들 수 있었다.

Find Windows를 클릭하면 실행중인 프로그램이 목록으로 뜨고 Show/Hide를 조정할 수 있다.

휴가 와서 이거랑 비트맵 깔짝대는거 한다고 하루는 사용한 것 같다.

아 내일 가는구나.. 기분이 착찹하다... 일단은 핵심 소스만

   i = SendMessage(hList,LB_GETCURSEL,0,0);
   SendMessage(hList,LB_GETTEXT,i,(LPARAM)str);
   tempHwnd = FindWindow(NULL,str);
   ShowWindow(tempHwnd,SW_SHOW);
Posted by 머리
나의 이야기/Note2007. 11. 10. 01:01
  지금 내가 공부 하고 있는 언어들을 한번 세어 보면

1. C
2. C++
3. JAVA
4. C#
5. VB.NET
6. Delphi

  여기에 세부 분야까지 넣어 보면
1. API
2. MFC
3. TCP/IP

  뭐 이정도. 문제는 이런 것들을 그냥 배워 보고 싶다는 생각에 건드려만 본다는 것이다. C/C++이야 항상 만지고 있는 거니 예외라 치고, 델파이는 거의 안하니 제외 해도 너무 하려고 하는게 많다. 특히 C#이랑 JAVA는 의욕에 비해 하는게 너무 없는듯하다.

  지금 생각은 이렇다. 한 두가지 정도만 집중할것이냐 조금만 더 힘낼 것이냐. 솔직히 말해서 조금 버겁긴 하지만 그렇다고 못하겠는건 아니다. 그냥 진도가 너무 안나가는 것에 대한 불안감?

  그래도 대학 와서 내가 이쪽 분야 그동안 공부했던것과 비교해 본다면 결코 무리라는 생각은 안든다. 다만 걱정되는건 어린 시절 근 10년간 VB를 파 오면서 너무 공부하기에는 어렸었다는 것도 있었겠지만 여러가지를 많이 해보고 싶어서 이것 저것 해보다가 어중간하게 얕고 넓게(?) 익히게 되어서 그게 반복되지 않을까 신경쓰인다.

  1월 안에는 윈어플 큰거 하나 짜놓고 가긴 가야 하는데.. 제일 승산 있는게 무엇일까? 제일 승산있는건 MFC나 C#쪽인데.. 아무래도 최근에 새로 배우는 언어는 C#이 제일 무난하게 나가고 있어 조금만 더 하면 윈폼 시작할것이니. 아니면 지금 텀 프로젝트한다고 공부중인 MFC도 요즘들어 뭔가 감이 잡혀가기 시작해서 조금만 더 노력하면 속도가 붙을 것 같은데..

  이렇게 해봐야 겠다. JAVA로는 뭐 만드는건 잠시 미루되 꾸준히 공부는 하고 MFC는 텀에 충실하되 실력이 늘 수 있게, 그리고 C#으로는 뭘 만들도록 주로 공부해 봐야지. 이렇게 실천할수 있으면 얼마나 좋아.
Posted by 머리
Study - Programming/C/C++2007. 11. 5. 23:39
사용자 삽입 이미지
사용자 삽입 이미지


심심해서 만들어 본것. 실제로 되니까 조금 신기하네. 이왕이면 star함수를 쓰지 않고 for 안에 for를 넣고 싶었는데 안되더라.
Posted by 머리
Study - Programming/C/C++2007. 10. 5. 03:01
  흠.. 학교 친구들이 선대 시간에 매트랩으로 확률을 이용하여 원주율을 구하고 그 그래프를 그려 보라는 숙제를 하도 많이 물어보더라. 나는 매트랩도 할줄 모르는데..

  그래도 괜찮은 주제다 싶어서 C 콘솔 버전이랑 API 사용해서 한번 만들어 봤다. 일단 Test Case 의 수 입력받고 하는건 다 생략하고(콘솔은 가능), 랜덤하게 좌표를 찍어 내어 그게 사분원(부채꼴) 안에 들어가는 경우의 수를 계산해 내어 pi를 구하는 방식이다.

Number of Test Case = n
Number of Point in Circle = C

(pi * r^2 / 4) / r^2 = C/n
pi/4 = C/n

pi = 4 * C/n

이렇게. 하다보니 콘솔은 그렇게 문제가 아닌데 API를 하도 오랫만에 해서 GDI 새로 공부하고 한다고 시간 다 보냈다. 11시쯤에 시작했는데 끝내니 2시 반.. 뭐 암튼 좋은 공부 되었다.
사용자 삽입 이미지
콘솔 버전
invalid-file

원주율 구하기 콘솔버전


사용자 삽입 이미지

윈도우 버전
Posted by 머리
Study - Programming/C/C++2007. 9. 22. 01:08
  음.. 블로그 포스트가 하도 올릴게 없어서.. 간단히 생각해 본게 C를 처음 접하거나 익숙하지 않은 사람들이 C에 익숙해지게 할 수 있도록 몇가지 간단한 퀴즈를 내 보는 식으로 포스팅을 해보자는 것이였다. 내 실력도 아직 많이 미숙하지만, 간단한 퀴즈 정도는 내 볼 수 있으니까, 내 능력이 되는대로 할만한 퀴즈가 있으면 내 보자 한다.

  커리큘럼은 일반 C 학습과 비슷하게. 이정도를 해결 할 수가 있다면 적어도 그 부분에서는 익숙하게 사용할수 있다고 할 수 있을 것이다. 자유자재라고는 장담 못하지만..

  그럼 첫 번째 퀴즈는 무엇인가? 맨 처음이니 출력 관련 문제를 간단히 내 볼까 한다. 출력 결과는 다음과 같다.
사용자 삽입 이미지

  간단하다. printf함수 하나만을 이용한 간단한 메뉴 출력 프로그램이다. 입력받거나 입력 후 작업 그런 것은 상관없으며, 단순히 위와 같은 결과물만 출력되면 된다. 참고로 Press...이 부분은 출력할 필요가 없다.

  단, 이 프로그램 작성의 조건은 다음과 같다.
1. printf함수의 사용은 한 번 뿐이다.
2. 개행을 하는 것은 \n을 이용하되, 한줄에 저 모든 것들이 나타나서는 안된다. 반드시 한 줄당 코드 한 줄이 있어야 한다.
   즉 저 프로그램을 짜기 위해서는 1개의 printf에 적어도 6줄의 코드(메뉴 및 Bar의 수 만큼)가 있어야 한다는 것이다.(기타 #include, main함수 이런 부분은 제외)

  그냥 단순히 한줄로 다 짜면 되지 않느냐 할 수 도 있겠지만 만약 콘솔에서 10개쯤 되는 메뉴를 출력하게 짜야 한다고 치자. 이것을 한줄로 한다 하면 꽤 복잡 할것이고, 문단 정렬도 꽤 성가실 것이다. 그냥 한 줄당 printf하나를 쓰기에는 뭔가 비효율 적이고, 그냥 하나의 printf로 코드를 보기 쉽게 짜 보는 것도 괜찮을 것이다.

  관심 있는 사람은 비밀글로 리플을 달으면 그에 대한 답을 할 예정이다. 간단한 테크닉이지만 의외로 잘 모르는 사람도 있어서 한번 해 본다. 그럼 답변 기대해야지!
Posted by 머리