(μŠ€ν”„λ§) RequestContextHolder

RequestContextHolder λŠ” μš”μ²­λ‹Ή λ°œμƒλ˜λŠ” μŠ€λ ˆλ“œμ— λ°”μΈλ”©λ˜μ–΄ RequestAttributes λ₯Ό key/value 쌍으둜 가지고 μžˆλ‹€. 개발적인 츑면에선 λ ˆμ΄μ–΄μ— ꡬ뢄없이 μŠ€νƒœν‹±ν•˜κ²Œ μ ‘κ·Όν•  수 μžˆλ‹€. ν•˜μ§€λ§Œ λ©”μΈμŠ€λ ˆλ“œλ₯Ό λ²—μ–΄λ‚œ 별도 μŠ€λ ˆλ“œν’€μ—μ„œ κΊΌλ‚Έ λ‹€λ₯Έ μŠ€λ ˆλ“œμ—μ„œ RequestContextHolder 에 μ ‘κ·Όν•˜μ—¬ 값을 κΊΌλ‚΄λ €κ³  ν•˜λ©΄ null 이 λ‚˜μ˜¨λ‹€. 이λ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄μ„  인터넷에 μ—¬λŸ¬ 방법듀이 μžˆλŠ” 걸둜 보인닀. λ‹€λ§Œ μ§κ΄€μ μ΄λ©΄μ„œ 영ν–₯도λ₯Ό κ°€μž₯ μ΅œμ†Œν™”ν•˜λŠ” 방법은 μ „λ‹¬λ˜λŠ” Param 객체에 RequsetAttributes λ‚΄μš©μ„ 인자둜 같이 λ„˜κ²¨μ„œ μˆ˜μ‹ λ°›λŠ” μ½”λ“œμͺ½μ— μ•„λž˜μ™€ 같이 μž‘μ„±ν•˜λ©΄ ν•΄μ†Œκ°€ λœλ‹€.

fun asyncMethod(param: Param) {
	// ν• λ‹Ή
    RequestContextHolder.setRequestAttributes(param.requestAttributes)
    
    // .. logic ..
    
    // λͺ…μ‹œμ  ν•΄μ œ
    RequestContextHolder.resetRequestAttributes()
}

 

 

(μŠ€ν”„λ§) @TransactionalEventListener

μ„œλΉ„μŠ€μ—μ„œ 객체의 μƒνƒœμ˜ λ³€κ²½ ν˜Ήμ€ λΉ„μ¦ˆλ‹ˆμŠ€ 둜직으둜 μΈν•˜μ—¬ μ½œλ°±μ΄λ‚˜ ν˜Ήμ€ 이벀트 μ „νŒŒκ°€ ν•„μš”ν•œ κ²½μš°κ°€ μžˆλ‹€. 이 λ•Œ λ ˆμ΄μ–΄κ°„ 도메인을 μΉ¨λ²”ν•˜μ§€ μ•Šκ³  싢을 λ•Œ μ“°λŠ” EventListener 의 νŠΈλžœμž­μ…˜ 버전이닀. νŠΈλžœμž­μ…˜μ΄ μ œκ³΅λ˜μ§„ μ•Šκ³  νŠΈλžœμž­μ…˜μ˜ μ „/후에 λ”°λΌμ„œ 이벀트 호좜 μ‹œμ μ„ μ‘°μ •ν•  수 μžˆλ‹€. μ΄λ²ˆμ— 처음 μ‚¬μš©ν•΄λ³Έκ±΄ @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) 이닀. 이걸 μ‚¬μš©ν•˜λ©΄ νŠΈλžœμž­μ…˜μ˜ λ™μž‘μ΄ λλ‚˜κ³  이후에 EventListener κ°€ λ™μž‘ν•œλ‹€. ν•˜μ§€λ§Œ ν•΄λ‹Ή 이벀트 μ˜μ—­μ—μ„  νŠΈλžœμž­μ…˜ μˆ˜ν–‰μ„ μ‹œν‚¬ 수 μ—†λ‹€. μ–΅μ§€λ‘œ μˆ˜ν–‰μ‹œν‚€λ©΄ μ•„λž˜μ™€ 같은 μ—λŸ¬λ₯Ό λ§Œλ‚œλ‹€.

ERROR 54966 --- [io-48081-exec-1] o.s.t.s.TransactionSynchronizationUtils  
: TransactionSynchronization.afterCompletion threw exception
org.springframework.dao.InvalidDataAccessApiUsageException
: no transaction is in progress

 

νŠΈλžœμž­μ…˜μ„ λ™μž‘ν•˜κ³  μ‹ΆμœΌλ©΄ @TransactionalEventListener κ°€ 뢙은 λ©”μ†Œλ“œμ— λ‹€λ₯Έ μ• λ…Έν…Œμ΄μ…˜μ„ λΆ™μ—¬μ£Όμ–΄μ•Ό λ™μž‘ν•œλ‹€. 방법은 두가지닀.

(1) @Transactional(propagation = Propagation.REQUIRES_NEW) λ₯Ό 같이 λΆ™μ—¬μ€€λ‹€.

(2) @Async + @Transactional λ₯Ό 같이 λΆ™μ—¬μ€€λ‹€.

 

(1) 은 μ‹ κ·œ νŠΈλžœμž­μ…˜μ„ λ§Œλ“€μ–΄ μ²˜λ¦¬ν•˜λŠ” 것이고 (2) λŠ” 비동기 μŠ€λ ˆλ“œν’€μ—μ„œ λ‹€λ₯Έ μŠ€λ ˆλ“œμ—μ„œ νŠΈλžœμž­μ…˜μ„ μ²˜λ¦¬ν•˜λŠ” 것이닀. ν•„μš”μ— 따라 μ‚¬μš©ν•˜λ„λ‘ ν•œλ‹€. (+ μ΅μ…‰μ…˜ 핸듀링은 λ‹Ήμ—°ν•˜κ²Œ ν•˜μž.)

 

 

(kotlin) ν™•μž₯ν•¨μˆ˜ λ‘œκ·Έμž‘μ„±

ν™•μž₯ν•¨μˆ˜μ—μ„œ 둜그λ₯Ό 써야할 κ²½μš°κ°€ μžˆμ„ 수 있음. 클래슀의 κ²½μš°λŠ” ν•„λ“œκ°’μœΌλ‘œ μ„ μ–Έμ²˜λ¦¬ν•  수 μžˆμ§€λ§Œ ν™•μž₯ν•¨μˆ˜λŠ” ν΄λž˜μŠ€λ‚΄ ν•„λ“œλ‘œ μ‘΄μž¬ν•  수 μ—†λ‹€. λ”°λΌμ„œ κ·Έλƒ₯ static ν•˜κ²Œ μ˜¬λ €λ†“κ³  μ¨μ•Όν•œλ‹€. μŠ€ν”„λ§μ΄λž‘ 같이 μš΄μš©ν•  λ•Œ 사싀 λ‘œκΉ… 객체도 빈 μ•ˆμ— μ„ μ–Έλœ ν˜•νƒœλ‘œ 쓰이기 λ•Œλ¬Έμ— κ·Έκ²ƒλ˜ν•œ κ²°κ΅­ static ν•˜λ‹€κ³  μƒκ°ν•œλ‹€. (μ•„λž˜ μ½”λ“œλ₯Ό λ””μ»΄νŒŒμΌν•˜λ©΄ logger κ°μ²΄λŠ” static {} λΈ”λŸ­μ•ˆμ— μ²˜λ¦¬λ˜μ–΄μžˆλ‹€.)

// kotlin κΈ°μ€€.
class CustomExtension

val logger: Logger  = LoggerFactory.getLogger(CustomExtension::class.java)

fun Person.toBatMan(): Person {
    logger.info("person -> batman")
    return this.copy(
        name = "batman",
        age = 99999
    )
}

// λ‘œκ·ΈλŠ” μ•„λž˜μ™€ 같이 좜λ ₯λœλ‹€.
// 00:41:47.496 [main] INFO com.example.springbootbasis.practice.logger.CustomExtension -- person -> batman

 

 

(μ΄νŽ™ν‹°λΈŒ μ½”ν‹€λ¦°) μ•ˆμ •μ„± μžˆλŠ” μ½”λ“œλ₯Ό λ§Œλ“€κΈ°

require/check/assert λ₯Ό μ΄μš©ν•˜μ—¬ μ˜ˆμ™Έλ₯Ό μΌμœΌν‚€κ³  μ½”λ“œμ— μ œν•œμ„ λ‘μž. μ—¬κΈ°μ„œ assert λŠ” μ œμ™Έν•˜κ³  require/check 만 보렀고 ν•œλ‹€.

 

require : μ•„κ·œλ¨ΌνŠΈμ— μ œν•œμ„ 두기

- age λΌλŠ” ν•„λ“œκ°’μ€ μŒμˆ˜κ°€ 될 수 μ—†λ‹€κ³  μ œν•œμ„ 두고 μŒμˆ˜κ°€ λ˜λŠ” μΌ€μ΄μŠ€μ˜ 경우 IllegalArgumentException μ—λŸ¬λ₯Ό λ°œμƒμ‹œν‚¨λ‹€.

fun main() {
     Person(age = -1, "홍길동")
}

data class Person(
    val age: Int,
    val name: String
) {
    init {
        require(age >= 0) {
            "λ‚˜μ΄λŠ” μŒμˆ˜κ°€ 될 수 μ—†μŠ΅λ‹ˆλ‹€. age=$age"
        }
    }
}

Exception in thread "main" java.lang.IllegalArgumentException: λ‚˜μ΄λŠ” μŒμˆ˜κ°€ 될 수 μ—†μŠ΅λ‹ˆλ‹€. age=-1

 

check : μƒνƒœκ°’ 쑰건을 확인

- isOrdered μƒνƒœκ°’μ΄ true 인 κ²½μš°μ— IllegalStateException μ—λŸ¬λ₯Ό λ°œμƒμ‹œν‚¨λ‹€.

fun main() {
    val order = Order().apply { this.isOrdered = true }
    order.ordered()
}


class Order {
    var isOrdered: Boolean = true

    fun ordered() {
        check(isOrdered.not()) {
            "주문된 μƒνƒœμž…λ‹ˆλ‹€. ordered=$isOrdered"
        }
    }
}

 

 

(μ΄νŽ™ν‹°λΈŒ μ½”ν‹€λ¦°) μ΄ˆκΈ°ν™”λ₯Ό μ§€μ—°μ‹œν‚¨λ‹€λ©΄ λͺ…μ‹œμ μœΌλ‘œ μ•Œλ¦°λ‹€.

κΈ°λ³Ένƒ€μž…μ—λŠ” Dellgates.notNull() 을 μ‚¬μš©ν•œλ‹€. κ·Έ μ™Έ λ ˆνΌλŸ°μŠ€μ—λŠ” lateinit var 을 μ‚¬μš©ν•œλ‹€. !! μ—°μ‚°μžλ₯Ό μ§€μ–‘ν•˜λΌκ³  μ±…μ—μ„œ ν•œλ‹€. 정적뢄석도ꡬ인 https://github.com/detekt/detekt μ΄λΌλŠ” μ˜€ν”ˆμ†ŒμŠ€λ₯Ό μΆ”μ²œν•΄μ€Œ

Posted by doubler
,