다음을 통해 공유


연습: 백그라운드 작업을 사용하는 폼 구현

완료하는 데 시간이 오래 걸리는 작업을 수행하면서 UI(사용자 인터페이스)가 응답을 중지하지 않게 하거나 "중단"되지 않게 하려는 경우 BackgroundWorker 클래스를 사용하여 다른 스레드에서 해당 작업을 실행할 수 있습니다.

이 연습에서는 BackgroundWorker 클래스를 사용하여 시간이 많이 걸리는 계산을 "백그라운드에서" 수행하고 사용자 인터페이스는 응답 가능한 상태로 유지하는 방법을 보여 줍니다. 이 연습을 마치면 피보나치 수(Fibonacci number)를 비동기적으로 계산하는 응용 프로그램을 갖게 됩니다. 큰 피보나치 수(Fibonacci number)를 계산하는 데에는 시간이 많이 걸릴 수 있지만 주 UI 스레드는 이러한 지연에 의해 중단되지 않으며 계산 중에 폼이 응답합니다.

이 연습에서 수행할 작업은 다음과 같습니다.

  • Windows 기반 응용 프로그램 만들기

  • 폼에 BackgroundWorker 만들기

  • 비동기 이벤트 처리기 추가

  • 진행률 보고 및 취소 지원 추가

이 예제에서 사용된 코드의 전체 목록은 방법: 백그라운드 작업을 사용하는 폼 구현을 참조하십시오.

참고

표시되는 대화 상자와 메뉴 명령은 활성 설정이나 버전에 따라 도움말에서 설명하는 것과 다를 수 있습니다. 설정을 변경하려면 도구 메뉴에서 설정 가져오기 및 내보내기를 선택합니다. 자세한 내용은 설정에 대한 작업을 참조하십시오.

프로젝트 만들기

첫 번째 단계는 프로젝트를 만들고 폼을 설정하는 것입니다.

백그라운드 작업을 사용하는 폼을 만들려면

  1. BackgroundWorkerExample이라는 Windows 기반 응용 프로그램 프로젝트를 만듭니다. 자세한 내용은 방법: 새 Windows Forms 응용 프로그램 프로젝트 만들기를 참조하십시오.

  2. 솔루션 탐색기에서 Form1을 마우스 오른쪽 단추로 클릭한 다음 바로 가기 메뉴에서 이름 바꾸기를 선택합니다. 파일 이름을 FibonacciCalculator로 변경합니다. 코드 요소 'Form1'에 대한 모든 참조의 이름을 변경할 것인지 묻는 메시지가 나타나면 단추를 클릭합니다.

  3. 도구 상자NumericUpDown 컨트롤을 폼으로 끌어 옵니다. Minimum 속성을 1로 설정하고 Maximum 속성을 91로 설정합니다.

  4. 두 개의 Button 컨트롤을 폼에 추가합니다.

  5. 첫 번째 Button 컨트롤 이름을 startAsyncButton으로 변경하고 Text 속성을 Start Async로 설정합니다. 두 번째 Button 컨트롤 이름을 cancelAsyncButton으로 변경하고 Text 속성을 Cancel Async로 설정합니다. Enabled 속성을 false로 설정합니다.

  6. Button 컨트롤 모두의 Click 이벤트에 대한 이벤트 처리기를 만듭니다. 자세한 내용은 방법: 디자이너를 사용하여 이벤트 처리기 만들기를 참조하십시오.

  7. 도구 상자Label 컨트롤을 폼으로 끌어 온 다음 이름을 resultLabel로 변경합니다.

  8. 도구 상자ProgressBar 컨트롤을 폼으로 끌어 옵니다.

폼에 BackgroundWorker 만들기

Windows Forms 디자이너를 사용하여 비동기 작업에 사용할 BackgroundWorker를 만들 수 있습니다.

디자이너로 BackgroundWorker를 만들려면

  • 도구 상자에서 구성 요소 탭에 있는 BackgroundWorker를 폼으로 끌어 옵니다.

비동기 이벤트 처리기 추가

이제 BackgroundWorker 구성 요소의 비동기 이벤트에 대한 이벤트 처리기를 추가할 수 있습니다. 백그라운드에서 실행되고 시간이 많이 걸리는 피보나치 수(Fibonacci number) 계산 작업은 이러한 이벤트 처리기 중 하나에서 호출됩니다.

