๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Back-end ๋ฐ๋ธŒ์ฝ”์Šค/week 03 - 05 TIL (Spring)

[TIL] 221111 - SpringBoot Part2 : ํŠธ๋žœ์žญ์…˜๊ณผ AoP

by young-ji 2022. 11. 22.

SpringBoot Part2 (5)

 

AoP - ๊ด€์  ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ

๊ธฐ๋Šฅ,๊ด€์‹ฌ ์ง€ํ–ฅ์  ํ”„๋กœ๊ทธ๋ž˜๋ฐ

 

 

Cross Cutting Concern

CCC

https://www.codejava.net/frameworks/spring/understanding-spring-aop

 

: ์ค‘๋ณต์ฝ”๋“œ๋ฅผ ์ผ์ผํžˆ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ํ–‰๋‹จ์œผ๋กœ ๊ฑธ์ณ์„œ ์ ์šฉํ•ด์•ผํ•˜๋Š” ๋ถ€๊ฐ€๊ธฐ๋Šฅ(Cross Cutting Concern)์„ ๋ถ„๋ฆฌํ•ด์„œ ๋ฐฉ์‹

ํ•ต์‹ฌ๋กœ์ง์— ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์‰ฝ๊ฒŒ ์ถ”๊ฐ€ํ• ์ˆ˜ ์žˆ๊ฒŒํ•œ๋‹ค.

 

class ๊ณ„์ขŒ์ด์ฒด์„œ๋น„์Šค {
		method ์ด์ฒด() {
				AAAA
				๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง
				BBBB
		}
		method ๊ณ„์ขŒํ™•์ธ() {
				AAAA
				๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง
				BBBB
		}
}
class ๋Œ€์ถœ์Šน์ธ์„œ๋น„์Šค {
		method ์Šน์ธ() {
				AAAA
				๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง
				BBBB
		}
}

๋น„์ง€๋‹ˆ์Šค ๋ฉ”์†Œ๋“œ๋Š” ๋น„์ง€๋‹ˆ์Šค ํ•ต์‹ฌ๋กœ์ง์—๋งŒ ์ง‘์ค‘ํ•˜๊ณ , ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ(AAA, BBB)์€ ๋ณ„๋„๋กœ ๋นผ์„œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋‹ด๋‹นํ•˜๋Š” ๋ชจ๋“ˆ์—์„œ ๋‹ด๋‹นํ•œ๋‹ค.

 

 

AoP ์ ์šฉ ๋ฐฉ๋ฒ•

https://www.researchgate.net/figure/The-relationships-between-different-AOP-weaving-strategies_fig2_220888911

 

 

์‹œ์ ์— ๋”ฐ๋ผ ํฌ๊ฒŒ 3๊ฐ€์ง€ ๊ด€์ ์„ ๋‚˜๋‰œ๋‹ค.

  • ์ปดํŒŒ์ผ ์‹œ์  → AoP ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์†Œ์ŠคํŒŒ์ผ์„ ์ปดํŒŒ์ผํ•˜๊ธฐ์ „์— ๊ณตํ†ต ๊ตฌํ˜„ ์ฝ”๋“œ๋ฅผ ์†Œ์Šค์— ์‚ฝ์ž…ํ•˜๋Š” ๋ฐฉ์‹
  • ํด๋ž˜์Šค ๋กœ๋”ฉ ์‹œ์ 
  • ๋Ÿฐํƒ€์ž„ ์‹œ์  → Spring์—์„œ ์ œ๊ณตํ•˜๋Š” AoP ๋ฐฉ์‹, proxy๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋™์ž‘ํ•˜๊ฒŒ ํ•˜๋Š” ๋ฐฉ์‹

 

 

Spring AoP - AoP Proxies

  • JDK Proxy (interface based → ๋‹ค์ด๋‚˜๋ฏน ํ”„๋ก์‹œ)
  • CGLib Proxy (class based)

 

Spring Proxies

JDK proxy ๊ธฐ๋ฒ• → Spring AoP์˜ ๋‚ด๋ถ€ ๋™์ž‘

// ํƒ€๊ฒŸ class
class CalculatorImpl implements Calculator {

