Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NDD-237] Interview 페이지의 useMedia hooks 개발 (2h/2h) #86

Merged
merged 5 commits into from
Nov 22, 2023

Conversation

adultlee
Copy link
Collaborator

@adultlee adultlee commented Nov 22, 2023

NDD-237 Powered by Pull Request Badge

Why

Interview 페이지에서의 Media 로직을 모두 정리해서 가독성이 좋은 hooks 상태로 변경하였습니다.

How

1. media util 함수, media에서 로직상 어려움을 없앨 수 있도록 util 함수로 분리

2. useMedia hooks 개발

import { closeMedia, getMedia, getSupportedMimeTypes } from '@/utils/media'; // util 에서 필요한 media를 모두 import 받습니다.
import { useState, useEffect, useCallback, useRef } from 'react'; 

const useMedia = () => {
  const [media, setMedia] = useState<MediaStream | null>(null); // media 정보를 담습니다. 이 정보를 바탕으로 다양한 ref에 연결해 사용이 가능합니다. 
  const [selectedMimeType, setSelectedMimeType] = useState(''); // MimeType을 설정합니다. media 연결직후 실행시켜야만 합니다. 
  const [connectStatus, setIsConnectedStatus] = useState<
    'connect' | 'fail' | 'pending'
  >('pending'); // 가장 고민한 부분이었습니다. 처음에는 boolean 값을 사용했었으나 3가지로 추가했습니다. 아래에서 후술합니다. 
  const videoRef = useRef<HTMLVideoElement>(null); // 반환할 ref입니다. 여기선 stream을 바로 연결할 수 있습니다.

  const connectMedia = useCallback(async () => {
    try {
      const media = await getMedia();

      setMedia(media);
      if (media) setIsConnectedStatus('connect');
      else setIsConnectedStatus('fail');
      if (videoRef.current) videoRef.current.srcObject = media;
    } catch (e) {
      console.log(`현재 마이크와 카메라가 연결되지 않았습니다`);
    }
  }, []);

  useEffect(() => {
    if (!media) {
      void connectMedia();
    }
    const mimeTypes = getSupportedMimeTypes();
    if (mimeTypes.length > 0) setSelectedMimeType(mimeTypes[0]);

    return () => {
      closeMedia(media);
    };
  }, [media, connectMedia]);

  return { media, videoRef, connectStatus, selectedMimeType };
};

export default useMedia;

주요 기능

  1. 기존의 media 연결 로직의 기능을 모두 사용가능
  2. 컴포넌트가 언마운트 되어 페이지 이동이 필요할 때, media 제거

반환값

1. media : setting페이지에서는 활용되지 않으며, recording 로직이 필요할때 사용됩니다.

2. videoRef: 반환 받은 직후, video 태그의 ref로 연결받습니다.

3. �connectStatus: media여부로 판단하려고 고민하다가, 아무래도 보기에 명확한 3가지 타입으로 받습니다.

connectStatus는 3가지 상태를 가집니다. 
- pending: media가 연결가능한지 가늠을 하는 준비 단계입니다. 
해당 상태를 이용하면 spinner나 Loading등등을 사용할것으로 기대합니다.
- connect: media가 연결된 상태로, 해당 상태를 이용해서 setting 페이지에서 recoil로 상태가 저장될 수 있으리라 기대할 수 있습니다.
- fail: 말 그대로 media가 연결되지 못한 상태로, 해당 상태의 경우 InterviewPage에서 라우터 처리를 수행합니다. 

해당 과정들은  아래의 코드에서 작성되었습니다.
image

결국 서비스 내부에선 media가 연결가능한 상태인지에 대해서, 비동기로 확인해야만 합니다.

그에 따라 아직 비동기의 결과 (Media의 여부)에 따라 다시금 상태를 정의하기 위해서 3가지 상태를 사용하게 되었습니다.

4. selectedMimeType: recording 혹은 데이터 업로드 과정에서 활용됩니다.

Result

useMedia를 사용후 카메라 연결을 하지 않은 경우

2023-11-22.3.20.04.mov

useMedia를 사용후 카메라 연결을 한 경우

2023-11-22.3.20.37.mov

해당 hook 은 Interview 페이지뿐 아닌, setting 페이지에서도 활용 가능하 수 있도록 개발되었습니다.
- 기능
1. useMedia 호출과 동시에 media에 접근해서 유저의 카메라와 마이크에 접근합니다. (getMedia함수를 통해 접근)
2. 접근한 결과 이상이 없다면 media 와 isConnected를 반환하여, videoRef에 연결합니다.

