IBM®
메인 컨텐츠로 가기
    Korea [국가변경]    이용약관
 
 
   
        제품    서비스 & 솔루션    고객지원 & 다운로드    회원 서비스    
한국 developerWorks   >  dW Column  > developerworks

C++로 돌아가는 자바

자바의 바람직한 진화를 이야기한다.


김도형김도형 dynaxis@alticast.com

양방향 TV 솔루션 개발을 해 왔고 현재는 DMB를 위한 자바 규격 표준화와 제품 개발을 맡고 있다. JSR 242, JSR 272 등 방송 관련 JSR에 전문가로 참여했으며, 자바 외에 프로그래밍 언어 전반, XML 등 다양한 분야에 걸쳐 관심을 가지고 있다.



2008년 4월 8일


C++가 ‘더 나은 C(a better C)’라는 이미지를 통해 급격히 주류 언어로 자리 잡았듯이, 자바도 초기에는 ‘제대로 된 C++(C++ done right)’라는 이미지를 심는 데 주력했다. 하지만 C++가 C를 그대로 포함한 확장 언어였던 것과 달리 자바는 사실상 C++와 비슷하지도 않은 언어다. 개인적으로 자바를 궁극의 언어라고 생각하지는 않지만, 최소한 처음 나온 자바 1.0은 미니멀리즘 미학이 돋보이는 간결한 언어였다고 생각한다. 지역 변수에는 참조형과 기본형 변수만 선언할 수 있고 모든 객체는 힙(heap)에 할당하는 과감한 간결성, 부호 없는 정수 제거, 연산자 재정의(operator overloading) 등 사소한 문법적 편의(syntactic sugar)의 과감한 배제 등이 바로 그런 예다. 그 때문에 API 부분을 제외하면 처음 자바 언어를 접하는 사람이라도 몇 시간이면 언어에 대한 감을 잡을 수 있었다.

이러던 자바 언어가 계속 덩치를 키우고 있다. 사실상 시작은 내부 클래스(innerclass)가 추가되면서부터였다. 그 후 한동안 잠잠하다가 제네릭스(generics) 이야기가 나오고 C#이 등장하면서 다양한 문법적 편의 요소와 개념이 추가되기 시작했다. 최근에는 자바 7에 클로저(closure)를 추가하기 위한 논의가 활발하다. 문제는 프로그래밍 언어가 설계될 때 고려하지 않던 개념을 추가하려다 보면 언어가 불필요하게 복잡해지고 일관성이 없어질 수 있다는 점이다. 특히 다수의 사람이 머리를 맞대면 문제가 더 심각해진다. 배가 산으로 간다. 자바도 예외가 아니라서 이미 명백한 실수로 봐야 하는 것이 많다. 결국 제대로 된 C++를 표방하던 자바가 이젠 C++와 비슷해 지고 있는 것이다. 이 때문에 최근 클로저 추가를 둘러싸고 자바 언어를 계속 확장하는 것이 옳은지에 대한 논쟁이 뜨겁다.

어차피 이 칼럼에서 뭐라고 하든 계속 커져가는 자바 언어에 아무런 영향을 끼치지 못하는 만큼 이번에는 자바 언어와 플랫폼의 바람직한 진화에 대해 맘 놓고 설파해 보겠다.



위로



왜 새로운 개념이 필요한가?

소프트웨어 개발 자체가 그렇듯 프로그래밍 언어도 사용되는 환경의 변화에 영향을 받는다. 예를 들어 데이터베이스 처리에 많이 사용되면 질의(query)를 쉽게 작성하고 결과를 처리할 수 있는 문법적 배려가 들어가기도 한다. 최근 .NET 프레임워크 3.5에 추가된 LINQ(Language Integrated Query)가 그런 예다.

하지만 프로그래밍 언어가 어떻든 결국 컴퓨터의 기계어로 변환되어 실행되는 바, 언어가 바뀐다고 컴퓨터가 뭔가 새로운 일을 할 수 있을 리는 만무하다. 결국 언어를 구현한 컴파일러가 프로그래머를 도와 특정 문제를 더 쉽게 해결할 수 있도록 해 주는 것이다.

