DEVELOP

01, 모듈이란? 

# 모듈 

: 간단하게, 자바스크립트 파일 하나 

 

# 모듈화

: 복잡하고 많은 양의 코드를 기능에 따라 각각의 파일(모듈)로 나누어 관리

- 더 효율적인 코드 관리 가능

- 비슷한 기능이 필요할 때 다른 프로그램에서 재사용 가능


02. 모듈 파일의 조건 

# 모듈 스코프

: 모듈 파일이 가지는 독립적인 스코프 

- 모듈 파일 내에서 선언한 함수나 변수는 모듈 파일 내에서만 사용이 가능 

- HTML 파일에서 JS 파일을 불러올 때 모듈 스코프를 갖게 하려면 script 태그에 type 속성을 module로 지정해줘야 함

<body>
  <script type="module" src="index.js"></script>
  <script type="module" src="printer.js"></script>
</body>

03. Live Server 설치하기 

- 모듈 문법을 활용할 때에는 브라우저에서 직접 파일을 불러오는 방식이 아니라 서버를  통해 html 파일을 실행해야 함 

 => VS code 에서 extentions -> live server 설치 -> go live 버튼 


05. 모듈 문법 

# export 키워드 

- 모듈 스코프를 가진 파일에서 외부로 내보내고자 하는 변수나 함수를 export 키워드를 통해 내보냄

# import 키우드

- 모듈 파일에서 내보낸 변수나 함수들은 다른 파일에서 import 키워드를 통해 가져옴 

export const title = 'CodeItPrinter';

export function print(value){
  console.log(value);
}
import {title, print} from './printer.js';

print(title);

- 모듈화 된 모듈은 모듈 문법을 통해 서로 연결될 수 밖에 없기 때문에 html 파일에서는 js의 진입점 역할을 하는 파일 하나만 불러옴 

  <script type="module" src="index.js"></script>

06. 메뉴 추가 기능 붙이기 

- index.js

  - && 연산자 :  왼쪽이 true면 오른쪽 항 실행 (if문을 대신함)

import { addMenu } from "./add.js";

const data = [];
const addBtn = document.querySelector('.add-btn');
const addInput = document.querySelector('.add-input');

addBtn.addEventListener('click', () => addMenu(data));
addInput.addEventListener('keypress', (e) => e.code === 'Enter' && addMenu(data));

- add.js

const addInput = document.querySelector('.add-input');
const list = document.querySelector('.list');

function add(data) {
  const inputValue = addInput.value;
  const index = data.length;
  console.log(inputValue,index);
  const li = document.createElement('li');
  li.classList.add('item');
  li.innerHTML = `<b>${index+1}</b>${inputValue}<button class="del-btn" data-index="${index}">x</div>`;
  list.append(li);

  data.push(inputValue);

  addInput.value = '';
  addInput.focus();
}

function emptyAlert() {
  alert('고민되는 메뉴를 입력해 주세요.');
  addInput.focus();
}

function maxAlert() {
  alert('메뉴는 최대 5개까지만 고민할 수 있습니다.');
  addInput.value = '';
}

export function addMenu(data) {
  const inputValue = addInput.value;

  if (inputValue === '') {
    emptyAlert();
  } else if (data.length > 4) {
    maxAlert();
  } else {
    add(data);
  }
}

- index.html

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>오늘 뭐 먹지?</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="circle1"></div>
  <div class="circle2"></div>
  <div class="main">
    <div class="container">
      <h2 class="title">오늘 뭐 먹지?</h2>
      <div class="add-column">
        <input class="add-input" type="text">
        <button class="add-btn">Add</button>
      </div>
    </div>
    <ul class="list"></ul>
    <button class="roll-btn">Roll</button>
  </div>
  <script type="module" src="index.js"></script>
</body>
</html>

- style.css

* {
	margin: 0;
	padding: 0;
	box-sizing: border-box;
	font-family: sans-serif;
}

