<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>거꾸로 바라본 세상</title>
    <link>https://clairdelunes.tistory.com/</link>
    <description>Remembering not to lose preciousness as being deceived by familiarity...</description>
    <language>ko</language>
    <pubDate>Mon, 29 Jun 2026 20:07:20 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>조슈아。</managingEditor>
    <image>
      <title>거꾸로 바라본 세상</title>
      <url>https://t1.daumcdn.net/cfile/tistory/2240A54E56927ACF23</url>
      <link>https://clairdelunes.tistory.com</link>
    </image>
    <item>
      <title>3. Harness Engineering(하네스 엔지니어링) &amp;mdash; AI가 실수를 못하게 하기위해 규칙과 울타리를 설계하는 기술</title>
      <link>https://clairdelunes.tistory.com/172</link>
      <description>&lt;h1&gt;&lt;b&gt;하네스 엔지니어링(Harness Engineering)&lt;/b&gt;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI가 실수를 못하게 하기위해 규칙과 울타리를 설계하는 기술 &amp;mdash; 제어의 기술&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;하네스(Harness)&lt;/b&gt;는 원래 말에 채우는 마구(馬具)에서 유래한 단어로, AI 맥락에서는 AI 모델을 안전하고 제어 가능하게 &amp;ldquo;장착&amp;rdquo;하는 구조적 틀을 의미&lt;/li&gt;
&lt;li&gt;강력한 것을 제어하여 유익하게 활용한다는 의미로 쓰이며, AI에서는 올바른 방향으로 제어하면서 최대한 활용하기 위한 구조를 일컫는 표현&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;[말을 비유로 이해]&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트를 &lt;b&gt;거대한 짐말(draft horse)&lt;/b&gt;이라고 생각&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;에이전틱 엔지니어링 = 말 훈련&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;추론 루프 설계, 멀티 에이전트 조율, 도구 사용법 교육 &amp;rarr; 말 자체를 더 강하게 만드는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;하네스 엔지니어링 = 마구 제작&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가죽 끈, 고삐, 수레를 만드는 것 &amp;rarr; 말이 밭을 갈 수 있도록 방향과 한계를 정해주는 장비&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;말을 아무리 잘 훈련시켜도, 마구 없이는 밭을 갈 수 없음&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;하네스란 AI 에이전트가 가장 안전하고 예측 가능한 방식으로 작동하도록 설계된 제어 구조의 전체&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI의 불확실성(환각, 편향 등)을 억제하고&amp;nbsp;&lt;b&gt;비즈니스 로직 안에서만 작동&lt;/b&gt;하게 만드는 울타리&lt;/li&gt;
&lt;li&gt;가드레일(Guardrails) 설정, 출력 형식의 강제(JSON 등), 에러 핸들링, 그리고 결과물이 정확한지 검증하는&amp;nbsp;&lt;b&gt;Evals(평가)&lt;/b&gt;&amp;nbsp;체계 구축&lt;/li&gt;
&lt;li&gt;아무리 똑똑한 에이전트라도 하네스(안전장치)가 없다면 실제 서비스로 출시하기 어려움&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;AI에게 잘 일할 수 있는 환경을 설계하는 것 - [팀원처럼 위임하는 방식]&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;규칙, 작업구조를 미리 합의&lt;/li&gt;
&lt;li&gt;검증 기준을 사전에 정의&lt;/li&gt;
&lt;li&gt;실행 루프로 반복 개선&lt;/li&gt;
&lt;li&gt;일관된 고품질 결과물&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;하네스의 역할&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하네스는 단순히 AI 에이전트를 동작시키는 데 그치지 않고, 3가지 기능을 통합적으로 담당&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;제어(Control) :&lt;/b&gt; 에이전트가 허용된 범위 밖의 행동을 하지 않도록 제안하는 구조&lt;/li&gt;
&lt;li&gt;&lt;b&gt;감시(Monitoring) :&lt;/b&gt; 에이전트 동작 상태와 출력 결과를 실시간으로 추적 및 기록하는 구조&lt;/li&gt;
&lt;li&gt;&lt;b&gt;개선(Feedback) :&lt;/b&gt; 오류나 이상 동작을 감지하고 다음 동작에 반영하는 피드백 루프&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;하네스 엔지니어링의 4가지 구성 요소&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AI - 새로 들어온 팀원&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;나는 바쁜 팀장이에요. AI한테 일을 위임하고 싶어요.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 규칙, 헌법, 가드레일(Rule, Constitution, Guardrails) -- 의도를 잘 전달하기 위해 규칙을 적어놓은 문서(소통)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Rule, Constitution] &amp;mdash; 우리팀은 이렇게 일해&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;에이전트가 적시에 적절한 정보(아키텍처 사양, 스타일 가이드, 프로젝트 규칙 등)를 갖도록 보장하는 것으로, 정적 파일(CLAUDE.md,&amp;nbsp;AGENTS.md)을 활용하여 에이전트에게 필요한 컨텍스트를 구조화하여 주입&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;[예시] &lt;a href=&quot;http://claude.md/&quot;&gt;CLAUDE.md&lt;/a&gt;에 &quot;새로운 라이브러리를 도입하지 마세요. DB 쿼리는 반드시 ORM을 통해서만 해주세요&amp;rdquo;라고 작성하면, AI 에이전트는 이 규칙을&amp;nbsp;자신의 행동 제약으로 인식. 매번 프롬프트에 반복할 필요가 없다.&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;[Guardrails] &amp;mdash; AI가 입력을 받거나 답변을 내보내기 직전에 필터링하는 단계&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Input Guardrail:&amp;nbsp;유해한 질문, 개인정보 유출 시도(Prompt Injection), 범위를 벗어난 요청을 차단&lt;/li&gt;
&lt;li&gt;Output Guardrail:&amp;nbsp;모델이 욕설, 편향된 발언, 혹은 기업 비밀을 답변에 포함하는지 실시간으로 감시하고 차단&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컨텍스트 엔지니어링 (Context Engineering / Structured Context)을 작성&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;에이전트에게 필요한 정보를 적시에 제공하는 기술. 단, 모든 지침을 하나의 파일에 몰아넣는 것은 지양&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;거대한&amp;nbsp;AGENTS.md&amp;nbsp;피하기:&lt;/b&gt;&amp;nbsp;하나의 거대한 지침 파일은 에이전트에게 혼란을 주고, 금방 유지보수가 안 되는 '낡은 규칙들의 무덤'이 되버림&lt;/li&gt;
&lt;li&gt;&lt;b&gt;점진적 정보 제공 (Progressive Disclosure):&lt;/b&gt;&amp;nbsp;메인 지침 파일은 100줄 이내의 '&lt;b&gt;목차&lt;/b&gt;' 역할만 하도록 유지하고, 상세한 내용은&amp;nbsp;docs/&amp;nbsp;하위 디렉터리에 구조화하여 에이전트가 필요할 때 깊이 탐색하게 만든다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단일 진실 공급원 (SSOT):&lt;/b&gt;&amp;nbsp;Slack 스레드나 개발자의 머릿속에 있는 지식은 에이전트에게 존재하지 않는 것과 같으므로, 모든 계획과 설계 내역은 리포지터리 내부에 아티팩트(Artifact)로 저장해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 아키텍처 제약 조건 및 스캐폴딩(Architectural Constraints &amp;amp; Scaffolding) &amp;mdash;&lt;/b&gt; &lt;b&gt;어떤 정보를 참조하게 할 것인가(지식)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;규칙은 문서로 남기는 것이 아니라,&amp;nbsp;&lt;b&gt;코드로 강제&lt;/b&gt;해야 한다. 에이전트의 자유도를 제한할수록 오히려 올바른 결과물에 더 빨리 도달한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;[작업 구조(Task Structure)] - 무엇을 어떻게 만드는지 정의하는 작업 지시서&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이건 꼭 지켜야 해(Linter)&lt;/li&gt;
&lt;li&gt;이건 하면 안돼(제약)&lt;/li&gt;
&lt;li&gt;수정 지침 주입&lt;b&gt;:&lt;/b&gt; 린터 에러 메시지에 '해결 방법'을 포함시켜 작성. 에러 발생 시 에이전트가 메시지를 읽고 즉시 자체 수정할 수 있는 피드백 루프를 형성&lt;/li&gt;
&lt;li&gt;AI에게 &quot;좋은 코드를 짜라&quot;고 말하는 대신, 구조적으로 실수할 수 없는 환경을 만든다.&lt;/li&gt;
&lt;li&gt;화이트리스트 방식을 통해 필요한 최소한의 도구 권한만 부여하거나, 작업 공간을 완전히 격리하여 외부 영향을 차단&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[출력 검증 및 구조화 (Output Parsing &amp;amp; Validation)]&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI의 답변이 프로그램이 이해할 수 있는 형태인지 확인하고 강제&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Schema Enforcement:&lt;/b&gt;&amp;nbsp;답변이 반드시 정해진 JSON 형식을 따르도록 정의. 형식이 틀리면 에이전트가 스스로 수정하게 하거나 에러 처리
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) 반드시&amp;nbsp;{&quot;status&quot;: &quot;success&quot;, &quot;data&quot;: ...}&amp;nbsp;형식으로 답해줘. 아니면 에러야.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Hallucination Check:&lt;/b&gt;&amp;nbsp;답변 내용이 제공된 컨텍스트(Context)에 실제로 존재하는 내용인지 사실 관계를 교차 검증
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI가 &quot;A는 B다&quot;라고 답하면, 시스템이 컨텍스트 문서에서 &quot;A&quot;와 &quot;B&quot;의 관계를 재검색하여 일치 여부 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[CI/CD Gate]&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드 린터, 구조 테스트, pre-commit hook 등을 통해 규칙을&amp;nbsp;&lt;b&gt;시스템이 자동으로 강제&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;린터(Linter)&lt;/b&gt;&amp;nbsp;&amp;mdash; 코드가 규칙을 어기면 자동으로 에러를 띄움&lt;/li&gt;
&lt;li&gt;&lt;b&gt;구조 테스트&lt;/b&gt;&amp;nbsp;&amp;mdash; 의존성 규칙을 테스트로 강제&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pre-commit Hook&lt;/b&gt;&amp;nbsp;&amp;mdash; 코드를 커밋하기 전에 자동으로 검사&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;CI가 실패하면 에이전트가 스스로 수정하고 사람이 개입하지 않음&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 검증 및 피드백 루프 (Verification &amp;amp; Feedback Loops / Hooks) &amp;mdash; 어떤 구조와 제약 속에서 움직이게 할 것인가(규칙)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트가 자신이 작성한 코드를 스스로 평가하게 하면 편향이 발생하므로, AI가 생성한 결과물을 코드로 자동 검증하는 장치이다. 오류가 발생하면 3단계 검사(오류 식별 -&amp;gt; 관련 테스트 실행 -&amp;gt; 자동 수정)를 수행하고, 성공할 때까지 반복하거나 사람에게 보고&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;[검증(Validation)]&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결과물이 괜찮은지 기준을 미리 정의해두는 것&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이런 결과물이 나와야 해&lt;/li&gt;
&lt;li&gt;작업 지시서와 검증 기준 합의&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[평가 체계 (Evaluations, Evals)]&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;이 프롬프트가 이전보다 나아졌는가?&quot;라는 질문에 데이터로 답하는 과정&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;LLM-as-a-Judge:&lt;/b&gt;&amp;nbsp;더 똑똑한 모델(예: GPT-4o)을 심판으로 세워, 서비스 모델의 답변 품질을 점수로 매긴다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Regression Testing:&lt;/b&gt;&amp;nbsp;프롬프트를 수정했을 때, 기존에 잘 되던 답변들이 망가지지 않았는지 수백 개의 테스트 케이스를 돌려 확인&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Ground Truth:&lt;/b&gt;&amp;nbsp;정답셋을 만들어 놓고 AI의 답변과 유사도를 비교&lt;/li&gt;
&lt;li&gt;&lt;b&gt;회의적인(Skeptical) 평가자 구성:&lt;/b&gt;&amp;nbsp;코드를 생성한 에이전트와 독립된 평가용 인스턴스를 두어, &quot;이 PR의 문제점만 찾아라&quot;와 같은 회의적인 프롬프트로 리뷰를 요청&lt;/li&gt;
&lt;li&gt;&lt;b&gt;무한 루프 방지 및 해결:&lt;/b&gt;&amp;nbsp;에이전트가 리뷰 코멘트를 반영하고, 수정하고, 다시 평가받는 과정을 루프(Ralph Wiggum Loop)로 구축하여 사람의 개입 없이 기준을 통과할 때까지 반복&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[명시적 도구 경계]&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI 에이전트가&amp;nbsp;어떤 도구를 쓸 수 있고, 어디까지 접근할 수 있는지를 명확하게 제한&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일 시스템:&amp;nbsp;src/&amp;nbsp;읽기&amp;middot;쓰기 가능,&amp;nbsp;config/&amp;nbsp;읽기만 가능&lt;/li&gt;
&lt;li&gt;API: 내부 API 호출 가능, 외부 서비스 호출 불가&lt;/li&gt;
&lt;li&gt;데이터베이스:&amp;nbsp;SELECT&amp;nbsp;가능,&amp;nbsp;DROP TABLE&amp;nbsp;절대 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;프롬프트는&amp;nbsp;부탁이고, 도구 경계는&amp;nbsp;물리적 차단&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 엔트로피 관리 및 가비지 컬렉션 (Entropy Management / Garbage Collection) &amp;mdash; 결과물을 어떻게 신뢰할 것인가(품질)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 작업을 수행하면서 발생하는 기술 부채나 불필요한 데이터를 정리하는 과정입니다. 이전 단계에서 쌓인 오정보나 중복된 코드를 에이전트가 직접 정리하여 효율성을 유지&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;[관측성 및 로깅 (Observability &amp;amp; Monitoring)]&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 서비스 중 AI가 어떻게 작동하는지 추적하는 'CCTV'와 같다.&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Traceability:&amp;nbsp;에이전트가 어떤 도구를 써서 어떤 답변을 냈는지 전체 경로를 추적&lt;/li&gt;
&lt;li&gt;Token &amp;amp; Cost Control:&amp;nbsp;예상치 못한 무한 루프나 과도한 토큰 사용으로 인한 '비용 폭탄'을 방지하기 위한 한계선(Threshold)을 설정&lt;/li&gt;
&lt;li&gt;Feedback Loop:&amp;nbsp;사용자의 '좋아요/싫어요' 데이터를 수집하여 다시 프롬프트나 컨텍스트 개선에 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[지속적 피드백 루프]&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI가 만든 코드를 주기적으로 점검하고 품질이 떨어지는 부분을 자동으로 감지 및 정리&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행루프(Agent Loop)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수정 &amp;rarr; 검증 &amp;rarr; 수정 &amp;rarr; 확인 을 만족할 때까지 반복&lt;/li&gt;
&lt;li&gt;workflow 반복&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;코딩 규칙 위반 자동 감지&lt;/li&gt;
&lt;li&gt;중복 코드 발견 및 리팩토링 PR 자동 생성&lt;/li&gt;
&lt;li&gt;사용하지 않는 코드 자동 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;에이전트가 실수할 때마다, 그 실수는&amp;nbsp;새로운 규칙이 된다. 린터 규칙이 추가되고, 테스트가 추가되고, 제약이 추가되고,&amp;nbsp;마구가 점점 더 정교해짐&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;에이전트의 위임 범위&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제대로 구축된 하네스 환경에서 에이전트는 단순한 코드 스니펫을 넘어 다음과 같은 광범위한 작업을 수행할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제품 코드 및 테스트 작성&lt;/li&gt;
&lt;li&gt;CI/CD 파이프라인 구성 및 릴리스 툴링&lt;/li&gt;
&lt;li&gt;내부 개발자 도구 및 리포지터리 관리 스크립트 작성&lt;/li&gt;
&lt;li&gt;문서화 및 설계 내역 정리&lt;/li&gt;
&lt;li&gt;PR 리뷰 코멘트 작성 및 응답&lt;/li&gt;
&lt;li&gt;생산 대시보드 및 관측성(Observability) 정의 파일 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4대 축의 상호작용 (The Synergy)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하네스 엔지니어링이 완성되면 비로소 다른 축들이 빛을 발한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Prompt(프롬프트) + Harness(하네스):&lt;/b&gt;&amp;nbsp;아무리 프롬프트를 잘 짜도 AI는 1%의 확률로 헛소리를 하는데, 하네스는 그 1%를 잡아낸다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Context(컨텍스트) + Harness(하네스):&lt;/b&gt;&amp;nbsp;외부 지식을 가져올 때(RAG), 하네스는 그 지식이 질문과 관련이 있는지, 혹은 오염된 데이터인지 검사한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Agentic(에이전틱) + Harness(하네스):&lt;/b&gt;&amp;nbsp;자율적으로 행동하는 에이전트에게 &quot;최대 5단계까지만 생각하라&quot;거나 &quot;이 API는 호출하지 마라&quot;는 물리적 제약(Constraints)을 부여한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;793&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWA18u/dJMcacin25W/1wO5x4CkJr4j4vIDJ1Z2Tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWA18u/dJMcacin25W/1wO5x4CkJr4j4vIDJ1Z2Tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWA18u/dJMcacin25W/1wO5x4CkJr4j4vIDJ1Z2Tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWA18u%2FdJMcacin25W%2F1wO5x4CkJr4j4vIDJ1Z2Tk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;666&quot; height=&quot;793&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;793&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;개발자 역할의 진화&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔지니어링역할비유&lt;/p&gt;
&lt;table id=&quot;33b48ac1-2f48-80d4-b6ad-c2eb91435286&quot; style=&quot;border-collapse: collapse; width: 100%; height: 74px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;엔지니어링&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;역할&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;비유&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;33b48ac1-2f48-80e1-a039-f52fa290155d&quot; style=&quot;height: 19px;&quot;&gt;
&lt;td id=&quot;imZk&quot; style=&quot;height: 19px;&quot;&gt;Prompt&lt;/td&gt;
&lt;td id=&quot;nTgo&quot; style=&quot;height: 19px;&quot;&gt;프롬프트 엔지니어&lt;/td&gt;
&lt;td id=&quot;&amp;gt;=:F&quot; style=&quot;height: 19px;&quot;&gt;AI에게 의도를 정확히 전달하는 사람&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;33b48ac1-2f48-80a8-8cbf-f3dda6b552a7&quot; style=&quot;height: 19px;&quot;&gt;
&lt;td id=&quot;imZk&quot; style=&quot;height: 19px;&quot;&gt;Context&lt;/td&gt;
&lt;td id=&quot;nTgo&quot; style=&quot;height: 19px;&quot;&gt;컨텍스트 아키텍트&lt;/td&gt;
&lt;td id=&quot;&amp;gt;=:F&quot; style=&quot;height: 19px;&quot;&gt;AI가 참조할 지식 체계를 설계하는 사람&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;33b48ac1-2f48-806e-a893-e3b297b1301f&quot; style=&quot;height: 19px;&quot;&gt;
&lt;td id=&quot;imZk&quot; style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Harness&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;nTgo&quot; style=&quot;height: 19px;&quot;&gt;&lt;b&gt;플랫폼 엔지니어&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;&amp;gt;=:F&quot; style=&quot;height: 19px;&quot;&gt;&lt;b&gt;AI가 실수할 수 없는 환경을 만드는 사람&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1&gt;하네스 엔지니어링의 두 단계의 역할&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Repository 하네스&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리회사는 이렇다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI가 이 저장소 전체에서 어떻게 일할지&lt;/li&gt;
&lt;li&gt;문서 구조, 팀 규칙, 코드 린터&lt;/li&gt;
&lt;li&gt;아키텍처 제약 같은 공통환경&lt;/li&gt;
&lt;li&gt;모든 프로젝트에 적용되는 기반&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Application 하네스&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로젝트는 이렇다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트 별로 만들고 싶은 앱, 서비스&lt;/li&gt;
&lt;li&gt;어떤 기능과 경험을 줘야 하는지&lt;/li&gt;
&lt;li&gt;AI가 이 앱을 위해 어떻게 판단할지&lt;/li&gt;
&lt;li&gt;해당 프로젝트에만 적용되는 규칙&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;OpenAI에서 5개월 간 노코드로 하네스 엔지니어링을 수행&quot; href=&quot;https://openai.com/ko-KR/index/harness-engineering/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://openai.com/ko-KR/index/harness-engineering/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 구조로 시작&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;AGENTS.md
ARCHITECTURE.md
docs/
├── design-docs/
│   ├── index.md
│   ├── core-beliefs.md
│   └── ...
├── exec-plans/
│   ├── active/
│   ├── completed/
│   └── tech-debt-tracker.md
├── generated/
│   └── db-schema.md
├── product-specs/
│   ├── index.md
│   ├── new-user-onboarding.md
│   └── ...
├── references/
│   ├── design-system-reference-llms.txt
│   ├── nixpacks-llms.txt
│   ├── uv-llms.txt
│   └── ...
├── DESIGN.md
├── FRONTEND.md
├── PLANS.md
├── PRODUCT_SENSE.md
├── QUALITY_SCORE.md
├── RELIABILITY.md
└── SECURITY.md
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;구조분석&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일/폴더 역할 작성 주체&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AGENTS.md&lt;/td&gt;
&lt;td&gt;에이전트 행동 규칙&lt;/td&gt;
&lt;td&gt;인간 (한 번)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ARCHITECTURE.md&lt;/td&gt;
&lt;td&gt;시스템 전체 지도&lt;/td&gt;
&lt;td&gt;인간 (한 번 + 업데이트)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;design-docs/&lt;/td&gt;
&lt;td&gt;설계 원칙 및 결정&lt;/td&gt;
&lt;td&gt;인간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;exec-plans/active/&lt;/td&gt;
&lt;td&gt;현재 작업 지시서&lt;/td&gt;
&lt;td&gt;인간이 작성 &amp;rarr; 에이전트가 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;product-specs/&lt;/td&gt;
&lt;td&gt;기능 명세&lt;/td&gt;
&lt;td&gt;인간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;references/&lt;/td&gt;
&lt;td&gt;압축된 라이브러리 문서&lt;/td&gt;
&lt;td&gt;인간 (or 에이전트가 생성)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DESIGN.md&lt;/td&gt;
&lt;td&gt;API 포맷&amp;middot;네이밍 등 내부 설계 기준&lt;/td&gt;
&lt;td&gt;인간 (한 번 + 협의 시 업데이트)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FRONTEND.md&lt;/td&gt;
&lt;td&gt;클라이언트 연동 계약&amp;middot;에러코드 정의&lt;/td&gt;
&lt;td&gt;인간 (클라이언트팀과 협의)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QUALITY_SCORE.md&lt;/td&gt;
&lt;td&gt;코드 품질 자체 평가 체크리스트&lt;/td&gt;
&lt;td&gt;인간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RELIABILITY.md&lt;/td&gt;
&lt;td&gt;장애 대응&amp;middot;타임아웃&amp;middot;재시도 기준&lt;/td&gt;
&lt;td&gt;인간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SECURITY.md&lt;/td&gt;
&lt;td&gt;보안 체크리스트&amp;middot;금지 패턴&lt;/td&gt;
&lt;td&gt;인간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;docs/generated/&lt;/td&gt;
&lt;td&gt;에이전트가 생성한 산출물&lt;/td&gt;
&lt;td&gt;&lt;b&gt;에이전트&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;에이전트 제어 레이어&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AGENTS.md&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트가 어떻게 행동해야 하는지 정의하는 메타 지시서 [&lt;b&gt;행동 헌법]&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- 너는 어떤 역할이야
- 코드 작성할 때 이 규칙을 따라
- 모르면 이렇게 해
- 절대 하면 안 되는 것
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람으로 치면 &quot;신입 개발자 온보딩 문서&quot;인데, 읽는 대상이 AI 이기 때문에 모호한 표현 없이 명확하게 작성해야 한다. Claude Code 기준으로는 이 파일을 프로젝트 루트에 두면 매 세션마다 자동으로 읽음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARCHITECTURE.md&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 전체 구조를 에이전트가 참조하는 지도&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- 서비스 구성 (모듈, 레이어)
- 주요 데이터 흐름
- 외부 의존성
- 레이어 간 책임 경계
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트가 새 기능을 추가할 때 &quot;이게 어디에 들어가야 하지?&quot;를 판단하는 근거. 이게 없으면 엉뚱한 레이어에 코드를 추가하게됨&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;의사결정 문서 레이어&lt;/b&gt; (docs/)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;design-docs/&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술적 의사결정 기록. ADR(Architecture Decision Record)과 비슷&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;index.md &amp;mdash; 전체 설계 문서 목록&lt;/li&gt;
&lt;li&gt;core-beliefs.md &amp;mdash; &quot;우리는 이런 이유로 이렇게 설계한다&quot;는 원칙&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# core-beliefs.md
- DB 트랜잭션 경계는 Service 레이어에서만 관리한다
- 외부 API 호출은 반드시 별도 Client 클래스로 분리한다
- 도메인 객체는 인프라 의존성을 가지지 않는다
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;exec-plans/&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 계획 관리.&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;exec-plans/
├── active/        &amp;larr; 지금 진행 중인 작업
├── completed/     &amp;larr; 완료된 작업 (히스토리)
└── tech-debt-tracker.md  &amp;larr; 기술 부채 목록
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;active/ 안에 파일 하나가 하나의 작업 단위&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;# feat/sensor-api.md

