노무현 대통령 배너
BLOG main image
왕미친놈의 왕미친세상입니다. 미친 소리는 써도 되지만, 근거 없는 소리는 쓰면 안 됩니다.


들어가며

IF ERRORLEVEL문은 방금 실행을 마친 프로그램의 종료코드(마침코드)를 비교하여 프로그램 수행을 결정하는 구문이다. 이를 위해 몇 가지 프로그램의 종료코드를 소개하고자 한다. 또한 이러한 종료코드는 ERRORLEVEL 환경변수(%ERRORLEVEL%)에 저장하여 이용할 수도 있다.

말뜻

종료코드(마침코드)는 프로그램이 수행 과정에서 어떻게 처리하였는지를 알려주는 정수를 가리킨다. 주로 정수 0은 정상 종료되었음을 뜻하면 1보다 크거나 같은 값은 오류가 발생했음을 뜻한다. 프로그램이 종료하면서 운영체제에 넘겨주기 때문에 종료코드라는 이름이 붙었다.

여러 프로그램의 종료코드

윈도XP의 도움말 및 지원 센터의 내용에 따르면 다음과 같은 종료코드를 가지고 있다고 한다.

  • diskcomp : 두 플로피디스크를 비교하는 명령어

    • 0 : 두 플로피디스크가 같다. (정상 종료)
    • 1 : 차이를 발견했다.
    • 2 : 하드웨어 오류 발생.
    • 3 : 초기화 오류 발생.
  • xcopy : 하위 디렉터리와 파일을 복사하는 명령어

    • 0 : 파일이 오류 없이 복사됨. (정상 종료)
    • 1 : 복사할 파일을 찾지 못하다.
    • 2 : 사용자가 Ctrl+C를 눌러 강제 종료.
    • 4 : 초기화 오류가 발생. 메모리나 디스크 공간 부족, 명령줄에 잘못된 드라이브 이름이나 잘못된 구문을 입력했을 때 생긴다.
    • 5 : 디스크 쓰기 오류가 발생했습니다.
  • color : 전경색(글자색)과 배경색을 지정하고 복원하는 명령어

    • 0 : 전경색과 배경색이 다르다.
    • 1 : 전경색과 배경색이 같다.
  • diskcopy : 플로피디스크에서 다른 플로피디스크로 복사하는 명령어

    • 0 : 복사 성공. (정상 종료)
    • 1 : 치명적이지 않은 읽기/쓰기 오류가 발생
    • 3 : 치명적인 하드웨어 오류가 발생
    • 4 : 초기화 오류가 발생
  • format : 디스크를 초기화하는 명령어

    • 0 : 포맷을 성공했습니다.  (정상 종료)
    • 1 : 잘못된 매개 변수를 입력
    • 4 : 치명적인 오류가 발생 (0, 1 또는 5를 제외한 오류).
    • 5 : "포맷을 실행하시겠습니까(Y/N)?" 메시지에서 N 키를 눌러 실행이 중단됨.
  • graftabl : 그래픽 모드에서 확장 문자 집합을 표시하는 기능을 사용 가능하게 설정합니다.

    • 0 : 문자 집합을 기억장치에 읽어들이는 데 성공. 이전 코드 페이지는 로드되지 않는다. (정상 종료)
    • 1 : 잘못된 매개 변수. 작업이 수행되지 않는다.
    • 2 : 파일 오류가 발생
  • replace : 대상 디렉터리의 파일을 이름이 같은 원본 디렉터리의 파일로 바꾼다. replace를 사용하여 대상 디렉터리에 고유 파일 이름을 추가할 수도 있다.

    • 0 : replace 명령이 성공적으로 파일을 바꾸거나 추가했습니다. (정상 종료)
    • 1 : replace 명령이 맞지 않는 MS-DOS 버전을 발견했습니다.
    • 2 : replace 명령이 원본 파일을 찾을 수 없습니다.
    • 3 : replace 명령이 원본이나 대상 경로를 찾을 수 없습니다.
    • 5 : 사용자는 바꾸고자 하는 파일을 액세스할 수 없습니다.
    • 8 : 명령을 실행할 시스템 메모리가 부족합니다.
    • 11 : 명령줄에 잘못된 구문을 사용했습니다.
  • 그밖에 마이크로소프트 제품의 setup.exe 프로그램 : 윈도용 애플리케이션의 설치 프로그램

    • 0 : 프로그램 설치를 성공 또는 사용자가 취소한 경우. (정상 종료)
    • 0 이외의 값 : 프로그램 설치 도중에 프로그램 오류 발생
  • 고스트 등의 시만텍 사의 프로그램

    • 0 : 오류가 발생하지 않고 정상 종료
    • 0 이외의 값 : 오류가 발생

