๐ŸŒฑ Spring @Transactional์˜ ์ „ํŒŒ ์†์„ฑ ์ดํ•ด์™€ ํ™œ์šฉ ๐Ÿš€

์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐ŸŒฑ Spring @Transactional์˜ ์ „ํŒŒ ์†์„ฑ ์ดํ•ด์™€ ํ™œ์šฉ ๐Ÿš€

 

 

์•ˆ๋…•ํ•˜์„ธ์š”, ์—ฌ๋Ÿฌ๋ถ„! ์˜ค๋Š˜์€ Spring Framework์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์ธ @Transactional ์–ด๋…ธํ…Œ์ด์…˜๊ณผ ๊ทธ ์ „ํŒŒ ์†์„ฑ์— ๋Œ€ํ•ด ๊นŠ์ด ํŒŒํ—ค์ณ๋ณผ ๊ฑฐ์˜ˆ์š”. ์ด ์ฃผ์ œ, ์ข€ ์–ด๋ ต๊ฒŒ ๋“ค๋ฆฌ์ฃ ? ใ…‹ใ…‹ใ…‹ ๊ฑฑ์ • ๋งˆ์„ธ์š”! ์ œ๊ฐ€ ์‰ฝ๊ณ  ์žฌ๋ฏธ์žˆ๊ฒŒ ์„ค๋ช…ํ•ด๋“œ๋ฆด๊ฒŒ์š”. ๋งˆ์น˜ ์นดํ†ก์œผ๋กœ ์ˆ˜๋‹ค ๋– ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ์š”! ๐Ÿ˜‰

์šฐ๋ฆฌ๊ฐ€ ์ด ์ฃผ์ œ๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฑด ์ •๋ง ์ค‘์š”ํ•ด์š”. ์™œ๋ƒ๊ณ ์š”? ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ชจ๋“  ๊ฐœ๋ฐœ์ž์—๊ฒŒ ํ•„์ˆ˜์ ์ธ ์Šคํ‚ฌ์ด๊ฑฐ๋“ ์š”. ํŠนํžˆ Java ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ๋”๋”์šฑ! ์ด ์ง€์‹์€ ์—ฌ๋Ÿฌ๋ถ„์˜ ๊ฐœ๋ฐœ ์‹ค๋ ฅ์„ ํ•œ ๋‹จ๊ณ„ ์—…๊ทธ๋ ˆ์ด๋“œ์‹œ์ผœ์ค„ ๊ฑฐ์˜ˆ์š”. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์ƒˆ๋กœ์šด ์žฌ๋Šฅ์„ ์Šต๋“ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ง์ด์ฃ ! ๐Ÿ˜Ž

๐Ÿ’ก ๊ฟ€ํŒ: ์ด ๊ธ€์„ ๋๊นŒ์ง€ ์ฝ๊ณ  ๋‚˜๋ฉด, ์—ฌ๋Ÿฌ๋ถ„๋„ @Transactional ์ „๋ฌธ๊ฐ€๊ฐ€ ๋  ์ˆ˜ ์žˆ์–ด์š”! ์žฌ๋Šฅ๋„ท์—์„œ Java ๊ฐœ๋ฐœ ๊ด€๋ จ ์žฌ๋Šฅ์„ ๊ณต์œ ํ•  ๋•Œ ์ด ์ง€์‹์„ ํ™œ์šฉํ•˜๋ฉด ๋Œ€๋ฐ•๋‚ ๊ฑธ์š”? ใ…Žใ…Ž

์ž, ๊ทธ๋Ÿผ ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ์‹œ์ž‘ํ•ด๋ณผ๊นŒ์š”? ์ค€๋น„๋˜์…จ๋‚˜์š”? ๊ณ ๊ณ ์”ฝ~! ๐Ÿš—๐Ÿ’จ

๐ŸŽญ ํŠธ๋žœ์žญ์…˜์ด ๋ญ๊ธธ๋ž˜? ๊ธฐ์ดˆ๋ถ€ํ„ฐ ํƒ„ํƒ„ํžˆ!

์šฐ์„ , ํŠธ๋žœ์žญ์…˜์ด ๋ญ”์ง€๋ถ€ํ„ฐ ์•Œ์•„๋ณผ๊นŒ์š”? ํŠธ๋žœ์žญ์…˜์€ ๋ญ”๊ฐ€ ์–ด๋ ค์›Œ ๋ณด์ด๋Š” ๋‹จ์–ด์ง€๋งŒ, ์‚ฌ์‹ค ์šฐ๋ฆฌ ์ผ์ƒ์—์„œ๋„ ์ž์ฃผ ๋ณผ ์ˆ˜ ์žˆ์–ด์š”.

์˜ˆ๋ฅผ ๋“ค์–ด๋ณผ๊ฒŒ์š”. ์—ฌ๋Ÿฌ๋ถ„์ด ํŽธ์˜์ ์—์„œ ๋ผ๋ฉด์„ ์‚ฌ๋Š” ์ƒํ™ฉ์„ ์ƒ์ƒํ•ด๋ณด์„ธ์š”. ๐Ÿœ

  1. ๋ผ๋ฉด์„ ๊ณ ๋ฅธ๋‹ค. ๐Ÿค”
  2. ๊ณ„์‚ฐ๋Œ€๋กœ ๊ฐ„๋‹ค. ๐Ÿšถโ€โ™‚๏ธ
  3. ๋ˆ์„ ์ง€๋ถˆํ•œ๋‹ค. ๐Ÿ’ฐ
  4. ์˜์ˆ˜์ฆ์„ ๋ฐ›๋Š”๋‹ค. ๐Ÿงพ

์ด ์ „์ฒด ๊ณผ์ •์ด ๋ฐ”๋กœ ํ•˜๋‚˜์˜ 'ํŠธ๋žœ์žญ์…˜'์ด์—์š”. ์ด ๊ณผ์ • ์ค‘ ์–ด๋Š ํ•˜๋‚˜๋ผ๋„ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”? ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ˆ์ด ๋ถ€์กฑํ•˜๋‹ค๊ฑฐ๋‚˜ ๋ผ๋ฉด์ด ํ’ˆ์ ˆ์ด๋ผ๋ฉด? ๊ทธ๋Ÿผ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ ์‹œ์ž‘ํ•ด์•ผ๊ฒ ์ฃ . ์ด๊ฑธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ๋Š” '๋กค๋ฐฑ(Rollback)'์ด๋ผ๊ณ  ํ•ด์š”.