비동기 이벤트 처리기를 구현하려면

  1. 속성 창에서 BackgroundWorker 구성 요소를 선택하고 이벤트 단추를 클릭합니다. DoWorkRunWorkerCompleted 이벤트를 두 번 클릭하여 이벤트 처리기를 만듭니다. 이벤트 처리기를 사용하는 방법에 대한 자세한 내용은 방법: 디자이너를 사용하여 이벤트 처리기 만들기을 참조하십시오.

  2. ComputeFibonacci라는 새 메서드를 폼에 만듭니다. 이 메서드가 실제 작업을 수행하며 백그라운드에서 실행됩니다. 이 코드에서는 계산해야 할 수가 커질수록 시간은 기하급수적으로 늘어나는 비효율적인 피보나치(Fibonacci) 알고리즘을 재귀적으로 구현하여 보여 줍니다. 이 코드는 설명을 위해 제공되었으며, 응용 프로그램을 오래 지연시킬 수 있는 작업을 보여 줍니다.

    ' This is the method that does the actual work. For this
    ' example, it computes a Fibonacci number and
    ' reports progress as it does its work.
    Function ComputeFibonacci( _
        ByVal n As Integer, _
        ByVal worker As BackgroundWorker, _
        ByVal e As DoWorkEventArgs) As Long
    
        ' The parameter n must be >= 0 and <= 91.
        ' Fib(n), with n > 91, overflows a long.
        If n < 0 OrElse n > 91 Then
            Throw New ArgumentException( _
                "value must be >= 0 and <= 91", "n")
        End If
    
        Dim result As Long = 0
    
        ' Abort the operation if the user has canceled.
        ' Note that a call to CancelAsync may have set 
        ' CancellationPending to true just after the
        ' last invocation of this method exits, so this 
        ' code will not have the opportunity to set the 
        ' DoWorkEventArgs.Cancel flag to true. This means
        ' that RunWorkerCompletedEventArgs.Cancelled will
        ' not be set to true in your RunWorkerCompleted
        ' event handler. This is a race condition.
        If worker.CancellationPending Then
            e.Cancel = True
        Else
            If n < 2 Then
                result = 1
            Else
                result = ComputeFibonacci(n - 1, worker, e) + _
                         ComputeFibonacci(n - 2, worker, e)
            End If
    
            ' Report progress as a percentage of the total task.
            Dim percentComplete As Integer = _
                CSng(n) / CSng(numberToCompute) * 100
            If percentComplete > highestPercentageReached Then
                highestPercentageReached = percentComplete
                worker.ReportProgress(percentComplete)
            End If
    
        End If
    
        Return result
    
    End Function
    
    // This is the method that does the actual work. For this
    // example, it computes a Fibonacci number and
    // reports progress as it does its work.
    long ComputeFibonacci(int n, BackgroundWorker worker, DoWorkEventArgs e)
    {
        // The parameter n must be >= 0 and <= 91.
        // Fib(n), with n > 91, overflows a long.
        if ((n < 0) || (n > 91))
        {
            throw new ArgumentException(
                "value must be >= 0 and <= 91", "n");
        }
    
        long result = 0;
    
        // Abort the operation if the user has canceled.
        // Note that a call to CancelAsync may have set 
        // CancellationPending to true just after the
        // last invocation of this method exits, so this 
        // code will not have the opportunity to set the 
        // DoWorkEventArgs.Cancel flag to true. This means
        // that RunWorkerCompletedEventArgs.Cancelled will
        // not be set to true in your RunWorkerCompleted
        // event handler. This is a race condition.
    
        if (worker.CancellationPending)
        {   
            e.Cancel = true;
        }
        else
        {   
            if (n < 2)
            {   
                result = 1;
            }
            else
            {   
                result = ComputeFibonacci(n - 1, worker, e) + 
                         ComputeFibonacci(n - 2, worker, e);
            }
    
            // Report progress as a percentage of the total task.
            int percentComplete = 
                (int)((float)n / (float)numberToCompute * 100);
            if (percentComplete > highestPercentageReached)
            {
                highestPercentageReached = percentComplete;
                worker.ReportProgress(percentComplete);
            }
        }
    
        return result;
    }
    
    // This is the method that does the actual work. For this
    // example, it computes a Fibonacci number and
    // reports progress as it does its work.
    long ComputeFibonacci( int n, BackgroundWorker^ worker, DoWorkEventArgs ^ e )
    {
       // The parameter n must be >= 0 and <= 91.
       // Fib(n), with n > 91, overflows a long.
       if ( (n < 0) || (n > 91) )
       {
          throw gcnew ArgumentException( "value must be >= 0 and <= 91","n" );
       }
    
       long result = 0;
    
       // Abort the operation if the user has cancelled.
       // Note that a call to CancelAsync may have set 
       // CancellationPending to true just after the
       // last invocation of this method exits, so this 
       // code will not have the opportunity to set the 
       // DoWorkEventArgs.Cancel flag to true. This means
       // that RunWorkerCompletedEventArgs.Cancelled will
       // not be set to true in your RunWorkerCompleted
       // event handler. This is a race condition.
       if ( worker->CancellationPending )
       {
          e->Cancel = true;
       }
       else
       {
          if ( n < 2 )
          {
             result = 1;
          }
          else
          {
             result = ComputeFibonacci( n - 1, worker, e ) + ComputeFibonacci( n - 2, worker, e );
          }
    
          // Report progress as a percentage of the total task.
          int percentComplete = (int)((float)n / (float)numberToCompute * 100);
          if ( percentComplete > highestPercentageReached )
          {
             highestPercentageReached = percentComplete;
             worker->ReportProgress( percentComplete );
          }
       }
    
       return result;
    }
    
  3. DoWork 이벤트 처리기에서 ComputeFibonacci 메서드 호출을 추가합니다. DoWorkEventArgsArgument 속성에서 ComputeFibonacci에 대한 첫 번째 매개 변수를 가져옵니다. BackgroundWorkerDoWorkEventArgs 매개 변수는 나중에 진행률 보고 및 취소 지원에 사용됩니다. ComputeFibonacci에서 반환된 값을 DoWorkEventArgsResult 속성에 할당합니다. 이 결과는 RunWorkerCompleted 이벤트 처리기에서 사용할 수 있습니다.

    참고

    DoWork 이벤트 처리기가 backgroundWorker1 인스턴스 변수를 직접 참조하지 않습니다. 이를 통해 이 이벤트 처리기는 BackgroundWorker의 특정 인스턴스에 결합됩니다. 대신 이 이벤트를 발생시킨 BackgroundWorker에 대한 참조가 sender 매개 변수에서 복구됩니다. 이것은 폼이 두 개 이상의 BackgroundWorker를 호스트하는 경우에 중요합니다. DoWork 이벤트 처리기에서 사용자 인터페이스 개체를 조작하지 않는 것도 중요합니다. 대신 BackgroundWorker 이벤트를 통해 사용자 인터페이스와 통신합니다.

    ' This event handler is where the actual work is done.
    Private Sub backgroundWorker1_DoWork( _
    ByVal sender As Object, _
    ByVal e As DoWorkEventArgs) _
    Handles backgroundWorker1.DoWork
    
        ' Get the BackgroundWorker object that raised this event.
        Dim worker As BackgroundWorker = _
            CType(sender, BackgroundWorker)
    
        ' Assign the result of the computation
        ' to the Result property of the DoWorkEventArgs
        ' object. This is will be available to the 
        ' RunWorkerCompleted eventhandler.
        e.Result = ComputeFibonacci(e.Argument, worker, e)
    End Sub 'backgroundWorker1_DoWork
    
    // This event handler is where the actual,
    // potentially time-consuming work is done.
    private void backgroundWorker1_DoWork(object sender, 
        DoWorkEventArgs e)
    {   
        // Get the BackgroundWorker that raised this event.
        BackgroundWorker worker = sender as BackgroundWorker;
    
        // Assign the result of the computation
        // to the Result property of the DoWorkEventArgs
        // object. This is will be available to the 
        // RunWorkerCompleted eventhandler.
        e.Result = ComputeFibonacci((int)e.Argument, worker, e);
    }
    
    // This event handler is where the actual,
    // potentially time-consuming work is done.
    void backgroundWorker1_DoWork( Object^ sender, DoWorkEventArgs^ e )
    {
       // Get the BackgroundWorker that raised this event.
       BackgroundWorker^ worker = dynamic_cast<BackgroundWorker^>(sender);
    
       // Assign the result of the computation
       // to the Result property of the DoWorkEventArgs
       // object. This is will be available to the 
       // RunWorkerCompleted eventhandler.
       e->Result = ComputeFibonacci( safe_cast<Int32>(e->Argument), worker, e );
    }
    
  4. startAsyncButton 컨트롤의 Click 이벤트 처리기에서 비동기 작업을 시작하는 코드를 추가합니다.

    Private Sub startAsyncButton_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) _
    Handles startAsyncButton.Click
    
        ' Reset the text in the result label.
        resultLabel.Text = [String].Empty
    
        ' Disable the UpDown control until 
        ' the asynchronous operation is done.
        Me.numericUpDown1.Enabled = False
    
        ' Disable the Start button until 
        ' the asynchronous operation is done.
        Me.startAsyncButton.Enabled = False
    
        ' Enable the Cancel button while 
        ' the asynchronous operation runs.
        Me.cancelAsyncButton.Enabled = True
    
        ' Get the value from the UpDown control.
        numberToCompute = CInt(numericUpDown1.Value)
    
        ' Reset the variable for percentage tracking.
        highestPercentageReached = 0
    
    
        ' Start the asynchronous operation.
        backgroundWorker1.RunWorkerAsync(numberToCompute)
    End Sub 
    
    private void startAsyncButton_Click(System.Object sender, 
        System.EventArgs e)
    {
        // Reset the text in the result label.
        resultLabel.Text = String.Empty;
    
        // Disable the UpDown control until 
        // the asynchronous operation is done.
        this.numericUpDown1.Enabled = false;
    
        // Disable the Start button until 
        // the asynchronous operation is done.
        this.startAsyncButton.Enabled = false;
    
        // Enable the Cancel button while 
        // the asynchronous operation runs.
        this.cancelAsyncButton.Enabled = true;
    
        // Get the value from the UpDown control.
        numberToCompute = (int)numericUpDown1.Value;
    
        // Reset the variable for percentage tracking.
        highestPercentageReached = 0;
    
        // Start the asynchronous operation.
        backgroundWorker1.RunWorkerAsync(numberToCompute);
    }
    
    void startAsyncButton_Click( System::Object^ /*sender*/, System::EventArgs^ /*e*/ )
    {
    
       // Reset the text in the result label.
       resultLabel->Text = String::Empty;
    
       // Disable the UpDown control until 
       // the asynchronous operation is done.
       this->numericUpDown1->Enabled = false;
    
       // Disable the Start button until 
       // the asynchronous operation is done.
       this->startAsyncButton->Enabled = false;
    
       // Enable the Cancel button while 
       // the asynchronous operation runs.
       this->cancelAsyncButton->Enabled = true;
    
       // Get the value from the UpDown control.
       numberToCompute = (int)numericUpDown1->Value;
    
       // Reset the variable for percentage tracking.
       highestPercentageReached = 0;
    
       // Start the asynchronous operation.
       backgroundWorker1->RunWorkerAsync( numberToCompute );
    }
    
  5. RunWorkerCompleted 이벤트 처리기에서 계산 결과를 resultLabel 컨트롤에 할당합니다.

    ' This event handler deals with the results of the
    ' background operation.
    Private Sub backgroundWorker1_RunWorkerCompleted( _
    ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) _
    Handles backgroundWorker1.RunWorkerCompleted
    
        ' First, handle the case where an exception was thrown.
        If (e.Error IsNot Nothing) Then
            MessageBox.Show(e.Error.Message)
        ElseIf e.Cancelled Then
            ' Next, handle the case where the user canceled the 
            ' operation.
            ' Note that due to a race condition in 
            ' the DoWork event handler, the Cancelled
            ' flag may not have been set, even though
            ' CancelAsync was called.
            resultLabel.Text = "Canceled"
        Else
            ' Finally, handle the case where the operation succeeded.
            resultLabel.Text = e.Result.ToString()
        End If
    
        ' Enable the UpDown control.
        Me.numericUpDown1.Enabled = True
    
        ' Enable the Start button.
        startAsyncButton.Enabled = True
    
        ' Disable the Cancel button.
        cancelAsyncButton.Enabled = False
    End Sub 'backgroundWorker1_RunWorkerCompleted
    
    // This event handler deals with the results of the
    // background operation.
    private void backgroundWorker1_RunWorkerCompleted(
        object sender, RunWorkerCompletedEventArgs e)
    {
        // First, handle the case where an exception was thrown.
        if (e.Error != null)
        {
            MessageBox.Show(e.Error.Message);
        }
        else if (e.Cancelled)
        {
            // Next, handle the case where the user canceled 
            // the operation.
            // Note that due to a race condition in 
            // the DoWork event handler, the Cancelled
            // flag may not have been set, even though
            // CancelAsync was called.
            resultLabel.Text = "Canceled";
        }
        else
        {
            // Finally, handle the case where the operation 
            // succeeded.
            resultLabel.Text = e.Result.ToString();
        }
    
        // Enable the UpDown control.
        this.numericUpDown1.Enabled = true;
    
        // Enable the Start button.
        startAsyncButton.Enabled = true;
    
        // Disable the Cancel button.
        cancelAsyncButton.Enabled = false;
    }
    
    // This event handler deals with the results of the
    // background operation.
    void backgroundWorker1_RunWorkerCompleted( Object^ /*sender*/, RunWorkerCompletedEventArgs^ e )
    {
       // First, handle the case where an exception was thrown.
       if ( e->Error != nullptr )
       {
          MessageBox::Show( e->Error->Message );
       }
       else
       if ( e->Cancelled )
       {
          // Next, handle the case where the user cancelled 
          // the operation.
          // Note that due to a race condition in 
          // the DoWork event handler, the Cancelled
          // flag may not have been set, even though
          // CancelAsync was called.
          resultLabel->Text = "Cancelled";
       }
       else
       {
          // Finally, handle the case where the operation 
          // succeeded.
          resultLabel->Text = e->Result->ToString();
       }
    
       // Enable the UpDown control.
       this->numericUpDown1->Enabled = true;
    
       // Enable the Start button.
       startAsyncButton->Enabled = true;
    
       // Disable the Cancel button.
       cancelAsyncButton->Enabled = false;
    }
    

