🥭 개요
우리는 JDBC와 통신에 대해서 많이 알아봤지만, 실제로는 여러 기업체들과 같이 일하게 될 수가 있다. 하지만 우리가 이전까지 공부한 내용으로는 다음과 같은 상황이 발생할 수 있다.
위 그림과 같이 프로그램을 납품을 받는 업체에서 기업의 상황에 따라 유연하게 메소드를 추가하는 상황이 생길 수 있고, 납품한 개발자 쪽에서는 그런 업데이트가 있을 때마다 업데이트 된 메소드를 적용하는 컨트롤러를 수정해야된다.
한 두번이면 귀찮음을 무릎쓰고 작업할 수 있겠지만, 만약 거래하는 업체가 100개가 된다면, 그 전화를 상대하는 것만해도 엄청난 유지보수비용이 들어가게된다. 그럴때 그 유지보수 비용을 생각한다면 연간 5000만원 정도는 아낄 수 있는 기술이 바로 리플렉션과 어노테이션 기능이라고 할 수 있다. 그럼 바로 들어가보자.
🙆 리플렉션이 필요한 상황이란??
리플렉션이 활용될 수 있는 은행 앱 예시
위 와같은 은행 어플리케이션 구조에서는 라우터에서 리플렉션 구현이 필요하다. 보통 개발업체가 일을 하게 될 때 보통 데이터베이스를 가지는 업체에서 서비스 앱 개발을 의뢰 하는 경우가 많다.
이러한 상황에서 고객들과 접촉하는 서비스를 개발하는 업체와 데이터베이스를 가진 업체의 협업이 필요하게 된다. 하지만 여기에 리플렉션의 개념이 없다면 어떻게 상호작용 하게 될까???
위 그림과 같이 납품업체는 구매업체에서 어떤 메소드를 업데이트가 되었는 지 알지 못하고, 매번 찾아서 그 메서드를 사용할 수 있는 서비스 앱을 수정해야 될것이다. 이게 한 두회사만 그런경우면 할만 하겠으나, 상대 없체가 50개 100개 정도 된다면 어떤가? 적어도 인력이 1년에 3000~5000만원 비용이 드는 단순 작업이 된다. 그리고 그 와중에 실수할 확률도 높다.
하지만 리플렉션과 어노테이션기능으로 이 엄청난 비용을 절약할 수가 있다.
🧐 그렇다면… 리플렉션이란??
리플렉션(Reflection)
은 자바에서 클래스, 인터페이스, 필드, 메소드 등에 대한 정보를 런타임에 동적으로 접근하고 조작할 수 있는 강력한 기능이다. 이를 사용하면 프로그램이 자신의 구조에 대한 정보를 직접 조사하고, 실행 중에 객체의 클래스를 알아내거나, 메소드와 필드에 접근하고 메소드를 호출하는 것이 가능하다. 이를 각 클래스의 메타데이터를 읽어온다라고 표현한다. 도로 양옆으로 메소드라는 나무들이 서있다고 상상해보자. 여기서 메타데이터를 가져온다는 것은 클래스에 구현된 모든 코드, 또는 메소드 내부에 구현된 모든 코드를 확인하는 것이 아니라, 특정 정보인 메타데이터를 조회할 수 있다는 의미이다. 자바의 리플렉션 API를 사용하여
Class
객체에서 다음과 같은 정보 (메타데이터)를 가져올 수 있다.- 클래스 이름
- 메소드 정보
- 필드 정보
- 생성자 정보
- 상위 클래스 및 인터페이스 정보
- 어노테이션 정보
예시 코드 구현
public class Dispatcher { private BankController con; public Dispatcher(BankController con) { this.con = con; } public void route(String path) { Method[] methods = con.getClass().getDeclaredMethods(); for (Method method : methods) { if (method.getName().equals(pathToMethodName(path))) { try { method.invoke(con); break; } catch (Exception e) { e.printStackTrace(); } } } } private String pathToMethodName(String path) { // 예시: URI 경로 "/account"는 "accountMethod" 메소드에 매핑됨 // 실제 매핑 로직은 여기에 구현 // 예를 들어, 경로를 메소드 이름 형식으로 변환하는 로직 return path.substring(1) + "Method"; } }
1️⃣ 위의 코드에서 리플렉션을 구현할 수 있게 핵심 코드는 아래와 같다.
Method[]
는java.lang.reflect
에 선언된 메소드를 저장할 수 있는 객체 배열이다.
.getClass()
는 클래스의 메타데이터를 조회한다.
.getDeclaredMethods()
에서 조회된 클래스에서 선언된 모든 메소드를 불러온다.
- 결국
con
변수 객체에서 조회된 모든 메소드가methods
배열에 저장이 된다.
2️⃣ 아래 코드에서 리플렉션의 구현이 완성이 된다.
for each
구문에서 배열에 담긴 메소드 배열을 순회한다.
method.getName()
이 검색중인(pathToMethodName(path))
요청값이랑 비교한다.
- 비교값이 메소드이름과 일치하면
method.invoke(con)
으로 해당 메소드를 실행한다.
따라서, 리플렉션이란 아래 모습과 같이 전체 메타데이터를 검색하여서 조건에 맞는 메소드를 찾게 된다.
하지만 위와 같은 방식으로는 대량의 대이터를 작업할 시에 컴퓨터 리소스를 많이 사용하게 되고, 속도역시 느려질 수 밖에 없으므로 상황에 맞게 사용해야한다. 하지만 이와 같은 단점을 보완해주는 것이 어노테이션의 활용이다.
💡 함께하면 강력한 어노테이션의 활용!!
리플렉션의 강력한 기능에도 불구하고, 위에 그 단점을 언급하였다. 그것은 리플렉션은 일일이 하나씩 다 찾아야 된다라는 것.. 😢
위 그림처럼 업데이트된 몇 개의 정보만 수정하면 되는 데 일일이 다 확인해야되는 상황이라면 컴퓨터 자원의 낭비라고 볼 수 있고, 그리고 속도 역시 느리다. 그래서 리플렉션은 상황을 잘 판단해서 사용하는 것이 중요했다.
하지만
RequestMapping
어노테이션을 활용하면 필요한 메소드에 어노테이션이라는 깃발을 꽂아서 그 메소드만 따로 호출하는 것이 가능하다.😍 어노테이션 활용 문법
리플렉션으로 전체를 순회 하지 않고 깃발을 꽂은, 즉 어노테이션 처리된 메소드만 순회하기 때문에 리플렉션의 장점과 효율성을 모두 갖추게 되었다. 이 기능을 활용하는 기업과 그렇지 않은 기업은 유지보수 측면에서 굉장한 비용의 차이가 발생할 수 도있다.
RequestMapping 어노테이션 직접 만들기
// RequestMapping 어노테이션 정의하는 문법 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface RequestMapping { String uri (); }
@Retention(RetentionPolicy.RUNTIME)
은 해당 어노테이션이 런타임에도 사라지지 않고 유지되게 해주며, 리플렉션이 이를 활용할 수 있게된다.
@Target(ElementType.METHOD)
이 어노테이션을 이용해서 전용타입을 결정한다. 여기서 메소드 타입을 설정하면, 메소드 전용 어노테이션이 된다. 이렇게 메소드로 설정이 되어있다면 해당 어노테이션은 메소드 위에 붙여야된다.
String URI();
이것은 RequestMapping어노테이션의 속성값이 된다. 따라서 어노테이션이 사용될때 속성값역시 받아올 수 있다.
RequestMapping 어노테이션 사용하기
package controller public class BankController { @RequestMapping(uri = "/insert") public void insert(){ System.out.println("controller : insert"); dao.insert("1234", 1000); }
- 직접 만든 어노테이션을 사용하기 위해서는 같은 패키지 안에 존재해야된다.
@RequestMapping (uri = “/insert”)
해당 어노테이션을 필요한 메소드 위에 붙여준다. 아까 정의된 대로 런타임에도 사라지지 않고 유지되므로 리플렉션에서 식별이 가능하다.
uri
에 정의된대로/insert
입력값을 받으면 내부 코드가 실행이 된다.
이 리플렉션과 어노테이션의 조합으로 앞으로 서비스 구매업체에서 수정해달라는 전화를 받으며 밤샐 필요가 없다. 이제는 이 한마디면 된다.
dkssud
Share article