[Spring] Transaction, Spring์—์„œ ์ œ๊ณตํ•˜๋Š” Transaction ํ•ต์‹ฌ ๊ธฐ์ˆ 

๐Ÿ”น Transaction

[ ํŠธ๋žœ์žญ์…˜(Transaction)์˜ ํ•„์š”์„ฑ ]

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๊ณผ์ •์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด, ๋ฐ์ดํ„ฐ๊ฐ€ ๋ถˆ์™„์ „ํ•œ ์ƒํƒœ๋กœ ๋‚จ์ง€ ์•Š๋„๋ก ์ˆ˜์ • ์ด์ „์˜ ์ƒํƒœ๋กœ ๋˜๋Œ๋ฆฐ ํ›„, ๋‹ค์‹œ ์ˆ˜์ • ์ž‘์—…์ด ์ด๋ฃจ์–ด์ ธ์•ผ ํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ์—ฌ๋Ÿฌ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋‹ค ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ, ์ด์ „ ์ƒํƒœ๋กœ ๋˜๋Œ๋ฆฌ๋Š” ๋กค๋ฐฑ(Rollback) ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด ํŠธ๋žœ์žญ์…˜(Transaction)์ด๋‹ค.

 

ํŠธ๋žœ์žญ์…˜์€ ํ•˜๋‚˜์˜ ๋…ผ๋ฆฌ์ ์ธ ์ž‘์—… ๋‹จ์œ„๋กœ, ๋ฐ˜๋“œ์‹œ Commit(์ปค๋ฐ‹) ๋˜๋Š” Rollback(๋กค๋ฐฑ)์œผ๋กœ ๋งˆ๋ฌด๋ฆฌ๋˜์–ด์•ผ ํ•œ๋‹ค. ์ฆ‰, ํŠธ๋žœ์žญ์…˜์ด ์ •์ƒ์ ์œผ๋กœ ์™„๋ฃŒ๋˜๋ฉด ์ปค๋ฐ‹์„ ์ˆ˜ํ–‰ํ•˜์—ฌ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ํ™•์ •ํ•˜๊ณ , ๋„์ค‘์— ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋กค๋ฐฑ์„ ์ˆ˜ํ–‰ํ•˜์—ฌ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ทจ์†Œํ•œ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋„์ค‘์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธด๋‹ค๋ฉด, ํ•ด๋‹น ์ž‘์—…์€ ์—†์—ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ ์ทจ์†Œ๋œ๋‹ค. ํ•˜์ง€๋งŒ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ž‘์—…์ด ์—ฐ์†์ ์œผ๋กœ ์ด๋ฃจ์–ด์ง€๋Š” ๊ฒฝ์šฐ, ๋ถ€๋ถ„์ ์œผ๋กœ๋งŒ ๋ณ€๊ฒฝ์ด ๋ฐ˜์˜๋˜์ง€ ์•Š๋„๋ก ์ „์ฒด ์ž‘์—…์„ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ฌถ์–ด ๊ด€๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.

 

ํŠธ๋žœ์žญ์…˜์˜ ์ข…๋ฃŒ ๋ฐฉ์‹์€ ํฌ๊ฒŒ ๋‘ ๊ฐ€์ง€๋กœ ๋‚˜๋‰œ๋‹ค.

1. ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹ (Commit) : ๋ชจ๋“  ์ž‘์—…์ด ์ •์ƒ์ ์œผ๋กœ ์™„๋ฃŒ๋˜์—ˆ์„ ๋•Œ, ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜ํ•œ๋‹ค.

2. ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ (Rollback) : ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ฑฐ๋‚˜ ์ž‘์—…์„ ์ทจ์†Œํ•ด์•ผ ํ•  ๊ฒฝ์šฐ, ๋ณ€๊ฒฝ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์›๋ž˜ ์ƒํƒœ๋กœ ๋˜๋Œ๋ฆฐ๋‹ค.

 

