<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>이나당</title>
    <link>https://h-owo-ld.tistory.com/</link>
    <description>농담곰 덕질하는 FE ╰(*&amp;deg;▽&amp;deg;*)╯

https://www.github.com/Ina-dang</description>
    <language>ko</language>
    <pubDate>Tue, 17 Mar 2026 02:22:01 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>이나당</managingEditor>
    <image>
      <title>이나당</title>
      <url>https://tistory1.daumcdn.net/tistory/5061156/attach/cb8079222f1b4c12a36b4b7b82908c89</url>
      <link>https://h-owo-ld.tistory.com</link>
    </image>
    <item>
      <title>감자 개발자의 2025년 회고</title>
      <link>https://h-owo-ld.tistory.com/373</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;491&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bP6LzT/btsORTrU7cM/gwWNB9ayfIST77IXWOgzy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bP6LzT/btsORTrU7cM/gwWNB9ayfIST77IXWOgzy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bP6LzT/btsORTrU7cM/gwWNB9ayfIST77IXWOgzy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbP6LzT%2FbtsORTrU7cM%2FgwWNB9ayfIST77IXWOgzy0%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;491&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;491&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2022년 11월 1일 현재 회사에 입사 후 드디어 3년이 지났다. 2025년에 경험한 것들을 정리하고 앞으로의 계획을 다시 새겨보는 시간을 가져보려한다.&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;2025 타임라인&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;상반기&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2024년 이어서 진행중인 프로젝트&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;- 한방병원 알림톡&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;병원측 사정으로 인해 테스트가 밀려서 오픈도 미뤄졌다. 오픈이 미뤄지면서 요구사항등이 자꾸자꾸 나오고, 예외상황들도 더 많아지고 기존에 없던 케이스들도 추가하게되어서 테스트가 더 오래 걸렸다. 그래도 막상 오픈하고나니 오픈 후 첫 달 정도만 로그분석 들어오고 이후에는 운영이슈 0% 기록중. 사용량은 많은데 이슈가 없어서 사용자들이 편하게 쓰고 있다고 생각하니 매우 뿌듯했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;케이스가 복잡한 시나리오가 많아서 README.md랑 JSDoc을 엄청 꼼꼼하게 달아두고 커밋내용도 육하원칙으로 쓰려고 신경을 많이 썼던 프로젝트&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;size18&quot;&gt;&lt;b&gt;- EMR 태블릿 앱&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래는 4분기에 개발하고 소스 자체를 인수인계 하기로 한 프로젝트인데, 그 업체에서 운영인력을 구하지못해서 6개월 유지보수 추가 요청을 주셨다. (추가개발도 ㅎ) 회사에서 사용해보지 않던 기능들을 만들어 볼 수 있어서 좋았다. 탭구성이나 화면 형태 외의 html을 만들어서 PDF로 저장하기도하고 구글 북마크처럼 즐겨찾기기능도 만들어보고 재밌었다. 마지막 인수인계때는 회사에서 그동안 문서를 그렇게까지 많이 해본적이 없는데 업체에서 요청한 완료보고 문서들이 많아서 조금 허덕였지만 그래도 무사히 마무리-&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 업체 본부장님이 인수인계끝나고 회식하면서 '그동안 외주할 때 이렇게 쫑파티를 한적이 없는데 정말 잘해줘서 식사자리 마련했다. 본인들의 작업에 자랑스러워 해도된다.'라고 말해주셔서 진짜 ㄹㅇ 완전 감동이었다. 이런 말 한마디마디에 또 에너지를 얻고 일하게 되는것같다. :D&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;b&gt;새로운 프로젝트&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;- 한방병원 식수개발 기획&lt;/b&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;갑분기. 갑자기 통합관리시스템(환자, 식수, 진료예약, 물리치료 등), 키오스크, 알림톡 기획(!!!!!!!)을 맡게되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 그 병원은 수기, 또는 스프레드시트로 식사와 치료, 그리고 상담예약을 관리하고 있고 CS나 환자관리는 각각의 개인 솔루션업체를 사용하고 있어 원무에서 한 환자를 볼때 4-5개의 창을 봐야하는 불편함이 있다고 하셨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 부분을 최대한 1-2개로 줄일 수 있도록 관리시스템을 구축하면서 가장 급한 식수(병원식)관리 키오스크와 알림톡 기획을 같이 맡게되었다. 담당하는 원무선생님이 매우 의욕적이어서 figma도 공부하면서 기획 ppt를 만들었다. 하지만 이후에 아래의 고도화 앱에 투입되면서 기획은 여기까지만 하고 다른분에게 넘어가게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이후에는 팔로업을 가-끔 도와주는 정도로만 관여하게되었고 중간에 해당 프로젝트 담당하던 사람 한 명이 이탈이 있어서 오픈일정에 차질도 생기고 남아있는 팀원이 진짜 힘들어했다. 기능을 3차까지 나눠서 오픈하기로 변경되었는데, 부디 3차까지 잘 마무리 되기를 ...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;- 기존 사용중인 병원 앱 고도화 및 장차법 적용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 사용중인 앱 중 하나를 고도화 하고싶다는 의뢰가 들어왔다. 고도화를 하면서 디자인도 새로 개편하고 장차법(장애인차별금지법)적용을 원하셨다. 그래서 기존 기능개선 + 고대비 + 글씨크기에 따른 화면 조절 기능을 추가하게 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 이렇게 장차법만 추가였긴한데, 결국 지금(6월 말)은 + 입원수속알림톡, 주차현황 등등등 막 늘어나고 있긴하다 :D ㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 네이티브(Java+Kotlin &amp;amp; Swift), 스프링부트(Java) 로 만들어진 프로젝트를 이번엔 네이티브(Kotlin &amp;amp; Swift)와 리액트(Javascript)로 고도화를 진행하고있다.&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;하지만 이전에는 수납기능이 없고 대부분 전산 + 백엔드와 주고받는 api 기능들이 전부라서 문제가 없었는데.. 외부 업체 연동이 끼니까 이슈가 생기긴해서 네이티브 수정이 좀 있긴 했다. (코쓱) 해당 부분은 별도의 포스팅으로 정리 해두었다。&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1758716240393&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;[React&amp;amp;Kotlin] 웹-네이티브 브리지를 활용한 PG 결제 연동 구조 설계기 - 안드로이드 편&quot; data-og-description=&quot;문제의 시작 현재 회사에서 안드로이드 앱들은 MainActivity에서 WebView를 활용해 React로 개발된 웹 화면을 네이티브 앱 내에 띄워서 표시하고 있다. 이전까지는 대부분 외부업체와의 연동이 없고 병&quot; data-og-host=&quot;h-owo-ld.tistory.com&quot; data-og-source-url=&quot;https://h-owo-ld.tistory.com/370&quot; data-og-url=&quot;https://h-owo-ld.tistory.com/370&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/k91d7/hyZJCkYeh5/4L1UMMXEDHvKPO1IbEYKz0/img.gif?width=426&amp;amp;height=640&amp;amp;face=0_0_426_640,https://scrap.kakaocdn.net/dn/D95jo/hyZJPp7x0o/hJt7KlJ80LOfmJTaW8PsLk/img.gif?width=426&amp;amp;height=640&amp;amp;face=0_0_426_640,https://scrap.kakaocdn.net/dn/bHKktZ/hyZJQCzjan/t517ew7145UrKefUQ8W7j0/img.png?width=918&amp;amp;height=1332&amp;amp;face=0_0_918_1332&quot;&gt;&lt;a href=&quot;https://h-owo-ld.tistory.com/370&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://h-owo-ld.tistory.com/370&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/k91d7/hyZJCkYeh5/4L1UMMXEDHvKPO1IbEYKz0/img.gif?width=426&amp;amp;height=640&amp;amp;face=0_0_426_640,https://scrap.kakaocdn.net/dn/D95jo/hyZJPp7x0o/hJt7KlJ80LOfmJTaW8PsLk/img.gif?width=426&amp;amp;height=640&amp;amp;face=0_0_426_640,https://scrap.kakaocdn.net/dn/bHKktZ/hyZJQCzjan/t517ew7145UrKefUQ8W7j0/img.png?width=918&amp;amp;height=1332&amp;amp;face=0_0_918_1332');&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;[React&amp;amp;Kotlin] 웹-네이티브 브리지를 활용한 PG 결제 연동 구조 설계기 - 안드로이드 편&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;문제의 시작 현재 회사에서 안드로이드 앱들은 MainActivity에서 WebView를 활용해 React로 개발된 웹 화면을 네이티브 앱 내에 띄워서 표시하고 있다. 이전까지는 대부분 외부업체와의 연동이 없고 병&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;h-owo-ld.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1758716255503&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;[React&amp;amp;iOS] 웹-네이티브 브리지를 활용한 PG 결제 연동 구조 설계기 - iOS 편&quot; data-og-description=&quot;문제의 시작현재 회사에서 안드로이드 앱들은 MainActivity에서 WebView를 활용해 웹 화면을 네이티브 앱 내에 띄워서 표시하고 있다. 이전까지는 대부분 외부 연동이 없는 조회 형태라서 특별한 문&quot; data-og-host=&quot;h-owo-ld.tistory.com&quot; data-og-source-url=&quot;https://h-owo-ld.tistory.com/371&quot; data-og-url=&quot;https://h-owo-ld.tistory.com/371&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/W3ulf/hyZJn9Ot5j/W3MxlKwnIoWtMAebL8Dq50/img.gif?width=426&amp;amp;height=640&amp;amp;face=0_0_426_640,https://scrap.kakaocdn.net/dn/Yu9wn/hyZJQP64O3/jfMk3NyTNcT60em3B02MF0/img.gif?width=426&amp;amp;height=640&amp;amp;face=0_0_426_640,https://scrap.kakaocdn.net/dn/cwTlG3/hyZJrqP4aT/Q5CjIGkMPIxOMI7BrY9Tf1/img.png?width=2978&amp;amp;height=1412&amp;amp;face=0_0_2978_1412&quot;&gt;&lt;a href=&quot;https://h-owo-ld.tistory.com/371&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://h-owo-ld.tistory.com/371&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/W3ulf/hyZJn9Ot5j/W3MxlKwnIoWtMAebL8Dq50/img.gif?width=426&amp;amp;height=640&amp;amp;face=0_0_426_640,https://scrap.kakaocdn.net/dn/Yu9wn/hyZJQP64O3/jfMk3NyTNcT60em3B02MF0/img.gif?width=426&amp;amp;height=640&amp;amp;face=0_0_426_640,https://scrap.kakaocdn.net/dn/cwTlG3/hyZJrqP4aT/Q5CjIGkMPIxOMI7BrY9Tf1/img.png?width=2978&amp;amp;height=1412&amp;amp;face=0_0_2978_1412');&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;[React&amp;amp;iOS] 웹-네이티브 브리지를 활용한 PG 결제 연동 구조 설계기 - iOS 편&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;문제의 시작현재 회사에서 안드로이드 앱들은 MainActivity에서 WebView를 활용해 웹 화면을 네이티브 앱 내에 띄워서 표시하고 있다. 이전까지는 대부분 외부 연동이 없는 조회 형태라서 특별한 문&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;h-owo-ld.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;하반기&amp;nbsp;&lt;/h3&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;7월부터 갑자기 매달 병이 터졌다 이석증에 코로나에 감기에 심재성2도화상에 디스크손상에 눈이상에.. 진짜 온몸이 난리가 나서 실비가 없었으면 저축1도못하고 그냥 가난뱅이 될뻔&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignLeft&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;010&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/010.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/010.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 기존 사용중인 병원 알림톡페이지 고도화 및 추가개발 &amp;amp; 외래최적화 LBS(위치기반서비스) 연동지원&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상반기 병원앱 고도화 및 장차법 적용을 끝내고 해당 프로젝트의 연장선으로&amp;nbsp; 기존에 서비스중인 기능 2개 페이지를 최신 알림톡 페이지로 UI를 바꿔주고, 추가 기능들을 넣었다. 알림톡페이지는 다른 병원들에서 많이 했던거라 크게 어렵지 않을 걸로 예상되는데 그중에 마음에 걸리는거라면 다른 알림톡 서비스 업체와 연계해서 공동으로 API를 맞춰야 하는부분이 매끄럽기를 바라는 마음!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외래최적화 LBS는 비콘 연계 서비스로 네이티브에서 해당 서비스를 활성화 해서 병원을 이용하는동안 자동으로 위치에 맞게 푸시알림을 보내주는 서비스다. 해당 부분에서 네이티브 연동 및 푸시알림 종류에 따라 앱 내에 화면이동까지 지원을 해달라는 요청이 있어서 이것도 네이티브랑 웹뷰 모두 수정이 들어갈 예정. 아직 기능이 명확하게 나오지 않아서 대기중이지만 수정된 푸시알림 연동규격을 통해서 네이티브 브릿지로 웹뷰 알림팝업을 띄워주고 앱내 페이지 이동까지 기능구현이 가능한거는 확인완료 한 상태!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;11월까지 LBS오픈과 알림톡을 마무리하고 10월에 새로 들어오신 경력직 팀원분에게 인수인계를 했다.&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;- 새로운 팀원의 등장&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10월 추석이 지나고 새로운 팀원이 들어왔다. 개발기간은 3-4년 정도인 중니어 개발자분이시고 이것저것 해보고 오셨다. 하지만 우리팀은 정말 &quot;이것저것 개발팀&quot;이기 때문에 적응하는데 조금 힘들어하셨다. 이전 회사는 깃도 커밋하려면 상사에게 컨펌받고 서버반영도 해주는분이 따로 있고 QA에 CS에 다 나눠져있는 회사였다하시는데,...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리회사는 모든걸 스스로 해야하기때문에... 게다가 개발서버도 없는 병원도 많구.. 처음적응엔 힘들어하셨지만 12월말인 지금은 꽤나 적응하셔서 반영도 문서보고 척척 해내고 있으시당. 다행쓰&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;- Goodbye 나의 PTSD&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심리상담을 받았다. 서울시 청년 마음지원사업에 선발돼서 상담 6회 지원이었는데, 상담과정중에 상담사분께서 필요한경우 10회까지 연장할 수 있는데 나의 경우 10회까지 연장하면 좋겠다고 해주셔서 총 10회기를 상담받게되었다.&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;상담중에 과거 학교다닐때 당했던 따돌림 상황이랑 전팀장이 가스라이팅 하는상황이 오버랩돼서 PTSD 재경험이 생긴것 같다고 하셨다. 학창시절때 이야기를 웃으면서 할 수 있어서 극복했다. 괜찮다. 라고 생각했는데 아무래도 어릴때 있었던 일은 커서도 꽤나 영향을 지속적으로 주는가보다. 무튼 상담덕분에 PTSD라는것도 인지하고 치료도 받고 다양한 상황에서 대처할 수 있는 방법들도 알게되었다. 담당해주신 상담사분은 인지치료 위주로 진행해주신 것 같았다. 상담받으면서 '음 그게 어떤이유에서 나당씨한테 중요한 부분일까요?'라는 질문이라던지 내 신체반응이나 그때의 상황탐색 그리고 메타인지를 할 수 있게 많이 도와주셨고 상담이 끝난 지금 엄청 많이 해소되어서 회사에서 내 역할에 대한 강박도 많이 줄어들어서 마음이 편해졌다. 우울이 너무 깊은상태라서 아무것도 안하고 싶고 해야하는데 의욕이 없어서 더 스트레스 받는 굴레에 갇혀있었는데 상담사분이 우울이 사라지면서 알아서 원래 기질대로 하고싶은 충동들이나 욕구들이 올라올거라 해주셨는데, 연말인 지금은 해보고 싶은것들이 조오금씩 생각나고 있어서 (회고도 갑자기 생각남 ㅎ) 내년이 기대되게됐다. 앞으로도 어린시절의 환경들이 영향을 줄테지만 그런 상황에서도 나를 지키기위해 꾸준히 숲을보고 메타인지를 하는 시간을 가지라고 조언해주셨다.&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;- 벌써 줄서고있는 2026업무와 회사이사&lt;/b&gt;&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;435&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EASl1/dJMcafyqBm2/GQNe9pVgA2fkJqh4WtKNq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EASl1/dJMcafyqBm2/GQNe9pVgA2fkJqh4WtKNq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EASl1/dJMcafyqBm2/GQNe9pVgA2fkJqh4WtKNq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEASl1%2FdJMcafyqBm2%2FGQNe9pVgA2fkJqh4WtKNq0%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;435&quot; height=&quot;308&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;435&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현팀장님이랑 업무 정리중인데 벌써 2026년 업무가 그득그득 찼다는게 어이가 없&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;&lt;b&gt;- 조용히 올라가고 있는 OTT JUMP의 사용자&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;243&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r1vuJ/dJMcadggneC/M1ll2Xf4WhaJMeq0H1Rbqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r1vuJ/dJMcadggneC/M1ll2Xf4WhaJMeq0H1Rbqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r1vuJ/dJMcadggneC/M1ll2Xf4WhaJMeq0H1Rbqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr1vuJ%2FdJMcadggneC%2FM1ll2Xf4WhaJMeq0H1Rbqk%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;912&quot; height=&quot;243&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;243&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;그래도 꾸준히 사용자가 올라가고 있다.. (감덩)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류문의 메일이 안오고 있어서.. 다들 잘 쓰고 있낭..? 싶기도하고.. 다행이기도하고.. 다들 편하게 썼으면 &amp;lt;3&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년 생활이 지나고 전팀장 퇴사와 기막힌 타이밍의 심리상담 덕분에 2026년은 좀더 밝은 분위기로 보낼거라는 기대가 생겨서.. 2026의 회고는 어떨지 궁금하다.&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 style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;내년 회고는 좀더 새롭고 즐거운 이야기들이 많아서 스크롤이 왕창 생겼으면 좋겠다!&lt;/b&gt;&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;niniz&quot; data-emoticon-name=&quot;004&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/004.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/004.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>일상/일기장</category>
      <author>이나당</author>
      <guid isPermaLink="true">https://h-owo-ld.tistory.com/373</guid>
      <comments>https://h-owo-ld.tistory.com/373#entry373comment</comments>
      <pubDate>Tue, 23 Dec 2025 21:06:07 +0900</pubDate>
    </item>
    <item>
      <title>[OTT JUMP] 크롬 웹 스토어 개발자 등록하기 &amp;amp; OTT JUMP 웹스토어 등록 완료!</title>
      <link>https://h-owo-ld.tistory.com/372</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;혼자 잘 쓰고 있었는데, 무니도 웨이브 자동 넘기기가 필요하대서 드디어 으쌰으쌰 하면서 크롬 웹스토어 개발자 등록을 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;추가하는김에 네이버멤버십중이라 넷플릭스도 호다닥!&amp;nbsp;&lt;/span&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;figure id=&quot;og_1755518321138&quot; style=&quot;color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-og-image=&quot;&quot; data-og-url=&quot;https://accounts.google.com/v3/signin/identifier?continue=https%3A%2F%2Fchrome.google.com%2Fwebstore%2Fdevconsole%2Fregister%3Fhl%3Dko&amp;amp;followup=https%3A%2F%2Fchrome.google.com%2Fwebstore%2Fdevconsole%2Fregister%3Fhl%3Dko&amp;amp;hl=ko&amp;amp;ifkv=AdBytiPYDVS3osyBly967GB0uxlFBc5AgK6tjyRJS4EDXZQe0u-z1wfmuJ0q83GmaD63gN0ZpOA8&amp;amp;passive=1209600&amp;amp;service=chromewebstore&amp;amp;flowName=WebLiteSignIn&amp;amp;flowEntry=ServiceLogin&amp;amp;dsh=S822248425%3A1755517060023929&quot; data-og-source-url=&quot;https://chrome.google.com/webstore/devconsole/register?hl=ko&quot; data-og-host=&quot;accounts.google.com&quot; data-og-description=&quot;로그인 Chrome 웹 스토어로 이동&quot; data-og-title=&quot;Chrome Web Store&quot; data-og-type=&quot;website&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://chrome.google.com/webstore/devconsole/register?hl=ko&quot; data-source-url=&quot;https://chrome.google.com/webstore/devconsole/register?hl=ko&quot;&gt;&lt;br /&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Chrome Web Store&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;로그인 Chrome 웹 스토어로 이동&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;accounts.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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;일단 크롬 웹스토어를 접속&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1071&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tZ4zY/btsPUIQADCy/2WkHvZH6YZkjLBM86wZkzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tZ4zY/btsPUIQADCy/2WkHvZH6YZkjLBM86wZkzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tZ4zY/btsPUIQADCy/2WkHvZH6YZkjLBM86wZkzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtZ4zY%2FbtsPUIQADCy%2F2WkHvZH6YZkjLBM86wZkzk%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;1071&quot; height=&quot;580&quot; data-origin-width=&quot;1071&quot; data-origin-height=&quot;580&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;접속하면 돈을 내라고 한다.&amp;nbsp;&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;등록비용은 $5&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;한국이 없어서 그냥 미국으로 하고 구글 우편번호 입력했따 ㅇ_ㅇ&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;&amp;nbsp;&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;&amp;nbsp;&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;&amp;nbsp;&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&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/HVIZh/btsPUX010xh/vSXKnTP3ZEiPVrorbSQ7Xk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HVIZh/btsPUX010xh/vSXKnTP3ZEiPVrorbSQ7Xk/img.png&quot; data-origin-width=&quot;1215&quot; data-origin-height=&quot;746&quot; data-is-animation=&quot;false&quot; data-filename=&quot;blob&quot; style=&quot;width: 50.4436%; margin-right: 10px;&quot; data-widthpercent=&quot;51.04&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HVIZh/btsPUX010xh/vSXKnTP3ZEiPVrorbSQ7Xk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHVIZh%2FbtsPUX010xh%2FvSXKnTP3ZEiPVrorbSQ7Xk%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;1215&quot; height=&quot;746&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PmOhS/btsPTP93Vs2/dhKSbN7L7g9mYvKrBokqlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PmOhS/btsPTP93Vs2/dhKSbN7L7g9mYvKrBokqlK/img.png&quot; data-origin-width=&quot;1225&quot; data-origin-height=&quot;784&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.3936%;&quot; data-widthpercent=&quot;48.96&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PmOhS/btsPTP93Vs2/dhKSbN7L7g9mYvKrBokqlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPmOhS%2FbtsPTP93Vs2%2FdhKSbN7L7g9mYvKrBokqlK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1225&quot; height=&quot;784&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;돈내고 들어온 첫화면!&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;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;&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;536&quot; data-origin-height=&quot;387&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caUDwW/btsPWii0Yp4/jY0KHRcpOKesYPWjPlbzz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caUDwW/btsPWii0Yp4/jY0KHRcpOKesYPWjPlbzz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caUDwW/btsPWii0Yp4/jY0KHRcpOKesYPWjPlbzz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaUDwW%2FbtsPWii0Yp4%2FjY0KHRcpOKesYPWjPlbzz0%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;536&quot; height=&quot;387&quot; data-origin-width=&quot;536&quot; data-origin-height=&quot;387&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;manifest.json에 경로를 ./을 넣었더니 저렇게 오류가 떴다. 경로에 ./ 를 지울것!&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;&amp;nbsp;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1227&quot; data-origin-height=&quot;1273&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mQiBA/btsPURsKzUx/8ckKUw99KyZ3G559FQxhLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mQiBA/btsPURsKzUx/8ckKUw99KyZ3G559FQxhLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mQiBA/btsPURsKzUx/8ckKUw99KyZ3G559FQxhLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmQiBA%2FbtsPURsKzUx%2F8ckKUw99KyZ3G559FQxhLk%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;1227&quot; height=&quot;1273&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1227&quot; data-origin-height=&quot;1273&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;&amp;nbsp;&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;그러면 '초안 저장' 버튼 왼쪽에 '왜 제출할 수 없나요?'라는 텍스트가 생기는데 이걸 누르면&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;&amp;nbsp;&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;&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;915&quot; data-origin-height=&quot;701&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3SoQG/btsPUdXbNQl/z9eUlpgoKV9uW65sh1w711/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3SoQG/btsPUdXbNQl/z9eUlpgoKV9uW65sh1w711/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3SoQG/btsPUdXbNQl/z9eUlpgoKV9uW65sh1w711/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3SoQG%2FbtsPUdXbNQl%2Fz9eUlpgoKV9uW65sh1w711%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;915&quot; height=&quot;701&quot; data-origin-width=&quot;915&quot; data-origin-height=&quot;701&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;채워야할 부분들을 설명해준다.&amp;nbsp;&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;&amp;nbsp;&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;&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;1179&quot; data-origin-height=&quot;614&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdu1qH/btsPWYkl8ob/tJtjCPWx55tptifzKkg35K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdu1qH/btsPWYkl8ob/tJtjCPWx55tptifzKkg35K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdu1qH/btsPWYkl8ob/tJtjCPWx55tptifzKkg35K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbdu1qH%2FbtsPWYkl8ob%2FtJtjCPWx55tptifzKkg35K%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;1179&quot; height=&quot;614&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;614&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;&amp;nbsp;&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;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&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/9i3P7/btsPVlmPULG/A6jNtCdavjSX80A413Qytk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9i3P7/btsPVlmPULG/A6jNtCdavjSX80A413Qytk/img.png&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;513&quot; data-is-animation=&quot;false&quot; style=&quot;width: 36.8191%; margin-right: 10px;&quot; data-widthpercent=&quot;37.25&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9i3P7/btsPVlmPULG/A6jNtCdavjSX80A413Qytk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9i3P7%2FbtsPVlmPULG%2FA6jNtCdavjSX80A413Qytk%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;719&quot; height=&quot;513&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpJuIx/btsPXXSt3L5/WvwFuqcxvhLMNWlj3qsADk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpJuIx/btsPXXSt3L5/WvwFuqcxvhLMNWlj3qsADk/img.png&quot; data-origin-width=&quot;602&quot; data-origin-height=&quot;255&quot; data-is-animation=&quot;false&quot; style=&quot;width: 62.0181%;&quot; data-widthpercent=&quot;62.75&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpJuIx/btsPXXSt3L5/WvwFuqcxvhLMNWlj3qsADk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpJuIx%2FbtsPXXSt3L5%2FWvwFuqcxvhLMNWlj3qsADk%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;602&quot; height=&quot;255&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;앱배포때도 항상 자동배포를 안하고 있으므로&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;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;&amp;nbsp;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;183&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d580AJ/btsPViQ5Ieu/bjEfKSkftH97yREmMlEZ5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d580AJ/btsPViQ5Ieu/bjEfKSkftH97yREmMlEZ5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d580AJ/btsPViQ5Ieu/bjEfKSkftH97yREmMlEZ5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd580AJ%2FbtsPViQ5Ieu%2FbjEfKSkftH97yREmMlEZ5K%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;938&quot; height=&quot;183&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;183&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;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;검토 대기 중 (25.08.18)&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;&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;893&quot; data-origin-height=&quot;621&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dBRgOP/btsQc30AGpy/vcyfPkHyH9UeNMe10eJuGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dBRgOP/btsQc30AGpy/vcyfPkHyH9UeNMe10eJuGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dBRgOP/btsQc30AGpy/vcyfPkHyH9UeNMe10eJuGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdBRgOP%2FbtsQc30AGpy%2FvcyfPkHyH9UeNMe10eJuGK%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;893&quot; height=&quot;621&quot; data-origin-width=&quot;893&quot; data-origin-height=&quot;621&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;25.08.30&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;p style=&quot;text-align: center;&quot; 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;448&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u7rYe/btsQdmFGW48/jJAWezK8R8bQwKQlevOM8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u7rYe/btsQdmFGW48/jJAWezK8R8bQwKQlevOM8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u7rYe/btsQdmFGW48/jJAWezK8R8bQwKQlevOM8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu7rYe%2FbtsQdmFGW48%2FjJAWezK8R8bQwKQlevOM8k%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;448&quot; height=&quot;223&quot; data-origin-width=&quot;448&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;814&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dQvQ1Y/btsQfAWYaLo/3EvRnAXfKkWoVVuZn9y0f1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dQvQ1Y/btsQfAWYaLo/3EvRnAXfKkWoVVuZn9y0f1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dQvQ1Y/btsQfAWYaLo/3EvRnAXfKkWoVVuZn9y0f1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdQvQ1Y%2FbtsQfAWYaLo%2F3EvRnAXfKkWoVVuZn9y0f1%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;1240&quot; height=&quot;814&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;814&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;figure id=&quot;og_1756553469366&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;OTT JUMP - Chrome 웹 스토어&quot; data-og-description=&quot;OTT 오프닝 건너뛰기랑 다음회차를 자동으로 재생해주는 크롬 확장 프로그램이에요! 현재는 웨이브와 티빙, 그리고 넷플릭스가 가능합니다.&quot; data-og-host=&quot;chromewebstore.google.com&quot; data-og-source-url=&quot;https://chromewebstore.google.com/detail/ott-jump/bfinfconkbehonefppgjgimmeichocig?authuser=0&amp;amp;hl=ko&quot; data-og-url=&quot;https://chromewebstore.google.com/detail/ott-jump/bfinfconkbehonefppgjgimmeichocig&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/epMG7A/hyZGkDEOUm/xMzBHWA5hkINUhrbCvSkr0/img.jpg?width=128&amp;amp;height=128&amp;amp;face=0_0_128_128&quot;&gt;&lt;a href=&quot;https://chromewebstore.google.com/detail/ott-jump/bfinfconkbehonefppgjgimmeichocig?authuser=0&amp;amp;hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://chromewebstore.google.com/detail/ott-jump/bfinfconkbehonefppgjgimmeichocig?authuser=0&amp;amp;hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/epMG7A/hyZGkDEOUm/xMzBHWA5hkINUhrbCvSkr0/img.jpg?width=128&amp;amp;height=128&amp;amp;face=0_0_128_128');&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;OTT JUMP - Chrome 웹 스토어&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;OTT 오프닝 건너뛰기랑 다음회차를 자동으로 재생해주는 크롬 확장 프로그램이에요! 현재는 웨이브와 티빙, 그리고 넷플릭스가 가능합니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;chromewebstore.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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>개발/Inafolio</category>
      <author>이나당</author>
      <guid isPermaLink="true">https://h-owo-ld.tistory.com/372</guid>
      <comments>https://h-owo-ld.tistory.com/372#entry372comment</comments>
      <pubDate>Sat, 30 Aug 2025 18:28:25 +0900</pubDate>
    </item>
    <item>
      <title>[React&amp;amp;iOS] 웹-네이티브 브리지를 활용한 PG 결제 연동 구조 설계기 - iOS 편</title>
      <link>https://h-owo-ld.tistory.com/371</link>
      <description>&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;문제의 시작&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QSaXn/btsON4ZZSVR/L7lidnGu2gXxta3LT63gok/tfile.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QSaXn/btsON4ZZSVR/L7lidnGu2gXxta3LT63gok/tfile.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QSaXn/btsON4ZZSVR/L7lidnGu2gXxta3LT63gok/tfile.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/QSaXn/btsON4ZZSVR/L7lidnGu2gXxta3LT63gok/tfile.gif&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;426&quot; height=&quot;640&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;640&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;현재 회사에서 안드로이드 앱들은 MainActivity에서 WebView를 활용해 웹 화면을 네이티브 앱 내에 띄워서 표시하고 있다. 이전까지는 대부분 외부 연동이 없는 조회 형태라서 특별한 문제가 없었다. 하지만 이번에 결제기능이 추가되면서.. 문제가생겼다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;결제 수단을 선택하고 그대로 프로세스를 진행하지않고 뒤로가기버튼을 선택한 경우, 앱내 웹뷰로 다시 돌아오지 않았다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&amp;nbsp;&lt;/span&gt;&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/H2zjT/btsPF6IUAkX/9EimCDkfX2lsK6Iq6HLWZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H2zjT/btsPF6IUAkX/9EimCDkfX2lsK6Iq6HLWZ1/img.png&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;824&quot; data-is-animation=&quot;false&quot; style=&quot;width: 55.591%; margin-right: 10px;&quot; data-widthpercent=&quot;56.25&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H2zjT/btsPF6IUAkX/9EimCDkfX2lsK6Iq6HLWZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH2zjT%2FbtsPF6IUAkX%2F9EimCDkfX2lsK6Iq6HLWZ1%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;730&quot; height=&quot;824&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TgzI3/btsPEqaJOlN/wULdwD7BzQMyoJU6NPpbFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TgzI3/btsPEqaJOlN/wULdwD7BzQMyoJU6NPpbFK/img.png&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;1332&quot; data-is-animation=&quot;false&quot; style=&quot;width: 43.2462%;&quot; data-widthpercent=&quot;43.75&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TgzI3/btsPEqaJOlN/wULdwD7BzQMyoJU6NPpbFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTgzI3%2FbtsPEqaJOlN%2FwULdwD7BzQMyoJU6NPpbFK%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;918&quot; height=&quot;1332&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;사담을 좀 하자면.. 최근 회사 상황이 꽤나 정신없다. 기존 팀장님께서 퇴사를 선언하신 뒤 사실상 칩거 상태에 들어가셨고(ㅠ) 이후 월초에 새로 채용된 팀장님도 하루 만에 퇴사를 결정하면서 팀장 자리는 공석이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;현재 팀 구성은 총 4명으로 나, 입사 동기, 2년 차 백엔드 개발자, 신입 프론트엔드 개발자 이렇게만 남아 있는 상황이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;그렇기에 팀장님이 없는 상태에서 의사결정이나 기술 논의가 쉽지 않았다. 특히 문제 해결 과정에서 누군가에게 조언을 받기는 어려운 상황에서 입사 동기와 이야기하던 중 두 가지 해결 방향이 나왔지만 각각 단점이 있어 쉽게 결정할 수 없었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;그러다 문득 평소 자주보는 오픈카톡방이 떠올랐고 고민 끝에 두 군데에 상황을 올려보았다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;그 중 한 방에서 비슷한 이슈를 겪고 해결한 분의 조언을 받을 수 있었고, 정말 큰 도움이 되었다. (갓...ㅠㅠ)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;조언을 바탕으로 적용한 해결책은 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;개선방법&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;Android 개선방법&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;안드로이드에서는 MainWebView 외 결제 수단용으로 별도의 자식 WebView(PGWebViewActivity)를 하나 더 만들어 결제요청 시 자식 WebView를 띄우고 뒤로가기 버튼을 누르면 해당 WebView를 닫아 다시 부모 WebView로 돌아올 수 있도록 구성했다. 이 방식으로 팝업 결제 페이지의 흐름을 안정적으로 처리할 수 있었다. 또한 뒤로가기 뿐 아니라 승인까지 진행하는 프로세스 결과도 MainActivity에서 전달받을 수 있는 구조로 개선했다.&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1754040955212&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;[React&amp;amp;Kotlin] 웹-네이티브 브리지를 활용한 PG 결제 연동 구조 설계기 - 안드로이드 편&quot; data-og-description=&quot;문제의 시작 현재 회사에서 안드로이드 앱들은 MainActivity에서 WebView를 활용해 React로 개발된 웹 화면을 네이티브 앱 내에 띄워서 표시하고 있다. 이전까지는 대부분 외부업체와의 연동이 없고 병&quot; data-og-host=&quot;h-owo-ld.tistory.com&quot; data-og-source-url=&quot;https://h-owo-ld.tistory.com/370&quot; data-og-url=&quot;https://h-owo-ld.tistory.com/370&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bhhRby/hyZqYPAcEU/Wn30bxAMVk74Dk8Z1PrpbK/img.gif?width=426&amp;amp;height=640&amp;amp;face=0_0_426_640,https://scrap.kakaocdn.net/dn/FJsIa/hyZqQqssmU/o1qswQRQ40YW2Cz5v3jDVk/img.gif?width=426&amp;amp;height=640&amp;amp;face=0_0_426_640,https://scrap.kakaocdn.net/dn/TkVfh/hyZuzHlSCB/4YCmftOptsAF10VZPF2TK1/img.png?width=918&amp;amp;height=1332&amp;amp;face=0_0_918_1332&quot;&gt;&lt;a href=&quot;https://h-owo-ld.tistory.com/370&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://h-owo-ld.tistory.com/370&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bhhRby/hyZqYPAcEU/Wn30bxAMVk74Dk8Z1PrpbK/img.gif?width=426&amp;amp;height=640&amp;amp;face=0_0_426_640,https://scrap.kakaocdn.net/dn/FJsIa/hyZqQqssmU/o1qswQRQ40YW2Cz5v3jDVk/img.gif?width=426&amp;amp;height=640&amp;amp;face=0_0_426_640,https://scrap.kakaocdn.net/dn/TkVfh/hyZuzHlSCB/4YCmftOptsAF10VZPF2TK1/img.png?width=918&amp;amp;height=1332&amp;amp;face=0_0_918_1332');&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;[React&amp;amp;Kotlin] 웹-네이티브 브리지를 활용한 PG 결제 연동 구조 설계기 - 안드로이드 편&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;문제의 시작 현재 회사에서 안드로이드 앱들은 MainActivity에서 WebView를 활용해 React로 개발된 웹 화면을 네이티브 앱 내에 띄워서 표시하고 있다. 이전까지는 대부분 외부업체와의 연동이 없고 병&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;h-owo-ld.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;iOS 개선방법&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;iOS 역시 비슷한 방식으로 자식 WebView를 사용하는 구조이지만 기기 특성상 하드웨어 뒤로가기 버튼이 없기 때문에 상단에 커스텀 뒤로가기 버튼을 별도로 추가해주는 작업을 덧붙였다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;모바일의 갓 핑관님 덕분에 꽤나 복잡했던 결제 플로우 문제를 안정적으로 처리할 수 있었고 무엇보다도 커뮤니티의 힘이 얼마나 큰지를 다시 한 번 느낄 수 있었다.  &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;iOS 개선방법 상세 코드&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;/// ViewController

