티스토리 뷰

Orthogonal Array를 이용한 Test Design


A사 techbard

1. 서론


필자가 소프트웨어 테스팅 분야에 투신(?)한 지도 어언 5년이 넘어 가고 있다. 필자가

테스팅을 처음 시작하던 때는 어디에도 가르쳐주는 사람하나 없었지만 그 열정만은

누구 못지 않았던 것으로 기억한다. 이런 왕성한 열정으로도 가끔은 테스트해야 할

분량을 놓고 고민하고 어쩔줄 몰라했던 기억이 아직도 생생하다. 필자는 그러한

고민을 해결하기 위해 많은 전문가들의 자료를 가지고 공부하기 시작했고, 그러던 중

소프트웨어 테스팅 분야에서 저명한 인사인 James Bach의 홈페이지를 섭렵하던 중에

그가 제작해 놓은 흥미있는 테스팅 툴을 보게 되었는데, 그의 주장으로는 테스트

케이스를 줄이고도 결함 검출 비율은 동일하다는 내용을 읽게 되었다.

앞으로 전개될 내용은 필자가 많은 관련 자료를 리뷰하고 그 주장들을 나름대로

검증해 가는 과정에서 발견한 사실들을 여러분과 공유하고자 한다. 2장 상황에서는

이러한 방법을 도입해야 하는 상황을 언급하고 3장에서는 왜 이러한 방법의 적용이

가능한지에 대한 원리 설명하고 이후 4장 적용 상의 주의사항, 5OA 계산 툴 소개,

6장 결론의 순서로 소개하도록 하겠다.


2. 상황


오늘날의 소프트웨어 제품은 다양한 조건하에서의 동작을 테스터나 개발팀 자신들도

예상할 수 없는 복잡한 구조로 이루어져 있다고 볼 수 있다. 따라서, 이런 상황은

자주 소프트웨어 개발에 들어가는 비용에 필적할 정도의 테스팅 비용을 요구하게

된다. 특히나, 소프트웨어 개발에 들어 가는 전체 총 비용이 계속적으로 증가하는

상황에서 품질에 대한 보증이 되지 않으면, 개발 비용에 대한 회수 비용을 확신할 수

없게 되어 점차 테스팅에 대한 중요성은 부각되고 있으며 그에 따른 테스팅 비용

상승은 자연스러운 것으로 받아들여 지고 있다.


이러한 상황에서 고민이 되는 것은 얼마만큼의 테스팅을 해야 적당한가에 대한 수준을

정의하는 것이라 할 수 있는데, 주목할 만한 다음의 두 가지 인식에 대해서 생각해

보도록 하자.


- 완전한 테스팅은 불가능하다.


- 테스팅은 기본적으로 위험을 전제로 하는 활동이다.


우리가 테스팅을 아무리 많이 하더라도 그 결과에 대해서 절대적으로 신뢰할 수는

없다. 또한, 완벽하게만 테스팅하려고 노력하더라도 그 결과는 테스터가 지치고

힘만 들게 된 뿐 결과적으로 볼 때 이득은 적다고 할 수 있다. 또한, 사실상 우리는

모든 위험 요소에 대해서 확인하고 발견해 낼 수 없으며, 위험 관리의 측면에서 볼

때도 얼마나 테스트 해야 하는지 또는 어디에 집중해야 하는지에 대해서 관리되어야

하기 때문에 어떤 근거를 가지고 테스트 범위나 정도를 결정하는 것이 필요하게 된다.


3. 원리 이해


Six SigmaGE에서 처음 시작된 운동으로 백 만개의 제품을 생산했을때 3.4개의

불량률을 가지면 최고 수준으로 본다. , 6 Six Sigma를 달성했다면 세계적인 수준의

생산 능력을 가졌다고 보는 것이다. 이러한 Six Sigma를 위한 실험 계획법(DOE,

Design of Experiments)에 쓰이는 기법중 하나가 Orthogonal Arrays(이하 OA)이다.

제조업체의 경우 대량 생산의 전 단계에서 제품의 품질에 영향을 미치는 요인들을

정하고 그 요인들의 적절한 수준을 알아내야 하기 때문에 미리 실험을 해야 하며

이러한 실험에는 대개 많은 비용이 들어가기 때문에 실험 전에 합리적인 수준으로

