요즘IT
위시켓
최근 검색어
전체 삭제
최근 검색어가 없습니다.

“잠깐... GPT로 이 정도 수준의 서비스를 만들 수 있다고?” ChatGPT나 Claude로 간단한 프로토타입을 만들어보던 순간의 깨달음을 잊을 수 없습니다. 시스템 프롬프트(System Prompt)만 잘 짜면 복잡한 코드 없이도 꽤 그럴듯한 서비스를 만들 수 있겠다는 가능성이 보였죠. 실제로 며칠 동안 시스템 프롬프트를 꼼꼼히 다듬었습니다. 마치 프로그래밍하듯 if-then 규칙을 정의하고, 여기에 도메인 특화된 지식들을 하나씩 추가했습니다. 교보문고에서 찾은 전공 서적의 전문 지식부터 마케팅 베스트셀러의 인사이트까지, 조금씩 시스템 프롬프트에 녹여내며 테스트해 보니 놀라울 정도로 훌륭한 결과가 나왔죠. 처음엔 모든 게 완벽해 보였습니다. AI는 정확히 의도한 대로 동작했고, 마치 잘 짜인 코드처럼 일관된 결과를 보여줬으니까요.

회원가입을 하면 원하는 문장을
저장할 수 있어요!

다음

회원가입을 하면
성장에 도움이 되는 콘텐츠를
스크랩할 수 있어요!

확인

개발

코드처럼 동작하는 ‘시스템 프롬프트’ 가이드

년차,
어떤 스킬
,
어떤 직무
독자들이 봤을까요?
어떤 독자들이 봤는지 궁금하다면?
로그인

“잠깐... GPT로 이 정도 수준의 서비스를 만들 수 있다고?” ChatGPT나 Claude로 간단한 프로토타입을 만들어보던 순간의 깨달음을 잊을 수 없습니다. 시스템 프롬프트(System Prompt)만 잘 짜면 복잡한 코드 없이도 꽤 그럴듯한 서비스를 만들 수 있겠다는 가능성이 보였죠. 실제로 며칠 동안 시스템 프롬프트를 꼼꼼히 다듬었습니다. 마치 프로그래밍하듯 if-then 규칙을 정의하고, 여기에 도메인 특화된 지식들을 하나씩 추가했습니다. 교보문고에서 찾은 전공 서적의 전문 지식부터 마케팅 베스트셀러의 인사이트까지, 조금씩 시스템 프롬프트에 녹여내며 테스트해 보니 놀라울 정도로 훌륭한 결과가 나왔죠. 처음엔 모든 게 완벽해 보였습니다. AI는 정확히 의도한 대로 동작했고, 마치 잘 짜인 코드처럼 일관된 결과를 보여줬으니까요.

 

하지만 대화가 조금만 길어져도 문제가 시작됐습니다. 분명 프롬프트에 “절대로 하지 말 것”이라고 명시한 행동을 AI가 버젓이 하기 시작했고, 더 큰 문제는 이게 왜 발생하는지 디버깅할 방법이 없다는 거였죠. 이는 GPT나 Claude를 활용해 서비스를 개발하는 모든 개발자가 직면하는 가장 큰 난관입니다. 시스템 프롬프트가 복잡해질수록 AI의 행동은 더욱 예측하기 어려워지고, ‘반드시’라고 강조한 규칙조차 종종 무시되기 시작합니다.

 

제가 속한 LLAMI팀에서도 똑같은 문제를 겪었습니다. 하지만 우리는 흥미로운 가능성을 발견했습니다. 그것도 우리가 매일 사용하는 프로그래밍 패러다임에서요. 복잡한 프로그램을 모듈로 나누듯, 시스템 프롬프트도 여러 개의 특화된 에이전트로 분할하면 어떨까? 하고 말이죠. 이번 글에서는 시스템 프롬프트를 여러 AI 에이전트가 분산 처리하는 아키텍처를 소개해 보려고 합니다. 이 접근법으로 우리는 시스템 프롬프트 지시 사항 이행률을 90% 이상으로 끌어올렸고, 진정한 의미의 ‘코드처럼 동작하는’ LLM 서비스를 구현할 수 있었습니다.

 

<출처: 작가>
 

시스템 프롬프트의 진화: 매직 프롬프트에서 행동 지시어로

