<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>insight00-15 님의 블로그</title>
    <link>https://insight00-15.tistory.com/</link>
    <description>insight00-15 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Fri, 8 May 2026 14:03:11 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>insight00-15</managingEditor>
    <item>
      <title>외부 API 연동 안정화 체크리스트 : Instagram API 사례로 정리</title>
      <link>https://insight00-15.tistory.com/6</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;최근에 Instagram API 연동을 필요로 하는 프로젝트를 진행하면서 &lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://insight00-15.tistory.com/5&quot;&gt;Instagram API 연동 with Instagram Login&lt;/a&gt; 이라는 글을 썼다. &lt;/b&gt;&lt;/span&gt;&lt;span&gt;&lt;b&gt;단순 구현을 하면서 점차 기술적으로 고민이 들었던 부분들을 리서치 하고 기록해보려고한다.&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;# Instagram API 연동, &amp;ldquo;기능 구현&amp;rdquo; 다음에 보이기 시작한 운영 관점의 고민들&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;처음에는 단순했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Instagram API를 붙이고, 필요한 데이터를 받아와서 DB에 저장하면 기능은 완성이라고 생각했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그런데 시간이 지나고 다시 코드를 보니, 이건 &amp;ldquo;동작하는 기능&amp;rdquo;일 뿐이지 아직 &amp;ldquo;안정적으로 운영할 수 있는 기능&amp;rdquo;은 아니었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;외부 API를 호출하는 순간부터 내 애플리케이션은 더 이상 내부 로직만 신경 쓰면 되는 구조가 아니고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;지연&lt;/span&gt;&lt;span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;실패&lt;/span&gt;&lt;span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;중복 호출&lt;/span&gt;&lt;span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;데이터 최신화 지연&lt;/span&gt;&lt;span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;DB 자원 점유&lt;/span&gt;&lt;span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;관측 불가&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 문제를 같이 안고 간다는 걸&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;뒤늦게 체감했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이번 글은 누군가를 가르치기 위한 정답 정리라기보다, 내가 Instagram API 연동을 운영 관점에서 다시 보면서 어떤 부분을 고민하게 되었는지 남겨두는 기록에 가깝다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;## 왜 이 고민을 하게 됐는가&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;지금 내 프로젝트는 Instagram API를 연동해서 데이터를 조회하고, 필요한 값은 DB에 저장하는 흐름까지는 구현된 상태다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;하지만 당시에는 MVP 기한이 정해져있었기에 기능 구현이 우선이었고, 외부 API 호출에서 흔히 필요한 안정화 요소들은 거의 고려하지 않았다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예를 들면 이런 것들이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 타임아웃&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 실패 분류&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 재시도 여부&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 멱등성&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- DB 트랜잭션과 외부 호출 분리&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 로깅/모니터링&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;처음에는 이런 고민들이 MSA 환경에서나 필요한 얘기라고 막연하게 생각했는데, 자료를 찾아보면서 그건 아니라는 걸 알게 됐다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;MSA는 이런 문제가 더 자주, 더 크게 드러나는 환경일 뿐이고, 사실 모놀리식이어도 외부 API를 호출하는 순간부터는 이미 분산 시스템의 일부 문제를 안고 가게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;즉, 내 프로젝트가 MSA가 아니더라도 외부 API를 호출하고 있다면, 안정적인 운영을 위해 위 항목들을 생각해야 하는 건 자연스러운거다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;## 1. Instagram 인사이트 데이터는 실시간이 아니다&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;가장 먼저 눈에 띈 건 인사이트 데이터의 최신화 시점이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;처음 메모할 때는 &amp;ldquo;Instagram 인사이트 지표는 최대 48시간이 걸린다&amp;rdquo;라고 적어뒀다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 표현은 방향은 맞지만, 조금 더 조심스럽게 쓰는 게 좋다. 현재 확인 가능한 자료들을 보면, Instagram 인사이트 데이터는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;실시간이 아니며 일부 지표는 최대 48시간 정도 지연될 수 있다&lt;/span&gt;고 이해하는 게 더&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;적절하다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이게 왜 중요하냐면, 같은 요청을 다시 보냈는데 데이터가 전과 똑같이 돌아왔다고 해서 곧바로 우리 시스템이 잘못됐다고 단정할 수 없기 때문이다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Instagram 쪽 집계 자체가 아직 갱신되지 않았을 가능성도 충분히 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;즉, 인사이트 데이터는 내부 DB 조회처럼 &amp;ldquo;지금 요청했으니 지금 시점의 최신 데이터가 오겠지&amp;rdquo;라고 생각하면 안 된다. &lt;/span&gt;&lt;span&gt;이 특성을 이해하지 못하면, 정상적인 지연을 장애처럼 오해하거나 반대로 실제 문제를 놓칠 수도 있다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;i&gt;### 참고 링크&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- Meta Docs: Instagram Media Insights Reference (&lt;a href=&quot;https://developers.facebook.com/docs/instagram-platform/reference/instagram-media/insights&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developers.facebook.com/docs/instagram-platform/reference/instagram-media/insights&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;## 2. Instagram API는 호출량 제한도 고려해야 한다&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Instagram API를 붙일 때 흔히 놓치기 쉬운 부분 중 하나가 &lt;span style=&quot;color: #38b9c7;&quot;&gt;호출량 제한&lt;/span&gt;이다. &lt;/span&gt;&lt;span&gt;처음에는 단순히 &amp;ldquo;요청만 보내면 응답을 주는 API&amp;rdquo;처럼 생각하기 쉽지만, &lt;span style=&quot;color: #38b9c7;&quot;&gt;Meta Graph API 계열은 호출량이 무한정 허용되는 구조가 아니다&lt;/span&gt;. 호출량&lt;/span&gt;&lt;span&gt;이 많아지면 &lt;span style=&quot;color: #38b9c7;&quot;&gt;rate limiting&lt;/span&gt;이나 &lt;span style=&quot;color: #38b9c7;&quot;&gt;throttling&lt;/span&gt;이 걸릴 수 있고, 이 경우 일부 요청은 실패하거나 지연될 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;여기서 중요한 건 호출 제한이 단순히 &amp;ldquo;분당 몇 번&amp;rdquo;처럼 고정된 숫자 하나로 끝나지 않는다는 점이다. Meta 문서를 보면 제한은 &lt;span style=&quot;color: #38b9c7;&quot;&gt;앱 수준&lt;/span&gt;, &lt;span style=&quot;color: #38b9c7;&quot;&gt;사용자/토&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;큰 수준&lt;/span&gt;, 그리고&lt;span style=&quot;color: #38b9c7;&quot;&gt; 비즈니스 사용 방식&lt;/span&gt;에 따라 다르게 적용될 수 있다. 즉 개발자는 &amp;ldquo;우리 서비스는 하루에 이 정도만 호출하니까 괜찮겠지&amp;rdquo;라고 막연&lt;/span&gt;&lt;span&gt;히 생각하기보다, 실제 운영 중에 어느 시점부터 &lt;span style=&quot;color: #38b9c7;&quot;&gt;usage가 높아지는지&lt;/span&gt;를 관측할 수 있어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이게 왜 중요하냐면, 처음에는 트래픽이 적어서 문제가 없더라도 기능이 늘어나고 동기화 대상이 많아지면 같은 API를 호출하는 횟수가 빠르게 증가 &lt;/span&gt;&lt;span&gt;할 수 있기 때문이다. 특히 인사이트 조회나 미디어 목록 조회처럼 여러 계정, 여러 콘텐츠를 순회하는 구조라면 요청 수가 생각보다 쉽게 커진다. &lt;/span&gt;&lt;span&gt;이런 구조에서 호출량 제한을 고려하지 않으면, 운영 중 특정 시점에 갑자기 일부 요청이 막히거나 응답이 불안정해질 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;즉 Instagram API 연동은 &amp;ldquo;응답이 오는가&amp;rdquo;만 볼 게 아니라, &amp;ldquo;얼마나 자주 호출하고 있고, 그 호출량이 제한에 가까워지고 있는가&amp;rdquo;도 같이 봐야 한&lt;/span&gt;&lt;span&gt;다. 이 특성을 이해하지 못하면, 평소에는 잘 동작하다가 트래픽이 몰리거나 수집 범위가 커졌을 때 예상하지 못한 장애를 맞을 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;b&gt;&lt;i&gt;### 참고 링크&lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #38b9c7;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;- Meta Docs: Graph API Rate Limiting (&lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://developers.facebook.com/docs/graph-api/overview/rate-limiting#platform-rate-limits&quot;&gt;https://developers.facebook.com/docs/graph-api/overview/rate-limiting#platform-rate-limits&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;## 3. 외부 API는 성공/실패 두 가지로만 나눌 수 없다&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;외부 API 연동을 다시 보면서 가장 크게 느낀 건, 네트워크 요청은 생각보다 애매한 상황이 많다는 점이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예를 들어 timeout이 발생했다고 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이때 가능한 경우는 여러 가지다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 요청이 Instagram 서버에 아예 도달하지 않았을 수도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 요청은 도달했지만 처리 중이었을 수도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 이미 처리는 끝났는데 응답만 받지 못했을 수도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이런 상황을 그냥 전부 &amp;ldquo;실패&amp;rdquo;라고 처리해버리면, 실제로는 이미 처리된 요청을 또 보내게 되거나, 중복 저장 같은 문제로 이어질 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그래서 외부 API를 다룰 때는 단순히 성공/실패만 보는 게 아니라, 경우에 따라서는 &lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;알 수 없음(unknown)&lt;/span&gt;&lt;span&gt; 상태까지 생각해야 한다는 점이 인상적이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 관점은 카카오페이 글을 보면서 더 명확해졌다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;결제 같은 민감한 도메인은 더 극단적이지만, 핵심은 똑같다. 외부 시스템과 통신할 때는 &amp;ldquo;응답이 안 왔다 = 처리 안 됐다&amp;rdquo;로 단정하면 안 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;다만 내 프로젝트는 결제처럼 외부 시스템의 상태를 직접 바꾸는 요청이 아니라, 아직은 조회 API 중심의 구조다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그래서 취소 API나 보상 트랜잭션까지 당장 필요한 상황은 아니지만, 그래도 &lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;실패 분류&lt;/span&gt;&lt;span&gt; 자체는 반드시 필요하다고 느꼈다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;&lt;i&gt;### 참고 링크&lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #38b9c7;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;- 카카오페이: MSA 환경에서 네트워크 예외를 잘 다루는 방법 (&lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://tech.kakaopay.com/post/msa-transaction/&quot;&gt;https://tech.kakaopay.com/post/msa-transaction/&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #38b9c7;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;- AWS Builders&amp;rsquo; Library: Timeouts, retries, and backoff with jitter (&lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/&quot;&gt;https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;## 4. 느린 외부 API는 결국 우리 서비스 전체를 느리게 만든다&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Instagram 서버가 완전히 죽는 경우만 문제가 되는 건 아니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;오히려 실무에서는 &amp;ldquo;죽지는 않았는데 엄청 느린 상태&amp;rdquo;가 더 골치 아플 수도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이럴 때 타임아웃이 없으면, 우리 서버는 외부 API 응답을 하염없이 기다리게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;요청 처리 스레드는 계속 묶이고, 응답 시간은 길어지고, 같은 요청이 쌓이면 전체 서비스가 느려질 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;게다가 그 호출 구간 안에 DB 트랜잭션까지 같이 들어 있으면 문제가 더 커진다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;외부 API가 느린 동안 DB 커넥션까지 계속 잡고 있게 되고, 요청이 많아지면 커넥션 풀 고갈 같은 2차 장애로 번질 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그래서 타임아웃은 단순히 &amp;ldquo;조금 기다리다 포기하는 옵션&amp;rdquo;이 아니라, 외부 시스템 지연이 우리 시스템 전체 자원 고갈로 전파되지 않게 막는 보호 장치에 가깝다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;b&gt;&lt;i&gt;### 참고 링크&lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #38b9c7;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;- AWS Builders&amp;rsquo; Library: Timeouts, retries, and backoff with jitter (&lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/&quot;&gt;https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;## 5. 재시도는 무조건 넣는다고 좋은 게 아니다&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;처음에는 외부 API가 실패하면 &amp;ldquo;그냥 한 번 더 호출하면 되지 않을까?&amp;rdquo;라고 생각하기 쉽다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그런데 자료를 보다 보니, 재시도는 생각보다 훨씬 조심스럽게 다뤄야 하는 영역이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;재시도는 일시적인 네트워크 오류나 순간적인 장애에는 도움이 될 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;하지만 모든 예외에 무조건 재시도를 걸어버리면 오히려 장애 상황에서 요청을 더 몰아넣는 형태가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이른바 retry storm 같은 안티패턴이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예를 들어 Instagram 서버가 이미 느리거나 불안정한 상태인데, 우리 서버가 같은 요청을 여러 번 다시 보내기 시작하면 상대 서버도 더 힘들어지고, 우리 쪽 스레드와 응답 시간도 더 악화될 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그래서 재시도는 &amp;ldquo;실패하면 다시&amp;rdquo;가 아니라, &amp;ldquo;어떤 실패에 한해, 몇 번까지, 어떤 간격으로 다시 시도할 것인가&amp;rdquo;를 정해서 써야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;b&gt;&lt;i&gt;### 참고 링크&lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #38b9c7;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;- Azure Architecture: Retry Storm antipattern (&lt;a href=&quot;https://learn.microsoft.com/ko-kr/azure/architecture/antipatterns/retry-storm/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/ko-kr/azure/architecture/antipatterns/retry-storm/&lt;/a&gt;)&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;## 6. 멱등성은 &amp;ldquo;조회&amp;rdquo;보다 &amp;ldquo;저장/동기화&amp;rdquo;에서 더 중요하다&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;내가 처음 메모할 때는 &amp;ldquo;인사이트 지표는 언제 최신화될지 모르니까, 계속 받아와도 이게 맞는 최신 데이터인지 모른다. 그러면 멱등성을 어떻게 유지해야 하지?&amp;rdquo;라고 적어뒀다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 고민의 방향은 맞았지만, 표현은 조금 더 정확히 다듬는 게 좋겠다고 느꼈다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;멱등성은 보통 조회 요청 자체보다, &lt;span style=&quot;color: #38b9c7;&quot;&gt;그 조회 결과를 저장하고 동기화하는 작업&lt;/span&gt; 쪽에서 더 중요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;즉, 중요한 질문은 이런 쪽이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 같은 인사이트 데이터를 여러 번 받아와도 DB 상태가 꼬이지 않는가&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 같은 동기화 작업이 중복 실행돼도 최종 결과가 동일한가&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 사용자가 버튼을 여러 번 누르거나 배치가 중복 실행돼도 중복 저장이 발생하지 않는가&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;인사이트 데이터처럼 최신화 시점이 불명확한 경우에는, 같은 데이터가 반복해서 들어오는 것이 정상일 수도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그래서 이럴수록 &amp;ldquo;같은 요청이 여러 번 왔다&amp;rdquo; 자체보다 &amp;ldquo;그 결과를 저장하는 로직이 중복에 안전한가&amp;rdquo;를 보는 게 더 중요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그리고 이 지점에서 로깅/모니터링과도 연결된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;똑같은 데이터가 들어왔을 때, 그게 Instagram 집계 지연 때문인지, 우리 동기화 로직의 중복 실행 때문인지 구분하려면 호출 이력과 저장 이력이 보여야 하기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;b&gt;&lt;i&gt;### 참고 링크&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #38b9c7;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;- 카카오페이: MSA 환경에서 네트워크 예외를 잘 다루는 방법 (&lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://tech.kakaopay.com/post/msa-transaction/&quot;&gt;https://tech.kakaopay.com/post/msa-transaction/&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;## 7. DB 트랜잭션과 외부 API 호출은 분리하는 편이 안전하다&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이번에 찾아본 내용 중 가장 실무적으로 바로 와닿았던 건, 외부 API 호출과 DB 트랜잭션을 가능한 한 분리하라는 이야기였다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;처음에는 &amp;ldquo;어차피 외부 응답을 보고 DB 처리할 건데, 결국 연결된 거 아닌가?&amp;rdquo; 싶었는데, 핵심은 &lt;span style=&quot;color: #38b9c7;&quot;&gt;외부 API 결과를 참고하느냐가 아니라&lt;/span&gt;, &lt;span style=&quot;color: #38b9c7;&quot;&gt;DB 트랜잭션을 언제 시작하느냐&lt;/span&gt;였다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예를 들어 이런 구조가 있다고 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 외부 API 호출&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 응답 대기&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 응답 결과를 바탕으로 DB 조회/저장&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 흐름 자체는 자연스럽다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;문제는 이 전체가 하나의 &lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;@Transactional&lt;/span&gt;&lt;span&gt; 메서드 안에 들어 있을 때다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그렇게 되면 메서드 진입 순간 트랜잭션이 열리고, 외부 API가 3초 걸리면 그 3초 동안 DB 트랜잭션도 살아 있을 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;결과적으로 DB 커넥션이 불필요하게 오래 점유되고, 트래픽이 커지면 커넥션 풀 고갈 위험이 생긴다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그래서 더 안전한 구조는:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 외부 API 호출은 트랜잭션 없이 먼저 수행하고&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 그 응답 결과를 받은 뒤에&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 필요한 DB 작업만 짧게 트랜잭션으로 묶는 방식이다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이렇게 하면 외부 시스템의 지연이 DB 자원 점유로 전파되는 걸 줄일 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;b&gt;&lt;i&gt;### 참고 링크&lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #38b9c7;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;- 카카오페이: 주니어 서버 개발자가 유저향 서비스를 개발하며 마주쳤던 이슈와 해결 방안 (&lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://tech.kakaopay.com/post/troubleshooting-logs-as-a-junior-developer/&quot;&gt;https://tech.kakaopay.com/post/troubleshooting-logs-as-a-junior-developer/&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;## 8. 로깅과 모니터링은 &amp;ldquo;문제를 고치는 장치&amp;rdquo;가 아니라 &amp;ldquo;문제를 보이게 하는 장치&amp;rdquo;다&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;처음에는 타임아웃, 재시도, 트랜잭션 분리 같은 기술적인 해결책에 더 눈이 갔다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그런데 정작 운영 관점에서 더 중요한 건, 지금 어떤 문제가 발생하고 있는지 &lt;span style=&quot;color: #38b9c7;&quot;&gt;관측할 수 있느냐&lt;/span&gt;였다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;외부 API 연동은 문제가 생겨도 원인이 바로 드러나지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 우리 서버 문제인지&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- Instagram API 문제인지&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 단순 지연인지&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 같은 데이터가 반복해서 들어오는 이유가 최신화 지연인지, 저장 로직 문제인지&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이런 걸 구분하려면 결국 &lt;span style=&quot;color: #38b9c7;&quot;&gt;로그와 메트릭&lt;/span&gt;, 그리고 필요하다면 &lt;span style=&quot;color: #38b9c7;&quot;&gt;헬스 체크 지표&lt;/span&gt;가 있어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;특히 인사이트 데이터처럼 &amp;ldquo;정상인데 늦을 수 있는&amp;rdquo; 연동은 더 그렇다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;데이터가 갱신되지 않았다는 사실 자체가 장애는 아닐 수 있으므로, 호출 시각, 응답 시간, 상태 코드, 예외 유형, 저장된 데이터의 기준 시각같은 &lt;/span&gt;&lt;span&gt;정보가 함께 보여야 원인을 해석할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;즉 단순히 &amp;ldquo;성공했는가 / 실패했는가&amp;rdquo;만 보는 것이 아니라, &lt;span style=&quot;color: #38b9c7;&quot;&gt;외부 의존성이 지금 얼마나 느린지&lt;/span&gt;, &lt;span style=&quot;color: #38b9c7;&quot;&gt;실패율이 높아지고 있는지&lt;/span&gt;, &lt;span style=&quot;color: #38b9c7;&quot;&gt;우리 애플리케이션이 그 &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;영향을 받고 있는지&lt;/span&gt;까지 볼 수 있어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #38b9c7;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 부분은 &lt;/span&gt;&lt;span&gt;Health Endpoint Monitoring pattern&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;을 보면서 더 정리가 됐다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;운영에서는 단순히 애플리케이션이 살아 있는지만 보는 게 아니라, 그 애플리케이션이 의존하고 있는 구성요소들까지 함께 봐야 한다. 내 프로젝트&lt;/span&gt;&lt;span&gt;로 치면, 서버 프로세스가 살아 있는 것만으로는 충분하지 않고, Instagram API 호출이 정상적으로 이뤄지는지, 외부 API 응답 시간이 급격히 느려&lt;/span&gt;&lt;span&gt;지지는 않았는지, 실패가 특정 구간에서 몰리고 있지는 않은지까지 같이 봐야 의미가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그래서 로깅/모니터링은 장애를 직접 막아주는 기능이라기보다, 문제를 더 빨리 발견하고, 원인을 좁히고, 대응할 수 있게 해주는 기반이라고 이해&lt;/span&gt;&lt;span&gt;하게 됐다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;결국 외부 API 연동에서 중요한 건 &amp;ldquo;장애가 났을 때 고치는 능력&amp;rdquo;만이 아니라, &lt;span style=&quot;color: #38b9c7;&quot;&gt;장애 징후를 먼저 볼 수 있는 능력&lt;/span&gt;이라는 생각이 들었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;b&gt;&lt;i&gt;### 참고 링크&lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;- Azure Architecture: Health Endpoint Monitoring pattern&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #38b9c7;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;(&lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/azure/architecture/patterns/health-endpoint-monitoring&quot;&gt;https://learn.microsoft.com/en-us/azure/architecture/patterns/health-endpoint-monitoring&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;## 지금 시점에서 내가 우선적으로 점검할 항목&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;모든 안정화 패턴을 한 번에 다 넣는 건 현실적이지 않다. 프로젝트를 진행하면서, 이제 2차 MVP에 들어갈 기능들도 구현해야돼서 시간도 없을테고 구현 난이도가 있을거같아 조사하면서도 어떻게, 어디서부터 해야될지 막막했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;결론은, 2차 MVP 기능 구현을 우선으로 하면서 안정화 작업을 후순위로 조금씩 해나가면 좋을거같다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;안정화 작업에 들어가게된다면, 지금 내 프로젝트 상황에서는 아래 다섯 가지를 먼저 정리하는 게 맞다고 보고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- &lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;타임아웃&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;- 외부 API 지연이 전체 서비스 자원 고갈로 이어지지 않게 하기 위해&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #38b9c7;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- &lt;/span&gt;&lt;span&gt;실패 분류&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;- 일시적 실패와 영구 실패를 구분하기 위해&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #38b9c7;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- &lt;/span&gt;&lt;span&gt;재시도 여부&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;- 무조건 재시도하지 않고, 특정 조건에만 제한적으로 적용하기 위해&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #38b9c7;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- &lt;/span&gt;&lt;span&gt;DB 트랜잭션과 외부 호출 분리&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;- 외부 응답 대기 시간이 DB 커넥션 점유 시간까지 길어지지 않게 하기 위해&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #38b9c7;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- &lt;/span&gt;&lt;span&gt;로깅/모니터링&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;- 문제를 빨리 감지하고 원인을 좁히고 정책의 효과를 검증하기 위해&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;반대로, &lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;벌크헤드&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;서킷 브레이커&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;이중화&lt;/span&gt;&lt;span&gt; 같은 항목은 지금 당장 필수라기보다, 트래픽 규모와 장애 영향도를 보면서 다음 단계에서 검토할 수 있는 주제라고 생각한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이번에 정리하면서 느낀 건, 외부 API 연동은 &amp;ldquo;호출해서 값 받아오기&amp;rdquo;에서 끝나는 문제가 아니라는 점이었다. &amp;nbsp;&lt;/span&gt;&lt;span&gt;오히려 그 다음부터가 진짜 시작이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Instagram API를 붙여서 기능이 돌아가는 것과, 장애와 지연을 견디면서 안정적으로 운영할 수 있는 것은 완전히 다른 문제였다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;지금 내 프로젝트는 아직 후자 쪽으로 가는 과정에 있고, 이 글은 그 과정에서 내가 어떤 포인트를 먼저 봐야 하는지 정리한 기록이다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발</category>
      <category>멱등성</category>
      <category>모니터링</category>
      <category>재시도</category>
      <category>타임아웃</category>
      <author>insight00-15</author>
      <guid isPermaLink="true">https://insight00-15.tistory.com/6</guid>
      <comments>https://insight00-15.tistory.com/6#entry6comment</comments>
      <pubDate>Mon, 27 Apr 2026 23:34:37 +0900</pubDate>
    </item>
    <item>
      <title>Instagram API 연동 with Instagram Login</title>
      <link>https://insight00-15.tistory.com/5</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;&lt;b&gt;Spring Boot에서 Instagram OAuth 연동하고 게시물 인사이트 조회하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;a href=&quot;https://developers.facebook.com/docs/instagram-platform?locale=ko_KR&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developers.facebook.com/docs/instagram-platform?locale=ko_KR&lt;/a&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1776688121682&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Instagram 플랫폼 - 문서 - Meta for Developers&quot; data-og-description=&quot;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&quot; data-og-host=&quot;developers.facebook.com&quot; data-og-source-url=&quot;https://developers.facebook.com/docs/instagram-platform?locale=ko_KR&quot; data-og-url=&quot;https://developers.facebook.com/docs/instagram-platform?locale=ko_KR&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developers.facebook.com/docs/instagram-platform?locale=ko_KR&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.facebook.com/docs/instagram-platform?locale=ko_KR&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Instagram 플랫폼 - 문서 - Meta for Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;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&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.facebook.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;이번 프로젝트에서는 뮤지션이 음악 홍보 페이지를 만들고, Instagram 게시물을 통해 발생한 성과를 확인하는 기능을 구현하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;이번 작업의 목표는 다음이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;Instagram OAuth로 뮤지션 가입/로그인&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;-&amp;gt; long-lived access token 저장&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;-&amp;gt; Instagram 게시물 목록 조회&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;-&amp;gt; 게시물별 공유수, 프로필 방문수 조회&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;1. 왜 Instagram OAuth를 로그인으로 사용했나&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;처음에는 일반 회원가입 후 Instagram을 연동하는 방식을 생각했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;이메일 회원가입&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;-&amp;gt; 로그인&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;-&amp;gt; Instagram 연동&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;하지만 현재 MVP의 핵심 사용자는 뮤지션이고, 뮤지션은 Instagram 계정이 있어야 서비스를 제대로 사용할 수 있다. 그래서 별도 회원가입을 먼저 만들기보다, Instagram OAuth 자체를 가입/로그인 수단으로 사용했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;최종 흐름은 이렇게 잡았다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;Instagram OAuth 성공&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;-&amp;gt; Instagram 계정 정보 조회&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;-&amp;gt; DB에 해당 Instagram 계정이 없으면 Musician으로 저장&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;-&amp;gt; OAuth에서 발급받은 long-lived access token 등 인스타와 연동할때 필요한 정보들 DB에 instagram_connection으로 저장&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;-&amp;gt; 이미 있으면 기존 Musician 재사용 + token 갱신&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;2. 초기 Instagram OAuth redirect uri 세팅&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;개발 환경에서는 ngrok을 사용했다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;ngrok은 로컬에서 실행 중인 서버를 외부에서 접근 가능한 HTTPS 주소로 노출해주는 터널링 도구다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;로컬 서버 주소인 &lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;localhost:8080&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;만으로는 Instagram OAuth redirect 테스트가 불안정했고, http로는 redirect URI를 등록이 &lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;되&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;지않는 문제가 있어서 ngrok을 사용하였다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;Spring Boot: http://localhost:8080&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;ngrok: &lt;a href=&quot;https://xxxx.ngrok-free.dev&quot;&gt;https://xxxx.ngrok-free.dev&lt;/a&gt; -&amp;gt; http://localhost:8080&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;Meta Developer App에는 아래 redirect URI를 등록했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;a href=&quot;https://xxxx.ngrok-free.dev/instagram/oauth/callback&quot;&gt;https://xxxx.ngrok-free.dev/instagram/oauth/callback&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Meta Developer App에서 redirect URI를 등록하면 제공해주는&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;OAuth 로그인 URL&lt;/span&gt;&amp;nbsp;링크를 타고 들어가서 인스타로 연동을 해주면, https://xxxx.ngrok...callback 링크로 redirect를 한 뒤 acceses token을 발급받는데 중요한 code 번호를 보내준다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; 내가 사용한 &lt;/span&gt;OAuth 로그인 URL은 다음 형태다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;a href=&quot;https://www.instagram.com/oauth/authorize&quot;&gt;https://www.instagram.com/oauth/authorize&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;?force_reauth=true&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&amp;amp;client_id={CLIENT_ID}&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&amp;amp;redirect_uri={REDIRECT_URI}&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&amp;amp;response_type=code&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&amp;amp;scope=instagram_business_basic%2Cinstagram_business_manage_insights&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;이번 기능에 필요한 scope는 두 개만 사용했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;instagram_business_basic&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;instagram_business_manage_insights&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;3. 구현한 API&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;&lt;i&gt;3.1 Instagram OAuth Callback&lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;GET /instagram/oauth/callback?code=...&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;역할:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;Instagram에서 받은 authorization code 처리&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;short-lived token 발급&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;long-lived token으로 교환&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;Instagram /me 조회&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;뮤지션 자동 생성 또는 기존 연결 갱신&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;성공 응답:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #747474;&quot;&gt;{&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&quot;success&quot;: true,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&quot;data&quot;: {&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&quot;musicianId&quot;: 1,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&quot;instagramAccountId&quot;: &quot;17841464237580902&quot;,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&quot;instagramUsername&quot;: &quot;robbi_developer&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;},&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&quot;message&quot;: &quot;Instagram 로그인이 완료되었습니다.&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #747474;&quot;&gt;}&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;테스트 결과 첫 로그인 시에는 &lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;musician&lt;/span&gt;&lt;span&gt;과 &lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;instagram_connection&lt;/span&gt;&lt;span&gt;이 생성되었고, 같은 계정으로 다시 로그인하면 row가 새로 생기지 않고 token 관련 값만 갱신되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;&lt;i&gt;3.2 Instagram 게시물 + 인사이트 조회&lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;GET /api/musicians/me/instagram/media&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;현재는 인증 기능이 없기 때문에 임시로 헤더를 사용했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;X-Musician-Id: 1&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;역할:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;저장된 Instagram access token 조회&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;Instagram /me/media 호출&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;각 게시물별 shares, profile_visits 조회&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;프론트에 게시물 목록과 지표 반환&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;테스트 curl:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;curl -s &quot;http://localhost:8080/api/musicians/me/instagram/media&quot; \&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;-H &quot;X-Musician-Id: 1&quot; \&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;| python -m json.tool&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;응답 예시:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #747474;&quot;&gt;{&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&quot;success&quot;: true,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&quot;data&quot;: [&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;{&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&quot;mediaId&quot;: &quot;18198936793355701&quot;,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&quot;mediaType&quot;: &quot;IMAGE&quot;,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&quot;permalink&quot;: &quot;&lt;a href=&quot;https://www.instagram.com/p/DXT5H-MEcv6/&quot;&gt;https://www.instagram.com/p/DXT5H-MEcv6/&lt;/a&gt;&quot;,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&quot;timestamp&quot;: &quot;2026-04-19T11:02:07+0000&quot;,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&quot;shareCount&quot;: 1,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&quot;profileVisitCount&quot;: 7&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;}&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;],&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&quot;message&quot;: &quot;Instagram 게시물을 조회했습니다.&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #747474;&quot;&gt;}&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;mediaUrl&lt;/span&gt;&lt;span&gt; 같은 긴 필드는 실제 응답에는 포함되지만, 블로그에서는 생략했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;4. DB 구조&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;이번 작업에서 핵심이 되는 테이블은 &lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;instagram_connection&lt;/span&gt;&lt;span&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;역할은 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;우리 서비스의 Musician&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&amp;lt;-&amp;gt; Instagram 계정&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&amp;lt;-&amp;gt; Instagram long-lived access token&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;주요 컬럼:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;musician_id&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;instagram_account_id&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;instagram_username&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;access_token&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;token_expires_at&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;connected_at&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;updated_at&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;현재는 access token을 평문으로 저장했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;개발 단계에서는 빠르게 검증하기 위해 이렇게 했지만, 운영 전에는 반드시 암호화 저장이 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;비밀번호: 해시 저장&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;access token: 복호화 가능한 암호화 저장&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;5. 구현 중 겪은 문제&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;&lt;i&gt;5.1 redirect URI 문제&lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;로컬 &lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;localhost&lt;/span&gt;&lt;span&gt;만으로는 Instagram OAuth redirect 테스트가 번거로웠다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;그래서 ngrok을 사용해서 HTTPS 주소를 만들고, 그 주소를 Meta App의 redirect URI로 등록했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;a href=&quot;https://xxxx.ngrok-free.dev/instagram/oauth/callback&quot;&gt;https://xxxx.ngrok-free.dev/instagram/oauth/callback&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;이렇게 하니 Instagram OAuth redirect를 안정적으로 테스트할 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;&lt;i&gt;5.2 permissions 파싱 문제&lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;Instagram token API 응답의 &lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;permissions&lt;/span&gt;&lt;span&gt;를 처음에는 문자열로 생각했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;하지만 실제 응답은 배열이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;permissions&quot;: [&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&quot;instagram_business_basic&quot;,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&quot;instagram_business_manage_insights&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #747474;&quot;&gt;]&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;그래서 DTO에서는 &lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;List&amp;lt;String&amp;gt;&lt;/span&gt;&lt;span&gt;으로 받도록 수정했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;&lt;i&gt;5.3 같은 계정 재로그인 처리&lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;OAuth는 여러 번 로그인할 수 있기 때문에, 같은 Instagram 계정으로 다시 로그인했을 때 row가 중복 생성되면 안 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;그래서 &lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;instagram_account_id&lt;/span&gt;&lt;span&gt;를 unique 기준으로 사용했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;첫 로그인: Musician + InstagramConnection 생성&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;재로그인: 기존 InstagramConnection token 갱신&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;6. 테스트 순서&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;&lt;i&gt;6.1 ngrok 실행&lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ngrok http 8080&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;&lt;i&gt;6.2 Spring Boot 실행&lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;필요한 환경변수:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;DB_URL=...&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;DB_USERNAME=...&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;DB_PASSWORD=...&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;INSTAGRAM_CLIENT_ID=...&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;INSTAGRAM_CLIENT_SECRET=...&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;INSTAGRAM_REDIRECT_URI=&lt;a href=&quot;https://xxxx.ngrok-free.dev/instagram/oauth/callback&quot;&gt;https://xxxx.ngrok-free.dev/instagram/oauth/callback&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;&lt;i&gt;6.3 Instagram OAuth 로그인&lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;open&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;a href=&quot;https://www.instagram.com/oauth/authorize?force_reauth=true&amp;amp;client_id=&quot;&gt;https://www.instagram.com/oauth/authorize?force_reauth=true&amp;amp;client_id=&lt;/a&gt;{CLIENT_ID}&amp;amp;redirect_uri=&lt;a href=&quot;https://xxxx.ngrok-free.dev/instagram/oauth/callback&amp;amp;response_type=code&amp;amp;scope=instagram_business_basic%2Cinstagram_business_manage_insights&quot;&gt;https://xxxx.ngrok-free.dev/instagram/oauth/callback&amp;amp;response_type=code&amp;amp;scope=instagram_business_basic%2Cinstagram_business_manage_insights&lt;/a&gt;&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;&lt;i&gt;6.4 DB 확인&lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;SELECT * FROM musician;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;instagram_connection_id,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;musician_id,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;instagram_account_id,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;instagram_username,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;token_expires_at,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;connected_at,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;updated_at&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;FROM instagram_connection;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;&lt;i&gt;6.5 게시물 조회 확인&lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;curl -s &quot;http://localhost:8080/api/musicians/me/instagram/media&quot; \&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;-H &quot;X-Musician-Id: 1&quot; \&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;| python -m json.tool&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;7. 현재 한계&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;현재는 아직 JWT/세션 인증이 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;그래서 임시로 &lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;X-Musician-Id&lt;/span&gt;&lt;span&gt; 헤더를 사용하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;X-Musician-Id: 1&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;운영에서는 이 방식은 사용할 수 없다. 사용자가 다른 뮤지션 ID를 임의로 넣을 수 있기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;나중에는 JWT에서 &lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;musicianId&lt;/span&gt;&lt;span&gt;를 꺼내는 방식으로 바꿔야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;또한 long-lived token은 약 60일 동안 유효하므로, 운영에서는 만료 전에 refresh하는 배치가 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&amp;mdash;&amp;mdash;&amp;mdash;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;8. 다음 작업&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;다음으로 구현할 API는 다음이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #747474;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;PATCH /api/music-promotions/{promotionId}/instagram-media&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;이 API는 우리 서비스의 음악 홍보 객체와 Instagram 게시물을 연결한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;필요한 이유는 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;music_promotion은 우리 DB에 있음&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;Instagram media는 Instagram API에서 조회됨&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;하지만 어떤 게시물이 어떤 홍보와 연결된 것인지는 백엔드가 자동으로 알 수 없음&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;그래서 뮤지션이 게시물 목록에서 하나를 선택하면, 해당 게시물의 &lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;mediaId&lt;/span&gt;&lt;span&gt;를 &lt;/span&gt;&lt;span style=&quot;color: #38b9c7;&quot;&gt;music_promotion&lt;/span&gt;&lt;span&gt;과 연결해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;이후에는 다음 흐름이 가능해진다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;promotionId&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;-&amp;gt; 연결된 instagramMediaId 조회&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;-&amp;gt; 해당 media의 shares/profile_visits 조회&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;-&amp;gt; promotion_tracking_click에서 클릭수 조회&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;-&amp;gt; 화면에서 홍보 성과 요약 표시&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발</category>
      <category>Instagram API</category>
      <author>insight00-15</author>
      <guid isPermaLink="true">https://insight00-15.tistory.com/5</guid>
      <comments>https://insight00-15.tistory.com/5#entry5comment</comments>
      <pubDate>Mon, 20 Apr 2026 21:30:06 +0900</pubDate>
    </item>
    <item>
      <title>[개발 도서] 개발자는 글을 못 쓴다고요? - 커밋 메시지 작성하기</title>
      <link>https://insight00-15.tistory.com/4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000218400404&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://product.kyobobook.co.kr/detail/S000218400404&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1776519186074&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;개발자는 글을 못 쓴다고요? | 전정은 - 교보문고&quot; data-og-description=&quot;개발자는 글을 못 쓴다고요? | 문서는 실력이고, 글은 또 하나의 코드입니다개발자는 오늘도 글을 씁니다. 커밋 메시지부터 리드미, 릴리스 노트, 기술 블로그까지 일의 많은 순간에 글이 필요합&quot; data-og-host=&quot;product.kyobobook.co.kr&quot; data-og-source-url=&quot;https://product.kyobobook.co.kr/detail/S000218400404&quot; data-og-url=&quot;https://product.kyobobook.co.kr/detail/S000218400404&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dYIPC5/dJMb9iaSZhL/s4PtKW79AX4IJUN2hvGZy1/img.jpg?width=458&amp;amp;height=606&amp;amp;face=0_0_458_606,https://scrap.kakaocdn.net/dn/IxSQG/dJMb9g5cZOm/IwnrEDemA2ucxPcLuDq30K/img.jpg?width=458&amp;amp;height=606&amp;amp;face=0_0_458_606,https://scrap.kakaocdn.net/dn/cLWlVQ/dJMb9iaSZhM/tcenWbsEgtgLO6WnA3nqJ0/img.jpg?width=599&amp;amp;height=608&amp;amp;face=0_0_599_608&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000218400404&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://product.kyobobook.co.kr/detail/S000218400404&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dYIPC5/dJMb9iaSZhL/s4PtKW79AX4IJUN2hvGZy1/img.jpg?width=458&amp;amp;height=606&amp;amp;face=0_0_458_606,https://scrap.kakaocdn.net/dn/IxSQG/dJMb9g5cZOm/IwnrEDemA2ucxPcLuDq30K/img.jpg?width=458&amp;amp;height=606&amp;amp;face=0_0_458_606,https://scrap.kakaocdn.net/dn/cLWlVQ/dJMb9iaSZhM/tcenWbsEgtgLO6WnA3nqJ0/img.jpg?width=599&amp;amp;height=608&amp;amp;face=0_0_599_608');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;개발자는 글을 못 쓴다고요? | 전정은 - 교보문고&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;개발자는 글을 못 쓴다고요? | 문서는 실력이고, 글은 또 하나의 코드입니다개발자는 오늘도 글을 씁니다. 커밋 메시지부터 리드미, 릴리스 노트, 기술 블로그까지 일의 많은 순간에 글이 필요합&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;product.kyobobook.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;p.12&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 훌륭한 개발자가 되려면 뛰어난 소프트웨어 개발 능력뿐만 아니라 원활한 소통 능력도 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;p.29&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 글 잘 쓰는 개발자가 되려면 쓰지 않는 습관과 코드로 소통한다는 생각의 굴레에서 벗어나야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 우선 단번에 좋은 글을 써내겠다는 희망을 버리세요. 그런 생각을 하면 오히려 글쓰기가 힘들고 어렵게 느껴져 시작하기 힘듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;p.44&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 내가 커밋한 코드를 왜 커밋했는지, 왜 그렇게 구현했는지 시간이 어느 정도 지나면 나도, 또 내 코드를 보는 이도 알 수 없습니다. 기록이 없다면 말입니다. 시간이 지난 후에도 기록이 유의미하도록 기록을 잘 쓰고 잘 남겨야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기록을 잘 찾을 수도 있어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 커밋 메시지를 작성할때는 검색도 고려해야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;p.46&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 커밋 메시지 제목(이하 제목)이 너무 길다면 그 커밋에는 너무 많은 걸 담았다 생각하고 커밋을 분리해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 한 커밋에 담기는 파일들은 전부 동일한 목적을 달성하는 변경 사항을 담은 파일들이어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 커밋 범위를 잘 구성하면 제목을 수월하게 쓸 수 있는것은 물론, 혹여나 롤백rollback해야 할 상황이 발생했을 때 도움이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 깃헙에서 제목과 본문을 구분해 표시하는 커밋 방법은 있습니다. (첫 줄 입력 후 따옴표를 닫지 않고 엔터를 치면 줄바꿈이 됩니다.)&lt;span style=&quot;color: #e6e8f0; text-align: start;&quot; data-processed=&quot;true&quot; data-sfc-cb=&quot;&quot; data-wiz-uids=&quot;kPgRvc_3z,kPgRvc_40&quot; data-sfc-root=&quot;c&quot;&gt;&lt;span data-processed=&quot;true&quot; data-wiz-attrbind=&quot;class=kPgRvc_3y/TKHnVd&quot; data-animation-atomic=&quot;&quot;&gt;&lt;span data-processed=&quot;true&quot; aria-hidden=&quot;true&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1776521052769&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git commit -m &quot;feat: 메인 페이지 UI 개선 --&amp;gt; &amp;lt;제목&amp;gt;
