Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor/#215 댓글 시간을 시간,일,달 등 자세하게 표시한다 #494

Merged
merged 9 commits into from
Oct 17, 2023
13 changes: 3 additions & 10 deletions frontend/src/features/comments/components/Comment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,23 @@ import styled from 'styled-components';
import shookshook from '@/assets/icon/shookshook.svg';
import Avatar from '@/shared/components/Avatar';
import Spacing from '@/shared/components/Spacing';
import convertRelativeTimeString from '@/shared/utils/convertRelativeTimeString';

interface CommentProps {
content: string;
createdAt: string;
writerNickname: string;
}

// FIXME: 분리 및 포맷 정리, ~일 전 말고도 세분화 필요
const rtf = new Intl.RelativeTimeFormat('ko', {
numeric: 'always',
});

const Comment = ({ content, createdAt, writerNickname }: CommentProps) => {
const time = Math.ceil(
(new Date(createdAt).getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24)
);

return (
<Wrapper>
<Flex>
<Avatar src={shookshook} alt="익명 프로필" />
<Spacing direction="horizontal" size={14} />
<Box tabIndex={0} role="comment">
<Username>{writerNickname}</Username>
<RelativeTime>{rtf.format(time, 'day')}</RelativeTime>
<RelativeTime>{convertRelativeTimeString(createdAt)}</RelativeTime>
<Content>{content}</Content>
</Box>
</Flex>
Expand Down
61 changes: 61 additions & 0 deletions frontend/src/shared/utils/convertRelativeTimeString.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import convertRelativeTimeString from './convertRelativeTimeString';
cruelladevil marked this conversation as resolved.
Show resolved Hide resolved

describe('ISO-8601 형식의 string을 받아 현재 시간과 비교해서 상대시간을 반환한다.', () => {
// NOTE: 상대 시간 기준은 n <= 상대 시간 < n + 1 입니다.
test('차이가 없을 경우 방금 전을 반환한다.', () => {
const now = new Date().toISOString();

expect(convertRelativeTimeString(now)).toBe('방금 전');
});

test('차이가 0초 이상, 1분 미만의 경우 방금 전을 반환한다.', () => {
const createdAt = '2023-10-06T09:59:59.999';
const now = '2023-10-06T10:00:00';

expect(convertRelativeTimeString(createdAt, now)).toBe('방금 전');
});

test('차이가 1분 이상, 1시간 미만인 경우 n분 전을 반환한다', () => {
const createdAt = '2023-10-06T09:50:10';
const now = '2023-10-06T10:00:00';

expect(convertRelativeTimeString(createdAt, now)).toBe('9분 전');
});

test('차이가 1시간 이상, 1일 미만인 경우 n시간 전을 반환한다.', () => {
const createdAt = '2023-10-06T08:12:00';
const now = '2023-10-06T10:00:00';

expect(convertRelativeTimeString(createdAt, now)).toBe('1시간 전');
});

test('차이가 1일 이상, 1주 미만인 경우 n일 전을 반환한다.', () => {
const createdAt = '2023-10-03T10:00:00';
const now = '2023-10-06T10:00:00';

expect(convertRelativeTimeString(createdAt, now)).toBe('3일 전');
});

test('차이가 1주 이상, 1달 미만인 경우 n주 전을 반환한다.', () => {
const createdAt = '2023-09-11T10:00:00';
const now = '2023-10-06T10:00:00';

expect(convertRelativeTimeString(createdAt, now)).toBe('3주 전');
});

test('차이가 1달 이상, 1년 미만인 경우 n개월 전을 반환한다.', () => {
// NOTE: 1달을 30일로 계산하기 때문에 오차가 약간 있습니다.
const createdAt = '2023-06-08T10:00:00';
const now = '2023-10-06T10:00:00';

expect(convertRelativeTimeString(createdAt, now)).toBe('4개월 전');
});

test('차이가 1년 이상인 경우 n년 전을 반환한다.', () => {
// NOTE: 예시가 3년이 되기 직전 값으로 최대값으로 볼 수 있습니다.
const createdAt = '2020-10-06T10:00:00.001';
const now = '2023-10-06T10:00:00';

expect(convertRelativeTimeString(createdAt, now)).toBe('2년 전');
});
});
43 changes: 43 additions & 0 deletions frontend/src/shared/utils/convertRelativeTimeString.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// NOTE: 상수는 모두 ms단위 입니다.
const SECOND = 1000;

// NOTE: cutoff를 위한 상수들입니다.
const LESS_THAN_MINUTE = 0;
const MINUTE = 60 * SECOND;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;
const WEEK = 7 * DAY;
const MONTH = 30 * DAY;
const YEAR = 365 * DAY;
const MORE_THAN_YEAR = Infinity;

const convertRelativeTimeString = (createdAt: string, now = new Date().toISOString()) => {
const rtf = new Intl.RelativeTimeFormat('ko', { numeric: 'always' });

const createdTime = new Date(createdAt).getTime();
const nowTime = new Date(now).getTime();
const deltaTime = createdTime - nowTime;

const units: Intl.RelativeTimeFormatUnit[] = [
'second',
'minute',
'hour',
'day',
'week',
'month',
'year',
];
// NOTE: 상대 시간 기준을 초과하는지 오름차순으로 확인하기 위한 배열입니다.
const cutoffs = [LESS_THAN_MINUTE, MINUTE, HOUR, DAY, WEEK, MONTH, YEAR, MORE_THAN_YEAR];

// NOTE: 기준을 초과하는 요소 바로 전 요소가 상대 시간의 기준이 되는 요소입니다.
const unitIndex = cutoffs.findIndex((cutoff) => cutoff > Math.abs(deltaTime)) - 1;
const divisor = cutoffs[unitIndex];

if (divisor === LESS_THAN_MINUTE) {
return '방금 전';
}
return rtf.format(Math.ceil(deltaTime / divisor), units[unitIndex]);
};

export default convertRelativeTimeString;
Loading