실험 비용을 줄면서 실험 결과를 극대화 하는 실험 계획법을 세우게 된다.


특히, 제조 업체의 특성상 실험 비용을 줄이면(물론 그 방법이 합리적이어야 하지만)

그 줄인 비용 만큼이 총 생산 비용에서 줄어들게 되어 이익으로 남게 되며, 또한

최소한의 실험으로 인해 제품의 최종 출시(대량 생산)가 앞당겨지게 된다. 이러한

상황에서 자동차 제조 업체와 같은 성숙한 산업에서는 실험 계획법을 최적화하는

방법을 사용해 전체 제조 비용의 5%만 사용해서도 만족할 만한 결과를 얻는 것으로

알려져 있다. 하지만, 우리가 몸담고 있는 소프트웨어 업계의 경우 실험 횟수라고

할 수 있는 테스트 과정이 제조 업체와는 달리 비용과의 연관 관계가 눈에 띄게

보이지 않는 특징이 있다고 할 수 있다. 따라서, 아직도 소프트웨어 업계에서는

테스팅 효율성을 위한 구체적인 방법이 많이 논의되고 있지 않은 것 같다.


이러한 OA 방법은 처음에 일본인 Dr. Taguchi가 제안했으며 그 방법이 일본 제조

업체와 AT&T 사 등에서 적용된 이후에 효율성이 입증된 바 있다. 우리가 지금부터

살펴볼 OA 방법은 이러한 개념을 소프트웨어 테스팅 분야에 적용하는 것이다.


OA에 대한 기초적인 이해를 위해서는 STEN 2호의 "Orthogonal Array의 소프트웨어

테스팅 적용 방법, 삼성전자 정성원"님의 글을 참고하기 바란다. 정성원 님의 글에도

언급이 되어 있지만, OA는 모든 원소간의 조합이 pairwise(서로소) 관계인 표를

의미한다. 이러한 OA표를 이해하는 여러 방법이 있지만, 필자는 간단하게 다음과 같이

정의하고자 한다.


  OA 표의 예

A B C : Parameter
=============================
1 1 1 1 : Levels
2 1 2 2 : Levels
3 2 1 2
4 2 2 1

(그림 1) OA 표의 예


여기서 주목해야 하는 내용은 ParameterLevel에 대한 것이다. 파라메터란 결과나

동작, 기능에 직접적인 영향을 미치는 요인이며, Level이란 이러한 파라메터의 성질,

특성, 속성 값을 의미한다. 효과적인 OA 적용을 위해서는 반드시 선행되어야 하는

것이 이러한 파라메터와 레벨에 대한 올바른 정의이다. , 각 파라메터가

독립적이어야 하며 두 개의 파라메터가 서로 영향을 받는 다면 그것은 두 개의

파라메터가 아니라 한 개 파라메터의 두 가지 레벨로 정의할 수 있을 것이다.


OA를 적용하고자 했을 경우 막연한 불안감이 있을 수 있으며, 개발팀이나 프로젝트

PM에게 이러한 방식의 테스팅을 설명했을 경우 그것이 합리적인가? 하는 의문을

제기 받을 수도 있다. 그렇기 때문에 왜 OA를 적용하는 방법이 합리적인가를 이해하는

것이 매우 중요하다고 할 수 있다. 이에 OA관해 언급한 다른 많은 사례들을 예로

들고자 한다.


"프로그램이 동작하는 방식에 따르면, 두 개의 파라메터 값에 의존해서 문제가

발생할 가능성이 매우 높다."


"여기서의 가정은 파라메터 간의 상호작용(조합)이 증가되면 될 수록 더 많은 버그가

발견될 것이라는 점이다."


"나는 12개의 파라메터가 서로 특정한 관계일 때만 발생하는 버그를 본 적이 없다."


"대부분의 버그는 파라메터 하나, 둘 또는 세 개가 동시에 어떠한 조합일 경우에만

발생한다."


"개발자가 자신이 작성한 코드를 테스트할 때, 한가지 파라메터에 대해서만 값을

넣어보는 테스트 하는 위험성이 존재하기 때문에, 우리는 각각의 입력 가능한 모든

조합에 대해서 테스트할 필요가 있다."


"사람들은 1) 개별 값이 잘못 들어 가거나 2) 값의 조합이 특정한 관계일 때 많은

