Java 개발자를 위한 Scala 소개

앞서 Scala 시작하기 포스팅을 쓰고나서, 주위로부터 Scala에 대한 관심이 커진 걸 조금씩 느낀다. 하지만, 아직도 Scala를 본격적으로 도입한 사례는 많지 않은 것 같다. 그래서, Scala의 장점과 실제 어떤 식으로 코딩을 하는 지에 대해서, 그동안 배운 것들을 공유해보려고 한다.

1. Java와 호환


Scala는 compile language이다. compile을 하면, Java Virtual Machine(JVM)상에서 동작하는 byte code가 만들어진다. 이미 검증된 VM이라 할 수 있는 JVM에서 동작하기 때문에, ruby나 python등 VM을 필요로 하는 다른 언어 등과 비교하면 안정적이다. 게다가 compile 언어이기 때문에 이러한 interpret 언어보다 빠르다.

Scala는 JVM에서 동작하면서, Java의 문법과 아주 유사하기 때문에 Java의 모든 라이브러리를 그대로 사용할 수 있다. 수많은 Java로 된 open source 라이브러리들이 모두 Scala의 라이브러리이기도 한 것이다!

2. 개발 생산성(productivity)


Scala는 object oriented language이면서 동시에 functional language이다. object oriented 방식으로 하다가도 필요한 경우, functional language적인 것들을 섞어 쓸 수 있다. 이것은 마치, 중국집에서 짜장면을 먹고나서 이태리 식당에나 어울릴 법한 티라미슈 케잌을 후식으로 먹는 것에 비유할 수 있겠다.

그러면, Scala에서 유용하게 쓰이는 functional language적인 feature 몇가지만 소개보겠다.

2.1 Loan Pattern

From Java
텍스트 파일을 읽어서 한줄씩 출력 해야 한다고 하면 Java로는 아래와 같이 만들게 될 것같다.

BufferedReader bufferedReader = null;
try {
  bufferedReader = new BufferedReader(new FileReader(filename));
  String line = null;
  while ((line = bufferedReader.readLine()) != null) {
    System.out.println(line);
  }
} catch (IOException ex) {
  ex.printStackTrace();
} finally {
if (bufferedReader != null)
  bufferedReader.close();
}

만약 위의 기능에 더해서, 텍스트 파일을 읽어서 한줄씩 읽어서 DB에 저장하는, 위와 유사한 코드를 만들고 싶다면 어떻게 해야 할까?

실질적으로는 위의 코드에서 System.out.println() 부분만 DB로 저장하는 걸로 바꾸면 되지만, 아쉽게도 앞뒤로 있는 코드들은 다시 써주어야 한다. 다시 말해 중복코드가 필연적으로 생긴다.

To Scala
Scala로는 다음과 같이 정리가 가능하다.

먼저 실제 line을 가지고 작업을 하는 것을 제외한, 앞뒤 부분에 해당하는 함수를 다음과 같이 만든다.

def withFile(filename:String)(work: String => Unit) {
  var bufferedReader = null
  try {
    bufferedReader = new BufferedReader(new FileReader(filename))
    String line = null;
    while ((line = bufferedReader.readLine()) != null) {
      work(line)
    }
  } catch {
    case ex:IOException => ex.printStackTrace()
  } finally {
    if (bufferedReader != null)
      bufferedReader.close()
  }
}

이 함수는 2개의 arguement를 받는다. 하나는 filename이고, 다른 하나는 work라는 이름의 함수이다. 7번째 줄에 보면 인자로 받은 work라는 함수를 호출하고 있는 모습이 보인다. 이것이 Object Oriented 프로그래머에게는 조금 어려운 개념인데, Scala는 Functional language이기 때문에 함수도 하나의 변수처럼 처리할 수 있다.

중요한 건 이렇게 앞뒤 귀찮은 작업을 하는 함수를 빼어(refactoring) 놓으면 다음과 같이 재사용할 수 있다.

– 한 줄씩 화면에 출력

withFile(filename) {
  line => System.out.println(line)
}

– 한 줄씩 DB에 저장 (storeToDb라는 함수가 있다는 가정하에)

withFile(filename) {
  line => storeToDb(line)
}

위와 같은 방법을 ‘loan pattern’이라고 한단다. 출처는 여기.

2.2 iteration

