230823 React Quill, Image Resize | 메모리 | Zustand

나윤빈·2023년 8월 23일
1

TIL

목록 보기
40/55
post-custom-banner

React Quill 사용하기

❓ Problem

이전 글을 보면 알 수 있듯이, React Quill 에디터를 사용하기 위한 기본 설정은 마친 뒤였다. 근데 직접 사용해보니 에디터 내에서 bold와 italic이 적용이 되지 않았다.

💡 Solution

현재 프로젝트에서는 styled-components 라이브러리를 통해 GlobalStyle.tsx 설정해놓은 상태였는데, 해당 컴포넌트에 다음과 같은 코드를 추가하여 이 부분은 쉽게 해결되었다.

// 에디터 설정
.ql-editor {
    strong {
        font-weight:bold;
    }
    em {
        font-style: italic;
    }
}

게시글 Create & Read

게시글 Create

import { NewPost } from '../../../types/types';
import { createPost } from '../../../api/post';
import { WriteProps } from '../../../types/props';

const Write = ({ writeModal, setWriteModal, setSearchModal, setPost }: WriteProps) => {
  const { pathname } = useLocation();
  const queryKey = pathname === '/review' ? 'reviews' : 'mates';
  const [title, setTitle] = useState<string>('');
  const [body, setBody] = useState<string>('');

  const onChangeTitle = (e: React.ChangeEvent<HTMLInputElement>) => {
    setTitle(e.target.value);
  };

  // Post 추가
  const queryClient = useQueryClient();
  const createMutation = useMutation(createPost, {
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: [`${queryKey}`, pathname] });
    }
  });

  const createButton = () => {
    // 유효성 검사
    if (!title) {
      return alert('제목을 입력해주세요.');
    }
    if (title.length > 35) {
      return alert('제목은 25글자 이하로 입력해주세요.');
    }
    if (!body) {
      return alert('내용을 입력해주세요.');
    }

    // newPost 선언
    const newPost: NewPost = {
      title,
      body
    };

    // DB 추가
    createMutation.mutate(newPost);

    // 입력값 초기화
    setTitle('');
    setBody('');
  };

  return (
    <>
      <ModalContainer>
        <ModalBox>
          <div>
            <div>
              <span>제목 : </span>
              <input
                value={title}
                onChange={onChangeTitle}
                placeholder="제목은 25글자 이하로 입력해주세요."
                style={{ width: '600px' }}
              />
            </div>
            <div>
              <Editor body={body} setBody={setBody} />
            </div>
          </div>
          <div>
            <button onClick={createButton}>등록</button>
          </div>
        </ModalBox>
      </ModalContainer>
    </>;
  );
};

export default Write;

Editor 컴포넌트에서 props로 내려받은 setBody에 에디터 입력값을 담아준다. 이후 Write 컴포넌트에서 body 값을 이용하여 게시글의 내용을 담아주고 게시글은 DB에 Create 한다.

게시글 Read

import Edit from '../write/Edit';
import Comments from './Comments';

import { DetailProps } from '../../../types/props';

const Detail = ({ post, setPost }: DetailProps) => {
  return (
    <ModalContainer>
      <ModalBox>
        <div>
            <div
              className="ql-snow"
              style={{ width: '90%', border: '1px solid black', padding: '20px', margin: '10px' }}
            >
              <div>작성일자 : {post.created_at}</div>
              <div>제목 : {post.title}</div>
              <div className="ql-editor" dangerouslySetInnerHTML={{ __html: post.body }} />
            </div>
        </div>
      </ModalBox>
    </ModalContainer>
  );
};

export default Detail;

React Quill로 작성한 내용은 HTML 형태로 저장된다. React Quill에는 HTML 형태의 내용을 변환해서 출력하는 기능은 따로 없었기 때문에 <div dangerouslySetInnerHTML={{ __html: post.body }} /> 방식을 사용하였다.

dangerouslySetInnerHTML은 React에서 HTML 콘텐츠를 직접 삽입할 때 사용되는 속성이고, __html은 실제로 HTML을 삽입하는데 사용되는 특수한 키이다.