    @Override
    public int add(int a, int b) {
        return a + b;
    }
}

interface Calculator {
    int add(int a,int b);
}

// invokation ํ•ธ๋“ค๋Ÿฌ -> targat ๊ฐ์ฒด๋ฅผ ํ˜ธ์ถœ
class LoggingInvocationHandler implements InvocationHandler{

    private static final Logger log = LoggerFactory.getLogger(LoggingInvocationHandler.class);
    private final Object target;

    LoggingInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("{} executed", method.getName());
        return method.invoke(target,args);
    }
}

public class JdkProxyTest {
    private static final Logger log = LoggerFactory.getLogger(JdkProxyTest.class);
    public static void main(String[] args) {
        Calculator calculator = new CalculatorImpl();
				// proxy ๊ฐ์ฒด ๋งŒ๋“ค๊ธฐ
        Calculator proxyInstance = (Calculator) Proxy.newProxyInstance( 
                LoggingInvocationHandler.class.getClassLoader(), //ClassLoader
                new Class[]{Calculator.class}, // target ์ธํ„ฐํŽ˜์ด์Šค
                new LoggingInvocationHandler(calculator)); // target์„ ์ „๋‹ฌํ•œ invoke ํ•ธ๋“ค๋Ÿฌ
        var res = proxyInstance.add(1,2);
        log.info("Add -> {} ",res);
    }
}

 

Spring AOP

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

 

 

@AspectJ

: JDK proxy๋ฅผ ์ด์šฉํ•˜์—ฌ ๋Ÿฐํƒ€์ž„์— AoP ์ ์šฉ

 

์ฃผ์š”์šฉ์–ด

  • ํƒ€๊ฒŸ(Target) - CustomerService
    • ํ•ต์‹ฌ ๊ธฐ๋Šฅ์„ ๋‹ด๊ณ  ์žˆ๋Š” ๋ชจ๋“ˆ๋กœ์„œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ•  ๋Œ€์ƒ
    • AoP๋ฅผ ์ ์šฉํ•  ๋Œ€์ƒ
  • ์กฐ์ธํฌ์ธํŠธ(Join Point) - createCustmer, getCustmer,..
    • ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ ์šฉํ• ์ˆ˜ ์žˆ๋Š” ์ง€์  (AoP๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์œ„์น˜)
    • ํƒ€๊ฒŸ ๊ฐ์ฒด๊ฐ€ ๊ตฌํ˜„ํ•œ ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ชจ๋“  ๋ฉ”์„œ๋“œ
  • ํฌ์ธํŠธ ์ปท(Pointcut)
    • ์–ด๋“œ๋ฐ”์ด์Šค(๋ถ€๊ฐ€๊ธฐ๋Šฅ)๋ฅผ ์ ์šฉํ•  ํƒ€๊ฒŸ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์„ ๋ณ„ํ•˜๋Š” ์ •๊ทœํ‘œํ˜„์‹
    • ์—ฌ๋Ÿฌ ์กฐ์ธํŠธํฌ์ธํŠธ ์ค‘์— ์–ด๋”” ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ ์šฉ์‹œํ‚ฌ์ง€ ๋‚˜ํƒ€๋‚ธ๋‹ค.
    • ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹์€ execution์œผ๋กœ ์‹œ์ž‘ํ•˜๊ณ  ๋ฉ”์„œ๋“œ์˜ Signature๋ฅผ ๋น„๊ตํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ฃผ๋กœ ์ด์šฉํ•จ
  • ์• ์ŠคํŽ™ํŠธ(Aspect)
    • ๊ด€์ , ๊ธฐ๋Šฅ, ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ
    • ์• ์ŠคํŽ™ํŠธ = ์–ด๋“œ๋ฐ”์ด์Šค + ํฌ์ธํŠธ์ปท (๋ถ€๊ฐ€๊ธฐ๋Šฅ set)
    • Spring์—์„œ๋Š” Aspect๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•ด์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์–ด๋“œ๋ฐ”์ด์Šค(Advice)
    • ์–ด๋“œ๋ฐ”์ด์Šค๋Š” ํƒ€๊ฒŸ์˜ ํŠน์ • ์กฐ์ธํŠธํฌ์ธํŠธ์— ์ œ๊ณตํ•  ๋ถ€๊ฐ€๊ธฐ๋Šฅ
    • Advice์—๋Š” @Before, @After, @Around, @AfterReturning, @AfterThrowing ๋“ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

