<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>꾸준히 개발공부하는 개발학생</title>
    <link>https://keep-programming-study.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 16 Jun 2026 13:39:10 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>개발학생</managingEditor>
    <image>
      <title>꾸준히 개발공부하는 개발학생</title>
      <url>https://tistory1.daumcdn.net/tistory/5309265/attach/72689414e3714d2ea029980102241a1a</url>
      <link>https://keep-programming-study.tistory.com</link>
    </image>
    <item>
      <title>스프링 인 액션 제5판(크레이그 월즈 지음) - 스프링 시작하기 2</title>
      <link>https://keep-programming-study.tistory.com/201</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*책 내용과 살짝 다르게, 다음과 같은 환경에서 프로젝트를 생성&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/200&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2026.05.25 - [스프링(Spring), 스프링부트(SpringBoot)/스프링(Spring) 기초] - 스프링 인 액션 제5판(크레이그 월즈 지음) - 스프링 시작하기 1&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1779957479350&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;스프링 인 액션 제5판(크레이그 월즈 지음) - 스프링 시작하기 1&quot; data-og-description=&quot;0. 스프링과 스프링 부트의 차이1) 스프링(Spring Framework)의존성 주입(DI), AOP, 트랜잭션 관리 등 엔터프라이즈 애플리케이션의 기반을 제공XML, Java Config, Annotation 등을 이용해 원하는 설정을 직접 해&quot; data-og-host=&quot;keep-programming-study.tistory.com&quot; data-og-source-url=&quot;https://keep-programming-study.tistory.com/200&quot; data-og-url=&quot;https://keep-programming-study.tistory.com/200&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bmyUEX/dJMb9hC7PMm/SYW1EkAX2uigflJc5xc1KK/img.png?width=800&amp;amp;height=482&amp;amp;face=0_0_800_482,https://scrap.kakaocdn.net/dn/5uuxX/dJMb85WZ1k0/C4F8PN6KXuLugMTYItAHBk/img.png?width=800&amp;amp;height=482&amp;amp;face=0_0_800_482,https://scrap.kakaocdn.net/dn/dje7to/dJMb8WeGqem/rpQPqA0Z8vCWybExpy98o1/img.png?width=1051&amp;amp;height=634&amp;amp;face=0_0_1051_634&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/200&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://keep-programming-study.tistory.com/200&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bmyUEX/dJMb9hC7PMm/SYW1EkAX2uigflJc5xc1KK/img.png?width=800&amp;amp;height=482&amp;amp;face=0_0_800_482,https://scrap.kakaocdn.net/dn/5uuxX/dJMb85WZ1k0/C4F8PN6KXuLugMTYItAHBk/img.png?width=800&amp;amp;height=482&amp;amp;face=0_0_800_482,https://scrap.kakaocdn.net/dn/dje7to/dJMb8WeGqem/rpQPqA0Z8vCWybExpy98o1/img.png?width=1051&amp;amp;height=634&amp;amp;face=0_0_1051_634');&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;스프링 인 액션 제5판(크레이그 월즈 지음) - 스프링 시작하기 1&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;0. 스프링과 스프링 부트의 차이1) 스프링(Spring Framework)의존성 주입(DI), AOP, 트랜잭션 관리 등 엔터프라이즈 애플리케이션의 기반을 제공XML, Java Config, Annotation 등을 이용해 원하는 설정을 직접 해&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;keep-programming-study.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 스프링 애플리케이션 작성하기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;0) '홈페이지' 기능 추가를 위해 필요한 것&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;홈페이지의 웹 요청(request)을 처리하는 컨트롤러(controller) 클래스&lt;/li&gt;
&lt;li&gt;홈페이지의 모습을 정의하는 뷰(View) 템플릿&lt;/li&gt;
&lt;li&gt;홈페이지를 테스트하는 간단한 테스트 클래스&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) '홈페이지'의 웹 요청 처리하기&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) 컨트롤러(Controller)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스프링은 스프링 MVC라는 강력한 웹 프레임워크를 갖고 있는데, 그 중심에 컨트롤러가 있음&lt;/li&gt;
&lt;li&gt;웹 요청과 응답을 처리하는 컴포넌트(구성 요소)에 해당&lt;/li&gt;
&lt;li&gt;웹 브라우저를 상대하는 애플리케이션의 경우, &lt;br /&gt;컨트롤러는 선택적으로 모델 데이터를 채워서 응답하는 동시에, 브라우저에 반환되는 HTML을 생성하기 위해 해당 응답의 웹 요청을 뷰에 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) '홈페이지' 컨트롤러 코드 작성&lt;/h4&gt;
&lt;pre id=&quot;code_1779964695289&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package tacos;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

// 애플리케이션의 컨트롤러 역할을 잘 설명해주는 어노테이션
@Controller
public class HomeController {
	
	// 루트 경로인 /의 웹 요청을 처리
	@GetMapping(&quot;/&quot;)
	public String home() {
		// 뷰 이름(home.html)을 반환
		return &quot;home&quot;;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) '홈페이지'의 뷰 정의하기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;/src/main/resources/templates에서 우클릭한 다음 New-&amp;gt;Other를 선택하고, HTML file을 선택하여 home.html 파일 생성&lt;/li&gt;
&lt;li&gt;html xmls=&quot;http://www.thymeleaf.org&quot;는 Thymeleaf 문법을 사용하기 위해 필수&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Thymeleaf @{} 표현식 내부에 th:src 속성으로 이미지 경로를 지정&lt;br /&gt;-&amp;gt; /src/main/resources/static 경로에 images 폴더를 만들고, 그 안에 이미지가 있어야 함&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1779965117778&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- home.html --&amp;gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html xmls=&quot;http://www.thymeleaf.org&quot;&amp;gt;
	&amp;lt;head&amp;gt;
		&amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
		&amp;lt;title&amp;gt;Taco Cloud&amp;lt;/title&amp;gt;
	&amp;lt;/head&amp;gt;
	
	&amp;lt;body&amp;gt;
		&amp;lt;h1&amp;gt;Welcome to...&amp;lt;/h1&amp;gt;
		&amp;lt;img th:src=&quot;@{/images/TacoCloud.png}&quot;/&amp;gt;
	&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&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;3) 1)에서 만든 컨트롤러 테스트하기&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) src/test/java 경로의 tacos 패키지에서 클래스를 만들고 다음과 같이 코드를 작성&lt;/h4&gt;
&lt;pre id=&quot;code_1780047553906&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package tacos;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.hamcrest.Matchers.containsString;

//HomeController의 웹 페이지 테스트 클래스
@WebMvcTest(HomeController.class) // HomeController만 로드해서 테스트
public class HomeControllerTest {
 
 // MockMvc: 웹 요청을 시뮬레이션하고 응답을 검증할 수 있는 객체
 @Autowired
 private MockMvc mockMvc;
 
 @Test
 public void testHomePage() throws Exception {
     // &quot;/&quot; 경로로 GET 요청을 수행하고 응답을 검증
     mockMvc.perform(get(&quot;/&quot;))
         // HTTP 응답 상태가 200 OK인지 확인
         .andExpect(status().isOk())
         // 반환된 뷰 이름이 &quot;home&quot;인지 확인
         .andExpect(view().name(&quot;home&quot;))
         // 응답 본문에 &quot;Welcome to...&quot; 문자열이 포함되어 있는지 확인
         .andExpect(content().string(
             containsString(&quot;Welcome to...&quot;)));
 }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) 프로젝트를 우클릭하고 Run As -&amp;gt; JUnit Test 선택&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;951&quot; data-origin-height=&quot;631&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJISLr/dJMcahdxFVj/ebZSOWrzhttf7eJWOGBkPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJISLr/dJMcahdxFVj/ebZSOWrzhttf7eJWOGBkPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJISLr/dJMcahdxFVj/ebZSOWrzhttf7eJWOGBkPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJISLr%2FdJMcahdxFVj%2FebZSOWrzhttf7eJWOGBkPk%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;951&quot; height=&quot;631&quot; data-origin-width=&quot;951&quot; data-origin-height=&quot;631&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(3) JUnit Test 결과 확인&amp;nbsp;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음과 같이 테스트가 에러 없이 정상적으로 수행됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;367&quot; data-origin-height=&quot;208&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vnNoW/dJMcacXAfS6/Rk91tEJG6JIGFeGrmMLkPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vnNoW/dJMcacXAfS6/Rk91tEJG6JIGFeGrmMLkPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vnNoW/dJMcacXAfS6/Rk91tEJG6JIGFeGrmMLkPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvnNoW%2FdJMcacXAfS6%2FRk91tEJG6JIGFeGrmMLkPk%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;367&quot; height=&quot;208&quot; data-origin-width=&quot;367&quot; data-origin-height=&quot;208&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4) 애플리케이션 빌드하고 실행하기&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) 애플리케이션 서버 실행&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;STS IDE의 왼쪽 아래에 있는 Boot Dashboard 탭에서 실행 가능&lt;br /&gt;-&amp;gt; 탭이 보이지 않으면, Window -&amp;gt; Show View -&amp;gt; Other를 선택한 다음, 대화상자가 열리면 Boot를 검색하여 Boot Dashboard를 선택해서 Open 클릭&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;381&quot; data-origin-height=&quot;137&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ejX1S9/dJMcaiDvnMe/NZcAbE24SBw3YvxpDv3nt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ejX1S9/dJMcaiDvnMe/NZcAbE24SBw3YvxpDv3nt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ejX1S9/dJMcaiDvnMe/NZcAbE24SBw3YvxpDv3nt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FejX1S9%2FdJMcaiDvnMe%2FNZcAbE24SBw3YvxpDv3nt1%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;381&quot; height=&quot;137&quot; data-origin-width=&quot;381&quot; data-origin-height=&quot;137&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;Boot Dashboard 탭에서 local 텍스트의 왼쪽에 있는 화살표를 눌러 확장한 다음, &lt;br /&gt;taco-cloud를 선택한 상태에서 시작 버튼(빨간 네모와 초록색 세모가 같이 있는 아이콘) 클릭&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;387&quot; data-origin-height=&quot;247&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPZgjy/dJMcaaMdTEE/TW0rfFBryKW3yM2QRkUx7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPZgjy/dJMcaaMdTEE/TW0rfFBryKW3yM2QRkUx7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPZgjy/dJMcaaMdTEE/TW0rfFBryKW3yM2QRkUx7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPZgjy%2FdJMcaaMdTEE%2FTW0rfFBryKW3yM2QRkUx7k%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;387&quot; height=&quot;247&quot; data-origin-width=&quot;387&quot; data-origin-height=&quot;247&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) 웹 브라우저에서 localhost:8080으로 접속해보기&amp;nbsp;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저: 크롬(Chrome), 이미지 출처: 위키백과&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;765&quot; data-origin-height=&quot;667&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EB70f/dJMcaf7Ou7L/5d33RXNHKAYzjNLWWdpYfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EB70f/dJMcaf7Ou7L/5d33RXNHKAYzjNLWWdpYfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EB70f/dJMcaf7Ou7L/5d33RXNHKAYzjNLWWdpYfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEB70f%2FdJMcaf7Ou7L%2F5d33RXNHKAYzjNLWWdpYfk%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;765&quot; height=&quot;667&quot; data-origin-width=&quot;765&quot; data-origin-height=&quot;667&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 스프링 부트 DevTools&lt;/h2&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;br /&gt;자동으로 애플리케이션 다시 시작 &amp;amp; 브라우저 새로고침&amp;nbsp;&lt;br /&gt;=&amp;gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;의존성 변경은 적용되지 않음&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;템플릿 캐시를 자동으로 비활성화&lt;br /&gt;-&amp;gt; 기본적으로 템플릿 엔진(Thymeleaf, FreeMarker 등)은 템플릿의 파싱(코드 분석) 결과를 캐시에 저장하고 사용하는 형태&lt;br /&gt;=&amp;gt; 따라서, &lt;span style=&quot;color: #ee2323;&quot;&gt;개발 환경에서 템플릿 엔진이 HTML 템플릿 파일을 캐싱하지 않고 매번 새로 읽어서 렌더링할 수 있게 함&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;H2 데이터베이스 사용 시 자동으로 H2 Console을 활성화&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 자동으로 브라우저를 새로고침하고 템플릿 캐시를 비활성화하는 방법&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 운영 시점에서는 템플릿 캐시를 활성화하는 것이 좋지만, 개발 시점에는 비활성화하는 것이 좋음&amp;nbsp;&lt;br /&gt;-&amp;gt; DevTools가 모든 템플릿 캐싱을 자동으로 비활성화하는 역할을 함&amp;nbsp;&lt;br /&gt;=&amp;gt; &lt;b&gt;필자는 매번 새로고침하는 것에 익숙해서 적용하지 않음..&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;DevTools 사용 시에는 자동으로 LiveReload 서버를 활성화&lt;br /&gt;-&amp;gt; LiveReload 자체는 유용하지 않으나, &lt;br /&gt;&amp;nbsp; &amp;nbsp; 그와 부합하는 브라우저 플러그인과 연결되면 브라우저에 전달되는 모든 것에 변경이 생기면 자동 새로고침을 해줌&lt;br /&gt;=&amp;gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;다만, 현재는 livereload.com 링크에 접속되지 않으며, Chrome의 확장 프로그램에서는 설치 불가능한 것으로 보임&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;* H2 Console(콘솔)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발용으로 H2 데이터베이스의 사용을 선택한다면,&lt;br /&gt;웹 브라우저에서 사용 가능한 H2 Console을 DevTools가 자동으로 활성화&lt;/li&gt;
&lt;li&gt;H2 데이터베이스 사용 시, &lt;br /&gt;웹 브라우저에서 localhost:8080/h2-console에 접속하면 애플리케이션에서 사용하는 데이터를 알 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 리뷰하기&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) 타코 클라우드 애플리케이션 구축 단계&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스프링 Initializr를 사용하여 초기 구조 생성&lt;/li&gt;
&lt;li&gt;홈페이지 웹 요청 처리를 위한 컨트롤러 클래스 작성&lt;/li&gt;
&lt;li&gt;홈페이지를 보여주기 위한 뷰 탬플릿 정의&lt;/li&gt;
&lt;li&gt;애플리케이션 테스트를 위한 간단한 테스트 클래스 작성&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) 애플리케이션의 요구 충족을 위해 스프링이 내부적으로 하는 일&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스프링 MVC 활성화를 위해 스프링 애플리케이션 컨텍스트에 관련된 Bean 들을 구성&lt;/li&gt;
&lt;li&gt;내장된 톰캣 서버를 스프링 애플리케이션 컨텍스트에 구성&lt;/li&gt;
&lt;li&gt;Thymeleaf 템플릿을 사용하는 스프링 MVC 뷰를 나타내기 위해 Thymeleaf 뷰 리졸버(resolver)를 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;=&amp;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;&amp;nbsp;&lt;/p&gt;</description>
      <category>스프링(Spring), 스프링부트(SpringBoot)/스프링(Spring) 기초</category>
      <category>Java</category>
      <category>JUnit</category>
      <category>Spring Tool Suite 4(STS4)</category>
      <category>빈(bean)</category>
      <category>스프링 애플리케이션 컨텍스트(Spring application context)</category>
      <category>스프링 이니셜라이저(Initializer)</category>
      <category>스프링(spring)</category>
      <category>의존성 주입(Dependency Injection/DI)</category>
      <category>자바</category>
      <category>컨테이너(container)</category>
      <author>개발학생</author>
      <guid isPermaLink="true">https://keep-programming-study.tistory.com/201</guid>
      <comments>https://keep-programming-study.tistory.com/201#entry201comment</comments>
      <pubDate>Wed, 10 Jun 2026 19:30:26 +0900</pubDate>
    </item>
    <item>
      <title>스프링 인 액션 제5판(크레이그 월즈 지음) - 스프링 시작하기 1</title>
      <link>https://keep-programming-study.tistory.com/200</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;0. 스프링과 스프링 부트의 차이&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 스프링(Spring Framework)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;의존성 주입(DI), AOP, 트랜잭션 관리 등 엔터프라이즈 애플리케이션의 기반을 제공&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;XML, Java Config, Annotation 등을 이용해 원하는 설정을 직접 해야 함&lt;br /&gt;: 웹 애플리케이션을 만들려면 DispatcherServlet, ViewResolver, DataSource 등을 직접 등록해야 함 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;원하는 대로 커스터마이징 가능하지만, 초기 설정이 복잡하여 시간이 많이 걸림&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;2) 스프링 부트(Spring Boot) &lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;스프링을 더 쉽게 쓰도록 만든 확장판&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;대부분의 설정을 &lt;b&gt;자동 설정(Auto Configuration)&lt;/b&gt; 가능하고, 필요한 부분만 오버라이드 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Tomcat, Jetty 같은 서버가 내장되어 있어 java -jar만으로 실행 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;스타터 의존성(spring-boot-starter-web, spring-boot-starter-data-jpa 등)을 추가하면 빠른 개발 시작 가능&lt;/span&gt;&lt;span&gt;&lt;br /&gt;-&amp;gt; @SpringBootApplication 하나만 붙이면 웹 서버가 바로 뜨고, REST API를 만들 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 스프링이란?&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 애플리케이션 기본 원리&lt;/h3&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;b&gt;컴포넌트 여러 개로 구성&lt;/b&gt;되며,&amp;nbsp;&lt;br /&gt;각 컴포넌트는 다른 애플리케이션의 구성 요소와 협력하여 작업을 처리&lt;/li&gt;
&lt;li&gt;애플리케이션이 실행될 때는 각 컴포넌트가 어떻게든 생성되어야 하고 상호 간 알 수 있어야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 스프링에서의 애플리케이션 동작 원리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스프링 애플리케이션 컨텍스트(Spring application context)라는 이름의 &lt;b&gt;컨테이너(container)&lt;/b&gt;를 통해,&lt;br /&gt;&lt;b&gt;애플리케이션 컴포넌트에 해당하는 빈(Bean)들을 생성 및 관리&lt;br /&gt;&lt;/b&gt;-&amp;gt; 빈(Bean)들은 스프링 애플리케이션 컨텍스트 내부에서 서로 연결되어 완전한 애플리케이션을 만듬&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빈(Bean) 간의 상호 연결은 의존성 주입(Dependency Injection/DI)&lt;/b&gt;을 기반으로 수행&lt;/li&gt;
&lt;li&gt;최신 버전의 스프링에서는 XML 파일이 아닌, &lt;b&gt;자바 기반의 구성 클래스(configuration)&lt;/b&gt;을 사용&lt;br /&gt;-&amp;gt; 컴포넌트와 다른 컴포넌트와의 관계를 나타내서 &lt;b&gt;빈(Bean)을 상호 연결&lt;/b&gt;하는 역할&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1779187348945&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Java 기반 Configuration 예시 코드
@Configuration
public class ServiceConfiguration {
  @Bean
  public InventoryService inventoryService() {
    return new InventoryService();
  }
  
  @Bean 
  public ProductService productService() {
    return new ProductService();
  }
}&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;애플리케이션 컴포넌트는 &lt;b&gt;스프링 애플리케이션 컨텍스트(Spring application context)&lt;/b&gt;에 의해 관리되고, 상호 주입됨&amp;nbsp;&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;922&quot; data-origin-height=&quot;458&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HqRjr/dJMcaaeiZJ8/wHzi2DYe6lKDK83Q8G9XI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HqRjr/dJMcaaeiZJ8/wHzi2DYe6lKDK83Q8G9XI0/img.png&quot; data-alt=&quot;스프링 애플리케이션 컨텍스트 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HqRjr/dJMcaaeiZJ8/wHzi2DYe6lKDK83Q8G9XI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHqRjr%2FdJMcaaeiZJ8%2FwHzi2DYe6lKDK83Q8G9XI0%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;922&quot; height=&quot;458&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;458&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스프링 애플리케이션 컨텍스트 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 스프링 애플리케이션 초기 설정하기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 스프링 이니셜라이저(Initializer)를 사용한 애플리케이션 초기화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스프링 이니셜라이저(Initializer)는 REST API를 사용하는 브라우저 기반 웹 애플리케이션으로,&amp;nbsp;&lt;br /&gt;원하는 기능을 구현할 수 있는 스프링 프로젝트의 구조를 생성해 줌&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Spring Tool Suite(STS)의 스프링 Initializer 지원 기능을 사용하여 새로운 프로젝트 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) Spring Tool Suite(STS)에서 스프링 프로젝트 초기 설정하기&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) Spring Tools 4 for Eclipse, Java JDK 17버전 다운로드 (Windows11 환경)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이전에 스프링부트(Spring Boot)를 공부할 때 썼던 환경설정을 그대로 사용&lt;br /&gt;: &lt;a style=&quot;letter-spacing: 0px;&quot; href=&quot;https://keep-programming-study.tistory.com/166&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.07.15 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링 부트 핵심 가이드(장정우 지음) - Gradle 기초, API를 작성하는 다양한 방법&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1779443254183&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;스프링 부트 핵심 가이드(장정우 지음) - Gradle 기초, API를 작성하는 다양한 방법&quot; data-og-description=&quot;*책 내용과 다르게, 다음과 같은 환경에서 프로젝트 생성 Windows11(윈도우 11) 환경자바 JDK 17 버전 설치 https://yungenie.tistory.com/11 [Java] 차근차근 Java 설치하기 (JDK17, Window 11)자바 개발 도구 설치 방법&quot; data-og-host=&quot;keep-programming-study.tistory.com&quot; data-og-source-url=&quot;https://keep-programming-study.tistory.com/166&quot; data-og-url=&quot;https://keep-programming-study.tistory.com/166&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bH1ldg/dJMb9aKK2im/UUJuvN3NsBmuWvK68pa7k1/img.png?width=800&amp;amp;height=677&amp;amp;face=0_0_800_677,https://scrap.kakaocdn.net/dn/1MiTg/dJMb9eTVv0m/eAjjMvdEXR9kF95Jd1oFDk/img.png?width=800&amp;amp;height=677&amp;amp;face=0_0_800_677,https://scrap.kakaocdn.net/dn/eqaAWX/dJMb887eOq0/wXCR9VaFMeVoWcnZy8RWN1/img.png?width=1272&amp;amp;height=811&amp;amp;face=0_0_1272_811&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/166&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://keep-programming-study.tistory.com/166&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bH1ldg/dJMb9aKK2im/UUJuvN3NsBmuWvK68pa7k1/img.png?width=800&amp;amp;height=677&amp;amp;face=0_0_800_677,https://scrap.kakaocdn.net/dn/1MiTg/dJMb9eTVv0m/eAjjMvdEXR9kF95Jd1oFDk/img.png?width=800&amp;amp;height=677&amp;amp;face=0_0_800_677,https://scrap.kakaocdn.net/dn/eqaAWX/dJMb887eOq0/wXCR9VaFMeVoWcnZy8RWN1/img.png?width=1272&amp;amp;height=811&amp;amp;face=0_0_1272_811');&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;스프링 부트 핵심 가이드(장정우 지음) - Gradle 기초, API를 작성하는 다양한 방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;*책 내용과 다르게, 다음과 같은 환경에서 프로젝트 생성 Windows11(윈도우 11) 환경자바 JDK 17 버전 설치 https://yungenie.tistory.com/11 [Java] 차근차근 Java 설치하기 (JDK17, Window 11)자바 개발 도구 설치 방법&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;keep-programming-study.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2ne97/btsPgUiDCuF/eVBIrd1OJpcnARSiekch31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2ne97/btsPgUiDCuF/eVBIrd1OJpcnARSiekch31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2ne97/btsPgUiDCuF/eVBIrd1OJpcnARSiekch31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2ne97%2FbtsPgUiDCuF%2FeVBIrd1OJpcnARSiekch31%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;1051&quot; height=&quot;634&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;634&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) 새로운 스프링 프로젝트 생성&amp;nbsp;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;File -&amp;gt; New -&amp;gt; Spring Starter Project 클릭 후 다음과 같이 입력한 후 Next 클릭&amp;nbsp;&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;588&quot; data-origin-height=&quot;773&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lliJ8/dJMcahLivHE/UpVAbZHGwugmD3azseYqt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lliJ8/dJMcahLivHE/UpVAbZHGwugmD3azseYqt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lliJ8/dJMcahLivHE/UpVAbZHGwugmD3azseYqt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlliJ8%2FdJMcahLivHE%2FUpVAbZHGwugmD3azseYqt0%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;588&quot; height=&quot;773&quot; data-origin-width=&quot;588&quot; data-origin-height=&quot;773&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;의존성 추가 화면에서 다음과 같이 체크한 후 Finish 클릭&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;593&quot; data-origin-height=&quot;782&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l2LaR/dJMcacpHqxC/irLgN5w43vKDv5k6FUgAhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l2LaR/dJMcacpHqxC/irLgN5w43vKDv5k6FUgAhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l2LaR/dJMcacpHqxC/irLgN5w43vKDv5k6FUgAhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl2LaR%2FdJMcacpHqxC%2FirLgN5w43vKDv5k6FUgAhK%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;593&quot; height=&quot;782&quot; data-origin-width=&quot;593&quot; data-origin-height=&quot;782&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) 스프링 프로젝트 구조 살펴보기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;애플리케이션 소스 코드는 src/main/java, 테스트 코드는 src/test/java, 자바 리소스가 아닌 것은 src/main/resources에 저장
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mvnw, nvnw.cmd: 메이븐(Maven) 래퍼(Wrapper) 스크립트로, &lt;br /&gt;메이븐이 각 컴퓨터에 설치되어 있지 않아도 이 스크립트로 프로젝트 빌드 가능&lt;/li&gt;
&lt;li&gt;pom.xml: 메이븐(Maven) 빌드 명세(프로젝트 빌드 시 필요한 정보)를 지정한 파일&lt;/li&gt;
&lt;li&gt;TaoCloudApplication.java: 스프링 부트 메인 클래스&lt;/li&gt;
&lt;li&gt;application.properties: 초기에는 내용이 없지만, 구성 속성을 지정 가능한 파일&lt;/li&gt;
&lt;li&gt;templates: 초기에는 내용이 없지만, 브라우저에 콘텐츠를 보여주는 템플릿 파일을 두는 폴더로 Thymeleaf 템플릿을 추가할 수 있음&lt;/li&gt;
&lt;li&gt;TacoCloudApplicationTests.java: 스프링 애플리케이션이 성공적으로 로드되는지 확인하는 간단한 테스트 클래스&lt;br /&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;360&quot; data-origin-height=&quot;581&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfGTWm/dJMcahYOkZI/NjA4xkPoU3WZlZz9cEmE0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfGTWm/dJMcahYOkZI/NjA4xkPoU3WZlZz9cEmE0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfGTWm/dJMcahYOkZI/NjA4xkPoU3WZlZz9cEmE0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfGTWm%2FdJMcahYOkZI%2FNjA4xkPoU3WZlZz9cEmE0k%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;360&quot; height=&quot;581&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;581&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) 빌드 명세 살펴보기&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스프링 Initializer가 우리가 지정한 내용(의존성)들을 갖는 pom.xml 파일을 생성하는데, 해당 파일에서 빌드 명세를 정의&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lt;parent&amp;gt;의 &amp;lt;version&amp;gt;: 부모 POM(Project Object Model)으로 spring-boot-starter-parent를 갖는다는 것을 지정&lt;br /&gt;-&amp;gt; 스프링 프로젝트에 흔히 사용되는 여러 라이브러리의 의존성 관리 제공 (버전을 지정할 필요 없음)&lt;/li&gt;
&lt;li&gt;&amp;lt;dependencies&amp;gt;: STS의 프로젝트 위저드에서 선택한 각 의존성이 &amp;lt;dependency&amp;gt; 요소로 지정되는데, &lt;br /&gt;Spring Web/Thymeleaf/Spring Boot DevTools/Lombok이 우리가 선택한 것이고 spring-boot-starter-test가 우리가 테스트를 작성할 것에 대비하여 스프링 Initializer가 자동으로 추가한 것
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring Web/Thymeleaf/Test는 &amp;lt;artifactId&amp;gt;에 starter 단어를 포함하고 있는데,&lt;br /&gt;이들은 스프링 부트 스타터 의존성이기에, 자체적으로 라이브러리 코드를 갖지 않고 다른 라이브러리의 것을 사용&amp;nbsp;&lt;br /&gt;-&amp;gt; 모든 라이브러리의 의존성을 선언하지 않아도 되므로 빌드 파일이 작아지고 관리하기 쉬워지며, &lt;br /&gt;&amp;nbsp; &amp;nbsp; 라이브러리들의 버전을 걱정하지 않아도 됨&amp;nbsp;&lt;/li&gt;
&lt;/ul&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;메이븐을 사용하는 애플리케이션 실행 가능&lt;/li&gt;
&lt;li&gt;의존성에 지정된 모든 라이브러리가 &lt;br /&gt;실행 가능 JAR 파일에 포함되어 있는지 + 런타임 시 classpath에서 찾을 수 있는지 확인&lt;/li&gt;
&lt;li&gt;실행 가능 JAR 파일에 메인 클래스로 부트스트랩(구동) 클래스(TacoCloudApplication.java)를 나타내는 매니페스트 파일을 JAR 파일에 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1779447129707&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
	xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd&quot;&amp;gt;
	&amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;
	&amp;lt;parent&amp;gt;
		&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;spring-boot-starter-parent&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;4.0.6&amp;lt;/version&amp;gt;
		&amp;lt;relativePath/&amp;gt; &amp;lt;!-- lookup parent from repository --&amp;gt;
	&amp;lt;/parent&amp;gt;
	&amp;lt;groupId&amp;gt;sia&amp;lt;/groupId&amp;gt;
	&amp;lt;artifactId&amp;gt;taco-cloud&amp;lt;/artifactId&amp;gt;
	&amp;lt;version&amp;gt;0.0.1-SNAPSHOT&amp;lt;/version&amp;gt;
	&amp;lt;name&amp;gt;taco-cloud&amp;lt;/name&amp;gt;
	&amp;lt;description&amp;gt;Taco Cloud Example&amp;lt;/description&amp;gt;
	&amp;lt;url/&amp;gt;
	&amp;lt;licenses&amp;gt;
		&amp;lt;license/&amp;gt;
	&amp;lt;/licenses&amp;gt;
	&amp;lt;developers&amp;gt;
		&amp;lt;developer/&amp;gt;
	&amp;lt;/developers&amp;gt;
	&amp;lt;scm&amp;gt;
		&amp;lt;connection/&amp;gt;
		&amp;lt;developerConnection/&amp;gt;
		&amp;lt;tag/&amp;gt;
		&amp;lt;url/&amp;gt;
	&amp;lt;/scm&amp;gt;
	&amp;lt;properties&amp;gt;
		&amp;lt;java.version&amp;gt;17&amp;lt;/java.version&amp;gt;
	&amp;lt;/properties&amp;gt;
	&amp;lt;dependencies&amp;gt;
		&amp;lt;dependency&amp;gt;
			&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
			&amp;lt;artifactId&amp;gt;spring-boot-starter-thymeleaf&amp;lt;/artifactId&amp;gt;
		&amp;lt;/dependency&amp;gt;
		&amp;lt;dependency&amp;gt;
			&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
			&amp;lt;artifactId&amp;gt;spring-boot-starter-web&amp;lt;/artifactId&amp;gt;
		&amp;lt;/dependency&amp;gt;

		&amp;lt;dependency&amp;gt;
			&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
			&amp;lt;artifactId&amp;gt;spring-boot-devtools&amp;lt;/artifactId&amp;gt;
			&amp;lt;scope&amp;gt;runtime&amp;lt;/scope&amp;gt;
			&amp;lt;optional&amp;gt;true&amp;lt;/optional&amp;gt;
		&amp;lt;/dependency&amp;gt;
		&amp;lt;dependency&amp;gt;
			&amp;lt;groupId&amp;gt;org.projectlombok&amp;lt;/groupId&amp;gt;
			&amp;lt;artifactId&amp;gt;lombok&amp;lt;/artifactId&amp;gt;
			&amp;lt;optional&amp;gt;true&amp;lt;/optional&amp;gt;
		&amp;lt;/dependency&amp;gt;
		&amp;lt;dependency&amp;gt;
			&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
			&amp;lt;artifactId&amp;gt;spring-boot-starter-thymeleaf-test&amp;lt;/artifactId&amp;gt;
			&amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
		&amp;lt;/dependency&amp;gt;
		&amp;lt;dependency&amp;gt;
			&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
			&amp;lt;artifactId&amp;gt;spring-boot-starter-webmvc-test&amp;lt;/artifactId&amp;gt;
			&amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
		&amp;lt;/dependency&amp;gt;
	&amp;lt;/dependencies&amp;gt;

	&amp;lt;build&amp;gt;
		&amp;lt;plugins&amp;gt;
			&amp;lt;plugin&amp;gt;
				&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
				&amp;lt;artifactId&amp;gt;spring-boot-maven-plugin&amp;lt;/artifactId&amp;gt;
				&amp;lt;configuration&amp;gt;
					&amp;lt;excludes&amp;gt;
						&amp;lt;exclude&amp;gt;
							&amp;lt;groupId&amp;gt;org.projectlombok&amp;lt;/groupId&amp;gt;
							&amp;lt;artifactId&amp;gt;lombok&amp;lt;/artifactId&amp;gt;
						&amp;lt;/exclude&amp;gt;
					&amp;lt;/excludes&amp;gt;
				&amp;lt;/configuration&amp;gt;
			&amp;lt;/plugin&amp;gt;
			&amp;lt;plugin&amp;gt;
				&amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
				&amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;
				&amp;lt;executions&amp;gt;
					&amp;lt;execution&amp;gt;
						&amp;lt;id&amp;gt;default-compile&amp;lt;/id&amp;gt;
						&amp;lt;phase&amp;gt;compile&amp;lt;/phase&amp;gt;
						&amp;lt;goals&amp;gt;
							&amp;lt;goal&amp;gt;compile&amp;lt;/goal&amp;gt;
						&amp;lt;/goals&amp;gt;
						&amp;lt;configuration&amp;gt;
							&amp;lt;annotationProcessorPaths&amp;gt;
								&amp;lt;path&amp;gt;
									&amp;lt;groupId&amp;gt;org.projectlombok&amp;lt;/groupId&amp;gt;
									&amp;lt;artifactId&amp;gt;lombok&amp;lt;/artifactId&amp;gt;
								&amp;lt;/path&amp;gt;
							&amp;lt;/annotationProcessorPaths&amp;gt;
						&amp;lt;/configuration&amp;gt;
					&amp;lt;/execution&amp;gt;
					&amp;lt;execution&amp;gt;
						&amp;lt;id&amp;gt;default-testCompile&amp;lt;/id&amp;gt;
						&amp;lt;phase&amp;gt;test-compile&amp;lt;/phase&amp;gt;
						&amp;lt;goals&amp;gt;
							&amp;lt;goal&amp;gt;testCompile&amp;lt;/goal&amp;gt;
						&amp;lt;/goals&amp;gt;
						&amp;lt;configuration&amp;gt;
							&amp;lt;annotationProcessorPaths&amp;gt;
								&amp;lt;path&amp;gt;
									&amp;lt;groupId&amp;gt;org.projectlombok&amp;lt;/groupId&amp;gt;
									&amp;lt;artifactId&amp;gt;lombok&amp;lt;/artifactId&amp;gt;
								&amp;lt;/path&amp;gt;
							&amp;lt;/annotationProcessorPaths&amp;gt;
						&amp;lt;/configuration&amp;gt;
					&amp;lt;/execution&amp;gt;
				&amp;lt;/executions&amp;gt;
			&amp;lt;/plugin&amp;gt;
		&amp;lt;/plugins&amp;gt;
	&amp;lt;/build&amp;gt;

