๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ“ TIL

JAVA์—์„œ BOT์„ ๋งŒ๋“ค์–ด๋ณด์ž. (๋ถ€์ œ. ๋ด‡ ๋“ค์ด ํŒ์น˜๋Š” ์„ธ์ƒ์„ ๋งŒ๋“ค์–ด๋ณด์ž!)

by Sjungwon 2025. 2. 24.

๋งŒ์•ฝ ์ด ์ž๋ฐ” ๋ด‡์„ ์ด์šฉํ•ด ์Šฌ๋ž™์ด๋‚˜ ๋…ธ์…˜ ์–ด์ฉŒ๊ตฌ ๋ด‡์„ ๋งŒ๋“ค๊ณ  ์‹ถ๋‹ค๋ฉด ํ•ด๋‹น ์Šค๋ž™๊ณผ ๋…ธ์…˜์˜ ์ถœ๋ ฅ ํ˜•์‹์— ๋งž๊ฒŒ ๊ณ ์ณ์•ผ ํ•œ๋‹ค. ๋‚˜๋Š” java ๋ด‡ ๋งŒ ํ•  ๊ฒƒ์ด๋‹ค.

 

์•ˆ๋…• ๊ตฌ๊ธ€ api

 

์ด๋ ‡๊ฒŒ ๋ชจ๋“  ๊ฒƒ์—๋Š” api๊ฐ€ ์žˆ๋‹ค.

 

๊ทธ ์ค‘์—์„œ ๋‚˜๋Š” ๊ทธ๋ก api๋ฅผ ์“ธ ๊ฒƒ์ด๋‹ค.


๊ทธ๋กฏ ํ•œํ…Œ ์™€์„œ ์ € api keys๋ผ๋Š” ๊ณณ์—์„œ apiํ‚ค๋ฅผ ๋ฐ›์•„๋‹ค๊ฐ€ ์–ด๋”˜๊ฐ€ ์ €์žฅ์„ ํ•ด๋‘์—ˆ๋‹ค๊ฐ€.

 

 


๋ชจ๋ธ์„ ์„ ํƒํ•˜๊ณ  view code ๋ถ€๋ถ„์— ๋“ค์–ด๊ฐ€์„œ ์„ค์ •์„ ํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. 
๊ทธ๋Ÿผ ์ฝ”๋“œ๊ฐ€ ๋‚˜์˜ค๋Š”๋ฐ ๊ทธ๊ฒƒ์„ ๊ธฐ์–ตํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.


 

gsk_xoYjD2j8JhXl  -- LLM_API_KEY



curl "https://api.groq.com/openai/v1/chat/completions-- LLM_API_URL \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${GROQ_API_KEY}" \
  -d '{
         "messages": [
           {
             "role": "user",
             "content": " ๋ฉ”๋กฑ"
           },
           {
             "role": "assistant",
             "content": "๋ฉ”๋กฑ!์€ ํ•œ๊ตญ์—์„œ ์œ ๋ช…ํ•œ ๋ฐฐ์šฐ์ž…๋‹ˆ๋‹ค. ๊ทธ์˜ ์—ฐ๊ธฐ๋ ฅ๊ณผ ์นด๋ฆฌ์Šค๋งˆ๋กœ ๋งŽ์€ ํŒฌ์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฐ€ ์ถœ์—ฐํ•œ ๋“œ๋ผ๋งˆ๋‚˜ ์˜ํ™”๋ฅผ ๋ณด์…จ๋‚˜์š”?"
           }
         ],
         "model": "llama-3.3-70b-versatile", -- LLM_MODEL
         "temperature": 1,
         "max_completion_tokens": 1024,
         "top_p": 1,
         "stream": true,
         "stop": null
       }'




์ด๋ ‡๊ฒŒ ๋‚˜์˜ต๋‹ˆ๋‹ค.
 ์ €๋ ‡๊ฒŒ ๋ถ€๋ถ„์— ํ•ด๋‹นํ•˜๋Š” ์• ๋“ค์„ ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ € ๋ชจ๋ธ ์นœ๊ตฌ๋Š” ์ œ ํ”„๋กฌํ”„ํŠธ์™€ ๋ณธ๋ฌธ ๊ธ€ ์นœ๊ตฌ์ž…๋‹ˆ๋‹ค.

