[Spring Boot] 8. 리플렉션과 어노테이션

김건우's avatar
Mar 25, 2025
[Spring Boot] 8. 리플렉션과 어노테이션
실습 1
실습3
실습4

리플렉션

💡
: 내가 직접 뭔가를 만들지 않고, 이미 존재하는 걸 분석하고 조작하는 기술
📌 상황
네가 마법사라고 상상해보자
마법사들은 보통 주문(Spell)을 외워야 마법을 쓸 수 있다
그런데 리플렉션을 사용할 수 있는 마법사는 주문을 외우지 않고도 마법을 사용할 수 있다
💡 일반 마법사 (리플렉션 X)
  • 마법사가 "파이어볼" 마법을 사용하려면, 반드시 "파이어볼"을 직접 외워야 한다
  • 하지만 "번개 마법"을 쓰려면? 새로운 주문을 배워야 한다
  • 즉, 컴파일 타임(코드를 작성할 때) 모든 주문을 알고 있어야 함
💡 리플렉션을 쓰는 마법사
  • 마법책(클래스 정보)을 보면 어떤 주문이 있는지 알 수 있다
  • 심지어 주문을 외우지 않고 책에서 찾아서 바로 실행할 수도 있다
  • 즉, 런타임(프로그램 실행 중)에 새로운 주문도 사용할 수 있다
🎯 👉 결론 :
  • 직접 코드를 작성하지 않아도, 실행 중에 필요한 정보를 찾아서 실행하는 기술

리플렉션의 단점

마법사가 마법책을 보고 주문을 찾는데, 어느 날 마법책 내용이 바뀌어 버리면?
예전에는 '파이어볼'이 3페이지에 있었는데, 이제는 7페이지로 바뀜
💡일반 마법사 (리플렉션 X) → 문제 없음
  • "파이어볼은 내가 외우고 있으니까 상관없어"
💡 리플렉션 마법사 → 혼란스러움
  1. "파이어볼이 어디 갔지? 어제는 3페이지였는데..."
  1. 마법이 실행되지 않거나, 엉뚱한 주문이 실행됨
실제 리플렉션에서도 마찬가지
  • 프로그램 실행 중에 클래스나 메서드 이름이 바뀌면, 리플렉션이 제대로 동작하지 않음
  • 코드가 변경될 때마다 리플렉션을 사용하는 부분도 수정해야 해서 유지보수가 어려움

어노테이션

💡
: 기본적인 정보 위에 추가적인 정보를 적어두는 개념
마법책에는 수많은 주문이 있다.
하지만 그냥 주문만 적혀 있으면 마법사가 이걸 언제, 어떻게 사용해야 할지 헷갈릴 수도 있다.
그래서 마법책에는 주석(Annotation)이 적혀 있음

💡 마법책 예제

🔥 주문: 파이어볼 (Fireball)
@위험
@초급자 사용 금지
@마나 50 이상 필요
설명: 강력한 화염구를 발사한다.
사용법: 적을 향해 손을 뻗고 "파이어볼!" 외치기
위처럼 주문 위에 추가적인 정보를 적어두는 게 바로 어노테이션의 개념
마법사는 이 주석을 보고 "아, 이 주문은 위험하고 초급자는 사용하면 안 되네!" 라고 알 수 있다.
👉 실제 코드에서 어노테이션은 이런 역할
  • 개발자가 추가적인 정보(메타데이터)를 제공할 수 있음
  • 어노테이션을 보고 프로그램이 자동으로 특정 작업을 수행할 수도 있음
마법책 예제
실제 코드 어노테이션
@위험
@Deprecated (더 이상 사용하지 말라는 의미)
@초급자 사용 금지
@Override (부모 클래스를 덮어쓸 때 사용)
@마나 50 이상 필요
@Transactional (트랜잭션 관리)
@자동 시전 가능
@Autowired (자동 의존성 주입)

어노테이션 코드 구현

1. 어노테이션 선언

package ex02; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) // 메서드가 오류 발생 시 알려줌 @Target(ElementType.METHOD) // METHOD 메서드 위에만 붙힐 수 있다. (Class 위에 붙히려면 type을 적어야함) public @interface RequsetMapping { String value(); }
@Retention(RetentionPolicy.RUNTIME)
  • Retention은 어노테이션이 어떤 시점까지 존재할지를 설정합니다.
  • RUNTIME은 실행 시점에 어노테이션이 유지되도록 설정합니다.
@Target(ElementType.METHOD)
  • Target은 어노테이션을 어떤 요소에 적용할지 정의합니다.

2. 클래스 선언

package ex02; import java.lang.reflect.Method; public class Dispatcher { UserController con; public Dispatcher(UserController con) { this.con = con; } public void routing(String path) { // /login Method[] methods = con.getClass().getMethods(); for (Method method : methods) { RequsetMapping rm = method.getAnnotation(RequsetMapping.class); if (rm == null) continue; // 다음 for문으로 바로 넘어감 if (rm.value().equals(path)) { try { method.invoke(con); } catch (Exception e) { throw new RuntimeException(e); } } } } }
Method[] methods = con.getClass().getMethods();
  • con.getClass().getMethods()는 UserController 클래스의 모든 메서드를 반복문을 통해 순차적으로 탐색할 수 있게 반환합니다.
  • getMethods()는 public 메서드만 반환합니다.

3. HTTP 요청을 처리하는 메서드들을 정의한 클래스

package ex02; public class UserController { @RequsetMapping("/login") public void login() { System.out.println("login call"); } @RequsetMapping("/join") public void join() { System.out.println("join call"); } @RequsetMapping("/logout") public void logout() { System.out.println("logout call"); } @RequsetMapping("/userinfo") public void userinfo() { System.out.println("userinfo call"); } }
@RequsetMapping
  • 어노테이션을 통해 특정 경로와 연결됩니다.
  • Dispatcher 클래스에서 해당 URL로 요청이 들어왔을 때 적절한 메서드를 호출하게 됩니다.

4. 메인 메서드 실행

package ex02; public class App { public static void main(String[] args) { Dispatcher ds = new Dispatcher(new UserController()); ds.routing("/userinfo"); } }
notion image
 
Share article

gunwoo