<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Tiny Commit</title>
    <link>https://marin-1104.tistory.com/</link>
    <description>A Developer's Log of Small Efforts &amp;amp; Big Growth</description>
    <language>ko</language>
    <pubDate>Mon, 8 Jun 2026 20:11:48 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Tiny Commit</managingEditor>
    <image>
      <title>Tiny Commit</title>
      <url>https://tistory1.daumcdn.net/tistory/7327648/attach/a332e929838e4311bed06bd7d0eddc4f</url>
      <link>https://marin-1104.tistory.com</link>
    </image>
    <item>
      <title>[스프링부트 완전정복] 13. RESTful 웹 서비스</title>
      <link>https://marin-1104.tistory.com/171</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EDlUE/dJMcadtP09a/4MrkDoaKGh9oZZN8Pqw3Mk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EDlUE/dJMcadtP09a/4MrkDoaKGh9oZZN8Pqw3Mk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EDlUE/dJMcadtP09a/4MrkDoaKGh9oZZN8Pqw3Mk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEDlUE%2FdJMcadtP09a%2F4MrkDoaKGh9oZZN8Pqw3Mk%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;1920&quot; height=&quot;1080&quot; data-filename=&quot;9.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;1. RESTful&amp;nbsp;웹&amp;nbsp;서비스의 개요&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-path-to-node=&quot;4&quot;&gt;REST(Representational State Transfer) 원리를 사용하여 HTTP와 웹의 장점을 최대한 활용할 수 있는 아키텍처입니다.&lt;/li&gt;
&lt;li data-path-to-node=&quot;4&quot;&gt;특정 자원에 대해 CRUD(Create, Read, Update, Delete) 연산을 수행하기 위해 URI로 자원을 명시하고, GET, POST, PUT, DELETE 등의 HTTP 방식을 사용하여 요청을 보냅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-path-to-node=&quot;5&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;RESTful 웹 서비스의 3대 구성 요소&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;6&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot;&gt;리소스 (Resource)&lt;/b&gt;: 서버의 고유한 아이디를 가진 자원을 의미하며 URI에 해당합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;메서드 (Method)&lt;/b&gt;: 리소스에 대해 요청을 보내는 방식으로 &lt;u&gt;GET, POST, PUT, PATCH, DELETE&lt;/u&gt; 등이 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,2,0&quot;&gt;리소소 형태 (Representation)&lt;/b&gt;: 클라이언트와 서버가 데이터를 주고받는 형태로 JSON, XML, TEXT 등이 있으며 최근에는 &lt;b data-index-in-node=&quot;78&quot; data-path-to-node=&quot;6,2,0&quot;&gt;JSON&lt;/b&gt;을 주로 사용합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0&quot;&gt;URL과 URI의 차이&lt;/b&gt; URL은 리소스의 위치를 의미하고, URI는 리소스를 식별하는 문자열 구성을 의미합니다. URI가 URL을 포함하는 더 포괄적인 범위입니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;2. RESTful 방식의 애너테이션&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 애너테이션 요약&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;5&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;유형&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0,0&quot;&gt;@RequestBody&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,1,1,0&quot;&gt;컨트롤러 요청 처리 메서드의 매개변수에 선언하며, &lt;b data-index-in-node=&quot;28&quot; data-path-to-node=&quot;5,1,1,0&quot;&gt;HTTP 요청 본문(body)을 해당 매개변수에 바인딩&lt;/b&gt;합니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,2,0,0&quot;&gt;@ResponseBody&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,2,1,0&quot;&gt;컨트롤러 요청 처리 메서드의 매개변수에 선언하며, &lt;b data-index-in-node=&quot;28&quot; data-path-to-node=&quot;5,2,1,0&quot;&gt;반환값을 HTTP 응답 본문(body)에 바인딩&lt;/b&gt;합니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,3,0,0&quot;&gt;@RestController&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,3,1,0&quot;&gt;@Controller와 @ResponseBody를 결합한 형태로, &lt;b data-index-in-node=&quot;37&quot; data-path-to-node=&quot;5,3,1,0&quot;&gt;REST API를 제공하는 컨트롤러&lt;/b&gt;임을 명시합니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt; 2.1 @RequestBody &lt;/b&gt;&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;HTTP 요청 본문 내용을 XML, JSON 또는 기타 데이터 형태의 자바 객체로 매핑하는 역할을 합니다.&lt;/li&gt;
&lt;li&gt;폼 페이지에서 name=value 형태로 전송되는 일반적인 데이터는 @RequestParam 등을 사용하지만, JSON과 같은 문자열 형태의 데이터는 @RequestBody를 통해 전달받아야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1767089197966&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// @RequestBody로 단일 매개변수 받기

