IMCAT.DEV
로그인 가입
Scala 중급 비동기

Scala 동시성 프로그래밍 기초

Scala 동시성 프로그래밍 기초 — 중급

동시성 프로그래밍 기초

1. 도입 (왜 배워야 하는가)

동시성 프로그래밍은 현대 소프트웨어 개발에서 필수적인 기술 중 하나입니다. 특히, 대규모 분산 시스템이나 멀티스레드 애플리케이션을 개발할 때 동시성을 효과적으로 활용해야 합니다. 예를 들어, 쇼핑몰 웹사이트에서 사용자가 상품을 장바구니에 추가할 때, 여러 사용자가 동시에 요청을 보낼 수 있습니다. 이때, 동시성 프로그래밍을 통해 각각의 요청을 효율적으로 처리하지 않으면 데이터 충돌이나 응답 지연이 발생할 수 있습니다.

동시성 프로그래밍을 모르면 다음과 같은 문제가 발생할 수 있습니다:

  • 데이터 무결성 문제: 여러 스레드가 동시에 데이터에 접근하여 수정할 경우, 예기치 않은 결과가 발생할 수 있습니다.
  • 성능 저하: 동시성을 활용하지 않으면 자원 낭비가 발생하고, 시스템 성능이 저하될 수 있습니다.
  • 2. 핵심 개념 설명

    동시성 프로그래밍에서 이해해야 할 몇 가지 핵심 개념은 다음과 같습니다.

    개념정의
    스레드프로그램의 실행 단위로, 여러 스레드가 동시에 실행될 수 있음.
    동기화여러 스레드가 공유 자원에 접근할 때 발생할 수 있는 문제를 해결하기 위한 기술.
    경량 스레드더 적은 자원으로 스레드를 생성할 수 있는 기능. Scala의 Future와 같은 기능이 이에 해당.

    비유하자면, 스레드는 여러 요리사가 동시에 주방에서 요리를 하는 것과 같습니다. 그런데 만약 여러 요리사가 같은 냄비를 사용하려고 할 때, 누가 먼저 사용할지를 정해야 합니다. 이때 '동기화' 기술이 필요합니다.

    3. 기본 코드 예제

    아래는 Scala에서 스레드를 생성하는 가장 기본적인 예제입니다.

    import scala.concurrent.{Future, Await}
    import scala.concurrent.ExecutionContext.Implicits.global
    import scala.concurrent.duration._
    
    val futureTask = Future {
      // 비동기 작업
      Thread.sleep(1000) // 1초 대기
      "작업 완료"
    }
    
    // 결과를 기다림
    val result = Await.result(futureTask, 2.seconds)
    println(result) // 출력: 작업 완료

    실행 결과:

    작업 완료

    코드 설명:

    1. import scala.concurrent.{Future, Await}: Future와 Await를 사용하기 위해 필요한 라이브러리를 가져옵니다.
    2. val futureTask = Future { ... }: Future를 사용하여 비동기 작업을 정의합니다.
    3. Thread.sleep(1000): 1초 동안 실행을 멈춥니다. (여기서는 비동기 작업의 예시로 사용)
    4. Await.result(futureTask, 2.seconds): futureTask의 결과를 기다립니다. 최대 2초까지 기다립니다.
    5. println(result): 비동기 작업의 결과를 출력합니다.
    6. 4. 실전 활용 예제

      예제 1: 쇼핑몰 장바구니 구현

      쇼핑몰에서 사용자가 상품을 장바구니에 추가할 때, 여러 사용자가 동시에 요청할 수 있습니다. 이때, 각 요청이 독립적으로 처리되도록 동시성을 활용할 수 있습니다.

      import scala.concurrent.{Future, Await}
      import scala.concurrent.ExecutionContext.Implicits.global
      import scala.concurrent.duration._
      import scala.collection.mutable
      
      object ShoppingCart {
        val cart: mutable.ListBuffer[String] = mutable.ListBuffer()
      
        def addItem(item: String): Unit = {
          Future {
            // 동시성 문제를 피하기 위한 동기화
            this.synchronized {
              cart += item
            }
            println(s"$item 추가됨")
          }
        }
      }
      
      // 사용 예
      ShoppingCart.addItem("상품 A")
      ShoppingCart.addItem("상품 B")
      
      // 결과를 기다림
      Thread.sleep(1000) // 모든 작업이 완료될 때까지 대기
      println(ShoppingCart.cart) // 출력: ListBuffer(상품 A, 상품 B)

      예제 2: 데이터 처리

      대량의 데이터를 비동기적으로 처리할 때 동시성을 활용할 수 있습니다.

      import scala.concurrent.{Future, Await}
      import scala.concurrent.ExecutionContext.Implicits.global
      import scala.concurrent.duration._
      
      val dataList = List(1, 2, 3, 4, 5)
      
      val processedData = dataList.map { data =>
        Future {
          // 데이터 처리 (여기선 간단히 2배로 만듦)
          Thread.sleep(500) // 0.5초 대기
          data * 2
        }
      }
      
      // 모든 Future의 결과를 기다림
      val result = Future.sequence(processedData)
      Await.result(result, 10.seconds).foreach(println) // 출력: 2, 4, 6, 8, 10

      5. 흔한 실수와 주의사항

      실수 1: 동기화 문제

    7. 잘못된 코드
    8.   // 동기화 없이 공유 자원에 접근
        def addItem(item: String): Unit = {
          cart += item
        }
    9. 올바른 코드
    10.   def addItem(item: String): Unit = {
          this.synchronized {
            cart += item
          }
        }

      실수 2: Future 결과 처리 누락

    11. 잘못된 코드
    12.   val futureTask = Future {
          Thread.sleep(1000)
          "작업 완료"
        }
        // 결과를 기다리지 않음
    13. 올바른 코드
    14.   val result = Await.result(futureTask, 2.seconds)
        println(result)

      6. 정리

    15. 동시성 프로그래밍은 대규모 시스템에서 데이터 무결성을 유지하는 데 필수적입니다.
    16. 스레드와 동기화 개념을 이해해야 합니다.
    17. Scala에서 Future를 사용하면 비동기 작업을 쉽게 처리할 수 있습니다.
    18. 코드 작성 시 동기화와 결과 처리를 신경 써야 합니다.
    19. 다음에 학습하면 좋은 주제:

    20. Akka로 배우는 분산 시스템
    21. Scala의 비동기 프로그래밍 패턴
    22. Reactive Programming에 대한 이해