Microsoft MVP성태의 닷넷 이야기
개발 환경 구성: 243. Scala 개발 환경 구성(JVM, 닷넷) [링크 복사], [링크+제목 복사],
조회: 17103
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

Scala 개발 환경 구성(JVM, 닷넷)

JVM 환경의 차세대 언어로 떠오르고 있는 Scala 언어가 눈길을 끄는군요. (주의할 것은 Scalar가 아니라 ^^ Scalability의 Scala입니다.) C# 개발자로써 자바의 답답한 구문에 숨이 막힐 것 같다면 스칼라 언어가 답이 될 수 있겠습니다. 그나저나 생각보다 역사가 꽤 깊은데요. 2003년에 나와서 10년이 넘었기 때문에 나름 꽤 안정적인 기반을 구축한 것 같습니다.

Scala
; http://www.scala-lang.org/

다운로드는 위의 사이트에서 현재 2.11.2 버전을 받을 수 있습니다. 윈도우 환경의 경우 MSI 설치 파일이 제공되며 실행 후에는 "c:\Program Files (x86)\scala\bin" 폴더에 바이너리가 생성되고 해당 경로가 PATH 환경 변수에 등록됩니다. 그래서 cmd.exe 창만 띄운 후 scala라고 입력하면 REPL 환경이 실행됩니다.

E:\>scala
Welcome to Scala version 2.11.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_45).
Type in expressions to have them evaluated.
Type :help for more information.

scala>

아쉽게도 현재 윈도우 환경의 REPL에서는 화살표 키가 먹지 않아서,

Use arrow keys in scala REPL gives Chinese character
; https://issues.scala-lang.org/browse/SI-8535

이전 입력어를 다시 재사용하려면 Ctrl+P 키를 눌러 우회해야 합니다. (여기서... 10년의 안정화 기간이라는 것은 단지 언어에 국한되었음을 인지하게 됩니다. ^^;) 그 외에 단축키 및 내부 명령어는 다음과 같습니다.

Ctrl+L: 도스의 cls와 같은 역할
:q (또는 :quit): REPL 종료

하지만 곧 cmd.exe를 뒤로 하고 역시 통합 개발환경이 필요하다는 생각이 절실하게 듭니다. ^^

Scala IDE - Version 3.0.3
; http://scala-ide.org/

위의 링크에 가서 "Download IDE" 이미지 버튼을 누르면 오늘자 기준으로 2.11.2 버전의 스칼라가 통합된 이클립스를 받을 수 있고, 압축을 풀어 실행한 후 "File" / "New" / "Scala Project" 메뉴를 선택해 새로운 스칼라 프로젝트를 생성할 수 있습니다.

scala_ide_1.png

그다음 "File" / "New" / "Scala Application" 메뉴를 선택해 새로운 스칼라 소스 코드 파일 "Main.scala"로 추가한 다음 아래와 같이 간단하게 코딩을 해주고,

object Main extends App {
 override def main(args: Array[String]) {
    println("Hello, World!")
  }
}

Ctrl+F11 키를 눌러주면 실행이 되어 콘솔 창에 "Hello, World!" 문자열이 출력됩니다.

또한 "Window" / "Show View" / "Scala Interpreter" 메뉴를 선택하면 이클립스에 로드된 프로젝트를 문맥으로 어느 정도의 기능을 갖춘 REPL 창이 뜹니다.




스칼라 언어는 닷넷 용도 지원하고 있는데요. 제법 양호한 것처럼 들리지만,

Scala comes to .Net
; http://www.scala-lang.org/old/node/10299.html

구현은 아직 미미합니다. 아래의 사이트에서 공개적으로 진행되고 있는데,

magarciaEPFL/scaladotnet 
; https://github.com/magarciaEPFL/scaladotnet

"Scala comes to .Net" 글에서는 금방 될 것처럼 이야기하고 Visual Studio용 플러그인도 내놓는다고 했지만 마지막 커밋 기록이 3년이 넘은 걸로 봐서 그다지 신뢰할 수 있는 수준은 아닌 것 같습니다.

어쨌든 일단 2012년 3월 10일 릴리즈된 버전으로 다운로드 받고,

scaladotnet - Download Packages
; https://github.com/magarciaEPFL/scaladotnet/downloads

실행해봤으나, 처음부터 예외가 발생하는 군요. ^^;

E:\scala>scalacompiler.exe

