Skip to content

Latest commit

ย 

History

History
1343 lines (1330 loc) ยท 76.6 KB

Ch6AOP.md

File metadata and controls

1343 lines (1330 loc) ยท 76.6 KB

#6์žฅ. AOP

์š”์•ฝ ๋ฐ ๊ฒฐ๋ก 

์ฑ… ๋‚ด์šฉ

AOP๋Š” ์Šคํ”„๋ง 3๋Œ€ ๊ธฐ๋ฐ˜๊ธฐ์ˆ  ์ค‘ ํ•˜๋‚˜์ด๋‹ค.(IoC/DI, ์„œ๋น„์Šค ์ถ”์ƒํ™”)

OOP๋ฅผ ๋Œ€์ฒดํ•˜๋ ค๋Š” ๊ฒƒ ์ฒ˜๋Ÿผ ๋ณด์ด๋Š” AOP์˜ ๋“ฑ์žฅ๋ฐฐ๊ฒฝ๊ณผ ์Šคํ”„๋ง์ด ์ด๊ฒƒ์„ ๋„์ž…ํ•œ ์ด์œ , ์žฅ์ ์„ ์ดํ•ดํ•ด์•ผ ํ•œ๋‹ค.

1. ๋“ฑ์žฅ๋ฐฐ๊ฒฝ : ์—ฌ๋Ÿฌ ํ•ต์‹ฌ๊ธฐ๋Šฅ๋“ค์— ์ค‘๋ณต๋˜๋Š” ๋ถ€๊ฐ€๊ธฐ๋Šฅ๋“ค์ด ์žˆ์—ˆ์Œ
2. ์ด์œ  : ๋ถ€๊ฐ€๊ธฐ๋Šฅ๋“ค์„ ๊น”๋”ํ•˜๊ฒŒ ๋ฝ‘์•„๋‚ด๊ณ  ์‹ถ์—ˆ์Œ
3. ์žฅ์  : ์ค‘๋ณต ์—†์ด ๋…๋ฆฝ์ ์ธ ๋ชจ๋“ˆ๋กœ ๊ตฌ๋ถ„ํ•ด๋ƒˆ์Œ. ํ•ต์‹ฌ๊ธฐ๋Šฅ์€ ์ˆœ์ˆ˜ํ•˜๊ฒŒ ๊ทธ ๊ธฐ๋Šฅ์„ ๋‹ด์€ ์ฝ”๋“œ๋กœ๋งŒ ์กด์žฌํ•˜๊ณ  ๋…๋ฆฝ์ ์œผ๋กœ ์‚ดํŽด๋ณผ ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ๋ถ„๋œ ๋ฉด์— ์กด์žฌํ•˜๊ฒŒ ๋จ

์–ด๋“œ๋ฐ”์ด์Šค : ์Šคํ”„๋ง์—์„œ ํƒ€๊นƒ ๊ฐ์ฒด์— ์ ์šฉํ•  ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋‹ด์€ ๊ฐ์ฒด

ํฌ์ธํŠธ์ปท : ๋ฉ”์„œ๋“œ ์„ ์ • ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋‹ด์€ ๊ฐ์ฒด

์„œ๋น„์Šค ๊ณ„์ธต์„ ํŠธ๋žœ์žญ์…˜์ด ์‹œ์ž‘๋˜๊ณ  ์ข…๋ฃŒ๋˜๋Š” ๊ฒฝ๊ณ„๋กœ ์ •ํ–ˆ๋‹ค๋ฉด, ํ…Œ์ŠคํŠธ์™€ ๊ฐ™์€ ํŠน๋ณ„ํ•œ ์ด์œ ๊ฐ€ ์•„๋‹ˆ๊ณ ๋Š” ๋‹ค๋ฅธ ๊ณ„์ธต์ด๋‚˜ ๋ชจ๋“ˆ์—์„œ DAO์— ์ง์ ‘ ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์€ ์ฐจ๋‹จํ•ด์•ผ ํ•œ๋‹ค.

ํ•ต์‹ฌ : 504์ชฝ AOP: ์• ์ŠคํŽ™ํŠธ ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ

Aspect : ๊ทธ ์ž์ฒด๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์„ ๋‹ด๊ณ  ์žˆ์ง€๋Š” ์•Š์ง€๋งŒ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ตฌ์„ฑํ•˜๋Š” ์ค‘์š”ํ•œ ํ•œ ๊ฐ€์ง€ ์š”์†Œ์ด๊ณ  ํ•ต์‹ฌ๊ธฐ๋Šฅ์— ๋ถ€๊ฐ€๋˜์–ด ์˜๋ฏธ๋ฅผ ๊ฐ–๋Š” ํŠน๋ณ„ํ•œ ๋ชจ๋“ˆ์„ ๊ฐ€๋ฆฌํ‚จ๋‹ค.

noaop-withaop

1. ํŠธ๋žœ์žญ์…˜ ์ฝ”๋“œ์˜ ๋ถ„๋ฆฌ

  • ์ฝ”๋“œ๋ฅผ ์ •๋ฆฌํ•ด๋ณธ๋‹ค :
    1. ํ•œ ๋ฉ”์„œ๋“œ๊ฐ€ ํ•˜๋‚˜์˜ ์ฑ…์ž„๋งŒ ๊ฐ–๋„๋ก : UserService.upgradeLevelsInternal()
    2. ํ•œ ํด๋ž˜์Šค๊ฐ€ ํ•˜๋‚˜์˜ ์ฑ…์ž„๋งŒ ๊ฐ–๋„๋ก : UserService์—์„œ ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„์„ค์ • ์ฝ”๋“œ
  1. ๋ฉ”์„œ๋“œ ๋ถ„๋ฆฌ
    • upgradeLevels()์—์„œ ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„์„ค์ • ์ฝ”๋“œ์™€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ฝ”๋“œ๋ฅผ ๋ถ„๋ฆฌํ•œ๋‹ค.
    • ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
      // ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„์„ค์ • ์ฝ”๋“œ๋งŒ ๋‚จ์€ ๋ฉ”์„œ๋“œ
      public void upgradeLevels() {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
          upgradeLevelsInternal();
          transactionManager.commit(status);
        } catch (Exception e) {
          transactionManager.rollback(status);
          throw e;
        }
      }      
      // upgradeLevels์—์„œ ๋น„์ฆˆ๋‹ˆ์Šค๋กœ์ง๋งŒ ๋ถ„๋ฆฌ
      private void upgradeLevelsInternal() {
        List userList = userDao.getAll();
        userList.forEach(user -> {
          if (canUpgradeLevel(user)) {
            upgradeLevel(user);
          }
        });
      }
      
  2. DI๋ฅผ ์ด์šฉํ•œ ํด๋ž˜์Šค์˜ ๋ถ„๋ฆฌ
    • ํŠธ๋žœ์žญ์…˜์„ ๋‹ด๋‹นํ•˜๋Š” ์ฝ”๋“œ๋Š” UserService์— ์žˆ์„ ํ•„์š”๊ฐ€ ์—†์œผ๋‹ˆ ํด๋ž˜์Šค ๋ฐ–์œผ๋กœ ๋ฝ‘์•„๋‚ธ๋‹ค.
    • UserService์— ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋„์ž…ํ•˜๊ณ  ๊ตฌํ˜„ํด๋ž˜์Šค๋ฅผ 2๊ฐœ ๋‘”๋‹ค(UserServiceImpl, UserServiceTx)
    • UserServiceTx๋Š” ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„์„ค์ •์— ๋Œ€ํ•œ ์ฑ…์ž„๋งŒ ๊ฐ–๊ฒŒ๋œ๋‹ค.
    • UserServiceTx๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ „ํ˜€ ๊ฐ–์ง€ ์•Š๊ณ  ๋‹ค๋ฅธ UserService ๊ตฌํ˜„ ์˜ค๋ธŒ์ ํŠธ์— ์œ„์ž„๋งŒ ํ•œ๋‹ค.
    • ํŠธ๋žœ์žญ์…˜์ด ์ ์šฉ๋œ UserServiceTx
    • ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
      @RequiredArgsConstructor
      public class UserServiceTx implements UserService {      
        private final UserService userService;
        private final PlatformTransactionManager transactionManager;  
        ...
        public void upgradeLevels() {
          TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
          try {
            userService.upgradeLevels();
            transactionManager.commit(status);
          } catch (Exception e) {
            transactionManager.rollback(status);
            throw e;
          }
        }
        ...
      }
      
    • ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„์„ค์ • ์ฝ”๋“œ ๋ถ„๋ฆฌ์˜ ์žฅ์ 
      1. UserServiceImpl์€ ํŠธ๋žœ์žญ์…˜ ๋‚ด์šฉ์„ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š์•„๋„ ๋œ๋‹ค.
      2. ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์†์‰ฝ๊ฒŒ ๋งŒ๋“ค์–ด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.(๋‹ค์Œ์ ˆ์—์„œ ํ™•์ธ)
    • @Transactional์ด ๋” ํŽธ๋ฆฌํ•ด ๋ณด์ธ๋‹ค.

2. ๊ณ ๋ฆฝ๋œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ

  • ๊ฐ€์žฅ ํŽธํ•˜๊ณ  ์ข‹์€ ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ•์€ ๊ฐ€๋Šฅํ•œ ํ•œ ์ž‘์€ ๋‹จ์œ„๋กœ ์ชผ๊ฐœ์„œ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
    • ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ–ˆ์„ ๋•Œ ์›์ธ์„ ์ฐพ๊ธฐ ์‰ฝ๋‹ค.
  1. ๋ณต์žกํ•œ ์˜์กด๊ด€๊ณ„ ์†์˜ ํ…Œ์ŠคํŠธ
    • UserService๋Š” ๊ฝค ๋ณต์žกํ•˜๋‹ค
    • UserService ๋’ค์— ์กด์žฌํ•˜๋Š” ํ›จ์”ฌ ๋” ๋งŽ์€ ๊ฐ์ฒด, ํ™˜๊ฒฝ, ์„œ๋น„์Šค, ์„œ๋ฒ„, ๋„คํŠธ์›Œํฌ๊นŒ์ง€ ํ…Œ์ŠคํŠธ ํ•˜๋Š” ์…ˆ์ด๋‹ค.
  2. ํ…Œ์ŠคํŠธ ๋Œ€์ƒ ๊ฐ์ฒด ๊ณ ๋ฆฝ์‹œํ‚ค๊ธฐ
    • ๊ทธ๋ž˜์„œ
      • ์™ธ๋ถ€ ์š”์†Œ๋“ค์— ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๋„๋ก ๊ณ ๋ฆฝ์‹œํ‚ฌ ํ•„์š”๊ฐ€ ์žˆ๋‹ค.
    • ์žฅ์ 
      1. ๋‹ค๋ฅธ ์š”์†Œ๋ฅผ ๋ฐฐ์ œํ•œ UserService์˜ ๊ธฐ๋Šฅ๋งŒ์„ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋‹ค.
      2. ๋‹ค๋ฅธ ์š”์†Œ๋“ค์— ํ•„์š”ํ•œ ์ž์›๋“ค์„ ์†Œ๋ชจํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์ˆ˜ํ–‰ ์„ฑ๋Šฅ์ด ์ข‹๋‹ค.(์†๋„ ํ–ฅ์ƒ)
    • ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ UserServiceImpl ๊ณ ๋ฆฝ
      • MockUserDao, MockMailSender ์‚ฌ์šฉํ•˜๋ฉด ๋จ
    • ๊ณ ๋ฆฝ๋œ ๋‹จ์œ„ํ…Œ์ŠคํŠธ ํ™œ์šฉ
      • upgradeLevels()์— ์ ์šฉ
      • UserDao ๋ชฉ ๊ฐ์ฒด
        • UserServiceTest์ „์šฉ์ด๋ฏ€๋กœ ์Šคํƒœํ‹ฑ ๋‚ด๋ถ€ ํด๋ž˜์Šค๋กœ ์„ ์–ธ
      • ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
        @Test
        public void upgradeLevels() throws Exception {
          // ๊ณ ๋ฆฝ๋œ ํ…Œ์ŠคํŠธ์ด๋ฏ€๋กœ ๋Œ€์ƒ ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•œ๋‹ค.
          UserServiceImpl userService = new UserServiceImpl();
          // ๋ชฉ ๊ฐ์ฒด๋ฅผ setํ•ด์ค€๋‹ค.
          MockUserDao mockUserDao = new MockUserDao(this.userList);
          userService.setUserDao(mockUserDao);
          MockMailSender mockMailSender = new MockMailSender();
          userService.setMailSender(mockMailSender);
          // ๋ชฉ ๊ฐ์ฒด๋ฅผ ๊ฐ€์ง„ UserService์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.
          userService.upgradeLevels();
          // UserService ๊ฒฐ๊ณผ๊ฐ€ ๋‹ด๊ธด ๋ชฉ ๊ฐ์ฒด์˜ ๊ฐ’๋“ค์„ ํ™•์ธํ•œ๋‹ค.
          List updated = mockUserDao.getUpdated();
          assertThat(updated.size()).isEqualTo(3);
          checkUserAndLevel(updated.get(0), userList.get(1).getId(), Level.SILVER);
          checkUserAndLevel(updated.get(1), userList.get(3).getId(), Level.GOLD);
          checkUserAndLevel(updated.get(2), userList.get(4).getId(), Level.GOLD);
          List requests = mockMailSender.getRequests();
          assertThat(requests.size()).isEqualTo(3);
          assertThat(requests.get(0)).isEqualTo(userList.get(1).getEmail());
          assertThat(requests.get(1)).isEqualTo(userList.get(3).getEmail());
          assertThat(requests.get(2)).isEqualTo(userList.get(4).getEmail());
        }
        
  3. ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์™€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ
    • ์ด ์ฑ…์—์„œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ : ์˜์กด ์˜ค๋ธŒ์ ํŠธ๋‚˜ ์™ธ๋ถ€ ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋„๋ก ๊ณ ๋ฆฝ์‹œํ‚จ ํ…Œ์ŠคํŠธ
    • ์ด ์ฑ…์—์„œ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ : ์™ธ๋ถ€์˜ DB, ํŒŒ์ผ ๋“ฑ์˜ ๋ฆฌ์†Œ์Šค๊ฐ€ ์ฐธ์—ฌํ•˜๋Š” ํ…Œ์ŠคํŠธ
    • ์„ ํƒ ๊ฐ€์ด๋“œ๋ผ์ธ
      1. ํ•ญ์ƒ ๋‹จ์œ„ํ…Œ์ŠคํŠธ ๋จผ์ € ๊ณ ๋ ค
      2. ์™ธ๋ถ€๋ฆฌ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•ด์•ผ๋งŒ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ
      3. ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๊ธฐ๊ฐ€ ๋„ˆ๋ฌด ๋ณต์žกํ•˜๋ฉด ์ฒ˜์Œ๋ถ€ํ„ฐ ํ†ตํ•ฉํ…Œ์ŠคํŠธ๋ฅผ ๊ณ ๋ คํ•ด๋ณธ๋‹ค.
  4. ๋ชฉ ํ”„๋ ˆ์ž„์›Œํฌ
    • Mockito ํ”„๋ ˆ์ž„์›Œํฌ
      • ํŠน์ง• : ๋ชฉ ํด๋ž˜์Šค๋ฅผ ์ค€๋น„ํ•  ํ•„์š” ์—†์Œ
      • ์˜ค... ์ข‹์€๋ฐ
      • ์ƒ์„ฑ์ž ์˜์กด์„ฑ ์ฃผ์ž…์œผ๋กœ ๋ฐ”๊พธ๋ฉด ๋” ์ข‹๊ฒ ๋‹ค.
      • ์ข‹์€๋ฐ ์•ž์œผ๋กœ ์•ˆ์“ด๋‹จ๋‹ค. ์™œ์•ˆ์“ฐ์ง€;;
      • ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
        @Test
        public void upgradeLevelsWithMockito() {
          UserServiceImpl userService = new UserServiceImpl();
          UserDao mockUserDao = mock(UserDao.class);
          when(mockUserDao.getAll()).thenReturn(this.userList);
          userService.setUserDao(mockUserDao);
          MailSender mockMailSender = mock(MailSender.class);
          userService.setMailSender(mockMailSender);
          userService.upgradeLevels();
          // ๋ชฉ ๊ฐ์ฒด๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ฒ€์ฆ ๊ธฐ๋Šฅ ํ™œ์šฉ
          // ์–ด๋–ค ๋ฉ”์„œ๋“œ๊ฐ€ ๋ช‡ ๋ฒˆ ํ˜ธ์ถœ๋๋Š”์ง€, ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๋ฌด์—‡์ธ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
          verify(mockUserDao, times(3)).update(any(User.class));
          verify(mockUserDao).update(userList.get(1));
          assertThat(userList.get(1).getLevel()).isEqualTo(Level.SILVER);
          verify(mockUserDao).update(userList.get(3));
          assertThat(userList.get(3).getLevel()).isEqualTo(Level.GOLD);
          verify(mockUserDao).update(userList.get(4));
          assertThat(userList.get(4).getLevel()).isEqualTo(Level.GOLD);
          // ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ •๋ฐ€ํ•˜๊ฒŒ ๊ฒ€์‚ฌํ•˜๊ธฐ ์œ„ํ•ด ์บก์ฒ˜
          ArgumentCaptor mailMessageArgs = ArgumentCaptor.forClass(SimpleMailMessage.class);
          verify(mockMailSender, times(3)).send(mailMessageArgs.capture());
          List mailMessages = mailMessageArgs.getAllValues();
          assertThat(mailMessages.get(0).getTo()[0]).isEqualTo(userList.get(1).getEmail());
          assertThat(mailMessages.get(1).getTo()[0]).isEqualTo(userList.get(3).getEmail());
          assertThat(mailMessages.get(2).getTo()[0]).isEqualTo(userList.get(4).getEmail());
        }
        

3. ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ์™€ ํŒฉํ† ๋ฆฌ ๋นˆ

  1. ํ”„๋ก์‹œ์™€ ํ”„๋ก์‹œ ํŒจํ„ด, ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด
    • ํ”„๋ก์‹œ
      • ํด๋ผ์ด์–ธํŠธ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ๋ณด๊ณ  ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•ต์‹ฌ๊ธฐ๋Šฅ์„ ๊ฐ–๋Š” ํด๋ž˜์Šค(ํƒ€๊นƒ)๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์œผ๋กœ ๊ธฐ๋Œ€ํ•˜์ง€๋งŒ ์‹ค์ œ์ ์œผ๋กœ๋Š” ๋ถ€๊ฐ€๊ธฐ๋Šฅ(ํ”„๋ก์‹œ)์„ ํ†ตํ•ด ํ•ต์‹ฌ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ตฌ์„ฑ
      • ํด๋ผ์ด์–ธํŠธ -> ํ”„๋ก์‹œ -> ํƒ€๊นƒ
      • ํ”„๋ก์‹œ : ๋งˆ์น˜ ์ž์‹ ์ด ์‹ค์ œ ๋Œ€์ƒ์ธ ๊ฒƒ์ฒ˜๋Ÿผ ์œ„์žฅํ•ด์„œ ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์„ ๋Œ€๋ฆฌ์ธ์ฒ˜๋Ÿผ ๋Œ€์‹  ๋ฐ›์•„์คŒ
      • ํƒ€๊นƒ ๋˜๋Š” ์‹ค์ฒด : ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•ด ์ตœ์ข…์ ์œผ๋กœ ์š”์ฒญ์„ ์œ„์ž„๋ฐ›์•„ ์ฒ˜๋ฆฌํ•˜๋Š” ์‹ค์ œ ๊ฐ์ฒด
      • ํ”„๋ก์‹œ์˜ ๋ชฉ์ 
        1. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํƒ€๊นƒ์— ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ œ์–ดํ•˜๊ธฐ ์œ„ํ•จ
        2. ํƒ€๊นƒ์— ๋ถ€๊ฐ€์ ์ธ ๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ•ด์ฃผ๊ธฐ ์œ„ํ•จ
    • ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด
      • ํƒ€๊นƒ์— ๋ถ€๊ฐ€์ ์ธ ๊ธฐ๋Šฅ์„ ๋Ÿฐํƒ€์ž„ ์‹œ์ ์— ๋™์ ์œผ๋กœ ๋ถ€์—ฌํ•ด์ฃผ๊ธฐ ์œ„ํ•ด ํ”„๋ก์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํŒจํ„ด
      • ๋™์ ์ธ ์ด์œ  : ์ปดํŒŒ์ผ ์‹œ์ ์—๋Š” ํ”„๋ก์‹œ์™€ ํƒ€๊นƒ์˜ ๊ด€๊ณ„๊ฐ€ ์ •ํ•ด์ ธ์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ
      • ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์ธ ์ด์œ  : ์—ฌ๋Ÿฌ ๊ฒน์˜ ํฌ์žฅ์ง€์™€ ์žฅ์‹์ฒ˜๋Ÿผ ์‹ค์ œ ๋‚ด์šฉ๋ฌผ์—๋Š” ๋ณ€ํ•จ ์—†์ง€๋งŒ ๋ถ€๊ฐ€์ ์ธ ํšจ๊ณผ๋ฅผ ๋ถ€์—ฌํ•˜๊ธฐ ๋•Œ๋ฌธ
      • ๋Œ€ํ‘œ์ ์ธ ์˜ˆ : InputStream(๋‚ด์šฉ๋ฌผ) - FileInputStream(๋ถ€๊ฐ€๊ธฐ๋Šฅ + ๋‚ด์šฉ๋ฌผ)
      • ์˜ˆ์ œ์—์„œ UserService์™€ UserServiceTx๋„ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด
    • ํ”„๋ก์‹œ ํŒจํ„ด
      • ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋ก์‹œ์™€ ๋””์ž์ธ ํŒจํ„ด ํ”„๋ก์‹œ ํŒจํ„ด์—์„œ์˜ ํ”„๋ก์‹œ๋Š” ๊ตฌ๋ถ„์ด ํ•„์š”ํ•˜๋‹ค.
        • ์ „์ž : ํด๋ผ์ด์–ธํŠธ์™€ ์‚ฌ์šฉ ๋Œ€์ƒ ์‚ฌ์ด์— ๋Œ€๋ฆฌ ์—ญํ• ์„ ๋งก์€ ๊ฐ์ฒด๋ฅผ ๋‘๋Š” ๋ฐฉ๋ฒ•์„ ์ด์นญ
        • ํ›„์ž : ํ”„๋ก์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ• ์ค‘ ํƒ€๊นƒ์— ๋Œ€ํ•œ ์ ‘๊ทผ ๋ฐฉ๋ฒ•์„ ์ œ์–ดํ•˜๋ ค๋Š” ๋ชฉ์ ์„ ๊ฐ€์ง„ ๊ฒฝ์šฐ
      • ํ”„๋ก์‹œ ํŒจํ„ด์—์„œ ํ”„๋ก์‹œ๋Š”
        • ํƒ€๊นƒ์˜ ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•˜๊ฑฐ๋‚˜ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๋Š”๋‹ค.
        • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํƒ€๊นƒ์— ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ์‹์„ ๋ณ€๊ฒฝํ•ด์ค€๋‹ค.
        • ํƒ€๊นƒ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ๊ฐ€ ๋ณต์žกํ•˜๊ฑฐ๋‚˜ ๋‹น์žฅ ํ•„์š”ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ์—๋Š” ๊ผญ ํ•„์š”ํ•œ ์‹œ์ ๊นŒ์ง€ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š๋Š” ํŽธ์ด ์ข‹๋‹ค.
          ๊ทธ๋Ÿฐ๋ฐ ํƒ€๊นƒ ๊ฐ์ฒด์— ๋Œ€ํ•œ ๋ ˆํผ๋Ÿฐ์Šค๊ฐ€ ๋ฏธ๋ฆฌ ํ•„์š”ํ•  ์ˆ˜ ์žˆ๋‹ค.
          ์ด๋Ÿด ๋•Œ ํ”„๋ก์‹œ ํŒจํ„ด์„ ์ ์šฉํ•œ๋‹ค.
          ์‹ค์ œ ํƒ€๊นƒ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š” ๋Œ€์‹  ํ”„๋ก์‹œ๋ฅผ ๋„˜๊ฒจ์ค€๋‹ค.
          ํ”„๋ก์‹œ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ํƒ€๊นƒ์„ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜๋ฉด?
          ๊ทธ๋•Œ ํ”„๋ก์‹œ๊ฐ€ ํƒ€๊นƒ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์š”์ฒญ์„ ์œ„์ž„ํ•˜๋Š” ์‹์ด๋‹ค.
          
    • ์•ž์œผ๋กœ๋Š”...
      • ํƒ€๊นƒ๊ณผ ๋™์ผํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ 
      • ํด๋ผ์ด์–ธํŠธ์™€ ํƒ€๊นƒ ์‚ฌ์ด์— ์กด์žฌํ•˜๋ฉด์„œ
      • ๊ธฐ๋Šฅ์˜ ๋ถ€๊ฐ€ ๋˜๋Š” ์ ‘๊ทผ ์ œ์–ด๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๊ฐ์ฒด
      • ๋ฅผ ๋ชจ๋‘ ํ”„๋ก์‹œ๋ผ๊ณ  ๋ถ€๋ฅด๊ฒ ๋‹ค.
  2. ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ
    • ํ”„๋ก์‹œ๋Š” ๊ธฐ์กด ์ฝ”๋“œ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์œผ๋ฉด์„œ ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•˜๊ฑฐ๋‚˜ ์ ‘๊ทผ ๋ฐฉ๋ฒ•์„ ์ œ์–ดํ•˜๋Š” ์œ ์šฉํ•œ ๋ฐฉ๋ฒ•์ด๋‹ค.
      • ๊ทธ๋Ÿฌ๋‚˜ ๋ฒˆ๊ฑฐ๋กญ๊ธฐ ๋•Œ๋ฌธ์— ์†์ด ์•ˆ๊ฐ„๋‹ค.
      • ํ”„๋ก์‹œ๋„ ๋ชฉ ํ”„๋ ˆ์ž„์›Œํฌ ์ฒ˜๋Ÿผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์„๊นŒ?
    • ํ”„๋ก์‹œ์˜ ๊ตฌ์„ฑ๊ณผ ํ”„๋ก์‹œ ์ž‘์„ฑ์˜ ๋ฌธ์ œ์ 
      • ํ”„๋ก์‹œ์˜ ๊ตฌ์„ฑ
        1. ํƒ€๊นƒ๊ณผ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋‹ค๊ฐ€ ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ํƒ€๊นƒ ๊ฐ์ฒด๋กœ ์œ„์ž„ํ•œ๋‹ค.
        2. ์ง€์ •๋œ ์š”์ฒญ์— ๋Œ€ํ•ด์„œ๋Š” ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
      • UserServiceTx์—์„œ ํ”„๋ก์‹œ์˜ ๊ตฌ์„ฑ
      • ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
           
        public class UserServiceTx implements UserService {          
          @Autowired private PlatformTransactionManager transactionManager;          
          @Autowired private UserServiceImpl userService;          
          // ํƒ€๊นƒ ๊ฐ์ฒด๋กœ ์œ„์ž„
          public void add(User user) { userService.add(user); }          
          public void upgradeLevels() {
            // ๋ถ€๊ฐ€๊ธฐ๋Šฅ ์ˆ˜ํ–‰ ~
            TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
            try {                
              userService.upgradeLevels(); // ํƒ€๊นƒ ๊ฐ์ฒด๋กœ ์œ„์ž„
              transactionManager.commit(status);
            } catch (Exception e) {
              transactionManager.rollback(status);
              throw e;
            }
            // ~ ๋ถ€๊ฐ€๊ธฐ๋Šฅ ์ˆ˜ํ–‰
          }
        }
        
      • ํ”„๋ก์‹œ ์ž‘์„ฑ์˜ ๋ฌธ์ œ์ 
        1. ํƒ€๊นƒ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์œ„์ž„ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ๋ฒˆ๊ฑฐ๋กญ๋‹ค. ๋ฉ”์„œ๋“œ๊ฐ€ ์ถ”๊ฐ€๋˜๊ฑฐ๋‚˜ ๋ณ€๊ฒฝ๋  ๋•Œ ๋งˆ๋‹ค ํ•ด์ค˜์•ผ๋œ๋‹ค.
        2. ๋ถ€๊ฐ€๊ธฐ๋Šฅ ์ฝ”๋“œ ์ค‘๋ณต์ด ๋ชน์‹œ ์‹ฌํ•ด์ง„๋‹ค.
    • ๋ฆฌํ”Œ๋ ‰์…˜
      • JDK์˜ ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ๋Š” ๋ฆฌํ”Œ๋ ‰์…˜ ๊ธฐ๋Šฅ์„ ์ด์šฉํ•ด์„œ ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค์–ด ์ค€๋‹ค.
      • ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
        @Test
        public void invokeMethod() throws Exception {
          String name = "Spring";
          // length
          int nameLength = name.length();
          Method lengthMethod = String.class.getMethod("length");
          assertThat(lengthMethod.invoke(name)).isEqualTo(nameLength);
          // charAt
          int index = 0;
          char charAt = name.charAt(index);
          Method charAtMethod = String.class.getMethod("charAt", int.class);
          assertThat(charAtMethod.invoke(name, index)).isEqualTo(charAt);
        }          
        
    • ํ”„๋ก์‹œ ํด๋ž˜์Šค
      • ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ๋ฅผ ์ด์šฉํ•œ ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค์–ด๋ณธ๋‹ค.
      • HelloProxyTest.simpleProxy()๋Š” ํ”„๋ก์‹œ ์ ์šฉ์˜ ์ผ๋ฐ˜์ ์ธ ๋ฌธ์ œ์  ๋‘๊ฐ€์ง€๋ฅผ ๋ชจ๋‘ ๊ฐ–๋Š”๋‹ค.
    • ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ ์ ์šฉ(UppercaseHandler, HelloProxyTest.dynamicProxy())
      • HelloUppercaseProxy๋ฅผ ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ๋ฅผ ์ด์šฉํ•ด ๋งŒ๋“ค์–ด๋ณธ๋‹ค.
      • Hello ์ธํ„ฐํŽ˜์ด์Šค์— ๋ฉ”์„œ๋“œ๊ฐ€ ์•„๋ฌด๋ฆฌ ๋งŽ์•„๋„ InvocationHandler์—์„œ invoke๋ฉ”์„œ๋“œ ํ•˜๋‚˜๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.
      • ์ถฉ๋ถ„ํžˆ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
      • ๋ฉ”์„œ๋“œ๋ฅผ ์„ ๋ณ„ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
  3. ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ๋ฅผ ์ด์šฉํ•œ ํŠธ๋žœ์žญ์…˜ ๋ถ€๊ฐ€๊ธฐ๋Šฅ
    • UserServiceTx๋ฅผ ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ ๋ฐฉ์‹์œผ๋กœ ๋ณ€๊ฒฝ
    • ๋ณ€๊ฒฝํ•˜๋Š” ์ด์œ ?
      1. ์„œ๋น„์Šค ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ฉ”์„œ๋“œ๋ฅผ ๋ชจ๋‘ ๊ตฌํ˜„ํ•ด์•ผ ํ•จ
      2. ํŠธ๋žœ์žญ์…˜์ด ํ•„์š”ํ•œ ๋ฉ”์„œ๋“œ ๋งˆ๋‹ค ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ์ฝ”๋“œ๊ฐ€ ์ค‘๋ณต
    • ํŠธ๋žœ์žญ์…˜ InvocationHandler
      • ํŠธ๋žœ์žญ์…˜ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๊ฐ–๋Š” ํ•ธ๋“ค๋Ÿฌ
      • ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
        public class TransactionHandler implements InvocationHandler {
          // ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•  ํƒ€๊นƒ ๊ฐ์ฒด. ์–ด๋–ค ํƒ€์ž…์˜ ๊ฐ์ฒด๋“  ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
          @Setter private Object target;
          // ํŠธ๋žœ์žญ์…˜์„ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•œ ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €
          @Autowired private PlatformTransactionManager transactionManager;
          // ํŠธ๋žœ์žญ์…˜์„ ์ ์šฉํ•  ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ํŒจํ„ด
          @Setter private String pattern;
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getName().startsWith(pattern)) {
              return invokeInTransaction(method, args);
            } else {
              return method.invoke(target, args);
            }
          }
          private Object invokeInTransaction(Method method, Object[] args) throws Throwable {
            TransactionStatus status = this.transactionManager.getTransaction(new DefaultTransactionDefinition());
            try {
              Object ret = method.invoke(target, args);
              this.transactionManager.commit(status);
              return ret;
            } catch (InvocationTargetException e) {
              this.transactionManager.rollback(status);
              throw e.getTargetException();
            }
          }
        }
        
    • TransactionHandler์™€ ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ๋ฅผ ์ด์šฉํ•˜๋Š” ํ…Œ์ŠคํŠธ
      • UserServiceTest์— ์ ์šฉ
      • ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
        @Test
        public void upgradeAllOrNothing() {
          TestUserService testUserService = new TestUserService(userList.get(3).getId());
          testUserService.setUserDao(this.userDao);
          testUserService.setMailSender(mailSender);
          TransactionHandler txHandler = new TransactionHandler();
          txHandler.setTarget(testUserService);
          txHandler.setTransactionManager(transactionManager);
          txHandler.setPattern("upgradeLevels");
          UserService txUserService = (UserService)Proxy.newProxyInstance(
                  getClass().getClassLoader(),
                  new Class[] { UserService.class },
                  txHandler
          );
          userDao.deleteAll();
          userList.forEach(user -> userDao.add(user));
          try {
            txUserService.upgradeLevels();
            fail("TestUserServiceException expected");
          } catch (TestUserServiceException e) {
          }
          checkLevelUpgraded(userList.get(1), false);
        }
        
  4. ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ๋ฅผ ์œ„ํ•œ ํŒฉํ† ๋ฆฌ ๋นˆ
    • ์ด์ œ TransactionHandler์™€ ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ๋ฅผ ์Šคํ”„๋ง์˜ DI๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค ์ฐจ๋ก€๋‹ค.
    • ๊ทธ๋Ÿฐ๋ฐ ์ผ๋ฐ˜์ ์ธ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•  ์ˆ˜ ์—†๋Š”๊ฒŒ ๋ฌธ์ œ
      • ์Šคํ”„๋ง ๋นˆ์€ ํด๋ž˜์Šค ์ด๋ฆ„๊ณผ ํ”„๋กœํผํ‹ฐ๋กœ ์ •์˜๋˜๋Š”๋ฐ?
      • ์Šคํ”„๋ง์€ ์ง€์ •๋œ ํด๋ž˜์Šค ์ด๋ฆ„์„ ๊ฐ€์ง€๊ณ  ๋ฆฌํ”Œ๋ ‰์…˜์„ ์ด์šฉํ•ด์„œ ํ•ด๋‹ฌ ํด๋ž˜์Šค์˜ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์š”
      • ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ ๊ฐ์ฒด๋Š” ์ด๋Ÿฐ ์‹์œผ๋กœ ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์ด๋‹ค.
      • ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ๋Š” Proxyํด๋ž˜์Šค์˜ newProxyInstance()๋ผ๋Š” ์Šคํƒœํ‹ฑ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด์„œ๋งŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
    • ํŒฉํ† ๋ฆฌ ๋นˆ
      • ์Šคํ”„๋ง์„ ๋Œ€์‹ ํ•ด์„œ ๊ฐ์ฒด์˜ ์ƒ์„ฑ๋กœ์ง์„ ๋‹ด๋‹นํ•˜๋„๋ก ๋งŒ๋“ค์–ด์ง„ ํŠน๋ณ„ํ•œ ๋นˆ
      • ํŒฉํ† ๋ฆฌ ๋นˆ์„ ๋“ฑ๋กํ•˜๋Š” ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์€ ์Šคํ”„๋ง์˜ FactoryBean์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•
      • ์ƒ์„ฑ์ž๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š” Messageํด๋ž˜์Šค๋ฅผ ํŒฉํ† ๋ฆฌ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๋Š” ์˜ˆ์ œ
      • ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
        // ์ฐธ๊ณ  : https://www.baeldung.com/spring-factorybean
        @RunWith(SpringJUnit4ClassRunner.class)
        @ContextConfiguration(classes = FactoryBeanConfig.class)
        public class MessageFactoryBeanTest {          
          @Autowired
          private ApplicationContext context;          
          @Qualifier("message")
          @Autowired
          private Message message;          
          @Resource(name = "&message")
          private MessageFactoryBean messageFactoryBean;          
          @Test
          public void getMessageFromFactoryBean() throws Exception {
            Object messageFromContext = context.getBean("message");
            assertThat(messageFromContext).isInstanceOf(Message.class);
            assertThat(((Message) messageFromContext).getText()).isEqualTo("Factory Bean");
            assertThat((message).getText()).isEqualTo("Factory Bean");
            Message messageFromFactoryBean = messageFactoryBean.getObject();
            assertThat(messageFromFactoryBean.getText()).isEqualTo("Factory Bean");
          }
        }
        
    • ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ํŒฉํ† ๋ฆฌ ๋นˆ
      • ํŒฉํ† ๋ฆฌ ๋นˆ์„ ์‚ฌ์šฉํ•˜๋ฉด ์Šคํ”„๋ง์˜ ๋นˆ์œผ๋กœ ๋งŒ๋“ค์–ด ์ค„ ์ˆ˜ ์žˆ๊ตฐ(ํŒฉํ† ๋ฆฌ ๋นˆ์˜ getObject ๋ฉ”์„œ๋“œ)

      • FactoryBean์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
        @Setter
        public class TxProxyFactoryBean implements FactoryBean<Object> {
          private Object target;
          private PlatformTransactionManager transactionManager;
          private String pattern;
          private Class<?> serviceInterface;          
          // FactoryBean ๊ตฌํ˜„ ๋ฉ”์„œ๋“œ
          @Override
          public Object getObject() throws Exception {
            TransactionHandler txHandler = new TransactionHandler();
            txHandler.setTarget(target);
            txHandler.setTransactionManager(transactionManager);
            txHandler.setPattern(pattern);
            return Proxy.newProxyInstance(getClass().getClassLoader(),
                    new Class[]{serviceInterface},
                    txHandler);
          }
          public Class<?> getObjectType() {
            return serviceInterface;
          }
            public boolean isSingleton () {
              // ์‹ฑ๊ธ€ํ†ค ๋นˆ์ด ์•„๋‹ˆ๋ผ๋Š” ๋œป์ด ์•„๋‹˜
              // getObject๊ฐ€ ๋งค ๋ฒˆ ๊ฐ™์€ ๊ฐ์ฒด๋ฅผ ๋ฆฌํ„ดํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์˜๋ฏธ
              return false;
          }
        }
        
      • Config ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
        @Configuration
        public class FactoryBeanConfig {          
          @Autowired private UserServiceImpl userServiceImpl;
          @Autowired private PlatformTransactionManager transactionManager;
          @Bean(name = "txProxy")
          public TxProxyFactoryBean txProxyFactoryBean() {
            TxProxyFactoryBean factoryBean = new TxProxyFactoryBean();
            factoryBean.setTarget(userServiceImpl);
            factoryBean.setTransactionManager(transactionManager);
            factoryBean.setPattern("upgradeLevels");
            factoryBean.setServiceInterface(UserService.class);
            return factoryBean;
          }          
        }
        
      • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
        @RunWith(SpringRunner.class)
        @SpringBootTest
        @Slf4j
        class UserServiceTest {
          @Autowired private TxProxyFactoryBean txProxy;
          @Test
          public void upgradeAllOrNothing() throws Exception {
            TestUserService testUserService = new TestUserService(userList.get(3).getId());
            testUserService.setUserDao(this.userDao);
            testUserService.setMailSender(mailSender);
            txProxy.setTarget(testUserService);
            UserService txProxyUserService = (UserService)txProxy.getObject();
        
        userDao.deleteAll();
        userList.forEach(user -> userDao.add(user));
        try {
          txProxyUserService.upgradeLevels();
          fail("TestUserServiceException expected");
        } catch (TestUserServiceException e) {
        }
        checkLevelUpgraded(userList.get(1), false);
        

        } static class TestUserService extends UserServiceImpl { private String id; public TestUserService(String id) { this.id = id; } protected void upgradeLevel(User user) { if (user.getId().equals(this.id)) { throw new TestUserServiceException(); } super.upgradeLevel(user); } } }

  5. ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ ๋ฐฉ์‹์˜ ์žฅ์ ๊ณผ ํ•œ๊ณ„
    • ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ์˜ ์žฌ์‚ฌ์šฉ
      • TransactionHandler๋ฅผ ์ด์šฉํ•˜๋Š” TxProxyFactoryBean์€ ์ฝ”๋“œ ์ˆ˜์ • ์—†์ด ๋‹ค์–‘ํ•œ ํด๋ž˜์Šค์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ ๋ฐฉ์‹์˜ ์žฅ์ 
      • ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด์ด ์ ์šฉ๋œ ํ”„๋ก์‹œ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ ๋‘ ๊ฐ€์ง€๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.
        1. ํ”„๋ก์‹œ ๋Œ€์ƒ ํด๋ž˜์Šค๋ฅผ ์ผ์ผ์ด ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€
        2. ๋ถ€๊ฐ€๊ธฐ๋Šฅ ์ฝ”๋“œ ์ค‘๋ณต
    • ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ์˜ ํ•œ๊ณ„
      1. ์—ฌ๋Ÿฌ ํด๋ž˜์Šค์— ๊ณตํ†ต์ ์ธ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋ ค๋ฉด ์ค‘๋ณต์ด ๋ฐœ์ƒํ•œ๋‹ค.
        • ํ•œ ํด๋ž˜์Šค ์•ˆ์— ์žˆ๋Š” ์—ฌ๋Ÿฌ ๋ฉ”์„œ๋“œ์— ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š”๊ฑด ๊ฐ€๋Šฅํ•˜๋‹ค.
        • ๋น„์Šทํ•œ ์„ค์ •์ด ๊ณ„์† ๋ฐ˜๋ณต๋ผ์„œ ๋‚˜ํƒ€๋‚˜๊ฒŒ ๋  ๊ฒƒ
      2. TransactionHandler ๊ฐ์ฒด๊ฐ€ ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ ๊ฐœ์ˆ˜๋งŒํผ ๋งŒ๋“ค์–ด์งˆ ๊ฒƒ์ด๋‹ค.
        • ํƒ€๊นƒ ๊ฐ์ฒด๋ฅผ ํ”„๋กœํผํ‹ฐ๋กœ ๊ฐ–๊ณ ์žˆ๋‹ค(UserServiceImpl)
        • ๊ทธ๋ž˜์„œ ํƒ€๊นƒ ๊ฐ์ฒด๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ๋ผ๋ฉด ๊ทธ ๊ฐœ์ˆ˜๋งŒํผ ๋นˆ์„ ์„ ์–ธํ•˜๊ณ  ๊ทธ๋งŒํผ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ ๋œ๋‹ค.
        • ์‹ฑ๊ธ€ํ†ค ๋นˆ์œผ๋กœ ๋งŒ๋“œ๋Š”๊ฒŒ ์ข‹๊ฒ ์ง€?
  • ์—ฌ๊ธฐ๊นŒ์ง€ ์š”์•ฝ
