안녕하세요. AI 서비스 활용 헬퍼 AIDA(아이다)입니다.
이번 시간에는 OpenAI의 Text To Image 생성형 AI 서비스인 DALLE3 API서비스를 Node Express 백엔드 어플리케이션과 연동하여 백엔드 앱에서 사용자 정의 프롬프트를 기반으로한 AI 이미지를 만들고  백엔드앱 이나 서버측에 해당 이미지를 다운로드 하는 방법에 대해 안내해 드리겠습니다.

1) Node Express 프로젝트 생성하기 

먼저 아래 Node Express 명령어를 통해  간단한 노드 백엔드 앱 프로젝트를 생성합니다.
VSCode 상단 메뉴 File> Open Folder 메뉴를 클릭하여 작업폴더를 지정합니다.
ex) D:\Projects

VSCode상단 메뉴 Terminal>New Terminal메뉴를 클릭하여 VSCode하단에 터미널 창을 오픈합니다.
터미널 영역에 하기 명령어를 순차적으로 입력하여 DALL-E-3-Node-App 프로젝트를 생성합니다.
express DALL-E-3-Node-App --view=ejs

-cd 명령어를 통해 생성된 프로젝트 로 이동합니다.
cd DALL-E-3-Node-App

-해당 DALL-E-3-Node-App 실행을 위한 필수 노드 패키지를 일괄 복원설치합니다.
npm i 

-DALL-E-3-Node-App 노드 어플리케이션을 실행합니다.
npm start

참고링크) Node Express 프로젝트 생성 및 실행하기 
https://mixedcode.com/blog/detail?pid=4

GitHub 소스 링크
https://github.com/eddykang1074/DALL-E-3-Node-App.git


2) OpenAI API 연동을 위한 관련 패키지 추가 설치하기 

-터미널 창에서 하기 3개의 노드 패키지를 추가 설치합니다.
-dotenv는 어플리케이션 환경설정파일 관리를 위해 axios는 OpenAI API와 AJAX 통신하기 위한 패키지이며 openai는 각종 OpenAI 서비스와 연계하여 노드 어플리케이션에서 생성형AI서비스를 개발하기 위한 오픈소스 노드패키지입니다.

npm i dotenv
npm i axios
npm i openai

openai Node패키지 GitHub 저장소 링크
https://www.npmjs.com/package/openai

openai Node패키지 설치 및 환경 설정 가이드 링크
https://platform.openai.com/docs/quickstart?context=node

OpenAI DALL.E 2/3 API 서비스 개발자 가이드 링크
https://platform.openai.com/docs/api-reference/images

3) OpenAI API 서비스 신청 및 키 생성하기 

-OpenAI API 서비스와 연동하는 서비스르 개발하기 위해서는 사전에 OpenAI 사이트에 회원으로 가입하고 Project API 인증키를 발급받아야 하며 Project API 인증키를 원할하게 사용하려면 유료결제 서비스 및 결제 신용카드 등록 및 해당 카드로 선결제를 통한 유료 크레딧을 확보하고 있어야합니다.
-보다 자세한 절차는 아래 연계 블로깅 링크를 참고해서 선 진행을 통해 인증키와 유료 크레딧을 확보해주시기 바랍니다.
https://mixedcode.com/blog/detail?pid=6

4) Proejct API 키값 환경 설정 파일로 구성하기  

A)프로젝트 루트에 환경설정파일 생성하기
-프로젝트 루트 경로에 .env파일을 생성합니다.
-.env파일을 열과 아래와 같이 3개의 환경변수와 값을 설정 저장합니다.

-.env 파일 생성

PORT = 3001  #노드백엔드 어플리케이션 서비스 실행 포트
NODE_ENV=development   #노드 백엔드 어플리케이션 실행모드
OPENAI_API_KEY=sk-proj-thisisSAMLEsecurityCodeValuesdfd   #OpenAI에서 발급받은 Project API 키값

B)노드 app.js 환경설정 파일 구성하기
-프로젝트 루트에 app.js을 오픈하고 하기 설정 내용을 추가합니다.

-/app.js 

//환경설정파일 구성하기

var logger = require("morgan");

//환경설정파일 구성하기
//주의: 반드시 라우팅 파일 참조 위쪽에 dotenv 환경구성 설정하기
require("dotenv").config();

//CORS 접근 이슈 해결을 위한 cors패키지 참조
const cors = require("cors");

var indexRouter = require("./routes/index");
var usersRouter = require("./routes/users");


5)이미지 생성 요청과 응답처리 라우팅 기능구현-Controller
-routes\index.js 라우터 파일에 dalle3 웹페이지 요청 및 응답처리에 대한 라우팅 메소드를 2개 구현합니다.
-이미지 생성 웹페이지 요청 및 응답처리 라우팅메소드

/*
 * dalle3 api 기반 TextToImage 생성 샘플
 */