흔히 누군가 C++나 자바로 프로그래밍을 해야 객체 지향 프로그래밍을 할 수 있다고 이야기하면 C로도 객체 지향 프로그래밍을 할 수 있다는 원론적인 정답을 이야기하곤 한다. 객체 지향 개발은 언어와 별개의 개념이라는 것이다. 하지만 사실 객체 지향 언어의 특성과 그 언어를 구현한 컴파일러의 도움 없이 객체 지향 프로그래밍을 하는 것은 매우 괴롭다. 실제 C를 객체 지향적으로 사용한 대표적인 사례인 X 윈도/모티프를 보면 눈물이 앞을 가린다. 구조체에서 손대지 말아야 하는 부분은 프로그래머가 알아서 손대지 말아야 하며, 코드를 읽는 입장에서도 그다지 직관적이지 않다. 이렇게 보면 프로그래밍 언어가 쓰이는 환경에 맞춰 적절한 특성을 지원하는 것은 좋은 일이다.

왜 언어에 새로운 개념을 고안하고 도입하는지를 정리해 보면 다음과 같다.

  • 흔히 나타나는 코드 패턴을 간결하게 추상화해 코딩하기도 쉽고 읽기도 쉽게 배려한다 - 자바 5에 도입된 for each 루프가 이런 부류에 속한다. 넓은 의미로는 객체 지향 언어에서 메서드 호출도 이런 부류에 속한다. C로 구현하려면 구조체(struct)로 표현된 객체에서 해당 클래스 정보를 나타내는 또 다른 구조체를 가리키는 포인터를 따라가 그 내부의 메서드 테이블을 찾아 호출할 메서드에 해당하는 함수 포인터를 얻어 함수를 호출하는 복잡한 코드가 필요하지만 객체 지향 언어에서는 단순 함수 호출과 같은 간단한 코드로 표현할 수 있다.
  • 컴파일러에 추가적인 정보를 주어 프로그래머의 실수를 자동으로 점검해 오류를 만들지 않도록 도와준다 - 자바 5에 도입된 제네릭스가 이런 부류다. 예를 들어 제네릭스 이전에는 컬렉션(Collection) 객체에서 모든 객체를 오브젝트로 처리했지만 이제 구체적인 타입 정보를 줄 수 있어 컴파일러가 프로그래머의 실수를 점검할 수 있게 되었다. C에서 객체 지향 프로그래밍을 할 때와 달리 자바나 C++가 객체의 멤버에 대해 public, protected, private 등 접근 권한을 점검해 주는 것도 이 부류에 속한다.
  • 상기 과정을 통해 컴파일러가 내부적으로 최적화할 수 있는 소지를 높여준다 - 더 고수준에서 프로그래밍을 하면 컴파일러가 코드를 생성하는 데 더 많은 융통성이 생긴다. 또 컴파일러에 더 많은 정보를 알려주면 더 많은 최적화가 가능하다. 예를 들어 문자열을 담을 리스트를 제네릭스를 이용해 정의하고 사용하는 경우 문자열을 리스트에 넣고 뺄 때 굳이 타입을 검사하고 변환하지 않아도 된다. .NET의 제네릭스는 VM 수준에서 제네릭스를 지원하므로 불필요한 검사와 변환 과정을 제거해 프로그램 효율을 높인다. 참고로 뒤에 언급하겠지만 자바는 VM 수준에서 제네릭스를 지원하지 않으므로 그저 사용자의 실수를 걸러내고 타이핑을 줄이는 목적으로만 활용하는 한계가 있다.

