[์†๋‹ฅ์†๋‹ฅ] ๐Ÿš€ ์šฐ๋‹นํƒ•ํƒ• ์ฟผ๋ฆฌ ์นด์šดํ„ฐ ๊ฐœ๋ฐœ๊ธฐ

ํ—Œ์น˜ยท2022๋…„ 11์›” 1์ผ
3

์šฐ์•„ํ•œํ…Œํฌ์ฝ”์Šค

๋ชฉ๋ก ๋ณด๊ธฐ
24/30

1. ๋“ค์–ด๊ฐ€๋ฉฐโœจ

์†๋‹ฅ์†๋‹ฅ์€ Spring Data JPA๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ง์ธ ์ฆ‰์Šจ, N+1 ๋ฌธ์ œ๋ฅผ ๊ฐœ์„ ํ•˜๊ณ , ์ฟผ๋ฆฌ ๊ฐœ์ˆ˜๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋งํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค๋Š” ๋œป์ด์—ˆ์Šต๋‹ˆ๋‹ค.
์ด์— ์ €์™€ ์ด์ŠคํŠธ๋Š” ํŠน์ • ์ปค๋„ฅ์…˜์—์„œ ์‚ฌ์šฉ๋œ ์ฟผ๋ฆฌ ๊ฐœ์ˆ˜๋ฅผ ์„ธ๋Š” "์ฟผ๋ฆฌ ์นด์šดํ„ฐ"๋ฅผ ๊ฐœ๋ฐœํ–ˆ์Šต๋‹ˆ๋‹ค!


sql์˜ count() ์•„๋‹™๋‹ˆ๋‹ค

์ฟผ๋ฆฌ ์นด์šดํ„ฐ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๊ณ ๋ คํ–ˆ๋˜ ๊ฒƒ์€ ์ด 2๊ฐ€์ง€์˜€์Šต๋‹ˆ๋‹ค.

  1. ํ•˜์ด๋ฒ„๋„ค์ดํŠธ์˜ StatementInspector ์‚ฌ์šฉํ•˜๊ธฐ
  2. ์ง์ ‘ AOP ๊ธฐ๋ฐ˜ ์ฟผ๋ฆฌ์นด์šดํ„ฐ ๋งŒ๋“ค๊ธฐ

์ €ํฌ๋Š” 2๊ฐ€์ง€ ๋ฐฉ๋ฒ• ์ค‘ 2๋ฒˆ์„ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.

1๋ฒˆ์ด ๊ตฌํ˜„ํ•˜๊ธฐ์—” ๋” ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ ์ดํ›„ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ์ด์™ธ์˜ ๊ตฌํ˜„์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ์‚ฌ์ •์ƒ ์ง์ ‘ JdbcTemlate์„ ์‚ฌ์šฉํ–ˆ์„ ๊ฒฝ์šฐ์—๋„ ๋ฒ”์šฉ์„ฑ์žˆ๋Š” ์ฟผ๋ฆฌ ์นด์šดํ„ฐ๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์‹ถ์–ด AOP๋กœ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ์ฟผ๋ฆฌ ์นด์šดํ„ฐ ์„ธํŒ… ๊ณผ์ •์ž…๋‹ˆ๋‹ค!

2. ์„ธํŒ… ๊ณผ์ •

1. Spring AOP๋ฅผ ํ†ตํ•œ PerformanceAspect ๊ตฌํ˜„

์šฐ์„  ์ €ํฌ ์ฟผ๋ฆฌ์นด์šดํ„ฐ๋Š” AOP ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
AOP๋Š” ๊ธฐ์กด ๊ธฐ๋Šฅ์„ ๋‹ด์€ ๊ฐ์ฒด(ํƒ€๊ฒŸ)์˜ ํ”„๋ก์‹œ์— ๋ถ€๊ฐ€๊ธฐ๋Šฅ(๊ด€์ , Aspect)์„ ์ถ”๊ฐ€ํ•ด์ฃผ๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

