<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>천천히 올바르게</title>
    <link>https://huisam.tistory.com/</link>
    <description>주로 개발에 대한 이야기들
Spring Kotlin k8s DB </description>
    <language>ko</language>
    <pubDate>Mon, 18 May 2026 12:43:06 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>huisam</managingEditor>
    <image>
      <title>천천히 올바르게</title>
      <url>https://tistory1.daumcdn.net/tistory/2997577/attach/26387a7b727f46fe8af42f0c7141c888</url>
      <link>https://huisam.tistory.com</link>
    </image>
    <item>
      <title>멱등한 HTTP 를 Idempotency-Key 를 통해 구현해보자</title>
      <link>https://huisam.tistory.com/entry/idempotent-http</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&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;u&gt;네트워크에 오류가 발생할 확률&lt;/u&gt;이 조금씩 늘어나게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;네트워크 오류&lt;/u&gt;는 &lt;span style=&quot;color: #f89009;&quot;&gt;일시적인 순단&lt;/span&gt;이거나 &lt;span style=&quot;color: #ee2323;&quot;&gt;connection timeout 이 발생하는 클라이언트 일시적인 오류&lt;/span&gt;거나 &lt;span style=&quot;color: #f89009;&quot;&gt;패킷 손실&lt;/span&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;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 &lt;b&gt;단순 retry 하는 것으로 끝나는 경우&lt;/b&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;그래서 Idempotent HTTP 를 설계하려는 노력들을 많이 시작하게 되었는데요. 한번 시작해보겠습니다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&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;span style=&quot;color: #ee2323;&quot;&gt;멱등성&lt;/span&gt;과 &lt;span style=&quot;color: #ee2323;&quot;&gt;회복탄력성&lt;/span&gt;에 대해 설명하고자 하는데요&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #444444; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;color: #444444;&quot;&gt;멱등성(Idempotency): 수학이나 전산학의 한 성질로, 연산을 여러번 적용하더라도 결과가 달라지지 않는 성질. &lt;span data-macro-name=&quot;mathinline&quot; data-hasbody=&quot;false&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #444444;&quot;&gt;회복탄력성(Resilience): 시스템이 외부 충격이나 장애 상황에서도 정상적인 기능을 유지하거나 빠르게 복구할 수 있는 능력&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Idempotency&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멱등성이 없는 HTTP API 는클라이언트가 요청을 보냈지만 요청시간이 초과되거나 불안정한 커넥션을 맞이한 경우, 클라이언트는 리소스의 상태에 불확실한 상태에 놓입니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;리소스가 생성되었는지, 업데이트 되었는지, 또는 서버가 요청을 처리를 완료했는지 &lt;span style=&quot;color: #ee2323;&quot;&gt;알 수가 없습니다&lt;/span&gt;.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #444444; text-align: start;&quot; 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;Gemini_Generated_Image_fgp2n1fgp2n1fgp2.png&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dL3vhP/dJMcaaSsYzM/gyvYKXKTRE8UfTEcCZIATK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dL3vhP/dJMcaaSsYzM/gyvYKXKTRE8UfTEcCZIATK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dL3vhP/dJMcaaSsYzM/gyvYKXKTRE8UfTEcCZIATK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdL3vhP%2FdJMcaaSsYzM%2FgyvYKXKTRE8UfTEcCZIATK%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;1408&quot; height=&quot;768&quot; data-filename=&quot;Gemini_Generated_Image_fgp2n1fgp2n1fgp2.png&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이것을 그림으로 설명하면 위와 같습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 리소스를 생성, 수정, 삭제하는 행위를 하는 API 에는 멱등성(Idempotency)을 제공하여 언제든 &lt;b&gt;재시도&lt;/b&gt;(retry) 할 수 있는 기반을 제공하고 시스템의 &lt;b&gt;회복탄력성&lt;/b&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;그러면 어떻게 설계해야 할까요?&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Architecture&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 설계방안들이 있지만, IETF 의 draft 로 올려놓은 설계안을 위주로 설계해보았습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.ietf.org/archive/id/draft-ietf-httpapi-idempotency-key-header-01.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.ietf.org/archive/id/draft-ietf-httpapi-idempotency-key-header-01.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1775994047431&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;The Idempotency-Key HTTP Header Field&quot; data-og-description=&quot;The following core rules are included by reference, as defined in Appendix B.1 of [RFC5234]: ALPHA (letters), CR (carriage return), CRLF (CR LF), CTL (controls), DIGIT (decimal 0-9), DQUOTE (double quote), HEXDIG (hexadecimal 0-9/A-F/a-f), LF (line feed), &quot; data-og-host=&quot;www.ietf.org&quot; data-og-source-url=&quot;https://www.ietf.org/archive/id/draft-ietf-httpapi-idempotency-key-header-01.html&quot; data-og-url=&quot;https://www.ietf.org/archive/id/draft-ietf-httpapi-idempotency-key-header-01.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.ietf.org/archive/id/draft-ietf-httpapi-idempotency-key-header-01.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.ietf.org/archive/id/draft-ietf-httpapi-idempotency-key-header-01.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;The Idempotency-Key HTTP Header Field&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The following core rules are included by reference, as defined in Appendix B.1 of [RFC5234]: ALPHA (letters), CR (carriage return), CRLF (CR LF), CTL (controls), DIGIT (decimal 0-9), DQUOTE (double quote), HEXDIG (hexadecimal 0-9/A-F/a-f), LF (line feed),&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.ietf.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 설계안에서는 Idempotency-Key HTTP header 를 optional 하게 제공하여,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 핵심이 되는 것은 아래와 같은 case 를 구분하는 것입니다&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;(첫 요청) HTTP 요청을 정상 처리했으면, &lt;span style=&quot;color: #ee2323;&quot;&gt;HTTP 200&lt;/span&gt; 을 내려주고, 요청과 응답을 repository 를 통해 저장한다(가급적 TTL 을 거는 것이 운영상 편리합니다)&lt;/li&gt;
&lt;li&gt;(두번째 요청) 동일한 Idempotency-Key 와 동일한 request body 로 요청이 오고 처리가 된게 server 쪽에 저장되어 있는 경우, 저장된 응답을 내려줍니다.&lt;/li&gt;
&lt;li&gt;(중복 요청) 동일한 Idempotency-Key 헤더 짧은 시간내로 중복 요청이 온 경우(=처리 진행중인 경우), &lt;span style=&quot;color: #ee2323;&quot;&gt;HTTP 409&lt;/span&gt; 를 내려주어 이미 처리중임을 client 에게 알립니다&lt;/li&gt;
&lt;li&gt;(오류 요청) 같은 Idempotency-Key 인데, 다른 request body 로 요청이 온 경우, &lt;span style=&quot;color: #ee2323;&quot;&gt;HTTP 422&lt;/span&gt; 를 내려주어 처리할 수 없음을 client 에게 알립니다&lt;/li&gt;
&lt;li&gt;(오류 요청) Idempotency-Key 헤더의 길이 제약에 실패한 경우, &lt;span style=&quot;color: #ee2323;&quot;&gt;HTTP 400&lt;/span&gt; 을 내려주어 요청이 잘못됨을 알립니다&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번은 흔한 성공 case 라 설명을 생략하겠습니다. 중요한 것은 2번 case 에 대한 기능을 client 에게 제공하여 쉽게 재시도할 수 있도록 가능성을 열어두는 것이죠&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3번 case 는 client 가 재시도하였으나, 너무 짧은 주기로 재시도한 경우 backoff 시간을 좀 더 길게 가져가라는 의미를 내려주는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4번 case 는 client 측 구현 실수 혹은 위변조 방지를 위한 기능입니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;846&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dFsxAW/dJMb99MJRSc/QVZnWx5zOvIyHnMmZE0sFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dFsxAW/dJMb99MJRSc/QVZnWx5zOvIyHnMmZE0sFK/img.png&quot; data-alt=&quot;API 스펙상 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dFsxAW/dJMb99MJRSc/QVZnWx5zOvIyHnMmZE0sFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdFsxAW%2FdJMb99MJRSc%2FQVZnWx5zOvIyHnMmZE0sFK%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;1280&quot; height=&quot;846&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;846&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;API 스펙상 예시&lt;/figcaption&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;이것을 코드로 구현하면 아래와 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1775994176112&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.huisam.springidempotency.idempotency.filter

import com.huisam.springidempotency.idempotency.domain.IdempotencyHeader
import com.huisam.springidempotency.idempotency.domain.IdempotentHttp
import com.huisam.springidempotency.idempotency.domain.IdempotentHttpRequest
import com.huisam.springidempotency.idempotency.domain.IdempotentHttpResponse
import com.huisam.springidempotency.idempotency.repository.IdempotentHttpRepository
import jakarta.servlet.FilterChain
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.slf4j.LoggerFactory
import org.springframework.boot.servlet.filter.OrderedFilter
import org.springframework.core.Ordered
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.stereotype.Component
import org.springframework.web.filter.OncePerRequestFilter
import org.springframework.web.util.ContentCachingResponseWrapper

/**
 * Filter for managing idempotent HTTP requests/responses based on [IdempotencyHeader.IDEMPOTENCY_KEY] value
 *
 * Since IdempotentHttpId is generated based on HTTP method and path, clients need to request a unique [IdempotencyHeader.IDEMPOTENCY_KEY] for each API.
 *
 * @property order The filter execution order defaults to the order before entering [org.springframework.web.servlet.DispatcherServlet]
 */