&amp;lt;/project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) 애플리케이션의 부트스트랩(구동)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행 가능 JAR 파일에서 애플리케이션을 실행하므로 제일 먼저 시작되는 부트스트랩 클래스 + 애플리케이션을 부트스트랩하기 위한 최소한의 스프링 구성이 있어야 함&lt;/li&gt;
&lt;li&gt;@SpringBootApplication은 세 개의 어노테이션이 결합한 것
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@SpringBootConfiguration: 현재 클래스를 구성 클래스로 지정, 필요 시 자바 기반 스프링 프레임워크 구성을 현재 클래스에 추가&lt;/li&gt;
&lt;li&gt;@EnableAutoConfiguration: 스프링 부트 자동 구성을 활성화하여 우리가 필요로 하는 컴포넌트들을 자동으로 구성하도록 스프링 부트에 알려줌&lt;/li&gt;
&lt;li&gt;@ComponentScan: 컴포넌트 검색을 활성화하여, @Component,@Controller,@Service 등의 어노테이션과 함께 클래스 선언 가능&amp;nbsp;&lt;br /&gt;-&amp;gt; 스프링에서 자동으로 위 어노테이션이 붙은 클래스를 찾아 스프링 애플리케이션 컨텍스트에 컴포넌트로 등록&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;main() 메서드: 실제로 애플리케이션을 시작시키고 스프링 애플리케이션 컨텍스트를 생성하는 SpringApplication 클래스의 run() 메서드를 호출&lt;br /&gt;-&amp;gt; run()에 전달되는 두 개의 매개변수는 구성 클래스와 명령행(Command-line) 인자로, 구성 클래스와 부트스트랩 클래스를 동일하게 지정&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1779717757211&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package tacos;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class TacoCloudApplication {

	public static void main(String[] args) {
		SpringApplication.run(TacoCloudApplication.class, args);
	}

}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(3) 애플리케이션 테스트하기 - 스프링 Initializer에서 제공하는 테스트 클래스&lt;/h4&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;br /&gt;-&amp;gt; 이 테스트가 실패할 경우 스프링 애플리케이션 컨텍스트의 생성을 저해하는 코드가 있는 것&lt;/li&gt;
&lt;li&gt;@SprintBootTest: 스프링 부트 기능으로 테스트를 시작하라는 것을 JUnit에 알려주며,&amp;nbsp;&lt;br /&gt;main() 메서드의 SpringApplication.run() 호출에 부합하는 테스트 클래스를 나타냄&lt;/li&gt;
&lt;li&gt;테스트 메서드: 이것이 없다면 @SpringBootTest는 아무 일도 하지 않음&amp;nbsp;&lt;br /&gt;-&amp;gt; 다만 contextLoad() 처럼 실행 코드는 없어도 테스트 메서드가 있다면 @SpringBootTest가 작업 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1779718473131&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package tacos;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class TacoCloudApplicationTests {

	@Test
	void contextLoads() {
	}

}&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*2편이 다음 글에서 계속됩니다&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;</description>
      <category>스프링(Spring), 스프링부트(SpringBoot)/스프링(Spring) 기초</category>
      <category>Java</category>
      <category>spring</category>
      <category>Spring Tool Suite 4(STS4)</category>
      <category>빈(bean)</category>
      <category>스프링</category>
      <category>스프링 애플리케이션 컨텍스트(Spring application context)</category>
      <category>스프링 이니셜라이저(Initializer)</category>
      <category>의존성 주입(Dependency Injection/DI)</category>
      <category>자바</category>
      <category>컨테이너(container)</category>
      <author>개발학생</author>
      <guid isPermaLink="true">https://keep-programming-study.tistory.com/200</guid>
      <comments>https://keep-programming-study.tistory.com/200#entry200comment</comments>
      <pubDate>Mon, 25 May 2026 23:25:48 +0900</pubDate>
    </item>
    <item>
      <title>제너럴리스트 지향 프리랜서 겸 개발공부하는 사람의 근황...</title>
      <link>https://keep-programming-study.tistory.com/199</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 스프링부트 핵심 가이드(장정우 지음) 완권[완독?]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 4월 25일에 올렸던 글이 해당 서적의 마지막 부분까지 다룬 글이었다.&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;(지금 밖이라서 무슨 책이었는지 잘 기억은 안 나지만... 아마 아래 이미지의 책이었던 것 같다)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;597&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bc285J/dJMb99M340b/U4b8jS52ICBniIj7nwGFZ0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bc285J/dJMb99M340b/U4b8jS52ICBniIj7nwGFZ0/img.jpg&quot; data-alt=&quot;출처-교보문고(https://product.kyobobook.co.kr/detail/S000001942493)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bc285J/dJMb99M340b/U4b8jS52ICBniIj7nwGFZ0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbc285J%2FdJMb99M340b%2FU4b8jS52ICBniIj7nwGFZ0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;458&quot; height=&quot;597&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;597&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처-교보문고(https://product.kyobobook.co.kr/detail/S000001942493)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;b&gt;스프링부트가 스프링의 핵심 개념(IoC, DI, AOP 등)을 자동화&amp;middot;간소화한 확장판..&lt;/b&gt;이기 때문이다. 스프링을 이해하지 못하면 스프링부트의 편리함이 왜 필요한지, 스프링부트에서 어떤 일이 일어나는지 알 수 없어 깊이 있는 개발이 어려운 것이 맞다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 나는 국비학원에서 스프링으로는 게시판만 만들고 바로 스프링부트를 배웠었는데, 스프링부트가 더 쉽고 익숙하게 느껴져서.. 스프링부트를 먼저 공부하면 쉽지 않을까 생각했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그건 내 착각이었다.. 스프링에 대한 이해가 없으니 스프링부트 개념이 여전히 이해가 안 되더라. 그러니 스프링 공부를 제대로 하는 게 맞을 것 같다...&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 프리랜서로서 4월 말부터 갑작스레 많아진 출장 일감&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 4월 27일부터, 2주째 평일 주 4일간 지방 출장을 다녀왔다.. 하루에 다른 지역 두 곳을 방문한 날도 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 내용은 극비라 말할 수 없지만, 프리랜서닷컴(Freelancer.com)에서 생긴 단골 클라이언트 분들이 &lt;b&gt;기업/현장방문 및 사진촬영&lt;/b&gt; 일감을 계속 줬다. 내일도 모레도 이미 출장 일정이 잡혀있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 프리랜서닷컴에서 &lt;b&gt;데이터 라벨링&lt;/b&gt;(시간당 9달러 정도) 단골 클라이언트도 생겼는데, 4월 말부터 갑자기 업무가 많아져서, 주말에는 조금만 쉬고 여기 매달려야 했다...&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;Fiverr(마찬가지로 글로벌 프리랜서 플랫폼이다) 진출도 시도하고 있었는데 거기서도 첫 일감&lt;/b&gt;이 들어와서.. 대중교통을 타면서도 휴대폰으로 계속 업무를 해야 했다... &lt;s&gt;사실 프리랜서의 대체어는 풀일랜서가 아닐까..?&amp;nbsp;&lt;/s&gt;&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;034&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/friends1/large/034.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/friends1/large/034.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 거의 3주째 여기저기 출장을 뛰고 온라인 작업도 엄청 해서 5월 수입은 최고 수치 갱신을 기대해봐도 좋을 정도긴 한데.. 문제는 내가 저체중이 되는 바람에 체력이 더 안 좋아져서... 매일 10,000보 씩 걸어서 그런지 대중교통에서도 집에서도 계속 잠을 자고 있는데도 피로가 안 풀린다... 그리고 개발공부도 취미생활도 근력운동도 할 시간이 없다... 어흐흑...&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;그치만 일들은 어떻게든 잘해내고 있어서 뿌듯하긴 하다.. 이상하게 일을 처리할때마다 새로운 일이 생기긴 하는데.. 아직 1년도 안된 프리랜서라 다음 달에는 수입이 어떻게 될지 모르기 때문에.. 많이 벌 수 있을 때 많이 벌어두긴 해야 한다...&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 data-ke-size=&quot;size26&quot;&gt;3. 앞으로의 계획&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음주까지는 출장+데이터 라벨링 일이 엄청나게 많긴 한데...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일이 없어져서 시간이 많이 남으면... 프리랜서닷컴과 Fiverr 외에도 데이터 태깅 위주의 프리랜서 플랫폼 확장도 계속 할 거고, 웹 백엔드 개발공부도 다시하면서 블로그에 포기하지 않고 글도 작성할 것이다...&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제너럴리스트를 지향하는 프리랜서로서 내가 가능한 일감들은 전부 지원해보고, 도전 가능한 새로운 분야의 일감에 입찰하는 것도 계속 해볼 생각이다! 경쟁률이 정말 심하긴 해도 개발쪽 일감도 종종 수주하게 되면 좋겠다.. 수주를 못하더라도 꾸준히 공부를 해서 내가 원하는 웹서비스를 개발하고 말 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;niniz&quot; data-emoticon-name=&quot;030&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/niniz/large/030.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/niniz/large/030.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>일상 이야기</category>
      <category>fiverr</category>
      <category>freelancer.com</category>
      <category>개발공부</category>
      <category>꿈을향한도전</category>
      <category>백엔드개발공부</category>
      <category>웹개발공부</category>
      <category>제너럴리스트</category>
      <category>풀일랜서</category>
      <category>프리랜서</category>
      <category>프리랜서닷컴</category>
      <author>개발학생</author>
      <guid isPermaLink="true">https://keep-programming-study.tistory.com/199</guid>
      <comments>https://keep-programming-study.tistory.com/199#entry199comment</comments>
      <pubDate>Sun, 10 May 2026 17:50:48 +0900</pubDate>
    </item>
    <item>
      <title>스프링부트 핵심 가이드(장정우 지음) - 스프링 시큐리티(Spring Security)와 JWT 3편 [책의 마지막 부분]</title>
      <link>https://keep-programming-study.tistory.com/198</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*책 내용과 다르게, 다음과 같은 환경에서 프로젝트 생성&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Windows11(윈도우 11) 환경&lt;/li&gt;
&lt;li&gt;자바 JDK 17 버전 설치&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://yungenie.tistory.com/11&quot;&gt;https://yungenie.tistory.com/11&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1776328701771&quot; style=&quot;color: #333333; text-align: start;&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;[Java] 차근차근 Java 설치하기 (JDK17, Window 11)&quot; data-og-description=&quot;자바 개발 도구 설치 방법에 대해서 알아보겠습니다. Java17은 LTS(Long Term Support : 장기 지원) 릴리즈로 1년 후까지 기술 지원 및 버그를 개선한 서비스를 제공받을 수 있습니다. 업데이트 버전을 꾸&quot; data-og-host=&quot;yungenie.tistory.com&quot; data-og-source-url=&quot;https://yungenie.tistory.com/11&quot; data-og-url=&quot;https://yungenie.tistory.com/11&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fsCfm/hyZjxqU4L2/XdX4MUTaSK5BaU7JmJjh90/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/nM72i/hyZjwZQPen/CQONWbqc5xGfLYajkBk8o0/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/JRE6h/hyZjgihxEK/k0VWPB43AkNJ8rLvVcy1M0/img.png?width=2762&amp;amp;height=2094&amp;amp;face=0_0_2762_2094&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://yungenie.tistory.com/11&quot; data-source-url=&quot;https://yungenie.tistory.com/11&quot;&gt;
&lt;div style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fsCfm/hyZjxqU4L2/XdX4MUTaSK5BaU7JmJjh90/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/nM72i/hyZjwZQPen/CQONWbqc5xGfLYajkBk8o0/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/JRE6h/hyZjgihxEK/k0VWPB43AkNJ8rLvVcy1M0/img.png?width=2762&amp;amp;height=2094&amp;amp;face=0_0_2762_2094');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;[Java] 차근차근 Java 설치하기 (JDK17, Window 11)&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;자바 개발 도구 설치 방법에 대해서 알아보겠습니다. Java17은 LTS(Long Term Support : 장기 지원) 릴리즈로 1년 후까지 기술 지원 및 버그를 개선한 서비스를 제공받을 수 있습니다. 업데이트 버전을 꾸&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;yungenie.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스프링 부트 4.31.0 사용 - STS(Spring Tool Suite) 설치(Spring Tools for Eclipse -&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://spring.io/tools&quot;&gt;https://spring.io/tools&lt;/a&gt;)&lt;br /&gt;=&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://priming.tistory.com/147&quot;&gt;https://priming.tistory.com/147&lt;/a&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;참고&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1776328701773&quot; style=&quot;color: #333333; text-align: start;&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;[Windows] Spring Tool Suite 4(STS 4) 다운로드 및 설치&quot; data-og-description=&quot;STS란?Spring Tool Suite(STS)는 스프링 프로젝트를 생성하고, 개발할 수 있게 해주는 도구입니다.&amp;nbsp;STS 설치 과정에 대해 설명드리겠습니다.&amp;nbsp;설치 파일 다운로드STS 공식 사이트에서 설치 파일을 다운로&quot; data-og-host=&quot;priming.tistory.com&quot; data-og-source-url=&quot;https://priming.tistory.com/147&quot; data-og-url=&quot;https://priming.tistory.com/147&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bylxoe/hyZjqL6ykw/At1UjkxTL5MxNIN6mN8Dhk/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/c4WuCh/hyZjyDk2Tg/jLlvw1t5ipklCwRzmvXHkK/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/vUGiq/hyZjaoN1DM/bbtYqu3fqKATJY2ztMT1a1/img.png?width=938&amp;amp;height=621&amp;amp;face=0_0_938_621&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://priming.tistory.com/147&quot; data-source-url=&quot;https://priming.tistory.com/147&quot;&gt;
&lt;div style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bylxoe/hyZjqL6ykw/At1UjkxTL5MxNIN6mN8Dhk/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/c4WuCh/hyZjyDk2Tg/jLlvw1t5ipklCwRzmvXHkK/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/vUGiq/hyZjaoN1DM/bbtYqu3fqKATJY2ztMT1a1/img.png?width=938&amp;amp;height=621&amp;amp;face=0_0_938_621');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;[Windows] Spring Tool Suite 4(STS 4) 다운로드 및 설치&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;STS란?Spring Tool Suite(STS)는 스프링 프로젝트를 생성하고, 개발할 수 있게 해주는 도구입니다.&amp;nbsp;STS 설치 과정에 대해 설명드리겠습니다.&amp;nbsp;설치 파일 다운로드STS 공식 사이트에서 설치 파일을 다운로&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;priming.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2ne97/btsPgUiDCuF/eVBIrd1OJpcnARSiekch31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2ne97/btsPgUiDCuF/eVBIrd1OJpcnARSiekch31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2ne97/btsPgUiDCuF/eVBIrd1OJpcnARSiekch31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2ne97%2FbtsPgUiDCuF%2FeVBIrd1OJpcnARSiekch31%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;1051&quot; height=&quot;634&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;634&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;MySQL&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;Community Server&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;8.0.42 설치&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://dev.mysql.com/downloads/mysql/&quot;&gt;https://dev.mysql.com/downloads/mysql/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1776328701774&quot; style=&quot;color: #333333; text-align: start;&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;MySQL :: Download MySQL Community Server&quot; data-og-description=&quot;Select Version: 9.3.0 Innovation 8.4.5 LTS 8.0.42 Select Operating System: Select Operating System&amp;hellip; Microsoft Windows Ubuntu Linux Debian Linux SUSE Linux Enterprise Server Red Hat Enterprise Linux / Oracle Linux Fedora Linux - Generic Oracle Solaris mac&quot; data-og-host=&quot;dev.mysql.com&quot; data-og-source-url=&quot;https://dev.mysql.com/downloads/mysql/&quot; data-og-url=&quot;https://dev.mysql.com/downloads/mysql/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/QP20c/hyZjCyYfQt/fudyPJf3QzC310TJECw8G1/img.jpg?width=841&amp;amp;height=244&amp;amp;face=0_0_841_244&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://dev.mysql.com/downloads/mysql/&quot; data-source-url=&quot;https://dev.mysql.com/downloads/mysql/&quot;&gt;
&lt;div style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/QP20c/hyZjCyYfQt/fudyPJf3QzC310TJECw8G1/img.jpg?width=841&amp;amp;height=244&amp;amp;face=0_0_841_244');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;MySQL :: Download MySQL Community Server&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Select Version: 9.3.0 Innovation 8.4.5 LTS 8.0.42 Select Operating System: Select Operating System&amp;hellip; Microsoft Windows Ubuntu Linux Debian Linux SUSE Linux Enterprise Server Red Hat Enterprise Linux / Oracle Linux Fedora Linux - Generic Oracle Solaris mac&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;dev.mysql.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;811&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btOcwO/btsPrnefLO2/pUhBPkOVmzbMTaKK1r5sG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btOcwO/btsPrnefLO2/pUhBPkOVmzbMTaKK1r5sG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btOcwO/btsPrnefLO2/pUhBPkOVmzbMTaKK1r5sG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtOcwO%2FbtsPrnefLO2%2FpUhBPkOVmzbMTaKK1r5sG0%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;1272&quot; height=&quot;811&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;811&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;Gradle&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;**STS에서 Gradle 프로젝트 생성한 과정&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K3amN/btsPiv4lwJt/uJutkklzbXbReULQxgZ8y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K3amN/btsPiv4lwJt/uJutkklzbXbReULQxgZ8y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K3amN/btsPiv4lwJt/uJutkklzbXbReULQxgZ8y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK3amN%2FbtsPiv4lwJt%2FuJutkklzbXbReULQxgZ8y1%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;914&quot; height=&quot;690&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;690&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;582&quot; data-origin-height=&quot;774&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m9kKL/btsPhOpIZXk/EXjrrKX55fmvxR1bktggSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m9kKL/btsPhOpIZXk/EXjrrKX55fmvxR1bktggSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m9kKL/btsPhOpIZXk/EXjrrKX55fmvxR1bktggSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm9kKL%2FbtsPhOpIZXk%2FEXjrrKX55fmvxR1bktggSK%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;582&quot; height=&quot;774&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;774&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;657&quot; data-origin-height=&quot;782&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kH6Gt/btsPjkt76G1/oLBQL2FY5kFBr6K3pyVy0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kH6Gt/btsPjkt76G1/oLBQL2FY5kFBr6K3pyVy0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kH6Gt/btsPjkt76G1/oLBQL2FY5kFBr6K3pyVy0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkH6Gt%2FbtsPjkt76G1%2FoLBQL2FY5kFBr6K3pyVy0K%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;657&quot; height=&quot;782&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;782&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;*** 함께 보면 좋은 글&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/164&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.07.10 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링 부트 핵심 가이드(장정우 지음) - 스프링 부트 개요&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1776328723952&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;스프링 부트 핵심 가이드(장정우 지음) - 스프링 부트 개요&quot; data-og-description=&quot;1. 스프링 프레임워크자바(Java) 기반 애플리케이션 프레임워크로, 엔터프라이즈급(기업 환경 대상 개발) 애플리케이션을 위한 다양한 기능 제공-&amp;gt; 오픈소스 경량급 애플리케이션 프레임워크로 &quot; data-og-host=&quot;keep-programming-study.tistory.com&quot; data-og-source-url=&quot;https://keep-programming-study.tistory.com/164&quot; data-og-url=&quot;https://keep-programming-study.tistory.com/164&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c71U3Q/dJMb87NX48W/5ybsR5oRKoSM2qvVFoLAH1/img.png?width=800&amp;amp;height=344&amp;amp;face=0_0_800_344,https://scrap.kakaocdn.net/dn/8tHJF/dJMb8PGxJGs/VJma2CrKKovrkwBVQVoHC0/img.png?width=800&amp;amp;height=344&amp;amp;face=0_0_800_344,https://scrap.kakaocdn.net/dn/ch6lej/dJMb8RRTt4z/kzAEDmqkfmqe9S4iZxdZL1/img.png?width=1109&amp;amp;height=477&amp;amp;face=0_0_1109_477&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/164&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://keep-programming-study.tistory.com/164&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c71U3Q/dJMb87NX48W/5ybsR5oRKoSM2qvVFoLAH1/img.png?width=800&amp;amp;height=344&amp;amp;face=0_0_800_344,https://scrap.kakaocdn.net/dn/8tHJF/dJMb8PGxJGs/VJma2CrKKovrkwBVQVoHC0/img.png?width=800&amp;amp;height=344&amp;amp;face=0_0_800_344,https://scrap.kakaocdn.net/dn/ch6lej/dJMb8RRTt4z/kzAEDmqkfmqe9S4iZxdZL1/img.png?width=1109&amp;amp;height=477&amp;amp;face=0_0_1109_477');&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;1. 스프링 프레임워크자바(Java) 기반 애플리케이션 프레임워크로, 엔터프라이즈급(기업 환경 대상 개발) 애플리케이션을 위한 다양한 기능 제공-&amp;gt; 오픈소스 경량급 애플리케이션 프레임워크로&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;keep-programming-study.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://keep-programming-study.tistory.com/165&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.07.11 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링 부트 핵심 가이드(장정우 지음) - 개발에 앞서 알면 좋은 기초 지식&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1776328732164&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;스프링 부트 핵심 가이드(장정우 지음) - 개발에 앞서 알면 좋은 기초 지식&quot; data-og-description=&quot;1. 서버 간 통신마이크로서비스 아키텍처에서 한 서버가 다른 서버에 통신을 요청하는 것을 의미-&amp;gt; 한 대는 서버/다른 한 대는 클라이언트가 됨 가장 많이 사용되는 방식은 HTTP/HTTPS 방식(TCP/IP, SOA&quot; data-og-host=&quot;keep-programming-study.tistory.com&quot; data-og-source-url=&quot;https://keep-programming-study.tistory.com/165&quot; data-og-url=&quot;https://keep-programming-study.tistory.com/165&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/lh6Hd/dJMb9jgyTd4/g748nkMEbKnzuzr8jMXuUK/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/bQqqyf/dJMb8SpJzuN/R0Jp5gK8Ban6DQ4yoe90Y1/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/yzCmX/dJMb8UHQK0L/Vkq0YWMN8ghoMq7SZ9WdCk/img.png?width=1239&amp;amp;height=564&amp;amp;face=0_0_1239_564&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/165&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://keep-programming-study.tistory.com/165&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/lh6Hd/dJMb9jgyTd4/g748nkMEbKnzuzr8jMXuUK/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/bQqqyf/dJMb8SpJzuN/R0Jp5gK8Ban6DQ4yoe90Y1/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/yzCmX/dJMb8UHQK0L/Vkq0YWMN8ghoMq7SZ9WdCk/img.png?width=1239&amp;amp;height=564&amp;amp;face=0_0_1239_564');&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;1. 서버 간 통신마이크로서비스 아키텍처에서 한 서버가 다른 서버에 통신을 요청하는 것을 의미-&amp;gt; 한 대는 서버/다른 한 대는 클라이언트가 됨 가장 많이 사용되는 방식은 HTTP/HTTPS 방식(TCP/IP, SOA&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;keep-programming-study.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://keep-programming-study.tistory.com/196&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2026.04.10 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링부트 핵심 가이드(장정우 지음) - 스프링 시큐리티(Spring Security)와 JWT 1편&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1776328768391&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;스프링부트 핵심 가이드(장정우 지음) - 스프링 시큐리티(Spring Security)와 JWT 1편&quot; data-og-description=&quot;*책 내용과 다르게, 다음과 같은 환경에서 프로젝트 생성 Windows11(윈도우 11) 환경자바 JDK 17 버전 설치 https://yungenie.tistory.com/11 [Java] 차근차근 Java 설치하기 (JDK17, Window 11)자바 개발 도구 설치 방법&quot; data-og-host=&quot;keep-programming-study.tistory.com&quot; data-og-source-url=&quot;https://keep-programming-study.tistory.com/196&quot; data-og-url=&quot;https://keep-programming-study.tistory.com/196&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/didVtx/dJMb8VNw2oY/GJt7KfeD3Oe1qyKCOuwhKk/img.png?width=792&amp;amp;height=580&amp;amp;face=0_0_792_580,https://scrap.kakaocdn.net/dn/bYyeHh/dJMb8SpJzvn/Nubk2YuRAmz0wyyr0i6d9k/img.png?width=792&amp;amp;height=580&amp;amp;face=0_0_792_580,https://scrap.kakaocdn.net/dn/c5Aa6V/dJMb8Z3sUdM/ZdNC1hTrMPRnpKY44AQt31/img.png?width=1272&amp;amp;height=811&amp;amp;face=0_0_1272_811&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/196&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://keep-programming-study.tistory.com/196&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/didVtx/dJMb8VNw2oY/GJt7KfeD3Oe1qyKCOuwhKk/img.png?width=792&amp;amp;height=580&amp;amp;face=0_0_792_580,https://scrap.kakaocdn.net/dn/bYyeHh/dJMb8SpJzvn/Nubk2YuRAmz0wyyr0i6d9k/img.png?width=792&amp;amp;height=580&amp;amp;face=0_0_792_580,https://scrap.kakaocdn.net/dn/c5Aa6V/dJMb8Z3sUdM/ZdNC1hTrMPRnpKY44AQt31/img.png?width=1272&amp;amp;height=811&amp;amp;face=0_0_1272_811');&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;스프링부트 핵심 가이드(장정우 지음) - 스프링 시큐리티(Spring Security)와 JWT 1편&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;*책 내용과 다르게, 다음과 같은 환경에서 프로젝트 생성 Windows11(윈도우 11) 환경자바 JDK 17 버전 설치 https://yungenie.tistory.com/11 [Java] 차근차근 Java 설치하기 (JDK17, Window 11)자바 개발 도구 설치 방법&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;keep-programming-study.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://keep-programming-study.tistory.com/197&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2026.04.16 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링부트 핵심 가이드(장정우 지음) - 스프링 시큐리티(Spring Security)와 JWT 2편&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1776328772996&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;스프링부트 핵심 가이드(장정우 지음) - 스프링 시큐리티(Spring Security)와 JWT 2편&quot; data-og-description=&quot;*** 함께 보면 좋은 글2026.04.10 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링부트 핵심 가이드(장정우 지음) - 스프링 시큐리티(Spring Security)와 JWT 1편 스프링부트 핵심 &quot; data-og-host=&quot;keep-programming-study.tistory.com&quot; data-og-source-url=&quot;https://keep-programming-study.tistory.com/197&quot; data-og-url=&quot;https://keep-programming-study.tistory.com/197&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bXZCbg/dJMb88F6wXC/A5eNkZe84cJQwAVYfKCxh0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/rjDuE/dJMb87NX49k/kfsJ9n99RBJR5HthaEhtc1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/b0SO6T/dJMb9iIIGtg/xHH5Pc1T6DUcSG60rhJ9Yk/img.png?width=242&amp;amp;height=262&amp;amp;face=0_0_242_262&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/197&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://keep-programming-study.tistory.com/197&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bXZCbg/dJMb88F6wXC/A5eNkZe84cJQwAVYfKCxh0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/rjDuE/dJMb87NX49k/kfsJ9n99RBJR5HthaEhtc1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/b0SO6T/dJMb9iIIGtg/xHH5Pc1T6DUcSG60rhJ9Yk/img.png?width=242&amp;amp;height=262&amp;amp;face=0_0_242_262');&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;스프링부트 핵심 가이드(장정우 지음) - 스프링 시큐리티(Spring Security)와 JWT 2편&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;*** 함께 보면 좋은 글2026.04.10 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링부트 핵심 가이드(장정우 지음) - 스프링 시큐리티(Spring Security)와 JWT 1편 스프링부트 핵심&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;keep-programming-study.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8) 스프링 시큐리티 테스트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'스프링 시큐리티(Spring Security)와 JWT' 1편과 2편에서 작성한 코드들을 바탕으로,&lt;br /&gt;클라이언트 입장에서 스프링 시큐리티가 동작하는 상황에서 테스트 수행&lt;/li&gt;
&lt;li&gt;Swagger 활용하여 테스트&lt;br /&gt;: WebSecurity를 사용하는 configure() 메서드에서 인증에 대한 예외 처리를 했으므로 정상 접속 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) 애플리케이션 가동 시(서버 시작 시), 스프링 시큐리티 관련 로그 살펴보기&amp;nbsp;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JwtTokenProvider 클래스는 @Component로 등록되어 있고, @PostConstruct로 init() 메서드가 정의되어 있음&lt;/li&gt;
&lt;li&gt;init() 메서드에서는 application.properties 파일에 정의된 secretKey의 값을 가져와 인코딩&lt;br /&gt;-&amp;gt; 기본값인 &quot;secretKey&quot;를 그대로 사용하면 보안에 취약하므로, application.properties 파일에 꼭 다음과 같이 '&lt;a href=&quot;https://randomkeygen.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://randomkeygen.com/&lt;/a&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;color: #ee2323;&quot;&gt;이때 application.properties의 변수명을 꼭 JwtTokenProvider와 대조해보자.. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;본인은&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;@Value(&quot;${&lt;/span&gt;&lt;/span&gt;springboot.jwt.secret&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;}&quot;)&lt;/span&gt;를 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;@Value(&quot;${springbot.jwt.secret}&quot;)&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;로 해서 오류가 났다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&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;1511&quot; data-origin-height=&quot;912&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJCdTz/dJMcah5bDvj/gvJQSXgpxCYleGuj8zAjek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJCdTz/dJMcah5bDvj/gvJQSXgpxCYleGuj8zAjek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJCdTz/dJMcah5bDvj/gvJQSXgpxCYleGuj8zAjek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJCdTz%2FdJMcah5bDvj%2FgvJQSXgpxCYleGuj8zAjek%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;1511&quot; height=&quot;912&quot; data-origin-width=&quot;1511&quot; data-origin-height=&quot;912&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;아래는 JwtTokenProvider 관련 로그이다(&lt;span style=&quot;color: #ee2323;&quot;&gt;실제 운영 환경이라면 비밀번호(security password)를 절대로 올리면 안된다!!&lt;/span&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1776411861552&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;2026-04-17 16:40:02.870 [main] INFO  c.e.d.c.security.JwtTokenProvider - [init] JwtTokenProvider 내 secretKey 초기화 시작
2026-04-17 16:40:02.881 [main] INFO  c.e.d.c.security.JwtTokenProvider - [init] JwtTokenProvider 내 secretKey 초기화 완료
2026-04-17 16:40:03.725 [main] WARN  o.s.b.a.o.j.JpaBaseConfiguration$JpaWebConfiguration - spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2026-04-17 16:40:04.978 [main] INFO  o.s.b.a.e.web.EndpointLinksResolver - Exposing 14 endpoints beneath base path '/actuator'
2026-04-17 16:40:05.106 [main] WARN  o.s.b.a.s.s.UserDetailsServiceAutoConfiguration - 

Using generated security password: 2376ca77-a5da-4db4-8cf9-f71ca896045f

This generated password is for development use only. Your security configuration must be updated before running your application in production.&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;DefaultSecurityFilterChain 관련 로그:&lt;br /&gt;&lt;b&gt;Spring Boot 3.x + Spring Security 6.x부터는 &lt;/b&gt;로깅 정책이 바뀌어 &lt;b&gt;디버그 레벨에서만 확인 가능&lt;br /&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 프로젝트에서는 application.properties에 logging.level.org.springframework.security&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;DEBUG를 추가하면 출력됨&lt;br /&gt;-&amp;gt; &lt;/span&gt;&lt;/span&gt;Spring Security 로그를 DEBUG 레벨로 출력하는 것&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) Hibernate 오류 해결&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;H2 데이터베이스에서 USER는 &lt;b&gt;예약어(reserved keyword)&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt;-&amp;gt; 따라서 references user라고 쓰면 SQL 파서가 식별자(identifier)가 아닌 예약어로 인식해서 문법 오류가 발생&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt; Hibernate가 user_roles 테이블에 외래 키를 만들면서 user 테이블을 참조하려고 했는데, 예약어 충돌 때문에 실패&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;엔티티 클래스에서 @Table(name=&quot;users&quot;)로, 테이블 이름을 예약어가 아닌 이름으로 바꾸기&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1777019018949&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
@Table(name = &quot;users&quot;) // user 대신 users
public class User implements UserDetails { 
  ... 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(3) JwtAuthenticationFilter에서, 특정 경로는 JWT 검증을 건너뛰도록 조건을 추가&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;http://localhost:8080/swagger-ui/index.html에 접속했을 때, 아래와 같은 &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;1098&quot; data-origin-height=&quot;391&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTuRch/dJMcabxbBAh/Yq1K9kduwVYoXbSCunZ5UK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTuRch/dJMcabxbBAh/Yq1K9kduwVYoXbSCunZ5UK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTuRch/dJMcabxbBAh/Yq1K9kduwVYoXbSCunZ5UK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTuRch%2FdJMcabxbBAh%2FYq1K9kduwVYoXbSCunZ5UK%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;1098&quot; height=&quot;391&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;391&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1777022698300&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.config.security;

import java.io.IOException;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

/**
 * JWT 인증을 처리하는 커스텀 필터 클래스
 * 
 * OncePerRequestFilter를 상속하여 모든 요청마다 한 번만 실행됨.
 *
 * 주요 역할:
 * 1. HTTP 요청에서 JWT 토큰 추출 (resolveToken)
 * 2. 토큰 유효성 검증 (validateToken)
 * 3. 토큰이 유효하다면 Authentication 객체 생성 후 SecurityContext에 저장
 *    - SecurityContextHolder는 현재 요청의 인증 상태를 관리하는 Spring Security의 핵심 컨텍스트
 * 4. 필터 체인을 계속 진행하여 이후 요청 처리 로직으로 전달
 * ++ Swagger UI, OpenAPI 문서 등 공개 엔드포인트는 필터를 건너뛰도록 예외 처리 추가
 *
 * 즉, 이 필터는 클라이언트 요청이 들어올 때마다 JWT 기반 인증을 수행하고,
 * 인증된 사용자 정보를 Spring Security 컨텍스트에 주입하는 역할을 담당
 */
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    private final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
    private final JwtTokenProvider jwtTokenProvider;
    
