login register Sysop! about ME  
qrcode
    최초 작성일 :    2009년 04월 13일
  최종 수정일 :    2009년 04월 13일
  작성자 :    프렐루드
  편집자 :    프렐루드 (김 영환)
  읽음수 :    31,414

강좌 목록으로 돌아가기

필자의 잡담~

꾸준히 열정적으로 차트 강좌를 올려주고 계신 프렐루드님에게 감사드립니다.
10회가 넘게 연재될 것으로 보이는 차트 강좌~~ 기대해주세요

이번 글에서는 원형 차트에 대해서 살펴보겠습니다. 지금까지 살펴본 선형이나 막대 차트는 어떠한 흐름을 보여주는 데 목적에 있는데 반해 원형 차트는 전체에 대한 각각의 비율을 보여주는데 최적화 되어 있습니다. 이렇게 그 쓰임새가 다른 만큼 원형 차트는 지금까지 살펴본 차트와는 다른 속성을 가집니다. 원형 차트는 Chart Control에서 다루어지는 여러 가지 차트를 두고 볼 때 가장 중요한 특징을 가지고 있는데요. 그것은 바로 XY 좌표계를 사용하지 않는다는 점입니다. 수학시간에도 나오는 부분이지만 XY 좌표계를 가지는 그래프는 2차원 평면상에서 그 데이터를 표현합니다. 그에 반해 원형 차트는 0에서 100까지의 값을 표현할 때 원형으로 360도를 사용하기 때문에 다른 차트와는 다른 특성을 가집니다. 이러한 원형 차트만의 특별한 성질들을 포함해 원형 차트의 내용들에 대해서 알아보겠습니다.

우선 간단하게 첫 번째 원형 차트에 대해서 살펴보겠습니다. 다음처럼 HTML 코드를 작성합니다.

<asp:Chart ID="Chart1" Palette="BrightPastel" runat="server">
  
<series>
    
<asp:Series ChartType="Pie" Name="Series1"></asp:Series>
  
</series>
  
<chartareas>
    
<asp:ChartArea Name="ChartArea1"></asp:ChartArea></chartareas>
</asp:Chart>    

그리고 다음처럼 .cs 코드를 작성합니다.

    protected void Page_Load(object sender, EventArgs e)

    {

        Random rand = new Random();

 

        for (int i = 0; i < 10; i++)

        {

            Chart1.Series[0].Points.AddY(rand.Next(100));

        }

    }

이에 의한 결과는 다음과 같습니다.

Hello World에 맞먹을 정도로 간결한 코드입니다. 이번 코드에는 그 전까지 사용했던 무엇인가가 보이지 않네요. 네 그렇습니다. 파이차트에서는 명시적인 색 지정을 하지 않고 팔레트 개념을 사용했습니다. Palette 애트리뷰트를 사용하면 미리 지정해 놓은 여러 가지 팔레트를 사용하거나 사용자가 지정하는 커스텀 팔레트를 사용할 수도 있습니다. HTML 코드에서 Palette="BrightPastel" 라고 지정해 주고 있기 때문에 이전의 차트처럼 명시적으로 색상 지정을 하지 않아도 나름 적절한 색상이 출력됨을 확인 할 수 있습니다. 그렇다면 지금까지 해온 명시적인 색상 지정이 쓸데없는 삽질이냐 하면 그건 또 아닙니다. MS에서 제공하는 색상 팔레트는 최악이라 쓸 수 없다는 수준은 아니지만 마구 사용하기에는 좀 부적절한 수준이기 때문에 대부분의 실무에서는 직접 색상을 지정하는 방식을 취해야 할 것입니다. 만약 커스텀 팔레트를 이용하고 싶다면 다음처럼 팔레트를 지정하면 됩니다.

<asp:Chart Palette="None" PaletteCustomColors="200, 222, 52, 8; 200, 8, 89, 140; 
            200, 49, 130, 239; 200, 189, 212, 42"
 ID="Chart1" runat="server">

이러한 팔레트 개념은 Pie 차트 이외에 Column이나 Bar등에도 동일하게 적용 가능합니다. 하지만 팔레트를 동일하게 주더라도 다른 개념으로 적용됨에 주의해야 합니다. 동일하게 팔레트를 주고 ChartType만 Column으로 주었을 때의 출력결과는 다음과 같습니다.

