login register Sysop! about ME  
qrcode
    최초 작성일 :    2004년 06월 21일
  최종 수정일 :    2004년 06월 21일
  작성자 :    taeyo
  편집자 :    Taeyo (김 태영)
  읽음수 :    27,914

강좌 목록으로 돌아가기

필자의 잡담~

조금씩 ASP.NET의 내부속으로 파고 들어가고 싶어지네요... 그만큼 ASP.NET에 익숙해진 것이 아닐까 싶습니다.

대상 : 200 - 300 *
분류 : ASP.NET & ViewState
독자층 : 이미 ASP.NET ViewState에 익숙하신 분. C# 언어에 익숙하신 분
* (100 :초급, 200 : 중급, 300 : 고급)

많은 ASP.NET 개발자들이 이제 ViewState라는 것에 익숙해져 있을 것입니다. 또한, 알게 모르게 상당히 유용하게 ViewState를 사용하고 계시기도 할 것이구 말입니다.^^

ViewState 문자열은 BASE64로 인코딩 되어져 있다는 것도 알고 계실 것이고... 그것을 디코딩하기 위해서 제 사이트의 별도 페이지를 이용해 보시기도 하셨을 겁니다. ^^ 바로 이것이지요!!!!  두둥!

http://www.taeyo.net/base64.htm

그게 뭐야~~~ 라고 하셨죠??? 그렇습니다. 사실, Base64 디코딩하는 로직은 인터넷에 널리 퍼져있는 것이라 그리 어렵지 않습니다. 제가 한 일이라곤, 그 로직을 구해서 자바스크립트로 옮겨둔 것 뿐이죠... 쩝. 지나치게 예리하신 덕분에.. 제가 조금 의기소침해 져버렸슴다!

다시 한번 맞장구치지만, 그렇습니다!!! 사실, 위의 페이지를 통해서 볼 수 있는 결과는 단순히 BASE64로 인코딩되어져 있는 문자열을 단순히 디코딩해서 보여주는 것일 뿐입니다. 인코딩되어져 있던 데이터보다는 디코딩되어져 풀어진 데이터가 보기에는 용이할지 모르겠지만, 여전히 디코딩된 데이터를 봐도 이게 무슨 데이터인지 잘 모르겠는 것은 여전할 것입니다. 그렇지 않나요??? 설마, 이해가 쏙쏙 된다는 분이 계신 것은 아니죠? 그렇다면, 당신은.. 인간 분석기!!!! 존경과 의심을 그대에게~~~

디코딩된 데이터, 즉, 풀어서 표현한 데이터를 봐도 이해하기가 어려운 것은, 그도 그럴것이, 디코딩 된 데이터도 어떤 규칙과 이니셜에 따라 데이터가 축약되어져 있기 때문이지요. 예를 들면, 다음 문자열은 일반적인 뷰상태(ViewState) 문자열이구요.

dDwtNTMwNzcxMzI0O3Q8cDxsPFlvdXJOYW1lO1lvdXJBZ2U7RmF2b3I7PjtsPFF1ZWVueTszMjto
PE5PMztXSU4zMjtOTzI7QyM7Tk8xO0FTUC5ORVQ7Pjs+Pjs7Pjs+

다음은 위의 데이터를 BASE64 디코딩한 문자열입니다.

t<-530771324;t<p<l<YourName;YourAge;Favor;>;l<Queeny;32;h<NO3;WIN32;NO2;C#;NO1;
ASP.NET;>;>>;;>;>

물론, 인코딩인 상태였을 때보다는 데이터의 내용을 확인하기가 쉬운 편이긴 하지만, 그래도 여전히 그러한 데이터들이 어떻게 사용되고 있는 것인지 이해하기 에는 어려움이 남아있어 보입니다. 그렇지 않나요?

이에 대한 이야기는 유명 베스트셀러(?)인 Taeyo's ASP.NET with C#에서도 다루었었 드랬습니다?? 하지만, 거기서는 단지 디코딩하고 나면 데이터가 보기에 더 쉬워진다고만 언급했었을 겁니다. 그렇게 얼버무렸던 것이 사실이지요.. 왜? 초급책에서 너무 깊게 특정 부분을 파고 들어가는 것은 여러모로 책의 기획과 맞지 않았기 때문입니다. (에... 또.... 실은 뭐.. 게으름 및 지식부재의 탓도 있긴 했습니다만.. ㅠㅠ)

