[DAY 20] VanillaJS를 통한 자바스크립트 기본 역량 강화 I (9)

송히·2023년 10월 16일
post-thumbnail

Today I Learn📖

  • 자동 저장 편집기 만들기 (강의)

API 알아보기

포스트 생성, 조회, 수정 서비스 제작시 필요한 API

  • 편집 가능한 글 목록 불러오기: https://도메인/posts (GET 요청)
  • 특정 게시글 불러오기: https://도메인/posts/{id} (GET 요청)
  • 게시글 저장하기: https://도메인/posts (POST 요청)
  • 게시글 수정하기: https://도메인/posts/{id} (PUT 요청)

별도의 저장 버튼을 누르지 않아도 자동으로 저장되는(서버와 싱크되는) 기능
-> url에 따른 라우팅 처리가 들어가는 것


textarea와 contenteditable

게시글 에디터(편집기) 만드는 방법

  1. textarea (전통적 방법)
  2. contenteditable

먼저 textarea로 돌아가게 만들고, 그 후 에디터만 contenteditable로 업그레이드 시키는 방식을 추천 !

  • textarea
    별도의 스타일을 넣을 수 없음, 스타일 입힌 거 자동으로 미리보기 안 됨
    -> 마크다운 텍스트를 넣고, 그 텍스트를 파싱해서 미리보기 띄울 수는 있음

  • contenteditable
    <div> 태그 안에 contenteditable="true" 넣으면 됨
    -> 값을 사용할 때는 innerHTML을 써야함 (.value X)

    • 서버에서 내래오는 스타일 값과 마크다운 문법이 달라서, 정규표현식을 통한 replace 혹은 if문 등으로 처리해줘야함
      ex) 서버에서 내려오는 개행값은 \n 라서 textarea 에서도 개행이 \n. 하지만 contenteditableinnerHTML을 쓰니까 <br>

      <div name="~~" contenteditable="true" ...> </div>


디바운스 (debounce)

같은 이벤트가 계속 발생하는 경우에, 그 중 마지막 작업 이후 일정 시간동안 이벤트가 더 발생하지 않으면 이벤트 핸들러를 실행시킴
=> 불필요한 처리를 줄이기 위해 이벤트가 발생하는 동안은 실행 지연시킴

let timer = null; // 시간 재는 애

new Editor({
  $target,
  initialState: post,
  onEditing: (post) => {
    // setTimeout은 아이디값을 뱉기 때문에, 그 값의 존재여부 등으로 초기화 가능
    if (timer !== null) clearTimeout(timer) // 타이머 초기화
    timer = setTimeout(() => { // 0.5초 이후에 실행됨
      setItem(TEMP_POST_SAVE_KEY, { // 0.5초 후에 로컬 스토리지에 저장
        ...post,
        tempSaveDate: new Date()
      })
    }, 500) // 0.5초 지정
  }
})


커스텀 이벤트 (Custom Event)

직접 만들어서 사용할 수 있는 이벤트 (필요하거나 자주 반복되는 기능 만들면 유용함)

  • 이벤트가 발생하는 시점이 명확함
  • depth를 따라 컴포넌트를 내리지 않고, 바로 전역적으로 사용 가능
  • 남용하면 디버깅에 어려움 생김
// PostList.js
/*
 * .dispatchEvent() 를 통해 이벤트 발생 시키기 (앞에 window 쓰면 전역적 사용 가능)
 * new CustomEvent로 커스텀 이벤트 생성 가능
 * 둘이 한번에 쓰면 정의하자마자 사용 가능
 */ 
window.dispatchEvent(new CustomEvent('route-change', { // 파라미터 => (이벤트 이름, {전달할 값})
  detail: { // detail 안에 전달할 값 정의
	nextUrl: `/post/${id}`
  }
}))

//App.js
window.addEventListener('route-change', (e) = { // 정의한 이름으로 이벤트 사용
  const { nextUrl } = e.detail // e.detail로 꺼내올 수 있음

  if (nextUrl) {
	history.pushState(null, null, nextUrl)
	this.route()
  }
})


/* 위 과정에서 'route-change'를 자주 쓰면 오타로 인한 에러 발생 위험이 있기 때문에 저것들을 모아서 한 파일로 만들면 더 좋음 */


// router.js
const ROUTE_CHANGE_EVENT_NAME = 'route-change' // 오타로 인한 에러 방지를 위해 선언

export const initRouter = (onRoute) => { // 커스텀 이벤트 감지해 url 변경 & 화면 렌더링
  window.addEventListener(ROUTE_CHANGE_EVENT_NAME, (e) => {
	const { nextUrl } = e.detail

    if (nextUrl) {
	  history.pushState(null, null, nextUrl)
	  onRoute()
	}
  })
}

export const push = (nextUrl) => { 
  window.dispatchEvent(new CustomEvent('route-change', { // 커스텀 이벤트 생성
	detail: {
	  nextUrl
	}
  }))
}

// App.js
  .
  .
  .
  initRoute(() => this.route())
}

// PostList.js
import { push } from "./router.js"

  .
  .
  .
	if ($li) {
      const { id } = $li.dataset
      push(`/post/${id}`) // push 함수를 통해 커스텀 이벤트에 요청 -> 이벤트 생성됨 -> initRouter가 감지해 작동
    }
  

😊오늘의 느낀점😊

에디터 만드는 도구는 textarea밖에 몰랐는데, 새로운 도구를 알게됐다 ㅎㅎ

노션의 자동 저장 방식이 궁금했었는데 디바운스를 이용하면 구현 할 수 있다는 것을 배웠고, 새로운 개념인 커스텀 이벤트에 대해서도 학습할 수 있었다. 커스텀 이벤트를 이용하면 컴포넌트들끼리의 의존성을 더 줄일 수 있을 것 같다 !

앞으로 진행될 노션클로닝 과제를 수행하는데 있어서 큰 힘이 될 것 같다 🥹

profile
데브코스 프론트엔드 5기

0개의 댓글