<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sad-zero.log</title>
        <link>https://velog.io/</link>
        <description>夫唯嗇是以早服</description>
        <lastBuildDate>Thu, 29 May 2025 14:29:31 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. sad-zero.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sad-zero" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[계약과 문서 with Javadoc]]></title>
            <link>https://velog.io/@sad-zero/%EA%B3%84%EC%95%BD%EA%B3%BC-%EB%AC%B8%EC%84%9C-with-Javadoc</link>
            <guid>https://velog.io/@sad-zero/%EA%B3%84%EC%95%BD%EA%B3%BC-%EB%AC%B8%EC%84%9C-with-Javadoc</guid>
            <pubDate>Thu, 29 May 2025 14:29:31 GMT</pubDate>
            <description><![CDATA[<h1 id="목표">목표</h1>
<ul>
<li>계약에 의한 설계에 따른 Operation 문서화 방법</li>
<li>Javadoc 문서 Convention 정리<h1 id="용어-정의">용어 정의</h1>
<table>
<thead>
<tr>
<th>용어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>추이적 계약(transitive contract)</td>
<td>A -&gt; B, B -&gt; C의 Operations가 있을 때, B -&gt; C의 계약이 A에게 노출되는 것</td>
</tr>
</tbody></table>
</li>
</ul>
<h1 id="배경">배경</h1>
<p>계약에 의한 설계(Design By Contract)에 의하면 모든 공개된 연산(public operation)은 계약을 갖는다. 계약은 호출자(Client)가 이 연산의 소유자(Server)에게 메시지(Message)를 통해 연산을 요청할 때, 호출자의 입장에서 연산에 대해 알아야 하는 정보로써 요청 메시지, 응답 값, 연산 수행 후의 상태를 제약(Constraint)한다.</p>
<h1 id="문제-상황">문제 상황</h1>
<pre><code class="language-java">class A {
    public Response1 operation1(Message1 message1) {
        Message2  message2 = ...;
        Response2 response2 = B.operation2(message2);

        Response1 response1 = ...;
        return response1;
    }
}

class B {
    /**
    * 
    * @param Message2 message2 operation2의 메시지
    * @return operation2의 결과
    */
    public Response2 operation2(Message2 message2) {
        Message3 message3 = ...;
        Response3 response3 = C.operation3(message3);

        Response2 response2 = ...;
        return response2;
    }
}

class C {
    /**
    * 
    * @param Message3 message3 operation3의 메시지
    * @return operation3의 결과
    * @throws Operation3Exception operation3의 예외
    */
    public Response3 operation3(Message3 message3) {
        if (message3 = ...) {
            throw new Operation3Exception(...);
        }
        return ...;
    }
}</code></pre>
<p>위와 같은 구조에서 A는 자신의 operation을 수행하기 위해 B의 operation을 호출하고, B 역시 C를 호출한다. A의 입장에서, B가 자신의 연산(operation2)를 수행하기 위해 C를 호출한다는 것을 알아야 하는가? 즉, A에게 B -&gt; C의 계약(operation3)은 노출되어야 하는가?</p>
<h1 id="원인-분석">원인 분석</h1>
<p>위의 상황은 체결된 계약의 유형을 나누지 않았기 때문에 발생한다. DBC에 따르면 하나의 계약은 최대 4가지(응답과 예외 분리 시) 요소로 나뉠 수 있다.</p>
<table>
<thead>
<tr>
<th>계약 요소</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>메시지</td>
<td>Client가 Server의 Operation을 사용하기 위해 Server로 전달하는 입력</td>
</tr>
<tr>
<td>응답</td>
<td>Server가 Operation의 결과로 Client에게 전달하는 응답</td>
</tr>
<tr>
<td>불변식</td>
<td>Server가 Operation 수행 후 보장하는 자신의 상태</td>
</tr>
<tr>
<td>예외</td>
<td>응답의 한 종류로, Client에게 Operation 수행 실패를 전달</td>
</tr>
</tbody></table>
<p>각 요소는 <strong>추이성</strong>에 따라 다른 계약으로 전파될 수 있는데, 추이성을 띄는 요소는 직전의 계약에 전파되어야 한다. 만약 계약에서 하나 이상의 요소가 추이성을 띈다면 그 계약은 <strong>추이적 계약</strong>으로 간주될 수 있고, 그것은 호출자가 명시적으로 처리하지 않는한, 호출자의 Operation에 대한 계약으로 전파되어야 한다.</p>
<h2 id="추이성-유무">추이성 유무</h2>
<p>일반적으로 각 계약 요소의 추이성은 다음과 같다.</p>
<table>
<thead>
<tr>
<th>계약 요소</th>
<th>추이성</th>
<th>이유</th>
</tr>
</thead>
<tbody><tr>
<td>메시지</td>
<td>X</td>
<td>Operation 내부에서 다른 Operation을 호출할 때 필요한 메시지는 현재 Operation의 호출자가 알 필요 없다.</td>
</tr>
<tr>
<td>응답</td>
<td>X</td>
<td>Operation 내부에서 다른 Operation을 호출한 결과는 현재 Operation의 호출자가 알 필요 없다.</td>
</tr>
<tr>
<td>불변식</td>
<td>X</td>
<td>Operation 내부에서 다른 Operation(<strong>B</strong> 소유)을 호출할 때 B의 불변식은 현재 Operation의 호출자가 알 필요 없다.</td>
</tr>
</tbody></table>
<p>예외의 경우 계약의 실패를 의미하기 때문에 조금 더 세분화된다.</p>
<table>
<thead>
<tr>
<th>예외 종류</th>
<th>추이성</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>Checked Exception</td>
<td>O</td>
<td>계약 실패 이유를 확인해야 하는 예외로, 명시적으로 처리하지 않는다면 추이성을 갖는다.</td>
</tr>
<tr>
<td>Unchecked Exception</td>
<td>X</td>
<td>계약 실패 이유를 확인할 수 없는 예외로, 추이성을 갖지 않는다.</td>
</tr>
</tbody></table>
<p><strong>Checked Exception</strong>과 <strong>Unchecked Exception</strong>은 다음을 기준으로 구분될 수 있다.</p>
<blockquote>
<p>모든 하위 Operations가 정상일 때, Operation에서 발생 가능한가?</p>
</blockquote>
<h1 id="해결-방안">해결 방안</h1>
<pre><code class="language-java">@Service
class UserService {
    private final UserRepository userRepository;

    /**
    * 
    * @param String userId desired id.
    * @return desired user.
    * @throws NotFoundException there is no user mapped by the id(CheckedException)
    */
    public User findById(String userId) {
        Optional&lt;User&gt; user = userRepository.findById(userId);
        if (user.isEmpty()) {
            throw new NotFoundException();
        }
        return user.get();
    }
}

@Repository
class UserRepository {
    private final SqlSession session;
    /*
    * @param String id desired id.
    * @return desired user.
    * @throws ClosedSessionException unexpected session error(Unchecked Exception)
    */
    public Optional&lt;User&gt; findById(String id) {
        if (session.isClosed()) {
            throw new ClosedSessionException();
        }
        return session.select(id);
    }
}</code></pre>
<p><code>UserService.findById</code>는 사용자를 찾기 위해 <code>UserRepository.findById</code>를 호출한다. 이때 Session이 의도치 않게 닫힌 경우, UserRepository의 계약은 실패하고 예외를 발생시킨다. 이때 발생된 <code>ClosedSessionException</code>은 Session이 정상 동작하는 경우 발생하지 않았을 예외로, Unchecked Exception에 해당한다.
반대로, UserService에서 사용자를 찾지 못한 경우 발생하는 <code>NotFoundException</code>은 UserRepository가 정상 동작하더라도 발생 가능하기 때문에 Checked Exception에 해당한다.
이에 따라 <code>UserService.findById</code>의 계약에는 <code>ClosedSessionException</code>이 전이되지 않고, 이는 문서로 명문화될 수 있다.
한발 더 나아가 <code>UserService.findById</code>의 호출자가 <code>NotFoundException</code>을 자신의 계약에 포함시키고 싶지 않다면, 그것은 명시적으로 처리되어야 한다.</p>
<pre><code class="language-java">@Service
class AlarmService {
    private final UserService userService;

    /**
    *
    * @param userId desried id.
    * @return whether the alarm is notified to the user or not.
    */
    public boolean notify(String userId) {
        try {
            final var user = userService.findById(userId);
        } catch (NotFoundException e) {
            return false;
        }
        ...
        return true;
    }
}       </code></pre>
<p><code>AlarmService.notify</code>는 명시적으로 <code>NotFoundException</code>을 처리하기 때문에 자신의 계약에 그것을 전이시키지 않는다.</p>
<h1 id="결론">결론</h1>
<blockquote>
<p>&quot;Working software over comprehensive documentation&quot; by Agile Manifesto</p>
</blockquote>
<p>문서와 코드는 상호보완되어야 한다. &quot;Don&#39;t reinvent the wheel&quot;라는 격언과 Agile 원칙을 고려할 때, 문서는 코드의 세부사항을 하나하나 다루기보다, <strong>어떤 계약이 체결되었는가?</strong>에 집중해야 한다.</p>
<h2 id="ps-java-checked-unchecked-exception">PS. Java Checked, Unchecked Exception</h2>
<p>Java는 언어 차원에서 명시적인 Checked Exception을 지원한다.</p>
<pre><code class="language-java">public User findById(String userId) throws NotFoundException {}</code></pre>
<p>이는 때때로 유용하나, 모든 상황에서 throws로 명시하기란 어려운데, 예를 들어 Stream을 사용하는 경우, Function, Consumer 등의 IF에 Checked Exception이 포함되지 않기 때문에 부가적인 작업이 요구된다. 따라서 Checked, Unchecked 예외는 맥락적 차원과 언어적 차원을 구분하여 이해하는 것이 필요하다.</p>
<h1 id="참고-자료">참고 자료</h1>
<ul>
<li><a href="https://velog.io/@sad-zero/%EA%B3%84%EC%95%BD%EC%97%90-%EC%9D%98%ED%95%9C-%EC%84%A4%EA%B3%84Design-By-Contract-with-Java">https://velog.io/@sad-zero/계약에-의한-설계Design-By-Contract-with-Java</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[계약에 의한 설계(Design By Contract) with Java]]></title>
            <link>https://velog.io/@sad-zero/%EA%B3%84%EC%95%BD%EC%97%90-%EC%9D%98%ED%95%9C-%EC%84%A4%EA%B3%84Design-By-Contract-with-Java</link>
            <guid>https://velog.io/@sad-zero/%EA%B3%84%EC%95%BD%EC%97%90-%EC%9D%98%ED%95%9C-%EC%84%A4%EA%B3%84Design-By-Contract-with-Java</guid>
            <pubDate>Sat, 17 May 2025 06:39:41 GMT</pubDate>
            <description><![CDATA[<h1 id="목표">목표</h1>
<ul>
<li>계약에 의한 설계</li>
<li>일반화된 계약에 의한 설계</li>
<li>Java 구현 예시<h1 id="용어-정의">용어 정의</h1>
<table>
<thead>
<tr>
<th>용어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>메시지(<code>Message</code>)</td>
<td>시스템이나 객체가 처리할 수 있는 기능</td>
</tr>
<tr>
<td>협력(<code>Cooperation</code>)</td>
<td>메시지에 처리에 책임을 가진 역할들의 시퀀스</td>
</tr>
<tr>
<td>역할(<code>Role</code>)</td>
<td>메시지 처리에 참여하는 메타 타입</td>
</tr>
<tr>
<td>책임(<code>Responsibility</code>)</td>
<td>메시지 처리 과정에서 각 역할의 기능</td>
</tr>
<tr>
<td>계약(<code>Contract</code>)</td>
<td>메시지 처리 과정에서 각 역할 사이의 명세</td>
</tr>
</tbody></table>
</li>
</ul>
<h1 id="계약에-의한-설계">계약에 의한 설계</h1>
<p>Design By Contract(DBC)는 객체 지향 설계 기법 중 하나로, <strong>책임을 세분화하여, 테스트 가능한 명세</strong>로 유지하는 것을 의미한다. DBC 하에 책임은 3가지로 나뉜다.</p>
<table>
<thead>
<tr>
<th>유형</th>
<th>설명</th>
<th>책임 대상</th>
</tr>
</thead>
<tbody><tr>
<td>사전 조건(<code>precondition</code>)</td>
<td>기능 호출 전 명세</td>
<td>호출자(<code>Client</code>)</td>
</tr>
<tr>
<td>사후 조건(<code>postcondition</code>)</td>
<td>기능 호출 후 명세</td>
<td>실행자(<code>Server</code>)</td>
</tr>
<tr>
<td>불변식(<code>(role) invariant</code>)</td>
<td>역할의 생명주기 내의 명세</td>
<td>실행자(<code>Server</code>)</td>
</tr>
</tbody></table>
<p>각 조건의 충족 여부를 가능한 빨리 판단하여, 시스템이 불안정한 상태를 빠르게 극복/종료하도록 하는 것을 목표로 한다(<strong>Fail-fast</strong>)</p>
<p><img src="https://velog.velcdn.com/images/sad-zero/post/15987986-87a0-40d9-ad8c-2c55c715c339/image.png" alt="DBC Flow"></p>
<h1 id="일반화된-계약에-의한-설계">일반화된 계약에 의한 설계</h1>
<p>DBC는 리스코프 치환 원칙(<a href="https://ko.wikipedia.org/wiki/%EB%A6%AC%EC%8A%A4%EC%BD%94%ED%94%84_%EC%B9%98%ED%99%98_%EC%9B%90%EC%B9%99">Liskov substitution principle</a>)에 따라 타입에 대해 확장될 수 있다.</p>
<table>
<thead>
<tr>
<th>규칙</th>
<th>조건</th>
<th>위계(Base -&gt; Drived)에 따른 강도</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>계약(Contract)</td>
<td>사전 조건</td>
<td>같거나 약함</td>
<td>Client에게 계약 이상을 요구할 수 없다.</td>
</tr>
<tr>
<td></td>
<td>사후 조건</td>
<td>같거나 강함</td>
<td>Client에게 계약 이하를 제공할 수 없다.</td>
</tr>
<tr>
<td></td>
<td>불변식</td>
<td>같거나 강함</td>
<td>상위 위계의 불변식은 유지되어야 한다.</td>
</tr>
<tr>
<td>가변성(Variance)</td>
<td>반환 타입</td>
<td>같거나 강함</td>
<td>사후조건과 동일</td>
</tr>
<tr>
<td></td>
<td>매개변수 타입</td>
<td>같거나 약함</td>
<td>사전조건과 동일</td>
</tr>
<tr>
<td></td>
<td>예외 타입</td>
<td>같거나 강함</td>
<td>사후조건과 동일. 기대하지 않은 예외는 발생하면 안된다.</td>
</tr>
<tr>
<td>가변성 규칙은 <code>Operation</code>에 대한 것이며, 다음의 용어를 사용하여 정리할 수 있다.</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>용어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>공변성(<code>covariance</code>)</td>
<td><code>Operation</code>의 위계가 동일하게 적용됨</td>
</tr>
<tr>
<td>반공변성(<code>contravariance</code>)</td>
<td><code>Operation</code>의 위계가 반대로 적용됨</td>
</tr>
</tbody></table>
<p><img src="https://velog.velcdn.com/images/sad-zero/post/78513f0e-16b2-4bdb-8583-9d0cd97f9973/image.png" alt="Variance Rule"></p>
<p>확장된 DBC를 준수하면 더욱 견고한 타입 계층을 제공할 수 있다는 장점이 있다.</p>
<h1 id="java-구현-예시">Java 구현 예시</h1>
<blockquote>
<p>DBC(명세 및 검증)은 Java Native로 지원되지 않으나, 다음과 같이 간단히 구현해볼 수 있다.</p>
</blockquote>
<p>사용한 라이브러리는 다음과 같다.</p>
<ul>
<li>spring-boot-starter-validation
계약의 작성은 <a href="https://jakarta.ee/specifications/bean-validation/3.1/jakarta-validation-spec-3.1.html#builtinconstraints">jakarta validation specification</a>으로 수행할 수 있으며, 검증을 하기 위해 구현체(Hibernate or Spring validation)을 사용할 수 있다. 검증 로직은 다음과 같다.</li>
</ul>
<pre><code class="language-java">import static jakarta.validation.Validation.buildDefaultValidatorFactory;

import jakarta.validation.ConstraintViolation;
import jakarta.validation.ValidationException;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import error.InvariantContractException;
import error.PostConditionContractException;
import error.PreConditionContractException;
import lombok.extern.slf4j.Slf4j;

/**
 * Validator for Design by Contract
 *
 * @param &lt;T&gt; target message or object
 */
@Slf4j
public final class ContractValidator&lt;T&gt; {

  private final String contractPath;
  private final ContractType contractType;

  /**
   * Initialize the validator
   *
   * @param contractPath class or operation&#39;s path
   * @param contractType contract type
   * @throws InvariantContractException invalid arguments
   */
  private ContractValidator(@NotBlank String contractPath, @NotNull ContractType contractType) {
    final var properties = new HashMap&lt;String, Object&gt;();

    if ((contractPath == null) || contractPath.isBlank()) {
      properties.put(&quot;contractPath&quot;, &quot;null&quot;);
    }
    if (contractType == null) {
      properties.put(&quot;contractType&quot;, &quot;null&quot;);
    }
    if (!properties.isEmpty()) {
      throw new InvariantContractException(ContractValidator.class.getName(), properties);
    }

    this.contractPath = contractPath;
    this.contractType = contractType;
  }

  /**
   * Create operation&#39;s precondition contract validator. &lt;br/&gt; To apply this, operations should be
   * currying.
   *
   * @param operationPath operation&#39;s path like &lt;code&gt;com.happy.Power.love&lt;/code&gt;
   * @param &lt;T&gt;           message&#39;s type
   * @return Initialized validator
   * @throws InvariantContractException invalid arguments
   */
  public static &lt;T&gt; ContractValidator&lt;T&gt; preCondition(@NotBlank String operationPath) {
    return new ContractValidator&lt;&gt;(operationPath, ContractType.PRECONDITION);
  }

  /**
   * Create operation&#39;s postcondition contract validator.
   *
   * @param operationPath operation&#39;s path like &lt;code&gt;com.happy.Power.love&lt;/code&gt;
   * @param &lt;T&gt;           message&#39;s type
   * @return Initialized validator
   * @throws InvariantContractException invalid arguments
   */
  public static &lt;T&gt; ContractValidator&lt;T&gt; postCondition(@NotBlank String operationPath) {
    return new ContractValidator&lt;&gt;(operationPath, ContractType.POSTCONDITION);
  }

  /**
   * Create operation&#39;s invariant contract validator.
   *
   * @param classPath class&#39;s path like &lt;code&gt;com.happy.Power&lt;/code&gt;
   * @param &lt;T&gt;       message&#39;s type
   * @return Initialized validator
   * @throws InvariantContractException invalid arguments
   */
  public static &lt;T&gt; ContractValidator&lt;T&gt; invariantCondition(@NotBlank String classPath) {
    return new ContractValidator&lt;&gt;(classPath, ContractType.INVARIANT);
  }

  /**
   * Test this contract to the target
   *
   * @param target message or class
   * @throws PreConditionContractException  whether &lt;code&gt;check&lt;/code&gt; fails itself or
   *                                        &lt;code&gt;ContractType.PRECONDITION&lt;/code&gt; fails.
   * @throws PostConditionContractException &lt;code&gt;ContractType.POSTCONDITION&lt;/code&gt; fails.
   * @throws InvariantContractException     &lt;code&gt;ContractType.INVARIANT&lt;/code&gt; fails.
   */
  public void check(@NotNull T target) {
    if (target == null) {
      throw new PreConditionContractException(ContractValidator.class.getName(),
          Map.of(&quot;target&quot;, &quot;null&quot;));
    }
    try (final var validatorFactory = buildDefaultValidatorFactory()) {
      final var validator = validatorFactory.getValidator();
      final var errors = validator.validate(target);
      if (errors.isEmpty()) {
        return;
      }
      final Map&lt;String, Object&gt; properties = errors.stream().collect(
          Collectors.toMap(violation -&gt; violation.getPropertyPath().toString(),
              ConstraintViolation::getMessage));
      switch (contractType) {
        case PRECONDITION -&gt; throw new PreConditionContractException(this.contractPath, properties);
        case POSTCONDITION -&gt;
            throw new PostConditionContractException(this.contractPath, properties);
        case INVARIANT -&gt; throw new InvariantContractException(this.contractPath, properties);
      }
    } catch (ValidationException e) {
      throw new PreConditionContractException(ContractValidator.class.getName(), Map.of(), e);
    }
  }

  private enum ContractType {
    INVARIANT, PRECONDITION, POSTCONDITION,
  }
}</code></pre>
<table>
<thead>
<tr>
<th>Operation</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td><code>preCondition</code></td>
<td>사전조건 검증기 생성</td>
</tr>
<tr>
<td><code>postCondition</code></td>
<td>사후조건 검증기 생성</td>
</tr>
<tr>
<td><code>invariantCondition</code></td>
<td>불변식 검증기 생성</td>
</tr>
<tr>
<td><code>check</code></td>
<td>검증 수행</td>
</tr>
</tbody></table>
<p>사용 예는 다음과 같다.</p>
<pre><code class="language-java">class User {
    private @NotBlink String name;
    private @Email String email;

    private User(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public static User create(String name, String email) {
        final var user = new User(name, email);
        ContractValidator.&lt;User&gt;invariantCondition(User.class.getName()).check(user);
        return user;
    }

    public void update(UserUpdate spec) {
        final var operationPath = User.class.getName() + &quot;.update&quot;;
        ContractValidator.&lt;UserUpdate&gt;preCondition(operationPath).check(spec);

        this.name = spec.name();
        this.email = spec.email();

        ContractValidator.&lt;User&gt;invariantCondition(User.class.getName()).check(this);
    }

    public User getItself() {
        final var operationPath = User.class.getName() + &quot;.getItself&quot;;
        ContractValidator.&lt;User&gt;postCondition(operationPath).check(this);
        return this;
    }
}

public record UserUpdate(@NotBlink String name, @Email email) {}</code></pre>
<table>
<thead>
<tr>
<th>Operation</th>
<th>Conditions</th>
</tr>
</thead>
<tbody><tr>
<td><code>create</code></td>
<td>불변식</td>
</tr>
<tr>
<td><code>update</code></td>
<td>사전조건, 불변식</td>
</tr>
<tr>
<td><code>getItself</code></td>
<td>사후조건</td>
</tr>
</tbody></table>
<h1 id="참고-자료">참고 자료</h1>
<ul>
<li><a href="https://product.kyobobook.co.kr/detail/S000001766367">Object</a></li>
<li><a href="https://velog.io/@happyjamy/Spring-%EC%9D%B4-Bean-validation-%EC%9D%84-%EC%88%98%ED%96%89%ED%95%98%EB%8A%94-%EB%B2%95-Hibernate-Validator-%EC%99%80-Spring-Validator-%EC%B0%A8%EC%9D%B4">Spring Bean Validation</a></li>
<li><a href="https://jakarta.ee/specifications/bean-validation/3.1/jakarta-validation-spec-3.1.html#builtinconstraints">Jakarta Validation</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[AI Agent의 맥락(Conext) 톺아보기]]></title>
            <link>https://velog.io/@sad-zero/ai-agent-context-role</link>
            <guid>https://velog.io/@sad-zero/ai-agent-context-role</guid>
            <pubDate>Sun, 23 Mar 2025 13:12:10 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>AI Agent는 정보를 언어로 작성된 프로그램입니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/sad-zero/post/bb980e2a-a413-46dd-a640-361be265c725/image.png" alt="AI Agent Architecture"></p>
<p>AI Agent는 사용자나 외부에서의 요청을 인지하고 분석, 추론, 계획, 정보 획득, 외부와의 상호작용, 피드백 등의 과정을 거쳐 그것을 해결하고자 시도하는 프로그램입니다. 요청에서 응답까지 필요한 여러 요소들 중, <strong>정보 획득</strong>은 Agent의 보급에 큰 영향을 미쳤습니다. 왜 그럴까요?</p>
<p>AI Agent는 LLM에 기반하기 때문에 맥락 학습(In-context learning)이라는 강력한 장점을 공유합니다. 맥락 학습은 사용 시점에 추가적인 정보를 학습시키는 것으로, 이를 통해 마치 우리가 하드웨어와 소프트웨어를 독립적으로 다루듯 중심 모델(GPT, Claude, Gemini, etc)과 Agent를 독립적으로 다룰 수 있게 만들었습니다. 그 결과, AI Agent는 낮은 비용으로도 개발 및 사용할 수 있게 되었습니다.</p>
<p>이런 추세에 따라 현재 시점에서 AI Agent를 개발한다는 것은 도메인에 적합하게 모델을 학습하는 것보다, <strong>도메인에 적합한 정보를 제공하는 방법</strong>을 고민하고 있습니다. 그럼 어떻게 정보를 제공해야 할까요? 사실, 단순한 기능(일회성 QA 등)만 제공하는 Agent라면 복잡한 방법을 사용할 필요 없이 관련된 모든 정보를 중심 모델에 제공해도 괜찮을 수 있습니다. 모델들은 갈수록 더 많은 맥락과 맥락 속에서 더 정확하게 정보를 찾을 수 있도록 향상되고 있기 때문입니다. 하지만, 이런 접근 방식은 비용, 속도, 보안 등 다양한 요소에서 부적절하고 머지않아 한계에 다다르게 됩니다.</p>
<p>적절한 정보를 제공하기 위해서는 우선 <strong>정보란 무엇인지</strong> 알아야 합니다. 특정 도메인(<strong>IoT</strong>, <strong>스마트 시티</strong> 등)과 플랫폼, 서비스는 자신만의 용어, 개체, 관계 등을 보유하고 있습니다. 지식 표현(Knowledge Representation) 관점에 따라 이들은 Ontology와 Knowledge Graph로 정리될 수 있습니다.</p>
<table>
<thead>
<tr>
<th>정의</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>Ontology</td>
<td>도메인에 대한 일반화된 선언문으로, 용어와 일반적인 관계 등을 정의한 표현법</td>
</tr>
<tr>
<td>Knowledge Graph</td>
<td>도메인에서 실제로 생성되는 개체와 개체간 관계를 정의한 표현법</td>
</tr>
</tbody></table>
<p>Ontology에 따라 정리된 도메인은 Knowledge Graph를 통해 실제 개체 사이의 연관성을 파악할 수 있습니다. 또한, 서로 다른 도메인이 Knowledge Graph를 통해 연결될 수 있기에 확장 가능한 형태로 도메인 정보를 관리할 수 있습니다. 이러한 내용에서 알 수 있듯 이것은 데이터 사전(Data Dictionary)를 만드는 것과 크게 다르지 않습니다. 따라서 이미 데이터 사전을 갖고 있었다면 그것을 사용할 수 있고, 만약 없다면 지식 표현을 구축함으로써 표준화되고 일반화된 데이터 사전을 함께 만들 수 있습니다.</p>
<p>AI Agent가 사용하는 정보는 위와 같이 간략히 정리할 수 있습니다. 이제 처음 질문으로 돌아가 <strong>어떻게 정보를 제공할 것인가</strong>를 고민할 필요가 있습니다. Agent가 받아들일 수 있는 맥락은 제한되어 있기 때문에, 요청을 해결하는데 필요한 정보만을 제공해야 합니다. 컴퓨터에 빗대어 생각하면, 한정된 메모리에 최적의 데이터를 올리는 것과 유사합니다. 이를 위해서는 <strong>정보를 쪼개고, 연관된 정보를 묶으며, 이들을 찾을 수 있어야 합니다.</strong> 상호작용의 관점에서 정보는 다음과 같이 나뉠 수 있습니다.</p>
<table>
<thead>
<tr>
<th>유형</th>
<th>설명</th>
<th>유사 정의</th>
</tr>
</thead>
<tbody><tr>
<td>일시적인 정보(Episodic)</td>
<td>하나의 주제에 대한 상호작용</td>
<td>대화(Multi-turns)</td>
</tr>
<tr>
<td>작업에 대한 정보(Working)</td>
<td>하나의 주제에 대한 상호작용 결과</td>
<td>주제(Topic)</td>
</tr>
</tbody></table>
<p>Working Memory는 Episodic Memory를 요약한 것으로, 압축된 정보를 생성합니다. 이와 유사하게 도메인 정보 역시 세부 요소에 따라 나뉘고, 이들에 대해 압축된 정보를 가질 수 있습니다. 이렇게 나뉜 정보들은 검색 가능한 형태로 색인되며, Agent의 한정된 메모리에 가장 적절한 정보만이 올라갈 수 있도록 구성됩니다. 이에 대한 도식은 아래와 같습니다.
<img src="https://velog.velcdn.com/images/sad-zero/post/d1446b23-ba5f-48e6-a05d-53094f5f9d9f/image.png" alt="Agent Memory System"></p>
<p>AI Agent는 외부 사용자와의 상호작용에 필요한 최적의 정보를 메모리에 갱신하고, 그것을 사용하여 응답합니다. 메모리는 도메인과 상호작용이라는 두 개의 영역으로 나뉘고, 각 영역마다 핵심 정보와 그와 연관된 세부 정보들로 채워집니다. 즉, 메모리는 일종의 계층 구조로 구성되며 이러한 구조를 통해 추가적인 정보가 필요할 때 정보간 연관성을 잃지 않고 확장될 수 있습니다.</p>
<p>마지막으로 고민해야 할 문제는 <strong>메모리를 어떻게 갱신할 것인가?</strong> 입니다. 메모리는 한정되어 있기 때문에 현재의 상호작용과 가장 관련성이 높은 정보만으로 메모리를 유지해야 합니다. 이를 위해 일반적인 캐시 전략을 도입해볼 수 있습니다. FIFO(First-In, First-Out)나 LRU(Least Recently Used) 같은 전략을 사용함으로써 메모리를 매번 초기화 후 갱신하지 않고도 적절한 정보를 남겨둘 수 있습니다. 관련성이 높은 정보인지는 핵심 정보(Semantic/Working Memory)의 적중 여부(Hit Ratio)를 통해 검증 가능하며, 불일치 시 연관된 세부 정보를 함께 지움으로써 관련성 없는 정보를 제거할 수 있습니다.</p>
<p>AI Agent는 이전에는 자동화할 수 없던 것들을 자동화하고 있습니다. Agent가 이를 할 수 있던 핵심 요소는 맥락 학습으로, 기반 모델과 정보를 분리시켜 마치 프로그래밍하는 것처럼 정보의 조합으로 Agent를 만들 수 있게 하였습니다. 현재까지도 <strong>어떻게 정보를 제공하는가</strong>에 대한 명확한 지침은 없기 때문에 앞으로 더 효율적인 기법이 나오기를 기대합니다.</p>
<h1 id="참고-자료">참고 자료</h1>
<ul>
<li><a href="https://www.linkedin.com/pulse/ai-agents-memory-context-retention-beyond-short-ganesh-jagadeesan-7hcoc/">AI Agents with Memory: Context Retention Beyond Short Prompts</a></li>
<li><a href="https://arxiv.org/pdf/2409.14908">KARMA: Augmenting Embodied AI Agents with Long-and-short Term Memory Systems</a></li>
<li><a href="https://product.kyobobook.co.kr/detail/S000213991301">프롬프트 엔지니어의 업무일지</a></li>
<li><a href="https://arxiv.org/html/2503.12687v1">AI Agents: Evolution, Architecture, and Real-World Applications</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[명세 주도 개발 톺아보기]]></title>
            <link>https://velog.io/@sad-zero/%EB%AA%85%EC%84%B8-%EC%A3%BC%EB%8F%84-%EA%B0%9C%EB%B0%9C-%ED%86%BA%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@sad-zero/%EB%AA%85%EC%84%B8-%EC%A3%BC%EB%8F%84-%EA%B0%9C%EB%B0%9C-%ED%86%BA%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Thu, 23 Jan 2025 08:57:03 GMT</pubDate>
            <description><![CDATA[<h1 id="서론">서론</h1>
<h2 id="목표">목표</h2>
<ul>
<li>명세 주도 개발 목표 이해하기</li>
<li>명세 주도 개발 장단점 이해하기</li>
<li>명세 주도 개발 Java/Springboot Boilerplate 이해하기</li>
</ul>
<h2 id="마인드-맵">마인드 맵</h2>
<p><img src="https://velog.velcdn.com/images/sad-zero/post/278c29b1-cbef-4687-9825-4c8f2f9f6dae/image.png" alt="Mind Map"></p>
<h2 id="핵심-키워드">핵심 키워드</h2>
<table>
<thead>
<tr>
<th>Keyword</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td>SSOT(Single Source of Truth)</td>
<td>명세, 계약과 관련된 문서를 각각 오직 하나만 관리<br></td>
</tr>
<tr>
<td>Specification(명세)</td>
<td>외부 사용자와의 IF 명세</td>
</tr>
<tr>
<td>Contract(계약)</td>
<td>외부 사용자의 Usecase 문서</td>
</tr>
<tr>
<td>OAS(Open API Specification)</td>
<td>API 명세 작성 문법</td>
</tr>
<tr>
<td>통합테스트</td>
<td>모듈(Micro Service) 수준에서 다른 모듈과의 IF 테스트</td>
</tr>
<tr>
<td>TDD(Test Driven Development)</td>
<td>구현 작성 전, 테스트를 먼저 작성하여 오류를 방지</td>
</tr>
<tr>
<td>SDD(Specification Driven Development)</td>
<td>코드 작성 전, 명세를 먼저 작성하여 오류를 방지</td>
</tr>
</tbody></table>
<h1 id="본문">본문</h1>
<h2 id="명세-주도-계약-정의">명세 주도 계약 정의</h2>
<p>명세 주도 계약(SDD)은 <strong>명세</strong>와 <strong>계약</strong>을 <strong>SSOT</strong>로 사용함으로써 다음 항목들을 보장하는 것을 목표로 합니다.</p>
<ul>
<li>코드 수준의 재작성(Boilerplate)을 최소화</li>
<li>SSOT의 변경이 코드에 실질적인 제약으로 동작하도록 강제</li>
<li>Usecase를 벗어나는 과잉 개발 방지</li>
</ul>
<h2 id="사용-이유">사용 이유</h2>
<p><img src="https://velog.velcdn.com/images/sad-zero/post/fd7e59bf-82b5-4bce-bc6b-e7dcdd09a945/image.png" alt="SDD 상태 전이"></p>
<p>최근의 개발 환경은 Extream Programming(XP)의 방법론을 도입하는 사례가 증가하고 있습니다. 켄트 벡의 <a href="https://product.kyobobook.co.kr/detail/S000002090485">XP Explained 2nd</a>에서 확인할 수 있듯 XP는 다음과 같은 개발 주기를 가지고 있습니다.</p>
<ol>
<li>현재 주기의 개발 목표(Story) 수립</li>
<li>개발 목표로부터 명세(Specification) 추출</li>
<li>명세를 반영하는 설계 변경점(Design) 추출</li>
<li>설계 변경점으로부터 세부 항목(Task) 추출</li>
<li>세부 항목에 대한 검증 항목(Test) 추출</li>
<li>검증 항목으로부터 개발(Code) 수행</li>
<li>검증 항목과 개발을 토대로 추가 설계 변경 여부 판단</li>
<li><code>1-7</code> 반복</li>
</ol>
<p>이러한 주기를 반복하는 것으로 서비스는 <strong>적정 수준</strong>을 유지하며 성장할 수 있습니다. 그런데, 위의 개발 주기를 반복하여 수행하다보면 몇 가지 개선할 수 있는 부분을 찾을 수 있습니다. 서비스가 <a href="https://product.kyobobook.co.kr/detail/S000001514402">도메인 주도 설계</a>의 원칙에 맞춰 개발되고 있다면, 도메인 기능과 외부 연결(API, Event 등) 기능은 독립적으로 유지될 것입니다. 이때 외부 연결 기능은 테스트와 코드에 있어 일정한 패턴을 보이곤 합니다. 예를 들어, Springboot로 API를 개발한다고 하면 다음과 같은 패턴이 발견됩니다.</p>
<ol>
<li>API 명세 작성</li>
<li>API 명세의 사용사례에 맞춰 <code>@SpringbootTest</code>로 통합 테스트 작성</li>
<li><code>@Controller</code>와 <code>ResponseEntity</code>를 사용하는 API IF 작성</li>
<li>API IF에 맞춰 도메인 접근 기능 개발</li>
</ol>
<p>위 과정 중 <code>2-3</code> 번은 문서로 작성된 명세와 사용사례를 단순히 코드로 옮기는 것에 지나지 않을 가능성이 큽니다. 만약 그런 경우, 이들을 코드로 옮기는 것은 <code>Translator</code>의 도움을 받아 자동화할 수 있습니다. <strong>SDD는 바로 이 지점을 해결하기 위한 방법론입니다.</strong> SSOT로부터 IF와 테스트를 번역함으로써 불필요한 부가 작업을 없애고, 혹시 모를 누락의 문제를 해결할 수 있습니다.</p>
<p>또한, SSOT로부터 코드와 테스트가 촉발되기 때문에 <strong>SSOT의 갱신은 서비스에 즉각적인 피드백으로 동작합니다.</strong> 이것은 <a href="https://product.kyobobook.co.kr/detail/S000060827347">진화적 아키텍처</a>에서 말하는 Fitness 함수로써 동작할 여지가 있는데, 이 관점에서 이러한 피드백은 명세와 실제 서비스의 간극을 최소화하는 제약으로 동작할 수 있습니다.</p>
<p>마지막으로, 계약이 통합 테스트로 동작하기 때문에 계약의 범위를 넘어선 과잉 개발을 방지하고, 현재 단계의 적정 수준을 유지하도록 유도할 수 있습니다.</p>
<h2 id="장단점">장단점</h2>
<p>소프트웨어 세상의 모든 것들이 그러하듯, SDD 역시 은탄환은 아닙니다. 따라서 다음의 장단점을 살펴보고, 적정 수준에서 도입하는 것이 필요합니다.</p>
<table>
<thead>
<tr>
<th>장점</th>
<th>단점</th>
</tr>
</thead>
<tbody><tr>
<td>이해관계자가 확인 가능한 명세, 계약을 사용</td>
<td>추가적인 DSL 계층 도입<br>- 서비스 소비자가 계약 작성 방법 학습 필요<br>- 명세, 계약 작성 및 검토 방법 학습 필요</td>
</tr>
<tr>
<td>반복작업 최소화</td>
<td>자동 생성된 코드 외 추가 기능이 필요한 경우, 더 복잡</td>
</tr>
<tr>
<td>요구사항과 구현의 정합성 유지</td>
<td>개발 속도 저하 가능성 있음(선제적 명세 체계화)</td>
</tr>
<tr>
<td>과잉 개발 억제</td>
<td>단일 모듈/서비스 수준에서의 적용은 큰 의미 없음</td>
</tr>
</tbody></table>
<h2 id="javaspringboot-boilerplate">Java/Springboot Boilerplate</h2>
<blockquote>
<p><a href="https://github.com/sad-zero/template-java">Github Repository</a></p>
</blockquote>
<p>위의 Github Repository는 SDD를 Java/Springboot로 간단히 구현한 것입니다. 해당 Repository의 <code>README.md</code>에도 작성하였지만, 주요 내용은 다음과 같습니다.</p>
<ul>
<li>API 명세는 <a href="https://swagger.io/specification/">Open API Specification3</a>로 작성되며, <a href="https://github.com/OpenAPITools/openapi-generator/tree/master/modules/openapi-generator-gradle-plugin">OpenAPI Generator</a>를 통해 API IF로 번역됩니다.<ul>
<li><code>contracts</code>에 예시를 포함하였습니다.</li>
<li><code>./gradlew compileJava</code> 실행 시 <code>build/generated</code>에 API IF가 생성됩니다.</li>
</ul>
</li>
<li>계약은 <a href="https://spring.io/projects/spring-cloud-contract">Spring Cloud Contract</a>로 작성됩니다.<ul>
<li><code>src/test/java/love/you/babe/contracts</code>와 <code>src/test/resources/contracts</code>에 예시를 포함하였습니다.</li>
<li><code>./gradlew contractTest</code> 실행 시 계약에 대한 통합 테스트가 수행됩니다.</li>
</ul>
</li>
</ul>
<h1 id="결론">결론</h1>
<p>명세 주도 개발은 XP 방법론의 일부를 자동화하기 위한 것으로, 외부 연동을 SSOT를 통해 관리합니다. 그 결과, 반복되는 코드와 테스트를 줄일 수 있고, 핵심 기능에 집중할 수 있습니다. 하지만 다른 모든 것들이 그러하듯 SDD 역시 장단점을 명확히 파악하고 사용해야 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[실용주의 사고와 학습]]></title>
            <link>https://velog.io/@sad-zero/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%EC%82%AC%EA%B3%A0%EC%99%80-%ED%95%99%EC%8A%B5</link>
            <guid>https://velog.io/@sad-zero/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%EC%82%AC%EA%B3%A0%EC%99%80-%ED%95%99%EC%8A%B5</guid>
            <pubDate>Sun, 05 Jan 2025 03:47:04 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://product.kyobobook.co.kr/detail/S000001766246">Pragmatic Thinking &amp; Learning(by Andy Hunt)</a></p>
</blockquote>
<h1 id="mind-map">Mind Map</h1>
<p><img src="https://velog.velcdn.com/images/sad-zero/post/2ab67200-49a9-40f9-8d11-624acef2a2a7/image.png" alt="Mind Map"></p>
<h1 id="keywords">Keywords</h1>
<table>
<thead>
<tr>
<th>Keyword</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td>드라이퍼스 모델</td>
<td>초보부터 전문가까지 각 단계 및 요구 역량을 체계적으로 정리한 모델</td>
</tr>
<tr>
<td>Wetware</td>
<td>뇌를 CPU에 빗대어 표현한 것</td>
</tr>
<tr>
<td>Inner Game</td>
<td>빠른 학습을 위해 관찰과 피드백을 섞는 것</td>
</tr>
<tr>
<td>SMART</td>
<td>체계적인 목표 관리 방법</td>
</tr>
<tr>
<td>SQ3R</td>
<td>체계적인 독서 방법</td>
</tr>
<tr>
<td>Mind Map</td>
<td>핵심 내용을 시각적 자료로 표현하는 방법</td>
</tr>
</tbody></table>
<h1 id="questions">Questions</h1>
<h2 id="전문가는-어떻게-되지">전문가는 어떻게 되지?</h2>
<p>모든 분야에는 초보자부터 전문가까지 많은 사람들이 분포되어 있다. <strong>드라이퍼스 모델</strong>은 어느 수준에 도달해야 전문가로 인정받을 수 있는지, 그리고 그곳에 도달하려면 어떻게 해야하는지 체계적으로 정리한 모델이다.</p>
<table>
<thead>
<tr>
<th>Level</th>
<th>Required</th>
</tr>
</thead>
<tbody><tr>
<td>Novice</td>
<td>지침서를 보고 따라할 수 있다.</td>
</tr>
<tr>
<td>Advanced Beginner</td>
<td>지침서를 보고 활용할 수 있다.</td>
</tr>
<tr>
<td>Competent</td>
<td>국소적인 모델을 정립하고 문제를 찾아서 해결할 수 있다.</td>
</tr>
<tr>
<td>Proficient</td>
<td>모델을 정립하고 자가 교정 및 사례를 통한 학습을 할 수 있다.</td>
</tr>
<tr>
<td>Expert</td>
<td>직관을 통해 문제를 해결할 수 있다.</td>
</tr>
</tbody></table>
<p>각 단계는 계단식으로 이루어진다. 상위 단계로 올라가기 위해서는 자신보다 앞 단계에 있는 사람들을 보고, 모방하고, 조언을 받아야 한다. 따라서 각 단계별로 필요한 환경이 다름을 인정해야 한다. 맥락에 따라 학습해야 하고, 다음을 고려해야 한다.</p>
<ul>
<li>초보자에게 큰 그림을 보도록 강요하지 말자.</li>
<li>전문가에게는 지침서(규칙)을 강요하지 말자.</li>
<li>팀은 초보자부터 전문가까지 섞이는게 좋다(나무를 보는 사람과 숲을 보는 사람 모두 필요하다.)</li>
<li>업무/학습에 있어 맥락을 반드시 고려해야 한다. 맥락에서 벗어난 객관성(성과 지표 등)을 강요하는 것은 무의미하다.<h2 id="논리와-직관은-어떤-관계가-있지">논리와 직관은 어떤 관계가 있지?</h2>
Wetware는 세상을 <strong>L 모드와 R 모드</strong>&quot; 두 가지 관점에서 인식하고, 판단한다.
L 모드는 분석적으로 세상을 바라보는 관점으로, 세상을 모델링을 통한 규격화, 절차와 규칙 등으로 예측 가능하고 이해 가능한 상태로 만든다. 즉, 논리로서 세상을 바라본다.
R 모드는 현재 상황과 사전 경험 등을 종합해 중간 단계를 건너뛰고 결과를 도출한다. 규격화되지 않은 확신으로 결론을 세운다. 즉, 직관으로서 세상을 바라본다.</li>
<li><em>R 모드가 동작하기 위해서는 L 모드가 선행되어야 한다.*</em> 직관은 수많은 경험의 총화이기 때문이다. 또한, <strong>R 모드의 결론을 인정받기 위해서는 L 모드가 뒤따라야 한다.</strong> 결론은 그 타당성을 검증받을 때 받아들여지기 때문이다.
그 결과 다음과 같은 도식이 만들어질 수 있다.
<img src="https://velog.velcdn.com/images/sad-zero/post/1176707c-997f-44c4-8a6d-5890d0a70480/image.png" alt="논리와 직관의 관계"><h3 id="inner-game">Inner Game</h3>
교수자와 학습자가 있고, 학습자의 효과적인 학습을 위해 사용하는 방법론이다. 크게 <strong>관측, 실험, 그리고 조언</strong>을 반복하여 수행함으로써 짧은 기간 동안 많은 지식을 학습할 수 있게 만든다.
이는 Agile에서 말하는 실행 가능한 상태를 많이 반복함으로써 결과를 향상시키는 방법과 유사하다. Inner Game을 통해 교수자는 학습자가 어떤 부분에서 막히고 있고, 어떤 교정을 받아야 하는지 파악할 수 있고, 학습자는 완결된 절차를 반복함으로써 실력을 향상할 수 있다.<h3 id="documantation">Documantation</h3>
Wetware는 Primary/Secondary Storage에 기억을 저장한다. 주기억장치는 빠르게 떠올릴 수 있지만 쉽게 휘발된다. 학습한 내용은 몇 주간 주기억장치에 머무를 수는 있으나, 결과적으로 사라질 것이다. 하지만 그것은 망각되지 않고 보조기억장치에 옮겨진다.
보조기억장치에 저장된 기억은 빠르게 떠오릴 수 없지만, 특정 키워드를 통해 검색될 수 있다. 검색된 기억은 다시 Primary로 복사되어 이용된다. 자주 필요한 기억은 이 과정을 수차례 거치며 Primary의 Cache 영역에 정착할 것이고, 큰 노력없이 이용할 수 있다.
위의 내용을 학습에 적용하면, 우리가 배우고 바로 사용하지 않는 내용은 결국 보조기억장치에 있을 것이다. <strong>필요할 때 이것을 떠올리기 위해서는 키워드를 관리해야 한다.</strong> 이것은 배운 내용을 문서화함으로써 해결할 수 있다. 문서화는 모든 내용을 적는 것이 아니라, 핵심이 되는 키워드, 문구, 그리고 내용 등을 정리함으로써 보조기억장치에 있던 기억을 다시 주기억장치로 불러오는 것을 목표로 한다.<h2 id="어떻게-학습을-관리하지">어떻게 학습을 관리하지?</h2>
<h3 id="sq3r-독서법">SQ3R 독서법</h3>
SQ3R 독서법은 학습을 위한 독서에 사용할 수 있는 방법론이다. 학습을 위해 책을 읽을 때, 무작정 책의 첫 페이지부터 읽는 것은 비효율적이다. SQ3R은 책을 읽고자 한 이유와 책을 읽고 얻은 내용을 정리하는 방법론으로 다음 단계를 거친다.</li>
</ul>
<ol>
<li>Survey/Question: 책의 초록, 목차를 읽고 궁금한 내용, 알고 싶은 내용을 질문으로 작성한다.</li>
<li>Read: 책을 빠르게 훑는다. 이때 중요한 것은 책의 모든 세부 내용을 파악하려 하지 않고, 전체적인 내용과 처음 떠올렸던 질문들의 답을 생각하며 읽는 것이다.</li>
<li>Recite: Question에 대한 답의 초록을 작성한다. 이때 책을 다시 들춰보며 쓰는게 아니라, 기억 속에 남은 내용을 더듬어가며 답변을 작성해야 한다.</li>
<li>Review: 책을 다시 읽으며 질문과 답변이 제대로 이어졌는지 확인한다. 잘못되거나 부족한 질문, 답변이 있다면 교정하고, 책의 내용을 전반적으로 이해한다.
위 4가지 과정은 질답의 형식으로 독서를 바라보고 속독과 숙독을 섞어 읽도록 권장한다. 이 방법론을 통해 최초 책을 읽은 동기에서 벗어나지 않고, 완결성있게 책을 읽을 수 있게 만든다.<h3 id="mind-map-1">Mind Map</h3>
Mind Map은 그래프 형태로 근원으로부터 1차, 2차, 3차 등 깊이를 더해가며 내용을 정리하는 방법이다. Mind Map을 그릴때는 처음부터 완결성 있게 그리려는 욕심을 버려야 한다. 해당 내용에 대한 이해가 더해짐에 따라 Map은 수정될 수 있다. 또한 Map을 예쁘게 그리는 여러 도구를 사용할 필요는 그다지 없다. 그런 도구는 보고용 Map을 그리는데 특화되었고, 창발적으로 뻗어나가는 초안을 관리하는데 불편하다.
위의 SQ3R 독서법에도 Mind Map을 적용할 수 있다. 책의 제목을 근원으로 하여, 질문들을 뻗어 나가고, 그것들에 대한 답(키워드)를 추가해감으로써 책에 대한 시각적 지표를 만들 수 있다.<h2 id="집중력을-유지하는-방법은">집중력을 유지하는 방법은?</h2>
<h3 id="smart">SMART</h3>
SMART는 목표를 체계적으로 관리해 집중력을 유지하도록 돕는 방법론이다. </li>
</ol>
<ul>
<li><p>Specific: 구체적인 목표를 설정해야 한다.</p>
</li>
<li><p>Measurable: 목표를 얼마나 달성했는지 수치로 표현할 수 있어야 한다.</p>
</li>
<li><p>Achievable: 도달 가능한 목표를 설정해야 한다.</p>
</li>
<li><p>Relatant: 현재의 최종 목표와 관련있어야 한다. 관련없는 목표를 위한 노력은 쉽게 동력을 잃는다.</p>
</li>
<li><p>Time-bound: 목표 도달까지 무제한의 시간을 허용하면 안된다.
목표를 세울 때, 위의 5가지 관점에서의 그것을 검토하여 집중력있게 달성할 수 있다.</p>
<h3 id="context-switch">Context Switch</h3>
<p>Multitasking은 별로다. 한번에 처리해야하는 업무가 늘어날수록 하나의 업무에 소홀해진다. 따라서 특정 시간 동안 하나의 업무만 처리하는편이 좋다. 하지만, 하루에 하나의 업무만 주어지는 것은 현실적으로 어렵다. 이때 중요해지는 것이 업무간 전환에 드는 비용을 최소화하는 것이다.
하루를 쪼개 여러 업무를 처리할 때, A 업무에서 B로 넘어가는 시간이 증가할수록 비효율이 증가한다. 이것을 줄이기 위해서는 여러가지 방법들을 사용할 수 있다.</p>
</li>
<li><p>Workspace 나누기: 업무별 Workspace를 나누고, 간단한 명령어, 조작만으로 다른 업무로 전환될 수 있게 한다.</p>
</li>
<li><p>업무별 투자 시간 정하기: 가능하다면 업무별로 처리해야 하는 시간을 명확하게 정의한다.</p>
</li>
<li><p>쉬는 시간에는 쉬기: 휴대폰 보는 것은 쉬는게 아니다. 머릿속 내용을 정리할 수 있는 시간을 정하고, 이것을 준수함으로써 다음 업무에 지장이 없도록 만든다.
Context Switch를 줄임으로써 업무를 효율적으로 처리할 수 있을 것이다.</p>
<h1 id="to-do-list">To Do List</h1>
</li>
<li><p>책의 내용을 Mind Map으로 정리해보기</p>
</li>
<li><p>Context Switch 비용을 최소화 하기 위해 업무별 시간을 정확히 분배하기</p>
</li>
<li><p>시각 자료를 이용하기 위해 화이트 보드 등 사용하기</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[독서록 구조]]></title>
            <link>https://velog.io/@sad-zero/%EB%8F%85%EC%84%9C%EB%A1%9D-%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@sad-zero/%EB%8F%85%EC%84%9C%EB%A1%9D-%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Sun, 05 Jan 2025 03:35:16 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>독서록은 SQ3R 구조를 참고하여 작성되었습니다.</p>
</blockquote>
<h1 id="mind-map">Mind Map</h1>
<p>독서록 구성에 대한 Mind Map을 표현하는 단락입니다.</p>
<h1 id="keywords">Keywords</h1>
<p>책의 주요 개념들에 대해 정리하는 단락입니다.</p>
<h1 id="questions">Questions</h1>
<p>책에 대해 궁금했던 질문들과, 질문에 대한 나름의 답들을 정리하는 단락입니다.</p>
<h2 id="question1">{Question1}</h2>
<p>질문에 대한 답을 정리하는 단락입니다.</p>
<h1 id="optional-to-do">(Optional) To Do</h1>
<p>책을 읽고 실천사항을 정리하는 단락입니다.</p>
]]></description>
        </item>
    </channel>
</rss>