예제 1

IF ERRORLEVEL문과 ELSE를 함께 사용한 예제는 GBackup1H2P.cmd 파일이다. 지난번에 작성한 GBackup1H2P.cmd을

GBackup1H2P-1.cmd로 이름을 바꾸어 저장하고, GBackup1H2P.cmd을 편집해 보자.

  1. GHOST.exe -CLONE,MODE=PDUMP,SRC=1:1,DST=1:2\GHOST.GHO -Z9 -AUTO -SURE -RB
  2.  
  3. REM IF-ELSE 구문 : 오류가 발생하면 오류 발생을 알려준다.
  4. IF ERRORLEVEL 1 (
  5.     ECHO ##############################################
  6.     ECHO ##      백업 도중 오류가 발생했습니다.      ##
  7.     ECHO ##############################################
  8. ) ELSE (
  9.     ECHO ##############################################
  10.     ECHO ##    백업 작업을 정상적으로 마쳤습니다.    ##
  11.     ECHO ##############################################
  12. )

바뀐 부분만 나타내면 위와 같다. 이때 IF는 '('와 같은 명령줄에 있어야 하며, ')'와 ELSE가 같은 명령줄에 있어야 한다. 또한 ELSE는 뒤따르는 '('와 같은 명령줄에 있어야 한다.

  • 참고 : 앞서 말했듯이 위 코드는 정상 작동하지 않는다. 다만 ERRORLEVEL 검사는 잘 된다.

예제 2

XCOPY를 이용한 백업 유틸리티를 제작해 보았다.

  1. @echo off
  2. rem 파일명 : Xback.bat
  3. rem 지은이 : koc2000/SALM
  4. rem 저작권 : GPL v3
  5.  
  6. rem 예제 12.
  7. rem XCOPY를 이용한 백업
  8. rem 사용법 : Xback  원본디렉터리  대상디렉터리
  9.  
  10. rem 작업 1 : 매개변수가 있는가? 없으면 도움말을 보여준다.
  11. IF NOT * == %2* (
  12.     REM 매개변수가 모두 있으면 작업을 시작한다.
  13.     REM XCOPY 명령을 이용해 백업합니다.
  14.     XCOPY %1 %2 /i /S /E /H /M
  15.  
  16.     REM 종료코드가 4 또는 그보다 크면
  17.     IF ERRORLEVEL 4 (
  18.         echo 파일을 백업하기에는 메모리가 부족하거나
  19.         echo 드라이브가 잘못 지정되었거나 명령줄 구문이 틀립니다.
  20.     ) ELSE IF ERRORLEVEL 2 (
  21.         echo 사용자가 CTRL+C를 눌러 백업 작업을 강제 종료했습니다.
  22.     ) ELSE IF ERRORLEVEL 0 (
  23.         echo 백업을 정상적으로 성공했습니다.
  24.     )
  25. ) ELSE (
  26.     REM 매개변수가 없으면 메시지를 보인 뒤 사용법을 보여준다.
  27.  
  28.     ECHO 매개변수가 없거나 모자랍니다.
  29.     ECHO 구문 : Xback  source_dir  backup_dir
  30. )
  31.  
  32. :END

위 구문에서 11행의 " IF NOT * == %2* ( " 부분은 문자열 비교문에서 다시 설명하기로 한다.

또한 위의 코드에서 ERRORLEVEL 값이 큰 값부터 차례대로 낮아지게 만드는 것이 배치파일에서는 묵시적 원칙입니다. 이는 실수로 일어날 수 있는 오류를 막기 위한 방법이지요. 왜냐하면 IF ERRORLEVEL 구문은 "같은 값"을 구하는 구문이 아니기 때문입니다.

  1. IF [NOT] ERRORLEVEL 정수 명령어