์Šคํ”„๋ง์—์„œ๋Š” ์–ด๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•œ ์‰ฌ์šด AOP ๊ตฌํ˜„์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ๋Š”๋ฐ์š”, ์› ๊ฐ์ฒด๊ฐ€ Bean์œผ๋กœ ๋“ฑ๋ก๋œ ์‹ฑ๊ธ€ํ„ด ๊ฐ์ฒด์—ฌ์•ผ ํ•œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.
์ €ํฌ๊ฐ€ ํƒ€๊ฒŸ์œผ๋กœ ์„ ์ •ํ•˜๊ณ  ์‹ถ์€ ๊ฑด Connection์ด๊ณ , ๋‹น์—ฐํžˆ ์‹ฑ๊ธ€ํ„ด ๊ฐ์ฒด๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.
๊ทธ๋ ‡๋‹ค๋ฉด ์ €ํฌ์˜ AOP๋ฅผ ํ†ตํ•œ ์ฟผ๋ฆฌ์นด์šดํŒ… ๊ด€์  ์ ์šฉ ๊ณ„ํš์€ ๋ฌผ๊ฑฐํ’ˆ์ด ๋œ ๊ฑธ๊นŒ์š”?๐Ÿ˜ข

๋‹คํ–‰ํžˆ ์•„๋‹™๋‹ˆ๋‹ค!๐Ÿ˜„
DataSource์—์„œ getConnection() ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด Connection์„ ์–ป๊ฒŒ ๋˜๋Š”๋ฐ์š”.
์ด DataSource๊ฐ€ Bean ๋“ฑ๋ก ๋˜์–ด์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค!

๋งŒ์•ฝ getConnection()์ด ํ˜ธ์ถœ๋  ๋•Œ๋งˆ๋‹ค,
์ง„์งœ ์ปค๋„ฅ์…˜์ด ์•„๋‹Œ
์ด๋ฅผ ๋‚š์•„์ฑ„ ์ฟผ๋ฆฌ ์นด์šดํ„ฐ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•œ ํ”„๋ก์‹œ ์ปค๋„ฅ์…˜ ๊ฐ์ฒด๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค๋ฉด?!

๋‹ค์Œ์€ ํ•ด๋‹น ์•„์ด๋””์–ด๋ฅผ ํ†ตํ•ด ํ”„๋ก์‹œ ์ปค๋„ฅ์…˜์„ ๋ฆฌํ„ดํ•˜๋Š” ํด๋ž˜์Šค, PerformanceAspect ์ž…๋‹ˆ๋‹ค.

import java.lang.reflect.Proxy;
import java.sql.Connection;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Slf4j
public class PerformanceAspect {

    private final ThreadLocal<QueryCounter> queryCounter = new ThreadLocal<>();

    @Pointcut("execution(* javax.sql.DataSource.getConnection(..))")
    public void performancePointcut() {
    }

    @Around("performancePointcut()")
    public Object start (ProceedingJoinPoint point) throws Throwable {
        final Connection connection = (Connection) point.proceed();
        queryCounter.set(new QueryCounter());
        final QueryCounter counter = this.queryCounter.get();

        final Connection proxyConnection = getProxyConnection(connection, counter);
        queryCounter.remove();
        return proxyConnection;
    }

    private Connection getProxyConnection(Connection connection, QueryCounter counter) {
        return (Connection) Proxy.newProxyInstance(
                getClass().getClassLoader(),
                new Class[]{Connection.class},
                new ConnectionHandler(connection, counter)
        );
    }
}

2. ConnectionHandler๋ฅผ ํ†ตํ•œ ๋‹ค์ด๋‚˜๋ฏน ํ”„๋ก์‹œ ๊ตฌํ˜„๐Ÿ˜„

PerformanceAspect ํด๋ž˜์Šค ์† getProxyConnection์˜ ๊ตฌํ˜„์ด