์ž˜ ์•Œ์•˜๋‹ค๋ฉด ์ด์ œ ๊ทธ๋ฆผ ์นœ๊ตฌ๋ชจ๋ธ์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.


ํˆฌ๊ฒŒ๋”
457afd2f0c997b28 --LLM2_API_KEY



curl -X POST "https://api.together.xyz/v1/images/generations--LLM2_API_URL
  -H "Authorization: Bearer $TOGETHER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "black-forest-labs/FLUX.1-schnell-Free", --LLM2_MODEL
    "prompt": "[]",
    "width": 640,
    "height": 640,
    "steps": 4,
    "n": 1,
    "response_format": "b64_json"
   }'

๋ญ ์ด๋ ‡๊ฒŒ  ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ํ•ด๋‹นํ•˜๋Š” ๋ถ€๋ถ„์„ ์ž˜ ๊ธฐ์–ตํ•ด ๋’€๋‹ค๊ฐ€ ์ด์ œ

 

์›๋ž˜๋Š” ์ธํ…”๋ฆฌ j ๋ผ๋Š” ์•  ํ•œํ…Œ์„œ ํ•ด์•ผํ•˜๋Š”๋ฐ ๊ท€์ฐฎ์œผ๋‹ˆ vscode์—์„œ ํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ ํŒŒ์ผ๋“ค์€ .github ์•„๋ž˜์— ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ์˜ bot์€ ๊นƒํ—ˆ๋ธŒ์—์„œ ๋Œ๋ฆด ๊บผ๋‹ˆ๊นŒ์š”. 


๊นƒํ—ˆ๋ธŒ์—์„œ๋Š” ์…‹ํŒ…์ฆˆ์—์„œ secrets and variavles์— ๋‚ด ์†Œ์ค‘ํ•œ key๋“ค๊ณผ ์ฃผ์†Œ๋“ค์„ secrets์™€ variables์— ๋‚˜๋ˆ ์„œ ์ €์žฅ์„ ํ•ด๋‘”๋‹ค.

๋ญ.. ๋‚˜์ค‘์— ๋ฐ”๋‚˜๋‚˜ ์šฐ์œ  ์ฒ˜๋Ÿผ ๊บผ๋‚ด ๋“œ์„ธ์š”.

๋ง›์žˆ๊ฒ ๋‹ค.

 

๋ฌดํŠผ 
ํŒŒ์ผ์—์„œ Send-message.yml ์ด๋ผ๋Š” ํŒŒ์ผ์ด ์žˆ๋Š”๋ฐ 


์ด๋ ‡๊ฒŒ ์›Œํฌ ํ”Œ๋กœ์šฐ์— JavaBot์ด๋ผ๊ณ  ๋‚˜์˜จ๋‹ค.
-----------------------------------------------------------------------------------------------------------------------------

name: JavaBot  # GitHub Actions์˜ ์›Œํฌํ”Œ๋กœ์šฐ ์ด๋ฆ„์„ "JavaBot"์œผ๋กœ ์„ค์ •

on:
  workflow_dispatch:  # ์ˆ˜๋™ ์‹คํ–‰์„ ํ—ˆ์šฉ (GitHub์—์„œ ์ง์ ‘ ์‹คํ–‰ ๊ฐ€๋Šฅ)
  schedule:
    - cron: '0 * * * *'  # โฐ ๋งค ์ •๊ฐ(00๋ถ„)์— ์‹คํ–‰ (์˜ˆ: 00:00, 01:00, 02:00 ...)
    # ๐Ÿ’ก cron ํ‘œํ˜„์‹: '0 * * * *'
    # - ์ฒซ ๋ฒˆ์งธ ๊ฐ’(0): ๋งค ์‹œ๊ฐ„ ์ •๊ฐ(00๋ถ„)์— ์‹คํ–‰
    # - ๋‘ ๋ฒˆ์งธ ๊ฐ’(*): ๋งค ์‹œ๊ฐ„์„ ์˜๋ฏธ
    # - ๋‚˜๋จธ์ง€ ๊ฐ’(* * *): ๋งค์ผ, ๋งค์›”, ์š”์ผ ๋ฌด๊ด€ํ•˜๊ฒŒ ์‹คํ–‰
    # → ์ฆ‰, ๋งค 1์‹œ๊ฐ„๋งˆ๋‹ค ์‹คํ–‰๋จ.

