-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
KDT0_JangYeongMin 멜론 홈페이지 클론 코딩 #54
base: main
Are you sure you want to change the base?
Changes from all commits
949338a
fdf8783
07a2929
3e230bc
d8b7526
2be61cd
3f48e30
1f72556
ac1de04
d7b361a
77e4e4d
0164b66
327e909
835e1fa
b2a8620
a180539
a1e000d
30656ba
e13b260
999220c
712b409
cc9f6bd
f40d856
695a1fd
210a1a7
868473d
c0c330e
98fa507
12ffd29
f188699
99540bc
1974c0a
130cadf
9c80fb3
f7ed06d
65559f2
ffc84a0
80dbc3f
019f816
f0d98dd
b60f365
45b0f61
f14085d
24cab00
6434f6c
b2119ba
68fad61
2e4afa9
fe8e17a
4a50e2e
f601395
fec7602
36d5005
13233e3
d4806b0
be8e871
e2fff11
beec944
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,85 @@ | ||
[참고 내용] | ||
<br> | ||
|
||
👀 자신이 원하는 사이트 레이아웃 클론 | ||
원하는 사이트(페이지)를 자유롭게 선택하고 레이아웃을 클론 코딩하세요. | ||
평소에 도전해 보고 싶었거나 혹은 자신의 수준에 맞는 사이트(페이지)를 선택하세요. | ||
과제 수행 및 리뷰 기간은 별도 공지를 참고하세요! | ||
![Melon](https://i.namu.wiki/i/gYv3IQQKE7WxU7CMkSQLRpfgBrTukY6a-_nMyJ70m4ZzZpQta7QN5cmhFwvcer3uE3i7yiX52yy48y3pWZZXhg.svg) | ||
<br> | ||
<br> | ||
## 🎧 멜론 홈페이지 클론 코딩 🎧 | ||
|
||
과제 수행 및 제출 방법 | ||
### 🔗 프로젝트 URL | ||
|
||
1. 현재 저장소를 로컬에 클론(Clone)합니다. | ||
2. 자신의 본명으로 브랜치를 생성합니다.(구분 가능하도록 본명을 꼭 파스칼케이스로 표시하세요, git branch KDT0_이름) | ||
3. 자신의 본명 브랜치에서 과제를 수행합니다. | ||
4. 과제 수행이 완료되면, 자신의 본명 브랜치를 원격 저장소에 푸시(Push)합니다.(main 브랜치에 푸시하지 않도록 꼭 주의하세요, git push origin KDT0_이름) | ||
5. 저장소에서 main 브랜치를 대상으로 Pull Request 생성하면, 과제 제출이 완료됩니다!(E.g, main <== KDT0_이름) | ||
- **멜론 홈페이지 URL** : https://www.melon.com/index.htm | ||
|
||
- **과제 URL** : [yeongmin-melonclone.netlify.app](https://yeongmin-melonclone.netlify.app/) | ||
|
||
- main 혹은 다른 사람의 브랜치로 절대 병합하지 않도록 주의하세요! | ||
- Pull Request에서 보이는 설명을 다른 사람들이 이해하기 쉽도록 꼼꼼하게 작성하세요! | ||
- Pull Request에서 과제 제출 후 절대 병합(Merge)하지 않도록 주의하세요! | ||
- 과제 수행 및 제출 과정에서 문제가 발생한 경우, 바로 담당 멘토나 강사에서 얘기하세요! | ||
*** | ||
|
||
필수 요구사항 | ||
### 📌 필수 요구사항 체크리스트 | ||
|
||
- 과제에 대한 설명을 포함한 README.md 파일을 제공하세요! | ||
- 과제 결과와 비교할 수 있는 실제 사이트(페이지)의 주소를 명시하세요! | ||
- 과정에서 사용한 프로젝트 폴더/파일이 모두 포함돼야 합니다, 일부 파일만 제출하지 마세요! | ||
- 실제 서비스로 배포하고 접근 가능한 링크를 추가해야 합니다. | ||
- [ ] 과제에 대한 설명을 포함한 README.md 파일 작성 | ||
- [ ] 실제 사이트 주소 명시 | ||
- [ ] 과정에서 사용한 프로젝트 폴더,파일 KDT0_JangYeongMin 브랜치에 업로드 | ||
- [ ] netlify를 사용하여 배포 후 클론 사이트 주소 명시 | ||
|
||
선택 요구사항 | ||
### 📌 선택 요구사항 체크리스트 | ||
|
||
- < header >, < section > 등 시멘틱 태그를 최대한 활용해보세요. | ||
- 실제 사이트의 레거시 코드 활용보단 최신의 CSS Flex 혹은 Grid 등을 활용해보세요. | ||
- 부분적으로 BEM 방법론을 도입해보세요. | ||
- JS가 필요한 부분은 되도록 생략하되 이유를 명시해보세요.(CSS로 대체 가능한지 피드백이 있을 수 있겠죠?!) | ||
- JS가 필요한 부분 중 구현할 부분이 있다면 자유롭게 구현해보세요.(JS 과제가 아니니까 가볍게 구현하시길 추천해요) | ||
- [ ] 시멘틱 태그를 최대한 사용해보기 | ||
- [ ] 부분적으로 CSS Grid 와 CSS Flex 사용하여 레이아웃 | ||
- [ ] JS가 꼭 필요한 부분에는 간단하기 구현 | ||
|
||
손쉬운 이미지 추출 방법 | ||
*** | ||
|
||
사이트 클론에 필요한 이미지를 좀 더 쉽게 추출하기 위해서 Chrome 확장 프로그램인 Image Downloader를 사용하세요. | ||
### 🗓️ 개발 기간 | ||
|
||
1. 원하는 사이트 접속 | ||
2. Image Downloader 확장 프로그램 실행 | ||
3. 다운로드 원하는 이미지 선택 | ||
4. 서브 폴더 이름(Save to subfolder) 명시 | ||
5. 다운로드! | ||
**2023.07.24 ~ 2023.07.28** | ||
|
||
*** | ||
|
||
### 🔨 사용 기술 스택 | ||
|
||
<img src="https://img.shields.io/badge/html5-E34F26?style=for-the-badge&logo=html5&logoColor=white"> | ||
<img src="https://img.shields.io/badge/css-1572B6?style=for-the-badge&logo=css3&logoColor=white"> | ||
<img src="https://img.shields.io/badge/javascript-F7DF1E?style=for-the-badge&logo=javascript&logoColor=black"> | ||
|
||
<img src="https://img.shields.io/badge/github-181717?style=for-the-badge&logo=github&logoColor=white"> | ||
<img src="https://img.shields.io/badge/git-F05032?style=for-the-badge&logo=git&logoColor=white"> | ||
|
||
|
||
*** | ||
|
||
### 🧑🏻💻 주요 구현 사항 | ||
|
||
#### HTML | ||
> - <`header`> , <`main`>, <`footer`> 등 시맨틱 태그 활용 | ||
> | ||
> - 실제 사이트와 거의 비슷한 레이아웃 구현 | ||
|
||
|
||
#### CSS | ||
> - flex를 활용하여 컨텐츠 요소들 배치 및 디자인 구현 | ||
> | ||
> - :hover를 사용하여 header 태그 및 링크 요소 디자인 구현 | ||
|
||
|
||
#### JavaScript | ||
> - 자바스크립트 내장함수를 이용하여 클릭, hover, 전환 등 기능 구현 | ||
> | ||
> - fontawesome 라이브러리를 활용하여 아이콘 사용 | ||
|
||
|
||
*** | ||
|
||
### 📝 아쉬운점 & 느낀점 | ||
|
||
> - 시간 부족 및 자바스크립트 경험 부족으로 인해 원하는 기능을 구현하지 못했습니다. | ||
> | ||
> - 자바스크립트를 사용하여 이미지 슬라이드, hover 기능을 추가하여 자연스러운 전환이 되어야 하지만 기능을 구현하지 못했습니다. | ||
> | ||
> - 시맨틱 태그의 정확한 사용 용도 파악, css 기능 파악, 자바스크립트 내장 함수 등 아직 공부할 것이 너무 많다고 느꼈고 좀 더 깊이 공부를 해야겠다고 다짐하는 계기가 되었습니다. | ||
|
||
### ⌨️ 추후 구현사항 | ||
|
||
> - 이미지 슬라이드 적용 | ||
> | ||
> - 작업하지 못한 버튼 클릭 시 이미지 전환 | ||
> | ||
> - 멜론 차트 크롤링 적용 |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 영민님 혹시 호버기능을 JS로 구현하신 이유가 따로 있을까요?? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 호버 기능을 넣을 때 이거는 JS 로 구현해야 되는게 아닌가 라고 생각해서 JS로 구현 했습니다! css로 구현이 가능하다면 수정 해보겠습니다 ㅎㅎ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아하 넵!! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// 이미지와 호버 요소에 대한 호버 핸들러를 추가하는 함수 정의 | ||
function addHoverHandlers(imageId, hoverId, singerId) { | ||
|
||
const img = document.getElementById(imageId); // 이미지 요소 | ||
const imgHover = document.getElementById(hoverId); // 호버(마우스 오버) 시 나타날 요소 | ||
const singer = document.getElementById(singerId); // 호버 시 사라질 가수 정보 등 다른 요소 | ||
|
||
let isHovered = false; // 이미지나 호버 요소에 마우스가 올려져 있는지 여부를 저장하는 변수 | ||
let leaveTimeout; // 요소를 벗어날 때 타이머를 설정하기 위한 변수 | ||
|
||
// 마우스가 요소 안으로 들어올 때 | ||
img.addEventListener("mouseenter", () => { | ||
imgHover.style.display = "block"; // 호버 요소를 보이도록 설정 | ||
singer.style.display = "none"; // 가수 정보 등 다른 요소를 숨김 | ||
isHovered = true; // 호버 상태를 true로 설정 | ||
}); | ||
|
||
// 마우스가 호버 요소 안으로 들어올 때 | ||
imgHover.addEventListener("mouseenter", () => { | ||
imgHover.style.display = "block"; // 호버 요소를 보이도록 설정 | ||
isHovered = true; // 호버 상태를 true로 설정 | ||
}); | ||
|
||
// 이미지에서 마우스가 떠날 때 | ||
img.addEventListener("mouseleave", () => { | ||
isHovered = false; // 호버 상태를 false로 설정 | ||
|
||
// 요소를 벗어나면 leaveTimeout을 설정하여 일정 시간 후에 hover 값을 해제 | ||
leaveTimeout = setTimeout(() => { | ||
// 만약 isHovered가 false인 경우 | ||
if (!isHovered) { | ||
imgHover.style.display = "none"; // 호버 요소를 숨김 | ||
singer.style.display = "block"; // 가수 정보 등 다른 요소를 보이도록 설정 | ||
} | ||
}, 100); // 100ms(0.1초) 후에 실행되는 콜백 함수 | ||
}); | ||
|
||
// 호버에서 마우스가 벗어날 때 | ||
imgHover.addEventListener("mouseleave", () => { | ||
imgHover.style.display = "none"; // 호버 요소를 숨김 | ||
singer.style.display = "block"; // 가수 정보 등 다른 요소를 보이도록 설정 | ||
isHovered = false; // 호버 상태를 false로 설정 | ||
}); | ||
} | ||
|
||
// 예시 요소에 대한 hover 핸들러 추가 | ||
addHoverHandlers("img", "img_hover", "singer_section"); | ||
addHoverHandlers("img2", "img_hover2", "singer_section2"); | ||
addHoverHandlers("img3", "img_hover3", "singer_section3"); | ||
addHoverHandlers("img4", "img_hover4", "singer_section4"); | ||
addHoverHandlers("img5", "img_hover5", "singer_section5"); | ||
addHoverHandlers("img6", "img_hover6", "singer_section6"); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// 클릭한 요소를 저장할 변수 | ||
let clickedElement = null; | ||
|
||
// .circle 요소들 가져오기 | ||
const circles = document.querySelectorAll('.circle'); | ||
|
||
// hover 효과를 적용하는 함수 | ||
function addHoverEffect(circle) { | ||
// 마우스가 요소 위에 올라갔을 때 | ||
circle.addEventListener('mouseenter', () => { | ||
// 클릭된 요소가 아닌 경우에만 효과를 적용 | ||
if (circle !== clickedElement) { | ||
circle.style.border = '1px solid #1b1b1b'; // 테두리 색상 변경 | ||
} | ||
}); | ||
|
||
// 마우스가 요소에서 벗어났을 때 | ||
circle.addEventListener('mouseleave', () => { | ||
// 클릭된 요소가 아닌 경우에만 효과를 초기화 | ||
if (circle !== clickedElement) { | ||
circle.style.border = '1px solid #999999'; // 테두리 색상을 원래대로 되돌리기 | ||
Comment on lines
+13
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. css 스타일은 가능한 자바스크립테서는 직접 조작하는 않는것을 추천드립니다, style 속성보다는 클래스를 만들고 class add, remove 를 이용해주시면 스타일 과 로직이 분리가 되어 코드 유지보수가 더 쉬워집니다
|
||
} | ||
}); | ||
} | ||
|
||
// 클릭 효과를 적용하는 함수 | ||
function addClickEffect(circle) { | ||
// 요소를 클릭했을 때 | ||
circle.addEventListener('click', () => { | ||
clickedElement = circle; // 클릭한 요소를 저장 | ||
circles.forEach(circle => circle.classList.remove('clicked')); // 모든 요소의 'clicked' 클래스를 제거 | ||
clickedElement.classList.add('clicked'); // 클릭한 요소에만 'clicked' 클래스를 추가 | ||
}); | ||
} | ||
|
||
// 모든 .circle 요소에 hover 효과와 클릭 효과를 추가 | ||
circles.forEach(circle => { | ||
addHoverEffect(circle); | ||
addClickEffect(circle); | ||
}); | ||
|
||
// .circle1 요소에 hover 효과를 추가 | ||
const circle1 = document.getElementById('circle1'); | ||
addHoverEffect(circle1); | ||
|
||
// .circle1 요소를 클릭했을 때 | ||
circle1.addEventListener('click', () => { | ||
clickedElement = circle1; // 클릭한 요소를 저장 | ||
circles.forEach(circle => circle.classList.remove('clicked')); // 모든 요소의 'clicked' 클래스를 제거 | ||
clickedElement.classList.add('clicked'); // 클릭한 요소에만 'clicked' 클래스를 추가 | ||
}); | ||
Comment on lines
+47
to
+51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 코드를 확인해보니 페이지의 모든 circle 을 반복하면 clicked 클래스를 제거하는걸로 보입니다
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// 각 이미지에 대한 href와 src 정보가 객체로 담겨 있다. | ||
// href는 이미지를 클릭했을 때 이동할 링크 주소를 나타낸다. | ||
// src는 이미지 파일의 URL을 나타낸다. | ||
// 각 객체마다 고유한 링크와 이미지 URL이 설정되어 있다. | ||
const circleInfo = [ | ||
{ | ||
href: "https://www.melon.com/album/detail.htm?albumId=11294520", | ||
src: "https://cdnimg.melon.co.kr/svc/images/main/imgUrl20230727030134.jpg/melon/quality/80", | ||
}, | ||
{ | ||
href: "https://www.melon.com/album/detail.htm?albumId=11292865", | ||
src: "https://cdnimg.melon.co.kr/svc/images/main/imgUrl20230726010900.png/melon/quality/80", | ||
}, | ||
{ | ||
href: "https://www.melon.com/album/detail.htm?albumId=11294303", | ||
src: "https://cdnimg.melon.co.kr/svc/images/main/imgUrl20230727030153.jpg/melon/quality/80", | ||
}, | ||
{ | ||
href: "https://www.melon.com/album/detail.htm?albumId=11294409", | ||
src: "https://cdnimg.melon.co.kr/svc/images/main/imgUrl20230727030212.jpg/melon/quality/80", | ||
}, | ||
{ | ||
href: "https://www.melon.com/album/detail.htm?albumId=11292440", | ||
src: "https://cdnimg.melon.co.kr/svc/images/main/imgUrl20230726112148.png/melon/quality/80", | ||
}, | ||
{ | ||
href: "https://www.melon.com/album/detail.htm?albumId=11292423", | ||
src: "https://cdnimg.melon.co.kr/svc/images/main/imgUrl20230726012238.png/melon/quality/80", | ||
}, | ||
{ | ||
href: "https://www.melon.com/event/view/index.htm?eventId=35425", | ||
src: "https://cdnimg.melon.co.kr/svc/images/main/imgUrl20230727030231.jpg/melon/quality/80", | ||
}, | ||
] | ||
Comment on lines
+5
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 작성해주신 코드와 같이 정적인 데이터들은 따로 파일로 관리해주시는게 좋을꺼같습니다~ |
||
|
||
// 모든 circle 요소들에 대해 이벤트 추가 | ||
for (let i = 0; i < circleInfo.length; i++) { | ||
const circle = document.getElementById(`circle${i + 1}`); | ||
circle.addEventListener("click", handleCircleClick); | ||
// 각 circle 요소에 클릭 이벤트를 추가하고, 클릭 시 handleCircleClick 함수를 실행 | ||
// circle 요소들은 HTML에서 id가 'circle1', 'circle2', ...와 같이 숫자를 포함한 형태로 되어 있다. | ||
// 숫자를 이용하여 circleInfo 배열에서 해당 이미지 정보를 찾는다. | ||
} | ||
|
||
// 이벤트 처리 함수 정의 | ||
function handleCircleClick(event) { | ||
// 모든 circle의 테두리를 원래 색상으로 초기화 | ||
for (let i = 0; i < circleInfo.length; i++) { | ||
const circle = document.getElementById(`circle${i + 1}`); | ||
circle.style.border = "1px solid #999999"; | ||
} | ||
Comment on lines
+37
to
+51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 코드에서 모든 circle들에 이벤트 리스너가 등록되는것을 확인할수있습니다 이러한 부분을 개선하기 위해 이벤트 위임을 해주시면 성능, 코드 가독성이 더욱 올라갑니다~
|
||
// 클릭한 circle을 제외한 모든 circle들의 테두리 스타일을 초기화 | ||
// 스타일은 회색 테두리를 가지도록 설정되어 있다. | ||
|
||
// 클릭한 circle에 테두리를 변경한 색상으로 설정 | ||
const clickedCircle = event.target; | ||
clickedCircle.style.border = "1px solid #1f1f1f"; | ||
// 클릭한 circle에 테두리 스타일을 검은색 테두리로 변경 | ||
|
||
// 클릭한 circle의 id 값을 가져와서 숫자 부분만 추출 | ||
const circleNumber = clickedCircle.id.match(/\d+/)[0]; | ||
// 클릭한 circle의 id에서 숫자만 추출하여 circleNumber 변수에 저장 | ||
// id는 'circle1', 'circle2', ...와 같이 숫자를 포함한 형태, 숫자만 추출하여 해당 이미지 정보를 찾을 때 사용 | ||
|
||
// 새로운 href와 src 값 가져오기 | ||
const newHref = circleInfo[circleNumber - 1].href; | ||
const newSrc = circleInfo[circleNumber - 1].src; | ||
// circleNumber를 이용하여 circleInfo 배열에서 해당 이미지 정보를 찾아온다. | ||
// circleNumber는 1부터 시작하고 배열은 0부터 시작, circleNumber - 1로 배열 인덱스에 접근 | ||
|
||
// href와 src 속성을 새로운 값으로 업데이트하고 애니메이션 클래스 추가 | ||
const eventImgA = document.getElementById("eventImgA"); | ||
const eventImg = document.getElementById("eventImg"); | ||
const eventImgContainer = document.getElementById("imgContainer"); | ||
// HTML에서 이미지와 이미지 컨테이너에 대한 요소들을 가져온다. | ||
|
||
eventImgA.href = newHref; | ||
eventImg.src = newSrc; | ||
// 클릭한 이미지에 해당하는 링크와 이미지 URL을 설정 | ||
// 클릭한 이미지를 클릭했을 때 해당 이미지와 연결된 링크로 이동 | ||
|
||
eventImgContainer.classList.add("new-value"); | ||
// 이미지 컨테이너에 'new-value'라는 클래스를 추가 | ||
// CSS로 정의되어 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
자바스크립트를 기능별로 나눠놓으신 부분도
이후 중요한 기능 구현 때 참고해보겠습니다!