[Java] CS 스터디 9주차
리플렉션에 대해 설명해 주세요.
자바의 리플렉션은 실행 중인 자바 프로그램이 자기 자신의 구조를 분석하고 조작할 수 있게 해주는 기능입니다. 리플렉션을 통해 프로그램은 런타임에 클래스, 인터페이스, 필드, 메서드 등의 정보에 접근하고 조작할 수 있습니다.
- 특징
- 동적 클래스 로딩: 컴파일 시점에 알 수 없는 클래스를 런타임에 로드하고 사용할 수 있습니다.
- 클래스 정보 접근: 클래스의 이름, 메서드, 필드, 생성자 등의 정보를 얻을 수 있습니다.
- private 멤버 접근: 접근 제어자와 관계없이 클래스의 모든 멤버에 접근할 수 있습니다.
- 객체 생성: 클래스 정보를 이용해 새로운 객체를 동적으로 생성할 수 있습니다.
- 장점
- 유연성과 확장성이 높아집니다.
- 런타임에 동적으로 클래스를 조작할 수 있습니다.
- 프레임워크나 IDE에서 유용하게 사용할 수 있습니다.
- 단점
- 성능 오버헤드가 발생할 수 있습니다.
- 캡슐화를 위반할 수 있어 보안 위험이 있습니다.
- 코드의 복잡성이 증가하고 가독성이 떨어질 수 있습니다.
의미만 들어보면 리플렉션은 보안적인 문제가 있을 가능성이 있어보이는데, 실제로 그렇게 생각하시나요? 만약 그렇다면, 어떻게 방지할 수 있을까요?
네, 리플렉션은 다음과 같은 보안 문제가 있습니다.
보안 문제
- 리플렉션을 통해 private 멤버에 접근할 수 있어 캡슐화를 깨뜨릴 수 있습니다.
- 악의적인 사용자가 원하는 클래스를 동적으로 로드하고 실행할 수 있습니다.
- 컴파일 시 타입 체크를 우회하여 런타임 오류를 유발할 수 있습니다.
- 리플렉션을 통해 민감한 정보에 접근하거나 시스템 보안을 우회할 수 있습니다.
방지법은 다음과 같습니다.
방지법
- 리플렉션에 사용되는 외부 입력을 철저히 검증합니다
- 필요한 최소한의 권한만 부여하고, 가능한 한 제한적으로 리플렉션을 사용합니다.
- 검증된 안전한 라이브러리와 프레임워크를 사용합니다.
AccessibleObject.setAccessible()
의 사용을 제한합니다.- 정기적으로 코드를 스캔하여 취약점을 찾아냅니다.
- 상세한 오류 메시지가 노출되지 않도록 합니다.
- 사용자 활동을 지속적으로 모니터링하고 로깅합니다.
리플렉션을 언제 활용할 수 있을까요?
- 스프링 같은 프레임워크에서 의존성 주입 등에 사용됩니다.
- private 메서드 테스트 등에 활용됩니다.
- 동적으로 클래스를 로드하고 실행할 수 있습니다.
- IDE의 코드 자동 완성 등의 기능 구현에 사용됩니다.
Static Class와 Static Method를 비교해 주세요.
- Static Class
- Java에서 Static Class는 반드시 Inner Class로만 존재할 수 있습니다.
- 외부 클래스의 인스턴스 없이도 사용할 수 있습니다.
- Static Class 내부에서는 외부 클래스의 static 멤버만 직접 접근 가능합니다.
- Static Method
- 클래스 레벨에서 호출할 수 있으며, 인스턴스 생성 없이 사용하여 메모리 절약이 가능합니다.
- this 키워드를 사용할 수 없고, 인스턴스 변수에 직접 접근할 수 없습니다.
- 오버라이딩이 불가능하지만, 하위 클래스에서 같은 이름으로 재정의할 수 있습니다. (예시는 아래에)
class Parent {
public static void staticMethod() {
System.out.println("Parent's static method");
}
}
class Child extends Parent {
public static void staticMethod() {
System.out.println("Child's static method");
}
}
public class Main {
public static void main(String[] args) {
Parent.staticMethod(); // 출력: Parent's static method
Child.staticMethod(); // 출력: Child's static method
Parent p = new Child();
p.staticMethod(); // 출력: Parent's static method
}
}
static을 사용하면 어떤 이점을 얻을 수 있나요? 어떤 제약이 걸릴까요?
이점
- 메모리 효율성: static 변수는 클래스당 하나만 생성되어 모든 인스턴스에서 공유됩니다. 이는 메모리 사용량을 줄이는 데 도움이 됩니다.
- 객체 생성 없이 사용 가능: static 멤버는 객체를 생성하지 않고도 직접 접근하여 사용할 수 있어 편리합니다.
- 속도 향상: 객체 생성 과정이 필요 없어 속도가 빠릅니다.
제약
- 메모리 관리의 어려움: static 멤버는 프로그램 종료 시까지 메모리에 남아있어 과도한 사용 시 성능에 악영향을 줄 수 있습니다.
- 객체지향 원칙 위반: 캡슐화 원칙을 위반할 수 있으며, 객체의 상태에 의존적인 동작을 구현하기 어렵습니다.
- 재사용성 저하: static 메소드는 인터페이스를 구현하는 데 사용될 수 없어 코드의 재사용성을 떨어뜨립니다.
- 상속 및 다형성 제한: static 메소드는 오버라이딩할 수 없어 상속과 다형성의 이점을 활용하기 어렵습니다.
- 테스트의 어려움: 전역적인 특성으로 인해 단위 테스트가 어려워질 수 있습니다.
컴파일 과정에서 static이 어떻게 처리되는지 설명해 주세요.
- 클래스 파일 생성: 자바 컴파일러(javac)는 소스 코드(.java 파일)를 컴파일하여 바이트코드(.class 파일)를 생성합니다.
- 클래스 로더 동작: 컴파일된 .class 파일은 클래스 로더에 의해 JVM의 메모리로 로드됩니다.
- 메모리 할당: static 키워드가 붙은 멤버들은 클래스 로더가 클래스를 로드할 때 메모리 공간(Static 공간)에 메모리를 할당받습니다.
- 초기화 순서: static 변수는 클래스가 로드될 때 초기화됩니다. 이는 인스턴스 생성 전에 이루어집니다.
- static 블록 실행: static 블록이 있다면, 클래스가 로드될 때 최초 한 번만 실행됩니다.
Java의 Exception에 대해 설명해 주세요.
- Exception은 프로그램 실행 중 발생하는 예기치 않은 이벤트로, 프로그램의 정상적인 흐름을 방해합니다.
- Exception은 error의 일종이지만, error와 달리 프로그램적으로 처리가 가능합니다.
- 모든 Exception 클래스는
java.lang.Exception
클래스를 상속받습니다. - Exception 처리의 목적은 프로그램의 비정상적인 종료를 방지하고, 정상적인 실행 상태를 유지하는 것입니다.
- Exception은 프로그램의 안정성과 신뢰성을 높이는 데 중요한 역할을 합니다.
- Exception은 크게 두 가지 종류로 나뉩니다.
- Checked Exception: 컴파일 시점에 확인되며, 반드시 예외 처리를 해야 합니다.
- Unchecked Exception: 실행 시점에 발생하며, 명시적인 예외 처리가 강제되지 않습니다.
예외처리를 하는 세 방법에 대해 설명해 주세요.
1. try-catch 블록 사용
try-catch 블록은 가장 일반적인 예외 처리 방법입니다.
try {
// 예외가 발생할 수 있는 코드
} catch (ExceptionType e) {
// 예외 처리 코드
}
이 방법은 예외가 발생할 수 있는 코드를 try 블록에 넣고, 발생할 수 있는 예외 유형을 catch 블록에서 처리합니다.
2. throws 키워드 사용
throws 키워드를 사용하여 메소드에서 발생할 수 있는 예외를 선언하고, 이를 호출하는 곳으로 예외를 던집니다.
public void method() throws ExceptionType {
// 예외가 발생할 수 있는 코드
}
이 방법은 예외를 직접 처리하지 않고 메소드를 호출하는 쪽에서 처리하도록 책임을 전가합니다.
3. try-with-resources 구문 사용
Java 7부터 도입되었고, 자동으로 리소스를 닫아주는 기능을 제공합니다.
try (Resource resource = new Resource()) {
// 리소스를 사용하는 코드
} catch (Exception e) {
// 예외 처리 코드
}
이 방법은 주로 파일 입출력이나 데이터베이스 연결과 같이 사용 후 반드시 닫아야 하는 리소스를 다룰 때 유용합니다.
CheckedException, UncheckedException 의 차이에 대해 설명해 주세요.
CheckedException
- 컴파일 시점 확인: 컴파일러가 이러한 예외를 확인하고 처리를 강제합니다.
- 예외 처리 의무: 반드시 try-catch 블록으로 처리하거나 throws 키워드로 선언해야 합니다.
- 상속 계층: Exception 클래스를 직접 상속받지만,
RuntimeException
을 상속받지 않습니다. - 예시:
IOException
,SQLException
,ClassNotFoundException
등 - 발생 원인: 주로 외부 요인에 의해 발생합니다. (파일 시스템, 네트워크 등)
UncheckedException
- 런타임 시점 확인: 컴파일 시점에는 확인되지 않고, 실행 중에 발생합니다.
- 예외 처리 선택: 명시적인 예외 처리가 강제되지 않습니다.
- 상속 계층:
RuntimeException
클래스를 상속받습니다. - 예시:
NullPointerException
,ArrayIndexOutOfBoundsException
,ArithmeticException
등 - 발생 원인: 주로 프로그래밍 오류에 의해 발생합니다.
차이점 (주의!: DB 트랜잭션이 아닌, 스프링 트랜잭션에서만 적용됨)
특성 | CheckedException | UncheckedException |
---|---|---|
컴파일 시 확인 | O | X |
예외 처리 | 필수 | 선택적 |
상속 | Exception | RuntimeException |
예외 선언 (throws) | 필요 | 불필요 |
트랜잭션 처리 (주의) | 롤백하지 않음 | 롤백함 |
CheckedException
은 예측 가능하고 복구 가능한 상황에서 사용되며, 개발자가 명시적으로 처리해야 합니다.
반면에 UncheckedException
은 프로그램 오류를 나타내며, 일반적으로 복구가 불가능하거나 예측하기 어려운 상황에서 발생합니다.
예외처리가 성능에 큰 영향을 미치나요? 만약 그렇다면, 어떻게 하면 부하를 줄일 수 있을까요?
자바에서 예외 처리는 성능에 상당한 영향을 미칠 수 있습니다. 하지만 적절히 사용하면 그 영향을 최소화할 수 있습니다.
예외 처리의 성능 영향
- 예외 생성 및 스택 트레이스 생성은 비용이 많이 드는 작업입니다.
- 예외가 발생하는 빈도가 높을수록 성능 저하가 더 심해집니다.
- 예외 처리 구문(try-catch)이 있는 것만으로도 JIT 컴파일러의 최적화를 방해할 수 있습니다.
- JIT(Just-In-Time) 컴파일러: 프로그램을 실행하는 시점에 기계어로 번역하는 컴파일 기법
부하 줄이는 방법
- 예외는 진정한 예외 상황에만 사용하고 일반적인 흐름 제어나 검증 로직에는 사용하지 말아야 합니다.
- 구체적인 예외 클래스를 사용하여 호출자가 더 잘 처리할 수 있게 합니다.
- 리소스 관리에 try-with-resources로 자동으로 리소스를 닫아줘서 메모리 누수를 방지합니다.
- 예외 처리 깊이를 최소화하여 예외 처리 비용을 줄입니다.
- 예외를 잡아서 로깅한 후 다시 던지지 말아야 합니다. 만약 던지면 중복 로깅이 발생합니다.
- 크리티컬 성능 코드에서는 가능하면 null 체크나 조건문으로 대체해서 예외 사용을 최소화합니다.
- JVM의 인라인 최적화를 통해 예외 처리 비용을 줄입니다.
- 인라인 최적화: 컴파일러나 JIT 컴파일러가 함수 호출을 최적화하여 성능을 개선하는 기법
- 예외 발생 빈도가 높아지면 코드를 리팩토링합니다.
Leave a comment