카페24 API 토큰 관리 자동화

파이썬으로 손쉽게 자동화하여 관리하는 카페24 API 토큰 활용법. 제가 쓰는 모든 코드 공개합니다.
Author

박세희

Published

2025-01-08

[들어가기] 카페24 API 토큰 관리 개념

  1. (반자동) 1분 간 유효한 임시 인증 코드 발급합니다. 1분 간 유효하기 때문에 2번이 준비된 상태에서 발급받으시는 게 좋습니다.

  2. (자동) 접근 토큰 access token2시간 동안 유효합니다

  3. (자동) 접근 토큰 발급할 때 접근 토근 재발급을 위한 refresh token이 함께 발급 됩니다. 이 리프레시 토큰은 14일간 유효합니다.

  4. (자동) 기존 접근 토큰 유효기간 2시간이 지나는 경우라도 리프레시 토큰이 아직 유효하다면 접근 토큰을 재발급 받을 수 있습니다. 리프레시 토큰 사용 시, 새로운 토큰 세트가 발행 되므로 신규 토큰 세트를 저장해서 사용 합니다.

  5. (반자동) 만약 리프레시 토큰까지 유효기간이 만료 되었다면 다시 1번으로 돌아갑니다. 1번은 웹에서 사람이 직접 진행하는 과정을 포함하기 때문에 기존 토큰이 만료되지 않도록 하는 게 편리 합니다.

이 글에서 다루는 내용

2~4번까지를 완전 자동화 하는 파이썬 코드를 제공 합니다.

1번, 5번은 이전 블로그 글 비개발자도 쉽게 카페24 API 인증 토큰 발급 받는 방법 을 참고해 주세요.

[참고] 카페24 액세스 토큰 재발급 공식 문서 바로가기

1. 최초 토큰 발급에 필요한 파라미터

  • 1분간 유효한 인증 코드 code

  • 쇼핑몰 아이디 mall_id

  • 클라이언트 아이디 client_id, 클라이언트 시크릿 client_secret_key

  • 처음 인증코드 발급 받았던 경로 redirect_uri

이게 뭔지 모르겠다면, 이전 블로그 글 비개발자도 쉽게 카페24 API 인증 토큰 발급 받는 방법 을 참고해 주세요

A. base64_encode로 클라이언트 정보 변환

카페24 공식 문서에는 “Header의 {base64_encode({client_id}:{client_Secret})} 부분은 {클라이언트 아이디}:{클라이언트 시크릿}을 Base64 방식으로 인코딩하여 입력하세요” 라고만 되어 있습니다.

파이썬으로 base64 문자열을 변환할 수 있어요. 다음 코드를 활용하세요.

import base64 

# 여러분의 id, secret key을 입력 하세요 
client_id = "my_client_id" 
client_secret = "my_client_secret_key"

code = f"{client_id}:{client_secret}"
encoded_code = base64.b64encode(code.encode('utf-8'))
base64_encode_str = encoded_code.decode('utf-8')

print(base64_encode_str)

# 여기 코드를 복사해서 여러분의 id, secret_key를 넣고 실행한 결과값을 쓰세요 
# 아래 값 복붙 하시면 작동하지 않습니다! 
bXlfY2xpZW50X2lkOm15X2NsaWVudF9zZWNyZXRfa2V5
base64 encode 값 유출 주의!

base64 encode 값은 다시 해독하기 너무 쉬워요. 유출되면 client_id, client_secret이 함께 노출되는 것이니 주의하세요. 유출이 의심된다면 카페24 개발자 홈페이지에서 기존 id, secret을 삭제하고 새로 발급 받으시는 것을 추천합니다.

B. 최초 접속 코드 발급

  • Restful API로 요청하고 받는데 파이썬에서는 requests 패키지를 씁니다.

  • 카페24 API 토큰 발급 경로: “https://{mall_id}.cafe24api.com/api/v2/oauth/token”

    • {..}는 변수로 정의한 내 쇼핑몰 id 값이 들어갈 거에요.

카페24 공식 문서 내용을 참고하여 작성한 파이썬 코드 입니다. 토큰 최초 발급 시에는 아래 함수를 1회 돌려요.

import requests, json 

# 내 쇼핑몰 변수 정의 
code = "1분간 유효한 인증코드"
redirect_uri = "인증코드 받을 때 사용한 경로"
mall_id = "내 쇼핑몰 아이디"
base64_encode_atr = "base64로 변환한 클라이언드 정보"

