SPRING - AOP

Sungjinยท2022๋…„ 1์›” 30์ผ
1

Spring

๋ชฉ๋ก ๋ณด๊ธฐ
23/23
post-thumbnail

AOP - Aspect Oriented Programming


๐Ÿ” ์œ ์ € ์„œ๋น„์Šค๊ธฐ๋Šฅ์— Transaction ๊ฒฝ๊ณ„ ์„ค์ • ๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ•˜๋ฉด์„œ, ๋‹จ๊ณ„๋ณ„๋กœ AOP์˜ ๋“ฑ์žฅ ๋ฐฐ๊ฒฝ๊ณผ ์žฅ์ ์„ ์•Œ์•„๋ด…์‹œ๋‹ค!

๐Ÿ“Œ ๋จผ์ €, ๋‹ค์Œ์˜ ์ฝ”๋“œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ ์ฐจ์ ์œผ๋กœ ๋ฐœ์ „์‹œ์ผœ ๋‚˜๊ฐ€๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

User.Java(Domain)

@Getter
@NoArgsConstructor(access= AccessLevel.PROTECTED)
public class User {

    private String id;
    private String name;
    private String password;

    private Level level;
    private int login;
    private int recommend;
    private String email;
    private LocalDateTime createdAt;
    private LocalDateTime lastUpgraded;

    public void updateLevel(Level level){
        this.level=level;
    }

    public void updateLogin(int login){
        this.login=login;
    }

    public void updateRecommend(int recommend){
        this.recommend=recommend;
    }

    public void upgradeLevel(){
        Level next=this.level.getNext();
        if(next==null){
            throw new IllegalStateException(this.level+"์€ ํ˜„์žฌ ์—…๊ทธ๋ ˆ์ด๋“œ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.");
        }
        updateLevel(next);

        this.lastUpgraded=LocalDateTime.now();
    }

    @Builder(builderMethodName = "createUser")
    public User(String id, String name, String password,Level level,int login,int recommend, String email
            ,LocalDateTime createdAt,LocalDateTime lastUpgraded){
        this.id=id;
        this.name=name;
        this.password=password;
        this.level=level;
        this.login=login;
        this.recommend=recommend;
        this.email=email;
        this.createdAt=createdAt;
        this.lastUpgraded=lastUpgraded;
    }
}

UserDao.java

public interface UserDao {
    void add(User user);
    Optional<User> get(String id);
    List<User> getAll();
    void deleteAll();
    int getCount();
    void update(User user);
}

UserDaoImpl.java

@RequiredArgsConstructor
public class UserDaoImpl implements UserDao {

    private final JdbcOperations jdbcOperations;

    private RowMapper<User> userMapper=new RowMapper<User>() {
        @Override
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            return User.createUser()
                    .id(rs.getString("id"))
                    .password(rs.getString("password"))
                    .name(rs.getString("name"))
                    .level(Level.valueOf(rs.getInt("level")))
                    .login(rs.getInt("login"))
                    .recommend(rs.getInt("recommend"))
                    .email(rs.getString("email"))
                    .createdAt(rs.getTimestamp("createdAt").toLocalDateTime())
                    .lastUpgraded(rs.getTimestamp("lastUpgraded").toLocalDateTime())
                    .build();
        }
    };

    @Override
    public void add(User user) {
        jdbcOperations.update("insert into users(id,name,password,level,login,recommend,email,createdAt,lastUpgraded) " +
                        "values(?,?,?,?,?,?,?,?,?)",
                user.getId(),user.getName(),user.getPassword()
                ,user.getLevel().getValue(),user.getLogin(),user.getRecommend(), user.getEmail()
                , Timestamp.valueOf(user.getCreatedAt()),Timestamp.valueOf(user.getLastUpgraded()));
    }

    @Override
    public Optional<User> get(String id) {
        return Optional.ofNullable(
                jdbcOperations.queryForObject("select * from users u where u.id=?", userMapper,new Object[]{id}));
    }

    @Override
    public List<User> getAll() {
        return jdbcOperations.query("select * from users u order by id", userMapper);
    }

    @Override
    public void deleteAll() {
        jdbcOperations.update("delete from users");
    }

    @Override
    public int getCount() {
        return jdbcOperations.queryForObject("select count(*) from users",Integer.class);
    }

    @Override
    public void update(User user) {
        jdbcOperations.update("update users set name=?,password=?,level=?,login=?,recommend=?,email=?, createdAt=?," +
                        "lastUpgraded=? where id=?",
                user.getName(), user.getPassword(),user.getLevel().getValue()
                ,user.getLogin(),user.getRecommend(),user.getEmail(),user.getCreatedAt(),user.getLastUpgraded(),user.getId());
    }
}

UserService.java

public interface UserService {
    void add(User user);
    
    void upgradeLevels();

    User get(String id);
    List<User> getAll();
    int getCount();
    
    void deleteAll();
    
    void update(User user);
}

UserServiceImpl.java

package study.aop.service;

import lombok.RequiredArgsConstructor;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import study.aop.domain.Level;
import study.aop.dao.UserDao;
import study.aop.domain.User;

import java.util.List;

@RequiredArgsConstructor
public class UserServiceImpl implements UserService {

    private final UserDao userDao;
    private final PlatformTransactionManager transactionManager;
    private final MailSender mailSender;

    public static final int LOG_COUNT_FOR_SILVER=50;
    public static final int REC_COUNT_FOR_GOLD=30;

    public void upgradeLevels(){
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());

        try{
            upgradeLevelsInternal();
            transactionManager.commit(status);
        }catch(Exception e){
            transactionManager.rollback(status);
            throw e;
        }
    }

    public void add(User user){
        if(user.getLevel()==null)
            user.updateLevel(Level.BASIC);
        userDao.add(user);
    }

    protected void upgradeLevel(User user){
        user.upgradeLevel();
        userDao.update(user);
        sendUpgradeMail(user);
    }

    @Override
    public User get(String id) {
        return userDao.get(id).orElseThrow(()->{
            throw new RuntimeException();
        });
    }

    @Override
    public List<User> getAll() {
        return userDao.getAll();
    }

    @Override
    public int getCount() {
        return userDao.getCount();
    }

    @Override
    public void deleteAll() {
        userDao.deleteAll();
    }

    @Override
    public void update(User user) {
        userDao.update(user);
    }
    

    private void sendUpgradeMail(User user){
        SimpleMailMessage mailMessage=new SimpleMailMessage();
        mailMessage.setTo(user.getEmail());
        mailMessage.setFrom("admin");
        mailMessage.setSubject("Upgrade ์•ˆ๋‚ด");
        mailMessage.setText("์‚ฌ์šฉ์ž๋‹˜์˜ ๋“ฑ๊ธ‰์ด "+user.getLevel()+"๋กœ ์—…๊ทธ๋ ˆ์ด๋“œ ๋ ์—ˆ์Šต๋‹ˆ๋‹ค.");

        mailSender.send(mailMessage);
    }

    private boolean checkUpgrade(User user){
        Level currentLevel=user.getLevel();

        if(currentLevel==Level.BASIC)
            return (user.getLogin()>=LOG_COUNT_FOR_SILVER);
        else if(currentLevel==Level.SILVER)
            return (user.getRecommend()>=REC_COUNT_FOR_GOLD);
        else if(currentLevel==Level.GOLD)
            return false;
        throw new IllegalArgumentException("Unknown Level : "+currentLevel);
    }


}

AppConfig.java

@Configuration
@RequiredArgsConstructor
public class AppConfig {

    private final Environment env;

    @Bean
    public DataSource dataSource(){
        SimpleDriverDataSource dataSource=new SimpleDriverDataSource();
        dataSource.setDriverClass(org.h2.Driver.class);
        dataSource.setUrl(env.getProperty("spring.datasource.url"));
        dataSource.setUsername(env.getProperty("spring.datasource.username"));
        dataSource.setPassword(env.getProperty("spring.datasource.password"));

        return dataSource;
    }

    @Bean
    public PlatformTransactionManager transactionManager(){
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean
    public JdbcOperations jdbcOperations(){
        return new JdbcTemplate(dataSource());
    }

    @Bean
    public UserDao userDao(){
        return new UserDaoImpl(jdbcOperations());
    }

    @Bean
    public MailSender mailSender(){
        return new DummyMailSender();
    }

    @Bean
    public UserService userService(){
        return new UserServiceImpl(userDao(),mailSender());
    }
}

๐Ÿš€ Transaction ์ฝ”๋“œ ๋ถ„๋ฆฌ

๋จผ์ €, UserServiceImpl์˜ ์ฝ”๋“œ์˜ UpgradeLevels ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ดํŽด๋ด…์‹œ๋‹ค.

public void upgradeLevels(){
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());

        try{
            upgradeLevelsInternal();
            transactionManager.commit(status);
        }catch(Exception e){
            transactionManager.rollback(status);
            throw e;
        }
    }

์ง€๊ธˆ ์ด ๋ฉ”์†Œ๋“œ๋Š” Transaction ๊ฒฝ๊ณ„์„ค์ • ์ฝ”๋“œ์™€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ฝ”๋“œ๊ฐ€ ์„œ๋กœ ๊ณต์กดํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ ‡๋‹ค๊ณ  ํ•˜๋”๋ผ๋„ ์ฝ”๋“œ๋ฅผ ์ž˜ ์‚ดํŽด๋ณด์‹œ๋ฉด ์„œ๋กœ๊ฐ„์˜ ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ๊ฑฐ์˜ ์—†์œผ๋ฉฐ, ๋šœ๋ ท์ด ๊ฐ์ž์˜ ๊ธฐ๋Šฅ๋“ค์„ ํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•„๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
1. Transaction ๊ฒฝ๊ณ„์„ค์ • ์ฝ”๋“œ๋Š” ์‹œ์ž‘๊ณผ ์ข…๋ฃŒ๋งŒ์„ ๋‹ด๋‹น.
2. ์Šคํ”„๋ง์ด ์ œ๊ณตํ•˜๋Š” Transaction (์„œ๋น„์Šค ์ถ”์ƒํ™”๊ฐ€ ๋˜์–ด์žˆ์Œ!)๊ณผ ๊ณ„์ธต ๋ถ„๋ฆฌ๊ฐ€ ์ž˜๋˜์–ด์ง„ Repository ์™€ Service๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ฝ”๋“œ๋Š”
์ง์ ‘ DB๋ฅผ ๋‹ค๋ฃจ์ง€ ์•Š์„ ๋ฟ๋”๋Ÿฌ Transaction ๊ฒฝ๊ณ„๊ธฐ๋Šฅ์„ ํ•˜๋Š” ์ฝ”๋“œ์™€ ์ •๋ณด๋ฅผ ์ฃผ๊ณ  ๋ฐ›์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ฒฐ๋ก ์ ์œผ๋กœ, ์ด ๋‘๊ฐ€์ง€ ๊ธฐ๋Šฅ์˜ ์ฝ”๋“œ๋Š” ์„œ๋กœ ์„ฑ๊ฒฉ์ด ๋‹ค๋ฅด๋‹ค๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