AOP ๋ƒ„์ƒˆ๊ฐ€ ๋‚˜๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค.
1 ํŠธ๋žœ์žญ์…˜ ์ฝ”๋“œ์˜ ๋ถ„๋ฆฌ : AOP๋“ค์–ด๊ฐ€๊ธฐ์œ„ํ•œ ์ค€๋น„
2 ๊ณ ๋ฆฝ๋œ ๋‹จ์œ„ํ…Œ์ŠคํŠธ
 - 1์—์„œ ๋ถ„๋ฆฌํ•œ ๋‚ด์šฉ์˜ ์žฅ์ ์„ ํ…Œ์ŠคํŠธ์ฝ”๋“œ์— ์ ์šฉ
 - ์žฅ์  : ์ฝ”๋“œ ๋ถ„๋ฆฌ๋ฅผ ํ†ตํ•ด ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ๋” ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ(๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง UserService ์ฝ”๋“œ์™€ ๋ถ€๊ฐ€๊ธฐ๋Šฅ ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„์„ค์ • ์ฝ”๋“œ ๋ถ„๋ฆฌ)  
 - mockito๋Š” ๋‚ด์šฉ ์„ค๋ช…ํ•˜๋‹ค ์ƒ๊ฐ๋‚˜์„œ ๋ผ์›Œ๋„ฃ์€๊ฑฐ ๊ฐ™์€๋ฐ ์ฐธ ์ข‹์•„๋ณด์˜€๋‹ค. ์•ˆ์“ด๋‹ค๋‹ˆ ์•„์‰ฝ๋‹ค.
3. ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ์™€ ํ”„๋ก์‹œ ํŒจํ„ด
 - ํ”„๋ก์‹œ ํŒจํ„ด์„ ์ ์šฉํ•ด์„œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค.
  - ๊ธฐ์กด ์ฝ”๋“œ์— ์˜ํ–ฅ ์—†์ด ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค.
 - ํ”„๋ก์‹œ๋ฅผ ํŒฉํ† ๋ฆฌ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ–ˆ๋‹ค.
  - ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์ด ์ค‘๋ณต๋˜๋Š” ๊ฒฝ์šฐ ์ค‘๋ณต์„ ์–ด๋Š์ •๋„ ๋ง‰์„ ์ˆ˜ ์žˆ๋‹ค.
  - ํ•˜์ง€๋งŒ ํƒ€๊นƒ ๊ฐ์ฒด๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ๊ฐ€ ๋˜๋Š” ๊ฒฝ์šฐ ์„ค์ • ์ค‘๋ณต์„ ํ”ผํ•  ์ˆ˜ ์—†๋‹ค.
  - AOP ๋ƒ„์ƒˆ๊ฐ€ ๋‚œ๋‹ค.
AOP? ์ค‘๋ณต๋˜๋Š” ๊ธฐ๋Šฅ์„ ์ฝ”๋“œ ์ค‘๋ณต ์—†์ด ๋™์ž‘ํ•˜๊ฒŒ ํ•˜๋Š” ๋ฐฉ๋ฒ•?

