✅ 리플렉션(Reflection)의 정의
실행 중(Runtime)에 클래스, 메서드, 필드 등의 정보를 동적으로 조회하거나 수정/호출할 수 있게 해주는 기능
🔍 핵심 키워드로 설명하면
요소 | 설명 |
동적(dynamic) | 컴파일 타임이 아닌 실행 시간에 처리 |
조회(inspect) | 클래스의 구조를 조사할 수 있음 (예: 필드 목록, 메서드 목록 등) |
제어(manipulate) | 필드 값을 수정하거나, 메서드를 호출하거나, 객체를 생성할 수 있음 |
🧠 대표적인 클래스
리플렉션은
java.lang.reflect
패키지와 Class
클래스를 중심으로 동작Class<?> cls = SomeClass.class;
cls.getDeclaredFields()
cls.getDeclaredMethods()
method.invoke(...)
field.setAccessible(true); field.set(obj, value);
✅ 예시
Class<?> clazz = Class.forName("com.example.User");
Object user = clazz.getDeclaredConstructor().newInstance();
Method method = clazz.getMethod("setName", String.class);
method.invoke(user, "홍길동");
→ 클래스 이름만 알고 있어도, 해당 클래스의 인스턴스를 만들고 메서드를 호출할 수 있다
⚠️ 리플렉션의 단점
- 성능 저하: 일반 호출보다 느리다
- 컴파일 타임 안정성↓: 문법 오류를 컴파일 시점에 못 잡는다
- 보안 제약: JVM 보안 매니저에 따라 접근이 제한될 수 있다
📝 한줄 정리
자바 리플렉션은 실행 중에 클래스 정보를 읽고 조작할 수 있게 해주는 기능으로, 프레임워크나 라이브러리 내부에서 매우 자주 사용됨
실습
메서드명 & 메서드의 변수와 타입 확인
UserController
package com.metacoding.ex02;
// B 개발자가 만든 것
public class UserController {
public void login(int n1, String s1) {
System.out.println("login 호출 됨");
}
public void join() {
System.out.println("join 호출 됨");
}
public void logout() {
System.out.println("logout 호출 됨");
}
}
App
package com.metacoding.ex02;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
// A 개발자가 만든 것
public class App {
public static void main(String[] args) {
String path = "/login";
Method[] methods = UserController.class.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName());
if (method.getName().equals("login")) {
int paramCount = method.getParameterCount();
System.out.println(paramCount);
Parameter[] params = method.getParameters();
for (Parameter param : params) {
System.out.println(param.getName());
System.out.println(param.getType());
}
}
}
}
}
⬇ 실행 결과