jobs:
  create-issue:  # "create-issue"๋ผ๋Š” ์ด๋ฆ„์˜ ์ž‘์—… ์ •์˜
    runs-on: ubuntu-latest  # ์ตœ์‹  Ubuntu ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰

    steps:  # ์ž‘์—… ๋‹จ๊ณ„ ์ •์˜
      - name: Checkout Repository  # ๐Ÿ”น ํ˜„์žฌ GitHub ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์˜ ์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์˜ด
        uses: actions/checkout@v4  # GitHub์˜ ๊ณต์‹ checkout ์•ก์…˜ ์‚ฌ์šฉ (์ฝ”๋“œ ๋ณต์‚ฌ)

      - name: Setup Java 17  # ๐Ÿ”น Java 17 ํ™˜๊ฒฝ์„ ์„ค์ •
        uses: actions/setup-java@v4  # Java๋ฅผ ์„ค์ •ํ•˜๋Š” GitHub ์•ก์…˜ ์‚ฌ์šฉ
        with:
          distribution: temurin  # OpenJDK ๊ธฐ๋ฐ˜์˜ Temurin ๋ฐฐํฌํŒ ์‚ฌ์šฉ
          java-version: '17'  # Java ๋ฒ„์ „์„ 17๋กœ ์„ค์ •
          # ๐Ÿ’ก ๋งŒ์•ฝ ์ธํ…”๋ฆฌ์ œ์ด ๋“ฑ์—์„œ 23๋ฒ„์ „์œผ๋กœ ์˜ฌ๋ฆฌ๊ณ  ์‹ถ๋‹ค๋ฉด ์—ฌ๊ธฐ์„œ ์ˆ˜์ • ๊ฐ€๋Šฅ

      - name: Compile and Run Bot  # ๐Ÿ”น JavaBot ์‹คํ–‰
        run: |  # ์—ฌ๋Ÿฌ ๋ช…๋ น์–ด๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰
          javac JavaBot.java  # Java ์†Œ์Šค ํŒŒ์ผ(JavaBot.java)์„ ์ปดํŒŒ์ผ
          java JavaBot  # ์ปดํŒŒ์ผ๋œ Java ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰
        env:  # ์‹คํ–‰ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ • (Java ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ์‹œ ํ•„์š”ํ•œ ๊ฐ’๋“ค)
            GITHUB_TOKEN: ${{ secrets.GIT_TOKEN }}  # GitHub API ์ธ์ฆ ํ† ํฐ
            GITHUB_REPO: ${{ vars.GIT_REPO }}  # ์‹คํ–‰ํ•  GitHub ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ์ •๋ณด
            LLM_API_URL: ${{ vars.LLM_API_URL }}  # LLM API์˜ ๊ธฐ๋ณธ URL
            LLM_API_KEY: ${{ secrets.LLM_API_KEY }}  # LLM API์˜ ์ธ์ฆ ํ‚ค (๋ณด์•ˆ ์œ ์ง€ ํ•„์š”)
            LLM_MODEL: ${{ vars.LLM_MODEL }}  # ์‚ฌ์šฉํ•  LLM ๋ชจ๋ธ ์ด๋ฆ„
            LLM2_API_URL: ${{ vars.LLM2_API_URL }}  # ์ถ”๊ฐ€ LLM API URL
            LLM2_API_KEY: ${{ secrets.LLM2_API_KEY }}  # ์ถ”๊ฐ€ LLM API ์ธ์ฆ ํ‚ค
            LLM2_MODEL: ${{ vars.LLM2_MODEL }}  # ์ถ”๊ฐ€ LLM ๋ชจ๋ธ ์ด๋ฆ„


-----------------------------------------------------------------------------------------------------------------------------



