교육 문의커뮤니티 입장하기

LinkedIn API 연동 이해하기: 초안 확인부터 OAuth와 게시 구조까지

블로그 소개글을 LinkedIn에 올리는 연동 구조를 설명합니다. 먼저 계정 연결 없이 초안을 확인하고, OAuth 권한과 토큰 저장, 실제 공개 게시를 구분해 배웁니다. Next.js 구현에 필요한 조건과 오류 대응도 함께 정리했습니다.

아직 읽은 기록이 많지 않은 글이에요
Share
LinkedIn API 연동 이해하기: 초안 확인부터 OAuth와 게시 구조까지 대표 이미지
목차
  1. 복사-붙여넣기가 싫었습니다
  2. 첫 연습은 게시할 초안 한 개 확인하기
  3. 완성된 모습부터 보겠습니다
  4. 전체 구조를 먼저 이해합니다
  5. OAuth 2.0이 뭔가요
  6. 사전 준비: LinkedIn Developer App 등록
  7. 1단계: DB 테이블 만들기
  8. 2단계: 서비스 레이어 만들기
  9. 3단계: API 라우트 만들기
  10. 4단계: UI 수정
  11. 막혔을 때 확인할 것
  12. 앱 등록용 회사 페이지와 게시 계정
  13. 자주 묻는 질문
  14. 정리
  15. Sources
  16. 3줄 요약

세 줄로 먼저 읽기

이번 방문에서 한 편은 바로 볼 수 있습니다.

복사-붙여넣기가 싫었습니다

이 기능을 만들던 당시 준이아빠블로그에는 글을 쓰고, 수정하고, 배포 상태를 관리하는 페이지가 있었습니다. 여기에 AI로 LinkedIn 소개글을 자동 생성하는 기능도 만들어 뒀습니다.

그런데 생성된 글을 LinkedIn에 올리려면 매번 같은 과정을 반복해야 했습니다. 화면 위에 뜨는 작은 창인 모달에서 텍스트를 복사하고, LinkedIn을 열고, 새 게시물을 만들고, 붙여넣고, 게시 버튼을 누르는 5단계입니다.

글 하나에 30초밖에 안 걸리는 작업이지만, 반복될수록 거슬립니다. "여기서 바로 올릴 수 없나?"라는 생각이 들었고, LinkedIn API를 연동하기로 했습니다.

이 글은 그 연동 구조를 설명하는 개발 사례입니다. API는 프로그램끼리 요청을 주고받는 통로이고, Posts API는 LinkedIn 게시물을 만드는 통로입니다. 아래 코드는 완성된 앱 전체가 아닙니다. 웹 앱을 만드는 Next.js 프로젝트, 관리자 로그인과 데이터를 저장할 개발용 데이터베이스(DB)가 있는 분이 자신의 환경에 맞게 구현할 때 참고하는 예제입니다. 지금 블로그에서 같은 관리 화면이 제공된다고 가정하지 않습니다.

처음 배우는 분은 다음 초안 연습과 인증 흐름까지 읽으면 됩니다. 계정 등록과 실제 게시를 끝내야 글의 내용을 이해할 수 있는 것은 아닙니다.

첫 연습은 게시할 초안 한 개 확인하기

메모 앱에서 새 문서를 만들고 아래 가상 소개글을 붙여 넣습니다. LinkedIn 계정이나 API 비용 없이 할 수 있는 연습입니다.

제목: 회의 메모에서 할 일을 뽑는 방법 소개: 회의 메모에서 담당자와 기한을 구분하는 연습을 정리했습니다. 확인할 점: 원문에 없는 담당자와 날짜를 덧붙이지 않습니다. 상태: 초안, 아직 게시하지 않음

LinkedIn 소개글 연습으로 저장하고 다시 엽니다. 제목이 무엇을 알려주는지, 소개가 그 제목과 맞는지, 없는 성과나 수치가 추가되지 않았는지 확인합니다. 링크를 넣는다면 실제 공개 글의 주소를 직접 열어 본 뒤 추가합니다. 위 연습에는 가짜 링크를 넣지 않습니다.

반복할 때는 주제를 ‘주간 일정 정리’로 바꾸고 소개와 확인할 점도 함께 수정합니다. 제목만 바꾸고 본문은 회의 메모 이야기로 남겨두면 수정이 덜 된 것입니다. 이 연습을 마치면 AI가 만든 소개글에서도 무엇을 확인할지 설명할 수 있습니다.

