최종 팀 프로젝트 - 기능 연결

Rock Kyun·2024년 1월 15일
1
post-thumbnail

오늘 했던 것

  • 로그인 - 게시물 상세 - 채팅 연결

1. supabase 카톡 소셜 로그인 연결하기

1. 카카오 디벨로퍼 사이트에 가입을 한다.

2. 내 어플리케이션을 등록한다.

  • 아래 사진의 애플리케이션 추가하기를 누른 후

  • 입력란을 채워준다. (나는 앱 이름과 사업자명을 같게 했다.)

3. 카카오 로그인 활성화

  • 내 애플리케이션 - 제품설정 - 카카오 로그인 탭에 들어가
    활성화 설정을 ON으로 설정한다.

4. 동의 항목 설정

  • 다음은 제품설정의 동의 항목으로 가서
  • 아래 항목들에 설정을 하자

4-1. 동의항목 설정 이메일 권한 얻기

  • 프로젝트 사이트에서는 이메일을 필수로 받아야 하는데
    처음 동의항목 설정에 들어가면 카카오 계정(이메일)이 권한 없음으로 되어있는데
    이것의 권한을 얻기 위해서는 비즈 앱 신청을 해야한다.
  • 위의 개인정보 동의항목 심사 신청 버튼을 누른 뒤
    비즈 앱으로 전환하도록 하자!