위와 같은 경우 정수보다 크거나 같으면 뒤따르는 명령어를 실행하게 되어 있습니다.

  1. IF ERRORLEVEL 1 command01
  2. IF ERRORLEVEL 2 command02
  3. IF ERRORLEVEL 3 command03
  4. IF ERRORLEVEL 4 command04
  5. IF ERRORLEVEL 15 command15

만약에 command01을 실행하고, command02부터는 실행하지 않을 생각이었다면 위와 같은 코드는 작성해서는 안됩니다. 위의 코드는 ERRORLEVEL이 1일 때만 정상 작동하고, 나머지 경우는 항상 사용자가 원하지 않는 작동을 하게 됩니다.

반드시 command01 명령만 실행하겠다면 다음과 같은 두 가지 방법이 있습니다.

  1. rem 방법1
  2. IF ERRORLEVEL 15 command15
  3. IF ERRORLEVEL 4 command04
  4. IF ERRORLEVEL 3 command03
  5. IF ERRORLEVEL 2 command02
  6. IF ERRORLEVEL 1 command05

우선 첫 방법으로는 위와 같이 큰 수부터 비교합니다. 2행에서는 종료코드가 15보다 크거나 같으면 command15 명령을 실행하게 되지요. 그러나 다음 행에 나오는 구문이 실행될 여지는 여전히 남습니다.

그럴 경우 다른 방법으로는 아래와 같이 정확한 수를 비교하게 만듭니다. 2행에서는 종료코드가 15보다 크거나 같고, 16보다 크거나 같지 않으면(작으면), 실행한다는 뜻입니다. 이러한 경우는 종료코드가 정확히 15인 경우뿐입니다.

  1. rem 방법2
  2. IF ERRORLEVEL 15 IF NOT ERRORLEVEL 16 command15
  3. IF ERRORLEVEL 4 IF NOT ERRORLEVEL 5 command04
  4. IF ERRORLEVEL 3 IF NOT ERRORLEVEL 4 command03
  5. IF ERRORLEVEL 2 IF NOT ERRORLEVEL 3 command02
  6. IF ERRORLEVEL 1 IF NOT ERRORLEVEL 2 command05

방법1에서는 goto 명령을 사용할 경우 오류가 생기지 않지만, 일반 명령을 사용할 경우에는 오류가 생길 수 있습니다.

방법2에서는 애초에 정확한 값을 비교하게 되므로 오류가 상당히 줄어들게 됩니다.

이와 비슷한 방법으로는 아래처럼 %ERRORLEVEL%을 쓰는 명령 확장이 있습니다.

  1. if /i %ERRORLEVEL% EQU 15 command15

위 문장은 문자열을 비교해 주는 if 구문입니다. 그런데 /i 선택사항이 들어감으로써 융통성을 지니게 되어 일반적인 비교를 하게 됩니다.

  1. IF ERRORLEVEL 4 (
  2.         echo 파일을 백업하기에는 메모리가 부족하거나
  3.         echo 드라이브가 잘못 지정되었거나 명령줄 구문이 틀립니다.
  4. ) ELSE IF ERRORLEVEL 2 (
  5.         echo 사용자가 CTRL+C를 눌러 백업 작업을 강제 종료했습니다.
  6. )

위의 부분은 if 구문에 뒤따르는 명령이 하나가 아닐 때 쓰는 방법입니다. 명령이 하나가 아니라는 말은 같은 명령을 두 번 이상 쓸 경우에도 해당합니다. 위의 문장은 echo 명령을 두 번 썼기 때문에 ( ) (괄호)로 묶어야 합니다. 이러한 구문은 도스의 if와는 호환성이 없는 명령 확장 가운데 하나입니다.

또한 위에서 나타난 else 명령은 반드시 if 문의 끝에만 쓰일 수 있습니다. 또한 else 앞에는 반드시 ) (닫는 괄호)가 와야 합니다. 이때 else의 뜻은 "~이지 않고"라는 뜻이고, 위의 상황에서는 "종료코드가 4보다 크거나 같지 않고"라는 뜻이 됩니다. 뒤따르는 if 구문까지 합해서 해석하면 "종료코드가 4보다 작고, 2보다 크거나 같으면"이라는 뜻이 되지요. 종료코드가 3일 수도 있지만, XCOPY 명령은 종료코드 3을 가지지 않으므로 위의 구문은 정확히 실행됩니다.