๐ŸŽˆ ์žฌ๋ฏธ์žˆ๋Š” ์‚ฌ์‹ค: ํŠธ๋žœ์žญ์…˜์ด๋ผ๋Š” ๊ฐœ๋…์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์šฐ๋ฆฌ ์ผ์ƒ ๊ณณ๊ณณ์— ์ˆจ์–ด์žˆ์–ด์š”. ์žฌ๋Šฅ๋„ท์—์„œ ์žฌ๋Šฅ์„ ์‚ฌ๊ณ ํŒŒ๋Š” ๊ณผ์ •๋„ ์ผ์ข…์˜ ํŠธ๋žœ์žญ์…˜์ด๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ์ฃ !

์ž, ์ด์ œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ์˜ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋Œ์•„์™€๋ณผ๊นŒ์š”?

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

ํŠธ๋žœ์žญ์…˜์˜ ํŠน์„ฑ์„ ACID๋ผ๊ณ  ๋ถˆ๋Ÿฌ์š”. ์ด๊ฒŒ ๋ญ”์ง€ ์•Œ์•„๋ณผ๊นŒ์š”?

  • ์›์ž์„ฑ(Atomicity): ํŠธ๋žœ์žญ์…˜์˜ ๋ชจ๋“  ์—ฐ์‚ฐ์ด ์™„์ „ํžˆ ์ˆ˜ํ–‰๋˜๊ฑฐ๋‚˜, ์•„๋‹ˆ๋ฉด ์ „ํ˜€ ์ˆ˜ํ–‰๋˜์ง€ ์•Š์•„์•ผ ํ•ด์š”. ์ค‘๊ฐ„์— ๋ฉˆ์ถ”๋ฉด ์•ˆ ๋ผ์š”!
  • ์ผ๊ด€์„ฑ(Consistency): ํŠธ๋žœ์žญ์…˜์ด ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ์ผ๊ด€๋œ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•ด์š”.
  • ๊ฒฉ๋ฆฌ์„ฑ(Isolation): ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ํŠธ๋žœ์žญ์…˜๋“ค์ด ์„œ๋กœ ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๋„๋ก ๊ฒฉ๋ฆฌ๋˜์–ด์•ผ ํ•ด์š”.
  • ์ง€์†์„ฑ(Durability): ํŠธ๋žœ์žญ์…˜์ด ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜๋ฉด ๊ทธ ๊ฒฐ๊ณผ๋Š” ์˜๊ตฌ์ ์œผ๋กœ ๋ฐ˜์˜๋˜์–ด์•ผ ํ•ด์š”.

์ด๋Ÿฐ ํŠน์„ฑ๋“ค ๋•Œ๋ฌธ์— ํŠธ๋žœ์žญ์…˜์€ ๋ฐ์ดํ„ฐ์˜ ์•ˆ์ •์„ฑ๊ณผ ์ผ๊ด€์„ฑ์„ ๋ณด์žฅํ•˜๋Š” ๋ฐ ๋งค์šฐ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•ด์š”. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ๊ฑฐ๋ž˜์˜ ์•ˆ์ „์„ฑ์„ ๋ณด์žฅํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ง์ด์ฃ ! ๐Ÿ˜Š

ACID ํŠน์„ฑ ๋‹ค์ด์–ด๊ทธ๋žจ ACID Atomicity Consistency Isolation Durability

์ด์ œ ํŠธ๋žœ์žญ์…˜์˜ ๊ธฐ๋ณธ ๊ฐœ๋…์„ ์•Œ์•˜์œผ๋‹ˆ, Spring์—์„œ ์–ด๋–ป๊ฒŒ ์ด๋ฅผ ๊ด€๋ฆฌํ•˜๋Š”์ง€ ์•Œ์•„๋ณผ๊นŒ์š”? ๊ทธ ํ•ต์‹ฌ์— ๋ฐ”๋กœ @Transactional ์–ด๋…ธํ…Œ์ด์…˜์ด ์žˆ๋‹ต๋‹ˆ๋‹ค! ๐ŸŽฏ

๐ŸŽˆ @Transactional ์–ด๋…ธํ…Œ์ด์…˜: ํŠธ๋žœ์žญ์…˜์˜ ๋งˆ๋ฒ•์‚ฌ

์ž, ์ด์ œ ์šฐ๋ฆฌ์˜ ์ฃผ์ธ๊ณต @Transactional ์–ด๋…ธํ…Œ์ด์…˜์„ ์†Œ๊ฐœํ•  ์‹œ๊ฐ„์ด์—์š”! ์ด ๋…€์„, ์ •๋ง ๋Œ€๋‹จํ•˜๋‹ต๋‹ˆ๋‹ค. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์žฌ๋Šฅ์„ ๋น›๋‚˜๊ฒŒ ํ•ด์ฃผ๋Š” ํ”Œ๋žซํผ์ฒ˜๋Ÿผ, @Transactional์€ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ฝ”๋“œ๋ฅผ ๋น›๋‚˜๊ฒŒ ํ•ด์ค„ ๊ฑฐ์˜ˆ์š”! โœจ

@Transactional์€ Spring Framework์—์„œ ์ œ๊ณตํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ, ๋ฉ”์„œ๋“œ๋‚˜ ํด๋ž˜์Šค์— ํŠธ๋žœ์žญ์…˜ ๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ•ด์ค๋‹ˆ๋‹ค. ์ด ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด, ๋ณต์žกํ•œ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„๋„ ๋ผ์š”. ์ •๋ง ํŽธ๋ฆฌํ•˜์ฃ ?

