<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>종합장</title>
    <link>https://naahy0co.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Fri, 17 Jul 2026 06:55:01 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>naahy</managingEditor>
    <image>
      <title>종합장</title>
      <url>https://tistory1.daumcdn.net/tistory/5343469/attach/3798f1952b0541e5a1ca6506ab2dea2a</url>
      <link>https://naahy0co.tistory.com</link>
    </image>
    <item>
      <title>[SpringBoot + Nexacro] Nexacro 설치 및 프로젝트 생성과 연동</title>
      <link>https://naahy0co.tistory.com/85</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Nexacro 공부를 위해 SpringBoot + Oracle + Nexacro 토이 프로젝트를 진행하기로 함&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;기술 스택&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style5&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.5349%;&quot;&gt;Frontend&lt;/td&gt;
&lt;td style=&quot;width: 85.3488%;&quot;&gt;Nexacro N&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.5349%;&quot;&gt;Backend&lt;/td&gt;
&lt;td style=&quot;width: 85.3488%;&quot;&gt;SpringBoot 3.2.5 + MyBatis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.5349%;&quot;&gt;DB&lt;/td&gt;
&lt;td style=&quot;width: 85.3488%;&quot;&gt;Oracle XE 21c (Docker)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.5349%;&quot;&gt;빌드&lt;/td&gt;
&lt;td style=&quot;width: 85.3488%;&quot;&gt;Gradle&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;❗JPA가 아닌 MyBatis를 선택한 이유&lt;/b&gt;&lt;br /&gt;MyBatis는 직접 SQL문을 작성할 수 있고, DBMS별 문법을 세밀하게 제어할 수 있음&lt;br /&gt;또한 Oracle과의 궁합도 좋기 때문에 Oracle 공부도 더 할겸 MyBatis를 선택함&lt;/blockquote&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;  Nexacro 핵심 개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Form&lt;/b&gt;&lt;br /&gt;화면 하나 = Form 하나&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React의 Page 컴포넌트와 비슷한 개념&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;Dataset&lt;/b&gt;&lt;br /&gt;화면에서 데이터를 담는 그릇&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SpringBoot API 응답을 받아서 저장하고, Grid 같은 컴포넌트에 연결&lt;/p&gt;
&lt;pre id=&quot;code_1781242558835&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Dataset &amp;larr; API 응답 데이터 저장
   &amp;darr;