Scala에서는 collection의 iteration 관련해서 유용한 함수가 많다. 사실 내가 Java를 버리고 Scala를 쓰게 된 동기도, 빵꾸똥꾸 같은 Java의 iteration 때문이다. 한동안 C#, Ruby 등으로 개발하다가, 다시 Java를 써야했는데, 오랜만에 써보는 Java iteration은 C 코딩하다가 어셈블리어를 썼던 딱 그때를 생각나게 했다.

그럼 예제 코드를 잠시 보겠다. numbers라는 collection에서 짝수인 것들만 추려내야 하는 경우에, Java에서는 다음과 같이 한다.

for (Iterator iterator = numbers.iterator(); iterator.hasNext();) {
  Integer number = iterator.next();
  if (number % 2 == 0) {
    iterator.remove();
  }
}

위 코드의 출처는 여기.

Scala에서는 다음과 같이 엘레강트하게 처리한다.

numbers.filter { n => n % 2 == 0 }

이 외에도 Ruby 프로그래머의 경우에 익숙할 map, count등 iteration을 위한 유용한 함수들이 많이 존재한다.

2.3 기타

꼭 functional language 적인 것 말고도, Java에서 불편하던 것들을 문법적으로 개선한 부분이 많다. Singleton 지원, 변수의 lazy 초기화 지원 등등 여러가지가 있는데, 길어질 것 같으니 생략하도록 하겠다. ^^

3. 성능(performance)


기본적으로 Scala는 JVM에서 동작하기 때문에 Java 만큼의 성능이 나온다. 물론 C나 C++과 같이, compile 결과로 machine code가 나오는 언어에 비해서는 느리지만, Ruby, Python, JavaScript등의 interpret 언어보다는 월등히 빠르다.

여기에, Scala에서 제공하는 Actor를 이용해 message 방식의 프로그래밍을 하면 훨씬 빠를 수 있다.

3.1 Actor?

Actor는 Java로 치면 일종의 Thread이다. Scala에서는 Actor에게 어떤 작업을 하라고 message를 보내면, Actor는 작업을 해야할 것들을 자신의 queue(mailbox라고 불린다)에 넣어두고, 하나씩 꺼내서 처리한다. Actor에게 message를 보낸 쪽에서는 message의 결과를 꼭 기다려야 하는 상황이 아니면, 자신이 하던 작업을 그냥 쭈욱 한다. 다시 말해 비동기적(asynchronous)으로 처리한다. 좀더 자세한 설명은 여기를 참고.

Actor가 대용량 처리시 빠른 이유는,
1) 비동기적으로 프로그래밍할 수 있도록 하며
2) 각 Actor가 mailbox에 메시지를 queueing을 해가면서, 동시에 엄청나게 많은 메시지를 처리할 수 있기 때문이다.

사실 actor, message의 개념은 erlang에서 차용한 개념이다. 이전의 포스팅에서 잠시 언급한 적인 있는데, erlang은 대용량 처리에서 훌륭한 performance로 유명하다.

4. Actor로 두 마리 토끼(productivity, performance) 잡기

웹 서비스에서 그 규모가 커지면, 흔히 client로부터 최초의 request를 받는 frontend 서버와 좀더 세분화된 작업을 처리하는 backend 서버로 그 구조가 진화하게 된다. Twitter역시 초기에는 Ruby on Rails의 한 종류의 서버만 있었지만, traffic이 커가면서 frontend/backend 서버 방식으로 진화했다. 좀더 자세한 내용은 여기 참고.

– Twitter에서의 frontend/backend 서버 구조 (출처: Twitter Engineering Blog)

frontend 서버가 client로부터 최초의 request를 받아서 여러 backend 서버에 다시 작업을 지시하고, 결과를 받아 최종 response를 client에 보내는 경우를 생각해보자. 가급적 backend 서버의 호출은 비동기적으로 이루어저야 frontend 서버는 좀더 대용량을 처리할 수 있다.

4.1 가상 시나리오

Actor의 훌륭함(?)을 설명하기 위해 다음과 같은 트위터에서의 가상의 request를 생각해보았다.

  1. 어떤 사용자 A의 가장 최근에 등록된 follower B를 찾아서
  2. B 사용자의 트윗 목록 중 최신 트윗 하나를 얻어와
  3. 이 트윗의 RT 카운트를 얻는다.

물론 이 시나리오는 조금 억지스러운 면이 있지만, 서비스를 만들다 보면 이렇게 backend 서버와 3단 이상의 콤보를 해야하는 경우는 사실 비일비재하다.

서비스 구조는 다음과 같이 되어 있다고 가정하겠다.

  • Follower를 알려주는 backend 서버와의 연동은 followerActor에서 담당
  • 트윗 목록을 알려주는 backend 서버와의 연동은 tweetActor가 담당
  • RT 카운트를 알려주는 backend 서버와의 연동은 statsActor가 담당

