프로젝트

#4 [REACT.JS] 에디터 구현하기-1

user-anonymous 2024. 2. 14. 21:56
728x90

간간히 진행해오던 Nerator 프로젝트에서, 사용자 명함 디자인을 조금 더 자유롭게 조작할 수 있도록 하기 위해 에디터 기능을 직접 개발해보기로 했다.

단순히 텍스트를 입력받아 출력하는 기능에서 벗어나, 사용자가 직접 디자인 요소들을 배치하고 커스터마이징할 수 있도록 하는 것이 이번 구현의 핵심 목표였다.

 

주요 기능 정의

  • 앞면 / 뒷면 구분
    • 앞/뒤 면에 따라 속성의 좌표(x, y)를 따로 관리해야 한다.
    • UI 상에서도 손쉽게 전환되며 편집할 수 있어야 한다.
  • 명함 사이즈 설정 가능
    • 사용자가 원하는 사이즈를 직접 입력 가능해야 함
  • 디자인 이미지 업로드
    • 명함 앞면, 뒷면에 각각 다른 이미지를 업로드하여 미리보기에 반영
  • Drag & Drop으로 속성 배치
    • 좌표 직접 입력이 아닌, 시각적으로 끌어다 놓는 방식 제공
  • 실시간 미리보기 제공
    • 배치된 상태가 어떻게 출력될지 바로 확인할 수 있어야 함
  • (2차 개발 예정) PDF 저장, 프린트 출력

 

 

UI 구성 방향

에디터의 기본 UI 구조는 아래와 같다:

  • 왼쪽: 명함 편집 영역 (이미지 + 배치된 속성들)
  • 오른쪽: 명함 설정 영역 (크기, 이미지 업로드, 속성 관리 등)

설정 → 반영 → 미리보기 구조로 빠르게 피드백을 받을 수 있도록 구성했다.

 

 

1. 명함 사이즈 설정 기능

사용자가 입력한 가로/세로 값을 실시간으로 적용되도록 하기 위해, React의 상태로 값을 관리하고 명함 컴포넌트에 props로 넘겨 스타일에 바로 반영되도록 구성했다.

 

INPUT 박스에서 값이 변경될 때마다 handleChangeForm 함수를 통해 state로 관리할 수 있도록 해준다.

 const handleChangeForm = useCallback(
    (parent, e) => {
      const { name, value } = e.target; 
      setForm({
        ...form,
        [parent]: {
          ...form[parent],
          [name]: value,
        },
      });
    },
    [form]
  );

form 객체는 단일 구조가 아니라 부모-자식 관계를 가진 중첩된 JSON 구조라, 변경하려는 영역의 parent 키와 name 키를 함께 넘겨서 처리하는 방식이다.

명함 자체는 아래와 같이 크기를 적용받는다. 

 

 

이렇게 이미지의 크기가 변경되면 바로 반영될 수 있도록 해당 명함 카드 컴포넌트에 form의 사이즈를 넘겨줬다. 

const Card = styled.div`
  width: ${({ size }) => size.w}px;
  height: ${({ size }) => size.h}px;
  background: white;
`;

 

Card는 이 props를 받아 바로 크기가 변경되도록 해준다.

 

2. 앞면 / 뒷면 이미지 업로드

명함에는 앞/뒷면이 있기 때문에, 각각의 이미지를 따로 관리해야 한다.
input의 id 값으로 구분해서 form.image.front, form.image.back 구조로 저장하도록 구성했다.

  <div className="child">
                        <div className="name">앞면</div>
                        <div className="value">
                          <input
                            type={"file"}
                            id={"front"}
                            onChange={handleUploadNameTag}
                          />
                        </div>
                      </div>

                      <div className="child">
                        <div className="name">뒷면</div>
                        <div className="value">
                          <input
                            type={"file"}
                            id={"back"}
                            onChange={handleUploadNameTag}
                          />
                        </div>
        </div>

 

  const handleUploadNameTag = (e) => {
    const item = { id: e.target.id, data: e.target.files[0] };
    setForm({ ...form, image: { ...form.image, [e.target.id]: item } });
  };

 

그 후 에디터에서 어떤 면을 편집하고 있는지 나타내는 editorView 상태에 따라 이미지를 조건부 렌더링한다.

file의 id 값을 가져와 form에 front, back마다 다른 이미지파일을 가지고  있도록 해줬다.

 

이제 사용자가 앞/뒤 버튼을 누를때마다 이 이미지를 보여줘야하므로

<Image
                data-key="1"
                src={
                  form.image[editorView]?.data &&
                  URL.createObjectURL(form.image[editorView]?.data)
                }
                draggable={false}
              />

 

front와 back 을 관리해주는 editorView가 있다. editorView state 값으로 어떤 이미지 파일을 뿌려주게 해줄지 설정해주는 코드다.

 

이후 개발 예정 기능

  • 속성 Drag & Drop 배치 기능
  • 속성별 폰트 / 정렬 / 스타일 설정
  • PDF 저장 or 프린트 출력 기능
  • 명함 템플릿 저장 및 불러오기 기능

 

기획 당시에는 단순한 명함 출력 도구 정도로 시작했지만,
직접 배치와 커스터마이징이 가능한 에디터를 구현하다 보니 점점 더 사용자 경험에 집중하게 되었다.

개발자 입장에서 구현 난이도는 다소 올라가지만, 사용자 관점에서는 훨씬 직관적이고 강력한 기능을 제공할 수 있다는 점에서 의미 있는 방향이라 생각했다.
다음엔 속성 Drag & Drop 기능 개발기를 정리해볼 예정이다.

728x90
반응형