본문 바로가기

카테고리 없음

WelKo

기존에는 채팅에 시각이 나오기만 하였으나

메시지를 그룹화 하는 함수를 사용하여 디자인 개선

  const groupedMessages = messages.reduce(
    (groups, msg) => {
      const date = format(new Date(msg.created_at), 'MMMM dd, yyyy');
      if (!groups[date]) {
        groups[date] = [];
      }
      groups[date].push(msg);
      return groups;
    },
    {} as Record<string, Message[]>
  );

1. reduce 함수:

  • reduce는 배열의 각 요소에 대해 반복적으로 호출되어 하나의 최종 결과를 생성하는 고차 함수입니다. 여기서 messages 배열을 순회하며 각 메시지를 날짜별로 그룹화하고, 그 결과를 groupedMessages 객체에 저장합니다.

2. groups 매개변수:

  • groups는 누적된 결과를 저장하는 객체입니다. 이 객체는 날짜를 키로, 해당 날짜에 보낸 메시지들의 배열을 값으로 가집니다.

3. msg 매개변수:

  • msg는 현재 reduce가 순회 중인 메시지 객체입니다. 이 메시지의 created_at 필드를 이용해 메시지가 전송된 날짜를 추출합니다.

4. date 변수:

  • date는 msg.created_at을 포맷팅하여 MMMM dd, yyyy 형식의 문자열로 변환한 값입니다. 예를 들어, 메시지가 2024년 8월 10일에 전송되었다면 date 변수는 'August 10, 2024'가 됩니다.

5. if (!groups[date]) { ... }:

  • 이 조건문은 현재 날짜(date)가 groups 객체에 아직 키로 존재하지 않는 경우, 해당 키를 초기화하고 빈 배열을 할당합니다. 즉, 이 날짜에 보낸 메시지가 처음 발견된 경우에 해당합니다.

6. groups[date].push(msg):

  • 현재 메시지(msg)를 해당 날짜 키에 할당된 배열에 추가합니다.

7. 최종 반환값:

  • 최종적으로 reduce는 모든 메시지를 그룹화한 groups 객체를 반환합니다. 이 객체는 날짜별로 메시지를 저장하는 구조입니다.

  return (
    <div className="flex h-screen flex-col text-[14px]">
      <div className="mb-[16px] overflow-y-auto border-b">
        {Object.entries(groupedMessages).map(([date, msgs]) => (
          <div key={date}>
            <p className="text-center text-[10px] text-grayscale-500">{date}</p>
            {msgs.map((msg) => (
              <div
                key={msg.id}
                className={`mb-10 flex flex-col ${msg.sender_id === senderId ? 'items-end' : 'items-start'}`}
              >
                {msg.sender_id !== senderId && msg.sender && (
                  <div className="flex items-center">
                    <Image
                      className="mr-2 rounded-full"
                      src={msg.sender.avatar}
                      alt="avatar"
                      width={44}
                      height={44}
                      style={{ width: '44px', height: '44px' }}
                    />
                    <p className="text-[13px] font-semibold">{msg.sender.name}</p>
                  </div>
                )}
                <div className="flex items-end">
                  <p
                    className={`${
                      msg.sender_id === senderId ? 'mr-[8px]' : 'order-1 ml-[8px]'
                    } mt-[4px] text-[10px] text-grayscale-500`}
                  >
                    {format(new Date(msg.created_at), 'HH:mm')}
                  </p>
                  <div
                    className={`mt-[7px] max-w-[240px] break-all px-[8px] py-[12px] ${
                      msg.sender_id === senderId
                        ? 'rounded-br-0 rounded-bl-[16px] rounded-tl-[16px] rounded-tr-[16px] bg-primary-50'
                        : 'rounded-bl-0 rounded-br-[16px] rounded-tl-[16px] rounded-tr-[16px] bg-grayscale-50'
                    }`}
                  >
                    <p className="text-[14px]">{msg.content}</p>
                  </div>
                </div>
              </div>
            ))}
          </div>
        ))}
      </div>
      <div className="flex items-center">
        <input
          className="h-[48px] flex-1 rounded-[16px] border bg-grayscale-50 p-[16px] text-[16px]"
          type="text"
          placeholder="Placeholder text"
          value={newMessage}
          onChange={(e) => setNewMessage(e.target.value)}
        />
        <button
          className="ml-[12px] flex h-[48px] w-[48px] items-center justify-center rounded-full bg-primary-300"
          onClick={handleSend}
        >
          <Image
            src="/icons/tabler-icon-send.svg"
            alt="Send"
            width={24}
            height={24}
            style={{ width: '24px', height: '24px' }}
          />
        </button>
      </div>
    </div>
  );