def initiate_token(): 
    code = "1분간 유효한 인증코드"
    url = f"https://{mall_id}.cafe24api.com/api/v2/oauth/token"
    payload = f"grant_type=authorization_code&code={code}&redirect_uri={redirect_uri}"
    
    headers = {
        'Authorization': f"Basic {base64_encode_str}",
        'Content-Type': "application/x-www-form-urlencoded"
    }
    
    response = requests.request("POST", url, data=payload, headers=headers)
    # API 콜 결과를 반환합니다: 200 이면 정상 
    print(response)
    tokens = response.json()
    access_token = tokens['access_token']
    
    # 'tokens.json' 파일에 토큰을 저장해요: 재활용 합니다  
    filename = f'tokens.json'
    with open(filename, 'w') as json_file:
        json.dump(tokens, json_file, indent = 4)
    
    return access_token
# 함수 실행하기 
access_token = initiate_token()
print(f'Access token: {access_token}')

토큰을 암호화하지 않고 저장하는 방법이기 때문에 제한된 접근으로 관리 가능한 로컬 컴퓨터나 클라우드에서만 사용하세요.

C. 발급된 토큰 형태

아래는 카페24 메뉴얼에서 복사한 샘플 데이터 입니다. Json 파일로 저장했기 때문에 파일을 코드 에디터로 열어보면 동일한 모습인 것을 볼 수 있어요. 아래 코드를 보면 각 토큰 유효기간 만료 시간이 발급된 시간으로부터 access_token 은 2시간 이후, refresh_token 이 14일 이후인 것을 알 수 있어요.

{
  "access_token": "sample9jIRUGHE5CBOiKRGC",
  "expires_at": "2018-11-07T20:12:25.916",
  "refresh_token": "sample80BQWWCJEiwTHWCrU",
  "refresh_token_expires_at": "2018-11-21T18:12:25.918",
  "client_id": "sample7eBNEqSfkd7I8hoA",
  "mall_id": "samplemall",
  "user_id": "jonhdoe123",
  "scopes": [
    "mall.read_application",
    "mall.write_application",
    "mall.read_category"
  ],
  "issued_at": "2018-11-07T18:12:25.918",
  "shop_no": 1
}

2. 토큰 관리 모듈

토큰 발급 자동화를 위해서는 현재 시간과 각 토큰의 유효기간 만료 시간을 비교해서 재발급 하면 됩니다. 이걸 “토큰 관리 Token Manager 라는 클래스로 정의해서 다양한 기능을 담을께요.

(모듈 1) 기존 토큰 불러오기

  • access_token 유효기간이 만료되지 않았다면: 저장된 토큰을 불러 옵니다.
import json

filename = "tokens.json" 

# 저장된 토큰 불러오는 함수 
def _load_token(filename): 
    try:
        with open(filename, 'r') as json_file: 
            return json.load(json_file)
    # 오류 발생 핸들링 
    except FileNotFoundError:
        raise Exception(f"Token file '{filename}' not found.")
    except json.JSONDecodeError:
        raise Exception("Token file is not properly formatted.")
# 함수 실행 확인하기 
tokens = _load_token(filename) 
print(tokens)

access_token = tokens['access_token']
print(f"저장되었던 접속 코드: {access_token}")
{'access_token': 'sample9jIRUGHE5CBOiKRGC', 'expires_at': '2018-11-07T20:12:25.916', 'refresh_token': 'sample80BQWWCJEiwTHWCrU', 'refresh_token_expires_at': '2018-11-21T18:12:25.918', 'client_id': 'sample7eBNEqSfkd7I8hoA', 'mall_id': 'samplemall', 'user_id': 'jonhdoe123', 'scopes': ['mall.read_application', 'mall.write_application', 'mall.read_category'], 'issued_at': '2018-11-07T18:12:25.918', 'shop_no': 1}
저장되었던 접속 코드: sample9jIRUGHE5CBOiKRGC

이렇게 기존 접속 코드가 유효하다면 저장했던 파일에서 꺼내 쓸 수 있습니다.

(모듈 2) 토큰 저장하기

리프레시 토큰 사용 시, 새로운 접속 코드가 풀세트로 발급되기 때문에 한 번 사용된 리프레시 코드는 더 이상 유효하지 않습니다. 따라서 새로 발급 받은 new_tokens 을 다시 json 파일에 저장해야 합니다. 저장 모듈을 먼저 준비할께요.

# 기존 정의한 파일과 동일하게 이름을 정의해야 덮어 써요. 
FILENAME = "tokens.json" 

def _save_tokens(new_tokens):
    """토큰 파일을 업데이트합니다."""
    with open(FILENAME, 'w') as json_file:
        json.dump(new_tokens, json_file, indent=4)

(모듈 3) 리프레시 토큰 사용하기

