react에서 더보기 기능 구현하기
React와 CSS를 활용해 말줄임 처리와 더보기 기능을 구현하는 방법을 단계별로 설명하며, 애니메이션까지 적용해 사용자 경험을 향상시키는 과정을 다룹니다.
line-clmapellipsisCSSReact
사용자 인터페이스(UI)를 디자인할 때 긴 텍스트를 제한된 공간에 표시해야 하는 경우가 종종 있습니다. 이러한 상황에서는 텍스트를 말줄임 처리하거나 "더보기" 기능을 추가하여 사용자 경험을 개선할 수 있습니다. 이 글에서는 React를 활용해 텍스트를 말줄임 처리하고, 더보기 기능을 구현하는 방법을 단계별로 알아보겠습니다.
텍스트 말줄임 처리하기
먼저 텍스트를 특정 줄까지만 표시하고 나머지는 숨기는 방법을 살펴보겠습니다. 이를 위해 CSS의 line-clamp
속성을 활용합니다. 아래는 lineClamp
속성을 받아 특정 줄까지만 표시하는 EllipsisTypography
컴포넌트입니다.
import type { ComponentProps, FC, PropsWithChildren } from "react";
import { css } from "@emotion/react";
import { Typography } from "ui";
const wrapperStyle = css`
position: relative;
display: -webkit-inline-box;
overflow: hidden;
transition: max-height 0.3s ease;
-webkit-box-orient: vertical;
`;
type EllipsisTypographyProps = PropsWithChildren<{
variant: ComponentProps<typeof Typography>["variant"];
lineClamp: number;
}>;
const EllipsisTypography: FC<EllipsisTypographyProps> = ({ lineClamp, variant, children }) => {
return (
<Typography
variant={variant}
css={[
wrapperStyle,
{
WebkitLineClamp: lineClamp,
},
]}
>
{children}
</Typography>
);
};
export default EllipsisTypography;
더보기 기능 추가하기
이제 말줄임 처리된 텍스트에 더보기 기능을 추가해봅시다. 텍스트의 말줄임 여부는 clientHeight
와 scrollHeight
를 비교하여 확인할 수 있습니다. 컴포넌트 이름은 역할에 맞게 ExpandableTypography
로 변경했습니다.
import type { ComponentProps, FC, PropsWithChildren, ReactNode } from "react";
import { useEffect, useRef, useState } from "react";
import { css } from "@emotion/react";
import { Typography } from "@PRNDcompany/revolt-ui";
const wrapperStyle = css`
position: relative;
display: -webkit-inline-box;
overflow: hidden;
-webkit-box-orient: vertical;
`;
type ExpandableContentProps = PropsWithChildren<{
variant: ComponentProps<typeof Typography>["variant"];
lineClamp: number;
expendButton?: ReactNode;
}>;
const ExpandableTypography: FC<ExpandableContentProps> = ({ lineClamp, children, expendButton, ...props }) => {
const ref = useRef<HTMLSpanElement>(null);
const [isExpanded, setIsExpanded] = useState(false);
useEffect(() => {
if (!ref.current) return;
setIsExpanded(ref.current.scrollHeight <= ref.current.clientHeight);
}, []);
const handleClick = () => {
if (!ref.current) return;
setIsExpanded(true);
};
return (
<Typography
{...props}
ref={ref}
onClick={handleClick}
css={[
wrapperStyle,
{
cursor: isExpanded ? "auto" : "pointer",
WebkitLineClamp: isExpanded ? "auto" : lineClamp,
},
]}
>
{children}
{!isExpanded && expendButton && <div css={{ position: "absolute", right: 0, bottom: 0 }}>{expendButton}</div>}
</Typography>
);
};
export default ExpandableTypography;
애니메이션 적용하기
더보기 기능에 애니메이션을 추가해 사용자 경험을 개선할 수 있습니다. WebkitLineClamp
는 애니메이션을 지원하지 않으므로 maxHeight
값을 사용하여 애니메이션을 구현합니다.
import type { ComponentProps, FC, PropsWithChildren, ReactNode } from "react";
import { useEffect, useRef, useState } from "react";
import { css } from "@emotion/react";
import { Typography } from "@PRNDcompany/revolt-ui";
const wrapperStyle = css`
position: relative;
display: -webkit-inline-box;
overflow: hidden;
transition: max-height 0.3s ease;
-webkit-box-orient: vertical;
`;
type ExpandableContentProps = PropsWithChildren<{
variant: ComponentProps<typeof Typography>["variant"];
lineClamp: number;
expendButton?: ReactNode;
}>;
const ExpandableTypography: FC<ExpandableContentProps> = ({ lineClamp, children, expendButton, ...props }) => {
const ref = useRef<HTMLSpanElement>(null);
const [isExpanded, setIsExpanded] = useState(false);
useEffect(() => {
if (!ref.current) return;
setIsExpanded(ref.current.scrollHeight <= ref.current.clientHeight);
ref.current.style.maxHeight = `${ref.current.getBoundingClientRect().height}px`;
}, []);
const handleClick = () => {
if (!ref.current) return;
setIsExpanded(true);
ref.current.style.maxHeight = `${ref.current.scrollHeight}px`;
};
return (
<Typography
{...props}
ref={ref}
onClick={handleClick}
css={[
wrapperStyle,
{
cursor: isExpanded ? "auto" : "pointer",
WebkitLineClamp: isExpanded ? "auto" : lineClamp,
},
]}
>
{children}
{!isExpanded && expendButton && <div css={{ position: "absolute", right: 0, bottom: 0 }}>{expendButton}</div>}
</Typography>
);
};
export default ExpandableTypography;
결과
이제 텍스트를 말줄임 처리하고, 더보기 기능과 애니메이션까지 적용할 수 있게 되었습니다. 이를 활용해 사용자 경험을 개선하고 다양한 UI 요구사항을 만족시킬 수 있습니다.
혹시 더 좋은 구현 방안이 있다면 의견 부탁드립니다!