
위의 화면이 최종적으로 완성한 화면 입니다.
2주동안 진행한 프로젝트로 바닐라 js 를 학습하고 DOM에 어떻게 접근할 수 있는지에 대해 학습할 수 있었습니다.
게이지가 차는 애니메이션 효과
- css 효과를 주기 위해 after를 사용해야 했습니다.
- setInterval와 clearInterval, setTimeout 과 clearTimeout 을 이용해 구현해야 했습니다.
- 게이지가 다 차거나 클릭이벤트가 발생하면 아래 언론사와 기사를 보여주는 정보를 바꿔야 했습니다.
주요 코드
// 선택된 카테고리의 스타일을 변경
NewsListCategory.classList.replace("notselectNews", "selectNews");
// news 안에 내용을 채워줌 (pressInfo)
const pressInfoText = `
<img src = ${newsData.companyIcon}>
<span class = "display-medium12">${newsData.updatedDate}</span>
${pressInfoButton(
subscribedData.includes(newsData.companyName) ? "" : "구독하기"
)}`;
pressInfo.innerHTML = pressInfoText;
// 모든 정보를 업데이트한 후에 intervalId를 종료하고, 일정 시간이 지난 후에 스타일을 초기화
if (currentMidiaIndex >= totalCompanies) {
clearInterval(currentIntervalId);
setTimeout(() => {
NewsListCategory.classList.replace("selectNews", "notselectNews");
NewsListCategory.textContent = allNewsData[index].category;
rightButton.classList.remove("hidden");
leftButton.classList.remove("show");
currentMidiaIndex = 0;
}, 20000);
}
// 일정 시간 간격으로 updateNewsCategory 함수를 실행
currentIntervalId = setInterval(() => {
leftButton.className = "show";
updateNewsCategory();
}, 20000);
// 맨 처음 랜더링을 해주기 위해 호출
updateNewsCategory();
// 일정 시간이 지난 후에 intervalId를 종료
await new Promise((resolve) => {
currentTimeoutId = setTimeout(() => {
clearInterval(currentIntervalId);
resolve();
NewsListCategory.classList.replace("selectNews", "notselectNews");
NewsListCategory.textContent = allNewsData[index].category;
currentMidiaIndex = 0;
}, (totalCompanies - nowIndex) * 20000);
});
카테고리 안에 언론사에서도 돌아야하기 때문에 clearInterval 을 맨 처음 시작될때나 카테고리를 클릭할때, 화살표를 통해 언론사를 이동할때 3가지로 나눠서 처리를 해줬습니다.
clearInterval, setInterval의 사용이 중요했고 Promise를 통해 비동기로 애니메이션을 처리해줘야 했습니다.

