WebView 에서 앱으로 권한요청 처리하기

김재욱·2023년 6월 3일
0

Flutter

목록 보기
2/2

Front와 Flutter의 통신 과정

WebView로 웹을 띄워서 앱을 만드는 과정은 너무나도 간편했고 좋았다. 하지만 이후 해결해야 할 문제가 있었는데, 이전까지는 웹에서 마이크 사용 권한 요청을 하면 웹에서 처리를 해줬는데 WebView로 띄워지지만 앱에서는 웹이 아닌 앱이므로 권한요청이 실행되지 않는다. 그래서 원하는 동작이 있을 때마다 프런트와 Flutter가 Bridge를 통해서 통신을 추가로 해야 한다.

이전에 Flutter에서 WebView를 띄우는 과정은 그냥 있는 web을 Flutter가 받아와서 띄우는 느낌이라 프런트에서 별다른 통신을 할 필요가 없었지만, 권한 요청과 같은 작업은 다음과 같은 순서로 이루어진다.
1. 프론트 -> Flutter (요청 메시지)
2. Flutter 요청 메시지에 따른 작업 수행

따라서 프런트에도 Flutter에 요청을 하는 Bridge 함수를 작성해야 하고, Flutter에서도 받은 메시지에 따라 특정 기능을 수행하는 함수를 작성해야 한다.

Front에서 Bridge 요청함수 작성하기

//bridge.d.ts
import { BridgeController } from '@/bridge/Bridge.Controller';
import { MicrophonePermissionBridgeService } from '@/bridge/services/Bridge.MicrphonePermission.service';

declare global {
 interface Window {
   FlutterBridge?: {
     postMessage: (status: string) => void;
   };
   MicrophonePermissionBridge: BridgeController<MicrophonePermissionBridgeService>;
 }
}

이런 식으로 내가 미리 작성해 둔 MicrophonePermissionBridge 함수를 호출하여 작성하면 된다.

아래는 MicrophonePermissionBridge 함수이다.

//MicrophonePermissionBridge
import { AgentController } from '@/util/models/Agent';
import { BridgeController, BridgeService } from '@/bridge';

export type MicPermissionResponseParam = 'GRANTED' | 'DENIED';
export type MicPermissionRequestMessage = 'requestMicrophonePermission';

export class MicrophonePermissionBridgeService
  implements BridgeService<MicPermissionRequestMessage, MicPermissionResponseParam>
{
  constructor(private agentController: AgentController, private isGranted: boolean = false) {}

  public receive(params: MicPermissionResponseParam): void {
    if (params === 'GRANTED') {
      this.setIsGranted(true);
      return;
    }
    this.setIsGranted(false);
  }

  public send(message: MicPermissionRequestMessage): void {
    window.FlutterBridge?.postMessage(message);
  }

  public getIsGranted(): boolean {
    return this.isGranted;
  }

  private setIsGranted(granted: boolean): void {
    this.isGranted = granted;
  }
}

export const microphonePermissionBridgeController = new BridgeController(
  new MicrophonePermissionBridgeService(new AgentController())
);

if (typeof window !== 'undefined') {
  window.MicrophonePermissionBridge = microphonePermissionBridgeController;
}

MicrophonePermissionBridge 함수를 위와 같이 작성하고 나면 프런트에서 아래와 같이 이 함수를 호출하여 작성할 수 있다.

useEffect(() => {
	//WebView 일때만 아래 함수를 실행한다.
    if (agentController.isOnWebview()) {
      const describe = micPermissionController.subscribe(function () {
        //micPermissionController.receiveMessage 호출 시 마다 옵저버 함수가 실행돼, 리액트 라이프사이클과 연동
        setIsGranted(this.getServiceImpl().getIsGranted());
      });
      micPermissionController.requestMessage('requestMicrophonePermission');

      return () => {
        describe();
      };
    } else {
      setIsGranted(true);
    }
  }, []);

다음과 같이 작성하게 되면 webView일 때 Flutter에게 requestMicrophonePermission이라는 메시지를 보내고, 이후 Flutter에서 보내오는 메시지에 따라 다르게 작동될 것이다.

Flutter에서 요청받은 message handling 하기

 
  void initState() {
    super.initState();
    _controller = PlatformWebViewController(
      // TODO: iOS
      AndroidWebViewControllerCreationParams(),
    )
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..loadRequest(
          LoadRequestParams(uri: Uri.parse('https://www.thelearningmate.com/')))
      ..setOnPlatformPermissionRequest((request) {
        request.grant();
      })
      ..addJavaScriptChannel(
        JavaScriptChannelParams(
          name: 'FlutterBridge',
          onMessageReceived: (message) {
            switch (message.message) {
              case 'requestMicrophonePermission':
                requestMicrophone();
                break;
              default:
                break;
            }
          },
        ),
      );
  }

Future<void> requestMicrophone() async {
    log("mic fun start");
    // 마이크 권한 요청 코드 작성
    PermissionStatus status = await Permission.microphone.request();

    //권한 요청 결과에 따라 front에 message를 보냄
    log(status.name);
    if (status == PermissionStatus.granted) {
      _controller.runJavaScript(
          'MicrophonePermissionBridge.receiveMessage(\'GRANTED\')');
    } else {
      _controller.runJavaScript(
          'MicrophonePermissionBridge.receiveMessage(\'DENIED\')');
    }
 }

프런트로부터 requestMicrophonePermission 이라는 message를 받게 된다면 Flutter에서 requestMicrophone이라는 함수를 실행하게 되고 그러면 내가 작성한 requestMicrophone이라는 함수가 실행된다. 위 함수가 실행된다면 마이크 권한 요청을 하고, 사용자가 권한 허용을 하면 GRANTED를, 거절을 한다면 DENIED를 프론트로 메시지를 보내주게 된다. -> 이후의 필요한 작업은 프런트에서 해주면 된다는 말이다!

마이크 권한 요청을 위해 프런트와 Flutter가 하는 통신 과정이랑 작성해야 하는 코드는 대략적으로 이 정도인 것 같다!

profile
초보 개발자의 우당탕탕 코딩일기

0개의 댓글