진행률 보고 및 취소 지원 추가

시간이 오래 걸리는 비동기 작업의 경우 사용자에게 진행률을 보고하여 사용자가 작업을 취소할 수 있도록 하는 것이 좋습니다. BackgroundWorker 클래스는 백그라운드 작업이 수행될 때 진행률을 게시할 수 있는 이벤트를 제공합니다. 또한 해당 클래스는 작업자 코드가 CancelAsync에 대한 호출을 감지하여 스스로 중지할 수 있게 하는 플래그를 제공합니다.

진행률 보고를 구현하려면

  1. 속성 창에서 backgroundWorker1을 선택합니다. WorkerReportsProgressWorkerSupportsCancellation 속성을 true로 설정합니다.

  2. FibonacciCalculator 폼에 두 개의 변수를 선언합니다. 이러한 변수는 진행률을 추적하는 데 사용됩니다.

    Private numberToCompute As Integer = 0
    Private highestPercentageReached As Integer = 0
    
    private int numberToCompute = 0;
    private int highestPercentageReached = 0;
    
    int numberToCompute;
    int highestPercentageReached;
    
  3. ProgressChanged 이벤트에 대한 이벤트 처리기를 추가합니다. ProgressChanged 이벤트 처리기에서 ProgressBarProgressChangedEventArgs 매개 변수의 ProgressPercentage 속성으로 업데이트합니다.

    ' This event handler updates the progress bar.
    Private Sub backgroundWorker1_ProgressChanged( _
    ByVal sender As Object, ByVal e As ProgressChangedEventArgs) _
    Handles backgroundWorker1.ProgressChanged
    
        Me.progressBar1.Value = e.ProgressPercentage
    
    End Sub
    
    // This event handler updates the progress bar.
    private void backgroundWorker1_ProgressChanged(object sender,
        ProgressChangedEventArgs e)
    {
        this.progressBar1.Value = e.ProgressPercentage;
    }
    
    // This event handler updates the progress bar.
    void backgroundWorker1_ProgressChanged( Object^ /*sender*/, ProgressChangedEventArgs^ e )
    {
       this->progressBar1->Value = e->ProgressPercentage;
    }
    

