๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿš€ ํŒ (๊ธฐ์ˆ  ์ ์šฉ ๋ฐฉ๋ฒ• ๋“ฑ)/JS&TS

[axios] axios ๋ฐ axios์˜ ์ธํ„ฐ์…‰ํ„ฐ ์•Œ์•„๋ณด๊ธฐ

by dev_writer 2024. 12. 28.

์•ˆ๋…•ํ•˜์„ธ์š” dev_writer์ž…๋‹ˆ๋‹ค.

 

ํ˜„ ํšŒ์‚ฌ์— ์ž…์‚ฌํ•˜๋ฉด์„œ axios๋ฅผ ํ˜„์—…์—์„œ ์ฒ˜์Œ ๋‹ค๋ฃจ๊ฒŒ ๋˜์—ˆ๋Š”๋ฐ์š”, ๊ทธ ๊ณผ์ •์—์„œ ์•Œ๊ฒŒ ๋œ axios์™€ axios ์ธํ„ฐ์…‰ํ„ฐ์— ๋Œ€ํ•ด ์ •๋ฆฌํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

๋ณธ ๊ธ€์— ์‚ฌ์šฉ๋˜๋Š” axios๋Š” 1.7.9 ๋ฒ„์ „์„ ์ด์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.

 

Axios

์šฐ์„  axios๋Š” node.js์™€ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์œ„ํ•œ Promise ๊ธฐ๋ฐ˜ HTTP ํด๋ผ์ด์–ธํŠธ์ž…๋‹ˆ๋‹ค. (axios ์†Œ๊ฐœ ๋ฌธ์„œ)

 

ํŠน์ง•

  • ์„œ๋ฒ„ ์‚ฌ์ด๋“œ์—์„œ๋Š” node.js์˜ http ๋ชจ๋“ˆ์„, ๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” XMLHttpRequest๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • Promise API๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
  • ์š”์ฒญ ๋ฐ ์‘๋‹ต์„ ์ธํ„ฐ์…‰ํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • JSON ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋™์œผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • XSRF (Cross-site request forgery, ํฌ๋กœ์Šค ์‚ฌ์ดํŠธ ์š”์ฒญ ์œ„์กฐ)๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•œ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

 

axios์™€ ์ฃผ๋กœ ๋น„๊ต๋˜๋Š” fetch๋„ ์žˆ๋Š”๋ฐ, axios์™€ fetch์˜ ๋น„๊ต๋Š” ๋‚ด์šฉ์ด ๊นŠ์–ด์ง€๊ธฐ์— ์•„๋ž˜ reference์— ์ถ”๊ฐ€ํ•œ ๊ธ€๋“ค์„ ์ฝ์–ด๋ณด์‹œ๊ธฐ๋ฅผ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค.

 

axios ์ด์šฉ ์˜ˆ์‹œ

axios๋ฅผ ์ด์šฉํ•˜์—ฌ OpenAI์— ๋ฉ”์‹œ์ง€ ์ƒ์„ฑ์„ ํ•˜๋Š” ๊ฒƒ์„ ์˜ˆ์‹œ๋กœ ๋“ค๊ฒ ์Šต๋‹ˆ๋‹ค. (POST ์˜ˆ์‹œ)

 

1๏ธโƒฃ axios (Axios ์ธ์Šคํ„ด์Šค)๋ฅผ ๋งŒ๋“  ํ›„ ํ˜ธ์ถœํ•˜๊ธฐ

axios๋ฅผ ๋งŒ๋“  ๋’ค ์ด์šฉํ•˜๋Š” ๋ฐฉ์‹์€ axios.post๋ฅผ ์ด์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. axios์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด post ์ด์™ธ์—๋„ get, delete ๋“ฑ HTTP Method ๋ณ„ ์š”์ฒญ์„ ์ •์˜ํ•ด ๋‘” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

axios์˜ Axios ํด๋ž˜์Šค ์ •์˜ ๋ถ€๋ถ„

 

์ด๋•Œ T๋Š” ๋ฐ›๊ณ ์ž ํ•˜๋Š” ์‘๋‹ต ๋ฐ์ดํ„ฐ ํ˜•์‹, R์€ T๋ฅผ data๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” AxiosResponse, D๋Š” ์š”์ฒญ ํ˜•์‹์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

 

๊ฐ€๋ น ChatGPT๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ์ผ€์ด์Šค๋ฅผ ๋ณธ๋‹ค๋ฉด ์•„๋ž˜ ์ฝ”๋“œ์ฒ˜๋Ÿผ ์ž‘์„ฑํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. (openai์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ)