Grid    &amp;larr; Dataset 바라보며 화면에 표시&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;b&gt; Transaction&lt;/b&gt;&lt;br /&gt;Nexacro에서 SpringBoot API를 호출하는 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 fetch/axios 대신 Nexacro 전용 통신 방식을 사용함&lt;/p&gt;
&lt;pre id=&quot;code_1781242594109&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Nexacro 방식
this.transaction(
    &quot;조회&quot;,                        // 트랜잭션 ID
    &quot;SVC::getDashboard.do&quot;,        // 호출할 서비스 URL
    &quot;&quot;,                            // 전송할 Dataset (없으면 빈칸)
    &quot;dsDashboard=output&quot;,          // 응답 받을 Dataset
    &quot;&quot;,                            // 파라미터
    &quot;fn_callBack&quot;                  // 콜백 함수명
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Nexacro 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 아래 링크에서 Nexacro 체험판과 라이선스를 다운 받음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.tobesoft.com/product/nexacro&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.tobesoft.com/product/nexacro&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1781242436259&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;DEV.TOOL 비즈니스용 UI&amp;middot;UX 개발 소프트웨어의 공급부터 안정적 구축, 유지보수 수행까지 기업을 위한 서비스를 제공합니다.&quot; data-og-host=&quot;www.tobesoft.com&quot; data-og-source-url=&quot;https://www.tobesoft.com/product/nexacro&quot; data-og-url=&quot;https://www.tobesoft.com/product/nexacro&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.tobesoft.com/product/nexacro&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.tobesoft.com/product/nexacro&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;투비소프트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;DEV.TOOL 비즈니스용 UI&amp;middot;UX 개발 소프트웨어의 공급부터 안정적 구축, 유지보수 수행까지 기업을 위한 서비스를 제공합니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.tobesoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 과정은 다른 거 건드릴 필요 없이 기본 세팅된 대로 설치하면 됨&lt;/p&gt;
&lt;p 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;Nexacro 라이브러리 다운로드&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;xapi 라이브러리와 uiadapter 라이브러리가 필요함&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;xapi&lt;/b&gt;는 클라이언트(화면)와 서버 간의 데이터를 송수신하고 처리하기 위해 필요한 자바 기반 서버 모듈&lt;/li&gt;
&lt;li&gt;&lt;b&gt;uiadapter&lt;/b&gt;는 넥사크로와 서버 간 데이터 구조 차이를 보완해주는 모듈&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;아래 링크에서 com/nexacro 폴더로 이동 후 xapi 라이브러리와 uiadapter 라이브러리 다운&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mangosteen.tobesoft.co.kr/nexus/#browse/browse:maven-public&quot;&gt;https://mangosteen.tobesoft.co.kr/nexus/#browse/browse:maven-public&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1781242776846&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;Nexus Repository Manager&quot; data-og-description=&quot;&quot; data-og-host=&quot;mangosteen.tobesoft.co.kr&quot; data-og-source-url=&quot;https://mangosteen.tobesoft.co.kr/nexus/#browse/browse:maven-public&quot; data-og-url=&quot;https://mangosteen.tobesoft.co.kr/nexus/#browse/browse:maven-public&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://mangosteen.tobesoft.co.kr/nexus/#browse/browse:maven-public&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://mangosteen.tobesoft.co.kr/nexus/#browse/browse:maven-public&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Nexus Repository Manager&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;mangosteen.tobesoft.co.kr&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;❗이때 SpringBoot 3로 프로젝트를 설정했다면 Nexacro 라이브러리는 반드시 jakarta 버전으로 다운받아야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jakarta 버전이 아닌 라이브러리를 다운 받으면 해당 라이브러리는 javax를 사용하기 때문에 &lt;b&gt;java.lang.NoClassDefFoundError: javax/servlet/ServletOutputStream&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;453&quot; data-origin-height=&quot;696&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dRdDAV/dJMcahkvybQ/guPBaL5AZXhKw4R3V7WtIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dRdDAV/dJMcahkvybQ/guPBaL5AZXhKw4R3V7WtIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dRdDAV/dJMcahkvybQ/guPBaL5AZXhKw4R3V7WtIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdRdDAV%2FdJMcahkvybQ%2FguPBaL5AZXhKw4R3V7WtIK%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;453&quot; height=&quot;696&quot; data-origin-width=&quot;453&quot; data-origin-height=&quot;696&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  Nexacro 프로젝트 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상단 메뉴에서 새 프로젝트 생성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cutVjm/dJMcacKg1Zl/GpL554Mflkj4eAX2vGK6lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cutVjm/dJMcacKg1Zl/GpL554Mflkj4eAX2vGK6lk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cutVjm/dJMcacKg1Zl/GpL554Mflkj4eAX2vGK6lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcutVjm%2FdJMcacKg1Zl%2FGpL554Mflkj4eAX2vGK6lk%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;451&quot; height=&quot;194&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;194&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;넥사크로 프로젝트와 스프링부트 프로젝트를 &lt;b&gt;같은 폴더에 두지 않고 분리&lt;/b&gt;할 것이기 때문에 프로젝트 위치는 본인이 편한 경로로 지정하면 됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 이름 설정하고 Next 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;968&quot; data-origin-height=&quot;734&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lVSje/dJMcajigtdi/HGnv9fUeKKEguTUyzfHz8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lVSje/dJMcajigtdi/HGnv9fUeKKEguTUyzfHz8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lVSje/dJMcajigtdi/HGnv9fUeKKEguTUyzfHz8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlVSje%2FdJMcajigtdi%2FHGnv9fUeKKEguTUyzfHz8K%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;968&quot; height=&quot;734&quot; data-origin-width=&quot;968&quot; data-origin-height=&quot;734&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 설정은 본인이 만들고 싶은 화면대로 설정하면 됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 간단한 개인 공부용 프로젝트기 때문에 Screen은 Desktop, 템플릿은 Full로 지정함&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/doSAJl/dJMcabEwSNd/PIKLJ3h50nDSkyCY0KSjG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/doSAJl/dJMcabEwSNd/PIKLJ3h50nDSkyCY0KSjG1/img.png&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;720&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.445%; margin-right: 10px;&quot; data-widthpercent=&quot;50.03&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/doSAJl/dJMcabEwSNd/PIKLJ3h50nDSkyCY0KSjG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdoSAJl%2FdJMcabEwSNd%2FPIKLJ3h50nDSkyCY0KSjG1%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;720&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZITdA/dJMcagFUkB2/cS6U6q9YSc0XEaA0SJwnbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZITdA/dJMcagFUkB2/cS6U6q9YSc0XEaA0SJwnbk/img.png&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;720&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.3922%;&quot; data-widthpercent=&quot;49.97&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZITdA/dJMcagFUkB2/cS6U6q9YSc0XEaA0SJwnbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZITdA%2FdJMcagFUkB2%2FcS6U6q9YSc0XEaA0SJwnbk%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;937&quot; height=&quot;720&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;이후 Finish 눌러 프로젝트 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;TypeDefinition&amp;nbsp;&lt;/span&gt;경로 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nexacro에서 script를 작성할 때마다 일일이 모든 경로를 작성하기 어려우므로, 자주 쓰는 경로의 경우 별칭을 지정하면 편함&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;TypeDefinition - Services 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;250&quot; data-origin-height=&quot;320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HP77Y/dJMcahLwYGC/AAeyuoEvaoDoXiud7Bgak0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HP77Y/dJMcahLwYGC/AAeyuoEvaoDoXiud7Bgak0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HP77Y/dJMcahLwYGC/AAeyuoEvaoDoXiud7Bgak0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHP77Y%2FdJMcahLwYGC%2FAAeyuoEvaoDoXiud7Bgak0%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;250&quot; height=&quot;320&quot; data-origin-width=&quot;250&quot; data-origin-height=&quot;320&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;User Service에서 아래 내용 등록&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SVC - 백엔드 API 호출용. 본인의 로컬호스트 주소 e.g.) http://localhost:8080/&lt;/li&gt;
&lt;li&gt;nexacrolib - 넥사크로 엔진 라이브러리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;prefixID의 경우 본인이 편한대로 작성해도 됨&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;541&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IkOxw/dJMcahdIm8y/kW6oSScjS288myk1T16eOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IkOxw/dJMcahdIm8y/kW6oSScjS288myk1T16eOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IkOxw/dJMcahdIm8y/kW6oSScjS288myk1T16eOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIkOxw%2FdJMcahdIm8y%2FkW6oSScjS288myk1T16eOK%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;541&quot; data-origin-width=&quot;915&quot; data-origin-height=&quot;541&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  SpringBoot 프로젝트 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 다운 받았던 nexacro 관련 라이브러리를 스프링부트 프로젝트에 직접 추가해야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본인은 루트 폴더에 libs 폴더 생성 후 필요한 jar 파일들을 넣어두었음&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;187&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz7V3g/dJMcaiRbWq9/mMilk6EKeYIEqIekqsXtRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz7V3g/dJMcaiRbWq9/mMilk6EKeYIEqIekqsXtRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz7V3g/dJMcaiRbWq9/mMilk6EKeYIEqIekqsXtRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz7V3g%2FdJMcaiRbWq9%2FmMilk6EKeYIEqIekqsXtRk%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;187&quot; height=&quot;240&quot; data-origin-width=&quot;187&quot; data-origin-height=&quot;240&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;h3 data-ke-size=&quot;size23&quot;&gt;의존성 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle에 로컬 jar 의존성 추가 및 라이브러리 관련 추가 의존성 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련 내용은 &lt;a href=&quot;https://naahy0co.tistory.com/83&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://naahy0co.tistory.com/83&lt;/a&gt;&amp;nbsp;참고&lt;/p&gt;
&lt;pre id=&quot;code_1781322875097&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
    // 기존 의존성들 ...

    // Nexacro 라이브러리 (로컬 jar)
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    
    // Nexacro XAPI 의존성
    implementation 'jakarta.servlet.jsp:jakarta.servlet.jsp-api:3.1.1'
    implementation 'commons-lang:commons-lang:2.6'
}&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;로컬 jar에 대한 의존성을 새로 추가했으므로 gradle 빌드를 새로 해줘야 함&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;  SpringBoot - Nexacro 연동&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SpringBoot CORS 설정 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nexacro Studio에서 localhost로 API를 호출할 때 CORS 에러가 나기 때문에 미리 열어둬야 함&lt;/p&gt;
&lt;pre id=&quot;code_1781324858087&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping(&quot;/**&quot;)
                .allowedOriginPatterns(&quot;*&quot;)
                .allowedMethods(&quot;GET&quot;, &quot;POST&quot;, &quot;PUT&quot;, &quot;DELETE&quot;)
                .allowedHeaders(&quot;*&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Nexacro Config 작성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring&amp;nbsp;MVC의&amp;nbsp;기본&amp;nbsp;JSON&amp;nbsp;기반&amp;nbsp;요청/응답&amp;nbsp;처리&amp;nbsp;체계를&amp;nbsp;Nexacro&amp;nbsp;Dataset/XML&amp;nbsp;기반&amp;nbsp;처리&amp;nbsp;체계로&amp;nbsp;교체하는&amp;nbsp;설정&amp;nbsp;클래스&lt;/p&gt;
&lt;pre id=&quot;code_1781329411410&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import com.nexacro.java.xapi.tx.PlatformType;
import com.nexacro.uiadapter.jakarta.core.context.ApplicationContextProvider;
import com.nexacro.uiadapter.jakarta.core.resolve.NexacroHandlerMethodReturnValueHandler;
import com.nexacro.uiadapter.jakarta.core.resolve.NexacroMappingExceptionResolver;
import com.nexacro.uiadapter.jakarta.core.resolve.NexacroMethodArgumentResolver;
import com.nexacro.uiadapter.jakarta.core.resolve.NexacroRequestMappingHandlerAdapter;
import com.nexacro.uiadapter.jakarta.core.view.NexacroFileView;
import com.nexacro.uiadapter.jakarta.core.view.NexacroView;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

import java.util.List;

@Configuration
public class NexacroConfig extends WebConfig implements WebMvcRegistrations {

    /**
     * Spring Bean들을 Nexacro 내부에서도 참조할 수 있게 함
     */
    @Bean
    @Lazy(false)
    public ApplicationContextProvider applicationContextProvider() {
        return new ApplicationContextProvider();
    }

    /**
     * SpringAdapter를 NexacroAdapter로 교체
     * Spring MVC를 Nexacro용으로 교체
     */
    @Override
    public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
        return new NexacroRequestMappingHandlerAdapter();
    }

    /**
     * Controller 파라미터 해석기 등록
     * Dataset --&amp;gt; Java 객체 변환기
     */
    @Override
    public void addArgumentResolvers(List&amp;lt;HandlerMethodArgumentResolver&amp;gt; resolvers) {
        NexacroMethodArgumentResolver nexacroResolver = new NexacroMethodArgumentResolver();
        resolvers.add(nexacroResolver);
        super.addArgumentResolvers(resolvers);
    }

    /**
     * 응답 처리기
     * Controller 반환값(NexacroResult)을 Nexacro Dataset/XML 응답으로 변환
     */
    @Override
    public void addReturnValueHandlers(List&amp;lt;HandlerMethodReturnValueHandler&amp;gt; handlers) {
        NexacroHandlerMethodReturnValueHandler returnValueHandler = new NexacroHandlerMethodReturnValueHandler();

        // 실제 응답 생성기
        NexacroView nexacroView = new NexacroView();
        nexacroView.setDefaultContentType(PlatformType.CONTENT_TYPE_XML);
        nexacroView.setDefaultCharset(&quot;UTF-8&quot;);
        returnValueHandler.setView(nexacroView);

        // 파일 다운로드 전용
        NexacroFileView nexacroFileView = new NexacroFileView();
        returnValueHandler.setFileView(nexacroFileView);

        handlers.add(0, returnValueHandler);    // 핸들러 순서 명시

        super.addReturnValueHandlers(handlers);
    }

    /**
     * 넥사크로플랫폼 에러 처리 ExceptionResolver 등록
     * Exception &amp;rarr; Nexacro 에러 응답
     */
    @Override
    public void configureHandlerExceptionResolvers(List&amp;lt;HandlerExceptionResolver&amp;gt; resolvers) {

        NexacroView nexacroView = new NexacroView();
        nexacroView.setDefaultContentType(PlatformType.CONTENT_TYPE_XML);
        nexacroView.setDefaultCharset(&quot;UTF-8&quot;);

        NexacroMappingExceptionResolver nexacroException = new NexacroMappingExceptionResolver();

        nexacroException.setView(nexacroView);
        nexacroException.setShouldLogStackTrace(true);  // 서버 로그 출력
        nexacroException.setShouldSendStackTrace(true); // 클라이언트(Nexacro)에도 스택트레이스 전달
        nexacroException.setOrder(1);
        resolvers.add(nexacroException);

        super.configureHandlerExceptionResolvers(resolvers);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Nexacro 전용 Controller 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nexacro는 JSON이 아닌 자체 포맷으로 통신함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 REST API Controller와 별도로 &lt;b&gt;Nexacro 전용 Controller&lt;/b&gt;를 만들어야 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 컨트롤러가 NexacroResult를 반환하도록 작성&lt;/p&gt;
&lt;pre id=&quot;code_1781324971410&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import com.example.inventory.domain.DashboardDto;
import com.example.inventory.mapper.DashboardNexacroMapper;
import com.example.inventory.service.DashboardService;
import com.nexacro.uiadapter.jakarta.core.data.NexacroResult;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

// 매핑용 URL은 원하는 내용으로 작성. 추후 nexacro script에 작성해야 하는 것만 알면 됨
@Controller
@ResponseBody
@RequestMapping(&quot;/nexacro&quot;)
@RequiredArgsConstructor
public class NexacroDashboardController {

    private final DashboardService dashboardService;

    @GetMapping(&quot;/dashboard&quot;)
    public NexacroResult getDashboard() {
    	// 1.
        DashboardDto dashboard = dashboardService.getDashboard();
        
        // 2.
        return DashboardNexacroMapper.toResult(dashboard);	// DTO를 NexacroResult로 변환
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;service에서 대시보드 정보를 받아와 DTO에 저장 (각 service와 DTO는 본인 프로젝트에 맞게 작성)&lt;/li&gt;
&lt;li&gt;해당 DTO를 NexacroResult로 변환하는 Mapper 클래스&lt;/li&gt;
&lt;/ol&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;DashboardNexacroMapper&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTO를 Dataset으로 변환하여 NexacroResult에 해당 데이터셋 추가 후 반환하는 클래스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nexacro는 Dataset 단위로 데이터를 주고받으므로, 각각의 데이터셋 정보는 분리해서 넘겨주는 것이 좋음&lt;/p&gt;
&lt;pre id=&quot;code_1781329570366&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import com.example.inventory.domain.DashboardDto;
import com.example.inventory.domain.DashboardSummary;
import com.nexacro.uiadapter.jakarta.core.data.NexacroResult;

import java.util.List;

public class DashboardNexacroMapper {

    public static NexacroResult toResult(DashboardDto dashboardDto) {
        NexacroResult result = new NexacroResult();

        // 요약 카드 - DataSet
        // DashboardSummary는 아래 내용을 담은 DTO
        DashboardSummary summary = new DashboardSummary(
                dashboardDto.getTotalItemCount(),
                dashboardDto.getTotalStockQty(),
                dashboardDto.getLowStockCount(),
                dashboardDto.getCategoryCount()
        );

        result.addDataSet(&quot;dsSummary&quot;, List.of(summary));
        
        /**
          * summary DTO를 새로 만들지 않고
          * 아래처럼 직접 데이터셋 생성 후 일일이 붙이는 방법도 있음
          * 관련 코드가 너무 길어지기 때문에 따로 DTO를 새로 선언한 것
        DataSet dsSummary = new DataSet(&quot;dsSummary&quot;);
        dsSummary.addColumn(&quot;totalItemCount&quot;, &quot;INT&quot;);
        dsSummary.addColumn(&quot;totalStockQty&quot;,  &quot;INT&quot;);
        dsSummary.addColumn(&quot;lowStockCount&quot;,  &quot;INT&quot;);
        dsSummary.addColumn(&quot;categoryCount&quot;,  &quot;INT&quot;);
        int row = dsSummary.newRow();
        dsSummary.set(row, &quot;totalItemCount&quot;, dashboardDto.getTotalItemCount());
        dsSummary.set(row, &quot;totalStockQty&quot;,  dashboardDto.getTotalStockQty());
        dsSummary.set(row, &quot;lowStockCount&quot;,  dashboardDto.getLowStockCount());
        dsSummary.set(row, &quot;categoryCount&quot;,  dashboardDto.getCategoryCount());
        outData.addDataSet(dsSummary);
        */

        // 카테고리별 재고
        result.addDataSet(
                &quot;dsCategoryStock&quot;,
                dashboardDto.getCategoryStocks()
        );

        ...	// 이후 위와 같이 필요한 Dataset을 각각 추가

        return result;
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두 작성 후 실행했을 때 문제 없이 실행 되면 연동이 잘 된 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  Nexacro 화면 작성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좌측 메뉴에서 FrameBase - Form_Work 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;266&quot; data-origin-height=&quot;543&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCSHFW/dJMcacKg4nK/ISdTqNOkdGNaFDdOWkoLb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCSHFW/dJMcacKg4nK/ISdTqNOkdGNaFDdOWkoLb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCSHFW/dJMcacKg4nK/ISdTqNOkdGNaFDdOWkoLb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCSHFW%2FdJMcacKg4nK%2FISdTqNOkdGNaFDdOWkoLb1%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;266&quot; height=&quot;543&quot; data-origin-width=&quot;266&quot; data-origin-height=&quot;543&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;프로젝트 생성 시 템플릿을 Full로 선택했기 때문에 빈 화면일 것&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;노란 원으로 표시된 것이 왼쪽부터 button, static, grid&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;211&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blW670/dJMcai4FC6E/BkOcVQbEVhEih2mTFcbyh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blW670/dJMcai4FC6E/BkOcVQbEVhEih2mTFcbyh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blW670/dJMcai4FC6E/BkOcVQbEVhEih2mTFcbyh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblW670%2FdJMcai4FC6E%2FBkOcVQbEVhEih2mTFcbyh1%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;994&quot; height=&quot;211&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;211&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;데이터 연동이 잘 이루어지는지만 먼저 확인할 것이기 때문에 간단하게 static 4개와 grid 1개만 화면에 배치함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 각각의 static은 dsSummary의 내용을, grid는 dsCategoryStock 내용을 저장할 것 -&amp;gt; 위의 &lt;b&gt;DashboardNexacroMapper&lt;/b&gt;에서 nexacroResult에 추가한 dataset 정보가 표시되는 것&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;619&quot; data-origin-height=&quot;342&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PFo5x/dJMcabq21yl/u5eGMyArHKbD1wKW2hkC91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PFo5x/dJMcabq21yl/u5eGMyArHKbD1wKW2hkC91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PFo5x/dJMcabq21yl/u5eGMyArHKbD1wKW2hkC91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPFo5x%2FdJMcabq21yl%2Fu5eGMyArHKbD1wKW2hkC91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;619&quot; height=&quot;342&quot; data-origin-width=&quot;619&quot; data-origin-height=&quot;342&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;각 컴포넌트의 아이디를 다음처럼 바꾼다. (static 컴포넌트의 경우 화면에 표시될 text도 아래처럼 변경)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 177px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 15.5814%; height: 17px;&quot;&gt;ID&lt;/td&gt;
&lt;td style=&quot;width: 14.7674%; height: 17px;&quot;&gt;컴포넌트&lt;/td&gt;
&lt;td style=&quot;width: 44.4187%; height: 17px;&quot;&gt;text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 15.5814%;&quot;&gt;stTotalItem&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 14.7674%;&quot;&gt;Static&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 44.4187%;&quot;&gt;0 개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 15.5814%;&quot;&gt;stTotalStock&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 14.7674%;&quot;&gt;Static&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 44.4187%;&quot;&gt;0 개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 15.5814%;&quot;&gt;stLowStock&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 14.7674%;&quot;&gt;Static&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 44.4187%;&quot;&gt;0 개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; width: 15.5814%;&quot;&gt;stCategory&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 14.7674%;&quot;&gt;Static&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 44.4187%;&quot;&gt;0 개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.5814%;&quot;&gt;grdCategory&lt;/td&gt;
&lt;td style=&quot;width: 14.7674%;&quot;&gt;Grid&lt;/td&gt;
&lt;td style=&quot;width: 44.4187%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상단 메뉴에서 Dataset 클릭 후, 페이지 아무 곳이나 클릭하면 하단에 dataset이 추가됨&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/dcz3Sw/dJMcabq21hv/0Iyy54vIe4eRYLPMAZNZDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcz3Sw/dJMcabq21hv/0Iyy54vIe4eRYLPMAZNZDk/img.png&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;148&quot; data-is-animation=&quot;false&quot; style=&quot;width: 62.7792%; margin-right: 10px;&quot; data-widthpercent=&quot;63.52&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcz3Sw/dJMcabq21hv/0Iyy54vIe4eRYLPMAZNZDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdcz3Sw%2FdJMcabq21hv%2F0Iyy54vIe4eRYLPMAZNZDk%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;331&quot; height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wBLHW/dJMcai4GLqK/ze5ZX05PiTeaKyO9hKAkVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wBLHW/dJMcai4GLqK/ze5ZX05PiTeaKyO9hKAkVk/img.png&quot; data-origin-width=&quot;158&quot; data-origin-height=&quot;123&quot; data-is-animation=&quot;false&quot; style=&quot;width: 36.058%;&quot; data-widthpercent=&quot;36.48&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wBLHW/dJMcai4GLqK/ze5ZX05PiTeaKyO9hKAkVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwBLHW%2FdJMcai4GLqK%2Fze5ZX05PiTeaKyO9hKAkVk%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;158&quot; height=&quot;123&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;dataset을 더블 클릭하여 id와 열 정보를 아래처럼 입력&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2I8tu/dJMcadbetLi/KozduQuXHAWwIJhE3xfqQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2I8tu/dJMcadbetLi/KozduQuXHAWwIJhE3xfqQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2I8tu/dJMcadbetLi/KozduQuXHAWwIJhE3xfqQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2I8tu%2FdJMcadbetLi%2FKozduQuXHAWwIJhE3xfqQ0%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;563&quot; height=&quot;246&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;246&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;이후 dataset을 마우스로 드래그하여 grid 컴포넌트에 놓으면 해당 ds가 grid 컴포넌트에 바인딩 됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터셋이 바인딩 된 grid 컴포넌트를 더블 클릭하여, 표시될 텍스트를 아래처럼 변경해보자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;797&quot; data-origin-height=&quot;538&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6g9ps/dJMcagFVwEu/vhEKELFJUoyo3Z2dCilr40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6g9ps/dJMcagFVwEu/vhEKELFJUoyo3Z2dCilr40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6g9ps/dJMcagFVwEu/vhEKELFJUoyo3Z2dCilr40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6g9ps%2FdJMcagFVwEu%2FvhEKELFJUoyo3Z2dCilr40%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;797&quot; height=&quot;538&quot; data-origin-width=&quot;797&quot; data-origin-height=&quot;538&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;h3 data-ke-size=&quot;size23&quot;&gt;Script 작성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지 하단을 보면 script 메뉴가 있을 것이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 메뉴를 클릭하면 빈 화면이 뜰 건데, 그 화면에 아래 내용을 입력하면 됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;springBoot에서 받아온 정보를 form의 컴포넌트와 연결하는 것&lt;/p&gt;
&lt;pre id=&quot;code_1781507109021&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 화면 로드 시 자동 실행
this.Dashboard_onload = function(obj, e) {
    this.fn_getDashboard();
}

// 대시보드 조회
this.fn_getDashboard = function() {
    this.transaction(
        &quot;getDashboard&quot;,                          // 트랜잭션 ID
        &quot;SVC::nexacro/dashboard&quot;,          // 서비스 URL - SpringBoot Controller
        &quot;&quot;,                                      // 전송 Dataset (없음)
        &quot;dsSummary=dsSummary &quot;
        + &quot;dsCategoryStock=dsCategoryStock &quot;
        + &quot;추가 데이터셋 있으면 작성 &quot;,           // 수신 Dataset
        &quot;&quot;,                                      // 파라미터
        &quot;fn_getDashboardCallBack&quot;                // 콜백 함수
    );
}

// 조회 완료 콜백
this.fn_getDashboardCallBack = function(svcId, errorCode, errorMsg) {
    if (errorCode &amp;lt; 0) {
        alert(&quot;오류 발생: &quot; + errorMsg);
        return;
    }

    // 요약 카드 수치 업데이트
    this.stTotalItem.set_text(this.dsSummary.getColumn(0, &quot;totalItemCount&quot;) + &quot; 개&quot;);
    this.stTotalStock.set_text(this.dsSummary.getColumn(0, &quot;totalStockQty&quot;) + &quot; 개&quot;);
    this.stLowStock.set_text(this.dsSummary.getColumn(0, &quot;lowStockCount&quot;) + &quot; 개&quot;);
    this.stCategory.set_text(this.dsSummary.getColumn(0, &quot;categoryCount&quot;) + &quot; 개&quot;);

    // Grid는 Dataset에 바인딩되어 있으면 자동으로 표시
}&lt;/code&gt;&lt;/pre&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;p data-ke-size=&quot;size16&quot;&gt;상단의 launch 버튼을 눌러 화면을 실행해보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 버튼을 누르면 어떤 화면이 뜰 건데, 뭐 건드릴 필요 없이 Run 실행하면 됨&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;189&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dsXOux/dJMcacwIQcc/GK2aqGJNg8dQN2zMTeyfy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dsXOux/dJMcacwIQcc/GK2aqGJNg8dQN2zMTeyfy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dsXOux/dJMcacwIQcc/GK2aqGJNg8dQN2zMTeyfy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdsXOux%2FdJMcacwIQcc%2FGK2aqGJNg8dQN2zMTeyfy1%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;584&quot; height=&quot;189&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;189&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연동이 잘 된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 데이터가 잘 주고받아지는지 확인하려 했던 것이기 때문에 나머지 화면 구현은 나중에...&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;863&quot; data-origin-height=&quot;368&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbFmIe/dJMcabkjtgu/4CVzl71lzngn8YGcn50VH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbFmIe/dJMcabkjtgu/4CVzl71lzngn8YGcn50VH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbFmIe/dJMcabkjtgu/4CVzl71lzngn8YGcn50VH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbFmIe%2FdJMcabkjtgu%2F4CVzl71lzngn8YGcn50VH1%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;863&quot; height=&quot;368&quot; data-origin-width=&quot;863&quot; data-origin-height=&quot;368&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;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://123okk2.tistory.com/538#google_vignette&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://123okk2.tistory.com/538#google_vignette&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1781241739539&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;[Nexacro + Spring Boot] 넥사크로(1/3) - 프로젝트 생성 및 설정&quot; data-og-description=&quot;다음 프로젝트에서 프론트엔드로 넥사크로를 사용하기로 결정했다. 처음 사용해보는 개발툴이라 열심히 연습하며 느낀 점은... 이번에야 이미 결정된 일이라 어쩔 수 없다지만 솔직히 이걸 두 &quot; data-og-host=&quot;123okk2.tistory.com&quot; data-og-source-url=&quot;https://123okk2.tistory.com/538#google_vignette&quot; data-og-url=&quot;https://123okk2.tistory.com/538&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/SdTM6/dJMb9jOvgHq/r2HXnQ33aevT1YVyaFAXxK/img.png?width=772&amp;amp;height=711&amp;amp;face=0_0_772_711,https://scrap.kakaocdn.net/dn/b4Coyj/dJMb9dHwyQp/WkHNcnqu6dQEU6SlQEAlIK/img.png?width=772&amp;amp;height=711&amp;amp;face=0_0_772_711,https://scrap.kakaocdn.net/dn/bI7tnD/dJMb9iIPgLb/Fqk2RawQvimZedJKOunuYK/img.png?width=1056&amp;amp;height=980&amp;amp;face=0_0_1056_980&quot;&gt;&lt;a href=&quot;https://123okk2.tistory.com/538#google_vignette&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://123okk2.tistory.com/538#google_vignette&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/SdTM6/dJMb9jOvgHq/r2HXnQ33aevT1YVyaFAXxK/img.png?width=772&amp;amp;height=711&amp;amp;face=0_0_772_711,https://scrap.kakaocdn.net/dn/b4Coyj/dJMb9dHwyQp/WkHNcnqu6dQEU6SlQEAlIK/img.png?width=772&amp;amp;height=711&amp;amp;face=0_0_772_711,https://scrap.kakaocdn.net/dn/bI7tnD/dJMb9iIPgLb/Fqk2RawQvimZedJKOunuYK/img.png?width=1056&amp;amp;height=980&amp;amp;face=0_0_1056_980');&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;[Nexacro + Spring Boot] 넥사크로(1/3) - 프로젝트 생성 및 설정&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;123okk2.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.naver.com/goodee0205/224071956196&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://blog.naver.com/goodee0205/224071956196&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1781242150978&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;[개발자국비교육]Mybatis vs JPA 어떤걸 선택하지?&quot; data-og-description=&quot;웹 백엔드 개발을 하다 보면 데이터베이스와 연결하는 방식으로 MyBatis 또는 JPA를 많이 접하게 됩니...&quot; data-og-host=&quot;blog.naver.com&quot; data-og-source-url=&quot;https://blog.naver.com/goodee0205/224071956196&quot; data-og-url=&quot;https://blog.naver.com/goodee0205/224071956196&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/g6mzg/dJMb9jgFHU2/Kb9ImgnsxBg79USEwL8xI1/img.png?width=743&amp;amp;height=743&amp;amp;face=0_0_743_743&quot;&gt;&lt;a href=&quot;https://blog.naver.com/goodee0205/224071956196&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.naver.com/goodee0205/224071956196&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/g6mzg/dJMb9jgFHU2/Kb9ImgnsxBg79USEwL8xI1/img.png?width=743&amp;amp;height=743&amp;amp;face=0_0_743_743');&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;[개발자국비교육]Mybatis vs JPA 어떤걸 선택하지?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;웹 백엔드 개발을 하다 보면 데이터베이스와 연결하는 방식으로 MyBatis 또는 JPA를 많이 접하게 됩니...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.naver.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;</description>
      <category>SpringBoot</category>
      <category>Nexacro</category>
      <category>SpringBoot</category>
      <author>naahy</author>
      <guid isPermaLink="true">https://naahy0co.tistory.com/85</guid>
      <comments>https://naahy0co.tistory.com/85#entry85comment</comments>
      <pubDate>Mon, 15 Jun 2026 16:13:35 +0900</pubDate>
    </item>
    <item>
      <title>[트러블슈팅/SpringBoot] Nexacro Dataset 인식 안 됨</title>
      <link>https://naahy0co.tistory.com/84</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt; 문제1&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nexacro와 SpringBoot를 연동하여 데이터를 주고 받으려는데 아래 에러가 발생함&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;Could not write JSON: Infinite recursion (StackOverflowError)
...
java.lang.IllegalStateException: getOutputStream() has already been called for this response&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;원인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 내가 작성한 Nexacro 전용 Controller 코드&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@RestController
@RequestMapping(&quot;/nexacro&quot;)
@RequiredArgsConstructor
public class NexacroDashboardController {

    @GetMapping(&quot;/dashboard&quot;)
    public NexacroResult getDashboard() {
        ...
        return result;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 &lt;code&gt;@RestController&lt;/code&gt;는 내부적으로 &lt;code&gt;@Controller&lt;/code&gt; + &lt;code&gt;@ResponseBody&lt;/code&gt;와 같음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 NexacroResult를 JSON으로 변환하여 응답하려고 하는데, NexacroResult는 내부에 Dataset, PlatformData, View 등의 객체를 갖고 있기 때문에 JSON 직렬화를 시도하면 A --&amp;gt; B --&amp;gt; C --&amp;gt; A 식으로 순환 참조가 발생함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;code&gt;Infinite recursion (StackOverflowError)&lt;/code&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;첫 번째 오류 발생 후, NexacroMappingExceptionResolver가 예외 응답을 내려 하는데 이미 Spring이 JSON Writer를 사용하여 JSON 응답을 내기 시작함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 상태에서 NexacroView가 XML OutputStream을 사용하려 시도했기 때문에 두 번째 오류가 발생한 것&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 data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@RestController&lt;/code&gt;를 &lt;code&gt;@Controller&lt;/code&gt;로 변경&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 NexacroAdapter가 NexacroResult --&amp;gt; Dataset --&amp;gt; XML로 처리&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 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;773&quot; data-origin-height=&quot;410&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xyYDB/dJMcagFUpzh/qpv7K6EwB4epuur6sEKCZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xyYDB/dJMcagFUpzh/qpv7K6EwB4epuur6sEKCZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xyYDB/dJMcagFUpzh/qpv7K6EwB4epuur6sEKCZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxyYDB%2FdJMcagFUpzh%2Fqpv7K6EwB4epuur6sEKCZ1%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;773&quot; height=&quot;410&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;410&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;h2 data-ke-size=&quot;size26&quot;&gt; 문제2&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTO를 Dataset으로 변환하여 NexacroResult로 보내는 코드를 아래와 같이 작성&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;public static NexacroResult toResult(DashboardDto dashboardDto) {
    NexacroResult result = new NexacroResult();

    // 요약 카드
    DashboardSummary summary = new DashboardSummary(
            dashboardDto.getTotalItemCount(),
            ...
    );

    result.addDataSet(&quot;dsSummary&quot;, List.of(summary));

    // 카테고리별 재고
    result.addDataSet(
            &quot;dsCategoryStock&quot;,
            dashboardDto.getCategoryStocks()
    );

    ...

    return result;
}&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;그러나 Nexacro에서 dsSummary를 인식하지 못하는 상황 발생&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1199&quot; data-origin-height=&quot;107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/npN4A/dJMcajbp0ta/Kt4tuj8sqvkBJfXUqUPYk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/npN4A/dJMcajbp0ta/Kt4tuj8sqvkBJfXUqUPYk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/npN4A/dJMcajbp0ta/Kt4tuj8sqvkBJfXUqUPYk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnpN4A%2FdJMcajbp0ta%2FKt4tuj8sqvkBJfXUqUPYk1%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;1199&quot; height=&quot;107&quot; data-origin-width=&quot;1199&quot; data-origin-height=&quot;107&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;trace()&lt;/code&gt;를 통해 데이터셋의 상태를 확인해보았더니 아래와 같이 비어있는 것을 확인할 수 있었다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;414&quot; data-origin-height=&quot;124&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7HqcF/dJMcabdp8uP/YxzIKwUuhE17JLtcMsz3xK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7HqcF/dJMcabdp8uP/YxzIKwUuhE17JLtcMsz3xK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7HqcF/dJMcabdp8uP/YxzIKwUuhE17JLtcMsz3xK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7HqcF%2FdJMcabdp8uP%2FYxzIKwUuhE17JLtcMsz3xK%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;414&quot; height=&quot;124&quot; data-origin-width=&quot;414&quot; data-origin-height=&quot;124&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 데이터셋을 제외한 다른 데이터셋은 인식이 잘 되는 상황이었기에 연동 자체가 실패한 것은 아니었음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;원인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인식되지 않은 데이터셋의 DTO를 아래와 같이 immutable 스타일로 작성함&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class DashboardSummary {

    private int totalItemCount;
    private int totalStockQty;
    private int lowStockCount;
    private int categoryCount;

}&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;Nexacro Adapter가 내부적으로 Java Bean --&amp;gt; Dataset 변환을 할 때 다음 조건들을 기대하는 경우가 많음&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본 생성자(NoArgsConstructor)&lt;/li&gt;
&lt;li&gt;setter/getter&lt;/li&gt;
&lt;li&gt;일반적인 JavaBean 형태&lt;/li&gt;
&lt;li&gt;복합 타입이 아닌 단순 DTO&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;그러나 내가 작성한 summary DTO는 기본 생성자와 setter가 없는 읽기 전용 객체이기 때문에, 어댑터가 데이터셋으로 제대로 변환하지 못한 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 다른 DTO는 완전한 JavaBean 형태이기 때문에 Dataset 생성에 성공함&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 data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@Setter&lt;/code&gt;, &lt;code&gt;@NoArgsConstructor&lt;/code&gt;를 추가하여 일반 DTO로 변경&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 data-ke-size=&quot;size16&quot;&gt;원래 데이터를 인식하지 못하고 '0개'로 뜨던 값들이 제대로 뜨는 것을 확인할 수 있음&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1195&quot; data-origin-height=&quot;137&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CHKII/dJMb9904rzA/U3jKeuQpTUnTDMIz7tvfMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CHKII/dJMb9904rzA/U3jKeuQpTUnTDMIz7tvfMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CHKII/dJMb9904rzA/U3jKeuQpTUnTDMIz7tvfMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCHKII%2FdJMb9904rzA%2FU3jKeuQpTUnTDMIz7tvfMK%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;1195&quot; height=&quot;137&quot; data-origin-width=&quot;1195&quot; data-origin-height=&quot;137&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>트러블슈팅</category>
      <category>Nexacro</category>
      <category>SpringBoot</category>
      <author>naahy</author>
      <guid isPermaLink="true">https://naahy0co.tistory.com/84</guid>
      <comments>https://naahy0co.tistory.com/84#entry84comment</comments>
      <pubDate>Sat, 13 Jun 2026 17:11:24 +0900</pubDate>
    </item>
    <item>
      <title>[트러블슈팅/SpringBoot] Nexacro 연동 시 발생한 의존성 문제</title>
      <link>https://naahy0co.tistory.com/83</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;SpringBoot 3와 Nexacro N을 연동하여 transaction 통신을 구현하던 중 서버 실행 및 통신 과정에서 여러 오류가 순차적으로 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 환경&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java 17&lt;/li&gt;
&lt;li&gt;SpringBoot 3.2.5&lt;/li&gt;
&lt;li&gt;MyBatis 3.0.3&lt;/li&gt;
&lt;li&gt;Nexacro N&lt;/li&gt;
&lt;li&gt;gradle&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; 문제1 - javax.servlet.ServletOutputStream&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 코드를 작성하고 SpringBoot 프로젝트를 실행했는데 아래와 같은 오류 발생&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;Caused by: java.lang.NoClassDefFoundError: javax/servlet/ServletOutputStream&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;원인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에 사용하기 위해 다운받았던 Nexacro uiadapter 라이브러리가 Spring Boot 2.x 환경용(javax.servlet 기반)이었기 때문&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 개발 환경은 SpringBoot3이기 때문에 javax 패키지를 찾을 수 없어 'NoClassDefFoundError'가 발생한 것&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 data-ke-size=&quot;size16&quot;&gt;아래 링크에서 버전에 맞는 라이브러리 다운&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mangosteen.tobesoft.co.kr/nexus/#browse/browse:maven-public&quot;&gt;https://mangosteen.tobesoft.co.kr/nexus/#browse/browse:maven-public&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1781189960395&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;Nexus Repository Manager&quot; data-og-description=&quot;&quot; data-og-host=&quot;mangosteen.tobesoft.co.kr&quot; data-og-source-url=&quot;https://mangosteen.tobesoft.co.kr/nexus/#browse/browse:maven-public&quot; data-og-url=&quot;https://mangosteen.tobesoft.co.kr/nexus/#browse/browse:maven-public&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://mangosteen.tobesoft.co.kr/nexus/#browse/browse:maven-public&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://mangosteen.tobesoft.co.kr/nexus/#browse/browse:maven-public&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Nexus Repository Manager&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;mangosteen.tobesoft.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;com/nexacro&lt;/b&gt; 폴더로 이동하여 필요한 라이브러리의 jakarta 버전 다운로드&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;453&quot; data-origin-height=&quot;696&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d6nsuG/dJMcacpVVGC/8YS1t6UTpeY73KD9vmHRF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d6nsuG/dJMcacpVVGC/8YS1t6UTpeY73KD9vmHRF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d6nsuG/dJMcacpVVGC/8YS1t6UTpeY73KD9vmHRF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd6nsuG%2FdJMcacpVVGC%2F8YS1t6UTpeY73KD9vmHRF0%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;453&quot; height=&quot;696&quot; data-origin-width=&quot;453&quot; data-origin-height=&quot;696&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;다운 받고 싶은 파일의 버전을 확인하고, 해당 jar 파일 링크를 클릭하면 다운 받을 수 있음&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;653&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2S53S/dJMcageLKBV/rO6e9kegpsytu4aTK1RyO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2S53S/dJMcageLKBV/rO6e9kegpsytu4aTK1RyO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2S53S/dJMcageLKBV/rO6e9kegpsytu4aTK1RyO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2S53S%2FdJMcageLKBV%2FrO6e9kegpsytu4aTK1RyO0%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;1600&quot; height=&quot;653&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;653&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다운 받은 파일을 프로젝트 폴더에 삽입&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Nexacro 라이브러리는 gradle을 통해 다운 받을 수 없기 때문에 직접 라이브러리 파일들을 넣어줘야 함)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;328&quot; data-origin-height=&quot;167&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4gZiA/dJMcahSew0M/Gt4YudpGHAc4pmqHhRlAs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4gZiA/dJMcahSew0M/Gt4YudpGHAc4pmqHhRlAs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4gZiA/dJMcahSew0M/Gt4YudpGHAc4pmqHhRlAs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4gZiA%2FdJMcahSew0M%2FGt4YudpGHAc4pmqHhRlAs1%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;328&quot; height=&quot;167&quot; data-origin-width=&quot;328&quot; data-origin-height=&quot;167&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;h2 data-ke-size=&quot;size26&quot;&gt; 문제2 - jakarta.servlet.jsp.JspWriter&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 오류는 해결 됐으나 아래와 같이 추가 오류가 발생함&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;java.lang.ClassNotFoundException: jakarta.servlet.jsp.JspWriter&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;원인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nexacro XAPI가 응답 데이터를 생성하는 과정에서 JSP API를 참조함. 아래 위치에서 오류 발생&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;com.nexacro.java.xapi.tx.HttpPlatformResponse.sendData()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Controller 실행 -&amp;gt; 에러 발생 -&amp;gt; NexacroView가 에러 응답 생성 -&amp;gt; XAPI가 응답을 XML/SSV로 변환 -&amp;gt; JspWriter 클래스를 찾으려 함 -&amp;gt; 없음 -&amp;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;Spring Boot 3는 JSP 관련 라이브러리를 기본 제공하지 않기 때문에 ClassNotFoundException 에러가 발생한 것&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 data-ke-size=&quot;size16&quot;&gt;build.gradle에 JSP 의존성 추가&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;implementation 'jakarta.servlet.jsp:jakarta.servlet.jsp-api:3.1.1'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; 문제3 - org.apache.commons.lang.StringUtils&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 아래 오류 발생&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;ClassNotFoundException: org.apache.commons.lang.StringUtils&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;원인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nexacro XAPI가 개발되었을 때엔 'org.apache.commons.lang.StringUtils'를 많이 사용했다고 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 Apache에서 commons-lang을 commons-lang3로 변경하면서, 구버전 의존성을 가진 nexacro와 충돌이 일어난 것&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 data-ke-size=&quot;size16&quot;&gt;gradle 의존성 추가&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;implementation 'commons-lang:commons-lang:2.6'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결과&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1248&quot; data-origin-height=&quot;667&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GzWQs/dJMcaiwS8gI/aZ1xhhlvcsLMfXyltIKoe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GzWQs/dJMcaiwS8gI/aZ1xhhlvcsLMfXyltIKoe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GzWQs/dJMcaiwS8gI/aZ1xhhlvcsLMfXyltIKoe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGzWQs%2FdJMcaiwS8gI%2FaZ1xhhlvcsLMfXyltIKoe1%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;1248&quot; height=&quot;667&quot; data-origin-width=&quot;1248&quot; data-origin-height=&quot;667&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 잘 불러와지는 것을 확인할 수 있음&lt;/p&gt;</description>
      <category>트러블슈팅</category>
      <category>Nexacro</category>
      <category>SpringBoot</category>
      <author>naahy</author>
      <guid isPermaLink="true">https://naahy0co.tistory.com/83</guid>
      <comments>https://naahy0co.tistory.com/83#entry83comment</comments>
      <pubDate>Fri, 12 Jun 2026 00:00:40 +0900</pubDate>
    </item>
    <item>
      <title>[SpringBoot] Flyway로 DB 마이그레이션 및 DDL 작성</title>
      <link>https://naahy0co.tistory.com/82</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;SpringBoot와 Oracle, MyBatis를 이용하여 재고/물품 관리 시스템을 개발하던 중, DDL 스크립트를 저장하고 관리할 필요성을 느끼게 되었다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 MyBatis는 SQL 실행 도구로 스키마를 자동 생성해주지 않기 때문에, 테이블 생성 시 DDL 스크립트를 필수로 작성해야 함 --&amp;gt; Flyway가 필수적&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;❗JPA 사용 시에도 Flyway를 사용한다&lt;br /&gt;&lt;/b&gt;JPA 사용 시 Hibernate가 테이블 생성 및 수정을 자동으로 해주기 때문에 Flyway가 필요 없다고 생각할 수 있으나, Flyway 없이 `ddl-auto` 기능만을 사용하면 테이블 변경 시 누가 언제 어떤 컬럼을 추가했는지 추적 불가함&lt;br /&gt;따라서 실수를 방지하고 sql 버전 관리를 위해 JPA + Flyway 조합도 많이 사용함&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Flyway&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DB 마이그레이션 버전 관리 도구&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 Git으로 버전 관리하듯이, &lt;b&gt;DB 스키마 변경 이력도 파일로 관리&lt;/b&gt;할 수 있게 해줌&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flyway가 실행한 이력은 &lt;code&gt;FLYWAY_SCHEMA_HISTORY&lt;/code&gt; 테이블에 자동으로 기록됨&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 SQL 파일에 버전 번호를 붙임 --&amp;gt; Flyway가 아직 실행되지 않은 파일만 골라서 순서대로 자동 실행&lt;/li&gt;
&lt;/ul&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 data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Prefix&lt;/code&gt; + &lt;code&gt;Version 번호&lt;/code&gt; + &lt;code&gt;__(언더바 2개)&lt;/code&gt; + &lt;code&gt;설명&lt;/code&gt; + &lt;code&gt;확장자&lt;/code&gt; ==&amp;gt;&amp;nbsp;&lt;code&gt;V&amp;lt;번호&amp;gt;__설명.sql&lt;/code&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;prefix&lt;/b&gt;: V(버전 마이그레이션), R(반복 마이그레이션), U(취소 마이그레이션 - 유료)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;version&lt;/b&gt;: 버전 마이그레이션에서만 사용됨. 숫자, 점(.), 언더바(_) 조합
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;V2, V2.1, V2_1&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;separator&lt;/b&gt;: 버전과 설명을 구분하는 구분자. 언더바 2개 사용 (__)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;description&lt;/b&gt;: schema_version 테이블에 저장시 설명 컬럼에 저장됨&lt;/li&gt;
&lt;li&gt;&lt;b&gt;suffix&lt;/b&gt;: 확장자 기본은 .sql&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DDL 관리 방법 비교&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.031%; text-align: center;&quot;&gt;&lt;b&gt;방법&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 17.403%; text-align: center;&quot;&gt;&lt;b&gt;도구&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 63.5659%; text-align: center;&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.031%;&quot;&gt;DDL 직접 실행&lt;/td&gt;
&lt;td style=&quot;width: 17.403%;&quot;&gt;sqlplus / DBeaver&lt;/td&gt;
&lt;td style=&quot;width: 63.5659%;&quot;&gt;가장 직접적, but 이력이 남지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.031%;&quot;&gt;schema.sql 자동 실행&lt;/td&gt;
&lt;td style=&quot;width: 17.403%;&quot;&gt;SpringBoot 내장&lt;/td&gt;
&lt;td style=&quot;width: 63.5659%;&quot;&gt;앱 기동 시 SQL 파일 자동 실행 --&amp;gt; 매번 DROP, CREATE 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.031%;&quot;&gt;Flyway&lt;/td&gt;
&lt;td style=&quot;width: 17.403%;&quot;&gt;마이그레이션 툴&lt;/td&gt;
&lt;td style=&quot;width: 63.5659%;&quot;&gt;버전 관리 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  프로젝트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;구조&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;src/main/resources/
├── db/
│   └── migration/
│       ├── V1__create_sequences.sql      &amp;larr; Sequence 생성
│       ├── V2__create_tables.sql         &amp;larr; 테이블 + 컬럼 COMMENT 생성
│       ├── V3__create_trigger.sql        &amp;larr; 트리거 생성
│       └── V4__insert_sample_data.sql    &amp;larr; 샘플 데이터
├── mapper/
│   └── item/
└── application.yml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 방법&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;build.gradle 의존성 추가&lt;/h4&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;// Flyway
implementation 'org.flywaydb:flyway-core'
implementation 'org.flywaydb:flyway-database-oracle'  // Oracle 전용&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;application.yml 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;oracle: sqlplus: true&lt;/code&gt; 설정이 없으면 V3의 트리거(PL/SQL 블록) 실행 시 에러 발생&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;spring:
  flyway:
    enabled: true
    locations: classpath:db/migration   # SQL 파일 위치
    baseline-on-migrate: true           # 기존 테이블이 있어도 에러 안 남
    oracle:
      sqlplus: true                     # PL/SQL 블록(BEGIN~END) 실행 허용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SQL 파일 목록&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;V1__create_sequences.sql
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 테이블의 PK용 시퀀스 생성 파일&lt;/li&gt;
&lt;li&gt;Oracle은 &lt;code style=&quot;letter-spacing: 0px;&quot;&gt;AUTO\_INCREMENT&lt;/code&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; 대신 &lt;/span&gt;&lt;code style=&quot;letter-spacing: 0px;&quot;&gt;Sequence&lt;/code&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;로 PK를 생성함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;V2__create_tables.sql
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테이블 + 각 테이블/컬럼 COMMENT 작성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;V3__create_trigger.sql
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1:1 관계인 두 테이블 A, B가 있을 때, A테이블에서 데이터가 삽입된 경우 B 테이블에 자동으로 관련 행이 생성되도록 트리거 작성&lt;/li&gt;
&lt;li&gt;데이터 무결성 보장 위함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;V4__insert_sample_data.sql
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트용 샘플 데이터 입력&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;/h2&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;앱 실행
  └─ Flyway 활성화
       └─ FLYWAY_SCHEMA_HISTORY 테이블 확인 (없으면 자동 생성)
            └─ db/migration/ 폴더 스캔
                 └─ 미실행 파일만 순서대로 Oracle에 자동 실행
                      └─ 실행 이력 기록&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정상 실행 로그 예시&lt;/h3&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;Flyway Community Edition - Flyway by Redgate
Database: jdbc:oracle:thin:@localhost:1521/XEPDB1 (Oracle 21.0)
Successfully validated 4 migrations (execution time 00:00.025s)
Migrating schema to version 1 - create sequences
Migrating schema to version 2 - create tables
Migrating schema to version 3 - create trigger
Migrating schema to version 4 - insert sample data
Successfully applied 4 migrations (execution time 00:02.503s)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;확인 쿼리&lt;/h3&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;-- Flyway 실행 이력 확인
SELECT version, description, success, installed_on
FROM flyway_schema_history
ORDER BY installed_rank;

-- 샘플 데이터 + 트리거 동작 확인
-- 이 부분은 각자 작성한 테이블 정보에 맞게 작성
SELECT i.item_name, i.item_code, s.current_qty
FROM item i JOIN stock s ON i.item_id = s.item_id;&lt;/code&gt;&lt;/pre&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;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;이미 실행된 파일을 수정하면 Flyway가 체크섬 불일치 에러를 냄&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;V5__add_column_item.sql       &amp;larr; 컬럼 추가
V6__create_index.sql          &amp;larr; 인덱스 추가&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  DB 초기화가 필요할 때&lt;/h2&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;# 컨테이너 + 데이터 볼륨 모두 삭제 후 재시작
docker compose down -v
docker compose up -d

# 이후 앱 재실행 &amp;rarr; Flyway가 V1~V4 처음부터 다시 실행&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;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@hyun-jii/Flyway-%EC%A0%95%EC%9D%98-DB-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%EB%8F%84%EA%B5%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@hyun-jii/Flyway-%EC%A0%95%EC%9D%98-DB-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%EB%8F%84%EA%B5%AC&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1780905563714&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;Flyway 정의, DB 마이그레이션 도구&quot; data-og-description=&quot;이번 신규 프로젝트에 Flyway 를 적용하게되어 Flyway 관련 세미나를 진행하게 되었다. 그래서 세미나를 준비하며 정리했던 Flyway 개념에 대해 간단히 소개해보고자 한다. 그리고 프로젝트 적용기는&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@hyun-jii/Flyway-%EC%A0%95%EC%9D%98-DB-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%EB%8F%84%EA%B5%AC&quot; data-og-url=&quot;https://velog.io/@hyun-jii/Flyway-정의-DB-마이그레이션-도구&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/HUfFs/dJMb88GcU5N/Zl7mdWhTXeRPYA9rnQiuuK/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/CI0Xt/dJMb89ylyFs/ZLRFRTf6RdJFO7QCgEHy11/img.png?width=3088&amp;amp;height=1266&amp;amp;face=0_0_3088_1266,https://scrap.kakaocdn.net/dn/m196V/dJMb85W1gNZ/gWXKIkCrzGE2jT75Uzf24k/img.png?width=3610&amp;amp;height=1004&amp;amp;face=0_0_3610_1004&quot;&gt;&lt;a href=&quot;https://velog.io/@hyun-jii/Flyway-%EC%A0%95%EC%9D%98-DB-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%EB%8F%84%EA%B5%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@hyun-jii/Flyway-%EC%A0%95%EC%9D%98-DB-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%EB%8F%84%EA%B5%AC&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/HUfFs/dJMb88GcU5N/Zl7mdWhTXeRPYA9rnQiuuK/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/CI0Xt/dJMb89ylyFs/ZLRFRTf6RdJFO7QCgEHy11/img.png?width=3088&amp;amp;height=1266&amp;amp;face=0_0_3088_1266,https://scrap.kakaocdn.net/dn/m196V/dJMb85W1gNZ/gWXKIkCrzGE2jT75Uzf24k/img.png?width=3610&amp;amp;height=1004&amp;amp;face=0_0_3610_1004');&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;Flyway 정의, DB 마이그레이션 도구&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 신규 프로젝트에 Flyway 를 적용하게되어 Flyway 관련 세미나를 진행하게 되었다. 그래서 세미나를 준비하며 정리했던 Flyway 개념에 대해 간단히 소개해보고자 한다. 그리고 프로젝트 적용기는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hu-bris.tistory.com/243&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://hu-bris.tistory.com/243&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1780905572007&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;유용한 DB 마이그레이션 도구, Flyway&quot; data-og-description=&quot;Flyway란 무엇인가?Flyway는 데이터베이스 마이그레이션 툴이다.그렇다면 마이그레이션은 뭘까? 마이그레이션은 이주하다라는 뜻으로, 데이터베이스 마이그레이션이라면 MySQL에서 PostgreSQL로 데이&quot; data-og-host=&quot;hu-bris.tistory.com&quot; data-og-source-url=&quot;https://hu-bris.tistory.com/243&quot; data-og-url=&quot;https://hu-bris.tistory.com/243&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bCiZAC/dJMb88GcVie/nWnZsbzGjTq2qoJq9nXKW0/img.png?width=800&amp;amp;height=180&amp;amp;face=0_0_800_180,https://scrap.kakaocdn.net/dn/gz2PF/dJMb82eUVjT/3g6sjUkWtQ3ARGHY7ZjEq1/img.png?width=800&amp;amp;height=180&amp;amp;face=0_0_800_180,https://scrap.kakaocdn.net/dn/RNSVZ/dJMb84X6Fdj/lzz7zRogNQdwS7rrQFOvZ0/img.png?width=1280&amp;amp;height=840&amp;amp;face=0_0_1280_840&quot;&gt;&lt;a href=&quot;https://hu-bris.tistory.com/243&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hu-bris.tistory.com/243&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bCiZAC/dJMb88GcVie/nWnZsbzGjTq2qoJq9nXKW0/img.png?width=800&amp;amp;height=180&amp;amp;face=0_0_800_180,https://scrap.kakaocdn.net/dn/gz2PF/dJMb82eUVjT/3g6sjUkWtQ3ARGHY7ZjEq1/img.png?width=800&amp;amp;height=180&amp;amp;face=0_0_800_180,https://scrap.kakaocdn.net/dn/RNSVZ/dJMb84X6Fdj/lzz7zRogNQdwS7rrQFOvZ0/img.png?width=1280&amp;amp;height=840&amp;amp;face=0_0_1280_840');&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;유용한 DB 마이그레이션 도구, Flyway&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Flyway란 무엇인가?Flyway는 데이터베이스 마이그레이션 툴이다.그렇다면 마이그레이션은 뭘까? 마이그레이션은 이주하다라는 뜻으로, 데이터베이스 마이그레이션이라면 MySQL에서 PostgreSQL로 데이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hu-bris.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;</description>
      <category>SpringBoot</category>
      <category>flyway</category>
      <category>SpringBoot</category>
      <author>naahy</author>
      <guid isPermaLink="true">https://naahy0co.tistory.com/82</guid>
      <comments>https://naahy0co.tistory.com/82#entry82comment</comments>
      <pubDate>Mon, 8 Jun 2026 17:06:05 +0900</pubDate>
    </item>
    <item>
      <title>[SpringBoot] Swagger를 이용한 API 문서 자동화</title>
      <link>https://naahy0co.tistory.com/81</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Swagger&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API를 &lt;b&gt;문서화&lt;/b&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;API 개발 시 프론트엔드 개발자나 외부 팀에게 아래 정보를 전달해야 함&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요청 URL&lt;/li&gt;
&lt;li&gt;HTTP 메서드 (GET, POST 등)&lt;/li&gt;
&lt;li&gt;요청 파라미터/바디&lt;/li&gt;
&lt;li&gt;응답 형태&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 내용을 수동 작성하면 코드가 바뀔 때마다 문서도 일일이 따로 수정해야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swagger는 이 과정을 코드에서 자동으로 생성, &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;Controller에 작성된 내용이 Swagger UI에 자동으로 API 표시됨&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Swagger UI 제공&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서 시각적으로 API를 확인할 수 있는 &lt;b&gt;인터랙티브 문서 페이지&lt;/b&gt;를 제공함&lt;/p&gt;
&lt;pre id=&quot;code_1779074305706&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;http://localhost:8080/swagger-ui.html&lt;/code&gt;&lt;/pre&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;모든 API 엔드포인트 목록 확인&lt;/li&gt;
&lt;li&gt;요청/응답 스펙 확인&lt;/li&gt;
&lt;li&gt;직접 API 호출 테스트 가능 (Postman 필요 X)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;OpenAPI 명세 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 구조를 &lt;b&gt;표준화된 JSON/YAML 형식&lt;/b&gt;으로 내보냄&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot; style=&quot;color: #14181f;&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;http://localhost:8080/api-docs&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일을 활용해서&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프론트엔드에서 자동으로 API 클라이언트 코드 생성&lt;/li&gt;
&lt;li&gt;Postman으로 import해서 테스트&lt;/li&gt;
&lt;li&gt;다른 팀/서비스와 API 스펙 공유 가능&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;h3 data-ke-size=&quot;size23&quot;&gt;Springdoc vs Springfox&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot 프로젝트에서는&amp;nbsp;&lt;code&gt;springdoc-openapi&lt;/code&gt; 라이브러리를 사용하여 Swagger UI 기반으로 API 문서를 자동화 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전엔 &lt;code&gt;Springfox Swagger&lt;/code&gt;가 많이 사용되었으나, &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;현재는&amp;nbsp;&lt;/span&gt;&lt;code&gt;springdoc-openapi&lt;/code&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;Springfox Swagger&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;OpenAPI 2.0 (Swagger 2) 기반&lt;/li&gt;
&lt;li&gt;2020년 이후 사실상 개발 중단 상태&lt;/li&gt;
&lt;li&gt;Spring Boot 2.6+ 버전과 호환 문제 발생&lt;/li&gt;
&lt;li&gt;Spring Boot 3.x와는 호환 불가&lt;/li&gt;
&lt;li&gt;설정이 다소 복잡 (별도 Docket Bean 등록 필요)&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;&lt;b&gt;Springdoc&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;OpenAPI 3.0 기반&lt;/li&gt;
&lt;li&gt;활발하게 유지보수 중&lt;/li&gt;
&lt;li&gt;Spring Boot 2.x, 3.x 모두 호환&lt;/li&gt;
&lt;li&gt;설정이 매우 간단 (의존성 추가만 해도 바로 동작)&lt;/li&gt;
&lt;li&gt;Spring Security, WebFlux, Actuator 등과 잘 통합됨&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Swagger 설정&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프로젝트 사양&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring Boot 3.3.5&lt;/li&gt;
&lt;li&gt;Gradle&lt;/li&gt;
&lt;li&gt;Java 17&lt;/li&gt;
&lt;/ul&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 data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;build.gradle&lt;/code&gt;에 의존성 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 SpringBoot 버전마다 &lt;b&gt;호환 가능한 springdoc 버전이 다르므로&lt;/b&gt; 아래 공식 문서를 확인할 것. 맞지 않는 버전을 사용할 시 프로젝트가 빌드되지 않고 오류가 날 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://springdoc.org/#what-is-the-compatibility-matrix-of-springdoc-openapi-with-spring-boot&quot;&gt;https://springdoc.org/#what-is-the-compatibility-matrix-of-springdoc-openapi-with-spring-boot&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;dependencies {
    # 현재 프로젝트는 SpringBoot 3.3.5이므로 springdoc 2.6.0 사용
    implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SpringSecurity 사용 시 허용 경로&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SpringSecurity 사용 시 &lt;code&gt;SecurityConfig&lt;/code&gt;에 Swagger 경로 허용 필요 (사용하지 않으면 필요 X)&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

    http
        .csrf(csrf -&amp;gt; csrf.disable())
        .authorizeHttpRequests(auth -&amp;gt; auth
            .requestMatchers(
                &quot;/swagger-ui/**&quot;,
                &quot;/v3/api-docs/**&quot;
            ).permitAll()
            .anyRequest().authenticated()
        );

    return http.build();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;접속&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 링크로 접속 시 Swagger 접속 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 &lt;code&gt;application.properties&lt;/code&gt;에서 포트 번호나 Context Path를 따로 설정한 경우 적용 필요&lt;/p&gt;
&lt;pre id=&quot;code_1778937102202&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;http://localhost:8080/swagger-ui/index.html

# 예) Context path로 /api를 설정한 경우
http://localhost:8080/api/swagger-ui/index.html&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Swagger 설정 클래스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API의 기본 정보 설정 가능&lt;/p&gt;
&lt;pre id=&quot;code_1779074840583&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@OpenAPIDefinition(
    info = @Info(
        title = &quot;My API&quot;,	// 문서 제목
        version = &quot;v1&quot;,		// 버전
        description = &quot;Spring Boot Swagger API 문서&quot;	// 상세설명
    )
)
public class SwaggerConfig {
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Swagger 문서 상세화&lt;/h2&gt;
&lt;h3 data-end=&quot;1036&quot; data-start=&quot;1020&quot; data-section-id=&quot;re7f08&quot; data-ke-size=&quot;size23&quot;&gt;Controller 설명&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Tag&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Tag(name = &quot;User API&quot;, description = &quot;사용자 관련 API&quot;)	// 어노테이션 추가
@RestController
@RequestMapping(&quot;/users&quot;)
public class UserController {&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-end=&quot;1189&quot; data-start=&quot;1180&quot; data-section-id=&quot;zkaw58&quot; data-ke-size=&quot;size23&quot;&gt;API 설명&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Operation&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Operation(summary = &quot;회원 조회&quot;, description = &quot;ID로 회원 정보를 조회합니다.&quot;)
@GetMapping(&quot;/{id}&quot;)
public UserResponse getUser(@PathVariable Long id) {
    return new UserResponse(id, &quot;hayeon&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1411&quot; data-start=&quot;1393&quot; data-section-id=&quot;e1lri0&quot; data-ke-size=&quot;size23&quot;&gt;Response DTO 설명&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Schema&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Getter
@AllArgsConstructor
@Schema(description = &quot;회원 응답 DTO&quot;)
public class UserResponse {

    @Schema(description = &quot;회원 ID&quot;, example = &quot;1&quot;)
    private Long id;

    @Schema(description = &quot;회원 이름&quot;, example = &quot;hayeon&quot;)
    private String name;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>SpringBoot</category>
      <category>SpringBoot</category>
      <category>Swagger</category>
      <author>naahy</author>
      <guid isPermaLink="true">https://naahy0co.tistory.com/81</guid>
      <comments>https://naahy0co.tistory.com/81#entry81comment</comments>
      <pubDate>Sun, 31 May 2026 22:09:45 +0900</pubDate>
    </item>
    <item>
      <title>[트러블슈팅/SpringBoot] 415 오류: Content-Type 'application/octet-stream' is not supported</title>
      <link>https://naahy0co.tistory.com/80</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt; 문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게시글을 작성할 때 사용자의 로그인이 필요하도록 코드를 작성함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 기능 테스트 시 사용자 회원가입 및 로그인은 문제 없이 되나, 게시글 작성만 403 오류가 뜨며 진행이 되지 않음&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;753&quot; data-origin-height=&quot;208&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0OQs1/dJMcad25lIj/DiM2Qwj3EDQMg2fQBJWWYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0OQs1/dJMcad25lIj/DiM2Qwj3EDQMg2fQBJWWYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0OQs1/dJMcad25lIj/DiM2Qwj3EDQMg2fQBJWWYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0OQs1%2FdJMcad25lIj%2FDiM2Qwj3EDQMg2fQBJWWYk%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;753&quot; height=&quot;208&quot; data-origin-width=&quot;753&quot; data-origin-height=&quot;208&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;403 오류가 떴으므로 인증 관련 문제일 거라고 생각했으나, 로그를 보면 다음 단계를 거치는 걸 확인할 수 있음&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;게시글 작성 요청&lt;/li&gt;
&lt;li&gt;인증 확인&lt;/li&gt;
&lt;li&gt;&lt;b&gt;415 오류 발생&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Spring이 /error로 POST 요청&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SecureConfig&lt;/code&gt;에서 해당 url을 처리하는 부분이 없어 403 에러 발생&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 사용자 인증에 실패하여 403 에러가 뜨는 것이 X&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류메시지를 자세히 들여다보면 아래와 같이 &lt;b&gt;Content-Type 'application/octet-stream' is not supported&lt;/b&gt; 라는 문구가 있음&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;Could not resolve parameter [0] in public org.springframework.http.ResponseEntity&amp;lt;?&amp;gt; 프로젝트.controller.DiaryController.createDiary(프로젝트.dto.diary.DiaryCreateRequest,java.util.List&amp;lt;org.springframework.web.multipart.MultipartFile&amp;gt;,org.springframework.security.core.userdetails.UserDetails): Content-Type 'application/octet-stream' is not supported
Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content-Type 'application/octet-stream' is not supported]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 메서드의 첫 번째 파라미터의 콘텐츠 유형이 지원되지 않아 이를 해결할 수 없다는 내용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  application/octet-stream&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;HTTP에서 사용하는 MIME 타입(Content-Type) 중 하나&lt;br /&gt;이 데이터가 어떤 형식인지 알 수 없을 때의 바이너리 데이터를 나타냄&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 파일의 형식을 알 수 없을 때 그 파일을 바이너리 데이터로 보내고, 브라우저는 해당 파일을 화면에 표시하는 대신 다운로드를 진행함 --&amp;gt; zip,. exe, pdf 등을 다운로드 시키고 싶을 때 사용&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 발생한 코드는 아래와 같다&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@PostMapping(path = &quot;/post&quot;, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity&amp;lt;?&amp;gt; createDiary(
        @RequestPart(&quot;diary&quot;) DiaryCreateRequest request,
        @RequestPart(value = &quot;images&quot;, required = false) List&amp;lt;MultipartFile&amp;gt; images,
        @AuthenticationPrincipal UserDetails userDetails
) {
    // 코드 내용 ....
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;@RequestPart(&quot;diary&quot;)&lt;/code&gt;로 JSON을 받고 있는데, 클라이언트가 요청을 보낼 때 diary 파트의 Content-Type을 &lt;code&gt;application/json&lt;/code&gt;으로 명시하지 않아 기본값인 &lt;code&gt;application/octet-stream&lt;/code&gt;으로 전송되었음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 타입을 Spring이 DiaryCreateRequest로 역직렬화 할 수 없기 때문에 &lt;b&gt;415 Unsupported Media Type&lt;/b&gt; 에러가 발생한 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트에서 요청을 보낼 때, diary 파트에 &lt;b&gt;Content-Type을 명시적으로 application/json으로 지정&lt;/b&gt;해야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 나는 지금 백엔드 개발 중이고, 당장 테스트 해봐야 하므로 서버 측에서 해결하는 방향으로 진행... (--&amp;gt; 추후 프론트엔드 개발 시 이 부분은 수정하기)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@RequestPart&lt;/code&gt;로 데이터를 받는 대신 &lt;code&gt;String&lt;/code&gt;으로 먼저 받고 직접 파싱하기로 함&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;@PostMapping(path = &quot;/post&quot;, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity&amp;lt;?&amp;gt; createDiary(
        @RequestPart(&quot;diary&quot;) String diaryJson,   // String으로 받기
        @RequestPart(value = &quot;images&quot;, required = false) List&amp;lt;MultipartFile&amp;gt; images,
        @AuthenticationPrincipal UserDetails userDetails
) {
    // 입력 받은 String을 DTO로 직접 파싱함
    DiaryCreateRequest request = objectMapper.readValue(diaryJson, DiaryCreateRequest.class);

    // ...
}&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;추가로, 컨트롤러에서 따로 처리한 예외가 아니라면 오류 코드가 403으로만 보이는 현상도 피해야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전역 예외 핸들러&lt;/b&gt; 클래스를 작성하여 각각의 오류 코드가 정확히 보이도록 명시한다&lt;/p&gt;
&lt;pre id=&quot;code_1779256384730&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestControllerAdvice
public class GlobalExceptionHandler {

    // @Valid 검증 실패 시 (400)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity&amp;lt;?&amp;gt; handleValidationException(MethodArgumentNotValidException e) {
        Map&amp;lt;String, String&amp;gt; errors = new LinkedHashMap&amp;lt;&amp;gt;();
        e.getBindingResult().getFieldErrors()
                .forEach(error -&amp;gt; errors.put(error.getField(), error.getDefaultMessage()));
        return ResponseEntity.badRequest().body(errors);
    }

    // 지원하지 않는 Content-Type (415)
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public ResponseEntity&amp;lt;?&amp;gt; handleMediaTypeNotSupported(HttpMediaTypeNotSupportedException e) {
        return ResponseEntity.status(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
                .body(Map.of(&quot;error&quot;, e.getMessage()));
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결과&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;279&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bn9ECs/dJMcaayBTHu/QlNIYLhLiLlO6WJk5tqxa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bn9ECs/dJMcaayBTHu/QlNIYLhLiLlO6WJk5tqxa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bn9ECs/dJMcaayBTHu/QlNIYLhLiLlO6WJk5tqxa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbn9ECs%2FdJMcaayBTHu%2FQlNIYLhLiLlO6WJk5tqxa0%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;482&quot; height=&quot;279&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;279&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;517&quot; data-origin-height=&quot;198&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vXqCo/dJMcabj0hk6/vqya4lPtPMGa75RPIjQKn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vXqCo/dJMcabj0hk6/vqya4lPtPMGa75RPIjQKn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vXqCo/dJMcabj0hk6/vqya4lPtPMGa75RPIjQKn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvXqCo%2FdJMcabj0hk6%2Fvqya4lPtPMGa75RPIjQKn0%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;517&quot; height=&quot;198&quot; data-origin-width=&quot;517&quot; data-origin-height=&quot;198&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;postman에서도, swagger에서도 게시글이 잘 작성되는 것을 확인할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;533&quot; data-origin-height=&quot;451&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d2m6DJ/dJMcacQHjCL/38U1sVK2eyc86F5ubkcGS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d2m6DJ/dJMcacQHjCL/38U1sVK2eyc86F5ubkcGS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d2m6DJ/dJMcacQHjCL/38U1sVK2eyc86F5ubkcGS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd2m6DJ%2FdJMcacQHjCL%2F38U1sVK2eyc86F5ubkcGS0%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;533&quot; height=&quot;451&quot; data-origin-width=&quot;533&quot; data-origin-height=&quot;451&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;비밀번호 기준을 맞추지 못했을 때 에러코드가 400이 아닌 403이 떴었으나, 이젠 오류 메시지와 함께 400 에러코드가 뜨는 것을 확인할 수 있음&lt;/p&gt;</description>
      <category>트러블슈팅</category>
      <category>SpringBoot</category>
      <category>트러블슈팅</category>
      <author>naahy</author>
      <guid isPermaLink="true">https://naahy0co.tistory.com/80</guid>
      <comments>https://naahy0co.tistory.com/80#entry80comment</comments>
      <pubDate>Tue, 19 May 2026 20:54:39 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] SpringBoot + AWS EC2 Ubuntu 서버 생성 및 배포</title>
      <link>https://naahy0co.tistory.com/78</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt; AWS 준비&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt; 인스턴스 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. AWS 로그인 후 우측 상단의 AWS Region에서 지역 설정&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;본인이 현재 위치한 지역으로 설정&lt;/li&gt;
&lt;li&gt;‼️ 인스턴스 생성 후엔 지역 바꿀 수 없으므로 반드시 미리 지역 설정 후 인스턴스 생성할 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;601&quot; data-origin-height=&quot;326&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgr2z6/dJMb99MFwsA/LKatP4DBTlYjEdlWaTr7Ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgr2z6/dJMb99MFwsA/LKatP4DBTlYjEdlWaTr7Ck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgr2z6/dJMb99MFwsA/LKatP4DBTlYjEdlWaTr7Ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdgr2z6%2FdJMb99MFwsA%2FLKatP4DBTlYjEdlWaTr7Ck%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;601&quot; height=&quot;326&quot; data-origin-width=&quot;601&quot; data-origin-height=&quot;326&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. EC2 서비스로 이동 후 '인스턴스 시작' 클릭&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;1079&quot; data-origin-height=&quot;414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz0Izs/dJMcahKKt7S/j63vo5GeNCJE2ixCdqIEIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz0Izs/dJMcahKKt7S/j63vo5GeNCJE2ixCdqIEIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz0Izs/dJMcahKKt7S/j63vo5GeNCJE2ixCdqIEIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz0Izs%2FdJMcahKKt7S%2Fj63vo5GeNCJE2ixCdqIEIk%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;743&quot; height=&quot;285&quot; data-origin-width=&quot;1079&quot; data-origin-height=&quot;414&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 인스턴스명 작성 후 AMI(Amazon Machine Image) 설정&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이름은 자유 입력&lt;/li&gt;
&lt;li&gt;AMI는 Ubuntu 서버를 사용할 것이므로 Ubuntu 선택&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1249&quot; data-origin-height=&quot;790&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckwpsx/dJMcadaq1kx/BP2LIqy09VGD9RIS8dLyf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckwpsx/dJMcadaq1kx/BP2LIqy09VGD9RIS8dLyf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckwpsx/dJMcadaq1kx/BP2LIqy09VGD9RIS8dLyf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fckwpsx%2FdJMcadaq1kx%2FBP2LIqy09VGD9RIS8dLyf0%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;1249&quot; height=&quot;790&quot; data-origin-width=&quot;1249&quot; data-origin-height=&quot;790&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 키 페어 설정&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  키 페어&lt;/b&gt;&lt;br /&gt;AWS EC2 인스턴스에 연결하기 위한 암호화 파일&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;우측의 &lt;b&gt;'새 키 페어 생성'&lt;/b&gt; 버튼 클릭&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;989&quot; data-origin-height=&quot;189&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TkCwf/dJMcadO2zBt/Rc2kIxqhSqLY6aSgkgq0m0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TkCwf/dJMcadO2zBt/Rc2kIxqhSqLY6aSgkgq0m0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TkCwf/dJMcadO2zBt/Rc2kIxqhSqLY6aSgkgq0m0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTkCwf%2FdJMcadO2zBt%2FRc2kIxqhSqLY6aSgkgq0m0%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;724&quot; height=&quot;138&quot; data-origin-width=&quot;989&quot; data-origin-height=&quot;189&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;키 페어 이름은 알아볼 수 있는 선에서 자유롭게 작성&lt;/li&gt;
&lt;li&gt;유형 및 파일 형식은 기본 설정 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciaxyE/dJMcaiQmNZm/ps7U5cS3mHCkiOQ9V4YCVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciaxyE/dJMcaiQmNZm/ps7U5cS3mHCkiOQ9V4YCVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciaxyE/dJMcaiQmNZm/ps7U5cS3mHCkiOQ9V4YCVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciaxyE%2FdJMcaiQmNZm%2Fps7U5cS3mHCkiOQ9V4YCVK%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;497&quot; height=&quot;460&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5. 나머지 값은 기본값으로 설정 후 우측의 '인스턴스 시작' 버튼 클릭&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;보안 그룹의 경우 추후 설정할 것이므로 기본값으로 두어도 무관&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;547&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cg44Db/dJMcafsBvD3/m1TMuLYlbiIRozpVkacJ5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cg44Db/dJMcafsBvD3/m1TMuLYlbiIRozpVkacJ5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cg44Db/dJMcafsBvD3/m1TMuLYlbiIRozpVkacJ5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcg44Db%2FdJMcafsBvD3%2Fm1TMuLYlbiIRozpVkacJ5K%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;442&quot; height=&quot;427&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;547&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6. 보안 그룹 생성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;좌측 메뉴바에서 &lt;b&gt;네트워크 및 보안 &amp;gt; 보안 그룹&lt;/b&gt;으로 이동&lt;/li&gt;
&lt;li&gt;우측 상단의 &lt;b&gt;'보안 그룹 생성'&lt;/b&gt; 버튼 클릭&lt;/li&gt;
&lt;li&gt;기본 세부 정보 작성 후 인바운드 규칙 작성 --&amp;gt; &lt;b&gt;'규칙 추가'&lt;/b&gt; 버튼 클릭&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;171&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dSbhhD/dJMcabjtBVz/SkrhHCzo3SINfGNx3BTW8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dSbhhD/dJMcabjtBVz/SkrhHCzo3SINfGNx3BTW8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dSbhhD/dJMcabjtBVz/SkrhHCzo3SINfGNx3BTW8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdSbhhD%2FdJMcabjtBVz%2FSkrhHCzo3SINfGNx3BTW8k%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;385&quot; height=&quot;171&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;171&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring 서버용 포트, SSH 포트 설정 (이외에도 필요한 부분은 추가 설정)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1601&quot; data-origin-height=&quot;278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dovrlW/dJMcahcTdqg/A2Kbzm7pFbfilhCTEwGGl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dovrlW/dJMcahcTdqg/A2Kbzm7pFbfilhCTEwGGl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dovrlW/dJMcahcTdqg/A2Kbzm7pFbfilhCTEwGGl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdovrlW%2FdJMcahcTdqg%2FA2Kbzm7pFbfilhCTEwGGl1%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;1601&quot; height=&quot;278&quot; data-origin-width=&quot;1601&quot; data-origin-height=&quot;278&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;7. 인스턴스에 생성한 보안 그룹 설정&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인스턴스 &amp;gt; 작업 &amp;gt; 보안 &amp;gt; 보안 그룹 변경&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;235&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FPFA0/dJMcacimv9m/kqlaKj9Ndm4vdgX7FIERM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FPFA0/dJMcacimv9m/kqlaKj9Ndm4vdgX7FIERM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FPFA0/dJMcacimv9m/kqlaKj9Ndm4vdgX7FIERM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFPFA0%2FdJMcacimv9m%2FkqlaKj9Ndm4vdgX7FIERM1%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;1160&quot; height=&quot;235&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;235&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연결된 보안 그룹 메뉴 안에서 아까 생성한 보안 그룹 검색 후 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;379&quot; data-origin-height=&quot;134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baR05y/dJMcacWWBTu/cKZIewZAIGCXI5JgDzQm80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baR05y/dJMcacWWBTu/cKZIewZAIGCXI5JgDzQm80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baR05y/dJMcacWWBTu/cKZIewZAIGCXI5JgDzQm80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaR05y%2FdJMcacWWBTu%2FcKZIewZAIGCXI5JgDzQm80%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;379&quot; height=&quot;134&quot; data-origin-width=&quot;379&quot; data-origin-height=&quot;134&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;8. 탄력적 IP 주소 할당&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성한 인스턴스를 재시작해도 IP 주소가 바뀌지 않도록 탄력적 IP 주소 할당&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;좌측 메뉴에서 &lt;b&gt;네트워크 및 보안 &amp;gt; 탄력적 IP&lt;/b&gt; 이동&lt;/li&gt;
&lt;li&gt;우측 상단의 &lt;b&gt;'탄력적 IP 주소 할당'&lt;/b&gt; 클릭&lt;/li&gt;
&lt;li&gt;기본값 그대로 두고 우측 하단의 &lt;b&gt;'할당'&lt;/b&gt; 버튼 클릭&lt;/li&gt;
&lt;li&gt;&lt;b&gt;작업 &amp;gt; 탄력적 IP 주소 연결&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1650&quot; data-origin-height=&quot;215&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wv6Os/dJMcabwXRqm/JEbnF3xX4ujjKjrtz7v0f1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wv6Os/dJMcabwXRqm/JEbnF3xX4ujjKjrtz7v0f1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wv6Os/dJMcabwXRqm/JEbnF3xX4ujjKjrtz7v0f1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwv6Os%2FdJMcabwXRqm%2FJEbnF3xX4ujjKjrtz7v0f1%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;1650&quot; height=&quot;215&quot; data-origin-width=&quot;1650&quot; data-origin-height=&quot;215&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;h3 data-ke-size=&quot;size23&quot;&gt; EC2 접속&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 인스턴스로 이동 후 '연결' 버튼 클릭&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;1007&quot; data-origin-height=&quot;233&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cP0Bsr/dJMcadVPcvO/mJDfEOIk0xQcxKGNU2j5n0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cP0Bsr/dJMcadVPcvO/mJDfEOIk0xQcxKGNU2j5n0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cP0Bsr/dJMcadVPcvO/mJDfEOIk0xQcxKGNU2j5n0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcP0Bsr%2FdJMcadVPcvO%2FmJDfEOIk0xQcxKGNU2j5n0%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;735&quot; height=&quot;170&quot; data-origin-width=&quot;1007&quot; data-origin-height=&quot;233&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 'SSH 클라이언트' 탭으로 이동 후 아래 단계 순차적으로 시행&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;윈도우의 경우:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다운된 pem 파일이 위치한 폴더에서 cmd 실행&lt;/li&gt;
&lt;li&gt;맨 아래의 명령어(ssh -i ~) 입력하여 우분투 접속&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;805&quot; data-origin-height=&quot;452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/thUMK/dJMcaakxvbM/5nyWY4fy46TJOicUn0rEo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/thUMK/dJMcaakxvbM/5nyWY4fy46TJOicUn0rEo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/thUMK/dJMcaakxvbM/5nyWY4fy46TJOicUn0rEo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FthUMK%2FdJMcaakxvbM%2F5nyWY4fy46TJOicUn0rEo1%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;680&quot; height=&quot;382&quot; data-origin-width=&quot;805&quot; data-origin-height=&quot;452&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 서버에 Java 설치&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에 jar 파일을 올려 실행할 것이기 때문에 설치 필요&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트의 자바 버전에 맞춰 설치&lt;/li&gt;
&lt;li&gt;‼️ EC2 apt 저장소 목록을 업데이트 해주지 않으면 404 에러가 뜸&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1775488184957&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt update
sudo apt install openjdk-17-jdk -y&lt;/code&gt;&lt;/pre&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;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 프로젝트 jar 파일 생성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;로컬&lt;/span&gt;에서 프로젝트의 jar 파일 생성&lt;/li&gt;
&lt;li&gt;인텔리제이의 경우 우측 메뉴에서 손쉽게 생성 가능 (Tasks &amp;gt; build &amp;gt; bootJar)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;401&quot; data-origin-height=&quot;284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nxJAU/dJMcadIh1W6/8CThU8QQbiCXE5MEmlxqN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nxJAU/dJMcadIh1W6/8CThU8QQbiCXE5MEmlxqN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nxJAU/dJMcadIh1W6/8CThU8QQbiCXE5MEmlxqN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnxJAU%2FdJMcadIh1W6%2F8CThU8QQbiCXE5MEmlxqN0%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;401&quot; height=&quot;284&quot; data-origin-width=&quot;401&quot; data-origin-height=&quot;284&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. jar 파일을 우분투 서버에 업로드&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;jar 파일이 위치한 폴더로 이동 (프로젝트 &amp;gt; build &amp;gt; libs)&lt;/li&gt;
&lt;li&gt;cmd 실행 후 아래 명령어 입력
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pem 파일의 경우, 현재 폴더 내에 존재하는 게 아니면 절대 경로로 입력&lt;/li&gt;
&lt;li&gt;‼️퍼블릭 DNS 뒤에 &lt;b&gt;:/home/ubuntu/&lt;/b&gt; 빠트리지 않도록 주의&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1775488605861&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;scp -i &quot;키파일.pem&quot; &quot;프로젝트.jar&quot; ubuntu@EC2_IP:/home/ubuntu/&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt; EC2에서 실행&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 서버에서 실행&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1775495970321&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;java -jar 프로젝트.jar&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;b&gt;2. 접속&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1775495987266&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;http://EC2_IP:8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 없이 접속되는 것을 확인할 수 있음&lt;/p&gt;</description>
      <category>SpringBoot</category>
      <category>AWS</category>
      <category>EC2</category>
      <category>SpringBoot</category>
      <author>naahy</author>
      <guid isPermaLink="true">https://naahy0co.tistory.com/78</guid>
      <comments>https://naahy0co.tistory.com/78#entry78comment</comments>
      <pubDate>Tue, 7 Apr 2026 02:54:28 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] RDS DB 인스턴스 생성(PostgreSQL) 및 SpringBoot 연동</title>
      <link>https://naahy0co.tistory.com/77</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt; RDS 생성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. AWS에서 RDS 검색 후 해당 서비스로 이동 (Aurora and RDS)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 데이터베이스 메뉴로 이동 후 우측 상단의 '데이터베이스 생성 - 전체 구성' 버튼 클릭&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;326&quot; data-origin-height=&quot;213&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crMP9R/dJMcaaSoJQi/n14nVGHY0IcTRKhNyV4vIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crMP9R/dJMcaaSoJQi/n14nVGHY0IcTRKhNyV4vIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crMP9R/dJMcaaSoJQi/n14nVGHY0IcTRKhNyV4vIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrMP9R%2FdJMcaaSoJQi%2Fn14nVGHY0IcTRKhNyV4vIk%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;326&quot; height=&quot;213&quot; data-origin-width=&quot;326&quot; data-origin-height=&quot;213&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;b&gt;3. 데이터베이스 설정&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엔진 옵션은 PostgreSQL로 설정&lt;/li&gt;
&lt;li&gt;템플릿은 프리티어로 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1635&quot; data-origin-height=&quot;339&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLzert/dJMcaiJBedM/myfXR4j6hM9Ob8uXgKnbIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLzert/dJMcaiJBedM/myfXR4j6hM9Ob8uXgKnbIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLzert/dJMcaiJBedM/myfXR4j6hM9Ob8uXgKnbIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLzert%2FdJMcaiJBedM%2FmyfXR4j6hM9Ob8uXgKnbIK%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;1635&quot; height=&quot;339&quot; data-origin-width=&quot;1635&quot; data-origin-height=&quot;339&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DB 인스턴스 이름 설정 후 자격 증명 설정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;마스터 사용자 이름은 자유롭게 작성&lt;/li&gt;
&lt;li&gt;자격 증명 관리는 자체 관리 설정 후 비밀번호 입력&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1153&quot; data-origin-height=&quot;510&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sl3iV/dJMb99MFxx1/XiS6vMj5zfDsK1ZtjyKtCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sl3iV/dJMb99MFxx1/XiS6vMj5zfDsK1ZtjyKtCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sl3iV/dJMb99MFxx1/XiS6vMj5zfDsK1ZtjyKtCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsl3iV%2FdJMb99MFxx1%2FXiS6vMj5zfDsK1ZtjyKtCK%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;1153&quot; height=&quot;510&quot; data-origin-width=&quot;1153&quot; data-origin-height=&quot;510&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스토리지는 기본값 그대로 설정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스토리지 자동 조정 활성화 란은 체크 해제해야 과금 위험 줄어들음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;376&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmLFZ7/dJMcahYgkA7/qQauEoNbQ63Zpca5ZTfEY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmLFZ7/dJMcahYgkA7/qQauEoNbQ63Zpca5ZTfEY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmLFZ7/dJMcahYgkA7/qQauEoNbQ63Zpca5ZTfEY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmLFZ7%2FdJMcahYgkA7%2FqQauEoNbQ63Zpca5ZTfEY0%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;531&quot; height=&quot;376&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;376&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;b&gt;4. DB 생성 완료&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엔드포인트 확인
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터베이스 세부 정보 &amp;gt; 연결 및 보안 &amp;gt; 다음을 사용하여 연결: 엔드포인트&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;410&quot; data-origin-height=&quot;552&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbJS3W/dJMcabwXSjK/HK05MMUAeaJmnRA3bizASk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbJS3W/dJMcabwXSjK/HK05MMUAeaJmnRA3bizASk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbJS3W/dJMcabwXSjK/HK05MMUAeaJmnRA3bizASk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbJS3W%2FdJMcabwXSjK%2FHK05MMUAeaJmnRA3bizASk%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;410&quot; height=&quot;552&quot; data-origin-width=&quot;410&quot; data-origin-height=&quot;552&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;b&gt;5. 연결 설정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에 외부에서 데이터베이스에 접근 가능하도록 퍼블릭 액세스를 가능하게 설정해야 함 (추후 불가능으로 변경)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터베이스 수정 &amp;gt; 연결 &amp;gt; 추가 구성&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;퍼블릭 액세스 가능&lt;/span&gt;으로 설정&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;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;6. 보안 그룹 설정&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인스턴스 세부 정보 &amp;gt; 연결 및 보안 &amp;gt; 보안 그룹 규칙&lt;/li&gt;
&lt;li&gt;보안 그룹 클릭&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;235&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPLPaP/dJMcaf7cJTs/Xoeu80kTxlEYk7wfv7ZIUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPLPaP/dJMcaf7cJTs/Xoeu80kTxlEYk7wfv7ZIUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPLPaP/dJMcaf7cJTs/Xoeu80kTxlEYk7wfv7ZIUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPLPaP%2FdJMcaf7cJTs%2FXoeu80kTxlEYk7wfv7ZIUK%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;603&quot; height=&quot;170&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;235&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인바운드 규칙 &amp;gt; 인바운드 규칙 편집&lt;/li&gt;
&lt;li&gt;'규칙 추가' 버튼 클릭
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타입: PostgreSQL&lt;/li&gt;
&lt;li&gt;소스는 0.0.0.0/0으로 설정 (이미지 속 내 IP 말고 anywhere로 설정)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;405&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bR7nLW/dJMcaiiy5JC/BjlR9x5HzsrvktFPUKAtiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bR7nLW/dJMcaiiy5JC/BjlR9x5HzsrvktFPUKAtiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bR7nLW/dJMcaiiy5JC/BjlR9x5HzsrvktFPUKAtiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbR7nLW%2FdJMcaiiy5JC%2FBjlR9x5HzsrvktFPUKAtiK%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;1670&quot; height=&quot;405&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;405&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'규칙 저장' 버튼 클릭&lt;/li&gt;
&lt;/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;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; SpringBoot 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. application.properties 설정&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;‼️ 이때 db명은 인스턴스명 아님 주의&lt;/li&gt;
&lt;li&gt;연결 및 보안 &amp;gt; 다음을 사용하여 연결(엔드포인트) 에서 db명 확인 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1775492633159&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ===============================
# Database Configuration (PostgreSQL)
# ===============================
spring.datasource.url=jdbc:postgresql://엔드포인트:5432/db명
spring.datasource.username=마스터 사용자명
spring.datasource.password=비밀번호
spring.datasource.driver-class-name=org.postgresql.Driver

# ===============================
# JPA/Hibernate Configuration
# ===============================
spring.jpa.hibernate.ddl-auto=update

spring.jpa.show-sql=true&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 의존성 추가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle 파일에 아래 내용 추가&lt;/p&gt;
&lt;pre id=&quot;code_1775495509577&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation 'org.postgresql:postgresql'&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt; 실행&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬에서 실행해보면 문제 없이 서버가 열리는 것을 볼 수 있음&lt;/p&gt;</description>
      <category>SpringBoot</category>
      <category>AWS</category>
      <category>PostgreSQL</category>
      <category>RDS</category>
      <category>SpringBoot</category>
      <author>naahy</author>
      <guid isPermaLink="true">https://naahy0co.tistory.com/77</guid>
      <comments>https://naahy0co.tistory.com/77#entry77comment</comments>
      <pubDate>Tue, 7 Apr 2026 02:50:58 +0900</pubDate>
    </item>
    <item>
      <title>[백준/Java] 3273: 두 수의 합</title>
      <link>https://naahy0co.tistory.com/76</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/3273&quot;&gt;https://www.acmicpc.net/problem/3273&lt;/a&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;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;n개의 &lt;b&gt;서로 다른 양의 정수&lt;/b&gt;(a1, a2, ..., an)로 이루어진 수열 존재&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자연수 x&lt;/b&gt;가 주어졌을 때, ai + aj = x (1 &amp;le; i &amp;lt; j &amp;le; n)을 만족하는 (ai, aj)쌍의 수를 구해야 함&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1 &amp;le; &lt;code&gt;ai&lt;/code&gt; &amp;le; 1,000,000&lt;/li&gt;
&lt;li&gt;1 &amp;le; 정수 개수 &lt;code&gt;n&lt;/code&gt; &amp;le; 100,000&lt;/li&gt;
&lt;li&gt;1 &amp;le; 자연수 &lt;code&gt;x&lt;/code&gt; &amp;le; 2,000,000&lt;/li&gt;
&lt;/ul&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;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;풀이1 (정렬 + 투 포인터)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 수열 오름차순 정렬&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 투 포인터 사용&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;p1&lt;/code&gt;: 배열의 시작 (가장 작은 값)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;p2&lt;/code&gt;: 배열의 끝 (가장 큰 값)&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;&lt;b&gt;3. 반복하면서 합 비교&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;sum = arr[p1] + arr[p2]&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;sum이 x보다 작은 경우
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;값이 작으므로 더 큰 값 필요&lt;/li&gt;
&lt;li&gt;p1++&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;sum이 x보다 큰 경우
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;값이 크므로 더 작은 값 필요&lt;/li&gt;
&lt;li&gt;p2--&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;sum이 x와 같은 경우
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;p1++, p2-- (이미 사용한 값이므로 둘 다 이동)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  투 포인터는 &lt;b&gt;합의 크기에 따라 이동 방향이 달라야 함&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {

    public static void main(String args[]) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int n = Integer.parseInt(br.readLine());
        int[] arr = new int[n];

        StringTokenizer st = new StringTokenizer(br.readLine());

        for (int i = 0; i &amp;lt; n; i++) {
            arr[i] = Integer.parseInt(st.nextToken());
        }

        int x = Integer.parseInt(br.readLine());

        Arrays.sort(arr);   // 입력 받은 배열 정렬

        // 포인터 위치 지정
        int p1 = 0;
        int p2 = n - 1;

        int ans = 0;    // 쌍의 개수

        while (p1 &amp;lt; p2) {
            if (arr[p1] + arr[p2] &amp;gt; x) {    // 합이 x보다 작은 경우 p1 증가
                p2--;
            } else if  (arr[p1] + arr[p2] &amp;lt; x) {    // 합이 x보다 큰 경우 p2 감소
                p1++;
            } else {
                ans++;
                p1++;
                p2--;
            }
        }

        System.out.println(ans);
    }

}&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;style1&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;풀이2 (boolean 배열 사용)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 숫자 &lt;code&gt;ai&lt;/code&gt;를 봤을 때, &lt;code&gt;x - ai&lt;/code&gt; 값이 존재하는지 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--&amp;gt; boolean 배열로 확인 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. visited 배열 생성&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;code&gt;ai&lt;/code&gt;의 최대값이 1,000,000이므로 &lt;code&gt;boolean[1000001]&lt;/code&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;2. 수열을 하나씩 순회하며 &lt;code&gt;visited[x - ai]&lt;/code&gt; 값이 존재하면 쌍 발견&lt;/b&gt;&lt;/p&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;&lt;b&gt;3. 존재하지 않는 경우 현재 값을 &lt;code&gt;visited&lt;/code&gt;에 저장&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  &lt;b&gt;값의 범위가 제한&lt;/b&gt;되어 있고 작으므로 boolean 배열을 사용하는 것이 빠름&lt;br /&gt;이미 수열이 정렬 되어있는 경우엔 투 포인터를 사용해도 무관&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {

    public static void main(String args[]) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int n = Integer.parseInt(br.readLine());
        int[] arr = new int[n];

        StringTokenizer st = new StringTokenizer(br.readLine());

        for (int i = 0; i &amp;lt; n; i++) {
            arr[i] = Integer.parseInt(st.nextToken());
        }

        int x = Integer.parseInt(br.readLine());
        //////////////////
        boolean[] visited = new boolean[1000001];   // 숫자 존재 여부 저장. ai 최대값
        int ans = 0;

        for (int ai : arr) {
            int target = x - ai;

            // target이 범위 안이고 이미 등장했다면
            if (target &amp;gt; 0 &amp;amp;&amp;amp; target &amp;lt;= 1000000 &amp;amp;&amp;amp; visited[target]) {
                ans++;
            }

            visited[ai] = true;
        }

        System.out.println(ans);
    }

}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩테스트/백준</category>
      <category>백준</category>
      <category>자바</category>
      <category>투포인터</category>
      <author>naahy</author>
      <guid isPermaLink="true">https://naahy0co.tistory.com/76</guid>
      <comments>https://naahy0co.tistory.com/76#entry76comment</comments>
      <pubDate>Wed, 18 Mar 2026 13:59:34 +0900</pubDate>
    </item>
    <item>
      <title>[트러블슈팅/SpringBoot] WebMvcTest로 Controller 테스트 시 JwtUtil 인식 안 됨</title>
      <link>https://naahy0co.tistory.com/74</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지와 함께 게시글 업로드 기능 관련 테스트 코드를 작성하는데, 계속 JwtUtil을 발견할 수 없다는 에러가 뜸&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException
Error creating bean with name 'jwtAuthenticationFilter'
...
Caused by: NoSuchBeanDefinitionException:
No qualifying bean of type 'com.ahy.diarybackend.security.JwtUtil' available&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;게시글 관련 controller에서 사용자가 로그인 상태일 때만 게시글이 업로드 되도록 설정해두었는데,&lt;br /&gt;Controller 테스트에선 JWT 검증이 필요 없도록 &lt;code&gt;@AutoConfigureMockMvc(addFilters = false)&lt;/code&gt;로 Security 필터를 껐음에도 계속 같은 오류가 떴다&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 data-ke-size=&quot;size16&quot;&gt;결론은 SecurityConfig를 직접 작성했기 때문&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;code&gt;@WebMvcTest(DiaryController.class)&lt;/code&gt;로 작성을 했는데,&lt;br /&gt;이는 컨트롤러 관련 Bean만 로딩을 하기 때문에 Security 관련 Bean은 일부만 로딩되면서 의존성이 깨질 수 있다 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;필터를 껐음에도 Spring Security가 실행되는 이유는 &lt;code&gt;addFilters = false&lt;/code&gt;가 MockMvc 요청 시 Filter 실행만 끄는 것이기 때문에 Spring Security 자체는 여전히 ApplicationContext에 로딩됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SpringSecurity를 끄기 위해 &lt;code&gt;@WebMvcTest&lt;/code&gt;에 &lt;code&gt;excludeAutoConfiguration = SecurityAutoConfiguration.class&lt;/code&gt; 값을 줘도 이 설정은 Spring Boot가 자동으로 등록하는 Security 설정만 제외함&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;code&gt;JwtAuthenticationFilter&lt;/code&gt;가 &lt;code&gt;@Component&lt;/code&gt;로 등록되어 있는 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ComponentScan --&amp;gt; JwtAuthenticationFilter 발견 -&amp;gt; Bean 생성 -&amp;gt; JwtUtil 필요 -&amp;gt; 에러&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 필요한 것들을 &lt;code&gt;@MockBean&lt;/code&gt;으로 등록하면 됨&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@MockBean
private JwtUtil jwtUtil;

@MockBean
private UserDetailsService userDetailsService;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결과&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;201&quot; data-origin-height=&quot;82&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLET1n/dJMcadHWmJ1/sVb94hmioVGVc9YtjtG951/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLET1n/dJMcadHWmJ1/sVb94hmioVGVc9YtjtG951/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLET1n/dJMcadHWmJ1/sVb94hmioVGVc9YtjtG951/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLET1n%2FdJMcadHWmJ1%2FsVb94hmioVGVc9YtjtG951%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;201&quot; height=&quot;82&quot; data-origin-width=&quot;201&quot; data-origin-height=&quot;82&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성한 2개의 테스트가 모두 잘 실행됨&lt;/p&gt;</description>
      <category>트러블슈팅</category>
      <category>SpringBoot</category>
      <category>테스트</category>
      <category>트러블슈팅</category>
      <author>naahy</author>
      <guid isPermaLink="true">https://naahy0co.tistory.com/74</guid>
      <comments>https://naahy0co.tistory.com/74#entry74comment</comments>
      <pubDate>Wed, 11 Mar 2026 12:09:00 +0900</pubDate>
    </item>
  </channel>
</rss>