๊ทธ๋ ‡๋‹ค๋ฉด, ํ˜„์žฌ๋กœ์„œ๋Š” Refactoring์„ ํ†ตํ•˜์—ฌ ์„œ๋กœ๋ฅผ ๋ถ„๋ฆฌ์‹œ์ผœ ๋†“๋Š” ๊ฒƒ์ด ์ตœ๊ณ ์˜ ๋ฐฉ๋ฒ•์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค! ๐Ÿ‘

โš™๏ธ ๊ธฐ๋Šฅ ๋ถ„๋ฆฌ - Refactoring

๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ฝ”๋“œ์™€ Transaction ๊ฒฝ๊ณ„ ์„ค์ • ์ฝ”๋“œ๋ฅผ ๋ถ„๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ€์žฅ ๋จผ์ € ์ƒ๊ฐํ•ด๋ณผ ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ ๋‘ ๊ฐ€์ง€ ์ž…๋‹ˆ๋‹ค.
1. ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•œ ๋ถ„๋ฆฌ
2. DI๋ฅผ ์ด์šฉํ•œ ๋ถ„๋ฆฌ

๋‹ค์‹œ ํ•œ๋ฒˆ ์ƒ๊ฐํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ๋”์šฑ ๊น”๋”ํ• ๊นŒ?!

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

์ด๋Ÿฐ ๋ฐฉ์‹์˜ ํ•ด๊ฒฐ๋ฒ•์„ ์ƒ๊ฐํ•ด๋ณด๋ฉด, ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•œ ๋ถ„๋ฆฌ๋Š” ์ฒดํƒ๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด, ๋˜ ๊ฐœ๋ฐœํ•œ ๋ฉ”์†Œ๋“œ๊ฐ€ ์›๋ž˜์˜ ํด๋ž˜์Šค์— ์˜จ์ „ํžˆ ๋‚จ์•„์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์ฆ‰, DI๋ฅผ ์ด์šฉํ•œ ๋ถ„๋ฆฌ๋ฅผ ์ƒ๊ฐํ•ด๋ด์•ผ ํ•ฉ๋‹ˆ๋‹ค!

๐Ÿ“Œ DI๋ฅผ ์ด์šฉํ•œ ๋ถ„๋ฆฌ

์–ด๋–ค ์›๋ฆฌ๋ฅผ ํ†ตํ•ด DI๋ฅผ ์ด์šฉํ•œ ๋ถ„๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•œ์ง€ ์‚ดํŽด๋ด…์‹œ๋‹ค.

์ผ๋‹จ์€, UserService ํด๋ž˜์Šค๋Š” UserService์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค๋ผ๋Š” ๊ฒƒ์„ ์ƒ๊ฐํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฆ‰, UserServiceImpl์€ ์–ด๋– ํ•œ ํด๋ผ์ด์–ธํŠธ์™€๋„ ๊ฐ•๋ ฅํ•œ, ์ง์ ‘์ ์ธ
๊ฒฐํ•ฉ์ด ๋˜์–ด์žˆ์ง€ ์•Š๊ณ  ์œ ์—ฐํ•œ ํ™•์žฅ์ด ๊ฐ€๋Šฅํ•œ ์ƒํƒœ์ž…๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ, ์ผ๋ฐ˜์ ์ธ DI๋ฅผ ์“ฐ๋Š” ์ด์œ ๋ฅผ ์ƒ๊ฐํ•ด ๋ณธ๋‹ค๋ฉด, ์ผ๋ฐ˜์ ์œผ๋กœ ๋Ÿฐํƒ€์ž„์‹œ ํ•œ๊ฐ€์ง€ ๊ตฌํ˜„ ํด๋ž˜์Šค๋ฅผ ๊ฐˆ์•„๊ฐ€๋ฉฐ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•˜์—ฌ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๋ณด์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ, ์ผ๋ฐ˜์ ์ด๋ผ๋Š” ๋ง์€ ๊ฒฐ์ฝ” ์ •ํ•ด์ง„ ์ œ์•ฝ์ด ์•„๋‹ˆ๋‹ˆ๊นŒ, ์ผ๋ฐ˜์ ์ธ DI๊ฐ€ ์•„๋‹Œ ํ•œ ๋ฒˆ์— ๋‘ ๊ฐ€์ง€ ๊ตฌํ˜„ํด๋ž˜์Šค๋ฅผ ๋™์‹œ์— ์ด์šฉํ•ด๋ณธ๋‹ค๋ฉด ์–ด๋–จ๊นŒ๋ผ๋Š” ์ƒ๊ฐ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ์กฐ๋ฅผ ์ƒ๊ฐํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

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

์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•์„ ์ด์šฉํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

UserServiceTx.java

@RequiredArgsConstructor
public class UserServiceTx implements UserService{

    private final UserService userService;
    private final PlatformTransactionManager transactionManager;

    @Override
    public void add(User user) {
        userService.add(user);
    }

    @Override
    public void upgradeLevels() {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            userService.upgradeLevels();
            transactionManager.commit(status);
        }catch(RuntimeException e){
            transactionManager.rollback(status);
            throw e;
        }
    }
}

UserServiceImpl.java - UpgradeLevels๋ฉ”์†Œ๋“œ

    public void upgradeLevels(){
        List<User> users=userDao.getAll();
        for(User user:users){
            if(checkUpgrade(user)){
                upgradeLevel(user);
            }
        }
    }

AppConfig.java - DI

@Bean
public UserService userServiceTx(){
        return new UserServiceTx(userService(),transactionManager());
}
    
@Bean
public UserService userService(){
        return new UserServiceImpl(userDao(),mailSender());
}

์ฝ”๋“œ๋ฅผ ์‚ดํŽด ๋ณด์‹œ๋ฉด, ๋จผ์ € ํŠธ๋žœ์žญ์…˜์„ ๋‹ด๋‹นํ•˜๋Š” ์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์‚ฌ์šฉ๋จ์œผ๋กœ ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„ ์„ค์ •์„ ํ•ด์ค€ ๋‹ค์Œ ์‹ค์ œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์€
UserServiceImpl์— ์œ„์ž„๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ตœ์ข…์ ์œผ๋กœ ๋‹ค์Œ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์€ ์˜์กด ๊ด€๊ณ„๊ฐ€ ๊ตฌ์„ฑ๋˜์—ˆ๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ ํด๋ผ์ด์–ธํŠธ๋Š” UserServiceTx ๋นˆ์„ ํ˜ธ์ถœํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„์„ค์ •์ด ๋œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.


๐Ÿš€ Dynamic Proxy & Factory Bean

์ง€๊ธˆ ๊นŒ์ง€ ํ•ด์™”๋˜ ๊ณผ์ •์˜ ํŠน์ง•์„ ์‚ดํŽด๋ด…์‹œ๋‹ค.

ํ˜„์žฌ, UserServiceTx์™€ UserServiceImpl ์ƒํƒœ๋กœ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋ถ€๊ฐ€ ๊ธฐ๋Šฅ์„ ๋‹ด๊ณ ์žˆ๋Š” ํด๋ž˜์Šค์ธ UserServiceTx๋Š” ๋ถ€๊ฐ€๊ธฐ๋Šฅ์™ธ์— ๋‚˜๋จธ์ง€ ํ•ต์‹ฌ ๋กœ์ง์€ ๋ชจ๋‘ UserServiceImpl์— ์œ„์ž„ํ•˜๋Š” ๊ตฌ์กฐ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ UserServiceImpl์€ UserServiceTx์˜ ์กด์žฌ ์ž์ฒด๋ฅผ ๋ชจ๋ฅด๋ฉฐ, ๋ถ€๊ฐ€๊ธฐ๋Šฅ์ด ํ•ต์‹ฌ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ตฌ์กฐ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

๋ฌธ์ œ์ 

ํด๋ผ์ด์–ธํŠธ๊ฐ€ UserServiceImpl๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•ด๋ฒ„๋ฆฌ๋ฉด?? ๋ถ€๊ฐ€๊ธฐ๋Šฅ์ด ์ ์šฉ๋˜์ง€๋ฅผ ๋ชปํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ, ์ด๋Ÿฌํ•œ ์‚ฌํƒœ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•˜์—ฌ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋‹ด์€ ํด๋ž˜์Šค์ธ UserServiceTx๋ฅผ ๋งˆ์น˜ ํ•ต์‹ฌ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ํด๋ž˜์Šค์ฒ˜๋Ÿผ ๊พธ๋ฉฐ ํด๋ผ์ด์–ธํŠธ๊ฐ€
์ด ํด๋ž˜์Šค๋งŒ์„ ์‚ฌ์šฉํ•˜๋„๋ก ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๊ธฐ ์œ„ํ•ด์„œ๋Š” ํด๋ผ์ด์–ธํŠธ๋Š” ์ง์ ‘ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด์„œ๋งŒ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋„๋ก ํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ฆ‰, ์‚ฌ์šฉ์ž๋Š” ์ €ํฌ๊ฐ€ ๋งŒ๋“ค์–ด๋†“์€

์ด ๊ตฌ์กฐ๋ฅผ ๋ชจ๋ฅด๋”๋ผ๋„ ์ด๋Ÿฐ ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ๋” ์œ ๋„ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

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

๋ด์™”๋˜ ๊ฒƒ์ฒ˜๋Ÿผ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•˜๋Š” ์‹ค์ œ ๋Œ€์ƒ์ธ๊ฒƒ ์ฒ˜๋Ÿผ ์œ„์žฅํ•ด์„œ ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์„ ๋ฐ›์•„์ฃผ๋Š” ๊ฒƒ,
์ง€๊ธˆ์˜ UserServiceTx๊ฐ€ ํ•˜๋Š” ์—ญํ• ์„ ๋Œ€๋ฆฌ์ž๋ผ๋Š” ๋œป์œผ๋กœ Proxy๋ผ๊ณ  ๋ถ€๋ฅด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฐ ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•ด ์œ„์ž„๋ฐ›์•„ ํ•ต์‹ฌ๊ธฐ๋Šฅ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์‹ค์ œ ์˜ค๋ธŒ์ ํŠธ๋ฅผ Target์ด๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.