๐Ÿ’ก ๊ฟ€ํŒ: @Transactional์„ ์‚ฌ์šฉํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ๊น”๋”ํ•ด์ง€๊ณ  ์œ ์ง€๋ณด์ˆ˜๋„ ์‰ฌ์›Œ์ ธ์š”. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ๊ฑฐ๋ž˜๋ฅผ ํ•  ๋•Œ ํ”Œ๋žซํผ์ด ๋ชจ๋“  ๋ณต์žกํ•œ ๊ณผ์ •์„ ๋Œ€์‹  ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์ฃ !

๊ทธ๋Ÿผ ์ด @Transactional์„ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”์ง€ ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋กœ ์‚ดํŽด๋ณผ๊นŒ์š”?


import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {

    @Transactional
    public void createUser(User user) {
        // ์‚ฌ์šฉ์ž ์ƒ์„ฑ ๋กœ์ง
    }
}

์œ„ ์ฝ”๋“œ์—์„œ createUser ๋ฉ”์„œ๋“œ์— @Transactional ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ถ™์˜€์–ด์š”. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ด ๋ฉ”์„œ๋“œ ๋‚ด์˜ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ์‚ฐ์ด ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ฒ˜๋ฆฌ๋ผ์š”. ๋งŒ์•ฝ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์ค‘์— ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋ชจ๋“  ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ๋กค๋ฐฑ๋ฉ๋‹ˆ๋‹ค. ์ฉ”์ง€ ์•Š๋‚˜์š”? ๐Ÿ˜Ž

ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์„œ ๋์ด ์•„๋‹ˆ์—์š”! @Transactional ์–ด๋…ธํ…Œ์ด์…˜์€ ๋‹ค์–‘ํ•œ ์†์„ฑ์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์š”. ์ด ์†์„ฑ๋“ค์„ ์ด์šฉํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜์˜ ๋™์ž‘์„ ๋” ์„ธ๋ฐ€ํ•˜๊ฒŒ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค.

๊ทธ ์ค‘์—์„œ๋„ ํŠนํžˆ ์ค‘์š”ํ•œ ๊ฒŒ ๋ฐ”๋กœ '์ „ํŒŒ ์†์„ฑ(Propagation)'์ด์—์š”. ์ด๊ฒŒ ๋ญ”์ง€ ๊ถ๊ธˆํ•˜์‹œ์ฃ ? ๋‹ค์Œ ์„น์…˜์—์„œ ์ž์„ธํžˆ ์•Œ์•„๋ณด๋„๋ก ํ•ด์š”! ๐Ÿš€

@Transactional ์–ด๋…ธํ…Œ์ด์…˜ ๋‹ค์ด์–ด๊ทธ๋žจ @Transactional ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ์˜ ๋งˆ๋ฒ•์‚ฌ ๋ฉ”์„œ๋“œ/ํด๋ž˜์Šค์— ์ ์šฉ ๋‹ค์–‘ํ•œ ์†์„ฑ ์ œ๊ณต

์ž, ์ด์ œ @Transactional ์–ด๋…ธํ…Œ์ด์…˜์˜ ๊ธฐ๋ณธ์„ ์•Œ์•˜์–ด์š”. ํ•˜์ง€๋งŒ ์ด๊ฑด ๋น™์‚ฐ์˜ ์ผ๊ฐ์ผ ๋ฟ์ด์—์š”! ๋‹ค์Œ ์„น์…˜์—์„œ๋Š” ์ด ์–ด๋…ธํ…Œ์ด์…˜์˜ ์ง„์งœ ๋งค๋ ฅ, ์ „ํŒŒ ์†์„ฑ์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ ๊ฑฐ์˜ˆ์š”. ๊ธฐ๋Œ€๋˜์ง€ ์•Š๋‚˜์š”? ์ €๋Š” ๋„ˆ๋ฌด ์‹ ๋‚˜์š”! ๐Ÿคฉ

๐ŸŒˆ ์ „ํŒŒ ์†์„ฑ: ํŠธ๋žœ์žญ์…˜์˜ ๋น„๋ฐ€ ๋ ˆ์‹œํ”ผ

์ž, ์ด์ œ ์šฐ๋ฆฌ์˜ ๋ฉ”์ธ ๋””์‹œ, ์ „ํŒŒ ์†์„ฑ(Propagation)์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ ์‹œ๊ฐ„์ด์—์š”! ์ด๊ฑฐ ์ง„์งœ ๋Œ€๋ฐ•์ด์—์š”. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์žฌ๋Šฅ์„ ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์œผ๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, ์ „ํŒŒ ์†์„ฑ์„ ์ด์šฉํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜์„ ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์œผ๋กœ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์–ด์š”. ๐Ÿ˜ƒ

์ „ํŒŒ ์†์„ฑ์€ ํŠธ๋žœ์žญ์…˜์˜ ๊ฒฝ๊ณ„๋ฅผ ์ •์˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋งํ•ด์š”. ์‰ฝ๊ฒŒ ๋งํ•ด, ๊ธฐ์กด์— ํŠธ๋žœ์žญ์…˜์ด ์ง„ํ–‰ ์ค‘์ผ ๋•Œ ์ƒˆ๋กœ์šด ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•˜๋ ค๊ณ  ํ•  ๋•Œ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๊ฑฐ์ฃ .

๐ŸŽˆ ์žฌ๋ฏธ์žˆ๋Š” ์‚ฌ์‹ค: ์ „ํŒŒ ์†์„ฑ์€ ๋งˆ์น˜ ์š”๋ฆฌ ๋ ˆ์‹œํ”ผ์™€ ๊ฐ™์•„์š”. ๊ฐ™์€ ์žฌ๋ฃŒ(ํŠธ๋žœ์žญ์…˜)๋กœ๋„ ์–ด๋–ป๊ฒŒ ์กฐ๋ฆฌ(์ „ํŒŒ)ํ•˜๋Š๋ƒ์— ๋”ฐ๋ผ ์ „ํ˜€ ๋‹ค๋ฅธ ๋ง›(๊ฒฐ๊ณผ)์ด ๋‚˜์˜ค์ฃ !