Unhandled Exception: System.TypeInitializationException: The type initializer for 'scala.tools.nsc.Main$' threw an exception. 
---> System.TypeInitializationException: The type initializer for 'scala.tools.nsc.Properties$' threw an exception. 
---> System.TypeInitializationException: The type initializer for 'scala.Predef$' threw an exception. 
---> System.TypeInitializationException: The type initializer for 'scala.package$' threw an exception. 
---> System.TypeInitializationException: The type initializer for 'scala.collection.Traversable$' threw an exception. 
---> System.TypeInitializationException: The type initializer for 'scala.util.control.NoStackTrace$' threw an exception. 
---> System.TypeInitializationException: The type initializer for 'scala.runtime.ScalaRunTime$' threw an exception. 
---> System.TypeInitializationException: The type initializer for 'scala.compat.Platform$' threw an exception. 
---> System.TypeInitializationException: The type initializer for 'scala.util.Properties$' threw an exception. 
---> System.TypeInitializationException: The type initializer for 'java.nio.charset.StandardCharsets' threw an exception. 
---> System.TypeInitializationException: The type initializer for 'java.lang.StdIO' threw an exception. 
---> java.lang.NullPointerException: charset is null
   at java.security.AccessController.doPrivileged(Object , AccessControlContext, CallerID )
   at java.security.AccessController.doPrivileged(PrivilegedAction action, CallerID )
   at java.nio.charset.Charset.lookupViaProviders(String )
   at java.nio.charset.Charset.lookup2(String )
   at java.nio.charset.Charset.lookup(String )
   at java.nio.charset.Charset.defaultCharset()
   at sun.nio.cs.StreamEncoder.forOutputStreamWriter(OutputStream out, Object lock, String charsetName)
   at java.io.OutputStreamWriter..ctor(OutputStream out)
   at java.io.PrintStream..ctor(Boolean , OutputStream )
   at java.io.PrintStream..ctor(OutputStream out, Boolean autoFlush)
   at java.lang.StdIO..cctor()
   --- End of inner exception stack trace ---
   at java.lang.System.get_out()
   at java.lang.Class$3.run()
   at java.lang.Class$3.run()
   at java.security.AccessController.doPrivileged(Object , AccessControlContext, CallerID )
   at java.security.AccessController.doPrivileged(PrivilegedAction action, CallerID )
   at java.lang.Class.checkInitted()
   at java.lang.Class.privateGetDeclaredConstructors(Boolean )
   at java.lang.Class.getConstructor0(Class[] , Int32 )
   at java.lang.Class.newInstance0(CallerID )
   at java.lang.Class.newInstance(CallerID )
   at sun.nio.cs.FastCharsetProvider.lookup(String )
   at sun.nio.cs.FastCharsetProvider.charsetForName(String charsetName)
   at java.nio.charset.Charset.lookup2(String )
   at java.nio.charset.Charset.lookup(String )
   at java.nio.charset.Charset.forName(String charsetName)
   at java.nio.charset.StandardCharsets..cctor()
   --- End of inner exception stack trace ---
   at scala.util.Properties$.scalaProps()
   at scala.util.PropertiesTrait$class.scalaPropOrNone(PropertiesTrait $this, String name)
   at scala.util.Properties$.scalaPropOrNone(String name)
   at scala.util.PropertiesTrait$class.$init$(PropertiesTrait $this)
   at scala.util.Properties$..ctor()
   at scala.util.Properties$..cctor()
   --- End of inner exception stack trace ---
   at scala.compat.Platform$..ctor()
   at scala.compat.Platform$..cctor()
   --- End of inner exception stack trace ---
   at scala.Array$.copy(Object src, Int32 srcPos, Object dest, Int32 destPos, Int32 length)
   at scala.collection.immutable.HashSet$.HashTrieSet.updated0(Object key, Int32 hash, Int32 level)
   at scala.collection.immutable.HashSet$.HashSet1.updated0(Object key, Int32 hash, Int32 level)
   at scala.collection.immutable.HashSet.$plus(Object e)
   at scala.collection.immutable.HashSet.$plus(Object elem1, Object elem2, Seq elems)
   at scala.collection.immutable.Set$.Set4.$plus(Object elem)
   at scala.collection.immutable.Set$.Set4.$plus(Object elem)
   at scala.collection.SetLike.$anonfun$$plus$plus$1.apply(Object v1, Object v2)
   at scala.collection.LinearSeqOptimized$class.foldLeft(LinearSeqOptimized $this, Object z, Function2 f)
   at scala.collection.immutable.List.foldLeft(Object z, Function2 f)
   at scala.collection.AbstractTraversable.$div$colon(Object z, Function2 op)
   at scala.collection.SetLike$class.$plus$plus(SetLike $this, GenTraversableOnce elems)
   at scala.collection.AbstractSet.$plus$plus(GenTraversableOnce elems)
   at scala.collection.TraversableOnce$class.toSet(TraversableOnce $this)
   at scala.collection.AbstractTraversable.toSet()
   at scala.runtime.ScalaRunTime$..ctor()
   at scala.runtime.ScalaRunTime$..cctor()
   --- End of inner exception stack trace ---
   at scala.sys.SystemProperties$.noTraceSupression()
   at scala.util.control.NoStackTrace$..ctor()
   at scala.util.control.NoStackTrace$..cctor()
   --- End of inner exception stack trace ---
   at scala.util.control.NoStackTrace$class.fillInStackTrace(NoStackTrace $this)
   at scala.util.control.BreakControl.fillInStackTrace()
   at java.lang.Throwable.instancehelper_fillInStackTrace(Exception this)
   at java.lang.Throwable..ctor()
   at scala.collection.Traversable$..ctor()
   at scala.collection.Traversable$..cctor()
   --- End of inner exception stack trace ---
   at scala.package$..ctor()
   at scala.package$..cctor()
   --- End of inner exception stack trace ---
   at scala.Predef$..ctor()
   at scala.Predef$..cctor()
   --- End of inner exception stack trace ---
   at scala.tools.nsc.Properties$.scalaProps()
   at scala.util.PropertiesTrait$class.scalaPropOrNone(PropertiesTrait $this, String name)
   at scala.tools.nsc.Properties$.scalaPropOrNone(String name)
   at scala.util.PropertiesTrait$class.$init$(PropertiesTrait $this)
   at scala.tools.nsc.Properties$..ctor()
   at scala.tools.nsc.Properties$..cctor()
   --- End of inner exception stack trace ---
   at scala.tools.nsc.Driver..ctor()
   at scala.tools.nsc.Main$..ctor()
   at scala.tools.nsc.Main$..cctor()
   --- End of inner exception stack trace ---
   at Main(String[] args)