router.get("/dalle3", async (req, res, next) => {
  let imageURL = "";
  let imageBinaryData = "";

  res.render("dalle3", { imageURL, imageBinaryData });
});



6)이미지 생성요청 웹페이지 화면 구현 -View(.ejs)

-http://localhost:3001/dalle3 주소로 반환되는 웹페이지 뷰파일을 하기 경로에 생성합니다.
-views\dalle3.ejs
-OpenAI API서비스 호출시 사용자가 선택할수 있는 몇가지 옵션을 UI로 선택할수 있게 화면요소를 표시합니다.

<html>
  <head>
    <title>DALL.E3 API 호출 샘플</title>
    <style>
      select {
        width: 270px;
        height: 50px;
        font-size: 20pt;
        margin-bottom: 10px;
      }

      input[type="text"] {
        width: 400px;
        height: 50px;
        font-size: 20pt;
        margin-bottom: 10px;
      }

      button {
        width: 180px;
        height: 48px;
        font-size: 20pt;
        vertical-align: auto;
      }
    </style>
  </head>

  <body>
    <h1>DALL.E3 API 호출 샘플-2024.04.15 기준</h1>

    <form action="/dalle3" method="post">
      <select name="version">
        <option value="">DALL.E 버전 선택</option>
        <option value="dall-e-2">DALL.E 2</option>
        <option value="dall-e-3">DALL.E 3</option>
      </select>
      <br />
      <select name="size">
        <option value="">이미지 크기 선택</option>
        <option value="1024x1024">1024x1024</option>
      </select>
      <br />
      <input
        type="text"
        name="prompt"
        id="prompt"
        value=""
        placeholder="프롬프트"
      />
      <button type="submit">이미지 생성</button>
      <div>예시 프롬프트</div>
      <p>
        영화 Kill Bill의 가상 레고 박스 세트(영화의 유명한 장면과 중심 캐릭터
        포함)
      </p>
      <p>바르셀로나 FC에서 영감을 받은 네일 아트 디자인</p>
      <p>ZX Spectrum Game MANIC MINER를 3D 현대적인 스타일의 게임으로 재해석</p>
      <a
        href="https://www.instantaiprompt.com/ko/prompts/dall-e/11-best/"
        target="_blank"
        >프롬프트 참고링크</a
      >
      <br />
      <br />
      BinaryData: <%=imageBinaryData%> <br />
      <img src="<%=imageURL%>" />
    </form>
  </body>
</html>


A) DALL.E 버전선택
-DALL.E 2 OR 3버전을 선택할수 있습니다.

B)이미지 크기 선택하기 
-이미지 크기는 DALL.E 2  또는 3버전에 따라 지원되는 이미지 사이즈버전이 다릅니다.
-DALL.E 2:256x256, 512x512 또는 1024x1024
-DALL.E 3:1024x1024(기본값), 1792x1024 또는 1024x1792

C)프롬프트: 사용자가 입력하는 이미지 생성요청 프롬프트입니다. 
-프롬프트는 사용자가 생성형 AI서비스 종료별 특성을 사전학습하여 정확한 프롬프트를 작성해야 양질의 이미지가 생성됩니다.
-하단에 예시 샘플을 이용해 새로운 이미지를 만들어 보세요.


7)DALL.E 2/3기반 AI이미지 생성하기

-http://localhost:3001/dalle3 화면에서 정보를 선택/프롬프트를 입력하고 이미지생성 버튼을 클릭합니다.
-사용자가 입력/선택한 정보를 이용해 OpenAI API서비스를 호출합니다.
-API 호출시 더 다양한 하기와 같은 선택옵션등이 제공됩니다.

    // {
    //   model: dall-e-2, dall-e-2(default) or dall-e-3
    //   prompt: a white siamese cat, 최대길이:dall-e-2:1000자,dall-e-3:4000자
    //   n: 1,  생성할 이미지수 ,기본값:1 , dall-e-2:1-10사이,dall-e-3:1만지원
    //   size: 생성할 이미지크기,dall-e-2의 경우 256x256, 512x512 또는 1024x1024(기본값) 중 하나,dall-e-3 모델의 경우 1024x1024(기본값), 1792x1024 또는 1024x1792 중 하나.
    //   quality:생성할 이미지품질, standard(default) or hd(dall-e-3만지원-세밀하고일관성이 뛰어남)
    //   response_format: 반환형식 , url(기본값=60분간만 유효함) or b64_json(binarydata)
    //   style: 기본값:vivid(선명하고 초현실적이고 극적인 이미지를 생성) or natural (모델이 더 자연스럽고 덜 초현실적인 이미지를 생성-dall-e-3 만지원)
    //   user:
    // }


/*
 * dalle3 api 호출 이미지 생성 및 다운로드 저장하기
 */