Spring์—์„œ๋Š” 7๊ฐ€์ง€์˜ ์ „ํŒŒ ์†์„ฑ์„ ์ œ๊ณตํ•ด์š”. ๊ฐ๊ฐ์˜ ์†์„ฑ์„ ํ•˜๋‚˜์”ฉ ์‚ดํŽด๋ณผ๊นŒ์š”?

  1. REQUIRED (๊ธฐ๋ณธ๊ฐ’): ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์ด ์žˆ์œผ๋ฉด ๊ทธ๊ฑธ ์‚ฌ์šฉํ•˜๊ณ , ์—†์œผ๋ฉด ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์š”. ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ์˜ต์…˜์ด์—์š”.
  2. SUPPORTS: ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์ด ์žˆ์œผ๋ฉด ๊ทธ๊ฑธ ์‚ฌ์šฉํ•˜๊ณ , ์—†์–ด๋„ ๊ดœ์ฐฎ์•„์š”. ํŠธ๋žœ์žญ์…˜ ์—†์ด๋„ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ๋ฉ”์„œ๋“œ์— ์‚ฌ์šฉํ•ด์š”.
  3. MANDATORY: ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์ด ๋ฐ˜๋“œ์‹œ ์žˆ์–ด์•ผ ํ•ด์š”. ์—†์œผ๋ฉด ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ์š”.
  4. REQUIRES_NEW: ํ•ญ์ƒ ์ƒˆ๋กœ์šด ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•ด์š”. ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜์ด ์žˆ๋‹ค๋ฉด ์ž ์‹œ ์ค‘๋‹จํ•˜๊ณ  ์ƒˆ ํŠธ๋žœ์žญ์…˜์„ ์‹คํ–‰ํ•ด์š”.
  5. NOT_SUPPORTED: ํŠธ๋žœ์žญ์…˜ ์—†์ด ์‹คํ–‰ํ•ด์š”. ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์ด ์žˆ๋‹ค๋ฉด ์ž ์‹œ ์ค‘๋‹จํ•ด์š”.
  6. NEVER: ํŠธ๋žœ์žญ์…˜ ์—†์ด ์‹คํ–‰ํ•ด์š”. ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์ด ์žˆ์œผ๋ฉด ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ์š”.
  7. NESTED: ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์ด ์žˆ์œผ๋ฉด ์ค‘์ฒฉ ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•ด์š”. ์—†์œผ๋ฉด REQUIRED์ฒ˜๋Ÿผ ๋™์ž‘ํ•ด์š”.

์™€์šฐ! ์ด๋ ‡๊ฒŒ ๋‹ค์–‘ํ•œ ์˜ต์…˜์ด ์žˆ๋‹ค๋‹ˆ ๋†€๋ž์ง€ ์•Š๋‚˜์š”? ๊ฐ๊ฐ์˜ ์ƒํ™ฉ์— ๋งž๋Š” ์ „ํŒŒ ์†์„ฑ์„ ์„ ํƒํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜์„ ๋”์šฑ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์š”. ๐Ÿ‘

ํŠธ๋žœ์žญ์…˜ ์ „ํŒŒ ์†์„ฑ ๋‹ค์ด์–ด๊ทธ๋žจ Propagation REQUIRED SUPPORTS MANDATORY REQUIRES_NEW NOT_SUPPORTED NEVER NESTED

์ž, ์ด์ œ ๊ฐ๊ฐ์˜ ์ „ํŒŒ ์†์„ฑ์— ๋Œ€ํ•ด ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณผ๊นŒ์š”? ์˜ˆ์ œ ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ์„ค๋ช…ํ•ด๋“œ๋ฆด๊ฒŒ์š”. ์ค€๋น„๋˜์…จ๋‚˜์š”? ๊ณ ๊ณ ์”ฝ~! ๐Ÿš€

1. REQUIRED (๊ธฐ๋ณธ๊ฐ’)

REQUIRED๋Š” ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ด๊ณ  ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ์ „ํŒŒ ์†์„ฑ์ด์—์š”. ํ˜„์žฌ ์ง„ํ–‰ ์ค‘์ธ ํŠธ๋žœ์žญ์…˜์ด ์žˆ์œผ๋ฉด ๊ทธ ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•˜๊ณ , ์—†์œผ๋ฉด ์ƒˆ๋กœ์šด ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•ด์š”.


@Transactional(propagation = Propagation.REQUIRED)
public void createUser(User user) {
    userRepository.save(user);
    emailService.sendWelcomeEmail(user);
}

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

2. SUPPORTS

SUPPORTS๋Š” ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์ด ์žˆ์œผ๋ฉด ๊ทธ๊ฑธ ์‚ฌ์šฉํ•˜๊ณ , ์—†์–ด๋„ ๊ดœ์ฐฎ์•„์š”. ํŠธ๋žœ์žญ์…˜์ด ๊ผญ ํ•„์š”ํ•˜์ง€ ์•Š์€ ์—ฐ์‚ฐ์— ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์•„์š”.


@Transactional(propagation = Propagation.SUPPORTS)
public List<user> getAllUsers() {
    return userRepository.findAll();
}
</user>

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

3. MANDATORY

MANDATORY๋Š” ํ˜„์žฌ ์ง„ํ–‰ ์ค‘์ธ ํŠธ๋žœ์žญ์…˜์ด ๋ฐ˜๋“œ์‹œ ์žˆ์–ด์•ผ ํ•ด์š”. ์—†์œผ๋ฉด ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ์š”.


@Transactional(propagation = Propagation.MANDATORY)
public void updateUserPassword(Long userId, String newPassword) {
    User user = userRepository.findById(userId).orElseThrow();
    user.setPassword(newPassword);
    userRepository.save(user);
}

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

4. REQUIRES_NEW

REQUIRES_NEW๋Š” ํ•ญ์ƒ ์ƒˆ๋กœ์šด ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•ด์š”. ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜์ด ์žˆ๋‹ค๋ฉด ์ž ์‹œ ์ค‘๋‹จํ•˜๊ณ  ์ƒˆ ํŠธ๋žœ์žญ์…˜์„ ์‹คํ–‰ํ•ด์š”.


