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

... [16]  17  18  19  20  21  22  23  24  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
13240정성태2/1/20234143디버깅 기술: 187. ASP.NET Web Application (.NET Framework) 프로젝트의 숨겨진 예외 - System.Web.HttpException
13239정성태2/1/20233826디버깅 기술: 186. C# - CacheDependency의 숨겨진 예외 - System.Web.HttpException
13238정성태1/31/20235932.NET Framework: 2092. IIS 웹 사이트를 TLS 1.2 또는 TLS 1.3 프로토콜로만 운영하는 방법
13237정성태1/30/20235609.NET Framework: 2091. C# - 웹 사이트가 어떤 버전의 TLS/SSL을 지원하는지 확인하는 방법
13236정성태1/29/20235144개발 환경 구성: 663. openssl을 이용해 인트라넷 IIS 사이트의 SSL 인증서 생성
13235정성태1/29/20234720개발 환경 구성: 662. openssl - 윈도우 환경의 명령행에서 SAN 적용하는 방법
13234정성태1/28/20235813개발 환경 구성: 661. dnSpy를 이용해 소스 코드가 없는 .NET 어셈블리의 코드를 변경하는 방법 [1]
13233정성태1/28/20237207오류 유형: 840. C# - WebClient로 https 호출 시 "The request was aborted: Could not create SSL/TLS secure channel" 예외 발생
13232정성태1/27/20234937스크립트: 43. uwsgi의 --processes와 --threads 옵션
13231정성태1/27/20233938오류 유형: 839. python - TypeError: '...' object is not callable
13230정성태1/26/20234300개발 환경 구성: 660. WSL 2 내부로부터 호스트 측의 네트워크로 UDP 데이터가 1개의 패킷으로만 제한되는 문제
13229정성태1/25/20235292.NET Framework: 2090. C# - UDP Datagram의 최대 크기
13228정성태1/24/20235394.NET Framework: 2089. C# - WMI 논리 디스크가 속한 물리 디스크의 정보를 얻는 방법 [2]파일 다운로드1
13227정성태1/23/20235083개발 환경 구성: 659. Windows - IP MTU 값을 바꿀 수 있을까요? [1]
13226정성태1/23/20234768.NET Framework: 2088. .NET 5부터 지원하는 GetRawSocketOption 사용 시 주의할 점
13225정성태1/21/20233951개발 환경 구성: 658. Windows에서 실행 중인 소켓 서버를 다른 PC 또는 WSL에서 접속할 수 없는 경우
13224정성태1/21/20234366Windows: 221. Windows - Private/Public/Domain이 아닌 네트워크 어댑터 단위로 방화벽을 on/off하는 방법
13223정성태1/20/20234544오류 유형: 838. RDP 연결 오류 - The two computers couldn't connect in the amount of time allotted
13222정성태1/20/20234251개발 환경 구성: 657. WSL - DockerDesktop.vhdx 파일 위치를 옮기는 방법
13221정성태1/19/20234448Linux: 57. C# - 리눅스 프로세스 메모리 정보파일 다운로드1
13220정성태1/19/20234535오류 유형: 837. NETSDK1045 The current .NET SDK does not support targeting .NET ...
13219정성태1/18/20234145Windows: 220. 네트워크의 인터넷 접속 가능 여부에 대한 판단 기준
13218정성태1/17/20234071VS.NET IDE: 178. Visual Studio 17.5 (Preview 2) - 포트 터널링을 이용한 웹 응용 프로그램의 외부 접근 허용
13217정성태1/13/20234670디버깅 기술: 185. windbg - 64비트 운영체제에서 작업 관리자로 뜬 32비트 프로세스의 덤프를 sos로 디버깅하는 방법
13216정성태1/12/20234928디버깅 기술: 184. windbg - 32비트 프로세스의 메모리 덤프인 경우 !peb 명령어로 나타나지 않는 환경 변수
13215정성태1/11/20236575Linux: 56. 리눅스 - /proc/pid/stat 정보를 이용해 프로세스의 CPU 사용량 구하는 방법 [1]
... [16]  17  18  19  20  21  22  23  24  25  26  27  28  29  30  ...