또한 위의 방식은 구조적 프로그래밍 또는 절차적 프로그래밍에 근접한 방식입니다. GOTO 명령은 최대한 억제하여 꼭 필요한 경우에만 써야 읽기 좋고, 알아보기 좋고, 고치기 쉬운 코드가 됩니다.

관련 문서

다음 예고

이 글은 스프링노트에서 작성되었습니다.

'스크립트 > 배치파일' 카테고리의 다른 글

IF 문자열  (4) 2009.04.18
IF EXIST  (0) 2009.04.15
IF 기본 설명  (6) 2009.04.09
배치파일에 매개변수 전달하기 2  (5) 2009.04.04
배치파일에 매개변수 전달하기  (0) 2009.04.01
글쓴이는 koc/SALM입니다.
본문에 저작권에 대한 사항이 나타나지 않거나, 저작권이 BY-SA로 표기되어 있다면,
이 글은 GFDL로 공개한 글입니다.

들어가며

IF 명령은 FOR 명령과 함께 배치파일에서 지원하는 제어 명령이다. 또한 윈도XP는 도스의 그것보다 훨씬 강력한 기능을 제공하고 있다. 이때 if 명령은 가정문을 만들고, for 명령은 반복문을 만드는 데 쓰인다.

말뜻

IF는 말 그대로 "만약"이라는 뜻을 가지고 있다. 그러므로 이 명령어를 발견하면 영어를 읽듯이 읽어가면 된다.

  • 참고 : 프로그래을 짜다 보면 프로그래밍 언어의 어순은 영어의 그것과 아주 비슷함을 알 수 있다. 또한 수학의 수식이 배열되는 순서도 영어의 어순과 많이 닮아 있다.

if 기본 문법

명령줄에서 if 사용

if를 명령줄에서 쓸 수도 있지만, 주로 배치파일 안에서 쓰인다.

위 그림을 보면 분명히 명령줄에서 if 명령을 사용하고 있다. 처음 명령은 현재 디렉터리 아래에 aa 디렉터리가 존재하지 않는다면 Not found를 출력하는 명령이며, 둘째 명령은 c:\2 디렉터리가 존재하면 Found를 출력하는 명령이다.

기본 문법

명령줄에서 다음과 같이 입력하면 기본 사용법을 알 수 있다.

if /?

일단 if 명령은 조건문에서 나타나는 결과에 따라 명령어를 수행하게 됩니다. 이때 조건문은 다음과 같은 세 가지입니다.

  • 에러레벨문 : IF [NOT] ERRORLEVEL 숫자 명령어
  • 문자열 비교문 : IF [NOT] 문자열1==문자열2 명령어
  • 파일 존재 검사문 : IF [NOT] EXIST 파일이름 명령어

if 문은 위 세 가지 경우에 각 조건문이 참값을 가지면 뒤따르는 명령어를 실행시킵니다. 또한 세 가지 모두 "NOT"을 사용하여 조건문이 거짓인 경우에 명령어를 실행시킬 수도 있습니다. if, not, errorlevel, exist 등은 대/소문자를 가립니다. 대문자와 소문자를 구별하지 않으려면 /i 선택사항을 이용하면 됩니다. 다만 이 /i 선택사항은 명령 확장을 이용해야 한다. 다시 말해 도스의 if 명령과 호환성이 없다.

이 기본 문법에서는 명령 확장을 제외한 사항만 다룹니다.

