Reflection
앞선 포스팅에서 스프링/스프링부트의 DispatcherServlet에 대해 알아보았습니다.. 짧게 요약하자면, DispatcherServlet은 클라이언트의 요청을 받아서 적절한 컨트롤러(또는 핸들러)를 찾아 동적으로 실행시킨 후 결과를 받아 Http형식에 맞게 리턴하는 기능을 합니다. 이때, '적절한 컨트롤러를 찾아 동적으로 실행'시키는 부분에서 '리플렉션(Reflection)'이라는 개념이 들어갑니다.
Reflection이란?
오라클 홈페이지에서는 리플렉션을 아래와 같이 소개하고 있습니다.
Reflection is a feature in the Java programming language. It allows an executing Java program to examine or "introspect" upon itself, and manipulate internal properties of the program. For example, it's possible for a Java class to obtain the names of all its members and display them. - 오라클 홈페이지
리플렉션은 '자신의 내부를 조회하고 조작하는 것'인 것 같습니다. 조금 더 자세히 알아보겠습니다.
모든 자바 파일(.java)는 클래스 파일(.class)로 컴파일 될 때 JVM에 의해 자동으로 Class 클래스를 생성하여 같은 .class파일에 저장됩니다. 여기서 Class 클래스는 해당 class의 이름, 필드의 이름과 타입, 메서드의 함수시그니쳐 등의 정보를 담고 있습니다. 그리고 이 정보들은 클래스 로더에 의해 동적 로딩될 때, Static 영역(Method 영역)에 로드됩니다. 따라서, 클래스의 이름만 알고 있다면 클래스에 대한 정보를 언제든 찾아볼 수가 있죠.
그리고 리플렉션은 런티임 시에 이런 정보들을 조회/조작하는 기술입니다. 또 Class 클래스 또한 클래스로더에 의해 동적로딩되어 메모리에 올라감으로, 찾고자하는 클래스가 로드 되어있지 않는다면 이때 클래스를 찾아서 로드시킵니다. 그리고 'java.lang.reflect'는 이 정보들을 런타임에 동적으로 조회하고 조작하는 기능을 하는 클래스입니다.
동적로딩
찾아보다 보니 이 기술을 조금 더 이해하기 위해서는 자바의 '동적 로딩'에 대해서 이해하야합니다. 우선 자바가 동작하는 원리를 살펴봅시다.

위의 그림처럼 우선 컴파일러가 .java 소스 파일을 .class파일로 번역합니다. 자바는 여기서 바로 모든 파일들을 바로 메모리에 로드하지 않고, 필요한 순간에 로드합니다. 이것이 바로 '동적 로딩'입니다. 동적로딩을 하는 이유는 메모리를 효율적으로 관리할 수 있을 뿐 아니라, 사용자의 요청에 더 유연하게 대처할 수 있습니다.
로드타임 정적로딩
조금 더 구체적으로 보면, 자바의 동적로딩은 로드타임 동적로딩과 런타임 동적로딩이 있는데, '런타임 동적로딩'기술을 이용한 것입니다. 우선 로드 타임 동적 로딩이란, 실행엔진(Execution Engine)이 한 줄씩 실행시키다가 처음 보는 클래스를 만나면 클래스로더에게 클래스를 로딩하라고 요청을 하는데요. 이때 해당 클래스를 훑어 보다가 클래스에 쓰인 다른 클래스가 있으면 원래 클래스 로딩 중에 필요한 클래스를 또 찾아서 로딩을 한 후 원래의 클래스 로딩을 마칩니다. 이렇게 클래스가 처음 호출될 때 같이 로드되는 것이 '로드타임 정적 로딩'입니다.
//main 메서드
Exam exam = new Exam(); //Exam클래스 발견 -> class load에게 Exam 클래스 로드 요청
...
//Exam.java
class Exam {
private Subject subject; // 또 첨보는 클래스 발견! Subject클래스부터 로드
public Exam(Subject subject) {
this.subject = subject;
}
}
런타임 동적로딩
런타임 동적로딩은 어떤 클래스를 로드할 때, 처음보는 클래스를 만나더라도 일단은 비워두고 로드를 한 후, 실제로 해당 코드가 실행될 때 로드하는 기술입니다. 그리고 가장 대표적으로 런타임 동적로딩을 사용하는 메서드가 Class.forName()입니다. 먼저 아래 코드를 보시죠.
//메인 메서드
public static void main(String[] args) throws 예외처리 생략{
Exam exam = new Exam(); //1. 클래스로더에게 클래스 로드 요청
exam.setSubject("superClazz.Math"); //3.이때 Math클래스를 로드 (=런타임 동적로딩)
}
//Exam 클래스
public class Exam {
private Subject subject;
public void setSubject(String subjectName) throws 예외처리 생략{
subject = (Subject) Class.forName(subjectName)
.getDeclaredConstructor().newInstance(); //2 .어떤 클래스가 쓰일지 모르니 빈칸으로 두고 Exam 클래스 로드
System.out.println(subject);
}
}
//Subject 인터페이스와 Math클래스
interface Subject {}
class Math implements Subject {}
위의 코드처럼 코드가 실제로 실행될 때(런타임) 클래스를 로드 하는 것을 런타임 동적로딩이라고 하며, 이렇게 클래스를 작성할 때는 어떤 클래스가 쓰일지 모르다가, 실제로 실행되는 순간에 알아내고 조작하는 것이 리플렉션입니다. 또한 바로 이 부분이 맨 앞에 언급한 디스패쳐 서블릿의 동작 원리 중 하나입니다. 아래 그림처럼 만약 리플렉션을 사용하지 않았다면, 많은 if구문이나 switch문을 사용해야하고 컨트롤러가 추가 될 때마다 코드를 수정해야하지만, 리플렉션을 사용하면 더 간편하고 유연하게 사용할 수 있습니다.

여기까지 읽어주셔서 감사합니다. 뭔가 조금씩 스프링에 가까워지는 느낌이 듭니다ㅎㅎㅎㅎ!
참고한 자료
https://steady-snail.tistory.com/67
https://baeldung.com/java-reflection
'Back-end > Spring Boot' 카테고리의 다른 글
| [SpringBoot] 스프링부트 이해하기 - 6편 리플렉션과 어노테이션으로 디스패처 필터 만들어보기 (0) | 2022.07.21 |
|---|---|
| [SpringBoot] 스프링부트 이해하기 - 5편 Annotation (0) | 2022.07.16 |
| [SpringBoot] 스프링부트 이해하기- 3편 IoC와 DI 쉬운 버전 (0) | 2022.07.14 |
| [SpringBoot] 스프링부트 이해하기- 2편 Dispatcher Servlet, FrontController패턴 (0) | 2022.07.14 |
| [SpringBoot] 스프링부트 이해하기- 1편 Servlet이란(feat. Web Container) (0) | 2022.07.12 |