회사에서 Handlebars.java 와 관련된 이슈가 공유가 되었다.

Handlebars 가 Javascript 버전과 Java 버전의 #with helper 결과가 동일하지 않습니다.

우선 이 이슈 버그를 해결한 코드는 필자의 github 저장소 https://github.com/powerumc/handlebars.java.bug-fix 에 커밋 되어 있고, 원본 저장소의 이슈 번호 #314, Pull Request #315 에 등록 되었다.

Handlebars vs Handlebars.java

이 테스트에서 사용되는 handlebars 데이터는 다음과 같습니다.

{ "company": { "ko": "쿠팡",
               "en": "Coupang" }}

그리고 handlebars 템플릿은 다음과 같다.

{{#with company}}
  우리 회사는 {{ko}}
  My company is {{company.en}}
{{/with}}
- Javascript handlebars 결과
  우리 회사는 쿠팡
  My company is 
- Handlebars.java 결과
  우리 회사는 쿠팡
  My company is Coupang

문제 원인

Handlebars.java 를 반나절 정도 분석하고 나니 대충 (정말 대충) 어떻게 흘러가는 지 조금 이해가 되었다.

문제는 Handlebars.java 에서는 #with helper 에서 value 값을 resolving 하지 못하면 parent object (=context) 에서 찾는다. #with helper 에게 전달된 데이터를 model 이라고 하면 전달된 데이터 전체를 context 로 불린다. 그래서 model 의 parent 는 context 가 된다.

위의 상황을 보면 My company is {{company.en}} 와 같은 코드의 model 에서 {#with company}}company.en을 찾지 못해서 context 에서 company.en을 찾게 된다.

문제 해결 방법

문제의 원인을 파악했으니 코드를 디버깅해 알겠지만 튜닝 포인트가 매우 다양하다. #with helper 전체를 뜯어 고칠 수 도 있고, 내부적으로 CompositeValueResolver 를 튜닝하거나, 그 외에 다양한 방법으로 고칠 수 있다.

가장 간단하게 이 버그를 픽스하기 위해 또 반나절 정도를 적용해 보고, 가장 적은 코드로 튜닝할 수 있는 코드를 만들었다.

Options.java 코드에서 wrap(final Object) 메서드를 다음과 같이 픽스하였다. 딱 세 줄만 추가해 주면 된다.

public Context wrap(final Object model) {
  if (model == context) {
    return context;
  }
  if (model == context.model()) {
    return context;
  }
  if (model instanceof Context) {
    return (Context) model;
  }
  if (model.getClass() == LinkedHashMap.class) {  // 이 코드부터...
    return Context.newContext(model);
  }                                               // 여기까지 추가...
    return Context.newContext(context, model);
  }

그럼 아래의 이슈가 되는 테스트 코드가 무사히 통과하고,Handlebars.java 의 모든 테스트도 통과한다.

public class Issue314 extends AbstractTest {

  @Test
  public void withHelperSpec() throws IOException {
    String context = "{ obj: { context: { one: 1, two: 2 } } }";

    shouldCompileTo("{{#with obj}}{{context.one}}{{/with}}",     context, "1");
    shouldCompileTo("{{#with obj}}{{obj.context.one}}{{/with}}", context, "");
 }
}


Posted by 땡초 POWERUMC

댓글을 달아 주세요

얼마 전 자바8 람다 나머지 이야기를 보면서 평소 필자가 알던 Java와는 완전히 달라 보였다. 필자가 알고 있던 Java는 보수적이지만 정통적이라고 생각 해왔는데 과감히 이 생각을 깨졌다.



Java 8 Lambda 에 대해 궁금한 부분은 필자가 예전에 작성한 아티클을 참고하기 바란다.

Java 8 Interface 변경 사항 default 키워드

오라클의 Defining an Interface 문서에 의하면 Java Interface의 정의는 변경되었다. Java Interface는 abstract methods, default methods, static methods 를 정의할 수 있다고 한다.

The interface body can contain abstract methods, default methods, and static methods.

명세에 따르면 Java Interface에 default 키워드를 통해 메서드를 구현할 수 있다. 또, 이를 구현하는 클래스는 Interface의 메서드를 @Override 할 수 있다.

Oracle 문서에서 아래와 같은 예제를 볼 수 있다.

public interface DoIt {
   void doSomething(int i, double x);
   int doSomethingElse(String s);
   default boolean didItWork(int i, double x, String s) {
       // Method body 
   }  
}

Java 8 Interface, 그 인터페이스는 그 인터페이스가 아니다.

원래 객체지향 언어에서 Interface는 그 시그너처와 선언이 변하지 않는다는 것을 전제로 하여 다형성(polymophism)을 정의하는 객체간의 규약이다. 그러나 Java8 Interface는 Interface를 업그레이드하는 개념을 도입하였다.

Now users of your code can choose to continue to use the old interface or to upgrade to the new interface.
Alternatively, you can define your new methods as default methods

객체지향 언어의 객체 규약을 정의함에 있어 항상 논의 되는 것이 Interface와 Abstract 클래스 둘 중 어떤 것을 쓸 것이가에 대한 것이다. 어떤 것을 사용해도 무방하겠지만 객체 간의 규약(서로간의 약속)은 Interface 로 정의해야 한다. 구현체가 없는 온전한 인터페이스 역할을 해야 하기 때문이다.

특히 분산 객체(distributed object)는 분산된 두 객체의 규약, 즉 Interface 의 프록시(proxy)를 통해 분산 객체를 사용한다. Java8 Interface는 구현체가 포함될 수 있으므로 더 이상 분산 객체를 정의하는 규약으로 부적합하다.

이는 반드시 분산 객체 뿐만 아니라, 일반적인 객체로서도 문제의 소지가 충분하다. 아래는 이런 현상을 약간 억지스럽게 구현한 코드이다.

이를 통해 알 수 있는 것은 Java 8 Interface 로 규약된 분산 객체에 구현 코드가 포함이 되므로 더 이상 ‘규약’이라는 표현 자체가 규약이 될 수 없을 것 같다.

interface Duck {
    void say();
    void walk();
}

interface MyDuck extends Duck { }

interface YourDuck extends Duck {
    default void walk() { say(); }
}

class MyDuckImpl implements MyDuck {
    @Override
    public void say() { System.out.println("MyDuck: 꽥~"); }

    @Override
    public void walk() { System.out.println("MyDuck: 뒤뚱~"); }
}

class YourDuckImpl implements YourDuck {
    @Override
    public void say() { System.out.println("YourDuck: 꽥꽥꽥~"); }
}

public class Main {
    public static void main(String[] args) {
        new MyDuckImpl().say();
        new YourDuckImpl().say();

        new MyDuckImpl().walk();
        new YourDuckImpl().walk();
    }
}

// 결과
MyDuck: 꽥~
YourDuck: 꽥꽥꽥~
MyDuck: 뒤뚱~
YourDuck: 꽥꽥꽥~

상대적인 취약해진 Java 8 Interface

일반적으로 실행 파일(executable file)이나 라이브러리(library) 파일은 코드와 데이터 등을 구조적으로 저장하고 링크(link) 과정을 거친 바이너리 파일이다. 반면 Java는 각 클래스 파일을 컴파일하면 .class 확장자를 가진 바이너리 파일을 출력한다.

일반적으로 코드와 데이터가 모두 포함된 바이너리 파일(일반적으로 portable executable)은 가리키는 offset 에 따라 RVA(relative virtual address)/VA(virtual address)와 매핑된다. 따라서 바이너리 파일을 비정상적으로 수정(삽입/삭제)하면 offset 정보도 함께 변경해 주어야 한다. 그렇지 않으면 메모리에 로드될 때 코드가 정상적으로 동작하지 않을 수 있다.

반면 Java 의 컴파일 된 바이너리는 클래스 별로 컴파일되어 .class 파일로 출력되기 때문에 이런 offset 정보가 필요가 없다. 컴파일된 코드가 논리적인 구조에 따라 물리적인 파일을 생성하지 않는다. 할 수 있는 건 Stack Size 정도 늘리거나 줄일 수 있다.

따라서 Java 8 Interface 의 .class 파일에 악의적으로 default 메서드 구현을 임의로 수정하면 간편하게 응용 프로그램 전체에 영향을 끼치도록 소정의 목적을 달성할 수 있다. (상대적으로 Java의 바이너리가 더 취약하다는 의미이다.)

다시 불거지는 다중 상속 문제

Java 8 Interface는 다시 다중 상속 문제에서 자유롭지 못하게 되었다.

Java는 단일 상속만 가능하지만 Interface는 다중으로 구현할 수 있다. 이는 C++의 다중 상속으로 인해 발생하는 문제점과 복잡성으로 Java는 단일 상속 구조를 택하게 되었다.

다중 상속으로 발생하는 모호성에 대해서도 Java 8 Interface 가 내놓은 해결책은 C++ 의 해결책과 다를 바가 없다.

아래는 Java 8 Interface 다중 상속(구현)으로 인한 모호성 문제 해결 방법이다.

public interface OperateCar {
    default public int startEngine(EncryptedKey key) {
        // Implementation
    }
}
public interface FlyCar {
    default public int startEngine(EncryptedKey key) {
        // Implementation
    }
}

public class FlyingCar implements OperateCar, FlyCar {
    public int startEngine(EncryptedKey key) {
        FlyCar.super.startEngine(key);
        OperateCar.super.startEngine(key);
    }
}

아래는 C++ 의 다중 상속으로 인해 발생하는 모호성을 해결 하는 방법이다.

#include <iostream>  
using namespace std;  

class OperateCar {  
public:  
    int startEngine(EncryptedKey *key) {  
        // Implementation
    }  
};  

class FlyCar {  
public:  
    int startEngine(EncryptedKey *key) {  
        // Implementation
    }  
};  

class CCC : public OperateCar, public FlyCar {  
public:  
    int startEngine(EncryptedKey *key){  
        OperateCar::startEngine(key);  
        FlyCar::startEngine(key);
    }  
};  

위의 Java와 C++ 소스 코드를 비교해 보면 결국 Java 8 Interface는 다중 상속의 개념이 다시 도입된 것을 알 수 있다.

참 아이러니 하다.

결론

어느 것이 답이 될 수는 없다. 이전까지 지향하고 고수하던 객체지향이 반드시 정답은 아닐 것이다. 다만, Java가 추구하던 Interface의 개념이 그 동안 알고 있던 개념과 이치에 맞지 않고, 어쩔 도리 없이 구현한 스팩일 수 있다.

필자처럼 C# 5.0의 가장 최신 스팩까지 쭉 경험한 개발자라면 도저히 용납할 수 없을 지도 모르겠다.

까마득한 2007년도에 릴리즈한 C# 3.0 스팩의 Lambda, LINQ, Extension Methods 의 언어 스팩과 비교해보면 Java 8 는 너무나도 기대에 미치지 못한 방법으로 구현해 놓았다는 게 실망스럽다.

아직 Java 8 모든 것을 훓어 본 것이 아니므로 필자가 오해하고 있는 부분은 정정해 주길 바란다. (이념, 철학, 사상적인 부분은 사양한다)

참고로 이해를 돕기 위해 2007년도 C# 3.0 스팩과 2014년 Java 8 스팩을 비교하면 다음과 같다.

  • C# Lambda = Java Lambda
  • C# Extension Methods = Java Stream API
  • C# LINQ = Java 엔 없다!


Posted by 땡초 POWERUMC

댓글을 달아 주세요

  1. 경준씨 2015.06.30 10:16 Address Modify/Delete Reply

    메소드가 void startEngine면 아래과 같이 적어도 됩니다.
    public class FlyingCar implements OperateCar, FlyCar {
    public void startEngine(EncryptedKey key) {
    FlyCar.super.startEngine(key);
    OperateCar.super.startEngine(key);
    }
    }
    그러나 메소드가 int startEngine라면
    public class FlyingCar implements OperateCar, FlyCar {
    public int startEngine(int key) {
    FlyCar.super.startEngine(key);
    return OperateCar.super.startEngine(key);
    }
    }
    이런식으로 자바의 인터페이스는 무조건 Override를 해야합니다.
    이 Override를 상속할 때 return이 void값은 둘다 받을 수 있지만 부모의 return 값은 둘 중의 하나만 명시적으로 선택해야 return값으로 쓸 수 있습니다.

  2. brian 2016.09.09 17:18 Address Modify/Delete Reply

    감사합니다. 잘보고 갑니다.

  3. 이창현 2016.11.10 16:12 Address Modify/Delete Reply

    C#의 LINQ 를 Java 8 의 Stream 에 대응시킬 수 있지 않나요?

    • 땡초 POWERUMC 2016.11.11 09:32 신고 Address Modify/Delete

      LINQ 는 C# 언어와 통합된 쿼리식을 말합니다.
      가령 이런거죠.
      var query = from i in list
      select i;

      Java StreamAPI 와 대응되는 것은 C# 의 Where, Select 와 같은 확장 메서드라고 보시면 될 것 같습니다.

  4. 2017.04.05 19:56 Address Modify/Delete Reply

    비밀댓글입니다

프로그래밍은 언제나 숫자와의 경쟁인 것 같다. 반올림이 되느냐, 부동소수점이냐, 정수 오버플로우(integer overflow) 등은 백발이 되어 코드를 만질 때 까지 항상 따라다니는 문제가 될 것이다.

MySQL 날짜 관련 이슈

얼마 전 필자가 다니는 회사에서 발생한 데이터베이스 관련 이슈로 다음과 같은 문제가 발생하였다. 아래는 MySQL 관련 문제에 대하여 공유된 내용이다.

MySQL 5.6.4 부터 시간값 저장시 밀리세컨드를 지원한다. 하지만 DATETIME의 경우 길이가 6일 경우에만 가능하다. 그런데, DATETIME 타입(이는 DATETIME(4)와 같다)일 경우 밀리세컨드 부분을 반올림(round)하는 버그가 있다. #68760
이 버그로 인해 1999년 12월 31일 23시 59분 59초 500ms 같은 값을 DATETIME 타입에 입력하면 반올림되어 2000년 1월 1일 0시 0분 0초 0ms 가 돼 버리는 현상이 발생한다.

MySQL Community Server 의 C 언어 소스코드를 보면 날짜와 관련된 데이터를 다루는 세 가지가 데이터 타입이 있다. MYSQL_TYPE_DATE, MYSQL_TYPE_DATETIME, MYSQL_TYPE_TIMESTAMP.

// MySQL 데이터 타입  
/*****************/  
#define CLIENT_MULTI_QUERIES    CLIENT_MULTI_STATEMENTS      
#define FIELD_TYPE_DECIMAL     MYSQL_TYPE_DECIMAL  
#define FIELD_TYPE_NEWDECIMAL  MYSQL_TYPE_NEWDECIMAL  
#define FIELD_TYPE_TINY        MYSQL_TYPE_TINY  
#define FIELD_TYPE_SHORT       MYSQL_TYPE_SHORT  
#define FIELD_TYPE_LONG        MYSQL_TYPE_LONG  
#define FIELD_TYPE_FLOAT       MYSQL_TYPE_FLOAT  
#define FIELD_TYPE_DOUBLE      MYSQL_TYPE_DOUBLE  
#define FIELD_TYPE_NULL        MYSQL_TYPE_NULL  
#define FIELD_TYPE_TIMESTAMP   MYSQL_TYPE_TIMESTAMP  
#define FIELD_TYPE_LONGLONG    MYSQL_TYPE_LONGLONG  
#define FIELD_TYPE_INT24       MYSQL_TYPE_INT24  
#define FIELD_TYPE_DATE        MYSQL_TYPE_DATE  
#define FIELD_TYPE_TIME        MYSQL_TYPE_TIME  
#define FIELD_TYPE_DATETIME    MYSQL_TYPE_DATETIME  
#define FIELD_TYPE_YEAR        MYSQL_TYPE_YEAR  
#define FIELD_TYPE_NEWDATE     MYSQL_TYPE_NEWDATE  
#define FIELD_TYPE_ENUM        MYSQL_TYPE_ENUM  
#define FIELD_TYPE_SET         MYSQL_TYPE_SET  
#define FIELD_TYPE_TINY_BLOB   MYSQL_TYPE_TINY_BLOB  
#define FIELD_TYPE_MEDIUM_BLOB MYSQL_TYPE_MEDIUM_BLOB  
#define FIELD_TYPE_LONG_BLOB   MYSQL_TYPE_LONG_BLOB  
#define FIELD_TYPE_BLOB        MYSQL_TYPE_BLOB  
#define FIELD_TYPE_VAR_STRING  MYSQL_TYPE_VAR_STRING  
#define FIELD_TYPE_STRING      MYSQL_TYPE_STRING  
#define FIELD_TYPE_CHAR        MYSQL_TYPE_TINY  
#define FIELD_TYPE_INTERVAL    MYSQL_TYPE_ENUM  
#define FIELD_TYPE_GEOMETRY    MYSQL_TYPE_GEOMETRY  
#define FIELD_TYPE_BIT         MYSQL_TYPE_BIT  

날짜가 포함되는 데이터 타입에 대해 코드를 분석해 본 결과 날짜 데이터를 조작하여 저장하는 코드를 찾지 못했다. 꼼꼼하게 분석한 것은 아니기 때문에 혹시 놓친 부분도 있을 거라고 생각한다.

MySQL Server에서 문제를 발견하지 못했다면,다음 봐야 할 것이 MySQL Client와 MySQL Connector/J 소스 코드인데, MySQL Connector/J 에서 이슈와 직/간접적으로 관련된 코드를 발견하였다.

MySQL Connector/J

MySQL Connector/J의 소스 코드에서 /src/com/mysql/jdbc/TimeUtil.java 파일에서 한 가지 이슈를 재연할 수 있는 코드를 발견했다. 메서드 이름  public static Time changeTimezone(…) 메서드를 살펴보면 클라이언트와 서버 간에 TimeZone이 다른 경우 이를 보정하는 연산이 포함된다.

다음은 TimeZone.java의 코드의 일부분이다.

Calendar fromCal = Calendar.getInstance(fromTz);  
fromCal.setTime(tstamp);  

int fromOffset = fromCal.get(Calendar.ZONE_OFFSET)
               + fromCal.get(Calendar.DST_OFFSET);  
Calendar toCal = Calendar.getInstance(toTz);  
toCal.setTime(tstamp);  

int toOffset = toCal.get(Calebndar.ZONE_OFFSET)
              + toCal.get(Calendar.DST_OFFSET);  
int offsetDiff = fromOffset - toOffset;  
long toTime = toCal.getTime().getTime();  

if (rollForward || (conn.isServerTzUTC() && !conn.isClientTzUTC())) {  
    toTime += offsetDiff;  
} else {  
    toTime -= offsetDiff;  
}  

Timestamp changedTimestamp = new Timestamp(toTime);  

만약, 클라이언트와 서버 사이에 0.001ms 시간 차이가 생긴다면, TimeZone은 13이라는 값의 차이가 발생한다. 클라이언트와 서버의 시간 차이 기준은 MySQL Server에서 열린 세션(session)을 기준으로 판단한다. 혹시나 클라이언트와 서버의 TimeZone이 상이하게 설정된 경우 어마 어마한 시간 차이가 생기게 된다.

다음과 같이 필자가 의도적으로 0.001ms 차이가 발생하는 경우 MySQL Connector/J 가 DateTime을 교정하는 것을 확인하였다. (단, getTimeFast 메서드는 MySQL Connector/J 에 포함된 메서드임)

Calendar calendar = Calendar.getInstance();  
calendar.set(1999, 12, 31, 23, 59, 59);  
calendar.setTimeInMillis(nanos);  

System.out.println(getTimeFast(0, bits, 12, 14, calendar, tz, true));  

// output  
2000-01-01 00:00:00.0  

결론

MySQL 날짜 관련 이슈의 직접적인 발생 원인은 찾지 못했다. 하지만 MySQL Connector는 MySQL Client와 쌍으로 움직이니 간접적으로 영향을 미칠 수 있을 것이다.

현재까지 살펴본 바로는 가장 가능성이 있는 이 이슈/버그는 MySQL Connector/J와 MySQL Client일 가능성이 가장 크고, MySQL Server 내부 버그일 가능성은 다소 낮다고 본다. 이 이슈/버그 밑에 코멘트에 MySQL .NET Connector를 사용하는 경우도 문제가 있다고 하니.. 어쨌거나 저쨋거나..

Posted by 땡초 POWERUMC

댓글을 달아 주세요

최근 재미있는 글을 봤다. C 언어로 모바일을 위한 API 서버를 만들었는데, 이에 대해 댓글의 토론이 가관이 아니다. 물귀신들이 들러 붙고 난리도 아니다.

  1. 아파치 모듈로 개발된 API 서버, 이음 베이론을 소개합니다.
  2. C언어로 API 서버 개발, 생각보다 나쁘지 않아요

글의 결론은 ‘모바일 API 서버를 C 언어로 만드니 성능이 좋네요’.. 이에 대항하는 물귀신들은 ‘나를 납득시킬만한 근거를 대라’, ‘C언어로 만들었다고 자랑질이냐’ 등등…



모든 사람이 경험도 다르고, 깊이도 다르니 글쓴이야 모든 눈높이에 맞춰 대응하기도 힘들겠다. 그냥 나는 내 생각대로 얘기하자면…

  • 글쓴이가 모바일 API를 C언어로 만든 글은 필자가 보기에 자랑질도 아니고, 이런 사례를 만들었는데 놀랍더라.. 정도인 것 같은데, 글쓴이가 무슨 비윤리적인 짓을 한 것 마냥 댓글러들의 오두방정이 지나치다. C언어로 만들어서 만족한다는 데 왜 주변에서 C로 만들었냐고 오두방정일까… C언어로 만들면 우리나라 박대통의 창조경제에 어긋나기라도 하나?

  • DB 부하가 가장 많은 상태에서 C언어로 API 성능을 극대화 한 것 자체 무용론… 댓글러들은 무슨 ’하드웨어 스팩을 대라..!’는둥, 저렇게 차이가 나는데 ‘프로파일링을 해봤느냐.. 당연히 해봐야 하는거 아닌감?’ 하는 뉘앙스들… 글쓴이가 자기들 따까리도 아니고… 프로파일링 해봤는 지 궁금하면 직접 자바가 왜 느린지 프로파일링 해보는 게 더 빠르지 않을까 생각드는데…

  • C는 시스템 프로그래밍에 적합한 로우레벨, 자바나 트랜드한 스크립트 언어들은 고급언어… 정말 욕 한 바가지 하고 싶다. 정말 지랄 옆차기도 제대로 헛발이다. 초기에 어느 정도 프레임과 API가 정의되면, API 코딩하는 건어느 언어로 하던 그 표현력은 거의 비슷하다. 10줄 짜리 1줄로 표현한다고 생산성이 좋고, 유지보수, 리팩토링까지 좋은 건 아니다. 언젠가 요구 사항으로 인해 1줄 짜리를 10줄로 풀어낼 날이 올거다.

  • C/C++/Objective-C 등 C 계열의 언어로 만들어진 라이브러리가 없다고들 하는데 C 계열의 오픈 소스가 가장 많다. (근거는 직접 찾아보세요) 이제 슬슬 트랜드의 정점을 찌르는 파이썬, 루비, 자바스크립트 등의 스크립트 언어들의 오픈 소스를 다 합쳐도 C 계열을 추월하지 못한다. 물론 댓글러들의 눈높이에 맞는 C 계열 오픈 소스는 그리 많지 않을 뿐이다.

  • 모바일 API 서버에 대한 글이라 웹 개발자만 우루루 죽자고 달라 붙는다. C언어로 만든 API 서버가 자바 서버 10대를 5대로 줄인거면 좋은 성과 아닌가? 100대라면 산술적으로 무료 50대나 줄일 수 있는데도… C언어로 만들면 마치 유지보수 헬게이트라고 하는데, 필자는 JavaScript가 최고의 헬게이트 아닌가 뼈저리게 느낀다.

  • 나는 개발자들이 포지셔닝된 트랜드를 어느 정도 따라갈 수 있으면, 더 이상 죽자살자 트랜드를 쫓지 않길 바란다. 그래봐야 빨리 은퇴하는 길이고, 나이들면 더 할게 없어질거다. 트랜디한 기술은 치고 올라오는 귀염둥이 신입들에게 넘기고…

    그때가 되어도 귀염둥이들과 트랜디한 기술 가지고 ‘그거 써봤니?’, ‘써보니 어떻드라’, ‘버그 있더라’ 농담 따먹고 놀 순 없지 않는가…?
 
 본격적으로 개발을 하던 시점에서 트랜드는 천천히 따라가고, 점점 그 이전 기술을 공부할 필요가 있다고 본다. 소프트웨어 공학도 공부하고, C 언어, 그리고 어셈블리어, 그리고 운영체제까지… 그럼 댓글러들이 하는 토론보다 더 발전적인 이야기와 비전이 보일거다.


Posted by 땡초 POWERUMC

댓글을 달아 주세요

  1. jh.l 2014.01.23 06:59 Address Modify/Delete Reply

    언어는 언어일뿐

  2. jongwook 2014.01.23 11:06 신고 Address Modify/Delete Reply

    안녕하세요 귀여워해주셔서 감사합니다!

  3. sebatyler 2014.01.23 11:53 Address Modify/Delete Reply

    좋게 봐주셔서 감사합니다.

  4. 전호진 2014.01.23 15:16 Address Modify/Delete Reply

    승리한 병신이 되라!
    멋져요..

  5. snail 2014.01.24 01:12 Address Modify/Delete Reply

    분명 생산성 좋다는 언어인데도 코드를 스파게티로 만들어두는 사람 보면서 느낀게, 역시 잘 짜는 사람은 뭘 해도 이쁘게 짜겠구나더라고요..

  6. Tae-Ho 2014.10.14 18:09 신고 Address Modify/Delete Reply

    ㅎㅎ c를 괜히 저평가하려는 개발자들이 좀 있습니다. 그들에겐 이유란 없습니다. 이유가 있다면 자기가 안해봤다는거...

  7. 조제 2016.01.25 12:40 Address Modify/Delete Reply

    좋은글 잘 읽었습니다

Gradle 로컬 캐시로 인한 빌드 실패

얼마 전 회사에서 Java 버전을 Java 7 버전으로 업그레이드 했다. 이에 따라 JDK, Tomcat 7을 구성하고 언어 스팩을 @1.7 버전으로 설정한 후 다음과 같은 오류가 발생하였다.

MCPOWERUMC:coupang powerumc$ ./gradlew

FAILURE: Build failed with an exception.

* What went wrong:
A problem occurred configuring root project 'coupang'.
> Could not resolve all dependencies for configuration ':classpath'.
   > Timeout waiting to lock artifact cache (/Users/powerumc/.gradle/caches/artifacts-24). It is currently in use by another Gradle instance.
     Owner PID: 7306
     Our PID: 8436
     Owner Operation: resolve configuration ':classpath'
     Our operation: resolve configuration ':classpath'
     Lock file: /Users/powerumc/.gradle/caches/artifacts-24/artifacts-24.lock

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 1 mins 2.294 secs 

아마도 필자에게 발생하는 오류로 봐선 머신의 환경적인 요소가 문제가 되었나보다. 오류의 주 원인이라고 콘솔에 출력된 Timeout waiting to lock artifact cache 메시지로 보아 다른 프로세스에서 해당 파일을 점유하는 것이 아닐까 생각할 수 있다. 하지만, 프로세스를 kill 또는 머신을 재부팅 한 후에도 같은 오류가 발생한다.

따라서 간단하게 Gradle 로컬 캐시를 지우기로 했다.

Gradle 로컬 캐시 제거 후 빌드

구성된 Gradle의 로컬 캐시는 사용자의 홈 디렉토리의 .gradle 디렉토리에 캐싱된다. 다음의 명령으로 로컬 캐시를 지워보자.

MCPOWERUMC:coupang powerumc$ rm -R /Users/powerumc/.gradle

그리고, 다시 Gradle 빌드를 수행하면 구성요소를 다운로드 후 다음과 같이 정상적으로 빌드가 완료된다.

MCPOWERUMC:coupang powerumc$ ./gradlew
Downloading http://services.gradle.org/distributions/gradle-1.6-bin.zip
...................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Unzipping /Users/powerumc/.gradle/wrapper/dists/gradle-1.6-bin/72srdo3a5eb3bic159kar72vok/gradle-1.6-bin.zip to /Users/powerumc/.gradle/wrapper/dists/gradle-1.6-bin/72srdo3a5eb3bic159kar72vok
Set executable permissions for: /Users/powerumc/.gradle/wrapper/dists/gradle-1.6-bin/72srdo3a5eb3bic159kar72vok/gradle-1.6/bin/gradle
Download http://devel.coupang.com/nexus/content/groups/public/com/eriwen/gradle-js-plugin/1.5.0/gradle-js-plugin-1.5.0.pom

................. 이하 생략 .................

Download http://devel.coupang.com/nexus/content/groups/public/com/asual/lesscss/lesscss-engine/1.3.0/lesscss-engine-1.3.0.jar
:help

Welcome to Gradle 1.6.

To run a build, run gradlew <task> ...

To see a list of available tasks, run gradlew tasks

To see a list of command-line options, run gradlew --help

BUILD SUCCESSFUL

Total time: 35.054 secs 


Posted by 땡초 POWERUMC

댓글을 달아 주세요

IntelliJ 배포 오류

Gradle로 구성된 프로젝트가 여러개 있는데, 그 중 하나만 IntelliJ에서 제대로 인식하는데 실패한 적이 있다. Tomcat으로 올바로 구동이 되어야 하는데, 배포가 되었다고만 나오고 Tomcat Context에 로드되지 않는 현상이 발생한다.


관련된 오류를 검색해 보았다. 하지만 전혀 다른 문제의 답변이었다.


 Artifact XXX:war exploded: Server is not connected. Deploy is not available. 


그러다가 Modules 구성을 살펴보니 여기에 문제가 있었다. 일전에 우리팀 루피님께서 Spring 구성이 잘 안되었다고 봐준적이 있는데, 어찌어찌 시간이 없어 이제서야 다시 살펴보게 되었다.

아래는 올바른 모듈의 Spring 구성이다. 


아래는 올바르지 않은 모듈 구성이다.  


문제가 되는 모듈을 제거하자. 


Gradle을 Import 로 다시 불러오자. 





이제 Tomcat에서 Spring MVC 응용 프로그램이 올바르게 구성이 된다. 


'Java' 카테고리의 다른 글

[IntelliJ] Tomcat 구동 오류 해결  (0) 2013.10.21
Posted by 땡초 POWERUMC

댓글을 달아 주세요

.NET 플랫폼이 나오고 십 여년 동안 마이크로소프트(Microsoft)는 .NET 플랫폼 시장을 개척하고 활성화 하기 위해 많은 투자를 아끼지 않았다. 많은 사람들이 주저 없이 .NET 개발에 뛰어 들었고, 비주얼 스튜디오(Visual Studio) 편리한 개발 도구는 .NET 플랫폼 개발에 필수 도구가 되었다.

하지만, 이제 한 때 과거의 이야기가 되어가고 있다. .NET은 새로 익히기 꺼려지는 플랫폼 중 하나가 되었고, 사회에 진출하는 새로운 .NET 개발자는 더 이상 예전처럼 양성 되지 않고 있다. 여기에 근거하는 사실을 매우 구체적으로 적고 싶으나 단순히 구체적인 한 두 가지의 문제라기 보다 복합적인 문제이므로 이를 읽는 독자는 넓은 시야로 가볍게 읽어주길 바란다.



[출처] 링크


그리고 본문에 잘못된 내용이나 사실에 근거를 대라고 지적하시는 것도 좋으나 그 정보는 직접 찾아 보고 반론을 제기해 주면 필자가 굳이 구구절절 같은 설명을 할 필요가 없어지므로 발전적인 토론이 될 것이라 생각한다.

1. .NET 플랫폼에 너무 많은 투자를 한 것

그야말로 .NET 개발 언어 중 대표적인 C#은 많은 다양한 프로그래밍 언어의 장점을 수용했다. 루비(Ruby), 파이썬(Python), 스몰 토크(Smalltalk)와 오브젝트 C(Objective C), 리스프(Lisp) 등의 프로그래밍 언어의 장점을 C# 언어에 녹아냈다. 이로써 C# 2.0, C# 3.0 시절엔 .NET 플랫폼의 춘추전국시대라고 해도 과언이 아닐 정도이다. (좋게 말하면 개방적인 언어가 되겠고, 나쁘게 말하면 줏대 없는 잡탕이라고 볼 수 있겠다.) 그와 함께 WCF(Windows Communication Foundation), WPF(Windows Presentation Foundation) 등 새로운 기술의 쏟아내는 기염을 토해냈다.

하지만 다른 측면에서 다른 분야의 기술과 그걸 밥벌이 하는 개발자들은 결국 소외될 수 밖에 없다. 그리고 그런 기술들은 기술 발전이 정체 되고, 지원이 끊기거나 지원 규모가 작아지게 된다. 대표적으로 비주얼 베이직(Visual Basic)과 ASP(Active Server Page), 실버라이트(Silverlight) 등이 사망 선고를 받게 된다.

다음은 각각 사망할/사망한 날짜이다.

최근 .NET 개발마저 침체기임이 틀림없다. .NET 플랫폼의 기본은 .NET 프레임워크(.NET Framework)이지만, 이 기본 라이브러리마저 파편화가 되고 있다. 엄밀히 말해 실버라이트, 윈도우 RT(Windows Runtime), .NET 프레임워크는 전혀 다른 메모리 공간에 로드 되는 외형만 비슷한 라이브러리이다. 자바처럼 JRE, JVM을 기초로 모든 코드가 실행되는 것과 다르다. 따라서 실버라이트, 윈도우RT, .NET 프레임워크는 각각 코걸이, 귀걸이 등 물리적으로 전혀 다른 구성 요소로 본다. 이는 마이크로소프트가 1년 앞도 염두하지 않고 급하게 설계하고 개발한 것이며, 아직 까지도 이런 릴리즈가 꾸준히 계속 되고 있다. 이는 뭔가 새로운 버전이 출시할 때마다 하위 호환성을 버리는 악순환이 된다.

2. 네이티브(Native)를 죽여버린 것

지난 수 십여년간 네이티브(Native) 기술의 암흑기였다. 더 이상 Visual C/C++ 6.0 컴파일러는 멀티 코어 CPU에 대응하기 힘들어지고 울며 겨자 먹기로 상위 버전으로 업그레이드를 할 수 밖에 없었다. VC 6.0에서 VC 2008/2010으로 약 십여년의 시간적인 격차가 벌어진 개발 도구와 개발 환경으로 옮겨야 했다.

UPDATE 2013-10-04
참고로 'Visual C/C++'이라 표기한 것은 C 언어 프로그래밍과 C++ 프로그래밍이 모두 가능하다는 의미이다. 아시다시피 Visual C/C++ 의 컴파일러는 다음처럼 Microsoft C/C++ Compiler 라고 표기되어 있기 때문에, 이를 통용하여 Visual C/C++이라고 표현하였다. 'Visual C 라는 것은 없다'고 하시는 분이 계셔서, 이 부분에서 오해의 소지가 없도록 하였으면 한다. 

또한, MSDN 의 일부 문서는 C/C++ 언어를 모두 다루는 문서가 있으니 MSDN의 C/C++ Languages 섹션을 참고하면 된다. 정확한 표기는 'Microsoft C/C++'로 표기하는 것이 정확하지만, Visual Studio IDE와 연관된 부분은 'Visual C/C++'이라 표기하는 것이 더 어울리지 않을까 생각한다.

참고: C 프로그램 컴파일 - http://msdn.microsoft.com/ko-kr/library/bb384838(v=vs.90).aspx

Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

simple.c
Microsoft (R) Incremental Linker Version 9.00
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:simple.exe
simple.obj

업그레이드는 컴파일러 뿐만 아니라 개발 도구마저 상위 버전으로 바꾸는 것은 매우 불합리하다. 컴파일러만 바꾸면 되지만 개발 도구까지 상위 버전으로 바꾸어야 하는 비합리적인 개발 환경은 오직 Visual C/C++ 밖에 없을 것이다. (그 외 윈도우 폰, 윈도우 8 앱 개발 환경도 마찬가지다)

그렇게 .NET 플랫폼이 춘추전국시대를 맞이하는 동안 마이크로소프트의 대부분의 SDK의 APIs 들은 .NET 용으로만 제공되었다. Visual C/C++로 만들고 싶어도 만들 수 없는 것이 더 많아지게 되었다. 아니, 만들 수는 있겠지만 ‘만들고 싶지 않다’는 표현이 맞을 것이다.

3. 실험적인 기술을 너무 서둘러 릴리즈 한 것

웹 2.0과 함께 떠오른 ASP.NET 기술 중 AJAX.NET 이 되겠다. 당시 AJAX 대표 라이브러리 중 prototype과 script.aculo.us 등이 대세임에도 불구하고 ASP.NET 서버 랜더링 모델을 고수하는 AJAX.NET을 내놓았다. 결국, 채 2년도 되지 않고 ASP.NET 웹 프레임워크는 jQuery를 공식적으로 지원하고 기존의 AJAX.NET은 어떤 언급도 없이 그렇게 버려졌다.

실버라이트(Silverlight). 이 기술은 애당초 세상에 나오지 말았어야 하는 기술이라 본다. 돌이켜보면 실버라이트 3.0 버전을 실버라이트 1.0 버전으로 나왔어야 경쟁력이라도 있었고 시장에서 기술적인 신뢰를 얻을 수 있었다. 물론 마이크로소프트(Microsoft)는 서둘러 어도비(Adobe)를 따라잡고 싶은 심정은 십분 이해된다. 하지만 이 실버라이트 기술 하나 믿고 시작한 개인과 회사는 시간, 인력, 기타 리소스의 금전적인 규모가 치명적인 타격을 주기에 충분하다. 한 마디로, 실버라이트 쇼크(shock)다.

실버라이트는 아직 까지 윈도우 폰 개발, (그 외 쉐어포인트(SharePoint))에 필요한 기술이다. 아직까지 실버라이트 개발을 하고 있는 분들에겐 미안하지만, 브라우저를 기반으로 하는 실버라이트 응용 프로그램은 거의 모조리 자취를 감췄다.

윈도우 폰 7. 조심스러운 부분이다. 다만 필자의 생각은 이 윈도우 폰 7이 정상적인 스마트 폰인지, 아니면 프로토타입 폰인지 아직도 구분하기 힘들다. 그리고 필자는 후자라고 생각한다. 다음의 인용을 보면 윈도우 폰 7은 Windows CE/Compact 7 커널 기반이지만, 윈도우 폰 8은 Windows NT 커널을 사용한다.

Windows Phone 7 is based on the Windows Embedded CE kernel – the next generation of the Windows Embedded CE platform will be Windows Embedded Compact 7 when released, and the current version is Windows Embedded CE 6.0 R3. Although Windows Phone 7 was built on the Windows Embedded CE kernel at its core, the Windows Phone team has incorporated innovative features and functionality on top of the platform to develop an OS specifically designed to meet the needs of mobile phone manufacturers. [2]

여기서 예상할 수 있는 것은 윈도우 폰 7은 이미 버린 카드이다. 왜냐하면, 윈도우 폰 7은 윈도우 폰 7.8 버전까지 업데이트를 할 수 있는데, 윈도우 폰 7과 8은 서로 전혀 다른 커널을 사용하므로 윈도우 폰 7 사용자는 윈도우 폰 8 운영체제로 업데이트를 할 수 없기 때문이다. 한 가지 재미있는 것은, 업데이트 ‘7.8’ 버전 번호를 보고 유머러스 한건지, 약올리는 건지 분간이 안된다. ㅎ

윈도우 8. 윈도우 8의 앱 개발 기반은 진흙 위에 빌딩을 세우는 것과 같을 정도로 안정적이지 못한 릴리즈이다. 결국 윈도우 8.1에서는 또 윈도우 8과 하위 호환성을 상당 부분 포기해야 했다. 이는 윈도우 폰 8도 마찬가지다. 윈도우 8 앱의 기반은 WinRT(Windows Runtime)인데, 이 WinRT도 완성도 면에서 프로토타입이라고 확신한다. 여기에 대해서는 아래의 필자의 글을 참고하기 바란다.

4. 플랫폼 사용자를 플랫폼에 가두는 것

물리적인 측면에서 .NET 플랫폼의 확장은 돈으로 직결되고 돈으로 귀결된다. 아주 간단한 예로 .NET 플랫폼이 클라우드에서 동작 가능한 Auto Scaling 환경이라고 치자. 서비스에 부하가 가중되면 필요에 따라 이를 분산하기 위해 스케일 아웃(scale out)이 필요할 때가 있는데, 이때 당장 윈도우 라이선스 부과라는 문제부터 해결해야 할 것이다.

마이크로소프트 윈도우즈(Windows) 서버 제품을 사용하는 순간부터 마이크로소프트의 제품을 사용해야 하는 처지에 놓이고 만다. 다음과 같이 말이다.

  • LDAP, Authentication, Certification -> 엑티브 디렉토리,(Active Directory)
    DNS -> 윈도우즈(Windows) DNS 서버
  • 웹 서버 -> IIS(Internet Information Services)
  • 가상화 -> Hyper-V
  • 응용 프로그램 서버 - COM+, WCF(Windows Communication Foundation)
  • 스크립트 언어 -> PowerShell

.NET 플랫폼과 매우 친숙하게 어울릴 수 있는 구성 요소들이다. 물론, 반드시 선택하지 않아도 되는 차선책은 있지만, 그렇게 되면 아름다운 아키텍처를 절대 만들 수 없게 된다. 솔루션 도입에서도 마찬가지로 사내 클라우드(Private Cloud) 구축에 Hyper-V과 결합하는 System Center 제품군은 최선책이고, 차선책은 없다.

위의 경우는 단적인 일부 예일 뿐이다.

.NET 플랫폼 또한 '그들만의 리그(League)' 일 뿐이다. 그들만의 리그 속에서는 환상적인 조합이지만, 조금만 밖에서 지켜보면 하나의 당이 모든 것을 통제하는 공산당 아키텍처와 다를 바가 없다.

5. 개발자를 경청하지 않는 마이크로소프트의 By Designed 철학

마이크로소프트의 모든 최종 답변은 이것으로 통한다. 바로 “BY DESIGNED-의도된 설계”.

섣부른 릴리즈(Release), 구조적인 아키텍처의 결함, 비효율적인 사용자 경험(UX) 등의 고객이나 사용자의 이의에 대해 마지막 (비)공식적인 마이크로소프트의 답변은 "by designed-의도된 설계" 라고 한다. 로드맵(roadmap)에서는 보여주지 않는 By designed 때문에 개발자와 회사들을 곤란한 상황에 빠트린다.

재미있는 일이 벌어졌는데, 마이크로소프트는 일방적으로 Microsoft SQL Server 라이선스 기준을 변경했다. 필자는 이것도 큰 범주에서 "By Designed" 철학과 전혀 무관하지 않다고 본다. 원래 CPU 라이선스(per cpu license)를 코어 라이선스(per core license)로 일방적으로 변경하는 바람에 SQL Server를 사용하는 멀쩡한 회사들을 불법 사용자로 취급 되어 라이선스가 부과되고, 이를 뒤늦게 안 영세(?)한 곳에서는 마른 하늘에 날벼락이 떨어지는 꼴이다. 합법적인 절차라 하더라도 충분히 고객들에게 분노를 살 수 있을 것이다. 이 라이선스 정책에 대해서는 다음의 링크를 참고하기 바란다.

"BY DESIGNED", 오리발 내밀기에 최고의 답변이자 최악의 답변이다. 필자도 이 말을 (불리할 때?) 쓰는 것을 좋아하지만, 반대로 또 가장 싫어 한다.

6. 개발자의 스스로 성장할 자생력을 죽여버린 것

제국 마이크로소프트는 개발자 생태계를 너무나 심각하게 교란 시킨다. 모든 개발 환경은 통제된 환경 하에서만, 그리고 통제된 방법으로 사용되길 원한다.

.NET 플랫폼 환경은 최선책은 있지만 차선책은 없다. 웹 개발만 예를 들어 보아도 자바(Java)는 스트럿츠(Struts), 스프링 MVC(Spring MVC), 플레이 프레임워크(Play Framework) 등 충분히 시장에서 검증된 웹 개발 프레임워크가 있다. 여기에 자바 언어보다 더 심플하고 강력한 언어인 스칼라(Scala), 그루비(Groovy)를 결합하면 폭발적인 생산력을 낼 수 있다.

하지만, ASP.NET은 ASP.NET MVC 이외에 차선책은 없다. 참고로 ASP.NET 웹 폼은 최선도, 차선도 아니다. 자세한 이야기는 다음의 필자의 글을 참고하기 바란다. 더불어 필자가 2009년에 쓴 글임을 감안하고 읽어 주길 바란다.

ASP.NET 웹 폼을 제외한 이유는 ASP.NET 웹 폼은 생산성 향상을 할 수 있는 기능 요소를 보여주기 위한 ASP.NET이 제공하는 기능의 일부이다. 그러므로 ASP.NET 웹 폼이 ASP.NET 전체 아키텍처에 영향을 주는 요소가 절대 아니므로 ASP.NET 웹 폼을 ASP.NET 웹 플랫폼과 동일 선상에서 비교하는 오류를 범하면 안되는 것을 당부한다. 즉 ASP.NET은 반드시 ASP.NET 웹 폼으로 개발하지 않아도 된다. 이 웹 폼은 사용자 정의 컨트롤로 구현된 컨트롤에 불과한 것이다. 자세한 내용은 MSDN 링크를 참고 하기 바란다. [3]

따라서 자바 플랫폼 웹 개발자들을 만나게 되면 다양한 기술과 경험에 대한 이야기를 들을 수 있고 충분히 토론할 만한 주제가 있는데 반면, .NET 웹 개발자들의 주요 관심사는 ASP.NET MVC 코드 조각을 찾아서 해결한 문제나 트러블 슈팅하는 팁 공유가 대부분이다. 그러므로 일반적인 .NET 웹 개발자는 자바 웹 개발자의 열린 사고 방식을 따라갈 수 없다고 결론을 내렸다.

뽀송뽀송한 .NET 플랫폼 테두리에 갇혀 있다면 우물 안의 물이 원래 당연히 썩는다고 생각할 거다. 하지만, 테두리 밖에서 바라보면 우물 안의 물이 원래 썩는 게 아니라 흐리지 않기 때문에 썩는 다는 걸 깨닿게 될 것이다.

7. 모든 것을 심각하게 통합한 것

.NET 개발자는 비주얼 스튜디오(Visual Studio) 없이는 단 한 줄도 코드를 만들어 내지 못한다. 단 한 줄, 과장된 표현이긴 하지만 비주얼 스튜디오(Visual Studio) 이외에 다른 대안이 없다.

통합은 곧 깡패다. 팀 파운데이션 서버(Team Foundation Server)에 체크인(Checkin)된 소스 코드를 받으려면 어이없게도 비주얼 스튜디오가 반드시 필요하다. 팀 탐색기(Team Explorer)가 설치된 비주얼 스튜디오 쉘(Visual Studio Shell)이 없으면 TFS Power Tools 같은 것도 무용지물이다. 왜냐하면 TFS 클라이언트 도구는 팀 탐색기에 포함된 필수 런타임이 반드시 필요하기 때문이다. 그 외 더 자세한 내용은 다음의 필자의 글을 참고 하기 바란다.

만약 마이크로소프트가 망한다면 기술의 기반, 개발 환경, 기타 솔루션 등 모든 것은 그저 한 줌의 재가 될 것이다. 소스 코드를 공개한다고 해도 그 일부는 더 장래가 밝은 1인자에게 기부될 것이다.

현재까지 100년 이상 된 IT 기업은 전세계에도 없다.  "영원히 위대한 기업, 영원히 호황을 누리는 산업은 없다. 다만 영원히 뛰어난 전략적 움직임만 존재할 뿐이다." [4] 마이크로소프트가 100주년이 된다면 IT 기업이 아닌 애플에 부품을 대주는 ‘제조업’ 회사일 수도, 플레이 스테이션을 유통 시키는 ‘무역업’ 회사일 수도, 그 아무도 모른다.

UPDATE 2013-10-04
IT 기업 중 100년 이상이 된 기업은 IBM으로 2011년도에 100주년이 되는 해였다. 댓글로 정보를 제공해 주셔서 감사합니다.

언젠가 (차선책이 없는) 통합된 제품을 쓰는 이들은 엄청난 대수술을 받아야 할 지 모른다. 마치 흔들리는 이빨만 뽑을 수 없고 잇몸 전체를 들어내야 할 수도...


Posted by 땡초 POWERUMC

댓글을 달아 주세요

  1. 이전 댓글 더보기
  2. 지나가던사람 2014.08.19 15:36 Address Modify/Delete Reply

    글 잘 읽었습니다.
    닷넷 개발만 10년넘게 하다가 최근 1년정도 php, java, python 등등 하고 있는 사람인데,
    저는 잘 모르겠습니다. 방금 언급한 언어들이 아직은 닷넷보다 미숙한 탓도 크겠지만,
    vs에 비해 개발 툴(이클립스, 인텔리제이)에서 느껴지는 답답함과 생산성, 유지보수의 불편함
    개발 프레임워크의 복잡성,, 이런 것들이 너무 크게 자리잡고 오히려 이런 것들이 쌓이니
    개발이 재미가 없어지고 있습니다.
    너무 닷넷에(편한 개발환경에) 익숙해져 말씀하신대로 변화에 적응력이 떨어진 거 같은 생각도 듭니다.
    닷넷이 망한(?) 원인은 여러 설명을 해주셨지만 제가 보기엔 기술의 흐름(ria, 오픈소스)에 대한 대응이 실패한 것이 크고 플랫폼 독립성이라는 주제에 대해 간과한 점, MS 제품군에 종속성을 유지하려 한점,
    라이센스에서의 독재성(?) 뭐 이런것들이 주요 원인이라 생각이 드네요.
    전 그래도 닷넷을 사랑합니다.
    10년넘게 개발해 오면서 닷넷이 아닌 다른 걸 선택했다면 쓸데없는 스트레스를 너무 많이 받았을 거 같네요..ㅎ 그만큼 닷넷이라는 기술에 있어서는 자바나 다른 언어들과 비교해도 뛰어나다고 생각합니다.

  3. 일단 공감, 그리고. 2014.08.24 22:32 Address Modify/Delete Reply

    MS는 MS DOS / Windows로 굳어진 OS 개발회사라는 틀을 벗어나는 것이 가장 시급하지 않은가라고 생각합니다. 90년 초반 MS-DOS를 사용하다 DR-DOS로 옮겨가던 사람들이 오피스 때문에 다시 Window 3.1를 다시 돌아보던 기억이 납니다. 그 때도 많은 사람들이 MS 욕은 엄첨 했었지만, 그건 특정 업체에만 국한되었던 것은 아니었죠. MS에서 느끼는 답답함은 그 시절과 크게 다르지 않은 것 같습니다. 오히려 닷넷때문에 이미지가 호전되었었다고나 할까요. :-) IBM, Oracle... 모두 다 사활을 건 생존경쟁에 서 있는 "업자"들일 뿐. 회사는 솔루션에 적합한 개발 플랫폼을 고르면 그만, 개발자는 회사에서 선택한 플랫폼이 머든 제발 리펙토링이라도 제대로 했으면 하는 쿨럭 -ㅁ- ㅋ

  4. 흠.. 요즘에는.. 2014.09.29 00:23 Address Modify/Delete Reply

    아주 새로운 데에서 닷넷이 풀리고 있습니다.
    혹시라도 닷넷을 망해가는 언어로 생각해서 더이상 배우는 사람이 나오지 않을까 몇 자 적어봅니다.

    마이크로소프트 닷넷 프레임워크를 멀티 플랫폼에서 돌릴 수 있도록 변환하는 작업이 오픈 진행되고 있습니다.

    Xamarin Studio 가 대표적인데, 닷넷으로 구현된 프로그램이 안드로이드, IOS, 윈도우 모두 돌아갈 수 있도록 합니다. 그 언어는 C#이고요..

    그 Xamarin studio 의 기본 툴이 MonoDevelop인데, 이건 요즘 각광받는 유니티에서 사용되고 있습니다.
    닷넷 관련 소스 및 커뮤니티도 상당히 크고요.

    MS가 전면에 나서서 하지는 못하지만 (회사 특성상) 멕시코 개발자가 시작한 MonoDevelop은 큰 반향을 일으키고 있습니다.

  5. feel 2014.11.14 11:19 Address Modify/Delete Reply

    어제자 발표입니다.
    우리의 목소리를 MS가 들은 걸까요? 이제 정신차리려고 하나 봅니다.

    http://news.microsoft.com/2014/11/12/microsoft-takes-net-open-source-and-cross-platform-adds-new-development-capabilities-with-visual-studio-2015-net-2015-and-visual-studio-online/

  6. 병팔이 2015.04.14 00:11 Address Modify/Delete Reply

    내용 중 7번에 공감 100% 입니다. SDK만 가지고 IDE 없이 빌드를 해보고자 했으나 너무나 큰일이더군요. 스케줄 빌드 같은 것은 대체 어떻게 하란 말인지? 그리고 SDK 설치했는데 나오는 경로명에 Visual Studio 뭐 이런 게 왜 섞여 나와야 하는건지. .NET 설치하는데 SQL Server 공짜로 깔아줘서 고맙다고 박수칠 줄 알고 넣어준건지. 하여간 너무 바보같고 monolithic한 덩어리를 만들어버렸습니다. 더 이상 기술향상은 안해도 좋으니 하루라도 빨리 깨끗이 나눠만 줬으면 좋겠습니다.

  7. 놔놔2 2015.06.18 08:02 Address Modify/Delete Reply

    저는 1984년에 마이크로 소프트 베이직을 보기 시작하여 비주얼툴로 자리를 잡았었는데요.
    엠에스가 욕을 많이 먹어도 왜 그러는지 몰랐다가.. 어느 순간부터 제가 욕을 하던데..ㅋㅋ 또 언제부턴가 기대도 안하고 그냥 기대를 포기했달까요.. 머하나 성공하는 모습을 못보게 되고 자만과 오만, 독선은 심지어 시작버튼 제거라는 지상 최대의 산업생산성에 역효율성의 극대화를 낳는등.. 사회와 국가와 세계에 미치는 악영향이 장난이 아니었죠. 만드는 것마다 자충수들 뿐이라 이제는 최고의 부자가 어떻게 잘근잘근 망해가는 가를 두고 강건너 불구경만 하고 있습니다. 왜 애플과 삼성이 발전을 하는데 엠에스는 스스로를 두고 우리는 개발사가 아니다 라고 말을 했을까 무척이나 씁쓸하고 안타까웠습니다. VB6.0의 기술지원단종도 산업에 끼친 악영향은 장난 아니며 IE의 버전별 버그는 또 어떻습니까.. 이젠 어떤 기술이 나와도 또 하나 시끄럽게 홍보하다가 또 사라지겠구나 합니다.

  8. 놔놔2 2015.06.18 08:02 Address Modify/Delete Reply

    저는 1984년에 마이크로 소프트 베이직을 보기 시작하여 비주얼툴로 자리를 잡았었는데요.
    엠에스가 욕을 많이 먹어도 왜 그러는지 몰랐다가.. 어느 순간부터 제가 욕을 하던데..ㅋㅋ 또 언제부턴가 기대도 안하고 그냥 기대를 포기했달까요.. 머하나 성공하는 모습을 못보게 되고 자만과 오만, 독선은 심지어 시작버튼 제거라는 지상 최대의 산업생산성에 역효율성의 극대화를 낳는등.. 사회와 국가와 세계에 미치는 악영향이 장난이 아니었죠. 만드는 것마다 자충수들 뿐이라 이제는 최고의 부자가 어떻게 잘근잘근 망해가는 가를 두고 강건너 불구경만 하고 있습니다. 왜 애플과 삼성이 발전을 하는데 엠에스는 스스로를 두고 우리는 개발사가 아니다 라고 말을 했을까 무척이나 씁쓸하고 안타까웠습니다. VB6.0의 기술지원단종도 산업에 끼친 악영향은 장난 아니며 IE의 버전별 버그는 또 어떻습니까.. 이젠 어떤 기술이 나와도 또 하나 시끄럽게 홍보하다가 또 사라지겠구나 합니다.

  9. hewon 2015.06.21 16:13 Address Modify/Delete Reply

    2015년 6월에 이 컬럼을 보는데 정말 정확하게 찍어보신것 같습니다.
    특히 윈도우폰이나 RT의 미래에 대해서는요.
    닷넷 개발하는 개발자 입장에서는 자기가 개발했던 제품이 언제 단종되나 두근두근하면서 개발하고 있습니다.

  10. MonoDevelop 2015.08.08 19:34 Address Modify/Delete Reply

    MonoDevelop 한글판이 익숙치 않아 영어로 놓고 있었는데 언제부터인가 언어 설정을 english로 해도 계속 한글이 튀어나와서 아주 괴롭습니다. 이거 어떻게 방법 없을까요?

    • 땡초 2015.08.09 00:17 Address Modify/Delete

      맥 버전을 사용하신게 맞나요?
      확인 차 여쭤봅니다.

  11. Yuhan 2016.04.05 11:22 Address Modify/Delete Reply

    옛날 게시물이니 그려려니하다 2015년도 글도 보여서 얕은 지식과 의견 몇자 적어봅니다.

    상당히 MS를 까기위한 편견만 가지고 접근하시는듯 한 부분이 많이 보입니다만.

    언어적인 측면에서는 2015년에는 이미 C#이 Java를 뛰어넘었다고 볼 수 있습니다.

    LINQ, Lambda Expression 등 현재는 Java가 언어적인 측면에서 오히려 .Net을 따라가고 있는 인상을 주고 있죠.

    사실 저도 원래 .Net 개발자이지만, 최근들어 Java로 프로젝트를 계속 진행하고 있습니다.

    Java가 좋아서도 아니고, 그저 국내 시장에 자바를 선호하는 경향이 강할 뿐 사실

    자바 4~5년 / 닷넷 6년 개발 해본 입장에서 어마어마하게 많은 중괄호와

    인터페이스 오히려 간결하지 못한 코드, 뭐 할려고만 하면 추가해야되는 플러그인이나

    아주 사소한것까지 추가해야되는 다양한 프레임워크로 스트레스 많이 받습니다.

    (StringUtils라는 클래스가 몇개의 라이브러리에 중복되어 섞여있습니까.)

    괜히 Groovy가 나왔겠습니까.

    그렇다면 Java를 버리고 Groovy를 배우는게 맞을까요? 이러면 자바 개발자가 아닌게 되는건가요?

    언어적 완성도 측면에서는 Java는 더이상 C#의 상대가 되지 않습니다. 2013년에도 말이죠.

    C#을 쓰지 않아도 배워야 하는 이유는 이것 하나만으로도 충분합니다.

    4번 제외하고는 사실상 진짜 닷넷에 대해 편파적으로 바라보는 자바개발자가 쓴 글 처럼 느껴지는데.

    이거랑 똑같이 자바도 단점 끄집어 내면 한도 끝도 없습니다.

    • 땡초 POWERUMC 2016.04.05 13:44 신고 Address Modify/Delete

      맞는 말씀입니다.
      굉장히 편파적으로 바라보면서 쓴 글입니다.

      하지만 자바 진영은 오픈 커뮤니티로 합의에 의해 주도되지만,
      MS는 상업적 기업이니 같은 시각으로만 바라보기 힘들기도 합니다.

      좋은 의견 주셔서 고맙습니다.

  12. 설마 2016.06.17 01:21 Address Modify/Delete Reply

    MS가 이 글을 본걸까요.. 3년이 지난 지금은 이 글에서 꼬집은 문제점이 대부분 해결되었다는 점이 신기하네요 ㄷㄷ

  13. John 2016.07.08 13:46 Address Modify/Delete Reply

    많은부부에서 공감을 얻고갑니다 자바 개발로 시작해서 현재 닷넷 개발을 하고있는데 위에 말씀하신분이 적으신것 처럼 언어적 편의성 은 C# 이 훨씬 우월하긴 합니다 , 그 편의성때문에 가끔 가독성이 떨어질때에도 있는데 작성하는 입장에서는 엄청 좋기도하고,, 그리고 닷넷이 정확히는 CORE 닷넷출시하면서 위에 말씀하시는 모든 문제들을 대부분 해결하거나 제안을 제시했습니다 마소가 좋은녀석들이라서 그런것은 아니고 , 시장경쟁력에서 워낙 많이 밀리고 또 대세가 그러하니 변경한것 같습니다 좋은 글 잘읽고 갑니다

  14. 요원009 2016.10.27 15:31 신고 Address Modify/Delete Reply

    엔지니어 출신 이사들 내쫓음 -> MBA 출신들 데려옴 -> 말아먹은 게 한 두개가 아님.

    말아먹은 것들이 저 위에 다 나오네요. 정신차리고 엔지니어 출신들 다시 모셔오니 정상 궤도로 올라오는 느낌입니다. 요즘 C# 너무 강력해요 ㅎㅎㅎ

  15. 나그네 2017.02.05 21:45 Address Modify/Delete Reply

    요즘 Java가 마소 보다 악질인 오라클 놈들 때문에 정나미가 떨어져서 C#으로 왔습니다. 나중에 ClojureCLR 프로젝트에나 참여하고 싶네요. 요즘 마소가 제공하는 자료도 너무나 많고(MSDN, Visual Academy 등) 리눅스 지원에 오픈소스 프로젝트도 많이 해서 진짜 많이 변한 것 같네요. 그놈의 윈도우의 괴상한 구조(특히 레지스트리)는 변함이 없지만 윈10이 마지막이라고 하니 다음 OS는 제발 개발자나 서버 관리자들에게도 일반 유저에게도 편리한 OS로 나오길...

  16. 얌전맨 2017.06.20 09:53 Address Modify/Delete Reply

    MS가 정말로 이 글을 읽고 반성한것처럼 저 시기 이후로 많이 달라졌습니다. 글 쓰신분의 예리한 통찰력에 감탄하구요. 자마린이 통합된 VS2017 출시로 다시 한번 C#에 희망을 걸어봅니다.

  17. 지나가다 2017.07.15 18:56 Address Modify/Delete Reply

    잘 읽었습니다.
    그런데, 외국을 보면 Java 가 우리나라 처럼 이렇게 융성하지도,
    .Net 이 한국처럼 저조하지도 않아요.

    이유는 딱 하나입니다.
    자기 돈 아닌 돈으로 프로젝트 하는 공무원들과
    국가 공인 프레임웍을 Java 로 만들어 놓은 사람들.

    아마 본인이 회사 사장인데,
    본인 회사 프로젝트를 전자정부 프레임웍을 써서 프로젝트 하라고 하면 하겠습니까?

    그리고, Java 진영에서 떠들던 MVC...
    숙련자들이 모이고, 원활한 커뮤니케이션이 되야
    중급이하의 개발자들이 모여서 대충 개발할 수 있는
    PHP, .Net 언어들만큼의 퍼포먼스가 나온다고 한다면...

    이건 공무원이 자기 돈 아닌 자금으로 진행하고,
    업자들은 그 열매를 받아 먹는 구조에서만 가능한겁니다.

  18. ㅇㅇ 2019.03.05 15:27 Address Modify/Delete Reply

    비교하려면 객관적인 도표자료는 필수인데 내용이 너무 주관적이고, 정보 신뢰도 그닥..
    실버라이트가 2021년에 사망이라.. 2010년에 이미 개발중단입니다. 2021년에는 웹브라우저에서 지원이 중단되는겁니다.
    사람들이 여기서 논쟁을 많이 하는 이유는 글을 이따구로 써놓으셔서 인거 같습니다.
    그럼 20000

    • 땡초 POWERUMC 2019.03.05 16:16 신고 Address Modify/Delete

      객관적인 자료는 본문 중에도 링크에 있습니다.
      https://support.microsoft.com/ko-kr/lifecycle/search/?ln=en-us&c2=12905
      2011년 12월에 출시한 SL5 가 있는데 2010년에 개발 중단이라뇨.
      그리고 10년이면 강산도 변한다고 하는데, 이미 6년이나 지난 글에 너무 큰 의미를 두지 않으셨으면 합니다.

  19. 그럼 글을 내리거나 수정하시는게 낫지 않나요? 2019.03.27 13:32 Address Modify/Delete Reply

    그럼 글을 내리거나 수정하시는게 낫지 않나요?

    분쟁 유도글 같아 보여서 올려봅니다.

    • 해구름 2019.04.08 12:40 신고 Address Modify/Delete

      2013년에 작성된 글이니 감안하고 보면 될 것 같습니다. 저도 2013년에는 마소에 대해 감정이 좋지 않았습니다. 폐쇄적이고 멋대로인데다가 제대로 되는 것도 별로 없었거든요. 샤티아 나델라가 지금의 마이크로소프트로 만들지 못했다면, 이 글은 좀더 공감 받았을지도 모르죠.

  20. 천하귀남 2019.04.18 10:19 Address Modify/Delete Reply

    최근 닷넷을 시작하면서 과거와 현재의 차이가 많아 뭐가 뭔지를 이해하기 힘들었는데
    당대의 상황을 이해 가능하게 해주는 글이라 많은 도움 받았습니다.

    문제를 언급하는것도 훌륭한 자료입니다. 불편하다고 지운다면 자료가 아니지요.

  21. 공부중입니다 2019.04.24 19:53 Address Modify/Delete Reply

    지금은 닷넷 전망을 어떻게 생각하시나요??

프로그래머를 위한 문제

프로그래머라면 알쏭달쏭한 논리적인 문제를 좋아하는 편인 것 같다. 답이 팍~ 나오는 문제보다 역량에 따라 코드의 아름다움이 달라지는 것을 추구하는 프로그래머라면 더욱 그렇다.

문제: 1부터 1만까지 8은 모두 몇 개가 나오나?

문제는 쉽다. 1부터 1만까지 8이라는 문자 개수만 카운팅하면 된다. 그런데 이렇게 간단한 문제를 코딩해 놓고 보면 맘에 안든다. 더 짧게…. 아래의 문제를 각 언어별로 풀어보았는데, 바이트 수는 캐러지 리턴(carriage return) 문자를 모두 제거한 바이트 수이다.

참고로, 이 문제는 ‘닷넷(.NET) 프로그래머 모임’ 에서 처음 본 문제인데, 오래 전의 일이라 게시글의 링크를 도저히 찾기가 힘들어서 링크를 남기지 못했다.

여러분 중 문제를 풀어보려고 한다면, 바로 아래에 답이 있으니 브라우저를 먼저 종료하길 바란다.

파이썬(python)

파이썬을 배운지 얼마 안되던 때에 이 문제를 만나서 파이썬으로 풀어보았다. 누가 짜도 최종적으로 아래의 코드가 될거다. 처음에는 이렇게 짧게 가능한 파이썬이 맘에 안들었지만, 이제는 맘에 든다. ㅋ;

더 이상은 짧게 안되지 싶다.

# 총 38 바이트   
(str(list(range(1,10001))).count('8'))  

C#

C# 코드로 짜면 아래처럼 된다. 아래의 코드는 확장 메서드와 람다 표현식이 전부다.

혹시나 요걸 비트 연산으로 풀어봤는데, 아래 코드보다 길어지더라. 뭐 내가 짠 코드라 길어질 수도 있겠다.

// 총 84 바이트  
Console.WriteLine(Enumerable.Range(1,10000).Sum(o=>o.ToString().Count(n=>n=='8')));   

Java

Java 8 정식이 나오면 Lambda 지원. 다만 C#의 확장 메서드를 언어상 지원하지 않으므로 Where, Select, Take, Skip 과 같은 확장 메서드는 사용하지 못할 것 같다. 어쨋든 Java 로 이 문제를 짧게 풀수 있는 경쟁력이 없으므로 패스!

C++ Update - 2013-07-04

C++에 대해 조예가 깊지 않아서 내 머리로는 더 짧게 안된다. 이 코드는 위의 파이썬과 C#과는 알고리즘(?)이 좀 틀리다. 어쩔 수 없다. 짧게 하려면…

// 총 146 바이트  
vector<int> v(10000);  
auto n=1,s=0;  
generate(v.begin(),v.end(),[&n, &s](){  
auto m=to_string(n);  
s+=count(m.begin(),m.end(),'8');  
return n++;});  
cout<<s;

아래 댓글에 더 짧은 C++ 코드를 올려주신 HATENA [링크] 님께서 올려주신 코드 입니다. Update - 2013-07-04

// 총 94 바이트
int c=0;
for(int i=1;i<10001;i++){
auto s=to_string(i);
c+=count(s.begin(),s.end();'8');}
cout<<c;

JavaScript - Update 2013-07-03

본 코드는 아래 댓글의 ddd 님께서 작성하신 자바스크립트 코드 입니다.

// 총 94 바이트  
var a=[];
for (var i=1;i<10001;i++)
a[i]=i;
console.info(a.join("").replace(/[^8]/gi,"").length);
Posted by 땡초 POWERUMC

댓글을 달아 주세요

  1. 황정현 2013.07.02 09:33 Address Modify/Delete Reply

    예전에 자바스크립트로 한번 풀어봤었네요... 근데 너무 길어요...
    for(var i = 0, len = 10000, count = 0, tmp = ""; i < len; i++){
    var arr = [];
    tmp = (""+i);
    for (var j = 1, len2 = tmp.length; j <= len2; j++)
    {
    arr[j] = tmp.substring((j - 1), j);
    if(arr[j].indexOf("8";) > -1) count++;
    }
    }
    console.log(count);

  2. ddd 2013.07.03 17:53 Address Modify/Delete Reply

    javascript 는 이정도 면 될듯한데
    var a = [];
    for (var i = 1; i < 10001; i++)
    a[i] = i;
    alert(a.join("";).replace(/[^8]/gi, "";).length);

    javascript 는 반복문 안쓸수 없나요 ㄷㄷ

    • 엄준일 2013.07.04 10:28 Address Modify/Delete

      댓글로 적어주신 소스 코드를 제 포스트에 업데이트 했습니다.
      ddd 님 사이트 링크를 남기고 싶었는데,
      링크가 없어서 아쉽네요 ㅠ

  3. HATENA 2013.07.04 11:27 Address Modify/Delete Reply

    항상 블로그 잘 보고 갑니다.
    C++은 vector, generate 안써도 쉽게 풀릴거같은데요 ㅎㅎ.

    int c=0;
    for(int i=1;i<10001;i++){
    auto s=to_string(i);
    c+=count(s.begin(),s.end();'8');}
    cout<<c;

    요거면 100byte 이내로 끝날거같습니다.
    파이선 코드길이 ㅎㄷㄷ하내요 ㄷㄷㄷ

  4. java론 2014.01.07 15:46 Address Modify/Delete Reply

    자바로는 아래처럼....
    int a=0;
    for(int i=0;i<10001;i++) {
    a+=(""+i).replaceAll("[^8]","";).length();
    }

  5. 김인혜 2014.04.06 03:56 Address Modify/Delete Reply

    마이너한 스크립트 언어입니다.
    AutoHotkey

    Loop 10000
    _ .= A_Index
    Msgbox % RegExReplace( _, "8", "", @ ) ? @ :

  6. 지나가다 2015.12.15 15:03 Address Modify/Delete Reply

    만들고 나니 위분거랑 같네요 ㅡㅡ;
    int result = 0;
    for (int i=0; i<=10001; i++) {
    result += String.valueOf(i).replaceAll("[^8]", "";).length();
    }
    return result;

  7. 행인 2016.11.20 15:28 Address Modify/Delete Reply

    Acm 문제에서 간간히 등장하는 유형이네요.
    그 당시 풀때 속도 문제 때문에 reject떠서 수학적으로
    규칙을 찾아서 accept 받았던 기억이 나네요

  8. 재밌네요 2016.11.29 16:02 Address Modify/Delete Reply

    Javascript 좀더 짧게~
    --------------------------------------
    var i,s,r; for (i = 1; i < 10001; i++) s += '' + i; r = s.split('8').length-1;
    console.log(r);

개요

Java 8 버전에서 Lambda 표현을 지원한다. 아직 Java 8은 Beta 버전이다. 여러 언어 중에서 Lambda 표현을 지원하지 않는 언어로 손꼽힌다. Wikipedia에서 Anonymous Function을 참고해보면 Java 언어가 언어의 표현력에 있어서 추세를 따라가지 못하는 것이 아닐까 생각한다.

반면,

  • C#은 2007년도에 C# 3.0 버전에 LINQ 라는 대주제를 중심으로 Lambda, Anonymous Class, Extension Methods를 내놓았고,
  • C# 4.0은 2010년도에 Dynamic이라는 대주제를 중심으로 동적 프로그래밍이 가능해졌다.
  • C# 5.0은 2012년도에 비동기 라는 대주제를 중심으로 비동기 프로그래밍을 언어적으로 지원한다.

Wikipedia에서 C# 역사에 대해 더 자세히 알고 싶은 분은 'C# (programming language)' 를 참고하면 좋겠다.

Java를 이용하여 프로그래밍을 하려고 하면 정말 C#이 많이 생각난다. C#에서 한 줄짜리 문장을 Java에서는 십여 줄 넘는 경우가 많기 때문이다. 굳이 예를 들자면, 우리나라에서 유행하는 줄임말 '엄친아'를 풀어서 '엄마 친구의 아들' 로만 말해야 하는 것과 같은 느낌이랄까… 어쨌든 Java는 Java만의 매력이 있는 법. 그 매력을 찾아보는 것도 재미있겠다.

각설하고, 먼저 Java 8을 사용하여 개발할 수 있는 환경부터 간단히 살펴보자.

현재 Java 8 버전은 베타 버전이다. 현재 Java 8은 Sun사의 JDK를 칭한다. 그러므로 Oracle 사이트에서 Java 8 버전을 다운로드 받을 수 없다.

그리고 Project Lambda를 지원하는 개발 툴을 사용해야 한다. 다음의 링크의 NetBeans와 IntelliJ IDEA 12 버전에서 Project Lambda를 사용해볼 수 있다. 아래의 링크에서 다운로드 받을 수 있다.

설치와 JDK 1.8 버전의 환경 구성이 완료되었으면 Lambda 표현을 Java 에서 사용할 수 있다.

Java 8 의 Lambda 샘플 예제 간단한 예제만 소개하겠다.

(Java에서 권장하는 네이밍이나 코드 구현 방식에 맞지 않는 부분이 있더라도 양해 바란다.) 간단한 더하기 계산을 Lambda 표현으로 작성하면 다음과 같다. 



위의 코드로 말미암아 Lambda 표현은 (arguments) -> { … } 로 표현할 수 있겠다.

간단하게 Thread를 돌리는 코드를 Lambda 표현식으로 작성해보자. 




다음은 ExecutorService를 Lambda 표현으로 작성하였다. 





Java의 Lambda 이야기가 나온 김에 어떻게 Lambda 표현으로 발전하였는지도 짤막하게 보자.

원래 이런 코드가 있었다. Runnable Interface를 구현하는 코드이다. 




또는 Java의 Local Class를 이용할 수 있다. Local Class는 메서드 구현부에서 Class를 선언하여 이를 인스턴스화 할 수 있다.

위의 Runnable Interface를 구현한 코드를 Anonymous Class(익명 클래스)로 표현할 수 있게 되었다. 그래서 아래의 예제와 같이 Interface를 구현하는 Class를 만들지 않아도 된다. 



위 Anonymous Class를 Lambda 표현으로 작성하면 더 간결하게 표현할 수 있다. 


단, Java 8의 Lambda 표현에 제약이 있다.

그리고 Project Lambda를 소개하는 페이지의 Functional Interfaces 에서 제약에 대한 설명이 있다. 하지만 이는 근본적으로 Java에서는 C/C++의 Pointer를 표현할 방법이 없는 이유이다. 그러므로 함수를 가리키는 Pointer도 있을 수 없다. 반면, C#에서는 함수포인터를 표현하기 위해 Delegate(대리자)를 지원한다. C#에서는 함수포인터를 안전하게 다룰 수 있다.

그래서 Java에서는 함수포인터를 표현하기 위해서 Listener 형태의 패턴을 주로 사용한다. 다른 말로 Observer 패턴이라고 부른다. Java의 Thread가 대표적이다. Java의 Thread는 Runnable을 인자로 받는 생성자가 있다. 위의 코드에서도 볼 수 있듯이 Runnable은 void run() 메서드만 달랑 가지고 있는 Interface이다. Java의 Thread는 이 Runnable Interface만 알고 있으면 되고, Runnable Interface를 구현하는 인스턴스를 Thread에게 넘겨주면 된다.

반면, C#의 Thread는 Delegate(대리자-안전한 함수 포인터)를 이용하여 Thread를 실행한다. C# 컴파일러는 Delegate를 결국 Class 로 취급한다. 이로 말미암아 Java와 C#에서 포인터라는 것은 언어적으로는 전혀 메커니즘으로 작동하지만 런타임 입장에서는 유사한 메커니즘으로 동작한다는 것을 알 수 있다. 하지만 Java에서 함수포인터를 흉내를 낼 수 있는 방법은 있다. 키/쌍의 컬렉션을 이용하여 참조를 전달하는 방법이다. 아래는 간단한 예제 코드이다. 



어찌되었든 결국, Java의 Lambda는 Interface를 이용하여 Lambda 함수를 만듦으로써 Interface의 함수가 단 1개만 있어야 Lambda 표현을 할 수 있는 제약이 생겼다. Interface를 이용하여 Lambda를 표현한다고 함은 내부적으로 Proxy 객체를 생성하여 그 안에 Lambda 표현을 메서드로 만든다.

아래의 익명 클래스를 보자. 아래의 runnable 로컬 변수를 리플랙션을 이용하여 getMethods() 의 목록이다. 



아래의 Lambda 표현을 보자. 마찬가지로 runnable 로컬 변수를 리플랙션을 이용하여 getMethods() 의 목록이다. 


이를 통해 Java의 Lambda 표현식은 내부적으로 Proxy 클래스가 생성됨이 확인되었다. 그런데 이 Proxy 클래스가 언제 생성이 될까? 컴파일 타임에 생성이 될까, 아니면 런타임에 생성이 될까?

이를 JD-GUI 도구를 이용하여 Decompile 결과를 확인하려고 하였으나, Java 1.8.0 버전에 대해서 JD-GUI 가 올바르게 인식을 하지 못해 전혀 class 파일을 전혀 읽을 수 없다. 대신 .class 파일을 Text Editor 로 열어서 대략적인 내용을 확인할 수 있는데, Text Editor 에서는 Lambda 표현으로 구현된 Proxy Class 를 찾을 수 없었다. 따라서 Lambda 표현은 런타임에 구현 객체 Proxy 가 생성된다는 것을 알 수 있다. (다만, 확신은 못하겠다.)

한가지, Java 8의 Lambda 표현의 다른 점이라면 Lambda 표현의 Proxy 객체는 java.lang.invoke.MagicLambdaImpl 클래스를 상속한다는 점이다. 앞서 얘기했듯이 JD-GUI 도구가 Java JRE 1.8.0 의 rt.jar 파일을 상위 호환성이 아직 지원되지 않아 구현 내용을 알 수는 없었다. 이는 좀 더 Java 8의 Release 시기가 다가오기를 기다려야 할 것 같다. 



결론은 Java 8의 Lambda 표현을 하기 위해서는 Interface 의 구현 함수는 반드시 1개여야 한다는 점이다.

Posted by 땡초 POWERUMC

댓글을 달아 주세요

  1. 짜두 2013.01.17 08:59 Address Modify/Delete Reply

    이제 베타버전이니 좀더 발전해야 겠네요. 굉장히 빠른 속도로 발전해나가지 않을까하는 생각이 드네요.ㅎ