๐ ๋ฐฐํฌURL
- Music Drop์ ์์ ์ ๋ค์ ์ ์๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋๋ค.
- open API (deezer)๋ฅผ ํ์ฉํ์ฌ ๋ค์ํ ์์ ์ ๊ฒ์ ๋ฐ ์ถ๊ฐ ํ ์ ์๊ณ 30์ด ๋ฏธ๋ฆฌ๋ฃ๊ธฐ๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ๊ตญ๋ด, ํด์ธ์ฐจํธ ์์๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
- ๋๋ง์ ํ๋ ์ด๋ฆฌ์คํธ๋ฅผ ๋ง๋ค์ด ์ํ๋ ์์ ์ ๋ค์ ์ ์์ต๋๋ค.
- ํ๋ ์ด๋ฆฌ์คํธ์ ๋ด๊ธด ์์ ์ ๊ธฐ๋ฐ์ผ๋ก ๋๋ค ์ฌ์, ํ๊ณก ๋ฐ๋ณต, ๋ค์๊ณก/์ด์ ๊ณก ์ฌ์ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
- ๋ชจ๋ ๊ธฐ๋ฅ๊ตฌํ์ VSC live share๋ฅผ ํ์ฉํ์ฌ ํ์ด ํ๋ก๊ทธ๋๋ฐ์ผ๋ก ์งํํ์ต๋๋ค.
2023.01.19 - 2023.03.31
1. ํ๋ก์ ํธ ์๊ฐ
2. ํ์ ์๊ฐ
3. ๊ฐ๋ฐ ํ๊ฒฝ
4. ๊ตฌํ ๊ธฐ๋ฅ
5. ํ๋ก์ ํธ ๊ตฌ์กฐ
6. ํธ๋ฌ๋ธ ์ํ
7. ํ๋ก์ ํธ ์งํ ์ค ์๋ก ์๊ฒ ๋ ๊ฒ๋ค
8. ํ๋ก์ ํธ ๋ฐฐํฌ ํ ๋๋ ์
๊น๋ค์ | ์ ์ํ | ์กฐ๋ฏผ์ง |
---|---|---|
DADA6041 | IntHyun | ming-Jo |
- Front-End
- Back-End
- Design
-
- GitHub Issues : ํ๋ ๋ด์ญ์ ๋ํด ์ด์ ๋ฑ๋ก ํ ์์ ์งํ
- VScode liveshare : ํ์ด ํ๋ก๊ทธ๋๋ฐ์ผ๋ก ์งํ ๋ฐ ์ฝ๋ ๋ฆฌ๋ทฐ
- Discord : ์ค์๊ฐ ์ํต ๋ฐ ํ๋ฉด ๊ณต์
-
ํ๋ ๋ด์ญ์ ๋ํด ์ด์ ๋ฑ๋ก ํ ์์ ์งํ
โจ Feat: ์๋ก์ด ๊ธฐ๋ฅ ์ถ๊ฐ
๐ท๏ธ MarkUp: ๋งํฌ์
๐ Design : CSS, ์ฌ์ฉ์ UI ๋์์ธ ๋ณ๊ฒฝ
๐ Docs : ๋ฌธ์ ์์
๐จ Style : ์ฝ๋ํฌ๋งท, ์ธ๋ฏธ์ฝ๋ก , ๊ฐํ, ์ฝ๋ ๊ตฌ์กฐ, ํํ
๐ค Test : ํ
์คํธ ์ฝ๋
โป๏ธ Refactor: ์ฝ๋ ๋ฆฌํฉํ ๋ง
๐ Fix : ๋ฒ๊ทธ ๋ฐ ์ค๋ฅ ์์
๐ฅ Remove : ๋ถํ์ํ ํ์ผ ์ญ์
๐จ Chore : ๋น๋ ์
๋ฌด, ํจํค์ง๋งค๋์ , ํด๋ํธ๋ฆฌ, ์ธํ
์์
๐ก Comment : ํ์ํ ์ฃผ์ ์ถ๊ฐ ๋ฐ ๋ณ๊ฒฝ
Co-authored-by: DADA6061 <[email protected]>
Co-authored-by: ming-Jo <[email protected]>
Co-authored-by: IntHyun <[email protected]>
Splash | Home | Chart |
---|---|---|
![]() |
![]() |
![]() |
Playlist | Search(๊ธฐ๋ณธ) | Search(๊ฒฐ๊ณผ ์์) |
---|---|---|
![]() |
![]() |
![]() |
Music Control | Not Found |
---|---|
![]() |
![]() |
๐ฆfrontend
โฃ ๐static
โ โฃ ๐css
โ โ โฃ ๐chart.css
โ โ โฃ ๐home.css
โ โ โฃ ๐myplaylist.css
โ โ โฃ ๐navbar.css
โ โ โฃ ๐playcontrol.css
โ โ โฃ ๐reset.css
โ โ โฃ ๐search.css
โ โ โ ๐style.css
โ โฃ ๐image
โ โ ๐js
โ โ โฃ ๐API
โ โ โ โฃ ๐API.js
โ โ โ โ ๐apikey.js
โ โ โฃ ๐json
โ โ โ โ ๐memberData.json
โ โ โฃ ๐views
โ โ โ โฃ ๐AbstractView.js
โ โ โ โฃ ๐Chart.js
โ โ โ โฃ ๐Home.js
โ โ โ โฃ ๐MyplayList.js
โ โ โ โฃ ๐NotFound.js
โ โ โ โฃ ๐PlayControl.js
โ โ โ โฃ ๐Search.js
โ โ โ โ ๐Splash.js
โ โ โ ๐index.js
โฃ ๐index.html
โ ๐_redirects
-
๋ฌธ์ ๋ฐ ์์ธ : createElement๋ก ์์๋ฅผ ์์ฑํ๊ณ appendChild๋ก ์์๋ฅผ ์ถ๊ฐํ๊ณ ์์ด์ ๋ฉ์๋๊ฐ ์คํ๋ ๋ ๋ง๋ค ์์๊ฐ ์์ฑ, ์ถ๊ฐ๋จ
-
ํด๊ฒฐ :
-
(์ฐธ๊ณ ) ๊ฐ ํ์ด์ง์๋ root ์์ ์์ main ์ปจํ ์ด๋ ์์๊ฐ ๊ณตํต์ ์ผ๋ก ์กด์ฌํจ
-
1์ฐจ ํด๊ฒฐ
-
ํด๋ฆญํ ํ์ด์ง์ main wrapper์ ์์๋ฅผ hiddenํ๋ class๋ฅผ ์ถ๊ฐ
-
๋ผ์ฐํ ํ hidden๋ ์์๊ฐ DOM์ ์กด์ฌํ์ง ์์ ์๋ฌ ๋ฐ์
-
-
2์ฐจ ํด๊ฒฐ
-
removeElement()
ํจ์๋ฅผ ์์ฑํ์ฌ ํ์ด์ง ์ด๋ ์ ์ง์ ํ์ด์ง์ ์์๋ฅผ ์ ๊ฑฐํ๋ ๋ก์ง ๊ตฌํ -
๋ค๋ก๊ฐ๊ธฐ ๋ฒํผ ํด๋ฆญ ์ ์ ๊ฑฐ๋์๋ ์์๊ฐ ๋ค์ ์ถ๊ฐ๋๋ ํ์ ๋ฐ์
-
-
3์ฐจ ํด๊ฒฐ(์๋ฃ)
-
๊ฐ ํ์ด์ง์ main wrapper๋ฅผ
appendChild
๊ฐ ์๋replaceChildren
์ผ๋ก ํ์ด์ง ์ด๋ ์ ์์ ๋์ฒด -
replaceChildren MDN ์ค๋ช ์ฐธ๊ณ
// ๊ธฐ์กด appendChild const wrapper = document.createElement("main"); wrapper.classList.add("wrapper"); this.$target.appendChild(wrapper); // ์์ replaceChildren const wrapper = document.createElement("main"); wrapper.classList.add("wrapper"); this.$target.replaceChildren(wrapper);
-
-
-
๋ฌธ์ ๋ฐ ์์ธ : ์์ ์ ์ฌ์ํ ๋ ์ฌ์๋ฐ๋ฅผ ๋๋๊ทธ ํ๋ฉด ๋๋๊ทธํ ์์น๋ฅผ ์ ์์ ์ผ๋ก ํ์ํ์ง ์์
-
ํด๊ฒฐ : input์ range๋
click
์ด ์๋๋ผinput
event๋ก ์ปจํธ๋กค ํด์ผํ๋ค.// click ์ด๋ฒคํธ๋ก ํ์ ๋ (๋๋๊ทธ ์์น ํ์ X) $progressBar.addEventListener("click", (e) => { let progressInputValue = e.target.value; let songDuration = $audio.duration; $audio.currentTime = (progressInputValue * songDuration) / 100; }); // input ์ด๋ฒคํธ๋ก ํ์ ๋ (๋๋๊ทธ ์์น ํ์ O) $progressBar.addEventListener("input", (e) => { let progressInputValue = e.target.value; let songDuration = $audio.duration; $audio.currentTime = (progressInputValue * songDuration) / 100; });
-
๋ฌธ์ ๋ฐ ์์ธ : ํ์ดํ ํจ์ ๋ด๋ถ์์ this๊ฐ ์ง์นญํ๋ ๋์์ด ๋ฌ๋ผ์ ๋ถ๋ชจ Class์ ๋ฉ์๋๊ฐ ์๋ํ์ง ์์
-
ํด๊ฒฐ :
this.method()
๊ฐ ์๋super.method()
๋ก ๋ถ๋ชจ Class์ prototype-method๋ฅผ ์ฐธ์กฐํ๊ธฐsetLocalData() { const btn = document.querySelectorAll(".music-add-btn"); btn.forEach((button) => { button.addEventListener("click", (event) => { super.toast(); let id = Number(event.currentTarget.dataset.id); let title = event.currentTarget.dataset.title; let coverImg = event.currentTarget.dataset.cover; let artist = event.currentTarget.dataset.artist; // ๊ธฐ์กด์ ์์ฑํ๋ this๋ ์์ ํด๋์ค์ setLocalStorage()๋ฅผ ์ฐพ์ง๋ชปํจ. // this.setLocalStorage(id, title, coverImg, artist); super.setLocalStorage(id, title, coverImg, artist); }); }); }
-
๋ฌธ์ : anchor ํ๊ทธ์ ์๋ก๊ณ ์นจ์ ๋ฐฉ์งํ๋ฉฐ ํ์ด์ง๋ฅผ ์ด๋ํ๊ธฐ ์ํด anchor ํ๊ทธ์ ์ ๊ทผํ๋ ๊ณผ์ ์์ anchor ํ๊ทธ๋ฅผ ์ฐพ์ง ๋ชปํ๋ ๋ฌธ์ ๋ฐ์
-
์์ธ : createElement๋ก ๋ชจ๋ ์์๋ค์ ์์ฑํ์๋๋ฐ ๋น๋๊ธฐ ํต์ ์ผ๋ก ์ธํด ์์๊ฐ ์์ฑ๋๊ธฐ ์ DOM์ ์ ๊ทผํ์ฌ ํด๋น ์์๋ฅผ ์ฐพ์ง ๋ชปํจ
-
ํด๊ฒฐ : ๋น๋๊ธฐ ํต์ ์ดํ ๋ณ๊ฒฝ๋ DOM ๋ฐ์ดํฐ๋ฅผ ๊ฐ์งํ๊ธฐ ์ํด MutationObserver๋ก ์์ฑ๊ณผ ์์๋ ธ๋, ์์๋ ธ๋์ ๋ณ๊ฒฝ๋๋ ์์๊ฐ ์๋์ง ๊ฐ์งํ ๋ค ํด๋น ์์์ ์ ๊ทผํ์ฌ event๋ฅผ ์ ์ฉ
let target; if (location.pathname === "/") { target = document.querySelector(".home-wrapper"); } else if (location.pathname === "/search") { target = document.querySelector(".searchlist-main"); } else if (location.pathname === "/chart") { target = document.querySelector(".chart-main"); } if (target) { const callback = () => { const $playControl = target.querySelectorAll(".to-play-control"); $playControl.forEach((url) => { url.addEventListener("click", (e) => { e.preventDefault(); navigateTo(e.currentTarget.href); }); }); }; const observer = new MutationObserver(callback); const config = { attributes: true, // ์์ฑ ๋ณํ ํ ๋ ๊ฐ์ง childList: true, // ์์๋ ธ๋ ์ถ๊ฐ/์ ๊ฑฐ ๊ฐ์ง subtree: true, // ์์๋ ธ๋๊น์ง ์ถ๊ฐ/์ ๊ฑฐ ๊ฐ์ง characterData: true, // ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ๋ด์ฉ ๊ธฐ๋ก }; observer.observe(target, config); }
-
Promise ๊ฐ์ฒด๋ฅผ ์์ฑํ์ฌ ๋ถ๋ฌ์จ ๋ฐ์ดํฐ๋ฅผ ๋ ๋๋ง ํ๊ธฐ ์ ๋ก๋ฉํ๋ฉด ๋์ฐ๊ธฐ
new Promise((resolve) => { const contCategory = data .map((list, idx) => { return ` ... ํ์ด์ง ์์ ์์ฑํ๋ ์ฝ๋ ... `; }) .join(""); resolve(/* ์์ฑ๋๊ณ ๋ ๋ค ๋๊ฒจ์ค ๋ฐ์ดํฐ */); }) .then((/* resolve์์ ๋ฐ์ ๋ฐ์ดํฐ */) => { // ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๊ณ ๋ ๋ค ์คํํ ์ฝ๋ }) .catch((error) => { throw new Error(error); });
-
scroll method ํน์ ์์น๋ก ์คํฌ๋กค ํ๊ธฐ
-
scrollBy
๋left
,top
,behavior
ํ๋กํผํฐ๋ฅผ ๊ฐ์ง -
left
๋ x์ถ /top
์ y์ถ ์ด๋ -
behavior
- auto(๊ธฐ๋ณธ๊ฐ) : ๋ธ๋ผ์ฐ์ ๊ฐ ์ ํ
- smooth : ๋ถ๋๋ฌ์ด ์คํฌ๋กค ์ ๋๋ฉ์ด์
- instant : ํ ๋ฒ์ ์ ํ๋ก ์ฆ์ ์คํ
-
Home ํ๋ฉด ๊ฐ๋ก ์คํฌ๋กค ๋ฒํผ ๊ตฌํ ์ ์ฌ์ฉ
horizontalScroll() { const $leftBtns = document.querySelectorAll(".left-scroll-button"); const $rightBtns = document.querySelectorAll(".right-scroll-button"); $leftBtns.forEach((item) => { item.addEventListener("click", () => { const contAlbum = item.previousElementSibling; if (contAlbum.scrollLeft + contAlbum.offsetWidth >= contAlbum.scrollWidth) { contAlbum.scrollBy({ left: -290, top: 0, behavior: "smooth" }); } else { contAlbum.scrollBy({ left: -260, top: 0, behavior: "smooth" }); } }); }); $rightBtns.forEach((item) => { item.addEventListener("click", () => { const contAlbum = item.previousElementSibling.previousElementSibling; contAlbum.scrollBy({ left: 260, top: 0, behavior: "smooth" }); }); }); }
-
css filter ์์ฑ์ผ๋ก asset ์ถ๊ฐ ์์ด ์ด๋ฏธ์ง ์ปฌ๋ฌ ๋ณ๊ฒฝํ๊ธฐ
- ๋ด๋น๊ฒ์ด์ ์์ด์ฝ active ํจ๊ณผ ๊ตฌํ
.nav-list-item.active { filter: invert(63%) sepia(51%) saturate(3249%) hue-rotate(136deg) brightness(93%) contrast(97%); }
const menuItems = document.querySelectorAll(".nav-list-item"); let currentActive; menuItems.forEach((item) => { const itemHref = item.querySelector("a").getAttribute("href"); if (location.pathname === "/") { if (itemHref === "/") { currentActive = item; currentActive.classList.add("active"); } } else if (location.pathname.includes(itemHref) && itemHref.length !== 1) { currentActive = item; currentActive.classList.add("active"); } });
- ๋ฉ์ฌ ๊ณผ์ ์ด ๋๋๊ณ ์ผ๋จ ์ด๋ ฅ์์ ์ธ ๊ฒฐ๊ณผ๋ฌผ์ ๋ ๋ง๋ค์ด์ผ ํ๋ค๋ ์๊ฐ์ ์์ํ ํ๋ก์ ํธ์ง๋ง ๊ธฐ์ ์ ์ธ ๊ฒ๋ฟ ์๋๋ผ ์ํตํ๋ ๋ฐฉ๋ฒ๊น์ง ๋๋ฌด ๋ง์ ๊ฒ์ ๋ฐฐ์ฐ๊ณ ์ป์ด ๊ฐ๋ ์๊ฐ์ด์์ต๋๋ค. ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ ์ฒซ ํ์ด ํ๋ก๊ทธ๋๋ฐ์ ์ด๊ธฐ ์ธํ ๋ฑ ๋งค๋ฒ ๊ณ ๋น๊ฐ ์์ง๋ง ์ด๋์ ์๋ํ๊ณ ์ถ์ ์ ์ ๋ฉ์ธ ํ๋ก์ ํธ์ ๋๋ค. ์ด๋ ค์ด ๋ถ๋ถ์ด ๋์ฌ ๋๋ง๋ค ๋ชจ๋ ์ ๊ทน์ ์ผ๋ก ๋์๊ณ ๊ฐ์ด ํด๊ฒฐํ๋ ค๊ณ ๋ ธ๋ ฅํ ๋์ ์ํํ๊ฒ ์ ๋ง๋ฌด๋ฆฌํ๊ฒ ๋ ๊ฒ ๊ฐ๊ณ ์์ผ๋ก๋ ๋ ์ด์ฌํ ํด์ผ๊ฒ ๋ค๋ ์๋๋ ฅ๋ ์ป์๊ณ ์์ ๊ฐ๋ ์ป์ด ๊ฐ๊ฒ ๋์์ต๋๋ค. :) ์ ํด์ง ๋ง๊ฐ๊ธฐํ์ด ์์ง๋ ์์ ๋ฐ๋ค ํ๋ฃจ 2์๊ฐ, 2๋ฌ์ด๋ ๊ธฐ๊ฐ์ด ๊ธธ๋ค๋ฉด ๊ธธ๊ณ ์งง์ผ๋ฉด ์งง๋ค๊ณ ๋๋ผ๊ฒ ์ง๋ง ํฌ๊ธฐํ์ง ์๊ณ ๊พธ์คํ ๊ฐ์ด ์งํํด ์ค ํ์๋ค ๊ฐ์ฌํ๊ณ ๋๋ฌด ๊ณ ์ ๋ง์ผ์ จ์ต๋๋ค!โค๏ธ
- ํ๋ก์ ํธ๋ฅผ ์์ํ๊ธฐ ์ ์๋ ๊ฐ์ฒด์งํฅ์ ๋ํด ๋ชจ๋ฅด๋ ๊ฒ์ด ๋ง์ ์ํ์๋๋ฐ Class๋ฌธ๋ฒ์ ์ค์ ๋ก ์ฌ์ฉํด๋ณด๊ณ ํน์ ๋ฉ์๋๋ฅผ ์์ Class์ ์ ์ฉํด ์ฌํ์ฉ๋ ํด๋ณด๋ฉด์ Class๋ฌธ๋ฒ์ด ์ข ๋ ์ต์ํด์ง ๊ฒ ๊ฐ์ต๋๋ค. ๋ํ ์ด์ ์๋ ์์ง ๋ชปํ๋ css์ filter์์ฑ, scrollBy ๋ฉ์๋, ๋์ ์ผ๋ก DOM์ ๊ฐ์งํ ์ ์๋ MutationObserver ๋ฑ์ ๋ํด ๋ฐฐ์ ๊ณ netlify๋ฅผ ํตํด ์ง์ ๋ฐฐํฌ๋ฅผ ํ๊ณ github Releasese๋ก ๋ฒ์ ๊ด๋ฆฌ๋ ํด๋ณด๋ฉด์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ด๋ฐ์์ผ๋ก ๋ฒ์ ๊ด๋ฆฌ๊ฐ ๋๊ณ ๋ฐฐํฌ๊ฐ ๋๋๊ตฌ๋์ ๋ํด ์ข ๋ ์์ธํ๊ฒ ์๊ฒ ๋ ๊ฒ ๊ฐ์ต๋๋ค. ์ด๋ฒ ํ๋ก์ ํธ๋ฅผ ํตํด JavaScript์ ๋ํด ๋ ๋ง์ด ์๊ฒ ๋์๊ณ ์ดํ์๋ ๋ค์ํ ํ๋ก์ ํธ๋ฅผ ํตํด ์ง์์ ํ์ฅํด๋๊ฐ๊ณ ์ถ๋ค๋ ์๊ฐ์ด ๋ค์์ต๋๋ค.
- ๋ฐ๋๋ผ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ๊ตฌํํ๋ ๊ณผ์ ์ด ์๊ฐ๋ณด๋ค ์ฝ์ง ์์์ง๋ง ๋ค์ ๋, ์ํ๋๊ณผ ํจ๊ป์ฌ์ ์ฒ์์ ์๊ฐํ๋ ๊ฒ๋ณด๋ค ๋ ๋ฉ์ง๊ฒ ํ๋ก์ ํธ๋ฅผ ์์ฑํ ์ ์์์ต๋๋ค! ๊ตฌํํ๋ฉด์ ๋งํ๋ ๋ถ๋ถ์ด ์์ ๋ '์ผ๋จ ๋ญ๋ ํด๋ณผ๊น์?' ๋ผ๋ ๋ง์ธ๋๋ก ๋ค๊ฐ์ด ์ฐ๋นํํ ํด๋๊ฐ๋ ๊ณผ์ ์ด ๋๋ฌด ์ฌ๋ฐ์์ด์๐ ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ ํ์์ ์ด๋ ต๋ค๊ณ ์๊ฐํ๋ ํด๋์ค ๋ฌธ๋ฒ, ๋น๋๊ธฐ ํจํด๊ณผ ๋ง์ฃผํ๊ฒ ๋์ด ๊ฑฑ์ ์ด ๋ง์์ง๋ง ํจ๊ป ์ ๋ฉด๋ํ๋ก ํด๋ด๊ฒ ๋์ด ๋๋ฌด ๋ฟ๋ฏํฉ๋๋ค๐ ์์ผ๋ก ๋ ๋ฉ์ง ๊ฐ๋ฐ์๊ฐ ๋ ์๋ค์กฐ ์ผ๋๋ง์ฐจ ์ฌ๋ฌ๋ถ ์ ๋ง ์๊ณ ํ์ จ์ต๋๋ค๐ช