์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ž‘์—…์ด ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ฌถ์–ด ์žˆ๋Š” ๊ฒฝ์šฐ, ๋ชจ๋“  ์ž‘์—…์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ˆ˜ํ–‰๋˜์—ˆ์„ ๋•Œ๋งŒ ์ปค๋ฐ‹์„ ์‹คํ–‰ํ•˜์—ฌ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ฐ˜์˜ํ•ด์•ผ ํ•œ๋‹ค. ๋งŒ์•ฝ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋กค๋ฐฑ์„ ์ˆ˜ํ–‰ํ•˜์—ฌ ๋ฐ์ดํ„ฐ์˜ ์ผ๊ด€์„ฑ๊ณผ ๋ฌด๊ฒฐ์„ฑ์„ ์œ ์ง€ํ•ด์•ผ ํ•œ๋‹ค.

 

[ ํŠธ๋žœ์žญ์…˜ ACID ]

ํŠธ๋žœ์žญ์…˜์€ ๋‹ค์Œ ๋„ค ๊ฐ€์ง€ ํŠน์„ฑ์„ ํ†ตํ•ด ๋ฐ์ดํ„ฐ์˜ ์‹ ๋ขฐ์„ฑ๊ณผ ์•ˆ์ •์„ฑ์„ ๋ณด์žฅํ•œ๋‹ค.

1. ์›์ž์„ฑ (Atomicity)

  • ํŠธ๋žœ์žญ์…˜ ๋‚ด์˜ ๋ชจ๋“  ์ž‘์—…์€ ์™„์ „ํžˆ ์‹คํ–‰๋˜๊ฑฐ๋‚˜ ์ „ํ˜€ ์‹คํ–‰๋˜์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค.
  • ์ฆ‰, ํ•˜๋‚˜์˜ ์ž‘์—…์ด๋ผ๋„ ์‹คํŒจํ•˜๋ฉด ์ „์ฒด ํŠธ๋žœ์žญ์…˜์ด ๋กค๋ฐฑ(Rollback)๋˜์–ด ๋ถ€๋ถ„ ์‹คํ–‰ ์ƒํƒœ๊ฐ€ ๋‚จ์ง€ ์•Š๋Š”๋‹ค.

2. ์ผ๊ด€์„ฑ (Consistency)

  • ํŠธ๋žœ์žญ์…˜ ์‹คํ–‰ ์ „ํ›„๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ฌด๊ฒฐ์„ฑ๊ณผ ์ •ํ•ฉ์„ฑ์ด ์œ ์ง€๋˜์–ด์•ผ ํ•œ๋‹ค.
  • ์˜ˆ๋ฅผ ๋“ค์–ด, ์€ํ–‰ ๊ณ„์ขŒ์—์„œ ๋ˆ์„ ์ด์ฒดํ•  ๋•Œ, ํ•œ ๊ณ„์ขŒ์—์„œ ๋น ์ ธ๋‚˜๊ฐ„ ๊ธˆ์•ก๊ณผ ๋‹ค๋ฅธ ๊ณ„์ขŒ์— ์ž…๊ธˆ๋œ ๊ธˆ์•ก์ด ํ•ญ์ƒ ์ผ์น˜ํ•ด์•ผ ํ•œ๋‹ค.

3. ๊ณ ๋ฆฝ์„ฑ(Isolation)

  • ์—ฌ๋Ÿฌ ํŠธ๋žœ์žญ์…˜์ด ๋™์‹œ์— ์‹คํ–‰๋  ๋•Œ, ์„œ๋กœ ๊ฐ„์„ญํ•˜์ง€ ์•Š๋„๋ก ๋ณด์žฅํ•ด์•ผ ํ•œ๋‹ค.
  • ์ฆ‰, ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์ด ์™„๋ฃŒ๋˜๊ธฐ ์ „์— ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋‹ค.
  • ์ด๋ฅผ ์œ„ํ•ด ํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€(Isolation Level)์„ ์„ค์ •ํ•˜์—ฌ ๋™์‹œ์„ฑ ์ œ์–ด๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