    public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }
    
    // JWT 검증을 건너뛸 경로들
    private static final List&amp;lt;String&amp;gt; EXCLUDE_URLS = List.of(
        &quot;/swagger-ui&quot;,
        &quot;/swagger-ui.html&quot;,
        &quot;/v3/api-docs&quot;,
        &quot;/api/public&quot;
    );
    
    /**
     * 실제 필터 로직 구현 메서드
     *
     * 동작 흐름:
     * 1. 요청(servletRequest)에서 JWT 토큰 추출
     * 2. 토큰 값 로그 출력
     * 3. 토큰 유효성 검증 시작
     * 4. 토큰이 존재하고 유효하다면:
     *    - JwtTokenProvider.getAuthentication(token) 호출 &amp;rarr; Authentication 객체 생성
     *    - SecurityContextHolder.getContext().setAuthentication(authentication) &amp;rarr; 인증 정보 저장
     *    - 로그 출력: 유효성 체크 완료
     * 5. 필터 체인 계속 진행 (filterChain.doFilter)
     *
     * @param servletRequest  클라이언트 요청 객체
     * @param servletResponse 클라이언트 응답 객체
     * @param filterChain     필터 체인 객체
     */
    @Override
    protected void doFilterInternal(HttpServletRequest servletRequest,
                                    HttpServletResponse servletResponse,
                                    FilterChain filterChain) throws ServletException, IOException {
    	
    	// Swagger, 공개 API는 JWT 검증 건너뛰기
    	String path = servletRequest.getRequestURI();
    	
        if (EXCLUDE_URLS.stream().anyMatch(path::startsWith)) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        
        // 요청에서 JWT 토큰 추출
        String token = jwtTokenProvider.resolveToken(servletRequest);
        LOGGER.info(&quot;[doFilterInternal] token 값 추출 완료. token: {}&quot;, token);
        
        // 토큰 유효성 검증
        LOGGER.info(&quot;[doFilterInternal] token 값 유효성 체크 시작&quot;);
        if (token != null &amp;amp;&amp;amp; jwtTokenProvider.validateToken(token)) {
            // 토큰이 유효하면 Authentication 객체 생성 후 SecurityContext에 저장
            Authentication authentication = jwtTokenProvider.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            LOGGER.info(&quot;[doFilterInternal] token 값 유효성 체크 완료&quot;);
        }
        
        // 필터 체인 계속 진행
        filterChain.doFilter(servletRequest, servletResponse);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(4) build.gradle에서 해당 코드를 지운 후, 파일 우클릭 -&amp;gt; Gradle -&amp;gt; Refresh Gradle Project 클릭&amp;nbsp;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;springdoc-openapi-starter-webmvc-ui:2.5.0은 내부적으로 swagger-annotations 최신 버전을 기대하는데,&lt;br /&gt;&lt;/span&gt;&lt;span&gt;swagger-annotations:2.2.15가 충돌을 일으켜서 springdoc이 기대하는 메서드가 없는 구버전 클래스가 로딩되어 &lt;b&gt;java.lang.NoSuchMethodError: 'java.lang.Class[] io.swagger.v3.oas.annotations.Parameter.validationGroups()'&lt;/b&gt; 오류 발생&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1777022878730&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation 'io.swagger.core.v3:swagger-annotations:2.2.20'  // Swagger(OpenAPI)의 어노테이션들을 직접 사용할 수 있게 해주는 라이브러리&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(4) 정상 동작 시나리오 기반 테스트 수행 - http://localhost:8080/swagger-ui/index.html에 접속하여 진행&amp;nbsp;&lt;/h4&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; 상품 컨트롤러의 상품 등록 API 호출 -&amp;gt; API 호출 시 로그인 과정에서 받은 토큰을 헤더에 추가해서 전달 -&amp;gt; 상품 등록 완료&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 회원가입&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;http://localhost:8080/swagger-ui/index.html#/sign-controller/signUp 부분에서 'Try it out' 클릭 후,&lt;br /&gt;각 칸에 다음과 같이 입력하고 'Execute' 클릭&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;1837&quot; data-origin-height=&quot;776&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sMdJy/dJMcajvdTuA/f6OrqTRUFycvkwhfA7bFj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sMdJy/dJMcajvdTuA/f6OrqTRUFycvkwhfA7bFj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sMdJy/dJMcajvdTuA/f6OrqTRUFycvkwhfA7bFj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsMdJy%2FdJMcajvdTuA%2Ff6OrqTRUFycvkwhfA7bFj1%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;1837&quot; height=&quot;776&quot; data-origin-width=&quot;1837&quot; data-origin-height=&quot;776&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;1822&quot; data-origin-height=&quot;602&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csyb9g/dJMcaiJPt3Q/pkBYeJ1bxPhNDfK4zuokBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csyb9g/dJMcaiJPt3Q/pkBYeJ1bxPhNDfK4zuokBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csyb9g/dJMcaiJPt3Q/pkBYeJ1bxPhNDfK4zuokBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcsyb9g%2FdJMcaiJPt3Q%2FpkBYeJ1bxPhNDfK4zuokBk%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;1822&quot; height=&quot;602&quot; data-origin-width=&quot;1822&quot; data-origin-height=&quot;602&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. 로그인&amp;nbsp;&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;http://localhost:8080/swagger-ui/index.html#/sign-controller/signIn에서 'Try it out' 클릭 후,&lt;br /&gt;각 칸에 다음과 같이 입력하고 'Execute' 클릭&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;1832&quot; data-origin-height=&quot;658&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBp4AU/dJMcahjVRPq/EQXkH2OREGAJ88KDD141zK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBp4AU/dJMcahjVRPq/EQXkH2OREGAJ88KDD141zK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBp4AU/dJMcahjVRPq/EQXkH2OREGAJ88KDD141zK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBp4AU%2FdJMcahjVRPq%2FEQXkH2OREGAJ88KDD141zK%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;1832&quot; height=&quot;658&quot; data-origin-width=&quot;1832&quot; data-origin-height=&quot;658&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;정상적인 로그인 성공 화면 + 발급된 jwt 토큰 값 출력&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;1798&quot; data-origin-height=&quot;882&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cehQOI/dJMcaaLQSKi/xfLtxRsbusQjfEeL8spJl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cehQOI/dJMcaaLQSKi/xfLtxRsbusQjfEeL8spJl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cehQOI/dJMcaaLQSKi/xfLtxRsbusQjfEeL8spJl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcehQOI%2FdJMcaaLQSKi%2FxfLtxRsbusQjfEeL8spJl0%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;1798&quot; height=&quot;882&quot; data-origin-width=&quot;1798&quot; data-origin-height=&quot;882&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;SwaggerConfig 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1777110817794&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.config;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.security.SecurityScheme;

@OpenAPIDefinition(info = @Info(title = &quot;Demo API&quot;, version = &quot;v1&quot;))
@SecurityScheme(
    name = &quot;X-AUTH-TOKEN&quot;,              // Swagger UI에서 표시될 이름
    type = SecuritySchemeType.APIKEY,   // API Key 방식
    in = io.swagger.v3.oas.annotations.enums.SecuritySchemeIn.HEADER, // 헤더에서 받음
    paramName = &quot;X-AUTH-TOKEN&quot;          // 실제 헤더 이름
)
public class SwaggerConfig {
}&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;이전에 만들었던 ProductController를 살짝 수정하고, Product와 관련 DTO들은 그대로 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1777110023894&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ProductController.java
package com.example.demo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import com.example.demo.jpa.data.dto.ChangeProductNameDTO;
import com.example.demo.jpa.data.dto.ProductDTO;
import com.example.demo.jpa.data.dto.ProductResponseDTO;
import com.example.demo.service.ProductService;

import io.swagger.v3.oas.annotations.security.SecurityRequirement;

/**
 * 상품 관련 HTTP 요청을 처리하는 REST 컨트롤러
 * 클라이언트로부터 요청을 받아 Service 계층에 전달하고,
 * 처리 결과를 ResponseEntity로 감싸서 응답
 */
@RestController
@RequestMapping(&quot;/product&quot;) // 모든 요청의 기본 경로 설정
public class ProductController {

    private final ProductService productService;

    @Autowired
    public ProductController(ProductService productService) {
        this.productService = productService;
    }

    /**
     * 상품 조회 API
     * GET /product?number=1
     * @param number 조회할 상품의 고유 번호 (쿼리 파라미터)
     * @return 상품 정보를 담은 응답 DTO
     */
    @GetMapping
    public ResponseEntity&amp;lt;ProductResponseDTO&amp;gt; getProduct(@RequestParam(&quot;number&quot;) Long number) {
        ProductResponseDTO productResponseDTO = productService.getProduct(number);
        return ResponseEntity.status(HttpStatus.OK).body(productResponseDTO);
    }

    /**
     * 상품 생성 API
     * POST /product
     * @param productDTO 클라이언트가 전달한 상품 정보 (JSON Body)
     * @return 저장된 상품 정보를 담은 응답 DTO
     */
    @PostMapping
    // jwt 인증 필요
    @SecurityRequirement(name = &quot;X-AUTH-TOKEN&quot;) 
    public ResponseEntity&amp;lt;ProductResponseDTO&amp;gt; createProduct(@RequestBody ProductDTO productDTO) {
        ProductResponseDTO productResponseDTO = productService.saveProduct(productDTO);
        return ResponseEntity.status(HttpStatus.OK).body(productResponseDTO);
    }

    /**
     * 상품 이름 변경 API
     * PUT /product
     * @param changeProductNameDTO 상품 번호와 새로운 이름을 담은 DTO (JSON Body)
     * @return 변경된 상품 정보를 담은 응답 DTO
     * @throws Exception 상품이 존재하지 않거나 변경 실패 시 예외 발생
     */
    @PutMapping
    // jwt 인증 필요
    @SecurityRequirement(name = &quot;X-AUTH-TOKEN&quot;) 
    public ResponseEntity&amp;lt;ProductResponseDTO&amp;gt; changeProductName(
            @RequestBody ChangeProductNameDTO changeProductNameDTO) throws Exception {
        ProductResponseDTO productResponseDTO = productService.changeProductName(
            changeProductNameDTO.getNumber(), changeProductNameDTO.getName());
        return ResponseEntity.status(HttpStatus.OK).body(productResponseDTO);
    }

    /**
     * 상품 삭제 API
     * DELETE /product?number=1
     * @param number 삭제할 상품의 고유 번호 (쿼리 파라미터)
     * @return 삭제 성공 메시지
     * @throws Exception 상품이 존재하지 않거나 삭제 실패 시 예외 발생
     */
    @DeleteMapping
    // jwt 인증 필요
    @SecurityRequirement(name = &quot;X-AUTH-TOKEN&quot;) 
    public ResponseEntity&amp;lt;String&amp;gt; deleteProduct(@RequestParam(&quot;number&quot;) Long number) throws Exception {
        productService.deleteProduct(number);
        return ResponseEntity.status(HttpStatus.OK).body(&quot;정상적으로 삭제되었습니다.&quot;);
    }
}&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;http://localhost:8080/swagger-ui/index.html 상단에서 'Authorize' 클릭&lt;br /&gt;-&amp;gt; 클릭하면 X-AUTH-TOKEN 입력창이 뜨고, &lt;br /&gt;&amp;nbsp; &amp;nbsp; 거기에 '로그인 성공 후 발급받은 token 값'을 입력하면 이후 모든 API 호출에 자동으로 헤더가 붙음&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;1911&quot; data-origin-height=&quot;515&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rHVUK/dJMcaaE6kkh/g2XAoL6Nd1GcKoktfvgelk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rHVUK/dJMcaaE6kkh/g2XAoL6Nd1GcKoktfvgelk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rHVUK/dJMcaaE6kkh/g2XAoL6Nd1GcKoktfvgelk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrHVUK%2FdJMcaaE6kkh%2Fg2XAoL6Nd1GcKoktfvgelk%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;1911&quot; height=&quot;515&quot; data-origin-width=&quot;1911&quot; data-origin-height=&quot;515&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;http://localhost:8080/swagger-ui/index.html#/product에서, &lt;br /&gt;Body에 { &quot;name&quot;:&quot;공책&quot;, &quot;price&quot;:&quot;2000&quot;, &quot;stock&quot;:&quot;3500&quot;}을 넣으면 정상적으로 상품 등록 성공 화면이 출력됨&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;(5) 비정상 동작 시나리오 기반 테스트 수행 시&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;token 값을 변조하여 상품 등록 API를 호출하면, 인증 예외 발생으로 { &quot;msg&quot;: &quot;인증이 실패하였습니다&quot; }가 출력됨&lt;/li&gt;
&lt;li&gt;사용자의 role이 ADMIN이 아닐 경우(USER 권한으로 회원가입 및 로그인 진행), &lt;br /&gt;인가 예외 발생으로 /exception으로 리다이렉트된 후 예외 메시지가 응답으로 돌아옴&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초</category>
      <category>Java</category>
      <category>json web token</category>
      <category>JWT</category>
      <category>spring boot</category>
      <category>Spring Security</category>
      <category>STS4</category>
      <category>스프링 부트</category>
      <category>스프링 시큐리티</category>
      <category>스프링 시큐리티 테스트</category>
      <category>자바</category>
      <author>개발학생</author>
      <guid isPermaLink="true">https://keep-programming-study.tistory.com/198</guid>
      <comments>https://keep-programming-study.tistory.com/198#entry198comment</comments>
      <pubDate>Sat, 25 Apr 2026 18:51:33 +0900</pubDate>
    </item>
    <item>
      <title>스프링부트 핵심 가이드(장정우 지음) - 스프링 시큐리티(Spring Security)와 JWT 2편</title>
      <link>https://keep-programming-study.tistory.com/197</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;*** 함께 보면 좋은 글&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/196&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2026.04.10 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링부트 핵심 가이드(장정우 지음) - 스프링 시큐리티(Spring Security)와 JWT 1편&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1775817652601&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;스프링부트 핵심 가이드(장정우 지음) - 스프링 시큐리티(Spring Security)와 JWT 1편&quot; data-og-description=&quot;*책 내용과 다르게, 다음과 같은 환경에서 프로젝트 생성 Windows11(윈도우 11) 환경자바 JDK 17 버전 설치 https://yungenie.tistory.com/11 [Java] 차근차근 Java 설치하기 (JDK17, Window 11)자바 개발 도구 설치 방법&quot; data-og-host=&quot;keep-programming-study.tistory.com&quot; data-og-source-url=&quot;https://keep-programming-study.tistory.com/196&quot; data-og-url=&quot;https://keep-programming-study.tistory.com/196&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/KJWGN/dJMb84XZNI7/fNPaySdq2AYTDaOHEAaKz0/img.png?width=792&amp;amp;height=580&amp;amp;face=0_0_792_580,https://scrap.kakaocdn.net/dn/38Y9c/dJMb9iaSc58/NZxtbBAG7oa5VXAJI9unH0/img.png?width=792&amp;amp;height=580&amp;amp;face=0_0_792_580,https://scrap.kakaocdn.net/dn/pORIg/dJMb9lk8dga/591KScHgyWu6uN3yQKGS9k/img.png?width=1272&amp;amp;height=811&amp;amp;face=0_0_1272_811&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/196&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://keep-programming-study.tistory.com/196&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/KJWGN/dJMb84XZNI7/fNPaySdq2AYTDaOHEAaKz0/img.png?width=792&amp;amp;height=580&amp;amp;face=0_0_792_580,https://scrap.kakaocdn.net/dn/38Y9c/dJMb9iaSc58/NZxtbBAG7oa5VXAJI9unH0/img.png?width=792&amp;amp;height=580&amp;amp;face=0_0_792_580,https://scrap.kakaocdn.net/dn/pORIg/dJMb9lk8dga/591KScHgyWu6uN3yQKGS9k/img.png?width=1272&amp;amp;height=811&amp;amp;face=0_0_1272_811');&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;스프링부트 핵심 가이드(장정우 지음) - 스프링 시큐리티(Spring Security)와 JWT 1편&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;*책 내용과 다르게, 다음과 같은 환경에서 프로젝트 생성 Windows11(윈도우 11) 환경자바 JDK 17 버전 설치 https://yungenie.tistory.com/11 [Java] 차근차근 Java 설치하기 (JDK17, Window 11)자바 개발 도구 설치 방법&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;keep-programming-study.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 스프링 시큐리티와 JWT 적용&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6) 커스텀 AccessDeniedHandler, AuthenticationEntryPoint 구현&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) CustomAccessDeniedHandler&lt;/h4&gt;
&lt;pre id=&quot;code_1776161409747&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.config.security;

import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

/**
 * Spring Security에서 접근 권한이 없는 요청(AccessDeniedException 발생 시)을 처리하는 핸들러 클래스
 *
 * 주요 역할:
 * 1. 사용자가 인증은 되었지만, 특정 리소스에 접근할 권한이 없을 때 실행됨
 *    - 예: ROLE_USER 권한만 있는 사용자가 ROLE_ADMIN 전용 API에 접근
 * 2. AccessDeniedHandler 인터페이스 구현 &amp;rarr; handle() 메서드 오버라이드
 * 3. 로그 기록: 접근 거부 상황을 로깅
 * 4. response.sendRedirect(&quot;/지정된 경로/&quot;) 호출: 접근 거부 시 해당 경로로 리다이렉트
 *
 * 특징:
 * - @Component로 등록되어 Spring Security 설정에서 자동으로 사용 가능
 * - &quot;인증은 되었지만 권한이 부족한 경우&quot;에만 동작
 */
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
    
	// Logger 인스턴스 생성
	// - SLF4J LoggerFactory를 사용하여 현재 클래스(CustomAccessDeniedHandler)에 대한 로깅 객체를 생성
	// - 접근 거부 상황 발생 시 로그를 남겨서 추적 및 디버깅에 활용 가능
	private final Logger LOGGER = LoggerFactory.getLogger(CustomAccessDeniedHandler.class);

	@Override
	public void handle(
	        HttpServletRequest request, 
	        HttpServletResponse response, 
	        AccessDeniedException exception) throws IOException {
	    
	    // 접근 거부 상황 발생 시 로그 기록
	    // - 사용자가 인증은 되었지만 권한이 부족하여 요청이 차단된 경우 실행됨
	    LOGGER.info(&quot;[handle] 접근이 막혔을 때 경로 리다이렉트&quot;);
	    
	    // 접근 거부 시 지정된 경로로 리다이렉트
	    // - &quot;/sign-api/exception&quot; 경로로 이동시켜 사용자에게 권한 부족 안내 페이지를 제공할 수 있음
	    // - response.sendRedirect()는 클라이언트 브라우저가 해당 URL로 다시 요청하도록 지시하는 방식
	    response.sendRedirect(&quot;/sign-api/exception&quot;);
	}

}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) CustomAuthenticationEntryPoint&lt;/h4&gt;
&lt;pre id=&quot;code_1776161929834&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.config.security;

import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.ObjectMapper;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

/**
 * 인증 실패(401 Unauthorized) 상황을 처리하는 커스텀 EntryPoint 클래스
 *
 * 주요 역할:
 * - Spring Security에서 인증되지 않은 사용자가 보호된 리소스에 접근할 때 실행됨
 * - AuthenticationEntryPoint 인터페이스 구현 &amp;rarr; commence() 메서드 오버라이드
 * - 로그 기록: 인증 실패 상황을 로깅
 * - 클라이언트에게 JSON 형태의 에러 응답 반환
 *
 * 특징:
 * - @Component로 등록되어 Spring Security 설정에서 자동으로 사용 가능
 * - &quot;인증 자체가 안 된 경우&quot;에만 동작
 */
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    
    // Logger 인스턴스 생성
    // - SLF4J LoggerFactory를 사용하여 현재 클래스(CustomAuthenticationEntryPoint)에 대한 로깅 객체를 생성
    // - 접근 거부 상황 발생 시 로그를 남겨서 추적 및 디버깅에 활용 가능
    private final Logger LOGGER = LoggerFactory.getLogger(CustomAuthenticationEntryPoint.class);

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) throws IOException {
        
        // ObjectMapper 생성
        // - Java 객체를 JSON 문자열로 변환하기 위해 사용
        ObjectMapper objectMapper = new ObjectMapper();
        
        // 인증 실패 로그 기록
        // - 인증되지 않은 사용자가 보호된 리소스에 접근하려 할 때 실행됨
        LOGGER.info(&quot;[commence] 인증 실패로 response.sendError 발생&quot;);
        
        // 사용자에게 반환할 에러 응답 객체 생성
        // - EntryPointErrorResponse는 커스텀 응답 DTO로, 메시지를 담아 클라이언트에 전달
        EntryPointErrorResponse entryPointErrorResponse = new EntryPointErrorResponse();
        entryPointErrorResponse.setMsg(&quot;인증에 실패하였습니다.&quot;);
        
        // HTTP 응답 설정
        // - 상태 코드: 401 Unauthorized (인증 실패)
        // - Content-Type: application/json (JSON 응답)
        // - CharacterEncoding: UTF-8 (한글 깨짐 방지)
        
        // response.sendError(response.SC_UNAUTHORIZED)와 같은 형식으로 인증 실패 코드만 전달할수도 있음
        response.setStatus(401);
        response.setContentType(&quot;application/json&quot;);
        response.setCharacterEncoding(&quot;utf-8&quot;);
        
        // 응답 본문에 JSON 문자열 작성
        // - objectMapper.writeValueAsString(entryPointErrorResponse)로 DTO를 JSON으로 변환
        response.getWriter().write(objectMapper.writeValueAsString(entryPointErrorResponse));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(3) EntryPointErrorResponse&lt;/h4&gt;
&lt;pre id=&quot;code_1776162005390&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.config.security;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

/**
 * 인증 실패 시 클라이언트에게 반환할 에러 응답 객체 (DTO)
 *
 * 주요 역할:
 * - CustomAuthenticationEntryPoint에서 사용됨
 * - 인증 실패(401 Unauthorized) 상황에서 JSON 응답으로 내려줄 데이터 구조 정의
 * - 단순히 메시지(msg) 필드 하나만 포함하여, 클라이언트가 쉽게 에러 내용을 확인할 수 있도록 함
 *
 * 특징:
 * - Lombok 어노테이션(@Data, @NoArgsConstructor, @AllArgsConstructor, @ToString)으로
 *   getter/setter, 생성자, toString 메서드를 자동 생성
 * - 직렬화 시 JSON 형태로 변환되어 클라이언트에 전달됨
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class EntryPointErrorResponse {

    /**
     * 에러 메시지를 담는 필드
     * - 인증 실패 시 &quot;인증에 실패하였습니다.&quot; 등의 안내 문구를 저장
     * - ObjectMapper를 통해 JSON 변환 시 {&quot;msg&quot;:&quot;인증에 실패하였습니다.&quot;} 형태로 응답됨
     */
    private String msg;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7) 회원가입과 로그인 구현&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;User 객체 생성을 위한 회원가입 구현&lt;/li&gt;
&lt;li&gt;생성된 User 객체로 인증을 시도하는 로그인 구현&lt;/li&gt;
&lt;li&gt;회원가입과 로그인의 도메인은 Sign으로 통합해서 표현하며, 각각 Sign-up과 Sign-in으로 구분하여 기능 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) SignUpResultDto, SignInResultDto&lt;/h4&gt;
&lt;pre id=&quot;code_1776162474992&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.jpa.data.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

/**
 * 회원가입 결과를 클라이언트에게 전달하기 위한 응답 DTO 클래스
 *
 * 주요 역할:
 * - 회원가입 처리 후 성공/실패 여부, 상태 코드, 메시지를 담아 반환
 * - Controller &amp;rarr; Service &amp;rarr; 클라이언트로 전달되는 데이터 구조 정의
 * - JSON 직렬화 시 {&quot;success&quot;:true, &quot;code&quot;:200, &quot;msg&quot;:&quot;회원가입 성공&quot;} 형태로 응답 가능
 *
 * 특징:
 * - Lombok 어노테이션(@Data, @NoArgsConstructor, @AllArgsConstructor, @ToString) 사용
 *   &amp;rarr; getter/setter, 기본 생성자, 전체 필드 생성자, toString 자동 생성
 * - 불필요한 보일러플레이트 코드 제거로 가독성 향상
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class SignUpResultDto {

    /**
     * 회원가입 성공 여부
     * - true: 회원가입 성공
     * - false: 회원가입 실패
     */
    private boolean success;

    /**
     * 결과 코드
     * - 예: 200 (성공), 400 (잘못된 요청), 500 (서버 오류) 등
     * - 클라이언트가 결과를 상태 코드 기반으로 처리할 수 있도록 제공
     */
    private int code;

    /**
     * 결과 메시지
     * - 회원가입 성공/실패에 대한 설명 문구
     * - 예: &quot;회원가입 성공&quot;, &quot;이미 존재하는 사용자입니다.&quot;
     */
    private String msg;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1776162592968&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.jpa.data.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

/**
 * 로그인 결과를 클라이언트에게 전달하기 위한 응답 DTO 클래스
 *
 * 주요 역할:
 * - 로그인 처리 후 JWT 토큰을 포함한 결과를 반환
 * - 부모 클래스(SignUpResultDto)의 필드(success, code, msg)를 상속받아 기본 응답 정보 제공
 * - 추가적으로 token 필드를 포함하여 로그인 성공 시 발급된 JWT 토큰을 클라이언트에 전달
 *
 * 특징:
 * - Lombok 어노테이션(@Data, @NoArgsConstructor, @AllArgsConstructor, @ToString) 사용
 *   &amp;rarr; getter/setter, 생성자, toString 자동 생성
 * - @Builder를 통해 빌더 패턴으로 객체 생성 가능
 * - 상속 구조 덕분에 회원가입/로그인 응답 DTO가 일관된 형태를 유지
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class SignInResultDto extends SignUpResultDto {
    
    /**
     * 로그인 성공 시 발급되는 JWT 토큰
     * - 클라이언트가 이후 요청 시 인증 헤더에 포함하여 사용
     */
    private String token;
    
    /**
     * 빌더 패턴을 활용한 생성자
     * - 부모 클래스(SignUpResultDto)의 생성자를 호출하여 success, code, msg 초기화
     * - 현재 클래스의 token 필드도 함께 초기화
     *
     * @param success 로그인 성공 여부
     * @param code    결과 코드 (예: 200 성공, 401 인증 실패 등)
     * @param msg     결과 메시지
     * @param token   JWT 토큰
     */
    @Builder
    public SignInResultDto(boolean success, int code, String msg, String token) {
        super(success, code, msg); // 부모 클래스 생성자 호출
        this.token = token;        // 현재 클래스 필드 초기화
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) SignService 인터페이스&lt;/h4&gt;
&lt;pre id=&quot;code_1776162834135&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.service;

import com.example.demo.jpa.data.dto.SignInResultDto;
import com.example.demo.jpa.data.dto.SignUpResultDto;

/**
 * 회원가입 및 로그인 관련 기능을 정의하는 서비스 인터페이스
 *
 * 주요 역할:
 * - 회원가입(signUp)과 로그인(signIn) 기능을 추상화하여 구현 클래스에서 실제 로직을 작성하도록 강제
 * - Controller 계층에서 이 인터페이스를 호출하여 서비스 로직을 실행
 * - DTO(SignUpResultDto, SignInResultDto)를 반환하여 클라이언트에 결과 전달
 *
 * 특징:
 * - 인터페이스로 정의되어 있어 다양한 구현체를 만들 수 있음 (예: DB 기반, 외부 인증 서버 기반 등)
 * - 예외 처리나 세부 로직은 구현 클래스에서 담당
 */
public interface SignService {
    
    /**
     * 회원가입 기능
     *
     * @param id       사용자 ID
     * @param password 사용자 비밀번호
     * @param name     사용자 이름
     * @param role     사용자 권한 (예: ROLE_USER, ROLE_ADMIN)
     * @return SignUpResultDto 회원가입 결과 DTO
     *         - success: 성공 여부
     *         - code: 상태 코드
     *         - msg: 결과 메시지
     */
    SignUpResultDto signUp(String id, String password, String name, String role);
    
    /**
     * 로그인 기능
     *
     * @param id       사용자 ID
     * @param password 사용자 비밀번호
     * @return SignInResultDto 로그인 결과 DTO
     *         - success, code, msg: 기본 응답 정보
     *         - token: 로그인 성공 시 발급된 JWT 토큰
     * @throws RuntimeException 로그인 실패 시 예외 발생
     */
    SignInResultDto signIn(String id, String password) throws RuntimeException;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(3) PasswordEncoderConfiguration 클래스&lt;/h4&gt;
&lt;pre id=&quot;code_1776249671308&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 비밀번호를 안전하게 암호화하고 검증하기 위해 
 Spring Security에서 제공하는 PasswordEncoder를 Bean으로 등록
**/
@Configuration // 설정 클래스임을 명시
public class PasswordEncoderConfiguration {
    
