런타임 타입 식별(Runtime Type Identification)이란 무엇인가요?

김상욱·2024년 11월 18일

런타임 타입 식별(Runtime Type Identification)이란 무엇인가요?

Runtime Type Identification(RTTI)은 프로그램 실행 중에 객체의 실제 타입(runtime type)을 확인할 수 있는 메커니즘. 이는 일반적으로 다형성(polymorphism)을 사용하는 객체 지향 프로그래밍 언어에서 중요하며, 컴파일 타임이 아닌 런타임에 타입 정보를 확인하거나 동작을 수행할 때 사용.

  • 객체가 포인터나 참조로 접근될 때, 해당 객체가 어떤 클래스의 인스턴스인지 확인할 수 있음.
  • 다운캐스팅을 안전하게 수행하기 위해 사용.
  • 실행 중에 이루어지므로, 프로그램 실행 성능에 약간의 영향을 미칠 수 있음.

instanceof 키워드와 클래스의 getClass() 메서드를 통해 RTTI를 수행

class Base {}
class Derived extends Base {}

public class Main {
    public static void main(String[] args) {
        Base base = new Derived();
        if (base instanceof Derived) {
            System.out.println("base는 Derived 타입입니다.");
        }
    }
}

객체의 실제 타입에 따라 동적으로 동작을 결정할 수 있어 유연성을 제공. 또한 잘못된 타입으로의 캐스팅을 방지 가능.
대신, 실행 중에 타입 정보를 확인하기 때문에 약간의 성능 오버헤드 발생. RTTI를 지나치게 사용하면 객체 지향 설꼐를 잘못 사용하는 결과를 초래 -> 다형성을 활용해 대체할 수 있는 경우에는 RTTI 사용을 피하는것이 좋음.

다형성과 템플릿(Generics)를 사용해 RTTI 사용을 줄일 수 있음.
-> 템플릿의 경우에는 컴파일 타임에 타입을 확정함으로써 RTTI를 사용할 필요 줄임.


1. 다형성과 RTTI 활용

RTTI는 다형성에서 자주 사용하는 기능이므로, 다형성을 활용해 객체의 실제 타입에 따라 다른 동작을 수행하는 연습을 해보세요.

실습: instanceof와 다형성 비교

abstract class Animal {
    abstract void makeSound();
}

class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("Woof");
    }
    void fetch() {
        System.out.println("Fetching the ball!");
    }
}

class Cat extends Animal {
    @Override
    void makeSound() {
        System.out.println("Meow");
    }
    void scratch() {
        System.out.println("Scratching the furniture!");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal[] animals = {new Dog(), new Cat()};

        for (Animal animal : animals) {
            // RTTI를 사용한 타입 확인
            if (animal instanceof Dog) {
                Dog dog = (Dog) animal;
                dog.makeSound();
                dog.fetch();
            } else if (animal instanceof Cat) {
                Cat cat = (Cat) animal;
                cat.makeSound();
                cat.scratch();
            }
        }
    }
}

목표:

  • 다형성을 활용해 makeSound() 메서드를 호출하는 것과 instanceof를 사용한 캐스팅의 차이를 이해합니다.
  • instanceof 없이 다형성으로 처리할 수 있는지 고민해 봅니다.

2. Spring에서 Bean 타입 확인

Spring에서는 ApplicationContext를 통해 Bean의 타입을 확인하고 특정 Bean을 가져올 수 있습니다. 이를 활용하면 RTTI와 비슷한 동작을 실습할 수 있습니다.

실습: Spring Bean 타입 확인
1. Bean 정의

@Component
public class MyService {}

@Component
public class YourService {}
  1. ApplicationContext 활용
@SpringBootApplication
public class RttiApplication {

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(RttiApplication.class, args);

        // 특정 Bean의 실제 타입 확인
        Object myService = context.getBean("myService");
        System.out.println("Bean 타입: " + myService.getClass().getName());

        if (myService instanceof MyService) {
            System.out.println("myService는 MyService의 인스턴스입니다.");
        }
    }
}