개인적으로는 단순히 타이핑을 줄여주는 목적으로 뭔가 새로운 개념을 도입하는 것은 찬성하지 않는다. 이클립스(Eclipse)나 넷빈즈(NetBeans) 같은 훌륭한 통합 개발 환경을 공짜로 구할 수 있는 요즘 프로그래머의 단순 노동은 현저하게 줄었고, 사실 타이핑하는 문자 수가 반이 된다고 생산성이 두 배 오르는 것이 아니기 때문이다. 생산성은 작성하는 로직의 복잡도와 관계가 있고 적절한 패러다임과 런타임 지원, 라이브러리 등을 이용해 이 복잡도를 낮추지 않는 한 생산성은 올라가지 않는다. 그래서 오히려 남이 작성한 코드를 읽고 이해해야 하는 측면을 생각할 때 가독성이 더 중요하다고 생각한다.



위로



언어의 확장은 불가피한가?

사실 뭔가 새로운 언어를 설계하는 상황에서 언어가 사용될 환경을 고려해 문법적 편의 요소나 새로운 개념을 소개하는 것은 문제가 될 것은 없다. 물론 해당 요소나 개념의 유용성이 충분히 검증되고 일관성 있게 녹아 들어가야 하겠지만 말이다. 문제는 소수의 사람에 의해 잘 설계된 언어에 뭔가를 더 추가할 때다. 앞서 언급했지만 원래 언어의 일관성과 단순함을 잃을 소지가 높기 때문이다

개인적으로 C++를 별로 좋아하지 않는다. 태생부터 C를 배제하지 못하다 보니 복잡하게 설계됐고 이른바 위원회가 모여 이러저러한 개념을 추가하다 보니 구현하기도 어렵고 이해하기도 어려운 언어가 되고 말았다. C++의 바이블에 해당하는 “The C++ Programming Language”는 이미 1030쪽에 육박한다. ISO 표준 문서는 말할 나위도 없다. 참고로 일찌감치 규격이 안정화된 C 언어의 경우 바이블에 해당하는 “The C Programming Language”가 겨우 275페이지다. 혹자는 만능 도구화된 C++를 적절한 약속 하에서 제한해 사용하면 별 문제가 없다고 이야기한다. 하지만 나와 마음을 맞춘 사람들의 코드만으로 작업을 할 수 없는 요즘 같은 세상에 내가 익숙하지 않은 C++의 특성을 활용한 라이브러리나 코드를 맞닥뜨려 당황할 일이 없다고 누가 장담하겠는가?

반면 소수의 사람만이 사용하지만 오베론-2(Oberon-2) 같은 언어는 1991년 이후 지금껏 거의 변경 없이도 잘 사용되고 있다. 이 언어는 객체 지향 언어지만 단일 상속에 오브젝티브-C(Objective-C)나 자바의 인터페이스 같은 개념도 없다. 이 언어는 EBNF(Extended Backus-Naur Form)으로 문법을 표현하는 데 고작 A4 1쪽이면 족하다. 그 외에도 스몰토크(Smalltalk), 리스프(Lisp) 등 많은 언어가 큰 확장 없이 안정되게 쓰이고 있다.

물론 과거에는 컴퓨터 성능이나 메모리 제약, 컴파일러 구현의 부담 때문에 언어를 단순하게 설계하고 유지한 면이 있었다. 하지만 간결한 언어는 익히기 쉽고, 소수의 표준적인 메커니즘 하에 일관성 있게 프로그래밍을 할 수 있다는 장점이 있다. 최근에는 컴퓨터 성능과 메모리 제약이 적고, 자바나 .NET의 경우 하부에 VM이 받치고 있어 언어 특성을 추가하기가 상대적으로 쉬워진 것은 사실이지만 이런 점이 무분별한 언어 확장으로 이어져서는 안될 것이다.