4. ์ง€์†์„ฑ(Durability)

  • ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋œ ํŠธ๋žœ์žญ์…˜์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ์˜๊ตฌ์ ์œผ๋กœ ์ €์žฅ๋˜์–ด์•ผ ํ•œ๋‹ค.
  • ์‹œ์Šคํ…œ ์žฅ์• ๋‚˜ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋”๋ผ๋„ ์ปค๋ฐ‹๋œ ๋ฐ์ดํ„ฐ๋Š” ์†์‹ค๋˜์ง€ ์•Š๋Š”๋‹ค.

๐Ÿ”น Spring์—์„œ ์ œ๊ณตํ•˜๋Š” Transaction ํ•ต์‹ฌ ๊ธฐ์ˆ 

Spring์€ ํšจ์œจ์ ์ธ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์„ธ ๊ฐ€์ง€ ํ•ต์‹ฌ ๊ธฐ์ˆ ์„ ์ œ๊ณตํ•œ๋‹ค.

1. ํŠธ๋žœ์žญ์…˜ ๋™๊ธฐํ™” (Transaction Synchronization)

2. ํŠธ๋žœ์žญ์…˜ ์ถ”์ƒํ™” (Transaction Abstraction)

3. AOP๋ฅผ ์ด์šฉํ•œ ํŠธ๋žœ์žญ์…˜ ๋ถ„๋ฆฌ (Transaction Management with AOP)

 

[ ํŠธ๋žœ์žญ์…˜ ๋™๊ธฐํ™” (Transaction Synchronization) ]

JDBC๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ž‘์—…์„ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๊ด€๋ฆฌํ•˜๋ ค๋ฉด, ์ปค๋„ฅ์…˜(Connection) ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•˜๊ณ  ๊ณต์œ ํ•˜๋Š” ๋“ฑ์˜ ์ž‘์—…์ด ํ•„์š”ํ•˜๋‹ค. ํ•˜์ง€๋งŒ ์ด๋Ÿฐ ๋ฐฉ์‹์€ ๋ณต์žกํ•˜๊ณ , ๋ถˆํ•„์š”ํ•œ ์ฝ”๋“œ๊ฐ€ ๋งŽ์•„์งˆ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ์ดˆ๋ž˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Spring์€ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ํŠธ๋žœ์žญ์…˜ ๋™๊ธฐํ™”(Transaction Synchronization) ๊ธฐ์ˆ ์„ ์ œ๊ณต์„ ์ œ๊ณตํ•œ๋‹ค.

ํŠธ๋žœ์žญ์…˜ ๋™๊ธฐํ™”๋Š” ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•  ๋•Œ ์ƒ์„ฑ๋œ ์ปค๋„ฅ์…˜ ๊ฐ์ฒด๋ฅผ ํŠน๋ณ„ํ•œ ์ €์žฅ์†Œ์— ๋ณด๊ด€ํ•ด ๋‘๊ณ , ํ•„์š”ํ•  ๋•Œ ๊บผ๋‚ด ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ธฐ์ˆ ์ด๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ถˆํ•„์š”ํ•œ ์ปค๋„ฅ์…˜ ์ƒ์„ฑ์„ ๋ฐฉ์ง€ํ•˜๊ณ , ์ผ๊ด€๋œ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

๋˜ํ•œ, Spring์˜ ํŠธ๋žœ์žญ์…˜ ๋™๊ธฐํ™” ์ €์žฅ์†Œ๋Š” ์ž‘์—… ์Šค๋ ˆ๋“œ๋ณ„๋กœ ์ปค๋„ฅ์…˜ ๊ฐ์ฒด๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋ฏ€๋กœ, ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ๋„ ์ปค๋„ฅ์…˜ ์ถฉ๋Œ ์—†์ด ์•ˆ์ „ํ•˜๊ฒŒ ํŠธ๋žœ์žญ์…˜์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

 

[ ํŠธ๋žœ์žญ์…˜ ๋™๊ธฐํ™” ์˜ˆ์‹œ ]

// ํŠธ๋žœ์žญ์…˜ ๋™๊ธฐํ™” ์‹œ์ž‘
TransactionSynchronizationManager.initSynchronization();
// ์ปค๋„ฅ์…˜ ํš๋“
Connection c = DataSourceUtils.getConnection(dataSource);

