Skip to content

[복습] 2025.03.12 #30

Description

@Yumi-Park996

이 문서는 LLM API 호출과 Slack 연동을 중심으로 로거와 멀티스레드의 개념을 설명하는 교안입니다.
이 문서를 통해, 다음 내용을 쉽게 이해할 수 있습니다.

  1. LLM API를 호출하고 응답을 처리하는 과정
  2. Slack을 이용해 결과를 전송하는 과정
  3. 로깅(Logger) 시스템을 활용하는 방법
  4. 멀티스레드를 사용해 성능을 최적화하는 방법

1. 전체적인 동작 흐름

📌 프로그램이 어떻게 동작하는지 한눈에 보기

이 프로그램의 전체적인 흐름을 도식화하면 다음과 같습니다.

+-----------------------------+
|      Application 실행       |
+-----------------------------+
        |    |    |
        V    V    V
 +-------------------------+
 |  3개의 LLM 요청 실행   |
 | (멀티스레드 동작)       |
 +-------------------------+
        |    |    |
        V    V    V
+-----------------------------+
|   AI 응답을 Slack으로 전송  |
+-----------------------------+

📌 핵심 개념
1️⃣ 사용자가 입력한 질문을 LLM API에 보냄
2️⃣ 멀티스레드를 사용해 3개의 요청을 동시에 실행
3️⃣ 응답을 받아 Slack으로 메시지 전송


2. 프로그램의 주요 기능

이 프로그램은 여러 개의 클래스로 구성되어 있으며, 각 파일의 역할을 정리하면 다음과 같습니다.

파일명 역할
Application.java 프로그램의 메인 실행 코드
LLM.java LLM API 요청을 처리하는 클래스
ModelCategory.java 사용할 LLM 모델 종류를 정의하는 Enum 클래스
Slack.java Slack으로 메시지를 보내는 클래스
MyLogger.java 로그를 기록하는 유틸리티 클래스 (싱글톤)
MySecret.java API 키를 관리하는 환경 변수 유틸리티 클래스
WebClient.java HTTP 요청을 처리하는 유틸리티 클래스 (싱글톤)

3. 주요 개념 설명

(1) LLM API란?

LLM (Large Language Model) API는 ChatGPT와 같은 AI 모델과 대화할 수 있도록 도와주는 인터페이스입니다.

이 프로그램에서는 LLM API를 통해 질문을 보내고 답변을 받는 기능을 수행합니다.

📌 LLM API 요청 방식

  • 요청 방식: HTTP POST
  • 요청 예시:
{
    "model": "meta-llama/Llama-3.3-70B-Instruct-Turbo-Free",
    "messages": [{
        "role": "user",
        "content": "What is Java?"
    }],
    "max_tokens": 2048
}
  • 응답 예시:
{
    "choices": [{
        "message": {
            "role": "assistant",
            "content": "Java is a programming language..."
        }
    }]
}

📌 핵심 개념
1️⃣ 사용자의 질문을 JSON 형식으로 LLM API에 보냄
2️⃣ AI 모델이 응답을 생성하고 반환


(2) Slack 메시지 전송

Slack 메시지는 특정 웹훅 URL을 통해 전송됩니다.
이 프로그램에서는 Slack.java 클래스에서 Slack 메시지를 전송하는 기능을 수행합니다.

  • 요청 데이터 예시:
{
    "text": "Java is a programming language...",
    "attachments": [{
        "image_url": "https://image.example.com/java.png"
    }]
}

📌 핵심 개념
1️⃣ API를 이용해 Slack으로 메시지를 보냄
2️⃣ 텍스트와 이미지를 함께 전송 가능


4. 로거 (Logger) 개념

📌 로깅(Log) 이란?

로깅이란 프로그램 실행 정보를 저장하여 디버깅과 오류 분석을 쉽게 만드는 것입니다.

📌 로그 레벨(Log Level)

로그 레벨 설명
DEBUG 상세한 디버깅 정보
INFO 일반적인 실행 정보
WARN 경고 메시지 (큰 문제는 아니지만 주의 필요)
ERROR 오류 발생 시 기록

📌 코드 예시 (MyLogger.java)

public class MyLogger {
    private static MyLogger instance;  // ✅ 유일한 인스턴스를 저장할 변수
    // static 키워드는 클래스가 로드될 때 한 번만 메모리에 할당됨
    // 따라서, instance 변수는 프로그램이 실행되는 동안 단 하나만 유지
    private final Logger logger;  // Java 기본 제공 Logger

    // ✅ private 생성자 (외부에서 new MyLogger()를 사용 못하게 막음)
    private MyLogger() {
        logger = Logger.getLogger(MyLogger.class.getName());
    }

    // ✅ getLogger() 메서드를 통해 단 하나의 인스턴스만 반환
    public static MyLogger getLogger() {
        if (instance == null) {  // 🔹 객체가 아직 생성되지 않았다면
            instance = new MyLogger();  // 🔹 객체를 생성
        }
        return instance;  // 🔹 이미 생성된 객체를 반환
    }

    // ✅ 로그 출력 함수
    public void info(String msg) {
        this.logger.info(msg);
    }
}

📌 싱글톤 패턴 적용
1️⃣ static 변수 instance로 단 하나의 객체만 유지
2️⃣ getLogger()를 통해 항상 같은 객체를 반환


5. 멀티스레드 (Multi-threading) 개념

📌 멀티스레드란?

멀티스레드란 여러 개의 작업을 동시에 실행하여 속도를 빠르게 하는 기법입니다.

📌 단일 스레드 vs 멀티스레드

방식 특징
단일 스레드 한 번에 하나의 작업만 실행 (응답이 느릴 수 있음)
멀티스레드 여러 개의 작업을 동시에 실행 (응답 속도 향상)

📌 멀티스레드 흐름도

+-----------------------------+
|        Main Thread          |
+-----------------------------+
        |    |    |
        V    V    V
+-----------------------------+
| 3개의 LLM 요청 실행 (병렬) |
+-----------------------------+
        |    |    |
        V    V    V
+-----------------------------+
|        응답 수집 후 Slack 전송        |
+-----------------------------+

📌 멀티스레드 코드 예시

ExecutorService executor = Executors.newFixedThreadPool(3);

Future<String> imageFuture = executor.submit(new LLMTask(ModelCategory.FLUX, "Create an image for Java."));
Future<String> reasoningFuture = executor.submit(new LLMTask(ModelCategory.R1, "Explain Java in detail."));
Future<String> reasoningFuture2 = executor.submit(new LLMTask(ModelCategory.R1, "Give Java job interview tips."));

String imageResult = imageFuture.get();
String reasoningResult = reasoningFuture.get();
String reasoningResult2 = reasoningFuture2.get();

executor.shutdown();

📌 핵심 개념
1️⃣ Executors.newFixedThreadPool(3); → 최대 3개의 작업을 동시에 실행
2️⃣ executor.submit(new LLMTask(...)); → 스레드에서 LLM 요청 실행
3️⃣ Future.get(); → 작업이 끝날 때까지 기다림


6. 마무리

✅ 로거: 프로그램 실행을 기록하고 디버깅을 돕는 도구
✅ 멀티스레드: 여러 작업을 동시에 실행하여 속도를 향상
✅ LLM API + Slack 연동: AI 응답을 Slack으로 전송



📌 API란?

1. API (Application Programming Interface)란?

API(Application Programming Interface)는 **소프트웨어 간의 상호 작용을 가능하게 하는 인터페이스(접점)**입니다.
즉, 프로그램이나 서비스가 서로 데이터를 주고받거나 기능을 호출할 수 있도록 해주는 규칙 및 프로토콜을 의미합니다.


2. API의 주요 역할

1️⃣ 데이터 교환: 한 시스템에서 다른 시스템으로 데이터를 요청하고 응답받을 수 있음
2️⃣ 기능 제공: 특정 기능(예: AI 모델 호출, 결제 시스템 등)을 외부에서도 사용할 수 있도록 제공
3️⃣ 보안 유지: API를 통해 제한된 데이터만 노출하고, 인증을 요구하여 보안성을 유지
4️⃣ 추상화(Abstract): 내부 구현 방식을 숨기고, 사용자가 쉽게 기능을 호출할 수 있도록 함


3. API의 종류

1️⃣ Web API (REST API, GraphQL API 등)

  • 인터넷을 통해 요청을 보내고 응답을 받는 API
  • HTTP 프로토콜을 사용하여 데이터를 주고받음
  • 예: GitHub API, OpenAI API, Google Maps API 등

2️⃣ Library API

  • 특정 라이브러리의 기능을 사용할 수 있도록 제공하는 API
  • 예: Java의 Math 라이브러리 API

3️⃣ Operating System API

  • 운영체제(OS) 기능을 활용할 수 있도록 제공하는 API
  • 예: Windows API, POSIX API

