Serverpod – The missing server for Flutter

Serverpod은 최근 Dart 언어로 Fullstack을 개발하는 개발자들 사이에서 많은 인기를 끌고 있는 서버 개발 프레임 워크 입니다. 아래는 공식 사이트 주소입니다.

https://serverpod.dev/

공식 사이트에 들어가면, 큰 글자로 “The missing server for Flutter”라고 되어 있으니, 결국 Dart/Flutter로 Client/App을 만들었다면, 자연스럽게 서버도 Dart 언어로 만들라는 의미라고 볼 수 있겠습니다.

일단, 어떻게 설치하고 사용하는지를 알아보도록 하겠습니다.

—————

1. 설치 및 개발 환경 구축

Serverpod의 설치 방법은 아래의 공식 사이트에서 확인할 수 있습니다.

https://docs.serverpod.dev/

Dart/Flutter가 설치되어 있어야 하고, 특이하게 Docker가 설치되어 있어야 합니다. Docker를 설치하는 이유는 Serverpod으로 만들어진 서버는 기본으로 Docker 컨테이너로 만들어진 Postgres 및 (optional한) Redis 데이터베이스를 활용하고 있습니다. 따라서, Serverpod을 다루고자 하는 경우에는, Docker에 대한 지식이 필요합니다.

설치 방법에 따라 다음의 명령으로 Serverpod을 설치합니다.

$ dart pub global activate serverpod_cli

설치가 잘 되었는지 다음의 명령으로 확인합니다. 즉 Serverpod 기본 명령어 입니다.

$ serverpod

그리고 Visual Code에 Serverpod 공식 플러그인을 찾아서 설치합니다.

2. 예제 프로그램 실행하기

예제 프로그램을 생성하고 실행하는 방법은 아래의 공식 사이트에서 확인할 수 있습니다.

https://docs.serverpod.dev/get-started

Serverpod을 통해서, 프로젝트에서 필요로 하는 server, client, flutter sample app 코드를 자동으로 생성할 수 있습니다. 이는 다음의 명령으로 가능합니다. 이 경우 프로젝트 이름을 mypod으로 한 예시입니다.

$ serverpod create mypod

이렇게 해서, mypod 폴더에는 다음의 폴더들이 생성됩니다.

(a) mypod_server: 이 패키지에는 서버 측 코드가 포함되어 있습니다. 서버에 필요한 새 Endpoint(클라이언트와의 통신 기능)나 기타 기능을 추가하려면 이를 수정합니다.

(b) mypod_client: 서버와 통신하는데 필요한 코드입니다. 일반적으로 이 패키지의 모든 코드는 자동으로 생성되므로 이 패키지의 파일을 편집하면 안 됩니다.

(c) mypod_flutter: 로컬 서버에 연결하도록 사전 구성된 Flutter 앱 입니다. 이를 참조하여 Client/App을 개발합니다.

서버의 실행은 다음과 같이 합니다. 이를 위해서 서버 실행을 위한 별도의 터미널을 사용하기를 권합니다.

$ cd mypod/mypod_server

$ docker compose up –build –detach

$ dart bin/main.dart –apply-migrations

아래와 같은 화면이 나오면 정상적으로 실행된 것입니다.

SERVERPOD version: 1.x.x, mode: development, time: 2022-09-12 17:22:02.825468Z
Insights listening on port 8081
Server default listening on port 8080
Webserver listening on port 8082

Flutter 앱의 실행은 다음과 같이 합니다.

$ cd mypod/mypod_flutter

$ flutter run -d chrome

Flutter 앱의 입력 창에 이름을 넣고 서버로 보내면, 서버에서 Hello 문장을 앞에 추가해서 응답하는 것을 볼 수 있습니다.

서버의 코드들에서 가장 중요한 폴더와 화일들은 아래와 같습니다.

config: 이는 Serverpod의 구성 파일입니다. 여기에는 password.yaml 개발, 준비 및 프로덕션 단계에서 서버를 실행하기 위한 구성과 비밀번호가 포함된 파일이 포함됩니다. 기본적으로 모든 것이 서버를 로컬에서 실행하도록 올바르게 구성되어 있습니다.

lib/src/endpoints: 여기에 서버의 Endpoint를 배치합니다. Endpoint에 메서드를 추가하면 Serverpod는 클라이언트에 해당 메서드를 생성합니다.

lib/src/models: 모델 정의 파일이 여기에 배치됩니다. 파일은 API를 통해 전달할 수 있는 클래스와 해당 클래스가 데이터베이스와 관련되는 방식을 정의합니다. Serverpod는 모델 정의에서 직렬화 가능한 객체를 생성합니다.

실행중인 서버를 멈추고 싶다면, 서버 프로그램은 Control-C 버튼으로 중지시키고, Docker compose down을 실행합니다.

3. 새로운 기능 추가

서버에 새로운 기능을 추가하는 작업은 Endpoint를 추가하는 것으로 수행됩니다. Endpoint는 클라이언트에서 서버로의 연결 지점입니다.

Serverpod 사용시, Endpoint에 메서드를 추가하면 클라이언트 코드가 생성됩니다. 코드를 생성하려면 서버의 lib/src/endpoints 디렉터리에 Endpoint를 배치해야 합니다. Endpoint는 Endpoint 클래스를 확장해야 합니다. 메소드를 생성하는 경우, Future 타입을 반환해야 하며, 첫 번째 매개변수는 Session 객체여야 합니다. Session 객체는 진행 중인 호출에 대한 정보를 보유하고 데이터베이스에 대한 액세스를 제공합니다.

import 'package:serverpod/serverpod.dart';

class ExampleEndpoint extends Endpoint {
  Future<String> hello(Session session, String name) async {
    return 'Hello $name';
  }
}

위의 코드로부터 example이라는 Endpoint가 생성됩니다. 즉, ExampleEndpoint 문자열에서 Endpoint 문자열을 삭제한 후, Example의 E를 소문자로 변환하여 endpoint 문자열로 명명된 Endpoint가 만들어 집니다. 이제 serverpod generate를 수행하면, client가 호출 가능한 코드도 생성됩니다.

이제 클라이언트 측에서 다음을 호출하여 메소드를 호출할 수 있습니다.

var result = await client.example.hello('World');

4. 새로운 기능 추가시 프로젝트 전체 업데이트

새로운 기능을 추가하기 위하여, endpoints 또는 models 폴더에서 코드를 변경할 때마다 Serverpod에서 관리하는 클래스를 다시 생성해야 합니다.  이는 다음의 명령으로 수행됩니다.

$ cd mypod/mypod_server

$ serverpod generate

—————

Serverpod에 대한 첫인상은 여러모로, gRPC와 유사합니다.

Client/server의 코드를 자동 생성하는 방식이나, client/server가 주고 받는 데이터를 정의하는 것, 그리고 스트리밍을 지원하는 등을 보면 하부에 gRPC가 동작하나 싶은 생각도 듭니다. 2022년에 비슷한 질문이 GitHub의 Serverpod 토론 사이트에 올라온 것을 찾을 수 있는데, 결론적으로는 비슷하지만 자체적인 방식을 사용하는 것으로 답변이 있기는 합니다 ( https://github.com/serverpod/serverpod/discussions/267 ).

추후 기회가 되면, 이전에 다뤘던 RESTful API 서버와 클라이언트를 Serverpod으로 만들어 보도록 하겠습니다.

[참조] Installing Serverpod : https://docs.serverpod.dev/
[참조] Get started : https://docs.serverpod.dev/get-started
[참조] Working with endpoints : https://docs.serverpod.dev/concepts/working-with-endpoints

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다