Dart를 Client(Browser상에서)와 Server에서 사용할 수 있는지에 대해 물어보는 글 들을 메일링 리스트와 Stack Overflow에서 심심치 않게 볼 수 있습니다. 그래서 이 번 포스트에서는 예제를 통해 어떻게 하는 것인지 보여드리고자 합니다.
Server Side
첫번째로 Server Side에 대해서 다루고자 합니다. Dart는 Server에서 Python, Ruby그리고 Node.js와 같은 Server Side Script들 처럼 사용할 수 있습니다. Windows에서는 dart.exe <실행.dart> 방식으로 사용 됩니다. 다음 예제를 보시겠습니다.//MyScript.dart
main() => print("Hello Dart");
그리고, 이것을 Console 에서 "dart.exe MyScript.dart"을 실행 시키면 다음과 같은 결과물을 볼 수 있습니다.
Hello Dart
그럼에도 불구하고 우리는 좀 더 유용한 것을 원합니다. 그래서 저는 다음 Diagram을 통해 어떻게 어플리케이션을 만들 수 있는지 계획을 세웠습니다.
Server Side Component는 2가지 행동을 실행 할 것입니다.
1. Http Protocol을 통해서 브라우저에게 다음 static 파일들을 전송할 것입니다.
- MyApp.html - MyApp.dart - MyApp.dart.js
2. 나머지 GET Http Request에 대해서는 요청된 이름을 가진 JSON 데이터를 전송합니다. 예를 들면 http://localhost:8080/helloWorld(/helloWorld가 GET됩니다.)를 호출 하게 되면, 다음과 같은 JSON 데이터가 전송 됩니다.
{"get":"helloWorld"}
이를 위해서 우리는 dart:io Library를 통해 HTTPServer를 이용해야 합니다. 모든 Dart 앱들은 main() 함수를 통해 시작이 되므로, MyServer.dart라는 화일을 다음과 같이 구성해 보겠습니다.
#import("dart:io"); // dart:io Library를 가져옵니다. main() { HttpServer server = new HttpServer(); // Server를 생성합니다. serve.listen("127.0.0.1", 8080); // Listening을 시작합니다. }
Server는 "dart.exe MyServer.dart"를 통해 실행 시킬 수 있습니다. 아직 이것은 아무 동작도 되지 않습니다. 그래서 우리는 Request에 반응 할 수 있는 Callback함수를 추가해야 합니다. 처음엔 기본적인 Response를 추가하겠습니다. 이 함수는 HttpRequest를 통해 URL을 분석하여 JSON 데이터를 생성하여 HttpResponse를 통해 데이터를 내려주는 것입니다. Callback은 defaultRequestHandler 속성을 통해서 할당 합니다.
#import("dart:io"); // dart:io Library를 가져옵니다. #import("dart:json"); //json Library를 가져옵니다. main() { HttpServer server = new HttpServer(); // Server를 생성합니다. server.defaultRequestHandler = defaultHandler; // Handle 함수를 할당 합니다.(하단에 있습니다) serve.listen("127.0.0.1", 8080); // Listening을 시작합니다. print("http://127.0.0.1:8080 서버가 활성화 되었습니다"); } defaultHandler(HttpRequest req, HttpResponse res) { Map data = new Map(); // Response를 위한 Map을 생성합니다. print("${req.path}가 요청 되었습니다"); data["get"] == req.path; // 요청된 Path를 data에 담습니다. String responseData = JSON.stringify(data); // map을 JSON으로 변경 합니다. res.outputStream.writeString(responseData); // Client 에게 응답을 합니다. res.outputStream.close(); // 응답을 닫습니다. }
이제 Script를 실행 하고, 브라우저에 http://localhost:8080/hello를 입력하게 된다면 JSON데이터를 받을 수 있습니다.
두 번째로 실행 할 것은 Static파일 제공하기 입니다. 1회 요청에 3개 파일을 Load하고, 그 데이터를 응답하게 하는 방법을 알아보도록 하겠습니다. MyApp.html을 요청 받고, Browser에 정상적으로 데이터를 보내는 예제를 만들어 보도록 하겠습니다.
#import("dart:io"); // dart:io Library를 가져옵니다. #import("dart:json"); //json Library를 가져옵니다. main() { HttpServer server = new HttpServer(); // Server를 생성합니다. server.addRequestHandler(matchMyApp, myAppHandler); // 만약에 "myApp"로 시작하는 요청이 있으면 Handle합니다. server.defaultRequestHandler = defaultHandler; // Handle 함수를 할당 합니다.(하단에 있습니다) serve.listen("127.0.0.1", 8080); // Listening을 시작합니다. print("http://127.0.0.1:8080 서버가 활성화 되었습니다"); } defaultHandler(HttpRequest req, HttpResponse res) { Map data = new Map(); // Response를 위한 Map을 생성합니다. print("${req.path}가 요청 되었습니다"); data["get"] == req.path; // 요청된 Path를 data에 담습니다. String responseData = JSON.stringify(data); // map을 JSON으로 변경 합니다. res.outputStream.writeString(responseData); // Client 에게 응답을 합니다. res.outputStream.close(); // 응답을 닫습니다. } bool matchMyApp(HttpRequest req){ bool result = req.path.startsWith("/MyApp"); // MyApp으로 시작 되면 print("MyApp과 일치여부 : $result"); return result; } // 파일을 열고 Client에게 전송한다. void myAppHandler(HttpRequest req, HttpResponse res) { File file = new File(".${req.path}"); // 요청한 파일을 가져옵니다. 예): ./MyApp.html file.openInputStream().pipe(res.outputStream); // 요청에 응답합니다. }
이제 Server를 돌리게 되면, Client가 MyApp.html을 호출 시 정상적으로 응답해줄 수 있습니다.(지금은 Editor 마법사에 의해 Dart 앱이 만들어질 것입니다.)
GitHub에서 이 파일을 다운로드 받고 실행 하신 후, http://localhost:8080/MyApp.html 혹은 http://localhost:8080/helloWorld를 실행해보시기 바랍니다.
(이것은 에디터 빌드넘버 7232 에서 안정적으로 동작합니다.)
Client Side Dart...
Client Side의 Dart를 시작해봅시다. 우선 Editor에서 New > Web Application으로 예제 앱을 만들어 봅니다.( Server Side Dart에서 호출이 될 수 있도록 MyApp이라고 이름을 설정합니다.)
Editor는 MyApp.dart와 MyApp.html 파일을 생성할 것입니다.MyApp.dart는 최초에 다음과 같을 것입니다.
#import('dart:html'); class MyApp{ MyApp(){ } void run() { write("Hello World!"); } void write(String message) { // HTML library는 "document" 전역 변수를 정의 합니다. document.query('#status').innerHTML = message; } } void main(){ new MuApp().run(); // Class의 instance를 생성하고, run함수를 호출 합니다. // TODO : ui element를 이곳에 더 추가합니다. }
Editor에서 "Tools > Generate Javascript"를 선택 하면 MyApp.dart.js 파일을 생성할 수 있습니다. HTML 파일을 살펴보면, Dartium에서는 자동으로 MyApp.dart 파일을 Load할 수 있도록 링크가 걸려 있을 것입니다. 그리고, 이 HTML파일은 Dartium이 아닌 브라우저를 위해서 MyApp.dart.js를 Load할 수 있도록 되어있습니다.
이 파일들은 MyServer.dart와 같은 폴더에 위치 되어야 합니다. 다시 한번 dart.exe를 이용해 MyServer.dart를 실행 시키고, http://127.0.0.1:8080/MyApp.html 을 요청 해보면 정상적으로 동작 됨을 볼 수 있습니다.
MyApp.dart를 좀 더 개선해보기로 하겠습니다. UI Element들을 더 추가하고, XMLHttpRequest(XHR)을 이용하여 Server Side에서 응답하는 JSON데이터를 받아보는 함수를 만들어보도록 하겠습니다.
이 함수는 Textbox, Button 그리고 서버에서 응답하는 데이터를 표시 할 수 있도록 Span Tag를 추가 할 것입니다. 그리고, Button을 클릭하면 Textbox에 입력된 URL에 XMLHttpRequest 객체를 이용하여 "GET"요청을 실행 할 것입니다. 예를 들어, "HelloWorld"를 TextBox에 입력 하고 Button을 클릭하게 되면 "http://127.0.0.1:8080/HelloWorld"에 요청하고 해당 JSON데이터로 응답할 것입니다. 아마도 {"get":"HelloWorld"}가 될 것입니다.
코드로 표현 해보도록 하겠습니다.
#import('dart:html'); class MyApp{ MyApp(){ } void run() { write("Hello World!"); } void write(String message) { // HTML library는 "document" 전역 변수를 정의 합니다. document.query('#status').innerHTML = message; } } void main(){ new MuApp().run(); // Class의 instance를 생성하고, run함수를 호출 합니다. addUiElements() } addUiElements() { // UI를 추가합니다. var elements = new Element.html("""Enter some text:"""); document.body.nodes.add(elements); ButtonElement button = document.body.query("#myButton"); // Button을 선택합니다. // Button에 Click이벤트를 추가합니다. button.on.click.add(EventListener (data) { // TextBox에서 URL을 가져와 XHR호출을 하게 됩니다. // URL을 가져옵니다. InputElement textbox = document.body.query("#myTextbox"); // TextBox를 선택합니다. String url = "http://127.0.0.1:8080/${textbox.value}"; // 중요: url은 호출 된 서버와 같아야 합니다. // 만약에 http://localhost 로 호출을 하였다면, // url도 http://localhost로 변경해야 합니다. // 그렇지 않다면 Access-Control-Origin 에러가 발생 됩니다. // UI에 url을 적습니다. SpanElement requestSpan = document.body.query("#requestSpan"); // 요청을 기록할 Span requestSpan.innerHTML += "GET: $url"; // UI에 요청을 적습니다. // 서버로 부터 응답이 오면 실행 되는 부분 XMLHttpRequest request = new XMLHttpRequest.get(url, (XMLHttpRequest request) { SpanElement responseSpan = document.body.query("#responseSpan"); // 응답 Span responseSpan.innerHTML += "${request.responseText}"; // 응답을 출력합니다. }); }); }
Request=
Response=
MyApp.dart를 수정 한 후, 다시 한번 Javascript 생성을 해주시기 바랍니다.
여기까지의 코드는 GitHub에서 받아 볼 수 있습니다. 총 4개의 파일로 구성 되어있습니다.(MyServer.dart, MyApp.html, MyApp.dart, MyApp.dart.js)
간단히 이 파일들을 한 폴더 안에 위치 시키고, 다음 명령어를 내려보세요.
path/to/dart.exe MyServer.dart
그 이후, Daritum 혹은 브라우저로 다음 URL을 실행 해보세요.
http://127.0.0.1:8080/MyApp.html
Cross Origin에러가 발생 되었다면, XMLHttpRequest가 다른 서버에서 호출하고 있다는 거을 뜻합니다. 서버가 http://127.0.0.1:8080 인지 재확인 해주시기 바랍니다.
만약에 문제가 없다면 다음과 같은 화면을 보실 수 있게 됩니다.
여러분이 Dart를 Client Side와 Server Side에서 어떻게 사용하는지 이해되었기를 희망합니다.
이 Server Side Implementation은 믿을 수 없을 정도로 기초적입니다. 어떠한 Http Header를 제공하지 않습니다. 예를 들어, MyApp이라는 것만 골라내는 코드를 만들기는 상당히 어렵습니다. 여러분들이 한 번 도전 해보시기 바랍니다.
비록 이 블로그에서는 Dart를 Client Side와 Server Side에서 동시에 사용하고 있습니다만, 여러분들은 다른 언어로 만들어 볼 수 있습니다. 예로, Server Side Dart + Client Side Flash 혹은 Server Side Ruby + Client Side Dart 등등이 되겠습니다.
JSON을 다루는 것을 더 알기 원한다면, 이 문서를 참조 해주세요.
댓글 없음:
댓글 쓰기