관련 이슈가 있지만 답이 없는 상황.

scalacompiler.exe NullPointerException #3 
; https://github.com/magarciaEPFL/scaladotnet/issues/3

알고 보니, 현재 닷넷용 프로젝트는 중단된 상황이라고 합니다.

what is the status of scala for .net ? 
; https://groups.google.com/forum/#!topic/scala-user/_PodIqLmGC8

[scala-user] What's the state of Scala.NET?
; http://grokbase.com/t/gg/scala-user/131pet3xr5/whats-the-state-of-scala-net

아쉽군요. ^^ IKVM을 통해 우회적으로 스칼라를 구동할 수 있다고는 하지만,

Scala on .NET via IKVM
; http://tountas-software.blogspot.kr/2010/07/scala-on-net-via-ikvm.html

저 같은 닷넷 매니아에게도 저건 왠지 몹쓸 시도 같습니다. ^^;




참고로, 제가 읽은 책입니다.

도서 쉽게 배워서 빨리 써먹는 스칼라 프로그래밍
; http://www.yes24.com/24/Goods/8744130?Acode=101

실습이 대체로 어렵진 않았는데 다음의 코드 실행에서 걸렸습니다.

object Main extends App 
{
  override def main(args: Array[String]) 
  {
    lazy val words = scala.io.Source.fromFile("c://temp/ConsoleApplication1.sln").mkString
    println(words)
  }
}

실행해 보면 이런 예외가 발생합니다.

Exception in thread "main" java.nio.charset.MalformedInputException: Input length = 1
    at java.nio.charset.CoderResult.throwException(Unknown Source)
    at sun.nio.cs.StreamDecoder.implRead(Unknown Source)
    at sun.nio.cs.StreamDecoder.read(Unknown Source)
    at java.io.InputStreamReader.read(Unknown Source)
    at java.io.BufferedReader.read1(Unknown Source)
    at java.io.BufferedReader.read(Unknown Source)
    at java.io.Reader.read(Unknown Source)
    at scala.io.BufferedSource.mkString(BufferedSource.scala:96)
    at Main$.words$lzycompute$1(Main.scala:20)
    at Main$.words$1(Main.scala:20)
    at Main$.main(Main.scala:21)
    at Main.main(Main.scala)