@Component
class IdempotentHttpWebMvcFilter(
    private val idempotentHttpWebMvcRegistry: IdempotentHttpWebMvcRegistry,
    private val idempotentHttpRepository: IdempotentHttpRepository,
    private val order: Int = Ordered.LOWEST_PRECEDENCE - 1,
) : OncePerRequestFilter(), OrderedFilter {
    private val log = LoggerFactory.getLogger(this::class.java)

    override fun doFilterInternal(
        request: HttpServletRequest,
        response: HttpServletResponse,
        filterChain: FilterChain
    ) {
        val httpMethod = request.method
        val httpPath = request.requestURI
        val idempotencyKey = request.getHeader(IdempotencyHeader.IDEMPOTENCY_KEY)

        when {
            idempotencyKey == null -&amp;gt; filterChain.doFilter(request, response)

            idempotencyKey.isBlank() || idempotencyKey.length &amp;gt; 100 -&amp;gt; {
                response.status = HttpStatus.BAD_REQUEST.value()
                response.contentType = MediaType.TEXT_PLAIN_VALUE
                response.writer.write(&quot;Idempotency-Key must be 1 to 100 characters and not blank.&quot;)
            }

            idempotentHttpWebMvcRegistry.notRegistered(httpMethod, httpPath) -&amp;gt; {
                response.status = HttpStatus.BAD_REQUEST.value()
                response.contentType = MediaType.TEXT_PLAIN_VALUE
                response.writer.write(&quot;Idempotency-Key is not allowed on $httpMethod $httpPath&quot;)
            }

            else -&amp;gt; processIdempotentHttp(
                idempotentHttpId = createIdempotentHttpId(httpMethod, httpPath, idempotencyKey),
                request = request,
                response = response,
                filterChain = filterChain,
            )
        }
    }

    private fun createIdempotentHttpId(httpMethod: String, httpPath: String, idempotencyKey: String): String {
        return &quot;$httpMethod::$httpPath::$idempotencyKey&quot;
    }

    private fun processIdempotentHttp(
        idempotentHttpId: String,
        request: HttpServletRequest,
        response: HttpServletResponse,
        filterChain: FilterChain,
    ) {
        val cachingRequest = ContentCachingRequestWrapper(request)
        val idempotentHttp = IdempotentHttp(
            idempotentHttpId = idempotentHttpId,
            request = IdempotentHttpRequest(
                method = cachingRequest.method,
                path = cachingRequest.requestURI,
                contentType = cachingRequest.contentType,
                body = cachingRequest.requestBody,
            ),
            response = null,
        )
        val existedIdempotentHttp = idempotentHttpRepository.findByIdOrNull(idempotentHttp.idempotentHttpId)

        when {
            existedIdempotentHttp == null -&amp;gt; {
                doIdempotentHttpFilter(idempotentHttp, cachingRequest, response, filterChain)
            }

            existedIdempotentHttp.isDifferentRequest(idempotentHttp.request) -&amp;gt; {
                response.status = HttpStatus.UNPROCESSABLE_ENTITY.value()
                response.contentType = MediaType.TEXT_PLAIN_VALUE
                response.writer.write(&quot;Request does not equal with original request.&quot;)
            }

            existedIdempotentHttp.response == null -&amp;gt; {
                response.status = HttpStatus.CONFLICT.value()
                response.contentType = MediaType.TEXT_PLAIN_VALUE
                response.writer.write(&quot;Request is still processing. Please try again later.&quot;)
                log.warn(&quot;Idempotent request is still processing. Please check the server latency or client retry interval --&amp;gt; $idempotentHttpId&quot;)
            }

            else -&amp;gt; {
                response.status = existedIdempotentHttp.response.status
                response.contentType = existedIdempotentHttp.response.contentType
                response.writer.write(existedIdempotentHttp.response.body)
            }
        }
    }

    private fun doIdempotentHttpFilter(
        idempotentHttp: IdempotentHttp,
        cachingRequest: ContentCachingRequestWrapper,
        response: HttpServletResponse,
        filterChain: FilterChain
    ) {
        recordRequest(idempotentHttp)

        val cachingResponse = ContentCachingResponseWrapper(response)
        try {
            filterChain.doFilter(cachingRequest, cachingResponse)

            recordResponse(idempotentHttp, cachingResponse)
            log.info(&quot;Success idempotent request --&amp;gt; ${idempotentHttp.idempotentHttpId}&quot;)
        } finally {
            // Respond cached response to client buffer
            cachingResponse.copyBodyToResponse()
        }
    }

    private fun recordRequest(idempotentHttp: IdempotentHttp) {
        idempotentHttpRepository.save(idempotentHttp)
    }

    /**
     * If saving the response fails, the request was processed but the response was not saved.
     * In this case, since reliable idempotent HTTP cannot be guaranteed, delete the HTTP record itself to avoid responding with a conflict(409) status.
     */
    private fun recordResponse(
        idempotentHttp: IdempotentHttp,
        cachingResponse: ContentCachingResponseWrapper
    ) {
        runCatching {
            val idempotentHttpResponse = IdempotentHttpResponse(
                status = cachingResponse.status,
                contentType = cachingResponse.contentType,
                body = String(cachingResponse.contentAsByteArray),
            )
            idempotentHttpRepository.save(idempotentHttp.recordResponse(idempotentHttpResponse))
        }.onFailure {
            log.error(&quot;Error while recording response for idempotencyHttp: ${idempotentHttp.idempotentHttpId}&quot;, it)
            deleteIdempotentHttp(idempotentHttp.idempotentHttpId)
        }
    }

    private fun deleteIdempotentHttp(idempotentHttpId: String) {
        runCatching {
            idempotentHttpRepository.deleteById(idempotentHttpId)
        }.onFailure {
            log.error(&quot;Error while deleting idempotentHttp: $idempotentHttpId&quot;, it)
        }
    }

    override fun getOrder(): Int = order
}&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;여기서 repository 를 선택하는 주요 요소는 TTL 이 지원되는 repository 를 고르는 것이 좋은데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis 를 이용하면 key value 형태의 command 요청으로 동시성 제어도 가능하고, 빠른 속도로 구현할 수 있게 되어 저는 redis 저장소를 추천드리는 편입니다.&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;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/huisam/spring-idempotency&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/huisam/spring-idempotency&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1775994902260&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - huisam/spring-idempotency: HTTP Idempotency based on Spring&quot; data-og-description=&quot;HTTP Idempotency based on Spring. Contribute to huisam/spring-idempotency development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/huisam/spring-idempotency&quot; data-og-url=&quot;https://github.com/huisam/spring-idempotency&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bFIE8a/dJMb8Rj28sD/udR87POSqgAfYHpjZyCKU1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/fYtVQ/dJMb8YpWA2Q/j0g308RiauC2Mv62WaKGrK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/Jlakl/dJMb8WeAPVl/57vCL36ZNVoFS3D8OGT4O1/img.png?width=1599&amp;amp;height=1058&amp;amp;face=0_0_1599_1058&quot;&gt;&lt;a href=&quot;https://github.com/huisam/spring-idempotency&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/huisam/spring-idempotency&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bFIE8a/dJMb8Rj28sD/udR87POSqgAfYHpjZyCKU1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/fYtVQ/dJMb8YpWA2Q/j0g308RiauC2Mv62WaKGrK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/Jlakl/dJMb8WeAPVl/57vCL36ZNVoFS3D8OGT4O1/img.png?width=1599&amp;amp;height=1058&amp;amp;face=0_0_1599_1058');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - huisam/spring-idempotency: HTTP Idempotency based on Spring&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;HTTP Idempotency based on Spring. Contribute to huisam/spring-idempotency development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Client configuration&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 client 측에서는 어떻게 Idempotency-Key header 와 retry 전략을 설정하는 것이 좋을까요?&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Idempotency-Key 헤더 입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Idempotency-Key 는 API 요청마다 유니크한 key 로 정의하면 됩니다.&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;UUID v4&amp;nbsp;를 사용할 것을 적극 권장&lt;/span&gt;합니다.&lt;/li&gt;
&lt;li style=&quot;color: #444444;&quot;&gt;&lt;span&gt;&lt;b&gt;Http Status 409&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;를 응답받은 경우, 아직 서버에서 요청을 처리하고 있어 일정시간 후에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;같은 Idempotency-Key 로 재시도&lt;/b&gt;가 가능합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #444444;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;서버에서는 요청의 결과를 성공/실패 상관없이 모두 기록&lt;/span&gt;합니다. 이는 &quot;클라이언트가 어떤 요청을 시도했고, 그 결과가 무엇이었는지&quot; 에 대한 불확실성을 제거하는데 초점을 둡니다.
&lt;ul style=&quot;list-style-type: circle; color: #444444;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li style=&quot;color: #444444;&quot;&gt;새로운 요청을 시도하고 싶다면, 새로운 Idempotency-Key 로 요청해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 성공/실패 를 상관없이 기록한다는 것의 의미는.. 비즈니스적인 실패 상황도 기록하는 것을 의미합니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들면, 결제 API 를 호출했는데, 카드 만료로 인해 실패한 경우, 해당 요청은 카드 만료로 실패했다고 응답한 것을 기록하는 것입니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Idempotency-Key 의 핵심은 요청에 대한 성공을 하나만 보장하기 위함이 아닌, 요청 자체의 성공/실패의 연산을 기록하는 것입니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;재시도에 관한 정책은 아래와 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #444444; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;color: #444444;&quot;&gt;재시도는 너무 많은 횟수를 지정하거나, 짧은 주기로 재시도한다면 서버에게 더 큰 악영향을 끼칠 수가 있습니다. 재시도 횟수를 제한하고, 지수적 backoff 전략을 고려할 것을 권장합니다.&lt;/li&gt;
&lt;li style=&quot;color: #444444;&quot;&gt;권장사항
&lt;ul style=&quot;list-style-type: circle; color: #444444;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li style=&quot;color: #444444;&quot;&gt;maxAttempts: 3&lt;/li&gt;
&lt;li style=&quot;color: #444444;&quot;&gt;exponential backoff
&lt;ul style=&quot;list-style-type: disc; color: #444444;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;color: #444444;&quot;&gt;initial delay: 2s&lt;/li&gt;
&lt;li style=&quot;color: #444444;&quot;&gt;multiplier: 2&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지수적 backoff 를 사용하는 이유는 서버의 부하를 막고, 처리중인 상황을 매번 안내받지 않도록 하는 것에 의의를 두고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;이는 API 마다 처리시간이 다르므로, API 별로 안내할 것을 적극 권장&lt;/span&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;대부분의 경우에는 위 권장사항으로 처리가능하나, 간혹가다 latency 가 늘어지는 API 도 있기에, latency 가 긴 API 들만 initial delay 를 살짝 조정해주면 됩니다.&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;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignLeft&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;007&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/007.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/007.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 이렇게 멱등성(Idempotency)과 Idempotency-Key HTTP header 를 통해 멱등한 API 를 제공하는 방법에 대해 알아보았네요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSA 환경에서 회복탄력성 있는 시스템을 운영하기 위해서는 알아야될 필수 요소이니&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 게시글을 통해 적용해보면 좋겠네요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Developer/Spring</category>
      <author>huisam</author>
      <guid isPermaLink="true">https://huisam.tistory.com/142</guid>
      <comments>https://huisam.tistory.com/entry/idempotent-http#entry142comment</comments>
      <pubDate>Sun, 12 Apr 2026 21:07:04 +0900</pubDate>
    </item>
    <item>
      <title>AI agent 란 무엇이고 agent 에게 tool 을 주어보자(feat. OpenAi)</title>
      <link>https://huisam.tistory.com/entry/ai-agent</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저번 시간에 우리는 아주 기초적인 LLM API 를 호출하고 간단하게 tool 을 활용하는 방법에 대해 배워보았습니다.&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;a href=&quot;https://huisam.tistory.com/entry/llm-api&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://huisam.tistory.com/entry/llm-api&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1774079990293&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;LLM API 를 이용하여 Text 와 Tool 을 제공해보자(feat. OpenAi)&quot; data-og-description=&quot;들어가며안녕하세요~! 저번 시간에는 우리가 LLM 의 기본 동작과 돌아가는 원리에 대해 살펴보았는데요오늘은 조금 더 실무 관점에서 어떤식으로 Frontier Model 을 사용할 수 있는지 설명해보려고 &quot; data-og-host=&quot;huisam.tistory.com&quot; data-og-source-url=&quot;https://huisam.tistory.com/entry/llm-api&quot; data-og-url=&quot;https://huisam.tistory.com/entry/llm-api&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/rRZUW/dJMb87f5Icc/7EpbXz72EO8PKVotzuszIk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/ggIyc/dJMb9cBHmv4/3kl8tP7x7YUbX5cEFdVQsk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cwbMqE/dJMb88674rc/wtfzToyZ24InuAuEvY15jK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024&quot;&gt;&lt;a href=&quot;https://huisam.tistory.com/entry/llm-api&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://huisam.tistory.com/entry/llm-api&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/rRZUW/dJMb87f5Icc/7EpbXz72EO8PKVotzuszIk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/ggIyc/dJMb9cBHmv4/3kl8tP7x7YUbX5cEFdVQsk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cwbMqE/dJMb88674rc/wtfzToyZ24InuAuEvY15jK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;LLM API 를 이용하여 Text 와 Tool 을 제공해보자(feat. OpenAi)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;들어가며안녕하세요~! 저번 시간에는 우리가 LLM 의 기본 동작과 돌아가는 원리에 대해 살펴보았는데요오늘은 조금 더 실무 관점에서 어떤식으로 Frontier Model 을 사용할 수 있는지 설명해보려고&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;huisam.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 매번 tool 관련된 복잡한 python code 를 작성하는 것은 보일러플레이트 코드를 양산하고,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;tool 이 많아질수록 코드는 더 복잡해지게 되겠죠&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 게시글에서는 위와 같은 불편함을 해결하기 위해, 등장한 AI agent 라이브러리를 간단하게 알아볼게요.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;수많은 AI agent 라이브러리들이 있지만, &lt;b&gt;OpenAI model 을 사용할 것이라 Open AI agents&lt;/b&gt; 를 기반으로 한번 설명드려보겠습니다~!&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;AI Agent&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본격적으로 들어가기 전에, AI agent 는 뭘까요? 도대체 우리는 무엇을 &lt;span style=&quot;color: #ee2323;&quot;&gt;AI agent&lt;/span&gt; 라고 부르고 있을까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI agent 에 정의는 시대에 따라 바뀌어왔지만, 개인적으로는 아래와 같이 &lt;span style=&quot;color: #ee2323;&quot;&gt;AI agent&lt;/span&gt; 라고 정의하고 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;To archieve a &lt;b&gt;goal&lt;/b&gt; using tools in a &lt;b&gt;loop&lt;/b&gt;&lt;br /&gt;&lt;b&gt;도구&lt;/b&gt;를 &lt;b&gt;반복적&lt;/b&gt;으로 사용하여 목표를 달성한다&lt;/blockquote&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;tool(도구)&lt;/b&gt; 들을 통해 주어진&amp;nbsp;&lt;b&gt;goal(목표)&lt;/b&gt;를 달성하기 위해 수행하는 것을 AI agent 라고 부릅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Gemini_Generated_Image_1sbpei1sbpei1sbp.png&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNY1dM/dJMcagLDm45/uKZZc3nOB5KeyKuu4ykS1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNY1dM/dJMcagLDm45/uKZZc3nOB5KeyKuu4ykS1k/img.png&quot; data-alt=&quot;AI agent&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNY1dM/dJMcagLDm45/uKZZc3nOB5KeyKuu4ykS1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNY1dM%2FdJMcagLDm45%2FuKZZc3nOB5KeyKuu4ykS1k%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;1408&quot; height=&quot;768&quot; data-filename=&quot;Gemini_Generated_Image_1sbpei1sbpei1sbp.png&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AI agent&lt;/figcaption&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;단순 LLM(Large Language Model) 의 token 예측기에서 어떻게 능동적으로 자율적으로 판단하여 목표를 이루는 것일까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 LLM 의 &lt;u&gt;next token 을 예측하는 방식은 그대로 사용&lt;/u&gt;하되, &lt;span style=&quot;color: #ee2323;&quot;&gt;tool 을 주고받는 interface 를 통해&lt;/span&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;tool 의 description 을 적어줌으로써, 어떤 상황에서 어떤 tool 을 사용하여 어떤 결과를 얻을 것인지 예측하여 실행하게 되는 것이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 한번의 tool 을 사용하는 것이 아닌 여러개의 tool 이 있다면, 상황에 따라 &lt;b&gt;반복적&lt;/b&gt;으로 tool 을 호출하여 목표를 달성하게 되는 것입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;OpenAI agents&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAI agents 는 OpenAI 에서 제공하는 agent 라이브러리입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 기능(Handoff, Guardrail, Agent as tool, Trace ..)들이 제공되지만, 앞으로 하나씩 상황에 맞게 소개해드릴 예정입니다 ㅎㅎ&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;아주 기초적인 Agent 를 예시 코드&lt;/b&gt;와 함께 설명드릴게요&lt;/p&gt;