body {
	display: flex;
	justify-content: center;
	align-items: center;
	min-height: 100vh;
	background-image: linear-gradient(to right top, #FFC9C9, #8CD3F9);
}

.circle1,
.circle2 {
	width: 200px;
	height: 200px;
	position: absolute;
	border-radius: 50%;
	background-image: linear-gradient(to right bottom, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.1));
}

.circle1 {
	bottom: calc(50% + 160px);
	left: calc(50% + 110px);
}

.circle2 {
	top: calc(50% + 120px);
	right: calc(50% + 130px);
}

.main {
	display: flex;
	flex-direction: column;
	justify-content: space-between;
	align-items: center;
	width: 360px;
	height: 530px;
	border-radius: 15px;
  background-image: linear-gradient(to right bottom, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0.3));
  z-index: 1;
}

.container {
	width: 100%;
	padding: 25px;
	background-image: linear-gradient(to right bottom, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0.3));
	border-radius: 15px;
	color: #658EC6;
}

.title {
	font-size: 30px;
	color: #426696;
	text-align: center;
	opacity: 0.8;
}

.add-column {
	display: flex;
	justify-content: center;
	margin-top: 15px;
}

.add-input {
	width: 200px;
	padding: 10px;
	background-color: linear-gradient(to right bottom, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0.3));
	border: none;
	border-radius: 5px;
}

.add-btn {
	padding: 10px;
	color: #FFFFFF;
	background-color: #8CD3F9;
	border: none;
	border-radius: 5px;
	font-weight: 600;
}

.list {
	display: flex;
	flex-direction: column;
	justify-content: center;
	align-items: center;
	width: 100%;
	height: 333px;
}

.item {
  position: relative;
	display: flex;
	align-items: center;
	width: 80%;
	height: 48px;
	margin: 7px;
	padding: 5px;
	color: #658EC6;
	background-image: linear-gradient(to right bottom, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0.3));
	border-radius: 45px;
  list-style: none;
  transition: all 1s ease-in-out;
}

.item b {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	margin-right: 15px;
	color: #FFFFFF;
  text-align: center;
  background-color: #FFC9C9;
  border-radius: 40px;
}

.del-btn {
  position: absolute;
  top: -5px;
  right: -10px;
  width: 30px;
  height: 30px;
  border: none;
  border-radius: 50%;
  color: #FF00D9;
  font-weight: 900;
  background-color: transparent;
  background-image: linear-gradient(to right bottom, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0.3));
  transition: all 0.3s ease-in-out;
  cursor: pointer;
  opacity: 0;
}

.item:hover .del-btn {
  opacity: 1;
}

.roll-btn {
	width: 100px;
	height: 45px;
	padding: 10px;
	margin-bottom: 15px;
	font-size: 18px;
	color: #FFFFFF;
	background-color: #8CD3F9;
	border: none;
	border-radius: 5px;
  font-weight: 900;
  cursor: pointer;
}

@keyframes roll {
  0% { transform: rotateX(0deg); }
  50% { transform: rotateX(180deg); }
  100% { transform: rotateX(0deg); }
}

.rolling .item {
  height: 0;
  margin: 0;
  padding: 0 5px; 
  animation: roll .5s infinite linear;
}

.item.selected {
  animation: roll .4s 2 linear;
}

07. 이름 바꾸기 

# as 키워드 

- import 키워드로 모듈을 불러올 때 as 키워드를 활용하면 import 하는 대상들의 이름 변경 가능 

- 이름을 바꿔서 import하면 여러 파일에서 불러오는 대상들의 이름이 중복되는 문제 해결 가능 

import {title as printerTitle, print} from './printer.js';

const title = 'Codeit';
print(title);

08. 이름 바꿔 붙이기 

- index.js

import { addMenu } from './add.js';
import { deleteMenuByIndex as deleteMenu } from './delete.js';