완성된 모습부터 보겠습니다

이 사례에서 목표로 삼은 흐름은 이렇습니다. 버튼 이름은 예시이며 다른 블로그에 기본으로 있는 기능은 아닙니다.

  1. 관리 페이지에서 포스트를 선택합니다
  2. "LinkedIn Summary" 버튼을 누르면 AI가 소개글을 생성합니다
  3. 모달에서 내용을 편집할 수 있습니다
  4. 계정 이름과 본문, 공개 범위를 확인한 뒤 "Post to LinkedIn"을 누르면 공개 게시를 요청합니다

기존에는 3번에서 끝이었습니다. 복사 버튼만 있었고, 텍스트는 읽기 전용이었습니다. 여기에 편집, 글자 수 카운터와 연결 상태 표시를 추가하는 구조입니다. 미리보기 버튼과 실제 공개 게시 버튼은 명확히 구분합니다.

변경 전후를 비교하면 이렇습니다.

항목변경 전변경 후
소개글 편집불가 (읽기 전용)가능
게시 방식복사 → LinkedIn 수동 붙여넣기버튼 한 번으로 자동 게시
글자 수 확인불가실시간 카운터 (3,000자 제한)
LinkedIn 연결없음OAuth 연결 상태 표시

전체 구조를 먼저 이해합니다

코드를 쓰기 전에, 전체 흐름을 머릿속에 그리는 것이 중요합니다. LinkedIn API 연동은 크게 세 단계로 나뉩니다.

1단계: OAuth 인증: 사용자가 LinkedIn에 로그인하고, "이 앱이 내 계정에 글을 올려도 됩니다"라고 허락하는 과정입니다.

2단계: 토큰 저장: 허락의 증거로 받은 "액세스 토큰"을 DB에 저장합니다. 이 토큰이 있어야 API를 호출할 수 있습니다.

3단계: 게시: 저장된 토큰을 사용해 LinkedIn Posts API를 호출하고, 텍스트를 게시합니다.

이 세 단계를 구현하기 위해 만들어야 하는 것은 다음과 같습니다.

  • DB 테이블 1개: 토큰을 저장할 곳
  • 서비스 레이어 1개: LinkedIn 관련 로직을 모아둔 파일
  • API 라우트 4개: 요청을 받는 주소로, 인증 시작, 콜백 처리, 상태 조회, 게시를 각각 담당
  • UI 수정: 사용자가 보는 화면에 버튼과 상태 표시 추가

OAuth 2.0이 뭔가요

LinkedIn API를 사용하려면 OAuth 2.0 인증을 거쳐야 합니다. 처음 들으면 복잡하게 느껴지지만, 핵심은 단순합니다.

일상에서 비유하면 이렇습니다. 아파트 택배 보관함에서 택배를 찾으려면 비밀번호가 필요합니다. 그런데 이 비밀번호는 아파트 관리실(LinkedIn)이 발급합니다. 본인(사용자)이 관리실에 가서 "이 사람(우리 앱)이 내 보관함에 접근해도 됩니다"라고 동의하면, 관리실이 임시 비밀번호(액세스 토큰)를 발급합니다.

기술적으로 정리하면 이런 흐름입니다.

  1. 우리 앱이 사용자를 LinkedIn 인증 페이지로 보냅니다
  2. 사용자가 LinkedIn에서 "허용"을 누릅니다
  3. LinkedIn이 우리 앱에 "인증 코드"를 전달합니다
  4. 우리 앱이 인증 코드를 "액세스 토큰"으로 교환합니다
  5. 이 토큰으로 API를 호출합니다

왜 인증 코드를 한 번 더 토큰으로 교환하는 걸까요? 보안 때문입니다. 인증 코드는 URL에 노출되지만 짧은 시간 안에 만료됩니다. 토큰 교환은 서버에서 HTTPS로 처리하고 토큰을 브라우저로 돌려주지 않도록 구현합니다. 서버 간 통신이라는 이유만으로 로그나 저장소의 노출까지 자동으로 막아주지는 않습니다.

사전 준비: LinkedIn Developer App 등록

코드를 작성하기 전에 LinkedIn Developer 포털에서 앱을 등록해야 합니다.

  1. LinkedIn Developer Apps에서 앱을 생성합니다
  2. Products 탭에서 개인 게시용 "Share on LinkedIn"과 사용자 식별용 "Sign In with LinkedIn using OpenID Connect"를 신청하고 이용 가능한 상태인지 확인합니다
  3. Auth 탭에서 Client ID와 Client Secret을 확인합니다
  4. 같은 탭에서 Redirect URL을 등록합니다