Pie 차트와는 달리 단색으로 하나의 Series를 표시함을 확인할 수 있습니다. Column등의 차트에 Palette를 적용하면 하나의 Series당 하나의 색을 순차적으로 지정해 출력됩니다. 그렇다면 Column 차트 등에서는 Pie 차트처럼 알록달록하게 색상 꾸미기를 할 수 없는 것일까요? 꼭 그렇지만은 않습니다. Palette 속성을 Chart 수준에서 지정하지 않고 Series 수준에서 지정해 주면 됩니다. 이를테면 다음과 같은 코드가 됩니다.

<asp:Chart ID="Chart1" runat="server">
  
<series>
    
<asp:Series Palette="BrightPastel" ChartType="Column" Name="Series1" />
  </
series>

이렇게 변경한 코드에 의한 결과는 다음과 같습니다. asp:Chart 태그에서는 팔레트를 제거하였고 asp:Series 태그에 팔레트 관련 정보를 넣어주었음에 주의하시기 바랍니다.

그 결과 하나의 Series에 출력되는 여러 막대를 다른 색으로 구성할 수 있다는 것을 확인할 수 있습니다. 이처럼 팔레트 관련 애트리뷰트를 사용하면 직접 색 지정을 하지 않고도 기본적인 색상 패턴을 차트에 적용할 수 있습니다.

흠흠... 원래는 원형 차트에 관련된 글이었는데 팔레트 이야기를 하다 보니 삼천포로 확 빠져 버렸네요. 그럼 다시 원형 차트에 대한 이야기로 돌아가 보겠습니다. 이번에는 원형 차트와 대동소이한 도넛 차트에 PieDrawingStyle의 값을 주어 효과를 주고 일부 요소를 분리해서 보여주는 방법으로 요소를 강조하는 법에 대해서 살펴보겠습니다. HTML 코드를 다음처럼 변경합니다.

<asp:Chart ID="Chart1" runat="server">
  
<series>
    
<asp:Series ChartType="Doughnut" Palette="BrightPastel" Name="Series1" />
  </
series>
  
<chartareas>
    
<asp:ChartArea Name="ChartArea1"></asp:ChartArea>
  
</chartareas>
 
</asp:Chart> 

그리고 .cs 코드를 다음처럼 변경합니다.

    protected void Page_Load(object sender, EventArgs e)

    {

        Random rand = new Random();

 

        for (int i = 0; i < 10; i++)

        {

            Chart1.Series[0].Points.AddY(rand.Next(100));

        }

 

        Chart1.Series[0]["PieDrawingStyle"] = "Concave";

        Chart1.Series[0].Points[3]["Exploded"] = "true";

        Chart1.Series[0]["DounutRadius"] = "60";

    }

이 코드에 의한 결과는 다음과 같습니다.

코드에서 변경된 부분은 그다지 많지 않습니다. ChartType을 Doughnut으로 변경했고 차트를 꾸미기 위해서 3가지 값을 세팅해 주었습니다.

PieDrawingStyle은 막대 차트에서 살펴본 DrawingStyle처럼 차트를 꾸미는 방법을 정의합니다. PieDrawingStyle은 Default, SoftEdge, Concave 세가지 값이 가능하며 여기에서 Concave 형태를 확인해 봤으니 값을 SoftEdge로 바꾸어 SoftEdge일때의 형태도 확인해 보시기 바랍니다. 여기서 주의할 점 하나는 DrawingStyle의 경우 3차원 차트에서도 그 내용이 적용되었지만 PieDrawingStyle은 2차원 방식에서만 가능하다는 점입니다. 또한 당연하게도 PieDrawingStyle은 Pie나 Doughnut에서만 유효합니다.

또 하나 Exploded 애트리뷰트는 원형 혹은 도넛 차트에서 특정 부분을 분리해서 보여주는 역할을 합니다. 위의 코드에서는 4번째 요소를 분리해서 보여주도록 지정하고 있습니다. 여기서 또 하나 짚어볼 부분은 Exploded가 복수의 요소에 대해서도 적용이 가능하다는 점입니다. 예를 들어 다음의 코드를 적용하면

    for (int i = 0; i < 10; i++)

        Chart1.Series[0].Points[i]["Exploded"] = "true";

다음과 같은 결과가 나옵니다