말뜻

  • NOT은 영어 뜻 그대로 풀이하면 됩니다. 다시 말해 "~이 아니면"이라고 해석하면 됩니다.
  • ERRORLEVEL은 마지막으로 실행된 프로그램이 실행 결과로서 사용자에게 반환하는 마침 코드를 가리킵니다. 흔히 반환값이라고 부르며, 0부터 255 사이의 값을 가집니다. 일반적으로 "error level"이라고 띄어 쓰지만, 배치파일에서는 "errorlevel"이라고 자주 붙여 씁니다. 또한 ERRORLEVEL과 %ERRORLEVEL%은 서로 다르므로 주의해야 합니다. 마침코드가 뒤따르는 수보다 크거나 같으면 조건을 참으로 지정합니다.
  • 문자열은 "문자의 배열"이라는 뜻입니다. 쉽게 말하자면 화면에 보이는 글자의 모임 또는 묶음이거나, 기억장치에 저장된 문자의 모임 또는 묶음입니다. 명령줄에서 문자열은 <Enter>가 입력될 때까지 명령줄에 나타난 문자의 모임입니다. 또한 환경변수에 저장된 값은 기본적으로 문자열입니다. 이렇듯이 가리키는 대상에 따라 문자열은 조금씩 다르지만, 문자의 모임 또는 문자의 묶음이라는 점에서는 항상 같습니다.
  • 문자열의 비교 : 문자열을 비교할 때는 등호를 두 번 써서 비교합니다. 다시 말해 "문자열1==문자열2"와 같은 꼴로 비교하게 됩니다.
  • EXIST는 "존재한다"라는 뜻입니다. 이것은 항상 뒤따르는 파일이나 디렉터리가 존재하는지를 묻고, 존재하면 조건을 참으로 지정합니다.
  • ELSE는 "~이 아니면"이라는 뜻입니다. 이것은 앞서 나온 IF 명령이 조건을 거짓으로 지정할 경우에 실행하게 됩니다. IF 기본 명령에서는 다루지 않습니다.

예제

ERRORLEVEL문

마침 코드를 검사하여 알맞은 문장을 보여줍니다.

  1. @echo off
  2. rem 파일명 : GBackup1H2P.cmd
  3. rem 지은이 : koc2000/SALM
  4. rem 저작권 : GPL v3
  5.  
  6. rem 예제 11-1.
  7. rem 고스트 작업이 정상적으로 끝났는지를 알려준다.
  8.  
  9. rem 첫 번째 하드디스크 첫 번째 파티션에서
  10. rem 첫 번째 하드디스크 두 번째 파티션으로 백업한다.
  11. GHOST.exe -CLONE,MODE=PDUMP,SRC=1:1,DST=1:2\GHOST.GHO -Z9 -AUTO -SURE -RB
  12.  
  13. REM ERRORLEVEL 값이 1보다 작으면 NOERROR로 이동한다.
  14. IF NOT ERRORLEVEL 1 GOTO NOERROR
  15.     ECHO ##############################################
  16.     ECHO ##      백업 도중 오류가 발생했습니다.      ##
  17.     ECHO ##############################################
  18.     GOTO END
  19.  
  20. :NOERROR
  21.     ECHO ##############################################
  22.     ECHO ##    백업 작업을 정상적으로 마쳤습니다.    ##
  23.     ECHO ##############################################
  24.  
  25. :END
  26. REM 배치파일을 종료할 때 마침 코드를 되돌려준다.
  27. REM 이때 %ERRORLEVEL% 환경변수는 일종의 SET 명령 확장에 해당한다.
  28. EXIT /B %ERRORLEVEL%

시만텍 사의 프로그램은 오류가 발생하지 않으면 마침 코드로 0을 돌려주고, 오류가 발생하면 1 또는 1보다 큰 값을 돌려준다. 그것을 이용하여 고스트가 제대로 작업을 마쳤는지를 사용자에게 알려줄 수 있다.

  • 참고 : 위 GBackup1H2P.cmd 배치파일은 실패작이다. 배치파일을 실행할 환경을 고려하지 않고 작성한 탓에 도스에서도 윈도에서도 디스크를 백업한다는 본래 목적대로 실행이 되지 않는다. 물론 ERRORLEVEL문을 시험하는 예제라는 의미에서는 잘 작동한다.

문자열 비교문

if 명령을 이용하여 문자열을 비교할 수 있다. 도스에서는 문자열에 공백이 들어가는 경우가 없으나, 윈도에서는 공백이 들어갈 수도 있다.

앞서 만든 HelloWho2.cmd 파일을 참조하여 매개변수를 출력하는 배치파일을 만들었다.

  1. @ECHO OFF
  2. REM 파일명 : ViewPara.bat
  3. REM 지은이 : koc2000/SALM
  4. REM 저작권 : GPL v3
  5.  
  6. REM 예제 11-2.
  7. REM 매개변수를 한 줄에 하나씩 보여준다.
  8.  
  9. REM 가장 먼저 %0을 보여준다.
  10. ECHO %0
  11.  
  12. REM for 명령을 쓰지 않고 순환문을 만든다.
  13. :LOOP_START
  14.     REM %1 매개변수가 없으면 순환문을 끝낸다.
  15.     IF * == %1* GOTO :LOOP_END
  16.  
  17.     REM %1 매개변수를 출력한다.
  18.     ECHO %1
  19.  
  20.     REM %(N) 매개변수를 %(N-1) 매개변수로 자리 이동한다.
  21.     SHIFT
  22.  
  23.     GOTO :LOOP_START
  24. :LOOP_END
  25.  
  26. :END