개인적으로 언어 확장은 필수는 아니라고 생각한다. 하지만 언어가 일관성을 잃지 않고 잘 확장되는 한, 시대가 요구하는 편의 요소와 개념을 점진적으로 추가해 나가는 것도 나쁘지 않다고 생각한다. 특히 상업적인 성공을 거둔 주류 언어는 사용처가 넓어지면서 늘 애매한 분량의 새로운 요구 사항에 직면하기 때문에 확장이 현실적인 방안인 면도 있다. 하지만 확장에는 분명히 한계가 있고 많은 전문가가 머리를 모으더라도 한 단계 한 단계가 많은 위험을 수반한다. 따라서 언어 자체의 확장은 최대한 보수적으로 하되 확장할 때는 제대로 해야 한다. 이런 관점에서 개인적으로는 생각하는 언어 확장의 원칙을 정리해 보았다.

  • 모든 확장은 사실상 필요 없다 - 범용 언어가 설계될 때는 그 자체로 어느 정도 완결성을 지니므로 사실 어떤 확장이든 엄밀히 따지면 불가피하지 않다는 입장에서 검토해야 한다.
  • 불가피할 때는 이전 판과의 호환성을 버려라 - 언어 확장에 가장 큰 걸림돌 중 하나는 기존 코드와의 호환성이다. 호환성을 깨지 않으면서도 새로운 개념을 녹여낼 수 있는 방안이 있으면 좋지만, 언어가 어느 이상 오염된 경우 이전 판과 호환성을 과감하게 포기하거나 아예 새로운 이름으로 언어를 다시 설계할 필요가 있다. 이런 이전 판과의 단절은 친숙한 언어의 골자는 유지하면서도 새로운 개념에 맞게 언어를 다시 정비할 수 있다는 점에서 과거와의 완전한 단절을 의미하지는 않는다는 장점이 있다.
  • 확장을 하려면 확실하게 하라 - 어중간하게 기존 개념을 확장하기보다는 제대로 된 개념을 추가하는 편이 낫다. 또 이러저러한 단점을 가진 대체로 괜찮은 해법보다는 시간이 걸리고 비용이 들더라도 만족스러운 해법을 택해야 한다.
  • 가능한 소수의 사람이 일관되게 언어를 확장하라 - 많은 사람이 모이고 확장마다 다른 사람이 모여 설계해서는 배가 산으로 간다. 고집불통이더라도 일관된 철학으로 언어를 확장하는 편이 훨씬 안전하다.



위로



자바는 뭐가 그렇게 잘못되었나?

앞서 자바의 진화를 이야기하면서 다소 과격한 원칙을 거론하였다. 엄밀히 자바는 비교적 보수적으로 진화해 온 언어다. 문제는 몇 번의 변화 사이에 큰 실수를 했고 또 실수를 되풀이할 것 같다는 점이다. 자바 1.0 이후로 자바 언어에 있었던 확장은 다음과 같다.

  • 내부 클래스(innerclass)
  • 어서션(assertion)
  • 제네릭스
  • for each 루프
  • 자동 객체/기본값 간 변환(autoboxing/unboxing)
  • 타입 안전한 열거형(Enums)
  • 가변 인자(varargs)
  • 클래스 멤버 임포트(static import)
  • 애노테이션(annotation)

위에서 내부 클래스는 1.1에, 어서션은 1.4에 추가되었고 나머지는 자바 5에 추가된 내용이다. 그리고 위에 언급되지 않았지만 자바 7에서 클로저 추가를 고려하고 있다. 이들 중 비교적 단순한 문법적 편의고 자바 전반에 미치는 영향이 미비한 어서션과 for each 루프, 가변 인자, 클래스 멤버 임포트는 우선 논외로 하자. 그리고 일종의 메타데이터로 언어 자체의 의미와는 무관하고 C#에서 어느 정도 검증된 후 베껴온 애노테이션도 가볍게 제쳐두자. 남은 다섯 가지 중 “Effective Java”에 소개됐고 자바에 추가된 타입 안전한 열거형은 잘 설계된 확장이라고 생각한다. 참고로 처음 아이디어를 제안한 Joshua Bloch는 간결한 디자인으로 호평을 받은 자바 컬렉션 API의 설계자이기도 하다. 문제는 내부 클래스, 제네릭스, 자동 객체/기본값 간 변환, 클로저 등 남은 네 가지다. 사실상 주요한 추가 사항이 다 문제를 일으키거나 문제를 일으킬 소지를 안고 있다.