10개의 모든 요소에 대해서 Exploded 애트리뷰트를 true로 주었기 때문에 도넛차트가 완전히 분해된 것 처럼 표현되고 있습니다. Exploded는 원형차트에서 특정 요소를 강조하기 위해서 많이 사용되는 방법이므로 매우 유용한 애트리뷰트입니다. 마지막으로 사용된 DoughnutRadius는 도넛의 안쪽 원의 크기를 지정하는 값입니다.

원형 차트는 CollectedThreshold 라는 유용한 개념도 제공합니다. 딱히 마땅한 용어가 생각나지 않아서 애트리뷰트의 이름을 그대로 사용하였지만 풀어서 설명하자면 '퍼센트로 지정한 경계 이내의 요소는 따로 표현하지 말고 묶어서 보여줘라' 라는 의미입니다. 국회의원 선거방송 같은 경우에 전체 국회의원의 정당 점유율등을 보여주면서 여당이 몇 석 제1 야당이 몇 석 제2 야당이 몇 석 그리고 나머지가 몇 석 하는 식으로 매우 작은 값을 한 덩어리로 묶어서 보여줘야 하는 경우는 굉장히 많습니다. 이런 경우 그 값의 경계를 조사해서 묶어주는 코딩을 하는 건 굉장히 귀찮은 일이 되겠죠? 파이 차트에서는 이런 경우 한 데 묶을 값의 경계를 지정해 주면 이 부분을 그 외로 지정해 주는 방법을 제공합니다. 역시 백마디 말보다 한번의 코딩이 알기 쉽죠? HTML 코드를 다음처럼 구성합니다.

<asp:Chart ID="Chart1" Width="600" Height="300" runat="server">
  
<series>
    
<asp:Series ChartType="Pie" Palette="BrightPastel" Name="Series1" />
  </
series>
  
<chartareas>
    
<asp:ChartArea Name="ChartArea1"></asp:ChartArea>
  
</chartareas>
</asp:Chart> 

그리고 .cs 코드는 다음과 같습니다.

    protected void Page_Load(object sender, EventArgs e)

    {

        Random rand = new Random();

 

        int value = 1000;

 

        for (int i = 0; i < 10; i++)

        {

            Chart1.Series[0].Points.AddY(value);

            value = value / 2;

        }           

 

        Chart1.Series[0]["CollectedThresholdUsePercent"] = "true";

        Chart1.Series[0]["CollectedThreshold"] = "5";

        Chart1.Series[0]["CollectedSliceExploded"] = "true";

 

        Response.Write("Point의 수 : " + Chart1.Series[0].Points.Count);

    }

그리고 이 코드에 의한 결과는 다음과 같습니다.

분명히 10개의 Point를 지정해 주었음에도 실제 표현되고 있는 조각은 5개 뿐임을 확인할 수 있습니다. 명시적으로 Point를 몇 개 지정했는지 확인하기 위해서 보여주는 Series의 Point 수를 Response.Write로 표시해주고 있기도 합니다. 이번 코드의 핵심은 다음 코드에 있습니다.

        Chart1.Series[0]["CollectedThresholdUsePercent"] = "true";

        Chart1.Series[0]["CollectedThreshold"] = "5";

        Chart1.Series[0]["CollectedSliceExploded"] = "true";

첫 번째 줄에서 CollectedThresholdUsePercent를 true로 지정해 특정 퍼센트 이하의 값을 묶어서 표현 하도록 하고 있고, 두 번째 줄에서 그 경계를 5퍼센트로 지정하고 있습니다. 그리고 마지막 줄에서 이렇게 합쳐진 조각을 분리해서 보여주도록 지정하고 있습니다. 여기서 만약 CollectedThresholdUsePercent의 값을 false로 지정하면 어떻게 될까요? 이렇게 지정하면 작은 값을 모으는 기준이 퍼센트가 아닌 절대값이 됩니다. 즉, 위의 코드에서는 5보다 작은 값인 요소를 모으라라는 이야기가 됩니다. 이처럼 CollectedThresholdUsePercent의 값을 true로 하면 값을 모으는 기준이 퍼센트 단위가 되고 false로 하면 딱 그 값 이하를 모으게 됩니다. false로 지정할 때 값은 Double으로 지정할 수 있다는 점도 기억해 두시기 바랍니다.(원칙적으로 Chart 컨트롤에서 각 Point에 할당하는 값은 Double형 입니다.)