자바에 익숙한 분들은 금방 원인을 알겠지만, 제 경우에는 감을 못잡았는데요. 다행히 아래의 글에 그 원인이 나옵니다.

How to read a text file with mixed encodings in Scala or Java?
; http://stackoverflow.com/questions/13625024/how-to-read-a-text-file-with-mixed-encodings-in-scala-or-java

따라서, 다음과 같이 명시적으로 인코딩 설정을 해주면 됩니다.

import scala.io.Codec
import java.nio.charset.CodingErrorAction

object Main extends App 
{
  override def main(args: Array[String]) 
  {
    implicit val codec = Codec("UTF-8")
    codec.onMalformedInput(CodingErrorAction.REPLACE)
    codec.onUnmappableCharacter(CodingErrorAction.REPLACE)

    lazy val words = scala.io.Source.fromFile("c://temp/ConsoleApplication1.sln").mkString
    println(words)
  }
}

한 가지 아쉬운 점은 UTF-8 인코딩을 지정해도 BOM이 붙은 파일의 경우 여전히 BOM 마크를 떼지 않고 포함한다는 것입니다. 그래서 BOM이 있는 파일을 읽을 때는 다음과 같이 "?" 문자가 출력됩니다.

?
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.30501.0
...[생략]...
EndGlobal

회사 동료가 UTF-8 인코딩의 경우 표준에는 BOM을 붙이지 않는다고 합니다. (검색해 보니 그 말이 맞더군요. ^^)




