React를 활용한 메신저 프로젝트

장영준·2022년 12월 28일
0

Frontend-Project

목록 보기
2/2

신촌 연합 동아리 CEOS의 프론트엔드 스터디 과제로 react js를 활용한 메신저 프로젝트를 진행했다.
(10월에 마무리했었는데, 이제서야 관련 글을 쓴다,,,)

배포링크
GITHUB

I. 폴더구조

폴더구조는 다음과 같다.

src
|-components
	|-chattingListPage
	|-chattingPage
	|-homePage
	|-profilePage
	|-settingPage
	|-elements (공유 컴포넌트)
|-data
	|-chatData.json
	|-userData.json
|-image
	|-emoji (icon 이라고 이름 지었어야 했는데..)
		|-CameraIcon.tsx
		|-...
|-state
|-styles
|-App

II. Home, Profile, ChattingList, Setting 페이지

<Route path="/" element={<HomePage />}>
  <Route path="" element={<ProfilePage />} />
  <Route path="chattingList" element={<ChattingListPage />} />
  <Route path="setting" element={<SettingPage />} />
</Route>
  • 홈페이지를 기본 구조로, react-router의 Outlet 기능을 사용하여 변화하는 부분만 바꾸었다.
  • ProfilePage와 ChattingListPage에서는 거의 같은 구조로 돼어있어 유저의 Bar 부분을 컴포넌트로 사용. (profilePage에서의 상태메시지도 chatListPage에서 마지막 보여지는 메시지로 바꾸면 되기에..)
  • chatList에서 마지막 메시지가 사진이면 ‘사진을 보냈습니다’로 바꾸었다.

III. Chatting 페이지

가장 주된 기능들만 정리했다.

1. Recoil을 통한 상태관리, 작성 후 상태 업로드

const updateChatList = () => {
    if (typeof content === 'string' && content.trim().length === 0) {
      window.alert('문자를 입력해주세요.');
    } else {
      let time = `${new Date().getHours()} : ${new Date().getMinutes()}`;

      setChatList(() => {
        let tempList = chatList[targetUserId];
        tempList = [
          ...tempList,
          {
            username: username,
            messageTime: time,
            content: content,
          },
        ];

        return { ...chatList, [targetUserId]: tempList };
      });
    }

    setContent('');
    setIsButtonActive(false);
  };
  1. 여러개의 chatListData중 채팅 상대의 userId에 맞는 데이터를 골라 tempList에 저장
  2. tempList에 username, messageTime, content 정보를 담은 새로운 챗 업로드
  3. 전체 chatList에 채팅 상대와 한 챗을 배치 (replace하는 느낌)

    마지막 return {…chatList, [targetUserId]: tempList} 에서 어떻게 하면 chatData의 key 부분을 변수로 할 수 있을지에 관해 많이 고민해
    return{…chatList, ${targetUserId}: tempList} 등 많은 시도를 해보았는데, 객체에서는 저런 방식으로 변수를 key값에 할당한다는 사실을 알게 되었다.

2. base64로 인코딩을 통한 사진 업로드

  1. 아이콘 클릭 시 파일 삭제 기능
<div>
	<input
    type="file"
    id="img-upload"
    style={{ display: 'none' }}
    onChange={(e) => encodeFileToBase64(e.target.files)}
    accept="image/x-png,image/gif,image/jpeg"
  />
  <label htmlFor="img-upload">
	  <CameraIcon className="camera-icon" />
  </label>
</div>
  • <input type=“file” /> 으로 설정하면 다음과 같이 ‘파일 선택’란이 나온다.
    Untitled
  • 속성
    • display:none 속성으로 저 아이콘을 없앨 수 있다.
    • onChange 속성으로 버튼 클릭 시 함수를 실행할 수 있다.
    • accept 속성으로 어떤 종류의 파일만 업로드 허용할 것인지 제한할 수 있다.
      hi
  • label
    • label 태그 를 통해 아이콘을 없앤 것을 대신에 무언가를 넣을 수 있다.
  1. 사진 파일 업로드
  • encodeToBase64 함수
    const encodeFileToBase64 = (files: any) => {
    const file = files[0];

    let fileReader = new FileReader();
    const time = `${new Date().getHours()} : ${new Date().getMinutes()}`;

    fileReader.onload = () => {
      let img = new Image();
      img.onload = () => {
        setImageHeight((img.height / img.width) * 200);
      };
      if (typeof fileReader.result === 'string') {
        img.src = fileReader.result;
      }

      setChatList(() => {
        let tempList = chatList[targetUserId];
        tempList = [
          ...tempList,
          {
            username: username,
            messageTime: time,
            content: fileReader.result,
          },
        ];

        return { ...chatList, [targetUserId]: tempList };
      });
    };
    fileReader.readAsDataURL(file);
  };

파일을 선택해서 ‘열기’ 버튼을 눌러 그것을 console.log(file.result) 해보면 엄청 긴 문자열이 나온다.

이건 코드의 맨 마지막 줄에 있는 fileReader.readAsDataURL(file); 때문인데, readAsDataURL 함수를 쓰면 바이너리 파일을 base64로 인코딩된 문자열을 반환한다.
이걸 일반 텍스트와 같이 content에 저장하고, 불러올 때는 <img src=`문자열` /> 하면 사진이 나온다.

이렇게 react-messanger 프로젝트를 진행하며 typescript와 recoil을 이용한 객체 상태 관리를 경험해보고, base64로 사진을 인코딩하여 사진을 첨부하는 기능도 사용해보았다.

profile
배움의 개발자

0개의 댓글