<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>영발이의 개발 일기</title>
    <link>https://zerofootblog.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Mon, 13 Apr 2026 10:30:30 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>영발개발</managingEditor>
    <item>
      <title>Servlet &amp;amp; Dispatcher-Servlet(서블릿 &amp;amp; 디스패처 서블릿)</title>
      <link>https://zerofootblog.tistory.com/20</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;table style=&quot;border-collapse: collapse; width: 100%; height: 17px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 100%; height: 17px;&quot;&gt;&lt;b&gt;목차&lt;/b&gt;&lt;br /&gt;- Servlet(서블릿)이란?&lt;br /&gt;- Dispatcher Servlet(디스패처 서블릿)이란?&lt;br /&gt;- Dispatcher Servlet(디스패처 서블릿) 동작 과정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Servlet(서블릿)이란?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;서블릿은 &lt;b&gt;Java 기반 웹 서버(WAS)가 HTTP 요청을 처리하기 위해 사용하는 자바 클래스&lt;/b&gt;다. 쉽게 말하면 '&lt;b&gt;브라우저가 보낸 요청을 받아서 그에 대한 응답을 만드는 자바 프로그램&lt;/b&gt;'이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;서블릿 특징&lt;/b&gt;&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTP 요청을 받아서 처리 가능&lt;/li&gt;