4. Web API에서 HTTP 요청 방식 (GET vs POST)

Web API는 주로 HTTP 프로토콜을 통해 데이터를 요청하거나 전송하는데,
대표적인 HTTP 요청 방식은 GET과 POST입니다.

요청 방식 설명
GET 데이터를 조회 (읽기)
POST 데이터를 생성 및 전송 (쓰기)
PUT 기존 데이터를 수정
DELETE 데이터를 삭제

📌 GET vs POST 차이점

1. GET 요청

  • 목적: 데이터를 가져올 때(조회) 사용
  • 데이터 전달 방식: URL의 **쿼리 스트링(Query String)**에 포함됨
  • 특징:
    ✔ 브라우저에서 직접 URL 입력하여 요청 가능
    ✔ 요청을 캐시(Cache)할 수 있음
    ✔ 요청 데이터가 URL에 노출되므로 보안에 취약

📌 GET 요청 예시 (브라우저에서 요청 가능)

https://api.example.com/user?id=123

📌 Java 코드 예제

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/user?id=123"))
    .GET()
    .build();

✔ 서버는 id=123에 해당하는 사용자의 정보를 반환함


2. POST 요청

  • 목적: 새로운 데이터를 서버에 전송하고 생성할 때 사용
  • 데이터 전달 방식: 요청의 **본문(Body)**에 포함됨
  • 특징:
    ✔ 데이터를 직접 URL에 포함하지 않음 → 보안성이 높음
    ✔ 요청 본문을 통해 대량의 데이터 전송 가능
    ✔ 요청을 캐시할 수 없음

📌 POST 요청 예시 (JSON 데이터 포함)

POST https://api.example.com/user
{
    "name": "Alice",
    "email": "alice@example.com"
}

📌 Java 코드 예제

String json = """
    {
        "name": "Alice",
        "email": "alice@example.com"
    }
""";

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/user"))
    .POST(HttpRequest.BodyPublishers.ofString(json))
    .header("Content-Type", "application/json")
    .build();

✔ 서버는 새로운 사용자를 데이터베이스에 추가하고, 성공 응답을 반환함


📌 LLM API 요청이 왜 POST 방식인가?

1. LLM API 요청 방식

LLM (Large Language Model) API는 사용자가 질문을 입력하면 AI 모델이 답변을 생성하는 API입니다.
이때 POST 요청을 사용해야 하는 이유는 다음과 같습니다.

📌 LLM API 요청 예시 (POST)

POST https://api.together.xyz/v1/chat/completions
{
    "model": "meta-llama/Llama-3.3-70B-Instruct-Turbo-Free",
    "messages": [{
        "role": "user",
        "content": "What is Java?"
    }],
    "max_tokens": 2048
}

2. LLM API가 POST를 사용하는 이유

이유 설명
① 질문 데이터가 길어질 수 있음 GET은 URL에 데이터를 포함하지만, 긴 질문을 URL에 포함하면 제한이 있음.
② 데이터 보안 GET 요청은 URL에 데이터가 포함되므로 질문 내용이 노출될 수 있음. POST는 본문(Body)에 포함되므로 보안성이 높음.
③ 문맥 유지 필요 AI 모델과의 대화는 이전 메시지를 포함하여 전달해야 함, 따라서 본문(Body)로 여러 개의 메시지를 한 번에 보낼 필요가 있음.
④ API 설계 원칙 POST는 새로운 데이터를 생성하는 역할을 하며, AI 응답은 새로운 데이터를 생성하는 과정이므로 적절함.

3. LLM API 요청이 GET이면 어떤 문제가 발생할까?

📌 GET 요청을 사용할 경우

https://api.together.xyz/v1/chat/completions?model=meta-llama&messages=What is Java?

✔ URL이 너무 길어질 수 있음 (브라우저의 URL 길이 제한 초과 가능)
✔ 질문 내용이 URL에 그대로 노출됨 (보안 위험)
✔ 과거 메시지를 포함하여 요청하는 것이 어려움 (대화 문맥 유지 불가)

📌 POST 요청을 사용할 경우
✔ 본문(Body)에 데이터를 포함하여 URL 제한 없음
✔ 데이터가 노출되지 않아 보안성이 높음
✔ 과거 메시지 포함 가능 → 문맥을 유지한 대화 가능


📌 5. 정리

✅ API란?