Proxy์˜ ํŠน์ง• & ์‚ฌ์šฉ์ด์œ 
1. Target๊ณผ ๊ฐ™์€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„, ํ”„๋ก์‹œ๋Š” Target์„ ์ œ์–ดํ•  ์œ„์น˜์— ์™€์•ผํ•ฉ.
2. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํƒ€๊นƒ์˜ ์ ‘๊ทผ ๋ฐฉ๋ฒ• ์ œ์–ด.
3. ํƒ€๊นƒ์— ๋ถ€๊ฐ€๊ธฐ๋Šฅ ๋ถ€์—ฌ ๊ฐ€๋Šฅ.

๐Ÿ” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด์ด๋ž€ ์ผ๋ฐ˜์ ์œผ๋กœ ๋Ÿฐํƒ€์ž„์‹œ์— ํƒ€๊นƒ์—๊ฒŒ ๋ถ€๊ฐ€์ ์ธ ๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ•˜๊ธฐ ์œ„ํ•ด ํ”„๋ก์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํŒจํ„ด์„ ๋งํ•ฉ๋‹ˆ๋‹ค.

์ฆ‰, ๋Ÿฐํƒ€์ž„ ์‹œ์ ์— ๋ถ€๊ฐ€์ ์ธ ๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ์ƒ์—์„œ๋Š” ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ํ”„๋ก์‹œ์™€ ํƒ€๊นƒ์ด ์—ฐ๊ฒฐ๋˜์–ด์žˆ๋Š”์ง€ ์•Œ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด์—์„œ๋Š” ๊ฐ™์€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ํƒ€๊ฒŸ๊ณผ ์—ฌ๋Ÿฌ๊ฐœ์˜ ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์˜ˆ๋ฅผ ๋“ค์–ด Transaction๋ฟ๋งŒ์•„๋‹ˆ๋ผ
ํƒ€๊ฒŸ์— ์—ฌ๋Ÿฌ๊ฐ€์ง€์˜ ๊ธฐ๋Šฅ์„ ํ•œ ๋ฒˆ์— ๋ถ€์—ฌ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๋œปํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋ก์‹œ๋กœ์„œ ๋™์ž‘ํ•˜๋Š” ๊ฐ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ๋‹ค์Œ ๋‹จ๊ณ„๊ฐ€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํ”„๋ก์‹œ์ธ์ง€ ์ตœ์ข… ํƒ€๊ฒŸ์ธ์ง€๋ฅผ ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์—
๋‹ค์Œ ์œ„์ž„ ๋Œ€์ƒ์€ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์„ ์–ธํ•˜๋ฉฐ ์™ธ๋ถ€์—์„œ ๋Ÿฐํƒ€์ž„ ์‹œ์— ์ฃผ์ž…๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

์ฆ‰, ์Šคํ”„๋ง์˜ DI๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด์„ ์ ์šฉ์‹œํ‚ค๊ธฐ์— ์•„์ฃผ ํŽธ๋ฆฌํ•˜๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์ง€๊ธˆ ๊ตฌ์„ฑํ•œ UserService์— ํŠธ๋žœ์žญ์…˜ ๊ธฐ๋Šฅ๊ณผ ๋”๋ถˆ์–ด ๋˜ ๋‹ค๋ฅธ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•œ๋‹ค๊ณ  ํ•˜๋ฉด,

@Bean
public UserService userServiceAnother(){
    return new UserServiceAnother(userServiceTx());
        }
        
@Bean
public UserService userServiceTx(){
        return new UserServiceTx(userService(),transactionManager());
        }

@Bean
public UserService userService(){
        return new UserServiceImpl(userDao(),mailSender());
        }

์ด๋Ÿฐ ์‹์œผ๋กœ ํ•„์š”ํ•˜๋ฉด ์–ธ์ œ๋“ ์ง€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๐Ÿ” ํ”„๋ก์‹œ ํŒจํ„ด

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

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

๋งˆ์น˜ JPA์˜ ํ”„๋ก์‹œ ํŒจํ„ด๊ณผ๋„ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

JPA ํ”„๋ก์‹œ ํŒจํ„ด ๋ณด๋Ÿฌ๊ฐ€๊ธฐ (์ฆ‰์‹œ๋กœ๋”ฉ, ์ง€์—ฐ๋กœ๋”ฉ)

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

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

์ด๋Ÿฐ ๋Š๋‚Œ์œผ๋กœ ๋ง์ด์ฃ ..

๐Ÿ” Dynamic Proxy

ํ”„๋ก์‹œ๊ฐ€ ์–ด๋–ค ์ด์œ ๋กœ ๋งŒ๋“ค์–ด ์กŒ๋Š”์ง€ ๋˜ํ•œ ํ”„๋ก์‹œ๊ฐ€ ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ๋งŒ๋“ค์–ด ์ง€๋Š” ์ง€๋ฅผ ์ง€๊ธˆ๊นŒ์ง€ ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค.

๊ทธ ์ด์œ ๋Š”

  • ์ฒซ๋ฒˆ์งธ๋กœ๋Š”, ํ”„๋ก์‹œ๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ  ๋‚œ ๋‹ค์Œ ํƒ€๊นƒ์—๊ฒŒ ์œ„์ž„ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ๋ฒˆ๊ฑฐ๋กญ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.

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

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

์ด๋Ÿฐ ๋ฌธ์ œ์ ์„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š”๊ฒƒ์ด ๋ฐ”๋กœ Dynamic Proxy์ž…๋‹ˆ๋‹ค.

Dynamic Proxy๋ฅผ ๊ตฌ์„ฑํ•˜๊ธฐ ์ „์— ๋จผ์ € ๋ฆฌํ”Œ๋ ‰์…˜์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ด…์‹œ๋‹ค.

๋ฆฌํ”Œ๋ ‰์…˜ API๋ฅผ ํ™œ์šฉํ•ด ๋ฉ”์†Œ๋“œ์— ๋Œ€ํ•œ ์ •์˜๋ฅผ ๋‹ด์€ Method ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ™œ์šฉํ•ด ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ด…์‹œ๋‹ค.

ArrayList์˜ size๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์ถ”์ถœํ•œ ๋’ค invoke๋ฅผ ํ†ตํ•ด ์ถ”์ถœํ•ด๋‚ธ ๋ฉ”์†Œ๋“œ๋ฅผ ์‹คํ–‰์‹œ์ผœ ๋ด…์‹œ๋‹ค.

@Test
    @DisplayName("Reflect - Method Test")
    void ๋ฆฌํ”Œ๋ ‰ํŠธ_๋ฉ”์†Œ๋“œ_์ถ”์ถœ_ํ…Œ์ŠคํŠธ() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method sizeMethod= ArrayList.class.getMethod("size");

        List<Integer> testList=new ArrayList<>();
        testList.add(1);
        testList.add(2);
        testList.add(3);
        
        Assertions.assertThat(testList.size()).isEqualTo(3);
        Assertions.assertThat(testList.size()).isEqualTo(sizeMethod.invoke(testList));
        Assertions.assertThat(sizeMethod.invoke(testList)).isEqualTo(3);
    }

ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

๋ณด์‹œ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์ด Reflect๋ฅผ ํ™œ์šฉํ•ด์„œ ๋ฉ”์†Œ๋“œ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ถ”์ถœํ•ด๋‚ผ ์ˆ˜ ์žˆ์—ˆ๊ณ , ์ด๋ฅผ ์ด์šฉํ•˜์—ฌ ์ง€์ •ํ•œ ์˜ค๋ธŒ์ ํŠธ์— ๋Œ€ํ•˜์—ฌ ๋ฉ”์†Œ๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„
ํ™•์ธํ•˜์˜€์Šต๋‹ˆ๋‹ค.

Dynamic Proxy์˜ ๋™์ž‘ ๋ฐฉ๋ฒ•๋ถ€ํ„ฐ ์‚ดํŽด๋ด…์‹œ๋‹ค.

Dynamic Proxy๋ž€? ๋จผ์ € ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ์— ์˜ํ•ด ๋Ÿฐํƒ€์ž„ ์‹œ ๋‹ค์ด๋‚ด๋ฏนํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ง€๋Š” ํ”„๋ก์‹œ ์ž…๋‹ˆ๋‹ค. ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ์—๊ฒŒ Interface์˜ ์ •๋ณด๋งŒ
๋„˜๊ฒจ์ฃผ๋ฉด ํ”„๋ก์‹œ๋ฅผ ์ ์šฉํ•œ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด ๊ณผ์ •์—์„œ, ์ถ”๊ฐ€์‹œํ‚ค๊ณ ์ž ํ•˜๋Š” ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ Invocation Handler์— ๋„ฃ์–ด์ฃผ๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

InvocationHandler.java

public interface InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

InvocationHancler ์ธํ„ฐํŽ˜์ด์Šค์ž…๋‹ˆ๋‹ค. invoke๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋Š” ์œ„์—์„œ ์ง„ํ–‰ํ•ด๋ณด์•˜๋˜ ๋ฆฌํ”Œ๋ ‰์…˜ API์˜ Method ์ธํ„ฐํŽ˜์ด์Šค์™€ ํƒ€๊นƒ ๋ฉ”์†Œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌ ๋ฐ›์Šต๋‹ˆ๋‹ค.

์ฆ‰, ํด๋ผ๋ฆฌ์–ธํŠธ์˜ ๋ชจ๋“  ์š”์ฒญ ๋ฉ”์†Œ๋“œ๋Š” Dynamic Proxy๋ฅผ ํ†ตํ•˜์—ฌ InvocationHandler์˜ Invoke๋ฉ”์†Œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌ๋˜๋ฉฐ
ํƒ€๊นƒ ๋ฉ”์†Œ๋“œ์— ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ ์šฉ์‹œ์ผœ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํ„ดํ•ด์ค๋‹ˆ๋‹ค.

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

์ด์ œ๋Š”, Transaction ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ Dynamic Proxy๋ฅผ ํ†ตํ•˜์—ฌ ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•ด๋ด…์‹œ๋‹ค.

TransactionHandler.java

@RequiredArgsConstructor
public class TransactionHandler implements InvocationHandler {

    private final Object target;
    private final PlatformTransactionManager transactionManager;
    private final String pattern;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(method.getName().startsWith(pattern))
            return invokeWithTransaction(method,args);
        return method.invoke(target,args);
    }

    private Object invokeWithTransaction(Method method,Object[] args) throws Throwable{
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try{
            Object invoke = method.invoke(target, args);
            transactionManager.commit(status);
            return invoke;
        }catch(InvocationTargetException e){
            transactionManager.rollback(status);
            throw e.getTargetException();
        }
    }
}

์ด ์ฝ”๋“œ์—์„œ๋Š”
1. Target Object
2. Transaction Manager
3. Method Pattern (๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ง€์ •๋œ ๋ฉ”์†Œ๋“œ์—๋งŒ ์ ์šฉ์‹œํ‚ค๊ธฐ ์œ„ํ•ด)

