티스토리 뷰

개요

  • REST API를 설계하다보면, 하나의 API 요청 사이클을 처리함에 있어 가장 앞 단계에서 이미 유효한 인증을 통해 획득했던 회원 오브젝트를 반복적으로 다시 획득해야 문제에 부딪히게 된다. (데이터베이스에서 다시 조회하는 것은 퍼포먼스 측면에서 비효율적이다.) 일반적으로 하나의 요청이 하나의 쓰레드로 처리되는 것을 감안하면, 특정 쓰레드의 생명 주기 안에서 전역으로 접근할 수 있는 변수를 설계하면 이러한 상황에서 고민 없이 재사용할 수 있을 것이다. 이번 글에서는 이 방법을 통해 하나의 쓰레드 안에서 특정 오브젝트를 재사용할 수 있는 방법을 예제를 통해 소개하고자 한다.

작성 예

  • 아래는 하나의 쓰레드 안에서 전역으로 참조할 수 있는 RequstContext라는 클래스를 작성한 예이다. 프로덕션 레벨에서는 회원 정보 외에도 특정 요청과 관련된 여러 오브젝트를 설계하는데 본 예제에서는 설명을 위해 간소화했다. (예를 들면 트랜잭션 ID가 대표적이다.)
class RequestContext {

    // companion object 블록을 작성하면 오브젝트 생성 없이 블록 내의 멤버를 클래스 레벨로 사용 가능
    companion object {

        // User 오브젝트를 동일 쓰레드 범위에서 접근할 수 있도록 ThreadLocal<T> 제너릭 타입으로 프라이빗 멤버 선언
        // 저장된 오브젝트는 동일 쓰레드의 생명주기와 같이 함
        private val user = ThreadLocal<User?>()

        // 프라이빗 멤버인 user를 실제로 제어할 퍼블릭 멤버 선언
        var USER: User?
            get() = user.get()
            set(user: User?) {
                Companion.user.set(user)
            }

        // 쓰레드 풀의 쓰레드가 재사용되면서 과거의 값이 사용되지 않도록 제거 조치
        fun remove() {
            user.remove()
        }
    }
}
  • companion object 블록을 작성하면 블록 안에 작성된 멤버를 오브젝트 생성 없이 접근할 수 있다.
  • ThreadLocal<T> 제너릭 타입으로 멤버를 선언하면 특정 쓰레드의 생명 주기 안에서 자유롭게 변수를 할당하고 획득할 수 있다. 주의할 점은 2가지가 있는데 첫째, 해당 쓰레드를 벗어나면 접근할 수 없다. (쓰레드 로컬이라는 네이밍을 생각하면 너무 당연하다.) 둘째는 해당 쓰레드를 사용 완료한 시점에는 반드시 멤버 값을 초기화해야 한다. 그렇지 않을 경우, 쓰레드 풀에 의해 다른 요청에서 재사용되면서 이전에 사용된 값 또한 그대로 노출될 수 있다.

사용 예

  • 사용 방법은 아래와 같다.
// User 오브젝트 저장
RequestContext.USER = userService.getUserById({user-id})

// 저장된 User 오브젝트 획득
val user: User? = RequestContext.USER

// 저장된 모든 오브젝트 제거
RequestContext.remove()

참고 글

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함