์ฌ๋ฆฌ๋ธ์ ์จ๋ผ์ธ๋ชฐ (์ ์ํ PC, Mobile)
- -

๐ ์ฌ๋ฆฌ๋ธ์ ์จ๋ผ์ธ๋ชฐ
๐ ์ฌ์ดํธ๋ช
: ์ฌ๋ฆฌ๋ธ์ ์จ๋ผ์ธ๋ชฐ
๐ ์์
๊ธฐ๊ฐ : PC๋ฒ์ 7์ผ, ๋ชจ๋ฐ์ผ 1.5์ผ
๐ ์ฌ์ฉ์ธ์ด : HTML5, CSS3, Jquery, JSON
๐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ : Swiper
๐ ๋ถ๋ฅ : PC ์ ์ํ, Mobile ์ ์ํ ์น์ฌ์ดํธ
๐ URL :
PC > https://codenablog.github.io/Oliveyoung-pc/
Mobile > https://codenablog.github.io/Oliveyoung-mo/
โจ POINT
โ๏ธ Fetch ๋น๋๊ธฐ ํต์ ์ ํ์ฉํด JSON ํ์ผ์ ์ ๋ณด ๋ถ๋ฌ์ค๊ธฐ
โ๏ธ Filter๋ก ์ํ๋ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฅ
โ๏ธ 3๋จ ์นดํ
๊ณ ๋ฆฌ ๊ตฌ์ฑ
โ๏ธ ๋ค๋ฅธ์ํ ์ถ์ฒ, ๋ค๋ฅธ ํค์๋ ๋๋ณด๊ธฐ ๋ฒํผ ํด๋ฆญ์ ์๋ก์ด ์ ๋ณด ๋ถ๋ฌ์ค๊ธฐ
โ๏ธ Swiper ์ปค์คํ
- ๋ด์ฅ ์์ฑ 'thumbs' ํ์ฉ , ๊ทธ๋ฆฌ๋ ํํ์ ์ฌ๋ผ์ด๋ ๊ตฌํ
โ๏ธ ๋ชจ๋ฐ์ผ์์ ์ด๋ฏธ์ง height ๊ณ ์ ํ์ผ๋ก ๊ฐ์
โ๏ธ Swiper ์ปค์คํ
- ์ค์๊ฐ View ๋ญํน
๐ Fetch ๋น๋๊ธฐํต์ ํ์ฉ
event.json
json ํ์ผ์ ๋ง๋ค์ด ๋ถ๋ฌ์ฌ ์ ๋ณด๋ค์ ๋ด์์ฃผ์๋ค.
{
"items":[
{
"thumbUrl":"./assets/images/banner01.jpg",
"title":"ํ๋ก์ฐํ๋ฉด 20% ์ฟ ํฐ GET",
"linkUrl":"#banner01"
},
{
"thumbUrl":"./assets/images/banner02.jpg",
"title":"์ฌ๋ฆฌ๋ธ ๋ฉค๋ฒ์ค ํ๋ฐ๊ธฐ ํํ",
"linkUrl":"#banner03"
}
]
}
main.js
jsonํ์ผ์ ๋ด๊ธด ์ ๋ณด๋ฅผ ๋ถ๋ฌ์ค๊ธฐ!
fetch('./assets/data/event.json')
.then(res=>res.json())
.then(json=>{
data = json.items // ์ ์ฒด ๋ฐ์ดํฐ๋ฅผ data๋ผ๋ ๋ณ์์ ๋ด๊ธฐ
let html=``; // ๋ฐ์ดํฐ ์
๋ ฅ์ ์ํ ๋ณ์ ํ ๋น
data.forEach(element=>{
html+=`${element.title}` // json ํ์ผ์ items์ title ์ ๋ณด๋ฅผ ๋ถ๋ฌ์จ๋ค
})
})
๐ Filter๋ก ์ํ๋ ๊ฐ์ ๋ถ๋ฅ


