[미니 프로젝트] Jelog 만들기 #5

박제현·2023년 7월 26일

jelog 만들기

목록 보기
5/15

🐫 게시글에 이미지 추가 구현하기.

지금까지는 <textarea> 태그로 단순히 텍스트만 입력 가능했지만, 오늘 이미지 추가 기능을 구현해서 좀 더 완성도 있는 포스팅 기능을 구현하려고 한다.

첫번째로 해야할 것은 <textarea>태그를 이미지를 추가할 수 있는 태그로 변경하는 것.
<textarea>에는 오로지 텍스트만 입력할 수 있으므로, 이미지를 추가할 수 있고 텍스트도 입력할 수 있는 태그를 찾아야한다.

처음 머릿속으로는 <div> 태그안에 <image> 태그를 추가하고, 텍스트들은 <span> 태그로 관리할려고 생각했었다.
근데.. 이러면 좀 이미지를 추가하고, 또 글을 작성하고, 또 이미지를 추가하고 이렇게 하다보면 이렇게 하나하나 DOM에 appendChild() 방식으로 하는 것은, 매우매우 비효율적이라고 생각했다.. 🤔

그래서! 구글링을 했다..🤣
생각보다 여러 사람들이 <textarea> 태그에 이미지를 넣는 방법에 대해 질문이 많았다.
정답은 <div> 태그에 contentEditable 속성을 추가하는 것이었다.
contentEditable 속성을 추가하면 <div> 태그도 마치 <textarea> 태그처럼 텍스트를 입력할 수 있게 변한다.

	previewContentRef.current.innerHTML = contentTextareaRef.current.innerHTML;

내부에 따로 value 값으로 요소들이 입력되는 것이 아니라, HTML 태그들로 입력되기 때문에 상태 변화는 innerHTML로 체크한다.

<div> 태그를 찍어보면 위와 같은 형태로 작성된다.
저 안에 이미지를 추가하면 자식 요소로<img> 태그가 추가된다.

참고한 사이트 : https://dev-bak.tistory.com/16
<div> 태그에 여러 텍스트 스타일 적용과 이미지 추가에 대해 자세하게 설명하고 있다.

작동 영상을 보면 이미지가 정상적으로 업로드 되고, 텍스트 역시 정상적으로 입력할 수 있는 것을 볼 수 있다.

🧐 DOM 객체를 Json에 넣는 방법.

<div> 태그를 이용해서 이미지, 텍스트가 있는 게시글 객체 만들기는 완성을 했는데..
이 게시글을 어떻게 저장하고, 또 어떻게 보여줘야 할 까?
그냥 무작정 기존에 포스트 정보를 담던

    const newPost = {
      userID: userID,
      id: postList.length.toString(),
      title: title,
      content: content,
      date: today.toLocaleDateString(),
      likesCount: 0,
    };

로 게시글 객체를 저장하면, 당연히 오류가 발생한다.
게시글 정보를 넘겨주는게 아니라 그냥 객체 자체를 넘겨주기 때문에 {content}로 출력하면 [object] 로 출력되거나, 아예 컴파일 조차 되지 않았다.

그래서 <div> 객체 자체를 string으로 변환하고, 화면에 띄워줄 때 다시 html 형태로 변환 해주면 정상적으로 객체를 넘겨 받을 수 있었다.

먼저 DOM 객체를 string 형태로 저장하려면, 그냥 outerHTML 속성을 사용하면 된다. 쉽네..?

  const handleAddPost = () => {
    const today = new Date();
    const title = titleTextareaRef.current.value;
    const content = previewContentRef.current.outerHTML;
    const newPost = {
      userID: userID,
      id: postList.length.toString(),
      title: title,
      content: content,
      date: today.toLocaleDateString(),
      likesCount: 0,
    };
    setPostList([...postList, newPost]);
    const headerContainer = document.querySelector(".headerContainer");
    headerContainer.classList.remove("hide");
  };

이렇게 넘겨주게 되면,
이렇게 HTML 코드가 string 형태로 넘겨지게 된다.

그러면 이제 string으로 받은 HTML 코드를 다시 DOM 객체로 만들어주는 방법은 무엇일까?

그 방법은 DOMParser를 사용하는 것이다.