&lt;pre id=&quot;code_1774083508101&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uv add openai-agents&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;openai-agents 라이브러리 의존성을 위 명령어를 통해 가져옵니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 이전에 Agent 라이브러리 없이 작성된 코드를 기억하시나요?&lt;/p&gt;
&lt;pre id=&quot;code_1774083043964&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import os

from dotenv import load_dotenv
from openai import OpenAI
from openai.types import Reasoning

load_dotenv()
open_ai = OpenAI(api_key=os.getenv(&quot;OPENAI_API_KEY&quot;))

tools = [
    {
        &quot;type&quot;: &quot;function&quot;,
        &quot;name&quot;: &quot;get_weather&quot;,
        &quot;description&quot;: &quot;Get current temperature for a given location.&quot;,
        &quot;parameters&quot;: {
            &quot;type&quot;: &quot;object&quot;,
            &quot;properties&quot;: {
                &quot;location&quot;: {
                    &quot;type&quot;: &quot;string&quot;,
                    &quot;description&quot;: &quot;City and country e.g. Bogot&amp;aacute;, Colombia&quot;,
                }
            },
            &quot;required&quot;: [&quot;location&quot;],
            &quot;additionalProperties&quot;: False,
        },
        &quot;strict&quot;: True,
    },
]

response = open_ai.responses.create(
    model=&quot;gpt-5-nano&quot;,
    input=&quot;What is the weather like in Paris today?&quot;,
    tools=tools,
    reasoning=Reasoning(effort=&quot;minimal&quot;)
)
print(response.output[1].to_json())

def get_weather(location):
    # 실제 날씨 API 호출 (예시에서는 하드코딩)
    return f&quot;The current temperature in {location} is 15&amp;deg;C with clear skies.&quot;

# Tool 호출 구현 (response API)
for item in response.output:
    if item.type == &quot;function_call&quot;:
        if item.name == &quot;get_weather&quot;:
            location = item.arguments
            # 실제 날씨 API 호출 (예시에서는 하드코딩)
            weather_info = get_weather(location)
            # 모델에 도구 호출 결과 제공
            follow_up_response = open_ai.responses.create(
                model=&quot;gpt-5-nano&quot;,
                input=f&quot;The weather information for {location} is: {weather_info}&quot;,
                reasoning=Reasoning(effort=&quot;minimal&quot;)
            )
            print(follow_up_response.output_text)&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;위와 같이 tool 의 json schema 를 정의하고, tool 호출시에 python function 을 호출해주고, LLM 에게 전달하는 과정을 거쳤는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAI agents 를 사용하면 아래와 같이 간소화 됩니다&lt;/p&gt;
&lt;pre id=&quot;code_1774083116143&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from agents import Agent, ModelSettings, Runner, function_tool
from dotenv import load_dotenv
from openai.types import Reasoning

load_dotenv()


@function_tool(docstring_style=&quot;google&quot;)
def get_weather(location: str) -&amp;gt; str:
    &quot;&quot;&quot;Get current temperature for a given location.

    Args:
        location: City and country e.g. Bogot&amp;aacute;, Colombia

    Returns:
        A concise weather sentence that includes Celsius temperature and a condition summary.
    &quot;&quot;&quot;
    return f&quot;The current temperature in {location} is 15&amp;deg;C with clear skies.&quot;


weather_search_agent = Agent(
    name=&quot;weather_search_agent&quot;,
    instructions=&quot;&quot;&quot;
    You are a weather assistant. For weather-related requests, 
    call the `get_weather` tool to get weather information and respond with a concise summary.
    &quot;&quot;&quot;,
    model=&quot;gpt-5.4-nano&quot;,
    model_settings=ModelSettings(reasoning=Reasoning(effort=&quot;none&quot;)),
    tools=[get_weather],
)&lt;/code&gt;&lt;/pre&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignLeft&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;001&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/001.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/001.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&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;OpenAI Agents 에 탑재되어 있는 &lt;b&gt;function_tool decorator&lt;/b&gt; 를 사용해주기만 하면, Agent 에게 정의할 tool 을 지정할 수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 &lt;b&gt;decorator&amp;nbsp;&lt;/b&gt;를 통해 json schema 를 만들게 되고, LLM 은 이를 인지하여 tool 을 언제 사용해야 겠다 를 판단할 수 있게 되죠&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 id=&quot;code_1774083473673&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def main() -&amp;gt; None:
    query = &quot;What is the weather in Seoul today?&quot;
    result = Runner.run_sync(weather_search_agent, input=query)
    print(json.dumps(get_weather.params_json_schema, indent=2, ensure_ascii=False))
    print(result.final_output)


if __name__ == &quot;__main__&quot;:
    main()&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;위와 같이 실행할 수 있는 코드와, 우리가 만든 tool 의 schema 를 확인할 수 있는 간단한 실행 코드를 작성해봅니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 되면 json schema 는 아래와 같이 나옵니다&lt;/p&gt;