## 목표
쿠폰 API 구현

## 범위
- [ ] CouponController
- [ ] CouponService
- [ ] CouponRepository

## 제약
- 쿠폰 중복 등록은 안됨
- 쿠폰은 최대 2개까지만 가능

## 완료 기준
- 단위 테스트 커버리지 80% 이상
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;product-specs/&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능 명세서 (AI 에이전트가 &quot;무엇을 만들지&quot; 참조)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;index.md &amp;mdash; 전체 기능 목록 및 상태&lt;/li&gt;
&lt;li&gt;new-user-onboarding.md &amp;mdash; 기능별 상세 명세&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;## 사용자 스토리
신규 가입자가 앱 첫 실행 시 3단계 온보딩을 완료한다

## 화면 흐름
1. 환영 화면
2. 관심사 선택
3. 알림 동의

## API 요구사항
- POST /onboarding/complete
- 완료 시 웰컴 쿠폰 발급 (CouponService 연동)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;references/&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 라이브러리 LLM 최적화 문서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이브러리 공식 문서를 그대로 넣으면 토큰 낭비가 심해서, &lt;b&gt;AI가 읽기 좋게 압축한 버전&lt;/b&gt;을 따로 만들어둔 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[예: Spring Data JPA 문서]&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# Spring Data JPA - LLM Reference
## Repository 정의
interface UserRepository extends JpaRepository&amp;lt;User, Long&amp;gt; {}

## 쿼리 메서드
findByEmailAndStatus(String email, Status status)
&amp;rarr; WHERE email = ? AND status = ?

## 주의사항
- N+1 문제: @EntityGraph 또는 fetch join 사용
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;품질 기준 레이어&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QUALITY_SCORE.md &amp;mdash; 에이전트가 코드를 생성할 때 지켜야 할 기준&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;## 체크리스트
- [ ] 메서드 단위 테스트 존재
- [ ] 예외 처리 명시
- [ ] 로깅 포함
- [ ] 매직 넘버 없음
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RELIABILITY.md &amp;mdash; 장애 대응 기준&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- 외부 API 호출은 timeout 설정 필수
- 재시도 로직은 exponential backoff 적용
- 서킷브레이커 적용 대상 목록
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SECURITY.md &amp;mdash; 보안 체크리스트&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- SQL 인젝션 방어 (파라미터 바인딩 필수)
- 민감 정보 로그 출력 금지
- 인증 없는 엔드포인트 목록 명시
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DESIGN.md &amp;mdash; 디자인 시스템 제약: &lt;b&gt;시스템의 미적/구조적 일관성 기준&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드가 있으면 UI 디자인 시스템이 들어가지만, 백엔드 전용 프로젝트라면 API 구조 포멧 정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트가 새 API를 만들 때마다 이 파일을 참조해서 포맷을 통일시킴. 이게 없으면 어떤 API는 data, 어떤 건 result, 어떤 건 response로 제각각 만들게됨&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;**# DESIGN.md

## API 응답 포맷
모든 응답은 아래 구조를 따른다

{
  &quot;success&quot;: true,
  &quot;data&quot;: {},
  &quot;error&quot;: null
}

## 에러 응답 구조
{
  &quot;success&quot;: false,
  &quot;data&quot;: null,
  &quot;error&quot;: {
    &quot;code&quot;: &quot;USER_NOT_FOUND&quot;,
    &quot;message&quot;: &quot;사용자를 찾을 수 없습니다&quot;
  }
}

## 네이밍 컨벤션
- URL: kebab-case  &amp;rarr; /car-wash-missions
- JSON 필드: camelCase &amp;rarr; userId, createdAt
- 에러 코드: UPPER_SNAKE_CASE &amp;rarr; INVALID_TOKEN

## 페이지네이션 구조
{
  &quot;content&quot;: [],
  &quot;page&quot;: 0,
  &quot;size&quot;: 20,
  &quot;totalElements&quot;: 100
}**
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FRONTEND.md &amp;mdash; 디자인 시스템 제약 : 외부(클라이언트)와의 연동&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트팀이 없어도, 앱 클라이언트나 외부 연동이 있다면 필요&lt;/p&gt;
&lt;pre class=&quot;gherkin&quot;&gt;&lt;code&gt;## 클라이언트 연동 원칙
- 필드 삭제 금지, 추가만 허용 (하위 호환성 유지)
- Null 필드는 응답에서 제외하지 않고 명시적으로 null 반환
- 날짜 포맷: ISO 8601 (2024-01-15T09:00:00Z)

## 인증
- Authorization: Bearer {token} 헤더 방식
- 토큰 만료 시 401 응답, refresh는 클라이언트 책임

## 에러 코드 목록
| 코드 | 의미 | HTTP Status |
|------|------|-------------|
| USER_NOT_FOUND | 사용자 없음 | 404 |
| INVALID_TOKEN | 토큰 오류 | 401 |
| MISSION_EXPIRED | 미션 만료 | 410 |

## 비동기 작업 처리
- 즉시 응답: 202 Accepted + jobId 반환
- 상태 조회: GET /jobs/{jobId}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>AI/엔지니어링</category>
      <category>AI</category>
      <category>AI Engineering</category>
      <category>ai엔지니어링</category>
      <category>하네스</category>
      <category>하네스엔지니어링</category>
      <author>조슈아。</author>
      <guid isPermaLink="true">https://clairdelunes.tistory.com/172</guid>
      <comments>https://clairdelunes.tistory.com/172#entry172comment</comments>
      <pubDate>Wed, 8 Apr 2026 15:32:24 +0900</pubDate>
    </item>
    <item>
      <title>2. Context Engineering(컨텍스트 엔지니어링) &amp;mdash; AI에게 필요한 정보를 적절하게 제공하는 기술</title>
      <link>https://clairdelunes.tistory.com/171</link>
      <description>&lt;h1&gt;2. 컨텍스트 엔지니어링(Context Engineering)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;AI에게 필요한 정보를 적절하게 제공하는 기술 &amp;mdash; 정보의 기술&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI에게 &lt;b&gt;'무엇을(What)'&lt;/b&gt; 해야 할지 알려주는 것을 넘어, 그 일을 수행하는 데 필요한 **'지식과 상황 정보(Context)'**를 가장 효과적으로 주입하는 기술&lt;/li&gt;
