본문 바로가기
language/java

Reflection + Annotation + Proxy + ThreadLocal

by 죄니안죄니 2026. 6. 16.
반응형

이 글은 지금까지의 내용을 하나의 이야기로 연결하는 글이다.

실제로 이 글을 이해하면 Spring을 단순히 사용하는 것이 아니라

"Spring이 내부에서 어떻게 움직이는지"

를 머릿속으로 그릴 수 있게 된다.


Reflection + Annotation + Proxy + ThreadLocal

Spring은 이 네 가지 기술로 움직인다.

Spring은 Reflection으로 클래스를 찾고,
Annotation으로 의미를 판단하고,
Proxy로 기능을 추가하며,
ThreadLocal로 요청 상태를 유지한다.


먼저 질문 하나

아래 코드를 보자.

 
@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/users")
    public void save() {

        userService.save();

    }

}
 

그리고

 
@Service
public class UserService {

    @Transactional
    public void save() {

        ...

    }

}
 

우리는 그냥

POST /users

↓

DB 저장
 

이라고 생각한다.


하지만 Spring 내부에서는 엄청난 일이 일어난다.


전체 흐름

Application Start

↓

Reflection

↓

Annotation 분석

↓

Bean 생성

↓

Proxy 생성

↓

BeanContainer 저장

========================

HTTP 요청

↓

Proxy 실행

↓

Transaction 시작

↓

ThreadLocal 저장

↓

실제 Service 실행

↓

Repository

↓

ThreadLocal에서 Connection 조회

↓

Commit

↓

ThreadLocal 정리
 

이 모든 과정이

지금까지 공부한

Reflection

Annotation

Proxy

ThreadLocal
 

네 가지 기술만으로 이루어진다.


1단계 - Reflection이 시작한다.

Spring Boot가 실행된다.

Application Start
 

Spring은

프로젝트 전체를 스캔한다.

com.example

↓

UserController.class

UserService.class

UserRepository.class
 

Spring은 Reflection으로

Class 객체를 얻는다.

 
Class<?> clazz = UserService.class;
 

그리고

필드

메서드

생성자

Annotation
 

모든 정보를 읽는다.


즉,

Reflection은

"프로젝트를 조사하는 눈"

이다.


2단계 - Annotation이 역할을 결정한다.

Reflection으로

Class를 찾았다.

이제 Spring은 묻는다.

@Service인가?

@Controller인가?

@Repository인가?
 

Reflection

 
clazz.isAnnotationPresent(Service.class);
 

결과

true
 

그러면

Bean 생성 대상
 

이라고 판단한다.


여기서 Annotation은

"Spring에게 힌트를 주는 표지판"

역할을 한다.


3단계 - Reflection으로 객체 생성

Spring은

 
new UserService()
 

를 코드에 적지 않는다.

Reflection을 사용한다.

 
clazz.getDeclaredConstructor()

     .newInstance();
 

즉,

Reflection

↓

객체 생성

↓

Bean 생성
 

이다.


그리고

BeanContainer에 저장한다.

BeanContainer

---------------------

UserService

UserRepository

UserController

---------------------
 

4단계 - @Autowired도 Reflection이다.

 
@Autowired

private UserService userService;
 

Spring은

Reflection으로

필드를 찾는다.

 
Field field = ...

field.setAccessible(true);

field.set(controller, userService);
 

즉,

@Autowired

↓

Reflection

↓

Field 접근

↓

객체 주입
 

이다.


5단계 - 그런데 @Transactional은?

 
@Transactional

public void save() {

}
 

많은 사람들이

@Transactional

↓

트랜잭션 시작
 

이라고 생각한다.


실제로는 아니다.


Spring은 Annotation을 발견한다.

Reflection

↓

@Transactional 발견
 

그리고

생각한다.

이 Bean은 특별하다.

Proxy를 만들어야겠다.
 

6단계 - Proxy 생성

기존

Client

↓

UserService
 

Spring

Client

↓

Transaction Proxy

↓

UserService
 

BeanContainer에는

실제 UserService가 아니라

Proxy가 들어간다.


그래서

 
@Autowired

UserService userService;
 

가 받아오는 것은

실제로

Transaction Proxy
 

이다.


7단계 - HTTP 요청 발생

POST /users

↓

DispatcherServlet

↓

Controller

↓

userService.save()
 

우리는

UserService가 호출된다고 생각한다.