취소에 대한 지원을 구현하려면

  1. cancelAsyncButton 컨트롤의 Click 이벤트 처리기에서 비동기 작업을 취소하는 코드를 추가합니다.

    Private Sub cancelAsyncButton_Click( _
    ByVal sender As System.Object, _
    ByVal e As System.EventArgs) _
    Handles cancelAsyncButton.Click
    
        ' Cancel the asynchronous operation.
        Me.backgroundWorker1.CancelAsync()
    
        ' Disable the Cancel button.
        cancelAsyncButton.Enabled = False
    
    End Sub 'cancelAsyncButton_Click
    
    private void cancelAsyncButton_Click(System.Object sender, 
        System.EventArgs e)
    {   
        // Cancel the asynchronous operation.
        this.backgroundWorker1.CancelAsync();
    
        // Disable the Cancel button.
        cancelAsyncButton.Enabled = false;
    }
    
    void cancelAsyncButton_Click( System::Object^ /*sender*/, System::EventArgs^ /*e*/ )
    {  
       // Cancel the asynchronous operation.
       this->backgroundWorker1->CancelAsync();
    
       // Disable the Cancel button.
       cancelAsyncButton->Enabled = false;
    }
    
  2. ComputeFibonacci 메서드의 다음 코드 조각이 진행률을 보고하고 취소를 지원합니다.

    If worker.CancellationPending Then
        e.Cancel = True
    
    
    ...
    
    
    ' Report progress as a percentage of the total task.
    Dim percentComplete As Integer = _
        CSng(n) / CSng(numberToCompute) * 100
    If percentComplete > highestPercentageReached Then
        highestPercentageReached = percentComplete
        worker.ReportProgress(percentComplete)
    End If
    
    if (worker.CancellationPending)
    {   
        e.Cancel = true;
    }
    
    
    ...
    
    
    // Report progress as a percentage of the total task.
    int percentComplete = 
        (int)((float)n / (float)numberToCompute * 100);
    if (percentComplete > highestPercentageReached)
    {
        highestPercentageReached = percentComplete;
        worker.ReportProgress(percentComplete);
    }
    
    if ( worker->CancellationPending )
    {
       e->Cancel = true;
    }
    
    
    ...
    
    
    // Report progress as a percentage of the total task.
    int percentComplete = (int)((float)n / (float)numberToCompute * 100);
    if ( percentComplete > highestPercentageReached )
    {
       highestPercentageReached = percentComplete;
       worker->ReportProgress( percentComplete );
    }
    