❓ Problem

문제는 에디터로 저장한 게시글의 내용이 제대로 출력되지 않았다. 에디터 내에서는 잘 적용되던 에디터 기능들이 위의 방식으로 출력을 하니까 적용되지 않은 채 텍스트와 이미지만 보여지는 것이다.

💡 Solution

해결 방법은 생각보다 간단했다. 에디터의 CSS를 적용해주기 위해서 className="ql-snow" 그리고 className="ql-editor" 과 같이 className을 명시해주었다.

React Quill Image Resize

React Quill 라이브러리에는 이미지 사이즈를 조절하는 툴이 따로 없다. 그래서 외부 라이브러리를 사용하기로 했다.

🤔 TRY

내가 사용해 보고자 시도 해본 이미지 사이즈를 조절하는 라이브러리는 다음과 같다.

  • quill-image-resize-module
  • quill-image-resize-module-ts
  • @looop/quill-image-resize-module-react

❓ Problem

위의 모듈을 모두 설치하고 import 하고자 했으나... 사실 3개 말고도 더 해봤던 것 같다. 결론은 모두 다음과 같은 오류를 내며 import 자체가 되지 않았다.

모듈 'quill-image-resize-module-react'에 대한 선언 파일을 찾을 수 없습니다. '/Users/iseungseob/Desktop/trippy-web/node_modules/quill-image-resize-module-react/image-resize.min.js'에는 암시적으로 'any' 형식이 포함됩니다.
해당 항목이 있는 경우 'npm i --save-dev @types/quill-image-resize-module-react'을(를) 시도하거나, 'declare module 'quill-image-resize-module-react';'을(를) 포함하는 새 선언(.d.ts) 파일 추가

💡 Solution

2일에 걸친 구글링 끝에 새로운 라이브러리를 찾을 수 있었고, 결국 성공했다. 내가 성공한 라이브러리는 다음과 같다.

  • "@xeger/quill-image-actions";
  • "@xeger/quill-image-formats";

✅ 사용 방법

1) 두 개의 라이브러리를 모두 다음과 같은 방법으로 import 하고 다음과 같이 코드를 작성한다.

import { ImageActions } from '@xeger/quill-image-actions';
import { ImageFormats } from '@xeger/quill-image-formats';

Quill.register('modules/imageActions', ImageActions);
Quill.register('modules/imageFormats', ImageFormats);

2) 모듈 설정에 imageActions: {}, imageFormats: {}를 추가하고, ImageResize 설정을 추가한다.

// 에디터 설정
  const modules = React.useMemo(
    () => ({
      imageActions: {},
      imageFormats: {},
      // 툴바 설정
      toolbar: {
        container: [
          [{ header: [1, 2, false] }], // header 설정
          ['bold', 'italic', 'underline', 'strike', 'blockquote'], // 굵기, 기울기, 밑줄 등 부가 tool 설정
          [{ list: 'ordered' }, { list: 'bullet' }, { indent: '-1' }, { indent: '+1' }], // 리스트, 인덴트 설정
          ['link', 'image'], // 링크, 이미지, 비디오 업로드 설정
          [{ align: [] }, { color: [] }, { background: [] }], // 정렬, 글자 색, 글자 배경색 설정
          ['clean'] // toolbar 설정 초기화 설정
        ],

        // 핸들러 설정
        handlers: {
          image: imageHandler // 이미지 tool 사용에 대한 핸들러 설정
        },

        // 이미지 크기 조절
        ImageResize: {
          modules: ['Resize']
        }
      }
    }),
    []
  );
  1. 툴바에 사용되는 툴 포맷에 float, height, width 를 추가한다.
// 툴바에 사용되는 툴 포맷
  const formats = [
    'header',
    'bold',
    'italic',
    'underline',
    'strike',
    'blockquote',
    'list',
    'bullet',
    'indent',
    'link',
    'image',
    'align',
    'color',
    'background',
    'float',
    'height',
    'width'
  ];

나를 에디터 늪에서 벗어나게 해준 소중한 참고 블로그

메모리

1. 메모리 계층 구조와 특징