이번에는 약간 기본기에서 빠진 이야기를 해 보겠습니다. 우선 다음의 파일을 다운로드 받아서 App_Code에 위치시킵니다. piehelper.cs 파일은 다음에 살펴볼 기능을 위해서 제공되는 클래스 파일입니다. 이 파일에 대한 설명은 생략하겠습니다.

[ piehelper.cs 파일 링크 ]

다음처럼 .cs 파일을 수정합니다. 우선 가장 위에 다음같이 using문을 추가해 주어야 합니다.

using System.Web.UI.DataVisualization.Charting.Utilities;

이 네임스페이스는 다운받은 piehelper.cs 파일에 관련되어 있습니다. 그리고 다음처럼 Page_Load 메서드를 변경합니다.

    protected void Page_Load(object sender, EventArgs e)

    {

        Random rand = new Random();

 

        int value = 1000;

 

        for (int i = 0; i < 10; i++)

        {

            Chart1.Series[0].Points.AddY(value);

            value = value / 2;

        }           

 

        Chart1.Series[0]["CollectedThresholdUsePercent"] = "true";

        Chart1.Series[0]["CollectedThreshold"] = "5";

        Chart1.Series[0]["CollectedSliceExploded"] = "true";

 

        Response.Write("Point의 수 : " + Chart1.Series[0].Points.Count);

 

        PieCollectedDataHelper pieHelper = new PieCollectedDataHelper(Chart1);

        pieHelper.CollectedPercentage = 5;

        pieHelper.ShowCollectedDataAsOneSlice = false;

        pieHelper.SupplementedAreaSizeRatio = 0.9f;

        pieHelper.ChartAreaPosition = new RectangleF(5f, 5f, 90f, 90f);

        pieHelper.ShowSmallSegmentsAsSupplementalPie("Series1");       

    }

이 코드에 의한 결과는 다음과 같습니다.

것 보기엔 별거 아닌 것 같지만 아주 멋진 기능이라고 생각합니다. PieCollectedDataHelper 클래스를 이용해 구현한 이 기능은 좌측의 원형차트에서 Collected 기능에 의해서 모아진 부분만 오른쪽에 또 다른 원형차트로 보여주는 기능입니다. PieHelper를 사용하는 부분의 코드는 메서드가 말해주는 그대로의 역할을 하기 때문에 큰 어려움 없이 이해하실 수 있을 거라고 믿습니다. Chart 컨트롤 예제에 보면 이처럼 기본 클래스에 헬퍼 클래스를 사용함으로써 기본 차트를 확장한 확장형태의 차트를 보여주고 있습니다. 아마도 이번 강좌에서는 이 이외의 확장 차트에 대해서 다루기 힘들 것이라 생각되지만 헬퍼 클래스의 내용을 곰곰히 뜯어 보신다면 더 멋진 확장 차트를 직접 만들 수 있지 않을까 싶네요.

이번에는 3차원 형태의 원형 차트에 대해서 살펴보겠습니다. 다음처럼 HTML 코드를 변경합니다.

<asp:Chart ID="Chart1" runat="server">
  
<series>
    
<asp:Series ChartType="Pie" Palette="SemiTransparent" Name="Series1" />
  </
series>
  
<chartareas>
    
<asp:ChartArea Area3DStyle-Enable3D="true" 
    Area3DStyle-Inclination
="50" Name="ChartArea1" />
  </
chartareas>
</asp:Chart>

그리고 다음처럼 .cs 코드를 변경합니다.

    protected void Page_Load(object sender, EventArgs e)

    {

        Random rand = new Random();

 

        for (int i = 0; i < 10; i++)

        {

            Chart1.Series[0].Points.AddY(rand.Next(100));

        }

 

        Chart1.Series[0].Points[3]["Exploded"] = "true";       

    }

이에 의한 결과는 다음과 같습니다.

다시 간단하기 그지없는 코드로 돌아왔습니다. 입체형태를 부각시키기 위해서 SemiTransparent 팔레트를 사용했습니다. 그리고 Series에 3차원 관련 애트리뷰트를 추가해 주었습니다. 3차원 원형 차트에서 가장 키 포인트가 되는 애트리뷰트는 Area3DStyle-Inclination 입니다. 상하 방향의 기울기를 결정하는 이 애트리뷰트를 이용하면 차트의 기울기를 결정할 수 있기 때문에 이 값을 여러 가지로 변경해 보면서 자신에게 잘 맞는 형태를 찾아 보시기 바랍니다.