&lt;pre id=&quot;code_1774083704548&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;properties&quot;: {
    &quot;location&quot;: {
      &quot;description&quot;: &quot;City and country e.g. Bogot&amp;aacute;, Colombia&quot;,
      &quot;title&quot;: &quot;Location&quot;,
      &quot;type&quot;: &quot;string&quot;
    }
  },
  &quot;required&quot;: [
    &quot;location&quot;
  ],
  &quot;title&quot;: &quot;get_weather_args&quot;,
  &quot;type&quot;: &quot;object&quot;,
  &quot;additionalProperties&quot;: false
}&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;Agents 라이브러리를 사용할 때와 거의 유사한 json 이 나오게 되죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 보일러플레이트 코드를 쉽게 생성하기 위해 Agent 라이브러리들을 사용하는 것에 많은 이점에 있습니다.(부가적인 기능들도 포함해서)&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;Agent 호출은 어떻게 되었는지 OpenAI Platform 에서 쉽게 확인해볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAI platform 은 AI Engineer 들을 위한 페이지이고, Observability, Evaluation, Fine tuning, Billing 등 다양한 기능들이 있어요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://platform.openai.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://platform.openai.com/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1774083807038&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;OpenAI Platform&quot; data-og-description=&quot;Explore developer resources, tutorials, API docs, and dynamic examples to get the most out of OpenAI's platform.&quot; data-og-host=&quot;platform.openai.com&quot; data-og-source-url=&quot;https://platform.openai.com/&quot; data-og-url=&quot;https://platform.openai.com&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cN7Lrg/dJMb9kmb2Hz/CoKkkvlutkPQXP3N0Lw1D0/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628,https://scrap.kakaocdn.net/dn/dsXdAk/dJMb9frEIOk/7JaK4KKkbXSgBkNdmJ0Az1/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628&quot;&gt;&lt;a href=&quot;https://platform.openai.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://platform.openai.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cN7Lrg/dJMb9kmb2Hz/CoKkkvlutkPQXP3N0Lw1D0/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628,https://scrap.kakaocdn.net/dn/dsXdAk/dJMb9frEIOk/7JaK4KKkbXSgBkNdmJ0Az1/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;OpenAI Platform&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Explore developer resources, tutorials, API docs, and dynamic examples to get the most out of OpenAI's platform.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;platform.openai.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1692&quot; data-origin-height=&quot;314&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bft06U/dJMcacCsAzG/FthU2Uf87K97fBLgLoMLSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bft06U/dJMcacCsAzG/FthU2Uf87K97fBLgLoMLSk/img.png&quot; data-alt=&quot;trace&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bft06U/dJMcacCsAzG/FthU2Uf87K97fBLgLoMLSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbft06U%2FdJMcacCsAzG%2FFthU2Uf87K97fBLgLoMLSk%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;1692&quot; height=&quot;314&quot; data-origin-width=&quot;1692&quot; data-origin-height=&quot;314&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;trace&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;agent 에 하나의 trace 로 묶인 것을 볼 수 있죠. 총 2번의 LLM API call 이 있었고, 1번의 tool 사용이 있었네요&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-origin-width=&quot;627&quot; data-origin-height=&quot;999&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DfSt3/dJMcaf0dNAT/9ingIDidHlNITM3xnKdE7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DfSt3/dJMcaf0dNAT/9ingIDidHlNITM3xnKdE7k/img.png&quot; data-alt=&quot;1st LLM call&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DfSt3/dJMcaf0dNAT/9ingIDidHlNITM3xnKdE7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDfSt3%2FdJMcaf0dNAT%2F9ingIDidHlNITM3xnKdE7k%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;627&quot; height=&quot;999&quot; data-origin-width=&quot;627&quot; data-origin-height=&quot;999&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1st LLM call&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 llm call 은 사용자의 input 과 instructions 를 통해, output 을 도출해냈는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;output 이 get_weather 호출과 parameter(location) 을 어떻게 지정해서 전달할 것인지를 나타냈습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;639&quot; data-origin-height=&quot;383&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuHRhK/dJMcafMJQs8/eTqqyYsM3BP56XK0vR6Di1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuHRhK/dJMcafMJQs8/eTqqyYsM3BP56XK0vR6Di1/img.png&quot; data-alt=&quot;1st tool call&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuHRhK/dJMcafMJQs8/eTqqyYsM3BP56XK0vR6Di1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuHRhK%2FdJMcafMJQs8%2FeTqqyYsM3BP56XK0vR6Di1%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;639&quot; height=&quot;383&quot; data-origin-width=&quot;639&quot; data-origin-height=&quot;383&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1st tool call&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 tool 호출인데요. local 에서 python code 를 실행하였고, 인자에 대한 결과값을 보여주고 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;1123&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz0Lku/dJMcajasJdM/8sdgDBtnVyG2GOkERe8dxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz0Lku/dJMcajasJdM/8sdgDBtnVyG2GOkERe8dxk/img.png&quot; data-alt=&quot;2nd LLM call&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz0Lku/dJMcajasJdM/8sdgDBtnVyG2GOkERe8dxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz0Lku%2FdJMcajasJdM%2F8sdgDBtnVyG2GOkERe8dxk%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;630&quot; height=&quot;1123&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;1123&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2nd LLM call&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 2번째 LLM API 호출인데요. tool 의 결과값을 LLM 에게 전달하여, 최종 output 을 이끌어내는 모습입니다&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;즉, 이렇게 해서 우리의 weather_search_agent 는 날씨를 가져오는 &lt;b&gt;tool&lt;/b&gt; 을 통해 최종적인 &lt;b&gt;목표&lt;/b&gt;(오늘 서울 날씨 뭐야?) 를 달성하게 되었네요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서는 간단한 예시로 설명드렸지만, 다양한 tool 을 제공하면 더 복잡하고 어려운 상황에서 많은 tool 호출이 이루어지고, &lt;span style=&quot;color: #ee2323;&quot;&gt;최종적인 목표를 달성하기 위한 과정(journey)&lt;/span&gt;를 거치게 되는 것입니다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리하며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러분들도 수작업으로 하고 있는 과정들을 개인화된 Agent 를 만들어 어떤 목표를 이루게 할 것인지 정의해보는 것도 좋겠네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent 에게 어떤 작업을 위임할 것이고, 어떤 Agent 라이브러리를 활용하여 목표를 달성하게 할 것인지 직접 구현해보고 실험해보면 좋겠습니다~!&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;다음 시간에는 조금 더 심화된 Agent pattern 을 통해서 어떤 상황에 어떤 pattern 를 사용하는게 적합할지에 대해 알아볼게요^^&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;a href=&quot;https://openai.github.io/openai-agents-python/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;OpenAI Agents SDK&lt;/a&gt;&lt;/blockquote&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;</description>
      <category>Developer/AI</category>
      <category>Agent</category>
      <category>ai</category>
      <category>llm</category>
      <category>openAI</category>
      <author>huisam</author>
      <guid isPermaLink="true">https://huisam.tistory.com/141</guid>
      <comments>https://huisam.tistory.com/entry/ai-agent#entry141comment</comments>
      <pubDate>Sat, 21 Mar 2026 18:12:37 +0900</pubDate>
    </item>
    <item>
      <title>LLM API 를 이용하여 Text 와 Tool 을 제공해보자(feat. OpenAi)</title>
      <link>https://huisam.tistory.com/entry/llm-api</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignLeft&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;001&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/001.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/001.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요~! 저번 시간에는 우리가 LLM 의 기본 동작과 돌아가는 원리에 대해 살펴보았는데요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 조금 더 실무 관점에서 어떤식으로 &lt;span style=&quot;color: #f89009;&quot;&gt;Frontier Model&lt;/span&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;span style=&quot;color: #f89009;&quot;&gt;Frontier Model&lt;/span&gt; 이라는 용어에 대해 이해가 어려우실텐데요. 우리가 흔히 사용하는 LLM 은 크게 2가지 분류로 분류됩니다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;Frontier Model&lt;/span&gt;: 많은 양의 데이터로 학습되었고, 파라미터 양이 엄청나게 많아 대규모 Model 을 일컫음(ex. GPT, Claude, Gemini ...)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Open source Model&lt;/span&gt;: 적은 양의 데이터로 학습되었고, 파라미터 양이 적어 무료로 오픈소스로 사용할 수 있는 Model 을 일컫음(ex. llama, gemma, gpt-oss... )&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 Chat Interface 에서 경험했던, 예를 들면 chatgpt.com 에서 경험했던 LLM model 들은 모두 &lt;span style=&quot;color: #f89009;&quot;&gt;Frontier Model&lt;/span&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;특정 LLM 을 학습시키고, 완성된 Model 을 제공한다는 것은 규모가 작은 기업 혹은 기술자가 부족한 기업에서는 자체적으로 LLM 을 운영할 수 없기에, LLM 을 전문적으로 다루는 기업에서 제공한 LLM 들을 사용하여 AI application 을 누구나 쉽게 만들 수 있게 되는 것이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신에 서버 비용이라는 것이 공짜는 없기에, &lt;u&gt;Token 단위의 사용량을 토대로 요금을 납부하는 대가&lt;/u&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;이번 게시글에서는 OpenAI 라는 AI platform 을 사용하여 실습을 진행하게 되므로, 이점 감안하여 실습을 진행해보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAI 에서는 5달러 라는 기본 요금만 납부하기만 하면 간단한 실습예제에서는 큰 Token 을 소비하지 않으니 크게 걱정 않으셔도 됩니다.&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;Tech stack 은 아래와 같이 진행합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;language: &lt;span style=&quot;color: #ee2323;&quot;&gt;python 3.14&lt;/span&gt; sdk 기반&lt;/li&gt;
&lt;li&gt;project manager: &lt;span style=&quot;color: #ee2323;&quot;&gt;uv&lt;/span&gt; 로 python project management&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고로 python application 의 최근 추세는 모두 uv 로 사용하는 추세로 넘어가고 있습니다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;uv 는 rust 로 작성되어 있어서 속도가 빠르고, virtual env 에 대한 관리 와 dependency 로드를 병렬로 하고 있어 매우 편리하고 빠르다고 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.astral.sh/uv/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.astral.sh/uv/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1771138073782&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;uv&quot; data-og-description=&quot;An extremely fast Python package and project manager, written in Rust. Installing Trio's dependencies with a warm cache. A single tool to replace pip, pip-tools, pipx, poetry, pyenv, twine, virtualenv, and more. 10-100x faster than pip. Provides comprehens&quot; data-og-host=&quot;docs.astral.sh&quot; data-og-source-url=&quot;https://docs.astral.sh/uv/&quot; data-og-url=&quot;https://docs.astral.sh/uv/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.astral.sh/uv/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.astral.sh/uv/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;uv&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;An extremely fast Python package and project manager, written in Rust. Installing Trio's dependencies with a warm cache. A single tool to replace pip, pip-tools, pipx, poetry, pyenv, twine, virtualenv, and more. 10-100x faster than pip. Provides comprehens&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.astral.sh&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Text generation&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/56ojX/dJMcaiPKKfv/kLef7pg0ydVdYJP4i3uRk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/56ojX/dJMcaiPKKfv/kLef7pg0ydVdYJP4i3uRk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/56ojX/dJMcaiPKKfv/kLef7pg0ydVdYJP4i3uRk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F56ojX%2FdJMcaiPKKfv%2FkLef7pg0ydVdYJP4i3uRk0%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;674&quot; height=&quot;1024&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&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;가장 쉬운 것부터 시작해보려고 합니다. 특정 input 을 주었을 때 output 을 어떻게 만들 수 있을까요?&lt;/p&gt;
&lt;pre id=&quot;code_1771137791592&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uv add opneai
uv add python-dotenv&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;먼저 프로젝트에 openai 라이브러리 종속성을 가져옵니다.&lt;/p&gt;
&lt;pre id=&quot;code_1771137774132&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import os

from dotenv import load_dotenv
from openai import OpenAI
from openai.types import Reasoning

load_dotenv()
open_ai = OpenAI(api_key=os.getenv(&quot;OPENAI_API_KEY&quot;))

# chat completion api
chat_response = open_ai.chat.completions.create(
    model=&quot;gpt-5-nano&quot;,
    messages=[
        {&quot;role&quot;: &quot;system&quot;, &quot;content&quot;: &quot;You are a helpful assistant.&quot;},
        {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;What is the capital of France?&quot;},
    ],
    reasoning_effort=&quot;minimal&quot;
)
print(chat_response.choices[0].message.content)