✔ API는 프로그램 간의 데이터 교환을 가능하게 하는 인터페이스
✔ Web API는 HTTP 요청을 사용하여 데이터를 요청하거나 전송

✅ HTTP 요청 방식 (GET vs POST)

요청 방식 설명
GET 데이터를 조회할 때 사용 (URL에 데이터 포함)
POST 새로운 데이터를 생성할 때 사용 (HTTP 본문에 데이터 포함)

✅ LLM API는 왜 POST 방식을 사용하는가?

✔ 질문 데이터가 길어질 수 있음 → URL의 제한을 피하기 위해 POST 사용
✔ 보안 문제 → GET 요청이면 질문 내용이 URL에 노출되므로 위험
✔ AI 모델과의 대화는 문맥을 유지해야 함 → 여러 개의 메시지를 본문에 포함 가능해야 함
✔ 새로운 응답을 생성하는 과정이므로 POST가 적절함



📌 멀티스레드를 사용한 실행 흐름

1️⃣ Main Thread 실행

  • ExecutorService를 생성하여 3개의 스레드를 실행할 준비를 함

2️⃣ 3개의 작업이 비동기적으로 실행됨

  • Task 1 (이미지 생성) 시작 → Task 2 (상세 설명 생성) 시작 → Task 3 (인터뷰 팁 생성) 시작
  • 각 작업이 동시에 실행됨 (병렬 실행)

3️⃣ 작업 완료 후 결과를 수집

  • imageFuture.get(), reasoningFuture.get(), reasoningFuture2.get() 호출하여 완료된 결과를 가져옴

4️⃣ Executor 종료

  • 모든 작업이 끝나면 executor.shutdown()을 호출하여 ExecutorService를 종료

📌 왜 처음부터 멀티스레드를 사용하지 않았을까?

1. 단일 스레드의 기본적인 동작

보통 프로그램은 **단일 스레드(Single Thread)**로 동작합니다.
즉, 한 번에 하나의 작업만 실행하고, 작업이 끝날 때까지 다음 작업을 실행하지 않음.

String imageResult = generateImage("Java");
String reasoningResult = generateReasoning("Java 설명");
String reasoningResult2 = generateInterviewTips("Java 면접 팁");

// 실행 흐름:
// 1. 이미지 생성 → 완료될 때까지 대기
// 2. 설명 생성 → 완료될 때까지 대기
// 3. 면접 팁 생성 → 완료될 때까지 대기

✔ 한 작업이 끝날 때까지 기다려야 하므로 실행 속도가 느림.
✔ 하지만 코드가 직관적이고 디버깅이 쉬움.


2. 멀티스레드 도입이 필요한 이유

LLM API를 호출하는 작업은 **네트워크 요청(HTTP 요청)**이 포함됨.
✔ 네트워크 응답 시간이 길어질 수 있음 (수백 ms ~ 수 초)
✔ 단일 스레드에서는 하나의 요청이 끝날 때까지 다음 작업이 시작되지 않음

📌 멀티스레드를 사용하면?

ExecutorService executor = Executors.newFixedThreadPool(3);

Future<String> imageFuture = executor.submit(new LLMTask(ModelCategory.FLUX, "Create an image for Java."));
Future<String> reasoningFuture = executor.submit(new LLMTask(ModelCategory.R1, "Explain Java in detail."));
Future<String> reasoningFuture2 = executor.submit(new LLMTask(ModelCategory.R1, "Give Java job interview tips."));

// 각 작업이 병렬로 실행됨 → 총 실행 시간이 단축됨
String imageResult = imageFuture.get();
String reasoningResult = reasoningFuture.get();
String reasoningResult2 = reasoningFuture2.get();

executor.shutdown();

✔ 모든 요청이 동시에 실행됨 → 실행 속도 향상
✔ Future.get()을 호출하면 해당 작업이 완료될 때까지 대기하지만, 이미 여러 작업이 동시에 실행되고 있으므로 기다리는 시간이 줄어듦


📌 정리

✅ 처음에는 단일 스레드를 사용했음 → 코드가 단순하고 이해하기 쉬움
✅ LLM API 요청은 네트워크 지연이 발생할 수 있음 → 응답 시간이 길어질 가능성이 있음
✅ 멀티스레드를 사용하면 여러 작업을 동시에 실행 가능 → 실행 속도가 빨라짐
✅ ExecutorService를 사용하여 3개의 작업을 동시에 처리함 → 네트워크 요청을 효율적으로 수행

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions