React TypeScript, Tailwind CSS - WinkNLP 중요 문장 하이라이팅 기능 만들기

이재환·2024년 8월 17일
0

React

목록 보기
4/4
post-thumbnail

들어가며

이번 포스트에서는 React와 TypeScript, WinkNLP를 사용하여 중요 문장 하이라이팅 및 포커스 모드 기능을 구현하는 방법을 소개하겠습니다.

Demo 및 HTML JavaScript 버전

https://observablehq.com/@winkjs/how-to-visualize-key-sentences-in-a-document
다운로드는 오른쪽 위에 점 3개 > export > download

문장의 중요도를 어떻게 아는가?

WinkNLP의 its.sentenceWiseImportance 헬퍼를 활용하여 문장의 중요도를 계산하고 해당 가설은 논문 Examining the Content Load of Part of Speech Blocks for Information Retrieval에서 제시되었습니다.


코드 구현

npm

npm install wink-nlp wink-eng-lite-web-model d3

코드

import React, { useState } from "react";
import winkNLP from "wink-nlp";
import model from "wink-eng-lite-web-model";
import { interpolateRgb } from "d3";

// NLP 초기화
const nlp = winkNLP(model);
const its = nlp.its;

const App: React.FC = () => {
  const [text, setText] = useState<string>(""); // 텍스트 상태 관리
  const [highlightPercentage, setHighlightPercentage] = useState<number>(20); // 하이라이팅 퍼센티지 상태 관리
  const [showControls, setShowControls] = useState<boolean>(false); // 컨트롤 표시 여부 상태 관리

  // 문장별 중요도 계산 함수
  const sentenceWiseNormalizedWeights = (doc: any, its: any) => {
    return doc.out(its.sentenceWiseImportance).map((e: any) => e.importance);
  };

  // 텍스트 하이라이팅 적용 함수
  const highlightText = (importance: number, maxImportance: number) => {
    if (!showControls || highlightPercentage === 0) {
      // 하이라이팅이 비활성화된 경우 기본 스타일 반환
      return { backgroundColor: "transparent", color: "black", opacity: 1 };
    }

    const normalizedImportance = importance / maxImportance;
    const opacity = Math.min(1, highlightPercentage / 100);
    const colorScale = interpolateRgb(
      `rgba(151, 78, 175, 0)`,
      `rgba(151, 78, 175, ${opacity})`
    );
    const backgroundColor = colorScale(normalizedImportance);

    return {
      backgroundColor,
      color: "black",
      opacity: 1,
      transition: "opacity 0.3s ease",
      display: "inline",
    };
  };

  // 텍스트 처리 및 출력 함수
  const processText = () => {
    const doc = nlp.readDoc(text); // 텍스트를 NLP로 처리
    const sentences = doc.sentences().out(its.value); // 문장 단위로 분리
    const sentenceWeights = sentenceWiseNormalizedWeights(doc, its); // 문장별 중요도 계산

    const maxImportance = Math.max(...sentenceWeights); // 최대 중요도 값 계산

    return sentences.map((sentence, index) => {
      const importance = sentenceWeights[index]; // 각 문장의 중요도
      const style = highlightText(importance, maxImportance); // 중요도에 따른 스타일 적용

      return (
        <span
          key={index}
          style={style} // 계산된 스타일을 적용
        >
          {sentence}{" "}
        </span>
      );
    });
  };

  return (
    <div className="p-4">
      <textarea
        value={text}
        onChange={(e) => setText(e.target.value)} // 사용자가 입력한 텍스트를 상태로 저장
        placeholder="Enter your text here..."
        className="w-full p-2 border border-gray-300 dark:border-gray-700 rounded mb-4 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100"
        rows={10}
      />

      <div className="mb-4 flex items-center">
        <label className="mr-2">Enable Controls</label>
        <label className="inline-flex relative items-center cursor-pointer">
          <input
            type="checkbox"
            checked={showControls}
            onChange={() => setShowControls(!showControls)}
            className="sr-only peer"
          />
          <div className="w-11 h-6 bg-gray-200 rounded-full peer peer-focus:ring-4 peer-focus:ring-[#974EAF] dark:peer-focus:ring-[#974EAF] dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-[#974EAF]"></div>
        </label>
      </div>

      {showControls && (
        <div>
          <div className="mb-4">
            <label className="mr-2">Highlight Percentage:</label>
            <input
              type="range"
              min="0"
              max="100"
              value={highlightPercentage}
              onChange={(e) => setHighlightPercentage(Number(e.target.value))}
              className="w-full accent-[#974EAF]"
            />
            <span className="ml-2">{highlightPercentage}%</span>
          </div>
        </div>
      )}

      <div className="prose prose-basic dark:prose-invert">{processText()}</div>
    </div>
  );
};

export default App;

중요!!

Tailwind CSS를 사용해서 prose prose-basic 없이는 스타일이 적용이 되지 않습니다.

<div className="prose prose-basic">{processText()}</div>

문장의 중요도를 분석하는 중요한 함수

const sentenceWiseNormalizedWeights = (doc: any, its: any) => {
    return doc.out(its.sentenceWiseImportance).map((e: any) => e.importance);
  };

결론

이번 포스트에서는 React와 TypeScript를 활용하여 문서에서 중요한 문장을 하이라이팅하는 기능을 만들어 봤습니다.

0개의 댓글