&amp;lt;본문 내용&amp;gt;
- 헤더 디자인 수정
- 푸터 링크 추가&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;p.49&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 리뷰어 입장에서 개발자가 무엇을 달성하려고 코드를 작성했는지 알고 리뷰하는것과 전혀 모른 채 리뷰하는 것은 다르지 않을까요? 목적을 확실하게 알면 커밋된 코드가 실제로 그 목적을 달성하는지 여부를 판단하는 데 도움을 얻을 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;p.50&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 많은 오픈소스 저장소에는 CONTRIBUTING.md 파일이 있습니다. 파일을 보면 지켜야 할 코딩 스타일과 더불어 커밋이나 풀 리퀘스트를 올리는 방법 등을 안내합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 커밋 메시지 제목이나 풀 리퀘스트 제목을 영어로 쓸 때는 동상 명령형으로 시작하는 것을 추천합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- '하다'라는 동작을 '해라', '하게' 등의 형태로 표현하는 것이 동사 명령형입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(예시. 커밋메세지: added an object(x), add an object(0))&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;p.54&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 커밋 메세지의 &lt;b&gt;목적어&lt;/b&gt;를 꾸미는 다양한 방식&lt;/p&gt;
&lt;pre id=&quot;code_1776569511144&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;공식 1: 해라 (어떤) 무엇을
제목  : Add (absent) students