๋“ค์„ DI ์‹œ์ผœ์ฃผ๋Š” ๋ถ€๋ถ„์„ ์œ ์˜ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

Dynamic Proxy - Client์—์„œ ์ง์ ‘ ์ƒ์„ฑ

TransactionHandler txHandler=new TranscationHandler();

UserService userService=(UserService)Proxy.newProxyInstance(
        getClass().getClassLoader(),new Class[]{UserService.class},txHndler
        );

์ด๋ ‡๊ฒŒ Dynamic Proxy๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•ด์ค„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์ง€๊ธˆ๋ถ€ํ„ฐ๋Š”, TransactionHandler์™€ Dynamic Proxy๋ฅผ ์Šคํ”„๋ง DI๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค๋ฉด ๋ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ, Dynamic Proxy๋Š” ๋Ÿฐํƒ€์ž„ ์‹œ์— ๋™์ ์œผ๋กœ ๋งŒ๋“ค์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์— ์ผ๋ฐ˜์ ์ธ ์Šคํ”„๋ง Bean์œผ๋กœ ๋“ฑ๋กํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๐Ÿ” Factory Bean

์Šคํ”„๋ง์€ ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ• ์™ธ์—๋„ ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์ค‘ ํ•˜๋‚˜๊ฐ€ ์ด Factory Bean์ž…๋‹ˆ๋‹ค.

Factory Bean ์€ ์Šคํ”„๋ง์„ ๋Œ€์‹ ํ•ด์„œ ์˜ค๋ธŒ์ ํŠธ์˜ ์ƒ์„ฑ๋กœ์ง์„ ๋‹ด๋‹นํ•˜๋„๋ก ๋งŒ๋“ค์–ด์ง„ ํŠน๋ณ„ํ•œ ๋นˆ์„ ๋งํ•ฉ๋‹ˆ๋‹ค.

public interface FactoryBean<T> {
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

    @Nullable
    T getObject() throws Exception;

    @Nullable
    Class<?> getObjectType();

    default boolean isSingleton() {
        return true;
    }
}

์ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

  • getObject() ๋ฉ”์†Œ๋“œ ๋‚ด๋ถ€์—์„œ Dynamic Proxy๋ฅผ ์ƒ์„ฑํ•œ ํ›„ ๋ฐ˜ํ™˜์‹œ์ผœ์ค๋‹ˆ๋‹ค.

๊ฒฐ๋ก ์ ์œผ๋กœ ์ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค๋ฅผ ์Šคํ”„๋ง์˜ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•ด์ฃผ๋ฉด ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ถ”๊ฐ€๋กœ, ์Šคํ”„๋ง์€ FactoryBean์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค๊ฐ€ ๋นˆ์˜ ํด๋ž˜์Šค๋กœ ์ง€์ •๋˜๋ฉด, ํŒฉํ† ๋ฆฌ ๋นˆ ํด๋ž˜์Šค์˜ getObject()๋ฅผ ํ†ตํ•˜์—ฌ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ ,
์ด๋ฅผ ๋นˆ ์˜ค๋ธŒ์ ํŠธ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋นˆ์˜ ํด๋ž˜์Šค๋กœ ๋“ฑ๋ก๋œ ํŒฉํ† ๋ฆฌ๋นˆ์€ ๋นˆ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ณผ์ •์—์„œ๋งŒ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

FactoryBean ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋งŒ๋“ค์–ด๋‘๋ฉด getObject() ๋ผ๋Š” ๋ฉ”์†Œ๋“œ๊ฐ€ ์ƒ์„ฑํ•ด์ฃผ๋Š” ์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์‹ค์ œ ๋นˆ์˜
์˜ค๋ธŒ์ ํŠธ๋กœ ๋Œ€์ฒด ๋œ๋‹ค๊ณ  ๋ณด์‹œ๋ฉด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์‚ดํŽด๋ด…์‹œ๋‹ค.

TransactionFactoryBean.java

@RequiredArgsConstructor
@Getter
public class TransactionFactoryBean implements FactoryBean<Object> {

    private final Object target;
    private final PlatformTransactionManager transactionManager;
    private final String pattern;
    private final Class<?> interfaces;

    @Override
    public Object getObject() throws Exception {
        return Proxy.newProxyInstance(getClass().getClassLoader(),new Class[]{interfaces},new TransactionHandler(target,
                transactionManager,pattern));
    }

    @Override
    public Class<?> getObjectType() {
        return interfaces;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}

AppConfig.java - ์Šคํ”„๋ง ๋นˆ ๋“ฑ๋ก

    @Bean
    public TransactionFactoryBean userService(){
        return new TransactionFactoryBean(userServiceImpl(),transactionManager()
        ,"upgradeLevels()",UserService.class);
    }

์ง€๊ธˆ ๊นŒ์ง€, Dynamic Proxy์™€ Factory Bean์„ ์ ์šฉํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์žฅ์ ๊ณผ ๋‹จ์  ๋˜ํ•œ ์•Œ์•„๋ด…์‹œ๋‹ค.

์žฅ์ 

  • ์žฌ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
    • Factory Bean์€ ๋‹ค์–‘ํ•œ ํด๋ž˜์Šค์— ์ ์šฉ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ํ•˜๋‚˜ ์ด์ƒ์˜ ๋นˆ์„ ๋“ฑ๋กํ•ด๋„ ์ƒ๊ด€ ์—†์Šต๋‹ˆ๋‹ค.
  • ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ํ”„๋ก์‹œ ํด๋ž˜์Šค๋ฅผ ์ผ์ผ์ด ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์„ ํ•ด๊ฒฐํ•ด์ค๋‹ˆ๋‹ค.
  • ๋ถ€๊ฐ€์ ์ธ ๊ธฐ๋Šฅ์ด ์—ฌ๋Ÿฌ ๋ฉ”์†Œ๋“œ์— ๋ฐ˜๋ณต์ ์œผ๋กœ ๋‚˜ํƒ€๋‚˜๊ฒŒ ๋˜๋Š” ๊ฒƒ์„ ํ•ด๊ฒฐํ•ด์ค๋‹ˆ๋‹ค.

๋‹จ์ 

  • ํ•œ ๋ฒˆ์— ์—ฌ๋Ÿฌ๊ฐœ์˜ ํด๋ž˜์Šค์— ๊ณตํ†ต์ ์ธ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ•˜๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. (Factory Bean์˜ ์„ค์ •์˜ ์ค‘๋ณต์„ ๋ง‰์„ ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์„ ๋œปํ•ฉ๋‹ˆ๋‹ค.)
  • ํ•˜๋‚˜์˜ ํƒ€๊นƒ์— ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ• ์ˆ˜๋ก ์„ค์ • ํŒŒ์ผ์ด ๋ณต์žกํ•ด์ง‘๋‹ˆ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด, Transaction ๊ธฐ๋Šฅ ์™ธ์— ์ ‘๊ทผ ์ œํ•œ ๊ธฐ๋Šฅ๊นŒ์ง€ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ๊ณ  ์ด ๊ธฐ๋Šฅ๋“ค์„ ๊ณตํ†ต์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ํƒ€๊นƒ์ด ์ˆ˜ ๋ฐฑ๊ฐœ๋ผ๋ฉด ๊ทธ ๊ฐฏ์ˆ˜๋งŒํผ ์„ค์ • ํŒŒ์ผ์—์„œ
      ์ถ”๊ฐ€๋กœ ์„ค์ •ํ•ด ์ค˜์•ผ ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
  • TransactionHandler ์˜ค๋ธŒ์ ํŠธ๋Š” FactoryBean์˜ ๊ฐœ์ˆ˜๋งŒํผ ๋งŒ๋“ค์–ด ์ง‘๋‹ˆ๋‹ค. ์œ„์˜ ์ฝ”๋“œ์—์„œ ๋ณด์…จ๋‹ค ์‹œํ”ผ ํƒ€๊ฒŸ์ด ๋‹ฌ๋ผ์งˆ ๋•Œ๋งˆ๋‹ค,
    ๊ณตํ†ต ๊ธฐ๋Šฅ์ž„์—๋„ ๋ถˆ๊ฐ€ํ•˜๊ณ  ์ƒˆ๋กœ TransactionHandler๋ฅผ ๋งŒ๋“ค์–ด ์ค˜์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ๋ถ€ํ„ฐ๋Š”, ์ด ๋‹จ์ ๋“ค์„ ํ•ด๊ฒฐํ•ด๋‚˜๊ฐ€ ๋ด…์‹œ๋‹ค!


๐Ÿš€ Spring Proxy Factory Bean

์Šคํ”„๋ง์€ ์ผ๊ด€๋œ ๋ฐฉ๋ฒ•์œผ๋กœ ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ฃผ๋Š” ์ถ”์ƒ ๋ ˆ์ด์–ด๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์Šคํ”„๋ง์€ ํ”„๋ก์‹œ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ด์ฃผ๋Š” ๊ธฐ์ˆ ์„
์ถ”์ƒํ™”ํ•œ ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ์„ ์ œ๊ณตํ•˜์—ฌ ์ค๋‹ˆ๋‹ค.

์Šคํ”„๋ง์˜ ProxyFactoryBean์€ ํ”„๋ก์‹œ๋ฅผ ์ƒ์„ฑํ•ด์„œ ๋นˆ ์˜ค๋ธŒ์ ํŠธ๋กœ ๋“ฑ๋กํ•˜๊ฒŒ ํ•ด์ฃผ๋Š” ํŒฉํ† ๋ฆฌ ๋นˆ์ด๋ฉฐ,
์ˆœ์ˆ˜ํ•˜๊ฒŒ ํ”„๋ก์‹œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ž‘์—…๋งŒ๋“ค ๋‹ด๋‹นํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋ถ€๊ฐ€๊ธฐ๋Šฅ๊ณผ ๊ฐ™์€ ์ž‘์—…์€ ๋ณ„๋„์˜ ๋นˆ์— ๋‘˜ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ProxyFactoryBean์€ InvocationHandler๊ฐ€ ์•„๋‹Œ MethodInterceptor๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋‘˜์˜ ๊ฐ€์žฅ ํฐ ์ฐจ์ด ์ ์€
InvocationHandler๋Š” target์˜ ์ •๋ณด๋ฅผ ์ง์ ‘ ์•Œ๊ณ  ์žˆ์–ด์•ผ Method๋ฅผ Invokeํ•  ์ˆ˜ ์žˆ์—ˆ๋˜ ๋ฐ˜๋ฉด์—,

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(method.getName().startsWith(pattern))
            return invokeWithTransaction(method,args);
        return method.invoke(target,args);
    }