버그가 발생한다는 사실을 인식하기 시작했다."


"대부분의 버그가 단 두개의 변수 조합을 통해 발견된다는 사실을 볼 때, 모든

Combination을 테스트할 필요가 없다."


결국 이 내용에서 이해할 수 있는 사실은 대부분의 프로그램은 하나, 두 개 또는

세 개의 조합의 파라메터를 통해서 대부분의 버그가 발견될 수 있으며, 따라서, OA

이용한 방법을 테스팅에 적용할 수 있다는 점이다. 그 이유는 OA의 특성에 기인한

것인데 "OA는 각각의 원소가 유일한 조합을 가진 모든 가능한 조합을 나열한 것"

이라는 특성이 있기 때문이다. 따라서, Pair 관계를 가진(두 개의 조합의 모든 나열

) OA 표에 테스트 파라메터와 레벨을 대입해서 나오는 테스트 케이스는 파라메터

두 개 씩의 관계가 유일한 케이스를 의미하는 것이다. 따라서, 대부분의 버그가

파라메터 두 개 이상의 관계일 때만 거의 발생하므로 OA를 이용한 테스트 케이스로도

대부분의 버그를 발견해 낼 수 있다는 의미로 이해할 수 있다.


이러한 OA를 테스트 케이스 작성에 이용했을때 얻을 수 있는 장점은 다음과 같다.


- 많은 조합(Combination)의 테스트 케이스 중에서 일부의 테스트 케이스를

효율적으로(더 적은 개수와, 버그가 발생할 가능성이 높은 케이스만을 추려낸) 선택할

수 있게 된다.


- 많은 조합의 테스트 케이스를 산출해 낼 경우 이 모든 케이스를 테스팅하기란

사실상 불가능하다. 따라서, 최적화된 케이스를 테스트할 경우 중도에 테스팅을

중단하지 않고 모든 파라메터 관계를 테스팅할 수 있게 됨으로서(케이스가

Combination 보다는 상대적으로 적으므로)결과적으로 테스팅 대상의 프로그램이 가진

모든 가능한 기능을 테스트 할 수 있게 해준다. 이러한 장점은 Test Coverage와도

관련이 있다고 볼 수 있다.


- OA를 이용한 테스트 케이스 생성은 파라메터와 레벨에 대한 정의만 올바르다면

누가 케이스를 생성하더라도 테스터 개인의 역량에 좌우되는 정도가 덜하며, 한 번에

모든 가능한 케이스를 디자인 할 수 있으므로 전체적인 작업 시간이 적게 드는

장점이 있다.


- OA를 이용하지 않은 기존 케이스에 대해서 기능 추가 및 변경으로 인한 케이스

재구성 시에 손쉽게 다시 케이스 생성이 가능해 진다.


4. OA 적용의 주의사항


많은 이들이 OA의 개념에 대해 잘못 이해하고 있는 사실중 하나는 OA를 이용해 산출된

케이스가 나름의 최적화된 방법을 통해 산출된 것이므로 이중 일부만 수행해도 되지

않겠는가 하는 의문이다. 하지만, 사실은 그렇지 않다. OA를 적용한 테스트 케이스는

통계적인 방법에 의해 추출된 것이고 각 케이스는 서로 간에 나름의 연관관계를

가지고 있어, 테스트 되는 케이스 자체가 생략된 케이스와 통계적으로 연결된 관계를 가지고 있기 때문에 OA를 이용한 케이스 중 일부를 생략하는 것은 생략한 몇 개의

케이스만 수행하지 않는 것이 아니라 이와 수학적으로 관계를 가진 케이스 모두를

수행하지 않는 것이다. 따라서, OA 케이스의 일부를 생략하는 것은 극히 위험한 상황으로 반드시 OA를 이용해 산출된 케이스는 모두 다 수행해야 한다.


아래 그림으로 왜 OA로 추린 케이스가 서로 간에 커버하는 관계를 가지는지

설명하겠다.


파라메터가 3이고 레벨이 2인 경우라고 가정하자.


(그림 2) 파라메터가 3이고 레벨이 2 CombinationOA


AB, BC, CA의 순서로 중복을 제거해 나가보자. 표에서 CombinationOA표를 비교해 보면 제거된 케이스 번호는 다음과 같다. (02), (03), (05), (08), (10), (11).