4. ์Šคํ”„๋ง์˜ ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ

  1. ProxyFactoryBean
    • ์Šคํ”„๋ง์€ ์ผ๊ด€๋œ ๋ฐฉ๋ฒ•์œผ๋กœ ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ฃผ๋Š” ์ถ”์ƒ๋ ˆ์ด์–ด๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
    • ProxyFactoryBean : ํ”„๋ก์‹œ๋ฅผ ์ƒ์„ฑํ•ด์„œ ๋นˆ ๊ฐ์ฒด๋กœ ๋“ฑ๋กํ•˜๊ฒŒ ํ•ด์ฃผ๋Š” ํŒฉํ† ๋ฆฌ ๋นˆ์ด๋‹ค.
    • ProxyFactoryBean์„ ์ด์šฉํ•œ ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ ํ…Œ์ŠคํŠธ(6-41)
    • ProxyFactoryBean์— ํƒ€๊นƒ ๊ฐ์ฒด๋ฅผ ์„ค์ •ํ•˜๊ณ  ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•œ ๋‹ค์Œ ProxyFactoryBean์—์„œ ์„ค์ •ํ•œ ๊ฐ์ฒด๋ฅผ ๋นผ์˜ค๋Š”์‹
    • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
      public class HelloProxyTest {
        @Test
        public void proxyFactoryBean() {
          ProxyFactoryBean pfBean = new ProxyFactoryBean();
          pfBean.setTarget(new HelloTarget()); //ํƒ€๊นƒ ์„ค์ •
          pfBean.addAdvice(new UppercaseAdvice()); //๋ถ€๊ฐ€๊ธฐ๋Šฅ ์ถ”๊ฐ€
          Hello proxiedHello = (Hello) pfBean.getObject(); //FacotryBean์ด๋ฏ€๋กœ ์ƒ์„ฑ๋œ ํ”„๋ก์‹œ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.      
          assertThat(proxiedHello.sayHello("Toby")).isEqualTo("HELLO TOBY");
          assertThat(proxiedHello.sayHi("Toby")).isEqualTo("HI TOBY");
          assertThat(proxiedHello.sayThankYou("Toby")).isEqualTo("THANKYOU TOBY");
        }      
        static class UppercaseAdvice implements MethodInterceptor {
          public Object invoke(MethodInvocation invocation) throws Throwable {
            String ret = (String)invocation.proceed(); //ํƒ€๊นƒ์„ ์•Œ๊ณ  ์žˆ๊ธฐ์— ํƒ€๊นƒ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ „๋‹ฌํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.
            return ret.toUpperCase(); //๋ถ€๊ฐ€๊ธฐ๋Šฅ ์ ์šฉ
          }
        }      
      }
      
    • ์–ด๋“œ๋ฐ”์ด์ € : ์–ด๋“œ๋ฐ”์ด์Šค(๋ถ€๊ฐ€๊ธฐ๋Šฅ)์™€ ํฌ์ธํŠธ์ปท(๋ฉ”์„œ๋“œ ์„ ์ • ์•Œ๊ณ ๋ฆฌ์ฆ˜)์„ ๋ฌถ์€ ๊ฐ์ฒด
    • ์–ด๋“œ๋ฐ”์ด์Šค : ํƒ€๊นƒ์ด ํ•„์š” ์—†๋Š” ์ˆœ์ˆ˜ํ•œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ
      • UppercaseAdvice์—๋Š” ํƒ€๊นƒ ๊ฐ์ฒด๊ฐ€ ๋“ฑ์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค.(setTarget ํ–ˆ๊ธฐ ๋•Œ๋ฌธ)
      • addAdvice : ์—ฌ๋Ÿฌ ๊ฐœ์˜ MethodInterceptor(๋ถ€๊ฐ€๊ธฐ๋Šฅ)๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.
      • ์–ด๋“œ๋ฐ”์ด์Šค : ํƒ€๊นƒ ๊ฐ์ฒด์— ์ข…์†๋˜์ง€ ์•Š๋Š” ์ˆœ์ˆ˜ํ•œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋‹ด์€ ๊ฐ์ฒด
    • ํฌ์ธํŠธ์ปท : ๋ถ€๊ฐ€๊ธฐ๋Šฅ ์ ์šฉ ๋Œ€์ƒ ๋ฉ”์„œ๋“œ ์„ ์ • ๋ฐฉ๋ฒ•
      • TxProxyFactoryBean์—์„œ๋Š” ๋Œ€์ƒ ๋ฉ”์„œ๋“œ๋ฅผ setPattern์œผ๋กœ ์„ค์ •
      • MethodInterceptor ๊ฐ์ฒด๋Š” ํƒ€๊นƒ ์ •๋ณด๋ฅผ ๊ฐ–๊ณ  ์žˆ์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ฉ”์„œ๋“œ ํŒจํ„ด์œผ๋กœ ๋Œ€์ƒ ๋ฉ”์„œ๋“œ๋ฅผ ์„ ํƒํ•˜๋Š” ๋ฐฉ์‹์€ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.
      • ํ”„๋ก์‹œ์— ๋ถ€๊ฐ€๊ธฐ๋Šฅ ์ ์šฉ ๋Œ€์ƒ ๋ฉ”์„œ๋“œ๋ฅผ ์„ ํƒํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๋„ฃ์œผ๋ฉด ๋œ๋‹ค.
      • ์–ด๋“œ๋ฐ”์ด์Šค๊ฐ€ ํ…œํ”Œ๋ฆฟ์ด ๋˜๊ณ  MethodInvocation ๊ฐ์ฒด๊ฐ€ ์ฝœ๋ฐฑ์ด ๋˜๋Š” ์‹
      • ์Šคํ”„๋ง์ด ์ œ๊ณตํ•˜๋Š” NameMatchMethodPointcut๊ณผ UppercaseAdvice ํ™œ์šฉ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ
      • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
        @Test
        public void pointcutAdvisor() {
          ProxyFactoryBean pfBean = new ProxyFactoryBean();
          pfBean.setTarget(new HelloTarget());
          // ๋ฉ”์„œ๋“œ ์ด๋ฆ„์„ ๋น„๊ตํ•ด์„œ ๋Œ€์ƒ์„ ์„ ์ •ํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ œ๊ณตํ•˜๋Š” ํฌ์ธํŠธ์ปท ๊ฐ์ฒด
          NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
          pointcut.setMappedName("sayH*");
          // ํฌ์ธํŠธ์ปท๊ณผ ์–ด๋“œ๋ฐ”์ด์Šค๋ฅผ advisor๋กœ ๋ฌถ์–ด์„œ ํ•œ ๋ฒˆ์— ์ถ”๊ฐ€
          // ๋ฌถ๋Š” ์ด์œ ? ์–ด๋–ค ์–ด๋“œ๋ฐ”์ด์Šค์— ๋Œ€ํ•ด ์–ด๋–ค ํฌ์ธํŠธ์ปท์„ ์‚ฌ์šฉํ•  ์ง€ ๋ช…ํ™•ํ•˜๊ฒŒ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•ด
          // ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์–ด๋“œ๋ฐ”์ด์Šค์™€ ํฌ์ธํŠธ์ปท์ด ์ถ”๊ฐ€๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ
          pfBean.addAdvisor(new DefaultPointcutAdvisor(pointcut, new UppercaseAdvice()));
          Hello proxiedHello = (Hello) pfBean.getObject();
          assertThat(proxiedHello.sayHello("Toby")).isEqualTo("HELLO TOBY");
          assertThat(proxiedHello.sayHi("Toby")).isEqualTo("HI TOBY");
          assertThat(proxiedHello.sayThankYou("Toby")).isEqualTo("ThankYou Toby");
        }
        
  2. ProxyFactoryBean ์ ์šฉ
    • JDK ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ์˜ ๊ตฌ์กฐ๋ฅผ ๊ทธ๋Œ€๋กœ ์ด์šฉํ•ด์„œ ๋งŒ๋“ค์—ˆ๋˜ TxProxyFactoryBean์„ ์Šคํ”„๋ง์ด ์ œ๊ณตํ•˜๋Š” ProxyFactoryBean์„ ์ด์šฉํ•˜๋„๋ก ์ˆ˜์ •
    • ์–ด๋“œ๋ฐ”์ด์Šค์™€ ํฌ์ธํŠธ์ปท์˜ ์žฌ์‚ฌ์šฉ
      • ProxyFactoryBean์€ ์Šคํ”„๋ง์˜ DI, ํ…œํ”Œ๋ฆฟ/์ฝœ๋ฐฑ ํŒจํ„ด, ์„œ๋น„์Šค ์ถ”์ƒํ™” ๋“ฑ์˜ ๊ธฐ๋ฒ•์ด ๋ชจ๋‘ ์ ์šฉ๋œ ๊ฒƒ์ด๋‹ค.
      • ์•„๋ž˜ ์˜ˆ์ œ์ฝ”๋“œ์—์„œ๋Š” UserService๋งŒ ์ ์šฉํ–ˆ์ง€๋งŒ ๊ทธ ์™ธ์— ๋‹ค๋ฅธ ํด๋ž˜์Šค๋„ ์ด๋ฆ„ ํŒจํ„ด๋งŒ ์ง€์ •ํ•ด์„œ ProxyFactoryBean์— ๋“ฑ๋กํ•ด์ฃผ๋ฉด ๋œ๋‹ค.
    • TransactionAdvice
      • ์˜ˆ์ œ์ฝ”๋“œ
      • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
        @Setter
        public class TransactionAdvice implements MethodInterceptor {
          PlatformTransactionManager transactionManager;
          // ํƒ€๊นƒ์„ ํ˜ธ์ถœํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ์ฝœ๋ฐฑ ๊ฐ์ฒด๋ฅผ ํ”„๋ก์‹œ๋กœ๋ถ€ํ„ฐ ๋ฐ›๋Š”๋‹ค.
          // ๋•๋ถ„์— ์–ด๋“œ๋ฐ”์ด์Šค๋Š” ํŠน์ • ํƒ€๊นƒ์— ์˜์กดํ•˜์ง€ ์•Š๊ณ  ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
          public Object invoke(MethodInvocation methodInvocation) throws Throwable {
            TransactionStatus status = this.transactionManager.getTransaction(new DefaultTransactionDefinition());
            try {
              // ์ฝœ๋ฐฑ์„ ํ˜ธ์ถœํ•ด์„œ ํƒ€๊นƒ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰
              // ํƒ€๊นƒ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์ „ํ›„๋กœ ํ•„์š”ํ•œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋„ฃ์„ ์ˆ˜ ์žˆ๋‹ค.
              // ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ํƒ€๊นƒ์ด ์•„์˜ˆ ํ˜ธ์ถœ๋˜์ง€ ์•Š๊ฒŒ๋” ํ•˜๊ฑฐ๋‚˜ ์žฌ์‹œ๋„๋ฅผ ์œ„ํ•œ ๋ฐ˜๋ณต์  ํ˜ธ์ถœ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.
              Object ret = methodInvocation.proceed();
              this.transactionManager.commit(status);
              return ret;
              // JDK ๋‹ค์ด๋‚ดํ”ฝ ํ”„๋ก์‹œ๊ฐ€ ์ œ๊ณตํ•˜๋Š” Method์™€ ๋‹ฌ๋ฆฌ
              // ์Šคํ”„๋ง์˜ MethodInvocation์„ ํ†ตํ•œ ํƒ€๊นƒ ํ˜ธ์ถœ์€ ์˜ˆ์™ธ๊ฐ€ ํฌ์žฅ๋˜์ง€ ์•Š๊ณ  ํƒ€๊นƒ์—์„œ ๋ณด๋‚ธ ๊ทธ๋Œ€๋กœ ์ „๋‹ฌ๋œ๋‹ค.
            } catch (RuntimeException e) {
              this.transactionManager.rollback(status);
              throw e;
            }
          }
        }
        
    • ์Šคํ”„๋ง XML ์„ค์ •ํŒŒ์ผ
      • ์ฝ”๋“œ๋Š” ์†๋ณผ๊ณณ์ด ์—†๊ณ  ์„ค์ •๋งŒ ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.
      • ์–ด๋“œ๋ฐ”์ด์Šค๋ฅผ ๋“ฑ๋กํ•œ๋‹ค.
        • ์„ค์ •ํŒŒ์ผ(Java Config)
        • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
          @Configuration
          @RequiredArgsConstructor
          public class AopConfig {
            private final PlatformTransactionManager transactionManager;
            private final UserService userService;
            @Bean
            public TransactionAdvice transactionAdvice() {
              return new TransactionAdvice(transactionManager);
            }
            @Bean
            public NameMatchMethodPointcut transactionPointcut() {
              NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
              pointcut.setMappedName("upgrade*");
              return pointcut;
            }
            @Bean
            public DefaultPointcutAdvisor transactionAdvisor() {
              DefaultPointcutAdvisor pointcutAdvisor = new DefaultPointcutAdvisor();
              pointcutAdvisor.setAdvice(transactionAdvice());
              pointcutAdvisor.setPointcut(transactionPointcut());
              return pointcutAdvisor;
            }
            @Bean
            public ProxyFactoryBean userServiceProxyFactoryBean() {
              ProxyFactoryBean factoryBean = new ProxyFactoryBean();
              factoryBean.setTarget(userService);
              factoryBean.setInterceptorNames("transactionAdvisor");
              return factoryBean;
            }
          }
          
        • ํ…Œ์ŠคํŠธ์ฝ”๋“œ
        • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
          @Test
          public void upgradeAllOrNothing() {
            TestUserService testUserService = new TestUserService(userList.get(3).getId());
            testUserService.setUserDao(this.userDao);
            testUserService.setMailSender(mailSender);
            ProxyFactoryBean proxyFactoryBean = context.getBean("&userServiceProxyFactoryBean", ProxyFactoryBean.class);
            proxyFactoryBean.setTarget(testUserService);
            UserService txUserService = (UserService) proxyFactoryBean.getObject();
            try {
              txUserService.upgradeLevels();
              fail("TestUserServiceException expected");
            } catch (TestUserServiceException e) { }
            checkLevelUpgraded(userList.get(1), false);
          }
          