# response api
response = open_ai.responses.create(
    model=&quot;gpt-5-nano&quot;,
    instructions=&quot;You are a helpful assistant.&quot;,
    input=&quot;What is the capital of France?&quot;,
    reasoning=Reasoning(effort=&quot;minimal&quot;)
)
print(response.output_text)&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;그 후에 위와 같은 코드를 간단하게 작성해보았는데요. model 은 가성비 model 인 gpt-5-nano 를 기반으로 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 위 코드를 가동하게 되면?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;147&quot; data-origin-height=&quot;80&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Gyoyp/dJMcagRZ4kW/js0n2BMltFaMxKZ61KdXk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Gyoyp/dJMcagRZ4kW/js0n2BMltFaMxKZ61KdXk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Gyoyp/dJMcagRZ4kW/js0n2BMltFaMxKZ61KdXk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGyoyp%2FdJMcagRZ4kW%2Fjs0n2BMltFaMxKZ61KdXk0%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;147&quot; height=&quot;80&quot; data-origin-width=&quot;147&quot; data-origin-height=&quot;80&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;Paris 라는 결과물을 주는군요!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 LLM 을 이용하여 프랑스의 수도를 알려주는 기능을 구현했습니다 ^-^&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignLeft&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;009&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/009.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/009.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 조금 더 세밀하게 들여다봐야겠죠? 먼저 크게 2가지 유형으로 작성하였는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 경우 &lt;b&gt;chat completion API&lt;/b&gt; 를 사용하는 것으로 생각하시면 됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Chat Completion API 는 OpenAI 가 처음으로 제공했던 API 양식이며, 다른 Frontier model provider 들도 OpenAI 에서 제공한 양식으로 맞추려는 움직임들도 있어 누구나 쉽게 Model 을 교체하여 application 을 운영하게 되었습니다.&lt;br /&gt;그러자 OpenAI 는 오로지 자기 모델에 종속적인 API 를 제공하는 것으로 스탠스를 바꾸었죠. 이는 다른 Model 사용하지 말고, OpenAI model 사용에 종속적인 application 이 되라는 것을 의미합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 데이터를 보낼 때, role 과 content 를 지정한 것을 볼 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;role 은 크게 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;span style=&quot;color: #ee2323;&quot;&gt;system&lt;/span&gt;: 시스템에서 설정한 text&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;user&lt;/span&gt;: 사용자가 질의한 text&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;assistant&lt;/span&gt;: LLM 이 생성한 응답 text&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjv2pH/dJMcachLbgO/z9xki967oSY6Fkz1BXRvR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjv2pH/dJMcachLbgO/z9xki967oSY6Fkz1BXRvR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjv2pH/dJMcachLbgO/z9xki967oSY6Fkz1BXRvR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbjv2pH%2FdJMcachLbgO%2Fz9xki967oSY6Fkz1BXRvR0%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;619&quot; height=&quot;1024&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 3가지 데이터를 모두 고려하여, LLM 의 최종 응답이 생성된다고 보면 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM 은 그 자체만으로 어떤 상태를 갖고 있지 않기 때문에, 대화형 interface 에서는 대화의 모든 이력을 데이터화 해서 전송하고, 최종적인 결과물을 얻게 되는 것이죠.&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;여기서 AHA 모먼트를 느끼면 좋겠네요 ^-^&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 우리가 사용하는 모든 chat interface 를 통한 질의는 모든 대화 이력을 데이터화 하는 것이라는 것을요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대화가 길어지면 길어질수록 LLM 이 생성하는 응답의 퀄리티가 이상해지는 현상을 볼 수도 있는데, 이는 너무 많은 Context 가 담겨있어 어떤 Text 를 생성해야될지 갈팡질팡 하는 단계라고 볼 수 있습니다.&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;많은 Context 가 담긴다는 것은 이전의 원리에서도 볼 수 있듯이, 수없이 많은 Vector 들이 나열되기에 서로 상충되는 Vector 데이터들이 생기거나 일관성이 없는 데이터가 생긴다면 LLM 의 output 퀄리티도 자연스럽게 떨어지게 되는것이죠&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;그래서 Frontier model 에서는 model 마다 허용가능한 Context window 를 안내하고 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;449&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/343cb/dJMcabwmGlh/Ac5dxNwO2jT1udL2ghx1RK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/343cb/dJMcabwmGlh/Ac5dxNwO2jT1udL2ghx1RK/img.png&quot; data-alt=&quot;gpt-5 nano&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/343cb/dJMcabwmGlh/Ac5dxNwO2jT1udL2ghx1RK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F343cb%2FdJMcabwmGlh%2FAc5dxNwO2jT1udL2ghx1RK%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;1270&quot; height=&quot;449&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;449&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;gpt-5 nano&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저희가 사용한 gpt-5-nano 모델은 최대 40만개의 Context window 라고 하는군요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 질의한 내용에 대해 결과물이 만족스럽지 못한다면, 지금 나의 대화는 얼만큼의 Context window 로 넘어갈지, 대화 이력동안의 일관성은 있는지 체크해보면 좋겠군요.&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;이러한 Context window 의 중요성은 추후에 AI agent application 을 제작할 때, Context Engineering 이라는 주제로도 이어지게 된답니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Tool&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM 이 Agentic AI 로 갈 수 있었던 배경에는 이 Tool 이 있습니다. 먼저 예시와 함께 설명해볼게요&lt;/p&gt;
&lt;pre id=&quot;code_1771140918724&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import os

from dotenv import load_dotenv
from openai import OpenAI
from openai.types import Reasoning

load_dotenv()
open_ai = OpenAI(api_key=os.getenv(&quot;OPENAI_API_KEY&quot;))

tools = [
    {
        &quot;type&quot;: &quot;function&quot;,
        &quot;name&quot;: &quot;get_weather&quot;,
        &quot;description&quot;: &quot;Get current temperature for a given location.&quot;,
        &quot;parameters&quot;: {
            &quot;type&quot;: &quot;object&quot;,
            &quot;properties&quot;: {
                &quot;location&quot;: {
                    &quot;type&quot;: &quot;string&quot;,
                    &quot;description&quot;: &quot;City and country e.g. Bogot&amp;aacute;, Colombia&quot;,
                }
            },
            &quot;required&quot;: [&quot;location&quot;],
            &quot;additionalProperties&quot;: False,
        },
        &quot;strict&quot;: True,
    },
]

response = open_ai.responses.create(
    model=&quot;gpt-5-nano&quot;,
    input=&quot;What is the weather like in Paris today?&quot;,
    tools=tools,
    reasoning=Reasoning(effort=&quot;minimal&quot;)
)
print(response.output[1].to_json())

def get_weather(location):
    # 실제 날씨 API 호출 (예시에서는 하드코딩)
    return f&quot;The current temperature in {location} is 15&amp;deg;C with clear skies.&quot;

# Tool 호출 구현 (response API)
for item in response.output:
    if item.type == &quot;function_call&quot;:
        if item.name == &quot;get_weather&quot;:
            location = item.arguments
            # 실제 날씨 API 호출 (예시에서는 하드코딩)
            weather_info = get_weather(location)
            # 모델에 도구 호출 결과 제공
            follow_up_response = open_ai.responses.create(
                model=&quot;gpt-5-nano&quot;,
                input=f&quot;The weather information for {location} is: {weather_info}&quot;,
                reasoning=Reasoning(effort=&quot;minimal&quot;)
            )
            print(follow_up_response.output_text)&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;지금은 직접적으로 Tool 을 호출하고 있기에 코드가 많이 어려운데요 ㅎㅎ..&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;나중에는 AI agent 라이브러리를 통해 이러한 보일러플레이트 같은 코드를 직접적으로 신경쓰지는 않을 것입니다.&lt;br /&gt;다음 포스팅을 기대해주세요~!&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쨌든 위 코드를 조금 쉽게 큰 그림으로 설명드리면, 이 코드는 LLM API call 을 총 2번하게 되는 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 호출에 대한 결과 print 는 아래와 같이 출력되고&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSgkWd/dJMcadOrb0h/6pUqJG4kIxWyN70hmsTt0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSgkWd/dJMcadOrb0h/6pUqJG4kIxWyN70hmsTt0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSgkWd/dJMcadOrb0h/6pUqJG4kIxWyN70hmsTt0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSgkWd%2FdJMcadOrb0h%2F6pUqJG4kIxWyN70hmsTt0K%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;548&quot; height=&quot;212&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;212&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째 호출에 대한 결과 print 는 아래와 같이 출력됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;633&quot; data-origin-height=&quot;226&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkqCmB/dJMcacovQBL/uDyxeg8s8ET7wkU2jaETq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkqCmB/dJMcacovQBL/uDyxeg8s8ET7wkU2jaETq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkqCmB/dJMcacovQBL/uDyxeg8s8ET7wkU2jaETq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkqCmB%2FdJMcacovQBL%2FuDyxeg8s8ET7wkU2jaETq1%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;633&quot; height=&quot;226&quot; data-origin-width=&quot;633&quot; data-origin-height=&quot;226&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;LLM 에게 get_weather(날씨 조회) 라는 tool 을 제공하였더니, LLM 이 tool 을 이용한 결과값을 요청하는 것이 첫번째 print 문이라고 보면 됩니다. 어떤 함수와 어떤 인자로 전달할 것인지도 결정해서 json 형태로 알려주었죠.&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;두번째 print 는 사용자가 요청한 질의와 함수 결과값을 최종적으로 종합하여 최종 응답을 생성한 것이라고 이해하면 됩니다.&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDVnT4/dJMcajnAIBq/vUQIzb79WqiUjNKtofVzT0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDVnT4/dJMcajnAIBq/vUQIzb79WqiUjNKtofVzT0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDVnT4/dJMcajnAIBq/vUQIzb79WqiUjNKtofVzT0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDVnT4%2FdJMcajnAIBq%2FvUQIzb79WqiUjNKtofVzT0%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;1900&quot; height=&quot;1000&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;1000&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;Tool 은 쉽게 이야기해서, LLM 사용할 수 있는 도구를 의미합니다. 이는 개발자가 정의한 함수를 의미하며, &lt;span style=&quot;color: #ee2323;&quot;&gt;로컬에서 돌린 함수 결과를 LLM 에게 전달하는 것&lt;/span&gt;이죠&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도구는 여러가지 형태로 사용될 수 있어요.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RAG retrieval: RAG 를 통한 질의&lt;/li&gt;
