본문으로 바로가기
윤창원uiwwsw · 작은 우주

개발과 기술

테이블의 요약과 상세 보기 — 최신 내용을 유지하는 컴포넌트

2026-09-10: 설명과 코드 예시를 보완했습니다. 아래 예시는 글의 설계 의도를 전달하기 위한 것이며, 원 프로젝트에 반영된 변경 내역과는 구분합니다.

테이블에서는 긴 내용을 짧게 보여주고, 필요할 때 전체를 열어볼 수 있으면 좋겠다. 같은 데이터를 요약과 상세 두 방식으로 렌더링하는 Magnifying 컴포넌트를 만들어본 이유다.

처음 예시에서는 렌더 함수의 결과를 useRef에 보관했다. 그러면 이후 데이터가 바뀌어도 처음 만든 화면이 남는다. 클릭 대상을 ref와 정확히 비교하는 방식도 내부 요소를 눌렀을 때 동작하지 않는 문제가 있었다.

이번에는 렌더 결과를 매번 계산하고, 열기 동작은 명시적인 버튼에 연결하도록 예시를 바꿨다.

요약과 상세를 같은 데이터에서 만들기

아래 Dialog는 프로젝트의 다이얼로그 컴포넌트다. open, onClose, children을 받으며, 접근 가능한 이름, 초점 이동·복귀, 키보드 닫기와 배경 상호작용 차단을 제공한다고 가정한다. 이 글에서는 테이블 셀의 렌더링 계약에 집중한다.

import { useState, type ReactNode } from 'react';
import { Dialog } from './Dialog';

type MagnifyingProps = {
  label: string;
  renderContent: (mode: 'summary' | 'detail') => ReactNode;
};

export function Magnifying({ label, renderContent }: MagnifyingProps) {
  const [open, setOpen] = useState(false);

  return (
    <>
      <button
        type="button"
        aria-label={label}
        aria-haspopup="dialog"
        onClick={() => setOpen(true)}
        style={{ maxWidth: '100%', textAlign: 'left' }}
      >
        {renderContent('summary')}
      </button>
      <Dialog open={open} onClose={() => setOpen(false)}>
        {open && renderContent('detail')}
        <button type="button" onClick={() => setOpen(false)}>닫기</button>
      </Dialog>
    </>
  );
}

사용처에서는 현재 값을 렌더 함수에 전달한다.

function DescriptionCell({ text }: { text: string }) {
  return (
    <Magnifying
      label="설명 전체 보기"
      renderContent={(mode) => mode === 'detail' ? (
        <p style={{ whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' }}>{text}</p>
      ) : (
        <span style={{
          display: 'block',
          maxWidth: 200,
          overflow: 'hidden',
          textOverflow: 'ellipsis',
          whiteSpace: 'nowrap',
        }}>
          {text}
        </span>
      )}
    />
  );
}

text가 바뀌면 요약과 열린 상세 화면 모두 새 값으로 렌더링된다. ref로 이전 JSX를 고정하지 않는다. 버튼 내부의 span을 눌러도 클릭 이벤트가 버튼으로 전달되고, 키보드로도 열 수 있다.

요약은 버튼 안에 들어가므로 링크나 또 다른 버튼을 포함하지 않는 콘텐츠여야 한다. 복잡한 셀이라면 요약 콘텐츠 옆에 별도의 상세 보기 버튼을 두는 편이 적절하다. 테이블 행 클릭 이벤트가 있다면 행 동작과 버튼 동작의 관계도 사용처에서 정해야 한다.

재사용의 경계

요약과 상세가 데이터를 공유한다는 계약은 남기고, 임의 요소 복제와 클릭 대상 추론은 줄였다. 상세 화면 안에 편집 상태가 있다면 닫을 때 초기화할지 유지할지는 추가로 정해야 한다. 이 예시는 읽기용 상세 보기를 대상으로 한다.

공통 컴포넌트를 만들 때는 줄인 코드의 양과 함께, 데이터가 바뀔 때 무엇이 갱신되는지와 사용자가 어떻게 여는지도 살펴보게 된다.

Assisted by AI

윤창원이 벨로그에 남긴 글을 이 작은 우주에도 모았습니다. 사진은 누르면 원본 크기로 볼 수 있습니다. 원문의 전체 서식 보기 ↗

모든 글 둘러보기 →