시스템 프롬프트는 흥미로운 진화 과정을 거쳐왔습니다. 초기의 시스템 프롬프트는 단순했습니다. “차분히 생각하세요”, “단계별로 접근하세요”와 같은 ‘매직 프롬프트’만으로도 LLM의 성능을 크게 향상시킬 수 있었죠. 마치 주니어 개발자에게 “침착하게 생각해 봐.”라고 조언하는 것처럼, 간단한 지시만으로도 놀라운 효과를 볼 수 있었습니다.

 

하지만 GPT-4, Claude와 같은 고성능 LLM이 등장하면서 상황이 달라졌습니다. 이제 시스템 프롬프트는 단순한 역할 부여가 아닌, 전문가 페르소나를 정의하는 도구가 되었습니다. 개발자들은 시스템 프롬프트에 도메인 전문 지식을 녹여냈고, 특정 상황에서 특정한 방식으로 응답하도록 정교한 규칙을 정의했습니다. 더 나아가 시스템 프롬프트는 이제 서비스의 핵심 비즈니스 로직을 담는 ‘컨테이너’가 되었습니다. “너는 마케팅 전문가야”라는 단순한 역할 부여를 넘어, “이런 상황에서는 이렇게 응답하고, 저런 경우에는 저렇게 행동해”라는 구체적인 행동 지시어들의 집합이 된 것이죠.

 

시스템 프롬프트의 딜레마

LLM의 성능이 향상될수록 우리는 흥미로운 딜레마에 직면했습니다. 더 똑똑해진 AI에 더 복잡한 일을 맡기고 싶어졌고, 이는 자연스럽게 시스템 프롬프트의 복잡도 증가로 이어졌습니다. 처음에는 이것이 당연한 과정처럼 보였습니다. AI가 더 똑똑해졌으니, 더 복잡한 지시 사항도 잘 수행할 것이라 기대했으니까요.

 

하지만 현실은 달랐습니다. 새로운 요구사항이 추가될 때마다 시스템 프롬프트는 비대해졌고, 프롬프트가 길어질수록 AI는 중요한 지시 사항조차 놓치기 시작했습니다. 개발자들은 이를 해결하기 위해 ‘절대로’, ‘반드시’ 같은 강력한 제약을 걸기 시작했죠.

 

하지만 이는 또 다른 문제를 낳았습니다. 모든 규칙이 ‘절대로’와 ‘반드시’로 채워지자, 아이러니하게도 AI는 어떤 규칙도 제대로 따르지 않게 되었습니다. 마치 모든 것이 중요하다고 강조하다 보니, 정작 무엇이 진짜 중요한지 알 수 없게 된 것처럼요. 결과적으로 전반적인 지시 사항 이행률은 더욱 저하되었죠.

 

RAG의 한계: 정보 검색만으론 부족하다

첫 번째 해결 시도는 RAG(Retrieval-Augmented Generation)였습니다. “시스템 프롬프트가 너무 길어지니, 필요할 때마다 관련 지시 사항을 검색해서 참조하면 되지 않을까?”라는 발상이었죠. 하지만 이는 근본적인 해결책이 되지 못했습니다. 시스템 프롬프트는 이제 더 이상 단순한 정보의 모음이 아니었기 때문입니다. “고객이 화를 내면 먼저 공감을 표현하고, 해결책을 제시하되, 절대로 변명하지 말 것.”과 같은 행동 지시어들은 단순히 검색하여, 참조할 성질의 것이 아니었습니다.

 

더군다나 이러한 행동 지시어들은 서로 긴밀하게 연결되어 있었습니다. 마치 객체지향 프로그래밍에서 여러 클래스가 상호작용하듯, 시스템 프롬프트의 각 규칙들도 서로 연관되어 있었죠. 이런 복잡한 상호작용을 단순한 정보 검색으로는 해결할 수 없었습니다.

 

모든 코드를 main() 함수에 작성하시겠습니까?

문득 이런 생각이 들었습니다. “우리가 지금 하는 일이 모든 코드를 main() 함수에 작성하는 것과 다를 바 있을까?” 코드를 모두 main() 함수에 작성하는 것은 얼마나 비효율적인 일일까요? 복잡한 로직을 하나의 함수에 욱여넣으면, 아무리 잘 정리해도 결국은 파국을 맞이하게 됩니다. 우리 팀의 시스템 프롬프트도 정확히 같은 상황이었습니다.

 