๊ทธ๋ฆฌ๊ณ  JavaBot.java๋Š” .github/workflows์˜ ๋ฐ–์— ์žˆ์–ด์•ผํ•œ๋‹ค.
---------------------------------------------------------------------------------------------------

// ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž„ํฌํŠธ
import java.net.URI; // URL์„ ๋‹ค๋ฃจ๋Š” Java ํด๋ž˜์Šค
import java.net.http.HttpClient; // HTTP ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ
import java.net.http.HttpRequest; // HTTP ์š”์ฒญ์„ ์ƒ์„ฑํ•˜๋Š” ํด๋ž˜์Šค
import java.net.http.HttpResponse; // HTTP ์‘๋‹ต์„ ์ฒ˜๋ฆฌํ•˜๋Š” ํด๋ž˜์Šค
import java.nio.charset.StandardCharsets; // UTF-8 ๋“ฑ์˜ ๋ฌธ์ž ์ธ์ฝ”๋”ฉ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ํŒจํ‚ค์ง€
import java.util.regex.Matcher; // ์ •๊ทœ ํ‘œํ˜„์‹์„ ์ด์šฉํ•ด ๋ฌธ์ž์—ด์—์„œ ์›ํ•˜๋Š” ํŒจํ„ด์„ ์ถ”์ถœํ•˜๋Š” ํด๋ž˜์Šค
import java.util.regex.Pattern; // ์ •๊ทœ ํ‘œํ˜„์‹์„ ์ •์˜ํ•˜๋Š” ํด๋ž˜์Šค

public class JavaBot { // JavaBot ํด๋ž˜์Šค ์ •์˜ (๋ฉ”์ธ ์‹คํ–‰ ํด๋ž˜์Šค)
    public static void main(String[] args) {
        // ๐ŸŸข 1. LLM์„ ์ด์šฉํ•˜์—ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ถ”์ฒœ ๋ฐ›๊ธฐ
        String llmResult = useLLM("์ž๋ฐ” ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ค‘ ๊ฐœ๋ฐœ์ž ํ˜„์—…์—์„œ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋žœ๋ค์œผ๋กœ ํ•˜๋‚˜๋ฅผ ์ถ”์ฒœํ•˜๊ณ  ์™œ ํ•„์š”ํ•œ์ง€ ์‚ฌ๋ก€ ์œ„์ฃผ 1000์ž ์ด๋‚ด๋กœ ์ž‘์„ฑ. ๋ณ„๋„์˜ ์•ž๋’ค ๋‚ด์šฉ ์—†์ด ํ•ด๋‹น ๋‚ด์šฉ๋งŒ ์ถœ๋ ฅ. nutshell, for slack message, in korean.");
        System.out.println("llmResult = " + llmResult);
        
        // ๐ŸŸข 2. LLM์„ ์ด์šฉํ•˜์—ฌ ์ œ๋ชฉ ์š”์•ฝ ์ƒ์„ฑ
        String summarizedTitle = summarizeText(llmResult);
        System.out.println("Summarized Title = " + summarizedTitle);

        // ๐ŸŸข 3. LLM์„ ์ด์šฉํ•ด ์ด๋ฏธ์ง€ ์ƒ์„ฑ
        String image_url = useLLM2("A modern software development company's interior in Japanese manga style. The open office layout features a spacious and organized workspace where multiple developers are actively coding at their desks. Some are discussing ideas while looking at their monitors, while others are brainstorming on a whiteboard. The walls are adorned with various tech-related posters and whiteboards, and desks are cluttered with laptops, dual monitors, coffee cups, snacks, and figurines. The lighting is warm and inviting, creating a comfortable atmosphere. A cityscape can be seen through the windows. The characters are drawn in a vibrant Japanese anime style, exuding energy and liveliness.");
        System.out.println("image url = " + image_url);

        // ๐ŸŸข 4. GitHub Issue ์ƒ์„ฑ
        sendIssues(summarizedTitle, llmResult, image_url);
    }

    // ๐Ÿ”น LLM API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฉ”์„œ๋“œ (ํ…์ŠคํŠธ ์ƒ์„ฑ์šฉ)
    public static String useLLM(String prompt) {
        return callLLMApi(prompt);
    }