MethodInterceptor๋Š” target์˜ค๋ธŒ์ ํŠธ์— ๋Œ€ํ•œ ์ •๋ณด๋„ ProxyFactoryBean์—๊ฒŒ ์ œ๊ณต๋ฐ›๊ธฐ ๋•Œ๋ฌธ์—, ํƒ€๊นƒ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ง์ ‘ ๋ชฐ๋ผ๋„ ๋ฉ๋‹ˆ๋‹ค.
๋”” ๋•๋ถ„์— MethodInterceptor๋Š” ํƒ€๊นƒ๊ณผ ์ƒ๊ด€ ์—†์ด ๋…๋ฆฝ์ ์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์‹ฑ๊ธƒํ†ค ๋นˆ์œผ๋กœ๋„ ๋“ฑ๋ก์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์ด์™€ ๊ฐ™์€ ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋ด…์‹œ๋‹ค

TransactionAdvice.java

@RequiredArgsConstructor
public class TransactionAdvice implements MethodInterceptor {
    private final PlatformTransactionManager transactionManager;

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try{
            Object ret = invocation.proceed();
            transactionManager.commit(status);
            return ret;
        }catch(RuntimeException e){
            transactionManager.rollback(status);
            throw e;
        }
    }
}

AppConfig.java

@Configuration
@RequiredArgsConstructor
@EnableTransactionManagement
public class AppConfig {
    private final Environment env;

    //Advice ๋ถ€๋ถ„ ์„ค๋ช…
    @Bean
    public TransactionAdvice transactionAdvice(){
        return new TransactionAdvice(transactionManager());
    }
    
    //Pointcut ๋ถ€๋ถ„ ์„ค๋ช…
    //NameMatchMethodPointcut์€ ์Šคํ”„๋ง ๊ธฐ๋ณธ ์ œ๊ณต
    @Bean
    public NameMatchMethodPointcut transactionPointcut(){
        NameMatchMethodPointcut pointcut=new NameMatchMethodPointcut();
        pointcut.setMappedNames("upgrade*");
        return pointcut;
    }

    //Advisor = Advice + Pointcut
    @Bean
    public DefaultPointcutAdvisor transactionAdvisor(){
        return new DefaultPointcutAdvisor(transactionPointcut(),transactionAdvice());
    }
    
    @Bean
    public ProxyFactoryBean userService() {
        ProxyFactoryBean factoryBean = new ProxyFactoryBean();
        factoryBean.setTarget(userServiceImpl());
        factoryBean.setInterceptorNames("transactionAdvisor");
        return factoryBean;
    }
    
    ...
}

์œ„์˜ ์ฝ”๋“œ๋ฅผ ๋ณด์‹œ๋‹ค ์‹œํ”ผ, ํƒ€๊นƒ์—๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ง์ ‘์ ์œผ๋กœ ์•Œ๊ณ  ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. MethodInvocation์ด๋ผ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ํƒ€๊นƒ์— ๋Œ€ํ•œ
์ •๋ณด์™€ ๋ฉ”์†Œ๋“œ์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ํ•จ๊ป˜ ๋„˜์–ด์˜จ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๐Ÿ” Advice ์–ด๋“œ๋ฐ”์ด์Šค

Target์ด ํ•„์š” ์—†๋Š” ์ˆœ์ˆ˜ํ•œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋œปํ•ฉ๋‹ˆ๋‹ค.

MethodInvocation์€ ๋ฉ”์†Œ๋“œ ์ •๋ณด์™€ ํƒ€๊นƒ ์˜ค๋ธŒ์ ํŠธ๊ฐ€ ๋‹ด๊ฒจ์žˆ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ์ž…๋‹ˆ๋‹ค.
MethodInvocation์€ ํƒ€๊นƒ ์˜ค๋ธŒ์ ํŠธ์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— MethodInterceptor๋Š” ๋ถ€๊ฐ€๊ธฐ๋Šฅ์—๋งŒ
์ง‘์ค‘์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

MethodInvocation์€ proceed() ๋ฉ”์†Œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ํƒ€๊ฒŸ ์˜ค๋ธŒ์ ํŠธ์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ๋‚ด๋ถ€์ ์œผ๋กœ ์‹คํ–‰ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ฆ‰, MethodInvocation์„ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค๋ฅผ ํด๋ž˜์Šค๊ฐ„ ๊ณต์œ  ๊ฐ€๋Šฅํ•˜๊ฒŒ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ทธ๋ƒฅ JDK์—์„œ์˜ ProxyFactoryBean์˜ ๋‹จ์ ์ด์—ˆ๋˜ TransactionHandler ์˜ค๋ธŒ์ ํŠธ๋Š” FactoryBean์˜ ๊ฐœ์ˆ˜๋งŒํผ ๋งŒ๋“ค์–ด ์ง‘๋‹ˆ๋‹ค. ์œ„์˜ ์ฝ”๋“œ์—์„œ ๋ณด์…จ๋‹ค ์‹œํ”ผ ํƒ€๊ฒŸ์ด ๋‹ฌ๋ผ์งˆ ๋•Œ๋งˆ๋‹ค,
๊ณตํ†ต ๊ธฐ๋Šฅ์ž„์—๋„ ๋ถˆ๊ฐ€ํ•˜๊ณ  ์ƒˆ๋กœ TransactionHandler๋ฅผ ๋งŒ๋“ค์–ด ์ค˜์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.
์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ, MethodInterceptor๋ฅผ ๊ตฌํ˜„ํ•œ TransactionAdvice์˜ ์ด๋ฆ„์—์„œ ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด

ํƒ€๊ฒŸ ์˜ค๋ธŒ์ ํŠธ์— ์ ์šฉํ•˜๋Š” ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋‹ด์€ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์Šคํ”„๋ง์—์„œ๋Š” ์–ด๋“œ๋ฐ”์ด์Šค(Advice)๋ผ๊ณ  ๋ถ€๋ฅด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ ๋‹ค๋ฅธ ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
TransactionFactoryBean์„ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ๋Š” Dynamic Proxy๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ ์ธํ„ฐํŽ˜์ด์Šค ํƒ€์ž…์„ ์ œ๊ณต๋ฐ›์•„์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

//TransactionFactoryBean
@RequiredArgsConstructor
@Getter
public class TransactionFactoryBean implements FactoryBean<Object> {

    private final Object target;
    private final PlatformTransactionManager transactionManager;
    private final String pattern;
    private final Class<?> interfaces;   //์ด ๋ถ€๋ถ„

    ...
}

ํ•˜์ง€๋งŒ, ์šฐ๋ฆฌ๊ฐ€ ๊ตฌํ˜„ํ•œ Advice์—์„œ๋Š” ๋”ฐ๋กœ ์ธํ„ฐํŽ˜์ด์Šค์˜ ์ •๋ณด๋ฅผ ์ œ๊ณต๋ฐ›์ง€ ์•Š์•„๋„ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š”,
์ธํ„ฐํŽ˜์ด์Šค์˜ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์•„๋„ ProxyFactoryBean์—๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ž๋™ ๊ฒ€์ถœํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ํƒ€๊ฒŸ ์˜ค๋ธŒ์ ํŠธ๊ฐ€
๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋Š” ์ธํ„ฐํŽ˜์ด์Šค ์ •๋ณด๋ฅผ ์•Œ์•„๋‚ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ Advice์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค. Advice๋Š” ํƒ€๊ฒŸ ์˜ค๋ธŒ์ ํŠธ์— ์ˆœ์ˆ˜ํ•œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋‹ด์€ ์˜ค๋ธŒ์ ํŠธ๋ผ๊ณ  ์•„์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๐Ÿ” Pointcut ํฌ์ธํŠธ์ปท

๋ถ€๊ฐ€๊ธฐ๋Šฅ ์ ์šฉ๋Œ€์ƒ ๋ฉ”์†Œ๋“œ ์„ ์ • ๋ฐฉ๋ฒ•์„ ๋œปํ•ฉ๋‹ˆ๋‹ค.

InvocationHandler๋ฅผ ๊ตฌํ˜„ํ•œTransactionHandler์—์„œ๋Š” String ๊ฐ’์œผ๋กœ Pattern์„ ์ฃผ์ž… ๋ฐ›์•„ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์ด ์ ์šฉ๋  ๋Œ€์ƒ ๋ฉ”์†Œ๋“œ๋ฅผ ์„ ์ • ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด MethodInterceptor์—์„œ๋„ ๋˜‘๊ฐ™์ด pattern์„ ์ฃผ์ž…๋ฐ›์•„ ๋‚ด๋ถ€ ๋กœ์ง์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋ฉด ๋ ๊นŒ์š”?? ์•„๋‹™๋‹ˆ๋‹ค!!

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

๋” ์ž์„ธํžˆ ๋ณด์ž๋ฉด, InvocationHandler๋ฐฉ์‹์˜ ๋ฌธ์ œ์ ์ด์—ˆ๋˜ InvocationHandler๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค๊ฐ€ FactoryBean์„ ๋งŒ๋“ค ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์ƒ์„ฑ
๋œ๋‹ค๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” ํƒ€๊ฒŸ๋งˆ๋‹ค ๋ฉ”์†Œ๋“œ ์„ ์ • ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‚˜ ํƒ€๊ฒŸ ์ž์ฒด๊ฐ€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋–ค ํƒ€๊ฒŸ์ด๋‚˜, ํด๋ž˜์Šค์— ์ข…์†๋˜์ง€ ์•Š๊ธฐ ์œ„ํ•ด์„œ ์ž…๋‹ˆ๋‹ค.