router.post("/dalle3", async (req, res, next) => {
  let imageURL = "";
  let imageBinaryData = "";

  //a white siamese cat

  try {
    var model = req.body.version;
    var prompt = req.body.prompt;
    var size = req.body.size;
    var style = "vivid"; // vivid
    var response_format = "url"; //b64_json or url

    //참고URL: https://platform.openai.com/docs/api-reference/images/createEdit
    // {
    //   model: dall-e-2, dall-e-2(default) or dall-e-3
    //   prompt: a white siamese cat, 최대길이:dall-e-2:1000자,dall-e-3:4000자
    //   n: 1,  생성할 이미지수 ,기본값:1 , dall-e-2:1-10사이,dall-e-3:1만지원
    //   size: 생성할 이미지크기,dall-e-2의 경우 256x256, 512x512 또는 1024x1024(기본값) 중 하나,dall-e-3 모델의 경우 1024x1024(기본값), 1792x1024 또는 1024x1792 중 하나.
    //   quality:생성할 이미지품질, standard(default) or hd(dall-e-3만지원-세밀하고일관성이 뛰어남)
    //   response_format: 반환형식 , url(기본값=60분간만 유효함) or b64_json(binarydata)
    //   style: 기본값:vivid(선명하고 초현실적이고 극적인 이미지를 생성) or natural (모델이 더 자연스럽고 덜 초현실적인 이미지를 생성-dall-e-3 만지원)
    //   user:
    // }

    const response = await openai.images.generate({
      model: model,
      prompt: prompt,
      n: 1,
      size: size,
      style: style,
      response_format: response_format,
    });

    const imgFileName = `sample-${Date.now()}.png`;
    const imgFilePath = `./public/images/${imgFileName}`;

    if (response_format == "url") {
      imageURL = response.data[0].url;

      axios({
        url: imageURL,
        responseType: "stream",
      })
        .then((response) => {
          response.data
            .pipe(fs.createWriteStream(imgFilePath))
            .on("finish", () => {
              console.log("Image saved successfully.");
            })
            .on("error", (err) => {
              console.error("Error saving image:", err);
            });
        })
        .catch((err) => {
          console.error("Error downloading image:", err);
        });
    } else {
      imageBinaryData = response.data[0].b64_json;
      const buffer = Buffer.from(imageBinaryData, "base64");
      fs.writeFileSync(imgFilePath, buffer);
      imageURL = `/images/${imgFileName}`;
    }

    console.log("이미지 생성 URL:", response);
  } catch (err) {
    console.log("OpenAI DALL.E3 API 호출 에러발생:", err);
  }

  res.render("dalle3", { imageURL, imageBinaryData });
});


-API가 호출되면 30초 가량이 API 호출시간이 발생하고 OPEN AI API에서는 두가지 방식으로 결과 값을 제공합니다.
-API 호출결과 반환 포맷은 API호출시 설정하는 response_format 속성 지정을 통해 선택적으로 지정하여 결과값을 받을수 있습니다.

A)API 호출결과값 유형1: 이미지 URL값

-OpenAI 스토리지에 생성된 이미지의 전체 저장경로를 URL 값으로 제공됩니다.
-OpenAI제공  URL은 이미지 생성후 60분간 제공되며 이후에는 접근이 불가하므로 해당 이미지 저장이 필요하면 다운로드해서 별도 개발자가 원하는 공간에 저장해야 재사용이 가능합니다.

B)API 호출결과값 유형1: 이미지 바이너리 데이터값 

-OpenAI에서 생성된 이미지를 바이너리 데이터 값으로 JSON형식을 통해 제공되며 해당 바이너리 데이터값을 이용해 개발자가 원하는 공간에 물리적 이미지 파일을 생성 저장할수 있습니다.


8) AI이미지 서버경로에  다운로드 또는 새로 만들기

-OpenAI에서 생성된 이미지를 영구보관거나 재사용하고 싶다면 해당 프롬프트나 호출시 사용한 설정값들을 보관하거나 결과물 이미지를 별도 공간에 보관해야합니다.
-동일한 프롬프트라고 해서 매번 호출시 동일한 결과값이 나오라는 보장은 없으며 생성시 관련 정보를 지속적으로 저장/유지해야 동일하 결과값 기반으로 이미지가 유지되며 생성된 이미지를 영구적으로 보관하려면 개발자가 지정한 저장공간에 URL을 통한 이미지 저장 또는 이미지 바이너리 데이터를 통한 물리적 이미지 파일 생성기능을  아래와 같이 언어별로 별도 구현해야합니다.

A)URL을 통한 이미지 다운로드 생성하기

response.data
            .pipe(fs.createWriteStream(imgFilePath))
            .on("finish", () => {
              console.log("Image saved successfully.");
            })
            .on("error", (err) => {
              console.error("Error saving image:", err);
            });


B)바이너리 데이터 기반 이미지 파일 생성하기 실수 있습니다.

      imageBinaryData = response.data[0].b64_json;
      const buffer = Buffer.from(imageBinaryData, "base64");
      fs.writeFileSync(imgFilePath, buffer);
      imageURL = `/images/${imgFileName}`;