...
class ViewController: UIViewController,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CLLocationManagerDelegate,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CBCentralManagerDelegate,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;WKNavigationDelegate,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;WKScriptMessageHandler,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;WKUIDelegate,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PGWebViewControllerDelegate {&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
...
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 결제 시 결제전용 웹뷰를 열도록 하는 브릿지 함수
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case &quot;openPGWebView&quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Logger.log(self, &quot;openPgWebView&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&quot;[PRINT DEBUG] openPgWebView message.body: \(String(describing: message.body))&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// JS Bridge로부터 받은 message.body가 String(결제 URL)인지 확인, URL 타입으로 변환 시도
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;guard let link = message.body as? String, let url = URL(string: link) else {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Logger.log(self, &quot;PGWebView: 잘못된 URL \(String(describing: message.body))&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// JS Bridge로부터 받은 message.body가 String(결제 URL)인지 확인, URL 타입으로 변환 시도
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let storyboard = UIStoryboard(name: &quot;Main&quot;, bundle: nil)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if let pgVC = storyboard.instantiateViewController(withIdentifier: &quot;PGWebViewController&quot;) as? PGWebViewController {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 결제 URL 및 delegate(콜백 받을 부모 컨트롤러) 주입
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pgVC.pgUrl = url // 원하는 결제 URL 전달
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pgVC.delegate = self
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 결제창을 UINavigationController로 감싸서 present (상단에 네비게이션바, 뒤로가기 가능)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let nav = UINavigationController(rootViewController: pgVC)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;nav.modalPresentationStyle = .fullScreen // 전체화면 모달로 띄움(iOS 기본은 half-modal이니 꼭 명시)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;present(nav, animated: true)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 결제승인 URL 콜백
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;func pgWebViewControllerDidFinish(url:URL){
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Logger.log(self, &quot;[PGWebView] 승인 URL 반환:&quot;, url)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 바로 부모 WKWebView에서 페이지 이동!
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;webView.load(URLRequest(url: url))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/code&gt;&lt;/pre&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;2978&quot; data-origin-height=&quot;1412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pjNpJ/btsPEMSae6H/r8XMM1KEcakNhPMDufZhJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pjNpJ/btsPEMSae6H/r8XMM1KEcakNhPMDufZhJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pjNpJ/btsPEMSae6H/r8XMM1KEcakNhPMDufZhJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpjNpJ%2FbtsPEMSae6H%2Fr8XMM1KEcakNhPMDufZhJ0%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;2978&quot; height=&quot;1412&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2978&quot; data-origin-height=&quot;1412&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;리액트에서 openPGWebView 라는 브릿지메시지를 보내면 해당 메시지 내의 url을 PGWebViewController로 보내준다. 이때 iOS의 경우는 뒤로가기 버튼이 따로없기때문에 별도로 상단에 navigation바에 뒤로가기 버튼을 추가해줬고, iOS에서는 모달이 기본적으로 half로 띄워지기 때문에 명시적으로 풀스크린을 지정해서 전체화면으로 띄우도록 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;이후 결제가 성공적으로 완료되면 결제승인 콜백을 통해 부모컨트롤러인 해당 컨트롤러로 승인 URL이 다시 전달된다. 전달받은 URL은 기존의 메인웹뷰에서 다시 로딩되어 웹페이지 흐름이 이어지게 된다.&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;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;pre class=&quot;swift&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;// PGWebViewController
...

/// 결제 전용 WebView의 결과를 콜백받기 위한 프로토콜
protocol PGWebViewControllerDelegate: AnyObject {
    /// 결제 성공 또는 승인 URL이 감지된 경우 호출됨
    func pgWebViewControllerDidFinish(url: URL)
    /// 사용자가 [&amp;lt;-] 뒤로가기/닫기 버튼을 눌러 결제창을 닫은 경우 호출됨
    func pgWebViewControllerDidCancel()
}

/// PG(결제) 전용 웹뷰 컨트롤러
class PGWebViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {
    /// 결제 URL (외부에서 반드시 할당해야 함)
    var pgUrl: URL!
    weak var delegate: PGWebViewControllerDelegate?
    private var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()
        print(&quot;[PGWebView] viewDidLoad - pgUrl:&quot;, pgUrl ?? &quot;nil&quot;)
        
        // WebView 환경설정: JS, 여러창 등 활성화
        let config = WKWebViewConfiguration()
        config.preferences.javaScriptEnabled = true
        config.preferences.javaScriptCanOpenWindowsAutomatically = true
        
        // WebView 인스턴스 생성 및 설정
        webView = WKWebView(frame: self.view.bounds, configuration: config)
        webView.navigationDelegate = self
        webView.uiDelegate = self
        webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        self.view.addSubview(webView)

        // 결제 URL 로드
        if let url = pgUrl {
            print(&quot;[PGWebView] loading URL:&quot;, url)
            webView.load(URLRequest(url: url))
        } else {
            print(&quot;[PGWebView] pgUrl is nil, cannot load webview&quot;)
        }

        print(&quot;[PGWebView] NavigationController?&quot;, navigationController != nil)
        print(&quot;[PGWebView] parent:&quot;, parent ?? &quot;nil&quot;)
    }
    
    /// 상단 네비게이션 바의 [&amp;lt;-] 버튼 (Left bar button items)
    @IBAction func didTapBack(_ sender: Any) {
        delegate?.pgWebViewControllerDidCancel()
        dismiss(animated: true)
    }

    /// 네비게이션 이벤트 감지(결제 승인/에러/카드앱 딥링크 등 처리)
    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -&amp;gt; Void) {
        guard let url = navigationAction.request.url else {
            print(&quot;[PGWebView] navigationAction: url is nil&quot;)
            decisionHandler(.allow)
            return
        }
        print(&quot;[PGWebView] navigating to:&quot;, url)

        // 결제 승인/에러 리턴 URL 감지
        if url.absoluteString.contains(&quot;/approval&quot;) {
            print(&quot;[PGWebView] approval detected, dismissing&quot;)
            delegate?.pgWebViewControllerDidFinish(url: url)
            self.dismiss(animated: true)
            decisionHandler(.cancel)
            return
        }

        // 카드/간편결제/은행 앱 등 외부앱 연동 스킴 감지시, 시스템으로 오픈
        if let scheme = url.scheme, paymentSchemes.contains(scheme) {
            print(&quot;[PGWebView] open external scheme:&quot;, scheme)
            UIApplication.shared.open(url)
            decisionHandler(.cancel)
            return
        }

        // 그 외는 웹뷰 기본동작
        decisionHandler(.allow)
    }
    ...
    
}

...&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;span style=&quot;color: #009a87;&quot;&gt;리액트에서 요청하면 메인을 거쳐 열리는 결제 웹뷰 페이지 컨트롤러다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;해당 컨트롤러에서는 외부로부터 전달받은 결제 URL을 로딩하고 결제 흐름중 발생하는 승인 완료나 간편결제 같은 외부 앱 호출 같은 이벤트를 감지해서 처리하게 된다.&amp;nbsp;&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;리액트에서 사용한 브릿지코드&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1754041382003&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * 모바일 네이티브의 `openPGWebView` 메서드 호출.
 *
 * @method openPGWebView
 * @param {String} link pg호출을 위해 별도의 웹뷰를 띄우는 URL.
 */
const openPGWebView = (link) =&amp;gt; {
  if (mobile.isMobile) {
    if (mobile.isAndroid) {
      if (window?.androidWebBridge?.openPGWebView) {
        window.androidWebBridge.openPGWebView(link)
      } else {
        window.open(link)
      }
    } else if (mobile.isIOS) {
      if (window?.webkit?.messageHandlers?.openPGWebView) {
        window.webkit.messageHandlers.openPGWebView.postMessage(link)
      } else {
        window.open(link)
      }
    }
  } else {
    window.open(link)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;결과&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qGfht/btsONAESRX7/SuCPXjHlrHF99jdROFqecK/tfile.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qGfht/btsONAESRX7/SuCPXjHlrHF99jdROFqecK/tfile.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qGfht/btsONAESRX7/SuCPXjHlrHF99jdROFqecK/tfile.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/qGfht/btsONAESRX7/SuCPXjHlrHF99jdROFqecK/tfile.gif&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;426&quot; height=&quot;640&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;640&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;span style=&quot;color: #009a87;&quot;&gt;이제 뒤로가기 버튼을 눌러도 다시 원래 페이지로 복귀가 자연스럽게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;결제 시나리오 완료후에도 메인 뷰로 승인결과를 URL에 담아서 가져오게되었다.&lt;/span&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;&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure style=&quot;color: #333333; text-align: center;&quot; contenteditable=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/012.gif&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-name=&quot;012&quot; data-emoticon-type=&quot;friends1&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;emoticon&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/012.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;동기랑 다음에는 앱개발 하게되면 RN를 진지하게 고려해보자는 이야기를 했다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발/Error note</category>
      <author>이나당</author>
      <guid isPermaLink="true">https://h-owo-ld.tistory.com/371</guid>
      <comments>https://h-owo-ld.tistory.com/371#entry371comment</comments>
      <pubDate>Fri, 1 Aug 2025 18:54:20 +0900</pubDate>
    </item>
    <item>
      <title>[React&amp;amp;Kotlin] 웹-네이티브 브리지를 활용한 PG 결제 연동 구조 설계기 - 안드로이드 편</title>
      <link>https://h-owo-ld.tistory.com/370</link>
      <description>&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;문제의 시작&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cacNgT/btsON9UbGk6/XXK172OSAQzbKnsfYkHrEk/tfile.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cacNgT/btsON9UbGk6/XXK172OSAQzbKnsfYkHrEk/tfile.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cacNgT/btsON9UbGk6/XXK172OSAQzbKnsfYkHrEk/tfile.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cacNgT/btsON9UbGk6/XXK172OSAQzbKnsfYkHrEk/tfile.gif&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;426&quot; height=&quot;640&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;640&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;현재 회사에서 안드로이드 앱들은 MainActivity에서 WebView를 활용해 React로 개발된 웹 화면을 네이티브 앱 내에 띄워서 표시하고 있다. 이전까지는 대부분 외부업체와의 연동이 없고 병원 전산팀과 연동하는 형태라서 특별한 문제가 없었다. 하지만 이번에 결제기능이 추가되면서.. 문제가생겼다.&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;h2 data-ke-size=&quot;size26&quot;&gt;문제 상황&lt;/h2&gt;
&lt;pre id=&quot;code_1750658360764&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    override fun onBackPressed() {
        Logger.debug()

        webView.evaluateJavascript(&quot;historyBack();&quot;) {
            if (it?.toString() == &quot;1&quot;) {
                super.onBackPressed()
            }
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안드로이드의&amp;nbsp;뒤로가기&amp;nbsp;버튼을&amp;nbsp;눌렀을&amp;nbsp;때&amp;nbsp;WebView에서는&amp;nbsp;historyBack()&amp;nbsp;자바스크립트&amp;nbsp;함수를&amp;nbsp;실행하고&amp;nbsp;그&amp;nbsp;리턴값에&amp;nbsp;따라&amp;nbsp;동작을&amp;nbsp;분기하도록&amp;nbsp;구성되어&amp;nbsp;있다.&amp;nbsp;하지만&amp;nbsp;수납&amp;nbsp;페이지처럼&amp;nbsp;외부에서&amp;nbsp;호출된&amp;nbsp;URL의&amp;nbsp;경우&amp;nbsp;해당&amp;nbsp;함수(historyBack)가&amp;nbsp;정의되어&amp;nbsp;있지&amp;nbsp;않기&amp;nbsp;때문에&amp;nbsp;함수&amp;nbsp;실행&amp;nbsp;시&amp;nbsp;웹&amp;nbsp;디버깅&amp;nbsp;콘솔에&amp;nbsp;&quot;함수가&amp;nbsp;존재하지&amp;nbsp;않는다&quot;는&amp;nbsp;오류가&amp;nbsp;출력되며&amp;nbsp;뒤로가기가&amp;nbsp;정상적으로&amp;nbsp;동작하지&amp;nbsp;않는&amp;nbsp;문제가&amp;nbsp;발생하게&amp;nbsp;됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhxHPa/btsON7PONGw/dO3muIz9YPUm0V5NlkrhbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhxHPa/btsON7PONGw/dO3muIz9YPUm0V5NlkrhbK/img.png&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;824&quot; data-is-animation=&quot;false&quot; style=&quot;width: 55.591%; margin-right: 10px;&quot; data-widthpercent=&quot;56.25&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhxHPa/btsON7PONGw/dO3muIz9YPUm0V5NlkrhbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhxHPa%2FbtsON7PONGw%2FdO3muIz9YPUm0V5NlkrhbK%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;730&quot; height=&quot;824&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ci4sBE/btsOLEhvSFI/QNGQdhNP45fTXgdgKlRUSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ci4sBE/btsOLEhvSFI/QNGQdhNP45fTXgdgKlRUSK/img.png&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;1332&quot; data-is-animation=&quot;false&quot; style=&quot;width: 43.2462%;&quot; data-widthpercent=&quot;43.75&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ci4sBE/btsOLEhvSFI/QNGQdhNP45fTXgdgKlRUSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fci4sBE%2FbtsOLEhvSFI%2FQNGQdhNP45fTXgdgKlRUSK%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;918&quot; height=&quot;1332&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사담을 좀 하자면.. 최근 회사 상황이 꽤나 정신없다. 기존 팀장님께서 퇴사를 선언하신 뒤 사실상 칩거 상태에 들어가셨고(ㅠ) 이후 월초에 새로 채용된 팀장님도 하루 만에 퇴사를 결정하면서 팀장 자리는 공석이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 팀 구성은 총 4명으로 나, 입사 동기, 2년 차 백엔드 개발자, 신입 프론트엔드 개발자 이렇게만 남아 있는 상황이다.&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;그 중 한 방에서 비슷한 이슈를 겪고 해결한 분의 조언을 받을 수 있었고, 정말 큰 도움이 되었다. (갓...ㅠㅠ)&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개선방법&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Android 개선방법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안드로이드에서는 MainWebView 외 결제 수단용으로 별도의 자식 WebView(PGWebViewActivity)를 하나 더 만들어 결제요청 시 자식 WebView를 띄우고 뒤로가기 버튼을 누르면 해당 WebView를 닫아 다시 부모 WebView로 돌아올 수 있도록 구성했다. 이 방식으로 팝업 결제 페이지의 흐름을 안정적으로 처리할 수 있었다. 또한 뒤로가기 뿐 아니라 승인까지 진행하는 프로세스 결과도 MainActivity에서 전달받을 수 있는 구조로 개선했다.&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;b&gt;iOS 개선방법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iOS 역시 비슷한 방식으로 자식 WebView를 사용하는 구조이지만 기기 특성상 하드웨어 뒤로가기 버튼이 없기 때문에 상단에 커스텀 뒤로가기 버튼을 별도로 추가해주는 작업을 덧붙였다. (iOS 관련 내용은 따로 포스팅할 예정)&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;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;안드로이드 개선방법 상세 코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1750659835664&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// MainActivity.kt
... 
class MainActivity : AppCompatActivity()  {
     private val pgLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult -&amp;gt;
        if (result.resultCode == Activity.RESULT_OK) {
            val approvalUrl = result.data?.getStringExtra(&quot;approval_url&quot;)
            Logger.debug(&quot;PG 승인 URL 받음: $approvalUrl&quot;)
            if (!approvalUrl.isNullOrEmpty()) {
                webView.loadUrl(approvalUrl)
            }
        }
    }
...
    inner class WebBridgeClass {
   		@JavascriptInterface
        fun openPGWebView(url:String){
            Logger.debug(&quot; openPGWebView: $url&quot;)
            val intent = Intent(this@MainActivity, PGWebViewActivity::class.java)
            intent.putExtra(&quot;pg_url&quot;,url)
            Logger.debug(&quot; PGWebViewActivity onCreate() called!!!&quot;)

            (this@MainActivity as MainActivity).pgLauncher.launch(intent)
        }
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저&amp;nbsp;아래의&amp;nbsp;WebBridgeClass&amp;nbsp;안의&amp;nbsp;openPGWebView함수를&amp;nbsp;통해&amp;nbsp;웹에서&amp;nbsp;PG&amp;nbsp;결제&amp;nbsp;URL을&amp;nbsp;넘기면&amp;nbsp;안드로이드에서&amp;nbsp;해당&amp;nbsp;URL을&amp;nbsp;Intent로&amp;nbsp;감싸서&amp;nbsp;PGWebViewActivity로&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;외부&amp;nbsp;PG사&amp;nbsp;페이지로&amp;nbsp;이동하게&amp;nbsp;되므로&amp;nbsp;이후&amp;nbsp;결제가&amp;nbsp;끝나면&amp;nbsp;PGWebViewActivity에서&amp;nbsp;MainActivity에&amp;nbsp;선언된&amp;nbsp;ActivityResultLauncher를&amp;nbsp;통해&amp;nbsp;결제&amp;nbsp;결과를&amp;nbsp;받아오는&amp;nbsp;구조를&amp;nbsp;추가해줬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;pre id=&quot;code_1750659605378&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// PGWebViewActivity.kt
...

class PGWebViewActivity : AppCompatActivity() {
    private lateinit var webView: WebView

    @SuppressLint(&quot;SetJavaScriptEnabled&quot;, &quot;MissingInflatedId&quot;)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_pgwebview)

        // (1) Lollipop 이하 호환용 - 쿠키 동기화 매니저 초기화
        @Suppress(&quot;DEPRECATION&quot;)
        CookieSyncManager.createInstance(this)

        // (2) Safe Area 대응 (상단/하단 시스템 영역 패딩 적용)
        val webviewContainer = findViewById&amp;lt;FrameLayout&amp;gt;(R.id.webviewContainer2)
        ViewCompat.setOnApplyWindowInsetsListener(webviewContainer) { v, insets -&amp;gt;
            val sysInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(0, sysInsets.top, 0, sysInsets.bottom)
            insets
        }

        // (3) Android 11(R) 이상에서 전체화면 표시 설정
        if (Build.VERSION.SDK_INT &amp;gt;= Build.VERSION_CODES.R) {
            window.setDecorFitsSystemWindows(false)
        }

        // (4) 상태바 배경색/아이콘 색상 설정 (화이트 배경 + 다크 아이콘)
        if (Build.VERSION.SDK_INT &amp;gt;= Build.VERSION_CODES.M) {
            window.statusBarColor = Color.WHITE
            window.decorView.systemUiVisibility =
                window.decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
        }

        // (5) PG 결제 페이지 URL 인텐트로 전달받기
        val pgUrl = intent.getStringExtra(&quot;pg_url&quot;)
        Logger.debug(&quot; pgUrl: $pgUrl&quot;)

        // (6) WebView 초기 설정
        webView = findViewById(R.id.pgWebView)
        webView.settings.javaScriptEnabled = true // 자바스크립트 허용
        webView.settings.setSupportMultipleWindows(true) // 다중창 지원
        webView.settings.javaScriptCanOpenWindowsAutomatically = true // JS에서 window.open 허용
        webView.settings.domStorageEnabled = true // localStorage 등 DOM 스토리지 허용

        // (7) Third-party 쿠키 허용 (PG 결제 시 필수)
        if (Build.VERSION.SDK_INT &amp;gt;= Build.VERSION_CODES.LOLLIPOP) {
            CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)
        }

        // (8) WebViewClient 설정 - URL 제어 및 외부앱/마켓 호출 처리
        webView.webViewClient = object : WebViewClient() {

            @Deprecated(&quot;Deprecated in Java&quot;)
            @Suppress(&quot;DEPRECATION&quot;)
            override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
                val url = request?.url.toString()
                Logger.debug(&quot; [shouldOverrideUrlLoading] url=$url&quot;)

                // (8-1) 결제 승인 URL 처리
                if (url.contains(&quot;/approval&quot;)) {
                    val resultIntent = Intent()
                    resultIntent.putExtra(&quot;approval_url&quot;, url)
                    setResult(RESULT_OK, resultIntent)
                    finish()
                    return true
                }

                // (8-2) 전화 연결 처리
                if (url.startsWith(&quot;tel:&quot;)) {
                    startActivity(Intent(Intent.ACTION_DIAL, url.toUri()))
                    return true
                }

                // (8-3) 외부 앱 호출/마켓 이동 처리
                if (!URLUtil.isNetworkUrl(url) &amp;amp;&amp;amp; !URLUtil.isJavaScriptUrl(url)) {
                    try {
                        val uri = Uri.parse(url)
                        if (uri.scheme == &quot;intent&quot;) {
                            return startSchemeIntent(url)
                        } else {
                            startActivity(Intent(Intent.ACTION_VIEW, uri))
                            return true
                        }
                    } catch (e: Exception) {
                        return false
                    }
                }

                // (8-4) 기본 WebView 처리
                return false
            }

            override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
                super.onPageStarted(view, url, favicon)
                Logger.debug(&quot; onPageStarted: $url&quot;)
            }

            override fun onPageFinished(view: WebView?, url: String?) {
                super.onPageFinished(view, url)
                Logger.debug(&quot; onPageFinished: $url&quot;)
            }
        }

        // (9) 팝업 등 지원을 위한 WebChromeClient 등록
        webView.webChromeClient = WebChromeClient()

        // (10) PG 결제 페이지 로딩 시작
        if (!pgUrl.isNullOrEmpty()) {
            webView.loadUrl(pgUrl)
        }
    }

    // (11) intent:// 스킴 처리 메서드 (앱 호출 or 마켓 fallback)
    private fun startSchemeIntent(url: String): Boolean {
        return try {
            val schemeIntent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME)
            try {
                startActivity(schemeIntent)
                true
            } catch (e: ActivityNotFoundException) {
                val packageName = schemeIntent.`package`
                if (!packageName.isNullOrEmpty()) {
                    startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(&quot;market://details?id=$packageName&quot;)))
                    true
                } else {
                    false
                }
            }
        } catch (e: URISyntaxException) {
            false
        }
    }

...

    // (14) 백버튼 &amp;rarr; 결제 취소 처리
    @Deprecated(&quot;Deprecated in Java&quot;)
    @Suppress(&quot;DEPRECATION&quot;, &quot;MissingSuperCall&quot;)
    override fun onBackPressed() {
        setResult(RESULT_CANCELED)
        finish()
    }
}&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;PGWebViewActivity는 결제에 필요한 PG URL을 인텐트로 전달받아서 화면이 표시된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 기능은 2가지이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. /approval 등의 URL 탐지 시 setResult(RESULT_OK)와 함께 부모 액티비티에 복귀&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. intent://, ispmobile:// 등 다양한 스킴 처리 및 마켓 fallback 로직을 추가&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;OneUI 7.0부터는 상단, 하단 시스템 영역(노치, 소프트 키 등)에 대응이 필요해서 safe-area 패딩을 적용했고, Android 11 이상에서는 status/navigation bar가 화면 위에 그려지도록 window 설정을 추가했다. 이대로 했더니 시계가 다크모드에서는 제대로 안보이는 이슈가 생겨서 상태바 배경도 흰색으로 바꾸고 아이콘이랑 글씨는 검정색으로 바꿔서 가독성을 높였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 코드 개선의 시작이었던.. 백버튼을 누르면 setResult(RESULT_CANCELED)로 결제 취소 결과를 돌려주고 액티비티를 종료하도록했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1750661186998&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// activity_pgwebview.xml
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    tools:context=&quot;.PGWebViewActivity&quot;&amp;gt;

    &amp;lt;FrameLayout
        android:id=&quot;@+id/webviewContainer2&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;match_parent&quot;&amp;gt;

        &amp;lt;WebView
            android:id=&quot;@+id/pgWebView&quot;
            android:layout_width=&quot;match_parent&quot;
            android:layout_height=&quot;match_parent&quot; /&amp;gt;

    &amp;lt;/FrameLayout&amp;gt;

&amp;lt;/androidx.coordinatorlayout.widget.CoordinatorLayout&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;activity_pgwebview.xml은 결제 전용 WebView 레이아웃을 위한 XML로 레이아웃 형태는 MainActivity와 똑같이 배치했다.&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;h3 data-ke-size=&quot;size23&quot;&gt;리액트에서 사용한 브릿지코드&lt;/h3&gt;
&lt;pre id=&quot;code_1750754103011&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * 모바일 네이티브의 `openPGWebView` 메서드 호출.
 *
 * @method openPGWebView
 * @param {String} link pg호출을 위해 별도의 웹뷰를 띄우는 URL.
 */
const openPGWebView = (link) =&amp;gt; {
  if (mobile.isMobile) {
    if (mobile.isAndroid) {
      if (window?.androidWebBridge?.openPGWebView) {
        window.androidWebBridge.openPGWebView(link)
      } else {
        window.open(link)
      }
    } else if (mobile.isIOS) {
      if (window?.webkit?.messageHandlers?.openPGWebView) {
        window.webkit.messageHandlers.openPGWebView.postMessage(link)
      } else {
        window.open(link)
      }
    }
  } else {
    window.open(link)
  }
}&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;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과&lt;/h2&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-origin-width=&quot;426&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwDBxh/btsONbrDtCv/WUNJgfvdrwaskKGZoMRgwk/tfile.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwDBxh/btsONbrDtCv/WUNJgfvdrwaskKGZoMRgwk/tfile.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwDBxh/btsONbrDtCv/WUNJgfvdrwaskKGZoMRgwk/tfile.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cwDBxh/btsONbrDtCv/WUNJgfvdrwaskKGZoMRgwk/tfile.gif&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;426&quot; height=&quot;640&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;640&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;결제 시나리오 완료후에도 MainActivity로 승인결과를 URL에 담아서 가져오게되었다.&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;&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;012&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/012.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/012.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;동기랑 다음에는 앱개발 하게되면 RN를 진지하게 고려해보자는 이야기를 했다.&lt;/p&gt;</description>
      <category>개발/Error note</category>
      <category>Kotlin</category>
      <category>react</category>
      <author>이나당</author>
      <guid isPermaLink="true">https://h-owo-ld.tistory.com/370</guid>
      <comments>https://h-owo-ld.tistory.com/370#entry370comment</comments>
      <pubDate>Tue, 24 Jun 2025 17:35:41 +0900</pubDate>
    </item>
    <item>
      <title>[React] 25.04 기준 신규버전 카카오페이 연동하기</title>
      <link>https://h-owo-ld.tistory.com/369</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;&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;1938&quot; data-origin-height=&quot;1514&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cl1qu7/btsNxqwLmoP/xcpN53Y8p47phgaEdQ6I1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cl1qu7/btsNxqwLmoP/xcpN53Y8p47phgaEdQ6I1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cl1qu7/btsNxqwLmoP/xcpN53Y8p47phgaEdQ6I1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcl1qu7%2FbtsNxqwLmoP%2FxcpN53Y8p47phgaEdQ6I1K%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;1938&quot; height=&quot;1514&quot; data-origin-width=&quot;1938&quot; data-origin-height=&quot;1514&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;계정생성&lt;/h2&gt;
&lt;figure id=&quot;og_1745468310831&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;카카오페이 | 개발자센터&quot; data-og-description=&quot;새로운 기회와 가치를 함께 만들어봐요&quot; data-og-host=&quot;developers.kakaopay.com&quot; data-og-source-url=&quot;https://developers.kakaopay.com/&quot; data-og-url=&quot;https://developers.kakaopay.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/8LWk7/hyYIfDLYp7/3pI3Kypf533v4d3BEhOrN1/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://developers.kakaopay.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.kakaopay.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/8LWk7/hyYIfDLYp7/3pI3Kypf533v4d3BEhOrN1/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;developers.kakaopay.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카카오페이 개발자센터 계정이 없어서 일단 계정부터 만들어줬다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;h2 data-ke-size=&quot;size26&quot;&gt;사전등록&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;애플리케이션 등록&lt;/h3&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;2202&quot; data-origin-height=&quot;1588&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l1EFz/btsNw0ygTf3/OZ4RM0NuJJirpoJmyCgRFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l1EFz/btsNw0ygTf3/OZ4RM0NuJJirpoJmyCgRFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l1EFz/btsNw0ygTf3/OZ4RM0NuJJirpoJmyCgRFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl1EFz%2FbtsNw0ygTf3%2FOZ4RM0NuJJirpoJmyCgRFK%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;2202&quot; height=&quot;1588&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2202&quot; data-origin-height=&quot;1588&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;&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;h3 data-ke-size=&quot;size23&quot;&gt;Secret key(dev)발급&lt;/h3&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;2500&quot; data-origin-height=&quot;1694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Tb3FT/btsOBpwRtZL/eR6Ymogzeds4Y7N39hLVjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Tb3FT/btsOBpwRtZL/eR6Ymogzeds4Y7N39hLVjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Tb3FT/btsOBpwRtZL/eR6Ymogzeds4Y7N39hLVjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTb3FT%2FbtsOBpwRtZL%2FeR6Ymogzeds4Y7N39hLVjk%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;2500&quot; height=&quot;1694&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2500&quot; data-origin-height=&quot;1694&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로 만들어진 애플리케이션을 선택한 후 하단에 Secret key(dev) 발급 버튼을 누른다.&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;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2572&quot; data-origin-height=&quot;1842&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cG2tSM/btsNw7RK4SQ/wQ0ZuW9aMkRjpxWs39jOv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cG2tSM/btsNw7RK4SQ/wQ0ZuW9aMkRjpxWs39jOv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cG2tSM/btsNw7RK4SQ/wQ0ZuW9aMkRjpxWs39jOv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcG2tSM%2FbtsNw7RK4SQ%2FwQ0ZuW9aMkRjpxWs39jOv1%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;2572&quot; height=&quot;1842&quot; data-origin-width=&quot;2572&quot; data-origin-height=&quot;1842&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션 - 플랫폼에 들어가서 Web부분에 테스트할 주소를 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 localhost:3300으로 입력했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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/7DjEG/btsNvbnkxY5/LDMqVMikXUieTVtyxQBg2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7DjEG/btsNvbnkxY5/LDMqVMikXUieTVtyxQBg2k/img.png&quot; data-origin-width=&quot;1056&quot; data-origin-height=&quot;1242&quot; data-is-animation=&quot;false&quot; style=&quot;width: 20.9838%; margin-right: 10px;&quot; data-widthpercent=&quot;21.23&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7DjEG/btsNvbnkxY5/LDMqVMikXUieTVtyxQBg2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7DjEG%2FbtsNvbnkxY5%2FLDMqVMikXUieTVtyxQBg2k%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;1056&quot; height=&quot;1242&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjfoMI/btsNwWbRNxm/BZv56xjIDkOcRb7hH1N24k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjfoMI/btsNwWbRNxm/BZv56xjIDkOcRb7hH1N24k/img.png&quot; data-origin-width=&quot;694&quot; data-origin-height=&quot;220&quot; data-is-animation=&quot;false&quot; style=&quot;width: 77.8534%;&quot; data-widthpercent=&quot;78.77&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjfoMI/btsNwWbRNxm/BZv56xjIDkOcRb7hH1N24k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjfoMI%2FbtsNwWbRNxm%2FBZv56xjIDkOcRb7hH1N24k%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;694&quot; height=&quot;220&quot;/&gt;&lt;/span&gt;&lt;/div&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;* 꼭 http부터 입력해 주어야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1745468809998&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;http://36.46.31.221 (O)
http://localhost:8080/ (O)
http://127.0.0.1 (O)
https://example (X)
ftp://hello.world (X)
https://example.com/path1 (X) (path, query string, hash 허용하지 않음)
https://https://example.com (X)
https://example.com/path1?key1=value1#hash (X) (path, query string, hash 허용하지 않음)
https://example.com/ (X)
https://*.com (X)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 디벨로퍼스 센터에서 설정할 사전작업들은 끝&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;연동하기 (테스트 기준)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결제준비&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;카카오페이&amp;nbsp;결제를&amp;nbsp;시작하기&amp;nbsp;위해&amp;nbsp;결제정보를&amp;nbsp;카카오페이&amp;nbsp;서버에&amp;nbsp;전달하고&amp;nbsp;결제&amp;nbsp;고유번호(TID)와&amp;nbsp;URL을&amp;nbsp;응답받는&amp;nbsp;단계입니다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Secret&amp;nbsp;key를&amp;nbsp;헤더에&amp;nbsp;담아&amp;nbsp;파라미터&amp;nbsp;값들과&amp;nbsp;함께&amp;nbsp;POST로&amp;nbsp;요청합니다.&lt;/li&gt;
&lt;li&gt;요청이 성공하면 응답 바디에 JSON 객체로 다음 단계 진행을 위한 값들을 받습니다.&lt;/li&gt;
&lt;li&gt;서버(Server)는 tid를 저장하고, 클라이언트는 사용자 환경에 맞는 URL로 리다이렉트(redirect)합니다.&lt;/li&gt;
&lt;/ul&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;pre id=&quot;code_1745469523450&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import axios from 'axios'
import { config } from '../../components'
import { log } from '../../tools'

const TestKakaoPage = () =&amp;gt; {
  const { REQUEST_URL, SECTRET_KEY, RETURN_URL } = config.constant.KAKAO
  
  const handleClick = async () =&amp;gt; {
    try {
      const headers = {
        Authorization: 'SECRET_KEY ' + SECRET_KEY,
      }

      const payload = {
        cid: 'TC0ONETIME', // 가맹점코드
        partner_order_id: 'order1234', // 가맹점 주문번호
        partner_user_id: 'YI_SEVERANCE', // 가맹점회원id
        item_name: '초코파이', 
        quantity: 1,
        total_amount: 2200,
        tax_free_amount: 0, 
        approval_url: RETURN_URL, 
        fail_url: RETURN_URL, 
        cancel_url: RETURN_URL,
      }

      log.info('결제 등록 요청에 담긴 payload:', payload)
      
      const response = await axios.post(REQUEST_URL, payload, { headers })
      const mobileURL = config.constant.IS_PRODUCTION
        ? response.data.next_redirect_mobile_url // 사용자 모바일 환경
        : response.data.next_redirect_pc_url // pc 테스트용

      const tid = response.data.tid
      console.log(tid)
      setLocalStorage('tid', JSON.stringify(tid))

      location.href = mobileURL

    } catch (error) {
      log.error('API 호출 오류:: ', error)
    }
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h2&amp;gt;테스트 Kakao&amp;lt;/h2&amp;gt;
      &amp;lt;button onClick={handleClick}&amp;gt;요청&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}

export { TestKakaoPage }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745471637739&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import axios from 'axios'
import { useEffect, useState } from 'react'
import { config } from '../../components'
import { getLocalStorage, hasLocalStorage, queryParamToObject } from '../../tools'

const TestKakaoIngPage = () =&amp;gt; {
  //http://localhost:3300/payment/kakao/ing?pg_token=토큰값
  const param = location.search
  const decodeKey = queryParamToObject(param)
  const { pg_token: pgToken } = decodeKey
  const { APPROVE_URL, PARKING_ADMIN_KEY } = config.constant.KAKAO
  const [status, setStatus] = useState('')
  
  useEffect(() =&amp;gt; {
    if (!pgToken || !hasLocalStorage('tid')) {

      return
    }

    const fetchData = async () =&amp;gt; {
      const headers = {
        Authorization: 'SECRET_KEY ' + PARKING_ADMIN_KEY,
      }

      const payload = {
        cid: 'TC0ONETIME', // 가맹점코드
        tid: getLocalStorage('tid'),
        partner_order_id: 'order1234', // 가맹점 주문번호 
        partner_user_id: 'YI_SEVERANCE', // 가맹점회원id
        pg_token: pgToken,
      }

      const response = await axios.post(APPROVE_URL, payload, { headers })

      setStatus(response.status)
    }

    fetchData()
  }, [decodeKey])

  return (
    &amp;lt;div&amp;gt;
      카카오페이 승인페이지
      {status === 200 &amp;amp;&amp;amp; &amp;lt;p&amp;gt;성공&amp;lt;/p&amp;gt;}
    &amp;lt;/div&amp;gt;
  )
}

export { TestKakaoIngPage }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745471757462&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
*
* https://devtalk.kakao.com/t/msg-authentication-doesn-t-complete-code-701/114140
*카카오페이 테스트 결제 구현 중 발생한 에러 문의 (&amp;ldquo;msg&amp;rdquo;:&amp;ldquo;authentication doesn&amp;rsquo;t complete!&amp;rdquo;,&amp;ldquo;code&amp;rdquo;:-701)
*안녕하세요. 카카오페이 입니다.
*말씀하신 오류의 경우, 사용자인증완료전 승인요청(approve)가 인입되어 발생되는 현상이 맞습니다.
*다만 테스트 CID의 경우, 결제수단 선택후, 비밀번호인증/페이스인증 등이 skip 되어 진행되기때문에,
*내부인증완료 처리진행 approve요청이 들어올경우 드물게 발생하는 현상일 수있습니다.
*운영시에는 발생되지 않는점 참고부탁드립니다.
*감사합니다.
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Apr-24-2025 14-12-38.gif&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBTTxR/btsNwYHUNSn/7DxVNbUgjNFXGESGIkq931/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBTTxR/btsNwYHUNSn/7DxVNbUgjNFXGESGIkq931/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBTTxR/btsNwYHUNSn/7DxVNbUgjNFXGESGIkq931/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bBTTxR/btsNwYHUNSn/7DxVNbUgjNFXGESGIkq931/img.gif&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;670&quot; height=&quot;520&quot; data-filename=&quot;Apr-24-2025 14-12-38.gif&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;520&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://developers.kakaopay.com/docs/getting-started/api-common-guide/prepare&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developers.kakaopay.com/docs/getting-started/api-common-guide/prepare&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://developers.kakaopay.com/docs/payment/online/single-payment&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developers.kakaopay.com/docs/payment/online/single-payment&lt;/a&gt;&lt;/p&gt;</description>
      <category>개발/Javascript</category>
      <author>이나당</author>
      <guid isPermaLink="true">https://h-owo-ld.tistory.com/369</guid>
      <comments>https://h-owo-ld.tistory.com/369#entry369comment</comments>
      <pubDate>Fri, 13 Jun 2025 22:20:09 +0900</pubDate>
    </item>
    <item>
      <title>[Javscript/Rechart] x축 스크롤링 추가</title>
      <link>https://h-owo-ld.tistory.com/368</link>
      <description>&lt;pre id=&quot;code_1742369879585&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const CustomTick = (props) =&amp;gt; {
  const { x, y, payload } = props
  const dateParts = payload.value.split('-') // [&quot;2024&quot;, &quot;12&quot;, &quot;15&quot;]

  return (
    &amp;lt;text
      x={x}
      y={y}
      textAnchor=&quot;front&quot;
      fontSize={12}
      letterSpacing={-0.55}
      fontWeight={500}
      fill=&quot;#333&quot;
    &amp;gt;
      &amp;lt;tspan x={x} dy=&quot;12&quot;&amp;gt;
        {dateParts[0]}년
      &amp;lt;/tspan&amp;gt;
      &amp;lt;tspan x={x} dy=&quot;15&quot;&amp;gt;
        {dateParts[1]}월 {dateParts[2]}일
      &amp;lt;/tspan&amp;gt;
    &amp;lt;/text&amp;gt;
  )
}

const StatUsageStatus = ({ value }) =&amp;gt; {
  ...코드생략 
  // x축 간격 조정 (4rem)
  const chartWidth =
    Math.max(chartData?.length * 64, 800) &amp;gt;=
    document?.querySelector('.ChartWrap')?.offsetWidth
      ? Math.max(chartData?.length * 64, 800)
      : '100%'

  return (
    &amp;lt;StatTabPanel value={value} index={0} className=&quot;StatUsageStatus&quot;&amp;gt;
		...코드생략
        &amp;lt;Box className=&quot;ChartBox&quot;&amp;gt;
          &amp;lt;Box className=&quot;ChartContent&quot;&amp;gt;
            &amp;lt;ResponsiveContainer width={chartWidth} height=&quot;100%&quot;&amp;gt;
              &amp;lt;AreaChart
                width={'100%'}
                height={500}
                data={chartData}
                margin={{
                  top: 10,
                  right: 60,
                  left: 0,
                  bottom: 30,
                }}
              &amp;gt;
                {/* ✅ SVG 그라데이션 정의 */}
                &amp;lt;defs&amp;gt;
                  {/* userCount 그래디언트 */}
                  &amp;lt;linearGradient
                    id=&quot;userCountGradient&quot;
                    x1=&quot;0&quot;
                    y1=&quot;0&quot;
                    x2=&quot;0&quot;
                    y2=&quot;1&quot;
                  &amp;gt;
                    &amp;lt;stop offset=&quot;0%&quot; stopColor=&quot;#f1bd44&quot; stopOpacity={0.5} /&amp;gt;
                    &amp;lt;stop offset=&quot;100%&quot; stopColor=&quot;#f1bd44&quot; stopOpacity={0} /&amp;gt;
                  &amp;lt;/linearGradient&amp;gt;
                  &amp;lt;linearGradient
                    id=&quot;preReviewCountGradient&quot;
                    x1=&quot;0&quot;
                    y1=&quot;0&quot;
                    x2=&quot;0&quot;
                    y2=&quot;1&quot;
                  &amp;gt;
                    &amp;lt;stop offset=&quot;0%&quot; stopColor=&quot;#53a9eb&quot; stopOpacity={0.5} /&amp;gt;
                    &amp;lt;stop offset=&quot;100%&quot; stopColor=&quot;#53a9eb&quot; stopOpacity={0} /&amp;gt;
                  &amp;lt;/linearGradient&amp;gt;
                &amp;lt;/defs&amp;gt;
                &amp;lt;CartesianGrid vertical={false} /&amp;gt;
                &amp;lt;XAxis
                  dataKey=&quot;date&quot;
                  interval={0}
                  tick={&amp;lt;CustomTick /&amp;gt;}
                  tickLine={false}
                /&amp;gt;
                &amp;lt;YAxis stroke=&quot;#333&quot; tickLine={false} /&amp;gt;
                &amp;lt;Tooltip /&amp;gt;
                &amp;lt;Area
                  dataKey=&quot;preReviewCount&quot;
                  dot={{ strokeWidth: 2, r: 1 }}
                  fill=&quot;url(#preReviewCountGradient)&quot;
                  name=&quot;테스트수&quot;
                  stroke=&quot;#53a9eb&quot;
                /&amp;gt;
                &amp;lt;Area
                  dataKey=&quot;userCount&quot;
                  dot={{ strokeWidth: 2, r: 1 }}
                  fill=&quot;url(#userCountGradient)&quot;
                  name=&quot;이용자 수&quot;
                  stroke=&quot;#f1bd44&quot;
                /&amp;gt;
              &amp;lt;/AreaChart&amp;gt;
            &amp;lt;/ResponsiveContainer&amp;gt;
          &amp;lt;/Box&amp;gt;
          &amp;lt;Box className=&quot;ChartLegends&quot;&amp;gt;
            &amp;lt;Box className=&quot;ChartLegend&quot;&amp;gt;
              &amp;lt;Box /&amp;gt;
              &amp;lt;T&amp;gt;이용자 수&amp;lt;/T&amp;gt;
            &amp;lt;/Box&amp;gt;
            &amp;lt;Box className=&quot;ChartLegend&quot;&amp;gt;
              &amp;lt;Box /&amp;gt;
              &amp;lt;T&amp;gt;테스트수&amp;lt;/T&amp;gt;
            &amp;lt;/Box&amp;gt;
          &amp;lt;/Box&amp;gt;
        &amp;lt;/Box&amp;gt;
    &amp;lt;/StatTabPanel&amp;gt;
  )
}

export { StatUsageStatus }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;438&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btXbss/btsMO8KTXri/aDoDVQJoO2o5hEpvw5JxJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btXbss/btsMO8KTXri/aDoDVQJoO2o5hEpvw5JxJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btXbss/btsMO8KTXri/aDoDVQJoO2o5hEpvw5JxJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtXbss%2FbtsMO8KTXri%2FaDoDVQJoO2o5hEpvw5JxJ1%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;1060&quot; height=&quot;438&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;438&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;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;처음엔 전체 가로스크롤 되는 코드였는데, 디자인상으로는 길어지면 y축은 고정이고 x축만 스크롤이 되게 디자인이 나와있어서, 코드를 수정했다. 처음에 어떻게 할지 고민했는데 해당 라이브러리 깃 issue에 비슷한 고민 해결을 올려둔 분이 있어서 참고할 수 있었다.&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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742370066707&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const StatUsageStatus = ({ value }) =&amp;gt; {
...코드생략
  return (
    &amp;lt;StatTabPanel value={value} index={0} className=&quot;StatUsageStatus&quot;&amp;gt;
      &amp;lt;Box className=&quot;ChartWrap&quot;&amp;gt;
		... 코드생략
        &amp;lt;Box className=&quot;ChartBox&quot;&amp;gt;
          &amp;lt;Box className=&quot;ChartContent&quot;&amp;gt;
            &amp;lt;Box className=&quot;ChartYAxis&quot;&amp;gt;
              &amp;lt;AreaChart
                width={62}
                height={400}
                data={chartData}
                margin={{
                  top: 10,
                  right: 0,
                  left: 0,
                  bottom: 65,
                }}
              &amp;gt;
                &amp;lt;YAxis stroke=&quot;#333&quot; tickLine={false} /&amp;gt;
                &amp;lt;Area
                  fillOpacity={0}
                  strokeOpacity={0}
                  dataKey=&quot;preReviewCount&quot;
                /&amp;gt;
                &amp;lt;Area fillOpacity={0} strokeOpacity={0} dataKey=&quot;userCount&quot; /&amp;gt;
              &amp;lt;/AreaChart&amp;gt;
            &amp;lt;/Box&amp;gt;
            &amp;lt;Box className=&quot;ChartXAxis&quot;&amp;gt;
              &amp;lt;ResponsiveContainer width={chartWidth} height=&quot;100%&quot;&amp;gt;
                &amp;lt;AreaChart
                  width={'100%'}
                  height={400}
                  data={chartData}
                  margin={{
                    top: 10,
                    right: 60,
                    left: 0,
                    bottom: 30,
                  }}
                &amp;gt;
                  {/* SVG 그라데이션 정의 */}
                  &amp;lt;defs&amp;gt;
                    {/* userCount 그래디언트 */}
                    &amp;lt;linearGradient
                      id=&quot;userCountGradient&quot;
                      x1=&quot;0&quot;
                      y1=&quot;0&quot;
                      x2=&quot;0&quot;
                      y2=&quot;1&quot;
                    &amp;gt;
                      &amp;lt;stop offset=&quot;0%&quot; stopColor=&quot;#f1bd44&quot; stopOpacity={0.5} /&amp;gt;
                      &amp;lt;stop offset=&quot;100%&quot; stopColor=&quot;#f1bd44&quot; stopOpacity={0} /&amp;gt;
                    &amp;lt;/linearGradient&amp;gt;
                    &amp;lt;linearGradient
                      id=&quot;preReviewCountGradient&quot;
                      x1=&quot;0&quot;
                      y1=&quot;0&quot;
                      x2=&quot;0&quot;
                      y2=&quot;1&quot;
                    &amp;gt;
                      &amp;lt;stop offset=&quot;0%&quot; stopColor=&quot;#53a9eb&quot; stopOpacity={0.5} /&amp;gt;
                      &amp;lt;stop offset=&quot;100%&quot; stopColor=&quot;#53a9eb&quot; stopOpacity={0} /&amp;gt;
                    &amp;lt;/linearGradient&amp;gt;
                  &amp;lt;/defs&amp;gt;
                  &amp;lt;CartesianGrid vertical={false} /&amp;gt;
                  &amp;lt;XAxis
                    dataKey=&quot;date&quot;
                    interval={0}
                    tick={&amp;lt;CustomTick /&amp;gt;}
                    tickLine={false}
                  /&amp;gt;
                  &amp;lt;Tooltip /&amp;gt;
                  &amp;lt;Area
                    dataKey=&quot;preReviewCount&quot;
                    dot={{ strokeWidth: 2, r: 1 }}
                    fill=&quot;url(#preReviewCountGradient)&quot;
                    name=&quot;테스트수&quot;
                    stroke=&quot;#53a9eb&quot;
                  /&amp;gt;
                  &amp;lt;Area
                    dataKey=&quot;userCount&quot;
                    dot={{ strokeWidth: 2, r: 1 }}
                    fill=&quot;url(#userCountGradient)&quot;
                    name=&quot;이용자 수&quot;
                    stroke=&quot;#f1bd44&quot;
                  /&amp;gt;
                &amp;lt;/AreaChart&amp;gt;
              &amp;lt;/ResponsiveContainer&amp;gt;
            &amp;lt;/Box&amp;gt;
          &amp;lt;/Box&amp;gt;
          &amp;lt;Box className=&quot;ChartLegends&quot;&amp;gt;
            &amp;lt;Box className=&quot;ChartLegend&quot;&amp;gt;
              &amp;lt;Box /&amp;gt;
              &amp;lt;T&amp;gt;이용자 수&amp;lt;/T&amp;gt;
            &amp;lt;/Box&amp;gt;
            &amp;lt;Box className=&quot;ChartLegend&quot;&amp;gt;
              &amp;lt;Box /&amp;gt;
              &amp;lt;T&amp;gt;테스트수&amp;lt;/T&amp;gt;
            &amp;lt;/Box&amp;gt;
          &amp;lt;/Box&amp;gt;
        &amp;lt;/Box&amp;gt;
      &amp;lt;/Box&amp;gt;
    &amp;lt;/StatTabPanel&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742370097572&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;      // recharts라이브러리 스타일
      .ChartContent {
        display: flex;
        height: dp(400);
        .ChartYAxis {
          width: dp(80);
          height: dp(400);
        }
        .ChartXAxis {
          overflow: hidden;
          overflow-x: auto;
          &amp;amp;::-webkit-scrollbar {
            height: dp(6);
          }

          &amp;amp;::-webkit-scrollbar-thumb {
            border-radius: dp(3);
            background: #aaa;
          }

          &amp;amp;::-webkit-scrollbar-thumb:hover {
            background: #aaa;
          }

          &amp;amp;::-webkit-scrollbar-track {
            background: #eee;
          }
          .recharts-responsive-container {
            .recharts-wrapper {
              .recharts-surface {
                // 차트 내 가로선
                .recharts-cartesian-grid {
                  .recharts-cartesian-grid-horizontal {
                    line {
                      stroke: #ddd;
                      stroke-dasharray: 2 2;
                    }
                  }
                }
                // 차트 내 도트
                .recharts-area {
                  &amp;amp;:first-child {
                    .recharts-area-dots {
                      circle {
                        stroke: #0b7cd3;
                      }
                    }
                  }
                  &amp;amp;:last-child {
                    .recharts-area-dots {
                      circle {
                        stroke: #d3970b;
                      }
                    }
                  }
                }
                // 차트 내 X축
                .recharts-xAxis {
                  .recharts-cartesian-axis-line {
                    stroke: #ddd;
                    stroke-width: dp(1);
                    stroke-dasharray: 2 2;
                  }
                  .recharts-cartesian-axis-ticks {
                    .recharts-cartesian-axis-tick {
                      text-align: left;
                      @include font(500, 12, 20, -0.55);
                    }
                  }
                }
                // 차트 내 Y축
                .recharts-yAxis {
                  .recharts-cartesian-axis-line {
                    stroke: #aaa;
                  }
                  .recharts-cartesian-axis-ticks {
                    @include font(bold, 14, 20);
                  }
                }
              }

              .recharts-tooltip-wrapper {
                .recharts-default-tooltip {
                  padding: dp(5) dp(10) dp(10) !important;
                  .recharts-tooltip-label {
                    @include font(600, 14, 24);
                  }
                  .recharts-tooltip-item-list {
                    .recharts-tooltip-item {
                      padding: 0 !important;
                      span {
                        @include font(bold, 14, 20);
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;Y축과 X축을 따로 분리하고 X축만 스크롤이 생기도록 div를 분리했다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; 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;style2&quot; /&gt;
&lt;p style=&quot;text-align: center;&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;1070&quot; data-origin-height=&quot;468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nBjd6/btsMO8YrGfX/i9kmlSmNvgYbGCIfFzFr90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nBjd6/btsMO8YrGfX/i9kmlSmNvgYbGCIfFzFr90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nBjd6/btsMO8YrGfX/i9kmlSmNvgYbGCIfFzFr90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnBjd6%2FbtsMO8YrGfX%2Fi9kmlSmNvgYbGCIfFzFr90%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;1070&quot; height=&quot;468&quot; data-origin-width=&quot;1070&quot; data-origin-height=&quot;468&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://recharts.org/en-US/api&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://recharts.org/en-US/api&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://github.com/recharts/recharts/issues/1364#issuecomment-2608588147&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/recharts/recharts/issues/1364#issuecomment-2608588147&lt;/a&gt;&lt;/p&gt;</description>
      <category>개발/Javascript</category>
      <author>이나당</author>
      <guid isPermaLink="true">https://h-owo-ld.tistory.com/368</guid>
      <comments>https://h-owo-ld.tistory.com/368#entry368comment</comments>
      <pubDate>Sat, 3 May 2025 16:57:37 +0900</pubDate>
    </item>
    <item>
      <title>[VSCode] 갑자기 svg파일이 코드가 아닌 미리보기로 보일 때</title>
      <link>https://h-owo-ld.tistory.com/367</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1169&quot; data-origin-height=&quot;478&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfTaww/btsMyUZQZKE/QsZN6DW1QgkjjHlngyHrB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfTaww/btsMyUZQZKE/QsZN6DW1QgkjjHlngyHrB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfTaww/btsMyUZQZKE/QsZN6DW1QgkjjHlngyHrB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfTaww%2FbtsMyUZQZKE%2FQsZN6DW1QgkjjHlngyHrB1%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;1169&quot; height=&quot;478&quot; data-origin-width=&quot;1169&quot; data-origin-height=&quot;478&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;갑자기 svg가 코드가 아닌 미리보기로 뜨게됐다.&amp;nbsp;&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;&amp;nbsp;&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;&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkRTiz/btsMyUrWIo2/XFE83rXhCiInsK7LmfMCAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkRTiz/btsMyUrWIo2/XFE83rXhCiInsK7LmfMCAK/img.png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;312&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;39.34&quot; style=&quot;width: 38.8846%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkRTiz/btsMyUrWIo2/XFE83rXhCiInsK7LmfMCAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkRTiz%2FbtsMyUrWIo2%2FXFE83rXhCiInsK7LmfMCAK%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;700&quot; height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clAvxm/btsMx67F6sM/ORadUpW6QWBy5s1lpyFJO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clAvxm/btsMx67F6sM/ORadUpW6QWBy5s1lpyFJO0/img.png&quot; data-origin-width=&quot;1017&quot; data-origin-height=&quot;294&quot; data-is-animation=&quot;false&quot; style=&quot;width: 59.9526%;&quot; data-widthpercent=&quot;60.66&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clAvxm/btsMx67F6sM/ORadUpW6QWBy5s1lpyFJO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclAvxm%2FbtsMx67F6sM%2FORadUpW6QWBy5s1lpyFJO0%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;1017&quot; height=&quot;294&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;원래 미리보기로 보다가 일회성으로 코드만 보려는 분들은&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;VSCode검색창 열어서 Text Editor모드로 하면 해당 파일을 코드로 볼 수있다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; 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;style2&quot; /&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 나는 모든 svg파일을 코드로 보는걸 원하기때문에 설정을 수정해줬다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;VSCode 설정 (Ctrl + , 또는 Cmd + ,)&lt;/b&gt; 열기&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1090&quot; data-origin-height=&quot;615&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bspPs4/btsMxGnMQIi/QMFu1n141mPkKYQWjFhQj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bspPs4/btsMxGnMQIi/QMFu1n141mPkKYQWjFhQj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bspPs4/btsMxGnMQIi/QMFu1n141mPkKYQWjFhQj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbspPs4%2FbtsMxGnMQIi%2FQMFu1n141mPkKYQWjFhQj1%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;1090&quot; height=&quot;615&quot; data-origin-width=&quot;1090&quot; data-origin-height=&quot;615&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;우측상단 JSON으로 열기 &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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;203&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Fgigi/btsMx6zMHOi/XCDG472gJAbxA0ebbSSMQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Fgigi/btsMx6zMHOi/XCDG472gJAbxA0ebbSSMQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Fgigi/btsMx6zMHOi/XCDG472gJAbxA0ebbSSMQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFgigi%2FbtsMx6zMHOi%2FXCDG472gJAbxA0ebbSSMQ0%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;532&quot; height=&quot;203&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;203&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;pre id=&quot;code_1740702484891&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;workbench.editorAssociations&quot;: {
  &quot;*.svg&quot;: &quot;default&quot;
}&lt;/code&gt;&lt;/pre&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;&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;355&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvbhjq/btsMyuUFZww/Y2IpiLuDLHc3UkNyrLuAzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvbhjq/btsMyuUFZww/Y2IpiLuDLHc3UkNyrLuAzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvbhjq/btsMyuUFZww/Y2IpiLuDLHc3UkNyrLuAzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdvbhjq%2FbtsMyuUFZww%2FY2IpiLuDLHc3UkNyrLuAzk%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;355&quot; height=&quot;262&quot; data-origin-width=&quot;355&quot; data-origin-height=&quot;262&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;</description>
      <category>개발/Install, setting, etc</category>
      <author>이나당</author>
      <guid isPermaLink="true">https://h-owo-ld.tistory.com/367</guid>
      <comments>https://h-owo-ld.tistory.com/367#entry367comment</comments>
      <pubDate>Sat, 19 Apr 2025 18:04:53 +0900</pubDate>
    </item>
    <item>
      <title>[CSS/SCSS] SCSS에서 CSS 변수 중복 적용 문제 해결하기</title>
      <link>https://h-owo-ld.tistory.com/366</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React 프로젝트에서 SCSS를 사용할 때, &lt;span style=&quot;text-align: start;&quot;&gt;common.scss&lt;/span&gt;&amp;nbsp;파일에 CSS 변수도 선언하고 여러 파일에서 불러오는 구조를 만들었다.&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;이로 인해 CSS 변수가 페이지마다 중복 선언되는 문제가 발생했다.&lt;/p&gt;
&lt;pre id=&quot;code_1741830939094&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기존의 common.scss
:root {
    --btn-green-border: none;
    --btn-yellow-border: none;
    /* (이런 변수들이 반복됨...) */
}


[data-theme=light]{
...
}

...그외 scss 공통함수들이 있었다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그로인해 아래처럼 똑같은 변수선언이 여러개가 생겨서 스크롤이 엄청 쌓이게 되어버린..&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/lYgMT/btsMJw46qen/aYlKCPMvjCFkGdmNM1Xk70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lYgMT/btsMJw46qen/aYlKCPMvjCFkGdmNM1Xk70/img.png&quot; data-origin-width=&quot;274&quot; data-origin-height=&quot;332&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.9654%; margin-right: 10px;&quot; data-widthpercent=&quot;49.54&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lYgMT/btsMJw46qen/aYlKCPMvjCFkGdmNM1Xk70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlYgMT%2FbtsMJw46qen%2FaYlKCPMvjCFkGdmNM1Xk70%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;274&quot; height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbqBtt/btsMIekdQYV/Y25MmKbO8FAWNNyEZoRQqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbqBtt/btsMIekdQYV/Y25MmKbO8FAWNNyEZoRQqK/img.png&quot; data-origin-width=&quot;290&quot; data-origin-height=&quot;345&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.8718%;&quot; data-widthpercent=&quot;50.46&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbqBtt/btsMIekdQYV/Y25MmKbO8FAWNNyEZoRQqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbqBtt%2FbtsMIekdQYV%2FY25MmKbO8FAWNNyEZoRQqK%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;290&quot; height=&quot;345&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원인: SCSS @use의 동작 방식&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SCSS의&amp;nbsp;@use는&amp;nbsp;파일을&amp;nbsp;독립적으로&amp;nbsp;불러와서&amp;nbsp;중복&amp;nbsp;로드를&amp;nbsp;방지하는&amp;nbsp;기능이&amp;nbsp;없다. 그렇기에 어떤 파일에서 @use를 했든 간에, 사용한 곳마다 새롭게 로드가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;br /&gt;시도1&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;그래서 scss안에 css변수들을 global.scss라는 파일로 분리를 해봤지만 문제가 똑같았다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;css와 scss 파일을 분리했으나 중복 문제를 일으킨 원인&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. global.scss파일을 만든후 CSS 변수를 해당 파일로 옮기고,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. common.scss에서 @use './global.scss';로 불러오고,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3. 개별 스타일 파일(예: patient.page.scss)에서 @use '../common.scss';를 하였다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;결과 =&amp;gt; common.scss가 다시 불러질 때마다 global.scss가 중복 로드됨. === 똑같음&lt;/p&gt;
&lt;figure style=&quot;text-align: center;&quot; contenteditable=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/012.gif&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-name=&quot;012&quot; data-emoticon-type=&quot;friends1&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;emoticon&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/012.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741831072867&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// common.scss에서 global.scss를 사용함 (이러면 중복됨!!!!)
@use './global.scss';

@function dp($n) {
  @return calc($n / 16 * 1rem);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시도1의 파일. 이렇게 하면 common.scss가 불러와질 때마다 결국 global.scss도 함께 불러와지게 된다.&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;pre id=&quot;code_1741831092802&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// patient.page.scss
@use '../common.scss';  // common.scss에서 global.scss를 가져옴 (1번 로드)

// 다른 파일에서도 common.scss를 사용
@use '../common.scss';  // common.scss에서 global.scss를 또 가져옴 (2번 로드)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, common.scss를 여러 SCSS 파일에서 @use하면, global.scss도 중복 로드..&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;h3 data-ke-size=&quot;size23&quot;&gt;시도2&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; global.scss를 딱 한 번만 로드하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;global.scss를 오직 index.scss에서만 한 번 불러오고, common.scss에서 따로 global.scss를 불러오지 않도록 수정했다.&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;pre id=&quot;code_1741831154656&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// global.scss ( !!어디에서도 @use하지 않음)
:root {
  --base-font-size: 1rem;
  --btn-green-border: none;
  --btn-yellow-border: none;
}

[data-theme='light'] {
  --bg-color: #fff;
}

[data-theme='dark'] {
  --bg-color: #000;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1741831162473&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// common.scss (SCSS 변수와 함수만 포함 위에 @use 사용 X)
@function dp($n) {
  @return calc($n / 16 * 1rem);
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1741831170010&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// index.js (여기서만 global.scss를 불러옴)
export * from './common.scss'
export * from './components'
export * from './default.scss'
export * from './global.scss' // 여기에서 한 번만 불러옴
export * from './pages'&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1741831178470&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 개별 페이지 스타일 (patient.page.scss)
@use '../common.scss' as common;

.PatientPage {
  background-color: var(--bg-color);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;global.scss는 index.scss에서 한 번만 불러오므로 더이상 중복 적용되지 않는다.&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;뭔가 해결법이 간단했는데, 생각이 꼬여가지고 굳이 분리한 파일을 다시 @use 해서 가져온 이상한 시도를 했던 경험 @.@&lt;/p&gt;</description>
      <category>개발/HTML, CSS</category>
      <category>CSS</category>
      <category>SCSS</category>
      <author>이나당</author>
      <guid isPermaLink="true">https://h-owo-ld.tistory.com/366</guid>
      <comments>https://h-owo-ld.tistory.com/366#entry366comment</comments>
      <pubDate>Wed, 26 Mar 2025 08:05:42 +0900</pubDate>
    </item>
    <item>
      <title>[OTT JUMP] 크롬익스텐션 테스트 등록하기 (wavve 오프닝 건너뛰기, 다음회차 자동재생)</title>
      <link>https://h-owo-ld.tistory.com/365</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;281&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D6cDb/btsMzRbdRQp/1NqnU4MvIjG5n9Y7FKvwSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D6cDb/btsMzRbdRQp/1NqnU4MvIjG5n9Y7FKvwSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D6cDb/btsMzRbdRQp/1NqnU4MvIjG5n9Y7FKvwSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD6cDb%2FbtsMzRbdRQp%2F1NqnU4MvIjG5n9Y7FKvwSK%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;281&quot; height=&quot;73&quot; data-origin-width=&quot;281&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애니보는데 넷플릭스는 저 '끝내지 않는 Netflix'가 있어서 자동으로 넘어가지는데, 웨이브는 안되는게 귀찮아서 만들었다.&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;pre id=&quot;code_1740880665916&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  // 사용자가 특정 웹페이지를 열 때 사용할 JavaScript 또는 CSS 파일을 지정
  &quot;content_scripts&quot;: [
    {
      &quot;matches&quot;: [&quot;*://www.wavve.com/*&quot;], // 확장 프로그램을 실행할 웹사이트
      &quot;js&quot;: [&quot;content.js&quot;] // 실행할 js파일
    }
  ],
  // 확장 프로그램의 설명란에 표시되는 설명
  &quot;description&quot;: &quot;Wavve에서 오프닝 건너뛰기랑 다음회차를 자동으로 재생해주는 크롬 확장 프로그램이에요!&quot;,
  &quot;host_permissions&quot;: [&quot;*://www.wavve.com/*&quot;], // activeTab설정 후 특정사이트 접근을 위해 추가한 설정
  &quot;manifest_version&quot;: 3, // 현재 확장 프로그램이 사용할 크롬 확장 프로그램의 버전. 유일하게 지원되는 값은 3
  &quot;name&quot;: &quot;Wavve AutoPlay&quot;, // 크롬 웹스토어에 등록될 때 보일 확장 프로그램의 이름
  &quot;permissions&quot;: [&quot;activeTab&quot;], // 확장 프로그램 특별 권한, activeTab은 현재 보고있는 탭에서만 실행. 참고로 안상의 이유로 크롬에서는 불필요한 권한을 요청하면 스토어 등록이 어려움
  &quot;version&quot;: &quot;1.0.0&quot; // 확장 프로그램의 버전
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;배포할 코드를 만든 후 manifest.json 설정을 입력한다.&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;더 필요한건 여기서 확인&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.chrome.com/docs/extensions/reference/manifest?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.chrome.com/docs/extensions/reference/manifest?hl=ko&lt;/a&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;&amp;nbsp;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_img.png&quot; data-origin-width=&quot;1967&quot; data-origin-height=&quot;1279&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnulFx/btsMyFI3OOj/sRzlXvKPtAvkYtIb64yeE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnulFx/btsMyFI3OOj/sRzlXvKPtAvkYtIb64yeE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnulFx/btsMyFI3OOj/sRzlXvKPtAvkYtIb64yeE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnulFx%2FbtsMyFI3OOj%2FsRzlXvKPtAvkYtIb64yeE1%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;1967&quot; height=&quot;1279&quot; data-filename=&quot;edited_img.png&quot; data-origin-width=&quot;1967&quot; data-origin-height=&quot;1279&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;chrome://extensions/&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;구글 크롬에서 해당 링크로 이동하면 위와 같은 창이 뜬다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;우측 상단에 개발자 모드를 켜고, 왼쪽 위에 &quot;압축해제된 확장 프로그램을 로드합니다.&quot;를 선택하여 내 프로젝트 경로를 열어준다. 그러면 아래 전체확장 프로그램에 내가 만든 확장프로그램이 뜨고, 코드 수정후 새로고침을 누르면 다시 올릴 필요없이 최신 소스가 반영이 된다.&amp;nbsp;&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;테스트 할 때는 새로고침 후 확인 하면된다.&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;오류 발생시에는 오류 버튼이 생기고&amp;nbsp;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;538&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdHIXJ/btsMx857yCK/dkw6anjycYqKz6zSXrkWg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdHIXJ/btsMx857yCK/dkw6anjycYqKz6zSXrkWg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdHIXJ/btsMx857yCK/dkw6anjycYqKz6zSXrkWg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdHIXJ%2FbtsMx857yCK%2Fdkw6anjycYqKz6zSXrkWg0%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;1132&quot; height=&quot;538&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;538&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;&amp;nbsp;&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;p style=&quot;text-align: center;&quot; 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;722&quot; data-origin-height=&quot;458&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bubPOR/btsMyDSdzPI/ioQr0nWNGtKuExiTIAOThk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bubPOR/btsMyDSdzPI/ioQr0nWNGtKuExiTIAOThk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bubPOR/btsMyDSdzPI/ioQr0nWNGtKuExiTIAOThk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbubPOR%2FbtsMyDSdzPI%2FioQr0nWNGtKuExiTIAOThk%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;722&quot; height=&quot;458&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;458&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;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;content.js 는 내가 추가한 js파일.&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;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&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/8aD74/btsMz9ioaZG/tiBauvHzwKvsKBGpkSY7Lk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8aD74/btsMz9ioaZG/tiBauvHzwKvsKBGpkSY7Lk/img.gif&quot; data-origin-width=&quot;789&quot; data-origin-height=&quot;385&quot; data-filename=&quot;a.gif&quot; data-is-animation=&quot;true&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/8aD74/btsMz9ioaZG/tiBauvHzwKvsKBGpkSY7Lk/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8aD74%2FbtsMz9ioaZG%2FtiBauvHzwKvsKBGpkSY7Lk%2Fimg.gif&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;789&quot; height=&quot;385&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tJd1U/btsMyH1hIYW/zhvsapO1agabcGosU77Z4k/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tJd1U/btsMyH1hIYW/zhvsapO1agabcGosU77Z4k/img.gif&quot; data-origin-width=&quot;789&quot; data-origin-height=&quot;385&quot; data-is-animation=&quot;true&quot; data-widthpercent=&quot;50&quot; data-filename=&quot;a.gif&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tJd1U/btsMyH1hIYW/zhvsapO1agabcGosU77Z4k/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtJd1U%2FbtsMyH1hIYW%2FzhvsapO1agabcGosU77Z4k%2Fimg.gif&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;789&quot; height=&quot;385&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;쏘편하다 이말이에요~&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;아이콘 눌렀을 때 사용자가 컨트롤 할 수 있도록 추가해야하고,.. 다음화넘기고 이전화 넘기고 할 때랑&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;&lt;s&gt;&lt;b&gt;(를 핑계로 열심히 애니를 본다)&lt;/b&gt;&lt;/s&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;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;전체코드는 여기서 확인&lt;/p&gt;
&lt;figure id=&quot;og_1740881281870&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;javascript/wavve-skip at master &amp;middot; Ina-dang/javascript&quot; data-og-description=&quot;Contribute to Ina-dang/javascript development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/Ina-dang/javascript/tree/master/wavve-skip&quot; data-og-url=&quot;https://github.com/Ina-dang/javascript/tree/master/wavve-skip&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eE3605/hyYjkzQX8h/7dfA08czAVCfBgdSm6CiCk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/dtdpA0/hyYmUfaJDG/yptie4ICVhrYzNHsUTXuz0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/Ina-dang/javascript/tree/master/wavve-skip&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/Ina-dang/javascript/tree/master/wavve-skip&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eE3605/hyYjkzQX8h/7dfA08czAVCfBgdSm6CiCk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/dtdpA0/hyYmUfaJDG/yptie4ICVhrYzNHsUTXuz0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;javascript/wavve-skip at master &amp;middot; Ina-dang/javascript&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to Ina-dang/javascript development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;추가할 기능 있나 보면서 잘 굴러가는것 같으면&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;익스텐션 정식등록까지 해야지.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;그때는 정식등록하는 법 포스팅을 가져오겠당&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;001&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/001.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/001.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;참고:&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://support.google.com/chrome/a/answer/2714278?hl=ko&quot;&gt;https://support.google.com/chrome/a/answer/2714278?hl=ko&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.chrome.com/docs/extensions/get-started&quot;&gt;- https://developer.chrome.com/docs/extensions/get-started&lt;/a&gt;&lt;/p&gt;</description>
      <category>개발/Inafolio</category>
      <category>javascript</category>
      <category>자바스크립트</category>
      <category>크롬익스텐션</category>
      <category>크롬익스텐션테스트</category>
      <author>이나당</author>
      <guid isPermaLink="true">https://h-owo-ld.tistory.com/365</guid>
      <comments>https://h-owo-ld.tistory.com/365#entry365comment</comments>
      <pubDate>Sun, 2 Mar 2025 11:12:19 +0900</pubDate>
    </item>
    <item>
      <title>[React] 폰트사이즈 설정 조정하기</title>
      <link>https://h-owo-ld.tistory.com/364</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;우리회사는 RN이나 플러터를 사용하지않고 리액트로 앱을 만든다 ㅇ_ㅇ.&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;이번에 진행하는 곳에서 기획에 폰트 크기 조절기능을 넣어달라고 하셔서..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹에서 폰트크기를 조절해서 설정에서 폰트크기를 조절할 수 있도록 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초반부 코드라 조금 더 다듬어져서 지금화면이랑은 다르긴 하지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기록용으로 남겨두는 포스팅&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignLeft&quot; data-emoticon-type=&quot;friends2&quot; data-emoticon-name=&quot;005&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends2/large/005.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends2/large/005.png&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우헤헹 (한달에 1번 이상은 기술 블로그를 올리고 싶어서..)&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;pre id=&quot;code_1735781576901&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// App.jsx
import createCache from '@emotion/cache'
import { CacheProvider } from '@emotion/react'
import { setDefaultOptions } from 'date-fns'
import { ko } from 'date-fns/locale'
import { SnackbarProvider } from 'notistack'
import { useEffect } from 'react'
import Router from './router'
import { useFontSizeStore } from './status'
import { log } from './tools'

/**
 * 애플리케이션의 최상위 컴포넌트입니다.
 * 전역 상태 및 설정을 모든 하위 컴포넌트에 제공합니다.
 *
 * @example
 * ReactDOM.render(&amp;lt;App /&amp;gt;, document.getElementById('root'))
 */
const App = () =&amp;gt; {
  const cache = createCache({ key: 'css', prepend: true })
  const { fontSize, setFontSize } = useFontSizeStore()

  useEffect(() =&amp;gt; {
    setFontSize(fontSize)
    document.documentElement.style.setProperty('--base-font-size', `${fontSize}rem`)
  }, [fontSize, setFontSize])

  setDefaultOptions({ locale: ko })
  log.info('App')
  return (
    &amp;lt;CacheProvider value={cache}&amp;gt;
      &amp;lt;SnackbarProvider
        anchorOrigin={{ horizontal: 'center', vertical: 'bottom' }}
        autoHideDuration={3000}
        dense
        maxSnack={2}
        preventDuplicate
      &amp;gt;
        &amp;lt;Router /&amp;gt;
      &amp;lt;/SnackbarProvider&amp;gt;
    &amp;lt;/CacheProvider&amp;gt;
  )
}

export default App&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1739594208168&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;// globalStore.js

import { create } from 'zustand'
import { getLocalStorage, hasLocalStorage, setLocalStorage } from '../../tools'

const keyFontSize = 'FontRatio'

const useFontSizeStore = create((set) =&amp;gt; ({
  fontSize: hasLocalStorage(keyFontSize) ? getLocalStorage(keyFontSize) : 1,

  setFontSize: (size) =&amp;gt; {
    setLocalStorage(keyFontSize, size)
    set({ fontSize: size })

    // 스토리지 이벤트 트리거
    window.dispatchEvent(
      new StorageEvent('storage', { key: keyFontSize, newValue: JSON.stringify(size) })
    )
  },

  refreshFontSize: () =&amp;gt; {
    const storedFontSize = getLocalStorage(keyFontSize)
    set({ fontSize: storedFontSize ? storedFontSize : 1 })
  },
}))

// 스토리지 이벤트 리스너 추가
if (typeof window !== 'undefined') {
  window.addEventListener('storage', (event) =&amp;gt; {
    if (event.key === keyFontSize) {
      useFontSizeStore.getState().refreshFontSize()
    }
  })
}

export { useFontSizeStore }&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1735781756364&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//SettingPage.jsx

...

const FontSizeSlider = ({ action }) =&amp;gt; {
  const { fontSize, setFontSize } = useFontSizeStore()

  const handleChange = (event, newValue) =&amp;gt; {
    setFontSize(newValue)
    document.documentElement.style.setProperty('--base-font-size', `${newValue}rem`)
    action
  }
  return (
    &amp;lt;Box&amp;gt;
      &amp;lt;T&amp;gt;가&amp;lt;/T&amp;gt;
      &amp;lt;Slider
        onChange={handleChange}
        aria-label=&quot;fontsize&quot;
        value={fontSize}
        step={0.25}
        min={0.75}
        max={2}
        valueLabelDisplay=&quot;off&quot;
      /&amp;gt;
      &amp;lt;T&amp;gt;가&amp;lt;/T&amp;gt;
    &amp;lt;/Box&amp;gt;
  )
}

FontSizeSlider.propTypes = {
  action: PropTypes.func.isRequired, // 필수 함수 Prop
}
...
const SettingPage = () =&amp;gt; {
  const tag = '[Layout RightSideMenu][SettingPage]'
  log.debug(`${tag} `)
  const propsButtons = {
    ok: {
      label: '로그아웃',
    },
  }
  return (
    &amp;lt;Container className=&quot;SettingPage&quot; component=&quot;main&quot;&amp;gt;
      &amp;lt;Box component=&quot;section&quot;&amp;gt;
        &amp;lt;T component=&quot;h1&quot;&amp;gt;설정&amp;lt;/T&amp;gt;
      &amp;lt;/Box&amp;gt;
      &amp;lt;Box component=&quot;section&quot;&amp;gt;
        {Object.entries(settingConfig).map(([key, list]) =&amp;gt; (
          &amp;lt;SettingsList key={key} title={STR_SETTING[key]} list={list} /&amp;gt;
        ))}
      &amp;lt;/Box&amp;gt;
      &amp;lt;Buttons {...propsButtons} /&amp;gt;
    &amp;lt;/Container&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1735781826620&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// common.scss
...
:root {
  --base-font-size: 1rem;
}

@function dp($n) {
  @return calc($n / 16 * var(--base-font-size));
}

...&lt;/code&gt;&lt;/pre&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/cOCYIl/btsLA53eC8j/697fQsx81so8ULky6HhCg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOCYIl/btsLA53eC8j/697fQsx81so8ULky6HhCg0/img.png&quot; data-origin-width=&quot;356&quot; data-origin-height=&quot;376&quot; data-is-animation=&quot;false&quot; style=&quot;width: 35.2014%; margin-right: 10px;&quot; data-widthpercent=&quot;36.04&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOCYIl/btsLA53eC8j/697fQsx81so8ULky6HhCg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOCYIl%2FbtsLA53eC8j%2F697fQsx81so8ULky6HhCg0%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;356&quot; height=&quot;376&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mHF7E/btsLBwlOKRr/kEqK0XUDLDuIEANbJd38c1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mHF7E/btsLBwlOKRr/kEqK0XUDLDuIEANbJd38c1/img.png&quot; data-origin-width=&quot;359&quot; data-origin-height=&quot;419&quot; data-is-animation=&quot;false&quot; style=&quot;width: 31.855%; margin-right: 10px;&quot; data-widthpercent=&quot;32.61&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mHF7E/btsLBwlOKRr/kEqK0XUDLDuIEANbJd38c1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmHF7E%2FbtsLBwlOKRr%2FkEqK0XUDLDuIEANbJd38c1%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;359&quot; height=&quot;419&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0zQKZ/btsLBuaP4hT/ge45UhsBoBukvHX6S2Bn0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0zQKZ/btsLBuaP4hT/ge45UhsBoBukvHX6S2Bn0K/img.png&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;425&quot; data-is-animation=&quot;false&quot; style=&quot;width: 30.618%;&quot; data-widthpercent=&quot;31.35&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0zQKZ/btsLBuaP4hT/ge45UhsBoBukvHX6S2Bn0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0zQKZ%2FbtsLBuaP4hT%2Fge45UhsBoBukvHX6S2Bn0K%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;350&quot; height=&quot;425&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p 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;초반부에는 폰트크기뿐 아니라 요소들에 적용하는 여백이나 다른것들도 같이 묶어놨었는데,&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;폰트 조절되는 다른 앱들을 보니 폰트사이즈 키운다고 다 커지는게 아니라 어떤건 커지고 어떤부분(테이블, 네비 등)은 키워도 적용이 안되는 어플들이 대부분이라서 좀더 다른 앱 UI들 참고하면서 계속 개선 진행중 ~.~&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;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;6월 오픈이니까 좀더 다른 레퍼런스들 많이많이 써봐야지..&amp;nbsp;&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;생각보다 글자크기 선택 없는 어플도 많더라!-!&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;구현하면서 이유를 느끼게 돼..&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;friends2&quot; data-emoticon-name=&quot;006&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends2/large/006.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends2/large/006.png&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Javascript</category>
      <author>이나당</author>
      <guid isPermaLink="true">https://h-owo-ld.tistory.com/364</guid>
      <comments>https://h-owo-ld.tistory.com/364#entry364comment</comments>
      <pubDate>Sat, 15 Feb 2025 13:41:03 +0900</pubDate>
    </item>
  </channel>
</rss>