return (Connection) Proxy.newProxyInstance(
                getClass().getClassLoader(),
                new Class[]{Connection.class},
                new ConnectionHandler(connection, counter)
        );

๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋˜์–ด์žˆ๋Š”๋ฐ์š”. ๋‹ค์ด๋‚˜๋ฏน ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•ด ์ปค๋„ฅ์…˜์˜ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋‹ค์ด๋‚˜๋ฏน ํ”„๋ก์‹œ๋ž€, ๋Ÿฐํƒ€์ž„ ์ค‘์— ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๊ตฌํ˜„๋œ ์ธ์Šคํ„ด์Šค๋ฅผ ๋™์ ์œผ๋กœ ๊ตฌํ˜„ํ•œ ํ”„๋ก์‹œ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

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

๊ทธ๋ž˜์„œ ๋‹ค์ด๋‚˜๋ฏน ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•ด ํ”„๋ก์‹œ ์ปค๋„ฅ์…˜ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ์คฌ์Šต๋‹ˆ๋‹ค.

์„ธํŒ…๋ฐฉ๋ฒ•

๋‹ค์ด๋‚˜๋ฏน ํ”„๋ก์‹œ๋Š” Java java.lang.reflect.Proxy package์—์„œ ์ œ๊ณตํ•ด์ฃผ๋Š” API๋ฅผ ์ด์šฉํ•˜์—ฌ ์†์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ค€๋น„๋ฌผ์€ ์ด ์„ธ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค.
ํด๋ž˜์Šค๋กœ๋”, ์› ํƒ€๊ฒŸ ํด๋ž˜์Šค(์ธํ„ฐํŽ˜์ด์Šค), ๋™์ ์œผ๋กœ ํ”„๋ก์‹œ๋ฅผ ์„ค์ •ํ•  ConnectionHandler์ž…๋‹ˆ๋‹ค.
์ด์ค‘ ์ง์ ‘ ๊ตฌํ˜„์ด ํ•„์š”ํ•œ ConnectionHandler๋กœ ๋“ค์–ด๊ฐ€ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

@Slf4j
public class ConnectionHandler implements InvocationHandler {

    private final Object target;
    private final QueryCounter counter;

    public ConnectionHandler(Object target, QueryCounter counter) {
        this.target = target;
        this.counter = counter;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        countPrepareStatement(method);
        logQueryCount(method);
        return method.invoke(target, args);
    }

    private void logQueryCount(Method method) {
        if (method.getName().equals("close")) {
            warnTooManyQuery();
            log.info("\n====== count : {} =======\n", counter.getCount());
        }
    }

    private void countPrepareStatement(Method method) {
        if (method.getName().equals("prepareStatement")) {
            counter.increase();
        }
    }

    private void warnTooManyQuery() {
        if (counter.isWarn()) {
            log.warn("\n======= Too Many Query !!!! =======");
        }
    }
}

ํ•ด๋‹น ํ•ธ๋“ค๋Ÿฌ๋Š” InvocationHandler ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ด ๊ผญ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์ด์ค‘ invoke() ๋ฉ”์†Œ๋“œ๊ฐ€ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.

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

์ €ํฌ๋Š” connection ๊ฐ์ฒด๊ฐ€ prepareStatement() ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค ์ฟผ๋ฆฌ ์นด์šดํŠธ๋ฅผ ์ฆ๊ฐ€์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.
์ปค๋„ฅ์…˜์„ ํ†ตํ•ด ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋  ๋•Œ๋งˆ๋‹ค ํ•ด๋‹น ๋ฉ”์†Œ๋“œ๊ฐ€ ํ•œ๋ฒˆ ์”ฉ ํ˜ธ์ถœ๋œ๋‹ค๋Š” ์ ์— ์ฐฉ์•ˆํ–ˆ์Šต๋‹ˆ๋‹ค.