실제로는

userService

↓

Transaction Proxy

↓

invoke()
 

이다.


8단계 - Proxy가 Transaction 시작

Proxy는

beginTransaction()

↓

real.save()

↓

commit()
 

를 수행한다.


그런데

Connection을 어디에 저장할까?


9단계 - ThreadLocal 등장

Proxy

Connection 생성

ThreadLocal 저장

Thread-1

↓

ThreadLocalMap

↓

Connection
 

그러면

Service

Repository

DAO

모두

같은 Connection을 사용할 수 있다.


Repository에서는

 
connection =
DataSourceUtils.getConnection();
 

실제로는

Thread.currentThread()

↓

ThreadLocalMap

↓

Connection 반환
 

이다.


그래서

Controller가

Connection을

전달하지 않아도 된다.


Spring Security도 동일하다.

로그인

Authentication 생성

ThreadLocal 저장


Controller

 
SecurityContextHolder

.getContext()

.getAuthentication()
 

실제로는

현재 Thread

↓

ThreadLocal

↓

Authentication
 

을 가져오는 것이다.


그림으로 보면

                   Spring Boot

                        │

                        ▼

                Reflection

                        │

        ┌───────────────┼───────────────┐

        ▼                               ▼

 Annotation 분석                 Reflection 객체 생성

        │                               │

        └───────────────┬───────────────┘

                        ▼

                 BeanContainer 등록

                        │

                        ▼

        @Transactional 발견

                        │

                        ▼

                 Proxy 생성

                        │

================================================

HTTP Request

                        │

                        ▼

                 Proxy.invoke()

                        │

        ┌───────────────┴───────────────┐

        ▼                               ▼

Transaction 시작              ThreadLocal 저장

        │                               │

        └───────────────┬───────────────┘

                        ▼

                 Real Service 실행

                        │

                        ▼

                  Repository

                        │

                        ▼

ThreadLocal에서 Connection 조회

                        │

                        ▼

                  Commit / Rollback

                        │

                        ▼

              ThreadLocal.remove()
 

한 줄로 기억하는 방법

Reflection

무엇이 있는지 찾는다.

Class

Field

Method

Annotation
 

Annotation

무엇을 해야 하는지 알려준다.

@Service

@Transactional

@Autowired
 

Proxy

실제 메서드 앞뒤에 기능을 추가한다.

Transaction

AOP

Cache

Async
 

ThreadLocal

현재 요청의 상태를 안전하게 보관한다.

Connection

Authentication

SqlSession

RequestContext
 

면접에서 가장 좋아하는 질문

Spring이 @Transactional을 어떻게 구현하는지 설명해 보세요.

 

이제는 이렇게 답할 수 있다.

Spring은 애플리케이션 시작 시 Reflection으로 클래스를 스캔하고 @Transactional Annotation을 발견하면 Proxy 객체를 생성한다. 실제 메서드 호출은 Proxy를 통해 이루어지며, Proxy는 트랜잭션을 시작한 후 Connection을 ThreadLocal에 저장한다. 이후 Repository 계층은 ThreadLocal에서 동일한 Connection을 조회하여 하나의 트랜잭션으로 동작하며, 작업이 끝나면 Commit 또는 Rollback을 수행하고 마지막에 ThreadLocal을 정리한다.


지금까지의 로드맵

Reflection
    ↓
Class 객체
    ↓
Annotation
    ↓
Meta Annotation
    ↓
Dynamic Proxy
    ↓
JDK Proxy vs CGLIB
    ↓
Serialization / Deserialization
    ↓
AutoBoxing / Unboxing
    ↓
Integer Cache
    ↓
StringBuilder vs StringBuffer
    ↓
BigDecimal
    ↓
ThreadLocal
    ↓
ThreadLocal Memory Leak
    ↓
★★★★ Reflection + Annotation + Proxy + ThreadLocal ★★★★
 

이 글은 단순한 개념 설명이 아니라 Spring 내부 동작을 관통하는 연결점이므로, 이번 챕터를 마무리하는 핵심 글이다.

 
반응형

'language > java' 카테고리의 다른 글

Text Block(Java 15)  (0) 2026.06.17
var 키워드  (0) 2026.06.17
ThreadLocal Memory Leak  (0) 2026.06.16
ThreadLocal 원리  (0) 2026.06.16
BigDecimal을 사용하는 이유  (0) 2026.06.16

댓글