Skip to content

olhkyle/trello

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

58 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Trello with Vanilla Javascript

๐Ÿš€ Overview

  • Implemented SPA (Single Page Application) using Vanilla JavaScript without any libraries or frameworks
  • Created a Diffing algorithm for efficient rendering(Reconciliation)
  • Designed a CBD library based on the implemented Diffing algorithm, using Class (ES6+) syntax
  • Implemented various DOM event interactions using event delegation
  • Define Components as declarative views with state

๐ŸŽ›๏ธ Tech Stacks

HTML CSS(Sass) JavaScript lodash


๐Ÿ–ฅ๏ธ Main Features with demo

Feature 1

  • add Another List
  • add new Card with using Card Creator
  • remove list

Feature 2

  • change title of list with pressing 'Enter' key to save new title
  • add new Card with using Card Creator

Feature 3

  • Drag and drop with Card and List Component

  • Close ListCreator and CardCreator component to press 'ESC'

    • If textarea component in ListCreator is focused, pressing 'ESC' makes ListCreator close
    • If all textarea components in List Components are opened, pressing 'ESC' makes all CardCreator close
  • Open Modal Component, after click Card component

    • After Click textarea component and insert some words, press 'Enter' to save Card Component's title
    • If click textarea in description part, then we have to save the contents.
      • The situations Trello will not save contents
        1. click the overlay area
        2. click the area without textarea in description part
        3. click 'x' button component
        4. after click textarea in description part, if we do not click 'save' button or press 'Enter' as textarea is focused
  • Close Modal clicking outside of modal, or clicking Close Button.



โ˜•๏ธ Efforts I encountered during SPA-Like Implementation without using Library

