# Spring @Transactional ์‚ฌ์šฉ

์›๋ณธ ๋ธ”๋กœ๊ทธ ๊ธ€ (opens new window)

# Transactional

Spring ์—์„œ JPA ๊ธฐ์ˆ ์„ ์“ธ ๋•Œ ๋นผ๋†“์„ ์ˆ˜ ์—†๋Š” ๊ธฐ๋Šฅ์ค‘ ํ•˜๋‚˜๋Š” @Transactional ์ด๋‹ค.

์•„๋ž˜์™€ ๊ฐ™์ด ๋งŽ์€ ํŽธ๋ฆฌ์„ฑ์„ ์šฐ๋ฆฌ์—๊ฒŒ ์ œ๊ณตํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

  • transaction begin, commit์„ ์ž๋™ ์ˆ˜ํ–‰ํ•ด์ค€๋‹ค.
  • ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋ฉด, rollback ์ฒ˜๋ฆฌ๋ฅผ ์ž๋™ ์ˆ˜ํ–‰ํ•ด์ค€๋‹ค.

๊ทธ๋ ‡๊ธฐ์—, ๋งŽ์€ ๋ณด์ผ๋Ÿฌ์ฝ”๋“œ๋ฅผ ์ค„ ์ผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์•„๋ž˜์™€ ๊ฐ™์ด ํŽธ๋ฆฌํ•˜๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Note. ๋ฌธ์ œ ์ƒํ™ฉ์„ ์–ต์ง€๋กœ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ์ฝ”๋“œ ํšจ์œจ์„ฑ์ด ์ข‹์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

public class BooksImpl implements Books {
  @Transactional
  public void addBook(String bookName) {
    Book book = new Book(bookName);
    bookRepository.save(book);
    book.setFlag(true);
  }
}

๋ฌธ์ œ๋Š” ์˜ˆ์‹œ์—์„œ ์“ด addBook ๋ฉ”์„œ๋“œ๋ฅผ Books ํด๋ž˜์Šค ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉํ•  ๋•Œ ๋ฐœ์ƒ ํ•  ์ˆ˜ ์žˆ๋‹ค.

public class BooksImpl implements Books {
  public void addBooks(List<String> bookNames) {
    bookNames.forEach(bookName -> this.addBook(bookName));
  }
  
  @Transactional
  public void addBook(String bookName) {
    Book book = new Book(bookName);
    bookRepository.save(book);
    book.setFlag(true);
  }
}

ํ˜น์‹œ, addBooks ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, ์–ด๋–ค๊ฒƒ์ด ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋Š”์ง€ ์˜ˆ์ƒ์ด ๊ฐ€์‹œ๋‚˜์š”?

๋ฌธ์ œ๋Š” addBooks ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์—์„œ ํ˜ธ์ถœํ•˜๋Š” addBook ๋ฉ”์„œ๋“œ์˜ @Transactional ์–ด๋…ธํ…Œ์ด์…˜์ด ์ ์šฉ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ทธ๋ ‡๊ธฐ์—, ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋”๋ผ๋„ Database์—๋Š” ์ €์žฅ๋œ Book ์ •๋ณด์— Flag ์ปฌ๋Ÿผ์— ์ •์ƒ์ ์œผ๋กœ ์—…๋ฐ์ดํŠธ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

bookRepository ์˜ save ๋ฉ”์„œ๋“œ๋Š” ์ž์ฒด์ ์œผ๋กœ @Transactional ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์–ด์žˆ์–ด, ์ •์ƒ์ ์œผ๋กœ ์ฝ”๋“œ๊ฐ€ ์ˆ˜ํ–‰์ด ๋˜์–ด Database์— insert ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ, update ์˜ ์—ญํ• ์„ ํ•˜๋Š” book ๊ฐ์ฒด์˜ ๋ณ€๊ฒฝ๊ฐ์ง€๊ฐ€ ๋™์ž‘ํ•˜์ง€ ์•Š์•„, Flag ์ปฌ๋Ÿผ์— ์—…๋ฐ์ดํŠธ๊ฐ€ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์™œ @Transactional ์ด ๋™์ž‘ํ•˜์ง€ ์•Š์•˜๋Š”์ง€๋Š” Spring ์˜ @Transactional ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๋ฐฉ์‹์— ๋Œ€ํ•ด ์‚ดํŽด๋ณผ ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