2시간 밖에 안 되는 접속 토큰 유효기간이 만료되기 일쑤라서 리프레시 토큰으로 전체 토큰 정보를 갱신해야 합니다. 아래와 같은 로직으로 진행합니다.

우선 리프레시 토큰 사용하는 모듈 입니다.

# 파일로 저장했던 걸 토큰 변수에 불러옵니다. 
filename = "sample_tokens.json" 
tokens =  _load_token(filename) 

# 리프레시 토큰 활용 함수 
def _refresh_access_token():
    # 리프레시 토큰을 사용하여 새 접속 토큰 세트를 발급 받아요 
    # 저장된 
    url = f"https://{mall_id}.cafe24api.com/api/v2/oauth/token"
    payload = f"grant_type=refresh_token&refresh_token={tokens['refresh_token']}"
    headers = {
        'Authorization': f"Basic {base64_encode}",
        'Content-Type': "application/x-www-form-urlencoded"
    }

    try:
        response = requests.post(url, data=payload, headers=headers)
        response.raise_for_status()  # HTTP 상태 코드 확인
        # new_tokens 라는 새로운 변수에 새 접속 토큰 세트를 담아 둡니다 
        new_tokens = response.json()
        
        # 여기에 신규 토큰 저장 함수를 추가해요 # 
        _save_tokens(new_tokens)

        return new_tokens['access_token']
      
    # 에러 발생 시 처리 
    except requests.RequestException as e:
        raise Exception(f"Failed to refresh token: {e}")

(모듈 4) 유효기간 검토 + 토큰 관리 자동화

  • 현재 시간을 확인합니다

    • 로컬 위치에 따라 시간이 틀어질 수 있으니 같은 시간대인지 확인 합니다.

    • 카페24 정보는 서울 기준시 (KST)로 주기 때문에 여기서는 시간 보정을 하지 않았어요.

  • 접속 토큰과 리프레시 토큰의 유효기간과 현재 시간을 비교합니다

  • 유효 여부에 따라 기존 코드를 꺼내 쓰거나 리프레시 토큰을 이용해서 새 토큰 세트를 발급 받아요

from datetime import datetime

# 유효기간을 날짜형식으로 처리 
def is_valid_datetime(value): 
    try: 
        if isinstance(value, datetime):
            return value
        return datetime.fromisoformat(value)  # ISO 형식 문자열인 경우 변환
    except ValueError:
        return None  # 유효하지 않은 경우 None 반환

def get_access_token():
    # 현재 시간을 저장합니다. 
    now = datetime.now()

    # 기존 접속 토큰 유효성 확인 
    expires_at = is_valid_datetime(tokens.get('expires_at'))
    if expires_at and now < expires_at:
        return tokens['access_token']

    # 리프레시 토큰 유효성 확인 
    refresh_token_expires_at = is_valid_datetime(tokens.get('refresh_token_expires_at'))
    if refresh_token_expires_at and now < refresh_token_expires_at:
        return _refresh_access_token()

    # Both tokens are invalid
    raise Exception("리프레시 코드도 만료되었어요. 응답코드 발급부터 다시 진행하세요.")
날짜형식 검토

파이썬에서 토큰을 저장할 때는 문자로 바뀌는 경향이 있어요. 그래서 위 코드에서는 날짜 형식인지 먼저 확인하고 날짜가 아닌 문자일 때(fromisoformat) datetime 으로 바꾸도록 추가 코드가 제공 되었습니다.

3. 토큰매니저로 통합한 최종본

최초 접속 토큰 발급 후 저장은 이 토큰 매니저가 아니라 1번의 initiate_token 함수로 실행하세요. 그래야 “tokens.json” 파일이 생깁니다. 토큰 파일을 생성한 이후에 아래 토큰매니저는 자동으로 작동합니다.

위 함수들을 모아서 TokenManager 라는 클래스로 묶어 줍니다. 모듈로 실행하기 위해서 self 라는 것이 추가 돼요. 위에 내용을 이해하셨다면 그냥 복붙하셔도 괜찮습니다.

# 필요한 패키지 
import requests, json 
from datetime import datetime 

# 내 쇼핑몰 변수 정의 
code = "1분간 유효한 인증코드"
redirect_uri = "인증코드 받을 때 사용한 경로"
mall_id = "내 쇼핑몰 아이디"
base64_encode_atr = "base64로 변환한 클라이언드 정보"

# 토큰 저장된 파일명; initiate_token()을 실행한 결과물 
FILENAME = "tokens.json"

