이 글은 리팩토링 버전이다. 원문은 이쪽.
요청 아닌 요청(...?)을 받아 오랜만에 위 코드를 봤는데 이게 웬걸... 가독성도 엉망에 유지보수성도 꽝인 스파게티 그 자체길래 이번에 코드를 뜯어 고치기로 했다.
그김에 기존에 60일 제한이 있던 토큰을 새로고침해주는 기능도 넣어준 건 덤.
토큰 새로 받기 귀찮아서 블로그 타이틀에서 인스타 섹션 자체를 삭제했었는데(...) 다시 추가해준 것도 덤.
이전 코드와 같이, 달력 스킨을 사용한다면 아래 코드를 복사해서 토큰 값만 수정한 후, 스킨 내부 "</s_cover_group>" 코드 바로 위에 붙여 넣으면 잘 작동한다!
더보기
<div class="list">
<header class="all-header">
<div class="all-header-icon instagram-bg">
<div class="center">
<span id="main-content-list-type">
<i class="fab fa-instagram"></i>
</span>
<style>
.instagram-bg {
background: #f09433;
background: -moz-linear-gradient(
45deg,
#f09433 0%,
#e6683c 25%,
#dc2743 50%,
#cc2366 75%,
#bc1888 100%
);
background: -webkit-linear-gradient(
45deg,
#f09433 0%,
#e6683c 25%,
#dc2743 50%,
#cc2366 75%,
#bc1888 100%
);
background: linear-gradient(
45deg,
#f09433 0%,
#e6683c 25%,
#dc2743 50%,
#cc2366 75%,
#bc1888 100%
);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f09433', endColorstr='#bc1888', GradientType=1);
}
#instagram .thumb-content .text .tt {
font-size: 1.3em;
}
.insta-more-box {
border: 0;
opacity: 0.5;
transition: opacity 0.5s;
}
.insta-more-box:hover {
opacity: 0.7;
}
.con {
width: 80%;
height: 80%;
background-color: var(--base);
z-index: 1;
}
</style>
</div>
</div>
<div class="all-header-text">
<h2 class="title">
<span class="main-content-list-title">
<a id="instagram_a" target="_blank" href="/"> 인스타그램 </a>
</span>
</h2>
</div>
</header>
<div id="instagram" class="list-style default"></div>
</div>
<script>
const TARGET_QUERY = "#instagram"; // 타깃이 되는 jQuery 선택자
const TARGET_A_QUERY = "#instagram_a"; // 타깃이 되는 jQuery 선택자 (링크)
const INSTA_URL = "https://www.instagram.com/d_dl_illust";
const TOKEN = "내 토큰";
const FIRST_LOAD_COUNT = 7; // 처음에 로딩할 글 수
const ONCE_LOAD_COUNT = 8; // 한 번 클릭 시 추가될 글 수
const MAX_LOAD_COUNT = 23; // 최대로 로딩할 글 수
const API_URI = "https://graph.instagram.com";
function getInstaApiDataUrl(token) {
return `${API_URI}/me/media?access_token=${token}&fields=id,caption,media_type,media_url,thumbnail_url,permalink,timestamp`;
}
function getInstaApiRefreshUrl(token) {
return `${API_URI}/refresh_access_token?grant_type=ig_refresh_token&access_token=${token}`;
}
function insta_fetchAjax(
url,
successFunction = (res) => {},
errorFunction = (err) => {}
) {
$.ajax({
type: "GET",
dataType: "jsonp",
cache: true,
url: url,
success: successFunction,
error: errorFunction,
});
}
let nowLoadedPostCount = 0; // 현재 로딩된 포스트 개수
let wantToLoadPostCount = 0; // 현재 로딩되길 바라는 포스트 개수
let allInstaData = []; // 현재 로딩된 전체 데이터 배열
function insta_add_list(res) {
if (res.data == undefined || res.data.length <= 0) {
// if api error
insta_show_fallback();
return;
}
// 아래 줄에서 어떤 타입의 글을 보여주고, 보여주지 않을 지 결정.
// data에 어떤 유형이 있는지/어떤 유형을 추가할 수 있는지는 api 문서 참고
// https://developers.facebook.com/docs/instagram-basic-display-api/reference/media#fields
allInstaData.push(
...res.data.filter((data) => data.media_type !== "VIDEO")
);
if (wantToLoadPostCount < FIRST_LOAD_COUNT)
wantToLoadPostCount = FIRST_LOAD_COUNT;
else if (wantToLoadPostCount <= nowLoadedPostCount)
wantToLoadPostCount += ONCE_LOAD_COUNT;
if (wantToLoadPostCount > MAX_LOAD_COUNT)
wantToLoadPostCount = MAX_LOAD_COUNT;
if (wantToLoadPostCount > allInstaData.length) {
insta_next_list(res.paging.next);
return;
}
for (; nowLoadedPostCount < wantToLoadPostCount; nowLoadedPostCount++) {
if (allInstaData[nowLoadedPostCount] == undefined) break;
insta_add_component(allInstaData[nowLoadedPostCount]);
}
// 마지막 버튼 생성
if (res.paging.next == undefined || nowLoadedPostCount >= MAX_LOAD_COUNT) {
// 만약 페이징 다음이 없거나(보여줄 글이 더이상 없거나) || 현재 로딩된 포스트가 최대 로딩할 포스트 수와 같을 경우
// 인스타로 갈 수 있도록 버튼 내용을 변경.
insta_add_button(`window.open('${INSTA_URL}')`, "계정에서<br />더보기");
} else {
insta_add_button(
`insta_next_list('${res.paging.next}'); this.remove();`, // 다음 목록을 추가하고 버튼을 삭제.
"더보기"
);
}
}
function insta_add_component(data) {
const item = insta_data_convert_data(data);
const postHtml = `
<div class="thumb-box">
<div class="thumb bg-cover" style="background-image: url('${
item.image_url
}')">
<div class="thumb-content">
<div class="text center">
<a class="tt" target="_blank" rel="noopener" href="${
item.permalink
}">
${item.caption}
</a>
</div>
<div class="point-box instagram-bg">
<div class="point-date title">
${item.year} <br />
${item.month + item.date}
</div>
</div>
</div>
</div>
</div>
`;
$(TARGET_QUERY).append(postHtml);
}
function insta_data_convert_data(item) {
// ajax로 가져온 데이터를 html에 바로 넣을 수 있도록 예쁘게 가공한다.
let caption = item.caption.split("\n")[0]; // 첫 번째 줄만 자른다
let caption_maxLength = 20;
if (caption.length > caption_maxLength)
caption = caption.substring(0, caption_maxLength) + "…";
// 이미지 대표 url 가져오기
let image_url = "";
if (item.media_type === "VIDEO") {
image_url = item.thumbnail_url;
} else {
image_url = item.media_url;
}
// 날짜를 텍스트로 계산하기 위해 new Date로 time 생성
const time = new Date(Date.parse(item.timestamp));
// 가공한 데이터 리턴
return {
image_url: image_url,
permalink: item.permalink,
caption: caption,
year: time.getFullYear(),
month: String(time.getMonth() + 1).padStart(2, "0"),
date: String(time.getDate()).padStart(2, "0"),
};
}
function insta_add_button(btn_action, btn_txt) {
const btnHtml = `
<button class="thumb-box insta-more-box" onclick="${btn_action}">
<div class="con title center"><span class="center">${btn_txt}</span></div>
<div class="thumb bg-cover instagram-bg"></div>
</button>
`;
$(TARGET_QUERY).append(btnHtml);
}
function insta_next_list(url) {
insta_fetchAjax(url, dataRefreshFunction, dataErrorFunction);
}
function insta_show_fallback() {
// 오류가 났을 경우 상황에 맞게 클래스 추가
$(TARGET_QUERY).addClass("loaded fallback");
}
/************************
* 실행부
************************/
const dataRefreshFunction = (res) => {
// console.log(res);
insta_add_list(res);
};
const dataErrorFunction = (err) => {
console.log(err);
insta_show_fallback();
};
$(TARGET_A_QUERY).attr("href", INSTA_URL);
insta_fetchAjax(
getInstaApiDataUrl(TOKEN),
dataRefreshFunction,
dataErrorFunction
);
insta_fetchAjax(
getInstaApiRefreshUrl(TOKEN),
(res) => {
// console.log("토큰 연장 성공 ");
},
dataErrorFunction
);
</script>
삼만 년만의 제이쿼리 작업...
코드도 짧은 편이고, 특별히 엄청난 기술이 들어간 것도 아니라서 한 번 쯤 도전해보기 좋은 작업 같다.
관련 문서
토큰 받는 법 : https://developers.facebook.com/docs/instagram-api/getting-started