이번에는 조금 더 복잡한 형태를 살펴 보겠습니다. 가장 첫 번째 글에서 보여드렸던 원형 차트와 비슷한 느낌의 차트인데요. 3차원 원형 차트를 두 개 쌓아서 보여주는 형태입니다. 이러한 차트는 다른 연대의 같은 내용 비교 같은 경우에 유용하게 쓰일 수 있겠죠? 80년대와 90년대의 소득 계층 비교라던가 교통 수단 이동 비교 등이 그 예일 수 있을 것입니다. 하지만 이러한 차트는 Stacked 차트처럼 기본적으로 제공해 주는 인터페이스가 없습니다. 때문에 두 개의 Series를 다른 두 개의 ChartArea에 바인딩 해서 크기와 위치를 조절하는 방법을 사용해야 합니다. 그럼 코드를 볼까요? HTML 코드는 다음과 같습니다.

<asp:Chart ID="Chart1" runat="server">
  
<series>
    
<asp:Series ChartType="Pie" Palette="BrightPastel" 
    Name
="Series1"  ChartArea="ChartArea1"></asp:Series>
    
<asp:Series ChartType="Pie" Palette="BrightPastel" 
    Name
="Series2" ChartArea="ChartArea2"></asp:Series>  
  
</series>
  
<chartareas>
    
<asp:ChartArea Area3DStyle-Enable3D="true" Area3DStyle-Inclination="50" 
    Name
="ChartArea1"></asp:ChartArea>
    
<asp:ChartArea Area3DStyle-Enable3D="true" Area3DStyle-Inclination="50" 
    BackColor
="Transparent" Name="ChartArea2"></asp:ChartArea>
  
</chartareas>
</asp:Chart> 

그리고 .cs코드는 다음과 같습니다.

    protected void Page_Load(object sender, EventArgs e)

    {

        Random rand = new Random();

 

        for (int i = 0; i < 10; i++)

        {

            Chart1.Series[0].Points.AddY(i * 100);

            Chart1.Series[1].Points.AddY(i * 100);

        }

 

        Chart1.Series[1].BorderColor = Color.FromArgb(20, Color.Black);       

 

        Chart1.ChartAreas[0].Position.Width = 100;

        Chart1.ChartAreas[0].Position.Height = 100;

        Chart1.ChartAreas[0].Position.X = 0;

        Chart1.ChartAreas[0].Position.Y = 0;

 

        Chart1.ChartAreas[1].Position.Width = 50;

        Chart1.ChartAreas[1].Position.Height = 50;

        Chart1.ChartAreas[1].Position.X = 25;

        Chart1.ChartAreas[1].Position.Y = 25;

    }

이 코드에 의한 결과는 다음과 같습니다.

앞에서의 설명처럼 두 개의 Series와 두 개의 ChartArea를 사용합니다. 위쪽에 올라가게 될 ChartArea2의 BackColor를 Transparent로 주어 배경을 투명하게 보이도록 하였습니다. 그리고 .cs 코드에서 위쪽에 올라갈 ChartArea의 크기를 아래쪽의 1/4로 하고 원의 중심을 아래에 위치하는 원과 동일하도록 맞추어 주었습니다. 그렇게 복잡한 작업은 아니지만 기본적으로 제공하는 인터페이스가 아니기 때문에 이것저것 신경 써서 손 봐줘야 되는 부분이 많네요. 하지만 이렇게 하는 것 만으로는 눈에 잘 띄지 않는 문제가 있습니다. 왠지 이런 차트에는 각각의 요소를 설명해주는 Label이 있으면 더 좋을 것 같지 않나요? 이번에는 두 그래프에 레이블을 달아 보겠습니다. 위에 올라간 그래프에는 원형 그래프 안쪽에 아래에 깔리는 그래프에는 원형 그래프 바깥쪽에 레이블을 표시해 보겠습니다. HTML 코드를 다음처럼 변경합니다.