    // ๐Ÿ”น LLM API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฉ”์„œ๋“œ (์ด๋ฏธ์ง€ ์ƒ์„ฑ์šฉ)
    public static String useLLM2(String prompt) {
        return callLLMApi2(prompt);
    }

    // ๐Ÿ”น LLM์„ ์ด์šฉํ•ด ํ…์ŠคํŠธ ์š”์•ฝ (10์ž ์ด๋‚ด์˜ ์ œ๋ชฉ ์ƒ์„ฑ)
    public static String summarizeText(String text) {
        return callLLMApi("๋‹ค์Œ ๋‚ด์šฉ์„ 10์ž ๋‚ด์™ธ์˜ ๊ฐ„๊ฒฐํ•œ ์ œ๋ชฉ์œผ๋กœ ์š”์•ฝํ•ด์ค˜. '" + text + "'");
    }

    // ๐Ÿ”น LLM API ํ˜ธ์ถœ (ํ…์ŠคํŠธ ๊ธฐ๋ฐ˜ ์‘๋‹ต)
    public static String callLLMApi(String prompt) {
        String apiUrl = System.getenv("LLM_API_URL"); // LLM API์˜ URL ํ™˜๊ฒฝ ๋ณ€์ˆ˜
        String apiKey = System.getenv("LLM_API_KEY"); // LLM API์˜ ์ธ์ฆ ํ‚ค
        String model = System.getenv("LLM_MODEL"); // ์‚ฌ์šฉํ•  LLM ๋ชจ๋ธ

        // API ์š”์ฒญ์„ ์œ„ํ•œ JSON ํŽ˜์ด๋กœ๋“œ ์ƒ์„ฑ
        String payload = """
                {
                  "messages": [
                    {
                      "role": "user",
                      "content": "%s"
                    }
                  ],
                  "model": "%s"
                }
                """.formatted(prompt, model);

        HttpClient client = HttpClient.newHttpClient(); // HTTP ํด๋ผ์ด์–ธํŠธ ์ƒ์„ฑ
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(apiUrl)) // ์š”์ฒญํ•  API URL ์„ค์ •
                .header("Content-Type", "application/json") // JSON ์š”์ฒญ์ž„์„ ๋ช…์‹œ
                .header("Authorization", "Bearer " + apiKey) // API ์ธ์ฆ ํ—ค๋” ์ถ”๊ฐ€
                .POST(HttpRequest.BodyPublishers.ofString(payload, StandardCharsets.UTF_8)) // POST ์š”์ฒญ
                .build();

        try {
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

            System.out.println("response.statusCode() = " + response.statusCode()); // HTTP ์ƒํƒœ ์ฝ”๋“œ ์ถœ๋ ฅ
            System.out.println("response.body() = " + response.body()); // ์‘๋‹ต ๋ณธ๋ฌธ ์ถœ๋ ฅ

            if (response.statusCode() == 200) {
                return extractContent(response.body()); // ์ •๊ทœ์‹์œผ๋กœ ์‘๋‹ต ๋‚ด์šฉ ์ถ”์ถœ
            } else {
                return "LLM API ์˜ค๋ฅ˜: " + response.statusCode();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "์˜ˆ์™ธ ๋ฐœ์ƒ: " + e.getMessage();
        }
    }

    // ๐Ÿ”น LLM API ํ˜ธ์ถœ (์ด๋ฏธ์ง€ ์ƒ์„ฑ)
    public static String callLLMApi2(String prompt) {
        String apiUrl = System.getenv("LLM2_API_URL");
        String apiKey = System.getenv("LLM2_API_KEY");
        String model = System.getenv("LLM2_MODEL");

        // API ์š”์ฒญ ํŽ˜์ด๋กœ๋“œ (์ด๋ฏธ์ง€ ์ƒ์„ฑ)
        String payload = """
                {
                "prompt": "%s",
                "model": "%s",
                "width": 640,
                "height": 640,
                "steps": 4,
                "n": 1
                }
                """.formatted(prompt, model);

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(apiUrl))
                .header("Content-Type", "application/json")
                .header("Authorization", "Bearer " + apiKey)
                .POST(HttpRequest.BodyPublishers.ofString(payload, StandardCharsets.UTF_8))
                .build();