@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logUserAction(Long userId, String action) {
    UserLog log = new UserLog(userId, action);
    logRepository.save(log);
}

์ด ๋ฉ”์„œ๋“œ๋Š” ์‚ฌ์šฉ์ž์˜ ํ–‰๋™์„ ๋กœ๊ทธ๋กœ ๊ธฐ๋กํ•ด์š”. REQUIRES_NEW๋ฅผ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ, ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์˜ ์„ฑ๊ณต ์—ฌ๋ถ€์™€ ๊ด€๊ณ„์—†์ด ๋กœ๊ทธ๊ฐ€ ํ•ญ์ƒ ์ €์žฅ๋˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์–ด์š”. ๋ฉ”์ธ ํŠธ๋žœ์žญ์…˜์ด ์‹คํŒจํ•ด๋„ ๋กœ๊ทธ๋Š” ๋‚จ๋Š” ๊ฑฐ์ฃ !

5. NOT_SUPPORTED

NOT_SUPPORTED๋Š” ํŠธ๋žœ์žญ์…˜ ์—†์ด ์‹คํ–‰ํ•ด์š”. ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์ด ์žˆ๋‹ค๋ฉด ์ž ์‹œ ์ค‘๋‹จํ•ด์š”.


@Transactional(propagation = Propagation.NOT_SUPPORTED)
public int countUsers() {
    return userRepository.count();
}

์ด ๋ฉ”์„œ๋“œ๋Š” ๋‹จ์ˆœํžˆ ์‚ฌ์šฉ์ž ์ˆ˜๋ฅผ ์„ธ๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด์š”. ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ  ๋น ๋ฅด๊ฒŒ ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์— NOT_SUPPORTED๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”. ํŠธ๋žœ์žญ์…˜ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ์ฃ .

6. NEVER

NEVER๋Š” ํŠธ๋žœ์žญ์…˜ ์—†์ด ์‹คํ–‰ํ•ด์š”. ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์ด ์žˆ์œผ๋ฉด ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ์š”.


@Transactional(propagation = Propagation.NEVER)
public void sendPromotionalEmail(String email) {
    // ํ”„๋กœ๋ชจ์…˜ ์ด๋ฉ”์ผ ์ „์†ก ๋กœ์ง
}

์ด ๋ฉ”์„œ๋“œ๋Š” ํ”„๋กœ๋ชจ์…˜ ์ด๋ฉ”์ผ์„ ์ „์†กํ•˜๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด์š”. ์ด๋ฉ”์ผ ์ „์†ก์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž‘์—…๊ณผ ๋ฌด๊ด€ํ•˜๋ฏ€๋กœ, ํŠธ๋žœ์žญ์…˜์ด ํ•„์š” ์—†์–ด์š”. NEVER๋ฅผ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ, ์‹ค์ˆ˜๋กœ ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ์ด ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์–ด์š”.

7. NESTED

NESTED๋Š” ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์ด ์žˆ์œผ๋ฉด ์ค‘์ฒฉ ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•ด์š”. ์—†์œผ๋ฉด REQUIRED์ฒ˜๋Ÿผ ๋™์ž‘ํ•ด์š”.


@Transactional(propagation = Propagation.NESTED)
public void createUserWithBonus(User user, Bonus bonus) {
    userRepository.save(user);
    bonusRepository.save(bonus);
}

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

๐Ÿ’ก ๊ฟ€ํŒ: ์ „ํŒŒ ์†์„ฑ์„ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ๋„ ๋ฐ์ดํ„ฐ์˜ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ์œ ์—ฐํ•œ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•ด์š”. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ๋‹ค์–‘ํ•œ ์žฌ๋Šฅ์„ ์กฐํ•ฉํ•ด ์ƒˆ๋กœ์šด ๊ฐ€์น˜๋ฅผ ๋งŒ๋“ค์–ด๋‚ด๋Š” ๊ฒƒ์ฒ˜๋Ÿผ์š”!

์™€์šฐ! ์ด๋ ‡๊ฒŒ ๋‹ค์–‘ํ•œ ์ „ํŒŒ ์†์„ฑ์ด ์žˆ๋‹ค๋‹ˆ ์ •๋ง ๋Œ€๋‹จํ•˜์ง€ ์•Š๋‚˜์š”? ๊ฐ๊ฐ์˜ ์ƒํ™ฉ์— ๋งž๋Š” ์ „ํŒŒ ์†์„ฑ์„ ์„ ํƒํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜์„ ๋”์šฑ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์š”. ๐Ÿ‘

ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์„œ ๋์ด ์•„๋‹ˆ์—์š”! ์ „ํŒŒ ์†์„ฑ์„ ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ์€ ๋ฌด์—‡์ธ์ง€ ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณผ๊นŒ์š”? ๋‹ค์Œ ์„น์…˜์—์„œ ๊ณ„์†๋ฉ๋‹ˆ๋‹ค! ๐Ÿš€

๐ŸŽญ ์ „ํŒŒ ์†์„ฑ์˜ ์‹ค์ „ ํ™œ์šฉ๊ณผ ์ฃผ์˜์‚ฌํ•ญ

์ž, ์ด์ œ ์šฐ๋ฆฌ๋Š” ์ „ํŒŒ ์†์„ฑ์˜ ๊ธฐ๋ณธ์„ ์•Œ์•˜์–ด์š”. ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ ์ด๊ฑธ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜๊ณ , ์–ด๋–ค ์ ์„ ์ฃผ์˜ํ•ด์•ผ ํ• ๊นŒ์š”? ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์žฌ๋Šฅ์„ ํšจ๊ณผ์ ์œผ๋กœ ์„ ๋ณด์ด๊ธฐ ์œ„ํ•ด ์ „๋žต์„ ์„ธ์šฐ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, ์ „ํŒŒ ์†์„ฑ๋„ ์ „๋žต์ ์œผ๋กœ ์‚ฌ์šฉํ•ด์•ผ ํ•ด์š”! ๐Ÿ˜‰

1. ์ „ํŒŒ ์†์„ฑ์˜ ์‹ค์ „ ํ™œ์šฉ