이때 마이크로소프트의 AutoGen 프로젝트를 보며 흥미로운 가능성을 발견했습니다. LLM이 서로 대화하며 문제를 해결하는 모습은, 마치 함수들이 서로 호출하며 복잡한 로직을 처리하는 것과 비슷해 보였습니다. “복잡한 로직을 여러 함수로 나누듯, 시스템 프롬프트도 여러 LLM에 나누어 처리하면 어떨까?” LLAMI팀은 이 단순한 질문에서 시작해, 시스템 프롬프트의 새로운 가능성을 발견했습니다. 이제 그 구체적인 방법을 살펴보겠습니다.

 

 

멀티 에이전트 시스템의 가능성

마이크로소프트의 AutoGen 프로젝트는 AI 에이전트를 구축하고, 여러 에이전트 간의 협력을 촉진하기 위한 오픈소스 프로그래밍 프레임워크입니다. 이 프레임워크의 가장 큰 특징은 여러 LLM이 서로 대화하며 복잡한 문제를 해결한다는 점입니다. 각 에이전트는 다른 에이전트와 대화하고, 필요한 도구를 활용하며, 자율적 또는 인간과의 협업을 통해 작업을 수행할 수 있습니다.

 

멀티 에이전트 접근법을 연구하면서, 우리 팀은 한가지 가능성을 발견했습니다. 바로 자유로운 전문가들의 회의를 넘어 체계적인 조직 시스템으로 LLM의 통신을 제약하면, 시스템 프롬프트를 분업할 수 있지 않을까란 생각이었죠. 이를 위해 시스템 프롬프트를 여러 개의 명확한 ‘직무’로 나누기 시작했습니다. 각 AI 에이전트는 더 이상 자유로운 전문가가 아닌, 거대한 시스템 프롬프트의 특정 부분을 완벽하게 수행하는 ‘전문 직원’이 되었습니다.

 

<출처: 마이크로소프트>

 

맥락 과부하의 해결

시스템 프롬프트가 복잡해질수록 LLM은 더 많은 맥락을 이해하고 처리해야 합니다. 마치 한 사람에게 너무 많은 업무를 부여했을 때 업무 효율이 떨어지는 것처럼, LLM도 처리해야 할 맥락이 많아질수록 성능이 저하되는 현상을 보였습니다. 우리는 이를 ‘맥락의 과부하’ 현상이라고 부르고 있습니다. 이 문제는 특히 장기적인 대화에서 두드러졌는데요. 대화가 길어질수록 LLM은 초기에 주어진 시스템 프롬프트의 지시 사항을 점차 잊어버리는 경향을 보였고, 이는 서비스의 일관성과 신뢰성을 크게 저해했습니다.

 

프롬프트의 분산 처리는 이 문제에 대한 우리의 해답이었습니다. 각 에이전트가 전체 맥락의 일부만을 담당함으로써, 개별 에이전트의 인지 부하를 크게 줄일 수 있었죠. 이는 마치 복잡한 프로젝트를 여러 팀에 분배하여, 각 팀이 자신의 전문 영역에만 집중할 수 있게 하는 것과 같은 원리였습니다.

 

통제된 협업의 설계

이 시스템의 진정한 혁신은 통제된 협업 구조에 있습니다. 우리는 자유로운 소통보다는 명확한 규칙과 체계가 더 효과적임을 발견했는데요. 각 에이전트는 마치 기업의 부서처럼 명확한 책임과 권한을 가지고, 정해진 프로토콜에 따라 소통합니다.

 

이러한 구조는 놀라운 이점을 가져왔습니다:

  1. 책임소재의 명확화: 각 에이전트의 역할과 책임이 명확히 정의됩니다.
  2. 예측 가능한 동작: 정해진 규칙에 따른 소통으로 시스템의 동작을 정확히 예측할 수 있습니다.
  3. 효과적인 디버깅: 문제 발생 시 원인을 정확히 추적할 수 있습니다.

 

단순한 멀티 에이전트가 아닌, 시스템 프롬프트의 체계적 분업화를 통해 우리는 LLM 시스템의 신뢰성을 한 단계 끌어올릴 수 있었습니다.

 

 

멀티 에이전트 시스템의 구체적인 구현 방법과 아키텍처 설명