์ด ๋ฌธ์ œ๋ฅผ ๊ธฐ๊ป ํ›Œ๋ฅญํžˆ ํ•ด๊ฒฐํ•ด ๋†จ๋Š”๋ฐ Pattern์„ ์ฃผ์ž… ๋ฐ›์•„ ํ™œ์šฉํ•œ๋‹ค๋ฉด ๋˜๋‹ค์‹œ ์–ด๋–ค ๋ฉ”์†Œ๋“œ๋‚˜ ํด๋ž˜์Šค์—๋งŒ ์ข…์†๋  ์ˆ˜ ๋ฐ–์— ์—†๋‹ค๋Š” ๊ฒƒ์„
์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฐ ๋ฌธ์ œ์ ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ, ์Šคํ”„๋ง์€ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ์˜ค๋ธŒ์ ํŠธ์ธ Advice์™€ ๋ฉ”์†Œ๋“œ ์„ ์ • ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์˜ค๋ธŒ์ ํŠธ์ธ Pointcut์„ ๋”ฐ๋กœ ๋‚˜๋ˆ„์—ˆ์Šต๋‹ˆ๋‹ค.
Advice์™€ Pointcut์€ ๋ชจ๋‘ ์ฃผ์ž…์„ ๋ฐ›์•„ ์‚ฌ์šฉํ•˜๋ฉฐ, ๋‘ ๊ฐ€์ง€ ๋ชจ๋‘ ์—ฌ๋Ÿฌ ํ”„๋ก์‹œ์—์„œ ๊ณต์œ ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋„๋ก ๋งŒ๋“ค์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์— ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ, ํ”„๋ก์‹œ๋Š” ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ์š”์ฒญ์„ ๋ฐ›์œผ๋ฉด ๋จผ์ € Pointcut์—๊ฒŒ ์ ์šฉ ๊ฐ€๋Šฅํ•œ ๋ฉ”์†Œ๋“œ์ธ์ง€ ํ™•์ธ์„ ํ•œ ๋’ค, Advice๋ฅผ ํ˜ธ์ถœํ•ด ์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๊ฒฐ๊ณผ์ ์œผ๋กœ, Advice์™€ Pointcut์˜ ๋„์ž…์œผ๋กœ ์ธํ•ด ์—ฌ๋Ÿฌ ํ”„๋ก์‹œ๊ฐ€ ๊ณต์œ ํ•˜๋ฉฐ ์œ ์—ฐํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๊ณ , ๊ตฌ์ฒด์ ์ธ ๋ถ€๊ฐ€๊ธฐ๋Šฅ ๋ฐฉ์น™์ด๋‚˜ ๋ฉ”์†Œ๋“œ ์„ ์ • ์•Œ๊ณ ๋ฅด์ง์ด ๋ฐ”๋€Œ๊ฒŒ ๋˜๋ฉด
Advice๋‚˜ Pointcut๋งŒ ๋ฐ”๊ฟ”์ฃผ๋ฉด ํ•ด๊ฒฐ๋˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

OCP : Open Closed Priciple์„ ์ž˜ ์ง€์ผฐ๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

OCP ๋” ์ž์„ธํžˆ ๋ณด๊ธฐ

๐Ÿ‘ ์ถ”๊ฐ€๋กœ, Advisor๋ž€?

Advisor๋ž€ Advice์™€ Pointcut์„ ๋ฌถ๋Š”๋‹ค๊ณ  ๋ณด์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๋ฌถ๋Š” ์ด์œ ๋Š”, ProxyFactoryBean์— ์—ฌ๋Ÿฌ๊ฐ€์ง€ Advice์™€ Pointcut์ด ์ถ”๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ, ๊ฐ๊ฐ์˜ Advice๋งˆ๋‹ค ๋ฉ”์†Œ๋“œ๋ฅผ ์„ ์ •ํ•˜๋Š” ๋ฐฉ์‹์ด ๋‹ฌ๋ผ์งˆ ์ˆ˜๋„ ์žˆ์œผ๋‹ˆ ์–ด๋–ค Pointcut์„ ์ ์šฉํ• ์ง€ ์• ๋งคํ•ด์งˆ ์ˆ˜ ์žˆ์๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— Advice์™€ Pointcut์„ ํ•˜๋‚˜๋กœ
๋ฌถ์–ด์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.


๐Ÿš€ ์Šคํ”„๋ง AOP

์ง€๊ธˆ๊นŒ์ง€ ํ•ด์™”๋˜ ๋ฐœ์ „ ๊ธฐ์ˆ ์„ ๋‹ค์‹œ ํ•œ๋ฒˆ ์‚ดํŽด ๋ด…์‹œ๋‹ค.

  1. Service ๋กœ์ง์—์„œ Transaction๋ถ€๊ฐ€๊ธฐ๋Šฅ์˜ ๋ถ„๋ฆฌ๋ฅผ ์œ„ํ•ด DynamicProxy์™€ FactoryBean์„ ๋„์ž…ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
    • ๋ฌธ์ œ์ 
      1. ํ•œ ๋ฒˆ์— ์—ฌ๋Ÿฌ๊ฐœ์˜ ํด๋ž˜์Šค์— ๊ณตํ†ต์ ์ธ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ•˜๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. (Factory Bean์˜ ์„ค์ •์˜ ์ค‘๋ณต์„ ๋ง‰์„ ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์„ ๋œปํ•ฉ๋‹ˆ๋‹ค.)
      2. ํ•˜๋‚˜์˜ ํƒ€๊นƒ์— ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ• ์ˆ˜๋ก ์„ค์ • ํŒŒ์ผ์ด ๋ณต์žกํ•ด์ง‘๋‹ˆ๋‹ค.
        • ์˜ˆ๋ฅผ ๋“ค์–ด, Transaction ๊ธฐ๋Šฅ ์™ธ์— ์ ‘๊ทผ ์ œํ•œ ๊ธฐ๋Šฅ๊นŒ์ง€ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ๊ณ  ์ด ๊ธฐ๋Šฅ๋“ค์„ ๊ณตํ†ต์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ํƒ€๊นƒ์ด ์ˆ˜ ๋ฐฑ๊ฐœ๋ผ๋ฉด ๊ทธ ๊ฐฏ์ˆ˜๋งŒํผ ์„ค์ • ํŒŒ์ผ์—์„œ
          ์ถ”๊ฐ€๋กœ ์„ค์ •ํ•ด ์ค˜์•ผ ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
      3. TransactionHandler ์˜ค๋ธŒ์ ํŠธ๋Š” FactoryBean์˜ ๊ฐœ์ˆ˜๋งŒํผ ๋งŒ๋“ค์–ด ์ง‘๋‹ˆ๋‹ค. ์œ„์˜ ์ฝ”๋“œ์—์„œ ๋ณด์…จ๋‹ค ์‹œํ”ผ ํƒ€๊ฒŸ์ด ๋‹ฌ๋ผ์งˆ ๋•Œ๋งˆ๋‹ค,
        ๊ณตํ†ต ๊ธฐ๋Šฅ์ž„์—๋„ ๋ถˆ๊ฐ€ํ•˜๊ณ  ์ƒˆ๋กœ TransactionHandler๋ฅผ ๋งŒ๋“ค์–ด ์ค˜์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.
  2. ๋ฌธ์ œ์ ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด SpringProxyFactoryBean์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
    • Advice์˜ ๋„์ž…
    • Pointcut์˜ ๋„์ž…
    • Advisor์˜ ๋„์ž…

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

But, ํ•œ๊ฐ€์ง€ ๋ฌธ์ œ์ ์ด ๋˜ ๋‚จ์•˜์Šต๋‹ˆ๋‹ค.

๊ทธ๊ฒƒ์€ ๋ฐ”๋กœ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์˜ ์ ์šฉ์ด ํ•„์š”ํ•œ ํƒ€๊ฒŸ ์˜ค๋ธŒ์ ํŠธ๋งˆ๋‹ค ๊ฑฐ์˜ ๋น„์Šทํ•œ ๋‚ด์šฉ์˜ ProxyFactoryBean ๋นˆ ์„ค์ •์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ๋Š”
๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.

@Configuration
@RequiredArgsConstructor
@EnableTransactionManagement
public class AppConfig {
    private final Environment env;
    
    //์ด ๋ถ€๋ถ„์ด ๊ณ„์†ํ•ด์„œ ๋Š˜์–ด๋‚˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
    @Bean
    public ProxyFactoryBean userService() {
        ProxyFactoryBean factoryBean = new ProxyFactoryBean();
        factoryBean.setTarget(userServiceImpl());
        factoryBean.setInterceptorNames("transactionAdvisor");
        return factoryBean;
    }
    
    ...
}

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

์ฆ‰, ํ•œ ๋ฒˆ์— ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋นˆ์— ํ”„๋ก์‹œ๋ฅผ ์ ์šฉํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค!

โš™๏ธ ๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ

๋จผ์ €, ๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ๋ž€ ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ ์Šคํ”„๋ง ๋นˆ ์˜ค๋ธŒ์ ํŠธ๋กœ ๋งŒ๋“ค์–ด์ง€๊ณ  ๋‚œ ํ›„์—, ๋นˆ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋‹ค์‹œ ๊ฐ€๊ณตํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์‚ดํŽด๋ณผ ๊ฒƒ์€ ๋นˆ์ด ์ƒ์„ฑ๋œ ์ดํ›„์— Advisor๋ฅผ ์ด์šฉํ•œ ์ž๋™ ํ”„๋ก์‹œ ์ƒ์„ฑ๊ธฐ์ธ DefaultAdvisorAutoProxyCreator๋ฅผ ์‚ดํŽด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

DefaultAdvisorAutoProxyCreator๊ฐ€ ๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ๋กœ ๋“ฑ๋ก๋˜์–ด ์žˆ์œผ๋ฉด ์Šคํ”„๋ง์€ ๋นˆ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ ๋งˆ๋‹ค ํ›„์ฒ˜๋ฆฌ๊ธฐ์—๊ฒŒ ๋นˆ์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค.
๊ทธ ์ดํ›„, ๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ๋Š” ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋œ ๋ชจ๋“  Advisor๋‚ด์˜ ํฌ์ธํŠธ์ปท์„ ์ด์šฉํ•ด ์ „๋‹ฌ๋ฐ›์€ ๋นˆ์ด ํ”„๋ก์‹œ ์ ์šฉ ๋Œ€์ƒ์ธ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋ก์‹œ ์ ์šฉ ๋Œ€์ƒ์ด๋ผ๋ฉด, ๋‚ด์žฅ๋œ ํ”„๋ก์‹œ ์ƒ์„ฑ๊ธฐ์—๊ฒŒ ํ˜„์žฌ ๋นˆ์— ๋Œ€ํ•œ ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค๊ฒŒ ํ•˜๊ณ , ๋งŒ๋“ค์–ด์ง„ ํ”„๋ก์‹œ์— Advisor๋ฅผ ์—ฐ๊ฒฐํ•ด์ค๋‹ˆ๋‹ค.

์ด์ œ, ํ”„๋ก์‹œ๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด ์›๋ž˜ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์ „๋‹ฌํ•ด์ค€ ๋นˆ ์˜ค๋ธŒ์ ํŠธ ๋Œ€์‹  ํ”„๋ก์‹œ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ปจํ…Œ์ด๋„ˆ์—๊ฒŒ ๋Œ๋ ค์ฃผ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๊ฒฐ๋ก ์ ์œผ๋กœ ์ปจํ…Œ์ด๋„ˆ๋Š” ํ”„๋ก์‹œ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๊ณ  ์‚ฌ์šฉํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

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

Pointcut์˜ ํ™•์žฅ

์Šคํ”„๋ง ProxyFactoryBean์„ ํ•  ๋•Œ Pointcut์—์„œ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์–ด๋–ป๊ฒŒ ํŒ์ •ํ• ์ง€๋งŒ ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทผ๋ฐ ๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ์—์„œ ์„ค๋ช…ํ•œ ๋ฐ”๋กœ๋Š”
Pointcut์œผ๋กœ ์–ด๋–ค ๋นˆ์ด ์„ ์ • ๋Œ€์ƒ์ด ๋  ๊ฒƒ์ธ์ง€ ๊ตฌ๋ณ„ํ•ด์•ผํ•œ๋‹ค๊ณ  ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์–ด๋–ป๊ฒŒ ๋œ ๊ฒƒ์ผ ๊นŒ์š”??

