insight00-15 님의 블로그
Instagram API 연동 with Instagram Login 본문
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 로그인이 완료되었습니다."
}
테스트 결과 첫 로그인 시에는 musician과 instagram_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에서 조회됨
하지만 어떤 게시물이 어떤 홍보와 연결된 것인지는 백엔드가 자동으로 알 수 없음
그래서 뮤지션이 게시물 목록에서 하나를 선택하면, 해당 게시물의 mediaId를 music_promotion과 연결해야 한다.
이후에는 다음 흐름이 가능해진다.
promotionId
-> 연결된 instagramMediaId 조회
-> 해당 media의 shares/profile_visits 조회
-> promotion_tracking_click에서 클릭수 조회
-> 화면에서 홍보 성과 요약 표시
'개발' 카테고리의 다른 글
| 외부 API 연동 안정화 체크리스트 : Instagram API 사례로 정리 (0) | 2026.04.27 |
|---|---|
| Github Actions로 개선하는 코드 리뷰 문화 (1) | 2026.04.16 |
| [개발] "백엔드 개발자" 오픈형 멘토링: 백엔드 - 손권남 멘토 (0) | 2026.04.14 |