&lt;li&gt;요청/응답 객체(HttpServletRequest, HttpServletResponse) 사용&lt;/li&gt;
&lt;li&gt;doGet(), doPost() 같은 메서드로 HTTP 메서드 처리&lt;/li&gt;
&lt;li&gt;서블릿은 하나 생성되면 계속 재사용됨 (싱글톤과 비슷)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Dispatcher-Servlet(디스패처 서블릿)이란?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;디스패처 서블릿은 스프링이 만든 서블릿으로, 스프링 MVC의 모든 요청을 받아서 처리하는 'Front Controller(프론트 컨트롤러)' 역할을 한다. 즉 사용자가 보낸 모든 요청을 받아서 어떤 컨트롤러가 처리할지 결정한다.&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;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Dispatcher-Servlet(디스패처 서블릿) 동작 과정&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위에서 언급한 바와 같이, 디스패처 서블릿은 적합한 컨트롤러와 메서드를 찾아 요청을 위임해야 한다. 동작 과정을 보면 아래 그림과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bj6Svb/dJMcadApCbu/V5mE8pXCUbhKqVxmfcULsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bj6Svb/dJMcadApCbu/V5mE8pXCUbhKqVxmfcULsk/img.png&quot; data-alt=&quot;출처 : https://mangkyu.tistory.com&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bj6Svb/dJMcadApCbu/V5mE8pXCUbhKqVxmfcULsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbj6Svb%2FdJMcadApCbu%2FV5mE8pXCUbhKqVxmfcULsk%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;1000&quot; height=&quot;472&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;472&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://mangkyu.tistory.com&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ 사용자로부터 디스패처 서블릿이 요청을 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2️⃣ HandlerMapping이 어떤 Controller가 처리할지 검색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3️⃣ 요청을 Controller로 위임할 Handler Adapter를 찾아서 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4️⃣ Handler Adapter가 Controller로 요청을 위임한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5️⃣ Controller랑 Service에서 비즈니스 로직을 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6️⃣ Controller가 응답값을 리턴한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7️⃣ Handler Adapter가 응답값을 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8️⃣ 서버의 응답을 사용자에게 전달한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 위의 내용은 사실 굉장히 많은 부분을 생략하고 처음 서블릿을 공부하는 사람들이 알기 쉽게 작성했다. 아마 실제 과정은 훨씬 복잡할 것이지만 아직 나역시 이해와 공부가 필요한 부분이기에 이번에는 이 정도로만 정리를 하고자 한다.&lt;/p&gt;</description>
      <category>공개 글</category>
      <category>dispatcher-servlet</category>
      <category>Servlet</category>
      <category>개발일기</category>
      <category>디스패처서블릿</category>
      <category>서블릿</category>
      <author>영발개발</author>
      <guid isPermaLink="true">https://zerofootblog.tistory.com/20</guid>
      <comments>https://zerofootblog.tistory.com/20#entry20comment</comments>
      <pubDate>Fri, 28 Nov 2025 17:09:43 +0900</pubDate>
    </item>
    <item>
      <title>예외 처리(Exception Handling)</title>
      <link>https://zerofootblog.tistory.com/19</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;JAVA에서 예외가 무엇인지부터 예외 처리를 어떻게 하는지, 또 이게 실무에서 왜 중요한지에 대해서 정리해볼까 한다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot;&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;width: 100%;&quot;&gt;&lt;b&gt;목차&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;br&gt;- 에러와 예외의 차이&lt;br&gt;&lt;br&gt;- 예외의 종류&lt;br&gt;&lt;br&gt;- 예외 처리 방법&lt;br&gt;&lt;br&gt;- 예외 처리의 중요성&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;에러와 예외의 차이&lt;/span&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;에러(Error)&lt;/b&gt;&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;메모리 부족이나 시스템 오류처럼&amp;nbsp;복구 불가능한 심각한 문제를 뜻한다. &lt;span style=&quot;color: #000000;&quot;&gt;시스템 수준에서 발생하여 프로그램 종료로 이어지는 경우가 많고, 에러는 컴파일 시점에 확인할 수 없으며 주로 실행 중에 발생한다. 즉 예측이 불가능해 개발자가 따로 코드 수정과 같은 것들로 처리할 수 없다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ex)&amp;nbsp; OutOfMemoryError, StackOverflowError&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;예외(Exception)&lt;/b&gt;&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;개발자가 코드를 통해 처리할 수 있는 비교적 덜 심각한 오류를 뜻한다. 정상적인 프로그램 흐름을 방해하는 이벤트로 컴파일 시점에 체크되는 경우와 실행 중에 발견되는 경우가 있다. 에러와 다르게 예측이 가능하기 때문에 'try-catch'와 같은 예외처리 구문을 통해 코드를 수정하여 수습이 가능하다.&lt;br&gt;&amp;nbsp;&lt;br&gt;ex ) NullPointerException, IOException, IllegalArgumentException&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;예외의 종류&lt;/span&gt;&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;443&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/62Kl9/dJMcaaKosSl/6ugnhvaG3GWY5enuaS7t3K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/62Kl9/dJMcaaKosSl/6ugnhvaG3GWY5enuaS7t3K/img.jpg&quot; data-alt=&quot;출처 : https://sorjfkrh5078.tistory.com/104&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/62Kl9/dJMcaaKosSl/6ugnhvaG3GWY5enuaS7t3K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F62Kl9%2FdJMcaaKosSl%2F6ugnhvaG3GWY5enuaS7t3K%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;582&quot; height=&quot;443&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;443&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://sorjfkrh5078.tistory.com/104&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;Exception은 크게 RuntimeException과 그 외 다른 Exception으로 나눌 수 있는데, OtherException은 컴파일 시점에 확인되는 &lt;b&gt;Checked Exception&lt;/b&gt;이고 RuntimeException은 컴파일 시점이 아닌 런타임 시점에 발생하는 &lt;b&gt;Unchecked Exception&lt;/b&gt;이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;즉 Checked Exception은 컴파일 시점에 확인되기 떄문에 예외 처리를 해주지 않으면 컴파일 자체가 실패해서 프로그램 실행이 되지 않고, Unchecked Exception은 컴파일 시점에는 확인이 안되기 때문에 실행 중에 에러가 터질 수 있다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;요약&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;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&lt;b&gt; Checked Exception &lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt; Unchecked Exception &lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;발생 시점&lt;/td&gt;&lt;td&gt;컴파일 시점&lt;/td&gt;&lt;td&gt;런타임 시점&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;예외 처리 필요 여부&lt;/td&gt;&lt;td&gt;반드시 필요&lt;/td&gt;&lt;td&gt;선택 사항&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;대표 클래스&lt;/td&gt;&lt;td&gt;IOException, SQLException&lt;/td&gt;&lt;td&gt;NullPointerException, ArithmeticException&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;원인&lt;/td&gt;&lt;td&gt;외부 요인 (파일, 네트워크 등)&lt;/td&gt;&lt;td&gt;개발자 실수 (논리 오류 등)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;예외 처리 방법&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;그렇다면 시스템이 비정상적으로 종료되지 않게 하려면 이 예외를 처리해야 하는데 코드에서 어떻게 처리할 수 있을까? Java에서는 &lt;b&gt;try-catch&lt;/b&gt;, &lt;b&gt;throw&lt;/b&gt;, &lt;b&gt;throws&lt;/b&gt;를 통해 예외를 처리한다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot;&gt;&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;try-catch&lt;/b&gt;&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;try-catch는 가장 기본적인 예외 처리 방식으로, 예외가 발생할 수 있는 코드를 try 블록 안에 작성하고 예외가 발생했을 때 수행할 동작을 catch 블록에 작성한다.&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;try {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int result = 10 / 0;&amp;nbsp;&amp;nbsp;// ArithmeticException 발생
} catch (ArithmeticException e) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.out.println(&quot;0으로 나눌 수 없습니다.&quot;);
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;try 블록 내에서 예외가 발생하면, 해당 예외를 처리할 수 있는 catch 블록으로 제어가 이동하며 프로그램은 정상적으로 이어진다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;throw&lt;/b&gt;&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;throw는 직접 예외를 발생시킬 때 사용한다.&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public void checkAge(int age) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (age &amp;lt; 19) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw new IllegalArgumentException(&quot;성인만 접근 가능합니다.&quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉, throw는 예외 객체를 던지는 역할을 한다. 던져진 예외는 현재 메서드를 호출한 쪽에서 처리해야 한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;보통 실제 프로젝트에서는 @RestControllerAdvice라는 어노테이션이 선언된 Exception Handler 클래스에서 던져진 예외를 받아 화면으로 return하는 식으로 예외를 처리한다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;throws&lt;/b&gt;&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;throws는 메서드 선언부에서 이 메서드가 어떤 예외를 던질 수 있는지 명시하는 키워드다.&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public void readFile(String filePath) throws IOException {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FileReader reader = new FileReader(filePath);&amp;nbsp;&amp;nbsp;// CheckedException
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;reader.read();
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;throws는 이 메서드를 사용할 때 이런 예외가 발생할 수도 있다라는 사전 경고 역할을 한다. 해당 메서드를 호출한 곳에서 반드시 예외를 처리해야 컴파일이 통과된다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;try-catch + throw&lt;/b&gt;&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;실무에서는 try-catch 안에서 throw로 예외를 던지는 경우를 많이 볼 수 있다. 아래에서 얘기할 거지만 실제 설계 단계에서 예외를 어떻게 구분할지를 정하고 개발 단계에 들어가기 때문에,&amp;nbsp; catch로 일단 예외를 잡고 throw로 개발자가 던지고자 하는 예외로 변환해서 던지는 것이다.&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;try {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;userRepository.save(user);
} catch (DataIntegrityViolationException e) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// DB 제약조건 위반 등 Spring 내부 예외를 감싸서
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 비즈니스 예외로 바꿔 던짐
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw new BusinessException(&quot;USER_DUPLICATE&quot;, &quot;이미 존재하는 사용자입니다.&quot;);
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;예외 처리의 중요성&lt;/span&gt;&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;실제 프로젝트를 개발하다 보면 단순히 예외를 잡는 것 이상으로, 예외를 어떻게 설계하고 구분할지가 시스템 안정성과 유지보수성에 큰 영향을 미친다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;예를들어 실제 프로젝트에서는 단일 애플리케이션만 있는게 아니라 프레임워크, ESB, EDMS, 그 외 외부 연계 시스템 등 서로 다른 다수의 시스템이 연결되어 있는데, 이런 환경에서는 한 곳에서 발생한 예외가 다른 시스템에까지 영향을 줄 수 있기 때문에, 예외를 명확하게 구분하고 설계하는게 매우 중요하다.&lt;br&gt;&amp;nbsp;&lt;br&gt;ex)&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;FrameworkException&lt;/b&gt; → 내부 공통 모듈에서 발생한 예외&lt;/li&gt;&lt;li&gt;&lt;b&gt;IntegrationException&lt;/b&gt; → ESB나 외부 연계 구간에서 발생한 예외&lt;/li&gt;&lt;li&gt;&lt;b&gt;BusinessException&lt;/b&gt; → 사용자의 잘못된 요청으로 발생한 예외&lt;/li&gt;&lt;li&gt;&lt;b&gt;SystemException&lt;/b&gt; → DB, 서버 자원, 네트워크 등의 시스템 문제&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 예외를 구분해두면 문제가 발생했을 때 로그만 봐도 “이건 프레임워크 담당이 봐야 하는 문제구나”, “이건 외부 연계 쪽 에러니까 ESB 담당한테 전달해야겠네” 하는 식으로 담당 영역을 빠르게 특정하고 조치를 취할 수 있다.&lt;/p&gt;</description>
      <category>공개 글</category>
      <category>Exception</category>
      <category>개발일기</category>
      <category>에외처리</category>
      <category>예외</category>
      <author>영발개발</author>
      <guid isPermaLink="true">https://zerofootblog.tistory.com/19</guid>
      <comments>https://zerofootblog.tistory.com/19#entry19comment</comments>
      <pubDate>Fri, 14 Nov 2025 08:00:07 +0900</pubDate>
    </item>
    <item>
      <title>세션(Session), 쿠키(Cookie), 토큰(Token), JWT 완벽 정리</title>
      <link>https://zerofootblog.tistory.com/18</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;로그인과 사용자 인증 관련 단골 용어인 세션, 쿠키, 토큰, JWT에 대해서 정리해 보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&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;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;사용자 인증?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&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;b&gt;회사 입구에서 사원증을 찍고 들어가는 것&lt;/b&gt;과 같다. 그리고 직장 내에서 &lt;b&gt;사원증을 목에 걸고 다니거나, 직원복을 입고 다니면서 직원임을 인증하는 것&lt;/b&gt;을 지속 인증이라고 생각하면 쉬울 것이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;세션 vs 토큰&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;대표적인 지속 인증 방법으로는 &lt;b&gt;세션 인증 방식&lt;/b&gt;과 &lt;b&gt;토큰 인증 방식&lt;/b&gt;이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;세션 인증 방식&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;세션 인증 방식으로 로그인 하는 흐름을 먼저 보자.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;화면에서 ID/PW 입력 ➡️ 서버에서 ID/PW 확인하고 유효하면 세션을 생성하고 세션을 가지고 있음 ➡️ 서버에서 세션 id만 쿠키에 담아서 화면에 전달 ➡️ 화면에서 클라이언트는 매 요청마다 요청과 함께 쿠키를 보냄 ➡️ 서버는 쿠키에서 세션 id가 유효한지 판단하고 요청을 처리&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;토큰 인증 방식&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;토큰 인증 방식도 흐름을 먼저 보자.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;화면에서 ID/PW 입력 ➡️ 서버에서 ID/PW 확인하고 유효하면 토큰을 만들어서 화면에 전달 ➡️ 화면에서 클라이언트는 매 요청마다 헤더에 해당 토큰 정보를 담아서 보냄 ➡️ 서버는 토큰이 유효한지 확인하고 요청을 처리&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;이와 같이 토큰 인증 방식은 서버가 로그인 상태를 기억하지 않고, 클라이언트가 인증 정보를 들고 다니는 방식이다. 서버가 상태를 저장하지 않아도 되기 때문에 확장성이 좋다는 장점이 있지만,&amp;nbsp; 토큰은 만료 시간이 되기 전까진 유효하기 때문에&amp;nbsp; 중간에 누군가가 탈취해도 무효화 시킬 수 없고 로그아웃 처리도 어렵다는 단점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;좀 더 설명을 덧붙이자면, 세션 인증 방식에서는 서버에서 세션을 저장하기 때문에 서버에서 세션을 삭제해 버리면 누군가가 세션 id를 가지고 요청을 해도 더 이상 서비스를 이용할 수 없다. 또한 'session.invalidate();'와 같은 방법으로 즉시 로그아웃도 가능하지만, 토큰 인증 방식은 로그인 정보를 서버에서 따로 저장하지 않기 때문에 이러한 것들이 불가능하다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;쿠키, JWT&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;쿠키(Cookie)&lt;/b&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;쿠키는 위에서 언급을 한 번 했지만, 정의하자면 &lt;b&gt;브라우저에 저장되는 작은 데이터(key - value)&lt;/b&gt;다. 주로 서버가 브라우저에 식별자(ex. 세션 id)나 설정을 저장할 때 사용되고, HTTP 요청마다 자동으로 전송된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;JWT&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;JWT는 JSON Web Token의 약자로 토큰의 구체적 포맷 중 하나다. JWT는 다음과 같이 세 부분으로 구성된다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;span&gt;&amp;nbsp;예:&lt;br /&gt;&amp;nbsp;eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJ1c2VyMTIzIn0.abc123signature&lt;/span&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;1378&quot; data-end=&quot;1490&quot;&gt;
&lt;li data-start=&quot;1378&quot; data-end=&quot;1409&quot;&gt;&lt;b&gt;Header:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;어떤 알고리즘으로 서명했는지&lt;/li&gt;
&lt;li data-start=&quot;1410&quot; data-end=&quot;1454&quot;&gt;&lt;b&gt;Payload:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용자 정보 (ex. userId, role 등)&lt;/li&gt;
&lt;li data-start=&quot;1455&quot; data-end=&quot;1490&quot;&gt;&lt;b&gt;Signature:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;비밀키로 서명한 값 &amp;rarr; 위조 방지&lt;/li&gt;
&lt;/ul&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;서버에서는 JWT api를 사용해 토큰을 만들고, 들어온 토큰이 유효한 토큰인지 확인하는 식으로 사용자 인증 처리를 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;정리&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;388&quot; data-start=&quot;297&quot;&gt;&lt;b&gt;세션(session)&lt;/b&gt;: 서버가 관리하는 사용자 상태 저장소. 보통 세션ID만 쿠키로 브라우저에 두고, 실제 사용자 상태(로그인 정보 등)는 서버에 저장.&lt;/li&gt;
&lt;li data-end=&quot;494&quot; data-start=&quot;389&quot;&gt;&lt;b&gt;토큰(token)&lt;/b&gt;: 인증을 위해 발급되는 문자열(=자격증명). 서버가 상태를 들고있을 수도 있고(세션 기반 토큰), 클라이언트에 자체적으로 정보를 담을 수도 있음(예: JWT).&lt;/li&gt;
&lt;li data-end=&quot;494&quot; data-start=&quot;389&quot;&gt;&lt;b&gt;쿠키(cookie)&lt;/b&gt;: 브라우저에 저장되는 작은 데이터(키-값). 주로 서버가 브라우저에 식별자(예: 세션ID)나 설정을 저장할 때 사용. HTTP 요청마다 자동으로 전송됨(도메인/경로 일치 시).&lt;/li&gt;
&lt;li data-end=&quot;638&quot; data-start=&quot;495&quot;&gt;&lt;b&gt;JWT(JSON Web Token)&lt;/b&gt;: 토큰의 구체적 포맷 중 하나. 헤더.페이로드.서명 구조로, 페이로드에 클레임(사용자ID, 만료시간 등)을 담아 서명해서 변조 여부 검증 가능. 기본적으로 암호화하지는 않음(인증/무결성 보장, 기밀성 X).&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>공개 글</category>
      <category>cookie</category>
      <category>JWT</category>
      <category>Session</category>
      <category>Token</category>
      <category>개발일기</category>
      <category>로그인</category>
      <category>사용자인증</category>
      <category>세션</category>
      <category>쿠키</category>
      <category>토큰</category>
      <author>영발개발</author>
      <guid isPermaLink="true">https://zerofootblog.tistory.com/18</guid>
      <comments>https://zerofootblog.tistory.com/18#entry18comment</comments>
      <pubDate>Tue, 28 Oct 2025 17:43:26 +0900</pubDate>
    </item>
    <item>
      <title>Spring Batch(Tasklet)에서 트랜잭션 롤백(rollback)이 되지 않는 경우와 해결 방법</title>
      <link>https://zerofootblog.tistory.com/17</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;최근에 프로젝트에서 개발자들이 배치 작업 중 에러가 발생해도 rollback이 되지 않는다는 문의가 들어왔다. 문제의 원인과 해결 방법을 찾아냈고, 그것을 기록해둘까 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;원인&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;코드를 살펴보니 배치에서는 예외를 catch만 하고 throw는 하지 않고 있었다. 온라인 서비스에서는 예외를 throw하면 상위 계층(Controller, @ControllerAdvice 등)에서 받아서 후처리할 수 있지만, 배치는 예외를 throw하면 Step이 실패 처리되며&amp;nbsp; Job 실행이 중단되고 예외를 받아서 후처리해줄 곳이 없기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해결 방법 &amp;amp; 예시 코드&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;롤백되지 않는 코드&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1757641137065&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@Component
public class SampleTasklet implements Tasklet {

    @Autowired
    private UserRepository userRepository;

    @Override
    @Transactional
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
        try {
            User user = new User(&quot;beforeRollback&quot;);
            userRepository.save(user); // DB insert

            // 강제로 에러 발생
            int error = 1 / 0;

        } catch (Exception e) {
            log.error(&quot;에러 발생: {}&quot;, e.getMessage());
            // ❌ 여기서 단순히 로그만 찍으면 트랜잭션은 롤백되지 않음
        }

        return RepeatStatus.FINISHED;
    }
}&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;위 코드에서 1/0 연산으로 예외가 발생하지만, catch 블록에서 예외를 다시 throw 하지 않았기 때문에 트랜잭션이 정상 종료된 것으로 인식되어 DB에 user 데이터가 그대로 insert 된다. 즉, rollback이 일어나지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;롤백 강제 지정한 코드(해결 방법)&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1757641526040&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@Component
public class SampleTasklet implements Tasklet {

