[flutter] 플러터를 사용하여 소켓 통신을 위한 SSL/TLS 연결 구현 방법

플러터(Flutter)는 구글에서 개발한 UI 프레임워크로, 다양한 플랫폼에서 동작하는 모바일 애플리케이션을 개발할 수 있도록 지원합니다. 이번 글에서는 플러터를 사용하여 소켓 통신을 위한 SSL/TLS 연결을 구현하는 방법에 대해 알아보겠습니다.

1. 패키지 추가

먼저, 아래의 패키지를 pubspec.yaml 파일에 추가해야 합니다.

dependencies:
  flutter_socket_io: ^0.6.0
  flutter_socket_io_example_helper: ^0.0.1

위의 패키지는 플러터에서 소켓 통신을 지원하기 위해 필요한 패키지입니다.

2. SSL/TLS 설정 구현

SSL/TLS 연결을 위해 flutter_socket_io: ^0.6.0 패키지에서 제공하는 IOWebSocket을 사용할 수 있습니다. 다음은 예시 코드입니다.

import 'package:flutter_socket_io/flutter_socket_io.dart';
import 'package:flutter_socket_io/socket_io_manager.dart';

final SocketIOManager socketIOManager = SocketIOManager();

void connectToServer() async {
  SocketIO socket = await socketIOManager.createInstance(SocketOptions(
      // 서버 URL 설정
      'https://example.com',
      enableLogging: true,
      transports: [Transports.WEB_SOCKET], // 웹소켓 사용
      secure: true, // SSL/TLS 연결
      query: {
        // 추가적인 쿼리 파라미터 설정 (선택)
        'token': '<YOUR_TOKEN>',
      }));

  socket.onConnect((data) {
    print('Connected to server: $data');
  });

  socket.onDisconnect((data) {
    print('Disconnected from server: $data');
  });

  socket.emit('join_room', ['room1']); // 예시로 join_room 이벤트 전송
}

위 코드에서는 SocketOptions를 사용하여 소켓 연결을 설정하고, socketIOManager.createInstance()를 통해 소켓 인스턴스를 만들고 연결합니다. secure 옵션을 true로 설정하여 SSL/TLS 연결을 가능하게 합니다. 연결이 성공하면 onConnect 콜백이 호출되고, 연결이 끊어지면 onDisconnect 콜백이 호출됩니다.

3. SSL 인증서 처리

SSL/TLS 연결을 위해 서버의 SSL 인증서를 신뢰할 수 있도록 해야 합니다. Android 및 iOS에서는 서버의 인증서를 포함한 Intermediate CA 인증서를 상수로 지정하여 인증서 체인을 생성해야 합니다.

Android의 경우, android/app/src/main/res/raw 폴더에 <your_certificate>.cer 파일을 추가한 후, MainActivity.java 파일을 열고 아래 코드를 추가합니다.

import androidx.annotation.NonNull;
import android.os.Bundle;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.plugin.common.MethodChannel;
import java.io.IOException;
import java.io.InputStream;

public class MainActivity extends FlutterActivity {
    private static final String METHOD_CHANNEL = "com.example/flutter_ssl_pinning";

    @Override
    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine);
        new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), METHOD_CHANNEL).setMethodCallHandler(
        (call, result) -> {
           if (call.method.equals("getCertificate")) {
              // SSL 인증서 반환
              result.success(getCertificate());
           } else {
              result.notImplemented();
           }
        });
    }

    private byte[] getCertificate() {
       InputStream inputStream = getResources().openRawResource(R.raw.your_certificate);
       try {
           int size = inputStream.available();
           byte[] buffer = new byte[size];
           inputStream.read(buffer);
           inputStream.close();
           return buffer;
       } catch (IOException e) {
           e.printStackTrace();
           return null;
       }
    }
}

iOS의 경우, Xcode를 열고 프로젝트를 선택한 후, Runner > Runner > Copy Bundle Resources를 클릭하여 인증서를 추가한 후, AppDelegate.swift 파일을 열고 아래 코드를 추가합니다.

import Flutter

class AppDelegate: FlutterAppDelegate {
    let METHOD_CHANNEL = "com.example/flutter_ssl_pinning"

    override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        GeneratedPluginRegistrant.register(with: self)
        let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
        let certificate Channel = FlutterMethodChannel(name: METHOD_CHANNEL, binaryMessenger: controller.binaryMessenger)
        certificateChannel.setMethodCallHandler({
            (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
             if call.method == "getCertificate" {
                // SSL 인증서 반환
                result(self.getCertificate())
             } else {
                result(FlutterMethodNotImplemented)
             }
         })
         return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }

    private func getCertificate() -> Data {
        let path = Bundle.main.path(forResource: "your_certificate", ofType: "cer")!
        let url = URL(fileURLWithPath: path)
        let data = try! Data(contentsOf: url)
        return data
    }
}

위 코드에서는 MethodChannel을 사용하여 getCertificate()를 호출하면 인증서 데이터를 반환합니다. 이후 flutter_socket_io 패키지에서 인증서를 사용할 수 있습니다.

참고 자료