<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>The Nirsa Way</title>
    <link>https://nirsa.tistory.com/</link>
    <description>Email: islandtim@naver.com
Blog: https://nirsa.tistory.com
GitHub: https://github.com/KoreaNirsa</description>
    <language>ko</language>
    <pubDate>Fri, 12 Jun 2026 22:31:18 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>KoreaNirsa</managingEditor>
    <image>
      <title>The Nirsa Way</title>
      <url>https://tistory1.daumcdn.net/tistory/3406000/attach/d7862faaa7ce4723b4a6f06ae5472b12</url>
      <link>https://nirsa.tistory.com</link>
    </image>
    <item>
      <title>[Spring Security] BCrypt Salt의 이해: 로그인 검증이 가능한 이유와 Pepper를 고려하는 상황</title>
      <link>https://nirsa.tistory.com/489</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;[Spring&amp;nbsp;Security]&amp;nbsp;BCrypt&amp;nbsp;Salt의&amp;nbsp;이해:&amp;nbsp;로그인&amp;nbsp;검증이&amp;nbsp;가능한&amp;nbsp;이유와&amp;nbsp;Pepper를&amp;nbsp;고려하는&amp;nbsp;상황&lt;/blockquote&gt;
&lt;p data-end=&quot;116&quot; data-start=&quot;82&quot; data-ke-size=&quot;size16&quot;&gt;비밀번호 저장을 처음 구현할 때 흔히 &quot;BCryp로 암호화해서 저장한다&quot;라고 표현하지만 정확히는 아래와 같은 단방향 해싱 흐름입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;[회원가입] 사용자 비밀번호 &amp;rarr; BCrypt 해싱 &amp;rarr; DB 저장
[로그인] 사용자 입력 비밀번호 &amp;rarr; DB의 BCrypt 해시와 비교&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;246&quot; data-start=&quot;215&quot; data-ke-size=&quot;size16&quot;&gt;그런데 BCrypt를 조금 더 들여다보면 BCrypt는 매번 랜덤 Salt를 생성하므로 같은 비밀번호도 매번 다른 Hash가 나오게 됩니다. 그렇다면 로그인이 성공 가능한 이유는 무엇일지 의문이 생깁니다.&lt;/p&gt;
&lt;p data-end=&quot;354&quot; data-start=&quot;336&quot; data-ke-size=&quot;size16&quot;&gt;이번 글은 위의 질문에서 출발하며 먼저 요약한 결론은 다음과 같습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. BCrypt는 암호화가 아니라 단방향 해시다.
2. BCrypt는 매번 랜덤 Salt를 생성한다.
3. Salt는 최종 Hash 문자열 안에 함께 저장된다.
4. 로그인 시에는 새 Salt를 만들지 않고, 저장된 Hash 안의 Salt를 재사용한다.
5. DB가 유출되면 Salt와 Hash가 함께 노출된다.
6. 따라서 오프라인 딕셔너리 공격은 가능하다.
7. 이를 완화하기 위해 서버 측 비밀 값인 Pepper를 추가로 사용할 수 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;865&quot; data-start=&quot;632&quot; data-ke-size=&quot;size16&quot;&gt;Spring Security 공식 문서에서도 BCrypt는 Salt를 내장 생성하고, 생성된 Salt를 출력 Hash에 포함한다고 설명합니다. 또한 Spring Security의 BCryptPasswordEncoder는 기본적으로 strength 값 10을 사용하며, version, strength, SecureRandom을 설정할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;676&quot; data-origin-height=&quot;274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WWBOT/dJMcabdjjfs/xC3HSTUthNm4TGfYl2m63k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WWBOT/dJMcabdjjfs/xC3HSTUthNm4TGfYl2m63k/img.png&quot; data-alt=&quot;https://docs.spring.io/spring-security/reference/features/authentication/password-storage.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WWBOT/dJMcabdjjfs/xC3HSTUthNm4TGfYl2m63k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWWBOT%2FdJMcabdjjfs%2FxC3HSTUthNm4TGfYl2m63k%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;676&quot; height=&quot;274&quot; data-origin-width=&quot;676&quot; data-origin-height=&quot;274&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://docs.spring.io/spring-security/reference/features/authentication/password-storage.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;BCrypt는 암호화가 아니라 Hash 이다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 아직도 &quot;비밀번호를 암호화해서 저장한다&quot;는 표현을 사용하는 경우가 많지만 정확히는 해시로 저장한다고 표현하는 것이 맞습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비밀번호 저장의 목적은 원문을 복원하는 것이 아니라 사용자가 입력한 값이 기존에 저장된 값과 동일한지를 검증하는 것이기 때문입니다. &lt;br /&gt;&lt;br /&gt;따라서 비밀번호 저장에는 AES와 같은 양방향 암호화보다 BCrypt, Argon2, PBKDF2와 같은 단방향 Password Hashing 알고리즘이 사용됩니다. 용어는 아래와 같습니다.&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;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.6124%; text-align: center;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 33.6821%; text-align: center;&quot;&gt;암호화&lt;/td&gt;
&lt;td style=&quot;width: 23.3527%; text-align: center;&quot;&gt;일반 해시&lt;/td&gt;
&lt;td style=&quot;width: 23.3527%; text-align: center;&quot;&gt;Password Hashing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.6124%; text-align: center;&quot;&gt;대표 예시&lt;/td&gt;
&lt;td style=&quot;width: 33.6821%; text-align: center;&quot;&gt;AES, RSA&lt;/td&gt;
&lt;td style=&quot;width: 23.3527%; text-align: center;&quot;&gt;SHA-256, SHA-512&lt;/td&gt;
&lt;td style=&quot;width: 23.3527%; text-align: center;&quot;&gt;BCrypt, Argon2, PBKDF2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.6124%; text-align: center;&quot;&gt;방향&lt;/td&gt;
&lt;td style=&quot;width: 33.6821%; text-align: center;&quot;&gt;양방향&lt;/td&gt;
&lt;td style=&quot;width: 23.3527%; text-align: center;&quot;&gt;단방향&lt;/td&gt;
&lt;td style=&quot;width: 23.3527%; text-align: center;&quot;&gt;단방향&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.6124%; text-align: center;&quot;&gt;복호화&lt;/td&gt;
&lt;td style=&quot;width: 33.6821%; text-align: center;&quot;&gt;가능&lt;/td&gt;
&lt;td style=&quot;width: 23.3527%; text-align: center;&quot;&gt;불가능&lt;/td&gt;
&lt;td style=&quot;width: 23.3527%; text-align: center;&quot;&gt;불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.6124%; text-align: center;&quot;&gt;비밀번호 저장&lt;/td&gt;
&lt;td style=&quot;width: 33.6821%; text-align: center;&quot;&gt;부적합&lt;/td&gt;
&lt;td style=&quot;width: 23.3527%; text-align: center;&quot;&gt;단독 사용 부적합&lt;/td&gt;
&lt;td style=&quot;width: 23.3527%; text-align: center;&quot;&gt;적합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.6124%; text-align: center;&quot;&gt;로그인 검증&lt;/td&gt;
&lt;td style=&quot;width: 33.6821%; text-align: center;&quot;&gt;복호화 후 비교&lt;/td&gt;
&lt;td style=&quot;width: 23.3527%; text-align: center;&quot;&gt;재계산 후 비교 가능&lt;/td&gt;
&lt;td style=&quot;width: 23.3527%; text-align: center;&quot;&gt;재계산 후 비교&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;단방향 해시라고 해서 모두 비밀번호 저장에 적합한 것은 아닙니다. &lt;/span&gt;&lt;span&gt;SHA-256, SHA-512 같은 일반 해시 함수는 계산이 너무 빠르기 때문에 &lt;/span&gt;&lt;span&gt;공격자가 대량의 후보 비밀번호를 빠르게 대입할 수 있습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;따라서 비밀번호 저장에는 BCrypt, Argon2id, PBKDF2, scrypt처럼 &lt;/span&gt;&lt;span&gt;의도적으로 계산 비용을 높인 Password Hashing 알고리즘을 사용해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span&gt;Bcrypt 구조 파악하기&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BCryptPasswordEncoder의 코드를 직접 확인해보면 Salt 생성 후 Hash를 생성하는 모습과 저장된 Hash와 비교하여 확인하는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;451&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgkvM0/dJMcafmy1d2/rlzNEd11OfSsgsr5vaFkOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgkvM0/dJMcafmy1d2/rlzNEd11OfSsgsr5vaFkOk/img.png&quot; data-alt=&quot;https://github.com/spring-projects/spring-security/blob/80dcf01ce5e755483db5078efd96393c5c557131/crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.java&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgkvM0/dJMcafmy1d2/rlzNEd11OfSsgsr5vaFkOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdgkvM0%2FdJMcafmy1d2%2FrlzNEd11OfSsgsr5vaFkOk%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;850&quot; height=&quot;451&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;451&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://github.com/spring-projects/spring-security/blob/80dcf01ce5e755483db5078efd96393c5c557131/crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.java&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BCrypt를 보면 Salt 길이가 16바이트로 정의되어 있으며 getsalt(...) 내부에서는 아래와 같은 코드가 실행됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1780538628497&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;byte rnd[] = new byte[BCRYPT_SALT_LEN];
random.nextBytes(rnd);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분이 실제 랜덤 Salt가 생성되는 지점입니다. SecureRandom을 통해 16바이트의 랜덤 데이터를 생성한 뒤, BCrypt 포맷($2a$10$...)에 맞게 버전(version), 비용 계수(cost factor), 그리고 Salt 값을 조합하여 문자열을 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성일 기준 BCrypt 소스코드 686~709라인을 살펴보면 SecureRandom#nextBytes()로 랜덤 바이트를 생성한 후, 이를 BCrypt 전용 Base64 형식으로 인코딩하여 Salt 문자열에 포함시키는 과정을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BCrypt의 저장되는 문자열을 보통 $2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy와 같이 작성되는데, 구조는 아래와 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Version : $2a&lt;/li&gt;
&lt;li&gt;Cost : 10&lt;/li&gt;
&lt;li&gt;Salt : N9qo8uLOickgx2ZMRZoMye&lt;/li&gt;
&lt;li&gt;Hash : IjZAgcfl7p92ldGxad68LJZdL17lhWy&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Security 내부 BCrypt에서는 저장된 문자열에서 cost와 salt를 파싱합니다. 실제로 BCrypt의 650~653 라인을 확인해보면 22자의 Salt 문자열을 추출하고 이를 다시 디코딩하는 것을 확인해볼 수 있습니다.&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;104&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvF45R/dJMcab5u03i/N1oio9euQ4rJzfSuQp0PFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvF45R/dJMcab5u03i/N1oio9euQ4rJzfSuQp0PFK/img.png&quot; data-alt=&quot;https://github.com/spring-projects/spring-security/blob/80dcf01ce5e755483db5078efd96393c5c557131/crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvF45R/dJMcab5u03i/N1oio9euQ4rJzfSuQp0PFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvF45R%2FdJMcab5u03i%2FN1oio9euQ4rJzfSuQp0PFK%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;104&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;104&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://github.com/spring-projects/spring-security/blob/80dcf01ce5e755483db5078efd96393c5c557131/crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 Hash 결과를 만들 때는 Salt와 Hash를 하나의 문자열에 이어붙여 만들어지게 됩니다. 즉, BCrypt는 단순히 Hash만 저장하는 것이 아니라 검증에 필요한 모든 정보를 하나의 문자열 안에 포함하고 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;검증할 때는 새로운 Salt를 만들지 않는다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BCrypt의 방식에 의문이 든것은 아래와 같았습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;BCrypt는 매번 랜덤 Salt를 생성함&lt;/li&gt;
&lt;li&gt;로그인때도 새 Salt를 만들면 전체적인 Hash가 달라짐&lt;/li&gt;
&lt;li&gt;그렇다면 같은 값을 입력하더라도 로그인은 매번 실패됨&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제로 코드를 살펴본 결과 검증을 할때는 새 Salt를 만들지 않았습니다. 스프링 시큐리티의 matchesNonNull()은 입력 비밀번호와 DB에 저장된 BCrypt 문자열을 BCrypt.checkpw()로 넘깁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 코드는 아래와 같은데, encodedPassword는 DB에 저장된 전체 BCrypt 문자열입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1780539144049&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;return BCrypt.checkpw(rawPassword.toString(), encodedPassword);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BCrypt.checkpw()는 이 문자열에서 version, cost, salt를 다시 꺼내고 사용자가 입력한 평문 비밀번호(rawPassword.toString())를 같은 조건으로 다시 해쉬하여 비교합니다. 간단한 흐름은 아래와 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;로그인 요청 : rawPassword = &quot;1234&quot;&lt;/li&gt;
&lt;li&gt;DB 저장 값 : &lt;span&gt;$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;DB에 저장된 값에서 Salt 추출&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;입력된 비밀번호(rawPassword)를 추출한 Salt로 BCrypt 재계산&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;재계산 결과와 DB 저장값 비교&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 BCrypt는 처음 저장 시 Salt를 랜덤 생성하여 Salt+입력값으로 해시를 만들어 저장하고, 추후 검증이 필요할 경우 Salt값을 가져와 입력값으로 해싱하여 기존 값과 비교하는 구조를 가집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 같은 값이더라도 매번 다른 결과가 나오므로 레인보우 테이블의 공격을 무효화할 수 있습니다. 예시는 아래와 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;rawPassword = &quot;1234&quot;&lt;/li&gt;
&lt;li&gt;BCrypt가 랜덤 Salt 생성 후 해싱 -&amp;gt; A의 결과&lt;/li&gt;
&lt;li&gt;이후 BCrypt를 재실행하면 또 새로운 랜덤 Salt가 생성되며 해싱 -&amp;gt; B의 결과&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Salt가&amp;nbsp;없으면&amp;nbsp;공격자는&amp;nbsp;&quot;1234&quot;&amp;nbsp;&amp;rarr;&amp;nbsp;Hash&amp;nbsp;값처럼&amp;nbsp;미리&amp;nbsp;계산해&amp;nbsp;둔&amp;nbsp;테이블을&amp;nbsp;여러&amp;nbsp;DB에&amp;nbsp;재사용할&amp;nbsp;수&amp;nbsp;있습니다. &lt;br /&gt;하지만&amp;nbsp;사용자마다&amp;nbsp;Salt가&amp;nbsp;다르면&amp;nbsp;같은&amp;nbsp;&quot;1234&quot;라도&amp;nbsp;Salt마다&amp;nbsp;결과가&amp;nbsp;달라집니다. &lt;br /&gt;&lt;br /&gt;따라서 공격자는 기존에 만들어 둔 범용 Rainbow Table을 그대로 재사용하기 어렵고 각 사용자 Salt마다 후보 비밀번호를 다시 계산해야 합니다. 즉 Salt는 공격 자체를 불가능하게 만드는 값이라기보다 사전&amp;nbsp;계산&amp;nbsp;공격의&amp;nbsp;경제성을&amp;nbsp;크게&amp;nbsp;떨어뜨리는&amp;nbsp;값입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span&gt;Salt는 왜 공개되어도 되고, Cost Factor는 어떤 역할을 수행하는가?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 사람들이 Salt도 숨겨야 하는 값이라고 생각하지만 Salt의 목적은 비밀 유지가 아닙니다. Salt는&amp;nbsp;동일한&amp;nbsp;비밀번호가&amp;nbsp;동일한&amp;nbsp;Hash로&amp;nbsp;저장되는&amp;nbsp;문제를&amp;nbsp;해결하기&amp;nbsp;위해&amp;nbsp;존재합니다. &lt;br /&gt;&lt;br /&gt;즉 공격자가 Salt를 알고 있다는 전제 하에 설계된 것이 BCrypt 이기 때문에&amp;nbsp;Salt를 Hash 내부에 그대로 저장합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1780540976779&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;1234&quot; &amp;rarr; Salt A &amp;rarr; Hash A 
&quot;1234&quot; &amp;rarr; Salt B &amp;rarr; Hash B&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;BCrypt의 Cost Factor는 해시 계산에 필요한 연산량을 의미하며 &lt;/span&gt;&lt;span&gt;Cost가 1 증가할 때마다 필요한 연산량은 2배씩 증가합니다.&lt;/span&gt;&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;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;COst&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;반복 시 발생하는 비용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;10&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;2^10 = 1,024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;11&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: center;&quot;&gt;2^11 =&lt;/span&gt; 2,048&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;12&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: center;&quot;&gt;2^12 =&lt;span&gt; &lt;/span&gt;&lt;/span&gt;4,096&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;13&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: center;&quot;&gt;2^13 =&lt;/span&gt; 8,192&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;14&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: center;&quot;&gt;2^14 =&lt;span&gt; &lt;/span&gt;&lt;/span&gt;16,384&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 Salt는 레인보우 테이블 공격을 방어하고 Cost Factor는 무차별 대입 공격과 딕셔너리 공격의 비용을 증가시키는 역할을 수행합니다. 다만, Cost Factor는 높을수록 많은 연산이 필요하기에 서버의 부하가 발생할 수 있으므로 너무 높기만 해도 좋은 것은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cost는&amp;nbsp;높을수록&amp;nbsp;공격&amp;nbsp;비용을&amp;nbsp;증가시키지만,&amp;nbsp;로그인&amp;nbsp;서버의&amp;nbsp;CPU&amp;nbsp;비용도&amp;nbsp;함께&amp;nbsp;증가시킵니다. &lt;br /&gt;따라서 무조건 높은 값을 선택하기보다는 실제 서버 환경에서 측정한 뒤 사용자&amp;nbsp;경험과&amp;nbsp;서버&amp;nbsp;부하를&amp;nbsp;감당할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;수준으로&amp;nbsp;조정해야&amp;nbsp;합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span&gt;BCrypt만 사용할 경우 DB 유출 시 어떤 일이 발생하는가&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 결론이 나옵니다. BCrypt가 레인보우 테이블 어택을 어렵게 만들지만, DB가 유출되면 오프라인 딕셔너리 공격 자체가 불가능해지는 것은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB가 유출되면 공격자는 아래와 같은 정보를 얻습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1780539559412&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$2a$12$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에는 Version, Cost, Salt, Hash의 정보가 들어있기에 공격자는 비밀번호 후보를 하나씩 대입할 수 있습니다. 간략한 공격 흐름은 아래와 같습니다. 딕셔너리 어택은 자주 사용되는 단어, 문구, 숫자등을 조합한 사전(Disctionary)을 미리 만들어두고 이를 순차적으로 대입하는 해킹 기법이며 아래에서 언급하는 후보 비밀번호는 사전에 있는 하나의 문자열을 의미합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;DB Hash 획득&lt;/li&gt;
&lt;li&gt;Hash 문자열에서 Salt만 추출&lt;/li&gt;
&lt;li&gt;후보 비밀번호 선택&lt;/li&gt;
&lt;li&gt;BCrypt(후보 비밀번호, 추출한 Salt)&lt;/li&gt;
&lt;li&gt;DB Hash와비교&lt;/li&gt;
&lt;li&gt;일치하면 비밀번호 추측 성공&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;충분히 길고 예측 불가능한 비밀번호라면 BCrypt Hash를 깨는 것은 매우 어렵지만&amp;nbsp;실제 사용자의 비밀번호는 사전에 있는 단어, 생년월일, 전화번호, 서비스명 등을 조합하는 경우가 많습니다.&lt;br /&gt;&lt;br /&gt;따라서 DB가 유출되면 공격자는 저장된 Salt와 Cost를 이용해 오프라인 딕셔너리 공격을 수행할 수 있고 약한&amp;nbsp;비밀번호는&amp;nbsp;여전히&amp;nbsp;추측될&amp;nbsp;수&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;다만 이 논의는 기본적으로 &amp;ldquo;비밀번호 저장&amp;rdquo;에 대한 이야기입니다. &lt;/span&gt;주민등록번호, 전화번호, 사번, 학번처럼 후보 공간이 작거나 복원이 필요한 데이터는 비밀번호와&amp;nbsp;다른&amp;nbsp;방식으로&amp;nbsp;설계해야&amp;nbsp;합니다. &lt;br /&gt;&lt;br /&gt;원문 복원이 필요 없다면 서버 측 비밀 키를 사용하는 HMAC 기반 저장을 검토할 수 있고, 원문 복원이 필요하다면 KMS/Secret Manager로 관리되는 키를 사용한 필드 레벨 암호화가 더 적합할 수 있습니다.&lt;br /&gt;&lt;br /&gt;중요한&amp;nbsp;점은&amp;nbsp;Salt가&amp;nbsp;공개된&amp;nbsp;단순&amp;nbsp;Hash만으로는&amp;nbsp;후보&amp;nbsp;공간이&amp;nbsp;작은&amp;nbsp;데이터를&amp;nbsp;충분히&amp;nbsp;보호하기&amp;nbsp;어렵다는&amp;nbsp;것입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span&gt;Salt와 Pepper&lt;/span&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 147px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 23.9147%; text-align: center; height: 21px;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 32.8682%; text-align: center; height: 21px;&quot;&gt;Salt&lt;/td&gt;
&lt;td style=&quot;width: 43.217%; text-align: center; height: 21px;&quot;&gt;Pepper&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 23.9147%; text-align: center; height: 21px;&quot;&gt;목적&lt;/td&gt;
&lt;td style=&quot;width: 32.8682%; text-align: center; height: 21px;&quot;&gt;Hash 중복 방지, 레인보우 테이블 방어&lt;/td&gt;
&lt;td style=&quot;width: 43.217%; text-align: center; height: 21px;&quot;&gt;DB 단독 유출 시 오프라인 크래킹 난이도 증가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 23.9147%; text-align: center; height: 21px;&quot;&gt;저장 위치&lt;/td&gt;
&lt;td style=&quot;width: 32.8682%; text-align: center; height: 21px;&quot;&gt;Hash 내부 또는 DB&lt;/td&gt;
&lt;td style=&quot;width: 43.217%; text-align: center; height: 21px;&quot;&gt;DB와 분리된 Secret Manager, Vault, KMS, HSM 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 23.9147%; text-align: center; height: 21px;&quot;&gt;공개 여부&lt;/td&gt;
&lt;td style=&quot;width: 32.8682%; text-align: center; height: 21px;&quot;&gt;공개 가능&lt;/td&gt;
&lt;td style=&quot;width: 43.217%; text-align: center; height: 21px;&quot;&gt;비공개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 23.9147%; text-align: center; height: 21px;&quot;&gt;사용자별&lt;/td&gt;
&lt;td style=&quot;width: 32.8682%; text-align: center; height: 21px;&quot;&gt;사용자마다 다름&lt;/td&gt;
&lt;td style=&quot;width: 43.217%; text-align: center; height: 21px;&quot;&gt;일반적으로 애플리케이션/시스템 단위로 공유&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 23.9147%; text-align: center; height: 21px;&quot;&gt;DB 유출 시 노출&lt;/td&gt;
&lt;td style=&quot;width: 32.8682%; text-align: center; height: 21px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 43.217%; text-align: center; height: 21px;&quot;&gt;X&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 23.9147%; text-align: center; height: 21px;&quot;&gt;교체 난이도&lt;/td&gt;
&lt;td style=&quot;width: 32.8682%; text-align: center; height: 21px;&quot;&gt;낮음&lt;/td&gt;
&lt;td style=&quot;width: 43.217%; text-align: center; height: 21px;&quot;&gt;높음. 기존 비밀번호 원문을 모르므로 로그인/비밀번호 재설정 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-end=&quot;11442&quot; data-start=&quot;11231&quot; data-ke-size=&quot;size16&quot;&gt;OWASP는 Pepper를 비밀번호 Salt처럼 공개하거나 Hash와 함께 저장하면 안 되고 DB와 분리된 Secret Vault나 HSM에 저장해야 한다고 설명합니다. 또한 Pepper가 노출되면 사용자의 원문 비밀번호를 알 수 없기 때문에 Pepper 교체에는 비밀번호 재설정이 필요하다고 설명합니다.&lt;/p&gt;
&lt;p data-end=&quot;11626&quot; data-start=&quot;11444&quot; data-ke-size=&quot;size16&quot;&gt;추가로 OWASP Cryptographic Storage Cheat Sheet는 키를 소스 코드에 하드코딩하거나 버전 관리 시스템에 올리지 말고, 가능하면 KMS, Key Vault, HSM, Vault 같은 별도 키 관리 시스템을 사용하라고 안내합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Pepper를 추가하면 무엇이 달라질까&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 서버에서 password + pepper 형태로 저장했다고 가정한다면 DB가 유출되더라도 공격자는 Pepper 값을 모르므로 Salt를 알고, 실제 사용된 값으로 해싱하더라도 동일한 해시를 재현할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 아래와 같은 코드가 존재한다면 DB가 유출되엇을 때 $2a$12$N9qo8uLOickgx2ZMRZoMye...와 같은 값을 가져가 Salt를 알 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1780541257695&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;String encoded = passwordEncoder.encode(rawPassword + pepper);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 pepper를 사용했을 경우 &quot;password123&quot; + &quot;pepper값&quot;이 붙기에 공격자는 pepper를 추측할 수 없어서 동일한 입력값을 넣더라도 같은 해시를 확인할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 &quot;password123&quot;를 해싱하였다면 공격자는 DB 유출 된 데이터를 기반으로 Salt + &quot;password123&quot;으로 동일한 해시값을 얻어서 추측이 가능하지만, &quot;password123secret-pepper&quot;를 해싱했을 경우 공격자는 Salt + &quot;password123&quot;으로 동일한 해시값을 얻어낼 수 없습니다. 실제 사용자의 비밀번호 뒤에 pepper가 추가적으로 붙어서 해싱된 값이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 DB와 함께 서버의 코드 또는 환경 변수, Secret Manager가 노출되었을 경우 공격자는 pepper의 값도 알아낼 수 있기 때문에 만능은 아닙니다. pepper가 유효한 것은 DB 데이터만 유출되었을 때로 한정됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-size=&quot;size16&quot; data-ke-style=&quot;style1&quot;&gt;Pepper를 추가할 때 단순 문자열 연결만으로 충분할까?&lt;/blockquote&gt;
&lt;p data-end=&quot;138&quot; data-start=&quot;97&quot; data-ke-size=&quot;size16&quot;&gt;에서는 이해를 돕기 위해 아래와 같은 형태로 Pepper를 설명했습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;String encoded = passwordEncoder.encode(rawPassword + pepper);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;292&quot; data-start=&quot;216&quot; data-ke-size=&quot;size16&quot;&gt;개념적으로는 맞습니다. 사용자가 입력한 비밀번호에 서버만 알고 있는 비밀값인 Pepper를 추가한 뒤 BCrypt로 해싱하는 구조입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;rawPassword + pepper &amp;rarr; BCrypt(rawPassword + pepper, random salt, cost) &amp;rarr; DB 저장&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;673&quot; data-start=&quot;422&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 하면 DB만 유출된 상황에서는 공격자가 Salt와 Hash를 모두 알고 있더라도 Pepper 값을 모르기 때문에 동일한 BCrypt 입력값을 재현하기 어려워집니다. OWASP도 Pepper를 Salt와 함께 사용할 수 있는 추가 방어층으로 설명하며, Pepper는 Hash와 함께 저장하지 말고 DB와 분리된 Secret Vault나 HSM 등에 보관해야 한다고 설명합니다.&lt;br /&gt;&lt;br /&gt;하지만 실무에서는 단순히 rawPassword + pepper를 그대로 BCrypt에 넣는 방식에는 주의가 필요합니다.&lt;br /&gt;&lt;br /&gt;가장 큰 이유는 BCrypt의 입력 길이 제한입니다. OWASP Password Storage Cheat Sheet는 bcrypt 사용 시 72바이트 입력 제한을 고려해야 한다고 설명합니다. Spring Security도 2025년에 BCryptPasswordEncoder의 72자 초과 비밀번호 처리와 관련된 보안 권고를 발표한 바 있습니다. (&lt;a href=&quot;https://spring.io/security/cve-2025-22228&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CVE-2025-2228&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;예를 들어 아래와 같은 입력이 있다고 가정해보겠습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;rawPassword = &quot;매우 긴 비밀번호...&quot;
pepper      = &quot;server-secret-pepper&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1226&quot; data-start=&quot;1128&quot; data-ke-size=&quot;size16&quot;&gt;BCrypt 구현체가 입력 앞부분 72바이트까지만 사용한다면, 비밀번호가 이미 72바이트를 초과하는 순간 뒤에 붙인 Pepper가 실제 해싱 입력에 반영되지 않을 수 있습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;rawPassword + pepper &amp;rarr; BCrypt는 앞 72바이트까지만 사용 &amp;rarr; pepper가 잘릴 수 있음&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1414&quot; data-start=&quot;1340&quot; data-ke-size=&quot;size16&quot;&gt;따라서 실무에서는 단순 문자열 연결보다는 HMAC 기반 pre-hash 후 BCrypt를 적용하는 방식을 고려할 수 있습니다. 흐름은 아래와 같습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;[Pre-hash Peppering]
사용자 입력 비밀번호 &amp;rarr; HMAC-SHA384(rawPassword, pepperKey) &amp;rarr; Base64 인코딩 &amp;rarr; BCrypt(Base64(HMAC 결과), random salt, cost) &amp;rarr; DB 저장&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1779&quot; data-start=&quot;1654&quot; data-ke-size=&quot;size16&quot;&gt;이 방식에서는 Pepper를 단순히 문자열로 이어붙이지 않고 HMAC의 Secret Key로 사용합니다. HMAC 결과를 Base64로 인코딩하면 고정 길이 문자열이 만들어지고 이 값을 BCrypt의 입력으로 사용합니다.&lt;/p&gt;
&lt;p data-end=&quot;1935&quot; data-start=&quot;1781&quot; data-ke-size=&quot;size16&quot;&gt;OWASP도 bcrypt와 pre-hashing을 함께 사용할 경우 bcrypt(base64(hmac-sha384(password, pepper)), salt, cost) 형태의 접근을 예시로 제시합니다.&lt;/p&gt;
&lt;p data-end=&quot;1935&quot; data-start=&quot;1781&quot; data-ke-size=&quot;size16&quot;&gt;만약, 스프링부트에서 해당 방식을 적용한다면 아래와 같은 코드와 같이 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1780546398570&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.security.crypto.password.PasswordEncoder;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.Base64;
import java.util.Objects;

public final class PepperingPasswordEncoder implements PasswordEncoder {

    private static final String HMAC_ALGORITHM = &quot;HmacSHA384&quot;;

    private final PasswordEncoder delegate;
    private final byte[] pepperKey;

    public PepperingPasswordEncoder(PasswordEncoder delegate, byte[] pepperKey) {
        this.delegate = Objects.requireNonNull(delegate);
        this.pepperKey = Objects.requireNonNull(pepperKey).clone();
    }

    @Override
    public String encode(CharSequence rawPassword) {
        return delegate.encode(preHash(rawPassword));
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        return delegate.matches(preHash(rawPassword), encodedPassword);
    }

    private String preHash(CharSequence rawPassword) {
        try {
            Mac mac = Mac.getInstance(HMAC_ALGORITHM);
            SecretKeySpec keySpec = new SecretKeySpec(pepperKey, HMAC_ALGORITHM);
            mac.init(keySpec);

            byte[] hmac = mac.doFinal(
                    rawPassword.toString().getBytes(StandardCharsets.UTF_8)
            );

            return Base64.getEncoder().encodeToString(hmac);
        } catch (GeneralSecurityException e) {
            throw new IllegalStateException(&quot;Failed to calculate password HMAC&quot;, e);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780546407417&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.Base64;

@Configuration
public class PasswordEncoderConfig {

    @Bean
    public PasswordEncoder passwordEncoder(
            @Value(&quot;${security.password.pepper-key}&quot;) String base64PepperKey
    ) {
        byte[] pepperKey = Base64.getDecoder().decode(base64PepperKey);

        return new PepperingPasswordEncoder(
                new BCryptPasswordEncoder(12),
                pepperKey
        );
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;4796&quot; data-start=&quot;4757&quot; data-ke-size=&quot;size16&quot;&gt;위와 같이 구성하면 회원가입과 로그인 검증 흐름은 아래처럼 동작합니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;[회원가입]
rawPassword &amp;rarr; HMAC-SHA384(rawPassword, pepperKey) &amp;rarr; Base64(HMAC 결과) &amp;rarr; BCryptPasswordEncoder.encode() &amp;rarr; DB 저장&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;[로그인]
rawPassword &amp;rarr; HMAC-SHA384(rawPassword, pepperKey) &amp;rarr; Base64(HMAC 결과) &amp;rarr; BCryptPasswordEncoder.matches() &amp;rarr; DB의 BCrypt Hash와 비교&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5244&quot; data-start=&quot;5152&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 하면 서비스 코드에서는 기존과 동일하게 passwordEncoder.encode()와 passwordEncoder.matches()만 사용하면 됩니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;String encodedPassword = passwordEncoder.encode(rawPassword);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;boolean result = passwordEncoder.matches(rawPassword, storedPasswordHash);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5452&quot; data-start=&quot;5409&quot; data-ke-size=&quot;size16&quot;&gt;즉, Pepper 적용 여부를 서비스 로직에서 매번 신경 쓰지 않아도 됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span&gt;마무리&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 BCrypt의 Salt는 숨기기 위한 값이 아니라 동일한 비밀번호가 동일한 Hash로 저장되는 것을 방지하고 Rainbow&amp;nbsp;Table과&amp;nbsp;같은&amp;nbsp;사전&amp;nbsp;계산&amp;nbsp;공격의&amp;nbsp;효율을&amp;nbsp;떨어뜨리기&amp;nbsp;위한&amp;nbsp;값입니다. &lt;br /&gt;&lt;br /&gt;BCrypt는 Salt와 Cost Factor를 통해 비밀번호 크래킹 비용을 증가시키지만 DB가 유출되면 공격자는 저장된 BCrypt 문자열에서 Version, Cost, Salt, Hash를 모두 얻을 수 있습니다. 따라서&amp;nbsp;약한&amp;nbsp;비밀번호에&amp;nbsp;대해서는&amp;nbsp;오프라인&amp;nbsp;딕셔너리&amp;nbsp;공격이&amp;nbsp;여전히&amp;nbsp;가능합니다. &lt;br /&gt;&lt;br /&gt;Pepper는&amp;nbsp;이런&amp;nbsp;상황에서&amp;nbsp;추가&amp;nbsp;방어층이&amp;nbsp;될&amp;nbsp;수&amp;nbsp;있습니다. &lt;br /&gt;Pepper를 DB와 분리된 Secret Manager, KMS, Vault, HSM 등에 안전하게 보관하면 공격자가&amp;nbsp;DB만&amp;nbsp;탈취했을&amp;nbsp;때&amp;nbsp;동일한&amp;nbsp;Hash를&amp;nbsp;재현하기&amp;nbsp;어려워집니다. &lt;br /&gt;&lt;br /&gt;다만&amp;nbsp;Pepper는&amp;nbsp;만능이&amp;nbsp;아닙니다. &lt;br /&gt;애플리케이션 서버, 환경 변수, Secret Manager, 배포 파이프라인까지 함께 노출되면 Pepper의 보호 효과는 크게 줄어들며&amp;nbsp;Pepper가&amp;nbsp;유출되면&amp;nbsp;기존&amp;nbsp;비밀번호&amp;nbsp;원문을&amp;nbsp;알&amp;nbsp;수&amp;nbsp;없기&amp;nbsp;때문에&amp;nbsp;교체와&amp;nbsp;마이그레이션이&amp;nbsp;어렵습니다. &lt;br /&gt;&lt;br /&gt;결국 BCrypt는 강력한 Password Hashing 알고리즘이지만 Salt,&amp;nbsp;Cost,&amp;nbsp;Pepper의&amp;nbsp;역할과&amp;nbsp;한계를&amp;nbsp;이해하고&amp;nbsp;위협&amp;nbsp;모델에&amp;nbsp;맞게&amp;nbsp;조합해야&amp;nbsp;합니다.&lt;/p&gt;</description>
      <category>Development/Spring</category>
      <author>KoreaNirsa</author>
      <guid isPermaLink="true">https://nirsa.tistory.com/489</guid>
      <comments>https://nirsa.tistory.com/489#entry489comment</comments>
      <pubDate>Thu, 4 Jun 2026 11:52:05 +0900</pubDate>
    </item>
    <item>
      <title>AGENTS.md와 이슈, 스펙, CodeRabbit으로 만드는 AI 개발 사이클</title>
      <link>https://nirsa.tistory.com/488</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;AGENTS.md와&amp;nbsp;이슈,&amp;nbsp;스펙,&amp;nbsp;CodeRabbit으로&amp;nbsp;만드는&amp;nbsp;AI&amp;nbsp;개발&amp;nbsp;사이클&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에는 AI에게 단순히 &amp;ldquo;이 기능 구현해줘&amp;rdquo;라고 요청하지 않습니다. &lt;br /&gt;먼저 Issue를 만들고, 필요한 경우 Spec을 작성한 뒤, AGENTS.md와 작업 프롬프트를 기준으로 개발 흐름을 고정합니다. &lt;br /&gt;&lt;br /&gt;현재 제가 사용하는 흐름은 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이슈 생성&lt;/li&gt;
&lt;li&gt;스펙 생성 또는 확인&lt;/li&gt;
&lt;li&gt;구현&lt;/li&gt;
&lt;li&gt;테스트 및 정적 검사&lt;/li&gt;
&lt;li&gt;PR 생성&lt;/li&gt;
&lt;li&gt;CodeRabbit 리뷰 요청&lt;/li&gt;
&lt;li&gt;리뷰 타당성 검토&lt;/li&gt;
&lt;li&gt;필요한 내용만 반영&lt;/li&gt;
&lt;li&gt;CodeRabbit 재리뷰&lt;/li&gt;
&lt;li&gt;최종 diff 검토&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 흐름은 AI가 임의로 정한 방식이 아닙니다. &lt;br /&gt;프로젝트 루트에 있는 AGENTS.md, 제가 사용하는 작업 프롬프트, Issue, Spec, CodeRabbit이 함께 작동하면서 만들어지는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;왜 이슈를 먼저 만드는가&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;저는 작업을 시작할 때 가능한 한 Issue를 먼저 만듭니다. &lt;br /&gt;&lt;br /&gt;이슈를 사용하는 이유는 단순히 할 일을 기록하기 위해서가 아닙니다. 제가 작업할 때 이슈는&amp;nbsp;작업의&amp;nbsp;목적,&amp;nbsp;범위,&amp;nbsp;완료&amp;nbsp;기준을&amp;nbsp;고정하는&amp;nbsp;기준점입니다. &lt;br /&gt;&lt;br /&gt;AI와 함께 개발할 때 가장 위험한 부분은 구현 속도가 아니라 작업 범위가 흐려지는 것입니다. &lt;br /&gt;요청이 모호하면 AI는 그럴듯한 방향으로 기능을 넓히거나, Spec에 없는 동작을 추가할 수 있는데, 이슈는 그 위험을 줄이는 역할을 합니다. &lt;br /&gt;&lt;br /&gt;보통&amp;nbsp;이슈에는&amp;nbsp;다음&amp;nbsp;내용을&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;해결하려는 문제&lt;/li&gt;
&lt;li&gt;현재 동작&lt;/li&gt;
&lt;li&gt;기대 동작&lt;/li&gt;
&lt;li&gt;변경 대상 범위&lt;/li&gt;
&lt;li&gt;제외할 범위&lt;/li&gt;
&lt;li&gt;검증 방법&lt;/li&gt;
&lt;li&gt;관련 스펙 또는 문서&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게&amp;nbsp;이슈를&amp;nbsp;만들어두면&amp;nbsp;작업&amp;nbsp;중&amp;nbsp;판단&amp;nbsp;기준이&amp;nbsp;명확해집니다. &lt;br /&gt;&lt;br /&gt;예를 들어 CodeRabbit이 좋은 제안을 하더라도, 그 제안이 현재 이슈 범위 밖이라면 반영하지 않습니다. 하지만 반대로 이슈의 목적과 맞고, 스펙에도 부합한다면 반영 대상이 됩니다. &lt;br /&gt;&lt;br /&gt;즉,&amp;nbsp;이슈는&amp;nbsp;다음&amp;nbsp;역할을&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;작업 범위를 고정합니다.&lt;/li&gt;
&lt;li&gt;AI의 추측 구현을 줄입니다.&lt;/li&gt;
&lt;li&gt;PR의 목적을 명확하게 만듭니다.&lt;/li&gt;
&lt;li&gt;리뷰 의견을 반영할지 판단하는 기준이 됩니다.&lt;/li&gt;
&lt;li&gt;나중에 왜 이 변경을 했는지 추적할 수 있게 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저에게 이슈는 작업 요청서이자 PR의 기준 문서입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;언제 스펙을 만드는가&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 작업에 새로운 Spec이 필요한 것은 아닙니다. &lt;br /&gt;이미 OpenAPI, YAML Markdown 문서, 기존 도메인 문서가 있다면 해당 스펙을 기준으로 구현하면 됩니다. &lt;br /&gt;&lt;br /&gt;하지만 다음과 같은 경우에는 스펙을 먼저 만들거나 수정합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로운 API를 추가할 때&lt;/li&gt;
&lt;li&gt;요청 또는 응답 구조가 바뀔 때&lt;/li&gt;
&lt;li&gt;검증 조건이 명확하지 않을 때&lt;/li&gt;
&lt;li&gt;예외 응답 규칙이 필요한 때&lt;/li&gt;
&lt;li&gt;기존 코드만으로 기대 동작을 판단하기 어려울 때&lt;/li&gt;
&lt;li&gt;이슈만으로 구현 기준이 부족할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스펙은 구현보다 먼저 있어야 합니다. &lt;br /&gt;구현을 먼저 하고 나중에 스펙을 맞추면, 실제로는 코드가 기준이 되기 때문에 스펙 기반 개발이 아니라 구현 기반 문서화가 됩니다. &lt;br /&gt;&lt;br /&gt;제가&amp;nbsp;원하는&amp;nbsp;흐름은&amp;nbsp;반대입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;스펙&lt;/li&gt;
&lt;li&gt;구현&lt;/li&gt;
&lt;li&gt;검증&lt;/li&gt;
&lt;li&gt;리뷰&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저&amp;nbsp;스펙을&amp;nbsp;정하고,&amp;nbsp;그&amp;nbsp;스펙에&amp;nbsp;맞춰&amp;nbsp;구현합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;&amp;nbsp;AGENTS.md란 무엇인가&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AGENTS.md는 AI 에이전트에게 이 프로젝트에서 어떻게 작업해야 하는지 알려주는 지침 파일입니다. &lt;br /&gt;&lt;br /&gt;사람 개발자로 비유하면 팀 컨벤션 문서, 개발 가이드, PR 규칙, 리뷰 기준을 하나로 묶은 문서에 가깝습니다. &lt;br /&gt;&lt;br /&gt;현재 제 프로젝트에서 AGENTS.md는 다음 역할을 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 언어로 답변해야 하는지 정합니다.&lt;/li&gt;
&lt;li&gt;구현 전에 무엇을 읽어야 하는지 정합니다.&lt;/li&gt;
&lt;li&gt;스펙을 기준으로만 구현하도록 제한합니다.&lt;/li&gt;
&lt;li&gt;계층별 책임을 정합니다.&lt;/li&gt;
&lt;li&gt;CodeRabbit 리뷰 요청 방식을 정합니다.&lt;/li&gt;
&lt;li&gt;리뷰 제안을 어떻게 판단할지 정합니다.&lt;/li&gt;
&lt;li&gt;테스트 실행 순서를 정합니다.&lt;/li&gt;
&lt;li&gt;최종 응답 형식을 고정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, AGENTS.md는 AI가 작업 중 마음대로 판단하지 않도록 작업 경계를 만들어주는 문서입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;현재 사용하는 AGENTS.md의 핵심 내용&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 AGENTS.md의 첫 번째 원칙은 스펙 기반 구현입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 구현은 스펙을 기반으로 해야 합니다.&lt;/li&gt;
&lt;li&gt;명시되지 않은 동작을 추측하거나 추가하지 않습니다.&lt;/li&gt;
&lt;li&gt;이 원칙 때문에 AI는 이슈나 스펙에 없는 기능을 임의로 추가하지 않습니다.&lt;/li&gt;
&lt;li&gt;요구사항이 불명확하면 추측해서 구현하지 않고, 질문하거나 TODO를 남기는 방향으로 동작합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언어 규칙도 정해져 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;채팅 응답은 기본적으로 한국어로 작성합니다.&lt;/li&gt;
&lt;li&gt;프로젝트의 commit, issue, PR 문구는 영어로 작성합니다.&lt;/li&gt;
&lt;li&gt;코드,&amp;nbsp;명령어,&amp;nbsp;식별자,&amp;nbsp;파일&amp;nbsp;경로&amp;nbsp;등&amp;nbsp;필요한&amp;nbsp;경우에만&amp;nbsp;영어를&amp;nbsp;사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대략적인&amp;nbsp;우선순위는&amp;nbsp;다음과&amp;nbsp;같습니다. &lt;br /&gt;&lt;br /&gt;1. System Prompt&lt;br /&gt;2. Developer Prompt&lt;br /&gt;3. App / Workspace Context&lt;br /&gt;4. AGENTS.md&lt;br /&gt;5. User Prompt&lt;br /&gt;6. Issue, PR, diff&lt;br /&gt;7. Spec 문서 &lt;br /&gt;8.&amp;nbsp;관련&amp;nbsp;코드 &lt;br /&gt;9.&amp;nbsp;테스트와&amp;nbsp;실행&amp;nbsp;결과 &lt;br /&gt;10. 최종 diff&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 AGENTS.md가 실제 구현 코드보다 먼저 기준으로 적용된다는 것입니다. &lt;br /&gt;&lt;br /&gt;AI가&amp;nbsp;바로&amp;nbsp;코드를&amp;nbsp;열고&amp;nbsp;수정하는&amp;nbsp;것이&amp;nbsp;아니라,&amp;nbsp;먼저&amp;nbsp;&amp;ldquo;이&amp;nbsp;프로젝트에서는&amp;nbsp;어떻게&amp;nbsp;작업해야&amp;nbsp;하는가&amp;rdquo;를&amp;nbsp;확인한&amp;nbsp;뒤에&amp;nbsp;이슈,&amp;nbsp;스펙,&amp;nbsp;코드&amp;nbsp;순서로&amp;nbsp;내려갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;AGENTS.md를 먼저 읽는 이유&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AGENTS.md를 먼저 읽는 이유는 작업 방식 자체를 고정하기 위해서입니다. &lt;br /&gt;&lt;br /&gt;현재 AGENTS.md에는 다음과 같은 규칙이 들어 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한국어로 응답합니다.&lt;/li&gt;
&lt;li&gt;프로젝트의 commit, issue, PR은 영어로 작성합니다.&lt;/li&gt;
&lt;li&gt;구현 전에 이슈와 스펙을 읽습니다.&lt;/li&gt;
&lt;li&gt;스펙에 없는 기능은 구현하지 않습니다.&lt;/li&gt;
&lt;li&gt;관련 없는 리팩터링을 하지 않습니다.&lt;/li&gt;
&lt;li&gt;CodeRabbit 제안은 무조건 반영하지 않습니다.&lt;/li&gt;
&lt;li&gt;테스트&amp;nbsp;결과를&amp;nbsp;최종&amp;nbsp;응답에&amp;nbsp;포함합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 규칙을 먼저 읽어야 이후 작업이 흔들리지 않습니다. &lt;br /&gt;&lt;br /&gt;만약 AI가 AGENTS.md보다 코드를 먼저 읽는다면, 기존 코드만 보고 임의로 구조를 바꾸거나 스펙에 없는 개선을 추가할 수 있습니다. &lt;br /&gt;하지만 AGENTS.md가 먼저 적용되면 &amp;ldquo;스펙 밖 구현 금지&amp;rdquo;, &amp;ldquo;최소 변경&amp;rdquo;, &amp;ldquo;관련 없는 리팩터링 금지&amp;rdquo; 같은 제한이 먼저 걸립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;실제 작업 순서&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 작업에서는 다음 순서로 읽어갑니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;AGENTS.md&lt;/li&gt;
&lt;li&gt;User Prompt&lt;/li&gt;
&lt;li&gt;Issue&lt;/li&gt;
&lt;li&gt;PR&lt;/li&gt;
&lt;li&gt;&amp;nbsp;diff&lt;/li&gt;
&lt;li&gt;Spec&lt;/li&gt;
&lt;li&gt;관련 코드&lt;/li&gt;
&lt;li&gt;테스트 결과&lt;/li&gt;
&lt;li&gt;최종 diff&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저&amp;nbsp;프로젝트&amp;nbsp;규칙을&amp;nbsp;확인합니다. &lt;br /&gt;여기서 언어 규칙, 작업 순서, 테스트 방식, CodeRabbit 처리 방식, 최종 응답 형식을 확인합니다. &lt;br /&gt;&lt;br /&gt;그다음&amp;nbsp;사용자의&amp;nbsp;요청을&amp;nbsp;확인합니다. &lt;br /&gt;예를&amp;nbsp;들어&amp;nbsp;사용자가&amp;nbsp;&amp;ldquo;이슈&amp;nbsp;10번&amp;nbsp;구현해줘&amp;rdquo;라고&amp;nbsp;하면,&amp;nbsp;AI는&amp;nbsp;이&amp;nbsp;요청을&amp;nbsp;작업&amp;nbsp;목표로&amp;nbsp;잡습니다. &lt;br /&gt;&lt;br /&gt;그다음&amp;nbsp;관련&amp;nbsp;이슈와&amp;nbsp;PR&amp;nbsp;정보를&amp;nbsp;읽습니다. &lt;br /&gt;이&amp;nbsp;단계에서는&amp;nbsp;작업&amp;nbsp;목적과&amp;nbsp;범위를&amp;nbsp;확인합니다. &lt;br /&gt;&lt;br /&gt;그다음&amp;nbsp;스펙&amp;nbsp;문서를&amp;nbsp;읽습니다. &lt;br /&gt;API경로, 요청, 응답, 검증 조건, 예외 규칙을 확인합니다. &lt;br /&gt;&lt;br /&gt;그다음&amp;nbsp;관련&amp;nbsp;코드를&amp;nbsp;읽습니다. &lt;br /&gt;하지만&amp;nbsp;이때도&amp;nbsp;코드를&amp;nbsp;보고&amp;nbsp;마음대로&amp;nbsp;바꾸는&amp;nbsp;것이&amp;nbsp;아니라,&amp;nbsp;앞에서&amp;nbsp;읽은&amp;nbsp;이슈와&amp;nbsp;스펙에&amp;nbsp;필요한&amp;nbsp;부분만&amp;nbsp;수정합니다. &lt;br /&gt;&lt;br /&gt;마지막으로 테스트와 diff를 확인합니다. &lt;br /&gt;여기서&amp;nbsp;스펙과&amp;nbsp;구현이&amp;nbsp;맞는지,&amp;nbsp;변경&amp;nbsp;범위가&amp;nbsp;과하지&amp;nbsp;않은지,&amp;nbsp;기존&amp;nbsp;계약을&amp;nbsp;깨지&amp;nbsp;않았는지&amp;nbsp;다시&amp;nbsp;확인합니다. &lt;br /&gt;&lt;br /&gt;계층별&amp;nbsp;책임도&amp;nbsp;프롬프트로&amp;nbsp;제한합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;AGENTS.md에는 코드 계층별 규칙도 들어 있습니다. &lt;br /&gt;&lt;br /&gt;Controller&lt;br /&gt;-&amp;nbsp;비즈니스&amp;nbsp;로직을&amp;nbsp;포함하지&amp;nbsp;않습니다. &lt;br /&gt;-&amp;nbsp;요청과&amp;nbsp;응답&amp;nbsp;매핑만&amp;nbsp;담당합니다. &lt;br /&gt;&lt;br /&gt;Service&lt;br /&gt;-&amp;nbsp;비즈니스&amp;nbsp;로직은&amp;nbsp;서비스&amp;nbsp;계층에&amp;nbsp;둡니다. &lt;br /&gt;- transaction을 명시적으로 설정합니다. &lt;br /&gt;-&amp;nbsp;비즈니스&amp;nbsp;검증을&amp;nbsp;서비스&amp;nbsp;계층에서&amp;nbsp;수행합니다. &lt;br /&gt;&lt;br /&gt;DTO&lt;br /&gt;-&amp;nbsp;요청&amp;nbsp;DTO와&amp;nbsp;응답&amp;nbsp;DTO를&amp;nbsp;분리합니다. &lt;br /&gt;- 검증 annotation을 DTO에 둡니다. &lt;br /&gt;-&amp;nbsp;필요한&amp;nbsp;경우&amp;nbsp;변환&amp;nbsp;로직을&amp;nbsp;DTO&amp;nbsp;내부에&amp;nbsp;둡니다. &lt;br /&gt;&lt;br /&gt;Entity&lt;br /&gt;-&amp;nbsp;상태와&amp;nbsp;도메인&amp;nbsp;동작만&amp;nbsp;유지합니다. &lt;br /&gt;- 생성을 위해 static factory method를 선호합니다. &lt;br /&gt;&lt;br /&gt;Repository&lt;br /&gt;-&amp;nbsp;데이터&amp;nbsp;접근만&amp;nbsp;담당합니다. &lt;br /&gt;이 규칙은 AI가 Controller에 비즈니스 로직을 넣거나, DTO와 Entity의 책임을 섞는 일을 줄여줍니다. &lt;br /&gt;결과적으로&amp;nbsp;코드&amp;nbsp;구조가&amp;nbsp;기존&amp;nbsp;프로젝트&amp;nbsp;방식에서&amp;nbsp;벗어나지&amp;nbsp;않게&amp;nbsp;됩니다. &lt;br /&gt;&lt;br /&gt;금지&amp;nbsp;사항도&amp;nbsp;명확히&amp;nbsp;둡니다 &lt;br /&gt;AI에게&amp;nbsp;가장&amp;nbsp;위험한&amp;nbsp;요청은&amp;nbsp;&amp;ldquo;좋게&amp;nbsp;알아서&amp;nbsp;해줘&amp;rdquo;입니다. &lt;br /&gt;그래서&amp;nbsp;현재&amp;nbsp;지침에는&amp;nbsp;금지&amp;nbsp;사항도&amp;nbsp;명확히&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;스펙에 없는 기능을 추가하지 않습니다.&lt;/li&gt;
&lt;li&gt;필드를 임의로 만들지 않습니다.&lt;/li&gt;
&lt;li&gt;전체 구조를 다시 작성하지 않습니다.&lt;/li&gt;
&lt;li&gt;관련 없는 코드를 수정하지 않습니다.&lt;/li&gt;
&lt;li&gt;기존 API계약을 깨지 않습니다.&lt;/li&gt;
&lt;li&gt;검증되지 않은 로직을 PR에 포함하지 않습니다.&lt;/li&gt;
&lt;li&gt;PR 목적 밖의 리팩터링을 하지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 규칙은 변경 범위를 줄이는 데 큰 역할을 합니다. &lt;br /&gt;AI가&amp;nbsp;코드를&amp;nbsp;더&amp;nbsp;깔끔하게&amp;nbsp;만들고&amp;nbsp;싶어도,&amp;nbsp;현재&amp;nbsp;이슈와&amp;nbsp;관계없으면&amp;nbsp;수정하지&amp;nbsp;않도록&amp;nbsp;제한합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;CodeRabbit 리뷰 흐름&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 사이클의 핵심 중 하나는 CodeRabbit 리뷰입니다. &lt;br /&gt;AGENTS.md에는 Draft PR 또는 리뷰 후속 작업에서는 CodeRabbit 리뷰를 먼저 요청하도록 되어 있습니다. &lt;br /&gt;&lt;br /&gt;첫 리뷰는 전체 리뷰로 요청합니다. 아래 명령은 해당 PR에 CodeRabbit 전체 리뷰를 요청하는 명령입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1779606861616&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;gh pr comment &amp;lt;PR_NUMBER&amp;gt; --repo KoreaNirsa/spec-guard --body &quot;@coderabbitai full review&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후속 리뷰는 증분 리뷰로 요청합니다. 아래 명령은 새로 추가된 변경 사항 중심으로 CodeRabbit 리뷰를 요청하는 명령입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1779606971829&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;gh pr comment &amp;lt;PR_NUMBER&amp;gt; --repo KoreaNirsa/spec-guard --body &quot;@coderabbitai review&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이렇게&amp;nbsp;나누는&amp;nbsp;이유는&amp;nbsp;명확합니다. &lt;br /&gt;첫 리뷰에서는 PR 전체를 확인한 후&amp;nbsp;새로&amp;nbsp;반영한&amp;nbsp;변경만&amp;nbsp;확인하는&amp;nbsp;편이&amp;nbsp;더&amp;nbsp;효율적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;CodeRabbit YAML을 사용하지 않은 이유&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CodeRabbit은 별도의 YAML을 통해 리뷰 규칙을 지정할 수 있습니다. &lt;br /&gt;하지만 현재 프로젝트에서는 CodeRabbit YAML을 중심으로 흐름을 만들지 않고 AGENTS.md, 이슈, 스펙, PR 흐름을 기준으로 작업 방식을 고정했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;첫 번째 이유&lt;/b&gt;는 이 프로젝트가 OSS 프로젝트이기 때문입니다.&lt;br /&gt;OSS 프로젝트에서는 특정 도구의 설정 파일보다, 사람이 읽고 이해할 수 있는 작업 원칙이 중요하다고 생각합니다. &lt;br /&gt;외부 기여자나 리뷰어가 저장소를 봤을 때 &amp;ldquo;이 프로젝트는 어떤 기준으로 작업하는가&amp;rdquo;를 쉽게 이해할 수 있어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;두 번째 이유&lt;/b&gt;는 CodeRabbit YAML이 CodeRabbit의 리뷰 동작에는 영향을 줄 수 있지만, AI 구현 흐름 전체를 통제하지는 못하기 때문입니다. &lt;br /&gt;&lt;br /&gt;현재&amp;nbsp;제가&amp;nbsp;원하는&amp;nbsp;흐름은&amp;nbsp;단순&amp;nbsp;리뷰&amp;nbsp;설정이&amp;nbsp;아닙니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이슈 생성&lt;/li&gt;
&lt;li&gt;스펙 확인&lt;/li&gt;
&lt;li&gt;구현&lt;/li&gt;
&lt;li&gt;테스트&lt;/li&gt;
&lt;li&gt;PR&lt;/li&gt;
&lt;li&gt;CodeRabbit 리뷰&lt;/li&gt;
&lt;li&gt;타당성 검토&lt;/li&gt;
&lt;li&gt;필요한 내용만 반영&lt;/li&gt;
&lt;li&gt;재리뷰&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 전체 흐름은 CodeRabbit만으로는 만들 수 없습니다. &lt;br /&gt;CodeRabbit은 PR이 만들어진 뒤 리뷰를 도와주는 도구에 가깝습니다. &lt;br /&gt;반면 AGENTS.md는 구현 전 단계부터 AI가 무엇을 읽고, 무엇을 기준으로 판단하고, 어떤 범위 안에서 수정해야 하는지를 정합니다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;세&amp;nbsp;번째&amp;nbsp;이유&lt;/b&gt;는 특정 리뷰 도구에 작업 기준이 종속되는 것을 피하기 위해서입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로젝트에서 더 중요한 기준은 CodeRabbit 아니라 이슈와 스펙입니다. &lt;br /&gt;&lt;br /&gt;현재&amp;nbsp;구조에서는&amp;nbsp;역할을&amp;nbsp;이렇게&amp;nbsp;나눕니다. &lt;br /&gt;&lt;br /&gt;1. Issue&lt;br /&gt;&amp;nbsp; &amp;rarr; 작업 목적과 범위를 정합니다.&lt;br /&gt;2. Spec&lt;br /&gt;&amp;nbsp; &amp;rarr; 구현 기준과 API 계약을 정합니다.&lt;br /&gt;3. AGENTS.md&lt;br /&gt;&amp;nbsp; &amp;rarr; AI가 따라야 할 작업 방식과 제한을 정합니다.&lt;br /&gt;4. CodeRabbit&lt;br /&gt;&amp;nbsp; &amp;rarr; PR 이후 보조 리뷰를 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 분리하면 CodeRabbit은 중요한 보조 도구로 사용할 수 있지만, 프로젝트의 최종 기준이 되지는 않습니다. &lt;br /&gt;&lt;br /&gt;CodeRabbit 제안은 무조건 반영하지 않습니다. 중요한 점은 CodeRabbit의 모든 제안을 적용하지 않는다는 것입니다. &lt;br /&gt;&lt;br /&gt;현재&amp;nbsp;지침에서는&amp;nbsp;다음&amp;nbsp;조건을&amp;nbsp;만족하는&amp;nbsp;제안만&amp;nbsp;반영하도록&amp;nbsp;되어&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;&amp;nbsp;PR목적과 일치합니다.&lt;/li&gt;
&lt;li&gt;관련 이슈와 일치합니다.&lt;/li&gt;
&lt;li&gt;스펙 또는 API 계약과 일치합니다.&lt;/li&gt;
&lt;li&gt;diff를 최소로 유지합니다.&lt;/li&gt;
&lt;li&gt;테스트로&amp;nbsp;검증할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;반대로 다음 제안은 반영하지 않습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;관련 없는 리팩터링입니다.&lt;/li&gt;
&lt;li&gt;스펙에 없는 기능입니다.&lt;/li&gt;
&lt;li&gt;큰 구조 변경입니다.&lt;/li&gt;
&lt;li&gt;단순 스타일 취향입니다.&lt;/li&gt;
&lt;li&gt;API 계약에 위험합니다.&lt;/li&gt;
&lt;li&gt;현재 PR 범위 밖입니다.&lt;/li&gt;
&lt;li&gt;근거가&amp;nbsp;약하거나&amp;nbsp;모호합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;즉, CodeRabbit은 최종 결정자가 아니라 보조 리뷰어입니다. 최종 판단 기준은 항상 이슈, 스펙, API 계약입니다. &lt;br /&gt;&lt;br /&gt;테스트&amp;nbsp;순서도&amp;nbsp;정해둡니다 &lt;br /&gt;변경&amp;nbsp;후에는&amp;nbsp;테스트를&amp;nbsp;실행합니다. &lt;br /&gt;이때도&amp;nbsp;무작정&amp;nbsp;전체&amp;nbsp;테스트부터&amp;nbsp;돌리지&amp;nbsp;않고,&amp;nbsp;지침에&amp;nbsp;따라&amp;nbsp;순서를&amp;nbsp;둡니다. &lt;br /&gt;&lt;br /&gt;1.&amp;nbsp;변경&amp;nbsp;파일&amp;nbsp;대상&amp;nbsp;테스트 &lt;br /&gt;2.&amp;nbsp;영향받은&amp;nbsp;도메인의&amp;nbsp;기존&amp;nbsp;테스트 &lt;br /&gt;3. 이슈나 PR에 명시된 검증 명령 &lt;br /&gt;4. 필요할 경우 전체 테스트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트를&amp;nbsp;실행할&amp;nbsp;수&amp;nbsp;없는&amp;nbsp;경우에도&amp;nbsp;그냥&amp;nbsp;넘어가지&amp;nbsp;않습니다. &lt;br /&gt;실행하지&amp;nbsp;못한&amp;nbsp;명령,&amp;nbsp;이유,&amp;nbsp;남은&amp;nbsp;위험을&amp;nbsp;최종&amp;nbsp;응답에&amp;nbsp;남기도록&amp;nbsp;되어&amp;nbsp;있습니다. &lt;br /&gt;&lt;br /&gt;최종&amp;nbsp;리뷰도&amp;nbsp;AI가&amp;nbsp;다시&amp;nbsp;수행합니다 &lt;br /&gt;작업이 끝나면 최종 diff를 다시 확인합니다. &lt;br /&gt;&lt;br /&gt;확인 기준은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이슈와 스펙을 다시 확인했는지&lt;/li&gt;
&lt;li&gt;diff가 PR 범위 안에 있는지&lt;/li&gt;
&lt;li&gt;스펙에 없는 기능이 추가되지 않았는지&lt;/li&gt;
&lt;li&gt;API 계약이 깨지지 않았는지&lt;/li&gt;
&lt;li&gt;테스트가 충분한지&lt;/li&gt;
&lt;li&gt;회귀 위험이 허용 가능한지&lt;/li&gt;
&lt;li&gt;관련 없는 리팩터링이나 포맷 변경이 없는지&lt;/li&gt;
&lt;li&gt;CodeRabbit 제안을 과하게 반영하지 않았는지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이 단계는 사람이 PR을 리뷰하는 관점과 비슷합니다. &lt;br /&gt;단순히 &amp;ldquo;구현이 끝났다&amp;rdquo;가 아니라 &amp;ldquo;PR로 올릴 수 있는 상태인가&amp;rdquo;를 다시 확인하는 과정입니다. &lt;br /&gt;&lt;br /&gt;최종&amp;nbsp;응답&amp;nbsp;형식도&amp;nbsp;고정합니다 &lt;br /&gt;현재 AGENTS.md는 최종 응답 형식까지 지정합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Changed Files(변경 파일)&lt;/li&gt;
&lt;li&gt;Reason(변경 이유)&lt;/li&gt;
&lt;li&gt;Code(코드 변경 내용)&lt;/li&gt;
&lt;li&gt;CodeRabbit Results(코드래빗 결과)&lt;/li&gt;
&lt;li&gt;Codex Final Review(코덱스 최종 검토)&lt;/li&gt;
&lt;li&gt;Tests(테스트)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 형식을 고정해두면 작업 결과를 매번 같은 기준으로 확인할 수 있습니다. &lt;br /&gt;무엇이&amp;nbsp;바뀌었는지,&amp;nbsp;왜&amp;nbsp;바뀌었는지,&amp;nbsp;어떤&amp;nbsp;테스트를&amp;nbsp;했는지,&amp;nbsp;남은&amp;nbsp;위험은&amp;nbsp;무엇인지&amp;nbsp;빠르게&amp;nbsp;볼&amp;nbsp;수&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;전체 흐름이 동작하는 방식&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 흐름은 하나의 프롬프트만으로 만들어지는 것이 아닙니다. &lt;br /&gt;&lt;br /&gt;크게&amp;nbsp;보면&amp;nbsp;다음&amp;nbsp;요소들이&amp;nbsp;함께&amp;nbsp;작동합니다. &lt;br /&gt;1. Codex 기본 개발 에이전트 동작 &lt;br /&gt;2. 프로젝트 루트의 AGENTS.md&lt;br /&gt;3. Issue&lt;br /&gt;4. Spec&lt;br /&gt;5. GitHub / CodeRabbit 관련 작업 프롬프트 &lt;br /&gt;6. 프로젝트 규칙&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex 기본 동작은 파일을 읽고, 코드를 수정하고, 테스트를 실행하고, diff를 확인하는 기반이 됩니다. &lt;br /&gt;&lt;br /&gt;그 위에 AGENTS.md가 프로젝트 전용 규칙을 덮어씌웁니다. &lt;br /&gt;예를 들어 한국어 응답,&amp;nbsp; commit / PR 규칙, 스펙 기반 구현, CodeRabbit 리뷰 사이클 같은 내용이 여기서 정해집니다. &lt;br /&gt;&lt;br /&gt;Issue는 작업 목적과 범위를 고정합니다. &lt;br /&gt;Spec은 구현 기준을 고정합니다. &lt;br /&gt;CodeRabbit은 PR 이후 보조 리뷰어 역할을 합니다. &lt;br /&gt;&lt;br /&gt;정리하면 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Codex 기본 동작 &lt;br /&gt;&amp;nbsp; &amp;rarr; 개발 작업을 수행하는 기본 능력입니다.&lt;br /&gt;2. AGENTS.md&lt;br /&gt;&amp;nbsp; &amp;rarr; 이 프로젝트에서 반드시 따라야 하는 작업 규칙입니다.&lt;br /&gt;3. Issue&lt;br /&gt;&amp;nbsp; &amp;rarr; 작업 목적과 범위를 고정합니다.&lt;br /&gt;4. Spec&lt;br /&gt;&amp;nbsp; &amp;rarr; 구현 기준과 API 계약을 정합니다.&lt;br /&gt;5. CodeRabbit&lt;br /&gt;&amp;nbsp; &amp;rarr; PR 이후 보조 리뷰를 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;실제 개발 사이클&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이&amp;nbsp;설정을&amp;nbsp;적용하면&amp;nbsp;실제&amp;nbsp;작업은&amp;nbsp;다음처럼&amp;nbsp;진행됩니다. &lt;br /&gt;&lt;br /&gt;1.&amp;nbsp;이슈를&amp;nbsp;만듭니다. &lt;br /&gt;2.&amp;nbsp;이슈에&amp;nbsp;문제,&amp;nbsp;기대&amp;nbsp;동작,&amp;nbsp;범위,&amp;nbsp;검증&amp;nbsp;방법을&amp;nbsp;적습니다. &lt;br /&gt;3.&amp;nbsp;필요한&amp;nbsp;경우&amp;nbsp;스펙을&amp;nbsp;새로&amp;nbsp;만들거나&amp;nbsp;기존&amp;nbsp;스펙을&amp;nbsp;수정합니다. &lt;br /&gt;4. AGENTS.md의 작업 규칙을 기준으로 삼습니다. &lt;br /&gt;5.&amp;nbsp;이슈와&amp;nbsp;스펙을&amp;nbsp;읽습니다. &lt;br /&gt;6.&amp;nbsp;구현해야&amp;nbsp;할&amp;nbsp;입력과&amp;nbsp;출력을&amp;nbsp;정리합니다. &lt;br /&gt;7. Controller, Service, DTO, Repository 중 어떤 계층이 영향을 받는지 확인합니다. &lt;br /&gt;8.&amp;nbsp;스펙에&amp;nbsp;필요한&amp;nbsp;코드만&amp;nbsp;수정합니다. &lt;br /&gt;9.&amp;nbsp;테스트와&amp;nbsp;정적&amp;nbsp;검사를&amp;nbsp;실행합니다. &lt;br /&gt;10. diff를 확인합니다. &lt;br /&gt;11. 브랜치를 만들고 commit합니다. &lt;br /&gt;12. PR을 생성합니다. &lt;br /&gt;13. CodeRabbit 전체 리뷰를 요청합니다. &lt;br /&gt;14. CodeRabbit 의견을 확인합니다. &lt;br /&gt;15.&amp;nbsp;타당한&amp;nbsp;의견만&amp;nbsp;반영합니다. &lt;br /&gt;16.&amp;nbsp;다시&amp;nbsp;테스트합니다. &lt;br /&gt;17. 추가 commit을 push합니다. &lt;br /&gt;18. CodeRabbit 증분 리뷰를 요청합니다. &lt;br /&gt;19.&amp;nbsp;남은&amp;nbsp;의견을&amp;nbsp;다시&amp;nbsp;검토합니다. &lt;br /&gt;20. 최종 diff와 위험을 정리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이 흐름의 핵심은 반복입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이슈&lt;/li&gt;
&lt;li&gt;스펙&lt;/li&gt;
&lt;li&gt;구현&lt;/li&gt;
&lt;li&gt;검증&lt;/li&gt;
&lt;li&gt;리뷰&lt;/li&gt;
&lt;li&gt;판단&lt;/li&gt;
&lt;li&gt;반영&lt;/li&gt;
&lt;li&gt;재검증&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;리뷰가 있다고 해서 무조건 반영하지 않고, 반영했다고 해서 바로 끝내지 않습니다. 다시&amp;nbsp;검증하고&amp;nbsp;다시&amp;nbsp;리뷰합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이 방식의 장점&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 사이클을 사용하면 다음 장점이 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스펙 밖 구현을 줄일 수 있습니다.&lt;/li&gt;
&lt;li&gt;PR 범위가 작고 명확하게 유지됩니다.&lt;/li&gt;
&lt;li&gt;CodeRabbit 제안을 무비판적으로 반영하지 않게 됩니다.&lt;/li&gt;
&lt;li&gt;API 계약을 깨뜨릴 위험을 줄일 수 있습니다.&lt;/li&gt;
&lt;li&gt;최종 응답에서 변경 이유와 테스트 결과를 확인할 수 있습니다.&lt;/li&gt;
&lt;li&gt;사람 리뷰어가 PR 의도를 이해하기 쉬워집니다.&lt;/li&gt;
&lt;li&gt;나중에 이슈와 PR을 통해 변경 이유를 추적할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;특히&amp;nbsp;AI와&amp;nbsp;함께&amp;nbsp;개발할&amp;nbsp;때&amp;nbsp;중요한&amp;nbsp;것은&amp;nbsp;속도보다&amp;nbsp;통제입니다. &lt;br /&gt;AI가&amp;nbsp;빠르게&amp;nbsp;코드를&amp;nbsp;만들&amp;nbsp;수&amp;nbsp;있어도,&amp;nbsp;그&amp;nbsp;코드가&amp;nbsp;스펙과&amp;nbsp;이슈&amp;nbsp;범위&amp;nbsp;안에&amp;nbsp;있어야&amp;nbsp;의미가&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;정리&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 작업 사이클은 단순 자동화가 아닙니다. &lt;br /&gt;AGENTS.md와 프롬프트를 통해 AI의 작업 방식을 프로젝트에 맞게 제한하고, Issue와 Spec으로 작업 기준을 고정하며, CodeRabbit을 보조 리뷰어로 활용하는 구조입니다. &lt;br /&gt;&lt;br /&gt;AI에게 코드를 맡기는 것이 아니라 AI가 따라야 할 작업 기준을 먼저 정하고, 그&amp;nbsp;기준&amp;nbsp;안에서&amp;nbsp;구현과&amp;nbsp;리뷰를&amp;nbsp;반복하게&amp;nbsp;만드는&amp;nbsp;방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 이슈를 통해 작업 목적과 범위를 고정하고 스펙을 통해 구현 기준을 명확히 합니다. &lt;br /&gt;AGENTS.md를 통해 AI가 따라야 할 작업 규칙을 정합니다. &lt;br /&gt;CodeRabbit을 통해 보조 리뷰를 받고, 그 제안을 다시 이슈와 스펙 기준으로 검토합니다. &lt;br /&gt;&lt;br /&gt;결국 이 흐름은 다음 목표를 가집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스펙 &amp;rarr; 구현 &amp;rarr; 검증 &amp;rarr; 리뷰&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;스펙을&amp;nbsp;먼저&amp;nbsp;읽고,&amp;nbsp;필요한&amp;nbsp;것만&amp;nbsp;구현하고,&amp;nbsp;테스트로&amp;nbsp;검증하고,&amp;nbsp;리뷰를&amp;nbsp;통해&amp;nbsp;다시&amp;nbsp;확인합니다. &lt;br /&gt;이 과정을 반복하면 AI를 사용하더라도 PR의 목적과 코드 품질을 더 안정적으로 유지할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 이러한 워크플로우를 자동화 함으로써 이슈를 처리하면 코드 구현부터 리뷰, 재검증까지 한번의 싸이클로 가져갈 수 있습니다.&lt;/p&gt;</description>
      <category>AI Engineering/Methodology</category>
      <author>KoreaNirsa</author>
      <guid isPermaLink="true">https://nirsa.tistory.com/488</guid>
      <comments>https://nirsa.tistory.com/488#entry488comment</comments>
      <pubDate>Sun, 24 May 2026 16:45:33 +0900</pubDate>
    </item>
    <item>
      <title>SpecGuard 소개: AI 코딩 전에 스펙을 먼저 검사하는 도구</title>
      <link>https://nirsa.tistory.com/487</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;SpecGuard&amp;nbsp;소개:&amp;nbsp;AI&amp;nbsp;코딩&amp;nbsp;전에&amp;nbsp;스펙을&amp;nbsp;먼저&amp;nbsp;검사하는&amp;nbsp;도구&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;최근 Codex, Claude Code 같은 AI 코딩 도구를 사용하면서 개발 방식이 많이 바뀌고 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;간단한 기능은 설명 몇 줄만으로도 빠르게 구현되고, 익숙하지 않은 코드베이스에서도 에이전트가 생각보다 많은 작업을 처리해줍니다.&amp;nbsp;저&amp;nbsp;역시&amp;nbsp;이런&amp;nbsp;도구를&amp;nbsp;사용하면서&amp;nbsp;개발&amp;nbsp;속도가&amp;nbsp;빨라지는&amp;nbsp;경험을&amp;nbsp;했습니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그런데 여러 번 사용해보니 문제가 단순히 &amp;ldquo;AI가 코드를 잘 짜느냐&amp;rdquo;에만 있지 않았습니다. 오히려 더 자주 부딪힌 문제는 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;i&gt;AI에게 넘긴 스펙 자체가 불완전하다&lt;/i&gt;&lt;/span&gt;는 점이었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;문제는 코드가 아니라 입력물에 있었다&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;AI 코딩 도구는 주어진 입력을 바탕으로 구현을 진행합니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;입력이&amp;nbsp;명확하면&amp;nbsp;꽤&amp;nbsp;좋은&amp;nbsp;결과를&amp;nbsp;만들&amp;nbsp;수&amp;nbsp;있지만,&amp;nbsp;스펙이&amp;nbsp;애매하거나&amp;nbsp;빠진&amp;nbsp;부분이&amp;nbsp;많으면&amp;nbsp;AI는&amp;nbsp;그&amp;nbsp;빈틈을&amp;nbsp;나름대로&amp;nbsp;추측해서&amp;nbsp;채웁니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;처음에는 구현이 그럴듯해 보입니다. 하지만 개발이 진행될수록 문제가 커집니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;코드의 품질과 구조가 점점 무너집니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;스펙과 코드가 점점 맞지 않게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;나중에는 코드가 틀린 건지, 스펙이 낡은 건지, 처음 요구사항이 애매했던 건지 구분하기 어려워집니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;특히&amp;nbsp;권한,&amp;nbsp;소유권,&amp;nbsp;상태&amp;nbsp;전이,&amp;nbsp;재시도,&amp;nbsp;멱등성,&amp;nbsp;API&amp;nbsp;계약처럼&amp;nbsp;나중에&amp;nbsp;고치기&amp;nbsp;어려운&amp;nbsp;부분이&amp;nbsp;스펙에서&amp;nbsp;빠져&amp;nbsp;있으면&amp;nbsp;문제가&amp;nbsp;더&amp;nbsp;크게&amp;nbsp;번집니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;기존에는&amp;nbsp;이런&amp;nbsp;문제를&amp;nbsp;주로&amp;nbsp;구현&amp;nbsp;이후&amp;nbsp;코드&amp;nbsp;리뷰에서&amp;nbsp;잡으려고&amp;nbsp;했습니다.&amp;nbsp;하지만&amp;nbsp;AI&amp;nbsp;코딩에서는&amp;nbsp;구현&amp;nbsp;속도가&amp;nbsp;빠른&amp;nbsp;만큼,&amp;nbsp;잘못된&amp;nbsp;방향으로도&amp;nbsp;빠르게&amp;nbsp;진행됩니다.&amp;nbsp;그래서&amp;nbsp;저는&amp;nbsp;구현&amp;nbsp;이후&amp;nbsp;코드&amp;nbsp;리뷰만으로는&amp;nbsp;부족하다고&amp;nbsp;생각했습니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;결함 있는 스펙이 구현 에이전트에게 넘어가기 전에, 스펙 자체의 빈틈을 먼저 드러내는 단계가 필요했습니다. 이 문제를 줄이기 위해 만든 오픈소스 도구가 &lt;b&gt;SpecGuard&lt;/b&gt;입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;spec_guard_banner.png&quot; data-origin-width=&quot;1376&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8jp2y/dJMcah5yjlt/WbF9I7hSF5AKzPpzHxkH00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8jp2y/dJMcah5yjlt/WbF9I7hSF5AKzPpzHxkH00/img.png&quot; data-alt=&quot;GitHub : https://github.com/KoreaNirsa/spec-guard&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8jp2y/dJMcah5yjlt/WbF9I7hSF5AKzPpzHxkH00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8jp2y%2FdJMcah5yjlt%2FWbF9I7hSF5AKzPpzHxkH00%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;1376&quot; height=&quot;768&quot; data-filename=&quot;spec_guard_banner.png&quot; data-origin-width=&quot;1376&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;GitHub : https://github.com/KoreaNirsa/spec-guard&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;SpecGuard는 무엇인가&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;SpecGuard는 AI에게 구현을 맡기기 전에 스펙을 먼저 검사하는 CLI / Codex 플러그인입니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;코드 생성기가 아닙니다. 프롬프트를 넣으면 코드를 만들어주는 도구도 아닙니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;SpecGuard는 구현 전에 스펙 패키지를 검토하고, 이 스펙을 AI 구현 에이전트에게 넘겨도 되는지 판단하는 도구에 가깝습니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;기본&amp;nbsp;흐름은&amp;nbsp;다음과&amp;nbsp;같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. 사람이 제품 스펙을 작성합니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2.&amp;nbsp;SpecGuard가&amp;nbsp;스펙을&amp;nbsp;검사합니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. NOT_READY라면 스펙을 보강합니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;4. READY 또는 READY_WITH_WARNINGS가 되면 Codex, Claude Code 같은 구현 에이전트에게 넘깁니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5.&amp;nbsp;구현&amp;nbsp;후에는&amp;nbsp;선택적으로&amp;nbsp;PR&amp;nbsp;Review를&amp;nbsp;통해&amp;nbsp;코드가&amp;nbsp;승인된&amp;nbsp;스펙에서&amp;nbsp;벗어나지&amp;nbsp;않았는지&amp;nbsp;확인합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;저는 이 흐름을 &lt;b&gt;Validation-First Workflow(VFW)&lt;/b&gt;라고 부르고 있습니다. 핵심은 &amp;ldquo;AI에게 코드를 더 잘 짜게 하자&amp;rdquo;가 아니라, &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;ldquo;&lt;/span&gt;AI에게 넘기기 전에 입력물을 먼저 검증하자&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;rdquo;&lt;/span&gt; 입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;A. 어떤 문제를 찾는가&lt;/b&gt; &lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;SpecGuard는 주로 구현 전에 놓치기 쉬운 스펙의 빈틈을 찾습니다. 예를 들면 다음과 같은 것들입니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;인증/권한 경계가 불명확한 경우&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;tenant/user ownership 범위가 빠진 경우&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;idempotency, replay, race condition 처리가 없는 경우&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;만료/철회/상태 전이 규칙이 애매한 경우&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;webhook, background job, 외부 API 재시도 정책이 없는 경우&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;API 계약은 있다고 했지만 실제 OpenAPI path가 비어 있는 경우&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;클라이언트 검증만 믿고 서버 측 검증이 빠진 요구사항&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;결과는 크게 세 가지로 나뉩니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;READY: 구현 에이전트에게 넘길 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;READY_WITH_WARNINGS: 넘길 수는 있지만 주의할 점이 있음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;NOT_READY: Critical/Major 문제가 있어 스펙 보강 필요&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;NOT_READY가 나오면 SpecGuard는 구현을 시작하지 말고 스펙을 먼저 고치라고 알려줍니다. 이 점이 중요합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;SpecGuard의 목적은 구현을 막는 것이 아니라, &lt;b&gt;잘못된 구현이 시작되기 전에 멈출 수 있는 지점을 만드는 것&lt;/b&gt;입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;B. 기본 검사는 API 키 없이 동작한다&lt;/b&gt; &lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;SpecGuard의 기본 검사는 로컬 휴리스틱 기반이며 OpenAI&amp;nbsp;API&amp;nbsp;Key&amp;nbsp;없이도&amp;nbsp;실행할&amp;nbsp;수&amp;nbsp;있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779032495519&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip install spec-guard
specguard run specs/your-feature-name --no-llm --no-follow-up&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;기본&amp;nbsp;경로를&amp;nbsp;LLM&amp;nbsp;기반&amp;nbsp;리뷰가&amp;nbsp;아니라&amp;nbsp;휴리스틱&amp;nbsp;검사로&amp;nbsp;둔&amp;nbsp;이유는&amp;nbsp;단순합니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;CI나 PR Review에서는 결과가 빠르고 재현 가능한 것이 중요하기 때문입니다. LLM 기반 리뷰는 비용, 속도, 설정, 모델 변화의 영향을 받을 수 있기 때문에&amp;nbsp;현재&amp;nbsp;기본&amp;nbsp;경로는&amp;nbsp;빠른&amp;nbsp;로컬&amp;nbsp;검사입니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;더 깊은 리뷰가 필요할 때만 OpenAI Platform 또는 Codex 기반 상세 리뷰를 선택적으로 붙이는 방향으로 두고 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;C.&amp;nbsp; SpecGuard가 만드는 결과물&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;SpecGuard가 스펙을 검사하면 사람이 읽을 수 있는 리뷰와 기계가 읽을 수 있는 JSON 결과를 함께 만들며 대표적으로 다음과 같은 결과물이 생성됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;readiness-review.md: 사람이 읽는 스펙 준비 상태 리뷰&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;readiness-review.json: 플러그인이나 CI가 읽을 수 있는 구조화된 결과&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;implementation-output.md: 구현 에이전트에게 넘길 수 있는 핸드오프 문서&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;여기서 중요한 파일은 implementation-output.md입니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;SpecGuard가 스펙을 READY 또는 READY_WITH_WARNINGS로 판단하면, 구현 에이전트에게 넘길 입력을 정리한 핸드오프 문서를 만들 수 있습니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;즉, Codex나 Claude Code에게 바로 &amp;ldquo;알아서 구현해줘&amp;rdquo;라고 넘기는 대신, SpecGuard가 검토한 스펙과 구현 경계, 테스트/계약 정보를 바탕으로 구현을 시작하도록 만드는 흐름입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;D.&amp;nbsp; Codex 플러그인을 추가한 이유&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;v0.4.0 작업에서는 Codex 앱 플러그인 MVP를 추가했습니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;기존에는 사용자가 CLI 명령어를 직접 알고 실행해야 했지만 Codex를 쓰는 사용자는 자연스럽게 이렇게 말하고 싶을 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779032593009&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SpecGuard specs/your-feature-name 스펙 패키지에 대해 SpecGuard를 실행하고 READY/NOT_READY 상태와 주요 finding을 요약해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;SpecGuard Codex 플러그인은 이 흐름을 돕기 위한 것이며 Codex 플러그인은 SpecGuard 엔진을 새로 구현하지 않습니다. 기존 specguard CLI를 호출하고, 생성된 구조화 결과를 읽어서 현재 상태와 다음 행동을 요약합니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;설치&amp;nbsp;흐름은&amp;nbsp;대략&amp;nbsp;다음과&amp;nbsp;같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779032649508&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip install spec-guard
specguard --help
codex plugin marketplace add KoreaNirsa/spec-guard --ref main&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이후 Codex 앱 플러그인에서 Bulit by OpenAI &amp;rarr; SpecGuard Plugins 소스를 선택하고 설치하면 됩니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;다만 중요한 제한이 존재하는데, 플러그인 설치가 specguard CLI 설치까지 자동으로 처리하는 것은 아닙니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;현재 MVP는 CLI-backed 구조입니다. 따라서 Codex가 실행되는 환경에서 specguard 명령이 동작해야 합니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;앞으로는 사용자가 직접 명령어를 몰라도 Codex에게 &amp;ldquo;SpecGuard CLI가 설치되어 있는지 확인하고, 없으면 설치한 뒤 실행해줘&amp;rdquo;라고 요청할 수 있는 흐름을 더 자연스럽게 만들어 가려고 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;E. PR Review도 지원한다&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;SpecGuard에는 GitHub Actions 기반 PR Review 흐름도 있습니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;구현&amp;nbsp;PR이&amp;nbsp;올라왔을&amp;nbsp;때,&amp;nbsp;승인된&amp;nbsp;스펙&amp;nbsp;패키지와&amp;nbsp;구현&amp;nbsp;diff를&amp;nbsp;비교해서&amp;nbsp;advisory&amp;nbsp;comment를&amp;nbsp;남기는&amp;nbsp;방식입니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;여기서 목표는 &amp;ldquo;AI가 만든 코드 리뷰&amp;rdquo;가 아니라, 목표는 &lt;b&gt;구현 결과가 승인된 스펙에서 벗어났는지 확인하는 것&lt;/b&gt;입니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;예를 들어 팀에서 다음 같은 규칙을 두고 싶을 때 사용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;NOT_READY 스펙은 구현으로 넘기지 않기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Critical/Major finding을 PR에서 먼저 노출하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;구현 결과물이 아니라 요구사항 입력 품질을 먼저 관리하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;구현 PR이 승인된 스펙과 어긋나는지 확인하기&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;설치는 CLI에서 다음 명령으로 사용할 수 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779032721990&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;specguard actions install-pr-review&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: start;&quot;&gt;또는 Codex에게 아래와 같이 요청할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779034330071&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@specguard SpecGuard PR Review 워크플로우를 설정해줘.&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;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;다만, PR Review가 실제로 OpenAI 기반 리뷰를 수행하려면 GitHub Actions Secret에 SPECGUARD_OPENAI_API_KEY를 등록해야 합니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;선택적으로&amp;nbsp;아래&amp;nbsp;repository&amp;nbsp;variable도&amp;nbsp;설정할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779032748842&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SPECGUARD_PR_REVIEW_MODEL=gpt-5.4-nano
SPECGUARD_REVIEW_SPEC_PATHS=specs/your-feature-name&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이 부분도 앞으로 Codex 플러그인에서 더 잘 도와주고 싶은 부분입니다. 예를 들어 사용자가 &quot;@SpecGuard PR Review를 설정해줘&quot;라고 요청하면, Codex가 다음을 순서대로 확인해주는 흐름입니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1.&amp;nbsp;현재&amp;nbsp;저장소와&amp;nbsp;브랜치&amp;nbsp;상태&amp;nbsp;확인 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. specguard CLI 설치 여부 확인 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3.&amp;nbsp;없으면&amp;nbsp;설치&amp;nbsp;안내&amp;nbsp;또는&amp;nbsp;설치&amp;nbsp;실행 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;4.&amp;nbsp;PR&amp;nbsp;Review&amp;nbsp;workflow&amp;nbsp;생성&amp;nbsp;전&amp;nbsp;사용자&amp;nbsp;확인 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. specguard actions install-pr-review 실행 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;6.&amp;nbsp;필요한&amp;nbsp;GitHub&amp;nbsp;Secret과&amp;nbsp;Variables&amp;nbsp;안내 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;7.&amp;nbsp;GitHub&amp;nbsp;연결이&amp;nbsp;되어&amp;nbsp;있으면&amp;nbsp;안전한&amp;nbsp;secret&amp;nbsp;등록&amp;nbsp;경로&amp;nbsp;안내&amp;nbsp;또는&amp;nbsp;보조 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;8.&amp;nbsp;연결이&amp;nbsp;없으면&amp;nbsp;수동&amp;nbsp;설정&amp;nbsp;방법&amp;nbsp;안내 &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;API Key를 플러그인이 임의로 만들거나 저장해서는 안 됩니다. Secret은 GitHub Actions Secret으로만 다뤄야 하고, 저장소에 커밋되어서는 안 됩니다. 이&amp;nbsp;보안&amp;nbsp;경계는&amp;nbsp;앞으로도&amp;nbsp;유지할&amp;nbsp;생각입니다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;설계하면서 중요하게 본 것&lt;/b&gt; &lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;SpecGuard를 만들면서 중요하게 본 기준은 아래와 같습니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;첫째,&amp;nbsp;CLI가&amp;nbsp;기준이&amp;nbsp;되어야&amp;nbsp;합니다. &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Codex 플러그인, GitHub Actions, PR Review는 모두 같은 SpecGuard CLI 결과를 읽어야 합니다. 플러그인 안에 별도의 리뷰 엔진을 만들면 동작이 갈라지고 유지보수가 어려워집니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;둘째,&amp;nbsp;기본&amp;nbsp;검사는&amp;nbsp;재현&amp;nbsp;가능해야&amp;nbsp;합니다. &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그래서 기본 게이트는 API Key 없이 동작하는 휴리스틱 검사입니다. LLM 리뷰는 더 깊게 보고 싶을 때 선택적으로 붙이는 보조 경로에 가깝습니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;셋째,&amp;nbsp;스펙&amp;nbsp;수정은&amp;nbsp;사용자가&amp;nbsp;결정해야&amp;nbsp;합니다. &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;SpecGuard는 finding을 보여주고 개선 방향을 제안할 수 있지만, 제품 요구사항 자체를 자동으로 바꾸는 것은 조심해야 합니다. 스펙을 바꾸는 것은 곧 제품 의도를 바꾸는 일이기 때문입니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;넷째,&amp;nbsp;구현&amp;nbsp;에이전트에게&amp;nbsp;넘기는&amp;nbsp;입력을&amp;nbsp;명확히&amp;nbsp;해야&amp;nbsp;합니다. &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;AI 구현 에이전트에게 애매한 요구사항을 던지고 결과를 기다리는 대신, 검토된 스펙과 핸드오프 문서를 넘기는 방식을 지향합니다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;현재 한계와 앞으로 할 일&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;SpecGuard는 아직 초기 버전입니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;모든 스펙을 완벽하게 판별하지 못합니다. 특히 휴리스틱 기반 검사는 빠르고 재현 가능하지만, 모든 도메인 문제를 다 잡아내지는 못합니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;현재 가장 먼저 개선해야 할 방향은 아래와 같이 잡았습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;불필요하게 깨지는 테스트 줄이기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;핵심 로직 중심의 안정적인 테스트 유지&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;휴리스틱 리뷰의 미탐/오탐률 개선&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Codex 플러그인 사용 흐름 개선&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;@SpecGuard 기반 PR Review 설정 UX 개선&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;GitHub Secret/Variables 설정 안내 개선&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;한국어 스펙에서의 finding 품질 개선&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;특히 OSS 프로젝트로 운영하려면 테스트가 너무 많은 구현 세부사항에 묶이면 안 된다고 보고 있습니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;핵심 동작은 지키되, 문서 문구나 내부 구조가 조금 바뀔 때마다 CI가 깨지는 형태는 줄이려고 합니다. &lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;마무리&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;AI 코딩 도구는 앞으로 더 많이 쓰이게 될 것 같습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;하지만 AI에게 무엇을 만들지 알려주는 스펙이 약하면, 구현 속도가 빨라질수록 잘못된 방향으로 더 빠르게 갈 수 있습니다. SpecGuard는&amp;nbsp;그&amp;nbsp;전에&amp;nbsp;한&amp;nbsp;번&amp;nbsp;멈춰서&amp;nbsp;묻는&amp;nbsp;도구입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779032840602&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;이 스펙을 정말 구현 에이전트에게 넘겨도 되는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이&amp;nbsp;질문을&amp;nbsp;자동화된&amp;nbsp;게이트로&amp;nbsp;만들고&amp;nbsp;싶었습니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;아직&amp;nbsp;초기&amp;nbsp;단계의&amp;nbsp;OSS&amp;nbsp;프로젝트지만,&amp;nbsp;AI&amp;nbsp;코딩을&amp;nbsp;사용하면서&amp;nbsp;&amp;ldquo;스펙이&amp;nbsp;약해서&amp;nbsp;구현이&amp;nbsp;틀어지는&amp;nbsp;문제&amp;rdquo;를&amp;nbsp;겪고&amp;nbsp;있다면&amp;nbsp;한&amp;nbsp;번&amp;nbsp;사용해보셔도&amp;nbsp;좋을&amp;nbsp;것&amp;nbsp;같습니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;GitHub 저장소: &lt;a href=&quot;https://github.com/KoreaNirsa/spec-guard&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/KoreaNirsa/spec-guard&lt;/a&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;피드백과 기여도 환영합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;현재&amp;nbsp;이슈와&amp;nbsp;PR은&amp;nbsp;주로&amp;nbsp;영어로&amp;nbsp;관리하고&amp;nbsp;있지만,&amp;nbsp;한국어로&amp;nbsp;작성해주셔도&amp;nbsp;괜찮습니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;특히 아래 피드백이 있으면 많은 도움이 될 것 같습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;-&amp;nbsp;어떤&amp;nbsp;종류의&amp;nbsp;스펙에서&amp;nbsp;잘&amp;nbsp;맞는지 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;-&amp;nbsp;어떤&amp;nbsp;finding이&amp;nbsp;과하거나&amp;nbsp;부족한지 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;-&amp;nbsp;Codex&amp;nbsp;플러그인&amp;nbsp;흐름이&amp;nbsp;실제로&amp;nbsp;쓸&amp;nbsp;만한지 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;-&amp;nbsp;PR&amp;nbsp;Review&amp;nbsp;방식이&amp;nbsp;팀&amp;nbsp;워크플로우에&amp;nbsp;맞는지 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;-&amp;nbsp;한국어&amp;nbsp;스펙에서&amp;nbsp;어색하거나&amp;nbsp;부족한&amp;nbsp;부분이&amp;nbsp;있는지 &lt;/span&gt;&lt;/p&gt;</description>
      <category>Project</category>
      <author>KoreaNirsa</author>
      <guid isPermaLink="true">https://nirsa.tistory.com/487</guid>
      <comments>https://nirsa.tistory.com/487#entry487comment</comments>
      <pubDate>Mon, 18 May 2026 00:52:07 +0900</pubDate>
    </item>
    <item>
      <title>내가 프로젝트에서 사용하는 개발 방식 - IDAD (Issue-Driven Agent Development)</title>
      <link>https://nirsa.tistory.com/486</link>
      <description>&lt;blockquote data-end=&quot;217&quot; data-start=&quot;196&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;내가&amp;nbsp;프로젝트에서&amp;nbsp;사용하는&amp;nbsp;개발&amp;nbsp;방식&amp;nbsp;-&amp;nbsp;IDAD&amp;nbsp;(Issue-Driven&amp;nbsp;Agent&amp;nbsp;Development)&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;266&quot; data-start=&quot;219&quot; data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트를 프로젝트에 사용하기 시작하면서 개발 방식이 조금씩 바뀌었습니다.&lt;/p&gt;
&lt;p data-end=&quot;366&quot; data-start=&quot;268&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 단순히 &amp;ldquo;AI에게 코드를 작성하게 한다&amp;rdquo; 정도로 생각했습니다. 하지만 실제 프로젝트에 적용해보니 중요한 것은 AI에게 코드를 얼마나 잘 쓰게 하느냐가 아니었으며 가장 중요한 것은 &lt;b&gt;AI가 무엇을 기준으로 코드를 작성하게 할 것인가&lt;/b&gt;였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;366&quot; data-start=&quot;268&quot; data-ke-size=&quot;size16&quot;&gt;이러한 방식으로 인해 스펙 주도 개발이 유행하였고, 대부분의 개발자가 이 기준을 스펙으로 진행하고 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;482&quot; data-start=&quot;415&quot; data-ke-size=&quot;size16&quot;&gt;하지만 저는 프로젝트에서 이 기준을 &lt;b&gt;이슈&lt;/b&gt;로 잡아 진행하고 있으며&amp;nbsp;이 방식을 개인적으로 &lt;b&gt;IDAD(Issue-Driven Agent Development)&lt;/b&gt;라고 부릅니다.&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;560&quot; data-start=&quot;520&quot; data-ke-size=&quot;size16&quot;&gt;말 그대로, 이슈를 중심으로 AI 에이전트가 개발을 수행하는 방식입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;353&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bc99Kw/dJMcaf0Pcqy/KmmwscKEIz6W38SXUviKZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bc99Kw/dJMcaf0Pcqy/KmmwscKEIz6W38SXUviKZ0/img.png&quot; data-alt=&quot;진행중인 SpecGuard 프로젝트에서 IDAD, 즉 이슈 기반으로 개발을 진행하는 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bc99Kw/dJMcaf0Pcqy/KmmwscKEIz6W38SXUviKZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbc99Kw%2FdJMcaf0Pcqy%2FKmmwscKEIz6W38SXUviKZ0%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;1238&quot; height=&quot;353&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;353&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;진행중인 SpecGuard 프로젝트에서 IDAD, 즉 이슈 기반으로 개발을 진행하는 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-end=&quot;583&quot; data-start=&quot;567&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;왜 이슈가 중요해졌을까&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;625&quot; data-start=&quot;585&quot; data-ke-size=&quot;size16&quot;&gt;기존 개발 방식에서는 이슈가 사람을 위한 작업 관리 도구에 가까웠습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;이슈 확인
  &amp;darr;
사람이 요구사항 해석
  &amp;darr;
사람이 코드 작성
  &amp;darr;
테스트
  &amp;darr;
PR 생성
  &amp;darr;
리뷰&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;736&quot; data-start=&quot;701&quot; data-ke-size=&quot;size16&quot;&gt;하지만 AI 코딩 에이전트를 사용하면 이슈의 역할이 달라집니다.&lt;/p&gt;
&lt;p data-end=&quot;795&quot; data-start=&quot;738&quot; data-ke-size=&quot;size16&quot;&gt;이슈는 더 이상 단순한 할 일 목록이 아닙니다. AI 에이전트가 읽는 &lt;b&gt;작업 지시서&lt;/b&gt;가 됩니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Issue
  &amp;darr;
Agent Context
  &amp;darr;
Branch
  &amp;darr;
Implementation
  &amp;darr;
Pull Request
  &amp;darr;
Review
  &amp;darr;
Merge&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;923&quot; data-start=&quot;902&quot; data-ke-size=&quot;size16&quot;&gt;즉, 이슈는 다음 역할을 하게 됩니다.&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;1119&quot; data-start=&quot;925&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;역할&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;976&quot; data-start=&quot;947&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;955&quot; data-start=&quot;947&quot;&gt;문제 정의&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;976&quot; data-start=&quot;955&quot;&gt;무엇을 해결해야 하는지 알려준다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1007&quot; data-start=&quot;977&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;985&quot; data-start=&quot;977&quot;&gt;범위 제한&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1007&quot; data-start=&quot;985&quot;&gt;어디까지 수정할 수 있는지 정한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1047&quot; data-start=&quot;1008&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1016&quot; data-start=&quot;1008&quot;&gt;맥락 제공&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1047&quot; data-start=&quot;1016&quot;&gt;AI가 참고해야 할 코드, 문서, 로그를 제공한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1080&quot; data-start=&quot;1048&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1056&quot; data-start=&quot;1048&quot;&gt;완료 기준&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1080&quot; data-start=&quot;1056&quot;&gt;어떤 상태가 되면 끝난 것인지 정한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1119&quot; data-start=&quot;1081&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1089&quot; data-start=&quot;1081&quot;&gt;리뷰 기준&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1119&quot; data-start=&quot;1089&quot;&gt;PR이 제대로 작성되었는지 판단하는 기준이 된다&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1151&quot; data-start=&quot;1121&quot; data-ke-size=&quot;size16&quot;&gt;제가 IDAD에서 가장 중요하게 보는 점은 이것입니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;1151&quot; data-start=&quot;1121&quot; data-ke-style=&quot;style3&quot;&gt;&lt;i&gt;이슈가 좋아야 AI가 좋은 코드를 작성할 수 있다.&lt;/i&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;1281&quot; data-start=&quot;1185&quot; data-ke-size=&quot;size16&quot;&gt;AI 에이전트는 이슈에 적힌 내용을 기준으로 움직입니다.&lt;br /&gt;이슈가 모호하면 AI는 빈칸을 추측으로 채웁니다.&lt;br /&gt;그리고 추측으로 작성된 코드는 리뷰 비용을 크게 늘립니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-end=&quot;1302&quot; data-start=&quot;1288&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;IDAD란 무엇인가?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;1323&quot; data-start=&quot;1304&quot; data-ke-size=&quot;size16&quot;&gt;제가 말하는 IDAD는 간단합니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;1323&quot; data-start=&quot;1304&quot; data-ke-style=&quot;style3&quot;&gt;&lt;i&gt;이슈를 AI 에이전트가 실행할 수 있는 작업 단위를 만들고,&lt;/i&gt;&lt;br /&gt;&lt;i&gt;AI가 그 이슈를 기준으로 구현을 진행하고,&lt;/i&gt;&lt;br /&gt;&lt;i&gt;사람은 PR에서 이슈 충족 여부를 검증하는 개발 방식&lt;/i&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;1468&quot; data-start=&quot;1425&quot; data-ke-size=&quot;size16&quot;&gt;기존 이슈 기반 개발과 비슷해 보일 수 있지만 차이가 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;1530&quot; data-start=&quot;1470&quot; data-ke-size=&quot;size16&quot;&gt;기존 이슈는 주로 사람을 위한 설명이었습니다.&lt;br /&gt;하지만 IDAD에서 이슈는 AI가 바로 작업할 수 있어야 합니다.&lt;/p&gt;
&lt;p data-end=&quot;1569&quot; data-start=&quot;1532&quot; data-ke-size=&quot;size16&quot;&gt;그래서 IDAD의 이슈에는 단순히 &amp;ldquo;무엇을 한다&amp;rdquo;만 적지 않고 아래와 같은&amp;nbsp;내용이 들어가야 합니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;## 문제
현재 어떤 문제가 있는가?

## 목표
이 이슈가 완료되면 무엇이 가능해야 하는가?

## 변경 범위
수정해도 되는 영역은 어디인가?

## 제외 범위
이번 작업에서 건드리지 말아야 할 영역은 어디인가?

## 완료 조건
어떤 조건을 만족하면 완료로 볼 수 있는가?

## 검증 방법
어떤 테스트, 명령, 화면 확인으로 검증할 것인가?

## 참고 맥락
관련 PR, 문서, 스펙, 로그, 오류 메시지는 무엇인가?&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1879&quot; data-start=&quot;1843&quot; data-ke-size=&quot;size16&quot;&gt;이 정도는 있어야 AI 에이전트가 안정적으로 작업할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-end=&quot;1904&quot; data-start=&quot;1886&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;내가 사용하는 IDAD 흐름&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;1934&quot; data-start=&quot;1906&quot; data-ke-size=&quot;size16&quot;&gt;제가 프로젝트에서 사용하는 흐름은 대략 이렇습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 이슈를 작성한다
2. 문제, 목표, 범위, 완료 조건, 검증 방법을 적는다
3. AI 에이전트에게 이슈 기준으로 작업을 요청한다
4. AI 에이전트가 관련 파일을 탐색한다
5. 최소 변경으로 구현한다
6. 테스트 또는 수동 검증을 수행한다
7. 이슈 번호와 연결된 PR을 만든다
8. 사람이 PR을 리뷰한다
9. 이슈 충족 여부와 범위 초과 여부를 확인한다&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2192&quot; data-start=&quot;2152&quot; data-ke-size=&quot;size16&quot;&gt;여기서 핵심은 &lt;b&gt;이슈 하나가 하나의 목적만 가져야 한다&lt;/b&gt;는 점입니다.&lt;/p&gt;
&lt;p data-end=&quot;2215&quot; data-start=&quot;2194&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 이런 이슈는 좋지 않습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;회원가입 개선&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2326&quot; data-start=&quot;2238&quot; data-ke-size=&quot;size16&quot;&gt;이런식으로 작성된 이슈에서 동작할 수 있는 경우의 수는 너무나 넓습니다.&lt;br /&gt;AI는 이메일 검증을 고칠 수도 있고, 비밀번호 정책을 바꿀 수도 있고, UI를 수정할 수도 있고, API 응답까지 건드릴 수도 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;2345&quot; data-start=&quot;2328&quot; data-ke-size=&quot;size16&quot;&gt;반면 이런 이슈는 더 좋습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;회원가입 시 이메일 중복 오류 메시지를 명확하게 수정한다&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2468&quot; data-start=&quot;2392&quot; data-ke-size=&quot;size16&quot;&gt;이슈의 목적이 분명합니다. 수정 범위도 제한하기 쉽고, 리뷰할 때도 &amp;ldquo;이 PR이 이 이슈를 해결했는가?&amp;rdquo;를 판단하기 쉽습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-end=&quot;2491&quot; data-start=&quot;2475&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;좋은 IDAD 이슈 예시&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;2517&quot; data-start=&quot;2493&quot; data-ke-size=&quot;size16&quot;&gt;제가 선호하는 이슈 형태는 아래와 같습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;## 문제
회원가입 화면에서 이미 등록된 이메일을 입력하면 일반적인 오류 메시지만 표시된다.
사용자는 왜 회원가입이 실패했는지 명확히 알기 어렵다.

## 목표
이메일 중복 오류 발생 시 사용자가 원인을 명확히 알 수 있도록 메시지를 개선한다.

## 변경 범위
- 회원가입 화면의 오류 메시지 표시 로직
- 이메일 중복 오류 응답 처리 로직

## 제외 범위
- DB 스키마 변경 없음
- 회원가입 API 응답 필드 추가 없음
- 이메일 인증 로직 변경 없음
- 관리자 기능 변경 없음

## 완료 조건
- 중복 이메일로 회원가입 시도 시 &quot;이미 사용 중인 이메일입니다.&quot; 메시지를 표시한다.
- 신규 이메일 회원가입은 기존과 동일하게 정상 동작한다.
- 다른 회원가입 오류 메시지는 기존과 동일하게 유지한다.

## 검증 방법
- 중복 이메일로 회원가입 시도
- 신규 이메일로 회원가입 시도
- 기존 회원가입 테스트 통과 확인&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3041&quot; data-start=&quot;3001&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 작성하면 AI가 해야 할 일과 하지 말아야 할 일이 명확해집니다.&lt;/p&gt;
&lt;p data-end=&quot;3160&quot; data-start=&quot;3043&quot; data-ke-size=&quot;size16&quot;&gt;제가 IDAD에서 특히 중요하게 보는 항목은 &lt;b&gt;제외 범위&lt;/b&gt;입니다.&lt;br /&gt;AI 에이전트는 관련 코드를 넓게 수정하려는 경향이 있기 때문에 이번 이슈에서 건드리지 말아야 할 영역을 명확히 적어야 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-end=&quot;3178&quot; data-start=&quot;3167&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Issue-Driven Agent Developemnt의 장단점&lt;/span&gt;&lt;/blockquote&gt;
&lt;blockquote data-end=&quot;3212&quot; data-start=&quot;3180&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;IDAD의 장점&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;3212&quot; data-start=&quot;3180&quot; data-ke-size=&quot;size16&quot;&gt;제가 IDAD를 쓰면서 느낀 장점은 크게 다섯 가지입니다.&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;3456&quot; data-start=&quot;3214&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;장점&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3280&quot; data-start=&quot;3236&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3248&quot; data-start=&quot;3236&quot;&gt;도입 비용이 낮다&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3280&quot; data-start=&quot;3248&quot;&gt;대부분의 프로젝트는 이미 이슈 기반으로 일하고 있다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3325&quot; data-start=&quot;3281&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3295&quot; data-start=&quot;3281&quot;&gt;작업 단위가 작아진다&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3325&quot; data-start=&quot;3295&quot;&gt;AI가 처리하기 좋은 단위로 일을 쪼갤 수 있다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3367&quot; data-start=&quot;3326&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3338&quot; data-start=&quot;3326&quot;&gt;추적성이 좋아진다&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3367&quot; data-start=&quot;3338&quot;&gt;이슈, 브랜치, 커밋, PR, 리뷰가 연결된다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3411&quot; data-start=&quot;3368&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3381&quot; data-start=&quot;3368&quot;&gt;운영 업무에 강하다&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3411&quot; data-start=&quot;3381&quot;&gt;버그 수정, 문서 보완, 테스트 추가에 적합하다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3456&quot; data-start=&quot;3412&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3424&quot; data-start=&quot;3412&quot;&gt;병렬 처리가 쉽다&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3456&quot; data-start=&quot;3424&quot;&gt;여러 이슈를 여러 에이전트가 나누어 처리할 수 있다&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3484&quot; data-start=&quot;3458&quot; data-ke-size=&quot;size16&quot;&gt;특히 추적성은 실제 프로젝트에서 꽤 중요합니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;Issue #128
  &amp;darr;
feature/issue-128-fix-email-error-message
  &amp;darr;
Commit
  &amp;darr;
Pull Request
  &amp;darr;
Review
  &amp;darr;
Merge&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3690&quot; data-start=&quot;3605&quot; data-ke-size=&quot;size16&quot;&gt;나중에 &amp;ldquo;이 코드는 왜 들어왔지?&amp;rdquo;라고 확인해야 할 때가 있습니다.&lt;br /&gt;그때 PR만 보는 것보다, 원래 이슈까지 연결되어 있으면 판단이 훨씬 쉬워집니다. 또한 여러 이슈들을 여러 에이전트가 병렬 처리가 가능하기에 개발 속도가 굉장히 빨리지는 것을 체감할 수 있었습니다.&lt;/p&gt;
&lt;p data-end=&quot;3690&quot; data-start=&quot;3605&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-end=&quot;3708&quot; data-start=&quot;3697&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;IDAD의 단점&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;3739&quot; data-start=&quot;3710&quot; data-ke-size=&quot;size16&quot;&gt;물론 IDAD가 모든 문제를 해결하는 것은 아닙니다.&lt;/p&gt;
&lt;p data-end=&quot;3773&quot; data-start=&quot;3741&quot; data-ke-size=&quot;size16&quot;&gt;가장 큰 단점은 &lt;b&gt;이슈가 명세보다 약하다&lt;/b&gt;는 점입니다. 일반적인 이슈에는 다음 내용이 충분히 들어가지 않는 경우가 많습니다.&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;4037&quot; data-start=&quot;3815&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;누락되기 쉬운 내용&lt;/td&gt;
&lt;td&gt;예시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3877&quot; data-start=&quot;3845&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3854&quot; data-start=&quot;3845&quot;&gt;API 계약&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3877&quot; data-start=&quot;3854&quot;&gt;요청 필드, 응답 필드, 상태 코드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3909&quot; data-start=&quot;3878&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3886&quot; data-start=&quot;3878&quot;&gt;예외 처리&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3909&quot; data-start=&quot;3886&quot;&gt;실패 시 어떤 응답을 반환할 것인가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3937&quot; data-start=&quot;3910&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3915&quot; data-start=&quot;3910&quot;&gt;권한&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3937&quot; data-start=&quot;3915&quot;&gt;누가 이 기능을 사용할 수 있는가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3972&quot; data-start=&quot;3938&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3946&quot; data-start=&quot;3938&quot;&gt;상태 전이&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3972&quot; data-start=&quot;3946&quot;&gt;어떤 상태에서 어떤 상태로 변경 가능한가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;4008&quot; data-start=&quot;3973&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3983&quot; data-start=&quot;3973&quot;&gt;데이터 소유권&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4008&quot; data-start=&quot;3983&quot;&gt;어떤 사용자가 어떤 데이터를 소유하는가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;4037&quot; data-start=&quot;4009&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4017&quot; data-start=&quot;4009&quot;&gt;장애 처리&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4037&quot; data-start=&quot;4017&quot;&gt;재시도, 타임아웃, 롤백 기준&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4114&quot; data-start=&quot;4039&quot; data-ke-size=&quot;size16&quot;&gt;이런 내용이 빠진 상태에서 AI가 구현하면 겉으로는 동작하는 코드가 나올 수 있지만 시스템의 계약을 위반할 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;4191&quot; data-start=&quot;4116&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 API 응답 필드를 마음대로 추가하거나, 권한 검증을 누락하거나, 기존 상태 전이 규칙을 깨는 코드가 만들어질 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-end=&quot;4250&quot; data-start=&quot;4233&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그래서 SDD와는 무엇이 다르고 언제, 어떻게 사용해야 하는가?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;4312&quot; data-start=&quot;4252&quot; data-ke-size=&quot;size16&quot;&gt;AI 시대의 개발 방식으로 SDD, 즉 &lt;b&gt;Spec-Driven Development&lt;/b&gt;도 자주 언급됩니다. 제가 보는 IDAD와 SDD의 차이는 이렇습니다.&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;4723&quot; data-start=&quot;4343&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;기준&lt;/td&gt;
&lt;td&gt;SDDI&lt;/td&gt;
&lt;td&gt;IDAD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;4393&quot; data-start=&quot;4377&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4382&quot; data-start=&quot;4377&quot;&gt;중심&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4387&quot; data-start=&quot;4382&quot;&gt;스펙&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4393&quot; data-start=&quot;4387&quot;&gt;이슈&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;4444&quot; data-start=&quot;4394&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4399&quot; data-start=&quot;4394&quot;&gt;목적&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4423&quot; data-start=&quot;4399&quot;&gt;구현 전 요구사항과 계약을 명확히 고정&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4444&quot; data-start=&quot;4423&quot;&gt;이슈 단위로 변경을 빠르게 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;4507&quot; data-start=&quot;4445&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4450&quot; data-start=&quot;4445&quot;&gt;강점&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4480&quot; data-start=&quot;4450&quot;&gt;API, 권한, 예외, 상태, 검증 기준을 구조화&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4507&quot; data-start=&quot;4480&quot;&gt;기존 백로그와 PR 흐름에 자연스럽게 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;4578&quot; data-start=&quot;4508&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4517&quot; data-start=&quot;4508&quot;&gt;적합한 작업&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4545&quot; data-start=&quot;4517&quot;&gt;신규 기능, API, 보안, 결제, 권한 변경&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4578&quot; data-start=&quot;4545&quot;&gt;버그 수정, 작은 기능, 문서, 테스트, 운영성 개선&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;4623&quot; data-start=&quot;4579&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4587&quot; data-start=&quot;4579&quot;&gt;검증 위치&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4600&quot; data-start=&quot;4587&quot;&gt;구현 전 스펙 검증&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4623&quot; data-start=&quot;4600&quot;&gt;PR 단계에서 이슈 충족 여부 검증&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;4664&quot; data-start=&quot;4624&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4629&quot; data-start=&quot;4624&quot;&gt;속도&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4646&quot; data-start=&quot;4629&quot;&gt;상대적으로 느리지만 안정적&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4664&quot; data-start=&quot;4646&quot;&gt;빠르지만 이슈 품질에 민감&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;4723&quot; data-start=&quot;4665&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4673&quot; data-start=&quot;4665&quot;&gt;실패 방식&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4694&quot; data-start=&quot;4673&quot;&gt;스펙이 약하면 구현 전 차단 가능&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4723&quot; data-start=&quot;4694&quot;&gt;이슈가 약하면 AI가 추측으로 구현할 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4748&quot; data-start=&quot;4725&quot; data-ke-size=&quot;size16&quot;&gt;간단히 말하면 이렇게 정리할 수 있습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;SDD
= 무엇을 만들지 명확해질 때까지 구현하지 않는다.

IDAD
= 이슈를 실행 가능한 단위로 정리하고, 그 범위 안에서 구현한다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4891&quot; data-start=&quot;4840&quot; data-ke-size=&quot;size16&quot;&gt;둘 중 하나가 항상 더 좋다고 보지는 않습니다. 작업의 성격에 따라 다르게 써야 합니다.&lt;/p&gt;
&lt;p data-end=&quot;4891&quot; data-start=&quot;4840&quot; data-ke-size=&quot;size16&quot;&gt;현재 저는 A 프로젝트는 SDD 방식으로, B 프로젝트는 IDAD 방식을 사용하며 AI 에이전트를 활용한 여러 개발 방식을 사용해보고, 테스트하고, 실험해보고 있습니다. 현재까지의 상황으로는 서로 명백히 장단점과 성격이 다르기 때문에 큰 특이사항이 없다면 다음 프로젝트에서는 SDD+IDAD 방식으로 함께 사용하 개발을 진행하려 합니다.&lt;/p&gt;
&lt;p data-end=&quot;4891&quot; data-start=&quot;4840&quot; data-ke-size=&quot;size16&quot;&gt;오히려 같이 사용했을 때 더 좋은 퍼포먼스가 나올 것으로 생각합니다.&lt;/p&gt;
&lt;p data-end=&quot;4891&quot; data-start=&quot;4840&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-end=&quot;4891&quot; data-start=&quot;4840&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;SDD와 IDAD를 무슨 기준으로 진행하는가?&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;4948&quot; data-start=&quot;4920&quot; data-ke-size=&quot;size16&quot;&gt;제가 프로젝트에서 기준으로 삼는 방식은 단순합니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;작고 명확한 변경
  &amp;rarr; IDAD로 처리

계약과 리스크가 큰 변경
  &amp;rarr; SDD로 전환&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5037&quot; data-start=&quot;5013&quot; data-ke-size=&quot;size16&quot;&gt;조금 더 구체적으로 나누면 다음과 같습니다.&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 227px;&quot; border=&quot;1&quot; data-end=&quot;5411&quot; data-start=&quot;5039&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;작업 유형&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;적합한 방식&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;이유&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;5105&quot; data-start=&quot;5077&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5085&quot; data-start=&quot;5077&quot;&gt;문구 수정&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5092&quot; data-start=&quot;5085&quot;&gt;IDAD&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5105&quot; data-start=&quot;5092&quot;&gt;영향 범위가 작다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;5142&quot; data-start=&quot;5106&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5117&quot; data-start=&quot;5106&quot;&gt;UI 버그 수정&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5124&quot; data-start=&quot;5117&quot;&gt;IDAD&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5142&quot; data-start=&quot;5124&quot;&gt;변경 범위를 제한하기 쉽다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;5177&quot; data-start=&quot;5143&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5152&quot; data-start=&quot;5143&quot;&gt;테스트 추가&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5159&quot; data-start=&quot;5152&quot;&gt;IDAD&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5177&quot; data-start=&quot;5159&quot;&gt;기대 동작이 이미 존재한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;5204&quot; data-start=&quot;5178&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5186&quot; data-start=&quot;5178&quot;&gt;문서 보완&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5193&quot; data-start=&quot;5186&quot;&gt;IDAD&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5204&quot; data-start=&quot;5193&quot;&gt;위험도가 낮다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;5240&quot; data-start=&quot;5205&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5220&quot; data-start=&quot;5205&quot;&gt;기존 기능의 작은 확장&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5227&quot; data-start=&quot;5220&quot;&gt;IDAD&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5240&quot; data-start=&quot;5227&quot;&gt;맥락이 이미 있다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;5277&quot; data-start=&quot;5241&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5253&quot; data-start=&quot;5241&quot;&gt;신규 API 설계&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5259&quot; data-start=&quot;5253&quot;&gt;SDD&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5277&quot; data-start=&quot;5259&quot;&gt;요청/응답 계약이 필요하다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;5309&quot; data-start=&quot;5278&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5289&quot; data-start=&quot;5278&quot;&gt;인증/권한 변경&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5295&quot; data-start=&quot;5289&quot;&gt;SDD&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5309&quot; data-start=&quot;5295&quot;&gt;보안 리스크가 크다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;5337&quot; data-start=&quot;5310&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5318&quot; data-start=&quot;5310&quot;&gt;결제/정산&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5324&quot; data-start=&quot;5318&quot;&gt;SDD&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5337&quot; data-start=&quot;5324&quot;&gt;실패 비용이 높다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;5369&quot; data-start=&quot;5338&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5350&quot; data-start=&quot;5338&quot;&gt;데이터 모델 변경&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5356&quot; data-start=&quot;5350&quot;&gt;SDD&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5369&quot; data-start=&quot;5356&quot;&gt;장기 영향이 크다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;5411&quot; data-start=&quot;5370&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5382&quot; data-start=&quot;5370&quot;&gt;외부 시스템 연동&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5388&quot; data-start=&quot;5382&quot;&gt;SDD&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5411&quot; data-start=&quot;5388&quot;&gt;장애 처리와 재시도 정책이 필요하다&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5499&quot; data-start=&quot;5413&quot; data-ke-size=&quot;size16&quot;&gt;즉, IDAD는 모든 변경을 가볍게 처리하기 위한 방식이 아닙니다.&lt;br /&gt;오히려 &lt;b&gt;가벼운 변경과 무거운 변경을 빠르게 구분하기 위한 방식&lt;/b&gt;에 가깝습니다.&lt;/p&gt;
&lt;p data-end=&quot;5499&quot; data-start=&quot;5413&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-end=&quot;5522&quot; data-start=&quot;5506&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;위험도에 따른 운영 기준&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;5558&quot; data-start=&quot;5524&quot; data-ke-size=&quot;size16&quot;&gt;제가 IDAD를 운영할 때는 이슈를 위험도에 따라 구분합니다.&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;5804&quot; data-start=&quot;5560&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;위험도&lt;/td&gt;
&lt;td&gt;예시&lt;/td&gt;
&lt;td&gt;처리 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5651&quot; data-start=&quot;5595&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5600&quot; data-start=&quot;5595&quot;&gt;낮음&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5636&quot; data-start=&quot;5600&quot;&gt;문구 수정, 문서 보완, 테스트 이름 변경, 작은 UI 버그&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5651&quot; data-start=&quot;5636&quot;&gt;IDAD로 바로 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5727&quot; data-start=&quot;5652&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5657&quot; data-start=&quot;5652&quot;&gt;중간&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5692&quot; data-start=&quot;5657&quot;&gt;기존 기능의 작은 확장, 내부 로직 수정, 단일 화면 변경&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5727&quot; data-start=&quot;5692&quot;&gt;IDAD로 처리하되 완료 조건과 검증 방법을 명확히 작성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5804&quot; data-start=&quot;5728&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5733&quot; data-start=&quot;5728&quot;&gt;높음&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5776&quot; data-start=&quot;5733&quot;&gt;API 계약 변경, 권한 변경, 데이터 모델 변경, 결제/보안 관련 작업&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5804&quot; data-start=&quot;5776&quot;&gt;이슈에서 스펙 작성을 요구하고 SDD로 전환&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5867&quot; data-start=&quot;5806&quot; data-ke-size=&quot;size16&quot;&gt;이 기준을 두면 AI 에이전트에게 맡겨도 되는 작업과, 먼저 사람이 설계해야 하는 작업을 구분할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-end=&quot;5895&quot; data-start=&quot;5874&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;IDAD를 사용할 때 지키는 규칙&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;5933&quot; data-start=&quot;5897&quot; data-ke-size=&quot;size16&quot;&gt;제가 IDAD를 사용할 때 중요하게 보는 규칙은 다음과 같습니다.&lt;/p&gt;
&lt;p data-end=&quot;5933&quot; data-start=&quot;5897&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-end=&quot;5960&quot; data-start=&quot;5935&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;1. 이슈 하나는 하나의 목적만 가진다&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;5974&quot; data-start=&quot;5962&quot; data-ke-size=&quot;size16&quot;&gt;좋지 않은 이슈입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;대시보드 개선&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6006&quot; data-start=&quot;5997&quot; data-ke-size=&quot;size16&quot;&gt;좋은 이슈입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;대시보드 최근 주문 목록의 빈 상태 메시지를 추가한다&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6082&quot; data-start=&quot;6051&quot; data-ke-size=&quot;size16&quot;&gt;AI에게 맡길수록 작업 단위는 더 작고 명확해야 합니다.&lt;/p&gt;
&lt;p data-end=&quot;6082&quot; data-start=&quot;6051&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-end=&quot;6113&quot; data-start=&quot;6089&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;2. 완료 조건은 검증 가능해야 한다&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;6130&quot; data-start=&quot;6115&quot; data-ke-size=&quot;size16&quot;&gt;좋지 않은 완료 조건입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;사용성이 좋아진다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6168&quot; data-start=&quot;6156&quot; data-ke-size=&quot;size16&quot;&gt;좋은 완료 조건입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;비밀번호 형식이 올바르지 않을 때 INVALID_PASSWORD_FORMAT 메시지를 표시한다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6270&quot; data-start=&quot;6237&quot; data-ke-size=&quot;size16&quot;&gt;완료 조건은 테스트하거나 화면에서 확인할 수 있어야 합니다.&lt;/p&gt;
&lt;p data-end=&quot;6270&quot; data-start=&quot;6237&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-end=&quot;6298&quot; data-start=&quot;6277&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;3. 검증 방법을 반드시 적는다&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;6331&quot; data-start=&quot;6300&quot; data-ke-size=&quot;size16&quot;&gt;이슈에는 최소한 하나 이상의 검증 방법이 있어야 합니다.&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;6539&quot; data-start=&quot;6333&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;검증 방식&lt;/td&gt;
&lt;td&gt;예시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;6399&quot; data-start=&quot;6358&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;6367&quot; data-start=&quot;6358&quot;&gt;테스트 명령&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;6399&quot; data-start=&quot;6367&quot;&gt;npm test, ./gradlew test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;6439&quot; data-start=&quot;6400&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;6411&quot; data-start=&quot;6400&quot;&gt;수동 확인 경로&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;6439&quot; data-start=&quot;6411&quot;&gt;회원가입 화면 &amp;rarr; 이메일 입력 &amp;rarr; 가입 시도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;6484&quot; data-start=&quot;6440&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;6448&quot; data-start=&quot;6440&quot;&gt;기대 응답&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;6484&quot; data-start=&quot;6448&quot;&gt;HTTP 400, EMAIL_ALREADY_EXISTS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;6509&quot; data-start=&quot;6485&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;6493&quot; data-start=&quot;6485&quot;&gt;화면 기준&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;6509&quot; data-start=&quot;6493&quot;&gt;특정 오류 메시지 표시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;6539&quot; data-start=&quot;6510&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;6518&quot; data-start=&quot;6510&quot;&gt;로그 기준&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;6539&quot; data-start=&quot;6518&quot;&gt;특정 에러 로그가 발생하지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6578&quot; data-start=&quot;6541&quot; data-ke-size=&quot;size16&quot;&gt;검증 방법이 없으면 PR 리뷰에서 완료 여부를 판단하기 어렵습니다.&lt;/p&gt;
&lt;h3 data-end=&quot;6602&quot; data-start=&quot;6585&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;blockquote data-end=&quot;6602&quot; data-start=&quot;6585&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;4. 제외 범위를 적는다&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;6629&quot; data-start=&quot;6604&quot; data-ke-size=&quot;size16&quot;&gt;제가 가장 중요하게 보는 규칙 중 하나입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;이번 이슈에서는 DB 스키마를 변경하지 않는다.
API 응답 필드는 추가하지 않는다.
인증 로직은 수정하지 않는다.
관리자 화면은 변경하지 않는다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6771&quot; data-start=&quot;6727&quot; data-ke-size=&quot;size16&quot;&gt;AI 에이전트가 코드를 잘 작성하더라도, 범위를 넘어서면 좋은 변경이 아닙니다.&lt;/p&gt;
&lt;h3 data-end=&quot;6801&quot; data-start=&quot;6778&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;blockquote data-end=&quot;6801&quot; data-start=&quot;6778&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;5. PR은 반드시 이슈와 연결한다&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;6830&quot; data-start=&quot;6803&quot; data-ke-size=&quot;size16&quot;&gt;PR 설명에는 최소한 아래 내용이 있어야 합니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;## 해결한 이슈
- Close #128

## 변경 내용
- 이메일 중복 오류 메시지 처리 로직 수정

## 검증 결과
- 중복 이메일 회원가입 시 오류 메시지 확인
- 신규 이메일 회원가입 정상 동작 확인
- 기존 테스트 통과

## 제외한 범위
- API 응답 필드 변경 없음
- DB 스키마 변경 없음&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7094&quot; data-start=&quot;7022&quot; data-ke-size=&quot;size16&quot;&gt;PR 리뷰는 단순히 코드 스타일을 보는 과정이 아닙니다.&lt;br /&gt;IDAD에서는 PR이 원래 이슈를 정확히 해결했는지 확인해야 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-end=&quot;7114&quot; data-start=&quot;7101&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;IDAD의 안티패턴&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;7142&quot; data-start=&quot;7116&quot; data-ke-size=&quot;size16&quot;&gt;제가 피하려고 하는 안티패턴은 다음과 같습니다.&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;7482&quot; data-start=&quot;7144&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;안티패턴&lt;/td&gt;
&lt;td&gt;문제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7210&quot; data-start=&quot;7168&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7190&quot; data-start=&quot;7168&quot;&gt;제목만 있는 이슈를 AI에게 넘긴다&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7210&quot; data-start=&quot;7190&quot;&gt;AI가 대부분을 추측하게 된다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7256&quot; data-start=&quot;7211&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7232&quot; data-start=&quot;7211&quot;&gt;여러 기능을 하나의 이슈에 섞는다&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7256&quot; data-start=&quot;7232&quot;&gt;변경 범위가 커지고 리뷰가 어려워진다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7298&quot; data-start=&quot;7257&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7279&quot; data-start=&quot;7257&quot;&gt;완료 조건 없이 &amp;ldquo;개선&amp;rdquo;만 요청한다&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7298&quot; data-start=&quot;7279&quot;&gt;완료 여부를 판단할 수 없다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7344&quot; data-start=&quot;7299&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7323&quot; data-start=&quot;7299&quot;&gt;API 계약 변경을 이슈만으로 처리한다&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7344&quot; data-start=&quot;7323&quot;&gt;요청/응답 계약이 깨질 수 있다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7383&quot; data-start=&quot;7345&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7365&quot; data-start=&quot;7345&quot;&gt;권한 변경을 스펙 없이 구현한다&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7383&quot; data-start=&quot;7365&quot;&gt;보안 문제가 생길 수 있다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7431&quot; data-start=&quot;7384&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7403&quot; data-start=&quot;7384&quot;&gt;실패 케이스를 정의하지 않는다&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7431&quot; data-start=&quot;7403&quot;&gt;정상 케이스만 동작하는 코드가 나올 수 있다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7482&quot; data-start=&quot;7432&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7459&quot; data-start=&quot;7432&quot;&gt;PR 리뷰에서 이슈 충족 여부를 보지 않는다&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7482&quot; data-start=&quot;7459&quot;&gt;AI가 범위를 벗어나도 놓치기 쉽다&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7526&quot; data-start=&quot;7484&quot; data-ke-size=&quot;size16&quot;&gt;이런 방식은 IDAD라기보다는 단순히 AI에게 일을 던지는 것에 가깝습니다.&lt;/p&gt;
&lt;p data-end=&quot;7600&quot; data-start=&quot;7528&quot; data-ke-size=&quot;size16&quot;&gt;IDAD의 핵심은 AI를 많이 쓰는 것이 아닙니다.&lt;br /&gt;&lt;b&gt;AI가 실행할 수 있을 만큼 작업 기준을 명확하게 만드는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-end=&quot;7626&quot; data-start=&quot;7607&quot; data-ke-style=&quot;style1&quot;&gt;IDAD의 핵심&lt;/blockquote&gt;
&lt;p data-end=&quot;7656&quot; data-start=&quot;7628&quot; data-ke-size=&quot;size16&quot;&gt;제가 IDAD를 사용하면서 내린 결론은 단순합니다.&lt;/p&gt;
&lt;p data-end=&quot;7711&quot; data-start=&quot;7658&quot; data-ke-size=&quot;size16&quot;&gt;AI 에이전트는 좋은 도구이지만, 좋은 입력이 없으면 좋은 결과를 만들기 어렵습니다.&lt;/p&gt;
&lt;p data-end=&quot;7757&quot; data-start=&quot;7713&quot; data-ke-size=&quot;size16&quot;&gt;AI에게 바로 코드를 맡기는 것보다 중요한 것은, 먼저 기준을 세우는 것입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;Issue = 작업의 입구
Spec  = 위험한 변경의 기준
PR    = 구현 결과의 검증 지점&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7922&quot; data-start=&quot;7827&quot; data-ke-size=&quot;size16&quot;&gt;가벼운 작업은 IDAD로 빠르게 처리하고 위험한 작업은 SDD로 전환해 먼저 스펙을 세우며 PR에서는 이슈와 스펙을 기준으로 구현 결과를 검증합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-end=&quot;7935&quot; data-start=&quot;7929&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;마무리&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;8096&quot; data-start=&quot;8000&quot; data-ke-size=&quot;size16&quot;&gt;IDAD는 AI에게 단순히 코드를 작성하게 하는 방식이 아닙니다. 이슈를 AI가 실행 가능한 작업 단위로 만들고, 그 이슈를 기준으로 구현과 리뷰를 연결하는 방식입니다.&lt;/p&gt;
&lt;p data-end=&quot;8109&quot; data-start=&quot;8098&quot; data-ke-size=&quot;size16&quot;&gt;정리하면 이렇습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;작고 명확한 변경
  &amp;rarr; IDAD로 빠르게 처리한다

계약과 리스크가 큰 변경
  &amp;rarr; SDD로 검증한 뒤 구현한다&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;8281&quot; data-start=&quot;8188&quot; data-ke-size=&quot;size16&quot;&gt;AI 시대의 개발 생산성은 코드를 얼마나 빨리 작성하느냐만으로 결정되지 않으며 오히려 더 중요한 것은 &lt;b&gt;어떤 입력을 기준으로 코드를 작성하게 하느냐&lt;/b&gt;입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;8281&quot; data-start=&quot;8188&quot; data-ke-size=&quot;size16&quot;&gt;서로 완전히 다른 개발 방식이 아니라, 서로의 장단점을 보완해줄 수 있는 방식이라고 생각하고 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;8340&quot; data-start=&quot;8283&quot; data-ke-size=&quot;size16&quot;&gt;IDAD는 이슈로 정리하는 방식입니다.&lt;br /&gt;SDD 스펙으로 검증하는 방식입니다.&lt;/p&gt;
&lt;p data-end=&quot;8356&quot; data-start=&quot;8342&quot; data-ke-size=&quot;size16&quot;&gt;둘의 공통점은 하나입니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;8356&quot; data-start=&quot;8342&quot; data-ke-style=&quot;style3&quot;&gt;&lt;i&gt;AI에게 코드를 맡기기 전에, 사람이 먼저 기준을 세운다.&lt;/i&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI Engineering/Methodology</category>
      <author>KoreaNirsa</author>
      <guid isPermaLink="true">https://nirsa.tistory.com/486</guid>
      <comments>https://nirsa.tistory.com/486#entry486comment</comments>
      <pubDate>Sun, 10 May 2026 23:36:54 +0900</pubDate>
    </item>
    <item>
      <title>개인 OSS 프로젝트 SpecGuard를 진행하며 겪은 AI 코딩 에이전트와의 작은 오해</title>
      <link>https://nirsa.tistory.com/485</link>
      <description>&lt;blockquote data-end=&quot;1191&quot; data-start=&quot;1128&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;개인&amp;nbsp;OSS&amp;nbsp;프로젝트&amp;nbsp;SpecGuard를&amp;nbsp;진행하며&amp;nbsp;겪은&amp;nbsp;AI&amp;nbsp;코딩&amp;nbsp;에이전트와의&amp;nbsp;작은&amp;nbsp;오해&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;1191&quot; data-start=&quot;1128&quot; data-ke-size=&quot;size16&quot;&gt;개인적으로 진행 중인 OSS 프로젝트 SpecGuard를 진행하며 오늘 있었던 일에 대해 작성하려 합니다. SpecGuard는 제가 개인적으로 만들고 있는 오픈소스 프로젝트입니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;1191&quot; data-start=&quot;1128&quot; data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;github&lt;/b&gt; : &lt;a href=&quot;https://github.com/KoreaNirsa/spec-guard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/KoreaNirsa/spec-guard&lt;/a&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;1191&quot; data-start=&quot;1128&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;프로젝트를 진행하면서 GitHub 이슈를 정리하고, 기능 우선순위를 판단하고, 새로운 아이디어가 프로젝트 방향과 맞는지 검토하는 과정에서 에이전트 AI를 함께 사용하고 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;1415&quot; data-start=&quot;1337&quot; data-ke-size=&quot;size16&quot;&gt;오늘도 평소처럼 AI에게 현재 열린 이슈를 기준으로 우선순위를 정하고, 특정 기능 아이디어가 프로젝트에 적절한지 검토해 달라고 요청했습니다.&lt;/p&gt;
&lt;p data-end=&quot;1449&quot; data-start=&quot;1417&quot; data-ke-size=&quot;size16&quot;&gt;그런데 AI의 답변이 제가 기대한 방향과 조금 달랐습니다.&lt;/p&gt;
&lt;p data-end=&quot;1559&quot; data-start=&quot;1451&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 &amp;ldquo;왜 이런 식으로 답변했을까?&amp;rdquo;라고 생각했습니다.&lt;br /&gt;하지만 다시 제가 작성한 프롬프트를 읽어보니, 문제는 AI가 아니라 제가 던진 질문의 범위가 충분히 명확하지 않았다는 점이었습니다.&lt;/p&gt;
&lt;p data-end=&quot;1619&quot; data-start=&quot;1561&quot; data-ke-size=&quot;size16&quot;&gt;이번 글에서는 그 사례를 바탕으로, 에이전트 AI를 사용할 때 왜 프롬프트가 중요한지 정리해보려 합니다&lt;/p&gt;
&lt;p data-end=&quot;1619&quot; data-start=&quot;1561&quot; data-ke-size=&quot;size16&quot;&gt;.&lt;/p&gt;
&lt;blockquote data-end=&quot;1641&quot; data-start=&quot;1626&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;오늘 작성했던 프롬프트&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;1669&quot; data-start=&quot;1643&quot; data-ke-size=&quot;size16&quot;&gt;제가 AI에게 작성한 요청은 다음과 같았습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;현재 이슈 기준으로 우선순위 결정해.

또한 현재 스펙가드에 자바 포맷팅을 검사하는 기능은 어떤지 검토해봐.
현재 프로젝트의 의도와 달라져 과한지.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1784&quot; data-start=&quot;1767&quot; data-ke-size=&quot;size16&quot;&gt;제가 원했던 것은 단순했습니다. 현재 SpecGuard에는 Java 포맷팅을 검사하는 기능이 없었으며 저는 이미 그 사실을 알고 있었습니다.&lt;/p&gt;
&lt;p data-end=&quot;1889&quot; data-start=&quot;1851&quot; data-ke-size=&quot;size16&quot;&gt;제가 궁금했던 것은 &amp;ldquo;현재 코드에 이 기능이 있는지&amp;rdquo;가 아니었고, 제가 알고 싶었던 것은 다음과 같은 설계적 판단이었습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;SpecGuard에 Java 포맷팅 검사 기능을 추가하는 아이디어가 괜찮은가?

이 기능이 프로젝트 목적과 맞는가?

혹시 오버 엔지니어링이 되는가?

기능이 많아지면서 프로젝트의 목적성이 흐려질 가능성은 없는가?

만약 넣는다면 core gate로 넣는 것이 맞는가?

아니면 PR Review 단계의 보조 피드백 정도로 넣는 것이 맞는가?&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2196&quot; data-start=&quot;2130&quot; data-ke-size=&quot;size16&quot;&gt;즉, 저는 기능 구현을 요청한 것도 아니었고, 현재 코드에 해당 기능이 있는지 확인해 달라고 요청한 것도 아니었습니다.&lt;/p&gt;
&lt;p data-end=&quot;2245&quot; data-start=&quot;2198&quot; data-ke-size=&quot;size16&quot;&gt;제가 원했던 것은 &lt;b&gt;기능 도입에 대한 제품적, 설계적, 구조적 의견&lt;/b&gt;이었습니다.&lt;/p&gt;
&lt;p data-end=&quot;2245&quot; data-start=&quot;2198&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-end=&quot;2266&quot; data-start=&quot;2252&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;AI가 실제로 한 일&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;2320&quot; data-start=&quot;2268&quot; data-ke-size=&quot;size16&quot;&gt;AI는 제 요청을 받고 현재 프로젝트에 Java 포맷팅 검사 기능이 있는지 먼저 확인했습니다.&lt;/p&gt;
&lt;p data-end=&quot;2464&quot; data-start=&quot;2322&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 google-java-format, checkstyle, spotless, pmd 같은 Java 포맷팅 검사 도구나 관련 코드가 있는지 검색했습니다. 그리고 현재 레포지토리에는 Java 포맷팅 검사 기능이 없다고 정리했습니다.&lt;/p&gt;
&lt;p data-end=&quot;2611&quot; data-start=&quot;2466&quot; data-ke-size=&quot;size16&quot;&gt;이후 SpecGuard의 목적을 기준으로 Java 포맷팅 검사를 core gate로 넣는 것은 과할 수 있다고 판단했습니다.&lt;br /&gt;SpecGuard의 중심은 코드 스타일 검사가 아니라, 스펙과 구현 사이의 불일치를 줄이는 데 있기 때문이라는 식의 답변이었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;254&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbEo3Y/dJMb99M4bEQ/axU5UU166vghsW7bNcwfgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbEo3Y/dJMb99M4bEQ/axU5UU166vghsW7bNcwfgK/img.png&quot; data-alt=&quot;실제로 AI로부터 답변받은 내용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbEo3Y/dJMb99M4bEQ/axU5UU166vghsW7bNcwfgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbEo3Y%2FdJMb99M4bEQ%2FaxU5UU166vghsW7bNcwfgK%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;767&quot; height=&quot;254&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;254&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실제로 AI로부터 답변받은 내용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;2706&quot; data-start=&quot;2613&quot; data-ke-size=&quot;size16&quot;&gt;AI의 결론 자체가 완전히 틀렸다고 보기는 어렵습니다. Java 포맷팅 검사를 core gate로 넣는 것이 과할 수 있다는 의견은 충분히 검토할 만한 의견입니다.&lt;/p&gt;
&lt;p data-end=&quot;2731&quot; data-start=&quot;2708&quot; data-ke-size=&quot;size16&quot;&gt;하지만 제가 기대했던 답변과는 달랐습니다.&lt;/p&gt;
&lt;p data-end=&quot;2821&quot; data-start=&quot;2733&quot; data-ke-size=&quot;size16&quot;&gt;제가 원한 것은 &amp;ldquo;현재 기능이 있는지 확인해줘&amp;rdquo;가 아니었습니다.&lt;br /&gt;또한 &amp;ldquo;core gate로 넣는 것이 맞는지 아닌지 하나의 결론만 내려줘&amp;rdquo;도 아니었습니다.&lt;/p&gt;
&lt;p data-end=&quot;2857&quot; data-start=&quot;2823&quot; data-ke-size=&quot;size16&quot;&gt;제가 원했던 답변은 여러 선택지를 놓고 비교하는 것이었습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 아예 추가하지 않는 경우
2. core gate로 추가하는 경우
3. PR Review 보조 피드백으로 추가하는 경우
4. 옵션 기능으로 추가하는 경우
5. 외부 도구 연동 형태로만 제공하는 경우&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3043&quot; data-start=&quot;2985&quot; data-ke-size=&quot;size16&quot;&gt;그리고 각 선택지가 SpecGuard의 목적과 얼마나 잘 맞는지, 어떤 장단점이 있는지 듣고 싶었습니다.&lt;/p&gt;
&lt;p data-end=&quot;3043&quot; data-start=&quot;2985&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-end=&quot;3079&quot; data-start=&quot;3050&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;문제는 &amp;ldquo;검토&amp;rdquo;라는 단어가 너무 넓다는 점입니다&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;3105&quot; data-start=&quot;3081&quot; data-ke-size=&quot;size16&quot;&gt;이번 일을 통해 다시 느낀 점은 이것입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;&amp;ldquo;검토해줘&amp;rdquo;라는 말은 너무 넓습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3195&quot; data-start=&quot;3141&quot; data-ke-size=&quot;size16&quot;&gt;사람과 대화할 때는 &amp;ldquo;이 기능 어떤지 검토해봐&amp;rdquo;라고 말해도 어느 정도 의도가 전달될 수 있습니다. 하지만 에이전트 AI에게는 이 표현이 여러 방식으로 해석될 수 있습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;3569&quot; data-start=&quot;3239&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;사용자의 의도&lt;/td&gt;
&lt;td&gt;AI가 해석할 수 있는 작업&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3339&quot; data-start=&quot;3279&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3315&quot; data-start=&quot;3279&quot;&gt;이 기능 아이디어가 프로젝트 방향과 맞는지 의견을 듣고 싶음&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3339&quot; data-start=&quot;3315&quot;&gt;현재 코드에 해당 기능이 있는지 검색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3398&quot; data-start=&quot;3340&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3370&quot; data-start=&quot;3340&quot;&gt;기능을 도입하면 오버 엔지니어링인지 판단하고 싶음&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3398&quot; data-start=&quot;3370&quot;&gt;현재 구현 여부와 문서 기준으로 적절성 판단&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3473&quot; data-start=&quot;3399&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3440&quot; data-start=&quot;3399&quot;&gt;core gate와 PR Review 중 어디에 넣을지 비교하고 싶음&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3473&quot; data-start=&quot;3440&quot;&gt;core gate로 넣는 것이 과한지 단일 결론 제시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3516&quot; data-start=&quot;3474&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3495&quot; data-start=&quot;3474&quot;&gt;여러 선택지의 장단점을 듣고 싶음&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3516&quot; data-start=&quot;3495&quot;&gt;가장 안전한 하나의 방향만 제안&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3569&quot; data-start=&quot;3517&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3540&quot; data-start=&quot;3517&quot;&gt;구현하지 말고 설계 의견만 받고 싶음&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3569&quot; data-start=&quot;3540&quot;&gt;구현 여부 또는 현재 구현 상태 중심으로 검토&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3607&quot; data-start=&quot;3571&quot; data-ke-size=&quot;size16&quot;&gt;즉, &amp;ldquo;검토&amp;rdquo;라는 표현 안에는 여러 종류의 작업이 섞여 있습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;현재 상태 확인
도입 필요성 판단
설계 방향 검토
구현 위치 검토
기능 범위 검토
대안 비교
리스크 분석&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3745&quot; data-start=&quot;3681&quot; data-ke-size=&quot;size16&quot;&gt;제가 원했던 것은 이 중에서 &lt;b&gt;도입 필요성 판단, 설계 방향 검토, 대안 비교, 리스크 분석&lt;/b&gt;에 가까웠습니다.&lt;/p&gt;
&lt;p data-end=&quot;3780&quot; data-start=&quot;3747&quot; data-ke-size=&quot;size16&quot;&gt;하지만 AI는 먼저 &lt;b&gt;현재 상태 확인&lt;/b&gt;으로 들어갔습니다.&lt;/p&gt;
&lt;p data-end=&quot;3780&quot; data-start=&quot;3747&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-end=&quot;3815&quot; data-start=&quot;3787&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;제가 원했던 검토와 AI가 수행한 검토의 차이&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;3839&quot; data-start=&quot;3817&quot; data-ke-size=&quot;size16&quot;&gt;이번 상황을 도식화하면 다음과 같습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;[제가 원했던 검토]
Java 포맷팅 검사 기능을 추가하는 아이디어가
SpecGuard의 목적과 맞는지 의견을 듣고 싶었습니다.
          │
          ▼
[구체적으로 궁금했던 것]
- 프로젝트 목적과 충돌하는가?
- 오버 엔지니어링인가?
- 기능이 많아져 목적성을 잃게 되는가?
- core gate가 맞는가?
- PR Review 보조 기능이 더 맞는가?
- 옵션 기능으로 두는 것이 나은가?
          │
          ▼
[AI가 수행한 검토]
- 현재 코드에 Java 포맷팅 검사 기능이 있는지 검색
- 관련 도구 설정이 있는지 확인
- core gate로 넣는 것은 과하다고 판단
          │
          ▼
[결과]
제가 원했던 다방면의 설계 검토가 아니라,
현재 구현 여부 확인과 제한적인 방향성 판단에 가까운 답변이 나왔습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4383&quot; data-start=&quot;4294&quot; data-ke-size=&quot;size16&quot;&gt;AI가 완전히 엉뚱한 일을 한 것은 아닙니다.&lt;br /&gt;제가 작성한 문장에는 &amp;ldquo;현재 스펙가드에 자바 포맷팅을 검사하는 기능은 어떤지 검토해봐&amp;rdquo;라는 표현이 있었습니다.&lt;/p&gt;
&lt;p data-end=&quot;4416&quot; data-start=&quot;4385&quot; data-ke-size=&quot;size16&quot;&gt;AI 입장에서는 이 문장을 다음처럼 이해할 수 있습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;현재 SpecGuard에 Java 포맷팅 검사 기능이 있는지 확인합니다.
그 기능이 프로젝트 목적과 맞는지 판단합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4517&quot; data-start=&quot;4498&quot; data-ke-size=&quot;size16&quot;&gt;하지만 제 실제 의도는 달랐습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;현재 기능이 없다는 것은 알고 있습니다.
그 기능을 추가하는 아이디어 자체가 프로젝트 방향과 맞는지 의견을 듣고 싶습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4625&quot; data-start=&quot;4601&quot; data-ke-size=&quot;size16&quot;&gt;이 작은 차이가 결과의 차이를 만들었습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-end=&quot;4664&quot; data-start=&quot;4632&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&amp;ldquo;존재 여부 확인&amp;rdquo;과 &amp;ldquo;도입 타당성 검토&amp;rdquo;는 다릅니다&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;4691&quot; data-start=&quot;4666&quot; data-ke-size=&quot;size16&quot;&gt;이번 사례에서 가장 중요한 구분은 이것입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;존재 여부 확인과 도입 타당성 검토는 다릅니다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4761&quot; data-start=&quot;4733&quot; data-ke-size=&quot;size16&quot;&gt;두 작업은 비슷해 보이지만 완전히 다른 질문입니다.&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 126px;&quot; border=&quot;1&quot; data-end=&quot;5102&quot; data-start=&quot;4763&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;질문의 의미&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;기대하는 답변&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;4859&quot; data-start=&quot;4803&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;4814&quot; data-start=&quot;4803&quot;&gt;존재 여부 확인&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;4836&quot; data-start=&quot;4814&quot;&gt;현재 프로젝트에 이 기능이 있는가?&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;4859&quot; data-start=&quot;4836&quot;&gt;관련 코드, 설정, 도구 존재 여부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;4922&quot; data-start=&quot;4860&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;4872&quot; data-start=&quot;4860&quot;&gt;도입 타당성 검토&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;4898&quot; data-start=&quot;4872&quot;&gt;이 기능을 프로젝트에 넣는 것이 적절한가?&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;4922&quot; data-start=&quot;4898&quot;&gt;목적 적합성, 장단점, 리스크, 대안&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;4992&quot; data-start=&quot;4923&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;4934&quot; data-start=&quot;4923&quot;&gt;설계 위치 검토&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;4956&quot; data-start=&quot;4934&quot;&gt;넣는다면 어디에 넣는 것이 맞는가?&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;4992&quot; data-start=&quot;4956&quot;&gt;core gate, PR Review, 옵션 기능 등 비교&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;5052&quot; data-start=&quot;4993&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5005&quot; data-start=&quot;4993&quot;&gt;제품 방향성 검토&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5029&quot; data-start=&quot;5005&quot;&gt;이 기능이 프로젝트 정체성을 흐리는가?&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5052&quot; data-start=&quot;5029&quot;&gt;프로젝트 범위와 목적성 관점의 판단&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;5102&quot; data-start=&quot;5053&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5061&quot; data-start=&quot;5053&quot;&gt;구현 검토&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5081&quot; data-start=&quot;5061&quot;&gt;실제로 어떻게 만들 수 있는가?&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;5102&quot; data-start=&quot;5081&quot;&gt;구현 방식, 테스트, 구조 변경&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5174&quot; data-start=&quot;5104&quot; data-ke-size=&quot;size16&quot;&gt;제가 원했던 것은 존재 여부 확인이 아니었으며 제가 원했던 것은 도입 타당성 검토와 설계 위치 검토였습니다.&lt;/p&gt;
&lt;p data-end=&quot;5208&quot; data-start=&quot;5176&quot; data-ke-size=&quot;size16&quot;&gt;하지만 프롬프트에는 이 차이가 충분히 드러나지 않았습니다.&lt;/p&gt;
&lt;p data-end=&quot;7074&quot; data-start=&quot;7037&quot; data-ke-size=&quot;size16&quot;&gt;개인 OSS 프로젝트를 만들다 보면 기능 아이디어가 계속 생깁니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;이것도 넣으면 좋지 않을까?
저것도 지원하면 좋지 않을까?
이 기능까지 있으면 더 완성도 있어 보이지 않을까?&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7186&quot; data-start=&quot;7151&quot; data-ke-size=&quot;size16&quot;&gt;하지만 모든 좋은 기능이 현재 프로젝트에 좋은 기능은 아닙니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;7320&quot; data-start=&quot;7188&quot;&gt;어떤 기능은 유용하지만 프로젝트의 핵심 목적과는 맞지 않을 수 있습니다.&lt;/li&gt;
&lt;li data-end=&quot;7320&quot; data-start=&quot;7188&quot;&gt;어떤 기능은 사용자에게 도움이 되지만 유지보수 비용을 크게 늘릴 수 있습니다.&lt;/li&gt;
&lt;li data-end=&quot;7320&quot; data-start=&quot;7188&quot;&gt;어떤 기능은 당장 보기에는 좋아 보여도 프로젝트의 정체성을 흐릴 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;7354&quot; data-start=&quot;7322&quot; data-ke-size=&quot;size16&quot;&gt;Java 포맷팅 검사 기능도 그런 유형의 아이디어였습니다.&lt;/p&gt;
&lt;p data-end=&quot;7411&quot; data-start=&quot;7356&quot; data-ke-size=&quot;size16&quot;&gt;기능 자체는 나쁘지 않습니다. 하지만 SpecGuard에 넣었을 때 좋은지는 별개의 문제입니다.&lt;/p&gt;
&lt;p data-end=&quot;7458&quot; data-start=&quot;7413&quot; data-ke-size=&quot;size16&quot;&gt;이때 필요한 것은 단순한 코드 검색이 아니라, 프로젝트 방향성에 대한 검토입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;이 기능이 정말 SpecGuard다운 기능인가?
이 기능이 핵심 문제 해결에 기여하는가?
이 기능이 들어가면 프로젝트 범위가 과도하게 넓어지는가?
이 기능을 넣는다면 어느 레벨에 두어야 하는가?&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7613&quot; data-start=&quot;7582&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저는 AI에게 이런 질문에 대한 의견을 듣고 싶었습니다.&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-end=&quot;7613&quot; data-start=&quot;7582&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-end=&quot;7649&quot; data-start=&quot;7620&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;프롬프트에서 질문의 레이어를 명확히 해야 합니다&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;7694&quot; data-start=&quot;7651&quot; data-ke-size=&quot;size16&quot;&gt;이번 일을 통해 배운 것은 단순히 &amp;ldquo;프롬프트를 자세히 써야 한다&amp;rdquo;가 아닙니다.&lt;/p&gt;
&lt;p data-end=&quot;7712&quot; data-start=&quot;7696&quot; data-ke-size=&quot;size16&quot;&gt;더 정확히는 다음과 같습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;AI에게는 질문의 레이어를 명확히 알려줘야 합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7781&quot; data-start=&quot;7756&quot; data-ke-size=&quot;size16&quot;&gt;개발 작업에서 질문은 여러 레이어로 나뉩니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;[현재 상태 확인]
현재 코드에 이 기능이 있는가?

[아이디어 검토]
이 기능을 넣는 것이 좋은가?

[설계 검토]
넣는다면 어디에 넣는 것이 맞는가?

[구현 검토]
어떻게 구현하는 것이 좋은가?

[실행 요청]
실제로 구현해달라.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7957&quot; data-start=&quot;7928&quot; data-ke-size=&quot;size16&quot;&gt;이번에 제가 원했던 것은 두 번째와 세 번째였습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;[아이디어 검토]
이 기능을 넣는 것이 좋은가?

[설계 검토]
넣는다면 어디에 넣는 것이 맞는가?&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;8092&quot; data-start=&quot;8028&quot; data-ke-size=&quot;size16&quot;&gt;하지만 프롬프트에는 그 구분이 명확하지 않았습니다.&lt;br /&gt;그 결과 AI는 첫 번째인 현재 상태 확인부터 수행했습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-end=&quot;8129&quot; data-start=&quot;8099&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;더 나은 프롬프트는 어떻게 작성할 수 있었을까요?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;8167&quot; data-start=&quot;8131&quot; data-ke-size=&quot;size16&quot;&gt;이번 상황에서 제가 더 정확히 작성했다면 다음과 같았을 것입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;현재 열린 이슈 기준으로 우선순위를 정해주십시오.

그리고 SpecGuard에 Java 포맷팅 검사 기능을 추가하는 아이디어에 대해
프로젝트 방향성 관점에서 의견을 듣고 싶습니다.

중요한 점은, 현재 코드에 해당 기능이 있는지 확인하는 것이 아닙니다.
현재 이 기능이 없다는 것은 알고 있습니다.

제가 알고 싶은 것은 다음입니다.

1. Java 포맷팅 검사가 SpecGuard의 핵심 목적과 맞는지
2. 이 기능을 추가하면 프로젝트 범위가 과도하게 넓어지는지
3. 오버 엔지니어링이 될 가능성이 있는지
4. 기능이 많아지면서 프로젝트의 목적성이 흐려질 위험이 있는지
5. 만약 추가한다면 core gate로 넣는 것이 맞는지
6. 아니면 PR Review 단계의 보조 피드백으로 넣는 것이 맞는지
7. 옵션 기능으로 제공하는 방식은 어떤지
8. 아예 외부 포맷팅 도구 사용을 권장하는 편이 나은지

코드는 수정하지 말고, 설계적 의견과 장단점 비교를 중심으로 답변해주십시오.

답변은 다음 형식으로 정리해주십시오.

1. 결론
2. 프로젝트 목적과의 적합성
3. core gate로 넣는 경우의 장단점
4. PR Review에 넣는 경우의 장단점
5. 옵션 기능으로 제공하는 경우의 장단점
6. 추가하지 않는 경우의 장단점
7. 최종 추천 방향&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;8904&quot; data-start=&quot;8826&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 작성했다면 AI가 현재 코드에 기능이 있는지 검색하는 데 시간을 쓰기보다, 제가 원했던 방향성 검토에 더 집중했을 가능성이 높습니다.&lt;/p&gt;
&lt;p data-end=&quot;8904&quot; data-start=&quot;8826&quot; data-ke-size=&quot;size16&quot;&gt;짧게 작성한다면 이렇게 작성할 수 있을 것 같습니다. 짧게 작성해야 한다면 다음 정도로도 충분히 의도를 줄일 수 있습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;현재 열린 이슈 기준으로 우선순위를 정해주십시오.

그리고 SpecGuard에 Java 포맷팅 검사 기능을 추가하는 아이디어가
프로젝트 목적에 맞는지 설계 관점에서 검토해주십시오.

현재 코드에 해당 기능이 있는지 확인하려는 것은 아닙니다.
해당 기능이 없다는 것은 알고 있습니다.

제가 궁금한 것은 이 기능이 오버 엔지니어링인지,
프로젝트의 목적성을 흐릴 위험이 있는지,
넣는다면 core gate가 맞는지 PR Review 보조 기능이 맞는지입니다.

코드는 수정하지 말고 의견만 정리해주십시오.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;9316&quot; data-start=&quot;9299&quot; data-ke-size=&quot;size16&quot;&gt;여기서 핵심 문장은 다음입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;현재 코드에 해당 기능이 있는지 확인하려는 것은 아닙니다.
해당 기능이 없다는 것은 알고 있습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;9424&quot; data-start=&quot;9387&quot; data-ke-size=&quot;size16&quot;&gt;이 문장 하나만 있었어도 AI의 작업 방향은 달라졌을 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;9424&quot; data-start=&quot;9387&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-end=&quot;9462&quot; data-start=&quot;9431&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;&amp;ldquo;하지 않아도 되는 일&amp;rdquo;을 알려주는 것도 중요합니다&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;9515&quot; data-start=&quot;9464&quot; data-ke-size=&quot;size16&quot;&gt;에이전트 AI에게는 해야 할 일뿐만 아니라, 하지 않아도 되는 일도 알려주는 것이 좋습니다.&lt;/p&gt;
&lt;p data-end=&quot;9544&quot; data-start=&quot;9517&quot; data-ke-size=&quot;size16&quot;&gt;이번 사례에서는 다음과 같은 문장이 필요했습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;현재 코드에 해당 기능이 있는지 검색하지 않아도 됩니다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;구현하지 않아도 됩니다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;코드 변경은 하지 말고, 설계적 의견만 주세요.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;core gate로 넣을지 여부를 단정하지 말고,
PR Review, 옵션 기능, 외부 도구 연동 등 여러 선택지를 비교해주세요.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;9821&quot; data-start=&quot;9744&quot; data-ke-size=&quot;size16&quot;&gt;사람에게는 이런 말을 굳이 하지 않아도 될 수 있습니다.&lt;br /&gt;하지만 AI에게는 이런 제약이 작업 범위를 명확히 하는 데 큰 도움이 됩니다.&lt;/p&gt;
&lt;p data-end=&quot;9850&quot; data-start=&quot;9823&quot; data-ke-size=&quot;size16&quot;&gt;좋은 프롬프트는 &amp;ldquo;할 일&amp;rdquo;만 적는 것이 아닙니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;할 일
하지 않아도 되는 일
하지 말아야 할 일
비교해야 할 선택지
답변 형식&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;9940&quot; data-start=&quot;9909&quot; data-ke-size=&quot;size16&quot;&gt;이 다섯 가지를 함께 정리하면 결과가 훨씬 안정적입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-end=&quot;9940&quot; data-start=&quot;9909&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-end=&quot;9976&quot; data-start=&quot;9947&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;에이전트 AI와 협업할 때 필요한 프롬프트 구조&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;10038&quot; data-start=&quot;9978&quot; data-ke-size=&quot;size16&quot;&gt;이번 사례를 바탕으로, 설계 의견을 듣고 싶을 때 사용할 수 있는 프롬프트 구조를 정리하면 다음과 같습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;목표
- [기능 아이디어]가 프로젝트에 적절한지 의견을 듣고 싶습니다.

전제
- 현재 해당 기능이 없다는 것은 알고 있습니다.
- 구현을 요청하는 것이 아닙니다.
- 코드 검색보다 설계적 판단이 필요합니다.

검토 관점
1. 프로젝트 목적과 맞는지
2. 오버 엔지니어링 가능성이 있는지
3. 기능 범위가 과도하게 넓어지는지
4. 유지보수 비용이 커지는지
5. 사용자에게 실제 가치가 있는지

비교할 선택지
1. 추가하지 않음
2. core gate로 추가
3. PR Review 보조 피드백으로 추가
4. 옵션 기능으로 추가
5. 외부 도구 연동 또는 문서 안내로 처리

답변 형식
1. 결론
2. 선택지별 장단점
3. 추천 방향
4. 추천하지 않는 방향과 이유
5. 나중에 다시 검토할 조건&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;10524&quot; data-start=&quot;10447&quot; data-ke-size=&quot;size16&quot;&gt;이런 식으로 작성하면 AI가 &amp;ldquo;현재 코드에 있는지 확인해야 하나?&amp;rdquo; 또는 &amp;ldquo;바로 구현해야 하나?&amp;rdquo; 같은 방향으로 빠질 가능성이 줄어듭니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-end=&quot;10547&quot; data-start=&quot;10531&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이번 사례에서 얻은 교훈&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;10573&quot; data-start=&quot;10549&quot; data-ke-size=&quot;size16&quot;&gt;이번 일을 통해 느낀 점은 다음과 같습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;AI에게 &amp;ldquo;검토해줘&amp;rdquo;라고 말하면 AI는 자신이 생각하는 검토를 수행합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;10670&quot; data-start=&quot;10631&quot; data-ke-size=&quot;size16&quot;&gt;그 검토가 제가 원하는 검토와 같을 수도 있지만, 다를 수도 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;10690&quot; data-start=&quot;10672&quot; data-ke-size=&quot;size16&quot;&gt;제가 원했던 것은 다음이었습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;1. 기능 아이디어의 도입 타당성 검토
2. 프로젝트 목적과의 적합성 판단
3. core gate와 PR Review의 비교
4. 오버 엔지니어링 가능성 검토
5. 기능 범위 확장에 따른 리스크 분석&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;10828&quot; data-start=&quot;10802&quot; data-ke-size=&quot;size16&quot;&gt;하지만 AI가 수행한 것은 다음에 가까웠습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;1. 현재 코드에 해당 기능이 있는지 확인
2. 관련 포맷팅 도구 존재 여부 검색
3. core gate로 넣는 것은 과할 수 있다는 단일 방향 제안&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;10969&quot; data-start=&quot;10918&quot; data-ke-size=&quot;size16&quot;&gt;둘 다 &amp;ldquo;검토&amp;rdquo;라는 단어 안에 들어갈 수 있기 때문에&amp;nbsp;더더욱 프롬프트가 중요합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-end=&quot;10969&quot; data-start=&quot;10918&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;결론&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;11081&quot; data-start=&quot;10983&quot; data-ke-size=&quot;size16&quot;&gt;오늘 SpecGuard를 진행하면서 겪은 일은 큰 문제는 아니었습니다.&lt;br /&gt;하지만 에이전트 AI와 협업할 때 프롬프트를 어떻게 작성해야 하는지 다시 생각하게 만든 사례였습니다.&lt;/p&gt;
&lt;p data-end=&quot;11168&quot; data-start=&quot;11083&quot; data-ke-size=&quot;size16&quot;&gt;저는 Java 포맷팅 검사 기능을 구현해 달라고 한 것이 아니었습니다.&lt;br /&gt;또한 현재 프로젝트에 해당 기능이 있는지 확인해 달라고 한 것도 아니었습니다.&lt;/p&gt;
&lt;p data-end=&quot;11294&quot; data-start=&quot;11170&quot; data-ke-size=&quot;size16&quot;&gt;제가 원했던 것은 이 기능 아이디어가 SpecGuard의 목적과 맞는지, 추가한다면 core gate가 맞는지 PR Review 보조 기능이 맞는지, 혹은 아예 넣지 않는 편이 나은지에 대한 다방면의 의견이었습니다.&lt;/p&gt;
&lt;p data-end=&quot;11335&quot; data-start=&quot;11296&quot; data-ke-size=&quot;size16&quot;&gt;하지만 제가 작성한 프롬프트에는 매우 짧았으며, 대충 적었고, 그 의도가 충분히 명확하지 않았습니다.&lt;/p&gt;
&lt;p data-end=&quot;11407&quot; data-start=&quot;11337&quot; data-ke-size=&quot;size16&quot;&gt;그 결과 AI는 현재 코드에 해당 기능이 있는지 먼저 확인했고, core gate로 넣는 것은 과하다는 방향으로 답변했습니다.&lt;/p&gt;
&lt;p data-end=&quot;11476&quot; data-start=&quot;11409&quot; data-ke-size=&quot;size16&quot;&gt;AI가 이상한 일을 한 것은 아닙니다.&lt;br /&gt;제가 원하는 검토의 종류를 충분히 구체적으로 설명하지 않은 것이 문제였습니다.&lt;/p&gt;
&lt;p data-end=&quot;11540&quot; data-start=&quot;11478&quot; data-ke-size=&quot;size16&quot;&gt;에이전트 AI는 강력합니다.&lt;br /&gt;코드를 읽고, 이슈를 확인하고, 문서를 분석하고, 설계 의견도 줄 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;11566&quot; data-start=&quot;11542&quot; data-ke-size=&quot;size16&quot;&gt;하지만 강력한 도구일수록 지시가 중요합니다.&lt;/p&gt;
&lt;p data-end=&quot;11602&quot; data-start=&quot;11568&quot; data-ke-size=&quot;size16&quot;&gt;특히 &amp;ldquo;검토해줘&amp;rdquo;라는 표현을 사용할 때는 더 조심해야 합니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;현재 상태를 확인해 달라는 것인지
아이디어의 타당성을 검토해 달라는 것인지
구현 방식을 비교해 달라는 것인지
실제로 구현해 달라는 것인지
코드는 수정하지 말라는 것인지
여러 선택지를 비교해 달라는 것인지&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;11748&quot; data-start=&quot;11731&quot; data-ke-size=&quot;size16&quot;&gt;이 차이를 명확히 해야 합니다.&lt;/p&gt;
&lt;p data-end=&quot;11830&quot; data-start=&quot;11750&quot; data-ke-size=&quot;size16&quot;&gt;좋은 프롬프트는 단순히 긴 문장이 아닙니다.&lt;br /&gt;좋은 프롬프트는 AI가 엉뚱한 방향으로 성실하게 움직이지 않도록 작업 범위를 정리한 문장입니다.&lt;/p&gt;
&lt;p data-end=&quot;11877&quot; data-start=&quot;11832&quot; data-ke-size=&quot;size16&quot;&gt;결국 에이전트 AI 시대의 개발자에게 필요한 역량은 코드 작성 능력만이 아닙니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;질문을 정확히 정의하는 능력
검토 범위를 명확히 하는 능력
비교할 선택지를 제시하는 능력
하지 않아도 되는 일을 제외하는 능력
원하는 답변 형식을 지정하는 능력&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;12006&quot; data-start=&quot;11982&quot; data-ke-size=&quot;size16&quot;&gt;이런 능력들이 점점 더 중요해지고 있다고 생각합니다.&lt;/p&gt;
&lt;p data-end=&quot;12052&quot; data-start=&quot;12008&quot; data-ke-size=&quot;size16&quot;&gt;AI는 의도를 알아서 읽지 않으며, AI는 프롬프트를 기준으로 작업합니다.&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;12097&quot; data-start=&quot;12054&quot; data-ke-size=&quot;size16&quot;&gt;따라서 원하는 답을 얻고 싶다면, 원하는 검토의 범위부터 정확히 써야 합니다.&lt;/p&gt;</description>
      <category>AI Engineering/Methodology</category>
      <author>KoreaNirsa</author>
      <guid isPermaLink="true">https://nirsa.tistory.com/485</guid>
      <comments>https://nirsa.tistory.com/485#entry485comment</comments>
      <pubDate>Sun, 10 May 2026 23:07:59 +0900</pubDate>
    </item>
    <item>
      <title>AI 시대, 왜 소프트웨어 기본기가 더 중요해졌는가?</title>
      <link>https://nirsa.tistory.com/484</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;※ 해당 포스팅은 Matt pocock의 발표 영상(&lt;a style=&quot;color: #ee2323;&quot; href=&quot;https://youtu.be/v4F1gFy-hqg?si=7M0lPIfwYiBs4Yfs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://youtu.be/v4F1gFy-hqg?si=7M0lPIfwYiBs4Yfs&lt;/a&gt;)을 정리하고, 저의 생각을 추가한 작성한 글입니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;AI&amp;nbsp;시대,&amp;nbsp;왜&amp;nbsp;소프트웨어&amp;nbsp;기본기가&amp;nbsp;더&amp;nbsp;중요해졌는가?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 AI 코딩 도구가 빠르게 확산 되면서 개발 방식도 크게 달라지고 있습니다. 이제는 개발자가 직접 모든 코드를 작성하지 않아도 AI에게 요구사항을 전달하고 코드를 생성하게 만들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Matt Pocock은 이런 흐름 속에서도 소프트웨어 기본기, 즉 Software Fundamentals가 그 어느때보다 중요해졌다고 강조합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 단순한데, &quot;AI에게 스펙을 전달하고 코드를 생성하게 하는 방식만으로는 좋은 시스템을 만들기 어렵다&quot; 입니다. 코드베이스 전체의 설계를 고려하지 않은 채 기능 추가와 수정만 반복하면 Software Entropy가 발생하고 시스템은 점점 이해하기 어렵고 변경하기 어려운 구조로 변해 결국 유지보수가 힘든 스파게티 코드가 만들어집니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;※ Software Fundamentals : 소프트웨어 개발의 기본기&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;※ Software Entropy : 시간이 지날수록 시스템 점점 무질서해지고, 이해하기 어렵고, 변경하기 어려운 상태로 변해가는 현산&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;AI는 전술적 프로그래머, 개발자는 전략적 설계자&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영상에서는 AI를 뛰어난 전술적 프로그래머로 바라봅니다. AI는 구체적인 코드 작성, 구현 세부 사항 처리, 반복 작업 수행에는 강한 강점을 보이지만 시스템 전체의 방향을 결정하고, 모듈간 관계를 설계하고, 유지보수가 가능한 구조를 만드는 책임은 개발자에 있다라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 AI 시대의 개발자는 코드를 더 많이 작성하는 사람이 아니라, AI가 좋은 코드를 작성할 수 있는 환경을 설계하는 사람에 더욱 가까워지고 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 영상에서 말하는 전체적인 워크플로우는 아래와 같은 형태를 가집니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;요구사항 전달 &amp;rarr; AI와 공통 이해 형성 &amp;rarr; 용어와 설계 개념 정리 &amp;rarr; 작은 단위로 테스트하며 구현(TDD) &amp;rarr; 딥 모듈로 복잡성 은닉 &amp;rarr; 인터페이스는 개발자가, 구현은 AI에게 위임 &amp;rarr; 유지보수 가능한 코드 베이스 확보&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;1. AI와 먼저 공통된 설계 개념을 맞춰야 한다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI와 협업할 때 자주 발생하는 문제는 &quot;AI가 내가 원한 대로 하지 않았다&quot;라는 것 입니다. 이 문제는 단순한 프롬프트 문제가 아니라, 개발자와 AI가 서로 다른 설계 개념을 가지고 있기 때문에 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영상에서는 이를 해결하기 위한 방법으로 Grill Me 기법이 소개되는데, 이 방식은 AI에게 다음과 같은 역할을 부여하는 것 입니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&quot;우리가 공유된 이해에 도달할 때까지 이 계획의 모든 측면에 대해 계속 질문하라&quot;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 요구사항, 의존 관계, 설계 의도를 계속 파고들게 만들면 개발자와 AI 사이의 생각 차이를 줄여나갈 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;2. 유비쿼터스 언어로 AI와 같은 단어를 사용한다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 영상에서는 도메인 주도 설계의 유비쿼터스 언어 개념도 중요하게 다루고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 기획자와 개발자 간의 용어 간극을 메우기 위한 방법이였다면, 이 포스텡어슨ㄴ 현재는 AI와 개발자 간의 간극을 메우기 위한 방법으로써 활용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI와 개발자가 같은 단어를 다른 의미로 사용하면 결과물을 쉽게 어긋나기 때문에, 코드 베이스에서 사용하는 핵심 용어를 마크다운 파일 등으로 정리하고 AI가 계획 수립과 코딩 과정에서 이 용어를 기준으로 생각하게 만드는 것이 필요합니다.&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;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.7752%;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 38.217%;&quot;&gt;문제 상황&lt;/td&gt;
&lt;td style=&quot;width: 46.0077%;&quot;&gt;해결 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.7752%;&quot;&gt;설계 개념&lt;/td&gt;
&lt;td style=&quot;width: 38.217%;&quot;&gt;개발자와 AI가 서로 다른 결과를 상상함&lt;/td&gt;
&lt;td style=&quot;width: 46.0077%;&quot;&gt;Grill Me 기법으로 질문을 유도하여 공통 이해 형성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.7752%;&quot;&gt;용어&lt;/td&gt;
&lt;td style=&quot;width: 38.217%;&quot;&gt;같은 단어를 다른 의미로 해석함&lt;/td&gt;
&lt;td style=&quot;width: 46.0077%;&quot;&gt;유비쿼터스 언어를 마크다운 파일로 정리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.7752%;&quot;&gt;구현 방향&lt;/td&gt;
&lt;td style=&quot;width: 38.217%;&quot;&gt;AI가 설계 의도와 다른 코드를 작성함&lt;/td&gt;
&lt;td style=&quot;width: 46.0077%;&quot;&gt;용어집과 설계 개념을 계속 참조하게 함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;여기서 말하는 용어집은 말 그대로 용어들을 정리하여 개발자와 AI가 용어에 의한 차이를 보이지 않도록 하는 것입니다.&lt;br /&gt;&lt;br /&gt;# 용어집 예시&lt;br /&gt;수강생(Student) &lt;br /&gt;&amp;nbsp; - 학원에서 강의를 신청하고 수강하는 사용자&lt;br /&gt;강사(Instructor)&lt;br /&gt;&amp;nbsp; - 강의를 개설하고 수업을 진행하는 사용자&lt;br /&gt;강의(Course)&lt;br /&gt;&amp;nbsp; - 특정 주제와 기간을 가진 수업 단위 &lt;br /&gt;수강신청(Enrollment)&lt;br /&gt;&amp;nbsp; - 수강생이 특정 강의에 참여 신청하는 행위&lt;br /&gt;정원(Capacity)&lt;br /&gt;&amp;nbsp; - 하나의 강의에 등록 가능한 최대 수강생 수&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;3. TDD로 AI의 작업 단위를 작게 유지한다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 AI를 사용하며 개발을 하시는 분들은 &quot;한번에 여러가지의 작업을 요청하지 마라&quot; 라는 말을 들어보셨을겁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 때때로 너무 많은 코드를 한 번에 작성하려 하는데, 이를 '헤드라이트보다 빨리 달리는 상황'으로 설명합니다. 코드가 빠르게 생성되더라도 검증 없이 많은 변경이 쌓이면 개발자는 오히려 결과를 따라가기 힘들어지고, 이는 오히려 개발자가 병목의 원인이 되는 이유로 이어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영상에서는 이를 제어하기 위한 핵심 방법으로 TDD를 소개하고, 이는 저의 개인적인 AI 이후 개발 방법론과 정확하게 일치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 사람이 TDD를 작성할때는 오히려 테스트 코드를 작성하느라 개발 속도가 느려지고, 테스트 코드가 '주'가 되면서 테스트 코드의 품질에 신경쓰다보니 실제 개발의 코드 품질이 오히려 낮아지며 대부분의 TDD는 실패한 TDD인 상황이 많았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 AI 시대에서 코드의 작성을 AI에게 위임하게 되면서 코드 생산성이 수십배 빨라진 상황에서, TDD는 오히려 AI와 함께 사용함으로써 더욱 많은 장점을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD를 사용함으로써 AI가 작은 단계로 움직이게 만듭니다. 테스트를 먼저 작성하고, 작은 단위로 구현하고, 다시 리팩터링하는 흐름은 AI의 코드 생성을 사람이 검증 가능한 단위로 제한할 수 있습니다.&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;4. 딥 모듈로 복잡성을 숨긴다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영상에서 강조하는 또 하나의 핵심은 딥 모듈(Deep Modules) 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딥 모듈은 많은 기능과 복잡성을 내부에 숨기고 외부에는 단순한 인터페이스만 노출하는 구조입니다. 이미 좋은 메이저 오픈소스들은 사용자가 내부 구현을 몰라도 사용할 수 있도록 복잡한 구현을 단순한 public interface 뒤에 숨기는 경우가 많습니다. 이는 딥 모듈의 핵심과 일치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 얕은 모듈(Shallow Modules)은 기능이 너무 작은 조각 단위로 흩어져 있어 AI가 코드의 맥락을 파악하기 어렵게 하며 AI는 수많은 파일과 함수, 의존 관계를 따라가야 하고 코드 베이스의 목적과 경계를 놓칠 수 있습니다.&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;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.2868%;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 35.0775%;&quot;&gt;얕은 모듈&lt;/td&gt;
&lt;td style=&quot;width: 47.6356%;&quot;&gt;딥 모듈&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.2868%;&quot;&gt;구조&lt;/td&gt;
&lt;td style=&quot;width: 35.0775%;&quot;&gt;작은 조각이 많이 흩어져 있음&lt;/td&gt;
&lt;td style=&quot;width: 47.6356%;&quot;&gt;관련 기능을 하나의 모듈로 묶음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.2868%;&quot;&gt;인터페이스&lt;/td&gt;
&lt;td style=&quot;width: 35.0775%;&quot;&gt;외부에서 알아야 할 것이 많음&lt;/td&gt;
&lt;td style=&quot;width: 47.6356%;&quot;&gt;외부 인터페이스가 단순함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.2868%;&quot;&gt;복잡성&lt;/td&gt;
&lt;td style=&quot;width: 35.0775%;&quot;&gt;외부로 노출되기 쉬움&lt;/td&gt;
&lt;td style=&quot;width: 47.6356%;&quot;&gt;내부로 캡슐화됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.2868%;&quot;&gt;AI 협업&lt;/td&gt;
&lt;td style=&quot;width: 35.0775%;&quot;&gt;맥락 추적이 어려움&lt;/td&gt;
&lt;td style=&quot;width: 47.6356%;&quot;&gt;모듈의 목적과 경계를 이해하기 쉬움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.2868%;&quot;&gt;테스트&lt;/td&gt;
&lt;td style=&quot;width: 35.0775%;&quot;&gt;검증 지점이 분산됨&lt;/td&gt;
&lt;td style=&quot;width: 47.6356%;&quot;&gt;입출력 경계 기준으로 테스트 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;5. 인터페이스는 개발자가 설계하고 구현은 AI에게 위임한다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 AI 시대의 개발자들은 모든 구현을 직접 작성할 필요가 없습니다. 인터페이스는 개발자가 직접 설계하고, 구현은 AI에게 위임하는 방식을 제안합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 접근은 개발자의 인지 부하를 줄여줍니다. 개발자는 모듈의 경계, 입출력, 테스트 가능성, 시스템 전체 방향에 집중하고 AI는 그 안쪽 구현을 담당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 전략적 프로그래머의 역할입니다.즉, 앞으로는 더욱 더 코드를 잘 작성하는 개발자보다는 아키텍처의 관점에서 코드를 잘 설계하고 AI에게 구현을 위임할 수 있는 개발자가 되어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;결론&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI의 압도적인 생산성으로 인해 개발자가 모든 코드를 직접 치던 시대는 점점 끝나가고 있습니다. 그러나 좋은 코드베이스와 좋은 설계가 없다면 AI는 오히려 소프트웨어 엔트로피를 빠르게 증가시킵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 시대에 중요한 것은 코드를 얼마나 많이 생성하고 빠르게 기능을 구현하느냐가 아니라, AI가 올바른 코드를 생성할 수 있도록 시스템을 설계하는 능력입니다. 이제 개발자는 코드를 직접 구현하는 포지션이 아니라, 전략적 설계자로써 코드베이스의 품질을 지켜야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 개발자들은 이러한 AI의 압도적은 생산성으로 인해 소프트웨어 기본기보다는 화려한 기술들을 향해 가고 있지만, 역설적이게도 AI의 압도적인 생산성으로 인해 소프트웨어의 기본기가 점점 더 중요해지고 있습니다.&lt;/p&gt;</description>
      <category>AI Engineering/Methodology</category>
      <author>KoreaNirsa</author>
      <guid isPermaLink="true">https://nirsa.tistory.com/484</guid>
      <comments>https://nirsa.tistory.com/484#entry484comment</comments>
      <pubDate>Sat, 2 May 2026 23:19:29 +0900</pubDate>
    </item>
    <item>
      <title>[이런 저런 이야기] 2025 표준프레임워크 컨트리뷰션 후기 (feat. 굿즈 개봉)</title>
      <link>https://nirsa.tistory.com/483</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;[이런&amp;nbsp;저런&amp;nbsp;이야기]&amp;nbsp;2025&amp;nbsp;표준프레임워크&amp;nbsp;컨트리뷰션&amp;nbsp;후기&amp;nbsp;(feat.&amp;nbsp;굿즈&amp;nbsp;개봉)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해는 2025 표준프레임워크 컨트리뷰션에 참여하였고 오늘 감사장과 굿즈를 배송 받아 후기를 남깁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;1. 참여한 계기&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해는 유독 일을 오래 쉬었다보니 &lt;s&gt;시간 부자가 되어서&lt;/s&gt; 평소에 관심있던 오픈소스에 기여를 해보자라는 생각을 가지게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그중에서 spring-boot, spring-petclinic, spring-ai, 표준프레임워크 이렇게 4가지 정도로 정리가 되었는데, 결과적으로는 spring-ai와 표준 프레임워크를 선택하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spring-ai는 학습하는 과정에서 일부 중복이나 성능 개선이 가능한 코드를 &lt;s&gt;아주 조금&lt;/s&gt; 수정하고 표준프레임워크에서는 레거시한 코드 구조와 스웨거를 명확히 출력 하도록 개선하기 위한 작업을 진행하였습니다. 처음부터 너무 많은 것을 바꾸려 하면 현실적으로 불가능하다 판단되어 우선은 패키지 구조와 요청/응답 데이터를 도입하여 유지보수성 및 스웨거를 출력하는 것이 1차 목표였어요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;621&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b60PHT/dJMcaiInI9B/JozEZLyZOHMmETToyPvzs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b60PHT/dJMcaiInI9B/JozEZLyZOHMmETToyPvzs1/img.png&quot; data-alt=&quot;올해 진행한 PR들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b60PHT/dJMcaiInI9B/JozEZLyZOHMmETToyPvzs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb60PHT%2FdJMcaiInI9B%2FJozEZLyZOHMmETToyPvzs1%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;757&quot; height=&quot;621&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;621&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;올해 진행한 PR들&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;455&quot; data-origin-height=&quot;237&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYOg0l/dJMcac9eE8U/JDlfU9UDlK4yVx3Nev1yqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYOg0l/dJMcac9eE8U/JDlfU9UDlK4yVx3Nev1yqk/img.png&quot; data-alt=&quot;기여하다보니 커밋 기준 #1 컨트리뷰터 ?!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYOg0l/dJMcac9eE8U/JDlfU9UDlK4yVx3Nev1yqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYOg0l%2FdJMcac9eE8U%2FJDlfU9UDlK4yVx3Nev1yqk%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;455&quot; height=&quot;237&quot; data-origin-width=&quot;455&quot; data-origin-height=&quot;237&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기여하다보니 커밋 기준 #1 컨트리뷰터 ?!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;2. 아쉬운 것들&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표준프레임워크 simple-backend 템플릿에서 bbs에 관련된 것은 컨트리뷰션이 종료되기 전 모두 진행하려 했으나, 아무래도 매일 이것만을 잡고 있을 순 없기 때문에 몇 개의 메서드를 아직 개선하지 못한채로 종료된 것이 아쉽네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 요청 데이터나 응답 데이터를 Map 형태로 주고 받고 모든 필드를 사용하며, 하나를 수정하게 되면 다른 의존하는 코드들이 에러를 뿜어대다보니 조금의 수정도 생각보다 많은 시간을 투자해야 했습니다 ㅠㅠ&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 컨트리뷰션이 종료된 후에도 simple-backend는 구조 개선을 목표로 여유가 있을 때 지속적으로 기여를 할 생각이고 최종적으로 구조 개선 후에는 성능 개선, 불필요한 로직, 미사용 필드를 제거하는 과정을 진행해보고 싶네요.&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;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;3. 굿즈 개봉 후기&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 감사장이 너무 귀여운 형태로 왔습니다 ㅋㅋ 상장같은 느낌일줄 알았는데 저의 깃허브 닉네임이 박힌 피규어와 함께 손바닥 크기 정도의 피규어 감사장이 도착했어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;피규어는 구글 스터디잼 이후로 처음 받아보는데, 피규어 감사장이 너무 귀여워서 제 책상에 올려두었습니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20251101_232325947_01.jpg&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;4000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dUfaCu/dJMcajHh2p4/bduXkJTpxiMYIkGNMnpkqk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dUfaCu/dJMcajHh2p4/bduXkJTpxiMYIkGNMnpkqk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dUfaCu/dJMcajHh2p4/bduXkJTpxiMYIkGNMnpkqk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdUfaCu%2FdJMcajHh2p4%2FbduXkJTpxiMYIkGNMnpkqk%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;444&quot; height=&quot;592&quot; data-filename=&quot;KakaoTalk_20251101_232325947_01.jpg&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;4000&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;906&quot; data-origin-height=&quot;680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dXjc1S/dJMcaiO9msa/SlMnNUEKnWvy0mKLLbXgok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dXjc1S/dJMcaiO9msa/SlMnNUEKnWvy0mKLLbXgok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dXjc1S/dJMcaiO9msa/SlMnNUEKnWvy0mKLLbXgok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdXjc1S%2FdJMcaiO9msa%2FSlMnNUEKnWvy0mKLLbXgok%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;623&quot; height=&quot;468&quot; data-origin-width=&quot;906&quot; data-origin-height=&quot;680&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;</description>
      <category>이런 저런 이야기</category>
      <author>KoreaNirsa</author>
      <guid isPermaLink="true">https://nirsa.tistory.com/483</guid>
      <comments>https://nirsa.tistory.com/483#entry483comment</comments>
      <pubDate>Sun, 2 Nov 2025 00:07:53 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] Route53 + CloudFront + S3 + ACM 정적 페이지 배포하기 (HTTPS 배포, 가비아 도메인 연동)</title>
      <link>https://nirsa.tistory.com/482</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;[AWS]&amp;nbsp;Route53&amp;nbsp;+&amp;nbsp;CloudFront&amp;nbsp;+&amp;nbsp;S3&amp;nbsp;+&amp;nbsp;ACM&amp;nbsp;정적&amp;nbsp;페이지&amp;nbsp;배포하기&amp;nbsp;(HTTPS&amp;nbsp;배포,&amp;nbsp;가비아&amp;nbsp;도메인&amp;nbsp;연동)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS의 Route53, CloudFront, S3, ACM를 사용하여 정적 페이지를 배포하는 방법입니다. 저는 미리 가비아에서 도메인을 구입해둔 상태이기 때문에 해당 도메인을 연동하고 ACM으로 인증서를 발급하여 HTTPS로 배포하도록 하겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;1. S3 버킷 생성&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 가장 먼저 AWS에 로그인하여 콘솔에서 s3를 검색한 후 클릭하여 서비스에 들어가주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1049&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVQmLU/btsQB0pvwN8/bgqo3QKtigkVLrpqN6XaQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVQmLU/btsQB0pvwN8/bgqo3QKtigkVLrpqN6XaQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVQmLU/btsQB0pvwN8/bgqo3QKtigkVLrpqN6XaQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVQmLU%2FbtsQB0pvwN8%2Fbgqo3QKtigkVLrpqN6XaQ1%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;1049&quot; height=&quot;363&quot; data-origin-width=&quot;1049&quot; data-origin-height=&quot;363&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1353&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sxndD/btsQDc3JZgR/PSz6mUWEAnKV7tsqcC0j71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sxndD/btsQDc3JZgR/PSz6mUWEAnKV7tsqcC0j71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sxndD/btsQDc3JZgR/PSz6mUWEAnKV7tsqcC0j71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsxndD%2FbtsQDc3JZgR%2FPSz6mUWEAnKV7tsqcC0j71%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;1353&quot; height=&quot;416&quot; data-origin-width=&quot;1353&quot; data-origin-height=&quot;416&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;버킷 이름을 작성하고 다른 옵션들은 그대로 둔 채 우측 하단의 &quot;버킷 만들기&quot;를 클릭 해주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1616&quot; data-origin-height=&quot;766&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDzKBE/btsQBZKMxgg/Ib2hvKCB8wZjsDEaAadLY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDzKBE/btsQBZKMxgg/Ib2hvKCB8wZjsDEaAadLY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDzKBE/btsQBZKMxgg/Ib2hvKCB8wZjsDEaAadLY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDzKBE%2FbtsQBZKMxgg%2FIb2hvKCB8wZjsDEaAadLY0%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;1616&quot; height=&quot;766&quot; data-origin-width=&quot;1616&quot; data-origin-height=&quot;766&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;이후 생성된 버킷을 클릭하고 업로드 하려는 파일을 마우스로 올려서 업로드를 진행한 뒤 우측 하단의 &quot;업로드&quot;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1099&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baSet6/btsQEELHqGN/F7hkMdtK2jeCe4PrTTF6j0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baSet6/btsQEELHqGN/F7hkMdtK2jeCe4PrTTF6j0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baSet6/btsQEELHqGN/F7hkMdtK2jeCe4PrTTF6j0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaSet6%2FbtsQEELHqGN%2FF7hkMdtK2jeCe4PrTTF6j0%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;1099&quot; height=&quot;268&quot; data-origin-width=&quot;1099&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1485&quot; data-origin-height=&quot;783&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biSmbt/btsQDGcuVZR/6qSSaTJSIxCCtqwH7CmKOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biSmbt/btsQDGcuVZR/6qSSaTJSIxCCtqwH7CmKOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biSmbt/btsQDGcuVZR/6qSSaTJSIxCCtqwH7CmKOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiSmbt%2FbtsQDGcuVZR%2F6qSSaTJSIxCCtqwH7CmKOk%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;1485&quot; height=&quot;783&quot; data-origin-width=&quot;1485&quot; data-origin-height=&quot;783&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;2. AWS CloudFront&amp;nbsp;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 다시 콘솔에서 cloudfront를 검색하여 해당 서비스를 클릭한 후 &quot;CloudFront 배포 생성&quot;을 클릭해주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;953&quot; data-origin-height=&quot;175&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lDWYR/btsQFdaiHpf/tzled6rfZGxkwTkNktnh11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lDWYR/btsQFdaiHpf/tzled6rfZGxkwTkNktnh11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lDWYR/btsQFdaiHpf/tzled6rfZGxkwTkNktnh11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlDWYR%2FbtsQFdaiHpf%2Ftzled6rfZGxkwTkNktnh11%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;953&quot; height=&quot;175&quot; data-origin-width=&quot;953&quot; data-origin-height=&quot;175&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;338&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uCqt1/btsQErfx6qL/lqU0rFVh2V4b1xkCaTuah0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uCqt1/btsQErfx6qL/lqU0rFVh2V4b1xkCaTuah0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uCqt1/btsQErfx6qL/lqU0rFVh2V4b1xkCaTuah0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuCqt1%2FbtsQErfx6qL%2FlqU0rFVh2V4b1xkCaTuah0%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;1230&quot; height=&quot;338&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;338&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;배포 이름은 편하신데로 임의로 작성한후 하단의 Next를 눌러줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;747&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfWEca/btsQEdWaTUS/KkxW9FuftzWLoXRukkZIFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfWEca/btsQEdWaTUS/KkxW9FuftzWLoXRukkZIFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfWEca/btsQEdWaTUS/KkxW9FuftzWLoXRukkZIFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfWEca%2FbtsQEdWaTUS%2FKkxW9FuftzWLoXRukkZIFK%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;729&quot; height=&quot;747&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;747&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 포스팅은 S3를 기반으로 배포하기 때문에 Origin type을 Amazone S3로 지정해주고, 아래의 Browse S3를 클릭하여 버킷을 연결해주시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Origin path - optional는 선택 사항이며 저는 S3 버킷에 iPortfolio라는 폴더가 기본적으로 있기 때문에 기본 경로로 설정하였습니다. 즉 도메인.com 으로 사용자가 접속할 경우 기본 경로를 &quot;도메인.com/iPortfolio&quot;가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 S3 버킷에 폴더 아래에 html, css, js 파일들이 있는 구조라면 폴더명을 작성해주시면 되며 S3 버킷에 따로 폴더를 한번 거쳐서 가는 형태가 아닌 html, css, js 파일들이 바로 있는 형태라면 빈값으로 두고 진행하시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1361&quot; data-origin-height=&quot;599&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdTDh6/btsQGqGv6XQ/CqF207dNASZgyMq3O92nm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdTDh6/btsQGqGv6XQ/CqF207dNASZgyMq3O92nm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdTDh6/btsQGqGv6XQ/CqF207dNASZgyMq3O92nm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdTDh6%2FbtsQGqGv6XQ%2FCqF207dNASZgyMq3O92nm1%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;1361&quot; height=&quot;599&quot; data-origin-width=&quot;1361&quot; data-origin-height=&quot;599&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 저는 현재 iPortfolio 폴더 아래에 정적 파일들이 있는 형태이기 때문에 작성한 것 입니다.&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;1050&quot; data-origin-height=&quot;202&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7PugT/btsQFNvoYpq/bep4eIx0Abeq5E0LlOm1gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7PugT/btsQFNvoYpq/bep4eIx0Abeq5E0LlOm1gk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7PugT/btsQFNvoYpq/bep4eIx0Abeq5E0LlOm1gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7PugT%2FbtsQFNvoYpq%2Fbep4eIx0Abeq5E0LlOm1gk%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;1050&quot; height=&quot;202&quot; data-origin-width=&quot;1050&quot; data-origin-height=&quot;202&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;정적 페이지이기도 하고 잠깐 사용할 것이기 때문에 비용을 최소화 하기 위해 &quot;보안 보호 비활성화&quot;로 진행하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1025&quot; data-origin-height=&quot;265&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QQxNv/btsQEgFqciI/ydEct8PoInZVGiZNAlSW9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QQxNv/btsQEgFqciI/ydEct8PoInZVGiZNAlSW9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QQxNv/btsQEgFqciI/ydEct8PoInZVGiZNAlSW9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQQxNv%2FbtsQEgFqciI%2FydEct8PoInZVGiZNAlSW9K%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;1025&quot; height=&quot;265&quot; data-origin-width=&quot;1025&quot; data-origin-height=&quot;265&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 하단의 Create distribution을 클릭하여 CloudFront 설정을 마무리 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1001&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OGaJv/btsQFaq9eJF/2AytcFNbR09jQ3RVaREWq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OGaJv/btsQFaq9eJF/2AytcFNbR09jQ3RVaREWq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OGaJv/btsQFaq9eJF/2AytcFNbR09jQ3RVaREWq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOGaJv%2FbtsQFaq9eJF%2F2AytcFNbR09jQ3RVaREWq1%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;1001&quot; height=&quot;363&quot; data-origin-width=&quot;1001&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;3. AWS Route53&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 콘솔에서 route53을 클릭하여 서비스에 들어가고 &quot;시작하기&quot; 버튼을 클릭해주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1033&quot; data-origin-height=&quot;355&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bu3eOw/btsQCPnXjvt/FJ94Wk3hOyugBcNzbkH8Jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bu3eOw/btsQCPnXjvt/FJ94Wk3hOyugBcNzbkH8Jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bu3eOw/btsQCPnXjvt/FJ94Wk3hOyugBcNzbkH8Jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbu3eOw%2FbtsQCPnXjvt%2FFJ94Wk3hOyugBcNzbkH8Jk%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;1033&quot; height=&quot;355&quot; data-origin-width=&quot;1033&quot; data-origin-height=&quot;355&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1225&quot; data-origin-height=&quot;403&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOQ4G9/btsQGi2U04F/bSSlIWlK7riarIgLnkvjak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOQ4G9/btsQGi2U04F/bSSlIWlK7riarIgLnkvjak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOQ4G9/btsQGi2U04F/bSSlIWlK7riarIgLnkvjak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOQ4G9%2FbtsQGi2U04F%2FbSSlIWlK7riarIgLnkvjak%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;1225&quot; height=&quot;403&quot; data-origin-width=&quot;1225&quot; data-origin-height=&quot;403&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;&quot;호스팅 영역 생성&quot;을 선택하고 &quot;시작하기&quot; 버튼을 클릭하여 다음 단계로 넘어갑니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;548&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5BcSj/btsQGiokoQo/JmmALUFKpDKgfGfBDDUyM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5BcSj/btsQGiokoQo/JmmALUFKpDKgfGfBDDUyM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5BcSj/btsQGiokoQo/JmmALUFKpDKgfGfBDDUyM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5BcSj%2FbtsQGiokoQo%2FJmmALUFKpDKgfGfBDDUyM1%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;1307&quot; height=&quot;548&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;548&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;도메인 이름(ex. tistory.com)을 입력하고 &quot;호스팅 영역 생성&quot;을 클릭하여 Route53을 생성하고 마무리합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;790&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Et2Mz/btsQFDMZ1of/u4TrTToFRDLGfKYUGartkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Et2Mz/btsQFDMZ1of/u4TrTToFRDLGfKYUGartkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Et2Mz/btsQFDMZ1of/u4TrTToFRDLGfKYUGartkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEt2Mz%2FbtsQFDMZ1of%2Fu4TrTToFRDLGfKYUGartkk%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;1290&quot; height=&quot;790&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;790&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;4. AWS Certificate Manager (ACM)&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 ACM을 사용하여 인증서를 발급하는 과정입니다. 콘솔 검색에서 ACM을 입력하여 Certificate Manager를 클릭해주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;849&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cevReO/btsQG0naIEq/O7pgXTQpirFqkUVMnAAaKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cevReO/btsQG0naIEq/O7pgXTQpirFqkUVMnAAaKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cevReO/btsQG0naIEq/O7pgXTQpirFqkUVMnAAaKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcevReO%2FbtsQG0naIEq%2FO7pgXTQpirFqkUVMnAAaKK%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;849&quot; height=&quot;312&quot; data-origin-width=&quot;849&quot; data-origin-height=&quot;312&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;우측 상단에서 리전을 &quot;버지니아 북부&quot;로 변경해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;334&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tt56O/btsQEGqhngi/IVkKKNMU8ygZdtXW0XnoV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tt56O/btsQEGqhngi/IVkKKNMU8ygZdtXW0XnoV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tt56O/btsQEGqhngi/IVkKKNMU8ygZdtXW0XnoV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftt56O%2FbtsQEGqhngi%2FIVkKKNMU8ygZdtXW0XnoV0%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;334&quot; height=&quot;350&quot; data-origin-width=&quot;334&quot; data-origin-height=&quot;350&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;※ CloudFront에 인증서를 사용하기 위해서는 ACM의 리전을 버지니아 북부(us-east-1)로 하여 사용해야 합니다.&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;161&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgBAs8/btsQEnRH6CP/RqLvdn5933p9qKwOkyAb5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgBAs8/btsQEnRH6CP/RqLvdn5933p9qKwOkyAb5K/img.png&quot; data-alt=&quot;https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cnames-and-https-requirements.html?utm_source=chatgpt.com&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgBAs8/btsQEnRH6CP/RqLvdn5933p9qKwOkyAb5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgBAs8%2FbtsQEnRH6CP%2FRqLvdn5933p9qKwOkyAb5K%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;656&quot; height=&quot;161&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;161&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cnames-and-https-requirements.html?utm_source=chatgpt.com&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;이제 요청 &amp;rarr; 퍼블릭 인증서 요청 &amp;rarr; 다음을 클릭해주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9ul3L/btsQEd9HezD/WCdf0n8nhwwYnVUeVxbgcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9ul3L/btsQEd9HezD/WCdf0n8nhwwYnVUeVxbgcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9ul3L/btsQEd9HezD/WCdf0n8nhwwYnVUeVxbgcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9ul3L%2FbtsQEd9HezD%2FWCdf0n8nhwwYnVUeVxbgcK%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;920&quot; height=&quot;248&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;248&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;305&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8hRN5/btsQG2Fh7Nn/CPanRGkgG2MKqbJUQeTKOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8hRN5/btsQG2Fh7Nn/CPanRGkgG2MKqbJUQeTKOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8hRN5/btsQG2Fh7Nn/CPanRGkgG2MKqbJUQeTKOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8hRN5%2FbtsQG2Fh7Nn%2FCPanRGkgG2MKqbJUQeTKOK%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;1172&quot; height=&quot;305&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;305&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구매해두었던 도메인 이름을 추가합니다. 저는 &quot;이 인증서에 다른 이름 추가&quot;를 클릭하여 도메인을 추가 하였습니다. 인증서를 발급받을 도메인을 입력한 후 우측 하단의 요청을 눌러주세요.&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;예시)&amp;nbsp;&lt;a style=&quot;color: #ee2323;&quot; href=&quot;http://nirsa.tistory.com,/&quot;&gt;tistory.com&lt;/a&gt; /&amp;nbsp;&lt;a style=&quot;color: #ee2323;&quot; href=&quot;http://www.nirsa.tistory.com/&quot;&gt;www.tistory.com&amp;nbsp;&lt;/a&gt;&amp;nbsp;이와 같은 형태로 총 2개를 입력하였습니다.&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1483&quot; data-origin-height=&quot;310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhMm44/btsQD8UPdFw/kPmwuBwXO4BXp7nCeWSPP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhMm44/btsQD8UPdFw/kPmwuBwXO4BXp7nCeWSPP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhMm44/btsQD8UPdFw/kPmwuBwXO4BXp7nCeWSPP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhMm44%2FbtsQD8UPdFw%2FkPmwuBwXO4BXp7nCeWSPP1%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;1483&quot; height=&quot;310&quot; data-origin-width=&quot;1483&quot; data-origin-height=&quot;310&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;인증서 생성이 완료되었다면 &quot;Route 53에서 레코드 생성&quot; 버튼을 클릭 후 레코드 생성을 진행해주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1481&quot; data-origin-height=&quot;297&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpYyBB/btsQFWMC3Vm/WkLzncEJDgiW5Cti3QbiD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpYyBB/btsQFWMC3Vm/WkLzncEJDgiW5Cti3QbiD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpYyBB/btsQFWMC3Vm/WkLzncEJDgiW5Cti3QbiD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpYyBB%2FbtsQFWMC3Vm%2FWkLzncEJDgiW5Cti3QbiD0%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;1481&quot; height=&quot;297&quot; data-origin-width=&quot;1481&quot; data-origin-height=&quot;297&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1492&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/be9bVS/btsQG2kZ0h4/rEfHqyElSKgk0T8SkBsOp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/be9bVS/btsQG2kZ0h4/rEfHqyElSKgk0T8SkBsOp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/be9bVS/btsQG2kZ0h4/rEfHqyElSKgk0T8SkBsOp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbe9bVS%2FbtsQG2kZ0h4%2FrEfHqyElSKgk0T8SkBsOp0%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;1492&quot; height=&quot;302&quot; data-origin-width=&quot;1492&quot; data-origin-height=&quot;302&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;처음엔 &quot;검증 대기 중&quot;으로 되어있으며 이후 가비아 도메인 연동 등 추가적인 설정 이후 완료될 것 이기 때문에 우선은 무시한 채 아래의 가비아 도메인 연동 설정을 진행해주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;291&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boSa69/btsQFzDRkOz/QWi36A4p5KOxT04Q3H3Vhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boSa69/btsQFzDRkOz/QWi36A4p5KOxT04Q3H3Vhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boSa69/btsQFzDRkOz/QWi36A4p5KOxT04Q3H3Vhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboSa69%2FbtsQFzDRkOz%2FQWi36A4p5KOxT04Q3H3Vhk%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;1039&quot; height=&quot;291&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;291&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;5. 가비아 도메인 연동&lt;/b&gt;&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://www.gabia.com/&quot;&gt;가비아 홈페이지&lt;/a&gt;로 이동하셔서 로그인 후 우측 상단의 My가비아 &amp;rarr; 도메인을 클릭해주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1208&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvOZbp/btsQGRw349t/bm5jOvsUBRzWLs5Ey8NfI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvOZbp/btsQGRw349t/bm5jOvsUBRzWLs5Ey8NfI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvOZbp/btsQGRw349t/bm5jOvsUBRzWLs5Ey8NfI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvOZbp%2FbtsQGRw349t%2Fbm5jOvsUBRzWLs5Ey8NfI1%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;1208&quot; height=&quot;306&quot; data-origin-width=&quot;1208&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;미리 구매해둔 도메인을 확인하고 우측 &quot;관리&quot; 버튼을 클릭합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;215&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZJaak/btsQEQe4PTk/v1K1RXkSKlZ4eFw0P61PbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZJaak/btsQEQe4PTk/v1K1RXkSKlZ4eFw0P61PbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZJaak/btsQEQe4PTk/v1K1RXkSKlZ4eFw0P61PbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZJaak%2FbtsQEQe4PTk%2Fv1K1RXkSKlZ4eFw0P61PbK%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;1222&quot; height=&quot;215&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;215&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; 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-origin-width=&quot;1135&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0wBy9/btsQGhpp9Kf/8daVTzMl8OxK4xzkHsYJF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0wBy9/btsQGhpp9Kf/8daVTzMl8OxK4xzkHsYJF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0wBy9/btsQGhpp9Kf/8daVTzMl8OxK4xzkHsYJF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0wBy9%2FbtsQGhpp9Kf%2F8daVTzMl8OxK4xzkHsYJF0%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;1135&quot; height=&quot;255&quot; data-origin-width=&quot;1135&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;AWS Route53을 보면 NS 유형에 총 4개의 값이 보일겁니다. 이 값들을 각각 가비아에서 1~4차의 호스트명에 입력하여 적용해주세요. 순서 상관 없이 값들을 채워 넣어주면 됩니다.&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;※ 가비아에 네임서버 입력 시 마지막에 . 을 빼주셔야 합니다.&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1217&quot; data-origin-height=&quot;379&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rJIyj/btsQD7uMVdQ/cFK6MDaWnmCzacBAGakTi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rJIyj/btsQD7uMVdQ/cFK6MDaWnmCzacBAGakTi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rJIyj/btsQD7uMVdQ/cFK6MDaWnmCzacBAGakTi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrJIyj%2FbtsQD7uMVdQ%2FcFK6MDaWnmCzacBAGakTi1%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;1217&quot; height=&quot;379&quot; data-origin-width=&quot;1217&quot; data-origin-height=&quot;379&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1685&quot; data-origin-height=&quot;521&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Do8Rz/btsQGFDzUKt/guVTOYJ8w5yUMbq3NgCwR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Do8Rz/btsQGFDzUKt/guVTOYJ8w5yUMbq3NgCwR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Do8Rz/btsQGFDzUKt/guVTOYJ8w5yUMbq3NgCwR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDo8Rz%2FbtsQGFDzUKt%2FguVTOYJ8w5yUMbq3NgCwR1%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;1685&quot; height=&quot;521&quot; data-origin-width=&quot;1685&quot; data-origin-height=&quot;521&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;7. 마무리 (CloudFront 대체 도메인 이름, Path 설정 및 Route53 A 레코드 등록)&lt;/b&gt;&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다시 CloudFront 서비스로 들어가서 대체 도메인 이름 아래에 있는 &quot;Add domain&quot;을 클릭해주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;259&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z9O1f/btsQEaykHhp/M7i3NPIEjAM7kXa9lqX3L1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z9O1f/btsQEaykHhp/M7i3NPIEjAM7kXa9lqX3L1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z9O1f/btsQEaykHhp/M7i3NPIEjAM7kXa9lqX3L1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ9O1f%2FbtsQEaykHhp%2FM7i3NPIEjAM7kXa9lqX3L1%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;1022&quot; height=&quot;259&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;259&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;&quot;Add another domain&quot;을 클릭하여 도메인 입력란을 추가하고 구매한 2개의 도메인 주소를 입력해주세요.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;예시) tistory.com / www.tistory.com&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;407&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcGSPA/btsQDJnJdFa/9fzjDcLxzgrsNsPkd8HrZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcGSPA/btsQDJnJdFa/9fzjDcLxzgrsNsPkd8HrZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcGSPA/btsQDJnJdFa/9fzjDcLxzgrsNsPkd8HrZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcGSPA%2FbtsQDJnJdFa%2F9fzjDcLxzgrsNsPkd8HrZ1%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;750&quot; height=&quot;407&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;407&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;ACM 설정이 잘 되어 있으면 아래와 같이 기본적으로 연결되어 있으니 Next &amp;rarr; Add domains를 눌러 마무리 해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1029&quot; data-origin-height=&quot;524&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAqRhi/btsQGR4U9wc/b5eJSkyqvK4iwfm4cWlQtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAqRhi/btsQGR4U9wc/b5eJSkyqvK4iwfm4cWlQtK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAqRhi/btsQGR4U9wc/b5eJSkyqvK4iwfm4cWlQtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAqRhi%2FbtsQGR4U9wc%2Fb5eJSkyqvK4iwfm4cWlQtK%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;1029&quot; height=&quot;524&quot; data-origin-width=&quot;1029&quot; data-origin-height=&quot;524&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1031&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4WY1f/btsQEMqwRC1/fDKyVJjqEk4eyvS3SUVP41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4WY1f/btsQEMqwRC1/fDKyVJjqEk4eyvS3SUVP41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4WY1f/btsQEMqwRC1/fDKyVJjqEk4eyvS3SUVP41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4WY1f%2FbtsQEMqwRC1%2FfDKyVJjqEk4eyvS3SUVP41%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;1031&quot; height=&quot;495&quot; data-origin-width=&quot;1031&quot; data-origin-height=&quot;495&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;다시 CloudFront 서비스 화면에서 우측의 편집을 눌러주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1651&quot; data-origin-height=&quot;317&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYLUX1/btsQFnKtirX/BxBPbC9vpjTcDFW1U8qmS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYLUX1/btsQFnKtirX/BxBPbC9vpjTcDFW1U8qmS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYLUX1/btsQFnKtirX/BxBPbC9vpjTcDFW1U8qmS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYLUX1%2FbtsQFnKtirX%2FBxBPbC9vpjTcDFW1U8qmS1%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;1651&quot; height=&quot;317&quot; data-origin-width=&quot;1651&quot; data-origin-height=&quot;317&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;비용 절감을 위해 &quot;&lt;span style=&quot;background-color: #ffffff; color: #0f141a; text-align: start;&quot;&gt;Use North America, Europe, Asia, Middle East, and Africa&quot;로 선택합니다.&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;1084&quot; data-origin-height=&quot;334&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnuZ33/btsQGhCXCfH/D2hkKKSIeVbk21g6DiXWAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnuZ33/btsQGhCXCfH/D2hkKKSIeVbk21g6DiXWAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnuZ33/btsQGhCXCfH/D2hkKKSIeVbk21g6DiXWAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnuZ33%2FbtsQGhCXCfH%2FD2hkKKSIeVbk21g6DiXWAK%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;1084&quot; height=&quot;334&quot; data-origin-width=&quot;1084&quot; data-origin-height=&quot;334&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크롤을 조금 내리면 &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Default root object - optional가 있을텐데, 이는 &lt;/span&gt;사용자가 Root URL(/)을 요청할 경우 반환할 파일을 선택해주어야 합니다. 즉 예를들어 htts://도메인.com/을 입력할 경우 어떤 정적 페이지를 보여줄 것인가에 대한 옵션입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 기본적으로 index.html 파일을 보여줄 것이기 때문에 index.html을 작성하였습니다. 설정 완료 후 &quot;변경 사항 저장&quot;을 클릭하여 CloudFront 설정을 마무리해주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1045&quot; data-origin-height=&quot;86&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/br8q5d/btsQGj1PlV7/RrcQorD1LS65xUbkYUVEsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/br8q5d/btsQGj1PlV7/RrcQorD1LS65xUbkYUVEsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/br8q5d/btsQGj1PlV7/RrcQorD1LS65xUbkYUVEsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbr8q5d%2FbtsQGj1PlV7%2FRrcQorD1LS65xUbkYUVEsK%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;1045&quot; height=&quot;86&quot; data-origin-width=&quot;1045&quot; data-origin-height=&quot;86&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;이제 마지막으로 Route 53으로 돌아와서 &quot;레코드 생성&quot;을 클릭해주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1203&quot; data-origin-height=&quot;169&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oJkiB/btsQFq1zwU3/dwWK31ShnxbWOzeK7GaKu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oJkiB/btsQFq1zwU3/dwWK31ShnxbWOzeK7GaKu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oJkiB/btsQFq1zwU3/dwWK31ShnxbWOzeK7GaKu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoJkiB%2FbtsQFq1zwU3%2FdwWK31ShnxbWOzeK7GaKu1%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;1203&quot; height=&quot;169&quot; data-origin-width=&quot;1203&quot; data-origin-height=&quot;169&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;A 레코드를 추가하고 별칭 토클을 한번 클릭하여 &quot;CloudFront 배포에 대한 별칭&quot;을 클릭하고 배포 선택을 클릭해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 &quot;다른 레코드 추가&quot;를 눌러 www에 대한 설정도 똑같이 진행한 뒤 &quot;레코드 생성&quot;을 클릭하여 마무리해주시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1652&quot; data-origin-height=&quot;629&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3Nzqv/btsQGWZr865/cCIZOxXrUMksqKfnr5EZB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3Nzqv/btsQGWZr865/cCIZOxXrUMksqKfnr5EZB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3Nzqv/btsQGWZr865/cCIZOxXrUMksqKfnr5EZB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3Nzqv%2FbtsQGWZr865%2FcCIZOxXrUMksqKfnr5EZB1%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;1652&quot; height=&quot;629&quot; data-origin-width=&quot;1652&quot; data-origin-height=&quot;629&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;배포 확인하기&lt;/span&gt;&lt;/blockquote&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;1922&quot; data-origin-height=&quot;1041&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btSDj5/btsQGUgg5m3/s3cbrdRYtERxGyG6d7crj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btSDj5/btsQGUgg5m3/s3cbrdRYtERxGyG6d7crj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btSDj5/btsQGUgg5m3/s3cbrdRYtERxGyG6d7crj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtSDj5%2FbtsQGUgg5m3%2Fs3cbrdRYtERxGyG6d7crj1%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;1922&quot; height=&quot;1041&quot; data-origin-width=&quot;1922&quot; data-origin-height=&quot;1041&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Cloud/AWS</category>
      <author>KoreaNirsa</author>
      <guid isPermaLink="true">https://nirsa.tistory.com/482</guid>
      <comments>https://nirsa.tistory.com/482#entry482comment</comments>
      <pubDate>Fri, 19 Sep 2025 00:26:19 +0900</pubDate>
    </item>
    <item>
      <title>[LLM] 컨텍스트 엔지니어링이란? (Context Engineering, RAG, Chunking, Embedding, Vector Search, Context Injection)</title>
      <link>https://nirsa.tistory.com/481</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;[LLM]&amp;nbsp;컨텍스트&amp;nbsp;엔지니어링이란?&amp;nbsp;(Context&amp;nbsp;Engineering)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM은 기본적으로 프롬프트를 기반으로 답변을 생성하게 되는데, 실제 서비스 환경에서는 특정 작업에 따른 정보를 제공하여 더 정확한 답변을 줄 수 있습니다. 즉, LLM이 응답을할 때 특정 도메인이나 상황에 맞춰 필요한 문맥(Context)을 공급하여 더 정확한 답변을 유도하는 기법을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 사내에서 사용할 QA 챗봇을 만드는 상황에서 &quot;우리 회사의 연차 규정을 알려줘&quot; 라고 하면 일반적으로 다른 회사들이 사용하는 정보들을 기반으로 답변이 제공됩니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1757917581868&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 간단한 예시
사용자 : 우리 회사의 연차 규정을 알려줘
답변 : 대한민국 근로기준법에 따르면 근로자는 1년에 15일 이상의 연차를 보장받습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 컨텍스트 엔지니어링을 적용하여 답변할 때 참고해야할 추가 문맥(Context)를 제공, 즉 회사 연차 규정이 적혀있는 문서를 제공한다면 회사에 맞춘 답변을 제공할 수 있게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1757917768699&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 간단한 예시 (컨텍스트 엔지니어링 적용)
사용자 : 우리 회사의 연차 규정을 알려줘
  - 사내 인사 규정 문서 검색
답변 : 우리 회사는 입사 1년 후 15일, 3년 이상 근속 시 20일, 최대 25일의 연차를 제공합니다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시와 같이 컨텍스트 엔지니어링이 적용된 사내 QA 챗 봇을 사용한다면 문서를 검색하여 &quot;우리 회사&quot;에 대한 연차 규정을 정확하게 확인하고 답변이 가능한 형태가 됩니다. 위의 예시를 조금만 더 자세히 한다면 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자 입력 : &quot;우리 회사의 연차 규정을 알려줘&quot;&lt;/li&gt;
&lt;li&gt;컨텍스트 검색 : 사내 DB 또는 문서에서 관련 컨텍스트(연차 규정) 검색&lt;/li&gt;
&lt;li&gt;실제 LLM 입력 : 검색된 컨텍스트, 사용자가 입력한 프롬프트 등을 LLM에게 실제로 입력&lt;/li&gt;
&lt;li&gt;답변 생성 : &quot;우리 회사는 입사 1년 후 15일, 3년 이상 근속 시 20일, 최대 25일의 연차를 제공합니다&quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;&quot;&gt;컨텍스트 엔지니어링의 대표적인 기술 RAG&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨텍스트 엔지니어링에서 가장 대표적인 기술로써는 RAG(Retrieval-Augmented Generation)이 있습니다. 이는 위에서 살펴봣던 예시와 같이 DB나 문서를 검색하여 그 결과를 LLM 입력에 함께 넣어 답변을 생성하는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG를 사용하게 되면 DB나 문서를 검색함으로써 모델 파라미터에 직접 학습시키지 않더라도 최신 정보 또는 도메인 지식을 활용할 수 있는 가장 큰 장점이 있습니다. 이로써 고객 지원 챗봇이나 제품 매뉴얼, 최신 뉴스 요약 등의 분야에서 활용될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동작 구조는 크게 5가지로 나뉘어 집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;1. 사용자 질문&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 처음으로는 &quot;우리 회사의 연차 규정을 알려줘&quot;와 같이 사용자에게 질문을 받습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;2. Chunking&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰 한계와 비용에 대한 문제로 긴 원문 자체를 넣기가 어렵기 때문에 효율을 위해 원문을 적절한 크기로 쪼개서 부분적으로 검색을 합니다. 즉 문서를 작은 단위로 나누는 작업단, 원문을 잘못 쪼갤 경우 문맥이 끊겨 정확도가 떨어지거나 불필요한 토큰이 낭비되는 단점이 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;※ 토큰이란?&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;LLM이 텍스트를 처리할 때 잘게 쪼개는 단위로써 보통 자주 쓰이는 단어나 문자열 패턴을 1토큰으로 지정합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;ex) ChatGPT is powerful!&amp;nbsp; &amp;rarr;&amp;nbsp; [&quot;Chat&quot;, &quot;G&quot;, &quot;PT&quot;, &quot; is&quot;, &quot; powerful&quot;, &quot;!&quot;]으로 나뉘어질 수 있으므로 6토큰&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 원문이 있다고 가정하겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1757920752246&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;우리 회사는 입사 1년 후 15일의 연차를 지급합니다.
3년 이상 근속한 직원에게는 20일 10년 이상 근속한 직원에게는 25일의 연차를 지급합니다.
연차는 근로기준법을 준수하며 개인 사정에 따라 분할 사용이 가능합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 대한 원문을 Chunking할 때 토큰 길이, 문장/문단 기준, 재귀 분할 등 여러가지가 있지만 &lt;u&gt;단순히 문자열을 기준으로 한다는 가정하&lt;/u&gt;에 chunk, overlap을 사용하는데 간단히는 chunk과 overlap을 문자열 기준으로 적용한다면 chunk는 자를 문자열의 수, overlap은 앞 청크의 뒷부분을 겹칠 문자열의 수가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;chunk 단위로 쪼개게 되면&amp;nbsp; 문장이 중간에 잘려서 의미가 끊길 수 있기 때문에 겹치는 부분으로 문장을 자연스럽게 연결시켜주는 징검다리 역할을 수행해주는 것이 overlap 입니다. 만약 chunk=40, overlap=10으로 설정한다면 아래와 같은 결과를 얻을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;40자를 기준으로 문서를 쪼게고, 앞 청크의 뒷부분을 overlap 만큼(10자) 다음 청크에서 반복시켜서 문장을 자연스럽게 연결시켜주게 됩니다. 아래의 예시에서는 ( ) 으로 표현된 것이 overlap으로 인해 다시 등장한 문자열입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1757922430716&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[Chunk 1] 우리 회사는 입사 1년 후 15일의 연차를 지급합니다. 3년 이상 근속한
[Chunk 2] ( 3년 이상 근속한) 직원에게는 20일 10년 이상 근속한 직원에게는 25
[Chunk 3] (한 직원에게는 25)일의 연차를 지급합니다. 연차는 근로기준법을 준수하며
[Chunk 4] (기준법을 준수하며 )개인 사정에 따라 분할 사용이 가능합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;3. Embedding (텍스트 &amp;rarr; 숫자 좌표화)&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임베딩에서는 텍스트를 벡터로 바꾸게 됩니다. 즉 텍스트를 숫자 좌표(벡터)로 바꾸는 기술로써 같은 문장 끼리는 벡터 공간에서 가깝게, 다른 뜻이라면 멀게 배치가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히는 지도를 생각해보시면 되는데, 서울(37.5, 127)과 인천(37.4, 126.7)의 좌표는 가깝고 부산(35.1, 129.0)과는 먼것을 생각하시면 됩니다. 이에 따라 벡터도 숫자 좌표를 기준으로 같은 문장끼리는 가까운 곳에, 다른 문장끼리는 먼 곳에 배치하는 기술입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 아래와 같이 텍스트를 벡터로 변환하게 됩니다. (숫자는 임의로 작성했습니다)&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1757922913642&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;텍스트: &quot;우리 회사의 연차 규정을 알려줘&quot;
질문 벡터: [0.011, -0.030, 0.543, ...]

텍스트: &quot;우리 회사는 입사 1년 후 15일의 연차를 지급합니다. 3년 이상 근속한&quot;
Chunk 1 벡터: [0.012, -0.028, 0.551, ...]

텍스트: &quot;( 3년 이상 근속한) 직원에게는 20일 10년 이상 근속한 직원에게는 25&quot;
Chunk 2 벡터: [0.010, -0.033, 0.561, ...]

텍스트: &quot;(한 직원에게는 25)일의 연차를 지급합니다. 연차는 근로기준법을 준수하며&quot;
Chunk 3 벡터: [0.015, -0.035, 0.575, ...]

텍스트: &quot;(기준법을 준수하며 )개인 사정에 따라 분할 사용이 가능합니다.&quot;
Chunk 4 벡터: [0.025, -0.061, 0.864, ...]

텍스트: &quot;퇴직금 정산 방법&quot;
벡터: [0.234, 0.481, -0.112, ...]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이러한 결과를 봣을 때&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&quot;우리 회사의 연차 규정을 알려줘&quot;과 &quot;우리 회사는 입사 1년 후 15일의 연차를 지급합니다. 3년 이상 근속한&quot;는 아주 가깝게 배치되어 있어 의미가 비슷하다고 판단할 수 있으며, &quot;퇴직금 정산 방법&quot;의 경우 멀게 배치되어 있어 다른 의미를 가지고 있다고 판단할 수 있는 근거로 활용될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 아래의 벡터 검색(Vector Search)에서 활용됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;4. Vector Search (질문과 가장 가까운 청크 찾기)&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벡터 검색에서는 질문과 가장 가까운 청크를 찾습니다. 질문 벡터와 각각의 청크 벡터들을 비교하여 코사인 유사도를 확인하여 이에 대한 정렬 결과를 가지고 답변에 활용할 문맥(Context)으로 활용되게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;187&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sYZc6/btsQypP73fQ/CfnGGzg8NEKXp6hIqDTpWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sYZc6/btsQypP73fQ/CfnGGzg8NEKXp6hIqDTpWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sYZc6/btsQypP73fQ/CfnGGzg8NEKXp6hIqDTpWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsYZc6%2FbtsQypP73fQ%2FCfnGGzg8NEKXp6hIqDTpWk%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;626&quot; height=&quot;187&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;187&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 코사인 유사도에 따라 Top 4로 정렬을 한다면 Chunk 1 &amp;gt; Chunk 2 &amp;gt; Chunk 3 &amp;gt; Chunk 4로 정렬되게 됩니다. &quot;퇴직금 정산 방법&quot;에 대한 벡터는 코사인 유사도가 매우 떨어지므로 제외되는 방식 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 최종적으로 벡터 검색에 의해 정렬된 내용은 아래와 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1757925788930&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[Chunk 1] 우리 회사는 입사 1년 후 15일의 연차를 지급합니다. 3년 이상 근속한
[Chunk 2] ( 3년 이상 근속한) 직원에게는 20일 10년 이상 근속한 직원에게는 25
[Chunk 3] (한 직원에게는 25)일의 연차를 지급합니다. 연차는 근로기준법을 준수하며
[Chunk 4] (기준법을 준수하며 )개인 사정에 따라 분할 사용이 가능합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;5. Context Injection (컨텍스트 주입 및 프롬프트 구성)&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 컨텍스트를 주입합니다. 최종적으로 아래와 같은 프롬프트가 구성될 수 있으며 토큰 절약을 위해 중복되는 표현(overlap 등)은 정리하여 주입해도 좋습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1757925938657&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 내부 시스템 프롬프트
[System]
너는 우리 회사 HR 정책을 안내하는 어시스턴트야.
아래 '컨텍스트'에 근거해 답하고 근거가 없으면 &quot;문서에 근거가 없습니다&quot;라고 말해야 하며
가능하면 출처(청크 번호/문장)를 괄호로 표시하도록 해

[Instruction]
사용자의 질문에 맞게 핵심만 간결히 요약해서 답변해

// 벡터 검색으로 찾은 청크 4개를 컨텍스트 주입
[Context]
[Chunk 1] 우리 회사는 입사 1년 후 15일의 연차를 지급합니다. 3년 이상 근속한
[Chunk 2] ( 3년 이상 근속한) 직원에게는 20일 10년 이상 근속한 직원에게는 25
[Chunk 3] (한 직원에게는 25)일의 연차를 지급합니다. 연차는 근로기준법을 준수하며
[Chunk 4] (기준법을 준수하며 )개인 사정에 따라 분할 사용이 가능합니다.

// 사용자 질문
[User]
우리 회사의 연차 규정을 알려줘&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;6. 답변 생성&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 LLM이 컨텍스트를 참고하여 답변을 제공 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1757926073475&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;우리 회사는 입사 1년 후 15일의 연차를 부여하며
3년 이상 근속 시 20일, 10년 이상 근속 시 25일을 부여합니다.
연차는 근로기준법을 준수하여 분할 사용을 할 수 있습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-size=&quot;size16&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;마무리&lt;/span&gt;&lt;/blockquote&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;A. 컨텍스트 엔지니어링 이란?&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM이 응답을할 때 특정 도메인이나 상황에 맞춰 필요한 문맥(Context)을 공급하여 더 정확한 답변을 유도하는 기법을 의미합니다&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;B. 왜 필요한가?&lt;/b&gt;&lt;/blockquote&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;정확성 향상 : 회사/제품/내부 규정과 같은 도메인 지식 반영&lt;/li&gt;
&lt;li&gt;최신성 유지 : 모델을 학습시키지 않아도 최신 문서 반영 가능&lt;/li&gt;
&lt;li&gt;일관성 확보 : DB/문서를 근거로 답변하므로 일관성 확보&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;C. 자주 사용되는 대표 상황 3가지&lt;/b&gt;&lt;/blockquote&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사내 QA : 인사/보안/IT 규정 챗봇&lt;/li&gt;
&lt;li&gt;고객 지원 : 매뉴얼/FAQ/회사 정책에 근거하여 답변&lt;/li&gt;
&lt;li&gt;지식 검색 : 위키/노션/사내 문서를 바탕으로 요약 또는 답변&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;D. 아주 간단히 보는 컨텍스트 엔지니어링의 흐름&lt;/b&gt;&lt;/blockquote&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;질문 수신&lt;/li&gt;
&lt;li&gt;관련 DB/문서 검색 (RAG 등)&lt;/li&gt;
&lt;li&gt;검색하여 찾은 조각을 프롬프트에 주입(컨텍스트 주입)&lt;/li&gt;
&lt;li&gt;근거 기반 답변 생성&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;E. 프롬프트 엔지니어링 vs 컨텍스트 엔지니어링 간단 비교&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;159&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yxVKT/btsQAuWY1Mx/9WMk8eVKJafs1xkhw9dX6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yxVKT/btsQAuWY1Mx/9WMk8eVKJafs1xkhw9dX6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yxVKT/btsQAuWY1Mx/9WMk8eVKJafs1xkhw9dX6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyxVKT%2FbtsQAuWY1Mx%2F9WMk8eVKJafs1xkhw9dX6k%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;860&quot; height=&quot;159&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;159&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;F. 자주 하는 오해&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1757927809764&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Q. 프롬프트만 잘 짜면 충분하지 않나? 컨텍스트 엔지니어링이 필요한 이유?
A. 내부 규정 또는 최신 정보, 특정 도메인에 대한 지식은 AI 모델에 의존해야 하므로 한계가 존재함

Q. 컨텍스트와 파인튜닝은 같은 것인가?
A. 파인튜닝은 AI 모델을 다시 학습하는 개념이며
   컨텍스트는 검색한 문서의 조각을 프롬프트에 붙여주는 행위에 가깝습니다.
   
Q. 그렇다면 컨텍스트 엔지니어링은 무조건 좋은것인가?
A. 문서 자체가 부정확하는 등 품질이 떨어지면 답변의 퀄리티 또한 같이 떨어집니다.
   컨텍스트 엔지니어링의 핵심은 좋은 문서가 기반이 되어야 합니다.&lt;/code&gt;&lt;/pre&gt;</description>
      <category>AI Engineering/Methodology</category>
      <author>KoreaNirsa</author>
      <guid isPermaLink="true">https://nirsa.tistory.com/481</guid>
      <comments>https://nirsa.tistory.com/481#entry481comment</comments>
      <pubDate>Mon, 15 Sep 2025 18:19:45 +0900</pubDate>
    </item>
    <item>
      <title>[LLM] 입문자를 위한 프롬프트 엔지니어링 개념과 3대 원칙 (Role, Instruction, Few-shot)</title>
      <link>https://nirsa.tistory.com/480</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;[LLM] 입문자를 위한 프롬프트 엔지니어링 개념과 3대 원칙 (Role, Instruction, Few-shot)&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프롬프트 엔지니어링은 AI 모델이 원하는 방식으로 응답할 수 있도록 프롬프트를 설계, 최적화 하는 과정입니다. LLM은 기본적으로 입력된 프롬프트를 기반으로 다음 토큰을 예측하기 때문에 프롬프트를 잘 작성하면 LLM 모델의 답변 품질을 크게 올릴 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 모호한 프롬프트를 주면 예상과 다른 답변이 나올 확률이 그만큼 높으며 AI 활용을 하기 위해서는 명확하고 구조화된 프롬프트를 작성하는 능력이 필요합니다. 이러한 프롬프트 설계 능력을 통해 각 분야에 AI를 활용했을 때 증가하는 생산성은 크게 차이가 나게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 GPT와 같은 AI 플랫폼을 사용할 때 &quot;잘 질문해야 좋은 답변을 얻는다&quot;가 프롬프트 엔지니어링에 해당하는 개념인데, 잘 질문하기 위해서는 아래의 3가지 원칙을 따라 작성하는 습관이 필요하며 가장 기본적인 틀이기도 합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;역할 부여(Role) : 모델에게 특정 역할을 주어 답변을 유도하여 답변의 톤과 관점 제어&lt;/li&gt;
&lt;li&gt;명확한 지시(Instruction) : 원하는 출력 형태와 조건을 구체적으로 전달&lt;/li&gt;
&lt;li&gt;예시 제공(Few-shot) : 몇 가지 샘플을 포함하여 전달하여 원하는 답변 스타일을 학습하도록 유도&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Role - Instruction - Few-shot&lt;b&gt;(이하 RIF)&lt;/b&gt;을 지키지 않고 단순하게 질문한 했을 경우와 RIF를 지켰을 경우 어떠한 답변의 차이가 있는지 살펴보도록 하겠습니다. 아래의 실험은 GPT에서 로그아웃 후 진행하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;RIF를 지키지 않고 단순하게 질문만 했을 경우 (GPT)&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 질문만 한 경우입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1757679404150&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;자바 ArrayList와 LinkedList를 비교해.&lt;/code&gt;&lt;/pre&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;809&quot; data-origin-height=&quot;811&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rH49A/btsQziBIm9M/ERbsgtItv92o1K69iiAWa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rH49A/btsQziBIm9M/ERbsgtItv92o1K69iiAWa0/img.png&quot; data-alt=&quot;RIF를 지키지 않고 단순하게 질문만 했을 경우 (답변 1-1)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rH49A/btsQziBIm9M/ERbsgtItv92o1K69iiAWa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrH49A%2FbtsQziBIm9M%2FERbsgtItv92o1K69iiAWa0%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;809&quot; height=&quot;811&quot; data-origin-width=&quot;809&quot; data-origin-height=&quot;811&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RIF를 지키지 않고 단순하게 질문만 했을 경우 (답변 1-1)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;731&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbb71W/btsQywmH3Ha/P3nIKsdQ2V4wMaty4mK7E1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbb71W/btsQywmH3Ha/P3nIKsdQ2V4wMaty4mK7E1/img.png&quot; data-alt=&quot;RIF를 지키지 않고 단순하게 질문만 했을 경우 (답변 1-2)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbb71W/btsQywmH3Ha/P3nIKsdQ2V4wMaty4mK7E1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcbb71W%2FbtsQywmH3Ha%2FP3nIKsdQ2V4wMaty4mK7E1%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;808&quot; height=&quot;731&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;731&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RIF를 지키지 않고 단순하게 질문만 했을 경우 (답변 1-2)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;810&quot; data-origin-height=&quot;428&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dffIo8/btsQxxfDPAm/zluYAgK2K2GNdGdobR92K1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dffIo8/btsQxxfDPAm/zluYAgK2K2GNdGdobR92K1/img.png&quot; data-alt=&quot;RIF를 지키지 않고 단순하게 질문만 했을 경우 (답변 1-3)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dffIo8/btsQxxfDPAm/zluYAgK2K2GNdGdobR92K1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdffIo8%2FbtsQxxfDPAm%2FzluYAgK2K2GNdGdobR92K1%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;810&quot; height=&quot;428&quot; data-origin-width=&quot;810&quot; data-origin-height=&quot;428&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RIF를 지키지 않고 단순하게 질문만 했을 경우 (답변 1-3)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;RIF를 지켜서 질문한 경우 (GPT)&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 강사가 학생들에게 ArrayList와 LinkedList의 차이를 설명하기 위해 AI를 활용하여 내용을 정리하고자 한다면 아래와 같은 프롬프트를 작성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1757679065905&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[Role]
너는 자바를 처음 입문하는 학생들에게 강의를 하는 전문 강사야.  
개발 경력이 10년 이상인 시니어 풀스택 개발자이면서 강의 경력이 10년 이상인 강사로
초보자가 이해하기 쉽게 설명하는 데 능숙해.

[Instruction]
&quot;ArrayList&quot;와 &quot;LinkedList&quot;의 차이를 표 형식으로 설명하고
마지막에 어떤 상황에서 어떤 것을 선택하여 사용해야 하는지 간단히 정리해줘.  
- 설명은 초보 학생이 이해할 수 있는 간단한 언어로 작성하고
  필요한 경우 간단한 코드 예제나 비유를 포함해.  
- 표에는 최소 다음 항목을 포함하되 필요하다면 
  추가 항목(예: 삽입/삭제 성능, 메모리 사용량)을 포함해 
  - 메모리 구조  
  - 접근 속도  
  - 삽입/삭제 속도  
- 잘못된 내용을 전달하지 않도록 답변 작성 전 공식 문서(예: Oracle Java 문서) 또는 
  신뢰할 수 있는 자료를 참조하여 3번 검증해.  
- 답변은 한국어로 작성하고, 500~700단어 내로 유지해.  
- 실제 개발 경험에서 나온 사례나 비유를 활용하여 학생들이 개념을 쉽게 이해하도록 도와줘.

[Free-shot]
예시 출력은 아래와 같아.

| 비교항목       | ArrayList         | LinkedList        |
|----------------|-------------------|------------------|
| 메모리 구조    |        ...        |        ...        |
| 접근 속도      |        ...        |        ...        |
| 삽입/삭제 속도 |        ...        |        ...        |

위 예시를 참고하여 ArrayList와 LinkedList 비교를 진행해.&lt;/code&gt;&lt;/pre&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;723&quot; data-origin-height=&quot;652&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAgcp3/btsQxzR1FA9/eHrVnkZIRM1TLTlBrtdMO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAgcp3/btsQxzR1FA9/eHrVnkZIRM1TLTlBrtdMO1/img.png&quot; data-alt=&quot;RIF를 지켜서 질문한 경우 (답변 2-1)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAgcp3/btsQxzR1FA9/eHrVnkZIRM1TLTlBrtdMO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAgcp3%2FbtsQxzR1FA9%2FeHrVnkZIRM1TLTlBrtdMO1%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;723&quot; height=&quot;652&quot; data-origin-width=&quot;723&quot; data-origin-height=&quot;652&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RIF를 지켜서 질문한 경우 (답변 2-1)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;502&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXOlIz/btsQxN3AiIF/y5IQqKYaKj0bTFkSSt3uC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXOlIz/btsQxN3AiIF/y5IQqKYaKj0bTFkSSt3uC1/img.png&quot; data-alt=&quot;RIF를 지켜서 질문한 경우 (답변 2-2)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXOlIz/btsQxN3AiIF/y5IQqKYaKj0bTFkSSt3uC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXOlIz%2FbtsQxN3AiIF%2Fy5IQqKYaKj0bTFkSSt3uC1%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;734&quot; height=&quot;502&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;502&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RIF를 지켜서 질문한 경우 (답변 2-2)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;725&quot; data-origin-height=&quot;612&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Fr4EL/btsQxApTp5T/lYHCAIhT2v9DL9uktKQdVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Fr4EL/btsQxApTp5T/lYHCAIhT2v9DL9uktKQdVk/img.png&quot; data-alt=&quot;RIF를 지켜서 질문한 경우 (답변 2-3)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Fr4EL/btsQxApTp5T/lYHCAIhT2v9DL9uktKQdVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFr4EL%2FbtsQxApTp5T%2FlYHCAIhT2v9DL9uktKQdVk%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;725&quot; height=&quot;612&quot; data-origin-width=&quot;725&quot; data-origin-height=&quot;612&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RIF를 지켜서 질문한 경우 (답변 2-3)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;마무리&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 질문을 했을 경우 기본적인 개념 설명은 간략하게 답변을 받았으나 깊이와 구조가 부족한 느낌이 강하며 RIF를 지켜서 질문했을 경우 조금 더 자세한 설명을 받았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ArrayList와 LinkedList의 경우 인터넷에 정보가 워낙 많다보니 큰 차이로 안보일 수 있지만, 이러한 프롬프트를 작성하는 능력을 키우다 보면 원하는 정보를 좀 더 자세히 얻고 특정 상황에 주어진 작업을 할 때 똑같은 AI를 활용하더라도 더 좋은 품질의 답변을 얻을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서 LLM을 활용할 때 가장 기본적이면서도 중요한 프롬프트 엔지니어링에 대해 간단히 알아보았는데, 프롬프트를 설계하는 방식에 따라 답변의 질과 활용도가 크게 달라질 수 있음을 알아야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;AI에게 질문을 잘 하자 = 프롬프트 엔지니어링&quot;은 크게 다르지 않지만 급격히 성장하는 AI의 흐름을 따라 활용 능력을 키우는 것은 중요하다고 생각합니다. AI를 활용할 때 어떠한 역할을 맡기고, 어떠한 형식의 답변을 원하며, 어떠한 예시 샘플을 주어 답변을 얻을것인지에 대해 고민하며 AI에게 질문한다면 같은 것을 사용하더라도 더 가치있는 답변을 얻을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 포스팅에서는 프롬프트 엔지니어링을 위한 세부 기법을 더 알아보도록 하겠습니다.&lt;/p&gt;</description>
      <category>AI Engineering/Methodology</category>
      <author>KoreaNirsa</author>
      <guid isPermaLink="true">https://nirsa.tistory.com/480</guid>
      <comments>https://nirsa.tistory.com/480#entry480comment</comments>
      <pubDate>Fri, 12 Sep 2025 21:54:58 +0900</pubDate>
    </item>
  </channel>
</rss>