<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>혠의 기술블로그</title>
    <link>https://devhyen.tistory.com/</link>
    <description>成長中...</description>
    <language>ko</language>
    <pubDate>Thu, 18 Jun 2026 23:27:50 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>devhyen</managingEditor>
    <image>
      <title>혠의 기술블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/7136890/attach/3528d7b68acf48e79b821bb1c50c74cc</url>
      <link>https://devhyen.tistory.com</link>
    </image>
    <item>
      <title>[스프링으로 배우는 안티패턴] Spring Security 6.3 부터는 왜 직렬화 버전(SERIAL_VERSION_UID)를 삭제했을까?</title>
      <link>https://devhyen.tistory.com/52</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #111827; text-align: start;&quot;&gt;&lt;a href=&quot;https://spring.io/blog/2024/01/19/spring-security-6-3-adds-passive-jdk-serialization-deserialization-for&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://spring.io/blog/2024/01/19/spring-security-6-3-adds-passive-jdk-serialization-deserialization-for&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #111827; text-align: start;&quot;&gt;serialVersionUID &lt;/span&gt;란?&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바 직렬화(Serialization)은 객체를 파일이나 네트워크로 보내기 위해서 바이트 배열로 변환하는 과정입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 자바는 내가 지금 저장하려는 객체와 나중에 다시 읽어들일 객체가 같은 클래스를 가지고 있는가를 확인해야합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 사용하는 것이 &lt;span style=&quot;color: #111827; text-align: start;&quot;&gt;serialVersionUID 입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;저장 할 때 UID와 읽을 때의 UID가 같다면 같은 클래스라고 생각하여 데이터를 복원합니다.&amp;nbsp;&lt;br /&gt;값이 다르다면 InvalidClassException로 데이터를 복원하기를 거부합니다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Spring Security에서 SERIAL_VERSION_UID&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용한 이유&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 설계 시, 서로 다른 버전 간의 직렬화 호환성을 보장하지 않겠다는 의도적인 결정을 했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당시 RMI(Remote Method Invocation)환경을 고려하여, 서버와 클라이언트가 반드시 동일한 버전의 시큐리티 라이브러리를 사용하도록 강제하기 위함이었습니다. 이를 위해 모든 시큐리티 클래스가 공통의 SpringSecurityCoreVersion.SERIAL_VERSION_UID 를 바라보게 설계했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;RMI란 ? 자바 객체를 네트워크를 통해 다른 서버로 보내고, 그 쪽 서버에서 그 객체의 메서드를 원격으로 실행하는 기술&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;강제한 이유&lt;/b&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;RMI는 통신과정에서 자바의 기본 직렬화를 사용합니다. 자바의 기본 직렬화는 엄격하기 때문에 역 직렬화 에러가 발생할 수 있습니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;시큐리티는 보안 프레임워크이기 때문에 양쪽이 서로 다른 버전의 보안 로직을 가지고 있다면, 보안 검증 과정에서 예상치 못한 허점이 생기거나, 혹은 검증 방식 자체의 충돌이 발생할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용자가 호출하는 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자가 직접 구현한 UserDetails나 OAuth2 관련 커스텀 클래스에서 보안 프레임워크의 UID를 참조하여 자신의 serialVersionUID를 할당하는 방식으로 사용했습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778741299001&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;heading-4&quot; data-ke-size=&quot;size20&quot;&gt;SERIAL_VERSION_UID 가 안티패턴인 이유&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;직렬화의 목적 위배&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;serialVersionUID는 클래스의 '구조'가 변했을 때 바뀌어야 합니다. 하지만 스프링은 릴리즈 버전에 종속시켰습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;운영 데이터와 프레임워크 버전의 강제 결합&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프레임워크를 업데이트 하는 것은 운영자의 결정이지만, 세션 데이터는 프레임워크보다 오래 살아남아야 합니다. 데이터를&amp;nbsp; 프레임워크의 릴리즈 주기에 가두어 버린 것은 가장 큰 설계적 과오였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt; 분산 시스템과 세션 영속성이 일상이 된 현대의 웹 환경에서, 그 '강제'는 곧 장애 &lt;/span&gt;&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;개선 방법&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;독립적 선언&lt;/li&gt;
&lt;li&gt;하위 호환성 유지&lt;/li&gt;
&lt;li&gt;JSON 직렬화 권장&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더이상 전역 상수를 참조하지 않고, 직렬화가 필요한 클래스는 각자 고유하고 불변한 &lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;serialVersionUID를 직접 명시해야합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;기존의 시스템을 업그레이드 할 때는 기존버전의 UID로 하드코딩하여 이전 데이터들을 그대로 읽어올 수 있도록 해줘야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;JDK 기본 직렬화는 취약점과 호환성 문제가 많으므로 가급적 Jackson등을 활용한 JSON 기반 직렬화로 전환하는 것이 권장됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;&quot;그렇다면 버전이 다를 때 발생하는 보안상의 위험은 어떻게 해결했을까?&quot;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;과거에는 데이터의 직렬화 버전만 틀어막으면 보안이 해결될 것이라 믿었지만, 이는 운영 환경의 장애라는 더 큰 비용을 치러야 했습니다. 스프링 시큐리티 6.3부터는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;'데이터(세션)'와 '보안 로직(코드)'을 분리&lt;/span&gt;했습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이제 프레임워크는 직렬화 호환성을 최대한 보장하여 세션 데이터를 살려내되, 그 데이터를 처리하는 보안 검증 로직은 항상 최신 버전의 코드가 엄격하게 수행하도록 설계되었습니다. 즉,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;데이터 호환성은 '테스트'가 보장하고, 보안 무결성은 '최신 코드'가 책임지는 구조로 진화한 것&lt;/span&gt;입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;교훈&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안과 개발 편의성도 중요하지만, 오히려 안전한 라이브러리를 위해서는 하위 호환성을 고려한 점진적 진화 혹은&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 영속성과 라이브러리 배포 주기를 분리하는 전략이 필요하다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend &amp;middot; Infra</category>
      <author>devhyen</author>
      <guid isPermaLink="true">https://devhyen.tistory.com/52</guid>
      <comments>https://devhyen.tistory.com/52#entry52comment</comments>
      <pubDate>Thu, 14 May 2026 16:25:59 +0900</pubDate>
    </item>
    <item>
      <title>[Elasticsearch] Low Level Client 6.x에서 Java API Client 9.x 마이그레이션 트러블슈팅</title>
      <link>https://devhyen.tistory.com/51</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;마이그레이션 하게된 배경&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Elasticsearch 6.x 버전은 공식 지원 종료 (EOL) 된지 오래 되었습니다.&amp;nbsp;&lt;br /&gt;더이상 보안 패치나 버그 수정을 기대할 수 없기 때문에 실제로 발견된 CVE 취약점들에 무방비로 노출될 위험이 있었습니다.&amp;nbsp;&lt;br /&gt;서비스의 지속 가능성과 보안 안정성을 위해 최신 버전인 9.x 로의 마이그레이션을 진행하게 되었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;이미 돌아가는 코드를 고치게 되다 .. 왜 ? ?&amp;nbsp;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6.x 에서 9.x 버전의 차이는 3세대간의 차이라서 &lt;b&gt;호환성이 단절&lt;/b&gt;되어있었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 코드로는 호환이 되지 않아서 트러블 슈팅을 하며 마이그레이션을 진행하게 되었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한,&amp;nbsp; 새로운 Java API Client 9.x 는 최소&lt;b&gt; Java 17 이상이 필요&lt;/b&gt;하기 때문에 Java 버전도 올려야했습니다.&amp;nbsp;&lt;br /&gt;java 버전을 올리는 것 만으로도 많은 코드 수정과 변화가 있었지만, 이 글에서는 ES 를 올리며 마주한 큰 트러블 슈팅 2가지를 다루려고 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. _id 필드에 대한 메모리 로딩 기본적 차단 이슈&amp;nbsp;&lt;br /&gt;문제 현상&amp;nbsp;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;co.elastic.clients.elasticsearch._types.ElasticsearchException: [es/search] failed: [search_phase_execution_exception] all shards failed&quot;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;기존 6.x 환경에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;_id&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;필드를 기준으로 정렬(Sort)하거나 집계(Aggregation)를 수행하던 쿼리들이 9.x 환경에서는&lt;span&gt; 위 메세지를 보이며 &lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;에러가 발생하고 있었습니다. 즉, 최신 버전의 Elasticsearch가 시스템 안정성을 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;strong&quot;&gt;_id&lt;span&gt;&amp;nbsp;&lt;/span&gt;필드를 메모리에 직접 올려 정렬이나 집계에 사용하는 행위를 원천 차단&lt;/span&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;하고 있었던 것입니다.&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6.x 시절에는 딥 페이징을 위해 _id 를 타이브레이커로 사용하는 것이 표준이었으나&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ES는 성능최적화를 위해 가상 필드인 _shard_doc을 도입했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해결 방법 : _id에서 _shard_doc으로의 전환&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 안정성을 해치며 _id의 fielddata 설정을 강제로 풀기보다는 9.x 정식 지원하며 성능적으로 우수한 _shard_doc을 타이브레이커로 사용하기로 결정했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;_shard_doc 은 정수 기반의 물리적 위치값이라 메모리 사용량이 매우 적고 정렬 속도도 빨랐기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 안정성을 위해 구현할 때 아래를 주의하며 진행했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 동적 정렬 값 추출&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스의 로직들은 검색응답의 sort [1] 값을 추출하여 다음 search_after 요청에 사용하는 구조였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬 필드를 _id 에서 _shard_doc 으로 변경하면 추출되는 값 자체가 자동으로 숫자형으로 바뀌므로 로직의 큰 틀은 유지할 수 있었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. JSON 직렬화 타입 불일치 해결&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존코드는 _id 가 문자열이었기때문에 &lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;g.writeString(searchAfter) 와 같이 처리하고 있었습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;shard_doc은 숫자 타입입니다. 이를 문자열로 보낼 경우 타입 물일치 에러를 던지기 때문에 정렬값의 타입에 따라 writeNumber와 writeString 을 분기 처리하여 JSON 타입을 정확히 맞추도록 수정했습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;3. 기존 오프셋 호환성&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;배치가 중단된 지점부터 재시작 하기 위해 저장해 둔 오프셋 파일에는 여전히 문자열 _id 값이 남아있습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;따라서 마이그레이션 시점에 기존 오프셋 파일을 초기화하거나 첫 실행 시 타입을 체크하여 안전하게 전이할 수 있는 보완 처리를 진행했습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. typed_keys 이슈&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;문제 현상&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마이그레이션 후, 기존 잘 작동하던 집계 리포트 기능들이 응답 데이터를 파싱하는 과정에서 에러를 보였습니다. 로그 확인 결과, 응답 JSON의 집계 키 이름이 date_terms이 아닌 range#date_terms 나 sterms#type_terms 와 같이 {type}#{name} 형태로 나오고 있었습니다. 이로인해 기존 파서가 키를 찾지 못해&amp;nbsp; NullPointerException&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;이나&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;IllegalStateException&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;이 발생했습니다.&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;원인 분석&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;범인은 typed_keys 매개변수 입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;typed_keys=true&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;옵션이 켜지면, ES 서버는 집계 응답의 키 앞에 해당 집계의 타입(range, sterms, sum 등)을 접두어로 붙여줍니다. 이는 클라이언트가 응답을 역직렬화(Deserialization)할 때, 각 데이터를 어떤 Java 객체(RangeAggregate, TermsAggregate 등)로 변환할지 정확히 판단하기 위한 정보로 사용됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;이 기능은 6.x 부터 있던 기능이지만, 9.x 는 타입안전성을 지향하기 때문에 내부적으로 typed_keys=true를 기본값으로 강제하여 요청합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;해결 방법 &lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;수십 개에 달하는 각 리포트 파서의 로직을 일일이 수정(예:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;#&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;앞을 잘라내는 로직 추가)하는 것은 비효율적일 뿐만 아니라 휴먼 에러의 위험이 컸습니다. 따라서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;strong&quot;&gt;응답을 JSON으로 변환하는 중앙 통로&lt;/span&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;를 수정하여 기존 형식을 유지하기로 결정했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;모든 ES 요청과 응답 직렬화가 집중되는 코드부분만 수정하기로 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778224999001&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ESClientQueryExecutor.java 내 헬퍼 메소드 추가