목표:

  • Spring의 DI 컨테이너에서 Bean의 실제 타입을 확인하고, 이를 활용해 조건별로 동작을 처리합니다.
  • Bean 타입 확인이 필요한 상황과 그렇지 않은 상황을 이해합니다.

3. 실제 프로젝트 시나리오

서비스 개발 중 특정 입력 데이터를 처리할 때, 객체의 타입에 따라 다른 비즈니스 로직을 적용해야 할 때가 있습니다. 이를 실습해 볼 수 있습니다.

실습: 입력 타입별로 로직 처리

interface PaymentMethod {}

class CreditCard implements PaymentMethod {
    void processPayment() {
        System.out.println("Processing credit card payment");
    }
}

class PayPal implements PaymentMethod {
    void processPayment() {
        System.out.println("Processing PayPal payment");
    }
}

public class PaymentProcessor {
    public void process(PaymentMethod paymentMethod) {
        if (paymentMethod instanceof CreditCard) {
            ((CreditCard) paymentMethod).processPayment();
        } else if (paymentMethod instanceof PayPal) {
            ((PayPal) paymentMethod).processPayment();
        } else {
            throw new IllegalArgumentException("Unsupported payment method");
        }
    }
    
    public static void main(String[] args) {
        PaymentProcessor processor = new PaymentProcessor();
        processor.process(new CreditCard());
        processor.process(new PayPal());
    }
}

목표:

  • 입력 타입에 따라 다른 로직을 처리하며, RTTI의 실용성을 이해합니다.
  • 다형성과 비교해, RTTI가 꼭 필요한 경우를 식별합니다.

4. Reflection API 사용

Java의 Reflection API는 RTTI를 기반으로 동작합니다. 이를 활용해 런타임에 클래스 정보를 확인하거나 메서드를 호출하는 실습을 진행할 수 있습니다.

실습: Reflection으로 런타임 메서드 호출

import java.lang.reflect.Method;

public class ReflectionExample {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("java.util.ArrayList");
        Object instance = clazz.getDeclaredConstructor().newInstance();

        Method addMethod = clazz.getMethod("add", Object.class);
        addMethod.invoke(instance, "Hello Reflection");

        Method sizeMethod = clazz.getMethod("size");
        System.out.println("ArrayList size: " + sizeMethod.invoke(instance));
    }
}

목표:

  • Reflection을 사용해 객체의 메서드를 런타임에 동적으로 호출합니다.
  • Reflection을 사용할 때의 장단점과 주의점을 이해합니다.

5. RTTI 없이 문제 해결

다형성과 인터페이스를 활용하여 RTTI를 줄이고 설계를 개선하는 연습도 중요합니다.

실습: 인터페이스 활용
위의 PaymentMethod 예제를 개선하여 instanceof 없이 다형성으로 문제를 해결해 보세요.

interface PaymentMethod {
    void processPayment();
}

class CreditCard implements PaymentMethod {
    @Override
    public void processPayment() {
        System.out.println("Processing credit card payment");
    }
}

class PayPal implements PaymentMethod {
    @Override
    public void processPayment() {
        System.out.println("Processing PayPal payment");
    }
}

public class PaymentProcessor {
    public void process(PaymentMethod paymentMethod) {
        paymentMethod.processPayment();
    }

    public static void main(String[] args) {
        PaymentProcessor processor = new PaymentProcessor();
        processor.process(new CreditCard());
        processor.process(new PayPal());
    }
}

신입 백엔드 개발자 관점의 학습 포인트

  • RTTI 사용 여부 결정: RTTI를 사용할 필요가 있는 상황과 아닌 상황을 구분할 수 있는 능력을 기릅니다.
  • 다형성 중심 설계: RTTI 없이 객체의 타입별 동작을 처리하는 설계 방법을 연습합니다.
  • Spring 기반 적용: Bean 관리, Reflection 등을 통해 실무에서 RTTI를 어떻게 사용하는지 감각을 익힙니다.
  • Reflection 학습: 고급 주제로 Reflection을 탐구하며, 런타임에 객체를 다루는 방식을 학습합니다.

이 실습은 단순히 RTTI를 사용하는 것뿐 아니라 객체 지향 설계를 강화하는 데도 도움이 될 것입니다.

0개의 댓글