5. ์Šคํ”„๋ง AOP

  • ์ง€๊ธˆ๊นŒ์ง€ : ๋ฐ˜๋ณต์ ์œผ๋กœ ๋“ฑ์žฅํ•˜๋Š” ํŠธ๋žœ์žญ์…˜ ์ฝ”๋“œ๋ฅผ ๊น”๋”ํ•˜๊ณ  ํšจ๊ณผ์ ์œผ๋กœ ๋ถ„๋ฆฌ
    • ํˆฌ๋ช…ํ•œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์ด๋‹ค : ๊ธฐ์กด ์„ค๊ณ„์™€ ์ฝ”๋“œ์—๋Š” ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์Œ(์žˆ๋Š” ๋“ฏ ์—†๋Š” ๋“ฏ)
  1. ์ž๋™ ํ”„๋ก์‹œ ์ƒ์„ฑ
    • ์•„์ง ๋‚จ์€ ๋ฌธ์ œ : ์„ค์ •์ •๋ณด๋ฅผ ๊ณ„์† ์ถ”๊ฐ€ํ•ด์ฃผ๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€
    • ์ค‘๋ณต ๋ฌธ์ œ์˜ ์ ‘๊ทผ ๋ฐฉ๋ฒ•
      • ์ง€๊ธˆ๊นŒ์ง€ ์‚ดํŽด๋ณธ ๋ฐฉ๋ฒ•์—๋Š” ํ•œ ๋ฒˆ์— ์—ฌ๋Ÿฌ ๋นˆ์— ํ”„๋ก์‹œ๋ฅผ ์ ์šฉํ•  ๋งŒํ•œ ๋ฐฉ๋ฒ•์€ ์—†์—ˆ๋‹ค.
    • ๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ์ด์šฉํ•œ ์ž๋™ ํ”„๋ก์‹œ ์ƒ์„ฑ๊ธฐ
      • ๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ : ๋นˆ ๊ฐ์ฒด๋กœ ๋งŒ๋“  ํ›„์— ๊ทธ ๋นˆ ๊ฐ์ฒด๋ฅผ ๋‹ค์‹œ ๊ฐ€๊ณตํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค.
      • ๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๋ฉด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
      • ๋นˆ ๊ฐ์ฒด ์ผ๋ถ€๋ฅผ ํ”„๋ก์‹œ๋กœ ํฌ์žฅํ•˜๊ณ  ํ”„๋ก์‹œ๋ฅผ ๋นˆ์œผ๋กœ ๋Œ€์‹  ๋“ฑ๋กํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
      • ๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ์˜ ๋™์ž‘
      • 1. ๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ๊ฐ€ ๋“ฑ๋ก๋ผ์žˆ์œผ๋ฉด ์Šคํ”„๋ง์€ ๋นˆ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค ๋•Œ ๋งˆ๋‹ค ํ›„์ฒ˜๋ฆฌ๊ธฐ์—๊ฒŒ ๋นˆ์„ ๋ณด๋‚ธ๋‹ค.
        2. ๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ๋Š” ๋“ฑ๋ก๋œ ์–ด๋“œ๋ฐ”์ด์ €์˜ ํฌ์ธํŠธ์ปท์„ ์ด์šฉํ•ด ์ „๋‹ฌ๋ฐ›์€ ๋นˆ์ด ํ”„๋ก์‹œ ์ ์šฉ ๋Œ€์ƒ์ธ์ง€ ํ™•์ธํ•œ๋‹ค.
        3. ์ ์šฉ ๋Œ€์ƒ์ด๋ฉด? ๋‚ด์žฅ๋œ ํ”„๋ก์‹œ ์ƒ์„ฑ๊ธฐ์—๊ฒŒ ํ˜„์žฌ ๋นˆ์— ๋Œ€ํ•œ ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค๊ฒŒ ํ•˜๊ณ  ์–ด๋“œ๋ฐ”์ด์ €๋ฅผ ์—ฐ๊ฒฐํ•œ๋‹ค.
        4. ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค์—ˆ์œผ๋ฉด? ์ „๋‹ฌ๋ฐ›์€ ๋นˆ ๊ฐ์ฒด ๋Œ€์‹  ๋งŒ๋“  ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ๋Œ๋ ค์ค€๋‹ค.
        5. ์Šคํ”„๋ง์€ ๋Œ๋ ค๋ฐ›์€ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๊ณ  ์‚ฌ์šฉํ•œ๋‹ค.
        
    • ํ™•์žฅ๋œ ํฌ์ธํŠธ์ปท
      • ํฌ์ธํŠธ์ปท์€ ํด๋ž˜์Šค ํ•„ํ„ฐ์™€ ๋ฉ”์„œ๋“œ ๋งค์ฒ˜ ๋‘ ๊ฐ€์ง€๋ฅผ ๋Œ๋ ค์ฃผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ–๊ณ ์žˆ๋‹ค.
      • ๋ชจ๋“  ๋นˆ์— ๋Œ€ํ•ด ํ”„๋ก์‹œ ์ ์šฉ ๋Œ€์ƒ์ธ์ง€ ํŒ๋‹จํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์—์„œ๋Š” ํด๋ž˜์Šค์™€ ๋ฉ”์„œ๋“œ ์„ ์ • ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋ชจ๋‘ ๊ฐ–๋Š” ํฌ์ธํŠธ์ปท์ด ํ•„์š”ํ•˜๋‹ค.
    • ํฌ์ธํŠธ์ปท ํ…Œ์ŠคํŠธ
      • ํ™•์žฅ ํฌ์ธํŠธ์ปท ํ•™์Šต ํ…Œ์ŠคํŠธ ์ฝ”๋“œ
      • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
        @Test
        @Description("ํ™•์žฅ ํฌ์ธํŠธ์ปท ํ…Œ์ŠคํŠธ 480p")
        public void classNamePointcutAdvisor() {
          // ํฌ์ธํŠธ์ปท ์ค€๋น„
          NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut() {
            public ClassFilter getClassFilter() {
              // ํด๋ž˜์Šค ์ด๋ฆ„์ด HelloT๋กœ ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ๋งŒ ์„ ํƒ
              return aClass -> aClass.getSimpleName().startsWith("HelloT");
            }
          };
          // sayH๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋งŒ ์„ ํƒ
          pointcut.setMappedName("sayH*");
          checkAdviced(new HelloTarget(), pointcut, true);
          checkAdviced(new HelloWorld(), pointcut, false);
          checkAdviced(new HelloToby(), pointcut, true);
        }          
        class HelloWorld extends HelloTarget { }
        class HelloToby extends HelloTarget { }          
        private void checkAdviced(Object target, Pointcut pointcut, boolean adviced) {
          ProxyFactoryBean pfBean = new ProxyFactoryBean();
          pfBean.setTarget(target);
          pfBean.addAdvisor(new DefaultPointcutAdvisor(pointcut, new UppercaseAdvice()));
          Hello proxiedHello = (Hello) pfBean.getObject();
          if (adviced) {
            assertThat(proxiedHello.sayHello("Toby")).isEqualTo("HELLO TOBY");
            assertThat(proxiedHello.sayHi("Toby")).isEqualTo("HI TOBY");
            assertThat(proxiedHello.sayThankYou("Toby")).isEqualTo("ThankYou Toby");
          } else {
            assertThat(proxiedHello.sayHello("Toby")).isEqualTo("Hello Toby");
            assertThat(proxiedHello.sayHi("Toby")).isEqualTo("Hi Toby");
            assertThat(proxiedHello.sayThankYou("Toby")).isEqualTo("ThankYou Toby");
          }
        }
        
  2. DefaultAdvisorAutoProxyCreator์˜ ์ ์šฉ
    • ํด๋ž˜์Šค ํ•„ํ„ฐ๋ฅผ ์ ์šฉํ•œ ํฌ์ธํŠธ์ปท ์ž‘์„ฑ
      • ์ฃผ์–ด์ง„ ์ด๋ฆ„ ํŒจํ„ด์„ ๊ฐ€์ง€๊ณ  ํด๋ž˜์Šค๋ฅผ ์ฐพ์•„๋‚ด๋Š” ClassFilter๋ฅผ ์ถ”๊ฐ€ํ•˜๋„๋ก ๋งŒ๋“ค ๊ฒƒ์ด๋‹ค.
      • ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
        public class NameMatchClassMethodPointcut extends NameMatchMethodPointcut {
          public void setMappedClassName(String mappedClassName) {
            // ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์€ ํด๋ž˜์Šค ์ด๋ฆ„์„ ์ด์šฉํ•œ ํ•„ํ„ฐ
            this.setClassFilter(new SimpleClassFilter(mappedClassName));
          }
          static class SimpleClassFilter implements ClassFilter {
            private final String mappedName;
            public SimpleClassFilter(String mappedName) {
              this.mappedName = mappedName;
            }
            @Override
            public boolean matches(Class clazz) {
              if (mappedName.startsWith("Test") || clazz.getSimpleName().startsWith("Test")) {
                System.out.println("!!!!!!!!!!!");
              }
              if (mappedName.startsWith("User") || clazz.getSimpleName().startsWith("User")) {
                System.out.println("!!!!!!!!!!!");
              }
              System.out.println(String.format("mappedName : %s, classSimpleName : %s", mappedName, clazz.getSimpleName()));
              return PatternMatchUtils.simpleMatch(mappedName, clazz.getSimpleName());
            }
          }
        }
        
    • ์–ด๋“œ๋ฐ”์ด์ €๋ฅผ ์ด์šฉํ•˜๋Š” ์ž๋™ ํ”„๋ก์‹œ ์ƒ์„ฑ๊ธฐ ๋“ฑ๋ก, ํฌ์ธํŠธ์ปท ๋“ฑ๋ก
      • ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
        @Bean
        public NameMatchClassMethodPointcut transactionPointcut() {
          NameMatchClassMethodPointcut pointcut = new NameMatchClassMethodPointcut();
          // ํด๋ž˜์Šค ์ด๋ฆ„ ํŒจํ„ด
          pointcut.setMappedClassName("*ServiceImpl");
          // ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ํŒจํ„ด
          pointcut.setMappedName("upgrade*");
          return pointcut;
        }          
        @Bean
        public DefaultPointcutAdvisor transactionAdvisor() {
          DefaultPointcutAdvisor pointcutAdvisor = new DefaultPointcutAdvisor();
          pointcutAdvisor.setAdvice(transactionAdvice());
          pointcutAdvisor.setPointcut(transactionPointcut());
          return pointcutAdvisor;
        }          
        @Bean
        public ProxyFactoryBean userServiceProxyFactoryBean() {
          transactionAdvisor();
          ProxyFactoryBean factoryBean = new ProxyFactoryBean();
          factoryBean.setTarget(userService);
          factoryBean.setInterceptorNames("transactionAdvisor");
          return factoryBean;
        }
        
    • ํ…Œ์ŠคํŠธ์ฝ”๋“œ
      • ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
        @Test
        public void upgradeAllOrNothing() {
          userDao.deleteAll();
          userDao.addAll(userList);
          testUserServiceImpl.setMailSender(mailSender);
          try {
            testUserServiceImpl.upgradeLevels();
            fail("TestUserServiceException expected");
          } catch (TestUserServiceException e) { }
          checkLevelUpgraded(userList.get(1), false);
        }
        
    • ์ž๋™์ƒ์„ฑ ํ”„๋ก์‹œ ํ™•์ธ
      • ํฌ์ธํŠธ์ปท ํด๋ž˜์Šค ์ด๋ฆ„ ํ•„ํ„ฐ๋ฅผ *NotServiceImpl๋กœ ๋ฐ”๊ฟ”์„œ ํ™•์ธ
      • ์ž๋™์ƒ์„ฑ๋œ ํ”„๋ก์‹œ ํ™•์ธ(testUserService๊ฐ€ ํ”„๋ก์‹œ๋กœ ๋ณ€๊ฒฝ๋œ ๊ฐ์ฒด์ธ์ง€)
  3. ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹์„ ์ด์šฉํ•œ ํฌ์ธํŠธ์ปท
    • ๋” ํŽธ๋ฆฌํ•œ ํฌ์ธํŠธ์ปท ์ž‘์„ฑ ๋ฐฉ๋ฒ•
    • ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹
      • ์ •๊ทœ์‹ ๊ฐ™์€ ์ผ์ข…์˜ ํ‘œํ˜„์‹ ์–ธ์–ด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํฌ์ธํŠธ์ปท์„ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ์‹œ
      • AspectJExpressionPointcut ํด๋ž˜์Šค ์‚ฌ์šฉ
    • ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹ ๋ฌธ๋ฒ•
      • [] : ์˜ต์…˜(์ƒ๋žต๊ฐ€๋Šฅ)
        | : or์กฐ๊ฑด
        execution([์ ‘๊ทผ์ œํ•œ์ž ํŒจํ„ด] ํƒ€์ž…ํŒจํ„ด1 [ํƒ€์ž…ํŒจํ„ด2.]์ด๋ฆ„ํŒจํ„ด (ํƒ€์ž…ํŒจํ„ด3 | "..", ...) [throws ์˜ˆ์™ธ ํŒจํ„ด])
        ์ ‘๊ทผ์ œํ•œ์ž : public, private ๋“ฑ
        ํƒ€์ž…ํŒจํ„ด1 : ๋ฆฌํ„ด ๊ฐ’์˜ ํƒ€์ž… ํŒจํ„ด
        ํƒ€์ž…ํŒจํ„ด2 : ํŒจํ‚ค์ง€์™€ ํด๋ž˜์Šค ์ด๋ฆ„์— ๋Œ€ํ•œ ํŒจํ„ด. ์‚ฌ์šฉํ•  ๋•Œ๋Š” '.'์œผ๋กœ ์—ฐ๊ฒฐ
        ์ด๋ฆ„ํŒจํ„ด : ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ํŒจํ„ด
        ํƒ€์ž…ํŒจํ„ด3 : ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ํƒ€์ž…์„ ์ˆœ์„œ๋Œ€๋กœ ๋„ฃ์„ ์ˆ˜ ์žˆ๋‹ค. ์™€์ผ๋“œ ์นด๋“œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
        ์˜ˆ์™ธ ํŒจํ„ด : ์˜ˆ์™ธ ์ด๋ฆ„ ํŒจํ„ด
        
      • ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹ ํ…Œ์ŠคํŠธ
      • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
        @Test
        public void methodSignaturePointcut() throws SecurityException, NoSuchMethodException {
          // aspectjweaver ๋””ํŽœ๋˜์‹œ ํ•„์š”
          AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
          pointcut.setExpression("execution(public int toby.common.learningtest.Target.minus(int,int)) ");
          // Target.minus() : ์„ฑ๊ณต
          assertThat(pointcut.getClassFilter().matches(Target.class) &&
                  pointcut.getMethodMatcher().matches(Target.class.getMethod("minus", int.class, int.class), null)).isTrue();
          // Target.plus() : ๋ฉ”์„œ๋“œ ๋งค์ฒ˜์—์„œ ์‹คํŒจ
          assertThat(pointcut.getClassFilter().matches(Target.class) &&
              pointcut.getMethodMatcher().matches(Target.class.getMethod("plus", int.class, int.class), null)).isFalse();
          // Bean.method() : ํด๋ž˜์Šค ๋งค์ฒ˜์—์„œ ์‹คํŒจ
          assertThat(pointcut.getClassFilter().matches(Bean.class) &&
              pointcut.getMethodMatcher().matches(Target.class.getMethod("method"), null)).isFalse();
        }
        
    • ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹ ํ…Œ์ŠคํŠธ
      • // ๋‘ ๊ฐœ์˜ ์ •์ˆ˜ํ˜• ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ–๋Š” minus๋ผ๋Š” ์ด๋ฆ„์˜ ๋ชจ๋“  ๋ฉ”์„œ๋“œ๋ฅผ ์„ ์ •ํ•˜๋Š” ํฌ์ธํŠธ์ปท
        execution(int minus(int, int))
        // ๋ฆฌํ„ดํƒ€์ž… ์ƒ๊ด€์—†๋Š” ํฌ์ธํŠธ์ปท
        execution(* minus(int, int))
        // ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐœ์ˆ˜์™€ ํƒ€์ž…์„ ๋ฌด์‹œํ•˜๋Š” ํฌ์ธํŠธ์ปท
        execution(* minus(..))
        // ๋ชจ๋“  ๋ฉ”์„œ๋“œ ๋‹ค ํ—ˆ์šฉ
        execution(* *(..))
        
      • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ
        @Test
        public void pointcut() throws NoSuchMethodException {
          targetClassPointcutMatches("execution(* *(..))", true, true, true, true, true, true);
          targetClassPointcutMatches("execution(* *(int, int))", false, false, true, true, false, false);
          targetClassPointcutMatches("execution(* *..*get.*(..))", true, true, true, true, true, false);
          targetClassPointcutMatches("execution(void *(..))", true, true, false, false, true, true);
        }
        // ํƒ€๊นƒ ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•ด ํฌ์ธํŠธ์ปท ์„ ์ •์—ฌ๋ถ€๋ฅผ ๊ฒ€์‚ฌํ•˜๋Š” ํ—ฌํผ ๋ฉ”์„œ๋“œ
        public void targetClassPointcutMatches(String expression, boolean... expected) throws NoSuchMethodException {
          pointcutMatches(expression, expected[0], Target.class, "hello");
          pointcutMatches(expression, expected[1], Target.class, "hello", String.class);
          pointcutMatches(expression, expected[2], Target.class, "plus", int.class, int.class);
          pointcutMatches(expression, expected[3], Target.class, "minus", int.class, int.class);
          pointcutMatches(expression, expected[4], Target.class, "method");
          pointcutMatches(expression, expected[5], Bean.class, "method");
        }
        // ํฌ์ธํŠธ์ปท๊ณผ ๋ฉ”์„œ๋“œ๋ฅผ ๋น„๊ตํ•ด์ฃผ๋Š” ํ…Œ์ŠคํŠธ ํ—ฌํผ ๋ฉ”์„œ๋“œ
        private void pointcutMatches(String expression, Boolean expected, Class clazz, String methodName, Class... args) throws NoSuchMethodException {
          AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
          pointcut.setExpression(expression);
          assertThat(pointcut.getClassFilter().matches(clazz) &&
              pointcut.getMethodMatcher().matches(clazz.getMethod(methodName, args), null)).isEqualTo(expected);
        }
        
    • ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹์„ ์ด์šฉํ•˜๋Š” ํฌ์ธํŠธ์ปท ์ ์šฉ
      • @Bean
        public AspectJExpressionPointcut transactionPointcut() {
          AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
          pointcut.setExpression("execution(* *..*ServiceImpl.upgrade*(..))");
          return pointcut;
        }
        
      • ์ฝ”๋“œ์™€ ์„ค์ •์ด ๋‹จ์ˆœํ•ด์ง„๋‹ค.
      • ๋ฌธ์ž์—ด๋กœ ๋œ ํ‘œํ˜„์‹์ด๊ธฐ ๋•Œ๋ฌธ์— ๋Ÿฐํƒ€์ž„ ์‹œ์ ๊นŒ์ง€ ๋ฌธ๋ฒ•์˜ ๊ฒ€์ฆ์ด๋‚˜ ๊ธฐ๋Šฅ ํ™•์ธ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.
    • ํƒ€์ž… ํŒจํ„ด๊ณผ ํด๋ž˜์Šค ์ด๋ฆ„ ํŒจํ„ด
      • TestUserServiceImpl ํด๋ž˜์Šค ์ด๋ฆ„์„ TestUserService๋กœ ๋ฐ”๊พธ๋ฉด ํ…Œ์ŠคํŠธ๋Š” ์‹คํŒจํ•˜๋Š”๊ฐ€?
      • ์Šˆํผํด๋ž˜์Šค๊ฐ€ UserServiceImpl์ด๊ณ  ์ธํ„ฐํŽ˜์ด์Šค๋Š” UserService์ด๊ธฐ ๋•Œ๋ฌธ์— ServiceImpl๋กœ ๋๋‚˜๋Š” ํƒ€์ž… ํŒจํ„ด์˜ ์กฐ๊ฑด์„ ์ถฉ์กฑํ•œ๋‹ค.
      • ํƒ€์ž… ํŒจํ„ด์˜ ์กฐ๊ฑด์„ ์ถฉ์กฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ…Œ์ŠคํŠธ๋Š” ์‹คํŒจํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ์—ฌ๊ธฐ๊นŒ์ง€ ์š”์•ฝ
AOP ๋ƒ„์ƒˆ๊ฐ€ ๋‚˜๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค.
1 ํŠธ๋žœ์žญ์…˜ ์ฝ”๋“œ์˜ ๋ถ„๋ฆฌ : AOP๋“ค์–ด๊ฐ€๊ธฐ์œ„ํ•œ ์ค€๋น„
2 ๊ณ ๋ฆฝ๋œ ๋‹จ์œ„ํ…Œ์ŠคํŠธ
 - 1์—์„œ ๋ถ„๋ฆฌํ•œ ๋‚ด์šฉ์˜ ์žฅ์ ์„ ํ…Œ์ŠคํŠธ์ฝ”๋“œ์— ์ ์šฉ
 - ์žฅ์  : ์ฝ”๋“œ ๋ถ„๋ฆฌ๋ฅผ ํ†ตํ•ด ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ๋” ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ(๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง UserService ์ฝ”๋“œ์™€ ๋ถ€๊ฐ€๊ธฐ๋Šฅ ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„์„ค์ • ์ฝ”๋“œ ๋ถ„๋ฆฌ)  
 - mockito๋Š” ๋‚ด์šฉ ์„ค๋ช…ํ•˜๋‹ค ์ƒ๊ฐ๋‚˜์„œ ๋ผ์›Œ๋„ฃ์€๊ฑฐ ๊ฐ™์€๋ฐ ์ฐธ ์ข‹์•„๋ณด์˜€๋‹ค. ์•ˆ์“ด๋‹ค๋‹ˆ ์•„์‰ฝ๋‹ค.
3. ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ์™€ ํ”„๋ก์‹œ ํŒจํ„ด
 - ํ”„๋ก์‹œ ํŒจํ„ด์„ ์ ์šฉํ•ด์„œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค.
  - ๊ธฐ์กด ์ฝ”๋“œ์— ์˜ํ–ฅ ์—†์ด ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค.
 - ํ”„๋ก์‹œ๋ฅผ ํŒฉํ† ๋ฆฌ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ–ˆ๋‹ค.
  - ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์ด ์ค‘๋ณต๋˜๋Š” ๊ฒฝ์šฐ ์ค‘๋ณต์„ ์–ด๋Š์ •๋„ ๋ง‰์„ ์ˆ˜ ์žˆ๋‹ค.
  - ํ•˜์ง€๋งŒ ํƒ€๊นƒ ๊ฐ์ฒด๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ๊ฐ€ ๋˜๋Š” ๊ฒฝ์šฐ ์„ค์ • ์ค‘๋ณต์„ ํ”ผํ•  ์ˆ˜ ์—†๋‹ค.
  - AOP ๋ƒ„์ƒˆ๊ฐ€ ๋‚œ๋‹ค.
AOP? ์ค‘๋ณต๋˜๋Š” ๊ธฐ๋Šฅ์„ ์ฝ”๋“œ ์ค‘๋ณต ์—†์ด ๋™์ž‘ํ•˜๊ฒŒ ํ•˜๋Š” ๋ฐฉ๋ฒ•?
4. ์Šคํ”„๋ง์˜ ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ
 - ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ์— ํƒ€๊นƒ๊ฐ์ฒด๋ฅผ ์„ค์ •ํ•˜๊ณ  ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•œ ๋‹ค์Œ ์„ค์ •ํ•œ ๊ฐ์ฒด๋ฅผ ํ•˜๋‚˜์”ฉ ๋นผ์˜ค๋Š” ์‹
 - ์–ด๋“œ๋ฐ”์ด์Šค์™€ ํฌ์ธํŠธ์ปท์„ ๋ฌถ์–ด์„œ ์–ด๋“œ๋ฐ”์ด์ €๋ฅผ ๋งŒ๋“ค๊ณ  ํŒฉํ† ๋ฆฌ ๋นˆ์— ๋“ฑ๋กํ•œ๋‹ค.
 - ๊ดœ์ฐฎ๊ธด ํ•œ๋ฐ ์„ค์ •์ •๋ณด๋ฅผ ๊ณ„์† ์ถ”๊ฐ€ํ•ด์ฃผ๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์ด ์žˆ๋‹ค.
