[원티드 프리온보딩 프론트엔드][2주차 2차 과제] - 메신저 구현하기

GY·2022년 2월 12일
0

원티드 프리온보딩

목록 보기
6/12
post-thumbnail

👉 깃허브링크 바로가기
👉 배포링크 바로가기


🎮  사용한 기술 스택

  • typescript
  • styled-component
  • redux-toolkit

🎮  구현 기능

유저 네임 입력메시지 입력답장삭제
  • 유저 네임 입력 기능
    • 접속 시 대화창에서 사용할 유저 네임 입력 모달창 표시
    • 입력한 유저네임으로 대화창에서 메시지 전송
  • 메시지 입력기능
    • 메시지 입력 시 전송버튼 즉시 활성화
    • 엔터키로 전송
    • 멀티라인으로 메시지 입력 가능
    • 개행시 input창 크기 증가 및 채팅창 크기 축소, 일정 height 도달시 scroll 추가
  • 대화창 메시지 표시 기능
    • 대화창 접속 시 메시지 시간 순으로 정렬
    • 메시지 전송 시 대화 목록은 항상 가장 아래로 스크롤
    • 내가 전송한 메시지 별도 표시: 이름 옆에 * 문자 출력
    • 보낸 날짜 및 시간 표시
  • 답장 기능
    • 답장 버튼 클릭 시 답장할 메시지의 사용자 이름, 메시지 내용을 입력창 상단에 표시
    • 답장의 답장의 경우 답장이 원본 메세지로 변경
  • 삭제 기능
    • 삭제 버튼 클릭시 삭제할 메시지와 함께 '메시지를 삭제 하시겠습니까?' 출력, 응답시 삭제
    • 삭제할 메시지 내용은 최대 10자 이후 ...처리

모달창 세팅

ReactDOM.Portal

이전에 처음으로 Portal을 사용해 모달창을 만들었었는데, 이번에는 조금 다르게 만들었다.
이전 프로젝트에서 모달창을 구현하면서 Portal을 사용한 코드는 다음 포스팅에 정리해두었다.
👉 포스팅 보러가기

기존 코드와 달라진 점이 있다면,

  • Recoil이 아닌 Redux-toolkit을 사용하고 모달창을 켜고 끄는 상태를 전역적으로 관리하지 않은 점
  • 타입스크립트로 작성했다는 점
  • html 파일에서 root 요소 옆에 새로운 요소를 직접 작성해주는 대신 동적으로 생성한다는 점

굳이 html 파일을 이동해 확인해보지 않아도, 해당 파일안에서 코드를 관리할 수 있어 유지 보수 및 가독성에서 더 좋을 것이라고 생각했기 때문이다.

//types.ts
export interface ModalPotalProps {
  children: React.ReactNode;
}
//ModalPortal
import { useEffect } from 'react';
import ReactDOM from 'react-dom';
import { ModalPotalProps } from './types.js';

const Modal = ({ children }: ModalPotalProps) => {
  const modalEl: HTMLDivElement = document.createElement('div');

  useEffect(() => {
    document.body.appendChild(modalEl);
    return () => {
      document.body.removeChild(modalEl);
    };
  }, []);

  return ReactDOM.createPortal(children, modalEl);
};

export default Modal;
import React from 'react';
import { IoMdClose } from 'react-icons/io';
import { ModalProps } from './types';
import * as Styled from './styled';

function Modal({ children, onClose }: ModalProps) {
  return (
    <Styled.Background onClick={onClose}>
      <Styled.ModalContainer>
        <Styled.CloseButton>
          {onClose && <IoMdClose onClick={onClose} />}
        </Styled.CloseButton>
        {children}
      </Styled.ModalContainer>
    </Styled.Background>
  );
}

export default Modal;

모달창 켜고 끄기

이전에는 Recoil을 사용해 isOpened와 같이 모달창을 켜고 끄는데 사용하는 상태를 전역으로 관리했다. 하지만 이게 좋은 방식일까? 라는 고민을 했었다.

전역 상태로 관리했던 이유