// ํŠธ๋žœ์žญ์…˜ ๋‚ด ์ž‘์—… ์ˆ˜ํ–‰
try {
    // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž‘์—… ์ˆ˜ํ–‰
} finally {
    // ์ปค๋„ฅ์…˜ ๋ฐ˜ํ™˜ ๋ฐ ๋™๊ธฐํ™” ์ข…๋ฃŒ
    DataSourceUtils.releaseConnection(c, dataSource);
    TransactionSynchronizationManager.unbindResource(dataSource);
    TransactionSynchronizationManager.clearSynchronization();
}

ํ•˜์ง€๋งŒ, ๊ฐœ๋ฐœ์ž๊ฐ€ JDBC๊ฐ€ ์•„๋‹Œ Hibernate์™€ ๊ฐ™์€ ORM ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, ์œ„์˜ JDBC ๊ธฐ๋ฐ˜ ํŠธ๋žœ์žญ์…˜ ๋™๊ธฐํ™” ์ฝ”๋“œ๋Š” ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค.

๋Œ€ํ‘œ์ ์œผ๋กœ Hibernate๋Š” JDBC์˜ Connection ๊ฐ์ฒด๊ฐ€ ์•„๋‹Œ Session ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์—, JDBC์˜ ์ข…์†์ ์ธ ๋ฐฉ์‹์œผ๋กœ ํŠธ๋žœ์žญ์…˜์„ ๊ด€๋ฆฌํ•˜๋ฉด Hibernate ํ™˜๊ฒฝ์—์„œ๋Š” ์ œ๋Œ€๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š์„ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค.

 

์ด๋Ÿฌํ•œ ๊ธฐ์ˆ  ์˜์กด์„ฑ์„ ์ค„์ด๊ณ , ๋‹ค์–‘ํ•œ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ ๊ธฐ๋ฒ•์„ ์ผ๊ด€๋˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก Spring์€ ํŠธ๋žœ์žญ์…˜ ์ถ”์ƒํ™”(Transaction Abstraction) ๊ธฐ์ˆ ์„ ์ œ๊ณตํ•œ๋‹ค.

 

[ ํŠธ๋žœ์žญ์…˜ ์ถ”์ƒํ™” (Transaction Abstraction) ]

Spring์€ ๋‹ค์–‘ํ•œ ํŠธ๋žœ์žญ์…˜ ๊ธฐ์ˆ ์˜ ๊ณตํ†ต์ ์„ ์ถ”์ƒํ™”ํ•˜์—ฌ ํŠธ๋žœ์žญ์…˜ ์ถ”์ƒํ™” (Transaction Abstraction) ๊ธฐ์ˆ ์„ ์ œ๊ณต์„ ์ œ๊ณตํ•œ๋‹ค. ์ด๋ฅผ ํ™œ์šฉํ•˜๋ฉด JDBC, Hibernate ๋“ฑ ํŠน์ • ๊ธฐ์ˆ ์— ์ข…์†๋˜์ง€ ์•Š๊ณ ๋„ ์ผ๊ด€๋œ ๋ฐฉ์‹์œผ๋กœ ํŠธ๋žœ์žญ์…˜์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

Spring์—์„œ ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„๋ฅผ ์„ค์ •ํ•˜๋Š” ํ•ต์‹ฌ ์ธํ„ฐํŽ˜์ด์Šค๋Š” PlatformTransactionManager์ด๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ์ž๋Š” ํŠธ๋žœ์žญ์…˜์„ ๊ธฐ์ˆ ์— ๊ด€๊ณ„์—†์ด ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์˜ˆ๋ฅผ ๋“ค์–ด, JDBC์˜ ๋กœ์ปฌ ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ DataSourceTransactionManager๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

[ ํŠธ๋žœ์žญ์…˜ ์ถ”์ƒํ™” ์˜ˆ์‹œ ]

Spring์€ PlatformTransactionManager๋ฅผ ํ†ตํ•ด ํŠธ๋žœ์žญ์…˜์„ ๊ณต์œ ํ•˜๊ณ , ์ปค๋ฐ‹ํ•˜๊ณ , ๋กค๋ฐฑํ•  ์ˆ˜ ์žˆ๋‹ค.