5. ์Šคํ”„๋ง AOP
 - ๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํฌ์ธํŠธ์ปท์— ๋งž๋Š” ํƒ€๊นƒ์— ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ• ์‚ฌ์šฉ(NameMatchClassMethodPointcut)
 - ๋” ํŽธํ•œ ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹ ํ™œ์šฉ(aspectjweaver)
 - ๊ฒฐ๊ตญ ๋นˆ์ด ์ƒ๊ธธ ๋•Œ ๋งˆ๋‹ค ํฌ์ธํŠธ์ปท์— ๋งž๋Š” ๋Œ€์ƒ์— ํŠธ๋žœ์žญ์…˜ ๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€๋๊ณ  ๋ถ€๊ฐ€๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€๋œ ๋นˆ์€ ํ”„๋ก์‹œ๋กœ ์กด์žฌํ•˜๊ฒŒ๋จ
  1. AOP๋ž€ ๋ฌด์—‡์ธ๊ฐ€?
    • UserService์— ํŠธ๋žœ์žญ์…˜์„ ์ ์šฉํ•ด์˜จ ๊ณผ์ •์„ ์ •๋ฆฌํ•ด๋ณด๋ฉด
      1. ํŠธ๋žœ์žญ์…˜ ์„œ๋น„์Šค ์ถ”์ƒํ™”
        • ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ์— ๋Œ€ํ•œ ๊ตฌ์ฒด์ ์ธ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์„ ์ž์œ ๋กญ๊ฒŒ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋„๋ก ์ถ”์ƒํ™” ๊ธฐ๋ฒ• ์ ์šฉ
        • ์„œ๋ฒ„ํ™˜๊ฒฝ์—์„œ ์ข…์†๋˜์ง€ ์•Š์Œ
      2. ํ”„๋ก์‹œ์™€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด
        • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ฝ”๋“œ์— ๋ถ€๊ฐ€๊ธฐ๋Šฅ(ํŠธ๋žœ์žญ์…˜) ์ฝ”๋“œ๊ฐ€ ๋ณด์ด์ง€ ์•Š๊ฒŒ๋์Œ
      3. ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ์™€ ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ
        • ๋ถ€๊ฐ€๊ธฐ๋Šฅ ๋ถ€์—ฌ ์ฝ”๋“œ ์ค‘๋ณต๋˜๋Š” ๋ฌธ์ œ ํ•ด๊ฒฐ(JDK ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ)
        • ํฌ์ธํŠธ ์ปท๊ณผ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋ถ„๋ฆฌํ•ด์„œ ์—ฌ๋Ÿฌ ํ”„๋ก์‹œ์—์„œ ๊ณต์œ ํ•˜๋„๋ก ์„ค์ •(ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ)
      4. ์ž๋™ ํ”„๋ก์‹œ ์ƒ์„ฑ ๋ฐฉ๋ฒ•๊ณผ ํฌ์ธํŠธ์ปท
        • ํŠธ๋žœ์žญ์…˜ ๋Œ€์ƒ ๋นˆ ๋งˆ๋‹ค ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ๋นˆ์„ ์„ค์ •ํ•ด์ฃผ๋Š” ๋ฌธ์ œ ํ•ด๊ฒฐ(๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ)
        • ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹ ํ™œ์šฉ(aspectjweaver)
      5. ๋ถ€๊ฐ€๊ธฐ๋Šฅ์˜ ๋ชจ๋“ˆํ™”
        • TransactionAdvice๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ๋ชจ๋“ˆํ™”
    • AOP : Aspect Oriented Programmin
      • noaop-withaop
        • ์™ผ์ชฝ : 2์ฐจ์›์ ์ธ ํ‰๋ฉด ๊ตฌ์กฐ์— ์ค‘๋ณต๋œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ๋“ค ๋ถ„ํฌ
        • ์˜ค๋ฅธ์ชฝ : 3์ฐจ์› ๊ตฌ์กฐ์—์„œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋‹ค๋ฅธ ๋ฉด์— ์กด์žฌํ•˜๋„๋ก ์„ค๊ณ„
      • AOP
        • ๋ถ€๊ฐ€์ ์ธ ๊ธฐ๋Šฅ์„ ๋ถ„๋ฆฌํ•ด์„œ Aspect๋ผ๋Š” ๋…ํŠนํ•œ ๋ชจ๋“ˆ๋กœ ๋งŒ๋“ค์–ด์„œ ์„ค๊ณ„ํ•˜๊ณ  ๊ฐœ๋ฐœํ•˜๋Š” ๋ฐฉ๋ฒ•
        • OOP๋ฅผ ๋•๋Š” ๋ณด์กฐ์ ์ธ ๊ธฐ์ˆ 
        • ๊ด€์  ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ : ์• ํ”Œ๋ฆฌ์ผ€์ž‡๋…€์„ ํŠน์ •ํ•œ ๊ด€์ ์„ ๊ธฐ์ค€์œผ๋กœ ๋ฐ”๋ผ๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค๋Š” ์˜๋ฏธ
      • Aspect
        • ๊ทธ ์ž์ฒด๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์„ ๋‹ด๊ณ  ์žˆ์ง€๋Š” ์•Š์ง€๋งŒ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ตฌ์„ฑํ•˜๋Š” ์ค‘์š”ํ•œ ์š”์†Œ์ด๋‹ค.
        • ํ•ต์‹ฌ๊ธฐ๋Šฅ์— ๋ถ€๊ฐ€๋˜์–ด ์˜๋ฏธ๋ฅผ ๊ฐ–๋Š” ํŠน๋ณ„ํ•œ ๋ชจ๋“ˆ์ด๋‹ค.
        • ๋ถ€๊ฐ€๊ธฐ๋Šฅ์ด ์ •์˜๋œ ์–ด๋“œ๋ฐ”์ด์Šค์™€ ์–ด๋“œ๋ฐ”์ด์Šค์˜ ๋Œ€์ƒ์„ ๊ฒฐ์ •ํ•˜๋Š” ์ •๋ณด๋ฅผ ๋‹ด์€ ํฌ์ธํŠธ์ปท์„ ๊ฐ–๋Š”๋‹ค.
  2. AOP ์ ์šฉ๊ธฐ์ˆ 
    • ํ”„๋ก์‹œ๋ฅผ ์ด์šฉํ•œ AOP
      • ํ”„๋ก์‹œ๋กœ ๋งŒ๋“ค์–ด์„œ DI๋กœ ์—ฐ๊ฒฐ๋œ ๋นˆ ์‚ฌ์ด์— ์ ์šฉํ•ด ํƒ€๊นƒ์˜ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ๊ณผ์ •์— ์ฐธ์—ฌํ•ด์„œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ œ๊ณต
      • ์–ด๋“œ๋ฐ”์ด์Šค๊ฐ€ ์ ์šฉ๋˜๋Š” ๋Œ€์ƒ์€ ๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ์ด๋‹ค.
      • ์Šคํ”„๋ง์˜ AOP๋Š” ํ”„๋ก์‹œ ๋ฐฉ์‹์˜ AOP๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค. : ํ”„๋ก์‹œ๋Š” ๋…๋ฆฝ์ ์ธ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ํƒ€๊นƒ ๋ฉ”์„œ๋“œ์— ๋‹ค์ด๋‚ด๋ฏนํ•˜๊ฒŒ ์ ์šฉํ•ด์ฃผ๊ธฐ ์œ„ํ•œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ์—ญํ• ์„ ๋งก์Œ
    • ๋ฐ”์ดํŠธ์ฝ”๋“œ ์ƒ์„ฑ๊ณผ ์กฐ์ž‘์„ ํ†ตํ•œ AOP
      • ํ”„๋ก์‹œ ๋ฐฉ์‹์ด ์•„๋‹Œ AOP๋Š”? AspectJ ํ”„๋ ˆ์ž„์›Œํฌ
      • ์ปดํŒŒ์ผ๋œ ํƒ€๊นƒ์˜ ํด๋ž˜์Šค ํŒŒ์ผ ์ž์ฒด๋ฅผ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜, JVM์— ๋กœ๋”ฉ๋˜๋Š” ์‹œ์ ์„ ๊ฐ€๋กœ์ฑ„์„œ ๋ฐ”์ดํŠธ์ฝ”๋“œ๋ฅผ ์กฐ์ž‘ํ•˜๋Š” ๋ฐฉ๋ฒ• ์‚ฌ์šฉ
      • ๋Œ€๋ถ€๋ถ„์˜ AOP๋Š” ํ”„๋ก์‹œ ๋ฐฉ์‹์œผ๋กœ๋„ ์ถฉ๋ถ„ํ•˜๋‹ค.
      • AspectJ๋Š” ๋ณต์žกํ•˜์ง€๋งŒ ๋” ๊ฐ•๋ ฅํ•˜๊ณ  ์œ ์—ฐํ•จ
  3. AOP์˜ ์šฉ์–ด
    • ํƒ€๊นƒ : ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ•  ๋Œ€์ƒ
    • ์–ด๋“œ๋ฐ”์ด์Šค : ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋‹ด์€ ๋ชจ๋“ˆ
    • ์กฐ์ธ ํฌ์ธํŠธ : ์–ด๋“œ๋ฐ”์ด์Šค๊ฐ€ ์ ์šฉ๋  ์ˆ˜ ์žˆ๋Š” ์œ„์น˜. ํ”„๋ก์‹œ AOP์—์„œ ์กฐ์ธ ํฌ์ธํŠธ๋Š” ๋ฉ”์„œ๋“œ์˜ ์‹คํ–‰๋‹จ๊ณ„๋ฟ
    • ํฌ์ธํŠธ์ปท : ์–ด๋“œ๋ฐ”์ด์Šค๋ฅผ ์ ์šฉํ•  ์กฐ์ธ ํฌ์ธํŠธ๋ฅผ ์„ ๋ณ„ํ•˜๋Š” ์ž‘์—… ๋˜๋Š” ๊ทธ ๊ธฐ๋Šฅ์„ ์ •์˜ํ•œ ๋ชจ๋“ˆ
    • ํ”„๋ก์‹œ : ํด๋ผ์ด์–ธํŠธ์™€ ํƒ€๊นƒ ์‚ฌ์ด์— ํˆฌ๋ช…ํ•˜๊ฒŒ ์กด์žฌํ•˜๋ฉด์„œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๊ฐ์ฒด
    • ์–ด๋“œ๋ฐ”์ด์ € : ํฌ์ธํŠธ์ปท๊ณผ ์–ด๋“œ๋ฐ”์ด์Šค๋ฅผ ํ•˜๋‚˜ ์”ฉ ๊ฐ–๋Š” ๊ฐ์ฒด
    • ์• ์ŠคํŽ™ํŠธ : AOP์˜ ๊ธฐ๋ณธ ๋ชจ๋“ˆ. ์Šคํ”„๋ง์˜ ์–ด๋“œ๋ฐ”์ด์ €๋Š” ์•„์ฃผ ๋‹จ์ˆœํ•œ ์• ์ŠคํŽ™ํŠธ๋ผ๊ณ  ๋ณผ ์ˆ˜๋„ ์žˆ๋‹ค.
  4. AOP ๋„ค์ž„์ŠคํŽ˜์ด์Šค
    • ์Šคํ”„๋ง AOP๋ฅผ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€ํ–ˆ๋˜ ์–ด๋“œ๋ฐ”์ด์ €, ํฌ์ธํŠธ์ปท, ์ž๋™ ํ”„๋ก์‹œ ์ƒ์„ฑ๊ธฐ ๊ฐ™์€ ๋นˆ๋“ค์€ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ์˜ํ•ด ์ž๋™์œผ๋กœ ์ธ์‹๋ผ์„œ ํŠน๋ณ„ํ•œ ์ž‘์—…์„ ์œ„ํ•ด ์‚ฌ์šฉ๋œ๋‹ค.
    • ์Šคํ”„๋ง์˜ ํ”„๋ก์‹œ ๋ฐฉ์‹ AOP๋ฅผ ์ ์šฉํ•˜๋ ค๋ฉด ์ตœ์†Œํ•œ ๋„ค ๊ฐ€์ง€ ๋นˆ์„ ๋“ฑ๋กํ•ด์•ผ ํ•œ๋‹ค.
      • ์–ด๋“œ๋ฐ”์ด์Šค ์™ธ์—๋Š” ์Šคํ”„๋ง์ด ์ง์ ‘ ์ œ๊ณตํ•˜๋Š” ํด๋ž˜์Šค๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๊ณ  ํ”„๋กœํผํ‹ฐ ์„ค์ •๋งŒ ํ•ด์ค€ ๊ฒƒ
      1. ์ž๋™ ํ”„๋ก์‹œ ์ƒ์„ฑ๊ธฐ
      2. ์–ด๋“œ๋ฐ”์ด์Šค
      3. ํฌ์ธํŠธ์ปท
      4. ์–ด๋“œ๋ฐ”์ด์ €
    • AOP ๋„ค์ž„์ŠคํŽ˜์ด์Šค
      • ์Šคํ”„๋ง์€ AOP์™€ ๊ด€๋ จ ํƒœ๊ทธ๋ฅผ ์ •์˜ํ•ด์ค€ aop์Šคํ‚ค๋งˆ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
      • transaction-config.xml
        <?xml version="1.0" encoding="UTF-8"?>          
        <beans xmlns="http://www.springframework.org/schema/beans"
        	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        	xmlns:aop="http://www.springframework.org/schema/aop"        
        	xsi:schemaLocation="http://www.springframework.org/schema/beans          
        						http://www.springframework.org/schema/beans/spring-beans-3.0.xsd          
        						http://www.springframework.org/schema/aop
        						http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"/>
          ...
        </beans>
        
        AOP ์„ค์ • ๋นˆ
        <aop:config>
          <aop:pointcut id="transactionPointcut" expression="execution(* *..*ServiceImpl.upgrade*(..))" />
          <aop:advisor advice-ref="transactionAdvice" pointcut-ref="transactionPointcut"/>
        </aop:config>
        ์ด์ „ ์„ค์ •
        @Bean
        public TransactionAdvice transactionAdvice() {
          return new TransactionAdvice(transactionManager);
        }
        @Bean
        public AspectJExpressionPointcut transactionPointcut() {
          AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
          pointcut.setExpression("execution(* *..*ServiceImpl.upgrade*(..))");
          return pointcut;
        }
        @Bean
        public DefaultPointcutAdvisor transactionAdvisor() {
          DefaultPointcutAdvisor pointcutAdvisor = new DefaultPointcutAdvisor();
          pointcutAdvisor.setAdvice(transactionAdvice());
          pointcutAdvisor.setPointcut(transactionPointcut());
          return pointcutAdvisor;
        }
        
    • ์–ด๋“œ๋ฐ”์ด์ € ๋‚ด์žฅ ํฌ์ธํŠธ์ปท
      • <aop:config>
          <aop:advisor advice-ref="transactionAdvice" pointcut="execution(* *..*ServiceImpl.upgrade*(..))"/>
        </aop:config>
        