import { Axios } from 'axios';

// OpenAI API ํ‚ค ์ €์žฅ
const OPENAI_API_KEY = 'Open AI API ํ‚ค';

// POST ์š”์ฒญ ์‹œ ๋ณด๋‚ผ ๋ฐ์ดํ„ฐ ์ •์˜
const requestData = {
    model: 'gpt-4o',
    messages: [
        {
            role: 'developer',
            content: 'You are a helpful assistant.',
        },
        {
            role: 'user',
            content: 'Tell me about axios',
        }
    ]
};

// ๋ถ€๊ฐ€์ ์ธ ์š”์ฒญ ์„ค์ • ์ •์˜
const requestConfig = {
    headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${OPENAI_API_KEY}`,
    }
};

// ๋ฐ›์„ ์‘๋‹ต ํƒ€์ž… ์ •์˜
type responseData = {
    id: string;
    object: string;
    created: number;
    model: string;
    system_fingerprint: string;
    choices: {
        index: number;
        message: {
            role: string;
            content: string;
        },
        logprobs: object;
        finish_reason: string;
    }[];
    usage: {
        prompt_token: number;
        completion_tokens: number;
        total_tokens: number;
        completion_tokens_details: {
            reasoning_tokens: number;
            accepted_prediction_tokens: number;
            rejected_prediction_tokens: number;
        }
    }
};

export class ChatGptApiInstance {
    protected readonly axios: Axios;
    constructor() {
        // ๊ธฐ๋ณธ baseURL ์„ค์ •
        this.axios = new Axios({ baseURL: 'https://api.openai.com' });
    }
    // data๋Š” responseData ํƒ€์ž…
    async generateText() {
        const { data } = await this.axios.post<responseData>('/v1/chat/completion', requestData, requestConfig);
    }
}

 

์ด๋•Œ post์— ์ •์˜๋œ T (responseData์— ํ•ด๋‹น) ๋ง๊ณ  R, D๋ฅผ ์ •์˜ํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ์ด์œ ๋Š” T๋ฅผ ์ •์˜ํ•  ์‹œ R์€ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ AxiosResponse<T>๋กœ ์ •์˜๋˜๋ฉฐ, D๋Š” any ํƒ€์ž…์œผ๋กœ ์ •์˜๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์š”์ฒญ ํƒ€์ž… (type)์„ ๋ช…ํ™•ํžˆ ํ•  ๊ฒƒ์ด๋ผ๋ฉด, post<์‘๋‹ต ํƒ€์ž…, AxiosResponse<์‘๋‹ต ํƒ€์ž…>, ์š”์ฒญ ํƒ€์ž…>์œผ๋กœ ์ž‘์„ฑํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

2๏ธโƒฃ axiosInstance๋ฅผ ์ด์šฉํ•œ ๋ฐฉ๋ฒ• (axios.create)

๋‹ค์Œ์œผ๋กœ ๊ฐ€๋Šฅํ•œ ๋ฐฉ๋ฒ•์€ axios (AxiosStatic).create๋ฅผ ํ†ตํ•ด AxiosInstance๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ฒซ ๋ฒˆ์งธ ๋ฐฉ์‹๊ณผ ์ฝ”๋“œ๋Š” ๊ฑฐ์˜ ๊ฐ™์Šต๋‹ˆ๋‹ค.

import axios, { AxiosInstance } from 'axios';

// .. ๊ฐ™์€ ์ฝ”๋“œ๋“ค

export class ChatGptApiInstance {
    protected readonly axiosInstance: AxiosInstance;
    constructor() {
        // ๊ธฐ๋ณธ baseURL ์„ค์ •
        this.axiosInstance = axios.create({
            baseURL: 'https://api.openai.com',
        });
    }
    // data๋Š” responseData ํƒ€์ž…
    async generateText() {
        const { data } = await this.axiosInstance.post<responseData>('/v1/chat/completion', requestData, requestConfig);
    }
}

 

1๋ฒˆ๊ณผ 2๋ฒˆ์˜ ์ฐจ์ด

๊ทธ๋ ‡๋‹ค๋ฉด 1๋ฒˆ (new Axios)์„ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ 2๋ฒˆ (axios.create)์„ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์˜ ์ฐจ์ด์ ์€ ๋ฌด์—‡์ผ์ง€ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

axios ๋ชจ๋“ˆ์— ์žˆ๋Š” AxiosInstance ์ฝ”๋“œ

 

AxiosInstance ํ•„๋“œ๋ฅผ ์ด์šฉํ•  ์‹œ, ํ•„๋“œ๋กœ ์ง์ ‘ ์š”์ฒญ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. (ํ•จ์ˆ˜๋กœ์„œ ์ด์šฉ ๊ฐ€๋Šฅ)

async generateText() {
    const { data } = await this.axiosInstance<responseData>('/v1/chat/completions', { method: 'POST', data: requestData, headers: requestConfig.headers });
    return data;
}

 

ํ•ด๋‹น ์˜ˆ์‹œ ์ฝ”๋“œ์ฒ˜๋Ÿผ ํ•„๋“œ๋ฅผ ํ•จ์ˆ˜๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. new Axios๋ฅผ ์ด์šฉํ•  ๋•Œ์—๋Š” ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

 

์ด ์™ธ์—๋„ ํ—ค๋”๋ฅผ ์ข€ ๋” ์„ธ๋ฐ€ํžˆ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋‹ค๋Š” ํŠน์ง•์ด ๋ณด์ž…๋‹ˆ๋‹ค.

 

declare class Axios {
    constructor(config?: axios.AxiosRequestConfig);
    defaults: axios.AxiosDefaults;
    ...
}

interface AxiosDefaults<D = any> extends Omit<AxiosRequestConfig<D>, 'headers'> {
    headers: HeadersDefaults;
}

interface HeadersDefaults {
    common: RawAxiosRequestHeaders;
    delete: RawAxiosRequestHeaders;
    get: RawAxiosRequestHeaders;
    head: RawAxiosRequestHeaders;
    post: RawAxiosRequestHeaders;
    put: RawAxiosRequestHeaders;
    patch: RawAxiosRequestHeaders;
    options?: RawAxiosRequestHeaders;
    purge?: RawAxiosRequestHeaders;
    link?: RawAxiosRequestHeaders;
    unlink?: RawAxiosRequestHeaders;
}

 

Axios๋ฅผ ์ด์šฉํ•  ๊ฒฝ์šฐ์—๋Š” axios.defaults.headers. [common | delete |... unlink?]๊ฐ€ ๊ฐ€๋Šฅํ•œ ๋ฐ˜๋ฉด

 

AxiosInstance์—์„œ๋Š” axios.defaults.headers ['example'] = 'value'์ฒ˜๋Ÿผ HeaderDefaults ์ด์™ธ์˜ ํ‚ค๋„ ๋ฌธ์ž์—ด๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

๊ตฌ๋ฒ„์ „์—์„œ๋Š” Axios ํด๋ž˜์Šค๊ฐ€ ๋”ฐ๋กœ ์—†๊ณ  AxiosInstance๋งŒ ์žˆ์—ˆ๋˜ ๋ฐ˜๋ฉด, ์ง€๊ธˆ์€ Axios์™€ AxiosInstance๊ฐ€ ๋‚˜๋‰˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. (AxiosInstance๊ฐ€ Axios๋ฅผ ์ƒ์†๋ฐ›๋„๋ก ์„ค๊ณ„) AxiosInstance๊ฐ€ Axios๋ฅผ ์ƒ์†๋ฐ›์€ ์ธํ„ฐํŽ˜์ด์Šค์ธ ๋งŒํผ, ํ–ฅํ›„ ํ™•์žฅ์„ฑ์„ ๊ณ ๋ คํ–ˆ์„ ๋•Œ์—๋Š” AxiosInstance๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒŒ ๋” ๋‚˜์•„ ๋ณด์ž…๋‹ˆ๋‹ค.

 

Interceptor

๊ฐœ๋ฐœ ์ƒํƒœ๊ณ„์—์„œ ์ธํ„ฐ์…‰ํ„ฐ (Interceptor)๋ž€ ์š”์ฒญ๊ณผ ์‘๋‹ต์„ ๊ฐ€๋กœ์ฑ„๋Š” ์—ญํ• ์„ ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๋œปํ•ฉ๋‹ˆ๋‹ค. ์œ„์— ํŠน์ง•์— ์ž‘์„ฑํ•˜์˜€๋“ฏ, axios์—์„œ๋„ ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์ด์šฉํ•˜์—ฌ ์š”์ฒญ๊ณผ ์‘๋‹ต์„ ๊ฐ€๋กœ์ฑ„๊ณ  ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

axios ๊ณต์‹ ๋ฌธ์„œ์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์˜ˆ์‹œ ์ฝ”๋“œ๋ฅผ ์•ˆ๋‚ดํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

/* ํ•ด๋‹น ์˜ˆ์ œ ์ฝ”๋“œ์—์„œ์˜ axios๋Š” axios ๋ชจ๋“ˆ์˜ AxiosStatic์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. */

// ์š”์ฒญ ์ธํ„ฐ์…‰ํ„ฐ ์ถ”๊ฐ€ํ•˜๊ธฐ
axios.interceptors.request.use(function (config) {
    // ์š”์ฒญ์ด ์ „๋‹ฌ๋˜๊ธฐ ์ „์— ์ž‘์—… ์ˆ˜ํ–‰
    return config;
  }, function (error) {
    // ์š”์ฒญ ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” ์ž‘์—… ์ˆ˜ํ–‰
    return Promise.reject(error);
  });

// ์‘๋‹ต ์ธํ„ฐ์…‰ํ„ฐ ์ถ”๊ฐ€ํ•˜๊ธฐ
axios.interceptors.response.use(function (response) {
    // 2xx ๋ฒ”์œ„์— ์žˆ๋Š” ์ƒํƒœ ์ฝ”๋“œ๋Š” ์ด ํ•จ์ˆ˜๋ฅผ ํŠธ๋ฆฌ๊ฑฐ ํ•ฉ๋‹ˆ๋‹ค.
    // ์‘๋‹ต ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š” ์ž‘์—… ์ˆ˜ํ–‰
    return response;
  }, function (error) {
    // 2xx ์™ธ์˜ ๋ฒ”์œ„์— ์žˆ๋Š” ์ƒํƒœ ์ฝ”๋“œ๋Š” ์ด ํ•จ์ˆ˜๋ฅผ ํŠธ๋ฆฌ๊ฑฐ ํ•ฉ๋‹ˆ๋‹ค.
    // ์‘๋‹ต ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” ์ž‘์—… ์ˆ˜ํ–‰
    return Promise.reject(error);
  });

 

์ธํ„ฐ์…‰ํ„ฐ๋Š” ์–ธ์ œ ์“ฐ๊ธฐ ์œ ์šฉํ• ๊นŒ?

์š”์ฒญ๊ณผ ์‘๋‹ต์„ ๊ฐ€๋กœ์ฑ„๊ณ  ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์—์„œ, ์ธํ„ฐ์…‰ํ„ฐ๋Š” ๋ฐ˜๋ณต๋˜๋Š” ์š”์ฒญ ํ˜•์‹ ์ž‘์„ฑ์„ ๋‹จ์ถ•ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ด์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

์š”์ฒญ์˜ ๊ฒฝ์šฐ, ๋‹จ์ˆœํžˆ ํ—ค๋”์— ํ† ํฐ์„ ์ฃผ์ž…ํ•˜๋Š” ๊ฒƒ ๋ง๊ณ ๋„ ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ๋“ค์„ ๋ชจ๋‘ ์ด์šฉํ•˜์—ฌ ๋ณ„๋„์˜ signature๋ฅผ ์ƒ์„ฑํ•œ ๋’ค API ์š”์ฒญ ์‹œ ์ „๋‹ฌํ•ด์•ผ ํ•˜๋Š” ์ž‘์—… ๋“ฑ์ด ์žˆ๋‹ค๋ฉด ์ฃผ์–ด์ง„ ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ๋“ค์„ ์ทจํ•ฉํ•˜์—ฌ signature๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ฃผ์ž…์‹œํ‚ฌ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์‘๋‹ต์˜ ๊ฒฝ์šฐ์—๋„ ๋กœ๊น…์„ ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค๋ฉด, ์ธํ„ฐ์…‰ํ„ฐ์—์„œ ๋กœ๊น…์„ ํ•˜๋„๋ก ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ •๋ฆฌํ•˜์ž๋ฉด ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์ด์šฉํ•˜๋ฉด ๋ฐ˜๋ณต์ ์ธ ํ–‰์œ„๋ฅผ ํ•œ ๋ฒˆ์˜ ์ฝ”๋“œ๋กœ ์ž๋™ํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ํŠน์ง•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ธํ„ฐ์…‰ํ„ฐ ์‚ฌ์šฉ ์˜ˆ์‹œ

ํ•ด๋‹น ์˜ˆ์‹œ์—์„œ์˜ ์š”์ฒญ/์‘๋‹ต ํ˜•์‹์€ ์‹ค์ œ OpenAI์™€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

AxiosInstance ๋ฐฉ์‹์˜ ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์ด์šฉํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์กฐ๊ธˆ ๋” ์š”๊ตฌ์‚ฌํ•ญ์„ ๋ณต์žกํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด, ChatGPT์— POST ์š”์ฒญ ์‹œ ์ „๋‹ฌ๋˜๋Š” body์˜ key ๊ฐ’๋“ค์„ ๋ชจ๋‘ ์ทจํ•ฉํ•˜์—ฌ signature๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•˜๋Š” ์š”๊ตฌ์‚ฌํ•ญ์ด ChatGPT API์— ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

1๋‹จ๊ณ„: ChatGptApiInstance ๋ฐ ์ธํ„ฐ์…‰ํ„ฐ ์ •์˜

๊ฐ API๋งˆ๋‹ค ๊ณตํ†ต์ ์œผ๋กœ ์‚ฌ์šฉํ•  ChatGptApiInstance๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

const OPENAI_API_KEY = 'Open AI API ํ‚ค';
const ENDPOINT = 'https://api.openai.com';

export type GptApiResponse<T = any> = {
    data: T,
    code: string;
};

export class ChatGptApiInstance {
    protected readonly axiosInstance: AxiosInstance;
    protected constructor(
    ) {
        this.axiosInstance = axios.create({
            baseURL: ENDPOINT,
            headers: {
                'Content-Type': 'application/json; charset=UTF-8',
                'Authorization': `Bearer ${OPENAI_API_KEY}`,
            }
        });

        // ์š”์ฒญ ์ธํ„ฐ์…‰ํ„ฐ
        this.axiosInstance.interceptors.request.use(
            (config) => this.onFulfilledRequest(config),
            (error) => this.onRejectedRequest(error),
        );
        
        // ์‘๋‹ต ์ธํ„ฐ์…‰ํ„ฐ
        this.axiosInstance.interceptors.response.use(
            (response) => this.onFulfilledResponse(response),
            (error) => this.onRejectedOnResponse(error),
        );
    }

    protected async onFulfilledRequest(
        config: InternalAxiosRequestConfig,
    ): Promise<InternalAxiosRequestConfig> {
        Logger.log(`[OpenAI API request] ${config.method} ${config.url}`);
        const payload: string = generateSignaturePayload(config.data);
        return {
            ...config,
            data: {
                ...config.data,
                // ๊ธฐ์กด ์š”์ฒญ๋œ data ์ด์™ธ์— ์ถ”๊ฐ€๋กœ signature ๋ง๋ถ™์ž„
                sign: generateSign(payload),
            }
        };
    }

    private onFulfilledResponse(
        res: AxiosResponse<GptApiResponse>
    ): Promise<AxiosResponse> {
        Logger.log(`[OpenAI API response] ${config.method} ${config.url}`);
        if (res.data.code === '0') {
            return Promise.resolve({
                ...res,
                data: res.data,
            });
        }
        return Promise.reject(new Error(`[${res.data.code}]`));
    }

    protected async onRejectedRequest(error: unknown): Promise<never> {
        return Promise.reject(error);
    }

    protected async onRejectedOnResponse(error: unknown): Promise<never> {
        return Promise.reject(error);
    }
}
  • AxiosInstance๋ฅผ ChatGptApiInstance์— ํ•„๋“œ๋กœ ๊ด€๋ฆฌ๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  • ์š”์ฒญ ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
    • ์š”์ฒญ ์ „ ์š”์ฒญํ•œ ๋ฐ์ดํ„ฐ๋“ค (config ํ•˜์œ„ ํ•„๋“œ)์„ ์ด์šฉํ•˜์—ฌ signature๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ธํ„ฐ์…‰ํ„ฐ์— ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์š”์ฒญ ์ค‘ ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ์ฒ˜๋ฆฌํ•  ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
  • ์‘๋‹ต ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
    • axios ์‘๋‹ต์„ ์ด์šฉํ•˜์—ฌ ์ •์ƒ ์—ฌ๋ถ€๋ฅผ ํŒ์ •ํ•˜๊ณ  ์ •์ƒ์ด๋ผ๋ฉด resolve, ์˜ค๋ฅ˜๋ผ๋ฉด reject ์‹œํ‚ค๋Š” ์ž‘์—…์„ ์ธํ„ฐ์…‰ํ„ฐ์— ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ ๊ฐ API ๊ตฌํ˜„์ฒด๋“ค์€ signature ์ƒ์„ฑ์„ ์ง์ ‘ ์ง„ํ–‰ํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค.

 

2๋‹จ๊ณ„: ๊ฐ ๊ตฌํ˜„์ฒด ์ •์˜ (Text, Image)

๊ฐ ๊ตฌํ˜„์ฒด์—์„œ๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ์ž‘์„ฑํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

// ํ…์ŠคํŠธ ์ƒ์„ฑ API ๊ตฌํ˜„์ฒด
export class ChatGptTextApi extends ChatGptApiInstance {
    constructor() {
        super();
    }

    async generateText(): Promise<GptTextResponse> {
        const { data } = await this.axiosInstance.post<GptTextResponse>(
            '/v1/chat/completions',
            {
                ...textRequestData,
            }
        )
        return data;
    }
}

// ์ด๋ฏธ์ง€ ์ƒ์„ฑ API ๊ตฌํ˜„์ฒด
export class ChatGptImageApi extends ChatGptApiInstance {
    constructor() {
        super();
    }

    async generateImage(): Promise<GptImageResponse> {
        const { data } = await this.axiosInstance.post<GptImageResponse>(
            '/v1/images/generations',
            {
                ...imageRequestData,
            }
        )
        return data;
    }
}

 

๊ฒฐ๋ก 

axios์™€ ์ด๊ฒƒ์ด ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ธํ„ฐ์…‰ํ„ฐ์— ๋Œ€ํ•ด ์ •๋ฆฌํ•ด ๋ณด๋‹ˆ ํ…์ŠคํŠธ๋กœ๋งŒ ์•Œ๊ณ  ์žˆ๋˜ ๋‚ด์šฉ์„ ์ง์ ‘ ์ ‘ํ•  ์ˆ˜ ์žˆ๋˜ ์ข‹์€ ๊ณ„๊ธฐ์˜€์Šต๋‹ˆ๋‹ค.

  • ์‚ฌ๋‚ด ์ฝ”๋“œ๋Š” ํ˜„์žฌ ๋ฒ„์ „ (1.7.9) ๋ณด๋‹ค ๋” ์ด์ „ ๋ฒ„์ „์ด๊ธฐ์—, ๊ตฌ์กฐ ๋ฐ ์›๋ฆฌ๊ฐ€ ์ง€๊ธˆ ์ •๋ฆฌํ•œ ๊ธ€๊ณผ ์•ฝ๊ฐ„ ๋‹ค๋ฅด๊ฒŒ ํ˜๋Ÿฌ๊ฐ‘๋‹ˆ๋‹ค. ์ด ๋•Œ๋ฌธ์— axios๋Š” ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋ฏ€๋กœ ์—…๋ฐ์ดํŠธ์— ๋”ฐ๋ผ ๊ตฌ์กฐ, ์›๋ฆฌ๊ฐ€ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋‹ค๋Š” ํŠน์ง•์„ ์ฒด๊ฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. (Axios ํด๋ž˜์Šค๊ฐ€ ์ •์˜๋˜๊ณ  ์š”์ฒญ ์ธํ„ฐ์…‰ํ„ฐ์˜ config๊ฐ€ AxiosRequestConfig์—์„œ InternalAxiosRequestConfig๋กœ ๋ณ€๊ฒฝ๋œ ๊ฒƒ ๋“ฑ)
  • axios์—์„œ๋Š” ์ •๋ฆฌํ•œ ๊ธ€์ฒ˜๋Ÿผ ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ๋ฐ”๋กœ ์ง€์›ํ•˜์ง€๋งŒ, fetch์—์„œ๋Š” ์ง์ ‘ ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋Š” ๋“ฑ fetch๊ฐ€ axios์— ๋น„ํ•ด ์ง€์›๋˜๋Š” ๊ธฐ๋Šฅ๋“ค์ด ์ œํ•œ๋œ๋‹ค๋Š” ์ ๋„ ์ฒด๊ฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์ง€๊ธˆ๊นŒ์ง€ axios ๋ฐ axios์˜ ์ธํ„ฐ์…‰ํ„ฐ์— ๋Œ€ํ•ด ์•Œ์•„๋ดค์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ‹€๋ฆฐ ์ ์ด ์žˆ๊ฑฐ๋‚˜ ๊ณ ์น  ์ ์ด ์žˆ๋‹ค๋ฉด ํ”ผ๋“œ๋ฐฑ ๋ฐœ์ƒ ์‹œ ๋ฐ˜์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

Reference