모달창이 사용되는 각 컴포넌트마다 ModalPortal과 Modal을 사용하는 것이 비효율적인 것 같았기 때문이다.
이번 프로젝트에서는 가장 처음과 삭제버튼을 누를 경우 2군데에서 사용되어서 분리했는데, 이전 프로젝트에서는 Modal 내부에서 컨텐츠 내용까지도 관리하고 싶었다.

하지만 그 모달이 결국 어디에서 사용되는지 찾기 힘들기 때문에... 이렇게 하는 것이 좋은 것 같다.

이전 프로젝트와 달라진 점

Styled Component 파일 별도 분리

스타일 컴포넌트와 일반 컴포넌트의 구분을 위해서였다.
한 파일안에 별도로 styled파일을 만들어 스타일 컴포넌트를 정의했다.

이 styled.tsx에서 정의한 스타일들을 export하고,

//styled.tsx
import styled from 'styled-components';

export const ChatBubbleContainer = styled.div`
  margin: 10px 0px 10px 0px;
  display: flex;
`;

팀 내에서 협의한 대로 S라는 이름으로 import해 사용했다.

//index.tsx
import * as S from './styled';

<S.YourChatBubbleBlock>
  <S.YourChatBubble>
    <ChatBubbleContent message={message} mine={mine} />
  </S.YourChatBubble>
  <S.ChatBubbleUtilBox>

S.가 붙은 컴포넌트는 스타일 컴포넌트이고, 그렇지 않은 ChatBubbleContent는 기능적인 일반 컴포넌트라는 것을 한 눈에 구분할 수 있다.

나름대로 스타일 컴포넌트를 사용하면서 가독성을 개선하기 위해 이번 과제부터 달리한 방법이었는데, 좋은 방법이었다고 생각한다.

아토믹 디자인 사용하지 않음

프로젝트 규모가 커질수록 확장성을 고려했을 때 하나의 컴포넌트는 하나의 역할을 하며 재사용이 가능하도록 잘게 쪼개는 것이 좋다고 생각한다. 그래서 이전 프로젝트에서는 아토믹 디자인으로 컴포넌트를 분할하였다.
그러나 이번 프로젝트는 규모 자체가 워낙 작았고, 팀원 4명이서 분담하기에도 어려워 주로 vsc liveshare로 동시에 작업하는 비중이 많았다. 그만큼 분할할 컴포넌트도 많지 않았고 프로젝트 진행 시점에서 효율적이지 않다는 판단이 들어 이번 과제에서는 사용하지 않기로 했다.


메시지 데이터 모델 정의

이번 과제에서는 별도의 Mock data 혹은 데이터 모델이 주어지지 않았다.
단 채팅창 접속 시 3명의 사람이 5건의 대화를 나눈 내역이 나타나야 한다는 조건이 있었기 때문에 해당 조건에 맞게 데이터 모델부터 정의해야 했다.
앞으로 작성할 코드에 영향을 많이 미치기 때문에 이 부분을 논의하는데 시간이 오래 걸렸다.

최종적으로 결정한 데이터 모델은 다음과 같았다.

  1. currentUser
  • 현재 채팅하고 있는 유저에 대한 정보를 갖는 객체이다. 처음 채팅창에 접속했을 때 입력한 유저네임이 저장되어 채팅 시 표시된다.
  • 프로필 이미지는 별도 설정하지 않을 시 기본 이미지로 설정된다는 전제를 설정했다. 크게 중요한 부분은 아니었기 때문에 간단하게 로컬에 저장한 기본이미지를 넣었다.

  1. currentMessage
  • currentUser 즉 내가 보낸 메시지에 대한 정보를 담는다.

  1. messages
  • 전체 대화내역에 대한 정보를 저장한 객체이다. 메시지를 보낸 날짜와 시간이 표시되어야 했기 때문에 date항목을 추가했다.
  • user는 메시지를 보낸사람, content는 메시지 내용이 들어간다. reply는 이 메시지가 어떤 메시지에 대한 답장인지에 대한 정보가 담겨있다. 위 데이터에서 peter가 보낸 메시지에 paul이 답장한 메시지에 대한 정보가 담겨져있는 것이다.

살펴보기

profile
Why?에서 시작해 How를 찾는 과정을 좋아합니다. 그 고민과 성장의 과정을 꾸준히 기록하고자 합니다.

0개의 댓글