해서, 이것을 좀 더 구체적으로 분석할 수 있는 방법은 없을까하여~ 한번 인터넷과 MSDN을 뒤져보며 알아보았지요... 이쪽과 관련한 정보를 찾기가 그리 쉽지는 않더라구요...  한번 찾아보세요!! 쉽지 않을걸요~~~

하지만, 지성이면 감천이고, 박~ 지성이면 골인이라고 그랬듯이, 열심히 찾아 헤메이다 보니, 이쪽에 대해서 아주 자세하게 까지는 아니지만, 대략적인 정보는 얻어낼 수 있었습니다. ViewState안에 들어있는 데이터들의 구조와 형식에 대한 힌트 및, 그 데이터를 Parsing 하는 방법에 대한 힌트를 말이지요 ^^

그렇다면, 그 내용을 정리해서 한번 이야기를 진행해 볼까요? 근데, 이렇게 어렵게 알아낸 것을 쉽게 풀어서 강좌로 제공하려 하니, 갑자기 가슴 한켠이 시려오면서, 뭔가 노력한 티를 내고 싶어지는 기분이 들면셔~~ 갑자기, 이 강좌의 파일명을 다음과같이 주고 싶다는 욕망이 불끔 솟아오르는 이유는 무엇일까요???

"끝내주는뷰상태이야기진짜남주기아까움초강추.avi"

당나귀에 자주 등장하는 표현을 패러디해 보았습니다. -_-;;;;;  엇??? 그건 성인용동영상 파일에 주로 쓰이는 이름이라구요??? 혹시, 태오도 그런 거 골라서 보는 뵨태성향의 남아인거냐구요???  헉??  그랬습니까? 저는 그 영상을 본적은 없구요. 단지, 제목이 너무 웃겨서 보고 웃었던 기억이 있을 뿐입니다. 그런 파일을 다운로드 받아서 미디어 플레이어로 실행하거나한 적은 결단코 없어요 -_-;;;  으음... 웃겨 볼라구 이야기 꺼냈다가 오히려 본전도 못 찾아 먹은듯한 플레이였네요. 죄송스럽습니다.

일단, 다시 본래의 강좌로 돌아가서.. 이야기를 계속 드리면~~~

일단, 뷰상태는 Triplet라는 개체를 사용하여 그 루트가 구성이 됩니다. 참고로, Triplet라는 개체는 .NET 프레임워크에서 제공하는 개체이구요. VS.NET 도움말이나 MSDN을 찾아보시면 알겠지만, 이 개체는 ASP.NET 서버 컨트롤의 뷰 상태에 함께 추가할 수 있는 세 개체를 보유하고 있는 개체입니다.말이 좀 어려운데요. 간단하게 이야기 하면, 이 개체는

First
Second
Third

라고 하는 3 개의 속성을 통해서 별도의 3개의 개체를 담을 수 있는 하나의 개체라는 이야기입니다. 그리고, 추가적으로 이 개체는 ASP.NET ViewState에 추가가 가능한 개체라는 것이죠. 기본적으로 ASP.NET ViewState 내부에서는 특정 단일 개체를 사용할 수 있는데요(모든 개체를 다 사용할 수 있는 것은 아니구요. 사용할 수 있는 개체가 정해져 있습니다).

만일, 3개의 내부 개체를 포함하는 단일 개체가 필요한 경우가 있다면, 그 경우, 유용하게 사용될 수 있는 개체가 바로 Triplet 라는 것입니다. 단일개체이지만, 내부적으로는 3개의 개체를 보유할 수 있는 기능을 제공하는 개체가 Triplet 이니까요 ^^

이와 유사하게 2개의 별도 개체를 담을 수 있는 기능을 제공하는 또 다른 녀석도 있는데요. 그 녀석의 이름은 Pair입니다. MSDN의 Pair 정의를 보면 다음과 같이 설명하고 있네요

"ASP.NET 서버 컨트롤의 뷰 상태에 함께 추가할 수 있는 두 개체를 보유합니다"

풀어서 이야기하면, 두 개체를 보유할 수 있는 단일 개체이며, ASP.NET 뷰상태에 추가가 가능한 특별한 개체라는 것입니다. ^^ Triplet과 마찬가지로 말입니다. ^^

이러한 특별한 개체들 외에도 ArrayList, Array, 기본 데이터 형식들 등이 ViewState에서 사용이 가능합니다. ^^

이해를 돕기 위해서, ViewState의 내부적인 개체 구조의 예를 한번 들어보도록 하겠습니다. 일단, 다음과 같이 어떤 aspx 페이지에서 뷰상태에 데이터를 추가해 두었다고 가정해 보겠습니다.

    private void Page_Load(object sender, System.EventArgs e)
    {
        // 여기에 사용자 코드를 배치하여 페이지를 초기화합니다.

        if(!IsPostBack)
        {
            Hashtable h = new Hashtable();
            h.Add("NO1", "ASP.NET");
            h.Add("NO2", "C#");
            h.Add("NO3", "WIN32");

            ViewState["YourName"] = "Queeny";
            ViewState["YourAge"] = "32";
            ViewState["Favor"] = h;
        }
    }

다음은 위와 같은 경우에 그 aspx 페이지의 ViewState를 개체의 구성으로 표현해 본 모습입니다. 뷰 상태가 내부적으로 triplet, Pair, ArrayList 등의 개체들을 사용하고 있는 것을 볼 수 있구요. 'YourName'이나 'YourAge'와 같은 데이터 값이 뷰 상태 안에 들어있는 것을 확인해 볼 수가 있습니다.

    Triplet
        System.String
        Triplet
            Pair
                ArrayList
                    'YourName'
                    'YourAge'
                    'Favor'
                ArrayList
                    'Queeny'
                    '32'
                    'System.Collections.Hashtable'
            ArrayList
            Null
        Null

개체 구조의 포함관계를 좀 더 이해하기 쉽게 하기 위해서 조금 위의 모습을 다듬어 보았습니다. 어떤가요? 보기에 더 편하죠? Triplet은 자신의 하위로 3개의 내부개체를 보유하고 있는 것을 보실 수 있을 것이구요. Pair는 2개를 보유하고 있는 것을 보실 수 있을 것입니다. ^^

이게 무슨 의미가 있느냐고 물으신다면 할말은 없습니다만... 나중에 여러분이 ViewState 파싱 프로그램을 만들게 될 때, 이러한 구조를 머리속으로 그리고 있느냐, 그렇지 못하냐에 따라 이해도에 상당한 차이가 있을 수 있을 겁니다. 저의 경우도 이러한 구조를 미처 알지못한 상태에서 파싱 소스를 접하게 되어 상당한 스팀을 받았던 기억이 있습니다.. ㅠㅠ

몇 시간 동안 입으로 툴툴 거리면서, 주변 사람들에게 민폐를 끼친 것 같다는.... -_-;;;

ViewState의 데이터를 분석해내는 방법은 사실 논리적으로는 간단합니다. 최초의 Triplet을 우선적으로 접근해서, 그 안에 있는 각각의 3개의 개체가 어떤 것인지를 알아냅니다. 만일, 그 값이 일반적인 기본 데이터형식이라면 그 값을 출력하구요. 만일, 개체가 Pair이나 ArrayList라면 다시 그 개체 내부안에 있는 개체로 파고 들어가서, 그 안의 내부개체는 또 무엇인지 알아봅니다. 그 안의 개체가 만일 기본 데이터 형식이라면 그 값을 출력하구요. 그렇지 않고, Pair이나 Triplet 혹은 기타 배열형식이라면 다시 또 그 내부로 파고 들어가 봅니다.

이런 식의 접근을 재귀적인 접근이라고 이야기하죠? 일명 무한함수. 재귀함수를 만들어서 계속적으로 반복해서 자신의 내부로 접근해 들어가 보는 것이죠~~  프로그래밍 언어를 하나 이상 해보셨다면, 분명 재귀함수나 재귀호출(순환호출)은 한번쯤 만나보셨을 겁니다. ^^. 바로 여기서도 그 방법이 필요합니다.!!

저의 경우, 인터넷을 통해 공개되어져 있는 여러 외국 개발자들의 소스를 조금 변경해서 나름대로의 소스를 만들어 보았는데요. 아무리 제가 작성한 소스라 할지라도, 구체적인 소스 설명까지는 어려울 것 같아요. 왜냐하면, 사실, 설명이 그리 쉽지는 않은 편이어서요. 하지만, 재귀호출을 이해하시는 분이라면 대략적인 코드를 보시면 그 로직을 이해하실 수 있을 것입니다.