적용된 방법:

  1. Object.entries(groupedMessages):
    • groupedMessages 객체의 키와 값을 배열 형태로 변환합니다. 각 키는 날짜(date), 각 값은 해당 날짜에 전송된 메시지 배열(msgs)입니다.
  2. date를 기반으로 렌더링:
    • groupedMessages의 각 날짜 그룹(date)을 순회하며, 해당 날짜를 <p> 태그를 통해 화면에 표시합니다.
  3. msgs를 기반으로 메시지 렌더링:
    • 해당 날짜에 속하는 각 메시지를 순회하며 화면에 표시합니다. 이 과정에서 메시지의 내용, 전송자 프로필 이미지, 그리고 전송 시간을 표시합니다.
  4. 메시지와 날짜의 구조화:
    • 이 구조는 메시지들이 날짜별로 묶여 표시되도록 합니다. 예를 들어, 2024년 8월 10일에 전송된 모든 메시지는 하나의 그룹으로 묶여 화면에 "August 10, 2024"라는 날짜와 함께 표시됩니다.

 

날짜 포맷팅에서 자주 사용되는 형식:

  1. MMMM:
    • 의미: 월의 풀 네임(예: January, February, March 등).
    • 예시: "August".
  2. MMM:
    • 의미: 월의 축약형(예: Jan, Feb, Mar 등).
    • 예시: "Aug".
  3. dd:
    • 의미: 날짜를 2자리로 표시(예: 01, 02, ... 31).
    • 예시: "10".
  4. d:
    • 의미: 날짜를 1자리 또는 2자리로 표시(예: 1, 2, ... 31).
    • 예시: "10".
  5. yyyy:
    • 의미: 연도를 4자리로 표시(예: 2024).
    • 예시: "2024".
  6. yy:
    • 의미: 연도의 마지막 두 자리를 표시(예: 24).
    • 예시: "24".
  7. EEEE:
    • 의미: 요일의 풀 네임(예: Monday, Tuesday, Wednesday 등).
    • 예시: "Saturday".
  8. EEE:
    • 의미: 요일의 축약형(예: Mon, Tue, Wed 등).
    • 예시: "Sat".
  9. HH:
    • 의미: 24시간 형식으로 시간 표시(00~23).
    • 예시: "14".
  10. mm:
    • 의미: 분을 2자리로 표시(00~59).
    • 예시: "45".
  11. ss:
    • 의미: 초를 2자리로 표시(00~59).
    • 예시: "30".

 

 

페이지 + 컴포넌트에서 스크롤이 각자 생기는 이중 스크롤 문제

overflow-hidden으로 해결이 안되었어서 flex-grow도 활용하였었으나 하단바 위치 및 크기를 잘 조정하여서 그런지

일시적인 오류였는지 overflow-hidden만으로 해결되었습니다

조정한 하단바 코드

      <div className="sticky bottom-0 flex items-center border-t bg-white pb-[16px] pt-[16px]">
        <input
          className="h-[48px] flex-1 rounded-[16px] border bg-grayscale-50 p-[16px] text-[16px]"
          type="text"
          placeholder="Placeholder text"
          value={newMessage}
          onChange={(e) => setNewMessage(e.target.value)}
        />
        <button
          className="ml-[12px] flex h-[48px] w-[48px] items-center justify-center rounded-full bg-primary-300"
          onClick={handleSend}
        >
          <Image
            src="/icons/tabler-icon-send.svg"
            alt="Send"
            width={24}
            height={24}
            style={{ width: '24px', height: '24px' }}
          />
        </button>
      </div>