public Object invoke(MethodInvocation invocation) throws Throwable {
    // ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘
    TransactionStatus status = this.transactionManager
    		.getTransaction(new DefaultTransactionDefinition());

    try {
        Object ret = invocation.proceed(); // ๋Œ€์ƒ ๋ฉ”์„œ๋“œ ์‹คํ–‰
        this.transactionManager.commit(status); // ์„ฑ๊ณต ์‹œ ์ปค๋ฐ‹
        return ret;
    } catch (Throwable t) {
        this.transactionManager.rollback(status); // ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ๋กค๋ฐฑ
        throw t; // ์˜ˆ์™ธ ๋‹ค์‹œ ๋˜์ง€๊ธฐ
    }
}

ํ•˜์ง€๋งŒ ์œ„์™€ ๊ฐ™์€ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ ์ฝ”๋“œ๊ฐ€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ฝ”๋“œ์™€ ๊ฒฐํ•ฉ๋˜์–ด ์žˆ์–ด ๋‘ ๊ฐ€์ง€ ์ฑ…์ž„(ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ + ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์‹คํ–‰)์„ ๊ฐ€์ง€๊ฒŒ ๋œ๋‹ค.

Spring์—์„œ๋Š” AOP(Aspect-Oriented Programming)๋ฅผ ํ™œ์šฉํ•˜์—ฌ ํŠธ๋žœ์žญ์…˜ ๋กœ์ง์„ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๋ถ„๋ฆฌํ•จ์œผ๋กœ์จ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€ ๋ณด์ˆ˜์„ฑ์„ ํ–ฅ์ƒ์‹œ์ผฐ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ์ž๋Š” @Transactional ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ๋”์šฑ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ํŠธ๋žœ์žญ์…˜์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

 

[ AOP๋ฅผ ์ด์šฉํ•œ ํŠธ๋žœ์žญ์…˜ ๋ถ„๋ฆฌ (Transaction Management with AOP) ]

๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•˜๊ฒŒ ์–ฝํ˜€ ์žˆ๋Š” ์ฝ”๋“œ๋กœ ์˜ˆ๋ฅผ ๋“ค์–ด๋ณด์ž.

public void addUsers(List<User> userList) {
    TransactionStatus status = this.transactionManager
    		.getTransaction(new DefaultTransactionDefinition());

    try {
        for (User user : userList) {
            if (isEmailNotDuplicated(user.getEmail())) {
                userRepository.save(user);
            }
        }
        this.transactionManager.commit(status);
    } catch (Exception e) {
        this.transactionManager.rollback(status);
        throw e;
    }
}

์œ„์˜ ์ฝ”๋“œ๋Š” ๋‘ ๊ฐ€์ง€ ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค.

1. ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ ์ฝ”๋“œ์™€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ์„ž์—ฌ ์žˆ์–ด ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์ง„๋‹ค.

2. ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ๋Š” ํ•ต์‹ฌ ๋กœ์ง๊ณผ ์„ฑ๊ฒฉ์ด ๋‹ค๋ฅด๋ฏ€๋กœ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ ์ ˆํ•˜๋‹ค.

 

ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ๋ฅผ ๋ถ„๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ์‹์ด ๋– ์˜ค๋ฅผ ์ˆ˜ ์žˆ๋‹ค.

1. ๋‚ด๋ถ€ ๋ฉ”์„œ๋“œ๋กœ ๋ถ„๋ฆฌ

2. DI(Dependency Injection)๋ฅผ ํ™œ์šฉํ•œ ํ•ฉ์„ฑ

3. ์ƒ์†์„ ์ด์šฉํ•œ ๊ตฌ์กฐํ™”

 

ํ•˜์ง€๋งŒ ์œ„ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ จ ์ฝ”๋“œ๊ฐ€ ์™„์ „ํžˆ ์‚ฌ๋ผ์ง€๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. ์—ฌ์ „ํžˆ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ ์ฝ”๋“œ๊ฐ€ ๊ณ„์† ๋‚จ์•„์žˆ๊ณ , ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ณณ๋งˆ๋‹ค ์ด๋ฅผ ์ ์šฉํ•ด์•ผ ๋ฒˆ๊ฑฐ๋กœ์›€์ด ์žˆ๋‹ค.

 

