juni
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>
);
적용된 방법:
- Object.entries(groupedMessages):
- groupedMessages 객체의 키와 값을 배열 형태로 변환합니다. 각 키는 날짜(date), 각 값은 해당 날짜에 전송된 메시지 배열(msgs)입니다.
- date를 기반으로 렌더링:
- groupedMessages의 각 날짜 그룹(date)을 순회하며, 해당 날짜를 <p> 태그를 통해 화면에 표시합니다.
- msgs를 기반으로 메시지 렌더링:
- 해당 날짜에 속하는 각 메시지를 순회하며 화면에 표시합니다. 이 과정에서 메시지의 내용, 전송자 프로필 이미지, 그리고 전송 시간을 표시합니다.
- 메시지와 날짜의 구조화:
- 이 구조는 메시지들이 날짜별로 묶여 표시되도록 합니다. 예를 들어, 2024년 8월 10일에 전송된 모든 메시지는 하나의 그룹으로 묶여 화면에 "August 10, 2024"라는 날짜와 함께 표시됩니다.
날짜 포맷팅에서 자주 사용되는 형식:
- MMMM:
- 의미: 월의 풀 네임(예: January, February, March 등).
- 예시: "August".
- MMM:
- 의미: 월의 축약형(예: Jan, Feb, Mar 등).
- 예시: "Aug".
- dd:
- 의미: 날짜를 2자리로 표시(예: 01, 02, ... 31).
- 예시: "10".
- d:
- 의미: 날짜를 1자리 또는 2자리로 표시(예: 1, 2, ... 31).
- 예시: "10".
- yyyy:
- 의미: 연도를 4자리로 표시(예: 2024).
- 예시: "2024".
- yy:
- 의미: 연도의 마지막 두 자리를 표시(예: 24).
- 예시: "24".
- EEEE:
- 의미: 요일의 풀 네임(예: Monday, Tuesday, Wednesday 등).
- 예시: "Saturday".
- EEE:
- 의미: 요일의 축약형(예: Mon, Tue, Wed 등).
- 예시: "Sat".
- HH:
- 의미: 24시간 형식으로 시간 표시(00~23).
- 예시: "14".
- mm:
- 의미: 분을 2자리로 표시(00~59).
- 예시: "45".
- 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>
);
'프로젝트 > WelKo' 카테고리의 다른 글
WelKo ( 앱 디자인 ) (0) | 2024.08.13 |
---|---|
WelKo_정렬 및 패딩 개선 (0) | 2024.08.12 |
WelKo_채팅 UI 개선 (0) | 2024.08.10 |
WelKo_예약 내역 UI 개선 (0) | 2024.08.10 |
WelKo_마이 페이지 UI 및 Server 코드 개선 (0) | 2024.08.09 |