Data Platform/Akka

Akka HelloWorld(Akka HTTP + Akka Actor연동)

태하팍 2025. 4. 15. 09:54
반응형

Akka HelloWorld 구현

build.sbt

ThisBuild / version := "0.1.0-SNAPSHOT"

ThisBuild / scalaVersion := "3.3.5"

lazy val root = (project in file("."))
  .settings(
    name := "HelloAkka",
    idePackagePrefix := Some("com.daum.www")
  )

libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-actor" % "2.6.20"
)


Akka System Source

 

package net.daum.www

import akka.actor.{Actor, ActorRef, ActorSystem, Props}

// 메시지 정의
case class Greet(name: String)

// 상태를 갖는 액터
class Greeter extends Actor:
  // 상태: 받은 메시지 수
  var count = 0

  def receive: Receive = {
    case Greet(name) =>
      count += 1
      println(s"[$count 번째] 안녕하세요, $name! 이것은 Akka Classic + Scala 3 예제입니다.")
  }

@main def runAkkaClassic(): Unit =
  val system: ActorSystem = ActorSystem("HelloClassicSystem")
  val greeter: ActorRef = system.actorOf(Props(classOf[Greeter]), "greeter")

  greeter ! Greet("태하팍")
  greeter ! Greet("카카오")
  greeter ! Greet("스칼라")
  greeter ! Greet("아카")

  Thread.sleep(1000)
  system.terminate()

Akka HelloWorld2 - main과 actor를 구분해보자!

Main.scala

// Main.scala
package net.daum.www

import akka.actor.{ActorSystem, Props}
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.duration.*
import scala.io.StdIn
import scala.util.{Failure, Success}

object Main:
  def main(args: Array[String]): Unit =
    val system = ActorSystem("HelloSystem")
    val helloActor = system.actorOf(Props[HelloActor](), name = "helloActor")

    given Timeout = Timeout(3.seconds)
    import system.dispatcher

    println("Enter a name to greet (or 'exit' to quit):")
    var input = StdIn.readLine()
    while input != "exit" do
      val future = helloActor ? input
      future.onComplete {
        case Success(response) => println(s"[response] $response")
        case Failure(ex) => println(s"[error] ${ex.getMessage}")
      }
      Thread.sleep(1000) // Wait a bit for response to print before next input
      input = StdIn.readLine()

    system.terminate()

HelloActor.scala

// HelloActor.scala
package net.daum.www

import akka.actor.{Actor, ActorSystem, Props}

class HelloActor extends Actor {
  def receive: Receive = {
    case name: String =>
      println(s"[Akka] Hello, $name!")
      sender() ! s"Hello, $name! (from Akka)"
    case _ =>
      println("[Akka] Unknown message received")
      sender() ! "Unknown message"
  }
}

Result

@main def runAkkaClassic(): Unit = ...

    • 간단하게 하나의 엔트리포인트 함수만 필요할 때 사용
    • Scala 3에서 새롭게 도입된 간결한 메인 함수 정의 방식
    • 클래스나 객체 없이 단독 함수로 실행 가능
    • 실행 시 runAkkaClassic이라는 이름으로 런타임에 잡힘
    • SBT로 run 할 때 자동으로 인식됨

object Main: def main(args: Array[String]): Unit = ...

    • Java 스타일의 main 메소드 정의 방식
    • 표준 JVM 앱처럼 public static void main(String[] args) 개념
    • object 안에 들어가야 함
    • def main(args: Array[String]) 형식을 정확히 따라야 함
    • Java나 다른 툴들과의 호환성이 좋음
    • 여러 main 메서드 중 1개만 선택적으로 실행하고 싶을 때 유용

Akka HTTP 연동해서  api로 호출해보자!

 build.sbt 
scalaVersion을 3.x에서 2.13.13으로 변경! (akka-http에서 버전 호환이 안맞음!)

ThisBuild / version := "0.1.0-SNAPSHOT"

