이 시점부터는 Spring을 이해하기 위한 핵심 Java 내부 동작으로 들어가는 구간이다.
여기서부터는 단순히 문법을 아는 것이 아니라
"Spring은 왜 저렇게 만들어졌을까?"
를 이해하는 단계라고 생각하면 된다.
이번 글은 최대한 실제로 JVM 안에서 일어나는 일을 눈으로 그릴 수 있도록 설명하겠다.
Reflection 원리
"클래스 정보를 실행 중(Runtime)에 읽고, 객체를 생성하고, 메서드를 호출하고, 필드를 조작하는 기술"
Spring을 공부하다 보면 반드시 보는 단어가 있다.
Reflection
그리고 대부분 처음에는
"실행 중에 클래스 정보를 읽는 기술입니다."
라는 한 줄 설명만 보고 넘어간다.
하지만 이것만으로는 왜 Reflection이 필요한지 절대 이해되지 않는다.
Reflection이 필요한 이유
먼저 일반적인 Java 코드를 보자.
User user = new User();
user.login();
컴파일 시점에
User 클래스 존재
login() 메서드 존재
모든 것이 이미 결정되어 있다.
즉,
컴파일
↓
어떤 객체를 만들지 결정
↓
실행
순서이다.
그런데 Spring은 다르다.
Spring은 개발자가 만든 수백 개의 클래스를
@Component
@Service
@Repository
@Controller
이런 어노테이션만 보고
자동으로 객체를 만들어 준다.
그런데 Spring은 어떻게 알았을까?
프로젝트 안에
UserService
OrderService
MemberRepository
ProductController
...
수백 개가 있는데
누가 하나하나 new를 했을까?
아니다.
Spring이 실행 중에 찾아낸 것이다.
바로 Reflection으로.
Reflection이 없으면?
예를 들어
Application 시작
하면
new UserService()
new OrderService()
new ProductService()
new MemberRepository()
...
모든 클래스를 직접 작성해야 한다.
하지만 Spring은
프로젝트를 스캔
↓
@Service 발견
↓
객체 생성
↓
Bean 등록
을 자동으로 수행한다.
Reflection 덕분이다.
Reflection이 하는 일
Reflection은 클래스에 대한 모든 정보를 가져올 수 있다.
예를 들어
User.class
에는 이미 엄청난 정보가 들어있다.
클래스 이름
패키지
필드
메서드
생성자
상속 정보
인터페이스 정보
Annotation 정보
전부 가져올 수 있다.
Class 객체
Java에는 모든 클래스마다
Class 객체
가 하나씩 존재한다.
User.java
↓
컴파일
↓
User.class
↓
JVM 로딩
↓
Class<User> 객체 생성
즉,
클래스도 JVM 안에서는 하나의 객체이다.
Class 객체 얻기
방법은 여러 가지가 있다.
1. class
Class<User> clazz = User.class;
가장 많이 사용된다.
2. getClass()
User user = new User();
Class<?> clazz = user.getClass();
객체에서 가져온다.
3. Class.forName()
Class<?> clazz =
Class.forName("com.example.User");
문자열로도 가져올 수 있다.
Spring이 많이 사용하는 방식이다.
Reflection으로 객체 생성
일반적인 생성
User user = new User();
Reflection
Class<?> clazz = User.class;
Object obj =
clazz.getDeclaredConstructor()
.newInstance();
실행 결과
User 객체 생성
즉
new User()
를 코드에 적지 않아도
실행 중에 생성 가능하다.
메서드 찾기
public class User {
public void login() {
System.out.println("login");
}
}
Reflection
Method method =
User.class.getMethod("login");
그러면
login 메서드 정보
를 가져온다.
메서드 실행
User user = new User();
Method method =
User.class.getMethod("login");
method.invoke(user);
결과
login
여기서 핵심은
user.login();
을 직접 호출하지 않았다는 것이다.
Method 객체
↓
invoke()
↓
실제 메서드 실행
이다.
필드 접근
class User {
private String name = "Kim";
}
Reflection
Field field =
User.class.getDeclaredField("name");
필드 정보를 가져온다.
값 읽기
field.setAccessible(true);
String value =
(String) field.get(user);
결과
Kim
심지어 private도 접근 가능하다.
private
↓
setAccessible(true)
↓
접근 허용
Annotation도 읽을 수 있다
@Service
public class UserService {
}
Reflection
Class<?> clazz = UserService.class;
clazz.isAnnotationPresent(Service.class);
결과
true
Spring은 이것을 이용해서
@Service
↓
Bean 생성
을 수행한다.
Spring 내부에서는
Spring이 시작되면
Application Start
↓
패키지 스캔
↓
Class 객체 발견
↓
Annotation 확인
↓
@Service ?
↓
객체 생성
↓
BeanContainer 등록
↓
@Autowired 주입
이 모든 과정이 Reflection으로 이루어진다.
Reflection이 없다면 Spring은?
상상해 보자.
@Service
class UserService
@Repository
class UserRepository
가 있다.
Reflection이 없으면
Spring은
@Service가 붙어있는지 모른다.
객체 생성도 못한다.
@Autowired도 못한다.
메서드도 못 찾는다.
즉,
Spring DI
AOP
MVC
Validation
JPA
Transaction
거의 모든 기능이 동작하지 않는다.
Reflection의 단점
Reflection은 매우 강력하지만 비용이 있다.
일반 호출
user.login()
↓
컴파일 시 이미 결정
↓
바로 실행
Reflection 호출
Method 검색
↓
접근 권한 확인
↓
invoke()
↓
실제 호출
중간 과정이 많다.
그래서 일반 호출보다 느리다.
또한 타입 안정성이 떨어진다.
Method method =
User.class.getMethod("login");
여기서
login
을
loginn
이라고 적어도
컴파일 오류가 아니다.
실행할 때
NoSuchMethodException
이 발생한다.
실무에서는 성능이 문제가 되지 않을까?
Spring도 Reflection이 느리다는 사실을 알고 있다.
그래서
애플리케이션 시작 시
↓
Reflection으로 분석
↓
메타데이터 캐싱
↓
실행 중에는 캐시 사용
방식을 사용한다.
즉,
매 요청마다 Reflection을 수행하는 것이 아니라
한 번 분석한 결과를 계속 재사용한다.
그래서 일반적인 Spring 애플리케이션에서는 Reflection 때문에 성능 문제가 발생하는 경우는 거의 없다.
면접 포인트
Reflection이란?
실행 중(Runtime)에 클래스 정보를 조회하고 객체 생성, 메서드 호출, 필드 접근, Annotation 분석 등을 수행할 수 있는 Java의 기능이다.
Spring에서 Reflection을 사용하는 이유
- @Component 계열 Annotation 탐색
- Bean 자동 생성 및 DI
- @Autowired 주입
- AOP 프록시 생성
- MVC 요청 메서드 매핑
- Validation 및 다양한 프레임워크 기능 구현
핵심 한 줄
Reflection은 "컴파일 시점에 알지 못하는 클래스도 실행 중에 조사하고 조작할 수 있게 해주는 Java의 메타프로그래밍 기술"이며, Spring이 개발자의 클래스를 자동으로 발견하고 관리할 수 있게 만드는 가장 중요한 기반 기술이다.
다음 글은 Reflection을 이해했다면 반드시 이어서 알아야 하는 **Class 객체란 무엇인가?**이다.
'language > java' 카테고리의 다른 글
| Annotation 동작 원리 (0) | 2026.06.16 |
|---|---|
| Class 객체란? (0) | 2026.06.16 |
| 번외편 - Java Comparator vs JavaScript sort() (0) | 2026.06.04 |
| 번외편) groupingBy()와 toMap()은 내부 아이디어가 매우 비슷하다. (0) | 2026.06.01 |
| 번외편) Typescript와 Generic 비교 (0) | 2026.06.01 |
댓글