반환값
1. media : setting페이지에서는 활용되지 않으며, recording 로직이 필요할때 사용됩니다.
2.  videoRef: 반환 받은 직후,  video 태그의 ref로 연결받습니다.
3. isConnected: media여부로 판단하려고 고민하다가, 아무래도 보기에 명확한 boolean 값으로 받으려고 노력했습니다.
4. selectedMimeType: recording 혹은 데이터 업로드 과정에서 활용됩니다.
@adultlee adultlee changed the title [NDD-237] Interview 페이지의 useMedia hooks 개발 [NDD-237] Interview 페이지의 useMedia hooks 개발 (2h/2h) Nov 22, 2023
Copy link

cloudflare-workers-and-pages bot commented Nov 22, 2023

Deploying with  Cloudflare Pages  Cloudflare Pages

Latest commit: 548a6ec
Status: ✅  Deploy successful!
Preview URL: https://1b8169b7.gomterview.pages.dev
Branch Preview URL: https://feature-ndd-237.gomterview.pages.dev

View logs

Copy link
Collaborator

@Yoon-Hae-Min Yoon-Hae-Min left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Comment on lines +1 to +5
export const closeMedia = (media: MediaStream | null) => {
if (media) {
media.getTracks().forEach((track) => track.stop());
}
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[p-3] 요거는 너무 useMedia hook내부에 있는 mediaStream을 의존하고 있어서 util말고 hook쪽의 함수로 설정해도 될것 같은데 어떤가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 이건 다른 의미도 있습니다!
예를들어 제가 화상으로 말씀드렸다 싶이, 제가 저 함수를 호출하는 시점은 useEffect로 컴포넌트가 unmount 될때 호출되고 있어요!
그런데 navigate로 인해서 라우터 처리를 진행할때는 unmount 로직이 실행되지 않는 문제가 발생했습니다.

공식문서를 찾아봤을때는 navigate를 통해서 라우터 처리를 진행할때, react 생명주기에 간섭하는 경우가 발생해 원하는 동작을 못하는 경우가 왕왕 발생한다구 하더라구요!

그래서 navigate를 사용하는 위치에서 해당 함수를 따로 import를 받아서 처리하면 어떨까 싶었습니다!

내부에 크게 의존하고 있지는 않습니다 사실 인자로 받은 media를 종료시키는 기능만을 담고 있으니까요!

@adultlee adultlee added FE 프론트엔드 코드 변경사항 feature 새로운 기능이 추가 된 경우 labels Nov 22, 2023
@adultlee adultlee self-assigned this Nov 22, 2023
Copy link
Collaborator

@milk717 milk717 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

훅으로 분리된 코드가 너무 아름답군요~~!!
고생하셨습니다!!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[p-5] 후후 대신 해결해주셔서 감사합니다~ 앞으로 머지할 때 주의할게요

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[p-5] before after가 눈물나게 아름답네요

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제 생각도 그렇습니다😂

const handleStartRecording = () => {
try {
mediaRecorderRef.current = new MediaRecorder(stream as MediaStream, {
mediaRecorderRef.current = new MediaRecorder(media as MediaStream, {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[p-3] 요 부분은 media가 MediaStream으로 추론되지 않아서라기 보단 media가 null 일수도 있어서 발생하는 ts 경고이기 때문에 아래와 같은 형식으로 처리하는 것은 어떨까요?

  const handleStartRecording = () => {
    try {
      if (!media) {
        return;
      }

      mediaRecorderRef.current = new MediaRecorder(media, {
        mimeType: selectedMimeType,
      });
      mediaRecorderRef.current.ondataavailable = (event) => {
        if (event.data && event.data.size > 0) {
          setRecordedBlobs([event.data]);
        }
      };
      mediaRecorderRef.current.start();
      setIsRecording(true);
      // RecordStartingTime 을 초기화합니다.
      // pre-signed url을 받습니다.
    } catch (e) {
      console.log(`MediaRecorder error`);
    }
  };

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 네네 이건 지금 record hook 을빼면서 진행하려구 아껴(?)두고 있습니다

@adultlee adultlee merged commit b03136e into dev Nov 22, 2023
1 check passed
@delete-merged-branch delete-merged-branch bot deleted the feature/NDD-237 branch November 22, 2023 07:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
FE 프론트엔드 코드 변경사항 feature 새로운 기능이 추가 된 경우
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants