JAVA 바이너리 호환성 관련 이슈

강민석·2022년 12월 7일
0

JAVA

목록 보기
3/7

회사에서 개발을 하면서, 생각치 못한 오류를 겪어 메모를 해두려고 한다.

1. 개요

1.1 개발 상황

A 인터페이스는 B 클래스에 의해 구현되었으며, C 클래스에서 A 인터페이스를 사용해 메소드를 호출하고 B 클래스를 주입해주었다.

interface A {
	void alphaMethod();
    void betaMethod();
    ...
}
public class B implements A {
	@Override
    void alphaMethod();
    ...
}
public class C <T extends A> {
	T service; // DI에 의해 B가 주입
    
    public void exec() {
    	service.betaMethod();
    }
}

1.2 인지 상태

인터페이스는 정의된 메소드를 구현체에 구현하기를 강제하므로, C와 같이 작성하고 컴파일 에러가 나지 않는다면 에러가 나지 않을 것이라고 생각했다.

1.3 이슈 발생

정의되지 않은 메소드가 호출되었다는 예외가 발생했다.

왜 이런 예외가 발생했을까??? 이해를 돕기위해 1.1의 코드들을 살펴보자.

클래스 C는 service라는 필드를 가지고있고, 제네릭에 명시된대로 외부로부터 A의 구현체인 B를 주입 받았다.

A의 확장인 T를 사용하므로, A에서 정의된 betaMethod()를 호출하였다.

그런데, B에는 betaMethod()가 구현되어 있지 않았다... 어째서 이럴 수 있었을까??

2. 바이너리 호환성

현대의 자바 개발 도구는 automatic recompilation을 지원하지만, 인터넷 환경과 같이 분산된 환경에서는 이런 작업이 힘들 수 있다. -오라클-

이슈의 원인은 바이너리 호환성(binary compatibility)라고 하는 컴퓨터 시스템의 특성에 있었다.

바이너리 호환성은 변경 이후에도 컴파일을 하지않고 바이너리를 실행할 수 있는 특성을 의미한다. 위의 이슈의 상황은 바이너리 호환성이 적용된 상황이였으며, 이로 인해 런타임에서 오류가 발견된 것이였다.

그렇다면 바이너리 호환성은 왜 존재할까??

2.1 바이너리 호환성의 존재이유

컴퓨터의 발전을 생각하면 항상 그 흐름에 존재하는 단어가 있다. 바로 이기종호환분산구조이다. 바이너리 호환성도 이런 특성에 의해 고려되었다.

작은 예시로 자바언어의 특성을 생각해보면 자바에서 바이너리 호환성이 지원되는 이유를 알 수 있다. 우리는 너무 당연하게 여기는 특성이지만, 자바언어의 특성을 나열하면 항상 OS에 독립적이다 라는 특성을 말하곤 한다.

인터넷이 도입되고 분산구조가 확장됨과 동시에 다양한 하드스펙의 컴퓨터들이 생산되기 시작했고, 여러명이서 개발을 하거나 개발과 운영의 자원이 다른 환경에서 문제들이 생겨나기 시작했다.

각기 다른 OS는 사용 언어가 다른 사람과 같으므로, 정상적인 동작을 위해서는 OS변경에 따라서도 매번 컴파일이 필요했다. 하지만 JAVA는 JVM이라는 가상환경을 OS위에 올림으로서 컴파일을 반복하지 않고 동작할 수 있도록 설계되었다.

이런 고민들은 비단 OS뿐만 아니라 코드의 변경에도 적용되었는데 이를 바이너리 호환성이라고 부른다. 분명 발전된 개발 툴을 사용하면 automatic recompilation을 지원할 수 있지만, 모든 환경에서 이런 동작이 필요하지는 않으며 또한 불가능할 수 있다는점에서 바이너리 호환성이 적용되게 되었다.

it is often impractical or impossible to automatically recompile the pre-existing binaries that directly or indirectly depend on a type that is to be changed. -오라클-

2.2 바이너리 호환성이 적용되는 경우

코드 변경에서의 경우의 수는 굉장히 다양하기 때문에 모든 경우에서 바이너리 호환성이 지켜지지는 않는다. 오라클에서 제공하는 바이너리 호환성이 지켜지는 경우는 그림1과 같다.
그림1. 바이너리 호환성이 지켜지는 경우

3. 회고

그렇다면 그림1에서 1.1 개발상황이 적용되는 경우를 찾아보자. 아래와 같을 것이다.

  • Adding new fields, methods, or constructors to an existing class or interface. (이미 존재하는 클래스나 인터페이스에 새로운 필드, 메소드 그리고 생성자를 추가하는 경우)

1.1 개발상황에서는 설명하지 않았지만 이미 인터페이스가 존재하는 상태에서, 새로운 메소드를 추가하였고 "인터페이스에 정의하면 콘크리트에는 강제되니까" 라는 생각으로 점검을 하지 않았던 부분이 이슈의 원인 중 하나였다.

고등언어의 편의성에 빠져서 당연하게 여겼던 일들을 다시 생각해볼 필요가 있겠다는 생각이 드는 이슈였다. 이 이슈의 연장선으로 저레벨의 지식에 대해 공부하는 것도 좋겠다는 생각을 하게 되었다.

0개의 댓글