<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>IT 방랑기</title>
    <link>https://jcdgods.tistory.com/</link>
    <description>IT 방랑기</description>
    <language>ko</language>
    <pubDate>Thu, 9 Jul 2026 10:33:17 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>포도알77</managingEditor>
    <item>
      <title>Python weakref.finalize 사용법</title>
      <link>https://jcdgods.tistory.com/642</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4CBD0/dJMcaaMIolm/WgbtG92UlS6tqIPm995dfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4CBD0/dJMcaaMIolm/WgbtG92UlS6tqIPm995dfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4CBD0/dJMcaaMIolm/WgbtG92UlS6tqIPm995dfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4CBD0%2FdJMcaaMIolm%2FWgbtG92UlS6tqIPm995dfk%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Python weakref.finalize 사용법&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;weakref.finalize()&lt;/code&gt;는 객체가 가비지 컬렉션될 때 실행할 정리 콜백을 등록하는 Python 표준 라이브러리 도구다. 2026년 7월 8일 확인 기준 Python 3.14.6 공식 문서는 이 finalizer가 일반 weak reference와 달리 참조 대상 객체가 수거될 때까지 살아남도록 설계됐다고 설명한다.&lt;/p&gt;
&lt;p&gt;실무에서는 임시 디렉터리 삭제, 외부 리소스 정리, 제3자 클래스에 대한 종료 훅 등록처럼 &quot;객체 소멸 시점에 한 번만 cleanup을 걸고 싶다&quot;는 상황에서 자주 검토하게 된다. 이 글은 &lt;code&gt;weakref.finalize()&lt;/code&gt;의 동작 방식, &lt;code&gt;__del__()&lt;/code&gt;와의 차이, &lt;code&gt;alive&lt;/code&gt;, &lt;code&gt;detach()&lt;/code&gt;, &lt;code&gt;atexit&lt;/code&gt; 기준만 정리한다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;weakref.finalize()&lt;/code&gt;는 언제 쓰면 될까?&lt;/h2&gt;
&lt;p&gt;공식 문서 기준 &lt;code&gt;weakref.finalize(obj, func, *args, **kwargs)&lt;/code&gt;는 &lt;code&gt;obj&lt;/code&gt;가 가비지 컬렉션될 때 호출할 finalizer 객체를 반환한다. 핵심은 반환된 finalizer 객체를 따로 보존하지 않아도 등록이 유지된다는 점이다.&lt;/p&gt;
&lt;p&gt;그래서 소유 객체의 생명주기에 맞춰 cleanup을 연결하되, 클래스 정의를 직접 바꾸기 어렵거나 &lt;code&gt;__del__()&lt;/code&gt;를 넣고 싶지 않은 경우에 적합하다. 반대로 파일 핸들처럼 종료 시점이 명확하고 즉시 정리해야 하는 자원이라면 &lt;code&gt;with&lt;/code&gt; 문이나 명시적 &lt;code&gt;close()&lt;/code&gt;가 여전히 우선이다.&lt;/p&gt;

&lt;h2&gt;가장 먼저 알아둘 동작은 무엇일까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;항목&lt;/th&gt;
      &lt;th&gt;기준&lt;/th&gt;
      &lt;th&gt;실무 해석&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;호출 횟수&lt;/td&gt;
      &lt;td&gt;live 상태에서 최대 1회만 콜백 실행&lt;/td&gt;
      &lt;td&gt;중복 cleanup 방지에 유리함&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;alive&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;아직 실행되지 않았으면 &lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;현재 finalizer 상태 점검용&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;detach()&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;finalizer를 죽이고 등록 정보 반환&lt;/td&gt;
      &lt;td&gt;자동 정리를 해제하고 수동 처리로 넘길 때 사용&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;atexit&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;기본값은 &lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;프로그램 종료 시 살아 있는 finalizer도 호출됨&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;예외 전파&lt;/td&gt;
      &lt;td&gt;GC 중 콜백 예외는 stderr에 표시되지만 호출자에게 전파되지 않음&lt;/td&gt;
      &lt;td&gt;cleanup 실패를 비즈니스 로직 예외 처리로 기대하면 안 됨&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;반환값을 꼭 변수에 저장해야 할까?&lt;/h2&gt;
&lt;p&gt;꼭 그렇지는 않다. Python 공식 문서는 &lt;code&gt;weakref.finalize()&lt;/code&gt;의 주요 장점으로 &quot;반환된 finalizer 객체를 보존하지 않아도 콜백 등록이 단순해진다&quot;는 점을 든다. 즉, 아래처럼 등록만 하고 참조를 따로 들고 있지 않아도 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import shutil
import tempfile
import weakref

class TempDir:
    def __init__(self):
        self.name = tempfile.mkdtemp()
        weakref.finalize(self, shutil.rmtree, self.name)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;다만 실행 상태 확인, 조기 실행, 등록 해제가 필요하면 반환값을 저장하는 편이 낫다. 예를 들어 &lt;code&gt;alive&lt;/code&gt;를 확인하거나 &lt;code&gt;detach()&lt;/code&gt;를 호출하려면 finalizer 객체를 참조하고 있어야 한다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;__del__()&lt;/code&gt; 대신 쓰는 이유는 무엇일까?&lt;/h2&gt;
&lt;p&gt;Python 3.14.6 공식 문서는 &lt;code&gt;__del__()&lt;/code&gt; 처리 방식이 인터프리터의 가비지 컬렉터 내부 구현에 의존해 구현체별 특성이 강하다고 설명한다. 반면 &lt;code&gt;weakref.finalize()&lt;/code&gt;는 cleanup에 필요한 함수와 인자만 명시적으로 등록하므로, 객체 전체 상태에 의존하는 &lt;code&gt;__del__()&lt;/code&gt;보다 더 견고한 대안이 될 수 있다.&lt;/p&gt;
&lt;p&gt;공식 문서의 임시 디렉터리 예시도 같은 흐름을 보여 준다. &lt;code&gt;self._finalizer = weakref.finalize(self, shutil.rmtree, self.name)&lt;/code&gt;처럼 필요한 정리 함수와 경로만 넘기면, 객체가 수거될 때나 프로그램 종료 시점에 cleanup을 걸 수 있다.&lt;/p&gt;

&lt;h2&gt;어떤 참조를 넘기면 안 될까?&lt;/h2&gt;
&lt;p&gt;이 부분은 실수하기 쉽다. 공식 문서는 &lt;code&gt;func&lt;/code&gt;, &lt;code&gt;args&lt;/code&gt;, &lt;code&gt;kwargs&lt;/code&gt;가 &lt;code&gt;obj&lt;/code&gt;를 직접 또는 간접으로 참조하면 &lt;code&gt;obj&lt;/code&gt;가 가비지 컬렉션되지 않을 수 있다고 경고한다. 특히 &lt;code&gt;func&lt;/code&gt;를 &lt;code&gt;obj&lt;/code&gt;의 bound method로 두지 말라고 명시한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import weakref

class Worker:
    def close(self):
        print(&quot;cleanup&quot;)

    def arm(self):
        weakref.finalize(self, self.close)  # 피해야 하는 형태
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 코드는 &lt;code&gt;self.close&lt;/code&gt;가 이미 &lt;code&gt;self&lt;/code&gt;를 붙잡고 있으므로 finalizer의 목적과 충돌할 수 있다. 안전한 기본값은 인스턴스 메서드 대신 모듈 함수나 정적 함수에 필요한 최소 데이터만 넘기는 방식이다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;alive&lt;/code&gt;, 직접 호출, &lt;code&gt;detach()&lt;/code&gt;는 어떻게 나눌까?&lt;/h2&gt;
&lt;p&gt;공식 문서에 따르면 live finalizer를 직접 호출하면 즉시 dead 상태가 되면서 &lt;code&gt;func(*args, **kwargs)&lt;/code&gt; 결과를 반환한다. 한 번 dead가 된 뒤에는 다시 호출해도 &lt;code&gt;None&lt;/code&gt;을 반환하고 콜백은 재실행되지 않는다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;alive&lt;/code&gt;는 아직 등록이 살아 있는지 확인하는 가장 단순한 상태 플래그다. &lt;code&gt;detach()&lt;/code&gt;는 자동 정리를 해제하고 &lt;code&gt;(obj, func, args, kwargs)&lt;/code&gt;를 돌려주므로, 소유권을 수동 정리 흐름으로 넘기고 싶을 때 맞다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import weakref

class Resource:
    pass

def cleanup(name):
    print(f&quot;{name} cleaned&quot;)

obj = Resource()
f = weakref.finalize(obj, cleanup, &quot;cache&quot;)

if f.alive:
    f()  # 즉시 정리, 이후 dead
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;프로그램 종료 시점에는 어떻게 동작할까?&lt;/h2&gt;
&lt;p&gt;공식 문서 기준 finalizer의 &lt;code&gt;atexit&lt;/code&gt; 속성 기본값은 &lt;code&gt;true&lt;/code&gt;다. 따라서 프로그램이 종료될 때까지 finalizer가 살아 있다면, &lt;code&gt;atexit&lt;/code&gt;를 &lt;code&gt;false&lt;/code&gt;로 바꾸지 않은 한 생성의 역순으로 호출된다.&lt;/p&gt;
&lt;p&gt;다만 문서는 인터프리터 종료 후반부처럼 모듈 전역이 &lt;code&gt;None&lt;/code&gt;으로 바뀔 수 있는 시점에는 finalizer가 콜백을 호출하지 않는다고도 설명한다. 또 daemonic thread에서 종료 직전 finalizer를 만드는 경우에는 종료 시 cleanup이 보장되지 않을 수 있다.&lt;/p&gt;

&lt;h2&gt;실무에서 무난한 선택 기준&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;무난한 선택&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;객체 수거 시점에 보조 cleanup을 연결하고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;weakref.finalize()&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;객체 수명에 맞춘 후처리를 한 번만 걸 수 있음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;리소스를 반드시 즉시 닫아야 한다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;with&lt;/code&gt; 또는 명시적 종료&lt;/td&gt;
      &lt;td&gt;GC 타이밍에 의존하지 않는 편이 안전함&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;클래스 정의를 수정하기 어렵다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;weakref.finalize()&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;제3자 클래스에도 외부에서 정리 훅을 연결할 수 있음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;cleanup이 객체 메서드 상태를 강하게 참조한다&lt;/td&gt;
      &lt;td&gt;설계 재검토&lt;/td&gt;
      &lt;td&gt;참조 순환으로 객체 수거가 막힐 수 있음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;정리 실패를 호출자 예외로 다뤄야 한다&lt;/td&gt;
      &lt;td&gt;별도 명시적 종료 API&lt;/td&gt;
      &lt;td&gt;GC 중 예외는 전파되지 않음&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. &lt;code&gt;weakref.finalize()&lt;/code&gt;는 &lt;code&gt;__del__()&lt;/code&gt;를 완전히 대체할까?&lt;/h3&gt;
&lt;p&gt;항상 그런 것은 아니다. 다만 Python 공식 문서는 &lt;code&gt;__del__()&lt;/code&gt;보다 &lt;code&gt;weakref.finalize()&lt;/code&gt;가 더 견고한 대안이 될 수 있다고 설명한다. 특히 cleanup에 필요한 최소 데이터만 분리해서 넘길 수 있다는 점이 실무적으로 크다.&lt;/p&gt;

&lt;h3&gt;Q. finalizer를 직접 호출하면 가비지 컬렉션 시에도 다시 실행될까?&lt;/h3&gt;
&lt;p&gt;아니다. 공식 문서 기준 finalizer는 최대 한 번만 콜백을 실행한다. 직접 호출해서 dead 상태가 되면 이후 객체가 수거돼도 콜백은 다시 실행되지 않는다.&lt;/p&gt;

&lt;h3&gt;Q. cleanup 함수 안에서 예외가 나면 &lt;code&gt;try/except&lt;/code&gt;로 받을 수 있을까?&lt;/h3&gt;
&lt;p&gt;GC 중 실행된 콜백 예외는 표준 에러에 표시되지만 호출자에게 전파되지 않는다. 따라서 cleanup 실패를 서비스 로직의 예외 분기 조건으로 삼으려면 finalizer 대신 명시적 종료 함수를 별도로 두는 편이 낫다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;weakref.finalize()&lt;/code&gt;는 객체 소멸 시점에 cleanup을 연결해야 하지만 &lt;code&gt;__del__()&lt;/code&gt;에 기대고 싶지 않을 때 검토할 만한 표준 도구다. 핵심 기준은 세 가지다. 반환 객체를 보존하지 않아도 등록은 유지된다는 점, 기본적으로 종료 시점까지 살아 있는 finalizer도 처리된다는 점, 그리고 콜백이 &lt;code&gt;obj&lt;/code&gt;를 다시 강하게 참조하면 안 된다는 점이다.&lt;/p&gt;
&lt;p&gt;대부분의 코드에서는 즉시 종료가 필요한 리소스는 &lt;code&gt;with&lt;/code&gt;나 명시적 &lt;code&gt;close()&lt;/code&gt;로 처리하고, 보조 정리 훅이나 제3자 객체 생명주기 후처리에는 &lt;code&gt;weakref.finalize()&lt;/code&gt;를 쓰는 구도가 무난하다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.python.org/3/library/weakref.html&quot; rel=&quot;noopener noreferrer&quot;&gt;Python 3.14.6 공식 문서: weakref&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/C, C++, Java, Python</category>
      <category>garbage collection</category>
      <category>python</category>
      <category>weakref</category>
      <category>weakref.finalize</category>
      <category>__del__</category>
      <category>표준라이브러리</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/642</guid>
      <comments>https://jcdgods.tistory.com/642#entry642comment</comments>
      <pubDate>Wed, 8 Jul 2026 16:07:36 +0900</pubDate>
    </item>
    <item>
      <title>Git bisect 사용법</title>
      <link>https://jcdgods.tistory.com/641</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Or3IP/dJMcaicX3fS/uEf6zEYHeO0pdjlhlnzlB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Or3IP/dJMcaicX3fS/uEf6zEYHeO0pdjlhlnzlB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Or3IP/dJMcaicX3fS/uEf6zEYHeO0pdjlhlnzlB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOr3IP%2FdJMcaicX3fS%2FuEf6zEYHeO0pdjlhlnzlB0%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Git bisect 사용법&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;git bisect&lt;/code&gt;는 특정 버그나 동작 변화가 처음 나타난 커밋을 이분 탐색 방식으로 좁혀 가는 Git 명령이다. 2026년 7월 8일 확인 기준 Git 공식 문서는 이 명령을 &quot;버그를 도입한 커밋을 찾기 위한 binary search&quot;로 설명한다.&lt;/p&gt;
&lt;p&gt;실무에서는 &quot;어느 커밋부터 테스트가 깨졌는지&quot;, &quot;어느 시점부터 성능이 느려졌는지&quot;, &quot;어느 변경에서 동작이 바뀌었는지&quot;를 빠르게 좁히는 데 쓴다. 이 글은 Git 공식 문서를 기준으로 &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;good&lt;/code&gt;/&lt;code&gt;bad&lt;/code&gt;, &lt;code&gt;skip&lt;/code&gt;, &lt;code&gt;run&lt;/code&gt;, &lt;code&gt;reset&lt;/code&gt;, 그리고 &lt;code&gt;--first-parent&lt;/code&gt; 같은 선택 기준을 정리한다.&lt;/p&gt;

&lt;h2&gt;Git bisect는 언제 쓰면 될까?&lt;/h2&gt;
&lt;p&gt;전제는 단순하다. 현재 상태 중 하나는 문제가 있고, 과거 상태 중 하나는 문제가 없다는 사실을 알고 있어야 한다. Git 공식 문서도 먼저 &quot;bad&quot; 커밋 하나와 버그가 들어오기 전의 &quot;good&quot; 커밋 하나를 알려 주면, 그 사이의 커밋을 Git이 중간 지점부터 골라 준다고 설명한다.&lt;/p&gt;
&lt;p&gt;그래서 재현 가능한 회귀 버그, 테스트 통과 여부, 특정 속성의 존재 여부처럼 각 커밋을 두 상태로 판정할 수 있을 때 가장 잘 맞는다. 반대로 결과가 들쭉날쭉하거나, 특정 시점이 good인지 bad인지 안정적으로 분류되지 않으면 bisect 결과 해석도 어려워진다.&lt;/p&gt;

&lt;h2&gt;가장 기본 흐름은 무엇일까?&lt;/h2&gt;
&lt;p&gt;가장 기본적인 흐름은 시작점 지정, 각 커밋 판정, 종료 후 원복의 세 단계다. Git 공식 문서 예시는 현재 커밋을 bad, 과거의 알려진 정상 버전을 good으로 지정한 뒤, Git이 제안하는 커밋을 하나씩 테스트하라고 안내한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git bisect start
git bisect bad
git bisect good v1.0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이후 Git이 체크아웃한 중간 커밋을 빌드하거나 테스트한 다음, 문제가 있으면 &lt;code&gt;git bisect bad&lt;/code&gt;, 없으면 &lt;code&gt;git bisect good&lt;/code&gt;을 반복한다. 모든 후보가 좁혀지면 Git이 첫 번째 bad 커밋을 알려 준다.&lt;/p&gt;

&lt;h2&gt;종료할 때 왜 &lt;code&gt;git bisect reset&lt;/code&gt;이 중요할까?&lt;/h2&gt;
&lt;p&gt;Bisect 도중에는 여러 커밋을 계속 체크아웃하므로, 끝난 뒤 원래 작업 위치로 돌아오는 정리 단계가 필요하다. 공식 문서에 따르면 &lt;code&gt;git bisect reset&lt;/code&gt;은 bisect 시작 전 체크아웃돼 있던 원래 &lt;code&gt;HEAD&lt;/code&gt;로 돌아가면서 bisection 상태를 정리한다.&lt;/p&gt;
&lt;p&gt;원하면 &lt;code&gt;git bisect reset &amp;lt;commit&amp;gt;&lt;/code&gt;처럼 다른 커밋으로 끝낼 수도 있다. 예를 들어 찾은 첫 bad 커밋 위에 그대로 머물고 싶다면 그 커밋을 인자로 줄 수 있다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;good&lt;/code&gt;/&lt;code&gt;bad&lt;/code&gt; 대신 다른 용어를 쓸 수 있을까?&lt;/h2&gt;
&lt;p&gt;그럴 수 있다. 공식 문서는 버그 도입 시점뿐 아니라 어떤 속성이 처음 나타난 시점도 찾을 수 있다고 설명하면서 &lt;code&gt;good&lt;/code&gt;/&lt;code&gt;bad&lt;/code&gt; 대신 &lt;code&gt;old&lt;/code&gt;/&lt;code&gt;new&lt;/code&gt;를 지원한다고 안내한다.&lt;/p&gt;
&lt;p&gt;예를 들어 버그 수정이 처음 들어간 커밋을 찾고 싶다면 현재 상태를 &lt;code&gt;new&lt;/code&gt;, 과거 상태를 &lt;code&gt;old&lt;/code&gt;로 두는 방식이 더 자연스럽다. 문서는 &lt;code&gt;git bisect start --term-old broken --term-new fixed&lt;/code&gt;처럼 사용자 정의 용어도 허용한다.&lt;/p&gt;

&lt;h2&gt;테스트할 수 없는 커밋이 나오면 어떻게 해야 할까?&lt;/h2&gt;
&lt;p&gt;중간 커밋이 빌드조차 되지 않거나, 현재 추적 중인 문제와 무관한 이유로 테스트가 불가능할 수 있다. 이럴 때 공식 문서는 &lt;code&gt;git bisect skip&lt;/code&gt;을 사용하라고 안내한다.&lt;/p&gt;
&lt;p&gt;다만 문서에는 중요한 제한도 있다. 찾으려는 커밋 바로 옆 커밋을 skip하면, Git은 둘 중 정확히 어느 커밋이 첫 bad인지 단정하지 못할 수 있다. 여러 커밋을 건너뛰어야 한다면 &lt;code&gt;git bisect skip v2.5..v2.6&lt;/code&gt;처럼 범위 지정도 가능하다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;git bisect run&lt;/code&gt;은 언제 유용할까?&lt;/h2&gt;
&lt;p&gt;각 단계의 판정을 스크립트로 자동화할 수 있다면 &lt;code&gt;git bisect run&lt;/code&gt;이 가장 직접적이다. 공식 문서에 따르면 이 명령에 넘긴 스크립트는 현재 소스가 good/old이면 종료 코드 0, bad/new이면 1 이상 127 이하의 값으로 끝나야 하며, 125는 테스트 불가를 의미한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git bisect start HEAD v1.0.0 --
git bisect run sh -c &quot;make test&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;공식 예시는 빌드 실패 시 125를 반환해서 해당 커밋을 skip 처리하는 스크립트도 보여 준다. 테스트가 명확하고 반복 가능하다면 수동 판정보다 자동 bisect가 훨씬 일관된 결과를 낸다.&lt;/p&gt;

&lt;h2&gt;판정 속도를 줄이는 방법은 무엇일까?&lt;/h2&gt;
&lt;p&gt;문서에는 &lt;code&gt;git bisect start&lt;/code&gt;에 추가 정보를 더 주면 탐색 범위를 더 줄일 수 있다고 나온다. 대표적인 방법은 pathspec을 함께 넘겨 특정 디렉터리나 파일 집합으로 범위를 제한하는 것이다.&lt;/p&gt;
&lt;p&gt;예를 들어 문제가 특정 서브시스템에만 있다고 강하게 의심된다면 관련 경로만 대상으로 시작할 수 있다. 또 known good 커밋을 하나만 주는 대신 여러 개를 넘기면 Git이 처음부터 후보 범위를 더 좁게 잡을 수 있다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;--first-parent&lt;/code&gt;는 언제 고려해야 할까?&lt;/h2&gt;
&lt;p&gt;공식 문서는 &lt;code&gt;--first-parent&lt;/code&gt;가 merge commit을 만났을 때 첫 번째 부모만 따라가도록 만든다고 설명한다. 이 옵션은 병합된 브랜치 안쪽의 깨진 커밋 때문에 오탐이 생기는 상황을 줄이는 데 유용하다.&lt;/p&gt;
&lt;p&gt;문서 설명대로라면 회귀가 merge를 통해 들어왔을 때, merge commit 자체가 문제 도입 지점으로 식별되고 병합 브랜치의 조상은 무시된다. 따라서 &quot;어느 merge가 메인 라인에 문제를 들여왔는지&quot;를 찾는 작업이라면 기본 bisect보다 더 실무적인 선택이 될 수 있다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;--no-checkout&lt;/code&gt;은 어떤 경우에 맞을까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;--no-checkout&lt;/code&gt;은 각 단계에서 working tree를 실제로 체크아웃하지 않고, 대신 &lt;code&gt;BISECT_HEAD&lt;/code&gt; 참조만 갱신하게 한다. 공식 문서는 체크아웃된 트리가 필요 없는 검사라면 이 옵션이 유용할 수 있다고 설명한다.&lt;/p&gt;
&lt;p&gt;예를 들어 객체 그래프 무결성처럼 워킹 디렉터리를 만들지 않아도 되는 검사를 자동화할 때가 해당한다. 일반적인 애플리케이션 빌드나 테스트처럼 파일 체크아웃이 필요한 경우에는 기본 동작이 더 자연스럽다.&lt;/p&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. 현재 커밋이 꼭 bad여야 하나?&lt;/h3&gt;
&lt;p&gt;그럴 필요는 없다. 중요한 것은 bad 상태와 good 상태를 구분할 수 있는 양 끝점을 잡는 것이다. 공식 문서도 &lt;code&gt;git bisect start &amp;lt;bad&amp;gt; &amp;lt;good&amp;gt;&lt;/code&gt; 형태와, start 후 별도로 bad와 good을 지정하는 형태를 모두 보여 준다.&lt;/p&gt;

&lt;h3&gt;Q. 버그가 아니라 특정 기능이 처음 들어온 커밋도 찾을 수 있나?&lt;/h3&gt;
&lt;p&gt;가능하다. 공식 문서는 이런 일반화된 사용을 위해 &lt;code&gt;old&lt;/code&gt;/&lt;code&gt;new&lt;/code&gt; 용어와 사용자 정의 용어를 지원한다고 설명한다. 즉 &quot;고장&quot;이 아니라 &quot;속성 변화&quot;를 찾는 도구로도 쓸 수 있다.&lt;/p&gt;

&lt;h3&gt;Q. bisect 중 다른 커밋을 잠깐 봤다가 다시 돌아올 수 있나?&lt;/h3&gt;
&lt;p&gt;가능하다. 공식 문서에는 자동으로 다음 단계를 진행하지 못한 뒤 이어서 계속해야 할 경우 &lt;code&gt;git bisect next&lt;/code&gt;를 사용할 수 있다고 나온다. 다만 작업이 끝나면 결국 &lt;code&gt;git bisect reset&lt;/code&gt;으로 상태를 정리하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git bisect&lt;/code&gt;는 재현 가능한 good/bad 기준만 있다면 회귀 원인을 가장 체계적으로 좁히는 Git 기본 도구다. 기본 흐름은 &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;good&lt;/code&gt;/&lt;code&gt;bad&lt;/code&gt;, 필요 시 &lt;code&gt;skip&lt;/code&gt; 또는 &lt;code&gt;run&lt;/code&gt;, 종료 후 &lt;code&gt;reset&lt;/code&gt;으로 이해하면 충분하다.&lt;/p&gt;
&lt;p&gt;대부분의 팀에서는 먼저 수동으로 재현 조건을 안정화한 뒤, 가능하면 &lt;code&gt;git bisect run&lt;/code&gt;으로 자동화하는 편이 효율적이다. merge 단위 원인 추적이라면 &lt;code&gt;--first-parent&lt;/code&gt;, 체크아웃이 필요 없는 검사라면 &lt;code&gt;--no-checkout&lt;/code&gt;을 함께 고려하면 된다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-bisect&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Documentation: git-bisect&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/Git, IDE, 툴 관련</category>
      <category>CLI</category>
      <category>Debugging</category>
      <category>Developer Tools</category>
      <category>GIT</category>
      <category>git bisect</category>
      <category>version control</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/641</guid>
      <comments>https://jcdgods.tistory.com/641#entry641comment</comments>
      <pubDate>Wed, 8 Jul 2026 11:32:20 +0900</pubDate>
    </item>
    <item>
      <title>HTML inert 속성 기준 정리</title>
      <link>https://jcdgods.tistory.com/640</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GMOYa/dJMcai4W27Y/5wvcuKRbuHh8gPqyWkrQZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GMOYa/dJMcai4W27Y/5wvcuKRbuHh8gPqyWkrQZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GMOYa/dJMcai4W27Y/5wvcuKRbuHh8gPqyWkrQZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGMOYa%2FdJMcai4W27Y%2F5wvcuKRbuHh8gPqyWkrQZ1%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;HTML inert 속성 기준 정리&lt;/h1&gt;
&lt;p&gt;배경을 가린 모달이나 아직 활성화되면 안 되는 패널을 화면에 남겨 두되, 사용자 입력과 포커스는 막아야 할 때가 있다. 2026년 7월 7일 기준 WHATWG HTML Living Standard은 &lt;code&gt;inert&lt;/code&gt;를 요소와 그 flat tree 하위 요소를 사용자 에이전트가 비활성 상태로 다루게 하는 boolean attribute로 정의한다.&lt;/p&gt;
&lt;p&gt;실무에서는 &quot;정말 상호작용 자체를 막아야 하는가&quot;, &quot;시각적으로도 가려져 있는가&quot;, &quot;단순 비노출이면 &lt;code&gt;hidden&lt;/code&gt;이 더 맞는가&quot;를 먼저 나누면 선택이 빨라진다. 이 글은 &lt;code&gt;inert&lt;/code&gt;의 동작, &lt;code&gt;dialog.showModal()&lt;/code&gt;과의 관계, &lt;code&gt;hidden&lt;/code&gt;과의 차이, 무난한 적용 기준만 정리한다.&lt;/p&gt;

&lt;h2&gt;먼저 무엇을 막을까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;항목&lt;/th&gt;
      &lt;th&gt;&lt;code&gt;inert&lt;/code&gt;&lt;/th&gt;
      &lt;th&gt;&lt;code&gt;hidden&lt;/code&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&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&gt;
      &lt;td&gt;포커스/클릭&lt;/td&gt;
      &lt;td&gt;포커스 불가, 명령 비작동&lt;/td&gt;
      &lt;td&gt;보이지 않으므로 상호작용 대상이 아님&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;접근성 API 노출&lt;/td&gt;
      &lt;td&gt;노출되지 않음&lt;/td&gt;
      &lt;td&gt;표시되지 않으므로 보통 접근 대상이 아님&lt;/td&gt;
    &lt;/tr&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;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;&lt;code&gt;inert&lt;/code&gt;는 정확히 어떤 의미일까?&lt;/h2&gt;
&lt;p&gt;HTML 표준은 &lt;code&gt;inert&lt;/code&gt;가 지정된 요소와 그 flat tree 하위 요소를 inert 상태로 만들어야 한다고 설명한다. 이 상태의 노드는 클릭 같은 사용자 상호작용 대상이 되지 않고, 텍스트 선택과 find-in-page 대상에서도 제외될 수 있으며, 포커스를 받을 수 없고 접근성 API에도 노출되지 않는다.&lt;/p&gt;
&lt;p&gt;중요한 점은 &lt;code&gt;inert&lt;/code&gt;가 단순 CSS 편의 기능이 아니라 &quot;사용자에게 지금 접근 가능한가&quot;를 표현하는 의미적 상태라는 점이다. 표준도 사용자가 이해하거나 조작해야 하는 핵심 콘텐츠를 inert subtree 안에 두지 말라고 권고한다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;dialog.showModal()&lt;/code&gt;과는 왜 함께 언급될까?&lt;/h2&gt;
&lt;p&gt;표준은 modal dialog가 top layer에 올라가면, 그 외 문서 영역이 inert가 되는 메커니즘을 설명한다. 그래서 모달을 직접 구현하면서 배경에 임의로 &lt;code&gt;tabindex&lt;/code&gt;를 조정하거나 클릭 방지 오버레이만 덧씌우는 방식보다, 실제 모달 의미와 배경 비활성 상태를 함께 맞추는 쪽이 더 일관적이다.&lt;/p&gt;
&lt;p&gt;다만 모든 비활성 UI가 &lt;code&gt;dialog&lt;/code&gt;인 것은 아니다. 사이드 패널이 아직 열리지 않았거나, 저장 중이라 폼 일부만 잠가야 할 때처럼 모달이 아닌 경우에는 해당 구역에 &lt;code&gt;inert&lt;/code&gt;를 직접 쓰는 편이 자연스럽다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;hidden&lt;/code&gt;으로는 왜 부족할 수 있을까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;hidden&lt;/code&gt;은 &quot;현재 직접 관련 없어서 렌더링하지 않는다&quot;는 의미에 가깝다. 반면 &lt;code&gt;inert&lt;/code&gt;는 요소가 화면 구조 안에 남아 있더라도 사용자 상호작용과 접근성 노출을 함께 끊고 싶을 때 맞는다.&lt;/p&gt;
&lt;p&gt;예를 들어 배경 콘텐츠가 시각적으로 그대로 보이는 모달 화면이라면 &lt;code&gt;hidden&lt;/code&gt;은 맞지 않는다. 콘텐츠를 숨기려는 것이 아니라, 잠시 조작 불가 상태로 바꾸려는 것이기 때문이다. 반대로 아코디언 패널을 아예 접어서 보이지 않게 할 때는 &lt;code&gt;inert&lt;/code&gt;보다 &lt;code&gt;hidden&lt;/code&gt;, &lt;code&gt;details&lt;/code&gt;, 또는 적절한 표시 제어가 더 직접적일 수 있다.&lt;/p&gt;

&lt;h2&gt;언제 쓰는 편이 무난할까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;무난한 선택&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;모달 뒤 배경 전체를 잠가야 한다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;dialog&lt;/code&gt; + modal 동작 우선&lt;/td&gt;
      &lt;td&gt;표준이 정의한 inert 처리와 자연스럽게 연결됨&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;로딩 중이라 폼 일부를 잠시 비활성화한다&lt;/td&gt;
      &lt;td&gt;해당 컨테이너에 &lt;code&gt;inert&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;하위 입력과 포커스를 한 번에 막을 수 있음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;콘텐츠를 아예 숨겨야 한다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;hidden&lt;/code&gt; 또는 다른 표시 제어&lt;/td&gt;
      &lt;td&gt;목적이 비상호작용이 아니라 비노출임&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;개별 버튼 하나만 잠그면 된다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;disabled&lt;/code&gt; 우선 검토&lt;/td&gt;
      &lt;td&gt;컨테이너 전체 inert보다 의도가 더 직접적임&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;실제 마크업은 어떻게 두면 될까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;inert&lt;/code&gt;는 boolean attribute라서 존재 자체가 상태를 뜻한다. 로딩 중이나 오버레이 표시 시점에 속성을 붙이고, 다시 상호작용을 허용할 때 제거하는 방식이 가장 단순하다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;section id=&quot;profile-form&quot; inert&amp;gt;
  &amp;lt;input type=&quot;text&quot; name=&quot;nickname&quot; /&amp;gt;
  &amp;lt;button type=&quot;submit&quot;&amp;gt;저장&amp;lt;/button&amp;gt;
&amp;lt;/section&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;다만 표준은 inert subtree 안의 콘텐츠가 사용자에게도 시각적으로 가려져 있어야 한다고 본다. 즉 속성만 붙이고 외형상 평소와 똑같이 보여 주면 &quot;왜 클릭이 안 되지&quot;라는 혼란을 만들기 쉽다. 로딩 오버레이, dim 처리, 패널 닫힘 상태 같은 시각적 신호를 함께 두는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;자주 놓치는 점&lt;/h2&gt;
&lt;p&gt;첫째, &lt;code&gt;inert&lt;/code&gt;는 하위 요소 전체를 묶어서 비활성화하는 도구다. 개별 폼 컨트롤 하나의 활성 여부만 바꾸는 문제라면 &lt;code&gt;disabled&lt;/code&gt;가 더 정확할 수 있다.&lt;/p&gt;
&lt;p&gt;둘째, 표준은 modal dialog 같은 경우를 예외로 들며, 하위 요소가 조상으로부터의 inertness를 벗어날 수 있는 경우가 있다고 설명한다. 그래서 복잡한 오버레이 구조에서는 &quot;컨테이너 하나에 속성을 붙였으니 모든 것이 똑같이 잠긴다&quot;라고 단순 가정하지 말고 실제 포커스 이동과 접근성을 같이 확인해야 한다.&lt;/p&gt;
&lt;p&gt;셋째, &lt;code&gt;inert&lt;/code&gt;는 보안 경계가 아니다. 개발자 도구나 스크립트로 상태를 바꾸는 것은 가능하므로, 서버 검증이나 권한 제어를 대신할 수는 없다.&lt;/p&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;Q. 클릭만 막고 화면은 그대로 두고 싶다면 &lt;code&gt;pointer-events: none&lt;/code&gt;이면 충분할까?&lt;/h3&gt;
&lt;p&gt;항상 충분하지는 않다. &lt;code&gt;pointer-events: none&lt;/code&gt;은 포인터 입력 차단에는 도움이 되지만, 포커스 이동이나 접근성 API 노출까지 함께 정리하지는 않는다. 사용자 상호작용 전체를 멈추려는 목적이라면 &lt;code&gt;inert&lt;/code&gt;가 더 직접적이다.&lt;/p&gt;

&lt;h3&gt;Q. 숨김 패널에도 &lt;code&gt;inert&lt;/code&gt;를 같이 붙여야 할까?&lt;/h3&gt;
&lt;p&gt;경우에 따라 다르다. 이미 &lt;code&gt;hidden&lt;/code&gt; 등으로 렌더링 대상에서 빠진다면 추가 &lt;code&gt;inert&lt;/code&gt;가 꼭 필요하지 않을 수 있다. 반대로 화면에 남아 있으나 잠겨 있어야 하는 구역이라면 &lt;code&gt;inert&lt;/code&gt;의 의미가 분명해진다.&lt;/p&gt;

&lt;h3&gt;Q. 모달 구현에서 직접 배경 컨테이너에 &lt;code&gt;inert&lt;/code&gt;를 붙여도 될까?&lt;/h3&gt;
&lt;p&gt;가능은 하지만, 먼저 &lt;code&gt;dialog.showModal()&lt;/code&gt;로 해결 가능한 구조인지 보는 편이 좋다. 표준이 modal dialog와 배경 inert 관계를 이미 정의하고 있기 때문이다. 커스텀 레이아웃 때문에 직접 제어해야 한다면 포커스 이동, 스크린 리더 노출, 시각적 obscuring까지 함께 확인해야 한다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;inert&lt;/code&gt;는 &quot;안 보이게&quot;가 아니라 &quot;지금은 접근 불가하게&quot;를 표현하는 속성으로 이해하면 실수가 줄어든다. 화면에 남아 있는 배경이나 로딩 중 구역을 사용자 입력, 포커스, 접근성 노출에서 함께 분리해야 할 때 가장 유용하다.&lt;/p&gt;
&lt;p&gt;2026년 7월 7일 기준 WHATWG HTML Living Standard과 그 문서에 표시된 MDN 호환성 정보만 놓고 보면, &lt;code&gt;inert&lt;/code&gt;는 현재 엔진 전반에서 지원되는 기능으로 안내된다. 다만 선택 기준은 여전히 단순하다. 비노출이면 &lt;code&gt;hidden&lt;/code&gt;, 개별 컨트롤 비활성화면 &lt;code&gt;disabled&lt;/code&gt;, 화면에 남아 있는 구역 전체를 잠글 때는 &lt;code&gt;inert&lt;/code&gt;가 무난하다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://html.spec.whatwg.org/multipage/interaction.html#the-inert-attribute&quot; rel=&quot;noopener noreferrer&quot;&gt;WHATWG HTML Living Standard: The inert attribute&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://html.spec.whatwg.org/multipage/interaction.html#inert-subtrees&quot; rel=&quot;noopener noreferrer&quot;&gt;WHATWG HTML Living Standard: Inert subtrees&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/HTML, Javascript, CSS</category>
      <category>dialog</category>
      <category>frontend</category>
      <category>html</category>
      <category>inert</category>
      <category>웹표준</category>
      <category>접근성</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/640</guid>
      <comments>https://jcdgods.tistory.com/640#entry640comment</comments>
      <pubDate>Tue, 7 Jul 2026 16:38:44 +0900</pubDate>
    </item>
    <item>
      <title>PostgreSQL transaction_timeout 기준 정리</title>
      <link>https://jcdgods.tistory.com/639</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciMU2s/dJMb991lKgS/f0HQTjhhREA35kqxGUWKKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciMU2s/dJMb991lKgS/f0HQTjhhREA35kqxGUWKKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciMU2s/dJMb991lKgS/f0HQTjhhREA35kqxGUWKKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciMU2s%2FdJMb991lKgS%2Ff0HQTjhhREA35kqxGUWKKk%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;PostgreSQL transaction_timeout 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;transaction_timeout&lt;/code&gt;은 트랜잭션 상태로 머문 전체 시간을 기준으로 세션을 종료하는 PostgreSQL 설정이다. 2026년 7월 7일 기준 PostgreSQL 18 공식 문서를 보면 명시적인 &lt;code&gt;BEGIN&lt;/code&gt; 트랜잭션뿐 아니라 단일 SQL 문이 암묵적으로 만드는 트랜잭션에도 적용되며, 기본값 0은 비활성화다.&lt;/p&gt;
&lt;p&gt;실무에서 헷갈리는 지점은 이 값이 &lt;code&gt;statement_timeout&lt;/code&gt;, &lt;code&gt;lock_timeout&lt;/code&gt;, &lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt;, &lt;code&gt;idle_session_timeout&lt;/code&gt;과 무엇이 다른가다. 이 글은 각 타임아웃이 재는 구간과 함께 어떤 상황에서 &lt;code&gt;transaction_timeout&lt;/code&gt;을 우선 검토할 만한지 기준 중심으로 정리한다.&lt;/p&gt;

&lt;h2&gt;transaction_timeout은 무엇을 끊을까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;설정&lt;/th&gt;
      &lt;th&gt;재는 대상&lt;/th&gt;
      &lt;th&gt;트리거 시점&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;transaction_timeout&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;트랜잭션 전체 수명&lt;/td&gt;
      &lt;td&gt;트랜잭션 상태가 지정 시간보다 길어질 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;statement_timeout&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;개별 SQL 문 실행 시간&lt;/td&gt;
      &lt;td&gt;문장 하나가 오래 걸릴 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;lock_timeout&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;락 대기 시간&lt;/td&gt;
      &lt;td&gt;락을 기다리는 구간이 길 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;트랜잭션 안의 유휴 시간&lt;/td&gt;
      &lt;td&gt;열린 트랜잭션에서 클라이언트 입력 대기가 길 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;idle_session_timeout&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;트랜잭션 밖의 유휴 시간&lt;/td&gt;
      &lt;td&gt;세션이 놀고 있을 때&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;transaction_timeout&lt;/code&gt;의 핵심은 &quot;실행 중인지, 락을 기다리는 중인지, 잠깐 멈춰 있는지&quot;를 따지지 않고 트랜잭션 상태 자체가 너무 오래 이어지면 세션을 끝낸다는 점이다. 따라서 긴 트랜잭션 자체를 제한하고 싶을 때 가장 직접적인 제어 수단이다.&lt;/p&gt;

&lt;h2&gt;공식 문서 기준 동작 범위는 어디까지일까?&lt;/h2&gt;
&lt;p&gt;PostgreSQL 18 공식 문서는 이 제한이 명시적인 트랜잭션뿐 아니라 단일 SQL 문이 암묵적으로 시작하는 트랜잭션에도 적용된다고 설명한다. 즉, 애플리케이션이 &lt;code&gt;BEGIN&lt;/code&gt;을 직접 쓰지 않아도 오래 걸리는 단일 작업이라면 영향을 받을 수 있다.&lt;/p&gt;
&lt;p&gt;또 하나 중요한 점은 prepared transaction은 이 타임아웃 대상이 아니라는 점이다. 따라서 2단계 커밋을 쓰는 환경에서는 일반 트랜잭션 보호와 prepared transaction 관리 기준을 따로 봐야 한다.&lt;/p&gt;

&lt;h2&gt;다른 타임아웃과 같이 걸면 무엇이 우선될까?&lt;/h2&gt;
&lt;p&gt;공식 문서는 &lt;code&gt;transaction_timeout&lt;/code&gt;이 &lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt; 또는 &lt;code&gt;statement_timeout&lt;/code&gt;보다 짧거나 같으면 더 긴 쪽은 무시된다고 설명한다. 결국 세션을 더 빨리 끝내는 제한이 있으면 늦게 발동할 제한은 의미가 없다는 뜻이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SET statement_timeout = '30s';
SET transaction_timeout = '20s';
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 경우 개별 문장 제한 30초를 두더라도 트랜잭션 전체가 20초를 넘기면 먼저 종료될 수 있다. 반대로 긴 배치 작업에서 문장 하나는 짧지만 트랜잭션 전체가 과도하게 길어지는 문제가 있다면 &lt;code&gt;statement_timeout&lt;/code&gt;만으로는 제어가 부족할 수 있다.&lt;/p&gt;

&lt;h2&gt;언제 유용할까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;검토 이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;애플리케이션 버그로 트랜잭션 종료가 자주 누락될 때&lt;/td&gt;
      &lt;td&gt;오래 열린 트랜잭션을 상한선으로 제한할 수 있음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;문장별 시간보다 트랜잭션 전체 길이가 중요한 배치 작업&lt;/td&gt;
      &lt;td&gt;짧은 문장이 반복돼도 총 소요 시간을 제한할 수 있음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;락 대기와 유휴 상태를 모두 포함해 긴 트랜잭션을 막고 싶을 때&lt;/td&gt;
      &lt;td&gt;상태 구분 없이 전체 트랜잭션 수명을 본다는 점이 맞음&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;반대로 세션 풀러나 미들웨어가 예기치 않은 연결 종료에 민감한 환경이라면 타임아웃을 넓게 잡거나 사용자·역할 단위로 제한하는 편이 안전하다. PostgreSQL 문서도 이런 종류의 타임아웃을 전역 &lt;code&gt;postgresql.conf&lt;/code&gt;에 두는 방식은 모든 세션에 영향을 주므로 권장하지 않는다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;idle_in_transaction_session_timeout과는 어떻게 다를까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt;은 열린 트랜잭션에서 클라이언트 질의를 기다리는 유휴 시간만 본다. 그래서 질의 실행 중이거나 락을 기다리는 동안에는 그 자체로 발동 기준이 되지 않는다.&lt;/p&gt;
&lt;p&gt;반면 &lt;code&gt;transaction_timeout&lt;/code&gt;은 트랜잭션이 시작된 뒤 끝날 때까지의 전체 시간을 본다. 실무 기준으로 정리하면, &quot;가만히 방치된 트랜잭션&quot;이 문제면 &lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt;, &quot;어떤 이유로든 너무 오래 사는 트랜잭션&quot;이 문제면 &lt;code&gt;transaction_timeout&lt;/code&gt;이 더 직접적이다.&lt;/p&gt;

&lt;h2&gt;lock_timeout, statement_timeout만으로 부족한 경우는?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;lock_timeout&lt;/code&gt;은 락을 기다리는 구간에만 걸리고, &lt;code&gt;statement_timeout&lt;/code&gt;은 개별 문장 기준이다. 예를 들어 문장 하나하나는 5초 안에 끝나지만 같은 트랜잭션이 수백 개 문장을 묶어서 10분 이상 지속된다면 둘 다 크게 문제를 드러내지 못할 수 있다.&lt;/p&gt;
&lt;p&gt;이런 패턴에서는 &lt;code&gt;transaction_timeout&lt;/code&gt;이 트랜잭션 상한선을 명확하게 만든다. 다만 너무 짧게 잡으면 정상적인 대량 처리나 마이그레이션도 잘릴 수 있으므로, 운영 배치와 온라인 요청의 성격을 구분해 적용 범위를 나누는 편이 맞다.&lt;/p&gt;

&lt;h2&gt;운영에서 무난한 적용 기준&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;대상&lt;/th&gt;
      &lt;th&gt;무난한 접근&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&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&gt;
      &lt;td&gt;배치·마이그레이션 역할&lt;/td&gt;
      &lt;td&gt;별도 역할 정책 또는 더 긴 값 사용&lt;/td&gt;
      &lt;td&gt;정상 작업이 오래 걸릴 수 있음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;전역 기본값&lt;/td&gt;
      &lt;td&gt;신중하게 사용&lt;/td&gt;
      &lt;td&gt;PostgreSQL 문서도 전체 세션 영향 때문에 권장하지 않음&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;Q. &lt;code&gt;transaction_timeout&lt;/code&gt;은 락 대기 시간만 재나?&lt;/h3&gt;
&lt;p&gt;아니다. 락 대기, 실행, 유휴 상태를 따로 나누지 않고 트랜잭션 전체 시간을 본다. 락 대기만 제한하려면 &lt;code&gt;lock_timeout&lt;/code&gt;이 더 직접적이다.&lt;/p&gt;

&lt;h3&gt;Q. 단일 SQL 문에도 적용되나?&lt;/h3&gt;
&lt;p&gt;적용된다. PostgreSQL 18 공식 문서는 단일 문장에 대응하는 암묵적 트랜잭션에도 이 제한이 적용된다고 설명한다.&lt;/p&gt;

&lt;h3&gt;Q. prepared transaction에도 적용되나?&lt;/h3&gt;
&lt;p&gt;아니다. 공식 문서의 주석은 prepared transaction은 이 타임아웃 대상이 아니라고 명시한다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;transaction_timeout&lt;/code&gt;은 개별 문장이나 특정 대기 구간이 아니라 트랜잭션 전체 수명을 제한하는 설정이다. 그래서 오래 살아남는 트랜잭션 자체를 줄이고 싶을 때 가장 먼저 비교할 값이다.&lt;/p&gt;
&lt;p&gt;2026년 7월 7일 기준 PostgreSQL 18 공식 문서만 놓고 보면, 무난한 기준은 전역 기본값보다 역할·세션 단위 적용을 우선 검토하고, &lt;code&gt;statement_timeout&lt;/code&gt;이나 &lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt;과의 우선순위를 함께 설계하는 것이다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/runtime-config-client.html&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL 18 공식 문서: Client Connection Defaults&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/서버, DBMS</category>
      <category>DBMS</category>
      <category>lock_timeout</category>
      <category>PostgreSQL</category>
      <category>sql</category>
      <category>statement_timeout</category>
      <category>transaction_timeout</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/639</guid>
      <comments>https://jcdgods.tistory.com/639#entry639comment</comments>
      <pubDate>Tue, 7 Jul 2026 10:25:23 +0900</pubDate>
    </item>
    <item>
      <title>리눅스 Device Tree aliases 기준 정리</title>
      <link>https://jcdgods.tistory.com/638</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kWXUh/dJMcacqe2Jq/88lWVD6Qk33o9wY85acpD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kWXUh/dJMcacqe2Jq/88lWVD6Qk33o9wY85acpD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kWXUh/dJMcacqe2Jq/88lWVD6Qk33o9wY85acpD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkWXUh%2FdJMcacqe2Jq%2F88lWVD6Qk33o9wY85acpD0%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;리눅스 Device Tree aliases 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;/aliases&lt;/code&gt; 노드는 Device Tree에서 긴 경로를 짧은 이름으로 참조할 수 있게 만드는 루트 노드다. 2026년 7월 6일 기준 Devicetree Specification stable 문서는 alias가 루트의 &lt;code&gt;/aliases&lt;/code&gt; 아래에 정의되며, 각 속성값은 실제 노드의 전체 경로 문자열이라고 설명한다.&lt;/p&gt;
&lt;p&gt;실무에서 헷갈리는 지점은 세 가지다. &lt;code&gt;aliases&lt;/code&gt;는 노드를 새로 만드는 기능이 아니고, &lt;code&gt;label&lt;/code&gt;이나 &lt;code&gt;phandle&lt;/code&gt;의 대체도 아니며, Linux에서는 모든 참조가 자동으로 alias만 쓰이도록 강제되지도 않는다. 이 글은 &lt;code&gt;/aliases&lt;/code&gt;의 의미, 이름 규칙, &lt;code&gt;/chosen/stdout-path&lt;/code&gt;와의 관계, Linux 커널에서 확인할 기준만 간단히 정리한다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;/aliases&lt;/code&gt;는 무엇을 담을까?&lt;/h2&gt;
&lt;p&gt;Devicetree Specification에 따르면 &lt;code&gt;/aliases&lt;/code&gt;는 루트에 놓이는 선택적 노드다. 각 속성의 이름이 alias 이름이고, 값은 Device Tree 안의 특정 노드를 가리키는 전체 경로 문자열이다. 값은 리프 노드만 가리켜야 하는 것은 아니며, 경로로 해석 가능한 노드라면 대상이 될 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dts&quot;&gt;/ {
    aliases {
        serial0 = &quot;/soc/serial@fe000000&quot;;
        ethernet0 = &quot;/soc/ethernet@ff0e0000&quot;;
    };
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;핵심은 alias가 &quot;짧은 이름에서 전체 경로로 가는 매핑&quot;이라는 점이다. 실제 하드웨어 속성은 여전히 대상 노드에 있어야 하고, alias에는 주소나 interrupt 같은 장치 정보 자체를 넣지 않는다.&lt;/p&gt;

&lt;h2&gt;이름은 어떻게 정해야 할까?&lt;/h2&gt;
&lt;p&gt;Devicetree Specification stable 문서는 alias 이름을 소문자 1자 이상 31자 이하로 제한하고, 허용 문자를 소문자 &lt;code&gt;a-z&lt;/code&gt;, 숫자 &lt;code&gt;0-9&lt;/code&gt;, 하이픈 &lt;code&gt;-&lt;/code&gt;으로 규정한다. 따라서 대문자, 밑줄, 공백이 들어간 이름은 표준 형식과 맞지 않는다.&lt;/p&gt;
&lt;p&gt;문서 예시는 &lt;code&gt;serial0&lt;/code&gt;, &lt;code&gt;ethernet0&lt;/code&gt; 같은 형태를 사용한다. 실무에서도 기능군과 번호를 조합한 짧은 이름이 가장 읽기 쉽다. 다만 번호가 곧 Linux의 모든 장치 번호를 보장한다고 일반화하면 안 된다. alias는 경로를 줄이는 수단이지, 모든 사용자 공간 이름 규칙을 단독으로 정의하는 메커니즘은 아니다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;label&lt;/code&gt;, &lt;code&gt;phandle&lt;/code&gt;와는 무엇이 다를까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;항목&lt;/th&gt;
      &lt;th&gt;주 용도&lt;/th&gt;
      &lt;th&gt;무난한 이해 방식&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;/aliases&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;짧은 이름에서 전체 경로를 가리키는 문자열 매핑&lt;/td&gt;
      &lt;td&gt;경로 별칭&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;DTS &lt;code&gt;label&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;소스 안에서 노드를 읽기 쉽게 이름 붙이기&lt;/td&gt;
      &lt;td&gt;작성 편의용 식별자&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;phandle&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;속성 값 안에서 다른 노드를 참조하기&lt;/td&gt;
      &lt;td&gt;구조화된 노드 참조&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;label&lt;/code&gt;은 DTS 작성 단계에서 &lt;code&gt;&amp;amp;uart0&lt;/code&gt;처럼 참조하기 쉽게 만드는 표기이고, &lt;code&gt;phandle&lt;/code&gt;은 컴파일된 트리 안에서 노드 간 연결을 표현하는 표준 속성이다. 반면 &lt;code&gt;/aliases&lt;/code&gt;는 경로 문자열을 줄이는 용도라서 성격이 다르다. 같은 노드에 label, phandle, alias가 모두 존재할 수 있지만 서로 대체 관계는 아니다.&lt;/p&gt;

&lt;h2&gt;Linux는 alias를 어디서 쓸까?&lt;/h2&gt;
&lt;p&gt;Linux kernel 문서의 DeviceTree Kernel API를 보면 &lt;code&gt;of_find_node_opts_by_path()&lt;/code&gt;는 인자로 받은 문자열이 &lt;code&gt;/&lt;/code&gt;로 시작하지 않으면 &lt;code&gt;/aliases&lt;/code&gt; 노드의 속성 이름으로 해석할 수 있다고 설명한다. 문서상 유효한 형태는 전체 경로, alias 단독, alias 뒤에 상대 경로를 붙인 형태다.&lt;/p&gt;
&lt;p&gt;즉 Linux에서는 alias가 단순한 문서 관례가 아니라, 커널 코드가 실제로 해석할 수 있는 경로 축약 수단이다. 다만 모든 커널 인터페이스가 alias 문자열만 받는 것은 아니다. 어떤 바인딩이나 속성은 여전히 &lt;code&gt;phandle&lt;/code&gt; 기반 참조를 요구하므로, alias를 &quot;모든 참조의 공통 포맷&quot;으로 이해하면 범위를 넘는다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;/chosen/stdout-path&lt;/code&gt;와는 왜 같이 언급될까?&lt;/h2&gt;
&lt;p&gt;Devicetree Specification stable 문서는 &lt;code&gt;/chosen/stdout-path&lt;/code&gt;가 부트 콘솔 출력 장치를 가리키는 문자열이며, 이 값은 alias일 수 있다고 명시한다. 또한 값에 &lt;code&gt;:&lt;/code&gt;가 들어가면 그 앞부분까지만 경로로 본다고 설명한다.&lt;/p&gt;
&lt;p&gt;그래서 아래처럼 전체 경로 대신 &lt;code&gt;serial0:115200n8&lt;/code&gt; 같은 형식이 가능하다. 이때 경로 해석의 첫 단계는 &lt;code&gt;serial0&lt;/code&gt; alias를 실제 노드 경로로 바꾸는 것이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dts&quot;&gt;/ {
    aliases {
        serial0 = &quot;/soc/serial@fe000000&quot;;
    };

    chosen {
        stdout-path = &quot;serial0:115200n8&quot;;
    };
};&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;본문 중간 기준 한 번에 정리&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;질문&lt;/th&gt;
      &lt;th&gt;무난한 기준&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;alias를 꼭 만들어야 할까?&lt;/td&gt;
      &lt;td&gt;필수는 아니지만, 반복해서 참조할 경로가 있으면 만드는 편이 낫다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;/aliases&lt;/code&gt;는 선택적 노드다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;아무 이름이나 써도 될까?&lt;/td&gt;
      &lt;td&gt;소문자, 숫자, 하이픈만 쓰고 31자 이내로 유지한다&lt;/td&gt;
      &lt;td&gt;스펙이 이름 형식을 제한한다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;label&lt;/code&gt; 대신 alias만 써도 될까?&lt;/td&gt;
      &lt;td&gt;그렇지 않다&lt;/td&gt;
      &lt;td&gt;작성 편의용 식별자와 경로 별칭은 역할이 다르다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;phandle&lt;/code&gt; 대신 alias를 속성 값에 넣어도 될까?&lt;/td&gt;
      &lt;td&gt;바인딩이 요구하는 참조 형식을 먼저 본다&lt;/td&gt;
      &lt;td&gt;많은 구조화된 참조는 여전히 &lt;code&gt;phandle&lt;/code&gt; 기반이다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;부트 콘솔 지정에 alias를 써도 될까?&lt;/td&gt;
      &lt;td&gt;가능하다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;stdout-path&lt;/code&gt; 값은 alias일 수 있다고 문서에 적혀 있다&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;자주 놓치는 점&lt;/h2&gt;
&lt;p&gt;Linux kernel의 &quot;Linux and the Devicetree&quot; 문서는 &lt;code&gt;/aliases&lt;/code&gt;, &lt;code&gt;/chosen&lt;/code&gt;, &lt;code&gt;/memory&lt;/code&gt;를 정보성 노드로 설명한다. 즉 &lt;code&gt;/aliases&lt;/code&gt;는 장치를 하나 더 기술하는 곳이 아니라, 다른 노드를 읽기 쉽게 가리키는 보조 정보다.&lt;/p&gt;
&lt;p&gt;또 하나는 alias 값이 전체 경로 문자열이라는 점이다. DTS 안에서 자주 보이는 &lt;code&gt;&amp;amp;label&lt;/code&gt; 표기는 사람이 쓰는 소스 문법이고, &lt;code&gt;/aliases&lt;/code&gt;의 값 형식과는 다르다. alias 값에 label 참조를 그대로 넣는 예시를 표준 규칙으로 이해하면 혼동이 생긴다.&lt;/p&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;Q. alias는 루트가 아닌 하위 노드에 두어도 될까?&lt;/h3&gt;
&lt;p&gt;권장 수준이 아니라 스펙상 위치가 정해져 있다. Devicetree Specification은 alias 노드가 루트에 있어야 하고 노드 이름도 &lt;code&gt;/aliases&lt;/code&gt;여야 한다고 설명한다.&lt;/p&gt;

&lt;h3&gt;Q. alias 값은 노드 이름만 적어도 될까?&lt;/h3&gt;
&lt;p&gt;그보다 더 구체적이어야 한다. 스펙은 alias 값이 Device Tree 안의 전체 경로를 나타내는 문자열이라고 설명한다. 필요하면 그 뒤에 상대 경로를 붙여 Linux API에서 해석할 수는 있지만, alias 정의 자체는 대상 경로를 분명히 적는 편이 맞다.&lt;/p&gt;

&lt;h3&gt;Q. alias를 만들면 해당 장치가 자동으로 활성화될까?&lt;/h3&gt;
&lt;p&gt;그렇지 않다. alias는 경로를 짧게 부르는 정보일 뿐이며, 장치 활성화 여부는 대상 노드의 &lt;code&gt;status&lt;/code&gt;, 바인딩, 드라이버 동작 같은 다른 조건이 결정한다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;/aliases&lt;/code&gt;는 Device Tree에서 긴 노드 경로를 짧고 일관된 이름으로 줄여 주는 보조 노드다. 표준 기준으로는 루트에 놓고, 소문자·숫자·하이픈만 사용하며, 값에는 실제 노드의 전체 경로 문자열을 넣는다.&lt;/p&gt;
&lt;p&gt;2026년 7월 6일 기준 공개 문서만 놓고 보면, alias는 특히 &lt;code&gt;stdout-path&lt;/code&gt; 같은 문자열 기반 경로 참조에서 실용적이다. 반면 노드 간 구조화된 연결까지 대신하는 도구는 아니므로, &lt;code&gt;label&lt;/code&gt;과 &lt;code&gt;phandle&lt;/code&gt;의 역할은 그대로 분리해서 이해하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://devicetree-specification.readthedocs.io/en/stable/devicenodes.html&quot; rel=&quot;noopener noreferrer&quot;&gt;Devicetree Specification stable: Device Node Requirements&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.kernel.org/devicetree/kernel-api.html&quot; rel=&quot;noopener noreferrer&quot;&gt;Linux kernel documentation: DeviceTree Kernel API&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.kernel.org/devicetree/usage-model.html&quot; rel=&quot;noopener noreferrer&quot;&gt;Linux kernel documentation: Linux and the Devicetree&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/리눅스 커널</category>
      <category>aliases</category>
      <category>device tree</category>
      <category>Devicetree</category>
      <category>DTS</category>
      <category>Linux Kernel</category>
      <category>stdout-path</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/638</guid>
      <comments>https://jcdgods.tistory.com/638#entry638comment</comments>
      <pubDate>Mon, 6 Jul 2026 17:26:15 +0900</pubDate>
    </item>
    <item>
      <title>Python ExitStack 사용법</title>
      <link>https://jcdgods.tistory.com/637</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YrIJ4/dJMcaalygxi/PsIo11i0ihwSQo3lAXfdGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YrIJ4/dJMcaalygxi/PsIo11i0ihwSQo3lAXfdGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YrIJ4/dJMcaalygxi/PsIo11i0ihwSQo3lAXfdGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYrIJ4%2FdJMcaalygxi%2FPsIo11i0ihwSQo3lAXfdGk%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Python ExitStack 사용법&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;with&lt;/code&gt; 문을 여러 개 중첩해야 하는데 개수가 입력값에 따라 달라지거나, 컨텍스트 매니저가 아닌 정리 함수까지 함께 묶어야 할 때가 있다. 2026년 7월 6일 기준 Python 3.14.6 공식 문서를 보면 &lt;code&gt;contextlib.ExitStack&lt;/code&gt;은 이런 상황에서 여러 컨텍스트 매니저와 cleanup callback을 프로그램적으로 결합하도록 설계된 도구다.&lt;/p&gt;
&lt;p&gt;실무에서는 &quot;리소스 개수가 가변적인가&quot;, &quot;정리만 필요한 콜백도 함께 등록해야 하는가&quot;, &quot;동기와 비동기를 섞어야 하는가&quot;를 먼저 나누면 선택이 빨라진다. 이 글은 &lt;code&gt;ExitStack&lt;/code&gt;, &lt;code&gt;AsyncExitStack&lt;/code&gt;, &lt;code&gt;enter_context()&lt;/code&gt;, &lt;code&gt;push()&lt;/code&gt;, &lt;code&gt;callback()&lt;/code&gt;, &lt;code&gt;pop_all()&lt;/code&gt;의 차이와 무난한 사용 기준만 정리한다.&lt;/p&gt;

&lt;h2&gt;먼저 무엇이 다를까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;도구&lt;/th&gt;
      &lt;th&gt;역할&lt;/th&gt;
      &lt;th&gt;무난한 사용 상황&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;ExitStack&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;동기 컨텍스트 매니저와 정리 콜백을 하나의 스택으로 관리&lt;/td&gt;
      &lt;td&gt;열 파일 수, 락 수, 옵션 리소스 개수가 실행 중 달라질 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;AsyncExitStack&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;비동기 컨텍스트 매니저와 코루틴 정리를 포함한 비동기 스택 관리&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;async with&lt;/code&gt; 안에서 연결 여러 개를 열고 함께 닫아야 할 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;enter_context(cm)&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;컨텍스트 매니저를 즉시 열고 &lt;code&gt;__exit__()&lt;/code&gt;를 스택에 등록&lt;/td&gt;
      &lt;td&gt;일반 &lt;code&gt;with&lt;/code&gt;를 반복문으로 확장하고 싶을 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;push(exit)&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;__enter__()&lt;/code&gt; 없이 &lt;code&gt;__exit__()&lt;/code&gt; 또는 exit 스타일 콜백만 등록&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;__enter__()&lt;/code&gt; 도중 실패 가능성이 있어 부분 정리가 필요할 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;callback(fn, ...)&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;임의의 정리 함수를 등록&lt;/td&gt;
      &lt;td&gt;컨텍스트 매니저가 아닌 release 함수만 있을 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;pop_all()&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;등록된 정리 스택을 새 &lt;code&gt;ExitStack&lt;/code&gt;으로 이전&lt;/td&gt;
      &lt;td&gt;모두 성공한 뒤에는 자동 정리를 넘기고 싶을 때&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;&lt;code&gt;ExitStack&lt;/code&gt;은 언제 쓰면 될까?&lt;/h2&gt;
&lt;p&gt;공식 문서는 &lt;code&gt;ExitStack&lt;/code&gt;의 대표 용도로 &quot;가변 개수의 컨텍스트 매니저를 하나의 &lt;code&gt;with&lt;/code&gt; 문으로 다루는 경우&quot;를 제시한다. 예를 들어 파일 목록 길이가 실행마다 다르면 정적인 &lt;code&gt;with open(...), open(...)&lt;/code&gt; 형태보다 &lt;code&gt;ExitStack&lt;/code&gt;이 더 직접적이다.&lt;/p&gt;
&lt;p&gt;또 한 가지 장점은 정리 순서가 명확하다는 점이다. 각 콜백은 등록의 역순으로 실행되므로, 여러 &lt;code&gt;with&lt;/code&gt; 문을 중첩한 것과 비슷한 정리 모델을 유지할 수 있다. 공식 문서도 예외가 바뀌거나 억제되면 바깥쪽 콜백은 그 갱신된 상태를 기준으로 호출된다고 설명한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from contextlib import ExitStack

filenames = [&quot;a.txt&quot;, &quot;b.txt&quot;, &quot;c.txt&quot;]

with ExitStack() as stack:
    files = [stack.enter_context(open(name, &quot;r&quot;, encoding=&quot;utf-8&quot;))
             for name in filenames]
    first_lines = [f.readline().strip() for f in files]
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;&lt;code&gt;enter_context()&lt;/code&gt;와 &lt;code&gt;push()&lt;/code&gt;는 왜 구분될까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;enter_context(cm)&lt;/code&gt;는 일반적인 경우의 기본값이다. 공식 문서 기준 이 메서드는 컨텍스트 매니저의 &lt;code&gt;__enter__()&lt;/code&gt;를 실행하고, 반환값을 돌려주며, 대응되는 &lt;code&gt;__exit__()&lt;/code&gt;를 스택에 등록한다. 3.11부터는 컨텍스트 매니저가 아닌 값을 넘기면 &lt;code&gt;AttributeError&lt;/code&gt;가 아니라 &lt;code&gt;TypeError&lt;/code&gt;를 올린다.&lt;/p&gt;
&lt;p&gt;반면 &lt;code&gt;push()&lt;/code&gt;는 &lt;code&gt;__enter__()&lt;/code&gt;를 호출하지 않는다. 그래서 리소스를 일부 확보한 뒤 나머지 검증이 실패할 수 있는 &lt;code&gt;__enter__()&lt;/code&gt; 구현 내부나, 이미 열린 객체의 exit 동작만 나중에 묶고 싶을 때 더 적합하다. 공식 문서도 이 메서드를 &quot;부분적으로 진행된 &lt;code&gt;__enter__()&lt;/code&gt; 구현을 정리하는 용도&quot;로 설명한다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;callback()&lt;/code&gt;은 무엇이 다를까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;callback()&lt;/code&gt;은 컨텍스트 매니저가 아닌 일반 함수도 정리 스택에 올릴 수 있게 해 준다. 선택적으로 생성한 임시 디렉터리 삭제 함수, 별도 API의 release 함수, 수동으로 열어 둔 핸들 정리 같은 흐름에서 유용하다.&lt;/p&gt;
&lt;p&gt;다만 공식 문서는 &lt;code&gt;callback()&lt;/code&gt;으로 등록한 함수는 예외 상세 정보를 받지 않기 때문에 예외를 억제할 수 없다고 명시한다. 예외를 보고 suppress 여부를 결정해야 한다면 &lt;code&gt;callback()&lt;/code&gt;보다 &lt;code&gt;push()&lt;/code&gt;로 exit 시그니처를 따르는 콜백을 등록하는 편이 맞다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from contextlib import ExitStack

def release_resource(resource):
    resource.close()

with ExitStack() as stack:
    resource = acquire_resource()
    stack.callback(release_resource, resource)
    use(resource)
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;&lt;code&gt;pop_all()&lt;/code&gt;은 언제 필요할까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;pop_all()&lt;/code&gt;은 &quot;중간에 하나라도 실패하면 지금까지 연 것들을 자동으로 닫되, 모두 성공했다면 열린 상태를 유지하고 싶다&quot;는 요구에 맞는다. 공식 문서는 여러 파일을 all-or-nothing 방식으로 여는 예제를 통해, 기존 스택의 정리 콜백을 새 &lt;code&gt;ExitStack&lt;/code&gt;으로 옮긴 뒤 나중에 명시적으로 &lt;code&gt;close()&lt;/code&gt;하게 할 수 있다고 설명한다.&lt;/p&gt;
&lt;p&gt;즉, 자원 확보 단계에서는 실패 복구를 자동화하고, 성공 이후의 생명주기는 호출자가 넘겨받는 패턴이다. 이때 &lt;code&gt;pop_all()&lt;/code&gt; 자체는 콜백을 실행하지 않고 이전만 수행한다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;AsyncExitStack&lt;/code&gt;은 언제 고르면 될까?&lt;/h2&gt;
&lt;p&gt;비동기 코드에서는 정리 단계도 &lt;code&gt;await&lt;/code&gt;가 필요할 수 있다. 공식 문서 기준 &lt;code&gt;AsyncExitStack&lt;/code&gt;은 동기·비동기 컨텍스트 매니저를 함께 다루고, 코루틴 기반 cleanup도 등록할 수 있도록 만든 비동기 버전이다. 닫기 메서드는 &lt;code&gt;close()&lt;/code&gt;가 아니라 &lt;code&gt;aclose()&lt;/code&gt;다.&lt;/p&gt;
&lt;p&gt;예를 들어 비동기 데이터베이스 연결이나 네트워크 세션을 여러 개 열고 일부만 선택적으로 추가하는 흐름이라면 &lt;code&gt;async with AsyncExitStack()&lt;/code&gt;이 더 자연스럽다. 비동기 컨텍스트 매니저는 &lt;code&gt;enter_async_context()&lt;/code&gt;, 코루틴 cleanup은 &lt;code&gt;push_async_callback()&lt;/code&gt; 같은 전용 메서드로 등록한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from contextlib import AsyncExitStack

async with AsyncExitStack() as stack:
    connections = [
        await stack.enter_async_context(get_connection())
        for _ in range(3)
    ]
    await run_query(connections)
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;운영에서 무난한 선택 기준&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;무난한 선택&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;컨텍스트 매니저 개수가 실행 중 달라진다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;ExitStack&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;반복문과 조건문으로 &lt;code&gt;with&lt;/code&gt; 구성을 만들 수 있음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;정리 함수만 있고 컨텍스트 매니저는 없다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;callback()&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;임의 함수와 인자를 그대로 등록할 수 있음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;예외 억제 가능성이 있는 exit 로직을 붙여야 한다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;push()&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;exit 시그니처를 따라 예외 정보를 받을 수 있음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;모두 성공한 뒤 자원 닫기를 호출자에게 넘기고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;pop_all()&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;현재 스택을 새 &lt;code&gt;ExitStack&lt;/code&gt;으로 이전할 수 있음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;비동기 컨텍스트와 코루틴 정리를 함께 관리한다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;AsyncExitStack&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;aclose()&lt;/code&gt;와 async 전용 등록 메서드를 제공함&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;자주 놓치는 점&lt;/h2&gt;
&lt;p&gt;공식 문서는 등록된 콜백이 스택이 가비지 컬렉션될 때 자동 호출되지는 않는다고 분명히 적고 있다. 따라서 &lt;code&gt;ExitStack&lt;/code&gt;를 단순히 변수에만 담아 두고 컨텍스트 종료나 &lt;code&gt;close()&lt;/code&gt; 호출을 생략하면 정리가 보장되지 않는다.&lt;/p&gt;
&lt;p&gt;또 하나는 재사용성과 재진입성의 차이다. 공식 문서 예시 기준 하나의 &lt;code&gt;ExitStack&lt;/code&gt; 인스턴스는 여러 번 다시 사용할 수는 있지만, 같은 인스턴스를 중첩해서 쓰면 안쪽 &lt;code&gt;with&lt;/code&gt; 종료 시점에 스택이 비워져 바깥쪽 정리 흐름이 깨질 수 있다. 중첩이 필요하면 새 인스턴스를 따로 만드는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;Q. 파일 여러 개를 여는 정도면 꼭 &lt;code&gt;ExitStack&lt;/code&gt;이 필요할까?&lt;/h3&gt;
&lt;p&gt;항상 그렇지는 않다. 개수가 고정돼 있다면 일반 &lt;code&gt;with&lt;/code&gt; 문이 더 단순하다. &lt;code&gt;ExitStack&lt;/code&gt;은 개수가 가변적이거나, 조건부 자원과 일반 cleanup 함수를 함께 묶어야 할 때 장점이 커진다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;callback()&lt;/code&gt;으로 등록한 함수도 예외를 막을 수 있을까?&lt;/h3&gt;
&lt;p&gt;그렇지 않다. 공식 문서는 &lt;code&gt;callback()&lt;/code&gt;으로 등록한 함수는 예외 세부 정보를 받지 않으므로 예외를 suppress할 수 없다고 설명한다. 그런 동작이 필요하면 &lt;code&gt;push()&lt;/code&gt; 쪽을 봐야 한다.&lt;/p&gt;

&lt;h3&gt;Q. 같은 &lt;code&gt;ExitStack&lt;/code&gt; 객체를 바깥 &lt;code&gt;with&lt;/code&gt;와 안쪽 &lt;code&gt;with&lt;/code&gt;에서 같이 써도 될까?&lt;/h3&gt;
&lt;p&gt;권장되지 않는다. 공식 문서 예시는 같은 인스턴스를 중첩 사용하면 안쪽 블록 종료 시 스택이 비워져 의도하지 않은 결과가 나온다고 설명한다. 중첩 구조라면 별도 인스턴스를 만들어야 한다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ExitStack&lt;/code&gt;은 &quot;가변적인 &lt;code&gt;with&lt;/code&gt;&quot;를 안전하게 표현하는 도구로 이해하면 가장 실용적이다. 기본값은 &lt;code&gt;enter_context()&lt;/code&gt;이고, 일반 정리 함수는 &lt;code&gt;callback()&lt;/code&gt;, exit 시그니처가 필요한 정리는 &lt;code&gt;push()&lt;/code&gt;, 성공 후 소유권 이전은 &lt;code&gt;pop_all()&lt;/code&gt;로 나누면 판단이 빨라진다.&lt;/p&gt;
&lt;p&gt;2026년 7월 6일 기준 Python 3.14.6 공식 문서만 놓고 보면, 동기 자원 정리는 &lt;code&gt;ExitStack&lt;/code&gt;, 비동기 자원 정리는 &lt;code&gt;AsyncExitStack&lt;/code&gt;으로 시작하는 편이 무난하다. 다만 자원 개수가 고정돼 있다면 일반 &lt;code&gt;with&lt;/code&gt; 문이 여전히 가장 읽기 쉬운 선택이다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.python.org/3/library/contextlib.html&quot; rel=&quot;noopener noreferrer&quot;&gt;Python 3.14.6 공식 문서: contextlib&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/C, C++, Java, Python</category>
      <category>AsyncExitStack</category>
      <category>contextlib</category>
      <category>ExitStack</category>
      <category>python</category>
      <category>with</category>
      <category>표준라이브러리</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/637</guid>
      <comments>https://jcdgods.tistory.com/637#entry637comment</comments>
      <pubDate>Mon, 6 Jul 2026 12:07:36 +0900</pubDate>
    </item>
    <item>
      <title>canonical 링크 기준 정리</title>
      <link>https://jcdgods.tistory.com/636</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eaNd5R/dJMcaiqnDN7/k15YjkZNOKeNIr3yxWLu51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eaNd5R/dJMcaiqnDN7/k15YjkZNOKeNIr3yxWLu51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eaNd5R/dJMcaiqnDN7/k15YjkZNOKeNIr3yxWLu51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeaNd5R%2FdJMcaiqnDN7%2Fk15YjkZNOKeNIr3yxWLu51%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;canonical 링크 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;rel=&quot;canonical&quot;&lt;/code&gt;은 중복되거나 매우 유사한 URL 집합에서 어떤 URL을 대표본으로 볼지 알려 주는 신호다. 2026년 7월 5일 확인 기준으로 RFC 6596과 Google Search Central 문서를 보면, canonical은 중복 콘텐츠 정리와 검색 신호 통합에 유용하지만 항상 강제 명령처럼 동작하는 것은 아니다.&lt;/p&gt;
&lt;p&gt;실무에서 중요한 질문은 네 가지다. 언제 canonical을 써야 하는지, HTML &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt;와 HTTP 헤더 중 무엇을 고를지, 리디렉션이나 sitemap과 어떤 차이가 있는지, 그리고 크롤러가 canonical을 어디까지 믿어야 하는지다. 이 글은 공식 문서에 나온 범위 안에서만 선택 기준을 정리한다.&lt;/p&gt;

&lt;h2&gt;canonical 링크는 무엇을 뜻할까?&lt;/h2&gt;
&lt;p&gt;RFC 6596은 canonical link relation을 중복 콘텐츠 집합에서 선호하는 IRI를 지정하는 방법으로 설명한다. 예를 들어 같은 상품 페이지가 추적 파라미터나 정렬 파라미터 때문에 여러 URL로 열리더라도, 그중 대표 URL 하나를 canonical로 둘 수 있다.&lt;/p&gt;
&lt;p&gt;이 문서에서 중요한 기준은 target canonical URL이 원본 페이지와 중복되거나 그보다 더 큰 내용을 포함해야 한다는 점이다. 즉 내용이 일부만 겹치거나, 오히려 정보가 줄어드는 페이지를 canonical 대상으로 삼으면 안 된다.&lt;/p&gt;

&lt;h2&gt;왜 canonical을 지정할까?&lt;/h2&gt;
&lt;p&gt;Google 문서는 canonical을 지정하는 이유로 검색 결과에 보여 줄 URL 통일, 중복 URL에 흩어진 신호 통합, 측정 지표 단순화, 중복 페이지 크롤링 시간 절감을 든다. 특히 같은 콘텐츠가 여러 URL로 노출되는 쇼핑몰, 문서 사이트, 필터 페이지에서 유용하다.&lt;/p&gt;
&lt;p&gt;반대로 canonical이 없다고 해서 반드시 문제가 생기는 것은 아니다. Google은 canonical을 명시하지 않아도 자체적으로 대표 URL을 고를 수 있다고 설명한다. 따라서 canonical은 &quot;필수 설정&quot;이라기보다, 중복 URL 구조가 분명할 때 정확도를 높이는 명시적 신호에 가깝다.&lt;/p&gt;

&lt;h2&gt;어떤 URL을 canonical로 잡아야 할까?&lt;/h2&gt;
&lt;p&gt;가장 안전한 기준은 사용자가 실제로 공유하고 검색 결과에서 보게 하려는 URL 하나를 정하는 것이다. RFC 6596은 canonical 대상이 중복 콘텐츠이거나 상위 집합이어야 한다고 설명하고, Google도 검색 결과에 보여 주고 싶은 URL을 기준으로 canonical을 고르라고 안내한다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;추적 파라미터만 다른 URL은 정규 URL 하나로 모은다.&lt;/li&gt;
  &lt;li&gt;HTTP와 HTTPS가 함께 열리면 보통 HTTPS 쪽을 canonical 후보로 둔다.&lt;/li&gt;
  &lt;li&gt;페이지 나뉜 문서를 전체 보기 한 장으로 제공한다면, 전체 보기 페이지가 각 분할 페이지의 상위 집합일 때만 canonical 후보가 될 수 있다.&lt;/li&gt;
  &lt;li&gt;언어가 다른 페이지끼리는 같은 언어 안에서 canonical을 정하고, 다국어 연결은 &lt;code&gt;hreflang&lt;/code&gt;으로 분리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;특히 여러 페이지로 나뉜 글에서 2페이지나 3페이지가 1페이지를 canonical로 가리키는 방식은 피해야 한다. RFC 6596은 이런 구성이 내용 손실을 만들 수 있다고 명시한다.&lt;/p&gt;

&lt;h2&gt;HTML canonical과 HTTP 헤더 canonical은 무엇이 다를까?&lt;/h2&gt;
&lt;p&gt;Google은 두 가지 명시적 canonical 방법을 지원한다. 하나는 HTML &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; 안의 &lt;code&gt;&amp;lt;link rel=&quot;canonical&quot; href=&quot;...&quot;&amp;gt;&lt;/code&gt;이고, 다른 하나는 HTTP 응답의 &lt;code&gt;Link&lt;/code&gt; 헤더다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;HTML &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt;: 일반 HTML 문서에서 가장 흔하다.&lt;/li&gt;
  &lt;li&gt;HTTP &lt;code&gt;Link&lt;/code&gt; 헤더: PDF 같은 비HTML 문서에도 쓸 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Google 문서는 두 방식을 동시에 써도 되지만, 서로 다른 URL을 넣을 위험이 있어서 하나를 정해 일관되게 쓰는 편이 더 낫다고 권장한다. HTML 방식은 반드시 &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; 안에 있어야 하며, Google은 상대 경로도 해석할 수 있지만 절대 URL 사용을 권장한다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;canonical과 redirect, sitemap은 어떻게 다를까?&lt;/h2&gt;
&lt;p&gt;Google 문서는 canonicalization 방법을 비교하면서 permanent redirect, &lt;code&gt;rel=&quot;canonical&quot;&lt;/code&gt;, sitemap을 함께 설명한다. 이 셋은 목적이 비슷해 보여도 쓰는 상황이 다르다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;redirect: 기존 중복 URL을 더 이상 직접 노출하지 않을 때 적합하다.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;rel=&quot;canonical&quot;&lt;/code&gt;: 사용자는 여러 URL에 접근할 수 있지만, 대표 URL을 알려 주고 싶을 때 적합하다.&lt;/li&gt;
  &lt;li&gt;sitemap: canonical 후보를 대량으로 제시하는 보조 신호로 유용하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Google 문서에 따르면 sitemap은 구현과 유지가 쉽지만, &lt;code&gt;rel=&quot;canonical&quot;&lt;/code&gt;보다 약한 신호다. 또한 redirect는 중복 페이지를 사실상 정리하는 방법이고, canonical은 여러 URL을 그대로 두면서 대표 URL을 선언하는 방법이라는 점이 다르다.&lt;/p&gt;

&lt;h2&gt;잘못된 canonical은 왜 문제가 될까?&lt;/h2&gt;
&lt;p&gt;RFC 6596은 canonical 대상이 4xx 오류를 반환하거나, 다른 URL로 다시 canonical을 거는 체인 구조이거나, permanent redirect의 출발점인 URL인 경우를 피하라고 권고한다. 이런 상황에서는 애플리케이션이 canonical을 무시하고 자체 판단을 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;Google도 비슷하게 같은 페이지에 서로 다른 canonical 신호를 섞지 말라고 안내한다. 예를 들어 sitemap에는 A URL을 적고 HTML canonical에는 B URL을 넣으면 해석이 모호해진다. 또한 &lt;code&gt;robots.txt&lt;/code&gt;나 &lt;code&gt;noindex&lt;/code&gt;는 canonical 대체 수단으로 쓰지 말라고 명시한다.&lt;/p&gt;

&lt;h2&gt;크롤러나 수집기는 canonical을 어떻게 해석하면 좋을까?&lt;/h2&gt;
&lt;p&gt;검색엔진이 아닌 일반 크롤러를 만든다면 canonical을 절대 진실처럼 믿기보다는, 중복 후보 정리용 힌트로 다루는 편이 안전하다. RFC 6596 자체도 잘못 선언된 canonical이 있을 수 있음을 전제한다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;대표 URL 저장 키를 정할 때 canonical을 우선 참고한다.&lt;/li&gt;
  &lt;li&gt;하지만 본문 유사도, HTTP 상태 코드, 리디렉션 결과도 함께 확인한다.&lt;/li&gt;
  &lt;li&gt;canonical 대상이 404이거나 내용이 더 적다면 그대로 병합하지 않는다.&lt;/li&gt;
  &lt;li&gt;HTML 문서는 &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;의 canonical을, 비HTML은 HTTP &lt;code&gt;Link&lt;/code&gt; 헤더를 함께 확인한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 접근은 검색엔진의 내부 알고리즘을 흉내 내자는 뜻이 아니라, 공식 문서가 전제하는 실패 가능성을 수집 파이프라인에도 반영하자는 의미다.&lt;/p&gt;

&lt;h2&gt;실무 체크리스트&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;대표 URL이 실제로 중복 페이지의 내용과 같거나 더 큰 범위인지 먼저 확인한다.&lt;/li&gt;
  &lt;li&gt;HTML 문서는 &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; 안에 canonical을 넣고 절대 URL을 사용한다.&lt;/li&gt;
  &lt;li&gt;PDF 같은 비HTML 문서는 HTTP &lt;code&gt;Link&lt;/code&gt; 헤더 canonical을 검토한다.&lt;/li&gt;
  &lt;li&gt;redirect, sitemap, 내부 링크가 서로 다른 대표 URL을 가리키지 않게 맞춘다.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;robots.txt&lt;/code&gt;, &lt;code&gt;noindex&lt;/code&gt;, URL fragment를 canonical 대체 수단으로 쓰지 않는다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. canonical을 넣으면 반드시 그 URL이 검색 결과 대표본이 될까?&lt;/h3&gt;
&lt;p&gt;항상 그렇다고 단정할 수는 없다. Google 문서는 canonical 신호를 종합해 대표 URL을 선택하며, 명시하지 않아도 자체적으로 대표 URL을 고를 수 있다고 설명한다.&lt;/p&gt;

&lt;h3&gt;Q. HTML canonical과 HTTP 헤더 canonical을 같이 써도 될까?&lt;/h3&gt;
&lt;p&gt;지원되기는 하지만, Google은 두 방식을 함께 쓸 때 서로 다른 URL을 넣는 실수가 더 발생하기 쉽다고 설명한다. 특별한 이유가 없다면 한 가지 방식을 일관되게 유지하는 편이 안전하다.&lt;/p&gt;

&lt;h3&gt;Q. 모든 중복 URL을 redirect로 바꾸는 편이 더 좋을까?&lt;/h3&gt;
&lt;p&gt;중복 URL을 실제로 없앨 계획이라면 redirect가 더 직접적일 수 있다. 하지만 여러 진입 URL을 계속 제공해야 한다면 canonical이 더 맞는 선택이다. 두 방법은 대체 관계라기보다 운영 목적이 다르다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;rel=&quot;canonical&quot;&lt;/code&gt;은 중복 URL 집합에서 대표 URL을 알리는 명시적 신호다. 2026년 7월 5일 확인 기준으로 RFC 6596은 canonical 대상을 중복 또는 상위 집합 콘텐츠로 제한하고, Google 문서는 HTML &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt;와 HTTP &lt;code&gt;Link&lt;/code&gt; 헤더를 모두 지원하되 절대 URL과 일관된 신호 구성을 권장한다. 운영 기준으로는 redirect가 필요한 상황과 canonical이 필요한 상황을 분리하고, 크롤러에서는 canonical을 힌트로 해석하는 구성이 가장 무난하다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc6596.html&quot; rel=&quot;noopener noreferrer&quot;&gt;RFC 6596: The Canonical Link Relation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls&quot; rel=&quot;noopener noreferrer&quot;&gt;Google Search Central: How to specify a canonical URL with rel=&quot;canonical&quot; and other methods&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>시리즈물/데이터 수집을 위한 크롤링</category>
      <category>canonical</category>
      <category>crawling</category>
      <category>google search</category>
      <category>HTTP Header</category>
      <category>rel canonical</category>
      <category>SEO</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/636</guid>
      <comments>https://jcdgods.tistory.com/636#entry636comment</comments>
      <pubDate>Sun, 5 Jul 2026 15:14:16 +0900</pubDate>
    </item>
    <item>
      <title>systemd override.conf 사용법</title>
      <link>https://jcdgods.tistory.com/635</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rFYpJ/dJMcahZihSt/qDnmPFlnPHKobte0vVSg81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rFYpJ/dJMcahZihSt/qDnmPFlnPHKobte0vVSg81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rFYpJ/dJMcahZihSt/qDnmPFlnPHKobte0vVSg81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrFYpJ%2FdJMcahZihSt%2FqDnmPFlnPHKobte0vVSg81%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;systemd override.conf 사용법&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;systemctl edit&lt;/code&gt;는 systemd 유닛 파일을 직접 덮어쓰기보다 작은 변경만 분리해서 적용할 때 가장 먼저 떠올릴 도구다. 2026년 7월 5일 기준 systemd 261.1 공식 문서를 보면 기본 동작은 메인 유닛 파일을 바꾸는 것이 아니라 drop-in 파일을 만들거나 수정하는 방식이며, 기본 파일명은 &lt;code&gt;override.conf&lt;/code&gt;다.&lt;/p&gt;
&lt;p&gt;실무에서는 &quot;패키지 업데이트를 따라가야 하는가&quot;, &quot;전체 유닛을 교체해야 하는가&quot;, &quot;재부팅 전까지만 임시로 바꿀 것인가&quot;가 핵심 기준이다. 이 글은 &lt;code&gt;systemctl edit&lt;/code&gt;, &lt;code&gt;--full&lt;/code&gt;, &lt;code&gt;--runtime&lt;/code&gt;, &lt;code&gt;revert&lt;/code&gt;, drop-in 우선순위를 공식 문서 기준으로 정리한다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;systemctl edit&lt;/code&gt;는 무엇을 만들까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;방식&lt;/th&gt;
      &lt;th&gt;무엇이 만들어지나&lt;/th&gt;
      &lt;th&gt;언제 쓰나&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;기본 &lt;code&gt;systemctl edit unit.service&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;drop-in 파일, 기본 이름은 &lt;code&gt;override.conf&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;특정 설정 몇 개만 추가·변경할 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;systemctl edit --drop-in=name.conf unit.service&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;이름을 지정한 drop-in 파일&lt;/td&gt;
      &lt;td&gt;목적별 설정을 파일별로 나눌 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;systemctl edit --full unit.service&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;메인 유닛 파일 대체본&lt;/td&gt;
      &lt;td&gt;의존성 제거처럼 drop-in만으로 부족할 때&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;systemctl edit&lt;/code&gt;는 편집이 끝나면 설정을 다시 읽어 들인다. 공식 문서는 이 동작이 &lt;code&gt;systemctl daemon-reload --system&lt;/code&gt; 또는 &lt;code&gt;--user&lt;/code&gt;와 동등하다고 설명한다. 따라서 파일만 손으로 바꾼 뒤 재적용을 깜빡하는 상황을 줄이려면, 가능하면 편집도 &lt;code&gt;systemctl edit&lt;/code&gt; 경로로 처리하는 편이 낫다.&lt;/p&gt;

&lt;h2&gt;왜 기본 방식은 drop-in일까?&lt;/h2&gt;
&lt;p&gt;공식 문서의 핵심 기준은 벤더 유닛 파일을 그대로 두고 필요한 설정만 덧붙이는 것이다. drop-in은 메인 유닛 파일이 먼저 파싱된 뒤 알파벳 순서로 추가 적용된다. 그래서 패키지 업데이트로 &lt;code&gt;/usr/lib/systemd/system&lt;/code&gt; 아래 원본 유닛이 개선되더라도, 내가 바꾼 부분만 유지한 채 새 기본값을 함께 가져가기 쉽다.&lt;/p&gt;
&lt;p&gt;반대로 &lt;code&gt;--full&lt;/code&gt;로 메인 유닛 파일을 대체하면 벤더 쪽 원본 전체를 사실상 직접 관리하게 된다. 이는 일부 항목만 고치는 데는 과하고, 이후 배포판 패키지 업데이트가 들어와도 자동 반영되지 않는다는 부담이 있다.&lt;/p&gt;

&lt;h2&gt;drop-in 우선순위는 어떻게 결정될까?&lt;/h2&gt;
&lt;p&gt;systemd 261.1 공식 문서는 시스템 서비스 기준 drop-in을 &lt;code&gt;/etc/systemd/system&lt;/code&gt;, &lt;code&gt;/run/systemd/system&lt;/code&gt;, &lt;code&gt;/usr/lib/systemd/system&lt;/code&gt; 아래에 둘 수 있고, 우선순위는 &lt;code&gt;/etc&lt;/code&gt;가 가장 높고 그다음이 &lt;code&gt;/run&lt;/code&gt;, 마지막이 &lt;code&gt;/usr/lib&lt;/code&gt;라고 설명한다. 서로 다른 파일명은 디렉터리 위치와 상관없이 사전식 순서로 적용된다.&lt;/p&gt;
&lt;p&gt;즉, 운영자가 장기 설정을 두려면 보통 &lt;code&gt;/etc/systemd/system/unit.service.d/&lt;/code&gt;가 기준이 되고, 재부팅 시 사라져도 되는 임시 변경은 &lt;code&gt;/run&lt;/code&gt; 아래 runtime drop-in이 어울린다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;systemctl edit nginx.service
systemctl edit --drop-in=limits.conf nginx.service
systemctl cat nginx.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;systemctl cat&lt;/code&gt;는 메인 fragment와 drop-in 소스 파일을 함께 보여 준다. 공식 문서는 단, 디스크 파일이 바뀌었는데 아직 &lt;code&gt;daemon-reload&lt;/code&gt;를 하지 않았다면 실제 매니저가 이해한 내용과 화면 출력이 다를 수 있다고 경고한다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;--full&lt;/code&gt;이 필요한 경우는 언제일까?&lt;/h2&gt;
&lt;p&gt;drop-in은 설정 추가와 일부 값 재정의에 강하지만, 모든 변경을 다 처리할 수 있는 것은 아니다. 공식 문서 예시는 list 성격의 일반 설정은 빈 값으로 먼저 리셋한 뒤 새 값을 줄 수 있지만, &lt;code&gt;After=&lt;/code&gt; 같은 dependency 계열은 drop-in에서 빈 목록으로 초기화할 수 없다고 설명한다. 이런 경우에는 전체 유닛을 대체하는 방식이 필요하다.&lt;/p&gt;
&lt;p&gt;그래서 의존성 제거, 원본 섹션 구조의 대폭 수정, 벤더 유닛 자체가 목적에 맞지 않는 수준의 재설계가 필요하면 &lt;code&gt;systemctl edit --full&lt;/code&gt; 쪽이 맞다. 반대로 &lt;code&gt;Environment=&lt;/code&gt;, &lt;code&gt;Restart=&lt;/code&gt;, &lt;code&gt;LimitNOFILE=&lt;/code&gt;, &lt;code&gt;ExecStart&lt;/code&gt; 재정의 같은 국소 수정은 drop-in이 일반적으로 더 관리하기 쉽다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;--runtime&lt;/code&gt;은 언제 유용할까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;systemctl edit --runtime&lt;/code&gt;는 변경을 &lt;code&gt;/run&lt;/code&gt; 아래에 기록하므로 다음 부팅에서 사라진다. 일시적인 장애 대응, 특정 배포 직후의 임시 제한 완화, 재부팅 전까지만 필요한 테스트성 조정에 맞는다.&lt;/p&gt;
&lt;p&gt;다만 공식 문서는 &lt;code&gt;/etc&lt;/code&gt; 아래 유닛이 이미 우선권을 가지는 상황에서는 그 유닛을 임시 편집할 수 없다고 설명한다. 즉, 영구 override가 이미 있는 유닛을 runtime만으로 덮어쓰는 흐름은 기대한 대로 동작하지 않을 수 있다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;revert&lt;/code&gt;는 무엇을 되돌릴까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;systemctl revert unit.service&lt;/code&gt;는 지정한 유닛의 drop-in 설정과, 벤더 버전을 덮고 있는 사용자 정의 메인 유닛 파일을 제거해 원래 벤더 버전으로 되돌리는 명령이다. 공식 문서는 이 명령이 &lt;code&gt;systemctl edit&lt;/code&gt;, &lt;code&gt;systemctl set-property&lt;/code&gt;, &lt;code&gt;systemctl mask&lt;/code&gt;로 만든 변경을 되돌리는 데 쓸 수 있다고 설명한다.&lt;/p&gt;
&lt;p&gt;운영에서는 설정 정리가 꼬였을 때 수동으로 &lt;code&gt;.d/&lt;/code&gt; 디렉터리를 지우기보다, 먼저 &lt;code&gt;systemctl cat&lt;/code&gt;로 현재 조합을 확인하고 그다음 &lt;code&gt;revert&lt;/code&gt;를 고려하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;운영에서 무난한 선택 기준&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;무난한 선택&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;벤더 유닛의 일부 값만 조정&lt;/td&gt;
      &lt;td&gt;기본 &lt;code&gt;systemctl edit&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;업데이트 반영을 유지하면서 변경 범위를 최소화할 수 있음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;설정을 목적별로 분리 관리&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;--drop-in=name.conf&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;리소스 제한, 환경변수, 하드닝을 파일별로 나누기 쉬움&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;재부팅 전까지만 필요한 임시 조정&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;--runtime&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;다음 부팅에서 자동 정리됨&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;의존성 제거·대규모 구조 변경&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;--full&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;drop-in으로는 제거할 수 없는 항목이 있기 때문&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;현재 적용 조합 점검&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;systemctl cat&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;fragment와 drop-in을 한 번에 볼 수 있음&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;Q. drop-in을 만들면 매번 직접 &lt;code&gt;daemon-reload&lt;/code&gt;를 해야 할까?&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;systemctl edit&lt;/code&gt;로 저장했다면 공식 문서 기준 자동으로 reload가 수행된다. 직접 파일을 수정했을 때는 별도로 &lt;code&gt;daemon-reload&lt;/code&gt;가 필요할 수 있다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;override.conf&lt;/code&gt; 하나만 써야 할까?&lt;/h3&gt;
&lt;p&gt;그럴 필요는 없다. 공식 문서에 따르면 &lt;code&gt;--drop-in=NAME&lt;/code&gt;으로 파일명을 지정할 수 있고, 여러 drop-in 파일은 사전식 순서로 함께 적용된다.&lt;/p&gt;

&lt;h3&gt;Q. drop-in만으로 모든 값을 지우거나 의존성을 제거할 수 있을까?&lt;/h3&gt;
&lt;p&gt;항상 그렇지는 않다. 공식 문서는 dependency 계열은 drop-in에서 빈 목록으로 초기화할 수 없다고 설명한다. 그런 경우는 메인 유닛을 대체하는 쪽을 검토해야 한다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;systemctl edit&lt;/code&gt;의 기본 철학은 벤더 유닛 파일을 직접 건드리지 않고 필요한 변경만 분리하는 것이다. 그래서 대부분의 운영 변경은 drop-in으로 시작하는 편이 맞고, 구조적 재정의가 필요할 때만 &lt;code&gt;--full&lt;/code&gt;로 올라가는 방식이 관리상 안전하다.&lt;/p&gt;
&lt;p&gt;2026년 7월 5일 기준 systemd 261.1 공식 문서만 놓고 보면, 무난한 기본값은 &quot;부분 수정은 drop-in, 임시는 &lt;code&gt;--runtime&lt;/code&gt;, 큰 구조 변경만 &lt;code&gt;--full&lt;/code&gt;, 문제 분석 전에는 &lt;code&gt;systemctl cat&lt;/code&gt;, 원복은 &lt;code&gt;revert&lt;/code&gt;&quot;라고 정리할 수 있다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/latest/systemctl.html&quot; rel=&quot;noopener noreferrer&quot;&gt;systemd 261.1 공식 문서: systemctl&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html&quot; rel=&quot;noopener noreferrer&quot;&gt;systemd 261.1 공식 문서: systemd.unit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/운영체제</category>
      <category>override.conf</category>
      <category>Service</category>
      <category>systemctl</category>
      <category>systemd</category>
      <category>리눅스</category>
      <category>운영체제</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/635</guid>
      <comments>https://jcdgods.tistory.com/635#entry635comment</comments>
      <pubDate>Sun, 5 Jul 2026 12:16:00 +0900</pubDate>
    </item>
    <item>
      <title>Permissions-Policy 헤더 기준 정리</title>
      <link>https://jcdgods.tistory.com/634</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MZ38m/dJMcaaMFpqD/cQHZwyP0hwgdnMi8z9wkN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MZ38m/dJMcaaMFpqD/cQHZwyP0hwgdnMi8z9wkN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MZ38m/dJMcaaMFpqD/cQHZwyP0hwgdnMi8z9wkN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMZ38m%2FdJMcaaMFpqD%2FcQHZwyP0hwgdnMi8z9wkN0%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Permissions-Policy 헤더 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;Permissions-Policy&lt;/code&gt;는 문서와 그 안의 &lt;code&gt;iframe&lt;/code&gt;에서 어떤 브라우저 기능을 허용할지 서버가 응답 헤더로 제어하는 방법이다. 2026년 7월 4일 기준 MDN과 W3C Permissions Policy 초안을 보면 이 헤더는 카메라, 마이크, 전체화면, geolocation 같은 기능을 선택적으로 허용하거나 차단하는 데 쓰인다.&lt;/p&gt;
&lt;p&gt;실무에서 중요한 판단은 세 가지다. 어떤 기능을 기본 차단할지, 어떤 기능을 같은 출처나 특정 서드파티 프레임에만 위임할지, 그리고 브라우저 호환성 때문에 운영 기본값으로 바로 써도 되는지다. 이 글은 공개 문서 기준으로 &lt;code&gt;Permissions-Policy&lt;/code&gt;를 언제 쓰고 어떤 문법을 조심해야 하는지 정리한다.&lt;/p&gt;

&lt;h2&gt;Permissions-Policy는 무엇을 해결할까?&lt;/h2&gt;
&lt;p&gt;W3C 문서는 이 정책의 목적을 브라우저 기능과 API를 선택적으로 활성화하거나 비활성화하는 메커니즘으로 설명한다. 특히 자사 코드뿐 아니라 페이지 안에서 실행되는 서드파티 콘텐츠까지 포함해 예기치 않은 기능 사용을 줄이고 싶을 때 의미가 있다.&lt;/p&gt;
&lt;p&gt;예를 들어 광고, 위젯, 임베드 지도, 사내 도구용 서브프레임이 섞인 페이지에서는 모든 프레임이 카메라나 마이크를 요청할 필요가 없다. 이런 경우 헤더로 기본 범위를 먼저 좁히고, 꼭 필요한 프레임에만 별도로 열어 주는 쪽이 관리가 쉽다.&lt;/p&gt;

&lt;h2&gt;기본 문법은 어떻게 읽으면 될까?&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Permissions-Policy: geolocation=(), camera=(self), fullscreen=*&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;MDN 기준으로 각 지시어는 &lt;code&gt;&amp;lt;directive&amp;gt;=&amp;lt;allowlist&amp;gt;&lt;/code&gt; 형태로 작성하고, 여러 지시어는 쉼표로 구분한다. 허용 목록은 괄호 안에 적는다.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;표현&lt;/th&gt;
      &lt;th&gt;의미&lt;/th&gt;
      &lt;th&gt;해석할 때의 기준&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;()&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;상위 문서와 모든 중첩 문서에서 기능 비활성화&lt;/td&gt;
      &lt;td&gt;가장 보수적인 차단 설정&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;(self)&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;현재 문서와 같은 출처 프레임에만 허용&lt;/td&gt;
      &lt;td&gt;자사 도메인 중심 서비스에 무난한 기본값&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;*&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;모든 중첩 문맥에 허용&lt;/td&gt;
      &lt;td&gt;제어 범위를 거의 주지 않으므로 신중히 사용&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;(self &quot;https://example.com&quot;)&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;현재 출처와 특정 외부 출처만 허용&lt;/td&gt;
      &lt;td&gt;임베드 파트너가 명확할 때 적합&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;MDN은 &lt;code&gt;*&lt;/code&gt;와 &lt;code&gt;()&lt;/code&gt;는 단독으로만 사용하고, &lt;code&gt;self&lt;/code&gt;는 특정 origin과 함께 조합할 수 있다고 설명한다. 반면 &lt;code&gt;src&lt;/code&gt;는 HTTP 헤더가 아니라 &lt;code&gt;iframe&lt;/code&gt;의 &lt;code&gt;allow&lt;/code&gt; 속성에서만 쓰는 값이다.&lt;/p&gt;

&lt;h2&gt;헤더와 iframe allow 속성은 어떻게 역할이 다를까?&lt;/h2&gt;
&lt;p&gt;W3C 예제는 헤더가 전체 문서 계층의 상한선을 정하고, &lt;code&gt;iframe allow&lt;/code&gt; 속성은 그 범위 안에서 특정 프레임에 기능을 위임하는 방식으로 설명한다. 즉 헤더에서 막아 둔 기능을 &lt;code&gt;iframe allow&lt;/code&gt;만으로 무제한 복구할 수는 없다.&lt;/p&gt;
&lt;p&gt;예를 들어 다음과 같이 응답 헤더를 보내면 geolocation은 기본적으로 모두 차단된다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Permissions-Policy: geolocation=()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 상태에서는 특정 &lt;code&gt;iframe&lt;/code&gt;에 &lt;code&gt;allow=&quot;geolocation&quot;&lt;/code&gt;를 넣더라도 상위 정책이 막고 있으면 실제 사용은 허용되지 않는다. 반대로 헤더가 &lt;code&gt;geolocation=(self &quot;https://maps.example&quot;)&lt;/code&gt;처럼 상한을 열어 둔 경우에는, 해당 출처 프레임에서만 &lt;code&gt;allow&lt;/code&gt; 속성으로 실제 위임이 가능하다.&lt;/p&gt;

&lt;h2&gt;어떤 기능을 먼저 제어하는 편이 실용적일까?&lt;/h2&gt;
&lt;p&gt;모든 지시어를 한 번에 다루기보다 사용자 권한 요청이나 개인정보 노출과 직접 연결되는 기능부터 보는 편이 현실적이다. 카메라, 마이크, geolocation, 전체화면, autoplay처럼 사용 여부가 명확한 기능이 우선순위가 높다.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;기능 예시&lt;/th&gt;
      &lt;th&gt;왜 먼저 보나&lt;/th&gt;
      &lt;th&gt;실무 기본값 예시&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;camera&lt;/code&gt;, &lt;code&gt;microphone&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;권한 요청과 직접 연결됨&lt;/td&gt;
      &lt;td&gt;필요한 프레임만 허용&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;geolocation&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;위치 정보 노출 가능성&lt;/td&gt;
      &lt;td&gt;대부분 &lt;code&gt;()&lt;/code&gt; 또는 &lt;code&gt;(self)&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;fullscreen&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;임베드 UX와 연결되지만 남용 가능성도 있음&lt;/td&gt;
      &lt;td&gt;콘텐츠 성격이 분명할 때만 제한적으로 허용&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;autoplay&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;사용자 경험 영향이 큼&lt;/td&gt;
      &lt;td&gt;미디어 프레임에만 선택 허용&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;MDN 문서에는 더 많은 지시어가 있지만, 기능 목록은 브라우저 구현과 함께 변할 수 있다. 운영 문서에 예시를 남길 때는 실제 서비스에서 쓰는 지시어만 좁게 기록하는 편이 유지보수에 유리하다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;브라우저 호환성은 왜 먼저 확인해야 할까?&lt;/h2&gt;
&lt;p&gt;MDN은 2026년 7월 4일 현재 이 헤더를 Baseline 기능으로 표시하지 않고, 일부 널리 쓰이는 브라우저에서 동작하지 않을 수 있다고 안내한다. 또한 experimental technology로 분류하고 있어서 운영 기본 보안 장치로 단독 의존하는 판단은 조심해야 한다.&lt;/p&gt;
&lt;p&gt;따라서 &lt;code&gt;Permissions-Policy&lt;/code&gt;는 있으면 더 좋은 방어선으로 보되, 핵심 보안 요구사항은 여전히 권한 체크, 서버 측 검증, 적절한 &lt;code&gt;sandbox&lt;/code&gt; 설정, 콘텐츠 분리 구조로 함께 보완하는 편이 맞다.&lt;/p&gt;

&lt;h2&gt;Feature Policy와는 어떤 관계일까?&lt;/h2&gt;
&lt;p&gt;W3C 초안은 이 사양이 과거에 &lt;code&gt;Feature Policy&lt;/code&gt;라는 이름으로 불렸다고 명시한다. 오래된 글이나 예제에서 &lt;code&gt;Feature-Policy&lt;/code&gt; 헤더를 보더라도 현재 문서 기준으로는 &lt;code&gt;Permissions-Policy&lt;/code&gt; 이름과 문법을 기준으로 읽는 편이 안전하다.&lt;/p&gt;
&lt;p&gt;다만 레거시 문서를 유지 중인 환경이라면 실제 대상 브라우저가 어떤 문법을 인식하는지 따로 확인해야 한다. 현재 운영 기준을 정하는 문서에는 구식 이름을 기본 예제로 남기지 않는 편이 혼란을 줄인다.&lt;/p&gt;

&lt;h2&gt;적용 전에 무엇을 점검해야 할까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;점검 항목&lt;/th&gt;
      &lt;th&gt;확인 이유&lt;/th&gt;
      &lt;th&gt;실무 기준&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&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&gt;
      &lt;td&gt;임베드 출처 목록&lt;/td&gt;
      &lt;td&gt;어느 프레임에 위임할지 결정해야 함&lt;/td&gt;
      &lt;td&gt;허용 출처를 명시적으로 관리&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;iframe allow&lt;/code&gt; 사용 위치&lt;/td&gt;
      &lt;td&gt;헤더만으로는 세부 위임이 끝나지 않음&lt;/td&gt;
      &lt;td&gt;필요 프레임에만 속성 추가&lt;/td&gt;
    &lt;/tr&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;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;Q. 이 헤더만 넣으면 브라우저 권한 문제가 모두 해결될까?&lt;/h3&gt;
&lt;p&gt;아니다. 이 헤더는 기능 사용 가능 범위를 좁히는 수단이지, 서버 측 인증이나 애플리케이션 권한 모델을 대체하지 않는다.&lt;/p&gt;

&lt;h3&gt;Q. 모든 기능을 &lt;code&gt;*&lt;/code&gt;로 열어 두면 어떤 문제가 있을까?&lt;/h3&gt;
&lt;p&gt;상위 문서가 프레임에 위임할 수 있는 범위를 과도하게 넓히게 된다. 서드파티 임베드가 많은 페이지에서는 제어 장치를 사실상 포기하는 것에 가깝다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;iframe allow&lt;/code&gt;만 잘 쓰면 헤더는 없어도 될까?&lt;/h3&gt;
&lt;p&gt;일부 경우에는 가능하지만, 전체 문서에 공통 상한선을 두려면 헤더가 더 일관된 제어 수단이다. 특히 개발자가 직접 만들지 않은 프레임이 추가될 수 있는 구조라면 헤더가 더 중요하다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Permissions-Policy&lt;/code&gt;는 문서와 &lt;code&gt;iframe&lt;/code&gt;에서 브라우저 기능 사용 범위를 좁히는 응답 헤더다. 실무에서는 모든 지시어를 다루기보다 카메라, 마이크, 위치처럼 민감도가 높은 기능부터 기본 차단 또는 제한 허용 정책을 정하는 편이 현실적이다.&lt;/p&gt;
&lt;p&gt;2026년 7월 4일 기준 공개 문서를 기준으로 보면, 무난한 적용 순서는 기능 목록 선별, 허용할 출처 정의, 필요한 프레임에만 &lt;code&gt;allow&lt;/code&gt; 속성 부여, 그리고 실제 대상 브라우저 호환성 확인이다. 특히 이 헤더는 유용하지만 Baseline 기능은 아니므로, 다른 보안 제어와 함께 사용하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Permissions-Policy&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN: Permissions-Policy header&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://w3c.github.io/webappsec-permissions-policy/&quot; rel=&quot;noopener noreferrer&quot;&gt;W3C Editor's Draft: Permissions Policy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/HTML, Javascript, CSS</category>
      <category>Browser API</category>
      <category>Frontend Security</category>
      <category>HTTP Header</category>
      <category>iframe</category>
      <category>Permissions-Policy</category>
      <category>web security</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/634</guid>
      <comments>https://jcdgods.tistory.com/634#entry634comment</comments>
      <pubDate>Sat, 4 Jul 2026 12:56:50 +0900</pubDate>
    </item>
    <item>
      <title>Git switch 사용법</title>
      <link>https://jcdgods.tistory.com/633</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kzEQy/dJMcaf1rDrQ/d239IG1Z1icDHwSsKplK80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kzEQy/dJMcaf1rDrQ/d239IG1Z1icDHwSsKplK80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kzEQy/dJMcaf1rDrQ/d239IG1Z1icDHwSsKplK80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkzEQy%2FdJMcaf1rDrQ%2Fd239IG1Z1icDHwSsKplK80%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Git switch 사용법&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;git switch&lt;/code&gt;는 브랜치를 전환하거나 새 브랜치를 만들고 곧바로 이동할 때 쓰는 명령이다. 2026년 7월 3일 기준 Git 공식 문서는 이 명령을 브랜치 전환 전용 도구로 설명하며, 파일 복원까지 함께 맡는 &lt;code&gt;git checkout&lt;/code&gt;보다 의도를 더 분명하게 드러낸다.&lt;/p&gt;
&lt;p&gt;실무에서 핵심은 네 가지다. 기존 브랜치로 이동하는지, 새 브랜치를 만드는지, 작업 중인 변경을 유지한 채 옮길지, 임시로 detached HEAD 상태에 들어갈지를 먼저 나눠야 한다. 이 글은 Git 2.55.0 공식 문서를 기준으로 &lt;code&gt;git switch&lt;/code&gt;의 기본 동작과 자주 쓰는 옵션을 정리한다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;git switch&lt;/code&gt;는 언제 쓰는 명령일까?&lt;/h2&gt;
&lt;p&gt;Git 공식 문서의 설명은 단순하다. 지정한 브랜치로 전환하고, 작업 트리와 index를 그 브랜치 상태에 맞춘다. 즉 파일 일부만 꺼내 오는 명령이 아니라 현재 작업할 브랜치를 바꾸는 명령이다.&lt;/p&gt;
&lt;p&gt;최근 Git 사용법에서 브랜치 이동은 &lt;code&gt;switch&lt;/code&gt;, 파일 복원은 &lt;code&gt;restore&lt;/code&gt;로 역할을 나누는 이유도 여기에 있다. 팀 문서나 교육 자료를 새로 쓴다면 &lt;code&gt;checkout&lt;/code&gt; 하나로 여러 동작을 설명하기보다 역할을 분리하는 편이 실수를 줄이기 쉽다.&lt;/p&gt;

&lt;h2&gt;가장 자주 쓰는 명령은 무엇일까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;권장 명령&lt;/th&gt;
      &lt;th&gt;의미&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;기존 브랜치로 이동&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git switch main&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;현재 작업 브랜치를 &lt;code&gt;main&lt;/code&gt;으로 바꾼다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;새 브랜치를 만들고 바로 이동&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git switch -c feature/login&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;브랜치를 생성한 뒤 전환한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;이전 브랜치로 되돌아가기&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git switch -&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;직전에 있던 브랜치나 커밋으로 빠르게 돌아간다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;원격 브랜치를 따라 새 로컬 브랜치 생성&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git switch feature/api&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;조건이 맞으면 추적 브랜치를 자동 생성한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;커밋을 잠시 확인&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git switch --detach HEAD~2&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;브랜치에 붙지 않은 detached HEAD로 이동한다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;문서 기준으로 &lt;code&gt;git switch&lt;/code&gt;는 &lt;code&gt;git checkout&lt;/code&gt;보다 사용 범위가 좁다. 대신 &quot;브랜치를 바꾸는 중인지&quot;가 명확해서 초보자와 팀 문서 모두에서 설명이 쉬워진다.&lt;/p&gt;

&lt;h2&gt;새 브랜치를 만들 때는 &lt;code&gt;-c&lt;/code&gt;와 &lt;code&gt;-C&lt;/code&gt;를 어떻게 나눌까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;-c&lt;/code&gt; 또는 &lt;code&gt;--create&lt;/code&gt;는 새 브랜치를 만들고 곧바로 전환한다. 문서상 이 동작은 &lt;code&gt;git branch &amp;lt;name&amp;gt;&lt;/code&gt; 후 &lt;code&gt;git switch &amp;lt;name&amp;gt;&lt;/code&gt;와 비슷하지만, 전환이 실패하면 중간 상태로 브랜치가 남지 않도록 한 번에 처리된다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-C&lt;/code&gt; 또는 &lt;code&gt;--force-create&lt;/code&gt;는 같은 이름의 브랜치가 이미 있으면 그 브랜치를 지정한 시작점으로 재설정한 뒤 전환한다. 따라서 기존 브랜치 포인터를 바꾸는 효과가 있으므로, 협업 중인 공유 브랜치에는 보수적으로 써야 한다.&lt;/p&gt;

&lt;h2&gt;작업 중인 변경이 있어도 브랜치를 바꿀 수 있을까?&lt;/h2&gt;
&lt;p&gt;기본적으로는 현재 변경이 대상 브랜치 전환 과정에서 손실될 수 있으면 명령이 중단된다. Git 공식 문서는 손실 위험이 없으면 깨끗한 작업 트리가 아니어도 전환이 가능하다고 설명한다.&lt;/p&gt;
&lt;p&gt;문제가 되는 파일이 겹칠 때 선택지는 두 가지다. 변경을 버리고 대상 브랜치 상태로 맞추려면 &lt;code&gt;--discard-changes&lt;/code&gt;를, 변경을 가능한 한 이어 가려면 &lt;code&gt;--merge&lt;/code&gt;를 검토한다. 둘 다 결과가 강하므로 실행 전 &lt;code&gt;git status&lt;/code&gt;로 범위를 확인하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;--merge&lt;/code&gt;와 &lt;code&gt;--discard-changes&lt;/code&gt; 중 무엇이 더 맞을까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;옵션&lt;/th&gt;
      &lt;th&gt;적합한 상황&lt;/th&gt;
      &lt;th&gt;주의점&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;--merge&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;현재 수정분을 새 브랜치 문맥으로 최대한 이어 가고 싶을 때&lt;/td&gt;
      &lt;td&gt;재적용 과정에서 충돌이 나면 stash 항목이 남을 수 있다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;--discard-changes&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;현재 수정분을 버리고 대상 브랜치 상태로 즉시 맞추고 싶을 때&lt;/td&gt;
      &lt;td&gt;작업 트리와 index 변경이 버려질 수 있다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Git 2.55.0 문서 기준으로 &lt;code&gt;--merge&lt;/code&gt;는 겹치는 변경이 있을 때 자동으로 stash한 뒤 전환 후 다시 적용하는 방식이다. 반면 &lt;code&gt;--discard-changes&lt;/code&gt;는 현재 작업 트리와 index를 전환 대상에 맞춰 되돌리는 쪽에 가깝다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;원격 브랜치도 자동으로 잡아 줄까?&lt;/h2&gt;
&lt;p&gt;그럴 수 있다. 공식 문서에 따르면 로컬에 해당 이름의 브랜치가 없더라도, 정확히 하나의 remote에 같은 이름의 추적 브랜치가 있으면 &lt;code&gt;--guess&lt;/code&gt; 기본 동작에 따라 로컬 브랜치를 만들고 추적 설정까지 한 번에 처리한다.&lt;/p&gt;
&lt;p&gt;여러 remote에 같은 이름의 브랜치가 있으면 모호해질 수 있다. 이때는 &lt;code&gt;checkout.defaultRemote&lt;/code&gt; 설정으로 선호 remote를 정하거나, 아예 &lt;code&gt;git switch -c local-name --track origin/feature/api&lt;/code&gt;처럼 명시적으로 쓰는 편이 더 안전하다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;git switch -&lt;/code&gt;는 왜 자주 쓰일까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;-&lt;/code&gt;는 문서상 &lt;code&gt;@{-1}&lt;/code&gt;의 축약형이다. 직전에 전환했던 브랜치나 커밋으로 빠르게 돌아갈 수 있으므로, 두 브랜치를 오가며 비교하거나 긴급 수정 후 원래 작업으로 복귀할 때 특히 편하다.&lt;/p&gt;
&lt;p&gt;예를 들어 기능 브랜치에서 작업하다가 &lt;code&gt;main&lt;/code&gt;으로 잠깐 이동해 로그를 확인한 뒤 다시 돌아와야 한다면 &lt;code&gt;git switch main&lt;/code&gt; 후 &lt;code&gt;git switch -&lt;/code&gt; 조합이 가장 단순하다.&lt;/p&gt;

&lt;h2&gt;detached HEAD는 언제 써야 할까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git switch --detach &amp;lt;commit&amp;gt;&lt;/code&gt;는 브랜치 이름이 아니라 특정 커밋을 잠시 확인하거나 실험할 때 쓴다. 이 상태에서는 새 커밋을 만들어도 어느 브랜치 포인터도 자동으로 따라오지 않으므로, 결과를 보존하려면 나중에 브랜치를 따로 만들어야 한다.&lt;/p&gt;
&lt;p&gt;문서 예시도 비슷하다. 특정 과거 커밋을 검토하다가 작업 가치가 생기면 그 자리에서 &lt;code&gt;git switch -c new-branch&lt;/code&gt;로 이름을 붙여 이어 가면 된다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;--orphan&lt;/code&gt;는 어떤 상황에서 필요할까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;--orphan&lt;/code&gt;는 부모 없는 새 브랜치를 만든다. 첫 커밋이 새로운 루트가 되므로, 기존 저장소 이력과 분리된 별도 공개용 히스토리를 만들고 싶을 때 쓸 수 있다.&lt;/p&gt;
&lt;p&gt;다만 일반적인 기능 개발에서는 드물다. 릴리스 산출물만 별도 브랜치로 공개하거나, 과거 이력을 노출하지 않은 채 현재 트리 비슷한 상태만 새로 시작해야 하는 특별한 경우에 가깝다.&lt;/p&gt;

&lt;h2&gt;이런 상황에서는 어떤 명령이 무난할까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;권장 명령&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;기존 브랜치로 단순 이동&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git switch develop&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;브랜치 전환 의도가 가장 직접적으로 드러난다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;새 작업 브랜치를 만들고 시작&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git switch -c feature/cache&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;생성과 전환을 한 번에 처리한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;원격 브랜치에서 로컬 작업 시작&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git switch bugfix/auth&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;하나의 remote에서만 이름이 보이면 자동 추적이 가능하다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;현재 변경을 유지하며 다른 브랜치 문맥으로 이동&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git switch --merge release/1.2&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;겹치는 수정이 있으면 자동 stash 후 재적용을 시도한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;과거 커밋을 잠깐 확인&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git switch --detach HEAD~3&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;브랜치 포인터를 움직이지 않고 검토할 수 있다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. &lt;code&gt;git switch&lt;/code&gt;만 있으면 &lt;code&gt;git checkout&lt;/code&gt;은 더 이상 필요 없을까?&lt;/h3&gt;
&lt;p&gt;아니다. &lt;code&gt;git switch&lt;/code&gt;는 브랜치 전환 전용이고, &lt;code&gt;git checkout&lt;/code&gt;은 여전히 파일 경로 복원 같은 다른 모드를 포함한다. 최근 워크플로에서는 브랜치 전환은 &lt;code&gt;switch&lt;/code&gt;, 파일 복원은 &lt;code&gt;restore&lt;/code&gt;로 나누는 편이 더 명확하다.&lt;/p&gt;

&lt;h3&gt;Q. 로컬 변경이 있어도 무조건 전환되나?&lt;/h3&gt;
&lt;p&gt;아니다. 현재 변경이 손실될 위험이 있으면 기본 동작은 중단이다. 이때 &lt;code&gt;--merge&lt;/code&gt;나 &lt;code&gt;--discard-changes&lt;/code&gt;를 검토할 수 있지만, 둘 다 결과가 강하므로 현재 변경 범위를 먼저 확인해야 한다.&lt;/p&gt;

&lt;h3&gt;Q. 원격 브랜치 이름만 써도 항상 자동 추적이 되나?&lt;/h3&gt;
&lt;p&gt;아니다. 같은 이름의 remote-tracking branch가 정확히 하나일 때만 기본 추정이 단순하다. 여러 remote에 같은 이름이 있으면 명시적으로 remote를 적는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git switch&lt;/code&gt;의 선택 기준은 단순하다. 브랜치 이동이 목적이면 먼저 &lt;code&gt;switch&lt;/code&gt;를 검토하고, 새 브랜치 생성은 &lt;code&gt;-c&lt;/code&gt;, 임시 커밋 검토는 &lt;code&gt;--detach&lt;/code&gt;, 현재 수정 유지 여부는 &lt;code&gt;--merge&lt;/code&gt;와 &lt;code&gt;--discard-changes&lt;/code&gt;로 나누면 된다.&lt;/p&gt;
&lt;p&gt;한 줄 기준으로 줄이면 이렇다. 브랜치 전환은 &lt;code&gt;git switch&lt;/code&gt;, 파일 복원은 &lt;code&gt;git restore&lt;/code&gt;, 다기능 호환 명령이 꼭 필요할 때만 &lt;code&gt;git checkout&lt;/code&gt;을 쓰는 구성이 실수를 줄이기 쉽다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-switch&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Documentation: git-switch&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-checkout&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Documentation: git-checkout&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-branch&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Documentation: git-branch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/Git, IDE, 툴 관련</category>
      <category>branch</category>
      <category>GIT</category>
      <category>git checkout</category>
      <category>git switch</category>
      <category>version control</category>
      <category>개발도구</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/633</guid>
      <comments>https://jcdgods.tistory.com/633#entry633comment</comments>
      <pubDate>Fri, 3 Jul 2026 11:16:19 +0900</pubDate>
    </item>
    <item>
      <title>PostgreSQL search_path 기준 정리</title>
      <link>https://jcdgods.tistory.com/632</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxEACI/dJMcabSgtkm/ILRZighk4A2JRDp5zNjmk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxEACI/dJMcabSgtkm/ILRZighk4A2JRDp5zNjmk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxEACI/dJMcabSgtkm/ILRZighk4A2JRDp5zNjmk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxEACI%2FdJMcabSgtkm%2FILRZighk4A2JRDp5zNjmk1%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;PostgreSQL search_path 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;search_path&lt;/code&gt;는 스키마를 생략한 이름을 PostgreSQL이 어떤 순서로 해석할지 정하는 설정이다. 2026년 7월 2일 기준 PostgreSQL 18 공식 문서를 보면 기본값은 &lt;code&gt;&quot;$user&quot;, public&lt;/code&gt;이며, 동일한 이름의 테이블·함수·타입이 여러 스키마에 있을 때 먼저 찾은 객체가 사용된다.&lt;/p&gt;
&lt;p&gt;실무에서는 편의성보다 안전성과 예측 가능성이 더 중요하다. 이 글은 &lt;code&gt;search_path&lt;/code&gt;가 새 객체 생성 위치에 어떤 영향을 주는지, &lt;code&gt;pg_catalog&lt;/code&gt;와 임시 스키마가 어떻게 동작하는지, 언제 &lt;code&gt;public&lt;/code&gt;의 &lt;code&gt;CREATE&lt;/code&gt; 권한을 걷어야 하는지만 기준 중심으로 정리한다.&lt;/p&gt;

&lt;h2&gt;search_path는 무엇을 결정할까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;항목&lt;/th&gt;
      &lt;th&gt;영향&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;스키마를 생략한 객체 참조&lt;/td&gt;
      &lt;td&gt;목록에서 먼저 찾은 스키마의 객체가 선택됨&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;스키마를 생략한 객체 생성&lt;/td&gt;
      &lt;td&gt;목록에서 첫 번째로 유효한 스키마에 생성됨&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;동일 이름 충돌&lt;/td&gt;
      &lt;td&gt;경로 앞쪽 스키마가 우선됨&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;권한 없는 스키마나 없는 스키마&lt;/td&gt;
      &lt;td&gt;조용히 무시됨&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;공식 문서는 &lt;code&gt;search_path&lt;/code&gt;를 쉼표로 구분한 스키마 목록으로 설명한다. 존재하지 않는 스키마나 현재 사용자가 &lt;code&gt;USAGE&lt;/code&gt; 권한이 없는 스키마는 무시된다. 따라서 설정 문자열만 보고 실제 탐색 순서를 단정하지 말고, 현재 세션에서는 &lt;code&gt;current_schemas()&lt;/code&gt;로 해석 결과를 확인하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;기본값 &lt;code&gt;&quot;$user&quot;, public&lt;/code&gt;는 어떻게 해석될까?&lt;/h2&gt;
&lt;p&gt;기본값의 첫 항목인 &lt;code&gt;&quot;$user&quot;&lt;/code&gt;는 현재 사용자 이름과 같은 스키마로 치환된다. 다만 그런 스키마가 없거나 &lt;code&gt;USAGE&lt;/code&gt; 권한이 없으면 무시된다. 두 번째 항목인 &lt;code&gt;public&lt;/code&gt;은 새 데이터베이스에 기본으로 존재하는 스키마다.&lt;/p&gt;
&lt;p&gt;그래서 사용자별 스키마를 따로 만들지 않았다면 대부분의 비정규화된 이름은 결국 &lt;code&gt;public&lt;/code&gt;에서 해석된다. 반대로 사용자별 스키마를 만들고 &lt;code&gt;public&lt;/code&gt;의 쓰기 권한을 정리해 두었다면 같은 기본값이라도 훨씬 안전한 구조가 된다.&lt;/p&gt;

&lt;h2&gt;새 객체는 어디에 만들어질까?&lt;/h2&gt;
&lt;p&gt;스키마를 명시하지 않고 &lt;code&gt;CREATE TABLE&lt;/code&gt;, &lt;code&gt;CREATE VIEW&lt;/code&gt;, &lt;code&gt;CREATE FUNCTION&lt;/code&gt; 같은 명령을 실행하면 &lt;code&gt;search_path&lt;/code&gt;에서 첫 번째로 유효한 스키마가 생성 대상이 된다. 그래서 팀 공용 데이터베이스에서 &lt;code&gt;search_path&lt;/code&gt;를 느슨하게 두면 &quot;왜 이 객체가 여기 생겼지?&quot; 같은 문제가 반복되기 쉽다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SHOW search_path;
SET search_path TO app, public;

CREATE TABLE orders (
  id bigint primary key
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 예시에서는 &lt;code&gt;app&lt;/code&gt; 스키마가 존재하고 권한이 있다면 &lt;code&gt;orders&lt;/code&gt;는 &lt;code&gt;app.orders&lt;/code&gt;로 생성된다. 생성 위치를 항상 고정하고 싶다면 DDL에서는 &lt;code&gt;app.orders&lt;/code&gt;처럼 스키마를 명시하는 쪽이 더 예측 가능하다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;pg_catalog&lt;/code&gt;와 임시 스키마는 왜 따로 봐야 할까?&lt;/h2&gt;
&lt;p&gt;공식 문서 기준 &lt;code&gt;pg_catalog&lt;/code&gt;는 &lt;code&gt;search_path&lt;/code&gt;에 쓰지 않아도 항상 탐색된다. 명시하지 않으면 경로에 적은 스키마들보다 먼저 탐색되고, 직접 경로 뒤쪽에 두면 사용자 정의 이름이 내장 이름보다 앞설 수 있다. 이 동작 때문에 함수나 연산자 이름 충돌은 단순한 가독성 문제가 아니라 동작 차이로 이어질 수 있다.&lt;/p&gt;
&lt;p&gt;임시 스키마 &lt;code&gt;pg_temp&lt;/code&gt;도 세션에 존재하면 항상 탐색되지만, 공식 문서는 이것이 relation과 data type 이름에만 적용되고 함수나 연산자 이름에는 적용되지 않는다고 설명한다. 즉, 임시 테이블 이름 충돌과 함수 이름 충돌은 같은 규칙으로 생각하면 안 된다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;보안 관점에서 가장 중요한 기준은 무엇일까?&lt;/h2&gt;
&lt;p&gt;PostgreSQL 공식 문서는 쓰기 가능한 스키마를 &lt;code&gt;search_path&lt;/code&gt;에 넣는 행위를 신뢰 문제로 본다. 어떤 스키마에 대해 다른 사용자가 &lt;code&gt;CREATE&lt;/code&gt; 권한을 가지고 있다면, 그 사용자는 같은 이름의 객체를 만들어 다른 사용자의 쿼리 동작을 바꾸거나 악의적인 SQL 함수를 실행시키는 방향으로 악용할 수 있다.&lt;/p&gt;
&lt;p&gt;이 때문에 기본 경로에 &lt;code&gt;public&lt;/code&gt;이 남아 있어도 괜찮은 환경은 단일 사용자 또는 서로 완전히 신뢰하는 소수 사용자 환경으로 한정된다. 여러 사용자가 함께 쓰는 데이터베이스라면 &lt;code&gt;public&lt;/code&gt;에 대한 &lt;code&gt;CREATE&lt;/code&gt; 권한을 재검토하고, 사용자별 전용 스키마 또는 애플리케이션 전용 스키마 중심으로 경로를 설계하는 편이 맞다.&lt;/p&gt;

&lt;h2&gt;PostgreSQL 15 이후에는 무엇이 달라졌을까?&lt;/h2&gt;
&lt;p&gt;PostgreSQL 18 공식 문서는 PostgreSQL 15 이상에서 기본 설정이 사용자별 private schema 패턴을 지원한다고 설명한다. 반면 14 이하에서 업그레이드된 데이터베이스나 이전 기본값을 유지한 환경은 &lt;code&gt;public&lt;/code&gt; 스키마에 여전히 넓은 &lt;code&gt;CREATE&lt;/code&gt; 권한이 남아 있을 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;REVOKE CREATE ON SCHEMA public FROM PUBLIC;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;공식 문서가 권장하는 안전한 패턴은 먼저 모든 공개 스키마에서 공용 &lt;code&gt;CREATE&lt;/code&gt; 권한을 제거하고, 각 사용자에게 자기 이름의 스키마를 주는 방식이다. 기본 &lt;code&gt;&quot;$user&quot;, public&lt;/code&gt;를 유지하더라도 이 구조라면 기본 탐색 경로가 훨씬 덜 위험해진다.&lt;/p&gt;

&lt;h2&gt;운영에서 무난한 선택 기준&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;무난한 기준&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;애플리케이션 DDL 관리&lt;/td&gt;
      &lt;td&gt;객체 생성 시 스키마를 명시&lt;/td&gt;
      &lt;td&gt;생성 위치가 세션 설정에 흔들리지 않음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;다중 사용자 DB&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;public&lt;/code&gt;의 공용 &lt;code&gt;CREATE&lt;/code&gt; 권한 제거 검토&lt;/td&gt;
      &lt;td&gt;경로 오염과 이름 가로채기 위험을 줄일 수 있음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;사용자별 작업 공간 필요&lt;/td&gt;
      &lt;td&gt;사용자 전용 스키마 + 기본 &lt;code&gt;&quot;$user&quot;, public&lt;/code&gt; 활용&lt;/td&gt;
      &lt;td&gt;기본값과 잘 맞고 충돌을 줄이기 쉬움&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;공용 객체 배포&lt;/td&gt;
      &lt;td&gt;별도 공유 스키마를 두고 필요 시만 경로에 추가&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;public&lt;/code&gt;에 모든 객체를 몰아넣는 것보다 통제가 쉬움&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;Q. &lt;code&gt;search_path&lt;/code&gt;에 없는 스키마의 객체는 못 쓰나?&lt;/h3&gt;
&lt;p&gt;그렇지는 않다. &lt;code&gt;schema.table&lt;/code&gt;, &lt;code&gt;schema.function()&lt;/code&gt;처럼 스키마를 명시하면 된다. &lt;code&gt;search_path&lt;/code&gt;는 스키마를 생략했을 때의 탐색 규칙이다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;search_path&lt;/code&gt; 문자열에 적힌 순서가 항상 실제 순서와 같나?&lt;/h3&gt;
&lt;p&gt;항상 그렇지는 않다. 존재하지 않는 스키마나 권한 없는 스키마는 무시되고, &lt;code&gt;pg_catalog&lt;/code&gt;와 임시 스키마는 별도 규칙으로 탐색된다. 현재 세션 기준 실제 결과는 &lt;code&gt;current_schemas()&lt;/code&gt;로 확인하는 편이 정확하다.&lt;/p&gt;

&lt;h3&gt;Q. 함수 이름 충돌도 임시 스키마가 먼저 잡나?&lt;/h3&gt;
&lt;p&gt;아니다. PostgreSQL 공식 문서는 임시 스키마의 자동 탐색이 relation과 data type 이름에만 적용되고, 함수와 연산자 이름에는 적용되지 않는다고 설명한다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;search_path&lt;/code&gt;는 단순한 편의 설정이 아니라 객체 해석 순서와 기본 생성 스키마를 함께 결정하는 규칙이다. 같은 이름 충돌이 가능한 환경에서는 쿼리 결과뿐 아니라 실행 함수 자체가 달라질 수 있으므로, 다중 사용자 DB에서는 특히 신중하게 관리해야 한다.&lt;/p&gt;
&lt;p&gt;2026년 7월 2일 기준 PostgreSQL 18 공식 문서만 놓고 보면, 무난한 기본 전략은 DDL에서 스키마를 명시하고, 다중 사용자 환경에서는 &lt;code&gt;public&lt;/code&gt;의 공용 &lt;code&gt;CREATE&lt;/code&gt; 권한을 검토하며, 필요하면 사용자별 스키마 패턴을 쓰는 것이다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/runtime-config-client.html&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL 18 공식 문서: Client Connection Defaults&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/ddl-schemas.html&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL 18 공식 문서: Schemas&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/서버, DBMS</category>
      <category>DATABASE</category>
      <category>DBMS</category>
      <category>PostgreSQL</category>
      <category>Schema</category>
      <category>search_path</category>
      <category>sql</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/632</guid>
      <comments>https://jcdgods.tistory.com/632#entry632comment</comments>
      <pubDate>Thu, 2 Jul 2026 10:24:14 +0900</pubDate>
    </item>
    <item>
      <title>Python asyncio timeout 기준 정리</title>
      <link>https://jcdgods.tistory.com/631</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBN7ge/dJMcabEJmNI/Zs8Wv2sjqhhoexGCksxno0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBN7ge/dJMcabEJmNI/Zs8Wv2sjqhhoexGCksxno0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBN7ge/dJMcabEJmNI/Zs8Wv2sjqhhoexGCksxno0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBN7ge%2FdJMcabEJmNI%2FZs8Wv2sjqhhoexGCksxno0%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Python asyncio timeout 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;asyncio&lt;/code&gt;에서 시간을 제한하고 싶을 때는 비슷해 보이는 API가 여럿 있다. 2026년 7월 1일 기준 Python 3.14.6 공식 문서를 보면 &lt;code&gt;asyncio.timeout()&lt;/code&gt;과 &lt;code&gt;timeout_at()&lt;/code&gt;은 비동기 컨텍스트 매니저이고, &lt;code&gt;wait_for()&lt;/code&gt;는 단일 awaitable을 감싸는 함수이며, &lt;code&gt;wait()&lt;/code&gt;는 timeout이 나도 &lt;code&gt;TimeoutError&lt;/code&gt;를 던지지 않는다.&lt;/p&gt;
&lt;p&gt;실무에서는 &quot;하나의 await만 제한할지&quot;, &quot;여러 await가 들어 있는 논리 블록 전체를 제한할지&quot;, &quot;상대 시간인지 절대 deadline인지&quot;를 먼저 나누면 선택이 빨라진다. 이 글은 &lt;code&gt;asyncio.timeout()&lt;/code&gt;, &lt;code&gt;timeout_at()&lt;/code&gt;, &lt;code&gt;wait_for()&lt;/code&gt;의 차이와 무난한 선택 기준만 정리한다.&lt;/p&gt;

&lt;h2&gt;먼저 무엇이 다를까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;API&lt;/th&gt;
      &lt;th&gt;형태&lt;/th&gt;
      &lt;th&gt;무난한 사용 상황&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;asyncio.timeout(seconds)&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;비동기 컨텍스트 매니저&lt;/td&gt;
      &lt;td&gt;여러 &lt;code&gt;await&lt;/code&gt;가 들어 있는 코드 블록 전체에 상대 시간 제한을 걸고 싶을 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;asyncio.timeout_at(when)&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;비동기 컨텍스트 매니저&lt;/td&gt;
      &lt;td&gt;이벤트 루프 시계 기준의 절대 deadline을 공유해야 할 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;asyncio.wait_for(aw, timeout)&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;함수&lt;/td&gt;
      &lt;td&gt;단일 awaitable 하나에 timeout을 적용하고 싶을 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;asyncio.wait(aws, timeout=...)&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;함수&lt;/td&gt;
      &lt;td&gt;timeout 시 예외 대신 완료된 작업과 남은 작업을 나눠 받고 싶을 때&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;공식 문서 기준 &lt;code&gt;asyncio.timeout()&lt;/code&gt;과 &lt;code&gt;timeout_at()&lt;/code&gt;은 3.11에서 추가됐다. &lt;code&gt;wait_for()&lt;/code&gt;는 더 오래된 API지만, timeout이 발생하면 대상 task를 취소하고 실제 취소가 끝날 때까지 기다릴 수 있어서 총 대기 시간이 지정한 timeout보다 길어질 수 있다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;asyncio.timeout()&lt;/code&gt;은 언제 고르면 될까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;asyncio.timeout()&lt;/code&gt;은 &lt;code&gt;async with&lt;/code&gt; 블록 안의 작업 전체를 하나의 시간 제한으로 묶고 싶을 때 가장 읽기 쉽다. 예를 들어 연결 생성, 초기 핸드셰이크, 첫 응답 대기를 한 덩어리로 보고 2초 안에 끝나야 한다면 개별 &lt;code&gt;await&lt;/code&gt;마다 &lt;code&gt;wait_for()&lt;/code&gt;를 겹치기보다 블록 바깥에서 timeout을 거는 편이 의도가 분명하다.&lt;/p&gt;
&lt;p&gt;공식 문서는 이 컨텍스트 매니저가 deadline을 다시 잡을 수 있고, 현재 deadline 확인과 만료 여부 확인도 가능하다고 설명한다. 시작 시점에는 시간을 모른다가 중간에 확정되는 흐름이라면 &lt;code&gt;asyncio.timeout(None)&lt;/code&gt;으로 열고 나중에 &lt;code&gt;reschedule()&lt;/code&gt;하는 패턴도 가능하다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import asyncio

async def fetch_with_budget():
    async with asyncio.timeout(2.0):
        await open_connection()
        await send_request()
        return await read_response()
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;&lt;code&gt;timeout_at()&lt;/code&gt;은 왜 따로 있을까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;timeout_at()&lt;/code&gt;은 상대 시간 대신 절대 deadline을 받는다. 그래서 상위 호출자가 이미 &lt;code&gt;loop.time()&lt;/code&gt; 기준 마감 시각을 계산해 두었고, 하위 함수 여러 개가 그 예산을 공유해야 할 때 더 적합하다.&lt;/p&gt;
&lt;p&gt;예를 들어 요청 하나에 5초 예산이 있고, 그 안에서 DNS 조회, 외부 API 호출, 후처리를 나눠 써야 한다면 각 함수에 &quot;남은 시간&quot;을 다시 계산해서 넘기기보다 같은 deadline을 넘기는 편이 drift를 줄이기 쉽다. 공식 문서도 &lt;code&gt;timeout_at()&lt;/code&gt;을 절대 시각 버전으로 설명한다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;wait_for()&lt;/code&gt;는 무엇이 다를까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;wait_for()&lt;/code&gt;는 awaitable 하나를 감싸는 형태라서 짧고 직접적이다. &quot;이 호출 하나만 500ms 안에 끝나야 한다&quot;처럼 단일 작업 제한에는 여전히 잘 맞는다.&lt;/p&gt;
&lt;p&gt;다만 timeout 시 동작은 생각보다 강하다. 공식 문서에 따르면 timeout이 발생하면 대상 awaitable을 취소하고 &lt;code&gt;TimeoutError&lt;/code&gt;를 올린다. 또 취소가 실제로 완료될 때까지 기다리기 때문에 총 대기 시간이 지정한 timeout보다 늘어날 수 있다. 그래서 timeout을 &quot;단순한 시계 알람&quot;으로 기대하면 오해가 생길 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import asyncio

async def read_once():
    return await asyncio.wait_for(read_message(), timeout=0.5)
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;어떤 기준으로 고르면 될까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;무난한 선택&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;여러 &lt;code&gt;await&lt;/code&gt;를 하나의 예산으로 묶고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;asyncio.timeout()&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;블록 단위 의도가 분명하고 중첩도 안전하게 다룰 수 있음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;절대 deadline을 여러 함수에 공유해야 한다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;asyncio.timeout_at()&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;이벤트 루프 시계 기준 deadline을 그대로 전달할 수 있음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;개별 awaitable 하나만 제한하면 된다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;asyncio.wait_for()&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;표현이 짧고 대상이 명확함&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;예외 없이 done/pending을 나눠 받고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;asyncio.wait()&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;timeout 시 남은 작업을 직접 정리하는 흐름에 맞음&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;예외 처리에서 자주 놓치는 점&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;asyncio.timeout()&lt;/code&gt;과 &lt;code&gt;TaskGroup&lt;/code&gt; 같은 구조화된 동시성 도구는 내부적으로 cancellation을 사용한다. Python 공식 문서는 coroutine이 &lt;code&gt;CancelledError&lt;/code&gt;를 잡고 삼켜 버리면 이런 구성 요소가 오동작할 수 있다고 설명한다. 정리 작업이 필요해 &lt;code&gt;CancelledError&lt;/code&gt;를 잡더라도, 보통은 정리를 마친 뒤 다시 전파하는 편이 맞다.&lt;/p&gt;
&lt;p&gt;또 하나는 &lt;code&gt;asyncio.wait()&lt;/code&gt;와 &lt;code&gt;wait_for()&lt;/code&gt;를 같은 부류로 보면 안 된다는 점이다. 공식 문서 기준 &lt;code&gt;wait()&lt;/code&gt;는 timeout이 나도 &lt;code&gt;TimeoutError&lt;/code&gt;를 던지지 않고, 끝나지 않은 task를 pending 집합으로 돌려준다. 반대로 &lt;code&gt;wait_for()&lt;/code&gt;는 timeout을 예외로 보고 대상 awaitable 취소까지 시도한다.&lt;/p&gt;

&lt;h2&gt;운영에서 무난한 기본값&lt;/h2&gt;
&lt;p&gt;새 코드를 작성한다면 여러 단계의 작업 예산에는 &lt;code&gt;asyncio.timeout()&lt;/code&gt;, 단일 awaitable에는 &lt;code&gt;wait_for()&lt;/code&gt;를 우선 기준으로 두는 편이 읽기 쉽다. 절대 deadline을 이미 계산해 두는 구조라면 &lt;code&gt;timeout_at()&lt;/code&gt;으로 통일하면 하위 함수에서 남은 시간 계산을 반복하지 않아도 된다.&lt;/p&gt;
&lt;p&gt;Python 3.11 미만도 함께 지원해야 한다면 &lt;code&gt;asyncio.timeout()&lt;/code&gt; 계열은 쓸 수 없다. 이 경우 호환성 때문에 &lt;code&gt;wait_for()&lt;/code&gt;를 더 많이 쓰게 되지만, timeout 시 취소 전파와 총 대기 시간 증가 가능성은 코드 리뷰 기준에 분명히 남겨 두는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;Q. &lt;code&gt;asyncio.timeout()&lt;/code&gt; 안에서 timeout이 나면 블록 안의 작업은 취소될까?&lt;/h3&gt;
&lt;p&gt;그렇다. 이 기능은 cancellation을 내부적으로 사용해 overdue coroutine을 멈추는 방식이다. 다만 호출부에서는 컨텍스트 매니저 바깥에서 &lt;code&gt;TimeoutError&lt;/code&gt;로 다루게 된다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;wait_for()&lt;/code&gt;는 timeout이 나도 대상 작업이 계속 돌 수 있을까?&lt;/h3&gt;
&lt;p&gt;기본 동작은 대상 awaitable을 취소하는 쪽이다. 다만 공식 문서는 취소가 실제로 완료될 때까지 기다릴 수 있으므로, 호출 시간이 timeout 값과 정확히 일치한다고 기대하면 안 된다고 설명한다.&lt;/p&gt;

&lt;h3&gt;Q. 여러 task를 동시에 기다리면 항상 &lt;code&gt;gather()&lt;/code&gt;와 timeout을 같이 쓰면 될까?&lt;/h3&gt;
&lt;p&gt;항상 그렇지는 않다. timeout이 블록 전체 예산인지, 개별 작업 제한인지 먼저 나눠야 한다. 그룹 전체 예산이면 &lt;code&gt;asyncio.timeout()&lt;/code&gt;으로 감싸는 쪽이 더 읽기 쉽고, 일부 완료된 작업을 따로 회수해야 하면 &lt;code&gt;asyncio.wait()&lt;/code&gt;가 더 맞을 수 있다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;asyncio.timeout()&lt;/code&gt;은 블록 단위 시간 예산, &lt;code&gt;timeout_at()&lt;/code&gt;은 절대 deadline 공유, &lt;code&gt;wait_for()&lt;/code&gt;는 단일 awaitable 제한에 가장 잘 맞는다. 세 API 모두 &quot;시간이 지나면 그냥 실패를 알린다&quot; 수준이 아니라 cancellation과 예외 전파 방식이 다르기 때문에, timeout을 어디에 걸고 싶은지부터 분리해서 고르는 편이 안전하다.&lt;/p&gt;
&lt;p&gt;2026년 7월 1일 기준 Python 3.14.6 공식 문서만 놓고 보면, 새 코드의 기본 출발점은 대체로 &lt;code&gt;asyncio.timeout()&lt;/code&gt;이다. 다만 단일 호출 제한이나 3.10 이하 호환성이 중요하면 &lt;code&gt;wait_for()&lt;/code&gt;가 더 현실적인 선택이 될 수 있다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.python.org/3/library/asyncio-task.html&quot; rel=&quot;noopener noreferrer&quot;&gt;Python 3.14.6 공식 문서: asyncio tasks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/C, C++, Java, Python</category>
      <category>asyncio</category>
      <category>python</category>
      <category>Timeout</category>
      <category>wait_for</category>
      <category>비동기</category>
      <category>표준라이브러리</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/631</guid>
      <comments>https://jcdgods.tistory.com/631#entry631comment</comments>
      <pubDate>Wed, 1 Jul 2026 11:42:47 +0900</pubDate>
    </item>
    <item>
      <title>Git stash 사용법</title>
      <link>https://jcdgods.tistory.com/630</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ydyAR/dJMcabkuPbz/a075fQDxJTMBrnIOvWmaKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ydyAR/dJMcabkuPbz/a075fQDxJTMBrnIOvWmaKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ydyAR/dJMcabkuPbz/a075fQDxJTMBrnIOvWmaKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FydyAR%2FdJMcabkuPbz%2Fa075fQDxJTMBrnIOvWmaKK%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Git stash 사용법&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;git stash&lt;/code&gt;는 작업 트리와 index의 현재 상태를 잠시 치워 두고 &lt;code&gt;HEAD&lt;/code&gt; 기준의 깨끗한 작업 디렉터리로 돌아가고 싶을 때 쓰는 명령이다. 2026년 6월 30일 기준 Git 공식 &lt;code&gt;git-stash&lt;/code&gt; 문서를 보면 기본 호출은 &lt;code&gt;git stash push&lt;/code&gt;와 같고, 저장된 항목은 &lt;code&gt;refs/stash&lt;/code&gt;와 그 reflog에서 관리된다.&lt;/p&gt;
&lt;p&gt;실무에서 헷갈리는 지점은 네 가지다. staged 변경만 넣을지, untracked 파일까지 같이 넣을지, 복원할 때 목록에서 제거할지, 충돌이 예상되면 새 브랜치로 푸는 편이 나을지다. 이 글은 최신 Git 문서 기준으로 &lt;code&gt;push&lt;/code&gt;, &lt;code&gt;apply&lt;/code&gt;, &lt;code&gt;pop&lt;/code&gt;, &lt;code&gt;branch&lt;/code&gt;와 주요 옵션 선택 기준을 짧게 정리한다.&lt;/p&gt;

&lt;h2&gt;Git stash는 정확히 무엇을 저장할까?&lt;/h2&gt;
&lt;p&gt;공식 문서 설명은 단순하다. &lt;code&gt;git stash push&lt;/code&gt;는 현재 작업 디렉터리와 index의 로컬 수정 상태를 새 stash entry로 저장한 뒤, 작업 트리와 index를 &lt;code&gt;HEAD&lt;/code&gt;에 맞춰 되돌린다. 그래서 임시 저장이 목적이어도 결과적으로는 작업 디렉터리를 비우는 명령으로 이해하는 편이 맞다.&lt;/p&gt;
&lt;p&gt;저장된 항목은 기본적으로 &lt;code&gt;stash@{0}&lt;/code&gt;, &lt;code&gt;stash@{1}&lt;/code&gt; 같은 이름으로 쌓인다. 가장 최근 stash는 &lt;code&gt;refs/stash&lt;/code&gt;에 놓이고, 이전 항목은 reflog 문법으로 참조할 수 있으므로 &quot;방금 넣은 것&quot;인지 &quot;어제 넣은 것&quot;인지 구분해서 적용할 수 있다.&lt;/p&gt;

&lt;h2&gt;어떤 명령을 먼저 외우면 될까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;명령&lt;/th&gt;
      &lt;th&gt;언제 쓰나&lt;/th&gt;
      &lt;th&gt;핵심 동작&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;git stash push&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;현재 변경을 잠시 치울 때&lt;/td&gt;
      &lt;td&gt;stash를 만들고 작업 트리와 index를 &lt;code&gt;HEAD&lt;/code&gt;에 맞춤&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;git stash apply&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;복원은 하되 목록은 남기고 싶을 때&lt;/td&gt;
      &lt;td&gt;stash를 적용하지만 삭제하지 않음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;git stash pop&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;복원 후 바로 소비하고 싶을 때&lt;/td&gt;
      &lt;td&gt;적용이 성공하면 목록에서 제거함&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;git stash branch &amp;lt;name&amp;gt;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;현재 브랜치와 충돌이 걱정될 때&lt;/td&gt;
      &lt;td&gt;stash가 만들어진 시점의 commit에서 새 브랜치를 만들고 적용함&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;git stash&lt;/code&gt;만 단독으로 치면 문서상 &lt;code&gt;git stash push&lt;/code&gt;와 동일하다. 반면 &lt;code&gt;git stash save&lt;/code&gt;는 deprecated로 표시되어 있고 pathspec을 받을 수 없으므로, 지금 기준으로는 새 문서나 팀 가이드에 &lt;code&gt;save&lt;/code&gt;를 기준 명령으로 적는 이유가 거의 없다.&lt;/p&gt;

&lt;h2&gt;staged 변경만 stash하고 싶다면 어떻게 해야 할까?&lt;/h2&gt;
&lt;p&gt;이때는 &lt;code&gt;git stash push --staged&lt;/code&gt;를 먼저 보면 된다. 공식 문서는 &lt;code&gt;--staged&lt;/code&gt;를 &quot;현재 staged 된 변경만 stash한다&quot;라고 설명한다. 즉 index에 올린 파일만 잠시 빼 두고, 아직 add 하지 않은 작업 트리 수정은 그대로 남길 수 있다.&lt;/p&gt;
&lt;p&gt;반대로 일부 hunk만 고르고 싶다면 &lt;code&gt;--patch&lt;/code&gt;가 맞다. 문서에 따르면 &lt;code&gt;--patch&lt;/code&gt;는 interactive hunk 선택 모드이며, 기본적으로 &lt;code&gt;--keep-index&lt;/code&gt;를 암시한다. 그래서 &quot;테스트가 통과한 staged 묶음은 남기고, 아직 정리 중인 일부 수정만 치우기&quot; 같은 상황에 더 잘 맞는다.&lt;/p&gt;

&lt;h2&gt;untracked 파일과 ignored 파일은 기본으로 들어갈까?&lt;/h2&gt;
&lt;p&gt;기본값은 아니다. 문서 기준으로 &lt;code&gt;--include-untracked&lt;/code&gt; 또는 &lt;code&gt;-u&lt;/code&gt;를 써야 untracked 파일까지 stash하고 정리할 수 있고, &lt;code&gt;--all&lt;/code&gt; 또는 &lt;code&gt;-a&lt;/code&gt;를 써야 ignored 파일까지 포함된다. 이 두 옵션은 내부적으로 정리 단계에서 &lt;code&gt;git clean&lt;/code&gt; 동작을 동반한다.&lt;/p&gt;
&lt;p&gt;선택 기준은 단순하다. 새로 만든 임시 파일까지 같이 치워야 브랜치 전환이나 긴급 hotfix가 가능한 상황이면 &lt;code&gt;-u&lt;/code&gt;를, 빌드 산출물이나 캐시 파일까지 포함해 완전히 비우고 싶다면 &lt;code&gt;-a&lt;/code&gt;를 본다. 다만 ignored 파일은 의도적으로 보존하는 경우가 많아서, 평소 기본값으로 &lt;code&gt;-a&lt;/code&gt;를 습관처럼 쓰는 편은 조심하는 것이 낫다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;apply&lt;/code&gt;와 &lt;code&gt;pop&lt;/code&gt;는 무엇이 다를까?&lt;/h2&gt;
&lt;p&gt;문서 정의는 명확하다. &lt;code&gt;pop&lt;/code&gt;은 stash를 적용하고, 적용이 성공하면 stash 목록에서 제거한다. &lt;code&gt;apply&lt;/code&gt;는 같은 복원 동작을 하지만 목록에서 제거하지 않는다.&lt;/p&gt;
&lt;p&gt;중요한 차이는 실패 처리다. 공식 문서는 &lt;code&gt;pop&lt;/code&gt; 적용 중 충돌이 나면 stash가 자동으로 제거되지 않는다고 설명한다. 그래서 현재 브랜치 상태와 충돌 가능성이 있거나, 먼저 diff를 확인하고 안전하게 재시도하고 싶다면 &lt;code&gt;apply&lt;/code&gt;가 더 보수적인 선택이다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;충돌이 걱정되면 왜 &lt;code&gt;git stash branch&lt;/code&gt;를 볼까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git stash branch &amp;lt;branchname&amp;gt;&lt;/code&gt;는 stash가 처음 만들어졌던 시점의 commit에서 새 브랜치를 만들고, 그 위에 stash를 적용한다. 문서도 현재 브랜치가 많이 달라져서 &lt;code&gt;apply&lt;/code&gt;가 충돌을 일으키기 쉬운 경우 이 명령이 유용하다고 설명한다.&lt;/p&gt;
&lt;p&gt;즉 원래 작업하던 기반 commit과 현재 브랜치가 많이 벌어졌다면, 지금 브랜치 위에 억지로 올리기보다 stash 생성 시점의 맥락을 복구하는 편이 안전하다. 실무에서는 오래 묵은 stash일수록 &lt;code&gt;apply&lt;/code&gt;보다 &lt;code&gt;branch&lt;/code&gt;가 덜 위험한 경우가 많다.&lt;/p&gt;

&lt;h2&gt;stash 내용을 확인할 때는 무엇을 보면 될까?&lt;/h2&gt;
&lt;p&gt;목록은 &lt;code&gt;git stash list&lt;/code&gt;, 상세 차이는 &lt;code&gt;git stash show&lt;/code&gt;로 본다. 문서에 따르면 &lt;code&gt;show&lt;/code&gt;의 기본 출력은 diffstat이며, &lt;code&gt;-p&lt;/code&gt;를 주면 patch 형식으로 볼 수 있다. 또 &lt;code&gt;stash.showStat&lt;/code&gt;, &lt;code&gt;stash.showPatch&lt;/code&gt;, &lt;code&gt;stash.showIncludeUntracked&lt;/code&gt; 설정으로 기본 표시 방식을 바꿀 수 있다.&lt;/p&gt;
&lt;p&gt;untracked 파일까지 확인하고 싶다면 &lt;code&gt;git stash show --include-untracked&lt;/code&gt;를 쓰면 된다. &quot;무엇을 stash했는지 기억이 흐릿한 상태&quot;에서 바로 &lt;code&gt;pop&lt;/code&gt;부터 하는 것보다, 먼저 &lt;code&gt;list&lt;/code&gt;와 &lt;code&gt;show -p&lt;/code&gt;로 범위를 확인하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;최신 Git 문서에서 같이 볼 만한 기능은 무엇일까?&lt;/h2&gt;
&lt;p&gt;2026년 6월 29일 갱신된 최신 문서에는 &lt;code&gt;git stash export&lt;/code&gt;와 &lt;code&gt;git stash import&lt;/code&gt;도 정식 하위 명령으로 나온다. 설명상 &lt;code&gt;export&lt;/code&gt;는 지정한 stash들을 일반 fetch/push 메커니즘으로 옮길 수 있는 commit 체인으로 내보내고, &lt;code&gt;import&lt;/code&gt;는 그 commit에서 stash 목록으로 다시 들여온다.&lt;/p&gt;
&lt;p&gt;이 기능은 대부분의 일상 작업에서 필수는 아니다. 다만 stash를 단순 로컬 임시 보관함이 아니라, 옮기거나 백업해야 하는 특별한 상태로 다뤄야 할 때는 문서상 공식 경로가 있다는 점을 알아 두면 좋다.&lt;/p&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;Q. &lt;code&gt;git stash&lt;/code&gt;와 &lt;code&gt;git stash push&lt;/code&gt;는 다른가?&lt;/h3&gt;
&lt;p&gt;현재 공식 문서 기준으로 인자 없이 호출한 &lt;code&gt;git stash&lt;/code&gt;는 &lt;code&gt;git stash push&lt;/code&gt;와 같다. 다만 pathspec을 쓸 때는 오타로 원치 않는 stash를 만들지 않도록 &lt;code&gt;git stash push -- path/to/file&lt;/code&gt;처럼 subcommand를 명시하는 편이 더 분명하다.&lt;/p&gt;

&lt;h3&gt;Q. stash를 적용하면서 staged 상태도 같이 복원할 수 있나?&lt;/h3&gt;
&lt;p&gt;가능하다. &lt;code&gt;apply&lt;/code&gt;와 &lt;code&gt;pop&lt;/code&gt;에는 &lt;code&gt;--index&lt;/code&gt; 옵션이 있고, 문서는 작업 트리 변경뿐 아니라 index 상태까지 되살리려 시도한다고 설명한다. 다만 충돌이 있으면 이 복원은 실패할 수 있다.&lt;/p&gt;

&lt;h3&gt;Q. 오래된 stash를 현재 브랜치에 바로 &lt;code&gt;pop&lt;/code&gt;해도 될까?&lt;/h3&gt;
&lt;p&gt;그렇게 단정하기는 어렵다. 공식 문서도 현재 브랜치가 많이 달라진 경우 &lt;code&gt;stash branch&lt;/code&gt;를 별도 대안으로 제시한다. 기반 commit이 많이 달라졌다면 새 브랜치에 먼저 복원해 차이를 보는 편이 더 안전하다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git stash&lt;/code&gt;의 판단 기준은 &quot;무엇을 치울지&quot;와 &quot;어디에 다시 올릴지&quot; 두 가지로 나눌 수 있다. staged만 숨길 때는 &lt;code&gt;--staged&lt;/code&gt;, 일부 hunk만 치울 때는 &lt;code&gt;--patch&lt;/code&gt;, untracked까지 비울 때는 &lt;code&gt;-u&lt;/code&gt;, ignored까지 포함할 때는 &lt;code&gt;-a&lt;/code&gt;를 보면 된다.&lt;/p&gt;
&lt;p&gt;복원 단계에서는 재사용 가능성을 남기려면 &lt;code&gt;apply&lt;/code&gt;, 한 번 쓰고 끝내려면 &lt;code&gt;pop&lt;/code&gt;, 충돌 위험이 크면 &lt;code&gt;branch&lt;/code&gt;가 무난하다. 2026년 6월 30일 기준 Git 공식 문서만 놓고 봐도 stash는 단순 임시 서랍이 아니라, 작업 단위를 안전하게 비우고 다시 붙이는 절차로 이해하는 편이 정확하다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-stash&quot; rel=&quot;noopener noreferrer&quot;&gt;Git Documentation: git-stash&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/Git, IDE, 툴 관련</category>
      <category>Developer Tools</category>
      <category>GIT</category>
      <category>git branch</category>
      <category>git stash</category>
      <category>Git Tips</category>
      <category>git workflow</category>
      <category>version control</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/630</guid>
      <comments>https://jcdgods.tistory.com/630#entry630comment</comments>
      <pubDate>Tue, 30 Jun 2026 10:11:31 +0900</pubDate>
    </item>
    <item>
      <title>PostgreSQL idle_in_transaction_session_timeout 기준 정리</title>
      <link>https://jcdgods.tistory.com/629</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yIoEw/dJMcab5NORO/tZ585winafjiPTfzAUKUek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yIoEw/dJMcab5NORO/tZ585winafjiPTfzAUKUek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yIoEw/dJMcab5NORO/tZ585winafjiPTfzAUKUek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyIoEw%2FdJMcab5NORO%2FtZ585winafjiPTfzAUKUek%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;PostgreSQL idle_in_transaction_session_timeout 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt;은 트랜잭션을 연 채로 클라이언트 입력을 기다리는 세션을 일정 시간 뒤 종료하는 설정이다. 2026년 6월 29일 기준 PostgreSQL 공식 문서를 보면 기본값은 &lt;code&gt;0&lt;/code&gt;이며, 단위를 적지 않으면 밀리초로 해석된다. 이 값은 단순히 오래 걸리는 쿼리를 끊는 용도가 아니라, 열린 트랜잭션이 락을 오래 잡거나 vacuum 진행을 방해해 테이블 bloat를 키우는 상황을 줄이는 데 목적이 있다.&lt;/p&gt;
&lt;p&gt;실무에서 자주 헷갈리는 지점은 네 가지다. 실행 시간이 긴 쿼리를 막는 설정인지, 락 대기만 제한하는 설정인지, 트랜잭션 전체 시간을 제한하는 설정인지, 아니면 트랜잭션이 없는 유휴 세션에도 적용되는지다. 이 글은 &lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt;의 의미와 &lt;code&gt;statement_timeout&lt;/code&gt;, &lt;code&gt;lock_timeout&lt;/code&gt;, &lt;code&gt;transaction_timeout&lt;/code&gt;, &lt;code&gt;idle_session_timeout&lt;/code&gt;과의 차이만 정리한다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt;은 언제 동작할까?&lt;/h2&gt;
&lt;p&gt;이 설정은 세션이 열린 트랜잭션 안에서 아무 쿼리도 보내지 않은 채 대기 상태일 때만 동작한다. 즉, &lt;code&gt;BEGIN&lt;/code&gt; 이후 애플리케이션이 다음 명령을 보내지 못하고 멈췄거나, 사용자가 트랜잭션을 열어 둔 채 세션을 방치한 상황이 대상이다.&lt;/p&gt;
&lt;p&gt;반대로 현재 쿼리가 계속 실행 중이라면 이 설정이 아니라 &lt;code&gt;statement_timeout&lt;/code&gt;이 관련 있고, 락을 기다리는 중이라면 &lt;code&gt;lock_timeout&lt;/code&gt; 또는 &lt;code&gt;statement_timeout&lt;/code&gt;이 먼저 의미를 가진다. 트랜잭션이 아예 열려 있지 않은 일반 idle 연결에는 이 설정이 적용되지 않는다.&lt;/p&gt;

&lt;h2&gt;왜 운영에서 신경 써야 할까?&lt;/h2&gt;
&lt;p&gt;PostgreSQL 공식 문서는 idle 상태의 열린 트랜잭션이 오랫동안 락을 보유할 수 있다고 설명한다. 또한 눈에 띄는 락이 없더라도, 아직 해당 트랜잭션에서 볼 수 있는 죽은 튜플을 vacuum이 정리하지 못해 테이블 bloat에 기여할 수 있다고 명시한다.&lt;/p&gt;
&lt;p&gt;즉 문제의 핵심은 &quot;아무 일도 안 하는 연결&quot;처럼 보여도 데이터베이스 내부에서는 정리 작업과 동시성 제어를 오래 막을 수 있다는 점이다. 특히 커넥션 풀 뒤에 있는 애플리케이션에서 예외 처리 누락으로 트랜잭션이 열린 채 반환되면, 장애 징후가 천천히 쌓이는 형태로 나타날 수 있다.&lt;/p&gt;

&lt;h2&gt;비슷한 timeout과 무엇이 다를까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;설정&lt;/th&gt;
      &lt;th&gt;대상&lt;/th&gt;
      &lt;th&gt;끊는 시점&lt;/th&gt;
      &lt;th&gt;주된 목적&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;statement_timeout&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;개별 SQL 문 실행&lt;/td&gt;
      &lt;td&gt;문 실행 시간이 기준을 넘을 때&lt;/td&gt;
      &lt;td&gt;너무 오래 실행되는 쿼리 제한&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;lock_timeout&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;락 획득 대기&lt;/td&gt;
      &lt;td&gt;락 대기 시간이 기준을 넘을 때&lt;/td&gt;
      &lt;td&gt;블로킹 대기 제한&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;transaction_timeout&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;트랜잭션 전체 수명&lt;/td&gt;
      &lt;td&gt;트랜잭션 지속 시간이 기준을 넘을 때&lt;/td&gt;
      &lt;td&gt;너무 긴 트랜잭션 자체 제한&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;열린 트랜잭션 안의 idle 세션&lt;/td&gt;
      &lt;td&gt;트랜잭션 안에서 유휴 시간이 기준을 넘을 때&lt;/td&gt;
      &lt;td&gt;idle 트랜잭션이 락과 vacuum을 방해하는 상황 제한&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;idle_session_timeout&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;트랜잭션이 없는 idle 세션&lt;/td&gt;
      &lt;td&gt;일반 idle 시간이 기준을 넘을 때&lt;/td&gt;
      &lt;td&gt;불필요하게 남은 연결 정리&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;운영에서 가장 중요한 구분은 &lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt;이 &quot;일하지 않는 트랜잭션&quot;을 겨냥한다는 점이다. 오래 실행되는 분석 쿼리나 배치 쿼리를 막고 싶다면 이 값만으로는 부족하고, 그 경우에는 &lt;code&gt;statement_timeout&lt;/code&gt;이나 현재 문서 기준의 &lt;code&gt;transaction_timeout&lt;/code&gt;을 같이 검토해야 한다.&lt;/p&gt;

&lt;h2&gt;어떤 값부터 시작하는 편이 무난할까?&lt;/h2&gt;
&lt;p&gt;공식 문서는 기본값과 동작만 정의하고, 특정 숫자를 권장하지는 않는다. 따라서 안전한 출발점은 애플리케이션이 정상적으로 열어 둘 수 있는 트랜잭션 유휴 시간을 먼저 파악한 뒤 그보다 조금 긴 값으로 시작하는 것이다.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;출발점&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&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&gt;
      &lt;td&gt;관리 콘솔, 수동 SQL 작업&lt;/td&gt;
      &lt;td&gt;조금 더 긴 값 또는 별도 역할 분리&lt;/td&gt;
      &lt;td&gt;사람이 직접 판단하며 중간에 멈출 가능성이 있음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;배치, ETL, 마이그레이션&lt;/td&gt;
      &lt;td&gt;전역 기본값보다 역할별 예외 검토&lt;/td&gt;
      &lt;td&gt;긴 트랜잭션과 중간 대기가 설계상 필요할 수 있음&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;무난한 운영 방식은 전체 서버에 매우 공격적인 값을 바로 거는 대신, 먼저 애플리케이션 역할이나 사용자별로 보수적으로 적용해 보는 것이다. 정상 플로우에서 timeout이 자주 나면 값이 너무 낮거나, 애플리케이션이 트랜잭션 경계를 불필요하게 길게 잡고 있을 가능성을 같이 봐야 한다.&lt;/p&gt;

&lt;h2&gt;어디에 적용해야 할까?&lt;/h2&gt;
&lt;p&gt;이 설정은 세션 단위로도 바꿀 수 있고, 역할이나 데이터베이스 단위 기본값으로도 운영할 수 있다. 실무에서는 공용 서버 전체에 동일한 값을 강제하기보다, 애플리케이션용 역할과 수동 운영용 역할을 나눠 서로 다른 기준을 두는 방식이 더 다루기 쉽다.&lt;/p&gt;
&lt;p&gt;예를 들어 애플리케이션 계정에는 보수적인 값을 두고, 운영자가 접속하는 계정은 더 긴 값이나 별도 정책을 쓰면 예기치 않은 세션 종료를 줄이기 쉽다. 반대로 psql로 수동 작업하는 계정까지 짧은 값을 공통 적용하면, 긴 검토 도중 세션이 끊기는 불편이 생길 수 있다.&lt;/p&gt;

&lt;h2&gt;이 설정만 넣으면 긴 트랜잭션 문제가 끝날까?&lt;/h2&gt;
&lt;p&gt;그렇지는 않다. 이 값은 &quot;idle인 상태&quot;만 겨냥한다. 긴 쿼리를 계속 실행하는 세션, 커밋하지 않은 채 활발히 작업하는 긴 트랜잭션, 락 경합으로 반복 재시도하는 애플리케이션 문제는 다른 timeout이나 애플리케이션 수정이 필요할 수 있다.&lt;/p&gt;
&lt;p&gt;또한 현재 PostgreSQL 18 공식 문서 기준으로 &lt;code&gt;transaction_timeout&lt;/code&gt;이 더 짧거나 같으면 더 긴 &lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt;이나 &lt;code&gt;statement_timeout&lt;/code&gt;은 무시된다. 여러 timeout을 함께 쓸 때는 어느 값이 먼저 발동할지 순서를 의도적으로 잡아야 한다.&lt;/p&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;Q. 커넥션 풀을 쓰면 이 설정이 필요 없을까?&lt;/h3&gt;
&lt;p&gt;그렇게 볼 수는 없다. 오히려 풀 환경에서는 애플리케이션 버그로 열린 트랜잭션이 풀에 남아 재사용되는 문제가 더 위험할 수 있다. 이 설정은 그런 세션이 오래 남는 시간을 제한하는 안전장치 역할을 할 수 있다.&lt;/p&gt;

&lt;h3&gt;Q. 오래 실행되는 SELECT도 이 값으로 끊을 수 있을까?&lt;/h3&gt;
&lt;p&gt;아니다. 쿼리가 실제로 실행 중이면 idle 상태가 아니므로 &lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt; 대상이 아니다. 오래 걸리는 문 실행은 &lt;code&gt;statement_timeout&lt;/code&gt;을 봐야 한다.&lt;/p&gt;

&lt;h3&gt;Q. 락 대기만 오래 걸리는 경우에도 이 값이 먼저 동작할까?&lt;/h3&gt;
&lt;p&gt;일반적으로는 그렇지 않다. 락 대기는 idle이 아니라 문 실행 과정의 일부이므로 &lt;code&gt;lock_timeout&lt;/code&gt;이나 &lt;code&gt;statement_timeout&lt;/code&gt;이 더 직접적인 제어 수단이다.&lt;/p&gt;

&lt;h3&gt;Q. 트랜잭션이 없는 idle 연결도 같이 정리하고 싶다면?&lt;/h3&gt;
&lt;p&gt;그 경우에는 &lt;code&gt;idle_session_timeout&lt;/code&gt;을 별도로 검토해야 한다. 두 설정은 이름이 비슷하지만 적용 대상이 다르다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt;은 느린 쿼리를 잡는 설정이 아니라, 열린 트랜잭션 안에서 멈춰 버린 세션이 락과 vacuum을 오래 방해하지 못하게 하는 장치다. 따라서 웹 애플리케이션이나 API 서버처럼 트랜잭션을 짧게 끝내야 하는 워크로드에서는 기본 검토 대상이 되지만, 수동 작업이나 장시간 배치가 많은 계정에는 같은 값을 그대로 적용하기 어렵다.&lt;/p&gt;
&lt;p&gt;핵심 기준은 세 가지다. 정상 플로우에서 트랜잭션이 idle로 머무를 수 있는 시간이 얼마나 되는지, 긴 트랜잭션이 필요한 역할이 분리돼 있는지, 그리고 &lt;code&gt;statement_timeout&lt;/code&gt;·&lt;code&gt;lock_timeout&lt;/code&gt;·&lt;code&gt;transaction_timeout&lt;/code&gt;과 어떤 순서로 맞물리게 할지다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/runtime-config-client.html&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL 공식 문서: Client Connection Defaults&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/sql-set.html&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL 공식 문서: SET&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/서버, DBMS</category>
      <category>DBMS</category>
      <category>idle_in_transaction_session_timeout</category>
      <category>idle_session_timeout</category>
      <category>lock_timeout</category>
      <category>PostgreSQL</category>
      <category>statement_timeout</category>
      <category>transaction_timeout</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/629</guid>
      <comments>https://jcdgods.tistory.com/629#entry629comment</comments>
      <pubDate>Mon, 29 Jun 2026 11:47:51 +0900</pubDate>
    </item>
    <item>
      <title>Clear-Site-Data 헤더 기준 정리</title>
      <link>https://jcdgods.tistory.com/628</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKDqcX/dJMb99NB64b/V8JN4Ejqm5c5Qm7p664pMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKDqcX/dJMb99NB64b/V8JN4Ejqm5c5Qm7p664pMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKDqcX/dJMb99NB64b/V8JN4Ejqm5c5Qm7p664pMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKDqcX%2FdJMb99NB64b%2FV8JN4Ejqm5c5Qm7p664pMk%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Clear-Site-Data 헤더 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;Clear-Site-Data&lt;/code&gt;는 로그아웃, 계정 전환, 민감 정보 초기화처럼 브라우저에 남은 데이터를 정리해야 할 때 검토하는 HTTP 응답 헤더다. 2026년 6월 28일 기준 MDN과 W3C Clear Site Data 문서를 보면 이 헤더는 HTTPS 응답에서 캐시, 쿠키, DOM 스토리지, 실행 중인 문맥 재로딩을 브라우저에 지시할 수 있다.&lt;/p&gt;
&lt;p&gt;실무에서 중요한 판단은 세 가지다. 정말 전체 정리가 필요한 상황인지, 어떤 데이터 타입만 지울지, 그리고 로그아웃 응답에서 새 쿠키를 다시 심는 흐름과 충돌하지 않는지다. 이 글은 공개 문서 기준으로 &lt;code&gt;Clear-Site-Data&lt;/code&gt;를 언제 쓰고 어디서 조심해야 하는지 짧게 정리한다.&lt;/p&gt;

&lt;h2&gt;Clear-Site-Data는 언제 필요한가?&lt;/h2&gt;
&lt;p&gt;이 헤더는 브라우저가 이전 세션의 흔적을 강하게 비워야 하는 상황에서 의미가 있다. 대표적으로 로그아웃 직후 인증 쿠키와 저장소를 함께 지우는 경우, 공유 PC에서 민감 데이터를 남기지 않으려는 경우, 계정 전환 뒤 이전 사용자 캐시와 저장소를 섞지 않으려는 경우가 있다.&lt;/p&gt;
&lt;p&gt;반대로 단순히 응답 캐시만 제어하고 싶다면 &lt;code&gt;Cache-Control&lt;/code&gt;이 먼저다. &lt;code&gt;Clear-Site-Data&lt;/code&gt;는 &quot;앞으로 캐시하지 말라&quot;가 아니라 &quot;이미 저장된 사이트 데이터를 비워라&quot;에 가깝다.&lt;/p&gt;

&lt;h2&gt;어떤 값을 넣을 수 있을까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;지시어&lt;/th&gt;
      &lt;th&gt;의미&lt;/th&gt;
      &lt;th&gt;주로 검토하는 상황&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;&quot;cache&quot;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;브라우저 캐시 등 origin 관련 캐시 데이터 제거&lt;/td&gt;
      &lt;td&gt;이전 사용자 화면이나 상태가 캐시에 남으면 안 될 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;&quot;cookies&quot;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;쿠키와 HTTP 인증 자격 증명 제거&lt;/td&gt;
      &lt;td&gt;로그아웃, 세션 강제 만료&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;&quot;storage&quot;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;localStorage&lt;/code&gt;, &lt;code&gt;sessionStorage&lt;/code&gt;, IndexedDB, 서비스 워커 등록 등 제거&lt;/td&gt;
      &lt;td&gt;오프라인 데이터나 클라이언트 저장소를 함께 비워야 할 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;&quot;executionContexts&quot;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;해당 origin을 렌더링 중인 문맥을 다시 로드&lt;/td&gt;
      &lt;td&gt;메모리에 남은 상태를 새로 고쳐야 할 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;&quot;*&quot;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;지원되는 전체 데이터 타입 정리&lt;/td&gt;
      &lt;td&gt;부분 선택보다 전체 초기화가 명확할 때&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;MDN은 현재 문서에서 &lt;code&gt;&quot;clientHints&quot;&lt;/code&gt;, &lt;code&gt;&quot;prefetchCache&quot;&lt;/code&gt;, &lt;code&gt;&quot;prerenderCache&quot;&lt;/code&gt;도 소개하지만, 실제 운영 판단에서는 우선 위 표의 핵심 값들을 이해하는 편이 중요하다. 특히 헤더 값은 반드시 큰따옴표로 감싸야 하며, 따옴표가 없으면 유효하지 않다.&lt;/p&gt;

&lt;h2&gt;HTTPS가 아니면 왜 동작을 기대하면 안 될까?&lt;/h2&gt;
&lt;p&gt;W3C 문서는 응답 URL이 a priori authenticated URL이 아니면 정리 알고리즘을 중단하도록 정의한다. 실무적으로는 HTTPS가 아닌 응답에서 이 헤더를 보내도 기대한 정리가 일어나지 않을 수 있다는 뜻이다.&lt;/p&gt;
&lt;p&gt;따라서 내부 테스트든 운영 환경이든 &lt;code&gt;Clear-Site-Data&lt;/code&gt;는 HTTPS 전제를 깔고 설계하는 편이 맞다. 로컬 개발 환경에서 동작이 다르게 보인다면 먼저 전송 경로와 브라우저 지원 범위를 확인해야 한다.&lt;/p&gt;

&lt;h2&gt;로그아웃에는 어떤 조합이 무난할까?&lt;/h2&gt;
&lt;p&gt;대부분의 웹 서비스에서 로그아웃 응답은 &lt;code&gt;&quot;cookies&quot;&lt;/code&gt;와 &lt;code&gt;&quot;storage&quot;&lt;/code&gt;를 기본 검토 대상으로 본다. 자동 로그인 흔적이나 사용자별 저장 상태가 남으면 안 되는 서비스라면 &lt;code&gt;&quot;cache&quot;&lt;/code&gt;까지 함께 고려할 수 있다.&lt;/p&gt;
&lt;p&gt;다만 W3C 문서는 &lt;code&gt;Set-Cookie&lt;/code&gt; 처리가 먼저 일어난 뒤 &lt;code&gt;Clear-Site-Data&lt;/code&gt;가 적용된다고 설명한다. 즉 같은 로그아웃 응답에서 새 쿠키를 심어 두고 &lt;code&gt;&quot;cookies&quot;&lt;/code&gt;를 같이 보내면 그 쿠키도 함께 지워질 수 있다. 로그아웃 후 새 상태 쿠키가 필요하면 후속 응답에서 다시 설정하는 흐름이 더 안전하다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;&quot;cookies&quot;&lt;/code&gt;만 지우면 끝일까?&lt;/h2&gt;
&lt;p&gt;항상 그렇지는 않다. MDN은 &lt;code&gt;&quot;cookies&quot;&lt;/code&gt;가 응답 origin의 등록 도메인 전체와 하위 도메인까지 영향을 줄 수 있다고 설명한다. 하지만 쿠키만 비워도 &lt;code&gt;localStorage&lt;/code&gt;나 IndexedDB에 남은 사용자 데이터는 그대로일 수 있다.&lt;/p&gt;
&lt;p&gt;그래서 SPA나 오프라인 저장을 쓰는 서비스라면 &lt;code&gt;&quot;cookies&quot;&lt;/code&gt; 단독보다 &lt;code&gt;&quot;storage&quot;&lt;/code&gt;를 함께 검토하는 편이 현실적이다. 반대로 여러 서브도메인이 쿠키를 공유하는 환경에서는 &lt;code&gt;&quot;cookies&quot;&lt;/code&gt;의 파급 범위를 먼저 점검해야 한다.&lt;/p&gt;

&lt;h2&gt;와일드카드 &lt;code&gt;&quot;*&quot;&lt;/code&gt;는 편한 기본값일까?&lt;/h2&gt;
&lt;p&gt;W3C 문서는 전체 정리가 목적일 때는 &lt;code&gt;&quot;*&quot;&lt;/code&gt;를 써도 되지만, 단지 네 가지 핵심 값을 줄여 쓰는 약어로 이해하지 말라고 적고 있다. 앞으로 데이터 타입이 늘어나면 와일드카드가 포함하는 범위도 함께 넓어질 수 있기 때문이다.&lt;/p&gt;
&lt;p&gt;즉 서비스 요구사항이 &quot;정말 전체 초기화&quot;라면 &lt;code&gt;&quot;*&quot;&lt;/code&gt;가 맞고, 아니면 필요한 지시어만 명시하는 편이 변경 영향이 더 예측 가능하다.&lt;/p&gt;

&lt;h2&gt;적용 전에 무엇을 확인해야 할까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;확인 항목&lt;/th&gt;
      &lt;th&gt;왜 필요한가&lt;/th&gt;
      &lt;th&gt;실무 기준&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;HTTPS 응답 여부&lt;/td&gt;
      &lt;td&gt;비보안 맥락에서는 동작을 기대하기 어려움&lt;/td&gt;
      &lt;td&gt;운영 적용 전 실제 배포 경로에서 확인&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;저장소 사용 범위&lt;/td&gt;
      &lt;td&gt;쿠키 외 데이터가 남을 수 있음&lt;/td&gt;
      &lt;td&gt;IndexedDB, 서비스 워커, &lt;code&gt;localStorage&lt;/code&gt; 사용 여부 점검&lt;/td&gt;
    &lt;/tr&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&gt;
      &lt;td&gt;후속 세션 처리&lt;/td&gt;
      &lt;td&gt;같은 응답에서 심은 쿠키가 지워질 수 있음&lt;/td&gt;
      &lt;td&gt;로그아웃 후 리다이렉트 또는 다음 응답에서 재설정&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;MDN은 이 헤더를 2023년 9월 이후 여러 브라우저에서 넓게 사용할 수 있는 기능으로 표시하지만, 세부 데이터 타입마다 지원 차이가 있을 수 있다고 함께 안내한다. 따라서 최신 브라우저 중심 서비스인지, 구형 환경까지 포함하는지도 같이 봐야 한다.&lt;/p&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;Q. &lt;code&gt;Cache-Control: no-store&lt;/code&gt;와 같은 역할일까?&lt;/h3&gt;
&lt;p&gt;아니다. &lt;code&gt;Cache-Control&lt;/code&gt;은 캐시 저장과 재사용 정책을 제어하고, &lt;code&gt;Clear-Site-Data&lt;/code&gt;는 이미 저장된 사이트 데이터를 지우도록 지시한다.&lt;/p&gt;

&lt;h3&gt;Q. 로그인 응답에도 써야 할까?&lt;/h3&gt;
&lt;p&gt;일반적으로는 로그아웃이나 계정 초기화 같은 정리 시점에 더 잘 맞는다. 로그인 직후에는 기존 세션 데이터와 충돌하지 않는지, 새로 설정한 쿠키를 같은 응답에서 다시 지우지 않는지 먼저 검토해야 한다.&lt;/p&gt;

&lt;h3&gt;Q. 모든 로그아웃에 &lt;code&gt;&quot;*&quot;&lt;/code&gt;를 넣는 편이 안전할까?&lt;/h3&gt;
&lt;p&gt;안전하다고 단정하기는 어렵다. 전체 초기화가 명확한 요구사항일 때는 적합하지만, 서비스 워커나 캐시까지 모두 비우면 사용성 영향이 커질 수 있다. 필요한 범위를 명시하는 편이 운영 영향 예측에는 유리하다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Clear-Site-Data&lt;/code&gt;는 브라우저에 남은 세션 흔적을 서버가 응답 헤더로 정리하도록 지시하는 기능이다. 실무에서는 로그아웃이나 계정 전환처럼 상태 정리가 핵심인 시점에 검토하고, &lt;code&gt;&quot;cookies&quot;&lt;/code&gt;만이 아니라 &lt;code&gt;&quot;storage&quot;&lt;/code&gt;, &lt;code&gt;&quot;cache&quot;&lt;/code&gt; 필요 여부까지 함께 판단하는 편이 맞다.&lt;/p&gt;
&lt;p&gt;2026년 6월 28일 기준 공개 문서를 기준으로 보면 무난한 적용 순서는 HTTPS 전제 확인, 실제 저장소 사용 범위 파악, 로그아웃 응답의 쿠키 재설정 흐름 점검, 그리고 필요한 지시어만 좁게 선택하는 것이다. 특히 와일드카드는 편의상 쓰기보다 전체 초기화 의도가 분명할 때만 선택하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Clear-Site-Data&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN: Clear-Site-Data header&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/clear-site-data/&quot; rel=&quot;noopener noreferrer&quot;&gt;W3C: Clear Site Data&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/HTML, Javascript, CSS</category>
      <category>browser storage</category>
      <category>Clear-Site-Data</category>
      <category>Cookie</category>
      <category>Frontend Security</category>
      <category>HTTP Header</category>
      <category>logout</category>
      <category>web security</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/628</guid>
      <comments>https://jcdgods.tistory.com/628#entry628comment</comments>
      <pubDate>Sun, 28 Jun 2026 12:36:00 +0900</pubDate>
    </item>
    <item>
      <title>systemd ExecCondition 기준 정리</title>
      <link>https://jcdgods.tistory.com/627</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdbRc0/dJMb997XkzM/vxdm4Mw11Ejvoqxaznt0Ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdbRc0/dJMb997XkzM/vxdm4Mw11Ejvoqxaznt0Ak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdbRc0/dJMb997XkzM/vxdm4Mw11Ejvoqxaznt0Ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdbRc0%2FdJMb997XkzM%2Fvxdm4Mw11Ejvoqxaznt0Ak%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;systemd ExecCondition 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;ExecCondition=&lt;/code&gt;은 systemd 서비스 시작 전에 조건을 검사하되, 조건이 맞지 않을 때 서비스를 실패로 기록하지 않고 조용히 건너뛸 수 있게 만든 옵션이다. 2026년 6월 27일 기준 upstream &lt;code&gt;systemd.service&lt;/code&gt; 공식 문서를 보면 &lt;code&gt;ExecCondition=&lt;/code&gt;은 &lt;code&gt;ExecStartPre=&lt;/code&gt;보다 먼저 실행되고, 종료 코드에 따라 &quot;실패&quot;와 &quot;조건 불일치&quot;를 구분한다.&lt;/p&gt;
&lt;p&gt;실무에서 자주 헷갈리는 부분은 세 가지다. 단순 준비 작업이면 &lt;code&gt;ExecStartPre=&lt;/code&gt;를 써야 하는지, 조건 검사면 &lt;code&gt;ExecCondition=&lt;/code&gt;을 써야 하는지, 그리고 시작 중간에 멈췄을 때 정리 작업을 &lt;code&gt;ExecStop=&lt;/code&gt;와 &lt;code&gt;ExecStopPost=&lt;/code&gt; 중 어디에 둬야 하는지다. 이 글은 그 경계를 공식 문서 기준으로만 정리한다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;ExecCondition=&lt;/code&gt;은 언제 쓰는 옵션일까?&lt;/h2&gt;
&lt;p&gt;공식 문서에 따르면 &lt;code&gt;ExecCondition=&lt;/code&gt;은 &lt;code&gt;ExecStartPre=&lt;/code&gt;와 condition check를 섞은 형태다. 서비스 시작 전에 명령을 실행한다는 점은 비슷하지만, 종료 코드 1부터 254까지는 유닛을 실패로 표시하지 않고 이후 시작 절차를 건너뛴다는 점이 다르다.&lt;/p&gt;
&lt;p&gt;예를 들어 특정 설정 파일이 있을 때만 서비스를 올리거나, 장비 노드가 준비된 경우에만 시작하고 싶을 때가 여기에 가깝다. 이런 경우 조건이 맞지 않았다는 사실과 서비스 자체의 비정상 실패는 운영상 의미가 다르므로, &lt;code&gt;ExecStartPre=&lt;/code&gt;보다 &lt;code&gt;ExecCondition=&lt;/code&gt;이 더 맞다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;ExecCondition=&lt;/code&gt;과 &lt;code&gt;ExecStartPre=&lt;/code&gt;는 무엇이 다를까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;항목&lt;/th&gt;
      &lt;th&gt;&lt;code&gt;ExecCondition=&lt;/code&gt;&lt;/th&gt;
      &lt;th&gt;&lt;code&gt;ExecStartPre=&lt;/code&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;실행 순서&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;ExecStartPre=&lt;/code&gt;보다 먼저 실행&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;ExecCondition=&lt;/code&gt; 뒤에 실행&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;종료 코드 0&lt;/td&gt;
      &lt;td&gt;다음 단계 계속 진행&lt;/td&gt;
      &lt;td&gt;다음 단계 계속 진행&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;종료 코드 1~254&lt;/td&gt;
      &lt;td&gt;남은 시작 절차를 건너뛰고 실패로 기록하지 않음&lt;/td&gt;
      &lt;td&gt;실패로 기록되고 이후 단계 중단&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;종료 코드 255 또는 비정상 종료&lt;/td&gt;
      &lt;td&gt;실패로 기록&lt;/td&gt;
      &lt;td&gt;실패로 기록&lt;/td&gt;
    &lt;/tr&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;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;핵심은 &quot;조건이 맞지 않아 시작하지 않음&quot;과 &quot;준비 작업이 실패함&quot;을 분리할 수 있느냐이다. 조건 불일치를 정상 흐름으로 다뤄야 하면 &lt;code&gt;ExecCondition=&lt;/code&gt;, 그 단계가 실패하면 유닛 실패로 봐야 하면 &lt;code&gt;ExecStartPre=&lt;/code&gt;가 맞다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;종료 코드는 어떻게 해석될까?&lt;/h2&gt;
&lt;p&gt;공식 문서 기준으로 &lt;code&gt;ExecCondition=&lt;/code&gt;은 세 구간으로 나뉜다. 첫째, 종료 코드 0 또는 &lt;code&gt;SuccessExitStatus=&lt;/code&gt;에 포함된 값이면 다음 명령으로 진행한다. 둘째, 1부터 254까지면 남은 명령을 건너뛰고 유닛을 실패로 표시하지 않는다. 셋째, 255 또는 타임아웃, 시그널 종료 같은 비정상 종료는 유닛 실패로 처리된다.&lt;/p&gt;
&lt;p&gt;이 차이 때문에 &lt;code&gt;/usr/bin/test&lt;/code&gt;, &lt;code&gt;grep -q&lt;/code&gt;, &lt;code&gt;sh -c '...'&lt;/code&gt; 같은 조건성 명령을 붙이기 쉽다. 다만 명령 자체가 복잡해져서 종료 코드 의미가 흐려지면 운영자가 상태를 읽기 어려워질 수 있으므로, 조건 검사만 맡기는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;ExecStartPre=&lt;/code&gt;에는 무엇을 두는 편이 맞을까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ExecStartPre=&lt;/code&gt;는 메인 프로세스를 띄우기 전에 반드시 성공해야 하는 준비 작업에 가깝다. 예를 들면 런타임 디렉터리 권한 조정, 설정 문법 검사, 필요한 파일 생성처럼 &quot;이 단계가 실패하면 시작 자체를 중단해야 하는 작업&quot;이 여기에 해당한다.&lt;/p&gt;
&lt;p&gt;공식 문서는 &lt;code&gt;ExecStartPre=&lt;/code&gt;에 장시간 실행되는 프로세스를 두지 말라고 명시한다. 여기서 파생된 프로세스는 다음 서비스 프로세스가 실행되기 전에 정리되기 때문이다. 같은 권고가 &lt;code&gt;ExecCondition=&lt;/code&gt;에도 적용되므로, 둘 다 짧고 결정적인 명령에만 쓰는 편이 맞다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;ExecStartPost=&lt;/code&gt;는 언제 의미가 생길까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ExecStartPost=&lt;/code&gt;는 &lt;code&gt;ExecStart=&lt;/code&gt;가 성공적으로 시작된 뒤 실행된다. 여기서 &quot;성공&quot;은 &lt;code&gt;Type=&lt;/code&gt;에 따라 달라진다. 예를 들어 &lt;code&gt;Type=simple&lt;/code&gt;은 프로세스 시작 직후, &lt;code&gt;Type=oneshot&lt;/code&gt;은 마지막 &lt;code&gt;ExecStart=&lt;/code&gt; 종료 후, &lt;code&gt;Type=notify&lt;/code&gt;는 &lt;code&gt;READY=1&lt;/code&gt; 수신 후를 기준으로 한다.&lt;/p&gt;
&lt;p&gt;또 하나 중요한 점은 공식 문서가 &lt;code&gt;ExecStartPost=&lt;/code&gt; 실행 시간도 &lt;code&gt;Before=&lt;/code&gt;, &lt;code&gt;After=&lt;/code&gt; ordering 계산에 포함된다고 설명한다는 점이다. 즉 후속 유닛이 시작되기 전에 꼭 끝나야 하는 짧은 후처리라면 여기에 둘 수 있지만, 오래 걸리는 부가 작업을 넣으면 의존 유닛 시작도 같이 늦어진다.&lt;/p&gt;

&lt;h2&gt;시작 중간에 실패했을 때 정리 작업은 어디에 둬야 할까?&lt;/h2&gt;
&lt;p&gt;이 구간에서 &lt;code&gt;ExecStop=&lt;/code&gt;와 &lt;code&gt;ExecStopPost=&lt;/code&gt;를 혼동하기 쉽다. 공식 문서에 따르면 &lt;code&gt;ExecStop=&lt;/code&gt;는 서비스가 &quot;성공적으로 시작된 뒤&quot;에만 실행된다. 반대로 &lt;code&gt;ExecStartPre=&lt;/code&gt;, &lt;code&gt;ExecStart=&lt;/code&gt;, &lt;code&gt;ExecStartPost=&lt;/code&gt; 중 하나가 실패하거나 타임아웃이 나면 &lt;code&gt;ExecStop=&lt;/code&gt;는 건너뛰고 &lt;code&gt;ExecStopPost=&lt;/code&gt;로 이어진다.&lt;/p&gt;
&lt;p&gt;따라서 시작 도중 남을 수 있는 임시 파일, 반쯤 만들어진 상태 디렉터리, 외부 락 해제 같은 정리 작업은 &lt;code&gt;ExecStopPost=&lt;/code&gt;에 두는 편이 맞다. 공식 문서는 이 단계가 서비스 실패 결과와 종료 상태를 담은 &lt;code&gt;$SERVICE_RESULT&lt;/code&gt;, &lt;code&gt;$EXIT_CODE&lt;/code&gt;, &lt;code&gt;$EXIT_STATUS&lt;/code&gt; 환경 변수를 받을 수 있다고 설명한다.&lt;/p&gt;

&lt;h2&gt;운영에서 무난한 선택 기준은 무엇일까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;권장 위치&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;특정 파일이나 장치가 있을 때만 시작&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;ExecCondition=&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;조건 불일치를 실패와 분리할 수 있음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;문법 검사나 디렉터리 생성처럼 실패 시 시작 중단이 맞는 준비 작업&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;ExecStartPre=&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;실패를 즉시 유닛 실패로 기록함&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;시작 성공 직후의 짧은 후처리&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;ExecStartPost=&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;Type=&lt;/code&gt; 기준의 시작 완료 뒤 실행됨&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;시작 실패까지 포함해 항상 수행해야 하는 정리 작업&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;ExecStopPost=&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;초기화 도중 실패한 경우에도 호출됨&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;실무 기본값은 단순하다. 시작 여부를 판정하면 &lt;code&gt;ExecCondition=&lt;/code&gt;, 시작 전에 반드시 끝내야 하는 준비면 &lt;code&gt;ExecStartPre=&lt;/code&gt;, 시작 성공 뒤 짧은 후처리면 &lt;code&gt;ExecStartPost=&lt;/code&gt;, 실패 포함 정리면 &lt;code&gt;ExecStopPost=&lt;/code&gt;로 나누면 된다.&lt;/p&gt;

&lt;h2&gt;자주 묻는 질문&lt;/h2&gt;
&lt;h3&gt;Q. 조건 검사도 결국 실패 아닌가?&lt;/h3&gt;
&lt;p&gt;systemd 공식 문서 기준으로는 다르다. &lt;code&gt;ExecCondition=&lt;/code&gt;에서 종료 코드 1부터 254까지는 유닛 실패가 아니라 &quot;나머지 시작 절차를 진행하지 않음&quot;으로 처리된다. 운영자가 의도적으로 건너뛴 시작과 실제 장애를 분리하고 싶을 때 이 차이가 중요하다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;ExecCondition=&lt;/code&gt; 안에서 백그라운드 프로세스를 띄워도 될까?&lt;/h3&gt;
&lt;p&gt;권장되지 않는다. 공식 문서는 &lt;code&gt;ExecStartPre=&lt;/code&gt;에 장시간 실행 프로세스를 두지 말라고 하며, 같은 권고가 &lt;code&gt;ExecCondition=&lt;/code&gt;에도 적용된다고 적고 있다. 두 옵션 모두 짧고 판정 가능한 명령에 한정하는 편이 안전하다.&lt;/p&gt;

&lt;h3&gt;Q. 시작 중간 실패에도 반드시 청소해야 하면 &lt;code&gt;ExecStop=&lt;/code&gt;면 충분할까?&lt;/h3&gt;
&lt;p&gt;충분하지 않다. 공식 문서 기준으로 서비스가 성공적으로 시작되지 못한 경우 &lt;code&gt;ExecStop=&lt;/code&gt;는 호출되지 않을 수 있다. 이런 경우까지 포함해야 하면 &lt;code&gt;ExecStopPost=&lt;/code&gt;를 써야 한다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ExecCondition=&lt;/code&gt;의 역할은 &quot;시작 전 검사&quot;가 아니라 &quot;조건 불일치를 실패와 분리하는 시작 전 검사&quot;에 가깝다. 그래서 준비 작업을 넣는 용도인 &lt;code&gt;ExecStartPre=&lt;/code&gt;와는 의도가 다르다.&lt;/p&gt;
&lt;p&gt;2026년 6월 27일 기준 upstream systemd 공식 문서를 기준으로 보면 판단 기준은 네 가지면 충분하다. 조건 불일치를 실패로 기록하지 않아야 하는지, 준비 작업이 반드시 성공해야 하는지, 시작 성공 뒤 후처리가 필요한지, 그리고 시작 실패까지 포함한 정리가 필요한지다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html&quot; rel=&quot;noopener noreferrer&quot;&gt;systemd 공식 문서: systemd.service&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://man7.org/linux/man-pages/man5/systemd.service.5.html&quot; rel=&quot;noopener noreferrer&quot;&gt;man7.org 미러: systemd.service(5)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/운영체제</category>
      <category>ExecCondition</category>
      <category>ExecStartPre</category>
      <category>ExecStopPost</category>
      <category>Linux</category>
      <category>systemd</category>
      <category>운영체제</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/627</guid>
      <comments>https://jcdgods.tistory.com/627#entry627comment</comments>
      <pubDate>Sat, 27 Jun 2026 12:15:46 +0900</pubDate>
    </item>
    <item>
      <title>Git restore 사용법</title>
      <link>https://jcdgods.tistory.com/626</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckT15P/dJMcacQ6ggh/WK05HppGr8LRnKh9UBrYLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckT15P/dJMcacQ6ggh/WK05HppGr8LRnKh9UBrYLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckT15P/dJMcacQ6ggh/WK05HppGr8LRnKh9UBrYLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckT15P%2FdJMcacQ6ggh%2FWK05HppGr8LRnKh9UBrYLK%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Git restore 사용법&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;git restore&lt;/code&gt;는 작업 트리 파일이나 스테이징 영역의 내용을 특정 기준 상태로 되돌릴 때 쓰는 명령이다. 2026년 6월 26일 기준 Git 공식 문서는 기본적으로 작업 트리는 index에서, &lt;code&gt;--staged&lt;/code&gt;를 주면 index는 &lt;code&gt;HEAD&lt;/code&gt;에서 복원된다고 설명한다.&lt;/p&gt;
&lt;p&gt;실무에서 핵심은 세 가지다. 작업 트리만 되돌릴지, 스테이징만 되돌릴지, 아니면 특정 커밋의 내용으로 맞출지를 먼저 구분해야 한다. 이 글은 Git 공식 문서를 기준으로 &lt;code&gt;git restore&lt;/code&gt;의 기본 동작과 자주 쓰는 옵션을 정리한다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;git restore&lt;/code&gt;는 언제 쓰는 명령일까?&lt;/h2&gt;
&lt;p&gt;Git 공식 문서에서 &lt;code&gt;git restore&lt;/code&gt;의 설명은 &quot;작업 트리 파일을 복원한다&quot;에 가깝다. tracked 경로를 지정한 소스 상태로 되돌리는 역할이므로, 브랜치 전환이 목적일 때는 &lt;code&gt;git switch&lt;/code&gt;나 &lt;code&gt;git checkout&lt;/code&gt;의 다른 모드를 쓰는 편이 맞다.&lt;/p&gt;
&lt;p&gt;즉 이 명령은 &quot;브랜치를 바꾼다&quot;보다 &quot;파일 내용을 되돌린다&quot;에 초점이 있다. 예를 들어 실수로 수정한 파일을 index 상태로 되돌리거나, 이미 &lt;code&gt;git add&lt;/code&gt;한 변경을 다시 unstaged 상태로 되돌릴 때 가장 직접적이다.&lt;/p&gt;

&lt;h2&gt;기본 동작은 무엇을 기준으로 복원할까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;권장 명령&lt;/th&gt;
      &lt;th&gt;복원 기준&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;작업 트리 수정만 취소하고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git restore path/to/file&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;index 내용을 작업 트리에 다시 쓴다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;스테이징만 취소하고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git restore --staged path/to/file&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;HEAD&lt;/code&gt; 내용을 index에 다시 쓴다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;작업 트리와 스테이징을 함께 되돌리고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git restore --staged --worktree path/to/file&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;HEAD&lt;/code&gt; 기준으로 index와 작업 트리를 함께 맞춘다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;특정 커밋 상태로 파일을 가져오고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git restore --source=&amp;lt;commit&amp;gt; --worktree path/to/file&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;지정한 commit, branch, tag를 소스로 삼는다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Git 공식 문서 기준으로 아무 옵션이 없으면 작업 트리만 복원 대상이다. 반대로 &lt;code&gt;--staged&lt;/code&gt;를 주면 index 복원으로 바뀌고, 이때 기본 소스는 &lt;code&gt;HEAD&lt;/code&gt;다. 이 차이를 놓치면 &quot;왜 예상한 내용이 안 돌아왔지?&quot; 같은 혼란이 생기기 쉽다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;--source&lt;/code&gt;는 언제 붙여야 할까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;--source=&amp;lt;tree&amp;gt;&lt;/code&gt;는 복원 기준을 index나 &lt;code&gt;HEAD&lt;/code&gt; 대신 특정 commit, branch, tag로 명시할 때 쓴다. 따라서 현재 작업 중인 브랜치의 최신 상태가 아니라, 이전 커밋의 특정 파일만 가져오고 싶을 때 유용하다.&lt;/p&gt;
&lt;p&gt;예를 들어 설정 파일 한 개만 지난 릴리스 버전으로 비교 복원하고 싶다면 &lt;code&gt;git restore --source=v1.2.0 --worktree config.yml&lt;/code&gt;처럼 쓰는 식이다. 문서상 tracked 경로가 소스에 없으면 해당 경로는 소스와 맞추기 위해 제거될 수 있으므로, 경로 단위 지정은 보수적으로 하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;--staged&lt;/code&gt;와 &lt;code&gt;--worktree&lt;/code&gt;는 어떻게 나눠야 할까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;--worktree&lt;/code&gt;는 실제 파일 시스템의 작업 트리를 복원 대상으로 삼고, &lt;code&gt;--staged&lt;/code&gt;는 index를 복원 대상으로 삼는다. 둘 중 하나만 주면 그 대상만 바뀌고, 둘 다 주면 index와 작업 트리를 함께 복원한다.&lt;/p&gt;
&lt;p&gt;실무에서는 &quot;파일 편집은 유지하고 add만 취소&quot;라면 &lt;code&gt;--staged&lt;/code&gt;만, &quot;편집 자체를 버리고 index 상태로 되돌림&quot;이라면 기본 &lt;code&gt;git restore&lt;/code&gt;, &quot;add도 취소하고 파일 수정도 버림&quot;이라면 &lt;code&gt;--staged --worktree&lt;/code&gt;를 구분하면 된다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;--patch&lt;/code&gt;는 언제 더 안전할까?&lt;/h2&gt;
&lt;p&gt;Git 공식 문서는 &lt;code&gt;--patch&lt;/code&gt;가 복원 소스와 대상의 차이를 hunk 단위로 보여 주고, 사용자가 선택적으로 복원할 수 있다고 설명한다. 전체 파일을 버리기에는 부담스럽고 일부 변경만 취소하고 싶을 때 적합하다.&lt;/p&gt;
&lt;p&gt;예를 들어 한 파일 안에서 디버그 로그만 되돌리고 실제 기능 수정은 남기고 싶다면 &lt;code&gt;git restore -p app.js&lt;/code&gt;가 더 안전하다. 이는 &lt;code&gt;git add -p&lt;/code&gt;와 비슷한 상호작용 모델을 사용하므로, 패치 선택 워크플로에 익숙한 팀에서 특히 편하다.&lt;/p&gt;

&lt;h2&gt;머지 충돌 중에는 무엇을 조심해야 할까?&lt;/h2&gt;
&lt;p&gt;문서 기준으로 &lt;code&gt;--ours&lt;/code&gt;와 &lt;code&gt;--theirs&lt;/code&gt;는 unmerged 경로를 index에서 작업 트리로 복원할 때만 쓸 수 있고, &lt;code&gt;--source&lt;/code&gt;와 함께는 사용할 수 없다. 즉 충돌 해결 중 특정 쪽 버전을 작업 트리에 꺼낼 때 쓰는 옵션이다.&lt;/p&gt;
&lt;p&gt;또 Git 공식 문서는 &lt;code&gt;rebase&lt;/code&gt;나 &lt;code&gt;pull --rebase&lt;/code&gt; 중에는 &lt;code&gt;ours&lt;/code&gt;와 &lt;code&gt;theirs&lt;/code&gt;가 뒤바뀐 것처럼 보일 수 있다고 분명히 적고 있다. rebase 중 충돌을 다룰 때는 평소 merge 때의 감각만 믿지 말고 현재 워크플로를 기준으로 확인하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;체크아웃과는 무엇이 다를까?&lt;/h2&gt;
&lt;p&gt;Git 문서 구조상 &lt;code&gt;restore&lt;/code&gt;는 기본 스냅샷 조작 명령, &lt;code&gt;checkout&lt;/code&gt;은 브랜치 전환과 경로 교체까지 함께 품은 명령으로 정리되어 있다. 최근 Git 사용법에서 파일 복원은 &lt;code&gt;restore&lt;/code&gt;, 브랜치 전환은 &lt;code&gt;switch&lt;/code&gt;로 의도를 나누는 이유가 여기에 있다.&lt;/p&gt;
&lt;p&gt;팀 문서나 교육 자료를 새로 쓴다면 &quot;브랜치 이동은 &lt;code&gt;git switch&lt;/code&gt;, 파일 복원은 &lt;code&gt;git restore&lt;/code&gt;&quot;처럼 역할을 분리해 적는 편이 실수를 줄인다. 특히 초보자가 &lt;code&gt;checkout&lt;/code&gt;의 여러 모드를 한 번에 외우려다 혼란을 겪는 문제를 줄이기 좋다.&lt;/p&gt;

&lt;h2&gt;이런 상황에서는 어떤 명령이 무난할까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;권장 명령&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;수정한 파일을 add 전 상태로 되돌리고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git restore src/app.py&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;index 기준으로 작업 트리만 복원한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;실수로 &lt;code&gt;git add&lt;/code&gt;한 파일을 unstaged로 돌리고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git restore --staged src/app.py&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;HEAD&lt;/code&gt; 기준으로 index만 복원한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;이전 커밋 버전의 파일을 현재 작업 트리에 가져오고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git restore --source=HEAD~1 --worktree src/app.py&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;특정 커밋을 소스로 명시한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;변경 일부만 골라서 취소하고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git restore -p src/app.py&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;hunk 단위로 선택 복원할 수 있다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. &lt;code&gt;git restore&lt;/code&gt;는 untracked 파일도 지우나?&lt;/h3&gt;
&lt;p&gt;아니다. Git 공식 문서의 설명 대상은 tracked 경로 복원이다. untracked 파일 정리는 &lt;code&gt;git clean&lt;/code&gt; 계열로 구분해야 한다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;git restore --staged&lt;/code&gt;는 파일 수정분까지 같이 버리나?&lt;/h3&gt;
&lt;p&gt;아니다. &lt;code&gt;--staged&lt;/code&gt;만 주면 index만 복원하고 작업 트리 수정은 남는다. 수정까지 버리려면 &lt;code&gt;--worktree&lt;/code&gt;를 함께 써야 한다.&lt;/p&gt;

&lt;h3&gt;Q. 특정 브랜치의 파일을 현재 브랜치에서 가져오고 싶으면 가능할까?&lt;/h3&gt;
&lt;p&gt;가능하다. &lt;code&gt;git restore --source=main --worktree path/to/file&lt;/code&gt;처럼 branch 이름을 소스로 줄 수 있다. 다만 해당 경로가 소스 브랜치에 실제로 있는지 먼저 확인하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git restore&lt;/code&gt;의 선택 기준은 단순하다. 작업 트리만 되돌릴지, index만 되돌릴지, 특정 commit을 소스로 삼을지를 먼저 정하면 된다. 그 다음에 &lt;code&gt;--worktree&lt;/code&gt;, &lt;code&gt;--staged&lt;/code&gt;, &lt;code&gt;--source&lt;/code&gt;, &lt;code&gt;--patch&lt;/code&gt;를 붙이면 대부분의 실무 상황을 설명할 수 있다.&lt;/p&gt;
&lt;p&gt;한 줄 기준으로 줄이면 이렇다. 수정 취소는 &lt;code&gt;git restore&lt;/code&gt;, 스테이징 취소는 &lt;code&gt;git restore --staged&lt;/code&gt;, 특정 커밋 기준 복원은 &lt;code&gt;git restore --source=&amp;lt;tree&amp;gt;&lt;/code&gt;를 우선 검토하면 된다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-restore&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Documentation: git-restore&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-checkout&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Documentation: git-checkout&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Documentation: git&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/Git, IDE, 툴 관련</category>
      <category>CLI</category>
      <category>GIT</category>
      <category>git checkout</category>
      <category>git reset</category>
      <category>git restore</category>
      <category>version control</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/626</guid>
      <comments>https://jcdgods.tistory.com/626#entry626comment</comments>
      <pubDate>Fri, 26 Jun 2026 11:43:28 +0900</pubDate>
    </item>
    <item>
      <title>Python tarfile 추출 필터 기준 정리</title>
      <link>https://jcdgods.tistory.com/625</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Y0K3T/dJMcahZa0qS/j7NLs4aGHgVFi6z20FScO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Y0K3T/dJMcahZa0qS/j7NLs4aGHgVFi6z20FScO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Y0K3T/dJMcahZa0qS/j7NLs4aGHgVFi6z20FScO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FY0K3T%2FdJMcahZa0qS%2Fj7NLs4aGHgVFi6z20FScO0%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Python tarfile 추출 필터 기준 정리&lt;/h1&gt;
&lt;p&gt;Python의 &lt;code&gt;tarfile&lt;/code&gt;은 tar 아카이브를 다룰 때 많이 쓰지만, 추출 기본값을 오래된 예제로만 기억하면 보안 판단이 어긋날 수 있다. 2026년 6월 25일 기준 Python 3.14.6 공식 문서를 보면 &lt;code&gt;TarFile.extractall()&lt;/code&gt;과 &lt;code&gt;extract()&lt;/code&gt;는 3.12에서 추출 필터를 도입했고, 3.14부터 기본 필터가 &lt;code&gt;fully_trusted&lt;/code&gt; 상당 동작에서 &lt;code&gt;data&lt;/code&gt;로 바뀌었다.&lt;/p&gt;
&lt;p&gt;핵심은 단순하다. 신뢰하지 않는 tar 파일을 풀 때는 기본값에 기대지 말고 &lt;code&gt;filter='data'&lt;/code&gt;를 명시하는 편이 안전하다. 이 글은 &lt;code&gt;fully_trusted&lt;/code&gt;, &lt;code&gt;tar&lt;/code&gt;, &lt;code&gt;data&lt;/code&gt;의 차이와 어떤 상황에서 무엇을 고를지 기준만 정리한다.&lt;/p&gt;

&lt;h2&gt;먼저 무엇이 달라졌을까?&lt;/h2&gt;
&lt;p&gt;PEP 706은 &lt;code&gt;tarfile.extractall()&lt;/code&gt;에 필터 개념을 추가해, 멤버를 추출 직전에 수정하거나 거부할 수 있게 하는 방향을 정의했다. 현재 공식 문서 기준으로는 3.12에서 &lt;code&gt;filter&lt;/code&gt; 인자가 추가됐고, 3.14부터 &lt;code&gt;filter&lt;/code&gt;를 생략했을 때 기본값이 &lt;code&gt;data&lt;/code&gt;가 된다.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;버전대&lt;/th&gt;
      &lt;th&gt;필터 기능&lt;/th&gt;
      &lt;th&gt;기본 동작에서 볼 점&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;3.11 이하&lt;/td&gt;
      &lt;td&gt;추출 필터 없음&lt;/td&gt;
      &lt;td&gt;안전한 기본 필터를 기대하기 어렵기 때문에 호출 코드에서 별도 방어가 필요함&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3.12~3.13&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;filter&lt;/code&gt; 인자 추가&lt;/td&gt;
      &lt;td&gt;현재 문서는 하위 버전 호환을 위해 &lt;code&gt;filter='data'&lt;/code&gt;를 명시하라고 권장함&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3.14+&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;filter&lt;/code&gt; 인자 유지&lt;/td&gt;
      &lt;td&gt;생략 시 기본값이 &lt;code&gt;data&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;중요한 점은 3.14에서도 &quot;아무 tar나 마음 놓고 풀어도 된다&quot;는 뜻은 아니라는 것이다. 공식 문서는 여전히 신뢰하지 않는 아카이브를 사전 확인 없이 추출하지 말라고 경고하고, 예외가 나면 일부 파일만 부분 추출된 상태로 남을 수 있다고 설명한다.&lt;/p&gt;

&lt;h2&gt;세 가지 필터는 어떻게 다를까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;필터&lt;/th&gt;
      &lt;th&gt;의미&lt;/th&gt;
      &lt;th&gt;무난한 사용 상황&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;fully_trusted&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;아카이브의 메타데이터를 그대로 신뢰&lt;/td&gt;
      &lt;td&gt;직접 만든 tar를 내부 파이프라인에서 다시 푸는 경우처럼 완전히 신뢰할 수 있을 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;tar&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;GNU tar 일반 사용자 기본 동작에 가깝게 일부 위험 요소를 제한&lt;/td&gt;
      &lt;td&gt;UNIX 파일 속성을 어느 정도 유지해야 하지만 절대 경로나 대상 경로 탈출은 막고 싶을 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;data&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;링크, 디바이스 파일, 일부 권한/소유자 정보를 더 강하게 제한&lt;/td&gt;
      &lt;td&gt;일반적인 데이터 번들, 업로드 파일, 외부에서 받은 패키지처럼 교차 플랫폼 데이터 추출에 가까울 때&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;data&lt;/code&gt;는 &lt;code&gt;tar&lt;/code&gt;가 막는 항목에 더해 절대 경로나 대상 경로 바깥을 가리키는 링크를 거부하고, 파이프와 디바이스 파일도 거부한다. 또한 일반 파일 권한을 더 보수적으로 다루고, 사용자와 그룹 정보도 무시한다. 반대로 tar 고유의 메타데이터를 최대한 보존해야 한다면 &lt;code&gt;data&lt;/code&gt;가 지나치게 제한적일 수 있다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;filter='data'&lt;/code&gt;를 우선으로 생각해도 되는 경우&lt;/h2&gt;
&lt;p&gt;대부분의 애플리케이션에서는 tar를 백업 복원 포맷이 아니라 &quot;파일 묶음 전달 포맷&quot;으로 쓴다. 예를 들어 업로드된 데이터 세트, 배치 작업 입력물, 테스트 fixture, 외부 공급자가 준 산출물은 소유자 정보나 디바이스 파일을 복원하는 것보다 안전한 경로 안에 일반 파일을 푸는 것이 더 중요하다.&lt;/p&gt;
&lt;p&gt;이런 경우라면 공식 문서가 설명하는 &lt;code&gt;data&lt;/code&gt; 필터가 가장 무난하다. 특히 3.13 이하도 같이 지원하는 코드라면 버전에 따라 기본값이 달라질 수 있으므로, 호출부에서 &lt;code&gt;filter='data'&lt;/code&gt;를 명시하는 편이 일관성이 있다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;tar&lt;/code&gt; 필터가 더 맞는 경우는 언제일까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;tar&lt;/code&gt;는 절대 경로와 대상 경로 이탈을 막고 위험한 모드 비트를 정리하지만, &lt;code&gt;data&lt;/code&gt;처럼 UNIX 기능을 크게 줄이지는 않는다. 그래서 아카이브 안의 디렉터리 구조와 일부 메타데이터를 유지해야 하고, 동시에 기본적인 경로 탈출 방어는 필요할 때 고려할 만하다.&lt;/p&gt;
&lt;p&gt;다만 &lt;code&gt;tar&lt;/code&gt;도 &quot;신뢰하지 않는 아카이브를 아무 검토 없이 안전하게 푸는 만능 스위치&quot;는 아니다. 링크 처리나 권한 복원 정책을 세밀하게 통제해야 하면 내장 필터만으로 끝내지 말고, 커스텀 필터나 추출 전 검증을 추가하는 편이 맞다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;fully_trusted&lt;/code&gt;는 언제만 써야 할까?&lt;/h2&gt;
&lt;p&gt;이 필터는 현재 동작을 최대한 보존하려는 목적에 가깝다. 아카이브에 담긴 경로, 링크, 권한, 소유자 정보를 그대로 존중하므로, 외부 입력을 다루는 일반 웹 서비스나 자동화 작업의 기본값으로 두기에는 부담이 크다.&lt;/p&gt;
&lt;p&gt;무난한 사용 조건은 둘 중 하나다. 첫째, tar를 생성한 주체와 추출하는 주체가 같은 신뢰 경계 안에 있고 아카이브 내용을 이미 알고 있을 때. 둘째, 자체 검증 로직을 별도로 구현해 내장 필터보다 더 엄격하게 통제할 때다. 그 외에는 굳이 &lt;code&gt;fully_trusted&lt;/code&gt;를 선택할 이유가 크지 않다.&lt;/p&gt;

&lt;h2&gt;실무에서는 어떻게 호출하는 편이 안전할까?&lt;/h2&gt;
&lt;p&gt;Python 3.13 이하를 함께 지원하거나, 실행 환경의 Python 마이너 버전이 자주 섞이는 서비스라면 다음처럼 필터를 명시하는 편이 안전하다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import tarfile

with tarfile.open(&quot;bundle.tar.gz&quot;, &quot;r:*&quot;) as tar:
    tar.extractall(&quot;/safe/destination&quot;, filter=&quot;data&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;필터 기능이 있는지 런타임에서 확인해야 한다면 공식 문서는 Python 버전 숫자 비교보다 &lt;code&gt;hasattr(tarfile, 'data_filter')&lt;/code&gt; 같은 기능 존재 여부를 보라고 안내한다. 배포판이 보안 백포트를 적용했을 수 있기 때문이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import tarfile

with tarfile.open(&quot;bundle.tar&quot;, &quot;r:*&quot;) as tar:
    if hasattr(tarfile, &quot;data_filter&quot;):
        tar.extractall(&quot;/safe/destination&quot;, filter=&quot;data&quot;)
    else:
        raise RuntimeError(&quot;tarfile extraction filter를 지원하는 Python이 필요합니다.&quot;)
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;추출 시 같이 기억할 점&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;항목&lt;/th&gt;
      &lt;th&gt;기억할 점&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;extract()&lt;/code&gt; vs &lt;code&gt;extractall()&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;공식 문서는 여러 추출 이슈 때문에 대부분 &lt;code&gt;extractall()&lt;/code&gt; 사용을 고려하라고 설명함&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;예외 처리&lt;/td&gt;
      &lt;td&gt;필터 예외나 I/O 예외가 나면 일부 파일만 추출된 상태가 남을 수 있어 정리 로직이 필요함&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;라이브 파일시스템&lt;/td&gt;
      &lt;td&gt;문서는 추출 도중 대상 디렉터리가 동시에 바뀌는 상황까지 완전히 막아주지 않는다고 설명함&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;하위 버전 지원&lt;/td&gt;
      &lt;td&gt;3.13 이하 호환이 필요하면 기본값에 기대지 말고 필터를 명시하는 편이 안전함&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;Q. Python 3.14 이상이면 &lt;code&gt;filter&lt;/code&gt;를 생략해도 될까?&lt;/h3&gt;
&lt;p&gt;3.14 이상에서는 기본값이 &lt;code&gt;data&lt;/code&gt;라서 이전보다 안전한 쪽으로 바뀌었다. 다만 코드 의도를 분명히 하고 하위 버전 동작 차이를 줄이려면 여전히 &lt;code&gt;filter='data'&lt;/code&gt;를 적어 두는 편이 운영상 더 명확하다.&lt;/p&gt;

&lt;h3&gt;Q. 링크나 소유자 정보가 필요한 tar도 &lt;code&gt;data&lt;/code&gt;를 써야 할까?&lt;/h3&gt;
&lt;p&gt;항상 그렇지는 않다. 링크나 소유자, 권한 복원이 중요한 백업성 아카이브라면 &lt;code&gt;data&lt;/code&gt;가 필요한 기능을 막을 수 있다. 이 경우 &lt;code&gt;tar&lt;/code&gt; 또는 커스텀 필터가 더 맞을 수 있다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;fully_trusted&lt;/code&gt;는 금지해야 할 값일까?&lt;/h3&gt;
&lt;p&gt;금지라기보다 신뢰 경계가 매우 좁을 때만 써야 하는 값에 가깝다. 직접 생성한 tar를 같은 시스템 경계 안에서 다시 푸는 작업처럼, 아카이브 메타데이터를 그대로 복원하는 것이 목적일 때만 선택하는 편이 무난하다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;현재 Python 공식 문서 기준으로 &lt;code&gt;tarfile&lt;/code&gt; 추출에서 가장 무난한 기본 선택은 &lt;code&gt;filter='data'&lt;/code&gt;다. 특히 외부 입력, 업로드 파일, 일반 데이터 번들을 다루는 서비스라면 이 선택이 가장 설명하기 쉽고 안전하다.&lt;/p&gt;
&lt;p&gt;반대로 tar 포맷의 UNIX 특성을 보존해야 하면 &lt;code&gt;tar&lt;/code&gt;나 커스텀 필터가 필요할 수 있고, &lt;code&gt;fully_trusted&lt;/code&gt;는 완전히 신뢰된 아카이브에만 제한적으로 쓰는 편이 맞다. 버전이 섞인 환경에서는 &quot;3.14부터 기본값이 바뀌었다&quot;는 사실만 믿지 말고 호출 코드에서 필터를 명시하는 습관이 더 안전하다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.python.org/3/library/tarfile.html&quot; rel=&quot;noopener noreferrer&quot;&gt;Python 3.14.6 공식 문서: tarfile&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://peps.python.org/pep-0706/&quot; rel=&quot;noopener noreferrer&quot;&gt;PEP 706 - Filter for tarfile.extractall&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/C, C++, Java, Python</category>
      <category>extractall</category>
      <category>python</category>
      <category>tarfile</category>
      <category>보안</category>
      <category>압축파일</category>
      <category>표준라이브러리</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/625</guid>
      <comments>https://jcdgods.tistory.com/625#entry625comment</comments>
      <pubDate>Thu, 25 Jun 2026 10:12:47 +0900</pubDate>
    </item>
    <item>
      <title>[크롤링] 데이터 수집을 위한 크롤링 11편 : RSS와 Atom 피드 사용법</title>
      <link>https://jcdgods.tistory.com/624</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUV9iY/dJMb997Uznc/pwtY0mQ4KzS8RMV5ls6VkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUV9iY/dJMb997Uznc/pwtY0mQ4KzS8RMV5ls6VkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUV9iY/dJMb997Uznc/pwtY0mQ4KzS8RMV5ls6VkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUV9iY%2FdJMb997Uznc%2FpwtY0mQ4KzS8RMV5ls6VkK%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;[크롤링] 데이터 수집을 위한 크롤링 11편 : RSS와 Atom 피드 사용법&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;RSS&lt;/code&gt;와 &lt;code&gt;Atom&lt;/code&gt;은 사이트의 새 글이나 변경 항목을 기계적으로 수집할 때 가장 먼저 확인할 만한 웹 피드 형식이다. 2026년 6월 24일 확인 기준으로 RSS 2.0 사양과 RFC 4287을 보면, 둘 다 XML 기반이지만 필수 메타데이터와 항목 식별 방식에는 차이가 있다.&lt;/p&gt;
&lt;p&gt;실무에서 중요한 질문은 세 가지다. 피드가 있으면 크롤링 범위를 얼마나 줄일 수 있는지, 중복 판정에는 어떤 필드를 믿어야 하는지, 그리고 RSS와 Atom 중 무엇을 우선 읽는 편이 안전한지다. 이 글은 공식 문서에 나온 구조와, 그 구조에서 바로 도출되는 운영 기준만 정리한다.&lt;/p&gt;

&lt;h2&gt;RSS와 Atom 피드는 왜 먼저 볼까?&lt;/h2&gt;
&lt;p&gt;피드는 사이트가 최근 게시물 목록과 핵심 메타데이터를 별도 XML 문서로 제공하는 방식이다. 전체 HTML 목록 페이지를 반복 탐색하는 것보다, 새 항목 후보를 좁히는 데 유리하다.&lt;/p&gt;
&lt;p&gt;이 점은 사양의 구조에서도 드러난다. RSS 2.0은 하나의 &lt;code&gt;channel&lt;/code&gt; 아래에 여러 &lt;code&gt;item&lt;/code&gt;을 담고, Atom은 하나의 &lt;code&gt;feed&lt;/code&gt; 아래에 여러 &lt;code&gt;entry&lt;/code&gt;를 담는다. 즉 두 형식 모두 &quot;최근 항목 목록&quot;을 전달하는 데 초점이 있다.&lt;/p&gt;

&lt;h2&gt;RSS 2.0에서 꼭 봐야 하는 필드는 무엇일까?&lt;/h2&gt;
&lt;p&gt;RSS 2.0 사양에서 &lt;code&gt;channel&lt;/code&gt;의 필수 요소는 &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;link&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt; 세 가지다. 항목 단위인 &lt;code&gt;item&lt;/code&gt;은 모든 요소가 선택 사항이지만, 최소한 &lt;code&gt;title&lt;/code&gt; 또는 &lt;code&gt;description&lt;/code&gt; 중 하나는 있어야 한다.&lt;/p&gt;
&lt;p&gt;또한 사양은 &lt;code&gt;guid&lt;/code&gt;가 있을 경우 리더가 새 항목 여부를 판단하는 데 이 값을 사용할 수 있다고 설명한다. &lt;code&gt;pubDate&lt;/code&gt;는 발행 시점을 나타내지만 필수는 아니다. &lt;code&gt;ttl&lt;/code&gt;은 몇 분 동안 캐시해도 되는지 알려 주는 힌트이며, 모든 클라이언트가 반드시 그대로 따를 의무는 없다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;channel/title&lt;/code&gt;: 피드 이름&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;channel/link&lt;/code&gt;: 대응되는 HTML 사이트 URL&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;channel/description&lt;/code&gt;: 피드 설명&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;item/guid&lt;/code&gt;: 중복 판정 후보&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;item/pubDate&lt;/code&gt;: 시간순 정렬 보조 정보&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;item/link&lt;/code&gt;: 상세 페이지 이동 경로&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;운영 기준으로 보면 RSS는 필수 필드가 비교적 느슨하다. 그래서 실제 현장에서는 &lt;code&gt;guid&lt;/code&gt;가 없거나, &lt;code&gt;pubDate&lt;/code&gt; 형식이 일정하지 않거나, 일부 항목이 제목 없이 설명만 있는 피드도 만날 수 있다.&lt;/p&gt;

&lt;h2&gt;Atom은 RSS와 무엇이 다를까?&lt;/h2&gt;
&lt;p&gt;RFC 4287은 Atom 피드 문서가 정확히 하나의 &lt;code&gt;feed&lt;/code&gt; 요소를 가져야 한다고 정의한다. 이 &lt;code&gt;feed&lt;/code&gt;에는 &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;updated&lt;/code&gt;가 반드시 포함되어야 한다. 항목 단위인 &lt;code&gt;entry&lt;/code&gt;도 &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;updated&lt;/code&gt; 같은 필수 요소 규칙을 가진다.&lt;/p&gt;
&lt;p&gt;RFC 4287 예시에는 &lt;code&gt;rel=&quot;self&quot;&lt;/code&gt; 링크와 &lt;code&gt;rel=&quot;alternate&quot;&lt;/code&gt; 링크가 함께 등장한다. 이 구조는 피드 자체 URL과 사람이 읽는 HTML URL을 구분하는 데 유용하다. RSS에서도 비슷한 해석은 가능하지만, Atom 쪽이 링크 관계를 더 명시적으로 표현한다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;feed/id&lt;/code&gt;: 피드 식별자&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;feed/title&lt;/code&gt;: 피드 제목&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;feed/updated&lt;/code&gt;: 피드 최신 갱신 시각&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;entry/id&lt;/code&gt;: 항목 식별자&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;entry/updated&lt;/code&gt;: 항목 갱신 시각&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;entry/link&lt;/code&gt;: 상세 페이지 또는 관련 리소스 링크&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 차이 때문에 같은 &quot;새 글 목록&quot; 용도라도, 중복 제거와 증분 수집 기준을 엄격하게 세우고 싶다면 Atom이 더 정돈된 입력으로 느껴질 수 있다. 이 평가는 사양상 필수 요소 수가 더 많다는 점에서 나오는 운영 해석이다.&lt;/p&gt;

&lt;h2&gt;크롤러는 어떤 값을 기준으로 새 항목을 판단할까?&lt;/h2&gt;
&lt;p&gt;RSS 사양은 &lt;code&gt;guid&lt;/code&gt;가 있으면 리더가 이를 사용해 새 항목 여부를 판단할 수 있다고 설명한다. 따라서 RSS에서는 &lt;code&gt;guid&lt;/code&gt;를 1순위 식별자로 보고, 없을 때는 &lt;code&gt;link&lt;/code&gt;와 제목 조합을 보조 키로 두는 편이 보수적이다.&lt;/p&gt;
&lt;p&gt;Atom에서는 &lt;code&gt;entry/id&lt;/code&gt;가 더 직접적인 기준이 된다. 갱신 여부는 &lt;code&gt;updated&lt;/code&gt;를 보고 판단할 수 있지만, 실제 처리 정책은 수집기 구현에 달려 있다. 예를 들어 같은 &lt;code&gt;id&lt;/code&gt;에 &lt;code&gt;updated&lt;/code&gt;만 바뀌면 &quot;새 글&quot;이 아니라 &quot;수정된 기존 글&quot;로 취급하는 쪽이 일반적이다.&lt;/p&gt;
&lt;p&gt;여기서 중요한 점은 날짜만으로 중복을 판단하지 않는 것이다. 발행 시각이 바뀌거나 시간대 표기가 흔들릴 수 있기 때문에, 식별자와 링크를 먼저 보고 날짜는 정렬과 재수집 우선순위에 쓰는 편이 안전하다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;RSS와 Atom 중 무엇을 우선하면 좋을까?&lt;/h2&gt;
&lt;p&gt;정답이 하나로 고정되지는 않지만, 공식 문서만 기준으로 보면 다음 순서가 무난하다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;둘 중 하나만 있으면 제공되는 형식을 그대로 사용한다.&lt;/li&gt;
  &lt;li&gt;둘 다 있으면 항목 식별자와 갱신 시각을 더 엄격히 다루기 쉬운 Atom을 우선 검토한다.&lt;/li&gt;
  &lt;li&gt;RSS만 제공되면 &lt;code&gt;guid&lt;/code&gt;, &lt;code&gt;link&lt;/code&gt;, &lt;code&gt;pubDate&lt;/code&gt; 조합으로 보수적으로 수집한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 기준은 Atom이 더 우월하다는 선언이 아니라, 필수 요소가 더 명시적이라는 사실에서 나온 실무 판단이다. 반대로 RSS는 오래된 CMS나 블로그 엔진에서 폭넓게 제공되는 경우가 많아, 실제 수집 현장에서는 여전히 매우 유용하다.&lt;/p&gt;

&lt;h2&gt;피드만으로 끝내도 될까?&lt;/h2&gt;
&lt;p&gt;대부분의 경우 피드는 &quot;새 항목 발견&quot; 단계에 특히 유리하고, 본문 정규화는 상세 HTML이 더 중요하다. RSS 2.0 사양은 &lt;code&gt;description&lt;/code&gt;을 시놉시스 또는 완전한 본문으로 사용할 수 있다고 설명한다. Atom도 항목 안에 요약이나 콘텐츠를 담을 수 있지만, 사이트별 제공 방식은 다를 수 있다.&lt;/p&gt;
&lt;p&gt;그래서 수집 파이프라인을 나누는 편이 좋다. 먼저 피드로 새 항목 후보를 찾고, 그다음 상세 링크를 기준으로 본문 HTML을 가져와 정규화하는 방식이다. 이렇게 하면 목록 탐색 비용을 줄이면서도 본문 품질을 별도 단계에서 통제할 수 있다.&lt;/p&gt;

&lt;h2&gt;재수집 주기는 무엇을 기준으로 잡아야 할까?&lt;/h2&gt;
&lt;p&gt;RSS에는 &lt;code&gt;ttl&lt;/code&gt;이 있지만, 사양상 이것은 힌트다. 즉 피드 제공자가 &lt;code&gt;ttl&lt;/code&gt;을 넣었다고 해서 모든 수집기가 정확히 그 주기를 따라야 하는 것은 아니다.&lt;/p&gt;
&lt;p&gt;실무에서는 사이트 성격과 실패 비용을 함께 본다. 속보형 뉴스 피드는 짧게, 일반 블로그는 길게 잡는 식이다. 다만 사양 문서만 기준으로 단정할 수 있는 것은 &lt;code&gt;ttl&lt;/code&gt;이 &quot;새로 고침 간격 힌트&quot;라는 점까지다. 따라서 실제 폴링 주기는 서비스 정책으로 분리해 두는 편이 맞다.&lt;/p&gt;

&lt;h2&gt;실전 체크리스트는 어떻게 잡으면 될까?&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;피드 형식이 RSS인지 Atom인지 먼저 구분한다.&lt;/li&gt;
  &lt;li&gt;RSS면 &lt;code&gt;guid&lt;/code&gt;, Atom이면 &lt;code&gt;id&lt;/code&gt;를 우선 식별자로 저장한다.&lt;/li&gt;
  &lt;li&gt;날짜 필드는 중복 판정이 아니라 정렬과 갱신 감지 보조 지표로 본다.&lt;/li&gt;
  &lt;li&gt;피드 본문 필드가 불완전할 수 있으므로 상세 HTML 수집 단계를 분리한다.&lt;/li&gt;
  &lt;li&gt;RSS의 &lt;code&gt;ttl&lt;/code&gt; 같은 힌트는 참고하되 폴링 정책을 별도 설정으로 둔다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위 항목은 모두 RSS 2.0 사양과 RFC 4287에 나온 구조에서 직접 이어지는 운영 기준이다. 별도 플랫폼 관행을 가정하지 않아도 적용할 수 있다.&lt;/p&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. RSS에는 &lt;code&gt;guid&lt;/code&gt;가 항상 있을까?&lt;/h3&gt;
&lt;p&gt;아니다. RSS 2.0 사양에서 &lt;code&gt;guid&lt;/code&gt;는 선택 요소다. 있으면 새 항목 판정에 사용할 수 있지만, 없을 가능성도 고려해야 한다.&lt;/p&gt;

&lt;h3&gt;Q. Atom의 &lt;code&gt;updated&lt;/code&gt;만 보고 새 글 여부를 판단해도 될까?&lt;/h3&gt;
&lt;p&gt;권장하기 어렵다. &lt;code&gt;updated&lt;/code&gt;는 갱신 시각을 보여 주지만, 항목 자체를 식별하는 값은 &lt;code&gt;id&lt;/code&gt;다. 중복 제거는 식별자를 우선하고, 시간은 상태 변화 판단에 보조적으로 쓰는 편이 안전하다.&lt;/p&gt;

&lt;h3&gt;Q. 피드가 있으면 목록 페이지 크롤링은 완전히 필요 없어질까?&lt;/h3&gt;
&lt;p&gt;항상 그렇지는 않다. 피드는 새 항목 발견에 강하지만, 본문 전체나 추가 메타데이터는 상세 HTML에서만 안정적으로 제공될 수 있다. 따라서 피드와 HTML 수집을 역할별로 나누는 방식이 보편적이다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;RSS와 Atom은 둘 다 새 글 탐지에 유용하지만, 사양상 필수 식별자와 갱신 정보는 Atom 쪽이 더 엄격하다. 반면 RSS는 널리 배포된 형식이라 현장에서 자주 만난다. 2026년 6월 24일 확인 기준으로, 수집기 입장에서는 RSS의 &lt;code&gt;guid&lt;/code&gt;와 Atom의 &lt;code&gt;id&lt;/code&gt;를 핵심 중복 키로 삼고 날짜 필드는 보조 지표로 두는 구성이 가장 안정적이다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.rssboard.org/rss-specification&quot; rel=&quot;noopener noreferrer&quot;&gt;RSS Advisory Board: RSS 2.0 Specification&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc4287.html&quot; rel=&quot;noopener noreferrer&quot;&gt;RFC 4287: The Atom Syndication Format&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://validator.w3.org/feed/docs/rss2.html&quot; rel=&quot;noopener noreferrer&quot;&gt;W3C Feed Validation Service: RSS 2.0 Quick Reference&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>시리즈물/데이터 수집을 위한 크롤링</category>
      <category>ATOM</category>
      <category>crawling</category>
      <category>Feed Parser</category>
      <category>GUID</category>
      <category>RSS</category>
      <category>Web Feed</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/624</guid>
      <comments>https://jcdgods.tistory.com/624#entry624comment</comments>
      <pubDate>Wed, 24 Jun 2026 11:20:54 +0900</pubDate>
    </item>
    <item>
      <title>Content-Disposition 헤더 사용 기준 정리</title>
      <link>https://jcdgods.tistory.com/623</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSilMV/dJMcahx7zEh/fBWYc7kkDRkUlrbgBQVOP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSilMV/dJMcahx7zEh/fBWYc7kkDRkUlrbgBQVOP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSilMV/dJMcahx7zEh/fBWYc7kkDRkUlrbgBQVOP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSilMV%2FdJMcahx7zEh%2FfBWYc7kkDRkUlrbgBQVOP1%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Content-Disposition 헤더 사용 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;Content-Disposition&lt;/code&gt;은 브라우저가 응답 본문을 화면에 그대로 표시할지, 다운로드 대상으로 다룰지를 정하는 HTTP 헤더다. 2026년 6월 23일 확인 기준 MDN, RFC 6266, RFC 7578을 보면 이 헤더는 &lt;code&gt;inline&lt;/code&gt;, &lt;code&gt;attachment&lt;/code&gt;, &lt;code&gt;filename&lt;/code&gt;, &lt;code&gt;filename*&lt;/code&gt; 같은 지시어로 동작이 갈린다.&lt;/p&gt;
&lt;p&gt;실무에서 자주 헷갈리는 지점은 세 가지다. 파일 다운로드를 강제하려면 어떤 값을 써야 하는지, 한글이나 공백이 있는 파일명은 어떻게 보내야 하는지, 그리고 업로드용 &lt;code&gt;multipart/form-data&lt;/code&gt; 파트에서도 같은 규칙이 그대로 적용되는지다. 이 글은 공식 문서 기준으로 그 차이를 짧게 정리한다.&lt;/p&gt;

&lt;h2&gt;Content-Disposition은 무엇을 제어할까?&lt;/h2&gt;
&lt;p&gt;MDN은 &lt;code&gt;Content-Disposition&lt;/code&gt;을 응답 콘텐츠를 웹페이지로 표시할지, 로컬에 저장할 첨부 파일로 다룰지 나타내는 헤더로 설명한다. 가장 흔한 값은 &lt;code&gt;inline&lt;/code&gt;과 &lt;code&gt;attachment&lt;/code&gt;다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;inline&lt;/code&gt;: 브라우저가 가능한 경우 화면 안에서 바로 표시한다.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;attachment&lt;/code&gt;: 브라우저가 보통 다운로드 동작으로 처리하고, 저장 대화상자에 파일명을 제안할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;다만 실제 표시 가능 여부는 &lt;code&gt;Content-Type&lt;/code&gt;과 브라우저 지원에도 영향을 받는다. 예를 들어 브라우저가 렌더링 가능한 PDF나 이미지라면 &lt;code&gt;inline&lt;/code&gt;일 때 화면에서 열릴 수 있고, 그렇지 않으면 다운로드될 수 있다.&lt;/p&gt;

&lt;h2&gt;다운로드 강제에는 무엇을 쓰면 될까?&lt;/h2&gt;
&lt;p&gt;브라우저에 저장 동작을 유도하려면 일반적으로 &lt;code&gt;Content-Disposition: attachment&lt;/code&gt;를 사용한다. MDN과 RFC 6266 예시는 이 값이 &quot;Save As&quot; 동작을 유도하는 대표 형태라고 설명한다.&lt;/p&gt;
&lt;p&gt;반대로 헤더를 아예 보내지 않거나 &lt;code&gt;inline&lt;/code&gt;을 쓰면 브라우저는 해당 콘텐츠를 가능한 범위에서 직접 표시하려고 시도한다. 따라서 문서 보기와 다운로드를 확실히 구분하고 싶다면 응답마다 의도를 명시하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;filename과 filename*은 어떻게 다른가?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;filename&lt;/code&gt;은 저장 제안 파일명을 전달하는 오래된 형태이고, &lt;code&gt;filename*&lt;/code&gt;은 RFC 5987 인코딩을 이용해 비ASCII 문자를 표현할 수 있는 확장 형태다. MDN은 두 값이 함께 있으면 이를 이해하는 브라우저에서는 &lt;code&gt;filename*&lt;/code&gt;이 우선된다고 설명한다.&lt;/p&gt;
&lt;p&gt;RFC 6266도 같은 방향의 예시를 제공한다. 즉 한글, 일본어, 유로 기호처럼 ASCII 밖의 문자가 들어갈 수 있다면 &lt;code&gt;filename*&lt;/code&gt;을 포함하고, 호환성을 위해 ASCII 대체 문자열을 &lt;code&gt;filename&lt;/code&gt;에 함께 두는 구성이 가장 무난하다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Content-Disposition: attachment;
 filename=&quot;report-q2.pdf&quot;;
 filename*=UTF-8''report-%ED%95%9C%EA%B8%80.pdf&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;MDN은 &lt;code&gt;filename&lt;/code&gt; 쪽의 퍼센트 인코딩은 브라우저별 처리 차이가 있을 수 있다고 설명한다. 따라서 비ASCII 파일명을 안정적으로 다루려면 &lt;code&gt;filename*&lt;/code&gt;을 기준으로 보고, &lt;code&gt;filename&lt;/code&gt;은 ASCII fallback으로 두는 편이 낫다.&lt;/p&gt;

&lt;h2&gt;공백, 경로 구분자, 특수문자는 어떻게 봐야 할까?&lt;/h2&gt;
&lt;p&gt;MDN은 공백처럼 특수 문자가 들어가면 &lt;code&gt;filename&lt;/code&gt; 값을 따옴표로 감싸는 편이 안전하다고 설명한다. 또한 브라우저는 파일 시스템 제약에 맞추기 위해 &lt;code&gt;/&lt;/code&gt;나 &lt;code&gt;\&lt;/code&gt; 같은 경로 구분자를 밑줄 등으로 바꿀 수 있다.&lt;/p&gt;
&lt;p&gt;RFC 6266과 RFC 7578은 수신 측이 서버가 보낸 파일명을 그대로 신뢰하면 안 된다고 본다. 경로 정보 제거, 기존 파일 덮어쓰기 방지, 운영체제에서 위험한 특수 파일명 회피 같은 방어가 필요하다는 뜻이다.&lt;/p&gt;

&lt;h2&gt;multipart/form-data에서도 filename*을 쓰면 될까?&lt;/h2&gt;
&lt;p&gt;여기서 규칙이 갈린다. 일반 HTTP 응답 헤더에서는 &lt;code&gt;filename*&lt;/code&gt;을 사용할 수 있지만, 업로드용 &lt;code&gt;multipart/form-data&lt;/code&gt; 각 파트의 &lt;code&gt;Content-Disposition&lt;/code&gt;에서는 RFC 7578이 &lt;code&gt;filename*&lt;/code&gt;을 쓰지 말아야 한다고 명시한다.&lt;/p&gt;
&lt;p&gt;RFC 7578 4.2절은 파일 파트에서 &lt;code&gt;filename&lt;/code&gt; 사용을 설명하고, 이어서 RFC 5987 방식의 &lt;code&gt;filename*&lt;/code&gt; 파라미터는 &lt;code&gt;multipart/form-data&lt;/code&gt; 안에서는 사용하면 안 된다고 적고 있다. 즉 다운로드 응답과 업로드 폼 파트는 같은 헤더 이름을 쓰더라도 세부 규칙이 완전히 같지 않다.&lt;/p&gt;

&lt;h2&gt;download 속성과 헤더가 충돌하면 무엇이 우선일까?&lt;/h2&gt;
&lt;p&gt;MDN은 same-origin URL에 대해서는 Chrome과 Firefox 82 이상에서 &lt;code&gt;&amp;lt;a download&amp;gt;&lt;/code&gt; 속성이 &lt;code&gt;Content-Disposition: inline&lt;/code&gt;보다 우선할 수 있다고 설명한다. 따라서 브라우저 내 링크 클릭 동작과 직접 응답 열기 동작이 항상 같다고 보면 안 된다.&lt;/p&gt;
&lt;p&gt;운영 기준으로는 서버 헤더만 보고 판단하지 말고, 실제 다운로드 링크를 &lt;code&gt;&amp;lt;a download&amp;gt;&lt;/code&gt;로 제공하는지까지 함께 확인하는 편이 정확하다.&lt;/p&gt;

&lt;h2&gt;어떤 기준으로 선택하면 될까?&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;브라우저에서 바로 보여도 되는 문서나 미디어라면 &lt;code&gt;inline&lt;/code&gt; 또는 헤더 생략을 검토할 수 있다.&lt;/li&gt;
  &lt;li&gt;저장 동작을 명확히 유도해야 하면 &lt;code&gt;attachment&lt;/code&gt;를 우선 검토한다.&lt;/li&gt;
  &lt;li&gt;비ASCII 파일명이 필요하면 &lt;code&gt;filename*&lt;/code&gt;을 포함하고, 호환용 ASCII &lt;code&gt;filename&lt;/code&gt;을 함께 둔다.&lt;/li&gt;
  &lt;li&gt;업로드 폼 파트에서는 응답 헤더 규칙을 그대로 복사하지 말고 RFC 7578 제약을 따로 확인한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;마지막 항목은 문서 차이에서 직접 나오는 운영 기준이다. 특히 다운로드 응답과 업로드 파서 구현을 같은 코드 경로에서 처리할 때 혼동이 많이 생긴다.&lt;/p&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. attachment를 쓰면 모든 브라우저에서 반드시 다운로드만 될까?&lt;/h3&gt;
&lt;p&gt;공식 문서는 일반적으로 다운로드 동작을 유도한다고 설명하지만, 실제 사용자 경험은 브라우저 UI와 플랫폼 정책의 영향을 받을 수 있다. 다만 서버 의도를 가장 분명하게 표현하는 값이 &lt;code&gt;attachment&lt;/code&gt;라는 점은 문서상 일관된다.&lt;/p&gt;

&lt;h3&gt;Q. filename만 보내도 한글 파일명이 항상 안전할까?&lt;/h3&gt;
&lt;p&gt;아니다. MDN은 &lt;code&gt;filename&lt;/code&gt;과 &lt;code&gt;filename*&lt;/code&gt;의 처리 차이를 설명하고, 최대 호환성을 위해 둘을 함께 두는 방식을 권장한다. 비ASCII 문자를 안정적으로 표현하려면 &lt;code&gt;filename*&lt;/code&gt;이 핵심이다.&lt;/p&gt;

&lt;h3&gt;Q. multipart/form-data 업로드에서도 filename*을 받도록 구현해도 될까?&lt;/h3&gt;
&lt;p&gt;현실 구현체는 다양한 입력을 보낼 수 있지만, 표준 문서 기준으로는 &lt;code&gt;multipart/form-data&lt;/code&gt; 안의 &lt;code&gt;filename*&lt;/code&gt;은 사용하지 않아야 한다. 범용 파서를 만든다면 허용 여부와 별개로 비표준 입력 처리 정책을 분리해서 두는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Content-Disposition&lt;/code&gt;은 단순히 다운로드 여부만 정하는 헤더가 아니라, 파일명 인코딩과 브라우저 동작 차이까지 함께 다뤄야 하는 헤더다. 2026년 6월 23일 확인 기준으로 일반 응답에서는 &lt;code&gt;attachment&lt;/code&gt;와 &lt;code&gt;filename*&lt;/code&gt; 조합이 자주 기준점이 되고, &lt;code&gt;multipart/form-data&lt;/code&gt;에서는 RFC 7578 제약을 별도로 봐야 한다.&lt;/p&gt;
&lt;p&gt;실무에서는 다운로드 응답, HTML 링크, 업로드 파트 파싱을 한 번에 묶어 생각하지 않는 것이 중요하다. 같은 이름의 헤더라도 맥락에 따라 허용 파라미터와 호환성 포인트가 다르기 때문이다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Disposition&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN: Content-Disposition header&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc6266&quot; rel=&quot;noopener noreferrer&quot;&gt;RFC 6266: Use of the Content-Disposition Header Field in HTTP&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7578.html&quot; rel=&quot;noopener noreferrer&quot;&gt;RFC 7578: multipart/form-data&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/HTML, Javascript, CSS</category>
      <category>Content-Disposition</category>
      <category>File Download</category>
      <category>filename*</category>
      <category>HTTP Header</category>
      <category>multipart/form-data</category>
      <category>web</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/623</guid>
      <comments>https://jcdgods.tistory.com/623#entry623comment</comments>
      <pubDate>Tue, 23 Jun 2026 11:28:27 +0900</pubDate>
    </item>
    <item>
      <title>systemd Type=notify 기준 정리</title>
      <link>https://jcdgods.tistory.com/622</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bv7Szs/dJMcadoRduv/hU2oTUMci79jlckS9rTpF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bv7Szs/dJMcadoRduv/hU2oTUMci79jlckS9rTpF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bv7Szs/dJMcadoRduv/hU2oTUMci79jlckS9rTpF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbv7Szs%2FdJMcadoRduv%2FhU2oTUMci79jlckS9rTpF1%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;systemd Type=notify 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;Type=notify&lt;/code&gt;는 서비스가 실제로 준비를 마친 시점에 &lt;code&gt;sd_notify()&lt;/code&gt;로 &lt;code&gt;READY=1&lt;/code&gt;을 보내고, 그때 systemd가 후속 유닛 시작을 진행하게 만드는 방식이다. 2026년 6월 22일 기준 upstream systemd 공식 문서를 보면 장기 실행 서비스의 기본값으로는 대체로 &lt;code&gt;Type=exec&lt;/code&gt;가 권장되지만, 서비스 코드가 자신의 준비 완료 시점을 정확히 알고 있고 그 시점을 systemd에 알려야 한다면 &lt;code&gt;Type=notify&lt;/code&gt;가 더 맞다.&lt;/p&gt;
&lt;p&gt;실무에서 자주 헷갈리는 지점은 두 가지다. 단순히 프로세스 실행 성공만 확인하면 되는지, 아니면 포트 바인딩이나 내부 초기화까지 끝난 뒤에 다른 유닛이 따라오게 해야 하는지다. 이 글은 &lt;code&gt;Type=notify&lt;/code&gt;, &lt;code&gt;NotifyAccess=&lt;/code&gt;, &lt;code&gt;sd_notify()&lt;/code&gt;의 의미와 무난한 선택 기준만 정리한다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;Type=notify&lt;/code&gt;는 무엇을 바꿀까?&lt;/h2&gt;
&lt;p&gt;upstream &lt;code&gt;systemd.service&lt;/code&gt; 문서에 따르면 &lt;code&gt;Type=notify&lt;/code&gt;는 동작 방식이 &lt;code&gt;Type=exec&lt;/code&gt;와 비슷하지만, 서비스가 시작 완료를 알리는 &lt;code&gt;READY=1&lt;/code&gt; 메시지를 보낼 때까지 systemd가 follow-up unit 시작을 미룬다. 반대로 &lt;code&gt;Type=exec&lt;/code&gt;는 서비스 바이너리의 &lt;code&gt;execve()&lt;/code&gt; 성공까지만 확인하고 다음 작업으로 넘어간다.&lt;/p&gt;
&lt;p&gt;이 차이는 의존 관계가 있는 서비스에서 중요하다. 예를 들어 데몬이 프로세스는 떴지만 실제로는 포트 바인딩, 캐시 로딩, 초기 동기화가 끝나야 요청을 받을 수 있다면 &lt;code&gt;Type=exec&lt;/code&gt;만으로는 너무 이르게 시작 성공으로 판단될 수 있다.&lt;/p&gt;

&lt;h2&gt;먼저 어떤 타입을 고르면 될까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;타입&lt;/th&gt;
      &lt;th&gt;systemd가 시작 완료로 보는 시점&lt;/th&gt;
      &lt;th&gt;우선 고려할 상황&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;simple&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;fork 직후&lt;/td&gt;
      &lt;td&gt;대기 없이 바로 다음 작업을 진행해도 되는 매우 단순한 서비스&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;exec&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;execve()&lt;/code&gt; 성공 직후&lt;/td&gt;
      &lt;td&gt;장기 실행 서비스의 기본 출발점&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;notify&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;서비스가 &lt;code&gt;READY=1&lt;/code&gt;을 보낸 뒤&lt;/td&gt;
      &lt;td&gt;서비스 코드가 준비 완료 시점을 직접 알려야 할 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;forking&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;부모 프로세스 종료 뒤&lt;/td&gt;
      &lt;td&gt;기존 데몬화 방식을 유지해야 하는 레거시 서비스&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;공식 문서는 장기 실행 서비스에 대해 &lt;code&gt;Type=exec&lt;/code&gt;를 권장 선택지로 설명한다. 서비스 실행 파일이 없거나 &lt;code&gt;User=&lt;/code&gt; 같은 실행 준비 단계에서 실패했을 때 이를 제대로 추적하기 쉽기 때문이다. 다만 서비스 코드 내부 초기화 완료 시점에 맞춰 정확히 순서를 제어해야 하면 &lt;code&gt;notify&lt;/code&gt;, D-Bus 이름 획득이 준비 완료 조건이면 &lt;code&gt;dbus&lt;/code&gt;가 더 적합하다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;Type=notify&lt;/code&gt;를 고를 만한 기준은 무엇일까?&lt;/h2&gt;
&lt;p&gt;첫째, 후속 유닛이나 클라이언트가 &quot;프로세스가 떴다&quot;가 아니라 &quot;서비스가 실제로 준비됐다&quot;는 신호를 필요로 할 때다. 공식 문서 기준 &lt;code&gt;notify&lt;/code&gt;는 서비스 코드가 정확히 언제 준비 완료로 볼지 결정할 수 있게 해 준다.&lt;/p&gt;
&lt;p&gt;둘째, 서비스가 reload 완료나 watchdog keep-alive까지 systemd와 같은 채널로 통신해야 할 때다. upstream 문서는 &lt;code&gt;notify-reload&lt;/code&gt;와 &lt;code&gt;WatchdogSec=&lt;/code&gt; 모두 notification socket 사용을 전제로 설명한다.&lt;/p&gt;
&lt;p&gt;반대로 서비스 코드에 &lt;code&gt;sd_notify()&lt;/code&gt; 지원이 없고, 준비 완료 시점을 외부에서 확실히 알 수 없다면 &lt;code&gt;Type=notify&lt;/code&gt;를 억지로 고르는 것이 맞지 않다. 이런 경우에는 &lt;code&gt;Type=exec&lt;/code&gt;를 쓰거나, 레거시 데몬이면 &lt;code&gt;forking&lt;/code&gt;을 유지하는 편이 더 단순하다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;NotifyAccess=&lt;/code&gt;는 왜 같이 봐야 할까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Type=notify&lt;/code&gt;는 notification socket에 메시지를 보내는 방식이므로, 어떤 프로세스의 메시지를 systemd가 신뢰할지 정하는 &lt;code&gt;NotifyAccess=&lt;/code&gt;를 같이 봐야 한다. 공식 문서에 따르면 값은 &lt;code&gt;none&lt;/code&gt;, &lt;code&gt;main&lt;/code&gt;, &lt;code&gt;exec&lt;/code&gt;, &lt;code&gt;all&lt;/code&gt; 중 하나다.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;값&lt;/th&gt;
      &lt;th&gt;허용 범위&lt;/th&gt;
      &lt;th&gt;무난한 사용 상황&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;main&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;메인 프로세스만 허용&lt;/td&gt;
      &lt;td&gt;주 프로세스가 직접 &lt;code&gt;READY=1&lt;/code&gt;을 보내는 일반적인 경우&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;exec&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;Exec*=&lt;/code&gt;로 시작된 main/control 프로세스 허용&lt;/td&gt;
      &lt;td&gt;control process도 상태 통지를 해야 할 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;all&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;같은 cgroup의 모든 프로세스 허용&lt;/td&gt;
      &lt;td&gt;보조 프로세스가 통지를 보내야 하지만 구조를 바꾸기 어려울 때&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;중요한 점은 공식 문서가 &lt;code&gt;Type=notify&lt;/code&gt; 또는 &lt;code&gt;WatchdogSec=&lt;/code&gt;를 사용할 때 &lt;code&gt;NotifyAccess=&lt;/code&gt;를 열어 두라고 설명한다는 것이다. 값을 따로 적지 않거나 &lt;code&gt;none&lt;/code&gt;이면 systemd가 강제로 &lt;code&gt;main&lt;/code&gt;으로 바꾼다.&lt;/p&gt;
&lt;p&gt;또한 &lt;code&gt;sd_notify&lt;/code&gt; 문서는 보조 프로세스가 메시지를 보낸 직후 바로 종료하면, 심지어 &lt;code&gt;NotifyAccess=all&lt;/code&gt;이어도 systemd가 해당 메시지를 유닛에 제대로 귀속시키지 못해 무시할 수 있다고 설명한다. 따라서 메인 프로세스나 systemd가 직접 시작한 프로세스가 통지하도록 설계하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;sd_notify()&lt;/code&gt;로 무엇을 보낼 수 있을까?&lt;/h2&gt;
&lt;p&gt;가장 기본적인 메시지는 &lt;code&gt;READY=1&lt;/code&gt;이다. upstream &lt;code&gt;sd_notify&lt;/code&gt; 문서에 따르면 이 값은 서비스 시작 완료 또는 reload 완료를 알릴 때 쓰이며, &lt;code&gt;READY=0&lt;/code&gt; 같은 값은 정의돼 있지 않다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;STATUS=...&lt;/code&gt;를 함께 보내면 &lt;code&gt;systemctl status&lt;/code&gt; 같은 곳에 사람이 읽을 수 있는 상태 문자열을 남길 수 있다. reload를 추적해야 하면 &lt;code&gt;Type=notify-reload&lt;/code&gt;와 함께 &lt;code&gt;RELOADING=1&lt;/code&gt;, &lt;code&gt;MONOTONIC_USEC=...&lt;/code&gt;, 그리고 reload 완료 후 다시 &lt;code&gt;READY=1&lt;/code&gt;을 보내는 흐름을 쓴다.&lt;/p&gt;
&lt;p&gt;watchdog를 켠 서비스는 주기적으로 &lt;code&gt;WATCHDOG=1&lt;/code&gt;을 보내 keep-alive를 갱신한다. 서비스가 내부 오류를 감지했고 watchdog 동작으로 처리하고 싶다면 &lt;code&gt;WATCHDOG=trigger&lt;/code&gt;도 보낼 수 있다.&lt;/p&gt;

&lt;h2&gt;운영에서 무난한 선택 기준은 어떻게 잡을까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;무난한 선택&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;실행 파일 존재와 권한 문제만 잡으면 되는 일반 데몬&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;Type=exec&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;실행 준비 실패를 명확히 잡고 설정이 단순함&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;포트 바인딩, 캐시 준비, 초기 동기화 이후에만 준비 완료로 봐야 하는 데몬&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;Type=notify&lt;/code&gt; + &lt;code&gt;NotifyAccess=main&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;서비스 코드가 정확한 readiness 시점을 통지할 수 있음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;reload 완료까지 systemd가 추적해야 하는 서비스&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;Type=notify-reload&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;reload 시작과 완료를 상태 메시지로 표현 가능&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;기존 daemonize 방식과 PID 파일 의존성이 남아 있는 서비스&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;Type=forking&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;레거시 호환 목적, 다만 신규 설계의 기본값으로 보기는 어려움&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;핵심은 &lt;code&gt;Type=notify&lt;/code&gt;가 더 &quot;좋은&quot; 타입이라기보다, readiness를 서비스가 스스로 선언해야 할 때 필요한 타입이라는 점이다. 준비 완료의 기준을 코드 안에서 정의하지 못한다면 오히려 운영 복잡도만 늘어난다.&lt;/p&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;Q. 장기 실행 서비스면 항상 &lt;code&gt;Type=notify&lt;/code&gt;가 더 나을까?&lt;/h3&gt;
&lt;p&gt;그렇게 보기 어렵다. 공식 문서는 장기 실행 서비스의 일반적인 권장값으로 &lt;code&gt;Type=exec&lt;/code&gt;를 설명한다. &lt;code&gt;notify&lt;/code&gt;는 readiness를 코드가 정확히 선언할 필요가 있을 때 선택하는 쪽에 가깝다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;NotifyAccess=all&lt;/code&gt;로 두면 가장 안전할까?&lt;/h3&gt;
&lt;p&gt;항상 그렇지는 않다. 허용 범위를 넓히는 대신 어떤 프로세스가 상태 통지를 보냈는지 추적이 어려워질 수 있고, 보조 프로세스가 바로 종료하면 귀속 race가 남을 수 있다. 메인 프로세스가 직접 보내는 구조라면 &lt;code&gt;main&lt;/code&gt;이 더 단순하다.&lt;/p&gt;

&lt;h3&gt;Q. reload도 같은 방식으로 처리할 수 있을까?&lt;/h3&gt;
&lt;p&gt;가능하다. 다만 공식 문서는 reload 추적이 필요하면 일반 &lt;code&gt;notify&lt;/code&gt;보다 &lt;code&gt;notify-reload&lt;/code&gt;를 권장한다. 이 경우 reload 시작 시 &lt;code&gt;RELOADING=1&lt;/code&gt;과 &lt;code&gt;MONOTONIC_USEC=...&lt;/code&gt;, 완료 시 &lt;code&gt;READY=1&lt;/code&gt;을 보내야 한다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Type=exec&lt;/code&gt;는 실행 준비 단계의 오류를 정확히 잡는 기본값이고, &lt;code&gt;Type=notify&lt;/code&gt;는 서비스 코드가 readiness를 선언해야 할 때 쓰는 선택지다. 대부분의 서비스는 먼저 &lt;code&gt;exec&lt;/code&gt;를 기준으로 생각하고, 실제 준비 완료 시점을 외부에 알려야 할 이유가 분명할 때만 &lt;code&gt;notify&lt;/code&gt;로 올리는 편이 안전하다.&lt;/p&gt;
&lt;p&gt;2026년 6월 22일 기준 upstream systemd 공식 문서를 기준으로 보면, &lt;code&gt;Type=notify&lt;/code&gt;를 도입할 때 같이 확인해야 할 핵심은 세 가지다. &lt;code&gt;READY=1&lt;/code&gt;을 언제 보낼지, 누가 보낼지에 맞는 &lt;code&gt;NotifyAccess=&lt;/code&gt; 값이 무엇인지, 그리고 reload나 watchdog까지 같은 채널로 다룰지다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://raw.githubusercontent.com/systemd/systemd/main/man/systemd.service.xml&quot; rel=&quot;noopener noreferrer&quot;&gt;systemd 공식 문서 원본: systemd.service&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://raw.githubusercontent.com/systemd/systemd/main/man/sd_notify.xml&quot; rel=&quot;noopener noreferrer&quot;&gt;systemd 공식 문서 원본: sd_notify&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/운영체제</category>
      <category>Linux</category>
      <category>NotifyAccess</category>
      <category>READY=1</category>
      <category>sd_notify</category>
      <category>systemd</category>
      <category>Type=notify</category>
      <category>운영체제</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/622</guid>
      <comments>https://jcdgods.tistory.com/622#entry622comment</comments>
      <pubDate>Mon, 22 Jun 2026 12:32:06 +0900</pubDate>
    </item>
    <item>
      <title>토허제 현황 사이트 사용 기준</title>
      <link>https://jcdgods.tistory.com/621</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cN7szV/dJMcagzaoWn/Kic7ncfgZ5rYPDOr6M1SG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cN7szV/dJMcagzaoWn/Kic7ncfgZ5rYPDOr6M1SG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cN7szV/dJMcagzaoWn/Kic7ncfgZ5rYPDOr6M1SG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcN7szV%2FdJMcagzaoWn%2FKic7ncfgZ5rYPDOr6M1SG1%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;토허제 현황 사이트 사용 기준&lt;/h1&gt;
&lt;p&gt;토지거래계약허가 공개 민원은 지자체별로 흩어져 있고, 접수량이나 처리 상태를 비교하려면 여러 화면을 오가야 한다. &lt;a href=&quot;https://land.awenify.com/&quot; rel=&quot;noopener noreferrer&quot;&gt;land.awenify.com&lt;/a&gt;은 이 흐름을 한 화면에서 확인할 수 있게 만든 토허제 처리 현황 사이트다.&lt;/p&gt;
&lt;p&gt;홍보 문구로만 보면 &quot;지역별 현황을 모아 둔 사이트&quot;라고 말할 수 있지만, 실제로 쓸 때 중요한 지점은 따로 있다. 특정 지역의 처리중 민원을 바로 보는 기능보다, 접수량 추이와 최근 변화 폭을 같이 보면서 어느 지역을 먼저 확인할지 정할 수 있다는 점이다.&lt;/p&gt;

&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7r5bV/dJMcagzaoWp/ZcFAvjpU0rZ20zLNFgLjr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7r5bV/dJMcagzaoWp/ZcFAvjpU0rZ20zLNFgLjr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7r5bV/dJMcagzaoWp/ZcFAvjpU0rZ20zLNFgLjr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7r5bV%2FdJMcagzaoWp%2FZcFAvjpU0rZ20zLNFgLjr0%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;

&lt;h2&gt;첫 화면에서 무엇을 먼저 봐야 할까?&lt;/h2&gt;
&lt;p&gt;홈페이지는 서울 주요 구와 경기 일부 지역을 카드 형태로 보여준다. 각 카드에는 최근 접수량, 처리중 건수, 평균 처리 완료 관측일 같은 값이 같이 표시된다. 그래서 특정 구 이름을 알고 들어가는 경우뿐 아니라, 어느 지역의 접수 흐름이 큰지 먼저 훑어보는 용도로도 쓸 수 있다.&lt;/p&gt;
&lt;p&gt;이런 사이트에서 첫 화면의 역할은 검색창보다 중요하다. 토허제 관련 민원은 한 지역만 고정해서 보는 경우도 있지만, 실제 관심은 &quot;최근에 어디가 늘었는가&quot;, &quot;처리중 건수가 어느 정도인가&quot;, &quot;서울과 다른 경기 지역을 같은 기준으로 볼 수 있는가&quot;에 가까운 경우가 많다. 홈페이지 카드가 이 판단의 시작점이 된다.&lt;/p&gt;

&lt;h2&gt;접수량 추이는 전체보다 선택 비교가 유용하다&lt;/h2&gt;
&lt;p&gt;접수량 추이 그래프는 전체, 지역별, 구별 모드를 제공한다. 전체 그래프는 서비스 규모를 보는 데 적합하고, 지역별 또는 구별 모드는 특정 지역끼리만 비교할 때 적합하다. 특히 체크박스로 항목을 선택할 수 있어서 서울 전체 구를 모두 띄워 놓는 대신 관심 있는 구만 남길 수 있다.&lt;/p&gt;
&lt;p&gt;기간도 1주일, 1달, 3달, 6개월, 1년, 전체기간으로 나뉜다. 단기적인 접수 급증은 1달이나 3달에서 더 잘 보이고, 제도 변화나 시장 관심이 길게 이어졌는지는 6개월 이상으로 보는 편이 낫다.&lt;/p&gt;

&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPsbNS/dJMcagzaoWs/KPVH0mxmhqTy2WyEpvKmF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPsbNS/dJMcagzaoWs/KPVH0mxmhqTy2WyEpvKmF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPsbNS/dJMcagzaoWs/KPVH0mxmhqTy2WyEpvKmF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPsbNS%2FdJMcagzaoWs%2FKPVH0mxmhqTy2WyEpvKmF1%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;

&lt;h2&gt;그래프 팝업은 날짜 단위 확인에 맞다&lt;/h2&gt;
&lt;p&gt;그래프 위 특정 날짜에 마우스를 올리면 해당 날짜의 접수량이 팝업으로 표시된다. 모바일에서는 터치로 같은 정보를 확인할 수 있다. 이 기능은 단순한 장식이 아니라, 날짜 축에서 눈에 띄는 지점이 있을 때 숫자를 바로 확인하는 용도다.&lt;/p&gt;
&lt;p&gt;접수량 그래프는 선이 많아지면 대략적인 흐름은 보이지만 정확한 수치는 읽기 어렵다. 이때 팝업이 있으면 특정 날짜의 지역별 수치를 다시 표로 찾지 않아도 된다. 글이나 보고서에 &quot;이 시점에 접수가 늘었다&quot;라고 언급하려면 이런 날짜 단위 확인이 필요하다.&lt;/p&gt;

&lt;h2&gt;최근 4주 변화는 우선순위를 정하는 데 쓸 수 있다&lt;/h2&gt;
&lt;p&gt;홈페이지 아래에는 최근 4주와 직전 4주를 비교한 구별 접수 변화가 나온다. 증가 폭 상위 3개구와 감소 폭 상위 3개구를 먼저 보여주고, 전체 보기를 누르면 모든 구가 증가폭 큰 순에서 감소폭 큰 순으로 정렬된다.&lt;/p&gt;
&lt;p&gt;이 기능은 단순 순위표라기보다 확인 우선순위를 정하는 데 가깝다. 접수량이 많이 늘어난 구는 최근 관심이나 민원 흐름이 커졌을 가능성이 있고, 크게 줄어든 구는 반대로 변동성이 잦아든 지역일 수 있다. 어느 쪽이든 개별 지역 페이지로 들어가 더 볼 이유가 생긴다.&lt;/p&gt;

&lt;h2&gt;지역 상세 페이지는 처리중 민원 확인에 맞춰져 있다&lt;/h2&gt;
&lt;p&gt;개별 지역 페이지로 들어가면 조회 기간, 구 필터, 상태 필터를 바꿔 볼 수 있다. 요약 지표에는 전체, 처리중, 해결, 평균 처리 완료 관측일이 표시되고, 아래에는 일별 접수와 처리 상태 그래프가 나온다.&lt;/p&gt;
&lt;p&gt;처리중 민원 표는 처음부터 모든 데이터를 한 번에 보여주지 않고 50건씩 로딩한다. 처리중 건수가 많은 지역에서는 이 방식이 중요하다. 초기 화면이 가볍게 뜨고, 더 필요한 경우 하단의 더 보기 버튼으로 이어서 확인할 수 있기 때문이다.&lt;/p&gt;

&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRnpYk/dJMb99UlDH8/REq6wYy5JmxxUBNXvZFPgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRnpYk/dJMb99UlDH8/REq6wYy5JmxxUBNXvZFPgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRnpYk/dJMb99UlDH8/REq6wYy5JmxxUBNXvZFPgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRnpYk%2FdJMb99UlDH8%2FREq6wYy5JmxxUBNXvZFPgK%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;

&lt;h2&gt;이 사이트가 특히 맞는 사용 상황&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;보기 좋은 화면&lt;/th&gt;
      &lt;th&gt;확인할 기준&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&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&gt;
      &lt;td&gt;최근 접수량이 많이 변한 구를 찾을 때&lt;/td&gt;
      &lt;td&gt;구별 4주 접수 변화&lt;/td&gt;
      &lt;td&gt;증가 폭, 감소 폭, 전체 순위&lt;/td&gt;
    &lt;/tr&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&gt;
      &lt;td&gt;블로그나 메모에 날짜별 수치를 인용할 때&lt;/td&gt;
      &lt;td&gt;그래프 날짜 팝업&lt;/td&gt;
      &lt;td&gt;선택 날짜의 접수량&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;사용할 때의 기준&lt;/h2&gt;
&lt;p&gt;먼저 홈페이지에서 전체 흐름을 보고, 관심 지역이 생기면 지역 상세 페이지로 들어가는 순서가 가장 자연스럽다. 특정 구만 보고 싶다면 처음부터 구별 그래프에서 체크박스로 항목을 줄이는 편이 낫다. 처리중 민원만 확인할 목적이라면 상세 페이지의 상태 필터와 더 보기 버튼을 중심으로 쓰면 된다.&lt;/p&gt;
&lt;p&gt;이 사이트는 공식 민원 처리 시스템을 대체하는 서비스가 아니다. 공개 민원 데이터를 보기 쉽게 모아 흐름을 파악하는 보조 도구에 가깝다. 따라서 실제 행정 처리의 최종 확인은 각 지자체의 공식 공개 민원 페이지에서 확인하는 것이 맞다. 대신 여러 지역을 비교하고, 최근 흐름을 빠르게 잡는 용도로는 충분히 쓸 만하다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://land.awenify.com/&quot; rel=&quot;noopener noreferrer&quot;&gt;land.awenify.com&lt;/a&gt;은 토허제 처리 현황을 지역 카드, 추이 그래프, 구별 변화표, 처리중 민원 목록으로 나눠 보여준다. 첫 화면에서 전체 흐름을 잡고, 그래프에서 관심 지역을 좁힌 뒤, 상세 페이지에서 처리중 민원을 확인하는 흐름이 가장 효율적이다.&lt;/p&gt;
&lt;p&gt;토지거래계약허가 관련 흐름을 자주 확인해야 한다면 여러 지자체 페이지를 따로 열기 전에 이 사이트에서 우선순위를 정해 보는 것이 좋다. 특히 구별 4주 변화와 50건씩 이어 보는 처리중 민원 목록은 실제 사용에서 체감 차이가 큰 기능이다.&lt;/p&gt;</description>
      <category>프로그래밍/서버, DBMS</category>
      <category>land.awenify.com</category>
      <category>데이터대시보드</category>
      <category>민원현황</category>
      <category>지역별현황</category>
      <category>처리중민원</category>
      <category>토지거래계약허가</category>
      <category>토허제</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/621</guid>
      <comments>https://jcdgods.tistory.com/621#entry621comment</comments>
      <pubDate>Mon, 22 Jun 2026 09:23:20 +0900</pubDate>
    </item>
    <item>
      <title>Fetch Metadata 헤더 기준 정리</title>
      <link>https://jcdgods.tistory.com/620</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwFzs5/dJMcadWJ4dj/sfCj9khtE66T6632q0CSE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwFzs5/dJMcadWJ4dj/sfCj9khtE66T6632q0CSE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwFzs5/dJMcadWJ4dj/sfCj9khtE66T6632q0CSE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwFzs5%2FdJMcadWJ4dj%2FsfCj9khtE66T6632q0CSE1%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Fetch Metadata 헤더 기준 정리&lt;/h1&gt;
&lt;p&gt;다른 사이트에서 보낸 요청을 서버가 구분하지 못하면 CSRF, XSSI, 교차 출처 정보 노출 같은 문제가 생길 수 있다. 2026년 6월 21일 기준 W3C Fetch Metadata Request Headers 작업 초안, MDN, web.dev 문서를 보면 브라우저는 &lt;code&gt;Sec-Fetch-Site&lt;/code&gt;, &lt;code&gt;Sec-Fetch-Mode&lt;/code&gt;, &lt;code&gt;Sec-Fetch-Dest&lt;/code&gt;, &lt;code&gt;Sec-Fetch-User&lt;/code&gt; 헤더로 요청의 맥락을 서버에 전달할 수 있고, 서버는 이 값을 바탕으로 요청을 초기에 차단할 수 있다.&lt;/p&gt;
&lt;p&gt;실무에서 중요한 판단은 세 가지다. 내 서비스가 원래 자기 자신만 불러야 하는 리소스를 많이 갖고 있는지, 서브도메인과 외부 CORS 엔드포인트를 어디까지 신뢰할지, 그리고 정책 적용 전에 예외 경로를 얼마나 정확하게 정리했는지다. 이 글은 Fetch Metadata를 언제 유용하게 쓸 수 있는지와 안전한 기본 허용 기준을 짧게 정리한다.&lt;/p&gt;

&lt;h2&gt;Fetch Metadata 헤더는 무엇을 위한 기능일까?&lt;/h2&gt;
&lt;p&gt;W3C 초안은 Fetch Metadata 요청 헤더를 &quot;요청이 어떤 방식으로 만들어졌고 어떤 맥락에서 쓰일지를 서버가 미리 판단할 수 있게 하는 정보&quot;로 정의한다. 핵심은 애플리케이션 코드가 본격적으로 요청을 처리하기 전에, 프록시나 CDN 단계에서도 의심스러운 교차 사이트 요청을 거를 수 있다는 점이다.&lt;/p&gt;
&lt;p&gt;web.dev는 이를 Resource Isolation Policy라는 방어 기법으로 설명한다. 원래 내 애플리케이션이 자기 자신의 문서와 스크립트 안에서만 불러야 하는 리소스라면, 다른 사이트가 그 리소스를 읽거나 로드하려는 요청을 차단하는 것만으로도 여러 교차 출처 공격의 표면을 줄일 수 있다.&lt;/p&gt;

&lt;h2&gt;각 헤더는 무엇을 알려줄까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;헤더&lt;/th&gt;
      &lt;th&gt;역할&lt;/th&gt;
      &lt;th&gt;대표 값&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;Sec-Fetch-Site&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;요청 시작점과 대상 리소스의 사이트 관계&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;same-origin&lt;/code&gt;, &lt;code&gt;same-site&lt;/code&gt;, &lt;code&gt;cross-site&lt;/code&gt;, &lt;code&gt;none&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;Sec-Fetch-Mode&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;요청 모드&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;navigate&lt;/code&gt;, &lt;code&gt;cors&lt;/code&gt;, &lt;code&gt;no-cors&lt;/code&gt;, &lt;code&gt;same-origin&lt;/code&gt;, &lt;code&gt;websocket&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;Sec-Fetch-Dest&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;리소스 목적지&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;document&lt;/code&gt;, &lt;code&gt;image&lt;/code&gt;, &lt;code&gt;script&lt;/code&gt;, &lt;code&gt;iframe&lt;/code&gt;, &lt;code&gt;empty&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;Sec-Fetch-User&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;사용자 활성화가 있는 탐색인지 여부&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;?1&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;MDN과 W3C 문서를 같이 보면 서버에서 가장 먼저 보는 값은 대개 &lt;code&gt;Sec-Fetch-Site&lt;/code&gt;다. 같은 origin인지, 같은 site인지, 완전히 다른 site인지가 1차 구분선이기 때문이다. 그다음에 &lt;code&gt;Sec-Fetch-Mode&lt;/code&gt;와 &lt;code&gt;Sec-Fetch-Dest&lt;/code&gt;로 이것이 탐색인지, 이미지나 스크립트 같은 하위 리소스 로드인지 세분화하는 흐름이 일반적이다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;same-origin&lt;/code&gt;, &lt;code&gt;same-site&lt;/code&gt;, &lt;code&gt;cross-site&lt;/code&gt;, &lt;code&gt;none&lt;/code&gt;는 어떻게 해석해야 할까?&lt;/h2&gt;
&lt;p&gt;MDN에 따르면 &lt;code&gt;same-origin&lt;/code&gt;은 스킴, 호스트, 포트까지 같은 경우이고, &lt;code&gt;same-site&lt;/code&gt;는 같은 사이트이지만 origin까지 같지는 않을 수 있는 경우다. 예를 들어 &lt;code&gt;app.example.com&lt;/code&gt;과 &lt;code&gt;static.example.com&lt;/code&gt; 사이 요청은 정책에 따라 허용할 수도 있고 더 엄격하게 막을 수도 있다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cross-site&lt;/code&gt;는 다른 사이트에서 온 요청이고, &lt;code&gt;none&lt;/code&gt;은 주소창 입력이나 북마크 클릭처럼 사용자가 브라우저에서 직접 시작한 탐색을 뜻한다. W3C 초안도 직접적인 사용자 상호작용으로 시작된 navigation request에는 &lt;code&gt;Sec-Fetch-Site: none&lt;/code&gt;을 설정하도록 설명한다.&lt;/p&gt;

&lt;h2&gt;어떤 요청을 기본 허용으로 보는 편이 무난할까?&lt;/h2&gt;
&lt;p&gt;web.dev가 제안하는 Resource Isolation Policy의 기본 흐름은 단순하다. 첫째, Fetch Metadata 헤더를 아예 보내지 않는 브라우저 요청은 호환성을 위해 우선 통과시킨다. 둘째, &lt;code&gt;same-origin&lt;/code&gt;, &lt;code&gt;same-site&lt;/code&gt;, &lt;code&gt;none&lt;/code&gt;은 기본 허용한다. 셋째, 단순한 최상위 &lt;code&gt;GET&lt;/code&gt; 탐색은 허용하되 &lt;code&gt;object&lt;/code&gt;와 &lt;code&gt;embed&lt;/code&gt; 같은 목적지는 제외한다. 마지막으로 그 밖의 교차 사이트 비탐색 요청은 거부한다.&lt;/p&gt;
&lt;p&gt;다만 이 기본값이 언제나 맞는 것은 아니다. 서브도메인을 완전히 신뢰하지 않는 서비스라면 &lt;code&gt;same-site&lt;/code&gt;도 막아야 할 수 있다. 반대로 외부에서 접근해야 하는 공개 이미지, CORS API, 결제 콜백 같은 엔드포인트는 경로 단위 예외가 필요하다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;if not sec_fetch_site:
    allow()
elif sec_fetch_site in (&quot;same-origin&quot;, &quot;same-site&quot;, &quot;none&quot;):
    allow()
elif sec_fetch_mode == &quot;navigate&quot; and method == &quot;GET&quot; and sec_fetch_dest not in (&quot;object&quot;, &quot;embed&quot;):
    allow()
elif path in CROSS_SITE_ALLOWED_PATHS:
    allow()
else:
    reject_403()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;왜 &lt;code&gt;Vary&lt;/code&gt; 헤더까지 같이 봐야 할까?&lt;/h2&gt;
&lt;p&gt;W3C 초안의 배포 고려사항은 응답이 Fetch Metadata 헤더 값에 따라 달라진다면 적절한 &lt;code&gt;Vary&lt;/code&gt; 헤더를 넣으라고 명시한다. 예를 들어 어떤 엔드포인트가 &lt;code&gt;Sec-Fetch-Site&lt;/code&gt;에 따라 200과 403을 다르게 반환한다면 캐시가 이 차이를 구분해야 한다.&lt;/p&gt;
&lt;p&gt;web.dev도 특히 GET 요청 보호가 목적이라면 서버의 &lt;code&gt;Vary&lt;/code&gt;에 &lt;code&gt;Sec-Fetch-Dest&lt;/code&gt;, &lt;code&gt;Sec-Fetch-Mode&lt;/code&gt;, &lt;code&gt;Sec-Fetch-Site&lt;/code&gt;를 포함하라고 안내한다. 이 부분을 빼면 프록시나 CDN 캐시가 정책이 다른 응답을 섞어 반환할 수 있다.&lt;/p&gt;

&lt;h2&gt;어떤 서비스에서 효과가 큰가?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;우선순위&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&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&gt;
      &lt;td&gt;내부 JSON, HTML 조각, 문서 다운로드 엔드포인트가 많은 서비스&lt;/td&gt;
      &lt;td&gt;높음&lt;/td&gt;
      &lt;td&gt;XSSI와 정보 노출 완화에 유리함&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;정적 파일 CDN 중심 서비스&lt;/td&gt;
      &lt;td&gt;중간&lt;/td&gt;
      &lt;td&gt;일부 공개 리소스는 예외 처리가 많이 필요할 수 있음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;외부 임베드와 공개 API가 많은 플랫폼&lt;/td&gt;
      &lt;td&gt;중간 이하&lt;/td&gt;
      &lt;td&gt;효과는 있지만 예외 경로 설계 비용이 큼&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;web.dev는 Fetch Metadata가 현대 브라우저 엔진 전반에서 지원된다고 설명하고, MDN은 &lt;code&gt;Sec-Fetch-Site&lt;/code&gt;를 2023년 3월부터 널리 사용 가능한 기능으로 표시한다. 다만 보안 정책은 &quot;지원 여부&quot;보다 &quot;정상 트래픽이 정책을 얼마나 자주 벗어나는지&quot;가 더 중요하므로, 실제 운영에서는 로깅과 점진 적용이 먼저다.&lt;/p&gt;

&lt;h2&gt;적용 전에 무엇을 점검해야 할까?&lt;/h2&gt;
&lt;p&gt;첫 번째는 교차 사이트로 열려 있어야 하는 경로 목록이다. 공개 이미지, 폰트, 파일 다운로드, CORS 허용 API, OAuth나 결제 리디렉션 복귀 경로처럼 외부 흐름과 연결되는 URL은 미리 구분해야 한다.&lt;/p&gt;
&lt;p&gt;두 번째는 인증 전에 차단하는 순서다. web.dev는 민감한 타이밍 정보를 줄이기 위해 인증 검사나 무거운 애플리케이션 처리보다 먼저 부적합 요청을 거부하라고 권장한다. 즉 애플리케이션 코드 깊숙이 들어간 뒤 403을 주는 구조보다, 앞단 미들웨어나 프록시에서 빠르게 끊는 편이 낫다.&lt;/p&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;Q. Fetch Metadata가 CSRF 방어를 완전히 대체할까?&lt;/h3&gt;
&lt;p&gt;아니다. Fetch Metadata는 방어 심화 수단에 가깝다. CSRF 토큰, SameSite 쿠키, 적절한 CORS 정책 같은 기존 방어와 함께 쓰는 편이 안전하다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;same-site&lt;/code&gt;는 항상 믿어도 될까?&lt;/h3&gt;
&lt;p&gt;그렇게 단정하면 안 된다. 서브도메인을 여러 팀이나 외부 시스템이 공유하는 환경이라면 &lt;code&gt;same-site&lt;/code&gt;도 충분히 공격 경로가 될 수 있다. 이 경우 web.dev가 제시한 기본 정책에서 &lt;code&gt;same-site&lt;/code&gt;를 제외하는 쪽이 더 맞다.&lt;/p&gt;

&lt;h3&gt;Q. 모든 403을 바로 적용해도 될까?&lt;/h3&gt;
&lt;p&gt;운영에서는 먼저 로그를 수집하는 편이 안전하다. web.dev도 실제 서비스 트래픽에서 위반을 먼저 확인하고, 필요한 엔드포인트 예외를 정리한 뒤 강제로 전환하는 순서를 권장한다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;Fetch Metadata 헤더는 &quot;누가 어떤 맥락으로 이 요청을 보냈는가&quot;를 서버가 빠르게 판단하게 해 주는 기능이다. 대부분의 웹앱에서는 &lt;code&gt;same-origin&lt;/code&gt;, &lt;code&gt;none&lt;/code&gt;, 필요할 때의 &lt;code&gt;same-site&lt;/code&gt;만 허용하고, 나머지 교차 사이트 하위 리소스 요청은 차단하는 정책이 출발점이 된다.&lt;/p&gt;
&lt;p&gt;2026년 6월 21일 기준 공개 문서를 기준으로 보면 핵심은 헤더 자체보다 정책 경계다. 어떤 경로가 외부에서 열려 있어야 하는지 먼저 분리하고, 응답이 달라지는 엔드포인트에는 &lt;code&gt;Vary&lt;/code&gt;를 정확히 설정하는 것이 실제 운영 품질을 좌우한다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/fetch-metadata/&quot; rel=&quot;noopener noreferrer&quot;&gt;W3C Working Draft: Fetch Metadata Request Headers&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-Fetch-Site&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN: Sec-Fetch-Site header&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://web.dev/articles/fetch-metadata&quot; rel=&quot;noopener noreferrer&quot;&gt;web.dev: Protect your resources from web attacks with Fetch Metadata&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/HTML, Javascript, CSS</category>
      <category>CSRF</category>
      <category>Fetch Metadata</category>
      <category>Sec-Fetch-Dest</category>
      <category>Sec-Fetch-Mode</category>
      <category>Sec-Fetch-Site</category>
      <category>web security</category>
      <category>XSSI</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/620</guid>
      <comments>https://jcdgods.tistory.com/620#entry620comment</comments>
      <pubDate>Sun, 21 Jun 2026 13:06:33 +0900</pubDate>
    </item>
    <item>
      <title>PostgreSQL lock_timeout 기준 정리</title>
      <link>https://jcdgods.tistory.com/619</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duDv13/dJMcadoQpcO/iH0uTUkptec5FMbkeQp8U1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duDv13/dJMcadoQpcO/iH0uTUkptec5FMbkeQp8U1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duDv13/dJMcadoQpcO/iH0uTUkptec5FMbkeQp8U1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduDv13%2FdJMcadoQpcO%2FiH0uTUkptec5FMbkeQp8U1%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;PostgreSQL lock_timeout 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;lock_timeout&lt;/code&gt;은 PostgreSQL에서 쿼리 실행 시간 전체를 제한하는 옵션이 아니라, 잠금을 기다리는 시간만 제한하는 설정이다. 2026년 6월 20일 기준 PostgreSQL 공식 문서를 보면 이 값은 테이블, 인덱스, 행 등 데이터베이스 객체의 잠금 획득을 기다리는 동안에만 적용되며, 기본값 0은 제한이 없다는 뜻이다.&lt;/p&gt;
&lt;p&gt;실무에서 이 설정이 필요한 이유는 분명하다. 배치 작업이나 DDL, 대량 수정 쿼리가 다른 세션의 잠금 때문에 오래 멈춰 있으면 애플리케이션 응답 지연이 길어질 수 있다. 반대로 값을 너무 짧게 잡으면 일시적인 경합에도 작업이 쉽게 실패한다. 선택 기준은 세 가지다. 잠금 대기가 사용자 요청 경로인지, 재시도가 쉬운 작업인지, 그리고 이미 &lt;code&gt;statement_timeout&lt;/code&gt;을 어떻게 쓰고 있는지다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;lock_timeout&lt;/code&gt;은 정확히 무엇을 제한할까?&lt;/h2&gt;
&lt;p&gt;PostgreSQL 공식 문서는 &lt;code&gt;lock_timeout&lt;/code&gt;을 &quot;잠금을 얻기 위해 기다리는 시간이 지정한 값을 넘으면 해당 statement를 중단하는 설정&quot;으로 설명한다. 여기서 중요한 점은 두 가지다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;잠금을 기다리는 동안에만 발동한다.&lt;/li&gt;
  &lt;li&gt;잠금 획득이 끝난 뒤 실제 쿼리 실행이 오래 걸리는 상황은 막지 못한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;예를 들어 어떤 &lt;code&gt;UPDATE&lt;/code&gt;가 다른 트랜잭션이 잡고 있는 행 잠금을 기다리느라 멈춰 있다면 &lt;code&gt;lock_timeout&lt;/code&gt;이 의미가 있다. 하지만 잠금은 바로 잡았고 이후 정렬, 조인, 대량 스캔 때문에 실행이 오래 걸리는 경우라면 &lt;code&gt;statement_timeout&lt;/code&gt;이 담당하는 영역이다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;statement_timeout&lt;/code&gt;과 무엇이 다를까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;항목&lt;/th&gt;
      &lt;th&gt;&lt;code&gt;lock_timeout&lt;/code&gt;&lt;/th&gt;
      &lt;th&gt;&lt;code&gt;statement_timeout&lt;/code&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;무엇을 제한하나&lt;/td&gt;
      &lt;td&gt;잠금 대기 시간&lt;/td&gt;
      &lt;td&gt;statement 전체 처리 시간&lt;/td&gt;
    &lt;/tr&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&gt;
      &lt;td&gt;기본값&lt;/td&gt;
      &lt;td&gt;0, 즉 비활성화&lt;/td&gt;
      &lt;td&gt;0, 즉 비활성화&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;공식 문서상의 주의점&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;statement_timeout&lt;/code&gt; 이상으로 잡으면 실익이 작다&lt;/td&gt;
      &lt;td&gt;전역 설정은 모든 세션에 영향을 준다&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;공식 문서는 &lt;code&gt;statement_timeout&lt;/code&gt;이 0이 아닌 경우 &lt;code&gt;lock_timeout&lt;/code&gt;을 같은 값이나 더 큰 값으로 잡는 것은 대체로 의미가 없다고 명시한다. 이유는 간단하다. statement 전체 시간 제한이 먼저 걸리기 때문이다.&lt;/p&gt;
&lt;p&gt;따라서 두 값을 함께 쓴다면 보통 &lt;code&gt;lock_timeout&lt;/code&gt;을 더 짧게 두고, 전체 실행 제한은 &lt;code&gt;statement_timeout&lt;/code&gt;이 맡는 구성이 자연스럽다. 예를 들어 잠금 대기는 2초만 허용하고, 실제 실행 시간은 15초까지 허용하는 식이다.&lt;/p&gt;

&lt;h2&gt;어떤 작업에 특히 유용할까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;lock_timeout&lt;/code&gt;은 &quot;기다리기보다 빨리 실패하고 다시 판단하는 편이 나은 작업&quot;에 잘 맞는다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;웹 요청 안에서 실행되는 짧은 조회나 수정&lt;/li&gt;
  &lt;li&gt;스키마 변경 전 잠금 경합 여부를 빨리 확인하고 싶은 운영 작업&lt;/li&gt;
  &lt;li&gt;재시도 로직이 있는 백그라운드 작업&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt;처럼 명시적으로 잠금 획득을 시도하는 코드&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;반대로 긴 배치나 마이그레이션처럼 어느 정도 잠금 대기를 감수해야 하는 작업은 너무 짧은 값이 오히려 방해가 될 수 있다. 공식 문서도 이 설정이 명시적 잠금뿐 아니라 암묵적으로 획득하는 잠금에도 적용된다고 설명하므로, 애플리케이션이 직접 &lt;code&gt;LOCK TABLE&lt;/code&gt;을 쓰지 않더라도 영향 범위는 넓다.&lt;/p&gt;

&lt;h2&gt;어디에 설정하는 것이 무난할까?&lt;/h2&gt;
&lt;p&gt;PostgreSQL 공식 문서는 &lt;code&gt;lock_timeout&lt;/code&gt;을 &lt;code&gt;postgresql.conf&lt;/code&gt;에서 전역 기본값으로 두는 것은 권장하지 않는다고 적고 있다. 모든 세션에 영향을 주기 때문이다. 이 문구는 실무 판단에도 직접 연결된다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;애플리케이션 요청 경로마다 요구사항이 다르면 세션 또는 트랜잭션 범위에서 설정하는 편이 낫다.&lt;/li&gt;
  &lt;li&gt;특정 마이그레이션 도구나 배치 계정만 다르게 가져가야 하면 그 연결 범위에 한정하는 편이 안전하다.&lt;/li&gt;
  &lt;li&gt;운영팀이 전체 인스턴스 기본값으로 밀어 넣기 전에, 어떤 워크로드가 실패로 바뀌는지 먼저 확인해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;SET&lt;/code&gt; 문서 기준으로 &lt;code&gt;SET&lt;/code&gt;은 현재 세션에만 적용되고, &lt;code&gt;SET LOCAL&lt;/code&gt;은 현재 트랜잭션이 끝날 때까지만 유지된다. 그래서 일회성 운영 작업이나 특정 트랜잭션 보호에는 &lt;code&gt;SET LOCAL lock_timeout = '2s'&lt;/code&gt; 같은 방식이 범위를 가장 좁게 잡기 쉽다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;BEGIN;
SET LOCAL lock_timeout = '2s';

UPDATE accounts
SET balance = balance - 1000
WHERE id = 10;

COMMIT;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이 예시는 문법 설명용이다. 핵심은 설정 범위를 세션 전체가 아니라 현재 트랜잭션으로 제한한다는 점이다.&lt;/p&gt;

&lt;h2&gt;값은 어느 정도부터 시작하면 될까?&lt;/h2&gt;
&lt;p&gt;공식 문서는 특정 권장 숫자를 제시하지 않는다. 따라서 값은 업무 특성으로 정해야 한다.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;생각해 볼 출발점&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;사용자 요청 안의 짧은 OLTP 쿼리&lt;/td&gt;
      &lt;td&gt;수백 ms에서 1~2초 수준&lt;/td&gt;
      &lt;td&gt;응답 지연을 길게 끌기보다 실패 후 재시도나 에러 처리로 넘기는 편이 나은 경우가 많다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;운영 중 DDL 실행 여부 점검&lt;/td&gt;
      &lt;td&gt;1~5초 수준&lt;/td&gt;
      &lt;td&gt;잠금 경합이 심하면 빨리 포기하고 점검 시간을 다시 잡는 편이 안전하다&lt;/td&gt;
    &lt;/tr&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;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;여기서 중요한 것은 숫자 자체보다 상대 관계다. 잠금 대기 허용 시간은 사용자 SLA, 재시도 정책, 그리고 같은 경로의 &lt;code&gt;statement_timeout&lt;/code&gt;보다 짧아야 해석이 쉬워진다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;Q. &lt;code&gt;lock_timeout&lt;/code&gt;을 걸면 느린 쿼리도 자동으로 막을까?&lt;/h3&gt;
&lt;p&gt;아니다. 공식 문서 기준으로 이 설정은 잠금 대기 중일 때만 발동한다. 실행 계획이 비효율적이어서 오래 걸리는 쿼리는 &lt;code&gt;statement_timeout&lt;/code&gt;, 인덱스 설계, 쿼리 튜닝으로 봐야 한다.&lt;/p&gt;

&lt;h3&gt;Q. 같은 statement 안에서 잠금을 여러 번 기다리면 어떻게 될까?&lt;/h3&gt;
&lt;p&gt;공식 문서는 시간 제한이 각 잠금 획득 시도마다 별도로 적용된다고 설명한다. 따라서 하나의 statement가 여러 객체 잠금을 차례로 기다릴 수 있다면, 각 대기 구간마다 &lt;code&gt;lock_timeout&lt;/code&gt;이 따로 평가된다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;NOWAIT&lt;/code&gt;와는 어떤 차이가 있을까?&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;NOWAIT&lt;/code&gt;는 잠금을 전혀 기다리지 않고 즉시 실패시키는 방식이고, &lt;code&gt;lock_timeout&lt;/code&gt;은 일정 시간까지는 기다린 뒤 실패시키는 방식이다. 즉 &quot;기다림 자체를 없앨 것인가&quot;와 &quot;짧게만 허용할 것인가&quot;의 차이다.&lt;/p&gt;

&lt;h3&gt;Q. 전역 설정으로 두면 안 될까?&lt;/h3&gt;
&lt;p&gt;기술적으로는 가능하지만 PostgreSQL 공식 문서는 &lt;code&gt;postgresql.conf&lt;/code&gt;에서 이 값을 두는 것을 권장하지 않는다. 이유는 모든 세션이 같은 정책을 강제로 따르게 되기 때문이다. 성격이 다른 OLTP, 배치, 마이그레이션 작업이 한 인스턴스를 공유하면 부작용이 생길 수 있다.&lt;/p&gt;

&lt;h2&gt;실무 적용 순서&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;먼저 같은 경로의 &lt;code&gt;statement_timeout&lt;/code&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&gt;이 순서가 유용한 이유는 분명하다. &lt;code&gt;lock_timeout&lt;/code&gt;은 장애를 예방하는 만능 스위치가 아니라, 잠금 대기를 어디까지 허용할지에 대한 정책이기 때문이다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;lock_timeout&lt;/code&gt;은 PostgreSQL에서 잠금 대기만 제한하는 설정이다. 쿼리 전체 실행 시간 제한은 &lt;code&gt;statement_timeout&lt;/code&gt;이 맡고, 두 값을 함께 쓰면 보통 &lt;code&gt;lock_timeout&lt;/code&gt;을 더 짧게 잡는 편이 해석이 쉽다.&lt;/p&gt;
&lt;p&gt;2026년 6월 20일 기준 PostgreSQL 공식 문서를 기준으로 보면, 이 설정은 전역 기본값보다 세션이나 트랜잭션 범위에서 좁게 적용하는 편이 안전하다. 특히 사용자 요청 경로처럼 잠금 때문에 오래 멈추는 것이 더 큰 문제인 워크로드에서 유용하다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/runtime-config-client.html&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL 공식 문서: Client Connection Defaults&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/sql-set.html&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL 공식 문서: SET&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/서버, DBMS</category>
      <category>DATABASE</category>
      <category>lock</category>
      <category>lock_timeout</category>
      <category>PostgreSQL</category>
      <category>sql</category>
      <category>statement_timeout</category>
      <category>Timeout</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/619</guid>
      <comments>https://jcdgods.tistory.com/619#entry619comment</comments>
      <pubDate>Sat, 20 Jun 2026 12:26:23 +0900</pubDate>
    </item>
    <item>
      <title>Python tempfile 사용법</title>
      <link>https://jcdgods.tistory.com/618</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tR7gI/dJMcabYXCco/wOMlVc0j6kEHSrGzXzied0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tR7gI/dJMcabYXCco/wOMlVc0j6kEHSrGzXzied0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tR7gI/dJMcabYXCco/wOMlVc0j6kEHSrGzXzied0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtR7gI%2FdJMcabYXCco%2FwOMlVc0j6kEHSrGzXzied0%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Python tempfile 사용법&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;tempfile&lt;/code&gt; 모듈은 임시 파일과 임시 디렉터리를 안전하게 만들기 위한 Python 표준 라이브러리다. Python 3.14 공식 문서 기준으로 실무에서 가장 많이 구분해야 하는 것은 이름 없는 임시 파일이 필요한지, 이름이 보이는 임시 파일을 다시 열어야 하는지, 디렉터리 단위 정리가 필요한지다.&lt;/p&gt;
&lt;p&gt;선택 기준은 네 가지다. 자동 정리가 필요한지, 파일 이름을 외부 API나 다른 프로세스에 넘겨야 하는지, Windows에서 재오픈이 필요한지, 그리고 수동 삭제 책임을 감수할지다. 이 글은 Python 3.14 공식 문서를 기준으로 &lt;code&gt;TemporaryFile&lt;/code&gt;, &lt;code&gt;NamedTemporaryFile&lt;/code&gt;, &lt;code&gt;TemporaryDirectory&lt;/code&gt;, &lt;code&gt;mkstemp&lt;/code&gt;, &lt;code&gt;mkdtemp&lt;/code&gt;의 차이만 정리한다.&lt;/p&gt;

&lt;h2&gt;tempfile은 언제 쓰면 될까?&lt;/h2&gt;
&lt;p&gt;다음 조건이면 &lt;code&gt;tempfile&lt;/code&gt;이 기본 선택지가 된다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;고유한 임시 경로를 충돌 없이 만들어야 한다.&lt;/li&gt;
  &lt;li&gt;작업이 끝나면 파일이나 디렉터리를 정리해야 한다.&lt;/li&gt;
  &lt;li&gt;공유 디렉터리에서 예측 가능한 파일명 생성 위험을 피하고 싶다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;공식 문서는 고수준 인터페이스로 &lt;code&gt;TemporaryFile&lt;/code&gt;, &lt;code&gt;NamedTemporaryFile&lt;/code&gt;, &lt;code&gt;TemporaryDirectory&lt;/code&gt;, &lt;code&gt;SpooledTemporaryFile&lt;/code&gt;를 제공하고, 저수준 함수로 &lt;code&gt;mkstemp()&lt;/code&gt;와 &lt;code&gt;mkdtemp()&lt;/code&gt;를 제공한다고 설명한다. 고수준 인터페이스는 자동 정리를 제공하지만, 저수준 함수는 호출자가 직접 삭제를 책임진다.&lt;/p&gt;

&lt;h2&gt;TemporaryFile과 NamedTemporaryFile은 무엇이 다를까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;TemporaryFile()&lt;/code&gt;은 임시 저장소가 필요하지만 파일 이름을 외부에 노출할 필요가 없을 때 적합하다. Python 공식 문서는 Unix에서 디렉터리 엔트리가 아예 생성되지 않거나 생성 직후 제거될 수 있다고 설명하며, 다른 플랫폼에서는 이 동작에 의존하지 말라고 안내한다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;NamedTemporaryFile()&lt;/code&gt;은 파일 시스템에 보이는 이름이 항상 필요할 때 쓴다. 공식 문서는 이 함수가 보이는 이름을 보장하고, &lt;code&gt;delete&lt;/code&gt;와 &lt;code&gt;delete_on_close&lt;/code&gt;로 삭제 시점을 제어한다고 명시한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from tempfile import TemporaryFile, NamedTemporaryFile

with TemporaryFile() as fp:
    fp.write(b&quot;hello&quot;)
    fp.seek(0)
    print(fp.read())

with NamedTemporaryFile(mode=&quot;w+t&quot;, delete=True, delete_on_close=False) as fp:
    print(fp.name)&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;NamedTemporaryFile을 다시 열어야 하면 무엇을 봐야 할까?&lt;/h2&gt;
&lt;p&gt;이 부분이 실무에서 가장 자주 문제를 만든다. Python 3.14 공식 문서는 파일을 닫은 뒤 이름으로 다시 열어야 한다면 &lt;code&gt;delete=False&lt;/code&gt;로 만들거나, &lt;code&gt;with&lt;/code&gt; 문 안에서 쓴다면 &lt;code&gt;delete_on_close=False&lt;/code&gt;를 고려하라고 설명한다. 후자는 컨텍스트 종료 시 자동 정리를 유지할 수 있어서 문서에서도 권장되는 방식이다.&lt;/p&gt;
&lt;p&gt;운영체제 차이도 있다. 공식 문서는 POSIX에서는 열려 있는 동안에도 이름으로 다시 열 수 있다고 설명하지만, Windows에서는 추가 조건이 필요하다고 밝힌다. 예를 들어 &lt;code&gt;delete=False&lt;/code&gt;이거나, 추가 오픈이 delete 공유 접근을 허용하거나, &lt;code&gt;delete=True&lt;/code&gt;이면서 &lt;code&gt;delete_on_close=False&lt;/code&gt;여야 한다. 또한 Windows에서 delete 공유 없이 다시 연 핸들이 남아 있으면 컨텍스트 종료 시 &lt;code&gt;PermissionError&lt;/code&gt;가 날 수 있다.&lt;/p&gt;

&lt;h2&gt;TemporaryDirectory는 언제 더 낫나?&lt;/h2&gt;
&lt;p&gt;여러 파일을 한 번에 만들고 마지막에 디렉터리째 정리하고 싶다면 &lt;code&gt;TemporaryDirectory()&lt;/code&gt;가 더 단순하다. 공식 문서는 이 객체가 컨텍스트 관리자이며, 종료 시 디렉터리와 내부 파일을 함께 제거한다고 설명한다.&lt;/p&gt;
&lt;p&gt;Python 3.12부터는 &lt;code&gt;delete&lt;/code&gt; 매개변수가 추가되어 컨텍스트를 벗어나도 디렉터리를 남겨둘 수 있다. 문서는 이 옵션이 디버깅이나 조건부 정리에 유용할 수 있다고 설명한다. 또 Python 3.10부터는 &lt;code&gt;ignore_cleanup_errors&lt;/code&gt;가 있어 Windows에서 열린 파일 때문에 정리가 실패하는 경우를 best-effort로 넘길 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from tempfile import TemporaryDirectory
from pathlib import Path

with TemporaryDirectory() as tmpdir:
    base = Path(tmpdir)
    report = base / &quot;report.txt&quot;
    report.write_text(&quot;ok&quot;, encoding=&quot;utf-8&quot;)&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;mkstemp와 mkdtemp는 언제 선택해야 할까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;mkstemp()&lt;/code&gt;와 &lt;code&gt;mkdtemp()&lt;/code&gt;는 저수준 함수다. 공식 문서는 두 함수 모두 안전하게 임시 경로를 만들지만, 생성 후 삭제 책임은 호출자에게 있다고 명시한다. 따라서 파일 디스크립터나 절대 경로를 더 직접적으로 다뤄야 할 때만 쓰는 편이 낫다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mkstemp()&lt;/code&gt;는 OS 수준 파일 핸들과 절대 경로를 반환한다. &lt;code&gt;mkdtemp()&lt;/code&gt;는 새 임시 디렉터리의 절대 경로를 반환하며, Python 3.12부터는 &lt;code&gt;dir&lt;/code&gt; 인자가 상대 경로여도 항상 절대 경로를 돌려준다고 문서에 적혀 있다.&lt;/p&gt;

&lt;h2&gt;임시 디렉터리는 어디에 만들어질까?&lt;/h2&gt;
&lt;p&gt;기본 위치가 궁금하면 &lt;code&gt;gettempdir()&lt;/code&gt; 문서를 보면 된다. Python 공식 문서는 기본 디렉터리 탐색 순서를 &lt;code&gt;TMPDIR&lt;/code&gt;, &lt;code&gt;TEMP&lt;/code&gt;, &lt;code&gt;TMP&lt;/code&gt;, 그 다음 플랫폼별 기본 경로 순서로 설명한다. 따라서 컨테이너, CI, 서버 배치 작업에서는 환경 변수 설정이 실제 저장 위치를 바꿀 수 있다.&lt;/p&gt;

&lt;h2&gt;실무에서는 어떤 선택이 무난할까?&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;파일 이름이 필요 없고 한 함수 안에서만 쓰면 &lt;code&gt;TemporaryFile()&lt;/code&gt;이 가장 단순하다.&lt;/li&gt;
  &lt;li&gt;외부 프로그램이나 다른 코드에 경로를 넘겨야 하면 &lt;code&gt;NamedTemporaryFile()&lt;/code&gt;을 먼저 본다.&lt;/li&gt;
  &lt;li&gt;여러 파일을 묶어서 처리하면 &lt;code&gt;TemporaryDirectory()&lt;/code&gt;가 정리 관리에 유리하다.&lt;/li&gt;
  &lt;li&gt;파일 디스크립터나 절대 경로를 직접 관리해야 하면 &lt;code&gt;mkstemp()&lt;/code&gt;, &lt;code&gt;mkdtemp()&lt;/code&gt;를 선택한다.&lt;/li&gt;
  &lt;li&gt;Windows 재오픈이 필요하면 &lt;code&gt;delete&lt;/code&gt;, &lt;code&gt;delete_on_close&lt;/code&gt;, 열린 핸들 정리 순서를 함께 점검한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;NamedTemporaryFile의 delete_on_close는 언제 추가됐을까?&lt;/h3&gt;
&lt;p&gt;Python 공식 문서는 &lt;code&gt;delete_on_close&lt;/code&gt; 매개변수가 Python 3.12에 추가됐다고 명시한다. 따라서 3.11 이하 호환 코드라면 이 인자를 전제로 작성하면 안 된다.&lt;/p&gt;

&lt;h3&gt;TemporaryDirectory는 항상 자동 삭제될까?&lt;/h3&gt;
&lt;p&gt;기본값은 자동 삭제지만, Python 3.12부터는 &lt;code&gt;delete=False&lt;/code&gt;로 비활성화할 수 있다. 다만 이 경우 정리 책임은 호출자에게 남는다.&lt;/p&gt;

&lt;h3&gt;mkstemp를 쓰면 자동으로 파일이 지워질까?&lt;/h3&gt;
&lt;p&gt;아니다. Python 공식 문서는 &lt;code&gt;mkstemp()&lt;/code&gt;와 &lt;code&gt;mkdtemp()&lt;/code&gt;는 호출자가 직접 삭제해야 한다고 설명한다. 자동 정리가 필요하면 고수준 인터페이스가 더 적합하다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.python.org/3/library/tempfile.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Python 3.14 tempfile 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/C, C++, Java, Python</category>
      <category>mkdtemp</category>
      <category>mkstemp</category>
      <category>NamedTemporaryFile</category>
      <category>python</category>
      <category>TEMPFILE</category>
      <category>TemporaryDirectory</category>
      <category>TemporaryFile</category>
      <category>표준라이브러리</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/618</guid>
      <comments>https://jcdgods.tistory.com/618#entry618comment</comments>
      <pubDate>Fri, 19 Jun 2026 11:20:36 +0900</pubDate>
    </item>
    <item>
      <title>Git clean 사용법</title>
      <link>https://jcdgods.tistory.com/617</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWe3E4/dJMcahdLSPk/CIf3yEd5RKMHpPaSJVQSkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWe3E4/dJMcahdLSPk/CIf3yEd5RKMHpPaSJVQSkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWe3E4/dJMcahdLSPk/CIf3yEd5RKMHpPaSJVQSkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWe3E4%2FdJMcahdLSPk%2FCIf3yEd5RKMHpPaSJVQSkK%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Git clean 사용법&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;git clean&lt;/code&gt;은 Git이 추적하지 않는 파일과 디렉터리를 작업 트리에서 제거할 때 쓰는 명령이다. 2026년 6월 18일 기준 Git 공식 문서는 이 명령이 기본적으로 untracked 파일만 지우고, &lt;code&gt;-x&lt;/code&gt;나 &lt;code&gt;-X&lt;/code&gt; 옵션에 따라 ignored 파일 처리 범위가 달라진다고 설명한다.&lt;/p&gt;
&lt;p&gt;실무에서 핵심은 세 가지다. 실제 삭제 전에는 &lt;code&gt;-n&lt;/code&gt;으로 범위를 먼저 확인할 것, 디렉터리까지 지우려면 &lt;code&gt;-d&lt;/code&gt;가 필요하다는 점, 그리고 ignored 파일까지 지우는 &lt;code&gt;-x&lt;/code&gt;와 ignored 파일만 지우는 &lt;code&gt;-X&lt;/code&gt;를 혼동하지 않는 것이다. 이 글은 Git 공식 문서를 기준으로 &lt;code&gt;git clean&lt;/code&gt;을 언제 어떻게 써야 하는지 정리한다.&lt;/p&gt;

&lt;h2&gt;Git clean은 무엇을 지울까?&lt;/h2&gt;
&lt;p&gt;Git 공식 문서의 설명에 따르면 &lt;code&gt;git clean&lt;/code&gt;은 현재 디렉터리부터 시작해 버전 관리 대상이 아닌 파일을 재귀적으로 정리한다. 다만 기본 동작은 보수적이어서, 아무 옵션 없이 실행하면 untracked 디렉터리까지 깊게 들어가 지우지 않는다.&lt;/p&gt;
&lt;p&gt;즉 생성 산출물, 임시 로그, 실험용 파일처럼 아직 Git에 추가하지 않은 파일을 정리할 때 적합하다. 반대로 이미 추적 중인 파일 변경분을 되돌리는 명령은 아니므로, tracked 파일 복원은 &lt;code&gt;git restore&lt;/code&gt;나 &lt;code&gt;git reset&lt;/code&gt; 계열로 구분해야 한다.&lt;/p&gt;

&lt;h2&gt;왜 &lt;code&gt;-f&lt;/code&gt; 없이 바로 지워지지 않을까?&lt;/h2&gt;
&lt;p&gt;Git 공식 문서는 &lt;code&gt;clean.requireForce&lt;/code&gt;가 기본적으로 &lt;code&gt;true&lt;/code&gt;라서, &lt;code&gt;-f&lt;/code&gt; 없이 파일이나 디렉터리를 삭제하지 않는다고 설명한다. 이는 실수로 로컬 파일을 잃는 상황을 막기 위한 안전장치다.&lt;/p&gt;
&lt;p&gt;또한 문서상 중첩된 Git 저장소처럼 별도 &lt;code&gt;.git&lt;/code&gt; 디렉터리를 가진 untracked 디렉터리는 더 보수적으로 다뤄진다. 이런 디렉터리를 지우려면 두 번째 &lt;code&gt;-f&lt;/code&gt;가 필요할 수 있으므로, 외부 저장소를 임시로 복사해 둔 작업 디렉터리에서는 특히 주의해야 한다.&lt;/p&gt;

&lt;h2&gt;가장 먼저 써야 하는 명령은 무엇일까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;권장 명령&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;무엇이 지워질지 먼저 보고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git clean -n&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;삭제 없이 대상만 출력한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;untracked 파일만 실제로 지우고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git clean -f&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;기본 안전장치를 해제하고 파일만 삭제한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;untracked 디렉터리까지 함께 지우고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git clean -fd&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;디렉터리 재귀 삭제를 명시한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;빌드 산출물 등 ignored 파일까지 모두 지우고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git clean -fdx&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;ignore 규칙을 무시하고 untracked 전체를 정리한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ignored 파일만 지우고 수동 파일은 남기고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git clean -fdX&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Git이 무시하는 파일만 대상으로 삼는다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;대부분의 사고는 &lt;code&gt;-n&lt;/code&gt;을 건너뛰고 바로 삭제했을 때 발생한다. 문서 기준으로 &lt;code&gt;--dry-run&lt;/code&gt;은 실제 삭제를 하지 않으므로, CI 실패 재현이나 깨끗한 빌드 테스트 전에도 먼저 범위를 확인하는 습관이 안전하다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;-x&lt;/code&gt;와 &lt;code&gt;-X&lt;/code&gt;는 어떻게 나눠야 할까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;-x&lt;/code&gt;는 표준 ignore 규칙을 사용하지 않고, 명령행의 &lt;code&gt;-e&lt;/code&gt; 예외 패턴만 유지한 채 untracked 파일을 정리한다. Git 공식 문서는 이 옵션이 build product를 모두 지워 깨끗한 작업 디렉터리를 만들 때 유용하다고 설명한다.&lt;/p&gt;
&lt;p&gt;반면 &lt;code&gt;-X&lt;/code&gt;는 ignored 파일만 제거한다. 따라서 &lt;code&gt;.env.local&lt;/code&gt;처럼 ignore되지 않은 개인 메모 파일을 남기고, &lt;code&gt;dist/&lt;/code&gt;나 &lt;code&gt;__pycache__/&lt;/code&gt;처럼 ignore된 산출물만 정리하려면 &lt;code&gt;-X&lt;/code&gt;가 더 보수적인 선택이다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;-e&lt;/code&gt; 예외 패턴은 언제 쓸까?&lt;/h2&gt;
&lt;p&gt;Git 공식 문서는 &lt;code&gt;-e &amp;lt;pattern&amp;gt;&lt;/code&gt;이 표준 ignore 규칙에 추가되는 exclude 패턴이라고 설명한다. 이 옵션을 쓰면 대량 정리 중에도 특정 파일이나 디렉터리를 남겨 둘 수 있다.&lt;/p&gt;
&lt;p&gt;예를 들어 &lt;code&gt;git clean -fdx -e .env.local&lt;/code&gt;처럼 실행하면 ignored 파일을 폭넓게 지우면서도 로컬 환경 파일은 보존하는 식의 운영이 가능하다. 빌드 캐시를 지우되 로컬 비밀값 파일은 남겨야 하는 저장소에서 유용하다.&lt;/p&gt;

&lt;h2&gt;interactive 모드는 언제 더 안전할까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git clean -i&lt;/code&gt;는 삭제 대상을 보여 준 뒤 interactive loop로 들어간다. Git 공식 문서는 이 모드에서는 &lt;code&gt;clean.requireForce&lt;/code&gt;를 무시하지만, 대신 사용자가 직접 선택하거나 패턴으로 걸러낼 수 있어 자체적인 안전장치가 된다고 설명한다.&lt;/p&gt;
&lt;p&gt;한 번에 여러 파일을 정리해야 하지만 경로가 애매할 때는 &lt;code&gt;-i&lt;/code&gt;가 실수 확률을 줄인다. 특히 오래된 브랜치 작업 디렉터리에서 어떤 파일이 임시 산출물인지 확신이 없을 때 일괄 삭제보다 interactive 선택이 낫다.&lt;/p&gt;

&lt;h2&gt;ignored 파일 규칙과는 어떤 관계가 있을까?&lt;/h2&gt;
&lt;p&gt;Git 공식 문서의 &lt;code&gt;gitignore&lt;/code&gt; 설명에 따르면 ignore 규칙은 &lt;code&gt;.gitignore&lt;/code&gt;, &lt;code&gt;$GIT_DIR/info/exclude&lt;/code&gt;, &lt;code&gt;core.excludesFile&lt;/code&gt; 등 여러 소스에서 읽힌다. &lt;code&gt;git clean&lt;/code&gt; 기본 동작은 이런 표준 ignore 규칙을 존중하므로, ignore된 파일은 기본적으로 남는다.&lt;/p&gt;
&lt;p&gt;그래서 &quot;왜 &lt;code&gt;git clean -fd&lt;/code&gt;를 했는데 &lt;code&gt;node_modules/&lt;/code&gt;는 안 지워지지?&quot; 같은 상황이 생길 수 있다. 이 경우는 대부분 &lt;code&gt;node_modules/&lt;/code&gt;가 ignore 대상이라서이고, 모두 지우고 싶다면 &lt;code&gt;-x&lt;/code&gt;를 써야 한다.&lt;/p&gt;

&lt;h2&gt;실무 기본값은 무엇이 무난할까?&lt;/h2&gt;
&lt;p&gt;대부분의 저장소에서는 바로 삭제보다 &lt;code&gt;git clean -nd&lt;/code&gt;로 확인하고, 문제가 없을 때 &lt;code&gt;git clean -fd&lt;/code&gt;로 진행하는 2단계가 무난하다. 빌드 환경을 완전히 초기화해야 할 때만 &lt;code&gt;-fdx&lt;/code&gt;를 쓰는 편이 안전하다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-fdX&lt;/code&gt;는 ignore 규칙이 잘 정리된 저장소에서 특히 유용하다. 개발자가 수동으로 만든 비추적 파일은 남기고, Git이 이미 산출물로 분류한 파일만 비우기 때문에 삭제 범위를 상대적으로 예측하기 쉽다.&lt;/p&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. &lt;code&gt;git clean&lt;/code&gt;은 tracked 파일 수정분도 같이 되돌리나?&lt;/h3&gt;
&lt;p&gt;아니다. Git 공식 문서 기준으로 &lt;code&gt;git clean&lt;/code&gt;은 untracked 파일과 디렉터리 정리용이다. tracked 파일 복원은 별도 명령으로 처리해야 한다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;git clean -fdx&lt;/code&gt;는 왜 위험하다고 하나?&lt;/h3&gt;
&lt;p&gt;ignore 규칙까지 무시하고 untracked 파일을 폭넓게 지우기 때문이다. 빌드 산출물 정리에는 유용하지만, 로컬에서만 쓰는 비추적 설정 파일도 함께 삭제될 수 있다.&lt;/p&gt;

&lt;h3&gt;Q. ignored 파일만 지우고 싶으면 무엇을 쓰면 될까?&lt;/h3&gt;
&lt;p&gt;이 경우는 &lt;code&gt;git clean -fdX&lt;/code&gt;가 더 맞다. Git 공식 문서는 &lt;code&gt;-X&lt;/code&gt;가 ignored 파일만 제거한다고 설명한다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git clean&lt;/code&gt;의 선택 기준은 단순하다. 먼저 &lt;code&gt;-n&lt;/code&gt;으로 범위를 확인하고, 디렉터리 삭제가 필요하면 &lt;code&gt;-d&lt;/code&gt;를 추가하며, ignore 규칙을 유지할지 여부에 따라 &lt;code&gt;-x&lt;/code&gt;와 &lt;code&gt;-X&lt;/code&gt;를 고르면 된다.&lt;/p&gt;
&lt;p&gt;한 줄 기준으로 줄이면 이렇다. 일반 정리는 &lt;code&gt;git clean -fd&lt;/code&gt;, 깨끗한 빌드 재현은 &lt;code&gt;git clean -fdx&lt;/code&gt;, ignored 파일만 청소할 때는 &lt;code&gt;git clean -fdX&lt;/code&gt;를 우선 검토하면 된다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-clean&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Documentation: git-clean&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/gitignore&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Documentation: gitignore&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/Git, IDE, 툴 관련</category>
      <category>Build Cleanup</category>
      <category>CLI</category>
      <category>GIT</category>
      <category>git clean</category>
      <category>gitignore</category>
      <category>version control</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/617</guid>
      <comments>https://jcdgods.tistory.com/617#entry617comment</comments>
      <pubDate>Thu, 18 Jun 2026 12:26:29 +0900</pubDate>
    </item>
    <item>
      <title>Trusted Types 적용 기준 정리</title>
      <link>https://jcdgods.tistory.com/616</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sNHXz/dJMb99Nt1tf/KvJjjsypO4w9zNoyOZxjx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sNHXz/dJMb99Nt1tf/KvJjjsypO4w9zNoyOZxjx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sNHXz/dJMb99Nt1tf/KvJjjsypO4w9zNoyOZxjx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsNHXz%2FdJMb99Nt1tf%2FKvJjjsypO4w9zNoyOZxjx1%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Trusted Types 적용 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;innerHTML&lt;/code&gt;, &lt;code&gt;document.write()&lt;/code&gt;, &lt;code&gt;eval()&lt;/code&gt; 같은 injection sink에 문자열이 바로 들어가면 DOM 기반 XSS로 이어질 수 있다. 2026년 6월 17일 기준 MDN과 W3C Trusted Types 초안 문서를 보면 Trusted Types는 이런 sink에 들어가는 값을 정책 기반 객체로 제한하고, CSP의 &lt;code&gt;require-trusted-types-for&lt;/code&gt;와 &lt;code&gt;trusted-types&lt;/code&gt; 지시어로 강제할 수 있다.&lt;/p&gt;
&lt;p&gt;실무에서 중요한 판단은 세 가지다. 지금 애플리케이션이 문자열 기반 DOM 조작을 얼마나 많이 쓰는지, sanitizer나 템플릿 계층을 어디에 둘지, 그리고 바로 강제 모드로 갈지 report-only로 위반 지점을 먼저 수집할지다. 이 글은 최신 문서 기준으로 Trusted Types의 역할과 적용 순서를 짧게 정리한다.&lt;/p&gt;

&lt;h2&gt;Trusted Types는 무엇을 막으려는 기능일까?&lt;/h2&gt;
&lt;p&gt;MDN은 Trusted Types API를 &quot;실행 가능성이 있는 브라우저 API에 값이 들어가기 전에 개발자가 지정한 변환 함수를 거치게 하는 방법&quot;으로 설명한다. 핵심은 문자열을 그냥 sink에 넣지 않고, 정책이 만든 &lt;code&gt;TrustedHTML&lt;/code&gt;, &lt;code&gt;TrustedScript&lt;/code&gt;, &lt;code&gt;TrustedScriptURL&lt;/code&gt; 객체만 통과시키는 데 있다.&lt;/p&gt;
&lt;p&gt;이 방식은 서버 측 XSS를 막는 기능이 아니라 클라이언트 측 DOM XSS의 공격 표면을 줄이는 기능에 가깝다. web.dev 문서도 &lt;code&gt;innerHTML&lt;/code&gt;, &lt;code&gt;outerHTML&lt;/code&gt;, &lt;code&gt;insertAdjacentHTML&lt;/code&gt;, &lt;code&gt;eval&lt;/code&gt;, &lt;code&gt;setTimeout&lt;/code&gt;, &lt;code&gt;HTMLScriptElement.src&lt;/code&gt; 같은 위험한 sink를 직접 예로 든다.&lt;/p&gt;

&lt;h2&gt;어떤 타입이 있고 언제 쓰일까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;타입&lt;/th&gt;
      &lt;th&gt;대상&lt;/th&gt;
      &lt;th&gt;대표 예시&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;TrustedHTML&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;HTML로 해석되는 sink&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;innerHTML&lt;/code&gt;, &lt;code&gt;document.write()&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;TrustedScript&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;JavaScript로 해석되는 sink&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;eval()&lt;/code&gt;, script text&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;TrustedScriptURL&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;스크립트 URL sink&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;&amp;lt;script src&amp;gt;&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;MDN 문서에 따르면 개발자는 &lt;code&gt;trustedTypes.createPolicy()&lt;/code&gt;로 정책을 만들고, 정책 함수 안에서 입력값을 정제하거나 허용 목록 검사를 수행한 뒤 적절한 trusted type 객체를 만든다. 즉 Trusted Types가 자동으로 안전한 값을 만들어 주는 것이 아니라, 어떤 입력을 안전하다고 볼지 기준을 애플리케이션이 직접 정의해야 한다.&lt;/p&gt;

&lt;h2&gt;CSP의 &lt;code&gt;require-trusted-types-for&lt;/code&gt;와 &lt;code&gt;trusted-types&lt;/code&gt;는 무엇이 다를까?&lt;/h2&gt;
&lt;p&gt;W3C 초안은 두 지시어의 역할을 분리한다. &lt;code&gt;require-trusted-types-for&lt;/code&gt;는 어떤 injection sink 그룹에 trusted type을 요구할지 정하는 강제 규칙이고, &lt;code&gt;trusted-types&lt;/code&gt;는 어떤 정책 이름을 생성할 수 있는지 통제하는 규칙이다.&lt;/p&gt;
&lt;p&gt;MDN도 같은 구조를 설명한다. JavaScript API만 도입하면 개발자가 우회해서 문자열을 다시 sink에 넣을 수 있으므로, 실제 강제는 CSP가 맡는다. 그래서 운영 기준으로는 &quot;정책 코드 작성&quot;과 &quot;CSP 강제&quot;를 별도 단계로 보는 편이 맞다.&lt;/p&gt;

&lt;h2&gt;바로 강제해야 할까, report-only부터 시작해야 할까?&lt;/h2&gt;
&lt;p&gt;대부분의 기존 서비스는 report-only부터 시작하는 편이 안전하다. web.dev는 먼저 &lt;code&gt;Content-Security-Policy-Report-Only: require-trusted-types-for 'script'&lt;/code&gt; 형태로 위반을 수집하고, &lt;code&gt;ReportingObserver&lt;/code&gt; 또는 &lt;code&gt;securitypolicyviolation&lt;/code&gt; 이벤트로 어떤 코드가 문자열을 sink에 넣는지 확인하는 흐름을 제안한다.&lt;/p&gt;
&lt;p&gt;이 접근이 필요한 이유는 코드베이스 안에 직접 작성한 코드뿐 아니라 서드파티 라이브러리, 오래된 위젯, 템플릿 헬퍼가 함께 섞여 있기 때문이다. 강제 모드부터 켜면 브라우저가 &lt;code&gt;TypeError&lt;/code&gt;를 던져 화면 기능이 바로 깨질 수 있다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;정책은 어떻게 잡는 편이 무난할까?&lt;/h2&gt;
&lt;p&gt;HTML 조각이 꼭 필요한 곳이라면 일반적으로 sanitizer를 정책 안에 두는 방식이 먼저 검토된다. MDN은 HTML sink에 대해 보통 sanitize 함수를 사용한다고 설명하고, web.dev는 DOMPurify가 &lt;code&gt;RETURN_TRUSTED_TYPE&lt;/code&gt; 옵션으로 &lt;code&gt;TrustedHTML&lt;/code&gt;을 반환할 수 있다고 소개한다.&lt;/p&gt;
&lt;p&gt;다만 &quot;sanitizer를 한 번 넣었으니 끝&quot;으로 보면 안 된다. web.dev도 sanitization 로직 자체에 결함이 있으면 여전히 DOM XSS가 남을 수 있다고 경고한다. 그래서 정책 함수는 애플리케이션에서 허용할 HTML 범위와 속성을 좁게 두는 쪽이 유리하다.&lt;/p&gt;

&lt;h2&gt;default policy는 기본값으로 써도 될까?&lt;/h2&gt;
&lt;p&gt;기본값으로 두기보다는 이행 단계에서만 제한적으로 쓰는 편이 맞다. W3C 초안은 이름이 &lt;code&gt;default&lt;/code&gt;인 정책이 있으면 문자열이 sink로 들어갈 때 이를 암묵적으로 호출한다고 설명하지만, 규칙이 느슨한 default policy는 Trusted Types의 이점을 사실상 무력화할 수 있다고 함께 적고 있다.&lt;/p&gt;
&lt;p&gt;web.dev 역시 default policy를 &quot;마지막 수단&quot;으로 다루고, 일반 정책으로 리팩터링하는 것을 우선하라고 권한다. 따라서 운영 기준으로는 default policy를 장기 구조로 삼기보다, 위반 구간을 찾고 옮겨 가는 과도기 장치로 보는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;어떤 서비스에서 먼저 도입할 가치가 클까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;도입 우선순위&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;사용자 입력을 DOM에 자주 렌더링하는 SPA&lt;/td&gt;
      &lt;td&gt;높음&lt;/td&gt;
      &lt;td&gt;문자열 기반 DOM 조작이 많아 DOM XSS 완화 효과가 큼&lt;/td&gt;
    &lt;/tr&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&gt;
      &lt;td&gt;정적 사이트 + 최소한의 스크립트&lt;/td&gt;
      &lt;td&gt;낮음&lt;/td&gt;
      &lt;td&gt;공격 표면 자체가 작아 다른 보안 설정이 우선일 수 있음&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;MDN은 2026년 2월부터 최신 브라우저 기준 Baseline 2026으로 표시하지만, 동시에 구형 환경에서는 동작하지 않을 수 있다고 적는다. 따라서 브라우저 지원 범위가 넓은 서비스라면 기능 감지, 점진 적용, 폴리필 사용 여부를 같이 검토해야 한다.&lt;/p&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;Q. Trusted Types만 켜면 XSS를 모두 막을 수 있을까?&lt;/h3&gt;
&lt;p&gt;아니다. Trusted Types는 DOM 기반 XSS 완화 수단이다. 서버 측 HTML 생성 단계의 XSS, 잘못된 sanitizer 규칙, 정책 내부의 허용 로직 오류까지 자동으로 해결해 주지는 않는다.&lt;/p&gt;

&lt;h3&gt;Q. 모든 프로젝트에 무조건 넣는 것이 좋을까?&lt;/h3&gt;
&lt;p&gt;그렇게 단정하기는 어렵다. 문자열 기반 DOM 조작이 거의 없고 프레임워크가 이미 안전한 렌더링 경로를 강하게 강제하는 프로젝트라면 체감 이익이 작을 수 있다. 반대로 레거시 JS와 직접적인 DOM 조작이 많은 앱은 도입 가치가 더 크다.&lt;/p&gt;

&lt;h3&gt;Q. 언제 enforcing 모드로 전환하는 것이 좋을까?&lt;/h3&gt;
&lt;p&gt;report-only에서 주요 위반이 정리되고, 필요한 정책 이름이 고정되고, 핵심 화면 회귀 테스트를 통과한 뒤가 일반적인 전환 시점이다. web.dev도 위반 수집 후 enforcing 헤더로 바꾸는 순서를 안내한다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;Trusted Types는 위험한 DOM sink에 문자열이 바로 들어가는 경로를 줄이기 위한 브라우저 보안 장치다. 실무에서는 sanitizer나 허용 목록을 담은 정책을 먼저 만들고, &lt;code&gt;require-trusted-types-for&lt;/code&gt;는 report-only로 시작한 뒤 위반을 정리하고 enforcing으로 옮기는 순서가 가장 무난하다.&lt;/p&gt;
&lt;p&gt;2026년 6월 17일 기준 공개 문서를 보면 default policy는 과도기 도구로 보고, 장기적으로는 명시적 정책과 제한된 정책 이름 목록으로 수렴하는 방향이 권장된다. 즉 Trusted Types의 핵심은 &quot;문자열 금지&quot;보다 &quot;어디서 어떤 규칙으로 안전한 값을 만들지 중앙화&quot;에 있다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN: Trusted Types API&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://w3c.github.io/trusted-types/dist/spec/&quot; rel=&quot;noopener noreferrer&quot;&gt;W3C Editor's Draft: Trusted Types&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://web.dev/articles/trusted-types&quot; rel=&quot;noopener noreferrer&quot;&gt;web.dev: Prevent DOM-based cross-site scripting vulnerabilities with Trusted Types&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/HTML, Javascript, CSS</category>
      <category>content security policy</category>
      <category>CSP</category>
      <category>DOM XSS</category>
      <category>Frontend Security</category>
      <category>Trusted Types</category>
      <category>TrustedHTML</category>
      <category>web security</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/616</guid>
      <comments>https://jcdgods.tistory.com/616#entry616comment</comments>
      <pubDate>Wed, 17 Jun 2026 11:54:24 +0900</pubDate>
    </item>
    <item>
      <title>PostgreSQL statement_timeout 기준 정리</title>
      <link>https://jcdgods.tistory.com/615</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhHyeu/dJMcadbeWkg/525tvnX0kMDKVrcpEUReZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhHyeu/dJMcadbeWkg/525tvnX0kMDKVrcpEUReZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhHyeu/dJMcadbeWkg/525tvnX0kMDKVrcpEUReZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhHyeu%2FdJMcadbeWkg%2F525tvnX0kMDKVrcpEUReZ0%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;PostgreSQL statement_timeout 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;statement_timeout&lt;/code&gt;은 PostgreSQL이 너무 오래 걸리는 SQL 문장을 언제 중단할지 정하는 제한 시간이다. 2026년 6월 16일 기준 PostgreSQL current 공식 문서는 PostgreSQL 18 문서이며, 이 문서에서 &lt;code&gt;statement_timeout&lt;/code&gt; 기본값은 &lt;code&gt;0&lt;/code&gt;으로 안내된다. 즉 기본 상태에서는 시간 제한 없이 문장을 실행한다.&lt;/p&gt;
&lt;p&gt;실무에서는 이 값을 전역으로 바로 켜도 되는지, &lt;code&gt;lock_timeout&lt;/code&gt;이나 &lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt;과 무엇이 다른지, 세션 단위와 역할 단위 중 어디에 두는 편이 안전한지가 자주 헷갈린다. 이 글은 PostgreSQL 공식 문서 기준으로 &lt;code&gt;statement_timeout&lt;/code&gt;의 의미, 적용 범위, 관련 timeout과의 차이, 무난한 적용 기준을 정리한다.&lt;/p&gt;

&lt;h2&gt;statement_timeout은 무엇을 제한할까?&lt;/h2&gt;
&lt;p&gt;공식 문서에서 &lt;code&gt;statement_timeout&lt;/code&gt;은 지정된 시간보다 오래 걸리는 statement를 중단하는 설정으로 설명된다. 단위를 생략하면 밀리초로 해석되고, &lt;code&gt;0&lt;/code&gt;은 timeout 비활성화다.&lt;/p&gt;
&lt;p&gt;핵심은 이 값이 세션 전체 시간을 재는 것이 아니라 개별 SQL 문장의 서버 처리 시간을 기준으로 동작한다는 점이다. 오래 걸리는 조회, 대기 중인 갱신, 비효율적인 집계처럼 한 문장이 오래 붙잡고 있으면 그 문장을 중단한다.&lt;/p&gt;

&lt;h2&gt;기본값과 공식 문서 기준 동작은?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;항목&lt;/th&gt;
      &lt;th&gt;공식 문서 기준&lt;/th&gt;
      &lt;th&gt;실무 해석&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;기본값&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;기본 상태에서는 timeout이 없다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;단위 생략&lt;/td&gt;
      &lt;td&gt;밀리초&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;5000&lt;/code&gt;은 5초와 같다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;simple query protocol&lt;/td&gt;
      &lt;td&gt;쿼리 문자열 안의 각 statement마다 따로 적용&lt;/td&gt;
      &lt;td&gt;세미콜론으로 여러 문장을 보내면 각 문장을 개별로 잰다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;extended query protocol&lt;/td&gt;
      &lt;td&gt;query 관련 메시지가 도착할 때 시작되고 &lt;code&gt;Execute&lt;/code&gt; 또는 &lt;code&gt;Sync&lt;/code&gt; 완료 시 취소&lt;/td&gt;
      &lt;td&gt;드라이버 동작 방식에 따라 체감 범위가 달라질 수 있다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;공식 문서는 PostgreSQL 13 이전에는 simple query 문자열 전체에 timeout을 적용하는 경우가 많았지만, 현재 문서 기준으로는 하나의 simple-query message 안에 여러 SQL 문장이 있어도 각 statement에 개별 적용된다고 설명한다.&lt;/p&gt;

&lt;h2&gt;전역 설정으로 바로 넣는 것이 왜 조심스러울까?&lt;/h2&gt;
&lt;p&gt;공식 문서는 &lt;code&gt;statement_timeout&lt;/code&gt;을 &lt;code&gt;postgresql.conf&lt;/code&gt;에 두는 것은 권장하지 않는다고 명시한다. 이유는 모든 세션에 영향을 주기 때문이다.&lt;/p&gt;
&lt;p&gt;이 문장 하나만으로도 운영 기준이 분명해진다. 배치 작업, 마이그레이션, 분석 쿼리, 관리 세션까지 같은 제한을 받으면 예상보다 넓은 범위에서 중단이 발생할 수 있다. 따라서 대부분의 환경에서는 먼저 세션, 역할, 또는 특정 데이터베이스 범위에서 좁게 적용하는 편이 더 안전하다.&lt;/p&gt;

&lt;h2&gt;어디에 적용하는 것이 무난할까?&lt;/h2&gt;
&lt;p&gt;공식 문서 기준으로 &lt;code&gt;SET&lt;/code&gt;은 현재 세션에만 영향을 준다. 또한 &lt;code&gt;ALTER ROLE ... SET&lt;/code&gt;과 &lt;code&gt;ALTER DATABASE ... SET&lt;/code&gt;은 이후 새로 시작되는 세션의 기본값을 바꾼다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;애플리케이션 연결 하나만 시험할 때: &lt;code&gt;SET statement_timeout = '5s'&lt;/code&gt;처럼 세션 단위 적용이 가장 안전하다.&lt;/li&gt;
  &lt;li&gt;특정 애플리케이션용 DB 계정이 분리돼 있을 때: &lt;code&gt;ALTER ROLE ... SET&lt;/code&gt;으로 역할 단위 기본값을 주는 편이 운영 경계를 나누기 쉽다.&lt;/li&gt;
  &lt;li&gt;하나의 데이터베이스 전체에 공통 정책이 필요할 때: &lt;code&gt;ALTER DATABASE ... SET&lt;/code&gt;을 검토할 수 있다.&lt;/li&gt;
  &lt;li&gt;서버 전체 기본값을 한 번에 바꿀 때: 공식 문서가 비권장하므로 신중해야 한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code&gt;SET statement_timeout = '5s';

ALTER ROLE app_user SET statement_timeout = '5s';

ALTER DATABASE app_db SET statement_timeout = '5s';
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;lock_timeout, idle_in_transaction_session_timeout, transaction_timeout과는 무엇이 다를까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;statement_timeout&lt;/code&gt;은 문장 전체 실행 시간을 본다. 반면 &lt;code&gt;lock_timeout&lt;/code&gt;은 락을 기다리는 시간만 본다. 공식 문서는 &lt;code&gt;statement_timeout&lt;/code&gt;이 0이 아닌데 &lt;code&gt;lock_timeout&lt;/code&gt;을 그보다 같거나 더 크게 두는 것은 실익이 적다고 설명한다. 먼저 &lt;code&gt;statement_timeout&lt;/code&gt;이 발동하기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt;은 열린 트랜잭션 안에서 클라이언트 질의를 기다리며 idle 상태로 머무는 세션을 종료한다. 이는 긴 statement를 끊는 용도가 아니라, 열린 트랜잭션이 vacuum을 방해하거나 락을 오래 잡는 상황을 줄이기 위한 설정이다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;transaction_timeout&lt;/code&gt;은 트랜잭션 전체 지속 시간을 제한한다. 공식 문서는 이 값이 &lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt;이나 &lt;code&gt;statement_timeout&lt;/code&gt;보다 짧거나 같으면 더 긴 timeout은 무시된다고 설명한다. 따라서 세 값을 같이 둘 때는 무엇을 먼저 끊고 싶은지 기준을 분명히 해야 한다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;대부분의 서비스에서는 어떤 기준으로 시작하면 될까?&lt;/h2&gt;
&lt;p&gt;공식 문서는 구체적인 초 단위 기본값을 강제하지 않는다. 따라서 절대값을 일반론으로 단정하는 대신, 애플리케이션 응답 기대치와 예외 작업의 존재 여부를 먼저 봐야 한다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;온라인 요청 중심 서비스: 애플리케이션 계정에만 보수적인 &lt;code&gt;statement_timeout&lt;/code&gt;을 두고, 배치 계정은 분리하는 구성이 무난하다.&lt;/li&gt;
  &lt;li&gt;운영 콘솔과 배치가 같은 계정을 쓰는 환경: 전역 적용보다 세션 단위 적용부터 시작하는 편이 안전하다.&lt;/li&gt;
  &lt;li&gt;긴 분석 쿼리가 필요한 환경: 짧은 timeout을 공통 기본값으로 두면 운영자 쿼리까지 자주 중단될 수 있다.&lt;/li&gt;
  &lt;li&gt;락 대기가 문제인 환경: &lt;code&gt;statement_timeout&lt;/code&gt;만 올리기보다 &lt;code&gt;lock_timeout&lt;/code&gt;과 함께 역할을 나눠서 봐야 한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;1. statement_timeout을 걸면 락 대기도 포함될까?&lt;/h3&gt;
&lt;p&gt;포함된다. &lt;code&gt;statement_timeout&lt;/code&gt;은 statement가 서버에 도착해 완료될 때까지를 본다. 락 대기만 따로 제한하고 싶다면 &lt;code&gt;lock_timeout&lt;/code&gt;이 더 직접적인 설정이다.&lt;/p&gt;

&lt;h3&gt;2. 세미콜론으로 여러 문장을 한 번에 보내면 전체 합산으로 재나?&lt;/h3&gt;
&lt;p&gt;현재 공식 문서 기준으로 simple-query message 안의 각 statement에 개별 적용된다. 다만 PostgreSQL 13 이전에는 전체 query string처럼 취급하는 경우가 많았다는 설명도 함께 확인할 수 있다.&lt;/p&gt;

&lt;h3&gt;3. 이미 접속 중인 세션에도 ALTER ROLE SET이 바로 적용될까?&lt;/h3&gt;
&lt;p&gt;아니다. 공식 문서는 역할이나 데이터베이스 기본값 변경이 이후 새로 시작되는 세션의 기본값이 된다고 설명한다. 이미 연결된 세션은 다시 접속해야 새 기본값을 받는다.&lt;/p&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. 모든 서버에 공통으로 5초를 넣어도 될까?&lt;/h3&gt;
&lt;p&gt;공식 문서는 서버 전체 &lt;code&gt;postgresql.conf&lt;/code&gt; 설정을 권장하지 않는다. 같은 수치라도 웹 요청용 계정과 분석용 계정의 요구가 다를 수 있으므로, 범위를 좁혀 적용하는 편이 더 안전하다.&lt;/p&gt;

&lt;h3&gt;Q. statement_timeout만 있으면 idle 트랜잭션 문제도 해결될까?&lt;/h3&gt;
&lt;p&gt;아니다. 열린 트랜잭션 안에서 아무 질의도 보내지 않고 대기하는 문제는 &lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt;이 직접 다루는 영역이다.&lt;/p&gt;

&lt;h3&gt;Q. timeout이 나면 로그에 SQL이 남을까?&lt;/h3&gt;
&lt;p&gt;공식 문서는 &lt;code&gt;log_min_error_statement&lt;/code&gt;가 &lt;code&gt;ERROR&lt;/code&gt; 이하이면 timeout이 난 statement도 함께 기록된다고 설명한다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;statement_timeout&lt;/code&gt;은 오래 걸리는 SQL 문장을 중단하는 안전장치이고, 2026년 6월 16일 기준 PostgreSQL current 공식 문서에서는 기본값을 &lt;code&gt;0&lt;/code&gt;으로 설명한다. 핵심 판단 기준은 서버 전체 공통값을 둘지보다, 어떤 세션과 역할에만 적용할지를 먼저 좁히는 것이다.&lt;/p&gt;
&lt;p&gt;대부분의 운영 환경에서는 &lt;code&gt;postgresql.conf&lt;/code&gt; 전역값보다 세션, 역할, 데이터베이스 단위 설정이 더 예측 가능하다. 그리고 락 대기, idle 트랜잭션, 트랜잭션 전체 시간은 각각 &lt;code&gt;lock_timeout&lt;/code&gt;, &lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt;, &lt;code&gt;transaction_timeout&lt;/code&gt;과 구분해서 보는 편이 정확하다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/runtime-config-client.html&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL Documentation: Client Connection Defaults&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/sql-set.html&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL Documentation: SET&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/sql-alterrole.html&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL Documentation: ALTER ROLE&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/sql-alterdatabase.html&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL Documentation: ALTER DATABASE&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/서버, DBMS</category>
      <category>Database Performance</category>
      <category>DBMS</category>
      <category>idle_in_transaction_session_timeout</category>
      <category>lock_timeout</category>
      <category>PostgreSQL</category>
      <category>Server Configuration</category>
      <category>statement_timeout</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/615</guid>
      <comments>https://jcdgods.tistory.com/615#entry615comment</comments>
      <pubDate>Tue, 16 Jun 2026 12:44:41 +0900</pubDate>
    </item>
    <item>
      <title>systemd Restart 정책 기준 정리</title>
      <link>https://jcdgods.tistory.com/614</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mycDd/dJMcagZ7EQr/kjI1dwUnLYK1FtfRRcwOm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mycDd/dJMcagZ7EQr/kjI1dwUnLYK1FtfRRcwOm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mycDd/dJMcagZ7EQr/kjI1dwUnLYK1FtfRRcwOm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmycDd%2FdJMcagZ7EQr%2FkjI1dwUnLYK1FtfRRcwOm1%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;systemd Restart 정책 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;Restart=&lt;/code&gt;는 서비스가 종료됐을 때 언제 다시 띄울지, &lt;code&gt;RestartSec=&lt;/code&gt;는 얼마나 기다렸다가 다시 띄울지, &lt;code&gt;StartLimitIntervalSec=&lt;/code&gt;와 &lt;code&gt;StartLimitBurst=&lt;/code&gt;는 재시도 폭주를 어디서 끊을지를 정하는 설정이다. 2026년 6월 15일 기준 upstream systemd 공식 문서를 보면 장기 실행 서비스의 기본 선택지는 대체로 &lt;code&gt;Restart=on-failure&lt;/code&gt;이고, 시작 제한은 별도로 생각해야 한다.&lt;/p&gt;
&lt;p&gt;실무에서 자주 생기는 문제는 두 가지다. 실패 복구를 위해 자동 재시작을 켰는데 정상 종료까지 다시 올라오거나, 반대로 장애가 반복될 때 재시작이 너무 빠르게 반복되어 로그만 쌓이는 경우다. 이 글은 공식 문서 기준으로 각 옵션의 의미와 무난한 선택 기준만 정리한다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;Restart=&lt;/code&gt;는 무엇을 기준으로 동작할까?&lt;/h2&gt;
&lt;p&gt;systemd 공식 문서에 따르면 &lt;code&gt;Restart=&lt;/code&gt;는 메인 프로세스의 종료뿐 아니라 &lt;code&gt;ExecStartPre=&lt;/code&gt;, &lt;code&gt;ExecStartPost=&lt;/code&gt;, &lt;code&gt;ExecStop=&lt;/code&gt;, &lt;code&gt;ExecStopPost=&lt;/code&gt;, &lt;code&gt;ExecReload=&lt;/code&gt;에 지정한 프로세스 종료와 일부 timeout까지 포함해 재시작 여부를 판단한다. 다만 &lt;code&gt;systemctl stop&lt;/code&gt;처럼 systemd가 의도적으로 내린 종료는 재시작 대상이 아니다.&lt;/p&gt;
&lt;p&gt;같은 문서에서 clean exit은 기본적으로 종료 코드 &lt;code&gt;0&lt;/code&gt;, 그리고 &lt;code&gt;Type=oneshot&lt;/code&gt;이 아닐 때 &lt;code&gt;SIGHUP&lt;/code&gt;, &lt;code&gt;SIGINT&lt;/code&gt;, &lt;code&gt;SIGTERM&lt;/code&gt;, &lt;code&gt;SIGPIPE&lt;/code&gt;로 설명된다. 여기에 &lt;code&gt;SuccessExitStatus=&lt;/code&gt;를 추가하면 특정 종료 코드를 정상 종료로 더 넓게 볼 수 있다.&lt;/p&gt;

&lt;h2&gt;어떤 &lt;code&gt;Restart=&lt;/code&gt; 값을 먼저 검토하면 될까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;값&lt;/th&gt;
      &lt;th&gt;의미&lt;/th&gt;
      &lt;th&gt;우선 고려할 상황&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;no&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;자동 재시작 안 함&lt;/td&gt;
      &lt;td&gt;실패 후 운영자 판단이 필요한 작업성 서비스&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;on-failure&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;비정상 종료, signal 종료, timeout, watchdog 시 재시작&lt;/td&gt;
      &lt;td&gt;대부분의 장기 실행 데몬&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;on-abnormal&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;signal 종료, timeout, watchdog일 때만 재시작&lt;/td&gt;
      &lt;td&gt;애플리케이션이 스스로 종료 코드로 종료 의사를 표현할 수 있을 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;on-watchdog&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;watchdog timeout일 때만 재시작&lt;/td&gt;
      &lt;td&gt;watchdog 기반 장애만 별도로 보고 싶을 때&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;always&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;정상 종료까지 포함해 거의 항상 재시작&lt;/td&gt;
      &lt;td&gt;상시 상주가 절대 조건인 프로세스이면서 정상 종료 개념이 사실상 없을 때&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;공식 문서는 장기 실행 서비스라면 &lt;code&gt;on-failure&lt;/code&gt;를 권장 선택지로 본다. 이유는 오류 복구를 시도하면서도 의도적인 정상 종료까지 다시 띄우는 부작용을 피하기 쉽기 때문이다.&lt;/p&gt;
&lt;p&gt;반대로 배치형 작업이나 상태 점검 도구처럼 종료 코드로 &quot;이번 실행은 여기까지&quot;를 표현하는 프로그램은 &lt;code&gt;on-abnormal&lt;/code&gt;이 더 맞을 수 있다. 이 경우 비정상 신호 종료나 timeout은 다시 시도하되, 애플리케이션이 스스로 끝낸 정상 종료는 그대로 받아들일 수 있다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;RestartSec=&lt;/code&gt;를 왜 꼭 같이 봐야 할까?&lt;/h2&gt;
&lt;p&gt;공식 문서 기준 &lt;code&gt;RestartSec=&lt;/code&gt;의 기본값은 &lt;code&gt;100ms&lt;/code&gt;다. 즉 &lt;code&gt;Restart=on-failure&lt;/code&gt;만 켜 두면 실패 직후 매우 짧은 간격으로 재시작을 시도할 수 있다.&lt;/p&gt;
&lt;p&gt;이 기본값이 항상 잘못된 것은 아니지만, 설정 오류나 포트 충돌처럼 즉시 반복 실패하는 장애에서는 너무 공격적일 수 있다. 로그 회전 부담, 외부 의존성 과부하, rate limit 조기 도달을 생각하면 대부분의 네트워크 서비스는 몇 초 단위 지연을 주는 편이 운영상 읽기 쉽다.&lt;/p&gt;
&lt;p&gt;예를 들어 외부 DB 연결이 잠깐 흔들릴 수 있는 API 서버라면 &lt;code&gt;RestartSec=3s&lt;/code&gt; 또는 &lt;code&gt;5s&lt;/code&gt;처럼 완만한 간격이 무난하다. 반면 아주 짧은 장애에도 즉시 복구가 중요하고 실패 원인이 일시적이라고 보는 로컬 보조 프로세스는 더 짧은 간격이 맞을 수 있다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;StartLimitIntervalSec=&lt;/code&gt;와 &lt;code&gt;StartLimitBurst=&lt;/code&gt;는 무엇을 막을까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Restart=&lt;/code&gt;가 &quot;다시 시작할지&quot;를 정한다면, &lt;code&gt;StartLimitIntervalSec=&lt;/code&gt;와 &lt;code&gt;StartLimitBurst=&lt;/code&gt;는 &quot;얼마나 자주 시작을 허용할지&quot;를 정한다. 공식 문서에 따르면 정해진 interval 안에서 burst 횟수를 넘기면 더 이상 시작이 허용되지 않는다.&lt;/p&gt;
&lt;p&gt;중요한 점은 이 제한이 자동 재시작에만 적용되지 않는다는 것이다. upstream 문서는 수동 시작까지 포함한 모든 start에 적용된다고 설명한다. 그래서 서비스가 계속 실패한 뒤 &lt;code&gt;systemctl start&lt;/code&gt;를 눌렀는데 바로 안 올라오는 상황은 이 설정과 연관될 수 있다.&lt;/p&gt;
&lt;p&gt;또한 공식 문서는 &lt;code&gt;systemctl reset-failed&lt;/code&gt;가 rate counter를 비우는 데 쓰인다고 설명한다. 즉 원인을 고친 뒤 즉시 다시 올려야 할 때는 단순 start보다 reset 후 start가 필요한 경우가 있다.&lt;/p&gt;

&lt;h2&gt;운영에서 무난한 조합은 어떻게 잡을까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;무난한 출발점&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;일반적인 웹 API, 워커, 에이전트&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;Restart=on-failure&lt;/code&gt; + &lt;code&gt;RestartSec=3s&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;비정상 종료는 복구하고 재시작 폭주는 완화&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;정상 종료 코드가 의미 있는 배치형 데몬&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;Restart=on-abnormal&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;프로그램이 의도적으로 종료한 경우까지 다시 띄우지 않음&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;짧은 시간에 반복 실패 가능성이 큰 서비스&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;Restart=on-failure&lt;/code&gt; + 시작 제한 추가&lt;/td&gt;
      &lt;td&gt;장애 반복 시 무한 재시작 루프를 차단&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;구체적인 숫자는 애플리케이션 특성에 따라 달라진다. 다만 재시작 정책과 시작 제한을 분리해서 생각하면 판단이 쉬워진다. 먼저 실패 시 다시 띄울지 정하고, 그다음 너무 자주 띄우는 것을 어디서 멈출지 정하는 순서가 안전하다.&lt;/p&gt;

&lt;h2&gt;정상 종료를 예외 처리해야 하면 무엇을 쓸까?&lt;/h2&gt;
&lt;p&gt;애플리케이션이 특정 종료 코드를 &quot;재시작하지 말아야 하는 종료&quot;로 사용한다면 &lt;code&gt;RestartPreventExitStatus=&lt;/code&gt;를 함께 볼 수 있다. 공식 문서 기준 이 옵션은 메인 서비스 프로세스가 특정 종료 코드를 반환했을 때, &lt;code&gt;Restart=&lt;/code&gt; 설정과 무관하게 자동 재시작을 막는다.&lt;/p&gt;
&lt;p&gt;반대로 특정 종료 코드는 강제로 재시작 대상으로 보고 싶다면 &lt;code&gt;RestartForceExitStatus=&lt;/code&gt;가 있다. 다만 이 두 옵션은 보조 명령이 아니라 메인 서비스 프로세스 기준으로 해석되므로, pre/post hook의 실패 제어까지 동일하게 된다고 보면 안 된다.&lt;/p&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;Q. &lt;code&gt;Restart=always&lt;/code&gt;가 가장 안전한 기본값일까?&lt;/h3&gt;
&lt;p&gt;그렇게 보기 어렵다. 정상 종료까지 다시 띄우므로, 프로그램이 의도적으로 종료해도 계속 살아나야 하는 프로세스가 아니라면 과한 설정이 될 수 있다. 장기 실행 데몬의 기본 선택지는 공식 문서 기준으로 &lt;code&gt;on-failure&lt;/code&gt;가 더 무난하다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;Type=oneshot&lt;/code&gt;에도 같은 기준을 적용할 수 있을까?&lt;/h3&gt;
&lt;p&gt;공식 문서는 &lt;code&gt;Type=oneshot&lt;/code&gt;에 대해 clean exit 기준의 자동 재시작에서 제한이 있다고 설명한다. 특히 &lt;code&gt;always&lt;/code&gt;와 &lt;code&gt;on-success&lt;/code&gt;는 허용되지 않는다. 그래서 일회성 작업 서비스는 장기 실행 데몬과 같은 재시작 전략으로 보면 안 된다.&lt;/p&gt;

&lt;h3&gt;Q. 시작 제한에 걸리면 영구적으로 막힐까?&lt;/h3&gt;
&lt;p&gt;아니다. 공식 문서 기준 interval이 지난 뒤에는 다시 시작을 시도할 수 있고, 관리자가 &lt;code&gt;systemctl reset-failed&lt;/code&gt;로 카운터를 비울 수도 있다. 다만 원인 수정 없이 반복 시작만 하면 같은 제한에 다시 걸릴 수 있다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Restart=&lt;/code&gt;는 장애 복구 정책이고, &lt;code&gt;RestartSec=&lt;/code&gt;는 재시도 간격, &lt;code&gt;StartLimitIntervalSec=&lt;/code&gt;와 &lt;code&gt;StartLimitBurst=&lt;/code&gt;는 재시도 상한선이다. 이 셋을 분리해서 보면 대부분의 서비스는 &lt;code&gt;Restart=on-failure&lt;/code&gt;를 시작점으로 삼고, 서비스 특성에 맞는 지연과 시작 제한을 추가하는 식으로 정리할 수 있다.&lt;/p&gt;
&lt;p&gt;2026년 6월 15일 기준 upstream systemd 공식 문서를 기준으로 보면 장기 실행 서비스에 가장 흔한 실수는 재시작 여부만 정하고 재시도 간격과 rate limit을 비워 두는 것이다. 자동 복구가 필요할수록 재시작 정책과 폭주 방지 정책을 같이 적는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html&quot; rel=&quot;noopener noreferrer&quot;&gt;systemd 공식 문서: systemd.service&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html&quot; rel=&quot;noopener noreferrer&quot;&gt;systemd 공식 문서: systemd.unit&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/systemd/systemd/blob/main/man/systemd.service.xml&quot; rel=&quot;noopener noreferrer&quot;&gt;systemd GitHub 문서 원본: systemd.service.xml&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/systemd/systemd/blob/main/man/systemd.unit.xml&quot; rel=&quot;noopener noreferrer&quot;&gt;systemd GitHub 문서 원본: systemd.unit.xml&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/운영체제</category>
      <category>Linux</category>
      <category>restart</category>
      <category>RestartSec</category>
      <category>StartLimitBurst</category>
      <category>StartLimitIntervalSec</category>
      <category>systemd</category>
      <category>운영체제</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/614</guid>
      <comments>https://jcdgods.tistory.com/614#entry614comment</comments>
      <pubDate>Mon, 15 Jun 2026 11:25:38 +0900</pubDate>
    </item>
    <item>
      <title>Git fetch.prune 사용법</title>
      <link>https://jcdgods.tistory.com/613</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pLOuM/dJMcadCf5X8/5ATx9kKgPwNRQHXqDAGfB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pLOuM/dJMcadCf5X8/5ATx9kKgPwNRQHXqDAGfB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pLOuM/dJMcadCf5X8/5ATx9kKgPwNRQHXqDAGfB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpLOuM%2FdJMcadCf5X8%2F5ATx9kKgPwNRQHXqDAGfB1%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Git fetch.prune 사용법&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;git fetch --prune&lt;/code&gt;와 &lt;code&gt;fetch.prune&lt;/code&gt;는 원격에서 이미 삭제된 브랜치에 대응하는 stale remote-tracking ref를 정리할 때 쓰는 설정이다. 2026년 6월 14일 기준 Git 공식 문서는 &lt;code&gt;--prune&lt;/code&gt;가 fetch 전에 원격에 더 이상 존재하지 않는 remote-tracking reference를 제거한다고 설명한다.&lt;/p&gt;
&lt;p&gt;실무에서 중요한 점은 이것이 로컬 브랜치를 지우는 기능이 아니라는 점이다. 기본 대상은 &lt;code&gt;origin/feature-x&lt;/code&gt; 같은 remote-tracking ref이며, 태그는 기본 자동 추적만으로 받아온 경우 pruning 대상이 아니다. 이 글은 Git 공식 문서를 기준으로 &lt;code&gt;git fetch --prune&lt;/code&gt;, &lt;code&gt;fetch.prune&lt;/code&gt;, &lt;code&gt;remote.&amp;lt;name&amp;gt;.prune&lt;/code&gt;, &lt;code&gt;--prune-tags&lt;/code&gt;를 언제 구분해야 하는지 정리한다.&lt;/p&gt;

&lt;h2&gt;Git fetch.prune은 무엇을 정리할까?&lt;/h2&gt;
&lt;p&gt;Git 공식 문서의 PRUNING 설명에 따르면 Git은 기본적으로 데이터를 쉽게 버리지 않으며, 원격에서 이미 삭제된 브랜치에 대한 로컬 참조도 계속 남겨 둔다. &lt;code&gt;--prune&lt;/code&gt;를 주면 이런 stale reference를 fetch 전에 정리한다.&lt;/p&gt;
&lt;p&gt;여기서 정리 대상은 일반적으로 &lt;code&gt;refs/remotes/origin/*&lt;/code&gt; 아래의 remote-tracking ref다. 따라서 &lt;code&gt;git branch -a&lt;/code&gt; 결과가 실제 원격 상태보다 지저분해졌거나, 브랜치 churn이 많은 저장소에서 오래된 원격 브랜치 흔적이 계속 남는다면 pruning이 직접적인 해결책이다.&lt;/p&gt;

&lt;h2&gt;로컬 브랜치 삭제와는 왜 다를까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git fetch --prune&lt;/code&gt;는 원격 추적 참조를 정리하는 동작이지, 사용자가 만든 로컬 브랜치를 직접 지우는 명령이 아니다. 예를 들어 원격의 &lt;code&gt;feature/login&lt;/code&gt;이 삭제되어도 로컬의 &lt;code&gt;feature/login&lt;/code&gt; 브랜치는 그대로 남을 수 있다.&lt;/p&gt;
&lt;p&gt;즉 &quot;원격에는 없지만 내 작업 기록으로는 남겨 둘 로컬 브랜치&quot;와 &quot;원격 상태를 반영하기 위한 추적 참조&quot;는 분리해서 봐야 한다. 이 차이를 이해하지 않으면 pruning을 과하게 위험한 기능으로 오해하기 쉽다.&lt;/p&gt;

&lt;h2&gt;한 번만 정리할 때와 항상 정리할 때는 어떻게 나눌까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;권장 방법&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;지금 한 번만 stale ref를 정리하고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git fetch --prune origin&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;명령 한 번에만 pruning을 적용할 수 있다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;대부분의 저장소에서 fetch마다 자동 정리하고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git config --global fetch.prune true&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;모든 fetch가 기본적으로 &lt;code&gt;--prune&lt;/code&gt;처럼 동작한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;특정 remote에만 적용하고 싶다&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git config remote.origin.prune true&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;remote별로 정책을 다르게 둘 수 있다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Git 공식 문서는 &lt;code&gt;fetch.prune=true&lt;/code&gt;이면 fetch가 자동으로 &lt;code&gt;--prune&lt;/code&gt;를 준 것처럼 동작한다고 설명한다. 동시에 &lt;code&gt;remote.&amp;lt;name&amp;gt;.prune&lt;/code&gt;로 per-remote 설정도 둘 수 있으므로, 사내 mirror와 일반 origin의 성격이 다를 때 구분해서 쓰기 좋다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;git remote prune&lt;/code&gt;는 언제 쓰면 될까?&lt;/h2&gt;
&lt;p&gt;Git 공식 문서에서 &lt;code&gt;git remote prune &amp;lt;name&amp;gt;&lt;/code&gt;은 stale reference를 삭제하지만 새 ref를 fetch하지 않는 명령으로 설명된다. 문서상 동작 의미는 &lt;code&gt;git fetch --prune &amp;lt;name&amp;gt;&lt;/code&gt;와 가깝지만, 새로 받을 변경이 없다는 점이 차이다.&lt;/p&gt;
&lt;p&gt;그래서 네트워크 fetch는 굳이 하지 않고 참조만 정리하고 싶을 때 &lt;code&gt;git remote prune&lt;/code&gt;가 더 직접적이다. 변경 전 영향 범위를 보고 싶다면 &lt;code&gt;git remote prune --dry-run origin&lt;/code&gt;으로 먼저 확인하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;태그까지 같이 지워질 수 있다는 말은 무슨 뜻일까?&lt;/h2&gt;
&lt;p&gt;Git 공식 문서는 기본 자동 tag auto-follow나 &lt;code&gt;--tags&lt;/code&gt;만으로 받아온 태그는 pruning 대상이 아니라고 설명한다. 하지만 explicit refspec으로 태그를 가져오거나 &lt;code&gt;--prune-tags&lt;/code&gt;를 함께 쓰는 경우에는 로컬 태그도 pruning 대상이 될 수 있다.&lt;/p&gt;
&lt;p&gt;문서가 특히 주의하라고 하는 지점도 여기다. &lt;code&gt;refs/tags/*:refs/tags/*&lt;/code&gt; 같은 refspec은 여러 remote가 같은 로컬 태그 네임스페이스를 공유할 수 있으므로, 단순히 &quot;원격 브랜치 정리&quot;라고 생각하고 적용하면 예상보다 넓은 삭제가 일어날 수 있다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;fetch.pruneTags&lt;/code&gt;는 언제 켜는 편이 맞을까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;fetch.pruneTags=true&lt;/code&gt;는 pruning이 활성화된 fetch에서 tag refspec이 함께 적용되도록 만드는 설정이다. Git 공식 문서는 이 설정이 &lt;code&gt;fetch.prune&lt;/code&gt;와 결합돼 upstream과 1:1 매핑을 유지하려는 경우에 유용하다고 설명한다.&lt;/p&gt;
&lt;p&gt;다만 모든 저장소에 무조건 권장되는 기본값으로 보기는 어렵다. 로컬 전용 태그를 자주 만들거나, 여러 remote를 다루는 저장소라면 태그 정리는 브랜치 pruning보다 훨씬 보수적으로 봐야 한다.&lt;/p&gt;

&lt;h2&gt;실무에서는 어떤 기본값이 무난할까?&lt;/h2&gt;
&lt;p&gt;대부분의 일반적인 개발 저장소에서는 &lt;code&gt;fetch.prune=true&lt;/code&gt; 정도가 무난한 기본값이다. 원격에서 이미 사라진 브랜치 참조를 계속 들고 있을 이유가 적고, &lt;code&gt;git branch -a&lt;/code&gt;나 각종 브랜치 목록 기반 도구의 잡음을 줄이는 데 도움이 된다.&lt;/p&gt;
&lt;p&gt;반면 &lt;code&gt;fetch.pruneTags&lt;/code&gt;까지 기본 활성화할지는 태그 운영 정책을 확인한 뒤 결정하는 편이 낫다. 공식 문서도 tag pruning이 별도 refspec과 연결된 동작이라는 점을 분명히 설명하므로, 브랜치 pruning과 같은 위험도로 보면 안 된다.&lt;/p&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. &lt;code&gt;git fetch --prune&lt;/code&gt;를 실행하면 내 로컬 브랜치도 같이 지워지나?&lt;/h3&gt;
&lt;p&gt;기본적으로는 아니다. Git 공식 문서 기준으로 pruning의 일반 대상은 원격에 더 이상 존재하지 않는 remote-tracking ref다.&lt;/p&gt;

&lt;h3&gt;Q. 자동화하고 싶으면 &lt;code&gt;fetch.prune&lt;/code&gt;와 &lt;code&gt;remote.origin.prune&lt;/code&gt; 중 무엇을 쓰면 될까?&lt;/h3&gt;
&lt;p&gt;대부분의 저장소에서 공통 정책을 쓰려면 &lt;code&gt;fetch.prune&lt;/code&gt;가 단순하다. 특정 remote에만 다르게 적용해야 한다면 &lt;code&gt;remote.&amp;lt;name&amp;gt;.prune&lt;/code&gt;가 더 정확하다.&lt;/p&gt;

&lt;h3&gt;Q. 태그도 항상 같이 정리하는 편이 좋나?&lt;/h3&gt;
&lt;p&gt;그렇게 단정하기는 어렵다. Git 공식 문서는 &lt;code&gt;--prune-tags&lt;/code&gt;와 tag refspec이 로컬 태그 삭제로 이어질 수 있다고 설명하므로, 로컬 전용 태그가 있는 저장소라면 먼저 정책을 확인해야 한다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git fetch --prune&lt;/code&gt;의 핵심은 &quot;원격에 없는 remote-tracking ref를 fetch 과정에서 정리한다&quot;는 데 있다. 일회성 정리는 &lt;code&gt;git fetch --prune origin&lt;/code&gt;, 지속적 자동화는 &lt;code&gt;fetch.prune&lt;/code&gt;, remote별 제어는 &lt;code&gt;remote.&amp;lt;name&amp;gt;.prune&lt;/code&gt;, 태그까지 1:1 동기화를 원할 때만 &lt;code&gt;--prune-tags&lt;/code&gt; 또는 &lt;code&gt;fetch.pruneTags&lt;/code&gt;를 검토하면 된다.&lt;/p&gt;
&lt;p&gt;운영 기준을 한 줄로 줄이면 이렇다. 브랜치 pruning은 대체로 기본값 후보가 될 수 있지만, tag pruning은 저장소의 태그 소유권과 refspec 구조를 확인한 뒤 켜는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-fetch&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Documentation: git-fetch&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-config&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Documentation: git-config&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-remote&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Documentation: git-remote&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/Git, IDE, 툴 관련</category>
      <category>CLI</category>
      <category>fetch.prune</category>
      <category>GIT</category>
      <category>git fetch</category>
      <category>git remote prune</category>
      <category>version control</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/613</guid>
      <comments>https://jcdgods.tistory.com/613#entry613comment</comments>
      <pubDate>Sun, 14 Jun 2026 11:08:45 +0900</pubDate>
    </item>
    <item>
      <title>Python Path.relative_to 사용법</title>
      <link>https://jcdgods.tistory.com/612</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLe6Zt/dJMcafUvK85/KhkivQmNxqXg0DpRPeR2dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLe6Zt/dJMcafUvK85/KhkivQmNxqXg0DpRPeR2dk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLe6Zt/dJMcafUvK85/KhkivQmNxqXg0DpRPeR2dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLe6Zt%2FdJMcafUvK85%2FKhkivQmNxqXg0DpRPeR2dk%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Python Path.relative_to 사용법&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;pathlib.PurePath.relative_to()&lt;/code&gt;는 기준 경로를 기준으로 상대 경로를 계산할 때 쓰는 메서드다. 다만 &lt;code&gt;os.path.relpath()&lt;/code&gt;처럼 아무 경로나 자동으로 상대화하는 함수는 아니다. Python 3.14 공식 문서는 이 메서드를 문자열 기반의 lexical 연산으로 설명하고, 조건이 맞지 않으면 &lt;code&gt;ValueError&lt;/code&gt;를 발생시킨다고 명시한다.&lt;/p&gt;
&lt;p&gt;실무 기준은 세 가지다. 기준 경로가 실제로 현재 경로의 상위 경로인지, 심볼릭 링크를 먼저 정리해야 하는지, 그리고 Python 3.12 이상에서 &lt;code&gt;walk_up=True&lt;/code&gt;가 필요한지다. 이 글은 Python 3.14 공식 문서를 기준으로 &lt;code&gt;Path.relative_to()&lt;/code&gt;를 안전하게 쓰는 판단 기준만 정리한다.&lt;/p&gt;

&lt;h2&gt;Path.relative_to는 언제 쓰면 될까?&lt;/h2&gt;
&lt;p&gt;다음 조건이면 &lt;code&gt;Path.relative_to()&lt;/code&gt;가 잘 맞는다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;두 경로의 관계를 문자열 규칙으로 확인할 수 있다.&lt;/li&gt;
  &lt;li&gt;기준 경로가 현재 경로의 실제 상위 경로인지 명확하다.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;pathlib.Path&lt;/code&gt; 기반 코드에서 상대 경로 표현만 간단히 만들고 싶다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;반대로 임의의 두 경로를 억지로라도 상대 경로로 만들고 싶다면 이 메서드의 동작을 먼저 이해해야 한다. Python 공식 문서는 입력 경로의 anchor가 다르거나 상하 관계가 맞지 않으면 &lt;code&gt;ValueError&lt;/code&gt;가 난다고 설명한다.&lt;/p&gt;

&lt;h2&gt;기본 동작은 무엇일까?&lt;/h2&gt;
&lt;p&gt;Python 3.14 문서의 시그니처는 &lt;code&gt;PurePath.relative_to(other, walk_up=False)&lt;/code&gt;다. 기본값에서는 현재 경로가 &lt;code&gt;other&lt;/code&gt;로 시작해야 하며, 그렇지 않으면 예외가 발생한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pathlib import Path

config_path = Path(&quot;/srv/app/config/settings.toml&quot;)

print(config_path.relative_to(&quot;/srv&quot;))
# app/config/settings.toml

print(config_path.relative_to(&quot;/usr&quot;))
# ValueError&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;즉 이 메서드는 &quot;가능하면 상대 경로를 만들어 준다&quot;보다 &quot;정해진 포함 관계가 맞는지 확인하면서 잘라낸다&quot;에 가깝다. 그래서 배포 경로 검증, 업로드 루트 검증, 로그 경로 정규화 같은 코드에서 더 예측 가능하다.&lt;/p&gt;

&lt;h2&gt;walk_up=True는 언제 필요할까?&lt;/h2&gt;
&lt;p&gt;Python 공식 문서는 3.12부터 &lt;code&gt;walk_up&lt;/code&gt; 매개변수가 추가됐다고 밝힌다. 이 값을 &lt;code&gt;True&lt;/code&gt;로 두면 하위 경로 관계가 아니더라도 &lt;code&gt;..&lt;/code&gt; 조각을 추가해서 상대 경로를 만들 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pathlib import Path

path = Path(&quot;/srv/app/config/settings.toml&quot;)

print(path.relative_to(&quot;/srv/app&quot;, walk_up=True))
# config/settings.toml

print(path.relative_to(&quot;/srv/log&quot;, walk_up=True))
# ../app/config/settings.toml&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 옵션은 &quot;같은 파일시스템 anchor 안에서 상대 표현이 필요하다&quot;는 조건이 분명할 때만 쓰는 편이 좋다. anchor가 다르면 여전히 &lt;code&gt;ValueError&lt;/code&gt;가 발생할 수 있다.&lt;/p&gt;

&lt;h2&gt;심볼릭 링크가 있으면 왜 resolve()를 먼저 보라고 할까?&lt;/h2&gt;
&lt;p&gt;Python 문서는 &lt;code&gt;relative_to()&lt;/code&gt;가 실제 파일시스템을 확인하지 않는 lexical 연산이라고 설명한다. 따라서 경로 문자열에 심볼릭 링크가 섞여 있으면 눈에 보이는 상하 관계와 실제 경로 구조가 다를 수 있다.&lt;/p&gt;
&lt;p&gt;공식 문서는 이런 경우 필요하면 먼저 &lt;code&gt;resolve()&lt;/code&gt;를 호출해 심볼릭 링크를 해석하라고 권고한다. 경로 접근 제어, 허용 디렉터리 검사, 파일 업로드 저장 경로 검증처럼 보안상 경계가 중요한 코드라면 이 순서가 특히 중요하다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pathlib import Path

base = Path(&quot;/srv/data&quot;).resolve()
target = Path(&quot;/srv/data/current/../reports/today.csv&quot;).resolve()

relative = target.relative_to(base)
print(relative)&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;os.path.relpath와는 무엇이 다를까?&lt;/h2&gt;
&lt;p&gt;Python 3.14 문서는 비교 표에서 &lt;code&gt;os.path.relpath()&lt;/code&gt;에 대응하는 pathlib 도구로 &lt;code&gt;PurePath.relative_to()&lt;/code&gt;를 제시하면서도, 두 동작이 완전히 같지는 않다고 별도 각주로 설명한다.&lt;/p&gt;
&lt;p&gt;공식 문서 기준으로 &lt;code&gt;os.path.relpath()&lt;/code&gt;는 내부적으로 절대 경로화와 &lt;code&gt;..&lt;/code&gt; 정리를 수행하는 반면, &lt;code&gt;relative_to()&lt;/code&gt;는 lexical 연산이어서 입력 anchor가 다르거나 절대/상대 경로 타입이 섞이면 &lt;code&gt;ValueError&lt;/code&gt;를 낸다. 즉 &quot;실패하면 예외로 드러나는 더 엄격한 API&quot;로 이해하는 편이 맞다.&lt;/p&gt;

&lt;h2&gt;실무에서는 어떤 기준이 무난할까?&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;상위 경로 포함 관계를 검증하면서 상대 경로를 만들고 싶다면 &lt;code&gt;Path.relative_to()&lt;/code&gt;가 적합하다.&lt;/li&gt;
  &lt;li&gt;심볼릭 링크나 &lt;code&gt;..&lt;/code&gt; 조각이 섞일 수 있으면 먼저 &lt;code&gt;resolve()&lt;/code&gt; 여부를 판단한다.&lt;/li&gt;
  &lt;li&gt;Python 3.11 이하 호환이 필요하면 &lt;code&gt;walk_up&lt;/code&gt;를 전제로 설계하면 안 된다.&lt;/li&gt;
  &lt;li&gt;경로 관계가 성립하지 않을 수 있다면 &lt;code&gt;try/except ValueError&lt;/code&gt;로 실패 경로를 분기한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;대부분의 서비스 코드에서는 &quot;루트 디렉터리 아래에 있는지 확인한 뒤 상대 경로만 저장&quot; 같은 패턴에 가장 잘 맞는다. 반면 사용자가 넘긴 임의의 두 경로를 무조건 상대화하는 용도라면 예외 처리 전략을 먼저 세워야 한다.&lt;/p&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Path.relative_to는 파일이 실제로 존재해야 동작할까?&lt;/h3&gt;
&lt;p&gt;아니다. Python 공식 문서는 이 메서드가 파일시스템을 확인하지 않는 lexical 연산이라고 설명한다. 따라서 경로 문자열만으로 계산되며, 존재 여부를 따로 검사하지 않는다.&lt;/p&gt;

&lt;h3&gt;walk_up=True를 쓰면 항상 상대 경로를 만들 수 있을까?&lt;/h3&gt;
&lt;p&gt;항상 그렇지는 않다. Python 공식 문서는 입력 경로의 anchor가 다르거나 절대/상대 경로 타입이 다르면 &lt;code&gt;ValueError&lt;/code&gt;가 발생할 수 있다고 설명한다.&lt;/p&gt;

&lt;h3&gt;보안 검증 코드에서 어떤 순서가 안전할까?&lt;/h3&gt;
&lt;p&gt;심볼릭 링크 가능성이 있으면 먼저 &lt;code&gt;resolve()&lt;/code&gt;로 실제 경로를 정규화한 뒤 &lt;code&gt;relative_to()&lt;/code&gt;로 허용 루트 내부인지 확인하는 편이 더 안전하다. Python 공식 문서도 심볼릭 링크가 있을 수 있으면 &lt;code&gt;resolve()&lt;/code&gt;를 먼저 호출하라고 안내한다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.python.org/3/library/pathlib.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Python 3.14 pathlib 공식 문서&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.python.org/3.12/library/pathlib.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Python 3.12 pathlib 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/C, C++, Java, Python</category>
      <category>os.path.relpath</category>
      <category>Path.relative_to</category>
      <category>pathlib</category>
      <category>python</category>
      <category>python3.12</category>
      <category>resolve</category>
      <category>walk_up</category>
      <category>표준라이브러리</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/612</guid>
      <comments>https://jcdgods.tistory.com/612#entry612comment</comments>
      <pubDate>Sat, 13 Jun 2026 10:16:13 +0900</pubDate>
    </item>
    <item>
      <title>Strict-Transport-Security 설정 기준 정리</title>
      <link>https://jcdgods.tistory.com/611</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHGQBq/dJMcacpWbEZ/JQby8oqyoYMchjw8ECMo5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHGQBq/dJMcacpWbEZ/JQby8oqyoYMchjw8ECMo5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHGQBq/dJMcacpWbEZ/JQby8oqyoYMchjw8ECMo5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHGQBq%2FdJMcacpWbEZ%2FJQby8oqyoYMchjw8ECMo5K%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Strict-Transport-Security 설정 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;Strict-Transport-Security&lt;/code&gt;는 브라우저가 특정 호스트를 앞으로는 HTTPS로만 접속하도록 기억하게 만드는 응답 헤더다. 2026년 6월 12일 기준 MDN 문서는 이 헤더가 이후의 HTTP 접속 시도를 자동으로 HTTPS로 올리고, 인증서 오류가 나면 사용자가 경고를 무시하고 진행할 수 없게 만든다고 설명한다.&lt;/p&gt;
&lt;p&gt;실무에서 자주 헷갈리는 지점은 세 가지다. &lt;code&gt;max-age&lt;/code&gt;를 얼마로 둘지, &lt;code&gt;includeSubDomains&lt;/code&gt;를 바로 켜도 되는지, 그리고 HSTS preload까지 등록해야 하는지다. 이 글은 RFC 6797, MDN, HSTS preload 공식 안내를 기준으로 선택 기준만 정리한다.&lt;/p&gt;

&lt;h2&gt;HSTS는 무엇을 보호할까?&lt;/h2&gt;
&lt;p&gt;RFC 6797은 HSTS를 통해 사용자 에이전트가 해당 호스트에 대해 안전하지 않은 전송을 사용하지 않도록 강제할 수 있다고 정의한다. 핵심은 사용자가 한 번이라도 HTTPS 응답에서 HSTS 헤더를 받으면, 다음부터는 같은 호스트에 대한 HTTP URL을 브라우저가 HTTPS로 바꿔 요청한다는 점이다.&lt;/p&gt;
&lt;p&gt;MDN은 여기에 더해 HSTS 호스트에서 TLS 인증서 오류가 발생하면 브라우저가 우회 수단을 제공하지 않는다고 설명한다. 따라서 HSTS는 단순 리디렉션보다 강한 정책이다.&lt;/p&gt;

&lt;h2&gt;헤더 값은 어떻게 구성할까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;지시어&lt;/th&gt;
      &lt;th&gt;의미&lt;/th&gt;
      &lt;th&gt;실무 기준&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;max-age&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;브라우저가 HSTS 정책을 기억할 기간(초)&lt;/td&gt;
      &lt;td&gt;운영 안정성이 확인되기 전에는 짧게 시작하고, 장기 적용이 가능하면 늘린다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;includeSubDomains&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;하위 도메인에도 동일 정책 적용&lt;/td&gt;
      &lt;td&gt;모든 서브도메인이 HTTPS를 안정적으로 제공할 때만 켠다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;preload&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;브라우저 preload 목록 제출용 선언&lt;/td&gt;
      &lt;td&gt;사전 로딩 요건을 장기적으로 만족할 수 있을 때만 검토한다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;MDN 문서의 문법 예시는 &lt;code&gt;max-age&lt;/code&gt;만 단독으로 쓰거나, 여기에 &lt;code&gt;includeSubDomains&lt;/code&gt;와 &lt;code&gt;preload&lt;/code&gt;를 붙이는 형태다. 또한 MDN은 &lt;code&gt;preload&lt;/code&gt;를 사용할 때 &lt;code&gt;max-age&lt;/code&gt;가 최소 31536000초(1년)여야 하고 &lt;code&gt;includeSubDomains&lt;/code&gt;가 함께 있어야 한다고 안내한다.&lt;/p&gt;

&lt;h2&gt;대부분의 서비스에서 먼저 볼 기준은?&lt;/h2&gt;
&lt;p&gt;첫 번째 기준은 HTTPS 운영 안정성이다. MDN에 따르면 HSTS는 인증서 오류가 나도 예외 진행이 불가능하므로, 인증서 자동 갱신과 체인 구성, 서브도메인 TLS 상태가 안정적이어야 한다.&lt;/p&gt;
&lt;p&gt;두 번째 기준은 서브도메인 범위다. RFC 6797과 MDN 모두 &lt;code&gt;includeSubDomains&lt;/code&gt;가 하위 도메인 전체에 영향을 준다고 설명한다. 개발용, 내부용, 오래된 서브도메인 중 HTTP만 쓰는 것이 하나라도 있으면 이 옵션은 바로 적용하기 어렵다.&lt;/p&gt;
&lt;p&gt;세 번째 기준은 롤백 난이도다. MDN은 HSTS 해제를 위해 &lt;code&gt;max-age=0&lt;/code&gt;을 HTTPS 응답으로 다시 보내야 하며, 누락한다고 바로 해제되지 않는다고 설명한다. 즉 잘못 길게 잡은 값은 브라우저에 남아 있기 때문에 초기에는 짧은 기간으로 검증하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;HTTP 리디렉션만 있으면 충분할까?&lt;/h2&gt;
&lt;p&gt;아니다. MDN은 HSTS를 모르는 첫 HTTP 요청은 여전히 네트워크 공격에 노출될 수 있다고 설명한다. 사용자가 처음 &lt;code&gt;http://example.com&lt;/code&gt;으로 접속하면, 브라우저는 아직 그 호스트를 HSTS 호스트로 모르기 때문이다.&lt;/p&gt;
&lt;p&gt;이 때문에 HSTS는 HTTP에서 HTTPS로 보내는 301 리디렉션을 대체하는 것이 아니라, 그 다음 요청부터 더 강하게 보호하는 보완 수단으로 이해하는 편이 정확하다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;preload는 언제 검토할까?&lt;/h2&gt;
&lt;p&gt;MDN은 preload가 HSTS 명세 자체의 일부는 아니며, Google이 운영하는 별도 preload 서비스라고 설명한다. 대신 preload에 들어가면 브라우저가 첫 방문 전부터 해당 도메인을 HTTPS 전용으로 취급할 수 있어, 최초 요청 취약점을 줄이는 데 도움이 된다.&lt;/p&gt;
&lt;p&gt;2026년 6월 12일 기준 &lt;a href=&quot;https://hstspreload.org/&quot; rel=&quot;noopener noreferrer&quot;&gt;hstspreload.org&lt;/a&gt;는 제출 요건으로 유효한 인증서, HTTP에서 HTTPS로의 동일 호스트 리디렉션, 모든 서브도메인의 HTTPS 제공, 그리고 base domain의 HTTPS 응답에 &lt;code&gt;max-age=31536000&lt;/code&gt;, &lt;code&gt;includeSubDomains&lt;/code&gt;, &lt;code&gt;preload&lt;/code&gt;가 포함된 HSTS 헤더를 요구한다.&lt;/p&gt;
&lt;p&gt;이 요건을 보면 preload는 &quot;한번 써 보고 아니면 되돌리는&quot; 설정에 가깝지 않다. 특히 내부 서브도메인까지 HTTPS 대상에 포함된다는 점이 중요하다. 따라서 preload는 모든 서브도메인을 장기적으로 HTTPS로 유지할 수 있을 때만 검토하는 편이 맞다.&lt;/p&gt;

&lt;h2&gt;권장 적용 순서는?&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;기본 HTTPS 운영과 인증서 자동 갱신이 안정적인지 먼저 확인한다.&lt;/li&gt;
  &lt;li&gt;초기에는 비교적 짧은 &lt;code&gt;max-age&lt;/code&gt;로 적용해 운영 이슈가 없는지 본다.&lt;/li&gt;
  &lt;li&gt;모든 서브도메인이 HTTPS만 쓰는 것이 확인되면 &lt;code&gt;includeSubDomains&lt;/code&gt;를 검토한다.&lt;/li&gt;
  &lt;li&gt;최초 요청 보호가 꼭 필요하고 preload 요건을 장기적으로 지킬 수 있을 때만 &lt;code&gt;preload&lt;/code&gt; 제출을 검토한다.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;자주 묻는 질문&lt;/h2&gt;
&lt;h3&gt;Q. HSTS 헤더를 HTTP 응답에 넣어도 될까?&lt;/h3&gt;
&lt;p&gt;안 된다. MDN은 브라우저가 HTTP로 전달된 HSTS 헤더를 무시한다고 설명한다. HSTS 헤더는 HTTPS 응답으로만 보내야 한다.&lt;/p&gt;

&lt;h3&gt;Q. IP 주소에도 HSTS를 적용할 수 있을까?&lt;/h3&gt;
&lt;p&gt;MDN은 HSTS 호스트가 도메인 이름으로만 식별되며 IP 주소는 HSTS 호스트가 될 수 없다고 설명한다. 따라서 운영 기준도 도메인 단위로 잡아야 한다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;includeSubDomains&lt;/code&gt;를 켜면 부모 도메인과 자식 도메인이 같은 방식으로 저장될까?&lt;/h3&gt;
&lt;p&gt;MDN 설명에 따르면 브라우저는 각 도메인과 서브도메인의 HSTS 정책을 독립적으로 저장할 수 있다. 다만 상위 도메인 정책에 &lt;code&gt;includeSubDomains&lt;/code&gt;가 있으면 하위 도메인에도 보호 효과가 이어질 수 있다.&lt;/p&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. 무난한 시작점은 무엇일까?&lt;/h3&gt;
&lt;p&gt;모든 서브도메인이 이미 HTTPS로 안정적이라면 &lt;code&gt;max-age=31536000; includeSubDomains&lt;/code&gt;가 자주 쓰이는 운영값이다. 다만 새로 도입하는 서비스라면 짧은 &lt;code&gt;max-age&lt;/code&gt;로 먼저 검증하는 절차가 더 안전하다.&lt;/p&gt;

&lt;h3&gt;Q. preload까지 바로 가는 것이 항상 좋을까?&lt;/h3&gt;
&lt;p&gt;그렇지 않다. preload는 최초 요청 보호에는 유리하지만, 내부 서브도메인까지 HTTPS 유지 의무가 생긴다. 운영 범위를 장기적으로 책임질 수 있을 때만 선택해야 한다.&lt;/p&gt;

&lt;h3&gt;Q. 리디렉션이 있으면 HSTS 없이도 충분할까?&lt;/h3&gt;
&lt;p&gt;첫 요청 관점에서는 충분하지 않다. 사용자가 처음 HTTP로 들어오는 순간은 리디렉션 전에 노출될 수 있기 때문에, HSTS는 그 약점을 줄이기 위한 추가 정책으로 봐야 한다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;HSTS는 HTTPS 리디렉션을 예쁘게 만드는 헤더가 아니라, 브라우저가 이후 요청을 HTTPS로 강제하고 인증서 오류 우회를 막도록 하는 보안 정책이다. 선택 기준은 HTTPS 운영 안정성, 모든 서브도메인의 HTTPS 준비 상태, 그리고 롤백 부담이다.&lt;/p&gt;
&lt;p&gt;대부분의 서비스에서는 preload부터 시작하기보다 HSTS 자체를 안정적으로 적용하는 것이 먼저다. 특히 &lt;code&gt;includeSubDomains&lt;/code&gt;와 preload는 범위가 넓기 때문에, 서브도메인 전체 운영 상태를 확인한 뒤 적용하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc6797&quot; rel=&quot;noopener noreferrer&quot;&gt;RFC 6797: HTTP Strict Transport Security (HSTS)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Strict-Transport-Security&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN Web Docs: Strict-Transport-Security header&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://hstspreload.org/&quot; rel=&quot;noopener noreferrer&quot;&gt;HSTS Preload List Submission&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/HTML, Javascript, CSS</category>
      <category>browser security</category>
      <category>hsts</category>
      <category>HTTP Header</category>
      <category>https</category>
      <category>preload</category>
      <category>Strict-Transport-Security</category>
      <category>web security</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/611</guid>
      <comments>https://jcdgods.tistory.com/611#entry611comment</comments>
      <pubDate>Fri, 12 Jun 2026 11:53:24 +0900</pubDate>
    </item>
    <item>
      <title>[크롤링] 데이터 수집을 위한 크롤링 10편 : Sitemap XML과 sitemap index 사용법</title>
      <link>https://jcdgods.tistory.com/610</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IPU00/dJMcaglvax0/jgr8vkpXf5kKsSVQHpubDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IPU00/dJMcaglvax0/jgr8vkpXf5kKsSVQHpubDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IPU00/dJMcaglvax0/jgr8vkpXf5kKsSVQHpubDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIPU00%2FdJMcaglvax0%2Fjgr8vkpXf5kKsSVQHpubDk%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;[크롤링] 데이터 수집을 위한 크롤링 10편 : Sitemap XML과 sitemap index 사용법&lt;/h1&gt;
&lt;p&gt;Sitemap은 크롤러가 어떤 URL을 우선 발견해야 하는지 전달하는 표준 형식이다. 2026년 6월 11일 기준으로 확인한 &lt;a href=&quot;https://www.sitemaps.org/protocol.html&quot; rel=&quot;noopener noreferrer&quot;&gt;sitemaps.org 프로토콜 문서&lt;/a&gt;와 &lt;a href=&quot;https://developers.google.com/search/docs/crawling-indexing/sitemaps/build-sitemap&quot; rel=&quot;noopener noreferrer&quot;&gt;Google Search Central 문서&lt;/a&gt;를 보면, Sitemap은 색인 보장을 약속하는 수단이 아니라 URL 발견과 메타데이터 전달을 돕는 입력에 가깝다.&lt;/p&gt;
&lt;p&gt;실무에서 핵심은 세 가지다. XML sitemap에 어떤 태그가 실제로 필요한지, URL이 많을 때 sitemap index를 언제 써야 하는지, 그리고 robots.txt나 Search Console 제출과 어떤 관계인지다. 이 글은 그 기준만 짧게 정리한다.&lt;/p&gt;

&lt;h2&gt;Sitemap은 무엇을 해 주고 무엇은 보장하지 않을까?&lt;/h2&gt;
&lt;p&gt;sitemaps.org는 Sitemap을 사이트에서 크롤링 가능한 URL 목록을 검색엔진에 알리기 위한 방법으로 설명한다. Google 문서도 Sitemap이 페이지와 파일, 동영상 같은 콘텐츠 정보를 제공한다고 설명하지만, Sitemap에 넣었다고 해서 반드시 크롤되거나 색인된다고 말하지는 않는다.&lt;/p&gt;
&lt;p&gt;즉 내부 링크가 약하거나 최근에 생성된 URL이 많거나, 대규모 사이트라서 발견 경로를 분명히 하고 싶을 때 Sitemap의 가치가 커진다. 반대로 작은 사이트에서 모든 페이지가 내부 링크로 잘 연결되어 있다면 반드시 필요한 것은 아니다.&lt;/p&gt;

&lt;h2&gt;XML sitemap의 최소 구조는 무엇일까?&lt;/h2&gt;
&lt;p&gt;sitemaps.org 프로토콜 기준으로 XML sitemap의 루트는 &lt;code&gt;&amp;lt;urlset&amp;gt;&lt;/code&gt;이고 각 URL은 &lt;code&gt;&amp;lt;url&amp;gt;&lt;/code&gt; 안에 넣는다. 각 URL에서 필수인 태그는 &lt;code&gt;&amp;lt;loc&amp;gt;&lt;/code&gt; 하나뿐이다.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;태그&lt;/th&gt;
      &lt;th&gt;필수 여부&lt;/th&gt;
      &lt;th&gt;의미&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;&amp;lt;loc&amp;gt;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;필수&lt;/td&gt;
      &lt;td&gt;정규화된 절대 URL&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;&amp;lt;lastmod&amp;gt;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;선택&lt;/td&gt;
      &lt;td&gt;페이지가 마지막으로 수정된 날짜 또는 시각&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;&amp;lt;changefreq&amp;gt;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;선택&lt;/td&gt;
      &lt;td&gt;변경 빈도에 대한 힌트&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;&amp;lt;priority&amp;gt;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;선택&lt;/td&gt;
      &lt;td&gt;사이트 내부 URL 사이의 상대적 우선순위 힌트&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;기본 예시는 아래처럼 단순하다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;urlset xmlns=&quot;http://www.sitemaps.org/schemas/sitemap/0.9&quot;&amp;gt;
  &amp;lt;url&amp;gt;
    &amp;lt;loc&amp;gt;https://example.com/docs/sitemap&amp;lt;/loc&amp;gt;
    &amp;lt;lastmod&amp;gt;2026-06-11&amp;lt;/lastmod&amp;gt;
  &amp;lt;/url&amp;gt;
&amp;lt;/urlset&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Google 문서는 XML 외에도 RSS, Atom, 텍스트 형식 Sitemap을 지원한다고 설명한다. 다만 메타데이터를 함께 전달해야 하거나 확장 네임스페이스를 쓸 가능성이 있으면 XML이 가장 일반적이다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;lastmod&lt;/code&gt;, &lt;code&gt;changefreq&lt;/code&gt;, &lt;code&gt;priority&lt;/code&gt;는 어떻게 써야 할까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;lastmod&lt;/code&gt;는 페이지가 실제로 의미 있게 변경된 시점을 넣는 편이 안전하다. sitemaps.org는 이 값이 서버의 &lt;code&gt;If-Modified-Since&lt;/code&gt; 헤더가 반환하는 시간과는 별개라고 설명한다. 즉 파일을 재생성한 시각을 기계적으로 넣는 것보다, 사용자가 보게 되는 내용이 바뀐 시점을 반영해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;changefreq&lt;/code&gt;와 &lt;code&gt;priority&lt;/code&gt;는 명령이 아니라 힌트다. sitemaps.org는 검색엔진 크롤러가 이 값을 다르게 고려할 수 있다고 설명한다. 따라서 모든 URL에 같은 높은 값을 넣는 방식은 정보량이 거의 없다.&lt;/p&gt;
&lt;p&gt;운영 기준으로는 &lt;code&gt;lastmod&lt;/code&gt;만 정확하게 관리해도 실효성이 높고, 나머지 두 태그는 실제로 구분할 근거가 있을 때만 쓰는 편이 낫다.&lt;/p&gt;

&lt;h2&gt;Sitemap index는 언제 필요할까?&lt;/h2&gt;
&lt;p&gt;URL 수가 많거나 섹션별로 파일을 나눠 관리해야 하면 sitemap index를 쓴다. Google 문서는 sitemap 한 개가 최대 50MB 비압축 크기 또는 URL 50,000개 제한을 가진다고 설명하고, 여러 파일을 묶을 때 sitemap index를 사용할 수 있다고 안내한다.&lt;/p&gt;
&lt;p&gt;예를 들어 상품, 게시글, 문서, 이미지처럼 생성 경로가 다른 URL을 따로 만들고 싶다면 &lt;code&gt;post-sitemap.xml&lt;/code&gt;, &lt;code&gt;product-sitemap.xml&lt;/code&gt;처럼 분리한 뒤 index 파일에서 참조하는 편이 운영과 장애 추적에 유리하다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;sitemapindex xmlns=&quot;http://www.sitemaps.org/schemas/sitemap/0.9&quot;&amp;gt;
  &amp;lt;sitemap&amp;gt;
    &amp;lt;loc&amp;gt;https://example.com/sitemaps/post-sitemap.xml&amp;lt;/loc&amp;gt;
    &amp;lt;lastmod&amp;gt;2026-06-11&amp;lt;/lastmod&amp;gt;
  &amp;lt;/sitemap&amp;gt;
  &amp;lt;sitemap&amp;gt;
    &amp;lt;loc&amp;gt;https://example.com/sitemaps/product-sitemap.xml&amp;lt;/loc&amp;gt;
  &amp;lt;/sitemap&amp;gt;
&amp;lt;/sitemapindex&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;robots.txt와 Search Console 제출은 어떤 차이가 있을까?&lt;/h2&gt;
&lt;p&gt;Google 문서는 Sitemap을 알리는 방법으로 두 가지를 먼저 제시한다. 하나는 &lt;code&gt;robots.txt&lt;/code&gt;에 Sitemap URL을 적는 방법이고, 다른 하나는 Search Console을 통한 제출이다. robots.txt 방식은 배포가 단순하고 여러 크롤러에게 한 번에 힌트를 줄 수 있다는 장점이 있다.&lt;/p&gt;
&lt;p&gt;반면 Search Console 제출은 Google 관점에서 처리 상태를 확인하기 쉽다. 제출 결과, 읽기 오류, 발견 URL 수 같은 운영 신호를 보기 좋기 때문이다. 둘은 상호 배타적이지 않아서, 배포는 robots.txt로 하고 모니터링은 Search Console로 하는 조합이 일반적이다.&lt;/p&gt;

&lt;h2&gt;여러 도메인을 한 Sitemap에 넣어도 될까?&lt;/h2&gt;
&lt;p&gt;sitemaps.org 프로토콜 문서는 Sitemap에 들어가는 모든 URL이 같은 호스트에 속해야 한다고 설명한다. 기본 규칙만 보면 &lt;code&gt;https://example.com/sitemap.xml&lt;/code&gt; 안에 다른 호스트 URL을 함께 넣는 것은 피하는 편이 맞다.&lt;/p&gt;
&lt;p&gt;다만 Google 문서는 소유권이 검증된 여러 사이트라면 한 위치의 Sitemap에서 여러 검증 사이트 URL을 참조하는 교차 제출(cross-site submission) 시나리오를 별도로 설명한다. 이것은 검색엔진별 지원 범위가 다를 수 있다는 뜻이다. 실무에서는 특정 엔진 전용 예외를 기대하기보다, 사이트 단위로 파일을 분리하는 구성이 더 보수적이다.&lt;/p&gt;

&lt;h2&gt;운영 기준만 빠르게 정리하면?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;권장 기준&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;URL 수가 적고 내부 링크가 충분함&lt;/td&gt;
      &lt;td&gt;Sitemap은 선택 사항이지만 있으면 운영 추적이 쉬워진다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;신규 URL이 자주 생기거나 대규모 사이트임&lt;/td&gt;
      &lt;td&gt;XML sitemap을 두고 &lt;code&gt;lastmod&lt;/code&gt;를 정확히 관리한다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;50,000 URL 또는 50MB 제한에 근접함&lt;/td&gt;
      &lt;td&gt;파일을 분리하고 sitemap index로 묶는다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;검색엔진 처리 상태를 추적해야 함&lt;/td&gt;
      &lt;td&gt;robots.txt 공개와 Search Console 제출을 함께 쓴다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;여러 도메인이나 서브사이트를 운영함&lt;/td&gt;
      &lt;td&gt;기본값은 사이트별 분리, 예외는 엔진별 문서를 따로 확인한다&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;자주 묻는 질문&lt;/h2&gt;
&lt;h3&gt;Q. Sitemap에 없는 URL은 색인될 수 없을까?&lt;/h3&gt;
&lt;p&gt;그렇지 않다. Sitemap은 발견을 돕는 입력이지 유일한 발견 경로가 아니다. 내부 링크나 외부 링크를 통해서도 URL은 크롤될 수 있다.&lt;/p&gt;

&lt;h3&gt;Q. 모든 URL에 &lt;code&gt;priority&lt;/code&gt;를 1.0으로 넣으면 더 유리할까?&lt;/h3&gt;
&lt;p&gt;그렇게 볼 근거는 없다. sitemaps.org는 &lt;code&gt;priority&lt;/code&gt;를 사이트 내부 URL 간 상대값으로 설명한다. 대부분을 최고값으로 채우면 구분 신호가 사라진다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;lastmod&lt;/code&gt;는 배포 시각을 자동으로 넣어도 될까?&lt;/h3&gt;
&lt;p&gt;항상 적절하지는 않다. 실제 내용 변경이 없는데 매 배포마다 값을 바꾸면 메타데이터 신뢰도가 떨어질 수 있다. 문서 내용 변경 시점과 연결되는 방식이 더 안전하다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;Sitemap 운영에서 가장 중요한 것은 형식을 복잡하게 만드는 것이 아니라, 검색엔진이 이해할 수 있는 안정적인 URL 목록과 신뢰할 수 있는 갱신 신호를 제공하는 것이다. 대부분의 서비스에서는 XML sitemap 하나로 시작하고, URL 규모가 커질 때 sitemap index로 분리하는 순서가 무난하다.&lt;/p&gt;
&lt;p&gt;크롤링 파이프라인 관점에서도 같은 기준이 적용된다. 수집 대상 URL 목록을 정기적으로 갱신해야 한다면, 내부 링크만 믿기보다 Sitemap을 명시적으로 관리하는 편이 장애 분석과 재수집 범위 판단에 유리하다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.sitemaps.org/protocol.html&quot; rel=&quot;noopener noreferrer&quot;&gt;sitemaps.org: Sitemap protocol&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developers.google.com/search/docs/crawling-indexing/sitemaps/build-sitemap&quot; rel=&quot;noopener noreferrer&quot;&gt;Google Search Central: Build and submit a sitemap&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developers.google.com/search/docs/crawling-indexing/robots/create-robots-txt&quot; rel=&quot;noopener noreferrer&quot;&gt;Google Search Central: How to write and submit a robots.txt file&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>시리즈물/데이터 수집을 위한 크롤링</category>
      <category>robots.txt</category>
      <category>Search Console</category>
      <category>SEO</category>
      <category>sitemap</category>
      <category>sitemap index</category>
      <category>XML Sitemap</category>
      <category>데이터수집</category>
      <category>크롤링</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/610</guid>
      <comments>https://jcdgods.tistory.com/610#entry610comment</comments>
      <pubDate>Thu, 11 Jun 2026 12:14:32 +0900</pubDate>
    </item>
    <item>
      <title>Git rerere 사용법</title>
      <link>https://jcdgods.tistory.com/609</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kPOor/dJMcajbm4Mg/RMNAcSYUBNvTTiNOnx3uf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kPOor/dJMcajbm4Mg/RMNAcSYUBNvTTiNOnx3uf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kPOor/dJMcajbm4Mg/RMNAcSYUBNvTTiNOnx3uf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkPOor%2FdJMcajbm4Mg%2FRMNAcSYUBNvTTiNOnx3uf1%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Git rerere 사용법&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;git rerere&lt;/code&gt;는 같은 충돌을 다시 만났을 때 이전에 해결한 결과를 재사용하도록 돕는 기능이다. 2026년 6월 10일 기준 Git 공식 문서를 보면 이 기능은 반복되는 merge conflict를 줄이는 데 초점이 있고, 사용 전에는 &lt;code&gt;rerere.enabled&lt;/code&gt; 설정 여부를 먼저 확인해야 한다.&lt;/p&gt;
&lt;p&gt;실무에서 핵심은 세 가지다. 충돌이 자주 반복되는 브랜치 구조인지, 자동으로 index까지 갱신할지, 그리고 자동 적용 후 사람이 다시 검토할지다. 이 글은 Git 공식 문서 기준으로 &lt;code&gt;rerere&lt;/code&gt;의 동작 방식과 안전한 사용 기준만 정리한다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;git rerere&lt;/code&gt;는 무엇을 기록할까?&lt;/h2&gt;
&lt;p&gt;공식 문서의 설명대로 &lt;code&gt;rerere&lt;/code&gt;는 처음 충돌이 났을 때 충돌 마커가 들어 있는 자동 병합 결과와, 이후 사용자가 직접 고친 최종 해결 결과를 함께 기록한다. 나중에 같은 충돌 hunk를 다시 만나면 이 기록을 바탕으로 해결 결과를 재적용한다.&lt;/p&gt;
&lt;p&gt;즉 단순히 &quot;파일 이름이 같으면 재사용&quot;하는 기능이 아니라, 이전 충돌 패턴과 해결 결과를 연결해 두었다가 비슷한 충돌이 다시 발생했을 때 재사용하는 구조다. 장기간 유지되는 topic branch에서 같은 충돌을 여러 번 푸는 상황에 특히 유용하다.&lt;/p&gt;

&lt;h2&gt;먼저 어떤 설정을 켜야 할까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git-rerere&lt;/code&gt; 문서는 이 기능을 쓰려면 &lt;code&gt;rerere.enabled&lt;/code&gt;를 설정해야 한다고 설명한다. 다만 &lt;code&gt;git-config&lt;/code&gt; 문서 기준으로는 저장소의 &lt;code&gt;$GIT_DIR&lt;/code&gt; 아래에 &lt;code&gt;rr-cache&lt;/code&gt; 디렉터리가 이미 있으면 기본적으로 활성화될 수 있다.&lt;/p&gt;
&lt;p&gt;처음부터 명시적으로 쓰려면 다음처럼 설정하는 편이 가장 분명하다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git config --global rerere.enabled true
git config --global rerere.autoUpdate false&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;rerere.autoUpdate&lt;/code&gt; 기본값은 &lt;code&gt;false&lt;/code&gt;다. 공식 문서 기준 이 값을 &lt;code&gt;true&lt;/code&gt;로 두면 이전 해결 결과를 깨끗하게 재사용했을 때 working tree뿐 아니라 index까지 함께 갱신한다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;rerere.autoUpdate&lt;/code&gt;는 언제 켜는 편이 맞을까?&lt;/h2&gt;
&lt;p&gt;자동으로 &lt;code&gt;git add&lt;/code&gt; 된 상태까지 만들고 싶다면 &lt;code&gt;rerere.autoUpdate=true&lt;/code&gt;를 검토할 수 있다. 반대로 자동 적용 결과를 먼저 눈으로 점검하고 싶다면 기본값인 &lt;code&gt;false&lt;/code&gt;를 유지하는 편이 안전하다.&lt;/p&gt;
&lt;p&gt;Git 공식 문서는 &lt;code&gt;git merge --no-rerere-autoupdate&lt;/code&gt; 옵션을 사용하면 자동 해결 후에도 index 갱신을 막고 결과를 다시 검토할 수 있다고 설명한다. 반복 충돌이 많아도 코드 영향 범위가 넓다면 이 쪽이 더 보수적인 선택이다.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;권장 선택&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;같은 충돌을 자주 반복하고 해결 패턴이 안정적임&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;rerere.autoUpdate=true&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;반복 입력과 &lt;code&gt;git add&lt;/code&gt; 단계를 줄일 수 있다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;자동 적용 결과를 반드시 diff로 확인해야 함&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;rerere.autoUpdate=false&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;index 자동 갱신을 막고 수동 검토 흐름을 유지할 수 있다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;이번 merge에서만 보수적으로 확인하고 싶음&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git merge --no-rerere-autoupdate&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;전역 설정을 바꾸지 않고 현재 merge에만 적용 가능하다&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;실제로 어떤 흐름에서 도움이 될까?&lt;/h2&gt;
&lt;p&gt;공식 예시는 장기간 유지되는 topic branch를 최신 기본 브랜치와 자주 맞춰 보는 경우를 보여 준다. 테스트용 merge를 한 번 수행하면서 수동 충돌 해결을 기록해 두면, 이후 비슷한 충돌이 다시 발생했을 때 같은 해결을 재사용할 수 있다.&lt;/p&gt;
&lt;p&gt;Git 문서는 &lt;code&gt;git merge&lt;/code&gt;가 자동 병합 실패 후 &lt;code&gt;git rerere&lt;/code&gt;를 자동 호출하고, merge 결과를 커밋할 때도 관련 기록을 반영한다고 설명한다. 그래서 기능을 켠 뒤에는 대부분 별도 명령을 자주 직접 입력하지 않아도 된다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;그래도 직접 확인할 수 있는 명령은 무엇일까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git rerere&lt;/code&gt;에는 현재 상태를 점검하는 하위 명령이 있다. 공식 문서 기준으로 자주 보는 항목은 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;git rerere status&lt;/code&gt;: 현재 충돌 중이며 기록 대상이 되는 경로를 본다.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;git rerere diff&lt;/code&gt;: 해결 과정에서 무엇이 바뀌었는지 diff로 확인한다.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;git rerere remaining&lt;/code&gt;: 자동 해결되지 않았거나 추적할 수 없는 충돌 경로를 본다.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;git rerere gc&lt;/code&gt;: 오래된 기록을 정리한다. 공식 기본값은 미해결 15일, 해결됨 60일이다.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;git rerere forget &amp;lt;path&amp;gt;&lt;/code&gt;: 현재 충돌 경로에 대해 저장된 해결을 다시 잊게 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;특히 &lt;code&gt;remaining&lt;/code&gt;은 rerere가 처리하지 못한 경로를 빠르게 분리하는 데 유용하다. 공식 문서에는 conflicting submodule처럼 추적 대상이 아닌 경우도 포함될 수 있다고 적혀 있다.&lt;/p&gt;

&lt;h2&gt;자동 적용 뒤에도 왜 검토가 필요할까?&lt;/h2&gt;
&lt;p&gt;Git 공식 문서는 rerere가 working tree 파일을 갱신해도 index는 그대로 둘 수 있으며, 최종적으로는 &lt;code&gt;git diff&lt;/code&gt;로 내용을 확인하고 &lt;code&gt;git add&lt;/code&gt;해야 한다고 설명한다. 자동 재사용은 반복 작업을 줄여 주지만 의미상 올바른 병합인지까지 대신 판단해 주는 기능은 아니다.&lt;/p&gt;
&lt;p&gt;따라서 같은 코드 조각이 보여도 주변 문맥이 달라졌을 수 있는 리팩터링 구간이나 설정 파일에서는 자동 적용 후 review를 생략하지 않는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;Q. &lt;code&gt;git rerere&lt;/code&gt;는 merge에서만 동작할까?&lt;/h3&gt;
&lt;p&gt;공식 문서는 merge 중심으로 설명하지만, 충돌 해결 중 &lt;code&gt;git rebase --abort&lt;/code&gt;나 &lt;code&gt;git am --abort&lt;/code&gt;에서도 관련 정리 동작이 자동 호출된다고 명시한다. 즉 merge 외의 충돌 흐름에서도 rerere 기록이 연결될 수 있다.&lt;/p&gt;

&lt;h3&gt;Q. 기능을 켜면 충돌을 항상 완전히 자동 해결할까?&lt;/h3&gt;
&lt;p&gt;아니다. 공식 문서도 &quot;이전에 기록한 해결이 현재 충돌에 clean하게 적용될 때&quot; 재사용한다고 설명한다. 패턴이 충분히 다르거나 추적 불가능한 충돌이면 사람이 다시 개입해야 한다.&lt;/p&gt;

&lt;h3&gt;Q. 기록을 잘못 학습했다면 어떻게 할까?&lt;/h3&gt;
&lt;p&gt;현재 충돌 경로에 대해 잘못된 해결을 버리려면 &lt;code&gt;git rerere forget &amp;lt;pathspec&amp;gt;&lt;/code&gt;를 사용할 수 있다. 전체 메타데이터를 초기화해야 하는 경우에는 &lt;code&gt;git rerere clear&lt;/code&gt;도 공식 하위 명령으로 제공된다.&lt;/p&gt;

&lt;h2&gt;선택 기준을 한 번에 정리하면?&lt;/h2&gt;
&lt;p&gt;같은 충돌을 여러 번 푸는 브랜치 운영이라면 &lt;code&gt;rerere.enabled=true&lt;/code&gt;는 검토 가치가 높다. 다만 자동으로 index까지 바꾸는 &lt;code&gt;rerere.autoUpdate&lt;/code&gt;는 팀의 검토 습관에 맞춰 켜고, 확신이 없으면 &lt;code&gt;false&lt;/code&gt; 또는 &lt;code&gt;--no-rerere-autoupdate&lt;/code&gt; 쪽이 더 안전하다.&lt;/p&gt;
&lt;p&gt;2026년 6월 10일 기준 Git 공식 문서 기준으로 보면 rerere의 장점은 &quot;반복 충돌의 재입력 감소&quot;이지 &quot;검토 생략&quot;이 아니다. 자동 적용 뒤에도 diff 확인을 포함한 마지막 점검은 유지하는 편이 무난하다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-rerere&quot; rel=&quot;noopener noreferrer&quot;&gt;Git 공식 문서: git-rerere&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-config&quot; rel=&quot;noopener noreferrer&quot;&gt;Git 공식 문서: git-config&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-merge&quot; rel=&quot;noopener noreferrer&quot;&gt;Git 공식 문서: git-merge&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/Git, IDE, 툴 관련</category>
      <category>GIT</category>
      <category>merge conflict</category>
      <category>rebase</category>
      <category>RERERE</category>
      <category>개발도구</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/609</guid>
      <comments>https://jcdgods.tistory.com/609#entry609comment</comments>
      <pubDate>Wed, 10 Jun 2026 12:05:06 +0900</pubDate>
    </item>
    <item>
      <title>systemd RuntimeDirectory와 StateDirectory 기준 정리</title>
      <link>https://jcdgods.tistory.com/608</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/W2b2O/dJMcaf09EFE/wSj32tQNaekycOfRSDYuc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/W2b2O/dJMcaf09EFE/wSj32tQNaekycOfRSDYuc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/W2b2O/dJMcaf09EFE/wSj32tQNaekycOfRSDYuc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FW2b2O%2FdJMcaf09EFE%2FwSj32tQNaekycOfRSDYuc1%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;systemd RuntimeDirectory와 StateDirectory 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;RuntimeDirectory=&lt;/code&gt;, &lt;code&gt;StateDirectory=&lt;/code&gt;, &lt;code&gt;CacheDirectory=&lt;/code&gt;, &lt;code&gt;LogsDirectory=&lt;/code&gt;는 systemd 서비스가 직접 쓸 디렉터리를 표준 위치에 만들고 수명과 권한을 함께 관리하는 지시어다. 2026년 6월 9일 기준 systemd 공식 문서를 보면 이 네 지시어는 이름이 비슷하지만 생성 위치, 삭제 시점, 적합한 데이터 성격이 다르다.&lt;/p&gt;
&lt;p&gt;실무에서 핵심은 &quot;재부팅이나 서비스 중지 후에도 남아야 하는가&quot;, &quot;캐시인지 상태 데이터인지&quot;, &quot;tmpfiles.d보다 서비스 unit 안에서 수명을 묶는 편이 맞는가&quot;를 먼저 구분하는 것이다. 이 글은 systemd 공식 문서 기준으로 각 지시어의 차이와 선택 기준만 정리한다.&lt;/p&gt;

&lt;h2&gt;각 지시어는 어디에 디렉터리를 만들까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;systemd.exec&lt;/code&gt; 문서의 표에 따르면 system service 기준 기본 위치는 다음과 같다.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;지시어&lt;/th&gt;
      &lt;th&gt;기본 경로&lt;/th&gt;
      &lt;th&gt;수명&lt;/th&gt;
      &lt;th&gt;대표 용도&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;RuntimeDirectory=&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;/run/&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;서비스 중지 시 제거 가능, 재부팅 시 사라짐&lt;/td&gt;
      &lt;td&gt;소켓, PID 파일, 일시적 런타임 상태&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;StateDirectory=&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;/var/lib/&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;서비스 중지 후에도 유지&lt;/td&gt;
      &lt;td&gt;애플리케이션 상태, 영속 데이터&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;CacheDirectory=&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;/var/cache/&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;서비스 중지 후에도 유지&lt;/td&gt;
      &lt;td&gt;재생성 가능한 캐시&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;LogsDirectory=&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;/var/log/&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;서비스 중지 후에도 유지&lt;/td&gt;
      &lt;td&gt;서비스 전용 로그 파일&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;user service에서는 같은 지시어가 각각 &lt;code&gt;$XDG_RUNTIME_DIR&lt;/code&gt;, &lt;code&gt;$XDG_STATE_HOME&lt;/code&gt;, &lt;code&gt;$XDG_CACHE_HOME&lt;/code&gt;, &lt;code&gt;$XDG_STATE_HOME/log&lt;/code&gt; 아래로 매핑된다. 즉 같은 unit 문법이라도 system manager인지 user manager인지에 따라 실제 경로는 달라질 수 있다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;RuntimeDirectory=&lt;/code&gt;는 언제 써야 할까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;RuntimeDirectory=&lt;/code&gt;는 서비스가 실행 중일 때만 필요한 경로를 &lt;code&gt;/run&lt;/code&gt; 아래에 만들고, 런타임 수명과 묶고 싶을 때 적합하다. 공식 문서는 권한이 없는 데몬이 직접 &lt;code&gt;/run&lt;/code&gt; 아래 디렉터리를 만들기 어려운 경우에도 이 지시어가 유용하다고 설명한다.&lt;/p&gt;
&lt;p&gt;대표 예시는 유닉스 소켓, lock 파일, PID 파일, 재부팅 후 남을 필요가 없는 임시 상태다. 기본적으로 &lt;code&gt;RuntimeDirectory=&lt;/code&gt;로 만든 가장 안쪽 디렉터리는 서비스가 멈출 때 제거되며, 재부팅 후에도 유지되지 않는다.&lt;/p&gt;
&lt;p&gt;다만 삭제 시점은 &lt;code&gt;RuntimeDirectoryPreserve=&lt;/code&gt;로 조정할 수 있다. 공식 문서 기준 기본값은 &lt;code&gt;no&lt;/code&gt;이고, &lt;code&gt;restart&lt;/code&gt;는 재시작 때 보존, &lt;code&gt;yes&lt;/code&gt;는 정지 후에도 제거하지 않는 동작이다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;StateDirectory=&lt;/code&gt;와 &lt;code&gt;CacheDirectory=&lt;/code&gt;는 무엇이 다를까?&lt;/h2&gt;
&lt;p&gt;둘 다 서비스가 멈춰도 남는 디렉터리지만 데이터 성격이 다르다. &lt;code&gt;StateDirectory=&lt;/code&gt;는 서비스의 지속 상태를 보관하는 경로이고, &lt;code&gt;CacheDirectory=&lt;/code&gt;는 필요하면 다시 만들 수 있는 캐시를 두는 경로다.&lt;/p&gt;
&lt;p&gt;예를 들어 인덱스 파일, 메타데이터 DB, 장기적으로 유지할 동기화 상태는 &lt;code&gt;StateDirectory=&lt;/code&gt; 쪽에 가깝다. 반면 다운로드한 중간 산출물, 렌더 캐시, 다시 생성 가능한 변환 결과는 &lt;code&gt;CacheDirectory=&lt;/code&gt; 쪽이 더 맞다.&lt;/p&gt;
&lt;p&gt;실무적으로 이 구분이 중요한 이유는 운영자가 백업, 정리, 마이그레이션 정책을 다르게 잡기 쉽기 때문이다. systemd가 데이터 의미까지 자동 판정해 주는 것은 아니므로, 어떤 파일이 &quot;없어져도 다시 만들 수 있는가&quot;를 기준으로 나누는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;LogsDirectory=&lt;/code&gt;는 저널을 써도 필요할까?&lt;/h2&gt;
&lt;p&gt;항상 필요한 것은 아니다. 서비스 로그를 전부 journald로만 보내고 파일 기반 로그를 남기지 않는다면 &lt;code&gt;LogsDirectory=&lt;/code&gt;가 없어도 된다. 반대로 애플리케이션이 자체 로그 파일을 굴리거나 외부 도구가 특정 로그 경로를 요구한다면 전용 로그 디렉터리를 unit에서 함께 관리할 수 있다.&lt;/p&gt;
&lt;p&gt;즉 &lt;code&gt;LogsDirectory=&lt;/code&gt;는 &quot;로그는 무조건 여기로 보내라&quot;는 지시어가 아니라, 서비스가 직접 사용할 로그 디렉터리를 표준 위치에 준비해 주는 수단으로 보는 편이 맞다.&lt;/p&gt;

&lt;h2&gt;왜 &lt;code&gt;tmpfiles.d&lt;/code&gt; 대신 이 지시어를 권장할까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;tmpfiles.d&lt;/code&gt; 문서는 서비스 전용 런타임 디렉터리라면 &lt;code&gt;RuntimeDirectory=&lt;/code&gt;를, &lt;code&gt;/var/lib&lt;/code&gt;, &lt;code&gt;/var/cache&lt;/code&gt;, &lt;code&gt;/var/log&lt;/code&gt;, &lt;code&gt;/etc&lt;/code&gt; 아래 디렉터리라면 각각 &lt;code&gt;StateDirectory=&lt;/code&gt;, &lt;code&gt;CacheDirectory=&lt;/code&gt;, &lt;code&gt;LogsDirectory=&lt;/code&gt;, &lt;code&gt;ConfigurationDirectory=&lt;/code&gt;를 쓰는 편이 낫다고 설명한다.&lt;/p&gt;
&lt;p&gt;이유는 두 가지다. 첫째, 설정이 service unit 한 곳에 모여 관리가 단순해진다. 둘째, 디렉터리 수명이 서비스 수명과 직접 연결되어 &lt;code&gt;tmpfiles.d&lt;/code&gt; 규칙이 먼저 실행됐는지 따로 신경 쓸 필요가 줄어든다.&lt;/p&gt;
&lt;p&gt;반대로 파일 수명이 특정 서비스와 독립적이거나 더 복잡한 생성 규칙, 정리 정책, ACL, 속성 조정이 필요하다면 &lt;code&gt;tmpfiles.d&lt;/code&gt;가 더 적합할 수 있다.&lt;/p&gt;

&lt;h2&gt;systemd는 디렉터리를 어떻게 준비할까?&lt;/h2&gt;
&lt;p&gt;공식 문서에 따르면 이 지시어들을 사용하면 systemd는 필요한 부모 디렉터리까지 함께 만들고, &lt;code&gt;ConfigurationDirectory=&lt;/code&gt;를 제외한 가장 안쪽 디렉터리의 소유자와 그룹을 &lt;code&gt;User=&lt;/code&gt;, &lt;code&gt;Group=&lt;/code&gt;에 맞춘다. 이미 디렉터리가 존재하지만 소유자가 다르면, 그 아래 파일과 디렉터리까지 재귀적으로 소유권을 바꿀 수 있다.&lt;/p&gt;
&lt;p&gt;또한 각 지시어에는 &lt;code&gt;StateDirectoryMode=&lt;/code&gt;처럼 접근 권한 모드를 지정하는 짝 옵션이 있고 기본값은 &lt;code&gt;0755&lt;/code&gt;다. 따라서 민감한 상태 파일을 둘 예정이라면 기본 권한이 적절한지 별도로 확인해야 한다.&lt;/p&gt;
&lt;p&gt;이 지시어들은 필요한 마운트 유닛에 대한 &lt;code&gt;Requires=&lt;/code&gt;와 &lt;code&gt;After=&lt;/code&gt; 의존성도 자동으로 추가한다. 즉 지정한 경로가 올라오기 전에 서비스를 시작하지 않도록 기본 의존성이 보강된다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;DynamicUser=&lt;/code&gt;를 쓰면 무엇이 달라질까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;DynamicUser=&lt;/code&gt;가 켜져 있으면 &lt;code&gt;StateDirectory=&lt;/code&gt;, &lt;code&gt;CacheDirectory=&lt;/code&gt;, &lt;code&gt;LogsDirectory=&lt;/code&gt;의 실제 host 쪽 생성 위치가 각각 &lt;code&gt;/var/lib/private&lt;/code&gt;, &lt;code&gt;/var/cache/private&lt;/code&gt;, &lt;code&gt;/var/log/private&lt;/code&gt; 아래로 바뀐다. systemd 문서는 이것이 동적 UID 재사용으로 인한 접근 문제를 줄이기 위한 처리라고 설명한다.&lt;/p&gt;
&lt;p&gt;다만 서비스 내부에서 보이는 경로는 여전히 &lt;code&gt;/var/lib&lt;/code&gt;, &lt;code&gt;/var/cache&lt;/code&gt;, &lt;code&gt;/var/log&lt;/code&gt; 아래처럼 보이도록 심볼릭 링크가 만들어진다. 그래서 애플리케이션 입장에서는 표준 경로를 계속 쓰되, host에서는 더 안전한 실제 저장 위치를 쓰는 구조다.&lt;/p&gt;

&lt;h2&gt;선택 기준을 한 번에 정리하면?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;우선 검토할 지시어&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;서비스 중지나 재부팅 후 사라져도 되는 소켓, PID, lock 파일&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;RuntimeDirectory=&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;/run&lt;/code&gt; 기반이고 수명이 서비스 실행과 묶인다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;백업하거나 마이그레이션해야 하는 영속 상태 데이터&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;StateDirectory=&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;/var/lib&lt;/code&gt; 아래 지속 상태 저장에 맞다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;지워져도 다시 만들 수 있는 캐시 데이터&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;CacheDirectory=&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;캐시 성격을 운영 정책에서 분리하기 쉽다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;애플리케이션이 직접 파일 로그를 관리해야 함&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;LogsDirectory=&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;/var/log&lt;/code&gt; 아래 전용 경로를 unit에서 준비한다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;파일 수명이 서비스와 무관하거나 복잡한 tmpfiles 규칙이 필요함&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;tmpfiles.d&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;서비스 단위 지시어보다 더 유연한 규칙을 줄 수 있다&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;Q. &lt;code&gt;StateDirectory=&lt;/code&gt;를 쓰면 백업 대상이라는 뜻까지 systemd가 보장할까?&lt;/h3&gt;
&lt;p&gt;아니다. systemd는 표준 위치와 생성, 소유권, 권한을 관리할 뿐 백업 정책까지 정의하지 않는다. 다만 &lt;code&gt;/var/lib&lt;/code&gt; 아래의 상태 데이터라는 의미를 운영자가 읽기 쉬워진다는 점이 실무상 장점이다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;RuntimeDirectory=&lt;/code&gt;만으로 재시작 중 파일을 반드시 유지할 수 있을까?&lt;/h3&gt;
&lt;p&gt;기본값은 아니다. 기본 동작은 서비스 중지 시 제거 쪽이므로, 재시작 중 유지가 필요하면 &lt;code&gt;RuntimeDirectoryPreserve=restart&lt;/code&gt; 또는 &lt;code&gt;yes&lt;/code&gt;를 검토해야 한다.&lt;/p&gt;

&lt;h3&gt;Q. 이미 경로가 존재하면 그냥 재사용만 할까?&lt;/h3&gt;
&lt;p&gt;그렇게 단정할 수 없다. 공식 문서는 소유자나 그룹이 다르면 하위 파일과 디렉터리까지 재귀적으로 소유권을 조정할 수 있다고 설명한다. 운영 중인 기존 디렉터리에 붙일 때는 이 동작을 먼저 확인하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;systemd의 디렉터리 지시어는 &quot;어디에 만들까&quot;보다 &quot;어떤 수명의 데이터인가&quot;를 명확히 하게 만드는 도구다. 일시적 런타임 상태는 &lt;code&gt;RuntimeDirectory=&lt;/code&gt;, 영속 상태는 &lt;code&gt;StateDirectory=&lt;/code&gt;, 재생성 가능한 캐시는 &lt;code&gt;CacheDirectory=&lt;/code&gt;, 파일 로그는 &lt;code&gt;LogsDirectory=&lt;/code&gt;로 나누면 공식 문서의 의도와 가장 잘 맞는다.&lt;/p&gt;
&lt;p&gt;2026년 6월 9일 기준 systemd 문서를 따르면 서비스 전용 디렉터리라면 먼저 unit 지시어로 해결할 수 있는지 보고, 서비스 수명과 독립적인 규칙이 필요할 때만 &lt;code&gt;tmpfiles.d&lt;/code&gt;로 넘어가는 순서가 무난하다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/256/systemd.exec.html&quot; rel=&quot;noopener noreferrer&quot;&gt;systemd 공식 문서: systemd.exec&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/249/tmpfiles.d.html&quot; rel=&quot;noopener noreferrer&quot;&gt;systemd 공식 문서: tmpfiles.d&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/latest/systemctl.html&quot; rel=&quot;noopener noreferrer&quot;&gt;systemd 공식 문서: systemctl&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/운영체제</category>
      <category>CacheDirectory</category>
      <category>Linux</category>
      <category>LogsDirectory</category>
      <category>RuntimeDirectory</category>
      <category>StateDirectory</category>
      <category>systemd</category>
      <category>tmpfiles.d</category>
      <category>운영체제</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/608</guid>
      <comments>https://jcdgods.tistory.com/608#entry608comment</comments>
      <pubDate>Tue, 9 Jun 2026 10:18:23 +0900</pubDate>
    </item>
    <item>
      <title>PostgreSQL listen_addresses 기준 정리</title>
      <link>https://jcdgods.tistory.com/607</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r86J7/dJMcacwDlAk/fNsell0UfgafxXhv50GAk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r86J7/dJMcacwDlAk/fNsell0UfgafxXhv50GAk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r86J7/dJMcacwDlAk/fNsell0UfgafxXhv50GAk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr86J7%2FdJMcacwDlAk%2FfNsell0UfgafxXhv50GAk0%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;PostgreSQL listen_addresses 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;listen_addresses&lt;/code&gt;는 PostgreSQL 서버가 어떤 TCP/IP 주소에서 접속을 받을지 정하는 설정이다. 2026년 6월 8일 기준 current 문서는 PostgreSQL 18.4 문서이며, 이 문서에서 기본값은 &lt;code&gt;localhost&lt;/code&gt;로 안내된다. 즉 기본 상태에서는 같은 서버 안에서의 loopback TCP 연결만 허용된다.&lt;/p&gt;
&lt;p&gt;실무에서는 원격 접속을 열어야 하는지, 모든 인터페이스를 한 번에 열어도 되는지, 그리고 &lt;code&gt;pg_hba.conf&lt;/code&gt;와 각각 무엇이 다른지가 가장 자주 헷갈린다. 이 글은 PostgreSQL 공식 문서 기준으로 &lt;code&gt;listen_addresses&lt;/code&gt;의 의미, 자주 쓰는 값, 안전하게 고르는 기준을 정리한다.&lt;/p&gt;

&lt;h2&gt;listen_addresses는 무엇을 제어할까?&lt;/h2&gt;
&lt;p&gt;공식 문서에서 &lt;code&gt;listen_addresses&lt;/code&gt;는 서버가 클라이언트 애플리케이션의 연결 시도를 받을 TCP/IP 주소 목록을 지정하는 값으로 설명된다. 값 형식은 호스트명이나 숫자 IP 주소의 쉼표 구분 목록이다.&lt;/p&gt;
&lt;p&gt;중요한 점은 이 설정이 &quot;누가 로그인할 수 있는지&quot;를 정하는 것이 아니라 &quot;어느 네트워크 인터페이스에서 연결 시도 자체를 받을지&quot;를 정한다는 점이다. 인증과 허용 범위의 세부 제어는 여전히 &lt;code&gt;pg_hba.conf&lt;/code&gt;가 맡는다.&lt;/p&gt;

&lt;h2&gt;기본값과 자주 쓰는 값은?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;값&lt;/th&gt;
      &lt;th&gt;공식 문서 의미&lt;/th&gt;
      &lt;th&gt;실무 해석&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;localhost&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;로컬 loopback TCP 연결만 받는다.&lt;/td&gt;
      &lt;td&gt;단일 서버 안에서만 접속하는 기본값으로 가장 보수적이다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;*&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;사용 가능한 모든 IP 인터페이스를 연다.&lt;/td&gt;
      &lt;td&gt;간단하지만 노출 범위가 넓어져서 주소 필터링을 다른 계층에 더 의존하게 된다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;0.0.0.0&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;모든 IPv4 주소를 연다.&lt;/td&gt;
      &lt;td&gt;IPv4만 대상으로 전체 개방이 필요할 때 쓴다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;::&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;모든 IPv6 주소를 연다.&lt;/td&gt;
      &lt;td&gt;IPv6 환경에서 전체 개방이 필요할 때 쓴다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;빈 값&lt;/td&gt;
      &lt;td&gt;어떤 IP 인터페이스도 듣지 않는다.&lt;/td&gt;
      &lt;td&gt;이 경우 TCP/IP 연결은 막히고 Unix-domain socket만 사용할 수 있다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;공식 문서는 목록이 비어 있으면 어떤 IP 인터페이스에서도 listen하지 않으며, 이 경우 Unix-domain socket으로만 연결할 수 있다고 설명한다. 반대로 목록이 비어 있지 않으면 서버는 적어도 하나의 TCP/IP 주소를 열 수 있을 때 시작하고, 열지 못한 주소에 대해서는 warning을 남긴다.&lt;/p&gt;

&lt;h2&gt;대부분의 환경에서는 어떤 값을 고르면 될까?&lt;/h2&gt;
&lt;p&gt;원격 클라이언트가 전혀 필요 없으면 기본값인 &lt;code&gt;localhost&lt;/code&gt;가 가장 단순하다. 애플리케이션과 PostgreSQL이 같은 서버 안에서만 통신한다면, 굳이 외부 인터페이스까지 열 이유가 없다.&lt;/p&gt;
&lt;p&gt;원격 접속이 필요하더라도 문서 관점에서 바로 &lt;code&gt;*&lt;/code&gt;를 고르는 것이 필수는 아니다. 특정 내부 IP만 명시할 수 있으면 그 편이 노출 면을 줄인다. 공식 문서도 &lt;code&gt;listen_addresses&lt;/code&gt;가 insecure network interface에서 반복적인 악성 연결 요청을 줄이는 데 도움이 될 수 있다고 설명한다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;단일 서버 애플리케이션: &lt;code&gt;localhost&lt;/code&gt; 유지가 무난하다.&lt;/li&gt;
  &lt;li&gt;내부망의 특정 NIC만 열어야 하는 서버: 그 IP 주소만 명시하는 편이 단순하다.&lt;/li&gt;
  &lt;li&gt;주소가 자주 바뀌거나 모든 인터페이스 수신이 필요한 환경: &lt;code&gt;*&lt;/code&gt; 또는 &lt;code&gt;0.0.0.0&lt;/code&gt;, &lt;code&gt;::&lt;/code&gt;를 검토하되 방화벽과 &lt;code&gt;pg_hba.conf&lt;/code&gt;를 함께 좁혀야 한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;pg_hba.conf와는 무엇이 다를까?&lt;/h2&gt;
&lt;p&gt;두 설정은 역할이 다르다. &lt;code&gt;listen_addresses&lt;/code&gt;는 서버 프로세스가 어느 인터페이스에서 연결 시도를 받을지 정하고, &lt;code&gt;pg_hba.conf&lt;/code&gt;는 그 연결을 어떤 사용자, 어떤 주소 대역, 어떤 인증 방식으로 처리할지 정한다.&lt;/p&gt;
&lt;p&gt;current 문서의 &lt;code&gt;pg_hba.conf&lt;/code&gt; 설명은 원격 TCP/IP 연결이 가능하려면 적절한 &lt;code&gt;listen_addresses&lt;/code&gt; 값으로 서버가 시작되어 있어야 한다고 명시한다. 즉 &lt;code&gt;pg_hba.conf&lt;/code&gt;만 열어 두어도 &lt;code&gt;listen_addresses&lt;/code&gt;가 기본값 &lt;code&gt;localhost&lt;/code&gt;라면 외부 원격 접속은 되지 않는다.&lt;/p&gt;
&lt;p&gt;반대로 &lt;code&gt;listen_addresses = '*'&lt;/code&gt;로 열어도 &lt;code&gt;pg_hba.conf&lt;/code&gt;가 허용하지 않으면 로그인은 성립하지 않는다. 실무에서는 두 값을 서로 대체재로 보면 안 된다.&lt;/p&gt;

&lt;h2&gt;설정을 바꾸면 reload로 끝날까?&lt;/h2&gt;
&lt;p&gt;아니다. current 문서는 이 파라미터를 server start에만 설정할 수 있다고 설명한다. 즉 &lt;code&gt;pg_reload_conf()&lt;/code&gt; 같은 reload만으로는 반영되지 않고 서버 재시작이 필요하다.&lt;/p&gt;
&lt;p&gt;이 점은 운영 절차에서 중요하다. &lt;code&gt;pg_hba.conf&lt;/code&gt;는 reload로 다시 읽을 수 있지만 &lt;code&gt;listen_addresses&lt;/code&gt;는 그렇지 않으므로, 원격 접속 경로를 새로 여는 작업은 재시작 창구를 고려해야 한다.&lt;/p&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;1. listen_addresses를 비우면 로컬 접속도 모두 막힐까?&lt;/h3&gt;
&lt;p&gt;TCP/IP 기준으로는 그렇다. 하지만 공식 문서는 빈 값일 때 Unix-domain socket은 계속 사용할 수 있다고 설명한다. 따라서 로컬 애플리케이션이 소켓으로 붙는 구성이라면 접속이 완전히 사라지는 것은 아니다.&lt;/p&gt;

&lt;h3&gt;2. 별표 하나면 설정이 끝날까?&lt;/h3&gt;
&lt;p&gt;아니다. &lt;code&gt;*&lt;/code&gt;는 수신 인터페이스 범위만 넓힌다. 실제 접속 허용은 &lt;code&gt;pg_hba.conf&lt;/code&gt;, 방화벽, 라우팅, TLS 설정 같은 다른 계층과 함께 봐야 한다.&lt;/p&gt;

&lt;h3&gt;3. 일부 주소를 열지 못해도 서버가 시작될 수 있을까?&lt;/h3&gt;
&lt;p&gt;그럴 수 있다. current 문서는 목록이 비어 있지 않을 때 적어도 하나의 TCP/IP 주소를 열 수 있으면 서버가 시작하고, 열지 못한 주소에 대해서는 warning을 남긴다고 설명한다.&lt;/p&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. 운영 서버라면 무조건 &lt;code&gt;*&lt;/code&gt;보다 명시적 IP가 나을까?&lt;/h3&gt;
&lt;p&gt;공식 문서는 정책 우선순위를 강제하지는 않지만, 인터페이스 수신 범위를 줄이는 것이 악성 연결 요청 노출을 줄이는 데 도움이 될 수 있다고 설명한다. 따라서 고정된 내부 IP만 필요하다면 그 주소만 명시하는 쪽이 더 보수적이다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;listen_addresses&lt;/code&gt;만 바꾸면 외부에서 바로 접속할 수 있을까?&lt;/h3&gt;
&lt;p&gt;아니다. current 문서 기준으로 원격 연결은 &lt;code&gt;listen_addresses&lt;/code&gt;와 &lt;code&gt;pg_hba.conf&lt;/code&gt;가 모두 맞아야 하고, 네트워크 경로와 방화벽도 통과해야 한다.&lt;/p&gt;

&lt;h3&gt;Q. 기본값 &lt;code&gt;localhost&lt;/code&gt;는 TCP를 아예 안 쓰는 뜻일까?&lt;/h3&gt;
&lt;p&gt;아니다. 이는 loopback TCP/IP 연결을 허용하는 값이다. TCP 자체를 끄려면 빈 값으로 두고 소켓만 사용해야 한다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;listen_addresses&lt;/code&gt;는 PostgreSQL이 어느 TCP/IP 인터페이스에서 접속 시도를 받을지 정하는 설정이고, 2026년 6월 8일 기준 PostgreSQL 18.4 current 문서의 기본값은 &lt;code&gt;localhost&lt;/code&gt;다. 실무 판단 기준은 원격 접속 필요 여부, 특정 NIC만 열 수 있는지, 그리고 &lt;code&gt;pg_hba.conf&lt;/code&gt; 및 방화벽을 함께 좁힐 수 있는지다.&lt;/p&gt;
&lt;p&gt;같은 서버 내부 통신만 필요하면 기본값 유지가 단순하고, 원격 접속이 필요하면 전체 개방보다 필요한 인터페이스만 명시하는 편이 더 보수적이다. 어느 경우든 &lt;code&gt;listen_addresses&lt;/code&gt;와 &lt;code&gt;pg_hba.conf&lt;/code&gt;를 분리해서 생각하는 것이 핵심이다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/runtime-config-connection.html&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL Documentation: Connections and Authentication&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/auth-pg-hba-conf.html&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL Documentation: The pg_hba.conf File&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/index.htm&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL 18.4 Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/서버, DBMS</category>
      <category>Database Security</category>
      <category>DBMS</category>
      <category>listen_addresses</category>
      <category>Networking</category>
      <category>pg_hba.conf</category>
      <category>PostgreSQL</category>
      <category>Server Configuration</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/607</guid>
      <comments>https://jcdgods.tistory.com/607#entry607comment</comments>
      <pubDate>Mon, 8 Jun 2026 11:49:49 +0900</pubDate>
    </item>
    <item>
      <title>Python Path.copy 사용법</title>
      <link>https://jcdgods.tistory.com/606</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zOOpN/dJMcacQSJls/1IscczAal2miyQyJVG6eJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zOOpN/dJMcacQSJls/1IscczAal2miyQyJVG6eJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zOOpN/dJMcacQSJls/1IscczAal2miyQyJVG6eJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzOOpN%2FdJMcacQSJls%2F1IscczAal2miyQyJVG6eJK%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Python Path.copy 사용법&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;pathlib.Path.copy()&lt;/code&gt;는 파일이나 디렉터리 트리를 복사한 뒤 새 경로를 다시 &lt;code&gt;Path&lt;/code&gt; 객체로 돌려주는 표준 라이브러리 API다. 2026년 6월 7일 확인한 Python 3.14.5 공식 문서 기준으로 &lt;code&gt;Path.copy()&lt;/code&gt;와 &lt;code&gt;Path.copy_into()&lt;/code&gt;는 Python 3.14에서 추가됐다.&lt;/p&gt;
&lt;p&gt;실무에서 볼 핵심은 세 가지다. Python 3.14 이상에서만 쓸 수 있다는 점, 심볼릭 링크를 따라갈지 여부를 &lt;code&gt;follow_symlinks&lt;/code&gt;로 고른다는 점, 메타데이터 보존 범위를 &lt;code&gt;preserve_metadata&lt;/code&gt;로 조절한다는 점이다. 이미 코드가 &lt;code&gt;pathlib&lt;/code&gt; 중심이면 &lt;code&gt;shutil.copy2()&lt;/code&gt;와 &lt;code&gt;copytree()&lt;/code&gt;보다 읽기 쉬운 경우가 많다.&lt;/p&gt;

&lt;h2&gt;Path.copy는 언제 쓰면 될까?&lt;/h2&gt;
&lt;p&gt;다음 조건이면 &lt;code&gt;Path.copy()&lt;/code&gt;를 우선 검토할 만하다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;실행 환경이 Python 3.14 이상이다.&lt;/li&gt;
  &lt;li&gt;입력과 출력 경로를 모두 &lt;code&gt;Path&lt;/code&gt; 객체로 다루고 있다.&lt;/li&gt;
  &lt;li&gt;파일 하나와 디렉터리 트리를 같은 인터페이스로 복사하고 싶다.&lt;/li&gt;
  &lt;li&gt;복사 후 결과 경로를 다시 &lt;code&gt;Path&lt;/code&gt; 연산으로 바로 이어 쓰고 싶다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;반대로 Python 3.13 이하도 지원해야 하면 이 메서드를 기본 전제로 두면 안 된다. 그 경우에는 &lt;code&gt;shutil.copy2()&lt;/code&gt;와 &lt;code&gt;shutil.copytree()&lt;/code&gt;를 유지하거나 버전 분기가 필요하다.&lt;/p&gt;

&lt;h2&gt;기본 동작은 무엇일까?&lt;/h2&gt;
&lt;p&gt;Python 3.14 &lt;code&gt;pathlib&lt;/code&gt; 문서는 &lt;code&gt;Path.copy(target, *, follow_symlinks=True, preserve_metadata=False)&lt;/code&gt;가 파일 또는 디렉터리 트리를 대상 경로로 복사하고, 복사된 위치를 가리키는 새 &lt;code&gt;Path&lt;/code&gt; 객체를 반환한다고 설명한다.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;항목&lt;/th&gt;
      &lt;th&gt;공식 문서 기준&lt;/th&gt;
      &lt;th&gt;실무 의미&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;지원 버전&lt;/td&gt;
      &lt;td&gt;Python 3.14 이상&lt;/td&gt;
      &lt;td&gt;하위 버전 호환이 필요하면 대체 API가 필요하다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;반환값&lt;/td&gt;
      &lt;td&gt;새 &lt;code&gt;Path&lt;/code&gt; 객체&lt;/td&gt;
      &lt;td&gt;복사 직후 후속 처리 연결이 자연스럽다.&lt;/td&gt;
    &lt;/tr&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&gt;
      &lt;td&gt;심볼릭 링크 기본값&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;follow_symlinks=True&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;기본적으로 링크 자체보다 링크가 가리키는 내용을 복사한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;메타데이터 기본값&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;preserve_metadata=False&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;기본값만으로는 데이터와 디렉터리 구조 위주 복사라고 보는 편이 안전하다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;Path.copy와 Path.copy_into 차이는?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Path.copy()&lt;/code&gt;는 대상 전체 경로를 직접 지정할 때 쓰고, &lt;code&gt;Path.copy_into()&lt;/code&gt;는 이미 존재하는 디렉터리 안으로 복사할 때 쓴다. Python 3.14.5 문서는 &lt;code&gt;copy_into(target_dir)&lt;/code&gt;의 대상은 existing directory여야 하며, 나머지 인수는 &lt;code&gt;Path.copy()&lt;/code&gt;와 동일하게 처리된다고 설명한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pathlib import Path

src = Path(&quot;data/report.csv&quot;)
backup_dir = Path(&quot;backup&quot;)

copied_file = src.copy(&quot;backup/report-20260607.csv&quot;)
copied_into_dir = src.copy_into(backup_dir)

print(copied_file)
print(copied_into_dir)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;대상 디렉터리가 이미 정해져 있다면 &lt;code&gt;copy_into()&lt;/code&gt;가 의도를 더 잘 드러낸다. 반면 파일명을 바꾸거나 다른 경로 규칙으로 보낼 때는 &lt;code&gt;copy()&lt;/code&gt;가 더 직관적이다.&lt;/p&gt;

&lt;h2&gt;심볼릭 링크는 어떻게 처리될까?&lt;/h2&gt;
&lt;p&gt;공식 문서 기준 기본값은 &lt;code&gt;follow_symlinks=True&lt;/code&gt;다. 즉 원본이 심볼릭 링크라면 링크 자체를 복사하는 대신, 그 링크가 가리키는 대상을 복사한다. 링크 자체를 다시 만들고 싶으면 &lt;code&gt;follow_symlinks=False&lt;/code&gt;를 명시해야 한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pathlib import Path

src = Path(&quot;current.log&quot;)

# 링크 대상 내용을 복사
copied_target = src.copy(&quot;archive/log-copy.txt&quot;)

# 심볼릭 링크 자체를 다시 생성
copied_link = src.copy(&quot;archive/current.log&quot;, follow_symlinks=False)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;배포 스크립트나 백업 작업에서 이 차이는 중요하다. 실제 파일 내용을 보존해야 하는지, 링크 구조 자체를 유지해야 하는지 먼저 정해 두는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;preserve_metadata는 어디까지 믿어야 할까?&lt;/h2&gt;
&lt;p&gt;Python 3.14.5 문서는 &lt;code&gt;preserve_metadata=False&lt;/code&gt;일 때 디렉터리 구조와 파일 데이터만 보장된다고 설명한다. &lt;code&gt;True&lt;/code&gt;로 바꾸면 권한, 플래그, 접근 시각, 수정 시각, 확장 속성을 지원되는 환경에서 복사하려고 시도한다. 다만 파일 복사 시 Windows에서는 메타데이터가 항상 보존되므로 이 인수는 효과가 없다고 명시한다.&lt;/p&gt;
&lt;p&gt;즉 권한 비트나 타임스탬프가 의미 있는 운영 스크립트라면 기본값을 그대로 두기보다 &lt;code&gt;preserve_metadata=True&lt;/code&gt;를 검토해야 한다. 반대로 단순 산출물 백업이나 캐시 복사처럼 내용만 중요하면 기본값도 충분할 수 있다.&lt;/p&gt;

&lt;h2&gt;shutil.copy2, copytree와는 어떻게 고르면 될까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Path.copy()&lt;/code&gt;는 새 기능이지만 기존 API를 완전히 대체해야 한다는 뜻은 아니다. Python 공식 문서 기준으로 &lt;code&gt;shutil.copy2()&lt;/code&gt;는 파일 메타데이터를 함께 복사하고, &lt;code&gt;copytree()&lt;/code&gt;는 디렉터리 트리를 재귀 복사한다. 따라서 선택 기준은 주로 코드 스타일과 지원 버전에 달려 있다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Python 3.14 이상이고 &lt;code&gt;Path&lt;/code&gt; 중심 코드라면 &lt;code&gt;Path.copy()&lt;/code&gt;가 더 자연스럽다.&lt;/li&gt;
  &lt;li&gt;Python 3.13 이하 호환이 필요하면 &lt;code&gt;shutil.copy2()&lt;/code&gt;, &lt;code&gt;copytree()&lt;/code&gt;가 안전하다.&lt;/li&gt;
  &lt;li&gt;대상 디렉터리가 이미 있고 그 안으로 넣는 의미를 드러내고 싶으면 &lt;code&gt;Path.copy_into()&lt;/code&gt;가 읽기 쉽다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Python 문서는 또 이 메서드가 운영체제와 파일시스템이 지원하면 copy-on-write 방식의 lightweight copy를 수행할 수 있다고 설명한다. 다만 이는 지원 환경에서의 최적화이므로, 모든 플랫폼에서 같은 성능 특성을 기대하는 식으로 일반화하면 안 된다.&lt;/p&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;1. Path.copy는 기존 대상 파일을 덮어쓸 수 있을까?&lt;/h3&gt;
&lt;p&gt;그럴 수 있다. 공식 문서는 원본이 파일이고 대상이 기존 파일이면 그 파일이 대체된다고 설명한다.&lt;/p&gt;

&lt;h3&gt;2. Path.copy_into는 대상 디렉터리를 자동 생성할까?&lt;/h3&gt;
&lt;p&gt;공식 문서 표현은 existing directory다. 따라서 대상 디렉터리가 이미 있다고 보고 쓰는 편이 맞다.&lt;/p&gt;

&lt;h3&gt;3. Python 3.12나 3.13에서도 같은 메서드가 있을까?&lt;/h3&gt;
&lt;p&gt;없다. Python 3.14 변경 문서는 &lt;code&gt;copy()&lt;/code&gt;와 &lt;code&gt;copy_into()&lt;/code&gt;를 3.14에서 추가된 기능으로 명시한다.&lt;/p&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. 새 프로젝트라면 shutil 대신 Path.copy로 통일해도 될까?&lt;/h3&gt;
&lt;p&gt;실행 환경을 Python 3.14 이상으로 고정할 수 있고, 코드베이스가 &lt;code&gt;pathlib&lt;/code&gt; 중심이라면 충분히 검토할 만하다. 다만 하위 버전 호환 요구가 남아 있으면 아직은 &lt;code&gt;shutil&lt;/code&gt; 계열이 더 무난하다.&lt;/p&gt;

&lt;h3&gt;Q. 메타데이터 보존이 중요하면 어떤 값을 먼저 볼까?&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;preserve_metadata=True&lt;/code&gt; 여부를 먼저 확인하는 편이 좋다. 기본값은 데이터와 구조 보장 쪽에 가깝고, 자세한 보존 범위는 플랫폼 지원 여부에 따라 달라질 수 있다.&lt;/p&gt;

&lt;h3&gt;Q. 링크 파일을 링크 그대로 백업하려면?&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;follow_symlinks=False&lt;/code&gt;를 사용해야 한다. 기본값은 링크 대상을 복사하는 쪽이다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Path.copy()&lt;/code&gt;와 &lt;code&gt;Path.copy_into()&lt;/code&gt;는 Python 3.14에서 추가된 파일시스템 복사 API다. 2026년 6월 7일 확인한 Python 3.14.5 공식 문서 기준으로, 이 메서드는 파일과 디렉터리 트리를 복사하고 결과를 다시 &lt;code&gt;Path&lt;/code&gt; 객체로 돌려준다.&lt;/p&gt;
&lt;p&gt;선택 기준은 단순하다. Python 3.14 이상인지, 심볼릭 링크를 따라갈지, 메타데이터 보존이 중요한지 세 가지를 먼저 보면 된다. 그 조건이 맞으면 &lt;code&gt;pathlib&lt;/code&gt; 기반 코드에서는 꽤 깔끔한 기본 선택지가 된다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.python.org/3.14/library/pathlib.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Python 3.14.5 pathlib 문서&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.python.org/3/whatsnew/3.14.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;What’s New in Python 3.14&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.python.org/3/library/shutil.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Python 3.14.5 shutil 문서&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/C, C++, Java, Python</category>
      <category>Path.copy</category>
      <category>Path.copy_into</category>
      <category>pathlib</category>
      <category>python</category>
      <category>Python3.14</category>
      <category>shutil</category>
      <category>파일복사</category>
      <category>표준라이브러리</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/606</guid>
      <comments>https://jcdgods.tistory.com/606#entry606comment</comments>
      <pubDate>Sun, 7 Jun 2026 11:00:34 +0900</pubDate>
    </item>
    <item>
      <title>Cross-Origin-Embedder-Policy 기준 정리</title>
      <link>https://jcdgods.tistory.com/605</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v2aWE/dJMcabYNyVf/7ol5g61Q9fkYK8WsMNNXA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v2aWE/dJMcabYNyVf/7ol5g61Q9fkYK8WsMNNXA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v2aWE/dJMcabYNyVf/7ol5g61Q9fkYK8WsMNNXA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv2aWE%2FdJMcabYNyVf%2F7ol5g61Q9fkYK8WsMNNXA0%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Cross-Origin-Embedder-Policy 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;Cross-Origin-Embedder-Policy&lt;/code&gt;(COEP)는 현재 문서가 &lt;code&gt;no-cors&lt;/code&gt; 모드로 요청되는 교차 출처 리소스를 어떤 조건에서 불러올 수 있는지 정하는 HTTP 응답 헤더다. 2026년 6월 6일 기준 MDN 문서를 보면 값은 &lt;code&gt;unsafe-none&lt;/code&gt;, &lt;code&gt;require-corp&lt;/code&gt;, &lt;code&gt;credentialless&lt;/code&gt;를 사용하며, 핵심 목적은 교차 출처 리소스가 명시적으로 허용되었는지 확인하고 필요하면 교차 출처 격리(cross-origin isolation) 조건을 맞추는 데 있다.&lt;/p&gt;
&lt;p&gt;실무에서는 COEP를 CORS의 다른 이름처럼 이해하면 운영이 꼬이기 쉽다. COEP는 문서 쪽에서 &quot;어떤 외부 리소스를 받아들일지&quot;를 더 엄격하게 정하는 정책이고, 실제 읽기 허용은 여전히 CORS나 리소스 응답의 CORP 설정이 담당한다. 이 글은 값별 차이와 적용 기준, 그리고 SharedArrayBuffer 같은 기능과의 관계를 공식 문서 기준으로 정리한다.&lt;/p&gt;

&lt;h2&gt;Cross-Origin-Embedder-Policy는 무엇을 제어할까?&lt;/h2&gt;
&lt;p&gt;MDN은 COEP를 현재 문서가 교차 출처 리소스를 로드하거나 임베드할 때의 정책을 구성하는 헤더로 설명한다. 특히 기본적으로 &lt;code&gt;no-cors&lt;/code&gt;로 요청되는 이미지, 스크립트, 스타일시트, iframe 같은 리소스에 직접적인 영향을 준다.&lt;/p&gt;
&lt;p&gt;중요한 점은 COEP가 CORS를 대체하지 않는다는 점이다. MDN은 &lt;code&gt;cors&lt;/code&gt; 모드 요청의 임베딩 정책은 CORS가 제어한다고 설명한다. 즉 COEP는 &quot;무조건 외부 리소스를 막는 헤더&quot;가 아니라, &lt;code&gt;no-cors&lt;/code&gt; 경로에서 명시적 허용이 없는 교차 출처 리소스를 더 엄격하게 다루는 정책이다.&lt;/p&gt;

&lt;h2&gt;값은 어떻게 다를까?&lt;/h2&gt;
&lt;h3&gt;1. &lt;code&gt;unsafe-none&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;기본값이다. MDN은 이 값에서 문서가 CORP 헤더 없이도 &lt;code&gt;no-cors&lt;/code&gt; 교차 출처 리소스를 로드할 수 있다고 설명한다. 호환성은 가장 높지만, 교차 출처 격리를 만들기 위한 기반은 되지 않는다.&lt;/p&gt;

&lt;h3&gt;2. &lt;code&gt;require-corp&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;교차 출처의 &lt;code&gt;no-cors&lt;/code&gt; 리소스가 로드되려면, 같은 origin이거나 응답이 적절한 &lt;code&gt;Cross-Origin-Resource-Policy&lt;/code&gt;(CORP) 헤더를 보내야 한다. 또는 요청이 &lt;code&gt;cors&lt;/code&gt; 모드로 전환되어 CORS 허용을 받아야 한다. 외부 자산을 많이 쓰지 않는 서비스에서 가장 먼저 검토하는 값이 보통 이것이다.&lt;/p&gt;

&lt;h3&gt;3. &lt;code&gt;credentialless&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;MDN은 이 값을 사용하면 &lt;code&gt;no-cors&lt;/code&gt; 교차 출처 리소스를 CORP 없이 로드할 수 있지만, 그 요청은 자격 증명 없이 전송된다고 설명한다. 즉 쿠키는 요청에서 빠지고, 응답에 포함된 쿠키도 무시된다. 다른 요청 모드의 동작은 &lt;code&gt;require-corp&lt;/code&gt;와 같으므로, 모든 서드파티 자산 문제가 자동으로 해결되는 것은 아니다.&lt;/p&gt;

&lt;h2&gt;실무에서는 언제 COEP를 켤까?&lt;/h2&gt;
&lt;h3&gt;교차 출처 격리가 필요한 기능을 쓸 때&lt;/h3&gt;
&lt;p&gt;MDN은 &lt;code&gt;SharedArrayBuffer&lt;/code&gt;나 더 정밀한 타이머 같은 기능을 쓰려면 문서가 cross-origin isolated 상태여야 한다고 설명한다. 이 상태를 만들려면 COEP와 COOP를 함께 맞춰야 하므로, 브라우저 기능 요구사항이 먼저라면 COEP 검토가 거의 필수다.&lt;/p&gt;

&lt;h3&gt;서드파티 자산을 통제 가능한 범위로 줄이고 싶을 때&lt;/h3&gt;
&lt;p&gt;외부 이미지, 폰트, 스크립트, iframe이 어디서 어떻게 로드되는지 관리 가능한 서비스라면 &lt;code&gt;require-corp&lt;/code&gt;로 운영 기준을 명확히 만들 수 있다. 반대로 광고, 분석, 위젯 등 제어하기 어려운 외부 자산이 많다면 도입 전에 차단 영향을 먼저 확인해야 한다.&lt;/p&gt;

&lt;h2&gt;COEP와 CORP, CORS는 어떻게 연결될까?&lt;/h2&gt;
&lt;p&gt;COEP는 문서 쪽의 요구사항이고, CORP와 CORS는 리소스 쪽의 허용 메커니즘이다. MDN은 &lt;code&gt;require-corp&lt;/code&gt;에서 교차 출처 리소스가 로드되려면, &lt;code&gt;no-cors&lt;/code&gt; 요청이라면 CORP가 허용해야 하고 &lt;code&gt;cors&lt;/code&gt; 요청이라면 CORS 허용을 받아야 한다고 설명한다.&lt;/p&gt;
&lt;p&gt;따라서 &quot;COEP를 켰더니 이미지가 안 뜬다&quot;는 상황은 문서 설정만의 문제가 아닐 수 있다. 리소스 제공 서버가 CORP를 보내지 않거나, 프런트엔드가 &lt;code&gt;crossorigin&lt;/code&gt; 속성을 통해 CORS 경로로 바꾸지 않았을 가능성을 함께 봐야 한다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;cross-origin isolation과는 어떤 관계일까?&lt;/h2&gt;
&lt;p&gt;MDN의 COEP 문서는 문서가 cross-origin isolated 상태가 되려면 COEP가 &lt;code&gt;require-corp&lt;/code&gt; 또는 &lt;code&gt;credentialless&lt;/code&gt;여야 하고, COOP는 &lt;code&gt;same-origin&lt;/code&gt;이어야 한다고 설명한다. 여기에 &lt;code&gt;Permissions-Policy: cross-origin-isolated&lt;/code&gt;가 이를 막지 않아야 한다.&lt;/p&gt;
&lt;p&gt;web.dev 가이드도 같은 맥락에서, 최상위 문서에 &lt;code&gt;Cross-Origin-Opener-Policy: same-origin&lt;/code&gt;과 &lt;code&gt;Cross-Origin-Embedder-Policy: require-corp&lt;/code&gt;를 설정해 교차 출처 격리를 만든 뒤 &lt;code&gt;self.crossOriginIsolated&lt;/code&gt;로 상태를 확인할 수 있다고 설명한다. 즉 COEP 단독 도입이 아니라 전체 리소스 체인과 팝업 동작까지 같이 점검해야 한다.&lt;/p&gt;

&lt;h2&gt;도입 전에 확인할 점&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;현재 페이지가 불러오는 이미지, 폰트, 스크립트, iframe 중 교차 출처 자산 목록을 먼저 정리해야 한다.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;require-corp&lt;/code&gt;를 검토한다면 각 자산이 CORP 또는 CORS를 지원하는지 확인해야 한다.&lt;/li&gt;
  &lt;li&gt;쿠키가 필요한 교차 출처 &lt;code&gt;no-cors&lt;/code&gt; 리소스가 있다면 &lt;code&gt;credentialless&lt;/code&gt;와 충돌할 수 있다.&lt;/li&gt;
  &lt;li&gt;교차 출처 격리가 목적이라면 COEP만이 아니라 COOP, Permissions-Policy, 워커 스크립트 로딩 방식까지 함께 점검해야 한다.&lt;/li&gt;
  &lt;li&gt;서드파티 분석 도구나 광고 태그가 많다면 배포 전 네트워크 패널에서 차단 상태를 반드시 확인해야 한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. COEP를 설정하면 CORS 없이도 외부 API를 읽을 수 있나?&lt;/h3&gt;
&lt;p&gt;아니다. MDN은 &lt;code&gt;cors&lt;/code&gt; 모드 요청의 허용은 여전히 CORS가 제어한다고 설명한다. COEP는 문서가 받아들일 수 있는 교차 출처 리소스 조건을 정하는 정책이다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;require-corp&lt;/code&gt;를 쓰면 모든 외부 리소스가 차단되나?&lt;/h3&gt;
&lt;p&gt;그렇게 단순하지 않다. 같은 origin 리소스는 로드할 수 있고, 교차 출처 리소스도 CORP가 허용하거나 CORS로 허용되면 로드할 수 있다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;credentialless&lt;/code&gt;는 &lt;code&gt;require-corp&lt;/code&gt;의 완전한 대체인가?&lt;/h3&gt;
&lt;p&gt;아니다. MDN은 &lt;code&gt;credentialless&lt;/code&gt;가 일부 &lt;code&gt;no-cors&lt;/code&gt; 교차 출처 리소스를 CORP 없이 불러오게 해주지만, 요청에서 쿠키를 빼고 응답 쿠키도 무시한다고 설명한다. 쿠키 의존 자산이 있으면 동작이 달라질 수 있다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;COEP는 외부 리소스를 얼마나 엄격하게 받아들일지 정하는 문서 정책이다. 2026년 6월 6일 기준 공식 문서상 기본 판단은 명확하다. 교차 출처 격리나 더 엄격한 리소스 통제가 필요하면 &lt;code&gt;require-corp&lt;/code&gt;부터 검토하고, 일부 &lt;code&gt;no-cors&lt;/code&gt; 자산을 쿠키 없이 허용해야 할 때만 &lt;code&gt;credentialless&lt;/code&gt;를 본다.&lt;/p&gt;
&lt;p&gt;중요한 것은 COEP를 단독 옵션으로 보지 않는 것이다. 실제 운영에서는 CORP, CORS, COOP, 그리고 현재 페이지가 불러오는 서드파티 자산 구조를 함께 봐야 차단 없이 적용할 수 있다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Embedder-Policy&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN: Cross-Origin-Embedder-Policy header&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cross-Origin_Resource_Policy&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN: Cross-Origin Resource Policy (CORP)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Opener-Policy&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN: Cross-Origin-Opener-Policy header&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://web.dev/articles/coop-coep?hl=en&quot; rel=&quot;noopener noreferrer&quot;&gt;web.dev: Making your website cross-origin isolated using COOP and COEP&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/HTML, Javascript, CSS</category>
      <category>COEP</category>
      <category>Coop</category>
      <category>corp</category>
      <category>CORS</category>
      <category>Cross-Origin Isolation</category>
      <category>Cross-Origin-Embedder-Policy</category>
      <category>HTTP Header</category>
      <category>javascript</category>
      <category>web security</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/605</guid>
      <comments>https://jcdgods.tistory.com/605#entry605comment</comments>
      <pubDate>Sat, 6 Jun 2026 12:44:05 +0900</pubDate>
    </item>
    <item>
      <title>PostgreSQL password_encryption 기준 정리</title>
      <link>https://jcdgods.tistory.com/604</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ASL1T/dJMcaaeuMNr/lazq6JKtUZWV3ikb0o8Yg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ASL1T/dJMcaaeuMNr/lazq6JKtUZWV3ikb0o8Yg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ASL1T/dJMcaaeuMNr/lazq6JKtUZWV3ikb0o8Yg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FASL1T%2FdJMcaaeuMNr%2Flazq6JKtUZWV3ikb0o8Yg0%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;PostgreSQL password_encryption 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;password_encryption&lt;/code&gt;은 PostgreSQL에서 새 비밀번호를 저장할 때 어떤 해시 알고리즘을 쓸지 결정하는 설정이다. 2026년 6월 5일 기준 PostgreSQL 공식 문서의 current 버전은 18.4이며, 이 문서에서는 &lt;code&gt;password_encryption&lt;/code&gt;의 기본값을 &lt;code&gt;scram-sha-256&lt;/code&gt;로 안내한다.&lt;/p&gt;
&lt;p&gt;이 설정은 로그인 자체를 허용할지 말지를 직접 정하는 값은 아니지만, 이후 &lt;code&gt;pg_hba.conf&lt;/code&gt;에서 선택하는 인증 방식과 실제 호환성에 영향을 준다. 운영 환경에서는 구형 클라이언트 호환이 필요한지, MD5를 언제까지 유지할지, 비밀번호를 다시 설정해야 하는 시점이 언제인지가 핵심 판단 기준이다.&lt;/p&gt;

&lt;h2&gt;password_encryption은 무엇을 바꿀까?&lt;/h2&gt;
&lt;p&gt;공식 문서 기준으로 &lt;code&gt;password_encryption&lt;/code&gt;은 &lt;code&gt;CREATE ROLE&lt;/code&gt; 또는 &lt;code&gt;ALTER ROLE&lt;/code&gt;로 비밀번호를 지정하는 시점에만 적용된다. 즉 값을 바꾼다고 기존 계정의 저장된 비밀번호 해시가 자동으로 재변환되지는 않는다.&lt;/p&gt;
&lt;p&gt;현재 지원되는 값은 &lt;code&gt;scram-sha-256&lt;/code&gt;와 &lt;code&gt;md5&lt;/code&gt;다. 다만 current 문서는 기본값이 &lt;code&gt;scram-sha-256&lt;/code&gt;이고, MD5로 저장된 비밀번호 지원은 deprecated이며 향후 릴리스에서 제거될 예정이라고 명시한다.&lt;/p&gt;

&lt;h2&gt;기본값은 무엇이고 왜 중요할까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;항목&lt;/th&gt;
      &lt;th&gt;공식 문서 기준&lt;/th&gt;
      &lt;th&gt;실무 의미&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;기본값&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;scram-sha-256&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;새로 설정하는 비밀번호는 기본적으로 SCRAM 해시로 저장된다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;레거시 값&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;md5&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;구형 클라이언트 호환이 필요할 때만 제한적으로 고려한다.&lt;/td&gt;
    &lt;/tr&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&gt;
      &lt;td&gt;추가 관련 설정&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;scram_iterations&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;SCRAM 저장 시 반복 횟수를 높일 수 있지만, 기존 비밀번호에는 소급 적용되지 않는다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;운영자가 가장 자주 놓치는 부분은 적용 시점이다. 서버 설정을 &lt;code&gt;scram-sha-256&lt;/code&gt;로 바꿔도, 이미 MD5로 저장된 계정은 비밀번호를 다시 설정하기 전까지 그대로 MD5 해시를 유지할 수 있다.&lt;/p&gt;

&lt;h2&gt;대부분의 환경에서 어떤 값을 고르면 될까?&lt;/h2&gt;
&lt;p&gt;PostgreSQL 공식 문서는 비밀번호 기반 인증 방식 중 &lt;code&gt;scram-sha-256&lt;/code&gt;를 현재 제공되는 방식 가운데 가장 안전한 선택으로 설명한다. 따라서 일반적인 신규 구축이나 운영 환경에서는 &lt;code&gt;password_encryption = 'scram-sha-256'&lt;/code&gt;를 기본값으로 유지하는 편이 자연스럽다.&lt;/p&gt;
&lt;p&gt;예외는 구형 드라이버나 오래된 클라이언트 라이브러리가 아직 SCRAM을 지원하지 않는 경우다. 이때는 인증 실패를 피하려고 MD5를 잠시 유지할 수 있지만, current 문서가 MD5 저장 비밀번호 지원의 제거 예정 상태를 명확히 밝히고 있으므로 장기 기본값으로 보기는 어렵다.&lt;/p&gt;

&lt;h2&gt;pg_hba.conf와는 어떻게 연결될까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;password_encryption&lt;/code&gt;은 저장 형식을 정하고, &lt;code&gt;pg_hba.conf&lt;/code&gt;는 접속 시 어떤 인증 방법을 요구할지 정한다. 두 설정은 분리되어 있지만 실제 접속 결과에서는 함께 작동한다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;비밀번호가 SCRAM 형식으로 저장돼 있으면 &lt;code&gt;scram-sha-256&lt;/code&gt; 인증을 사용할 수 있다.&lt;/li&gt;
  &lt;li&gt;문서에 따르면 &lt;code&gt;pg_hba.conf&lt;/code&gt;에 &lt;code&gt;md5&lt;/code&gt;를 적어도, 저장된 비밀번호가 SCRAM 형식이면 SCRAM 기반 인증이 자동 선택될 수 있다.&lt;/li&gt;
  &lt;li&gt;비밀번호가 MD5 형식으로 저장돼 있으면 &lt;code&gt;scram-sha-256&lt;/code&gt; 인증 전용 구성으로는 바로 전환할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;즉 서버 설정 파일 하나만 바꾸는 방식으로 전환이 끝나지 않는다. 저장된 비밀번호 형식과 &lt;code&gt;pg_hba.conf&lt;/code&gt;의 인증 방식이 서로 맞아야 한다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;MD5에서 SCRAM으로 바꿀 때 순서는?&lt;/h2&gt;
&lt;p&gt;공식 문서의 전환 설명은 비교적 단순하다. 먼저 사용 중인 클라이언트 라이브러리가 SCRAM을 지원하는지 확인하고, 그다음 &lt;code&gt;postgresql.conf&lt;/code&gt;에서 &lt;code&gt;password_encryption = 'scram-sha-256'&lt;/code&gt;를 설정한다. 이후 각 사용자 비밀번호를 다시 설정하고, 마지막으로 &lt;code&gt;pg_hba.conf&lt;/code&gt;의 인증 방식도 &lt;code&gt;scram-sha-256&lt;/code&gt;로 정리한다.&lt;/p&gt;
&lt;p&gt;이 순서가 중요한 이유는 중간 단계 호환성 때문이다. 비밀번호를 다시 저장하기 전까지는 기존 해시가 남아 있고, 드라이버가 SCRAM을 지원하지 않으면 인증 실패가 발생할 수 있다.&lt;/p&gt;

&lt;h2&gt;scram_iterations는 같이 봐야 할까?&lt;/h2&gt;
&lt;p&gt;current 문서는 &lt;code&gt;scram_iterations&lt;/code&gt;의 기본값을 &lt;code&gt;4096&lt;/code&gt;으로 설명한다. 반복 횟수를 높이면 저장된 비밀번호 해시에 대한 brute-force 공격 저항성이 커지지만, 인증 시 계산 비용도 증가한다.&lt;/p&gt;
&lt;p&gt;여기서도 적용 시점은 같다. 이 값을 바꿔도 기존 SCRAM 비밀번호에는 영향이 없고, 새로 비밀번호를 설정한 계정에만 새 반복 횟수가 반영된다. 그래서 반복 횟수 조정은 계정 비밀번호 갱신 계획과 같이 보는 편이 맞다.&lt;/p&gt;

&lt;h2&gt;자주 헷갈리는 질문&lt;/h2&gt;
&lt;h3&gt;1. 설정만 바꾸면 기존 계정도 바로 SCRAM이 될까?&lt;/h3&gt;
&lt;p&gt;아니다. 공식 문서 기준으로 &lt;code&gt;password_encryption&lt;/code&gt;은 비밀번호를 저장하는 시점에 적용된다. 기존 계정을 SCRAM으로 전환하려면 비밀번호를 다시 설정해야 한다.&lt;/p&gt;

&lt;h3&gt;2. md5를 쓰면 평문 비밀번호가 서버에 저장될까?&lt;/h3&gt;
&lt;p&gt;그렇지는 않다. 문서는 MD5도 해시 형태로 저장된다고 설명한다. 다만 current 문서는 MD5 알고리즘 자체가 더 이상 충분히 안전하지 않다고 보고, MD5 저장 비밀번호 지원을 deprecated 상태로 다룬다.&lt;/p&gt;

&lt;h3&gt;3. SCRAM이면 네트워크도 자동으로 암호화될까?&lt;/h3&gt;
&lt;p&gt;아니다. SCRAM은 비밀번호 인증 방식에 대한 이야기다. 네트워크 전송 암호화는 SSL/TLS나 GSSAPI 암호화 같은 별도 설정이 담당한다.&lt;/p&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. 신규 PostgreSQL 서버라면 굳이 값을 바꿔야 할까?&lt;/h3&gt;
&lt;p&gt;current PostgreSQL 18.4 문서 기준 기본값이 이미 &lt;code&gt;scram-sha-256&lt;/code&gt;이므로, 특별한 레거시 호환 요구가 없다면 그대로 두는 편이 무난하다.&lt;/p&gt;

&lt;h3&gt;Q. 구형 클라이언트가 하나라도 있으면 전환을 미뤄야 할까?&lt;/h3&gt;
&lt;p&gt;적어도 그 클라이언트가 SCRAM을 지원하는지 확인하기 전에는 일괄 전환을 바로 진행하기 어렵다. 공식 문서도 오래된 클라이언트가 SCRAM을 지원하지 않을 수 있다고 설명한다.&lt;/p&gt;

&lt;h3&gt;Q. 보안만 보면 md5를 계속 유지할 이유가 있을까?&lt;/h3&gt;
&lt;p&gt;공식 문서 기준으로는 호환성 전환 구간을 제외하면 설득력이 크지 않다. current 문서에서 SCRAM을 더 안전한 방식으로 설명하고, MD5 저장 비밀번호 지원은 제거 예정이라고 밝히기 때문이다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;password_encryption&lt;/code&gt;은 PostgreSQL의 비밀번호 저장 형식을 결정하는 설정이고, 2026년 6월 5일 기준 current PostgreSQL 18.4 문서의 기본값은 &lt;code&gt;scram-sha-256&lt;/code&gt;다. 실무에서 중요한 기준은 신규 서버인지, 구형 클라이언트 호환이 필요한지, 기존 계정 비밀번호를 다시 설정할 수 있는지 세 가지다.&lt;/p&gt;
&lt;p&gt;신규 구축은 SCRAM을 기본값으로 두고, 기존 환경은 드라이버 호환 확인 후 비밀번호 재설정과 &lt;code&gt;pg_hba.conf&lt;/code&gt; 정리를 함께 진행하는 방식이 공식 문서와 가장 잘 맞는다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/runtime-config-connection.html&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL Documentation: Connections and Authentication&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/encryption-options.html&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL Documentation: Encryption Options&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/18/auth-password.html&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL Documentation: Password Authentication&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL Documentation Home&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/서버, DBMS</category>
      <category>Authentication</category>
      <category>Database Security</category>
      <category>DBMS</category>
      <category>md5</category>
      <category>password_encryption</category>
      <category>PostgreSQL</category>
      <category>scram</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/604</guid>
      <comments>https://jcdgods.tistory.com/604#entry604comment</comments>
      <pubDate>Fri, 5 Jun 2026 11:40:05 +0900</pubDate>
    </item>
    <item>
      <title>Git sparse-checkout 사용법</title>
      <link>https://jcdgods.tistory.com/603</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvlnwt/dJMcada6z3n/pKKZrcK5N77XSG25KwWQD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvlnwt/dJMcada6z3n/pKKZrcK5N77XSG25KwWQD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvlnwt/dJMcada6z3n/pKKZrcK5N77XSG25KwWQD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcvlnwt%2FdJMcada6z3n%2FpKKZrcK5N77XSG25KwWQD1%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Git sparse-checkout 사용법&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;git sparse-checkout&lt;/code&gt;은 작업 트리에 필요한 경로만 체크아웃해서 큰 저장소를 더 가볍게 다루게 해 주는 Git 기능이다. Git 공식 문서 기준으로 이 기능은 monorepo처럼 디렉터리가 많은 저장소에서 특정 하위 경로만 집중해서 작업할 때 유용하다.&lt;/p&gt;
&lt;p&gt;핵심은 세 가지다. 어떤 경로를 작업 트리에 펼칠지, 패턴을 cone mode로 단순하게 관리할지, 그리고 이것이 전체 저장소를 부분 clone하는 기능과는 어떻게 다른지다. 이 글은 Git 공식 문서를 기준으로 &lt;code&gt;init&lt;/code&gt;, &lt;code&gt;set&lt;/code&gt;, &lt;code&gt;add&lt;/code&gt;, &lt;code&gt;reapply&lt;/code&gt;, &lt;code&gt;disable&lt;/code&gt;와 &lt;code&gt;git clone --sparse&lt;/code&gt;의 관계를 정리한다.&lt;/p&gt;

&lt;h2&gt;Git sparse-checkout은 정확히 무엇을 줄여 줄까?&lt;/h2&gt;
&lt;p&gt;공식 문서에서 sparse checkout은 working tree에 있는 파일 수를 줄이는 기능으로 설명된다. 저장소 이력 자체를 없애는 것이 아니라, 현재 작업 디렉터리에 펼쳐지는 경로 범위를 제한하는 방식에 가깝다.&lt;/p&gt;
&lt;p&gt;그래서 저장소 전체 브랜치 구조를 이해하거나 커밋 기록을 조회하는 일과, 실제 작업 트리에 어떤 파일을 꺼내 둘지를 분리해서 생각해야 한다. 디스크 사용량과 파일 탐색 복잡도를 줄이고 싶을 때 특히 직접적인 효과가 있다.&lt;/p&gt;

&lt;h2&gt;언제 sparse-checkout이 가장 잘 맞을까?&lt;/h2&gt;
&lt;p&gt;가장 대표적인 경우는 monorepo에서 한두 개 서비스 디렉터리만 수정할 때다. 예를 들어 &lt;code&gt;frontend/&lt;/code&gt;만 담당하는 개발자가 매번 백엔드, 인프라, 문서 디렉터리까지 모두 작업 트리에 펼칠 이유는 크지 않다.&lt;/p&gt;
&lt;p&gt;반대로 저장소 전체를 자주 grep 하거나, 여러 하위 디렉터리를 동시에 리팩터링하거나, 빌드 시스템이 광범위한 경로를 기본 전제로 삼는다면 sparse-checkout이 기대만큼 단순하지 않을 수 있다. 이 경우에는 패턴 관리 비용까지 같이 봐야 한다.&lt;/p&gt;

&lt;h2&gt;가장 먼저 알아둘 명령은 무엇일까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;목적&lt;/th&gt;
      &lt;th&gt;예시 명령&lt;/th&gt;
      &lt;th&gt;의미&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;초기화&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git sparse-checkout init --cone&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;cone mode 기준으로 sparse checkout 사용을 시작한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;대상 경로 지정&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git sparse-checkout set frontend docs&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;작업 트리에 남길 디렉터리를 지정한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;대상 경로 추가&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git sparse-checkout add scripts&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;기존 설정을 유지한 채 경로를 더 포함한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;패턴 재적용&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git sparse-checkout reapply&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;충돌이나 설정 변경 뒤 sparse 규칙을 다시 작업 트리에 반영한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;해제&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git sparse-checkout disable&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;전체 작업 트리로 되돌린다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;실무에서는 &lt;code&gt;set&lt;/code&gt;과 &lt;code&gt;add&lt;/code&gt;의 차이를 분명히 기억하는 편이 중요하다. &lt;code&gt;set&lt;/code&gt;은 현재 sparse 목록을 새로 정하는 성격이 강하고, &lt;code&gt;add&lt;/code&gt;는 이미 있는 범위에 경로를 더하는 명령이다.&lt;/p&gt;

&lt;h2&gt;cone mode는 왜 기본값처럼 다뤄질까?&lt;/h2&gt;
&lt;p&gt;Git 공식 문서는 cone mode를 디렉터리 단위로 다루는 단순한 패턴 집합으로 설명한다. 지정한 디렉터리와 그 조상, 그리고 형제 디렉터리의 즉시 하위 파일까지 포함하는 방식이라 사람이 읽고 유지하기 쉽다.&lt;/p&gt;
&lt;p&gt;대부분의 개발 저장소는 파일 개별 패턴보다 디렉터리 기준으로 작업 범위를 정하는 경우가 많다. 그래서 특별한 glob 패턴이 꼭 필요하지 않다면 cone mode가 운영 부담이 더 적다.&lt;/p&gt;

&lt;h2&gt;non-cone mode는 언제만 고려하면 될까?&lt;/h2&gt;
&lt;p&gt;공식 문서에 따르면 non-cone mode는 보다 일반적인 패턴을 다룰 수 있지만, 복잡성과 성능 측면에서 tradeoff가 있다. 파일 단위나 복잡한 include-exclude 패턴이 필요할 때만 신중하게 선택하는 편이 맞다.&lt;/p&gt;
&lt;p&gt;즉 &quot;특정 디렉터리만 작업하겠다&quot;는 수준이라면 cone mode로 충분한 경우가 많다. non-cone mode가 필요한 이유를 명확히 설명하기 어렵다면 기본 선택지로 보기에는 과하다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;git clone --sparse&lt;/code&gt;와 무엇이 다를까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git clone --sparse&lt;/code&gt;는 clone 시점부터 최상위 디렉터리만 initially present 하도록 시작하는 옵션이다. 이후 필요한 경로는 &lt;code&gt;git sparse-checkout set&lt;/code&gt;이나 &lt;code&gt;add&lt;/code&gt;로 넓혀 갈 수 있다.&lt;/p&gt;
&lt;p&gt;반면 기존에 이미 clone한 저장소에서도 &lt;code&gt;git sparse-checkout&lt;/code&gt;은 바로 적용할 수 있다. 즉 새로 받는 저장소라면 &lt;code&gt;clone --sparse&lt;/code&gt;, 이미 로컬에 있는 저장소라면 &lt;code&gt;sparse-checkout&lt;/code&gt; 명령군으로 이해하면 흐름이 단순하다.&lt;/p&gt;

&lt;h2&gt;partial clone과 sparse-checkout은 같은 기능일까?&lt;/h2&gt;
&lt;p&gt;같지 않다. Git 공식 문서에서 &lt;code&gt;git clone --filter=&amp;lt;filter-spec&amp;gt;&lt;/code&gt;는 object 전송 범위를 줄이는 partial clone 계열 옵션이고, sparse-checkout은 작업 트리에 펼쳐지는 파일 범위를 줄이는 기능이다.&lt;/p&gt;
&lt;p&gt;그래서 네트워크 전송량과 로컬 object 저장까지 줄이고 싶다면 partial clone을 같이 검토할 수 있고, 일단 저장소는 받아 두되 작업 디렉터리만 가볍게 유지하고 싶다면 sparse-checkout만으로도 목적을 달성할 수 있다. 둘은 겹치지만 층위가 다르다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;reapply&lt;/code&gt;는 언제 필요한가?&lt;/h2&gt;
&lt;p&gt;Git 공식 문서는 merge, rebase, conflict 처리, 또는 다른 명령으로 인해 sparse specification과 working tree 상태가 어긋난 경우 &lt;code&gt;reapply&lt;/code&gt;를 사용할 수 있다고 설명한다. 설정은 남아 있는데 실제 파일 배치가 기대와 달라졌다면 이 명령이 복구 역할을 한다.&lt;/p&gt;
&lt;p&gt;특히 도구가 자동으로 파일을 만들거나 충돌 처리 과정에서 원래 sparse 범위 밖 파일이 나타난 뒤 정리 기준이 애매해졌다면, 먼저 현재 패턴을 확인하고 &lt;code&gt;reapply&lt;/code&gt;로 규칙을 다시 반영하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;sparse index는 꼭 같이 켜야 할까?&lt;/h2&gt;
&lt;p&gt;공식 문서 기준으로 sparse index는 sparse-checkout 사용자에게 더 적은 index 크기와 일부 명령 성능 개선을 제공할 수 있는 기능이다. 다만 모든 외부 도구나 오래된 워크플로가 이를 자연스럽게 다루는지는 별도 확인이 필요하다.&lt;/p&gt;
&lt;p&gt;즉 저장소가 매우 크고 Git 자체 명령 성능이 병목이라면 가치가 있지만, 팀 도구 호환성을 먼저 봐야 한다. 현재 운영 환경이 표준 Git CLI 중심인지, IDE나 스크립트가 index 내부 가정에 민감한지에 따라 판단이 달라질 수 있다.&lt;/p&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. 지정하지 않은 경로의 파일은 완전히 삭제되나?&lt;/h3&gt;
&lt;p&gt;작업 트리에서는 sparse specification 밖 경로가 보이지 않거나 제거될 수 있지만, 저장소 이력 자체가 사라지는 것은 아니다. 핵심은 working tree 노출 범위를 줄이는 것이라는 점이다.&lt;/p&gt;

&lt;h3&gt;Q. 디렉터리 하나만 작업하는 팀에 기본값으로 권할 만한가?&lt;/h3&gt;
&lt;p&gt;그럴 수 있다. 특히 monorepo에서 디렉터리 경계가 분명하고, 팀이 주로 한 하위 트리만 수정한다면 cone mode 기반 sparse-checkout은 운영상 이해하기 쉬운 선택지다.&lt;/p&gt;

&lt;h3&gt;Q. 전체 파일이 다시 필요해지면 어떻게 되돌리나?&lt;/h3&gt;
&lt;p&gt;공식 문서 기준으로 &lt;code&gt;git sparse-checkout disable&lt;/code&gt;을 쓰면 sparse checkout을 끄고 전체 작업 트리로 돌아갈 수 있다. 잠깐 경량 모드로 쓰다가 일반 clone처럼 복원할 수 있다는 점이 장점이다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git sparse-checkout&lt;/code&gt;은 큰 저장소에서 필요한 경로만 작업 트리에 펼치고 싶을 때 가장 직접적인 기능이다. 대부분의 팀에서는 cone mode로 시작하고, 경로 지정은 &lt;code&gt;set&lt;/code&gt;, 점진적 확장은 &lt;code&gt;add&lt;/code&gt;, 상태 복구는 &lt;code&gt;reapply&lt;/code&gt;, 전체 복원은 &lt;code&gt;disable&lt;/code&gt;로 이해하면 된다.&lt;/p&gt;
&lt;p&gt;새 저장소를 받는 시점이라면 &lt;code&gt;git clone --sparse&lt;/code&gt;를 같이 고려할 수 있고, 네트워크 전송량까지 줄여야 한다면 partial clone 옵션을 별도로 검토하면 된다. 판단 기준은 작업 디렉터리를 줄이고 싶은지, object 전송량까지 줄이고 싶은지다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-sparse-checkout&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Documentation: git-sparse-checkout&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-clone&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Documentation: git-clone&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/sparse-checkout&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Documentation: sparse-checkout&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/Git, IDE, 툴 관련</category>
      <category>CLI</category>
      <category>Developer Tools</category>
      <category>GIT</category>
      <category>git sparse-checkout</category>
      <category>monorepo</category>
      <category>version control</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/603</guid>
      <comments>https://jcdgods.tistory.com/603#entry603comment</comments>
      <pubDate>Thu, 4 Jun 2026 12:57:24 +0900</pubDate>
    </item>
    <item>
      <title>Cross-Origin-Opener-Policy 기준 정리</title>
      <link>https://jcdgods.tistory.com/602</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNo2DY/dJMcaf05Qdo/AwcSZ1escbNqNrt5fZqnuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNo2DY/dJMcaf05Qdo/AwcSZ1escbNqNrt5fZqnuK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNo2DY/dJMcaf05Qdo/AwcSZ1escbNqNrt5fZqnuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNo2DY%2FdJMcaf05Qdo%2FAwcSZ1escbNqNrt5fZqnuK%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Cross-Origin-Opener-Policy 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;Cross-Origin-Opener-Policy&lt;/code&gt;(COOP)는 새 탭이나 팝업으로 열린 최상위 문서를 현재 문서와 같은 browsing context group에 둘지, 분리된 그룹으로 보낼지 제어하는 HTTP 응답 헤더다. 2026년 6월 3일 기준 MDN과 WHATWG HTML Standard를 보면 값은 &lt;code&gt;unsafe-none&lt;/code&gt;, &lt;code&gt;same-origin-allow-popups&lt;/code&gt;, &lt;code&gt;same-origin&lt;/code&gt;, &lt;code&gt;noopener-allow-popups&lt;/code&gt; 네 가지이며, 핵심 목적은 &lt;code&gt;window.opener&lt;/code&gt; 관계와 교차 출처 격리 조건을 더 엄격하게 통제하는 데 있다.&lt;/p&gt;
&lt;p&gt;실무에서는 COOP를 CORS처럼 네트워크 읽기 권한을 다루는 헤더로 이해하면 방향이 어긋난다. COOP는 문서 간 참조 관계와 브라우징 컨텍스트 그룹 분리를 다루는 정책이다. 이 글은 어떤 값이 어떤 상황에 맞는지와, SharedArrayBuffer 같은 기능을 쓰려면 왜 COEP를 함께 봐야 하는지까지 공식 문서 기준으로 정리한다.&lt;/p&gt;

&lt;h2&gt;Cross-Origin-Opener-Policy는 무엇을 제어할까?&lt;/h2&gt;
&lt;p&gt;MDN은 COOP를 &lt;code&gt;Window.open()&lt;/code&gt; 또는 새 페이지 내비게이션으로 열린 최상위 문서가 같은 browsing context group(BCG)에 남을지, 새 BCG로 분리될지를 제어하는 헤더로 설명한다. 새 BCG로 분리되면 opener와 opened 문서 사이 참조가 끊어지고, 경우에 따라 프로세스 분리까지 이어질 수 있다.&lt;/p&gt;
&lt;p&gt;이 차이는 단순한 탭 동작 문제가 아니다. MDN은 이런 분리가 XS-Leaks 계열 공격 위험을 줄이는 데 도움이 된다고 설명한다. 따라서 COOP의 핵심 판단 기준은 &quot;다른 문서와 스크립트 참조를 유지해야 하는가&quot;와 &quot;현재 문서를 더 강하게 격리해야 하는가&quot;다.&lt;/p&gt;

&lt;h2&gt;값은 어떻게 다를까?&lt;/h2&gt;
&lt;h3&gt;1. &lt;code&gt;unsafe-none&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;기본값이다. MDN과 WHATWG 문서 모두 별도 COOP를 적용하지 않으면 이 동작을 따른다고 설명한다. 같은 문맥 그룹 공유를 넓게 허용하므로 호환성은 높지만, opener 관계를 통한 격리 강화 효과는 없다.&lt;/p&gt;

&lt;h3&gt;2. &lt;code&gt;same-origin&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;같은 origin이고 같은 정책을 가진 문서끼리만 같은 BCG를 공유한다. MDN은 교차 출처 격리에 필요한 COOP 값으로 &lt;code&gt;same-origin&lt;/code&gt;을 제시한다. SharedArrayBuffer처럼 교차 출처 격리 전제가 필요한 기능을 쓰려면 보통 이 값이 기준점이 된다.&lt;/p&gt;

&lt;h3&gt;3. &lt;code&gt;same-origin-allow-popups&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;기본 동작은 &lt;code&gt;same-origin&lt;/code&gt;에 가깝지만, &lt;code&gt;Window.open()&lt;/code&gt;으로 연 문서가 &lt;code&gt;unsafe-none&lt;/code&gt;이면 같은 BCG에 남게 할 수 있다. MDN은 OAuth나 결제처럼 신뢰하는 교차 출처 팝업과의 연동이 필요한 경우에 이 값을 예로 든다. 즉 강한 격리가 필요하지만, 특정 팝업 참조는 유지해야 할 때 쓰는 완화형 선택지다.&lt;/p&gt;

&lt;h3&gt;4. &lt;code&gt;noopener-allow-popups&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;MDN은 이 값을 opener와 opened 문서의 연결을 더 강하게 끊는 용도로 설명한다. 내비게이션이나 &lt;code&gt;Window.open()&lt;/code&gt; 상황에서 새 BCG를 만들도록 유도하므로, 같은 origin 내부에서도 opener 스크립팅을 줄이고 싶은 분리 시나리오에 맞는다.&lt;/p&gt;

&lt;h2&gt;실무에서는 어떤 값을 먼저 검토할까?&lt;/h2&gt;
&lt;h3&gt;일반 서비스 페이지&lt;/h3&gt;
&lt;p&gt;외부 팝업과의 스크립트 연동이 필요 없고, 페이지를 더 안전하게 격리하고 싶다면 &lt;code&gt;same-origin&lt;/code&gt;이 가장 명확한 출발점이다. 다만 이 값을 넣으면 opener 관계에 의존하던 기존 로그인 흐름이나 도구 연동이 달라질 수 있어 실제 팝업 사용 경로를 먼저 점검해야 한다.&lt;/p&gt;

&lt;h3&gt;OAuth, 결제, 신뢰된 외부 팝업 연동&lt;/h3&gt;
&lt;p&gt;외부 인증 창이나 결제 창을 열고 참조를 유지해야 한다면 &lt;code&gt;same-origin-allow-popups&lt;/code&gt;가 현실적인 절충안이 될 수 있다. MDN이 이 값을 설명할 때도 신뢰된 교차 출처 문서와의 통합 사례를 든다.&lt;/p&gt;

&lt;h3&gt;같은 origin 안에서도 opener 관계를 최대한 끊고 싶을 때&lt;/h3&gt;
&lt;p&gt;관리자 페이지나 민감한 내부 도구처럼 같은 origin의 다른 앱이 열더라도 opener 스크립팅을 최소화하고 싶다면 &lt;code&gt;noopener-allow-popups&lt;/code&gt;를 검토할 수 있다. 다만 MDN은 이것만으로 충분한 보안 경계가 생기는 것은 아니라고 명시한다.&lt;/p&gt;

&lt;h2&gt;교차 출처 격리와는 어떤 관계일까?&lt;/h2&gt;
&lt;p&gt;MDN의 &lt;code&gt;Window.crossOriginIsolated&lt;/code&gt; 문서는 문서가 교차 출처 격리 상태가 되려면 &lt;code&gt;Cross-Origin-Opener-Policy: same-origin&lt;/code&gt;과 &lt;code&gt;Cross-Origin-Embedder-Policy: require-corp&lt;/code&gt; 또는 &lt;code&gt;credentialless&lt;/code&gt;가 함께 필요하다고 설명한다. 또한 &lt;code&gt;Permissions-Policy: cross-origin-isolated&lt;/code&gt;가 이를 막지 않아야 한다.&lt;/p&gt;
&lt;p&gt;WHATWG HTML Standard는 COOP 값 자체로는 &lt;code&gt;same-origin-plus-COEP&lt;/code&gt;를 직접 헤더에 쓰는 것이 아니라, &lt;code&gt;same-origin&lt;/code&gt;과 교차 출처 격리에 호환되는 COEP 조합이 있을 때 내부적으로 그 상태가 성립한다고 설명한다. 따라서 COOP만 단독으로 설정해서는 SharedArrayBuffer 같은 기능 조건이 충족되지 않는다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;window.opener&lt;/code&gt;만 끊고 싶다면 &lt;code&gt;rel=noopener&lt;/code&gt;로 충분할까?&lt;/h2&gt;
&lt;p&gt;일부 링크에는 &lt;code&gt;rel=noopener&lt;/code&gt;만으로도 충분할 수 있다. 하지만 MDN은 COOP가 &lt;code&gt;Window.open()&lt;/code&gt;으로 열린 문서까지 포함해 문서 간 관계를 더 넓게 제어할 수 있다고 설명한다. 즉 개별 링크 수준 제어와 문서 전체 정책 제어는 범위가 다르다.&lt;/p&gt;

&lt;h2&gt;도입 전에 확인할 점&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;팝업 로그인, 결제, 외부 콘솔처럼 &lt;code&gt;window.opener&lt;/code&gt; 또는 창 참조를 유지해야 하는 흐름이 있는지 확인해야 한다.&lt;/li&gt;
  &lt;li&gt;교차 출처 격리가 목적이라면 COOP만이 아니라 COEP와 &lt;code&gt;Permissions-Policy: cross-origin-isolated&lt;/code&gt;까지 함께 점검해야 한다.&lt;/li&gt;
  &lt;li&gt;같은 origin 안의 여러 앱을 분리하려면 COOP 외에도 Fetch Metadata, 쿠키 속성, Service Worker 범위 같은 보완 통제가 필요하다.&lt;/li&gt;
  &lt;li&gt;링크 한두 개만 분리하면 되는지, 문서 전체 opener 정책이 필요한지 구분해야 한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. COOP를 설정하면 CORS 없이도 교차 출처 데이터를 읽을 수 있나?&lt;/h3&gt;
&lt;p&gt;아니다. COOP는 문서 간 브라우징 컨텍스트와 opener 관계를 제어하는 헤더다. 네트워크 응답 읽기 허용은 여전히 CORS 같은 메커니즘이 담당한다.&lt;/p&gt;

&lt;h3&gt;Q. SharedArrayBuffer를 쓰려면 COOP만 넣으면 되나?&lt;/h3&gt;
&lt;p&gt;부족하다. MDN은 &lt;code&gt;Cross-Origin-Opener-Policy: same-origin&lt;/code&gt;과 &lt;code&gt;Cross-Origin-Embedder-Policy: require-corp&lt;/code&gt; 또는 &lt;code&gt;credentialless&lt;/code&gt;가 함께 필요하고, &lt;code&gt;Permissions-Policy: cross-origin-isolated&lt;/code&gt;가 이를 막지 않아야 한다고 설명한다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;noopener-allow-popups&lt;/code&gt;를 쓰면 같은 origin 앱도 완전히 안전하게 분리되나?&lt;/h3&gt;
&lt;p&gt;그렇게 단정하면 과하다. MDN은 이 값만으로 충분한 보안 경계가 생기지는 않으며, 서버 측 요청 통제와 쿠키, Service Worker, 메시징 채널까지 함께 봐야 한다고 설명한다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;COOP는 교차 출처 읽기 권한 헤더가 아니라, 새 문서와 현재 문서가 같은 browsing context group을 공유할지와 opener 관계를 유지할지를 정하는 정책이다. 2026년 6월 3일 기준 공식 문서상 실무 기본값은 단순하다. 강한 격리가 필요하면 &lt;code&gt;same-origin&lt;/code&gt;, 신뢰한 외부 팝업 참조가 필요하면 &lt;code&gt;same-origin-allow-popups&lt;/code&gt;, 같은 origin 내부에서도 opener 관계를 끊고 싶다면 &lt;code&gt;noopener-allow-popups&lt;/code&gt;를 검토한다.&lt;/p&gt;
&lt;p&gt;교차 출처 격리가 목표라면 COOP만 보면 부족하다. COEP와 &lt;code&gt;cross-origin-isolated&lt;/code&gt; 권한까지 함께 확인해야 실제 기능 조건과 운영 호환성을 동시에 맞출 수 있다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Opener-Policy&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN: Cross-Origin-Opener-Policy header&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/crossOriginIsolated&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN: Window.crossOriginIsolated&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Permissions-Policy/cross-origin-isolated&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN: Permissions-Policy: cross-origin-isolated&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://html.spec.whatwg.org/browsers.html#cross-origin-opener-policies&quot; rel=&quot;noopener noreferrer&quot;&gt;WHATWG HTML Standard: Cross-origin opener policies&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/HTML, Javascript, CSS</category>
      <category>COEP</category>
      <category>Coop</category>
      <category>Cross-Origin Isolation</category>
      <category>Cross-Origin-Opener-Policy</category>
      <category>html</category>
      <category>HTTP Header</category>
      <category>javascript</category>
      <category>web security</category>
      <category>Window Opener</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/602</guid>
      <comments>https://jcdgods.tistory.com/602#entry602comment</comments>
      <pubDate>Wed, 3 Jun 2026 10:23:37 +0900</pubDate>
    </item>
    <item>
      <title>Python Path.walk 사용법</title>
      <link>https://jcdgods.tistory.com/601</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcYvrt/dJMcaciYhIP/kv0APPBK5JBv4rMGI4XncK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcYvrt/dJMcaciYhIP/kv0APPBK5JBv4rMGI4XncK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcYvrt/dJMcaciYhIP/kv0APPBK5JBv4rMGI4XncK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcYvrt%2FdJMcaciYhIP%2Fkv0APPBK5JBv4rMGI4XncK%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Python Path.walk 사용법&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;pathlib.Path.walk()&lt;/code&gt;는 디렉터리 트리를 순회할 때 &lt;code&gt;os.walk()&lt;/code&gt; 대신 검토할 수 있는 표준 라이브러리 API다. Python 공식 문서 기준으로 이 메서드는 Python 3.12에서 추가됐고, 현재 Python 3.14 문서에도 같은 동작 모델이 정리돼 있다.&lt;/p&gt;
&lt;p&gt;핵심은 세 가지다. &lt;code&gt;dirpath&lt;/code&gt;는 문자열이 아니라 &lt;code&gt;Path&lt;/code&gt; 객체로 전달되고, &lt;code&gt;top_down=True&lt;/code&gt;일 때는 &lt;code&gt;dirnames&lt;/code&gt;를 직접 수정해 하위 탐색을 줄일 수 있으며, 기본값에서는 심볼릭 링크를 따라가지 않는다. 이 글은 Python 공식 문서와 변경 기록을 바탕으로 실무에서 바로 판단할 기준만 정리한다.&lt;/p&gt;

&lt;h2&gt;Path.walk는 언제 쓰면 될까?&lt;/h2&gt;
&lt;p&gt;다음 조건이면 &lt;code&gt;Path.walk()&lt;/code&gt;를 기본 후보로 두기 좋다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;실행 환경이 Python 3.12 이상이다.&lt;/li&gt;
  &lt;li&gt;이미 코드베이스가 &lt;code&gt;pathlib.Path&lt;/code&gt; 중심으로 작성돼 있다.&lt;/li&gt;
  &lt;li&gt;순회 결과를 다시 &lt;code&gt;Path&lt;/code&gt; 연산으로 이어 붙이고 싶다.&lt;/li&gt;
  &lt;li&gt;특정 하위 디렉터리를 제외하는 prune 처리가 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;반대로 Python 3.11 이하 호환이 필요하면 이 API를 전제로 설계하면 안 된다. 그 경우에는 &lt;code&gt;os.walk()&lt;/code&gt;를 유지하거나 버전 조건 분기가 필요하다.&lt;/p&gt;

&lt;h2&gt;반환값은 어떻게 생겼을까?&lt;/h2&gt;
&lt;p&gt;Python 3.14 &lt;code&gt;pathlib&lt;/code&gt; 문서는 &lt;code&gt;Path.walk(top_down=True, on_error=None, follow_symlinks=False)&lt;/code&gt;가 각 디렉터리마다 &lt;code&gt;(dirpath, dirnames, filenames)&lt;/code&gt; 3튜플을 만든다고 설명한다. 여기서 &lt;code&gt;dirpath&lt;/code&gt;는 현재 디렉터리를 가리키는 &lt;code&gt;Path&lt;/code&gt; 객체이고, &lt;code&gt;dirnames&lt;/code&gt;와 &lt;code&gt;filenames&lt;/code&gt;는 이름 문자열 목록이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pathlib import Path

root = Path(&quot;src&quot;)

for dirpath, dirnames, filenames in root.walk():
    print(dirpath)
    print(dirnames)
    print(filenames)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;파일의 전체 경로가 필요하면 &lt;code&gt;dirpath / name&lt;/code&gt;처럼 다시 조합하면 된다. 이 점이 문자열 경로를 돌려주는 기존 습관보다 읽기 쉬운 경우가 많다.&lt;/p&gt;

&lt;h2&gt;os.walk와 가장 큰 차이는 무엇일까?&lt;/h2&gt;
&lt;p&gt;가장 눈에 띄는 차이는 심볼릭 링크 분류 방식이다. Python 3.12 문서는 &lt;code&gt;os.walk()&lt;/code&gt;가 디렉터리 여부를 분류할 때 심볼릭 링크를 따라가지만, &lt;code&gt;Path.walk()&lt;/code&gt;는 &lt;code&gt;follow_symlinks=False&lt;/code&gt;일 때 모든 심볼릭 링크를 &lt;code&gt;filenames&lt;/code&gt; 쪽으로 분류한다고 설명한다.&lt;/p&gt;
&lt;p&gt;즉 기존 &lt;code&gt;os.walk()&lt;/code&gt; 코드를 그대로 옮기면, 링크 디렉터리가 &lt;code&gt;dirnames&lt;/code&gt;에서 보이지 않아 탐색 결과가 달라질 수 있다. 디렉터리 링크까지 실제로 내려가야 하는 작업이라면 &lt;code&gt;follow_symlinks=True&lt;/code&gt;를 의식적으로 지정해야 한다.&lt;/p&gt;

&lt;h2&gt;하위 디렉터리를 제외하고 싶다면?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;top_down=True&lt;/code&gt;일 때는 호출자가 &lt;code&gt;dirnames&lt;/code&gt; 목록을 직접 수정할 수 있다. Python 문서는 이 방식으로 탐색 가지치기(prune), 방문 순서 제어, 순회 중 생성한 디렉터리 반영이 가능하다고 설명한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pathlib import Path

root = Path(&quot;.&quot;)

for dirpath, dirnames, filenames in root.walk():
    dirnames[:] = [name for name in dirnames if name not in {&quot;.git&quot;, &quot;__pycache__&quot;, &quot;node_modules&quot;}]
    for filename in filenames:
        print(dirpath / filename)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 패턴은 대용량 저장소를 훑을 때 특히 유용하다. 다만 &lt;code&gt;top_down=False&lt;/code&gt;에서는 이미 하위 디렉터리가 생성된 뒤라서 &lt;code&gt;dirnames&lt;/code&gt; 수정이 동작에 영향을 주지 않는다.&lt;/p&gt;

&lt;h2&gt;에러 처리는 어떻게 보는 편이 좋을까?&lt;/h2&gt;
&lt;p&gt;기본값에서는 &lt;code&gt;os.scandir()&lt;/code&gt;가 내는 오류가 무시된다. Python 공식 문서는 &lt;code&gt;on_error&lt;/code&gt;에 호출 가능 객체를 넘기면 &lt;code&gt;OSError&lt;/code&gt;를 받아 계속 진행하거나 다시 예외를 던져 중단할 수 있다고 설명한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pathlib import Path

def handle_error(exc: OSError) -&amp;gt; None:
    print(f&quot;scan failed: {exc.filename}: {exc.strerror}&quot;)

for dirpath, dirnames, filenames in Path(&quot;/var&quot;).walk(on_error=handle_error):
    pass&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;운영 환경에서 권한 문제나 일시적 파일시스템 오류를 추적해야 한다면 기본 무시 동작에만 기대지 않는 편이 낫다. 반대로 best-effort 스캔이면 기본값이 더 단순하다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;follow_symlinks는 언제 켜야 할까?&lt;/h2&gt;
&lt;p&gt;심볼릭 링크가 가리키는 디렉터리까지 실제로 순회 대상에 포함해야 할 때만 켜는 편이 안전하다. Python 3.14 문서는 &lt;code&gt;follow_symlinks=True&lt;/code&gt;가 부모 디렉터리를 다시 가리키는 링크를 만나면 무한 재귀로 이어질 수 있고, &lt;code&gt;Path.walk()&lt;/code&gt;는 이미 방문한 디렉터리를 추적하지 않는다고 경고한다.&lt;/p&gt;
&lt;p&gt;따라서 백업, 코드 인덱싱, 로그 수집처럼 디렉터리 트리 전체를 자동 순회하는 작업에서는 기본값 &lt;code&gt;False&lt;/code&gt;가 더 보수적이다. 링크까지 내려가야 한다면 방문 집합 관리나 최대 깊이 같은 별도 안전장치를 같이 두는 편이 낫다.&lt;/p&gt;

&lt;h2&gt;실무에서는 어떤 패턴이 무난할까?&lt;/h2&gt;
&lt;p&gt;대부분의 프로젝트에서는 아래 기준으로 판단하면 된다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Python 3.12 이상이고 &lt;code&gt;pathlib&lt;/code&gt;를 이미 쓰고 있다면 &lt;code&gt;Path.walk()&lt;/code&gt;를 우선 검토한다.&lt;/li&gt;
  &lt;li&gt;숨기고 싶은 디렉터리가 있으면 &lt;code&gt;top_down=True&lt;/code&gt;와 &lt;code&gt;dirnames&lt;/code&gt; 수정 패턴을 같이 쓴다.&lt;/li&gt;
  &lt;li&gt;권한 오류를 로그로 남겨야 하면 &lt;code&gt;on_error&lt;/code&gt;를 설정한다.&lt;/li&gt;
  &lt;li&gt;심볼릭 링크 순회는 필요한 경우에만 &lt;code&gt;follow_symlinks=True&lt;/code&gt;로 연다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;즉 이 API의 장점은 단순히 &lt;code&gt;os.walk()&lt;/code&gt;의 대체제가 아니라, &lt;code&gt;Path&lt;/code&gt; 객체 기반 코드와 더 자연스럽게 이어진다는 데 있다. 반면 링크 처리와 버전 호환성은 반드시 같이 확인해야 한다.&lt;/p&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Python 3.11 이하에서도 Path.walk를 쓸 수 있을까?&lt;/h3&gt;
&lt;p&gt;표준 라이브러리 &lt;code&gt;Path.walk()&lt;/code&gt;는 Python 3.12에서 추가됐다. 따라서 Python 3.11 이하에서는 같은 이름의 메서드를 전제로 작성할 수 없다.&lt;/p&gt;

&lt;h3&gt;dirnames를 수정하면 정말 하위 탐색이 줄어들까?&lt;/h3&gt;
&lt;p&gt;그렇다. Python 공식 문서는 &lt;code&gt;top_down=True&lt;/code&gt;일 때 호출자가 &lt;code&gt;dirnames&lt;/code&gt;를 제자리에서 수정하면 남아 있는 디렉터리만 재귀 대상으로 사용한다고 설명한다.&lt;/p&gt;

&lt;h3&gt;심볼릭 링크를 따라가면 무엇을 조심해야 할까?&lt;/h3&gt;
&lt;p&gt;부모를 다시 가리키는 링크가 있으면 무한 재귀가 생길 수 있다. Python 공식 문서는 &lt;code&gt;Path.walk()&lt;/code&gt;가 이미 방문한 디렉터리를 기억하지 않는다고 명시한다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.python.org/3/library/pathlib.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Python 3.14 pathlib 문서&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.python.org/3.12/library/pathlib.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Python 3.12 pathlib 문서&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.python.org/3.12/whatsnew/changelog.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Python 3.12 changelog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/C, C++, Java, Python</category>
      <category>os.walk</category>
      <category>Path.walk</category>
      <category>pathlib</category>
      <category>python</category>
      <category>python3.12</category>
      <category>디렉터리순회</category>
      <category>파일시스템</category>
      <category>표준라이브러리</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/601</guid>
      <comments>https://jcdgods.tistory.com/601#entry601comment</comments>
      <pubDate>Tue, 2 Jun 2026 12:16:08 +0900</pubDate>
    </item>
    <item>
      <title>Git worktree 사용법</title>
      <link>https://jcdgods.tistory.com/600</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSCi5j/dJMcajh6Rj9/LW5G4vGswYsjy4yp0kCey1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSCi5j/dJMcajh6Rj9/LW5G4vGswYsjy4yp0kCey1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSCi5j/dJMcajh6Rj9/LW5G4vGswYsjy4yp0kCey1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSCi5j%2FdJMcajh6Rj9%2FLW5G4vGswYsjy4yp0kCey1%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Git worktree 사용법&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;git worktree&lt;/code&gt;는 하나의 Git 저장소에 여러 working tree를 연결해서, 서로 다른 브랜치를 동시에 체크아웃할 수 있게 하는 기능이다. 2026년 6월 1일 기준 Git 공식 문서에서는 main worktree 하나와 0개 이상의 linked worktree를 둘 수 있다고 설명한다.&lt;/p&gt;
&lt;p&gt;실무에서 이 기능이 필요한 이유는 단순하다. 기능 개발 브랜치를 유지한 채 hotfix를 바로 열거나, 리뷰 재현용 작업 디렉터리를 따로 만들거나, 실험용 checkout을 기존 작업과 분리할 수 있기 때문이다. 이 글은 Git 공식 문서를 기준으로 &lt;code&gt;add&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;remove&lt;/code&gt;, &lt;code&gt;lock&lt;/code&gt;, &lt;code&gt;prune&lt;/code&gt;, &lt;code&gt;repair&lt;/code&gt;를 어떻게 판단하면 되는지 정리한다.&lt;/p&gt;

&lt;h2&gt;Git worktree는 정확히 무엇을 만들까?&lt;/h2&gt;
&lt;p&gt;공식 문서는 worktree를 &quot;repository에 연결된 working tree와 그를 구분하기 위한 추가 메타데이터&quot;로 설명한다. 즉 저장소를 통째로 여러 번 복제하는 방식이 아니라, 하나의 저장소 이력을 공유하면서 작업 디렉터리만 추가로 여는 구조에 가깝다.&lt;/p&gt;
&lt;p&gt;Git은 처음 &lt;code&gt;git init&lt;/code&gt;이나 &lt;code&gt;git clone&lt;/code&gt;으로 만들어진 디렉터리를 main worktree로 보고, 이후 &lt;code&gt;git worktree add&lt;/code&gt;로 만든 디렉터리를 linked worktree로 관리한다. linked worktree를 다 썼다면 공식 문서 기준 정식 정리 방법은 &lt;code&gt;git worktree remove&lt;/code&gt;다.&lt;/p&gt;

&lt;h2&gt;언제 clone보다 worktree가 더 직접적일까?&lt;/h2&gt;
&lt;p&gt;이미 같은 저장소를 로컬에 받아 둔 상태에서 브랜치 하나만 더 열어야 한다면 &lt;code&gt;git worktree&lt;/code&gt;가 더 직접적이다. 새 clone은 저장소 메타데이터와 원격 설정을 다시 준비해야 하지만, worktree는 기존 저장소에 연결된 새 작업 디렉터리를 추가하는 방식이라 맥락 전환이 빠르다.&lt;/p&gt;
&lt;p&gt;반대로 서로 완전히 다른 Git 설정, 별도의 object store, 완전히 독립된 인증이나 hook 구성이 필요한 경우에는 별도 clone이 더 분명할 수 있다. 공식 문서도 worktree를 &quot;같은 repository에 연결된 복수의 working tree&quot;로 설명하므로, 독립 저장소가 필요한 상황까지 대체하는 기능으로 보면 과하다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;git worktree add&lt;/code&gt;는 어떻게 동작할까?&lt;/h2&gt;
&lt;p&gt;Git 공식 문서에 따르면 가장 단순한 형태의 &lt;code&gt;git worktree add &amp;lt;path&amp;gt;&lt;/code&gt;는 새 worktree를 만들면서, 경로의 마지막 요소를 이름으로 하는 새 브랜치를 자동 생성해 체크아웃할 수 있다. 예를 들어 &lt;code&gt;../hotfix&lt;/code&gt;를 주면 &lt;code&gt;hotfix&lt;/code&gt; 브랜치를 새로 만들고 그 경로에 연결하는 식이다.&lt;/p&gt;
&lt;p&gt;기존 브랜치를 새 worktree에서 열고 싶다면 &lt;code&gt;git worktree add &amp;lt;path&amp;gt; &amp;lt;branch&amp;gt;&lt;/code&gt; 형태를 쓴다. 문서는 실험이나 테스트처럼 브랜치에 묶지 않을 작업이라면 &lt;code&gt;git worktree add -d &amp;lt;path&amp;gt;&lt;/code&gt;로 detached HEAD worktree를 만드는 예도 제시한다.&lt;/p&gt;

&lt;h2&gt;가장 많이 쓰는 패턴은 무엇일까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;예시 명령&lt;/th&gt;
      &lt;th&gt;해석&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;새 기능 브랜치를 별도 디렉터리에서 시작&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git worktree add ../feature-login&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;경로 이름을 따라 새 브랜치를 만들 가능성이 있다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;기존 브랜치를 따로 열기&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git worktree add ../release release&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;이미 있는 브랜치를 다른 디렉터리에서 동시에 본다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;실험용 임시 checkout&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;git worktree add -d ../scratch&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;브랜치 없이 detached HEAD로 연다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;핵심 판단 기준은 &quot;브랜치 이력을 남길 작업인지&quot;와 &quot;잠깐 확인만 할 실험인지&quot;다. 브랜치로 이어질 작업이면 branch 기반 &lt;code&gt;add&lt;/code&gt;가 맞고, 잠깐 빌드 확인이나 비교만 할 경우에는 detached HEAD가 더 깔끔하다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;git worktree list&lt;/code&gt;는 무엇을 보여줄까?&lt;/h2&gt;
&lt;p&gt;공식 문서에서 &lt;code&gt;list&lt;/code&gt;는 main worktree를 먼저, 그 다음 linked worktree를 나열한다고 설명한다. 출력에는 현재 체크아웃한 revision, 연결된 branch 또는 detached HEAD 여부, 잠금 여부, prune 대상 여부가 포함될 수 있다.&lt;/p&gt;
&lt;p&gt;즉 여러 작업 디렉터리가 생기면 &lt;code&gt;list&lt;/code&gt;가 사실상 인벤토리 역할을 한다. 어떤 경로가 아직 살아 있고, 어느 브랜치가 어느 디렉터리에 연결돼 있는지, 잠금이나 정리 대상이 있는지 확인할 때 가장 먼저 보는 명령이다.&lt;/p&gt;

&lt;h2&gt;정리할 때는 왜 수동 삭제보다 &lt;code&gt;remove&lt;/code&gt;가 낫나?&lt;/h2&gt;
&lt;p&gt;공식 문서는 linked worktree를 다 썼다면 &lt;code&gt;git worktree remove&lt;/code&gt;로 정리하라고 안내한다. 디렉터리만 수동으로 지워 버리면 저장소 내부의 administrative files가 바로 함께 정리되지 않을 수 있기 때문이다.&lt;/p&gt;
&lt;p&gt;문서에 따르면 수동 삭제로 남은 administrative files는 시간이 지나 자동 정리되거나, 사용자가 &lt;code&gt;git worktree prune&lt;/code&gt;를 실행해 치울 수 있다. 그래서 작업 종료 시점에는 &lt;code&gt;remove&lt;/code&gt;, 잔여 메타데이터 청소에는 &lt;code&gt;prune&lt;/code&gt;라는 구분이 가장 단순하다.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;lock&lt;/code&gt;과 &lt;code&gt;prune&lt;/code&gt;는 언제 같이 봐야 할까?&lt;/h2&gt;
&lt;p&gt;Git 공식 문서는 외장 디스크나 네트워크 공유처럼 항상 마운트되지 않는 경로의 worktree라면 &lt;code&gt;git worktree lock&lt;/code&gt;으로 잠글 수 있다고 설명한다. 필요하면 &lt;code&gt;--reason&lt;/code&gt;으로 잠금 이유도 기록할 수 있다.&lt;/p&gt;
&lt;p&gt;이 잠금은 단순 표시가 아니라 pruning 방지와 연결된다. Git 저장소 레이아웃 문서는 &lt;code&gt;worktrees/&amp;lt;id&amp;gt;/locked&lt;/code&gt; 파일이 있으면 해당 linked worktree가 자동 또는 수동 &lt;code&gt;git worktree prune&lt;/code&gt; 대상에서 제외된다고 설명한다. 그래서 휴대용 장치 위 worktree는 &lt;code&gt;lock&lt;/code&gt;을 먼저 고려하는 편이 맞다.&lt;/p&gt;

&lt;h2&gt;이동이 필요한 경우에는 무엇을 주의해야 할까?&lt;/h2&gt;
&lt;p&gt;공식 문서에서 &lt;code&gt;git worktree move&lt;/code&gt;는 worktree 경로를 새 위치로 옮기는 명령이지만, main worktree는 이동할 수 없고 submodule을 포함한 linked worktree도 이 명령으로는 옮길 수 없다고 설명한다.&lt;/p&gt;
&lt;p&gt;대신 문서는 main worktree를 수동으로 옮긴 뒤 연결을 다시 세워야 하는 경우 &lt;code&gt;git worktree repair&lt;/code&gt;를 언급한다. 즉 이동이 필요한 상황이라면 &quot;그냥 파일 시스템에서 경로를 바꾸는가&quot;보다, &quot;Git이 연결 정보를 다시 인식할 수 있는가&quot;를 함께 봐야 한다.&lt;/p&gt;

&lt;h2&gt;worktree별 설정은 어디에 저장될까?&lt;/h2&gt;
&lt;p&gt;Git 공식 문서에서 &lt;code&gt;git config --worktree&lt;/code&gt;는 &lt;code&gt;extensions.worktreeConfig&lt;/code&gt;가 켜져 있을 때 &lt;code&gt;$GIT_DIR/config.worktree&lt;/code&gt;를 읽고 쓰는 방식으로 설명된다. linked worktree에서는 이 &lt;code&gt;$GIT_DIR&lt;/code&gt;가 공용 저장소 루트가 아니라 &lt;code&gt;$GIT_DIR/worktrees/&amp;lt;id&amp;gt;/&lt;/code&gt; 형태가 될 수 있다.&lt;/p&gt;
&lt;p&gt;저장소 레이아웃 문서도 main working directory용 &lt;code&gt;config.worktree&lt;/code&gt;와 linked worktree별 &lt;code&gt;worktrees/&amp;lt;id&amp;gt;/config.worktree&lt;/code&gt;가 존재할 수 있음을 명시한다. 따라서 모든 설정을 공용 &lt;code&gt;.git/config&lt;/code&gt;에 넣는다고 가정하면 worktree별 동작을 오해할 수 있다.&lt;/p&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. 브랜치 없이도 worktree를 만들 수 있나?&lt;/h3&gt;
&lt;p&gt;그럴 수 있다. Git 공식 문서는 실험이나 테스트처럼 기존 브랜치를 건드리지 않을 작업이라면 &lt;code&gt;git worktree add -d &amp;lt;path&amp;gt;&lt;/code&gt;로 detached HEAD worktree를 만드는 예를 제시한다.&lt;/p&gt;

&lt;h3&gt;Q. worktree 디렉터리를 Finder나 파일 탐색기에서 그냥 지워도 되나?&lt;/h3&gt;
&lt;p&gt;가능은 하지만 권장되는 정리 방식은 아니다. Git 공식 문서는 수동 삭제 후 남은 administrative files가 나중에 자동 정리되거나 &lt;code&gt;prune&lt;/code&gt;로 치워질 수 있다고 설명하므로, 처음부터 &lt;code&gt;git worktree remove&lt;/code&gt;를 쓰는 편이 더 명확하다.&lt;/p&gt;

&lt;h3&gt;Q. 외장 SSD에 둔 worktree가 가끔 사라진 것처럼 보이면 어떻게 봐야 하나?&lt;/h3&gt;
&lt;p&gt;공식 문서 기준으로 그런 경로는 &lt;code&gt;git worktree lock&lt;/code&gt; 대상이다. 잠금을 걸면 pruning으로 메타데이터가 지워지는 일을 줄일 수 있고, 이유를 함께 남겨 두면 나중에 정리 판단도 쉬워진다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git worktree&lt;/code&gt;의 핵심은 같은 저장소를 유지한 채 브랜치별 작업 디렉터리를 분리하는 데 있다. 현재 Git 공식 문서 기준으로 새 작업 시작은 &lt;code&gt;add&lt;/code&gt;, 현황 확인은 &lt;code&gt;list&lt;/code&gt;, 정상 정리는 &lt;code&gt;remove&lt;/code&gt;, 휴대용 경로 보호는 &lt;code&gt;lock&lt;/code&gt;, 잔여 메타데이터 청소는 &lt;code&gt;prune&lt;/code&gt;, 이동 후 연결 복구는 &lt;code&gt;repair&lt;/code&gt;로 이해하면 된다.&lt;/p&gt;
&lt;p&gt;대부분의 개발 환경에서는 기능 브랜치 하나당 worktree 하나라는 규칙만 잡아도 운영이 단순해진다. 반대로 임시 실험이나 재현 작업은 detached HEAD worktree로 분리하면 현재 작업 디렉터리를 건드리지 않고 확인 범위를 좁힐 수 있다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-worktree&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Documentation: git-worktree&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-config&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Documentation: git-config&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/gitrepository-layout&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Documentation: gitrepository-layout&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/Git, IDE, 툴 관련</category>
      <category>branch</category>
      <category>CLI</category>
      <category>Developer Tools</category>
      <category>GIT</category>
      <category>git worktree</category>
      <category>version control</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/600</guid>
      <comments>https://jcdgods.tistory.com/600#entry600comment</comments>
      <pubDate>Mon, 1 Jun 2026 11:12:09 +0900</pubDate>
    </item>
    <item>
      <title>PostgreSQL sslmode 기준 정리</title>
      <link>https://jcdgods.tistory.com/599</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpuStL/dJMcajoNDse/3Dg7NNgZx6eioH48BVQpv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpuStL/dJMcajoNDse/3Dg7NNgZx6eioH48BVQpv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpuStL/dJMcajoNDse/3Dg7NNgZx6eioH48BVQpv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpuStL%2FdJMcajoNDse%2F3Dg7NNgZx6eioH48BVQpv1%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;PostgreSQL sslmode 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;sslmode&lt;/code&gt;는 PostgreSQL 클라이언트가 서버와 연결할 때 TLS를 아예 쓰지 않을지, 우선 시도만 할지, 인증서까지 검증할지를 정하는 핵심 옵션이다. 현재 PostgreSQL 18 공식 문서 기준으로 이 값은 여섯 가지 모드를 가지며, 기본값은 &lt;code&gt;prefer&lt;/code&gt;다.&lt;/p&gt;
&lt;p&gt;실무에서 중요한 기준은 세 가지다. 평문 연결을 허용해도 되는지, 서버 인증서를 검증해야 하는지, 그리고 접속 대상이 TCP/IP인지 유닉스 도메인 소켓인지다. 이 글은 PostgreSQL 공식 문서를 바탕으로 각 모드의 의미와 안전한 기본값을 객관적으로 정리한다.&lt;/p&gt;

&lt;h2&gt;sslmode는 정확히 무엇을 제어할까?&lt;/h2&gt;
&lt;p&gt;공식 문서에서 &lt;code&gt;sslmode&lt;/code&gt;는 클라이언트가 서버와 secure SSL TCP/IP connection을 어떤 우선순위로 협상할지 결정하는 옵션으로 설명된다. 즉 서버 설정인 &lt;code&gt;ssl = on&lt;/code&gt;과는 역할이 다르며, 클라이언트 쪽에서 연결 정책을 정하는 값이다.&lt;/p&gt;
&lt;p&gt;이 옵션은 연결 문자열이나 환경 변수로 지정할 수 있고, &lt;code&gt;psql&lt;/code&gt;, libpq 기반 애플리케이션, 여러 PostgreSQL 드라이버의 하위 설정에도 자주 연결된다. 다만 공식 문서 기준으로 &lt;code&gt;sslmode&lt;/code&gt;는 TCP/IP 연결에만 의미가 있고, 유닉스 도메인 소켓 통신에서는 무시된다.&lt;/p&gt;

&lt;h2&gt;여섯 가지 모드는 어떻게 다를까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;모드&lt;/th&gt;
      &lt;th&gt;동작&lt;/th&gt;
      &lt;th&gt;실무 해석&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;disable&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;SSL 없이만 연결 시도&lt;/td&gt;
      &lt;td&gt;TLS를 전혀 쓰지 않는다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;allow&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;평문 먼저, 실패하면 SSL 시도&lt;/td&gt;
      &lt;td&gt;보안보다 호환성 우선이다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;prefer&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;SSL 먼저, 실패하면 평문 시도&lt;/td&gt;
      &lt;td&gt;기본값이지만 평문 fallback이 남아 있다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;require&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;SSL로만 연결 시도&lt;/td&gt;
      &lt;td&gt;암호화는 강제하지만 호스트명 검증은 아니다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;verify-ca&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;SSL 강제 + 신뢰된 CA 확인&lt;/td&gt;
      &lt;td&gt;인증서 체인은 보지만 대상 호스트명은 따로 보지 않는다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;verify-full&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;SSL 강제 + CA 확인 + 호스트명 일치 확인&lt;/td&gt;
      &lt;td&gt;가장 엄격한 검증이다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;공식 문서는 &lt;code&gt;require&lt;/code&gt;에서 root CA 파일이 있으면 &lt;code&gt;verify-ca&lt;/code&gt;처럼 인증서 검증이 함께 일어날 수 있다고 설명한다. 반대로 root CA 파일이 없으면 &lt;code&gt;require&lt;/code&gt;는 &quot;암호화는 하지만 서버 신원은 엄격히 검증하지 않는 상태&quot;로 이해하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;기본값 prefer를 그대로 써도 될까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;prefer&lt;/code&gt;는 현재 libpq의 기본값이지만, 서버가 SSL을 받지 않으면 평문으로도 연결될 수 있다. 그래서 네트워크 경로에서 반드시 암호화를 강제해야 하는 운영 환경이라면 기본값 그대로 두는 판단은 보수적이지 않다.&lt;/p&gt;
&lt;p&gt;대부분의 운영 서비스에서는 다음처럼 나누면 단순하다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;로컬 유닉스 소켓 접속: &lt;code&gt;sslmode&lt;/code&gt;보다 소켓 사용 자체가 더 중요한 기준이다.&lt;/li&gt;
  &lt;li&gt;같은 VPC나 내부망이라도 TLS 강제가 필요함: 최소 &lt;code&gt;require&lt;/code&gt; 이상을 본다.&lt;/li&gt;
  &lt;li&gt;서버 신원 검증까지 필요함: &lt;code&gt;verify-full&lt;/code&gt;이 기본 후보다.&lt;/li&gt;
  &lt;li&gt;개발 편의 때문에 평문 fallback을 허용해야 함: 그때만 &lt;code&gt;prefer&lt;/code&gt; 또는 &lt;code&gt;allow&lt;/code&gt;를 검토한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;왜 verify-full이 자주 권장될까?&lt;/h2&gt;
&lt;p&gt;PostgreSQL 18의 SSL 지원 문서는 보안 민감한 대부분의 환경에서 &lt;code&gt;verify-full&lt;/code&gt;을 권장한다고 설명한다. 이유는 단순하다. TLS 연결만으로는 &quot;암호화됐다&quot;는 사실만 보장할 뿐, 지금 붙은 서버가 원래 의도한 그 서버인지까지는 자동으로 보장하지 않기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;verify-full&lt;/code&gt;은 서버 인증서가 신뢰된 CA로부터 발급됐는지 확인하고, 요청한 호스트명이 인증서의 SAN 또는 필요한 경우 Common Name과 일치하는지도 본다. DNS 오염, 잘못된 라우팅, 다른 서버 인증서 오배치 같은 문제를 막으려면 이 단계가 빠지면 안 된다.&lt;/p&gt;

&lt;h2&gt;verify-ca와 verify-full 차이는 어디서 생길까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;verify-ca&lt;/code&gt;는 인증서 체인이 신뢰된 CA로 이어지는지만 확인한다. 반면 &lt;code&gt;verify-full&lt;/code&gt;은 거기에 더해 &quot;내가 접속하려는 호스트 이름과 인증서 이름이 맞는가&quot;까지 검사한다.&lt;/p&gt;
&lt;p&gt;즉 내부 CA를 쓰고 있다고 해도, 같은 CA가 여러 서버 인증서를 발급할 수 있는 환경이라면 &lt;code&gt;verify-ca&lt;/code&gt;만으로는 오접속을 완전히 막지 못한다. 운영 환경에서 접속 대상을 명확히 구분해야 한다면 &lt;code&gt;verify-full&lt;/code&gt; 쪽이 더 직접적인 기본값이다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;유닉스 도메인 소켓에서는 왜 sslmode가 무시될까?&lt;/h2&gt;
&lt;p&gt;공식 문서는 &lt;code&gt;sslmode&lt;/code&gt;가 유닉스 도메인 소켓 통신에서는 ignored 된다고 명시한다. 이 경로는 네트워크 TCP 연결이 아니라 로컬 시스템 내부 IPC이기 때문에, TCP 위에서 협상하는 SSL/TLS 자체가 적용 대상이 아니다.&lt;/p&gt;
&lt;p&gt;따라서 로컬 호스트 접속 문제를 점검할 때는 &lt;code&gt;sslmode&lt;/code&gt;보다 실제로 소켓으로 붙는지, 혹은 &lt;code&gt;host&lt;/code&gt;를 명시해 TCP/IP로 붙는지부터 구분해야 한다. 같은 &lt;code&gt;localhost&lt;/code&gt; 접속이라도 드라이버와 파라미터에 따라 경로가 달라질 수 있다.&lt;/p&gt;

&lt;h2&gt;require만 써도 충분한 경우가 있을까?&lt;/h2&gt;
&lt;p&gt;있을 수는 있다. 예를 들어 인증서 검증 체계를 아직 배포하지 못했지만 평문 연결만은 막아야 하는 전환 단계라면 &lt;code&gt;require&lt;/code&gt;가 현실적인 중간값이 될 수 있다. 다만 이 경우에도 &quot;암호화 강제&quot;와 &quot;서버 신원 검증&quot;을 같은 수준으로 보면 안 된다.&lt;/p&gt;
&lt;p&gt;공식 문서가 &lt;code&gt;verify-full&lt;/code&gt;을 별도로 강조하는 이유도 여기에 있다. 운영 환경에서 접속 대상 위조 가능성을 줄이려면 최종 목적지는 보통 &lt;code&gt;require&lt;/code&gt;가 아니라 &lt;code&gt;verify-full&lt;/code&gt;이다.&lt;/p&gt;

&lt;h2&gt;최근 문서 기준으로 같이 봐야 할 옵션은 무엇일까?&lt;/h2&gt;
&lt;p&gt;PostgreSQL 18 연결 문서는 &lt;code&gt;sslrootcert=system&lt;/code&gt;을 사용할 때 기본 &lt;code&gt;sslmode&lt;/code&gt;가 &lt;code&gt;verify-full&lt;/code&gt;로 바뀌고, 그보다 약한 설정은 오류가 난다고 설명한다. 시스템 신뢰 저장소를 쓰는 경우에는 약한 검증 모드가 의미를 잃기 쉽다는 점을 문서가 분명히 드러낸 셈이다.&lt;/p&gt;
&lt;p&gt;또한 문서는 GSSAPI 암호화가 가능한 환경에서는 &lt;code&gt;sslmode&lt;/code&gt; 값과 무관하게 GSSAPI 암호화가 우선될 수 있다고 설명한다. Kerberos 기반 환경에서 반드시 SSL을 강제하려면 &lt;code&gt;gssencmode=disable&lt;/code&gt;도 함께 봐야 한다.&lt;/p&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. 내부망이면 &lt;code&gt;prefer&lt;/code&gt;로도 충분할까?&lt;/h3&gt;
&lt;p&gt;상황에 따라 다르다. 내부망 자체가 신뢰 경계라고 보더라도, 공식 문서 기준으로 &lt;code&gt;prefer&lt;/code&gt;는 SSL 실패 시 평문 fallback을 허용한다. 내부망에서도 암호화 강제가 필요하면 &lt;code&gt;require&lt;/code&gt; 이상이 더 명확하다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;verify-full&lt;/code&gt;을 쓰려면 무엇이 먼저 준비돼야 할까?&lt;/h3&gt;
&lt;p&gt;클라이언트가 신뢰할 CA 체인과, 접속에 사용하는 호스트명이 서버 인증서의 SAN 또는 필요한 경우 Common Name과 일치해야 한다. 즉 인증서 배포만이 아니라 접속 문자열의 호스트명 관리도 같이 맞아야 한다.&lt;/p&gt;

&lt;h3&gt;Q. IP 주소로 접속해도 &lt;code&gt;verify-full&lt;/code&gt;이 가능할까?&lt;/h3&gt;
&lt;p&gt;가능하다. 공식 문서는 IP로 접속하는 경우 인증서의 &lt;code&gt;iPAddress&lt;/code&gt; 또는 경우에 따라 &lt;code&gt;dNSName&lt;/code&gt; SAN과 비교한다고 설명한다. 다만 실무에서는 IP 변경과 인증서 재발급 관리까지 함께 고려해야 한다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;sslmode&lt;/code&gt;를 고를 때 가장 중요한 기준은 &quot;평문 fallback을 허용할지&quot;와 &quot;서버 신원 검증까지 할지&quot;다. 현재 PostgreSQL 공식 문서 기준으로 기본값 &lt;code&gt;prefer&lt;/code&gt;는 호환성에는 유리하지만, 운영 보안 기본값으로는 다소 느슨할 수 있다.&lt;/p&gt;
&lt;p&gt;대부분의 운영 환경에서는 &lt;code&gt;verify-full&lt;/code&gt;이 가장 설명 가능한 선택이고, 전환 단계에서는 &lt;code&gt;require&lt;/code&gt;를 임시값으로 둘 수 있다. 반대로 로컬 유닉스 소켓 접속이라면 &lt;code&gt;sslmode&lt;/code&gt;가 아니라 실제 연결 경로를 먼저 보는 편이 맞다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/libpq-connect.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;PostgreSQL Documentation: libpq connection parameters&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/libpq-ssl.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;PostgreSQL Documentation: libpq SSL support&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;PostgreSQL 18 current documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/서버, DBMS</category>
      <category>Database Security</category>
      <category>DBMS</category>
      <category>libpq</category>
      <category>PostgreSQL</category>
      <category>ssl</category>
      <category>sslmode</category>
      <category>TLS</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/599</guid>
      <comments>https://jcdgods.tistory.com/599#entry599comment</comments>
      <pubDate>Sun, 31 May 2026 11:32:57 +0900</pubDate>
    </item>
    <item>
      <title>Cross-Origin-Resource-Policy 기준 정리</title>
      <link>https://jcdgods.tistory.com/598</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cl5shI/dJMcada3LTh/c98T5BP9PdaQS9B1fAa0y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cl5shI/dJMcada3LTh/c98T5BP9PdaQS9B1fAa0y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cl5shI/dJMcada3LTh/c98T5BP9PdaQS9B1fAa0y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcl5shI%2FdJMcada3LTh%2Fc98T5BP9PdaQS9B1fAa0y1%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Cross-Origin-Resource-Policy 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;Cross-Origin-Resource-Policy&lt;/code&gt;(CORP)는 브라우저가 &lt;code&gt;no-cors&lt;/code&gt; 방식으로 가져오는 교차 출처 리소스에 대해, 응답 소유자가 허용 범위를 직접 선언하게 하는 HTTP 응답 헤더다. 2026년 5월 30일 기준 MDN과 Fetch Standard를 보면 값은 &lt;code&gt;same-origin&lt;/code&gt;, &lt;code&gt;same-site&lt;/code&gt;, &lt;code&gt;cross-origin&lt;/code&gt; 세 가지이며, 주로 이미지, 스크립트, 스타일시트 같은 임베드 리소스의 노출 범위를 좁히는 데 사용한다.&lt;/p&gt;
&lt;p&gt;실무에서는 CORP를 CORS의 대체재로 보면 판단이 어긋나기 쉽다. CORP는 읽기 권한을 부여하는 헤더가 아니라, 원치 않는 &lt;code&gt;no-cors&lt;/code&gt; 임베딩과 응답 노출을 제한하는 쪽에 가깝다. 이 글은 공식 문서 기준으로 CORP가 정확히 무엇을 막는지와 어떤 값이 무난한 선택인지 정리한다.&lt;/p&gt;

&lt;h2&gt;Cross-Origin-Resource-Policy는 무엇을 할까?&lt;/h2&gt;
&lt;p&gt;MDN은 CORP를 브라우저가 교차 출처 또는 교차 사이트의 &lt;code&gt;no-cors&lt;/code&gt; 요청을 차단하도록 지시하는 응답 헤더로 설명한다. 여기서 핵심은 &quot;요청 자체를 전송하지 않게 만드는 것&quot;이 아니라 &quot;브라우저가 해당 응답을 다른 출처 문서에 노출하지 않도록 제한하는 것&quot;이다.&lt;/p&gt;
&lt;p&gt;MDN의 구현 가이드는 실제 요청은 전송될 수 있지만, 브라우저가 응답 본문을 제거해 데이터 누출을 막는 방향으로 동작한다고 설명한다. 따라서 CORP는 트래픽 차단 장치라기보다 브라우저 측 노출 통제 장치로 이해하는 편이 정확하다.&lt;/p&gt;

&lt;h2&gt;CORS와는 무엇이 다를까?&lt;/h2&gt;
&lt;p&gt;CORS는 &lt;code&gt;fetch()&lt;/code&gt;나 &lt;code&gt;XMLHttpRequest&lt;/code&gt; 같은 교차 출처 읽기를 명시적으로 허용하는 메커니즘이다. 반면 CORP는 주로 &lt;code&gt;img&lt;/code&gt;, &lt;code&gt;script&lt;/code&gt;, &lt;code&gt;link&lt;/code&gt;처럼 기본적으로 &lt;code&gt;no-cors&lt;/code&gt;로 로드될 수 있는 리소스에 대해, 응답 제공자가 어디까지 임베딩을 허용할지 제한하는 데 초점이 있다.&lt;/p&gt;
&lt;p&gt;즉 외부 프런트엔드가 API 응답 본문을 읽게 하려면 여전히 CORS가 필요하다. 반대로 사용자별 이미지, 내부 JSON, 사설 리소스가 다른 사이트에 임베드되는 범위를 줄이고 싶다면 CORP가 더 직접적인 수단이다.&lt;/p&gt;

&lt;h2&gt;값은 어떻게 고를까?&lt;/h2&gt;
&lt;h3&gt;1. &lt;code&gt;same-origin&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;MDN은 이 값을 민감한 사용자 정보나 비공개 API 응답에 권장한다. 요청을 보낸 문서의 origin이 응답 URL의 origin과 같을 때만 허용하므로 가장 보수적인 선택이다.&lt;/p&gt;

&lt;h3&gt;2. &lt;code&gt;same-site&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;MDN은 여러 서브도메인이 기능을 나눠 쓰는 환경, 예를 들어 같은 회사의 CDN이나 SSO 애플리케이션처럼 같은 사이트 범위에서 공유해야 하는 리소스에 이 값을 권장한다. 다만 Fetch Standard는 단순히 등록 가능 도메인이 같다고 항상 허용되는 것은 아니라고 설명한다. 예를 들어 보안 전송으로 제공된 응답이 비보안 요청 문서와 만나는 경우는 &lt;code&gt;same-site&lt;/code&gt;로 보지 않는다.&lt;/p&gt;

&lt;h3&gt;3. &lt;code&gt;cross-origin&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;이 값은 어떤 origin에서도 리소스를 로드할 수 있게 한다. MDN 구현 가이드는 공개 CDN이나 외부 위젯처럼 넓게 배포해야 하는 리소스에만 이 값을 권장하며, 헤더를 아예 보내지 않을 때의 기본 동작도 사실상 이 방향이라고 설명한다.&lt;/p&gt;

&lt;h2&gt;언제 적용 효과가 분명할까?&lt;/h2&gt;
&lt;h3&gt;민감한 파일 응답을 같은 브라우저에만 묶고 싶을 때&lt;/h3&gt;
&lt;p&gt;사용자별 프로필 이미지, 사내 전용 정적 리소스, 인증 이후에만 보여야 하는 다운로드 응답처럼 제3자 사이트에서 임베드될 이유가 없는 경우에는 &lt;code&gt;same-origin&lt;/code&gt;이 기본 후보가 된다.&lt;/p&gt;

&lt;h3&gt;여러 서브도메인이 하나의 서비스처럼 동작할 때&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;static.example.com&lt;/code&gt;과 &lt;code&gt;app.example.com&lt;/code&gt;처럼 같은 사이트 내부에서만 자산을 공유한다면 &lt;code&gt;same-site&lt;/code&gt;가 현실적인 절충안이 될 수 있다. 이 경우 CORS처럼 허용 origin 목록을 세세하게 열기보다, 브라우저에 같은 사이트 범위까지만 허용하라고 선언하는 셈이다.&lt;/p&gt;

&lt;h3&gt;공개 배포 자산을 운영할 때&lt;/h3&gt;
&lt;p&gt;여러 외부 사이트가 공용으로 쓰는 라이브러리, 폰트, 위젯이라면 &lt;code&gt;cross-origin&lt;/code&gt;이 필요할 수 있다. 다만 이 값은 보호 범위를 사실상 열어 두는 선택이므로, 정말 공개 배포가 목적일 때만 쓰는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;COEP와는 어떤 관계일까?&lt;/h2&gt;
&lt;p&gt;MDN의 &lt;code&gt;Cross-Origin-Embedder-Policy&lt;/code&gt;(COEP) 문서는, 문서가 &lt;code&gt;require-corp&lt;/code&gt;를 사용하면 교차 출처의 &lt;code&gt;no-cors&lt;/code&gt; 리소스가 적절한 CORP 헤더를 보내거나 CORS로 허용되지 않는 한 로드되지 않는다고 설명한다. 즉 COEP는 문서 쪽의 요구사항이고, CORP는 리소스 쪽의 허용 선언이다.&lt;/p&gt;
&lt;p&gt;따라서 SharedArrayBuffer 같은 교차 출처 격리가 필요한 기능을 준비하는 과정에서는 COEP만 켜서는 부족할 수 있다. 함께 불러오는 이미지, 스크립트, 워커, 폰트가 어떤 CORP 또는 CORS 정책을 보내는지까지 같이 확인해야 한다.&lt;/p&gt;

&lt;h2&gt;실무 점검 기준&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;외부 사이트가 실제로 임베드해야 하는 리소스인지 먼저 구분해야 한다.&lt;/li&gt;
  &lt;li&gt;민감하거나 로그인 문맥에 묶인 응답은 &lt;code&gt;same-origin&lt;/code&gt;부터 검토하는 편이 안전하다.&lt;/li&gt;
  &lt;li&gt;서브도메인 간 공유가 필요할 때만 &lt;code&gt;same-site&lt;/code&gt;를 검토하고, HTTP와 HTTPS가 섞여 있지 않은지도 봐야 한다.&lt;/li&gt;
  &lt;li&gt;공개 CDN, 위젯, 범용 자산이 아니라면 &lt;code&gt;cross-origin&lt;/code&gt;은 기본값으로 두지 않는 편이 낫다.&lt;/li&gt;
  &lt;li&gt;API 본문 읽기 허용이 목적이라면 CORP가 아니라 CORS 설정을 검토해야 한다.&lt;/li&gt;
  &lt;li&gt;COEP를 도입하는 페이지라면 서드파티 자산의 CORP 또는 CORS 지원 여부를 함께 확인해야 한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. CORP를 설정하면 교차 출처 요청이 아예 나가지 않나?&lt;/h3&gt;
&lt;p&gt;그렇게 이해하면 부정확하다. MDN 구현 가이드는 CORP가 실제 요청 자체를 막는 것이 아니라, 브라우저가 응답 본문을 노출하지 않도록 제한한다고 설명한다.&lt;/p&gt;

&lt;h3&gt;Q. API 서버에도 CORP만 넣으면 되나?&lt;/h3&gt;
&lt;p&gt;교차 출처 프런트엔드가 응답 본문을 읽어야 한다면 CORP만으로는 충분하지 않다. 그 경우에는 CORS 응답 헤더가 필요하다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;same-site&lt;/code&gt;는 서브도메인이 같으면 항상 안전한가?&lt;/h3&gt;
&lt;p&gt;항상 그렇지는 않다. Fetch Standard는 &lt;code&gt;same-site&lt;/code&gt; 판정에서 보안 전송 여부도 고려하며, HTTPS 응답이 HTTP 문서와 단순히 같은 registrable domain이라는 이유만으로 허용되는 구조가 아니다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;CORP는 교차 출처 읽기 허용을 위한 헤더가 아니라, &lt;code&gt;no-cors&lt;/code&gt; 리소스의 노출 범위를 리소스 소유자가 제한하는 헤더다. 2026년 5월 30일 기준 공식 문서상 기본 판단은 단순하다. 외부 임베드가 필요 없으면 &lt;code&gt;same-origin&lt;/code&gt;, 같은 사이트 내부 공유만 필요하면 &lt;code&gt;same-site&lt;/code&gt;, 공개 배포 자산일 때만 &lt;code&gt;cross-origin&lt;/code&gt;을 검토한다.&lt;/p&gt;
&lt;p&gt;특히 COEP를 함께 쓰는 환경에서는 개별 리소스가 CORP 또는 CORS 요구를 충족하는지까지 연결해서 봐야 운영 중 차단을 줄일 수 있다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Resource-Policy&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN: Cross-Origin-Resource-Policy header&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/CORP&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN: Cross-Origin Resource Policy (CORP) implementation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Embedder-Policy&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN: Cross-Origin-Embedder-Policy header&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://fetch.spec.whatwg.org/#cross-origin-resource-policy-header&quot; rel=&quot;noopener noreferrer&quot;&gt;WHATWG: Fetch Standard&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/HTML, Javascript, CSS</category>
      <category>COEP</category>
      <category>corp</category>
      <category>CORS</category>
      <category>Cross-Origin-Resource-Policy</category>
      <category>html</category>
      <category>HTTP Header</category>
      <category>javascript</category>
      <category>web security</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/598</guid>
      <comments>https://jcdgods.tistory.com/598#entry598comment</comments>
      <pubDate>Sat, 30 May 2026 11:45:04 +0900</pubDate>
    </item>
    <item>
      <title>Python tomllib 사용법</title>
      <link>https://jcdgods.tistory.com/597</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n7O5I/dJMcaaS0LqQ/Hq3KhkGqAHtumKneVE270k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n7O5I/dJMcaaS0LqQ/Hq3KhkGqAHtumKneVE270k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n7O5I/dJMcaaS0LqQ/Hq3KhkGqAHtumKneVE270k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn7O5I%2FdJMcaaS0LqQ%2FHq3KhkGqAHtumKneVE270k%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;Python tomllib 사용법&lt;/h1&gt;
&lt;p&gt;Python에서 &lt;code&gt;pyproject.toml&lt;/code&gt;이나 별도 설정 파일을 읽어야 할 때는 표준 라이브러리 &lt;code&gt;tomllib&lt;/code&gt;가 가장 먼저 검토할 도구다. &lt;code&gt;tomllib&lt;/code&gt;는 Python 3.11부터 추가됐고, 현재 Python 3.14 문서도 이 모듈을 TOML 파서로 설명한다.&lt;/p&gt;
&lt;p&gt;핵심은 세 가지다. &lt;code&gt;tomllib&lt;/code&gt;는 읽기 전용이고, 파일 입력은 바이너리 모드가 기준이며, 부동소수점 변환은 &lt;code&gt;parse_float&lt;/code&gt;로 바꿀 수 있다. 이 글은 Python 공식 문서, PEP 680, PyPA의 &lt;code&gt;pyproject.toml&lt;/code&gt; 설명을 바탕으로 실무에서 바로 판단할 기준만 정리한다.&lt;/p&gt;

&lt;h2&gt;Python tomllib는 무엇을 위한 모듈일까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;tomllib&lt;/code&gt;는 TOML 문서를 Python 객체로 파싱하는 표준 라이브러리다. Python 3.14 문서는 변환 결과가 딕셔너리, 리스트, 문자열, 정수, 날짜/시간 객체 등으로 매핑된다고 설명한다.&lt;/p&gt;
&lt;p&gt;도입 배경은 PEP 680에 정리돼 있다. Python 생태계에서 &lt;code&gt;pyproject.toml&lt;/code&gt; 사용이 늘어나면서, 별도 의존성 없이 TOML을 읽을 수 있는 표준 도구가 필요해졌고 그 결과가 &lt;code&gt;tomllib&lt;/code&gt;다.&lt;/p&gt;

&lt;h2&gt;언제 tomllib를 쓰면 될까?&lt;/h2&gt;
&lt;p&gt;다음 조건이면 &lt;code&gt;tomllib&lt;/code&gt;가 기본값으로 무난하다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;실행 환경이 Python 3.11 이상이다.&lt;/li&gt;
  &lt;li&gt;TOML 파일을 읽기만 하면 된다.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;pyproject.toml&lt;/code&gt;처럼 표준 형식의 설정 파일을 안전하게 파싱하고 싶다.&lt;/li&gt;
  &lt;li&gt;서드파티 TOML 파서를 추가 설치하지 않고 싶다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;반대로 TOML을 다시 저장하거나 수정 결과를 파일에 써야 하면 &lt;code&gt;tomllib&lt;/code&gt;만으로는 부족하다. Python 공식 문서는 쓰기 작업용 모듈을 제공하지 않으며, 이 경우에는 별도 라이브러리를 검토해야 한다.&lt;/p&gt;

&lt;h2&gt;기본 사용법은 어떻게 될까?&lt;/h2&gt;
&lt;p&gt;가장 기본 형태는 파일을 바이너리 모드로 열고 &lt;code&gt;tomllib.load()&lt;/code&gt;를 호출하는 방식이다. 문서는 &lt;code&gt;fp&lt;/code&gt;가 읽기 가능한 바이너리 파일 객체여야 한다고 적고 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import tomllib

with open(&quot;pyproject.toml&quot;, &quot;rb&quot;) as f:
    data = tomllib.load(f)

project_name = data[&quot;project&quot;][&quot;name&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;문자열이 이미 메모리에 있다면 &lt;code&gt;tomllib.loads()&lt;/code&gt;를 쓸 수 있다. 이 함수는 TOML 문서 문자열을 직접 받아 같은 형태의 Python 객체를 돌려준다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import tomllib

text = &quot;&quot;&quot;
[project]
name = &quot;sample-app&quot;
version = &quot;1.0.0&quot;
&quot;&quot;&quot;

data = tomllib.loads(text)
print(data[&quot;project&quot;][&quot;version&quot;])&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;왜 파일을 텍스트 모드가 아니라 바이너리 모드로 열어야 할까?&lt;/h2&gt;
&lt;p&gt;공식 문서는 &lt;code&gt;tomllib.load()&lt;/code&gt;의 입력을 바이너리 파일 객체로 정의한다. 이는 TOML 명세가 UTF-8 인코딩을 기준으로 하기 때문이며, 파일 읽기 단계에서 플랫폼 기본 인코딩이나 줄바꿈 처리 차이를 끼워 넣지 않는 쪽이 더 명확하다.&lt;/p&gt;
&lt;p&gt;실무에서는 &lt;code&gt;open(path, &quot;rb&quot;)&lt;/code&gt;를 기본 패턴으로 고정하는 편이 안전하다. 반대로 이미 문자열로 읽어 둔 데이터라면 그때는 &lt;code&gt;loads()&lt;/code&gt;를 사용하면 된다.&lt;/p&gt;

&lt;h2&gt;parse_float는 언제 필요할까?&lt;/h2&gt;
&lt;p&gt;TOML 안의 부동소수점 값을 기본 &lt;code&gt;float&lt;/code&gt; 대신 다른 타입으로 받고 싶다면 &lt;code&gt;parse_float&lt;/code&gt; 인자를 사용할 수 있다. Python 문서는 이 인자가 문자열을 받아 다른 객체를 반환하는 호출 가능 객체여야 한다고 설명한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from decimal import Decimal
import tomllib

with open(&quot;settings.toml&quot;, &quot;rb&quot;) as f:
    data = tomllib.load(f, parse_float=Decimal)

price = data[&quot;payment&quot;][&quot;amount&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;예를 들어 금액이나 정밀한 소수 계산을 다뤄야 하면 &lt;code&gt;Decimal&lt;/code&gt;로 받는 편이 더 예측 가능하다. 다만 이 선택은 TOML 파싱 자체를 바꾸는 것이 아니라, 부동소수점 토큰이 Python 객체로 변환되는 방식을 바꾸는 것이다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;pyproject.toml을 읽을 때 무엇을 조심해야 할까?&lt;/h2&gt;
&lt;p&gt;PyPA 문서는 &lt;code&gt;pyproject.toml&lt;/code&gt;이 빌드 시스템과 프로젝트 메타데이터를 담는 표준 파일이라고 설명한다. 따라서 &lt;code&gt;tomllib&lt;/code&gt;로 읽은 뒤에는 단순히 키가 존재하는지만 보는 것이 아니라, 어느 테이블이 어떤 표준을 따르는지 구분해서 해석해야 한다.&lt;/p&gt;
&lt;p&gt;대표적으로 &lt;code&gt;[build-system]&lt;/code&gt;과 &lt;code&gt;[project]&lt;/code&gt;는 의미가 다르다. &lt;code&gt;[project]&lt;/code&gt; 메타데이터는 PEP 621 규칙을 따르며, 도구별 설정은 보통 &lt;code&gt;[tool.도구이름]&lt;/code&gt; 아래에 들어간다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import tomllib

with open(&quot;pyproject.toml&quot;, &quot;rb&quot;) as f:
    data = tomllib.load(f)

project = data.get(&quot;project&quot;, {})
requires_python = project.get(&quot;requires-python&quot;)
dependencies = project.get(&quot;dependencies&quot;, [])&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;즉 &lt;code&gt;tomllib&lt;/code&gt;는 파싱까지만 책임지고, 어떤 키 조합이 유효한지는 해당 표준이나 도구 문서를 따로 봐야 한다. 이 점을 구분해야 &quot;파싱 성공&quot;과 &quot;메타데이터가 표준에 맞음&quot;을 혼동하지 않는다.&lt;/p&gt;

&lt;h2&gt;에러 처리는 어떻게 보면 될까?&lt;/h2&gt;
&lt;p&gt;문법이 잘못된 TOML을 읽으면 &lt;code&gt;tomllib.TOMLDecodeError&lt;/code&gt;가 발생한다. Python 3.14 문서는 이 예외가 &lt;code&gt;msg&lt;/code&gt;, &lt;code&gt;doc&lt;/code&gt;, &lt;code&gt;pos&lt;/code&gt;, &lt;code&gt;lineno&lt;/code&gt;, &lt;code&gt;colno&lt;/code&gt; 같은 속성을 제공한다고 설명한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import tomllib

try:
    with open(&quot;broken.toml&quot;, &quot;rb&quot;) as f:
        data = tomllib.load(f)
except tomllib.TOMLDecodeError as exc:
    print(exc.msg)
    print(exc.lineno, exc.colno)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;또한 Python 3.14 문서는 이 예외를 위치 인자로 생성하는 방식이 deprecated라고 명시한다. 직접 예외를 만들어 던질 일이 거의 없더라도, 예외 객체 구조가 문서화되어 있다는 점은 로그와 진단 메시지를 만들 때 유용하다.&lt;/p&gt;

&lt;h2&gt;실무 기준으로 정리하면?&lt;/h2&gt;
&lt;p&gt;대부분의 Python 3.11 이상 프로젝트에서는 &lt;strong&gt;TOML 읽기 기본값으로 &lt;code&gt;tomllib&lt;/code&gt;&lt;/strong&gt;를 써도 된다. 표준 라이브러리라 의존성 추가가 없고, &lt;code&gt;pyproject.toml&lt;/code&gt; 같은 표준 파일을 읽는 목적에 잘 맞기 때문이다.&lt;/p&gt;
&lt;p&gt;다만 아래는 분리해서 판단해야 한다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;파일을 수정해 다시 저장해야 하는가&lt;/li&gt;
  &lt;li&gt;파싱 이후 메타데이터 유효성 검증까지 필요한가&lt;/li&gt;
  &lt;li&gt;실행 환경이 Python 3.11 미만인가&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 세 조건 중 하나라도 걸리면 &lt;code&gt;tomllib&lt;/code&gt;만으로는 부족할 수 있다. 반대로 &quot;읽기만 하고, Python 3.11 이상이며, 표준 라이브러리를 선호한다&quot;면 선택은 단순하다.&lt;/p&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Python 3.10 이하에서는 tomllib를 쓸 수 없을까?&lt;/h3&gt;
&lt;p&gt;표준 라이브러리 &lt;code&gt;tomllib&lt;/code&gt;는 Python 3.11에서 추가됐다. 따라서 3.10 이하에서는 같은 이름의 표준 모듈을 사용할 수 없다.&lt;/p&gt;

&lt;h3&gt;tomllib로 TOML 파일을 다시 저장할 수 있을까?&lt;/h3&gt;
&lt;p&gt;아니다. Python 3.14 문서는 &lt;code&gt;tomllib&lt;/code&gt;를 파싱 모듈로 설명하며, 쓰기 API는 제공하지 않는다. 읽기와 쓰기가 모두 필요하면 별도 라이브러리를 따로 검토해야 한다.&lt;/p&gt;

&lt;h3&gt;pyproject.toml을 읽었다면 그 내용이 표준에 맞는지도 자동으로 보장될까?&lt;/h3&gt;
&lt;p&gt;그렇지 않다. &lt;code&gt;tomllib&lt;/code&gt;는 TOML 문법을 Python 객체로 바꿔 줄 뿐이고, &lt;code&gt;[project]&lt;/code&gt;나 &lt;code&gt;[build-system]&lt;/code&gt;의 의미 검증은 해당 표준과 도구 규칙이 맡는다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.python.org/3/library/tomllib.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Python 3.14 tomllib 문서&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://peps.python.org/pep-0680/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;PEP 680 - tomllib: Support for Parsing TOML in the Standard Library&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://packaging.python.org/en/latest/guides/writing-pyproject-toml/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;PyPA - Writing your pyproject.toml&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://packaging.python.org/en/latest/specifications/declaring-project-metadata/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;PyPA - pyproject.toml specification&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://toml.io/en/v1.0.0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TOML v1.0.0 specification&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/C, C++, Java, Python</category>
      <category>PEP680</category>
      <category>pyproject.toml</category>
      <category>python</category>
      <category>toml</category>
      <category>tomllib</category>
      <category>개발환경</category>
      <category>패키징</category>
      <category>표준라이브러리</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/597</guid>
      <comments>https://jcdgods.tistory.com/597#entry597comment</comments>
      <pubDate>Fri, 29 May 2026 12:42:40 +0900</pubDate>
    </item>
    <item>
      <title>리눅스 tmpfiles.d 설정 기준 정리</title>
      <link>https://jcdgods.tistory.com/596</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzZOTu/dJMcaaFuw6j/If5CZc3pVyibUB2K4Wy1qK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzZOTu/dJMcaaFuw6j/If5CZc3pVyibUB2K4Wy1qK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzZOTu/dJMcaaFuw6j/If5CZc3pVyibUB2K4Wy1qK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdzZOTu%2FdJMcaaFuw6j%2FIf5CZc3pVyibUB2K4Wy1qK%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;리눅스 tmpfiles.d 설정 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;tmpfiles.d&lt;/code&gt;는 리눅스에서 부팅 시 필요한 디렉터리와 파일을 만들고, 일정 시간이 지난 임시 파일을 정리할 때 자주 등장한다. 2026년 5월 28일 기준 systemd 공식 문서를 보면 이 기능은 단순히 &lt;code&gt;/tmp&lt;/code&gt; 청소용이 아니라, 생성, 권한 조정, 내용 기록, 시간 기반 정리까지 포함하는 일반적인 파일 관리 규칙으로 정의돼 있다.&lt;/p&gt;
&lt;p&gt;실무에서 핵심은 세 가지다. 서비스 전용 런타임 디렉터리처럼 unit 수명과 함께 관리해야 하는 경로라면 &lt;code&gt;RuntimeDirectory=&lt;/code&gt; 같은 service 옵션이 우선이고, 여러 파일을 배포판 기본값 위에서 덮어써야 할 때는 &lt;code&gt;/etc/tmpfiles.d/&lt;/code&gt; 우선순위를 이해해야 하며, 정리 동작까지 기대한다면 &lt;code&gt;age&lt;/code&gt;와 &lt;code&gt;systemd-tmpfiles --clean&lt;/code&gt;이 실제로 어떤 범위를 지우는지 확인해야 한다.&lt;/p&gt;

&lt;h2&gt;tmpfiles.d는 무엇을 하는 설정일까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;tmpfiles.d(5)&lt;/code&gt;는 이 포맷을 파일, 디렉터리, FIFO, 디바이스 노드 생성과 접근 권한 조정, 속성 변경, 내용 기록, 시간 기반 제거를 위한 메커니즘으로 설명한다. 이름 때문에 임시 파일 전용처럼 보이지만, 문서상 범위는 그보다 넓다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;systemd-tmpfiles(8)&lt;/code&gt;는 이 규칙을 읽어 생성, 제거, 정리 작업을 수행하는 도구다. 시스템 부팅 시에는 관련 service가 생성 작업을 실행하고, 이후에는 clean timer가 age가 있는 항목을 주기적으로 정리할 수 있다.&lt;/p&gt;

&lt;h2&gt;어떤 디렉터리에 파일을 둬야 할까?&lt;/h2&gt;
&lt;p&gt;공식 문서 기준 역할은 분명하다. &lt;code&gt;/etc/tmpfiles.d/&lt;/code&gt;는 로컬 관리자가 쓰는 위치이고, &lt;code&gt;/usr/lib/tmpfiles.d/&lt;/code&gt;는 패키지 공급자가 기본 규칙을 배포하는 위치다. &lt;code&gt;/run/tmpfiles.d/&lt;/code&gt;는 런타임에 생성되는 규칙에 사용된다.&lt;/p&gt;
&lt;p&gt;같은 이름의 파일이 여러 디렉터리에 있으면 &lt;code&gt;/etc&lt;/code&gt;가 &lt;code&gt;/run&lt;/code&gt;과 &lt;code&gt;/usr/lib&lt;/code&gt;를 덮어쓴다. 문서는 공급자 파일을 비활성화해야 할 때 같은 이름으로 &lt;code&gt;/dev/null&lt;/code&gt; 심볼릭 링크를 &lt;code&gt;/etc/tmpfiles.d/&lt;/code&gt;에 두는 방식을 권장한다.&lt;/p&gt;

&lt;h2&gt;우선순위는 파일명과 경로 중 무엇이 더 중요할까?&lt;/h2&gt;
&lt;p&gt;여기서 자주 헷갈린다. 먼저 같은 basename 파일은 디렉터리 우선순위로 하나만 선택된다. 그다음 실제 적용 대상이 된 모든 파일은 파일명 기준 사전식 순서로 정렬된다.&lt;/p&gt;
&lt;p&gt;또 &lt;code&gt;tmpfiles.d(5)&lt;/code&gt;는 여러 파일이 같은 path를 지정하면 파일명이 더 이른 항목이 적용되고, 나머지 충돌 항목은 오류로 기록된다고 설명한다. 즉 &lt;code&gt;sysctl.d&lt;/code&gt;처럼 나중 파일이 같은 키를 덮어쓰는 구조가 아니라, 동일 path 중복 자체를 피하는 쪽으로 이해하는 편이 맞다.&lt;/p&gt;

&lt;h2&gt;언제 tmpfiles.d보다 RuntimeDirectory가 나을까?&lt;/h2&gt;
&lt;p&gt;systemd 공식 문서는 서비스 데몬이 &lt;code&gt;/run&lt;/code&gt; 아래에 개인 런타임 디렉터리를 필요로 할 때, &lt;code&gt;tmpfiles.d&lt;/code&gt;의 유연성이 꼭 필요하지 않다면 unit 파일의 &lt;code&gt;RuntimeDirectory=&lt;/code&gt;를 쓰는 편이 더 낫다고 안내한다.&lt;/p&gt;
&lt;p&gt;이유는 수명 주기가 서비스와 직접 연결되기 때문이다. &lt;code&gt;systemd.exec(5)&lt;/code&gt;는 이런 디렉터리 옵션을 쓰면 디렉터리가 표준 경로 아래에 자동 생성되고, unit 시작과 정지 흐름에 맞춰 관리된다고 설명한다. 단순히 소켓 디렉터리 하나를 만들 목적이라면 서비스 정의 안에서 끝내는 편이 운영상 더 예측 가능하다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;정리 작업은 어떻게 동작할까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;systemd-tmpfiles --clean&lt;/code&gt;은 age 값이 지정된 항목만 정리한다. 문서상 age는 디렉터리 내용이나 파일을 얼마나 지난 뒤 정리할지 나타내며, line type에 따라 의미가 조금씩 다르다.&lt;/p&gt;
&lt;p&gt;예를 들어 &lt;code&gt;d&lt;/code&gt;는 디렉터리를 만들고 age가 있으면 내부 항목을 시간 기준으로 정리할 수 있다. &lt;code&gt;D&lt;/code&gt;는 여기에 더해 &lt;code&gt;--remove&lt;/code&gt; 시 내용 제거까지 연결된다. 따라서 생성만 필요한 경로와, 주기 청소까지 맡길 경로를 구분해서 써야 한다.&lt;/p&gt;

&lt;h2&gt;자주 쓰는 line type은 무엇을 뜻할까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;타입&lt;/th&gt;
      &lt;th&gt;의미&lt;/th&gt;
      &lt;th&gt;실무 해석&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;d&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;디렉터리 생성&lt;/td&gt;
      &lt;td&gt;권한과 소유자까지 함께 관리할 때 기본 선택지&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;D&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;디렉터리 생성 + remove 시 내용 정리&lt;/td&gt;
      &lt;td&gt;패키지 제거 시 내부까지 같이 정리할 필요가 있을 때 검토&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;f&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;일반 파일 생성&lt;/td&gt;
      &lt;td&gt;빈 파일이나 기본 내용을 가진 파일 생성에 사용&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;w&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;파일에 내용 쓰기&lt;/td&gt;
      &lt;td&gt;단순 생성이 아니라 지정 문자열을 기록해야 할 때 사용&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;z&lt;/code&gt;, &lt;code&gt;Z&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;권한과 소유자, 보안 라벨 조정&lt;/td&gt;
      &lt;td&gt;기존 경로의 퍼미션 보정이 목적일 때 사용&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;r&lt;/code&gt;, &lt;code&gt;R&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;경로 제거&lt;/td&gt;
      &lt;td&gt;생성 규칙과는 별도이며 제거 범위를 명확히 이해해야 함&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;운영 기준은 어떻게 잡으면 될까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;권장 기준&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;서비스 전용 &lt;code&gt;/run&lt;/code&gt; 디렉터리&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;RuntimeDirectory=&lt;/code&gt; 우선&lt;/td&gt;
      &lt;td&gt;unit 수명과 같이 관리되고 선행 실행 순서를 덜 신경 써도 된다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;패키지 기본 규칙 배포&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;/usr/lib/tmpfiles.d/&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;로컬 관리자가 &lt;code&gt;/etc&lt;/code&gt;에서 쉽게 오버라이드할 수 있다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;로컬 운영 정책 추가&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;/etc/tmpfiles.d/&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;공급자 기본값보다 높은 우선순위를 가진다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;임시 파일 청소 자동화&lt;/td&gt;
      &lt;td&gt;age를 명시한 &lt;code&gt;d&lt;/code&gt; 또는 관련 타입 사용&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;--clean&lt;/code&gt;은 age가 없는 항목을 정리하지 않는다&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. tmpfiles.d는 부팅 때만 실행될까?&lt;/h3&gt;
&lt;p&gt;아니다. 생성은 부팅 시 관련 service로 실행될 수 있지만, &lt;code&gt;systemd-tmpfiles&lt;/code&gt; 자체는 &lt;code&gt;--create&lt;/code&gt;, &lt;code&gt;--clean&lt;/code&gt;, &lt;code&gt;--remove&lt;/code&gt; 같은 명령으로 별도 실행할 수 있다. 정리도 timer를 통해 주기적으로 일어날 수 있다.&lt;/p&gt;

&lt;h3&gt;Q. 같은 path를 두 파일에서 선언해도 마지막 파일이 이길까?&lt;/h3&gt;
&lt;p&gt;그렇게 보면 안 된다. &lt;code&gt;tmpfiles.d(5)&lt;/code&gt;는 같은 path를 지정하는 충돌 항목이 있으면 파일명이 더 이른 항목을 적용하고, 다른 충돌 항목은 오류로 기록된다고 설명한다. 따라서 path 중복을 피하는 설계가 안전하다.&lt;/p&gt;

&lt;h3&gt;Q. 느낌표 &lt;code&gt;!&lt;/code&gt;가 붙은 규칙은 언제 적용될까?&lt;/h3&gt;
&lt;p&gt;문서 기준으로 &lt;code&gt;!&lt;/code&gt;는 부팅 시에만 안전한 규칙을 뜻하며, &lt;code&gt;--boot&lt;/code&gt; 옵션을 줄 때만 고려된다. 실행 중 시스템에서 돌리면 위험할 수 있는 동작을 구분하는 표시다.&lt;/p&gt;

&lt;h2&gt;한 줄 정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;tmpfiles.d&lt;/code&gt;는 임시 파일 삭제 규칙만이 아니라 시스템 경로 생성과 정리 정책을 선언하는 포맷이다. 다만 서비스 전용 런타임 디렉터리는 가능하면 &lt;code&gt;RuntimeDirectory=&lt;/code&gt;로 처리하고, 로컬 오버라이드는 &lt;code&gt;/etc/tmpfiles.d/&lt;/code&gt;, 공급자 기본값은 &lt;code&gt;/usr/lib/tmpfiles.d/&lt;/code&gt;로 나누는 편이 현재 systemd 문서 기준에 맞는 운영 방식이다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/latest/tmpfiles.d.html&quot; rel=&quot;noopener noreferrer&quot;&gt;systemd 공식 문서: tmpfiles.d(5)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/latest/systemd-tmpfiles.html&quot; rel=&quot;noopener noreferrer&quot;&gt;systemd 공식 문서: systemd-tmpfiles(8)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html&quot; rel=&quot;noopener noreferrer&quot;&gt;systemd 공식 문서: systemd.exec(5)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://man7.org/linux/man-pages/man5/tmpfiles.d.5.html&quot; rel=&quot;noopener noreferrer&quot;&gt;man7: tmpfiles.d(5)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/운영체제</category>
      <category>Linux</category>
      <category>RuntimeDirectory</category>
      <category>systemd</category>
      <category>systemd-tmpfiles</category>
      <category>tmpfiles.d</category>
      <category>리눅스</category>
      <category>서버운영</category>
      <category>운영체제</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/596</guid>
      <comments>https://jcdgods.tistory.com/596#entry596comment</comments>
      <pubDate>Thu, 28 May 2026 10:40:19 +0900</pubDate>
    </item>
    <item>
      <title>X-Content-Type-Options nosniff 기준 정리</title>
      <link>https://jcdgods.tistory.com/595</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceW6R4/dJMcaiwGV2s/NmnZjcdlaGmYdeGXXGVEK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceW6R4/dJMcaiwGV2s/NmnZjcdlaGmYdeGXXGVEK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceW6R4/dJMcaiwGV2s/NmnZjcdlaGmYdeGXXGVEK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceW6R4%2FdJMcaiwGV2s%2FNmnZjcdlaGmYdeGXXGVEK1%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;X-Content-Type-Options nosniff 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;X-Content-Type-Options&lt;/code&gt;는 서버가 선언한 &lt;code&gt;Content-Type&lt;/code&gt;을 브라우저가 임의로 추정해 바꾸지 않도록 돕는 HTTP 응답 헤더다. 2026년 5월 27일 기준 MDN과 WHATWG Fetch Standard를 보면, 이 헤더는 &lt;code&gt;nosniff&lt;/code&gt; 값 하나만 사용하며 특히 &lt;code&gt;script&lt;/code&gt;와 &lt;code&gt;style&lt;/code&gt; 요청에서 MIME 타입이 맞지 않으면 로딩을 차단한다.&lt;/p&gt;
&lt;p&gt;실무에서는 정적 파일 서버, 업로드 파일 제공 경로, CDN, 프록시 캐시를 운영할 때 이 헤더와 올바른 &lt;code&gt;Content-Type&lt;/code&gt; 설정을 함께 점검하는 편이 안전하다. 이 글은 공식 문서 기준으로 &lt;code&gt;nosniff&lt;/code&gt;가 실제로 무엇을 막는지와 언제 빠뜨리기 쉬운지를 정리한다.&lt;/p&gt;

&lt;h2&gt;X-Content-Type-Options는 정확히 무엇을 할까?&lt;/h2&gt;
&lt;p&gt;MDN은 이 헤더를 서버가 보낸 MIME 타입을 존중하라고 브라우저에 알리는 응답 헤더로 설명한다. 핵심은 브라우저가 응답 본문 내용을 보고 다른 타입으로 추정하는 동작, 즉 MIME sniffing을 줄이는 데 있다.&lt;/p&gt;
&lt;p&gt;문법은 단순하다. 공식 문서 기준으로 사용할 수 있는 값은 &lt;code&gt;nosniff&lt;/code&gt; 하나뿐이다. 즉 이 헤더는 세부 정책을 여러 값으로 조정하는 형태가 아니라, 켜거나 끄는 성격에 가깝다.&lt;/p&gt;

&lt;h2&gt;nosniff가 막는 것은 무엇일까?&lt;/h2&gt;
&lt;p&gt;Fetch Standard는 &lt;code&gt;nosniff&lt;/code&gt;가 있을 때 요청 목적지가 &lt;code&gt;script&lt;/code&gt; 계열인데 MIME 타입을 파싱할 수 없거나 JavaScript MIME 타입이 아니면 차단하도록 정의한다. &lt;code&gt;style&lt;/code&gt; 요청도 마찬가지로 &lt;code&gt;text/css&lt;/code&gt;가 아니면 차단한다.&lt;/p&gt;
&lt;p&gt;MDN은 여기에 더해, 다른 응답 유형에서는 브라우저가 선언된 &lt;code&gt;Content-Type&lt;/code&gt;을 그대로 사용하고 내용을 보고 HTML 등으로 추정하지 않는다고 설명한다. 예를 들어 본문에 HTML 마크업이 있어도 &lt;code&gt;Content-Type: text/plain&lt;/code&gt;과 &lt;code&gt;X-Content-Type-Options: nosniff&lt;/code&gt;를 함께 보내면 브라우저는 이를 HTML 문서로 해석하지 않는다.&lt;/p&gt;

&lt;h2&gt;왜 보안 설정으로 자주 같이 언급될까?&lt;/h2&gt;
&lt;p&gt;MDN의 보안 가이드는 모든 사이트가 &lt;code&gt;X-Content-Type-Options: nosniff&lt;/code&gt;를 설정하고, 동시에 각 파일에 맞는 &lt;code&gt;Content-Type&lt;/code&gt;을 정확히 보내야 한다고 권장한다. 잘못된 MIME 타입을 선언한 스크립트나 스타일시트가 우연히 실행되거나 해석되는 일을 줄이기 때문이다.&lt;/p&gt;
&lt;p&gt;특히 사용자가 올린 파일을 그대로 제공하는 경로나, 프록시가 원본의 &lt;code&gt;Content-Type&lt;/code&gt;을 잘못 전달할 수 있는 환경에서는 이 헤더만 넣는 것으로 끝나지 않는다. 응답 헤더 자체가 정확해야 하고, 업로드 파일을 HTML이나 JavaScript로 오해할 수 없도록 타입과 제공 위치를 함께 관리해야 한다.&lt;/p&gt;

&lt;h2&gt;언제 효과를 체감하기 쉬울까?&lt;/h2&gt;
&lt;h3&gt;1. 정적 자산의 MIME 타입이 잘못 나갈 때&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;app.js&lt;/code&gt;가 실수로 &lt;code&gt;text/plain&lt;/code&gt;으로 나가면, &lt;code&gt;nosniff&lt;/code&gt;가 없는 환경에서는 브라우저별 동작 차이가 문제를 숨길 수 있다. 반면 &lt;code&gt;nosniff&lt;/code&gt;가 있으면 스크립트 로딩이 차단되어 설정 오류가 더 빨리 드러난다.&lt;/p&gt;

&lt;h3&gt;2. 업로드 파일을 별도 도메인 없이 직접 제공할 때&lt;/h3&gt;
&lt;p&gt;MDN은 브라우저가 비HTML 타입 응답을 HTML로 해석하지 않도록 만드는 효과를 설명한다. 따라서 사용자 업로드 파일을 같은 서비스 안에서 제공하는 경우, &lt;code&gt;Content-Type&lt;/code&gt;과 &lt;code&gt;nosniff&lt;/code&gt; 조합은 기본 점검 항목에 가깝다.&lt;/p&gt;

&lt;h3&gt;3. CDN이나 리버스 프록시가 헤더를 바꿀 수 있을 때&lt;/h3&gt;
&lt;p&gt;원본 서버는 올바르게 설정했더라도, 중간 계층이 &lt;code&gt;Content-Type&lt;/code&gt;을 잘못 캐시하거나 기본 타입으로 덮어쓸 수 있다. 이 경우 &lt;code&gt;nosniff&lt;/code&gt;는 문제를 숨기지 않고 드러내는 역할을 한다.&lt;/p&gt;

&lt;h2&gt;무엇을 함께 확인해야 할까?&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;이 실제 파일 종류와 맞는지 확인해야 한다.&lt;/li&gt;
  &lt;li&gt;스크립트는 JavaScript MIME 타입으로, 스타일시트는 &lt;code&gt;text/css&lt;/code&gt;로 나가야 한다.&lt;/li&gt;
  &lt;li&gt;업로드 파일 제공 경로가 애플리케이션 본문과 같은 origin인지 분리 전략이 필요한지 검토해야 한다.&lt;/li&gt;
  &lt;li&gt;CSP, 다운로드 강제, 별도 파일 도메인 같은 다른 방어 수단이 필요한지 함께 봐야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;X-Content-Type-Options&lt;/code&gt;는 리소스 출처 허용 범위를 정하는 정책은 아니다. 어떤 스크립트를 어디서 불러올 수 있는지는 CSP 같은 별도 정책이 담당한다. 따라서 &lt;code&gt;nosniff&lt;/code&gt;는 MIME 타입 검증 축의 설정으로 이해하는 편이 정확하다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;기본값처럼 생각해도 될까?&lt;/h2&gt;
&lt;p&gt;MDN의 보안 가이드는 모든 사이트에 &lt;code&gt;nosniff&lt;/code&gt; 설정을 권장한다. 다만 이것은 헤더 하나만 추가하면 끝난다는 뜻이 아니라, 잘못된 MIME 타입을 내보내는 자산이 없는지 먼저 확인하라는 의미에 가깝다.&lt;/p&gt;
&lt;p&gt;운영 중인 서비스에 이 헤더를 새로 켜면, 이전에는 브라우저가 관대하게 받아주던 잘못된 정적 파일 설정이 바로 드러날 수 있다. 그래서 적용 전에는 자주 쓰는 JavaScript, CSS, 업로드 파일 응답 헤더를 먼저 점검하는 편이 현실적이다.&lt;/p&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. 값은 &lt;code&gt;nosniff&lt;/code&gt; 말고 다른 것도 있나?&lt;/h3&gt;
&lt;p&gt;2026년 5월 27일 기준 MDN과 Fetch Standard는 &lt;code&gt;X-Content-Type-Options&lt;/code&gt;에 대해 &lt;code&gt;nosniff&lt;/code&gt;만 정의한다. 다른 값으로 세밀한 정책을 조정하는 헤더는 아니다.&lt;/p&gt;

&lt;h3&gt;Q. JavaScript와 CSS에만 영향이 있나?&lt;/h3&gt;
&lt;p&gt;Fetch Standard는 차단 규칙을 &lt;code&gt;script&lt;/code&gt; 계열과 &lt;code&gt;style&lt;/code&gt; 목적지에 대해 명시한다. MDN은 여기에 더해, 다른 응답 유형에서도 선언된 &lt;code&gt;Content-Type&lt;/code&gt;을 그대로 사용해 HTML 등으로 추정하지 않는 효과가 있다고 설명한다.&lt;/p&gt;

&lt;h3&gt;Q. &lt;code&gt;Content-Type&lt;/code&gt;이 정확하면 굳이 필요 없을까?&lt;/h3&gt;
&lt;p&gt;MDN 보안 가이드는 정확한 &lt;code&gt;Content-Type&lt;/code&gt;과 &lt;code&gt;nosniff&lt;/code&gt;를 함께 권장한다. 즉 둘 중 하나를 고르는 관계가 아니라 같이 설정하는 관계에 가깝다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;X-Content-Type-Options: nosniff&lt;/code&gt;는 브라우저가 MIME 타입을 추정해 스크립트나 스타일시트를 받아들이는 일을 줄이고, 다른 응답도 선언된 타입대로 해석하도록 돕는 기본 방어선이다. 2026년 5월 27일 기준 공식 문서상 핵심은 간단하다. &lt;code&gt;nosniff&lt;/code&gt;를 켜고, &lt;code&gt;Content-Type&lt;/code&gt;을 정확히 보낸다.&lt;/p&gt;
&lt;p&gt;실무에서 확인할 기준도 명확하다. 정적 파일 서버, CDN, 업로드 파일 경로에서 실제 응답 헤더가 올바른지 먼저 보고, 그다음 &lt;code&gt;nosniff&lt;/code&gt;를 일관되게 적용하는 편이 안전하다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Content-Type-Options&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN: X-Content-Type-Options header&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/MIME_types&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN: MIME type verification&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://fetch.spec.whatwg.org/#x-content-type-options-header&quot; rel=&quot;noopener noreferrer&quot;&gt;WHATWG: Fetch Standard&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/HTML, Javascript, CSS</category>
      <category>content-type</category>
      <category>html</category>
      <category>HTTP Header</category>
      <category>javascript</category>
      <category>MIME Type</category>
      <category>nosniff</category>
      <category>web security</category>
      <category>X-Content-Type-Options</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/595</guid>
      <comments>https://jcdgods.tistory.com/595#entry595comment</comments>
      <pubDate>Wed, 27 May 2026 12:34:12 +0900</pubDate>
    </item>
    <item>
      <title>PostgreSQL pg_hba.conf 인증 기준 정리</title>
      <link>https://jcdgods.tistory.com/594</link>
      <description>&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s0f43/dJMcagljGIF/2F6dGhkM8jZYdQQUh3rcgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s0f43/dJMcagljGIF/2F6dGhkM8jZYdQQUh3rcgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s0f43/dJMcagljGIF/2F6dGhkM8jZYdQQUh3rcgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs0f43%2FdJMcagljGIF%2F2F6dGhkM8jZYdQQUh3rcgK%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;PostgreSQL pg_hba.conf 인증 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;pg_hba.conf&lt;/code&gt;는 PostgreSQL 서버에 들어오는 연결이 어떤 사용자, 어떤 데이터베이스, 어떤 주소 대역에서 어떤 인증 방식으로 접속할 수 있는지 결정하는 핵심 설정 파일이다. 2026년 5월 26일 기준 PostgreSQL 공식 문서를 보면 이 파일은 연결 시도마다 위에서 아래로 순서대로 평가되며, 처음 일치한 규칙 하나만 적용된다.&lt;/p&gt;
&lt;p&gt;실무에서는 접속이 안 되는 원인 분석, 로컬 개발 환경과 운영 환경 분리, &lt;code&gt;md5&lt;/code&gt;에서 &lt;code&gt;scram-sha-256&lt;/code&gt;로의 전환 판단이 자주 이 파일과 연결된다. 이 글은 공식 문서 기준으로 &lt;code&gt;pg_hba.conf&lt;/code&gt;의 동작 방식, 안전한 기본값, 자주 헷갈리는 포인트를 객관적으로 정리한다.&lt;/p&gt;

&lt;h2&gt;pg_hba.conf는 무엇을 제어할까?&lt;/h2&gt;
&lt;p&gt;PostgreSQL 공식 문서에서 &lt;code&gt;pg_hba.conf&lt;/code&gt;는 클라이언트 인증을 제어하는 파일로 설명된다. 기본 파일은 &lt;code&gt;initdb&lt;/code&gt;로 데이터 디렉터리를 만들 때 함께 생성되며, 위치는 &lt;code&gt;hba_file&lt;/code&gt; 설정으로 확인하거나 바꿀 수 있다.&lt;/p&gt;
&lt;p&gt;이 파일의 한 줄은 대체로 다음 다섯 가지를 정한다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;연결 유형: &lt;code&gt;local&lt;/code&gt;, &lt;code&gt;host&lt;/code&gt;, &lt;code&gt;hostssl&lt;/code&gt;, &lt;code&gt;hostnossl&lt;/code&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;code&gt;scram-sha-256&lt;/code&gt;, &lt;code&gt;peer&lt;/code&gt;, &lt;code&gt;reject&lt;/code&gt; 등&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;왜 순서가 중요할까?&lt;/h2&gt;
&lt;p&gt;공식 문서는 &lt;code&gt;pg_hba.conf&lt;/code&gt;가 연결 시도마다 순차적으로 검사되고, 처음 일치한 레코드가 인증에 사용된다고 설명한다. 뒤에 더 구체적이거나 더 강한 규칙이 있어도 앞의 규칙이 먼저 맞아 버리면 뒤 규칙은 보지 않는다.&lt;/p&gt;
&lt;p&gt;그래서 실무 기준으로는 더 좁은 범위, 더 예외적인 규칙을 위에 두고, 넓은 범위 규칙을 아래에 두는 편이 안전하다. 특정 IP 하나를 막는 &lt;code&gt;reject&lt;/code&gt; 규칙을 네트워크 전체 허용 규칙보다 앞에 두는 패턴이 대표적이다.&lt;/p&gt;

&lt;h2&gt;어떤 인증 방식을 우선적으로 보면 될까?&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;방식&lt;/th&gt;
      &lt;th&gt;적합한 상황&lt;/th&gt;
      &lt;th&gt;주의점&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;scram-sha-256&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;일반적인 비밀번호 기반 운영 환경&lt;/td&gt;
      &lt;td&gt;구형 클라이언트는 지원하지 않을 수 있다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;peer&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;로컬 유닉스 소켓 기반 접속&lt;/td&gt;
      &lt;td&gt;운영체제 사용자명과 DB 사용자명 매핑을 이해해야 한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;reject&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;특정 호스트 또는 대역 차단&lt;/td&gt;
      &lt;td&gt;순서가 뒤로 밀리면 차단되지 않을 수 있다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;md5&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;구형 클라이언트 호환이 필요한 전환 구간&lt;/td&gt;
      &lt;td&gt;MD5 저장 비밀번호 지원은 deprecated 상태다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;trust&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;엄격히 통제된 로컬 테스트 환경&lt;/td&gt;
      &lt;td&gt;접속만 가능하면 어떤 DB 사용자로든 로그인할 수 있다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;현재 공식 문서는 &lt;code&gt;scram-sha-256&lt;/code&gt;를 가장 안전한 비밀번호 기반 방식으로 설명한다. 반면 &lt;code&gt;md5&lt;/code&gt;는 여전히 호환 목적으로 문서에 남아 있지만, MD5로 저장된 비밀번호 지원은 deprecated이며 향후 릴리스에서 제거될 예정이라고 안내한다.&lt;/p&gt;

&lt;h2&gt;안전한 기본값은 어떻게 잡는 편이 나을까?&lt;/h2&gt;
&lt;p&gt;대부분의 운영 환경에서는 다음 기준이 무난하다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;로컬 소켓 접속은 필요 범위 안에서 &lt;code&gt;peer&lt;/code&gt; 또는 &lt;code&gt;scram-sha-256&lt;/code&gt;를 사용한다.&lt;/li&gt;
  &lt;li&gt;원격 접속은 필요한 네트워크 대역만 CIDR로 좁혀서 허용한다.&lt;/li&gt;
  &lt;li&gt;비밀번호 기반 인증은 가능하면 &lt;code&gt;scram-sha-256&lt;/code&gt;를 사용한다.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;0.0.0.0/0&lt;/code&gt;, &lt;code&gt;::0/0&lt;/code&gt; 같은 전체 허용 대역은 특별한 이유가 없으면 피한다.&lt;/li&gt;
  &lt;li&gt;예외 차단 규칙이 필요하면 넓은 허용 규칙보다 위에 둔다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;공식 예시에서도 단일 호스트는 &lt;code&gt;/32&lt;/code&gt;, IPv6 단일 호스트는 &lt;code&gt;/128&lt;/code&gt;처럼 좁은 범위로 표현한다. 허용 대역을 구체적으로 적는 습관이 가장 기본적인 안전장치다.&lt;/p&gt;

&lt;h2&gt;예시는 어떻게 읽으면 될까?&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;local   all             all                                     peer
host    appdb           app_user        10.20.30.40/32          scram-sha-256
host    all             all             10.20.30.0/24           scram-sha-256
host    all             all             10.20.30.99/32          reject
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 예시에서 첫 줄은 로컬 유닉스 소켓 접속을 운영체제 사용자 기준으로 인증한다. 둘째 줄은 특정 애플리케이션 사용자와 단일 호스트를 아주 좁게 허용한다. 셋째 줄은 더 넓은 대역을 허용하지만, 넷째 줄처럼 특정 주소를 막으려면 그 &lt;code&gt;reject&lt;/code&gt; 규칙을 더 위로 올려야 실제로 차단된다.&lt;/p&gt;
&lt;p&gt;즉 사람 눈에 보기 쉬운 정렬보다, PostgreSQL이 실제로 어떤 줄을 먼저 만나는지가 더 중요하다.&lt;/p&gt;

&lt;h2&gt;설정 변경 후 바로 반영될까?&lt;/h2&gt;
&lt;p&gt;공식 문서 기준으로 &lt;code&gt;pg_hba.conf&lt;/code&gt;는 서버 시작 시 읽히고, 실행 중에는 메인 서버 프로세스가 &lt;code&gt;SIGHUP&lt;/code&gt;를 받으면 다시 읽는다. 문서는 &lt;code&gt;pg_ctl reload&lt;/code&gt;, SQL 함수 &lt;code&gt;pg_reload_conf()&lt;/code&gt;, 또는 &lt;code&gt;kill -HUP&lt;/code&gt;를 예로 든다.&lt;/p&gt;
&lt;p&gt;예외도 있다. Windows에서는 이후 새 연결부터 변경 사항이 즉시 적용된다고 공식 문서에 별도 주석이 있다.&lt;/p&gt;

&lt;h2&gt;호스트명과 CIDR 중 무엇이 나을까?&lt;/h2&gt;
&lt;p&gt;공식 문서는 주소 필드에 CIDR, 키워드, 호스트명을 모두 허용한다. 다만 호스트명은 역방향 조회와 정방향 조회를 모두 사용해 일치 여부를 판단하므로, 이름 해석이 느리거나 불안정하면 연결 지연과 운영 난이도를 만들 수 있다.&lt;/p&gt;
&lt;p&gt;운영 환경에서 허용 대역이 명확하다면 보통 CIDR이 더 예측 가능하다. 호스트명 기반 제어가 필요하다면 DNS 응답 속도와 정확성이 전제돼야 한다.&lt;/p&gt;

&lt;h2&gt;자주 헷갈리는 포인트는 무엇일까?&lt;/h2&gt;
&lt;h3&gt;1. &lt;code&gt;md5&lt;/code&gt;로 적어도 실제 인증은 SCRAM일 수 있을까?&lt;/h3&gt;
&lt;p&gt;그럴 수 있다. 현재 공식 문서는 &lt;code&gt;pg_hba.conf&lt;/code&gt;에 &lt;code&gt;md5&lt;/code&gt;를 적었더라도 서버에 저장된 사용자 비밀번호가 SCRAM 형식이면 SCRAM 기반 인증이 자동으로 선택될 수 있다고 설명한다. 다만 이것은 전환 편의 기능이지, 장기적으로 &lt;code&gt;md5&lt;/code&gt;를 계속 유지하라는 의미는 아니다.&lt;/p&gt;

&lt;h3&gt;2. 연결 허용과 DB 권한은 같은 문제일까?&lt;/h3&gt;
&lt;p&gt;아니다. 공식 문서는 사용자가 &lt;code&gt;pg_hba.conf&lt;/code&gt;를 통과해도 데이터베이스의 &lt;code&gt;CONNECT&lt;/code&gt; 권한이 별도로 필요하다고 설명한다. 데이터베이스별 접속 가능 여부를 모두 &lt;code&gt;pg_hba.conf&lt;/code&gt;에만 몰아넣기보다, 권한 관리와 나눠서 생각하는 편이 단순할 수 있다.&lt;/p&gt;

&lt;h3&gt;3. 문법 오류는 어떻게 점검할까?&lt;/h3&gt;
&lt;p&gt;현재 공식 문서는 시스템 뷰 &lt;code&gt;pg_hba_file_rules&lt;/code&gt;가 파일 변경 사전 점검이나 문제 진단에 도움이 된다고 설명한다. 이 뷰에서 &lt;code&gt;error&lt;/code&gt; 필드가 비어 있지 않은 행은 해당 규칙에 문제가 있다는 뜻이다.&lt;/p&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. 운영 서버에서 &lt;code&gt;trust&lt;/code&gt;를 써도 될까?&lt;/h3&gt;
&lt;p&gt;공식 문서 설명대로 &lt;code&gt;trust&lt;/code&gt;는 접속 가능한 누구나 원하는 PostgreSQL 사용자명으로 로그인하게 허용한다. 그래서 일반적인 운영 서버의 원격 접속 규칙에는 적합하지 않다.&lt;/p&gt;

&lt;h3&gt;Q. 로컬 접속은 무조건 &lt;code&gt;peer&lt;/code&gt;가 정답일까?&lt;/h3&gt;
&lt;p&gt;항상 그렇지는 않다. 같은 머신 안에서 운영체제 사용자와 DB 사용자 매핑이 명확하면 &lt;code&gt;peer&lt;/code&gt;가 간단하지만, 애플리케이션 계정 구조나 배포 방식에 따라 로컬도 &lt;code&gt;scram-sha-256&lt;/code&gt;를 쓰는 구성이 더 관리하기 쉬울 수 있다.&lt;/p&gt;

&lt;h3&gt;Q. 원격 접속 한 줄만 넓게 열고 권한으로 막으면 충분할까?&lt;/h3&gt;
&lt;p&gt;권한 관리가 중요하긴 하지만, 네트워크 허용 범위를 좁히는 것은 별도의 방어층이다. 공식 예시와 설명을 기준으로 보면, 필요한 주소 대역만 허용하는 쪽이 더 보수적인 기본값이다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;pg_hba.conf&lt;/code&gt;에서 가장 중요한 기준은 규칙의 순서, 허용 주소 범위의 폭, 그리고 인증 방식의 선택이다. 현재 공식 문서 기준으로 비밀번호 기반 운영 환경의 기본값은 대체로 &lt;code&gt;scram-sha-256&lt;/code&gt; 쪽에 가깝고, &lt;code&gt;md5&lt;/code&gt;는 호환성 전환 구간으로 보는 편이 맞다.&lt;/p&gt;
&lt;p&gt;접속 장애나 보안 설정 점검을 할 때는 규칙이 위에서 아래로 어떻게 매치되는지, 너무 넓은 주소 대역을 열어 두지 않았는지, 그리고 변경 후 재적용이 필요한 환경인지부터 확인하면 된다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/auth-pg-hba-conf.html&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL Documentation: The pg_hba.conf File&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/18/auth-password.html&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL Documentation: Password Authentication&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/&quot; rel=&quot;noopener noreferrer&quot;&gt;PostgreSQL Documentation Home&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/서버, DBMS</category>
      <category>Authentication</category>
      <category>Database Security</category>
      <category>DBMS</category>
      <category>md5</category>
      <category>pg_hba.conf</category>
      <category>PostgreSQL</category>
      <category>scram</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/594</guid>
      <comments>https://jcdgods.tistory.com/594#entry594comment</comments>
      <pubDate>Tue, 26 May 2026 10:38:03 +0900</pubDate>
    </item>
    <item>
      <title>리눅스 Device Tree label과 overlay 기준 정리</title>
      <link>https://jcdgods.tistory.com/593</link>
      <description>&lt;p class=&quot;coupang-partners-disclosure&quot;&gt;본문 중간의 쿠팡 추천 상품 구매시 쿠팡 파트너스에서 일정액의 수수료를 제공받습니다.&lt;/p&gt;
&lt;div class=&quot;jcdgods-inline-image&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6Fohc/dJMcabqOfNY/wy9lAuFVr3KcKx5RH77VHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6Fohc/dJMcabqOfNY/wy9lAuFVr3KcKx5RH77VHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6Fohc/dJMcabqOfNY/wy9lAuFVr3KcKx5RH77VHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6Fohc%2FdJMcabqOfNY%2Fwy9lAuFVr3KcKx5RH77VHK%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1&gt;리눅스 Device Tree label과 overlay 기준 정리&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;Device Tree&lt;/code&gt;에서 &lt;code&gt;label&lt;/code&gt;은 보기 편한 별칭 정도로 오해되기 쉽지만, 실제로는 DTS 작성과 overlay 적용 위치를 안정적으로 지정할 때 자주 쓰이는 참조 기준이다. 2026년 5월 25일 기준 Devicetree Specification과 Linux 커널 문서를 보면, label은 소스 형식에서만 쓰이며 DTB 바이너리에 그대로 저장되는 이름은 아니다.&lt;/p&gt;
&lt;p&gt;실무에서 헷갈리는 지점은 세 가지다. label이 정확히 어디에 붙는지, &lt;code&gt;&amp;amp;label&lt;/code&gt;로 노드를 확장하는 것과 overlay에서 대상을 찾는 것이 같은 원리인지, 그리고 base DT와 overlay를 만들 때 무엇을 준비해야 하는지다. 이 글은 공식 명세와 커널 문서만 기준으로 그 차이를 정리한다.&lt;/p&gt;

&lt;h2&gt;Device Tree label은 정확히 무엇일까?&lt;/h2&gt;
&lt;p&gt;Devicetree Specification의 DTS 형식 문서는 label을 노드나 속성값에 붙일 수 있는 소스 레벨 식별자로 설명한다. label을 참조하면 명시적인 phandle 값이나 전체 경로를 직접 쓰지 않아도 참조를 만들 수 있다. 문서상 label은 1자 이상 31자 이하이며 숫자로 시작할 수 없고, 영문자와 숫자, 밑줄만 사용할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uart0: serial@12340000 {
    compatible = &quot;vendor,soc-uart&quot;;
    reg = &amp;lt;0x12340000 0x1000&amp;gt;;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 예시에서 &lt;code&gt;uart0&lt;/code&gt;가 label이고, 이후 다른 위치에서 &lt;code&gt;&amp;amp;uart0&lt;/code&gt;로 이 노드를 참조할 수 있다. 중요한 점은 이 이름이 사람이 읽는 편의 장치이면서 동시에 컴파일 단계에서 phandle 참조를 만드는 입력이라는 점이다.&lt;/p&gt;

&lt;h2&gt;label은 DTB에도 그대로 남을까?&lt;/h2&gt;
&lt;p&gt;명세는 label이 DTS source format에서만 사용되며 DTB binary에는 인코딩되지 않는다고 설명한다. 그래서 일반적인 base DTS만 컴파일한 결과물만 보고 label 문자열이 항상 남아 있다고 가정하면 안 된다.&lt;/p&gt;
&lt;p&gt;다만 Linux 커널 overlay 문서는 base DT가 &lt;code&gt;-@&lt;/code&gt; 옵션으로 컴파일되지 않으면 &lt;code&gt;&amp;amp;ocp&lt;/code&gt; 같은 label을 overlay 대상 해석에 쓸 수 없다고 설명한다. 즉 overlay 해석이 가능한 환경에서는 dtc가 별도의 symbol 정보를 만들어 두지만, 그것은 &quot;label 문자열이 DTB 표준 본문에 그대로 저장된다&quot;는 뜻과는 다르다.&lt;/p&gt;

&lt;h2&gt;&amp;amp;label로 노드를 확장하는 것은 무엇을 의미할까?&lt;/h2&gt;
&lt;p&gt;DTS에서는 이미 정의된 노드를 &lt;code&gt;&amp;amp;label { ... };&lt;/code&gt; 형식으로 다시 열어 속성이나 자식 노드를 추가할 수 있다. Linux 커널의 DTS coding style 문서도 보드 DTS에서 &lt;code&gt;&amp;amp;label&lt;/code&gt;로 노드를 확장하는 패턴을 전제로 정렬 규칙을 설명한다.&lt;/p&gt;
&lt;p&gt;이 문법은 기존 노드를 새 노드로 대체하는 것이 아니라, 그 노드에 대한 추가 정의를 병합하는 쪽에 가깝다. 그래서 SoC dtsi에서 기본 비활성으로 둔 장치를 보드 DTS에서 다시 열어 &lt;code&gt;status = &quot;okay&quot;;&lt;/code&gt;를 넣는 패턴이 널리 쓰인다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;amp;uart0 {
    current-speed = &amp;lt;115200&amp;gt;;
    status = &quot;okay&quot;;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;실무에서는 이것을 흔히 &quot;오버라이드&quot;라고 부르지만, DTS 소스 차원에서는 기존 노드를 label로 찾아 내용을 보강하거나 일부 속성을 갱신하는 방식으로 이해하는 편이 더 정확하다.&lt;/p&gt;

&lt;h2&gt;overlay에서 label은 왜 더 중요할까?&lt;/h2&gt;
&lt;p&gt;Linux 커널의 overlay 문서는 overlay가 라이브 Device Tree를 수정하는 용도라고 설명한다. 여기서 overlay는 base tree의 어느 위치에 조각을 붙일지 알아야 하는데, 문서는 label 기반 타깃 지정과 경로 기반 타깃 지정을 모두 보여준다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/dts-v1/;
/plugin/;

&amp;amp;ocp {
    bar {
        compatible = &quot;corp,bar&quot;;
    };
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;커널 문서 기준으로 label 기반 문법은 base DT 안에서 label이 어디에 있든 같은 의미로 대상을 찾을 수 있어서 경로보다 선호된다. 반대로 base DT가 &lt;code&gt;-@&lt;/code&gt; 옵션 없이 컴파일됐다면 label 해석이 불가능할 수 있으므로 &lt;code&gt;&amp;amp;{/ocp}&lt;/code&gt; 같은 경로 타깃 문법이 대안이 된다.&lt;/p&gt;

&lt;h2&gt;overlay의 label 참조는 내부적으로 어떻게 풀릴까?&lt;/h2&gt;
&lt;p&gt;Linux 커널의 Dynamic Resolver 문서는 overlay 입력이 적절한 dtc 옵션과 &lt;code&gt;/plugin/&lt;/code&gt; 태그로 컴파일되면 &lt;code&gt;__fixups__&lt;/code&gt;와 &lt;code&gt;__local_fixups__&lt;/code&gt; 정보가 생성된다고 설명한다. 그리고 resolver는 overlay의 &lt;code&gt;__fixups__&lt;/code&gt; 각 속성 이름을 라이브 트리의 symbol, 즉 label 이름과 매칭해 실제 phandle 값으로 치환한다.&lt;/p&gt;
&lt;p&gt;Kernel API 문서도 overlay의 &lt;code&gt;__fixups__&lt;/code&gt; 속성 이름이 라이브 트리의 symbol(label)에 해당한다고 설명한다. 즉 overlay에서 &lt;code&gt;&amp;amp;label&lt;/code&gt;을 쓴다는 것은 단순 문자열 검색이 아니라, 컴파일 단계에서 만들어진 symbol 정보를 바탕으로 phandle 참조를 다시 연결하는 절차에 가깝다.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;iframe src=&quot;https://ads-partners.coupang.com/widgets.html?id=987671&amp;template=carousel&amp;trackingCode=AF7456057&amp;subId=&amp;width=320&amp;height=140&amp;tsource=&quot; width=&quot;320&quot; height=&quot;140&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; referrerpolicy=&quot;unsafe-url&quot; browsingtopics&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;base DTS와 overlay DTS에서 구분해서 볼 기준은 무엇일까?&lt;/h2&gt;
&lt;p&gt;base DTS나 dtsi에서는 다른 보드나 overlay가 안정적으로 참조해야 하는 핵심 노드에 일관된 label을 두는 편이 유지보수에 유리하다. 경로는 파일 구조나 버스 배치가 바뀌면 쉽게 흔들리지만, label은 의도한 참조 지점을 명시적으로 드러낸다.&lt;/p&gt;
&lt;p&gt;overlay 쪽에서는 첫째, base DT가 label 해석이 가능하도록 준비됐는지, 둘째, 경로보다 label 타깃이 더 이식성 있는지, 셋째, 새로 추가하는 노드 안의 phandle 참조가 local fixup으로 안전하게 조정될 수 있는지를 함께 보는 편이 좋다.&lt;/p&gt;

&lt;h2&gt;label, alias, path는 어떻게 구분해야 할까?&lt;/h2&gt;
&lt;p&gt;label은 DTS 소스 안에서 참조를 만들기 위한 식별자다. path는 &lt;code&gt;/soc/serial@12340000&lt;/code&gt; 같은 절대 위치 표현이고, overlay에서는 base DT가 label symbol을 제공하지 않을 때 대안으로 쓸 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/aliases&lt;/code&gt; 노드에 들어가는 alias는 부트 인자나 사용자 공간, 일부 커널 경로에서 사람이 기억하기 쉬운 이름을 제공하는 별도 메커니즘이다. 공식 문서가 설명하는 overlay 타깃 지정은 alias가 아니라 label 또는 path를 기준으로 한다.&lt;/p&gt;

&lt;h2&gt;실무에서 자주 생기는 오해는 무엇일까?&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;label은 노드 이름 자체가 아니다. &lt;code&gt;serial@12340000&lt;/code&gt;이 노드 이름이고 &lt;code&gt;uart0&lt;/code&gt;는 참조용 label일 수 있다.&lt;/li&gt;
  &lt;li&gt;label은 DTS 소스용 식별자다. 일반 DTB 결과만 보고 label 문자열 존재를 전제하면 안 된다.&lt;/li&gt;
  &lt;li&gt;overlay의 &lt;code&gt;&amp;amp;label&lt;/code&gt;은 base DT에 symbol 정보가 있을 때 가장 안정적으로 동작한다.&lt;/li&gt;
  &lt;li&gt;보드 DTS에서의 &lt;code&gt;&amp;amp;label { ... }&lt;/code&gt; 확장은 기존 정의를 병합하는 패턴이지, 별도 노드를 새 이름으로 복제하는 문법이 아니다.&lt;/li&gt;
  &lt;li&gt;경로 타깃은 가능하지만, 커널 문서 기준으로는 label 타깃이 더 이식성이 높다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Q. overlay에서 label을 쓰려면 base DT와 overlay 둘 다 특별한 준비가 필요할까?&lt;/h3&gt;
&lt;p&gt;Linux 커널 문서 기준으로 overlay 입력은 적절한 dtc 옵션과 &lt;code&gt;/plugin/&lt;/code&gt; 태그가 있어야 resolver가 &lt;code&gt;__fixups__&lt;/code&gt; 정보를 사용할 수 있다. 또 base DT가 &lt;code&gt;-@&lt;/code&gt; 옵션으로 컴파일되지 않았다면 label 기반 타깃을 해석하지 못할 수 있다.&lt;/p&gt;

&lt;h3&gt;Q. 같은 노드에 label을 붙여 두면 경로가 바뀌어도 overlay를 그대로 쓸 수 있나?&lt;/h3&gt;
&lt;p&gt;커널 문서는 label 타깃 문법이 base DT 안에서 label 위치가 어디에 있든 적용 가능하므로 경로보다 선호된다고 설명한다. 다만 그 label이 계속 유지된다는 전제가 필요하므로, base DTS 리팩터링 때 label 이름을 바꾸면 overlay도 함께 점검해야 한다.&lt;/p&gt;

&lt;h3&gt;Q. label로 속성값에도 이름을 붙일 수 있나?&lt;/h3&gt;
&lt;p&gt;Devicetree Specification은 label을 노드뿐 아니라 속성값에도 붙일 수 있다고 설명한다. 다만 실무에서 overlay 타깃 지정이나 보드 DTS 확장처럼 자주 보는 패턴은 대개 노드 label 참조다.&lt;/p&gt;

&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Device Tree label&lt;/code&gt;을 볼 때 핵심은 &quot;소스 형식의 참조 이름&quot;, &quot;overlay에서는 symbol 기반 타깃 해석의 출발점&quot;, &quot;경로보다 이식성이 높을 수 있음&quot; 이 세 가지다. 그래서 label을 단순 별명으로만 보지 말고, DTS 병합과 overlay 적용을 안정화하는 기준으로 이해하는 편이 맞다.&lt;/p&gt;
&lt;p&gt;특히 오버라이드와 overlay를 함께 다룰 때는 보드 DTS의 &lt;code&gt;&amp;amp;label { ... }&lt;/code&gt; 확장과 런타임 overlay의 &lt;code&gt;&amp;amp;label&lt;/code&gt; 타깃 지정이 비슷해 보여도 적용 시점과 내부 처리 경로가 다르다는 점을 구분해 두면 디버깅이 훨씬 수월하다.&lt;/p&gt;

&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://devicetree-specification.readthedocs.io/en/v0.3/source-language.html&quot; rel=&quot;noopener noreferrer&quot;&gt;Devicetree Specification: DTS format and labels&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.kernel.org/devicetree/overlay-notes.html&quot; rel=&quot;noopener noreferrer&quot;&gt;Linux Kernel Documentation: Devicetree Overlay Notes&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.kernel.org/devicetree/dynamic-resolution-notes.html&quot; rel=&quot;noopener noreferrer&quot;&gt;Linux Kernel Documentation: Devicetree Dynamic Resolver Notes&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.kernel.org/devicetree/kernel-api.html&quot; rel=&quot;noopener noreferrer&quot;&gt;Linux Kernel Documentation: DeviceTree Kernel API&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.kernel.org/devicetree/bindings/dts-coding-style.html&quot; rel=&quot;noopener noreferrer&quot;&gt;Linux Kernel Documentation: Devicetree Sources Coding Style&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/리눅스 커널</category>
      <category>device tree</category>
      <category>Devicetree</category>
      <category>DTS</category>
      <category>Embedded</category>
      <category>Kernel</category>
      <category>Linux</category>
      <category>overlay</category>
      <author>포도알77</author>
      <guid isPermaLink="true">https://jcdgods.tistory.com/593</guid>
      <comments>https://jcdgods.tistory.com/593#entry593comment</comments>
      <pubDate>Mon, 25 May 2026 11:02:25 +0900</pubDate>
    </item>
  </channel>
</rss>