product.json ํ์ผ์์ ๊ฐ๊ฐ EventId, brand ๊ฐ์ด ์ผ์นํ๋ ์ํ๋ง ๋ฝ์์๋ค!
event.json
{
"items":[
{
"id":"prd01",
"cate":"1001",
"brand":"์์คํธ๋ผ",
"productName":"์์คํธ๋ผ ์ํ ๋ฒ ๋ฆฌ์ด365 ๋ก์
150ml ๊ธฐํ ...",
"thumbUrl":"./assets/images/product01.jpg",
"best":true,
"cost":31000,
"sale":23560,
"tag":["์ธ์ผ","์ฟ ํฐ","์ฆ์ ","์ค๋๋๋ฆผ"],
"eventId":false,
"view":32,
"linkUrl":"#product1"
},
.
.
.
main.json
Filter๋ฅผ ํจ์๋ก ๋ง๋ค์ด์ ๋งค๊ฐ๋ณ์์ ์ํ๋ ๊ฐ์ ๋ฃ์ด ํธ์ถํด์ฃผ๋ ๋ฐฉ์์ผ๋ก ๋ฝ์์๋ค.
// ๋ธ๋๋๋ณ ์ํ ๋ฆฌ์คํธ ๋ถ๋ฌ์ค๊ธฐ
fetch('./assets/data/product.json')
.then(res=>res.json())
.then(json=>{
data = json.items;
function sortData(brandId,sortId){
// ๋ธ๋๋๊ฐ ์ผ์นํ๋ ์ํ ํํฐ๋ง
brandData = data.filter(function(parm){
return parm.brand === sortId;
})
let html=``;
brandData.forEach(element=>{
.
.
})
$(`.sc-brand .brand${brandId}`).html(html);
}
sortData(1,"๋น๋ฆฌํ");
sortData(2,"์๋ฒค๋");
sortData(3,"๋น๋ฆฌํ");
sortData(4,"์๋ฒค๋");
})
๐ 3๋จ Category ๊ตฌ์ฑ
๐ ๋๋ถ๋ฅ > ์ค๋ถ๋ฅ > ์๋ถ๋ฅ

์ ์ฒด ์นดํ
๊ณ ๋ฆฌ ๋ฒํผ์ ๋๋ฅด๋ฉด ๋์ค๋ ์์ญ์
"๋ทฐํฐ > ์คํจ์ผ์ด > ํ ๋/๋ก์
/์ฌ์ธ์" ํํ์ 3๋จ ๋ถ๋ฅ๋ก ๋์ด์๋ค.
category.js
์ค๋ถ๋ฅ๋ฅผ ๊ธฐ์ค์ผ๋ก category.json ํ์ผ์ ์์ฑํ๋ฉด์
์๋ถ๋ฅ๋ submenu๋ผ๋ key์ ๋ฐฐ์ด๋ก ๋ง๋ค์ด์ฃผ๊ณ ,
๋๋ถ๋ฅ๋ sort๋ผ๋ key๋ฅผ ๋ง๋ค์ด์ ํํฐ๋ก ๋ถ๋ฅํด์ฃผ์๋ค.
{
"items":[
{
"id":1,
"name":"์คํจ์ผ์ด",
"sort":"๋ทฐํฐ",
"submenu":[
{
"id":1001,
"name":"ํ ๋/๋ก์
/์ฌ์ธ์"
},
{
"id":1002,
"name":"์์ผ์ค/ํฌ๋ฆผ"
},
{
"id":1003,
"name":"๋ฏธ์คํธ/์ค์ผ"
}
]
},
.
.
.
main.js
// ์ ์ฒด ์นดํ
๊ณ ๋ฆฌ ๋ถ๋ฌ์ค๊ธฐ
fetch('./assets/data/category.json')
.then(res=>res.json())
.then(json=>{
data=json.items; // ์ ์ฒด ๋ฐ์ดํฐ
let html=``;
// ์นดํ
๊ณ ๋ฆฌ ์ถ๋ ฅ ํผ
function cateAll(sortId){
cateList = data.filter(function(parm){
return parm.sort == sortId; // ๋๋ถ๋ฅ ํํฐ
})
// ๋๋ถ๋ฅ
html+=`
<div class="group-cate">
<h3>${sortId}</h3>
<ul class="cate-list">
`
//์ค๋ถ๋ฅ
cateList.forEach(element => {
html+=`<li class="cate-item">
<a href="#${element.id}">${element.name}</a>
<ul class="sub-list">`
//์๋ถ๋ฅ
sub = element.submenu;
sub.forEach(element=>{
html+=`
<li class="sub-item"><a href="#${element.id}">${element.name}</a></li>
`
})
html+=`
</ul>
</li>
`
});
html+=`
</ul>
</div>
`
$(".gnb .cate-wrapper").html(html);
}
// ๋๋ถ๋ฅ ๊ฐ ๋ฃ์ด์ ์นดํ
๊ณ ๋ฆฌ ์ถ๋ ฅํผ ํธ์ถ
cateAll("๋ทฐํฐ");
cateAll("ํฌ์ค&ํธ๋");
cateAll("๋ผ์ดํ");
})
๐ ๋ค๋ฅธ์ํ ์ถ์ฒ, ๋ค๋ฅธ ํค์๋ ๋๋ณด๊ธฐ


๐ ์ถ์ฒ ์ํ, ํค์๋ ์์ญ์ ๋ฒํผ์ ๋๋ฅด๋ฉด ๋ค๋ฅธ ์ํ์ ํธ์ถํด์ผํ๋ค.
๐ ์ด ๋ถ๋ถ ์ญ์ ํจ์๋ฅผ ๋ง๋ค์ด์, ๋ฒํผ์ ๋๋ฅด๋ฉด ๋ค๋ฅธ ๋งค๊ฐ๋ณ์๊ฐ์ ๋ฃ์ ํจ์๋ฅผ ํธ์ถํ๋ ๋ฐฉ์์ผ๋ก ๋ง๋ค์๋ค.
current3 = 1; // ํ์ฌ ๊ฐ์ ์ ์ฅํ๊ธฐ์ํ ๋ณ์
function keywords(i,j){
fetch('./assets/data/keywords.json')
.then(res=>res.json())
.then(json=>{
data=json.items;
html=``;
while(i < j){
element = data[i];
html+=`
<li class="keyword-item">
<a href="${element.linkUrl}">
<figure class="img-box">
<img src="${element.thumbUrl}" alt">
</figure>
<div class="text-box">
<h3>${element.title}</h3>
<p>${element.subTitle}</p>
</div>
</a>
</li>
`
i++;
}
$('.sc-keyword .keyword-list').html(html);
});
}
keywords(0,2); //๊ธฐ๋ณธ์ผ๋ก ๋ณด์ฌ์ค ๊ฒ
$('.sc-keyword .other-btn').click(function(){
if(current3 == 1){
keywords(2,4);
current3 = 2;
}else if(current3 == 2){
keywords(4,6);
current3 = 3;
}else{
keywords(0,2);
current3 = 1;
}
$('.sc-keyword .other-btn .current').html(current3);
})
forEach ๋ฌธ๋ฒ์ ํ์ฉํด์ ๋ฆฌ์คํธ๋ฅผ ๋ฝ์์ฌ ๊ฒฝ์ฐ return true/false์ฌ๋ถ์ ๊ด๊ณ์์ด continue ๋๊ธฐ ๋๋ฌธ์ ์ํ๋ ๊ฐฏ์๋ง ๋ฝ์์ค๊ธฐ ์ํด while๋ฌธ๋ฒ์ ํ์ฉํ๋๋ฐ, ์ดํ ๋ ์ฐพ์๋ณด๋ try catch๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ๋ ์์๋ค.
๐ Swiper ์ปค์คํ
1. ํญ ๋ฉ๋ด๋ฅผ ์ฌ๋ผ์ด๋์ ์ฐ๋

Swiper.js Demo ํ์ด์ง์ Thumbs gallery๋ฅผ ํ์ฉํ๋ค.

ํญ ์ฌ๋ผ์ด๋์ ๋ด์ฉ ์ฌ๋ผ์ด๋๋ฅผ ๊ฐ๊ฐ ๋ฐ๋ก ๋ง๋ค์ด ์ค ํ, ์ฐ๋ํด์ฃผ๋ฉด ๋๋ค.
const brandSlider = new Swiper(".sc-brand .swiper",{
loop:true,
spaceBetween:30,
navigation:{
nextEl:".sc-brand .next",
prevEl:".sc-brand .prev"
},
pagination:{
el:".sc-brand .pagination",
type: "fraction"
},
//์ด ๋ถ๋ถ!!
thumbs: {
swiper: brandTab,
}
})
2. Grid ํํ์ ์ฌ๋ผ์ด๋

main.js
swiper์ grid ์์ฑ์ ์ด์ฉํด์ ๋ง๋ค์๋ค.
$(`.sc-dessert .prd-list`).html(html)
const dessertSlider = new Swiper("#dessertSlider",{
slidesPerView:3,
slidesPerGroup:3,
spaceBetween:8,
grid:{
rows:2
},
pagination:{
el:".sc-dessert .pagination"
},
})
๊ทธ๋ฆฌ๋ ์ฌ๋ผ์ด๋๋ฅผ ๋ง๋ค๋ ์ฃผ์ํ ์ ์, .swiper-slide์ ๋ถ๋ชจ ์์์ธ .swiper-wrapper์ ๋์ด๊ฐ์ด ๊ผญ ์ง์ ๋์์ด์ผ ์ ์์ ์ผ๋ก ์๋ํ๋ค.
์์ ์ํ ๋ฆฌ์คํธ์ ๊ฒฝ์ฐ ํ๋ฉด ๋๋น์๋ฐ๋ผ ์ ๋์ ์ผ๋ก ๊ฐ์ด ๋ฐ๋๊ธฐ๋๋ฌธ์, calc()๋ก ์ ํํ ๊ฐ์ ๊ณ์ฐํด์ ๋์ด๊ฐ์ ๋ฃ์ด์ฃผ์๋ค !
CSS
/* swiper-wrapper */
.sc-dessert .prd-list{
gap: 0;
min-height: 456px;
height: calc(((100vw - 30px) / 3 + 118px) * 2);
/* ((ํ๋ฉด๋๋น - ์์ margin) / 3์ด +textbox์ ๋์ด) * 2ํ*/
}
/* swiper-slide */
.sc-dessert .prd-item{
width: 100%;
margin-top:0 !important;
min-height: 228px;
height: calc((100vw - 30px) / 3 + 118px);
/* (ํ๋ฉด๋๋น - ์์ margin) / 3์ด +textbox์ ๋์ด) */
}
3. ์ค์๊ฐ View ๋ญํน

โ 1-5์ ์ํ์ด ์ฒซ๋ฒ์งธ ์ฌ๋ผ์ด๋, 6-10์ ์ํ์ด ๋๋ฒ์งธ ์ฌ๋ผ์ด๋์ ์๋ค.
โก 3์ด์ ํ๋ฒ์ฉ ์์ดํ
์ ํฌ์ปค์ค๊ฐ ์ด๋ํ๊ณ , ํฌ์ปค์ค๊ฐ ํด๋น ์ฌ๋ผ์ด๋์ ๋๊น์ง ์ด๋ํ๋ฉด ๋ค์ ์ฌ๋ผ์ด๋๋ก ๋์ด๊ฐ๋ค.
ํ์ด์ง ํน์ ์์ดํ
์ ํด๋ฆญํ๋ฉด ์ด๊ธฐํํ๊ธฐ ์ํด ํจ์์ ๋ด์์ค ๋ค์ ํธ์ถํ๋ค.
// 3์ด์ ํ๋ฒ์ฉ ์๋์ผ๋ก ๋์ด๊ฐ
function rankAuto(){
rankFocus++;
if(rankFocus == 11){
rankFocus = 1;
}
// rankCurr ๊ฐ์ด ๋ฐ๋๋ฉด
if(rankFocus < 6){
rankSlider.slideTo(0);
$('.ranking-prd a').removeClass('active');
$(`.sc-ranking .rank${rankFocus}`).addClass('active');
}else if(rankFocus < 11){
rankSlider.slideTo(1);
$('.ranking-prd a').removeClass('active');
$(`.sc-ranking .rank${rankFocus}`).addClass('active');
}
}
interval = setInterval(rankAuto, 3000);
โข ์์ดํ ์ ๋ชฉ์ ํด๋ฆญํ๋ฉด ํฌ์ปค์ค๊ฐ ์ด๋ํ๋ค. (์ด๋ setInterval์ ํ์ด๋จธ ์ด๊ธฐํ)
// ์์ดํ
ํด๋ฆญ์
$('.sc-ranking .rank-item').click(function(a){
a.preventDefault();
rankFocus = Number($(this).attr('href').charAt(1)*10) + Number($(this).attr('href').charAt(2))
i = rankFocus ;
$('.ranking-prd a').removeClass('active');
$(`.sc-ranking .rank${rankFocus}`).addClass('active');
clearInterval(interval);
interval = setInterval(rankAuto, 3000);
})
โฃ ํ์ด์ง ํด๋ฆญ์์ ์ฌ๋ผ์ด๋๊ฐ ๋์ด๊ฐ๋ค. (setInterval์ ํ์ด๋จธ ์ด๊ธฐํ)
let rankFocus = 1; //ํ์ฌ ๊ฐ์ ์ ์ฅํ๋ ๋ณ์
$('.sc-ranking .rank1').addClass('active');//์ฒซ๋ฒ์งธ ์์ดํ
์ ๊ธฐ๋ณธ ํฌ์ปค์ค
// ํ์ด์ง ํด๋ฆญ์
$('.sc-ranking .swiper-pagination-bullet').click(function(a){
a.preventDefault();
i = $(this).index();
rankSlider.slideTo(i);
if(i == 0){
rankFocus = 1;
}else{
rankFocus = 6;
}
$('.ranking-prd a').removeClass('active');
$(`.sc-ranking .rank${rankFocus}`).addClass('active');
clearInterval(interval); //ํด๋ฆญ์ ์ธํฐ๋ฒ์ ํ์ด๋จธ ์ด๊ธฐํ
interval = setInterval(rankAuto, 3000); // ํ์ด๋จธ ๋ค์ ์์
})
๐ ๋ชจ๋ฐ์ผ์์ ์ด๋ฏธ์ง height ๊ณ ์ ํ์ผ๋ก ๊ฐ์
๐ ๊ฐ์ ์ , ๊ธฐ์กด ์ฌ๋ฆฌ๋ธ์ ์ฌ์ดํธ
ํ๋ฉด ๋๋น๊ฐ ์ค์ด๋ค๋ฉด ๋ฐฐ๋์ height๋ ๋ฐ๋ผ์ ์ค์ด๋ ๋ค.

๐ ๊ฐ์ ํ
ํ๋ฉด ๋๋น๊ฐ ์ค์ด๋ค์ด๋ height๋ ๊ทธ๋๋ก ์ ์ง๋๋ค.
์ด๋ฏธ์ง์ object-fit:cover๋ฅผ ์ฃผ์ด ๋์น๋ ๋ถ๋ถ์ ๊น๋ํ๊ฒ ์๋ฆฌ๋๋ก ํ๋ค.

๐ Healthy life CSS ๊ฐ์


๊ธฐ์กด ์ฌ๋ฆฌ๋ธ์ ์ฌ์ดํธ์์ Healthy ์์ญ์ ํ
์คํธ ๋ฐ์ค ๋ฐฐ๊ฒฝ์ด๋ฏธ์ง๊น์ง ํต์ผ๋ก ์ฐ๊ณ ์๋ค.
๊ทธ๋ฐ๋ฐ ์ด ๋ถ๋ถ์ด ๋ง์ฐ์ค๋ฅผ hover ํ๋ฉด scale์ด ์ด์ง ์ปค์ง๋ ๋ชจ์
์ด ๋ค์ด๊ฐ๋๋ฐ, ๊ทธ๋ ๋ฐฐ๊ฒฝ์๊ณผ ์ด๋ฏธ์ง์ ๊ฒฝ๊ณ๋ถ๋ถ์ด ์ข ๋ญ๊ฐ์ ธ์ ๊ฐ์ ํ๊ธฐ๋ก ํ๋ค.

ํด๋ก ์ฝ๋ฉ ์์
ํ ๋ ํ์ํ ์ด๋ฏธ์ง ๋ถ๋ถ๋ง ์๋ผ์ ์
๋ก๋ํ๊ณ , ์์ ๋ฐฐ๊ฒฝ ๋ถ๋ถ์ CSS์์ Background๋ก ์ฒ๋ฆฌํ๋ค.
(+ ISSUE ) ์ฌ๋ผ์ด๋๋ then()ํจ์ ์์ชฝ์ ..๐ฆ
ํจ์ ๋ฐ๊นฅ์ชฝ์ ์ฌ๋ผ์ด๋ ํจ์๋ฅผ ์ฐ๊ฒ๋๋ฉด ๋์ ์ผ๋ก ์์ฑ๋ ์์๋ฅผ ์ฌ๋ผ์ด๋ ํจ์๊ฐ ๊ฐ์งํ์ง ๋ชปํด ๊ฐ ์ฌ๋ผ์ด๋๋ง๋ค ์ธ๋ฑ์ค ๊ฐ์ด NaN์ผ๋ก ๋จ๊ณ ํ์ด์ง์ด ์ ๋๋ก ๋์ํ์ง ์๋๋ค.
์ด๊ฑฐ๋๋ฌธ์ ํ์ฐธ ํค๋งธ๋ค.๐คข ์ค๋ ์๊ฐ์ ์์นญํด๋ณธ ๋์ ํด๊ฒฐ ์๋ฃ ๐ฆ
'Code Review' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
์ํฐํ๋ผ์ด์ฆ๋ธ๋ก์ฒด์ธ (0) | 2024.01.10 |
---|---|
๋ค์ฐ์คํผ์ค (0) | 2023.09.19 |
NAVER Careers (0) | 2023.02.25 |
์์ธ์์ฒญ (0) | 2023.02.17 |
๋น์ ์ด ์ข์ํ ๋งํ ์ฝํ ์ธ
์์คํ ๊ณต๊ฐ ๊ฐ์ฌํฉ๋๋ค