# Spring @Transactonal ๊ธฐ๋Šฅ์ œ๊ณต ๋ฐฉ์‹

JPA ์˜ ๊ฐ์ฒด ๋ณ€๊ฒฝ๊ฐ์ง€๋Š” transacton ์ด commit ๋  ๋•Œ, ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ ‡๊ธฐ์— Spring ์€ @Transactonal ์–ด๋…ธํ…Œ์ด์…˜์„ ์„ ์–ธํ•œ ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋˜๊ธฐ์ „, transaction begin ์ฝ”๋“œ๋ฅผ ์‚ฝ์ž…ํ•˜๋ฉฐ

๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋œ ํ›„, transaction commit ์ฝ”๋“œ๋ฅผ ์‚ฝ์ž…ํ•˜์—ฌ, ๊ฐ์ฒด ๋ณ€๊ฒฝ๊ฐ์ง€๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ฒŒ ์œ ๋„ํ•ฉ๋‹ˆ๋‹ค.

Spring ์˜ ์ฝ”๋“œ ์‚ฝ์ž… ๋ฐฉ๋ฒ•์€ ํฌ๊ฒŒ 2๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋ฐ”์ดํŠธ ์ฝ”๋“œ ์ƒ์„ฑ (CGLIB ์‚ฌ์šฉ)
  • ํ”„๋ก์‹œ ๊ฐ์ฒด ์‚ฌ์šฉ

2๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ค‘ Spring ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ํ”„๋ก์‹œ ๊ฐ์ฒด ์‚ฌ์šฉ ์ด ์„ ํƒ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ์— interface ๊ฐ€ ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
SpringBoot ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฐ”์ดํŠธ ์ฝ”๋“œ ์ƒ์„ฑ ์ด ์„ ํƒ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ์—, ๊ตณ์ด interface ๊ฐ€ ํ•„์š”์—†์Šต๋‹ˆ๋‹ค.
๋งŒ์•ฝ ๊ฐœ๋ฐœํ™˜๊ฒฝ์ด SpringBoot ๋ผ๋ฉด Books ์ธํ„ฐํŽ˜์ด์Šค ์—†์ด ๋ด๋„ ๋ฌด๋ฐฉํ•ฉ๋‹ˆ๋‹ค.

์›๋ฆฌ๋Š” ์ด๋ ‡์Šต๋‹ˆ๋‹ค. ํ”„๋ก์‹œ ๊ฐ์ฒด๋กœ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ๋ฉ”์„œ๋“œ๋ฅผ ํ•œ๋ฒˆ ๊ฐ์‹ธ์„œ, ๋ฉ”์„œ๋“œ ์œ„ ์•„๋ž˜๋กœ ์ฝ”๋“œ๋ฅผ ์‚ฝ์ž… ํ•ด์ค๋‹ˆ๋‹ค.

public class BooksProxy {
  private final Books books;
  private final TransactonManager manager = TransactionManager.getInstance();
  
  public BooksProxy(Books books) {
    this.books = books;
  }
  
  public void addBook(String bookName) {
    try {
      manager.begin();
      books.addBook(bookName);
      manager.commit();
    } catch (Exception e) {
      manager.rollback();
    }
  }
}

๊ทธ๋ ‡๊ธฐ์—, ์šฐ๋ฆฌ๋Š” BooksImpl ์ž๋ฃŒํ˜•์„ ์‚ฌ์šฉ ํ•  ๋•Œ, ์Šคํ”„๋ง์ด ์ œ๊ณตํ•˜๋Š” BooksProxy ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉฐ,

BooksProxy ๊ฐ์ฒด๊ฐ€ ์ œ๊ณตํ•˜๋Š” addBook ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ๋งŒ transaction ์ฒ˜๋ฆฌ๊ฐ€ ์ˆ˜ํ–‰๋˜๋Š”๊ฒƒ์ž…๋‹ˆ๋‹ค.

# @Transactonal ์ด ์ˆ˜ํ–‰๋˜์ง€ ์•Š์€ ์ด์œ .

๋‹ค์‹œ ์•„๋ž˜์˜ ์˜ˆ์ œ๋ฅผ ์‚ดํŽด๋ด…๋‹ˆ๋‹ค.