앞서 말했듯 우리 팀은 마이크로소프트의 AutoGen에서 제시한 멀티 에이전트 인터페이스에서 많은 영감을 받았습니다. 그러나 중앙 통제식 라우팅이라는 AutoGen의 핵심 설계 철학과는 다른 길을 택했죠. 그래서 시스템의 근간이 되는 두 가지 핵심 컴포넌트인 GroupChatManager와 ConversableAgent를 통해 이 차이점이 어떻게 구현되는지 살펴보겠습니다.

 

시스템의 두 기둥

먼저 GroupChatManager는 대화의 기록 관리자이자 신뢰할 수 있는 메신저입니다. 이 컴포넌트는 모든 대화의 기록을 보관하고, 각 에이전트가 정상적으로 활동하고 있는지 모니터링하며, 메시지가 정확히 전달되도록 보장합니다. 마치 회사의 문서관리 시스템이 모든 업무 기록을 안전하게 보관하는 것과 같습니다.

 

한편, ConversableAgent는 모든 대화 참여자의 기본 설계도와 같습니다. 이 인터페이스는 놀라울 정도로 단순하며, 질문이 왔을 때 어떻게 응답할지만 정의하면 됩니다. 이러한 단순함은 강력한 확장성을 가져옵니다. 심지어 인간의 참여도 이 동일한 인터페이스를 통해 자연스럽게 이루어지는데요. 인간은 시스템 안에서 다른 AI 에이전트들과 동등한 참여자가 되어, 진정한 의미의 human-in-the-loop를 실현합니다.

 

자율성과 책임의 분산

우리의 접근법은 각 에이전트에게 더 많은 자율성을 부여합니다. AutoGen이 GroupChatManager를 통해 모든 소통을 중앙에서 관리하는 방식을 택했다면, LLAMI는 각 에이전트가 자신의 이웃 에이전트들을 직접 알고 소통하는 분산형 구조를 채택했습니다. 이는 마치 회사에서 각 부서가 다른 부서와 직접 협업하는 것과 같은 효율적인 구조를 만들어냅니다.

 

이러한 구조는 시스템의 유연성과 확장성을 크게 향상시켰으며, 각 에이전트의 자율성을 보장하면서도 전체적인 일관성을 유지할 수 있게 했습니다. 이제 이 구조가 실제로 어떻게 구현되는지 살펴볼게요.

 

핵심 컴포넌트의 구현

GroupChatManager

class GroupChatManager {
  protected history: Message[] = [];
  protected agents: Map<string, ConversableAgent> = new Map();

  protected async processMessage(message: Message): Promise<Message> {
    this.history.push(message);

    if (this.shouldEndConversation(message, this.history)) {
      return message;
    }

    const currentAgent = this.agents.get(message.receiver);
    if (!currentAgent) {
      throw new Error(`Agent ${message.receiver} not found`);
    }

    const fullPrompt = await this.contextStrategy(this.history, message);
    const responseMessage = await currentAgent.answer(
      {
        sender: message.sender,
        receiver: currentAgent.name,
        content: fullPrompt,
      },
      this.history,
    );

    return this.processMessage(responseMessage);
  }
}

 

GroupChatManager의 핵심은 ‘processMessage’ 메서드입니다. 우선 메시지를 히스토리에 기록하고, 대화가 종료 조건에 도달했는지 확인합니다. 대화가 계속될 수 있다면, 수신자로 지정된 에이전트를 찾아 메시지를 전달합니다. 여기서 주목할 점은 대화의 다음 단계를 결정하는 것이 GroupChatManager가 아니라는 것입니다. 현재 메시지의 수신자는 이미 메시지에 포함되어 있으며, 이는 이전 에이전트가 결정한 것입니다.

 

ConversableAgent

abstract class ConversableAgent {
  abstract readonly name: string;
  protected manager!: GroupChatManager;

  async answer(message: Message, history: Message[]): Promise<Message> {
    const { content, nextReceiver } = await this.processAnswer(
      message,
      history,
    );
    return {
      sender: this.name,
      receiver: nextReceiver,
      content,
    };
  }

  protected abstract processAnswer(
    message: Message,
    history: Message[],
  ): Promise<{
    content: string;
    nextReceiver: string;
  }>;
}

 