Spring์€ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด AOP(Aspect Oriented Programming, ๊ด€์  ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ) ๊ฐœ๋…์„ ๋„์ž…ํ–ˆ๋‹ค.

AOP๋ฅผ ํ™œ์šฉํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜ ์ฝ”๋“œ์™€ ๊ฐ™์€ ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ์„ ํ•ต์‹ฌ ๋กœ์ง์—์„œ ์™„์ „ํžˆ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. Spring์€ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ ๊ธฐ๋Šฅ์„ AOP ๊ธฐ๋ฐ˜์˜ ๋ณ„๋„ ๋ชจ๋“ˆ๋กœ ์ œ๊ณตํ•˜์—ฌ, ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜์ง€ ์•Š์•„๋„ ์ž๋™์œผ๋กœ ์ ์šฉ๋˜๋„๋ก ์ง€์›ํ•œ๋‹ค. ์ด๋Ÿฌํ•œ ๊ธฐ๋Šฅ์„ ๋” ์‰ฝ๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก @Transactional ์–ด๋…ธํ…Œ์ด์…˜์„ ์ œ๊ณตํ•˜๋ฉฐ, ์ด๋ฅผ ์„ ์–ธํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ Spring์ด ํŠธ๋žœ์žญ์…˜์„ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•œ๋‹ค.

 

[ @Transactional์„ ์ ์šฉํ•œ ๊ฐœ์„ ๋œ ์ฝ”๋“œ ]

Spring์˜ AOP ๊ธฐ๋ฐ˜ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ ์ฝ”๋“œ ์—†์ด ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๋งŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

@Service
@RequiredArgsConstructor
@Transactional // ํด๋ž˜์Šค ๋ ˆ๋ฒจ์—์„œ ์„ ์–ธํ•˜๋ฉด ๋ชจ๋“  public ๋ฉ”์„œ๋“œ์— ํŠธ๋žœ์žญ์…˜์ด ์ ์šฉ๋จ
public class UserService {
    
    private final UserRepository userRepository;

    public void addUsers(List<User> userList) {
        for (User user : userList) {
            if (isEmailNotDuplicated(user.getEmail())) {
                userRepository.save(user);
            }
        }
    }
}

@Transactional์„ ์ ์šฉํ•˜๋ฉด ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€ ๋ณด์ˆ˜์„ฑ์ด ํ–ฅ์ƒ๋˜๋ฉฐ, ํŠธ๋žœ์žญ์…˜ ๊ด€๋ จ ์ฝ”๋“œ๊ฐ€ ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๋ถ„๋ฆฌ๋˜์–ด ์ฝ”๋“œ์˜ ๊ฐ„๊ฒฐํ•จ๊ณผ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ์ž๋Š” ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ์— ๋Œ€ํ•œ ์„ธ๋ถ€ ๊ตฌํ˜„์„ ์‹ ๊ฒฝ ์“ธ ํ•„์š” ์—†์ด, ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋˜ํ•œ, @Transactional์„ ํด๋ž˜์Šค๋‚˜ ๋ฉ”์„œ๋“œ ๋ ˆ๋ฒจ์—์„œ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์–ด, ํŠน์ • ๋ฉ”์„œ๋“œ์—๋งŒ ํŠธ๋žœ์žญ์…˜์„ ์ ์šฉํ•˜๊ฑฐ๋‚˜ ํด๋ž˜์Šค ์ „์ฒด์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์œ ์—ฐ์„ฑ์„ ์ œ๊ณตํ•œ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์ฝ๊ธฐ ์ž‘์—…์ด ๋งŽ์€ ์กฐํšŒ ์ž‘์—…์—์„œ๋Š” @Transactional(readOnly = true)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„ฑ๋Šฅ ์ตœ์ ํ™”๋ฅผ ๋„์šธ ์ˆ˜ ์žˆ๋‹ค.

 

 

 

 

 

๐Ÿ“ƒ reference