Microsoft MVP성태의 닷넷 이야기
개발 환경 구성: 243. Scala 개발 환경 구성(JVM, 닷넷) [링크 복사], [링크+제목 복사],
조회: 24044
글쓴 사람
정성태 (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
정성태

... 61  62  63  64  65  66  67  68  [69]  70  71  72  73  74  75  ...
NoWriterDateCnt.TitleFile(s)
12211정성태4/27/202019264개발 환경 구성: 486. WSL에서 Makefile로 공개된 리눅스 환경의 C/C++ 소스 코드 빌드
12210정성태4/20/202020706.NET Framework: 903. .NET Framework의 Strong-named 어셈블리 바인딩 (1) - app.config을 이용한 바인딩 리디렉션 [1]파일 다운로드1
12209정성태4/13/202017414오류 유형: 614. 리눅스 환경에서 C/C++ 프로그램이 Segmentation fault 에러가 발생한 경우 (2)
12208정성태4/12/202015975Linux: 29. 리눅스 환경에서 C/C++ 프로그램이 Segmentation fault 에러가 발생한 경우
12207정성태4/2/202015821스크립트: 19. Windows PowerShell의 NonInteractive 모드
12206정성태4/2/202018439오류 유형: 613. 파일 잠금이 바로 안 풀린다면? - The process cannot access the file '...' because it is being used by another process.
12205정성태4/2/202015105스크립트: 18. Powershell에서는 cmd.exe의 명령어를 지원하진 않습니다.
12204정성태4/1/202015104스크립트: 17. Powershell 명령어에 ';' (semi-colon) 문자가 포함된 경우
12203정성태3/18/202017949오류 유형: 612. warning: 'C:\ProgramData/Git/config' has a dubious owner: '...'.
12202정성태3/18/202021208개발 환경 구성: 486. .NET Framework 프로젝트를 위한 GitLab CI/CD Runner 구성
12201정성태3/18/202018438오류 유형: 611. git-credential-manager.exe: Using credentials for username "Personal Access Token". [1]
12200정성태3/18/202018529VS.NET IDE: 145. NuGet + Github 라이브러리 디버깅 관련 옵션 3가지 - "Enable Just My Code" / "Enable Source Link support" / "Suppress JIT optimization on module load (Managed only)"
12199정성태3/17/202016172오류 유형: 610. C# - CodeDomProvider 사용 시 Unhandled Exception: System.IO.DirectoryNotFoundException: Could not find a part of the path '...\f2_6uod0.tmp'.
12198정성태3/17/202019531오류 유형: 609. SQL 서버 접속 시 "Cannot open user default database. Login failed."
12197정성태3/17/202018828VS.NET IDE: 144. .NET Core 콘솔 응용 프로그램을 배포(publish) 시 docker image 자동 생성 - 두 번째 이야기 [1]
12196정성태3/17/202015948오류 유형: 608. The ServicedComponent being invoked is not correctly configured (Use regsvcs to re-register).
12195정성태3/16/202018267.NET Framework: 902. C# - 프로세스의 모든 핸들을 열람 - 세 번째 이야기
12194정성태3/16/202020996오류 유형: 607. PostgreSQL - Npgsql.NpgsqlException: sorry, too many clients already
12193정성태3/16/202017913개발 환경 구성: 485. docker - SAP Adaptive Server Enterprise 컨테이너 실행 [1]
12192정성태3/14/202019938개발 환경 구성: 484. docker - Sybase Anywhere 16 컨테이너 실행
12191정성태3/14/202021046개발 환경 구성: 483. docker - OracleXE 컨테이너 실행 [1]
12190정성태3/14/202015628오류 유형: 606. Docker Desktop 업그레이드 시 "The process cannot access the file 'C:\Program Files\Docker\Docker\resources\dockerd.exe' because it is being used by another process."
12189정성태3/13/202021231개발 환경 구성: 482. Facebook OAuth 처리 시 상태 정보 전달 방법과 "유효한 OAuth 리디렉션 URI" 설정 규칙
12188정성태3/13/202026020Windows: 169. 부팅 시점에 실행되는 chkdsk 결과를 확인하는 방법
12187정성태3/12/202015597오류 유형: 605. NtpClient was unable to set a manual peer to use as a time source because of duplicate error on '...'.
12186정성태3/12/202017402오류 유형: 604. The SysVol Permissions for one or more GPOs on this domain controller and not in sync with the permissions for the GPOs on the Baseline domain controller.
... 61  62  63  64  65  66  67  68  [69]  70  71  72  73  74  75  ...