6. ํŠธ๋žœ์žญ์…˜ ์†์„ฑ

  • ํŠธ๋žœ์žญ์…˜ ์†์„ฑ๋“ค์ด ์ด๋Ÿฐ๊ฒŒ ์žˆ๋‹ค ์ •๋„๋งŒ ์•Œ๋ฉด ๋ ๋“ฏ
    ํฌ์ธํŠธ์ปท ๋งˆ๋‹ค ์†์„ฑ์„ ๋‹ค๋ฅด๊ฒŒ ํ•˜๋ฉด ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ๋ชน์‹œ ๋–จ์–ด์งˆ ๋“ฏ
    get~์—๋‹ค๊ฐ€ readOnly๋ถ™์ด๋Š” ์ •๋„๊นŒ์ง€๋Š” ์ข‹๋‹ค๊ณ  ์ƒ๊ฐํ•จ.
    
  • DefaultTransactionDefinition์˜ ์šฉ๋„๋Š”?
  1. ํŠธ๋žœ์žญ์…˜ ์ •์˜
    • ํŠธ๋žœ์žญ์…˜์ด๋ผ๊ณ  ๋ชจ๋‘ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค.
    • ํŠธ๋žœ์žญ์…˜ ์ „ํŒŒ
      • ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„์—์„œ ์ด๋ฏธ ์ง„ํ–‰ ์ค‘์ธ ํŠธ๋žœ์žญ์…˜์ด ์žˆ์„ ๋•Œ ๋˜๋Š” ์—†์„ ๋•Œ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•  ๊ฒƒ์ธ๊ฐ€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๋ฐฉ์‹
      • noaop-withaop
      • B๋Š” AํŠธ๋žœ์žญ์…˜์— ์ข…์†๋ผ์•ผ ํ•˜๋Š”๊ฐ€, ๋…๋ฆฝ์ ์œผ๋กœ ์ž‘๋™ํ•ด์•ผ ํ•˜๋Š”๊ฐ€๋ฅผ ๊ฒฐ์ •
      1. PROPAGATION_REQUIRED
        • ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ์ „ํŒŒ ์†์„ฑ
        • ์ง„ํ–‰ ์ค‘์ธ ํŠธ๋žœ์žญ์…˜์ด ์—†์œผ๋ฉด ์ƒˆ๋กœ ์‹œ์ž‘ํ•˜๊ณ , ์ด๋ฏธ ์‹œ์ž‘๋œ ํŠธ๋žœ์žญ์…˜์ด ์žˆ์œผ๋ฉด ์ฐธ์—ฌํ•œ๋‹ค.
      2. PROPAGATION_REQUIRES_NEW
        • ํ•ญ์ƒ ์ƒˆ๋กœ์šด ํŠธ๋žœ์žญ์…˜์„ ์‹คํ–‰ํ•œ๋‹ค.
      3. PROPAGATION_NOT_SUPPORTED
        • ์ง„ํ–‰ ์ค‘์ธ ํŠธ๋žœ์žญ์…˜์ด ์žˆ์–ด๋„ ๋ฌด์‹œํ•œ๋‹ค.
        • ํฌ์ธํŠธ์ปท์„ ๋ณต์žกํ•˜๊ฒŒ ๋งŒ๋“ค์ง€ ์•Š๊ณ  ํŠน์ • ๊ธฐ๋Šฅ์—๋งŒ ํŠธ๋žœ์žญ์…˜ ์ ์šฉ์ด ์•ˆ๋˜๋„๋ก ์„ค์ •ํ•˜๊ณ ์ž ํ•  ๋•Œ ์‚ฌ์šฉ
        • ์ด๋Ÿฌ๋ฉด ๋” ๋ณต์žกํ• ๊ฑฐ ๊ฐ™์€๋ฐ... config์ชฝ ๋ณด๋Š”๊ฒŒ ์•„๋‹Œ ์ด์ƒ ์ด ๋ฉ”์„œ๋“œ์— ํŠธ๋žœ์žญ์…˜์ด ๊ฑธ๋ ค์žˆ๋Š”์ง€ ์•ˆ๊ฑธ๋ ค์žˆ๋Š”์ง€ ์–ด๋–ป๊ฒŒ์•Œ์•„
    • ๊ฒฉ๋ฆฌ ์ˆ˜์ค€(Isolation level)
      • ๋ชจ๋“  DB ํŠธ๋žœ์žญ์…˜์€ ๊ฒฉ๋ฆฌ์ˆ˜์ค€์„ ๊ฐ–๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค.
      • ๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” DB์— ์„ค์ •๋˜์–ด ์žˆ๊ณ  DefaultTransactionDefinition๋„ ISOLATION_DEFAULT์ด๋‹ค.
      • ํŠน๋ณ„ํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฉ”์„œ๋“œ์˜ ๊ฒฝ์šฐ ๋…์ž์ ์ธ ๊ฒฉ๋ฆฌ์ˆ˜์ค€์„ ์ง€์ •ํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค.
    • ์ œํ•œ์‹œ๊ฐ„
      • ํŠธ๋žœ์žญ์…˜ ์ˆ˜ํ–‰ ์ œํ•œ์‹œ๊ฐ„์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
      • DefaultTransactionDefinition์˜ ๊ธฐ๋ณธ ์„ค์ •์€ ์ œํ•œ์‹œ๊ฐ„์ด ์—†๋Š” ๊ฒƒ์ด๋‹ค.
    • ์ฝ๊ธฐ์ „์šฉ
      • ์ฝ๊ธฐ์ „์šฉ ํŠธ๋žœ์žญ์…˜์€ ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ๋ฐ์ดํ„ฐ์กฐ์ž‘ ์‹œ๋„๋ฅผ ๋ง‰์•„์ค„ ์ˆ˜ ์žˆ๋‹ค.
  2. ํŠธ๋žœ์žญ์…˜ ์ธํ„ฐ์…‰ํ„ฐ์™€ ํŠธ๋žœ์žญ์…˜ ์†์„ฑ
  • ๋ฉ”์„œ๋“œ ๋ณ„๋กœ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜ ์ •์˜๋ฅผ ์ ์šฉํ•˜๋ ค๋ฉด ์–ด๋“œ๋ฐ”์ด์Šค์˜ ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•ด์•ผ ํ•œ๋‹ค.  
    
    • TransactionInterceptor
      • ์Šคํ”„๋ง์ด ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„์„ค์ • ์–ด๋“œ๋ฐ”์ด์Šค๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“  ์ธํ„ฐ์…‰ํ„ฐ.
      • PlatformTransactionMnager์™€ Properties ํƒ€์ž…์˜ ๋‘ ๊ฐ€์ง€ ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ฐ–๋Š”๋‹ค.
      • Properties๋Š” Mapํƒ€์ž… ๊ฐ์ฒด๋‹ค. ๋ฉ”์„œ๋“œ ํŒจํ„ด์— ๋”ฐ๋ผ ๊ฐ๊ธฐ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜ ์†์„ฑ์„ ๋ถ€์—ฌํ•˜๊ธฐ ์œ„ํ•ด Mapํƒ€์ž…์œผ๋กœ ๋ฐ›๋Š”๋‹ค.
    • ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ํŒจํ„ด์„ ํ†ตํ•œ ํŠธ๋žœ์žญ์…˜ ์†์„ฑ ์ง€์ •
      • TransactionInterceptor์˜ Properties์˜ ํ‚ค

      • PROPAGATION_NAME // ํŠธ๋žœ์žญ์…˜ ์ „ํŒŒ ๋ฐฉ์‹. ํ•„์ˆ˜๊ฐ’
        , ISOLATION_NAME // ๊ฒฉ๋ฆฌ ์ˆ˜์ค€. ์ƒ๋žต ๊ฐ€๋Šฅ(deafult=๋””ํดํŠธ๊ฒฉ๋ฆฌ์ˆ˜์ค€)
        , readOnly // ์ฝ๊ธฐ์ „์šฉ. ์ƒ๋žต ๊ฐ€๋Šฅ(default=์ฝ๊ธฐ์ „์šฉ์•„๋‹˜)
        , timeout_NNNN // ์ œํ•œ์‹œ๊ฐ„. NNNN์€ ์ดˆ๋‹จ์œ„ ์‹œ๊ฐ„. ์ƒ๋žต ๊ฐ€๋Šฅ
        , -Exception1 // ์ฒดํฌ ์˜ˆ์™ธ ์ค‘ ๋กค๋ฐฑ ๋Œ€์ƒ์œผ๋กœ ์ถ”๊ฐ€ํ•  ์˜ˆ์™ธ. ํ•œ ๊ฐœ ์ด์ƒ ๋“ฑ๋ก๊ฐ€๋Šฅ
        , +Exception2 // ๋Ÿฐํƒ€์ž„ ์˜ˆ์™ธ์ง€๋งŒ ๋กค๋ฐฑ์‹œํ‚ค์ง€ ์•Š์„ ์˜ˆ์™ธ. ํ•œ ๊ฐœ ์ด์ƒ ๋“ฑ๋ก๊ฐ€๋Šฅ
        
      • ํŠธ๋žœ์žญ์…˜ ์†์„ฑ ์ •์˜ ์˜ˆ์‹œ
        @Bean
        public TransactionInterceptor transactionInterceptor() {
          TransactionInterceptor interceptor = new TransactionInterceptor();
          interceptor.setTransactionManager(transactionManager);
        

        NameMatchTransactionAttributeSource txAttributeSource = new NameMatchTransactionAttributeSource(); Map<String, TransactionAttribute> txMethods = new HashMap();

        RuleBasedTransactionAttribute txAttributeForAll = new RuleBasedTransactionAttribute(); txAttributeForAll.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); txMethods.put("*", txAttributeForAll);

        RuleBasedTransactionAttribute txAttributeForGet = new RuleBasedTransactionAttribute(); txAttributeForGet.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); txAttributeForGet.setReadOnly(true); txAttributeForGet.setTimeout(30); txMethods.put("get*", txAttributeForGet);

        RuleBasedTransactionAttribute txAttributeForUpgrade = new RuleBasedTransactionAttribute(); txAttributeForUpgrade.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); txAttributeForUpgrade.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE); txMethods.put("upgrade*", txAttributeForGet);

        txAttributeSource.setNameMap(txMethods); interceptor.setTransactionAttributeSources(txAttributeSource); return interceptor; }

    • tx ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ฅผ ์ด์šฉํ•œ ์„ค์ • ๋ฐฉ๋ฒ•
      • // transaction-config.xml
        <?xml version="1.0" encoding="UTF-8"?>          
        <beans xmlns="http://www.springframework.org/schema/beans"          
        	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"          
        	xmlns:aop="http://www.springframework.org/schema/aop"          
        	xmlns:tx="http://www.springframework.org/schema/tx"          
        	xmlns:context="http://www.springframework.org/schema/context"          
        	xsi:schemaLocation="http://www.springframework.org/schema/beans
          					http://www.springframework.org/schema/beans/spring-beans-3.0.xsd          
        						http://www.springframework.org/schema/aop          
        						http://www.springframework.org/schema/aop/spring-aop-3.0.xsd         
        						http://www.springframework.org/schema/context          
        						http://www.springframework.org/schema/context/spring-context-3.0.xsd          
        						http://www.springframework.org/schema/tx           
        						http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
        ...
        // ์˜คํƒ€๋‚˜๋ฉด? Enumeration์œผ๋กœ ์Šคํ‚ค๋งˆ์— ๊ฐ’์ด ์ •์˜๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— XML ์œ ํšจ์„ฑ๊ฒ€์‚ฌ๋กœ ํ™•์ธ ๊ฐ€๋Šฅํ•˜๋‹จ๋‹ค.
        <tx:advice // ์ด ํƒœ๊ทธ์— ์˜ํ•ด TransactionInterceptor ๋นˆ์ด ๋“ฑ๋ก๋œ๋‹ค. 
          id="transactionAdvice transaction-manger="transactionManager"> // ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €์™€ ๋นˆ ์•„์ด๋””๊ฐ€ transactionManager๋ผ๋ฉด ์ƒ๋žต ๊ฐ€๋Šฅ\
          <tx:attributes>
            <tx:method name="get*" propagation="REQUIRED" read-only="true" timeout="30" />
            <tx:method name="upgrade*" propagation="REQUIRES_NEW" isolation="SERIALIZABLE" />
            <tx:method name="*" propagation="REQUIRED" />
          </tx:attributes>
        </tx:advice>
        
  1. ํฌ์ธํŠธ์ปท๊ณผ ํŠธ๋žœ์žญ์…˜ ์†์„ฑ์˜ ์ ์šฉ ์ „๋žต
    • ํŠธ๋žœ์žญ์…˜ ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹์€ ํƒ€์ž… ํŒจํ„ด์ด๋‚˜ ๋นˆ ์ด๋ฆ„์„ ์ด์šฉํ•œ๋‹ค.
      • ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„๋กœ ์‚ผ์„ ํด๋ž˜์Šค๋“ค์ด ๋ชจ์—ฌ์žˆ๋Š” ํŒจํ‚ค์ง€๋ฅผ ํ†ต์งธ๋กœ ์„ ํƒ
      • ํด๋ž˜์Šค ์ด๋ฆ„์—์„œ ์ผ์ •ํ•œ ํŒจํ„ด์„ ์ฐพ์•„์„œ ํ‘œํ˜„์‹ ๊ฒฐ์ • : execution(* *..*Service.*(..))
      • ์Šคํ”„๋ง์˜ ๋นˆ ์ด๋ฆ„์„ ์ด์šฉ : bean(*Service)
    • ๊ณตํ†ต๋œ ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ๊ทœ์น™์„ ํ†ตํ•ด ์ตœ์†Œํ•œ์˜ ํŠธ๋žœ์žญ์…˜ ์–ด๋“œ๋ฐ”์ด์Šค์™€ ์†์„ฑ์„ ์ •์˜ํ•œ๋‹ค.
      • ์‹ค์ œ๋กœ ํ•˜๋‚˜์˜ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์‚ฌ์šฉํ•  ํŠธ๋žœ์žญ์…˜ ์†์„ฑ์˜ ์ข…๋ฅ˜๋Š” ๊ทธ๋‹ค์ง€ ๋‹ค์–‘ํ•˜์ง€ ์•Š๋‹ค.
      • ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ํŠธ๋žœ์žญ์…˜ ์†์„ฑ ๋ถ€์—ฌ ๋ฐฉ๋ฒ•์€ ๋ชจ๋“  ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•ด ๋””ํดํŠธ ์†์„ฑ์„ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๊ฐœ๋ฐœ ์ง„์ฒ™๋„์— ๋”ฐ๋ผ ๋‹จ๊ณ„์ ์œผ๋กœ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋จ
      • get*, find*๋“ฑ ๋ช…๋ช…๊ทœ์น™์„ ์„ค์ •ํ•ด์„œ ํŠธ๋žœ์žญ์…˜์„ ์ ์šฉํ•˜๋ฉด ์ข‹์Œ
    • ํ”„๋ก์‹œ ๋ฐฉ์‹ AOP๋Š” ๊ฐ™์€ ํƒ€๊นƒ ๊ฐ์ฒด ๋‚ด์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋Š” ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.
      • ๊ทธ๋ ‡์ง€์š”? ํด๋ผ์ด์–ธํŠธ๋Š” ํ”„๋ก์‹œ๋ฅผ ํ˜ธ์ถœํ•˜๊ฒŒ๋” ์„ค์ •๋ผ์žˆ๋Š”๋ฐ ํƒ€๊นƒ ๊ฐ์ฒด ๋‚ด์—์„œ๋Š” ํ”„๋ก์‹œ๋ฅผ ๋ชป๋ถ€๋ฅด๋‹ˆ๊นŒ
      • ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋‚ด์šฉ
  2. ํŠธ๋žœ์žญ์…˜ ์†์„ฑ ์ ์šฉ
    • UserService์— ์ ์šฉํ•ด๋ณด์ž
    • ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„์„ค์ •์˜ ์ผ์›ํ™”
      • *์ค‘์š”* ์„œ๋น„์Šค ๊ณ„์ธต์„ ํŠธ๋žœ์žญ์…˜์ด ์‹œ์ž‘๋˜๊ณ  ์ข…๋ฃŒ๋˜๋Š” ๊ฒฝ๊ณ„๋กœ ์ •ํ–ˆ๋‹ค๋ฉด, ํ…Œ์ŠคํŠธ์™€ ๊ฐ™์€ ํŠน๋ณ„ํ•œ ์ด์œ ๊ฐ€ ์•„๋‹ˆ๊ณ ๋Š” ๋‹ค๋ฅธ ๊ณ„์ธต์ด๋‚˜ ๋ชจ๋“ˆ์—์„œ DAO์— ์ง์ ‘ ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์€ ์ฐจ๋‹จํ•ด์•ผ ํ•œ๋‹ค.
      • ์•„๋ž˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ๊ตฌํ˜„ํ•ด์„œ User๊ด€๋ จ ๋ฐ์ดํ„ฐ ์กฐ์ž‘์€ UserService๋ผ๋Š” ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„๋ฅผ ํ†ตํ•ด ์ง„ํ–‰ํ•  ๊ฒฝ์šฐ ๋ชจ๋‘ ํŠธ๋žœ์žญ์…˜์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋๋‹ค.
      • UserService์— ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€
        public interface UserService {
          // ...
          User get(String id);
          List getAll();
          void deleteAll();
          void update(User user);
        }
        
    • ์„œ๋น„์Šค ๋นˆ์— ์ ์šฉ๋˜๋Š” ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹ ๋“ฑ๋ก
      • ๋นˆ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•œ ํ‘œํ˜„์‹์„ ๊ฐ–๋Š” ํฌ์ธํŠธ์ปท๊ณผ ์–ด๋“œ๋ฐ”์ด์ €
      • <aop:config>
          <aop:advisor advice-ref="transactionAdvice" pointcut="bean(*Service)"/>
        </aop:config>
        
    • ํŠธ๋žœ์žญ์…˜ ์†์„ฑ์„ ๊ฐ€์ง„ ํŠธ๋žœ์žญ์…˜ ์–ด๋“œ๋ฐ”์ด์Šค ๋“ฑ๋ก
      • ์ด์ „์— ์„ค์ •ํ–ˆ๋˜ transaction-config.xml ๊ทธ๋Œ€๋กœ ๋‘๋ฉด ๋
    • ํŠธ๋žœ์žญ์…˜ ์†์„ฑ ํ…Œ์ŠคํŠธ
      • ํฌ์ธํŠธ์ปท์„ Bean์œผ๋กœ ์„ค์ •ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— testUserServiceImpl๋กœ ์„ค์ •ํ–ˆ๋˜ ๋นˆ ์ด๋ฆ„ testUserService๋กœ ๋ณ€๊ฒฝ
      • ์ฝ๊ธฐ์ „์šฉ ์†์„ฑ ํ…Œ์ŠคํŠธ
      • ์•ˆ๋˜๋Š”๋ฐ..? h2๋Š” ์•ˆ๋œ๋‹ค๋Š”๋ฐ mariadb๋กœ ํ•ด๋ด์•ผ๊ฒ ๋‹ค
      • mariadb๋กœ ํ•˜๋‹ˆ๊นŒ ๋œ๋‹ค(TransientDataAccessResourceException)