โ˜•๏ธ Rendering based on Reconciliation mentioned in React using a Diffing Algorithm

  • SPA(Single Page Application)์™€ ์œ ์‚ฌํ•˜๊ฒŒ ๋™์ž‘ํ•˜๋„๋ก html์˜ body์—๋Š” id๊ฐ€ root์ธ ์š”์†Œ(์ปจํ…Œ์ด๋„ˆ ์š”์†Œ) ํ•˜๋‚˜๋งŒ ์กด์žฌํ•˜ ๋ฉฐ, #root ์˜ ์ž์‹ ์š”์†Œ๋“ค์€ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•˜๋ฉด ์ •์˜ํ•œ ์ปดํฌ๋„ŒํŠธ๋“ค์ด ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฌธ์ž์—ด(DOMString)์ด ํŒŒ์‹ฑ ๊ณผ์ •์— ์˜ํ•ด DOM ์š”์†Œ๋กœ ๋ณ€ํ™˜๋˜์–ด ๋™์ ์œผ๋กœ ํ™”๋ฉด์„ ๊ทธ๋ฆฌ๋„๋ก ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

  • ํšจ์œจ์ ์ธ ๋ Œ๋”๋ง์„ ์œ„ํ•œ Diffing ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ฐ CBD ๊ตฌํ˜„

    // renderDOM.js
    let $root = null;
    let component = null;
    
    const renderDOM = (Component, $container) => {
    	if ($container) $root = $container;
    	if (Component) {
    		component = new Component({ $root: $container });
    		$root.innerHTML = component.render();
    	}
    
    	const createNewTree = () => {
    		const cloned = $root.cloneNode(true);
    		const domString = component.render();
    
    		cloned.innerHTML = domString;
    		return cloned;
    	};
    
    	// reconciliation
    	diff($root, createNewTree());
    
    	// bind Events
    	bindEventHandler($root);
    };
    • Initial Rendering

      • renderDOM์ด๋ผ๋Š” ํ•จ์ˆ˜๋Š” id๊ฐ€ root์ธ ์š”์†Œ์— ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœํ•˜๋ฉด์„œ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” DOMString์„ ๋ชจ์•„,์ด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ UI๋ฅผ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.
    • Rerendering

      • ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐ–๊ณ  ์žˆ๋Š” ์ƒํƒœ๊ฐ€ ๋ณ€ํ•˜๋ฉด, UI๋ฅผ ๋‹ค์‹œ ๊ทธ๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋•Œ, setState๋ผ๋Š” ์ƒํƒœ ์—…๋ฐ์ดํŠธ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด์„œ, renderDOM์ด๋ผ๋Š” ํ•จ์ˆ˜๋Š” ๋ฆฌ๋ Œ๋”๋ง์„ ์œ„ํ•ด ๋‹ค์‹œ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ์ด ๋•Œ, ์ธ์ˆ˜๋กœ Component์™€ $container๋ฅผ ์ „๋‹ฌํ•˜์ง€ ์•Š์Šต ๋‹ˆ๋‹ค.
      • ๊ทธ ์ด์œ ๋Š”, Initial Rendering ์‹œ์— renderDOM ํ•จ์ˆ˜ ์Šค์ฝ”ํ”„ ๋ฐ”๊นฅ์— ์„ ์–ธํ•œ $root(id๊ฐ€ root์ธ ์š”์†Œ)์—๋Š” App Class Component๊ฐ€ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœํ•œ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋“ค์ด ๋ฐ˜ํ™˜ํ•˜๋Š” DOMString์ด innerHTML ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ํ• ๋‹น๋˜์–ด ์žˆ์–ด, ํด๋กœ์ €์— ์˜ํ•ด ์–ด๋–ค UI๋ฅผ ๊ทธ๋ฆด ์ง€์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ด๋ฏธ ๋‹ด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด $root๋Š” ๋’ค์—์„œ ์ด์•ผ๊ธฐํ•  ๋ฉ”๋ชจ๋ฆฌ์— DOMString์ด ์ €์žฅ๋œ oldNode๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.
      • ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•˜๋ฉด ์—…๋ฐ์ดํŠธ ๋œ ์ƒํƒœ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ UI๋ฅผ ๋‹ค์‹œ ๊ทธ๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋จผ์ €, createNewTree๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ, ๋ณต์ œํ•œ $root ๋ณ€์ˆ˜์— ์—…๋ฐ์ดํŠธ ๋œ ์ƒํƒœ๋ฅผ ๊ฐ€์ง„ component ๋ณ€์ˆ˜๊ฐ€ ์ €์žฅํ•˜๊ณ  ์žˆ๋Š” DOMString(App Class Component๊ฐ€ ์žฌ๊ท€์ ์œผ๋กœ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด์„œ ๋Œ์–ด๋ชจ์€ DOMstring ๊ฐ’)์„ innerHTML ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ํ• ๋‹นํ•˜๊ณ , ๋ณต์ œํ•œ $root๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
      • ๊ทธ๋ฆฌ๊ณ , diff ํ•จ์ˆ˜(diffing ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ๊ตฌํ˜„๋œ ํ•จ์ˆ˜)๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ, ๊ธฐ์กด์— ๋ฉ”๋ชจ๋ฆฌ์— ์˜ฌ๋ ค์ ธ ์žˆ๋˜ oldNode(type === object)์™€ createNewTree ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜ํ•œ ๋ณต์ œ๋œ $root์ธ newNode(type === object)๋ฅผ ๋น„๊ตํ•˜์—ฌ reconciliation์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
      • ์ฆ‰, ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ๋˜์–ด ์žˆ๋˜ DOM ํŠธ๋ฆฌ ํ˜•ํƒœ๋ฅผ ๋„๋Š” oldNode์™€ ์ƒํƒœ ๋ณ€๊ฒฝ์— ์˜ํ•ด ์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑ๋œ newNode๋ฅผ ๋น„๊ตํ•˜๋ฉด์„œ, ๋‹ค๋ฅธ ๋ถ€๋ถ„(e.g ์š”์†Œ ๋…ธ๋“œ, ์–ดํŠธ๋ฆฌ๋ทฐํŠธ ๋…ธ๋“œ ๋“ฑ)๋งŒ ์—…๋ฐ์ดํŠธ ํ•˜๋Š” ๋ฐฉ์‹์„ ์ทจํ•ฉ๋‹ˆ๋‹ค.
      • ๊ฒฐ๊ณผ์ ์œผ๋กœ, ๋ฉ”๋ชจ๋ฆฌ์— ์˜ฌ๋ ค์ ธ ์žˆ๋Š” ๊ฐ์ฒด(DOMString)์˜ ์ฐธ์กฐ๊ฐ’์„ ๋น„๊ตํ•˜๋ฉด์„œ ์—…๋ฐ์ดํŠธ ํ•œ๋‹ค๊ณ  ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โ˜•๏ธ DOMstring์€ utf-16 ๋ฌธ์ž์—ด์„ ์œ„ํ•œ ๋…๋ฆฝ์ ์œผ๋กœ ๊ตฌํ˜„๋œ DOM ์ธํ„ฐํŽ˜์ด์Šค๋กœ, javascript ๋ฌธ์ž์—ด์€ ์ด๋ฏธ utf-16 ๋ฌธ์ž์—ด ํ˜•ํƒœ์ด๋‹ค. ๋”ฐ๋ผ์„œ, javascript์˜ ๋ชจ๋“  ์ธ์Šคํ„ด์Šค String์€ DOMString ์ธ์Šคํ„ด์Šค๋ผ๊ณ  ์ด์•ผ๊ธฐ ํ•  ์ˆ˜ ์žˆ๋‹ค.