왼쪽의 (01)은 오른쪽에 이미 들어 있다. 왼쪽의 (02)는 왼쪽 리스트의 (01), (06), (04)에서 모든 두 원소간의 관계가 커버된다. 따라서, 오른쪽의 OA로 추린 케이스를 모두 실행할 것이기 때문에 (02) 케이스에 포함된 AB, BC, CA 두 개씩의 관계가 (01), (06), (04)에 이미 들어 있기 때문에 (01), (06), (04) 케이스만 수행하면 자동적으로 (02) 케이스를 검증한 셈이 된다.(왼쪽의 (06)은 오른쪽의 (03)과 같고, (04)는 오른쪽의 (02)와 같다.)잘 생각해 보자. 이상과 같은 내용이 Orthogonal Array를 이해하는 핵심이다.


마찬가지로 나머지 원소에 대해서도 대응되는 관계를 나타내면 다음과 같다.


(02) = (01), (06), (04)

(03) = (04), (07), (01)

(05) = (06), (01), (07)

(08) = (07), (04), (06)

(10) = (09), (06), (12)

(11) = (12), (07), (09)


좀 더 자세히 풀어 쓰면 다음과 같이 표현할 수도 있다.


(02) = AB1, 1의 관계인 다른 요소는 (01)이다. AB1,1 이기 때문이다. , (02)BC1,2인 관계는 (06)이다. 이런 식으로 (02)CA2,1인 관계는 (04)에서 발견할 수 있다.


왜 이런 관계가 되는지 이해가 되는가? 이러한 관계이기 때문에 OA를 이용한 케이스가

Combination의 케이스를 모두 커버하는 것이다.(역으로 얘기하면 OA 케이스로도 Combination을 수행했을 때 발견할 수 있는 파라메터 두 개 관계와 연관된 버그를 찾아 낼 수 있다.) 또한 이러한 관계이기 때문에 OA를 이용한 케이스 중 일부를 생략하게 되면 그것만 생략하는 것이 아닌 연관된 케이스 모두를 수행하지 않게 되는 것이다.


이제 또 다른 주의 사항에 대해서 알아보자.


아래와 같은 테스트 조건이 있다고 가정을 해보면 이 케이스를 OA로 추려낸

케이스에서는 (1, 2, 1)인 조합은 테스트할 수 없다.




A B C

===========================

1 1 1

2 2 2

3 3


====>


OA를 이용해 추린 케이스


1 1 2

1 2 2

1 3 1

2 1 2

2 2 1

2 3 2

3 1 1

3 2 2

3 3 2


(그림 3) OA를 이용해 검출될 수 없는 조합



, OA를 이용한 케이스 산출에도 검증할 수 없는 조합이 있을 수 있다는 의미이다.

따라서, 이런 경우에는 Pair가 아닌 Triple 조합을 적용한 케이스를 산출해야 하나

그렇게 되면 이 경우 OA를 이용해 추린 케이스가 Combination과 동일한 케이스 개수를

가지게 되므로 OA의 적용 의미가 없어져 버린다. 이 경우 지금까지의 경험과 노하우를

잘 살려서 임의의 케이스를 추가하는(OA 케이스 이외의) 방법이 최선이라고 할 수

있다.


OA를 적용하다 보면 파라메터와 레벨을 이용해 만든 표가 직각을 이루지 않는 경우도

있는데(그림 4), 이 경우 pair 관계의 OA 케이스를 만들어 보면 Don't care Value

볼 수 있다.


A B C Parings

========================================

(1) 1 1 1 3

(2) 1 2 2 3

(3) 2 1 2 3

(4) 2 2 1 3

(5) 3 3 1 3

(6) 3 1 2 2

(7) 1 3 2 2

(8) 2 3 ~1 1

(9) 3 2 ~1 1


(그림 4) - Allpairs를 이용해 생성한 결과중 ~가 붙은 원소


이 경우 ~가 붙은 원소의 경우 그것과 짝관계(pairing)에 있는 것들의 개수가 다른

원소에 비해 극히 적기 때문에 테스팅 할 때 이 틸드가 포함된 조합의 경우 유독

반드시 수행하는 것이 좋다. 이유는 언급했듯이 해당 하는 짝이 다른 것들 보다 더

적기 때문이다.(커버링의 개수가 적다.)