overflow-hidden을 적용한 페이지 코드

    <div className="flex h-screen flex-col overflow-hidden">
      <div className="mx-[20px] mt-[56px]">
        <div className="relative my-[20px] flex items-center justify-between">
          <button className="rounded-[24px] bg-grayscale-50" onClick={handleBack}>
            <Image src="/icons/tabler-icon-chevron-left.svg" alt="Go Back" width={32} height={32} />
          </button>
          <p className="absolute left-1/2 -translate-x-1/2 transform text-[18px] font-semibold">Message</p>
          <div className="w-8"></div>
        </div>
        <div className="flex">
          <Image
            className="rounded-[8px]"
            src={postImage ?? '/icons/upload.png'}
            alt={postTitle ?? 'Default title'}
            width={44}
            height={44}
            style={{ width: '44px', height: '44px' }}
          />
          <div className="ml-[8px] max-w-[360px]">
            <p className="line-clamp-1 text-[14px] font-bold">{postTitle}</p>
          </div>
        </div>
        <Chat postId={postId || ''} senderId={senderId} receiverId={receiverId} />
      </div>
    </div>

Chat.tsx 코드

  return (
    <div className="flex h-screen flex-col">
      <div className="overflow-y-auto pb-[180px]">
        {Object.entries(groupedMessages).map(([date, msgs]) => (
          <div key={date}>
            <p className="mt-[8px] text-center text-[10px] text-grayscale-500">{date}</p>
            {msgs.map((msg) => (
              <div
                key={msg.id}
                className={`mb-[16px] mt-[24px] flex flex-col ${msg.sender_id === senderId ? 'items-end' : 'items-start'}`}
              >
                {msg.sender_id !== senderId && msg.sender && (
                  <div className="flex items-center">
                    <Image
                      className="rounded-full"
                      src={msg.sender.avatar}
                      alt="avatar"
                      width={44}
                      height={44}
                      style={{ width: '44px', height: '44px' }}
                    />
                    <p className="ml-[8px] text-[13px] font-semibold">{msg.sender.name}</p>
                  </div>
                )}
                <div className="flex items-end">
                  <p
                    className={`${
                      msg.sender_id === senderId ? 'mr-[8px]' : 'order-1 ml-[8px]'
                    } text-[10px] text-grayscale-500`}
                  >
                    {format(new Date(msg.created_at), 'HH:mm')}
                  </p>
                  <div
                    className={`max-w-[240px] break-all px-[8px] py-[12px] ${
                      msg.sender_id === senderId
                        ? 'rounded-br-0 rounded-bl-[16px] rounded-tl-[16px] rounded-tr-[16px] bg-primary-50'
                        : 'rounded-bl-0 ml-[56px] rounded-br-[16px] rounded-tl-[16px] rounded-tr-[16px] bg-grayscale-50'
                    }`}
                  >
                    <p className="text-[14px]">{msg.content}</p>
                  </div>
                </div>
              </div>
            ))}
          </div>
        ))}
      </div>
      <div className="sticky bottom-0 flex items-center border-t bg-white pb-[16px] pt-[16px]">
        <input
          className="h-[48px] flex-1 rounded-[16px] border bg-grayscale-50 p-[16px] text-[16px]"
          type="text"
          placeholder="Placeholder text"
          value={newMessage}
          onChange={(e) => setNewMessage(e.target.value)}
        />
        <button
          className="ml-[12px] flex h-[48px] w-[48px] items-center justify-center rounded-full bg-primary-300"
          onClick={handleSend}
        >
          <Image
            src="/icons/tabler-icon-send.svg"
            alt="Send"
            width={24}
            height={24}
            style={{ width: '24px', height: '24px' }}
          />
        </button>
      </div>
    </div>
  );