&lt;li&gt;모델이 '모르는 것'을 알게 하거나, '특정 정보'에 기반해 답변하게 만드는 환경 조성&lt;/li&gt;
&lt;li&gt;**RAG(검색 증강 생성)**가 대표적. 벡터 데이터베이스 구축, 데이터 전처리, 사용자 히스토리 관리 등을 통해 AI에게 &amp;rdquo;지식의 맥락&amp;rdquo;을 제공&lt;/li&gt;
&lt;li&gt;프롬프트가 '두뇌'라면, 컨텍스트는 그 두뇌가 참고할 '교과서'&lt;/li&gt;
&lt;li&gt;단순히 텍스트를 많이 집어넣는 것이 아니라, 모델의 제한된 기억력(Context Window) 내에 가장 가치 있는 정보를 정교하게 배치하는 것이 핵심&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주요 기법들 (Techniques)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 검색 증강 생성 (RAG, Retrieval-Augmented Generation)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 대표적인 기법으로, 모델의 파라미터 외부에 있는 지식을 실시간으로 찾아내어 전달&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Semantic Search (의미론적 검색):&lt;/b&gt;&amp;nbsp;질문의 단어가 정확히 일치하지 않아도, 의미적으로 유사한 문서를 벡터(Vector) 공간에서 찾아낸다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;뜨겁다&quot;라는 단어가 없어도 의미가 유사한 &quot;발열 현상&quot;, &quot;온도 상승&quot;, &quot;냉각 팬 오작동&quot; 관련 매뉴얼 데이터를 찾아 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Hybrid Search:&lt;/b&gt;&amp;nbsp;키워드 기반의 정확도(BM25)와 의미 기반의 유사도(Dense Vector)를 결합하여 검색 품질을 높인다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'M3'라는 정확한 키워드(Keyword Search)와 '노트북 성능'이라는 의미(Semantic Search)를 동시에 검색하여 가장 정확한 제품 정보를 추출&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Re-ranking (재정렬):&lt;/b&gt;&amp;nbsp;검색된 수십 개의 문서 중, 질문과 가장 연관성이 높은 순서대로 다시 정렬하여 최상단 정보만 AI에게 전달.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI가 읽기 전, 다시 한번 고성능 알고리즘으로 계산하여 사용자의 질문과 가장 밀접한 상위 3개의 문서만 골라 프롬프트에 넣어줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 데이터 전처리 및 청킹 (Chunking)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방대한 데이터를 AI가 읽기 좋은 크기로 쪼개는 기술&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Fixed-size Chunking:&lt;/b&gt;&amp;nbsp;고정된 길자로 자르는 기초적인 방식&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Recursive Character Chunking:&lt;/b&gt;&amp;nbsp;문단, 문장 마침표 등을 인식하여 의미가 끊기지 않게 유동적으로 자름
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;500자 단위로 자르되, 문장의 마침표(.)나 줄바꿈(\\n)을 기준으로 잘라서 &quot;사과는 맛있다. 그래서 나는&quot; 처럼 의미가 중간에 끊기지 않게 조절&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Semantic Chunking:&lt;/b&gt;&amp;nbsp;텍스트의 주제가 바뀌는 지점을 감지하여 의미 단위로 데이터를 분절
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;회사 소개&quot; 섹션이 끝나고 &quot;연봉 복지&quot; 섹션으로 넘어가는 지점을 AI가 감지하여 주제별로 데이터를 분절&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Overlapping:&lt;/b&gt;&amp;nbsp;앞뒤 청크의 내용을 일부 겹치게 하여 맥락이 단절되는 것을 방지
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;A는 B의 원인이다.&quot;라는 문장이 잘려도 앞뒤 맥락을 통해 인과관계를 유지함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 컨텍스트 윈도우 관리 (Window Management)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델이 한 번에 처리할 수 있는 정보량은 제한되어 있으므로, 이를 효율적으로 압축하고 선별&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Context Truncation &amp;amp; Sliding:&lt;/b&gt;&amp;nbsp;대화가 길어질 때 오래된 내용을 삭제하거나, 최근 맥락만 유지하며 이동하는 방식&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Summarization (요약):&lt;/b&gt;&amp;nbsp;이전 대화 내용이나 긴 문서를 요약본으로 변환하여 컨텍스트 용량을 확보
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상황: 고객과 50번의 대화를 나눈 상태&lt;/li&gt;
&lt;li&gt;작동: 이전의 40번 대화를 &quot;고객은 환불 규정에 대해 문의 중임&quot;이라는 한 줄 요약으로 압축하여 입력창 공간을 확보하고 최신 대화에 집중&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Information Compression:&lt;/b&gt;&amp;nbsp;불필요한 수식어나 조사를 제거하여 정보 밀도를 높이는 기법
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;안녕하세용 고객님! 무엇을 도와드릴까용?^^&quot; &amp;rarr; &quot;문의 사항 확인 필요&quot;와 같이 핵심 정보 위주로 토큰(Token)을 절약&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. GraphRAG (지식 그래프 결합)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 텍스트 파편화를 넘어, 정보 간의 **'관계'**를 구조화하여 제공하는 고도화된 방식&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Entity Extraction:&lt;/b&gt;&amp;nbsp;문서 내 핵심 개체(인물, 장소, 개념 등)를 추출&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Relationship Mapping:&lt;/b&gt;&amp;nbsp;개체 간의 연결 고리를 그래프 형태로 저장하여, 단순 검색으로는 찾기 힘든 복합적인 추론(예: &quot;A와 B의 공통점은?&quot;)을 가능하게 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;데이터:&lt;/b&gt;&amp;nbsp;[스티브 잡스] - (설립) - [애플], [애플] - (제조) - [아이폰]&lt;/li&gt;
&lt;li&gt;&lt;b&gt;효과:&lt;/b&gt;&amp;nbsp;사용자가 &quot;잡스가 만든 휴대폰의 특징은?&quot;이라고 물으면, 여러 문서를 뒤지지 않고도 그래프를 따라 '잡스 &amp;rarr; 애플 &amp;rarr; 아이폰'의 관계를 즉시 파악해 답변&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. Few-shot &amp;amp; In-context Learning&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프롬프트 안에 예시 데이터(Example)를 직접 포함시켜 모델의 적응력을 증가시킨다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Dynamic Few-shot:&lt;/b&gt;&amp;nbsp;모든 예시를 다 넣는 대신, 현재 질문과 가장 유사한 성공 사례(Golden Set)만 골라 컨텍스트에 삽입&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Instruction-Context Separation:&lt;/b&gt;&amp;nbsp;지시사항과 참고 데이터를 명확히 구분(예:&amp;nbsp;### Context ###&amp;nbsp;태그 활용)하여 모델이 혼동하지 않게 한다.실제 서비스를 만들 때, 이 기법들은&amp;nbsp;&lt;b&gt;모델의 입력창(Context Window)을 어떻게 구성하느냐&lt;/b&gt;의 문제로 직결되기 때문&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Few-shot은 프롬프트 엔지니어링일텐데?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 서비스를 만들 때, 이 기법들은&amp;nbsp;&lt;span data-token-index=&quot;1&quot;&gt;모델의 입력창(Context Window)을 어떻게 구성하느냐&lt;/span&gt;의 문제로 직결되기 때문&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;프롬프트 측면 (How):&lt;/b&gt;&amp;nbsp;&quot;예시를 이런 형식으로 보여줄게&quot;라는&amp;nbsp;&lt;b&gt;지시 방식&lt;/b&gt;에 집중&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컨텍스트 측면 (What):&lt;/b&gt;&amp;nbsp;&quot;수만 개의 과거 상담 사례 중,&amp;nbsp;&lt;b&gt;지금 질문과 가장 유사한 사례 3개&lt;/b&gt;를 데이터베이스에서 찾아와서 넣어주자&quot;는&amp;nbsp;&lt;b&gt;데이터 선별&lt;/b&gt;에 집중&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 73px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;기법&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;프롬프트 엔지니어링 측면 (How)&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;컨텍스트 엔지니어링 측면 (What)&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;Zero-shot&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;명확하고 간결한&amp;nbsp;&lt;b&gt;지시문&lt;/b&gt;&amp;nbsp;작성&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;추가 정보 없이 모델의&amp;nbsp;&lt;b&gt;내부 지식&lt;/b&gt;만 활용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Few-shot&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;예시를 보여주는&amp;nbsp;&lt;b&gt;포맷&lt;/b&gt;&amp;nbsp;설계&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;질문에 최적화된&amp;nbsp;&lt;b&gt;참조 데이터&lt;/b&gt;&amp;nbsp;선별 및 주입&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;CoT&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&quot;단계별로 생각하라&quot;는&amp;nbsp;&lt;b&gt;트리거&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;모델의&amp;nbsp;&lt;b&gt;추론 로그&lt;/b&gt;를 컨텍스트에 누적&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[비교]&lt;/b&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 76px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;프롬프트 엔지니어링 (기술)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;컨텍스트 엔지니어링 (정보)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;비유&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;요리사의 칼질과 레시피 설명&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;요리사 앞에 놓인 신선한 재료들&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;핵심&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&quot;어떻게 말하게 할 것인가?&quot;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&quot;무엇을 보고 말하게 할 것인가?&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&quot;전문가처럼 답해줘(페르소나)&quot;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&quot;이 PDF 파일을 참고해서 답해줘(RAG)&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/엔지니어링</category>
      <category>AI</category>
      <category>AI Enginieering</category>
      <category>ai엔지니어링</category>
      <category>컨텍스트 엔지니어링</category>
      <author>조슈아。</author>
      <guid isPermaLink="true">https://clairdelunes.tistory.com/171</guid>
      <comments>https://clairdelunes.tistory.com/171#entry171comment</comments>
      <pubDate>Wed, 8 Apr 2026 13:57:57 +0900</pubDate>
    </item>
    <item>
      <title>1. Prompt Engineering(프롬프트 엔지니어링) &amp;mdash; AI에게 일(말)을 잘 거는 기술</title>
      <link>https://clairdelunes.tistory.com/170</link>
      <description>&lt;h1&gt;&lt;b&gt;1. 프롬프트 엔지니어링(Propt Engineering)&lt;/b&gt;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;AI에게 그때그때 일들을 잘 요청하는 기술 &amp;mdash; 소통의 기술&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GPT에게 질문 던지기&lt;/li&gt;
&lt;li&gt;모델에게 묘사 전달&lt;/li&gt;
&lt;li&gt;매번 맥락을 새로 설명&lt;/li&gt;
&lt;li&gt;일관된 결과를 얻기 어려움&lt;/li&gt;
&lt;li&gt;모델이 가진 잠재력을 최대로 끌어내는 &lt;b&gt;'질문과 지시의 최적화'&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;단순히 말을 잘 거는 것을 넘어, 페르소나 설정, Few-shot 학습, Chain-of-Thought(사고의 사슬) 유도 등을 통해 모델의 추론 능력을 극대화&lt;/li&gt;
&lt;li&gt;아무리 데이터(Context)가 좋아도 지시(Prompt)가 모호하면 결과물은 엉뚱하게 나옴
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그래서 &lt;b&gt;모델의 추론 경로를 가이드하고 출력의 품질을 결정하는 '구조 설계'를 잘 해야한다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;주요 기법들 (Techniques)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 질문을 잘하는 것을 넘어, AI의 사고 능력을 극대화하는 몇 가지 전문 기법들이 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 기본 지시 기법 (The Foundations)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 기초적이면서도 강력한 기법&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Zero-shot 기법&lt;/b&gt; :&amp;nbsp;예시 없이 바로 명령을 내리는 것으로, 모델의 성능이 좋을 때 주로 사용(모델의 내장 지식에만 의존)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;양자역학의 개념을 초등학생도 이해할 수 있게 한 문장으로 설명해줘.&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Few-shot 기법 :&lt;/b&gt;&amp;nbsp;AI에게 지시사항과 함께 몇 가지의&amp;nbsp;&lt;b&gt;입출력 예시&lt;/b&gt;를 제공하여 미리 보여주어 패턴을 학습시키는 방법(모델이 문맥과 형식을 빠르게 파악하도록 도와줌)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;다음 문장의 감정을 분류해줘.&lt;br /&gt;1. 오늘 날씨가 정말 좋다: 긍정&lt;br /&gt;2. 배송이 너무 늦어서 짜증 나요: 부정&lt;br /&gt;3. 그럭저럭 볼만하네요: 중립&lt;br /&gt;4. 이 영화는 내 인생 최고의 작품이다:&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Persona (페르소나) 기법 :&lt;/b&gt;&amp;nbsp;&quot;너는 20년 경력의 시니어 소프트웨어 엔지니어다&quot;와 같이 특정 페르소나를 부여하여 답변의 톤과 전문성을 조절&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;너는 20년차 배드민턴 전문 코치야. 이제 막 배드민턴을 시작하는 사람에게 따뜻하면서도 현실적인 조언을 해줘&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 논리적 추론 기법 (Reasoning)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 복잡한 문제를 풀 때 '&lt;b&gt;생각의 단계&lt;/b&gt;'를 밟게 만드는 기술&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Chain-of-Thought (CoT, 사고의 사슬):&lt;/b&gt;&amp;nbsp;&quot;단계별로 차근차근 생각해보자(Let's think step by step)&quot;라는 문구를 넣어 모델이 중간 논리 과정을 생략하지 않게 만드는 기법. 결과의 정확도가 비약적으로 상승&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;철수는 사과 5개를 가지고 있었어. 영희에게 2개를 주고, 시장에서 10개를 더 산 뒤, 그중 절반을 동생에게 줬어.&lt;br /&gt;철수에게 남은 사과는 몇 개일까? 단계별로 차근차근 생각해서 답해줘.&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Least-to-Most :&lt;/b&gt;&amp;nbsp;큰 문제를 작고 해결 가능한 하위 문제(Sub-problems)로 먼저 분해한 뒤, 하나씩 순차적으로 해결하도록 유도&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;파이썬으로 웹 크롤러를 만들고 싶어.&lt;br /&gt;1. 먼저 웹 크롤링을 위해 필요한 라이브러리가 무엇인지 알려줘.&lt;br /&gt;2. 그 라이브러리들을 설치하는 코드를 작성해줘.&lt;br /&gt;3. 특정 뉴스 사이트의 제목만 가져오는 간단한 코드를 짜줘.&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Self-Consistency (자기 일관성):&lt;/b&gt; 동일한 질문에 대해 여러 개의 추론 경로를 생성하게 한 뒤, 가장 많이 공통으로 나온 답을 최종 결과로 선택하는 다수결 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;초콜릿 3박스가 있어. 각 박스에는 초콜릿이 12개씩 들어있고, 나는 친구 4명에게 똑같이 나눠주려고 해. 이 문제를 해결하기 위한 서로 다른 3가지 논리적 경로를 제시하고, 가장 일관된 최종 답을 도출해줘.&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 구조화 및 명확화 기법 (Structuring)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델이 지시사항과 데이터를 혼동하지 않게 만드는 '형식의 기술'&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Delimiters (구분자) 활용:&lt;/b&gt;&amp;nbsp;###,&amp;nbsp;--,&amp;nbsp;&quot;&quot;&quot;&amp;nbsp;같은 기호를 사용하여 지시문, 참고 문헌, 사용자 입력을 명확히 분리&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;다음 [본문]의 내용을 아래의 ###형식###에 맞춰 요약해줘&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Output Structuring:&lt;/b&gt;&amp;nbsp;답변 형식을 JSON, Markdown, Table 등으로 강제. 이는 나중에 코드가 답변을 파싱(Parsing)하기 쉽게 만들어 준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;서울의 주요 관광지 3곳을 추천해주고, 결과는 반드시 JSON 형식으로 출력해줘. 키값은 'name', 'location', 'description'으로 해줘&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Negative Prompting:&lt;/b&gt;&amp;nbsp;&quot;~는 포함하지 마&quot;, &quot;설명은 생략하고 결과만 말해&quot;와 같이 하지 말아야 할 행동을 명시하여 출력을 제어&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;인공지능의 미래에 대해 에세이를 써줘. 단, '인공지능', 'AI', '딥러닝'이라는 단어는 절대 사용하지 말고 설명해줘.&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 고도화 기법 (Advanced)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 연구되고 있는 더 정교한 프롬프트 전략&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Generated Knowledge Prompting:&lt;/b&gt;&amp;nbsp;질문에 바로 답하게 하지 않고, 모델이 관련 지식을 먼저 스스로 생성하게 한 뒤, 그 지식을 바탕으로 최종 답변을 내게 하는 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1. 먼저 '간헐적 단식'의 과학적 원리와 부작용에 대한 지식을 요약해줘.&lt;br /&gt;2. 위에서 생성한 지식을 바탕으로, 평소 위장이 약한 사람을 위한 식단 가이드를 작성해줘&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Directional Stimulus Prompting (방향성 자극):&lt;/b&gt;&amp;nbsp;답변에 포함되어야 할 핵심 키워드나 힌트를 프롬프트에 슬쩍 던져주어 모델이 원하는 방향으로 답변하게 유도&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;강아지에 대한 짧은 동화를 써줘. [힌트: 우주선, 뼈다귀 모양 행성, 그리움]이 키워드들이 반드시 이야기의 핵심 요소로 포함되어야 해.&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Chain-of-Verification (CoVe):&lt;/b&gt;&amp;nbsp;답변을 생성한 후, 모델 스스로가 답변의 사실 관계를 검증하기 위한 질문을 던지고 다시 확인하게 하는 자기 검증 루프&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1. 17세기 유명한 화가 3명과 그들의 대표작을 알려줘.&lt;br /&gt;2. 방금 네가 답변한 내용 중 사실과 다른 부분이 있는지 스스로 검증하는 질문 3개를 던져봐.&lt;br /&gt;3. 그 질문에 답하면서 오류가 있다면 최종적으로 수정된 답변을 줘.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프롬프트 엔지니어링의 핵심&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분 전략 효과&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;구체성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&quot;짧게 써줘&quot; 대신&amp;nbsp;&lt;b&gt;&quot;100자 이내로 요약해줘&quot;&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;모호함 제거 및 의도 반영&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;순서&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;중요한 지시는&amp;nbsp;&lt;b&gt;프롬프트의 맨 끝&lt;/b&gt;에 한 번 더 강조&lt;/td&gt;
&lt;td&gt;최신 편향(Recency Bias) 활용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;반복&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&quot;반드시 JSON으로 답해&quot;를 반복 명시&lt;/td&gt;
&lt;td&gt;출력 형식 강제력 강화&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>AI/엔지니어링</category>
      <category>AI</category>
      <category>AI Engineering</category>
      <category>ai엔지니어링</category>
      <category>프롬프트엔지니어링</category>
      <author>조슈아。</author>
      <guid isPermaLink="true">https://clairdelunes.tistory.com/170</guid>
      <comments>https://clairdelunes.tistory.com/170#entry170comment</comments>
      <pubDate>Wed, 8 Apr 2026 13:33:30 +0900</pubDate>
    </item>
    <item>
      <title>[AI LLM] LLM 중추, 트랜스포머 아키텍처</title>
      <link>https://clairdelunes.tistory.com/169</link>
      <description>&lt;h1&gt;&lt;b&gt;트랜스포머 아키텍처&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜스포머는 순환신경망(RNN: Recurrent Neural Network)의 문제를 해결하기 위해 입력을 하나씩 순차적으로 처리하는 방식을 버리고 셀프 어텐션(Self-Attention)개념을 도입했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;셀프 어텐션(Self-Attention) :&lt;/b&gt; 입력된 문장 내의 각 단어가 서로 어떤 관련이 있는지 계산하여 각 단어의 표현(representation)을 조정하는 역할을한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 대부분의 LLM은 트랜스포머 아키텍처를 활용하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[장점]&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;확장성 : 더 깊은 모델을 만들어도 학습이 잘되며, 동일한 블록을 반복해 사용하기 때문에 확장이 용이하다.&lt;/li&gt;
&lt;li&gt;효율성 : 학습할 때 병렬 연산이 가능하기 때문에 학습시간이 단축된다.&lt;/li&gt;
&lt;li&gt;더 긴 입력 처리 : 입력이 길어져도 성능이 거의 떨어지지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜스포머 아키텍처는 크게 &lt;b&gt;인코더(Encoder)&lt;/b&gt;와 &lt;b&gt;디코더(Decoder)&lt;/b&gt;로 구분되고, 공통적으로 입력을 &lt;b&gt;임베딩(Embbedding)&lt;/b&gt; 층을 통해 숫자 집합인 임베딩으로 변환하고 &lt;b&gt;위치 인코딩(Positional Encoding)&lt;/b&gt; 층에서 문장의 위치 정보를 더한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[인코더]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;층 정규화(Layer normalization)&lt;/b&gt;, &lt;b&gt;멀티 헤드 어텐션(Multi-head Attention)&lt;/b&gt;, &lt;b&gt;피드포워드(feed forward)&lt;/b&gt;층을 거치며 문장을 이해하고 그 결과를 디코더로 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[디코더]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인코더와 유사하게 &lt;b&gt;층 졍규화(Layer normalization)&lt;/b&gt;, &lt;b&gt;멀티 헤드 어텐션(Multi-head Attention)&lt;/b&gt; 연산을 수행하면서 &lt;b&gt;크로스 어텐션(Cross Attention)&lt;/b&gt; 연산을 통해 인코더가 전달한 데이터를 출력과 함께 종합하여 피드 포워드 층을 거쳐 번역 결과를 생성.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7118.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;2208&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJBXzN/dJMcaaqJrky/jT2c8Bv89graqute1QCry1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJBXzN/dJMcaaqJrky/jT2c8Bv89graqute1QCry1/img.png&quot; data-alt=&quot;[트랜스포머 아키텍처 구성]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJBXzN/dJMcaaqJrky/jT2c8Bv89graqute1QCry1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJBXzN%2FdJMcaaqJrky%2FjT2c8Bv89graqute1QCry1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;2208&quot; data-filename=&quot;IMG_7118.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;2208&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[트랜스포머 아키텍처 구성]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;텍스트를 임베딩으로 변환&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터는 텍스트를 그대로 계산할 수 없으므로, 숫자 형식의 데이터로 변경이 필요하다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;텍스트를 적절한 단위로 잘라 숫자형 아이디를 부여하는 &lt;b&gt;토큰화(Tokenization)&lt;/b&gt;을 수행&lt;/li&gt;
&lt;li&gt;토큰 아이디를 토큰 임베딩 층을 통해 여러 숫자 집합인 토큰 임베딩(Token Embbedding)으로 변환&lt;/li&gt;
&lt;li&gt;위치 인코딩 층을 통해 토큰의 위치 정보를 담고 있는 위치 임베딩을 추가해 모델에 입력할 임베딩 생성&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7120.jpg&quot; data-origin-width=&quot;549&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TOyoa/dJMcabQGTU6/2qZH8IY9Q1LfndWkPJbUcK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TOyoa/dJMcabQGTU6/2qZH8IY9Q1LfndWkPJbUcK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TOyoa/dJMcabQGTU6/2qZH8IY9Q1LfndWkPJbUcK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTOyoa%2FdJMcabQGTU6%2F2qZH8IY9Q1LfndWkPJbUcK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;433&quot; height=&quot;342&quot; data-filename=&quot;IMG_7120.jpg&quot; data-origin-width=&quot;549&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 토큰화&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;텍스트를 적절한 단위로 나누고 숫자 아이디를 부여하는 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(한글은 작게는 자음과 모음 단위부터 크게는 단어 단위로 나눌 수 있다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰화를 할 때는 사전(Vocabulary)를 만들어 어떤 토큰이 어떤 숫자 아이디로 연결 됐는지 기록해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;큰 단위 기준으로 토큰화&lt;/b&gt; : 텍스트 의미가 잘 유지되는 장점이 있지만, 사전의 크기가 크진다는 단점이 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단어 기준으로 토큰화&lt;/b&gt; : 단어 수만큼 토큰 아이디가 필요하기 때문에 사전이 커진다. 또한 이전에 본적없는 새로운 단어는 사전에 없기 때문에 처리하지 못하는 &lt;b&gt;OOV(Out Of Vocabulary)&lt;/b&gt;문제가 자주 발생&lt;/li&gt;
&lt;li&gt;&lt;b&gt;작은 단위로 토큰화&lt;/b&gt; : 사전의 크기가 작고 OOV 문제를 줄일 수 있지만, 텍스트의 이미가 유지되지 않는 단점이 존재&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서브워드(Subword) 토큰화 방식&lt;/b&gt; : 위 방식은 각각의 장단점이 뚜렷하므로 데이터에 등장하는 빈도에 따라 토큰화 단위를 결정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자주 나오는 단어는 단어 단위 그대로 유지&lt;/li&gt;
&lt;li&gt;가끔 나오는 단어는 더 작은 단위로 텍스트 의미를 최대한 유지&lt;/li&gt;
&lt;li&gt;사전의 크기는 작고 효율적으로 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 토큰 임베딩 변환&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;임베딩(Embedding)&lt;/b&gt; : 데이터를 의미를 담아 숫자 집합으로 변환하는 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이토치(PyTorch)가 제공하는 nn.Embedding 클래스를 활용하여 토큰 아이디를 토큰 임베딩으로 변환 가능&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import torch
import torch.nn as nn

input_text =('나는 최근에 파리 여행을 다녀왔다.')
input_text_list = input_text.split()

str2idx = {word:idx for idx, word in enumerate(input_text_list)}
idx2str = {idx:word for idx, word in enumerate(input_text_list)}

# 텍스트를 숫자 ID 리스트로 변환
input_ids = [str2idx[word] for word in input_text_list]

embedding_dim = 16
embed_layer = nn.Embedding(len(str2idx), embedding_dim)

# input_ids를 LongTensor로 변환하여 임베딩 레이어에 전달
input_tensor = torch.tensor(input_ids)
input_embeddings = embed_layer(input_tensor)

# 배치 차원 추가 
input_embeddings = input_embeddings.unsqueeze(0)

# 결과 텐서의 모양 출력
print(input_embeddings.shape)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 위치 인코딩&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RNN과 트랜스포머의 가장 큰 차이는 입력을 순차적으로 처리하는지 여부인데, 트랜스포머는 순차적으로 처리하는 방식을 사용하지 않고, 모든 입력을 동시에 처리하며, 이 과정에서 순서 정보가 사라진다. 텍스트에서 순서는 매우 중요한 정보인데 위치 인코딩이 순서 정보 역할을 담당한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;절대적 위치 인코딩(Absolute Position Encoding)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수식을 통해 위치 정보를 추가하는 방식이나 임베딩으로 위치 정보를 학습하는 방식 모두 결국 모델로 추론을 수행하는 시점에서는 입력 토큰의 위치에 따라 고정된 임베딩을 더해주는 것&lt;/li&gt;
&lt;li&gt;구현방식은 간편하지만, 토큰과 토큰 사이의 상대적 위치 정보는 활용하지 못함&lt;/li&gt;
&lt;li&gt;학습 데이터에서 긴 텍스트를 추론하는 경우 성능이 떨어짐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;상대적 위치 인코딩(Relative Position Encoding)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;import torch
import torch.nn as nn

input_text =('나는 최근에 파리 여행을 다녀왔다.')
input_text_list = input_text.split()

str2idx = {word:idx for idx, word in enumerate(input_text_list)}
idx2str = {idx:word for idx, word in enumerate(input_text_list)}

# 텍스트를 숫자 ID 리스트로 변환
input_ids = [str2idx[word] for word in input_text_list]

# 위치인코딩
max_position = 12
embed_layer = nn.Embedding(len(str2idx), embedding_dim)
position_embed_layer = nn.Embedding(max_position, embedding_dim)

position_ids = torch.arange(len(input_ids), dtype=torch.long).unsqueeze(0)
position_encodings = position_embed_layer(position_ids)

token_embeddings = embed_layer(torch.tensor(input_ids))
token_embeddings = token_embeddings.unsqueeze(0)
input_embeddings = token_embeddings + position_encodings
print(input_embeddings.shape)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7123.png&quot; data-origin-width=&quot;1952&quot; data-origin-height=&quot;2176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFqP2b/dJMcadt946z/jKuaA1pWeqgL7bNaYrLtg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFqP2b/dJMcadt946z/jKuaA1pWeqgL7bNaYrLtg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFqP2b/dJMcadt946z/jKuaA1pWeqgL7bNaYrLtg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFqP2b%2FdJMcadt946z%2FjKuaA1pWeqgL7bNaYrLtg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1952&quot; height=&quot;2176&quot; data-filename=&quot;IMG_7123.png&quot; data-origin-width=&quot;1952&quot; data-origin-height=&quot;2176&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;어텐션(Attention)&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜스포머 아키텍처의 핵심 논문의 제목 &lt;b&gt;[Attention is All you need]&lt;/b&gt; 에서도 알수 있듯 &amp;lsquo;어텐션&amp;rsquo;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;텍스트를 처리하는 관점에서는 입력한 텍스트에서 어떤 단어가 서로 관련되었는지 &amp;lsquo;주의를 기울여&amp;rsquo; 파악한다는 의미로 이해할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;사람이 글을 읽는 방법과 어텐션&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 글을 읽을 때 보통 왼쪽에서 오른쪽으로 흐르듯 읽지만 복잡하고 어려운 글을 자주 멈추면서 어떤 단어가 어떤 단어와 연결되는지 문장 안에서 고민하고 찾아본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 &lt;b&gt;어텐션&lt;/b&gt;은 사람이 단어 사이의 관계를 고민하는 과정을 딥러닝 모델이 수행할 수 있도록 모방한 연산이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;어텐션 :&lt;/b&gt; 단어 사이의 관계를 고민하는 과정을 딥러닝 모델이 수행할 수 있도록 모방한 연산이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 자연스럽게 관련이 있는 단어를 찾고 그 맥락을 반영해 단어를 재해석하는 것처럼 어텐션을 만들려면?&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;단어와 단어 사이의 관계를 계산해서 그 값에 따라 관련이 깊은 단어와 그렇지 않은 단어를 구분&lt;/li&gt;
&lt;li&gt;관련이 깊은 단어는 더 많이, 관련이 적은 단어는 더 적게 맥락을 반영&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 해결하기위해 &lt;b&gt;쿼리(Query)&lt;/b&gt;, &lt;b&gt;키(Key)&lt;/b&gt;, &lt;b&gt;값(Value)&lt;/b&gt;의 개념을 도입&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿼리(Query) : 입력한 검색어&lt;/li&gt;
&lt;li&gt;키(Key) : 수 많은 자료 중 쿼리와 관련이 있는지 계산하기 위해 문서가 가진 특징&lt;/li&gt;
&lt;li&gt;값(Value) : 쿼리와 관련이 깊은 키를 가진 문서를 찾아 관련도순으로 정렬하여 문서를 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7125.png&quot; data-origin-width=&quot;3104&quot; data-origin-height=&quot;1376&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lo08u/dJMcahDmNMU/Pgm9ycDfb2WLuekfGBKBn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lo08u/dJMcahDmNMU/Pgm9ycDfb2WLuekfGBKBn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lo08u/dJMcahDmNMU/Pgm9ycDfb2WLuekfGBKBn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flo08u%2FdJMcahDmNMU%2FPgm9ycDfb2WLuekfGBKBn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3104&quot; height=&quot;1376&quot; data-filename=&quot;IMG_7125.png&quot; data-origin-width=&quot;3104&quot; data-origin-height=&quot;1376&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임베딩 시 같은 단어끼리 임베딩이 동일하면 관련도가 크게 계산되어 주변 맥락을 충분히 반영하지 못하는 경우가 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 토큰의 의미가 유사하거나, 반대되는 경우처럼 직접적인 관련성을 띨때는 잘 작동하지만 문법에 의거해 토큰이 이어지는 경우처럼 간접적인 관련성은 반영되기 어려울 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜스포머는 이와같은 문제를 파히기 위해 토큰 임베딩을 반환하는 가중치 $W_Q, W_K$를 도입했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딥러닝에서 어떤 기능을 잘 하게 하고싶을 때 가중치를 도입하고 학습 단계에서 업데이트되게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜스포머에서는 $W_Q, W_K$가중치를 통해 토큰과 토큰 사이의 관계를 계산하는 능력을 학습 시킨것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7128.png&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;1472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbjA32/dJMcafr1F4a/EbPjTSORepXwIvIRp5ozlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbjA32/dJMcafr1F4a/EbPjTSORepXwIvIRp5ozlK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbjA32/dJMcafr1F4a/EbPjTSORepXwIvIRp5ozlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbjA32%2FdJMcafr1F4a%2FEbPjTSORepXwIvIRp5ozlK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2880&quot; height=&quot;1472&quot; data-filename=&quot;IMG_7128.png&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;1472&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜스포머에서는 값(Value)도 토큰 임베딩 가중치$(W_V)$를 통해 변환한다. 이렇게 세 가지 가중치를 활용하여 토큰 간의 관계를 계산하고, 주변 맥락을 적절히 반영하는 방법을 학습한다. 최종적으로 쿼리와 키의 관계를 계산한 관련도 값과 값 가중치$(W_V)$로 변환한 값(Value)을 가중합하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[쿼리, 키 값 벡터를 만드는 nn.Linear 층]&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;head_dim = 16
# 쿼리, 키 값을 계산하기 위한 변환
weight_q = nn.Linear(embedding_dim, head_dim)
weight_k = nn.Linear(embedding_dim, head_dim)
weight_v = nn.Linear(embedding_dim, head_dim)

## 변환 수행
querys = weight_q(input_embeddings)
keys = weight_k(input_embeddings)
values = weight_v(input_embeddings)

print(f&quot;querys={querys}, keys={keys}, values={values}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[스케일 점곱 방식 어텐션]&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿼리와 키를 곱한다. 이때 분산이 커지는 것을 방지하기 위해 ㅇ미베딩 차원 수(dim_k)의 제곱근으로 나눈다.&lt;/li&gt;
&lt;li&gt;쿼리와 키를 곱해 스코어(score)를 합이 1이 되도록 소프트맥스softmax)를 취해 가중치(weights)로 바꾼다.&lt;/li&gt;
&lt;li&gt;가중치와 값을 곱해 입력과 동일한 형태의 출력을 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from math import sqrt
import torch.nn.functional as F
import torch
import torch.nn as nn

input_text =('나는 최근에 파리 여행을 다녀왔다.')
input_text_list = input_text.split()

str2idx = {word:idx for idx, word in enumerate(input_text_list)}
idx2str = {idx:word for idx, word in enumerate(input_text_list)}

# 텍스트를 숫자 ID 리스트로 변환
input_ids = [str2idx[word] for word in input_text_list]

embedding_dim = 16
embed_layer = nn.Embedding(len(str2idx), embedding_dim)

# input_ids를 LongTensor로 변환하여 임베딩 레이어에 전달
input_tensor = torch.tensor(input_ids)
input_embeddings = embed_layer(input_tensor)

# 배치 차원 추가
input_embeddings = input_embeddings.unsqueeze(0)
#############################################################
### 스케일 점곱 어텐션
head_dim = 16
# 쿼리, 키 값을 계산하기 위한 변환
weight_q = nn.Linear(embedding_dim, head_dim)
weight_k = nn.Linear(embedding_dim, head_dim)
weight_v = nn.Linear(embedding_dim, head_dim)

## 변환 수행
querys = weight_q(input_embeddings)
keys = weight_k(input_embeddings)
values = weight_v(input_embeddings)

print(f&quot;querys={querys}, keys={keys}, values={values}&quot;)

def compute_attention(query, key, value):
    dim_k = query.size(-1)
    score = query @ key.transpose(-2, -1) / sqrt(dim_k)
    weight = F.softmax(score, dim=-1)
    return weight @ value

print(f&quot;attention={compute_attention(querys, keys, values).shape}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[가중치의 값을 곱한 후 더해서 주변 단어의 맥락을 반영한 하나의 값 임베딩으로 만든다.]&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7135.png&quot; data-origin-width=&quot;2592&quot; data-origin-height=&quot;1632&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BYLO0/dJMcahpRaGG/RsRQLMqjPGGecrVCr5JniK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BYLO0/dJMcahpRaGG/RsRQLMqjPGGecrVCr5JniK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BYLO0/dJMcahpRaGG/RsRQLMqjPGGecrVCr5JniK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBYLO0%2FdJMcahpRaGG%2FRsRQLMqjPGGecrVCr5JniK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2592&quot; height=&quot;1632&quot; data-filename=&quot;IMG_7135.png&quot; data-origin-width=&quot;2592&quot; data-origin-height=&quot;1632&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[$v_1$은 가중치가 0.672로 가장 크기 때문에 가장 많은 비중으로 섞이고 $v_3$은 가중치가 0.013으로 가장 작기 때문에 가장 적은 비중으로 섞인다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7134.jpg&quot; data-origin-width=&quot;1379&quot; data-origin-height=&quot;734&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bia9Vk/dJMcai95rKS/2mTgJ0ZRAA1JBrCkKJIc3K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bia9Vk/dJMcai95rKS/2mTgJ0ZRAA1JBrCkKJIc3K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bia9Vk/dJMcai95rKS/2mTgJ0ZRAA1JBrCkKJIc3K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbia9Vk%2FdJMcai95rKS%2F2mTgJ0ZRAA1JBrCkKJIc3K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1379&quot; height=&quot;734&quot; data-filename=&quot;IMG_7134.jpg&quot; data-origin-width=&quot;1379&quot; data-origin-height=&quot;734&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;attention.py

import torch.nn as nn
import torch.nn.functional as F
from math import sqrt

def compute_attention(query, key, value):
    dim_k = query.size(-1)
    score = query @ key.transpose(-2, -1) / sqrt(dim_k)
    weight = F.softmax(score, dim=-1)
    return weight @ value

class AttentionHead(nn.Module):
    def __init__(self, token_embed_dim, head_dim):
        super().__init__()
        self.weight_q = nn.Linear(token_embed_dim, head_dim)
        self.weight_k = nn.Linear(token_embed_dim, head_dim)
        self.weight_v = nn.Linear(token_embed_dim, head_dim)

    def forward(self, queries, keys, values):
        return compute_attention(
            self.weight_q(queries)
            , self.weight_k(keys)
            , self.weight_v(values))&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import attention
input_text =('나는 최근에 파리 여행을 다녀왔다.')
input_text_list = input_text.split()

embedding_dim = 16

str2idx = {word:idx for idx, word in enumerate(input_text_list)}
input_ids = [str2idx[word] for word in input_text_list]

input_tensor = torch.tensor(input_ids)
embed_layer = nn.Embedding(len(str2idx), embedding_dim)
input_embeddings = embed_layer(input_tensor)
input_embeddings = input_embeddings.unsqueeze(0)
####################################################################

attention_head = AttentionHead(embedding_dim, embedding_dim)
after_attention_embeddings = attention_head(input_embeddings, input_embeddings, input_embeddings)

print(f&quot;after_attention_embeddings={after_attention_embeddings.shape}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;멀티 헤드 어텐션&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 어텐션 연산을 동시에 적용하면 성능을 더 높일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 토큰 사이의 관계를 한 가지 측면에서 이해하는 것보다 여러 측면을 동시에 고려할 때 언어나 문장에 대한 이해도가 높아지며 동시에 헤드의 수(n) 만큼 어텐션 연산을 수행하는 것을 멀티 헤드 어텐션이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7138.png&quot; data-origin-width=&quot;2720&quot; data-origin-height=&quot;1568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TA03O/dJMcabDbdKQ/izdd3mqvn6EWnRfbvxDY5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TA03O/dJMcabDbdKQ/izdd3mqvn6EWnRfbvxDY5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TA03O/dJMcabDbdKQ/izdd3mqvn6EWnRfbvxDY5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTA03O%2FdJMcabDbdKQ%2Fizdd3mqvn6EWnRfbvxDY5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2720&quot; height=&quot;1568&quot; data-filename=&quot;IMG_7138.png&quot; data-origin-width=&quot;2720&quot; data-origin-height=&quot;1568&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;헤드의 수 만큼 연산을 수행하기 위해 쿼리, 키, 값을 n_head개로 쪼갠다.&lt;/li&gt;
&lt;li&gt;각각의 어텐션을 계산&lt;/li&gt;
&lt;li&gt;입력과 같은 형태로 다시 변환&lt;/li&gt;
&lt;li&gt;선형 층을 통과시키고 최종 결과를 반환&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import torch.nn as nn
from attention import compute_attention

class MultiheadAttention(nn.Module):
    def __init__(self, token_embed_dim, d_model, n_heads):
        super().__init__()
        self.n_heads = n_heads
        self.weight_q = nn.Linear(token_embed_dim, d_model)
        self.weight_k = nn.Linear(token_embed_dim, d_model)
        self.weight_v = nn.Linear(token_embed_dim, d_model)
        self.concat_linear = nn.Linear(d_model, d_model)

    def forward(self, queries, keys, values):
        B,T,C = queries.size()
        queries = (self.weight_q(queries)
                   .view(B, T, self.n_heads, C // self.n_heads).transpose(1,2))

        keys = (self.weight_k(keys)
                   .view(B, T, self.n_heads, C // self.n_heads).transpose(1, 2))

        values = (self.weight_v(values)
                .view(B, T, self.n_heads, C // self.n_heads).transpose(1, 2))

        attention = compute_attention(queries, keys, values)
        output = attention.transpose(1,2).contiguous().view(B, T, C)
        output = self.concat_linear(output)
        return output&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from multihead_attention import MultiheadAttention

input_text =('나는 최근에 파리 여행을 다녀왔다.')
input_text_list = input_text.split()

embedding_dim = 16

str2idx = {word:idx for idx, word in enumerate(input_text_list)}
input_ids = [str2idx[word] for word in input_text_list]

input_tensor = torch.tensor(input_ids)
embed_layer = nn.Embedding(len(str2idx), embedding_dim)
input_embeddings = embed_layer(input_tensor)
input_embeddings = input_embeddings.unsqueeze(0)

n_head = 4
mh_attention = MultiheadAttention(embedding_dim, embedding_dim, n_head)
after_attention_embeddings = mh_attention(input_embeddings, input_embeddings, input_embeddings)
print(f&quot;after&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&lt;b&gt;층 졍규화&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규화란 딥러닝 모델에서 입력이 일정한 분포를 갖도록 만들어 학습이 안정적이고 빨라질 수 있도록 하는 기법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거에는 배치 입력 데이터 사이에 정규화를 수행하는 &lt;b&gt;배치 정규화&lt;/b&gt;를 주로 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜스포머 아키텍처에서는 &lt;b&gt;층 정규화&lt;/b&gt;를 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사전 정규화(post-norm) : 어텐션과 피드포워드 층 이후에 층 정규화를 하는 것&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어텐션 &amp;rarr; 합 &amp;rarr; 층 정규화 &amp;rarr; 피드포워드 층 &amp;rarr; 합 정규화 &amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사후 정규화(pre-norm) : 먼저 층 정규화를 적용하고 어텐션과 피드포워드 층을 통과하는 것&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;층 정규화 &amp;rarr; 멀티 헤드 어텐션 &amp;rarr; 합 &amp;rarr; 층 정규화 &amp;rarr; 피드포워드 층 &amp;rarr; 합&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;피드포워드 층(feedforward layer)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터의 특징을 학습하는 완전 연결 층(fully connected layer)을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀테 헤드 어텐션이 단어 사이의 관계를 파악하는 역할이라면 피드 포워드 층은 입력 텍스트 전체를 이해하는 역할을 담당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;피드포워드는 선형 층, 드롭아웃 층 , 층 정규화, 활성함수로 구성되며, 임베딩의 차원을 동일하게 유지해야 쉽게 층을 쌓아 확장이 가능하기 때문에 입력과 출력의 형태가 동일하도록 맞춘다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import torch.nn as nn

class PreLayerNormFeedForward(nn.Module):
    def __init__(self, d_model, dim_feedforward, dropout):
        super().__init__()
        self.linear1 = nn.Linear(d_model, dim_feedforward) ## 선형 층 1
        self.linear2 = nn.Linear(dim_feedforward, d_model) ## 선형 층 2
        self.dropout1 = nn.Dropout(dropout) ## 드롭아웃 층 1
        self.dropout2 = nn.Dropout(dropout) ## 드롭아웃 층 2
        self.activation = nn.GELU() ## 활성 함수
        self.norm = nn.LayerNorm(d_model) ## 층 정규화

    def forward(self, x):
        x = self.norm(x)
        x = x + self.linear2(self.dropout1(self.activation(self.linear1(x))))
        x = self.dropout2(x)
        return x&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&lt;b&gt;인코더&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜스포머 인코더는 멀티 헤드 어텐션, 층 정규화, 피드 포워드 층이 반복되는 형태이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 상자 안을 보면 밖으로 뻗어나갔다가 다시 더해지는 화살표 2개가 있는데, 안정적인 학습이 가능하도록 도와주는 잔차 연결(residual connection)이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;잔차연결: 화살표 모양 그대로 입력을 다시 더해서 더해주는 형태로 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7140.png&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;2304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duJhmG/dJMcaf6DoOu/vQi28Zx40u5Nim0PQEwvm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duJhmG/dJMcaf6DoOu/vQi28Zx40u5Nim0PQEwvm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duJhmG/dJMcaf6DoOu/vQi28Zx40u5Nim0PQEwvm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduJhmG%2FdJMcaf6DoOu%2FvQi28Zx40u5Nim0PQEwvm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1856&quot; height=&quot;2304&quot; data-filename=&quot;IMG_7140.png&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;2304&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜스포머 인코더는 인코더 블록을 $N_e$번 반복해서 쌓아서 만든다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import torch.nn as nn

from feedforward import PreLayerNormFeedForward
from multihead_attention import MultiheadAttention

class TransformerEncoderLayer(nn.Module):
    def __init__(self, d_model, n_head, dim_feedforward, dropout):
        super().__init__()
        self.attn = MultiheadAttention(d_model, d_model, n_head) ## 멀티 헤드 어텐션
        self.norm1 = nn.LayerNorm(d_model)                       ## 층 정규화
        self.dropout1 = nn.Dropout(dropout)                       ## 드롭아웃
        self.feedForward = PreLayerNormFeedForward(d_model, dim_feedforward, dropout) ## 피드 포워드

    def forward(self, src):
        output = src
        for mod in self.layers:
            output = mod(output)
        return output&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&lt;b&gt;디코더&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인코더와 디코더를 비교할 때 두가지 부분 차이&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;마스크 멀티 헤드 어텐션을 사용&lt;/li&gt;
&lt;li&gt;생성을 담당하는 부분으로, 사람이 글을 쓸 때 앞 단어부터 순차적으로 작성하는 것처럼 트랜스포머 모델도 앞에서 생성한 토큰을 기반으로 다음 토큰을 생성.&lt;/li&gt;
&lt;li&gt;이를 인과적(causal) or 자기 회귀적(auto-regressive)이라 한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7143.png&quot; data-origin-width=&quot;1824&quot; data-origin-height=&quot;2336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Gzf1J/dJMcajnB7XP/jDXlQKmHNttoQgMFkwHojK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Gzf1J/dJMcajnB7XP/jDXlQKmHNttoQgMFkwHojK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Gzf1J/dJMcajnB7XP/jDXlQKmHNttoQgMFkwHojK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGzf1J%2FdJMcajnB7XP%2FjDXlQKmHNttoQgMFkwHojK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1824&quot; height=&quot;2336&quot; data-filename=&quot;IMG_7143.png&quot; data-origin-width=&quot;1824&quot; data-origin-height=&quot;2336&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 텍스트를 생성할 때 디코더는 이전까지 생성한 텍스트만 확인할 수 있다. 하지만 학습할 때는 인코더와 디코더 모두 완성된 텍스트를 입력으로 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 어텐션을 그대로 활용하면 미래 시점에 작성해야 하는 텍스트를 미리 확인하게 되는 문제가 발생한다. 이를 방지하기 위해 특정 시점에는 그 이전에 생성된 토큰까지만 확인할 수 있도록 마스크를 추가한다.&lt;/p&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;def compute_attention(query, key, value, is_causal=False):
    dim_k = query.size(-1)
    score = query @ key.transpose(-2, -1) / sqrt(dim_k)

    if is_causal:
        query_length = query.size(-2)
        key_length = key.size(-2)
        temp_mask = torch.ones(query_length, key_length, dtype=torch.bool).tril(diagonal=0)
        score = score.masked_fill(temp_mask == False, float(&quot;-inf&quot;))
    weight = F.softmax(score, dim=-1)

    return weight @ value
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;is_causal 이 True이면 torch.ones로 모두 1인 행렬에 tril 함수를 취해 대각선 아래 부분만 1로 유지되고 나머지는 음의 무한대(-inf)로 변경해 마스크를 생성한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7145.png&quot; data-origin-width=&quot;3680&quot; data-origin-height=&quot;1152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Raa2E/dJMcaihY1L7/zL5v3TxZiKUf9reYz5kEnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Raa2E/dJMcaihY1L7/zL5v3TxZiKUf9reYz5kEnK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Raa2E/dJMcaihY1L7/zL5v3TxZiKUf9reYz5kEnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRaa2E%2FdJMcaihY1L7%2FzL5v3TxZiKUf9reYz5kEnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3680&quot; height=&quot;1152&quot; data-filename=&quot;IMG_7145.png&quot; data-origin-width=&quot;3680&quot; data-origin-height=&quot;1152&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;크로스 어텐션(Cross Attention)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크로스 어텐션(Cross Attention)은 트랜스포머 디코더에서 사용되는 메커니즘으로, 디코더가 인코더의 출력 정보를 참조할 수 있도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;셀프 어텐션과 달리, 크로스 어텐션에서는:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Query&lt;/b&gt;는 디코더의 출력에서 가져온다&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Key&lt;/b&gt;와 &lt;b&gt;Value&lt;/b&gt;는 인코더의 출력에서 가져온다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 디코더는 생성 중인 텍스트와 입력 텍스트(인코더가 처리한 정보) 사이의 관계를 파악하여, 입력 문맥을 고려한 출력을 생성할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import torch.nn as nn
from feedforward import PreLayerNormFeedForward
from multihead_attention import MultiheadAttention

class TransformerDecoderLayer(nn.Module):
    def __init__(self, token_embed_dim, d_model, n_heads, dim_feedforward=2048, dropout=0.1):
        super().__init__()
        self.self_attn = MultiheadAttention(d_model, d_model, n_heads)
        self.multihead_attn = MultiheadAttention(d_model, d_model, n_heads)
        self.feedForward = PreLayerNormFeedForward(d_model, dim_feedforward, dropout)
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.dropout1 = nn.Dropout(dropout)
        self.dropout2 = nn.Dropout(dropout)

    def forward(self, tgt, encoder_output, is_causal=True):
        # 셀프 어텐션 계산
        x = self.norm1(tgt)
        x = x + self.dropout1(self.self_attn(x, x, x,
        is_causal = True))
        # 크로스 어텐션 연산
        x = self.norm2(x)
        x = x + self.dropout2(self.multihead_attn(x, encoder_output, encoder_output))
        # 피드 포워드 연산
        x = self.feed_forward(x)
        return x&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디코더는 인코더와 마찬가지로 디코더 층을 여러 번 쌓아 만든다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import copy
def get_clones(module, N):
  return nn.ModuleList([copy.deepcopy(module) for i in range(N)])

class TransformerDecoder(nn.Module):
  def __init__(self, decoder_layer, num_layers):
    super().__init__()
    self.layers = get_clones(decoder_layer, num_layers)
    self.num_layers = num_layers

  def forward(self, tgt, src):
    output = tgt
    for mod in self.layers:
      output = mod(output, src)
    return output&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM을 활용한 실전 AI 애플리케이션 개발&lt;/p&gt;</description>
      <category>AI/LLM</category>
      <category>AI</category>
      <category>attention</category>
      <category>LLM</category>
      <category>Transformer</category>
      <category>어텐션</category>
      <category>트랜스포머</category>
      <author>조슈아。</author>
      <guid isPermaLink="true">https://clairdelunes.tistory.com/169</guid>
      <comments>https://clairdelunes.tistory.com/169#entry169comment</comments>
      <pubDate>Tue, 17 Feb 2026 21:15:59 +0900</pubDate>
    </item>
    <item>
      <title>[AI LLM] 대규모 언어 모델 소개 (언어 AI 역사)</title>
      <link>https://clairdelunes.tistory.com/168</link>
      <description>&lt;h1&gt;&lt;b&gt;언어 AI 역사&lt;/b&gt;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. BoW로 언어 표현&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언어 모델의 역사는 비구조적인 텍스트를 표현하는 방법인 &lt;b&gt;BoW(bag-of-words)&lt;/b&gt;라는 기법으로 시작&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;작동방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수치 표현을 만들려는 두 개의 문장이 있다고 가정&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;BoW 모델의 첫 단계 : 문장을 개별 단어 또는 부분 단어(subword)(토큰token)로 분할하는 과정인 &lt;b&gt;토큰화(Tokenization)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;가장 널리 사용되는 토큰화 방법 : 공백을 기준으로 개별 단어를 분할 [ But, 개별 단어 사이 공백이 없는 언어는 불리]&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오전_10.16.14.png&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;636&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bX8hyo/dJMcacvkMoK/BvKz8D5NXv1au8VBkQ2Kok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bX8hyo/dJMcacvkMoK/BvKz8D5NXv1au8VBkQ2Kok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bX8hyo/dJMcacvkMoK/BvKz8D5NXv1au8VBkQ2Kok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbX8hyo%2FdJMcacvkMoK%2FBvKz8D5NXv1au8VBkQ2Kok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1424&quot; height=&quot;636&quot; data-filename=&quot;스크린샷_2026-01-07_오전_10.16.14.png&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;636&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰화를 한 다음 각 문장의 고유한 단어를 모두 합쳐 어휘사전(vocabulary)를 생성하여 문장을 표현&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오전_10.17.48.png&quot; data-origin-width=&quot;1460&quot; data-origin-height=&quot;860&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMeUEa/dJMcag5yN20/69iVz6IBDBEdSxh6b2A5s0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMeUEa/dJMcag5yN20/69iVz6IBDBEdSxh6b2A5s0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMeUEa/dJMcag5yN20/69iVz6IBDBEdSxh6b2A5s0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMeUEa%2FdJMcag5yN20%2F69iVz6IBDBEdSxh6b2A5s0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1460&quot; height=&quot;860&quot; data-filename=&quot;스크린샷_2026-01-07_오전_10.17.48.png&quot; data-origin-width=&quot;1460&quot; data-origin-height=&quot;860&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어휘사전을 사용하여 각 문장에 단어가 얼마나 많이 등장하는지 확인. 이 방법을 단어 가방(bag of words)라 고하며, BoW는 벡터 또는 벡터 표현이라는 수치 형태로 텍스트를 표현하는 것이 목표&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[단점]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언어를 글자 그대로 가방에 담긴 단어의 모음 이상으로 생각하지 않고 텍스트 본질과 의미를 무시&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오전_10.18.44.png&quot; data-origin-width=&quot;1634&quot; data-origin-height=&quot;958&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IGdPq/dJMcadnpCfA/pmrC2OTEmPpyBGHLdcZqL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IGdPq/dJMcadnpCfA/pmrC2OTEmPpyBGHLdcZqL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IGdPq/dJMcadnpCfA/pmrC2OTEmPpyBGHLdcZqL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIGdPq%2FdJMcadnpCfA%2FpmrC2OTEmPpyBGHLdcZqL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1634&quot; height=&quot;958&quot; data-filename=&quot;스크린샷_2026-01-07_오전_10.18.44.png&quot; data-origin-width=&quot;1634&quot; data-origin-height=&quot;958&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 밀집 벡터 임베딩으로 더 나은 표현 생성&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2013년 에 개발된 word2vec는 임베딩으로 텍스트 의미를 포작하는데 성공&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;임베딩&lt;/b&gt;은 데이터의 의미를 포착하기 위한 벡터 표현으로 위키백과와 같은 방대한 텍스트 데이터에서 훈련하여 단어의 의미를 나타내는 표현을 학습&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;word2vec는 신경망(neural network)를 사용해 의미 표현을 생성하고, 이 신경망은 여러 층이 서로 연결되어 구성되고, 각 층은 정보를 처리하는 여러 노드로 구성.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 층의 노드와 다음 층의 노드를 잇는 연결에는 입력 값에 따라 특정한 가중치가 부여된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[가중치를 종종 모델의 파라미터라고 부른다]&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오전_10.23.48.png&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;1020&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N5NlR/dJMcafMmtRl/O5fL8BW57z6AEiHRUK6c7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N5NlR/dJMcafMmtRl/O5fL8BW57z6AEiHRUK6c7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N5NlR/dJMcafMmtRl/O5fL8BW57z6AEiHRUK6c7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN5NlR%2FdJMcafMmtRl%2FO5fL8BW57z6AEiHRUK6c7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1684&quot; height=&quot;1020&quot; data-filename=&quot;스크린샷_2026-01-07_오전_10.23.48.png&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;1020&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;word2vec은 신경망을 사용하여 주어진 문장에서 다음에 어떤 단어가 등장하는지를 살펴봄으로써 단어 임베딩을 생성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오전_10.28.49.png&quot; data-origin-width=&quot;2080&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chv1BJ/dJMcahpRats/MfLvGKBLFAb4PI5ToyD5bK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chv1BJ/dJMcahpRats/MfLvGKBLFAb4PI5ToyD5bK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chv1BJ/dJMcahpRats/MfLvGKBLFAb4PI5ToyD5bK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fchv1BJ%2FdJMcahpRats%2FMfLvGKBLFAb4PI5ToyD5bK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2080&quot; height=&quot;600&quot; data-filename=&quot;스크린샷_2026-01-07_오전_10.28.49.png&quot; data-origin-width=&quot;2080&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임베딩은 단어의 의미를 표현하기 위해 여러 속성을 가질 수 있으며, 임베딩의 크기가 고정이기 때문에 임베딩 속성은 단어의 정신적 표상(mental representation)을 만들도록 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오전_10.31.11.png&quot; data-origin-width=&quot;2058&quot; data-origin-height=&quot;982&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIuyHa/dJMcai95rzY/ZNER9N1X75QHOPsPomfyWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIuyHa/dJMcai95rzY/ZNER9N1X75QHOPsPomfyWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIuyHa/dJMcai95rzY/ZNER9N1X75QHOPsPomfyWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIuyHa%2FdJMcai95rzY%2FZNER9N1X75QHOPsPomfyWk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2058&quot; height=&quot;982&quot; data-filename=&quot;스크린샷_2026-01-07_오전_10.31.11.png&quot; data-origin-width=&quot;2058&quot; data-origin-height=&quot;982&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임베딩은 단어 사이에 있는 유사성을 측정할 수 있으므로, 다양한 거리 측정 방법을 사용해 한 단어가 다른 단어와 얼마나 가까운지 판단 가능.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오전_10.32.59.png&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;994&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdS5aU/dJMcafexr2K/SIAHyorrD0q6rPWeavFAak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdS5aU/dJMcafexr2K/SIAHyorrD0q6rPWeavFAak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdS5aU/dJMcafexr2K/SIAHyorrD0q6rPWeavFAak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdS5aU%2FdJMcafexr2K%2FSIAHyorrD0q6rPWeavFAak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1670&quot; height=&quot;994&quot; data-filename=&quot;스크린샷_2026-01-07_오전_10.32.59.png&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;994&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임베딩의 종류는 많고 단어와 임베딩과 문장 임베딩은 다른 수준(단어 vs 문장)의 추상화에 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들면 BoW는 전체 문서를 표현하므로 문서 수준에서 임베딩을 만들지만, word2vec은 단어에 대한 임베딩만 생성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오전_10.33.44.png&quot; data-origin-width=&quot;2094&quot; data-origin-height=&quot;1530&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BkhBW/dJMcahpRava/c4fKqVlmgpK8bpVF3wQhE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BkhBW/dJMcahpRava/c4fKqVlmgpK8bpVF3wQhE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BkhBW/dJMcahpRava/c4fKqVlmgpK8bpVF3wQhE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBkhBW%2FdJMcahpRava%2Fc4fKqVlmgpK8bpVF3wQhE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2094&quot; height=&quot;1530&quot; data-filename=&quot;스크린샷_2026-01-07_오전_10.33.44.png&quot; data-origin-width=&quot;2094&quot; data-origin-height=&quot;1530&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 어텐션을 사용한 문맥 인코딩과 디코딩&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인코더가 입력을 이해하고, 그 이해한 내용을 바탕으로 디코더가 새로운 문장을 생성하는 복합구조다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;특징:&lt;/b&gt; 입력 문장과 출력 문장의 형식이 다른 &lt;b&gt;Sequence-to-Sequence(SeqToSeq)&lt;/b&gt; 작업에 매우 강력&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대표 모델:&lt;/b&gt; T5, BERT, MarianMT&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주요 용도:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기계 번역 (한국어 입력 &amp;rarr; 영어 출력)&lt;/li&gt;
&lt;li&gt;텍스트 요약 (긴 본문 입력 &amp;rarr; 짧은 요약문 출력)&lt;/li&gt;
&lt;li&gt;문법 교정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;word2vec의 훈련 과정은 정적이고 다운로드 가능한 단어 표현을 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예) 단어 &amp;lsquo;bank&amp;rsquo;는 문맥에 상관없이 항상 임베딩이 동일. but, &amp;lsquo;bank&amp;rsquo;가 은행이나 강둑을 의미할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 임베딩은 문맥에 따라 달라져야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 텍스트를 인코딩하는 단계는 순환 신경망(recurrent neural network[RNN])을 사용하며, RNN은 연속적인 입력으로 시퀀스를 모델링 할 수 있는 신경망을 한 종류이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.04.51.png&quot; data-origin-width=&quot;1930&quot; data-origin-height=&quot;1092&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRjAyH/dJMcaihY1vV/HGEVv0pq6qKqIhFkBobp91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRjAyH/dJMcaihY1vV/HGEVv0pq6qKqIhFkBobp91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRjAyH/dJMcaihY1vV/HGEVv0pq6qKqIhFkBobp91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRjAyH%2FdJMcaihY1vV%2FHGEVv0pq6qKqIhFkBobp91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1930&quot; height=&quot;1092&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.04.51.png&quot; data-origin-width=&quot;1930&quot; data-origin-height=&quot;1092&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조에서 각 단계는 자기회기적(autoregressive)이다. 즉 이전에 생성한 단어를 사용해 다음 단어를 생성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.04.54.png&quot; data-origin-width=&quot;1564&quot; data-origin-height=&quot;850&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GWO9z/dJMcac27kLL/7Mq2QJlvOsaNhMYuIcFEe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GWO9z/dJMcac27kLL/7Mq2QJlvOsaNhMYuIcFEe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GWO9z/dJMcac27kLL/7Mq2QJlvOsaNhMYuIcFEe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGWO9z%2FdJMcac27kLL%2F7Mq2QJlvOsaNhMYuIcFEe1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1564&quot; height=&quot;850&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.04.54.png&quot; data-origin-width=&quot;1564&quot; data-origin-height=&quot;850&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인코딩 단계의 목표는 입력을 가능한 잘 표현하여 디코더의 입력으로 사용되는 임베딩의 형태로 문맥을 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력 및 출력은 한 번에 하나씩 순차적으로 처리&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.07.05.png&quot; data-origin-width=&quot;1680&quot; data-origin-height=&quot;1306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q8X9W/dJMcahciWJi/R5K2t4lbcZhZUGVURtYO6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q8X9W/dJMcahciWJi/R5K2t4lbcZhZUGVURtYO6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q8X9W/dJMcahciWJi/R5K2t4lbcZhZUGVURtYO6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq8X9W%2FdJMcahciWJi%2FR5K2t4lbcZhZUGVURtYO6k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1680&quot; height=&quot;1306&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.07.05.png&quot; data-origin-width=&quot;1680&quot; data-origin-height=&quot;1306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 문맥 임베딩은 하나의 임베딩으로 전체 입력을 표현하기 때문에 긴 문장을 처리하긴 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조를 크게 개선한 &lt;b&gt;어텐션(Attention)&lt;/b&gt;이라는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;어텐션(Attention)&lt;/b&gt;은 주어진 문장에서 어떤 단어가 가장 중요한지 선택적으로 결정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &amp;ldquo;lama&amp;rsquo;s&amp;rdquo;는 &amp;ldquo;llamas&amp;rdquo;에 해당하는 네덜란드 언어이다. 따라서 두 단어 사이의 어텐션이 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 &amp;ldquo;lama&amp;rsquo;s&amp;rdquo;와 &amp;ldquo;I&amp;rdquo;는 관련이 없기 때문에 어텐션이 낮다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.13.43.png&quot; data-origin-width=&quot;1760&quot; data-origin-height=&quot;986&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgGKHx/dJMcachMDRJ/QrfrMKEXdpKuqgCBUDYUBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgGKHx/dJMcachMDRJ/QrfrMKEXdpKuqgCBUDYUBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgGKHx/dJMcachMDRJ/QrfrMKEXdpKuqgCBUDYUBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgGKHx%2FdJMcachMDRJ%2FQrfrMKEXdpKuqgCBUDYUBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1760&quot; height=&quot;986&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.13.43.png&quot; data-origin-width=&quot;1760&quot; data-origin-height=&quot;986&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어텐션 메커니즘을 다코더 단계에 추가함으로 RNN이 입력 시퀀스의 각 단어에 대해 출력 가능성에 관한 신호를 생성하고, 문맥 임베딩을 디코더에 전달하는 대신 모든 입력은 단어의 은닉 상태가 전달&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.15.19.png&quot; data-origin-width=&quot;2064&quot; data-origin-height=&quot;1184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPaBNm/dJMcafyO2Q4/xmcy1DhdvlDiKwjl0ruzV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPaBNm/dJMcafyO2Q4/xmcy1DhdvlDiKwjl0ruzV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPaBNm/dJMcafyO2Q4/xmcy1DhdvlDiKwjl0ruzV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPaBNm%2FdJMcafyO2Q4%2Fxmcy1DhdvlDiKwjl0ruzV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2064&quot; height=&quot;1184&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.15.19.png&quot; data-origin-width=&quot;2064&quot; data-origin-height=&quot;1184&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. Attention Is All You Need&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어텐션의 진정한 힘과, 대규모 언어 모델의 능력을 만들어 낸 동력은 2017년에 발표된 논문에서 소개&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어텐션 메커니즘만 사용하고 순환신경망을 제거한 &lt;b&gt;트랜스포머(Transformer)&lt;/b&gt;라는 신경망 구조를 제안&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜스포머도 자기회기적이어서 이전에 생성된 모든 단어를 사용해서 새로운 단어를 생성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.18.17.png&quot; data-origin-width=&quot;2086&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQStqp/dJMcadAUYfJ/HYA8aSm1AZ7G43fF8keSq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQStqp/dJMcadAUYfJ/HYA8aSm1AZ7G43fF8keSq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQStqp/dJMcadAUYfJ/HYA8aSm1AZ7G43fF8keSq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQStqp%2FdJMcadAUYfJ%2FHYA8aSm1AZ7G43fF8keSq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2086&quot; height=&quot;1080&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.18.17.png&quot; data-origin-width=&quot;2086&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인코더와 디코더 블록은 어텐션 기능이 포함된 RNN을 활용하는 대신 어텐션을 중심으로 구동&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜스포머의 인코더 블록은 &lt;b&gt;셀프 어텐션(self-attention)&lt;/b&gt;과 &lt;b&gt;피드포워드 신경망(feedforward neural network)&lt;/b&gt;로 구성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.20.23.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1054&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5cdkJ/dJMcafMmtSF/EygnytgeLIGAtCZ4jpT7Gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5cdkJ/dJMcafMmtSF/EygnytgeLIGAtCZ4jpT7Gk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5cdkJ/dJMcafMmtSF/EygnytgeLIGAtCZ4jpT7Gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5cdkJ%2FdJMcafMmtSF%2FEygnytgeLIGAtCZ4jpT7Gk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1054&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.20.23.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1054&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;셀프 어텐션&lt;/b&gt;은 한 시퀀스 안의 다른위치에 주의를 기울이며, 입력 시퀀스를 더 쉽고 정확하게 표현할 수 있으며, 한 번에 하나의 토큰을 처리하지 않고 전체 시퀀스를 한번에 처리할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.21.48.png&quot; data-origin-width=&quot;2038&quot; data-origin-height=&quot;840&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVxjZa/dJMcajnB7IC/sePps2ufe36RYRVKMtTwN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVxjZa/dJMcajnB7IC/sePps2ufe36RYRVKMtTwN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVxjZa/dJMcajnB7IC/sePps2ufe36RYRVKMtTwN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVxjZa%2FdJMcajnB7IC%2FsePps2ufe36RYRVKMtTwN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2038&quot; height=&quot;840&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.21.48.png&quot; data-origin-width=&quot;2038&quot; data-origin-height=&quot;840&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디코더는(입력에서 관련된 부분을 찾기 위해) 인코더의 출력에 주의를 기울이는 별도의 층이 추가&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.23.16.png&quot; data-origin-width=&quot;1946&quot; data-origin-height=&quot;1530&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OF62d/dJMcai95rBK/tjYqumJLdETF9CpnvlwXoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OF62d/dJMcai95rBK/tjYqumJLdETF9CpnvlwXoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OF62d/dJMcai95rBK/tjYqumJLdETF9CpnvlwXoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOF62d%2FdJMcai95rBK%2FtjYqumJLdETF9CpnvlwXoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1946&quot; height=&quot;1530&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.23.16.png&quot; data-origin-width=&quot;1946&quot; data-origin-height=&quot;1530&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디코더의 셀프 어텐션 층은 미래의 위치를 마스킹(Masking)하여 출력을 생성할 때 정보 누출을 방지하기 위해 앞선 위치에만 주의를 기울일 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.24.44.png&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czZkIp/dJMcabQGTMX/e4zUYRP5NwQuxf0KAgVSR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czZkIp/dJMcabQGTMX/e4zUYRP5NwQuxf0KAgVSR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czZkIp/dJMcabQGTMX/e4zUYRP5NwQuxf0KAgVSR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczZkIp%2FdJMcabQGTMX%2Fe4zUYRP5NwQuxf0KAgVSR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1246&quot; height=&quot;768&quot; data-filename=&quot;스크린샷_2026-01-07_오전_11.24.44.png&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 구성 요소가 합쳐저서 트랜스포머 구조가 만들어지며, 이 구조를 통해 BERT와 GPT-1 같은 언어 AI 분야에 영향을 끼친 많 은 모델의 기반을 이룬다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4-1. 표현모델:인코더 기반 모델&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인코더는 입력된 문장을 &amp;lsquo;이해&amp;lsquo;하고 그 의미를 고차원적인 벡터(숫자)로 변환하는 데 최적화되어 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;특징:&lt;/b&gt; &lt;b&gt;양방향(Bidirectional)&lt;/b&gt; 맥락 파악이 가능. 문장 전체를 한꺼번에 보고 각 단어가 앞뒤 단어들과 어떤 관계인지 분석.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;학습 방식:&lt;/b&gt; 문장의 중간 단어를 가리고(Masking) 맞히는 '마스크 언어 모델링(MLM)'을 주로 사용.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대표 모델:&lt;/b&gt; &lt;b&gt;BERT&lt;/b&gt;, RoBERTa, ALBERT&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주요 용도:&lt;/b&gt; * 문장 분류 (스팸 메일 분류 등)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개체명 인식 (NER: 인물, 장소 추출)&lt;/li&gt;
&lt;li&gt;질의응답 (주어진 지문에서 정답 찾기)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원본 트랜스포머 모델의 경우 인코더-디코더 구조라서 번역 작업에는 적합하지만, 텍스트 분류같은 작업에는 쉽게 사용하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 2018년 BERT(Bidirectional Encoder Representations form Transformers) 라는 새로운 구조가 소개되었으며 언어 AI 기반으로 자리매김&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BERT는 언어를 표현하는데 초점을 맞춘 인코더 기반 구조이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인코더 기반 구조 : 인코더만 사용하고 디코더는 사용하지 않는다는 의미&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오후_1.15.03.png&quot; data-origin-width=&quot;1660&quot; data-origin-height=&quot;1228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dXZRfW/dJMcagkaigA/06EuFqHk8X4KdriOgWfsx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dXZRfW/dJMcagkaigA/06EuFqHk8X4KdriOgWfsx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dXZRfW/dJMcagkaigA/06EuFqHk8X4KdriOgWfsx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdXZRfW%2FdJMcagkaigA%2F06EuFqHk8X4KdriOgWfsx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1660&quot; height=&quot;1228&quot; data-filename=&quot;스크린샷_2026-01-07_오후_1.15.03.png&quot; data-origin-width=&quot;1660&quot; data-origin-height=&quot;1228&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BERT는 마스크드 언어 모델링(Masked Language Modeling)이라는 기법을 적용&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델이 예측할 입력의 일부분을 마스킹&lt;/li&gt;
&lt;li&gt;일반적으로 전이 학습(transfer learning)에 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;언어 모델링을 위해 모델을 사전 훈련(Pretraining) 후 구체적인 작업을 위해 미세튜닝을 진행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오후_1.32.47.png&quot; data-origin-width=&quot;1742&quot; data-origin-height=&quot;924&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvFqIe/dJMcahwDwh9/oMZfpIzKgKQEuwAXKO7D8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvFqIe/dJMcahwDwh9/oMZfpIzKgKQEuwAXKO7D8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvFqIe/dJMcahwDwh9/oMZfpIzKgKQEuwAXKO7D8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvFqIe%2FdJMcahwDwh9%2FoMZfpIzKgKQEuwAXKO7D8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1742&quot; height=&quot;924&quot; data-filename=&quot;스크린샷_2026-01-07_오후_1.32.47.png&quot; data-origin-width=&quot;1742&quot; data-origin-height=&quot;924&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사전 훈련 모델의 장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대부분의 훈련이 이미 완료되었다는 것&lt;/li&gt;
&lt;li&gt;특정 작업을 위한 미세튜닝은 일반적으로 컴퓨팅 자원을 적게 사용&lt;/li&gt;
&lt;li&gt;BERT 같은 모델은 거의 모든 단계에서 임베딩을 생성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 작업에서 미세튜닝 할 필요 없이 특성 추출기로 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오후_1.15.03.png&quot; data-origin-width=&quot;1660&quot; data-origin-height=&quot;1228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bx9TqL/dJMcaadcDN1/FU2yKxyM3kwqFASqQThaY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bx9TqL/dJMcaadcDN1/FU2yKxyM3kwqFASqQThaY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bx9TqL/dJMcaadcDN1/FU2yKxyM3kwqFASqQThaY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbx9TqL%2FdJMcaadcDN1%2FFU2yKxyM3kwqFASqQThaY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1660&quot; height=&quot;1228&quot; data-filename=&quot;스크린샷_2026-01-07_오후_1.15.03.png&quot; data-origin-width=&quot;1660&quot; data-origin-height=&quot;1228&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델의 구조와 작동 방식의 주요 차이점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인코더 기반 모델&lt;/b&gt; : 임베딩 생성처럼 언어를 표현하는데 초점을 맞추고, 텍스트를 생성하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;디코더 기반 모델&lt;/b&gt; : 텍스트를 생성하는데 초점을 맞추며, 임베딩을 생성하도록 훈련되지 않는다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4-2. 생성모델: 디코더 기반 모델&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디코더는 이전에 나온 단어들을 바탕으로 '다음에 올 단어를 예측'하며 문장을 생성하는 데 최적화되어 있습니다. 현재 대다수의 생성형 AI(ChatGPT 등)가 이 구조를 따릅니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;특징:&lt;/b&gt; &lt;b&gt;단방향(Unidirectional/Causal)&lt;/b&gt; 맥락을 따릅니다. 미래의 단어를 미리 볼 수 없도록 마스킹 처리를 하며, 왼쪽에서 오른쪽으로 순차적으로 텍스트를 생성합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;학습 방식:&lt;/b&gt; 다음 단어를 예측하는 '인과적 언어 모델링(CLM)'을 사용합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대표 모델:&lt;/b&gt; &lt;b&gt;GPT 시리즈(GPT-4, GPT-o1)&lt;/b&gt;, Llama 3, Mistral, Claude&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주요 용도:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자유로운 텍스트 생성 (에세이 작성, 코드 생성)&lt;/li&gt;
&lt;li&gt;대화형 챗봇&lt;/li&gt;
&lt;li&gt;창의적 글쓰기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPT-1 구조&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오후_2.40.22.png&quot; data-origin-width=&quot;1556&quot; data-origin-height=&quot;1368&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TDjG1/dJMcagEu42f/tFA1gRGmWhqGmHokzAgF61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TDjG1/dJMcagEu42f/tFA1gRGmWhqGmHokzAgF61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TDjG1/dJMcagEu42f/tFA1gRGmWhqGmHokzAgF61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTDjG1%2FdJMcagEu42f%2FtFA1gRGmWhqGmHokzAgF61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1556&quot; height=&quot;1368&quot; data-filename=&quot;스크린샷_2026-01-07_오후_2.40.22.png&quot; data-origin-width=&quot;1556&quot; data-origin-height=&quot;1368&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 디코더 기반 생성 모델을 대규모 언어 모델(LLM:Large Language Models) 이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시퀀스-투-시퀀스(Sequence-to-Sequence) 모델로 생성 LLM은 어떤 텍스트를 받아 자동으로 완성&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 모델의 진정한 힘은 챗봇으로 훈련되었을 때 빛을 발한다.&lt;/li&gt;
&lt;li&gt;이런 모델을 미세튜닝하여 지시를 따르는 인스트럭트 모델(Instruct Model) 또는 채팅 모델(Chat Model)을 만들 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만든 모델은 사용자 쿼리(Query:[프롬프트:Prompt])를 입력 받고, 이 프롬프트를 따르는 응답을 출력 할 수 있다. 따라서 종종 생성모델을 완성 모델이라고 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오후_2.44.35.png&quot; data-origin-width=&quot;2012&quot; data-origin-height=&quot;1028&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vzrsg/dJMcahi64WV/Uyk8XSOWgTKezHrkdy8MjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vzrsg/dJMcahi64WV/Uyk8XSOWgTKezHrkdy8MjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vzrsg/dJMcahi64WV/Uyk8XSOWgTKezHrkdy8MjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvzrsg%2FdJMcahi64WV%2FUyk8XSOWgTKezHrkdy8MjK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2012&quot; height=&quot;1028&quot; data-filename=&quot;스크린샷_2026-01-07_오후_2.44.35.png&quot; data-origin-width=&quot;2012&quot; data-origin-height=&quot;1028&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성모델(완성모델)에서 중요한 부분은 &lt;b&gt;문맥 길이(Context Length)&lt;/b&gt; 또는 &lt;b&gt;문맥 윈도(Context Window)&lt;/b&gt; 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문맥 길이는 모델이 처리할 수 있는 최대 토큰 수를 나타낸다. 문맥길이가 클 경우 LLM에 문서를 통채로 전달 할 수 있고, 이런 모델은 자가회귀 성질을 가지고 있어 새로운 토큰이 생성됨에 따라 현재 문맥의 길이가 증가한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오후_2.52.47.png&quot; data-origin-width=&quot;1962&quot; data-origin-height=&quot;1026&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rFQ3F/dJMcahi64W5/LT3CaaFqMeRWJMEei2ZZSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rFQ3F/dJMcahi64W5/LT3CaaFqMeRWJMEei2ZZSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rFQ3F/dJMcahi64W5/LT3CaaFqMeRWJMEei2ZZSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrFQ3F%2FdJMcahi64W5%2FLT3CaaFqMeRWJMEei2ZZSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1962&quot; height=&quot;1026&quot; data-filename=&quot;스크린샷_2026-01-07_오후_2.52.47.png&quot; data-origin-width=&quot;1962&quot; data-origin-height=&quot;1026&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;요약 및 비교&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;width: 72px;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/th&gt;
&lt;th style=&quot;width: 201px;&quot;&gt;&lt;b&gt;인코더 기반 (Encoder-only)&lt;/b&gt;&lt;/th&gt;
&lt;th style=&quot;width: 216px;&quot;&gt;&lt;b&gt;디코더 기반 (Decoder-only)&lt;/b&gt;&lt;/th&gt;
&lt;th style=&quot;width: 231px;&quot;&gt;&lt;b&gt;인코더-디코더 (Full Transformer)&lt;/b&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 72px; text-align: center;&quot;&gt;&lt;b&gt;핵심 목적&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 201px; text-align: center;&quot;&gt;문맥 이해 (NLU)&lt;/td&gt;
&lt;td style=&quot;width: 216px; text-align: center;&quot;&gt;텍스트 생성 (NLG)&lt;/td&gt;
&lt;td style=&quot;width: 231px; text-align: center;&quot;&gt;입력-출력 변환 (Translation)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 72px; text-align: center;&quot;&gt;&lt;b&gt;방향성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 201px; text-align: center;&quot;&gt;양방향 (문장 전체 참조)&lt;/td&gt;
&lt;td style=&quot;width: 216px; text-align: center;&quot;&gt;단방향 (이전 단어만 참조)&lt;/td&gt;
&lt;td style=&quot;width: 231px; text-align: center;&quot;&gt;입력은 양방향, 출력은 단방향&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 72px; text-align: center;&quot;&gt;&lt;b&gt;대표 모델&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 201px; text-align: center;&quot;&gt;BERT&lt;/td&gt;
&lt;td style=&quot;width: 216px; text-align: center;&quot;&gt;GPT, Llama&lt;/td&gt;
&lt;td style=&quot;width: 231px; text-align: center;&quot;&gt;T5, BART&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 72px; text-align: center;&quot;&gt;&lt;b&gt;비유&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 201px; text-align: center;&quot;&gt;독해 능력이 뛰어난 학생&lt;/td&gt;
&lt;td style=&quot;width: 216px; text-align: center;&quot;&gt;소설을 써 내려가는 작가&lt;/td&gt;
&lt;td style=&quot;width: 231px; text-align: center;&quot;&gt;외국어를 통역하는 통역사&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1&gt;&lt;b&gt;대규모 언어 모델의 정의&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 언어 AI역사를 살펴보면 주로 디코더 기반(트랜스포머)의 생성 모델을 일컬어 &lt;b&gt;대규모 언어모델(LLM : Large Language Model)&lt;/b&gt; 이라고 한다. 특히, 규모가 큰 모델일 때 해당.&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;대규모 언어 모델의 훈련 패러다임&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 머신러닝(Matchine Learning)의 분류와 같은 특정 작업을 위해 모델을 훈련한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 한 단계로 구성된 과정으로 간주&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오후_3.02.27.png&quot; data-origin-width=&quot;1952&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vlf92/dJMcadVe70I/CpiKGRzj2iAWUj5K5plyW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vlf92/dJMcadVe70I/CpiKGRzj2iAWUj5K5plyW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vlf92/dJMcadVe70I/CpiKGRzj2iAWUj5K5plyW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvlf92%2FdJMcadVe70I%2FCpiKGRzj2iAWUj5K5plyW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1952&quot; height=&quot;508&quot; data-filename=&quot;스크린샷_2026-01-07_오후_3.02.27.png&quot; data-origin-width=&quot;1952&quot; data-origin-height=&quot;508&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 달리 LLM을 만드는 것은 일반적으로 적어도 두 단계로 구성&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;언어모델 : 사전훈련(Pretraining)이라고 부르며 대부분의 계산과 훈련 시간이 소요
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;인터넷에서 수집한 대규모 텍스트 덩어리에서 LLM을 훈련시켜 모델이 문법, 맥락, 언어 패턴을 학습할 수 있다.&lt;/li&gt;
&lt;li&gt;광범위한 이 훈련 단계는 다음 단어를 예측 하는 것 외에 아직 특정 작업이나 애플리케이션에 맞춰져 있지 않는다.&lt;/li&gt;
&lt;li&gt;이렇게 만들어진 모델을 &lt;b&gt;파운데이션 모델&lt;/b&gt; 또는 &lt;b&gt;베이스 모델 이라고 부르며, 이런 모델은 일반적으로 명령을 따르지 못한다.&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;미세튜닝 : 미세튜닝(fine-tuning) 또는 사후 훈련(post-training)이라고 부르며, 이전에 훈련된 모델을 사용해 구체적인 작업에 맞춰 추가로 훈련시키고, 이를 통해 LLM이 특정 작업에 적응하거나 원하는 행동을 수행할 수 있다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;베이스모델을 미세튜닝하여 분류 작업을 수행하거나 명령을 따르게 할 수 있다.(막대한 자원 절약)&lt;/li&gt;
&lt;li&gt;사전 훈련 단계는 비용이 많이 들고 대부분의 사람과 조직에게는 감당하지 못할 만큼 데이터와 컴퓨팅 자원이 필요&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미세튜닝된 모델도 포함해 첫번 째 단계인 사전 훈련을 통과한 모든 모델을 &lt;b&gt;사전 훈련된 모델(pretrained model)이라고 간주.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2026-01-07_오후_3.09.24.png&quot; data-origin-width=&quot;1842&quot; data-origin-height=&quot;684&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CEo6D/dJMcabXsT8m/BhWhFrsHmJ1uDpaZjHIxs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CEo6D/dJMcabXsT8m/BhWhFrsHmJ1uDpaZjHIxs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CEo6D/dJMcabXsT8m/BhWhFrsHmJ1uDpaZjHIxs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCEo6D%2FdJMcabXsT8m%2FBhWhFrsHmJ1uDpaZjHIxs0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1842&quot; height=&quot;684&quot; data-filename=&quot;스크린샷_2026-01-07_오후_3.09.24.png&quot; data-origin-width=&quot;1842&quot; data-origin-height=&quot;684&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핸즈온 LLM&lt;/p&gt;</description>
      <category>AI/LLM</category>
      <category>AI</category>
      <category>LLM</category>
      <category>언어 AI 역사</category>
      <author>조슈아。</author>
      <guid isPermaLink="true">https://clairdelunes.tistory.com/168</guid>
      <comments>https://clairdelunes.tistory.com/168#entry168comment</comments>
      <pubDate>Tue, 17 Feb 2026 21:03:50 +0900</pubDate>
    </item>
    <item>
      <title>[MongoDB] 1.기본 개념 정리</title>
      <link>https://clairdelunes.tistory.com/167</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;MongoDB&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;문서 지향 오픈소스 NoSQL 데이터베이스&lt;/b&gt;로 키와 연결된 값의 집합으로 이루어진 Documents 이다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비정형 데이터를 JSON 형식으로 저장&lt;/li&gt;
&lt;li&gt;필드, 범위 기반, 문자열 패턴 일치 등 다양한 쿼리를 지원&lt;/li&gt;
&lt;li&gt;확장성이 뛰어나며 데이터 파티셔닝 기능이 내장&lt;/li&gt;
&lt;li&gt;샤딩을 통해 여러 서버에 워크로드를 분산하여 대용량 데이터를 효율적으로 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;MongoDB의 주요 구성 요소와 기능&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Collection(컬렉션) : Document Group으로 RDB에서는 테이블에 해당하며, 동적 스키마를 가진다.&lt;/li&gt;
&lt;li&gt;Document(도큐먼트) : 관계형 데이터베이스에서는 행에 대응&lt;/li&gt;
&lt;li&gt;Mongo Shell(몽고 쉘) : MongoDB 인스턴스와 상호작용하는 JavaScript 쉘&lt;/li&gt;
&lt;li&gt;Sharding(샤딩) : 데이터를 여러 서버로 분산하는 프로세스&lt;/li&gt;
&lt;li&gt;인덱싱, 집계, 파일 스토리지 등의 기능을 제공&lt;/li&gt;
&lt;li&gt;MongoDB는 대규모 데이터 처리, 빠른 확장이 필요한 경우, 또는 데이터 구조가 자주 변경되는 애플리케이션에 적합&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;기본 사용 방법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 데이터베이스 선택 &lt;code&gt;use 'db_name'&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;use library;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 현재 DB에 할당된 데이터베이스를 확인 &lt;code&gt;db&lt;/code&gt; 입력&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;db&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 현재 데이터베이스의 컬렉션 반환 &lt;code&gt;db.[collectionName]&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;db.books&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. DB 목록을 확인할 수 있다. &lt;code&gt;show dbs&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;show dbs;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 현재 데이터베이스의 컬렉션을 확인할 수 있다. &lt;code&gt;show collections&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;show collections;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 데이터베이스의 사용자 확인할 수 있다. &lt;code&gt;show users&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;show users;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 현재 데이터베이스의 상태 확인 &lt;code&gt;db.stats()&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;db.stats()&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;생성(create), 읽기(read), 갱신(update) 삭제(delete) 네 가지 기본적인 작업을 수행&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 생성(create)&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;insertOne() : 1개의 document를 insert&lt;/h4&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;book = {
    &quot;_id&quot;: ObjectId(), 
    &quot;title&quot; : &quot;MongoDB&quot;, 
    &quot;author&quot;: &quot;DevPaik&quot;, 
    &quot;publication_year&quot;: 2024
    }
db.books.insertOne(book);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;insertMany() : 여러 Document를 insert&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 Document는 16MB 보다 작아야 한다.&lt;/li&gt;
&lt;li&gt;도큐먼트 option에 'ordered' 키에 true 를 지정하면 도큐먼트가 제공된 순서대로 삽입된다. false를 지정하면 MongoDB가 성능을 개선하려고 삽입을 재배열할 수 있다.(기본 값 정렬된 삽입)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;bookList = [
    { &quot;title&quot; : &quot;MySQL&quot;,
      &quot;author&quot;: &quot;DevPaik&quot;,
      &quot;publication_year&quot;: 2024
      },
   { &quot;title&quot; : &quot;MongoDB&quot;,
     &quot;author&quot;: &quot;DevPaik&quot;,
      &quot;publication_year&quot;: 2024
    }
]
db.books.insertMany(
    bookList
    ,{&quot;ordered&quot;: true}
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 읽기(read)&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;findOne() - 단일 도큐먼트 조회&lt;/h4&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;db.books.findOne();
db.books.findOne({title: &quot;MongoDB&quot; });&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;find() - 여러건 조회&lt;/h4&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;db.books.find({title: &quot;MongoDB&quot; });&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 갱신(update)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키를 추가, 변경, 삭제할 때는 항상 &lt;code&gt;$&lt;/code&gt; 제한자를 사용한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;updateOne() : 단건 업데이트&lt;/h4&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;db.books.updateOne(
    { title: &quot;MongoDB&quot; },
    { $set: {author: &quot;Chris&quot;} }
);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;updateMany() : 여러 건 업데이트&lt;/h4&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;db.books.updateMany(
    { &quot;title&quot;: &quot;MongoDB&quot; },
    { $set: { &quot;reviews&quot;: &quot;재미있어용&quot; } }
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$set&lt;/code&gt;은 필드 값을 설정하고 필드가 존재하지 않을 경우 새 필드가 생성된다. (스키마를 갱신하거나 사용자 정의 키를 추가할 때 편리)&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;db.books.updateOne({title: &quot;MongoDB&quot;}, {$set: {date: new Date()}});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$unset&lt;/code&gt;으로 키와 값을 제거할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;db.books.updateOne({title: &quot;MongoDB&quot;}, {$unset: {date: null}});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$inc&lt;/code&gt; 연산자 : 이미 존재하는 키 값을 변경하거나 새 키를 생성하는데 사용&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;db.books.updateOne({title: &quot;MongoDB&quot;}, {$inc: {loan : 1}})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;갱신 요청이 동시 발생 시 서버에 먼저 도착한 요청이 적용된 후 다믕 요청이 적용되므로, 여러개의 갱신 요청이 빨리 발생하더라도 결국 마지막 요청이 최종 값으로 변경된다. (기본 동작을 원하지 않을 경우 : Document 비저닝 패턴을 고려)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ReplaceOne() : 도큐먼트를 새로운 것으로 완전히 치환 (스키마 마이그레이션 시 유용)&lt;/h4&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;var book = db.books.findOne({&quot;title&quot; : &quot;MongoDB&quot;});
var book = db.books.findOne({_id : ObjectId(&quot;66dfd5bf28a9a762d47dc0ce&quot;)});
book.reviews = &quot;재미있다!&quot;;
db.books.replaceOne({_id : ObjectId(&quot;66dfd5bf28a9a762d47dc0ce&quot;)}, book);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$push&lt;/code&gt;는 배열이 존재할 경우 배열 끝에 요소를 추가하고 존재하지 않을 경우 새로운 배열을 생성한다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;db.books.updateMany(
    { title: &quot;MongoDB&quot; },
    { $push : { &quot;categories&quot; : &quot;IT&quot;} }
);

db.books.updateMany(
    { title: &quot;MongoDB&quot; },
    { $push : { &quot;categories&quot; : &quot;COMPUTER&quot;} }
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$push&lt;/code&gt;에 &lt;code&gt;$each&lt;/code&gt; 연산자를 이용하면 한번에 여러 값을 추가할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;db.books.updateMany(
    { title: &quot;MongoDB&quot; },
    { $push : { &quot;categories&quot; :{ $each :  [&quot;DB&quot;, &quot;NO_SQL&quot;] } } }
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$slice&lt;/code&gt;와 &lt;code&gt;$push&lt;/code&gt;를 결합하여 배열이 특정 크기 이상으로 늘어나지 않게 할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;db.books.updateMany(
    { title: &quot;MongoDB&quot; },
    { $push : { 
                &quot;categories&quot; :
                        { $each :  [&quot;DB&quot;, &quot;NO_SQL&quot;],
                          $slice: -10  //10 제한
                      }
                 } 
             } 
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$pop&lt;/code&gt; : 배열 요소 제거&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;db.books.updateMany(
    { title: &quot;MongoDB&quot; },
    { $pull : { &quot;categories&quot; :&quot;NO_SQL&quot; } }
);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 삭제(delete) - 도큐먼트를 DB에서 삭제&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;deleteOne() : 단건 삭제&lt;/h4&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;db.books.deleteOne({title: &quot;MongoDB&quot;});&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;deleteMany() : 여러 건 삭제&lt;/h4&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;db.books.deleteMany({title: &quot;MongoDB&quot; });&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;drop() : 전체 컬렉션 삭제&lt;/h4&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;db.books.drop()&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기본 데이터형&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;null : 존재하지 않는 필드를 표현하는데 사용&lt;/h4&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{ &quot;author&quot;: null}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Boolean : true, false 에 사용&lt;/h4&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;{isDelete: true)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;숫자(Number) : 64비트 부동소수점 수를 기본으로 사용&lt;/h4&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;{price: 1000}
{price :1000.5}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4바이트 혹은 8바이트 부호 정수 : NumberInt, NumberLong 클래스를 사용&lt;/h4&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;{price: NumberInt(&quot;1000&quot;)}
{price :NumberLong(&quot;100000&quot;)}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;문자열&lt;/h4&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{&quot;title&quot;: &quot;MongoDB&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;날짜&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1970 1월 1일부터의 시간을 1/1000초 단위로 나타내는 64비트 정수로 날짜를 저장하며, 표준시간 대는 저장하지 않는다.&lt;/p&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;{&quot;date&quot;: new Date()}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;정규표현식&lt;/h4&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{&quot;regex&quot;: /MongoDB/i}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;배열 : Set or list 를 배열로 표현&lt;/h4&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{&quot;data&quot;: [ {&quot;title&quot; : &quot;MySQL&quot;, &quot;author&quot;: &quot;DevPaik&quot;, &quot;publication_year&quot;: 2024}, 
                     {&quot;title&quot; : &quot;MongoDB&quot;, &quot;author&quot;: &quot;DevPaik&quot;, &quot;publication_year&quot;: 2024}
                    ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;내장 도큐먼트 : 도큐먼트는 부모 도큐먼트의 값으로 내장된 도큐먼테 전체를 포함가능&lt;/h4&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;{data: {&quot;title&quot; : &quot;MySQL&quot;, &quot;author&quot;: &quot;DevPaik&quot;, &quot;publication_year&quot;: 2024}}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;객체 ID : 도큐먼트용 12바이트 ID&lt;/h4&gt;
&lt;pre class=&quot;clojure&quot;&gt;&lt;code&gt;{&quot;_id&quot; : ObjectId()}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;코드 : 쿼리와 도큐먼트는 임의의 자바 스크립트 코드를 포함 할 수 있다.&lt;/h4&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;{&quot;script&quot;: function() { return this.title + &quot; by &quot; + this.author; } }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MongoDB는 하나의 컬렉션에서 모든 Document는 &lt;code&gt;_id Key&lt;/code&gt;를 가지며 &lt;code&gt;ObjectId()&lt;/code&gt;를 기본으로 생성한다.&lt;/p&gt;</description>
      <category>Database/MongoDB</category>
      <category>MongoDB</category>
      <category>기본정리</category>
      <author>조슈아。</author>
      <guid isPermaLink="true">https://clairdelunes.tistory.com/167</guid>
      <comments>https://clairdelunes.tistory.com/167#entry167comment</comments>
      <pubDate>Tue, 10 Sep 2024 22:47:20 +0900</pubDate>
    </item>
    <item>
      <title>15. 그래프 최단경로(다익스트라, 벨만-포드)</title>
      <link>https://clairdelunes.tistory.com/166</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;최단경로(shortest path) 문제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크에서 정점 u 와 정점 v 를 연결하는 경로 중에서 간선들의 가중치 합이 최소가되는 경로를 찾는 문제&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;가중치&lt;/b&gt; : 거리, 비용, 시간 등&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가중치가 없는 그래프 :&lt;/b&gt; 간선의 개수가 가장 적은 경로가 최단경로&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가중치가 있는 그래프 :&lt;/b&gt; 시작노드에서 마지막 노드까지 이동할 때 거치는 간선의 가중치의 총합이 최소가 되는 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;다익스트라 알고리즘(Dijkstra algorithm)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크에서 하나의 시작정점에서 모든 다른 정점까지의 최단경로를 찾는 알고리즘&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가중치가 있는 알고리즘은 대부분 다익스트라 알고리즘을 사용한다고 보면된다.&lt;/li&gt;
&lt;li&gt;다익스트라 알고리즘은 양의 가중치만 있는 그래프에서만 동작하므로 음의 가중치가 있는 그래프에서 제대로 동작하지 않는다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;다익스트라 알고리즘은 한 번 방문한 노드는 다시 방문하지 않는다. 그러므로 음의 가중치가 있는 그래프에서는 제대로 작동하지 않는것.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;시작노드를 설정하고 시작 노드부터 특정 노드까지 최소 비용을 저장할 공간과 직전노드를 저장할 공간을 마련한다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;최소비용을 저장할 공간은 모두 매우 큰 값(무한대 INF 표현)으로 초기화&lt;/li&gt;
&lt;li&gt;시작노드의 최소 비용은 0이고 직전 노드는 자신으로 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;해당 노드를 통해 방문할 수 있는 노드(아직 방문하지 않은 노드) 중 현재까지 구한 최소 비용이 가작 적은 노드를 선택
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;해당 노드를 거쳐 각 노드까지 가는 최소 비용과 현재가지 구한 최소 비용을 비교하여 작은 값을 각 노드의 최소비용으로 갱신&lt;/li&gt;
&lt;li&gt;이때 직전 노드도 같이 갱신&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;노드의 개수에서 1을 뺀 만큼 반복&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;scss&quot;&gt;&lt;code&gt;//입력 : 가중치 그래프 G, 가중치는 음수가 아님
//출력 : 출력 distance 배열, distance[u]는 v에서 u까지의 최단 거리이다.
//Big O (n^2)
shortest_path(G, v)

s &amp;larr; {v}
  for 각 정점 w &amp;sube; G do
    distance[w] &amp;lt;- weight[v][w];
  while 모든 정점이 S에 포함되지 않으면 do
    u &amp;lt;- 집합 S에 속하지 않는 정점 중에서 최소 distance 정점;
    S &amp;lt;- S &amp;cup; {u}
    for u에 인접하지 않고 S에 인접하지 않는 각 정점 z do
      if distance[u] + weight[u][z] &amp;lt; distance[z]
         then distance[z] &amp;lt;- distance[u] + weight[u][z]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;알고리즘 절차&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 시작노드 A의 최소비용은 0 직전노드는 A로 초기화&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_5E79219458EF-1.jpeg&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;1225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eVbW6X/btsHTatzeYd/RsAvIDysBghchC1QBWUr5k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eVbW6X/btsHTatzeYd/RsAvIDysBghchC1QBWUr5k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eVbW6X/btsHTatzeYd/RsAvIDysBghchC1QBWUr5k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeVbW6X%2FbtsHTatzeYd%2FRsAvIDysBghchC1QBWUr5k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;999&quot; height=&quot;1225&quot; data-filename=&quot;IMG_5E79219458EF-1.jpeg&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;1225&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 방문하지 않은 노드 중 최소비용이 가장 적은 A노드를 선택&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 노드를 거쳐 각 노드까지 가는 비용과 기존에 구한 각 노드의 최소 비용을 비교&lt;/li&gt;
&lt;li&gt;A 노드에서 {B, C, E}의 가중치는 각각 {4, 4, 1}&lt;/li&gt;
&lt;li&gt;현재까지 해당 노드의 최소 비용은 INF이므로 B의 최소비용은 4, C의 최소비용은 4, E의 최소비용은 1로 갱신&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_9FD0172C10AC-1.jpeg&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oLDg3/btsHUNwVFXN/fK0s5qpSjsNju1eq2LqD0K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oLDg3/btsHUNwVFXN/fK0s5qpSjsNju1eq2LqD0K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oLDg3/btsHUNwVFXN/fK0s5qpSjsNju1eq2LqD0K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoLDg3%2FbtsHUNwVFXN%2FfK0s5qpSjsNju1eq2LqD0K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;1318&quot; data-filename=&quot;IMG_9FD0172C10AC-1.jpeg&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 방문하지 않은 노드 중 최소 비용이 가장 적은 노드 E를 선택&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;선택한 노드를 거쳤을 때 최소 비용을 갱신할 수 있는지 확인.&lt;/li&gt;
&lt;li&gt;노드 C의 현재 비용은 4, E를 거쳤을 때의 비용은 E의 최소비용(1)과 E &amp;rarr; C의 가중치(2)를 합친 값 3&lt;/li&gt;
&lt;li&gt;현재까지 구한 최소 비용보다 값이 더 작으므로 C의 최소비용3, 직전노드 E 로 수정&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_51D3BD742FBD-1.jpeg&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjW6zc/btsHUxnLFOy/nNEx2yekfbKtHf6dfkNkS0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjW6zc/btsHUxnLFOy/nNEx2yekfbKtHf6dfkNkS0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjW6zc/btsHUxnLFOy/nNEx2yekfbKtHf6dfkNkS0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjW6zc%2FbtsHUxnLFOy%2FnNEx2yekfbKtHf6dfkNkS0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;1312&quot; data-filename=&quot;IMG_51D3BD742FBD-1.jpeg&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1312&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4.방문하지 않은 노드 중 최소비용이 가장 적은 노드 C를 선택&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;선택한 노드를 거쳤을 때 최소 비용을 갱신할 수 있는지 확인&lt;/li&gt;
&lt;li&gt;노드 D의 경우 기존 최소비용이 INF(경로 없음)이지만, C를 거치면 최소비용(3) + C &amp;rarr; D 의 가중치(8)을 합쳐 11이 되어 INF보다 작다.&lt;/li&gt;
&lt;li&gt;그러므로 D는 최소비용 11, 직전노드 C로 갱신&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_B20FC7F245C5-1.jpeg&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1245&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6UKfd/btsHUNcDHlj/ZMlpfh11kJ9yy2jhxF1Ltk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6UKfd/btsHUNcDHlj/ZMlpfh11kJ9yy2jhxF1Ltk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6UKfd/btsHUNcDHlj/ZMlpfh11kJ9yy2jhxF1Ltk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6UKfd%2FbtsHUNcDHlj%2FZMlpfh11kJ9yy2jhxF1Ltk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;1245&quot; data-filename=&quot;IMG_B20FC7F245C5-1.jpeg&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1245&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 방문하지 않은 노드 중 최소비용이 가장 적은 노드 B를 선택&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;선택한 노드를 거쳤을 때 최소 비용을 갱신할 수 있는지 확인&lt;/li&gt;
&lt;li&gt;확인 할 필요가 없으므로 건너뜀(갱신 없음)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_2C48786DBF67-1.jpeg&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYlC5E/btsHUdC8WYQ/TcvposVFo55zkMnvnAZev1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYlC5E/btsHUdC8WYQ/TcvposVFo55zkMnvnAZev1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYlC5E/btsHUdC8WYQ/TcvposVFo55zkMnvnAZev1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYlC5E%2FbtsHUdC8WYQ%2FTcvposVFo55zkMnvnAZev1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;1200&quot; data-filename=&quot;IMG_2C48786DBF67-1.jpeg&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 방문하지 않은 노드 중 최소 비용이 가장 적은 노드 D를 방문&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;확인 할 필요가 없으므로 건너뜀(갱신 없음)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_00105A0376FA-1.jpeg&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1237&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9FerB/btsHUSEXWxF/oi7VQkhYVKds6DlHj782AK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9FerB/btsHUSEXWxF/oi7VQkhYVKds6DlHj782AK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9FerB/btsHUSEXWxF/oi7VQkhYVKds6DlHj782AK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9FerB%2FbtsHUSEXWxF%2Foi7VQkhYVKds6DlHj782AK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;1237&quot; data-filename=&quot;IMG_00105A0376FA-1.jpeg&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1237&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import java.util.*;
public class DijkstraAlgorithm {

    public static void main(String[] args) {
        Solution sol = new Solution();
        int[] result = sol.solution(new int[][] {
                {0,1,9},
                {0,2,3},
                {1,0,5},
                {2,1,1}
        }, 0, 3);
        System.out.println(Arrays.toString(result));

        int[] result2 = sol.solution(new int[][] {
                {0,1,1},
                {1,2,5},
                {2,3,1}
        }, 0, 4);
        System.out.println(Arrays.toString(result2));
    }

    private static class Solution {
        public int[] solution(int[][] graph, int start, int n) {
            return dijkstra(start, n, initAdjList(graph, n));
        }

        private List&amp;lt;List&amp;lt;Node&amp;gt;&amp;gt; initAdjList(int[][] graph, int n) {
            List&amp;lt;List&amp;lt;Node&amp;gt;&amp;gt; adjList = new ArrayList&amp;lt;&amp;gt;(n);

            //인접리스트 초기화
            for (int i = 0; i &amp;lt; n; i ++) {
                adjList.add(new ArrayList&amp;lt;&amp;gt;());
            }

            //graph 정보 인접 리스트로 저장(단방향)
            for (int[] edge : graph) {
                adjList.get(edge[0]).add(new Node(edge[1], edge[2]));
            }
            return adjList;
        }

        private int[] dijkstra(int start, int n, List&amp;lt;List&amp;lt;Node&amp;gt;&amp;gt; adjList) {
            int[] dist = new int[n];
            Arrays.fill(dist, Integer.MAX_VALUE); //INF 로 초기화

            //시작 위치 0으로 초기화
            dist[start] = 0;

            //우선순위 큐를 생성하고 시작 노드를 삽입
            PriorityQueue&amp;lt;Node&amp;gt; priorityQueue = new PriorityQueue&amp;lt;&amp;gt;(Comparator.comparingInt(o -&amp;gt; o.cost));
            priorityQueue.add(new Node(start, 0));

            while(!priorityQueue.isEmpty()) {
                //거리가 가장 짧은 노드를 dequeue
                Node now = priorityQueue.poll();

                //현재노드의 거리 값이 큐에서 거리 값보다 크면, 해당 노드는 이미 방문한 것으로 무시
                if (dist[now.dest] &amp;lt; now.cost) {
                    continue;
                }

                //현재 노드와 인접한 노드들의 거리 값을 계산하여 업데이트
                for (Node next : adjList.get(now.dest)) {
                    //기존에 발견했던 거리보다 더 짧은 거리를 발견했다면 거리 값을 갱신하고 큐에 넣음
                    if (dist[next.dest] &amp;gt; now.cost + next.cost) {
                        dist[next.dest] = now.cost + next.cost;
                        priorityQueue.add(new Node(next.dest, dist[next.dest]));
                    }
                }
            }
            return dist;
        }

        private static class Node {
            int dest;
            int cost;

            public Node(int dest, int cost) {
                this.dest = dest;
                this.cost = cost;
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;벨만-포드 알고리즘(Bellman-ford Algorithm)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다익스트라 알고리즘과 마찬가지로 노드에서 노드까지의 최소 비용을 구하는 알고리즘으로, 매 단계마다 모든 간선의 가중치를 다시 확인하여 최소 비용을 갱신하므로 &lt;b&gt;음의 가중치&lt;/b&gt;를 가지는 그래프에서도 최단경로를 구할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;간선의 가중치가 음수인 일반적인 경우 단일 출발지 최단 경로 문제를 해결
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가중 방향 그래프 G= (V,E)가 출발점 s와 가중함수 w : E &amp;rarr; R 과 함께 주어졌을 때 벨만-포드 알고리즘은 출발점으로부터 도달 가능한 음의 가중치를 갖는 순한이 있는지를 나타내는 논리값을 리턴&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;시작 노드를 설정한 다음 시작 노드의 최소비용은 0, 나머지 노드는 INF로 초기화
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이후 최소비용을 갱신할 때마다 직전노드도 갱신&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;노드의 개수 - 1&lt;/b&gt;만큼 연산을 반복
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시작노드에서 갈 수 있는 각 노드에 대해 전체 노드 각각을 거쳐갈 때 현재까지 구한 최소 비용보다 더 적은 최소 비용이 있는지 확인하여 갱신&lt;/li&gt;
&lt;li&gt;최소비용을 갱신할 때 V의 직전 노드 값도 같이 갱신&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;과정 2를 마지막으로 한번 더 수행하여 갱신되는 최소 비용이 있는지 확인.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 있다면 음의 순환이 있음을 의미&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;bellman_ford(G, w, s)
 initialize_single_source(G,s)
 for i = 1 to | G.V | - 1
   for 각 간선(u, v) &amp;lt;= G.E
     RELAX(u,v,w)
 for 각 간선(u,v) &amp;lt;= G.E
   if v.d &amp;gt; u.d + w(u.v)
	   return FALSE
return TRUE
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;알고리즘 절차&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 시작노드 A 기준 최소비용 0, 직전노드 0, 나머지 노드 INF로 초기화&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7BA26F114E81-1.jpeg&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1389&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmpWhf/btsHUqvskXr/lCQGhKkliT8Dd37Dyv6zB1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmpWhf/btsHUqvskXr/lCQGhKkliT8Dd37Dyv6zB1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmpWhf/btsHUqvskXr/lCQGhKkliT8Dd37Dyv6zB1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmpWhf%2FbtsHUqvskXr%2FlCQGhKkliT8Dd37Dyv6zB1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;1389&quot; data-filename=&quot;IMG_7BA26F114E81-1.jpeg&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1389&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 노드 A에서 A를 거쳐 각 노드 {B, C, E}까지 가는 비용중 현재까지 구한 최소 비용보다 적은 값이 있는지 확인하고 현재까지 구한 최소비용보다 적으면 갱신 (간선이 없다면 INF로 계산한다는 것을 주의)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최소비용(A)(0) == 최소비용(A)(0) + 간선(A, A)(0) : 갱신하지 않음&lt;/li&gt;
&lt;li&gt;최소비용(B)(INF) &amp;gt; 최소비용(A)(0) + 간선(A, B)(4) : 최소비용(B)를 INF에서 4로 갱신&lt;/li&gt;
&lt;li&gt;최소비용(C)(INF) &amp;gt; 최소비용(A)(0) + 간선(A, C)(3) : 최소비용(C)를 INF에서 3로 갱신&lt;/li&gt;
&lt;li&gt;최소비용(D)(INF) == 최소비용(A)(0) + 간선(A, D)(INF) : 갱신하지 않음&lt;/li&gt;
&lt;li&gt;최소비용(E)(INF) &amp;gt; 최소비용(A)(0) + 간선(A, E)(-6) : 최소비용(E)를 INF에서 -6로 갱신&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_5AEB46F30BE6-1.jpeg&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1402&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bad4JK/btsHUymFuu6/6kAAUenOh0qkP7HJtzzF61/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bad4JK/btsHUymFuu6/6kAAUenOh0qkP7HJtzzF61/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bad4JK/btsHUymFuu6/6kAAUenOh0qkP7HJtzzF61/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbad4JK%2FbtsHUymFuu6%2F6kAAUenOh0qkP7HJtzzF61%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;1402&quot; data-filename=&quot;IMG_5AEB46F30BE6-1.jpeg&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1402&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 노드 A에서 B를 거쳐 각 노드 {D}까지 가는 비용중 현재까지 구한 최소 비용보다 적은 값이 있는지 확인하고 현재까지 구한 최소비용보다 적으면 갱신&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최소비용(A)(0) &amp;lt; 최소비용(B)(4) + 간선(B, A)(INF) : 갱신하지 않음&lt;/li&gt;
&lt;li&gt;최소비용(B)(4) == 최소비용(B)(4) + 간선(B, B)(0) : 갱신하지 않음&lt;/li&gt;
&lt;li&gt;최소비용(C)(3) &amp;lt; 최소비용(B)(4) + 간선(B, C)(INF) : 갱신하지 않음&lt;/li&gt;
&lt;li&gt;최소비용(D)(INF) &amp;gt; 최소비용(B)(4) + 간선(B, D)(5) : 9로 갱신&lt;/li&gt;
&lt;li&gt;최소비용(E)(-6) &amp;lt; 최소비용(B)(4) + 간선(B, E)(-6) : 갱신하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_EC143DBD655D-1.jpeg&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1425&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/C4ePh/btsHUHDFP6r/hZ9VuJVV0W4ZvYYyU13UYk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C4ePh/btsHUHDFP6r/hZ9VuJVV0W4ZvYYyU13UYk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C4ePh/btsHUHDFP6r/hZ9VuJVV0W4ZvYYyU13UYk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC4ePh%2FbtsHUHDFP6r%2FhZ9VuJVV0W4ZvYYyU13UYk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;1425&quot; data-filename=&quot;IMG_EC143DBD655D-1.jpeg&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1425&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 노드 A에서 C를 거쳐 각 노드 {B}까지 가는 비용중 현재까지 구한 최소 비용보다 적은 값이 있는지 확인하고 현재까지 구한 최소비용보다 적으면 갱신 (C를 거쳐 B로 가는 새 경로(5)는 기존 B(4)의 최소 비용보다 크므로 갱신 X)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최소비용(A)(0) &amp;lt; 최소비용(C)(3) + 간선(C, A)(INF) : 갱신하지 않음&lt;/li&gt;
&lt;li&gt;최소비용(B)(4) &amp;lt; 최소비용(C)(3) + 간선(C, B)(2) : 갱신하지 않음&lt;/li&gt;
&lt;li&gt;최소비용(C)(3) == 최소비용(C)(3) + 간선(C, C)(0) : 갱신하지 않음&lt;/li&gt;
&lt;li&gt;최소비용(D)(9) &amp;gt; 최소비용(C)(3) + 간선(C, D)(INF) : 갱신하지 않음&lt;/li&gt;
&lt;li&gt;최소비용(E)(-6) &amp;lt; 최소비용(C)(3) + 간선(C, E)(INF) : 갱신하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_84467D673173-1.jpeg&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;1418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3XZuw/btsHTUqfRVk/S5ew9pSLKc5EIFwYKRagR0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3XZuw/btsHTUqfRVk/S5ew9pSLKc5EIFwYKRagR0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3XZuw/btsHTUqfRVk/S5ew9pSLKc5EIFwYKRagR0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3XZuw%2FbtsHTUqfRVk%2FS5ew9pSLKc5EIFwYKRagR0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;999&quot; height=&quot;1418&quot; data-filename=&quot;IMG_84467D673173-1.jpeg&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;1418&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 노드 A에서 D를 거쳐서 가는 방법은 없으므로 갱신하지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 노드 A에서 E를 거쳐 각 노드까지 가는 최소비용을 갱신. 모든 최단 경로에 대해 노드의 최소비용을 체크했으므로, 벨만-포드 알고리즘의 첫번째 반복이 끝난다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최소비용(A)(0) &amp;lt; 최소비용(E)(-6) + 간선(E, A)(INF) : 갱신하지 않음&lt;/li&gt;
&lt;li&gt;최소비용(B)(4) &amp;lt; 최소비용(E)(-6) + 간선(E, B)(INF) : 갱신하지 않음&lt;/li&gt;
&lt;li&gt;최소비용(C)(3) &amp;gt; 최소비용(E)(-6) + 간선(E, C)(2) : -4로 갱신&lt;/li&gt;
&lt;li&gt;최소비용(D)(9) &amp;gt; 최소비용(E)(-6) + 간선(E, D)(INF) : 갱신하지 않음&lt;/li&gt;
&lt;li&gt;최소비용(E)(-6) &amp;lt; 최소비용(E)(-6) + 간선(E, E)(0) : 갱신하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_CDFB45F4E42E-1.jpeg&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1267&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Bsn15/btsHT9HtYEl/3BkKvjGoO65kKX9QVoafM1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Bsn15/btsHT9HtYEl/3BkKvjGoO65kKX9QVoafM1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Bsn15/btsHT9HtYEl/3BkKvjGoO65kKX9QVoafM1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBsn15%2FbtsHT9HtYEl%2F3BkKvjGoO65kKX9QVoafM1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;1267&quot; data-filename=&quot;IMG_CDFB45F4E42E-1.jpeg&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1267&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;위해서 했던 과정을 노드개수 -1 번 반복하므로 1~6단계를 4번 더 반복&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class BellManFord {

    public static void main(String[] args) {
        Solution sol = new Solution();
        int[] result = sol.solution(new int[][] {
                {1,2,7},
                {1,3,5},
                {1,4,3},
                {3,4,3},
                {3,5,3},
                {4,2,3},
                {4,5,-6},
                {5,3,2},
        }, 1, 7);
        System.out.println(Arrays.toString(result));

        int[] result2 = sol.solution2(new int[][] {
                {1,2,7},
                {1,3,5},
                {1,4,3},
                {3,4,3},
                {3,5,3},
                {4,2,3},
                {4,5,-6},
                {5,3,2},
        }, 1, 7);
        System.out.println(Arrays.toString(result2));
    }
    private static class Solution {
        private static final int INF = Integer.MAX_VALUE;

        public int[] solution(int[][] graph, int start, int n) {
            List&amp;lt;List&amp;lt;Node&amp;gt;&amp;gt; adjList = initAdjList(graph, n);

            int[] dist = new int[n];
            Arrays.fill(dist, INF);
            dist[start] = 0;

            //정점 수 만큼 반복
            for (int v = 1; v &amp;lt;= n; v++) {
                //모든 간선을 순회
                for (Node node : adjList.get(v)) {
                    if (dist[node.from] == INF) {
                        continue;
                    }

                    if (dist[node.to] &amp;gt; dist[node.from] + node.cost) {
                        dist[node.to] = dist[node.from] + node.cost;
                    }
                }
            }
            return dist;
        }
        private int[] solution2(int[][] graph, int start, int n) {
            List&amp;lt;List&amp;lt;Node&amp;gt;&amp;gt; adjList = initAdjList(graph, n);

            int[] dist = new int[n];
            Arrays.fill(dist, INF);
            dist[start] = 0;

            //정점 수 만큼 반복
            for (int v = 1; v &amp;lt;= n; v++) {
                //모든 간선을 순회
                for (Node node : adjList.get(v)) {
                    if (dist[node.from] != INF) {
                        dist[node.to] = Math.min(dist[node.to], dist[node.from] + node.cost);
                    }
                }
            }
            return dist;
        }
        private List&amp;lt;List&amp;lt;Node&amp;gt;&amp;gt; initAdjList(int[][] graph, int n) {
            List&amp;lt;List&amp;lt;Node&amp;gt;&amp;gt; adjList = new ArrayList&amp;lt;&amp;gt;();

            for (int i = 0; i &amp;lt;= n; i ++) {
                adjList.add(new ArrayList&amp;lt;&amp;gt;());
            }

            for (int[] edge : graph) {
                adjList.get(edge[0]).add(new Node(edge[0], edge[1], edge[2]));
            }
            return adjList;
        }

        private static class Node {
            int from;
            int to;
            int cost;

            public Node(int from, int to, int cost) {
                this.from = from;
                this.to = to;
                this.cost = cost;
            }
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다익스트라 알고리즘, 벨만 포드 알고리즘 차이&lt;/h2&gt;
&lt;table id=&quot;c9713ad8-b0ca-4aa5-89e5-9afa27beacf6&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 10.9302%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 17.4418%;&quot;&gt;목적&lt;/td&gt;
&lt;td style=&quot;width: 46.1628%;&quot;&gt;장 단점 및 특성&lt;/td&gt;
&lt;td style=&quot;width: 25.3488%;&quot;&gt;시간 복잡도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;5b15d7ea-c770-404b-9125-15e6e0ae419e&quot;&gt;
&lt;td style=&quot;width: 10.9302%;&quot;&gt;다익스트라&lt;br /&gt;알고리즘&lt;/td&gt;
&lt;td id=&quot;X&amp;#96;;E&quot; style=&quot;width: 17.4418%;&quot;&gt;출발 노드부터 도착 노드들까지의 최단 경로 찾기&lt;/td&gt;
&lt;td id=&quot;XgRE&quot; style=&quot;width: 46.1628%;&quot;&gt;음의 가중치를 가지는 그래프에서 최단 경로를 구할 수 없다(그리디 방식)&lt;/td&gt;
&lt;td id=&quot;~nBA&quot; style=&quot;width: 25.3488%;&quot;&gt;O(V^2), 우선순위 큐로 개선하면 O(E*logV)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;25e9deb4-bb1b-413a-a118-6e5195802fcf&quot;&gt;
&lt;td style=&quot;width: 10.9302%;&quot;&gt;벨만-포드&lt;br /&gt;알고리즘&lt;/td&gt;
&lt;td id=&quot;X&amp;#96;;E&quot; style=&quot;width: 17.4418%;&quot;&gt;출발 노드부터 도착노드들까지의 최단 경로 찾기&lt;/td&gt;
&lt;td id=&quot;XgRE&quot; style=&quot;width: 46.1628%;&quot;&gt;음의 가중치를 가지는 그래프에서 최단 경로를 구할 수 있고, 음의 순환도 감지할 수 있다.&lt;/td&gt;
&lt;td id=&quot;~nBA&quot; style=&quot;width: 25.3488%;&quot;&gt;O(V*E)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;References&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C 언어로 쉽게 풀어쓴 자료 구조&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩 테스트 완전 전복&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Interotuction to algorithms&lt;/p&gt;</description>
      <category>Algorithms</category>
      <category>Algorithm</category>
      <category>bellman-ford algorithm</category>
      <category>belman-ford</category>
      <category>Dijkstra</category>
      <category>Dijkstra algorithm</category>
      <category>다익스트라</category>
      <category>다익스트라 알고리즘</category>
      <category>벨만-포드 알고리즘</category>
      <category>벨만포드</category>
      <category>알고리즘</category>
      <author>조슈아。</author>
      <guid isPermaLink="true">https://clairdelunes.tistory.com/166</guid>
      <comments>https://clairdelunes.tistory.com/166#entry166comment</comments>
      <pubDate>Tue, 11 Jun 2024 00:30:57 +0900</pubDate>
    </item>
    <item>
      <title>14. Spanning Tree(신장트리) [Kruskal, Prim]</title>
      <link>https://clairdelunes.tistory.com/165</link>
      <description>&lt;h1&gt;&lt;b&gt;Spanning Tree(신장트리)&lt;/b&gt;&lt;/h1&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;그래프 내의 모든 정점을 포함하는 트리&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
 &lt;li&gt;모든 정점들이 연결되어 있어야하고, 사이클을 포함해서는 안된다.&lt;/li&gt; 
 &lt;li&gt;그래프의 최소 연결 부분 그래프이다. 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;최소연결 = 간선의 수가 가장 적다&lt;/li&gt; 
   &lt;li&gt;그래프에 있는 n개의 정점(vertax)을 가지는 그래프의 최소 간선(edge) 수는 (n-1)개이고, (n-1)개의 간선으로 연결되 있으면 필연적으로 트리형태가 되므로 Spanning Tree가 되므로 즉, 그래프에서 일부 간선을 선택해서 만든 트리이다.&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
 &lt;li&gt;신장트리는 깊이 우선 탐색이나 너비 우선 탐색 을 이용하여 사용된 간선만 모으면 만들 수 있다.&lt;/li&gt; 
 &lt;li&gt;하나의 그래프에 많은 신장 트리가 존재할 수 있다.&lt;/li&gt; 
&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;[시작 정점을 바꾸어 가면서 깊이 우선 탐색을 하여 만든 신장 트리 중 일부]&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1882&quot; data-origin-height=&quot;1162&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8Kkvh/btsHPPbsPbf/SMx9gK8xzTMRX6wTiq9sH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8Kkvh/btsHPPbsPbf/SMx9gK8xzTMRX6wTiq9sH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8Kkvh/btsHPPbsPbf/SMx9gK8xzTMRX6wTiq9sH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8Kkvh%2FbtsHPPbsPbf%2FSMx9gK8xzTMRX6wTiq9sH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1882&quot; height=&quot;1162&quot; data-origin-width=&quot;1882&quot; data-origin-height=&quot;1162&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;깊이우선 탐색알고리즘을 이용하여 만든 신장 트리 알고리즘&lt;/p&gt;&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;depth_first_search(v)

	v를 방문되었다고 표시:
	for all u &amp;lt;= (v에 인접한 정점) do
		then (v, u)를 신장 트리 간선이라고 표시;
				  depth_first_search(u)&lt;/code&gt;&lt;/pre&gt;&lt;h1&gt;Minimum Spanning Tree(MST)&lt;/h1&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;최소비용 신장트리 또는 최소비용 스패닝 트리라고 불리며 사용된 간선들의 가중치 합이 최소인 Spanning Tree를 말한다.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;간선의 가중치 합이 최소여야 한다.&lt;/li&gt;&lt;li&gt;n개의 정점을 가지는 그래프에 대해 반드시(n-1)개의 간선만 사용해야 한다.&lt;/li&gt;&lt;li&gt;사이클이 포함되어서는 안된다.&lt;/li&gt;&lt;/ul&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;사례&lt;/h2&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
 &lt;li&gt;도로건설 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;도시들을 모두 연결하면서 도로의 길이가 최소가 되도록 하는 문제&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
 &lt;li&gt;전기 회로 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;단자들을 모두 연결하면서 전선의 길이가 가장 최소가 되도록 하는 문제&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
 &lt;li&gt;통신 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;전화선의 길이가 최소가 되도록 전화 케이블 망을 구성하는 문제&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
 &lt;li&gt;배관 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;파이프를 모두 연결하면서 파이프의 총 길이가 최소가 되도록 연결하는 문제&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
&lt;/ul&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;구현방법&lt;/b&gt;&lt;/h2&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. Kruskal MST 알고리즘&lt;/b&gt;&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;탐욕적인 방법(greedy method) 을 이용하여 네트워크(가중치를 간선에 할당한 그래프)의 모든 정점을 최소 비용으로 연결하는 최적 해답을 구하는 것.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
 &lt;li&gt;MST가 최소비용의 간선으로 구성&lt;/li&gt; 
 &lt;li&gt;사이클이 포함되지 않다는 것을 조건으로 각 단계에서 사이클을 이루지 않는 최소 비용간선을 선텍&lt;/li&gt; 
 &lt;li&gt;탐욕적인 방법이란? 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;결정을 해야할 때마다 그 순간마다 가장 좋다고 생각되는 것을 해답으로 선택함으로써 최종적인 해답에 도달하는 것.&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;알고리즘 절차&lt;/b&gt;&lt;/p&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt; 
 &lt;li&gt;그래프의 e개의 간선들의 가중치를 오름차순으로 정렬한다.&lt;/li&gt; 
 &lt;li&gt;정렬된 간선들의 리스트에서 사이클을 형성하지 않은 간선을 찾는다. 
  &lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt; 
   &lt;li&gt;가장 낮은 가중치를 먼저 선택&lt;/li&gt; 
   &lt;li&gt;만약 사이클을 형성하면 그 간선은 제외된다.&lt;/li&gt; 
  &lt;/ol&gt; &lt;/li&gt; 
 &lt;li&gt;현재의 최소 비용 신장 트리의 집합에 추가한다.&lt;/li&gt; 
&lt;/ol&gt;&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;//입력 : 가중치 그래프 G = (V, E) n은 노드의 개수
//출력 : E_t, 최소 비용 신장트리를 이루는 간선들의 집합
kruskal(G)

E의 가중치를 오름차순으로 정렬
**E_t = (집합)**
count &amp;lt;- 0
k &amp;lt;- 0
while count &amp;lt; (n - 1) do
	k &amp;lt;- k + 1
	if (E_t and {e_k} 가 사이클을 포함하지 않으면
		then E_t &amp;lt; = Et U {e_k}; 
				 count &amp;lt;- count + 1
return E_t;
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;알고리즘 구현 시 주의사항&lt;/b&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
 &lt;li&gt;다음 간선을 이미 선택된 간선들의 집합에 추가할 때 사이클을 생성하는지를 체크해야 한다.&lt;/li&gt; 
 &lt;li&gt;새로운 간선이 이미 다른 경로에 의해 연결되어 있는 정점들을 연결할 때 사이클이 형성된다. 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;즉, 추가할 새로운 간선의 양끝 정점이 같은 집합에 속해 있으면(간선을 추가하였을 경우) 사이클이 형성된다.&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사이클 생성 여부를 확인하는 방법&lt;/b&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
 &lt;li&gt;추가하고자 하는 간선의 양끝 정점이 같은 집합에 속해 있는지를 먼저 검사해야 한다.&lt;/li&gt; 
 &lt;li&gt;&lt;b&gt;union-find 알고리즘&lt;/b&gt; 이용하면 사이클 생성여부를 확인할 수 있다. 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;union(x,y) 연산 : 원소 x와 y가 속해 있는 집합을 입력 받아 2개의 집합의 합집합을 만든다.&lt;/li&gt; 
   &lt;li&gt;find(x) 연산 : 원소 x가 속해 있는 집합을 반환&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
&lt;/ul&gt;&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.coding.exam;

import java.util.Arrays;
import java.util.Comparator;

public class KruskalAlgorithm {
    public static void main(String[] args) {
        Solution sol = new Solution();
        int result = sol.kruskal(4, new int[][] {
                {0,1,1},{0,2,2},{1,2,5},{1,3,1},{2,3,8}
        });
        System.out.println(result);
    }
    private static class Solution {
        private int[] parent; //부모노드
        private int[] rank; //각 집합의 크기
        public int kruskal(int n, int[][] costs) {

            init(n);

            //가중치 값 작은 순으로 정렬
            Arrays.sort(costs, Comparator.comparingInt(o -&amp;gt; o[2]));

            int cost = 0;
            int edges = 0;

            //간선수 n -1
            for (int[] edge : costs) {

                if (edges == n - 1) {
                    break;
                }

                //서로 속한 집합이 다르면
                if (find(edge[0]) != find(edge[1])) {
                    unionByRank(edge[0], edge[1]); //두 집합을 합친다.
                    cost += edge[2];
                    edges++;
                }
            }
            System.out.println(Arrays.toString(rank));
            return cost;
        }

        /**
         * 초기화
         * @param n
         */
        private void init(int n) {
            this.parent = new int[n];
            this.rank = new int[n];
            for (int i = 0; i &amp;lt; n; i++) {
                this.parent[i] = i;
            }
        }

        private int find(int x) {
            if (parent[x] == x) {
                return x;
            }
            //경로 압축
            return parent[x] = find(parent[x]);
        }

        private void union(int x, int y) {
            int root1 = find(x);
            int root2 = find(y);

            if (root1 == root2) {
                return;
            }

            if (root1 &amp;lt; root2) {
                parent[root2] = root1;
            } else {
                parent[root1] = root2;
            }
        }

        /**
         * 두 트리를 합칠 때 높이가 더 낮은 트리를 높이가 높은 트리의 밑에 자손을 넣어주는 방식
         * 메모리를 배로 사용하므로 공간복잡도가 늘어난다.
         * @param x
         * @param y
         */
        private void unionByRank(int x, int y) {
            int root1 = find(x);
            int root2 = find(y);

            if (root1 == root2) {
                return;
            }

            if (rank[root1] &amp;gt; rank[root2]) {
                parent[root2] = root1;
            } else if (rank[root1] &amp;lt; rank[root2]) {
                parent[root1] = root2;
            } else { //rank[root1] == rank[root2]
                parent[root1] = root2;
                rank[root2]++;
            }
        }
    }

}&lt;/code&gt;&lt;/pre&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;시간복잡도&lt;/b&gt;&lt;/h3&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;간선들을 정렬하는 시간에 좌우&lt;/li&gt;&lt;li&gt;e개를 퀵정렬과 같은 효율적인 알고리즘으로 정렬한다면 시간복잡도는 O(elog₂e)이 된다.&lt;/li&gt;&lt;/ul&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. Prim MST 알고리즘&lt;/b&gt;&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;시작정점에서 정점을 추가하여 신장 트리 잡합을 단계적으로 확장해 나가는 방법&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
 &lt;li&gt;시작단계에서는 시작 정점만이 신장 트리 집합에 포함&lt;/li&gt; 
 &lt;li&gt;앞 단계의 신장 트리 집합에 인접한 정점들 중에서 최소 간선으로 연결된 정점을 선택하여 트리를 확장 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;인접한 정점들 중에서 가장 낮은 가중치의 간선과 연결된 정점을 선택하여 집합에 넣는 다는 것.&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
 &lt;li&gt;이 과정은 트리가 n-1개의 간선을 지날 때까지 반복&lt;/li&gt; 
&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1452&quot; data-origin-height=&quot;2284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnzQT4/btsHPNkmxH8/acKNNqOhtkeoSKZHOrTcQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnzQT4/btsHPNkmxH8/acKNNqOhtkeoSKZHOrTcQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnzQT4/btsHPNkmxH8/acKNNqOhtkeoSKZHOrTcQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnzQT4%2FbtsHPNkmxH8%2FacKNNqOhtkeoSKZHOrTcQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1452&quot; height=&quot;2284&quot; data-origin-width=&quot;1452&quot; data-origin-height=&quot;2284&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;배열로 구현할 경우 시간 복잡도&amp;nbsp;o(n^2)&lt;/li&gt;&lt;li&gt;최소 힙으로 구현할 경우 시간 복잡도&amp;nbsp;O(Elog n)&lt;/li&gt;&lt;/ul&gt;&lt;pre class=&quot;scss&quot;&gt;&lt;code&gt;//입력 : 네트워크 G=(V,E), s는 시작정점
//출력 : 최소 비용 신장트리를 이루는 정점들의 집합
Prims(G, s)

	for each u ⊆ V do
		dist[u] &amp;lt;- ∞
	dist[s] &amp;lt;- 0
	우선순위 큐 Q에 모든 정점을 삽입(우선순위는 dist[])
	for i &amp;lt;- 0 to n-1 do
		u &amp;lt;- delete_min(Q)
		화면에 u 출력
		for each v ⊆ (u의 인접 정점)
			if ( v ⊆ Q and weight[u][v] &amp;lt; dist[v] )
				then dist[v] &amp;lt;- weight[u][v] 
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;초기상태 정점(vertex)는 서로 연결되어 있지 않은 상태로 정점과 연결된 간선을 하나씩 추가하면서 최소신장트리 집합을 만들어냄.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
 &lt;li&gt;시작 정점을 정한 후 우선순위 큐에 넣는다. 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;우선순위 큐는 (정점, 가중치) 형식으로 저장&lt;/li&gt; 
   &lt;li&gt;첫 시작은 (시작정점, 0)으로 넣는다.&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
 &lt;li&gt;우선순위 큐가 빌 때까지 반복 (n-1) 
  &lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt; 
   &lt;li&gt;우선순위 큐에서 하나를 dequeue() [dequeue = 정점(v)]&lt;/li&gt; 
   &lt;li&gt;v가 이미 최소 신장 트리에 포함되어 있다면 1.을 수행, 그렇지 않으면 3. 진행&lt;/li&gt; 
   &lt;li&gt;v 와 연결된 간선을 모두 살핀다. 간선(w, cost)는 v와 정점 w 사이에 연결된 간선이며 cost의 가중치를 가지고 w를 방문하지 않으면 우선순위 큐에 추가&lt;/li&gt; 
  &lt;/ol&gt; &lt;/li&gt; 
&lt;/ul&gt;&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;import java.util.*;

public class PrimAlgorithm {
    public static void main(String[] args) {
        Solution sol = new Solution();
        int result = sol.solution(4, new int[][] {
                {0,1,1},
                {0,2,2},
                {1,2,5},
                {1,3,1},
                {2,3,8}
        });
        System.out.println(result);

        int result2 = sol.solution(5, new int[][] {
                {1,3,3},
                {1,4,8},
                {4,5,9},
                {1,2,10},
                {2,5,14}
        });
        System.out.println(result2);
    }
    private static class Solution {
        public int solution(int n, int[][] costs) {
            List&amp;lt;List&amp;lt;Edge&amp;gt;&amp;gt; graph = new ArrayList&amp;lt;&amp;gt;();
            for (int i = 0; i &amp;lt; n + 1; i++) {
                graph.add(new ArrayList&amp;lt;&amp;gt;());
            }

            for (int[] cost : costs) {
                graph.get(cost[0]).add(new Edge(cost[0], cost[2]));
                graph.get(cost[0]).add(new Edge(cost[1], cost[2]));
            }

            return prim(graph, costs[0][0]);
        }
        public int prim(List&amp;lt;List&amp;lt;Edge&amp;gt;&amp;gt; graph, int start) {
            Set&amp;lt;Integer&amp;gt; visited = new HashSet&amp;lt;&amp;gt;();

            PriorityQueue&amp;lt;Edge&amp;gt; priorityQueue = new PriorityQueue&amp;lt;&amp;gt;();
            priorityQueue.offer(new Edge(start, 0));

            int costs = 0;
            while (!priorityQueue.isEmpty()) { //우선순위 큐가 빌 때까지 반복

                Edge u = priorityQueue.poll();

                if (visited.contains(u.vertex)) //방문했다면 건너뜀
                    continue;;

                visited.add(u.vertex); // 방문하지 않았다면 vertex 방문했다고 표시
                costs += u.cost; //가중치 증가

                for (Edge edge : graph.get(u.vertex)) { //u.vertex 의 인접 정점 추가
                    if (!visited.contains(edge.vertex)) {
                        priorityQueue.add(edge);
                    }
                }
            }
            return costs;
        }
        private static class Edge implements Comparable&amp;lt;Edge&amp;gt; {
            int vertex;
            int cost;

            public Edge(int vertex, int cost) {
                this.vertex = vertex;
                this.cost = cost;
            }

            @Override
            public int compareTo(Edge o) {
                return Integer.compare(cost, o.cost);
            }

            @Override
            public String toString() {
                return &quot;Edge{&quot; +
                        &quot;vertex=&quot; + vertex +
                        &quot;, cost=&quot; + cost +
                        '}';
            }
        }
    }

}
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Kruskal 알고리즘 Prim 알고리즘 차이&lt;/b&gt;&lt;/h3&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 78px;&quot; border=&quot;1&quot; data-ke-style=&quot;style8&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;Kruskal 알고리즘&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;&amp;nbsp;Prim 알고리즘&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;간선 선택을 기반으로 하는 알고리즘&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;정점 선택을 기반으로 하는 알고리즘&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;이전 단계에서 만들어진 신장 트리와 상관없이 최소 간선만을 선택하는 방법&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;이전 단계에서 만들어진 신장 트리를 확장하는 방식&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;참고 : c 언어로 쉽게 풀어 쓴 자료구조&lt;/p&gt;</description>
      <category>Algorithms</category>
      <category>Graph</category>
      <category>kruskal</category>
      <category>MST</category>
      <category>prim</category>
      <category>spanning tree</category>
      <category>그래프</category>
      <category>스패닝트리</category>
      <category>신장트리</category>
      <category>최소신장트리</category>
      <category>프림</category>
      <author>조슈아。</author>
      <guid isPermaLink="true">https://clairdelunes.tistory.com/165</guid>
      <comments>https://clairdelunes.tistory.com/165#entry165comment</comments>
      <pubDate>Thu, 6 Jun 2024 00:15:15 +0900</pubDate>
    </item>
    <item>
      <title>[Kotlin] 2. 연산자</title>
      <link>https://clairdelunes.tistory.com/164</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;연산자 구분&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단항 연산자(Unary Operator) : 항이 하나인 연산자로 하나의 변수에서만 작동하는 연산자&lt;/li&gt;
&lt;li&gt;이항 연산자(Binary Operator) : 두 개의 항을 처리하는 연산자로 두 변수의 값으로 작동하는 연산자&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사칙연산자&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;표현식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;메서드명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;내부 실행&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;a + b&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;plus&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;a.plus(b)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;a -b&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;minus&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;a.minus(b)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;a * b&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;times&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;a.times(b)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;a / b&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;div&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;a.div(b)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;a % b&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;rem/mod&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;a.rem(b), a.mod(b)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;println(&quot;num1 + num2 = ${num1 + num2}&quot;)
println(&quot;num1 - num2 = ${num1 - num2}&quot;)
println(&quot;num1 * num2 = ${num1 * num2}&quot;)
println(&quot;num1 / num2 = ${num1 / num2}&quot;)
println(&quot;num1 % num2 = ${num1 % num2}&quot;)

println(&quot;num1.plus(num2) = ${num1.plus(num2)}&quot;)
println(&quot;num1.minus(num2) = ${num1.minus(num2)}&quot;)
println(&quot;num1.times(num2) = ${num1.times(num2)}&quot;)
println(&quot;num1.div(num2) = ${num1.div(num2)}&quot;)
println(&quot;num1.rem(num2) = ${num1.rem(num2)}&quot;)
println(&quot;num1.mod(num2) = ${num1.mod(num2)}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;할당복합연산자&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 102px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;&lt;b&gt;표현식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;&lt;b&gt;같은 표현식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;&lt;b&gt;내부 실행&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;a += b&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;a = a + b&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;a.plusAssign(b)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;a -= b&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;a = a - b&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;a.minusAssign(b)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;a *= b&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;a = a * b&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;a.timesAssign(b)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;a /= b&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;a = a / b&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;a.divAssign(b)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;a %= b&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;a = a % b&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;a.modAssign(b)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;var num1 = 12.5
var num2 = 10.5
num1 += num2;
println(&quot;num1 + num2 = ${num1}&quot;)
num1 -= num2;
println(&quot;num1 - num2 = ${num1}&quot;)
num1 *= num2;
println(&quot;num1 * num2 = ${num1}&quot;)
num1 /= num2;
println(&quot;num1 / num2 = ${num1}&quot;)
num1 %= num2;
println(&quot;num1 % num2 = ${num1}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;단항연산자&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 103px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;연산자&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;의미&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;같은표현식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;내부실행&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;+&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;양수표시&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;+a&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;a.unaryPlus()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;음수표시&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;-a&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;a.unaryMinus()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;!&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;not&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;!a&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;a.not()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;++&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;증감연산&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;++a&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;a.inc()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;&amp;mdash;&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;감소연산&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;&amp;mdash;a&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;a.dec()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;var num1 = 10
var num2 = 15

println(&quot; 양수표시 : ${num1.unaryPlus()}  / 음수표시 : ${ num2.unaryMinus()} &quot;)
println(&quot; 증감연산 : ${num1.inc()} / 감소연산 ${num2.dec()}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;비트 연산자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비트연산자를 메서드로만 제공&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 150px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;&lt;b&gt;연산자&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;&lt;b&gt;자바&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;&lt;b&gt;의미&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;shl&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;&amp;lt;&amp;lt;&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;이진 수를 왼쪽으로 이동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;shr&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;&amp;gt;&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;이진수를 오른쪽으로 이동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;ushr&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;부호 없는 이진수를 오른쪽으로 이동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;and&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;&amp;amp;&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;논리곱 비트연산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;or&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;xor&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;^&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;베타적 논리합 비트연산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;inv&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;!&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;비트 반전&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;varbit1 = 10
varbit2 = 1

println(&quot; 이진수를 왼쪽으로 이동 (&amp;lt;&amp;lt;) : ${bit1.shl(bit2)}&quot;)
println(&quot; 이진수를 오른쪽으로 이동 (&amp;gt;&amp;gt;) : ${bit1.shr(bit2)}&quot;)
println(&quot; 부호 없는 이진수를 오른쪽으로 이동 (&amp;gt;&amp;gt;&amp;gt;) : ${bit1.ushr(bit2)}&quot;)
println(&quot; 논리곱 비트 연산  and (&amp;amp;) : ${bit1 and bit2}&quot;)
println(&quot; 논리합 비트 연산  or (|) : ${bit1 or bit2}&quot;)
println(&quot; 베타적 논리합 비트 연산  xor (^) : ${bit1 xor bit2}&quot;)
println(&quot; 비트 반전 inv (!) : ${bit1.inv()}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;포함연산자&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 범위에 속한 값이 포함하는지 여부를 확인 할 때 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 54px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; text-align: center;&quot;&gt;&lt;b&gt;연산자&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; text-align: center;&quot;&gt;&lt;b&gt;표현식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; text-align: center;&quot;&gt;&lt;b&gt;메서드&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; text-align: center;&quot;&gt;in&lt;/td&gt;
&lt;td style=&quot;height: 18px; text-align: center;&quot;&gt;a in b&lt;/td&gt;
&lt;td style=&quot;height: 18px; text-align: center;&quot;&gt;b.contains(a)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; text-align: center;&quot;&gt;!in&lt;/td&gt;
&lt;td style=&quot;height: 18px; text-align: center;&quot;&gt;a !in b&lt;/td&gt;
&lt;td style=&quot;height: 18px; text-align: center;&quot;&gt;!b.contains(a)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;var height = 46;
println(height in 1..53); //특정 범위 내에 속하는지.
println(height !in 1..53); //특정 범위 내에 속하면 거짓.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연산자 의미&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;any&lt;/td&gt;
&lt;td&gt;하나라도 참 인경우&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;all&lt;/td&gt;
&lt;td&gt;모드 것이 참이 경우&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;모든 것이 거짓인 경우&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;golo&quot;&gt;&lt;code&gt;var list = listOf(1,2, null);
println(list.any { it == null }); //리스트 모든 값에 null이 하나라도 있으면 참
println(list.all { it == null }); //리스트 모든 값이 null이면 참
println(list.none { it == null }); //리스트 모든 값이 null이 아니면 참
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;동등성 규칙&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연산자 의미 메서드&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 96px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; text-align: center;&quot;&gt;&lt;b&gt;연산자&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; text-align: center;&quot;&gt;&lt;b&gt;의미&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; text-align: center;&quot;&gt;&lt;b&gt;메서드&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;==&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;구조적 동등성으로 동일한 값을 비교&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;equals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;===&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;참조적 동등성으로 객체 참조를 비교&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;?.equals&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;null이 들어올 경우 null값 체크&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;var a1 = 100;
var a2 = 100;
var a3 = null;
println(&quot;a1 == a2 ${a1 == a2}&quot;) // - true
println(&quot;a1.equals(a2) : ${a1.equals(a2)}&quot;) // - true
println(&quot;a1?.equals(a2) : ${a1?.equals(a3)}&quot;) //null타입 처리일 경우 null 체크 - false
println(&quot;a1 === a2 : ${a1 === a2}&quot;) //객체 참조비교 - true
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;When 조건문&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 패턴 매칭: 특정 값이나 특정 패턴을 하나만 처리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복합 패턴 매칭: 여러 종류의 패턴을 하나의 구문에서 처리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패턴이 없는 경우: if문 처럼 임의의 패턴을 지정해서 처리&lt;/p&gt;
&lt;pre class=&quot;elm&quot;&gt;&lt;code&gt;val [return 변수] = when([변수명]) {
	[조건 값] -&amp;gt; [결과 값]              -- 특정 조건 값이 있을 경우
   in [S...N 조건 값] -&amp;gt; [결과 값]    -- 특정 조건 값이 있을 경우 
	else -&amp;gt; [결과 값]                 -- 원하는 조건 값이 없을 경우
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;when 표현식&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;val cores = Runtime.getRuntime().availableProcessors();

    val result = when (cores) {
        1 -&amp;gt; &quot;1 Core&quot;                     //특정 값 패턴 조건
        in 2..16 -&amp;gt; &quot;$cores Cores&quot; //특정 값 패턴 조건
        else -&amp;gt; &quot;I want your machine&quot;    //패턴 불일치
    }

    println(&quot;result : $result&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;when 내부에 지역변수 정의하여 함수 반환에 직접 대입&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;fun main(args: Array&amp;lt;String&amp;gt;) {
    println(sysInfoCore());
}
fun sysInfoCore() : String = when 
        (val cores = Runtime.getRuntime().availableProcessors()) {
            1 -&amp;gt; &quot;1 Core&quot;                     //특정 값 패턴 조건
            in 2..16 -&amp;gt; &quot;$cores Cores&quot; //특정 값 패턴 조건
            else -&amp;gt; &quot;I want your machine&quot;    //패턴 불일치
        }
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;if 조건식 처럼 사용 가능&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;val num = 12

when {
    num &amp;lt; 0 -&amp;gt; println(&quot;음수&quot;)
    num == 0 -&amp;gt; println(&quot;ZERO&quot;)
    num % 2 == 0 -&amp;gt; println(&quot;짝수&quot;)
    else -&amp;gt; println(&quot;홀수&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예외처리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특 정 조건에서 예외가 발생할 경우 프로세스가 중단되는 것을 방지하기 위한 처리방법&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;try {
    [내용]
} catch (e : Exception) {
		[예외처리]
} finally {
    [나머지 처리]]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예외 던지기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;throw 키워드를 통해 예외를 강제로 발생 시킬 수 있으며 예외가 발생하면 catch문에서 예외를 처리해준다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;try {
    throw Exception(&quot;예외 강제 발생&quot;)
} catch (e : Exception) {
    println(e);
} finally {
    println(&quot;나머지 처리&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;fun main(args: Array&amp;lt;String&amp;gt;) {
		try {
        exp();
    } catch (e : Exception) {
        println(e)
    } finally {
        println(&quot;나머지 처리&quot;)
    }
}

fun exp() : Nothing {
    return throw Exception(&quot;exp() 예외발생&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외 처리를 변수에 할당하여 결과 값을 변수에 할당되게 할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;php&quot;&gt;&lt;code&gt;val exVal = try {
    100
    throw Exception(&quot;예외발생&quot;)
} catch (e : Exception) {
    200
} finally {
    300;
}
println(exVal);// 200
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Language/Kotlin</category>
      <category>Kotlin</category>
      <category>연산자</category>
      <author>조슈아。</author>
      <guid isPermaLink="true">https://clairdelunes.tistory.com/164</guid>
      <comments>https://clairdelunes.tistory.com/164#entry164comment</comments>
      <pubDate>Tue, 22 Aug 2023 10:07:22 +0900</pubDate>
    </item>
    <item>
      <title>[Kotlin] 1.변수(Variable)</title>
      <link>https://clairdelunes.tistory.com/163</link>
      <description>&lt;h1&gt;1. 변수선언&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;val (불변변수)&lt;/b&gt; : 한번 저장하면 다시 할당 할 수 없는 변수를 정의.&lt;/h3&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;//문자, 문자열, 논리 값 객체
//Char(2byte), String class, Boolean : (true, false)
val charVal = 'a'
val strVal = &quot;Hello&quot;
val boolVal = true;

//charVal = 'b' // Error: 불변변수는 다시 재할당 불가능.
println(charVal);
println(strVal);
println(boolVal);

println(charArrayOf('가','나').contentToString())

var str = String(charArrayOf('가','을'));

println(str);

//문자열 탬플릿
//$변수명, ${표현식}

val date = &quot;20230818&quot;;
val a = 100;
val b = 200;
println(&quot;날 짜 : $date&quot;)
println(&quot;100 + 200 = ${a + b}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;var (가변변수) :&lt;/b&gt; 재할당 할 수 있는 변수를 정의&lt;/h3&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;//정수와 실수 객체
//숫자를 관리하는 정수와 실수
//Byte(1byte), Short(2byte), Int(4byte), Long(8 byte)
var initVar = 100
var longVar = 100L
var doubleVar = 100.0
var floatVar = 100.0F

println(initVar)
println(longVar)
println(doubleVar)
println(floatVar)
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;2. &lt;b&gt;초기화 미루기(Lazy init)&lt;/b&gt;&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;lateinit&amp;nbsp;키워드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1692601473446&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;lateinit var data: String&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;변수 선언 이후에&amp;nbsp;초기 값을&amp;nbsp;할당할&amp;nbsp;것을&amp;nbsp;명시적으로&amp;nbsp;선언&lt;/li&gt;
&lt;li&gt;사용하기&amp;nbsp;전&amp;nbsp;초기화&amp;nbsp;후&amp;nbsp;사용해야&amp;nbsp;함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;var&lt;/b&gt;&amp;nbsp;키워드&amp;nbsp;변수만&amp;nbsp;사용가능&lt;/li&gt;
&lt;li&gt;Primitive Type (Int,&amp;nbsp;Long.&amp;nbsp;Short,&amp;nbsp;Double,&amp;nbsp;Float,&amp;nbsp;Boolean,&amp;nbsp;Byte)&amp;nbsp;타입에는&amp;nbsp;사용불가능&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;lateinit var lateInitText: String

// 변수선언
val result1 = 30

//사용하기 전 초기화 후 사용
lateInitText = &quot;Result : $result1&quot;
println(lateInitText)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;lazy&lt;/b&gt;&amp;nbsp;키워드&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 변수를 사용할 때 초기값을 할당&lt;/li&gt;
&lt;li&gt;&lt;b&gt;val 키워드로 선언가능&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;한 번의 lazy init이 이루어지고 이후에는 값이 불변함을 보장&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1692601494653&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  val data: Int by lazy {   ... }&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;lateinit var lateInitText: String
val lazyTextLength: Int by lazy {
    lateInitText.length
}

// 변수 값 선언
lateInitText = &quot;Hello World Kotlin&quot;
println(lazyTextLength)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;자료형 타입 선언&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Int&lt;/li&gt;
&lt;li&gt;Short&lt;/li&gt;
&lt;li&gt;Int&lt;/li&gt;
&lt;li&gt;Long&lt;/li&gt;
&lt;li&gt;Float&lt;/li&gt;
&lt;li&gt;Double&lt;/li&gt;
&lt;li&gt;Char&lt;/li&gt;
&lt;li&gt;String&lt;/li&gt;
&lt;li&gt;Boolean&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1692601511069&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  [var or val] [변수명] : [타입] = [선언값] or [var or val] [변수명] : [타입]&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;val byteVal: Byte = 0b110
println(byteVal)
val shortVal: Short = 10
println(shortVal)
val intVal: Int = 10
println(intVal)
val longVal: Long = 10L
println(longVal)
val floatVal: Float = 10f
println(floatVal)
val doubleVal: Double = 10.0
println(doubleVal)
val chVal: Char = 'c'
println(chVal)
val strVal: String = &quot;Hello World&quot;
println(strVal)
val boolVal: Boolean = true
println(boolVal)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Any: 모든 타입 가능한 최상위 클래스&lt;/h3&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;valanyVal: Any = 10
valanyVal1: Any = &quot;Change&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Unit : 리턴문이 없는 함수&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Unit 타입으로 선언한 변수는 Unit 객체만 대입 가능&lt;/li&gt;
&lt;li&gt;Unit 타입으로 변수를 선언이 가능하지만 의미없음&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;valdtVal: Unit = Unit;

funA(): Unit {
println(100 * 100);
}
A();
funB() {
println(100 * 100);
}
B();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Nothing : null 또는 예외를 반환하는 함수&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Nothing 으로 선언한 변수는 데이터로서 의미없음&lt;/li&gt;
&lt;li&gt;Nothing 으로 선언한 변수는 &lt;b&gt;null&lt;/b&gt;만 대입가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;val dtNull: Nothing? = null;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코틀린의 모든타입은 객체이므로 변수에 null 대입이 가능.&lt;/li&gt;
&lt;li&gt;변수 선언 시 null을 대입 할 수 있는지 없는지 명확하게 구분하여 선언&lt;/li&gt;
&lt;li&gt;타입 뒤에 &lt;b&gt;물음표(?)&lt;/b&gt; 키워드를 추가하면 null을 허용하고 추가하지 않으면 허용하지 않는다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Language/Kotlin</category>
      <category>Kotlin</category>
      <category>variable</category>
      <author>조슈아。</author>
      <guid isPermaLink="true">https://clairdelunes.tistory.com/163</guid>
      <comments>https://clairdelunes.tistory.com/163#entry163comment</comments>
      <pubDate>Mon, 21 Aug 2023 16:05:44 +0900</pubDate>
    </item>
  </channel>
</rss>