Spotify 클론 코딩 Ui 프로젝트
목표 : 스포티파이의 클론 코딩을 통해 배웠던 HTML/CSS 지식을 사용하고 몰랐던 지식을 습득하는 것을 목표로 합니다.
선정 이유
1. 공통의 관심사와의 연계: 팀원 모두의 취향과 맞닿아 있어 작업 동기와 팀 시너지를 극대화할 수 있다고 판단했습니다.
2. UI 중심의 구조: Spotify는 깔끔하고 직관적인 단색 기반의 디자인을 가지고 있으며, 과도한 JavaScript 없이도 CSS와 HTML만으로 충분히 표현 가능한 구조를 갖추고 있어, UI/UX 중심의 프론트엔드 개발 학습 목적에 적합했습니다.
배포 URL : https://frontend-lion-friends.netlify.app/
Github Repo URL : https://github.com/FRONTENDBOOTCAMP-14th/ui-project-team-10
해당 페이지는 교육용 목적으로 제작되었으며, 상업적 사용이 제한되어있음을 명시합니다.


Spotify 웹의 상단 헤더 영역을 고정(fixed) 위치로 구현하였으며, 다음과 같은 기능 및 반응형 동작을 적용했습니다.
검색창 토글 기능: 돋보기 아이콘 클릭 시 원형 버튼으로 축소되며, 다시 클릭 시 검색창이 확장되어 돋보기 아이콘 + 입력 필드 + 둘러보기 버튼이 표시되는 토글 방식으로 구성했습니다.
네비게이션 메뉴 구성: Premium, 지원, 다운로드, 앱 설치하기, 가입하기, 로그인하기 등 6개의 메뉴를 제공하며, 그 중 앱 설치 / 회원가입 / 로그인 페이지는 직접 구현하였습니다.
미디어쿼리 기반 반응형 UI (4단계)
지원, 다운로드, Premium 메뉴가 다시 표시됨접근성 개선: 각 네비게이션 버튼에 title 속성을 부여하여 마우스 오버 시 기능 설명이 표시되도록 구현

<header> 태그를 기준으로 내부에 사용되어지는 컴포넌트의 구분작업을 우선적으로 진행하였습니다.markup작업 진행<div>로 묶고 @media에서 작동되는 기준으로 각 컴포넌트를 마크업작성을 하였습니다.
<header> 컴포넌트 즉 해더컴포넌트를 작업하였습니다.<header>에서 @media의 동작이 총 5단계로 구성이 되어 있었으며, JavaScript의 사용이 필요로한 기능이 있었기에, 기능을 추가하는 과정에서 어려움을 격었습니다.
특히 검색창은 토글기능을 통해 축소 확장이 어려웠습니다.


.hiden클래스를 부여하여 숨김처리하며, 확장상태에서는 축소상태의 컴포넌트에 .absolute클래스를 부여하는 방식으로 기능을 제작하였습니다.문제점
window.visualViewport.width를 사용하여 현제 상태의 브라우저 너비를 인식하고, 이하일 경우에 클릭 이벤트를 주어 작동되도록 코드를 작성하였습니다.extendSearchBar)에 hidden 클래스를 적용해놓아 클릭시 hidden클래스는 사라지며, 동시에 축소상태의 검색창(clossingSearchBar)에 absolute클래스가 추가됩니다.toggleSearchBtn.addEventListener("click", function () {
// 브라우저 너비 확인
const viewportWidth = window.visualViewport.width;
// test
console.log("click");
// 브라우저 너비 1078px 미만일경우
if (viewportWidth < 1078) {
// 확장상태 검색창 -> hidden 클래스 토글추가
extendSearchBar.classList.toggle("hidden");
// 축소상태 검색창 -> absolute 클래스 토글 추가
clossingSearchBar.classList.toggle("absolute");
// 확장시 input에 커서 집중
searchInput.focus();
} else {
searchInput.focus();
}
});

페이지 하단에 고정된(fixed) 미리듣기 광고 박스를 구현했습니다. 이 요소는 Spotify 비로그인 사용자에게 표시됩니다.
min-height, max-height 속성을 설정해 안정적인 UI 유지hover 시 스케일을 1.05로 확대하는 애니메이션 효과를 추가<footer> 태그가 가시적으로 맞는 것 같으나, 의미적으로는 옳지 못하다는 판단이 되어 <div> 마크업으로 전체 컴포넌트를 구성하였습니다.
Spotify 스타일의 로그인 페이지를 구현하였으며, 기본적인 유효성 검사 및 반응형 UI를 적용했습니다.
767px 이하 해상도에서 input 요소가 전체 너비를 차지하도록 조정하여 모바일에서도 적절히 작동type="email"과 required 속성을 통해 형식 검사:user-invalid CSS 선택자를 활용하여 유효하지 않은 입력 시 빨간색 경고 메시지 출력 및 input 테두리 색상 변경반응형이기에 모바일환경과 브라우저환경을 나누어 작업을 하였습니다. 처음 작업할 때에는 모바일을 기준으로 작업하였으며, 확장상태를 @media로 작업하였습니다.

input에 사용되는 기능이 많은지라 많은 디자인코드가 사용되었습니다.
hover : 선의 색이 흰색으로 변합니다.
focus : outline이 두꺼워지며, 색은 흰색입니다.

유효성에 옳지 못할 경우 빨강색선으로 변합니다.