내부클래스

원래 자바 1.0의 AWT 컴포넌트는 서브클래스를 만들어 이벤트 발생 시 호출되는 메서드를 재정의해야 이벤트를 받을 수 있었다. 자바 1.1에서는 불필요한 서브클래스 정의를 피하기 위해 이벤트 리스너 모델을 도입했는데 리스너로 사용할 C의 콜백 함수(callback function) 같은 것이 필요했다. 이 때 마이크로소프트는 현재 .NET의 전매 특허인 대리자(delegate) 개념을 제안했는데 썬이 가볍게 무시해 주고 대신 추가한 것이 바로 내부 클래스다. 개인적으로는 내부 클래스가 JVM 수정이 거의 없다는 측면에서는 매력적이었나 몰라도 당시 자존심 살짝 접어주고 대리자를 도입하는 편이 더 좋지 않았을까 한다. 내부 클래스에는 다음과 같은 문제점이 있다.

  • 장황한 문법 : 리스너가 많이 사용되는 걸 감안하면 내부 클래스 문법은 장황하고 읽기도 어렵다. 메서드 인자로 무명 클래스를 생성해 넣을 때 ‘}’에 바로 이어 ‘)’를 닫으면서 느끼는 어색함은 10년이 넘게 자바를 쓰면서도 여전하다. 오죽하면 자바 7에 추가되는 클로저 대신 장황한 문법을 간소화한 CICE(Concise Instance Creation Expressions)를 추가하자는 제안이 있을 정도다.
  • 불필요한 확장과 제약 : 내부 클래스를 만들면서 지역 클래스처럼 거의 사용하지도 않는 모든 경우에 해당하는 문법을 정의해 불필요하게 언어를 복잡하게 만들었다. 또 정적이 아닌 내부 클래스에서는 굳이 static final 상수 필드를 정의할 수 없게 만드는 불필요한 배려까지 해 놓았다. 코딩을 해 보면 알겠지만 경우에 따라 상당히 불편하다.
  • 잘못된 구현 : 내부 클래스를 하나 정의할 때마다 별도의 클래스 파일이 생긴다. 자바는 여러 개의 클래스 파일을 모아 내부에서 참조하는 문자열 등을 공유하는 메커니즘이 없어 클래스 파일 개수가 늘어나면 코드 크기가 커지는 것은 물론이고 클래스를 로드할 때 부하도 더 걸린다. 특히 자바 ME 쪽에서는 클래스 수를 줄이기 위해 일부러 내부 클래스를 사용하지 않는 웃지 못할 일도 비일비재하다. 게다가 성능 면에서도 대리자에 비해 떨어진다.
  • 이질적인 의미 : 내부 클래스 객체가 생성될 때 감싸고 있는 클래스의 객체와 자동으로 연결된다는 점은 지금은 익숙해졌지만 똑같은 클래스 생성임에도 이질적인 의미를 부여함으로써 이해를 어렵게 만든다. 또 숨은 객체 참조 때문에 객체가 제 때 가베지 컬렉션되지 못하거나 객체 저장(serialization) 시에 문제를 일으키기도 한다.

자동 객체/기본값 간 변환

이 특성은 C#에서 베껴온 것임에도 어설프게 베껴오다 보니 문제가 발생하는 경우다. .NET에서는 자동 변환이 일어나는 타입은 모두 ValueType이라는 특수한 타입의 서브클래스로 필드를 선언하거나 지역 변수로 선언할 경우 힙에 객체가 생기지 않는 C의 구조체에 가까운 타입들이다. 따라서 자바와는 달리 다음과 같은 문제를 접할 가능성은 없다.

Integer j1 = 127;
Integer j2 = 127;
System.out.println( j1==j2);

Integer w1 = -128;
Integer w2 = -128;
System.out.println( w1==w2);

답은 true다. 자동 변환에 사용되는 Integer.valueOf(int) 메서드 구현에서 -128에서 127까지 정수 객체는 캐시를 하기 때문이다.