private static JsonpMapper createEsJsonpMapper() {
    return new JacksonJsonpMapper()
        .withAttribute(JsonpMapperFeatures.SERIALIZE_TYPED_KEYS, false);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;Java API Client가 응답 객체를 JSON으로 바꿀 때 사용하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;JacksonJsonpMapper&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;에 직렬화 속성을 제어하는 기능을 적용했습니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;SERIALIZE_TYPED_KEYS&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;기능을 비활성화(&lt;/span&gt;false&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;)하도록 설정했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일반적인 검색 요청 처리를 담당하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;restClientInit()과 특정 호스트에 직접 요청을 보내는 일회성&lt;span&gt;&amp;nbsp;&lt;/span&gt;execute()&lt;span&gt;&amp;nbsp;&lt;/span&gt;메소드 양쪽 모두에 커스텀 매퍼를 적용했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;unordered-list&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;기존:&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;new Rest5ClientTransport(restClient, new JacksonJsonpMapper())&lt;/li&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;변경:&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;new Rest5ClientTransport(restClient, createEsJsonpMapper())&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;이 설정을 통해 ES 서버로부터는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;typed_keys&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;가 포함된 응답을 받되, 애플리케이션 내부에서 파싱을 위해 다시 JSON으로 변환할 때는 접두어가 제거된 기존 형식(&lt;/span&gt;date_terms&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;)으로 출력되게 되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;느낀점&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;마이그레이션에 무조건적인 정답은 없다는 것을 다시금 느꼈습니다. 최신 라이브러리의 표준을 무작정 따르는 것보다, 결국&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;우리 프로젝트의 구조를 얼마나 깊이 이해하고 있느냐&lt;/span&gt;가 해결의 핵심이었습니다. 덕분에 수많은 코드를 일일이 수정하며 사이드 이펙트를 키우기보다, 프로젝트의 맥락에 맞춰 영향을 최소화할 수 있는 적절한 해결책을 찾아낼 수 있었습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또한, 이번 과정을 통해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;EOL(공식 지원 종료)을 놓치지 않고 제때 버전 업을 관리하는 것이 얼마나 중요한지&lt;/span&gt;도 절감했습니다. 기술 부채가 쌓이기 전에 미리 대응하는 것이 결국 시스템의 안정성과 개발자의 생산성을 지키는 길임을 배우게 된 값진 경험이었습니다.&lt;/p&gt;</description>
      <category>Backend &amp;middot; Infra</category>
      <author>devhyen</author>
      <guid isPermaLink="true">https://devhyen.tistory.com/51</guid>
      <comments>https://devhyen.tistory.com/51#entry51comment</comments>
      <pubDate>Fri, 8 May 2026 16:28:09 +0900</pubDate>
    </item>
    <item>
      <title>AI 시대의 사이버보안, Anthropic의 &amp;lsquo;Project Glasswing&amp;rsquo;가 의미하는 것</title>
      <link>https://devhyen.tistory.com/50</link>
      <description>&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt; &lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;이 글은 Anthropic의 공식 발표인&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://www.anthropic.com/glasswing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;strong&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Project Glasswing&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;내용을 바탕으로 정리했습니다.&lt;/span&gt; &lt;/i&gt;&lt;i&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;642&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c134m1/dJMcahYivjV/adr5sNzFJKYDhXzaAFdImk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c134m1/dJMcahYivjV/adr5sNzFJKYDhXzaAFdImk/img.png&quot; data-alt=&quot;[Anthropic] Project Glasswing&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c134m1/dJMcahYivjV/adr5sNzFJKYDhXzaAFdImk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc134m1%2FdJMcahYivjV%2Fadr5sNzFJKYDhXzaAFdImk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1207&quot; height=&quot;642&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;642&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[Anthropic] Project Glasswing&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Anthropic이 새로운 사이버보안 프로젝트 &lt;b&gt;&amp;lsquo;Project Glasswing&amp;rsquo;&lt;/b&gt;를 발표했습니다. 이번 프로젝트에는 AWS, Apple, Cisco, Google, Microsoft, NVIDIA, CrowdStrike, JPMorganChase, Linux Foundation, Palo Alto Networks 등 글로벌 기업과 기관들이 함께 참여합니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;목표는 분명합니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;AI의 급격한 발전으로 커지고 있는 사이버 위협에 대응하기 위해, 최첨단 AI를 공격이 아니라 방어에 먼저 활용하자&lt;/span&gt;는 것입니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 발표의 핵심에는 아직 공개되지 않은 AI 모델&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;Claude Mythos Preview&lt;/span&gt;가 있습니다. Anthropic은 이 모델이 소프트웨어 취약점을 찾아내고 이를 악용하는 방법을 고안하는 능력에서, 극소수의 최고 수준 인간 전문가를 제외한 대부분을 능가할 정도의 성능을 보여주고 있다고 설명했습니다. 쉽게 말해, AI가 이제 단순히 코드를 작성하는 수준을 넘어,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;코드의 약점을 스스로 찾아내고 실제 공격 경로까지 설계할 수 있는 단계&lt;/span&gt;에 접어들었다는 의미입니다.&lt;/p&gt;
&lt;hr data-streamdown=&quot;horizontal-rule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;heading-2&quot; data-ke-size=&quot;size20&quot;&gt;왜 지금 사이버보안이 더 중요해졌을까&lt;/h4&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우리가 매일 사용하는 소프트웨어에는 크고 작은 버그가 존재합니다. 대부분은 별문제가 없지만, 일부는 심각한 보안 취약점으로 이어질 수 있습니다. 이런 취약점이 공격자에게 발견되면 시스템이 장악되거나, 서비스가 중단되거나, 민감한 정보가 유출될 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;실제로 사이버공격은 이미 기업 네트워크, 병원, 에너지 시설, 교통 인프라, 정부 기관 등 사회 핵심 영역에 큰 피해를 주고 있습니다. 국가 차원의 공격도 점점 더 빈번해지고 있고, 병원이나 학교 같은 개별 기관을 노린 공격조차 큰 경제적 피해와 안전 문제를 초래할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문제는 이제 AI가 이런 공격의 문턱을 빠르게 낮추고 있다는 점입니다. 원래 소프트웨어 취약점을 찾고 악용하려면 높은 수준의 전문성과 많은 시간이 필요했습니다. 하지만 최신 AI 모델은 코드를 읽고 이해하며, 취약점을 추론하고, 악용 가능한 경로를 찾는 데 점점 더 뛰어난 성능을 보이고 있습니다. 즉,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;공격 역량이 소수 전문가의 영역에서 더 넓은 범위로 확산될 가능성&lt;/span&gt;이 커진 것입니다.&lt;/p&gt;
&lt;hr data-streamdown=&quot;horizontal-rule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;heading-2&quot; data-ke-size=&quot;size20&quot;&gt;Claude Mythos Preview가 보여준 것&lt;/h4&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Anthropic에 따르면, Claude Mythos Preview는 지난 몇 주 동안&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;수천 건의 제로데이 취약점&lt;/span&gt;을 찾아냈습니다. 제로데이란 소프트웨어 개발자도 모르고 있던 미공개 취약점을 뜻합니다. 특히 주요 운영체제와 웹 브라우저, 리눅스 커널 같은 핵심 소프트웨어에서도 심각한 취약점을 발견했다고 밝혔습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;대표 사례도 공개됐습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle; background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;unordered-list&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;OpenBSD에서 27년간 남아 있던 취약점&lt;/span&gt;을 발견했습니다. 이 취약점은 원격에서 시스템을 다운시킬 수 있는 수준이었다고 합니다.&lt;/li&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;FFmpeg에서 16년 된 취약점&lt;/span&gt;도 찾아냈습니다. 이 코드는 자동화 테스트 도구가 500만 번이나 실행했지만 문제를 잡아내지 못한 부분이었다고 합니다.&lt;/li&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;리눅스 커널의 여러 취약점을 연결해&lt;/span&gt;, 일반 사용자 권한에서 시스템 완전 장악 권한으로 올라갈 수 있는 공격 경로도 스스로 찾아냈다고 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Anthropic은 이런 취약점 대부분을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;사람의 별도 개입 없이 모델이 자율적으로 발견했고&lt;/span&gt;, 관련 유지관리자들에게 제보해 현재는 패치가 완료됐다고 설명했습니다.&lt;/p&gt;
&lt;hr data-streamdown=&quot;horizontal-rule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;heading-2&quot; data-ke-size=&quot;size20&quot;&gt;왜 이 발표가 중요한가&lt;/h4&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 발표가 중요한 이유는 단순히 &amp;ldquo;AI가 취약점을 잘 찾는다&amp;rdquo;는 사실 때문만은 아닙니다. 더 중요한 메시지는 이것입니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;앞으로는 AI가 공격자와 방어자 모두에게 강력한 도구가 될 가능성이 매우 크다.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;그리고 지금 필요한 것은, 이 능력이 무분별하게 확산되기 전에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;&lt;b&gt;방어 체계를 먼저 강화&lt;/b&gt;하는 것&lt;/span&gt;입니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Anthropic은 바로 이 지점을 강조하며 Project Glasswing을 시작했다고 설명합니다. 참여 기업들은 Mythos Preview를 활용해 자사의 핵심 시스템과 오픈소스 인프라를 점검하고, 취약점을 더 빨리 찾고 수정하는 데 집중하게 됩니다. 또 Anthropic은 이 과정에서 얻은 교훈을 산업 전반과 공유하겠다고 밝혔습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;즉, 이 프로젝트는 특정 회사만의 기술 시연이 아니라,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;AI 시대에 맞는 새로운 보안 협력 모델을 실험하는 시도&lt;/span&gt;로 볼 수 있습니다.&lt;/p&gt;
&lt;hr data-streamdown=&quot;horizontal-rule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;heading-2&quot; data-ke-size=&quot;size20&quot;&gt;참여 기업들이 본 현실&lt;/h4&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Project Glasswing에 참여한 여러 기업들의 반응도 인상적입니다.&lt;br /&gt;Cisco는 AI 역량이 이제 핵심 인프라 보호의 긴급성을 완전히 바꿔놓았다고 평가했습니다.&lt;br /&gt;AWS는 자사 보안 운영에 Mythos Preview를 시험 적용한 결과, 핵심 코드 보안 강화에 이미 도움을 주고 있다고 밝혔습니다.&lt;br /&gt;Microsoft는 사이버보안이 더 이상 순수하게 인간 역량에만 의존하는 시대가 아니라고 진단했습니다.&lt;br /&gt;CrowdStrike는 취약점이 발견된 뒤 공격에 활용되기까지의 시간이 &amp;ldquo;몇 달에서 몇 분&amp;rdquo; 수준으로 줄어들 수 있다고 경고했습니다.&lt;br /&gt;Linux Foundation은 특히 오픈소스 유지관리자들에게 이런 AI 도구가 큰 힘이 될 수 있다고 강조했습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 메시지들을 종합하면 분명합니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;AI가 사이버보안의 속도와 규모를 바꾸고 있으며, 기존 방식만으로는 대응이 어려워지고 있다&lt;/span&gt;는 것입니다.&lt;/p&gt;
&lt;hr data-streamdown=&quot;horizontal-rule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;heading-2&quot; data-ke-size=&quot;size20&quot;&gt;Anthropic의 계획&lt;/h4&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Anthropic은 Project Glasswing를 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;최대 1억 달러 규모의 모델 사용 크레딧&lt;/span&gt;을 제공하겠다고 밝혔습니다. 여기에 더해 오픈소스 보안 단체를 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;총 400만 달러의 기부금&lt;/span&gt;도 지원합니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트 참여자들은 Claude Mythos Preview를 활용해 다음과 같은 작업을 수행하게 됩니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;unordered-list&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;취약점 탐지&lt;/li&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;바이너리 블랙박스 테스트&lt;/li&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;엔드포인트 보안 강화&lt;/li&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;침투 테스트&lt;/li&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;핵심 시스템의 약점 분석&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Anthropic은 이 프로젝트가 앞으로 수개월 이상 이어질 것이며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;90일 이내에 공개 가능한 결과와 배운 점을 공유하겠다&lt;/span&gt;고 했습니다. 또한 AI 시대에 맞는 새로운 보안 관행, 예를 들어 취약점 공개 절차, 공급망 보안, 패치 자동화, secure-by-design 원칙 등에 대한 실질적인 권고안도 마련할 계획이라고 밝혔습니다.&lt;/p&gt;
&lt;hr data-streamdown=&quot;horizontal-rule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;heading-2&quot; data-ke-size=&quot;size20&quot;&gt;하지만 일반 공개는 하지 않는다&lt;/h4&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;흥미로운 점은 Anthropic이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;Claude Mythos Preview를 일반에 공개할 계획은 없다&lt;/span&gt;고 선을 그었다는 점입니다. 그 이유도 분명합니다. 이 모델이 가진 사이버 역량이 너무 강력해, 충분한 안전장치 없이 널리 배포될 경우 위험이 크다고 판단한 것입니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;대신 Anthropic은 앞으로 더 정교한 보호 장치를 개발해, 향후에는 이런 수준의 모델을 더 안전하게 활용할 수 있는 기반을 마련하겠다고 설명했습니다. 다시 말해,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;능력을 키우는 것만큼이나 통제 장치를 만드는 것이 중요하다&lt;/span&gt;는 메시지입니다.&lt;/p&gt;
&lt;hr data-streamdown=&quot;horizontal-rule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;heading-2&quot; data-ke-size=&quot;size20&quot;&gt;결국 이 발표가 우리에게 말하는 것&lt;/h4&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Project Glasswing은 단순한 제품 발표가 아닙니다.&lt;br /&gt;이 발표는&lt;b&gt; AI가 이제 사이버보안에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;질적으로 다른 국면&lt;/span&gt;을 만들고 있다&lt;/b&gt;는 신호에 가깝습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;앞으로는 취약점을 찾는 일도, 이를 막는 일도, 패치를 적용하는 일도 점점 더 AI 중심으로 재편될 가능성이 높습니다. 공격자 역시 AI를 활용하게 될 것이기 때문에, 방어자들이 뒤처지지 않으려면 지금부터 새로운 도구와 협력 체계를 준비해야 합니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;결국 핵심은 하나입니다.&lt;br /&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;AI는 사이버보안을 더 위험하게 만들 수도 있지만, 동시에 더 안전하게 만들 수 있는 가장 강력한 도구이기도 하다.&lt;/span&gt;&lt;br /&gt;Project Glasswing은 그 힘을 먼저 방어에 쓰기 위한 시도이며, 앞으로 AI와 보안의 관계가 어떻게 재정의될지를 보여주는 중요한 출발점이라고 할 수 있습니다.&lt;/p&gt;
&lt;hr data-streamdown=&quot;horizontal-rule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;heading-1&quot; data-ke-size=&quot;size20&quot;&gt;한줄 정리&lt;/h4&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;Anthropic의 Project Glasswing은 초강력 AI의 취약점 탐지 능력을 방어 목적으로 먼저 활용해, AI 시대의 새로운 사이버보안 표준을 만들려는 시도입니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Computer Science</category>
      <author>devhyen</author>
      <guid isPermaLink="true">https://devhyen.tistory.com/50</guid>
      <comments>https://devhyen.tistory.com/50#entry50comment</comments>
      <pubDate>Thu, 9 Apr 2026 13:59:07 +0900</pubDate>
    </item>
    <item>
      <title>Windows 에서 특정 포트가 안 죽는 것처럼 보일 때 (netstat PID가 죽지 않는 이유)</title>
      <link>https://devhyen.tistory.com/49</link>
      <description>&lt;blockquote data-ke-size=&quot;size16&quot; data-ke-style=&quot;style1&quot;&gt;파이썬 웹서버를 띄우다 보면 가끔 일어나는 일인데 제대로 정리된 블로그 글을 찾기 힘들어서&lt;/blockquote&gt;
&lt;blockquote data-ke-size=&quot;size16&quot; data-ke-style=&quot;style1&quot;&gt;직접 작성했습니다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;상황&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬에서 API서버 (python fastAPI)을 돌리다가 재실행 하려고 하는데 8000포트가 계속 점유된 것 처럼 보였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;netstat으로 확인하면 분명 LISTENING 이 있는데, taskkill은 &quot;프로세스를 찾을 수 없습니다&quot; 라고 뜨는 상황&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;119&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dL0NsQ/dJMcaf6uYmG/TXvrPgPKvJKhknCSw2pvgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dL0NsQ/dJMcaf6uYmG/TXvrPgPKvJKhknCSw2pvgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dL0NsQ/dJMcaf6uYmG/TXvrPgPKvJKhknCSw2pvgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdL0NsQ%2FdJMcaf6uYmG%2FTXvrPgPKvJKhknCSw2pvgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;710&quot; height=&quot;119&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;119&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1769669762197&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;netstat -abno | findstr /i &quot;:8000&quot;
TCP  0.0.0.0:8000  0.0.0.0:0  LISTENING  42424
taskkill /F /T /PID 42424
오류: 프로세스 &quot;42424&quot;을(를) 찾을 수 없습니다.
Get-Process -Id 42424
프로세스를 찾을 수 없습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 해당 8000번 포트는 계속 살아있는 상태이고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버를 재시동 하기 어려운 상황이었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해결방법&lt;/h4&gt;
&lt;pre id=&quot;code_1769669862504&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Get-Process python
# python PID = 22736
taskkill /F /T /PID 22736
성공: PID 22736인 프로세스(PID 42424인 자식 프로세스)가 종료되었습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬의 pid를 확인해서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;taskkill을 시키니 포트가 정상 해제 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;왜 이런일이 생기는가&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;netstat이 보여주는 PID는 그 순간의 스냅샷이다.&lt;/li&gt;
&lt;li&gt;개발 서버(특히 --reload 켠 uvicorn 같은 것)는 부모 프로세스 + 자식 워커 프로세스 구조로 뜨는 경우가 많다.&lt;/li&gt;
&lt;li&gt;그래서 포트를 잡던 &amp;ldquo;자식 PID(42424)&amp;rdquo;가 이미 종료됐거나 바뀌었는데, 내가 그 PID로 죽이려 하면 이미 없는 프로세스라 &amp;ldquo;찾을 수 없음&amp;rdquo;이 뜰 수 있다.&lt;/li&gt;
&lt;li&gt;실제로&amp;nbsp;서버를&amp;nbsp;유지하고&amp;nbsp;다시&amp;nbsp;워커를&amp;nbsp;띄우는&amp;nbsp;&amp;ldquo;부모&amp;nbsp;python(22736)&amp;rdquo;이&amp;nbsp;남아있으면,&amp;nbsp;포트&amp;nbsp;점유가&amp;nbsp;계속되는&amp;nbsp;것처럼&amp;nbsp;느껴진다. &lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;즉, &amp;ldquo;포트가 안 죽는 게 아니라&amp;rdquo;내가&amp;nbsp;죽이려던&amp;nbsp;PID가&amp;nbsp;이미&amp;nbsp;죽었고,&amp;nbsp;진짜&amp;nbsp;주범은&amp;nbsp;다른&amp;nbsp;PID(부모&amp;nbsp;python)였던&amp;nbsp;것.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;TIME_WAIT&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;netstat에 TIME_WAIT가 보이면 포트가 살아있다고 생각할 수 있는데, TIME_WAIT는&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 리슨중이라서 점유하는 상태가 아니라&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료된 TCP 연결이 정리되는 과정이라서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 기다리면 사라진다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발&amp;nbsp;서버에서&amp;nbsp;이런&amp;nbsp;일이&amp;nbsp;잦다면,&amp;nbsp;그냥&amp;nbsp;python&amp;nbsp;프로세스&amp;nbsp;목록을&amp;nbsp;보고&amp;nbsp;&amp;ldquo;내가&amp;nbsp;띄운&amp;nbsp;서버&amp;rdquo;의&amp;nbsp;커맨드라인을&amp;nbsp;확인한&amp;nbsp;뒤&amp;nbsp;종료하는&amp;nbsp;게&amp;nbsp;제일&amp;nbsp;빠르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend &amp;middot; Infra</category>
      <category>특정포트죽이기</category>
      <category>포트안죽을때</category>
      <category>포트죽이기</category>
      <author>devhyen</author>
      <guid isPermaLink="true">https://devhyen.tistory.com/49</guid>
      <comments>https://devhyen.tistory.com/49#entry49comment</comments>
      <pubDate>Thu, 29 Jan 2026 16:00:59 +0900</pubDate>
    </item>
    <item>
      <title>Windows 11 KB5074109(2026-01-13) 정리</title>
      <link>https://devhyen.tistory.com/48</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://support.microsoft.com/ko-kr/topic/2026%EB%85%84-1%EC%9B%94-13%EC%9D%BC-kb5074109-os-%EB%B9%8C%EB%93%9C-26200-7623-%EB%B0%8F-26100-7623-3ec427dd-6fc4-4c32-a471-83504dd081cb&quot;&gt;https://support.microsoft.com/ko-kr/topic/2026%EB%85%84-1%EC%9B%94-13%EC%9D%BC-kb5074109-os-%EB%B9%8C%EB%93%9C-26200-7623-%EB%B0%8F-26100-7623-3ec427dd-6fc4-4c32-a471-83504dd081cb&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1768441862645&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;2026년 1월 13일 &amp;mdash; KB5074109(OS 빌드 26200.7623 및 26100.7623) - Microsoft 지원&quot; data-og-description=&quot;2026년 1월 보안 업데이트부터 Windows Server 2025에는 자체 KB 식별자와 빌드 번호가 있습니다. 이 변경 내용은 Windows Server 2025에만 적용됩니다. Windows 11, 버전 24H2 또는 Windows 11 버전 25H2에 대한 업데이&quot; data-og-host=&quot;support.microsoft.com&quot; data-og-source-url=&quot;https://support.microsoft.com/ko-kr/topic/2026%EB%85%84-1%EC%9B%94-13%EC%9D%BC-kb5074109-os-%EB%B9%8C%EB%93%9C-26200-7623-%EB%B0%8F-26100-7623-3ec427dd-6fc4-4c32-a471-83504dd081cb&quot; data-og-url=&quot;https://support.microsoft.com/ko-kr/topic/2026%EB%85%84-1%EC%9B%94-13%EC%9D%BC-kb5074109-os-%EB%B9%8C%EB%93%9C-26200-7623-%EB%B0%8F-26100-7623-3ec427dd-6fc4-4c32-a471-83504dd081cb&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/mp9Ft/dJMb9ee7n7b/8E0MemIlJQVSDuWmGB19k0/img.gif?width=960&amp;amp;height=540&amp;amp;face=0_0_960_540,https://scrap.kakaocdn.net/dn/fvSNl/dJMb8Z3kFet/dRBQK1l28ESj5YRza3yEB1/img.png?width=594&amp;amp;height=332&amp;amp;face=0_0_594_332,https://scrap.kakaocdn.net/dn/b9DgL7/dJMb8SXre4D/8Sbyfnv8raiyNvWfVy0XN1/img.png?width=594&amp;amp;height=332&amp;amp;face=0_0_594_332&quot;&gt;&lt;a href=&quot;https://support.microsoft.com/ko-kr/topic/2026%EB%85%84-1%EC%9B%94-13%EC%9D%BC-kb5074109-os-%EB%B9%8C%EB%93%9C-26200-7623-%EB%B0%8F-26100-7623-3ec427dd-6fc4-4c32-a471-83504dd081cb&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://support.microsoft.com/ko-kr/topic/2026%EB%85%84-1%EC%9B%94-13%EC%9D%BC-kb5074109-os-%EB%B9%8C%EB%93%9C-26200-7623-%EB%B0%8F-26100-7623-3ec427dd-6fc4-4c32-a471-83504dd081cb&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/mp9Ft/dJMb9ee7n7b/8E0MemIlJQVSDuWmGB19k0/img.gif?width=960&amp;amp;height=540&amp;amp;face=0_0_960_540,https://scrap.kakaocdn.net/dn/fvSNl/dJMb8Z3kFet/dRBQK1l28ESj5YRza3yEB1/img.png?width=594&amp;amp;height=332&amp;amp;face=0_0_594_332,https://scrap.kakaocdn.net/dn/b9DgL7/dJMb8SXre4D/8Sbyfnv8raiyNvWfVy0XN1/img.png?width=594&amp;amp;height=332&amp;amp;face=0_0_594_332');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;2026년 1월 13일 &amp;mdash; KB5074109(OS 빌드 26200.7623 및 26100.7623) - Microsoft 지원&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;2026년 1월 보안 업데이트부터 Windows Server 2025에는 자체 KB 식별자와 빌드 번호가 있습니다. 이 변경 내용은 Windows Server 2025에만 적용됩니다. Windows 11, 버전 24H2 또는 Windows 11 버전 25H2에 대한 업데이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;support.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;- WSL 네트워킹(미러링) 실패, VPN 연결 후 회사 리소스 접근 문제, Azure Virtual Desktop RemoteApp 연결 실패 같은 네트워크 이슈 수정이 포함됩니다.&lt;br /&gt;- NPU 탑재 기기에서 유휴 상태 전력 소모 증가 이슈를 개선합니다(전력/배터리 체감 가능).&lt;br /&gt;- 특정 레거시 모뎀 드라이버가 제거되어, 해당 하드웨어는 더 이상 동작하지 않을 수 있음(특수 케이스 주의).&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;업데이트 체감 가능 변경점&lt;/h3&gt;
&lt;h3 style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;heading-3&quot; data-ke-size=&quot;size23&quot;&gt;(1) WSL 네트워킹(미러링) 문제 수정&lt;/h3&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;공식 문서에 따르면 WSL에서&amp;nbsp;미러링 네트워킹이 실패하면서&lt;br /&gt;호스트에 대한 경로가 없습니다&amp;nbsp;류의 오류가 발생할 수 있는 문제를 수정합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;unordered-list&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;WSL을 개발 환경으로 쓰는 경우(로컬 &amp;harr; 컨테이너/리눅스 툴체인), 네트워크가 꼬이면 체감이 큽니다.&lt;/li&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;&amp;ldquo;갑자기 WSL에서 외부 접근/사내 접근이 안 된다&amp;rdquo; 유형이면 이번 수정의 직접 수혜일 가능성이 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;heading-3&quot; data-ke-size=&quot;size23&quot;&gt;(2) VPN 연결 후 회사 리소스 접근 불가 문제 수정&lt;/h3&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;VPN은 &amp;ldquo;연결은 되는데 사내 리소스(예: Git, 사내 DNS, 프록시, 파일 서버)가 안 붙는&amp;rdquo; 경우가 흔한데, 문서에 이 케이스가 명시돼 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;unordered-list&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;원격근무/사내망 개발환경(사내 패키지 레지스트리, 내부 도메인)을 쓰는 사람에게는 중요 포인트입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;heading-3&quot; data-ke-size=&quot;size23&quot;&gt;(3) Azure Virtual Desktop(AVD) RemoteApp 연결 실패 수정&lt;/h3&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AVD/RemoteApp을 업무용으로 쓰는 조직에서는 &amp;ldquo;업데이트 후 갑자기 RemoteApp이 안 뜸&amp;rdquo;이 가장 위험한 유형인데, 이번에 그 이슈가 수정 항목으로 들어가 있습니다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;heading-3&quot; data-ke-size=&quot;size23&quot;&gt;(4) 전력/배터리: NPU 장치 유휴 전력 이슈 개선&lt;/h3&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Copilot+ PC 같은 NPU 탑재 기기군에서&amp;nbsp;유휴 상태 전력 소모가 커질 수 있는 문제를 해결한다고 안내합니다.&lt;br /&gt;&amp;rarr; 노트북 사용자라면 배터리/발열/팬 동작이 완화될 여지가 있습니다.&lt;/p&gt;</description>
      <category>TIL &amp;middot; 잡담</category>
      <category>26100.7623</category>
      <category>26200.7623</category>
      <category>KB5074109</category>
      <author>devhyen</author>
      <guid isPermaLink="true">https://devhyen.tistory.com/48</guid>
      <comments>https://devhyen.tistory.com/48#entry48comment</comments>
      <pubDate>Thu, 15 Jan 2026 10:55:18 +0900</pubDate>
    </item>
    <item>
      <title>CORS를 제대로 이해하고 안전하게 설정하기</title>
      <link>https://devhyen.tistory.com/47</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;CORS?&amp;nbsp;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;서버 보안 기능이 아니라 브라우저 정책!&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CORS(Cross-Origin Resource Sharing)의 본질은 브라우저의 동일 출처 정책(Same-Origin Policy SOP)을 예외적으로 완화하기 위한 규칙입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;보통 개발자는 &quot;프론트에서 API호출이 실패했다&quot;라고 이해하고, console창에 &quot;blocked by CORS policy&quot;라고 뜨기 때문에, 서버가 막았다고 생각할 수 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 브라우저가 응답을 JS에 전달하지 않은 것인데, 실패 지점이 백엔드처럼 보입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 해당 에러는 Access-Control-Allow-Origin 같은 헤더를 추가하면 문제가 해결되기 때문에 오해할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 CORS는 서버가 막는게 아니라 브라우저가 막는 것이고, 서버는 이 Origin에게는 읽을 수 있게 해도 된다는 허가증(헤더)를 발급할 &lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;뿐이고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;strong&quot;&gt;그 허가증을 확인해서 통과시킬지 말지 결정하는 문지기는 브라우저&lt;/span&gt;&lt;span style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot;&gt;입니다.&lt;/span&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;그렇다면, SOP은 왜 있는걸까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 사이트들이 한 브라우저 안에서 같이 돌아가는 환경이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 다른 사이트에서 사용자가 &lt;b&gt;로그인해둔 사이트의 권한을 가로채지 못하게 격리하기 위해서&lt;/b&gt;입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹은 기본적으로 악성사이트를 방문할 수 있다는 위협 모델이 있습니다. 사용자는 여러 사이트에 로그인한 상태로 웹 서핑을 합니다. 이 때 사용자가 하나라도 악성사이트를 방문하게 되면 그 페이지의 JS가 사용자의 브라우저에서 실행됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SOP가 없다면, 악성사이트의 JS가 사용자의 쿠키/세션이 실린 요청을 보내고 응답내용을 자기의 서버로 빼돌릴 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 다른 출처의 응답을 스크립트가 마음대로 읽지 못하게 해서 데이터의 탈취를 막습니다.&amp;nbsp;&lt;br /&gt;브라우저는 자동 첨부 권한인 쿠키/세션으로 인해 편의성이 있지만, 격리가 없다면 권한위임/탈취의 문제가 됩니다. SOP은 자동권한에 있어서도 안전장치를 제공할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹은 광고/위/서드파티스크립트/여러탭/여러도메인 링크로 자유롭게 이동하는 구조이기 때문에 기본적으로 격리되어야합니다. SOP이 웹 보안의 기본 격리 모델입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇지만, 요즘의 웹앱은 프론트와 API가 나뉘는 경우가 많습니다. 그래서 SOP의 예외 규칙이 필요합니다. 여기서 CORS가 예외규칙입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Origin 판단 기준&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- schema : http/https&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- host : example.com&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- port : 80/443/3000 등&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3가지 모두 같은지로 판단 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;heading-3&quot; data-ke-size=&quot;size23&quot;&gt;내 요청은 왜 두 번 날아갈까? (Preflight와 Simple Request)&lt;/h3&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;개발을 하다 보면 네트워크 탭에 분명 API를 한 번 호출했는데, 요청이 두 번 잡히는 경우를 볼 수 있습니다. 자세히 보면 첫 번째 요청의 메서드가&lt;span&gt;&amp;nbsp;&lt;/span&gt;GET이나&lt;span&gt;&amp;nbsp;&lt;/span&gt;POST가 아니라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;OPTIONS&lt;/span&gt;&lt;/b&gt;로 되어 있죠.&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이것이 바로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;Preflight(예비 요청)&lt;/span&gt;입니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;브라우저는 요청을 크게 두 가지로 나눕니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;1) 단순 요청 (Simple Request)&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;브라우저가 보기에 &quot;이 정도는 안전하다&quot;고 판단하는 요청입니다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;unordered-list&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;메서드가&lt;span&gt;&amp;nbsp;&lt;/span&gt;GET,&lt;span&gt;&amp;nbsp;&lt;/span&gt;POST,&lt;span&gt;&amp;nbsp;&lt;/span&gt;HEAD&lt;span&gt;&amp;nbsp;&lt;/span&gt;중 하나이고,&lt;/li&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;Content-Type이&lt;span&gt;&amp;nbsp;&lt;/span&gt;application/x-www-form-urlencoded,&lt;span&gt;&amp;nbsp;&lt;/span&gt;multipart/form-data,&lt;span&gt;&amp;nbsp;&lt;/span&gt;text/plain&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 기본적인 것들일 때입니다.&lt;/li&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;커스텀 헤더가 없어야 합니다.&lt;/li&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;이때는 예비 검사 없이 서버에 바로 요청을 보냅니다. (물론 SOP 위반 시 응답은 브라우저가 가립니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;2) 예비 요청이 필요한 경우 (Preflight)&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;하지만 요즘 우리는 대부분&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;JSON&lt;/span&gt;(application/json)으로 통신하거나,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Authorization&lt;span&gt;&amp;nbsp;&lt;/span&gt;헤더에 토큰을 실어 보냅니다. 브라우저 입장에서 이건 &quot;단순하지 않은, 서버 데이터에 부작용을 일으킬 수 있는 요청&quot;입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 본 요청을 보내기 전에&lt;span&gt;&amp;nbsp;&lt;/span&gt;OPTIONS&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드로 서버를 먼저 찔러봅니다.&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;blockquote&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;브라우저:&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&quot;나&lt;span&gt;&amp;nbsp;&lt;/span&gt;application/json&lt;span&gt;&amp;nbsp;&lt;/span&gt;데이터 보낼 건데, 받아줄래?&quot; (OPTIONS&lt;span&gt;&amp;nbsp;&lt;/span&gt;요청)&lt;span&gt;&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;서버:&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&quot;어, 그 출처(Origin)라면 허용해.&lt;span&gt;&amp;nbsp;&lt;/span&gt;POST&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드 써도 돼.&quot; (200 OK)&lt;span&gt;&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;span data-streamdown=&quot;strong&quot;&gt;브라우저:&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&quot;확인했어. 이제 진짜 데이터 보낼게.&quot; (본 요청&lt;span&gt;&amp;nbsp;&lt;/span&gt;POST)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #f9fafb; color: #111827; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;CORS 에러는 주로 이 예비 요청 단계에서 서버가 허가증(헤더)을 제대로 주지 않아 발생합니다. 네트워크 탭에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;OPTIONS&lt;span&gt;&amp;nbsp;&lt;/span&gt;요청이 빨간색으로 실패했다면, 서버 설정을 먼저 확인해야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인이 왜 안될까(쿠키가 안넘어감)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CORS 설정을 다 했는데 쿠키가 안넘어갈 수 있는데, 이것도 SOP의 목적과 관련있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 브라우저는 다른 출처로 요청을 보낼 때, 쿠키/인증헤더 같은 민감한 정보는 함부로 담지 않습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해서는 클라이언트와 서버 양쪽에 명시해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 클라이언트 (프론트)&lt;/b&gt; : 요청을 보낼 때 인증정보도 함께 보낸다고 명시해야합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;unordered-list&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;axios의 경우:&lt;span&gt;&amp;nbsp;&lt;/span&gt;withCredentials: true&lt;span&gt;&amp;nbsp;&lt;/span&gt;옵션 필수&lt;/li&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;fetch의 경우:&lt;span&gt;&amp;nbsp;&lt;/span&gt;credentials: 'include'&lt;span&gt;&amp;nbsp;&lt;/span&gt;옵션 필수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 서버 (백엔드)&lt;/b&gt; : 응답 헤더에 인증정보를 받아도 된다고 허락해야합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #f9fafb; color: #111827; text-align: start;&quot; data-streamdown=&quot;unordered-list&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-streamdown=&quot;list-item&quot;&gt;Access-Control-Allow-Credentials: true&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;참고) Credentials를 true로 설정하는 순간,&amp;nbsp;&lt;br /&gt;Access-Control-Allow-Origin 헤더에는 절대로 와일드카드(*)를 쓸 수 없습니다.&lt;br /&gt;이 경우 와일드카드(*) 대신 정확한 출처(Origin)를 명시해서 내려줘야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Access-Control-Allow-Origin: * (모든 곳 허용)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼블릭 API 라면 상관없지만, 민감한 정보를 다루는 서비스라면 매우 위험합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SOP라는 방어막을 서버가 스스로 무력화 시킬 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;서버간 통신이 유리한 이유&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 CORS의 원리와 안전하게 설정하는 법을 알아봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 보안 관점에서 &quot;꼭 브라우저에서 그 API를 직접 호출해야만 할까요?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버끼리는 SOP가 없습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SOP은 브라우저의 보안 정책입니다. 즉, api 서버 -&amp;gt; api 서버를 호출할때는 CORS 검사가 아예 발생하지 않습니다. preflight (두번 호출) 로 인한 네트워크 낭비도 없습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 중요한 이유는 보안 입니다.&amp;nbsp;&lt;br /&gt;만약 프론트엔드에서 직접 타사 API를 호출한다면 필연적으로 API key나 인증 토큰이 브라우저에 노출됩니다. 개발자 도구만 열어도 누구나 그 키를 볼 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 proxy 방식을 사용하면&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 프론트엔드 : 같은 도메인에 요청을 보냅니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 백엔드 : 숨겨진 비밀 키를 사용해 API 서버와 통신합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. response : 받은 데이터만 프론트에 전달합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Admin API나 결제 API처럼 민감한 권한이 필요한경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API key가 외부에 절대 노출되면 안되는경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CORS설정 권한이 없는 외부 서버와 통신해야할 때 는 서버간 통신(proxy)를 권장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;그럼에도 불구하고 CORS를 써야하는 경우&amp;nbsp;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 직접 CDN이나 퍼블릭 API 데이터를 가져와야할 때&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버를 거치지 않고 빠른 속도로 대용량 파일을 다운로드/업로드 할 때&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 CORS는 브라우저의 보안을 잠시 꺼주는 예외 규칙일 뿐입니다. 보안이 중요한 기능이라면 억지로 CORS를 뚫으려고 하기 보다는 백엔드에서 처리하는 서버간 통신을 우선 고려하는 것이 좋습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend &amp;middot; UI</category>
      <category>cors</category>
      <category>Proxy</category>
      <category>보안</category>
      <category>브라우저보안</category>
      <author>devhyen</author>
      <guid isPermaLink="true">https://devhyen.tistory.com/47</guid>
      <comments>https://devhyen.tistory.com/47#entry47comment</comments>
      <pubDate>Mon, 12 Jan 2026 18:44:29 +0900</pubDate>
    </item>
    <item>
      <title>왜 FastAPI는 http로 리다이렉트 될까? Nginx 프록시 환경에서 발생하는 Redirect 문제</title>
      <link>https://devhyen.tistory.com/46</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI + Nginx 조합으로 서비스를 배포할 때 가장 자주 겪는 문제 중 하나가 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;HTTPS 로 접속했는데 왜 HTTP로 리다이렉트 된다.&lt;br /&gt;&quot;/&quot;로 끝나는 경로만 리다이렉트가 발생한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 단순히 서버 한 곳의 문제가 아니라,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI의 URL 정책, Nginx의 프록시 동작, Uvicorn의 proxy 설정 문제가 서로 엇갈리면서 발생하는 현상이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;전체구조 - Nginx는 HTTPS 종료 지점 이다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 사용하는 구조는 대부분 아래와 같다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Jnm0z/dJMcacaANAP/Jn99a9R5ODKuX04I0Qe0ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Jnm0z/dJMcacaANAP/Jn99a9R5ODKuX04I0Qe0ck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Jnm0z/dJMcacaANAP/Jn99a9R5ODKuX04I0Qe0ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJnm0z%2FdJMcacaANAP%2FJn99a9R5ODKuX04I0Qe0ck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트는 HTTPS로 접속하지만, FastAPI는 내부적으로 HTTP로 톧신한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Nginx가 SSL을 종료하고 내부 FastAPI로 요청을 넘겨주는 구조다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;FastAPI는 원래 요청이 HTTPS였는지 알 수 없다.&lt;br /&gt;Nginx가 Forwarded 헤더들을 제대로 전달하지 않으면 FastAPI는&amp;nbsp;&lt;br /&gt;HTTP로 들어왔다고 판단한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 잘못된 redirect를 만드는 첫번째 원인이다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Nginx의 proxy_pass&lt;/h3&gt;
&lt;pre id=&quot;code_1765942931966&quot; class=&quot;awk&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;location /mm/ {
    proxy_pass http://backend:4000/mm/;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 설정은 다음과 같이 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;클라이언트&amp;nbsp;요청&lt;/b&gt; &lt;br /&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;https://example.com/mm/users/list&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Nginx가&amp;nbsp;백엔드로&amp;nbsp;전달하는&amp;nbsp;요청&lt;/b&gt; &lt;br /&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;http://backend:4000/mm/users/list&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지는 문제없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;하지만, FastAPI가 리다이렉트를 발생시키기 시작하면 상황이 달라진다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;FastAPI는 왜 redirect를 만드는가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI(Starlette)는 URL 끝 슬래시(/) 를 엄청 중요하게 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 라우트를 이렇게 정의했다면&lt;/p&gt;
&lt;pre id=&quot;code_1765943174445&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.get(&quot;/mm/user/list/&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트는 이렇게 요청하면&lt;/p&gt;
&lt;pre id=&quot;code_1765943197777&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/mm/user/list&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 바로 이렇게 판단한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;슬래시가 빠졌네, 원래 URL은 /list/ 다 &lt;br /&gt;&lt;/span&gt;➡️ 307 Temporary Redirect 발생&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대 케이스도 마찬가지이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, FastAPI는 라우트의 슬래시와 요청 슬래시가 다르면 무조건 redirect를 발생시킨다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; 참고용 프레임워크 기본 동작 리다이렉트 여부&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 47.2093%;&quot;&gt;프레임 워크&lt;/td&gt;
&lt;td style=&quot;width: 52.7907%;&quot;&gt;기본 동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 47.2093%;&quot;&gt;FastAPI&lt;/td&gt;
&lt;td style=&quot;width: 52.7907%;&quot;&gt;자동 리다이렉트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 47.2093%;&quot;&gt;Django&lt;/td&gt;
&lt;td style=&quot;width: 52.7907%;&quot;&gt;자동 리다이렉트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 47.2093%;&quot;&gt;Spring Boot&lt;/td&gt;
&lt;td style=&quot;width: 52.7907%;&quot;&gt;리다이렉트 안 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 47.2093%;&quot;&gt;NestJS&lt;/td&gt;
&lt;td style=&quot;width: 52.7907%;&quot;&gt;설정에 따라 다름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 47.2093%;&quot;&gt;Express.js&lt;/td&gt;
&lt;td style=&quot;width: 52.7907%;&quot;&gt;리다이렉트 안 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 47.2093%;&quot;&gt;Flask&lt;/td&gt;
&lt;td style=&quot;width: 52.7907%;&quot;&gt;리다이렉트 안 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 47.2093%;&quot;&gt;ASP.NET Core&lt;/td&gt;
&lt;td style=&quot;width: 52.7907%;&quot;&gt;리다이렉트 안 함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리다이렉트를 안하는 경우에는 정확히 일치하는 경로만 매칭되고, 그렇지 않으면 404를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1704&quot; data-start=&quot;1658&quot; data-ke-size=&quot;size16&quot;&gt;따라서 슬래시 정책이 맞지 않으면 &lt;b&gt;redirect는 무조건 한 번 발생&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-end=&quot;1742&quot; data-start=&quot;1706&quot; data-ke-size=&quot;size16&quot;&gt;문제는 그 redirect URL이 엉망이 될 수 있다는 점이다.&lt;/p&gt;
&lt;h3 data-end=&quot;1742&quot; data-start=&quot;1706&quot; data-ke-size=&quot;size23&quot;&gt;FastAPI가 redirect URL을 HTTPS ➡️ HTTP 로 잘못되게 만드는 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 redirect Location을 만들 때 다음 값을 참고한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Host&lt;/li&gt;
&lt;li&gt;Scheme(http/https)&lt;/li&gt;
&lt;li&gt;Port&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Nginx가 이것을 제대로 전달하지 않으면 https를 http로 redirectLocation이 만들어진다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 브라우저로 그대로 전달되면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Mixed Content 정책 위배 ! !&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1765943969267&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Mixed Content: The page at 'https://example.com' was loaded over HTTPS, 
but requested an insecure resource 'http://example.com/api'. 
This request has been blocked; the content must be served over HTTPS.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 된다...&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;uvicorn에서 --proxy-headers 옵션&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx는 원래 요청 정보를 FastAPI에 알려주려고 이런 헤더를 넣는다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1765944072052&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;X-Forwarded-Proto: https
X-Forwarded-Host: example.com
X-Forwarded-Port: 443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 문제는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Uvicorn은 기본적으로 이 헤더들을 신뢰하지 않는다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안 때문인데, 해커가 헤더를 조작할 수도 있기 때문이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 FastAPI에게 믿어도 된다는 옵션을 줘야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 옵션이&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1765950246068&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;--proxy-headers&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결방법&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 슬래시(/) 정책 통일&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우트와 프론트 호출 URL을 둘 다 /path 또는 둘 다 /path/ 로 맞추기&lt;br /&gt;- 일반적으로 REST API 관례 상 '/path' 트레일링 슬래시 (Trailing Slash) 없음 으로 진행해야합니다.&lt;br /&gt;- 예외&amp;nbsp;&lt;br /&gt;&amp;nbsp; 1. &lt;b&gt;디렉토리/컬렉션을 명시적으로 표현할 때&lt;/b&gt;: `/static/images/`, `/api/users/` (컬렉션) &lt;br /&gt;&amp;nbsp;&amp;nbsp;2. &lt;b&gt;루트 경로&lt;/b&gt;: `/` (HTTP 요청에서 경로 부분이 비어있거나 `/`인 경우)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.&amp;nbsp; Nginx에서 헤더 정확히 넘기기&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1765951757442&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. uvicorn에서 proxy headers&amp;nbsp; 활성화&lt;/h4&gt;
&lt;pre id=&quot;code_1765951783386&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uvicorn main:app --host 0.0.0.0 --port 4000 --proxy-headers&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI + Nginx 환경에서 HTTPS 가 HTTP로 튀는 문제는&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 FastAPI의 슬래시 redirect + Nginx의 헤더 처리 + uvicorn proxy 설정이 서로 맞지 않아 발생하는 현상이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세 가지를 올바르게 잡아주면 redirect 문제는 해결된다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend &amp;middot; Infra</category>
      <category>API</category>
      <category>FastAPI</category>
      <category>https redirect</category>
      <category>nginx</category>
      <category>proxy header</category>
      <category>reverse proxy</category>
      <category>SSL</category>
      <category>trailing slash</category>
      <category>uvicorn</category>
      <category>web server configuration</category>
      <author>devhyen</author>
      <guid isPermaLink="true">https://devhyen.tistory.com/46</guid>
      <comments>https://devhyen.tistory.com/46#entry46comment</comments>
      <pubDate>Wed, 17 Dec 2025 15:12:42 +0900</pubDate>
    </item>
    <item>
      <title>Ai가 브라우저 보는 눈  : Cursor에서 Browser Tools MCP 사용하는 방법 (윈도우 + WSL)</title>
      <link>https://devhyen.tistory.com/45</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;781&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYGlr9/dJMb9MiB1eo/xjmDoaQWKxKLPw5BFIbeY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYGlr9/dJMb9MiB1eo/xjmDoaQWKxKLPw5BFIbeY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYGlr9/dJMb9MiB1eo/xjmDoaQWKxKLPw5BFIbeY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYGlr9%2FdJMb9MiB1eo%2FxjmDoaQWKxKLPw5BFIbeY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;383&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;781&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Browser tools mcp 는 AI가 실제 브라우저와 연결되어 웹 페이지의 행동 데이터를 직접 수집 이해할 수 있게 해주는 도구입니다.&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;장점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 콘솔로그 네트워크 요청 스크린샷 등을 자동으로 캡쳐해서 디버깅이나 테스트에 활용 가능&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. AI가 실제 브라우저 동작을 관찰하므로, 코드만이 아니라 실행 결과까지 분석 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 웹 페이지 UI변화나 오류를 시각적으로 파악할 수 있어 QA, 프론트엔드 디버깅에 유용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 로컬 테스트 자동화에 적합.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. AI가 &lt;b&gt;웹을 직접 보는 눈&lt;/b&gt;   을 갖게 되어 코드와 화면을 함께 이해하는 개발 지원 가능.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용방법&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;브라우저 툴 MCP를 cursor AI에서 사용하기 위해서는 &lt;br /&gt;1. 크롬 익스텐션 &lt;br /&gt;2. 커서 연동 &lt;br /&gt;3. 서버 실행 &lt;br /&gt;의 단계가 필요합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;크롬 익스텐션 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. &lt;a title=&quot;browser-tools-mcp 깃허브&quot; href=&quot;https://github.com/AgentDeskAI/browser-tools-mcp&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/AgentDeskAI/browser-tools-mcp&lt;/a&gt; 에서 git clone을 받습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;461&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KmVn8/dJMb9kzyJxA/3qx3MhcohOONK6agbhlCQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KmVn8/dJMb9kzyJxA/3qx3MhcohOONK6agbhlCQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KmVn8/dJMb9kzyJxA/3qx3MhcohOONK6agbhlCQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKmVn8%2FdJMb9kzyJxA%2F3qx3MhcohOONK6agbhlCQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;948&quot; height=&quot;461&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;461&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1761261211221&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git clone https://github.com/AgentDeskAI/browser-tools-mcp.git&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.&amp;nbsp;&lt;a&gt;chrome://extensions/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfUhKS/dJMb83EAguj/43VqPDtcFiMA8d5u2GrtpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfUhKS/dJMb83EAguj/43VqPDtcFiMA8d5u2GrtpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfUhKS/dJMb83EAguj/43VqPDtcFiMA8d5u2GrtpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfUhKS%2FdJMb83EAguj%2F43VqPDtcFiMA8d5u2GrtpK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1917&quot; height=&quot;184&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크롬 익스텐션 설정 페이지에서 개발자 모드를 키고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;압축해제된 확장 프로그램 로드를 눌러서 방금 받은 chrome-extension 폴더를 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3(선택).&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;698&quot; data-origin-height=&quot;203&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOAsx3/dJMb83EAgur/9GwdOHuWqeFdizJgZYkXr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOAsx3/dJMb83EAgur/9GwdOHuWqeFdizJgZYkXr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOAsx3/dJMb83EAgur/9GwdOHuWqeFdizJgZYkXr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOAsx3%2FdJMb83EAgur%2F9GwdOHuWqeFdizJgZYkXr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;203&quot; data-origin-width=&quot;698&quot; data-origin-height=&quot;203&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size16&quot;&gt;보안을 위해 세부정보 &amp;gt; 사이트 액세스 &amp;gt; 로컬호스트만 추가합니다.&lt;/p&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size20&quot;&gt;커서 연동&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;311&quot; data-origin-height=&quot;56&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BNmjb/dJMb9NV7i0Y/yKg0LjRKmHXt9kIct4AZXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BNmjb/dJMb9NV7i0Y/yKg0LjRKmHXt9kIct4AZXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BNmjb/dJMb9NV7i0Y/yKg0LjRKmHXt9kIct4AZXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBNmjb%2FdJMb9NV7i0Y%2FyKg0LjRKmHXt9kIct4AZXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;311&quot; height=&quot;56&quot; data-origin-width=&quot;311&quot; data-origin-height=&quot;56&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size16&quot;&gt;커서 우측 상단에 있는 톱니바퀴 아이콘 클릭하면 설정창입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;953&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clL8B2/dJMb9NV7i02/cGxtMtzAQzE9PyXKoRgCKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clL8B2/dJMb9NV7i02/cGxtMtzAQzE9PyXKoRgCKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clL8B2/dJMb9NV7i02/cGxtMtzAQzE9PyXKoRgCKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclL8B2%2FdJMb9NV7i02%2FcGxtMtzAQzE9PyXKoRgCKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;953&quot; height=&quot;388&quot; data-origin-width=&quot;953&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.&lt;/p&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size16&quot;&gt;Tools&amp;amp;MCP &amp;gt; New MCP Server를 누르면 mcp.json이 뜨는데 아래 코드를 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1761261382830&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;mcpServers&quot;: {
    &quot;browser-tools&quot;: {
      &quot;command&quot;: &quot;npx.cmd&quot;,
      &quot;args&quot;: [&quot;-y&quot;, &quot;@agentdeskai/browser-tools-mcp@latest&quot;]
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size16&quot;&gt;WSL(Ubuntu)환경에서의 MCP 설정&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size16&quot;&gt;윈도우에서는 위 설정으로 잘 되었지만, wsl ubuntu에서는 cursor가 mcp를 인식하지 못했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size16&quot;&gt;방법을 찾아서 했더니 성공했고 공유합니다.&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WSL 내부의 Node.js 경로를 Cursor가 직접 인식 못하는 경우가 있습니다. 이럴 땐&lt;b&gt; mcp.json에서 절대경로로 Node 실행 경로를 지정&lt;/b&gt;해야합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1761263989094&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;mcpServers&quot;: {
    &quot;browser-tools&quot;: {
      &quot;command&quot;: &quot;/home/hyen/.nvm/versions/node/v22.21.0/bin/npx&quot;,
      &quot;args&quot;: [&quot;-y&quot;, &quot;@agentdeskai/browser-tools-mcp@latest&quot;]
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 경로는 본인의 wsl 에서 &lt;b&gt;which npx&lt;/b&gt; 를 통해 알라낸 경로로 수정해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node를 nvm으로 설치한 경우, 경로는 /home/사용자명/.nvm/versions/node/... 형태로 설정됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;mcp 서버 실행&lt;/h4&gt;
&lt;pre id=&quot;code_1761264176159&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npx @agentdeskai/browser-tools-server@latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 거치면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;758&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4X52U/dJMb9Xxz7we/6tgWjbWyqHdQwPSt4axZ5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4X52U/dJMb9Xxz7we/6tgWjbWyqHdQwPSt4axZ5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4X52U/dJMb9Xxz7we/6tgWjbWyqHdQwPSt4axZ5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4X52U%2FdJMb9Xxz7we%2F6tgWjbWyqHdQwPSt4axZ5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1072&quot; height=&quot;758&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;758&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크롬 개발자모드 (F12)에서 BrowserToolsMCP가 잘 연결되어 있음을 볼 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend &amp;middot; Infra</category>
      <category>browsertools</category>
      <category>cursor</category>
      <category>MCP</category>
      <category>브라우저mcp</category>
      <category>크롬</category>
      <author>devhyen</author>
      <guid isPermaLink="true">https://devhyen.tistory.com/45</guid>
      <comments>https://devhyen.tistory.com/45#entry45comment</comments>
      <pubDate>Fri, 24 Oct 2025 09:04:25 +0900</pubDate>
    </item>
    <item>
      <title>사내 개발 스터디 : 첫 세미나 발표 회고</title>
      <link>https://devhyen.tistory.com/44</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devhyen.tistory.com/38&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.07.04 - [회고] - 사내 개발 스터디를 만들었습니다&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1754398886424&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;사내 개발 스터디를 만들었습니다&quot; data-og-description=&quot;회사에서 개발자들과 기술 이야기를 나누는 방법 크래프톤정글과 팀메이트 활동을 하며 서로 다른 회사를 다니고, 서로 다른 분야의 개발을 하는 사람들이지만개발자들과 기술 이야기를 나눌 &quot; data-og-host=&quot;devhyen.tistory.com&quot; data-og-source-url=&quot;https://devhyen.tistory.com/38&quot; data-og-url=&quot;https://devhyen.tistory.com/38&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bB5Mhu/hyZrAapQvY/jNkaiozrlRRoY9yRb4U6G0/img.png?width=616&amp;amp;height=454&amp;amp;face=0_0_616_454,https://scrap.kakaocdn.net/dn/bKbkJ3/hyZqRcnOKi/ODHKw5BbxtS0a5SxHWjYC1/img.png?width=616&amp;amp;height=454&amp;amp;face=0_0_616_454,https://scrap.kakaocdn.net/dn/u8TT0/hyZuHMGqLl/LV6Reqn2I0EsTODIlTwURK/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://devhyen.tistory.com/38&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devhyen.tistory.com/38&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bB5Mhu/hyZrAapQvY/jNkaiozrlRRoY9yRb4U6G0/img.png?width=616&amp;amp;height=454&amp;amp;face=0_0_616_454,https://scrap.kakaocdn.net/dn/bKbkJ3/hyZqRcnOKi/ODHKw5BbxtS0a5SxHWjYC1/img.png?width=616&amp;amp;height=454&amp;amp;face=0_0_616_454,https://scrap.kakaocdn.net/dn/u8TT0/hyZuHMGqLl/LV6Reqn2I0EsTODIlTwURK/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;사내 개발 스터디를 만들었습니다&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;회사에서 개발자들과 기술 이야기를 나누는 방법 크래프톤정글과 팀메이트 활동을 하며 서로 다른 회사를 다니고, 서로 다른 분야의 개발을 하는 사람들이지만개발자들과 기술 이야기를 나눌&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devhyen.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사내 개발 스터디를 만든 후&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 세미나 발표를 끝냈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기획&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두의 퇴근 후 시간이 소중하기에, 6시부터 7시까지 한 시간으로 짧고 굵게 준비했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세미나는 매달 말 진행하기로 했고, 이번엔 동호회 창립 후 첫 세미나였기에, 서로의 일정을 조율하다 보니 7월 세미나가 8월 초에 진행되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 날짜를 고정으로 진행하게 될 듯 하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;월간 세미나에서는 3명의 발표자를 자원받는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다행히 첫 세미나지만 3명(나포함)이 자원했고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 시간이라는 제한된 시간 때문에 발표 시간이 다소 타이트하게 배분되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발표가 끝난 후&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빠른 투표와 시상식을 한 후 기념하기위한 단체 사진 촬영을하고 마무리하기로 했다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IBJio/btsPJBowtgs/MmRwjZDxMjUs3lqC58ClMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IBJio/btsPJBowtgs/MmRwjZDxMjUs3lqC58ClMk/img.png&quot; data-origin-width=&quot;1587&quot; data-origin-height=&quot;2245&quot; data-is-animation=&quot;false&quot; data-filename=&quot;1.png&quot; width=&quot;430&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IBJio/btsPJBowtgs/MmRwjZDxMjUs3lqC58ClMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIBJio%2FbtsPJBowtgs%2FMmRwjZDxMjUs3lqC58ClMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1587&quot; height=&quot;2245&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rPwAe/btsPHFMwQdI/el0zcA6sjRIv5UWmoQIRlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rPwAe/btsPHFMwQdI/el0zcA6sjRIv5UWmoQIRlk/img.png&quot; width=&quot;430&quot; data-filename=&quot;blob&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;2245&quot; data-origin-width=&quot;1587&quot; data-widthpercent=&quot;50&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rPwAe/btsPHFMwQdI/el0zcA6sjRIv5UWmoQIRlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrPwAe%2FbtsPHFMwQdI%2Fel0zcA6sjRIv5UWmoQIRlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1587&quot; height=&quot;2245&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;canva로 예쁘게 포스터와 타임테이블도 만들었다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;세미나 준비&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영진들은 미리 모여 포스터도 붙이고, 화면에 자료를 띄우는 등 준비를 했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동호회를 처음 만들 때, 운영진을 아무도 지원하지 않을까봐 걱정했는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감사하게도 부회장님, 기획님, 총무님 같은 분들이 자발적으로 나서주셔서 감사했다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;1700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXHWcE/btsPJyrOQdS/IremikKQJapHss5eGsgu41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXHWcE/btsPJyrOQdS/IremikKQJapHss5eGsgu41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXHWcE/btsPJyrOQdS/IremikKQJapHss5eGsgu41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXHWcE%2FbtsPJyrOQdS%2FIremikKQJapHss5eGsgu41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;200&quot; height=&quot;447&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;1700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;공주들이 준비중인 우리를 사진도 찍어줬다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;발표&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1411&quot; data-origin-height=&quot;1058&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGzrRY/btsPF0KnUtB/OvKGJ8cbMhH4aIw0W0ITkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGzrRY/btsPF0KnUtB/OvKGJ8cbMhH4aIw0W0ITkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGzrRY/btsPF0KnUtB/OvKGJ8cbMhH4aIw0W0ITkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGzrRY%2FbtsPF0KnUtB%2FOvKGJ8cbMhH4aIw0W0ITkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1411&quot; height=&quot;1058&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1411&quot; data-origin-height=&quot;1058&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 발표는 내가 했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 토이프로젝트를 만드는 방법에 대해서 설명했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부담갖지 않고 웹 사이트를 만들기 위해서 next.js + vercel + firebase로 쉽게 만들수 있음을 설명드렸고&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두가 경청해주셔서 감사한 마음이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째 발표는&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에 비개발직군이신 분이신데 놀랍게도 퇴근후 밤에는 VR게임을 개발하시는 분의 이야기이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩을 잘 하는 법에 대해 설명해주셨는데, 실무에 바로 활용할 수 있게끔 설명을 잘 해주셔서 큰 도움이 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, VR게임에 대해 잘 몰랐는데 어떤식으로 개발해야하는지를 이야기해주셔서 흥미로웠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세번째 발표는&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커와 쿠버네티스의 기초이야기였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드개발을 하다보면 도커를 필연적으로 사용하게 된다. 특히 현재 회사의 서비스들은 각각 도커에 올라가는 구조라서 쿠버네티스와 도커를 사용하고 있는데, 역시 데브옵스팀이셔서 전문가가 설명해주시는건 정말 도움이 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에 또 기회가 되면 그 다음 과정으로 해주시면 좋을 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;1913&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkBsW4/btsPJ1tPqgN/02jMY5sAZKK5AmY6SnSi00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkBsW4/btsPJ1tPqgN/02jMY5sAZKK5AmY6SnSi00/img.png&quot; data-alt=&quot;최고 최고&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkBsW4/btsPJ1tPqgN/02jMY5sAZKK5AmY6SnSi00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkBsW4%2FbtsPJ1tPqgN%2F02jMY5sAZKK5AmY6SnSi00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;319&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;1913&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;최고 최고&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;아쉬운점&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간이 정말 촉박했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각자 15~20분정도 시간분배는 발표하기엔 너무 짧은 시간임을 깨달았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음부터는 발표 시간을 20~30분으로 넉넉하게 잡고, 총 진행 시간을 2시간으로 늘려서 Q&amp;amp;A와 가벼운 네트워킹 시간을 더 여유롭게 가졌으면 좋겠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추후에 운영진회의를 통해 좀 더 좋은 세미나를 기획해보려고 한다!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배가 너무 고팠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퇴근 후 저녁 시간이라는 점을 간과했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음엔 회비로 간식도 좀 구비하면 좋겠다는 생각이 들었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외에 모든 건 좋았던 세미나였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에는 시간도 넉넉하게 짜고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두의 소중한 시간을 더 값진 시간을 보낼 수 있게 기획해야겠다.&amp;nbsp;&lt;/p&gt;</description>
      <category>회고</category>
      <author>devhyen</author>
      <guid isPermaLink="true">https://devhyen.tistory.com/44</guid>
      <comments>https://devhyen.tistory.com/44#entry44comment</comments>
      <pubDate>Tue, 5 Aug 2025 22:31:49 +0900</pubDate>
    </item>
    <item>
      <title>PKCE란? OAuth2 인증을 안전하게 만드는 핵심 보안 메커니즘</title>
      <link>https://devhyen.tistory.com/43</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;PKCE란?&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;proof key for code exchange&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;OAuth2&lt;/b&gt;에서 &lt;b&gt;Authorization code flow&lt;/b&gt; 를 더 안전하게 만들기 위해 고안된 보안 메커니즘&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Authorization Code Flow 의 문제점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Authorization Code Flow는&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;앱이 사용자에게 로그인 페이지를 열고&lt;/li&gt;
&lt;li&gt;로그인 후 code를 redirect로 전달받고&lt;/li&gt;
&lt;li&gt;그 code를 가지고 서버에서 토큰을 받아오는 구조&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;rarr; 이 과정에서&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;code가 노출되면 해커가 가로채서 토큰을 받아갈 수 있음&lt;/li&gt;
&lt;li&gt;모바일 앱이나 SPA는 client_secret을 숨기기 어려움&lt;/li&gt;
&lt;li&gt;브라우저 히스토리, 네트워크 로그 등에서 탈취 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;어떻게 PKCE가 해결 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱이 인증 요청할 때 &quot;나는 이 인증 코드를 요청한 진짜 앱이다&quot; 라는 증명(Proof)를 같이 보낸다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 38px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 23.4884%; height: 21px;&quot;&gt;code_verifier&lt;/td&gt;
&lt;td style=&quot;width: 76.5116%; height: 21px;&quot;&gt;앱이 만든 랜덤 문자열 (증명본 원본)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.4884%; height: 17px;&quot;&gt;code_challenge&lt;/td&gt;
&lt;td style=&quot;width: 76.5116%; height: 17px;&quot;&gt;code_verifier를 SHA256으로 해시한 값 (서버로 전달)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;효율적인 포스팅, 시작하세요! (1).jpg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAbRiq/btsPtVVs2mm/9rhKbnQL8t4bDggRMeluW0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAbRiq/btsPtVVs2mm/9rhKbnQL8t4bDggRMeluW0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAbRiq/btsPtVVs2mm/9rhKbnQL8t4bDggRMeluW0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAbRiq%2FbtsPtVVs2mm%2F9rhKbnQL8t4bDggRMeluW0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1080&quot; height=&quot;1080&quot; data-filename=&quot;효율적인 포스팅, 시작하세요! (1).jpg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위&amp;nbsp;도식은&amp;nbsp;PKCE&amp;nbsp;(Proof&amp;nbsp;Key&amp;nbsp;for&amp;nbsp;Code&amp;nbsp;Exchange)를&amp;nbsp;사용하는&amp;nbsp;OAuth2&amp;nbsp;Authorization&amp;nbsp;Code&amp;nbsp;Flow의&amp;nbsp;전체&amp;nbsp;과정을&amp;nbsp;정리한&amp;nbsp;것입니다. &lt;br /&gt;&lt;br /&gt;모바일 앱이나 SPA 같은 공개 클라이언트 환경에서는 client_secret을 안전하게 보관하기 어렵기 때문에, PKCE를 통해 code를 요청한 주체가 토큰 요청도 같은 주체라는 것을 증명하게 됩니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클라이언트는 먼저 &lt;b&gt;code_verifier를 생성&lt;/b&gt;하고, &lt;b&gt;이를 기반으로 code_challenge를 계산해 Authorization Server(Hydra)에 인증 요청&lt;/b&gt;을 보냅니다.&lt;/li&gt;
&lt;li&gt;&amp;nbsp;Authorization Server는 사용자가 로그인하지 않은 경우, 로그인 UI를 제공하는 Identity Provider(예: Ory Kratos)로 &lt;b&gt;redirect&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;&amp;nbsp;사용자가 로그인하면, &lt;b&gt;Hydra는 code를 발급하고 클라이언트의 redirect_uri로 전달&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;클라이언트는 받은&lt;b&gt; code와 함께 code_verifier를 사용해 토큰을 요청&lt;/b&gt;하며, 서버는 이 값을 &lt;b&gt;검증&lt;/b&gt;하여&lt;b&gt; access token 또는 ID token을 발급&lt;/b&gt;합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이&amp;nbsp;과정에서&amp;nbsp;state&amp;nbsp;파라미터는&amp;nbsp;CSRF&amp;nbsp;공격을&amp;nbsp;방지하기&amp;nbsp;위해&amp;nbsp;사용되며,&amp;nbsp;클라이언트가&amp;nbsp;임의의&amp;nbsp;값을&amp;nbsp;설정하여&amp;nbsp;요청마다&amp;nbsp;다르게&amp;nbsp;유지하는&amp;nbsp;것이&amp;nbsp;권장됩니다. &lt;br /&gt;&lt;br /&gt;PKCE는&amp;nbsp;별도의&amp;nbsp;client&amp;nbsp;secret&amp;nbsp;없이도&amp;nbsp;안전하게&amp;nbsp;인증&amp;nbsp;흐름을&amp;nbsp;구성할&amp;nbsp;수&amp;nbsp;있게&amp;nbsp;해&amp;nbsp;주며,&amp;nbsp;특히&amp;nbsp;모바일/SPA&amp;nbsp;환경에서는&amp;nbsp;사실상&amp;nbsp;필수적인&amp;nbsp;보안&amp;nbsp;구성입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;PKCE 구현 시 주의할 점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;code_challenge_method&lt;/span&gt;는 반드시 &lt;b&gt;`S256`(SHA256)&lt;/b&gt;을 쓰는 게 보안상 안전하며, 대부분의 Authorization Server는 이 방법만 지원합니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;code_verifier&lt;/span&gt;는 클라이언트가 임의로 만든 값이며, 길이는 43~128자 사이의 랜덤 문자열이 좋습니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;redirect_uri&lt;/span&gt;는 사전에 Authorization Server에 등록된 값과 정확히 일치해야 합니다. 특히 모바일 앱의 딥링크는 scheme까지 포함해 등록해야 합니다.&lt;/li&gt;
&lt;li&gt;CSRF 방지를 위해 항상 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;state&lt;/span&gt;&amp;nbsp;파라미터를 설정하고, 응답 시 값이 일치하는지 확인해야 합니다.&lt;/li&gt;
&lt;li&gt;토큰 요청 시 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;client_secret&lt;/span&gt;은 사용하지 않습니다 (&lt;span style=&quot;background-color: #f6e199;&quot;&gt;token_endpoint_auth_method=none&lt;/span&gt;). &lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Backend &amp;middot; Infra</category>
      <category>Hydra</category>
      <category>IAM</category>
      <category>OAuth2</category>
      <category>ory</category>
      <category>pkce</category>
      <category>보안</category>
      <category>인증</category>
      <author>devhyen</author>
      <guid isPermaLink="true">https://devhyen.tistory.com/43</guid>
      <comments>https://devhyen.tistory.com/43#entry43comment</comments>
      <pubDate>Tue, 22 Jul 2025 14:05:38 +0900</pubDate>
    </item>
  </channel>
</rss>