GOTO 명령은 배치파일 내부에서 레이블이 있는 곳으로 CMD.EXE 명령줄을 옮기는 역할을 한다. 레이블은 앞에 콜론( : )을 붙여 구분하고 있다.

또한 SHIFT 명령은 %0부터 %9까지의 매개변수의 순서를 바꾸어 주는 역할을 한다. 다시 말해 %9는 %8이 되고, %8은 %7이 된다. 그렇게 하나씩 이동하며 %1은 %0이 된다. 또한 %9 바깥에 있던, 지금까지 표현하지 못했던, %10의 위치에 있던 값을 %9로 옮겨 준다.

ViewPara.bat 배치 파일은 윈도와 도스에서 모두 실행할 수 있으나, 실행 결과는 항상 같지 않다.


위는 윈도XP의 명령 프롬프트이며, 아래는 버추얼박스에서 도스 v6.22를 실행한 화면이다. 다른 경우는 모두 같지만, 따옴표로 묶인 부분을 처리하는 방법이 다르다. 다시 말해 윈도XP에서는 따옴표로 묶인 부분은 하나의 문자열로 보았지만, 도스에서는 따옴표는 무시하고 화이트스페이스(공백문자)로 나뉜 부분을 문자열로 보았다.

또한 이 문자열 비교문은 운영체제의 환경변수를 비교할 때도 쓸 수 있다.

파일 존재 검사문

배치파일은 작업에 앞서 파일이나 디렉터리가 존재하는지를 알아볼 수 있는 방법을 제공한다. 바로 "if exist"라는 구문이다.

  1. @ECHO OFF
  2. REM 파일명 : FindFile.cmd
  3. REM 지은이 : koc2000/SALM
  4. REM 저작권 : GPL v3
  5.  
  6. REM 예제 11-3.
  7. REM 명령줄에서 지정한 파일이 현재 경로에 있는지를 알려준다.
  8.  
  9. REM 판별 : %1이 없으면 도움말을 보여준다.
  10. IF * == %1* GOTO :ViewHelp
  11.  
  12. REM 판별 : %1이 있으면 그 파일이 있는지를 보여준다.
  13. IF EXIST %1 GOTO :Found
  14. ECHO File %1 was Not Found.
  15. ECHO.
  16. GOTO :END
  17.  
  18. :Found
  19. ECHO File %1 was Found.
  20. ECHO.
  21. GOTO :END
  22.  
  23. REM 도움말
  24. :ViewHelp
  25. ECHO FindFile v0.0.1
  26. ECHO Syntax : FindFile FileName
  27. ECHO.
  28.  
  29. :END

명령줄에서 매개변수로 파일명을 입력받아, 그 파일명에 해당하는 파일이 존재하는지를 알려준다.

 

다음 예고

IF 명령을 하나씩 짚어보자. (1)

이 글은 스프링노트에서 작성되었습니다.

'스크립트 > 배치파일' 카테고리의 다른 글

IF EXIST  (0) 2009.04.15
IF ERRORLEVEL에 쓰이는 종료코드  (12) 2009.04.14
배치파일에 매개변수 전달하기 2  (5) 2009.04.04
배치파일에 매개변수 전달하기  (0) 2009.04.01
배치파일 멈추기  (1) 2009.03.29
글쓴이는 koc/SALM입니다.
본문에 저작권에 대한 사항이 나타나지 않거나, 저작권이 BY-SA로 표기되어 있다면,
이 글은 GFDL로 공개한 글입니다.

카테고리

분류 전체보기 (1005)
스크립트 (22)
벌레와 팁 (126)
소프트웨어 (240)
하드웨어 (6)
이야기 (24)
말의 나무 (506)
미쳐보자 (22)
일기 (48)
아이폰 (10)

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

달력

«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

글 보관함