검사점

이 시점에서 피보나치(Fibonacci) 계산기 응용 프로그램을 컴파일하고 실행할 수 있습니다.

프로젝트를 테스트하려면

  • F5 키를 눌러 응용 프로그램을 컴파일하고 실행합니다.

    백그라운드에서 계산이 실행되는 동안 완료될 때까지의 계산 진행률을 표시하는 ProgressBar가 표시됩니다. 또한 보류 중인 작업을 취소할 수 있습니다.

    숫자가 작은 경우 계산이 빠르지만 숫자가 큰 경우에 작업이 현격하게 지연되는 것을 볼 수 있습니다. 30 이상의 값을 입력하면 컴퓨터의 속도에 따라 몇 초의 시간이 지연됩니다. 40 이상의 값을 입력하면 계산을 완료하는 데 몇 분 또는 몇 시간이 걸릴 수 있습니다. 계산기로 큰 피보나치 수(Fibonacci number)를 계산하는 동안 폼을 자유롭게 이동, 최소화, 최대화할 수 있을 뿐만 아니라 폼을 닫을 수도 있습니다. 이는 주 UI 스레드가 계산이 완료될 때까지 기다리지 않기 때문입니다.

다음 단계

BackgroundWorker 구성 요소를 사용하여 백그라운드에서 계산을 실행하는 폼을 구현했으므로 비동기 작업에 대한 다른 예도 찾아볼 수 있습니다.

참고 항목

작업

방법: 백그라운드 작업을 사용하는 폼 구현

연습: 백그라운드에서 작업 실행

참조

BackgroundWorker

개념

관리되는 스레딩을 구현하는 최선의 방법

기타 리소스

구성 요소에서 다중 스레딩

Multithreading in Visual Basic

BackgroundWorker 구성 요소