        try {
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

            if (response.statusCode() == 200) {
                return extractImageUrl(response.body());
            } else {
                return "LLM API ์˜ค๋ฅ˜: " + response.statusCode();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "์˜ˆ์™ธ ๋ฐœ์ƒ: " + e.getMessage();
        }
    }

    // ๐Ÿ”น JSON ์‘๋‹ต์—์„œ "content" ๊ฐ’ ์ถ”์ถœ (ํ…์ŠคํŠธ)
    public static String extractContent(String json) {
        Pattern pattern = Pattern.compile("\"content\":\"([^\"]+)\""); // "content":"๋‚ด์šฉ"
        Matcher matcher = pattern.matcher(json);

        if (matcher.find()) {
            return matcher.group(1);
        }
        return "์‘๋‹ต์—์„œ content ๊ฐ’์„ ์ฐพ์„ ์ˆ˜ ์—†์Œ";
    }

    // ๐Ÿ”น JSON ์‘๋‹ต์—์„œ "url" ๊ฐ’ ์ถ”์ถœ (์ด๋ฏธ์ง€ URL)
    public static String extractImageUrl(String json) {
        Pattern pattern = Pattern.compile("\"url\"\\s*:\\s*\"(https?://[^\"]+)\"");
        Matcher matcher = pattern.matcher(json);

        if (matcher.find()) {
            return matcher.group(1);
        }
        return "์‘๋‹ต์—์„œ ์ด๋ฏธ์ง€ URL์„ ์ฐพ์„ ์ˆ˜ ์—†์Œ";
    }

    // ๐Ÿ”น GitHub Issue ์ƒ์„ฑ ํ•จ์ˆ˜
    public static void sendIssues(String title, String body, String imageUrl) {
        String repo = System.getenv("GITHUB_REPO");
        String token = System.getenv("GITHUB_TOKEN");
        String apiUrl = "https://api.github.com/repos/" + repo + "/issues";

        if (repo == null || token == null) {
            System.out.println("GITHUB_REPO ๋˜๋Š” GITHUB_TOKEN ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.");
            return;
        }

        // ์ œ๋ชฉ๊ณผ ๋ณธ๋ฌธ์—์„œ JSON ๋ฌธ๋ฒ• ์˜ค๋ฅ˜ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด ์ด์Šค์ผ€์ดํ”„ ์ฒ˜๋ฆฌ
        String safeTitle = title.replace("\"", "\\\"").replace("\n", "\\n");
        String safeBody = body.replace("\"", "\\\"").replace("\n", "\\n");

        // Markdown ํ˜•์‹์œผ๋กœ ์ด๋ฏธ์ง€ ์ถ”๊ฐ€
        String fullBody = safeBody + "\\n\\n---\\n![Generated Image](" + imageUrl + ")";

        String payload = "{"
                + "\"title\": \"" + safeTitle + "\","
                + "\"body\": \"" + fullBody + "\""
                + "}";

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(apiUrl))
                .header("Authorization", "Bearer " + token)
                .POST(HttpRequest.BodyPublishers.ofString(payload))
                .build();

        try {
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

            if (response.statusCode() == 201) {
                System.out.println("โœ… GitHub Issue ์ƒ์„ฑ ์„ฑ๊ณต!");
            } else {
                System.out.println("โŒ GitHub Issue ์ƒ์„ฑ ์‹คํŒจ: " + response.body());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}



์ฝ”๋“œ๋Š” ์œ„์™€ ๊ฐ™๋‹ค.

------------------------------------------------------------------------------------------------------------------------------------------
์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ด์ œ ๊นƒํ—ˆ๋ธŒ์—์„œ ์•Œ์•„์„œ llm์ด ๊ธ€ ๊ณผ ์ด๋ฏธ์ง€๋ฅผ ์ค€๋‹ค.

200์€ ์ •์ƒ 400 403 ์—๋Ÿฌ