각 에이전트는 메시지에 대한 응답을 생성할 때, 두 가지 핵심적인 결정을 내립니다. 응답 내용(content)과 다음 대화 상대(nextReceiver)입니다. 특히 nextReceiver를 직접 결정한다는 점이 중요합니다. 이는 LLAMI의 분산형 의사결정을 가능하게 하는 핵심 메커니즘입니다.

 

Human-in-the-Loop: 인간과 AI의 자연스러운 협업

인간 역시 LLM과 동일한 인터페이스를 통해 시스템의 자연스러운 일부로 참여합니다. 이는 UserProxy라는 특별한 에이전트와 이와 상호작용하는 UI 컴포넌트를 통해 구현됩니다.

 

UserProxy: 인간 참여자를 위한 인터페이스

export class UserProxy extends ConversableAgent {
  readonly name = "user";
  private resolveInput: ((value: string) => void) | null = null;
  private _isMyTurn: boolean = true;

  async processAnswer(message: Message): Promise<{
    content: string;
    nextReceiver: string;
  }> {
    this.manager.resetTurns();
    this.setTurn(true);
    return new Promise<{
      content: string;
      nextReceiver: string;
    }>((resolve) => {
      this.resolveInput = (input: string) => {
        this.setTurn(false);
        resolve({ content: input, nextReceiver: "dialogue" });
      };
    });
  }

  handleInput(input: string) {
    if (this.resolveInput && this._isMyTurn) {
      this.resolveInput(input);
      this.resolveInput = null;
    }
  }
}

 

UserProxy는 Promise를 사용해 비동기적으로 사용자의 입력을 기다립니다. 다른 에이전트들은 LLM이나 규칙 기반 로직으로 즉시 응답을 생성하지만, UserProxy는 실제 인간의 입력이 들어올 때까지 대기합니다.

 

UI와의 통합

이러한 비동기 처리는 React 컴포넌트와 자연스럽게 연결됩니다.

 