Redirect URL은 OAuth 인증이 완료된 후 LinkedIn이 사용자를 돌려보내는 주소입니다. 개발 환경과 실제 서비스 환경에 쓸 정확한 주소를 등록해야 합니다. 공식 안내는 HTTPS 콜백을 기준으로 하므로 개발 환경도 자신이 관리하는 HTTPS 주소를 준비합니다. 아래 도메인은 형식 예시이며 그대로 등록할 주소가 아닙니다.

개발: https://dev.example.com/api/linkedin/callback 프로덕션: https://yourdomain.com/api/linkedin/callback

Client ID는 앱 식별자, Client Secret은 서버만 보관하는 비밀값입니다. 아래는 값의 이름을 보여주는 예시입니다. 실제 Secret은 서버용 .env.local에 저장하고 Git에 포함하지 않습니다. 브라우저에 공개되는 NEXT_PUBLIC_ 이름을 붙이지 않습니다.

LINKEDIN_CLIENT_ID=your_client_id LINKEDIN_CLIENT_SECRET=your_client_secret LINKEDIN_REDIRECT_URI=https://dev.example.com/api/linkedin/callback

1단계: DB 테이블 만들기

토큰을 어딘가에 저장해야 합니다. 환경 변수에 넣을 수도 있지만, 토큰은 갱신될 때마다 값이 바뀌기 때문에 DB가 적합합니다.

Drizzle ORM은 TypeScript 코드로 DB 구조와 조회를 작성하는 도구입니다. Drizzle과 PostgreSQL을 이미 쓰는 프로젝트라면 아래처럼 저장할 항목을 정할 수 있습니다. import와 DB 연결은 프로젝트마다 다르므로 아래 정의만 붙여 넣어서는 실행되지 않습니다.

export const linkedinTokens = pgTable("linkedin_tokens", { id: serial("id").primaryKey(), personUrn: varchar("person_urn", { length: 255 }).notNull(), accessToken: text("access_token").notNull(), refreshToken: text("refresh_token"), accessTokenExpiresAt: timestamp("access_token_expires_at").notNull(), refreshTokenExpiresAt: timestamp("refresh_token_expires_at"), scopes: varchar("scopes", { length: 500 }), linkedinName: varchar("linkedin_name", { length: 255 }), createdAt: timestamp("created_at").defaultNow().notNull(), updatedAt: timestamp("updated_at").defaultNow().notNull(), });

각 컬럼의 역할을 짚어보겠습니다.

  • personUrn: LinkedIn 사용자 고유 식별자입니다. urn:li:person:abc123 형태로, 게시물을 올릴 때 "누구의 이름으로 올릴지"를 지정하는 데 사용됩니다
  • accessToken: API를 호출할 때 매번 보내야 하는 인증 키입니다. 기본 유효 기간은 60일이지만 저장할 만료 시점은 실제 응답의 expires_in을 기준으로 계산합니다
  • refreshToken: 액세스 토큰이 만료되었을 때 새 토큰을 발급받는 데 사용됩니다. 자동 갱신이 허용된 앱에 발급될 때만 사용할 수 있습니다
  • accessTokenExpiresAt: 토큰 만료 시점입니다. 이 시간이 지나면 토큰이 무효화됩니다

한 명만 연결하는 앱이라도 계정 ID와 앱의 관리자 ID를 연결해서 저장합니다. 기존 행을 먼저 지우기보다 새 인증이 끝난 뒤 여러 DB 변경을 함께 성공하거나 함께 취소하는 트랜잭션으로 교체해 중간 실패를 처리합니다. 여러 사용자를 받는 앱은 사용자별 토큰이 필요합니다. 토큰은 서버에서만 접근하고 암호화해 보관하며, 상태 조회 응답이나 로그에 넣지 않습니다. 위의 text 컬럼 정의만으로 저장값이 암호화되지는 않습니다.

DB 변경은 DB 구조의 변경 이력을 관리하는 migration 절차로 개발용 DB에서 먼저 확인합니다. db:push라는 명령이 있는지, 어느 DB를 가리키는지 모르는 상태에서 실행하지 않습니다. 이 글은 DB를 바꾸는 명령까지 제공하는 설치 안내는 아닙니다.