    @Bean // PasswordEncoder를 Bean으로 등록 &amp;rarr; 스프링 컨테이너에서 관리
    public PasswordEncoder passwordEncoder() {
        // DelegatingPasswordEncoder 생성 (기본은 BCrypt)
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(4) CommonResponse&lt;/h4&gt;
&lt;pre id=&quot;code_1776249815259&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.config.security.common;

/**
 * 애플리케이션에서 공통적으로 사용하는 응답 상태 Enum
 *
 * 주요 역할:
 * - API 응답이나 서비스 로직에서 성공/실패 여부를 일관되게 표현하기 위해 사용
 * - 각 상태는 코드(int)와 메시지(String)를 함께 제공
 *   &amp;rarr; 클라이언트가 응답을 처리할 때 코드와 메시지를 함께 활용 가능
 *
 * 특징:
 * - SUCCESS: 성공 상태 (code=0, msg=&quot;Success&quot;)
 * - FAIL: 실패 상태 (code=-1, msg=&quot;Fail&quot;)
 * - Enum으로 정의되어 있어 타입 안정성을 보장하고, 상수 값 관리가 용이함
 */
public enum CommonResponse {
    SUCCESS(0, &quot;Success&quot;), // 성공 응답
    FAIL(-1, &quot;Fail&quot;);      // 실패 응답
    
    // 응답 코드 (성공/실패를 숫자로 표현)
    int code; 
    
    // 응답 메시지 (성공/실패를 문자열로 표현)
    String msg;
    
    /**
     * Enum 생성자
     * - 각 Enum 상수에 대응하는 code와 msg를 초기화
     */
    CommonResponse(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    
    /**
     * 응답 코드 반환
     * @return code (예: 0, -1)
     */
    public int getCode() {
        return code;
    }
    
    /**
     * 응답 메시지 반환
     * @return msg (예: &quot;Success&quot;, &quot;Fail&quot;)
     */
    public String getMsg() {
        return msg;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(5) SignServiceImpl (SignService 인터페이스를 구현)&lt;/h4&gt;
&lt;pre id=&quot;code_1776250148586&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.service;

import java.util.Collections;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import com.example.demo.config.security.JwtTokenProvider;
import com.example.demo.config.security.common.CommonResponse;
import com.example.demo.jpa.data.dto.SignInResultDto;
import com.example.demo.jpa.data.dto.SignUpResultDto;
import com.example.demo.jpa.data.entity.User;
import com.example.demo.jpa.data.repository.UserRepository;

/**
 * 회원가입과 로그인 기능을 실제로 구현한 서비스 클래스
 */
@Service // Spring의 Service 컴포넌트로 등록 &amp;rarr; 비즈니스 로직 담당
public class SignServiceImpl implements SignService {
    
    // Logger 인스턴스 생성 &amp;rarr; 서비스 동작 과정 로깅
    private final Logger LOGGER = LoggerFactory.getLogger(SignServiceImpl.class);
    
    // 의존성 주입 받을 Repository, TokenProvider, PasswordEncoder
    public UserRepository userRepository;
    public JwtTokenProvider jwtTokenProvider;
    public PasswordEncoder passwordEncoder;
    
    // 생성자 주입 &amp;rarr; Spring이 자동으로 Bean을 주입해줌
    @Autowired
    public SignServiceImpl(UserRepository userRepository, JwtTokenProvider jwtTokenProvider, PasswordEncoder passwordEncoder) {
        this.userRepository = userRepository;         // DB 접근용 Repository
        this.jwtTokenProvider = jwtTokenProvider;     // JWT 토큰 생성/검증용 Provider
        this.passwordEncoder = passwordEncoder;       // 비밀번호 암호화/검증용 Encoder
    }
    
    @Override
    public SignUpResultDto signUp(String id, String password, String name, String role) {
        LOGGER.info(&quot;[getSignUpResult] 회원 가입 정보 전달&quot;); // 로그 기록
        User user;
        
        // role이 admin이면 ROLE_ADMIN 부여, 아니면 ROLE_USER 부여
        if (role.equalsIgnoreCase(&quot;admin&quot;)) {
            user = User.builder()
                    .uid(id)                                   // 사용자 ID
                    .name(name)                                // 사용자 이름
                    .password(passwordEncoder.encode(password)) // 비밀번호 암호화 후 저장
                    .roles(Collections.singletonList(&quot;ROLE_ADMIN&quot;)) // 관리자 권한 부여
                    .build();
        } else {
            user = User.builder()
                    .uid(id)
                    .name(name)
                    .password(passwordEncoder.encode(password)) // 비밀번호 암호화 후 저장
                    .roles(Collections.singletonList(&quot;ROLE_USER&quot;)) // 일반 사용자 권한 부여
                    .build();
        }
        
        // DB에 사용자 저장
        User savedUser = userRepository.save(user);
        SignUpResultDto signUpResultDto = new SignUpResultDto();
        
        LOGGER.info(&quot;[getSignUpResult] userEntity 값이 들어왔는지 확인 후 결과값 주입&quot;);
        // 저장된 사용자 이름이 비어있지 않으면 성공 처리
        if(!savedUser.getName().isEmpty()) {
            LOGGER.info(&quot;[getSignUpResult] 정상 처리 완료&quot;);
            setSuccessResult(signUpResultDto); // 성공 응답 세팅
        } else {
            LOGGER.info(&quot;[getSignUpResult] 실패 처리 완료&quot;);
            setFailResult(signUpResultDto);    // 실패 응답 세팅
        }
        
        return signUpResultDto; // 결과 반환
    }

    @Override
    public SignInResultDto signIn(String id, String password) throws RuntimeException {
        LOGGER.info(&quot;[getSignInResult] signDataHandler로 회원 정보 요청&quot;);
        User user = userRepository.getByUid(id); // ID로 사용자 조회
        LOGGER.info(&quot;[getSignInResult] Id: {}&quot;, id);
        
        LOGGER.info(&quot;[getSignInResult] 패스워드 비교 수행&quot;);
        // 입력한 비밀번호와 DB에 저장된 암호화된 비밀번호 비교
        if(!passwordEncoder.matches(password, user.getPassword())) {
            throw new RuntimeException(); // 불일치 시 예외 발생
        }
        LOGGER.info(&quot;[getSignInResult] 패스워드 일치&quot;);
        
        LOGGER.info(&quot;[getSignInResult] SignInResultDto 객체 생성&quot;);
        // 로그인 성공 시 JWT 토큰 생성 후 DTO에 담음
        SignInResultDto signInResultDto = SignInResultDto.builder()
                .token(jwtTokenProvider.createToken(String.valueOf(user.getUid()), user.getRoles()))
                .build();
        
        LOGGER.info(&quot;[getSignInResult] SignInResultDto 객체에 값 주입&quot;);
        setSuccessResult(signInResultDto); // 성공 응답 세팅
        
        return signInResultDto; // 결과 반환
    }

    // 성공 응답 세팅 메서드
    private void setSuccessResult(SignUpResultDto result) {
        result.setSuccess(true);                          // 성공 여부 true
        result.setCode(CommonResponse.SUCCESS.getCode()); // 공통 응답 코드 SUCCESS
        result.setMsg(CommonResponse.SUCCESS.getMsg());   // 공통 응답 메시지 SUCCESS
    }
    
    // 실패 응답 세팅 메서드
    private void setFailResult(SignUpResultDto result) {
        result.setSuccess(false);                         // 성공 여부 false
        result.setCode(CommonResponse.FAIL.getCode());    // 공통 응답 코드 FAIL
        result.setMsg(CommonResponse.FAIL.getMsg());      // 공통 응답 메시지 FAIL
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(6) SignController&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;required=true는 선택 사항으로, &lt;br /&gt;이미 @PathVariable이나 @RequestParam에서 필수로 지정하면 자동 반영되므로 생략 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1776328614601&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.controller;

import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.jpa.data.dto.SignInResultDto;
import com.example.demo.jpa.data.dto.SignUpResultDto;
import com.example.demo.service.SignService;

import io.swagger.v3.oas.annotations.Parameter;

/**
 * 회원가입 및 로그인 관련 API를 제공하는 REST 컨트롤러
 *
 * 주요 역할:
 * - /sign-api 경로 하위에서 회원가입(sign-up), 로그인(sign-in) 요청 처리
 * - SignService를 호출하여 실제 비즈니스 로직 실행
 * - 예외 발생 시 @ExceptionHandler로 처리하여 JSON 응답 반환
 *
 * 특징:
 * - @RestController: REST API 응답(JSON)을 반환하는 컨트롤러
 * - @RequestMapping(&quot;/sign-api&quot;): 모든 엔드포인트가 /sign-api 하위에 위치
 */
@RestController
@RequestMapping(&quot;/sign-api&quot;)
public class SignController {
	 // 로깅 객체 생성 &amp;rarr; 요청/응답 과정 기록
    private final Logger LOGGER = LoggerFactory.getLogger(SignController.class);
    
    // 회원가입/로그인 서비스 의존성
    private final SignService signService;
    
    // 생성자 주입 &amp;rarr; Spring이 SignService 구현체를 자동으로 주입
    @Autowired
    public SignController(SignService signService) {
        this.signService = signService;
    }
    
    /**
     * 로그인 API
     * - POST /sign-api/sign-in
     * - id, password를 받아 로그인 처리
     * - 성공 시 JWT 토큰을 포함한 SignInResultDto 반환
     */
    @PostMapping(value = &quot;sign-in&quot;)
    public SignInResultDto signIn(
            // Swagger 문서화를 위한 @Parameter 사용
            @Parameter(required=true) @RequestParam String id, 
            @Parameter(required=true) @RequestParam String password)
            throws RuntimeException {
        
        LOGGER.info(&quot;[signIn] 로그인을 시도하고 있습니다. id: {}, pw: ****&quot;, id);
        SignInResultDto signInResultDto = signService.signIn(id, password);
        
        // 로그인 성공 시 로그 기록
        if (signInResultDto.getCode() == 0) {
            LOGGER.info(&quot;[signIn] 로그인 성공. id: {}, token: {}&quot;, id, signInResultDto.getToken());
        }
        return signInResultDto; 
    }
    
    /**
     * 회원가입 API
     * - POST /sign-api/sign-up
     * - id, password, name, role을 받아 회원가입 처리
     * - 성공/실패 여부를 SignUpResultDto로 반환
     */
    @PostMapping(value = &quot;/sign-up&quot;)
    public SignUpResultDto signUp(
            @Parameter(required=true) @RequestParam String id,
            @Parameter(required=true) @RequestParam String password,
            @Parameter(required=true) @RequestParam String name,
            @Parameter(required=true) @RequestParam String role) {
        
        LOGGER.info(&quot;[signUp] 회원가입 수행. id: {}, password: ****, name: {}, role: {}&quot;, id, name, role);
        SignUpResultDto signUpResultDto = signService.signUp(id, password, name, role);
        
        LOGGER.info(&quot;[signUp] 회원가입 완료. id: {}&quot;, id);
        return signUpResultDto;
    }
    
    /**
     * 예외 발생 테스트 API
     * - GET /sign-api/exception
     * - 강제로 RuntimeException 발생시켜 예외 처리 흐름 확인
     */
    @GetMapping(value=&quot;/exception&quot;)
    public void exceptionText() throws RuntimeException {
        throw new RuntimeException(&quot;접근 권한이 없습니다.&quot;);
    }
    
    /**
     * RuntimeException 처리 핸들러
     * - 컨트롤러 내에서 발생한 RuntimeException을 잡아 JSON 응답 반환
     * - 상태 코드: 400 Bad Request
     * - 응답 본문: 에러 타입, 코드, 메시지 포함
     */
    @ExceptionHandler(value = RuntimeException.class)
    public ResponseEntity&amp;lt;Map&amp;lt;String, String&amp;gt;&amp;gt; ExceptionHandler(RuntimeException e) {
        HttpHeaders responseHeaders = new HttpHeaders();
        HttpStatus httpStatus = HttpStatus.BAD_REQUEST;
        
        LOGGER.error(&quot;ExceptionHandler 호출, {}, {}&quot;, e.getCause(), e.getMessage());
        
        // 에러 응답 데이터 구성
        Map&amp;lt;String, String&amp;gt; map = new HashMap&amp;lt;&amp;gt;();
        map.put(&quot;error type&quot;, httpStatus.getReasonPhrase()); // &quot;Bad Request&quot;
        map.put(&quot;code&quot;, &quot;400&quot;);
        map.put(&quot;message&quot;, &quot;에러 발생&quot;);
        
        // ResponseEntity로 JSON 응답 반환
        return new ResponseEntity&amp;lt;&amp;gt;(map, responseHeaders, httpStatus);
    }
}&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 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*** 다음 편에서 계속됩니다 ***&lt;/span&gt;&lt;/h2&gt;</description>
      <category>스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초</category>
      <category>AccessDeniedHandler</category>
      <category>Java</category>
      <category>json web token</category>
      <category>JWT</category>
      <category>spring boot</category>
      <category>Spring Security</category>
      <category>STS4</category>
      <category>스프링 부트</category>
      <category>스프링 시큐리티</category>
      <category>자바</category>
      <author>개발학생</author>
      <guid isPermaLink="true">https://keep-programming-study.tistory.com/197</guid>
      <comments>https://keep-programming-study.tistory.com/197#entry197comment</comments>
      <pubDate>Thu, 16 Apr 2026 17:37:55 +0900</pubDate>
    </item>
    <item>
      <title>스프링부트 핵심 가이드(장정우 지음) - 스프링 시큐리티(Spring Security)와 JWT 1편</title>
      <link>https://keep-programming-study.tistory.com/196</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*책 내용과 다르게, 다음과 같은 환경에서 프로젝트 생성&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Windows11(윈도우 11) 환경&lt;/li&gt;
&lt;li&gt;자바 JDK 17 버전 설치&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://yungenie.tistory.com/11&quot;&gt;https://yungenie.tistory.com/11&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1774942785995&quot; style=&quot;color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fsCfm/hyZjxqU4L2/XdX4MUTaSK5BaU7JmJjh90/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/nM72i/hyZjwZQPen/CQONWbqc5xGfLYajkBk8o0/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/JRE6h/hyZjgihxEK/k0VWPB43AkNJ8rLvVcy1M0/img.png?width=2762&amp;amp;height=2094&amp;amp;face=0_0_2762_2094&quot; data-og-url=&quot;https://yungenie.tistory.com/11&quot; data-og-source-url=&quot;https://yungenie.tistory.com/11&quot; data-og-host=&quot;yungenie.tistory.com&quot; data-og-description=&quot;자바 개발 도구 설치 방법에 대해서 알아보겠습니다. Java17은 LTS(Long Term Support : 장기 지원) 릴리즈로 1년 후까지 기술 지원 및 버그를 개선한 서비스를 제공받을 수 있습니다. 업데이트 버전을 꾸&quot; data-og-title=&quot;[Java] 차근차근 Java 설치하기 (JDK17, Window 11)&quot; data-og-type=&quot;article&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://yungenie.tistory.com/11&quot; data-source-url=&quot;https://yungenie.tistory.com/11&quot;&gt;
&lt;div style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fsCfm/hyZjxqU4L2/XdX4MUTaSK5BaU7JmJjh90/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/nM72i/hyZjwZQPen/CQONWbqc5xGfLYajkBk8o0/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/JRE6h/hyZjgihxEK/k0VWPB43AkNJ8rLvVcy1M0/img.png?width=2762&amp;amp;height=2094&amp;amp;face=0_0_2762_2094');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;[Java] 차근차근 Java 설치하기 (JDK17, Window 11)&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;자바 개발 도구 설치 방법에 대해서 알아보겠습니다. Java17은 LTS(Long Term Support : 장기 지원) 릴리즈로 1년 후까지 기술 지원 및 버그를 개선한 서비스를 제공받을 수 있습니다. 업데이트 버전을 꾸&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;yungenie.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스프링 부트 4.31.0 사용 - STS(Spring Tool Suite) 설치(Spring Tools for Eclipse -&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://spring.io/tools&quot;&gt;https://spring.io/tools&lt;/a&gt;)&lt;br /&gt;=&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://priming.tistory.com/147&quot;&gt;https://priming.tistory.com/147&lt;/a&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;참고&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1774942785998&quot; style=&quot;color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bylxoe/hyZjqL6ykw/At1UjkxTL5MxNIN6mN8Dhk/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/c4WuCh/hyZjyDk2Tg/jLlvw1t5ipklCwRzmvXHkK/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/vUGiq/hyZjaoN1DM/bbtYqu3fqKATJY2ztMT1a1/img.png?width=938&amp;amp;height=621&amp;amp;face=0_0_938_621&quot; data-og-url=&quot;https://priming.tistory.com/147&quot; data-og-source-url=&quot;https://priming.tistory.com/147&quot; data-og-host=&quot;priming.tistory.com&quot; data-og-description=&quot;STS란?Spring Tool Suite(STS)는 스프링 프로젝트를 생성하고, 개발할 수 있게 해주는 도구입니다.&amp;nbsp;STS 설치 과정에 대해 설명드리겠습니다.&amp;nbsp;설치 파일 다운로드STS 공식 사이트에서 설치 파일을 다운로&quot; data-og-title=&quot;[Windows] Spring Tool Suite 4(STS 4) 다운로드 및 설치&quot; data-og-type=&quot;article&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://priming.tistory.com/147&quot; data-source-url=&quot;https://priming.tistory.com/147&quot;&gt;
&lt;div style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bylxoe/hyZjqL6ykw/At1UjkxTL5MxNIN6mN8Dhk/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/c4WuCh/hyZjyDk2Tg/jLlvw1t5ipklCwRzmvXHkK/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/vUGiq/hyZjaoN1DM/bbtYqu3fqKATJY2ztMT1a1/img.png?width=938&amp;amp;height=621&amp;amp;face=0_0_938_621');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;[Windows] Spring Tool Suite 4(STS 4) 다운로드 및 설치&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;STS란?Spring Tool Suite(STS)는 스프링 프로젝트를 생성하고, 개발할 수 있게 해주는 도구입니다.&amp;nbsp;STS 설치 과정에 대해 설명드리겠습니다.&amp;nbsp;설치 파일 다운로드STS 공식 사이트에서 설치 파일을 다운로&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;priming.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2ne97/btsPgUiDCuF/eVBIrd1OJpcnARSiekch31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2ne97/btsPgUiDCuF/eVBIrd1OJpcnARSiekch31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2ne97/btsPgUiDCuF/eVBIrd1OJpcnARSiekch31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2ne97%2FbtsPgUiDCuF%2FeVBIrd1OJpcnARSiekch31%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;1051&quot; height=&quot;634&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;634&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;MySQL&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;Community Server&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;8.0.42 설치&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://dev.mysql.com/downloads/mysql/&quot;&gt;https://dev.mysql.com/downloads/mysql/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1774942785999&quot; style=&quot;color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/QP20c/hyZjCyYfQt/fudyPJf3QzC310TJECw8G1/img.jpg?width=841&amp;amp;height=244&amp;amp;face=0_0_841_244&quot; data-og-url=&quot;https://dev.mysql.com/downloads/mysql/&quot; data-og-source-url=&quot;https://dev.mysql.com/downloads/mysql/&quot; data-og-host=&quot;dev.mysql.com&quot; data-og-description=&quot;Select Version: 9.3.0 Innovation 8.4.5 LTS 8.0.42 Select Operating System: Select Operating System&amp;hellip; Microsoft Windows Ubuntu Linux Debian Linux SUSE Linux Enterprise Server Red Hat Enterprise Linux / Oracle Linux Fedora Linux - Generic Oracle Solaris mac&quot; data-og-title=&quot;MySQL :: Download MySQL Community Server&quot; data-og-type=&quot;website&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://dev.mysql.com/downloads/mysql/&quot; data-source-url=&quot;https://dev.mysql.com/downloads/mysql/&quot;&gt;
&lt;div style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/QP20c/hyZjCyYfQt/fudyPJf3QzC310TJECw8G1/img.jpg?width=841&amp;amp;height=244&amp;amp;face=0_0_841_244');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;MySQL :: Download MySQL Community Server&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Select Version: 9.3.0 Innovation 8.4.5 LTS 8.0.42 Select Operating System: Select Operating System&amp;hellip; Microsoft Windows Ubuntu Linux Debian Linux SUSE Linux Enterprise Server Red Hat Enterprise Linux / Oracle Linux Fedora Linux - Generic Oracle Solaris mac&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;dev.mysql.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;811&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btOcwO/btsPrnefLO2/pUhBPkOVmzbMTaKK1r5sG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btOcwO/btsPrnefLO2/pUhBPkOVmzbMTaKK1r5sG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btOcwO/btsPrnefLO2/pUhBPkOVmzbMTaKK1r5sG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtOcwO%2FbtsPrnefLO2%2FpUhBPkOVmzbMTaKK1r5sG0%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;1272&quot; height=&quot;811&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;811&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;Gradle&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;**STS에서 Gradle 프로젝트 생성한 과정&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K3amN/btsPiv4lwJt/uJutkklzbXbReULQxgZ8y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K3amN/btsPiv4lwJt/uJutkklzbXbReULQxgZ8y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K3amN/btsPiv4lwJt/uJutkklzbXbReULQxgZ8y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK3amN%2FbtsPiv4lwJt%2FuJutkklzbXbReULQxgZ8y1%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;914&quot; height=&quot;690&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;690&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;582&quot; data-origin-height=&quot;774&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m9kKL/btsPhOpIZXk/EXjrrKX55fmvxR1bktggSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m9kKL/btsPhOpIZXk/EXjrrKX55fmvxR1bktggSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m9kKL/btsPhOpIZXk/EXjrrKX55fmvxR1bktggSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm9kKL%2FbtsPhOpIZXk%2FEXjrrKX55fmvxR1bktggSK%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;582&quot; height=&quot;774&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;774&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;657&quot; data-origin-height=&quot;782&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kH6Gt/btsPjkt76G1/oLBQL2FY5kFBr6K3pyVy0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kH6Gt/btsPjkt76G1/oLBQL2FY5kFBr6K3pyVy0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kH6Gt/btsPjkt76G1/oLBQL2FY5kFBr6K3pyVy0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkH6Gt%2FbtsPjkt76G1%2FoLBQL2FY5kFBr6K3pyVy0K%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;657&quot; height=&quot;782&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;782&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;*** 함께 보면 좋은 글&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://keep-programming-study.tistory.com/164&quot;&gt;2025.07.10 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링 부트 핵심 가이드(장정우 지음) - 스프링 부트 개요&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1774942786001&quot; style=&quot;color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cKwogg/dJMb8YXLeby/t0Bu5mY707lZsNsYmzSEwK/img.png?width=800&amp;amp;height=344&amp;amp;face=0_0_800_344,https://scrap.kakaocdn.net/dn/fJiK1/dJMb8PGvR9H/JLg23FPplvyYp1FUztqDR0/img.png?width=800&amp;amp;height=344&amp;amp;face=0_0_800_344,https://scrap.kakaocdn.net/dn/cmXT54/dJMb8XR5ntE/1b1MkIf52cZ35jO3kKrcFk/img.png?width=1109&amp;amp;height=477&amp;amp;face=0_0_1109_477&quot; data-og-url=&quot;https://keep-programming-study.tistory.com/164&quot; data-og-source-url=&quot;https://keep-programming-study.tistory.com/164&quot; data-og-host=&quot;keep-programming-study.tistory.com&quot; data-og-description=&quot;1. 스프링 프레임워크자바(Java) 기반 애플리케이션 프레임워크로, 엔터프라이즈급(기업 환경 대상 개발) 애플리케이션을 위한 다양한 기능 제공-&amp;gt; 오픈소스 경량급 애플리케이션 프레임워크로&quot; data-og-title=&quot;스프링 부트 핵심 가이드(장정우 지음) - 스프링 부트 개요&quot; data-og-type=&quot;article&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://keep-programming-study.tistory.com/164&quot; data-source-url=&quot;https://keep-programming-study.tistory.com/164&quot;&gt;
&lt;div style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cKwogg/dJMb8YXLeby/t0Bu5mY707lZsNsYmzSEwK/img.png?width=800&amp;amp;height=344&amp;amp;face=0_0_800_344,https://scrap.kakaocdn.net/dn/fJiK1/dJMb8PGvR9H/JLg23FPplvyYp1FUztqDR0/img.png?width=800&amp;amp;height=344&amp;amp;face=0_0_800_344,https://scrap.kakaocdn.net/dn/cmXT54/dJMb8XR5ntE/1b1MkIf52cZ35jO3kKrcFk/img.png?width=1109&amp;amp;height=477&amp;amp;face=0_0_1109_477');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;스프링 부트 핵심 가이드(장정우 지음) - 스프링 부트 개요&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;1. 스프링 프레임워크자바(Java) 기반 애플리케이션 프레임워크로, 엔터프라이즈급(기업 환경 대상 개발) 애플리케이션을 위한 다양한 기능 제공-&amp;gt; 오픈소스 경량급 애플리케이션 프레임워크로&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;keep-programming-study.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://keep-programming-study.tistory.com/165&quot;&gt;2025.07.11 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링 부트 핵심 가이드(장정우 지음) - 개발에 앞서 알면 좋은 기초 지식&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1774942786002&quot; style=&quot;color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c9gDhz/dJMb8ZvBccu/2QNWq4g5C6VrkP0wGKtZY1/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/cPOVnB/dJMb9gxk78R/WcjaaAR4axEqNjjUgA5Sl1/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/bgWzrr/dJMb87f6bU5/fIlGVlzvvAdNhnssUsRXYK/img.png?width=1239&amp;amp;height=564&amp;amp;face=0_0_1239_564&quot; data-og-url=&quot;https://keep-programming-study.tistory.com/165&quot; data-og-source-url=&quot;https://keep-programming-study.tistory.com/165&quot; data-og-host=&quot;keep-programming-study.tistory.com&quot; data-og-description=&quot;1. 서버 간 통신마이크로서비스 아키텍처에서 한 서버가 다른 서버에 통신을 요청하는 것을 의미-&amp;gt; 한 대는 서버/다른 한 대는 클라이언트가 됨 가장 많이 사용되는 방식은 HTTP/HTTPS 방식(TCP/IP, SOA&quot; data-og-title=&quot;스프링 부트 핵심 가이드(장정우 지음) - 개발에 앞서 알면 좋은 기초 지식&quot; data-og-type=&quot;article&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://keep-programming-study.tistory.com/165&quot; data-source-url=&quot;https://keep-programming-study.tistory.com/165&quot;&gt;
&lt;div style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c9gDhz/dJMb8ZvBccu/2QNWq4g5C6VrkP0wGKtZY1/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/cPOVnB/dJMb9gxk78R/WcjaaAR4axEqNjjUgA5Sl1/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/bgWzrr/dJMb87f6bU5/fIlGVlzvvAdNhnssUsRXYK/img.png?width=1239&amp;amp;height=564&amp;amp;face=0_0_1239_564');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;스프링 부트 핵심 가이드(장정우 지음) - 개발에 앞서 알면 좋은 기초 지식&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;1. 서버 간 통신마이크로서비스 아키텍처에서 한 서버가 다른 서버에 통신을 요청하는 것을 의미-&amp;gt; 한 대는 서버/다른 한 대는 클라이언트가 됨 가장 많이 사용되는 방식은 HTTP/HTTPS 방식(TCP/IP, SOA&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;keep-programming-study.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://keep-programming-study.tistory.com/195&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2026.03.26 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링부트 핵심 가이드(장정우 지음) - 스프링 시큐리티(Spring Security) 개요&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1774942797717&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;스프링부트 핵심 가이드(장정우 지음) - 스프링 시큐리티(Spring Security) 개요&quot; data-og-description=&quot;*책 내용과 다르게, 다음과 같은 환경에서 프로젝트 생성 Windows11(윈도우 11) 환경자바 JDK 17 버전 설치 https://yungenie.tistory.com/11 [Java] 차근차근 Java 설치하기 (JDK17, Window 11)자바 개발 도구 설치 방법&quot; data-og-host=&quot;keep-programming-study.tistory.com&quot; data-og-source-url=&quot;https://keep-programming-study.tistory.com/195&quot; data-og-url=&quot;https://keep-programming-study.tistory.com/195&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cEY76J/dJMb8RRR15w/rklKGtzneJ2ma67xsw0dOK/img.png?width=800&amp;amp;height=303&amp;amp;face=0_0_800_303,https://scrap.kakaocdn.net/dn/3Ub3X/dJMb8SXxVEf/ZDbhWM10jyhNhXUEGVC22K/img.png?width=800&amp;amp;height=303&amp;amp;face=0_0_800_303,https://scrap.kakaocdn.net/dn/j7FIT/dJMb87NWy9P/rfSQuBcvuBHe8yuXgCYmGk/img.png?width=1051&amp;amp;height=634&amp;amp;face=0_0_1051_634&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/195&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://keep-programming-study.tistory.com/195&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cEY76J/dJMb8RRR15w/rklKGtzneJ2ma67xsw0dOK/img.png?width=800&amp;amp;height=303&amp;amp;face=0_0_800_303,https://scrap.kakaocdn.net/dn/3Ub3X/dJMb8SXxVEf/ZDbhWM10jyhNhXUEGVC22K/img.png?width=800&amp;amp;height=303&amp;amp;face=0_0_800_303,https://scrap.kakaocdn.net/dn/j7FIT/dJMb87NWy9P/rfSQuBcvuBHe8yuXgCYmGk/img.png?width=1051&amp;amp;height=634&amp;amp;face=0_0_1051_634');&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;스프링부트 핵심 가이드(장정우 지음) - 스프링 시큐리티(Spring Security) 개요&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;*책 내용과 다르게, 다음과 같은 환경에서 프로젝트 생성 Windows11(윈도우 11) 환경자바 JDK 17 버전 설치 https://yungenie.tistory.com/11 [Java] 차근차근 Java 설치하기 (JDK17, Window 11)자바 개발 도구 설치 방법&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;keep-programming-study.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. JWT(Json Web Token) 개요&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;당사자 간 정보를 JSON 형태로 안전하게 전송하기 위한 토큰&lt;/li&gt;
&lt;li&gt;URL로 이용 가능한 문자열로만 구성되어 있어, HTTP 구성요소 어디든 위치 가능&lt;/li&gt;
&lt;li&gt;디지털 서명이 적용되어 있어 신뢰성 확보&lt;/li&gt;
&lt;li&gt;주로 서버와의 통신에서 권한 인가를 위해 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) JWT의 구조&lt;/h3&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;b&gt;헤더(Header).내용(Payload).서명(Signature)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) 헤더(Header)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검증과 관련 내용을 담고 있음&lt;/li&gt;
&lt;li&gt;alg, typ 속성
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;alg: 해싱 알고리즘(SHA256 또는 RSA 사용)을 지정하여 토큰 검증시 사용되는 서명 부분에서 사용&lt;br /&gt;-&amp;gt; 어떤&amp;nbsp;알고리즘을&amp;nbsp;사용해서&amp;nbsp;서명을&amp;nbsp;만들고&amp;nbsp;검증할지&amp;nbsp;지정하는&amp;nbsp;값&amp;nbsp;&lt;/li&gt;
&lt;li&gt;typ: 토큰 타입 지정&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 51px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20.2326%; height: 17px;&quot;&gt;해싱 알고리즘&amp;nbsp; &amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 17.2093%; height: 17px;&quot;&gt;방식&lt;/td&gt;
&lt;td style=&quot;width: 37.5581%; height: 17px;&quot;&gt;키 구조&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;JWT 사용 예&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20.2326%; height: 17px;&quot;&gt;SHA-256 (HMAC)&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 17.2093%; height: 17px;&quot;&gt;해시 기반&amp;nbsp;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 37.5581%; height: 17px;&quot;&gt;대칭키&amp;nbsp;(서명&amp;middot;검증에&amp;nbsp;같은&amp;nbsp;키&amp;nbsp;사용)&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;HS256&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20.2326%; height: 17px;&quot;&gt;RSA&lt;/td&gt;
&lt;td style=&quot;width: 17.2093%; height: 17px;&quot;&gt;공개키&amp;nbsp;암호화&lt;/td&gt;
&lt;td style=&quot;width: 37.5581%; height: 17px;&quot;&gt;비대칭키&amp;nbsp;(서명은&amp;nbsp;비밀키,&amp;nbsp;검증은&amp;nbsp;공개키)&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;RS256&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1774946539065&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;alg&quot;: &quot;HS256&quot;,
  &quot;typ&quot;: &quot;JWT&quot;
}&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;완성된 헤더는 Base64Url 형식으로 인코딩되어 사용 &lt;br /&gt;&lt;span&gt;-&amp;gt; JWT &lt;/span&gt;&lt;span&gt;헤더를 &lt;/span&gt;&lt;span&gt;JSON으로 &lt;/span&gt;&lt;span&gt;만든 &lt;/span&gt;&lt;span&gt;뒤, &lt;/span&gt;&lt;span&gt;Base64Url &lt;/span&gt;&lt;span&gt;방식으로 &lt;/span&gt;&lt;span&gt;변환해서 &lt;/span&gt;&lt;span&gt;토큰에 &lt;/span&gt;&lt;span&gt;포함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) 내용(Payload)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;토큰에 담는 정보를 포함&lt;/li&gt;
&lt;li&gt;클레임(Claim) 속성을 세 가지로 분류
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;등록된 클레임(Registered Claims)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;iss: JWT 발급자(Issuer) 주체로, 값은 문자열이나 URI를 포함하는 대소문자 구분 문자열&lt;/li&gt;
&lt;li&gt;sub: JWT의 제목(Subject)&lt;/li&gt;
&lt;li&gt;aud: JWT의 수신인(Audience)로, 각 주체(iss)가 이 값으로 자신을 식별하지 않으면 JWT가 거부됨&lt;/li&gt;
&lt;li&gt;exp: JWT의 만료시간(Expiration)으로, 시간은 NumericDate 형식[&lt;b&gt;1970년 1월 1일 00:00:00 UTC(유닉스 에포크, Unix Epoch)부터 경과한 초를 정수로 나타낸 값&lt;/b&gt;] 으로 지정&amp;nbsp;&lt;/li&gt;
&lt;li&gt;nbf: 'Not Before'를 의미&lt;/li&gt;
&lt;li&gt;iat: JWT가 발급된 시간(Issued at)&lt;/li&gt;
&lt;li&gt;jti: JWT의 식별자(JWT ID)로, 중복 처리 방지를 위해 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;공개 클레임(Public Claims)&lt;br /&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;키 값을 마음대로 정의할 수 있는데, 충돌이 발생하지 않도록 주의 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;비공개 클레임(Private Claims)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;통신 간 상호 합의된, 등록된/공개된 클레임이 아닌 클레임&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1774947454566&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;sub&quot;: &quot;wikibooks payload&quot;,
  &quot;exp&quot;: &quot;1602076408&quot;,
  &quot;userId&quot;: &quot;wikibooks&quot;,
  &quot;username&quot;: &quot;flature&quot;
}&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;완성된 헤더는 Base64Url 형식으로 인코딩되어 사용&lt;br /&gt;&lt;span&gt;-&amp;gt; JWT&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;헤더를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;JSON으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;만든&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;뒤,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Base64Url&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;방식으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;변환해서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;토큰에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;포함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;(3) 서명(Signature)&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인코딩된 헤더, 인코딩된 내용, 비밀 키, 헤더의 알고리즘 속성값을 가져와 생성&lt;/li&gt;
&lt;li&gt;서명은 토큰 값들을 포함하여 암호화하므로, 메시지가 도중에 변경되지 않았는지 확인할 때 사용&amp;nbsp;&lt;/li&gt;
&lt;li&gt;예시(HMAC SHA256 알고리즘 사용)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1775119467593&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;HMACSHA256(
  base64UrlEncode(Header) + &quot;.&quot; +
  base64UrlEncode(Payload),
  secret
  )&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) JWT 디버거 사용하기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JWT 공식 사이트(&lt;a href=&quot;https://www.jwt.io/#debugger-io&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.jwt.io/#debugger-io&lt;/a&gt;)에서 더욱 쉽게 JWT 생성 가능&lt;br /&gt;-&amp;gt; 웹 브라우저에서 접속하여 다음과 같은 화면을 볼 수 있음&lt;/li&gt;
&lt;li&gt;JWT에 대한 상세 내용은 &lt;a href=&quot;https://www.jwt.io/introduction#what-is-json-web-token&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.jwt.io/introduction#what-is-json-web-token&lt;/a&gt;에서 확인 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1775119810330&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;JSON Web Token Introduction - jwt.io&quot; data-og-description=&quot;Learn about JSON Web Tokens, what are they, how they work, when and why you should use them.&quot; data-og-host=&quot;www.jwt.io&quot; data-og-source-url=&quot;https://www.jwt.io/introduction#what-is-json-web-token&quot; data-og-url=&quot;https://jwt.io/introduction&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/zWNEw/dJMb8TB9XWG/OoK8AykrMODKit9OwGRNm0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/e4O65/dJMb8U8TV0J/BkzNJERLF5tset9zuTKFS0/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675,https://scrap.kakaocdn.net/dn/bf7wlR/dJMb8VNvJRM/oozfmiFM4RuZIXnphKLeg0/img.png?width=1551&amp;amp;height=2379&amp;amp;face=0_0_1551_2379&quot;&gt;&lt;a href=&quot;https://www.jwt.io/introduction#what-is-json-web-token&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.jwt.io/introduction#what-is-json-web-token&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/zWNEw/dJMb8TB9XWG/OoK8AykrMODKit9OwGRNm0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/e4O65/dJMb8U8TV0J/BkzNJERLF5tset9zuTKFS0/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675,https://scrap.kakaocdn.net/dn/bf7wlR/dJMb8VNvJRM/oozfmiFM4RuZIXnphKLeg0/img.png?width=1551&amp;amp;height=2379&amp;amp;face=0_0_1551_2379');&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;JSON Web Token Introduction - jwt.io&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn about JSON Web Tokens, what are they, how they work, when and why you should use them.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.jwt.io&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1405&quot; data-origin-height=&quot;921&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wDtGy/dJMcajhn8Ak/XhaLv2kU5PPT5SQUm0ilCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wDtGy/dJMcajhn8Ak/XhaLv2kU5PPT5SQUm0ilCk/img.png&quot; data-alt=&quot;JWT 공식 사이트 - Decoded의 내용 변경 시 Encoded 콘텐츠가 자동으로 수정됨&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wDtGy/dJMcajhn8Ak/XhaLv2kU5PPT5SQUm0ilCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwDtGy%2FdJMcajhn8Ak%2FXhaLv2kU5PPT5SQUm0ilCk%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;1405&quot; height=&quot;921&quot; data-origin-width=&quot;1405&quot; data-origin-height=&quot;921&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JWT 공식 사이트 - Decoded의 내용 변경 시 Encoded 콘텐츠가 자동으로 수정됨&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 스프링 시큐리티와 JWT 적용&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 기존 프로젝트(study)에 의존성 추가&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;build.gradle의 dependencies에 다음과 같이 추가한 후, 저장 후 우클릭하여 Gradle -&amp;gt; Refresh Gradle Project 클릭&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1775120400033&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
    ... 생략 ...
    
    // Spring Boot 기본 스타터
    implementation 'org.springframework.boot:spring-boot-starter'

    // MariaDB JDBC Driver
    runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'

    // Spring Security
    implementation 'org.springframework.boot:spring-boot-starter-security'

    // JJWT (Java JWT)
    implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
    runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
    runtimeOnly('io.jsonwebtoken:jjwt-jackson:0.11.5') // JSON 처리용
}&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;2) UserDetails와 UserDetailsService 구현&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UserDetails는 스프링 시큐리티에서 제공하는 개념&lt;/li&gt;
&lt;li&gt;UserDetails에서의 username은 각 사용자를 구분할 수 있는 ID를 의미&lt;/li&gt;
&lt;li&gt;UserDetails의 구현체로 User 엔티티를 생성하게 하여 User 객체를 리턴하게끔 구현&amp;nbsp;&lt;br /&gt;-&amp;gt; &lt;b&gt;Spring Security가 요구하는 사용자 정보 구조와 DB 엔티티를 일치시켜서 변환 과정을 줄이고, &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; 권한/계정 상태를 통합 관리&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) User 엔티티 생성&lt;/h4&gt;
&lt;pre id=&quot;code_1775121447600&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.jpa.data.entity;

import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;

import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;

import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;


@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Table
public class User implements UserDetails {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Column(nullable = false, unique = true)
    private String uid;

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Column(nullable = false)
    private String password;

    @Column(nullable = false)
    private String name;

    @ElementCollection(fetch = FetchType.EAGER)
    @Builder.Default
    private List&amp;lt;String&amp;gt; roles = new ArrayList&amp;lt;&amp;gt;();

    /** 사용자 권한 목록 반환 (roles &amp;rarr; SimpleGrantedAuthority 변환) */
    @Override
    public Collection&amp;lt;? extends GrantedAuthority&amp;gt; getAuthorities() {
        return this.roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
    }

    /** 로그인 아이디로 사용할 uid 반환 */
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Override
    public String getUsername() {
        return this.uid;
    }

    /** 비밀번호 반환 (JSON 응답에는 노출되지 않음) */
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Override
    public String getPassword() {
        return this.password;
    }

    /** 계정 만료 여부 (true &amp;rarr; 만료 없음) */
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    /** 계정 잠금 여부 (true &amp;rarr; 잠금 없음) */
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    /** 자격 증명 만료 여부 (true &amp;rarr; 만료 없음) */
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    /** 계정 활성화 여부 (true &amp;rarr; 항상 활성화) */
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Override
    public boolean isEnabled() {
        return true;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) UserRepository 구현&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JpaRepository 상속&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1775810824524&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.jpa.data.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.example.demo.jpa.data.entity.User;

/**
 * User 엔티티용 JPA Repository
 *
 * - JpaRepository&amp;lt;User, Long&amp;gt; 상속: 기본적인 CRUD 메서드 제공
 * - 제네릭 타입:
 *   User &amp;rarr; 엔티티 클래스
 *   Long &amp;rarr; 기본 키 타입
 *
 * 커스텀 메서드:
 * - getByUid(String uid): uid 컬럼을 기준으로 User 엔티티 조회
 *   (Spring Data JPA의 메서드 이름 규칙에 따라 자동으로 쿼리 생성)
 */
public interface UserRepository extends JpaRepository&amp;lt;User, Long&amp;gt; {
    User getByUid(String uid);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(3) UserDetailsService, UserDetailsServiceImpl 구현&lt;/h4&gt;
&lt;pre id=&quot;code_1775811157979&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.service;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

/**
 * 사용자 인증을 위한 서비스 인터페이스
 *
 * - Spring Security에서 사용자 정보를 불러오기 위해 사용
 * - UserDetailsService 인터페이스는 반드시 구현체가 필요하며,
 *   loadUserByUsername() 메서드를 통해 DB 등에서 사용자 정보를 조회
 *
 * 주요 메서드:
 * - loadUserByUsername(String username)
 *   &amp;rarr; username(로그인 아이디)으로 UserDetails 객체를 반환
 *   &amp;rarr; 사용자가 존재하지 않을 경우 UsernameNotFoundException 발생
 */
public interface UserDetailsService {
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1775811415931&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.service.impl;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import com.example.demo.jpa.data.repository.UserRepository;
import com.example.demo.service.UserDetailsService;


import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    
    /** 로깅을 위한 Logger 인스턴스 */
    private final Logger LOGGER = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
    
