읽기 전에

Mono에서 웹개발을 하고 싶다면 OWIN 프로젝트를 활용하자. 차후 .NET mvc 프레임웍도 owin 기반에서 구동 가능할 예정이다.

tl;dr

  • Mono에서 MVC5 지금은 안됨
  • .Net 개발은 정신 건강을 위해 Windows 위에서 하자

요즘 닷넷 스터디를 한창 하고 있는데 요번에 새로 나온 MVC5를 기준으로 스터디가 진행되고 있다. 아직 윈도우 개발 환경이 준비 안된 탓에 이 MVC5 프로젝트를 Mono 환경에서 구동해보려고 했는데 결과적으로는 운용조차 해볼 수 없었다. 안된다고 딱 잘라 말하는 글이 하나도 없어서 에러 로그를 정리해 올려보려고 한다. 참고로 Mono의 호환 현황은 Mono 공식 사이트의 Compatibility에서 확인할 수 있다.1

웹으로 접속하면 다음의 에러가 발생한다.

Missing method System.Web.Hosting.HostingEnvironment::get_InClientBuildManager() in assembly /Library/Frameworks/Mono.framework/Versions/3.2.4/lib/mono/gac/System.Web/4.0.0.0__b03f5f7f11d50a3a/System.Web.dll, referenced in assembly /private/tmp/root-temp-aspnet-0/8717103c/assembly/shadow/d4ff52ca/402bd257_94d4809d_00000001/WebActivatorEx.dll

Application Exception System.TypeLoadException Could not load type 'System.Web.Http.WebHost.HttpControllerHandler' from assembly 'System.Web.Http.WebHost, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.

Description: HTTP 500.Error processing request. Details: Non-web exception. Exception origin (name of application or object): System.Web. Exception stack trace: at System.Web.Routing.RouteCollection.GetRouteData (System.Web.HttpContextBase httpContext)
[0x00000] in <filename unknown>:0 at System.Web.Routing.UrlRoutingModule.PostResolveRequestCache (System.Web.HttpContextBase context)
[0x00000] in <filename unknown>:0 at System.Web.Routing.UrlRoutingModule.PostResolveRequestCache (System.Object o, System.EventArgs e)
[0x00000] in <filename unknown>:0 at System.Web.HttpApplication+<RunHooks>c__Iterator5.MoveNext ()
[0x00000] in <filename unknown>:0 at System.Web.HttpApplication+<Pipeline>c__Iterator6.MoveNext ()
[0x00000] in <filename unknown>:0 at System.Web.HttpApplication.Tick ()
[0x00000] in <filename unknown>:0 Version Information: 3.2.4 ((no/294f999 Fri Oct 25 20:18:12 EDT 2013); ASP.NET Version: 4.0.30319.17020 Powered by Mono

위 에러는 get_InClientBuildManager 메소드가 없어 나타나는 문제로 Mono에서 구현된 System.Web.Hosting.HostingEnvironment에 해당 메소드가 구현되어 있지 않다. 그래서 MS에서 배포한 라이브러리 dll을 사용해 시도했다. System.Web.dll을 local copy 해서 다시 구동했다. 다음은 MVC5 프로젝트를 Mono의 xsp4로 구동했을 때 나오는 에러다.

Handling exception type TargetInvocationException Message is Exception has been thrown by the target of an invocation. IsTerminating is set to True System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.

Server stack trace: at System.Reflection.MonoCMethod.InternalInvoke (System.Object obj, System.Object[] parameters)
[0x00000] in <filename unknown>:0 at System.Reflection.MonoCMethod.DoInvoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture)
[0x00000] in <filename unknown>:0 at System.Reflection.MonoCMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture)
[0x00000] in <filename unknown>:0 at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters)
[0x00000] in <filename unknown>:0 at System.Runtime.Serialization.ObjectRecord.LoadData (System.Runtime.Serialization.ObjectManager manager, ISurrogateSelector selector, StreamingContext context)
[0x00000] in <filename unknown>:0 at System.Runtime.Serialization.ObjectManager.DoFixups ()
[0x00000] in <filename unknown>:0 at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadNextObject (System.IO.BinaryReader reader)
[0x00000] in <filename unknown>:0 at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObjectGraph (BinaryElement elem, System.IO.BinaryReader reader, Boolean readHeaders, System.Object& result, System.Runtime.Remoting.Messaging.Header[]& headers)
[0x00000] in <filename unknown>:0 at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.NoCheckDeserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler)
[0x00000] in <filename unknown>:0 at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream)
[0x00000] in <filename unknown>:0 at System.Runtime.Remoting.RemotingServices.DeserializeCallData (System.Byte[] array)
[0x00000] in <filename unknown>:0 at (wrapper xdomain-dispatch) System.AppDomain:DoCallBack (object,byte[]&,byte[]&)