Integer k1 = 128;
Integer k2 = 128;
System.out.println( k1==k2);

Integer m1 = -129;
Integer m2 = -129;
System.out.println( m1==m2);

이번에는 false다. -129와 128은 캐시하지 않기 때문이다.

사실 위 경우를 제외하더라도 굳이 자동 변환을 언어에 추가해야 했어야 하나에 대해서는 의문이 있다. 이런 변환은 겉보기에는 그저 값을 대입하는 것뿐이지만 객체 생성이나 메서드 호출을 수반하기 때문에 단순 대입에 비하면 아주 느린 작업이다. 자칫 이런 요소를 이해하지 못하고 프로그래밍을 하다가는 애먼 성능 저하를 경험할 수도 있기 때문이다.


제네릭스

지금까지 확장에 비하면 제네릭스는 재앙에 가깝다. 특히 과거 내부 클래스를 추가할 때처럼 굳이 JVM 수정을 최소화하고 기존 코드와의 호환성에 집착하다 보니 언어만 복잡해지고 거의 효용이 없는 개념을 도입한 격이 되고 말았다. 또 기존 코드와의 호환성을 유지하다 보니 기존 API에 쉽게 제네릭스를 적용할 수 있게 되면서 안 쓸 수도 없게 만들어 버렸다. 제네릭스 사용 시 겪을 수 있는 문제점에 대해서는 다음 글을 참고하라(http://www.ibm.com/developerworks/java/library/j-jtp01255.html). 여기서는 큰 견지에서 문제점을 지적해 보겠다.

  • 잘못된 구현 : 제네릭스는 JVM 수정 없이 완전히 javac 수준에서 구현되었다. 소스 코드를 컴파일할 때만 제네릭스로 명시된 조건을 검사하고, 실행할 때는 타입 정보가 모두 사라진다. 이 때문에 타입 인자로 주어진 타입의 객체를 생성할 수 없다든지, 한번 Object 변수에 넣고 나면 다시 제네릭스를 사용하는 타입으로 변환할 수 없다든지 하는 이상한 제약이 생긴다. 자연스럽게 이렇게 되어야 하지 않나 라고 생각한 코드가 컴파일되지 않는 일이 비일비재하다.
  • 불필요한 복잡성 : 제네릭스의 와일드카드 등의 기능에 대해 읽다 보면 과연 평범한 자바 개발자가 제네릭스를 100% 이해하고 쓸 수 있을 지 회의가 생긴다. Enum <T extends Enum<T>> 같은 표현을 제대로 이해할 사람이 몇 명이나 되겠는가?

제네릭스를 컴파일러 수준에서 구현하기로 한 것은 JVM을 수정할 경우 다른 JVM 업체가 JVM을 갱신하기 어려울 것이라는 점과 제네릭스를 사용한 코드와 기존 코드를 아무 문제 없이 혼용할 수 있어야 한다는 두 가지 상황을 고려한 것이라고 한다.

개인적으로는 사실상 서버와 데스크톱에서 썬의 핫스팟 VM이 유일한 구현인 상황에서 JVM 수정을 극도로 꺼렸다는 것이 이해가 가지 않는다. 게다가 최근에는 OpenJDK 형태로 소스가 공개되면서 스크립트 언어 지원을 위해 JVM을 수정하는 프로젝트까지 생긴 마당이다. .NET처럼 시간이 걸리더라도 제대로 된 구현을 했다면 제네릭스의 유용성이 높아졌을 것이다.

그리고 기존 코드와 제네릭스를 사용한 코드의 호환성 면에서도 JVM을 수정한 뒤 java.lang.Class 등 굳이 제네릭스를 쓸 필요가 없는 API에 제네릭스를 적용하기보다는 그저 .NET처럼 일부 API만 제네릭스를 사용하도록 새 버전을 추가하거나, JVM이 제네릭스를 사용하지 않는 코드를 실행할 때는 제네릭스를 적용한 클래스의 타입 인자에 java.lang.Object를 자동으로 명시한 걸로 처리할 수는 없었나 하는 아쉬움이 남는다. 사실 그토록 지키고자 했던 호환성 자체가 다소 허무한 것이 제네릭스를 사용하는 코드에서 그렇지 않은 코드를 호출해 사용할 때는 앞서 언급한 제네릭스 구현의 한계로 수많은 컴파일 오류 속에서 허우적거려야 한다.


클로저

클로저는 아직 규격이 완성되지는 않았다. 하지만 초안을 보면 그다지 아름답지 않은 또 하나의 이상한 문법이 추가되어 가는 느낌이다. 게다가 이번에도 JVM 수정 없이 javac 수준에서 구현하는 것으로 가닥이 잡혔다. 현재 클로저 이슈에 대한 규격 제안은 크게 네 가지다. 이에 대한 비교는 “Comparing Inner Class/Closure Proposals”를 참고한다.

그 중 최종 규격의 기반으로 논의되는 것은 BGGA인데 통상적인 클로저의 특성을 모두 갖춘 제안이다. 참고로 BGGA는 초기 제안자 이름의 머리글자를 딴 것인데 자바 언어의 확장에 비교적 보수적이던 James Gosling이 두 개의 G중 하나라는 점이 놀랍다(Gilad Bracha, Neal Gafter, James Gosling, Peter von der Ahé). 개인적으로는 굳이 이 정도 복잡도의 개념을 자바에 꼭 추가해야 하는지 의심스럽다. 또, 자바 컴파일러 수준에서 프로그래머가 모르는 인터페이스를 생성하고 각종 코드를 토해내는 것에 대해서는 거부감이 있다.

그 외 C3S(Clear, Consistent, and Concise Syntax for Java), CICE(Concise Instance Creation Expressions)는 간결한 문법을 제안하고 새로운 개념의 추가를 자제한 경우이고, 간단한 문법과 더불어 FCM(First Class Method)은 메서드 타입을 추가한 경우다.

개인적으로는 당장에는 CISE처럼 가능한 언어에 새로운 개념을 추가하지 않는 안으로 만족했으면 한다. 다만 클로저 자체는 아주 유용한 개념이므로 JVM에서 직접적으로 메서드를 별도 개체로 다룰 수 있게 확장하고 그를 이용해 도입되는 쪽이 바람직하다고 생각한다. 해당 변경은 스크립트 언어 지원을 위해 이미 논의되는 내용이다. 그렇게 해야 성능 향상을 볼 수 있고 JVM 상의 다른 언어 구현이 쉽고 그 언어에서 자바 API를 사용할 때도 상호 운용이 용이할 것이다.



위로



바람직한 자바의 진화를 말한다

지금까지 살펴봤듯이 자바 언어에 생긴 주된 변화는 잘못된 선택으로 그다지 성공적이지 못했고 또 당장 자바 7에서도 그다지 희망적이지 않다. 그렇다면 자바 언어와 JVM 등 자바 플랫폼 전반에서 쳐다봤을 때 바람직한 진화의 방향은 무엇일까?

  • JVM도 함께 진화해야 한다 - JVM은 기존 컴파일러의 뒤 부분을 JVM에 옮겨 놓은 것과 같다(지난 칼럼인 “JVM, .NET CLR, 구글 안드로이드... 당신이 VM에 대해 알아야 하는 모든 것....”(http://www.ibm.com/developerworks/kr/library/dwclm/20080212/을 참고한다). 저수준의 CPU에서 서로 다른 언어 간의 함수 호출, 예외 처리 등을 담보하려면 ABI(Application Binary Interface)가 정의되어야 하지만 JVM은 그 자체가 ABI와 유사하게 클래스, 예외 등의 개념에 대한 추상화를 제공한다. 따라서 효율적이고 효과적인 구현을 꾀하고 JVM 상의 다른 언어가 자바와 자연스럽게 연동하기 위해서는 반드시 JVM도 함께 진화해야 한다. .NET은 이런 면에서 자바에 비해 현명한 진화를 꾀하고 있다. 2.0까지 제네릭스 등의 개념을 충분히 녹여 넣고 3.5에 이르면서는 적절한 컴파일러 쪽 구현을 통해 진화를 해 나가고 있다.
  • 사실상 JVM 구현이 하나인 상황으로 만들자 - “One size doesn’t fit all”이라는 말이 있기는 하지만 구현 자체가 하나인 대부분의 스크립트 언어나 .NET에 비해 자바 플랫폼이 치러야 하는 대가가 너무 크다. 임베디드 시스템 쪽에는 실시간성 보장 유무 등 상황에 따라 필연적으로 여러 자바 구현이 필요하지만 최근 자바 SE의 변화를 그대로 수용할 필요가 없으니 문제가 되지 않는다. 하지만 데스크톱이나 서버에서 이미 소스까지 공개된 마당에 다른 JVM 구현이 필요할까?
  • 스크립트 언어는 물론이고 다른 주류 언어를 키워야 한다 - 앞서 경우에 따라서는 이전과의 호환성을 포기할 필요도 있다고 했다. 스칼라(Scala)처럼 자바 언어와의 상호 연동은 가능하되 처음부터 새로 설계되는 언어가 많이 나타나야 하고 지나치게 자바 언어를 확장하는 데 집착하지 말아야 한다. 이미 움직임이 있지만 .NET처럼 JVM이 하나의 언어가 아닌 다양한 언어를 지원하는 하부 구조로 자리 잡을 때가 오지 않았나 한다.
  • 서두르지 말아야 한다 - 제네릭스의 예에서 보듯 당장 없어서 큰 일이 생기지 않는데 JVM 변경을 걱정하는 것은 어떻게 보면 우스운 일이다. 또 C#에 있는 기능이 없어도 당장 자바가 버림을 받지는 않는다. 시간이 걸리더라도 제대로 된 해법을 찾아가야 한다.
  • 언어 확장은 JCP(Java Community Process) 모델을 따르지 말았으면 좋겠다 - 설계 철학이 다른 다양한 전문가가 협의하면 통상 가장 최적의 해법이 나오기보다는 일관성이 떨어지고 여러 제안을 버무려 어중간한 선택을 하기도 한다. 여담이지만 개인적으로는 Joshua Bloch 같이 간결한 설계를 선호하는 사람이 리더였으면 좋겠다.


위로



마치면서

이번 칼럼에서는 C#에 자극 받은 후 꾸준히 진화가 시도되는 자바의 변화에 대해 다소 비판적인 관점에서 살펴보는 기회를 가졌다. 매번 자바가 판올림(버전 업그레이드)할 때마다 그저 새로운 기능을 받아 들고 뿌듯해 하기만 했다면 이제부터는 관점을 바꿔 보자. 이 칼럼으로 자바의 진화 방향이 바뀌지는 않겠지만 각자 자바 커뮤니티의 일원으로 하다못해 썬의 버그 퍼레이드(bug parade)에 투표할 수도 있고, 자바 플랫폼을 활용하면서 스칼라 등 다른 대안을 바라 보는 시각을 바꿔볼 수 있을 것이다.


이 문서 북마킹 하기

mar.gar.in mar.gar.in naver naver eolin eolin del.icio.us del.icio.us



위로


[지난 developerWorks Column 보기]

사이트 여행

dW 커뮤니티
포럼 | 블로그 | Spaces
dW Student Community

로컬 컨텐츠

행사 및 세미나

기획 기사

개발자 입문

튜토리얼 및 교육

TOP 10 인기자료

SW 다운로드

RSS 피드

뉴스레터
 
  
자바스크립트가 작동이 중지되었습니다. 이 기능을 수행하시려면 브라우저에서 자바스크립스트를 작동시켜 주시거나 이곳을 클릭해주세요.

Special offers
Screencast
IBM SOA Sandbox 시험판
dW Student Community
로보코드
코드 트레이닝


    IBM 소개 개인정보 보호정책 문의