콘텐츠로 이동

2025 02 17

2025-02-17

LocalDateTime vs Date

  • Date
    • 1970.01.01 이후의 밀리초 저장
    • mutable => deprecated
  • LocalDateTime
    • immutable, thread-safe
    • 장점
      1. 불변성 : 한번 생성시 값 변경 X
      2. 직관적인 API : 날짜, 시간, 타임스탬프, 시간대 구분하여 사용 가능. Date는 단지 타임스탬프 wrapper.
      3. 시간대 관리 명시성 : 시간대 포함 안하니, 시간대 필요없는 경우에 더 좋음
  • Timestamp
    • Unix Epoch(1970.01.01 00:00:00 UTC) 이후 경과한 시간을 나타냄. 밀리초/초단위로 표기
    • Date -> Timestamp
      • 객체 내부적으로 이미 epoch 기준의 밀리초 값을 저장하고 있음
    • LocalDateTime -> Timestamp
      • 타임존 정보가 없어, 타임스탬프 변환을 위해서는 어떤 타임존 기준으로 해석할 것인지 명시 필요.
        def toTimestamp(value: LocalDateTime): Long = value.atZone(ZoneId.systemDefault).toInstant.toEpochMilli
        

Play 버전 업에 따른 Akka 변경 포인트

참고: https://www.playframework.com/documentation/3.0.x/Highlights28

  • [AS-IS: Play 2.5.19]
    1. 글로벌 ActorSystem 접근
    2. 설정 통합 방식
    3. 암묵적 관리 - 전역 ActorSystem 사용
      class MyActor extends Actor {
        def receive = {
          case msg => println(s"Received: $msg")
        }
      }
      
      object MyActorExample {
        // Play가 내부적으로 생성한 전역 ActorSystem 사용
        val actorSystem = Akka.system
      
        // 액터 생성
        val myActor = actorSystem.actorOf(Props[MyActor], "myActor")
      
        def sendMessage(msg: String): Unit = {
          myActor ! msg
        }
      }
      
  • [TO-BE: Play 2.8.9]
    1. 명시적 DI 기반 ActorSystem
    2. 업데이트된 Akka 버전
    3. 설정 병합 및 구성 변화
    4. 라이프사이클 관리 강화
  • 변경된 사용 방법

    • ActorSystem 주입 예시
      class MyActor extends Actor {
        def receive = {
          case msg => println(s"Received: $msg")
        }
      }
      
      @Singleton
      class MyActorExample @Inject()(actorSystem: ActorSystem) {
        // DI를 통해 주입된 ActorSystem 사용한 액터 생성
        private val myActor = actorSystem.actorOf(Props[MyActor], "myActor")
      
        def sendMessage(msg: String): Unit = {
          myActor ! msg
        }
      }
      
    • Actor 바인딩을 위한 Guice Module (AkkaGuideSupport 활용)

      class Module extends AbstractModule with AkkaGuiceSupport {
        override def configure(): Unit = {
          bindActor[MyActor]("myActor") // "myActor"라는 이름으로 MyActor 바인딩
        }
      }
      
      @Singleton
      class MyService @Inject()(myActor: ActorRef) {
        def sendMessage(msg: String): Unit = {
          myActor ! msg
        }
      }
      

      1. 단일 ActorSystem 사용
        • 일반적으로 Play에는 단일 ActorSystem이 생성되어 DI 컨테이너에 바인딩. 어플리케이션 전체에서 액터 생성/관리
      2. 액터 생성 및 바인딩: bindActor[MyActor]("myActor")
        • DI 컨테이너(Guice)에 "myActor"라는 이름으로 ActorRef 바인딩
        • 내부적으로는 DI 컨테이너(Guice)에 이미 바인딩된 기본 ActorSystem을 주입받아, 그 ActorSystem 내에서 MyActor 액터 인스턴스 생성
          • ActorSystem에서 액터 인스턴스를 생성, ActorRef를 DI 컨테이너에 바인딩
        • 생성된 액터의 ActorRef가 Guice의 바인딩에 등록되어, 나중에 @Named("myActor")로 주입
      3. DI를 통한 주입
        • 타 클래스에서 @Named("myActor") 활용해 ActorRef 주입 시, Guice는 바인딩된 액터 참조 찾아 주입
      4. asEagerSingleton()
        • Guice가 어플리케이션 시작 지점에 즉시 인스턴스화 하도록 하는 설정
        • 일반적인 싱글톤은 처음 요청될 때 생성, eager singleton은 DI 컨테이너가 생성되는 시점에 미리 인스턴스 만들어 초기화 로직 실행.
          • Guice는 기본적으로 싱글톤 바인딩을 요청 시점에 생성한다...
          • (ex. @Singleton 붙여두면, 실제 주입 요청이 발생할 때 생성됨. (아하 이러면 거의다 load 시점에 주입되겠네 생성되고.))
        • 바로 생성해서 스케줄링 등 지연없이 뚝딱
  • ActorSystem
    • 액터를 생성, 관리, 실행하는 컨테이너
    • 역할
      1. 액터 생성 및 관리
        • 액터를 생성하는 팩토리 역할. 생성시 반드시 어떤 ActorSystem에서 생성할 것
        • 생성된 액터들을 트리 구조로 관리.
          • 상위 액터가 하위 액터의 오류를 감지하고 재시작하는 등 생명 주기 관리
      2. 메시지 디스패칭
        • 액터들이 메시지 서로 주고 받을 때, 메시지 전달, 스케줄링, 디스패칭 등 담당
        • 내부적으로 쓰레드풀 활용하여 액터에게 메시지 전달/처리
      3. 라이프사이클 관리
        • ActorSystem -> 생성/종료의 책임
        • 어플리케이션 종료시 모든 액터 종료, 관련 리소스 해제
        • 시스템 전체의 초기화/정리 담당
      4. 구성 및 설정 관리
        • application.conf 파일을 통해 Akka 관련 설정 관리, 이를 기반으로 내부 동작 방식 설정
    • Play 2.8에서는 ActorSystem이 DI 컨테이너에 싱글톤으로 등록됨에 따라, Play의 내장 라이프사이클에 의해 자동 관리 (ApplicationLifecyle)
      • 별도 GuiceApplicationLoader에서 액터 바인딩/stop 필요 없이, 모듈에만 등록하면 OK
  • 업그레이드시 고려할 포인트
    1. DI 방식으로의 전환
    2. application.conf 및 Akka 설정 업데이트
    3. Akka API 변경사항 점검
    4. 라이프사이클 및 종료 처리
  • 왜 DI 방식으로 변했을까...
    • 전역 방식의 문제
      • 암묵적 의존성 : 액터와 ActorSystem이 전역 상태로 관리, 어느 클래스가 어느 전역 객체에 의존하는지 코드상 명확 X
      • 테스트 어려움 : mock 객체 (mockito)로 객체지향적으로 테스트 분리해서 진행하기 어려운건 알고...
      • 유지보수 복잡성 : 변경을 했을 때 영향 범위가 어디까지인지, 어플리케이션 전반에 영향 미칠 수 있으니 조심스러워짐