&lt;li&gt;API call: API 호출&lt;/li&gt;
&lt;li&gt;DB query: DB 질의&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Tool 이 있기에 LLM 은 정말 무엇이든지 할 수 있게 되었습니다. 단순한 Text 예측기에서 LLM 이 직접 행동할 수 있는 기반을 갖게 되었죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기에 모든 Model 의 Tool 표준을 정해주는 MCP(Model Context Protocol) 이 각광받게 되었습니다.&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;여기서도 AHA 모먼트를 느낄 수 있죠 ^-^&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM 에게 어떤 tool 을 제공할 것인지에 따라 설계하고자하는 AI application 이 다양하게 개발될 수 있고, 어떤 일을 하게 될지 경계를 명확히 할 수 있다는 것을요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Tool 을 정의하고, Tool 에 대한 설명을 어떻게 작성하느냐에 따라 결과물은 정말 완전히 달라질 수 있습니다.&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;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignLeft&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;005&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/005.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/005.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 크게 2가지 API 를 실전 예제와 함께 해보면서 정리해보았는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Text generation 을 통해 Context window 의 중요성과 실제 데이터구조를 알아보았고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tool 을 통해 Tool 의 동작원리와 중요성에 대해 알아보았네요.&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;이제 AI Engineer 가 되기 위한 기초가 되었으니, 다음 게시글에서는 조금 심화된 과정의 Agent 로 찾아오도록 할게요!&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고문서&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;a href=&quot;https://developers.openai.com/api/docs/quickstart?language=python&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Developer QuickStart&lt;/a&gt;&lt;/blockquote&gt;</description>
      <category>Developer/AI</category>
      <author>huisam</author>
      <guid isPermaLink="true">https://huisam.tistory.com/140</guid>
      <comments>https://huisam.tistory.com/entry/llm-api#entry140comment</comments>
      <pubDate>Sun, 15 Feb 2026 16:58:42 +0900</pubDate>
    </item>
    <item>
      <title>LLM 의 구조와 원리에 대해 쉽게 알아보자 (feat. llama)</title>
      <link>https://huisam.tistory.com/entry/llm-architecture</link>
      <description>&lt;h1&gt;들어가며&lt;/h1&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignLeft&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;001&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/001.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/001.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&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;LLM(Large Language Model)의&lt;/b&gt; 구조와 동작 방식에 대해 이해하기 쉽게 한번 정리해보려 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깊게 들어가면 들어갈수록 수학적인 공식과, 논문 레벨 단위로 설명을 해야 되는데, 그렇게 deep 한 level 은 다루진 않고&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;Architecture&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM 은 기본적으로 &lt;b&gt;언어(Language)를 기반으로 다음에 나올 Token 을 예측하여 확률을 할당&lt;/b&gt;하는 모델입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수십억개의 파라미터를 가진 &lt;span style=&quot;color: #ee2323;&quot;&gt;신경망(Neural Network)&lt;/span&gt; 구조를 기반으로 하고 있습니다. 예전에는 통계(ex. Linear Regression)를 기반으로 한 모델들도 있었지만, 요즘은 &lt;span style=&quot;color: #ee2323;&quot;&gt;신경망(Neural Network)&lt;/span&gt; 을 기반으로 하는 것이 보편적으로 자리를 잡게 되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eRpD4n/dJMcagc9MsW/9iCPEqFVUM1iJraziZszn1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eRpD4n/dJMcagc9MsW/9iCPEqFVUM1iJraziZszn1/img.jpg&quot; data-alt=&quot;Neural Network 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eRpD4n/dJMcagc9MsW/9iCPEqFVUM1iJraziZszn1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeRpD4n%2FdJMcagc9MsW%2F9iCPEqFVUM1iJraziZszn1%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;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Neural Network 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신경망(Neural Network)은 각각의 layer들이 있고, 이 Layer는 주어진 &lt;span style=&quot;color: #f89009;&quot;&gt;입력(input)과&lt;/span&gt; &lt;span style=&quot;color: #f89009;&quot;&gt;가중치(weight)를&lt;/span&gt; 기반으로 &lt;span style=&quot;color: #f89009;&quot;&gt;출력(output)을&lt;/span&gt; 이끌어내게 되는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신경망(Neural Network)에서 주고받는 데이터는 숫자에 불과하며, 우리가 LLM에게 입력한 언어(Language)는 사실 내부 구조적으로 숫자로 변경되어 전달하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론, 이 과정속에서 이루어지는 다양한 수식과 원리를 통해 지나가게 되지만, 신경망(Neural Network)의 구조는 너무나 복잡하므로 이번 게시글에서는 이렇게 간략하게만 짚고 넘어가려고 합니다.&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignLeft&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;008&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/008.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/008.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 본격적으로 LLM 의 상세 구조와 동작원리에 대해서 알고자 한다면, 기본적으로 어떤 아키텍처를 가지고 있는지 알아보아야 하는데요.&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-origin-width=&quot;1198&quot; data-origin-height=&quot;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3sQEH/dJMcaa44CRI/x6MJB3jP6BStrC04YgMnWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3sQEH/dJMcaa44CRI/x6MJB3jP6BStrC04YgMnWK/img.png&quot; data-alt=&quot;Overview&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3sQEH/dJMcaa44CRI/x6MJB3jP6BStrC04YgMnWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3sQEH%2FdJMcaa44CRI%2Fx6MJB3jP6BStrC04YgMnWK%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;1198&quot; height=&quot;542&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;542&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Overview&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선은 우리는 Llama-3.2-1B 모델을 기반으로 한 아키텍쳐를 살펴보려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 모델은 opensource model 이며, hugging face에서 모델을 다운로드할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://huggingface.co/meta-llama/Llama-3.2-1B&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://huggingface.co/meta-llama/Llama-3.2-1B&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1768118630668&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;meta-llama/Llama-3.2-1B &amp;middot; Hugging Face&quot; data-og-description=&quot;We&amp;rsquo;re on a journey to advance and democratize artificial intelligence through open source and open science.&quot; data-og-host=&quot;huggingface.co&quot; data-og-source-url=&quot;https://huggingface.co/meta-llama/Llama-3.2-1B&quot; data-og-url=&quot;https://huggingface.co/meta-llama/Llama-3.2-1B&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bbmihj/hyZRpecdaD/VMcNhkNBfWll2QUgDBbiAk/img.png?width=1200&amp;amp;height=648&amp;amp;face=0_0_1200_648,https://scrap.kakaocdn.net/dn/nFgDc/hyZReX3XfG/I6w8akGL7ixgSdKFsGzqy1/img.png?width=1200&amp;amp;height=648&amp;amp;face=0_0_1200_648&quot;&gt;&lt;a href=&quot;https://huggingface.co/meta-llama/Llama-3.2-1B&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://huggingface.co/meta-llama/Llama-3.2-1B&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bbmihj/hyZRpecdaD/VMcNhkNBfWll2QUgDBbiAk/img.png?width=1200&amp;amp;height=648&amp;amp;face=0_0_1200_648,https://scrap.kakaocdn.net/dn/nFgDc/hyZReX3XfG/I6w8akGL7ixgSdKFsGzqy1/img.png?width=1200&amp;amp;height=648&amp;amp;face=0_0_1200_648');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;meta-llama/Llama-3.2-1B &amp;middot; Hugging Face&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;We&amp;rsquo;re on a journey to advance and democratize artificial intelligence through open source and open science.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;huggingface.co&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1768119737071&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LlamaForCausalLM(
  (model): LlamaModel(
    (embed_tokens): Embedding(128256, 2048)
    (layers): ModuleList(
      (0-15): 16 x LlamaDecoderLayer(
        (self_attn): LlamaAttention(
          (q_proj): Linear(in_features=2048, out_features=2048, bias=False)
          (k_proj): Linear(in_features=2048, out_features=512, bias=False)
          (v_proj): Linear(in_features=2048, out_features=512, bias=False)
          (o_proj): Linear(in_features=2048, out_features=2048, bias=False)
        )
        (mlp): LlamaMLP(
          (gate_proj): Linear(in_features=2048, out_features=8192, bias=False)
          (up_proj): Linear(in_features=2048, out_features=8192, bias=False)
          (down_proj): Linear(in_features=8192, out_features=2048, bias=False)
          (act_fn): SiLUActivation()
        )
        (input_layernorm): LlamaRMSNorm((2048,), eps=1e-05)
        (post_attention_layernorm): LlamaRMSNorm((2048,), eps=1e-05)
      )
    )
    (norm): LlamaRMSNorm((2048,), eps=1e-05)
    (rotary_emb): LlamaRotaryEmbedding()
  )
  (lm_head): Linear(in_features=2048, out_features=128256, bias=False)&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;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Embedding&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 첫줄에는 embed_tokens 이라는 것이 있군요. 처음에 소개할 때 &lt;span style=&quot;color: #ee2323;&quot;&gt;Token&lt;/span&gt; 을 예측하는 모델이라고 하였는데요.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Token&lt;/span&gt; 은 무엇이고, &lt;span style=&quot;color: #ee2323;&quot;&gt;embed&lt;/span&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;span style=&quot;color: #ee2323;&quot;&gt;Token&lt;/span&gt; 은 &lt;u&gt;주어진 Text 를 Model 이 처리할 수 있는 데이터&lt;/u&gt;로 변환한 단위를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 Model 은 Token 단위로 unqiue 한 숫자ID 를 가지게 되며, 내부적으로는 숫자ID 를 기반으로 데이터만 전달하게 되는 것이죠&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;Token 은 너무나 &lt;span style=&quot;color: #f89009;&quot;&gt;작은 단위(1 character)&lt;/span&gt; 로 하게 되면, 모델이 처리해야 되는 데이터가 기하급수적으로 증가하고 &lt;span style=&quot;color: #ee2323;&quot;&gt;의미적인 관계&lt;/span&gt;를 파악하기가 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너무나 &lt;span style=&quot;color: #f89009;&quot;&gt;큰 단위(1 word)&lt;/span&gt; 로 하게 되면, 엄청나게 많은 단어집을 모델이 들고 있어야하고, &lt;span style=&quot;color: #ee2323;&quot;&gt;의미적인 관계&lt;/span&gt;가 같은데도 서로 다른 id 를 가져야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;예를 들면, word 와 words 는 단어를 의미하고, 둘간의 단어의 차이는 1개의 word 이냐 복수개의 word 를 지칭하고 있습니다.&lt;br /&gt;'w' , 'o' , 'r', 'd' 는 의미적인 관계를 파악하기에 어렵고, 'word' 'words' 는 서로 같은 의미를 가지고 있습니다&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 보편적으로는 이에 대한 절충안으로 &lt;span style=&quot;color: #f89009;&quot;&gt;유사도를 고려한 하위 단어(sub word)&lt;/span&gt; 로 token 의 단위를 결정하게 됩니다&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 id=&quot;code_1768120313865&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tokenizer.tokenize(&quot;Hello. I am studying about LLMs.&quot;)
