1. 01, 모듈이란?
# 모듈
: 간단하게, 자바스크립트 파일 하나
# 모듈화
: 복잡하고 많은 양의 코드를 기능에 따라 각각의 파일(모듈)로 나누어 관리
- 더 효율적인 코드 관리 가능
- 비슷한 기능이 필요할 때 다른 프로그램에서 재사용 가능
2. 02. 모듈 파일의 조건
# 모듈 스코프
: 모듈 파일이 가지는 독립적인 스코프
- 모듈 파일 내에서 선언한 함수나 변수는 모듈 파일 내에서만 사용이 가능
- HTML 파일에서 JS 파일을 불러올 때 모듈 스코프를 갖게 하려면 script 태그에 type 속성을 module로 지정해줘야 함
<html />
<body>
<script type="module" src="index.js"></script>
<script type="module" src="printer.js"></script>
</body>
3. 03. Live Server 설치하기
- 모듈 문법을 활용할 때에는 브라우저에서 직접 파일을 불러오는 방식이 아니라 서버를 통해 html 파일을 실행해야 함
=> VS code 에서 extentions -> live server 설치 -> go live 버튼
4. 05. 모듈 문법
# export 키워드
- 모듈 스코프를 가진 파일에서 외부로 내보내고자 하는 변수나 함수를 export 키워드를 통해 내보냄
# import 키우드
- 모듈 파일에서 내보낸 변수나 함수들은 다른 파일에서 import 키워드를 통해 가져옴
<javascript />
export const title = 'CodeItPrinter';
export function print(value){
console.log(value);
}
<javascript />
import {title, print} from './printer.js';
print(title);
- 모듈화 된 모듈은 모듈 문법을 통해 서로 연결될 수 밖에 없기 때문에 html 파일에서는 js의 진입점 역할을 하는 파일 하나만 불러옴
<html />
<script type="module" src="index.js"></script>
06. 메뉴 추가 기능 붙이기
- index.js
- && 연산자 : 왼쪽이 true면 오른쪽 항 실행 (if문을 대신함)
<javascript />
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
<javascript />
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
<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
<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;
}
5. 07. 이름 바꾸기
# as 키워드
- import 키워드로 모듈을 불러올 때 as 키워드를 활용하면 import 하는 대상들의 이름 변경 가능
- 이름을 바꿔서 import하면 여러 파일에서 불러오는 대상들의 이름이 중복되는 문제 해결 가능
<javascript />
import {title as printerTitle, print} from './printer.js';
const title = 'Codeit';
print(title);
6. 08. 이름 바꿔 붙이기
- index.js
<javascript />
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개 삭제
<javascript />
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. 메뉴 추가 기능 붙이기 와 동일
7. 09. 한꺼번에 다루기
# 한꺼번에 import
- import 할 때 와일드카드 문자(*) 와 as를 활용하면 모듈 파일에서 export하는 모든 대상을 하나의 객체로 불러올 수 있음
<javascript />
import * as printerJS from './printer.js';
# 한꺼번에 export
- 중괄호로 선언된 변수나 함수를 하나의 객체로 모아 한꺼번에 export 가능
<javascript />
export {title as printerTitle, print};
8. 10. 태그 정리하기
- index.html
<javascript />
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
<javascript />
const addBtn = document.querySelector('.add-btn');
const addInput = document.querySelector('.add-input');
const list = document.querySelector('.list');
export {addBtn,addInput,list};
- add.js
<javascript />
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
<javascript />
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);
};
9. 11. default export
# default export
- 모듈 파일에서 export 대상이 하나일 경우
<javascript />
const title = 'CodeitPrinter';
function print(value) {
console.log(value);
}
export default print;
- import 시 default as 로 불러올 수 있음
<javascript />
import { default as printerJS } from './printer.js';
console.log(printerJS.title); // CodeitPrinter
console.log(printerJS.print); // ƒ print(value) { console.log(value); }
- 아래와 같이 축약형으로 불러오는 것도 가능
<javascript />
import printerJS from './printer.js';
console.log(printerJS.title); // CodeitPrinter
console.log(printerJS.print); // ƒ print(value) { console.log(value); }
10. 12. 간결하게 기능 붙이기
- index.js
- || 연산자 : 왼쪽 항이 false이면 오른쪽 항을 실행 (if문을 대신함)
<javascript />
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
<javascript />
export default deleteMenuByIndex;
- add.js의 export
<javascript />
export default addMenu;
- roll.js
<javascript />
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;
11. 13. 복습과 활용
- 여러 개의 기능으로 잘게 나누어진 모듈을 import한 다음 다시 export하는 모듈 파일 생성 가능
- 비슷한 특징을 가진 여러 모듈 파일들을 다시 하나의 모듈 파일로 만들 수 있어 파일관리 유용
<javascript />
// (modules.js)
import module1 from './sub-module1.js';
import module2 from './sub-module2.js';
import module3 from './sub-module3.js';
export { module1, module2, module3 };
<javascript />
// index.js
import { module1, module2, module3 } from 'modules.js';
14. 가져와서 다시 내보내기
- functions.js
<javascript />
import addMenu from "./functions/add.js";
import deleteMenuByIndex from "./functions/delete.js";
import rollMenu from "./functions/roll.js";
export {addMenu,deleteMenuByIndex,rollMenu};
- index.js
<javascript />
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));
'FRONTEND > JavaScript' 카테고리의 다른 글
[코드잇 - 자바스크립트 객체 지향 기본기] 2. 객체지향 프로그래밍의 4개의 기둥 : 추상화, 캡슐화, 상속, 다형성 (0) | 2022.11.22 |
---|---|
[코드잇 - 자바스크립트 객체 지향 기본기] 1. 객체와 클래스 (0) | 2022.11.22 |
[코드잇 - 모던 자바스크립트] 05. 자바스크립트의 유용한 내부 기능 (0) | 2022.11.21 |
[코드잇 - 모던 자바스크립트] 04. 자바스크립트의 문법과 표현 (0) | 2022.11.17 |
[코드잇 - 모던 자바스크립트] 03. 함수 다루기 (0) | 2022.11.16 |