2단계: 서비스 레이어 만들기

서비스 레이어는 LinkedIn 관련 로직을 한 곳에 모아둔 파일입니다. API 라우트에서 직접 LinkedIn 로직을 쓰면 코드가 뒤섞이기 때문에, 별도 파일로 분리합니다.

아래는 서비스 파일에서 나눠 맡을 역할입니다. getAuthClient, db, saveTokens 같은 이름은 프로젝트에서 구현해야 하는 함수와 연결 객체이며 내장 기능이 아닙니다.

인증 URL 생성

export function getAuthorizationUrl(state: string): string { const authClient = getAuthClient(); return authClient.generateMemberAuthorizationUrl( ["openid", "profile", "w_member_social"], state ); }

openid와 profile은 사용자 정보를 가져오기 위한 권한이고, w_member_social은 게시물을 올리기 위한 권한입니다. state는 인증을 시작한 요청과 돌아온 요청을 연결하는 임의의 값입니다. 다른 곳에서 만든 요청을 정상 동의인 것처럼 처리하는 CSRF 공격을 막으려면, 생성한 값을 관리자 세션에 저장하고 콜백에서 비교한 뒤 폐기해야 합니다.

토큰 교환

LinkedIn이 보내준 인증 코드를 액세스 토큰으로 바꾸는 함수입니다.

export async function exchangeCodeForTokens(code: string) { const authClient = getAuthClient(); return authClient.exchangeAuthCodeForAccessToken(code); }

linkedin-api-client 라이브러리가 HTTP 요청을 대신 처리해 주므로, 코드는 간단합니다.

프로필 조회

LinkedIn 사용자의 이름과 고유 ID(Person URN)를 가져옵니다.

export async function getLinkedInProfile(accessToken: string) { const response = await fetch("https://api.linkedin.com/v2/userinfo", { headers: { Authorization: `Bearer ${accessToken}` }, }); if (!response.ok) throw new Error(`Profile request failed: ${response.status}`); const data = await response.json(); if (typeof data.sub !== "string" || !data.sub) { throw new Error("Profile ID is missing"); } return { personUrn: `urn:li:person:${data.sub}`, name: data.name || "LinkedIn User", }; }

OpenID Connect의 /userinfo 엔드포인트를 사용합니다. data.sub이 사용자의 고유 ID이고, 이것을 urn:li:person: 형태로 조합하면 LinkedIn API에서 사용할 수 있는 Person URN이 됩니다.

유효한 토큰 선택

자동 갱신이 모든 앱에 제공되는 것은 아닙니다. 공식 문서는 승인된 Marketing Developer Platform 파트너를 대상으로 프로그램 방식의 리프레시 토큰을 안내합니다. 일반 개인 게시용 앱은 리프레시 토큰 없이 동작하는 경우를 기본으로 처리합니다.

다음은 실행 코드가 아닌 처리 순서입니다.

현재 관리자에게 연결된 토큰을 서버에서 찾는다 없으면 재연결 필요를 반환한다 만료 시점까지 여유가 있으면 기존 토큰을 사용한다 자동 갱신 권한과 유효한 refresh_token이 있으면 갱신을 시도한다 갱신 성공 시 새 토큰과 응답의 만료 시점을 함께 저장한다 갱신할 수 없지만 기존 토큰이 아직 유효하면 기존 토큰을 사용한다 만료됐거나 LinkedIn이 토큰을 취소했다면 재연결을 안내한다

만료 7일 전은 앱이 정할 수 있는 갱신 준비 시점이지, LinkedIn의 필수 규칙이 아닙니다. 일주일 남았다는 이유만으로 아직 유효한 일반 토큰까지 무효로 처리하지 않습니다. 자동 갱신이 성공해도 토큰이 나중에 취소될 수 있으므로 게시 오류를 별도로 처리합니다.

게시 본문을 먼저 준비하기

공개 게시와 본문 준비를 분리하면 실수로 글을 올리지 않고 입력부터 확인할 수 있습니다. 다음 함수는 외부 요청을 보내지 않고 게시용 객체만 만듭니다. 길이는 JavaScript 문자열 길이로 3,000 이내인지 먼저 검사하는 예시이며, 실제 API의 최종 검증도 처리해야 합니다.