์ „ํŒŒ ์†์„ฑ์€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์˜ ํŠน์„ฑ์— ๋”ฐ๋ผ ์ ์ ˆํžˆ ์„ ํƒํ•ด์•ผ ํ•ด์š”. ๋ช‡ ๊ฐ€์ง€ ์‹ค์ „ ์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด ์‚ดํŽด๋ณผ๊นŒ์š”?

์˜ˆ์‹œ 1: ์ฃผ๋ฌธ ์ฒ˜๋ฆฌ ์‹œ์Šคํ…œ


@Service
public class OrderService {

    @Autowired
    private ProductService productService;

    @Autowired
    private PaymentService paymentService;

    @Transactional
    public void processOrder(Order order) {
        // ์ฃผ๋ฌธ ์ €์žฅ
        orderRepository.save(order);

        // ์žฌ๊ณ  ๊ฐ์†Œ
        productService.decreaseStock(order.getProductId(), order.getQuantity());

        // ๊ฒฐ์ œ ์ฒ˜๋ฆฌ
        paymentService.processPayment(order.getPaymentDetails());
    }
}

@Service
public class ProductService {

    @Transactional(propagation = Propagation.REQUIRED)
    public void decreaseStock(Long productId, int quantity) {
        // ์žฌ๊ณ  ๊ฐ์†Œ ๋กœ์ง
    }
}

@Service
public class PaymentService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void processPayment(PaymentDetails details) {
        // ๊ฒฐ์ œ ์ฒ˜๋ฆฌ ๋กœ์ง
    }
}

์ด ์˜ˆ์‹œ์—์„œ ์ฃผ๋ฌธ ์ฒ˜๋ฆฌ๋Š” ์„ธ ๋‹จ๊ณ„๋กœ ์ด๋ฃจ์–ด์ ธ์š”. ์ฃผ๋ฌธ ์ €์žฅ, ์žฌ๊ณ  ๊ฐ์†Œ, ๊ฒฐ์ œ ์ฒ˜๋ฆฌ. ProductService์˜ decreaseStock ๋ฉ”์„œ๋“œ๋Š” REQUIRED๋ฅผ ์‚ฌ์šฉํ•ด ์ฃผ๋ฌธ ์ฒ˜๋ฆฌ์˜ ํŠธ๋žœ์žญ์…˜์— ์ฐธ์—ฌํ•ด์š”. ํ•˜์ง€๋งŒ PaymentService์˜ processPayment๋Š” REQUIRES_NEW๋ฅผ ์‚ฌ์šฉํ•ด ๋ณ„๋„์˜ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ฒ˜๋ฆฌ๋ผ์š”. ์™œ ๊ทธ๋Ÿด๊นŒ์š”?

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

์˜ˆ์‹œ 2: ๋กœ๊น… ์‹œ์Šคํ…œ


@Service
public class UserService {

    @Autowired
    private LoggingService loggingService;

    @Transactional
    public void registerUser(User user) {
        // ์‚ฌ์šฉ์ž ๋“ฑ๋ก ๋กœ์ง
        userRepository.save(user);

        // ๋กœ๊ทธ ๊ธฐ๋ก
        loggingService.logUserRegistration(user);
    }
}

@Service
public class LoggingService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logUserRegistration(User user) {
        // ๋กœ๊ทธ ์ €์žฅ ๋กœ์ง
    }
}

์ด ์˜ˆ์‹œ์—์„œ ๋กœ๊น… ์„œ๋น„์Šค๋Š” REQUIRES_NEW๋ฅผ ์‚ฌ์šฉํ•ด์š”. ์™œ ๊ทธ๋Ÿด๊นŒ์š”? ์‚ฌ์šฉ์ž ๋“ฑ๋ก์ด ์‹คํŒจํ•˜๋”๋ผ๋„ ๋กœ๊ทธ๋Š” ๋‚จ์•„์žˆ์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด์—์š”. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์‚ฌ์šฉ์ž ๋“ฑ๋ก ํŠธ๋žœ์žญ์…˜์ด ๋กค๋ฐฑ๋˜์–ด๋„ ๋กœ๊ทธ๋Š” ๊ทธ๋Œ€๋กœ ์ €์žฅ๋  ์ˆ˜ ์žˆ์–ด์š”.

2. ์ฃผ์˜์‚ฌํ•ญ

์ „ํŒŒ ์†์„ฑ์„ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ๋ช‡ ๊ฐ€์ง€ ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ์ด ์žˆ์–ด์š”. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์žฌ๋Šฅ์„ ์„ ๋ณด์ผ ๋•Œ ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ์ด ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ง์ด์ฃ !

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

๐Ÿ’ก ๊ฟ€ํŒ: ์ „ํŒŒ ์†์„ฑ์„ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ํ•ญ์ƒ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ž˜ ์ดํ•ดํ•˜๊ณ , ๊ฐ ์ƒํ™ฉ์— ๊ฐ€์žฅ ์ ํ•ฉํ•œ ์†์„ฑ์„ ์„ ํƒํ•ด์•ผ ํ•ด์š”. ๋•Œ๋กœ๋Š” ๊ฐ„๋‹จํ•œ REQUIRED๊ฐ€ ๊ฐ€์žฅ ์ข‹์€ ์„ ํƒ์ผ ์ˆ˜ ์žˆ์–ด์š”!

3. ์‹ค์ œ ์‚ฌ์šฉ ์‚ฌ๋ก€

์ž, ์ด์ œ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ ์–ด๋–ป๊ฒŒ ์ „ํŒŒ ์†์„ฑ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์ข€ ๋” ๋ณต์žกํ•œ ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด ์‚ดํŽด๋ณผ๊นŒ์š”?


@Service
public class ShoppingService {

    @Autowired
    private OrderService orderService;

    @Autowired
    private InventoryService inventoryService;

    @Autowired
    private NotificationService notificationService;