@PostMapping()
public String submit(@RequestBody String param, Model model) {
    model.addAttribute(&quot;data1&quot;, &quot;@RequestBody로 정보 받기&quot;);
    model.addAttribute(&quot;data2&quot;, param);
    return &quot;viewPage01_result&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;요청 처리를 RequestBody에 선언하고 선언된 매개변수는 폼 페이지에서 입력된 다중 값을 &amp;amp;로 연결된 name = value형태로 전달 받습니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;폼페이지에서 입력을 하면, sumit(컨트롤러 요청처리 메서드)에서 매개변수를 받가 각각을 출력합니다. (data1, data2)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1767089000719&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// @RequestBody를 활용한 요청 데이터 처리

@Controller
@RequestMapping(&quot;/exam02&quot;)
public class Example02Controller {

    @GetMapping(&quot;/form&quot;)
    public String form() {
        return &quot;exam02/form&quot;; // 입력 폼 페이지로 이동
    }

    @PostMapping(&quot;/submit&quot;)
    @ResponseBody // 리턴값을 페이지가 아닌 데이터(JSON 등)로 응답
    public Map&amp;lt;String, String&amp;gt; submit(@RequestBody HashMap&amp;lt;String, String&amp;gt; map) {
        // @RequestBody: HTTP 요청 본문(JSON)을 Map 객체로 변환
        return map; // 전달받은 데이터를 그대로 JSON 형태로 반환
    }
}&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;사용자 웹 요청이 GET방식이면 컨트롤러 요청 처리 메서드가 showFOrm()으로 viewPage02를 출력합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;폼을 입력하면 POST방식으로 호출되어 sumit()이 실행됩니다.&lt;/li&gt;
&lt;li&gt;sumit매서드는 매개변수 HashMap타입의 map객체는 JSON형식으로 전달됩니다.&amp;nbsp;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;2.2 @ResponseBody&lt;/b&gt;&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;자바 객체를 HTTP 응답 본문(body) 내용으로 매핑하여 클라이언트에게 전달합니다.&lt;/li&gt;
&lt;li&gt;@RequestBody와 마찬가지로 데이터 형식(JSON/XML)에 맞는 메시지 변환기(HttpMessageConverter)가 작동하여 자동으로 변환해 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1767088086047&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Controller
@RequestMapping(&quot;/exam03&quot;)
public class Example03Controller {

    @GetMapping(&quot;/submit&quot;)
    @ResponseBody // 객체를 직접 HTTP 응답 본문으로 전송
    public Person submit() {
        Person person = new Person();
        person.setName(&quot;HongGilSon&quot;);
        person.setAge(20);
        person.setEmail(&quot;hgs@naver.com&quot;);
        
        return person; // MappingJackson2HttpMessageConverter가 JSON으로 자동 변환 
        		// 객체가 JSON 형식 {&quot;name&quot;:&quot;HongGilSon&quot;, ...}으로 응답됨
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1767090220562&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.springboot.domain;

import lombok.Data; // Lombok 라이브러리 사용

@Data // Getter, Setter, toString, EqualsAndHashCode 등을 자동 생성
public class Person {
    private String name;
    private String age;
    private String email;
}&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;2.3 @RestController&lt;/b&gt;&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;클래스 전체에 @RestController를 적용하여 모든 메서드에서 @ResponseBody를 생략하는 최신 방식입니다.&lt;/li&gt;
&lt;li&gt;@Controller일때는 @ResponseBody를 선언해야 하지만 @RestController를 사용하면 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;@ResponseBody를 붙일 필요 없다.&amp;nbsp;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;메서드마다 @ResponseBody를 자동으로 붙여 코드가 간결해집니다.&lt;/li&gt;
&lt;li&gt;변환값을 JSON으로 변환해서 응답합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1767089971202&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestController // @Controller + @ResponseBody 합쳐진 형태
@RequestMapping(&quot;/exam04&quot;)
public class Example04Controller {

    @GetMapping(&quot;/submit&quot;)
    public Person submit() {
        // 별도의 @ResponseBody 선언 없이도 Person 객체가 JSON으로 반환됨
        Person person = new Person();
        person.setName(&quot;HongGilSon&quot;);
        person.setAge(20);
        person.setEmail(&quot;hgs@naver.com&quot;);
        
        return person;
    }
}&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;3. RESTful&lt;span&gt; 웹 서비스의 CRUD&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CRUD(Create, Read, Update, Delete)를 HTTP 요청 방식과 매핑하여 리소스에 접근합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;5&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;유형&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;CRUD 작업&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0,0&quot;&gt;POST&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,1,1,0&quot;&gt;새로운 리소스를 생성하거나 기존 리소스를 갱신&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,1,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,2,0&quot;&gt;Create&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,2,0,0&quot;&gt;GET&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,2,1,0&quot;&gt;리소스를 조회하여 읽어 오기&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,2,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,2,2,0&quot;&gt;Read&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,3,0,0&quot;&gt;PUT&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,3,1,0&quot;&gt;리소스를 변경&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,3,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,3,2,0&quot;&gt;Update&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,4,0,0&quot;&gt;DELETE&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,4,1,0&quot;&gt;기존 리소스를 삭제&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,4,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,4,2,0&quot;&gt;Delete&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,5,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,5,0,0&quot;&gt;OPTION&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;5,5,1,0&quot;&gt;리소스에 대한 사용 가능한 작업 얻기&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1767090325475&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// application.properties
spring.mvc.hiddenmethod.filter.enabled=true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1767090381504&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Controller
@RequestMapping(&quot;/exam05&quot;)
public class Example05Controller {

    // 수정을 위한 폼 페이지 출력 (GET 방식)
    @GetMapping
    public String showForm(@ModelAttribute Person person) {
        return &quot;viewPage05&quot;;
    }

    // 데이터를 실제로 수정 처리 (PUT 방식)
    @PutMapping
    public String submit(@ModelAttribute Person person, Model model) {
        model.addAttribute(&quot;data1&quot;, &quot;@PutMapping 적용하기&quot;);
        model.addAttribute(&quot;data2&quot;, person);
        System.out.println(person); // 콘솔에 수정된 객체 정보 출력
        return &quot;viewPage05_result&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1767090394478&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;form action=&quot;exam05&quot; method=&quot;post&quot;&amp;gt;
    &amp;lt;input type=&quot;hidden&quot; name=&quot;_method&quot; value=&quot;put&quot; /&amp;gt; 
    
    &amp;lt;p&amp;gt;이름 : &amp;lt;input type=&quot;text&quot; name=&quot;name&quot; /&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;나이 : &amp;lt;input type=&quot;text&quot; name=&quot;age&quot; /&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;이메일 : &amp;lt;input type=&quot;text&quot; name=&quot;email&quot; /&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;input type=&quot;submit&quot; value=&quot;확인&quot; /&amp;gt;
&amp;lt;/form&amp;gt;&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;4. [도서 쇼핑몰] 장바주니 페이지 만들기&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;RESTful 방식의 장바구니 기본 구조 만들기&lt;/li&gt;
&lt;li&gt;RESTful 웹 서비스를 위한 장바구니 CRUD 만들기&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: right;&quot; data-ke-size=&quot;size16&quot;&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;span&gt;&amp;nbsp;&lt;/span&gt;스프링부트 완전정복: 개념부터 실정 프로젝트까지 』길벗캠퍼스&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(2024).&lt;/p&gt;</description>
      <category>DEVELOPMENT/Spring</category>
      <author>Tiny Commit</author>
      <guid isPermaLink="true">https://marin-1104.tistory.com/171</guid>
      <comments>https://marin-1104.tistory.com/171#entry171comment</comments>
      <pubDate>Tue, 30 Dec 2025 19:38:21 +0900</pubDate>
    </item>
    <item>
      <title>[스프링부트 완전정복] 12. 로그 기록</title>
      <link>https://marin-1104.tistory.com/170</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wk8s4/dJMcaaDVCxY/NqhZGQ6CtChFeU86KrlOuk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wk8s4/dJMcaaDVCxY/NqhZGQ6CtChFeU86KrlOuk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wk8s4/dJMcaaDVCxY/NqhZGQ6CtChFeU86KrlOuk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwk8s4%2FdJMcaaDVCxY%2FNqhZGQ6CtChFeU86KrlOuk%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;1920&quot; height=&quot;1080&quot; data-filename=&quot;9.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;1. 로드의 개요&lt;/b&gt;&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;실행되는 동안 남긴 로그 기옥은 애플리테이션의 정확한 상황과 상태 정보를 제공합니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.1 로깅 라이브러리&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로그는 애플리케이션이 실행되는 동안 버그나 성능에 관한 통계 등의 정보를 전달하는 역할을 합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;필요한 정보를 직접 로깅하거나, 문제의 원인을 파악할 수 있는 다양한 로깅 라이브러리를 제공합니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;로깅 라이브러리 유형&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;12&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;유형&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;12,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0,0&quot;&gt;java.util.logging&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;12,1,1,0&quot;&gt;자바에서 기본으로 제공하는 로깅 패키지&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;12,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,2,0,0&quot;&gt;Log4j&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;12,2,1,0&quot;&gt;2015년에 개발이 중단된 가장 오래된 로깅 라이브러리 중 하나&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;12,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,3,0,0&quot;&gt;Logback&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;12,3,1,0&quot;&gt;Log4j 이후 출시되었으며, 스프링 부트의 spring-boot-starter-logging에 기본 포함됨&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;12,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,4,0,0&quot;&gt;Log4j2&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;12,4,1,0&quot;&gt;가장 최근에 등장했으며 Logback과 유사한 기능을 가짐 (Java 7 이상 동작)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;로그 레벨 유형&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-path-to-node=&quot;19&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-path-to-node=&quot;19,0&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,0&quot;&gt;TRACE &amp;lt; DEBUG &amp;lt; INFO &amp;lt; WARN &amp;lt; ERROR &amp;lt; FATAL&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;20&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;레벨&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,1,0,0&quot;&gt;FATAL&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,1,1,0&quot;&gt;조기 종료를 유발하는 아주 심각한 오류&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,2,0,0&quot;&gt;ERROR&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,2,1,0&quot;&gt;런타임 오류 또는 예상치 못한 에러 상황&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,3,0,0&quot;&gt;WARN&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,3,1,0&quot;&gt;실행에는 문제없으나 향후 발생할 수 있는 잠재적 문제에 대한 경고&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,4,0,0&quot;&gt;INFO&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,4,1,0&quot;&gt;시작/종료와 같은 런타임 이벤트 정보 (일반적인 정보 확인용)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,5,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,5,0,0&quot;&gt;DEBUG&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,5,1,0&quot;&gt;개발 단계에서 시스템 흐름을 파악하기 위한 상세 정보&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,6,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,6,0,0&quot;&gt;TRACE&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,6,1,0&quot;&gt;가장 상세한 로그 레벨로 최하위 수준의 정보 출력&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.2 SLF4J와 로깅 라이브러리&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SLF4J는 다양한 로깅 라이브러리를 통일된 방식으로 사용할 수 있게 해주는 &lt;b data-index-in-node=&quot;43&quot; data-path-to-node=&quot;16&quot;&gt;추상 인터페이스&lt;/b&gt;입니다.&lt;/li&gt;
&lt;li&gt;SLF4J를 사용하면 코드 수정 없이 라이브러리만 교체하여 로그를 관리할 수 있습니다.&lt;/li&gt;
&lt;li&gt;Lombok의 @Slf4j 애너테이션을 사용하면 별도의 로깅 선언 코드 없이 바로 log 객체를 사용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-30 120238.png&quot; data-origin-width=&quot;376&quot; data-origin-height=&quot;226&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byPwy3/dJMcadgjhV6/xqFIRkEQkapSlgLqJxYda1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byPwy3/dJMcadgjhV6/xqFIRkEQkapSlgLqJxYda1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byPwy3/dJMcadgjhV6/xqFIRkEQkapSlgLqJxYda1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyPwy3%2FdJMcadgjhV6%2FxqFIRkEQkapSlgLqJxYda1%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;376&quot; height=&quot;226&quot; data-filename=&quot;스크린샷 2025-12-30 120238.png&quot; data-origin-width=&quot;376&quot; data-origin-height=&quot;226&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1767056870920&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// SLF4J의 로그 레벨 동작 예

@Controller
public class Example01Controller {
    @GetMapping(&quot;/exam01&quot;)
    public String requestMethod(Model model) {
    	Logger logger = LoggerFactory.getLogger(Example01Controller.class);
        logger.trace(&quot;Trace 메시지!&quot;);
        logger.debug(&quot;Debug 메시지!&quot;);
        logger.info(&quot;Info 메시지!&quot;);
        logger.warn(&quot;Warn 메시지!&quot;);
        logger.error(&quot;Error 메시지!&quot;);
        model.addAttribute(&quot;data&quot;, &quot;Slf4j로 로그 출력하기&quot;)
        return &quot;viewPage&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1767056599770&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Slf4j를 이용한 로그 레벨 동작 예

@Slf4j
@Controller
public class Example02Controller {
    @GetMapping(&quot;/exam02&quot;)
    public String requestMethod(Model model) {
        log.trace(&quot;Trace 메시지!&quot;);
        log.debug(&quot;Debug 메시지!&quot;);
        log.info(&quot;Info 메시지!&quot;);
        log.warn(&quot;Warn 메시지!&quot;);
        log.error(&quot;Error 메시지!&quot;);
        
        return &quot;viewPage&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;2. Log4j2를 이용한 로그 기록&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.1 Log4j2의 개요&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-path-to-node=&quot;17&quot;&gt;자바로 작성되어 안정적이고 신속하며 유연합니다.&lt;/li&gt;
&lt;li data-path-to-node=&quot;17&quot;&gt;보안 취약점을 개선하여 로깅기능 강화시킨것으로 멀티스레드 환경에서 성능향상&lt;/li&gt;
&lt;li data-path-to-node=&quot;17&quot;&gt;로그 메시지의 더 나은 파라미터화&lt;/li&gt;
&lt;li data-path-to-node=&quot;17&quot;&gt;로그 이벤트에 더 많은 메타데이터 제공&lt;/li&gt;
&lt;li data-path-to-node=&quot;17&quot;&gt;로깅의 유연성과 확장성을 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;20&quot; data-ke-size=&quot;size16&quot;&gt;스프링 부트 기본 로거인 Logback과 충돌을 방지하기 위해 기본 로거를 제외하고 Log4j2 의존성을 추가해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767057512879&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;configurations {
    all {
        // Log4j2와의 충돌 방지를 위해 기본 로거 삭제
        exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
    }
}

dependencies {
    // Log4j2 의존성 라이브러리 추가
    implementation 'org.springframework.boot:spring-boot-starter-log4j2'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;22&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.2 Log4j2 구조&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-path-to-node=&quot;23&quot; data-ke-size=&quot;size16&quot;&gt;Log4j2는 크게 세 가지 요소로 구성됩니다:&lt;/p&gt;
&lt;p data-path-to-node=&quot;23&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;23&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;24,0,0&quot;&gt;Logger&lt;/b&gt;: 로그 기록을 수행하는 핵심 모듈.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-path-to-node=&quot;24,1,1&quot;&gt;
&lt;li&gt;Logger는 로그 작업을 수행하는 핵심 요소로, 로그 레벨과 Appender 설정을 통해 출력 대상과 위치를 결정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot;&gt;Root Logger&lt;/b&gt;: &amp;lt;Root&amp;gt; 요소로 정의하며, 일반적인 로그 설정의 기본이 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;일반 Logger&lt;/b&gt;: &amp;lt;Logger&amp;gt; 요소로 정의하며, 특정 패키지나 클래스별로 다른 설정을 적용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1767057797682&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Configuration&amp;gt;
    &amp;lt;Loggers&amp;gt;
        &amp;lt;Logger name=&quot;com.springboot&quot; level=&quot;DEBUG&quot; additivity=&quot;false&quot;&amp;gt;
            &amp;lt;AppenderRef ref=&quot;console&quot;/&amp;gt;
        &amp;lt;/Logger&amp;gt;
        
        &amp;lt;Root level=&quot;INFO&quot;&amp;gt;
            &amp;lt;AppenderRef ref=&quot;console&quot;/&amp;gt;
        &amp;lt;/Root&amp;gt;
    &amp;lt;/Loggers&amp;gt;
&amp;lt;/Configuration&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;24,1,0&quot;&gt;Appender&lt;/b&gt;: 로그가 출력될 위치(콘솔, 파일, DB 등)를 결정.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;24,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lt;Console&amp;gt;: 콘솔에 출력.&lt;/li&gt;
&lt;li&gt;&amp;lt;File&amp;gt;: 특정 파일에 계속 기록.&lt;/li&gt;
&lt;li&gt;&amp;lt;RollingFile&amp;gt;: 파일 크기나 날짜에 따라 백업 파일을 생성하며 기록.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1767057910826&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Appenders&amp;gt;
    &amp;lt;Console name=&quot;console&quot; target=&quot;SYSTEM_OUT&quot;&amp;gt;
        &amp;lt;PatternLayout pattern=&quot;%d %5p [%c] %m%n&quot;/&amp;gt;
    &amp;lt;/Console&amp;gt;

    &amp;lt;File name=&quot;file&quot; fileName=&quot;./logs/file/sample.log&quot; append=&quot;false&quot;&amp;gt;
        &amp;lt;PatternLayout pattern=&quot;%d %5p [%c] %m%n&quot;/&amp;gt;
    &amp;lt;/File&amp;gt;

    &amp;lt;RollingFile name=&quot;rollingFile&quot; fileName=&quot;log.log&quot; filePattern=&quot;log.%i.log&quot;&amp;gt;
        &amp;lt;PatternLayout pattern=&quot;%d %5p [%c] %m%n&quot;/&amp;gt;
        &amp;lt;Policies&amp;gt;
            &amp;lt;SizeBasedTriggeringPolicy size=&quot;15MB&quot;/&amp;gt; &amp;lt;TimeBasedTriggeringPolicy interval=&quot;1&quot;/&amp;gt; &amp;lt;/Policies&amp;gt;
        &amp;lt;DefaultRolloverStrategy max=&quot;3&quot; fileIndex=&quot;min&quot;/&amp;gt; &amp;lt;/RollingFile&amp;gt;
&amp;lt;/Appenders&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;24,2,0&quot;&gt;Layout&lt;/b&gt;: 로그의 출력 형식을 결정 (주로 PatternLayout 사용).&lt;/p&gt;
&lt;pre id=&quot;code_1767057932348&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;PatternLayout pattern=&quot;%d{HH:mm:ss,SSS} [%thread] %-5level %logger{36} - %msg%n&quot; /&amp;gt;&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;pre id=&quot;code_1767057590220&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 로깅 실전 코드 (Lombok @Slf4j 활용)
// Lombok의 @Slf4j를 사용하면 별도의 로거 생성 코드 없이 편리하게 로그를 남길 수 있습니다.

@Slf4j
@Controller
public class Example02Controller {
    @GetMapping(&quot;/exam02&quot;)
    public String requestMethod2(Model model) {
        log.trace(&quot;Trace 메시지!&quot;);
        log.debug(&quot;Debug 메시지!&quot;);
        log.info(&quot;Info 메시지!&quot;);
        log.warn(&quot;Warn 메시지!&quot;);
        log.error(&quot;Error 메시지!&quot;);
        
        model.addAttribute(&quot;data&quot;, &quot;@Slf4j로 로그 출력하기&quot;);
        return &quot;viewPage&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;3. 인터셉터를 이용한 로그 기록&lt;/b&gt;&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;특정&amp;nbsp; URL 을 요청할 때 컴트롤러로 가는 요청을 가로챈 뒤 특정 작업을 처리하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.1 인터셉터&lt;/b&gt;&lt;/h4&gt;
&lt;p data-path-to-node=&quot;23&quot; 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;사용자가 URL을 요청하면 컨트롤러에 요청이 들어가기 전(또는 응답하기 전)에 가로채어 특정 작업을 수행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;주요 기능&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;응답 페이지를 출력하기 전에 서버에서 데이터를 미리 가져오는 기능&lt;/li&gt;
&lt;li&gt;폼에서의 제출(submit) 중복을 막는 기능&lt;/li&gt;
&lt;li&gt;요청이 처리되기 전에 파일을 업로드하는 기능&lt;/li&gt;
&lt;li&gt;각 요청에 대한 상세 내역을 기록하는 기능&lt;/li&gt;
&lt;li&gt;유효성을 검사하는 기능&lt;/li&gt;
&lt;li&gt;시간별 동작 및 성능의 병목 지점을 검사하는 기능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;23&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;23&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 3.2 HandlerInterceptor로 로그 기록 &lt;/b&gt;&lt;/h4&gt;
&lt;p data-path-to-node=&quot;23&quot; data-ke-size=&quot;size16&quot;&gt;인터셉터의 주요 메서드 및 동작 시점&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;24&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;24,0,0&quot;&gt;preHandle()&lt;/b&gt;: 컨트롤러가 호출되기 전 실행됩니다. false 반환 시 이후 과정은 실행되지 않습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;24,1,0&quot;&gt;postHandle()&lt;/b&gt;: 컨트롤러가 정상 처리된 후, 뷰로 전달되기 전 실행됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;24,2,0&quot;&gt;afterCompletion()&lt;/b&gt;: 뷰 전송이 완료된 후, 즉 모든 처리가 끝난 후 실행됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1767058511173&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 인터셉터 클래스 구현 (ExampleInterceptor.java)

package com.springboot.controller;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.method.HandlerMethod;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ExampleInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info(&quot;preHandle() 호출......&quot;);
        if (handler instanceof HandlerMethod) {
            HandlerMethod method = (HandlerMethod) handler;
            log.info(&quot;핸들러 메서드명 : &quot; + method.getMethod().getName());
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, org.springframework.web.servlet.ModelAndView modelAndView) throws Exception {
        log.info(&quot;postHandle() 호출......&quot;);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info(&quot;afterCompletion() 호출......&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1767058543613&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 인터셉터 등록 설정 (LoggingConfig.java)
// 작성한 인터셉터를 스프링 빈으로 등록해야 작동합니다.

package com.springboot.config;

import com.springboot.controller.ExampleInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class LoggingConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 인터셉터를 등록함
        registry.addInterceptor(new ExampleInterceptor());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;4. [도서 쇼핑몰] 로그 기록 만들기&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Log4j2로 콘솔에 로그 기록하기&lt;/li&gt;
&lt;li&gt;인터센터로 모든 요청 URL의 콘솔 및 파일에 로그 기록하기&lt;/li&gt;
&lt;li&gt;인터셉터로 특정 요청 URL의 콘솔 및 파일에 로그 기록하기&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: right;&quot; data-ke-size=&quot;size16&quot;&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;span&gt;&amp;nbsp;&lt;/span&gt;스프링부트 완전정복: 개념부터 실정 프로젝트까지 』길벗캠퍼스&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(2024).&lt;/p&gt;</description>
      <category>DEVELOPMENT/Spring</category>
      <author>Tiny Commit</author>
      <guid isPermaLink="true">https://marin-1104.tistory.com/170</guid>
      <comments>https://marin-1104.tistory.com/170#entry170comment</comments>
      <pubDate>Tue, 30 Dec 2025 10:38:18 +0900</pubDate>
    </item>
    <item>
      <title>[스프링부트 완전정복] 11. 예외 처리</title>
      <link>https://marin-1104.tistory.com/169</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zKFRA/dJMcaaYeUIv/Av6ix6eZuYbl8qyZWXM8vK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zKFRA/dJMcaaYeUIv/Av6ix6eZuYbl8qyZWXM8vK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zKFRA/dJMcaaYeUIv/Av6ix6eZuYbl8qyZWXM8vK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzKFRA%2FdJMcaaYeUIv%2FAv6ix6eZuYbl8qyZWXM8vK%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;1920&quot; height=&quot;1080&quot; data-filename=&quot;9.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;1. 예외 처리의 개요&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 부트 애플리케이션을 개발하다 보면 웹 요청 처리 중 다양한 예외가 발생합니다. 예외가 발생했을 때 사용자에게 단순히 에러 페이지를 보여주는 것이 아니라, 적절한 &lt;b data-index-in-node=&quot;94&quot; data-path-to-node=&quot;3&quot;&gt;HTTP 상태 코드&lt;/b&gt;와 &lt;b data-index-in-node=&quot;106&quot; data-path-to-node=&quot;3&quot;&gt;메시지&lt;/b&gt;를 전달하는 것이 중요합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.1 예외 처리&lt;/b&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;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.2 예외 처리 방법&lt;/b&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;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;25&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;애너테이션&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;25,1,0,0&quot;&gt;@ResponseStatus&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,1,1,0&quot;&gt;예외를 특정 HTTP 상태 코드와 매핑하여 응답&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;25,2,0,0&quot;&gt;@ExceptionHandler&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,2,1,0&quot;&gt;특정 컨트롤러 내에서 발생하는 에러를 직접 구체화하여 처리&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;25,3,0,0&quot;&gt;@ControllerAdvice&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,3,1,0&quot;&gt;여러 컨트롤러에서 발생하는 공통 예외를 한곳에서 모아 처리&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;2. @ResponseStatus 를 이용한 HTTP 상태 코드 기반 예외 처리&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.1 HTTP 상태 코드&lt;/b&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;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;8&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;상태 코드&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;상숫값&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;8,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0,0&quot;&gt;400&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;8,1,1,0&quot;&gt;BAD_REQUEST&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;8,1,2,0&quot;&gt;잘못된 요청일 때 사용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;8,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,2,0,0&quot;&gt;401&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;8,2,1,0&quot;&gt;UNAUTHORIZED&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;8,2,2,0&quot;&gt;클라이언트 인증 문제 발생 시 사용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;8,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,3,0,0&quot;&gt;403&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;8,3,1,0&quot;&gt;FORBIDDEN&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;8,3,2,0&quot;&gt;접근 권한이 없을 때 사용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;8,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,4,0,0&quot;&gt;404&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;8,4,1,0&quot;&gt;NOT_FOUND&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;8,4,2,0&quot;&gt;요청한 리소스가 없을 때 사용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;8,5,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,5,0,0&quot;&gt;405&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;8,5,1,0&quot;&gt;METHOD_NOT_ALLOWED&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;8,5,2,0&quot;&gt;지원하지 않는 HTTP 메서드 사용 시&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;8,6,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,6,0,0&quot;&gt;500&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;8,6,1,0&quot;&gt;INTERNAL_SERVER_ERROR&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;8,6,2,0&quot;&gt;서버 내부 로직 오류(API 오류) 발생 시&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;&lt;b&gt;2.2 @ResponseStatus로 예외 처리&lt;/b&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;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,0,0&quot;&gt;컨트롤러 메서드에 직접 사용&lt;/b&gt;: 특정 메서드 실행 시 예외가 발생하면 지정된 코드를 반환합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,1,0&quot;&gt;사용자 정의 예외 클래스에 사용&lt;/b&gt;: 특정 예외 클래스가 던져질 때마다 지정된 코드를 반환합니다. (권장)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1767054711861&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// [코드 예시 1] 메서드에 선언하는 경우

@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = &quot;요청에 실패했습니다.&quot;)
@GetMapping(&quot;/exam01&quot;)
public void requestMethod() {
    // 예외 발생 시 400 Bad Request 반환
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1767054728308&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// [코드 예시 2] 예외 클래스에 선언하는 경우
// 사용자 정의 예외를 만들고 어노테이션을 붙여두면, 어디서든 이 예외가 발생할 때 동일한 응답을 보낼 수 있습니다.

@GetMapping(&quot;/exam02&quot;)
public void requestMethod() throws Exception {
    // 예외 발생 시 
}



@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = &quot;찾을 수 없습니다&quot;)
public class UserException extends RuntimeException {
    public UserException(String message) {
        super(message);
    }
}&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;3.&lt;span&gt; @ExceptionHandler &lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;를 이용한 HTTP 상태 코드 기반 예외 처리&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-path-to-node=&quot;4&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.1 @ExceptionHandler: 컨트롤러 기반 예외 처리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;@ExceptionHandler는 특정 컨트롤러 클래스 내에서 발생하는 예외를 직접 잡아서 처리할 때 사용합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;6&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot;&gt;특징&lt;/b&gt;: 발생한 예외 정보를 직접 얻을 수 있고, 에러 전용 뷰 페이지로 이동시키거나 상세 메시지를 사용자에게 응답할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;사용법&lt;/b&gt;: 컨트롤러 내부에 별도의 메서드를 만들고 잡고 싶은 예외 클래스를 지정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1767055224860&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// [코드 예시] 컨트롤러 내 예외 처리

@Controller
public class Example03Controller {

    @GetMapping(&quot;/exam03&quot;)
    public void requestMethod() {
        throw new UserException(&quot;UserException 메시지입니다&quot;);
    }

    // UserException 발생 시 이 메서드가 실행됨
    @ExceptionHandler(UserException.class)
    public String handleException(UserException ex, Model model) {
        model.addAttribute(&quot;data1&quot;, ex.getMessage());
        model.addAttribute(&quot;data2&quot;, ex);
        return &quot;viewPage&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;4.&lt;span&gt;&lt;span&gt; @ControllerAdvice &lt;/span&gt;&lt;/span&gt;를 이용한 HTTP 상태 코드 기반 예외 처리&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-path-to-node=&quot;11&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4.1 @ControllerAdvice: 전역(Global) 예외 처리 &lt;/b&gt;&lt;/h4&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;애플리케이션 내의 여러 컨트롤러에서 발생하는 &lt;b&gt;예외를 한곳에서 공통으로 처리하고 싶을 때&lt;/b&gt; 사용합니다. 이를 통해 각 컨트롤러마다 예외 처리 코드를 중복해서 작성할 필요가 없어집니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;12&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot;&gt;범위 설정&lt;/b&gt;: basePackages 속성을 통해 특정 패키지 내의 컨트롤러만 관리하도록 설정할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0&quot;&gt;지원 기능&lt;/b&gt;: @ExceptionHandler뿐만 아니라 @ModelAttribute, @InitBinder가 선언된 메서드도 사용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@ControllerAdvice 주요 속성&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;요소&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;타입&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,1,0,0&quot;&gt;annotations&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,1,1,0&quot;&gt;Class&amp;lt;? extends Annotation&amp;gt;[]&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,1,2,0&quot;&gt;특정 애너테이션이 붙은 컨트롤러만 대상&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,2,0,0&quot;&gt;basePackages&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,2,1,0&quot;&gt;String[]&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,2,2,0&quot;&gt;지정한 패키지 및 하위 패키지 대상 (가장 많이 사용)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,3,0,0&quot;&gt;assignableTypes&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,3,1,0&quot;&gt;Class&amp;lt;?&amp;gt;[]&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;14,3,2,0&quot;&gt;특정 클래스 타입의 컨트롤러 대상&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1767055450675&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// [코드 예시] 전역 예외 처리 클래스

@ControllerAdvice(basePackages={&quot;com.springboot&quot;})
public class GlobalException {

    @ExceptionHandler(RuntimeException.class)
    private String handleErrorMessage(Exception ex, Model model) {
        model.addAttribute(&quot;data1&quot;, &quot;GlobalException 메시지입니다&quot;);
        model.addAttribute(&quot;data2&quot;, ex);
        return &quot;viewPage&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;5. [도서 쇼핑몰] 예외 처리 페이지 만들기&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;@ResponseStatus로 예외 처리하기&lt;/li&gt;
&lt;li&gt;@ExceptionHandler로 예외 처리하기&lt;/li&gt;
&lt;li&gt;@ControllerAdvice로 예외 처리하기&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: right;&quot; data-ke-size=&quot;size16&quot;&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;span&gt;&amp;nbsp;&lt;/span&gt;스프링부트 완전정복: 개념부터 실정 프로젝트까지 』길벗캠퍼스&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(2024).&lt;/p&gt;</description>
      <category>DEVELOPMENT/Spring</category>
      <author>Tiny Commit</author>
      <guid isPermaLink="true">https://marin-1104.tistory.com/169</guid>
      <comments>https://marin-1104.tistory.com/169#entry169comment</comments>
      <pubDate>Tue, 30 Dec 2025 09:47:40 +0900</pubDate>
    </item>
    <item>
      <title>[스프링부트 완전정복] 10장. 시큐리티 처리</title>
      <link>https://marin-1104.tistory.com/168</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKZwxy/dJMcai2YGAu/wstgZhWkUdT2rLockPpzo1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKZwxy/dJMcai2YGAu/wstgZhWkUdT2rLockPpzo1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKZwxy/dJMcai2YGAu/wstgZhWkUdT2rLockPpzo1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKZwxy%2FdJMcai2YGAu%2FwstgZhWkUdT2rLockPpzo1%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;1920&quot; height=&quot;1080&quot; data-filename=&quot;9.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;b&gt;1. 스프링 시큐리티의 개요&lt;/b&gt;&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;시큐리티: 허가된 사용자만 특정 웹페이지에 접근할 수 있도록 제한하는 보안 기능을 쉽게 구현할 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.1 스프링 시큐리티&lt;/b&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;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;사용자의 URL 접근 허용 또는 차단&lt;/li&gt;
&lt;li&gt;OAuth 구현&lt;/li&gt;
&lt;li&gt;통합 인증, 경량 디렉터리 액세스 프로포콜 구현&lt;/li&gt;
&lt;li&gt;역할 기반 액세스 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.2 기본 설정&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Build.gradle파일에 의존 라이브러리를 등록해야 합니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1765863978699&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
    implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;2. 시큐리티 세부 구성 설정&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1765864093660&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
public class 클래스 이름 {
    @Bean
    // 클래스 세부 사항
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시큐리티 구성에 주로 등록하는 빈 유형&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%; height: 126px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 20.6977%; height: 21px;&quot;&gt;유형&lt;/td&gt;
&lt;td style=&quot;width: 79.186%; height: 21px;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 20.6977%; height: 21px;&quot;&gt;UserDetailsService&lt;/td&gt;
&lt;td style=&quot;width: 79.186%; height: 21px;&quot;&gt;사용자 정보를 가져오는 역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 20.6977%; height: 21px;&quot;&gt;PasswordEncoder&lt;/td&gt;
&lt;td style=&quot;width: 79.186%; height: 21px;&quot;&gt;비밀번호를 암호화하는 역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;width: 20.6977%; height: 42px;&quot;&gt;SecurityFilterChain&lt;/td&gt;
&lt;td style=&quot;width: 79.186%; height: 42px;&quot;&gt;실제 보안 필터 체인을 구성하는 데 사용하며 여러 보안 필터가 어떤 순서로 실행될지 결정하고 보안과 관련한 요청 처리 담당&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 20.6977%; height: 21px;&quot;&gt;WebSecurityCustomizer&lt;/td&gt;
&lt;td style=&quot;width: 79.186%; height: 21px;&quot;&gt;보안 구성을 조정하고 보안 설정을 사용자 정의하는 데 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.1 접근 권한 설정&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;httpSecurity는 시큐리티 설정에 필요한 모든 기능을 제공하는 클래스&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 308px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 38.2558%;&quot;&gt;메서드&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 61.7442%;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 38.2558%;&quot;&gt;authorizeHttpRequests()&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 61.7442%;&quot;&gt;HTTP 요청 인가를 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 38.2558%;&quot;&gt;requestMatchers()&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 61.7442%;&quot;&gt;특정 리소스(경로, URL 패턴)에 대한 인가를 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 38.2558%;&quot;&gt;anyRequest()&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 61.7442%;&quot;&gt;모든 리소스에 대한 인가를 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 38.2558%;&quot;&gt;authenticated()&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 61.7442%;&quot;&gt;인증된 사용자의 접근을 허용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 38.2558%;&quot;&gt;fullyAuthenticated()&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 61.7442%;&quot;&gt;인증된 사용자의 접근은 허용, rememberMe 인증은 제외&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 38.2558%;&quot;&gt;permitAll()&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 61.7442%;&quot;&gt;인증 없이 모든 사용자의 접근을 허용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 38.2558%;&quot;&gt;denyAll()&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 61.7442%;&quot;&gt;무조건 접근을 허용하지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 38.2558%; height: 21px;&quot;&gt;anonymous()&lt;/td&gt;
&lt;td style=&quot;width: 61.7442%; height: 21px;&quot;&gt;익명 사용자의 접근을 허용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 38.2558%; height: 17px;&quot;&gt;rememberMe()&lt;/td&gt;
&lt;td style=&quot;width: 61.7442%; height: 17px;&quot;&gt;기억하기를 통해 인증된 사용자의 접근을 허용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 38.2558%; height: 17px;&quot;&gt;access(String)&lt;/td&gt;
&lt;td style=&quot;width: 61.7442%; height: 17px;&quot;&gt;지정된 SpEL 표현식에 적합하면 접근을 허용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 38.2558%; height: 17px;&quot;&gt;hasRole(String role)&lt;/td&gt;
&lt;td style=&quot;width: 61.7442%; height: 17px;&quot;&gt;지정된 역할을 가진 사용자의 접근을 허용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 38.2558%; height: 17px;&quot;&gt;hasAnyRole(String ... roles)&lt;/td&gt;
&lt;td style=&quot;width: 61.7442%; height: 17px;&quot;&gt;지정된 역할 중 어떤 것이라도 가진 사용자의 접근을 허용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 38.2558%; height: 17px;&quot;&gt;hasAuthority(String authority)&lt;/td&gt;
&lt;td style=&quot;width: 61.7442%; height: 17px;&quot;&gt;지정된 권한을 가진 사용자의 접근을 허용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 38.2558%; height: 17px;&quot;&gt;hasAnyAuthority(String ... authorities)&lt;/td&gt;
&lt;td style=&quot;width: 61.7442%; height: 17px;&quot;&gt;지정된 권한 중 어떤 것이라도 가진 사용자의 접근을 허용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 38.2558%; height: 17px;&quot;&gt;hasIpAddress(String)&lt;/td&gt;
&lt;td style=&quot;width: 61.7442%; height: 17px;&quot;&gt;지정된 IP로부터 요청이 있으면 접근을 허용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1765864523488&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        http.authorizeHttpRequests(
            authorizeHttpRequests -&amp;gt; authorizeHttpRequests
                .requestMatchers(&quot;/admin/**&quot;).hasAuthority(&quot;ROLE_ADMIN&quot;)   // 1
                .requestMatchers(&quot;/manager/**&quot;).hasRole(&quot;MANAGER&quot;)         // 2
                .requestMatchers(&quot;/member/**&quot;).authenticated()             // 3
                .anyRequest().permitAll()                                  // 4
        );

        return http.build();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-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 data-end=&quot;1331&quot; data-start=&quot;1233&quot;&gt;웹 요청 URL이 http://.../admin 또는 http://.../admin/main이면 ROLE_ADMIN 권한이 있는 사용자만 접근이 가능합니다.&lt;/li&gt;
&lt;li data-end=&quot;1432&quot; data-start=&quot;1332&quot;&gt;웹 요청 URL이 http://.../manager/ 또는 http://.../manager/main이면 MANAGER 권한이 있는 사용자만 접근이 가능합니다.&lt;/li&gt;
&lt;li data-end=&quot;1517&quot; data-start=&quot;1433&quot;&gt;웹 요청 URL이 http://.../member 또는 http://.../member/main이면 인증된 사용자만 접근이 가능합니다.&lt;/li&gt;
&lt;li data-is-last-node=&quot;&quot; data-end=&quot;1554&quot; data-start=&quot;1518&quot;&gt;그 외 요청은 모두 접근 허용(permitAll)입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.2 사용자 정보 설정&lt;/b&gt;&lt;/h4&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;/ul&gt;
&lt;pre id=&quot;code_1765865306172&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails admin = User.builder()
                .username(&quot;admin&quot;)      // 사용자 이름(계정)
                .password(&quot;admin123&quot;)   // 사용자 비밀번호
                .roles(&quot;ADMIN&quot;)         // 사용자 권한
                .build();

        return new InMemoryUserDetailsManager(admin);
    }
}&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.3 사용자 비밀번호의 암호화 설정&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자 정보 설정에서 사용자 비밀번호를 안전하게 저장할 수 있도록 비밀번호의 단방향 암호화를 지원하는 PasswordEncoder 인터페이스를 빈으로 등록합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1765865382320&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(); // BCrypt 해시 암호화 방식
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails admin = User.builder()
                .username(&quot;admin&quot;)
                .password(passwordEncoder().encode(&quot;admin123&quot;))
                .roles(&quot;ADMIN&quot;)
                .build();
        return new InMemoryUserDetailsManager(admin);
    }
}&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;pre id=&quot;code_1765865511213&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.springboot.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public UserDetailsService users() {
        UserDetails user = User.builder()
                .username(&quot;guest&quot;)
                .password(passwordEncoder().encode(&quot;g1234&quot;))
                .roles(&quot;USER&quot;)
                .build();

        UserDetails manager = User.builder()
                .username(&quot;manager&quot;)
                .password(passwordEncoder().encode(&quot;m1234&quot;))
                .roles(&quot;MANAGER&quot;)
                .build();

        UserDetails admin = User.builder()
                .username(&quot;admin&quot;)
                .password(passwordEncoder().encode(&quot;a1234&quot;))
                .roles(&quot;ADMIN&quot;)
                .build();

        return new InMemoryUserDetailsManager(user, manager, admin);
    }

    @Bean
    SecurityFilterChain examMethod01(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(authorize -&amp;gt; authorize
                .requestMatchers(&quot;/member/**&quot;).hasAnyRole(&quot;USER&quot;, &quot;ADMIN&quot;)
                .requestMatchers(&quot;/manager/**&quot;).hasRole(&quot;MANAGER&quot;)
                .requestMatchers(&quot;/admin/**&quot;).hasRole(&quot;ADMIN&quot;)
                .anyRequest().permitAll()
        )
        .formLogin(); // 기본 로그인 폼 사용

        return http.build();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20251216_151225408.jpg&quot; data-origin-width=&quot;1245&quot; data-origin-height=&quot;658&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpmB7e/dJMcaiaN8Iu/vCC4ATMKHqdAFI8NsxzHW0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpmB7e/dJMcaiaN8Iu/vCC4ATMKHqdAFI8NsxzHW0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpmB7e/dJMcaiaN8Iu/vCC4ATMKHqdAFI8NsxzHW0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpmB7e%2FdJMcaiaN8Iu%2FvCC4ATMKHqdAFI8NsxzHW0%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;1245&quot; height=&quot;658&quot; data-filename=&quot;KakaoTalk_20251216_151225408.jpg&quot; data-origin-width=&quot;1245&quot; data-origin-height=&quot;658&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;3. 사용자 정보 검색&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.1 컨트롤러에서 사용자 정보 검색&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용자 정보 검색 방법&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;381&quot; data-start=&quot;158&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;유형&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;232&quot; data-start=&quot;186&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;200&quot; data-start=&quot;186&quot;&gt;Principal&lt;/td&gt;
&lt;td data-end=&quot;232&quot; data-start=&quot;200&quot; data-col-size=&quot;md&quot;&gt;getName() 메서드로 사용자 정보 가져오기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;284&quot; data-start=&quot;233&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;252&quot; data-start=&quot;233&quot;&gt;Authentication&lt;/td&gt;
&lt;td data-end=&quot;284&quot; data-start=&quot;252&quot; data-col-size=&quot;md&quot;&gt;getName() 메서드로 사용자 정보 가져오기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;381&quot; data-start=&quot;285&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;314&quot; data-start=&quot;285&quot;&gt;@AuthenticationPrincipal&lt;/td&gt;
&lt;td data-end=&quot;381&quot; data-start=&quot;314&quot; data-col-size=&quot;md&quot;&gt;UserDetails를 구현한 클래스와 UserDetailsService를 사용할 때 사용자 정보 가져오기&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1765865809077&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Principal 인터페이스 사용 예

import java.security.Principal;

@GetMapping(&quot;/manager/user&quot;)
public String principalMethod(Principal principal, ...) {
    String username = principal.getName(); // 사용자 이름(계정) 가져오기
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1765865824858&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Authentication 인터페이스 사용 예 1

import org.springframework.security.core.Authentication;

@GetMapping(&quot;/manager/user&quot;)
public String authenticationMethod1(Authentication authentication, ...) {
    String username = authentication.getName(); // 사용자 이름(계정) 가져오기
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1765865841257&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Authentication 인터페이스 사용 예 2

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

@GetMapping(&quot;/manager/user&quot;)
public String authenticationMethod2(Authentication authentication, ...) {
    UserDetails userDetails = (UserDetails) authentication.getPrincipal();
    String username = userDetails.getUsername();   // 사용자 이름(계정)
    String password = userDetails.getPassword();   // 사용자 비밀번호
    String authority = userDetails.getAuthorities().toString(); // 사용자 권한
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1765865857632&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// @AuthenticationPrincipal 사용 예

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;

@GetMapping(&quot;/manager/user4&quot;)
public String annotationMethod(@AuthenticationPrincipal UserDetails userDetails, ...) {
    String username = userDetails.getUsername();   // 사용자 이름(계정)
    String password = userDetails.getPassword();   // 사용자 비밀번호
    String authority = userDetails.getAuthorities().toString(); // 사용자 권한
    ...
}&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.2 뷰 페이지에서 사용자 정보 검색&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1765865890488&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
    implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 사용자 역할 권한 태그: &amp;lt;sec:authorize&amp;gt; &lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1765865928907&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- 인가 메서드 --&amp;gt;
&amp;lt;HTML 태그 sec:authorize=&quot;인가메서드()&quot; /&amp;gt;



&amp;lt;!-- 사용자가 설정된 권한일 때 --&amp;gt;
&amp;lt;div sec:authorize=&quot;hasRole('ROLE_ADMIN')&quot;&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;!-- 사용자가 설정된 권한을 가지고 있지 않을 때 --&amp;gt;
&amp;lt;div sec:authorize=&quot;!hasRole('ROLE_ADMIN')&quot;&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;!-- 사용자가 둘 중 하나의 권한을 가질 때 --&amp;gt;
&amp;lt;div sec:authorize=&quot;hasAnyRole('ROLE_ADMIN','ROLE_MANAGER')&quot;&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;!-- 사용자가 로그인 할 때 --&amp;gt;
&amp;lt;div sec:authorize=&quot;isAuthenticated()&quot;&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;!-- 사용자가 로그인하지 않을 때 --&amp;gt;
&amp;lt;div sec:authorize=&quot;isAnonymous()&quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 사용자 인증 태그: &amp;lt;sec:authentication&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1765866004397&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;span sec:authentication=&quot;name&quot;/&amp;gt;           &amp;lt;!-- 사용자 이름 --&amp;gt;
&amp;lt;span sec:authentication=&quot;authorities&quot;/&amp;gt;    &amp;lt;!-- 사용자 권한 --&amp;gt;
&amp;lt;span sec:authentication=&quot;authenticated&quot;/&amp;gt;  &amp;lt;!-- 인증여부 --&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1765866036799&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.springboot.controller;

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

@Controller
public class Example04Controller {

    @GetMapping(&quot;/exam04&quot;)
    public String requestMethod(Model model) {
        return &quot;viewPage04&quot;;
    }

    @GetMapping(&quot;/admin/tag&quot;)
    public String requestMethod2(Model model) {
        return &quot;viewPage04&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1765866046969&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;html xmlns:sec=&quot;http://www.thymeleaf.org/extras/spring-security&quot;&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;Chap10&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h3&amp;gt;스프링 시큐리티&amp;lt;/h3&amp;gt;

&amp;lt;div sec:authorize=&quot;hasRole('ROLE_ADMIN')&quot;&amp;gt;
    &amp;lt;b&amp;gt;관리자 권한 화면입니다.&amp;lt;/b&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;div sec:authorize=&quot;isAuthenticated()&quot;&amp;gt;
    로그인 중입니다.
    &amp;lt;p&amp;gt;사용자 이름: &amp;lt;span sec:authentication=&quot;name&quot;/&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;사용자 권한: &amp;lt;span sec:authentication=&quot;authorities&quot;/&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;사용자 인증여부: &amp;lt;span sec:authentication=&quot;authenticated&quot;/&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;a href=&quot;/exam04&quot;&amp;gt;웹 요청 URL /exam04로 이동하기&amp;lt;/a&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;div sec:authorize=&quot;isAnonymous()&quot;&amp;gt;
    로그인 중이 아닙니다.
    &amp;lt;a href=&quot;/admin/tag&quot;&amp;gt;웹 요청 URL /admin/tag로 이동하기&amp;lt;/a&amp;gt;
&amp;lt;/div&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;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;b&gt;4. 로그인과 로그아웃 처리&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4.1 로그인 처리&lt;/b&gt;&lt;/h4&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본 인증 방식&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1765866152856&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain examMethod(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(authorize -&amp;gt; authorize
                .anyRequest().authenticated())
            .httpBasic(Customizer.withDefaults());
        return http.build();
    }
}&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;사용자의 username:password 를 Base64 인코딩해 Authorization 헤더에 담아 전송하는 인증 방식입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;폼 기반 인증 방식&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1765866417255&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain examMethod(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(authorize -&amp;gt; authorize
                .anyRequest().authenticated())
            .formLogin(Customizer.withDefaults()); // 로그인 성공 및 실패 처리
        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-end=&quot;1575&quot; data-start=&quot;1548&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;2031&quot; data-start=&quot;1577&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;메서드&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1655&quot; data-start=&quot;1608&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1624&quot; data-start=&quot;1608&quot;&gt;loginPage()&lt;/td&gt;
&lt;td data-end=&quot;1655&quot; data-start=&quot;1624&quot; data-col-size=&quot;sm&quot;&gt;기본 로그인 페이지 대신 커스텀 페이지 경로 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1718&quot; data-start=&quot;1656&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1681&quot; data-start=&quot;1656&quot;&gt;loginProcessingUrl()&lt;/td&gt;
&lt;td data-end=&quot;1718&quot; data-start=&quot;1681&quot; data-col-size=&quot;sm&quot;&gt;인증 처리 URL 설정 (form action 속성과 동일)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1768&quot; data-start=&quot;1719&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1743&quot; data-start=&quot;1719&quot;&gt;defaultSuccessUrl()&lt;/td&gt;
&lt;td data-end=&quot;1768&quot; data-start=&quot;1743&quot; data-col-size=&quot;sm&quot;&gt;로그인 성공 시 이동할 기본 경로 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1826&quot; data-start=&quot;1769&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1790&quot; data-start=&quot;1769&quot;&gt;successHandler()&lt;/td&gt;
&lt;td data-end=&quot;1826&quot; data-start=&quot;1790&quot; data-col-size=&quot;sm&quot;&gt;로그인 성공 후 추가 처리가 필요할 때 커스텀 핸들러 등록&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1883&quot; data-start=&quot;1827&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1844&quot; data-start=&quot;1827&quot;&gt;failureUrl()&lt;/td&gt;
&lt;td data-end=&quot;1883&quot; data-start=&quot;1844&quot; data-col-size=&quot;sm&quot;&gt;로그인 실패 시 이동할 경로 설정 (/login?error)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1931&quot; data-start=&quot;1884&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1905&quot; data-start=&quot;1884&quot;&gt;failureHandler()&lt;/td&gt;
&lt;td data-end=&quot;1931&quot; data-start=&quot;1905&quot; data-col-size=&quot;sm&quot;&gt;로그인 실패 시 커스텀 실패 핸들러 등록&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1982&quot; data-start=&quot;1932&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1956&quot; data-start=&quot;1932&quot;&gt;usernameParameter()&lt;/td&gt;
&lt;td data-end=&quot;1982&quot; data-start=&quot;1956&quot; data-col-size=&quot;sm&quot;&gt;로그인 폼에서 사용자 이름 파라미터 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2031&quot; data-start=&quot;1983&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2007&quot; data-start=&quot;1983&quot;&gt;passwordParameter()&lt;/td&gt;
&lt;td data-end=&quot;2031&quot; data-start=&quot;2007&quot; data-col-size=&quot;sm&quot;&gt;로그인 폼에서 비밀번호 파라미터 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1765866489691&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Bean
public SecurityFilterChain examMethod02(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(authorize -&amp;gt; authorize
            .anyRequest().authenticated())
        .formLogin(form -&amp;gt; form
            .loginPage(&quot;/login&quot;)               // ① 커스텀 로그인 페이지
            .loginProcessingUrl(&quot;/login&quot;)      // ② 인증 처리 요청 경로
            .defaultSuccessUrl(&quot;/admin&quot;)       // ③ 성공 시 이동 페이지
            .usernameParameter(&quot;username&quot;)     // ④ 사용자 이름 파라미터
            .passwordParameter(&quot;password&quot;)     // ⑤ 비밀번호 파라미터
            .failureUrl(&quot;/loginfailed&quot;)        // ⑥ 실패 시 이동 페이지
        );
    return http.build();
}&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;&lt;b&gt;4.2 로그아웃 처리&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;2984&quot; data-start=&quot;2734&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;메서드&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2801&quot; data-start=&quot;2765&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2785&quot; data-start=&quot;2765&quot;&gt;deleteCookies()&lt;/td&gt;
&lt;td data-end=&quot;2801&quot; data-start=&quot;2785&quot; data-col-size=&quot;sm&quot;&gt;로그아웃 시 쿠키 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2846&quot; data-start=&quot;2802&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2830&quot; data-start=&quot;2802&quot;&gt;invalidateHttpSession()&lt;/td&gt;
&lt;td data-end=&quot;2846&quot; data-start=&quot;2830&quot; data-col-size=&quot;sm&quot;&gt;세션 무효화 여부 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2881&quot; data-start=&quot;2847&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2863&quot; data-start=&quot;2847&quot;&gt;logoutUrl()&lt;/td&gt;
&lt;td data-end=&quot;2881&quot; data-start=&quot;2863&quot; data-col-size=&quot;sm&quot;&gt;로그아웃 요청 URL 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2925&quot; data-start=&quot;2882&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2905&quot; data-start=&quot;2882&quot;&gt;logoutSuccessUrl()&lt;/td&gt;
&lt;td data-end=&quot;2925&quot; data-start=&quot;2905&quot; data-col-size=&quot;sm&quot;&gt;로그아웃 성공 시 이동할 경로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2984&quot; data-start=&quot;2926&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2953&quot; data-start=&quot;2926&quot;&gt;logoutSuccessHandler()&lt;/td&gt;
&lt;td data-end=&quot;2984&quot; data-start=&quot;2953&quot; data-col-size=&quot;sm&quot;&gt;로그아웃 후 추가 작업 처리용 커스텀 핸들러 등록&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1765866520036&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Bean
public SecurityFilterChain examMethod03(HttpSecurity http) throws Exception {
    http.logout(logout -&amp;gt; logout
            .logoutUrl(&quot;/logout&quot;)             // 로그아웃 처리 요청 URL
            .logoutSuccessUrl(&quot;/login&quot;));     // 성공 후 이동할 페이지
    return http.build();
}&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;&lt;b&gt;5. [도서 쇼핑몰 도서 등록 페이지의 보안 처리하기]&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 접근 권한 설정하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 로그인 페이지와 로그인 처리 구현하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 로그아웃 처리 구현하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: right;&quot; data-ke-size=&quot;size16&quot;&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;span&gt;&amp;nbsp;&lt;/span&gt;스프링부트 완전정복: 개념부터 실정 프로젝트까지 』길벗캠퍼스&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(2024).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DEVELOPMENT/Spring</category>
      <author>Tiny Commit</author>
      <guid isPermaLink="true">https://marin-1104.tistory.com/168</guid>
      <comments>https://marin-1104.tistory.com/168#entry168comment</comments>
      <pubDate>Tue, 16 Dec 2025 15:30:29 +0900</pubDate>
    </item>
    <item>
      <title>[스프링부트 완전정복] 9.  유효성 검사</title>
      <link>https://marin-1104.tistory.com/155</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Brown and Beige Minimalist Modern illustrated Book Lover Presentation.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1uHd2/dJMcaaXVkiQ/kZirgFu6yfTtL74ZTJ7YT0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1uHd2/dJMcaaXVkiQ/kZirgFu6yfTtL74ZTJ7YT0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1uHd2/dJMcaaXVkiQ/kZirgFu6yfTtL74ZTJ7YT0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1uHd2%2FdJMcaaXVkiQ%2FkZirgFu6yfTtL74ZTJ7YT0%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;1920&quot; height=&quot;1080&quot; data-filename=&quot;Brown and Beige Minimalist Modern illustrated Book Lover Presentation.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;&lt;b&gt;1. 유효성 검사의 개요&lt;/b&gt;&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;폼데이터를 전달할때 유효하지 않은 값이 전송 될 수 있다. 이를 방지하기 위해&amp;nbsp;&lt;/li&gt;
&lt;li&gt;유효성 검사는 폼 페이지의 입력 값이 서버로 전송되기전 정해진 규칙에 따라 입력되어 있는지, 값이 타장한지 검사합니다.&amp;nbsp;&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;@Valid&lt;/li&gt;
&lt;li&gt;의존 라이브러리 등록&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1762744833511&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation 'org.springframework.boot:spring-boot-starter-validation'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 유효성 검사의 유형&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Bean Validation&lt;/b&gt;: 특정 도메인 클래스의 멤버 변수&amp;nbsp;&amp;rarr; 제약사항 애너테이션&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ConstraintValidation&lt;/b&gt;: 멤버 변수에 사용자 정의 애너테이션을 정의 &amp;rarr;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 유효성 검사 수행, 클래스 제약 조건 부여&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Validator:&lt;/b&gt; 스프링부트에서 제공하는 Validator 인터페이스 &amp;rarr;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 애플리케이션의 모든 계층에서 유효성 검사&lt;/li&gt;
&lt;li&gt;유효성 검사 폼데이터&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력 데이터가 null인가&lt;/li&gt;
&lt;li&gt;형식에 맞는가 (날짜, 이메일)&lt;/li&gt;
&lt;li&gt;나이를 숫자로 입력했는가&lt;/li&gt;
&lt;li&gt;입력 길이를 초과하는가&lt;/li&gt;
&lt;li&gt;로그인 인증 시 아이디와 패스워드가 유효한가&lt;/li&gt;
&lt;li&gt;회원가입 시 아이디가 중복되는가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;&lt;b&gt;2. Bean Validation을 이용한 유효성 검사&lt;/b&gt;&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;제약사항 애너테이션 선언으로 필드에 대한 유효성 검사&lt;/li&gt;
&lt;li&gt;@제약 사항 애너테이션 -&amp;gt; @Valid -&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;pre id=&quot;code_1762748777813&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class 클래스 이름 {
    @제약사항 애너페이션( 속성, [, message = &quot;오류 메시지 [오류 코드]&quot;])
    private String 멤버 변수;

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. Bean Validation 제약 조건 어노테이션 유형&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;유형&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;&lt;b&gt;주요 속성&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@AssertFalse&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 &lt;b&gt;거짓(false)&lt;/b&gt; 인지 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@AssertTrue&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 &lt;b&gt;참(true)&lt;/b&gt; 인지 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@DecimalMax&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 가질 수 있는 &lt;b&gt;최대 실수 값&lt;/b&gt; 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;value (값), inclusive (포함 여부)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@DecimalMin&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 가질 수 있는 &lt;b&gt;최소 실수 값&lt;/b&gt; 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;value (값), inclusive (포함 여부)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@Digits&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 가진 소수점 위치를 포함하는 정수 및 소수점 자리수 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;integer (정수 자리수), fraction (소수 자리수)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@Email&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;&lt;b&gt;이메일 형식&lt;/b&gt;인지 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@Future&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 &lt;b&gt;미래 날짜/시간&lt;/b&gt;인지 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@FutureOrPresent&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 &lt;b&gt;현재 또는 미래 날짜/시간&lt;/b&gt;인지 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@Max&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 가질 수 있는 &lt;b&gt;최대 정수 값&lt;/b&gt; 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;value (최대 값)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@Min&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 가질 수 있는 &lt;b&gt;최소 정수 값&lt;/b&gt; 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;value (최소 값)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@Negative&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 &lt;b&gt;음수&lt;/b&gt;인지 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@NegativeOrZero&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 &lt;b&gt;음수 또는 0&lt;/b&gt;인지 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@NotBlank&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 null이 아니고 &lt;b&gt;공백이 아닌 문자&lt;/b&gt;를 포함하는지 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@NotEmpty&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값(문자열, 컬렉션 등)이 &lt;b&gt;비어있지 않은지&lt;/b&gt; 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@NotNull&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 &lt;b&gt;null이 아닌지&lt;/b&gt; 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@Null&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 &lt;b&gt;Null인지&lt;/b&gt; 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@Past&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 &lt;b&gt;과거 날짜/시간&lt;/b&gt;인지 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@PastOrPresent&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 &lt;b&gt;과거 또는 현재 날짜/시간&lt;/b&gt;인지 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@Pattern&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 정의된 &lt;b&gt;정규 표현식에 일치&lt;/b&gt;하는지 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;regexp (정규 표현식)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@Positive&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 &lt;b&gt;양수&lt;/b&gt;인지 검사 (0 제외)&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@PositiveOrZero&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 &lt;b&gt;양수 또는 0&lt;/b&gt;인지 검사&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.5581%;&quot;&gt;&lt;b&gt;@Size&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.0465%;&quot;&gt;프로퍼티 값이 가질 수 있는 &lt;b&gt;최대/최소 크기&lt;/b&gt; 검사 (문자열 길이, 컬렉션 크기 등)&lt;/td&gt;
&lt;td style=&quot;width: 31.2791%;&quot;&gt;min (최소 값), max (최대 값)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 제약사항&amp;nbsp;어노테이션의&amp;nbsp;기본&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;제약사항을 위반하면 hibernate-validator-xxx.jar에서 제공하는 메시지 출력&lt;/li&gt;
&lt;li&gt;어노테이션들이 message 속성을 지정하지 않았을 때 &lt;b&gt;기본으로 출력하는 오류 메시&lt;/b&gt;&lt;b&gt;지&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;를 보여줍니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div id=&quot;model-response-message-contentr_ff644959eed2173c&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-ved=&quot;0CAAQ3ecQahgKEwi2hsKg0OaQAxUAAAAAHQAAAAAQpgU&quot; data-hveid=&quot;0&quot;&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;유형&lt;/td&gt;
&lt;td&gt;기본 메시지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@AssertFalse&lt;/td&gt;
&lt;td&gt;false여야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@AssertTrue&lt;/td&gt;
&lt;td&gt;true여야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@DecimalMax&lt;/td&gt;
&lt;td&gt;다음 값 이하여야 합니다(&lt;span data-math=&quot;{inclusive == true ? : ''}&quot;&gt;&lt;span&gt;&lt;span aria-hidden=&quot;true&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;l&lt;/span&gt;&lt;span&gt;u&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;u&lt;/span&gt;&lt;span&gt;e&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;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;&amp;prime;&amp;prime;&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;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;{value}).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@DecimalMin&lt;/td&gt;
&lt;td&gt;다음 값 이상이어야 합니다(&lt;span data-math=&quot;{inclusive == true ? : ''}&quot;&gt;&lt;span&gt;&lt;span aria-hidden=&quot;true&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;l&lt;/span&gt;&lt;span&gt;u&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;u&lt;/span&gt;&lt;span&gt;e&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;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;&amp;prime;&amp;prime;&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;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;{value}).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@Digits&lt;/td&gt;
&lt;td&gt;숫자 값이 한계를 초과합니다(&lt;span data-math=&quot;{integer}자리). (&quot;&gt;&lt;span&gt;&lt;span aria-hidden=&quot;true&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;g&lt;/span&gt;&lt;span&gt;er&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;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;{fraction}자리)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@Email&lt;/td&gt;
&lt;td&gt;올바른 형식의 이메일 주소여야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@Future&lt;/td&gt;
&lt;td&gt;미래 날짜여야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@FutureOrPresent&lt;/td&gt;
&lt;td&gt;현재 또는 미래 날짜여야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@Max&lt;/td&gt;
&lt;td&gt;${value} 이하여야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@Min&lt;/td&gt;
&lt;td&gt;${value} 이상이어야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@Negative&lt;/td&gt;
&lt;td&gt;0 미만이어야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@NegativeOrZero&lt;/td&gt;
&lt;td&gt;0 이하이어야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@NotBlank&lt;/td&gt;
&lt;td&gt;공백일 수 없습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@NotEmpty&lt;/td&gt;
&lt;td&gt;비어 있을 수 없습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@NotNull&lt;/td&gt;
&lt;td&gt;null이어서는 안됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@Null&lt;/td&gt;
&lt;td&gt;null이어야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@Past&lt;/td&gt;
&lt;td&gt;과거 날짜여야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@PastOrPresent&lt;/td&gt;
&lt;td&gt;과거 또는 현재 날짜여야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@Pattern&lt;/td&gt;
&lt;td&gt;${regexp}와 일치해야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@Positive&lt;/td&gt;
&lt;td&gt;0보다 커야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@PositiveOrZero&lt;/td&gt;
&lt;td&gt;0 이상이어야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@Size&lt;/td&gt;
&lt;td&gt;크기가 ${min}에서 ${max} 사이여야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1762749267091&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class 클래스 이름 {
    @NotEmpty
    @Size(min=4, max=10)
	@Min(value=0)
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1762749351925&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @PostMapping
    public String submitForm( @Valid @ModelAttribute(&quot;product&quot;) Product product, // @Valid로 검사 실행
            BindingResult bindingResult) {                     // 검사 결과를 받음

        if(bindingResult.hasErrors()) {
            // 유효성 검사 실패 시, 다시 폼 페이지를 호출 (에러 메시지는 Thymeleaf가 처리)
            return &quot;viewPage01&quot;;
        }
        
        // 유효성 검사 성공 시, 결과 페이지 호출
        return &quot;viewPage01_result&quot;; 
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1764057172929&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt; !--viewPage01.html--&amp;gt;
&amp;lt;body&amp;gt;
	&amp;lt;h3&amp;gt;유효성 검사&amp;lt;/h3&amp;gt;
	
	&amp;lt;form th:object=&quot;${product}&quot; action=&quot;/exam01&quot;  method=&quot;post&quot;&amp;gt;
		&amp;lt;p&amp;gt;품명 : &amp;lt;input type=&quot;text&quot;   th:field=&quot;*{name}&quot;&amp;gt; &amp;lt;span th:errors=&quot;*{name}&quot;&amp;gt;&amp;lt;/span&amp;gt;
		&amp;lt;p&amp;gt;가격 : &amp;lt;input type=&quot;text&quot;   th:field=&quot;*{price}&quot;&amp;gt; &amp;lt;span th:errors=&quot;*{price}&quot;&amp;gt;&amp;lt;/span&amp;gt;
		&amp;lt;p&amp;gt;&amp;lt;input type=&quot;submit&quot; value=&quot;확인&quot;/&amp;gt; 
		&amp;lt;input type=&quot;reset&quot;  value=&quot;취소&quot;/&amp;gt;
	&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;


//viewPage_result01.html
&amp;lt;body&amp;gt;
	&amp;lt;h3&amp;gt;유효성 검사&amp;lt;/h3&amp;gt;
	&amp;lt;p&amp;gt;품명 :  [[${product.name}]]
		&amp;lt;p&amp;gt;가격 :  [[${product.price}]]
		
&amp;lt;/body&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1764057200870&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt; !--viewPage01_result.html--&amp;gt;
&amp;lt;body&amp;gt;
	&amp;lt;h3&amp;gt;유효성 검사&amp;lt;/h3&amp;gt;
	&amp;lt;p&amp;gt;품명 :  [[${product.name}]]
	&amp;lt;p&amp;gt;가격 :  [[${product.price}]]
		
&amp;lt;/body&amp;gt;&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 제약사항 애너테이션에 사용자 정의 오류 메시지 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메시지 속성 값에 출력할 오류 메시지를 직접 정의한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오류코드 = 출력할 오류 메시지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1762749589660&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 제약사항 어노테이션.필드이름 = 출력할 오류 메시지 형식 사용
NotEmpty.product.name = 값을 입력해 주세요
Min.product.price = 0 이상의 값을 입력해 주세요


# 메시지 리소스 파일의 기본 이름 설정 (경로는 src/main/resources가 기본)
spring.messages.basename = messages
# 메시지 리소스 파일의 인코딩을 UTF-8로 설정 (한글 깨짐 방지)
spring.messages.encoding = UTF-8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1762749688162&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Product.java (메시지 리소스 파일 사용 시)
public class Product {
    
    // message를 생략하면 Spring이 messages.properties에서 키를 찾아 출력
    @NotEmpty 
    @Size(min=4, max=10, message=&quot;4자~10자 이내로 입력해 주세요&quot;) 
    private String name;

    @Min(value=0) // messages.properties의 Min.product.price 키와 매핑됨
    private int price;
}&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;gt; 도매인 클래스의 프로퍼티 값에 대한 중복 여부를 체크할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;( 독립적인 라이브러리이기 때문에, 기본적으로 DB나 외부 시스템에 접근하는 로직을 포함하고 있지 않습니다: 아이디 중복 검사 x)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;3. ConstraintValidator 인터페이스를 이용한 유효성 검사&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 사용자 정의 애너체이션 생성&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자 정의 애너테이션을 이용해 유효성 검사&lt;/li&gt;
&lt;li&gt;사용자 정의 애너테이션 선언, 유효성 검사, 오류 메시지&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1762750346884&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Target({ElementType.FIELD}) // 1. 애너테이션을 적용할 위치 (필드)
@Retention(RetentionPolicy.RUNTIME) // 2. 런타임까지 유지
@Constraint(validatedBy = {CustomValidator.class}) // 3. 검증 로직을 구현할 클래스 지정
public @interface CustomConstraint {

    // 4. 필수 요소: 오류 메시지 설정
    String message() default &quot;기본 사용자 정의 오류 메시지입니다.&quot;;

    // 5. 필수 요소: 유효성 검사 그룹 지정 (기본값)
    Class&amp;lt;?&amp;gt;[] groups() default {};

    // 6. 필수 요소: 페이로드 지정 (기본값)
    Class&amp;lt;? extends Payload&amp;gt;[] payload() default {};
    
    // 7. (선택적) 사용자 정의 속성 추가
    int minLength() default 5;
}&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;@Constraint : 제약사항을 생성하는 애너테이션&lt;/li&gt;
&lt;li&gt;@Retention : 지속 시간 결정&lt;/li&gt;
&lt;li&gt;@Target : 적용 위치 결정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 구현체 생성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ConstraintValidator 구현체 -&amp;gt; 유효성 검사 클래스&amp;nbsp;&lt;/li&gt;
&lt;li&gt;initialize() -&amp;gt; 사용자 정의 애너테이션 정보 초기화&lt;/li&gt;
&lt;li&gt;isValid() -&amp;gt; 유효성 검사 로직 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1762750624915&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// domain

// ConstraintValidator&amp;lt;애너테이션, 검사 대상 타입&amp;gt;
public class CustomValidator implements ConstraintValidator&amp;lt;CustomConstraint, String&amp;gt; {
    
    private int requiredMinLength;

    // 1. 초기화 메서드
    @Override
    public void initialize(CustomConstraint constraintAnnotation) {
        // 정의한 애너테이션의 속성값(여기선 minLength)을 가져와 초기화합니다.
        this.requiredMinLength = constraintAnnotation.minLength();
    }

    // 2. 검증 로직 메서드
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        // null 값은 @NotNull로 처리하는 것이 일반적이므로 여기서는 true 반환
        if (value == null) {
            return true;
        }
        
        // 실제 유효성 검사 로직
        // 입력된 문자열의 길이가 애너테이션에서 설정한 minLength보다 크거나 같으면 true 반환
        return value.length() &amp;gt;= requiredMinLength;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1762750707445&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class UserDto {
    
    // @CustomConstraint를 사용
    @CustomConstraint(minLength = 8, message = &quot;아이디는 최소 8자 이상이어야 합니다.&quot;)
    private String userId;

}&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;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;b&gt;4. Validator 인터페이스를 이용한 유효성 검사&lt;/b&gt;&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;Valudator인터페이스 구현체생성 -&amp;gt; @InitBinder메서드 추가 -&amp;gt; @Valid를 이용한 유효성 검사 -&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;Validator의 인터페이스 메서드
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;supports(Class&amp;lt;?&amp;gt; clazz): 지원 여부 확인:&amp;nbsp;
&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;(Product.class.isAssignableFrom(clazz) 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;validate(Object target, Errors errors):&amp;nbsp; 실제 검증 로직: 유효성 검사를 수행.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;target은 검사 대상 객체(DTO/Entity)이며,&lt;/li&gt;
&lt;li&gt;errors는 검사 실패 시 오류 정보(에러 코드, 메시지 등)를 담는 객체입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ValidationUtils의 메서드
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;rejectIfEmpty(Errors errors, String field, String errorCode):
&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;지정된 field의 값이 null이거나 빈 문자열 (&quot;&quot;)인 경우, errors 객체에 errorCode로 오류를 등록합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;rejectIfEmptyOrWhitespace(Errors errors, String field, String errorCode): 빈 값/공백 검사지정된 field의 값이 null, 빈 문자열, 또는 공백 문자(&quot; &quot;)로만 구성되어 있는 경우, errors 객체에 errorCode로 오류를 등록합니다. (가장 흔하게 사용)&lt;/li&gt;
&lt;li&gt;invokeValidator(Validator validator, Object target, Errors errors):
&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;다른 Validator 인스턴스를 호출하여 특정 객체에 대한 유효성 검사를 위임하는 데 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1762751307429&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import com.springboot.domain.Product;

public class ProductValidator implements Validator {

    // 1-1. supports(): 이 검증기가 어떤 클래스를 지원하는지 정의
    @Override
    public boolean supports(Class&amp;lt;?&amp;gt; clazz) {
        // Product 클래스 타입인지 확인
        return Product.class.isAssignableFrom(clazz);
    }

    // 1-2. validate(): 실제 유효성 검사 로직 구현
    @Override
    public void validate(Object target, Errors errors) {
        Product product = (Product) target;

        // **(1) ValidationUtils를 이용한 빈 값(NotBlank) 검사**
        // &quot;name&quot; 필드가 비어있으면 &quot;notBlank&quot; 에러 코드를 Errors 객체에 추가
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, &quot;name&quot;, &quot;notBlank&quot;);

        // **(2) 직접 코드를 이용한 특정 조건(Min/Max) 검사**
        if (product.getPrice() &amp;lt; 0 || product.getPrice() &amp;gt; 100000) {
            // &quot;price&quot; 필드가 조건에 맞지 않으면 &quot;priceRange&quot; 에러 코드를 Errors 객체에 추가
            errors.rejectValue(&quot;price&quot;, &quot;priceRange&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. @initBinder를 선언한 메서드 추가&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;@initBinder가 선언된 메서드에 Validator을 미리 등록하면 해당 컨트롤러의 모든 메서드에서 유효성 검사를 할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1762751651250&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.stereotype.Controller;
// ... (다른 import 생략)

@Controller
// @RequestMapping(&quot;/exam01&quot;)
public class Example01Controller {
    
    // 2. @InitBinder 메서드: 요청 처리 전에 실행되며 검증기를 WebDataBinder에 등록
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        // ProductValidator의 인스턴스를 생성하여 WebDataBinder에 추가
        binder.addValidators(new ProductValidator());
    }

    // ... (GetMapping, PostMapping 메서드)
}&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;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;&lt;b&gt;5. [도서 쇼핑몰] 도서 등록 데이터의 유효성 검사하기&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. Bean Validation으로 유효성 검사하기&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;의존 라이브러리 등록&lt;/li&gt;
&lt;li&gt;메시지 리소스 파일 작성 (message.properties)&lt;/li&gt;
&lt;li&gt;도서 클래스에 제약사항 애너테이션 설정 (Book.java)&lt;/li&gt;
&lt;li&gt;도서 등록 데이터의 우효성 검사 실행 (BookController)&lt;/li&gt;
&lt;li&gt;도서 등록 페이지 수정 (addBook.html)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. ConstraintValidator 인터페이스 유효성 검사하기&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메시지 리소스 파일 작성&lt;/li&gt;
&lt;li&gt;도서 클래스에 자용사 정의 애너테이션 설정 (Book.java)&lt;/li&gt;
&lt;li&gt;사용자 정의 애너페이션 생성 (BookId.java)&lt;/li&gt;
&lt;li&gt;도서 아디이의 유효성 검사 클래스 생성 (VookVAlidator.java)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. Validator 인터헤이스로 유효성 검사하기&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메시지 리소스 파일 설정&lt;/li&gt;
&lt;li&gt;제약사항의 유효성 검사 클래스 생성 (UnitslnStockValidator.java)&lt;/li&gt;
&lt;li&gt;도서 재고수의 유효성 검사 실행 (BookController.java)&lt;/li&gt;
&lt;li&gt;도서 등록 페이지 수정 (addBook.html)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 제약사항 애너테이션과 VAlidator 인터페이스를 연동한 유효성 검사하기&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;유효성 검사클래스 생성 (BookValidator.java)&lt;/li&gt;
&lt;li&gt;오류 메시지 가져오기 (Book.java)&lt;/li&gt;
&lt;li&gt;유효성 검사 실행 (BookController.java)&lt;/li&gt;
&lt;li&gt;유효성 검사 연동 (ValidationConfig.java)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;div style=&quot;color: #333333; text-align: start;&quot;&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #dddddd; color: #ee2323;&quot;&gt;&lt;b&gt;연습문제&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: right;&quot; data-ke-size=&quot;size16&quot;&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;span&gt;&amp;nbsp;&lt;/span&gt;스프링부트 완전정복: 개념부터 실정 프로젝트까지 』길벗캠퍼스&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(2024).&lt;/p&gt;
&lt;/div&gt;
&lt;div style=&quot;color: #333333; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div id=&quot;reaction-153&quot; data-tistory-react-app=&quot;Reaction&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>DEVELOPMENT/Spring</category>
      <author>Tiny Commit</author>
      <guid isPermaLink="true">https://marin-1104.tistory.com/155</guid>
      <comments>https://marin-1104.tistory.com/155#entry155comment</comments>
      <pubDate>Fri, 21 Nov 2025 15:25:35 +0900</pubDate>
    </item>
    <item>
      <title>[스프링부트 완전정복] 8. 다국어 처리</title>
      <link>https://marin-1104.tistory.com/154</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Brown and Beige Minimalist Modern illustrated Book Lover Presentation.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KHcTF/dJMcagRm6vw/3Okwk1KQZKGb3AAOOhkOK1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KHcTF/dJMcagRm6vw/3Okwk1KQZKGb3AAOOhkOK1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KHcTF/dJMcagRm6vw/3Okwk1KQZKGb3AAOOhkOK1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKHcTF%2FdJMcagRm6vw%2F3Okwk1KQZKGb3AAOOhkOK1%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;1920&quot; height=&quot;1080&quot; data-filename=&quot;Brown and Beige Minimalist Modern illustrated Book Lover Presentation.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;1. 다국어 처리의 개요&lt;/b&gt;&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;다양한 언어로 작성된 메시지를 웨 브라우저에 따라 각언어에 대당하는 메시지로 표현할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;2. MessageSource를 이용한 다국어 처리&lt;/b&gt;&lt;/span&gt;&lt;/h2&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;언어 대한 메시지 리소스 파일(.properties)을 생성하고 출력 메시지를 작성합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;key=value 쌍으로 messages.properties&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1762741912296&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 파일이름_ko.properties
Persom.form.Enter.message = 당신의 정보를 입력하세요.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 기본 환경 설정&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring Boot는 MessageSource라는 인터페이스를 제공하며, 이를 통해 messages.properties와 같은 메시지 리소스 파일에서 언어별 메시지를 가져와 출력합니다.&lt;/li&gt;
&lt;li&gt;웹 브라우저의 언어 설정(Locale)에 따라 다양한 언어의 메시지를 출력하는 것입니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한국어 환경: messages_ko.properties (예: Person.form.Enter.message = 당신의 정보를 입력하세요.)&lt;/li&gt;
&lt;li&gt;영어 환경: messages_en.properties (예: Person.form.Enter.message = Input your information)&lt;/li&gt;
&lt;li&gt;기본/기타 환경: messages.properties (기본값)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;별도의 빈(Bean) 등록 없이도 Spring Boot가 자동으로 ResourceBundleMessageSource를 등록하여 resources 폴더 아래의 messages.properties 파일을 읽어옵니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;Spring Boot가 기본으로 설정하는 MessageSource의 속성(파일명, 인코딩 등)을 변경하고 싶을 때 사용합니다.&lt;/li&gt;
&lt;li&gt;설정 파일: application.properties 또는 application.yml 파일에 설정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1762742788935&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// application.yml

spring:
  messages:
    # 1. 메시지 리소스 파일의 기본 이름 및 위치 설정
    # resources 폴더 하위 messages/i18n.properties 파일 등을 찾습니다.
    basename: messages/i18n 
    
    # 2. 메시지 리소스 파일의 인코딩을 UTF-8로 지정
    encoding: UTF-8 
    
    # 3. 메시지를 못 찾았을 때 시스템 로케일(Locale)을 사용할지 여부 (사용 안 함)
    fallbackToSystemLocale: false 
    
    # 4. MessageFormat 규칙을 항상 적용할지 여부 (적용)
    alwaysUseMessageFormat: true&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1762742811375&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// application.properties
spring.messages.basename=messages/i18n
spring.messages.encoding=UTF-8
spring.messages.fallbackToSystemLocale=false
spring.messages.alwaysUseMessageFormat=true&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;3. LocaleRecolver와&amp;nbsp; LocaleChangeInterceptor를 이용한 다국어 변경&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; 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;정의: 사용자의 언어와 지역(국가) 정보를 통합하여 나타내는 개념입니다. (예: ko_KR = 한국어, 대한민국)&lt;/li&gt;
&lt;li&gt;목적: MessageSource가 이 정보를 바탕으로 messages_ko.properties, messages_en.properties 등 적절한 리소스 파일을 선택하게 합니다.&lt;/li&gt;
&lt;li&gt;로케일 결정 유형: LocaleResolver
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;AcceptHeaderLocaleResolver: &lt;/b&gt;HTTP 요청 헤더의 Accept-Language 값(웹 브라우저 설정)을 이용하여 로케일을 결정합니다. 브라우저 설정에 맞는 언어를 자동으로 제공할 때&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CookieLocaleResolver: &lt;/b&gt;로케일 정보를 쿠키(Cookie)에 저장하여 사용자가 나중에 다시 접속해도 이전에 선택한 언어를 유지하게 합니다. 사용자 선택 언어를 장기간 보존할 때&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SessionLocaleResolver: &lt;/b&gt;로케일 정보를 사용자 세션(Session)에 저장하여 사용자가 웹을 사용하는 동안 언어를 유지하게 합니다. 예시 코드에서 사용된 유형이며, 세션이 만료되면 정보가 사라집니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;FixedLocaleResolver: &lt;/b&gt;로케일을 특정 언어로 고정합니다. (예: 무조건 영어)특정 지역/언어만 지원하는 서비스나 테스트용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1762743352873&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class MessageConfig implements WebMvcConfigurer {

    @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver resolver = new SessionLocaleResolver();
        // 애플리케이션의 기본 로케일을 영어(Locale.ENGLISH)로 설정
        resolver.setDefaultLocale(Locale.ENGLISH); 
        return resolver;
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 로케일 변경&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 URL 파라미터를 통해 로케일을 동적으로 변경할 수 있도록 하는 설정입니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 요청의 파라미터에 로케일 값을 전달하여 손쉽게 로케일을 바꿀 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;http://localhost:8080/exam04?language=ko로 요청하면 로케일이 한국어로 변경됩니다.&lt;/li&gt;
&lt;li&gt;http://localhost:8080/exam04?language=en로 요청하면 로케일이 영어로 변경됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1762743487923&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;
    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
        // 로케일을 변경할 때 사용할 URL 파라미터 이름을 &quot;language&quot;로 지정
        interceptor.setParamName(&quot;language&quot;);
        return interceptor;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1762743578732&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;a href=&quot;?language=ko&quot;&amp;gt;Korean&amp;lt;/a&amp;gt;|&amp;lt;a href=&quot;?language=en&quot;&amp;gt;English&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;4. [도서 쇼핑몰] 도서 등록 페이지의 다국어 처리하기&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; 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;MessageSource의 사용자 환경 설정 (application.properties)&lt;/li&gt;
&lt;li&gt;메시지 리소스 파일 작성 (message_ko.properties)&lt;/li&gt;
&lt;li&gt;도서 등록 페이지의 메시지 출력 (addBook.html)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 다국어 변경을 위한 빈 객체 등록&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다국어 변경을 위한 빈 객체 등록 (MeddageConfig)&lt;/li&gt;
&lt;li&gt;도서 등록 페이지에 요청 파라미터 추가 (addBook.html)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #dddddd; color: #ee2323;&quot;&gt;&lt;b&gt;연습문제&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: right;&quot; data-ke-size=&quot;size16&quot;&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;span&gt;&amp;nbsp;&lt;/span&gt;스프링부트 완전정복: 개념부터 실정 프로젝트까지 』길벗캠퍼스&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(2024).&lt;/p&gt;</description>
      <category>DEVELOPMENT/Spring</category>
      <author>Tiny Commit</author>
      <guid isPermaLink="true">https://marin-1104.tistory.com/154</guid>
      <comments>https://marin-1104.tistory.com/154#entry154comment</comments>
      <pubDate>Sat, 15 Nov 2025 13:04:53 +0900</pubDate>
    </item>
    <item>
      <title>[REACT] 반응형 웹 구현하기 (react-responsive)</title>
      <link>https://marin-1104.tistory.com/156</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GUSpj/dJMcahQiN0x/Xu5waTv08aww78yetHYg10/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GUSpj/dJMcahQiN0x/Xu5waTv08aww78yetHYg10/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GUSpj/dJMcahQiN0x/Xu5waTv08aww78yetHYg10/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGUSpj%2FdJMcahQiN0x%2FXu5waTv08aww78yetHYg10%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;1920&quot; height=&quot;1080&quot; data-filename=&quot;5.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;b&gt;1. 반응형 웹이란?&lt;/b&gt;&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;PC, 태블릿, 모바일 등 다양한 화면 크기에서 보기 좋게 보이도록&amp;nbsp;&lt;/li&gt;
&lt;li&gt;다양한 디바이스로 접근이 가능&lt;/li&gt;
&lt;li&gt;사용자 편의성이 높은 UI&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;2. REACT 시작&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 패키지 설치&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763036252223&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install react-responsive # npm
yarn add react-responsive    # yarn&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 기본 사용법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;react-responsive는 Media Query를 컴포넌트로 사용할 수록 도와주는 라이브러리입니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;모바일 화면에서는 특정 컴포넌트를 숨기고, 데스트답에서 다른 컴포넌트를 보여주고 싶을 때 유용하게 쓰인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763036383696&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from &quot;react&quot;;
import { useMediaQuery } from &quot;react-responsive&quot;;

const ResponsiveExample = () =&amp;gt; {
  const isDesktop = useMediaQuery({ minWidth: 1024 });
  const isTablet = useMediaQuery({ minWidth: 768, maxWidth: 1023 });
  const isMobile = useMediaQuery({ maxWidth: 767 });

  return (
    &amp;lt;div&amp;gt;
      {isDesktop &amp;amp;&amp;amp; &amp;lt;h2&amp;gt; 데스크탑 화면입니다.&amp;lt;/h2&amp;gt;}
      {isTablet &amp;amp;&amp;amp; &amp;lt;h2&amp;gt; 태블릿 화면입니다.&amp;lt;/h2&amp;gt;}
      {isMobile &amp;amp;&amp;amp; &amp;lt;h2&amp;gt; 모바일 화면입니다.&amp;lt;/h2&amp;gt;}
    &amp;lt;/div&amp;gt;
  );
};

export default ResponsiveExample;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 컴포넌트 분리하기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763036415170&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useMediaQuery } from &quot;react-responsive&quot;;

export const Desktop = ({ children }) =&amp;gt; {
  const isDesktop = useMediaQuery({ minWidth: 1024 });
  return isDesktop ? children : null;
};

export const Tablet = ({ children }) =&amp;gt; {
  const isTablet = useMediaQuery({ minWidth: 768, maxWidth: 1023 });
  return isTablet ? children : null;
};

export const Mobile = ({ children }) =&amp;gt; {
  const isMobile = useMediaQuery({ maxWidth: 767 });
  return isMobile ? children : null;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763036427695&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from &quot;react&quot;;
import { Desktop, Tablet, Mobile } from &quot;./Responsive&quot;;

const App = () =&amp;gt; (
  &amp;lt;&amp;gt;
    &amp;lt;Desktop&amp;gt;데스크탑 전용 페이지&amp;lt;/Desktop&amp;gt;
    &amp;lt;Tablet&amp;gt;태블릿 전용 페이지&amp;lt;/Tablet&amp;gt;
    &amp;lt;Mobile&amp;gt;모바일 전용 페이지&amp;lt;/Mobile&amp;gt;
  &amp;lt;/&amp;gt;
);

export default App;&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DEVELOPMENT/react</category>
      <author>Tiny Commit</author>
      <guid isPermaLink="true">https://marin-1104.tistory.com/156</guid>
      <comments>https://marin-1104.tistory.com/156#entry156comment</comments>
      <pubDate>Thu, 13 Nov 2025 21:21:44 +0900</pubDate>
    </item>
    <item>
      <title>[스프링부트 완전정복] 7. 파일 업로드 처리</title>
      <link>https://marin-1104.tistory.com/153</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xLVom/dJMcahpc0Yn/EqFsxdhVXpj9WnDIfvl6bK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xLVom/dJMcahpc0Yn/EqFsxdhVXpj9WnDIfvl6bK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xLVom/dJMcahpc0Yn/EqFsxdhVXpj9WnDIfvl6bK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxLVom%2FdJMcahpc0Yn%2FEqFsxdhVXpj9WnDIfvl6bK%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;1920&quot; height=&quot;1080&quot; data-filename=&quot;9.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;1. 파일 업로드 개요&lt;/b&gt;&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;파일 업로드: 파일을 웹 브라우저에 서버로 전송하여 저장하는 것&lt;/li&gt;
&lt;li&gt;텍스트 파일, 바이너리 파일, 이미지 파일, 문서 파일&lt;/li&gt;
&lt;li&gt;웹 페이지에서 폼태그를 사용하고, 파일을 서버로 업로드.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;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;별도의 라이브러리 추가 없이 application.properties파일에 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1762734250956&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring.servlet.multipart.enabled=true			# 멀티 파트 업로드 지원 여부
spring.servlet.multipart.file-size-threshold=2KB	# 파일이 메모리에 기록되는 임계값
spring.servlet.multipart.location=C:/upload		# 업로드된 파일의 임시 저장 공간
spring.servlet.multipart.max-file-size=200MB		# 파일의 최대 크기
spring.servlet.multipart.max-request-size=215MB		# 요청의 최대 크기&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 파일 업로드를 위한 웹 페이지&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹페이지 -&amp;gt; 서버로 전송하는 &lt;u&gt;폼페이지&lt;/u&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Method는 POST&lt;/li&gt;
&lt;li&gt;enctype은 multipart/form-data&lt;/li&gt;
&lt;li&gt;action은 실행 파일&lt;/li&gt;
&lt;li&gt;type은 file설정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1762734584287&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;form method=&quot;post&quot; enctype=&quot;multipart/form-data&quot; action=&quot;viewPage_process.html&quot;&amp;gt;
    &amp;lt;p&amp;gt;제목 : &amp;lt;input type=&quot;text&quot; name=&quot;title&quot;&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;파일 : &amp;lt;input type=&quot;file&quot; name=&quot;fileName&quot;&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;&amp;lt;input type=&quot;submit&quot; value=&quot;submit&quot;&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;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;ResourceHttpRequestHandler 클래스: 파일 시스템 접근
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;/static&lt;/li&gt;
&lt;li&gt;/public&lt;/li&gt;
&lt;li&gt;/resources&lt;/li&gt;
&lt;li&gt;/META-INF/resources&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ResourceHandlerRegistry : 정적 리소스가 저장된 WAR, 파일 시스템 접근
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WAR: 리소스가 저장됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1762734990746&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry
            .addResourceHandler(&quot;/resources/**&quot;) //외부 URI
            .addResourceLocations(&quot;/resources/&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;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;&lt;b&gt;2. MultupartFile을 이용한 파일 업로드 처리&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. MultipartFile을 이용한 파일 업로드 처리&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;업로드된 파일과 파일 데이터를 표현할 때 사용&lt;/li&gt;
&lt;li&gt;단일, 다중 업로드 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;메서드&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;타입&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;getName()&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;멀티 파트 폼에서 파라미터 이름을 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;getContentType()&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;파일의 콘텐츠 형식을 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;getOriginalFilename()&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;클라이언트의 파일 시스템에서 실제 파일 이름을 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;isEmpty()&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;업로드한 파일의 존재 여부를 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;getSize()&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;long&lt;/td&gt;
&lt;td&gt;바이트의 파일 크기를 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;getBytes()&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;byte[]&lt;/td&gt;
&lt;td&gt;바이트의 배열로 파일 내용을 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;getInputStream()&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;InputStream&lt;/td&gt;
&lt;td&gt;파일 폼의 내용을 읽어 &lt;b&gt;InputStream&lt;/b&gt;으로 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;transferTo(File dest)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;전송된 파일을 지정한 대상 파일로 전송&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 파일 업로드 유형&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;MultipartFileServletRequest 인터페이스 이용하기&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;우선 HttpServletRequest(getParameter()), MultipartRequest(getFile())&amp;nbsp;인터페이스를 상속받아야 한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt; 요청 전체를 받아 필요한 파라미터와 파일을 직접 꺼내야 합니다. (request.getParameter(), request.getFile()) &lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1762735911793&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Controller
@RequestMapping(&quot;/exam01&quot;)
public class Example01Controller {

    @GetMapping(&quot;/form&quot;)
    public String requestForm() {
        return &quot;viewPage&quot;;
    }

    @PostMapping(&quot;/form&quot;)
    public String submitForm(MultipartHttpServletRequest request, Model model) {
        // 1. 폼 페이지의 요청 파라미터 name의 값을 전송받습니다.
        String name = request.getParameter(&quot;name&quot;);
        
        // 2. 폼 페이지의 요청 파라미터 fileImage의 파일을 전송받습니다.
        MultipartFile file = request.getFile(&quot;fileImage&quot;);
        
        String filename = file.getOriginalFilename();
        File saveFile = new File(&quot;C:\\upload\\&quot; + name + &quot;_&quot; + filename);

        try {
            // 3. 파일을 업로드합니다.
            file.transferTo(saveFile);
            
            model.addAttribute(&quot;data1&quot;, &quot;MultipartHttpServletRequest 예제&quot;);
            model.addAttribute(&quot;data2&quot;, filename);
            model.addAttribute(&quot;data3&quot;, saveFile.getName());
        } catch (IOException e) {
            e.printStackTrace();
        }

        return &quot;viewPage_process&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@RequestParam&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;멀티 파트 요청이 들어오면 @RequestParam과 MultipartFile 타입의 매개변수를 사용해야 한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt; 파일을 포함한 각 폼 데이터를 @RequestParam을 이용해 개별적으로 필요한 타입(String, MultipartFile)으로 주입받음.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1762736150642&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Controller
@RequestMapping(&quot;/exam02&quot;)
public class Example02Controller {

    @GetMapping(&quot;/form&quot;)
    public String requestForm() {
        return &quot;viewPage&quot;;
    }

    @PostMapping(&quot;/form&quot;)
    public String submitForm(
        @RequestParam(&quot;name&quot;) String name, 
        @RequestParam(&quot;fileImage&quot;) MultipartFile file, 
        Model model
    ) {
        String filename = file.getOriginalFilename();
        // C:\\upload\\ 경로와 파일 이름 사이에 &quot;_&quot;가 추가된 것
        File saveFile = new File(&quot;C:\\upload\\&quot; + name + &quot;_&quot; + filename); 

        try {
            // 파일 업로드
            file.transferTo(saveFile);
            
            model.addAttribute(&quot;data1&quot;, &quot;@RequestParam 예제&quot;);
            model.addAttribute(&quot;data2&quot;, filename);
            model.addAttribute(&quot;data3&quot;, saveFile.getName());
        } catch (IOException e) {
            e.printStackTrace();
        }

        return &quot;viewPage_process&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; @ModelAttribute &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;매개변수와 동일한 이름으로 커맨드 객체 내에 MultipartFile타입의 프로퍼티를 추가합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;요청 메서드는 @ModelAttribute를 선언하여 업로드할 파일을 포함한 요청 파라미터를 전송받음.&lt;/li&gt;
&lt;li&gt;&lt;b&gt; 폼 데이터와 파일을 객체(Member 클래스)로 한 번에 묶어 받고 싶을 때 사용합니다. &lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1762736647949&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 커맨드 객체
package com.springboot.domain;

import org.springframework.web.multipart.MultipartFile;
import lombok.Data; // Lombok Data 어노테이션 사용

@Data // Getter, Setter, toString 등을 자동 생성
public class Member {

    private String name;
    private MultipartFile fileImage;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1762736704208&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//컨트롤러
...
import com.springboot.domain.Member; // 커맨드 객체 import
import java.io.File;
import java.io.IOException;

@Controller
@RequestMapping(&quot;/exam03&quot;)
public class Example03Controller {

    @GetMapping(&quot;/form&quot;)
    public String requestForm(Member member) { // @ModelAttribute가 적용
        return &quot;viewPage&quot;;
    }

    @PostMapping(&quot;/form&quot;)
    public String submitForm(@ModelAttribute Member member, Model model) {
        // 1. 폼 페이지의 요청 파라미터 name의 값을 전송받습니다.
        String name = member.getName();
        
        // 2. 폼 페이지의 요청 파라미터 fileImage의 파일을 전송받습니다.
        MultipartFile file = member.getFileImage();
        
        String filename = file.getOriginalFilename();
        File saveFile = new File(&quot;C:\\upload\\&quot; + name + &quot;_&quot; + filename);

        try {
            // 3. 파일을 업로드합니다.
            file.transferTo(saveFile);
            
            model.addAttribute(&quot;data1&quot;, &quot;@ModelAttribute 예제&quot;);
            model.addAttribute(&quot;data2&quot;, filename);
            model.addAttribute(&quot;data3&quot;, saveFile.getName());
        } catch (IOException e) {
            e.printStackTrace();
        }

        return &quot;viewPage_process&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;외부 파일 시스템 접근 설정&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1762736820409&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.springboot.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class ResourceConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry
            // URL 패턴: &quot;/images/**&quot;로 요청이 오면
            .addResourceHandler(&quot;/images/**&quot;)
            // C 드라이브의 &quot;/upload/&quot; 디렉토리에서 파일을 찾습니다.
            .addResourceLocations(&quot;file:///C:/upload/&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;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;&lt;b&gt;3. [도서 쇼핑몰 도서 이미지 업로드 처리하기]&lt;/b&gt;&lt;/span&gt;&lt;/h2&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;정적 리소스 관리 폴더 생성 (src/main/resource/static)&lt;/li&gt;
&lt;li&gt;도서 클래스 멤버 변수 추가 (Book.java)&lt;/li&gt;
&lt;li&gt;도서 데이터 저장소 클래스 수정 (BookRepositoryImpl)&lt;/li&gt;
&lt;li&gt;도서 목록 페이지 수정 (books.html)&lt;/li&gt;
&lt;li&gt;도서 상세 정보 페이지 수정 (book.html)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 도서 이미지 업로드하기&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도서 이미지 파일의 저장 위치 생성&lt;/li&gt;
&lt;li&gt;파일 업로드 저장 위치 설정 (application.properties)&lt;/li&gt;
&lt;li&gt;파일 시스템에 저장된 이미지에 접근하는 클래스 생성 (ResourceConfig 클래스 생성)&lt;/li&gt;
&lt;li&gt;도서 클래스에 멤버 변수 추가 (Book.java)&lt;/li&gt;
&lt;li&gt;도서 이미지 파일 업로드 (BookController)&lt;/li&gt;
&lt;li&gt;도서 등록 페이지 수정 (addBook.html)&lt;/li&gt;
&lt;/ul&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;도서 이미지 파일 다운로드 (BookController)&lt;/li&gt;
&lt;li&gt;도서 정보 상세 페이지 수정 (book.html)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style8&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #dddddd; color: #ee2323;&quot;&gt;&lt;b&gt;연습문제&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;01.&lt;/b&gt;다음 중 파일 업로드를 위한 폼 태그의 규칙으로 옳지 않은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;②&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;02.&lt;/b&gt;다음 중 파일을 업로드하는 방법으로 사용되는 클래스(또는 인터페이스)로 옳은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;①&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;03.&lt;/b&gt;다음 중 컨트롤러에 멀티 파트 요청으로 들어오는 업로드된 파일 데이터를 전달받기 위해 MultipartFile 인터페이스와 함께 사용할 수 있는 애너테이션으로 옳은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;④&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;04.&lt;/b&gt;다음 중 MultipartHttpServletRequest에서 MultipartFile 데이터를 전달받는 메서드로 옳은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;①&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;05.&lt;/b&gt;다음 중 MultipartFile 인터페이스의 메서드에 대한 설명으로 옳지 않은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;④&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;06.&lt;/b&gt;다음 중 1에 지정하는 클래스로 옳은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;①&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;07.&lt;/b&gt;다음 중 2에 지정하는 파일을 업로드하는 메서드로 옳은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;④&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;08.&lt;/b&gt;다음 중 앞의 예제에 따라 application.properties 파일에 업로드할 파일의 저장 경로 설정에 해당하는 구문으로 옳은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;③&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;09.&lt;/b&gt;다음 중 앞의 예제에 따라 application.properties 파일에 업로드할 파일의 최대 크기를 200MB로 설정하는 구문으로 옳은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;①&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;10.&lt;/b&gt;다음 중 앞의 예제에 따라 윈도우 운영체제에서 빈칸에 들어가는 정적 리소스 위치 설정에 해당하는 구문으로 옳은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;③&lt;/b&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: right;&quot; data-ke-size=&quot;size16&quot;&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;span&gt;&amp;nbsp;&lt;/span&gt;스프링부트 완전정복: 개념부터 실정 프로젝트까지 』길벗캠퍼스&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(2024).&lt;/p&gt;</description>
      <category>DEVELOPMENT/Spring</category>
      <author>Tiny Commit</author>
      <guid isPermaLink="true">https://marin-1104.tistory.com/153</guid>
      <comments>https://marin-1104.tistory.com/153#entry153comment</comments>
      <pubDate>Mon, 10 Nov 2025 12:13:29 +0900</pubDate>
    </item>
    <item>
      <title>[스프링부트 완전정복] 6. 폼 태그</title>
      <link>https://marin-1104.tistory.com/150</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coWyX6/dJMcabbqlJx/uBsU8LUR9ojkAwXX9Wm7Xk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coWyX6/dJMcabbqlJx/uBsU8LUR9ojkAwXX9Wm7Xk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coWyX6/dJMcabbqlJx/uBsU8LUR9ojkAwXX9Wm7Xk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcoWyX6%2FdJMcabbqlJx%2FuBsU8LUR9ojkAwXX9Wm7Xk%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;1920&quot; height=&quot;1080&quot; data-filename=&quot;9.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;&lt;b&gt;1. 폼 태그 개요&lt;/b&gt;&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;폼태그: 사용가의 입력이나 선택 정보를 웹 서보로 전달하는 폼태그&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;입력 받은 데이터를 서버로 전송하고 서버는 데이터를 처리하고 결과에 따라 다른 웹 페이지로 보여준다.&lt;/li&gt;
&lt;li&gt;사용자와의 상호작용: 사용자가 뭘 원하는지 파악 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8YYA2/dJMcaaKlYRG/Hxb8R19OM2tnXwGtZxKV6K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8YYA2/dJMcaaKlYRG/Hxb8R19OM2tnXwGtZxKV6K/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1151&quot; data-origin-height=&quot;463&quot; data-filename=&quot;KakaoTalk_20251103_012358375_01.jpg&quot; style=&quot;width: 47.6564%; margin-right: 10px;&quot; data-widthpercent=&quot;48.22&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8YYA2/dJMcaaKlYRG/Hxb8R19OM2tnXwGtZxKV6K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8YYA2%2FdJMcaaKlYRG%2FHxb8R19OM2tnXwGtZxKV6K%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;1151&quot; height=&quot;463&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDC8B0/dJMcacuDriG/X1JKyYjpgX2vjufv5k0v20/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDC8B0/dJMcacuDriG/X1JKyYjpgX2vjufv5k0v20/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;424&quot; data-filename=&quot;KakaoTalk_20251103_012358375.jpg&quot; style=&quot;width: 51.1808%;&quot; data-widthpercent=&quot;51.78&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDC8B0/dJMcacuDriG/X1JKyYjpgX2vjufv5k0v20/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDC8B0%2FdJMcacuDriG%2FX1JKyYjpgX2vjufv5k0v20%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;1132&quot; height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. &amp;lt;form&amp;gt; 태그 사용법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;form 태그의 속성&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 147px;&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: 21px;&quot;&gt;
&lt;td style=&quot;width: 28.4884%; height: 21px;&quot;&gt;&lt;b&gt;속성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 71.3953%; height: 21px;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 28.4884%; height: 21px;&quot;&gt;&lt;b&gt;action&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 71.3953%; height: 21px;&quot;&gt;데이터를 받아 처리하는 웹 페이지의 &lt;b&gt;URL 설정&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 28.4884%; height: 21px;&quot;&gt;&lt;b&gt;method&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 71.3953%; height: 21px;&quot;&gt;데이터를 전송하는 &lt;b&gt;HTTP 방식 설정&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 28.4884%; height: 21px;&quot;&gt;&lt;b&gt;name&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 71.3953%; height: 21px;&quot;&gt;&lt;b&gt;폼을 식별하는 이름 설정&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 28.4884%; height: 21px;&quot;&gt;&lt;b&gt;target&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 71.3953%; height: 21px;&quot;&gt;폼 처리 결과를 응답할 &lt;b&gt;프레임 설정&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 28.4884%; height: 21px;&quot;&gt;&lt;b&gt;enctype&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 71.3953%; height: 21px;&quot;&gt;폼을 전송하는 &lt;b&gt;콘텐츠 MIME 유형 설정&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 28.4884%; height: 21px;&quot;&gt;&lt;b&gt;accept-charset&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 71.3953%; height: 21px;&quot;&gt;폼 전송에 사용할 &lt;b&gt;문자 인코딩 설정&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;입력 태그 양식&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span data-math=&quot;\mathbf{&amp;lt;input&amp;gt;}&quot;&gt;&amp;lt;input&lt;/span&gt;&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3721%;&quot;&gt;&lt;b&gt;속성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20.6977%;&quot;&gt;&lt;b&gt;값&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3721%;&quot; rowspan=&quot;9&quot;&gt;&lt;b&gt;type&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20.6977%;&quot;&gt;&lt;b&gt;text&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;&lt;b&gt;한 줄 텍스트 입력&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.6977%;&quot;&gt;&lt;b&gt;radio&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;&lt;b&gt;라디오 버튼&lt;/b&gt;으로 열거된 것 중 &lt;b&gt;하나만 선택&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.6977%;&quot;&gt;&lt;b&gt;checkbox&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;&lt;b&gt;체크 박스&lt;/b&gt;로 열거된 것 중 &lt;b&gt;여러 개 선택&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.6977%;&quot;&gt;&lt;b&gt;password&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;&lt;b&gt;암호 입력&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.6977%;&quot;&gt;&lt;b&gt;hidden&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;보이지 않고 &lt;b&gt;숨겨서 전송&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.6977%;&quot;&gt;&lt;b&gt;file&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;&lt;b&gt;파일 업로드를 위한 파일 선택&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.6977%;&quot;&gt;&lt;b&gt;button&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;&lt;b&gt;버튼 모양 출력&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.6977%;&quot;&gt;&lt;b&gt;reset&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;폼에 입력된 값을 &lt;b&gt;모두 초기화&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.6977%;&quot;&gt;&lt;b&gt;submit&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;폼에 입력된 값을 &lt;b&gt;서버에 모두 전송&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3721%;&quot;&gt;&lt;b&gt;name&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20.6977%;&quot;&gt;텍스트&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;&lt;b&gt;입력 양식을 식별하는 이름 설정&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3721%;&quot;&gt;&lt;b&gt;value&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20.6977%;&quot;&gt;텍스트&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;&lt;b&gt;입력 양식의 초깃값 설정&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span data-math=&quot;\mathbf{&amp;lt;select&amp;gt;}&quot;&gt;&amp;lt;select&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;속성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;값&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;name&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;텍스트&lt;/td&gt;
&lt;td&gt;&lt;b&gt;목록 상자의 이름 설정&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;size&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;숫자&lt;/td&gt;
&lt;td&gt;한 번에 표시할 &lt;b&gt;항목의 개수 설정&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;multiple&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&lt;span data-math=&quot;\mathbf{\text{[Ctrl]}}&quot;&gt;$\mathbf{\text{[Ctrl]}}$&lt;/span&gt; 키를 눌러 &lt;b&gt;다중 선택 설정&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하위 태그 &lt;span data-math=&quot;\mathbf{&amp;lt;option&amp;gt;}&quot;&gt;&amp;lt;option&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 84px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;속성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;값&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;value&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;텍스트&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;항목의 값 설정&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;selected&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;해당 항목을 &lt;b&gt;초깃값으로 선택&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;disabled&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;항목을 비활성화&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span data-math=&quot;\mathbf{&amp;lt;textarea&amp;gt;}&quot;&gt;&amp;lt;textarea&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 147px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 8.72093%; height: 21px;&quot;&gt;&lt;b&gt;속성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 9.06977%; height: 21px;&quot;&gt;&lt;b&gt;값&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 82.093%; height: 21px;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 8.72093%; height: 21px;&quot;&gt;&lt;b&gt;name&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 9.06977%; height: 21px;&quot;&gt;텍스트&lt;/td&gt;
&lt;td style=&quot;width: 82.093%; height: 21px;&quot;&gt;&lt;b&gt;이름 설정&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 8.72093%; height: 21px;&quot;&gt;&lt;b&gt;cols&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 9.06977%; height: 21px;&quot;&gt;숫자&lt;/td&gt;
&lt;td style=&quot;width: 82.093%; height: 21px;&quot;&gt;입력할 텍스트 영역의 &lt;b&gt;너비 (열 크기) 설정&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 8.72093%; height: 21px;&quot;&gt;&lt;b&gt;rows&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 9.06977%; height: 21px;&quot;&gt;숫자&lt;/td&gt;
&lt;td style=&quot;width: 82.093%; height: 21px;&quot;&gt;입력할 텍스트 영역의 &lt;b&gt;높이 (행 크기) 설정&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 8.72093%; height: 63px;&quot; rowspan=&quot;3&quot;&gt;&lt;b&gt;wrap&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 9.06977%; height: 21px;&quot;&gt;&lt;b&gt;off&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 82.093%; height: 21px;&quot;&gt;줄 바꿈을 &lt;b&gt;하지 않음&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 9.06977%; height: 21px;&quot;&gt;&lt;b&gt;soft&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 82.093%; height: 21px;&quot;&gt;$\mathbf{\text{[Enter]}}$를 누르지 않아도 &lt;b&gt;입력 행 끝에서 자동 줄 바꿈&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 9.06977%; height: 21px;&quot;&gt;&lt;b&gt;hard&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 82.093%; height: 21px;&quot;&gt;&lt;span data-math=&quot;\mathbf{\text{soft}}&quot;&gt;$\mathbf{\text{soft}}$&lt;/span&gt; 상태와 비슷하며, &lt;b&gt;서버에 전송할 때 캐리지 리턴 문자를 전달&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1762101652954&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...

        &amp;lt;label for=&quot;userId&quot;&amp;gt;아이디:&amp;lt;/label&amp;gt;
        &amp;lt;input type=&quot;text&quot; id=&quot;userId&quot; name=&quot;id&quot; value=&quot;&quot; size=&quot;10&quot; required&amp;gt;
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;

        &amp;lt;label for=&quot;userPw&quot;&amp;gt;비밀번호:&amp;lt;/label&amp;gt;
        &amp;lt;input type=&quot;password&quot; id=&quot;userPw&quot; name=&quot;pw&quot; value=&quot;&quot; size=&quot;10&quot; required&amp;gt;
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;

        &amp;lt;p&amp;gt;성별:
        &amp;lt;input type=&quot;radio&quot; name=&quot;gender&quot; value=&quot;male&quot; checked&amp;gt; 남자
        &amp;lt;input type=&quot;radio&quot; name=&quot;gender&quot; value=&quot;female&quot;&amp;gt; 여자
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;

        &amp;lt;p&amp;gt;취미:
        &amp;lt;input type=&quot;checkbox&quot; name=&quot;hobby&quot; value=&quot;독서&quot;&amp;gt; 독서
        &amp;lt;input type=&quot;checkbox&quot; name=&quot;hobby&quot; value=&quot;운동&quot;&amp;gt; 운동
        &amp;lt;input type=&quot;checkbox&quot; name=&quot;hobby&quot; value=&quot;영화&quot;&amp;gt; 영화
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;

        &amp;lt;label for=&quot;userLoc&quot;&amp;gt;거주 지역:&amp;lt;/label&amp;gt;
        &amp;lt;select id=&quot;userLoc&quot; name=&quot;location&quot;&amp;gt;
            &amp;lt;option value=&quot;&quot;&amp;gt;--지역 선택--&amp;lt;/option&amp;gt;
            &amp;lt;option value=&quot;seoul&quot;&amp;gt;서울&amp;lt;/option&amp;gt;
            &amp;lt;option value=&quot;gyeonggi&quot; selected&amp;gt;경기&amp;lt;/option&amp;gt;
            &amp;lt;option value=&quot;busan&quot;&amp;gt;부산&amp;lt;/option&amp;gt;
        &amp;lt;/select&amp;gt;
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;

        &amp;lt;label for=&quot;intro&quot;&amp;gt;자기소개:&amp;lt;/label&amp;gt;&amp;lt;br&amp;gt;
        &amp;lt;textarea id=&quot;intro&quot; name=&quot;introduction&quot; cols=&quot;40&quot; rows=&quot;5&quot; wrap=&quot;soft&quot;&amp;gt;&amp;lt;/textarea&amp;gt;
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;
        
        &amp;lt;input type=&quot;hidden&quot; name=&quot;joinDate&quot; value=&quot;2025-11-03&quot;&amp;gt;

        &amp;lt;input type=&quot;submit&quot; value=&quot;가입 완료&quot;&amp;gt;
        
        &amp;lt;input type=&quot;reset&quot; value=&quot;다시 작성&quot;&amp;gt;
...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-03 014010.png&quot; data-origin-width=&quot;401&quot; data-origin-height=&quot;320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9lfZo/dJMcaiax5ma/3JYi6BAGKzzurtZuF3O341/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9lfZo/dJMcaiax5ma/3JYi6BAGKzzurtZuF3O341/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9lfZo/dJMcaiax5ma/3JYi6BAGKzzurtZuF3O341/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9lfZo%2FdJMcaiax5ma%2F3JYi6BAGKzzurtZuF3O341%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;401&quot; height=&quot;320&quot; data-filename=&quot;스크린샷 2025-11-03 014010.png&quot; data-origin-width=&quot;401&quot; data-origin-height=&quot;320&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;&lt;b&gt;2. @ModelAttribute를 이용한 데이터 바인딩&lt;/b&gt;&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;웹 요청이 URL를 처리하기 전에 커맨드 객체에 데이터를 담는다.&lt;/li&gt;
&lt;li&gt;@ModelAttribute&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;커맨드 객체: Setter()매서드를 이용해 도메인 객체에 정의된 프로퍼티로 데이터를 바인딩하는 객체&lt;/li&gt;
&lt;li&gt;요청 파라미터 이름 == 폼에서 전송된&amp;nbsp; 파라미터 이름&lt;/li&gt;
&lt;li&gt;lombok: Getter, Setter자동 생성 (의존성 주입 필요)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1762102293313&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 요청 처리 메서드의 매개변수에 적용&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@ModelAttribute: 폼 데이터를 커맨드 객체가 매핑하고 데이터를 프로퍼티에 채우는 역할이다.&lt;/li&gt;
&lt;li&gt;컨트롤러 안에 @RequestMapping이 적용된 요청 매서드의 매개변수로 설정해서 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1762102891745&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
    // URL: /member로 POST 요청이 올 경우 처리
    @PostMapping(&quot;/member&quot;)
    public String submitForm(@ModelAttribute Member member, Model model) {
        // @ModelAttribute Member member: 폼에서 전송된 데이터(id, passwd, ...)가 Member 객체의 필드(프로퍼티)에 자동으로 바인딩됩니다.
        // Model model: 뷰 페이지(ViewPage02.html)로 데이터를 전달하기 위한 객체
        // 바인딩된 member 객체를 &quot;member&quot;라는 이름으로 model에 추가
        
        model.addAttribute(&quot;member&quot;, member); 
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@ModelAttribute를 사용하지 않는 경우&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1762103043381&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
    @PostMapping(&quot;/member&quot;)
    public String submitForm(Member member, Model model) {
        model.addAttribute(&quot;member&quot;, member); 
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;커맨드 객체 이름을 변경한 경우&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1762103132613&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
    @PostMapping(&quot;/member&quot;)
    public String submitForm(@ModelAttribute(&quot;member&quot;) Member mem, Model model) {
        model.addAttribute(&quot;member&quot;, mem); 
...&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 메서드에 적용&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1762103440799&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
    // 1. 요청 처리 메서드: GET 요청을 처리하고 ViewPage03을 반환
    @GetMapping 
    public String showForm() {
        return &quot;ViewPage03&quot;;
    }

    // 2. @ModelAttribute 메서드 (모델 속성 이름: &quot;title&quot;)
    // @RequestMapping이 없지만 showForm()보다 먼저 호출되어 Model에 데이터를 추가합니다.
    @ModelAttribute(&quot;title&quot;) // (1) 모델 속성 이름 명시
    public void setTitle(Model model) {
        // Model에 &quot;title&quot;이라는 이름으로 &quot;ModelAttribute 예제&quot; 값을 추가
        model.addAttribute(&quot;title&quot;, &quot;ModelAttribute 예제&quot;); 
    }

    // 3. @ModelAttribute 메서드 (모델 속성 이름: &quot;color&quot;)
    // 메서드의 반환 값이 그대로 Model 속성에 추가됩니다. (속성 이름은 &quot;color&quot;)
    @ModelAttribute(&quot;color&quot;) // (2) 모델 속성 이름 명시
    public List&amp;lt;String&amp;gt; populateColor() {
        // List 객체를 반환하여 Model에 &quot;color&quot;라는 이름으로 자동 추가
        return Arrays.asList(&quot;red&quot;, &quot;green&quot;, &quot;blue&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;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;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;3. @InitBinder를 이용한 커스턴 데이터 바이딩&lt;/span&gt;&lt;/b&gt;&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;/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;@InitBinder는 컨트롤러에서 사용자가 입력한 데이터가 커맨드 객체의 프로퍼티에 매핑되기 전에 데이터 바인딩을 사용자 정의(커스터마이징)하는 데 사용됩니다.&lt;/li&gt;
&lt;li&gt;사용자 첨 페이징서 입력 데이터를 커맨드 객체의 프로퍼티로 &lt;u&gt;전체 또는 일부&lt;/u&gt;만 전달 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1762104067409&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@InitBinder

@InitBinder(&quot;커맨드 객체&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 폼 파라미터의 커스텀 데이터 마인딩&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; setAllowedFields()&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;기본값 none&lt;/li&gt;
&lt;li&gt;데이터 바인딩을 허용하지 않는 폼 파라미터 설정으로 바인딩 방지 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1762104145513&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@InitBinder
public void initBinder(WebDataBinder binder) {
    // 명시적으로 &quot;id&quot;, &quot;passwd&quot;, &quot;city&quot;, &quot;sex&quot;, &quot;greetings&quot;만 바인딩을 허용
    // 나머지 폼 파라미터(예: hobby)는 바인딩에서 제외됩니다.
    binder.setAllowedFields(&quot;id&quot;, &quot;passwd&quot;, &quot;city&quot;, &quot;sex&quot;, &quot;greetings&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; setDisallowedFields()&lt;/b&gt;:&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1762104411425&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@InitBinder
public void initBinder(WebDataBinder binder) {
    // 명시적으로 &quot;hobby&quot; 파라미터만 바인딩을 차단
    // 나머지 모든 폼 파라미터(id, passwd, city, sex, greetings 등)는 바인딩이 허용됩니다.
    binder.setDisallowedFields(&quot;hobby&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;4. [도서 쇼핑몰] 도서 등록 페이지 만들기&lt;/b&gt;&lt;/span&gt;&lt;/h2&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;도서 등록 페이지를 출력하는 요청 처리 메서드 작성 (BookController)&lt;/li&gt;
&lt;li&gt;도서 등록 페이지 작성 (addBook.html)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-03 025103.png&quot; data-origin-width=&quot;1370&quot; data-origin-height=&quot;968&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qHHdx/dJMcag4SbIn/1BnvAWZwEeKasrJQ8eKze1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qHHdx/dJMcag4SbIn/1BnvAWZwEeKasrJQ8eKze1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qHHdx/dJMcag4SbIn/1BnvAWZwEeKasrJQ8eKze1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqHHdx%2FdJMcag4SbIn%2F1BnvAWZwEeKasrJQ8eKze1%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;1370&quot; height=&quot;968&quot; data-filename=&quot;스크린샷 2025-11-03 025103.png&quot; data-origin-width=&quot;1370&quot; data-origin-height=&quot;968&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. @ModelAttribute로 도서 등록 처리하기&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;lombok 의존 라이브러리 등록&lt;/li&gt;
&lt;li&gt;롬복 애너테이션 선언&lt;/li&gt;
&lt;li&gt;신규 도서를 등록하는 메서드 정의 &amp;amp; 작성 (BookRepository)&lt;/li&gt;
&lt;li&gt;신규 도서를 등록하는 메서드 정의 &amp;amp; 작성 (BookService)&lt;/li&gt;
&lt;li&gt;신규 도서 등록하는 메서드 작성 (BookController)&lt;/li&gt;
&lt;li&gt;도서 등록 페이지에 제목을 출력하는 메서드 작성 ( BookController )&lt;/li&gt;
&lt;li&gt;도서 등록 페이지 작성 (addBook.html)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-03 034225.png&quot; data-origin-width=&quot;457&quot; data-origin-height=&quot;326&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dSoXHl/dJMcaj8mK2d/g1K4W1XTkt9J2naB1zYF4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dSoXHl/dJMcaj8mK2d/g1K4W1XTkt9J2naB1zYF4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dSoXHl/dJMcaj8mK2d/g1K4W1XTkt9J2naB1zYF4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdSoXHl%2FdJMcaj8mK2d%2Fg1K4W1XTkt9J2naB1zYF4K%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;457&quot; height=&quot;326&quot; data-filename=&quot;스크린샷 2025-11-03 034225.png&quot; data-origin-width=&quot;457&quot; data-origin-height=&quot;326&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. @InitBinder로 커스텀 테이터 메서드 작성&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;커스텀 데이터 바인딩 메서드 작성 ( BookController )&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #dddddd; color: #ee2323;&quot;&gt;&lt;b&gt;연습문제&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;01. 다음 중 input 태그의 type에 대한 설명으로 옳지 않은 것은?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;✅&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;정답:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;③ password: 양을 입력할 때 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;02. 다음 중 스프링 폼 태그(form)의 속성에 대한 설명으로 옳지 않은 것은?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;✅&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;정답:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;② action: 데이터를 받아 처리하는 뷰 페이지의 URL을 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;03. 다음 중 롬복(lombok)에 대한 설명으로 옳지 않은 것은?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;✅&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;정답:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;④ 파라미터를 받는 일반 생성자를 생성하는 @RequiredArgsConstructor를 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;04. 다음 중 폼 요청 URL을 처리하는 요청 처리 메서드가 호출되기 전에 뷰 페이지에서 입력된 커맨드 객체를 미리 담아 주는 어노테이션으로 옳은 것은?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;✅&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;정답:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;③ @ModelAttribute&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;05. 다음 중 폼 페이지에서 사용자가 입력한 데이터가 커맨드 객체의 프로퍼티에 매핑되어 데이터 바인딩을 사용자 정의(customizing)할 수 있는 어노테이션으로 옳은 것은?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;✅&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;정답:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;④ @InitBinder&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;06. 다음 중 사용자 요청에 따라 현재 뷰 페이지에서 다른 뷰 페이지로 이동하는 방식으로 옳지 않은 것은?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;✅&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;정답:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;③ return 방식: /요청 URL&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;07. 다음 중 사용자 요청에 따라 뷰 페이지를 이동하는 redirect 방식에 대한 설명으로 옳지 않은 것은?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;✅&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;정답:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;④ 웹 브라우저가 최초 요청 URL을 유지하면서 응답된 뷰 페이지를 표현하는 방식입니다.&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;08. 다음 중 메서드 수준의 @ModelAttribute로 뷰 페이지에서 공통으로 사용할 수 있는 커맨드 객체의 프로퍼티를 설정하여 뷰 페이지에 출력하려는 역할을 하는 메서드로 옳은 것은?&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;b&gt;✅&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;정답:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;① requestMovies01()&lt;br /&gt;&lt;br /&gt;&lt;b&gt;09. 다음 중 웹 브라우저의 주소창에 http://localhost:8080/movies/add를 입력하면 매핑하여 실행하는 요청 처리 메서드로 옳은 것은?&lt;/b&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;b&gt; &lt;b&gt;&lt;b&gt;✅&lt;/b&gt;&lt;/b&gt; 정답:&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;② requestMovies02()&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;b&gt;10. 다음 중 요청 처리 메서드 requestMovies03()을 실행한 후 웹 브라우저에 출력되는 뷰 페이지로 옳은 것은?&lt;/b&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;b&gt; &lt;b&gt;&lt;b&gt;✅&lt;/b&gt;&lt;/b&gt; 정답:&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;② addMovie.html&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: right;&quot; data-ke-size=&quot;size16&quot;&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;span&gt;&amp;nbsp;&lt;/span&gt;스프링부트 완전정복: 개념부터 실정 프로젝트까지 』길벗캠퍼스&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(2024).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DEVELOPMENT/Spring</category>
      <author>Tiny Commit</author>
      <guid isPermaLink="true">https://marin-1104.tistory.com/150</guid>
      <comments>https://marin-1104.tistory.com/150#entry150comment</comments>
      <pubDate>Mon, 3 Nov 2025 18:06:39 +0900</pubDate>
    </item>
    <item>
      <title>#2 트러블 슈팅 [스프링부트 완전정복] 6장 실습2</title>
      <link>https://marin-1104.tistory.com/149</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;교재를 보면서 실습을 따라하는데,&amp;nbsp;실습1에서 오류가 발생했다 ㅜㅜㅜ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt; 400 Bad Request 오류의 원인&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-03 030640.png&quot; data-origin-width=&quot;849&quot; data-origin-height=&quot;330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czR1S3/dJMcadmL9FF/PBhijg6FKZkkmbrRZErH0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czR1S3/dJMcadmL9FF/PBhijg6FKZkkmbrRZErH0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czR1S3/dJMcadmL9FF/PBhijg6FKZkkmbrRZErH0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczR1S3%2FdJMcadmL9FF%2FPBhijg6FKZkkmbrRZErH0k%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;849&quot; height=&quot;330&quot; data-filename=&quot;스크린샷 2025-11-03 030640.png&quot; data-origin-width=&quot;849&quot; data-origin-height=&quot;330&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;원인:&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BookController.java의 submitAddNewBook 메서드는 바인딩 대상인 @ModelAttribute Book book만 받고, 오류를 처리할 BindingResult 파라미터가 없었습니다.&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: #000000;&quot;&gt;&lt;b&gt;1. BookController.java 수정 (오류 검사 및 모델 속성 추가) &lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BookController의 POST 메서드를 수정하여 BindingResult를 추가하고, 오류가 발생했을 때 &lt;b&gt;400 에러 대신 폼 페이지(addBook.html)로 다시 포워딩&lt;/b&gt;하도록 했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1762109045424&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@GetMapping(&quot;/add&quot;)
public String requestAddBookForm(Model model) { 
    Book book = new Book();
    model.addAttribute(&quot;newBook&quot;, book); // ① 빈 객체 모델 추가 (필수)
    return &quot;addBook&quot;; 
}

@PostMapping(&quot;/add&quot;)
public String submitAddNewBook(@ModelAttribute(&quot;newBook&quot;) Book book, BindingResult result) {
    if (result.hasErrors()) { // ② BindingResult로 오류 검사
        return &quot;addBook&quot;;     // ③ 오류 시 폼으로 복귀 (400 에러 방지)
    }
    // ... (정상 처리)
    bookService.setNewBook(book);
    return &quot;redirect:/books&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. addBook.html 수정 (Thymeleaf 바인딩 적용) &lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BindingResult가 정상적으로 동작하려면, Thymeleaf에게 &lt;b&gt;어떤 폼 필드가 어떤 객체의 필드에 바인딩되는지&lt;/b&gt; 명시적으로 알려주어야 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;요소&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;수정 내용&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;이유&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;form&amp;gt; 태그&lt;/td&gt;
&lt;td&gt;&amp;lt;form ... th:object=&quot;${newBook}&quot;&amp;gt; 추가&lt;/td&gt;
&lt;td&gt;폼이 Book 객체(newBook)와 연결됨을 선언합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;input&amp;gt; 태그&lt;/td&gt;
&lt;td&gt;name을 th:field=&quot;*{필드명}&quot;으로 변경&lt;/td&gt;
&lt;td&gt;Thymeleaf가 필드 간의 바인딩을 관리하고, 오류 발생 시 오류 메시지를 표시하며, 기존 입력 값을 유지할 수 있게 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DEVELOPMENT/Spring</category>
      <author>Tiny Commit</author>
      <guid isPermaLink="true">https://marin-1104.tistory.com/149</guid>
      <comments>https://marin-1104.tistory.com/149#entry149comment</comments>
      <pubDate>Mon, 3 Nov 2025 16:46:55 +0900</pubDate>
    </item>
  </channel>
</rss>