Notice
Recent Posts
Recent Comments
Link
«   2026/05   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Archives
Today
Total
관리 메뉴

insight00-15 님의 블로그

Instagram API 연동 with Instagram Login 본문

개발

Instagram API 연동 with Instagram Login

insight00-15 2026. 4. 20. 21:30

Spring Boot에서 Instagram OAuth 연동하고 게시물 인사이트 조회하기

https://developers.facebook.com/docs/instagram-platform?locale=ko_KR

 

Instagram 플랫폼 - 문서 - Meta for Developers

Instagram Platform The Instagram Platform from Meta provides tools to help your app user's interact with Instagram users. Instagram API with Business Login for Instagram The Instagram API with Instagram Login allows users of your app to access data in thei

developers.facebook.com

 

 

  이번 프로젝트에서는 뮤지션이 음악 홍보 페이지를 만들고, Instagram 게시물을 통해 발생한 성과를 확인하는 기능을 구현하고 있다.

 

  이번 작업의 목표는 다음이었다.

 

  Instagram OAuth로 뮤지션 가입/로그인

  -> long-lived access token 저장

  -> Instagram 게시물 목록 조회

  -> 게시물별 공유수, 프로필 방문수 조회

 

  ———

 

  1. 왜 Instagram OAuth를 로그인으로 사용했나

 

  처음에는 일반 회원가입 후 Instagram을 연동하는 방식을 생각했다.

 

  이메일 회원가입

  -> 로그인

  -> Instagram 연동

 

  하지만 현재 MVP의 핵심 사용자는 뮤지션이고, 뮤지션은 Instagram 계정이 있어야 서비스를 제대로 사용할 수 있다. 그래서 별도 회원가입을 먼저 만들기보다, Instagram OAuth 자체를 가입/로그인 수단으로 사용했다.

 

  최종 흐름은 이렇게 잡았다.

 

  Instagram OAuth 성공

  -> Instagram 계정 정보 조회

  -> DB에 해당 Instagram 계정이 없으면 Musician으로 저장

  -> OAuth에서 발급받은 long-lived access token 등 인스타와 연동할때 필요한 정보들 DB에 instagram_connection으로 저장

  -> 이미 있으면 기존 Musician 재사용 + token 갱신

 

  ———

 

  2. 초기 Instagram OAuth redirect uri 세팅

 

  개발 환경에서는 ngrok을 사용했다.

  • ngrok은 로컬에서 실행 중인 서버를 외부에서 접근 가능한 HTTPS 주소로 노출해주는 터널링 도구다.
  • 로컬 서버 주소인 localhost:8080만으로는 Instagram OAuth redirect 테스트가 불안정했고, http로는 redirect URI를 등록이 지않는 문제가 있어서 ngrok을 사용하였다.

  Spring Boot: http://localhost:8080

  ngrok: https://xxxx.ngrok-free.dev -> http://localhost:8080

 

  Meta Developer App에는 아래 redirect URI를 등록했다.

  https://xxxx.ngrok-free.dev/instagram/oauth/callback

  • Meta Developer App에서 redirect URI를 등록하면 제공해주는 OAuth 로그인 URL 링크를 타고 들어가서 인스타로 연동을 해주면, https://xxxx.ngrok...callback 링크로 redirect를 한 뒤 acceses token을 발급받는데 중요한 code 번호를 보내준다. 

  내가 사용한 OAuth 로그인 URL은 다음 형태다.

  https://www.instagram.com/oauth/authorize

    ?force_reauth=true

    &client_id={CLIENT_ID}

    &redirect_uri={REDIRECT_URI}

    &response_type=code

    &scope=instagram_business_basic%2Cinstagram_business_manage_insights

 

  이번 기능에 필요한 scope는 두 개만 사용했다.

 

  instagram_business_basic

  instagram_business_manage_insights

 

  ———

 

  3. 구현한 API

 

  3.1 Instagram OAuth Callback

 

  GET /instagram/oauth/callback?code=...

 

  역할:

 

  Instagram에서 받은 authorization code 처리

  short-lived token 발급

  long-lived token으로 교환

  Instagram /me 조회

  뮤지션 자동 생성 또는 기존 연결 갱신

 

  성공 응답:

 

  {

    "success": true,

    "data": {

      "musicianId": 1,

      "instagramAccountId": "17841464237580902",

      "instagramUsername": "robbi_developer"

    },

    "message": "Instagram 로그인이 완료되었습니다."

  }

 

  테스트 결과 첫 로그인 시에는 musicianinstagram_connection이 생성되었고, 같은 계정으로 다시 로그인하면 row가 새로 생기지 않고 token 관련 값만 갱신되었다.

 

  ———

 

  3.2 Instagram 게시물 + 인사이트 조회

 

  GET /api/musicians/me/instagram/media

 

  현재는 인증 기능이 없기 때문에 임시로 헤더를 사용했다.

 

  X-Musician-Id: 1

 

  역할:

 

  저장된 Instagram access token 조회

  Instagram /me/media 호출

  각 게시물별 shares, profile_visits 조회

  프론트에 게시물 목록과 지표 반환

 

  테스트 curl:

 

  curl -s "http://localhost:8080/api/musicians/me/instagram/media" \

    -H "X-Musician-Id: 1" \

    | python -m json.tool

 

  응답 예시:

 

  {

    "success": true,

    "data": [

      {

        "mediaId": "18198936793355701",

        "mediaType": "IMAGE",

        "permalink": "https://www.instagram.com/p/DXT5H-MEcv6/",

        "timestamp": "2026-04-19T11:02:07+0000",

        "shareCount": 1,

        "profileVisitCount": 7

      }

    ],

    "message": "Instagram 게시물을 조회했습니다."

  }

 

  mediaUrl 같은 긴 필드는 실제 응답에는 포함되지만, 블로그에서는 생략했다.

 

  ———

 

  4. DB 구조

 

  이번 작업에서 핵심이 되는 테이블은 instagram_connection이다.

 

  역할은 다음과 같다.

 

  우리 서비스의 Musician

  <-> Instagram 계정

  <-> Instagram long-lived access token

 

  주요 컬럼:

 

  musician_id

  instagram_account_id

  instagram_username

  access_token

  token_expires_at

  connected_at

  updated_at

 

  현재는 access token을 평문으로 저장했다.

  개발 단계에서는 빠르게 검증하기 위해 이렇게 했지만, 운영 전에는 반드시 암호화 저장이 필요하다.

 

  비밀번호: 해시 저장

  access token: 복호화 가능한 암호화 저장

 

  ———

 

  5. 구현 중 겪은 문제

 

  5.1 redirect URI 문제

 

  로컬 localhost만으로는 Instagram OAuth redirect 테스트가 번거로웠다.

  그래서 ngrok을 사용해서 HTTPS 주소를 만들고, 그 주소를 Meta App의 redirect URI로 등록했다.

 

  https://xxxx.ngrok-free.dev/instagram/oauth/callback

 

  이렇게 하니 Instagram OAuth redirect를 안정적으로 테스트할 수 있었다.

 

  ———

 

  5.2 permissions 파싱 문제

 

  Instagram token API 응답의 permissions를 처음에는 문자열로 생각했다.

  하지만 실제 응답은 배열이었다.

 

  "permissions": [

    "instagram_business_basic",

    "instagram_business_manage_insights"

  ]

 

  그래서 DTO에서는 List<String>으로 받도록 수정했다.

 

  ———

 

  5.3 같은 계정 재로그인 처리

 

  OAuth는 여러 번 로그인할 수 있기 때문에, 같은 Instagram 계정으로 다시 로그인했을 때 row가 중복 생성되면 안 된다.

 

  그래서 instagram_account_id를 unique 기준으로 사용했다.

 

  첫 로그인: Musician + InstagramConnection 생성

  재로그인: 기존 InstagramConnection token 갱신

 

  ———

 

  6. 테스트 순서

 

  6.1 ngrok 실행

 

  ngrok http 8080

 

  6.2 Spring Boot 실행

 

  필요한 환경변수:

 

  DB_URL=...

  DB_USERNAME=...

  DB_PASSWORD=...

  INSTAGRAM_CLIENT_ID=...

  INSTAGRAM_CLIENT_SECRET=...

  INSTAGRAM_REDIRECT_URI=https://xxxx.ngrok-free.dev/instagram/oauth/callback

 

  6.3 Instagram OAuth 로그인

 

  open

  "https://www.instagram.com/oauth/authorize?force_reauth=true&client_id={CLIENT_ID}&redirect_uri=https://xxxx.ngrok-free.dev/instagram/oauth/callback&response_type=code&scope=instagram_business_basic%2Cinstagram_business_manage_insights"

 

  6.4 DB 확인

 

  SELECT * FROM musician;

 

  SELECT

    instagram_connection_id,

    musician_id,

    instagram_account_id,

    instagram_username,

    token_expires_at,

    connected_at,

    updated_at

  FROM instagram_connection;

 

  6.5 게시물 조회 확인

 

  curl -s "http://localhost:8080/api/musicians/me/instagram/media" \

    -H "X-Musician-Id: 1" \

    | python -m json.tool

 

  ———

 

  7. 현재 한계

 

  현재는 아직 JWT/세션 인증이 없다.

  그래서 임시로 X-Musician-Id 헤더를 사용하고 있다.

 

  X-Musician-Id: 1

 

  운영에서는 이 방식은 사용할 수 없다. 사용자가 다른 뮤지션 ID를 임의로 넣을 수 있기 때문이다.

 

  나중에는 JWT에서 musicianId를 꺼내는 방식으로 바꿔야 한다.

 

  또한 long-lived token은 약 60일 동안 유효하므로, 운영에서는 만료 전에 refresh하는 배치가 필요하다.

 

  ———

 

  8. 다음 작업

 

  다음으로 구현할 API는 다음이다.

 

  PATCH /api/music-promotions/{promotionId}/instagram-media

 

  이 API는 우리 서비스의 음악 홍보 객체와 Instagram 게시물을 연결한다.

 

  필요한 이유는 다음과 같다.

 

  music_promotion은 우리 DB에 있음

  Instagram media는 Instagram API에서 조회됨

  하지만 어떤 게시물이 어떤 홍보와 연결된 것인지는 백엔드가 자동으로 알 수 없음

 

  그래서 뮤지션이 게시물 목록에서 하나를 선택하면, 해당 게시물의 mediaIdmusic_promotion과 연결해야 한다.

 

  이후에는 다음 흐름이 가능해진다.

 

  promotionId

  -> 연결된 instagramMediaId 조회

  -> 해당 media의 shares/profile_visits 조회

  -> promotion_tracking_click에서 클릭수 조회

  -> 화면에서 홍보 성과 요약 표시