# ['Hello', '.', 'ĠI', 'Ġam', 'Ġstudying', 'Ġabout', 'ĠL', 'LM', 's', '.']&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;하나의 문장을 Model 이 정의한 token 단위로 변환되는 것을 볼 수 있죠. 신기한 것은 LLMs 과 같은 축약어를 'L' 'LM' 's' 로 변환했다는 것에 주목해주세요&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;span style=&quot;color: #ee2323;&quot;&gt;Embed&lt;/span&gt; 는 뭘까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Embedding 은 Token 에서 ID 로 변환된 데이터를 &lt;b&gt;벡터(Nx1 행렬&lt;/b&gt;) 표현한 것을 의미합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/peUJw/dJMcaaRxN2b/MhXbOqhVZvKSDbBugkgYMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/peUJw/dJMcaaRxN2b/MhXbOqhVZvKSDbBugkgYMK/img.png&quot; data-alt=&quot;Embedding&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/peUJw/dJMcaaRxN2b/MhXbOqhVZvKSDbBugkgYMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpeUJw%2FdJMcaaRxN2b%2FMhXbOqhVZvKSDbBugkgYMK%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;1200&quot; height=&quot;572&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Embedding&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 앞서서 &lt;span style=&quot;color: #ee2323;&quot;&gt;의미적인 관계&lt;/span&gt;를 파악하는 것에 대해 말씀드렸는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 어떤 Token 을 예측하기 위해서는, &lt;span style=&quot;color: #ee2323;&quot;&gt;의미적인 관계&lt;/span&gt;를 파악하는 것이 매우 중요하기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'나는 XXX 다' 와 '똑똑한 나는 XXX 다' 에서 XXX 를 예측하는데에는 의미적인 관계를 알면 올바르게 XXX 를 예측할 확률이 높아진다고 보면 되요&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;그래서 Embedding 은 어떤 하나의 문장을 고차원 행렬로 변환시키는 과정을 갖게 되는 것이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Token 은 &lt;b&gt;vector(Nx1 행렬)&lt;/b&gt; 하나의 문장은 &lt;b&gt;여러개의 Vector 를 가진 NxN 행렬&lt;/b&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;고차원의 행렬을 만들게 되면, cosine 함수와 같은 것을 이용하여, 유사도를 계산할 수 있게 됩니다. (같은 방향이면 같은 유사도)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;645&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btfqyx/dJMcaiPwPeV/3jOcqBH1qjZBZp4IPbz1fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btfqyx/dJMcaiPwPeV/3jOcqBH1qjZBZp4IPbz1fk/img.png&quot; data-alt=&quot;Cosine 유사도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btfqyx/dJMcaiPwPeV/3jOcqBH1qjZBZp4IPbz1fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbtfqyx%2FdJMcaiPwPeV%2F3jOcqBH1qjZBZp4IPbz1fk%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;645&quot; height=&quot;120&quot; data-origin-width=&quot;645&quot; data-origin-height=&quot;120&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Cosine 유사도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;길고긴 embed_token 을 지나 이제 본격적으로 다음 Layer 로 넘어가도록 하겠습니다 ㅎㅎ&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Self Attention Layer&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 가장 중요한 Attention Layer 입니다. 여기서부터는 사실상 &lt;span style=&quot;color: #ee2323;&quot;&gt;Transformer&lt;/span&gt; 아키텍쳐라고 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;617&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eowhWo/dJMcajnl9A2/RYIMoU3eRtBANk7wfXCg3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eowhWo/dJMcajnl9A2/RYIMoU3eRtBANk7wfXCg3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eowhWo/dJMcajnl9A2/RYIMoU3eRtBANk7wfXCg3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeowhWo%2FdJMcajnl9A2%2FRYIMoU3eRtBANk7wfXCg3k%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;512&quot; height=&quot;617&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;617&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서도 보시다 싶이 크게 2가지 단계를 볼 수 있는데요.&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;Multi-Head Attention&amp;nbsp;&lt;/b&gt;: Token 들의 문맥을 파악하고, Token 의미를 업데이트하는 과정(Apple 은 사과일 수도 혹은 기업일 수도)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Feed forward : &lt;/b&gt;Token 그 자체의 정보를 해석하고 지식을 꺼내는 곳(Apple 기업은 아이폰을 만든다)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;llama 에서 Multi head attention 은&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1768122986004&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        (self_attn): LlamaAttention(
          (q_proj): Linear(in_features=2048, out_features=2048, bias=False)
          (k_proj): Linear(in_features=2048, out_features=512, bias=False)
          (v_proj): Linear(in_features=2048, out_features=512, bias=False)
          (o_proj): Linear(in_features=2048, out_features=2048, bias=False)
        )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 구성되어 있군요! q,k,v,o 는 아래와 같은 역할을 가지고 있습니다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;q_proj: Query(질문) 에 대한 Token 들의 행렬(2048 x 2048)&lt;/li&gt;
&lt;li&gt;k_proj: Key(색인) 에 대한 Tokene 들의 행렬(2048 x 512)&lt;/li&gt;
&lt;li&gt;v_proj: Value(값) 에 대한 Token 들의 행렬(2048 x 512)&lt;/li&gt;
&lt;li&gt;o_proj: Output(결과) 에 대한 Token 들의 행렬(2048 x 2048)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 Q, K, V 라는 것을 알 수 있습니다. 각 Q,K,V 를 아래와 같은 수식으로 변환하게 되는데요&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;118&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cob9V/dJMcadtUIbG/HBmwvRDxG6r69iGPwqL981/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cob9V/dJMcadtUIbG/HBmwvRDxG6r69iGPwqL981/img.png&quot; data-alt=&quot;Attention&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cob9V/dJMcadtUIbG/HBmwvRDxG6r69iGPwqL981/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCob9V%2FdJMcadtUIbG%2FHBmwvRDxG6r69iGPwqL981%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;452&quot; height=&quot;118&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;118&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Attention&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Query 와 Key 를 곱해서 연관성을 확인하고, 그 후 숫자를 안정화(분모) 한뒤에 softmax 함수를 통해 확률분포를 갖게 되며 각 확률별로 문장내에 있는 다른 Token의 Value 를 곱해서 모두 더하게 됩니다.&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;Token 이 개별로 갖는 의미를 파악하기 위함&lt;/b&gt;이라고 볼 수 있죠&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;MLP layer&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MLP(Multi Level Perceptron) 은 &lt;b&gt;Perceptron&lt;/b&gt; 을 여러개의 Level 로 구성해둔 것을 의미하는데요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Perceptron&lt;/b&gt; 은 인공신경망(Neural Network)를 구축하는 가장 기본적인 단위라고 이해하시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;327&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eCu7h4/dJMcajgD3sE/ZJSlJn9NsVIoNjuX3fRRWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eCu7h4/dJMcajgD3sE/ZJSlJn9NsVIoNjuX3fRRWk/img.png&quot; data-alt=&quot;Perceptron&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eCu7h4/dJMcajgD3sE/ZJSlJn9NsVIoNjuX3fRRWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeCu7h4%2FdJMcajgD3sE%2FZJSlJn9NsVIoNjuX3fRRWk%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;658&quot; height=&quot;327&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;327&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Perceptron&lt;/figcaption&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;그러면 실제로 llama 에서는 어떤식으로 설계하게 된 걸까요?&lt;/p&gt;
&lt;pre id=&quot;code_1768126056846&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;          (gate_proj): Linear(in_features=2048, out_features=8192, bias=False)
          (up_proj): Linear(in_features=2048, out_features=8192, bias=False)
          (down_proj): Linear(in_features=8192, out_features=2048, bias=False)
          (act_fn): SiLUActivation()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;구성&lt;/td&gt;
&lt;td style=&quot;width: 88.2558%;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;gate_proj&lt;/td&gt;
&lt;td style=&quot;width: 88.2558%;&quot;&gt;정보의 중요도를 판단하기 위해 입력된 vector 의 차원을 확장합니다. up_proj 와 동일한 차원으로 확장합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;up_proj&lt;/td&gt;
&lt;td style=&quot;width: 88.2558%;&quot;&gt;입력된 vector 의 차원을 확장합니다. llama 는 2048 차원에서 8192 인 4배로 확장합니다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;down_proj&lt;/td&gt;
&lt;td style=&quot;width: 88.2558%;&quot;&gt;넓어진 vector 를 다시 축소합니다. 이때 중요정보만 남기고 불필요한 것은 버립니다. (gate_proj, up_proj 값을 기반)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;act_fn&lt;/td&gt;
&lt;td style=&quot;width: 88.2558%;&quot;&gt;활성화 함수. 복잡하고 추상적인 개념을 이해하기 위함. llama 에서는 SiLU 함수를 따름&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;llama 3.2 기준으로는 projection layer 에 gateway projection 을 추가로 둠으로써 더 정교하게 데이터를 제공하기 위한 기반을 제공하고 있습니다. &lt;b&gt;Projection layer&lt;/b&gt; 라는 것은 단순 행렬 곱연산을 위한 하나의 단계라고 이해하시면 됩니다. Token 들이 가지고 있는 벡터들을 N 차원의 공간으로 투영시켜, 벡터가 가지는 의미적인 관계를 추론하다고 보시면 되겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8YKTi/dJMcacaN5UI/NQQx5ZcUJ5qLdA5oAK1r91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8YKTi/dJMcacaN5UI/NQQx5ZcUJ5qLdA5oAK1r91/img.png&quot; data-alt=&quot;activation functions&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8YKTi/dJMcacaN5UI/NQQx5ZcUJ5qLdA5oAK1r91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8YKTi%2FdJMcacaN5UI%2FNQQx5ZcUJ5qLdA5oAK1r91%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;1400&quot; height=&quot;582&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;582&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;activation functions&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;활성화 함수(Activation functions)&lt;/b&gt;라는 것에 대해 조금 더 어렵게 느껴지긴 합니다만. 쉽게 설명하면 선형적인 그래프를 곡선의 그래프로 바꾸기 위함입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더~ 쉽게 이야기하면 터무니 없는 결과값은 취급하지 않게 하기 위해 계산이 아닌 drop 하는 것을 의미합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;위 그림으로 보이듯이&amp;nbsp;선형(Linear)&amp;nbsp;그래프는 y 값의 제한이 없기 때문에, 무한으로 값이 확장될 수 있는 구조인데,&lt;br /&gt;y 값 자체를 한계를 지어버리면 다른 그래프들처럼 완만한곡선 혹은 기울기가 매우 작은 형태를 띄게 되는 것을 볼 수 있습니다.&lt;/blockquote&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;span style=&quot;color: #ee2323;&quot;&gt;Selft attention layer&lt;/span&gt; 와 &lt;span style=&quot;color: #ee2323;&quot;&gt;MLP layer&lt;/span&gt; 를 통과하게 되면, 다음 Token 을 예측하이 모두 끝난 것이죠&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Token 자체의 문맥을 이미 파악&lt;/b&gt;했고, &lt;b&gt;Token 의 개념까지 확장&lt;/b&gt;해내었으니, 어떤 Token 이 다음에 와야할지 vector 정보로 갖고 있게 된답니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Normalization&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 위에 원리들을 보다보면, 수없이 확장되면 경우의 수가 너무 많아지지 않을까? 하는 생각도 드는데요.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 과한 확장을 방지하기 위해 &lt;span style=&quot;color: #ee2323;&quot;&gt;Normalization&lt;/span&gt; 이 존재한답니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768127345435&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    (norm): LlamaRMSNorm((2048,), eps=1e-05)&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;Normalization 은 일종의 안정화 작업이라고 보면 됩니다. llama 구조에서는 아래와 같은 normalizatioin 이 존재하는데요&lt;/p&gt;
&lt;pre id=&quot;code_1768127582564&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        (input_layernorm): LlamaRMSNorm((2048,), eps=1e-05)
        (post_attention_layernorm): LlamaRMSNorm((2048,), eps=1e-05)&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;attention 에 들어가기 전과 후에 normalization 을 넣어주면 학습시에 더 안정적인 결과를 얻어낼 수 있겠죠!&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Lm head&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자, 이제 최종적인 최종적인 vector 정보를 알게 되었으니, 이 vector 를 다시 Token ID 숫자를 기반으로 Token 언어로 바꾸게 되는 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768127803208&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  (lm_head): Linear(in_features=2048, out_features=128256, bias=False)&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;어떻게 보면 embed_token 의 반대의 행렬이라고 볼 수 있죠. embed_tokens 는 128,256 x 2,048 행렬이었지만, lm_head 는 2,048 x 128,256 행렬이겠군요&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;그래서 Lm head 는 주어진 vector 를 기반으로 행렬곱 연산을 진행하여 model 이 갖고 있는 모든 Token 에 대한 점수(logits) 를 반환하게 되는 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;457&quot; data-origin-height=&quot;714&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bv0lgk/dJMcaacWjui/RkZikABPsr47uJmq6K1xEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bv0lgk/dJMcaacWjui/RkZikABPsr47uJmq6K1xEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bv0lgk/dJMcaacWjui/RkZikABPsr47uJmq6K1xEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbv0lgk%2FdJMcaacWjui%2FRkZikABPsr47uJmq6K1xEK%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;457&quot; height=&quot;714&quot; data-origin-width=&quot;457&quot; data-origin-height=&quot;714&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;요런식으로 말이죠&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;우리가 Open AI 혹은 Anthropic 에서 Temperature 나 Top_k 이런 파라미터를 받는 것들은 LM head 를 통해 나온 점수(logits)를 기반으로 확률을 구할 때 어떤 곱셈 파라미터를 적용하여, &lt;span style=&quot;color: #ee2323;&quot;&gt;확률 분포를 고르게 하거나(Temperature)&lt;/span&gt;, &lt;span style=&quot;color: #ee2323;&quot;&gt;가장 가능성 있는 몇 개만 선정(Top_k)&lt;/span&gt;하기 위한 과정이라고 이해하시면 됩니다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리하며&lt;/h2&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignLeft&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;007&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/007.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/007.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 우리는 llama 구조를 기반으로 LLM 이 동작하는 방식에 대해 쉽게 설명해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 layer 마다 되게 심도있는 학술 자료들이 많지만, 깊이 들어가지 않는 선에서 쉽게 정리해보았는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 원리를 이해함으로써, LLM application 을 제작하기 위한 Insight 를 얻기 위함으로 이해해 주시면 좋겠네요 ㅎㅎ&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;Reference&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;a href=&quot;https://huggingface.co/learn/llm-course/chapter1/5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;How transformers solve problem&lt;/a&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;a href=&quot;https://www.syncly.kr/blog/what-is-embedding-and-how-to-use&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Embedding 이란 무엇이고 언제 사용하는가&lt;/a&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;a href=&quot;https://sampathkumaran.medium.com/llms-simplified-language-modelling-and-decoding-2402ae5eb85c&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;LLM's simplified&lt;/a&gt;&lt;/blockquote&gt;</description>
      <category>Developer/AI</category>
      <category>llm</category>
      <category>transformer</category>
      <author>huisam</author>
      <guid isPermaLink="true">https://huisam.tistory.com/139</guid>
      <comments>https://huisam.tistory.com/entry/llm-architecture#entry139comment</comments>
      <pubDate>Sun, 11 Jan 2026 19:45:56 +0900</pubDate>
    </item>
    <item>
      <title>Automated dependency update - Renovate 란 무엇인가?</title>
      <link>https://huisam.tistory.com/entry/renovate</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignLeft&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;001&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/001.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/001.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요~ 오늘은 자동적으로 버젼을 관리해주는 renovate 에 대해 알아보도록 해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;p data-ke-size=&quot;size16&quot;&gt;우리는 &lt;span style=&quot;color: #ee2323;&quot;&gt;버젼관리&lt;/span&gt; 에 대한 자동화를 통해 조금 더 원활하게 버젼관리를 진행하고, 매번 업데이트되는 사항들을 받아보는 것을 기반으로 Pull request 까지 자동 생성하게끔 하는 것이 renovate 를 사용하는 목적이라고 볼 수 있겠습니다.&lt;/p&gt;
