diff --git a/week2/assign2/data/HISTORY_LIST.js b/week2/assign2/data/HISTORY_LIST.js
new file mode 100644
index 0000000..77b8cc9
--- /dev/null
+++ b/week2/assign2/data/HISTORY_LIST.js
@@ -0,0 +1,30 @@
+export const HISTORY_LIST_DATA = [
+ {
+ id: 1,
+ category: "식비",
+ contents: "맥도날드 안암점",
+ type: "expense",
+ amount: 10800,
+ },
+ {
+ id: 2,
+ category: "취미",
+ contents: "하루필름 강남2호점",
+ type: "expense",
+ amount: 5000,
+ },
+ {
+ id: 3,
+ category: "월급",
+ contents: "이번 달 알바비",
+ type: "income",
+ amount: 300000,
+ },
+ {
+ id: 4,
+ category: "쇼핑",
+ contents: "오렌즈 강남역 지하쇼핑역점",
+ type: "expense",
+ amount: 30000,
+ },
+];
diff --git a/week2/assign2/index.html b/week2/assign2/index.html
new file mode 100644
index 0000000..48c253c
--- /dev/null
+++ b/week2/assign2/index.html
@@ -0,0 +1,158 @@
+
+
+
+
+
+
+ 연또의 가계부
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/week2/assign2/reset.css b/week2/assign2/reset.css
new file mode 100644
index 0000000..644951d
--- /dev/null
+++ b/week2/assign2/reset.css
@@ -0,0 +1,133 @@
+/* http://meyerweb.com/eric/tools/css/reset/
+ v2.0 | 20110126
+ License: none (public domain)
+*/
+
+* {
+ box-sizing: border-box;
+}
+
+html,
+body,
+div,
+span,
+applet,
+object,
+iframe,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p,
+blockquote,
+pre,
+a,
+abbr,
+acronym,
+address,
+big,
+cite,
+code,
+del,
+dfn,
+em,
+img,
+ins,
+kbd,
+q,
+s,
+samp,
+small,
+strike,
+strong,
+sub,
+sup,
+tt,
+var,
+b,
+u,
+i,
+center,
+dl,
+dt,
+dd,
+ol,
+ul,
+li,
+fieldset,
+form,
+label,
+legend,
+table,
+caption,
+tbody,
+tfoot,
+thead,
+tr,
+th,
+td,
+article,
+aside,
+canvas,
+details,
+embed,
+figure,
+figcaption,
+footer,
+header,
+hgroup,
+menu,
+nav,
+output,
+ruby,
+section,
+summary,
+time,
+mark,
+audio,
+video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ /* font: inherit; */
+ vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section {
+ display: block;
+}
+body {
+ line-height: 1;
+}
+ol,
+ul {
+ list-style: none;
+}
+blockquote,
+q {
+ quotes: none;
+}
+blockquote:before,
+blockquote:after,
+q:before,
+q:after {
+ content: "";
+ content: none;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
diff --git a/week2/assign2/script.js b/week2/assign2/script.js
new file mode 100644
index 0000000..3dd1896
--- /dev/null
+++ b/week2/assign2/script.js
@@ -0,0 +1,228 @@
+import { HISTORY_LIST_DATA } from "./data/HISTORY_LIST.js";
+
+const $ = (selector) => document.querySelector(selector);
+const $$ = (selector) => document.querySelectorAll(selector);
+
+let INIT_BALANCE = 0;
+let SUM_EXPENSE = 0;
+let SUM_INCOME = 0;
+
+//**** 데이터로부터 초기 렌더링 구현 *****
+// 내역 리스트 렌더링 함수
+const renderHistory = () => {
+ const historyContainer = $(".history__list__container");
+
+ HISTORY_LIST_DATA.forEach((list) => {
+ const { id, category, contents, type, amount } = list;
+
+ const historyBox = document.createElement("li");
+ historyBox.classList.add(`history__list__box`);
+ historyBox.id = id;
+ historyBox.innerHTML = `
+ ${category}
+ ${contents}
+ ${amount.toLocaleString()}
+ `;
+
+ historyContainer.appendChild(historyBox);
+ });
+};
+
+// 총 자산, 수입, 지출 렌더링 함수
+const renderTotalBalance = () => {
+ const incomeAmounts = [...$$(".history-amount.income")].map((history) => {
+ return Number(history.innerHTML.replaceAll(",", ""));
+ });
+ const expenseAmounts = [...$$(".history-amount.expense")].map((history) => {
+ return Number(history.innerHTML.replaceAll(",", ""));
+ });
+
+ SUM_INCOME = incomeAmounts.reduce((sum, curr) => {
+ return sum + curr;
+ }, 0);
+
+ SUM_EXPENSE = expenseAmounts.reduce((sum, curr) => {
+ return sum + curr;
+ }, 0);
+
+ const totalAmount = $(".asset__box__total-amount");
+ totalAmount.innerHTML = (
+ INIT_BALANCE +
+ SUM_INCOME -
+ SUM_EXPENSE
+ ).toLocaleString();
+
+ const totalExpense = $(".detail-amount__num__minus");
+ totalExpense.innerHTML = SUM_EXPENSE.toLocaleString();
+
+ const totalIncome = $(".detail-amount__num__plus");
+ totalIncome.innerHTML = SUM_INCOME.toLocaleString();
+};
+
+// 초기 데이터 렌더링 함수
+const handleRenderInitData = () => {
+ renderHistory(); //내역 리스트와
+ renderTotalBalance(); // 총 수입, 지출, 자산을 데이터로부터 가져와 보여준다
+};
+
+//*******체크 박스 선택에 따른 필터링 구현**********
+const incomeCheckbox = $("#checkbox-income");
+const expenseCheckbox = $("#checkbox-expense");
+
+//리스트 필터 함수
+const filterList = () => {
+ const incomeLists = $$(".history-amount.income");
+ const expenseLists = $$(".history-amount.expense");
+
+ incomeCheckbox.checked
+ ? incomeLists.forEach((list) => (list.parentNode.style.display = "flex"))
+ : incomeLists.forEach((list) => (list.parentNode.style.display = "none"));
+
+ expenseCheckbox.checked
+ ? expenseLists.forEach((list) => (list.parentNode.style.display = "flex"))
+ : expenseLists.forEach((list) => (list.parentNode.style.display = "none"));
+};
+
+//체크 박스 이벤트에 의한 필터링 헨들링 함수
+const handleFilterCheckbox = () => {
+ incomeCheckbox.addEventListener("change", filterList);
+ expenseCheckbox.addEventListener("change", filterList);
+};
+
+//***** 리스트 삭제 구현 *****
+//리스트 삭제 함수
+const delData = (event) => {
+ //이벤트 전파 방지 조건문
+ if (event.target.className === "history-del-btn") {
+ event.target.parentNode.remove(); //리스트 삭제
+ }
+
+ //총 자산에도 반영
+ renderTotalBalance();
+};
+
+// 리스트 삭제 버튼 클릭시 삭제 구현 핸들링 함수
+const handleDelList = () => {
+ const delBtns = $$(".history-del-btn");
+
+ delBtns.forEach(() => addEventListener("click", delData));
+};
+
+// ***** 리스트 추가 모달 구현 *****
+const addListModal = $(".add-list-modal__background");
+
+// 리스트 추가 모달 나타나게 하는 핸들러 함수
+const handleOpenListAddModal = () => {
+ const openBtn = $(".footer__add-btn");
+
+ openBtn.addEventListener("click", () => {
+ addListModal.style.display = "block";
+
+ // 초기 값 세팅
+ $(".add-amounts__input").value = "";
+ $(".add-contents__input").value = "";
+ $(
+ ".add-category__select"
+ ).innerHTML = `
+ `;
+ });
+};
+
+// type 버튼 이벤트 감지 핸들러 함수
+const handleChangeType = () => {
+ const radioInput = $(".add-list-modal__radio-input");
+ radioInput.addEventListener("change", renderOptions);
+};
+
+// select의 option을 type에 따라 렌더해주는 함수
+const renderOptions = (event) => {
+ let targetType = "income"; //default 값
+
+ if (event.target.id === "modal__radio__expense") {
+ targetType = "expense";
+ }
+
+ const categorySelect = $(".add-category__select");
+
+ targetType === "income"
+ ? (categorySelect.innerHTML = `
+
+ `)
+ : (categorySelect.innerHTML = `
+
+ `);
+};
+
+//숫자만 입력하도록 구현하는 함수
+const checkNumber = (event) => {
+ if (isNaN(event.key)) {
+ alert("숫자만 입력하세요");
+ }
+ event.target.value = Number(
+ event.target.value.replace(/[^0-9]/g, "")
+ ).toLocaleString();
+};
+
+// 금액에 숫자만 입력하도록 하는 핸들러 함수
+const handleEnterAmount = () => {
+ const newAmount = $(".add-amounts__input");
+
+ newAmount.addEventListener("keyup", checkNumber);
+};
+
+// 리스트 추가 함수
+const addNewList = () => {
+ const newType = $('input[name="type"]:checked').value;
+
+ const categories = $(".add-category__select");
+ const newCategory = categories.options[categories.selectedIndex].innerHTML;
+
+ const newAmount = $(".add-amounts__input").value;
+
+ const newContents = $(".add-contents__input").value;
+
+ const historyContainer = $(".history__list__container");
+ const historyBox = document.createElement("li");
+ historyBox.classList.add(`history__list__box`);
+ historyBox.innerHTML = `
+ ${newCategory}
+ ${newContents}
+ ${newAmount.toLocaleString()}
+ `;
+
+ if (newCategory && newAmount && newContents) {
+ historyContainer.appendChild(historyBox);
+ alert("저장 되었습니다.");
+ } else {
+ alert("아직 입력되지 않은 항목이 있습니다.");
+ }
+};
+
+// 모달에서 저장 버튼 클릭시 리스트 추사하는 핸들러 함수
+const handleAddList = () => {
+ const saveBtn = $(".add-list-modal__save-btn");
+
+ saveBtn.addEventListener("click", addNewList);
+};
+
+// 모달을 닫아주는 핸들러 함수
+const handleCloseListAddModal = () => {
+ const closeBtn = $(".add-list-modal__close-btn");
+ closeBtn.addEventListener("click", () => {
+ addListModal.style.display = "none";
+ });
+};
+
+// ***** 최종 실행 함수들 *****
+handleRenderInitData();
+
+handleFilterCheckbox();
+
+handleDelList();
+
+handleOpenListAddModal();
+handleChangeType();
+handleAddList();
+handleCloseListAddModal();
+
+handleEnterAmount();
diff --git a/week2/assign2/style.css b/week2/assign2/style.css
new file mode 100644
index 0000000..0dbb328
--- /dev/null
+++ b/week2/assign2/style.css
@@ -0,0 +1,442 @@
+@import url("./reset.css");
+
+@font-face {
+ font-family: "DungGeunMo";
+ src: url("https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_six@1.2/DungGeunMo.woff")
+ format("woff");
+ font-weight: normal;
+ font-style: normal;
+}
+
+body {
+ display: flex;
+ flex-direction: column;
+
+ /* 전체 스크롤 방지 */
+ overflow: hidden;
+ touch-action: none;
+
+ font-family: "DungGeunMo";
+}
+
+/* 버튼 기본 스타일링 제거, 포인터 설정*/
+button {
+ border: none;
+ background-color: transparent;
+
+ cursor: pointer;
+}
+
+/*전역 적용*/
+.income {
+ color: blue;
+}
+
+.expense {
+ color: red;
+}
+
+/*header*/
+.header {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ width: 100%;
+ height: 3.5rem;
+
+ background-color: black;
+ color: white;
+
+ font-size: 2rem;
+}
+
+/* 나의 자산 */
+.asset {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ padding: 1rem 2rem;
+ border: solid lightgray;
+ border-width: 0.2rem 0;
+}
+
+.asset__box {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+
+ width: 100%;
+ height: fit-content;
+ padding: 1rem 0;
+ gap: 1.2rem;
+
+ border: 0.3rem solid black;
+ border-radius: 0.5rem;
+
+ background-color: #ddd;
+}
+
+.asset__box__title {
+ font-size: 1.8rem;
+}
+
+.asset__box__total-amount {
+ font-size: 1.5rem;
+}
+
+.asset__box__total-amount::before {
+ content: "💲 ";
+
+ font-size: 1.3rem;
+}
+
+.asset__box__detail-amount {
+ display: flex;
+ gap: 3rem;
+
+ font-size: 1.4rem;
+}
+
+.detail-amount__plus,
+.detail-amount__minus {
+ display: flex;
+ gap: 0.5rem;
+}
+
+.amount-sign {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ width: 1.5rem;
+ height: 1.5rem;
+
+ border-radius: 1rem;
+
+ background-color: white;
+}
+
+.history {
+ display: flex;
+ flex-direction: column;
+
+ width: 100%;
+}
+
+.history__top {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ padding: 0.5rem 0;
+
+ font-size: 1.3rem;
+}
+
+.history__top > button {
+ font-size: 1.5rem;
+}
+
+.history__top > h2 {
+ padding: 0 2rem;
+}
+
+.history__detail {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ padding: 0.5rem 2rem;
+
+ font-size: 1rem;
+
+ border-bottom: 0.1rem solid lightgray;
+}
+
+.history__detail__checkbox {
+ display: flex;
+ gap: 1rem;
+}
+
+/* checkbox input 커스텀 */
+input[type="checkbox"],
+input[type="radio"] {
+ display: none;
+}
+
+input[id="checkbox-income"] + label,
+input[id="checkbox-expense"] + label {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ width: 5rem;
+ height: 2rem;
+
+ border-radius: 0.5rem;
+
+ cursor: pointer;
+}
+
+input[id="checkbox-income"] + label {
+ background-color: transparent;
+ border: 0.1rem solid blue;
+}
+input[id="checkbox-income"]:checked + label {
+ background-color: blue;
+ color: white;
+}
+
+input[id="checkbox-expense"] + label {
+ background-color: transparent;
+ border: 0.1rem solid red;
+}
+input[id="checkbox-expense"]:checked + label {
+ background-color: red;
+ color: white;
+}
+
+/* 내역 리스트 영역 */
+
+/*리스트 내 세로 스크롤*/
+.history__list {
+ height: 42vh;
+ overflow-y: scroll;
+
+ margin-bottom: 3rem;
+}
+
+.history__list > ul {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+
+ width: 100%;
+ padding: 1rem 2rem 0 2rem;
+ gap: 1rem;
+}
+
+.history__list__box {
+ display: flex;
+ align-items: center;
+
+ width: 100%;
+ height: 3.5rem;
+ padding: 1rem 1.5rem;
+
+ background-color: #a6b7da;
+ color: white;
+
+ border-radius: 0.5rem;
+}
+
+.history-category {
+ width: fit-content;
+ padding-right: 1.5rem;
+
+ color: #444;
+
+ font-size: 0.8rem;
+ text-align: center;
+}
+
+.history-contents {
+ width: 8.5rem;
+
+ /* 말줄임표 */
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+
+ color: #242526;
+
+ font-size: 1rem;
+}
+.history-amount {
+ width: fit-content;
+ padding-right: 0.5rem;
+ margin-left: auto;
+
+ font-size: 1.1rem;
+}
+
+/*수입, 지출에 따라 부호 붙여주기*/
+.history__list__box > .income::before {
+ content: "+";
+}
+
+.history__list__box > .expense::before {
+ content: "-";
+}
+
+.history-del-btn {
+ width: 1.3rem;
+ height: 1.3rem;
+ border-radius: 0.7rem;
+
+ font-size: 0.5rem;
+
+ color: #242526;
+}
+
+.history-del-btn:hover {
+ background-color: white;
+}
+
+/* footer 영역 */
+.footer {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ position: fixed;
+ bottom: 0;
+
+ width: 100%;
+ height: 3rem;
+
+ background-color: #ddd;
+}
+
+.footer__add-btn {
+ width: 2rem;
+ height: 2rem;
+
+ background-color: #a6b7da;
+ color: white;
+
+ border-radius: 1rem;
+
+ font-size: 1.5rem;
+}
+
+/*내역 추가 모달*/
+@keyframes fadeInUp {
+ 0% {
+ opacity: 0.5;
+ transform: translate3d(0, 100%, 0);
+ }
+ to {
+ opacity: 1;
+ transform: translateZ(0);
+ }
+}
+
+.add-list-modal__background {
+ position: absolute;
+ top: 0;
+ left: 0;
+
+ display: none;
+
+ width: 100%;
+ height: 100dvh;
+ background-color: rgba(0, 0, 0, 0.4);
+ z-index: 10;
+}
+
+.add-list-modal {
+ position: absolute;
+ bottom: 0;
+
+ display: flex;
+ flex-direction: column;
+
+ width: 100%;
+ height: 70%;
+ gap: 1.3rem;
+ padding: 1.3rem 2rem;
+
+ background-color: #a6b7da;
+ color: black;
+
+ z-index: 100;
+
+ animation: fadeInUp 0.5s;
+}
+
+.add-list-modal__header {
+ align-self: center;
+
+ font-size: 1.7rem;
+
+ color: white;
+}
+
+.add-list-modal__radio-input {
+ display: flex;
+ justify-content: center;
+
+ gap: 2rem;
+}
+
+input[id="modal__radio__income"] + label,
+input[id="modal__radio__expense"] + label {
+ padding: 0.5rem 2.7rem;
+
+ border-radius: 0.5rem;
+
+ font-size: 1.3rem;
+
+ background-color: whitesmoke;
+}
+
+input[id="modal__radio__income"]:checked + label,
+input[id="modal__radio__expense"]:checked + label {
+ background-color: grey;
+ color: white;
+}
+
+.add-list-modal__category,
+.add-list-modal__contents,
+.add-list-modal__amounts {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+
+ width: 100%;
+}
+
+.add-category__title,
+.add-contents__title,
+.add-amounts__title {
+ font-size: 1.3rem;
+ width: 100%;
+}
+
+.add-category__select {
+ width: 100%;
+ padding: 0.5rem;
+
+ background: whitesmoke;
+ border: 0.1rem solid #999;
+ border-radius: 0.5rem;
+
+ -webkit-appearance: none; /* 네이티브 외형 감추기 */
+ -moz-appearance: none;
+ appearance: none;
+}
+
+.add-contents__input,
+.add-amounts__input {
+ width: 100%;
+ padding: 0.5rem;
+
+ border: none;
+ border-radius: 0.5rem;
+}
+
+.add-list-modal__save-btn,
+.add-list-modal__close-btn {
+ align-self: center;
+
+ width: 100%;
+ padding: 0.5rem;
+
+ border-radius: 0.5rem;
+
+ background-color: whitesmoke;
+}