구독하기나 해지하기 버튼을 눌렀을때 일어나는 동작
구독하기 버튼을 누를때는 5초동안 snackBar가 나오고 snackBar가 사라지면 내가 구독한 언론사로 이동해야 했습니다.
해지하기 버튼이나 x버튼을 누를때는 정말로 해지할건지 물어보는 모달창이 나오고 예를 누르면 해지한 언론사의 다음 언론사로 넘어가게 만들어야 했습니다.
import { pressInfoButton } from "../components/button.js";
import { snackBar } from "../components/snackBar.js";
import { clickSubscribeNews } from "./clickSubscribeNews.js";
import { addSubscribedData } from "./subscribeData.js";
// 구독하기 버튼이나 해지하기 버튼을 눌렀을 때 실행되는 함수
export const handlePressInfoButtonClick = (
button,
newsData,
subscribedData,
isList
) => {
button.addEventListener("click", () => { // 버튼에 클릭 이벤트 리스너 추가
const pressInfo = document.querySelector(".pressInfo");
const leftButton = document.getElementById("leftButton");
if (!subscribedData.includes(newsData.companyName)) { // 구독 데이터에 현재 뉴스 회사가 포함되어 있지 않은 경우
const pressInfoText = `
<img src="${newsData.companyIcon}"> <!-- 뉴스 회사 아이콘 표시 -->
<span class="display-medium12">${newsData.updatedDate}</span> <!-- 업데이트 날짜 표시 -->
${pressInfoButton("구독하기")} <!-- 구독하기 버튼 생성 -->
`;
pressInfo.innerHTML = pressInfoText; // pressInfo 요소에 위의 HTML을 삽입
snackBar("내가 구독한 언론사에 추가되었습니다.", isList); // 스낵바 알림 표시
if (isList) { // 만약 리스트 형태인 경우
setTimeout(() => { // 5초 후에 실행되는 함수
clickSubscribeNews(newsData.companyName); // 뉴스 회사 이름을 인자로 clickSubscribeNews 함수 호출
leftButton.className = "show"; // leftButton 요소의 클래스 이름을 "show"로 변경
}, 5000);
} else { // 리스트 형태가 아닌 경우
button.innerHTML = `<img src='../../images/closed.svg' alt='closed icon'/>
<span>해지하기</span>`; // 버튼의 내용을 해지하기로 변경
}
addSubscribedData(newsData.companyName); // 뉴스 회사 이름을 구독 데이터에 추가
} else { // 구독 데이터에 현재 뉴스 회사가 포함되어 있는 경우
const cancelAlert = document.getElementById("cancelAlert"); // cancelAlert 아이디를 가진 요소르ㄹ 선택
const cancelText = document
.getElementById("cancelAlertTop")
.querySelector("p"); // cancelAlertTop 아이디를 가진 요소의 자식 요소 p를 선택
cancelText.innerHTML = `<strong>${newsData.companyName}</strong>을/를<br>구독해지하시겠습니까?`; // 구독 해지 확인 메시지 설정
cancelAlert.className = "show"; // cancelAlert 요소의 클래스 이름을 "show"로 변경
}
});
};
위와 같이 구현을 했고 구독하기 / 해지하기 로 나누어 컴포넌트화 했으며 해당 코드를 그리드, 리스트일때 동일하게 사용했습니다.
또한 그리드와 리스트 각각에서 전체 언론사 / 내가 구독한 언론사에서도 동일하게 클릭이벤트로 재사용했습니다.

1. 프로젝트 설계의 중요성
프로젝트의 범위를 정의하고, 필요한 기능을 나열하며, 이를 바탕으로 구조와 흐름을 계획했습니다.
이를 통해 구현 중간에 발생할 수 있는 문제를 미리 예측하고 대비할 수 있었습니다.
2. DOM 접근 방식
선택자 활용: document.querySelector와 document.getElementById를 사용하여 필요한 요소를 선택하고 조작했습니다.
이벤트 리스너: addEventListener를 통해 사용자 인터랙션을 처리했습니다. 클릭 이벤트, 마우스 오버 이벤트 등을 통해 사용자와 상호작용했습니다.
동적 생성 및 삽입: innerHTML을 사용하여 동적으로 콘텐츠를 생성하고 삽입했습니다.
3. setInterval 활용 방법
setInterval을 사용하여 주기적으로 특정 작업을 수행할 수 있습니다.
이번 프로젝트에서는 자동으로 뉴스 기사를 갱신하거나 특정 동작을 반복하기 위해 활용했습니다.
4. CSS :after
CSS의 :after 가상 요소를 사용하여, DOM 요소의 실제 구조를 변경하지 않고도 스타일을 추가할 수 있음을 배웠습니다.
추가적인 스타일링: 애니메이션을 적용할때 이용했습니다.
이번 프로젝트를 통해 React와 같은 라이브러리의 중요성을 다시 한번 느낄 수 있었습니다.
바닐라 js 로 기능을 구현하면서 불편함을 많이 느낄 수 있었고 특히 useState 와 useEffect의 필요성을 느낄 수 있었습니다!!
완벽하다...