&lt;h3 data-end=&quot;218&quot; data-start=&quot;204&quot; data-ke-size=&quot;size23&quot;&gt;Renovate란?&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;409&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/veFxq/btsM1ozWciu/C3ZVZU2yDlrNhQbHZLfve0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/veFxq/btsM1ozWciu/C3ZVZU2yDlrNhQbHZLfve0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/veFxq/btsM1ozWciu/C3ZVZU2yDlrNhQbHZLfve0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FveFxq%2FbtsM1ozWciu%2FC3ZVZU2yDlrNhQbHZLfve0%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;1200&quot; height=&quot;409&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;409&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;416&quot; data-start=&quot;220&quot; data-ke-size=&quot;size16&quot;&gt;Renovate는 오픈 소스 도구로, &lt;u&gt;GitHub, GitLab, Bitbucket과 같은 다양한 플랫폼에서 의존성의 자동 업데이트&lt;/u&gt;를 지원합니다.&lt;/p&gt;
&lt;p data-end=&quot;416&quot; data-start=&quot;220&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;416&quot; data-start=&quot;220&quot; data-ke-size=&quot;size16&quot;&gt;이 도구는 프로젝트의 package.json, Gemfile, pom.xml 등 다양한 설정 파일을 분석하여 필요한 업데이트를 자동으로 제안하고, 이를 Pull Request 형태로 생성합니다.&lt;/p&gt;
&lt;h3 data-end=&quot;427&quot; data-start=&quot;418&quot; data-ke-size=&quot;size23&quot;&gt;주요 기능&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 주요한 기능에 대해 먼저 알아볼 것인데요. Renovate 에서 제공하는 주요 기능들은 아래와 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크게 4가지 주요한 기능들이 아주 중요한 포인트라고 볼 수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;686&quot; data-start=&quot;506&quot;&gt;&lt;b&gt;자동 업데이트&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;686&quot; data-start=&quot;525&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;613&quot; data-start=&quot;525&quot;&gt;Renovate는 다양한 패키지 매니저(예: npm, Yarn, Maven 등)를 지원하여 프로젝트의 의존성을 자동으로 확인하고, 최신 버전을 제안합니다.&lt;/li&gt;
&lt;li data-end=&quot;686&quot; data-start=&quot;617&quot;&gt;각 Pull Request는 업데이트된 버전의 세부 정보와 변경 로그를 포함하여 개발자가 쉽게 이해할 수 있도록 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;784&quot; data-start=&quot;688&quot;&gt;&lt;b&gt;스케줄링&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;784&quot; data-start=&quot;704&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;784&quot; data-start=&quot;704&quot;&gt;사용자 맞춤형 스케줄을 설정하여 업데이트 빈도를 조정할 수 있습니다. 예를 들어, 매주 월요일마다 업데이트를 확인하도록 설정할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;895&quot; data-start=&quot;786&quot;&gt;&lt;b&gt;보안 취약점 탐지&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;895&quot; data-start=&quot;807&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;895&quot; data-start=&quot;807&quot;&gt;Renovate는 보안 취약점이 발견된 패키지를 자동으로 감지하여 최신 안정 버전으로 업데이트합니다. 이는 프로젝트의 보안을 크게 강화하는 데 기여합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1044&quot; data-start=&quot;897&quot;&gt;&lt;b&gt;세부 설정 옵션&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1044&quot; data-start=&quot;917&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1044&quot; data-start=&quot;917&quot;&gt;Renovate는 다양한 설정을 통해 업데이트 방식과 조건을 조정할 수 있습니다. 예를 들어, 특정 라이브러리는 항상 최신 버전으로 업데이트하도록 설정하거나, 메이저 버전 업그레이드는 수동으로 처리하도록 설정할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;적용 예시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;span style=&quot;color: #ee2323;&quot;&gt;k8s 기반으로 self hosting&lt;/span&gt; 하는 예시를 들어볼까 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 회사내 시스템에 적용하는 것을 목표로 한다면, public github 보다는 enterprise github 혹은 gitlab 을 사용할 가능성이 높으니까요&lt;/p&gt;
&lt;pre id=&quot;code_1743343708200&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: batch/v1
kind: CronJob
metadata:
  name: renovate
spec:
  schedule: '@hourly' # 실행 주기 설정
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: renovate
              image: renovate/renovate:latest
              args:
                - user/repo
              env:
                - name: LOG_LEVEL
                  value: debug
              envFrom:
                - secretRef:
                    name: renovate-env
          restartPolicy: Never&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;위와 같이 기본적인 cronjob 을 생성해주고, 얼마정도의 주기로 실행해줄 지를 적용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 인증정보를 채워주는 secret 만 등록해주면 되는데요~!&lt;/p&gt;
&lt;pre id=&quot;code_1743343824492&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: v1
kind: Secret
metadata:
  name: renovate-env
type: Opaque
stringData:
  RENOVATE_GITHUB_COM_TOKEN: 'any-personal-user-token-for-github-com-for-fetching-changelogs'
  # You can set RENOVATE_AUTODISCOVER to true to run Renovate on all repos you have push access to
  RENOVATE_AUTODISCOVER: 'false'
  RENOVATE_ENDPOINT: 'https://github.company.com/api/v3'
  RENOVATE_GIT_AUTHOR: 'Renovate Bot &amp;lt;bot@renovateapp.com&amp;gt;'
  RENOVATE_PLATFORM: 'github'
  RENOVATE_TOKEN: 'your-github-enterprise-renovate-user-token'&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;이런식으로 사외 github 에서 change log 를 가져올 github access token 과 사내에서 사용되는 github token 을 발급받아 지정해주면 됩니다.&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;그렇게 되면, 자동으로 renovate 는 돌게 되고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;sample repository&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 사내 github 을 가져올 수는 없으니, 공개 github 에 적용되어 있는 제 예시 repository 와 함께 샘플로 남겨드립니다 ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/huisam/spring-observability/blob/main/renovate.json&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/huisam/spring-observability/blob/main/renovate.json&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743343894801&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;spring-observability/renovate.json at main &amp;middot; huisam/spring-observability&quot; data-og-description=&quot;Playground for spring observability based on opentelemetry - huisam/spring-observability&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/huisam/spring-observability/blob/main/renovate.json&quot; data-og-url=&quot;https://github.com/huisam/spring-observability/blob/main/renovate.json&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://github.com/huisam/spring-observability/blob/main/renovate.json&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/huisam/spring-observability/blob/main/renovate.json&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;spring-observability/renovate.json at main &amp;middot; huisam/spring-observability&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Playground for spring observability based on opentelemetry - huisam/spring-observability&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리하며&lt;/h2&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignLeft&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;007&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/007.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/007.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Renovate는 소프트웨어 개발에서 의존성 관리를 혁신적으로 변화시키는 도구입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동화된 업데이트, 보안 관리, 유연한 설정 옵션 등을 통해 개발자는 코드의 품질을 유지하고, 프로젝트의 안전성을 강화할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 이상 수동으로 의존성을 관리할 필요 없이, Renovate를 통해 효율적인 개발 환경을 구축해보시면 좋겠네요 ㅎㅎ&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;a href=&quot;https://docs.renovatebot.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Renovate Docs&lt;/a&gt;&lt;/blockquote&gt;</description>
      <category>Developer/Observability</category>
      <author>huisam</author>
      <guid isPermaLink="true">https://huisam.tistory.com/138</guid>
      <comments>https://huisam.tistory.com/entry/renovate#entry138comment</comments>
      <pubDate>Sun, 30 Mar 2025 23:15:25 +0900</pubDate>
    </item>
  </channel>
</rss>