전체적으로 Scala 책을 한번 읽어 본 소감은... 뭐랄까? Scala 언어가 분명 "함수형 + OOP"의 성격을 지니고 LISP와 같은 함수형 언어들에 비해 쉽다는 것은 분명하지만 여전히 주류 언어로 되기에는 개념이 어려운 것은 사실입니다. (제가 C#만 고집해서 그런 걸까요? ^^)

Scala 언어에 대해 흔히 말하기를 "단어가 적어서 생산성 향상으로 이뤄진다"는 것이 가장 눈에 띄는 선전 문구인데요. 사실... 이것은 양날의 검과 같습니다. 가령 물리학에서 열역학 제 2법칙을 다음과 같이 간단하게 썼다고 해서 누가 이 말 뜻을 제대로 이해하겠습니까?

열적으로 고립된 계의 총 엔트로피가 감소하지 않는다는 법칙

그랬으면 이런 위키백과 글은 씌여지지도 않았을 것입니다.

열역학 제2법칙
; http://ko.wikipedia.org/wiki/%EC%97%B4%EC%97%AD%ED%95%99_%EC%A0%9C2%EB%B2%95%EC%B9%99

짧게 표현했다고 해서 모두 쉬운 것이 아니라는!

정리해 볼까요?

개인적으로 최근의 Swift 언어를 비롯해서 F#, Java, Objective-C, Scala, Clojure, Python, PHP 등의 언어를 그야말로 수박겉핡기 식으로 책 한권 보는 수준 정도만 학습해 봤는데요. (그 외에 주력 언어인 C#을 비롯해서 Assembly, C/C++, Javascript 까지 합치면 저도 꽤나 돌아다닌 것 같습니다. ^^) 하지만, 모든 언어를 통달하기에는 인생이 너무 짧으므로. 제가 여러분들에게 혹시나 조언을 드릴 위치가 조금이라도 된다면 이렇게 말하고 싶습니다.

  1. 전문 개발자로써 C/C++ 언어는 꼭 하시기 바랍니다. 어셈블리 만큼은 아니겠지만 그래도 C/C++만 어느 정도 할 줄 알아도 시스템에 대한 기본 이해력이 보다 더 향상됩니다.
  2. Virtual Machine 위에 올라가는 언어를 하나 정도는 마스터하세요. C/C++ 언어는 분명 강력하지만 모든 프로젝트를 C/C++로 하기에는 인생이 너무 짧습니다. Java나 C#과 같은 GC 헤택을 받는 언어를 배운다면 여러분들의 생산성에 날개가 달릴 것입니다.
  3. 그 외에, 혹시나 또 다른 언어를 위의 2개 언어만큼 잘하고 싶은 마음이 들고 여유가 생긴다면? 저는 Python을 권하고 싶습니다. 스크립트 성격도 겸하고 있으며 여러 모로 C/C++과 Java/C#과 같은 언어와 함께 배워두면 유용하게 쓸 것 같습니다.

저는 그래서, 이것으로 '언어 건드리기'는 (당분간 혁신적인 무언가가 없는 한) 이쯤에서 접고, 이제부터는 짬이 나는대로 Python 언어를 좀 더 배워볼까 합니다. ^^




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 7/10/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2014-10-02 04시15분
오픈북 프로젝트 - 양질의 컴퓨터 문서 공급 프로젝트
; http://itguru.tistory.com/201
정성태

1  2  3  4  5  [6]  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13493정성태12/19/20232851닷넷: 2185. C# - object를 QueryString으로 직렬화하는 방법
13492정성태12/19/20232487개발 환경 구성: 699. WSL에 nopCommerce 예제 구성
13491정성태12/19/20232312Linux: 63. 리눅스 - 다중 그룹 또는 사용자를 리소스에 권한 부여
13490정성태12/19/20232446개발 환경 구성: 698. Golang - GLIBC 의존을 없애는 정적 빌드 방법
13489정성태12/19/20232218개발 환경 구성: 697. GoLand에서 ldflags 지정 방법
13488정성태12/18/20232201오류 유형: 884. HTTP 500.0 - 명령행에서 실행한 ASP.NET Core 응용 프로그램을 실행하는 방법
13487정성태12/16/20232488개발 환경 구성: 696. C# - 리눅스용 AOT 빌드를 docker에서 수행 [1]
13486정성태12/15/20232300개발 환경 구성: 695. Nuget config 파일에 값 설정/삭제 방법
13485정성태12/15/20232172오류 유형: 883. dotnet build/restore - error : Root element is missing
13484정성태12/14/20232286개발 환경 구성: 694. Windows 디렉터리 경로를 WSL의 /mnt 포맷으로 구하는 방법
13483정성태12/14/20232407닷넷: 2184. C# - 하나의 resource 파일을 여러 프로그램에서 (AOT 시에도) 사용하는 방법파일 다운로드1
13482정성태12/13/20233109닷넷: 2183. C# - eFriend Expert OCX 예제를 .NET Core/5+ Console App에서 사용하는 방법 [2]파일 다운로드1
13481정성태12/13/20232416개발 환경 구성: 693. msbuild - .NET Core/5+ 프로젝트에서 resgen을 이용한 리소스 파일 생성 방법파일 다운로드1
13480정성태12/12/20232822개발 환경 구성: 692. Windows WSL 2 + Chrome 웹 브라우저 설치
13479정성태12/11/20232464개발 환경 구성: 691. WSL 2 (Ubuntu) + nginx 환경 설정
13477정성태12/8/20232721닷넷: 2182. C# - .NET 7부터 추가된 Int128, UInt128 [1]파일 다운로드1
13476정성태12/8/20232469닷넷: 2181. C# - .NET 8 JsonStringEnumConverter의 AOT를 위한 개선파일 다운로드1
13475정성태12/7/20232528닷넷: 2180. .NET 8 - 함수 포인터에 대한 Reflection 정보 조회파일 다운로드1
13474정성태12/6/20232339개발 환경 구성: 690. 닷넷 코어/5+ 버전의 ilasm/ildasm 실행 파일 구하는 방법 - 두 번째 이야기
13473정성태12/5/20232624닷넷: 2179. C# - 값 형식(Blittable)을 메모리 복사를 이용해 바이트 배열로 직렬화/역직렬화파일 다운로드1
13472정성태12/4/20232285C/C++: 164. Visual C++ - InterlockedCompareExchange128 사용 방법
13471정성태12/4/20232418Copilot - To enable GitHub Copilot, authorize this extension using GitHub's device flow
13470정성태12/2/20232752닷넷: 2178. C# - .NET 8부터 COM Interop에 대한 자동 소스 코드 생성 도입파일 다운로드1
13469정성태12/1/20232570닷넷: 2177. C# - (Interop DLL 없이) CoClass를 이용한 COM 개체 생성 방법파일 다운로드1
13468정성태12/1/20232409닷넷: 2176. C# - .NET Core/5+부터 달라진 RCW(Runtime Callable Wrapper) 대응 방식파일 다운로드1
13467정성태11/30/20232547오류 유형: 882. C# - Unhandled exception. System.Runtime.InteropServices.COMException (0x800080A5)파일 다운로드1
1  2  3  4  5  [6]  7  8  9  10  11  12  13  14  15  ...