3. QueryCounter ๊ตฌํ˜„๐Ÿ˜„

์ด์ œ ๋Œ€๋ง์˜ QueryCounter ์ž…๋‹ˆ๋‹ค.

๋ณ„ ๊ตฌํ˜„์€ ํ•„์š” ์—†๊ณ , count ๋ณ€์ˆ˜๋ฅผ ๊ฐ์‹ผ ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.
increase() ๋ฅผ ํ†ตํ•ด ์ฟผ๋ฆฌ ์‹คํ–‰ ์ˆ˜๋ฅผ 1 ์ฆ๊ฐ€์‹œํ‚ค๊ณ 
isWarn() ์„ ํ†ตํ•ด 11๊ฐœ ์ด์ƒ์˜ ์ฟผ๋ฆฌ ์นด์šดํŠธ ์—ฌ๋ถ€๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

public class QueryCounter {

    private int count;

    public void increase() {
        count++;
    }

    public int getCount() {
        return count;
    }

    public boolean isWarn() {
        return count > 10;
    }
}

4. ThreadLocal์„ ํ†ตํ•œ ์Šค๋ ˆ๋“œ ๋ณ„ ์นด์šดํ„ฐ ๊ฐ์ฒด ์ž์› ํ• ๋‹น๐Ÿ˜„

์ž, ์ด์ œ ํ•ด๋‹น ์นด์šดํ„ฐ ๊ฐ์ฒด๊ฐ€ ์Šค๋ ˆ๋“œ๋ณ„๋กœ ํ• ๋‹น๋˜๊ฒŒ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ThreadLocal์„ ์ด์šฉํ•˜๋ฉด ๋˜๋Š”๋ฐ์š”.
์Šค๋ ˆ๋“œ ์˜์—ญ์— ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ํŠน์ • ์Šค๋ ˆ๋“œ๊ฐ€ ์‹คํ–‰ํ•˜๋Š” ๋ชจ๋“  ์ฝ”๋“œ์—์„œ ๊ทธ ์“ฐ๋ ˆ๋“œ์— ์„ค์ •๋œ ๋ณ€์ˆ˜ ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ๋ฒ•

ThreadLocal.set() : ํ˜„์žฌ ์“ฐ๋ ˆ๋“œ์˜ ๋กœ์ปฌ ๋ณ€์ˆ˜์— ๊ฐ’์„ ์ €์žฅํ•œ๋‹ค.
ThreadLocal.get() : ํ˜„์žฌ ์“ฐ๋ ˆ๋“œ์˜ ๋กœ์ปฌ ๋ณ€์ˆ˜ ๊ฐ’์„ ์ฝ์–ด์˜จ๋‹ค.
ThreadLocal.remove() : ํ˜„์žฌ ์“ฐ๋ ˆ๋“œ์˜ ๋กœ์ปฌ ๋ณ€์ˆ˜ ๊ฐ’์„ ์‚ญ์ œํ•œ๋‹ค.

@Slf4j
public class ConnectionHandler implements InvocationHandler {

    ...

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        countPrepareStatement(method);
        logQueryCount(method);
        return method.invoke(target, args);
    }

    private void logQueryCount(Method method) {
        if (method.getName().equals("close")) {
            warnTooManyQuery();
            log.info("\n====== count : {} =======\n", counter.getCount());
        }
    }

    private void warnTooManyQuery() {
        if (counter.isWarn()) {
            log.warn("\n======= Too Many Query !!!! =======");
        }
    }
}

์ €ํฌ๋Š” connection์ด ๋๋‚  ๋•Œ์— ์ฟผ๋ฆฌ ์นด์šดํŠธ ๊ฐœ์ˆ˜๋ฅผ ๋กœ๊ทธ๋กœ ๋ณด์—ฌ์ฃผ๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค.