function preparePost(text, personUrn) { if (typeof text !== "string" || !text.trim()) { throw new Error("본문을 입력해 주세요."); } if (text.length > 3000) { throw new Error("본문을 3,000자 이내로 줄여 주세요."); } if (!/^urn:li:person:[A-Za-z0-9_-]+$/.test(personUrn)) { throw new Error("연결된 개인 계정을 확인해 주세요."); } return { author: personUrn, commentary: text, visibility: "PUBLIC", distribution: { feedDistribution: "MAIN_FEED", targetEntities: [], thirdPartyDistributionChannels: [], }, lifecycleState: "PUBLISHED", isReshareDisabledByAuthor: false, }; } console.log(preparePost("연동 구조를 배우는 가상 초안입니다.", "urn:li:person:demo"));

demo는 형식을 확인하기 위한 가짜 ID입니다. 실제 전송에 쓰지 않습니다. 이미 Node.js가 설치된 개발 환경이라면 코드 편집기의 새 파일에 이 코드만 붙여 넣고 linkedin-preview.cjs로 저장합니다. 파일명 끝에 .txt가 붙지 않았는지 확인하고, 해당 폴더의 터미널에서 node linkedin-preview.cjs를 실행합니다. Cannot find module이 나오면 파일을 저장한 폴더에서 실행했는지 확인합니다. author, commentary, visibility가 출력되면 본문 준비 확인은 끝입니다. LinkedIn에는 아무것도 전송되지 않습니다.

본문을 빈 문자열 ""로 바꾸면 오류가 나고, 다른 짧은 문장으로 바꾸면 그 문장이 commentary에 나와야 합니다. 파일을 다시 열어 수정하고 재실행하면 입력과 결과의 관계를 확인할 수 있습니다. Node.js가 없다면 첫 메모 연습까지만 진행해도 됩니다.

실제 전송은 관리자 인증과 게시 의사를 확인한 서버에서 POST https://api.linkedin.com/rest/posts로 처리합니다. 토큰과 본문 외에 Linkedin-Version 및 X-Restli-Protocol-Version: 2.0.0 헤더가 필요합니다. 과거 예제의 202401을 그대로 쓰지 말고 Posts API 문서에서 지원 중인 버전을 골라 서버 설정으로 관리합니다.

visibility: PUBLIC과 lifecycleState: PUBLISHED는 공개 게시 요청입니다. 로컬 객체를 출력하는 것과 이 객체를 실제 API로 보내는 것은 다릅니다. 성공 시 HTTP 201과 응답 헤더 x-restli-id의 게시물 ID를 저장합니다. 네트워크 오류로 응답을 못 받았을 때는 게시됐을 가능성이 있어 바로 재전송하지 않습니다.

3단계: API 라우트 만들기

서비스 레이어의 함수들을 HTTP 요청으로 호출할 수 있게 API 라우트를 만듭니다. Next.js의 App Router 방식을 사용합니다.

총 4개의 라우트가 필요합니다.

/api/linkedin/authorize와 /api/linkedin/callback

authorize는 동의 화면으로 보내는 시작점이고, callback은 동의 뒤 돌아오는 주소입니다. 다음은 프로젝트의 로그인 기능에 연결해야 하는 의사 코드입니다. 함수 이름만 복사해서 실행하는 예제는 아닙니다.

authorize: 현재 사용자가 허용된 관리자인지 확인한다 예측하기 어려운 state를 새로 만든다 state와 짧은 만료 시점을 현재 관리자 세션에 저장한다 같은 state를 넣은 LinkedIn 동의 주소로 이동한다 callback: 현재 관리자 세션을 다시 확인한다 돌아온 state와 세션에 저장된 state가 일치하는지 확인한다 값이 없거나 다르거나 만료됐다면 중단한다 확인한 state는 한 번 사용한 뒤 폐기한다 사용자가 취소했거나 code가 없으면 토큰을 바꾸지 않고 종료한다 code를 서버에서 토큰으로 교환한다 토큰으로 프로필을 조회하고 사용자 ID를 확인한다 토큰, 연결 계정, 실제 만료 시점을 서버에 저장한다 허용된 고정 관리 페이지로 돌아간다

성공 안내는 DB 저장까지 끝난 뒤 표시합니다. 주소에 linkedin=connected가 있다는 이유만으로 연결됐다고 믿지 않고 서버에서 상태를 다시 조회합니다. 인증을 취소했거나 state가 맞지 않으면 새로 연결을 시작해야 합니다.

/api/linkedin/status와 /api/linkedin/post

