태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.

블로그 이미지
눈 감고 둘러보니 종이의 바다라.
만두의전설

공지사항

글 보관함

calendar

      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    

'Past/computing'에 해당되는 글 4

  1. 2008.12.25 Java SE 접근 제한자 - 클래스 생성자 접근 제한에 관한 고찰 (5)
  2. 2008.11.17 WOC 2008 . (4)
  3. 2008.05.21 New authentication algorithem likes OTP (2)
  4. 2008.05.13 Sun / MySQL Launching Seminar (4)

Java SE 접근 제한자 - 클래스 생성자 접근 제한에 관한 고찰

난이도 : 하.

작성자 : 옥민수. (http://blindlibrary.tistory.com), (niceminsu@gmail.com)

Original Reference : http://blindlibrary.tistory.com/entry/Java-SE-접근-제한자-클래스-생성자-접근-제한에-관한-고찰




당신이 작성한 클래스 코드는 public으로 도배되어 있는가?

뛰어난 프로그래머는 public을 그다지 좋아하지 않는다. 왜냐하면 접근 가능하다는 것은 복잡도를 증가시키기 때문이다.

접근 제한이란 결국 어떻게 하면 조금 더 단순한 클래스 인터페이스를 만들 수 있을까 하는데서 나오지 않았나 생각된다.



생성자 접근 제한에 관해 이야기 하기 전 접근 제한자에 대한 기본적인 사항을 집고 넘어가자.

Java 접근 제한자에는 일반적으로 4가지가 있다.
  • public : 모든 곳에서 접근 가능.
  • protected : 상속받은 클래스와 동일 패키지 내부에서 접근 가능.
  • default :  동일 패키지.
  • private : 외부에서 접근 불가.

이 중에서 잘 쓰이지 않는 접근 제한자가 default 접근 제한자이다.

자바를 얕게 배운 사람이나 자바 개발 초보들은 default 접근 제한이 존재하는지 조차 모른다.

심지어 대학에서 자바 강의 시험을 볼 때 접근 제한의 종류를 묻는 문제가 나왔을 경우 public, protected, private 만 써도 정답 처리가 되는 상황이다.

또한 실제 코드상에는 버젓이 default 접근제한이 쓰이고 있으면서도 대부분 이 접근 제한을 private 과 동일하게 인식하고 만다.

하지만 default 접근 제한은 엄연히 private 접근 제한과 구별되며 private 접근 제한보다 느슨하다.

default 접근 제한. 바로 접근 제한필드를 비워둠으로서 선언하는 묵시적 접근 제한인 것이다.

default  접근 제한의 경우 접근 같은 패키지 내부의 참조만 허용하며 오직 클래스 내부에서만 접근할 수 있는 private과 구별된다.

한 가지 더 잊지 말아야 할 것은 protected 선언은 default선언을 포함한다는 것이다. 즉 protected로 선언되어 있다 하더라도 같은 패키지 내에서 접근하는 것이라면 굳이 상속받지 않아도 접근 가능하다.
 
하지만 이것은 코드분석을 어렵게 하는 요인이기도 하니 protected로 선언되어 있다면 같은 패키지 내부에 있다 하더라도 상속받아서 접근하도록 하자.



접근 제한이 선언되는 곳은 Class 본체 정의시 , 메소드 정의시, 멤버 변수 정의시, 그리고 생성자 정의시 에 선언된다.

Class 본체의 접근 제한자 선언은 클래스의 선언에 관계된다. 단, 클래스 접근 제한은 private과 protected를 선언할 경우 컴파일 에러를 일으키게 된다.

그 이유는 두 접근 제한자가 선언되었을 경우 논리적으로 모순된 상황에 빠지기 때문이다.

private class foo 로 선언되어 있다면 애초에 쓸 수 없는 클래스가 되니 말 할것도 없다. 다른 모든 곳에서 선언조차 안 되는 클래스를 어떻게 쓴단 말인가?

protected class foo 인 경우를 생각해 보자. 같은 패키지 내부에서라면 문제가 없겠지만, 다른 패키지에서 이 클래스를 상속받기 위해서는 이 클래스를 상속받는다고 선언되어야 한다.

즉, extends foo 가 되어야 하지만, foo를 상속받는다고 선언 하지 못하게 된다. 왜냐하면 아직 상속받지 못한 상태이므로  foo의 선언을 할 수가 없기 때문이다.

클래스 접근 제한이 선언이 아니라 생성에 제한이 걸린다면 protected class foo 는 가능할 것이다.
 
하지만 선언에 제한을 거는 자바 문법 구조상 상속 선언이 되어야 하기 때문에 다른 패키지의 클래스에서는 상속 받지 못하게 되므로 protected class는 쓸모가 없고 코드 모순을 발생시킬 우려가 있으므로 protected 접근 제한은 class 접근 제한으로 사용 할 수 없도록 자바 문법은 만들어 졌다.



이제 이번 포스팅의 메인 주제인 생성자 접근 제한에 대해 생각 해 보자.

class 접근 제한은 public 과 default만 가능했다. 그 이유로는 객체 선언에 접근 제한이 걸리기 때문이었다.

선언은 가능하되 객체 생성에 제한을 거는 방법. 그것이 바로 생성자 접근 제한이다. 또한 생성자 접근 제한을 이용해서 좀 더 다양한 형태의 프로그램 구조를 만들어 낼 수 있다.

또한 잊지 말아야 하는 것은 생성자 역시 다형성을 지원한다는 것이다.

먼저 private 생성자 를 살펴보자.

public class Foo{

    private Foo(){
    //any code
    }
}

와 같은 형태의 생성자다. inner class 만들어서 쓰는 걸 싫어한다면 유용할 지 모르겠다.

inner class를 만들게 되면 클래스가 하나 더 로드되니 그것보다는 private 생성자를 이용해 자가 객체를 만들어 어떻게서든 클래스를 하나라도 더 줄여보고자 한다면 쓸 수 있겠다.

필자는 private 생성자를 써 본 일 없으니 혹시라도 쓰면 좋은 상황이나 유용함을 알고 있다면 feedback 해 주길 바란다.

다음으로 default 생성자와 protected 생성자를 살펴보자.

public class Foo{

    Foo(){
    //any code
    }
}

public class Foo{

    protected Foo(){
    //any code
    }
}

와 같은 형태의 생성자다. 패키지 내부에서만 사용되는 기능이 있는 클래스라거나 패키지 내 최상위 상속 클래스라면 default 생성자를 정의해 주는 것이 프로그램 구조상 좋다.


이와 같은 상속 관계의 클래스 구조가 있을 경우 sample.master.MasterFoo  클래스의 생성자를 protected로 하기보다는 default로 하는 편이 좋다.

클래스가 아닌 생성자 접근 제한을 default로 할 경우 다른 패키지에서도 MasterFoo를 선언 할 수 있기 때문에 상속 상위 클래스 추상화를 잃어버리지 않는다는 장점이 있다.

패키지 내부와 외부의 기능 차이를 만들고 싶다면 역시 좋은 선택이다.

MasterFoo의 객체를 다른 패키지의 클래스에서 생성할 때 다른 기능을 하도록 만들 필요성이 있다면 생성자 오버로딩(overloading)을 이용해 public으로 정의 해 주면 된다.

MasterFoo는 쿵푸 고수다.

MasterFoo는 자신이 머무는 도장(sample.master)안에서는 사랑하는 두 제자에게 쿵푸를 전수해 주는 사부지만 도장 밖(other package)에서는 약자의 편에서 싸우는 영웅이다.

이 이야기에서 MasterFoo의 기능은 패키지에 따라 명확히 나뉜다. 사부와 영웅. 하지만 MasterFoo는 하나여야 한다.

protected로 도장 밖에 제자를 만들 일이 없다면 default 접근 제한 생성자로 해결된다고 생각하지 않는가?

반면에 도장 밖에 제자를 만드는 SubFoo1, SubFoo2는 protected생성자를 가지는 게 좋은 선택이라고 본다.

또한 protected 생성자의 경우 생성받은 클래스만이 생성 가능한 super()함수가 있기 때문에 상속 받은 클래스의 관점에서 부모 클래스의 역할을 정의 할 수 있다는 장점이 있다.

public 생성자는 특별히 언급하지 않더라도 모두가 잘 사용하고 있다.


결국 생성자 접근 제한은 default와 protected 접근 제한 사용에 무게가 실려 있다. 이 둘을 적절히 잘 사용한다면 객체지향 프로그래밍의 장점인 추상화와 정보 은닉을 더욱 잘 살릴 수 있을 것이다.

또한 프로그램 구조적으로도 조금 더 안정된 형태를 만들 수 있지 않을까 생각된다.
신고

'Past > computing' 카테고리의 다른 글

Java SE 접근 제한자 - 클래스 생성자 접근 제한에 관한 고찰  (5) 2008.12.25
WOC 2008 .  (4) 2008.11.17
New authentication algorithem likes OTP  (2) 2008.05.21
Sun / MySQL Launching Seminar  (4) 2008.05.13

WOC 2008 .

2008.11.17 16:11 | Posted by 만두의전설




개인적으로 너무 바람직하다고 생각되는 행사가 올해도 열린다.

작년 2회째 까지도 사실 비공개 행사가 아닌지 의심스러울 정도였는데

올해는 풍성하게 열린다고 하니 정말 기대해도 좋을 것 같다.

 공식 블로그 : http://woc.openmaru.com
신고

'Past > computing' 카테고리의 다른 글

Java SE 접근 제한자 - 클래스 생성자 접근 제한에 관한 고찰  (5) 2008.12.25
WOC 2008 .  (4) 2008.11.17
New authentication algorithem likes OTP  (2) 2008.05.21
Sun / MySQL Launching Seminar  (4) 2008.05.13

New authentication algorithem likes OTP

2008.05.21 21:54 | Posted by 만두의전설

알고리즘 : 옥민수
설계자 : 옥민수
코딩자 : 옥민수
기타등등 : 옥민수..
결국 원맨쇼..ㅋㅋ
e-mail : niceminsu@gmail.com
blog : blindlibrary.tistory.com




예전에 WOC 행사에서 이희승(멘토)님과 김참이(멘티)님께서 진행하신 OTP프로젝트를 보고

한번 만들어 볼까 하고 생각했었습니다. 왜 지금에서야 만드냐고 물으시면 슬픕니다,.

OTP의 장점인 인스턴스 패스워드는 좋긴 하지만 매우 불편합니다.

OTP생성기를 따로둬서 생성해야 하고 또 OTP생성기 역시 안전하다고 할 수는 없거든요..

그래서 기존의 방식대로 ID와 고정된 Password 만을 가지고 로그인 하는데 다만 전송되는 패스워드 메시지만

그때 그때 달라지는 방식을 구현해 봤습니다. 물론 전송되는 메시지에서 패스워드를 추출하는건 불가능합니다.

암호화가 아니라 패스워드 인증을 위해서 키를 생성하는거니 패스워드를 모르면 키에서 패스워드를 추출하는건

불가능합니다. 한마디로 복호화 할 필요가 없으니 추출 불가능하게 키를 생성해도 상관 없는거죠..

에에.. 나머지는 제가 그린 몇가지 그림과 코드를 훑어보시고 밑에 적을 설명서를 읽어보시면

잘 알게 되실겁니다. 코드이용에 관한 주의사항은 코드안에 들어있습니다. 한글로 되어있어요..

영어가 안되서..;;(현재 배우고 있..)

에.. 제가 이름붙이기로는 '로즈마리' 라고 이름을 붙였는데 아직은 가칭입니다. 혹은 로또같다고 해서

로또 프로젝트라고 붙여볼까도 생각중입니다. 만.. 만들었더니 필요없더라 하면..ㅋㅋ

일단 자바코드로 구현했고. 잘 돌아갑니다. 이클립스 프로젝트를 압축했습니다. 거의 대부분 자바 하시는

분들이 이클립스 쓰시니 불편함은 없을겁니다. IDE 딴거 쓰시거나 오로지 텍스트파일로.. 라고 하시면

코드 뽑아 쓰시면 되겠죠.. 에.. 테스트를 위해 메인 함수도 넣어놨는데 그냥 패스워드 받고 생성된

키를 출력해줄 뿐입니다.

그러면 설명 하겠습니다. 일단 그림을 봐주세요.

사용자 삽입 이미지
그냥 예전에 하던데로 쓰면 된다는걸 표현하고 있습니다.(알아요. 그림 못그리는거) 특히 웹에서 강력할 수 있습니다.


사용자 삽입 이미지

알고리즘이 대강 어떻게 흘러가는가를 표현하고 있습니다. 여기 안 들어 있지만 위에것들외에 추가로 몇개가 들어가 있습니다.


사용자 삽입 이미지



역시 서버와 유저 사이에 일어나는 일을 그리고 있습니다.

에.. 그러면 본격적으로 설명을 하겠습니다.
정확한 건 코드를 참고해 주셔도 감사하겠습니다.

아. 비트에 거부감을 가지시면 못 보실 수 도 있습니다. 또한 비트 논리연산에 거부감을 가지시는 분들도 보기 힘드실 수 있습니다.

기본적으로 유니코드를 기반으로 하고 있습니다. 아스키를 써도 되요,.. 그러면 최소 1자리에서 최대 60 자리가 되겠지요..

유니코드를 기반으로 하고 있기 때문에 거의 모든 연산은 2byte단위로 일어납니다. 알아두세요. 참고로 자바에서 char가 2byte 이기 때문에 유니코드를 기반으로 한건 아닙니다.(아니에요.. 절대 아니에요..)

먼저 패스워드는 1자 이상 30자 이하여야 합니다 혹은 최대 31자 까지 할 수 있습니다.(30자인 이유는 나중에 나옵니다.)

먼저 서버에서 공개용 키를 생성하는 방법인데요. 사실은 상관없을것 같습니다만 저는 제 나름대로 값을 크게 불리기 위해서 난수를 두번 생성합니다.
 
16비트중에서 처음 3비트 따로 난수를 생성하고 그 다음에 나머지 13비트의 난수를 생성해서 서로 OR연산을 합니다. 이때 앞의 3비트의 값이 0이 되지 않도록 합니다. 그러면 큰 값이 생성되게 됩니다. 지금와서 생각해 보니 왜 그랬을까 하는 생각도 듭니다. 뭔가 만들기 시작할 당시에는 이유가 있었던거 같은데.//.

이 값을 클라이언트에 전송합니다. 서버에서는 개별 클라이언트를 따로 인식할 수 있는 공간이면 어디건 값을 저장해도 괜찮습니다. 혹은 클라이언트에서 전송되어저 오는 값에 난수를 포함해도 상관없습니다. 어차피 공개하는 값이니까요.

그러면 클라이언트는 이 값을 받아서 유저로 부터 받은 오리지널 패스워드와 서버로부터 받은 2byte의 값을 가지고 패스워드 대체 키를 만들어 냅니다. 총 16byte의 일회용 키가 생성됩니다.

지금부터 이 값을 어떻게 생성하는지 설명하겠습니다.

먼저 패스워드 각 캐릭터(2byte)의 비트를 재 정렬 합니다. 저는 앞에서부터 홀수비트는 왼쪽 짝수비트는 오른쪽으로 정렬했습니다. 왜 이렇개 해야 하냐면 영어를 예로 들면 아스키코드를 그대로 씁니다. 제가 알고 있기로 유니코드는 하위 호환을 위해 아스키코드에다 앞의 비트를 붙인겁니다. 틀리다면 열심히 지적해 주세요.
 
결국 숫자가 작아지는 결과를 초래합니다. 또한 수치의 범위가 정해져 버립니다.  특정 언어만 쓴다고 가정하거나 기존의 패스워드가 아스키 방식이니 기존걸 그대로 쓴다고 가정하면 수치 범위가 정해지는 약점과 앞의 8bit가 거의 0이 되어버리는 현상이 발생할 수 있습니다. 그걸 방지하고자 비트를 분산시킵니다.

이렇게 비트를 바꿔놓은 후에는 이제 패스워드의 입력 순서를 검증해야 합니다. '바보나라'와 '보바나라'가 같을 수 없지 않습니까.. 이건 각 캐릭터를 XOR 하는것으로 해결합니다. 물론 XOR만 하면 안됩니다. XOR연산 결과에 다음 캐릭터를 더해줍니다. 그리고 다시 XOR합니다. x = pw[0]; x += pw[1]; x ^= pw[1]; 이런식으로 구현했습니다. 이러면 패스워드 순서를 보장해 줄 수 있는 2byte값이 생성됩니다. 이 값에다가 마지막으로 서버에서 받은 키값을 XOR헤줍니다./. 이것 역시 덧셈 포함해서요..

이제 실제 전송용 코드를 생성해 내는 방법을 설명 드립니다.

기본 원리는 이렇습니다.(X = 앞서 얻은 순서검증 값, Ax = 패스워드, Sx =  계산되어 나온 값)

          A1     A2        A3        A4        A5        A6  ···
xor      X    S1+A1   S2+A2   S3+A3   S4+A4   S5+A5 ···
───────────────────────────
          S1     S2        S3        S4        S5        S6

이게 기본 원리입니다.. 아리송 하시죠? 이걸가지고 키를 만든다고해서 키를 추출 못할리 없기때문입니다.

몇번 키 캡쳐해서 경우의수 연산하면 패스워드를 특정할 수 있을겁니다.

하지만 이건 말 그대로 기본원리이고,, 여기에 두가지 정도가 더 들어가서 추출 불가능하게 만들어 집니다.
에.. 어떻게 하냐하면.. 루프와 추가를 합니다. 에에.. 바뀐게 이렇습니다. A6까지만 있다고 가정하고

loop{
          A1(S1)      A2        A3        A4        A5        A6                
xor      X(S7+X)  S1+A1   S2+A2   S3+A3   S4+A4   S5+A5   S1+S6   ···
────────────────────────────────
             S1         S2        S3        S4        S5        S6        S7
}

이런식으로 루프할때마다 값이 추가가 됩니다. 첫째 자리의 괄호안에 있는 건 두번째 이상의 루프타임시 연산되는 거고요.. 이제 감이 좀 오실겁니다. 이런식으로 됩니다. 하나더 설명해야 할게 있는데 루프를 돌때마다 추가되는 자리의 연산에 쓰이는 앞의 연산결과의 자리가 한 자리씩 증가한다는겁니다. 2회차때는 S2+S7 같이요. 자세한건 코드를 참고하시면 됩니다.

이제 남은건 배열크기 정하는거와 루프횟수와 16byte를 채우는 방법입니다. 여기서 패스워드가 30자리이햐여야 하는 이유가 나옵니다. 루프횟수는 1792~2040 안에서 돌게됩니다. 총 11비트를 쓰게 되고  너무 많이 돌면 배열공간 낭비에 연산시간도 길어질거 같아 범위를 이렇게 정했습니다. 혹 불안해서 좀 더 돌려야 겠다는 분들은 앞에 남는 5bit를조정하시면 되겠지요..

그리고 뒤에 3비트를 111로 고정했습니다. 그러니까 0000011100000000 이 되는거죠.

에.. 그리고 이 루프횟수 산정과정에서 8로 나누어 떨어지는 값이 나와야 하는데 이걸 비트 끝 세자리를 0으로 채우게 했습니다. 그러니까 가운데 남는 비트수는 5개가 되는겁니다. 2의 5승은 32고 0을 빼면 31입니다.

이 5비트에 패스워드 자리수를 채워넣습니다. 1 ~ 30까지이겠지요. 31까지 해도 상관없습니다. 1차이..;;
여기서 나오는 11자리의 비트값이 배열의 크기와 루프의 횟수가 되겠습니다. 루프한계의 카운터가 증가해서 결국에 11자리의 값과 같아지는 시점에서 끝나게 되어있습니다. 말로 설명드리기 어려우므로 코드를 참고하세요.

이렇게 배열이 알 수 없는 값으로 채워지게 됩니다. 이제 16byte를 채우는 일이 남았는데요. 8개의 값을 선택해서 채워 넣습니다. 배열 전체의 값을 이용해 8개의 숫자를 구할수도 있겠습니다만. 그냥 하나씩 찍는게 편합니다. 여기에서 11bit 숫자를 구할때 뒤의 3bit를 0으로 채운 이유가 나옵니다. 8로 떨어지는 숫자가 나오니 배열크기 를 8로 나누어 나오는 값만큼 카운터 해서 8개 구하면 8개의 숫자를 집어낼 수 있습니다. 역시 자세한건 코드를 보시면 금방 이해하실 수 있습니다. 이러한 연유로 로또 프로젝트라고 불러볼까도 생각중입니다. 8개의 숫자찍기..;; 로또..흐음,,

이렇게 하여 대망의 128bit의 숫자가 나왔습니다. 이제 클라이언트는 ID와 계산되어진 128bit를 서버에 전송합니다. 그러면 서버는 DB에서 ID를 검색해서 얻은 패스워드와 클라이언트에 전송한 키를 가지고 앞의 연산을 다시 합니다. 그렇게 얻은 키와 클라이언트에서 전송되어진 키가 동일한지 검사해보면 됩니다. 그리고 한번 인증된 난수값은 DB에 저장해서 다시 그 난수값을 사용하지 않으면 됩니다. 그러면 같은 값이 전송되어 오는걸 볼 일은 거의 없어지겠지요.

또한, 코드를 열어보시면 알겠지만 코드를 수정해서 혼자쓰시는거면 상관없으십니다만, 배포는 안되십니다. 수정사항이 발생했을경우에는 제게 연락해 주시면 감사하겠습니다.

에에.. 마지막으로 이 글의 내용과 같은 내용의 글을 게시, 게재 하는것은 안되십니다. 링크를 넣어주세요. 어차피 tistory 서비스를 이용하는거라 트래픽 많이 걸려도 돈 제가 안 내는 겁니다.ㅋ
신고

'Past > computing' 카테고리의 다른 글

Java SE 접근 제한자 - 클래스 생성자 접근 제한에 관한 고찰  (5) 2008.12.25
WOC 2008 .  (4) 2008.11.17
New authentication algorithem likes OTP  (2) 2008.05.21
Sun / MySQL Launching Seminar  (4) 2008.05.13

Sun / MySQL Launching Seminar

2008.05.13 12:50 | Posted by 만두의전설

 4월 29일 화요일 Sun사가 MySQL사를 인수한 기념으로 세미나를 개최했다..

 에에.. 다른게 아니라 티가 가지고 싶어서 참석했다..;;;
(http://www.mysqlkorea.co.kr/gnuboard4/bbs/board.php?bo_table=community_01&wr_id=195)

 그런겁니다.. 어차피 속물이라능..;;;

 (위에 링크 따라 들어가시면 어떤 주제로 세미나가 이루어 졌는지 알 수 있어요.)

 영어로 진행된 세미나는 동시통역이 제공되었지만, 그날 귀가 않좋아서.. 그냥 들었다..;;;  

 한가지 아쉬웠던 점은 개발자들이 많이 참석하지 않았다는 거고,ㅡ, 나 같은 학생들도 많이 참석하지 않았다는 것이다..

 가장 흥미로웠던 주제는 역시 Colin Charles씨의 성능튜닝 벤치마킹이었다.. 여러가지 화제가 된 MySQL의 기술적 이슈들에 대해서 들을 수 있었고 또 그에대한 답변도..ㅋ

 뜬금없이 AMD 사에서 자사 CPU를 광고하기도 하셨는데.. 시장은 아직 AMD가 내놓은 새로운 쿼드코어에 대해 관망적인 입장이니.. 하지만 발열에 관한 이야기는 솔깃했다.

 끝나고 칵테일 리셉션이 있었는데.. 맛난게 많았다.. 하지만 저녁약속이 있었던 관계로..

사용자 삽입 이미지


 가운데 키위 쥬스 비스무리 한거 한개만 냠냠.. 정말 맛난거 많았는데.. 휴,,

 에., 또 인상깊은건 얼음조각이랄까..
사용자 삽입 이미지
사용자 삽입 이미지

 얼음조각이 굉장히 인상깊었다랄까..

 아.. 티셔츠에 관한 이야기를 해야겠는데.. 그냥 줄서서 주는데로 받긴했는데.. 이거 라지사이즈다.. 그것도 일반 라지사이즈보다 크다..;;

 너무 커... 그래도 돌고래 그림이 그려진 티셔츠는 많이 없으니까 만족. 만족.

 아.. 마지막으로 지금에서야 포스티이 하는건.. 정신이 없었다랄까..;;
신고

'Past > computing' 카테고리의 다른 글

Java SE 접근 제한자 - 클래스 생성자 접근 제한에 관한 고찰  (5) 2008.12.25
WOC 2008 .  (4) 2008.11.17
New authentication algorithem likes OTP  (2) 2008.05.21
Sun / MySQL Launching Seminar  (4) 2008.05.13
이전 1 다음