โ˜•๏ธโ˜•๏ธ Event Delegation, 'this' binding and event Handling

- class ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ์ด๋ฒคํŠธ ์œ„์ž„, ๊ทธ๋ฆฌ๊ณ  this ๋ฐ”์ธ๋”ฉ, ์ด๋ฒคํŠธ ํ•ธ๋“ค๋ง ๋ฐฉ์‹์— ๋Œ€ํ•ด ๊ณ ๋ฏผํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • ์ด๋ฒคํŠธ ์œ„์ž„์„ ํ†ตํ•ด ๋ชจ๋“  ์ด๋ฒคํŠธ๋ฅผ ํ•ธ๋“ค๋ง ํ•˜๋„๋ก ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ฆ‰, $root๋ผ๋Š” ๋ฃจํŠธ ์ปจํ…Œ์ด๋„ˆ ์š”์†Œ์— ์ด๋ฒคํŠธ๋ฅผ ์œ„์ž„ํ•จ์œผ๋กœ์จ, ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ๋ฅผ ํ•ธ๋“ค๋งํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ด๋ฒคํŠธ ์œ„์ž„์— ์˜ํ•ด $root ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ, window ์ „์—ญ ๊ฐ์ฒด์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ event Type์— ๋งž๋Š” ์ด๋ฒคํŠธ๋ฅผ ํ•ธ๋“ค๋งํ•ฉ๋‹ˆ๋‹ค.
  • event Type์— ๋”ฐ๋ผ ์‹คํ–‰๋  handler์—๋Š” this์— ๋Œ€ํ•œ ์ดํ•ด๊ฐ€ ํ•„์š”ํ•œ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•˜์˜€๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. this๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ ๊ฐ์ฒด์— ๋ฐ”์ธ๋”ฉ ๋˜๋ฏ€๋กœ, bind ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์ž์‹ ์ปดํฌ๋„ŒํŠธ์— ๋ฐ”์ธ๋”ฉํ•˜์—ฌ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ(์ฝœ๋ฐฑํ•จ์ˆ˜)๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.


๐Ÿ”ญ What I learned

  • ๋ฐ”๋‹๋ผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ์›น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค์–ด ๋ณธ ๊ฐ’์ง„ ๊ฒฝํ—˜
    • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์—†์ด ๋ฐ”๋‹๋ผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋งŒ์„ ์ด์šฉํ•˜์—ฌ SPA์ฒ˜๋Ÿผ ๊ตฌํ˜„ํ•  ๋•Œ ์ด๋ฒคํŠธ๋ฅผ ํ•ธ๋“ค๋งํ•˜๊ณ  ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ๊ณผ์ •์€ ์ƒ๋‹นํžˆ ์–ด๋ ค์› ์ง€๋งŒ, ๊ตฌํ˜„์ด ์™„๋ฃŒ๋˜์—ˆ์„ ๋•Œ ์ƒ๋‹นํžˆ ๋งŒ์กฑ์Šค๋Ÿฌ์› ์Šต๋‹ˆ๋‹ค.
    • this ๋ฐ”์ธ๋”ฉ, class Syntax์— ๋Œ€ํ•œ ์ดํ•ด๋„๊ฐ€ ์ด์ „๋ณด๋‹ค ๋†’์•„์กŒ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.
    • ์ž˜ ์“ฐ์ง€ ์•Š์•˜๋˜ DOM API์— ๋Œ€ํ•ด ์•Œ๊ฒŒ ๋˜์–ด ์ข‹์•˜์œผ๋ฉฐ, ์ด๋ฒคํŠธ ์œ„์ž„์„ ํ™œ์šฉํ•œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋ง ๊ฒฝํ—˜์€ ๊ฐ’์กŒ์Šต๋‹ˆ๋‹ค.