Exception rethrown at [0]: ---> System.ArgumentException: Couldn't bind to method 'SetHostingEnvironment'. at System.Delegate.GetCandidateMethod (System.Type type, System.Type target, System.String method, BindingFlags bflags, Boolean ignoreCase, Boolean throwOnBindFailure)
[0x00000] in <filename unknown>:0 at System.Delegate.CreateDelegate (System.Type type, System.Type target, System.String method, Boolean ignoreCase, Boolean throwOnBindFailure)
[0x00000] in <filename unknown>:0 at System.Delegate.CreateDelegate (System.Type type, System.Type target, System.String method)
[0x00000] in <filename unknown>:0 at System.DelegateSerializationHolder+DelegateEntry.DeserializeDelegate (System.Runtime.Serialization.SerializationInfo info)
[0x00000] in <filename unknown>:0 at System.DelegateSerializationHolder..ctor (System.Runtime.Serialization.SerializationInfo info, StreamingContext ctx)
[0x00000] in <filename unknown>:0 at (wrapper managed-to-native) System.Reflection.MonoCMethod:InternalInvoke (System.Reflection.MonoCMethod,object,object[],System.Exception&) at System.Reflection.MonoCMethod.InternalInvoke (System.Object obj, System.Object[] parameters)
[0x00000] in <filename unknown>:0 --- End of inner exception stack trace --- at (wrapper xdomain-invoke) System.AppDomain:DoCallBack (System.CrossAppDomainDelegate) at (wrapper remoting-invoke-with-check) System.AppDomain:DoCallBack (System.CrossAppDomainDelegate) at System.Web.Hosting.ApplicationHost.CreateApplicationHost (System.Type hostType, System.String virtualDir, System.String physicalDir)
[0x00000] in <filename unknown>:0 at Mono.WebServer.VPathToHost.CreateHost (Mono.WebServer.ApplicationServer server, Mono.WebServer.WebSource webSource)
[0x00000] in <filename unknown>:0 at Mono.WebServer.XSP.Server.RealMain (System.String[] args, Boolean root, IApplicationHost ext_apphost, Boolean quiet)
[0x00000] in <filename unknown>:0 at Mono.WebServer.XSP.Server.Main (System.String[] args)
[0x00000] in <filename unknown>:0 [ERROR] FATAL UNHANDLED EXCEPTION: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.

Server stack trace: at System.Reflection.MonoCMethod.InternalInvoke (System.Object obj, System.Object[] parameters)
[0x00000] in <filename unknown>:0 at System.Reflection.MonoCMethod.DoInvoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture)
[0x00000] in <filename unknown>:0 at System.Reflection.MonoCMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture)
[0x00000] in <filename unknown>:0 at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters)
[0x00000] in <filename unknown>:0 at System.Runtime.Serialization.ObjectRecord.LoadData (System.Runtime.Serialization.ObjectManager manager, ISurrogateSelector selector, StreamingContext context)
[0x00000] in <filename unknown>:0 at System.Runtime.Serialization.ObjectManager.DoFixups ()
[0x00000] in <filename unknown>:0 at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadNextObject (System.IO.BinaryReader reader)
[0x00000] in <filename unknown>:0 at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObjectGraph (BinaryElement elem, System.IO.BinaryReader reader, Boolean readHeaders, System.Object& result, System.Runtime.Remoting.Messaging.Header[]& headers)
[0x00000] in <filename unknown>:0 at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.NoCheckDeserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler)
[0x00000] in <filename unknown>:0 at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream)
[0x00000] in <filename unknown>:0 at System.Runtime.Remoting.RemotingServices.DeserializeCallData (System.Byte[] array)
[0x00000] in <filename unknown>:0 at (wrapper xdomain-dispatch) System.AppDomain:DoCallBack (object,byte[]&,byte[]&)