7. ์• ๋…ธํ…Œ์ด์…˜ ํŠธ๋žœ์žญ์…˜ ์†์„ฑ๊ณผ ํฌ์ธํŠธ์ปท

  1. ํŠธ๋žœ์žญ์…˜ ์• ๋…ธํ…Œ์ด์…˜
    • ์ž๋ฐ” 5์—์„œ ๋“ฑ์žฅ
    • @Target({ElementType.TYPE, ElementType.METHOD})
      @Retention(RetentionPolicy.RUNTIME) // ์• ๋…ธํ…Œ์ด์…˜ ์ •๋ณด๊ฐ€ ์–ธ์ œ๊นŒ์ง€ ์œ ์ง€๋  ์ง€ ์ง€์ •. ์ด๋ ‡๊ฒŒ ์„ค์ •ํ•˜๋ฉด ๋Ÿฐํƒ€์ž„ ๋•Œ๋„ ๋ฆฌํ”Œ๋ ‰์…˜์„ ํ†ตํ•ด ์• ๋…ธํ…Œ์ด์…˜ ์ •๋ณด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.
      @Inherited // ์ƒ์†์„ ํ†ตํ•ด์„œ๋„ ์• ๋…ธํ…Œ์ด์…˜ ์ •๋ณด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.
      @Documented
      public @interface Transactional {
        // ๋””ํดํŠธ ๊ฐ’์ด ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ๋ชจ๋‘ ์ƒ๋žต ๊ฐ€๋Šฅํ•˜๋‹ค.
      	@AliasFor("transactionManager")	String value() default "";
      	@AliasFor("value") String transactionManager() default "";
      	Propagation propagation() default Propagation.REQUIRED;
      	Isolation isolation() default Isolation.DEFAULT;
      	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
      	boolean readOnly() default false;
      	Class<? extends Throwable>[] rollbackFor() default {};
      	String[] rollbackForClassName() default {};
      	Class<? extends Throwable>[] noRollbackFor() default {};
      	String[] noRollbackForClassName() default {};      
      }
      
    • ํŠธ๋žœ์žญ์…˜ ์†์„ฑ์„ ์ด์šฉํ•˜๋Š” ํฌ์ธํŠธ์ปท
      • ํฌ์ธํŠธ์ปท๊ณผ ํŠธ๋žœ์žญ์…˜ ์†์„ฑ์„ ์• ๋…ธํ…Œ์ด์…˜ ํ•˜๋‚˜๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
      • ๋ฉ”์„œ๋“œ ๋งˆ๋‹ค ๋ถ€์—ฌํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ์ง€์ €๋ถ„ํ•ด ์งˆ ์ˆ˜ ์žˆ๋‹ค.
    • ๋Œ€์ฒด ์ •์ฑ…
      • ๊ทธ๋ž˜์„œ ์Šคํ”„๋ง์€ @Transactional์„ ์ ์šฉํ•  ๋•Œ 4๋‹จ๊ณ„์˜ ๋Œ€์ฒด ์ •์ฑ…์„ ์ด์šฉํ•˜๊ฒŒ ํ•ด์ค€๋‹ค.
      • ํƒ€๊นƒ ๋ฉ”์„œ๋“œ, ํƒ€๊นƒ ํด๋ž˜์Šค, ์„ ์–ธ ๋ฉ”์„œ๋“œ, ์„ ์–ธ ํƒ€์ž…(ํด๋ž˜์Šค, ์ธํ„ฐํŽ˜์ด์Šค) ์ˆœ์„œ์— ๋”ฐ๋ผ @Transactional์ด ์ ์šฉ๋๋Š”์ง€ ํ™•์ธ
      • transactional
      • ํ›„๋ณด ์ˆœ์„œ : [5, 6], [4], [2,3], [1]
    • ํŠธ๋žœ์žญ์…˜ ์• ๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉ์„ ์œ„ํ•œ ์„ค์ •
      • <tx:annotation-driven/>
      • spring boot์—์„œ๋Š” ๋ณธ ์ ์ด ์—†๋Š”๋ฐ? ์ž๋™์„ค์ •์ธ๊ฐ€๋ด„
  2. ํŠธ๋žœ์žญ์…˜ ์• ๋…ธํ…Œ์ด์…˜ ์ ์šฉ
    • UserService ์ธํ„ฐํŽ˜์ด์Šค์— ์• ๋…ธํ…Œ์ด์…˜ ์ ์šฉ
    • UserService ์ธํ„ฐํŽ˜์ด์Šค
      @Transactional
      public interface UserService {
        void add(User user);
        void upgradeLevels();
        void createOrIncreaseRecommend(User user);
        void setMailSender(MailSender mailsender);
        @Transactional(readOnly = true)
        User get(String id);
        @Transactional(readOnly = true)
        List getAll();
        void deleteAll();
        void update(User user);
      }
      
    • ์—ฌ๊ธฐ์„œ ๊ตฌํ˜„์ฒด UserServiceImpl์— @Transactional์„ ๋ถ™์ด๋ฉด? ๋Œ€์ฒด์ •์ฑ…์— ๋”ฐ๋ผ readOnly๋Š” ๋ฌด์‹œ๋œ๋‹ค.

8. ํŠธ๋žœ์žญ์…˜ ์ง€์› ํ…Œ์ŠคํŠธ

  1. ์„ ์–ธ์  ํŠธ๋žœ์žญ์…˜๊ณผ ํŠธ๋žœ์žญ์…˜ ์ „ํŒŒ ์†์„ฑ
    • ์„ ์–ธ์  ํŠธ๋žœ์žญ์…˜ : AOP๋ฅผ ์ด์šฉํ•ด ์ฝ”๋“œ ์™ธ๋ถ€์—์„œ ํŠธ๋žœ์žญ์…˜์˜ ๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ•ด์ฃผ๊ณ  ์†์„ฑ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” ๋ฐฉ๋ฒ•
    • ํ”„๋กœ๊ทธ๋žจ์— ์˜ํ•œ ํŠธ๋žœ์žญ์…˜ : ์œ„์™€ ๋ฐ˜๋Œ€๋กœ ๊ฐœ๋ณ„ ๋ฐ์ดํ„ฐ ๊ธฐ์ˆ ์˜ ํŠธ๋žœ์žญ์…˜ API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
    • ํŠน๋ณ„ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ์„ ์–ธ์  ๋ฐฉ์‹์˜ ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์งํ•˜๋‹ค.
  2. ํŠธ๋žœ์žญ์…ฉ ๋™๊ธฐํ™”์™€ ํ…Œ์ŠคํŠธ
    • ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €์™€ ํŠธ๋žœ์žญ์…˜ ๋™๊ธฐํ™”
    • 3๊ฐœ์˜ ํŠธ๋žœ์žญ์…˜์ด ๋งŒ๋“ค์–ด์ง„ ํ…Œ์ŠคํŠธ์ฝ”๋“œ
      @Test
      public void transactionSync() {
        userService.deleteAll();
        userService.add(userList.get(0));
        userService.add(userList.get(1));
      }
      
    • ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๋ฅผ ์ด์šฉํ•œ ํ…Œ์ŠคํŠธ์šฉ ํŠธ๋žœ์žญ์…˜ ์ œ์–ด
    • 3๊ฐœ์˜ ํŠธ๋žœ์žญ์…˜์„ ํ•ฉ์นœ ํ…Œ์ŠคํŠธ์ฝ”๋“œ
      @Test
      public void transactionSync() {
        // UserService๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์ „์— ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•ด์ฃผ๋ฉด ํŠธ๋žœ์žญ์…˜์ด ์ „ํŒŒ๋˜์–ด ํ†ตํ•ฉ๋œ๋‹ค.
        DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
        // ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €์—๊ฒŒ ํŠธ๋žœ์žญ์…˜์„ ์š”์ฒญํ•œ๋‹ค.
        // ๊ธฐ์กด์— ์‹œ์ž‘๋œ ํŠธ๋žœ์žญ์…˜์ด ์—†์œผ๋ฏ€๋กœ ์ƒˆ๋กœ์šด ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘์‹œํ‚ค๊ณ  ํŠธ๋žœ์žญ์…˜ ์ •๋ณด๋ฅผ ๋Œ๋ ค์ค€๋‹ค.
        // ๋™์‹œ์— ๋งŒ๋“ค์–ด์ง„ ํŠธ๋žœ์žญ์…˜์„ ๋‹ค๋ฅธ ๊ณณ์—์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋™๊ธฐํ™”ํ•œ๋‹ค.    
        TransactionStatus txStatus = transactionManager.getTransaction(txDefinition);
        userService.deleteAll();
        userService.add(userList.get(0));
        userService.add(userList.get(1));
        transactionManager.commit(txStatus);
      }
      
    • ํŠธ๋žœ์žญ์…˜ ๋™๊ธฐํ™” ๊ฒ€์ฆ์šฉ ํ…Œ์ŠคํŠธ
    • read-only ํ…Œ์ŠคํŠธ
      @Test(expected = TransientDataAccessResourceException.class)
      public void transactionSyncReadOnly() {
        DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
        txDefinition.setReadOnly(true);
        TransactionStatus txStatus = transactionManager.getTransaction(txDefinition);
        userService.deleteAll();
      }
      
    • ๋กค๋ฐฑ ํ…Œ์ŠคํŠธ
      @Test
      public void transactionSyncRollBack() {
        userDao.deleteAll();
        assertThat(userDao.getAll().size()).isEqualTo(0);
        DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
        TransactionStatus txStatus = transactionManager.getTransaction(txDefinition);
        userService.add(userList.get(0));
        assertThat(userDao.getAll().size()).isEqualTo(1);
        transactionManager.rollback(txStatus);
        assertThat(userDao.getAll().size()).isEqualTo(0);
      }
      
    • ๋กค๋ฐฑ ํ…Œ์ŠคํŠธ
      • ํ…Œ์ŠคํŠธ ๋‚ด์˜ ๋ชจ๋“  DB์ž‘์—…์„ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ๋™์ž‘ํ•˜๊ฒŒ ํ•˜๊ณ , ํ…Œ์ŠคํŠธ๊ฐ€ ๋๋‚˜๋ฉด ๋ฌด์กฐ๊ฑด ๋กค๋ฐฑํ•˜๋Š” ํ…Œ์ŠคํŠธ
      • ํ…Œ์ŠคํŠธ์˜ DB์กฐ์ž‘์œผ๋กœ ์ธํ•œ ์‚ฌ์ด๋“œ์ดํŽ™ํŠธ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Œ
      • ๊ทผ๋ฐ ๋ฐ์ดํ„ฐ ์กฐ๊ธˆ๋งŒ ๋ณต์žกํ•ด์ ธ๋„ ๋งŒ๋“ค๊ธฐ ๋นก์„ผ๋ฐ
  3. ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ํŠธ๋žœ์žญ์…˜ ์• ๋…ธํ…Œ์ด์…˜
    • @Transactional
      • ํ…Œ์ŠคํŠธ์—๋„ @Transactional์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
      • ์œ„์—์„œ ๋ดค๋˜ read-only ํ…Œ์ŠคํŠธ
        @Test(expected = TransientDataAccessResourceException.class)
        @Transactional(readOnly = true)
        public void transactionSyncReadOnly() {
          userService.deleteAll();
        }
        
    • @Rollback
      • @Transactional์ด ๋‹ฌ๋ ค์žˆ์–ด๋„ @Rollback(false)ํ•ด์ฃผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š” ํ•œ ํŠธ๋žœ์žญ์…˜์€ ์ปค๋ฐ‹๋œ๋‹ค.
    • @TransactionConfiguration
      • @Rollback์€ ๋ฉ”์„œ๋“œ์—๋งŒ ๋ถ™์ผ ์ˆ˜ ์žˆ๋‹ค.
      • @TransactionalConfiguration(defaultRollback = false) ํ•ด์ฃผ๋ฉด ์ฃ„ @Rollback(false) ๋˜๋Š”๊ฒƒ
      • ํŠน์ • ํ…Œ์ŠคํŠธ๋ฉ”์„œ๋“œ๋งŒ ๋กค๋ฐฑ์‹œํ‚ค๊ณ  ์‹ถ์œผ๋ฉด ๊ทธ ๋ฉ”์„œ๋“œ์— @Rollback ๋ถ™์ด๋ฉด ๋จ
      • @TransactionalConfiguration deprecated ๋๋‹ค.
      • ์™œ๋ƒ? ํ…Œ์ŠคํŠธ๋ ˆ๋ฒจ์—์„œ @Transactional์€ ํ•ญ์ƒ rollback=true๋ผ์„œ
      • ํ…Œ์ŠคํŠธํด๋ž˜์Šค์— @Transactional ๋ถ™์ด๊ณ  ํ…Œ์ŠคํŠธ์ฝ”๋“œ ๋Œ๋ฆฌ๋ฉด ์ „์ฒด ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋Š” rollback๋œ๋‹ค.
    • NotTransactional๊ณผ Propagation.NEVER
      • @NotTransactional : @Transactional ์„ค์ •์„ ๋ฌด์‹œํ•˜๊ณ  ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•˜์ง€ ์•Š์€ ์ฑ„๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์‹œ์ž‘ํ•˜๊ฒŒ ํ•จ
      • @Transactional(propagation = Propagation.NEVER) : ํŠธ๋žœ์žญ์…˜์ด ์‹œ์ž‘๋˜์ง€ ์•Š๋Š”๋‹ค.
    • ํšจ๊ณผ์ ์ธ DB ํ…Œ์ŠคํŠธ
      • ํŠธ๋žœ์ ์…˜ ์ œ์–ด ์• ๋…ธํ…Œ์ด์…˜์€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ ์•„์ฃผ ์œ ์šฉํ•˜๋‹ค.
      • DB๊ฐ€ ์‚ฌ์šฉ๋˜๋Š” ํ†ตํ•ฉํ…Œ์ŠคํŠธ๋Š” ๋ณ„๋„์˜ ํด๋ž˜์Šค๋กœ ๋งŒ๋“ค๊ณ  ๋กค๋ฐฑ๋˜๋„๋ก ์„ค์ •ํ•˜๋Š”๊ฒŒ ์ข‹๋‹ค.
    • ํ† ๋น„ ์•„์ €์”จ๋Š” ํŠธ๋žœ์žญ์…˜์„ ์˜ˆ์‹œ๋กœ AOP๋ฅผ ์„ค๋ช…ํ–ˆ๊ณ , ์„ค๋ช…์ด ๋‹ค ๋๋‚˜๋‹ˆ ํŠธ๋žœ์žญ์…˜ ์„ค์ •์„ ์‰ฝ๊ณ  ๊น”๋”ํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค์ฃผ์…จ๋‹ค. ๋ฐ•์ˆ˜์ง์ง

9. ์ •๋ฆฌ

  • ์ง„์งœ ๊ธธ์—ˆ๋‹ค ์ •๋ง
  • 6์žฅ์—์„œ๋Š” ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„์„ค์ • ๊ธฐ๋Šฅ์„ ์„ฑ๊ฒฉ์ด ๋‹ค๋ฅธ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ํด๋ž˜์Šค์—์„œ ๋ถ„๋ฆฌํ•˜๊ณ  ์œ ์—ฐํ•˜๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์ฐพ์•„๋ณด๋ฉด์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์‚ฐ์žฌํ•ด์„œ ๋‚˜ํƒ€๋‚˜๋Š” ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋ชจ๋“ˆํ™”ํ•  ์ˆ˜ ์žˆ๋Š” AOP ๊ธฐ์ˆ ์„ ์•Œ์•„๋ดค๋‹ค.
  • ํŠธ๋žœ์žญ์…˜์ฒ˜๋Ÿผ ํ™˜๊ฒฝ๊ณผ ์™ธ๋ถ€ ๋ฆฌ์†Œ์Šค์— ์˜ํ–ฅ์„ ๋ฐ›๋Š” ์ฝ”๋“œ๋ฅผ ๋ถ„๋ฆฌํ•˜๋ฉด ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—๋งŒ ์ถฉ์‹คํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
  • ๋ชฉ ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•˜๋ฉด ์˜์กด๊ด€๊ณ„ ์†์— ์žˆ๋Š” ๊ฐ์ฒด๋„ ์†์‰ฝ๊ฒŒ ๊ณ ๋ฆฝ๋œ ํ…Œ์ŠคํŠธ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
  • ํฌ์ธํŠธ์ปท์€ AspectJ ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•ด์„œ ์ž‘์„ฑํ•˜๋ฉด ํŽธ๋ฆฌํ•˜๋‹ค.
  • AOP๋Š” OOP๋งŒ์œผ๋กœ๋Š” ๋ชจ๋“ˆํ™”ํ•˜๊ธฐ ํž˜๋“  ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ํšจ๊ณผ์ ์œผ๋กœ ๋ชจ๋“ˆํ™”ํ•˜๋„๋ก ๋„์™€์ฃผ๋Š” ๊ธฐ์ˆ ์ด๋‹ค.
  • ์Šคํ”„๋ง์€ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” AOP ์„ค์ •๊ณผ ํŠธ๋žœ์žญ์…˜ ์†์„ฑ์„ ์ง€์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ „์šฉ ํƒœ๊ทธ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
  • AOP๋ฅผ ์ด์šฉํ•ด ํŠธ๋žœ์žญ์…˜ ์†์„ฑ์„ ์ง€์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•
    1. ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹๊ณผ ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ํŒจํ„ด์„ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
    2. ํƒ€๊นƒ์— ์ง์ ‘ ๋ถ€์—ฌํ•˜๋Š” @Transactional ์• ๋…ธํ…Œ์ด์…˜ ํ™œ์šฉ
  • @Transactional์„ ์ด์šฉํ•œ ํŠธ๋žœ์žญ์…˜ ์†์„ฑ์„ ํ…Œ์ŠคํŠธ์— ์ ์šฉํ•˜๋ฉด ์†์‰ฝ๊ฒŒ DB๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ์˜ ํ…Œ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.