์ด์— ๋‹ค์ด๋‚˜๋ฏน ํ”„๋ก์‹œ์—์„œ,
์ปค๋„ฅ์…˜ ์† close() ๋ฉ”์†Œ๋“œ๊ฐ€ ์‹คํ–‰๋˜์—ˆ์„ ๋•Œ ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋˜๋„๋ก ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค!

3. ์•ž์œผ๋กœ์˜ ๋ฐฉํ–ฅ

1. ์ฟผ๋ฆฌ ์ •๋ณด๋„ ํ•„์š”ํ•˜๋‹ค๐Ÿ‘€

ํ˜„์žฌ ๋„ˆ๋ฌด ๋งŽ์€ ์ฟผ๋ฆฌ๊ฐ€ ์นด์šดํŠธ๋˜์—ˆ์„ ๋•Œ ๊ฒฝ๊ณ ํ•ด์ฃผ๋Š” warnTooManyQuery() ๋ฉ”์†Œ๋“œ์—์„œ๋Š”, ํ•ด๋‹น ์ฟผ๋ฆฌ๋‚˜ ๋ฉ”์†Œ๋“œ ๊ด€๋ จ ์ •๋ณด๊ฐ€ ์ œ๊ณต๋˜์ง€ ์•Š๋Š”๋ฐ์š”.
ํ•œ๋ฒˆ ๋” ๋‹ค์ด๋‚˜๋ฏน ํ”„๋ก์‹œ๋ฅผ ์ ์šฉํ•˜๋˜๊ฐ€ ํ•ด์„œ, ๋งˆ์ง€๋ง‰ ์ฟผ๋ฆฌ ์ƒํƒœ๋ผ๋„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋กœ๊น…ํ•˜๋Š” ๋ฐฉ๋ฒ•๊นŒ์ง€ ๊ณ ๋ คํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

2. ์™œ ๋กœ๊ทธ๊ฐ€ 3๋ฒˆ์”ฉ ๋‚˜๊ฐ€???๐Ÿ‘€

ํ˜„์žฌ DEV ์„œ๋ฒ„์—์„œ ์ฟผ๋ฆฌ์นด์šดํ„ฐ ๋กœ๊ทธ๋ฅผ ๋ณผ ์‹œ, ํŠน์ • ์š”์ฒญ๋งˆ๋‹ค 3๋ฒˆ์”ฉ ๋™์ผํ•œ ๋กœ๊ทธ๊ฐ€ ์ฐํžˆ๋Š” ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์ €ํฌ๊ฐ€ DB์— ๋ฆฌํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ ์šฉํ•˜๋ฉด์„œ ์ด 3๊ฐœ์˜ DataSource๊ฐ€ ์žˆ๊ณ , ๊ฐ DataSource๋งˆ๋‹ค ๋กœ๊ทธ๊ฐ€ ์ฐํ˜”๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

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

4. ์ฐธ๊ณ ์ž๋ฃŒ

http://knes1.github.io/blog/2015/2015-07-08-counting-queries-per-request-with-hibernate-and-spring.html

https://likispot.tistory.com/73

https://velog.io/@ohzzi/API%EC%9D%98-%EC%BF%BC%EB%A6%AC-%EA%B0%9C%EC%88%98-%EC%84%B8%EA%B8%B0-2-JDBC-Spring-AOP-Dynamic-Proxy%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%B9%B4%EC%9A%B4%ED%8C%85

์†๋‹ฅ์†๋‹ฅ ๊ธฐ์ˆ ๋ธ”๋กœ๊ทธ

[์†๋‹ฅ์†๋‹ฅ] ์šฐ๋‹นํƒ•ํƒ• ์ฟผ๋ฆฌ ์นด์šดํ„ฐ ๊ฐœ๋ฐœ๊ธฐ

profile
๐ŸŒฑ ํ•จ๊ป˜ ์ž๋ผ๋Š” ์ค‘์ž…๋‹ˆ๋‹ค ๐Ÿš€ rerub0831@gmail.com

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