5. 플랫폼 설정하기

  • 거의 다 왔다. 플랫폼 설정에서
    아직 배포하지 않았다면 npm start 시 뜨는 주소! (http://localhost:3000)
    배포했다면 배포한 사이트의 주소를 적자!

6. 앱 키 입력하기

  • 아래의 키를 보면 다양하게 있는데
  • supabase의 카카오톡 Provider에는 REST API 키, Javascript 키를 사용한다.

7. supabase Provider 설정

  • 아래와 같이 내 프로젝트 auth탭의 Providers에 들어가서
    카카오톡을 Enabled로 해놓고
    REST API Key에는 6번의 REST API 키를
    Cilent Secret Code 에는 6번의 Javascript 키(웹 기준)를 입력하면 끝..!

2. 상세 페이지와 채팅 연결하기

큰 흐름

  1. 상세페이지에 접속하여 채팅하기 버튼 클릭 시
    채팅방 테이블에 현재 로그인 유저와 게시물 작성 유저의 uid가 담긴
    row를 하나 insert 한다.

  2. 채팅방 테이블에 채팅방이 생성되면
    첫 메세지로 내가 클릭한 상품의 { 게시물의 제목, 가격, 관심 있어요! }
    라는 세 개의 메세지를 상대방에게 전송한다.

코드

1. 상세 페이지에서 채팅방 생성

  • 채팅하기 버튼에는 게시물 작성자의 uid가 심어져 있다.
  • 현재 로그인 유저는 mount 시 state에 저장된다.
  • 게시물 데이터는 mount 시 state에 저장된다
// 채팅하기 버튼 클릭 시
const makeChatRoom = async (e: MouseEvent) => {
  // 게시물 작성자의 uid
  const targetId = e.currentTarget.id;

  // user 테이블에서 채팅 상대 정보 가져오기
  // user 테이블의 uid 컬럼 값 === 게시물 작성자의 uid인 것을 가져온다
  const { data: targetUser, error: noUser } = await supabase
  .from('user')
  .select('*')
  .eq('uid', targetId);

  // 게시물 작성자의 정보가 존재하면
  // state에 저장
  if (targetUser && targetUser.length > 0) {
    setTarget(targetUser[0]);
  }

  // error 처리 (추후 UI적으로 표현 예정)
  if (noUser) console.log('user is not exists', noUser);
};

1-1. 유저들의 정보를 채팅방 테이블에 담아 insert 하기

  • 미리 준비되어야 하는 정보들과 그 흐름을 설명하기 위해
    최종 목표에 도달하기까지의 함수들을 설명하겠습니당.
  // 생성된 채팅방 row에 로그인 유저와 채팅 상대 정보 insert 하는 함수
  const insertUserIntoChatRoom = async (
    curUser: CustomUser,
    target: CustomUser
  ) => {
    // participants는 채팅방 테이블의 row가 갖는 채팅 참여자 필드이다.
    const participants = [
      {
        participants: [
          { user_id: target.uid, user_name: target.username },
          {
            user_id: curUser.uid,
            user2_name: curUser.username
          }
        ]
      }
    ];
    
    // 채팅방 테이블의 participants 필드에 참여자 정보 insert
    const { data: chatRoom, error } = await supabase
      .from('chat_room')
      .insert(participants);

    // 유저가 속한 채팅방을 반환하는 함수
    // 이것은 유저가 속한 채팅방의 id를 얻기 위해 사용합니다
    await findRoom();

    if (error) console.log('생성 실패');
  };

1-2. 각 유저에게 본인이 속한 채팅방 id 업데이트

// 유저가 속한 채팅방을 반환해주는 함수
const findRoom = async () => {
  const { data: foundRoom, error } = await supabase
  .from('chat_room')
  .select('*');

  if (error) {
    console.error('일치하는 방 없음', error);
    return;
  }

  // 채팅 테이블의 row를 읽어와서 (curUser는 현재 로그인 된 유저 정보 state)
  if (foundRoom && curUser) {
    // 유저가 속한 채팅방을 filter 후
    const filtered = foundRoom.filter((room: any) => {
      return room.participants.some(
        (participant: any) => participant.user_id === curUser.uid
      );
    });

    // 유저가 속한 채팅방 반환
    return filtered;
  }
};

1-3. 유저 테이블에도 소속 된 채팅방 id insert

// 유저 테이블의 chat_rooms 필드값에 방을 업뎃해주자
const insertRoomintoUser = async (userInfo: CustomUser[]) => {
  // 헬퍼함수로 유저가 속한 채팅 테이블 할당
  const room = (await findRoom()) as any;

  // 만약 유저가 속한 채팅방 데이터와 현재 로그인 한 유저 정보가 있다면
  if (room && userInfo) {
    // 채팅방 id 추출
    const room_id = room[0].id;
    
    // 인자로 받은 user 정보의 uid와 같은 필드의 chat_rooms에
    // 채팅방 id 추가!
    const { data, error } = await supabase
    .from('user')
    .update({ chat_rooms: [room_id] })
    .eq('uid', userInfo[0].uid)
    .select();

    if (error) {
      console.error('채팅방 추가 실패', error.message);
      return false;
    }
  }
};

1-4. 최종적으로 유저 각자에게도 채팅방 id 추가

// 이제 각 유저에게 채팅방을 추가하자
// 현재 로그인 유저, 채팅 상대 유저 정보를 인자로 보내어
// 유저의 chat_room_id에 채팅방 id를 update 해준다 (insertRoomIntoUser 함수)
const findUser = async (User: CustomUser) => {
  const { data: userInfo, error } = await supabase
  .from('user')
  .select('*')
  .eq('uid', User?.uid);

      if (userInfo) {
    await insertRoomIntoUser(userInfo as any);
  }
};

2. 채팅방 생성 / 유저와 채팅방 연결 후 시작 메세지 전송

// 게시물 관련 데이터를 첫 메세지로 보낸다.
const sendFirstMessage = async () => {
  if (product && curUser) {
    // 유저가 속한 채팅방을 반환하여 room에 할당
    const room = await findRoom();
    
    if (room) {
      // 채팅방 생성 시 자동으로 전송되는 메세지
      const InitMessage = [
        {
          sender_id: curUser.uid,
          content: `제목: ${product[0].title}`,
          chat_room_id: room[0]?.id
        },
          {
          sender_id: curUser.uid,
          content: `${product[0].price}`,
		  chat_room_id: room[0]?.id
		},
		  {
        sender_id: curUser.uid,
        content: '이 상품에 관심 있어요!',
        chat_room_id: room[0]?.id
        }
	  ];

    // 메세지 테이블에 시작 메세지 insert
    const { data, error } = await supabase
    .from('chat_messages')
    .insert(InitMessage);

    if (error) console.log('메세지 전송 실패..', error);
    }
  } else console.log('데이터 없음');
};

결과물

느낀점

  • DB 데이터를 다루는 것에 있어서 뭔가
    엄청나게 많은 함수를 선언하고 사용하는데 이게 맞나..? 싶다🥺
    좀 더 효율적으로 바꿀 수 있겠다는 생각이 든다.

  • typescript에서 any를 많이 사용했는데
    type은 어떻게 지정해야 되는 것인지 모를 때가 많다..⭐
    리팩토링 단계에서는 명시하는 것으로....!!!

0개의 댓글