예제
RequestMapping
- 직접 어노테이션 생성
package com.metacoding.ex02;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 언제까지 유지(보존)될지를 지정하는 메타 어노테이션, RetentionPolicy -> 언제까지 유지(보존)될지를 지정하는 데 사용하는 열거형(enum)
@Retention(RetentionPolicy.RUNTIME)
// 메서드 위에만 작성 가능한 어노테이션, ElementType -> 어노테이션의 적용 대상을 지정할 때 사용하는 열거형(enum)
@Target(ElementType.METHOD)
public @interface RequestMapping {
String value(); // value: 기본 키워드-> 만약 매개변수가 하나면 value를 생략할 수 있다
}
UserController
package com.metacoding.ex02;
// B 개발자가 만든 것
public class UserController {
@RequestMapping("/login")
public void login() {
System.out.println("login 호출 됨");
}
@RequestMapping("/join")
public void join() {
System.out.println("join 호출 됨");
}
@RequestMapping("/logout")
public void logout() {
System.out.println("logout 호출 됨");
}
}
App
package com.metacoding.ex02;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Scanner;
// A 개발자가 만든 것
public class App {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String path = sc.nextLine();
Method[] methods = UserController.class.getDeclaredMethods();
UserController uc = new UserController();
for (Method method : methods) {
Annotation anno = method.getDeclaredAnnotation(RequestMapping.class);
RequestMapping rm = (RequestMapping) anno;
if (rm.value().equals(path)) {
try {
method.invoke(uc);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
}
}
→ UserController에 메서드가 추가되더라도, 추가된 메서드에 @RequestMapping 어노테이션만 붙어 있으면 메서드 별로 코드를 작성하지 않아도 호출할 수 있다
-> 깃발(어노테이션) 사용법만 알면 된다!
문제
- "/login" 호출할때 Model 객체 있으면 instance 넣어서 username값 출력
- "userinfo" 호출할 때 @Principal, SessionUser 객체 있으면 instance 넣어서 id와 username 출력
SessionUser
package com.metacoding.ex03;
public class SessionUser {
private int id;
private String username;
private static SessionUser instance = new SessionUser();
private SessionUser() {
this.id = 1;
this.username = "ssar";
}
public static SessionUser getInstance() {
return instance;
}
public int getId() {
return id;
}
public String getUsername() {
return username;
}
}
Model
package com.metacoding.ex03;
import java.util.HashMap;
import java.util.Map;
public class Model {
private static Model instance = new Model();
private Model() {
attributes.put("username", "ssar");
}
public static Model getInstance() {
return instance;
}
private Map<String, Object> attributes = new HashMap<>();
public void addAttribute(String name, Object value) {
attributes.put(name, value);
}
public Object getAttribute(String name) {
return attributes.get(name);
}
}
Principal
package com.metacoding.ex03;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 언제까지 유지(보존)될지를 지정하는 메타 어노테이션, RetentionPolicy -> 언제까지 유지(보존)될지를 지정하는 데 사용하는 열거형(enum)
@Retention(RetentionPolicy.RUNTIME)
// 메서드 위에만 작성 가능한 어노테이션, ElementType -> 어노테이션의 적용 대상을 지정할 때 사용하는 열거형(enum)
@Target(ElementType.PARAMETER)
public @interface Principal {
}
RequestMapping
package com.metacoding.ex03;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 언제까지 유지(보존)될지를 지정하는 메타 어노테이션, RetentionPolicy -> 언제까지 유지(보존)될지를 지정하는 데 사용하는 열거형(enum)
@Retention(RetentionPolicy.RUNTIME)
// 메서드 위에만 작성 가능한 어노테이션, ElementType -> 어노테이션의 적용 대상을 지정할 때 사용하는 열거형(enum)
@Target(ElementType.METHOD)
public @interface RequestMapping {
String value(); // value: 기본 키워드-> 만약 매개변수가 하나면 value를 생략할 수 있다
}
UserController
package com.metacoding.ex03;
// B 개발자가 만든 것
public class UserController {
@RequestMapping("/userinfo")
public void userinfo(@Principal SessionUser sessionUser) {
System.out.println(sessionUser.getId());
System.out.println(sessionUser.getUsername());
System.out.println("userinfo 호출됨");
}
@RequestMapping("/login")
public void login(Model model) {
System.out.println(model.getAttribute("username"));
System.out.println("login 호출 됨");
}
@RequestMapping("/join")
public void join() {
System.out.println("join 호출 됨");
}
@RequestMapping("/logout")
public void logout() {
System.out.println("logout 호출 됨");
}
}
App
package com.metacoding.ex03;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Scanner;
// A 개발자가 만든 것
public class App {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String path = sc.nextLine(); // 콘솔에서 URL 입력 받기
Method[] methods = UserController.class.getDeclaredMethods(); // UserController의 모든 메서드 조회
UserController uc = new UserController(); // 컨트롤러 객체 생성
// [1] 모든 메서드 순회
for (Method method : methods) {
Annotation anno = method.getDeclaredAnnotation(RequestMapping.class); // @RequestMapping 어노테이션 추출
RequestMapping rm = (RequestMapping) anno; // 다운캐스팅
// [2] 현재 메서드의 @RequestMapping 값이 요청 경로와 일치하는지 확인
if (rm.value().equals(path)) {
try {
// [3] 해당 메서드의 파라미터 어노테이션과 타입 정보 수집
Annotation[][] paramAnnotations = method.getParameterAnnotations(); // 파라미터의 어노테이션들
Class<?>[] paramTypes = method.getParameterTypes(); // 파라미터 타입들
Object[] argsForInvoke = new Object[paramTypes.length]; // 메서드 실행에 넘길 실제 인자들
// [4] 파라미터 수 만큼 반복
for (int i = 0; i < paramTypes.length; i++) {
boolean isPrincipal = false;
// [5] 현재 파라미터에 @Principal 어노테이션이 있는지 확인
for (Annotation annotation : paramAnnotations[i]) {
if (annotation.annotationType().equals(Principal.class)) {
isPrincipal = true;
break;
}
}
// [6] 주입할 인자 결정
if (isPrincipal || paramTypes[i].equals(SessionUser.class)) {
argsForInvoke[i] = SessionUser.getInstance(); // @Principal 또는 SessionUser 타입이면 세션 유저 주입
} else if (paramTypes[i].equals(Model.class)) {
argsForInvoke[i] = Model.getInstance(); // Model 타입이면 싱글톤 모델
} else {
argsForInvoke[i] = null; // 처리하지 않는 타입은 null 주입
}
}
// [7] 준비된 인자를 이용해 컨트롤러 메서드 실행
method.invoke(uc, argsForInvoke);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e); // 예외 발생 시 래핑해서 던짐
}
}
}
}
}
⬇ 실행 결과
“userinfo”

“/login”

“/join”

“/logout”

ElementType
✅ 정의
java.lang.annotation.ElementType
은 어노테이션을 어떤 대상에 붙일 수 있는지를 지정하는 열거형(enum)
- 예를 들어, 어떤 어노테이션이 클래스에만 붙을 수 있는지, 메서드에만 붙을 수 있는지 등을 결정할 때 사용
🧠 주요 용도
@Target({...})
어노테이션의 인자로 사용됨@Target(ElementType.METHOD) // 메서드에만 붙일 수 있음
public @interface MyAnnotation {
}
📚 주요 상수 목록
상수 | 적용 대상 설명 |
TYPE | 클래스, 인터페이스, enum, 애노테이션 |
FIELD | 멤버 변수 |
METHOD | 메서드 |
PARAMETER | 메서드의 매개변수 |
CONSTRUCTOR | 생성자 |
LOCAL_VARIABLE | 지역 변수 |
ANNOTATION_TYPE | 다른 애노테이션에 붙일 수 있음 |
PACKAGE | 패키지 |
TYPE_PARAMETER | 제네릭 타입 파라미터 (Java 8+) |
TYPE_USE | 타입이 쓰이는 모든 위치 (Java 8+) |
✅ 예시
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
→ 이 어노테이션은 클래스(
TYPE
)와 필드(FIELD
)에만 사용할 수 있다✍️ 한줄 요약
ElementType
은 어노테이션이 적용될 수 있는 대상을 제한하기 위한 열거형(enum)이며,@Target
에서 사용됨
Share article