4.2 구현

이것은 다음과 같이 구현될 수 있다.

val futureResult = for {
    follower <- (followerActor ? GetLatestFollower(userId)).mapTo[User]
    tweet <- (tweetActor ? GetLastestTweet(follower.id)).mapTo[Tweet]
    count <- (statsActor ? GetRetweetCount(tweet.id)).mapTo[Int]
} yield count

futureResult onSuccess {
  case n:Int => //처리
}

futureResult onFailure {
  case e:Exception => //처리
}

1~5줄을 보면, 각 backend 서비스에 대해서 하나씩 별도로 처리할 필요없이, for를 이용해 처리하고 있다. 2~4 줄에서 actor에 대한 호출은 비동기적으로 처리되고, 성공적인 결과가 나올 때만 ‘<-‘ 연산자 앞의 변수 이름으로 값이 저장된다.

성공적으로 모든 backend 서버에 대한 작업들이 끝나면 8줄 부분이 호출된다. 중간에 어떤 작업에서라도 exception이 일어나면, 12번 줄 부분이 호출된다.

참고로 아래 코드는 actor에게 메시지를 보내고 결과를 기다리기 위해 ‘?’라는 연산자를 사용했다. 또 onSuccess, onFailure 등을 통해 callback을 등록했는데, 이것은 scala 언어 자체에서 지원하는 것은 아니고, Akka라는 라이브러리를 이용하면 가능하다.

마치며


최근에 node.js가 각광받고 있다. 무엇보다 node.js의 장점은 비동기적으로 쉽게 코딩할 수 있도록 해주는 것이 아닐까 한다. 혹시 Java 개발자의 경우에, node.js의 장점에 끌린다면, 이웃사촌인 Scala를 먼저 검토해보라고 권유해보고 싶다. 안정적인 VM, 이미 풍부한 open source들, 거기에 생산성을 배가시켜주는 scala의 문법. 또, 최근에는 Eclipse나 IntelliJ 같은 IDE에서 Scala 지원이 잘되고 있다.

많이 부족한 글을 끝까지 읽어주신 분들께 진심으로, 정말로 감사한 마음을 전하고 싶다. 내용 중에 설명이 부족했거나 또는 틀린 내용있으면 꼭꼭꼭 코멘트를!

22 thoughts on “Java 개발자를 위한 Scala 소개

  1. 좋은 글 감사합니다.🙂 다시 스칼라를 손대게 만드는 절대적인 이유 중에 하나가 트위터랑 링크드인! 다음 글도 기대하고 있겠어요!! ㅋ

  2. Pingback: Scala : By-Name Parameters & Repeated Parameters « Dani's Notes

  3. 스칼라 개념을 잡을 수 있는 좋은 글 감사합니다
    계속 기대해 볼께요 ㅎㅎ

  4. Pingback: Java 개발자를 위한 Scala 소개 | FireLight

  5. Pingback: Scala + Play Framework 2 Server 성능 향상 시키기 | Inspired

  6. 얼마전에 스칼라+Play2 조합으로 개발하는 것을 포기하려고 했었는데, ppassa님의 블로그에서 많은 영감을 얻고 다시금 힘내려 하고 있습니다. 블로그 네임처럼, 참 제게 Inspired되는 글들 감사합니다. ^^

  7. 안녕하세요. 혹시 스칼라 현업에 적용 중이신가요?

    막 학원을 다니고 있는 늦깍이 개발자인데, 1~2년 정도는 살아남는 것에 중점을 두고 싶은데 스칼라가 효율적이라 배우고 싶으면서도 현실적인 개발 환경에서 java 를 선호하고, 더 경력으로 인정해줄 것 같은 분위기라 이 언어의 장래성(한국)에 대해서 궁금합니다.

    • 국내 큰 IT회사에서도 요즘은 꽤 쓰이는 듯 해요. Functional Language가 처음에 배우기가 좀 어려운데, 그것만 넘기면 분명 몸값(?)은 jump하시지 않을까 조심스레 예상해봅니다 ^^

      • 답변 감사합니다. 시간이 허락하신다면, IT 업체 이름도 알 수 있을까요? 수료가 2개월 남아서 스칼라.js 를 이 기간 안에 수업과 병행하여 익히고, 해당 IT 업체 쪽에 문의해서 영어 같은 spec 등을 대비하려고 합니다.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s