본문 바로가기
Back-end/Spring Boot

[SpringBoot] 스프링부트 이해하기- 4편 Reflection(feat. 동적 로딩)

by whatamigonnabe 2022. 7. 16.

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'는 이 정보들을 런타임에 동적으로 조회하고 조작하는 기능을 하는 클래스입니다.

 

동적로딩

찾아보다 보니 이 기술을 조금 더 이해하기 위해서는 자바의 '동적 로딩'에 대해서 이해하야합니다. 우선 자바가 동작하는 원리를 살펴봅시다.

https://steady-snail.tistory.com/67

 

위의 그림처럼 우선 컴파일러가 .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 

https://sujl95.tistory.com/74

https://coneseo.tistory.com/32

https://velog.io/@agugu95/%EC%9E%90%EB%B0%94-%ED%81%B4%EB%9E%98%EC%8A%A4-%EB%A1%9C%EB%94%A9%EA%B3%BC-%EC%86%8D%EB%8F%84-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EA%B8%B0%EB%B2%95%EB%93%A4