ThisBuild / scalaVersion := "2.13.13"

lazy val root = (project in file("."))
  .settings(
    name := "HelloAkka",
    idePackagePrefix := Some("com.daum.www")
  )

libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-actor" % "2.6.20",
  "com.typesafe.akka" %% "akka-stream"        % "2.6.20",
  "com.typesafe.akka" %% "akka-http"          % "10.2.10",
  "com.typesafe.akka" %% "akka-http-spray-json" % "10.2.10" // JSON 쓰고 싶다면
)
// HelloActor.scala
package net.daum.www

import akka.actor.{Actor, Props}

class HelloActor extends Actor {
  def receive: Receive = {
    case name: String =>
      sender() ! s"Hello, $name! (from Akka)"
    case _ =>
      sender() ! "Unknown message"
  }
}
// Main.scala
package net.daum.www

import akka.actor.{ActorSystem, Props}
import akka.pattern.ask
import akka.util.Timeout
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import scala.concurrent.duration._
import scala.concurrent.Future
import scala.io.StdIn

object Main {
  def main(args: Array[String]): Unit = {
    implicit val system = ActorSystem("HelloSystem")
    implicit val executionContext = system.dispatcher
    implicit val timeout: Timeout = Timeout(3.seconds)

    val helloActor = system.actorOf(Props[HelloActor], name = "helloActor")

    val route = path("hello" / Segment) { name =>
      get {
        val response: Future[String] = (helloActor ? name).mapTo[String]
        complete(response)
      }
    }

    val bindingFuture = Http().newServerAt("localhost", 8080).bind(route)

    println("Server now online at http://localhost:8080/hello/태하팍\nPress RETURN to stop...")
    StdIn.readLine()

    bindingFuture
      .flatMap(_.unbind())
      .onComplete(_ => system.terminate())
  }
}

Result


위의 소스는 Akka HTTP와 Akka Actor를 연동한 기본 웹 서버 코드 입니다.

구조

  • ActorSystem 생성 → 액터 사용을 위한 환경 준비
  • helloActor 생성 → 이름을 입력하면 인사 메시지를 주는 액터
  • HTTP 경로 /hello/{name} 등록 → 해당 경로 요청 시 액터에게 메시지를 보내고 응답 받음
  • 웹 서버 실행localhost:8080 에서 서버 시작
  • 콘솔에서 엔터 입력 시 서버 종료
implicit val system = ActorSystem("HelloSystem")
implicit val executionContext = system.dispatcher
implicit val timeout: Timeout = Timeout(3.seconds)
  • ActorSystem: 액터를 만들고 관리할 수 있는 환경 (HelloSystem이라는 이름)
  • executionContext: 비동기 처리용 스레드풀 (Future 연산 등)
  • timeout: ask 패턴에서 응답을 기다릴 시간 설정

액터생성

val helloActor = system.actorOf(Props[HelloActor], name = "helloActor")

Http Route 정의

  • GET /hello/{name} 요청이 오면:
    1. helloActorname을 메시지로 전달 (?는 ask 패턴)
    2. 응답이 Future[String]으로 오면 그대로 클라이언트에게 전송
val route = path("hello" / Segment) { name =>
  get {
    val response: Future[String] = (helloActor ? name).mapTo[String]
    complete(response)
  }
}

서버 시작

  • HTTP 서버를 localhost:8080에 열고, 앞서 정의한 route에 따라 요청 처리
val bindingFuture = Http().newServerAt("localhost", 8080).bind(route)

서버 유지 및 종료 대기

  • 콘솔에서 엔터 치기 전까지 서버는 계속 실행됨
println("Server now online at http://localhost:8080/hello/태하팍")
StdIn.readLine()

서버 종료 처리

  • 엔터 입력 시:
    1. 서버 언바인드 (연결 끊기)
    2. ActorSystem 종료 (앱 종료)
bindingFuture
  .flatMap(_.unbind())
  .onComplete(_ => system.terminate())
반응형