const data = [];
const addBtn = document.querySelector('.add-btn');
const addInput = document.querySelector('.add-input');
const list = document.querySelector('.list');

addBtn.addEventListener('click', () => addMenu(data));
addInput.addEventListener('keypress', (e) => e.code === 'Enter' && addMenu(data));
list.addEventListener('click', ({ target }) => target.tagName === 'BUTTON' && deleteMenu(data, target.dataset.index));

- delete.js

  - splice(start[, deleteCount[, item1[, item2[, ...]]]]) : 배열의 기존 요소를 삭제 또는 교체하거나 새 요소를 추가하여 원본 배열의 내용을 변경

  - data.splice(index,1) : data 배열 index에 해당하는 위치부터 요소 1개 삭제

const list = document.querySelector('.list');

function reloadMenu(data) {
  list.innerHTML = '';

  data.forEach((title, index) => {
    const li = document.createElement('li');
    li.classList.add('item');
    li.innerHTML = `<b>${index + 1}</b>${title}<button class="del-btn" data-index="${index}">x</div>`;
    list.append(li);
  });
}

export function deleteMenuByIndex(data, index) {
  data.splice(index, 1);
  reloadMenu(data);
};

- style.css / add.js 파일은 06. 메뉴 추가 기능 붙이기 와 동일 


09. 한꺼번에 다루기 

# 한꺼번에 import

- import 할 때 와일드카드 문자(*) 와 as를 활용하면 모듈 파일에서 export하는 모든 대상을 하나의 객체로 불러올 수 있음

import * as printerJS from './printer.js';

# 한꺼번에 export 

-  중괄호로 선언된 변수나 함수를 하나의 객체로 모아 한꺼번에 export 가능  

export {title as printerTitle, print};

10. 태그 정리하기 

- index.html

import { addMenu } from './add.js';
import { deleteMenuByIndex as deleteMenu } from './delete.js';
import {addBtn,addInput,list} from './tag.js';


const data = [];

addBtn.addEventListener('click', () => addMenu(data));
addInput.addEventListener('keypress', (e) => e.code === 'Enter' && addMenu(data));
list.addEventListener('click', ({ target }) => target.tagName === 'BUTTON' && deleteMenu(data, target.dataset.index));

- tag.js

const addBtn = document.querySelector('.add-btn');
const addInput = document.querySelector('.add-input');
const list = document.querySelector('.list');

export {addBtn,addInput,list};

- add.js

import{addInput,list} from './tag.js';

function add(data) {
  const inputValue = addInput.value;
  const index = data.length;
  console.log(inputValue,index);
  const li = document.createElement('li');
  li.classList.add('item');
  li.innerHTML = `<b>${index+1}</b>${inputValue}<button class="del-btn" data-index="${index}">x</div>`;
  list.append(li);

  data.push(inputValue);

  addInput.value = '';
  addInput.focus();
}

function emptyAlert() {
  alert('고민되는 메뉴를 입력해 주세요.');
  addInput.focus();
}

function maxAlert() {
  alert('메뉴는 최대 5개까지만 고민할 수 있습니다.');
  addInput.value = '';
}

// 아래 코드를 수정해 주세요.
export function addMenu(data) {
  const inputValue = addInput.value;

  if (inputValue === '') {
    emptyAlert();
  } else if (data.length > 4) {
    maxAlert();
  } else {
    add(data);
  }
}

- delete.js

import { list } from "./tag.js";

function reloadMenu(data) {
  list.innerHTML = '';

  data.forEach((title, index) => {
    const li = document.createElement('li');
    li.classList.add('item');
    li.innerHTML = `<b>${index + 1}</b>${title}<button class="del-btn" data-index="${index}">x</div>`;
    list.append(li);
  });
}

export function deleteMenuByIndex(data, index) {
  data.splice(index, 1);
  reloadMenu(data);
};

11. default export

# default export 