Pointcut์˜ ๊ธฐ๋Šฅ์œผ๋กœ๋Š” ์›๋ž˜ ๋ฉ”์†Œ๋“œ ์„ ์ • ๊ธฐ๋Šฅ๋งŒ ์žˆ๋Š” ๊ฒƒ์ด ์•„๋‹Œ Class Filter๋˜ํ•œ ๋ฉ”์†Œ๋“œ๋กœ ๊ฐ–๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

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

์•ž์„œ ์•Œ์•„๋ณด์•˜๋˜ ๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ์ธ DefaultAdvisorAutoCreator์—์„œ๋Š” ํด๋ž˜์Šค์™€ ๋ฉ”์†Œ๋“œ ์„ ์ •์ด ๋ชจ๋‘ ๊ฐ€๋Šฅํ•œ Pointcut์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

NameMatchMethodPointcut
์œ„์—์„œ ๋“ฑ๋กํ–ˆ์—ˆ๋˜ Pointcut์„ ์‚ดํŽด๋ด…์‹œ๋‹ค.

    @Bean
    public NameMatchMethodPointcut transactionPointcut(){
        NameMatchMethodPointcut pointcut=new NameMatchMethodPointcut();
        pointcut.setMappedNames("upgrade*");
        return pointcut;
    }

์œ„์™€ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋“ฑ๋ก์„ ํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์Šคํ”„๋ง์ด ๊ธฐ๋ณธ ์ œ๊ณตํ•˜๋Š” NameMethodPointcut์€
๋ฉ”์†Œ๋“œ ์„ ์ • ๊ธฐ๋Šฅ๋งŒ์„ ๊ฐ–๊ณ ์žˆ์„ ๋ฟ, ํด๋ž˜์Šค ํ•„ํ„ฐ์˜ ๊ธฐ๋Šฅ์€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ, ์ด ํด๋ž˜์Šค๋ฅผ ํ™•์žฅํ•˜์—ฌ ํด๋ž˜์Šค ํ•„ํ„ฐ์˜ ๊ธฐ๋Šฅ์œผ๋กœ์„œ๋„ ์ž‘๋™ํ•˜๋„๋ก ๋งŒ๋“ค์–ด ๋ด…์‹œ๋‹ค.

NameMathClassMethodPointcut.java

public class NameMatchClassMethodPointcut extends NameMatchMethodPointcut {

    public void setMappedClassName(String mappedClassName){
        this.setClassFilter(new SimpleFilter(mappedClassName));
    }

    @RequiredArgsConstructor
    static class SimpleFilter implements ClassFilter{
        private final String mappedName;

        @Override
        public boolean matches(Class<?> clazz) {
            return PatternMatchUtils.simpleMatch(mappedName, clazz.getSimpleName());
        }
    }
}
public void setMappedClassName(String mappedClassName){
        this.setClassFilter(new SimpleFilter(mappedClassName));
    }

์—ฌ๊ธฐ์„œ setClassFilter๊ฐ€ ์–ด๋–ป๊ฒŒ ๋‚˜์™”๋Š”์ง€ ์˜์•„ํ•˜์‹ค์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์™œ๋ƒ๋ฉด, NameMatchMethodPointcut๋˜ํ•œ Pointcut์ด๋ผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ implementsํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

public interface Pointcut {
    Pointcut TRUE = TruePointcut.INSTANCE;

    ClassFilter getClassFilter();

    MethodMatcher getMethodMatcher();
}
public abstract class StaticMethodMatcherPointcut extends StaticMethodMatcher implements Pointcut {
    private ClassFilter classFilter;

    public StaticMethodMatcherPointcut() {
        this.classFilter = ClassFilter.TRUE;
    }

    public void setClassFilter(ClassFilter classFilter) {
        this.classFilter = classFilter;
    }

    public ClassFilter getClassFilter() {
        return this.classFilter;
    }

    public final MethodMatcher getMethodMatcher() {
        return this;
    }
}
public class NameMatchMethodPointcut extends StaticMethodMatcherPointcut implements Serializable {
    private List<String> mappedNames = new ArrayList();

    public NameMatchMethodPointcut() {
    }

    public void setMappedName(String mappedName) {
        this.setMappedNames(mappedName);
    }

    public void setMappedNames(String... mappedNames) {
        this.mappedNames = new ArrayList(Arrays.asList(mappedNames));
    }

    public NameMatchMethodPointcut addMethodName(String name) {
        this.mappedNames.add(name);
        return this;
    }

    public boolean matches(Method method, Class<?> targetClass) {
        Iterator var3 = this.mappedNames.iterator();

        String mappedName;
        do {
            if (!var3.hasNext()) {
                return false;
            }

            mappedName = (String)var3.next();
        } while(!mappedName.equals(method.getName()) && !this.isMatch(method.getName(), mappedName));

        return true;
    }

    protected boolean isMatch(String methodName, String mappedName) {
        return PatternMatchUtils.simpleMatch(mappedName, methodName);
    }

    public boolean equals(@Nullable Object other) {
        return this == other || other instanceof NameMatchMethodPointcut && this.mappedNames.equals(((NameMatchMethodPointcut)other).mappedNames);
    }

    public int hashCode() {
        return this.mappedNames.hashCode();
    }

    public String toString() {
        return this.getClass().getName() + ": " + this.mappedNames;
    }
}

์ด๋Ÿฐ ์‹์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋กœ์จ, ํด๋ž˜์Šค ํ•„ํ„ฐ๊ธฐ๋Šฅ ๋˜ํ•œ ๊ฐ€์ง„ Pointcut์„ ์ž‘์„ฑ ์™„๋ฃŒํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์ด์ œ, Advisor๋ฅผ ์ด์šฉํ•˜๋Š” ์ž๋™ ํ”„๋ก์‹œ ์ƒ์„ฑ๊ธฐ๊ฐ€ ์–ด๋–ค ์ˆœ์„œ๋กœ ์ž‘๋™์„ ํ•˜๋Š”์ง€ ์•Œ์•„๋ณด๊ณ  ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ด…์‹œ๋‹ค.

  1. DefaultAdvisorAutoProxyCreator๋Š” ๋“ฑ๋ก๋œ ๋นˆ ์ค‘์—์„œ Advisor์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฒƒ์„ ๋ชจ๋‘ ์ฐพ์Šต๋‹ˆ๋‹ค.
  2. Advisor์˜ Pointcut์„ ์ ์šฉํ•ด๋ณด๋ฉด์„œ ๋ชจ๋“  ๋นˆ์— ๋Œ€ํ•˜์—ฌ ํ”„๋ก์‹œ ์ ์šฉ ๋Œ€์ƒ์„ ์„ ์ •ํ•ฉ๋‹ˆ๋‹ค.
  3. ๋นˆ์ด ํ”„๋ก์‹œ ์ ์šฉ ๋Œ€์ƒ์ด๋ผ๋ฉด ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค์–ด ์›๋ž˜ ๋นˆ ์˜ค๋ธŒ์ ํŠธ์™€ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.
  4. ์ด์ œ ์›๋ž˜ ๋นˆ์€ ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•ด ์ ‘๊ทผ๊ฐ€๋Šฅํ•˜๋„๋ก ์„ค์ •์ด ์™„๋ฃŒ ๋ฉ๋‹ˆ๋‹ค.

๐Ÿ‘ ์ฐธ๊ณ ๋กœ! DefaultAdvisorAutoProxyCreator๋Š” ๋นˆ์œผ๋กœ๋งŒ ๋“ฑ๋กํ•ด ๋‘์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

AppConfig.java

@Configuration
@RequiredArgsConstructor
@EnableTransactionManagement
public class AppConfig {

    private final Environment env;

    @Bean
    public DataSource dataSource(){
        SimpleDriverDataSource dataSource=new SimpleDriverDataSource();
        dataSource.setDriverClass(org.h2.Driver.class);
        dataSource.setUrl(env.getProperty("spring.datasource.url"));
        dataSource.setUsername(env.getProperty("spring.datasource.username"));
        dataSource.setPassword(env.getProperty("spring.datasource.password"));

        return dataSource;
    }

    @Bean
    public PlatformTransactionManager transactionManager(){
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean
    public JdbcOperations jdbcOperations(){
        return new JdbcTemplate(dataSource());
    }

    @Bean
    public UserDao userDao(){
        return new UserDaoImpl(jdbcOperations());
    }

    @Bean
    public TransactionAdvice transactionAdvice(){
        return new TransactionAdvice(transactionManager());
    }

    @Bean
    public NameMatchClassMethodPointcut transactionPointcut(){
        NameMatchClassMethodPointcut pointcut=new NameMatchClassMethodPointcut();
        pointcut.setMappedClassName("*ServiceImpl");
        pointcut.setMappedNames("upgrade*");
        return pointcut;
    }

    @Bean
    public DefaultPointcutAdvisor transactionAdvisor(){
        return new DefaultPointcutAdvisor(transactionPointcut(),transactionAdvice());
    }
    
    //๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ ๋“ฑ๋ก
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        return new DefaultAdvisorAutoProxyCreator();
    }

    @Bean
    public UserService userService(){
        return new UserServiceImpl(userDao(),mailSender());
    }


}

์ฝ”๋“œ ์ž‘์„ฑ๋˜ํ•œ ์™„๋ฃŒํ–ˆ์œผ๋ฉฐ, ๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ํ†ตํ•œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ ๋ถ€์—ฌ ๋˜ํ•œ ์™„๋ฒฝํžˆ ์ˆ˜ํ–‰ํ•˜์˜€์Šต๋‹ˆ๋‹ค.


๐Ÿš€ Pointcut ํ‘œํ˜„์‹

์ง€๊ธˆ๊นŒ์ง€ ๋ฐœ์ „ํ•ด์˜จ ๊ณผ์ •์„ ์‚ดํŽด๋ณด๋ฉด, ์ผ์ผ์ด ํด๋ž˜์Šค ํ•„ํ„ฐ์™€ ๋ฉ”์†Œ๋“œ ๋งค์ฒ˜๋ฅผ ๊ตฌํ˜„ํ•˜๊ฑฐ๋‚˜ ๊ธฐ๋ณธ์ ์œผ๋กœ ์Šคํ”„๋ง์ด ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด์™”์Šต๋‹ˆ๋‹ค.

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

์ด๋ ‡๊ฒŒ ๊ณ ์•ˆ๋œ ๊ฒƒ์ด ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹์€ AspectJExpressionPointcut ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

