Skip to content

Commit

Permalink
[item65]: 리플렉션보다는 인터페이스를 사용하라라 (depromeet#146)(건호)
Browse files Browse the repository at this point in the history
  • Loading branch information
c0dep1ayer committed Sep 3, 2023
1 parent 529d797 commit bc5a07f
Showing 1 changed file with 106 additions and 0 deletions.
106 changes: 106 additions & 0 deletions Ch09/item65/리플렉션보다는_인터페이스를_사용하라.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Item 65. 리플렉션보다는 인터페이스를 사용하라

### Reflection

오라클 API 문:

Reflection is commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine. This is a relatively advanced feature and should be used only by developers who have a strong grasp of the fundamentals of the language. With that caveat in mind, reflection is a powerful technique and can enable applications to perform operations which would otherwise be impossible.

- 리플랙션은 Java 가상 머신에서 실행되는 응용 프로그램의 런타임 동작을 조사하거나 수정해야 하는 프로그램에서 주로 사용된다.
- 이 기능은 상대적으로 고급 기능이므로 언어의 기본 원칙을 잘 이해하고 있는 개발자만 사용해야 한다.
- 이 주의사항을 염두에 두면, 리플랙션은 강력한 기술이다. 그리고 그렇지 않으면 불가능한 작업을 수행할 수 있게 한다.

<br/>

### 올바른 사용 예시

**프레임워크나 라이브러리 제작**:
대표적으로 Spring과 같은 프레임워크에서는 리플랙션을 사용하여 객체를 생성하거나 메서드를 호출한다. 사용자가 정의한 클래스와 메서드에 대한 메타 데이터를 얻기 위해 사용한다.

```java
public Object createInstance(String className) throws Exception {
Class<?> clazz = Class.forName(className);
return clazz.newInstance();
}
```

**제네릭 타입 검사**:
제네릭 정보는 런타임에 지워지기 때문에(타입 소거) 리플랙션을 사용하여 해당 정보를 얻을 수 있다.

```java
public <T> void genericTypeCheck(T obj) {
Class<?> clazz = obj.getClass();
System.out.println("제네릭 타입은: " + clazz.getSimpleName());
}
```

<br/>

### 잘못 사용한 예시

**비공개 필드나 메서드 접근**:
비공개 필드나 메서드에 접근하려고 리플랙션을 사용하는 것은 권장되지 않는다. 그 이유는 개발자가 비공개로 설정한 것은 내부 동작을 위해 숨겨진 것이기 때문이다.

```java
import java.lang.reflect.Field;

public class BadReflectionExample {
private String secret = "비밀 정보";

public static void main(String[] args) throws Exception {
BadReflectionExample example = new BadReflectionExample();
Field field = BadReflectionExample.class.getDeclaredField("secret");
field.setAccessible(true);
System.out.println("비공개 필드 접근: " + field.get(example));
}
}
```

**비효율적인 객체 생성**:
Class.newInstance() 를 사용하여 객체를 생성하는 것은 해당 클래스의 기본 생성자가 호출되지만, 생성자에서 발생하는 예외는 래핑되어 다시 던져진다. 따라서 Constructor.newInstance()를 사용하는 것이 더 안전하다.

```java
// 잘못된 방식
Object obj = SomeClass.class.newInstance();

// 올바른 방식
Constructor<SomeClass> constructor = SomeClass.class.getDeclaredConstructor();
Object obj = constructor.newInstance();
```

**타입 안전성 무시**:
리플랙션을 사용하면 컴파일 타임에 확인할 수 있는 타입 안전성을 무시하게 되어 런타임 에러의 위험이 증가한다.

```java
public void unsafeSet(Object obj, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField("someField");
field.setAccessible(true);
field.set(obj, value); // 타입 검사가 없다면, 잘못된 타입의 값이 설정될 수 있다.
}
```

<br/>

### 리플랙션의 단점

- `성능 이슈`: 리플랙션은 런타임에 타입을 검사하고 조작하기 때문에 직접 코드를 호출하는 것에 비해 성능 손실이 발생한다.

- `보안 제한`: 보안 매니저가 설정되어 있는 경우, 리플랙션을 사용하여 접근하려는 클래스나 메서드에 대한 권한이 없을 수 있다.

- `내부 API 및 변경 가능성`: 리플랙션을 사용하여 접근하는 클래스나 메서드가 미래의 Java 버전에서 변경되거나 사라질 수 있다. 내부 API에 대한 접근은 항상 위험하다.

- `복잡성 증가`: 리플랙션 코드는 복잡하며 디버깅하기 어렵다. 코드의 가독성도 저하된다.

결론적으로, 리플랙션은 필요할 때만 사용하고, 그 사용을 최소화해야 한다. 가능하면 컴파일 타임에 알려진 타입과 API를 사용하는 것이 바람직하다.

<br/>

### 정리

- 리플렉션은 Java에서 제공하는 매우 강력한 기능이다. 이를 통해 런타임에 객체의 내부 정보를 조사하거나 조작할 수 있다. 특히 프레임워크나 라이브러리의 개발, 플러그인 시스템 구현 등 복잡하고 특수한 시스템을 개발할 때 유용하다.

- 그러나 이 강력한 기능은 단점도 많다. 성능 이슈, 보안 제한, 코드 복잡성 증가 등이 대표적인 문제점이다. 따라서 리플렉션은 반드시 필요한 경우에만 사용하고, 그 범위를 최소화하는 것이 바람직하다.

- 특히 객체를 생성할 때는 리플렉션을 사용할 수 있지만, 생성된 객체를 다룰 때는 가능한 적절한 인터페이스나 알려진 상위 클래스로 형변환하여 사용해야 한다. 이렇게 하면 코드의 안정성과 가독성, 유지보수성을 높일 수 있다.

결론적으로, 리플렉션은 `필요한 경우에만, 그리고 신중하게` 사용해야 하는 기술이다. 가능하면 컴파일 타임에 타입이 알려진, 안전한 방법을 우선적으로 고려해야 한다.

0 comments on commit bc5a07f

Please sign in to comment.