status는 연결 여부, 계정 표시 이름과 만료 상태만 반환합니다. 액세스 토큰이나 Secret은 반환하지 않습니다. post는 서버에서 관리자 인증, 요청 위조 방지, 본문과 길이, 연결 계정을 확인한 뒤 실제 게시를 수행합니다. 브라우저에서 보낸 임의의 author를 그대로 신뢰하지 않고 서버의 연결 기록을 사용합니다.

네 라우트 모두 접근 조건을 구현해야 합니다. 앞의 예제는 기존 사이트의 인증 코드를 포함하지 않으므로 getServerSession()을 한 번 호출하는 것만으로 보호가 완성되는 것은 아닙니다.

4단계: UI 수정

LinkedIn Summary연결됨: 홍OO
AI가 생성한 소개글이 여기에 표시되고, 게시 전에 자유롭게 편집할 수 있습니다.
1,240 / 3,000자
복사
Post to LinkedIn
3,000자 초과 시 카운터가 빨간색으로 바뀌고 게시 버튼이 비활성화됩니다.

위는 실제 캡처가 아닌 화면 구성 예시입니다. 초안 편집과 실제 게시를 구분하기 위해 네 가지를 구성합니다.

편집 가능한 텍스트 영역

기존에는 readOnly 속성이 있어서 생성된 텍스트를 수정할 수 없었습니다. 이 속성을 제거하고 onChange 핸들러를 추가하여 게시 전에 내용을 편집할 수 있게 했습니다.

글자 수 카운터

LinkedIn은 게시물에 3,000자 제한이 있습니다. 실시간으로 글자 수를 표시하고, 초과 시 빨간색으로 경고합니다. 초과 상태에서는 게시 버튼이 비활성화됩니다.

연결 상태 표시

모달 헤더에 LinkedIn 연결 상태와 연결된 계정 이름을 표시합니다. 연결되지 않은 상태에서는 "Connect LinkedIn" 버튼이, 연결된 상태에서는 "Post to LinkedIn" 버튼이 나타납니다.

게시 결과 피드백

게시 성공 시 생성된 게시물의 링크를 표시합니다. 실패 시에는 오류 원인에 맞춰 재연결, 본문 수정, 사용량 확인 등 다음 행동을 안내합니다.

막혔을 때 확인할 것

상태의미와 다음 행동
권한 목록에 w_member_social이 없음앱의 Products 승인 상태와 Auth의 권한 목록 확인
Redirect URI 오류등록 주소와 요청 주소의 프로토콜, 경로, 끝 슬래시까지 비교
state 불일치 또는 만료토큰 교환을 중단하고 관리 화면에서 연결을 처음부터 재시작
HTTP 401토큰 만료나 취소 여부 확인 후 필요한 경우 재연결
HTTP 403게시 권한과 연결 계정 확인, 개인 게시와 회사 페이지 권한 구분
HTTP 429요청을 멈추고 앱 사용량과 제한 확인, 반복 클릭 금지
시간 초과 또는 응답 없음LinkedIn의 해당 계정 피드에서 게시 여부를 먼저 확인
3,000자 초과초안을 줄이고 서버에서도 길이 검사, 이모지 등은 API 오류도 확인

LinkedIn은 앱별 제한과 사용자별 제한을 적용합니다. 정확한 한도는 API별로 다를 수 있고 Developer Portal의 Analytics에서 확인합니다. ‘하루 1~3회면 문제없다’고 보장할 수는 없습니다.

첫 실제 게시가 필요한 경우에는 연결 계정, 최종 본문, 링크, 공개 범위를 화면에서 확인하고 한 번만 요청합니다. 성공 안내 후 LinkedIn에서 그 게시물을 열어 계정과 문장을 확인하고 URL을 기록합니다. 일반 피드 링크만 열리는 것으로 검증을 끝내지 않습니다. 중복 요청 방지는 버튼 비활성화뿐 아니라 서버의 요청 기록에도 구현합니다.

앱 등록용 회사 페이지와 게시 계정

공식 등록 안내에는 자신의 회사 페이지를 연결하는 단계가 있습니다. ‘Individual Developer용 기본 Company Page’를 누구나 선택할 수 있다고 가정하지 않습니다. 연결할 페이지와 필요한 권한을 준비하지 못했다면 등록 단계에서 멈추고 초안 연습을 계속할 수 있습니다.