메모리는 다음과 같은 계층 구조를 가진다.

1) 레지스터
레지스터는 CPU에 위치하며 CPU가 연산을 수행하거나 데이터를 처리할 때 필요한 값을 빠르게 저장하고 검색하는 데 사용된다.

2) 캐시 메모리
캐시 메모리는 CPU와 메인 메모리 사이에 위치하며 CPU와 메인 메모리 간의 속도 차이를 줄이기 위해 사용된다.

3) 메인 메모리
메인 메모리는 프로그램 실행 중에 필요한 코드와 데이터를 보유하며 CPU가 현재 처리중인 데이터나 명령을 일시적으로 저장하는 휘발성 메모리이다.

4) 보조 기억장치
보조 기억장치는 데이터와 프로그램을 반영구적으로 저장하며 전원을 끄더라도 저장된 데이터가 보존되는 비휘발성 메모리이다.

2. SRAM과 DRAM

1) SRAM: SRAM은 정적 메모리로 전원 공급이 되는 동안은 기록된 내용이 지워지지 않기 때문에 재충전이 필요 없다. 접근 속도가 빠르고 가격이 비싸며 주로 레지스터나 캐시 메모리로 사용된다.

2) DRAM: DRAM은 동적 메모리로 전원이 계속 공급되더라도 주기적으로 재충전되어야 기억된 내용을 유지할 수 있다. 주로 대용량의 기억장치에 사용되며 가격이 저렴하다. 주로 주기억장치(RAM)이라고 표현하는 것은 거의 DRAM을 가리킨다.

3. CPU와 메모리의 동작 순서

주기억장치가 입력 장치에서 입력 받은 데이터 또는 보조 기억장치에 저장된 프로그램을 읽어온다. CPU는 프로그램을 실행하기 위해 주기억장치에 저장된 프로그램 명령어와 데이터를 읽어와 처리하고 결과를 다시 주기억장치에 저장한다. 주기억장치는 처리 결과를 보조 기억장치에 저장하거나 출력 장치로 보내서 출력시킨다. CPU 내 제어장치가(CU)가 앞의 과정에서 명령어가 순서대로 실행되도록 각 장치들을 제어한다.

Zunstand

❓ Redux 말고 다른 전역 상태관리 하나와 차이점을 말해주세요.

Redux 외에 React에서 사용할 수 있는 전역 상태관리 라이브러리로 Zustand가 있다. Zustand는 React의 Context와 Hooks를 기반으로 동작하며, 상태를 분리하고 모듈화하여 관리하기 때문에 상태 변경을 추적할 수 있다는 점에서 Redux 비슷하다. 이처럼 Zustand는 Redux와 비슷한 목적을 가지지만, 상대적으로 더 작고 가볍게 설계되어 있다.

Redux는 많은 설정과 구성이 필요한 반면, Zustand는 더 간단한 설정으로도 전역 상태를 관리할 수 있다. 특히, Zustand는 자체적으로 React Context를 활용하기 때문에 별도의 Provider 컴포넌트를 제공하지 않아도 된다. 또한 Redux는 액션, 리듀서, 스토어 등 많은 보일러플레이트 코드를 작성해야 하는 반면, Zustand는 간단한 함수와 Hook을 활용하여 상태와 업데이트 로직을 선언할 수 있어 코드의 양을 줄일 수 있다.

Zustand는 상태가 변경될 때 해당 상태를 사용하는 컴포넌트만 리랜더링하는 데에 최적화 되어 있는 반면, Redux는 연결된 컴포넌트의 개수와 무관하게 모든 연결 컴포넌트를 리렌더링한다. 또한 Redux는 상태 관리를 위해 단일 스토어를 사용하지만 Zustand는 각각의 상태 슬라이스를 독립적으로 관리하므로 상태의 분산 관리가 가능하다.

이러한 특징을 가진 Zustand는 작은 규모의 프로젝트나 상태 관리의 간소화를 원하는 경우 적합한 선택지일 수 있다.

profile
프론트엔드 개발자를 꿈꾸는
post-custom-banner

0개의 댓글