.error-massage : display가 none인 <p> 태그에 :user-invaild선택자를 이용하여, 유효성검사에 오류가 발생할 경우 display에 block을 주어 경고문을 출력합니다.
.error-massage {
height: 20px;
align-content: center;
color: #f3727f;
font-weight: 400;
font-size: 0.8125rem;
margin-block-start: 8px;
display: none;
svg {
vertical-align: bottom;
}
}
input {
border: 1px solid var(--spotify-gray);
width: 100%;
height: 48px;
border-radius: 4px;
padding: 12px;
color: var(--spotify-white);
&::placeholder {
font-weight: 600;
}
&:hover {
border-color: white;
}
&:focus {
outline: 3px solid white;
}
&:user-invalid:focus {
outline: 3px solid rgb(237, 44, 63);
border: none;
}
&:user-invalid {
outline: 1px solid rgb(237, 44, 63);
border: none;
}
&:user-invalid + .error-massage {
display: block;
}
}
}

로그인 페이지에서 연결되는 회원가입 페이지입니다. UI 위주의 구성에 집중하였으며 다음과 같은 특징이 있습니다.
input 요소에 대해 기본적인 유효성 검사와 시각적 피드백 제공hover 시 테두리가 흰색으로 변경@media가 존재하나, 기능을 추가하지 못하였습니다.
Spotify 앱 설치 유도 페이지로, 실제 Microsoft Store 또는 앱 다운로드 링크를 통해 브라우저 기반 설치 기능이 동작하도록 구성했습니다.
width ≥ 1387px에서는 flex-direction: row로 가로 배치width < 1387px에서는 flex-direction: column으로 전환되는 구조<div class="main-component">
...
<div />
<div> 태그의 내부에 제작된 컴포넌트를 불러왔습니다.async function loadPage() {
// 현재 주소창에서 해시값을 가져옴. 예: #install-page
const hash = location.hash;
// 해시가 없으면 → 기본 페이지 (index.html의 <main>) 그대로 둠
if (!hash) return;
const pageName = hash.substring(1); // '#' 제거: "install-page"
try {
const response = await fetch(`./${pageName}.html`);
const html = await response.text();
document.querySelector(".main-component").innerHTML = html;
} catch (error) {
document.querySelector(".main-component").innerHTML =
"<p>페이지를 불러올 수 없습니다.</p>";
}
}
window.addEventListener("hashchange", () => {
location.reload();
});
// 해시가 바뀌었을 때만 실행 (ex. #about, #contact 클릭)
window.addEventListener("hashchange", loadPage);
// 페이지 처음 열었을 때 실행
window.addEventListener("DOMContentLoaded", loadPage);
초반에 컴포넌트의 구분과, 제작계획 등을 세울 때에 성급한 마음에 정확하게 나누지 못하고 제작을 했었습니다. 처음에는 진행속도가 붙어 빠르게 제작되는가 싶었으나, 수정사항이 생기고, 서로의 코드를 병합하는 과정에서 충돌등의 문제가 발생하였습니다.
배운점
다음부터는 느리더라도, 컨벤션, 요구사항, 목표, 역할 분담, 구현기능 등을 세분화하여 각각 분업화하고 팀원간의 규칙을 정하는데에 더 많은 시간을 사용할 것입니다. 이것이 초반에는 불편하고 시간이 걸릴지라도, 프로젝트를 진행하면서 생기는 문제점들을 보안할 수 있는 어쩌면 가장 빠른 길이라고 생각됩니다.
UI프로젝트를 진행하면서 눈에 보이는 부분에 집중을 하다보니 사용자적 개발을 하지 못한것 같습니다.
배운점
디자인 시안을 제작 할 때에는 원소단위부터 큰컴포넌트까지 세분화하여 제작하고, 자주사용되어지는 치수 및 기능들을 묶어 공통으로 사용 할 수 있도록 기능을 추가해보는 작업도 좋을 것 같습니다.
커밋메시지를 작성할 때 규칙을 정하였으나, 소통의 오류에서인지 조금씩 다른 규칙으로 커밋을 작성했던 것을 발견하였습니다. 때문에 어떠한 부분이 수정되더라도, 서로 작성된 글의 내용이 다르다보니 이해하는데에 많은 시간이 걸렸고 이는 작업에 효율적이지 못한 영향을 받았습니다.
배운점
대표적인 기능으로feat:,fix:,refactor:등의 커밋메시지를 적극 활용하여, 각각의 커밋이 어떠한 역활을 하고 있는지 직관적으로 이해할 수 있도록 세분화 할것입니다.
Pull Request를 진행 할 때에 시간이 부족하고, 귀찮음에 코드리뷰를 정확하게 진행하지 못했던 경험이 있었습니다. 이로 큰 문제가 발생하지는 않았지만, 이러한 PR이 누적되면서 각자의 코드에 개성이 보이는? 즉 서로 다른 규칙으로 코드를 작성하는 상황이 생기게 되었습니다.
배운점
하루에 계획된 작업이 끝나고 PR의 시간을 정해 함께 코드 리뷰 등 서로 작성한 코드가 어떻게 작성이 되었고, 어떻게 개선하면 더 좋을지를 의논하는 시간을 만들 필요가 있다고 생각됩니다.
배운점
이러한 문제를 해결할 수 있는 유일한 방법은 서로의 생각을 이야기하고 의견을 수립하는 것이라고 생각됩니다.
생각이 다름을 이해하고, 사소한점이라도 이야기하고 말하는 습관을 가져야 할것 같습니다. 나에게는 중요하지 않다고 생각되는 부분이라도 함께 작업하는 팀원에게는 중요한 사안이 될
수도 있기 때문입니다.