.cs 코드를 다음처럼 변경합니다. HTML 코드는 그대로입니다.

    protected void Page_Load(object sender, EventArgs e)

    {

        Random rand = new Random();

 

        string[] valueX = { "가", "나", "다", "라", "마" };

        int[] valueY = { 500, 400, 300, 200, 100 };

 

        Chart1.Series[0].Points.DataBindXY(valueX, valueY);

        Chart1.Series[1].Points.DataBindXY(valueX, valueY);

 

        Chart1.Series[0].BorderColor = Color.FromArgb(80, Color.Black);

        Chart1.Series[0]["PieLabelStyle"] = "Outside";

        Chart1.Series[0].LabelForeColor = Color.Black;       

 

        Chart1.Series[1].BorderColor = Color.FromArgb(80, Color.Black);

        Chart1.Series[1]["PieLabelStyle"] = "Inside";

        Chart1.Series[1].LabelForeColor = Color.White;

        Chart1.Series[1].ShadowColor = Color.Gray;

        Chart1.Series[1].ShadowOffset = 2;

 

        Chart1.ChartAreas[0].Position.Width = 100;

        Chart1.ChartAreas[0].Position.Height = 100;

        Chart1.ChartAreas[0].Position.X = 0;

        Chart1.ChartAreas[0].Position.Y = 0;

 

        Chart1.ChartAreas[1].Position.Width = 50;

        Chart1.ChartAreas[1].Position.Height = 50;

        Chart1.ChartAreas[1].Position.X = 25;

        Chart1.ChartAreas[1].Position.Y = 25;

    }

이 코드에 의한 결과는 다음과 같습니다.

앞에서 살펴본 코드에서 그렇게 크게 달라진 점은 없습니다. 달라진 부분은 레이블을 표시하기 위해서 XY 값을 주기 위해서 X의 배열과 Y의 배열을 만들어 DataBindXY로 바인딩한 부분 정도가 달라진 점이겠네요. 그리고 바인딩 코드 바로 아래에 각 Series의 레이블 형태를 지정해 주고 있습니다. 그 결과 이미지 처럼 하나의 그래프는 그래프 안쪽에 레이블이 다른 하나는 그래프 바깥쪽으로 레이블이 표시됩니다. 이러한 레이블 표시는 단일 원형 그래프에서도 유용하게 사용될 수 있습니다.

이것으로 원형 차트에 대해서 살펴보았습니다. 마지막으로 원형 차트에 대해 표면적으로 드러나는 부분이 아닌 논리적인 문제를 설명하고 글을 마칠까 합니다. 지금까지 글에서 부분부분 언급해왔던 내용이기는 하지만 원형차트는 선형, 막대 차트 등의 다른 차트와는 다른 좌표계를 가지고 있습니다. 이 때문에 발생하는 몇 가지 차이점으로 원형차트에는 제한사항이 존재합니다. 바로 XY 좌표계를 사용하는 그래프와 동시사용(같은 ChartArea에 복수의 그래프를 두는 것)이 불가능하다는 점입니다. 이를테면 선형 그래프와 막대 그래프는 그 수에 관계없이 하나의 ChartArea에 여러 개의 Series를 올려 사용할 수 있습니다. 이를 이용해서 복잡한 데이터를 표현하는 그래프를 만들어낼 수 있습니다. 하지만 원형 Series와 선형 Series를 하나의 ChartArea에 올려놓게 되면 실행 조차 되지 않음을 확인할 수 있습니다. 이는 두 Series의 좌표계가 다르기 때문에 차트 자체를 생성할 수 없기 때문입니다. 어거지로 두개의 ChartArea를 생성한다음 하나를 바탕을 투명하게 만들어 하나의 위치에 원형 그래프와 선형 그래프가 위치하는 것처럼 표현할 수 있겠지만. 논리적으로 하나의 영역에 선형 등의 XY 좌표계를 사용하는 Series와 원형 Series는 같이 사용될 수 없음을 기억해 두시기 바랍니다. 원형 그래프는 선형이나 막대 그래프와는 다른 용도로 많이 사용되는 그래프이기 때문에 그 씀씀이를 잘 익혀 두시기 바랍니다.

그럼 다음 글에서 뵙겠습니다.


authored by

  goni0607
  2009-09-18(15:14)
캐릭 이미지
감사히 잘 봤습니다~ ^^

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

로딩 중입니다...

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