공식 2: 해라 무엇을 (어떤)
제목  : Add students (who are not listed in the roll)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;커밋 메세지의 &lt;b&gt;언제&lt;/b&gt;라는 정보를 추가할 수 있는 방식&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1776566960496&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;공식 1: 2주마다 학생을 출석 시스템에 등록하는 코드 커밋
제목  : Add students to the attendance system every two weeks

공식 2: 출석 시스템이 준비 상태가 되면 학생을 추가하는 코드 커밋
제목  : Add students when the attendance system is ready&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;p.56&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 제목에 쓸 만한 동사&lt;/p&gt;
&lt;pre id=&quot;code_1776567379536&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;기능 분류: 동사
----------------------------------------------------------------------------------
추가    : add, create, generate, include, introduce, issue, post, put, save, store
----------------------------------------------------------------------------------
변경    : bump,change,configure,edit,fix,modify,rename,replace,set,update,upgrade
----------------------------------------------------------------------------------
삭제    : cancel, delete, eject, eliminate, erase, exclude, hide, remove, reset
----------------------------------------------------------------------------------
그 외   : alert,align,allocate,allow,alternate,delimit,display,divide,draw,enable,
		 enter,fix,follow,free,get,handle,initialize,improve,notify,order,process,
         provide,read,refactor,retrieve,return,rewrite,search,separate,show,sum,stop
         strip,support,terminate,use,watch,wait,write