- 모듈 파일에서 export 대상이 하나일 경우 

const title = 'CodeitPrinter';

function print(value) {
  console.log(value);
}

export default print;

- import 시 default as 로 불러올 수 있음 

import { default as printerJS } from './printer.js';

console.log(printerJS.title); // CodeitPrinter
console.log(printerJS.print); // ƒ print(value) { console.log(value); }

- 아래와 같이 축약형으로 불러오는 것도 가능 

import printerJS from './printer.js';

console.log(printerJS.title); // CodeitPrinter
console.log(printerJS.print); // ƒ print(value) { console.log(value); }

12. 간결하게 기능 붙이기 

- index.js 

  - || 연산자 : 왼쪽 항이 false이면 오른쪽 항을 실행 (if문을 대신함)

import { addBtn, addInput, list, rollBtn } from './tags.js';
import addMenu from './add.js';
import rollMenu from './roll.js';
import deleteMenu from './delete.js';

const data = [];

addBtn.addEventListener('click', () => addMenu(data));
addInput.addEventListener('keypress', (e) => e.code === 'Enter' && addMenu(data));
list.addEventListener('click', ({ target }) => target.tagName === 'BUTTON' && deleteMenu(data, target.dataset.index));
rollBtn.addEventListener('click', () => list.classList.contains('rolling') || rollMenu(data));

-  delete.js 의 export 

export default deleteMenuByIndex;

- add.js의 export

export default addMenu;

- roll.js

import { addInput, list, rollBtn } from './tags.js';

function selectMenu(data) {
  list.classList.remove('rolling');
  list.innerHTML = '';

  const selectedIndex = (Math.floor(Math.random() * data.length));

  const li = document.createElement('li');
  li.classList.add('item', 'selected');
  li.innerHTML = `<b>${selectedIndex + 1}</b>${data[selectedIndex]}`;
  list.append(li);

  rollBtn.textContent = 'Clear';
}

function minAlert() {
  alert('최소 1개 이상의 메뉴를 입력해 주세요.');
  addInput.focus();
}

function clearMenu(data) {
  data.splice(0);
  list.innerHTML = '';
  rollBtn.textContent = 'Roll';
}

function rollMenu(data) {
  if (data.length === 0) {
    minAlert();
  } else if (rollBtn.textContent === 'Clear') {
    clearMenu(data);
  } else {
    list.classList.add('rolling');
    setTimeout(() => selectMenu(data), 1200);
  }
}

export default rollMenu;

13.  복습과 활용 

- 여러 개의 기능으로 잘게 나누어진 모듈을 import한 다음 다시 export하는 모듈 파일 생성 가능 

- 비슷한 특징을 가진 여러 모듈 파일들을 다시 하나의 모듈 파일로 만들 수 있어 파일관리 유용 

// (modules.js)
import module1 from './sub-module1.js';
import module2 from './sub-module2.js';
import module3 from './sub-module3.js';

export { module1, module2, module3 };
// index.js
import { module1, module2, module3 } from 'modules.js';

14. 가져와서 다시 내보내기

- functions.js

import addMenu from "./functions/add.js";
import deleteMenuByIndex from "./functions/delete.js";
import rollMenu from "./functions/roll.js";

export {addMenu,deleteMenuByIndex,rollMenu};

- index.js

import { addBtn, addInput, list, rollBtn } from './tags.js';
import { addMenu,deleteMenuByIndex as deleteMenu,rollMenu } from './functions.js';
const data = [];

addBtn.addEventListener('click', () => addMenu(data));
addInput.addEventListener('keypress', (e) => e.code === 'Enter' && addMenu(data));
list.addEventListener('click', ({ target }) => target.tagName === 'BUTTON' && deleteMenu(data, target.dataset.index));
rollBtn.addEventListener('click', () => list.classList.contains('rolling') || rollMenu(data));

 

profile

DEVELOP

@JUNGY00N