    /** 사용자 정보를 조회하기 위한 JPA Repository */
    private final UserRepository userRepository;
    
    /**
     * username(uid)으로 사용자 정보를 조회하여 UserDetails 반환
     * - 로그 기록: 메서드 수행 시 username 출력
     * - userRepository.getByUid(username) 호출로 DB에서 사용자 조회
     * - 조회된 User 엔티티는 UserDetails를 구현하므로 그대로 반환 가능
     */
    @Override
    public UserDetails loadUserByUsername(String username) {
        LOGGER.info(&quot;[loadUserByUsername] 수행, username: {}&quot;, username);
        return userRepository.getByUid(username);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) JwtTokenProvider 구현&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) UsernamePasswordAuthenticationToken의 상속 구조&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bktcrc/dJMcadn3p40/pjUbOXc0iOpmNkE4uhWTPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bktcrc/dJMcadn3p40/pjUbOXc0iOpmNkE4uhWTPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bktcrc/dJMcadn3p40/pjUbOXc0iOpmNkE4uhWTPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbktcrc%2FdJMcadn3p40%2FpjUbOXc0iOpmNkE4uhWTPk%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;792&quot; height=&quot;580&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;580&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;(2) JwtTokenProvider 구현&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JWT 토큰 생성에 필요한 정보를 UserDetails에서 가져와서, JwtTokenProvider에서 JWT 토큰을 생성&lt;/li&gt;
&lt;li&gt;보안을 위해서는 secretKey 값을 application.properties 파일에서 값을 정의하나, &lt;br /&gt;값을 가져올 수 없을 경우 기본값(아래 코드에서는 secretKey)을 가져옴&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1775812646081&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.config.security;

import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Base64;
import java.util.Date;
import java.util.List;

import javax.crypto.spec.SecretKeySpec;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import com.example.demo.service.UserDetailsService;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;

/**
 * JWT 토큰을 생성하고 관리하는 Provider 클래스
 *
 * 주요 역할:
 * - secretKey 초기화 (Base64 인코딩)
 * - 사용자 정보(uid, roles)를 Claims에 담아 JWT 토큰 생성
 * - 토큰에서 subject(uid) 추출 (getUsername)
 * - 토큰 기반으로 UserDetails 조회 후 Authentication 객체 생성 (getAuthentication)
 * - HTTP 요청 헤더에서 토큰 추출 (resolveToken)
 * - 토큰 유효성 검증 (validateToken: 서명 검증 및 만료 시간 확인)
 *
 * 즉, JWT 인증 흐름의 핵심 기능을 모두 제공하는 클래스이며,
 * Spring Security의 필터나 인증 로직에서 직접 사용되어
 * 사용자 인증 및 권한 부여 과정을 지원한다.
 *
 * 사용 라이브러리:
 * - io.jsonwebtoken.Jwts: JWT 빌더 및 파서
 * - io.jsonwebtoken.Claims: JWT payload에 담을 데이터
 * - SignatureAlgorithm.HS256: HMAC-SHA256 알고리즘으로 서명
 */

@Component
@RequiredArgsConstructor
public class JwtTokenProvider {
    
    /** 로깅을 위한 Logger 인스턴스 */
    private final Logger LOGGER = LoggerFactory.getLogger(JwtTokenProvider.class);

    /** 사용자 정보를 조회하기 위한 UserDetailsService (인증 과정에서 활용 가능) */
    private final UserDetailsService userDetailsService;
    
    /** application.yml 또는 properties에서 주입받는 secretKey (기본값: &quot;secretKey&quot;) */
    @Value(&quot;${springboot.jwt.secret}&quot;)
    private String secretKey = &quot;secretKey&quot;;

    /** 토큰 유효 시간 (1시간 = 1000ms * 60 * 60) */
    private final long tokenValidMillisecond = 1000L * 60 * 60;
    
    /** 변환된 Key 객체를 저장할 필드 */
    private Key key;
    
    /**
     * secretKey 초기화 메서드
     * - @PostConstruct: Bean 생성 후 자동 실행
     * - secretKey를 Base64 인코딩하여 보안 강화
     * - secretKey &amp;rarr; Key 객체 변환을 한 번만 수행(보안 강화를 위해 String 대신 Key 객체 사용)
     */
    @PostConstruct
    protected void init() {
        LOGGER.info(&quot;[init] JwtTokenProvider 내 secretKey 초기화 시작&quot;);
        secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes(StandardCharsets.UTF_8));
        key = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8),
                                SignatureAlgorithm.HS256.getJcaName());
        
        LOGGER.info(&quot;[init] JwtTokenProvider 내 secretKey 초기화 완료&quot;);
    }
    
    /**
     * JWT 토큰 생성 메서드
     *
     * @param userUid 사용자 고유 ID (subject)
     * @param roles   사용자 권한 목록
     * @return        생성된 JWT 토큰 문자열
     *
     * 동작 흐름:
     * 1. Claims 객체 생성 &amp;rarr; subject(userUid)와 roles 추가
     * 2. 현재 시간(now) 기준으로 발급일(issuedAt)과 만료일(expiration) 설정
     * 3. secretKey를 Key 객체로 변환 (SecretKeySpec)
     * 4. Jwts.builder()로 토큰 생성 및 HS256 알고리즘으로 서명
     * 5. compact() 호출로 최종 JWT 문자열 반환
     */
    public String createToken(String userUid, List&amp;lt;String&amp;gt; roles) {
        LOGGER.info(&quot;[createToken] 토큰 생성 시작&quot;);
        
        // JWT payload에 담을 Claims 생성
        Claims claims = Jwts.claims().setSubject(userUid);
        claims.put(&quot;roles&quot;, roles);
        Date now = new Date();
        
        // JWT 토큰 빌드
        String token = Jwts.builder()
                .setClaims(claims) // 사용자 정보 포함
                .setIssuedAt(now) // 발급 시간
                .setExpiration(new Date(now.getTime() + tokenValidMillisecond)) // 만료 시간
                .signWith(key, SignatureAlgorithm.HS256) // 서명 (Key 객체 기반)
                .compact(); // 최종 문자열 반환
        
        LOGGER.info(&quot;[createToken] 토큰 생성 완료&quot;);
        return token;
    }
    
    /**
     * JWT 토큰에서 사용자 이름(subject)을 추출하는 메서드
     *
     * 동작 흐름:
     * 1. secretKey를 Key 객체로 변환 (SecretKeySpec 사용)
     *    - 문자열 기반 키 대신 Key 객체를 사용해야 보안 강화 및 최신 JJWT API 대응 가능
     * 2. Jwts.parserBuilder()로 파서 생성
     *    - setSigningKey(key): 토큰 서명 검증을 위한 키 설정
     *    - build(): 파서 빌드
     * 3. parseClaimsJws(token): 토큰을 파싱하여 Claims(페이로드) 추출
     * 4. getBody().getSubject(): Claims에서 subject 값(사용자 UID) 반환
     * 5. 로그 기록: 토큰 기반 회원 구별 정보 추출 시작/완료 로그 출력
     *
     * @param token JWT 토큰 문자열
     * @return      토큰에 저장된 subject (userUid)
     */
    public String getUsername(String token) {
        LOGGER.info(&quot;[getUsername] 토큰 기반 회원 구별 정보 추출&quot;);

        // JWT 파싱 및 subject 추출
        String info = Jwts.parserBuilder()
                .setSigningKey(key)   // ✅ Key 객체 사용
                .build()
                .parseClaimsJws(token)
                .getBody()
                .getSubject();

        LOGGER.info(&quot;[getUsername] 토큰 기반 회원 구별 정보 추출 완료, info: {}&quot;, info);

        return info;
    }

    /**
     * JWT 토큰을 기반으로 Authentication 객체를 생성하는 메서드
     *
     * 동작 흐름:
     * 1. 로그 기록: 토큰 인증 정보 조회 시작
     * 2. getUsername(token) 호출 &amp;rarr; 토큰에서 subject(uid) 추출
     * 3. userDetailsService.loadUserByUsername(uid) 호출 &amp;rarr; DB에서 사용자 정보(UserDetails) 조회
     * 4. 로그 기록: 조회 완료 및 사용자 이름 출력
     * 5. UsernamePasswordAuthenticationToken 생성
     *    - principal: 조회된 UserDetails 객체
     *    - credentials: 빈 문자열(&quot;&quot;) &amp;rarr; 비밀번호는 필요하지 않음
     *    - authorities: UserDetails에 포함된 권한 목록
     * 6. 최종적으로 Authentication 객체 반환 &amp;rarr; SecurityContext에 저장되어 인증 완료 상태로 사용됨
     *
     * @param token JWT 토큰 문자열
     * @return      Spring Security Authentication 객체
     */
    public Authentication getAuthentication(String token) {
        LOGGER.info(&quot;토큰 인증 정보 조회 시작&quot;);

        // 토큰에서 사용자 uid(subject) 추출 후 DB에서 UserDetails 조회
        UserDetails userDetails = userDetailsService.loadUserByUsername(this.getUsername(token));

        LOGGER.info(&quot;토큰 인증 정보 조회 완료, UserDetails UserName: {}&quot;, userDetails.getUsername());

        // UserDetails 기반으로 Authentication 객체 생성
        return new UsernamePasswordAuthenticationToken(userDetails, &quot;&quot;, userDetails.getAuthorities());
    }

    
    /**
     * HTTP 요청 헤더에서 JWT 토큰을 추출하는 메서드
     *
     * 동작 흐름:
     * 1. 로그 기록: 토큰 추출 시작
     * 2. HttpServletRequest의 &quot;X-AUTH-TOKEN&quot; 헤더 값 반환
     *    - 클라이언트가 요청 시 Authorization 헤더 대신 커스텀 헤더(&quot;X-AUTH-TOKEN&quot;)에 토큰을 담아 전달
     * 3. 반환된 문자열이 JWT 토큰으로 사용됨
     *
     * @param request HttpServletRequest 객체
     * @return        요청 헤더에 담긴 JWT 토큰 문자열
     */
    public String resolveToken(HttpServletRequest request) {
        LOGGER.info(&quot;[resolveToken] HTTP 헤더에서 Token 값 추출&quot;);
        return request.getHeader(&quot;X-AUTH-TOKEN&quot;);
    }

    /**
     * JWT 토큰의 유효성을 검증하는 메서드
     *
     * 동작 흐름:
     * 1. 로그 기록: 토큰 유효성 체크 시작
     * 2. secretKey &amp;rarr; Key 객체 변환 (SecretKeySpec 사용)
     *    - 문자열 기반 키 대신 Key 객체를 사용하여 보안 강화
     * 3. Jwts.parserBuilder()로 파서 생성 후 build()
     *    - setSigningKey(key): 토큰 서명 검증용 키 설정
     *    - parseClaimsJws(token): 토큰 파싱 및 서명 검증
     * 4. Claims에서 만료 시간(expiration) 추출
     *    - 현재 시간과 비교하여 만료 여부 확인
     *    - 만료되지 않았다면 true 반환
     * 5. 예외 발생 시 (서명 불일치, 토큰 변조, 만료 등) false 반환
     *
     * @param token JWT 토큰 문자열
     * @return      토큰이 유효하면 true, 그렇지 않으면 false
     */
    public boolean validateToken(String token) {
        LOGGER.info(&quot;[validateToken] 토큰 유효 체크 시작&quot;);

        try {
            // 토큰 파싱 및 서명 검증
            Jws&amp;lt;Claims&amp;gt; claims = Jwts.parserBuilder()
                    .setSigningKey(key)
                    .build()
                    .parseClaimsJws(token);

            // 만료 시간 확인
            return !claims.getBody().getExpiration().before(new Date());
        } catch (Exception e) {
            LOGGER.info(&quot;[validateToken] 토큰 유효 체크 예외 발생&quot;);
            return false;
        }
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4) JwtAuthenticationFilter 구현&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JWT 토큰으로 인증한 후 SecurityContextHolder에 추가하는 필터를 설정하는 클래스&lt;/li&gt;
&lt;li&gt;GenericFilterBean은 기존 필터에서 가져올 수 없는 스프링 설정 정보를 가져올 수 있게 확장된 추상 클래스&lt;br /&gt;-&amp;gt; &lt;b&gt;서블릿은 사용자의 요청을 받으면 서블릿 생성 후 메모리에 저장해둔 다음 동일한 클라이언트의 요청을 받으면 재활용하는 구조이므로, 이를 상속받으면 RequestDispatcher에 의해 다른 서블릿으로 디스패치되면서 필터가 두 번 실행&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;이를 해결하기 위해 등장한 것이 OncePerRequestFilter&lt;/b&gt;인데, &lt;br /&gt;이는 GenericFilterBean을 상속받지만 매 요청받아 한 번만 실행됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) OncePerRequestFilter 상속 ver - 이 버전을 사용할 것&lt;/h4&gt;
&lt;pre id=&quot;code_1775816200882&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.config.security;

import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