export const ChatInterface = () => {
  const [isUserTurn, setIsUserTurn] = React.useState(true);
  const [input, setInput] = useState("");

  // UserProxy의 턴 변경 감지
  useEffect(() => {
    const unsubscribe = userAgent.onTurnChanged((isMyTurn) => {
      setIsUserTurn(isMyTurn);
    });
    return () => unsubscribe();
  }, []);

  // 사용자 입력 처리
  const handleSubmit = async (e: React.SyntheticEvent) => {
    e.preventDefault();
    if (!input.trim() || !isUserTurn) return;

    const userMessage: Message = {
      sender: "user",
      receiver: "dialogue",
      content: input,
    };

    setInput("");

    if (isFirst.current) {
      isFirst.current = false;
      await groupChatManager.chatStart(userMessage);
    } else {
      userAgent.handleInput(input);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <textarea
        value={input}
        onChange={(e) => setInput(e.target.value)}
        placeholder={
          isUserTurn ? "메시지를 입력하세요..." : "AI가 응답하는 중입니다..."
        }
        disabled={!isUserTurn}
      />
      <Button
        type="submit"
        disabled={!isUserTurn || !input.trim()}
      >
        전송
      </Button>
    </form>
  );
};

 

이 구현의 묘미는 자연스러움에 있습니다. 사용자는 자신의 차례에 입력을 제공하고, 시스템은 마치 다른 AI 에이전트와 대화하듯 이 입력을 처리합니다. UI는 isUserTurn 상태를 통해 현재가 사용자의 차례인지 명확히 보여주며, 사용자의 입력은 handleInput을 통해 UserProxy에 전달됩니다.

 

이러한 방식으로 LLAMI는 인간 참여자가 시스템의 자연스러운 일부가 되게 합니다. 인간은 다른 AI 에이전트들과 동등한 대화 참여자로서, 자신의 차례에 입력을 제공하고 대화에 참여할 수 있죠. 이것이 바로 우리가 추구하는 진정한 의미의 human-in-the-loop입니다.

 

실제 적용 사례와 효과

우리 팀은 멀티 에이전트 시스템의 효과를 검증하기 위해 AI 제목 생성 프로젝트를 진행했습니다. 기존의 단일 프롬프트 방식에서는 500줄이 넘는 방대한 시스템 프롬프트를 하나의 LLM이 처리해야 했지만, 이를 네 개의 직무 에이전트로 분산했습니다.

 

  • 서비스 안내자: 사용자와의 첫 대화를 시작하고 전체 맥락을 파악
  • 창의적 사고 촉진자: 사용자의 의도를 구체화하고 다양한 가능성 탐색
  • 제목 생성기: 효과적인 제목을 생성하고 평가 기준에 따라 검증
  • 사용자 에이전트: 인간 사용자의 입력을 시스템에 자연스럽게 통합

 

각 에이전트는 자신의 직무에만 집중함으로써, 시스템 프롬프트 지시 사항 이행률이 90%를 넘어서는 놀라운 성과를 달성했습니다. 더욱 중요한 것은 이 구조의 지속가능성입니다. 새로운 프롬프팅 규칙이나 LLM의 응답 평가 기준이 필요할 때마다 해당 부분을 담당할 새로운 에이전트를 추가하기만 하면 됩니다.

 

다음은 실제 제목 생성 과정의 예시입니다.

 

현재는 제목 생성기 대신 ‘글쓰기 도구 모음’으로 제공되고 있다. <출처: 작가>

 

결론

LLM의 비약적인 발전은 AI 기반 서비스의 새로운 지평을 열었습니다. 하지만 모델이 똑똑해질수록 시스템 프롬프트는 더욱 복잡해졌고, 결국 그 효과가 점차 감소하는 한계에 봉착했습니다. 결국 이 문제를 해결하기 위해 시스템 프롬프트를 ‘코드처럼’ 다루는 새로운 접근법이 필요했죠.

 

마이크로소프트의 AutoGen에서 영감을 받은 우리의 접근법은, 거대한 시스템 프롬프트를 여러 전문 에이전트가 분담하여 처리하는 것이었습니다. 마치 복잡한 코드를 여러 함수로 나누어 관리하듯이, 시스템 프롬프트도 모듈화하여 관리할 수 있다는 발상이었죠. 초기 실험 결과는 고무적이었습니다. AI 제목 생성기 프로젝트에서 이 접근법은 90%가 넘는 시스템 프롬프트 이행률을 보여주었습니다. 더 주목할 만한 점은 이 구조의 확장성입니다. 새로운 요구사항이 생길 때마다 관련 에이전트를 추가하는 것만으로도 시스템을 발전시킬 수 있었습니다.

 

이는 시스템 프롬프트를 다루는 새로운 패러다임의 시작일 수 있습니다. 마치 소프트웨어 공학의 원칙들이 복잡한 코드를 관리 가능하게 만들었듯이, 이러한 접근법이 LLM 기반 서비스의 신뢰성과 확장성을 한 단계 높여줄 수 있을 것으로 보입니다. 아직 해결해야 할 과제가 많지만, 이 새로운 방향이 LLM 기반 서비스의 미래를 한층 더 밝게 만들어줄 수 있을 겁니다.

 

요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.

좋아요

댓글

공유

공유

댓글 0
스벨트를 전도하고 싶은 웹 개발자
4
명 알림 받는 중

작가 홈

스벨트를 전도하고 싶은 웹 개발자
4
명 알림 받는 중
스벨트전도사로 각종 커뮤니티에서 활동하고 있는 개발자 문대승입니다.

여러 오픈소스들을 만들고 운영하고 있습니다.

- 노션프레소(https://notionpresso.com)
- 노션 페이지를 쉽게 웹 사이트에 임베딩할 수 있는 툴모음(cli, renderer)
- 리액트, 스벨트 모두 지원

- 쓰고이(https://ssgoi.dev)
- 페이지 이동 애니메이션 라이브러리
- 핀터레스트 수준의 트랜지션을 크로스 브라우저 호환

- 플리터(https://flitter.dev)
- svg,canvas 모두 호환하는 랜더링 엔진 프레임워크
- 선언형으로 복잡한 데이터 시각화를 가능하게 합니다.
- flutter-like api로 학습 곡선을 낮췄습니다.
- 디비 다이어그램툴 이지알디(https://easyrd.dev) 를 만드는데 사용했습니다.

오픈소스 기여하고 싶은 분은 연락주세요

좋아요

댓글

스크랩

공유

공유

요즘IT가 PICK한 뉴스레터를 매주 목요일에 만나보세요

요즘IT가 PICK한 뉴스레터를
매주 목요일에 만나보세요

뉴스레터를 구독하려면 동의가 필요합니다.
https://auth.wishket.com/login