https://mossgreen.github.io/Spring-Certification-Spring-AOP/

  • ์œ„๋น™
    • ํƒ€์ผ“์˜ ์กฐ์ธ ํฌ์ธํŠธ์— ์–ด๋“œ๋ฐ”์ด์ฆˆ๊ฐ€ ์ ์šฉ๋˜๋Š” ๊ณผ์ • (AoP๋ฅผ ์ ์šฉํ•˜๋Š” ๊ณผ์ •)

 

์‹ค์Šต

// Aspect ์ •์˜
@Aspect
@Component
public class LoggingAspect {
    private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class);

		// execution(์ ‘๊ทผ์ง€์ •์ž ๋ฆฌํ„ดํƒ€์ž… ํŒจํ‚ค์ง€.๋ฉ”์†Œ๋“œ(arg))
    @Around("execution(public * org.prgrms.kdt..*.*(..))") // ํฌ์ธํŠธ์ปท
    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("Before method called. {}",joinPoint.getSignature().toString());
        var result = joinPoint.proceed();
        log.info("After method called with result => {}",result);
        return result;
    }
}

@Configuration
@ComponentScan(basePackages = {"org.prgrms.kdt.voucher","org.prgrms.kdt.aop"})
@EnableAspectJAutoProxy // AoP ์ ์šฉ ์–ด๋…ธํ…Œ์ด์…˜
static  class Config{
				....
}

๋“ฑ๋ก๋œ Bean์— ์˜ํ•ด์„œ๋งŒ ์–ด๋“œ๋ฐ”์ด์Šค๊ฐ€ ๊ฐ€๋Šฅ

 

 

@Around() → ํฌ์ธํŠธ ์ปท

  • execution PCD

 

  • ํฌ์ธํŠธ์ปท ๋ชจ๋“ˆํ™”
public class CommonPointcut {
    @Pointcut("execution(public * org.prgrms.kdt..*.*(..))")
    public void servicePublicMethodPointcut(){};

    @Pointcut("execution(* org.prgrms.kdt..*Repository.*(..))")
    public void repositoryPublicMethodPointcut(){};
}

@Aspect
@Component
public class LoggingAspect {
    private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class);

    @Around("org.prgrms.kdt.aop.CommonPointcut.repositoryPublicMethodPointcut()") // ํฌ์ธํŠธ์ปท ํ’€class ๋ช…
    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("Before method called. {}",joinPoint.getSignature().toString());
        var result = joinPoint.proceed();
        log.info("After method called with result => {}",result);
        return result;
    }
}

 

  • ์–ด๋…ธํƒœ์ด์…˜์„ ์ด์šฉํ•œ ํฌ์ธํŠธ์ปท
// ์‚ฌ์šฉ์ž ์–ด๋…ธํ…Œ์ด์…˜ ๋งŒ๋“ค๊ธฐ
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TrackTime {
}

// Aspect
@Aspect
@Component
public class LoggingAspect {
    private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class);

		// TrackTime ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•œ class๋ฅผ ํฌ์ธํŠธ์ปท์œผ๋กœ ์ค€๋‹ค.
    @Around("@annotation(org.prgrms.kdt.aop.TrackTime)")
    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("Before method called. {}",joinPoint.getSignature().toString());
        var startTime = System.nanoTime(); // 1 -> 1,000,000,000
        var result = joinPoint.proceed();
        var endTime = System.nanoTime() - startTime;
        log.info("After method called with result => {} and time taken {} nanoseconds",result,endTime);
        return result;
    }
}

// ์–ด๋…ธํ…Œ์ด์…˜ ์ ์šฉ
public class MemoryVoucherRepository {

	private  final Map<UUID, Voucher> storage = new ConcurrentHashMap<>();
  
	@Override
	@TrackTime
	public Voucher insert(Voucher voucher) {
	    storage.put(voucher.getVoucherId(),voucher);
	    return voucher;
	}

	....
}

 

์‹ค์ œ AoP๋ฅผ ์ง์ ‘ ๋งŒ๋“ค์–ด ์“ฐ๋Š” ๊ฒฝ์šฐ๋„ ์ข…์ข…์žˆ์ง€๋งŒ ๊ฑฐ์˜ ๊ณตํ†ตํŒ€(ํ”„๋ ˆ์ž„์›Œํฌ ํŒ€)์—์„œ ์–ด๋…ธํ…Œ์ด์…˜, aspect๋“ฑ์„ ๋งŒ๋“ค์–ด ์ฃผ๊ณ  ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง„ํ–‰ํ•˜๊ฒŒ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.

 


ํŠธ๋žœ์žญ์…˜

Spring ํŠธ๋žœ์žญ์…˜์€ ์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜์œผ๋กœ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ์— ๋Œ€ํ•œ Aspect๋ฅผ ์ œ๊ณตํ•ด์ฃผ๋Š” ๋™์ž‘์ด๋‹ค.

@Configuration
@ComponentScan(basePackages = {"org.prgrms.kdt.voucher","org.prgrms.kdt.aop"})
@EnableAspectJAutoProxy // AoP ์ ์šฉ ์–ด๋…ธํ…Œ์ด์…˜
static  class Config{
				....
}

 

PlatfoemTranactionManager

PlatfoemTranactionManager๋ฅผ ํ†ตํ•ด ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ๊ฐ€ ์ด๋ฃจ์›Œ์ง„๋‹ค.

DataTranactionManager ํ†ตํ•ด ์ง์ ‘ ํŠธ๋žœ์žญ์…˜ ๋“ฑ๋ก

// Bean๋“ฑ๋ก
@Bean
public PlatformTransactionManager platformTransactionManager(DataSource dataSource){
    return new DataSourceTransactionManager(dataSource);
}

public class CustomerJdbcRepository implements CustomerRepository {
	
		// ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ์ฃผ์ž…
		private final PlatformTransactionManager transactionManager;

		....

		public void testTransaction(Customer customer){
        var transaction = transactionManager.getTransaction(new DefaultTransactionDefinition()); //ํŠธ๋žœ์žญ์…˜ ์ƒ์„ฑ
        try {
            jdbcTemplate.update("UPDATE customers SET name= :name  WHERE customer_id = UUID_TO_BIN(:customerId)",toParamMap(customer));
            jdbcTemplate.update("UPDATE customers SET email= :email WHERE customer_id = UUID_TO_BIN(:customerId)",toParamMap(customer));
            transactionManager.commit(transaction);
        }catch (DataAccessException e) {
            logger.info(e.getMessage());
            transactionManager.rollback(transaction);
        }
    }
}

 

 

tranaction template ์‚ฌ์šฉ

// Bean๋“ฑ๋ก
@Bean
public PlatformTransactionManager platformTransactionManager(DataSource dataSource){
    return new DataSourceTransactionManager(dataSource);
}

@Bean
public TransactionTemplate transactionTemplate(PlatformTransactionManager platformTransactionManager){
    return new TransactionTemplate(platformTransactionManager);
}

public class CustomerJdbcRepository implements CustomerRepository {
	
		// ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ์ฃผ์ž…
		private final TransactionTemplate transactionTemplate;

		....

		public void testTransaction(Customer customer){
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                jdbcTemplate.update("UPDATE customers SET name= :name  WHERE customer_id = UUID_TO_BIN(:customerId)",toParamMap(customer));
                jdbcTemplate.update("UPDATE customers SET email= :email WHERE customer_id = UUID_TO_BIN(:customerId)",toParamMap(customer));
            }
        });
    }
}

 

@Transactional

spring์—์„œ ์–ด๋…ธํ…Œ์ด์…˜์„ ์ด์šฉํ•ด ํŠธ๋žœ์žญ์…˜์„ ์ œ๊ณตํ•œ๋‹ค.(์„ ์–ธ์ „ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ)

Aop proxy๋ฅผ ํ†ตํ•ด ๋™์ž‘ํ•œ๋‹ค.

: service๋‹จ ์—์„œ ๋งŽ์ด ์‚ฌ์šฉํ•˜๊ฒŒ๋œ๋‹ค. ์ผ๋ จ์˜ ๋™์ž‘๋“ค์„ ํ•˜๋‚˜๋กœ ๋ฌถ๊ธฐ์œ„ํ•ด

public class CustomerServiceImpl implements CustomerService {
    
    private final CustomerRepository customerRepository;

    public CustomerServiceImpl(CustomerRepository customerRepository) {
        this.customerRepository = customerRepository;
    }

    @Override
    @Transactional
    public void createCustomers(List<Customer> customers) {
        customers.forEach(customerRepository::insert);
    }
}

 

 

ํŠธ๋žœ์žญ์…˜ ์ „ํŒŒ

ํŠน์ • ๋ฉ”์†Œ๋“œ A ๋‚ด ํŠธ๋ Œ์ น์…˜์ด ์ฒ˜๋ฆฌ๋˜๋Š” ๊ณผ์ •์•ˆ์—์„œ ๋˜ ๋‹ค๋ฅธ ๋ฉ”์†Œ๋“œ B ํŠธ๋ Œ์ ์…˜์ด ์ฒ˜๋ฆฌ๋˜๋Š” ๊ณผ์ •

// propagation ์œผ๋กœ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.
@Transactional(propagation = Propagation.REQUIRED) // defualt
public void createCustomers(List<Customer> customers) {
    customers.forEach(customerRepository::insert);
}

REQUIRED

: ํ˜„์žฌ ์ง„ํ–‰์ค‘์ธ ํŠธ๋ Œ์ ์…˜(addCustomer)์ด ์žˆ๋‹ค๋ฉด ์ƒˆ๋กœ์šด ํŠธ๋ Œ์ ์…˜์ด ๋งŒ๋“ค์–ด ์ง€์ง€์•Š๊ณ  ํ•ด๋‹น ํŠธ๋ Œ์ ์…˜์„ ์‚ฌ์šฉํ•œ๋‹ค. ์—†๋‹ค๋ฉด ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์ง„๋‹ค.

 

MANDATORY

: ์ด๋ฏธ ์ง„ํ–‰์ค‘์ธ ๋ฉ”์†Œ๋“œ(์ƒ์œ„ ๋ฉ”์†Œ๋“œ)์— ํŠธ๋ Œ์ ์…˜์ด ์—†๋‹ค๋ฉด ์—๋Ÿฌ ๋ฐœ์ƒ. ํ˜ธ์ถœ์ „์— ๋ฐ˜๋“œ์‹œ ์ง„ํ–‰ ์ค‘์ธ ํŠธ๋ Œ์ ์…˜์ด ์žˆ์–ด์•ผํ•œ๋‹ค.

 

REQURED_NEW

: ์ด๋ฏธ ์ง„ํ–‰์ค‘์ธ ํŠธ๋ Œ์ ์…˜์ด ์žˆ์–ด๋„ ํ•ญ์ƒ ์ƒˆ๋กœ์šด ํŠธ๋ Œ์ ์…˜(createCustomer, findOne)์ด ์ƒ๊ฒจ๋‚œ๋‹ค. ์ง„ํ–‰ ์ค‘์ด๋˜ ํŠธ๋ Œ์ ์…˜์€ ์ž ์‹œ ์ค‘๋‹จ๋œ๋‹ค.

: ์ƒˆ๋กœ์šด ํŠธ๋ Œ์ ์…˜์—์„œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋ฉด ํ•ด๋‹น ํŠธ๋ Œ์ ์…˜์—์„œ๋งŒ rollback์ด ๋˜๊ณ  ์ด์ „์— ํŠธ๋ Œ์ ์…˜(addCustomer)์—๋Š” ์˜ํ–ฅ์„ ์ฃผ์ง€์•Š๋Š”๋‹ค.

 

SUPPORTES

: ํ˜„์žฌ ์ง„ํ–‰์ค‘์ธ ํŠธ๋ Œ์ ์…˜(addCustomer)์ด ์žˆ๋‹ค๋ฉด ์ƒˆ๋กœ์šด ํŠธ๋ Œ์ ์…˜์„ ๋งŒ๋“ค์ง€์•Š๊ณ  ํ•ด๋‹น ํŠธ๋ Œ์ ์…˜์„ ์‚ฌ์šฉํ•˜๊ณ  ์—†๋‹ค๋ฉด ํŠธ๋ Œ์ ์…˜์„ ๋งŒ๋“ค์ง€์•Š๋Š”๋‹ค.

 

NOT_SUPPORTED

: ํ˜„์žฌ ์ง„ํ–‰์ค‘์ธ ํŠธ๋ Œ์ ์…˜(addCustomer)์ด ์žˆ์–ด๋„ ์ƒˆ๋กœ์šด ํŠธ๋ Œ์ ์…˜์ด ๋งŒ๋“ค์ง€์•Š๊ณ  ์ž ์‹œ ๋ฉˆ์ถฐ์žˆ๋‹ค๊ฐ€ ์ข…๋ฃŒ๊ฐ€๋˜๋ฉด ๋‹ค์‹œ ์‹คํ–‰๋œ๋‹ค.

 

 

ํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ

ํŠธ๋ Œ์ ์…˜์ด ์ง„ํ–‰ ์ค‘์ผ๋•Œ ํ•˜์œ„ ํŠธ๋ Œ์ ์…˜์—์„œ ์ž์›์„ ๋ณ€๊ฒฝํ• ๋•Œ, ๊ฐœ๋ณ„ ํŠธ๋ Œ์ ์…˜์˜ ๋…๋ฆฝ์„ฑ์˜ ๋‹จ๊ณ„๋ฅผ ๋‚˜๋ˆˆ ๊ฒƒ.

@Transactional(isolation = Isolation.DEFAULT) // ์‚ฌ์šฉํ•˜๋Š” DBMS์˜ default๊ฐ’์„ ์‚ฌ์šฉํ•˜๊ฒ ๋‹ค.
public void createCustomers(List<Customer> customers) {
    customers.forEach(customerRepository::insert);
}

๋ช…๋ น์–ด๋ฅผ ํ†ตํ•œ defualt๊ฐ’ ํ™•์ธ

https://techannotation.wordpress.com/2014/12/04/5-minutes-with-spring-transaction-isolation-level/

 

dirty reads

: ์•„์ง ์ˆ˜์ •์ค‘์ธ ๋ฐ์ดํ„ฐ(commit ๋˜์ง€์•Š์€ ๋ฐ์ดํ„ฐ)๋ฅผ ๋‹ค๋ฅธ ํŠธ๋ Œ์ ์…˜์—์„œ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค. rollback์ด ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋ฌธ์ œ.

 

non-repeatable

: ํ•œ ํŠธ๋ Œ์ ์…˜ ๋‚ด์—์„œ ๊ฐ™์€ ์ฟผ๋ฆฌ๋ฅผ ๋‘๋ฒˆ ์‚ฌ์šฉํ• ๋•Œ, ๋‹ค๋ฅธ ํŠธ๋ Œ์ ์…˜์ด ๊ทธ ์‚ฌ์ด ๊ฐ’์„ ์ˆ˜์ •ํ•˜๋ฉด ์ผ๊ด€๋˜์ง€์•Š์€ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜จ๋‹ค.

 

phantom reads

: ํ•œ ํŠธ๋žœ์žญ์…˜์•ˆ์—์„œ ์ผ์ • ๋ฒ”์œ„์˜ ๋ ˆ์ฝ”๋“œ๋ฅผ ์—ฌ๋Ÿฌ๋ฒˆ ์ฝ์–ด์˜ฌ๋•Œ, ์ฒซ๋ฒˆ์งธ ์ฟผ๋ฆฌ์—์„œ ์—†๋˜ ์œ ๋ น์˜ ๋ ˆ์ฝ”๋“œ๊ฐ€ ๋‘๋ฒˆ์งธ ์ฟผ๋ฆฌ์—์„œ ๋ฐœ์ƒํ•œ๋‹ค.

 

 

์ถœ์ฒ˜ - ํ•ด๋ฆฌ : SpringBoot Part1

๋Œ“๊ธ€