public class BooksImpl implements Books {
  public void addBooks(List<String> bookNames) {
    bookNames.forEach(bookName -> this.addBook(bookName));
  }
  
  @Transactional
  public void addBook(String bookName) {
    Book book = new Book(bookName);
    bookRepository.save(book);
    book.setFlag(true);
  }
}

BooksProxy ๊ฐ€ addBooks ๋ฉ”์„œ๋“œ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์€ ์ˆœ์„œ๋กœ ์ž‘๋™๋ฉ๋‹ˆ๋‹ค.

BooksProxy::addBooks -> BooksImpl::Book

์ฆ‰, BooksImpl ๋‚ด๋ถ€์˜ ์ฝ”๋“œ(addBook) ๊ฐ€ ์ˆ˜ํ–‰ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋Š” ํ”„๋ก์‹œ๋กœ ๊ฐ์‹ธ์ง„ ๋ฉ”์„œ๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ๋Š” ์ ์—์„œ

@Transactonal ์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ๋Šฅ์ด ์ˆ˜ํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

# ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

์‚ฌ์‹ค @Transactional ๋ฉ”์„œ๋“œ๋ฅผ ๋‚ด๋ถ€์ ์œผ๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๊ฒƒ์ด ๊ทผ๋ณธ์ ์ธ ํ•ด๊ฒฐ์ฑ…์ž…๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋งŒ์•ฝ ๊ตณ์ด ์‚ฌ์šฉํ•ด์•ผ ๊ฒ ๋‹ค๋ฉด, ์˜์กด์„ฑ ์ฃผ์ž…์„ ์ด์šฉํ•˜์—ฌ Proxy ์ธ์Šคํ„ด์Šค๋ฅผ ์ž์ฒด์ ์œผ๋กœ ๊ฐ€์ ธ์™€ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

@Service
public class BooksImpl implements Books {
  @Autowired
  private Books self;
  
  public void addBooks(List<String> bookNames) {
    bookNames.forEach(bookName -> self.addBook(bookName)); // this ๊ฐ€ ์•„๋‹Œ ๋ณ€์ˆ˜ self ๋กœ
  }
  
  @Transactional
  public void addBook(String bookName) {
    Book book = new Book(bookName);
    bookRepository.save(book);
    book.setFlag(true);
  }
}

ํ•ด๋‹น ์ฝ”๋“œ๋Š” Books ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ด์šฉํ•˜์—ฌ BooksProxy ์ธ์Šคํ„ด์Šค๋ฅผ ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ๋„๋ก ์œ ๋„ํ•ฉ๋‹ˆ๋‹ค.

์ƒ์„ฑ์ž ์ฃผ์ž…์„ ์‚ฌ์šฉํ•˜๋ฉด, ์ˆœํ™˜ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. @Autowired ๋กœ๋งŒ ํ•ด์ฃผ์„ธ์š”. Autowired ์‹ซ๋‹ค.

๊ทธ ํ›„, ์ž๊ธฐ ์ž์‹  ์ฆ‰ this ๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ˆœ์ˆ˜ํ•œ addBook ๋ฉ”์„œ๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ proxy ๋กœ ๊ฐ์‹ธ์ง„ addBook ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด@Transactional ์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

# ์ •๋ฆฌํ•˜์ž๋ฉด

@Transactonal ์–ด๋…ธํ…Œ์ด์…˜ ๋ฉ”์„œ๋“œ๋Š” ํด๋ž˜์Šค ๋‚ด๋ถ€์ ์œผ๋กœ ์‚ฌ์šฉํ•˜์ง€ ๋ง๊ณ , ๋ฐ–์—์„œ ์‚ฌ์šฉํ•˜์ž. (ํ”„๋ก์‹œ ๊ฐ์ฒด ๋•Œ๋ฌธ์—) ๊ตณ์ด ๋‚ด๋ถ€์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋ ค๋ฉด, ์ž๊ธฐ ์ž์‹ ์˜ Proxy ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜์ž.

์ถœ์ฒ˜: https://mommoo.tistory.com/92 [๊ฐœ๋ฐœ์ž๋กœ ํ™€๋กœ ์„œ๊ธฐ]

Last Updated: 6/18/2023, 2:13:15 PM