    @Transactional
    public void processPurchase(Purchase purchase) {
        // ์ฃผ๋ฌธ ์ƒ์„ฑ
        Order order = orderService.createOrder(purchase);

        // ์žฌ๊ณ  ํ™•์ธ ๋ฐ ๊ฐ์†Œ
        inventoryService.updateInventory(purchase.getItems());

        // ๊ฒฐ์ œ ์ฒ˜๋ฆฌ
        paymentService.processPayment(purchase.getPaymentDetails());

        // ์ฃผ๋ฌธ ํ™•์ธ ์ด๋ฉ”์ผ ๋ฐœ์†ก
        notificationService.sendOrderConfirmation(order);
    }
}

@Service
public class OrderService {

    @Transactional(propagation = Propagation.REQUIRED)
    public Order createOrder(Purchase purchase) {
        // ์ฃผ๋ฌธ ์ƒ์„ฑ ๋กœ์ง
    }
}

@Service
public class InventoryService {

    @Transactional(propagation = Propagation.REQUIRED)
    public void updateInventory(List<item> items) {
        // ์žฌ๊ณ  ์—…๋ฐ์ดํŠธ ๋กœ์ง
    }
}

@Service
public class PaymentService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void processPayment(PaymentDetails details) {
        // ๊ฒฐ์ œ ์ฒ˜๋ฆฌ ๋กœ์ง
    }
}

@Service
public class NotificationService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void sendOrderConfirmation(Order order) {
        // ์ด๋ฉ”์ผ ๋ฐœ์†ก ๋กœ์ง
    }
}
</item>

์ด ์˜ˆ์ œ์—์„œ ShoppingService์˜ processPurchase ๋ฉ”์„œ๋“œ๋Š” ์ „์ฒด ๊ตฌ๋งค ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ด€๋ฆฌํ•ด์š”. OrderService์™€ InventoryService๋Š” REQUIRED๋ฅผ ์‚ฌ์šฉํ•ด ์ฃผ ํŠธ๋žœ์žญ์…˜์— ์ฐธ์—ฌํ•˜๊ณ  ์žˆ์–ด์š”. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ฃผ๋ฌธ ์ƒ์„ฑ๊ณผ ์žฌ๊ณ  ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ฒ˜๋ฆฌ๋˜์–ด ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์–ด์š”.

๋ฐ˜๋ฉด, PaymentService์™€ NotificationService๋Š” REQUIRES_NEW๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์–ด์š”. ์™œ ๊ทธ๋Ÿด๊นŒ์š”?

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

์ด๋ ‡๊ฒŒ ์ „ํŒŒ ์†์„ฑ์„ ์ ์ ˆํžˆ ํ™œ์šฉํ•˜๋ฉด, ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ๋„ ๋ฐ์ดํ„ฐ์˜ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ์œ ์—ฐํ•œ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•ด์ ธ์š”. ๐Ÿ˜Š

ํŠธ๋žœ์žญ์…˜ ์ „ํŒŒ ์˜ˆ์‹œ ๋‹ค์ด์–ด๊ทธ๋žจ ShoppingService.processPurchase() OrderService InventoryService PaymentService NotificationService Main Transaction New Transaction New Transaction

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

์ž, ์ด์ œ ์šฐ๋ฆฌ์˜ @Transactional ์—ฌํ–‰์ด ๊ฑฐ์˜ ๋๋‚˜๊ฐ€๊ณ  ์žˆ์–ด์š”. ๋งˆ์ง€๋ง‰์œผ๋กœ ์ „์ฒด ๋‚ด์šฉ์„ ์ •๋ฆฌํ•˜๊ณ  ๋ช‡ ๊ฐ€์ง€ ์ถ”๊ฐ€ ํŒ์„ ๋“œ๋ฆด๊ฒŒ์š”. ์ค€๋น„๋˜์…จ๋‚˜์š”? ๋งˆ์ง€๋ง‰ ์ŠคํผํŠธ ํ•œ๋ฒˆ ํ•ด๋ณผ๊นŒ์š”? ๐Ÿ’ช

๐ŸŽ“ ์ •๋ฆฌ ๋ฐ ์ถ”๊ฐ€ ํŒ

์ž, ์ด์ œ ์šฐ๋ฆฌ์˜ @Transactional ์—ฌํ–‰์ด ๋๋‚˜๊ฐ€๊ณ  ์žˆ์–ด์š”. ์ •๋ง ๊ธด ์—ฌ์ •์ด์—ˆ์ฃ ? ํ•˜์ง€๋งŒ ์—ฌ๋Ÿฌ๋ถ„์€ ์ด์ œ @Transactional์˜ ์ „๋ฌธ๊ฐ€๊ฐ€ ๋˜์—ˆ์–ด์š”! ๐Ÿ‘๐Ÿ‘๐Ÿ‘

์šฐ๋ฆฌ๊ฐ€ ๋ฐฐ์šด ๋‚ด์šฉ์„ ๊ฐ„๋‹จํžˆ ์ •๋ฆฌํ•ด๋ณผ๊นŒ์š”?

  1. @Transactional์€ Spring์—์„œ ํŠธ๋žœ์žญ์…˜์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ์˜ˆ์š”.
  2. ์ „ํŒŒ ์†์„ฑ์„ ํ†ตํ•ด ํŠธ๋žœ์žญ์…˜์˜ ๋™์ž‘์„ ์„ธ๋ฐ€ํ•˜๊ฒŒ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์–ด์š”.
  3. REQUIRED, REQUIRES_NEW, NESTED ๋“ฑ ๋‹ค์–‘ํ•œ ์ „ํŒŒ ์†์„ฑ์ด ์žˆ์œผ๋ฉฐ, ๊ฐ๊ฐ ๋‹ค๋ฅธ ์ƒํ™ฉ์—์„œ ์œ ์šฉํ•ด์š”.
  4. ์ „ํŒŒ ์†์„ฑ์„ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ๋„ ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์–ด์š”.
  5. ํ•˜์ง€๋งŒ ์ „ํŒŒ ์†์„ฑ ์‚ฌ์šฉ ์‹œ ์„ฑ๋Šฅ, ๋ฐ๋“œ๋ฝ, ๋กค๋ฐฑ ๋ฒ”์œ„ ๋“ฑ์„ ์ฃผ์˜ํ•ด์•ผ ํ•ด์š”.