    @Autowired
    private UserRepository userRepository;

    @Override
    @Transactional
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
        try {
            User user = new User(&quot;beforeRollback&quot;);
            userRepository.save(user); // DB insert

            // 강제로 에러 발생
            int error = 1 / 0;

        } catch (Exception e) {
            log.error(&quot;에러 발생: {}&quot;, e.getMessage());

            // ✅ 현재 트랜잭션을 롤백 상태로 표시
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }

        return RepeatStatus.FINISHED;
    }
}&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;- TransactionAspectSupport&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;268&quot; data-start=&quot;184&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;216&quot; data-start=&quot;184&quot;&gt;Spring 프레임워크에서 제공하는 유틸리티 클래스&lt;/li&gt;
&lt;li data-end=&quot;268&quot; data-start=&quot;219&quot;&gt;AOP 기반 트랜잭션 관리 기능을 지원하며, 현재 트랜잭션에 접근할 수 있게 해줌&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- currentTransactionStatus()&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;418&quot; data-start=&quot;309&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;358&quot; data-start=&quot;309&quot;&gt;현재 진행 중인 트랜잭션의 상태(TransactionStatus) 객체를 반환&lt;/li&gt;
&lt;li data-end=&quot;418&quot; data-start=&quot;361&quot;&gt;이 객체를 통해 트랜잭션의 진행 여부, rollback-only 여부 등을 확인/제어할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- setRollbackOnly()&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;533&quot; data-start=&quot;450&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;481&quot; data-start=&quot;450&quot;&gt;트랜잭션 상태를 &quot;rollback 전용&quot;으로 표시&lt;/li&gt;
&lt;li data-end=&quot;533&quot; data-start=&quot;484&quot;&gt;이렇게 표시되면 트랜잭션이 commit 시도가 되더라도 무조건 rollback 됨&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;때문에, 'setRollbackOnly()'를 호출하면 현재 진행 중인 트랜잭션이 rollback-only 상태로 바뀐다. 이후 트랜잭션 commit 시도가 들어오면 무조건 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;rollback&lt;span&gt; &lt;/span&gt;&lt;/span&gt;되고, 덕분에 throw를 하지 않고도 DB 변경 내역이 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;rollback&lt;span&gt; &lt;/span&gt;&lt;/span&gt;된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;정리&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;단순히 catch 후 로그만 찍으면 rollback되지 않는다.&lt;/li&gt;
&lt;li&gt;예외를 throw 하면 Step 자체가 실패 처리된다.&lt;/li&gt;
&lt;li&gt;rollback만 하고 Step은 성공 처리하고 싶을 때는 'TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()' 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>공개 글</category>
      <category>batch</category>
      <category>rollback</category>
      <category>Spring</category>
      <category>springboot</category>
      <category>Tasklet</category>
      <category>개발</category>
      <category>개발일기</category>
      <category>롤백</category>
      <category>배치</category>
      <category>스프링</category>
      <author>영발개발</author>
      <guid isPermaLink="true">https://zerofootblog.tistory.com/17</guid>
      <comments>https://zerofootblog.tistory.com/17#entry17comment</comments>
      <pubDate>Fri, 12 Sep 2025 10:55:38 +0900</pubDate>
    </item>
    <item>
      <title>Reflection과 Class 객체</title>
      <link>https://zerofootblog.tistory.com/16</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Reflection에 대해 쓰기 전에 먼저 Class 객체에 대해서 먼저 정리해보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Class 객체&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;정의&lt;/b&gt;&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;Class는 모든 자바 클래스의 '메타데이터(설계도)'를 담는 그릇&lt;/li&gt;
