6️⃣ 순차적 애니메이션 - 전역 버튼 스타일
Sementic Tag의 사용
Sementic Tag란?
sementic이란 의미론적, 의미론적인을 뜻한다.
즉, sementic tag란 의미를 부여하기 위한 역할의 태그라고 할 수 있다.
이전에 header 영역을 작업할 때에도 <div>
태그가 아닌 <header>
태그를 사용한 것 처럼, 의미가 없는 <div>
대신 해당 영역이 하나의 문서(컨텐츠 그룹)라는 의미를 부여하기 위해 <section>
이라는 태그를 사용하였다.
<body>
<-- HEADER -->
<hedaer>
...
</header>
<-- VISUAL -->
<section class="visual">
...
</section>
</body>
아래 MDN문서를 참고해보면 시멘틱 태그에는 <header>
와 <section>
말고도 다양한 의미를 부여할 수 있는 태그들이 존재한다는 것을 알 수 있다.
Semantics - MDN Web Docs 용어 사전: 웹 용어 정의 | MDN
프로그래밍에서, 시맨틱은 코드 조각의 '의미'를 나타냅니다. 예를 들어, ("이게 어떻게 시각적으로 보여질까?" 보다는), 이 Javascript 라인을 실행하는 것은 어떤 효과가 있나요?", 혹은 "이 HTML 엘
developer.mozilla.org
시맨틱 태그 (Semantic Tag)란? - 태그 요소의 종류와 이점
시맨틱 태그 (Semantic Tag)는 포함된 콘텐츠의 특정 의미를 정의하고 목적을 갖는 태그를 말합니다. 이번 글을 통해 시맨틱 태그 요소의 종류와 이점까지 확인해보세요.
seo.tbwakorea.com
Sementic Tag를 사용하는 이유?
- 접근성 향상
스크린 리더 사용자에게 큰 이점을 줄 수 있다.
신체적, 인지적 장애가 있는 사람들을 포함하여 모든 사람에게 원활한 경험을 보장할 수 있다. - SEO(검색엔진최적화) 향상
관련 키워드와 문구에 대해 웹페이지를 최적화하는 데 도움이 되고, 검색결과 상에서 웹 사이트 노출 순위를 높여 더 많은 트래픽을 유도할 수 있다.
Screen Reader
A web-powered screen reader
chromewebstore.google.com
버튼 공통 스타일로 정의하기
기본 상태 버튼 스타일 정의하기
/* COMMON */
.btn {
display: block;
width: 130px;
padding: 10px;
border: 2px solid #333;
border-radius: 4px;
color: #333;
font-size: 16px;
font-weight: 700;
text-align: center;
cursor: pointer;
box-sizing: border-box;
transition: 0.4s;
}
.btn:hover {
background-color: #333;
color: #fff;
}
기본 상태의 버튼을 디자인하고 버튼을 hover했을때 배경색와 글자색을 변경하도록 정의한다.
상태에 따른 버튼 스타일 정의하기
.btn.btn--reverse {
background-color: #333;
color: #fff;
}
.btn.btn--reverse:hover {
background-color: transparent;
color: #3333;
}
각 버튼 상태에 따른 디자인 정의를 일치 선택자를 통해 정의하고 hover했을 경우도 같이 디자인한다.
정의한 버튼의 클래스 적용하기
<a href="javascfipt:void(0)" class="btn btn--brown">자세히 보기</a>
일치 선택자로 정의하였기 때문에 기존 .btn
클래스를 기본 디자인으로 .btn—brown
와 같은 상태 클래스를 덮어씌우는 형태로 한칸 공백을 주고 클래스를 적용한다.
결과
7️⃣ 순차적 애니메이션 - 순차적으로 요소 보이기
visual 내부에있는 모든 요소들을 순차적으로 페이드인 하여 보여주려고 한다.
우선 모든 요소들을 안보이게 opcity
를 0
으로 부여하고, 자바스크립트를 통해 순차적으로 보이게끔 opacity
를 1
로 설정하여 애니메이션을 부여할 예정이다.
.fade-in
클래스 정의 및 요소들 그룹핑
.visual .fade-in {
opacity: 0;
}
.fade-in
클래스를 정의하여 투명도를 0으로 만들도록 설정한다.
<!-- VISUAL -->
<section class="visual">
<div class="inner">
<div class="title fade-in">
<img
src="./images/visual_title.png"
alt="STARBUCKS DELIGHTFUL START TO THE YEARS"
/>
<a href="javascfipt:void(0)" class="btn btn--brown">자세히 보기</a>
</div>
<div class="fade-in">
<img
src="./images/visual_cup1.png"
alt="new OATMEAL LATTE"
class="cup1 image"
/>
<img
src="./images/visual_cup1_text.png"
alt="오트밀 라떼"
class="cup1 text"
/>
</div>
<div class="fade-in">
<img
src="./images/visual_cup2.png"
alt="new STARBUCKS CARMEL CRUMBLE MOCHA"
class="cup2 image"
/>
<img
src="./images/visual_cup2_text.png"
alt="스타벅스 카라멜 크럼블 모카"
class="cup2 text"
/>
</div>
<div class="fade-in">
<img src="./images/visual_spoon.png" alt="Spoon" class="spoon" />
</div>
</div>
</section>
- 타이틀 부분 하나와 이미지 부분 2개, 총 3개의 영역을
<div>
태그로 그룹핑하고 각 그룹에.fade-in
클래스를 부여한다.
순차적으로 요소 보이기
const fadeEls = document.querySelectorAll(".visual .fade-in");
fadeEls.forEach((fadeEl, index) => {
gsap.to(fadeEl, 1, {
delay: (index + 1) * 0.7,
opacity: 1,
});
});
.fade-in
클래스를 가지고 있는 요소들을querySelectorAll()
을 통해fadeEls
라는 변수에 각 요소들을 배열 형태로 할당해놓는다.forEach()
를 통해 보일 요소들을 하나씩 순회해서 gsap 라이브러리의to()
메서드를 활용하여 애니메이션을 부여한다.- 공통적으로
opcity: 1
로 설정하고 각 요소마다 적용되는 지연시간을 달리하기 위해(index + 1)
을 기준으로0.7
씩 곱해서 각 요소마다0.7
씩 투명도를 적용하도록 자동화한다.
ex) 0.7s ⇒ 1.4s ⇒ 2.1s ⇒ 2.7s
결과
8️⃣ 요소 슬라이드
Swiperjs란?
Swiper - The Most Modern Mobile Touch Slider
Swiper is the most modern free mobile touch slider with hardware accelerated transitions and amazing native behavior.
swiperjs.com
Swiperjs 라이브러리는 요소들을 좌우 혹은 수직으로 슬라이드할 수 있게끔 도와주는 라이브러리이다.
해당 프로젝트는 swiper 6버전을 사용하며, 최신버전과의 차이는 다음과 같다.
<!-- Slider main container -->
<div class="swiper-container">
<!-- Additional required wrapper -->
<div class="swiper-wrapper">
<!-- Slides -->
<div class="swiper-slide">Slide 1</div>
<div class="swiper-slide">Slide 2</div>
<div class="swiper-slide">Slide 3</div>
</div>
<!-- Slider main container -->
<div class="swiper">
<!-- Additional required wrapper -->
<div class="swiper-wrapper">
<!-- Slides -->
<div class="swiper-slide">Slide 1</div>
<div class="swiper-slide">Slide 2</div>
<div class="swiper-slide">Slide 3</div>
</div>
6버전에서의 swiper 최상위 부모 클래스는 .swiper-container
이고 최신 버전에서는 .swiper
이다. 이 점을 고려해서 작성한다.
Swiperjs CDN 추가하기
Swiper - The Most Modern Mobile Touch Slider
Swiper is the most modern free mobile touch slider with hardware accelerated transitions and amazing native behavior.
swiperjs.com
Swiperjs 라이브러리를 CDN을 통해 가져올 경우 위와 같이 가져올 수 있다.
현 프로젝트에서는 6.8.4 버전을 사용할 것이기 때문에 swiper@6.8.4
를 기입하여 특정 버전의 라이브러리를 가져오도록 한다.
<link
rel="stylesheet"
href="https://unpkg.com/swiper@6.8.4/swiper-bundle.min.css"
/>
<script src="https://unpkg.com/swiper@6.8.4/swiper-bundle.min.js"></script>
공지사항 수직 슬라이드 적용하기
수직 슬라이드 데모 참고
수직 슬라이드의 데모를 확인하기 위해 데모에서 Vertical
탭을 클릭하면 관련 데모를 확인할 수 있다.
현 프로젝트는 바닐라 자바스크립트 환경이므로 데모 코드를 확인을 위해 core
탭을 클릭하하면 코드를 확인해볼 수 있다.
Swpierjs를 사용하기 위한 HTML구조와 javascript코드를 간단하게 확인할 수 있다.
슬라이드 HTML 구조 작성
<div class="inner__left">
<h2>공지사항</h2>
<div class="swiper-container">
<div class="swiper-wrapper">
<div class="swiper-slide">
<a href="javascript:void(0)"
>크리스마스 & 연말연시 스타벅스 매장 영업시간 변경 안내</a
>
</div>
<div class="swiper-slide">
<a href="javascript:void(0)"
>[당첨자 발표] 2021 스타벅스 플래너 영수증 이벤트</a
>
</div>
<div class="swiper-slide">
<a href="javascript:void(0)"
>스타벅스커피 코리아 애플리케이션 버전 업데이트 추가 안내</a
>
</div>
<div class="swiper-slide">
[당첨자 발표] 뉴이어 전자영수증 이벤트
</div>
</div>
</div>
<a href="javascript:viod(0)" class="notice-line__more">
<span class="material-symbols-outlined"> add_circle </span>
</a>
</div>
- 최상위 부모의 클래스로는
.swiper-container
를 가지고 그 자식 요소로는.swiper-wrapper
를 가지도록 한다. - 각 슬라이드 요소는
.swiper-slide
로 클래스를 부여한다.
수직 슬라이드 동작 구현
new Swiper(".notice-line .swiper-container", {
direction: "vertical",
autoplay: true,
loop: true,
});
- Swpierjs에서 제공하는 Swiper클래스의 인스턴스를 생성한다.
- 생성자에 swiper container요소의 선택자와 옵션(객체)을 전달한다.
- 수직 슬라이드를 위해
direction: “vertical”
로 설정한다. autoplay: true
를 통해 자동재생을 활성화한다.loop: true
를 통해 마지막 슬라이드에 도달하면 다시 첫번째 슬라이드로 넘어가도록 활성화한다.
결과
프로모션 이미지 좌우 슬라이드 적용하기
슬라이드 요소 중앙 배치
.notice .promotion .swiper-container {
width: calc(819px * 3 + 20px);
height: 533px;
position: absolute;
top: 40px;
left: 50%;
/* margin-left: calc((819px * 3 + 20px) / -2); */
transform: translateX(-50%);
}
.swiper-container
너비를calc
를 통해 계산한다.calc(하나의 슬라이드 너비 819px * 슬라이드 개수 + 여백 20px)
swiper-container
를 중앙으로 배치하기 위해 너비를 -2로 나눈 값을 왼쪽 여백으로 설정한다.
좌우 슬라이드 동작 구현
new Swiper(".promotion .swiper-container", {
direction: horizontal
slidesPerView: 3,
spaceBetween: 10,
centeredSlides: true,
loop: true,
autoplay: {
delay: 5000,
},
});
slidesPerView
를 통해 한번에 보여줄 슬라이드 개수를 설정한다.spaceBetween
을 통해 슬라이드 간의 여백을 설정한다.centeredSlides
를 통해 슬라이드를 중앙으로 배치한다.autoplay
에서 세부 옵션(객체)를 통해delay
를 부여하여 지연시간을 설정한다.
투명도 설정으로 슬라이드 집중시키기
.notice .promotion .swiper-slide {
opacity: 0.2;
}
.notice .promotion .swiper-slide-active {
opacity: 1;
}
.swiper-slide-active
클래스가 있으면 불투명하게 설정하고, 나머지 슬라이드는 투명하게 처리하여 현재 슬라이드에 집중시키도록 디자인한다.
결과
페이지네이션 & arrow 버튼 추가 하기
페이지네이션 & arrow 버튼 HTML 구조
<div class="swiper-pagination"></div>
<div class="swiper-prev"></div>
<div class="swiper-next"></div>
.swiper-pagination
은 페이지 번호를 의미하는 선택자이다..swiper-prev
와.swiper-next
는 각각 arrow 버튼의 이전과 다음을 의미하는 선택자이다.
페이지네이션 & arrow 버튼 요소 지정
new Swiper(".promotion .swiper-container", {
slidesPerView: 3,
spaceBetween: 10,
centeredSlides: true,
loop: true,
// autoplay: {
// delay: 5000,
// },
pagination: {
el: ".promotion .swiper-pagination",
clickable: true,
},
navigation: {
prevEl: ".promotion .swiper-prev",
nextEl: ".promotion .swiper-next",
},
});
pagintaion
속성에el
페이지 번호 요소 선택자인.promotion .swiper-pagination
을 설정하고, 페이지 번호를 누르면 제어할 수 있도록clickable
속성을true
로 설정한다.navitaion
속성에prevEl
과nextEl
에 각 arrow 버튼 요소 선택자인.promotion .swiper-prev
와.promotion .swiper-next
를 설정한다.
결과
슬라이드 영역 토글 버튼 추가하기
토글 버튼을 클릭하면 프로모션 영역이 숨겨지거나 보여지도록 하는 역할을 구현한다.
토글 버튼 요소 추가 및 토글 구현하기
const promotionEl = document.querySelector(".promotion");
const promotionToggleBtn = document.querySelector(".toggle-promotion");
let isHidePromotion = false;
promotionToggleBtn.addEventListener("click", () => {
isHidePromotion = !isHidePromotion;
if (isHidePromotion) {
promotionEl.classList.add("hide");
return;
}
promotionEl.classList.remove("hide");
});
- 프로모션 영역에 해당하는 요소 .promotion 선택자와 토글 버튼에 해당하는 요소 .toggle-promotion를 추가한다.
- 토글의 상태를 의미하는 isHidePromotion에 초기 boolean 상태값 false를 할당해놓는다.
- 토글 버튼이 클릭되면 기존
isHidePromotion
상태값의 반대값을 할당하고false
이면 promotion 요소에.hide
클래스를 추가하고true
이면.hide
클래스를 제거한다.
.hide
클래스 정의
.notice .promotion {
position: relative;
height: 693px;
background-color: #f6f5ef;
transition: height 0.4s;
overflow: hidden;
}
.notice .promotion.hide {
height: 0;
}
- 요소에
.hide
클래스가 추가되면height
를0
으로 설정한다. transition
을 통해height
값을 대상으로 0.4초 지연시키도록 애니메이션 처리한다.height
값이 줄어드면 내부 요소가 외부 영역에 침범하지 않아야하므로overflow: hidden
을 설정해준다.
결과
9️⃣ 유튜브 영상 배경
유튜브 영상은 보통 16:9 비율 화면의 크기로 제공되고 있다.
유튜브 영상을 배경으로 설정하려면 해당 비율에 대한 width
값과 height
값을 설정해야한다.
이는 부모요소와 자식요소의 관계를 통해 설정이 가능한데 해당 내용에 대한 예시는 다음과 같다.
<div class="container">
<div class="item"></div>
</div>
.container {
width: 500px;
background-color: royalblue;
}
.container .item {
width: 100%;
height: 0;
padding-top: 50%;
}
부모요소의 container에서의 width
값과 자식요소 item의 padding-top
을 각 2:1 비율로 설정한다고 가정할 때, 자식요소는 부모요소의 width
인 500px
을 기준으로 padding-top
에 설정한 50%값인 250px
를 갖게된다.
즉, 자식요소의 %크기는 부모요소의 크기를 기준으로 값이 설정되어진다는 것을 알 수 있고, 이를 활용하여 비율 설정이 가능하다.
유튜브 영상 배경 구조
<section class="youtube">
<div class="youtube__area">
<div id="player"></div>
</div>
<div class="youtube__cover"></div>
</section>
.youtube__area
는 유튜브 영상이 보여지는 영역을 의미한다.#player
는 유튜브 영상에 해당하는 영역을 의미한다..youtube__cover
는 영상 위에 덮여지는 이미지 패턴 영역을 의미한다.
유튜브 영상 배경 스타일링
유튜브 영상이 보일 영역은 16:9 크기만큼 설정해놓고, 유튜브 영상을 중앙배치하여 영상의 중앙 부분만 보일수 있게 스타일링 한다.
.youtube {
position: relative;
height: 700px;
background-color: #333;
overflow: hidden;
}
.youtube .youtube__area {
width: 1920px;
background-color: orange;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.youtube .youtube__area::before {
content: "";
display: block;
width: 100%;
height: 0;
padding-top: 56.25%;
}
.youtube .youtube__cover {
background-image: url("../images/video_cover_pattern.png");
background-color: rgba(0, 0, 0, 0.3);
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.youtube
에overflow: hidden
을 하여, 중앙을 제외한 위, 아래 영역을 외부 영역으로 침범하지 않도록 설정한다.
YouTube Player API
IFrame Player API를 사용하면 YouTube 동영을 가져오고 제어할 수 있다.
iframe 삽입에 대한 YouTube Player API 참조 문서 | YouTube IFrame Player API | Google for Developers
애플리케이션에 YouTube 플레이어를 삽입합니다.
developers.google.com
라이브러리에서 제공하는 예시코드를 살펴보면 onYouTubeIframeAPIReady()
메서드를 통해 YT
객체에 있는 Player()
메서드를 호출해서 제공하는 옵션들을 객체 형태로 전달하고 있는 것을 확인할 수 있다.
또한 HTML에서 정의한 player
를 id
로 갖는 태그에 해당 영상을 삽입하는 기능을 수행한다는 것을 확인할 수 있다.
이전에 해당 요소를 마크업해놓았기 때문에 위 코드를 참고하여 다음과 같이 적용해보았다.
const tag = document.createElement("script");
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName("script")[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
function onYouTubeIframeAPIReady() {
new YT.Player("player", {
videoId: "aCkI0tjzUbQ",
playerVars: {
autoplay: true,
loop: true,
playlist: "aCkI0tjzUbQ",
},
events: {
onReady: (event) => {
event.target.mute();
},
},
});
}
- 첫번째
script
태그를 지정하고 그 이전 위치에 라이브러리를 갖고오는script
태그를 삽입할 수 있도록 한다. onYouTubeIframeAPIReady()
함수 이름은 Youtube Player API에서 사용하는 이름이기 때문에 다른 함수명으로 작성하면 동작하지 않는다. 추가로 함수는 전역으로 등록해야한다.YT
객체에 있는Player()
메서드를 호출하여 옵션들을 파라미터로 전달한다.videoId
는 최초로 재생할 유튜브 영상의 Id를 입력한다. 유튜브 영상의 Id는 특정 유튜브 영상의 url에 key가 v인 값에 해당한다.playerVars
를 통해autoplay
(자동재생),loop
(반복),playlist
(반복을 위한 플레이리스트 ID)를 설정한다.
더 많은 플레이어 매개변수를 확인하려면 다음 링크를 참조한다.events
에서onReady
메서드를 통해 영상이 준비되면mute()
메서드가 호출되어 영상을 음소거 처리하도록한다.
YouTube 내장 플레이어 및 플레이어 매개변수 | YouTube IFrame Player API | Google for Developers
YouTube 내장 플레이어 및 플레이어 매개변수 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 개요 이 문서에서는 애플리케이션에 YouTube 플레이어를 삽입하는
developers.google.com
결과
아이콘 반복 애니메이션 적용하기(feat. gsap)
유튜브 영상 배경화면 위에 아이콘 요소들을 통통튀는 효과의 애니메이션을 부여하려고 한다.
먼저 3개의 아이콘에 해당하는 요소들을 마크업한다.
각 아이콘 마크업 및 배치 시키기
<div class="inner">
<img
src="./images/floating1.png"
alt="Icon"
class="floating floating1"
/>
<img
src="./images/floating2.png"
alt="Icon"
class="floating floating2"
/>
<img
src="./images/floating3.png"
alt="Icon"
class="floating floating3"
/>
</div>
- 각 아이콘에 해당하는 이미지를 이미지 경로에 작성한다.
- class는 공통 스타일링에 해당하는
.floating
과 각 요소마다의 위치를 다르게 배치해야하므로, 넘버를 달리 하여 클래스를 부여한다.
.youtube .floating1 {
position: absolute;
top: 50px;
left: 0;
}
.youtube .floating2 {
position: absolute;
top: 350px;
left: 150;
}
.youtube .floating3 {
position: absolute;
bottom: -200px;
right: 0;
}
- 부모 요소인
.inner
를 기준으로 각 아이콘의position
을absolute
로 설정하여 배치시킨다.
애니메이션 적용하기
각 요소에 애니메이션을 간단하게 적용하기 위해 gsap
라이브러리를 활용하여 구현한다.
function floatingObject(selector) {
gsap.to(selector, 1, {
y: 20,
repeat: -1,
yoyo: true,
});
}
floatingObject(".floating");
- floatingObject 함수를 만들고 매개변수로 각 아이콘에 공동 클래스에 해당하는 .floating을 요소로 전달한다.
- gsap의 to()메서드를 호출하고 동작 시간을 1s로 하여 각 요소에 애니메이션을 부여한다.
- 옵션으로는 각 아이콘을
y
로 20px 이하로 움직인다. 추가로repeat
값을-1
로 하여 애니메이션을 거꾸로 설정하고,yoyo
를true
로 하여 해당 애니메이션을 반복적으로 수행되어지도록 설정한다.
Easing | GSAP | Docs & Learning
"slow", "rough", and "expoScale" eases are not in the core - they are packaged together in an EasePack file in order to minimize file size. "CustomEase", "CustomBounce", and "CustomWiggle" are packaged independently as well (not in the core).
gsap.com
애니메이션의 변화를 주기 위해 gsap에서 제공하는 easing 페이지를 참고하였다.
해당 페이지를 참고하면 다양한 에니메이션을 그래프 형태로 preview를 확인할 수 있다.
추가로 type을 따로 설정하여 in, out을 설정할 수 있다.
원하는 core와 type을 선택하고 ease 속성에 대한 값을 복사한다. 본인은 power1를 in-out을 적용해보았다.
function floatingObject(selector) {
gsap.to(selector, 1, {
y: 20,
repeat: -1,
ease: "power2.inOut",
yoyo: true,
delay: 1,
});
}
- ease의 값을 복사한 값인 power2.in으로 설정한다.
추가적으로 애니메이션과 각 요소의 동작 지연시간을 랜덤으로 부여하기 위해 다음과 같이 랜덤한 값을 반환하는 함수를 정의하였다.
function random(min, max) {
return parseFloat((Math.random() * (max - min) + min).toFixed(2));
}
function floatingObject(selector, delay, size) {
gsap.to(selector, random(1.5, 2.5), {
y: size,
repeat: -1,
yoyo: true,
ease: "power2.in",
delay: random(0, delay),
});
}
floatingObject(".floating1", 1, 15);
floatingObject(".floating2", 0.5, 15);
floatingObject(".floating3", 1.5, 20);
- 요소, 지연시간,
y
의 위치를 인자로 받을 수 있다록 매개변수를selector
,delay
,size
로 정의하였다. - 애니메이션 동작시간에
random()
함수를 호출하고 1.5에서 2.5사이의 값을 반환받을 수 있도록 한다. y
의 위치를 인자로 받은size
로 설정한다.delay
는 random함수를 호출하여 0에서delay
사이의 값을 받을 수 있도록 한다.
결과
✍️ 소감
이번 주차는 Swiperjs, YouTube Player API 같은 오픈소스를 처음 접해보게 되었다.
Swiperjs 라이브러리를 이용하면, 요소들을 간편하게 수평이나 수직으로 슬라이드로 구현할 수 있다는 것을 알게 되었다.
또한 YouTube Player API를 활용하면 원하는 유튜브 영상의 ID만 있으면 쉽게 해당 영상을 가져올 수 있으며, 사용자에게 영상을 보여주기 전에 필요한 설정을 쉽게 할 수 있다는 것을 깨달았다.
마지막으로 GSAP의 easing을 활용하면 다양한 애니메이션과 타입을 사용하여 요소에 보다 디테일한 애니메이션을 적용할 수 있다는 것을 알게 되었다.
'🍪 카카오 테크 캠퍼스 2기' 카테고리의 다른 글
[카카오 테크 캠퍼스 2기] 1단계 4주차 WIL - JavaScript 핵심 (0) | 2024.05.06 |
---|---|
[카카오 테크 캠퍼스 2기] 1단계 4주차 WIL - 스타벅스 예제 클론하기(4) (0) | 2024.05.03 |
[카카오 테크 캠퍼스 2기] 1단계 2주차 WIL - 스타벅스 예제 클론하기(2) (0) | 2024.04.21 |
[카카오 테크 캠퍼스 2기] 1단계 1주차 WIL - 스타벅스 예제 클론하기(1) (0) | 2024.04.15 |
[카카오 테크 캠퍼스 2기] 프론트엔드 최종 합격 후기 (2) | 2024.04.07 |