/**
 * JWT 인증을 처리하는 커스텀 필터 클래스
 * 
 * OncePerRequestFilter를 상속하여 모든 요청마다 한 번만 실행됨.
 *
 * 주요 역할:
 * 1. HTTP 요청에서 JWT 토큰 추출 (resolveToken)
 * 2. 토큰 유효성 검증 (validateToken)
 * 3. 토큰이 유효하다면 Authentication 객체 생성 후 SecurityContext에 저장
 *    - SecurityContextHolder는 현재 요청의 인증 상태를 관리하는 Spring Security의 핵심 컨텍스트
 * 4. 필터 체인을 계속 진행하여 이후 요청 처리 로직으로 전달
 *
 * 즉, 이 필터는 클라이언트 요청이 들어올 때마다 JWT 기반 인증을 수행하고,
 * 인증된 사용자 정보를 Spring Security 컨텍스트에 주입하는 역할을 담당
 */
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    private final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
    private final JwtTokenProvider jwtTokenProvider;
    
    public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }
    
    // JWT 검증을 건너뛸 경로들
    private static final List&amp;lt;String&amp;gt; EXCLUDE_URLS = List.of(
    	&quot;/swagger-ui&quot;,
    	&quot;/swagger-ui/&quot;,
    	&quot;/swagger-ui.html&quot;,
    	&quot;/swagger-ui/**&quot;,
    	&quot;/v3/api-docs&quot;,
    	&quot;/v3/api-docs/&quot;,
    	&quot;/v3/api-docs/**&quot;,
    	&quot;/v3/api-docs.yaml&quot;,
    	&quot;/api/public&quot;,
    	&quot;/sign-api&quot;,
    	&quot;/exception&quot;
    );
    
    /**
     * 실제 필터 로직 구현 메서드
     *
     * 동작 흐름:
     * 1. 요청(servletRequest)에서 JWT 토큰 추출
     * 2. 토큰 값 로그 출력
     * 3. 토큰 유효성 검증 시작
     * 4. 토큰이 존재하고 유효하다면:
     *    - JwtTokenProvider.getAuthentication(token) 호출 &amp;rarr; Authentication 객체 생성
     *    - SecurityContextHolder.getContext().setAuthentication(authentication) &amp;rarr; 인증 정보 저장
     *    - 로그 출력: 유효성 체크 완료
     * 5. 필터 체인 계속 진행 (filterChain.doFilter)
     *
     * @param servletRequest  클라이언트 요청 객체
     * @param servletResponse 클라이언트 응답 객체
     * @param filterChain     필터 체인 객체
     */
    @Override
    protected void doFilterInternal(HttpServletRequest servletRequest,
                                    HttpServletResponse servletResponse,
                                    FilterChain filterChain) throws ServletException, IOException {
        
        // Swagger, 공개 API는 JWT 검증 건너뛰기
    	String path = servletRequest.getRequestURI();
    	
        if (EXCLUDE_URLS.stream().anyMatch(path::startsWith)) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        
        // 요청에서 JWT 토큰 추출
        String token = jwtTokenProvider.resolveToken((HttpServletRequest) servletRequest);
        LOGGER.info(&quot;[doFilterInternal] token 값 추출 완료. token: {}&quot;, token);
        
        // 토큰 유효성 검증
        LOGGER.info(&quot;[doFilterInternal] token 값 유효성 체크 시작&quot;);
        if (token != null &amp;amp;&amp;amp; jwtTokenProvider.validateToken(token)) {
            // 토큰이 유효하면 Authentication 객체 생성 후 SecurityContext에 저장
            Authentication authentication = jwtTokenProvider.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            LOGGER.info(&quot;[doFilterInternal] token 값 유효성 체크 완료&quot;);
        }
        
        // 필터 체인 계속 진행
        filterChain.doFilter(servletRequest, servletResponse);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) GenericFilterBean 상속 ver&lt;/h4&gt;
&lt;pre id=&quot;code_1775816597937&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.config.security;

import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;

/**
 * JWT 인증을 처리하는 커스텀 필터 클래스 (GenericFilterBean 기반)
 *
 * 주요 역할:
 * 1. HTTP 요청에서 JWT 토큰 추출 (resolveToken)
 * 2. 토큰 유효성 검증 (validateToken)
 * 3. 토큰이 유효하다면 Authentication 객체 생성 후 SecurityContext에 저장
 *    - SecurityContextHolder는 현재 요청의 인증 상태를 관리하는 Spring Security의 핵심 컨텍스트
 * 4. 필터 체인을 계속 진행하여 이후 요청 처리 로직으로 전달
 *
 * 특징:
 * - GenericFilterBean을 상속하여 Spring Security 필터 체인에 등록 가능
 * - OncePerRequestFilter와 달리, 요청마다 한 번만 실행된다는 보장은 없지만
 *   보통 SecurityConfig에서 적절히 등록하면 동일하게 동작
 *
 * 즉, 이 필터는 클라이언트 요청이 들어올 때마다 JWT 기반 인증을 수행하고,
 * 인증된 사용자 정보를 Spring Security 컨텍스트에 주입하는 역할을 담당한다.
 */
public class JwtAuthenticationFilter2 extends GenericFilterBean {
    private final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationFilter2.class);
    private final JwtTokenProvider jwtTokenProvider;
    
    public JwtAuthenticationFilter2(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }
    
    /**
     * 실제 필터 로직 구현 메서드
     *
     * 동작 흐름:
     * 1. 요청(servletRequest)에서 JWT 토큰 추출
     * 2. 토큰 값 로그 출력
     * 3. 토큰 유효성 검증 시작
     * 4. 토큰이 존재하고 유효하다면:
     *    - JwtTokenProvider.getAuthentication(token) 호출 &amp;rarr; Authentication 객체 생성
     *    - SecurityContextHolder.getContext().setAuthentication(authentication) &amp;rarr; 인증 정보 저장
     *    - 로그 출력: 유효성 체크 완료
     * 5. 필터 체인 계속 진행 (filterChain.doFilter)
     *
     * @param servletRequest  클라이언트 요청 객체
     * @param servletResponse 클라이언트 응답 객체
     * @param filterChain     필터 체인 객체
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        // 요청에서 JWT 토큰 추출
        String token = jwtTokenProvider.resolveToken((HttpServletRequest) servletRequest);
        LOGGER.info(&quot;[doFilter] token 값 추출 완료. token: {}&quot;, token);
        
        // 토큰 유효성 검증
        LOGGER.info(&quot;[doFilter] token 값 유효성 체크 시작&quot;);
        if (token != null &amp;amp;&amp;amp; jwtTokenProvider.validateToken(token)) {
            // 토큰이 유효하면 Authentication 객체 생성 후 SecurityContext에 저장
            Authentication authentication = jwtTokenProvider.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            LOGGER.info(&quot;[doFilter] token 값 유효성 체크 완료&quot;);
        }
        
        // 필터 체인 계속 진행
        filterChain.doFilter(servletRequest, servletResponse);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5) SecurityConfig 구현&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WebSecurityConfigureAdapter를 상속받는 Configuration 구현하는 방식은 최신 버전에서 완전히 제거됨&lt;/li&gt;
&lt;li&gt;Spring Security 팀은 &lt;b&gt;구성 클래스 상속 방식 대신, 명시적인 Bean 등록 방식&lt;/b&gt;을 권장&lt;br /&gt;-&amp;gt; SecurityFilterChain Bean을 직접 등록&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1775817461191&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.config.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig {
	// JWT 필터를 SecurityConfig에 연결하기 위해 꼭 필요한 의존성 주입 코드
	private final JwtTokenProvider jwtTokenProvider;
	
	@Autowired
	public SecurityConfig(JwtTokenProvider jwtTokenProvider) {
		this.jwtTokenProvider = jwtTokenProvider;
	}
	
	 /**
     * Spring Security의 핵심 보안 설정을 정의하는 Bean
     *
     * SecurityFilterChain을 Bean으로 등록해야 최신 버전(Spring Security 5.7+)에서
     * 보안 설정이 적용됨. (WebSecurityConfigurerAdapter는 제거됨)
     *
     * @param http HttpSecurity 객체 (Spring Security가 제공하는 보안 설정 DSL)
     * @return     SecurityFilterChain (최종 보안 필터 체인)
     */
	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
	        // CSRF 비활성화 (REST API 서버에서는 보통 CSRF 토큰을 사용하지 않음)
	        .csrf(csrf -&amp;gt; csrf.disable())
	
	        // 요청별 권한 설정
	        .authorizeHttpRequests(auth -&amp;gt; auth
	            	            .requestMatchers(
	            		&quot;/api/public/**&quot;,
	            		&quot;/swagger-ui/**&quot;,     // Swagger UI 리소스 전체 허용
	            		&quot;/swagger-ui.html&quot;,    // Swagger UI 진입 페이지
	            		&quot;/v3/api-docs/**&quot;,     // OpenAPI 문서 엔드포인트 허용
                        // 뒤에 실습할 api 엔드포인트들 허용
                        &quot;/sign-api/**&quot;,    
                        &quot;**exception**&quot;
	              ).permitAll() // 공개 API는 모두 허용
	            .requestMatchers(HttpMethod.GET, &quot;/product&quot;, &quot;/product/**&quot;).permitAll()  // GET 메서드 외에 나머지는 모두 인증 필요
                // 관리자 전용 페이지라면 .authenticated() 대신 .hasRole(&quot;ADMIN&quot;)
	            .anyRequest().authenticated()                 // 그 외 요청은 인증 필요
	        )
	
	        // JWT 인증 필터를 UsernamePasswordAuthenticationFilter 앞에 추가
	        // &amp;rarr; 기본 로그인 인증 필터보다 먼저 JWT 검증을 수행하도록 설정
	        .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider),
	                         UsernamePasswordAuthenticationFilter.class)
	
	        // 기본 form 로그인 설정 (커스터마이징 가능)
	        .formLogin(Customizer.withDefaults());
	
	    // 최종적으로 SecurityFilterChain 반환
	    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;&amp;nbsp;&lt;/h3&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;***다음 편에서 계속됩니다***&lt;/span&gt;&lt;/h2&gt;</description>
      <category>스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초</category>
      <category>Java</category>
      <category>json web token</category>
      <category>JWT</category>
      <category>Signature</category>
      <category>spring boot</category>
      <category>Spring Security</category>
      <category>STS4</category>
      <category>스프링 부트</category>
      <category>스프링 시큐리티</category>
      <category>자바</category>
      <author>개발학생</author>
      <guid isPermaLink="true">https://keep-programming-study.tistory.com/196</guid>
      <comments>https://keep-programming-study.tistory.com/196#entry196comment</comments>
      <pubDate>Fri, 10 Apr 2026 19:39:53 +0900</pubDate>
    </item>
    <item>
      <title>스프링부트 핵심 가이드(장정우 지음) - 스프링 시큐리티(Spring Security) 개요</title>
      <link>https://keep-programming-study.tistory.com/195</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*책 내용과 다르게, 다음과 같은 환경에서 프로젝트 생성&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Windows11(윈도우 11) 환경&lt;/li&gt;
&lt;li&gt;자바 JDK 17 버전 설치&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://yungenie.tistory.com/11&quot;&gt;https://yungenie.tistory.com/11&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1774517368546&quot; style=&quot;color: #333333; text-align: start;&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;[Java] 차근차근 Java 설치하기 (JDK17, Window 11)&quot; data-og-description=&quot;자바 개발 도구 설치 방법에 대해서 알아보겠습니다. Java17은 LTS(Long Term Support : 장기 지원) 릴리즈로 1년 후까지 기술 지원 및 버그를 개선한 서비스를 제공받을 수 있습니다. 업데이트 버전을 꾸&quot; data-og-host=&quot;yungenie.tistory.com&quot; data-og-source-url=&quot;https://yungenie.tistory.com/11&quot; data-og-url=&quot;https://yungenie.tistory.com/11&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fsCfm/hyZjxqU4L2/XdX4MUTaSK5BaU7JmJjh90/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/nM72i/hyZjwZQPen/CQONWbqc5xGfLYajkBk8o0/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/JRE6h/hyZjgihxEK/k0VWPB43AkNJ8rLvVcy1M0/img.png?width=2762&amp;amp;height=2094&amp;amp;face=0_0_2762_2094&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://yungenie.tistory.com/11&quot; data-source-url=&quot;https://yungenie.tistory.com/11&quot;&gt;
&lt;div style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fsCfm/hyZjxqU4L2/XdX4MUTaSK5BaU7JmJjh90/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/nM72i/hyZjwZQPen/CQONWbqc5xGfLYajkBk8o0/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/JRE6h/hyZjgihxEK/k0VWPB43AkNJ8rLvVcy1M0/img.png?width=2762&amp;amp;height=2094&amp;amp;face=0_0_2762_2094');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;[Java] 차근차근 Java 설치하기 (JDK17, Window 11)&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;자바 개발 도구 설치 방법에 대해서 알아보겠습니다. Java17은 LTS(Long Term Support : 장기 지원) 릴리즈로 1년 후까지 기술 지원 및 버그를 개선한 서비스를 제공받을 수 있습니다. 업데이트 버전을 꾸&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;yungenie.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스프링 부트 4.31.0 사용 - STS(Spring Tool Suite) 설치(Spring Tools for Eclipse -&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://spring.io/tools&quot;&gt;https://spring.io/tools&lt;/a&gt;)&lt;br /&gt;=&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://priming.tistory.com/147&quot;&gt;https://priming.tistory.com/147&lt;/a&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;참고&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1774517368548&quot; style=&quot;color: #333333; text-align: start;&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;[Windows] Spring Tool Suite 4(STS 4) 다운로드 및 설치&quot; data-og-description=&quot;STS란?Spring Tool Suite(STS)는 스프링 프로젝트를 생성하고, 개발할 수 있게 해주는 도구입니다.&amp;nbsp;STS 설치 과정에 대해 설명드리겠습니다.&amp;nbsp;설치 파일 다운로드STS 공식 사이트에서 설치 파일을 다운로&quot; data-og-host=&quot;priming.tistory.com&quot; data-og-source-url=&quot;https://priming.tistory.com/147&quot; data-og-url=&quot;https://priming.tistory.com/147&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bylxoe/hyZjqL6ykw/At1UjkxTL5MxNIN6mN8Dhk/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/c4WuCh/hyZjyDk2Tg/jLlvw1t5ipklCwRzmvXHkK/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/vUGiq/hyZjaoN1DM/bbtYqu3fqKATJY2ztMT1a1/img.png?width=938&amp;amp;height=621&amp;amp;face=0_0_938_621&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://priming.tistory.com/147&quot; data-source-url=&quot;https://priming.tistory.com/147&quot;&gt;
&lt;div style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bylxoe/hyZjqL6ykw/At1UjkxTL5MxNIN6mN8Dhk/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/c4WuCh/hyZjyDk2Tg/jLlvw1t5ipklCwRzmvXHkK/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/vUGiq/hyZjaoN1DM/bbtYqu3fqKATJY2ztMT1a1/img.png?width=938&amp;amp;height=621&amp;amp;face=0_0_938_621');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;[Windows] Spring Tool Suite 4(STS 4) 다운로드 및 설치&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;STS란?Spring Tool Suite(STS)는 스프링 프로젝트를 생성하고, 개발할 수 있게 해주는 도구입니다.&amp;nbsp;STS 설치 과정에 대해 설명드리겠습니다.&amp;nbsp;설치 파일 다운로드STS 공식 사이트에서 설치 파일을 다운로&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;priming.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2ne97/btsPgUiDCuF/eVBIrd1OJpcnARSiekch31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2ne97/btsPgUiDCuF/eVBIrd1OJpcnARSiekch31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2ne97/btsPgUiDCuF/eVBIrd1OJpcnARSiekch31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2ne97%2FbtsPgUiDCuF%2FeVBIrd1OJpcnARSiekch31%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;1051&quot; height=&quot;634&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;634&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;MySQL&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;Community Server&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;8.0.42 설치&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://dev.mysql.com/downloads/mysql/&quot;&gt;https://dev.mysql.com/downloads/mysql/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1774517368550&quot; style=&quot;color: #333333; text-align: start;&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;MySQL :: Download MySQL Community Server&quot; data-og-description=&quot;Select Version: 9.3.0 Innovation 8.4.5 LTS 8.0.42 Select Operating System: Select Operating System&amp;hellip; Microsoft Windows Ubuntu Linux Debian Linux SUSE Linux Enterprise Server Red Hat Enterprise Linux / Oracle Linux Fedora Linux - Generic Oracle Solaris mac&quot; data-og-host=&quot;dev.mysql.com&quot; data-og-source-url=&quot;https://dev.mysql.com/downloads/mysql/&quot; data-og-url=&quot;https://dev.mysql.com/downloads/mysql/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/QP20c/hyZjCyYfQt/fudyPJf3QzC310TJECw8G1/img.jpg?width=841&amp;amp;height=244&amp;amp;face=0_0_841_244&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://dev.mysql.com/downloads/mysql/&quot; data-source-url=&quot;https://dev.mysql.com/downloads/mysql/&quot;&gt;
&lt;div style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/QP20c/hyZjCyYfQt/fudyPJf3QzC310TJECw8G1/img.jpg?width=841&amp;amp;height=244&amp;amp;face=0_0_841_244');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;MySQL :: Download MySQL Community Server&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Select Version: 9.3.0 Innovation 8.4.5 LTS 8.0.42 Select Operating System: Select Operating System&amp;hellip; Microsoft Windows Ubuntu Linux Debian Linux SUSE Linux Enterprise Server Red Hat Enterprise Linux / Oracle Linux Fedora Linux - Generic Oracle Solaris mac&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;dev.mysql.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;811&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btOcwO/btsPrnefLO2/pUhBPkOVmzbMTaKK1r5sG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btOcwO/btsPrnefLO2/pUhBPkOVmzbMTaKK1r5sG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btOcwO/btsPrnefLO2/pUhBPkOVmzbMTaKK1r5sG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtOcwO%2FbtsPrnefLO2%2FpUhBPkOVmzbMTaKK1r5sG0%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;1272&quot; height=&quot;811&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;811&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;Gradle&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;**STS에서 Gradle 프로젝트 생성한 과정&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K3amN/btsPiv4lwJt/uJutkklzbXbReULQxgZ8y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K3amN/btsPiv4lwJt/uJutkklzbXbReULQxgZ8y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K3amN/btsPiv4lwJt/uJutkklzbXbReULQxgZ8y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK3amN%2FbtsPiv4lwJt%2FuJutkklzbXbReULQxgZ8y1%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;914&quot; height=&quot;690&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;690&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;582&quot; data-origin-height=&quot;774&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m9kKL/btsPhOpIZXk/EXjrrKX55fmvxR1bktggSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m9kKL/btsPhOpIZXk/EXjrrKX55fmvxR1bktggSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m9kKL/btsPhOpIZXk/EXjrrKX55fmvxR1bktggSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm9kKL%2FbtsPhOpIZXk%2FEXjrrKX55fmvxR1bktggSK%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;582&quot; height=&quot;774&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;774&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;657&quot; data-origin-height=&quot;782&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kH6Gt/btsPjkt76G1/oLBQL2FY5kFBr6K3pyVy0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kH6Gt/btsPjkt76G1/oLBQL2FY5kFBr6K3pyVy0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kH6Gt/btsPjkt76G1/oLBQL2FY5kFBr6K3pyVy0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkH6Gt%2FbtsPjkt76G1%2FoLBQL2FY5kFBr6K3pyVy0K%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;657&quot; height=&quot;782&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;782&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;*** 함께 보면 좋은 글&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/164&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.07.10 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링 부트 핵심 가이드(장정우 지음) - 스프링 부트 개요&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1774518063648&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;스프링 부트 핵심 가이드(장정우 지음) - 스프링 부트 개요&quot; data-og-description=&quot;1. 스프링 프레임워크자바(Java) 기반 애플리케이션 프레임워크로, 엔터프라이즈급(기업 환경 대상 개발) 애플리케이션을 위한 다양한 기능 제공-&amp;gt; 오픈소스 경량급 애플리케이션 프레임워크로 &quot; data-og-host=&quot;keep-programming-study.tistory.com&quot; data-og-source-url=&quot;https://keep-programming-study.tistory.com/164&quot; data-og-url=&quot;https://keep-programming-study.tistory.com/164&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cKwogg/dJMb8YXLeby/t0Bu5mY707lZsNsYmzSEwK/img.png?width=800&amp;amp;height=344&amp;amp;face=0_0_800_344,https://scrap.kakaocdn.net/dn/fJiK1/dJMb8PGvR9H/JLg23FPplvyYp1FUztqDR0/img.png?width=800&amp;amp;height=344&amp;amp;face=0_0_800_344,https://scrap.kakaocdn.net/dn/cmXT54/dJMb8XR5ntE/1b1MkIf52cZ35jO3kKrcFk/img.png?width=1109&amp;amp;height=477&amp;amp;face=0_0_1109_477&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/164&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://keep-programming-study.tistory.com/164&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cKwogg/dJMb8YXLeby/t0Bu5mY707lZsNsYmzSEwK/img.png?width=800&amp;amp;height=344&amp;amp;face=0_0_800_344,https://scrap.kakaocdn.net/dn/fJiK1/dJMb8PGvR9H/JLg23FPplvyYp1FUztqDR0/img.png?width=800&amp;amp;height=344&amp;amp;face=0_0_800_344,https://scrap.kakaocdn.net/dn/cmXT54/dJMb8XR5ntE/1b1MkIf52cZ35jO3kKrcFk/img.png?width=1109&amp;amp;height=477&amp;amp;face=0_0_1109_477');&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;1. 스프링 프레임워크자바(Java) 기반 애플리케이션 프레임워크로, 엔터프라이즈급(기업 환경 대상 개발) 애플리케이션을 위한 다양한 기능 제공-&amp;gt; 오픈소스 경량급 애플리케이션 프레임워크로&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;keep-programming-study.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://keep-programming-study.tistory.com/165&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.07.11 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링 부트 핵심 가이드(장정우 지음) - 개발에 앞서 알면 좋은 기초 지식&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1774518068588&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;스프링 부트 핵심 가이드(장정우 지음) - 개발에 앞서 알면 좋은 기초 지식&quot; data-og-description=&quot;1. 서버 간 통신마이크로서비스 아키텍처에서 한 서버가 다른 서버에 통신을 요청하는 것을 의미-&amp;gt; 한 대는 서버/다른 한 대는 클라이언트가 됨 가장 많이 사용되는 방식은 HTTP/HTTPS 방식(TCP/IP, SOA&quot; data-og-host=&quot;keep-programming-study.tistory.com&quot; data-og-source-url=&quot;https://keep-programming-study.tistory.com/165&quot; data-og-url=&quot;https://keep-programming-study.tistory.com/165&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c9gDhz/dJMb8ZvBccu/2QNWq4g5C6VrkP0wGKtZY1/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/cPOVnB/dJMb9gxk78R/WcjaaAR4axEqNjjUgA5Sl1/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/bgWzrr/dJMb87f6bU5/fIlGVlzvvAdNhnssUsRXYK/img.png?width=1239&amp;amp;height=564&amp;amp;face=0_0_1239_564&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/165&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://keep-programming-study.tistory.com/165&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c9gDhz/dJMb8ZvBccu/2QNWq4g5C6VrkP0wGKtZY1/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/cPOVnB/dJMb9gxk78R/WcjaaAR4axEqNjjUgA5Sl1/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/bgWzrr/dJMb87f6bU5/fIlGVlzvvAdNhnssUsRXYK/img.png?width=1239&amp;amp;height=564&amp;amp;face=0_0_1239_564');&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;1. 서버 간 통신마이크로서비스 아키텍처에서 한 서버가 다른 서버에 통신을 요청하는 것을 의미-&amp;gt; 한 대는 서버/다른 한 대는 클라이언트가 됨 가장 많이 사용되는 방식은 HTTP/HTTPS 방식(TCP/IP, SOA&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;keep-programming-study.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 보안 용어 이해&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 인증(authentication)&lt;/h3&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;br /&gt;-&amp;gt; 로그인 성공 시 서버에서 응답으로 사용자에게 토큰(token) 전달,&lt;br /&gt;&amp;nbsp; &amp;nbsp; 실패 시 사용자가 토큰을 받지 못해 원하는 리소스에 접근 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 인가(authorization)&lt;/h3&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;br /&gt;사용자가 리소스에 접근할 때 토큰을 전달하면 서버에서 토큰을 통해 권한을 확인하고 인가를 수행하는 것&lt;/li&gt;
&lt;li&gt;예시: 로그인한 사용자가 특정 게시판의 글을 보려고 하는 경우, 게시판 접근 등급을 확인해 접근 허가 or 접근 거부&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) 접근 주체(principal)&lt;/h3&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;h2 data-ke-size=&quot;size26&quot;&gt;2. 스프링 시큐리티(Spring Security)&amp;nbsp;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 개요&amp;nbsp;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스프링 프레임워크에서 제공하는 강력한 보안 프레임워크&lt;/li&gt;
&lt;li&gt;웹 애플리케이션의 인증, 인가 등 보안 기능을 체계적으로 처리하는 스프링 하위 프로젝트 중 하나&lt;/li&gt;
&lt;li&gt;보안과 관련된 많은 기능을 제공하기 때문에, 이를 활용하여 편리하게 원하는 기능을 설계&lt;/li&gt;
&lt;li&gt;기본적으로&amp;nbsp;모든&amp;nbsp;요청을&amp;nbsp;필터&amp;nbsp;체인(Filter&amp;nbsp;Chain)을&amp;nbsp;통해&amp;nbsp;검사하며,&amp;nbsp;로그인&amp;middot;권한&amp;nbsp;관리&amp;middot;세션&amp;nbsp;제어&amp;middot;데이터&amp;nbsp;보호까지&amp;nbsp;폭넓게&amp;nbsp;지원&lt;/li&gt;
&lt;li&gt;모든 요청이 DispatcherServlet에 도달하기 전에, &lt;br /&gt;필터 체인(Filter Chain, 서블릿 컨테이너에서 관리하는 ApplicationFilterChain을 의미)을 통해 여러 보안 필터를 거쳐 검사&lt;br /&gt;-&amp;gt; DispatcherServlet: &lt;span&gt;Model, &lt;/span&gt;&lt;span&gt;View, &lt;/span&gt;&lt;span&gt;Controller를 &lt;/span&gt;&lt;span&gt;연결하는 &lt;/span&gt;&lt;span&gt;중심 &lt;/span&gt;&lt;span&gt;허브로, 개별 서블릿 등록 없이 모든 요청 관리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;개발자가 SecurityFilterChain 또는 WebSecurityConfigurerAdapter(구버전) 등을 통해 &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;보안 설정(Security Configuration)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&amp;nbsp; 커스터마이징 가능&lt;/li&gt;
&lt;li&gt;자세한 내용은 공식 문서 참조: &lt;a href=&quot;https://spring.io/projects/spring-security&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://spring.io/projects/spring-security&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 동작 구조&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서블릿 필터(Servlet Filter)를 기반으로 동작하며, 필터는 다음과 같이 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;DispatcherServlet 앞에 배치&lt;/span&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;1047&quot; data-origin-height=&quot;397&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buqM7h/dJMcaaxWS3i/kMQKnHvAtgpr4kEVlFjfP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buqM7h/dJMcaaxWS3i/kMQKnHvAtgpr4kEVlFjfP1/img.png&quot; data-alt=&quot;서블릿 필터의 배치&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buqM7h/dJMcaaxWS3i/kMQKnHvAtgpr4kEVlFjfP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuqM7h%2FdJMcaaxWS3i%2FkMQKnHvAtgpr4kEVlFjfP1%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;1047&quot; height=&quot;397&quot; data-origin-width=&quot;1047&quot; data-origin-height=&quot;397&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서블릿 필터의 배치&lt;/figcaption&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;클라이언트에서 애플리케이션으로 요청을 보내면, 서블릿 컨테이너는 URI를 확인하여 필터와 서블릿을 매핑&lt;br /&gt;-&amp;gt; 스프링 시큐리티는 사용하고자 하는 필터체인을 서블릿 컨테이너의 필터 사이어세 동작하게 하기 위해,&lt;br /&gt;&amp;nbsp; &amp;nbsp; 아래와 같이 DelegatingFilterProxy 사용&lt;br /&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;color: #333333; text-align: start;&quot;&gt;&lt;b&gt;DelegatingFilterProxy&lt;/b&gt;: 서블릿 컨테이너의 생명주기와 스프링 애플리케이션 컨텍스트(Application Context) 사이에서&lt;br /&gt;다리 역할을 수행하는 필터 구현체&lt;br /&gt;=&amp;gt; 표준 서블릿 필터를 구현하고 있으며, &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;역할을 위임할 &lt;b&gt;필터체인 프록시(FliterChainProxy, 스프링 부트의 자동 설정으로 자동 생성)&lt;/b&gt;를 내부에 갖고 있음)&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;b&gt;FilterChainProxy&lt;/b&gt;: 스프링 시큐리티에서 제공하는 필터로서, &lt;b&gt;보안 필터체인(SecurityFilterChain)&lt;/b&gt;을 통해 많은 보안 &lt;b&gt;필터(Security Filter)&lt;/b&gt;를 사용 가능&lt;br /&gt;-&amp;gt; 여기서 사용 가능한 보안 필터체인은 WebSecurityConfigurerAdapter 클래스를 상속받아 설정하며,&lt;br /&gt;&amp;nbsp; &amp;nbsp; List 형식으로 담을 수 있게 설정되어 있어 URI 패턴에 따라 특정 보안필터 체인을 선택하여 사용&amp;nbsp;&lt;/span&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;680&quot; data-origin-height=&quot;537&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t0q6U/dJMb99TjZXL/cp0pJUkFkyd7K3j1laE7l1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t0q6U/dJMb99TjZXL/cp0pJUkFkyd7K3j1laE7l1/img.png&quot; data-alt=&quot;DelegatingFilterProxy 내부 FilterChainProxy 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t0q6U/dJMb99TjZXL/cp0pJUkFkyd7K3j1laE7l1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft0q6U%2FdJMb99TjZXL%2Fcp0pJUkFkyd7K3j1laE7l1%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;537&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;537&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DelegatingFilterProxy 내부 FilterChainProxy 구조&lt;/figcaption&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;별도로 추가 설정을 하지 않으면, 스프링 시큐리티에서는 SecurityFilterChain에서 사용하는 필터 중&amp;nbsp;&lt;br /&gt;&lt;b&gt;UsernamePasswordAuthenticationFilter&lt;/b&gt;를 통해 인증 처리&lt;br /&gt;-&amp;gt; 인증이 실패할 경우, 로그인 폼이라는 화면을 보내는 역할&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;*UsernamePasswordAuthenticationFilter&lt;/b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;를 통한&amp;nbsp;&lt;/span&gt;인증 수행 과정 (순서대로)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1157&quot; data-origin-height=&quot;611&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J2CkE/dJMb996RRTO/K2NwIizC3SXZ4VdoSTS050/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J2CkE/dJMb996RRTO/K2NwIizC3SXZ4VdoSTS050/img.png&quot; data-alt=&quot;UsernamePasswordAuthenticationFilter를 통한 인증 과정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J2CkE/dJMb996RRTO/K2NwIizC3SXZ4VdoSTS050/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ2CkE%2FdJMb996RRTO%2FK2NwIizC3SXZ4VdoSTS050%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;1157&quot; height=&quot;611&quot; data-origin-width=&quot;1157&quot; data-origin-height=&quot;611&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;UsernamePasswordAuthenticationFilter를 통한 인증 과정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클라이언트로부터 요청을 받으면, 서블릿 필터에서 SecurityFilterChain으로 작업이 위임되고&lt;br /&gt;그 증 UsernamePasswordAuthenticationFilter(위 그림의 AuthenticationFilter)에서 인증 처리&lt;/li&gt;
&lt;li&gt;UsernamePasswordAuthenticationFilter는 요청 객체(HttpServletRequest)에서 &lt;br /&gt;username과 password를 추출하여 토큰 생성&lt;/li&gt;
&lt;li&gt;생성한 토큰을 AuthenticationManager(구현체로 ProviderManager 사용)에 전달&lt;/li&gt;
&lt;li&gt;ProviderManager가 인증을 위해 AuthenticationProvider로 토큰 전달&lt;/li&gt;
&lt;li&gt;AuthenticationProvider가 토큰의 정보를 UserDetailsService에 저장&lt;/li&gt;
&lt;li&gt;UserDetailsService가 전달받은 정보를 통해, 데이터베이스에서 일치하는 사용자를 찾아 UserDetails 객체 생성&lt;/li&gt;
&lt;li&gt;생성된 UserDetails 객체는 AuthenticationProvider로 전달되는데, &lt;br /&gt;AuthenticationProvider에서 인증을 수행하여 성공하면 ProviderManager로 권한을 담은 토큰 전달&lt;/li&gt;
&lt;li&gt;ProviderManager가 검증된 토큰을 AUthenticationFilter로 전달&lt;/li&gt;
&lt;li&gt;AuthenticationFilter가 검증된 토큰을 SecurityContextHolder에 있는 SecurityContext에 저장&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초</category>
      <category>Java</category>
      <category>spring boot</category>
      <category>Spring Security</category>
      <category>STS4</category>
      <category>스프링 부트</category>
      <category>스프링 시큐리티</category>
      <category>인가</category>
      <category>인증</category>
      <category>자바</category>
      <category>접근 주체</category>
      <author>개발학생</author>
      <guid isPermaLink="true">https://keep-programming-study.tistory.com/195</guid>
      <comments>https://keep-programming-study.tistory.com/195#entry195comment</comments>
      <pubDate>Thu, 26 Mar 2026 19:57:12 +0900</pubDate>
    </item>
    <item>
      <title>스프링부트 핵심 가이드(장정우 지음) - 서버 간 통신 2: WebClient</title>
      <link>https://keep-programming-study.tistory.com/194</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*책 내용과 다르게, 다음과 같은 환경에서 프로젝트 생성&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Windows11(윈도우 11) 환경&lt;/li&gt;
&lt;li&gt;자바 JDK 17 버전 설치&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://yungenie.tistory.com/11&quot;&gt;https://yungenie.tistory.com/11&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1773827648705&quot; style=&quot;color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fsCfm/hyZjxqU4L2/XdX4MUTaSK5BaU7JmJjh90/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/nM72i/hyZjwZQPen/CQONWbqc5xGfLYajkBk8o0/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/JRE6h/hyZjgihxEK/k0VWPB43AkNJ8rLvVcy1M0/img.png?width=2762&amp;amp;height=2094&amp;amp;face=0_0_2762_2094&quot; data-og-url=&quot;https://yungenie.tistory.com/11&quot; data-og-source-url=&quot;https://yungenie.tistory.com/11&quot; data-og-host=&quot;yungenie.tistory.com&quot; data-og-description=&quot;자바 개발 도구 설치 방법에 대해서 알아보겠습니다. Java17은 LTS(Long Term Support : 장기 지원) 릴리즈로 1년 후까지 기술 지원 및 버그를 개선한 서비스를 제공받을 수 있습니다. 업데이트 버전을 꾸&quot; data-og-title=&quot;[Java] 차근차근 Java 설치하기 (JDK17, Window 11)&quot; data-og-type=&quot;article&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://yungenie.tistory.com/11&quot; data-source-url=&quot;https://yungenie.tistory.com/11&quot;&gt;
&lt;div style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fsCfm/hyZjxqU4L2/XdX4MUTaSK5BaU7JmJjh90/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/nM72i/hyZjwZQPen/CQONWbqc5xGfLYajkBk8o0/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/JRE6h/hyZjgihxEK/k0VWPB43AkNJ8rLvVcy1M0/img.png?width=2762&amp;amp;height=2094&amp;amp;face=0_0_2762_2094');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;[Java] 차근차근 Java 설치하기 (JDK17, Window 11)&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;자바 개발 도구 설치 방법에 대해서 알아보겠습니다. Java17은 LTS(Long Term Support : 장기 지원) 릴리즈로 1년 후까지 기술 지원 및 버그를 개선한 서비스를 제공받을 수 있습니다. 업데이트 버전을 꾸&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;yungenie.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스프링 부트 4.31.0 사용 - STS(Spring Tool Suite) 설치(Spring Tools for Eclipse -&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://spring.io/tools&quot;&gt;https://spring.io/tools&lt;/a&gt;)&lt;br /&gt;=&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://priming.tistory.com/147&quot;&gt;https://priming.tistory.com/147&lt;/a&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;참고&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1773827648706&quot; style=&quot;color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bylxoe/hyZjqL6ykw/At1UjkxTL5MxNIN6mN8Dhk/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/c4WuCh/hyZjyDk2Tg/jLlvw1t5ipklCwRzmvXHkK/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/vUGiq/hyZjaoN1DM/bbtYqu3fqKATJY2ztMT1a1/img.png?width=938&amp;amp;height=621&amp;amp;face=0_0_938_621&quot; data-og-url=&quot;https://priming.tistory.com/147&quot; data-og-source-url=&quot;https://priming.tistory.com/147&quot; data-og-host=&quot;priming.tistory.com&quot; data-og-description=&quot;STS란?Spring Tool Suite(STS)는 스프링 프로젝트를 생성하고, 개발할 수 있게 해주는 도구입니다.&amp;nbsp;STS 설치 과정에 대해 설명드리겠습니다.&amp;nbsp;설치 파일 다운로드STS 공식 사이트에서 설치 파일을 다운로&quot; data-og-title=&quot;[Windows] Spring Tool Suite 4(STS 4) 다운로드 및 설치&quot; data-og-type=&quot;article&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://priming.tistory.com/147&quot; data-source-url=&quot;https://priming.tistory.com/147&quot;&gt;
&lt;div style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bylxoe/hyZjqL6ykw/At1UjkxTL5MxNIN6mN8Dhk/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/c4WuCh/hyZjyDk2Tg/jLlvw1t5ipklCwRzmvXHkK/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/vUGiq/hyZjaoN1DM/bbtYqu3fqKATJY2ztMT1a1/img.png?width=938&amp;amp;height=621&amp;amp;face=0_0_938_621');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;[Windows] Spring Tool Suite 4(STS 4) 다운로드 및 설치&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;STS란?Spring Tool Suite(STS)는 스프링 프로젝트를 생성하고, 개발할 수 있게 해주는 도구입니다.&amp;nbsp;STS 설치 과정에 대해 설명드리겠습니다.&amp;nbsp;설치 파일 다운로드STS 공식 사이트에서 설치 파일을 다운로&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;priming.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2ne97/btsPgUiDCuF/eVBIrd1OJpcnARSiekch31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2ne97/btsPgUiDCuF/eVBIrd1OJpcnARSiekch31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2ne97/btsPgUiDCuF/eVBIrd1OJpcnARSiekch31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2ne97%2FbtsPgUiDCuF%2FeVBIrd1OJpcnARSiekch31%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;1051&quot; height=&quot;634&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;634&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;MySQL&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;Community Server&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;8.0.42 설치&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://dev.mysql.com/downloads/mysql/&quot;&gt;https://dev.mysql.com/downloads/mysql/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1773827648707&quot; style=&quot;color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/QP20c/hyZjCyYfQt/fudyPJf3QzC310TJECw8G1/img.jpg?width=841&amp;amp;height=244&amp;amp;face=0_0_841_244&quot; data-og-url=&quot;https://dev.mysql.com/downloads/mysql/&quot; data-og-source-url=&quot;https://dev.mysql.com/downloads/mysql/&quot; data-og-host=&quot;dev.mysql.com&quot; data-og-description=&quot;Select Version: 9.3.0 Innovation 8.4.5 LTS 8.0.42 Select Operating System: Select Operating System&amp;hellip; Microsoft Windows Ubuntu Linux Debian Linux SUSE Linux Enterprise Server Red Hat Enterprise Linux / Oracle Linux Fedora Linux - Generic Oracle Solaris mac&quot; data-og-title=&quot;MySQL :: Download MySQL Community Server&quot; data-og-type=&quot;website&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://dev.mysql.com/downloads/mysql/&quot; data-source-url=&quot;https://dev.mysql.com/downloads/mysql/&quot;&gt;
&lt;div style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/QP20c/hyZjCyYfQt/fudyPJf3QzC310TJECw8G1/img.jpg?width=841&amp;amp;height=244&amp;amp;face=0_0_841_244');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;MySQL :: Download MySQL Community Server&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Select Version: 9.3.0 Innovation 8.4.5 LTS 8.0.42 Select Operating System: Select Operating System&amp;hellip; Microsoft Windows Ubuntu Linux Debian Linux SUSE Linux Enterprise Server Red Hat Enterprise Linux / Oracle Linux Fedora Linux - Generic Oracle Solaris mac&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;dev.mysql.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;811&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btOcwO/btsPrnefLO2/pUhBPkOVmzbMTaKK1r5sG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btOcwO/btsPrnefLO2/pUhBPkOVmzbMTaKK1r5sG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btOcwO/btsPrnefLO2/pUhBPkOVmzbMTaKK1r5sG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtOcwO%2FbtsPrnefLO2%2FpUhBPkOVmzbMTaKK1r5sG0%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;1272&quot; height=&quot;811&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;811&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;Gradle&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;**STS에서 Gradle 프로젝트 생성한 과정&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K3amN/btsPiv4lwJt/uJutkklzbXbReULQxgZ8y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K3amN/btsPiv4lwJt/uJutkklzbXbReULQxgZ8y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K3amN/btsPiv4lwJt/uJutkklzbXbReULQxgZ8y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK3amN%2FbtsPiv4lwJt%2FuJutkklzbXbReULQxgZ8y1%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;914&quot; height=&quot;690&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;690&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;582&quot; data-origin-height=&quot;774&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m9kKL/btsPhOpIZXk/EXjrrKX55fmvxR1bktggSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m9kKL/btsPhOpIZXk/EXjrrKX55fmvxR1bktggSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m9kKL/btsPhOpIZXk/EXjrrKX55fmvxR1bktggSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm9kKL%2FbtsPhOpIZXk%2FEXjrrKX55fmvxR1bktggSK%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;582&quot; height=&quot;774&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;774&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;657&quot; data-origin-height=&quot;782&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kH6Gt/btsPjkt76G1/oLBQL2FY5kFBr6K3pyVy0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kH6Gt/btsPjkt76G1/oLBQL2FY5kFBr6K3pyVy0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kH6Gt/btsPjkt76G1/oLBQL2FY5kFBr6K3pyVy0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkH6Gt%2FbtsPjkt76G1%2FoLBQL2FY5kFBr6K3pyVy0K%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;657&quot; height=&quot;782&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;782&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;*** 함께 보면 좋은 글&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/165&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.07.11 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링 부트 핵심 가이드(장정우 지음) - 개발에 앞서 알면 좋은 기초 지식&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1772878390416&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;스프링 부트 핵심 가이드(장정우 지음) - 개발에 앞서 알면 좋은 기초 지식&quot; data-og-description=&quot;1. 서버 간 통신마이크로서비스 아키텍처에서 한 서버가 다른 서버에 통신을 요청하는 것을 의미-&amp;gt; 한 대는 서버/다른 한 대는 클라이언트가 됨 가장 많이 사용되는 방식은 HTTP/HTTPS 방식(TCP/IP, SOA&quot; data-og-host=&quot;keep-programming-study.tistory.com&quot; data-og-source-url=&quot;https://keep-programming-study.tistory.com/165&quot; data-og-url=&quot;https://keep-programming-study.tistory.com/165&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/brtr5t/dJMb9kT0W0f/FKXosB99izC8W6NGcqGE00/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/bp4rpi/dJMb9cBF4kv/QpNS9Kmfkn2zG4Wh1Aw4e0/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/wXrgU/dJMb9lL9wkm/YRveOLLMIZ1IvUrPSrJBQK/img.png?width=1239&amp;amp;height=564&amp;amp;face=0_0_1239_564&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/165&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://keep-programming-study.tistory.com/165&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/brtr5t/dJMb9kT0W0f/FKXosB99izC8W6NGcqGE00/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/bp4rpi/dJMb9cBF4kv/QpNS9Kmfkn2zG4Wh1Aw4e0/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/wXrgU/dJMb9lL9wkm/YRveOLLMIZ1IvUrPSrJBQK/img.png?width=1239&amp;amp;height=564&amp;amp;face=0_0_1239_564');&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;1. 서버 간 통신마이크로서비스 아키텍처에서 한 서버가 다른 서버에 통신을 요청하는 것을 의미-&amp;gt; 한 대는 서버/다른 한 대는 클라이언트가 됨 가장 많이 사용되는 방식은 HTTP/HTTPS 방식(TCP/IP, SOA&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;keep-programming-study.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://keep-programming-study.tistory.com/193&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2026.03.03 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링부트 핵심 가이드(장정우 지음) - 서버 간 통신 1: RestTemplate&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1772878402886&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;스프링부트 핵심 가이드(장정우 지음) - 서버 간 통신 1: RestTemplate&quot; data-og-description=&quot;*** 함께 보면 좋은 글2025.07.11 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링 부트 핵심 가이드(장정우 지음) - 개발에 앞서 알면 좋은 기초 지식 한 대는 서버/다른 한 &quot; data-og-host=&quot;keep-programming-study.tistory.com&quot; data-og-source-url=&quot;https://keep-programming-study.tistory.com/193&quot; data-og-url=&quot;https://keep-programming-study.tistory.com/193&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/POwdN/dJMb8XR3B5o/ACES1eqKE7ixTYCFAuTUg0/img.png?width=800&amp;amp;height=315&amp;amp;face=0_0_800_315,https://scrap.kakaocdn.net/dn/cW24mN/dJMb8YXJs9T/abjrIJPrSe3VKicKcsCjsK/img.png?width=800&amp;amp;height=315&amp;amp;face=0_0_800_315,https://scrap.kakaocdn.net/dn/JBKc2/dJMb8VNtkvs/Az6fucuyBz4TkHvtdmPknk/img.png?width=1187&amp;amp;height=468&amp;amp;face=0_0_1187_468&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/193&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://keep-programming-study.tistory.com/193&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/POwdN/dJMb8XR3B5o/ACES1eqKE7ixTYCFAuTUg0/img.png?width=800&amp;amp;height=315&amp;amp;face=0_0_800_315,https://scrap.kakaocdn.net/dn/cW24mN/dJMb8YXJs9T/abjrIJPrSe3VKicKcsCjsK/img.png?width=800&amp;amp;height=315&amp;amp;face=0_0_800_315,https://scrap.kakaocdn.net/dn/JBKc2/dJMb8VNtkvs/Az6fucuyBz4TkHvtdmPknk/img.png?width=1187&amp;amp;height=468&amp;amp;face=0_0_1187_468');&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;스프링부트 핵심 가이드(장정우 지음) - 서버 간 통신 1: RestTemplate&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;*** 함께 보면 좋은 글2025.07.11 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링 부트 핵심 가이드(장정우 지음) - 개발에 앞서 알면 좋은 기초 지식 한 대는 서버/다른 한&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;keep-programming-study.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. WebClient 개요&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스프링부트 최신 버전에서는 RestTemplate의 지원이 중단되어, WebClient를 사용할 것을 권고&lt;/li&gt;
&lt;li&gt;Spring WebFlux에서 HTTP 요청을 수행하는 클라이언트로 제공&lt;/li&gt;
&lt;li&gt;리액터(Reactor) 기반으로 동작하는 API로, 스레드와 동시성 문제를 벗어나 비동기 형식으로 사용 가능&lt;br /&gt;-&amp;gt; 리액터(Reactor):&amp;nbsp;&lt;b&gt;Java에서 리액티브 프로그래밍을 구현한 라이브러리&lt;/b&gt; 중 하나로, 이벤트 스트림(event stream) 기반으로 &lt;b&gt;데이터의 흐름을 비동기적으로 처리&lt;/b&gt;할 수 있도록 도움&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) WebClient의 주요 특징&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) 논블로킹(Non-Bolcking) I/O 지원&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요청을 보낼 때 서버 스레드가 기다리지 않고, I/O 작업이 끝날 때만 알림을 받아 처리&lt;/li&gt;
&lt;li&gt;동시에 많은 요청을 처리할 수 있고, 스레드 점유가 적어 서버 리소스를 아낄 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1772879754572&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;WebClient.create()
    .get()
    .uri(&quot;https://api.example.com/data&quot;)
    .retrieve()
    .bodyToMono(String.class); // 요청 보내고 바로 반환, 나중에 결과 받음&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) 리액티브 스트림(Reactive Streans)의 백 프레셔(Back Pressure) 지원&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;609&quot; data-start=&quot;544&quot; data-section-id=&quot;y6njuw&quot;&gt;데이터가 너무 빨리 들어오면 소비자가 처리 속도를 따라갈 수 있도록 조절할 수 있음&lt;/li&gt;
&lt;li data-end=&quot;659&quot; data-start=&quot;610&quot; data-section-id=&quot;1if8yvs&quot;&gt;한쪽이 너무 빨리 데이터를 보내서 시스템이 다운되는 것을 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(3) 적은 하드웨어 리소스로 동시성 지원&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;811&quot; data-start=&quot;757&quot; data-section-id=&quot;1j68z4m&quot;&gt;많은 요청을 동시에 처리할 수 있으며, 그러면서도 스레드를 많이 쓰지 않음&lt;/li&gt;
&lt;li data-end=&quot;916&quot; data-start=&quot;812&quot; data-section-id=&quot;htlmfi&quot;&gt;&lt;b&gt;비유: &lt;/b&gt;기존 스레드 방식은 1000개 요청 처리하려면 1000개 스레드 필요 &amp;rarr; 메모리 부담&lt;br /&gt;WebClient는 10~20개 스레드로 1000개 요청 처리 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(4) 함수형 API 지원&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 다루는 과정에서 map, flatMap, filter 같은 함수형 연산을 자연스럽게 지원&lt;/li&gt;
&lt;li&gt;코드가 깔끔하고, 데이터 처리 로직 쉽게 연결 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1772879739734&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;webClient.get()
    .uri(&quot;/users&quot;)
    .retrieve()
    .bodyToFlux(User.class)
    .filter(user -&amp;gt; user.isActive())
    .map(User::getName);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(5) 동기, 비동기 상호작용 지원&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WebClient는 Mono/Flux를 반환하기 때문에 기본적으로 비동기 지원, .block()을 사용하여 동기 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1772879704207&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 비동기
Mono&amp;lt;String&amp;gt; result = webClient.get().uri(&quot;/data&quot;).retrieve().bodyToMono(String.class);

// 동기
String data = result.block(); // 결과 올 때까지 기다림&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(6) 스트리밍 지원&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버가 데이터를 조각(스트림)으로 보내도 점진적으로 처리 가능&lt;/li&gt;
&lt;li&gt;큰 데이터 파일, 실시간 이벤트, SSE(Server-Sent Events) 처리에 적합&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1772879772403&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Flux&amp;lt;ServerEvent&amp;gt; eventStream = webClient.get()
    .uri(&quot;/events&quot;)
    .retrieve()
    .bodyToFlux(ServerEvent.class);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) WebClient 구성 방법&amp;nbsp;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 프로젝트에서, build.gradle의 dependencies에 WebFlux 의존성 추가&lt;br /&gt;-&amp;gt; 저장한 후 우클릭 한 다음 Gradle =&amp;gt; Refresh Gradle Project 클릭&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1773828104478&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
    ... 생략 ...
	
	/// WebClient 구성을 위한 WebFlux 모듈 의존성 추가
	implementation 'org.springframework.boot:spring-boot-starter-webflux'
}&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;2. WebClient 사용하기&amp;nbsp;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) WebClient 구현(코드 작성)&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) GET 요청 구현: WebClientService.java 생성&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;defaultHeader(): WebClient의 기본 헤더 설정&lt;/li&gt;
&lt;li&gt;defaultCookie(): WebClient의 기본 쿠키 설정&lt;/li&gt;
&lt;li&gt;defaultUriVariable(): WebClient의 기본 URI 확장값 설정&lt;/li&gt;
&lt;li&gt;filter(): WebClient에서 발생하는 요청에 대한 필터 설정&amp;nbsp;&lt;/li&gt;
&lt;li&gt;복제: WebClient clone = webClient.mutate().build();&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1773829372376&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.service;


import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriBuilder;

import reactor.core.publisher.Mono;

@Service
public class WebClientService {
	 /**
     * 단순 GET 요청 
     * - baseUrl: http://localhost:9090
     * - 기본 Header: Content-Type = application/json
     * - /api/v1/crud-api 엔드포인트 호출 후 문자열 응답 반환
     */
	public String getName() {
		WebClient webClient = WebClient.builder()
				.baseUrl(&quot;http://localhost:9090&quot;)
				.defaultHeader(HttpHeaders.CONTENT_TYPE,  MediaType.APPLICATION_JSON_VALUE)
				.build();
		
		return webClient.get()
				.uri(&quot;/api/v1/crud-api&quot;)
				.retrieve()
				.bodyToMono(String.class)
				.block();
	}
	
	/**
     * PathVariable을 포함한 GET 요청 
     * - /api/v1/crud-api/{name} 엔드포인트 호출
     * - {name} 자리에 &quot;Flature&quot; 값 삽입
     * - 응답을 ResponseEntity&amp;lt;String&amp;gt;으로 받아 Body 반환
     */
	public String getNameWithPathVariable() {
		WebClient webClient = WebClient.create(&quot;http://localhost:9090&quot;);
		
		ResponseEntity&amp;lt;String&amp;gt; responseEntity = webClient.get()
				.uri(uriBuilder -&amp;gt; uriBuilder.path(&quot;/api/v1/crud-api/{name}&quot;)
						.build(&quot;Flature&quot;))
				.retrieve().toEntity(String.class).block();
		
		return responseEntity.getBody();
	}
	
	/**
	 * Query Parameter를 포함한 GET 요청 
	 * - 요청 대상: http://localhost:9090/api/v1/crud-api?name=Flature
	 * - UriBuilder를 사용해 queryParam(&quot;name&quot;, &quot;Flature&quot;) 추가
	 * - exchangeToMono()로 응답 처리:
	 *   - 상태 코드가 200 OK일 경우 Body를 String으로 반환
	 *   - 그 외에는 예외를 발생시킴
	 * - block()으로 Mono를 동기적으로 실행해 최종 문자열 응답 반환
	 */
	public String getNameWithParamenter() {
		WebClient webClient = WebClient.create(&quot;http://localhost:9090&quot;);
		
		return webClient.get().uri(uriBuilder -&amp;gt; uriBuilder.path(&quot;/api/v1/crud-api&quot;)
				.queryParam(&quot;name&quot;, &quot;Flature&quot;)
				.build())
			.exchangeToMono(clientResponse -&amp;gt; {
				if (clientResponse.statusCode().equals(HttpStatus.OK)) {
					return clientResponse.bodyToMono(String.class);
				} else {
					return clientResponse.createException().flatMap(Mono::error);
				}
			})
			.block();
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) POST 요청 구현: WebClientService.java에 코드 추가&lt;/h4&gt;
&lt;pre id=&quot;code_1773830333544&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.service;


import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriBuilder;

import com.example.demo.jpa.data.dto.MemberDto;

import reactor.core.publisher.Mono;

@Service
public class WebClientService {
	... 생략 ...
    	/**
	 * POST 요청 (Query Parameter + Body)
	 * - 요청 대상: http://localhost:9090/api/v1/crud-api
	 * - Query Parameter: name, email, organization
	 * - Body: MemberDto 객체(JSON으로 직렬화)
	 * - 응답: MemberDto를 ResponseEntity로 받아 반환
	 */
	public ResponseEntity&amp;lt;MemberDto&amp;gt; postWithParamAndBody() {
	    WebClient webClient = WebClient.builder()
	            .baseUrl(&quot;http://localhost:9090&quot;)
	            .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
	            .build();
	    
	    MemberDto memberDto = new MemberDto();
	    memberDto.setName(&quot;flature!!&quot;);
	    memberDto.setEmail(&quot;flature@gmail.com&quot;);
	    memberDto.setOrganization(&quot;Around Hub&quot;);
	    
	    return webClient.post().uri(uriBuilder -&amp;gt; uriBuilder.path(&quot;/api/v1/crud-api&quot;)
	            .queryParam(&quot;name&quot;, &quot;Flature&quot;)
	            .queryParam(&quot;email&quot;, &quot;flature@wikibooks.co.kr&quot;)
	            .queryParam(&quot;organization&quot;, &quot;wikibooks&quot;)
	            .build())
	        .bodyValue(memberDto)
	        .retrieve()
	        .toEntity(MemberDto.class)
	        .block();
	}


	/**
	 * POST 요청 (Custom Header + Body)
	 * - 요청 대상: http://localhost:9090/api/v1/crud-api/add-header
	 * - Body: MemberDto 객체(JSON으로 직렬화)
	 * - Header: &quot;my-header&quot; = &quot;WkBooks API&quot;
	 * - 응답: MemberDto를 ResponseEntity로 받아 반환
	 */
	public ResponseEntity&amp;lt;MemberDto&amp;gt; postWithHeader() {
	    WebClient webClient = WebClient.builder()
	            .baseUrl(&quot;http://localhost:9090&quot;)
	            .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
	            .build();
	    
	    MemberDto memberDto = new MemberDto();
	    memberDto.setName(&quot;flature!!&quot;);
	    memberDto.setEmail(&quot;flature@gmail.com&quot;);
	    memberDto.setOrganization(&quot;Around Hub&quot;);
	    
	    return webClient
	            .post()
	            .uri(uriBuilder -&amp;gt; uriBuilder.path(&quot;/api/v1/crud-api/add-header&quot;).build())
	            .bodyValue(memberDto)
	            .header(&quot;my-header&quot;, &quot;WkBooks API&quot;)
	            .retrieve()
	            .toEntity(MemberDto.class)
	            .block();
	}

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초</category>
      <category>Java</category>
      <category>REACTOR</category>
      <category>restTemplate</category>
      <category>RestTemplate 지원 중단</category>
      <category>spring boot</category>
      <category>spring webflux</category>
      <category>WebClient</category>
      <category>서버 간 통신</category>
      <category>스프링부트</category>
      <category>자바</category>
      <author>개발학생</author>
      <guid isPermaLink="true">https://keep-programming-study.tistory.com/194</guid>
      <comments>https://keep-programming-study.tistory.com/194#entry194comment</comments>
      <pubDate>Wed, 18 Mar 2026 19:39:55 +0900</pubDate>
    </item>
    <item>
      <title>스프링부트 핵심 가이드(장정우 지음) - 서버 간 통신 1: RestTemplate</title>
      <link>https://keep-programming-study.tistory.com/193</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;*** 함께 보면 좋은 글&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/165&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.07.11 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링 부트 핵심 가이드(장정우 지음) - 개발에 앞서 알면 좋은 기초 지식&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1771573367102&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;스프링 부트 핵심 가이드(장정우 지음) - 개발에 앞서 알면 좋은 기초 지식&quot; data-og-description=&quot;1. 서버 간 통신마이크로서비스 아키텍처에서 한 서버가 다른 서버에 통신을 요청하는 것을 의미-&amp;gt; 한 대는 서버/다른 한 대는 클라이언트가 됨 가장 많이 사용되는 방식은 HTTP/HTTPS 방식(TCP/IP, SOA&quot; data-og-host=&quot;keep-programming-study.tistory.com&quot; data-og-source-url=&quot;https://keep-programming-study.tistory.com/165&quot; data-og-url=&quot;https://keep-programming-study.tistory.com/165&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eDCHwq/dJMb81GTPJC/Kv6NRjCwI4HTqOijmBxGbK/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/oz99a/dJMb85WPUz4/RP4adTqZx2Vo2ASpVz7nW0/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/mCIBy/dJMb9aKBKpQ/saV3k1SpYLChlaHW7JY5Nk/img.png?width=1239&amp;amp;height=564&amp;amp;face=0_0_1239_564&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/165&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://keep-programming-study.tistory.com/165&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eDCHwq/dJMb81GTPJC/Kv6NRjCwI4HTqOijmBxGbK/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/oz99a/dJMb85WPUz4/RP4adTqZx2Vo2ASpVz7nW0/img.png?width=800&amp;amp;height=364&amp;amp;face=0_0_800_364,https://scrap.kakaocdn.net/dn/mCIBy/dJMb9aKBKpQ/saV3k1SpYLChlaHW7JY5Nk/img.png?width=1239&amp;amp;height=564&amp;amp;face=0_0_1239_564');&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;1. 서버 간 통신마이크로서비스 아키텍처에서 한 서버가 다른 서버에 통신을 요청하는 것을 의미-&amp;gt; 한 대는 서버/다른 한 대는 클라이언트가 됨 가장 많이 사용되는 방식은 HTTP/HTTPS 방식(TCP/IP, SOA&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;keep-programming-study.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/167&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.07.16 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링 부트 핵심 가이드(장정우 지음) - REST API 명세를 문서화하는 방법(Swagger), 로깅 라이브러리(Logback)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1771573372948&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;스프링 부트 핵심 가이드(장정우 지음) - REST API 명세를 문서화하는 방법(Swagger), 로깅 라이브러리&quot; data-og-description=&quot;*책 내용과 다르게, 다음과 같은 환경에서 프로젝트 생성 Windows11(윈도우 11) 환경자바 JDK 17 버전 설치 https://yungenie.tistory.com/11 [Java] 차근차근 Java 설치하기 (JDK17, Window 11)자바 개발 도구 설치 방법&quot; data-og-host=&quot;keep-programming-study.tistory.com&quot; data-og-source-url=&quot;https://keep-programming-study.tistory.com/167&quot; data-og-url=&quot;https://keep-programming-study.tistory.com/167&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c7PhYL/dJMb9jgtPEh/McVsDvXkSNKBFDhj1zV3AK/img.png?width=793&amp;amp;height=741&amp;amp;face=0_0_793_741,https://scrap.kakaocdn.net/dn/U9EYR/dJMb9jgtPEg/6xSFUdsZoF6wALepeTY3I0/img.png?width=793&amp;amp;height=741&amp;amp;face=0_0_793_741,https://scrap.kakaocdn.net/dn/oK47b/dJMb9dHkFTJ/UxkVOeWWJXOwhFQqlZCYnK/img.png?width=1272&amp;amp;height=811&amp;amp;face=0_0_1272_811&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/167&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://keep-programming-study.tistory.com/167&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c7PhYL/dJMb9jgtPEh/McVsDvXkSNKBFDhj1zV3AK/img.png?width=793&amp;amp;height=741&amp;amp;face=0_0_793_741,https://scrap.kakaocdn.net/dn/U9EYR/dJMb9jgtPEg/6xSFUdsZoF6wALepeTY3I0/img.png?width=793&amp;amp;height=741&amp;amp;face=0_0_793_741,https://scrap.kakaocdn.net/dn/oK47b/dJMb9dHkFTJ/UxkVOeWWJXOwhFQqlZCYnK/img.png?width=1272&amp;amp;height=811&amp;amp;face=0_0_1272_811');&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;스프링 부트 핵심 가이드(장정우 지음) - REST API 명세를 문서화하는 방법(Swagger), 로깅 라이브러리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;*책 내용과 다르게, 다음과 같은 환경에서 프로젝트 생성 Windows11(윈도우 11) 환경자바 JDK 17 버전 설치 https://yungenie.tistory.com/11 [Java] 차근차근 Java 설치하기 (JDK17, Window 11)자바 개발 도구 설치 방법&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;keep-programming-study.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://keep-programming-study.tistory.com/53&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2023.11.12 - [자바(JAVA)/JSP 웹 프로그래밍 공부 (성낙현의 JSP 자바 웹 프로그래밍 참고)] - JAVA/JSP 16. 데이터베이스 - 커넥션 풀로 성능 개선, 간단한 쿼리 작성 및 실행&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1772529575739&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;JAVA/JSP 16. 데이터베이스 - 커넥션 풀로 성능 개선, 간단한 쿼리 작성 및 실행&quot; data-og-description=&quot;6. 커넥션 풀로 성능 개선웹은 클라이언트의 요청에 서버가 응답하는 구조&amp;rarr; Connection 객체 생성 때마다 네트워크 통신이 이뤄지며, 시간이 걸리는 작업들이 수반됨 == 시스템 성능에 큰 영향을 &quot; data-og-host=&quot;keep-programming-study.tistory.com&quot; data-og-source-url=&quot;https://keep-programming-study.tistory.com/53&quot; data-og-url=&quot;https://keep-programming-study.tistory.com/53&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/egQDjH/dJMb8U8ReCD/2uGKQgldDt9Iyh03Xl7DR0/img.png?width=800&amp;amp;height=266&amp;amp;face=0_0_800_266,https://scrap.kakaocdn.net/dn/Rsded/dJMb84p6jVl/8GheKFUpXDLo41iWgIaKF1/img.png?width=800&amp;amp;height=266&amp;amp;face=0_0_800_266,https://scrap.kakaocdn.net/dn/mb7nP/dJMb83kqwBP/kkkMQmSYNhoi4IwJ1bJtI1/img.png?width=1260&amp;amp;height=702&amp;amp;face=0_0_1260_702&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/53&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://keep-programming-study.tistory.com/53&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/egQDjH/dJMb8U8ReCD/2uGKQgldDt9Iyh03Xl7DR0/img.png?width=800&amp;amp;height=266&amp;amp;face=0_0_800_266,https://scrap.kakaocdn.net/dn/Rsded/dJMb84p6jVl/8GheKFUpXDLo41iWgIaKF1/img.png?width=800&amp;amp;height=266&amp;amp;face=0_0_800_266,https://scrap.kakaocdn.net/dn/mb7nP/dJMb83kqwBP/kkkMQmSYNhoi4IwJ1bJtI1/img.png?width=1260&amp;amp;height=702&amp;amp;face=0_0_1260_702');&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;JAVA/JSP 16. 데이터베이스 - 커넥션 풀로 성능 개선, 간단한 쿼리 작성 및 실행&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;6. 커넥션 풀로 성능 개선웹은 클라이언트의 요청에 서버가 응답하는 구조&amp;rarr; Connection 객체 생성 때마다 네트워크 통신이 이뤄지며, 시간이 걸리는 작업들이 수반됨 == 시스템 성능에 큰 영향을&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;keep-programming-study.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;0. 서버 간 통신 개요&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최근에 개발되는 서비스들은 MSA(마이크로서비스 아키텍처)를 주로 채택&lt;br /&gt;: 애플리케이션이 가지고 있는 기능(서비스)이 하나의 비즈니스 범위만 가지는 형태로,&amp;nbsp;&lt;br /&gt;&amp;nbsp; 각 애플리케이션은 자신이 가진 기능을 API로 외부에 노출하고 다른 서버가 호출해서 사용할 수 있도록 구성되므로,&lt;br /&gt;&amp;nbsp; 각 서버가 다른 서버의 클라이언트가 되는 경우도 많음&lt;/li&gt;
&lt;li&gt;서버 간 통신은 이러한 트렌드에 맞춰 다른 서버로 웹 요청을 보내고 응답을 받을 수 있게 도와주는, &lt;br /&gt;&lt;b&gt;RestTemplate&lt;/b&gt;과 &lt;b&gt;WebClient&lt;/b&gt;로 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. RestTemplate 개요&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스프링에서 HTTP 통신 기능을 손쉽게 사용할 수 있도록 설계한 템플릿으로, HTTP 서버와의 통신을 단순화함&lt;/li&gt;
&lt;li&gt;RestTemplate 사용 시 RESTful 원칙을 따르는 서비스를 편리하게 만들 수 있음&lt;/li&gt;
&lt;li&gt;기본적으로 동기 방식으로 처리되는데, &lt;b&gt;비동기 방식을 원한다면 AsyncRestTemplate 사용&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;현업에서 많이 쓰이지만, &lt;span style=&quot;color: #ee2323;&quot;&gt;지원 중단(deprecated)된 상태이므로 WebClient 방식도 함께 알아두는 것을 권장&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) RestTemplate 특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTP 프로토콜의 메서드에 맞는 여러 메서드 제공&lt;/li&gt;
&lt;li&gt;RESTful 형식을 갖춘 템플릿&lt;/li&gt;
&lt;li&gt;HTTP 요청 후 JSON, XML, 문자열 등 다양한 형식의 응답을 받을 수 있음&lt;/li&gt;
&lt;li&gt;블로킹(blocking) I/O 기반의 동기 방식 사용&lt;br /&gt;: &lt;span&gt;프로그램이 I/O(입출력) 작업을 요청하면 &lt;b&gt;그 자리에서 멈추고 결과가 올 때까지 기다린 뒤&lt;/b&gt; 다음 작업을 진행&lt;br /&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;(&lt;b&gt;요청 &amp;rarr; 대기 &amp;rarr; 결과 &amp;rarr; 다음 실행&lt;/b&gt; 흐름)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;다른 API 호출 시 HTTP 헤더에 다양한 값 설정 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) RestTemplate 동작 구조&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1187&quot; data-origin-height=&quot;468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGcQOy/dJMcagLhYGM/VONZK86hZu4blDUgdxt2Y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGcQOy/dJMcagLhYGM/VONZK86hZu4blDUgdxt2Y0/img.png&quot; data-alt=&quot;RestTemplate 동작 방식 도식화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGcQOy/dJMcagLhYGM/VONZK86hZu4blDUgdxt2Y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGcQOy%2FdJMcagLhYGM%2FVONZK86hZu4blDUgdxt2Y0%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;1187&quot; height=&quot;468&quot; data-origin-width=&quot;1187&quot; data-origin-height=&quot;468&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RestTemplate 동작 방식 도식화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;그림에서 애플리케이션은 우리가 직접 작성하는 애플리케이션 코드 구현부를 의미하며,&lt;br /&gt;RestTemplate을 선언하고 URI와 HTTP 메서드, Body 등을 설정하는 역할을 함&lt;/li&gt;
&lt;li&gt;외부 API로 요청을 보내게 되면 RestTemplate에서 HttpMessageConverter를 통해 RequestEntity를 요청 메시지로 변환&lt;/li&gt;
&lt;li&gt;RestTemplate에서는 변환된 요청 메시지를 ClientHttpRequestFactory를 통해,&lt;br /&gt;ClientHttpRequest로 가져온 후 외부 API로 요청을 보냄&lt;/li&gt;
&lt;li&gt;외부에서 요청에 대한 응답을 받으면 RestTemplate는 ResponseErrorHandler로 오류를 확인한 후,&lt;br /&gt;오류가 있다면 ClientHttpResponse에서 응답 데이터를 처리&lt;/li&gt;
&lt;li&gt;받은 응답 데이터가 정상적이라면 다시 한 번 HttpMessageConverter를 거쳐 자바 객체로 변환한 다음,&lt;br /&gt;애플리케이션으로 반환&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) RestTemplate의 대표적인 메서드 정리&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 208px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 23.5659%; text-align: center; height: 21px;&quot;&gt;메서드&lt;/td&gt;
&lt;td style=&quot;width: 17.9844%; text-align: center; height: 21px;&quot;&gt;HTTP 형태&lt;/td&gt;
&lt;td style=&quot;width: 58.4496%; text-align: center; height: 21px;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.5659%; height: 17px;&quot;&gt;getForObject&lt;/td&gt;
&lt;td style=&quot;width: 17.9844%; height: 17px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 58.4496%; height: 17px;&quot;&gt;GET 형식으로 요청한 결과를 객체로 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.5659%; height: 17px;&quot;&gt;getForEntity&lt;/td&gt;
&lt;td style=&quot;width: 17.9844%; height: 17px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 58.4496%; height: 17px;&quot;&gt;GET 형식으로 요청한 결과를 ResponseEntity로 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.5659%; height: 17px;&quot;&gt;postForLocation&lt;/td&gt;
&lt;td style=&quot;width: 17.9844%; height: 17px;&quot;&gt;POST&lt;/td&gt;
&lt;td style=&quot;width: 58.4496%; height: 17px;&quot;&gt;POST 형식으로 요청한 결과를 헤더에 저장된 URI로 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.5659%; height: 17px;&quot;&gt;postForObject&lt;/td&gt;
&lt;td style=&quot;width: 17.9844%; height: 17px;&quot;&gt;POST&lt;/td&gt;
&lt;td style=&quot;width: 58.4496%; height: 17px;&quot;&gt;POST 형식으로 요청한 결과를 객체로 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.5659%; height: 17px;&quot;&gt;postForEntity&lt;/td&gt;
&lt;td style=&quot;width: 17.9844%; height: 17px;&quot;&gt;POST&lt;/td&gt;
&lt;td style=&quot;width: 58.4496%; height: 17px;&quot;&gt;POST 형식으로 요청한 결과를 ResponseEntity로 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.5659%; height: 17px;&quot;&gt;delete&lt;/td&gt;
&lt;td style=&quot;width: 17.9844%; height: 17px;&quot;&gt;DELETE&lt;/td&gt;
&lt;td style=&quot;width: 58.4496%; height: 17px;&quot;&gt;DELETE 형식으로 요청&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.5659%; height: 17px;&quot;&gt;put&lt;/td&gt;
&lt;td style=&quot;width: 17.9844%; height: 17px;&quot;&gt;PUT&lt;/td&gt;
&lt;td style=&quot;width: 58.4496%; height: 17px;&quot;&gt;PUT 형식으로 요청&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.5659%; height: 17px;&quot;&gt;patchForObject&lt;/td&gt;
&lt;td style=&quot;width: 17.9844%; height: 17px;&quot;&gt;PATCH&lt;/td&gt;
&lt;td style=&quot;width: 58.4496%; height: 17px;&quot;&gt;PATCH 형식으로 요청한 결과를 객체로 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.5659%; height: 17px;&quot;&gt;optionsForAllow&lt;/td&gt;
&lt;td style=&quot;width: 17.9844%; height: 17px;&quot;&gt;OPTIONS&lt;/td&gt;
&lt;td style=&quot;width: 58.4496%; height: 17px;&quot;&gt;해당 URI에서 지원하는 HTTP 메서드를 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.5659%; height: 17px;&quot;&gt;exchange&lt;/td&gt;
&lt;td style=&quot;width: 17.9844%; height: 17px;&quot;&gt;모두&lt;/td&gt;
&lt;td style=&quot;width: 58.4496%; height: 17px;&quot;&gt;HTTP 헤더를 임의로 추가할 수 있으며, 모든 메서드 형식에서 사용 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.5659%; height: 17px;&quot;&gt;execute&lt;/td&gt;
&lt;td style=&quot;width: 17.9844%; height: 17px;&quot;&gt;모두&lt;/td&gt;
&lt;td style=&quot;width: 58.4496%; height: 17px;&quot;&gt;요청과 응답에 대한 콜백을 수정할 수 있으며, 모든 메서드 형식에서 사용 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. RestTemplate 사용하기&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요청을 보낼 서버 용도로 별도 프로젝트를 하나 생성한 다음, 다른 프로젝트에서 RestTemplate을 통해 요청을 보내는 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 서버 프로젝트 생성: spring-boot-starter-web만 의존성에 추가&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;File -&amp;gt; new -&amp;gt; Spring Starter Project 클릭&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;591&quot; data-origin-height=&quot;783&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dR9SyB/dJMcagxJyS1/HTvri1AXQhh2cuZYRfcJo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dR9SyB/dJMcagxJyS1/HTvri1AXQhh2cuZYRfcJo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dR9SyB/dJMcagxJyS1/HTvri1AXQhh2cuZYRfcJo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdR9SyB%2FdJMcagxJyS1%2FHTvri1AXQhh2cuZYRfcJo0%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;591&quot; height=&quot;783&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;783&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;560&quot; data-origin-height=&quot;725&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2gOx5/dJMcagLiW5U/J5TVPbhJIv3111yo9B8llK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2gOx5/dJMcagLiW5U/J5TVPbhJIv3111yo9B8llK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2gOx5/dJMcagLiW5U/J5TVPbhJIv3111yo9B8llK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2gOx5%2FdJMcagLiW5U%2FJ5TVPbhJIv3111yo9B8llK%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;560&quot; height=&quot;725&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;725&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;581&quot; data-origin-height=&quot;773&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/097lz/dJMcagYOupk/bwLVeb4TFxI7EFkCSksK3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/097lz/dJMcagYOupk/bwLVeb4TFxI7EFkCSksK3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/097lz/dJMcagYOupk/bwLVeb4TFxI7EFkCSksK3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F097lz%2FdJMcagYOupk%2FbwLVeb4TFxI7EFkCSksK3K%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;581&quot; height=&quot;773&quot; data-origin-width=&quot;581&quot; data-origin-height=&quot;773&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 서버 프로젝트 설정 변경&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) application.properties에서 server.port 속성 추가&lt;/h4&gt;
&lt;pre id=&quot;code_1771669697124&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server.port=9090&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) MemberDto.java 생성&lt;/h4&gt;
&lt;pre id=&quot;code_1771670191673&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.dto;

public class MemberDto {
	private String name;
	private String email;
	private String organization;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getOrganization() {
		return organization;
	}
	public void setOrganization(String organization) {
		this.organization = organization;
	}
	
	@Override
	public String toString() {
		return &quot;MemberDto [name=&quot; + name + &quot;, email=&quot; + email + &quot;, organization=&quot; + organization + &quot;]&quot;;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(3) CrudController.java 생성&lt;/h4&gt;
&lt;pre id=&quot;code_1771670562840&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.controller;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.dto.MemberDto;

@RestController // REST API 컨트롤러임을 명시
@RequestMapping(&quot;/api/v1/crud-api&quot;) // 기본 URL 경로 설정
public class CrudController {
    
    @GetMapping
    public String getName() {
        // GET /api/v1/crud-api
        // 단순히 문자열 &quot;Flature&quot;를 반환
        return &quot;Flature&quot;;
    }
    
    @GetMapping(value=&quot;/{variable}&quot;)
    public String getVariable(@PathVariable String variable) {
        // GET /api/v1/crud-api/{variable}
        // URL 경로에 포함된 값을 그대로 반환
        return variable;
    }
    
    @GetMapping(&quot;/param&quot;)
    public String getNameWithParam(@RequestParam String name) {
        // GET /api/v1/crud-api/param?name=값
        // 쿼리 파라미터로 전달된 name을 사용해 인사 메시지 반환
        return &quot;Hello. &quot;+ name + &quot;!&quot;;
    }
    
    @PostMapping
    public ResponseEntity&amp;lt;MemberDto&amp;gt; getMember(
            @RequestBody MemberDto dto, // 요청 Body(JSON)로 전달된 MemberDto 객체
            @RequestParam String name, // 쿼리 파라미터 name
            @RequestParam String email, // 쿼리 파라미터 email
            @RequestParam String organization // 쿼리 파라미터 organization
    ) {
        // POST /api/v1/crud-api?name=...&amp;amp;email=...&amp;amp;organization=...
        // Body와 파라미터를 함께 받아 처리
        System.out.println(dto.getName());
        System.out.println(dto.getEmail());
        System.out.println(dto.getOrganization());
        
        // 새로운 MemberDto 객체 생성 후 파라미터 값으로 세팅
        MemberDto mdto = new MemberDto();
        mdto.setName(name);
        mdto.setEmail(email);
        mdto.setOrganization(organization);
        
        // HTTP 200 OK 응답과 함께 MemberDto 반환
        return ResponseEntity.status(HttpStatus.OK).body(mdto);
    }
    
    @PostMapping(value=&quot;/add-header&quot;)
    public ResponseEntity&amp;lt;MemberDto&amp;gt; addHeader(
            @RequestHeader(&quot;my-header&quot;) String header, // 요청 헤더에서 &quot;my-header&quot; 값 추출
            @RequestBody MemberDto mdto // 요청 Body(JSON)로 전달된 MemberDto 객체
    ) {
        // POST /api/v1/crud-api/add-header
        // 헤더 값과 Body를 함께 받아 처리
        System.out.println(header);
        
        // HTTP 200 OK 응답과 함께 Body로 받은 MemberDto 반환
        return ResponseEntity.status(HttpStatus.OK).body(mdto);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(4) 프로젝트 우클릭 -&amp;gt; Properties -&amp;gt; Java Compiler -&amp;gt; Store information about method parameters (-parameters) 체크&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@RequestBody가 안 먹히는 문제 방지&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;1028&quot; data-origin-height=&quot;940&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ujZOK/dJMcaf6F0xY/8TuVYmGsYVuUSQRd5cwnr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ujZOK/dJMcaf6F0xY/8TuVYmGsYVuUSQRd5cwnr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ujZOK/dJMcaf6F0xY/8TuVYmGsYVuUSQRd5cwnr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FujZOK%2FdJMcaf6F0xY%2F8TuVYmGsYVuUSQRd5cwnr1%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;1028&quot; height=&quot;940&quot; data-origin-width=&quot;1028&quot; data-origin-height=&quot;940&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) RestTemplate 구현하기: 서버+클라이언트 역할 모두 수행하는 프로젝트 생성&amp;nbsp;&lt;/h3&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;color: #333333; text-align: start;&quot;&gt;아래 이미지에서&lt;span&gt; 클라이언트는 서버를 대상으로 요청을 보내고 응답을 받는 역할을 하고,&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;앞서 2)에서 만든 프로젝트는 '서버2'의 역할을 함&lt;/li&gt;
&lt;li&gt;즉, RestTemplate를 포함하는 새 프로젝트를 생성&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;722&quot; data-origin-height=&quot;357&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SD2Vn/dJMcadOAEXf/8MdbruqPIFMBbEm1UQfk60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SD2Vn/dJMcadOAEXf/8MdbruqPIFMBbEm1UQfk60/img.png&quot; data-alt=&quot;서버 간 통신 도식화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SD2Vn/dJMcadOAEXf/8MdbruqPIFMBbEm1UQfk60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSD2Vn%2FdJMcadOAEXf%2F8MdbruqPIFMBbEm1UQfk60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;722&quot; height=&quot;357&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;357&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서버 간 통신 도식화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) 1)과 같은 방식으로 resttemplate라는 이름의 프로젝트 생성&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) GET 형식의 RestTemplate 작성하기: RestTemlpateService.java 생성&lt;/h4&gt;
&lt;pre id=&quot;code_1772274336546&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.service;

import java.net.URI;

import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

@Service // 서비스 레이어 컴포넌트로 등록
public class RestTemlpateService {
    
    /**
     * 단순 GET 요청을 보내는 메서드
     * - 요청 대상: http://localhost:9090/api/v1/crud-api
     * - CrudController의 getName() 메서드 호출
     * - 응답 Body(&quot;Flature&quot;)를 반환
     */
    public String getName() {
        URI uri = UriComponentsBuilder
                .fromUriString(&quot;http://localhost:9090&quot;)
                .path(&quot;/api/v1/crud-api&quot;)
                .encode()
                .build()
                .toUri();
        
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity&amp;lt;String&amp;gt; responseEntity = restTemplate.getForEntity(uri, String.class);
        
        return responseEntity.getBody();
    }
    
    /**
     * PathVariable을 포함한 GET 요청을 보내는 메서드
     * - 요청 대상: http://localhost:9090/api/v1/crud-api/{name}
     * - expand(&quot;Flature&quot;)로 {name} 자리에 값 대입
     * - CrudController의 getVariable() 메서드 호출
     * - 응답 Body(&quot;Flature&quot;)를 반환
     */
    public String getNameWithPathVariable() {
        URI uri = UriComponentsBuilder
                .fromUriString(&quot;http://localhost:9090&quot;)
                .path(&quot;/api/v1/crud-api/{name}&quot;)
                .encode()
                .build()
                .expand(&quot;Flature&quot;)    // {name} 자리에 &quot;Flature&quot; 삽입
                .toUri();
        
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity&amp;lt;String&amp;gt; responseEntity = restTemplate.getForEntity(uri, String.class);
        
        return responseEntity.getBody();
    }
    
    /**
     * Query Parameter를 포함한 GET 요청을 보내는 메서드
     * - 요청 대상: http://localhost:9090/api/v1/crud-api/param?name=Flature
     * - CrudController의 getNameWithParam() 메서드 호출
     * - 응답 Body(&quot;Hello. Flature!&quot;)를 반환
     */
    public String getNameWithParameter() {
        URI uri = UriComponentsBuilder
                .fromUriString(&quot;http://localhost:9090&quot;)
                .path(&quot;/api/v1/crud-api/param&quot;)
                .queryParam(&quot;name&quot;, &quot;Flature&quot;)
                .encode()
                .build()
                .toUri();
        
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity&amp;lt;String&amp;gt; responseEntity = restTemplate.getForEntity(uri, String.class);
        
        return responseEntity.getBody();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;(3) POST 형식의 RestTemplate 작성하기: RestTemlpateService.java에 추가&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MemberDto는 서버 프로젝트의 MemberDto 복사&lt;/li&gt;
&lt;li&gt;RestTemplateService.java에 추가한 코드&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1772275495299&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    /**
     * 서버의 POST 엔드포인트를 호출하면서
     * RequestBody(MemberDto)와 Query Parameter(name, email, organization)를 함께 전달하는 예시
     * 
     * 요청 대상: http://localhost:9090/api/v1/crud-api?name=Flature&amp;amp;email=flature@book.com&amp;amp;organization=book
     * 요청 Body: MemberDto(JSON)
     * 응답: 서버에서 반환한 MemberDto 객체
     */
    public ResponseEntity&amp;lt;MemberDto&amp;gt; postWithParamAndBody() {
        URI uri = UriComponentsBuilder
                .fromUriString(&quot;http://localhost:9090&quot;)
                .path(&quot;/api/v1/crud-api&quot;)
                .queryParam(&quot;name&quot;, &quot;Flature&quot;)
                .queryParam(&quot;email&quot;, &quot;flature@book.com&quot;)
                .queryParam(&quot;organization&quot;, &quot;book&quot;)
                .encode()
                .build()
                .toUri();

        // 요청 Body로 보낼 MemberDto 객체 생성
        MemberDto dto = new MemberDto();
        dto.setName(&quot;flature!!&quot;);
        dto.setEmail(&quot;flature@gmail.com&quot;);
        dto.setOrganization(&quot;gmail&quot;);

        // RestTemplate을 이용해 POST 요청 전송
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity&amp;lt;MemberDto&amp;gt; responseEntity = restTemplate.postForEntity(uri, dto, MemberDto.class);

        // 서버 응답 반환
        return responseEntity;
    }

    /**
     * 서버의 POST 엔드포인트를 호출하면서
     * RequestBody(MemberDto)와 Custom Header(&quot;my-header&quot;)를 함께 전달하는 예시
     * 
     * 요청 대상: http://localhost:9090/api/v1/crud-api
     * 요청 Body: MemberDto(JSON)
     * 요청 Header: my-header=flature API
     * 응답: 서버에서 반환한 MemberDto 객체
     */
    public ResponseEntity&amp;lt;MemberDto&amp;gt; postWithHeader() {
        URI uri = UriComponentsBuilder
                .fromUriString(&quot;http://localhost:9090&quot;)
                .path(&quot;api/v1/crud-api&quot;)
                .encode()
                .build()
                .toUri();

        // 요청 Body로 보낼 MemberDto 객체 생성
        MemberDto dto = new MemberDto();
        dto.setName(&quot;flature&quot;);
        dto.setEmail(&quot;flature@naver.com&quot;);
        dto.setOrganization(&quot;naver&quot;);

        // RequestEntity를 이용해 Header와 Body를 함께 설정
        RequestEntity&amp;lt;MemberDto&amp;gt; requestEntity = RequestEntity
                .post(uri)
                .header(&quot;my-header&quot;, &quot;flature API&quot;)
                .body(dto);

        // RestTemplate.exchange()로 요청 전송
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity&amp;lt;MemberDto&amp;gt; responseEntity = restTemplate.exchange(requestEntity, MemberDto.class);

        // 서버 응답 반환
        return responseEntity;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(4) Swagger 설정: build.gradle에 의존성 추가 및 저장 후 우클릭-&amp;gt;Gradle-&amp;gt;Refresh Gradle Project 선택, SwaggerConfiguration.java 추가&lt;/h4&gt;
&lt;pre id=&quot;code_1772275699332&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// build.gradle
dependencies {
  // Swagger UI 및 OpenAPI 3 지원
  implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0'
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1772527847981&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;

/**
 * Swagger(OpenAPI) 설정 클래스
 * 
 * Springdoc-openapi 라이브러리를 사용하여 Swagger UI를 구성
 * 이 Bean이 등록되면 /swagger-ui/index.html 경로에서 API 문서를 확인
 */
@Configuration
public class SwaggerConfiguration {
    
    /**
     * OpenAPI Bean 정의
     * - title: API 문서의 제목
     * - version: API 버전
     * - description: API 설명
     * 
     * 필요에 따라 Info 객체에 license, contact 등을 추가
     */
    @Bean
    public OpenAPI api() {
        return new OpenAPI()
                .info(new Info() 
                .title(&quot;Demo API&quot;) 
                .version(&quot;1.0&quot;) 
                .description(&quot;com.example.demo&quot;));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(5) RestTemplateController.java 추가&lt;/h4&gt;
&lt;pre id=&quot;code_1772528275093&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.dto.MemberDto;
import com.example.demo.service.RestTemlpateService;

/**
 * RestTemplateController
 *
 * REST API 호출을 테스트하기 위한 컨트롤러 클래스
 * - @RestController: REST API 응답(JSON 등)을 반환하는 컨트롤러임을 명시
 * - @RequestMapping(&quot;/rest-template&quot;): 모든 메서드의 기본 URL prefix를 &quot;/rest-template&quot;로 설정
 *
 * 이 컨트롤러는 RestTemlpateService를 주입받아 실제 외부 API 호출 로직을 실행
 */
@RestController
@RequestMapping(&quot;/rest-template&quot;)
public class RestTemplateController {
    
    // Service 레이어 의존성 주입 (생성자 방식)
    private final RestTemlpateService restTemlpateService;
    
    public RestTemplateController(RestTemlpateService restTemlpateService) {
        this.restTemlpateService = restTemlpateService;
    }
    
    /**
     * 단순 GET 요청 예제
     * - /rest-template
     * - Service에서 이름을 가져와 반환
     */
    @GetMapping
    public String setName() {
        return restTemlpateService.getName();
    }
    
    /**
     * PathVariable을 사용하는 GET 요청 예제
     * - /rest-template/path-variable
     */
    @GetMapping(&quot;/path-variable&quot;)
    public String getNameWithPathVariable() {
        return restTemlpateService.getNameWithPathVariable();
    }
    
    /**
     * Request Parameter를 사용하는 GET 요청 예제
     * - /rest-template/parameter
     */
    @GetMapping(&quot;/parameter&quot;)
    public String getNameWithParameter() {
        return restTemlpateService.getNameWithParameter();
    }
    
    /**
     * POST 요청 예제 (Body + Parameter)
     * - /rest-template
     * - MemberDto 객체를 응답으로 반환
     */
    @PostMapping
    public ResponseEntity&amp;lt;MemberDto&amp;gt; postDto() {
        return restTemlpateService.postWithParamAndBody();
    }
    
    /**
     * POST 요청 예제 (Header 포함)
     * - /rest-template/header
     * - MemberDto 객체를 응답으로 반환
     */
    @PostMapping(&quot;/header&quot;)
    public ResponseEntity&amp;lt;MemberDto&amp;gt; postWithHeader() {
        return restTemlpateService.postWithHeader();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(6) 서버(resttemplate 프로젝트) 실행 후, swagger 페이지(http://localhost:8080/swagger-ui/index.html)에서 postDto() 메서드 호출한 결과&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1858&quot; data-origin-height=&quot;762&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c058Dt/dJMcabJ7knB/qhu81nkv6mWjNgOoKmJQLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c058Dt/dJMcabJ7knB/qhu81nkv6mWjNgOoKmJQLk/img.png&quot; data-alt=&quot;swagger 페이지(http://localhost:8080/swagger-ui/index.html)에서 postDto() 메서드를 클릭하고 execute 클릭&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c058Dt/dJMcabJ7knB/qhu81nkv6mWjNgOoKmJQLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc058Dt%2FdJMcabJ7knB%2Fqhu81nkv6mWjNgOoKmJQLk%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;1858&quot; height=&quot;762&quot; data-origin-width=&quot;1858&quot; data-origin-height=&quot;762&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;swagger 페이지(http://localhost:8080/swagger-ui/index.html)에서 postDto() 메서드를 클릭하고 execute 클릭&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4) RestTemplate 커스텀 설정 방법: HttpClient&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RestTemplate는 HTTPClient를 추상화하고 있기에 HTTPClient의 종류에 따라 기능에 다소 차이가 있음&lt;br /&gt;-&amp;gt; 가장 큰 차이가 커넥션 풀(Connection Pool): RestTemplate 에서는 커넥션 풀을 지원하지 않음&lt;/li&gt;
&lt;li&gt;커넥션 풀을 지원하지 않으면 매번 호출할 때마다 포트를 열어 커넥션 생성 필요&lt;br /&gt;-&amp;gt; TIME_WAIT 상태가 된 소켓을 다시 사용하려 접근하면 재사용 불가한 문제를 방지하기 위해,&lt;br /&gt;&amp;nbsp; &amp;nbsp; 커넥션 풀 기능을 활성화하여 재사용할 수 있도록 &lt;b&gt;아파치에서 제공하는 HttpClient로 대체하여 사용&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) resttemplate 프로젝트에 HttpClient 의존성 추가&lt;/h4&gt;
&lt;pre id=&quot;code_1772529758681&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
    // HttpClient 의존성 추가
    implementation 'org.apache.httpcomponents.client5:httpclient5:5.3.1'
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) RestTemplateService.java 코드 수정&lt;/h4&gt;
&lt;pre id=&quot;code_1772530997084&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.service;

import java.net.URI;

import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import com.example.demo.dto.MemberDto;

@Service // 서비스 레이어 컴포넌트로 등록
public class RestTemlpateService {
    ... 생략 ...
    /**
     * RestTemplate Bean 생성
     *
     * - HttpClient 5.x 기반 커넥션 풀 사용
     *   PoolingHttpClientConnectionManager로 최대 연결 수와 라우트별 연결 수 설정
     * - HttpComponentsClientHttpRequestFactory로 HttpClient 적용
     *   setConnectionRequestTimeout: 커넥션 풀에서 커넥션 가져올 때 대기 시간(ms)
     *   setReadTimeout: 서버 응답 대기 시간(ms)
     *
     * 외부 API 호출 시 커넥션 풀과 타임아웃 설정이 적용된 RestTemplate 반환
     */
    public RestTemplate restTemplate() {
    	PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(); 
    	connManager.setMaxTotal(500); 
    	connManager.setDefaultMaxPerRoute(500);
    	
    	CloseableHttpClient httpClient = HttpClients.custom()
    			// HttpClient 5.x에서는 connection pool 설정이 다름 
    			// setMaxConnTotal() 대신 ConnectionManager를 직접 설정해야 함 
    			.build();

        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
        factory.setConnectionRequestTimeout(2000);
        factory.setReadTimeout(5000);

        return new RestTemplate(factory);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초</category>
      <category>AsyncRestTemplate</category>
      <category>Java</category>
      <category>rest api</category>
      <category>RESTful</category>
      <category>restTemplate</category>
      <category>spring boot</category>
      <category>WebClient</category>
      <category>서버 간 통신</category>
      <category>스프링부트</category>
      <category>자바</category>
      <author>개발학생</author>
      <guid isPermaLink="true">https://keep-programming-study.tistory.com/193</guid>
      <comments>https://keep-programming-study.tistory.com/193#entry193comment</comments>
      <pubDate>Tue, 3 Mar 2026 18:44:55 +0900</pubDate>
    </item>
    <item>
      <title>스프링부트 핵심 가이드(장정우 지음) - 액추에이터 주요 기능 살펴보기, 액추에이터 커스텀 기능 만들기</title>
      <link>https://keep-programming-study.tistory.com/192</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*책 내용과 다르게, 다음과 같은 환경에서 프로젝트 생성&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Windows11(윈도우 11) 환경&lt;/li&gt;
&lt;li&gt;자바 JDK 17 버전 설치&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://yungenie.tistory.com/11&quot;&gt;https://yungenie.tistory.com/11&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1770198602816&quot; style=&quot;color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fsCfm/hyZjxqU4L2/XdX4MUTaSK5BaU7JmJjh90/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/nM72i/hyZjwZQPen/CQONWbqc5xGfLYajkBk8o0/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/JRE6h/hyZjgihxEK/k0VWPB43AkNJ8rLvVcy1M0/img.png?width=2762&amp;amp;height=2094&amp;amp;face=0_0_2762_2094&quot; data-og-url=&quot;https://yungenie.tistory.com/11&quot; data-og-source-url=&quot;https://yungenie.tistory.com/11&quot; data-og-host=&quot;yungenie.tistory.com&quot; data-og-description=&quot;자바 개발 도구 설치 방법에 대해서 알아보겠습니다. Java17은 LTS(Long Term Support : 장기 지원) 릴리즈로 1년 후까지 기술 지원 및 버그를 개선한 서비스를 제공받을 수 있습니다. 업데이트 버전을 꾸&quot; data-og-title=&quot;[Java] 차근차근 Java 설치하기 (JDK17, Window 11)&quot; data-og-type=&quot;article&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://yungenie.tistory.com/11&quot; data-source-url=&quot;https://yungenie.tistory.com/11&quot;&gt;
&lt;div style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fsCfm/hyZjxqU4L2/XdX4MUTaSK5BaU7JmJjh90/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/nM72i/hyZjwZQPen/CQONWbqc5xGfLYajkBk8o0/img.jpg?width=680&amp;amp;height=440&amp;amp;face=0_0_680_440,https://scrap.kakaocdn.net/dn/JRE6h/hyZjgihxEK/k0VWPB43AkNJ8rLvVcy1M0/img.png?width=2762&amp;amp;height=2094&amp;amp;face=0_0_2762_2094');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;[Java] 차근차근 Java 설치하기 (JDK17, Window 11)&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;자바 개발 도구 설치 방법에 대해서 알아보겠습니다. Java17은 LTS(Long Term Support : 장기 지원) 릴리즈로 1년 후까지 기술 지원 및 버그를 개선한 서비스를 제공받을 수 있습니다. 업데이트 버전을 꾸&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;yungenie.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스프링 부트 4.31.0 사용 - STS(Spring Tool Suite) 설치(Spring Tools for Eclipse -&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://spring.io/tools&quot;&gt;https://spring.io/tools&lt;/a&gt;)&lt;br /&gt;=&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://priming.tistory.com/147&quot;&gt;https://priming.tistory.com/147&lt;/a&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;참고&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1770198602818&quot; style=&quot;color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bylxoe/hyZjqL6ykw/At1UjkxTL5MxNIN6mN8Dhk/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/c4WuCh/hyZjyDk2Tg/jLlvw1t5ipklCwRzmvXHkK/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/vUGiq/hyZjaoN1DM/bbtYqu3fqKATJY2ztMT1a1/img.png?width=938&amp;amp;height=621&amp;amp;face=0_0_938_621&quot; data-og-url=&quot;https://priming.tistory.com/147&quot; data-og-source-url=&quot;https://priming.tistory.com/147&quot; data-og-host=&quot;priming.tistory.com&quot; data-og-description=&quot;STS란?Spring Tool Suite(STS)는 스프링 프로젝트를 생성하고, 개발할 수 있게 해주는 도구입니다.&amp;nbsp;STS 설치 과정에 대해 설명드리겠습니다.&amp;nbsp;설치 파일 다운로드STS 공식 사이트에서 설치 파일을 다운로&quot; data-og-title=&quot;[Windows] Spring Tool Suite 4(STS 4) 다운로드 및 설치&quot; data-og-type=&quot;article&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://priming.tistory.com/147&quot; data-source-url=&quot;https://priming.tistory.com/147&quot;&gt;
&lt;div style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bylxoe/hyZjqL6ykw/At1UjkxTL5MxNIN6mN8Dhk/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/c4WuCh/hyZjyDk2Tg/jLlvw1t5ipklCwRzmvXHkK/img.png?width=800&amp;amp;height=529&amp;amp;face=0_0_800_529,https://scrap.kakaocdn.net/dn/vUGiq/hyZjaoN1DM/bbtYqu3fqKATJY2ztMT1a1/img.png?width=938&amp;amp;height=621&amp;amp;face=0_0_938_621');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;[Windows] Spring Tool Suite 4(STS 4) 다운로드 및 설치&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;STS란?Spring Tool Suite(STS)는 스프링 프로젝트를 생성하고, 개발할 수 있게 해주는 도구입니다.&amp;nbsp;STS 설치 과정에 대해 설명드리겠습니다.&amp;nbsp;설치 파일 다운로드STS 공식 사이트에서 설치 파일을 다운로&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;priming.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2ne97/btsPgUiDCuF/eVBIrd1OJpcnARSiekch31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2ne97/btsPgUiDCuF/eVBIrd1OJpcnARSiekch31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2ne97/btsPgUiDCuF/eVBIrd1OJpcnARSiekch31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2ne97%2FbtsPgUiDCuF%2FeVBIrd1OJpcnARSiekch31%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;1051&quot; height=&quot;634&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;634&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;MySQL&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;Community Server&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;8.0.42 설치&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://dev.mysql.com/downloads/mysql/&quot;&gt;https://dev.mysql.com/downloads/mysql/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1770198602819&quot; style=&quot;color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/QP20c/hyZjCyYfQt/fudyPJf3QzC310TJECw8G1/img.jpg?width=841&amp;amp;height=244&amp;amp;face=0_0_841_244&quot; data-og-url=&quot;https://dev.mysql.com/downloads/mysql/&quot; data-og-source-url=&quot;https://dev.mysql.com/downloads/mysql/&quot; data-og-host=&quot;dev.mysql.com&quot; data-og-description=&quot;Select Version: 9.3.0 Innovation 8.4.5 LTS 8.0.42 Select Operating System: Select Operating System&amp;hellip; Microsoft Windows Ubuntu Linux Debian Linux SUSE Linux Enterprise Server Red Hat Enterprise Linux / Oracle Linux Fedora Linux - Generic Oracle Solaris mac&quot; data-og-title=&quot;MySQL :: Download MySQL Community Server&quot; data-og-type=&quot;website&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://dev.mysql.com/downloads/mysql/&quot; data-source-url=&quot;https://dev.mysql.com/downloads/mysql/&quot;&gt;
&lt;div style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/QP20c/hyZjCyYfQt/fudyPJf3QzC310TJECw8G1/img.jpg?width=841&amp;amp;height=244&amp;amp;face=0_0_841_244');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;MySQL :: Download MySQL Community Server&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Select Version: 9.3.0 Innovation 8.4.5 LTS 8.0.42 Select Operating System: Select Operating System&amp;hellip; Microsoft Windows Ubuntu Linux Debian Linux SUSE Linux Enterprise Server Red Hat Enterprise Linux / Oracle Linux Fedora Linux - Generic Oracle Solaris mac&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;dev.mysql.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;811&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btOcwO/btsPrnefLO2/pUhBPkOVmzbMTaKK1r5sG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btOcwO/btsPrnefLO2/pUhBPkOVmzbMTaKK1r5sG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btOcwO/btsPrnefLO2/pUhBPkOVmzbMTaKK1r5sG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtOcwO%2FbtsPrnefLO2%2FpUhBPkOVmzbMTaKK1r5sG0%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;1272&quot; height=&quot;811&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;811&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;Gradle&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;**STS에서 Gradle 프로젝트 생성한 과정&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K3amN/btsPiv4lwJt/uJutkklzbXbReULQxgZ8y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K3amN/btsPiv4lwJt/uJutkklzbXbReULQxgZ8y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K3amN/btsPiv4lwJt/uJutkklzbXbReULQxgZ8y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK3amN%2FbtsPiv4lwJt%2FuJutkklzbXbReULQxgZ8y1%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;914&quot; height=&quot;690&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;690&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;774&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m9kKL/btsPhOpIZXk/EXjrrKX55fmvxR1bktggSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m9kKL/btsPhOpIZXk/EXjrrKX55fmvxR1bktggSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m9kKL/btsPhOpIZXk/EXjrrKX55fmvxR1bktggSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm9kKL%2FbtsPhOpIZXk%2FEXjrrKX55fmvxR1bktggSK%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;582&quot; height=&quot;774&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;774&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;657&quot; data-origin-height=&quot;782&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kH6Gt/btsPjkt76G1/oLBQL2FY5kFBr6K3pyVy0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kH6Gt/btsPjkt76G1/oLBQL2FY5kFBr6K3pyVy0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kH6Gt/btsPjkt76G1/oLBQL2FY5kFBr6K3pyVy0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkH6Gt%2FbtsPjkt76G1%2FoLBQL2FY5kFBr6K3pyVy0K%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;657&quot; height=&quot;782&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;782&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;*** 함께 보면 좋은 글&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/191&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2026.02.04 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링부트 핵심 가이드(장정우 지음) - 예외 처리 에러 해결, 액추에이터 활용하기&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1770989584394&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;스프링부트 핵심 가이드(장정우 지음) - 예외 처리 에러 해결, 액추에이터 활용하기&quot; data-og-description=&quot;*책 내용과 다르게, 다음과 같은 환경에서 프로젝트 생성 Windows11(윈도우 11) 환경자바 JDK 17 버전 설치 https://yungenie.tistory.com/11 [Java] 차근차근 Java 설치하기 (JDK17, Window 11)자바 개발 도구 설치 방법&quot; data-og-host=&quot;keep-programming-study.tistory.com&quot; data-og-source-url=&quot;https://keep-programming-study.tistory.com/191&quot; data-og-url=&quot;https://keep-programming-study.tistory.com/191&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/oTaKJ/dJMb84p4UEj/rVQL2eCOu055XMqHCarqek/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/byjCdL/dJMb88643uF/TgD8htU1BlWkIiGhdRIew0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/I9O9d/dJMb895ZLB0/bd3Rg3scKLFlDz09wyaOCK/img.png?width=1272&amp;amp;height=811&amp;amp;face=0_0_1272_811&quot;&gt;&lt;a href=&quot;https://keep-programming-study.tistory.com/191&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://keep-programming-study.tistory.com/191&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/oTaKJ/dJMb84p4UEj/rVQL2eCOu055XMqHCarqek/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/byjCdL/dJMb88643uF/TgD8htU1BlWkIiGhdRIew0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/I9O9d/dJMb895ZLB0/bd3Rg3scKLFlDz09wyaOCK/img.png?width=1272&amp;amp;height=811&amp;amp;face=0_0_1272_811');&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;*책 내용과 다르게, 다음과 같은 환경에서 프로젝트 생성 Windows11(윈도우 11) 환경자바 JDK 17 버전 설치 https://yungenie.tistory.com/11 [Java] 차근차근 Java 설치하기 (JDK17, Window 11)자바 개발 도구 설치 방법&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;keep-programming-study.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 액추에이터 주요 기능 살펴보기&amp;nbsp;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 애플리케이션 기본 정보(/info)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;액추에이터의 /info 엔드포인트를 활용하면 가동 중인 애플리케이션의 정보를 볼 수 있음&lt;/li&gt;
&lt;li&gt;application.properties 파일 내에 다음과 같이 애플리케이션 정보 속성을 작성한 후 서버 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1770285368560&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 액추에이터 info 정보 설정
info.organization.name=programming
info.contact.email=abc@defg.co.kr
info.contact.phoneNumber=010-1234-5678&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;에러 발생!&lt;/b&gt;&lt;/h4&gt;
&lt;div style=&quot;background-color: #ffffff;&quot;&gt;
&lt;div style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;2026-02-05 19:00:42.291 [main] WARN o.h.e.j.e.i.JdbcEnvironmentInitiator - HHH000342: Could not obtain connection to query metadata&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;org.hibernate.exception.JDBCConnectionException: unable to obtain isolated JDBC connection [Public Key Retrieval is not allowed] [n/a]&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:100)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:58)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:108)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:94)&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;액추에이터를 추가하면, Spring Boot가&amp;nbsp;자동으로 DataSource HealthIndicator를 등록:&lt;br /&gt;이때 spring-boot-starter-actuator가 들어오면서, DataSource 관련 빈을 확인하다가 Hibernate/JPA가&amp;nbsp;MySQL 드라이버 설정을 기본값으로 잡음&amp;nbsp;&lt;br /&gt;-&amp;gt; application.properties 다음과 같이 수정하여 해결&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1770285972141&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring.application.name=study

# 테스트용 인메모리 H2 DB 설정
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

# JPA 설정
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true

# H2 콘솔 (선택)
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console


# Spring Boot 3.x 계열이면 아래 두 줄 반드시 붙임
management.info.env.enabled=true 
management.info.defaults.enabled=true

# 액추에이터 info 정보 설정
info.organization.name=programming
info.contact.email=abc@defg.co.kr
info.contact.phoneNumber=010-1234-5678


# 액추에이터 엔드포인트 노출 설정(모든 엔드포인트 열기)
management.endpoints.web.exposure.include=*
# 액추에이터 health 상세 내역 활성화
management.endpoint.health.show-details=always&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;http://localhost:8080/actuator/info에 접속 시 다음과 같이 뜸&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff;&quot;&gt;
&lt;div style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;111&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUEzd0/dJMcagxCUz8/kP8zUNLQ8mHXmhCtX4aO30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUEzd0/dJMcagxCUz8/kP8zUNLQ8mHXmhCtX4aO30/img.png&quot; data-alt=&quot;http://localhost:8080/actuator/info에 접속 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUEzd0/dJMcagxCUz8/kP8zUNLQ8mHXmhCtX4aO30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUEzd0%2FdJMcagxCUz8%2FkP8zUNLQ8mHXmhCtX4aO30%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;782&quot; height=&quot;111&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;111&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;http://localhost:8080/actuator/info에 접속 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 애플리케이션 상태(/health)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주로 네트워크 계층 중 L4(Loadbalancing) 레벨에서 애플리케이션의 상태를 확인하기 위해 사용&amp;nbsp;&lt;/li&gt;
&lt;li&gt;http://localhost:8080/actuator/health에 접속 시 다음과 같이 뜸&amp;nbsp;&lt;br /&gt;-&amp;gt; &lt;b&gt;management.endpoint.health.show-details=always&lt;/b&gt;가 있으므로, health 상세 내역이 보이는 것&amp;nbsp;&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;527&quot; data-origin-height=&quot;561&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sfda5/dJMcai3fwBV/b5FEGvk0v2EZcoU1E9YgN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sfda5/dJMcai3fwBV/b5FEGvk0v2EZcoU1E9YgN0/img.png&quot; data-alt=&quot;http://localhost:8080/actuator/health에 접속 결과(pretty print 적용, health 상세 내역 보임)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sfda5/dJMcai3fwBV/b5FEGvk0v2EZcoU1E9YgN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsfda5%2FdJMcai3fwBV%2Fb5FEGvk0v2EZcoU1E9YgN0%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;527&quot; height=&quot;561&quot; data-origin-width=&quot;527&quot; data-origin-height=&quot;561&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;http://localhost:8080/actuator/health에 접속 결과(pretty print 적용, health 상세 내역 보임)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) 빈 정보 확인(/beans)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스프링 컨테이너에 등록된 빈의 전체 목록을 표시(JSON 형식으로 빈 정보를 반환)&lt;/li&gt;
&lt;li&gt;http://localhost:8080/actuator/beans에 접속 시 다음과 같이 뜸&amp;nbsp;&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;1515&quot; data-origin-height=&quot;962&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uNIZU/dJMcacIN1pZ/CZk9WrJxMjf9pbndTxSEP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uNIZU/dJMcacIN1pZ/CZk9WrJxMjf9pbndTxSEP0/img.png&quot; data-alt=&quot;http://localhost:8080/actuator/beans에 접속 결과(pretty print 적용, 기존 프로젝트에 연결하여 엄청난 bean들이 보임)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uNIZU/dJMcacIN1pZ/CZk9WrJxMjf9pbndTxSEP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuNIZU%2FdJMcacIN1pZ%2FCZk9WrJxMjf9pbndTxSEP0%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;1515&quot; height=&quot;962&quot; data-origin-width=&quot;1515&quot; data-origin-height=&quot;962&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;http://localhost:8080/actuator/beans에 접속 결과(pretty print 적용, 기존 프로젝트에 연결하여 엄청난 bean들이 보임)&lt;/figcaption&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;4) 스프링 부트의 자동설정 내역 확인(/conditions)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스프링부트의 자동설정(AutoConfiguration) 조건 내역 확인&lt;/li&gt;
&lt;li&gt;http://localhost:8080/actuator/conditions에 접속 시 다음과 같이 뜸&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;1340&quot; data-origin-height=&quot;966&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTUJmf/dJMb996m8wJ/5EgMGdAZlXcHKoflqqokc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTUJmf/dJMb996m8wJ/5EgMGdAZlXcHKoflqqokc0/img.png&quot; data-alt=&quot;http://localhost:8080/actuator/conditions에 접속 결과(pretty print 적용, 기존 프로젝트에 연결하여 엄청난 conditions들이 보임)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTUJmf/dJMb996m8wJ/5EgMGdAZlXcHKoflqqokc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTUJmf%2FdJMb996m8wJ%2F5EgMGdAZlXcHKoflqqokc0%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;1340&quot; height=&quot;966&quot; data-origin-width=&quot;1340&quot; data-origin-height=&quot;966&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;http://localhost:8080/actuator/conditions에 접속 결과(pretty print 적용, 기존 프로젝트에 연결하여 엄청난 conditions들이 보임)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5) 스프링 환경변수 정보(/env)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스프링의 환경변수 정보 확인(application.properties 파일의 변수들, OS/JVM의 환경변수들이 표시됨)&lt;/li&gt;
&lt;li&gt;http://localhost:8080/actuator/env에 접속 시 다음과 같이 뜸&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;717&quot; data-origin-height=&quot;980&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RRO6C/dJMcad1WIVQ/oEuK0Knk5yl6dPOxk63i2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RRO6C/dJMcad1WIVQ/oEuK0Knk5yl6dPOxk63i2K/img.png&quot; data-alt=&quot;http://localhost:8080/actuator/env에 접속 결과(pretty print 적용, 기존 프로젝트에 연결하여 엄청난 env들이 보임)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RRO6C/dJMcad1WIVQ/oEuK0Knk5yl6dPOxk63i2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRRO6C%2FdJMcad1WIVQ%2FoEuK0Knk5yl6dPOxk63i2K%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;717&quot; height=&quot;980&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;980&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;http://localhost:8080/actuator/env에 접속 결과(pretty print 적용, 기존 프로젝트에 연결하여 엄청난 env들이 보임)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6) 로깅 레벨 확인(/loggers)&lt;/h3&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;http://localhost:8080/actuator/loggers에 접속 시 다음과 같이 뜸&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;745&quot; data-origin-height=&quot;960&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvOBMU/dJMcaiCc9s8/fjcdjFDEj7tDRchM0mKKPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvOBMU/dJMcaiCc9s8/fjcdjFDEj7tDRchM0mKKPK/img.png&quot; data-alt=&quot;http://localhost:8080/actuator/loggers에 접속 결과(pretty print 적용, 기존 프로젝트에 연결하여 엄청난 loggers들이 보임&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvOBMU/dJMcaiCc9s8/fjcdjFDEj7tDRchM0mKKPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdvOBMU%2FdJMcaiCc9s8%2FfjcdjFDEj7tDRchM0mKKPK%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;745&quot; height=&quot;960&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;960&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;http://localhost:8080/actuator/loggers에 접속 결과(pretty print 적용, 기존 프로젝트에 연결하여 엄청난 loggers들이 보임&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 액추에이터 커스텀 기능 만들기&lt;/h2&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;커스텀 기능 개발 방식: 기존 기능에 내용을 추가 or 새로운 엔드포인트를 개발&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 기존 기능에 내용 추가: InfoContributer 인터페이스를 구현하는 클래스 생성&lt;/h3&gt;
&lt;pre id=&quot;code_1771494559721&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.config.actuator;

import java.util.HashMap;
import java.util.Map;

import org.springframework.boot.actuate.info.Info.Builder;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.stereotype.Component;

/**
 * Spring Boot Actuator의 Info 엔드포인트에
 * 커스텀 정보를 추가하기 위한 클래스
 */
@Component // 스프링 빈으로 등록되어 자동으로 Actuator에 연결
public class CustomInfoContributor implements InfoContributor {

    /**
     * InfoContributor 인터페이스의 구현 메서드
     * : 애플리케이션의 /actuator/info 엔드포인트에 원하는 정보를 추가
     */
    @Override
    public void contribute(Builder builder) {
        // Info에 담을 커스텀 데이터를 저장할 Map 생성
        Map&amp;lt;String, Object&amp;gt; content = new HashMap&amp;lt;&amp;gt;();
        
        // key-value 형태로 원하는 정보 추가
        content.put(&quot;code-info&quot;, &quot;InfoContributor 구현체에서 정의한 정보&quot;);
        
        // builder에 &quot;custom-info-contributor&quot;라는 이름으로 Map을 등록
        builder.withDetail(&quot;custom-info-contributor&quot;, content);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;*서버 재실행 후 http://localhost:8080/actuator/info에 접속&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;513&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pxV7B/dJMcafyPOin/5kkvwBHClEYxWSNxesCkW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pxV7B/dJMcafyPOin/5kkvwBHClEYxWSNxesCkW0/img.png&quot; data-alt=&quot;http://localhost:8080/actuator/info에 접속 한 결과(pretty print 적용)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pxV7B/dJMcafyPOin/5kkvwBHClEYxWSNxesCkW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpxV7B%2FdJMcafyPOin%2F5kkvwBHClEYxWSNxesCkW0%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;513&quot; height=&quot;351&quot; data-origin-width=&quot;513&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;http://localhost:8080/actuator/info에 접속 한 결과(pretty print 적용)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 새로운 엔드포인트 개발: 커스텀 엔드포인트 클래스 생성&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@Endpoint 어노테이션으로 빈에 추가된 객체들은, @ReadOperation @WriteOperation @DeleteOperation 어노테이션으로 JMX나 HTTP를 통해 커스텀 엔드포인트를 노출시킬 수 있음&amp;nbsp;&lt;br /&gt;-&amp;gt; JMX에서만 사용하거나 HTTP에서만 사용하고 싶을 경우, @JmxEndpoint @WebEndpoint를 사용하면 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1771495182725&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.config.actuator;

import java.util.HashMap;
import java.util.Map;

import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.stereotype.Component;

/**
 * Spring Boot Actuator에 커스텀 엔드포인트를 추가하는 클래스
 * : &quot;/actuator/note&quot; 경로로 접근할 수 있으며, Read, Write, Delete 기능을 제공
 */
@Component // 스프링 빈으로 등록되어 Actuator에 자동 연결
@Endpoint(id=&quot;note&quot;) // 엔드포인트 ID를 &quot;note&quot;로 지정하여, /actuator/note 로 접근 가능
public class NoteEndpoint {
    
    // 엔드포인트에서 관리할 데이터를 저장하는 Map
    private Map&amp;lt;String, Object&amp;gt; noteContent = new HashMap&amp;lt;&amp;gt;();
    
    /**
     * ReadOperation: GET 요청 시 호출
     * : 현재 저장된 noteContent 전체를 반환
     */
    @ReadOperation
    public Map&amp;lt;String, Object&amp;gt; getNote() {
        return noteContent;
    }
    
    /**
     * WriteOperation: POST 요청 시 호출
     * : key-value 형태로 데이터를 저장하고 전체 Map을 반환
     */
    @WriteOperation
    public Map&amp;lt;String, Object&amp;gt; writeNote(String key, Object value) {
        noteContent.put(key, value);
        return noteContent;
    }
    
    /**
     * DeleteOperation: DELETE 요청 시 호출
     * : 전달받은 key에 해당하는 데이터를 삭제하고 전체 Map을 반환
     */
    @DeleteOperation
    public Map&amp;lt;String, Object&amp;gt; deleteNote(String key) {
        noteContent.remove(key);
        return noteContent;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;(1) 서버 실행&amp;nbsp;&lt;/h4&gt;
&lt;div style=&quot;background-color: #ffffff;&quot;&gt;
&lt;div style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;2026-02-19 19:01:45.279 [main] ERROR o.s.boot.SpringApplication - Application run failed&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'healthEndpointWebMvcHandlerMapping' defined in class path resource [org/springframework/boot/actuate/autoconfigure/health/HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.class]: Failed to instantiate [org.springframework.boot.actuate.endpoint.web.servlet.AdditionalHealthEndpointPathsWebMvcHandlerMapping]: Factory method 'healthEndpointWebMvcHandlerMapping' threw exception with message: Failed to extract parameter names for public java.util.Map com.example.demo.config.actuator.NoteEndpoint.deleteNote(java.lang.String)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:657)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:645)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1375)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1205)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1222)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1188)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1123)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.boot.SpringApplication.run(SpringApplication.java:318)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; at com.example.demo.StudyApplication.main(StudyApplication.java:11)&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버실행에 에러발생!! 역시 IntelliJ 환경이었다면 발생하지 않았을 에러...&lt;br /&gt;-&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;파라미터 이름을 명시적으로 지정할 수 있는 액추에이터의 @Selector 어노테이션을 사용하도록 수정하면 해결 (요청에서 key값 추출 가능) + Spring&amp;nbsp;Boot&amp;nbsp;3.5.3&amp;nbsp;환경에서는&amp;nbsp;메서드&amp;nbsp;시그니처를&amp;nbsp;Actuator가&amp;nbsp;이해할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;형태로&amp;nbsp;맞춰야&amp;nbsp;함&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1771495605589&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo.config.actuator;

import java.util.HashMap;
import java.util.Map;

import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.stereotype.Component;

/**
 * Spring Boot Actuator에 커스텀 엔드포인트를 추가하는 클래스
 * : &quot;/actuator/note&quot; 경로로 접근할 수 있으며, Read, Write, Delete 기능을 제공
 */
@Component // 스프링 빈으로 등록되어 Actuator에 자동 연결
@Endpoint(id=&quot;note&quot;) // 엔드포인트 ID를 &quot;note&quot;로 지정하여, /actuator/note 로 접근 가능
public class NoteEndpoint {
    
    // 엔드포인트에서 관리할 데이터를 저장하는 Map
    private Map&amp;lt;String, String&amp;gt; noteContent = new HashMap&amp;lt;&amp;gt;();
    
    /**
     * ReadOperation: GET 요청 시 호출
     * : 현재 저장된 noteContent 전체를 반환
     */
    @ReadOperation
    public Map&amp;lt;String, String&amp;gt; getNote() {
        return noteContent;
    }
    
    /**
     * WriteOperation: POST 요청 시 호출
     * : key-value 형태로 데이터를 저장하고 전체 Map을 반환
     */
    @WriteOperation
    public Map&amp;lt;String, String&amp;gt; writeNote(@Selector String key, String value) {
        noteContent.put(key, value);
        return noteContent;
    }
    
    /**
     * DeleteOperation: DELETE 요청 시 호출
     * : 전달받은 key에 해당하는 데이터를 삭제하고 전체 Map을 반환
     */
    @DeleteOperation
    public Map&amp;lt;String, String&amp;gt; deleteNote(@Selector String key) {
        noteContent.remove(key);
        return noteContent;
    }
}&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;&lt;b&gt;추가로, 프로젝트 우클릭 -&amp;gt; Properties -&amp;gt; Java Compiler -&amp;gt; Store information about method parameters (-parameters) 체크하니까 서버실행 됨!!&amp;nbsp;&lt;br /&gt;&lt;/b&gt;-&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;이걸하면... 그동안 @RequestBody가 안되서 @RequestParameter를 썼던 문제도 해결가능할지도??&lt;/span&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;1037&quot; data-origin-height=&quot;922&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UXrIZ/dJMcaaK2x5j/dMCaToDacNKL98QJWTfCOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UXrIZ/dJMcaaK2x5j/dMCaToDacNKL98QJWTfCOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UXrIZ/dJMcaaK2x5j/dMCaToDacNKL98QJWTfCOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUXrIZ%2FdJMcaaK2x5j%2FdMCaToDacNKL98QJWTfCOk%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;1037&quot; height=&quot;922&quot; data-origin-width=&quot;1037&quot; data-origin-height=&quot;922&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) 서버 실행 후, Talend API Tester(크롬 확장 프로그램)에서 커스텀 엔드포인트 호출 [Send를 클릭하면 됨]&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GET 요청: 값을 넣지 않은 상태이기에 JSON 형태의 빈 값이 보임&amp;nbsp;&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;960&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJyJTJ/dJMcaiWzydd/d8ZBwBGPHRSCVKKoDFct10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJyJTJ/dJMcaiWzydd/d8ZBwBGPHRSCVKKoDFct10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJyJTJ/dJMcaiWzydd/d8ZBwBGPHRSCVKKoDFct10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJyJTJ%2FdJMcaiWzydd%2Fd8ZBwBGPHRSCVKKoDFct10%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;960&quot; height=&quot;559&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;559&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;POST 호출 후 다시 GET 호출: 값을 추가하여 /note에서 확인이 가능한 상태&amp;nbsp;&amp;nbsp;&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;1557&quot; data-origin-height=&quot;858&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lRG9a/dJMcahDnBuo/Lr1O9mIba0UEGchsa8mAp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lRG9a/dJMcahDnBuo/Lr1O9mIba0UEGchsa8mAp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lRG9a/dJMcahDnBuo/Lr1O9mIba0UEGchsa8mAp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlRG9a%2FdJMcahDnBuo%2FLr1O9mIba0UEGchsa8mAp1%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;1557&quot; height=&quot;858&quot; data-origin-width=&quot;1557&quot; data-origin-height=&quot;858&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;1595&quot; data-origin-height=&quot;723&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baS8s3/dJMcafMnlz6/eldSUB93K4uZgUknmAK4J0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baS8s3/dJMcafMnlz6/eldSUB93K4uZgUknmAK4J0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baS8s3/dJMcafMnlz6/eldSUB93K4uZgUknmAK4J0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaS8s3%2FdJMcafMnlz6%2FeldSUB93K4uZgUknmAK4J0%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;1595&quot; height=&quot;723&quot; data-origin-width=&quot;1595&quot; data-origin-height=&quot;723&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;DELETE 호출 후 다시 GET 호출: 값을 삭제하여 /note가 다시 빈 값이 된 상태&amp;nbsp;&amp;nbsp;&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;1590&quot; data-origin-height=&quot;715&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bArqHY/dJMcajupPD6/pT8AwVSsvGW5OnU3bwDAEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bArqHY/dJMcajupPD6/pT8AwVSsvGW5OnU3bwDAEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bArqHY/dJMcajupPD6/pT8AwVSsvGW5OnU3bwDAEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbArqHY%2FdJMcajupPD6%2FpT8AwVSsvGW5OnU3bwDAEk%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;1590&quot; height=&quot;715&quot; data-origin-width=&quot;1590&quot; data-origin-height=&quot;715&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;1565&quot; data-origin-height=&quot;692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6OtvT/dJMcajgRunD/nSButbbkJqgAG4D9pOPFk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6OtvT/dJMcajgRunD/nSButbbkJqgAG4D9pOPFk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6OtvT/dJMcajgRunD/nSButbbkJqgAG4D9pOPFk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6OtvT%2FdJMcajgRunD%2FnSButbbkJqgAG4D9pOPFk0%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;1565&quot; height=&quot;692&quot; data-origin-width=&quot;1565&quot; data-origin-height=&quot;692&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초</category>
      <category>actuator</category>
      <category>EndPoint</category>
      <category>Java</category>
      <category>spring boot</category>
      <category>STS4</category>
      <category>스프링부트</category>
      <category>액추에이터</category>
      <category>에러 해결</category>
      <category>엔드포인트</category>
      <category>자바</category>
      <author>개발학생</author>
      <guid isPermaLink="true">https://keep-programming-study.tistory.com/192</guid>
      <comments>https://keep-programming-study.tistory.com/192#entry192comment</comments>
      <pubDate>Thu, 19 Feb 2026 19:36:51 +0900</pubDate>
    </item>
  </channel>
</rss>