&lt;li&gt;&amp;nbsp;일반 객체(new Person())는 실제 사람 인스턴스&lt;/li&gt;
&lt;li&gt;&amp;nbsp;Class&amp;lt;Person&amp;gt;은 사람이라는 설계도 자체&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;왜 Class&amp;lt;?&amp;gt;를 쓰는가?&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;어떤 클래스가 올지 모를때 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(추가로, 어떤 객체가 올지 모를때 사용된다는 점은 비슷하지만 Object는 실제 객체를 담는 것이고, Class는 클래스의 메타데이터를 담는다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1753936650688&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;Object obj = new Person(); // 실제 Person 객체가 들어감
			
Class&amp;lt;?&amp;gt; clazz = Person.class;   // Person의 설계도
Object obj = clazz.getDeclaredConstructor().newInstance(); // 설계도로 객체 생성&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;실제 프로젝트에서 봤던 코드&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1753936836623&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Class&amp;lt;?&amp;gt; clazz = vo.getClass();
Field[] fields = clazz.getDeclaredFields();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 나오는 '.getDeclaredFields()'는 Class&amp;lt;?&amp;gt; 객체가 가진 모든 필드(멤버 변수)를 Field 객체 배열로 반환하는 메서드다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어,&lt;/p&gt;
&lt;pre id=&quot;code_1753936939644&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Person {
	private String name;
	public int age;
}

public class FieldDetailExample {
	public static void main(String[] args) throws Exception {
		Class&amp;lt;?&amp;gt; clazz = Person.class;
		Field[] fields = clazz.getDeclaredFields();
        
		for (Field field : fields) {
			System.out.println(&quot;toString(): &quot; + field);  // Field 객체의 문자열 표현
			System.out.println(&quot;이름: &quot; + field.getName());
			System.out.println(&quot;타입: &quot; + field.getType());
			System.out.println(&quot;접근제어자: &quot; + java.lang.reflect.Modifier.toString(field.getModifiers()));
			System.out.println(&quot;------&quot;);
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 코딩하면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;toString():&amp;nbsp;private&amp;nbsp;java.lang.String&amp;nbsp;Person.name &lt;br /&gt;이름:&amp;nbsp;name &lt;br /&gt;타입:&amp;nbsp;class&amp;nbsp;java.lang.String &lt;br /&gt;접근제어자:&amp;nbsp;private &lt;br /&gt;------ &lt;br /&gt;toString():&amp;nbsp;public&amp;nbsp;int&amp;nbsp;Person.age &lt;br /&gt;이름:&amp;nbsp;age &lt;br /&gt;타입:&amp;nbsp;int &lt;br /&gt;접근제어자:&amp;nbsp;public &lt;br /&gt;------&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 객체의 멤버 변수(필드) 하나하나의 정보를 담은 객체가 출력되는 걸 확인 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;Reflection&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;정의&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Java에서 제공하는 표준 API로, 런타임에 클래스, 메서드, 필드, 생성자 등에 접근하고 조작할 수 있는 기능이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 중요 포인트&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴파일&amp;nbsp;시점이&amp;nbsp;아니라&amp;nbsp;프로그램&amp;nbsp;실행&amp;nbsp;중(런타임)에&amp;nbsp;클래스의&amp;nbsp;구조를&amp;nbsp;읽고&amp;nbsp;조작&lt;/li&gt;
&lt;li&gt;클래스&amp;nbsp;이름,&amp;nbsp;어노테이션,&amp;nbsp;메서드&amp;nbsp;시그니처&amp;nbsp;등을&amp;nbsp;문자열로&amp;nbsp;찾아&amp;nbsp;동적으로&amp;nbsp;로직&amp;nbsp;실행&amp;nbsp;가능&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;**&amp;nbsp;컴파일(Compile-time)과&amp;nbsp;런타임(Runtime)&amp;nbsp;차이 &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[소스코드]&amp;nbsp;--(컴파일)--&amp;gt;&amp;nbsp;[바이트코드(.class)]&amp;nbsp;--(실행)--&amp;gt;&amp;nbsp;[런타임:&amp;nbsp;JVM&amp;nbsp;메모리&amp;nbsp;위에서&amp;nbsp;동작] &lt;br /&gt;&lt;br /&gt;-&amp;nbsp;컴파일: &lt;br /&gt;javac가&amp;nbsp;.java&amp;nbsp;&amp;rarr;&amp;nbsp;.class(바이트코드)로&amp;nbsp;변환 &lt;br /&gt;문법&amp;nbsp;검사&amp;nbsp;+&amp;nbsp;타입&amp;nbsp;체크만&amp;nbsp;함 &lt;br /&gt;클래스가&amp;nbsp;실제로&amp;nbsp;실행되거나&amp;nbsp;메모리에&amp;nbsp;올라오진&amp;nbsp;않음 &lt;br /&gt;&lt;br /&gt;-&amp;nbsp;런타임: &lt;br /&gt;java로&amp;nbsp;JVM이&amp;nbsp;.class를&amp;nbsp;읽어서&amp;nbsp;메모리에&amp;nbsp;로드 &lt;br /&gt;메서드&amp;nbsp;호출,&amp;nbsp;객체&amp;nbsp;생성,&amp;nbsp;변수&amp;nbsp;값&amp;nbsp;변경&amp;nbsp;등&amp;nbsp;실제&amp;nbsp;동작이&amp;nbsp;이루어짐 &lt;br /&gt;Reflection은&amp;nbsp;이&amp;nbsp;시점에서&amp;nbsp;클래스&amp;nbsp;정보를&amp;nbsp;읽어옴&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;코드로 보는 차이&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;일반 호출(컴파일 시점에 확정)&amp;gt;&lt;/p&gt;
&lt;pre id=&quot;code_1753937627221&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Person p = new Person(); // 컴파일러가 Person 클래스 존재 확인
p.sayHello();           // 메서드 존재 여부도 컴파일 시점에 체크됨&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;lt;Reflection&amp;nbsp;호출&amp;nbsp;(런타임에만&amp;nbsp;검사됨)&amp;gt;&lt;/p&gt;
&lt;pre id=&quot;code_1753937679399&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Class&amp;lt;?&amp;gt; clazz = Class.forName(&quot;Person&quot;);   // 문자열 기반 &amp;rarr; 컴파일러는 확인 못함
Object obj = clazz.getDeclaredConstructor().newInstance();
Method m = clazz.getMethod(&quot;sayHello&quot;);     // 없는 메서드면 여기서 런타임 예외
m.invoke(obj);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>공개 글</category>
      <category>class</category>
      <category>Class객체</category>
      <category>Reflection</category>
      <category>개발일기</category>
      <category>리플랙션</category>
      <author>영발개발</author>
      <guid isPermaLink="true">https://zerofootblog.tistory.com/16</guid>
      <comments>https://zerofootblog.tistory.com/16#entry16comment</comments>
      <pubDate>Thu, 31 Jul 2025 13:53:49 +0900</pubDate>
    </item>
    <item>
      <title>static</title>
      <link>https://zerofootblog.tistory.com/15</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;자바 소스를 보다보면 자주 보이는 녀석. 오늘은 static 변수와 메서드에 대해서 글을 쓴다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;static?&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;static은 '정적인', '고정된'이라는 뜻을 가지고 있는 만큼, 클래스 안에서 static으로 선언된 변수나 메서드는 객체 없이 클래스 자체에 고정되어 공유되는 형태로 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&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_1746161844636&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class MathUtil {
    public int square(int x) {
        return x * x;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1746161927132&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MathUtil util = new MathUtil();
int result = util.square(5);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이런 식으로 귀찮게 객체 생성을 한 번 해주고 메서드를 사용해야한다. 매번 이렇게 사용하면 귀찮고 코드도 많아질 것이다. 하지만 static을 사용한다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1746162077955&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class MathUtil {
    public static int square(int x) {
        return x * x;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1746162103478&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int result = MathUtil.square(5);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;간단하게 사용이 가능하다. 이걸 보고나면 우리가 보통 코딩을 처음 배우면 Main class에다가 main메서드를 만들어서 사용할 때 main 메서드에 static이 항상 붙는 이유를 알 수 있을 것이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;static 사용시 주의할 점&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;static은 인스턴스 변수에 접근이 불가하다는 특징이 있다. 이게 무슨 말인지 간단한 코드를 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1746170160733&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Person {
    String name;

    public static void sayHello() {
        System.out.println(&quot;Hello, &quot; + name); // ⚠ 오류 발생!
    }
}&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;'name'은 인스턴스 변수이다. 즉, 'new Person()' 해서 만들어진 객체 안에 있는 값이다. 하지만 'sayHello()'는 static 메서드이므로 객체 없이도 호출이 가능하다. 그렇다면 객체 생성이 되지 않은 상태에서 인스턴스 변수를 쓰려고 하니 오류가 나는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1746170358997&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Person {
    String name;

    public void sayHello() {
        System.out.println(&quot;Hello, &quot; + name); // this.name 가능
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여기서는 'sayHello()'가 static이 아니므로 객체가 있어야 호출되고, 그 객체의 'name'을 쓸 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;언제 static을 쓰면 좋을까?&lt;/b&gt;&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체의 상태에 의존하지 않는 기능 (ex. 덧셈, 날짜 계산 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1746171826715&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Person p1 = new Person(&quot;영발&quot;);
p1.sayHello();  // 출력: Hello, 영발&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; Person마다 name이라는 상태(정보)를 갖는 경우, 해당 메서드는 p1이라는 객체가 어떤 이름을 갖고 있는지(상태)에 따라 결과가 바뀐다. 이런 경우에는 static을 사용하면 당연히 적절하지 못할 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1746171986867&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static int add(int a, int b) {
    return a + b;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반대로 위와 같은 add 메서드는 누가 호출하든 결과가 같을 것이고 이와 같은 경우(입력만 있으면 결과가 항상 같은 계산, 변환 등)에 static을 사용하기 적절하다고 볼 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유틸 클래스(Utils, Converter, Math)&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_1746172197960&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class MathUtils {
    public static int square(int x) {
        return x * x;
    }

    public static int max(int a, int b) {
        return a &amp;gt; b ? a : b;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예시 코드인 MathUtils를 보면 상태를 가지지 않고, 필드도 없다. 오직 기능만 있으므로 전부 static으로 만들어도 문제가 없다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메인 함수('public static void main')처럼 프로그램 진입점&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;프로그램의 진입점은 클래스의 인스턴스 없이 실행되어야 하므로 static으로 선언되어야 한다.&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;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;-끝-&lt;/p&gt;</description>
      <category>공개 글</category>
      <category>static</category>
      <category>개발일기</category>
      <author>영발개발</author>
      <guid isPermaLink="true">https://zerofootblog.tistory.com/15</guid>
      <comments>https://zerofootblog.tistory.com/15#entry15comment</comments>
      <pubDate>Fri, 2 May 2025 17:03:34 +0900</pubDate>
    </item>
    <item>
      <title>트랜잭션(Transaction)</title>
      <link>https://zerofootblog.tistory.com/14</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;대학생 시절에 정보처리기사 공부하다가 트랜잭션의 특성을 외웠던 게 기억난다. 사실 그때 개발을 아예 안해봐서 뭔 뜻인지도 모르고 그냥 달달 외웠던 게 기억난다. 현재 프로젝트에서 개발 가이드 문서를 보다가 트랜잭션 처리하는 쪽이 나와서 보다가 오랜만에 찾아보게 되었고 그런 김에 간단하게 트랜잭션에 대해서 글을 써본다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;트랜잭션(Transaction)이란?&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;트랜잭션은 데이터베이스 작업을 하나의 묶음으로 처리하는 단위다. 트랜잭션은 반드시 모두 성공하거나, 모두 실패해야 한다. 중간에 오류가 발생하면 이전까지의 작업도 모두 취소되어 데이터가 꼬이지 않게 해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;트랜잭션이 필요한 이유&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;1483&quot; data-start=&quot;1351&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 들어, 콘서트 좌석 예약 시스템에서는 좌석을 동시에 여러 명이 선택할 수 있다. 만약 트랜잭션이 없다면 같은 좌석이 중복 예약될 수 있다. 트랜잭션을 적용하면 한 명만 좌석 예약에 성공하고, 다른 시도는 실패하게 할 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;1483&quot; data-start=&quot;1351&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-end=&quot;1483&quot; data-start=&quot;1351&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;트랜잭션의 4가지 특성(ACID)&lt;/b&gt;&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원자성(Atomicity) : 작업이 전부 완료되거나 전부 실패해야 한다.&lt;/li&gt;
&lt;li&gt;일관성(Consistency) : 트랜잭션 전후로 데이터 무결성이 유지되어야 한다.&lt;/li&gt;
&lt;li&gt;격리성(Isolation) : 동시에 여러 트랜잭션이 실행돼도 서로 간섭하지 않아야 한다.&lt;/li&gt;
&lt;li&gt;지속성(Durability) : 트랜잭션이 성공하면 그 결과는 영구적으로 보존된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Spring Boot에서 트랜잭션 다루기&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Spring Boot에서는 @Transactional 어노테이션을 사용해 메서드 단위로 트랜잭션을 관리할 수 있다. 메서드 안에서 여러 DB 작업을 수행하다가 예외가 발생하면, 자동으로 모든 변경사항이 롤백된다.&lt;/p&gt;
&lt;p data-ke-size=&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_1745817020482&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional
public void createOrder() {
    orderRepository.save(order);       // 주문 저장
    stockRepository.decreaseStock();   // 재고 감소
    paymentService.pay();              // 결제 처리 (여기서 예외 발생!)
    notificationService.sendEmail();   // 이건 실행 안 됨
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;주문이 저장되고 재고 감소처리까지 된 상황에서 결제 처리에서 예외가 발생하면 맨 마지막에 결제완료 메일을 보내는 작업은 실행이 되지 않고, 앞서 진행되었던 주문 저장과 재고 감소도 전부 롤백 처리된다. 이런 식으로 트랜잭션 처리를 하면 중간에 오류가 발생해도 데이터가 꼬이지 않게 된다.&lt;/p&gt;</description>
      <category>공개 글</category>
      <category>transaction</category>
      <category>개발일기</category>
      <category>트랜잭션</category>
      <author>영발개발</author>
      <guid isPermaLink="true">https://zerofootblog.tistory.com/14</guid>
      <comments>https://zerofootblog.tistory.com/14#entry14comment</comments>
      <pubDate>Mon, 28 Apr 2025 14:17:35 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript 기본</title>
      <link>https://zerofootblog.tistory.com/13</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;script 태그&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;자바 스크립트는 html 위에서 동작하는 언어다. 자바 스크립트를 html 안에 작성하기 위해서는 script 태그를 사용하는 방법이 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1742259896002&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; &amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;script&amp;gt;
        document.write(&quot;Hello&quot;);
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&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-origin-width=&quot;195&quot; data-origin-height=&quot;117&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mcprF/btsMNBMD6GJ/nNpbHoTKseNl4Vi3kybCk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mcprF/btsMNBMD6GJ/nNpbHoTKseNl4Vi3kybCk0/img.png&quot; data-alt=&quot;실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mcprF/btsMNBMD6GJ/nNpbHoTKseNl4Vi3kybCk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmcprF%2FbtsMNBMD6GJ%2FnNpbHoTKseNl4Vi3kybCk0%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;195&quot; height=&quot;117&quot; data-origin-width=&quot;195&quot; data-origin-height=&quot;117&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Event&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;웹 브라우저 위에서 일어나는 사건을 &lt;b&gt;Event(이벤트)&lt;/b&gt;라고 한다. 마우스를 클릭했을때, 키보드를 눌렀을때, 스크롤 바를 움직였을때 등 모두 이벤트라고 할 수 있다. 사용자가 어떤 이벤트를 발생시켰을 해당 이벤트에 대응하여 처리하는 것을 &lt;b&gt;'Event Handler(이벤트 핸들러)'&lt;/b&gt;라고 하는데, 이벤트 이름 앞에 'on'을 붙이고 이벤트에 대한 동작(함수)을 처리해주면 된다. 아래 코드에서처럼 on______ 뒤에는 자바스크립트 문법이 와야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1742274534085&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;input type=&quot;button&quot; value=&quot;hello&quot; onclick=&quot;alert('Hello')&quot;&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&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-origin-width=&quot;719&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8RWly/btsMM3DdPNF/cwMd5uKQcUZL1q9gCGeOUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8RWly/btsMM3DdPNF/cwMd5uKQcUZL1q9gCGeOUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8RWly/btsMM3DdPNF/cwMd5uKQcUZL1q9gCGeOUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8RWly%2FbtsMM3DdPNF%2FcwMd5uKQcUZL1q9gCGeOUk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;719&quot; height=&quot;218&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;제어할 태그 선택하기&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;'querySelector()', 'getElementById()' 등을 사용해서 내가 제어할 태그를 선택할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1742432087243&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;BLOG - git &amp;amp; github&amp;lt;/title&amp;gt;
        &amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;
        &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&amp;gt;
    &amp;lt;/head&amp;gt;
    
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;
            &amp;lt;a href=&quot;index.html&quot;&amp;gt;BLOG&amp;lt;/a&amp;gt;
            &amp;lt;span&amp;gt;
                &amp;lt;button&amp;gt;night&amp;lt;/button&amp;gt;
                &amp;lt;button&amp;gt;day&amp;lt;/button&amp;gt;
            &amp;lt;/span&amp;gt;
        &amp;lt;/h1&amp;gt;
        
        &amp;lt;div id=&quot;grid&quot;&amp;gt;
            &amp;lt;ol&amp;gt;
                &amp;lt;li&amp;gt;&amp;lt;a href=&quot;1.html&quot;&amp;gt;http&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
                &amp;lt;li&amp;gt;&amp;lt;a href=&quot;2.html&quot;&amp;gt;API &amp;amp; REST API&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
                &amp;lt;li&amp;gt;&amp;lt;a href=&quot;3.html&quot;&amp;gt;Git &amp;amp; Github&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
            &amp;lt;/ol&amp;gt;
            &amp;lt;div id=&quot;text&quot;&amp;gt;
                &amp;lt;p&amp;gt; 개발자라면 깃과 깃허브에 대해서 알고 있을 것이다. 써본 적도 있고 알고는 있는데 뭔가 제대로 알고 쓰는 거 같은 느낌이 들지 않아 유튜브에 있는 유노코딩 님의 강의를 보았다. 깃을 모르는 초보자들이나 나처럼 대충 알고 쓰고 있던 사람들이 아주 보기 좋도록 잘 정리되어 있고 도움이 많이 되었다. 그래서 해당 내용을 정리하고자 공부한 내용과 영상 내용을 요약하여 글로 남겨볼까 한다. (해당 글 안에 내용들의 출처는 유노코딩님으로, 강의 내용들을 요약한 것이다.)&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;a href=&quot;https://youtu.be/PwIei1tdHSU?si=2hphqG_O8tWuhLbE&quot; target=&quot;_blank&quot;&amp;gt;유노코딩님의 강의 링크&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt; 우선 강의에서 알려주는 깃과 깃허브, 브랜치 등 주요 내용들을 먼저 정리하고, 그에 관한 명령어들을 한 번에 정리하는 식으로 글을 작성하려 한다.&amp;lt;/p&amp;gt;
                &amp;lt;h2&amp;gt;git에 관한 주요 내용 정리&amp;lt;/h2&amp;gt;
                &amp;lt;h3&amp;gt;&amp;lt;a href=&quot;https://zerofootblog.tistory.com/4&quot; target=&quot;_blank&quot; title=&quot;git specification&quot;&amp;gt;깃(Git)이란?&amp;lt;/a&amp;gt;&amp;lt;/h3&amp;gt;
                &amp;lt;p&amp;gt; 깃은 소규모 프로젝트에서 초대형 프로젝트에 이르기까지 모든 것을 신속하고 효율적으로 처리하도록 설계된 오픈 소스 버전 제어 시스템이다. (쉽게 생각하면 우리가 컴퓨터에 설치하는 다양한 종류의 소프트웨어 중 한 가지이다.)&amp;lt;/p&amp;gt;
            &amp;lt;/div&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1263&quot; data-origin-height=&quot;393&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kWIFJ/btsMQb1mCXg/cA8UkK90YmemoFyqOnOoaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kWIFJ/btsMQb1mCXg/cA8UkK90YmemoFyqOnOoaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kWIFJ/btsMQb1mCXg/cA8UkK90YmemoFyqOnOoaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkWIFJ%2FbtsMQb1mCXg%2FcA8UkK90YmemoFyqOnOoaK%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;1263&quot; height=&quot;393&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1263&quot; data-origin-height=&quot;393&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;해당 코드는 간단한 블로그 화면을 만들어본 것인데, 위에 night와 day버튼을 눌렀을때, 다크모드와 라이트모드로 각각 변할 수 있게 자바스크립트 코드를 넣어볼 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742432506090&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;BLOG - git &amp;amp; github&amp;lt;/title&amp;gt;
        &amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;
        &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&amp;gt;
    &amp;lt;/head&amp;gt;
    
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;
            &amp;lt;a href=&quot;index.html&quot;&amp;gt;BLOG&amp;lt;/a&amp;gt;
            &amp;lt;span&amp;gt;
                &amp;lt;button onclick=&quot;
                document.querySelector('body').style.backgroundColor='black';
                document.querySelector('body').style.color='white';
                &quot;&amp;gt;night&amp;lt;/button&amp;gt;
                &amp;lt;button onclick=&quot;
                document.querySelector('body').style.backgroundColor='white';
                document.querySelector('body').style.color='black';
                &quot;&amp;gt;day&amp;lt;/button&amp;gt;
            &amp;lt;/span&amp;gt;
        &amp;lt;/h1&amp;gt;
        
        &amp;lt;div id=&quot;grid&quot;&amp;gt;
            &amp;lt;ol&amp;gt;
                &amp;lt;li&amp;gt;&amp;lt;a href=&quot;1.html&quot;&amp;gt;http&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
                &amp;lt;li&amp;gt;&amp;lt;a href=&quot;2.html&quot;&amp;gt;API &amp;amp; REST API&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
                &amp;lt;li&amp;gt;&amp;lt;a href=&quot;3.html&quot;&amp;gt;Git &amp;amp; Github&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
            &amp;lt;/ol&amp;gt;
            &amp;lt;div id=&quot;text&quot;&amp;gt;
                &amp;lt;p&amp;gt; 개발자라면 깃과 깃허브에 대해서 알고 있을 것이다. 써본 적도 있고 알고는 있는데 뭔가 제대로 알고 쓰는 거 같은 느낌이 들지 않아 유튜브에 있는 유노코딩 님의 강의를 보았다. 깃을 모르는 초보자들이나 나처럼 대충 알고 쓰고 있던 사람들이 아주 보기 좋도록 잘 정리되어 있고 도움이 많이 되었다. 그래서 해당 내용을 정리하고자 공부한 내용과 영상 내용을 요약하여 글로 남겨볼까 한다. (해당 글 안에 내용들의 출처는 유노코딩님으로, 강의 내용들을 요약한 것이다.)&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;a href=&quot;https://youtu.be/PwIei1tdHSU?si=2hphqG_O8tWuhLbE&quot; target=&quot;_blank&quot;&amp;gt;유노코딩님의 강의 링크&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt; 우선 강의에서 알려주는 깃과 깃허브, 브랜치 등 주요 내용들을 먼저 정리하고, 그에 관한 명령어들을 한 번에 정리하는 식으로 글을 작성하려 한다.&amp;lt;/p&amp;gt;
                &amp;lt;h2&amp;gt;git에 관한 주요 내용 정리&amp;lt;/h2&amp;gt;
                &amp;lt;h3&amp;gt;&amp;lt;a href=&quot;https://zerofootblog.tistory.com/4&quot; target=&quot;_blank&quot; title=&quot;git specification&quot;&amp;gt;깃(Git)이란?&amp;lt;/a&amp;gt;&amp;lt;/h3&amp;gt;
                &amp;lt;p&amp;gt; 깃은 소규모 프로젝트에서 초대형 프로젝트에 이르기까지 모든 것을 신속하고 효율적으로 처리하도록 설계된 오픈 소스 버전 제어 시스템이다. (쉽게 생각하면 우리가 컴퓨터에 설치하는 다양한 종류의 소프트웨어 중 한 가지이다.)&amp;lt;/p&amp;gt;
            &amp;lt;/div&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1291&quot; data-origin-height=&quot;448&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pYbVd/btsMQ7jwYqH/YXzDsq0kBWBDepud8gk0I1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pYbVd/btsMQ7jwYqH/YXzDsq0kBWBDepud8gk0I1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pYbVd/btsMQ7jwYqH/YXzDsq0kBWBDepud8gk0I1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpYbVd%2FbtsMQ7jwYqH%2FYXzDsq0kBWBDepud8gk0I1%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;1291&quot; height=&quot;448&quot; data-origin-width=&quot;1291&quot; data-origin-height=&quot;448&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;'querySelector'를 이용한 자바스크립트 코드를 통해 night버튼을 눌렀을 때, body태그의 백그라운드 컬러와 글씨 색을 변경할 수 있도록 만들었다. 하지만 a태그에는 css 효과가 따로 적용되어 있었기 때문에 우선 순위가 밀려 적용이 되지 않았다. 그러면 따로 a태그도 선택해서 효과를 주어야 하는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742434626741&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;BLOG - git &amp;amp; github&amp;lt;/title&amp;gt;
        &amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;
        &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&amp;gt;
    &amp;lt;/head&amp;gt;
    
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;
            &amp;lt;a href=&quot;index.html&quot;&amp;gt;BLOG&amp;lt;/a&amp;gt;
            &amp;lt;span&amp;gt;
                &amp;lt;button onclick=&quot;
                document.querySelector('body').style.backgroundColor='black';
                document.querySelector('body').style.color='white';
                document.querySelector('a').style.color='white';
                &quot;&amp;gt;night&amp;lt;/button&amp;gt;
                &amp;lt;button onclick=&quot;
                document.querySelector('body').style.backgroundColor='white';
                document.querySelector('body').style.color='black';
                document.querySelector('a').style.color='black';
                &quot;&amp;gt;day&amp;lt;/button&amp;gt;
        &amp;lt;/h1&amp;gt;
        &amp;lt;!-- 생략 --&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-origin-width=&quot;1184&quot; data-origin-height=&quot;427&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/92egH/btsMQclAvP8/h7Jgqx7BPoyKpwekgRlzb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/92egH/btsMQclAvP8/h7Jgqx7BPoyKpwekgRlzb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/92egH/btsMQclAvP8/h7Jgqx7BPoyKpwekgRlzb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F92egH%2FbtsMQclAvP8%2Fh7Jgqx7BPoyKpwekgRlzb1%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;1184&quot; height=&quot;427&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;427&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;보는 것과 같이 h1안에 있는 a태그는 변경이 됐지만, ol 태그 안에 있던 3개의 a태그는 변경이 안됐다. 그 이유는 'querySelector'는 선택한 태그의 오직 첫번째 태그만 적용이 되기 때문이다. 여러개의 a태그를 선택하기 위해서는 'querySelectorAll'을 사용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742435130838&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;BLOG - git &amp;amp; github&amp;lt;/title&amp;gt;
        &amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;
        &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&amp;gt;
    &amp;lt;/head&amp;gt;
    
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;
            &amp;lt;a href=&quot;index.html&quot;&amp;gt;BLOG&amp;lt;/a&amp;gt;
            &amp;lt;span&amp;gt;
                &amp;lt;button onclick=&quot;
                document.querySelector('body').style.backgroundColor='black';
                document.querySelector('body').style.color='white';

                document.querySelectorAll('a').forEach(list =&amp;gt; {
                    list.style.color='white';
                })
                &quot;&amp;gt;night&amp;lt;/button&amp;gt;
                &amp;lt;button onclick=&quot;
                document.querySelector('body').style.backgroundColor='white';
                document.querySelector('body').style.color='black';
                
                document.querySelectorAll('a').forEach(list =&amp;gt; {
                    list.style.color='black';
                })
                &quot;&amp;gt;day&amp;lt;/button&amp;gt;
        &amp;lt;/h1&amp;gt;
        &amp;lt;!-- 생략 --&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-origin-width=&quot;1312&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bN5WOm/btsMPzuWEBA/q8Pk0lH9s2Uvy9pxylreZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bN5WOm/btsMPzuWEBA/q8Pk0lH9s2Uvy9pxylreZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bN5WOm/btsMPzuWEBA/q8Pk0lH9s2Uvy9pxylreZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbN5WOm%2FbtsMPzuWEBA%2Fq8Pk0lH9s2Uvy9pxylreZk%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;1312&quot; height=&quot;422&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이와같이 정상적으로 모두 적용되는 것을 볼 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;'===' or '==' ?&amp;nbsp;&lt;/b&gt;&lt;/blockquote&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;var, let, const ?&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;세 가지 모두 javascript에서 변수를 선언할 때 사용되는 키워드이다. 여러가지 특성들이 있지만 요약하자면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;var : 가능하면 쓰지 않기&lt;/li&gt;
&lt;li&gt;let :&amp;nbsp; 값이 변하는 변수를 선언할 때 사용하기&lt;/li&gt;
&lt;li&gt;const : 값이 변하지 않는 변수를 선언할 때 사용하기&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;객체 &lt;/b&gt;&lt;/blockquote&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;pre id=&quot;code_1742460141089&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Object&amp;lt;/h1&amp;gt;
    &amp;lt;script&amp;gt;
        const numbers = {
            &quot;one&quot; : &quot;1&quot;,
            &quot;two&quot; : &quot;2&quot;,
        }
        document.write(&quot;one : &quot; + numbers.one + &quot;&amp;lt;br&amp;gt;&quot;);
        document.write(&quot;two : &quot; + numbers.two)
    &amp;lt;/script&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;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;pre id=&quot;code_1742460503559&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Object&amp;lt;/h1&amp;gt;
    &amp;lt;script&amp;gt;
        const numbers = {
            &quot;one&quot; : &quot;1&quot;,
            &quot;two&quot; : &quot;2&quot;,
        }
        document.write(&quot;one : &quot; + numbers.one + &quot;&amp;lt;br&amp;gt;&quot;);
        document.write(&quot;two : &quot; + numbers.two + &quot;&amp;lt;br&amp;gt;&quot;);
        numbers.three = &quot;3&quot;;
        document.write(&quot;three : &quot; + numbers.three + &quot;&amp;lt;br&amp;gt;&quot;);
        numbers[&quot;number four&quot;] = &quot;4&quot;;
        document.write(&quot;four : &quot; + numbers[&quot;number four&quot;] + &quot;&amp;lt;br&amp;gt;&quot;); 
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&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-origin-width=&quot;327&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/syCBA/btsMSCwP4DO/Kh1i4R3KykUAPLkJsPJvh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/syCBA/btsMSCwP4DO/Kh1i4R3KykUAPLkJsPJvh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/syCBA/btsMSCwP4DO/Kh1i4R3KykUAPLkJsPJvh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsyCBA%2FbtsMSCwP4DO%2FKh1i4R3KykUAPLkJsPJvh0%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;327&quot; height=&quot;192&quot; data-origin-width=&quot;327&quot; data-origin-height=&quot;192&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;'number four'와 같은 경우에는 중간에 띄어쓰기가 포함되어 있기 때문에 ' numbers[&quot;number four&quot;] = &quot;4&quot; '라는 식으로 코딩을 해야한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;for...in 과 for...of&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;for...in은 주로 객체의 속성(키)을 반복할 때, for...of는 배열, 문자열, Set, Map과 같은 이터러블 객체를 반복할 때 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;for...in&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1742519480488&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const obj = { a: 1, b: 2, c: 3 };

for (let key in obj) {
	document.write(key + &quot; : &quot; + obj[key] + &quot;&amp;lt;br&amp;gt;&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;186&quot; data-origin-height=&quot;106&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oQUoU/btsMQH7QPHG/LJtb3KPcOY0gynGevmudE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oQUoU/btsMQH7QPHG/LJtb3KPcOY0gynGevmudE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oQUoU/btsMQH7QPHG/LJtb3KPcOY0gynGevmudE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoQUoU%2FbtsMQH7QPHG%2FLJtb3KPcOY0gynGevmudE1%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;186&quot; height=&quot;106&quot; data-origin-width=&quot;186&quot; data-origin-height=&quot;106&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;for...of&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1742519933705&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arr = [10, 20, 30];

for (let value of arr) {
  document.write(value + &quot;&amp;lt;br&amp;gt;&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;150&quot; data-origin-height=&quot;99&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQZw5f/btsMRpFtUJ1/GH7avrEkULjaG0rc5ISPPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQZw5f/btsMRpFtUJ1/GH7avrEkULjaG0rc5ISPPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQZw5f/btsMRpFtUJ1/GH7avrEkULjaG0rc5ISPPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQZw5f%2FbtsMRpFtUJ1%2FGH7avrEkULjaG0rc5ISPPK%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;150&quot; height=&quot;99&quot; data-origin-width=&quot;150&quot; data-origin-height=&quot;99&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;객체에 함수 선언해서 사용하기&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;객체 안에 함수를 선언해서 코드를 더 간략하게 만들 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1742797735960&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Body = {
    setColor:function(color) {
        document.querySelector('body').style.color = color;
    },
    setBackgroundColor:function(color) {
        document.querySelector('body').style.backgroundColor = color;
    }
}
const Links = {
    setColor:function(color) {
        const alist = document.querySelectorAll('a');
        let i = 0;
        while(i &amp;lt; alist.length) {
            alist[i].style.color = color;
            i++;
        }
    }
}
const Borders = {
    setColor:function(color) {
        document.querySelector('#grid ol').style.borderRight = `1px solid ${color}`;
        document.querySelector('h1').style.borderBottom = `1px solid ${color}`;
    }
}
function nightDayHandler(self) {
    if(self.innerText === 'night'){
        Body.setColor('white');
        Body.setBackgroundColor('black');
        self.innerText = 'day';

        Links.setColor('white');

        Borders.setColor('white');
    } else {
        Body.setColor('black');
        Body.setBackgroundColor('white');
        self.innerText = 'night';

        Links.setColor('black');

        Borders.setColor('black');
    }
}&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_1742797765885&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;BLOG - git &amp;amp; github&amp;lt;/title&amp;gt;
        &amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;
        &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&amp;gt;
        &amp;lt;script src=&quot;color.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;/head&amp;gt;
    
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;
            &amp;lt;a href=&quot;index.html&quot;&amp;gt;BLOG&amp;lt;/a&amp;gt;
            &amp;lt;span&amp;gt;
                &amp;lt;button onclick=&quot;nightDayHandler(this)&quot;&amp;gt;night&amp;lt;/button&amp;gt;
            &amp;lt;/span&amp;gt;
        &amp;lt;/h1&amp;gt;
        &amp;lt;div id=&quot;grid&quot;&amp;gt;
            &amp;lt;ol&amp;gt;
                &amp;lt;li&amp;gt;&amp;lt;a href=&quot;1.html&quot;&amp;gt;http&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
                &amp;lt;li&amp;gt;&amp;lt;a href=&quot;2.html&quot;&amp;gt;API &amp;amp; REST API&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
                &amp;lt;li&amp;gt;&amp;lt;a href=&quot;3.html&quot;&amp;gt;Git &amp;amp; Github&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
            &amp;lt;/ol&amp;gt;
            &amp;lt;div id=&quot;text&quot;&amp;gt;
                &amp;lt;p&amp;gt; 개발자라면 깃과 깃허브에 대해서 알고 있을 것이다. 써본 적도 있고 알고는 있는데 뭔가 제대로 알고 쓰는 거 같은 느낌이 들지 않아 유튜브에 있는 유노코딩 님의 강의를 보았다. 깃을 모르는 초보자들이나 나처럼 대충 알고 쓰고 있던 사람들이 아주 보기 좋도록 잘 정리되어 있고 도움이 많이 되었다. 그래서 해당 내용을 정리하고자 공부한 내용과 영상 내용을 요약하여 글로 남겨볼까 한다. (해당 글 안에 내용들의 출처는 유노코딩님으로, 강의 내용들을 요약한 것이다.)&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;a href=&quot;https://youtu.be/PwIei1tdHSU?si=2hphqG_O8tWuhLbE&quot; target=&quot;_blank&quot;&amp;gt;유노코딩님의 강의 링크&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt; 우선 강의에서 알려주는 깃과 깃허브, 브랜치 등 주요 내용들을 먼저 정리하고, 그에 관한 명령어들을 한 번에 정리하는 식으로 글을 작성하려 한다.&amp;lt;/p&amp;gt;
                &amp;lt;h2&amp;gt;git에 관한 주요 내용 정리&amp;lt;/h2&amp;gt;
                &amp;lt;h3&amp;gt;&amp;lt;a href=&quot;https://zerofootblog.tistory.com/4&quot; target=&quot;_blank&quot; title=&quot;git specification&quot;&amp;gt;깃(Git)이란?&amp;lt;/a&amp;gt;&amp;lt;/h3&amp;gt;
                &amp;lt;p&amp;gt; 깃은 소규모 프로젝트에서 초대형 프로젝트에 이르기까지 모든 것을 신속하고 효율적으로 처리하도록 설계된 오픈 소스 버전 제어 시스템이다. (쉽게 생각하면 우리가 컴퓨터에 설치하는 다양한 종류의 소프트웨어 중 한 가지이다.)&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>공개 글</category>
      <category>HTML</category>
      <category>javascript</category>
      <category>JS</category>
      <category>프론트엔드</category>
      <author>영발개발</author>
      <guid isPermaLink="true">https://zerofootblog.tistory.com/13</guid>
      <comments>https://zerofootblog.tistory.com/13#entry13comment</comments>
      <pubDate>Mon, 24 Mar 2025 15:30:32 +0900</pubDate>
    </item>
    <item>
      <title>Web Server와 WAS</title>
      <link>https://zerofootblog.tistory.com/12</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&amp;nbsp;&lt;b&gt;Web Server와 WAS, 그들의 역할&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Web Server&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;웹 서버는 정적인 요청(HTML, CSS, JavaScript, image 등)을 처리하는 서버다. 대표적인 웹 서버로는 Apache HTTP Server, NginX, Microsoft IIS 등이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;WAS(Web Application Server)&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;WAS는 동적인 요청(서버에서 처리해야 하는 로직이 필요한 요청)을 처리하는 서버로, Servlet과 JSP를 처리할 수 있는 Web Container를 제공한다. 대표적인 WAS로는 Tomcat, JBoss, WebLogic, WebSphere 등이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1550&quot; data-origin-height=&quot;481&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N3YIH/btsMMJw62Xx/pyGlZW1rvOCeELOTzB5uf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N3YIH/btsMMJw62Xx/pyGlZW1rvOCeELOTzB5uf0/img.png&quot; data-alt=&quot;출처 : https://taes-k.github.io/2019/05/24/webserver/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N3YIH/btsMMJw62Xx/pyGlZW1rvOCeELOTzB5uf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN3YIH%2FbtsMMJw62Xx%2FpyGlZW1rvOCeELOTzB5uf0%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;1550&quot; height=&quot;481&quot; data-origin-width=&quot;1550&quot; data-origin-height=&quot;481&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://taes-k.github.io/2019/05/24/webserver/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;클라이언트로부터 요청이 Web Server로 들어오면 정적인 요청일 경우 WAS까지 가지 않고 Web Server에서 처리한다. 하지만 동적인 요청이 들어올 경우 Web Server는 해당 요청을 WAS에게 전달한다. WAS는 해당 요청을 처리 후에 Web Server에게 전달하고 Web Server는 클라이언트에게 응답을 주는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;홈페이지의 목록을 조회하는 상황을 예시로 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;139&quot; data-start=&quot;84&quot; data-ke-size=&quot;size23&quot;&gt;✅ &lt;b&gt;홈페이지 목록 조회 요청의 처리 과정 (WAS &amp;amp; Web Server 역할 정리)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;205&quot; data-start=&quot;140&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  &lt;/b&gt;가정: 사용자가 &lt;a href=&quot;https://example.com/list&quot;&gt;https://example.com/list&lt;/a&gt; 페이지에서 목록을 조회한다고 해보자.&lt;/p&gt;
&lt;p data-end=&quot;240&quot; data-start=&quot;207&quot; data-ke-size=&quot;size16&quot;&gt;1️⃣ &lt;b&gt;사용자 요청 (클라이언트 &amp;rarr; 웹 서버)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;332&quot; data-start=&quot;241&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;291&quot; data-start=&quot;241&quot;&gt;사용자가 브라우저에서 &lt;a href=&quot;https://example.com/list&quot;&gt;https://example.com/list&lt;/a&gt; 페이지를 요청&lt;/li&gt;
&lt;li data-end=&quot;332&quot; data-start=&quot;292&quot;&gt;이 요청이 &lt;b&gt;웹 서버(Apache, Nginx 등)&lt;/b&gt; 에 도착&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;362&quot; data-start=&quot;334&quot; data-ke-size=&quot;size16&quot;&gt;2️⃣ &lt;b&gt;웹 서버 &amp;rarr; WAS 요청 전달&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;464&quot; data-start=&quot;363&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;423&quot; data-start=&quot;363&quot;&gt;웹 서버는 정적 리소스(HTML, CSS, JS 등)가 아니라 &lt;b&gt;동적인 데이터 조회 요청&lt;/b&gt;이므로,&lt;/li&gt;
&lt;li data-end=&quot;464&quot; data-start=&quot;424&quot;&gt;이 요청을 &lt;b&gt;WAS(Tomcat, JBoss 등)&lt;/b&gt; 에게 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;498&quot; data-start=&quot;466&quot; data-ke-size=&quot;size16&quot;&gt;3️⃣ &lt;b&gt;WAS가 서블릿을 실행하고 DB 조회&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;668&quot; data-start=&quot;499&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;553&quot; data-start=&quot;499&quot;&gt;WAS 내부의 &lt;b&gt;웹 컨테이너(Web Container)&lt;/b&gt; 가 요청을 받을 서블릿을 실행&lt;/li&gt;
&lt;li data-end=&quot;602&quot; data-start=&quot;554&quot;&gt;서블릿은 &lt;b&gt;비즈니스 로직(Service)&lt;/b&gt; 을 호출하여 DB에서 데이터 조회&lt;/li&gt;
&lt;li data-end=&quot;668&quot; data-start=&quot;603&quot;&gt;조회된 데이터(예: 게시글 목록)를 &lt;b&gt;JSP/Thymeleaf 같은 템플릿 엔진과 결합해서 HTML 생성&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;706&quot; data-start=&quot;670&quot; data-ke-size=&quot;size16&quot;&gt;4️⃣&lt;b&gt;WAS &amp;rarr; 웹 서버 &amp;rarr; 사용자에게 응답 반환&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;804&quot; data-start=&quot;707&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;746&quot; data-start=&quot;707&quot;&gt;WAS가 생성한 동적인 HTML 페이지를 &lt;b&gt;웹 서버에 전달&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;777&quot; data-start=&quot;747&quot;&gt;웹 서버는 클라이언트(브라우저)에게 응답을 반환&lt;/li&gt;
&lt;li data-end=&quot;804&quot; data-start=&quot;778&quot;&gt;사용자는 목록이 출력된 웹 페이지를 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;Web Server와 WAS를 따로 두는 이유&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;웹 서버와 WAS에 대해서 찾아보다가 궁금한 점이 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGJXL0/btsMNGGAWkO/mRXpUKI9gP87F3JS9ZK2o0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGJXL0/btsMNGGAWkO/mRXpUKI9gP87F3JS9ZK2o0/img.png&quot; data-alt=&quot;출처 : https://ecsimsw.tistory.com/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGJXL0/btsMNGGAWkO/mRXpUKI9gP87F3JS9ZK2o0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGJXL0%2FbtsMNGGAWkO%2FmRXpUKI9gP87F3JS9ZK2o0%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;1280&quot; height=&quot;363&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://ecsimsw.tistory.com/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;해당 그림에서 보이는 것처럼 WAS 안에는 WS(웹서버) 기능이 포함되어 있다. 그럼에도 독립된 WS를 두는 이유가 궁금했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;211&quot; data-start=&quot;165&quot; data-ke-size=&quot;size26&quot;&gt;✅ &lt;b&gt;WebServer와 WAS를 따로 두는 이유&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;384&quot; data-start=&quot;361&quot; data-ke-size=&quot;size16&quot;&gt;1️⃣ &lt;b&gt;정적 리소스 처리 최적화&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;519&quot; data-start=&quot;388&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;466&quot; data-start=&quot;388&quot;&gt;웹 서버(Apache, Nginx)는 정적 리소스(HTML, CSS, JS, 이미지 등)를 빠르게 처리할 수 있도록 최적화되어 있음.&lt;/li&gt;
&lt;li data-end=&quot;519&quot; data-start=&quot;470&quot;&gt;하지만 WAS는 이런 정적 리소스 처리를 최적화하는 데 초점이 맞춰져 있지 않음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;545&quot; data-start=&quot;521&quot; data-ke-size=&quot;size16&quot;&gt;2️⃣ &lt;b&gt;부하 분산 (로드 밸런싱)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;645&quot; data-start=&quot;549&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;610&quot; data-start=&quot;549&quot;&gt;웹 서버가 먼저 요청을 받고, 여러 개의 WAS로 부하를 분산(Load Balancing)할 수 있음.&lt;/li&gt;
&lt;li data-end=&quot;645&quot; data-start=&quot;614&quot;&gt;예: &lt;b&gt;Nginx &amp;rarr; 여러 개의 Tomcat&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;662&quot; data-start=&quot;647&quot; data-ke-size=&quot;size16&quot;&gt;3️⃣ &lt;b&gt;보안 강화&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;782&quot; data-start=&quot;666&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;720&quot; data-start=&quot;666&quot;&gt;WAS를 직접 외부에 노출하지 않고 웹 서버를 프록시 역할로 둬서 보안을 강화할 수 있음.&lt;/li&gt;
&lt;li data-end=&quot;782&quot; data-start=&quot;724&quot;&gt;예: WAS는 내부 네트워크에서만 접근 가능하도록 하고, 웹 서버가 외부 요청을 필터링한 후 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;803&quot; data-start=&quot;784&quot; data-ke-size=&quot;size16&quot;&gt;4️⃣ &lt;b&gt;WAS 성능 향상&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;910&quot; data-start=&quot;807&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;859&quot; data-start=&quot;807&quot;&gt;WAS는 비즈니스 로직(DB 조회, 연산 등)만 집중적으로 처리할 수 있도록 최적화됨.&lt;/li&gt;
&lt;li data-end=&quot;910&quot; data-start=&quot;863&quot;&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;그럼 여기서 추가로 궁금한 점이 생긴다. 이렇게 WS와 WAS를 따로 둘 경우, WAS에 있는 WS는 작동을 안하고 따로 둔 WS만 작동할까? 기본적으로는 WAS 안에 있는 WS 기능을 비활성화하고, NginX나 아파치 같은 외부 웹 서버에서만 요청을 처리하도록 구성한다.&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;그렇다고 WAS에 있는 WS 기능을 아예 안 쓰는 것은 아니다. 보통 트래픽이 상대적으로 적고 부하가 크지 않은 소규모 프로젝트에서는 웹 서버와 WAS를 따로 둘 필요가 굳이 없기 때문에 WAS만 단독 사용한다. 반대로 트래픽이 많고 부하가 큰 대규모 프로젝트에서는 위에서 말한 이점들을 위해 웹 서버와 WAS를 따로 두어 실행한다.&lt;/p&gt;</description>
      <category>공개 글</category>
      <category>WAS</category>
      <category>Web Server</category>
      <category>WS</category>
      <category>개발</category>
      <category>개발일기</category>
      <category>웹서버</category>
      <author>영발개발</author>
      <guid isPermaLink="true">https://zerofootblog.tistory.com/12</guid>
      <comments>https://zerofootblog.tistory.com/12#entry12comment</comments>
      <pubDate>Mon, 17 Mar 2025 15:24:34 +0900</pubDate>
    </item>
    <item>
      <title>CSS 기본 - 2</title>
      <link>https://zerofootblog.tistory.com/11</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Grid(그리드)&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1741831481827&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;style&amp;gt;
        #grid {
            border: 5px solid pink;
            display: grid;
            grid-template-columns: 150px 1fr;
        }
        div {
            border: 5px solid gray;
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div id=&quot;grid&quot;&amp;gt;
        &amp;lt;div&amp;gt;NAVIGATION&amp;lt;/div&amp;gt;
        &amp;lt;div&amp;gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. 
        Totam odit dignissimos expedita voluptas repellendus beatae doloribus inventore? 
        Totam quis rem aspernatur repellat harum error.
        Odio vel atque quos recusandae dolore.&amp;lt;/div&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;나누고자 하는 태그들을 id가 grid라는 하나의 div태그로 묶고, css에서 display 속성을 grid로 설정해준다. 그리고 grid-template-colums로 grid의 가로열 크기를 조정해준다. 위 코드는 NAVIGATION을 150px로 설정하고 나머지 크기는 다른 하나의 div태그가 갖는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1006&quot; data-origin-height=&quot;239&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F1Hv0/btsMJxiCVbS/QjXxk21dBQho2wtD0uXju1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F1Hv0/btsMJxiCVbS/QjXxk21dBQho2wtD0uXju1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F1Hv0/btsMJxiCVbS/QjXxk21dBQho2wtD0uXju1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF1Hv0%2FbtsMJxiCVbS%2FQjXxk21dBQho2wtD0uXju1%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;1006&quot; height=&quot;239&quot; data-origin-width=&quot;1006&quot; data-origin-height=&quot;239&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;pre id=&quot;code_1741832149755&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;style&amp;gt;
        #grid {
            border: 5px solid pink;
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            grid-template-rows: 200px 200px;
        }
        div {
            border: 5px solid gray;
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div id=&quot;grid&quot;&amp;gt;
        &amp;lt;div&amp;gt;1&amp;lt;/div&amp;gt;
        &amp;lt;div&amp;gt;2&amp;lt;/div&amp;gt;
        &amp;lt;div&amp;gt;3&amp;lt;/div&amp;gt;
        &amp;lt;div&amp;gt;4&amp;lt;/div&amp;gt;
        &amp;lt;div&amp;gt;5&amp;lt;/div&amp;gt;
        &amp;lt;div&amp;gt;6&amp;lt;/div&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;위 코드처럼 grid-template-rows로 세로 크기도 지정해 줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1117&quot; data-origin-height=&quot;471&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DsNbC/btsMKbGanUG/OSkzjtKWysyL0dnZZeSIZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DsNbC/btsMKbGanUG/OSkzjtKWysyL0dnZZeSIZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DsNbC/btsMKbGanUG/OSkzjtKWysyL0dnZZeSIZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDsNbC%2FbtsMKbGanUG%2FOSkzjtKWysyL0dnZZeSIZ1%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;1117&quot; height=&quot;471&quot; data-origin-width=&quot;1117&quot; data-origin-height=&quot;471&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;미디어 쿼리&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;css에서는 특정 조건에서만 css style을 적용시킬 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1741849742933&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;&amp;lt;/title&amp;gt;
    &amp;lt;style&amp;gt;
        div {
            border: 10px solid green;
            font-size: 60px;
        }
        @media(max-width:800px) {
            div {
                display: none;
            }
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div&amp;gt;
        Responsive
    &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;위 코드처럼 @media를 사용해 조건을 달아준다. max-width:800px는 최대 너비가 800px라는 것으로 너비가 800px보다 작으면 div 태그가 보이지 않게 해준다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;239&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bm82gL/btsMJxwIhyT/kmWkt7djj6netkWdY59MtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bm82gL/btsMJxwIhyT/kmWkt7djj6netkWdY59MtK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bm82gL/btsMJxwIhyT/kmWkt7djj6netkWdY59MtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbm82gL%2FbtsMJxwIhyT%2FkmWkt7djj6netkWdY59MtK%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;800&quot; height=&quot;239&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;239&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;775&quot; data-origin-height=&quot;207&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Q2f9y/btsMKDJGIz6/kR162Rsruac6VCDxkK3gk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Q2f9y/btsMKDJGIz6/kR162Rsruac6VCDxkK3gk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Q2f9y/btsMKDJGIz6/kR162Rsruac6VCDxkK3gk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQ2f9y%2FbtsMKDJGIz6%2FkR162Rsruac6VCDxkK3gk0%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;775&quot; height=&quot;207&quot; data-origin-width=&quot;775&quot; data-origin-height=&quot;207&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;해당 사진들처럼 너비가 줄어들면 div가 보이지 않게 설정되는 것을 볼 수 있다.&lt;/p&gt;</description>
      <category>공개 글</category>
      <category>CSS</category>
      <category>HTML</category>
      <author>영발개발</author>
      <guid isPermaLink="true">https://zerofootblog.tistory.com/11</guid>
      <comments>https://zerofootblog.tistory.com/11#entry11comment</comments>
      <pubDate>Thu, 13 Mar 2025 16:40:35 +0900</pubDate>
    </item>
  </channel>
</rss>