DOMParser

DOMParser는 String 형태의 XML 또는 HTML 코드를 DOM 문서로 분석, 구조화 할 수 있는 기반을 제공한다.

const trendingPosts = trendingPostList.map((item, index) => {
    const contentElement = new DOMParser()
      .parseFromString(item.content, "text/html")
      .querySelector("div");
});
                                       

마지막에 .querySelector("div")를 붙여준 이유는, DOMParser를 이용해 html 문자열을 파싱하면 자동으로 <head>, <body>등의 요소가 생성된다.
그래서 우리가 필요한 <div> 태그만 선택해서 작업하기 편하게 선언했다.

👨‍💻 변수에 저장된 DOM 객체를 다른 DOM 객체에 추가하기.

정상적으로 HTML 코드로 변환했으니, 이제 내가 필요한 위치에 이 객체를 추가하기만 하면 될 것 같았다.
그래서, 그냥

<Link
        to={`/post/${item.userID}/${item.id}`}
        key={index}
        className="postBox"
      >
        <div className="innerBox">
          <span className="title">{item.title}</span>
          <span className="content">{contentElement}</span>
        </div>
        <span className="date">{item.date}</span>
        <section className="infoSection">
          <div>
            <div className="userIcon" />
            <span className="userID">by {item.userID}</span>
          </div>
          <div className="likesCount">
            <FontAwesomeIcon icon={faHeart} />
            {item.likesCount}
          </div>
        </section>
      </Link>

같이 코드를 작성했는데.. 안되더라.

이 에러는 React 상에서 객체를 직접 렌더링하는 것이 불가능하다는 말이다.

따라서, 객체를 React에서 정상적으로 렌더링 할 수 있게 JSX로 렌더링하여 반환해야 했다.

    let thumbnailJSX;

    try {
      const thumbnail = contentElement.querySelector("img");

      thumbnailJSX = React.createElement("div", {
        dangerouslySetInnerHTML: {
          __html: thumbnail.outerHTML,
        },
      });
    } catch (err) {
      thumbnailJSX = React.createElement("div");
    }

    const firstContent = Array.from(contentElement.childNodes).find(
      (item) => item.nodeName === "#text"
    ).textContent;

가장 먼저 썸네일로 보여질 <img> 태그를 찾아 createElement() 메소드를 사용하여 <div> 태그를 생성한다.
그리고, 가장 먼저 입력한 텍스트 한 줄을 찾아서 firstContent에 변수로 저장한다.
querySelector()를 사용하지 않은 이유는,
<div> 내부로 입력된 첫번째 텍스트는 따로 <div> 태그안에 입력되지 않고 그냥 [object Text] 형태로 입력됐기 때문이다.

그래서 constElement.childNodesArray 형태로 변환하여 .find() 메소드를 사용한 것이다.

이제야 정상적으로 이미지 썸네일과 가장 첫번째 문장이 포스팅 카드에 보여진다.

이제 좀 진짜 게시글의 역할을 할 수 있게 된 것 같다.

느리지만 한 스텝, 한 스텝 발전해 가고 있다.. 완전히 개발 할 때 까지 뽜이아!!🔥🔥🔥🔥

📚 오늘의 챗 GPT

  • 페이지가 완전히 로드 된 후 코드가 실행되게 하는 방법

    DOMContentLoaded 이벤트를 사용한다.

document.addEventListener("DOMContentLoaded", () => {
	//실행하고자 하는 코드를 작성한다.
}
  • HTML 코드를 String 형태로 변환하는 방법 / String 코드를 HTML 형태로 변환하는 방법

    HTML 코드를 JavaScript에서 문자열로 변환하는 방법은 DOM 객체의 outerHTML 속성 또는, jQuery의 html()메소드를 사용하여 HTML 코드를 문자열로 변환할 수 있다.
    ==================================
    문자열을 HTML로 변환하는 방법은 DOMParser를 사용하는 것이 일반적이다. DOMParser는 문자열을 파싱하여 DOM 트리를 생성하는 객체이다.

  • <div> 태그에 텍스트를 입력하는 방법

    https://developer-talk.tistory.com/844
    https://dev-bak.tistory.com/16

profile
닷넷 새싹

0개의 댓글