# 통합한 토큰 매니저 
class TokenManager: 
  
    def __init__(self, mall_id=mall_id, base64_encode=base64_encode, filename=FILENAME):
        self.mall_id = mall_id
        self.base64_encode = base64_encode
        self.filename = filename
        self.tokens = self._load_tokens()
        
    def _load_tokens(self):
        """토큰 파일을 로드합니다."""
        try:
            with open(self.filename, 'r') as json_file:
                return json.load(json_file)
        except FileNotFoundError:
            raise Exception(f"Token file '{self.filename}' not found.")
        except json.JSONDecodeError:
            raise Exception("Token file is not properly formatted.")

    def _save_tokens(self):
        """토큰 파일을 업데이트합니다."""
        with open(self.filename, 'w') as json_file:
            json.dump(self.tokens, json_file, indent=4)

    def _refresh_access_token(self):
        """리프레시 토큰을 사용하여 새 액세스 토큰을 발급받습니다."""
        url = f"https://{self.mall_id}.cafe24api.com/api/v2/oauth/token"
        payload = f"grant_type=refresh_token&refresh_token={self.tokens['refresh_token']}"
        headers = {
            'Authorization': f"Basic {self.base64_encode}",
            'Content-Type': "application/x-www-form-urlencoded"
        }
        
        try:
            response = requests.post(url, data=payload, headers=headers)
            response.raise_for_status()  # HTTP 상태 코드 확인
            new_tokens = response.json()

            # 업데이트 토큰 정보 저장 
            self.tokens = new_tokens
            self._save_tokens()
            
            return new_tokens['access_token']
        
        except requests.RequestException as e:
            raise Exception(f"Failed to refresh token: {e}")
          
    def _is_valid_datetime(self, value):
        """입력값이 날짜 형식인지 확인하고 변환합니다."""
        try:
            if isinstance(value, datetime):
                return value
            return datetime.fromisoformat(value)  # ISO 형식 문자열 변환
        except ValueError:
            return None  # 유효하지 않으면 None 반환
          
    def get_access_token(self):
        """유효한 액세스 토큰을 반환하거나 새로 발급합니다."""
        now = datetime.now()
        print(f"지금 시간: {now}")

        # 기존 접속 토큰 유효성 확인
        expires_at = self._is_valid_datetime(self.tokens.get('expires_at'))
        if expires_at and now < expires_at:
            print("access token is valid.")
            return self.tokens['access_token']

        # 리프레시 토큰 유효성 확인
        refresh_token_expires_at = self._is_valid_datetime(self.tokens.get('refresh_token_expires_at'))
        if refresh_token_expires_at and now < refresh_token_expires_at:
            print("Using refresh token ...")
            return self._refresh_access_token()

        # 두 조건 모두 실패 시 예외 처리
        raise Exception("리프레시 토큰도 만료되었습니다. 새로운 인증을 진행하세요.")

(보너스) 토큰 매니저 실행 방법

여기까지 잘 따라 오셨나요? 이제 토큰 매니저를 실행해서 카페24에 토큰 매니저를 활용해 봐요. 데이터를 꺼내는 공통 모듈 함수는 아래와 같습니다. 기존 쇼핑몰 변수와 토큰 저장된 파일명, 패키지가 로드된 상태여야 합니다.

# API로 데이터 꺼내는 공통 모듈 함수
# info 부분을 바꾸면서 다양한 정보를 읽거나 쓸 수 있어요. 
# 기본 모듈은 상품 수 추출로 정의되어 있습니다. 
def call_request(info='products/count'):

    # 토큰 매니저 활용! 
    manager = TokenManager(mall_id, base64_encode)
    access_token = manager.get_access_token()
    # 유효한 토큰을 확인해요. 
    print(f"access token: {access_token}")

    url = f'https://{mall_id}.cafe24api.com/api/v2/admin/{info}'
    headers = {
        'Authorization': f"Bearer {access_token}",
        'Content-Type': "application/json"
        }
    response = requests.request("GET", url, headers=headers)
    response_dict = response.json()

    first_key = list(response_dict.keys())[0]
    result = response_dict[first_key]

    return result
# 모듈 테스트: 등록 상품 추출
product_count = call_request()
print(f'Number of products: {product_count}')

코드가 정상적으로 작동해서 리프레시 토큰이 사용되었다면 다음과 같은 결과가 나올 거에요.

지금 시간: 2025-01-08 19:59:20.353054
access token expires at: 2025-01-08 19:53:36+00:00
Using refresh token ...
access token: WcRzDp3NEh5lNP--------
Number of products: 3742

그럼 해피 코딩 하세요!

더 자세한 카페24 API 활용법은 공식 문서를 참고하시는 게 가장 정확합니다: 바로가기