Exception rethrown at [0]: ---> System.ArgumentException: Couldn't bind to method 'SetHostingEnvironment'. at System.Delegate.GetCandidateMethod (System.Type type, System.Type target, System.String method, BindingFlags bflags, Boolean ignoreCase, Boolean throwOnBindFailure)
[0x00000] in <filename unknown>:0 at System.Delegate.CreateDelegate (System.Type type, System.Type target, System.String method, Boolean ignoreCase, Boolean throwOnBindFailure)
[0x00000] in <filename unknown>:0 at System.Delegate.CreateDelegate (System.Type type, System.Type target, System.String method)
[0x00000] in <filename unknown>:0 at System.DelegateSerializationHolder+DelegateEntry.DeserializeDelegate (System.Runtime.Serialization.SerializationInfo info)
[0x00000] in <filename unknown>:0 at System.DelegateSerializationHolder..ctor (System.Runtime.Serialization.SerializationInfo info, StreamingContext ctx)
[0x00000] in <filename unknown>:0 at (wrapper managed-to-native) System.Reflection.MonoCMethod:InternalInvoke (System.Reflection.MonoCMethod,object,object[],System.Exception&) at System.Reflection.MonoCMethod.InternalInvoke (System.Object obj, System.Object[] parameters)
[0x00000] in <filename unknown>:0 --- End of inner exception stack trace --- at (wrapper xdomain-invoke) System.AppDomain:DoCallBack (System.CrossAppDomainDelegate) at (wrapper remoting-invoke-with-check) System.AppDomain:DoCallBack (System.CrossAppDomainDelegate) at System.Web.Hosting.ApplicationHost.CreateApplicationHost (System.Type hostType, System.String virtualDir, System.String physicalDir)
[0x00000] in <filename unknown>:0 at Mono.WebServer.VPathToHost.CreateHost (Mono.WebServer.ApplicationServer server, Mono.WebServer.WebSource webSource)
[0x00000] in <filename unknown>:0 at Mono.WebServer.XSP.Server.RealMain (System.String[] args, Boolean root, IApplicationHost ext_apphost, Boolean quiet)
[0x00000] in <filename unknown>:0 at Mono.WebServer.XSP.Server.Main (System.String[] args)

위 기록을 보면 Mono에서 구현한 xsp 서버에 연결이 되지 않는데 SetHostingEnvironment 메소드가 제대로 바인딩 되지 않는다. 에러가 나는 두가지 모두 빌드는 제대로 진행 되는 것 보면, 추측컨데 xsp에서 구현되지 않은 부분이 문제일 뿐이지 본 프로젝트에 작성된 코드는 문제가 없는 것 같다. xsp 구현을 들여다보고 손을 댈려고 했지만, 간단한 문제라면 이미 수정이 되었을 것이고 그로 미루어 볼 때 내 작업으로 만들 수 있는 수준이 아닌듯 싶어서 코드를 좀 보다가 말았다. (게다가… C#은 전혀 모르는 쪽이니까.)

MVC5가 OWIN을 지원한다는 얘기를 들었는데 xsp 이외의 방법으로 이종 플랫폼에서 구동할 수 있는 방법이 있을 법도 하다. 하지만 문서도 잘 검색이 안되고 사용자도 너무 작아, 명목상의 존재 의의만 다하고 있는 것 같아 아쉽다.2 윈도우 환경을 위해 구입한 선더볼트 외장하드가 오면 mono를 쓸 일이 없어져 그렇게 큰 고민거리는 아니긴 하지만, 다른 플랫폼에서 닷넷을 오픈소스로 구현하기 위해 애쓰는 모습이 존경스럽다. 이 구현이 성숙해 닷넷 플랫폼을 온전한 오픈소스로 구동할 수 있다면 더 재미있는 프로젝트가 닷넷으로 작성되지 않을까 생각해본다.

  • 이 목록을 확인하지 않고서 무턱대고 시작한게 화근이었다. 아직 MVC4도 완전히 지원하는 상황이 아니다. 
  • 그리고 이 문제를 해결하기 위해 엄청 검색을 했었는데 비슷한 문제를 버전 대대로 겪은 사람들이 많이 나왔다. 물론 구현 자체가 없으니 아무도 해결하지 못했다. 
  • 색상을 바꿔요

    눈에 편한 색상을 골라보세요 :)

    Darkreader 플러그인으로 선택한 색상이 제대로 표시되지 않을 수 있습니다.