그리고, 인터넷을 통해 공개한 여러 외국 개발자들이 짠 소스를 봐도 아주 완전하게 작성된 것은 없는 것 같더라구요. 수많은 case가 있을 수 있어서 그런지는 몰라도 조금은 각각의 소스들이 제한적인 결과만을 보여주더라구요. 그렇다면, 일단, 진행에 앞서 공부에 도움이 되는 외국 개발자분들의 강좌와 컬럼, 소스에 대한 정보를 먼저 알려드리겠습니다.

제 소스를 보시기 전에, 이 컬럼들과 소스들을 먼저 보시고, 테스트 해보시고 하시면 더욱 더 도움이 될 것임을 믿어의심치 않습니다. ^^

우선, 다음 컬럼은 짧지만 매우 강력한 힌트들을 제공하는 글입니다. 개인적으로 반드시 읽어봐야 한다고 생각하는 글이지요... 번역을 해달라구요??? -_-+++  그것은 죄송하지만, 어렵겠습니다...  영어라고 무조건 건너뛰려는 자세는 가급적 버려주세요. 이렇게 좋은 정보를 단지 작성된 글이 영어라는 이유만으로 건너뛰어 버리면, 남들보다 더 밀려나게 될 수 밖에 없답니다. 영어가 이해 안됨 코드를 보시면 되구요. 코드가 조금 이해되기 시작하면 영어도 이해되기 시작합니다. 한시간 정도 이상은 그 글에 성의를 다해보려는 노력만 있다면 전혀 문제될 것은 없을 거예요~~~

초강추!! ViewState: All You Wanted to Know (Paul Wilson)

다음 링크는 윗 컬럼을 제공하신 Paul Wilson 씨의 개인 사이트로 이 역시도 필독 사이트입니다요~~ 그러고 보니, Paul Wilson씨는 상당히 유명한 개발자인듯 하네요.

http://www.wilsondotnet.com/
http://authors.aspalliance.com/PaulWilson/

사진으로 봐서는 그다지 대단해보이지 않는 평범한 얼굴을 하고 있는데요. 내공은 상당한 듯 합니다. ^^ 위의 사이트에 가시면 지금 제가 제공할 ViewStateParser와 흡사한 데모 페이지와 그 페이지의 소스 코드를 구하실 수 있을 겁니다. 데모 페이지와 소스의 바로가기 경로는 각각 다음과 같습니다.

데모 http://www.wilsondotnet.com/Demos/ViewState.aspx
소스 http://www.wilsondotnet.com/Code/?path=Demos&file=ViewState.aspx.cs

그리고, 다음 컬럼 또한 영어이지만, MSDN 에서 제공하는가장 최근에 올라온 ViewState를 완전 해부한 컬럼글입니다 ViewState에 대한 모든 내용을 깔끔하게 정리해서 제공하는 컬럼이기도 하지요. 제가 언급한 개발자들도 다 나오고 있네요..  가장 최근에 올라온 글이니 이 글도 꼭 정독해 보시기 바랍니다....(그래놓고, 막상 태오는 건너뛰면서 읽었다는... -_-;)

MSDN Understanding ASP.NET View State