์ด์ œ ๋ช‡ ๊ฐ€์ง€ ์ถ”๊ฐ€ ํŒ์„ ๋“œ๋ฆด๊ฒŒ์š”. ์ด ํŒ๋“ค์„ ๊ธฐ์–ตํ•˜๋ฉด @Transactional ์‚ฌ์šฉ ์‹œ ๋”์šฑ ๋น›๋‚˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”! โœจ

1. ํ…Œ์ŠคํŠธ๋Š” ํ•„์ˆ˜!

ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ๋Š” ๋ณต์žกํ•  ์ˆ˜ ์žˆ์–ด์š”. ํ•ญ์ƒ ๋‹ค์–‘ํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค์— ๋Œ€ํ•ด ์ฒ ์ €ํžˆ ํ…Œ์ŠคํŠธํ•˜์„ธ์š”. ํŠนํžˆ ๋กค๋ฐฑ ์ƒํ™ฉ์„ ๊ผญ ํ…Œ์ŠคํŠธํ•ด๋ณด์„ธ์š”.


@RunWith(SpringRunner.class)
@SpringBootTest
public class ShoppingServiceTest {

    @Autowired
    private ShoppingService shoppingService;

    @Test
    @Transactional
    public void testPurchaseRollback() {
        // ํ…Œ์ŠคํŠธ ๋กœ์ง
    }
}

2. ๋กœ๊น…์€ ์นœ๊ตฌ์˜ˆ์š”

ํŠธ๋žœ์žญ์…˜ ๋™์ž‘์„ ๋กœ๊น…ํ•˜๋ฉด ๋ฌธ์ œ ํ•ด๊ฒฐ์— ํฐ ๋„์›€์ด ๋ผ์š”. Spring์˜ ๋กœ๊น… ์„ค์ •์„ ํ™œ์šฉํ•˜์„ธ์š”.


logging.level.org.springframework.transaction=DEBUG

3. ์ฝ๊ธฐ ์ „์šฉ ํŠธ๋žœ์žญ์…˜ ํ™œ์šฉํ•˜๊ธฐ

๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š” ์—ฐ์‚ฐ์—๋Š” ์ฝ๊ธฐ ์ „์šฉ ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•˜์„ธ์š”. ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋  ์ˆ˜ ์žˆ์–ด์š”.


@Transactional(readOnly = true)
public List<user> getAllUsers() {
    return userRepository.findAll();
}
</user>

4. ํƒ€์ž„์•„์›ƒ ์„ค์ • ์žŠ์ง€ ๋งˆ์„ธ์š”

์žฅ์‹œ๊ฐ„ ์‹คํ–‰๋˜๋Š” ํŠธ๋žœ์žญ์…˜์€ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์–ด์š”. ์ ์ ˆํ•œ ํƒ€์ž„์•„์›ƒ์„ ์„ค์ •ํ•˜์„ธ์š”.


@Transactional(timeout = 30) // 30์ดˆ ํ›„ ํƒ€์ž„์•„์›ƒ
public void longRunningOperation() {
    // ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…
}

5. ์˜ˆ์™ธ ์ฒ˜๋ฆฌ์— ์‹ ๊ฒฝ ์“ฐ๊ธฐ

์ฒดํฌ ์˜ˆ์™ธ์™€ ์–ธ์ฒดํฌ ์˜ˆ์™ธ์— ๋”ฐ๋ผ ํŠธ๋žœ์žญ์…˜์˜ ๋กค๋ฐฑ ๋™์ž‘์ด ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์–ด์š”. ์ด๋ฅผ ์ดํ•ดํ•˜๊ณ  ์ ์ ˆํžˆ ์ฒ˜๋ฆฌํ•˜์„ธ์š”.


@Transactional(rollbackFor = Exception.class)
public void operationWithCustomRollback() throws Exception {
    // ์ž‘์—… ๋กœ์ง
}

๐Ÿ’ก ์ตœ์ข… ๊ฟ€ํŒ: @Transactional์€ ๊ฐ•๋ ฅํ•˜์ง€๋งŒ, ๊ณผ๋„ํ•œ ์‚ฌ์šฉ์€ ํ”ผํ•˜์„ธ์š”. ๋•Œ๋กœ๋Š” ๋‹จ์ˆœํ•œ ๊ฒƒ์ด ๊ฐ€์žฅ ์ข‹์„ ์ˆ˜ ์žˆ์–ด์š”. ํ•ญ์ƒ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋จผ์ € ์ดํ•ดํ•˜๊ณ , ๊ทธ์— ๋งž๋Š” ๊ฐ€์žฅ ์ ์ ˆํ•œ ๋ฐฉ์‹์„ ์„ ํƒํ•˜์„ธ์š”!

์—ฌ๋Ÿฌ๋ถ„, ์ •๋ง ์ˆ˜๊ณ ํ•˜์…จ์–ด์š”! ์ด์ œ ์—ฌ๋Ÿฌ๋ถ„์€ @Transactional๊ณผ ์ „ํŒŒ ์†์„ฑ์˜ ์ง„์ •ํ•œ ๋งˆ์Šคํ„ฐ๊ฐ€ ๋˜์—ˆ์–ด์š”. ์ด ์ง€์‹์„ ํ™œ์šฉํ•ด ๋”์šฑ ์•ˆ์ •์ ์ด๊ณ  ํšจ์œจ์ ์ธ Spring ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค์–ด๋ณด์„ธ์š”. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์žฌ๋Šฅ์œผ๋กœ ์„ธ์ƒ์„ ๋” ํ’์š”๋กญ๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ง์ด์—์š”! ๐Ÿ‘จโ€๐Ÿ’ป๐Ÿ‘ฉโ€๐Ÿ’ป

์•ž์œผ๋กœ์˜ ๊ฐœ๋ฐœ ์—ฌ์ •์— ํ–‰์šด์ด ํ•จ๊ป˜ํ•˜๊ธฐ๋ฅผ ๋ฐ”๋ž„๊ฒŒ์š”. ํ™”์ดํŒ…! ๐Ÿš€๐ŸŒŸ