여기서 PairTriple이라는 얘기가 나왔는데, 기본적인 OA표는 두 개의 원소 간의

관계가 유일한 조합이라는 의미였으며 Triple인 표라는 의미는 세 개의 원소 간의

관계가 유일한 조합의 배열이라는 의미이다. 당연히 Pair 보다는 그 수가 많을

것이라는 점은 이미 눈치채셨으리라 믿는다.


이상과 같이 OA를 적용할 경우의 일반적인 주의 사항에 대해서 알아 보았는데, 특히나

조심해야 하는 몇 가지가 더 있는데 다음과 같다.


- 동치 분할, 경계값 분석 등의 기법으로 범위를 축소한 이후에 그 결과를 가지고

OA를 적용해 본다.


- OA를 이용해서 효과를 보기 위해서는 파라메터와 레벨을 선정하는 과정이 매우

중요하다. 따라서, 적절한 파라메터나 레벨을 찾는 과정은 해당 제품의 전문가나

제품 코드를 알고 있는 개발자 또는 다른 테스트 엔지니어의 조언이 필요하다.


- OA의 목적은 철저하게 테스트 하는 것이 아니라 테스트 케이스를 최소화 하는

것이기 때문에, Critical Application에 이를 적용하는 것은 법적인 문제와 여러 다른 문제를 야기할 소지가 있다. 적용 범위에 대해서는 OA의 위험성을 충분히 인지한 이후에 사용해야 한다.


- OA는 정해진 논리에 입각해 최적의 케이스를 추려주는 것으로 입력 파라메터와

레벨을 잘 선정하지 않으면 원하는 결과를 얻을 수 없다. 다시 한 번 강조하지만

파라메터와 레벨을 잘 선정하는 것이 중요하다.


5. OA 계산 툴 소개


- Jenny


- http://burtleburtle.net/bob/math/jenny.html


다음과 같은 장점을 가지고 있다.


- C로 작성되어 속도 빠름

- Pair Triple 방식의 계산 가능

- OA표만 생성하므로 별도의 치환 과정 필요

- 복잡한 계산의 경우 독특한 알고리즘으로 Allpairs보다 더 적은 케이스를 생성

- OA 공식 결과에 더해 일부 케이스를 수행하지 않은 경우의 위험도를 덜하게 하기

위해 부가적인 케이스를 추가


- Allpairs


- http://www.satisfice.com/tools.shtml


다음과 같은 장단점을 가지고 있다.


- PerlScript로 작성되어 복잡한 다차원 배열 계산에 시간이 걸림

- 파라미터 치환된 결과를 생성

- Pair 계산만 가능, Triple 계산은 못함


- ReduceArray2/3


- http://www.stsc.hill.af.mil/consulting/sw_testing/improvement/cst.html


- 필자의 경우 이 툴(MS 오피스 엑셀 매크로)을 동작시켜 보려 했으나 에러로 인해

동작할 수 없었다. , 엑셀에서 곧바로 케이스를 만들어 주기 때문에 관리하기

편리할 것이라는 예상은 가능하다.


지금까지 소개한 툴은 GPL이나 Public Domain 라이센스의 형태를 취하고 있기 때문에

자유롭게 사용할 수 있을 것이다. 이 이외에도 상용 툴이 존재하는데 상용 툴의 경우

독특한 알고리즘의 구조로 인해 복잡한 배열의 OA 계산의 경우 위에 언급한 툴에

비해 더 나은 결과를 보여 준다.


(그림 5) 상용 제품과 공개 툴의 수행 결과 비교(파라메터 20, 레벨 10의 경우)


T사의 A제품 Jenny Allpairs

180 194 230


6. 결론


지금까지 OA란 무엇이며 어떻게 소프트웨어 테스팅에 적용할 수 있는지 살펴 보았다.

전체적인 내용이 실제 테스트 케이스를 만들어 내는 과정을 설명하기 보다는 왜 그렇게

될 수 있는지에 대한 개념을 소개하고 특별히 주의해야 할 사항을 설명하였다.

이렇게 설명한 이유는 이미 STEN 2호에서 언급한 기고가 있기 때문에 그것을 참고하면

될 것이며, 툴을 이용해 케이스를 생성하는 방법은 매우 간단하기 때문이다. 오히려