그리고, 또 하나의 유용한 소스를 제공하는 개발자도 알려드려야 겠는데요. 이 분은 .NET 개발자가 반드시 보유해야만 하는 .NET 관련 도구중 하나인 ASP.NET Version Switcher를 개발, 무료 배포하고 있는 Denis Bauer 이라는 분입니다. 그 분의 사이트를 가보면, ViewStateViewer라는 재미난 도구를 만나볼 수 있는데요. 이것이 지금 제가 진행하고 있는 강좌와 매우 부합하는 흥미로운 도구입니다. 게다가, 더욱 흥미로운 것은 그 컨트롤에 대한 소스코드(C#)까지 제공한다는점이지요.

http://www.denisbauer.com/ASPNETControls.aspx

여기는 꼭 한번 가보도록 하세요. 비록 소스에 대한 설명은 없지만, 그 소스를 가지고 이리저리 건드리다보면나름대로 상당한 도움이 될 것입니다. 자료가 없을 때는 돌아가는 소스 하나만 구해도 얼마나 큰 도움이 되는지 고생을 해보신 분들은 다 아실 겁니다. ㅠㅠ. 감격해서 눈물까지 흘리곤 하기도 하지요~~~~핫핫핫

마지막으로, view state decoder WinForms application 를 제공하고 있는 Fritz Onion라는 분의 사이트입니다. 꽤 잘생긴 외모를 갖추고 있는데요. 이 사이트에서는 .NET 버전별로 ViewState Decoder를 제공하고 있습니다. 성급하게도 .NET 2.0 버전까지 벌써 제공하고 있네요. 아이~~ 재간둥이 같으니라구~~

참고할 리소스가 많죠???  그렇습니다. 불과 몇 달 사이에 많은 리소스가 등장해버렸습니다.

"아.. 너무 많아서 보기가 귀찮다~~ 태오가 알아서 정리해 주겠지???"

라고 생각하고 계시다면, 죄송하게도 그것은 어려울 것 같습니다. 우선적으로, 저도 완전하게 이해하고 있지 못하기에 그것을 정리해서 설명드린다는 것은 어려울 것 같구요. 이러한 지식을 위해서는 제 강좌로만 만족할 것이 아니라 많은 내공만빵 미국 개발자들의 소스와 접근법을 직접 느끼셨으면 하기에 그렇게 해서는 안될 것 같습니다. 이번 강좌는 초급이라기 보다는 중, 고급에 가까우니까요...

많아 보이는 이러한 리소스도 사실 살펴보다 보면 그다지 충분하지가 않다는 것을 느끼실 수가 있을 겁니다.(욕심은 끝이 없죠~~ 하하) 하지만, 리소스가 전혀 없었을 때를 생각하면 이것은 대단한 것입니다. 아무것도 구할 수 없을 때는 단 하나의 리소스만 있어도 감동을 합니다. 단 한줄의 코드만 얻을 수 있어도 하루를 충분히 희생할 값어치가 있지요!!  ㅠㅠ  그 기분 아시죠????

자. 다음의 코드는 제가 위에서 나열한 여러 개발자들이 공개한 소스를 조금 편집(!!)하여 만들어 본 코드입니다.(Special Thanks to Paul Wilson & Denis Bauer & Fritz Onion) 보여드리는 코드는 코드 비하인드쪽만 보여드리겠습니다만, 파일자체는 다운로드 가능하게 밑에 링크를 걸어두도록 하겠습니다.

private void Page_Load(object sender, System.EventArgs e)
{
    // 여기에 사용자 코드를 배치하여 페이지를 초기화합니다.
    if(!IsPostBack)
    {
        // 테스트를 위해서 약간의 데이터를 뷰상태에 추가합니다
        Hashtable h = new Hashtable();
        h.Add("NO1", "ASP.NET");
        h.Add("NO2", "C#");
        h.Add("NO3", "WIN32");

        ViewState["YourName"] = "Queeny";
        ViewState["YourAge"] = "32";
        ViewState["Favor"] = h;
    }
}

private void WriteData(string data)
{
    lblData.Text +=data + "<BR>";
}

private void ParseViewState(object vs, int level)
{
    if (vs == null)
    {
        WriteData(Spaces(level) + "null");
    }
    else if (vs.GetType() == typeof(System.Web.UI.Triplet))
    {
        WriteData(Spaces(level) + "Triplet");
        ParseViewState((Triplet) vs, level);
    }
    else if (vs.GetType() == typeof(System.Web.UI.Pair))
    {
        WriteData(Spaces(level) + "Pair");
        ParseViewState((Pair) vs, level);
    }
    else if (vs.GetType() == typeof(System.Collections.ArrayList))
    {
        WriteData(Spaces(level) + "ArrayList");
        ParseViewState((IEnumerable) vs, level);
    }
    else if (vs.GetType().IsArray)
    {
        WriteData(Spaces(level) + "Array");
        ParseViewState((IEnumerable) vs, level);
    }
    else if (vs.GetType() == typeof(System.String))
    {
        WriteData(Spaces(level) + "'" + vs.ToString() + "'");
    }
    else if (vs.GetType().IsPrimitive)
    {
        WriteData(Spaces(level) + vs.ToString());
    }
    else
    {
        WriteData(Spaces(level) + vs.GetType().ToString());
    }
}

private void ParseViewState(Triplet vs, int level)
{
    ParseViewState(vs.First, level + 1);
    ParseViewState(vs.Second, level + 1);
    ParseViewState(vs.Third, level + 1);
}

private void ParseViewState(Pair vs, int level)
{
    ParseViewState(vs.First, level + 1);
    ParseViewState(vs.Second, level + 1);
}

private void ParseViewState(IEnumerable vs, int level)
{
    foreach (object item in vs)
    {
        ParseViewState(item, level + 1);
    }
}

private string Spaces(int count)
{
    string spaces ="";
    for (int index =0;index <count; index++)
    {
        spaces += "&nbsp;&nbsp;&nbsp;&nbsp;";
    }
    return spaces;
}

private void Button1_Click(object sender, System.EventArgs e)
{
    string vString =ViewStateText.Text;

    LosFormatter format = new LosFormatter();
    object vs =format.Deserialize(vString);

    if(vs != null)
        ParseViewState(vs, 0);
}

private void Button2_Click(object sender, System.EventArgs e)
{
    lblData.Text ="";
    ViewStateText.Text = "";
}

이 시점!! 여러분은 LosFormatter이라는 클래스에 관심을 조금 가져주셔야 합니다. ^^. 이 클래스가 Web Forms 페이지의 뷰 상태를 직렬화(serialize) 및 역직렬화(deserialize) 하는역할을 수행하는 클래스이니 말입니다. ^^

자. 소스가 필요하신 분들은 다운로드를 받아주세요.

소스는 위에서 보여드린 것과 같으며, 주 핵심내용은 ParseViewState라는 재귀함수를 반복적 호출하는 것입니다. 다시 이야기하지만, 이 소스는 위의 여러 미국 개발자들의 소스를 참고로 하여 제가 조금 정리한 것입니다. ^^; 고로, 원본 소스와 비교해서 보시면 더욱 이해가 용이할 수 있을 것입니다. ^^

그리고, 다음 샘플은 위의 여러 소스들을 또 한번 참고하여서 만들어본 Base Page 템플릿인데요. 간단하게 여러분의 웹 폼 페이지에서 이 Base Page 클래스를 상속 받아서 웹 폼 페이지를 만들면, 다음과 같이 현재 페이지에 존재하는 ViewState 값들을 표로써 확인하실 수 있게 됩니다.

단, 이 표로 출력되는 목록이 전체 ViewState 이다라고 장담드릴 수는 없을 것 같습니다요. 어쩌면 약간 일부 빠진 부분이 존재할 수도 있어요. 사실, 외국 개발자들이 공개한 소스도 그렇게 완전하지 않더라구요. 해서, 그 소스를 기반으로 해서 조금 더 용을 써 보긴 했습니다만... 그래도 이게 완전하다고 장담할 수는 없을 것 같아요.

능력이 되시는 분은 소스를 참고하셔서(경고!! 소스가 꽤나 지저분함을 연출하고 있습니다) 더욱 완전한 코드를 작성해서 주셔도 좋을 것 같아요 ^^  그 분께는 미리 대단히 감사드리겠습니다.

그럼, 샘플의 스크린샷을 보여드리겠습니다.

이 소스를 여러분의 웹 폼 페이지에 적용하고자 할 경우, 여러분이 해 주어야 할 것은 단지, 웹 폼 페이지를 만들고, 그 페이지를 ViewStateViewerPage라는 클래스로부터 상속받도록 한 다음, 웹 폼 페이지에 다음과 같이 코드를 작성해 주시면 됩니다. 물론,  ViewState의 표를 보고 싶을 경우에만 말입니다.

    private void Page_Load(object sender, System.EventArgs e)
    {
        this.ShowViewStateTable =true;
    }

그러면, 현재 페이지의 뷰상태 목록이 화면에 표로서 출력될 것입니다. 표가 출력되기를 원하지 않는다면, 위의 코드를 삭제하거나, ShowViewStateTable의 값을 false로 지정하시면 됩니다. ^^

* 참고 : UsingViewStateViewerPage.aspx(샘플 aspx 페이지)
            ViewStateViewerPage.cs(Base Class 페이지)

조금 어렵고, 설명도 부족한 강좌였습니다만... 도움이 되신 분들이 있을 것이라 믿어 의심해 보겠습니다.

감사합니다.


authored by


 
 
.NET과 Java 동영상 기반의 교육사이트

로딩 중입니다...

서버 프레임워크 지원 : NeoDEEX
based on ASP.NET 3.5
Creative Commons License
{5}
{2} 읽음   :{3} ({4})