----------------------------------------------------------------------------------&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;p.57&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 커밋한 후 수개월 혹은 수년이 지난 후에도 이 커밋이 무엇을 위해 커밋된 것인지 바로 파악할 수 있도록 상세하게 작성하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 내가 어떤 코드를 추가했고 바꿨는지는 git diff로, 코드 변경 전후를 비교하면 바로 알 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 커밋에 담긴 코드를 언급하는 대신 코드가 수행하는. 기능 관점이나 내가 한 작업을 설명하는 제목을 작성하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 내가 추가한 코드가 하는일. (ex. &quot;Find students by name&quot; )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 내가 추가한 코드 때문에 뭘 할 수 있게 됐는가? (ex. &quot;Enable finding students by name&quot;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; * 이전에는 이름으로 학생을 찾을 수 없었는데 내가 올린 커밋이 병합되면 가능해진다는 것을 알림.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 내가 코드를 추가해서 전과 뭐가 달라졌는가? (ex. &quot;Add the name filter for searching students&quot;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; * 학생을 조회하는 기능은 이미 있었으나, 기존 기능에 부가 기능을 추가한 것을 알림.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;p.62&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 커밋 제목을 줄이려고 아무리 노력해도 짧아지지 않을 때는 유의어를 써보세요. (&lt;a href=&quot;https://www.thesaurus.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.thesaurus.com&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;p.64&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 단어 풀 만들기. 우리 팀에서는 '변경'이란 의미를 표현할 때 'edit'나 'modify'가 아닌 'change'를 쓴다고 명시해두는 거죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;p.64~67&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커밋 메세지 본문 쓰기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 커밋한 이유를 알려주세요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 커밋한 이유, 커밋하게 된 계기를 적어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 외부 자원을 명시해주세요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 새롭게 이용하게 된 외부 라이브러리나 패키지가 있다면 기재해주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 선택한 이유를 알려주세요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 커밋에 반영된 선택을 한 이유를 본문에 남겨주세요.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 코드 리뷰어도 왜 이렇게 하지 않았냐고 질문하지 않을 수 있습니다.나중에 관련 작업을 할 때도 유용한 정보가 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 관련 자료를 첨부해주세요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; * 커밋 메시지에도 정보 자체를 상세히 남기길 권합니다. 자료에 있는 글을 '복붙'을 하더라도요.&lt;/p&gt;</description>
      <category>개발 도서</category>
      <category>git</category>
      <category>PR</category>
      <author>insight00-15</author>
      <guid isPermaLink="true">https://insight00-15.tistory.com/4</guid>
      <comments>https://insight00-15.tistory.com/4#entry4comment</comments>
      <pubDate>Sun, 19 Apr 2026 12:34:07 +0900</pubDate>
    </item>
    <item>
      <title>Github Actions로 개선하는 코드 리뷰 문화</title>
      <link>https://insight00-15.tistory.com/3</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;a href=&quot;https://toss.tech/article/25431&quot;&gt;https://toss.tech/article/25431&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Github Actions로 개선하는 코드 리뷰 문화&lt;/h4&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;김성일, 토스페이먼츠 Server Developer&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드 리뷰 문화의 가장 중요한 조건
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;피드백의 속도&lt;/li&gt;
&lt;li&gt;PR 코멘트로 이루어지는 대화의 양&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Github Actions
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GitHub에서 제공해주는 자동화 툴&lt;/li&gt;
&lt;li&gt;특정&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;이벤트&lt;/b&gt;를 기반으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;워크플로우&lt;/b&gt;를 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Github Actions의 장점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기능 구현과 유지보수가 간편&lt;/li&gt;
&lt;li&gt;사용자가 많아서 접근성이 좋음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;GitHub Actions 커스텀
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/actions/toolkit&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/actions/toolkit&lt;/a&gt;에서 GitHub이 제공한 SDK를 통해 JavaScript / TypeScript로 actions 커스텀 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;토스페이먼츠에서 적용한 GitHub Actions&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;리뷰어 지정하기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;토스페이먼츠에서는 이 GitHub Actions를 사용해 랜덤으로 PR 리뷰어를 지정해주는 자동화 시스템을 구축했다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결과: 한두 명만이 알고있었던 맥락(코드)들도 팀 내 여럿에게 공유되어 운영 이슈 대응 시간 90% 단축.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;리뷰어에게 알림 보내기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;슬랙봇을 사용해, PR 리뷰어로 선정되었을때 슬랙 알림이 갈 수 있도록 설정. 이메일로 알림을 보냈을때는 메일을 자주 확인하지 않는 사람들이 알아차리기 어려운 이슈가 있었음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;오늘 안에 리뷰할 수 있게 하기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GitHub Actions에는 cron표현식을 사용해 &lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;워크플로우 &lt;/span&gt;스케줄링 할 수 있는 이벤트 기능이 있다.&lt;/li&gt;
&lt;li&gt;토스페이먼츠에서 적용한 워크플로우
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;월-금 오후 2시마다 레포지토리에 Open 상태로 되어있는 PR 리뷰 상태 체크&lt;/li&gt;
&lt;li&gt;리뷰 되어있지않은 PR들을 모아 팀 슬랙 채널로 전송&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>개발</category>
      <category>Github Actions</category>
      <category>PR Review</category>
      <category>토스 페이먼츠</category>
      <author>insight00-15</author>
      <guid isPermaLink="true">https://insight00-15.tistory.com/3</guid>
      <comments>https://insight00-15.tistory.com/3#entry3comment</comments>
      <pubDate>Thu, 16 Apr 2026 20:29:48 +0900</pubDate>
    </item>
    <item>
      <title>[개발] &amp;quot;백엔드 개발자&amp;quot; 오픈형 멘토링: 백엔드  - 손권남 멘토</title>
      <link>https://insight00-15.tistory.com/2</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드 개발자- 손권남, 우아한형제들 기술이사.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://2024.woowacon.com/sessions/737/&quot;&gt;https://2024.woowacon.com/sessions/737/&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발을 잘하는것보단 그냥 일 잘하는 사람&lt;/li&gt;
&lt;li&gt;경청이 중요&lt;/li&gt;
&lt;li&gt;회의에서 똑같은 말을 하는 사람들이있음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 말인데 서로 다른 의미로 이해하고 소통하기때문&lt;/li&gt;
&lt;li&gt;같은 용어를 쓰는게 중요(해당 업계에서 쓰는 단어나 뜻, 의미 등을 잘 알고 쓰면 좋을듯)&lt;/li&gt;
&lt;li&gt;경청이 중요한 이유가 여기. 저 사람이 무슨 말을 하고 있는건지 경청&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;서버, 데이터베이스, 기본을 잘 알고 다뤄야한다. 도메인 경험 중요.&lt;/li&gt;
&lt;li&gt;기획자의 기획서를 완벽하지 않다고 비판하기전에 알아서 잘 파악하고, 무결하지가 않다는것을 인정하고, 기획자의 언어, 의도를 잘 파악해서 하면 됨. 모든것은 완벽하지않는다.&lt;/li&gt;
&lt;li&gt;KPT(Keep-유지, Problem-문제, Try-시도), 개발자 성장 치트키.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.google.com/search?client=safari&amp;amp;rls=en&amp;amp;q=Keep&amp;amp;ie=UTF-8&amp;amp;oe=UTF-8&amp;amp;ved=2ahUKEwjQmMerpu2TAxXW5TQHHS8IKCsQgK4QegYIAQgAEA8&quot;&gt;**Keep&lt;/a&gt;&amp;nbsp;(유지할 것):**&amp;nbsp;현재 만족하고 있는 부분, 계속 이어갔으면 하는 잘한 점.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.google.com/search?client=safari&amp;amp;rls=en&amp;amp;q=Problem&amp;amp;ie=UTF-8&amp;amp;oe=UTF-8&amp;amp;ved=2ahUKEwjQmMerpu2TAxXW5TQHHS8IKCsQgK4QegYIAQgAEBE&quot;&gt;**Problem&lt;/a&gt;&amp;nbsp;(문제점):**&amp;nbsp;프로젝트 과정 중 겪은 어려움, 아쉬웠던 점, 개선이 필요한 부분.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.google.com/search?client=safari&amp;amp;rls=en&amp;amp;q=Try&amp;amp;ie=UTF-8&amp;amp;oe=UTF-8&amp;amp;ved=2ahUKEwjQmMerpu2TAxXW5TQHHS8IKCsQgK4QegYIAQgAEBM&quot;&gt;**Try&lt;/a&gt;&amp;nbsp;(시도할 것):**&amp;nbsp;Problem에 대한 해결책이나 앞으로 시도해보고 싶은 구체적인 아이디어.&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;없는문제를 억지를 만들지말고, 실제로 있는 문제를 잘 해결해라.&lt;/li&gt;
&lt;li&gt;어려운 과제가 나올때, 해내야겠다, 어렵겠지만 해봐야겠다 이런 태도를 가지는게 중요
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;안 풀어본 문제도 도전해낼 실력이 뒷받침이 되어야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;기술을 잘하면서도, 사람들 관리를 잘 하는 사람은 많지않다.&lt;/li&gt;
&lt;li&gt;현업에서는, strategy pattern, 일종의 템플릿 콜백 패턴을 흔하게 사용. kiss(Keep It Simple, Stupid) pattern.&lt;/li&gt;
&lt;li&gt;서비스를 만들때는, 어떠한 디자인 패턴을 쓰고싶어, 어떤 기술을 쓰고싶어, 이러한 이유들 때문에 필요없는 코드들을 만들지마라.&lt;/li&gt;
&lt;li&gt;sql 안티패턴 책을 보면 데이터설계때 좋은 책임.&lt;/li&gt;
&lt;li&gt;개발자 머피의법칙 블로그 글을 쓰심.&lt;/li&gt;
&lt;li&gt;실시간 데이터 처리하는데 흔하게 쓰이는 기술 &amp;rarr; cache + non-blocking i/o 아니면 그냥 비동기 처리&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발</category>
      <category>2024</category>
      <category>woowacon</category>
      <author>insight00-15</author>
      <guid isPermaLink="true">https://insight00-15.tistory.com/2</guid>
      <comments>https://insight00-15.tistory.com/2#entry2comment</comments>
      <pubDate>Tue, 14 Apr 2026 21:44:49 +0900</pubDate>
    </item>
    <item>
      <title>k6, Prometheus, Grafana를 사용한 부하 테스트 환경 구성하기(with Docker-compose)</title>
      <link>https://insight00-15.tistory.com/1</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;grafana_result.png&quot; data-origin-width=&quot;1899&quot; data-origin-height=&quot;866&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clBPhY/dJMcaaLzEKF/svQEiQPYc9ms08VhUwyq1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clBPhY/dJMcaaLzEKF/svQEiQPYc9ms08VhUwyq1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clBPhY/dJMcaaLzEKF/svQEiQPYc9ms08VhUwyq1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclBPhY%2FdJMcaaLzEKF%2FsvQEiQPYc9ms08VhUwyq1K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1899&quot; height=&quot;866&quot; data-filename=&quot;grafana_result.png&quot; data-origin-width=&quot;1899&quot; data-origin-height=&quot;866&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;부하 테스트는 시스템에 예상되는 최대 사용자수나 데이터 처리량을 시물레이션하여, 시스템의 응답 시간, 처리 속도(TPS), 안정성 등을 측정하고 병목 현상을 파악하는 성능 테스트이다.&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;▶ &lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;부하 테스트 툴&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;K6&lt;/b&gt;: &amp;nbsp;API에 가상 사용자를 만들어 부하를 주고 성능과 안정성을 검증하는 부하 테스트 도구&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Prometheus&lt;/b&gt;: 애플리케이션과 인프라의 메트릭을 주기적으로 수집하고 저장하는 모니터링 시스템&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Grafana&lt;/b&gt;: Prometheus 같은 데이터소스의 메트릭을 그래프와 대시보드로 시각화하는 도구&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Spring API + Actuator&lt;/b&gt;: &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Spring Boot 앱 상태, 메트릭 상태 지표 제공(JVM, CPU, HTTP 요청 수 메모리 등)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Micrometer&lt;/b&gt;: &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Spring Boot 앱 메트릭을 수집하고 Prometheus 같은 모니터링 시스템과 연결해주는 계측 라이브러리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;docker-compose&lt;/b&gt;: &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;여러 컨테이너 서비스를 한 번에 실행할 수 있게 해주는 도구&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #000000; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;▶ &lt;b&gt;테스트 환경 폴더 경로 설계&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;folder_structure.png&quot; data-origin-width=&quot;378&quot; data-origin-height=&quot;712&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFLiXS/dJMcajuWSvU/mxk6osfszItCEDYOfplHrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFLiXS/dJMcajuWSvU/mxk6osfszItCEDYOfplHrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFLiXS/dJMcajuWSvU/mxk6osfszItCEDYOfplHrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFLiXS%2FdJMcajuWSvU%2Fmxk6osfszItCEDYOfplHrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;200&quot; height=&quot;377&quot; data-filename=&quot;folder_structure.png&quot; data-origin-width=&quot;378&quot; data-origin-height=&quot;712&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;일단 이런식으로 나중에 쓸 docker-compose.yml 파일과 같은 경로에 폴더 경로들을 구성한다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;▶ &lt;b&gt;기본 설정 구성&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;&lt;b&gt;(1). application.yml 설정&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;management:
  endpoints:
    web:
      exposure:
        include: &quot;prometheus&quot;  
metrics:
  export:
    prometheus:
      enabled: true&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;&lt;b&gt;(2). build.gralde 의존성 추가&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1775189179171&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation(&quot;org.springframework.boot:spring-boot-starter-actuator:3.4.3&quot;)
implementation(&quot;io.micrometer:micrometer-registry-prometheus:1.14.5&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;&lt;b&gt;(3). 실행 확인(브라우저/터미널)&lt;/b&gt;&lt;br /&gt;-브라우저-&lt;/h4&gt;
&lt;pre id=&quot;code_1775189194172&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;http://localhost:8080/actuator/prometheus&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;-터미널-&lt;/p&gt;
&lt;pre id=&quot;code_1775130667247&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl http://localhost:8080/actuator/prometheus&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이런식으로 아래 문구처럼 뜬다면 Actuator와 Micrometer 설정은 끝나고 Prometheus는 결과를 수집할 준비가 되었다는 뜻.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;curlActuator.png&quot; data-origin-width=&quot;1794&quot; data-origin-height=&quot;1238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1Houv/dJMcaadKj1V/4iF58j2kkvGQ8T0HBWIIP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1Houv/dJMcaadKj1V/4iF58j2kkvGQ8T0HBWIIP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1Houv/dJMcaadKj1V/4iF58j2kkvGQ8T0HBWIIP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1Houv%2FdJMcaadKj1V%2F4iF58j2kkvGQ8T0HBWIIP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;414&quot; data-filename=&quot;curlActuator.png&quot; data-origin-width=&quot;1794&quot; data-origin-height=&quot;1238&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;▶&lt;b&gt;&lt;span&gt; 도커 이미지 기반 Prometheus + Grafana 연동&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(1). docker-compose.yml 설정&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1775189776532&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;services:
  prometheus:
    image: prom/prometheus:v2.47.0
    container_name: test-monitoring-prometheus
    ports:
      - &quot;9090:9090&quot;
    command:
      - --web.enable-remote-write-receiver
      - --enable-feature=native-histograms
      - --config.file=/etc/prometheus/prometheus.yml
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
    extra_hosts:
      - &quot;host.docker.internal:host-gateway&quot;

  grafana:
    image: grafana/grafana:10.1.2
    container_name: test-monitoring-grafana
    ports:
      - &quot;3000:3000&quot;
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - ./grafana-provisioning:/etc/grafana/provisioning:ro
      - ./grafana-dashboard:/dashboard:ro
    depends_on:
      - prometheus

  k6:
    image: grafana/k6:latest
    container_name: test-monitoring-k6
    volumes:
      - ./k6-scripts:/scripts:ro
    environment:
      - K6_PROMETHEUS_RW_SERVER_URL=http://prometheus:9090/api/v1/write
      - K6_PROMETHEUS_RW_TREND_AS_NATIVE_HISTOGRAM=true
      - BASE_URL=http://host.docker.internal:8080
      - PERFORMANCE_ID=1
      - MEMBER_ID=1
    extra_hosts:
      - &quot;host.docker.internal:host-gateway&quot;
    depends_on:
      - prometheus&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, &quot;host.docker.internal:host-gateway&quot;이런 라인이 보일텐데, 어느 블로그에선 localhost를 쓰는 경우도 있고, host.docker.internal을 쓰는 경우도있다. 지금 같은 경우엔, 로컬PC안에서 Spring Boot 앱이 실행중이고 K6, Prometheus는 docker를 쓰는 환경이기때문에, host.docker.internal을 써야 K6나 Prometheus같이 도커 컨테이너가 로컬PC에서 실행중인 앱에 호스트 IP를 통해서 붙을 수가 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;쉽게 말해서, host.docker.internal은 도커 컨테이너 안에서 로컬 호스트 컴퓨터에 접속하기 위해 사용하는 주소이다.&lt;br /&gt;https://grafana.com/docs/grafana/latest/datasources/prometheus/configure/&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(2). prometheus.yml 설정&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1775190747673&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;global:
  scrape_interval: 5s

scrape_configs:
  - job_name: prometheus
    static_configs:
      - targets: [&quot;prometheus:9090&quot;]

  - job_name: spring-boot-app
    metrics_path: /actuator/prometheus
    static_configs:
      - targets: [&quot;host.docker.internal:8080&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(3). /grafana-dashboard/k6.json 설정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;k6.json 파일 같은 경우엔 아래 링크에서 우측 하단의 Download JSON 버튼을 통해서 붙여넣어주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://grafana.com/grafana/dashboards/18030-k6-prometheus-native-histograms/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://grafana.com/grafana/dashboards/18030-k6-prometheus-native-histograms/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1775191061127&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;k6 Prometheus (Native Histograms) | Grafana Labs&quot; data-og-description=&quot;Grafana 12.4 is here &amp;mdash; faster and easier data visualization, Git Sync for observability as code updates, and more.&quot; data-og-host=&quot;grafana.com&quot; data-og-source-url=&quot;https://grafana.com/grafana/dashboards/18030-k6-prometheus-native-histograms/&quot; data-og-url=&quot;https://grafana.com/grafana/dashboards/18030-k6-prometheus-native-histograms/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b6ygDv/dJMb89532fZ/8ebnOrjrdDgyDmpXEX9OfK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/diBLd8/dJMb9g5bzme/qZhNgphSXqi2BdJOXUnw91/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://grafana.com/grafana/dashboards/18030-k6-prometheus-native-histograms/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://grafana.com/grafana/dashboards/18030-k6-prometheus-native-histograms/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b6ygDv/dJMb89532fZ/8ebnOrjrdDgyDmpXEX9OfK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/diBLd8/dJMb9g5bzme/qZhNgphSXqi2BdJOXUnw91/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;k6 Prometheus (Native Histograms) | Grafana Labs&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Grafana 12.4 is here &amp;mdash; faster and easier data visualization, Git Sync for observability as code updates, and more.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;grafana.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(4). /grafana-provisioning/dashboards/dashboards.yml 설정&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1775191160516&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: 1

providers:
  - name: &quot;k6 Stress Test&quot;
    orgId: 1
    folder: &quot;&quot;
    type: file
    disableDeletion: false
    updateIntervalSeconds: 10
    options:
      path: /dashboard
      foldersFromFilesStructure: true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(5). /grafana-provisioning/datasources/prometheus.yml 설정&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1775191423487&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 prometheus.yml 파일 두개를 만들었을텐데, 전에 2번 단계에서 만들었던 prometheus.yml파일은 어디를 수집할지, 몇 초 마다 할지, 이런 Prometheus의 서버 설정에 대한 수정이였다면, 여기서는 나중에 Grafana가 Prometheus에 대한 데이터를 받을때 필요한 어떤 &lt;span&gt;Prometheus서버인지 url은 뭔지, g&lt;/span&gt;rafana datasource 설정 파일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(6). /k6-scripts/stress.js 설정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이때 부하 테스트 스크립트는 따로 생성해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(7). docker-compose 실행&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1775191843678&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker compose up -d&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;▶&lt;b&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;Grafana 대시보드 생성 및 테스트 실행 결과&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(1). 대시보드 확인&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1775192618910&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;http://localhost:3000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 url 을 통해서 Grafana사이트를 접속후에 로그인을 해준다&lt;/span&gt;&lt;b&gt;&lt;span&gt;(id: admin, pw: admin).&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;span&gt;왼쪽 창에 대시보드를 들어가면, 이미 생성되어있는 대시보드가 있을것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(2). 테스트 실행 및 결과 확인&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1775192925846&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker compose run --rm k6 run -o experimental-prometheus-rw /scripts/&amp;lt;테스트파일_이름&amp;gt;.js&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 커맨드로 부하 테스트를 진행해주고, 다시 Grafana들어가서 대시보드를 새로고침해주면 결과 값들이 보일것이다.&lt;/p&gt;
&lt;p style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;grafana_result.png&quot; data-origin-width=&quot;1899&quot; data-origin-height=&quot;866&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MgIYB/dJMcab4L97P/KQa6atto116PKpVwtPv9k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MgIYB/dJMcab4L97P/KQa6atto116PKpVwtPv9k1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MgIYB/dJMcab4L97P/KQa6atto116PKpVwtPv9k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMgIYB%2FdJMcab4L97P%2FKQa6atto116PKpVwtPv9k1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1899&quot; height=&quot;866&quot; data-filename=&quot;grafana_result.png&quot; data-origin-width=&quot;1899&quot; data-origin-height=&quot;866&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;k6_test_results.png&quot; data-origin-width=&quot;1792&quot; data-origin-height=&quot;1914&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXDLTW/dJMcaaLzZrM/bRmXjUnJ6252FgKIO7wkl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXDLTW/dJMcaaLzZrM/bRmXjUnJ6252FgKIO7wkl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXDLTW/dJMcaaLzZrM/bRmXjUnJ6252FgKIO7wkl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXDLTW%2FdJMcaaLzZrM%2FbRmXjUnJ6252FgKIO7wkl0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;641&quot; data-filename=&quot;k6_test_results.png&quot; data-origin-width=&quot;1792&quot; data-origin-height=&quot;1914&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;▶&lt;b&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt; 참고 자료&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;a href=&quot;https://grafana.com/docs/grafana/latest/setup-grafana/installation/docker/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://grafana.com/docs/grafana/latest/setup-grafana/installation/docker/&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1775193443127&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Run Grafana Docker image | Grafana documentation&quot; data-og-description=&quot;CautionStarting with Grafana release 12.4.0 , the grafana/grafana-oss Docker Hub repository will no longer be updated. Instead, we encourage you to use the grafana/grafana Docker Hub repository. These two repositories have the same Grafana OSS docker image&quot; data-og-host=&quot;grafana.com&quot; data-og-source-url=&quot;https://grafana.com/docs/grafana/latest/setup-grafana/installation/docker/&quot; data-og-url=&quot;https://grafana.com/docs/grafana/latest/setup-grafana/installation/docker/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/nTivf/dJMb8SpInOX/giadlRtUPW2MIKeKpjejJk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bScGEZ/dJMb89532xT/fA898ugzACgkzbk4cvPKCk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://grafana.com/docs/grafana/latest/setup-grafana/installation/docker/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://grafana.com/docs/grafana/latest/setup-grafana/installation/docker/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/nTivf/dJMb8SpInOX/giadlRtUPW2MIKeKpjejJk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bScGEZ/dJMb89532xT/fA898ugzACgkzbk4cvPKCk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Run Grafana Docker image | Grafana documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;CautionStarting with Grafana release 12.4.0 , the grafana/grafana-oss Docker Hub repository will no longer be updated. Instead, we encourage you to use the grafana/grafana Docker Hub repository. These two repositories have the same Grafana OSS docker image&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;grafana.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://grafana.com/docs/k6/latest/results-output/real-time/prometheus-remote-write/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://grafana.com/docs/k6/latest/results-output/real-time/prometheus-remote-write/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1775193466576&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Prometheus remote write | Grafana k6 documentation&quot; data-og-description=&quot;User-centered observability: load testing, real user monitoring, and synthetics Learn how to use load testing, synthetic monitoring, and real user monitoring (RUM) to understand end users' experience of your apps. Watch on demand.&quot; data-og-host=&quot;grafana.com&quot; data-og-source-url=&quot;https://grafana.com/docs/k6/latest/results-output/real-time/prometheus-remote-write/&quot; data-og-url=&quot;https://grafana.com/docs/k6/latest/results-output/real-time/prometheus-remote-write/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Nk0ez/dJMb8SpInPx/DcQMJS3fhGbGKwLKIUYJ40/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/KtZIp/dJMb8XR55AJ/VG8NeZ7tJEt7krM0dqRSa0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://grafana.com/docs/k6/latest/results-output/real-time/prometheus-remote-write/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://grafana.com/docs/k6/latest/results-output/real-time/prometheus-remote-write/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Nk0ez/dJMb8SpInPx/DcQMJS3fhGbGKwLKIUYJ40/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/KtZIp/dJMb8XR55AJ/VG8NeZ7tJEt7krM0dqRSa0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Prometheus remote write | Grafana k6 documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;User-centered observability: load testing, real user monitoring, and synthetics Learn how to use load testing, synthetic monitoring, and real user monitoring (RUM) to understand end users' experience of your apps. Watch on demand.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;grafana.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://one-armed-boy.tistory.com/entry/k6-Prometheus-Grafana-자동-환경-구성하기-with-Docker-compose&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://one-armed-boy.tistory.com/entry/k6-Prometheus-Grafana-자동-환경-구성하기-with-Docker-compose&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1775193682146&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;부하테스트를 위해 k6 + Prometheus + Grafana 자동 환경 구성하기 (with Docker-compose)&quot; data-og-description=&quot;k6를 통하면 부하테스트를 js 기반 스크립트로 손쉽게 작성이 가능하다. 다만 k6는 단순 테스트 만을 수행해주기 때문에 추가적인 시각화 구성을 진행해야 할 필요가 있다. 이를 위해 구글링을 해&quot; data-og-host=&quot;one-armed-boy.tistory.com&quot; data-og-source-url=&quot;https://one-armed-boy.tistory.com/entry/k6-Prometheus-Grafana-자동-환경-구성하기-with-Docker-compose&quot; data-og-url=&quot;https://one-armed-boy.tistory.com/entry/k6-Prometheus-Grafana-%EC%9E%90%EB%8F%99-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0-with-Docker-compose&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dH02F7/dJMb89532zE/8ICw0TxdWk9q6f53lbWlP0/img.png?width=278&amp;amp;height=262&amp;amp;face=0_0_278_262,https://scrap.kakaocdn.net/dn/wJpYp/dJMb8869eGY/lX5GcJkxU20WH6yTAijnq1/img.png?width=278&amp;amp;height=262&amp;amp;face=0_0_278_262,https://scrap.kakaocdn.net/dn/gJpOi/dJMb81fS2wM/ckqaDv4GFvA1tljJG1gigk/img.png?width=1280&amp;amp;height=573&amp;amp;face=0_0_1280_573&quot;&gt;&lt;a href=&quot;https://one-armed-boy.tistory.com/entry/k6-Prometheus-Grafana-자동-환경-구성하기-with-Docker-compose&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://one-armed-boy.tistory.com/entry/k6-Prometheus-Grafana-자동-환경-구성하기-with-Docker-compose&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dH02F7/dJMb89532zE/8ICw0TxdWk9q6f53lbWlP0/img.png?width=278&amp;amp;height=262&amp;amp;face=0_0_278_262,https://scrap.kakaocdn.net/dn/wJpYp/dJMb8869eGY/lX5GcJkxU20WH6yTAijnq1/img.png?width=278&amp;amp;height=262&amp;amp;face=0_0_278_262,https://scrap.kakaocdn.net/dn/gJpOi/dJMb81fS2wM/ckqaDv4GFvA1tljJG1gigk/img.png?width=1280&amp;amp;height=573&amp;amp;face=0_0_1280_573');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;부하테스트를 위해 k6 + Prometheus + Grafana 자동 환경 구성하기 (with Docker-compose)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;k6를 통하면 부하테스트를 js 기반 스크립트로 손쉽게 작성이 가능하다. 다만 k6는 단순 테스트 만을 수행해주기 때문에 추가적인 시각화 구성을 진행해야 할 필요가 있다. 이를 위해 구글링을 해&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;one-armed-boy.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>테스트/load test(부하 테스트)</category>
      <category>grafana</category>
      <category>K6</category>
      <category>Prometheus</category>
      <author>insight00-15</author>
      <guid isPermaLink="true">https://insight00-15.tistory.com/1</guid>
      <comments>https://insight00-15.tistory.com/1#entry1comment</comments>
      <pubDate>Fri, 3 Apr 2026 14:22:22 +0900</pubDate>
    </item>
  </channel>
</rss>