이 방법을 개발 팀의 다른 인원에게 설명하고 이해시키는 것이 더 어려운 일이라고

생각한다. 이미 익숙해져 버린 방법을 대신한 다른 방법을 찾기는 매우 어려운 일이며

그것을 적용하는 것은 더욱 더 어려운 일이라는 생각이 든다. 마지막으로 OA

적용했을때 특히 효과를 볼 수 있는 영역에 대해 언급하면서 이 글을 마치도록 한다.

아무쪼록 이 글이 수 많은 이 땅의 테스트 엔지니어에 자그마한 도움이 되었으면

하는 바램이다. 여러분의 건투를 빈다!


(그림 6)


OA 적용이 적합한 테스트 유형


+ API Test

+ GUI Test

+ Configuration Test(SW, HW 호환성 테스트)


(참고 문헌)


1) Orthogonal Array: Applicability Software Product Testing by Selvam Ponnusamy

2) LITTLE BOOK OF TESTING by Norm Brown

3) Orthogonally Speaking by Elfriede Dustin

4) allpairs.rtf by James Bach

5) http://burtleburtle.net/bob/math/jenny.html

6) http://www.developsense.com/testing/PairwiseTesting.html

7) http://blogs.msdn.com/micahel/archive/2004/04/28/122702.aspx

8) http://blogs.msdn.com/nihitk/archive/2004/05/09/128821.aspx

9) Orthogonal Array의 소프트웨어 테스팅 적용 방법, 삼성 전자 정성원

댓글
  • 프로필사진 BlogIcon 오네테르 안녕하세요. 좋은글 잘 읽었습니다. 그런데 내용중에 의아한 사항이 있어서요. 중간에 (1,2,1)을 (1, 2, 1)을 테스트할 수 없다고 말씀하셨는데 아래 OA를 이용해 만들어진 TC에서는 (1,2,2), (1,3,1), (2,2,1)로 테스트 가능한거 아닌가요? 그리고 OA를 이용한 툴소개에서 All-Pairs를 소개해 주셨는데 All-Pairs는 좀 다른식으로 추출하는 개념으로 알고 있습니다. http://www.stickyminds.com/BetterSoftware/magazine.asp?fn=cifea
    여기 링크에 그 차이점을 잘 설명하고 있습니다. 참고하시고 의견부탁드립니다..^^
    2007.10.19 08:27 신고
  • 프로필사진 BlogIcon techbard 안녕하세요? 댓글 감사합니다. 제가 알고 있는 한도 내에서 설명을 드리면,
    1) (1, 2, 1)의 각 성분은 나머지 TC에 골고루 나누어져 있지만 꼭 (1, 2, 1) 순서인 것은 TC에 없기 때문입니다. All-Pairs나 OA에서는 성분 2개 끼리의 모든 조합이 반드시 포함되더라도, 가장 적은 케이스를 산출하는 것이 목적이기 때문입니다. 두 개 끼리의 성분이 반드시 포함되는 것으로 볼때는 (1, 2, 1)이 커버되는 것 같지만, 반드시 (1, 2, 1)의 순서에서만 버그가 발생한다면, 이 버그는 추출된 TC로도 발견하지 못한다는 얘기 입니다. ^^

    2) 맞습니다. All-Pairs와는 다릅니다. 다만, 실무에 있어서는 개념은 OA로 설명하는게 이해가 쉽고, 실제 업무는 All-Pairs를 많이 쓰기 때문에 같은 선상에서 설명 드렸습니다. 사실 추출하는 알고리즘이나 방법이 다르지만, 각 성분 두 개 씩의 유니크한 모든 조합이 포함되게 한다는 결론은 동일합니다. (적어도 제가 보기엔 ^^)

    답변이 되었는지 모르겠네요. 비가 세차게 오지만 좋은 하루 되세요. ^^
    2007.10.19 09:53 신고
  • 프로필사진 BlogIcon 오네테르 답변 감사합니다. 테스팅쪽 업무를 시작한지 얼마 안되어서 이것저것 공부해야할게 많더군요..^^ 틈틈히 들러서 읽고 있습니다. 좋은 내용 계속 부탁드릴께요 2007.10.22 16:24 신고
댓글쓰기 폼
공지사항
Total
394,963
Today
7
Yesterday
27
«   2018/11   »
        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  
글 보관함