앱에 연결한 회사 페이지와 실제 게시 작성자는 별개입니다. 이 글은 w_member_social과 개인 person URN을 쓰는 구조입니다. 회사 페이지 명의 게시에는 조직 권한과 페이지 역할 등 다른 조건이 필요합니다.

자주 묻는 질문

토큰을 환경 변수에 저장하면 안 되나요?

사용자 연결과 재인증을 관리하는 앱에서는 DB나 비밀값 저장 서비스를 쓸 수 있습니다. 환경 변수도 가능하지만 값 변경 후 재시작이나 재배포가 필요한지는 호스팅 환경에 따라 다릅니다. 어떤 저장 방식을 쓰든 토큰 접근 권한과 연결 해제 시 삭제 처리를 정해야 합니다.

액세스 토큰이 만료되면 매번 다시 로그인해야 하나요?

자동 갱신 권한과 리프레시 토큰이 없는 앱은 다시 동의받는 흐름을 준비해야 합니다. 승인된 파트너 앱은 자동 갱신을 사용할 수 있지만 실패하거나 토큰이 취소될 때를 위한 재연결도 필요합니다.

개발을 처음 배우는데 여기까지 모두 구현해야 하나요?

첫 메모 연습과 ‘동의 화면, 토큰 보관, 게시 요청’의 역할 구분부터 익히면 됩니다. 구현에는 서버 코드, DB, 로그인과 오류 처리 경험이 필요합니다. 복사해서 직접 올리는 방식이 현재 업무량에 충분하다면 API 연동을 나중으로 미뤄도 됩니다.

정리

LinkedIn API 연동의 전체 흐름을 다시 정리합니다.

  1. LinkedIn Developer App 등록: Client ID/Secret 발급, Redirect URL 설정
  2. DB 테이블 추가: 토큰 저장용 테이블 1개
  3. 서비스 레이어: OAuth, 토큰 관리, 게시 함수를 한 파일에 구현
  4. API 라우트 4개: 인증 시작, 콜백, 상태, 게시
  5. UI 수정: 편집, 글자 수, 게시 버튼, 상태 표시

OAuth 2.0이 처음에는 복잡해 보이지만, 핵심은 "사용자 동의 → 인증 코드 → 토큰 교환" 세 단계입니다. 다른 서비스도 비슷한 인증 흐름을 쓰지만 권한 이름과 토큰 갱신 조건은 각각 확인해야 합니다.

자동화할 수 있는 반복 작업이 보이면, 그것이 API 연동의 출발점입니다.

Sources

3줄 요약

  1. LinkedIn 연동은 초안 생성, 사용자 동의, 토큰 저장, 게시 요청을 나눠 이해하고 처음에는 초안 확인까지만 연습합니다.

  2. OAuth의 state는 생성뿐 아니라 세션에 저장하고 콜백에서 비교해야 하며, 자동 토큰 갱신은 승인된 앱에만 제공됩니다.

  3. 실제 게시 전에 계정과 공개 범위를 확인하고, 응답이 불분명할 때는 피드에서 중복 여부를 확인한 뒤 다시 시도합니다.

Share

제대로 이해했는지 한 문제로 확인해 볼까요?

답을 고르면 바로 풀이가 나와요.

앱 등록이 끝났고 아직 사용자 동의가 없는 경우, OAuth 인증 코드를 얻기 위한 다음 단계는 무엇인가요?

이 글이 도움이 되었나요?

이 글 다음 배우기AI 업무 자동화 입문입문 코스 · 10편

메일, 회의록, 리포트처럼 반복되는 일을 AI로 자동화하는 순서와 안전장치를 다룹니다

  1. 1AI 업무 자동화 뜻과 규칙 자동화와 다른 점
  2. 2자동화할 일 고르기: 반복, 규칙, 되돌리기
  3. 3n8n 이해하기, 노드로 잇는 자동화와 셀프호스팅
코스 전체 보기 →
이어서 읽기 좋은 글클로드 Haiku 5.5(하이쿠 5.5) 가격, 성능, 사용법과 Haiku 4.5에서 옮길 때 확인할 것 →

Haiku 5.5는 10만 토큰 이하 요청의 단가가 Haiku 4.5보다 90% 낮지만, 같은 글이 약 30% 더 많은 토큰으로 계산되고 10만 토큰을 넘으면 단가가 다섯 배가 됩니다. 계산 예시로 비용 변화를 확인하고 벤치마크, 클로드 코드 사용법, API 이전 항목을 정리했습니다.