11
30

 

이 글은 리팩토링 버전이다. 원문은 이쪽.

 

 

요청 아닌 요청(...?)을 받아 오랜만에 위 코드를 봤는데 이게 웬걸... 가독성도 엉망에 유지보수성도 꽝인 스파게티 그 자체길래 이번에 코드를 뜯어 고치기로 했다.

 

그김에 기존에 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

 

시작하기 - Instagram 플랫폼 - 문서 - Meta for Developers

다음 단계 앱에 필요한 모든 권한에 대해 앱 검수 절차와 요청 승인을 완료하여 앱이 라이브 모드일 때 앱 사용자가 권한을 부여할 수 있도록 합니다.앱을 라이브 모드로 전환하고 잠재적 사용

developers.facebook.com

 

토큰 연장 관련 글 : https://developers.facebook.com/docs/instagram-basic-display-api/guides/long-lived-access-tokens#refresh-a-long-lived-token

 

장기 실행 토큰 - Instagram 플랫폼 - 문서 - Meta for Developers

장기 실행 토큰 새로 고침 GET /refresh_access_token 엔드포인트를 사용하여 만료되지 않은 장기 실행 Instagram 사용자 액세스 토큰을 새로 고침합니다. 장기 실행 토큰을 새로 고침하면 다시 60일간 유

developers.facebook.com

 

COMMENT