NameMatchClassMethodPointcut์€ ํด๋ž˜์Šค์™€ ๋ฉ”์†Œ๋“œ์˜ ์ด๋ฆ„์„ ๊ฐ๊ฐ ๋…๋ฆฝ์ ์œผ๋กœ ๋น„๊ตํ•œ ๋ฐ˜๋ฉด์—, ํ‘œํ˜„์‹์œผ๋กœ๋Š” ํ•œ๋ฒˆ์— ์ง€์ •๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.

AspectJExpressionPointcut์˜ ์ด๋ฆ„์„ ๋ณด๋‹ค์‹œํ”ผ, ์Šคํ”„๋ง์€ AspectJ๋ผ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉฐ, ์ด๊ฒƒ์„ AspectJํ‘œํ˜„์‹์ด๋ผ ๋ถ€๋ฆ…๋‹ˆ๋‹ค.

๐Ÿ” ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹ ๋ฌธ๋ฒ•

AspectJ ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹์€ ํฌ์ธํŠธ์ปท ์ง€์‹œ์ž๋ฅผ ์ด์šฉํ•ด ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ํฌ์ธํŠธ์ปท ์ง€์‹œ์ž์ค‘์—์„œ ๊ฐ€์žฅ ๋Œ€ํ‘œ์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์€ execution์ž…๋‹ˆ๋‹ค.

execution(์ ‘๊ทผ์ œํ•œ์ž ํƒ€์ž…ํŒจํ„ด:return ํƒ€์ž… ํƒ€์ž…ํŒจํ„ด:ํด๋ž˜์Šค ํƒ€์ž….์ด๋ฆ„ํŒจํ„ด(๋ฉ”์†Œ๋“œ) (ํƒ€์ž…ํŒจํ„ด:ํŒŒ๋ผ๋ฏธํ„ฐํŒจํ„ด) throws ์˜ˆ์™ธํŒจํ„ด)

  1. : ๋Š” ์„ค๋ช…์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
  2. ์ ‘๊ทผ์ œํ•œ์ž, ํด๋ž˜์Šค ํƒ€์ž…ํŒจํ„ด, ์˜ˆ์™ธํŒจํ„ด ๋“ฑ์€ ์ƒ๋žต๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

๋ฌธ๋ฒ•์— ๋Œ€ํ•œ ์ž์„ธํ•œ ์„ค๋ช…์€ ์ƒ๋žตํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค!

์ด์ œ๋Š” Pointcutํ‘œํ˜„์‹์„ AspectJ ๋ฉ”์†Œ๋“œ์˜ ํŒŒ๋ฆฌ๋ฏธํ„ฐ๋กœ ํ•˜๋ฉด ์‹คํ–‰ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

Pointcut์„ ์ ์šฉํ•ด ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹์—๋Š” ์œ„์—์„œ ์ž ์‹œ ์–ธ๊ธ‰ํ–ˆ๋˜ execution ์™ธ์—๋„ bean์„ ์„ ํƒํ•˜์—ฌ์ฃผ๋Š” bean()๋ฉ”์†Œ๋“œ, ๋˜ํ•œ ํŠน์ • ์• ๋…ธํ…Œ์ด์…˜์ด ํƒ€์ž…, ๋ฉ”์†Œ๋“œ, ํŒŒ๋ผ๋ฏธํ„ฐ์— ์ ์šฉ๋˜์–ด ์žˆ๋Š” ๊ฒƒ์„ ๋ณด๊ณ 
๋ฉ”์†Œ๋“œ๋ฅผ ์„ ์ •ํ•˜๊ฒŒ ํ•˜๋Š” ํฌ์ธํŠธ์ปท์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด @Transactional ๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ๋ฅผ ๋งํ•ฉ๋‹ˆ๋‹ค.

ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹์€ AspectJExpressionPointcut๋นˆ์„ ๋“ฑ๋กํ•˜๊ณ  expression ํ”„๋กœํผํ‹ฐ์— ๋„ฃ์–ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ํด๋ž˜์Šค์ด๋ฆ„์€ ServiceImpl๋กœ ๋๋‚˜๊ณ  ๋ฉ”์†Œ๋“œ ์ด๋ฆ„์€
upgrade๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  ํด๋ž˜์Šค์— ์ ์šฉ๋˜๋„๋ก ์ฝ”๋“œ๋ฅผ ์งœ๋ด…์‹œ๋‹ค.

AppConfig.java

@Configuration
@RequiredArgsConstructor
@EnableTransactionManagement
public class AppConfig {

    private final Environment env;

    @Bean
    public DataSource dataSource(){
        SimpleDriverDataSource dataSource=new SimpleDriverDataSource();
        dataSource.setDriverClass(org.h2.Driver.class);
        dataSource.setUrl(env.getProperty("spring.datasource.url"));
        dataSource.setUsername(env.getProperty("spring.datasource.username"));
        dataSource.setPassword(env.getProperty("spring.datasource.password"));

        return dataSource;
    }

    @Bean
    public PlatformTransactionManager transactionManager(){
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean
    public JdbcOperations jdbcOperations(){
        return new JdbcTemplate(dataSource());
    }

    @Bean
    public UserDao userDao(){
        return new UserDaoImpl(jdbcOperations());
    }

    @Bean
    public TransactionAdvice transactionAdvice(){
        return new TransactionAdvice(transactionManager());
    }

    //์ถ”๊ฐ€๋œ ๋ถ€๋ถ„
    @Bean
    public AspectJExpressionPointcut transactionPointcut(){
        AspectJExpressionPointcut pointcut=new AspectJExpressionPointcut();
        pointcut.setExpression("bean(*Service)");
        return pointcut;
    }

    @Bean
    public DefaultPointcutAdvisor transactionAdvisor(){
        return new DefaultPointcutAdvisor(transactionPointcut(),transactionAdvice());
    }
    
    //๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ ๋“ฑ๋ก
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        return new DefaultAdvisorAutoProxyCreator();
    }

    @Bean
    public UserService userService(){
        return new UserServiceImpl(userDao(),mailSender());
    }


}

์ง€๊ธˆ๊นŒ์ง€์˜ ๊ณผ์ •์„ ๊ฑฐ์ณ AspectJExpressionPointcut์˜ ์ ์šฉ๊นŒ์ง€ ์™„๋ฃŒํ–ˆ์Šต๋‹ˆ๋‹ค.


๐Ÿš€ ๋งˆ์ง€๋ง‰์œผ๋กœ, AOP๋ž€?!

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

Aspect๋Š” ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค๋กœ์ง ๋ฐ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€๋Š” ์•Š์ง€๋งŒ ์ง€๊ธˆ๊นŒ์ง€ ํ•ด์™”๋˜ ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„ ์„ค์ • ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•œ๋‹ค๋˜์ง€ ํ•ต์‹ฌ๊ธฐ๋Šฅ์— ๋ถ€๊ฐ€๋˜๋Š” ๋ชจ๋“ˆ์„ ์ง€์นญํ•ฉ๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํ•ต์‹ฌ์ ์ธ ๊ธฐ๋Šฅ์—์„œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋ถ„๋ฆฌํ•ด์„œ Aspect๋ผ๋Š” ๋ชจ๋“ˆ๋กœ ๋งŒ๋“ค์–ด์„œ ๊ฐœ๋ฐœํ•˜๋Š” ๋ฐฉ๋ฒ•๋ก ์„ Aspect Oriented Progreamming AOP๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.

ํ•œ ๊ฐ€์ง€ ์œ ์˜ํ•˜์‹ค์ ์€, AOP๋Š” OOP์—์„œ ๋ถˆ๋ฆฌ๋œ ์ƒˆ๋กœ์šด ๊ฐœ๋…์˜ ๊ฐœ๋ฐœ๋ก ์ด ์•„๋‹Œ OOP๋ฅผ ๋•๋Š” ๋ณด์กฐ์ ์ธ ๊ธฐ์ˆ ์ด๋ผ๊ณ  ๋ณด์‹œ๋ฉด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ฆ‰, AOP๋Š” Aspect๋ฅผ ๋ถ„๋ฆฌํ•จ์œผ๋กœ์จ ํ•ต์‹ฌ ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๋Š”๋ฐ ๋ถ€๋‹ด์ด ์—†๋„๋ก ๋˜ํ•œ ์ตœ๋Œ€ํ•œ ๊ฐ์ฒด์ง€ํ–ฅ ๊ธฐ์ˆ ์„ ์œ ์ง€ํ•˜๋„๋ก ๋•๋Š” ๊ฐœ๋ฐœ๋ก ์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ” AOP ์ ์šฉ๊ธฐ์ˆ 

ํ”„๋ก์‹œ๋ฅผ ์ด์šฉํ•œ AOP

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

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

๋ฐ”์ดํŠธ์ฝ”๋“œ ์ƒ์„ฑ๊ณผ ์กฐ์ž‘์„ ํ†ตํ•œ AOP

AOPํ”„๋ ˆ์ž„์›Œํฌ์˜ ๋Œ€ํ‘œ๊ฒฉ์ธ AspectJ๋Š” ํ”„๋ก์‹œ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋Œ€ํ‘œ์ ์ธ AOP ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค.

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

์ด์™€ ๊ฐ™์ด ๋ฒˆ๊ฑฐ๋กœ์šด ์ด์œ ๋ฅผ ํ•˜๋Š” ์ด์œ ๋Š”
1. DI์˜ ๋„์›€์„ ๋ฐ›์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค.

  • ํƒ€๊ฒŸ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•˜๋Š” ๋ฐฉ์‹์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
  1. ํ›จ์‹ , Detailํ•˜๊ณ  ์œ ์—ฐํ•˜๊ฒŒ AOP๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ฐ”์ดํŠธ ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ์กฐ์ž‘ํ•จ์œผ๋กœ์„œ AOP๋ฅผ ์ ์šฉํ•˜๋ฉด ์˜ค๋ธŒ์ ํŠธ์˜ ์ƒ์„ฑ, ํ•„๋“œ ๊ฐ’์˜ ์กฐํšŒ์™€ ์กฐ์ž‘๋“ฑ ๋‹ค์–‘ํ•œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ ๋ถ€์—ฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

๊ฑฐ์˜ ์ผ๋ฐ˜์ ์ธ ๊ฒฝ์šฐ๋‚ด์—์„œ๋Š” ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•ด์„œ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ์ข€ ๋” ํŠน๋ณ„ํ•œ ์ƒํ™ฉ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ์— ์ด์™€๊ฐ™์€ ๋ฐฉ๋ฒ•์„ ์“ด๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.


๋๋งˆ์น˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๐Ÿ‘‹

profile
WEB STUDY & etc.. HELLO!

0๊ฐœ์˜ ๋Œ“๊ธ€