<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>nov.Zip</title>
    <link>https://novlog.tistory.com/</link>
    <description>https://github.com/novvvv</description>
    <language>ko</language>
    <pubDate>Tue, 7 Jul 2026 20:48:17 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>novs</managingEditor>
    <image>
      <title>nov.Zip</title>
      <url>https://tistory1.daumcdn.net/tistory/4698226/attach/9bdd6977f55f4d3aa0e4398a1804bcee</url>
      <link>https://novlog.tistory.com</link>
    </image>
    <item>
      <title>[Security] Note2. FTP/SFTP Protocol</title>
      <link>https://novlog.tistory.com/entry/Security-Note2-FTPSFTP-Protocol</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;FTP&lt;/span&gt;&lt;/b&gt;는 &lt;span&gt;1971&lt;/span&gt;년 처음 제안된 프로토콜로 데이터 전송 시 암호화를 제공하지 않는 &lt;span&gt;&amp;ldquo;&lt;/span&gt;평문 기반 프로토콜&lt;span&gt;&amp;rdquo;&lt;/span&gt;로 명령어 포트&lt;span&gt; (21&lt;/span&gt;번&lt;span&gt;)&lt;/span&gt;를 통해 전송되는 제어 정보는 모두 암호화되지 않은 텍스트 형태로 오고간다&lt;span&gt;. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Kaist FTP Server, &lt;/span&gt;리눅스 &lt;span&gt;ISO &lt;/span&gt;이미지 파일 배포 등 누구에게나 공개된 오픈소스 소프트웨어 혹은 학술자료나&lt;span&gt;, &lt;/span&gt;암호화 연산 자체가 필요없는 초 경량형 임베디드&lt;span&gt;, IoT&lt;/span&gt;장비와 같은 경우에는 &lt;span&gt;FTP Protocol&lt;/span&gt;을 사용해도 상관없으나&lt;span&gt;, &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;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Cmd&lt;/span&gt;에&lt;span&gt; ftp protocol&lt;/span&gt;을 사용해&lt;span&gt; kaist ftp&lt;/span&gt;에 접속한 뒤&lt;span&gt;, id : anonymous, pw : 1234&lt;/span&gt;를 입력한뒤&lt;span&gt;, &lt;/span&gt;와이어샤크에서 &lt;span&gt;ftp &lt;/span&gt;패킷을 캡처해 보았다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mCWJQ/dJMcadosUdk/mHF2o16NTVKWF5QNXEEvK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mCWJQ/dJMcadosUdk/mHF2o16NTVKWF5QNXEEvK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mCWJQ/dJMcadosUdk/mHF2o16NTVKWF5QNXEEvK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmCWJQ%2FdJMcadosUdk%2FmHF2o16NTVKWF5QNXEEvK0%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;400&quot; height=&quot;95&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;107&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 실험 결과와 같이 사용자가 입력한 &lt;span&gt;ID&lt;/span&gt;와&lt;span&gt; PW&lt;/span&gt;가 그대로 노출되는 것을 확인 가능하다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 동일한 네트워크망(LAN)에 있는 공격자가 스니핑(Sniffing)을 하거나, 라우터를 장악한 경우 해당 정보는 손쉽게 탈취당할 위험이있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;50&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7OzOx/dJMcadWlKtR/OKXlbf5kgYpZyclBwQKFok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7OzOx/dJMcadWlKtR/OKXlbf5kgYpZyclBwQKFok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7OzOx/dJMcadWlKtR/OKXlbf5kgYpZyclBwQKFok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7OzOx%2FdJMcadWlKtR%2FOKXlbf5kgYpZyclBwQKFok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;451&quot; height=&quot;50&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;50&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;보안 대책&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FTP의 보안 취약점을 해결하기 위해서는 데이터 채널을 암호화하는 프로토콜로 전환해야 한다. 대표적으로&lt;span style=&quot;color: #333333;&quot;&gt; &lt;span&gt;FTP&lt;/span&gt;&lt;/span&gt;에&lt;span style=&quot;color: #333333;&quot;&gt; &lt;span&gt;SSH &lt;/span&gt;&lt;/span&gt;암호화 기능을 결합해 모든 데이터 전송을 완전하게 암호화하는&lt;span style=&quot;color: #333333;&quot;&gt; &lt;span&gt;SFTP(Secure File Transfer Protocol)&lt;/span&gt;&lt;/span&gt;가 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기반이되는 SSH(Secure Shell) 프로토콜은 기본적으로 22번 포트를 사용하며 클라이언트 서버간 모든 통신을 암호화하여 안전한 원격 제어를 지원한다. 특히&lt;span style=&quot;color: #333333;&quot;&gt; &lt;span&gt;AWS&lt;/span&gt;&lt;/span&gt;와 같은 클라우드 인프라를 운영하는 경우 보안을 위해 반드시&lt;span style=&quot;color: #333333;&quot;&gt; &lt;span&gt;SSH&lt;/span&gt;&lt;/span&gt;를 통해 서버에 안전하게 접근하거나, AWS 콘솔에서 제공하는 관리 도구를 이용해 직접 제어해야 한다.&lt;/p&gt;</description>
      <category>Security</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/403</guid>
      <comments>https://novlog.tistory.com/entry/Security-Note2-FTPSFTP-Protocol#entry403comment</comments>
      <pubDate>Tue, 19 May 2026 19:06:23 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Note3. @JsonProperty</title>
      <link>https://novlog.tistory.com/entry/Spring-Note3-JsonProperty</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;@JsonProperty는 Entity를 Json형태로 직렬화하는 경우 속성의 이름을 설정할 수 있도록 도와주는 어노테이션이다.&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;예시로 아래와 같은 Board Entity 파일이 백엔드에 정의되어 있다고 가정해 보자.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1772252026554&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Board extends BaseTimeEntity {

    ...
    @Column(name = &quot;post_view&quot;, nullable = false)
    private Integer postView = 0;

    @Column(name = &quot;pinned&quot;, nullable = false)
    private Boolean pinned = false;

    @Column(length = 255)
    private String filename;
    ...
    
}&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;이를 Json으로 직렬화하면 아래와 같이 변환될 것이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1772252069069&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{&quot;post_view&quot;:0, &quot;pinned&quot;: false, &quot;filename&quot;: &quot;&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 만약 프론트단에서 Board 엔터티에 대한 인터페이스를 아래와 같이 정의했다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;views 필드에 대한 필드명 불일치 문제가 발생한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 프로젝트라면 큰 문제가 되지 않겠지만, 어느정도 규모가 있는 프로젝트에서 views와 관련된 모든 코드를 고치는 것은 사이드 이펙트가 굉장히 커진다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1772252186723&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export interface Board {
  ...
  views: number;
  pinned: boolean;
  filename?: string | null;
  ...
}&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;따라서 이 필드와 관련 코드를 모두 수정하는 대신 @JsonProperty 어노테이션으로 필드명 위에 &quot;직렬화 시 속성의 이름을 명시&quot;해주면 별도로 코드를 수정하지 않아도 된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1772252376437&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Board extends BaseTimeEntity {

    ...
    @JsonProperty(&quot;views&quot;) // &amp;lt;-- 
    @Column(name = &quot;post_view&quot;, nullable = false)
    private Integer postView = 0;
    
    @Column(name = &quot;pinned&quot;, nullable = false)
    private Boolean pinned = false;

    @Column(length = 255)
    private String filename;
    ...
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1772252407715&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{&quot;view&quot;:0, &quot;pinned&quot;: false, &quot;filename&quot;: &quot;&quot;} 
// JsonProperty 어노테이션에 따라서 post_view가 아닌 view로 직렬화&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Spring Framework/Spring Framework</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/402</guid>
      <comments>https://novlog.tistory.com/entry/Spring-Note3-JsonProperty#entry402comment</comments>
      <pubDate>Sat, 28 Feb 2026 13:22:44 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Note2. @Configuration @Value</title>
      <link>https://novlog.tistory.com/entry/Spring-Note2-Configuration-Value</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note1. @Configuration &amp;amp; @Value Annotation&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@Configuration&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 클래스가 스프링의 설정 정보를 담고 있음을 나타낸다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 컨테이너는 @Configuration 어노테이션이 붙은 클래스를 스프링빈 정의를 위한 소스로 사용하며, 내부적으로는 &lt;u&gt;&lt;b&gt;CGLIB 라이브러리 내부의 프록시 객체를 생성해&lt;/b&gt;&lt;/u&gt; @Bean이 여러번 호출되더라도 언제든지 동일한 싱글톤 객체를 반환하도록 보장해준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@Value&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 설정 파일 application.propeties, application.yml 과 같이 &lt;u&gt;&lt;b&gt;시스템 환경 변수에 정의된 값을 필드나 파라미터에 주입할 때 사용&lt;/b&gt;&lt;/u&gt;한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;${property.name} 형식으로 사용&lt;/b&gt;&lt;/u&gt;하며 스프링이 관리하는 객체 안에서만 동작한다는 점에 유의하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(new, static 같이 직접 생성한 객체에서는 값이 주입되지 않는다)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note2. @Value는 어떻게 설정값을 읽어오는가&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링은 애플리케이션 실행 시 설정 파일을 찾아 로드한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 부트 내부의 &lt;u&gt;&lt;b&gt;PropertySourceLoader Component&lt;/b&gt;&lt;/u&gt;는 application.yml, application.properties 같은 파일을 읽어 Key-Value 형태로 &lt;u&gt;&lt;b&gt;PropertySource 객체에 담아 &quot;Enviroment Interface&quot;에 저장&lt;/b&gt;&lt;/u&gt;한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Enviroment에는 application.yml, 시스템 환경변수, 명령줄 인수 등 다양한 환경값이 통합되어 관리된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 application.yml은 스프링 빈이 아니더라도 @Value가 값을 읽어올 수 있는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note3. S3 Example Code&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 AWS의 S3서비스를 사용하기 위해 정의한 S3Config 코드이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771468762980&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class S3Config {

    @Value(&quot;${aws.s3.access-key}&quot;)
    private String accessKey;

    @Value(&quot;${aws.s3.secret-key}&quot;)
    private String secretKey;

    @Value(&quot;${aws.s3.region}&quot;)
    private String region;

    @Bean
    public S3Client s3Client() {
        AwsBasicCredentials awsCredentials = AwsBasicCredentials.create(accessKey, secretKey);
        
        return S3Client.builder()
                .region(Region.of(region))
                .credentialsProvider(StaticCredentialsProvider.create(awsCredentials))
                .build();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Framework</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/401</guid>
      <comments>https://novlog.tistory.com/entry/Spring-Note2-Configuration-Value#entry401comment</comments>
      <pubDate>Thu, 19 Feb 2026 11:42:31 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] Note0. S3</title>
      <link>https://novlog.tistory.com/entry/AWS-Note0-S3</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note1. S3 Architecture&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이미지파일 업로드 로직&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 EC2(인스터스/서버)에 이미지 업로드를 API로 요청한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;S3에 이미지를 업로드한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지가 저장된 URL을 리턴한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB에 이미지가 저장된 URL을 저장한다.&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;처음에 이미지파일 업로드 아키텍쳐를 구성할때 이미지 자체를 DB에 저장하는것이 아닌가 많이들 오해하는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 DB에 이미지파일 자체를 저장할 수도 있으나 대체로 DB에는 이미지 파일 주소만을 저장하고 실제 이미지 파일은 S3에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이미지파일 다운로드 로직&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 EC2(인스턴스/서버)에 이미지 조회를 API로 요청한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버는 DB로 조회 SQL문을 날린다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB는 서버에 저장되어 있던 이미지 URL을 응답한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버는 사용자에게 이미지 URL을 응답한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 S3로부터 URL을 통해 이미지를 다운받는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note2. S3 Practice&amp;nbsp;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Note2.1 Bucket 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS S3 서비스로 들어가 Bucket을 생성해준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✨S3 Bucket &amp;amp; Object&lt;b&gt;✨&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Bucket - S3내부의 하나의 저장소 단위&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Object - S3 Bucket에 업로드된 파일&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;296&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AcYYP/dJMcaaRPigP/T8MvZrKwLMlTfol3OtLXH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AcYYP/dJMcaaRPigP/T8MvZrKwLMlTfol3OtLXH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AcYYP/dJMcaaRPigP/T8MvZrKwLMlTfol3OtLXH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAcYYP%2FdJMcaaRPigP%2FT8MvZrKwLMlTfol3OtLXH1%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;760&quot; height=&quot;226&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;296&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;1413&quot; data-origin-height=&quot;461&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vNUfp/dJMb99SSUY8/ArHf5Y5cunNYCCreptpjM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vNUfp/dJMb99SSUY8/ArHf5Y5cunNYCCreptpjM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vNUfp/dJMb99SSUY8/ArHf5Y5cunNYCCreptpjM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvNUfp%2FdJMb99SSUY8%2FArHf5Y5cunNYCCreptpjM1%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;1413&quot; height=&quot;461&quot; data-origin-width=&quot;1413&quot; data-origin-height=&quot;461&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Note2.2 정책 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✨정책(Policy)&lt;b&gt;✨&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS 내부에서 권한(Permission)을 정의하는 JSON 문서이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS에서는 버킷 설정 사진처럼 액세스 차단을 비활성화 하더라도 기본적으로 권한이 없는 상태가 default이며 특정한 리소스에 접근하고자 하면 권한을 별도로 설정해 주어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;444&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dm8K6V/dJMcaadcRt3/s0KoA8w4Pkkq2KhSs8uHV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dm8K6V/dJMcaadcRt3/s0KoA8w4Pkkq2KhSs8uHV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dm8K6V/dJMcaadcRt3/s0KoA8w4Pkkq2KhSs8uHV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdm8K6V%2FdJMcaadcRt3%2Fs0KoA8w4Pkkq2KhSs8uHV1%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;1442&quot; height=&quot;444&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;444&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1056&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UhvoR/dJMcaaK1VWF/gRM0ggkRVOXBZCktFgo7d0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UhvoR/dJMcaaK1VWF/gRM0ggkRVOXBZCktFgo7d0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UhvoR/dJMcaaK1VWF/gRM0ggkRVOXBZCktFgo7d0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUhvoR%2FdJMcaaK1VWF%2FgRM0ggkRVOXBZCktFgo7d0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1056&quot; height=&quot;590&quot; data-origin-width=&quot;1056&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리소스에 접근 가능하도록 하는 GetObject 권한을 추가해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;662&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZifvH/dJMcafFzNsH/zRangkMhHlQreZXS5fd7wK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZifvH/dJMcafFzNsH/zRangkMhHlQreZXS5fd7wK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZifvH/dJMcafFzNsH/zRangkMhHlQreZXS5fd7wK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZifvH%2FdJMcafFzNsH%2FzRangkMhHlQreZXS5fd7wK%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;518&quot; height=&quot;361&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;662&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리소스 ARN의 {BucketName} 부분에 생성한 Bucket 이름을 ObjectName에는 *를 작성한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 생성된 Json의 Principal은 접근 가능한 대상 목록을 작성해야 하는데, 사이트 이미지라면 모든 사람에게 노출되어야 하니 *를 적어준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1330&quot; data-origin-height=&quot;656&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRoQtt/dJMcaaYyzBh/kZ55kKLHylNACDzpMOaUi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRoQtt/dJMcaaYyzBh/kZ55kKLHylNACDzpMOaUi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRoQtt/dJMcaaYyzBh/kZ55kKLHylNACDzpMOaUi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRoQtt%2FdJMcaaYyzBh%2FkZ55kKLHylNACDzpMOaUi0%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;1330&quot; height=&quot;656&quot; data-origin-width=&quot;1330&quot; data-origin-height=&quot;656&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Note2.3 파일 업로드 권한 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드 서버가 S3에 접근해 파일을 업로드할 수 있는 권한을 설정해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IAM 서비스를 사용해 S3 파일 업로드 권한을 설정하자.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1142&quot; data-origin-height=&quot;636&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WOlIv/dJMcajnCkDh/3BlzjsiLnjXTKBai1LSFkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WOlIv/dJMcajnCkDh/3BlzjsiLnjXTKBai1LSFkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WOlIv/dJMcajnCkDh/3BlzjsiLnjXTKBai1LSFkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWOlIv%2FdJMcajnCkDh%2F3BlzjsiLnjXTKBai1LSFkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;743&quot; height=&quot;414&quot; data-origin-width=&quot;1142&quot; data-origin-height=&quot;636&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;616&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y6LkD/dJMcai3jfm0/JbCjYhuQhdN1dgyApNe6Ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y6LkD/dJMcai3jfm0/JbCjYhuQhdN1dgyApNe6Ik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y6LkD/dJMcai3jfm0/JbCjYhuQhdN1dgyApNe6Ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy6LkD%2FdJMcai3jfm0%2FJbCjYhuQhdN1dgyApNe6Ik%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;729&quot; height=&quot;402&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;616&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1122&quot; data-origin-height=&quot;606&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z4KV4/dJMcafFzOcA/QnjdmyIRdddoueJLN54C4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z4KV4/dJMcafFzOcA/QnjdmyIRdddoueJLN54C4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z4KV4/dJMcafFzOcA/QnjdmyIRdddoueJLN54C4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ4KV4%2FdJMcafFzOcA%2FQnjdmyIRdddoueJLN54C4K%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;388&quot; data-origin-width=&quot;1122&quot; data-origin-height=&quot;606&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1082&quot; data-origin-height=&quot;602&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdnp7Q/dJMcahQVgFk/ieAm2KqPRIhilzbzZxKk21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdnp7Q/dJMcahQVgFk/ieAm2KqPRIhilzbzZxKk21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdnp7Q/dJMcahQVgFk/ieAm2KqPRIhilzbzZxKk21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbdnp7Q%2FdJMcahQVgFk%2FieAm2KqPRIhilzbzZxKk21%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;1082&quot; height=&quot;602&quot; data-origin-width=&quot;1082&quot; data-origin-height=&quot;602&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액세스키는 이후에 서버 환경설정 파일에 세팅해 주어야 하기에 따로 다운하거나 복사해두자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1764&quot; data-origin-height=&quot;978&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZWE1f/dJMcahQVgGE/EGHpLgSvHo3BQgsftwnfv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZWE1f/dJMcahQVgGE/EGHpLgSvHo3BQgsftwnfv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZWE1f/dJMcahQVgGE/EGHpLgSvHo3BQgsftwnfv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZWE1f%2FdJMcahQVgGE%2FEGHpLgSvHo3BQgsftwnfv1%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;1764&quot; height=&quot;978&quot; data-origin-width=&quot;1764&quot; data-origin-height=&quot;978&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note3. AWS S3 SpringBoot Setting&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LightSale Instance 내부에 Mysql, SpringBoot 인스턴스를 Docker로 가상화하여 올려둔 상태이며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별도의 RDS같은 외부 데이터베이스 서비스는 사용하지 않는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;pom.xml dependency 추가&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771396060196&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        &amp;lt;!-- AWS SDK S3 --&amp;gt;
        &amp;lt;!-- AWS SDK for Java 2.x (Spring Boot 3.x 호환) --&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;software.amazon.awssdk&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;s3&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;2.20.0&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;.env&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771396453052&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# AWS S3 설정
AWS_S3_BUCKET=
AWS_REGION=
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Docker AWS S3 Setting&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771396419059&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    environment:
    ...
      # AWS S3 설정
      - AWS_S3_BUCKET=${AWS_S3_BUCKET}
      - AWS_REGION=${AWS_REGION}
      - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
      - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;application.yml&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771395642782&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# AWS 설정 
aws:
  s3:
    bucket: ${AWS_S3_BUCKET}
    region: ${AWS_REGION}
    access-key: ${AWS_ACCESS_KEY_ID}
    secret-key: ${AWS_SECRET_ACCESS_KEY}

    # 파일 저장 경로 
    profile-image-path: profiles/ # 프로필 이미지 저장
    board-image-path: boards/ # 게시판 이미지
    board-file-path: boards/files # 게시판 첨부파일

    # 파일 크기 제한 
    max-file-size: 10485760 # 10mb
    # 허용 이미지 타입
    allowed-image-types: jpg,jpeg,png,gif,webp
    # 허용된 파일 타입 
    allowed-file-types: pdf,doc,docx,xls,xlsx,zip&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;application.yml multipart setting&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SpringBoot의 기본 파일 설정은 1MB이기에 Nginx와 최대 파일 업로드 제한 크기를 맞춰주는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx.conf 환경설정 파일에서 파일 업로드 제한을 10Mb로 제한하였기에 똑같이 10Mb로 설정했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 다만 multipart는 전역 설정만 가능하기에 파일 타입에 따라 용량 제한을 커스텀하고 싶다면 애플리케이션 코드 레벨에서 검증을 진행해야 한다. *&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771396820921&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  # 파일 업로드 설정 (Multipart)
  servlet:
    multipart:
      max-file-size: 10MB        # 파일당 최대 크기 (nginx의 client_max_body_size와 일치)
      max-request-size: 10MB     # 요청 전체 최대 크기
      enabled: true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Cloud/AWS</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/400</guid>
      <comments>https://novlog.tistory.com/entry/AWS-Note0-S3#entry400comment</comments>
      <pubDate>Thu, 19 Feb 2026 11:09:58 +0900</pubDate>
    </item>
    <item>
      <title>Spring Security Note0. UserDetails</title>
      <link>https://novlog.tistory.com/entry/Spring-Security-Note0-UserDetails</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Note1. UserDetails&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Security에서 사용자 정보를 담기 위해 정의하는 인터페이스이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Domain Entity에 직접 구현하는 방식도 가능하지만 이는 엔터티가 Spring Security에 의존하게 된다는 단점이 있어 보통 CustomUserDetails, UserDetailsImpl과 같은 네이밍으로 커스텀 래퍼 클래스를 사용해 정의한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lombok @RequiredArgsConsructor 어노테이션을 사용하면 생성자를 생략해줘도 상관없고, 사용하지 않는다면 User 객체를 주입받기 위한 별도의 생성자를 정의해 주어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771335590082&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class CustomUserDetails implements UserDetails {
	...
    private final User user; // Domain Entity
    // Constructor
    public CustomUserDetails(User user) {
        this.user = user;
    }
 	...   
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Note2. UserDetails Field&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;getAuthroties()&lt;/b&gt; - 사용자 권한 목록을 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;getPassword()&lt;/b&gt; - 사용자 비밀번호를 반환한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;getUsername()&lt;/b&gt; - 사용자명을 반환한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;isAccountNonExpired()&lt;/b&gt; - 계정 만료 기능을 활성화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;isAccountNonLocked()&lt;/b&gt; - 계정 잠금 기능을 활성화한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;isCredentialsNonExpired()&lt;/b&gt; - 비밀번호 만료 기능을 활성화한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771336290728&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * UserDetails 구현 클래스
 * Spring Security가 사용하는 사용자 인증 정보를 제공
 */
public class CustomUserDetails implements UserDetails {

    private final User user;

    // Constructor
    public CustomUserDetails(User user) {
        this.user = user;
    }

    // getAuthorities - 사용자 권한 목록을 반환한다.
    @Override
    public Collection&amp;lt;? extends GrantedAuthority&amp;gt; getAuthorities() {
        return user.getAuthorities().stream()
                .map(authority -&amp;gt; new SimpleGrantedAuthority(authority.getAuthorityName()))
                .collect(Collectors.toList());
    }

    // getPassword - 사용자 비밀번호를 반환한다.
    @Override
    public String getPassword() {
        return user.getPassword();
    }

    // getUsername - 사용자명을 반환한다.
    @Override
    public String getUsername() {
        return user.getUserId();
    }

    // isAccountNonExpired - 계정 만료 기능 (Default : true)
    @Override
    public boolean isAccountNonExpired() {
        return true; // 현재는 계정 만료 기능 미사용
    }

    // isAccountNonLocked - 계정 잠금 기능
    @Override
    public boolean isAccountNonLocked() {
        return true; // 현재는 계정 잠금 기능 미사용
    }

    // isCredentialsNonExpired - 비밀번호 만료 기능
    @Override
    public boolean isCredentialsNonExpired() {
        return true; // 현재는 자격 증명 만료 기능 미사용
    }

    // isEnabled - 계정 활성화 기능
    @Override
    public boolean isEnabled() {
        return true; // 현재는 계정 비활성화 기능 미사용
    }

    // Domain Entity 반환용 Getter
    public User getUser() {
        return user;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Note3. @AuthenticationPrincipal&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;@AuthenticationPrincipal 어노테이션&lt;/b&gt;&lt;/u&gt;을 사용하면 별도의 CustomUserDetails 객체 생성 없이 곧바로 주입받아 사용이 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771336412761&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    // Note. MyPageAPI - 현재 로그인한 사용자 정보를 JWT에서 추출한다.
    @GetMapping(&quot;/me&quot;)
    public ResponseEntity&amp;lt;?&amp;gt; getCurrentUser(@AuthenticationPrincipal CustomUserDetails customUserDetails) {
        try {

            // 인증되지 않은 경우
            if (customUserDetails == null) {
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                        .body(Map.of(&quot;message&quot;, &quot;인증이 필요합니다.&quot;));
            }

            // CustomUserDetails에서 User 엔터티 추출
            User user = customUserDetails.getUser();

            // DTO로 변환 (민감 정보 제외)
            MyPageResponseDTO response = MyPageResponseDTO.builder()
                    .id(user.getId())
                    .userId(user.getUserId())
                    .name(user.getName())
                    .nickname(user.getNickname())
                    .email(user.getEmail())
                    .universityName(user.getUniversityName())
                    .major(user.getMajor())
                    .grade(user.getGrade())
                    .profileImage(user.getProfileImage())
                    .createdAt(user.getCreatedAt())
                    .updatedAt(user.getUpdatedAt())
                    .build();

            return ResponseEntity.ok(response);

        }

        catch (Exception e) {
            Map&amp;lt;String, String&amp;gt; error = new HashMap&amp;lt;&amp;gt;();
            error.put(&quot;message&quot;, &quot;사용자 정보 조회 실패: &quot; + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
        }
    }&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Spring Framework/SpringSecurity</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/399</guid>
      <comments>https://novlog.tistory.com/entry/Spring-Security-Note0-UserDetails#entry399comment</comments>
      <pubDate>Tue, 17 Feb 2026 23:04:40 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Note1. Flyway Setting</title>
      <link>https://novlog.tistory.com/entry/Spring-Note1-Flyway-Setting</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note1. Flyway란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 마이그레이션 툴로 Git의 데이터베이스 버전이라고 이해하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 Flyway를 도입하기 이전에는 데이터베이스 스키마 구조가 변경될 때마다 매번 FE측에 Sql 스크립트를 보내줘야만 했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇보다 실제 프로덕션 환경이라면 서버에 직접 들어가 데이터베이스의 구조를 변경하는것은 위험하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이번 프로젝트에서는 빈번하게 데이터베이스가 변경되는 환경이라 매번 팀원들에게 데이터베이스를 수정하라고 공지하는 것도 번거로워서 Springboot 프로젝트에 Flyway를 도입을 결정했다.&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;비슷한 db 형상관리 툴로 Liquibase가 존재하는듯 한데, 롤백, 스냅샷등의 기능이 필요하거나 자세한 커스텀이 필요하다면 Liquibase를 사용하는 편이 좋을 듯싶다. Liquibase와 Flyway의 차이점에 대해서는 아래 블로그에 자세하게 정리되어 있으니 참고하도록 하자.&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1771062043890&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;데이터 베이스 형상관리(Migration)툴 비교 Flyway vs Liquibase&quot; data-og-description=&quot;데이터 베이스 형상관리는 테이블 구조, 스키마, 뷰, 저장 프로시저, 데이터 자체의 변경에 대한 형상을 관리하는 것입니다. 혼자 개발 할 때는 상관이 없지만 여러명이 같이 개발을 하게 된다면&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@kjy0302014/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4-%ED%98%95%EC%83%81%EA%B4%80%EB%A6%ACMigration%ED%88%B4-%EB%B9%84%EA%B5%90&quot; data-og-url=&quot;https://velog.io/@kjy0302014/데이터-베이스-형상관리Migration툴-비교&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/02G59/dJMb9frBGRR/Vfti4Wx1ur9Fg15RKlXkEK/img.jpg?width=1280&amp;amp;height=853&amp;amp;face=0_0_1280_853,https://scrap.kakaocdn.net/dn/R4MXZ/dJMb87NSAKW/cGSJgVrITAIeP5VzoTCn60/img.jpg?width=1280&amp;amp;height=853&amp;amp;face=0_0_1280_853,https://scrap.kakaocdn.net/dn/DbEDT/dJMb88646On/SS5kSGa9KOrB3dup8ppMcK/img.jpg?width=1280&amp;amp;height=853&amp;amp;face=0_0_1280_853&quot;&gt;&lt;a href=&quot;https://velog.io/@kjy0302014/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4-%ED%98%95%EC%83%81%EA%B4%80%EB%A6%ACMigration%ED%88%B4-%EB%B9%84%EA%B5%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@kjy0302014/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4-%ED%98%95%EC%83%81%EA%B4%80%EB%A6%ACMigration%ED%88%B4-%EB%B9%84%EA%B5%90&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/02G59/dJMb9frBGRR/Vfti4Wx1ur9Fg15RKlXkEK/img.jpg?width=1280&amp;amp;height=853&amp;amp;face=0_0_1280_853,https://scrap.kakaocdn.net/dn/R4MXZ/dJMb87NSAKW/cGSJgVrITAIeP5VzoTCn60/img.jpg?width=1280&amp;amp;height=853&amp;amp;face=0_0_1280_853,https://scrap.kakaocdn.net/dn/DbEDT/dJMb88646On/SS5kSGa9KOrB3dup8ppMcK/img.jpg?width=1280&amp;amp;height=853&amp;amp;face=0_0_1280_853');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;데이터 베이스 형상관리(Migration)툴 비교 Flyway vs Liquibase&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;데이터 베이스 형상관리는 테이블 구조, 스키마, 뷰, 저장 프로시저, 데이터 자체의 변경에 대한 형상을 관리하는 것입니다. 혼자 개발 할 때는 상관이 없지만 여러명이 같이 개발을 하게 된다면&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note2. Setup&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Spring Boot pom.xml Dependency&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771077154928&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        &amp;lt;!-- Flyway Database Migration --&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.flywaydb&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;flyway-core&amp;lt;/artifactId&amp;gt;
        &amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;JPA Hibernate ddl-auto option change&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ddl-auto option을 update로 설정해두면 엔터티 클래스와 매핑되는 테이블이 없으면 자동으로 생성하고 변경사항을 적용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 update 옵션은 한계가 있기에 실제 프로덕션 환경에서는 잘 사용하지 않는데 그 이유는 기존에 존재하는 컬럼 속성은 변경하지 않고 &quot;새롭게 추가되는 컬럼의 변경사항만 반영한다&quot;는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 nullable, datatype 등의 변경사항은 반영하지 못한다. 어찌보면 당연하게 만약 varchar(20)을 varchar(10)으로 변경했다고 가정해 보자. 그러면 10크기만큼의 데이터가 잘려간다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;u&gt;&lt;b&gt;flyway를 도입하는 환경에서는 충돌을 막기 위해 ddl-auto option을 none&lt;/b&gt;&lt;/u&gt;으로 두도록 하자&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771116231697&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  jpa:
    hibernate:
      ddl-auto: none&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Migration Script&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sql 스크립트 명명규칙은 고정으로 &lt;u&gt;&lt;b&gt;V{Version}__{description}.sql&lt;/b&gt;&lt;/u&gt; 형태이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버전과 설명 사이에 언더스코어가 2개임에 유의하고 이는 기본값으로 application.yml에서 원하는 대로 커스텀이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 명명규칙을 제대로 적어주지 않으면 flyway가 스크립트를 인식하지 못하니 조심하자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;226&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cx4pxb/dJMcagRZY5X/8uqykcXQsUbArwUuokAkxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cx4pxb/dJMcagRZY5X/8uqykcXQsUbArwUuokAkxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cx4pxb/dJMcagRZY5X/8uqykcXQsUbArwUuokAkxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcx4pxb%2FdJMcagRZY5X%2F8uqykcXQsUbArwUuokAkxK%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;375&quot; height=&quot;126&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;226&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1771116740656&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Flyway 설정
flyway:
  enabled: true
  locations: classpath:db/migration
  baseline-on-migrate: true  # 기존 DB가 있을 경우 baseline 설정
  validate-on-migrate: true
  clean-disabled: true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Framework</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/398</guid>
      <comments>https://novlog.tistory.com/entry/Spring-Note1-Flyway-Setting#entry398comment</comments>
      <pubDate>Sun, 15 Feb 2026 13:05:02 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Note0. ResponseEntity</title>
      <link>https://novlog.tistory.com/entry/Spring-Note0-ResponseEntity</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note1. ResponseEntity&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ResponseEntity란 Spring Framework에서 Http 요청에 대한 응답을 제어하기 위해 제공하는 클래스이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ReponseEntity에는 Http Protocol 표준에 따라 Reponse Body, Header, Status Code 등 다양한 응답 정보를 포함할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771053722570&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ResponseEntity&amp;lt;T&amp;gt; extends HttpEntity&amp;lt;T&amp;gt; {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note2. ResponseEntity 동작 &amp;amp; @RestController&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring MVC는 기본적으로 Controller의 Return Type을 처리할 때, ResponseEntity 객체가 반환되면 아래와 같이 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;1. ViewResolver 생략&lt;/b&gt;&lt;/u&gt; - @Controller의 경우 리턴값으로 문자열이 들어오면 HTML 파일을 탐색하지만 ResponseEntity는 이 탐색 과정을 생략한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;2. HttpMessageConverter 사용&lt;/b&gt;&lt;/u&gt; - ResponseEntity 안에 담긴 객체를 JSON 혹은 XML 형태로 반환하기 위한 컨버터를 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ResponseEntity를 사용할 때 반드시 @RestController 어노테이션과 함께 사용해야 하는 것은 아니지만 주로 @RestController를 사용해 모든 메서드는 데이터를 반환한다는 것을 명시하는 것이 가독성 측면에서 효율적이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note3. ResponseEntity.status()&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.status() 메서드를 사용해 특정 HTTP Status Code를 직접 설정 가능하며 아래와 같은 상태 상수를 제공한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HttpStatus.OK (200) - 성공&amp;nbsp;&lt;br /&gt;HttpStatus.CREATED (201) - 생성 성공&lt;br /&gt;HttpStatus.BAD_REQUEST (400) - 잘못된 요청&amp;nbsp;&lt;br /&gt;HttpStatus.UNAUTHORIZED (401) - 인증 필요&lt;br /&gt;HttpStatus.FORBIDDEN (403) - 권한 없음&lt;br /&gt;HttpStatus.NOT_FOUND (404) - 리소스 없음&amp;nbsp;&lt;br /&gt;HttpStatus.INTERNAL_SERVER_ERROR (500) - 서버 오류&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note4. Login Example&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 로직에 따른 적절한 상태코드를 부여한뒤 에러를 핸들링한 로그인 예제이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771053908139&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestController
@RequestMapping(&quot;/api/auth&quot;)
@CrossOrigin(origins = &quot;*&quot;)
public class AuthController {

    @Autowired
    private AuthService authService;

    @PostMapping(&quot;/login&quot;)
    public ResponseEntity&amp;lt;?&amp;gt; login(@Valid @RequestBody LoginDTO loginRequest) {
        try {
            // 사용자 인증
            Optional&amp;lt;User&amp;gt; userOpt = authService.validateUser(
                    loginRequest.getUserId(),
                    loginRequest.getPassword());

            if (userOpt.isPresent()) {
                // 로그인 성공
                LoginResponseDTO response = authService.login(userOpt.get());
                return ResponseEntity.ok(response);
            }

            else {
                // 인증 실패 (비밀번호 오류 또는 사용자 없음)
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                        .body(Map.of(&quot;message&quot;, &quot;Invalid user ID or password&quot;));
            }
        }

        catch (UserNotApprovedException e) {
            // 승인되지 않은 계정
            return ResponseEntity.status(HttpStatus.FORBIDDEN)
                    .body(Map.of(&quot;message&quot;, e.getMessage()));
        }

        catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(Map.of(&quot;message&quot;, &quot;Login failed: &quot; + e.getMessage()));
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Framework</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/397</guid>
      <comments>https://novlog.tistory.com/entry/Spring-Note0-ResponseEntity#entry397comment</comments>
      <pubDate>Sat, 14 Feb 2026 16:28:28 +0900</pubDate>
    </item>
    <item>
      <title>[Security] Note1. Nginx Version 숨기기</title>
      <link>https://novlog.tistory.com/entry/Security-Note1-Nginx-Version-%EC%88%A8%EA%B8%B0%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Nginx&lt;/h2&gt;
&lt;pre id=&quot;code_1771035461923&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl -I https://...&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;현재 운영중인 사이트로 요청을 보내보니 아래와 같이 http 응답 헤더에 버전 정보를 자동으로 노출한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 버전 노출이 일반적이었으나 최근에는 nginx 버전을 숨기는 것을 권장한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771035572085&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;HTTP/2 403 
server: nginx/1.29.5 &amp;lt;-------- 보안 문제점
date: Sat, 14 Feb 2026 02:17:06 GMT
content-length: 0
vary: Origin
...&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;방법은 간단한데 nginx.conf 설정 파일에 server_tokens 값을 off로 설정해주면 된다. (default: on)&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;49&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lhNwY/dJMcaaRNd3j/IxRylvkiRTRU5HiqHKrQnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lhNwY/dJMcaaRNd3j/IxRylvkiRTRU5HiqHKrQnK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lhNwY/dJMcaaRNd3j/IxRylvkiRTRU5HiqHKrQnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlhNwY%2FdJMcaaRNd3j%2FIxRylvkiRTRU5HiqHKrQnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;350&quot; height=&quot;49&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;49&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;585&quot; data-origin-height=&quot;58&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NVJdl/dJMcagxEXHj/KSwi8pKQhLzFcuOPRfrJF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NVJdl/dJMcagxEXHj/KSwi8pKQhLzFcuOPRfrJF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NVJdl/dJMcagxEXHj/KSwi8pKQhLzFcuOPRfrJF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNVJdl%2FdJMcagxEXHj%2FKSwi8pKQhLzFcuOPRfrJF0%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;585&quot; height=&quot;58&quot; data-origin-width=&quot;585&quot; data-origin-height=&quot;58&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1771038714866&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;HTTP/2 403 
server: nginx &amp;lt;----- 해결
date: Sat, 14 Feb 2026 03:12:31 GMT
content-length: 0
vary: Origin&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Security</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/396</guid>
      <comments>https://novlog.tistory.com/entry/Security-Note1-Nginx-Version-%EC%88%A8%EA%B8%B0%EA%B8%B0#entry396comment</comments>
      <pubDate>Sat, 14 Feb 2026 12:25:14 +0900</pubDate>
    </item>
    <item>
      <title>[JPA] Note2. @BaseTimeEntity Refactoring</title>
      <link>https://novlog.tistory.com/entry/Note2-BaseTimeEntity-Refactoring</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note1. @BaseTimeEntity&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 Entity에는 created_at, updated_at column이 포함되어 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 생성/수정일자를 관리하는 방식은 여러가지로 개인 프로젝트에서도 아래와 같이 User Entity는 JPA Auditing 기능을 사용하고 Problem Entity에는 생성자에서 생성/수정 일자를 초기화하는 등 여러 방식이 혼합되어 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이는 &lt;u&gt;&lt;b&gt;코드 일관성과 유지보수 측면에서 좋지 않은 코드이고 이러한 생성/수정 일자를 한 곳에서 일괄적으로 관리하기 위해 JPA는 BaseTimeEntity라는 어노테이션을 제공&lt;/b&gt;&lt;/u&gt;한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;User Entity&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771033560477&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @CreatedDate
    @Column(name = &quot;created_at&quot;, updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    @Column(name = &quot;updated_at&quot;)
    private LocalDateTime updatedAt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Problem&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771033594087&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @Column(name = &quot;created_at&quot;)
    private LocalDateTime createdAt;

    @Column(name = &quot;updated_at&quot;)
    private LocalDateTime updatedAt;

    // 기본 생성자
    public Problem() {
        LocalDateTime now = LocalDateTime.now();
        this.createdAt = now;
        this.updatedAt = now;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Note2. Code&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역에서 사용할 BaseTimeEntity를 선언한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;@MappedSuperclass 어노테이션&lt;/b&gt;&lt;/u&gt;으로 엔터티가 아닌 상속용 클래스임을 명시하고, &lt;u&gt;&lt;b&gt;JPA Auditing을 사용해 자동으로 시간을 관리&lt;/b&gt;&lt;/u&gt;하도록 해주었다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771034089844&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * 모든 엔티티의 공통 시간 필드를 관리하는 Base 클래스
 * - @MappedSuperclass: 엔티티가 아닌 상속용 클래스
 * - JPA Auditing을 사용하여 자동으로 시간 관리
 */
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@Getter
public abstract class BaseTimeEntity {

    @CreatedDate
    @Column(name = &quot;created_at&quot;, nullable = false, updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    @Column(name = &quot;updated_at&quot;, nullable = false)
    private LocalDateTime updatedAt;
}&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;created_at, updated_at column을 사용할 엔터티는 별도의 헬퍼 메서드나, 생성자를 선언할 필요 없이 BaseTimeEntity를 상속받기만 하면 자동으로 컬럼이 생성되어 관리된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771034404839&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class User extends BaseTimeEntity {
...
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Spring Framework/JPA</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/395</guid>
      <comments>https://novlog.tistory.com/entry/Note2-BaseTimeEntity-Refactoring#entry395comment</comments>
      <pubDate>Sat, 14 Feb 2026 11:02:40 +0900</pubDate>
    </item>
    <item>
      <title>[JPA] Note1 @EnableJpaAduting @EntityListeners</title>
      <link>https://novlog.tistory.com/entry/JPA-Note1-EnableJpaAduting-EntityListeners</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note1. JPA Auditing&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JPA (Java Persistence API)에서 제공하는 Auditing 기술로 엔터티의 생성/수정 시점을 자동으로 추적&amp;nbsp;및 기록해 준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JPA Auditing 기능을 사용하면 getter/setter로 매번 직접 시간을 설정하지 않아도 되고 반복되지 않은 간결한 코드를 작성 가능하다는 장점이 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note2. @EnableJpaAduiting&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 애플리케이션 전체에서 JPA Auditing 기능을 활성화한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 주로 메인 클래스에 선언하며, 한 번 선언하면 Spring Framework가 자동으로 관련 빈을 등록한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note3. @EntityListeners(AuditingEntityListener.class)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 엔터티 레벨에서 JPA Auditing 기능을 활성화해 리스너를 등록해준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JPA Auditing을 활성해주는 어노테이션으로 주로 &lt;u&gt;&lt;b&gt;@CreatedDate, @LastModifiedDate 어노테이션과 같이 사용&lt;/b&gt;&lt;/u&gt;하여 댓글의 생성/수정 일시나 사용자의 가입/갱신 일자 등을 자동으로 기록해 준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note4. Example&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Main Entry Point에 &lt;u&gt;&lt;b&gt;@EnableJpaAduiting&lt;/b&gt;&lt;/u&gt;를 명시한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1770961885504&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@EnableJpaAuditing
public class NimdaConApplication {
    public static void main(String[] args) {
        SpringApplication.run(NimdaConApplication.class, args);
    }
}&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;추적하고자 하는 엔터티에 EntityListeners를 붙여준 뒤, 생성/수정일자 컬럼에 &lt;u&gt;&lt;b&gt;@CreatedDate, @LastModifiedDate&lt;/b&gt; &lt;/u&gt;어노테이션을 추가한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1770961948271&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@EntityListeners(AuditingEntityListener.class)
public class User {
 ...
     @CreatedDate
    @Column(name = &quot;created_at&quot;, updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    @Column(name = &quot;updated_at&quot;)
    private LocalDateTime updatedAt;
 ...
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Spring Framework/JPA</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/394</guid>
      <comments>https://novlog.tistory.com/entry/JPA-Note1-EnableJpaAduting-EntityListeners#entry394comment</comments>
      <pubDate>Fri, 13 Feb 2026 14:55:44 +0900</pubDate>
    </item>
    <item>
      <title>[React] Note2. Protected Route</title>
      <link>https://novlog.tistory.com/entry/React-Note2-Protected-Route</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note1. Proetected Route&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Protected Route는 React Router에서 특정 경로 접근 전에 인증/권한을 확인하는 컴포넌트이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 중요한 자원 (관리자 대시보드 등) 의 UI 노출을 방지할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;u&gt;&lt;b&gt;개발자 도구로 우회가 가능하기에 완전한 보안은 불가능하고 서버단에서 API 접근에 대한 보안처리는 반드시 따로 해줘야만 한다.&amp;nbsp;&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 CSR (Client-Side-Rendering)의 한계로 Spring MVC나 Next.js같은 SSR(Server-Side-Rendering) 방식을 사용하는 프레임워크는 Nginx 같은 프록시에서 html 자원 호출 자체를 서버단에서 제한할 수 있으나, CSR은 불가능하기 때문이다.&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;예시로 사용자가 .../admin 페이지로 접근을 시도하면 Protected Route가 권한을 확인하고, 권한이 있으면 페이지를 표시 권한이 없으면 설정한 페이지로 리다이렉트한다.&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;Protected Route에서 사용하기 위해 utils/jwt.js에 JWT 어드민 권한 체크 함수를 정의했다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;311&quot; data-origin-height=&quot;44&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8dbYX/dJMcachKmzd/XFIGRPD15mr1Oa7dkD04D0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8dbYX/dJMcachKmzd/XFIGRPD15mr1Oa7dkD04D0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8dbYX/dJMcachKmzd/XFIGRPD15mr1Oa7dkD04D0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8dbYX%2FdJMcachKmzd%2FXFIGRPD15mr1Oa7dkD04D0%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;311&quot; height=&quot;44&quot; data-origin-width=&quot;311&quot; data-origin-height=&quot;44&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1770948354027&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const isAdmin = (): boolean =&amp;gt; {

  const token = localStorage.getItem('authToken');
  if (!token) { return false; }

  try {
    const base64Url = token.split('.')[1];
    const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
    const jsonPayload = decodeURIComponent(
      atob(base64)
        .split('')
        .map(function (c) {
          return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
        })
        .join('')
    );
    const payload = JSON.parse(jsonPayload);
    const authorities = payload.authorities || [];
    return Array.isArray(authorities) &amp;amp;&amp;amp; authorities.includes('ROLE_ADMIN');
  } catch (error) {
    return false;
  }

};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note2. 403 Forbidden Page&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;310&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bC1X58/dJMcaibcGqE/O2khEpD3j6qnDW4bvR1Pi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bC1X58/dJMcaibcGqE/O2khEpD3j6qnDW4bvR1Pi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bC1X58/dJMcaibcGqE/O2khEpD3j6qnDW4bvR1Pi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbC1X58%2FdJMcaibcGqE%2FO2khEpD3j6qnDW4bvR1Pi1%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;310&quot; height=&quot;73&quot; data-origin-width=&quot;310&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 권한 없이 관리자 페이지에 접근하면 리다이렉트할 403 페이지를 간단하게 정의한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1770949081069&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useNavigate } from 'react-router-dom';

const ForbiddenPage = () =&amp;gt; {
    const navigate = useNavigate();

    return (
        &amp;lt;div className=&quot;min-h-screen bg-white flex items-center justify-center&quot;&amp;gt;
            &amp;lt;div className=&quot;text-center&quot;&amp;gt;
                &amp;lt;h1 className=&quot;text-6xl font-bold text-gray-900 mb-4&quot;&amp;gt;403&amp;lt;/h1&amp;gt;
                &amp;lt;h2 className=&quot;text-2xl font-medium text-gray-700 mb-2&quot;&amp;gt;접근 권한이 없습니다&amp;lt;/h2&amp;gt;
                &amp;lt;p className=&quot;text-gray-500 mb-8&quot;&amp;gt;이 페이지에 접근할 수 있는 권한이 없습니다.&amp;lt;/p&amp;gt;
                &amp;lt;div className=&quot;space-x-4&quot;&amp;gt;
                    &amp;lt;button
                        onClick={() =&amp;gt; navigate(-1)}
                        className=&quot;px-6 py-2 bg-gray-200 text-gray-700 rounded hover:bg-gray-300 transition&quot;
                    &amp;gt;
                        이전 페이지
                    &amp;lt;/button&amp;gt;
                    &amp;lt;button
                        onClick={() =&amp;gt; navigate('/')}
                        className=&quot;px-6 py-2 bg-gray-900 text-white rounded hover:bg-gray-800 transition&quot;
                    &amp;gt;
                        홈으로
                    &amp;lt;/button&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    );
};

export default ForbiddenPage;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Note3. ProtectedRotue&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;143&quot; data-origin-height=&quot;46&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zq5Uj/dJMcahcgzI3/7SGiPcIbVtSMs7m6Tox3cK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zq5Uj/dJMcahcgzI3/7SGiPcIbVtSMs7m6Tox3cK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zq5Uj/dJMcahcgzI3/7SGiPcIbVtSMs7m6Tox3cK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZq5Uj%2FdJMcahcgzI3%2F7SGiPcIbVtSMs7m6Tox3cK%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;143&quot; height=&quot;46&quot; data-origin-width=&quot;143&quot; data-origin-height=&quot;46&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인하지 않은 경우에는 로그인 페이지로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 했지만 권한이 없는 경우에는 방금 정의한 403 페이지로 리다이렉트 시킨다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1770949155824&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Navigate } from 'react-router-dom';
import { isAdmin } from '@/utils/jwt';

interface ProtectedRouteProps {
    children: React.ReactNode;
    requireAdmin?: boolean;
}

const ProtectedRoute = ({ children, requireAdmin = false }: ProtectedRouteProps) =&amp;gt; {
    if (requireAdmin) {
        const token = localStorage.getItem('authToken');

        // 로그인하지 않은 경우 &amp;rarr; 로그인 페이지로 리다이렉트
        if (!token) {
            return &amp;lt;Navigate to=&quot;/login&quot; replace /&amp;gt;;
        }

        // 로그인했지만 관리자 권한이 없는 경우 &amp;rarr; 403 페이지로 리다이렉트
        if (!isAdmin()) {
            return &amp;lt;Navigate to=&quot;/403&quot; replace /&amp;gt;;
        }
    }

    return &amp;lt;&amp;gt;{children}&amp;lt;/&amp;gt;;
};

export default ProtectedRoute;&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;마지막으로 Admin Dashboard로의 접근을 ProtectedRoute로 막아주었다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1770949228863&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        &amp;lt;Route
          path=&quot;/admin&quot;
          element={
            &amp;lt;ProtectedRoute requireAdmin={true}&amp;gt;
              &amp;lt;AdminDashboard /&amp;gt;
            &amp;lt;/ProtectedRoute&amp;gt;
          }
        /&amp;gt;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>dev/React</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/393</guid>
      <comments>https://novlog.tistory.com/entry/React-Note2-Protected-Route#entry393comment</comments>
      <pubDate>Fri, 13 Feb 2026 11:23:08 +0900</pubDate>
    </item>
    <item>
      <title>[Git] Your local changes to the following files would be overwritten by chekcout</title>
      <link>https://novlog.tistory.com/entry/Git-Your-local-changes-to-the-following-files-would-be-overwritten-by-chekcout</link>
      <description>&lt;pre id=&quot;code_1770945383041&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;error: Your local changes to the following files would be overwritten by checkout:
	NimdaLandingPage/package-lock.json
Please commit your changes or stash them before you switch branches.
Aborting&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;현재 브랜치의 파일과 이동하고자 하는 브랜치의 파일이 일치하지 않을 때 발생하는 오류이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해결 방안은 현재 브랜치의 변경 사항을 커밋하거나 stash 명령어를 사용해 현재 브랜치의 변경사항을 임시로 저장한 뒤 이동할 브랜치에서 작업을 마치고 다시 되돌아와서 임시로 저장한 내용을 제거하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1770945526792&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git stash&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;574&quot; data-origin-height=&quot;106&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q7FoA/dJMcachKlfi/AylcsKQnK9k4mP7iR862UK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q7FoA/dJMcachKlfi/AylcsKQnK9k4mP7iR862UK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q7FoA/dJMcachKlfi/AylcsKQnK9k4mP7iR862UK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq7FoA%2FdJMcachKlfi%2FAylcsKQnK9k4mP7iR862UK%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;314&quot; height=&quot;58&quot; data-origin-width=&quot;574&quot; data-origin-height=&quot;106&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;git stash list로 stash된 파일 이력을 출력한 뒤&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1770945878135&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git stash list&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1770945902547&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;stash@{0}
stash@{1}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;git stash pop으로 stash 목록을 제거해주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1770945968253&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git stash pop&lt;/code&gt;&lt;/pre&gt;</description>
      <category>dev/Git</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/392</guid>
      <comments>https://novlog.tistory.com/entry/Git-Your-local-changes-to-the-following-files-would-be-overwritten-by-chekcout#entry392comment</comments>
      <pubDate>Fri, 13 Feb 2026 10:28:53 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] Note1. docker exec</title>
      <link>https://novlog.tistory.com/entry/Docker-Note1-docker-exec</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Note1. docker exec vs docker run&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;docker exec&amp;nbsp;&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이미 실행 중인 컨테이너에 명령을 실행한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 컨테이너를 새로 생성하지 않으며 기존 컨테이너 내부에서 명령을 실행한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;docker exec -it &amp;lt;container_name&amp;gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;option&quot;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-i (interactive) 표준 입력(STDIN)을 열어두어 키보드 입력을 받을 수 있도록 허용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-t (tty/terminal) 가상 터미널을 할당하여 터미널처럼 보이게 한다. 프롬프트 표시나 색상을 지원한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-it 대화형 터미널 모드를 지원해 입출력이 모두 가능하도록 설정한다. 대부분의 경우 함께 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;docker run&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 새로운 컨테이너를 생성하고 실행한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 컨테이너가 없으면 새로 생성하고 컨테이너가 있더라도 하나 더 생성한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;docker run -it &amp;lt;container_name&amp;gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Note2. docker exec example&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS EC2 Instance 내부의 mysql docker image에 docker exec 명령어를 사용하여 접근한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;docker exec 명령어를 사용하기 위해선 현재 컨테이너가 실행중이어야 하기 때문에 docker ps 명령어를 통해 접근하고자 하는 인스턴스 (mysql)이 실행 중인지 체크한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1770766657253&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker ps&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;접근하고자 하는 컨테이너가 실행중이라면 docker exec 명령어를 사용해 컨테이너에 접근한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1770766727812&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker exec -it nimda-mysql mysql -u nimda -p&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Cloud/Docker</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/391</guid>
      <comments>https://novlog.tistory.com/entry/Docker-Note1-docker-exec#entry391comment</comments>
      <pubDate>Wed, 11 Feb 2026 08:45:10 +0900</pubDate>
    </item>
    <item>
      <title>React Note1. state, variable useState, useEffect</title>
      <link>https://novlog.tistory.com/entry/React-Note1-state-variable-useState-useEffect</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Note1. React Rendering mechanism &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React의 Component는 Rendering 상태가 변경될 때마다 함수를 다시 렌더링한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태(State) : React가 감시하는 데이터이다. 상태가 변하면 리액트는 UI를 다시 렌더링한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수(let, const) : 렌더링될 때마다 초기화되며 값이 변하더라도 UI는 렌더링되지 않는다.&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;즉, 상태가 변경되면 리액트는 변경된 컴포넌트를 추적해 업데이트된 상태를 반영하여 UI를 자동으로 리랜더링 하지만 변수가 변경된다고 하더라도 UI를 자동으로 랜더링하지는 않는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Note2. useState&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useState는 컴포넌트 내부에서 동적 데이터를 관리할 때 사용하는 일종의 기억 장치이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;state : 현재 상태 값&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setState : 상태 업데이트 함수. 해당 함수가 호출되면 리액트는 리랜더링 작업을 수행한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1770419007393&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [state, setState] = useState(initialValue);&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;size18&quot;&gt;&lt;b&gt;Note3. useEffect&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트 랜더링과 관련 없는 별개의 side effect를 수행할 때 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트가 화면에 나타난 직후 (Mount), 사라지기 직전 (Unmmount), 특정 값의 변경 (Update)시 실행할 로직을 정의한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 외부 데이터에 접근하거나 API를 호출하는 등의 작업에 빈번하게 사용되고는 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 첫 번째 인자에는 실행할 함수를 두 번째 인자에는 의존성 배열을 대입한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의존성 배열이 변경될 때마다 첫 번째 인자에 대입한 함수가 실행된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- Dependency Array&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;... [] : 컴포넌트가 나타난 직후 딱 한 번만 실행되며 API 초기 호출에 사용된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;... [value] : value가 바뀔 때마다 실행된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;... 생략 : 리랜더링이 일어날 때마다 실행되며, 생략이 가능하긴 하지만 성능에 무리가 갈 수 있기에 추천하지 않는다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1770419848353&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    useEffect(() =&amp;gt; {
       // api 호출 로직
    }, [method, dependency Array]);&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;size18&quot;&gt;&lt;b&gt;Summary.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useState는 데이터를 보관하는 저장소 역할을 수행하고, useEffect는 특정 시점에 행동하는 명령을 수행한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useEffect를 사용해서 상태의 변화를 추적하는것이 불가능하지는 않지만 이는 불필요한 리렌더링 Double Rendering을 발생시켜 성능 저하의 원인이된다. 또한, 앱의 규모가 작을때는 상관없지만 규모가 커지만 성능 저하 및 버그의 원인이된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>dev/React</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/390</guid>
      <comments>https://novlog.tistory.com/entry/React-Note1-state-variable-useState-useEffect#entry390comment</comments>
      <pubDate>Sat, 7 Feb 2026 08:26:26 +0900</pubDate>
    </item>
    <item>
      <title>[Git] Branch Protection Rule</title>
      <link>https://novlog.tistory.com/entry/Git-Branch-Protection-Rule</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;해당 글은 Github에서 Branch를 보호하는 룰을 설정하는 방법에 대해 다룬다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;특히 팀 협업에 있어서 Main Branch의 Rule을 설정하는 것은 매우 중요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;우선 Team Repository에 들어가 브랜치 규칙을 할당할 팀을 먼저 생성한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;797&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Okyq5/dJMcadgAjvF/hc8Sz1A21f8IVChEXLYfBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Okyq5/dJMcadgAjvF/hc8Sz1A21f8IVChEXLYfBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Okyq5/dJMcadgAjvF/hc8Sz1A21f8IVChEXLYfBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOkyq5%2FdJMcadgAjvF%2Fhc8Sz1A21f8IVChEXLYfBk%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;1197&quot; height=&quot;797&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;797&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;541&quot; data-origin-height=&quot;665&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/73aCQ/dJMcaa5eTxb/71kbwvkuKScUKQGwSTKh50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/73aCQ/dJMcaa5eTxb/71kbwvkuKScUKQGwSTKh50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/73aCQ/dJMcaa5eTxb/71kbwvkuKScUKQGwSTKh50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F73aCQ%2FdJMcaa5eTxb%2F71kbwvkuKScUKQGwSTKh50%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;541&quot; height=&quot;665&quot; data-origin-width=&quot;541&quot; data-origin-height=&quot;665&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Branch Name &amp;amp; Enforcement Status&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Repository Settings Rulesets로 들어가 Ruleset Name, Enforcement Status를 Active한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;911&quot; data-origin-height=&quot;316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dLQQHd/dJMcagRVU7L/IYVbkG0bivoNg4Wgi3UlF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dLQQHd/dJMcagRVU7L/IYVbkG0bivoNg4Wgi3UlF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dLQQHd/dJMcagRVU7L/IYVbkG0bivoNg4Wgi3UlF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdLQQHd%2FdJMcagRVU7L%2FIYVbkG0bivoNg4Wgi3UlF1%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;646&quot; height=&quot;224&quot; data-origin-width=&quot;911&quot; data-origin-height=&quot;316&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Bypass list&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Rulset을 무시하고 직접 브랜치에 Merge, Push 가능한 Organization Group을 설정한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;프로젝트 리더나 중요한 멤버만 설정할 수 있도록 하자.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;621&quot; data-origin-height=&quot;142&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MFdMc/dJMcagj4P26/vHUOD5EV1NcBIutitdyK80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MFdMc/dJMcagj4P26/vHUOD5EV1NcBIutitdyK80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MFdMc/dJMcagj4P26/vHUOD5EV1NcBIutitdyK80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMFdMc%2FdJMcagj4P26%2FvHUOD5EV1NcBIutitdyK80%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;573&quot; height=&quot;131&quot; data-origin-width=&quot;621&quot; data-origin-height=&quot;142&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Target branches &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;BranchRule을 적용할 Branch를 선택한다. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;165&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HlstG/dJMcagRVVaI/k2DtxSVSSqPkShwAz7PBrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HlstG/dJMcagRVVaI/k2DtxSVSSqPkShwAz7PBrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HlstG/dJMcagRVVaI/k2DtxSVSSqPkShwAz7PBrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHlstG%2FdJMcagRVVaI%2Fk2DtxSVSSqPkShwAz7PBrk%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;624&quot; height=&quot;165&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;165&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Branch Rules&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;중요한 Branch Rules만 적어두었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Restrict updates&lt;/b&gt; - 브랜치로의 직접적인 Push를 차단한다. 관리자나 허용된 인원외에는 메인 브랜치에 직접 코드를 올릴 수 없다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Restrict delections&lt;/b&gt; - 브랜치 삭제를 방지한다. 실수로 브랜치를 날려버리는 상황을 방지한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Block force pushes&lt;/b&gt; - 커밋 히스토리를 보호한다. git push -f 명령어로 과거 기록을 덮어쓰거나 지우는 상황을 방지한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chf0IP/dJMcac21ip3/nQKUoKNtbC4ZeLFKDXZli0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chf0IP/dJMcac21ip3/nQKUoKNtbC4ZeLFKDXZli0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chf0IP/dJMcac21ip3/nQKUoKNtbC4ZeLFKDXZli0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fchf0IP%2FdJMcac21ip3%2FnQKUoKNtbC4ZeLFKDXZli0%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;788&quot; height=&quot;470&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Require a pull request before merging&lt;/b&gt; - PR을 강제한다. 모든 코드는 반드시 PR을 생성하고 리뷰를 거쳐야만 브랜에 합쳐질 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Required approvals&lt;/b&gt; - 최소한의 PR 동료 승인 인원을 설정한다. (혼자 커밋하고 PR하는 상황을 방지)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;Dismiss stale pull request approvals when new commits are pushed&lt;/span&gt; - 승인을 받은 유저가 코드를 다시 수정하고 푸시하면, 기존 승인을 무효화하고 다시 리뷰받도록 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;Require conversation resolution before merging&lt;/span&gt; - 리뷰어가 단 코멘트가 모두 Resolved 상태가 되어야만 머지할 수 있도록 강제한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;Require reviews from Code Owners (Option)&lt;/span&gt; - 특정 파일 (DB, 보안 관련 코드 등)은 미리 전문가만 승인할 수 있도록 강제한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Allowed merge methods - 머지 방식 (Merge, Squash, Rebase) 을 설정한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;586&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Aga9O/dJMcabwiA26/XNgkrrm5edywvrSOkBo3V1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Aga9O/dJMcabwiA26/XNgkrrm5edywvrSOkBo3V1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Aga9O/dJMcabwiA26/XNgkrrm5edywvrSOkBo3V1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAga9O%2FdJMcabwiA26%2FXNgkrrm5edywvrSOkBo3V1%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;779&quot; height=&quot;571&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;586&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Require Status Checks to Pass&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;CI도구를 Github Actions 워크플로우 파일(.yml)에 등록한 경우에만 활성화하도록 하자.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;코드를 검사해 CI도구가 Pass 상태가 되어야지만 Merge 버튼이 활성화 되도록 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;Require branches to be up to date before merging&lt;/span&gt; - 머지하려는 코드가 타겟 브랜치의 최신 상태를 반영하고 있어야 머지를 허용한다. 충돌을 미리 방지하는 역할을 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kSrvB/dJMcafertbR/FSkhOOYSpyfvBeqBCZ5in0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kSrvB/dJMcafertbR/FSkhOOYSpyfvBeqBCZ5in0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kSrvB/dJMcafertbR/FSkhOOYSpyfvBeqBCZ5in0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkSrvB%2FdJMcafertbR%2FFSkhOOYSpyfvBeqBCZ5in0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;782&quot; height=&quot;442&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>dev/Git</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/389</guid>
      <comments>https://novlog.tistory.com/entry/Git-Branch-Protection-Rule#entry389comment</comments>
      <pubDate>Thu, 5 Feb 2026 14:19:30 +0900</pubDate>
    </item>
    <item>
      <title>BOJ 1431 Cpp Feat. Sort Function Custorm</title>
      <link>https://novlog.tistory.com/entry/BOJ-1431-Cpp-Feat-Sort-Function-Custorm</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Sort Function을 커스텀해서 정렬 조건을 설정하는 문제이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;문제에서 제시한 3가지 조건을 comp 비교 함수 내부에 잘 정의하면 쉽게 풀 수 있다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;condtion1 : 길이가 더 작은 문자열이 앞으로 온다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;condition2 : 모든 수의 합을 비교해 작은 합이 먼저 온다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;condition3 : 사전순으로 정렬한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1767071353865&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        // a의 합이 b보다 작다
        if (a_digit_sum != b_digit_sum)
            return a_digit_sum &amp;lt; b_digit_sum;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;tip : 정렬되어야 하는 순서를 비교 연산식으로 리턴하는 것이 실수를 줄일 수 있다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1767071384916&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;PS2026/12월/1431.cpp at main &amp;middot; novvvv/PS2026&quot; data-og-description=&quot;26년 알고리즘 문제 풀이 레포지토리 . Contribute to novvvv/PS2026 development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/novvvv/PS2026/blob/main/12%EC%9B%94/1431.cpp&quot; data-og-url=&quot;https://github.com/novvvv/PS2026/blob/main/12%EC%9B%94/1431.cpp&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bCsdpv/hyZQ8ifZfN/DSHaQCr2xHmFVT4ceKl7Mk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/Pc0FF/hyZQFvdBli/DEVUBVNetkvvBYExAguQk0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/novvvv/PS2026/blob/main/12%EC%9B%94/1431.cpp&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/novvvv/PS2026/blob/main/12%EC%9B%94/1431.cpp&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bCsdpv/hyZQ8ifZfN/DSHaQCr2xHmFVT4ceKl7Mk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/Pc0FF/hyZQFvdBli/DEVUBVNetkvvBYExAguQk0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;PS2026/12월/1431.cpp at main &amp;middot; novvvv/PS2026&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;26년 알고리즘 문제 풀이 레포지토리 . Contribute to novvvv/PS2026 development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1431&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1431&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm/PS With C++</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/388</guid>
      <comments>https://novlog.tistory.com/entry/BOJ-1431-Cpp-Feat-Sort-Function-Custorm#entry388comment</comments>
      <pubDate>Tue, 30 Dec 2025 14:11:41 +0900</pubDate>
    </item>
    <item>
      <title>Solved.ac Class3++ AC 5430 Cpp</title>
      <link>https://novlog.tistory.com/entry/Solvedac-Class3-AC-5430-Cpp</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;문제에서 제시하는대로 단순히 배열에 담고 원소를 Reverse 하는 방식은 시간 복잡도가 O(N)만큼 소요된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;따라서 최악의 경우 100,000(p) * 100,000(n) * 100(t)를 하면 시간초과가 발생하기에 단순 Reverse로는 문제를 풀이할 수 없다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그렇기에 처음에는 current_deque와 reverse_deque 2개를 두어 R연산이 발생할때마다 swap하는 방식을 채택하였으나 마찬가지로 시간초과가 발생하여 코드를 분석해 보았더니 Java나 Python같은 언어와는 달리 Cpp에서는 아래 코드처럼 덱에 다른 덱을 대입하면 참조에 의한 복사가 아닌 값에 의한 복사가 발생해 단순 주소값을 변경하는것이 아니라 모든 원소를 복사하여 O(N)만큼의 시간 복잡도가 소요된다는 정보를 얻게되었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1766826054814&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;            // O(N)
            if (cmd[j] == 'R') {

                /* deque&amp;lt;int&amp;gt; temp_deq;
                temp_deq = current_deq;
                current_deq = reverse_deq;
                reverse_deq = temp_deq; */
                
            }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;만약 참조에 의한 복사가 일어나는 Java, Python같은 언어였다면 해당 방식으로도 통과했겠으나 C++이기에 다른 방식을 찾아보아야 했다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;is_reverse라는 배열의 원소가 뒤집혀 있는지 확인하는 별도의 boolean 변수를 두고, R연산이 발생할때마다 이 변수를 토글하는 방식으로 코드 로직을 수정해 문제를 해결했다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1766826209432&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;            // O(N)
            if (cmd[j] == 'R') {
            
                if (is_reverse) is_reverse = false;
                else is_reverse = true;
                
            }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://github.com/novvvv/PS2026/blob/main/12%EC%9B%94/5430.cpp&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/novvvv/PS2026/blob/main/12%EC%9B%94/5430.cpp&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1766826224017&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;PS2026/12월/5430.cpp at main &amp;middot; novvvv/PS2026&quot; data-og-description=&quot;26년 알고리즘 문제 풀이 레포지토리 . Contribute to novvvv/PS2026 development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/novvvv/PS2026/blob/main/12%EC%9B%94/5430.cpp&quot; data-og-url=&quot;https://github.com/novvvv/PS2026/blob/main/12%EC%9B%94/5430.cpp&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bB3rrT/hyZPISMHQT/FmoT6KhXMtRhw5pYJ2uHmK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/c5WFTl/hyZQCqLnKS/C7hdcpJN16V8WWU2O13GAk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/novvvv/PS2026/blob/main/12%EC%9B%94/5430.cpp&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/novvvv/PS2026/blob/main/12%EC%9B%94/5430.cpp&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bB3rrT/hyZPISMHQT/FmoT6KhXMtRhw5pYJ2uHmK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/c5WFTl/hyZQCqLnKS/C7hdcpJN16V8WWU2O13GAk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;PS2026/12월/5430.cpp at main &amp;middot; novvvv/PS2026&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;26년 알고리즘 문제 풀이 레포지토리 . Contribute to novvvv/PS2026 development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm/PS With C++</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/387</guid>
      <comments>https://novlog.tistory.com/entry/Solvedac-Class3-AC-5430-Cpp#entry387comment</comments>
      <pubDate>Sat, 27 Dec 2025 18:05:27 +0900</pubDate>
    </item>
    <item>
      <title>[RAG] RAG 성능의 평가 척도</title>
      <link>https://novlog.tistory.com/entry/RAG-RAG-%EC%84%B1%EB%8A%A5%EC%9D%98-%ED%8F%89%EA%B0%80-%EC%B2%99%EB%8F%84</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;RAGAS&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;모델 혹은 파이프라인을 구성했다면 이 모델이 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;'잘' 만들어진 모델인지 평가&lt;/span&gt;하는 과정이 필요하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;정통적인 모델들은 F1 Score 등의 방식을 사용하나 RAG 아키텍쳐 어플리케이션 성능 평가에는 살짝 애매하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이러한 RAG(Retrieval-Augmented Generation) 파이프라인을 평가하기 위해서 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;RAGAS&lt;/span&gt;라는 프레임워크를 주로 사용한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;즉 RAG 파이프라인을 구축하고 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;평가 지표 계산에 필요한 데이터셋 (question, answer, context, ground truth) 을 만든 뒤 RAGAS는 이 데이터셋을 이용해 평가 지표를 계산&lt;/span&gt;하는 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Ground Truth&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;특히 이 데이터셋 중에서도&amp;nbsp; RAG 아키텍쳐 기반의 에플리케이션을 평가에는 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;Ground Truth&lt;/span&gt;가 매우 중요하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Ground Truth(GT)란 평가하려는 시스템의 출력과 비교하기 위해서 사용하는 &quot;사람이 직접 작성한 정답지&quot;이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;RAG 평가에서 GT는 일반적으로 다음을 포함하는 데이터셋이라고 생각하면 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;1. Question (질문)&lt;/b&gt; : 사용자가 챗봇에 입력하는 질문이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;2. Ground Truth Answer (정답)&lt;/b&gt; : 해당 질문에 대한 완벽하고 정확한 사람 작성 답변이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그렇다면 나만의 Ground Truth 데이터셋은 어떻게 생성해야할까?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;혹은 비공개 데이터셋을 사용하는 RAG 어플리케이션에 필요한 Ground Truth는 어떻게 생성해야 할까&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;최적의 방안은 해당 도메인 전문가가 직접 답안을 생성하는 것이겠지만&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;가장 간단한 방법은 GPT, Gemini 같은 시중 LLM을 사용해서 비공개 데이터셋을 기반으로 답변 예제를 생성하는 방안이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;혹은 Raga, LlamaIndex 같은 테스트 데이터셋 생성 툴을 사용하는 방법도 존재한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Generaiton &amp;amp; Retrieval &lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;RAGAS의 주요 성능 메트릭을 정리해 보자면 크게 Retrieval과 Generation 카테고리로 구분이 가능한데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Retrieval은 정확하고 일관성 있는 답변 생성을 위한 좋은 품질의 Context 정보 검색 여부를 판단하며&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Generation은 검색된 Context 정보를 기반으로 LLM이 생성한 답변의 정확성과 질문을 명확하게 이해했는지 답변 생성 여부에 대한 성능을 평가한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;357&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LgifP/dJMcagYiFxP/SnUDAWicXOek06aggqS6vK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LgifP/dJMcagYiFxP/SnUDAWicXOek06aggqS6vK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LgifP/dJMcagYiFxP/SnUDAWicXOek06aggqS6vK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLgifP%2FdJMcagYiFxP%2FSnUDAWicXOek06aggqS6vK%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;510&quot; height=&quot;280&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;357&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://medium.aiplanet.com/evaluate-rag-pipeline-using-ragas-fbdd8dd466c1?gi=f8b0fc6b73df&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://medium.aiplanet.com/evaluate-rag-pipeline-using-ragas-fbdd8dd466c1?gi=f8b0fc6b73df&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Generation&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Faithfulness - 생성된 답변이 얼마나 컨텍스트(사실)에 근거한 정확한 답변인가&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Answer Relevancy - 생성된 답변이 질문(주어진 프롬프트)에 얼마나 관련성이 있는가&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Retrieval&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Context Recall - 전체 문서 집합에서 관련 있는 컨텍스트를 찾는 비율 (검색된 관련 컨텍스트 수 / 전체 관련 컨텍스트 수)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Context Precision - 검색한 컨텍스트중 질문에 대한 답변과 관련 있는 문서가 얼마나 상위에 랭크되어 있는지 평가하는 지표이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1764955163751&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[우아한 스터디] RAGAS : RAG 파이프라인 평가 프레임워크&quot; data-og-description=&quot;공식문서GitHubRagas 주요 성능 메트릭을 살펴보면 크게 Retrieval, Generation 각 카테고리 별 측면에서 메트릭을 정의할 수 있습니다.Retrieval 은 정확하고 일관성 있는 답변 생성을 위해 정확성, 정밀성,&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@judy_choi/%EC%9A%B0%EC%95%84%ED%95%9C-%EC%8A%A4%ED%84%B0%EB%94%94-RAGAS-RAG-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%ED%8F%89%EA%B0%80-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC&quot; data-og-url=&quot;https://velog.io/@judy_choi/우아한-스터디-RAGAS-RAG-파이프라인-평가-프레임워크&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dz7uLK/hyZOUsy1Nc/OqNlyVhGMoS9obWypQDB7k/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/bIHMVZ/hyZOLuNYC7/FB3oTEZK514uRHWCXkqWFK/img.png?width=1824&amp;amp;height=764&amp;amp;face=0_0_1824_764,https://scrap.kakaocdn.net/dn/tSy7Z/hyZOAfIKRr/v3SkSb0Q8poIBNTmAOEzfK/img.png?width=2764&amp;amp;height=498&amp;amp;face=0_0_2764_498&quot;&gt;&lt;a href=&quot;https://velog.io/@judy_choi/%EC%9A%B0%EC%95%84%ED%95%9C-%EC%8A%A4%ED%84%B0%EB%94%94-RAGAS-RAG-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%ED%8F%89%EA%B0%80-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@judy_choi/%EC%9A%B0%EC%95%84%ED%95%9C-%EC%8A%A4%ED%84%B0%EB%94%94-RAGAS-RAG-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%ED%8F%89%EA%B0%80-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dz7uLK/hyZOUsy1Nc/OqNlyVhGMoS9obWypQDB7k/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/bIHMVZ/hyZOLuNYC7/FB3oTEZK514uRHWCXkqWFK/img.png?width=1824&amp;amp;height=764&amp;amp;face=0_0_1824_764,https://scrap.kakaocdn.net/dn/tSy7Z/hyZOAfIKRr/v3SkSb0Q8poIBNTmAOEzfK/img.png?width=2764&amp;amp;height=498&amp;amp;face=0_0_2764_498');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[우아한 스터디] RAGAS : RAG 파이프라인 평가 프레임워크&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;공식문서GitHubRagas 주요 성능 메트릭을 살펴보면 크게 Retrieval, Generation 각 카테고리 별 측면에서 메트릭을 정의할 수 있습니다.Retrieval 은 정확하고 일관성 있는 답변 생성을 위해 정확성, 정밀성,&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1764997000345&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;LLM의 좋은 친구, 'RAG' - RAG의 효과는 어떻게 평가ᄒ&quot; data-og-description=&quot;RAG 어플리케이션 평가 방법론, 지표, 그리고 도구&quot; data-og-host=&quot;turingpost.co.kr&quot; data-og-source-url=&quot;https://turingpost.co.kr/p/llm-rag-optimization-tools&quot; data-og-url=&quot;https://turingpost.co.kr/p/llm-rag-optimization-tools&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eiqQTd/hyZO740tbp/WXRLTMsKRON48fKbQfSkwk/img.png?width=920&amp;amp;height=486&amp;amp;face=0_0_920_486,https://scrap.kakaocdn.net/dn/nydMm/hyZOECtczZ/RT8j7KGPnGLgI0lyF0QrM0/img.png?width=920&amp;amp;height=486&amp;amp;face=0_0_920_486,https://scrap.kakaocdn.net/dn/ceuCf7/hyZO00HQ3G/eMAdaTnPCv8ZlDIKGGebck/img.png?width=1600&amp;amp;height=500&amp;amp;face=0_0_1600_500&quot;&gt;&lt;a href=&quot;https://turingpost.co.kr/p/llm-rag-optimization-tools&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://turingpost.co.kr/p/llm-rag-optimization-tools&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eiqQTd/hyZO740tbp/WXRLTMsKRON48fKbQfSkwk/img.png?width=920&amp;amp;height=486&amp;amp;face=0_0_920_486,https://scrap.kakaocdn.net/dn/nydMm/hyZOECtczZ/RT8j7KGPnGLgI0lyF0QrM0/img.png?width=920&amp;amp;height=486&amp;amp;face=0_0_920_486,https://scrap.kakaocdn.net/dn/ceuCf7/hyZO00HQ3G/eMAdaTnPCv8ZlDIKGGebck/img.png?width=1600&amp;amp;height=500&amp;amp;face=0_0_1600_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;LLM의 좋은 친구, 'RAG' - RAG의 효과는 어떻게 평가ᄒ&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;RAG 어플리케이션 평가 방법론, 지표, 그리고 도구&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;turingpost.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project/Rag Blog</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/386</guid>
      <comments>https://novlog.tistory.com/entry/RAG-RAG-%EC%84%B1%EB%8A%A5%EC%9D%98-%ED%8F%89%EA%B0%80-%EC%B2%99%EB%8F%84#entry386comment</comments>
      <pubDate>Sat, 6 Dec 2025 23:30:21 +0900</pubDate>
    </item>
    <item>
      <title>[DevLog] RAG BLOG</title>
      <link>https://novlog.tistory.com/entry/RAG-BLOG-Project-DevLog</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;RAG 기반 블로그 프로젝트를 진행하며 학습한 내용을 정리합니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://novlog.tistory.com/entry/RAG-RAG-%EC%84%B1%EB%8A%A5%EC%9D%98-%ED%8F%89%EA%B0%80-%EC%B2%99%EB%8F%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;RAGAS RAG 성능의 평가 척도&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project/Rag Blog</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/385</guid>
      <comments>https://novlog.tistory.com/entry/RAG-BLOG-Project-DevLog#entry385comment</comments>
      <pubDate>Sat, 6 Dec 2025 02:25:31 +0900</pubDate>
    </item>
    <item>
      <title>[DevLog] 카페 주문 시스템 Vol2. 주문 상태 관리</title>
      <link>https://novlog.tistory.com/entry/DevLog-%EC%B9%B4%ED%8E%98-%EC%A3%BC%EB%AC%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-Vol2-%EC%A3%BC%EB%AC%B8-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;본 게시글은 단순 구현 기록용도로 자세한 소스코드 설명이 포함되어 있지 않습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #fafafa; color: #222222; text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;모든 소스코드는 아래 깃허브 주소에 공개되어 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #fafafa; color: #222222; text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;a href=&quot;https://github.com/novvvv/cafe_order_system&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/novvvv/cafe_order_system&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1763789125882&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - novvvv/cafe_order_system: 3학년2학기 웹프로그래밍 기말프로젝트&quot; data-og-description=&quot;3학년2학기 웹프로그래밍 기말프로젝트. Contribute to novvvv/cafe_order_system development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/novvvv/cafe_order_system&quot; data-og-url=&quot;https://github.com/novvvv/cafe_order_system&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/br5Ktn/hyZOfwfy9D/pJ8bpKxBh8DK9K60yTH1f1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/c4oHKT/hyZNzhhCRH/nANmr9RXwxu0xsO4aaGez1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/novvvv/cafe_order_system&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/novvvv/cafe_order_system&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/br5Ktn/hyZOfwfy9D/pJ8bpKxBh8DK9K60yTH1f1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/c4oHKT/hyZNzhhCRH/nANmr9RXwxu0xsO4aaGez1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - novvvv/cafe_order_system: 3학년2학기 웹프로그래밍 기말프로젝트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;3학년2학기 웹프로그래밍 기말프로젝트. Contribute to novvvv/cafe_order_system development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;#Related&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1763789152138&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[DevLog] 카페 주문 시스템 Vol1. 메인 화면 구현 및 CRUD&quot; data-og-description=&quot;https://github.com/novvvv/cafe_order_system GitHub - novvvv/cafe_order_system: 3학년2학기 웹프로그래밍 기말프로젝트3학년2학기 웹프로그래밍 기말프로젝트. Contribute to novvvv/cafe_order_system development by creating an accou&quot; data-og-host=&quot;novlog.tistory.com&quot; data-og-source-url=&quot;https://novlog.tistory.com/entry/DevLog-%EC%B9%B4%ED%8E%98-%EC%A3%BC%EB%AC%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-1-%EB%A9%94%EC%9D%B8-%ED%99%94%EB%A9%B4-%EA%B5%AC%ED%98%84-%EB%B0%8F-CRUD&quot; data-og-url=&quot;https://novlog.tistory.com/entry/DevLog-%EC%B9%B4%ED%8E%98-%EC%A3%BC%EB%AC%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-1-%EB%A9%94%EC%9D%B8-%ED%99%94%EB%A9%B4-%EA%B5%AC%ED%98%84-%EB%B0%8F-CRUD&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/mbe2p/hyZOghBofV/D0AzVFJHHXVAl4k8drPVVk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/eBBUH/hyZOgBUENx/a9ftfOQtJ5wtCSwkmT4rA1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/THZ2R/hyZN5AnNMj/kw7G9YNdLVcuZ0S4OtOIW0/img.png?width=1869&amp;amp;height=900&amp;amp;face=0_0_1869_900&quot;&gt;&lt;a href=&quot;https://novlog.tistory.com/entry/DevLog-%EC%B9%B4%ED%8E%98-%EC%A3%BC%EB%AC%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-1-%EB%A9%94%EC%9D%B8-%ED%99%94%EB%A9%B4-%EA%B5%AC%ED%98%84-%EB%B0%8F-CRUD&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://novlog.tistory.com/entry/DevLog-%EC%B9%B4%ED%8E%98-%EC%A3%BC%EB%AC%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-1-%EB%A9%94%EC%9D%B8-%ED%99%94%EB%A9%B4-%EA%B5%AC%ED%98%84-%EB%B0%8F-CRUD&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/mbe2p/hyZOghBofV/D0AzVFJHHXVAl4k8drPVVk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/eBBUH/hyZOgBUENx/a9ftfOQtJ5wtCSwkmT4rA1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/THZ2R/hyZN5AnNMj/kw7G9YNdLVcuZ0S4OtOIW0/img.png?width=1869&amp;amp;height=900&amp;amp;face=0_0_1869_900');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[DevLog] 카페 주문 시스템 Vol1. 메인 화면 구현 및 CRUD&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;https://github.com/novvvv/cafe_order_system GitHub - novvvv/cafe_order_system: 3학년2학기 웹프로그래밍 기말프로젝트3학년2학기 웹프로그래밍 기말프로젝트. Contribute to novvvv/cafe_order_system development by creating an accou&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;novlog.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;#Goal&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;✨주문 데이터에 주문 상태 필드를 추가해 상태 관리를 추가한다.&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: center;&quot;&gt;✨&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1079&quot; data-origin-height=&quot;594&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kWw5k/dJMcafLMnVk/dj9mpktqU0Wu0nR8NAkqc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kWw5k/dJMcafLMnVk/dj9mpktqU0Wu0nR8NAkqc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kWw5k/dJMcafLMnVk/dj9mpktqU0Wu0nR8NAkqc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkWw5k%2FdJMcafLMnVk%2Fdj9mpktqU0Wu0nR8NAkqc1%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;1079&quot; height=&quot;594&quot; data-origin-width=&quot;1079&quot; data-origin-height=&quot;594&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;850&quot; data-origin-height=&quot;673&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r84JM/dJMcahpiBDy/PbhmsMhrMCBhcUDPEVWtM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r84JM/dJMcahpiBDy/PbhmsMhrMCBhcUDPEVWtM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r84JM/dJMcahpiBDy/PbhmsMhrMCBhcUDPEVWtM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr84JM%2FdJMcahpiBDy%2FPbhmsMhrMCBhcUDPEVWtM1%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;850&quot; height=&quot;673&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;673&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그림1.png&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;898&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwyQ0r/dJMcaaRe1Fc/noDMqFbkLLBpVXUZ7Lmz2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwyQ0r/dJMcaaRe1Fc/noDMqFbkLLBpVXUZ7Lmz2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwyQ0r/dJMcaaRe1Fc/noDMqFbkLLBpVXUZ7Lmz2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwyQ0r%2FdJMcaaRe1Fc%2FnoDMqFbkLLBpVXUZ7Lmz2k%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;300&quot; height=&quot;300&quot; data-filename=&quot;그림1.png&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;898&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;데이터베이스 구조 변경&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;cafe_orders 테이블에 ENUM 타입으로 order_status를 추가한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763788753458&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 주문 테이블 생성
CREATE TABLE cafe_orders (
    order_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '주문 ID',
    customer_name VARCHAR(50) NOT NULL COMMENT '고객 이름',
    customer_phone VARCHAR(20) NOT NULL COMMENT '고객 연락처',
    menu_name VARCHAR(50) NOT NULL COMMENT '메뉴 이름',
    size_code VARCHAR(10) NOT NULL COMMENT '사이즈 코드 (S, M, L, G, X)',
    temperature VARCHAR(10) NOT NULL COMMENT '온도 (H: Hot, I: Ice)',
    quantity INT NOT NULL DEFAULT 1 COMMENT '수량',
    unit_price INT NOT NULL COMMENT '단가',
    total_price INT NOT NULL COMMENT '총 가격',
    request_message VARCHAR(500) COMMENT '요청사항',
    order_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '주문 시간',
    order_status ENUM('WAITING', 'PENDING', 'COMPLETED', 'CANCELED') NOT NULL DEFAULT 'WAITING' COMMENT '주문 상태',
    INDEX idx_customer_phone (customer_phone),
    INDEX idx_order_time (order_time),
    INDEX idx_order_status (order_status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='카페 주문 테이블';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;OrderDAO 수정&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;OrderDAO에 주문 상태를 수정하는 Update Query를 날리는 updateOrderStatus 메서드를 정의한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763788858802&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class OrderDAO {

    // * insertOrder() - 주문 데이터 삽입 함수
    // * 데이터베이스에 주문 데이터 (Order)를 삽입한다.
    public boolean insertOrder(Order order) {
    	...
    }

    // * getAllOrders() - 전체 주문 목록 조회 (최신순으로 정렬))
    // * 데이터베이스에서 주문 데이터 (Order)를 조회해 리스트로 반환한다. *
    public List&amp;lt;Order&amp;gt; getAllOrders() {
    	...
    }

    // * updateOrderStatus() - 주문 상태 업데이트 함수
    // * 주문 ID와 새로운 상태를 받아서 주문 상태를 업데이트한다.
    public boolean updateOrderStatus(int orderId, String status) {
        Connection conn = null;
        PreparedStatement pstmt = null;

        String sql = &quot;UPDATE cafe_orders SET order_status = ? WHERE order_id = ?&quot;;

        try {
            conn = DBUtil.getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, status);
            pstmt.setInt(2, orderId);
            int result = pstmt.executeUpdate();
            return result &amp;gt; 0;

        } 
        
        catch (SQLException e) {
            e.printStackTrace();
            return false;
        } 
        
        finally {
            closeResources(pstmt, conn);
        }
    }

    // * closeResources() - 리소스 해제 함수 (ResultSet 포함)
    private void closeResources(ResultSet rs, PreparedStatement pstmt, Connection conn) {
    	...
    }

    // * closeResources() - 리소스 해제 함수 (기존 메서드 - 하위 호환성)
    private void closeResources(PreparedStatement pstmt, Connection conn) {
    	...
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;listOrder.jsp&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;사용자가 드롭다운 메뉴 선택 시 해당 메뉴에 selected 속성을 적용한 후, 메뉴가 바뀔때마다 updateOrderStatus 함수를 호출한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763787377786&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;td&amp;gt;
	&amp;lt;select name=&quot;newStatus&quot; class=&quot;status-select&quot; data-order-id=&quot;${order.orderId}&quot; onchange=&quot;updateOrderStatus(this)&quot;&amp;gt;
        &amp;lt;option value=&quot;WAITING&quot; ${order.orderStatus == 'WAITING' ? 'selected' : ''}&amp;gt;대기중&amp;lt;/option&amp;gt;
        &amp;lt;option value=&quot;PENDING&quot; ${order.orderStatus == 'PENDING' ? 'selected' : ''}&amp;gt;진행중&amp;lt;/option&amp;gt;
        &amp;lt;option value=&quot;COMPLETED&quot; ${order.orderStatus == 'COMPLETED' ? 'selected' : ''}&amp;gt;완료&amp;lt;/option&amp;gt;
        &amp;lt;option value=&quot;CANCELED&quot; ${order.orderStatus == 'CANCELED' ? 'selected' : ''}&amp;gt;취소&amp;lt;/option&amp;gt;
	&amp;lt;/select&amp;gt;
&amp;lt;/td&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;AJAX 방식으로 통신하기 위해 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;XMLHttpRequest 객체&lt;/span&gt;를 사용한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;POST 방식으로 데이터를 담아 updateOrderStatus.jsp에 정보를 전달한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763788025784&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
        function updateOrderStatus(selectElement) {

            var orderId = selectElement.getAttribute('data-order-id');
            var newStatus = selectElement.value;
            
            // * AJAX 요청 전송 * 
            // XMLHttpRequest 객체 생성
            // XMLHttpRequest - 브라우저에서 서버와 통신하는 객체로, xhr변수에 요청 정보를 담는다.
            // 페이지 새로고침 없이 서버와 데이터를 주고받기 위해 AJAX 요청을 사용한다.
            var xhr = new XMLHttpRequest();
            xhr.open('POST', '${pageContext.request.contextPath}/updateOrderStatus.jsp', true);
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            
            // 요청 완료 시 처리
            xhr.onreadystatechange = function() {

                // * 요청 완료 시 처리 * 
                // readyState : 요청의 현재 상태를 나타내는 숫자 (4 : 요청 완료)
                if (xhr.readyState === 4) {

                    // 200 : 요청 성공 (HTTP 상태 코드)
                    if (xhr.status === 200) {
                        try {
                            
                            var response = JSON.parse(xhr.responseText);
                            
                            if (response.success) {
                                console.log(response.message);
                            } 
                            
                            else {
                                alert(response.message || '주문 상태 변경에 실패했습니다.');
                            }

                        } catch (e) {
                            // JSON 파싱 실패 
                            console.log(&quot;파싱 실패&quot;);
                        }
                    } 
                    else {
                        alert('서버 오류가 발생했습니다.');
                    }
                }


            };
            
            // 요청 전송
            xhr.send('orderId=' + encodeURIComponent(orderId) + '&amp;amp;newStatus=' + encodeURIComponent(newStatus));
        }
    &amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;updateOrder.jsp&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;lisrOrder.jsp에서 받은 파라미터 (OrderId, newStatus) 를 검증한 뒤 OrderDAO를 사용해 데이터베이스로 UPDATE 쿼리를 날린다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763788668387&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;%@ page language=&quot;java&quot; pageEncoding=&quot;UTF-8&quot;%&amp;gt;
&amp;lt;%@ page import=&quot;com.cafe.dao.OrderDAO&quot; %&amp;gt;
&amp;lt;%
    response.setContentType(&quot;application/json; charset=UTF-8&quot;);
    
    // 파라미터 수신 (OrderId, newStatus) (주문 아이디, 상태)
    String orderIdStr = request.getParameter(&quot;orderId&quot;);
    String newStatus = request.getParameter(&quot;newStatus&quot;);
    
    boolean success = false;
    String message = &quot;&quot;;
    
    // [Exception] 파라미터 유효성 검증
    if (orderIdStr == null || newStatus == null || orderIdStr.trim().isEmpty() || newStatus.trim().isEmpty()) {
        message = &quot;파라미터가 올바르지 않습니다.&quot;;
    }

    // [Exception] 상태 값 유효성 검증
    else if (!newStatus.equals(&quot;WAITING&quot;) &amp;amp;&amp;amp; !newStatus.equals(&quot;PENDING&quot;) &amp;amp;&amp;amp; 
             !newStatus.equals(&quot;COMPLETED&quot;) &amp;amp;&amp;amp; !newStatus.equals(&quot;CANCELED&quot;)) {
        message = &quot;유효하지 않은 상태 값입니다.&quot;;
    }

    // [Success] 주문 상태 업데이트
    else {
        try {
            int orderId = Integer.parseInt(orderIdStr);
            
            // OrderDAO를 사용해서 주문 상태 업데이트 
            OrderDAO orderDAO = new OrderDAO();
            success = orderDAO.updateOrderStatus(orderId, newStatus);
            
            if (success) {
                message = &quot;주문 상태가 변경되었습니다.&quot;;
            } else {
                message = &quot;주문 상태 변경에 실패했습니다.&quot;;
            }
        } 
        // [Exception] 주문 ID가 숫자가 아닌 경우
        catch (NumberFormatException e) {
            message = &quot;주문 ID가 올바르지 않습니다.&quot;;
        } 
        
        // [Exception] 오류가 발생한 경우
        catch (Exception e) {
            message = &quot;오류가 발생했습니다.&quot;;
        }
    }
    
    // JSON 응답 출력
    if (message == null) {
        message = &quot;&quot;;
    }
    out.print(&quot;{\&quot;success\&quot;:&quot; + success + &quot;,\&quot;message\&quot;:\&quot;&quot; + message + &quot;\&quot;}&quot;);
%&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Archive/카페 주문 시스템</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/384</guid>
      <comments>https://novlog.tistory.com/entry/DevLog-%EC%B9%B4%ED%8E%98-%EC%A3%BC%EB%AC%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-Vol2-%EC%A3%BC%EB%AC%B8-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC#entry384comment</comments>
      <pubDate>Sat, 22 Nov 2025 14:29:54 +0900</pubDate>
    </item>
    <item>
      <title>[DevLog] 카페 주문 시스템 Vol1. 메인 화면 구현 및 CRUD</title>
      <link>https://novlog.tistory.com/entry/DevLog-%EC%B9%B4%ED%8E%98-%EC%A3%BC%EB%AC%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-1-%EB%A9%94%EC%9D%B8-%ED%99%94%EB%A9%B4-%EA%B5%AC%ED%98%84-%EB%B0%8F-CRUD</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://github.com/novvvv/cafe_order_system&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/novvvv/cafe_order_system&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1763786677406&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - novvvv/cafe_order_system: 3학년2학기 웹프로그래밍 기말프로젝트&quot; data-og-description=&quot;3학년2학기 웹프로그래밍 기말프로젝트. Contribute to novvvv/cafe_order_system development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/novvvv/cafe_order_system&quot; data-og-url=&quot;https://github.com/novvvv/cafe_order_system&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/br5Ktn/hyZOfwfy9D/pJ8bpKxBh8DK9K60yTH1f1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/c4oHKT/hyZNzhhCRH/nANmr9RXwxu0xsO4aaGez1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/novvvv/cafe_order_system&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/novvvv/cafe_order_system&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/br5Ktn/hyZOfwfy9D/pJ8bpKxBh8DK9K60yTH1f1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/c4oHKT/hyZNzhhCRH/nANmr9RXwxu0xsO4aaGez1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - novvvv/cafe_order_system: 3학년2학기 웹프로그래밍 기말프로젝트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;3학년2학기 웹프로그래밍 기말프로젝트. Contribute to novvvv/cafe_order_system development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;본 게시글은 단순 구현 기록용도로 자세한 소스코드 설명이 포함되어 있지 않습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;모든 소스코드는 위 깃허브 주소에 공개되어 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;#Related&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;a href=&quot;https://novlog.tistory.com/entry/DevLog-%EC%B9%B4%ED%8E%98-%EC%A3%BC%EB%AC%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B0%9C%EC%9A%94&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://novlog.tistory.com/entry/DevLog-%EC%B9%B4%ED%8E%98-%EC%A3%BC%EB%AC%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B0%9C%EC%9A%94&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1763786751224&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;DevLog - 카페 주문 시스템 개요&quot; data-og-description=&quot;#DevLog 카페 주문 시스템 개요 본 프로젝트는 교내 3학년 2학기 '웹프로그래밍 실습' 수업의 기말 텀 프로젝트입니다.주요 목표는 데이터베이스 연동 및 기본 CRUD(Create, Read, Update, Delete) 로직을 실&quot; data-og-host=&quot;novlog.tistory.com&quot; data-og-source-url=&quot;https://novlog.tistory.com/entry/DevLog-%EC%B9%B4%ED%8E%98-%EC%A3%BC%EB%AC%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B0%9C%EC%9A%94&quot; data-og-url=&quot;https://novlog.tistory.com/entry/DevLog-%EC%B9%B4%ED%8E%98-%EC%A3%BC%EB%AC%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B0%9C%EC%9A%94&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/VD957/hyZOexkeKV/Uqm59G0hNANVYLLIu1z3qk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/efVuOP/hyZOec0ubZ/DCC2pXFerU6DyCmpijwPe0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/KsLaD/hyZND43GZP/bAjiMfzGvVBWHWG1nDEig0/img.png?width=1156&amp;amp;height=1064&amp;amp;face=0_0_1156_1064&quot;&gt;&lt;a href=&quot;https://novlog.tistory.com/entry/DevLog-%EC%B9%B4%ED%8E%98-%EC%A3%BC%EB%AC%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B0%9C%EC%9A%94&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://novlog.tistory.com/entry/DevLog-%EC%B9%B4%ED%8E%98-%EC%A3%BC%EB%AC%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B0%9C%EC%9A%94&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/VD957/hyZOexkeKV/Uqm59G0hNANVYLLIu1z3qk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/efVuOP/hyZOec0ubZ/DCC2pXFerU6DyCmpijwPe0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/KsLaD/hyZND43GZP/bAjiMfzGvVBWHWG1nDEig0/img.png?width=1156&amp;amp;height=1064&amp;amp;face=0_0_1156_1064');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;DevLog - 카페 주문 시스템 개요&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;#DevLog 카페 주문 시스템 개요 본 프로젝트는 교내 3학년 2학기 '웹프로그래밍 실습' 수업의 기말 텀 프로젝트입니다.주요 목표는 데이터베이스 연동 및 기본 CRUD(Create, Read, Update, Delete) 로직을 실&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;novlog.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그림1.png&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;898&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VjKno/dJMcadUF8Jy/hWjvcn1StbJcwbeRRz9df0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VjKno/dJMcadUF8Jy/hWjvcn1StbJcwbeRRz9df0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VjKno/dJMcadUF8Jy/hWjvcn1StbJcwbeRRz9df0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVjKno%2FdJMcadUF8Jy%2FhWjvcn1StbJcwbeRRz9df0%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;300&quot; height=&quot;300&quot; data-filename=&quot;그림1.png&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;898&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;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;#단순 CRUD 기능 구현 및 페이지구현&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1869&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDdgzx/dJMcacBwxRF/pLuVjt4fXD4EKLTNk2jkFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDdgzx/dJMcacBwxRF/pLuVjt4fXD4EKLTNk2jkFk/img.png&quot; data-alt=&quot;메인페이지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDdgzx/dJMcacBwxRF/pLuVjt4fXD4EKLTNk2jkFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDdgzx%2FdJMcacBwxRF%2FpLuVjt4fXD4EKLTNk2jkFk%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;1869&quot; height=&quot;900&quot; data-origin-width=&quot;1869&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;메인페이지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;메인 페이지 구조는 최대한 간단하게 구성했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;화면 중앙의 사진 4개와 Header, Footer 그리고 페이지 이동이 가능한 NavBar로 구성된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/U0b8I/dJMcab3Gsfs/sWYk52N5wphXO0YGsYXXzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/U0b8I/dJMcab3Gsfs/sWYk52N5wphXO0YGsYXXzK/img.png&quot; data-alt=&quot;OrderForm.jsp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/U0b8I/dJMcab3Gsfs/sWYk52N5wphXO0YGsYXXzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FU0b8I%2FdJMcab3Gsfs%2FsWYk52N5wphXO0YGsYXXzK%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;904&quot; height=&quot;508&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;508&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;OrderForm.jsp&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;다음은 주문 입력 폼이다. 필드 정보를 유저가 입력할 수 있으며, 입력된 필드는 실시간 리스너를 통해 validate.js에서 검증된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;마지막으로 주문하기 버튼을 클릭하면 주문 정보를 다음 주문 처리 결과 페이지로 전송한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/td25H/dJMcaiuVqAu/8NtBw2saeUS4mB9m8xo1r0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/td25H/dJMcaiuVqAu/8NtBw2saeUS4mB9m8xo1r0/img.png&quot; data-alt=&quot;InsertOrder.jsp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/td25H/dJMcaiuVqAu/8NtBw2saeUS4mB9m8xo1r0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftd25H%2FdJMcaiuVqAu%2F8NtBw2saeUS4mB9m8xo1r0%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;766&quot; height=&quot;582&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;582&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;InsertOrder.jsp&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;유저가 입력한 필드를 기반으로 화면에 처리 결과가 화면에 출력된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;OrderDAO에 의해 주문 객체가 MySQL기반 데이터베이스에 저장된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WdnjQ/dJMcabijBcS/hXnUEONQuocExMuWJY8oU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WdnjQ/dJMcabijBcS/hXnUEONQuocExMuWJY8oU1/img.png&quot; data-alt=&quot;DataGrip&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WdnjQ/dJMcabijBcS/hXnUEONQuocExMuWJY8oU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWdnjQ%2FdJMcabijBcS%2FhXnUEONQuocExMuWJY8oU1%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;904&quot; height=&quot;158&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;158&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DataGrip&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;마지막으로 주문 목록 페이지다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;데이터베이스에서 INSERT 쿼리를 날려 주문 시간을 기준으로 내림차(desc)정렬하여, 주문 정보를 조회한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0qILC/dJMcabJnYtZ/LQZsU13HcyEeF9HB2Z4bMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0qILC/dJMcabJnYtZ/LQZsU13HcyEeF9HB2Z4bMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0qILC/dJMcabJnYtZ/LQZsU13HcyEeF9HB2Z4bMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0qILC%2FdJMcabJnYtZ%2FLQZsU13HcyEeF9HB2Z4bMK%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;904&quot; height=&quot;420&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;420&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Archive/카페 주문 시스템</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/383</guid>
      <comments>https://novlog.tistory.com/entry/DevLog-%EC%B9%B4%ED%8E%98-%EC%A3%BC%EB%AC%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-1-%EB%A9%94%EC%9D%B8-%ED%99%94%EB%A9%B4-%EA%B5%AC%ED%98%84-%EB%B0%8F-CRUD#entry383comment</comments>
      <pubDate>Sat, 22 Nov 2025 13:47:56 +0900</pubDate>
    </item>
    <item>
      <title>[DevLog] Vol0. 카페 주문 시스템 개요</title>
      <link>https://novlog.tistory.com/entry/DevLog-%EC%B9%B4%ED%8E%98-%EC%A3%BC%EB%AC%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B0%9C%EC%9A%94</link>
      <description>&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;#DevLog 카페 주문 시스템 개요&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-path-to-node=&quot;3&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-path-to-node=&quot;3,0&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;본 프로젝트는 교내 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;3학년 2학기 '웹프로그래밍 실습' 수업의 기말 텀 프로젝트&lt;/span&gt;입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3,1&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;주요 목표는 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;데이터베이스 연동 및 기본 CRUD(Create, Read, Update, Delete) 로직을 실습&lt;/span&gt;하는 것입니다.학술적 실습을 목적으로 하므로, 실제 결제 로직, 복잡한 트랜잭션 처리, 인증/인가 로직, 외부 클라우드 서비스 연동 등은 구현 범위에서 제외되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-path-to-node=&quot;3,2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;모든 소스 코드는 본 GitHub 저장소를 통해 공개됩니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;프로젝트 기간 2025.11.17 ~ ing&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;프로젝트 인원 1명&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Tech Stack&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Frontend - Html/Css/Js/Jsp&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Database - JDBC, mysql&lt;/span&gt;&lt;/p&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;figure id=&quot;og_1763427879529&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignLeft&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - novvvv/cafe_order_system: 3학년2학기 웹프로그래밍 기말프로젝트&quot; data-og-description=&quot;3학년2학기 웹프로그래밍 기말프로젝트. Contribute to novvvv/cafe_order_system development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/novvvv/cafe_order_system&quot; data-og-url=&quot;https://github.com/novvvv/cafe_order_system&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fNP2t/hyZNFnG7iH/YGk91uP79cdlAzopwMGaAK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/oqYKd/hyZOf9GiOM/xL8jR0IxbDKOpvFnsaJ7b1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/novvvv/cafe_order_system&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/novvvv/cafe_order_system&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fNP2t/hyZNFnG7iH/YGk91uP79cdlAzopwMGaAK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/oqYKd/hyZOf9GiOM/xL8jR0IxbDKOpvFnsaJ7b1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - novvvv/cafe_order_system: 3학년2학기 웹프로그래밍 기말프로젝트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;3학년2학기 웹프로그래밍 기말프로젝트. Contribute to novvvv/cafe_order_system development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;#프로젝트 요구사항&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; background-color: #333333; color: #dddddd;&quot;&gt;기본기능 (필수적 요구사항)&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 162px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 12.1705%; height: 19px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;구분&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 24.1473%; height: 19px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;기능&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 63.6821%; height: 19px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;세부 내용&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 12.1705%; height: 19px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;HTML&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 24.1473%; height: 19px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;주문 입력 폼을 구성한다.&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 63.6821%; height: 19px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이름, 연락처, 메뉴, 사이즈(S/M/L/G/X), 온도(H/I), 수량, 요청사항을 입력한다. orderForm.jsp&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.1705%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;CSS&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 24.1473%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;레이아웃 및 스타일을 구성한다.&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 63.6821%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;입력 폼, 테이블, 버튼 색상 및 폰트 디자인을 구성&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 12.1705%; height: 19px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;JS&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 24.1473%; height: 19px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;입력값을 검증한다.&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 63.6821%; height: 19px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;공란 확인, 수량 범위 (1 이상) 검증, 필수 선택 항목 체크&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.1705%; height: 36px;&quot; rowspan=&quot;2&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;JSP&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px; width: 24.1473%;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;DB 저장 처리&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 63.6821%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;insertOrder.jsp에서 주문 내용 DB에 INSERT 처리한다.&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 24.1473%; height: 19px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;예약 목록 조회&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 63.6821%; height: 19px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;listOrder.jsp에서 DB 데이터 SELECT 후 테이블 형태로 출력한다.&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 52px;&quot;&gt;
&lt;td style=&quot;width: 12.1705%; height: 52px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;DB&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 24.1473%; height: 52px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;테이블 설계&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 63.6821%; height: 52px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;cafe_orders(order_id, customer_name, customer_phone,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;menu_name, size_code, temperature, quantity, unit_price,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;total_price, request_message, order_time)&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd; font-family: 'Nanum Gothic';&quot;&gt;기본 기능 전체 코드&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- create_cafe_orders.sql (DB 테이블)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- orderForm.jsp (주문 입력 화면)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- css/style.css (레이아웃/스타일)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- js/validate.js (입력값 검증)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- insertOrder.jsp (DB INSERT)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- lsitOrder.jsp (주문 목록 조회)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd; font-family: 'Nanum Gothic';&quot;&gt;확장 기능&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;확장 기능은 다음 3개적 관점으로 고려하여 개발한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;1. 데이터 활용 관점 - 저장된 데이터를 &quot;어떻게 의미 있게 보여줄 것인가?&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;2. 사용자 경험 관점 - 사용자가 더 명확하고, 편리하게 예약할 수 있도록 도와주는 기능&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;3. 업무/서비스 로직 관점 - 시스템의 목적과 운영 규칙에 맞는 절차, 조건을 스스로 정의하고 적용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd; font-family: 'Nanum Gothic';&quot;&gt;확장 기능 1 : 데이터 활용 관점 - 주문을 상태로 관리하며, 일별/월별 매출을 그래프로 정리한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;현재 기본기능 (필수적 요구사항) 에서는 단순히 주문 입력 폼에서 들어온 데이터를 &quot;저장&quot; 하는 것에 초점을 둔다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;하지만, 백다방, 메가커피와 같은 어플리케이션을 분석해보면 보통 주문이 들어오면&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;주문이 들어오면 &quot;주문접수 - 제조중 - 제조완료&quot; 3가지 단계로 주문 상태를 관리한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;본 프로젝트에서는 제출폼(orderForm.jsp)을 통해서 생성된 주문 데이터(cafe_order)를 &quot;Waiting(주문접수)-Pending(제조중)-Completed(제조완료)-Cancled(취소)&quot; 상태로 관리하고,&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Completed(제조완료)된 데이터를 기준으로 일별/월별 매출을 그래프 형태로 제공하는 기능을 추가한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이는 관리자(사용자)가 토글을 통해 주문 상태를 설정 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;확장 기능 2: 사용자 경험 관점 - 예상 대기시간을 계산하여 출력하는 기능을 추가한다.&lt;/span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;백다방/쿠팡과 같은 서비스 사례와 같이, 사용자가 자신의 시간을 효율적으로 계획하고, &lt;b&gt;더욱 명확하고 편리하게 서비스(예약, 주문 픽업)를 이용&lt;/b&gt;할 수 있도록 지원함으로써 &lt;b&gt;사용자 경험을 극대화한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;예상 대기시간 계산 수식과 수식에 사용되는 가중치는 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;메뉴별 제조 시간&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 메뉴별 제조 시간이 상이하기에 별도의 가중치를 설정한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;현재 프로젝트에서 사용되는 메뉴는 총 4가지 (아메리카노, 에스프레소, 카푸치노, 카페라떼) 이며, 각각 제조 시간은 아래와 같다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;아메리카노 : 60s, 에스프레소 : 60s, 카푸치노 : 90s, 카푸치노 : 90s&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;시간대&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;-시간대별 매장 혼잡도를 계산한다. (매장 오픈 시간은 오전9시 ~ 오후6시로 가정한다.)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;비피크 타임 (9시~12시, 17시~18시)&amp;nbsp; 가중치 : 0.9&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;피크 타임 (12시~13시) 가중치 : 1.2&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;평상시 (13시~17시) 가중치 : 1.0&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;바리스타 수&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 매장에 재직중인 바리스타 수를 입력하여 예상 제조 시간을 계산한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;최소 1명 ~ 최대 3명 까지 지정 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;제조 중 건수&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 현재 Waiting(주문접수), Pending(제조중) 인 건수를 계산하여 가중치에 합산한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;최종 계산 수식&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OEHtd/dJMcadNTzck/ikqFWQkFP3QVmziFmHV7fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OEHtd/dJMcadNTzck/ikqFWQkFP3QVmziFmHV7fk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OEHtd/dJMcadNTzck/ikqFWQkFP3QVmziFmHV7fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOEHtd%2FdJMcadNTzck%2FikqFWQkFP3QVmziFmHV7fk%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;521&quot; height=&quot;172&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; background-color: #333333; color: #dddddd;&quot;&gt;확장 기능 3 &lt;span style=&quot;text-align: center;&quot;&gt;업무/서비스 로직 관점 - &lt;/span&gt;재고 관리 시스템 (품절 기능)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;기존 시스템은 메뉴의 잔여 재고 수량을 고려하지 않고 모든 주문 요청을 처리하도록 설정되어 있다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이로 인해 실제 재고가 없는 메뉴에 대해서도 주문이 접수되는 문제가 발생한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;3,3&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;메뉴별 재고를 관리하는 시스템을 신규 구축한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;주문 발생 시 해당 메뉴의 재고 수량을 확인하는 로직을 추가한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;재고가 부족할 경우 주문 생성을 폼에서 제한(품절 처리)하며, 사용자에게 해당 메뉴가 품절 상태임을 안내하는 기능을 구현한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Archive/카페 주문 시스템</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/382</guid>
      <comments>https://novlog.tistory.com/entry/DevLog-%EC%B9%B4%ED%8E%98-%EC%A3%BC%EB%AC%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B0%9C%EC%9A%94#entry382comment</comments>
      <pubDate>Thu, 20 Nov 2025 15:52:59 +0900</pubDate>
    </item>
    <item>
      <title>[DevLog] 동아리 사이트 개발일지 (2) 사용자 문제 제출 조회 API 구현</title>
      <link>https://novlog.tistory.com/entry/DevLog-%EB%8F%99%EC%95%84%EB%A6%AC-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-2-%EC%82%AC%EC%9A%A9%EC%9E%90-%EB%AC%B8%EC%A0%9C-%EC%A0%9C%EC%B6%9C-%EC%A1%B0%ED%9A%8C-API-%EA%B5%AC%ED%98%84</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;문제상황&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;현재 구현된 문제조회 API는 모든 사용자가 제출한 채점결과를 그대로 필터링 없이 프론트 페이지에 뿌려주는 방식&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;따라서 특정 사용자가 특정한 문제에 대해 제출한 결과를 필터링할 수 있는 방안이 없다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;* 백준으로 치면 내 제출같은 기능이 필요하다. *&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;사용자 경험 개선을 위해, API에 &lt;b&gt;사용자 ID 및 문제 ID 기반 필터링 로직을 추가&lt;/b&gt;하였다. 이를 통해 현재는 로그인된 사용자의 제출 기록만을 정확하게 조회할 수 있도록 기능을 구현 완료하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2310&quot; data-origin-height=&quot;840&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6RmqB/dJMcaiobpuC/3TLpc80vboVDo0R8yxocpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6RmqB/dJMcaiobpuC/3TLpc80vboVDo0R8yxocpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6RmqB/dJMcaiobpuC/3TLpc80vboVDo0R8yxocpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6RmqB%2FdJMcaiobpuC%2F3TLpc80vboVDo0R8yxocpK%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;659&quot; height=&quot;240&quot; data-origin-width=&quot;2310&quot; data-origin-height=&quot;840&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;문제해결&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;특정 사용자의 특정 문제에 대한 제출 목록을 조회하는 API의 서비스 로직을 정의한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;파라미터로 사용자 아이디 userId와 문제 아이디 problemId를 받는다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;userRepository에서 사용자 아이디를 조회하고, submissionRepository에서 유저와, 문제아이디로 제출결과를 불러오는 메서드를 조회한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763541678110&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    /**
     * 특정 사용자의 특정 문제에 대한 제출 목록 조회
     */
    public List&amp;lt;Submission&amp;gt; getSubmissionsByUserAndProblem(Long userId, Long problemId) {
        logger.info(&quot;사용자별 문제별 제출 목록 조회 요청 - 사용자 ID: {}, 문제 ID: {}&quot;, userId, problemId);
        User user = userRepository.findById(userId).orElse(null);
        if (user == null) {
            logger.warn(&quot;사용자를 찾을 수 없음: {}&quot;, userId);
            return List.of();
        }
        return submissionRepository.findByUserAndProblemIdOrderBySubmittedAtDesc(user, problemId);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1763542553797&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Repository
public interface SubmissionRepository extends JpaRepository&amp;lt;Submission, Long&amp;gt; {
    
    ...  
    /**
     * 사용자의 특정 문제에 대한 제출 기록
     */
    List&amp;lt;Submission&amp;gt; findByUserAndProblemIdOrderBySubmittedAtDesc(User user, Long problemId);
    ...
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;#SubmissionController API&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1763543400497&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; /**
     * 특정 사용자의 특정 문제에 대한 제출 목록 조회 API
     * 
     * @param userId    사용자 ID
     * @param problemId 문제 ID
     * @return 사용자의 특정 문제에 대한 제출 목록
     */
    @GetMapping(&quot;/submissions/user/{userId}/problem/{problemId}&quot;)
    public ResponseEntity&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt; getSubmissionsByUserAndProblem(
            @PathVariable Long userId,
            @PathVariable Long problemId) {

        try {
            logger.info(&quot;사용자별 문제별 제출 목록 조회 요청 - 사용자 ID: {}, 문제 ID: {}&quot;, userId, problemId);

            // * JudgeService에 위임한 getSubmissionsByUserAndProblem 메서드를 통해 사용자 제출 데이털르 가져온다. * 
            List&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt; submissions = judgeService.getSubmissionsByUserAndProblem(userId, problemId)
                    .stream()
                    .map(submission -&amp;gt; {
                        Map&amp;lt;String, Object&amp;gt; submissionData = new HashMap&amp;lt;&amp;gt;();
                        submissionData.put(&quot;id&quot;, submission.getId());
                        submissionData.put(&quot;code&quot;, submission.getCode());
                        submissionData.put(&quot;language&quot;, submission.getLanguage().name());
                        submissionData.put(&quot;status&quot;, submission.getStatus().name());
                        submissionData.put(&quot;submittedAt&quot;, submission.getSubmittedAt());
                        submissionData.put(&quot;problemId&quot;, submission.getProblem().getId());
                        submissionData.put(&quot;problemTitle&quot;, submission.getProblem().getTitle());

                        // * 사용자 정보 추가 * 
                        if (submission.getUser() != null) {
                            submissionData.put(&quot;userId&quot;, submission.getUser().getId());
                            submissionData.put(&quot;nickname&quot;, submission.getUser().getNickname());
                        } else {
                            submissionData.put(&quot;userId&quot;, null);
                            submissionData.put(&quot;nickname&quot;, &quot;익명&quot;);
                        }

                        // * JudgeResult 정보 추가 * 
                        if (submission.getJudgeResult() != null) {
                            submissionData.put(&quot;executionTime&quot;, submission.getJudgeResult().getExecutionTime());
                            submissionData.put(&quot;memoryUsage&quot;, submission.getJudgeResult().getMemoryUsage());
                            submissionData.put(&quot;score&quot;, submission.getJudgeResult().getScore());
                        } else {
                            submissionData.put(&quot;executionTime&quot;, null);
                            submissionData.put(&quot;memoryUsage&quot;, null);
                            submissionData.put(&quot;score&quot;, null);
                        }

                        return submissionData;
                    })
                    .collect(Collectors.toList());

            // * HashpMap에 응답 데이터를 담아서 리턴 * 
            Map&amp;lt;String, Object&amp;gt; response = new HashMap&amp;lt;&amp;gt;();
            response.put(&quot;success&quot;, true);
            response.put(&quot;message&quot;, &quot;사용자 ID &quot; + userId + &quot;의 문제 ID &quot; + problemId + &quot;에 대한 제출 목록을 성공적으로 조회했습니다.&quot;);
            response.put(&quot;submissions&quot;, submissions);
            response.put(&quot;totalCount&quot;, submissions.size());
            logger.info(&quot;사용자별 문제별 제출 목록 조회 완료 - 사용자 ID: {}, 문제 ID: {}, 총 {}개&quot;, userId, problemId, submissions.size());
            return ResponseEntity.ok(response);

        } catch (Exception e) {
            logger.error(&quot;사용자별 문제별 제출 목록 조회 중 오류 발생&quot;, e);

            Map&amp;lt;String, Object&amp;gt; errorResponse = new HashMap&amp;lt;&amp;gt;();
            errorResponse.put(&quot;success&quot;, false);
            errorResponse.put(&quot;message&quot;, &quot;제출 목록 조회 중 오류가 발생했습니다: &quot; + e.getMessage());

            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;#API 테스트&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2114&quot; data-origin-height=&quot;110&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r5GGf/dJMcabWTGak/qKFmMViMvdv4pEo6szaxa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r5GGf/dJMcabWTGak/qKFmMViMvdv4pEo6szaxa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r5GGf/dJMcabWTGak/qKFmMViMvdv4pEo6szaxa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr5GGf%2FdJMcabWTGak%2FqKFmMViMvdv4pEo6szaxa0%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;2114&quot; height=&quot;110&quot; data-origin-width=&quot;2114&quot; data-origin-height=&quot;110&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1763543726191&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;success&quot;: true,
    &quot;submissions&quot;: [
        {
            &quot;executionTime&quot;: 0,
            &quot;score&quot;: 0,
            &quot;code&quot;: &quot;import java.util.*;\npublic class Main {\n  public static void main(String[] args) {\n    Scanner sc = new Scanner(System.in);\n    int a = sc.nextInt();\n    int b = sc.nextInt();\n    System.out.println(a + b);\n  }\n}&quot;,
            &quot;memoryUsage&quot;: 0,
            &quot;nickname&quot;: &quot;&quot;,
            &quot;problemTitle&quot;: &quot;A + B&quot;,
            &quot;language&quot;: &quot;JAVA&quot;,
            &quot;id&quot;: 2,
            &quot;submittedAt&quot;: &quot;2025-11-11T20:23:48.607397&quot;,
            &quot;problemId&quot;: 1,
            &quot;userId&quot;: 2,
            &quot;status&quot;: &quot;COMPILATION_ERROR&quot;
        },
        {
            &quot;executionTime&quot;: 0,
            &quot;score&quot;: 0,
            &quot;code&quot;: &quot;import java.util.Scanner;\npublic class Main {\n  public static void main(String[] args) {\n    Scanner sc = new Scanner(System.in);\n    int a = sc.nextInt();\n    int b = sc.nextInt();\n    System.out.println(a + b);\n  }\n}&quot;,
            &quot;memoryUsage&quot;: 0,
            &quot;nickname&quot;: &quot;&quot;,
            &quot;problemTitle&quot;: &quot;A + B&quot;,
            &quot;language&quot;: &quot;JAVA&quot;,
            &quot;id&quot;: 1,
            &quot;submittedAt&quot;: &quot;2025-11-11T20:23:31.51855&quot;,
            &quot;problemId&quot;: 1,
            &quot;userId&quot;: 2,
            &quot;status&quot;: &quot;COMPILATION_ERROR&quot;
        }
    ],
    &quot;message&quot;: &quot;사용자 ID 2의 문제 ID 1에 대한 제출 목록을 성공적으로 조회했습니다.&quot;,
    &quot;totalCount&quot;: 2
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Archive/동아리 사이트 개발일지</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/381</guid>
      <comments>https://novlog.tistory.com/entry/DevLog-%EB%8F%99%EC%95%84%EB%A6%AC-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-2-%EC%82%AC%EC%9A%A9%EC%9E%90-%EB%AC%B8%EC%A0%9C-%EC%A0%9C%EC%B6%9C-%EC%A1%B0%ED%9A%8C-API-%EA%B5%AC%ED%98%84#entry381comment</comments>
      <pubDate>Wed, 19 Nov 2025 18:19:14 +0900</pubDate>
    </item>
    <item>
      <title>[Devlog] 동아리 사이트 개발일지 (1) 테스트 케이스 로직 수정</title>
      <link>https://novlog.tistory.com/entry/Devlog-%EB%8F%99%EC%95%84%EB%A6%AC-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-1-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BC%80%EC%9D%B4%EC%8A%A4-%EB%A1%9C%EC%A7%81-%EC%88%98%EC%A0%95</link>
      <description>&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;#문제상황&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;현재 API 구조와 테스트케이스 엔터티를 분석한 결과&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; background-color: #333333; color: #dddddd;&quot;&gt;문제 엔터티에 연결되어있는 모든 테스트케이스 엔터티가 화면에 출력되는 상황&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;따라서 내부 테스트 케이스 (내부 채점 로직에서 돌아가는 테스트케이스)와 외부 테스트 케이스 (사용자에게 보여지는 테스트 케이스) 를 구별할 수 있는 방법이 없음.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1580&quot; data-origin-height=&quot;952&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxGyCR/dJMb99LxTMX/bGdu2MMZ9DKfLKkM8W6URk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxGyCR/dJMb99LxTMX/bGdu2MMZ9DKfLKkM8W6URk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxGyCR/dJMb99LxTMX/bGdu2MMZ9DKfLKkM8W6URk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxGyCR%2FdJMb99LxTMX%2FbGdu2MMZ9DKfLKkM8W6URk%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;487&quot; height=&quot;293&quot; data-origin-width=&quot;1580&quot; data-origin-height=&quot;952&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;따라서 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;TestCase Entity에 내부 테스트 케이스 / 외부 테스트 케이스를 구분할 수 있는 isPublic 필드를 추가하고 로직을 수정한 뒤 관련 API를 재정의&lt;/span&gt;한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;TestCase Entity&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;내부/외부 테스트케이스를 구별해줄 isPublic 필드를 추가한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763532797860&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.nimda.cup.judge.entity;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;

import java.time.LocalDateTime;

@Entity
@Table(name = &quot;test_cases&quot;)
@Data
@AllArgsConstructor
public class TestCase {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    // N:1 관계 - 여러 테스트케이스가 하나의 문제에 속함
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = &quot;problem_id&quot;, nullable = false)
    private Problem problem;

    @Column(columnDefinition = &quot;TEXT&quot;, nullable = false)
    private String input; // 입력 데이터 (예: &quot;1 2&quot;)

    @Column(columnDefinition = &quot;TEXT&quot;, nullable = false)
    private String output; // 예상 출력 (예: &quot;3&quot;)

    @Column(name = &quot;is_public&quot;, nullable = false)
    private Boolean isPublic; // 프론트엔드에 공개 여부 (true: 공개, false: 백엔드 전용)

    @Column(name = &quot;created_at&quot;)
    private LocalDateTime createdAt;

    @Column(name = &quot;updated_at&quot;)
    private LocalDateTime updatedAt;

    // 기본 생성자
    public TestCase() {
        LocalDateTime now = LocalDateTime.now();
        this.createdAt = now;
        this.updatedAt = now;
        this.isPublic = false; // 기본값: 백엔드 전용 (기존 데이터 호환성)
    }

    // 생성용 생성자
    public TestCase(Problem problem, String input, String output) {
        this();
        this.problem = problem;
        this.input = input;
        this.output = output;
        this.isPublic = false; // 기본값: 백엔드 전용
    }

    // * 문제 생성 시 테스트케이스를 화면단에 출력 여부를 결정하는 생성자.
    // * isPublic: true -&amp;gt; 화면단에 출력, false -&amp;gt; 내부 테스트케이스
    public TestCase(Problem problem, String input, String output, Boolean isPublic) {
        this();
        this.problem = problem;
        this.input = input;
        this.output = output;
        this.isPublic = isPublic != null ? isPublic : false;
    }

    // 시간 정보 헬퍼 메서드
    public LocalDateTime getCreatedAt() {
        return this.createdAt;
    }

    public LocalDateTime getUpdatedAt() {
        return this.updatedAt;
    }

    @PrePersist
    public void prePersist() {
        LocalDateTime now = LocalDateTime.now();
        if (this.createdAt == null) {
            this.createdAt = now;
        }
        if (this.updatedAt == null) {
            this.updatedAt = now;
        }
    }

    @PreUpdate
    public void preUpdate() {
        this.updatedAt = LocalDateTime.now();
    }

    /**
     * 테스트케이스 실행 결과 검증 메서드
     */
    public boolean isCorrect(String actualOutput) {
        if (actualOutput == null || this.output == null) {
            return false;
        }
        return this.output.trim().equals(actualOutput.trim());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;#TestCase Repository&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;findByProblemId - 문제 ID로 모든 테스트케이스를 조회. (실제 채점 로직에 사용된다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;findByProblemIdAndIsPublicTrue - 테스트케이스의 isPublic 필드가 true인 테스트케이스만 조회. (프론트단에 출력할 테스트케이스 목록을 조회할때 사용된다.)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763532868826&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Repository
public interface TestCaseRepository extends JpaRepository&amp;lt;TestCase, Long&amp;gt; {

    /**
     * 특정 문제의 모든 테스트케이스 조회
     */
    List&amp;lt;TestCase&amp;gt; findByProblemOrderById(Problem problem);

    /**
     * 문제 ID로 테스트케이스 조회 (모든 테스트케이스 - 백엔드 채점용)
     */
    @Query(&quot;SELECT tc FROM TestCase tc WHERE tc.problem.id = :problemId ORDER BY tc.id&quot;)
    List&amp;lt;TestCase&amp;gt; findByProblemId(@Param(&quot;problemId&quot;) Long problemId);

    /**
     * 문제 ID로 공개된 테스트케이스만 조회 (프론트엔드용)
     */
    @Query(&quot;SELECT tc FROM TestCase tc WHERE tc.problem.id = :problemId AND tc.isPublic = true ORDER BY tc.id&quot;)
    List&amp;lt;TestCase&amp;gt; findByProblemIdAndIsPublicTrue(@Param(&quot;problemId&quot;) Long problemId);

    /**
     * 특정 문제의 테스트케이스 개수 조회
     */
    long countByProblem(Problem problem);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;PtoblemCreateDTO nested Class TestCaseDTO 수정&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;TestCaseDTO에 isPublic 필드 추가&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763533215598&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class ProblemCreateDTO {

    @NotBlank(message = &quot;문제 제목을 입력해주세요&quot;)
    @Size(max = 200, message = &quot;문제 제목은 200자를 초과할 수 없습니다&quot;)
    private String title;

    @NotBlank(message = &quot;문제 설명을 입력해주세요&quot;)
    @Size(max = 10000, message = &quot;문제 설명은 10000자를 초과할 수 없습니다&quot;)
    private String description;

    @NotNull(message = &quot;점수를 입력해주세요&quot;)
    @Positive(message = &quot;점수는 양수여야 합니다&quot;)
    private Integer points;

    @NotNull(message = &quot;시간 제한을 입력해주세요&quot;)
    @Positive(message = &quot;시간 제한은 양수여야 합니다&quot;)
    private Integer timeLimit;

    @NotNull(message = &quot;메모리 제한을 입력해주세요&quot;)
    @Positive(message = &quot;메모리 제한은 양수여야 합니다&quot;)
    private Integer memoryLimit;

    @NotNull(message = &quot;난이도를 선택해주세요&quot;)
    private Difficulty difficulty;

    // @NotBlank(message = &quot;프로그래밍 언어를 선택해주세요&quot;)
    private String language; // Optional로 허용

    private List&amp;lt;TestCaseDTO&amp;gt; testCases;

    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    public static class TestCaseDTO {
        @NotBlank(message = &quot;테스트 케이스 입력을 입력해주세요&quot;)
        private String input;

        @NotBlank(message = &quot;테스트 케이스 출력을 입력해주세요&quot;)
        private String output;

        private Boolean isPublic; // 프론트엔드에 공개 여부 (true: 공개, false: 백엔드 전용, null이면 기본값 false)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;#ProblemService&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;문제의 테스트케이스를 만드는 로직에 isPublic을 설정하는 필드를 추가&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763533553106&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        // 4. TestCase 생성 및 저장
        if (problemCreateDTO.getTestCases() != null &amp;amp;&amp;amp; !problemCreateDTO.getTestCases().isEmpty()) {
            for (ProblemCreateDTO.TestCaseDTO testCaseDTO : problemCreateDTO.getTestCases()) {
                TestCase testCase = new TestCase();
                testCase.setProblem(problem);
                testCase.setInput(testCaseDTO.getInput());
                testCase.setOutput(testCaseDTO.getOutput());
                // isPublic 설정 (null이면 기본값 false)
                testCase.setIsPublic(testCaseDTO.getIsPublic() != null ? testCaseDTO.getIsPublic() : false);
                testCaseRepository.save(testCase);
            }
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;#ProblemController&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;ID로 문제 조회하는 API수정&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;기존 구조는 모든 테스트케이스를 담아서 반환했으나 Map에 담아서 공개 테스트케이스만 반환하도록 로직을 수정한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763534367122&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
     * ID로 문제 조회 (공개된 테스트케이스만 포함)
     */
    @GetMapping(&quot;/{id}&quot;)
    public ResponseEntity&amp;lt;?&amp;gt; getProblemById(@PathVariable Long id) {
        try {
            Problem problem = problemService.getProblemById(id);

            // 공개된 테스트케이스만 조회 (프론트엔드용)
            List&amp;lt;TestCase&amp;gt; publicTestCases = testCaseRepository.findByProblemIdAndIsPublicTrue(id);

            // 테스트케이스를 Map으로 변환 (isPublic 필드는 제외)
            List&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt; testCaseList = publicTestCases.stream()
                    .map(tc -&amp;gt; {
                        Map&amp;lt;String, Object&amp;gt; tcMap = new HashMap&amp;lt;&amp;gt;();
                        tcMap.put(&quot;id&quot;, tc.getId());
                        tcMap.put(&quot;input&quot;, tc.getInput());
                        tcMap.put(&quot;output&quot;, tc.getOutput());
                        return tcMap;
                    })
                    .collect(Collectors.toList());

            Map&amp;lt;String, Object&amp;gt; response = new HashMap&amp;lt;&amp;gt;();
            response.put(&quot;success&quot;, true);
            response.put(&quot;problem&quot;, problem);
            response.put(&quot;testCases&quot;, testCaseList); // 공개된 테스트케이스만 포함

            return ResponseEntity.ok(response);
        } catch (RuntimeException e) {
            Map&amp;lt;String, Object&amp;gt; error = new HashMap&amp;lt;&amp;gt;();
            error.put(&quot;success&quot;, false);
            error.put(&quot;message&quot;, e.getMessage());
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
        } catch (Exception e) {
            Map&amp;lt;String, Object&amp;gt; error = new HashMap&amp;lt;&amp;gt;();
            error.put(&quot;success&quot;, false);
            error.put(&quot;message&quot;, &quot;문제 조회 중 오류가 발생했습니다: &quot; + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;#Postman Test&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1550&quot; data-origin-height=&quot;84&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mxnxB/dJMcahbJ2o3/Ph6TgcjMi7bWLdV7Vl8sZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mxnxB/dJMcahbJ2o3/Ph6TgcjMi7bWLdV7Vl8sZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mxnxB/dJMcahbJ2o3/Ph6TgcjMi7bWLdV7Vl8sZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmxnxB%2FdJMcahbJ2o3%2FPh6TgcjMi7bWLdV7Vl8sZ1%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;84&quot; data-origin-width=&quot;1550&quot; data-origin-height=&quot;84&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;588&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bK8m57/dJMcahJz2m7/E8eh9Ypk5S5gh5Q0uFCfe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bK8m57/dJMcahJz2m7/E8eh9Ypk5S5gh5Q0uFCfe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bK8m57/dJMcahJz2m7/E8eh9Ypk5S5gh5Q0uFCfe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbK8m57%2FdJMcahJz2m7%2FE8eh9Ypk5S5gh5Q0uFCfe0%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;459&quot; height=&quot;378&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;588&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트단에서도 공개/비공개 테스트케이스를 구분하는 체크박스 추가&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1596&quot; data-origin-height=&quot;814&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1etyj/dJMcahphiDx/yR3JAkOTd5L9p9lGSuYRc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1etyj/dJMcahphiDx/yR3JAkOTd5L9p9lGSuYRc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1etyj/dJMcahphiDx/yR3JAkOTd5L9p9lGSuYRc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1etyj%2FdJMcahphiDx%2FyR3JAkOTd5L9p9lGSuYRc0%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;637&quot; height=&quot;325&quot; data-origin-width=&quot;1596&quot; data-origin-height=&quot;814&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Archive/동아리 사이트 개발일지</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/380</guid>
      <comments>https://novlog.tistory.com/entry/Devlog-%EB%8F%99%EC%95%84%EB%A6%AC-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-1-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BC%80%EC%9D%B4%EC%8A%A4-%EB%A1%9C%EC%A7%81-%EC%88%98%EC%A0%95#entry380comment</comments>
      <pubDate>Wed, 19 Nov 2025 16:06:35 +0900</pubDate>
    </item>
    <item>
      <title>[DevLog] 동아리 사이트 개발 일지</title>
      <link>https://novlog.tistory.com/entry/DevLog-%EB%8F%99%EC%95%84%EB%A6%AC-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EA%B0%9C%EB%B0%9C-%EC%9D%BC%EC%A7%80</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트 기간 2025.09.01 ~ 진행중&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/novvvv/NimdaPage&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/novvvv/NimdaPage&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1763532477488&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - novvvv/NimdaPage: Nimda Security Web Page Team Project&quot; data-og-description=&quot;Nimda Security Web Page Team Project . Contribute to novvvv/NimdaPage development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/novvvv/NimdaPage&quot; data-og-url=&quot;https://github.com/novvvv/NimdaPage&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bO9nvW/hyZN8XhHim/tTuSkUAujyoZvAK7lisRx0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bpYMxN/hyZNYaVnEB/liMS2KgDTl6ncC1Z4jK0bK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/novvvv/NimdaPage&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/novvvv/NimdaPage&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bO9nvW/hyZN8XhHim/tTuSkUAujyoZvAK7lisRx0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bpYMxN/hyZNYaVnEB/liMS2KgDTl6ncC1Z4jK0bK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - novvvv/NimdaPage: Nimda Security Web Page Team Project&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Nimda Security Web Page Team Project . Contribute to novvvv/NimdaPage development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;목차&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;vol1. &lt;a href=&quot;https://novlog.tistory.com/entry/Devlog-%EB%8F%99%EC%95%84%EB%A6%AC-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-1-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BC%80%EC%9D%B4%EC%8A%A4-%EB%A1%9C%EC%A7%81-%EC%88%98%EC%A0%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;테스트케이스 엔터티 로직 수정&lt;/a&gt;&lt;/p&gt;</description>
      <category>Archive/동아리 사이트 개발일지</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/379</guid>
      <comments>https://novlog.tistory.com/entry/DevLog-%EB%8F%99%EC%95%84%EB%A6%AC-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EA%B0%9C%EB%B0%9C-%EC%9D%BC%EC%A7%80#entry379comment</comments>
      <pubDate>Wed, 19 Nov 2025 15:09:15 +0900</pubDate>
    </item>
    <item>
      <title>[Android] Activity, Fragment, Inflater</title>
      <link>https://novlog.tistory.com/entry/Android-Activity-Fragment-Inflater</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-09 03.30.56.png&quot; data-origin-width=&quot;324&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xRuSg/dJMcacIcqZW/y0upo9E8Cq4yPQgWw6yBp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xRuSg/dJMcacIcqZW/y0upo9E8Cq4yPQgWw6yBp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xRuSg/dJMcacIcqZW/y0upo9E8Cq4yPQgWw6yBp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxRuSg%2FdJMcacIcqZW%2Fy0upo9E8Cq4yPQgWw6yBp0%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;249&quot; height=&quot;244&quot; data-filename=&quot;스크린샷 2025-11-09 03.30.56.png&quot; data-origin-width=&quot;324&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;개인적인 학습 용도로 정리한 글이기에 잘못된 내용을 포함하고 있을 수 있습니다. &lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Inflater란?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;xml로 정의한 레이아웃을 메모리에 올리고 객체화시키는 함수&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;모든 xml 레이아웃은 Inflater를 통해 객체화된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Activity With Inflater&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Activity로 화면을 생성할 경우 Java/Kotlin 소스코드와 xml 레이아웃 파일 2개가 생성된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;나는 Activity로 화면을 생성했을 때 Inflater를 호출한 적이 없는데?라고 생각할 수 있으나&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Activity에서 onCreate() 메서드 내부의 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;setContentView() 메서드가 내부 생명주기에서 자동으로 Inflater를 호출&lt;/span&gt;해 주기에 inflater를 사용하지 않는 것처럼 보이는 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1762625977196&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main); // inflate

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Fragment With Inflater&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Fragment 방식으로 화면을 생성하는 경우도 마찬가지다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;onCreateView 함수 내부에서 inflater.inflate를 호출&lt;/span&gt;해 xml 레이아웃을 메모리에 로딩한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1762626048621&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class WordListFragment extends Fragment {

	...
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater,
            @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        // fragment_word_list.xml 레이아웃을 View 객체로 변환해서 반환
        return inflater.inflate(R.layout.fragment_word_list, container, false);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;즉, &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;어떤 방식으로 xml 화면을 구성하던지 반드시 Inflate로 xml 레이아웃을 메모리에 로딩해야 화면을 띄울 수 있다&lt;/span&gt;는 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Dynamic Inflater&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Inflate 메서드를 사용하면 동적으로 xml을 원하는 시점에 출력할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; background-color: #333333; color: #dddddd;&quot;&gt;inflate(레이아웃 파일 id, 생성될 뷰의 parent, 생성되는 View를 자동으로 자식에 추가할지 여부 [true/false])&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;만약 Activity에 딸린 Fragment 내부에서 xml을 inflate로 동적으로 호출하고 싶다면 아래와 같이 Fragment의 부모 Acitvity 정보를 getContenxt()로 가져와야 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;다음은 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;프래그먼트 내부에서 inflate를 사용해 Dialog를 호출하는 예제&lt;/span&gt;이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1762626180567&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; // dialogVIew는 dialog_create_word_list.xml 파일을 인플레이트하여 생성
        // inflate : XML Layout을 실제 View 객체로 변환하는 메서드
        LayoutInflater inflater = LayoutInflater.from(requireContext()); // 현재 프래그먼트가 붙어있는 액티비티의 인플레이터를 받아온다.
        View dialogView = inflater.inflate(R.layout.dialog_create_word_list, null, false); // 다이얼로그를 호춯한다.&lt;/code&gt;&lt;/pre&gt;</description>
      <category>dev/Android</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/377</guid>
      <comments>https://novlog.tistory.com/entry/Android-Activity-Fragment-Inflater#entry377comment</comments>
      <pubDate>Sun, 9 Nov 2025 03:33:14 +0900</pubDate>
    </item>
    <item>
      <title>JWT</title>
      <link>https://novlog.tistory.com/entry/JWT</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-10-31 14.14.59.png&quot; data-origin-width=&quot;190&quot; data-origin-height=&quot;200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crEhUs/dJMcaj1Ap1l/eMSce3gCukMkLErOCx94k0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crEhUs/dJMcaj1Ap1l/eMSce3gCukMkLErOCx94k0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crEhUs/dJMcaj1Ap1l/eMSce3gCukMkLErOCx94k0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrEhUs%2FdJMcaj1Ap1l%2FeMSce3gCukMkLErOCx94k0%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;190&quot; height=&quot;200&quot; data-filename=&quot;스크린샷 2025-10-31 14.14.59.png&quot; data-origin-width=&quot;190&quot; data-origin-height=&quot;200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;#JWT란?&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;정보를 안전하게 전송하기 위한 표준 규격&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;서버가 상태를 저장하지 않는 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;Stateless 방식&lt;/span&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;서명 정보(Signature)를 통해 정보가 위조되지 않았음을 보장&lt;/span&gt;한다. (무결성 보장)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;JWT의 구조&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;JWT는 dot(.)으로 구분된 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;세 가지 영역 (Header, Payload, Signature)&lt;/span&gt; 으로 나눈다&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;Header&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&amp;rarr; 어떤 알고리즘으로 서명했는지, 토큰 타입은 무엇인지 등 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;메타 정보를 담는다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;ex) { &amp;ldquo;alg&amp;rdquo; : &amp;ldquo;HS256&amp;rdquo;, &amp;ldquo;typ&amp;rdquo;: &amp;ldquo;JWT&amp;rdquo; }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;JSON객체(헤더)를 Base64Url로 인코딩하여 xxxxx를 만든다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;Payload&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&amp;rarr; 서버와 클라이언트가 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;주고받고자 하는 &amp;ldquo;실제 정보&amp;rdquo;&lt;/span&gt;를 담는다. 이 정보를 클레임(Claim)이라고 부른다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;등록된 클레임 (Registered Claims)&lt;/span&gt; &amp;rarr; 표준으로 권장되는 정보들 (iss - 발행자, exp - 만료시간, sub- 주제 등)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;개인 클레임 (Private Claims)&lt;/span&gt; &amp;rarr; userId, userName 같이 서비스에서 사용자가 직접 정의해서 사용하는 정보. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;주의점&lt;/b&gt; : Base64는 암호화(Encryption)가 아닌 인코딩(Encoding)이다. 그렇기에 정해진 규칙(Base64)에 따라서 문자열로 바꾼 것이라 누구나 쉽게 원본(JSON)으로 디코딩이 가능하다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;따라서 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;절대로 Payload 부분에는 비밀번호 같은 민감한 정보를 넣으면 안 된다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;Signature&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&amp;rarr; 토큰의 무결성(위조 여부)을 검증한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;생성 방법 (HS256)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;1. Base64url로 인코딩된 Header 정보 (xxxxx)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;2. Base64Url로 인코딩된 Payload (yyyyy)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;3. 서버가 가지고 있는 Secret Key&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;위 3가지를 합쳐서 Header에서 명시한 HS256 알고리즘으로 암호화하여 생성한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;JWT 회원가입/로그인 로직&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;회원가입&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;[클라이언트]&lt;/b&gt; 아이디(nov), 비밀번호(1234)를 입력한 뒤 &quot;회원가입을 요청한다&quot; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;[서버]&lt;/b&gt;&amp;nbsp;비밀번호(1234)를 복호화가 불가능한 해시함수 (bycrpt 등)로 해싱하여 해시값($21212@sada... x)을 DB에 아이디와 함께 저장한다. 여기서 원본은 바로 버린다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;JWT 기반 로그인&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;[클라이언트]&lt;/b&gt; 아이디(nov), 비밀번호(1234)를 입력한 뒤 &quot;로그인&quot;을 요청한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;[서버]&lt;/b&gt; 아이디(nov)를 DB에서 찾아서 해시값을 꺼내온다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;[서버]&lt;/b&gt; 클라이언트가 보낸 비밀번호(1234)를 같은 해시함수 bcrypt로 다시 헤싱하고, 방금 계산한 해시값과 DB에 저장되어 있던 해시값이 서로 일치하는지 비교한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;(동일한 해시 함수를 사용해 같은 값(1234)를 해싱하면 결과물도 동일하기 때문)&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;해시값이 일치하면 서버는 &lt;b&gt;&lt;u&gt;비밀번호 원본(1234)는 바로 버리고, 사용자가 본인임이&lt;/u&gt; 확인되었으니 JWT를 발급&lt;/b&gt;해 준다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;이후 로그인 요청&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;[클라이언트]&lt;/b&gt; 서버로부터 받은 JWT 토큰을 웹 브라우저의&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt; localStorage&lt;/span&gt; 같은 저장소에 저장한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;[클라이언트]&lt;/b&gt; 인증이 필요한 페이지를 요청할때 보통 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;HTTP의 Authorization 헤더&lt;/span&gt;에 발급받은 JWT 토큰을 담아 전송한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1761886756082&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /api/user/me HTTP/1.1
Host: api.example.com
Authorization: Bearer xxxxx.yyyyy.zzzzz&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;[서버]&lt;/b&gt; Authorization 헤더를 보고 토큰을 꺼내 서명을 검증하고 요청을 처리해 준다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;#JWT 서버는 기밀성을 보장하지 않는다.&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;페이로드는 단순히 Base64로 인코딩된 값이기에 누구나 디코딩해서 내용을 볼 수 있다. 따라서 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;JWT방식은 기밀성 (Confidentiality)을 보장하지는 않는다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;서버는 비밀키 하나로 토큰의 내용이 (Payload)가 서버가 발급한 원본 그대로인지 확인하는 역할만을 수행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;해커가 특정 유저의 Paylaod를 탈취해서 관리자 권한 페이지를 요청한다고 가정해보자.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;해커는 Payload는 변조할 수 있으나 서버 안의 비밀키(Secret Key)는 변조할 수 없기에 JWT의 Signature는 바꾸지 못한다.&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;따라서 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;서명(Signature)을 통해 내용(Payload)이 발급된 이후 값이 위조되거나 변경되지 않았음을 보장하기에 무결성&lt;/span&gt;을 보장한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;관련 포스팅&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;a href=&quot;https://novlog.tistory.com/entry/Session&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://novlog.tistory.com/entry/Session&lt;/a&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1761887673649&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Session&quot; data-og-description=&quot;Session 기반 인증 방식이란?사용자의 상태 정보(로그인 여부 등)를 서버에 저장하는 Stateful 상태 유지 방식이다.세션은 클라이언트가 서버에 요청을 한 시점이 언제인지에 따라서 동작이 달라진&quot; data-og-host=&quot;novlog.tistory.com&quot; data-og-source-url=&quot;https://novlog.tistory.com/entry/Session&quot; data-og-url=&quot;https://novlog.tistory.com/entry/Session&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dcbf4F/hyZMvE4BJH/Kpe3vEI9NztI7xmlnFeSPk/img.png?width=152&amp;amp;height=150&amp;amp;face=0_0_152_150,https://scrap.kakaocdn.net/dn/NArr3/hyZMQW3xtL/O52nV3pghE2V4vI1CapwQ1/img.png?width=152&amp;amp;height=150&amp;amp;face=0_0_152_150,https://scrap.kakaocdn.net/dn/bYg53i/hyZMVD3Yw0/ZNkBsYShKoFdLkjKkrBmH1/img.png?width=1156&amp;amp;height=1064&amp;amp;face=0_0_1156_1064&quot;&gt;&lt;a href=&quot;https://novlog.tistory.com/entry/Session&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://novlog.tistory.com/entry/Session&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dcbf4F/hyZMvE4BJH/Kpe3vEI9NztI7xmlnFeSPk/img.png?width=152&amp;amp;height=150&amp;amp;face=0_0_152_150,https://scrap.kakaocdn.net/dn/NArr3/hyZMQW3xtL/O52nV3pghE2V4vI1CapwQ1/img.png?width=152&amp;amp;height=150&amp;amp;face=0_0_152_150,https://scrap.kakaocdn.net/dn/bYg53i/hyZMVD3Yw0/ZNkBsYShKoFdLkjKkrBmH1/img.png?width=1156&amp;amp;height=1064&amp;amp;face=0_0_1156_1064');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Session&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Session 기반 인증 방식이란?사용자의 상태 정보(로그인 여부 등)를 서버에 저장하는 Stateful 상태 유지 방식이다.세션은 클라이언트가 서버에 요청을 한 시점이 언제인지에 따라서 동작이 달라진&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;novlog.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>dev/Computer Science</category>
      <category>ㅏ</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/375</guid>
      <comments>https://novlog.tistory.com/entry/JWT#entry375comment</comments>
      <pubDate>Fri, 31 Oct 2025 14:16:34 +0900</pubDate>
    </item>
    <item>
      <title>[Cpp] vector max_element, min_element, distance</title>
      <link>https://novlog.tistory.com/entry/Cpp-vector-maxelement-minelement-distance</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-10-30 12.07.07.png&quot; data-origin-width=&quot;188&quot; data-origin-height=&quot;188&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d2ylRA/dJMcaiVUcKa/kUMTj09hXcCUnuDYcY5U21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d2ylRA/dJMcaiVUcKa/kUMTj09hXcCUnuDYcY5U21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d2ylRA/dJMcaiVUcKa/kUMTj09hXcCUnuDYcY5U21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd2ylRA%2FdJMcaiVUcKa%2FkUMTj09hXcCUnuDYcY5U21%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;151&quot; height=&quot;151&quot; data-filename=&quot;스크린샷 2025-10-30 12.07.07.png&quot; data-origin-width=&quot;188&quot; data-origin-height=&quot;188&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;* Cpp STL max_element, min_element의 사용법과&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;distance method를 사용해 벡터 내부의 최대/최소 원소의 인덱스를 구하는 방법에 대해 소개합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;#max_element / min element&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; background-color: #333333; color: #dddddd;&quot;&gt;usage &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;max_element(vector_name.begin(), vector_name.end())&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;min_element(vector_name.begin(), vector_name.end())&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;lt;algorithm&amp;gt; Header에 선언되어 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;벡터 내부의 최대/최소 원소의 첫 번째 위치를 &quot;이터레이터&quot; 형태로 반환&lt;/span&gt;한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;반환타입이 &quot;값&quot;이 아닌 &quot;이터레이터&quot; 이기에 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;* 연산자를 사용해 값에 접근&lt;/span&gt;해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1761793049451&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;vector&amp;gt;
using namespace std;

int main() {
    
    vector&amp;lt;int&amp;gt; novs = {1, 2, 2, 5, 5, 3};
    
    auto max_it = max_element(novs.begin(), novs.end());
    auto min_it = min_element(novs.begin(), novs.end());
    
    cout &amp;lt;&amp;lt; &quot;max_element : &quot; &amp;lt;&amp;lt; *max_it &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; &quot;min_element : &quot; &amp;lt;&amp;lt; *min_it &amp;lt;&amp;lt; endl;
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1761793058929&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;max_element : 5
min_element : 1&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;#distance&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; background-color: #333333; color: #dddddd;&quot;&gt;usage&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;distance(first, last)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;&amp;lt;iterator&amp;gt;&lt;/span&gt; 내부에 선언되어 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;두 이터레이터 사이의 거리를 계산하는 함수로 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;first 위치에서 last 위치까지 얼마나 떨어져 있는지 확인&lt;/span&gt;할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;앞서 소개한 max/min_element를 last parameter에 탐색할 벡터의 시작점 (begin())을 first에 넣어주면 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;벡터 내부의 최대/최소 위치의 인덱스를 탐색&lt;/span&gt;할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1761793457356&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;iterator&amp;gt;
using namespace std;

int main() {
    
    vector&amp;lt;int&amp;gt; novs = {1, 2, 2, 5, 5, 3};
    
    auto max_it = max_element(novs.begin(), novs.end());
    auto min_it = min_element(novs.begin(), novs.end());
    
    cout &amp;lt;&amp;lt; &quot;max_idx : &quot; &amp;lt;&amp;lt; distance(novs.begin(), max_it) &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; &quot;min_idx : &quot; &amp;lt;&amp;lt; distance(novs.begin(), min_it) &amp;lt;&amp;lt; endl;
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1761793486830&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;max_idx : 3
min_idx : 0&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm/PS With C++</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/374</guid>
      <comments>https://novlog.tistory.com/entry/Cpp-vector-maxelement-minelement-distance#entry374comment</comments>
      <pubDate>Thu, 30 Oct 2025 12:08:35 +0900</pubDate>
    </item>
    <item>
      <title>Session</title>
      <link>https://novlog.tistory.com/entry/Session</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;Session 기반 인증 방식이란?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;사용자의 상태 정보(로그인 여부 등)를 서버에 저장하는 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;Stateful &lt;/span&gt;상태 유지 방식이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;세션은 클라이언트가 서버에 요청을 한 시점이 언제인지에 따라서 동작이 달라진다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;가장 흔하게 사용되는 로그인 동작을 기반으로 &quot;세션&quot;의 동작에 대해 소개한다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;세션 동작 방식&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #dddddd; background-color: #333333;&quot;&gt;최초 요청 (로그인)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&amp;ldquo;클라이언트&amp;rdquo; &amp;rarr; 로그인 정보를 서버에 전달 (ID: User, Pw: 1234)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&amp;ldquo;서버&amp;rdquo; &amp;rarr; DB에서 아이디/패스워드를 검증 후&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt; User 전용 &amp;ldquo;세션 저장소&amp;rdquo;를 생성. &lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;해당 저장소에는 &lt;b&gt;이 사람은 User이고 &amp;ldquo;관리자&amp;rdquo; 권한이 있다.. 등의 정보를 저장&lt;/b&gt;함&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&amp;ldquo;서버&amp;rdquo; &amp;rarr; 클라이언트에 반환할때 header의 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;Set-Cookie에 session-ID를 담아서 반환&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&amp;ldquo;클라이언트&amp;rdquo; &amp;rarr; 응답 헤더의 Set-Cookie를 보고 Session-ID(1234)를 쿠키 저장소에 보관&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #dddddd; background-color: #333333;&quot;&gt;두 번째 이후 요청 (인증)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&amp;ldquo;클라이언트&amp;rdquo; &amp;rarr; &amp;ldquo;마이페이지&amp;rdquo;와 같은 인증이 필요한 페이지 리소스를 요청. 이 때 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;로그인 ID/PW가 아닌 Session ID를 전송&lt;/span&gt; (두 번째 부터 클라이언트는 아이디, 비밀번호가 아닌 세션 아이디를 전송. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;왜냐하면 로그인한지 10초도 안됐는데 인증이 필요한 페이지에 들어갈때마다 서버가 아이디, 비밀번호를 요청한다면 서버도 그만큼의 비용이 발생하고, 클라이언트 또한 매우 불편하기 때문)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&amp;ldquo;서버&amp;rdquo; &amp;rarr; 클라이언트 측에서 전달한 세션 아이디를 기반으로 사용자 검증&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&amp;ldquo;서버&amp;rdquo; &amp;rarr; 마이페이지 HTML 반환&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;세션은 언제 사라지는가?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #dddddd; background-color: #333333;&quot;&gt;#1 사용자가 로그아웃 했을 때&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&amp;rarr; 사용자가 로그아웃 버튼을 누르면 서버는 세션 저장소에서 사용자의 세션 데이터를 제거&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #dddddd; background-color: #333333;&quot;&gt;#2 타임 아웃&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&amp;rarr; 아무런 요청을 하지 않고 보통 30분이 지나면 서버는 세션 저장소에서 해당 데이터를 자동 삭제&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #dddddd; background-color: #333333;&quot;&gt;#3 브라우저 종료&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&amp;rarr; 이는 세션 쿠키 방식에 따라서 달라지는데, 만약 서버가 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;만료시간 (Expires, Max-Age) 을 설정하지 않으면 해당 쿠키는 &amp;lsquo;세션 쿠키&amp;rsquo;가 된다.&lt;/span&gt; 이 쿠키는 브라우저가 닫힐 때 사라짐&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;만약 서버가 해당 쿠키를 &amp;ldquo;한 달간 로그인 유지&amp;rdquo;처럼 길게 설정하면 영구 쿠키 (Persistent Cookie) 가 되어 브라우저를 닫았다가 켜도 남아있음.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;세션 쿠키 (Session Cookie)는 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;&quot;로그인 상태 유지&quot; 혹은 &quot;임시 데이터&quot; 등에 사용되어&lt;/span&gt; 브라우저 메모리에 저장되고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;영구 쿠키 (Persistent Cookie) 또한 다양한 상황에서 사용되며 주로 웹 사이트의 &quot;ID저장&quot; 기능이나 &quot;오늘 하루 팝업 보지 않기&quot; 같은 기능에 사용된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;세션 방식의 단점&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;여러 대의 서버 (WAS1, WAS2, &amp;hellip;) 가 있을 때는 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;세션 불일치 (Session Consitency) 문제&lt;/span&gt;가 발생한다. 만약 사용자가 WAS1에서 로그인하고 세션을 발급 받았는데 로드 밸런서를 통해 WAS2로 요청이 전달된다면 WAS2에는 해당 사용자의 세션 정보가 없기에 &amp;ldquo;로그인되지 않은 사용자&amp;rdquo;로 분류할 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;해결 방법&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #dddddd; background-color: #333333;&quot;&gt;#1 Sticky Session&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;로드 밸런서에게 이 사용자는 WAS1으로만 보내라고 설정하는 방식&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;b&gt;단점&lt;/b&gt; : WAS1이 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #dddddd; background-color: #333333;&quot;&gt;#2 Session Clustering&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;모든 WAS 서버에 실시간으로 세션 정보를 복제, 공유하는 방식&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;b&gt;단점&lt;/b&gt; : 서버가 많아질수록 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #dddddd; background-color: #333333;&quot;&gt;#3 Session Stroage 분리&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;현대 프로그래밍에서 가장 많이 채택하는 방식으로, 모든 WAS가 공유하는 별도의 세션 저장소를 둔다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;b&gt;단점 (HDD/RDBMS를 사용하는 경우)&lt;/b&gt; : 모든 요청마다 DB에 접근하면 Disk I/O (물리적으로 회전하고 헤더가 움직여야 하는 기계적 디스크 I/O) 가 발생해 성능이 저하된다.&lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt; 따라서 공유 세션 저장소는 하드디스크가 아닌 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;메모리 기반 데이터베이스 (RAM 기반) 를 사용&lt;/span&gt;해 순차 접근이 아닌 랜덤 메모리 접근으로 전기적 신호로 메모리에 접근하도록 설정해 I/O비용을 낮춘다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;이러한 대표적인 RAM 기반 데이터베이스로 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;Redis&lt;/span&gt;가 존재한다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>dev/Computer Science</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/373</guid>
      <comments>https://novlog.tistory.com/entry/Session#entry373comment</comments>
      <pubDate>Wed, 29 Oct 2025 14:23:42 +0900</pubDate>
    </item>
    <item>
      <title>[Unix] Mac OS AWS EC2 SSH 접근 방법</title>
      <link>https://novlog.tistory.com/entry/Unix-Mac-OS-AWS-EC2-SSH-%EC%A0%91%EA%B7%BC-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;232&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUet6R/btsQ4B2GyyX/5DXKjn5T7cGNYHYRzJUX3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUet6R/btsQ4B2GyyX/5DXKjn5T7cGNYHYRzJUX3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUet6R/btsQ4B2GyyX/5DXKjn5T7cGNYHYRzJUX3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUet6R%2FbtsQ4B2GyyX%2F5DXKjn5T7cGNYHYRzJUX3k%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;106&quot; height=&quot;114&quot; data-origin-width=&quot;232&quot; data-origin-height=&quot;250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EC2 컨테이너 생성시에 받아둔 .pem 확장자 키페어 파일이 위치한 경로로 이동한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760006658260&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(base) % cd nimda_key
(base) % ls
nimda-server-key-pair.pem&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안을 위해 읽기 권한을 부여한다.&lt;/p&gt;
&lt;pre id=&quot;code_1760006762458&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;chmod 400 [파일경로]/[키체인이름].pem&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;ssh 명령어를 통해 아래와 같은 형식으로 접근하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Are you sure you wna to continue ... 라는 처음 접근하는 아이피 주소에 대한 보안 경고 메시지가 뜨는데 yes를 입력한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760007117258&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ssh -i [키페어_주소] [AWS_사용자명]@[EC2주소]
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes&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;</description>
      <category>Archive/Linux</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/372</guid>
      <comments>https://novlog.tistory.com/entry/Unix-Mac-OS-AWS-EC2-SSH-%EC%A0%91%EA%B7%BC-%EB%B0%A9%EB%B2%95#entry372comment</comments>
      <pubDate>Thu, 9 Oct 2025 19:54:24 +0900</pubDate>
    </item>
    <item>
      <title>[Git] rejected non-fast-forward</title>
      <link>https://novlog.tistory.com/entry/Git-rejected-non-fast-forward</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BjWCl/btsQ3gdSlbj/Z6PKQKXjSOAGROUeISp9pK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BjWCl/btsQ3gdSlbj/Z6PKQKXjSOAGROUeISp9pK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BjWCl/btsQ3gdSlbj/Z6PKQKXjSOAGROUeISp9pK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBjWCl%2FbtsQ3gdSlbj%2FZ6PKQKXjSOAGROUeISp9pK%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;616&quot; height=&quot;278&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;278&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;문제상황 : 로컬 be branch에서 원격의 main branch로 push 하려고 했을 때 다음과 같은 에러가 발생했다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759943615396&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;! [rejected]        main -&amp;gt; main (non-fast-forward)
error: failed to push some refs to 'https://github.com/username/repository.git'
hint: Updates were rejected because a pushed branch tip is behind its remote
hint: counterpart. If you want to integrate the remote changes, use 'git pull'
hint: before pushing again.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;문제 발생 이유 : 원격의 main branch가 로컬의 main branch보다 앞서 있는 상태이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;따라서&lt;u&gt;&lt;b&gt; local main이 outdated(구버전) 상태로 fast-forward merge가 불가능&lt;/b&gt;&lt;/u&gt;하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;해결방안 1. --allow-unrelated-histories&lt;/span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;완전히 별개의 두 Git History를 병합하고자 할 때 사용한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;하지만 현재 상황은 같은 프로젝트의 같은 브랜치로 공통 조상이 존재한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;따라서 Git History가 지저분해지기에 적절하지 않다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; background-color: #333333; color: #dddddd;&quot;&gt;해결방안 2. git push -f&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;원격의 최신 커밋들을 강제로 덮어쓴다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;혼자 작업하는 프로젝트라면 상관없겠지만 협업 중인 프로젝트이기에 적절하지 않다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; background-color: #333333; color: #dddddd;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; text-align: start;&quot;&gt;✨ &lt;/span&gt;해결방안 3. main branch로 이동 후 병합한다. ✨&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759943303643&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git checkout main #main 브랜치로 전환
git pull origin main #원격의 main 최신 내용 가져오기
git merge be # be 브랜치의 변경사항을 main에 병합한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;충돌되는 코드가 있다면 해결 후 병합한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;239&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pCD9X/btsQ2SK7LtL/jOgLKTPnSif7Jfctx1wTH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pCD9X/btsQ2SK7LtL/jOgLKTPnSif7Jfctx1wTH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pCD9X/btsQ2SK7LtL/jOgLKTPnSif7Jfctx1wTH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpCD9X%2FbtsQ2SK7LtL%2FjOgLKTPnSif7Jfctx1wTH0%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;1155&quot; height=&quot;239&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;239&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1546&quot; data-origin-height=&quot;140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cls4GJ/btsQ2uDH13r/5smMegtBJPLnQLrBXkbbMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cls4GJ/btsQ2uDH13r/5smMegtBJPLnQLrBXkbbMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cls4GJ/btsQ2uDH13r/5smMegtBJPLnQLrBXkbbMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcls4GJ%2FbtsQ2uDH13r%2F5smMegtBJPLnQLrBXkbbMK%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;1546&quot; height=&quot;140&quot; data-origin-width=&quot;1546&quot; data-origin-height=&quot;140&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;마지막으로 원격 브랜치에 푸시한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759943502156&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git push origin main&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;결론 : 커밋하기 전에 항상 원격 브랜치를 pull한뒤 작업하자.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Archive/Git</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/371</guid>
      <comments>https://novlog.tistory.com/entry/Git-rejected-non-fast-forward#entry371comment</comments>
      <pubDate>Thu, 9 Oct 2025 02:21:08 +0900</pubDate>
    </item>
    <item>
      <title>[Algorithm] Union Find</title>
      <link>https://novlog.tistory.com/entry/Algorithm-Union-Find</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;#1 About Union-Find&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;유니온 파인드는 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;여러 개의 노드가 주어졌을 때 특정한 노드들이 같은 그룹에 속해있는지 판별하&lt;/span&gt;고 여러 개의 그룹 노드를 하나로 합치는 작업을 수행할 때 사용되는 알고리즘이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이름에서 유추 가능하듯 노드를 한 그룹으로 합치는 &quot;유니온(Union)&quot;연산과 노드가 특정 그룹 내에 존재하는지 판별하는 &quot;파인드(Find)&quot;연산으로 이루어진다.&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;Union-Find 알고리즘을 직접 구현해 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;우선 크기가 5인 배열을 모두 -1로 초기화해준다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;각 배열의 원소값은 해당 인덱스의 부모 노드를 가리킨다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;배열의 원소가 -1이라면 부모 노드가 자기 자신 즉, 루트노드라는 의미이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uw02j/btsQ3qHhXJn/MMr4pFqq6S7kxu3VddmwbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uw02j/btsQ3qHhXJn/MMr4pFqq6S7kxu3VddmwbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uw02j/btsQ3qHhXJn/MMr4pFqq6S7kxu3VddmwbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fuw02j%2FbtsQ3qHhXJn%2FMMr4pFqq6S7kxu3VddmwbK%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;507&quot; height=&quot;184&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;468&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;union(1, 5)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;노드1과 노드5를 유니온 연산을 진행한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;노드5는 노드1의 자식노드가 되며 배열의 5번째 인덱스를 인덱스 5의 부모인 1로 업데이트한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이제 1과 5는 같은 그룹이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AhK10/btsQ4NnZEAr/ZkdJUzWqmvyg92OWNsIWok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AhK10/btsQ4NnZEAr/ZkdJUzWqmvyg92OWNsIWok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AhK10/btsQ4NnZEAr/ZkdJUzWqmvyg92OWNsIWok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAhK10%2FbtsQ4NnZEAr%2FZkdJUzWqmvyg92OWNsIWok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;533&quot; height=&quot;163&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;352&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;union(1,2)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;노드1과 노드2를 유니온 연산을 진행한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;마찬가지로 노드2를 노드1의 자식으로 포함시키며, 배열의 2번째 인덱스 값을 1로 업데이트한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2L1Bl/btsQ2ReacmD/AlAz3pjXE9MMn4ktTVmYz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2L1Bl/btsQ2ReacmD/AlAz3pjXE9MMn4ktTVmYz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2L1Bl/btsQ2ReacmD/AlAz3pjXE9MMn4ktTVmYz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2L1Bl%2FbtsQ2ReacmD%2FAlAz3pjXE9MMn4ktTVmYz0%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;545&quot; height=&quot;312&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;union(3,4) union(1,4)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;다음으로 union(3,4), union(1,4) 연산을 차례로 진행한 예제를 살펴보자. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;union(3,4) 연산까지는 지금까지 한대로 하면 되는데, union(1,4)에서 두 가지 케이스가 발생한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;이때 노드4를 노드1의 자식으로 두면 안되고, 노드3을 노드1의 자식으로 세팅해야 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S2tMM/btsQ5C0A5SA/qFmHULTkbwL2BSTDN7eYK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S2tMM/btsQ5C0A5SA/qFmHULTkbwL2BSTDN7eYK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S2tMM/btsQ5C0A5SA/qFmHULTkbwL2BSTDN7eYK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS2tMM%2FbtsQ5C0A5SA%2FqFmHULTkbwL2BSTDN7eYK0%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;530&quot; height=&quot;204&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;380&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;최종적으로 배열과 그래프는 아래와 같은 상태가 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LWRTZ/btsQ4YwgTT7/kQXcKkrwjnNqqTB63FYa9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LWRTZ/btsQ4YwgTT7/kQXcKkrwjnNqqTB63FYa9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LWRTZ/btsQ4YwgTT7/kQXcKkrwjnNqqTB63FYa9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLWRTZ%2FbtsQ4YwgTT7%2FkQXcKkrwjnNqqTB63FYa9k%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;524&quot; height=&quot;189&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;370&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;find 연산은 간단하다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;노드4의 그룹을 확인하고 싶다면 부모노드를 차례로 타고 올라가서 값이 -1인 노드 (루트노드)의 인덱스가 노드4가 속한 그룹 번호이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;456&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dHjxvx/btsQ3H9UAoH/M02wJ4vKI2o3LpSRdh2gI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dHjxvx/btsQ3H9UAoH/M02wJ4vKI2o3LpSRdh2gI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dHjxvx/btsQ3H9UAoH/M02wJ4vKI2o3LpSRdh2gI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdHjxvx%2FbtsQ3H9UAoH%2FM02wJ4vKI2o3LpSRdh2gI1%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;524&quot; height=&quot;230&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;456&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;find 함수는 인자로 노드 x (해당 노드가 몇 번 그룹에 속하는지 탐색할 노드) 를 받는다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;만약 인자로 받은 노드를 인덱스로 하는 배열 안의 값이 음수 (-1) 이라면 루트노드라는 뜻이기에 해당 노드를 반환한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;만약 노드의 값이 -1이 아니면 노드가 루트 노드가 아니며, 부모 노드가 존재한다는 뜻이기에 재귀 호출로 부모 탐색을 진행한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759826022644&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vector&amp;lt;int&amp;gt; p(5, -1);

// find 연산 
int find (int x) {
    if (p[x] &amp;lt; 0) return x; // 정점 도달
    return find(p[x]); // 부모 탐색 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;uni 함수는 인자로 합칠 2개의 노드(u, v)를 받는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;앞서 구현한 find 함수를 사용해 두 노드의 루트 노드를 탐색한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;만약 루트 노드가 동일하다면 같은 그룹에 속한다는 의미이며, 루트 노드가 다르다면 서로 다른 그룹에 속한 노드라는 뜻이기에 v노드를 u노드의 자식으로 세팅한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759826153350&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// union 연산
// 정점 u가 속한 트리의 루트를 탐색. 
// v가 속한 트리의 루트를 찾은 후 &quot;v가 속한 트리의 루트&quot;를 &quot;u가 속한 트리의 자식&quot;으로 설정 
bool uni(int u, int v) {
    u = find(u); // 정점 u가 속한 트리의 루트를 탐색 
    v = find(v); // 정점 v가 속한 트리의 루트를 탐색 
    if (u == v) return false; // 정점 u와 정점 v가 같다면, 같은 그룹
    p[v] = u; // 정점 v가 속한 트리의 루트를 정점 u의 자식으로 설정 
    return true; 

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;#2 Union Find Usage&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;1. Cycle Detection - 그래프 내부에서 사이클이 존재하는지 판별할 때 사용한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;2. Kruskal's Algorithm - 최소 신장 트리 (MST)를 만드는 알고리즘인 크루스칼 알고리즘에서 유니온 파인드 자료구조가 사용된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;3. Network Connectivity - 노드와 연결선이 주어졌을때 두 노드가 서로 연결되어 있는지 판별할 때 사용된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;Cycle Detection&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;유니온 파인드의 첫 번째 사례인 Cycle Detection 에서 단순히 그래프 내부의 사이클 존재 여부만 탐색할 경우에는 DFS를 사용하는 아이디어도 가능하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&quot;그래프가 어떠한 상태에 놓여져 있는지&quot;를 먼저 파악해 보면 어떤 알고리즘을 채택하는 것이 효율적인지 판별할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;정적 그래프에서 이미 모든 간선과 노드의 정보가 주어진 상태에서 사이클을 존재 유무를 한 번에 판별하는 상황에서는 DFS 알고리즘을 채택하는 것이 직관적이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;하지만 동적 그래프에서 간선이 하나씩 추가되는 상황에서 사이클 여부를 판별하는 상황에서는 Union Find 알고리즘을 떠올리는 것이 바람직하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;예시로 BOJ 20040 사이클게임 같은 문제와 같이 실시간으로 그래프의 상태가 변하는 상황에서 DFS를 적용해 매 번 모든 그래프의 노드를 탐색하는 것은 굉장히 비효율적이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;a style=&quot;color: #333333;&quot; href=&quot;https://www.acmicpc.net/problem/20040&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/20040&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2332&quot; data-origin-height=&quot;194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8TLy6/btsQ3fFMc0r/pleDXnsomRkMsbXear8unk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8TLy6/btsQ3fFMc0r/pleDXnsomRkMsbXear8unk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8TLy6/btsQ3fFMc0r/pleDXnsomRkMsbXear8unk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8TLy6%2FbtsQ3fFMc0r%2FpleDXnsomRkMsbXear8unk%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;2332&quot; height=&quot;194&quot; data-origin-width=&quot;2332&quot; data-origin-height=&quot;194&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Network Connectivity&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #585f69; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;백준 7511 소셜 네트워킹 어플리케이션 문제 또한 유니온 파인드 알고리즘의 3번째 문제 상황을 대입해 보기에 좋은 문제이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #585f69; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;마찬가지로 해당 문제도 단순히 모든 상황에서 DFS/BFS를 사용해 두 친구 노드가 연결되어 있는지 탐색하고자 하면 시간초과가 발생한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #585f69; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;하지만 유니온 파인드 알고리즘을 적용하면 어렵지 않게 노드가 같은 그룹에 속하는지 판별 가능하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #585f69; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;(BFS/DFS 알고리즘을 사용하더라도 전처리 과정을 거치면 시간 내에 통과하기는 한다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/7511&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/7511&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2336&quot; data-origin-height=&quot;608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bT59CM/btsQ37tL1B1/naGY2bkKwxwSiBVnwqCRyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bT59CM/btsQ37tL1B1/naGY2bkKwxwSiBVnwqCRyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bT59CM/btsQ37tL1B1/naGY2bkKwxwSiBVnwqCRyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbT59CM%2FbtsQ37tL1B1%2FnaGY2bkKwxwSiBVnwqCRyK%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;2336&quot; height=&quot;608&quot; data-origin-width=&quot;2336&quot; data-origin-height=&quot;608&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;code&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/novvvv/PS2025/blob/main/202509/7511.cpp&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/novvvv/PS2025/blob/main/202509/7511.cpp&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/novvvv/PS2025/blob/main/202509/20040.cpp&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/novvvv/PS2025/blob/main/202509/20040.cpp&lt;/a&gt;&lt;/p&gt;</description>
      <category>Algorithm/Algorithm</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/370</guid>
      <comments>https://novlog.tistory.com/entry/Algorithm-Union-Find#entry370comment</comments>
      <pubDate>Tue, 7 Oct 2025 17:41:47 +0900</pubDate>
    </item>
    <item>
      <title>[Algorithm] 2D Prefix Sum BOJ 23247</title>
      <link>https://novlog.tistory.com/entry/Algorithm-2D-Prefix-Sum-BOJ-23247</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;본 문서는 이차원 배열에서 누적합 알고리즘을 적용하는 방법에 대해 다루고 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;BOJ 23247 Ten 문항을 기반으로 설명합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/23247&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/23247&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이차원 배열에서 누적합을 구하는 방법&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;1. &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;행 방향으로 원소들의 누적합을 계산&lt;/span&gt;한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciDXCZ/btsQ3w8hoYo/K8bahFDejl1OUne0uyEQMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciDXCZ/btsQ3w8hoYo/K8bahFDejl1OUne0uyEQMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciDXCZ/btsQ3w8hoYo/K8bahFDejl1OUne0uyEQMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciDXCZ%2FbtsQ3w8hoYo%2FK8bahFDejl1OUne0uyEQMK%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;489&quot; height=&quot;175&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1759731214605&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    // 2차원 누적합 계산 로직
    // [Logic1] row(행) 방향으로 모든 원소의 합을 구한다.
    for (int y = 1; y &amp;lt;= n; y++) {
        for (int x = 1; x &amp;lt;= m; x++) {
            // 이전 열까지의 합(apple_sum[y][x-1])에 현재 값을 더한다.
            apple_sum[y][x] = apple_sum[y][x-1] + apple[y][x];
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;2. 행 방향으로 누적된 누적합 배열을 기반으로&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;열 방향으로 누적합을 계산&lt;/span&gt;한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;298&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8ZP1n/btsQ4ZuZkQX/Gs7MNsjB2O6fX2XgJxsaJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8ZP1n/btsQ4ZuZkQX/Gs7MNsjB2O6fX2XgJxsaJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8ZP1n/btsQ4ZuZkQX/Gs7MNsjB2O6fX2XgJxsaJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8ZP1n%2FbtsQ4ZuZkQX%2FGs7MNsjB2O6fX2XgJxsaJ1%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;439&quot; height=&quot;155&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;298&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그러면 최종적으로 2차원 배열 누적합이 완성된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;apple_sum 누적합의 1행2열(17)에는&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;기존 apple 배열의 0행0열부터 1행2열까지의 합이 담기게 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lr5rg/btsQ2UaIQ5c/mpugRdpvxUdgkYt3NyW9E0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lr5rg/btsQ2UaIQ5c/mpugRdpvxUdgkYt3NyW9E0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lr5rg/btsQ2UaIQ5c/mpugRdpvxUdgkYt3NyW9E0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flr5rg%2FbtsQ2UaIQ5c%2FmpugRdpvxUdgkYt3NyW9E0%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;462&quot; height=&quot;161&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;294&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1759731411083&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    // [Logic2] Column(열) 방향으로 모든 원소의 합을 구한다.
    for (int y = 1; y &amp;lt;= n; y++) {
        for (int x = 1; x &amp;lt;= m; x++) {
            // 이전 행까지의 합(apple_sum[y-1][x] + apple[y-1][x])에 현재 값을 더한다.
            apple_sum[y][x] = apple_sum[y-1][x] + apple_sum[y][x];
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그렇다면 &lt;b&gt;1행1열 원소부터 2행3열까지의 원소의 합&lt;/b&gt;은 어떻게 계산할까?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;410&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c57BBB/btsQ32FO5dd/v1GHnghe9EqmFBi26vhZQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c57BBB/btsQ32FO5dd/v1GHnghe9EqmFBi26vhZQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c57BBB/btsQ32FO5dd/v1GHnghe9EqmFBi26vhZQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc57BBB%2FbtsQ32FO5dd%2Fv1GHnghe9EqmFBi26vhZQk%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;218&quot; height=&quot;163&quot; data-origin-width=&quot;410&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;단순히 누적합의 2행3열 원소를 출력해서는 안된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;apple_sum[2][3]의 원소 33은&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;기존 apple 배열의 &lt;b&gt;0행0번째 원소부터 2행3번째 원소의 모든 합을 계산&lt;/b&gt;한 것이기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;810&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BC9QC/btsQ3BBBePo/Vktn8GHBC8ZyciZrNoBgq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BC9QC/btsQ3BBBePo/Vktn8GHBC8ZyciZrNoBgq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BC9QC/btsQ3BBBePo/Vktn8GHBC8ZyciZrNoBgq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBC9QC%2FbtsQ3BBBePo%2FVktn8GHBC8ZyciZrNoBgq0%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;400&quot; height=&quot;148&quot; data-origin-width=&quot;810&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그렇기에 1행1열부터 2행3열까지의 배열의 합을 구하기 위해서는&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;apple_sum[2][3] (0행0열~2행3열) 에서&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;apple_sum[2][0] (0행0열~2행0열), apple_sum[0][3] (0행0열~0행3열) 을 빼준뒤&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;마지막으로 2번 빼진 apple_sum[0][0] (0행0열) 을 더해주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1826&quot; data-origin-height=&quot;846&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HmUy7/btsQ1X0ba7j/JAXuhNGuQeiK7qOfqvHg30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HmUy7/btsQ1X0ba7j/JAXuhNGuQeiK7qOfqvHg30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HmUy7/btsQ1X0ba7j/JAXuhNGuQeiK7qOfqvHg30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHmUy7%2FbtsQ1X0ba7j%2FJAXuhNGuQeiK7qOfqvHg30%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;1826&quot; height=&quot;846&quot; data-origin-width=&quot;1826&quot; data-origin-height=&quot;846&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_1759732295906&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    int ans = 0;
    for (int y = 1; y &amp;lt;= n; y++) {
        for (int x = 1; x &amp;lt;= m; x++) {
            for (int y_idx = y; y_idx &amp;lt;= n; y_idx++) {
                for (int x_idx = x; x_idx &amp;lt;= m; x_idx++) {
                    int rect = apple_sum[y_idx][x_idx] - apple_sum[y-1][x_idx]
                                - apple_sum[y_idx][x-1] + apple_sum[y-1][x-1];

                    if (rect == 10) ans++;
                    if (rect &amp;gt; 10) break; // 가지치기
                }
            }
        }
    }
    
    cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; endl;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/novvvv/PS2025/blob/main/202509/23247.cpp&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/novvvv/PS2025/blob/main/202509/23247.cpp&lt;/a&gt;&lt;/p&gt;</description>
      <category>Algorithm/Algorithm</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/369</guid>
      <comments>https://novlog.tistory.com/entry/Algorithm-2D-Prefix-Sum-BOJ-23247#entry369comment</comments>
      <pubDate>Mon, 6 Oct 2025 15:34:35 +0900</pubDate>
    </item>
    <item>
      <title>[JPA] @Transactional</title>
      <link>https://novlog.tistory.com/entry/JPA-Transaction</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;트랜잭션이란?&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;트랜잭션은 데이터베이스의 상태를 변경시키는 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;더 이상 쪼개질 수 없는 최소한의 작업 단위&lt;/span&gt;이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;회원가입을 예시로 들어보자.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;다음 예제는 총 4가지 작업으로 구성된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;b&gt;1. validateUserUniqueness&lt;/b&gt; - 사용자 이름과 이메일이 디비에서 중복되지 않는지 검사한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;b&gt;2. encodePassword&lt;/b&gt; - 패스워드를 암호화한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;3. 유저 객체를 생성한다.&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;4. 유저 레포지토리에 저장한다.&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758604535029&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    public User createUser(String username, String password, String email) {
        validateUserUniqueness(username, email);
        String encodedPassword = passwordEncoder.encode(password);
        User user = new User(username, encodedPassword, email);
        return userRepository.save(user);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;4가지 작업이 모두 성공적으로 완료되어야만 회원가입이 정상적으로 처리된 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;만약 3번 (유저 객체 생성) 로직 까지만 수행되고 4번 (유저 레포지토리에 저장) 하는 로직이 정상적으로 수행되지 않았다면, 메모리 누수가 발생할 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;따라서 트랜잭션은 4가지 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;작업을 하나의 묶음으로 처리해 하나라도 실패하면 모두 이전 상태로 되돌리는 Rollback을 보장&lt;/span&gt;한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;ACID&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;트랜잭션은 ACID라는 4가지 특성을 통해서 데이터의 일관성을 보장한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;1. 원자성 (Atomicity)&lt;/span&gt; - 트랜잭션의 모든 작업이 전부 성공하거나 전부 실패하는 것을 보장한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;2. 일관성 (Consistency)&lt;/span&gt; - 트랜잭션이 성공되었다면 데이터베이스는 일관성 있는 상태를 유지한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;3. 고립성 (Isolation)&lt;/span&gt; - 하나의 트랜잭션이 실행 중일때 다른 트랜잭션이 영향을 주지 못하도록 격리한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;4. 지속성 (Durability) &lt;/span&gt;- 성공적으로 수행된 트랜잭션은 시스템에 영구적으로 저장되어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;@Transactional&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;Spring은 이러한 트랜잭션을 개발자가 쉽게 사용할 수 있도록 Transactional Annotation을 제공한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;b&gt;@Transactional&lt;/b&gt;은 &lt;b&gt;클래스 단위에 사용하거나 메소드 단위에 사용&lt;/b&gt;할 수 있으며&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;클래스 단위에 붙여주면 해당 클래스의 메서드는 모두 하나의 트랜잭션 안에서 실행된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;만약 &lt;b&gt;메소드 실행 중 예외가 터진다면 스프링이 자동으로 rollback을 수행&lt;/b&gt;한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758604980320&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
@Transactional // 클래스 단위 수행
public class UserService {
    
    ...
    
    // 메서드 단위 수행
    @Transactional
    public User createUser(String username, String password, String email) {
        validateUserUniqueness(username, email);
        String encodedPassword = passwordEncoder.encode(password);
        User user = new User(username, encodedPassword, email);
        return userRepository.save(user);
    }
    
   	...
    
    @Transactional
    public User updateUser(User user) {
        return userRepository.save(user);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;readOnly&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;@Transactional 에는 다양한 속성이 존재하는데 그중 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;readOnly&lt;/span&gt; 속성을 잘 사용하면 성능을 최적화할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;readOnly 속성의 Default 값은 false이며, &lt;b&gt;readOnly 속성을 True로 설정하면 해당 트랜잭션 내부의 데이터는 읽기 전용으로 세팅&lt;/b&gt;된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;readOnly 속성이 붙은 트랜잭션은 불필요한 오버헤드 (스냅샷, 프록시 등..) 를 줄여 성능 향상에 도움이 되며, 데이터의 일관성 또한 지킬 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;따라서 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;단순 조회 메서드는 readOnly 속성을 true로 설정&lt;/span&gt;하도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758605184260&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    // 사용자명 중복 확인
    @Transactional(readOnly = true)
    public boolean existsByUsername(String username) {
        return userRepository.existsByUsername(username);
    }
    
    /// 이메일 중복 확인
    @Transactional(readOnly = true)
    public boolean existsByEmail(String email) {
        return userRepository.existsByEmail(email);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Framework/JPA</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/368</guid>
      <comments>https://novlog.tistory.com/entry/JPA-Transaction#entry368comment</comments>
      <pubDate>Tue, 23 Sep 2025 14:30:19 +0900</pubDate>
    </item>
    <item>
      <title>[JPA] Embedded, Embeddable annotation &amp;amp; Refactoring Example</title>
      <link>https://novlog.tistory.com/entry/JPA-Embedded-Embeddable-annotation-Refactoring-Example</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;About Embedded Type&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Embedded&lt;/b&gt;란 JPA 표준에서 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;&quot;객체 지향적인 방식으로 데이터베이스 구조를 설계할 수 있도록 도와주는 어노테이션이다.&quot;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;하나의 데이터 모델 엔터티 내부의 컬럼을 재사용 가능한 클래스 단위로 묶어 관리할 수 있게 해 준다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;별도의 테이블을 생성하지 않으며, 같은 테이블에 필드들을 포함한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이해를 돕기 위해 현재 진행중인 알고리즘 대회 사이트 프로젝트의 ERD를 가져와 보았다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;제출(submissions), 문제(problems), 테스트케이스(test_cases) 테이블에서 (생성일자, 수정일자) 컬럼이 반복돼서 사용되는 상황이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;따라서 createdAt, updatedAt Column을 Embeddable Class로 분리하여 재사용 가능한 구조로 변경해 보고자 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;971&quot; data-origin-height=&quot;464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dT9DGU/btsQJ95wjKi/ntkBCUnKcnbT4TSTxMC1d0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dT9DGU/btsQJ95wjKi/ntkBCUnKcnbT4TSTxMC1d0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dT9DGU/btsQJ95wjKi/ntkBCUnKcnbT4TSTxMC1d0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdT9DGU%2FbtsQJ95wjKi%2FntkBCUnKcnbT4TSTxMC1d0%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;971&quot; height=&quot;464&quot; data-origin-width=&quot;971&quot; data-origin-height=&quot;464&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Usage&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Embeddable로 만들 클래스에는 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;@Embeddable Annotation&lt;/span&gt;을 적어주고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Embeddable로 만든 클래스를 사용하는 코드에서 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;@Embedded Annotation&lt;/span&gt;을 적어주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;345&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FlbBm/btsQKaXE4gA/ovXKHUSWlPnGUr8gZDitb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FlbBm/btsQKaXE4gA/ovXKHUSWlPnGUr8gZDitb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FlbBm/btsQKaXE4gA/ovXKHUSWlPnGUr8gZDitb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFlbBm%2FbtsQKaXE4gA%2FovXKHUSWlPnGUr8gZDitb1%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;345&quot; height=&quot;130&quot; data-origin-width=&quot;345&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;BaseTimeEntity&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;createdAt, updatedAt Column을 하나로 묶은 Embeddable Type을 선언해 주었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;@Embeddable Annotation을 사용해 JPA에 Embeddable Type임을 명시해 준다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758446196964&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Embeddable
@Getter
public class BaseTimeEntity {
    
    @Column(name = &quot;created_at&quot;)
    private LocalDateTime createdAt;
    
    @Column(name = &quot;updated_at&quot;)
    private LocalDateTime updatedAt;
    
    // 기본 생성자 (JPA 필수)
    protected BaseTimeEntity() {}
    
    // 생성용 생성자
    public BaseTimeEntity(LocalDateTime createdAt, LocalDateTime updatedAt) {
        this.createdAt = createdAt;
        this.updatedAt = updatedAt;
    }
      
    @PrePersist
    public void prePersist() {
        if (this.createdAt == null || this.updatedAt == null) {
            LocalDateTime now = LocalDateTime.now();
            this.createdAt = now;
            this.updatedAt = now;
        }
    }
    
    @PreUpdate
    public void preUpdate() {
        this.updatedAt = LocalDateTime.now();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Problem Entity&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758446522844&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
@Table(name = &quot;problems&quot;)
@Data
@AllArgsConstructor
public class Problem {
    
    ...
    
    @Embedded
    private BaseTimeEntity timeInfo;
    
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;TestCase Entity&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758446629035&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
@Table(name = &quot;test_cases&quot;)
@Data
@AllArgsConstructor
public class TestCase {
    
    ...
    @Embedded
    private BaseTimeEntity timeInfo;
    
    // 기본 생성자
    public TestCase() {
        this.timeInfo = BaseTimeEntity.now();
    }
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Embedded Type 사용 시 주의점&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; background-color: #333333; color: #dddddd;&quot;&gt;Embedded Type 은 기본적으로 변경 불가능 하도록 설계해야 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;@Setter Annotation은 지운 뒤, 생성자에서 값을 모두 초기화하는 방식으로 클래스 구조를 작성하도록 하자.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;277&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GXQhA/btsQHTQfuEa/hfkgsCkNrzv2iPZkoJwKS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GXQhA/btsQHTQfuEa/hfkgsCkNrzv2iPZkoJwKS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GXQhA/btsQHTQfuEa/hfkgsCkNrzv2iPZkoJwKS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGXQhA%2FbtsQHTQfuEa%2FhfkgsCkNrzv2iPZkoJwKS1%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;277&quot; height=&quot;73&quot; data-origin-width=&quot;277&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; background-color: #333333; color: #dddddd;&quot;&gt;JPA 기본 스펙상 Entity, Embeddable은 리플렉션, 지연로딩 기술 제공을 위해 Default Constructor를 요구한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이때 기본 생성자는 외부에서 호출되지 않는다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;따라서 Public으로 선언해 어디서나 접근 가능하게 허락하기보다는 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;protected로 선언하여 같은 패키지, 상속 클래스만 접근 가능하도록 코드를 작성&lt;/span&gt;하도록 하자.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;337&quot; data-origin-height=&quot;57&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rfItF/btsQHYKIY9B/turDXcSQMoU0jDEWlNsvQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rfItF/btsQHYKIY9B/turDXcSQMoU0jDEWlNsvQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rfItF/btsQHYKIY9B/turDXcSQMoU0jDEWlNsvQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrfItF%2FbtsQHYKIY9B%2FturDXcSQMoU0jDEWlNsvQ0%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;337&quot; height=&quot;57&quot; data-origin-width=&quot;337&quot; data-origin-height=&quot;57&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Spring Framework/JPA</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/367</guid>
      <comments>https://novlog.tistory.com/entry/JPA-Embedded-Embeddable-annotation-Refactoring-Example#entry367comment</comments>
      <pubDate>Sun, 21 Sep 2025 18:32:59 +0900</pubDate>
    </item>
    <item>
      <title>Cpp Algorithm Technic Climits 가장 큰 최대, 최소값 설정하기</title>
      <link>https://novlog.tistory.com/entry/Cpp-Algorithm-Technic-Climits-%EA%B0%80%EC%9E%A5-%ED%81%B0-%EC%B5%9C%EB%8C%80-%EC%B5%9C%EC%86%8C%EA%B0%92-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;알고리즘 문제를 풀이하다 보면 가장 큰 값을 초기화해야 한다던지&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;가장 작은 값을 초기화해야 하는 케이스가 빈번하게 발생한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;그럴 때마다 문제에서 제시해 준 조건으로 나올 수 있는 최대 최소를 계산했는데,&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;climits 내부의 LLONG_MAX, LLONG_MIN&lt;/span&gt;을 활용하면 간편하게 계산할 수 있어 정리해 보고자 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1757223355787&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;climits&amp;gt;
using namespace std;

int main() {

    cout &amp;lt;&amp;lt; &quot;LLONG_MAX : &quot; &amp;lt;&amp;lt; LLONG_MAX &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; &quot;LLONG_MIN : &quot; &amp;lt;&amp;lt; LLONG_MIN &amp;lt;&amp;lt; endl;
    
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1757223370653&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LLONG_MAX : 9223372036854775807
LLONG_MIN : -9223372036854775808
Program ended with exit code: 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;LLONG_MAX의 경우 64bit long long 정수형의 최댓값인 &lt;span aria-hidden=&quot;true&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;2^63&amp;minus;&lt;/span&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;1 (9223372036854775807)&lt;/span&gt; 을 리턴하며&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;LLONG_MIN의 경우 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;2^&lt;/span&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;63 (-9223372036854775808)&lt;/span&gt;를 리턴한다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;거의 모든 알고리즘 문제의 상황에서 해당 값의 범위를 뛰어넘는 케이스는 존재하지 않기에 기억해 두면 유용하게 사용될 듯하다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Algorithm/PS With C++</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/366</guid>
      <comments>https://novlog.tistory.com/entry/Cpp-Algorithm-Technic-Climits-%EA%B0%80%EC%9E%A5-%ED%81%B0-%EC%B5%9C%EB%8C%80-%EC%B5%9C%EC%86%8C%EA%B0%92-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0#entry366comment</comments>
      <pubDate>Sun, 7 Sep 2025 14:39:20 +0900</pubDate>
    </item>
    <item>
      <title>[NextJS] Client To Server Data 전송 방식 정리</title>
      <link>https://novlog.tistory.com/entry/NextJS-Client-To-Server-Data-%EC%A0%84%EC%86%A1-%EB%B0%A9%EC%8B%9D-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;NextJS 환경에서 클라이언트(브라우저) 단에서 서버단으로 데이터를 전송하는 방식은 아래와 같이 크게 4가지로 구분할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; background-color: #c0d1e7;&quot;&gt;1. &amp;lt;Form&amp;gt; 태그를 활용한 전송&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; background-color: #c0d1e7;&quot;&gt;2. Query String&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; background-color: #c0d1e7;&quot;&gt;3. 다이나믹 라우팅 + URL Parameter&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; background-color: #c0d1e7;&quot;&gt;4. fetch API를 사용해 body에 직접 데이터를 넣어서 전송하기&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;이번 포스팅에서는 위 4가지 방식의 장단점 그리고 사용 방법에 대해서 간략하게 정리하고자 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;1. &amp;lt;Form&amp;gt; 태그를 활용한 전송&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;b&gt;page.js&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;form tag &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;method 속성에 HTTP Method&lt;/span&gt;를 정의, &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;action 속성에 api 요청 주소&lt;/span&gt;를 명시한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751960736075&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function ServerComponent() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;form method=&quot;POST&quot; action=&quot;/api/login&quot;&amp;gt;
        &amp;lt;input name=&quot;username&quot; placeholder=&quot;ID&quot; required /&amp;gt;
        &amp;lt;input name=&quot;password&quot; type=&quot;password&quot; placeholder=&quot;PW&quot; required /&amp;gt;
        &amp;lt;button type=&quot;submit&quot;&amp;gt;로그인&amp;lt;/button&amp;gt;
      &amp;lt;/form&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;b&gt;login api&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;page.js로부터 입력받은 ID, PW 정보가 Json 형태로 브라우저에 반환된다. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;421&quot; data-origin-height=&quot;93&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n8SqD/btsO8zNvx3V/ikg2k5UyMnve54iANwTcs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n8SqD/btsO8zNvx3V/ikg2k5UyMnve54iANwTcs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n8SqD/btsO8zNvx3V/ikg2k5UyMnve54iANwTcs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn8SqD%2FbtsO8zNvx3V%2Fikg2k5UyMnve54iANwTcs0%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;421&quot; height=&quot;93&quot; data-origin-width=&quot;421&quot; data-origin-height=&quot;93&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1751960871648&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function handler(req, res) {
  if (req.method === 'POST') {
    const { username, password } = req.body
    res.status(200).json({ msg: '로그인 처리 완료', username, password })
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;581&quot; data-origin-height=&quot;183&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dOkcOU/btsO9rgT997/lZpClc6hk8pipvkItjqnk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dOkcOU/btsO9rgT997/lZpClc6hk8pipvkItjqnk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dOkcOU/btsO9rgT997/lZpClc6hk8pipvkItjqnk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdOkcOU%2FbtsO9rgT997%2FlZpClc6hk8pipvkItjqnk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;581&quot; height=&quot;183&quot; data-origin-width=&quot;581&quot; data-origin-height=&quot;183&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;사용 상황&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;로그인, 회원가입, 주문, 배송 등 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;특정 폼을 사용한 양식을 제출할 때 사용하면 용이&lt;/span&gt;하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;사용자와 웹사이트 혹은 어플리케이션간 상호 작용할 때 빈번하게 사용되는 태그로 페이지가 재랜더링 된다는 단점이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;자세한 사용 방법은 아래 블로그를 참고하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;a style=&quot;color: #333333;&quot; href=&quot;https://inpa.tistory.com/entry/HTML-%F0%9F%93%9A-%ED%8F%BCForm-%ED%83%9C%EA%B7%B8-%EC%A0%95%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://inpa.tistory.com/entry/HTML-%F0%9F%93%9A-%ED%8F%BCForm-%ED%83%9C%EA%B7%B8-%EC%A0%95%EB%A6%AC&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1751961181806&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot; ️ 폼(Form) 태그 양식 &amp;amp; 종류 - 한방 정리&quot; data-og-description=&quot;폼(Form) 양식 HTML 에선 브라우저의 내장 요소를 사용하여 웹 양식에 대한 지원을 제공한다. 예를들어 우리가 특정 사이트에 로그인 할때 아래와 같이 계정 아이디와 비밀번호를 입력하는 화면을 &quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/HTML-%F0%9F%93%9A-%ED%8F%BCForm-%ED%83%9C%EA%B7%B8-%EC%A0%95%EB%A6%AC&quot; data-og-url=&quot;https://inpa.tistory.com/entry/HTML-%F0%9F%93%9A-%ED%8F%BCForm-%ED%83%9C%EA%B7%B8-%EC%A0%95%EB%A6%AC&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/UDGyg/hyZjya5KkM/AD1z5SYFltqjR2D3T2PNNk/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/c19YJc/hyZjvFrjUO/g6gIFX91wxgX1JScqu6rek/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/gNZWM/hyZfZ2tewc/MkFe2XBBykrvcpQ4pNB0sk/img.png?width=992&amp;amp;height=406&amp;amp;face=0_0_992_406&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/HTML-%F0%9F%93%9A-%ED%8F%BCForm-%ED%83%9C%EA%B7%B8-%EC%A0%95%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/HTML-%F0%9F%93%9A-%ED%8F%BCForm-%ED%83%9C%EA%B7%B8-%EC%A0%95%EB%A6%AC&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/UDGyg/hyZjya5KkM/AD1z5SYFltqjR2D3T2PNNk/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/c19YJc/hyZjvFrjUO/g6gIFX91wxgX1JScqu6rek/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/gNZWM/hyZfZ2tewc/MkFe2XBBykrvcpQ4pNB0sk/img.png?width=992&amp;amp;height=406&amp;amp;face=0_0_992_406');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt; ️ 폼(Form) 태그 양식 &amp;amp; 종류 - 한방 정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;폼(Form) 양식 HTML 에선 브라우저의 내장 요소를 사용하여 웹 양식에 대한 지원을 제공한다. 예를들어 우리가 특정 사이트에 로그인 할때 아래와 같이 계정 아이디와 비밀번호를 입력하는 화면을&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;2. Query String&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;별도의 메시지 바디가 존재하지 않으며, &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;Url에 데이터 정보를 포함시켜 ?Key=value 형식으로 데이터를 전송&lt;/span&gt;하는 방식이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&amp;amp;구분자를 사용해 다량의 key-value쌍을 보내는것 또한 가능하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;page.js&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;JS의 fetch API를 사용해야 하기에 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;Client Component로 생성&lt;/span&gt;해 주어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;Client Component와 Server Component의 차이점은 아래 포스팅을 참고하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;a style=&quot;color: #333333;&quot; href=&quot;https://novlog.tistory.com/entry/Nextjs-Client-Component-vs-Server-Component&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://novlog.tistory.com/entry/Nextjs-Client-Component-vs-Server-Component&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;368&quot; data-origin-height=&quot;59&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N99g8/btsPasFRxNV/2jdzhLjSzymNpCvKNkhOkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N99g8/btsPasFRxNV/2jdzhLjSzymNpCvKNkhOkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N99g8/btsPasFRxNV/2jdzhLjSzymNpCvKNkhOkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN99g8%2FbtsPasFRxNV%2F2jdzhLjSzymNpCvKNkhOkK%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;368&quot; height=&quot;59&quot; data-origin-width=&quot;368&quot; data-origin-height=&quot;59&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1751961481968&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'use client'
export default function ClientComponent() {
  return (
    &amp;lt;div&amp;gt;
          &amp;lt;span onClick = {(e) =&amp;gt; {
            fetch('/api/test?name=kim&amp;amp;age=20')
          }}&amp;gt;Send&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;pages/api/test.js&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;103&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qU6Uo/btsO8ATiDZL/wu8W5RZiv9ZdDwkzFUNgeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qU6Uo/btsO8ATiDZL/wu8W5RZiv9ZdDwkzFUNgeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qU6Uo/btsO8ATiDZL/wu8W5RZiv9ZdDwkzFUNgeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqU6Uo%2FbtsO8ATiDZL%2Fwu8W5RZiv9ZdDwkzFUNgeK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;435&quot; height=&quot;93&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;103&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1751961555797&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function handler(request, response) {
    console.log(request.query)
    return response.status(200).json()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;request parameter의 query property&lt;/span&gt;에 접근하면 Client단에서 보낸 Query String에 접근 가능하다&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751961668781&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{ name: 'kim', age: '20' }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;사용 상황&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;단순 페이지 정렬, 검색, 필터링, 페이징 등에서 자주 사용&lt;/span&gt;되는 기법이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;GET 요청임에도 불구하고 데이터를 서버로 전송할 수 있으며 무엇보다 간편하다는 장점이 있지만&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; background-color: #c0d1e7;&quot;&gt;Url에 데이터가 노출되기에 비밀번호와 같은 민감한 정보는 절대로 쿼리 스트링 방식을 사용해서는 안된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;3. 다이나믹 라우팅 + Url Parameter&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;NextJS의 다이나믹 라우팅 기법을 사용해 URL Parameter로 데이터를 전송하는 기법이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;다이나믹 라우팅이란 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;api 주소를 [ ] 대괄호로 묶어주면 [ ] 안에 어떠한 요청 문자열이 들어와도 해당 코드로 라우팅 시켜주는&lt;/span&gt; 기법이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;예시로 폴더 구조가 아래 사진과 같이 되어있다고 가정해 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;365&quot; data-origin-height=&quot;107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bchl5a/btsO8mm6ffo/3OmlTRkUIjIIcBTC3W14NK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bchl5a/btsO8mm6ffo/3OmlTRkUIjIIcBTC3W14NK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bchl5a/btsO8mm6ffo/3OmlTRkUIjIIcBTC3W14NK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbchl5a%2FbtsO8mm6ffo%2F3OmlTRkUIjIIcBTC3W14NK%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;365&quot; height=&quot;107&quot; data-origin-width=&quot;365&quot; data-origin-height=&quot;107&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;만약 클라이언트단에서 api/abc/test로 api 요청이 들어와도 api/abc/test.js로 api 요청을 처리하며&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;api/abc/nov로 api 요청이 들어오면 api/abc/nov.js로 api 요청을 처리한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;나머지 코드는 Query-String 방식과 동일하며, request.query로 접근해 요청 정보를 받아온다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;page.js&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751962098998&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'use client'
export default function ClientComponent() {
  return (
    &amp;lt;div&amp;gt;
          &amp;lt;span onClick = {(e) =&amp;gt; {
            // fetch('/api/test?name=kim&amp;amp;age=20')
            fetch('/api/abc/nov')
          }}&amp;gt;Send&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;api/abc/[dynamic].js&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751962153789&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function handler(request, response) {
    console.log(request.query)
    return response.status(200).json()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;사용상황&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;REST API에 적합&lt;/span&gt;한 방식이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;예시로 특정 게시글의 id를 조회하는 경우에 다이나믹 라우팅 방식을 사용하면, 여러 개의 API를 명세할 필요 없이 단 하나의 js 파일로 게시글 조회와 관련된 post 요청을 모두 처리할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;4. fetch API를 사용해 body에 직접 데이터를 넣어서 전송하기&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;JavaScript의 fetch API를 사용해 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;body에 직접 데이터를 넣어 전송&lt;/span&gt;하는 방법이다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;fetch의 첫 번째 파라미터에 url 주소를 작성한 뒤, 두 번째 파라미터에 객체 형식으로 method 속성에는 메서드 타입을, body에는 전송할 데이터를 명시한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;fetch API와 관련된 자세한 내용을 추후에 따로 정리할 예정이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;page.js&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1751962428958&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'use client'
export default function ClientComponent() {
  return (
    &amp;lt;span onClick = {(r) =&amp;gt; {
      fetch('/api/test', {
          method: 'POST',
          body: &quot;Hello Nov&quot;
      }).then((r) =&amp;gt; {
          return r.json()
      })
    }}&amp;gt;Send&amp;lt;/span&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;api/test.js&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;fetch는 비동기 방식으로 동작하기에 handler 함수에 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;async&lt;/span&gt; 키워드를 붙여주자&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751962550803&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default async function handler(request, response) {
    if (request.method == 'POST') {
        console.log(request.body)
        response.status(200).json(&quot;Post Request Compelete&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1751962580073&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Hello Nov&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;사용 상황&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;댓글 작성, DB 입출력 등 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;비동기처리가 필요한 기능&lt;/span&gt;에 사용하면 된다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Archive/Next.js</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/365</guid>
      <comments>https://novlog.tistory.com/entry/NextJS-Client-To-Server-Data-%EC%A0%84%EC%86%A1-%EB%B0%A9%EC%8B%9D-%EC%A0%95%EB%A6%AC#entry365comment</comments>
      <pubDate>Tue, 8 Jul 2025 17:21:11 +0900</pubDate>
    </item>
    <item>
      <title>[Next.js] Client Component vs Server Component</title>
      <link>https://novlog.tistory.com/entry/Nextjs-Client-Component-vs-Server-Component</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;Next.js에서 Component는 Client Component와 Server Component로 구분한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;Client Component&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;Next.js에서 의미하는 Client Component는 React의 Client Component와 동일하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;즉, &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;React에서의 Component를 Next.js 환경에서 Client Component&lt;/span&gt;라고 칭한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;Client Component를 생성하는 방법은 최상단에 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;'use client'&lt;/span&gt;라는 문구를 넣어 주기만 하면 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;다음 예제 코드는 Client Component 형태로 Hello Component를 정의한 뒤&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333;&quot;&gt;Hello! Button을 클릭하면 브라우저 콘솔에 Hello World!를 출력하는 React 예제 코드이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751016400096&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'use client';

import { useState } from 'react';

export default function Hello() {
  const [clicked, setClicked] = useState(false);

  return (
    &amp;lt;button
      onClick={() =&amp;gt; {
        console.log('hello world');
        setClicked(true);
      }}
    &amp;gt;
      {clicked ? 'Clicked!' : 'Hello!'}
    &amp;lt;/button&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;322&quot; data-origin-height=&quot;78&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kx8rn/btsO4dIDxFL/YB43ESkrDvsLxKGQ5Le92k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kx8rn/btsO4dIDxFL/YB43ESkrDvsLxKGQ5Le92k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kx8rn/btsO4dIDxFL/YB43ESkrDvsLxKGQ5Le92k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fkx8rn%2FbtsO4dIDxFL%2FYB43ESkrDvsLxKGQ5Le92k%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;322&quot; height=&quot;78&quot; data-origin-width=&quot;322&quot; data-origin-height=&quot;78&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Server Component&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Server Component는 기본적으로 Next.js에서 Component를 생성하면 설정되는 기본값이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;따라서 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;별도의 문구를 표기할 필요 없이 Component를 정의만 해주면 자동으로 Server Component로 생성&lt;/span&gt;된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;단, Server Component를 사용하는 경우 Client Component와 달리 JSX 태그 내부에 onClick과 같은 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;JavaScript 코드를 포함하거나, React의 useState 같은 Hook을 사용할 수 없다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751526415870&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function ServerComponent() {
  return (
    &amp;lt;div&amp;gt;
      &quot;Hello World!&quot;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Client Component vs Server Component&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Client Component&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 브라우저 (클라이언트) 에서 실행된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;... 실행 위치가 클라이언트이기에 검색엔진이 HTML 코드를 읽어 들이는데 불리하기에 SEO 측면에서 좋지 않다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;... 하지만 렌더링이 더 부드럽기에 사용자 경험을 향상시킬 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- Component 내부에 JS Code를 사용 가능하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 코드 상단에 use client를 명시해 주어야 한다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Server Component&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 서버에서 실행된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;... 실행 위치가 서버이기에 SEO 측면에서 유리하다. (HTML 코드를 서버에서 모두 생성한 뒤 브라우저로 뿌려주기 때문)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- Component 내부에 JS Code를 작성할 수 없다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;... React Hooks (useState, useEffect ...) 을 사용 불가능하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 별도의 문구를 명시해 주지 않더라도 자동으로 Server Component로 생성된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;사용 전략 &lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;대부분의 웹사이트는 SEO가 중요하기에 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;전체적인 틀은 Server Component로 작성한 뒤, 버튼, 입력창, 폼과 같은 사용자 경험이 중요한 최소한의 컴포넌트만 클라이언트 컴포넌트로 작성&lt;/span&gt;한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;DB에서 데이터를 읽고 보여주는 기능, 혹은 인증 쿠키를 기반으로 서버에서 동작해야하는 기능 등은 Server Component로 제작하여 읽고 쓰는 성능을 향상하는 것이 중요하다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Archive/Next.js</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/364</guid>
      <comments>https://novlog.tistory.com/entry/Nextjs-Client-Component-vs-Server-Component#entry364comment</comments>
      <pubDate>Thu, 3 Jul 2025 16:19:45 +0900</pubDate>
    </item>
    <item>
      <title>[React] export default vs named export</title>
      <link>https://novlog.tistory.com/entry/React-export-default-vs-named-export</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;* 아래 코드는 Next.JS 14 환경에서 테스트하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;React (JS ES6)에서 하나의 파일(Module)을 내보는 방식은 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;export default&lt;/span&gt; 방식과 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;named export&lt;/span&gt; 방식으로 구분된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;데이터를 내보내는 측에서는 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;export keyword를 통해 내보낼 데이터를 명시&lt;/span&gt;하며, &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;데이터를 받는 측에서는 import keyword를 사용해 받을 데이터를 정의&lt;/span&gt;한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;export default&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;export default 방식은 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;단 하나의 데이터만을 내보낼 수 있으며 데이터 변수명을 재정의 가능&lt;/span&gt;하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;또한 export default는 모듈 당 하나만 정의할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&lt;b&gt;data.js&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1750939096466&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let data = 20
export default data&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&lt;b&gt;page.js&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;data.js 모듈에서 받은 data를 my_data라는 변수명으로 재정의한 뒤 my_data를 콘솔에 출력한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1750939337557&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import my_data from &quot;./data.js&quot;

export default function Home() {
  return (
    console.log(my_data)
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1750939384900&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;named export &lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;named export는 default export와 달리 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;여러 개의 변수 혹은 컴포넌트를 export 하는 것이 가능&lt;/span&gt;하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;단, 반드시 import 하는 측에서 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;중괄호 { }를 사용&lt;/span&gt;해 가져와야 하며 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;이름을 재정의 하는 것이 불가능&lt;/span&gt;하다. (as를 사용해 별칭을 지정하는 것은 허용된다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;data.js&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1750939962667&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export let age = 20
export let name = &quot;nov&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;page.js&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1750939973205&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import {age, name} from &quot;./data.js&quot;

export default function Home() {
  return (
    console.log(`name : ${name}, age : ${age}`)
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1750939982264&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;name : nov, age : 20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;Component Export&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;변수뿐만 아니라 React의 Component를 반환하는 것 또한 가능하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;아래 코드는 export default 구문을 사용해 Hello World JSX 블록을 반환한 예제코드이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&lt;b&gt;data.js&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1750940421092&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function hello() {
    return (
        &amp;lt;div&amp;gt;Hello World!&amp;lt;/div&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&lt;b&gt;page.js&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1750940459677&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import hello from &quot;./data.js&quot;

export default function Home() {
  return (
    hello()
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;export default vs named default&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;export default를 사용함으로써 얻는 이점은 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;1. import 하는 대상에게 해당 모듈의 대표 컴포넌트를 명시할 수 있다. Component Export 예제 코드를 예시로 들어보면 page.js에서는 data.js 모듈로부터 hello와 관련된 모듈이 넘어올 것이라는 것을 예측할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;2. 네이밍이 가능하기에 충돌이 날 가능성이 적다.&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;그렇기에 라이브러리나 컴포넌트를 단일로 Export 하는 경우에는 export default를 사용하며,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;유틸 함수 혹은 컴포넌트의 묶음을 보내야 하는 경우에는 named export를 사용하면 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Archive/React</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/363</guid>
      <comments>https://novlog.tistory.com/entry/React-export-default-vs-named-export#entry363comment</comments>
      <pubDate>Thu, 26 Jun 2025 21:26:05 +0900</pubDate>
    </item>
    <item>
      <title>[Next.js] App Router</title>
      <link>https://novlog.tistory.com/entry/Nextjs-App-Router</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;App Router&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;App Router&lt;/span&gt;는 Next.js 13에서 도입된 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;app/ 디렉토리를 기반으로 하는 파일 기반 라우팅&lt;/span&gt; 방식이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000; background-color: #c0d1e7;&quot;&gt;app/ directory 하위에 디렉토리를 하나 생성하면 해당 디렉토리가 라우팅 주소가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;예시로 app/ 바로 아래에 list directory를 생성, list directory 하위에 page.js 파일을 정의하면&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;웹 페이지 상에서 http://localhost:port/list 로 요청 시 list directory의 page.js가 출력된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;Next.js로 생성한 프로젝트 폴더 구조를 경로로 표시하면 아래와 같다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;page.js는 하나의 경로에 단일 UI를 표시한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;569&quot; data-origin-height=&quot;346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9uZty/btsORW2MWDy/eVam0oL6zkKKMEW7y9dUok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9uZty/btsORW2MWDy/eVam0oL6zkKKMEW7y9dUok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9uZty/btsORW2MWDy/eVam0oL6zkKKMEW7y9dUok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9uZty%2FbtsORW2MWDy%2FeVam0oL6zkKKMEW7y9dUok%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;479&quot; height=&quot;291&quot; data-origin-width=&quot;569&quot; data-origin-height=&quot;346&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;layout.js&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;위에 Next.js로 생성한 프로젝트 구조를 보면 app 디렉토리 하위에 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;layout.js&lt;/span&gt;라는 파일이 존재한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;layout.js는 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;App Router (App/ Directory를 기반으로 하는 라우팅 시스템) 에서 공유하는 레이아웃을 구성하기 위한 파일&lt;/span&gt;로&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;해당 경로의 모든 하위 페이지에 적용되는 공통 UI를 정의하는 데 사용된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;예시로 &lt;b&gt;app/page.js, info/mypage/page.js, list/page.js에 동일한 header, footer 컴포넌트를 추가해야 하는 상황을 가정&lt;/b&gt;해 보자.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;그러면 &lt;b&gt;중복되는 Html 코드를 모두 3개의 page.js 파일에 작성&lt;/b&gt;해 주어야 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;하지만 app 디렉토리의&lt;b&gt; layout.js에 3가지 파일에 공통적으로 추가할 UI 코드를 명시해 주면 모든 파일에 해당 코드가 추가되어 렌더링&lt;/b&gt;된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;layout.js 코드를 뜯어보면 {children} 이라는 코드가 있는데 해당 위치에 page.js가 삽입된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1750918240755&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function RootLayout({ children }) {
  return (
    &amp;lt;html lang=&quot;en&quot;&amp;gt;
      &amp;lt;body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      &amp;gt;
     // 공통으로 추가할 코드 
        &amp;lt;header&amp;gt;공통 헤더&amp;lt;/header&amp;gt;
        {children}
      	&amp;lt;footer&amp;gt;공통 푸터&amp;lt;/footer&amp;gt;
      // 공통으로 추가할 코드        
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;이처럼 layout.js는 Next.js App Router에서 필수적인 구성요소로 Header, Footer, Navbar 같이 다수의 Component에서 공통적으로 사용하는 UI를 깔끔하게 관리, 정의할 수 있는 기능을 제공한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Archive/Next.js</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/362</guid>
      <comments>https://novlog.tistory.com/entry/Nextjs-App-Router#entry362comment</comments>
      <pubDate>Thu, 26 Jun 2025 15:16:08 +0900</pubDate>
    </item>
    <item>
      <title>[React] JSX</title>
      <link>https://novlog.tistory.com/entry/React-JSX</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;JSX란?&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;JavaScript XML&lt;/span&gt;의 줄임말로 JavaScript Code 내부에 테그 기반 문법을 사용할 수 있도록 해주는 기술이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;React Framework는 JSX를 웹 페이지가 이해 가능한 JavaScript Code로 변환한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;JSX Rule&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;1. return 메서드 내부에는 반드시 하나의 태그로 감싸야 한다.&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;아래와 같이 p태그 2개를 병렬로 중첩하는 형태는 불가능하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1750596041821&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  return (
      &amp;lt;p&amp;gt;test1&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;test2&amp;lt;/p&amp;gt;
  )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;여러 요소를 리턴하기 위해서는 하나의 태그로 감싸 주어야 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1750596103349&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;test1&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;test2&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;2. Tag에 class를 적용할 때 class 대신 className을 사용하자.&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;html 코드에 style을 지정할떄는 class라는 수식어를 사용한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;그러나 JSX는 Javascript의 class 예약어와의 혼동을 피하기 위해 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;className&lt;/span&gt;을 사용한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;기존 HTML 방식&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1750596291946&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p class=&quot;title&quot;&amp;gt;novDev&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;JSX 방식&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1750596323368&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p className=&quot;title&quot;&amp;gt;novDev&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;3. JSX Variable DataBinding&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;JSX 내부에서 JavaScript 표현식을 사용할 때 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;{ } 내부에 명시&lt;/span&gt;한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;이는 반드시 문자열 뿐 만 아니라 속성 내부에 바인딩하는 것 또한 가능하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1750596547801&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  let name = &quot;nov&quot;
  let link = 'http://google.com'
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&quot;&amp;gt;by dev { name }&amp;lt;/p&amp;gt;
      &amp;lt;a href= {link}&amp;gt;Link&amp;lt;/a&amp;gt;
    &amp;lt;/div&amp;gt;
  )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;4. JSX Style 적용 방법&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;기존에 Html에서 태그 내부에 style을 적용할때&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt; style=&quot;&quot;&lt;/span&gt;와 같이 명시했다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;그러나 JSX 문법에서는 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;{{}} 기호 내부에 스타일 그리고 속성을 명시&lt;/span&gt;해야 한다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;또한 이는 자바스트립트 객체 자료형 이기에 스&lt;b&gt;타일 이름에 대시(-)를 쓸 수 없으며, 카멜케이스로 구분&lt;/b&gt;해야 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;font-size:'30px' (x)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;fontSize:'30px' (o)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1750596702534&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h4 style={{ fontSize: '30px'}}&amp;gt;style test&amp;lt;/h4&amp;gt;
    &amp;lt;/div&amp;gt;
  )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Archive/React</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/361</guid>
      <comments>https://novlog.tistory.com/entry/React-JSX#entry361comment</comments>
      <pubDate>Sun, 22 Jun 2025 21:54:23 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.js] Composition API v-for</title>
      <link>https://novlog.tistory.com/entry/Vuejs-Composition-API-v-for</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; background-color: #c0d1e7; color: #000000;&quot;&gt;vue.js 공식문서, GPT를 통해 학습한 내용을 정리합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #fafafa; color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; background-color: #c0d1e7; color: #000000;&quot;&gt;잘못된 내용을 포함하고 있을 수 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;#v-for&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;v-for&lt;/span&gt; 문법을 사용하면 반복되는 HTML 컴포넌트를 마치 일반 프로그래밍 언어의 For문을 사용하듯 반복할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;예시로 아래와 같이 동일한 div 태그 컴포넌트가 3개 반복되는 구조가 있다고 가정해 보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748757564831&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;            &amp;lt;div&amp;gt;
                &amp;lt;a&amp;gt;ID&amp;lt;/a&amp;gt;
                &amp;lt;a&amp;gt;PW&amp;lt;/a&amp;gt;
                &amp;lt;a&amp;gt;About&amp;lt;/a&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div &amp;gt;
                &amp;lt;a&amp;gt;ID&amp;lt;/a&amp;gt;
                &amp;lt;a&amp;gt;PW&amp;lt;/a&amp;gt;
                &amp;lt;a&amp;gt;About&amp;lt;/a&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div&amp;gt;
                &amp;lt;a&amp;gt;ID&amp;lt;/a&amp;gt;
                &amp;lt;a&amp;gt;PW&amp;lt;/a&amp;gt;
                &amp;lt;a&amp;gt;About&amp;lt;/a&amp;gt;
            &amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;296&quot; data-origin-height=&quot;134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJLCw6/btsOmrHVGuQ/rKDmViikRIzc5v20wlLDa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJLCw6/btsOmrHVGuQ/rKDmViikRIzc5v20wlLDa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJLCw6/btsOmrHVGuQ/rKDmViikRIzc5v20wlLDa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJLCw6%2FbtsOmrHVGuQ%2FrKDmViikRIzc5v20wlLDa1%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;223&quot; height=&quot;101&quot; data-origin-width=&quot;296&quot; data-origin-height=&quot;134&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;이를 vue.js의 v-for 문법을 사용하면 아래와 같이 한 개의 div Tag로 축약이 가능하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;(출력 결과는 동일하다.)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748757649974&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;            &amp;lt;!-- v-for=&quot;변수명 in 반복횟수&quot; :key=&quot;변수명&quot; --&amp;gt;
            &amp;lt;div v-for=&quot;ID in 3&quot; :key=&quot;ID&quot;&amp;gt;
                &amp;lt;a&amp;gt;ID&amp;lt;/a&amp;gt; 
                &amp;lt;a&amp;gt;PW&amp;lt;/a&amp;gt;
                &amp;lt;a&amp;gt;ABOUT&amp;lt;/a&amp;gt;
            &amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;v-for 문법의 사용형식은 다음과 같다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748757688221&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;v-for=&quot;변수명 in 반복횟수&quot; :key=&quot;변수명&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;#객체순회&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;단순히 태그를 반복하는 것뿐만 아니라 반복 횟수 부분에 반복 가능한 변수 (Array, Object) 자료형을 넣어서 데이터 내부의 자료를 순차적으로 출력하는 것 또한 가능하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;Composition API 방식으로 script 태그 내부에 출력할 contents 배열을 선언해 주었다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748757876477&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script setup&amp;gt;

    import { ref } from 'vue' 

    // 데이터를 객체 형식으로 정의 
    const contents = ref([
        'ID', 'PW', 'About'
    ])

&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;다음으로 &amp;lt;template&amp;gt; Tag 내부에 v-for 문법을 사용해 contents 내부 데이터를 순차적으로 화면에 출력한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748757972346&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;a v-for=&quot;content in contents&quot; :key=&quot;content&quot;&amp;gt;{{content}}&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;312&quot; data-origin-height=&quot;52&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y8kLi/btsOlyA6nG5/LSziKdh469aL1IckBhdm61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y8kLi/btsOlyA6nG5/LSziKdh469aL1IckBhdm61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y8kLi/btsOlyA6nG5/LSziKdh469aL1IckBhdm61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy8kLi%2FbtsOlyA6nG5%2FLSziKdh469aL1IckBhdm61%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;234&quot; height=&quot;39&quot; data-origin-width=&quot;312&quot; data-origin-height=&quot;52&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;객체 내부의 데이터를 출력할 때 v-for 문법 형식은 아래와 같다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748758041284&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;v-for &quot;변수명 in 객체,배열 이름&quot; :key=&quot;변수명&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;#:key&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;&quot;객체, 배열 내부에 중복되는 변수가 없다면&quot;&lt;/span&gt; &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;:key 뒤에 변수명과 순회할 변수명은 통일시켜 주는 것을 권장&lt;/span&gt;한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;굳이 왜 :key를 적어 주어야 하는지 의문이 생길 수 있는데&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;vue.js는 DOM을 렌더링시 가상 DOM (Virtual DOM) 방식을 사용한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;만약 :key가 없으면 항목의 순서만 보고 비교를 시도한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;그렇기에 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;:key를 별도로 명시해 주지 않으면 예상치 못한 렌더링 결과가 생길 수 있기에 적어주도록 하자&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Archive/Vue.js</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/360</guid>
      <comments>https://novlog.tistory.com/entry/Vuejs-Composition-API-v-for#entry360comment</comments>
      <pubDate>Sun, 1 Jun 2025 15:11:30 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.js] Composition API ref</title>
      <link>https://novlog.tistory.com/entry/Vuejs-Composition-API-ref</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000; background-color: #c0d1e7;&quot;&gt;vue.js 공식문서, GPT를 통해 학습한 내용을 정리합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000; background-color: #c0d1e7;&quot;&gt;잘못된 내용을 포함하고 있을 수 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;ref&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;vue.js 3버전 이후부터 HTML 페이지 내부에서 사용될 변수를 저장할 수 있는 기능이 추가되었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;ref()&lt;/span&gt;로 묶어 내부에 다양한 타입의 변수 (Array, Object,..)를 선언할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;#일반 변수 With ref&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;우선 &amp;lt;script&amp;gt; Tag 내부에서 ref를 사용하기 위해서 import 해준다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748679449553&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
    import { ref } from 'vue' 
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;다음으로 const type count 변수를 하나 선언해 주었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748679545156&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
    import { ref } from 'vue' 
    const count = ref(3);
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;선언한 count 변수의 내부값 3은 count.value 에 들어있는데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&amp;lt;template&amp;gt; &amp;lt;/template&amp;gt; 태그 내부에서 바인딩 문법을 사용해 count 변수에 접근하고자 할 때는&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;{{ count.value }} 같은 형태가 아니라 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;{{ count }}&lt;/span&gt; 와 같이 접근해야 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;그 이유는&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt; 자동으로 ref를 탬플릿 내부에서 unwrapping&lt;/span&gt; 하기 때문이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;따라서 {{ count.value }}라고 작성 시 실제로 Vue 내부적으론 count.value.value로 인식해 Undefined가 출력될 가능성이 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1748679756599&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;
            &amp;lt;div&amp;gt;
                ref Test : {{ count }}
            &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;186&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k0I2S/btsOmuEreMQ/vtb5kf95jqGOtMLcIrpJp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k0I2S/btsOmuEreMQ/vtb5kf95jqGOtMLcIrpJp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k0I2S/btsOmuEreMQ/vtb5kf95jqGOtMLcIrpJp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk0I2S%2FbtsOmuEreMQ%2Fvtb5kf95jqGOtMLcIrpJp0%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;124&quot; height=&quot;48&quot; data-origin-width=&quot;186&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;#객체 With ref&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;Array, Object&lt;/span&gt; 타입도 똑같이 정의해주면 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;3개의 문자열을 가지고 있는 contents 배열을 ref를 사용해 선언해 준 뒤 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;v-for 문법을 이용해 차례로 &amp;lt;div&amp;gt; 태그 내부에 출력&lt;/span&gt;한 예제이다. &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748679916017&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
    import { ref } from 'vue' 
    // 데이터를 객체 형식으로 정의 
    const contents = ref([
        'ID', 'PW', 'About'
    ])
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1748680009191&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;            &amp;lt;template&amp;gt;
            &amp;lt;div class=&quot;menu&quot;&amp;gt;
                &amp;lt;a v-for=&quot;content in contents&quot; :key=&quot;content&quot;&amp;gt;{{content}}&amp;lt;/a&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;182&quot; data-origin-height=&quot;64&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pNgIg/btsOk9BBzQA/OjLEqt3e334kQ3cCwVf6MK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pNgIg/btsOk9BBzQA/OjLEqt3e334kQ3cCwVf6MK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pNgIg/btsOk9BBzQA/OjLEqt3e334kQ3cCwVf6MK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpNgIg%2FbtsOk9BBzQA%2FOjLEqt3e334kQ3cCwVf6MK%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;131&quot; height=&quot;46&quot; data-origin-width=&quot;182&quot; data-origin-height=&quot;64&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Archive/Vue.js</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/359</guid>
      <comments>https://novlog.tistory.com/entry/Vuejs-Composition-API-ref#entry359comment</comments>
      <pubDate>Sat, 31 May 2025 17:29:35 +0900</pubDate>
    </item>
    <item>
      <title>[Linux] chmod - 파일 실행 권한 확인 및 부여 명령어</title>
      <link>https://novlog.tistory.com/entry/Linux-chmod-%ED%8C%8C%EC%9D%BC-%EC%8B%A4%ED%96%89-%EA%B6%8C%ED%95%9C-%ED%99%95%EC%9D%B8-%EB%B0%8F-%EB%B6%80%EC%97%AC-%EB%AA%85%EB%A0%B9%EC%96%B4</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;h2 Database sh 파일 실행 시 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;premission denied&lt;/span&gt; 접근 권한 없음 현상 발생&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746851608811&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(base) xxx@xxx-ui-noteubug bin % ./h2.sh
zsh: permission denied: ./h2.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;ls -l&lt;/span&gt; 명령어를 입력해 파일 접근 권한 출력&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;가장 앞의 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;-는 일반 파일, d는 디렉토리&lt;/span&gt;를 의미한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;그 뒤 9문자는 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;소유자, 그룹, 다른 사용자의 r(읽기) w(쓰기) x(실행) 권한&lt;/span&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746851685965&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(base) xxx@Xxx-ui-noteubug bin % ls -l h2.sh
-rw-rw-r--@ 1 xxx  staff  109  8 10  2024 h2.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;chmod +x file_name&lt;/span&gt; 으로 해당 파일에 대한 실행 권한 추가 후 실행&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746851832208&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(base) xxx@xxx-ui-noteubug bin % chmod +x h2.sh
(base) xxx@xxx-ui-noteubug bin % ./h2.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;정상적으로 동작된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;698&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkMY0p/btsNTxVDjyt/HOwyTL95JANjycXHzsFWR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkMY0p/btsNTxVDjyt/HOwyTL95JANjycXHzsFWR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkMY0p/btsNTxVDjyt/HOwyTL95JANjycXHzsFWR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkMY0p%2FbtsNTxVDjyt%2FHOwyTL95JANjycXHzsFWR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;385&quot; height=&quot;299&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;698&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Archive/Linux</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/358</guid>
      <comments>https://novlog.tistory.com/entry/Linux-chmod-%ED%8C%8C%EC%9D%BC-%EC%8B%A4%ED%96%89-%EA%B6%8C%ED%95%9C-%ED%99%95%EC%9D%B8-%EB%B0%8F-%EB%B6%80%EC%97%AC-%EB%AA%85%EB%A0%B9%EC%96%B4#entry358comment</comments>
      <pubDate>Sat, 10 May 2025 13:39:32 +0900</pubDate>
    </item>
    <item>
      <title>[Algorithm] Graph 용어 정리</title>
      <link>https://novlog.tistory.com/entry/Algorithm-Graph-%EC%9A%A9%EC%96%B4-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;* 교내 동아리 알고리즘 학습 자료로 사용하기 위해 작성된 글입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;잘못된 내용을 포함하고 있을 수 있습니다.&lt;/span&gt;&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;&lt;span style=&quot;font-family: 'Noto Sans Light'; background-color: #c0d1e7; color: #000000;&quot;&gt;&lt;b&gt;Graph란?&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;그래프(Graph)는 노드와 노드를 연결하는 간선으로 구성되며 이러한 노드와 간선의 관계를 나타내는 자료구조이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;그래프는 다양한 알고리즘에서 사용되는데 SNS 플랫폼의 경우 친구 추천 알고리즘, 지도, 네비게이션 알고리즘 등에서 사용된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;이번 글에서는 그래프 학습 이전에 반드시 알아야할 개념에 대해 정리해 보고자 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; background-color: #c0d1e7; color: #000000;&quot;&gt;&lt;b&gt;그래프 용어&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;b&gt;1. 노드 (Vertex, 정점)&lt;/b&gt; 그래프에서 데이터를 담고있는 단위이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;b&gt;2. 간선 (Edge)&lt;/b&gt;&amp;nbsp;노드 사이를 연결하는 선. 노드와의 관계를 나타낸다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;(아래 그래프는 5개의 정점, 5개의 간선으로 이루어진 그래프이다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;3. 차수 (Degree)&lt;/b&gt;&amp;nbsp;특정 노드와 간선으로 연결되어 있는 노드의 수&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3Lx51/btsNPcru3TT/2RsKvXq2R3vw3by4vPQTx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3Lx51/btsNPcru3TT/2RsKvXq2R3vw3by4vPQTx0/img.png&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;548&quot; data-is-animation=&quot;false&quot; width=&quot;407&quot; data-widthpercent=&quot;53.98&quot; style=&quot;width: 53.3553%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3Lx51/btsNPcru3TT/2RsKvXq2R3vw3by4vPQTx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3Lx51%2FbtsNPcru3TT%2F2RsKvXq2R3vw3by4vPQTx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1132&quot; height=&quot;548&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kPd9m/btsNNQiqtqA/8SY5aHicAOqMDLeUisD6V0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kPd9m/btsNNQiqtqA/8SY5aHicAOqMDLeUisD6V0/img.png&quot; data-origin-width=&quot;1134&quot; data-origin-height=&quot;644&quot; data-is-animation=&quot;false&quot; width=&quot;444&quot; style=&quot;width: 45.4819%;&quot; data-widthpercent=&quot;46.02&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kPd9m/btsNNQiqtqA/8SY5aHicAOqMDLeUisD6V0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkPd9m%2FbtsNNQiqtqA%2F8SY5aHicAOqMDLeUisD6V0%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;1134&quot; height=&quot;644&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;4. 리프노드 (Leaf Node)&amp;nbsp; 차수가 1인 노드 &lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000; text-align: start;&quot;&gt;- 이진트리의 단말노드와 리프노드의 개념을 혼동하는 사람이 많은데, 리프노드의 정확한 정의는 차수가 1인 노드이다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000; text-align: start;&quot;&gt;(즉, 자식을 가지지 않는 노드를 의미한다.)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pusu4/btsNPa8lH71/zUOptFO9BrKFpjuszV3Iq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pusu4/btsNPa8lH71/zUOptFO9BrKFpjuszV3Iq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pusu4/btsNPa8lH71/zUOptFO9BrKFpjuszV3Iq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpusu4%2FbtsNPa8lH71%2FzUOptFO9BrKFpjuszV3Iq0%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;330&quot; height=&quot;198&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;b&gt;5. 무방향 그래프 [Undirected Graph]&lt;/b&gt;&amp;nbsp; 간선이 방향성을 가지지 않는 그래프&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;b&gt;6. 방향 그래프 [Directed Graph]&amp;nbsp;&lt;/b&gt;&amp;nbsp; 간선이 방향성을 가지는 그래프&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;610&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NYal6/btsNOURX4ZI/j96P7ttZdLW6DaKd9kN1Jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NYal6/btsNOURX4ZI/j96P7ttZdLW6DaKd9kN1Jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NYal6/btsNOURX4ZI/j96P7ttZdLW6DaKd9kN1Jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNYal6%2FbtsNOURX4ZI%2Fj96P7ttZdLW6DaKd9kN1Jk%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;592&quot; height=&quot;221&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;610&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000; text-align: start;&quot;&gt;* 간선은 방향성을 가질 수 있으며, 방향성을 가지지 않는 그래프를 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;무방향 그래프 [Undriected Graph]&lt;/span&gt; , 방향성을 가지는 그래프를 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;방향 그래프 [Directed Graph]라고 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;7. 사이클 &lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;사이클이란 한 정점에서 출발하여 자기 자신으로 돌아올 수 있는 경로를 의미한다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000; text-align: start;&quot;&gt;이러한 &quot;사이클&quot;을 가지는 그래프를 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;순환 그래프 (Cylclic Graph),&lt;/span&gt; 사이클을 가지지 않는 그래프를 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;비순환 그래프 (Acyclic Graph)라고&lt;/span&gt; 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1658&quot; data-origin-height=&quot;618&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bW0KZH/btsNPGyQg5G/cfV971HzkzCah7VK1YM4ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bW0KZH/btsNPGyQg5G/cfV971HzkzCah7VK1YM4ak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bW0KZH/btsNPGyQg5G/cfV971HzkzCah7VK1YM4ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbW0KZH%2FbtsNPGyQg5G%2FcfV971HzkzCah7VK1YM4ak%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;689&quot; height=&quot;257&quot; data-origin-width=&quot;1658&quot; data-origin-height=&quot;618&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Archive</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/357</guid>
      <comments>https://novlog.tistory.com/entry/Algorithm-Graph-%EC%9A%A9%EC%96%B4-%EC%A0%95%EB%A6%AC#entry357comment</comments>
      <pubDate>Thu, 8 May 2025 15:45:49 +0900</pubDate>
    </item>
    <item>
      <title>[Nimda] 1. Git 사용을 위한 기초 터미널 유닉스 명령어 정리</title>
      <link>https://novlog.tistory.com/entry/Nimda-1-Git-%EC%82%AC%EC%9A%A9%EC%9D%84-%EC%9C%84%ED%95%9C-%EA%B8%B0%EC%B4%88-%ED%84%B0%EB%AF%B8%EB%84%90-%EC%9C%A0%EB%8B%89%EC%8A%A4-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Git이란?&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;깃에 대해서 배우기 전에 왜 깃을 배워야 하는지 의문이 생길 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아마 대학교 새내기 때 과제를 하면서 파일을 아래처럼 정리해 본 경험이 한 번쯤은 있을 것이다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;202&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s7DRC/btsNJ4sOD3Y/A6TUAkNAksUQ2zMld168bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s7DRC/btsNJ4sOD3Y/A6TUAkNAksUQ2zMld168bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s7DRC/btsNJ4sOD3Y/A6TUAkNAksUQ2zMld168bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs7DRC%2FbtsNJ4sOD3Y%2FA6TUAkNAksUQ2zMld168bk%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;357&quot; height=&quot;117&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;202&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;좀 학년이 올라가면 아래처럼 진화한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Hj9c7/btsNJpqRZ8q/8ZLvukpUsgHiestp9G0qp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Hj9c7/btsNJpqRZ8q/8ZLvukpUsgHiestp9G0qp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Hj9c7/btsNJpqRZ8q/8ZLvukpUsgHiestp9G0qp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHj9c7%2FbtsNJpqRZ8q%2F8ZLvukpUsgHiestp9G0qp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;381&quot; height=&quot;127&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;204&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;혼자 개발하는 거면 어떻게 저장하든 상관없는데&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;만약 코드 하나를 가지고 여러 명에서 협업을 한다면?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;저렇게 소스코드를 하나하나 저장해서 카톡으로 주고받을 수도 있겠지만&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;만약 코드가 10기가가 넘는다면?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;그리고 특정 프로그래머가 코드 수정을 끝마치기 전까지 다른 프로그래머는 그냥 기다려야 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;또 만약 나는 열심히 코드를 짰는데&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;다른 팀원이 짠 코드랑 구조가 아예 달라져버린다면?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;처음부터 코드를 다시 짜야 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;그래서 이런 개발자의 고충을 덜어주기 위해 버전관리툴 (VCS)라는 게 등장한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;수십 년간 수많은 버전관리툴이 등장했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;그중에 살아남은 가장 유명한 버전관리툴이 Git이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;여하튼 협업을 하려면&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;Git은 반드시 배워야 한다.... 정도만 이해하고 넘어가고&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;궁금하면 구글링으로 따로 더 찾아보면 좋을 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;Terminal이란?&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;Unix 기반 명령어를 사용할 수 있도록 도와주는 쉘 환경.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;우리가 평소에 익숙한 마우스 클릭으로 프로그램을 실행하는 방식을 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;GUI (Graphic User Interface) 방식이라고 하며,&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000; text-align: center;&quot;&gt;텍스트 기반으로 컴퓨터와 상호작용하는 방식을 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;CLI (Command Line Interface) 방식이라고&lt;/span&gt; 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;span style=&quot;text-align: center;&quot;&gt;그러면 편하게 GUI로 코딩을 하면 되지 왜 불편하게 CLI를 사용해야 할까?&lt;/span&gt;&lt;span style=&quot;text-align: center;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000; text-align: center;&quot;&gt;우리가 앞으로 사용할 Git이라는 버전관리툴이 CLI를 기반으로 동작하기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;따라서 Git 버전관리툴을 사용하기 위해서는 컴퓨터와 텍스트를 기반으로 상호작용하기 위해서는&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;기초적인 터미널 명령어에 대한 이해가 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;잠시 터미널에서 사용되는 기초 유닉스 명령어를 살펴보기 전에&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;우선 Git부터 설치해야 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;근데 하나하나 다 설명해 주기엔 너무 시간이 오래 걸려서 다음 블로그 보고 깃을 설치하도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://junesker.tistory.com/95&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://junesker.tistory.com/95&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1746170359445&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Github] Git 설치하기 / Git 설치방법 / Git 다운로드&quot; data-og-description=&quot;'Git은 빠르고 효율적으러 작은 프로젝트부터 대규모 프로젝트까지 모든 것을 처리하도록 설계된 무료 오픈 소스 분산형 버전 제어 시스템이다. Git은 배우기 쉽고 매우 작은 면적과 번개처럼 빠&quot; data-og-host=&quot;junesker.tistory.com&quot; data-og-source-url=&quot;https://junesker.tistory.com/95&quot; data-og-url=&quot;https://junesker.tistory.com/95&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/SCf90/hyYIdmUcCy/fLjhDvmfkf0jDukAQbDlnK/img.png?width=800&amp;amp;height=517&amp;amp;face=0_0_800_517,https://scrap.kakaocdn.net/dn/cgmeDD/hyYPqRRgg1/Ql1BLAn0JW7cQe6mBjGBNK/img.png?width=800&amp;amp;height=517&amp;amp;face=0_0_800_517,https://scrap.kakaocdn.net/dn/hQieg/hyYM1yzcrg/tqFzD21MVZoBuW2RcKoeLK/img.png?width=1920&amp;amp;height=1039&amp;amp;face=0_0_1920_1039&quot;&gt;&lt;a href=&quot;https://junesker.tistory.com/95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://junesker.tistory.com/95&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/SCf90/hyYIdmUcCy/fLjhDvmfkf0jDukAQbDlnK/img.png?width=800&amp;amp;height=517&amp;amp;face=0_0_800_517,https://scrap.kakaocdn.net/dn/cgmeDD/hyYPqRRgg1/Ql1BLAn0JW7cQe6mBjGBNK/img.png?width=800&amp;amp;height=517&amp;amp;face=0_0_800_517,https://scrap.kakaocdn.net/dn/hQieg/hyYM1yzcrg/tqFzD21MVZoBuW2RcKoeLK/img.png?width=1920&amp;amp;height=1039&amp;amp;face=0_0_1920_1039');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Github] Git 설치하기 / Git 설치방법 / Git 다운로드&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;'Git은 빠르고 효율적으러 작은 프로젝트부터 대규모 프로젝트까지 모든 것을 처리하도록 설계된 무료 오픈 소스 분산형 버전 제어 시스템이다. Git은 배우기 쉽고 매우 작은 면적과 번개처럼 빠&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;junesker.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;Mac OS - Terminal, Window OS - 명령프롬프트&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;각 운영체제에 맞게 터미널을 실행시켜 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000; background-color: #c0d1e7;&quot;&gt;(윈도우의 경우 아래와 같이 따라가면 명령프롬프트를 실행시킬 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000; background-color: #c0d1e7;&quot;&gt;윈도우 + S &amp;rarr; cmd 입력 &amp;rarr; Enter)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;182&quot; data-origin-height=&quot;228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btNGhJ/btsNHM1muvX/6kSnlixwx7sc4ekLxkiwMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btNGhJ/btsNHM1muvX/6kSnlixwx7sc4ekLxkiwMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btNGhJ/btsNHM1muvX/6kSnlixwx7sc4ekLxkiwMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtNGhJ%2FbtsNHM1muvX%2F6kSnlixwx7sc4ekLxkiwMK%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;81&quot; height=&quot;101&quot; data-origin-width=&quot;182&quot; data-origin-height=&quot;228&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;시작하기 전에 깃이 잘 깔려있는지 확인해 보자.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;다음 명령어를 따라 쳐보면&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746170423777&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git --version&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;이렇게 버전이 출력되는데 그러면 잘 깔린 거다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;이제 본격적으로 리눅스 명령어를 배워보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746170437805&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git version 2.47.0&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;pwd (print working directory)&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;pwd는 현재 프로그래머가 위치한 경로를 출력하는 명령어다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;여기서 directory라는 단어가 생소할 수 있는데,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;윈도우 운영체제에서 폴더를 유닉스 운영체제에서는 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;디렉토리(Directory)&lt;/span&gt; 라고 칭한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;터미널에서 pwd를 입력해 보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746164162401&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pwd&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1746169884128&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/Users/choedoil&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Light';&quot;&gt;현재 /Users/choedoil 이라는 경로에 위치하고 있다는 의미이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;ls (list)&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Light';&quot;&gt;ls는 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;현재 경로에 위치한 디렉터리 혹은 파일 목록을 출력해 주는&lt;/span&gt; 명령어다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Light'; background-color: #c0d1e7;&quot;&gt;(앞으로 계속 폴더 대신 디렉토리라는 용어를 사용할 예정이니 잘 숙지하도록 하자)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746169864443&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ls&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1746164374981&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
Anaconda3-2024.02-1-MacOSX-arm64.sh
Applications
Applications (Parallels)
CLionProjects
Desktop
Documents
...&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;하지만 이렇게 출력하면 해당 항목들이 파일인지 디렉토리인지 구분이 불가능하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;따라서 우측에 -l 옵션을 주면 항목들의 세부적인 정보를 출력할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;ls -l&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;전과 다르게 이상한 영어가 추가됐는데&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;이렇게 명령어 옆에 -로 붙이는 것을 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;옵션(Option)이라고 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;옵션을 붙이면 명령어 기능을 추가할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;맨 앞의 글자만 확인하면 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;가장 앞에 있는 심볼이&lt;b&gt; &quot;-&quot;로 시작하면 파일&lt;/b&gt;이라는 뜻이며,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;b&gt;&quot;d&quot;로 시작하면 디렉토리&lt;/b&gt;라는 의미다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1746169855377&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ls -l&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1746169576408&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
-rw-r--r--@   1 xxx  staff      736841  4 11  2024 기타db.pptx
-rw-r--r--@   1 xxx  staff     3778490  4 12  2024 자바수업2주차.pptx
drwx------@  36 xxx  staff        1152  5  2 15:58 Desktop
drwx------+  34 xxx  staff        1088  4 25 14:01 Documents
drwx------+ 152 xxx  staff        4864  5  2 15:22 Downloads
...&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;이제 어떤 항목이 디렉토리인지 확인했으니&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;원하는 디렉토리로 들어가 새로운 폴더를 생성해 보도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;cd (change Directory)&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;cd 명령어는 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;특정 디렉토리로 이동하는 명령어&lt;/span&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;윈도우에서 그냥 폴더를 더블클릭하는 동작을 CLI 명령으로 바꾼 거라고 이해하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;cd 뒤에 이동할 디렉토리 명을 명시하면 되는데&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;나는 Desktop (바탕화면)으로 이동하고 싶으니 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;cd Desktop이라는&lt;/span&gt; 명령어를 커맨드 라인에 입력한 뒤,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt; ls명령어를 사용해 바탕화면에 있는 폴더를 출력해 보았다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746169825681&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd Desktop
ls&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1746169837019&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
$RECYCLE.BIN
9주차 강의자료
GitTest
Icon?
Myls.dmg
Resource
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;mkdir (make directory)&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;다음으로 디렉토리를 생성해 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;바탕화면에서 &quot;마우스로 우클릭하고 새로운 폴더 만들기&quot;의 CLI 버전&lt;/span&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;(이제 대충 뭐 하는지는 알 것 같아서 이런 식의 설명은 생략하도록 하겠다.)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;cd 명령어와 마찬가지로 mkdir 뒤에 생성할 디렉토리명을 적어주면 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;나는 nimdaEx라는 이름으로 디렉토리를 생성했다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746170074043&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mkdir nimdaEx&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;그다음 cd 명령어를 사용해 nimdaEx 디렉터리로 이동한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746170083682&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd nimdaEx&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;git init&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;이제 nimdaEx 디렉토리로 이동했으니&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;해당 디렉토리를 git이 추적할 수 있도록 설정해야 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;git init &lt;/span&gt;명령어를 사용하면 현재 위치한 디렉토리는 이제 git이 관리하게된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746170788690&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git init&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1746170809424&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/Users/xxx/Desktop/nimdaEx/.git/ 안의 빈 깃 저장소를 다시 초기화했습니다&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;ls -a&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;다음으로 앞서 배운 ls 명령어에서 다른 옵션을 써보도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;-a 옵션&lt;/span&gt;인데 해당 옵션은 디렉토리 내부의 숨긴 파일까지 모두 보여주는 명령이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;그냥 다 보여주면 되지 왜 파일을 숨기냐고 의문이 들 수 있는데,&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;그냥....중요한건 숨겨둔다고 생각하고 넘어가자&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746170948843&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.	..	.git&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;명령어를 입력해 보면.... git 이렇게 3개가 출력될 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000; background-color: #c0d1e7;&quot;&gt;. 은 현재 디렉토리 경로를 저장하는 파일이며&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;.. 은 현재 디렉토리의 상위 디렉토리 경로를 저장하는 파일&lt;/span&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;왜 경로를 저장해두어야 하는지 궁금할 텐데&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;생각해 보면 당연하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;우리는 GUI 환경처럼 마우스클릭으로 폴더를 넘나들수 없기 때문에&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;저렇게 디렉토리마다 경로를 저장해 두는 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;그래서 cd .. 하면 상위 디렉토리로 이동한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;마지막으로. git 이게 중요한데&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;해당 디렉토리를 깃이 관리할 수 있도록 해주는 파일이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;여튼 .git이 보인다면 잘 따라온 거다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;그냥 뭐 어렵게 생각할 것 없이 지금 과정을 요약하자면&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;바탕화면에서 우클릭해서 nimdaEx 폴더 만들고&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;더블클릭해서 해당 폴더로 들어간 거다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;206&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kHlxb/btsNIhm9EQt/Bb6Rpv6jF5hkKcs9Iwh6d0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kHlxb/btsNIhm9EQt/Bb6Rpv6jF5hkKcs9Iwh6d0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kHlxb/btsNIhm9EQt/Bb6Rpv6jF5hkKcs9Iwh6d0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkHlxb%2FbtsNIhm9EQt%2FBb6Rpv6jF5hkKcs9Iwh6d0%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;94&quot; height=&quot;85&quot; data-origin-width=&quot;206&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;왜 굳이 이런 짓을 해야 하냐면...&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;앞에서도 말했듯이 Git을 사용하기 위해선 커맨드에서 폴더를 만들고, &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;이동하고 생성하는 방법에 대해서 알아야 하기 때문이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;지금까지 정말 깃을 사용하기 위한 최소한의 명령어만을 배워봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;우리가 리눅스를 공부하는 게 아니니까 더 자세하고 세부적인 내용은 리눅스마스터 2급이나 LPIC 자격증을 준비하면서 공부해도 좋고&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;궁금하다면 따로 알아서 찾아보도록 하자.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;다음에는 대면으로 깃 기초하고 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;깃허브 사용법에 대해서 수업할 예정이니까&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;다른 건 몰라도 정말 아래 명령어만큼은&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;정말 정말 기초적인 명령어니까&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt; 꼭 숙지하도록 하자&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;pwd&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;cd&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;ls&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;ls -l&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;ls -a&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;mkdir&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;cd ..&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;git init&lt;/span&gt;&lt;/p&gt;</description>
      <category>Archive</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/356</guid>
      <comments>https://novlog.tistory.com/entry/Nimda-1-Git-%EC%82%AC%EC%9A%A9%EC%9D%84-%EC%9C%84%ED%95%9C-%EA%B8%B0%EC%B4%88-%ED%84%B0%EB%AF%B8%EB%84%90-%EC%9C%A0%EB%8B%89%EC%8A%A4-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%A0%95%EB%A6%AC#entry356comment</comments>
      <pubDate>Fri, 2 May 2025 16:47:39 +0900</pubDate>
    </item>
    <item>
      <title>[Cpp] STL 2D Vector</title>
      <link>https://novlog.tistory.com/entry/Cpp-STL-2D-Vector</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;Cpp STL의 &lt;span style=&quot;background-color: #1a5490; color: #dddddd;&quot;&gt;2차원 벡터를 생성하는 방법에 대해 정리한 문서&lt;/span&gt;입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;개인적인 공부 내용을 정리하는 용도로 작성한 글이기에 잘못된 내용을 포함하고 있을 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;2차원 벡터를 선언하는 방법은 크게 2가지로 구분됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;Case1. 벡터 내부에 벡터를 선언하여 2차원 벡터를 생성하는 방식&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;Case2. &lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000; text-align: center;&quot;&gt;1차원 벡터 배열을 사용해 2차원 벡터처럼 사용하는 방식&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;1번째 케이스는 Row, Column이 모두 동적인 벡터로 벡터 안에 백터가 들어가 있는 형태입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;반면 2번째 케이스는 Row는 정적이지만 Column은 동적인 벡터로 1차원 벡터를 배열 형태로 선언하여&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;실제로는 1차원 벡터이지만 2차원 벡터와 같이 사용 가능합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;주로 그래프 관련 알고리즘 문제를 풀이할때 인접리스트를 정의할때 2번 방식을 자주 사용합니다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;#1 &lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000; text-align: center;&quot;&gt;벡터 내부에 벡터를 선언하여 2차원 벡터를 생성하는 방식&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #1a5490; color: #dddddd;&quot;&gt;vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; vector_name&lt;/span&gt; 형태로 선언합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;행과 열이 모두 동적인 형태로 할당됩니다. &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1745904388116&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
using namespace std;

int main() {
    
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; myVec; // Row : Dynamic, Column : Dynamic
    cout &amp;lt;&amp;lt; &quot;Row, Column이 모두 동적인 벡터&quot; &amp;lt;&amp;lt; endl;
    
    // 0~2번 Row 추가
    myVec.push_back(vector&amp;lt;int&amp;gt;()); // 0번 Row 추가
    myVec.push_back(vector&amp;lt;int&amp;gt;()); // 1번 Row 추가
    myVec.push_back(vector&amp;lt;int&amp;gt;()); // 2번 Row 추가
    
    myVec[0].push_back(1);
    myVec[0].push_back(1);
    myVec[0].push_back(1);
    
    myVec[1].push_back(10);
    myVec[1].push_back(10);
    myVec[1].push_back(10);
    myVec[1].push_back(10);
    
    myVec[2].push_back(100);
    myVec[2].push_back(100);
    
    for (int row = 0; row &amp;lt; myVec.size(); row++) {
        cout &amp;lt;&amp;lt; row &amp;lt;&amp;lt; &quot;번 째 행 - &quot;;
        for (int column = 0; column &amp;lt; myVec[row].size(); column++) {
            cout &amp;lt;&amp;lt; myVec[row][column] &amp;lt;&amp;lt; &quot; &quot;;
        }
        cout &amp;lt;&amp;lt; endl;
    }
    
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1745904591850&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Row, Column이 모두 동적인 벡터
0번 째 행 - 1 1 1 
1번 째 행 - 10 10 10 10 
2번 째 행 - 100 100 
Program ended with exit code: 0&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;#2 &lt;span style=&quot;text-align: center;&quot;&gt;1차원 벡터 배열을 사용해 2차원 벡터처럼 사용하는 방식&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #1a5490; color: #dddddd;&quot;&gt;vector&amp;lt;int&amp;gt; vector_name[n]&lt;/span&gt; 형태로 선언합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;실제로 2차원 벡터는 아니지만 2차원 벡터 처럼 사용 가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1745976004619&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
using namespace std;

int main() {
    
    vector&amp;lt;int&amp;gt; myVec[3];
    
    // 0번째 Row에 Column 추가
    myVec[0].push_back(1);
    myVec[0].push_back(1);
    myVec[0].push_back(1);
    
    // 1번째 Row에 Column 추가
    myVec[1].push_back(2);
    myVec[1].push_back(2);
    myVec[1].push_back(2);
    
    // 2번째 Row에 Column 추가
    myVec[2].push_back(3);
    myVec[2].push_back(3);
    
    for (int row = 0; row &amp;lt; 3; row++) {
        cout &amp;lt;&amp;lt; &quot;#&quot; &amp;lt;&amp;lt; row &amp;lt;&amp;lt; &quot;번 행 - &quot;;
        for (int column = 0; column &amp;lt; myVec[row].size(); column++) {
            cout &amp;lt;&amp;lt; myVec[row][column] &amp;lt;&amp;lt; &quot; &quot;;
        }
        cout &amp;lt;&amp;lt; endl;
    }
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1745976022271&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#0번 행 - 1 1 1 
#1번 행 - 2 2 2 
#2번 행 - 3 3 
Program ended with exit code: 0&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/PS With C++</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/355</guid>
      <comments>https://novlog.tistory.com/entry/Cpp-STL-2D-Vector#entry355comment</comments>
      <pubDate>Wed, 30 Apr 2025 10:22:13 +0900</pubDate>
    </item>
    <item>
      <title>[Linux] 특정 프로세스 포트번호 확인 후 Kill 방법 정리 (lsof, kill)</title>
      <link>https://novlog.tistory.com/entry/Linux-%ED%8A%B9%EC%A0%95-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%ED%8F%AC%ED%8A%B8%EB%B2%88%ED%98%B8-%ED%99%95%EC%9D%B8-%ED%9B%84-Kill-%EB%B0%A9%EB%B2%95-%EC%A0%95%EB%A6%AC-lsof-kill</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;인텔리제이를 사용해 스프링부트 프로젝트를 여러 개 실습하다 보니&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;프로세스 포트를 제대로 끊어주지 않아 포트가 충돌하는 오류가 빈번하게 발생했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;매 번 찾아보는것도 번거로워서 그냥 블로그에 따로 정리해 두고자 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;713&quot; data-origin-height=&quot;128&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kHsHv/btsNA3okzhx/9lcIXzK253KpcUtLYiDKc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kHsHv/btsNA3okzhx/9lcIXzK253KpcUtLYiDKc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kHsHv/btsNA3okzhx/9lcIXzK253KpcUtLYiDKc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkHsHv%2FbtsNA3okzhx%2F9lcIXzK253KpcUtLYiDKc1%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;713&quot; height=&quot;128&quot; data-origin-width=&quot;713&quot; data-origin-height=&quot;128&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_1745726001123&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;lsof -i :8080&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;Terminal로 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;lsof 명령어&lt;/span&gt;를 사용해 &lt;b&gt;특정 포트번호를 점유하고 있는 프로세스 목록을 출력&lt;/b&gt;한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;lsof는 list open files의 약자로 시스템에서 열려있는 파일에 대한 정보를 출력해 주는 리눅스 명령어다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;해당 포트를 점유중인 프로세스 아이디(PID)를 찾아 &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;kill 명령어&lt;/span&gt;를 사용해 강제로 종료한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;609&quot; data-origin-height=&quot;31&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lKET0/btsNz3JrUiK/Sgc8HXKRKHuowe51OG1wwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lKET0/btsNz3JrUiK/Sgc8HXKRKHuowe51OG1wwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lKET0/btsNz3JrUiK/Sgc8HXKRKHuowe51OG1wwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlKET0%2FbtsNz3JrUiK%2FSgc8HXKRKHuowe51OG1wwK%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;609&quot; height=&quot;31&quot; data-origin-width=&quot;609&quot; data-origin-height=&quot;31&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1745726435215&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kill -9 66111&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;270&quot; data-origin-height=&quot;138&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d2MWVX/btsNAhOesfE/TDQATk38S1jiPGsKoHnpWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d2MWVX/btsNAhOesfE/TDQATk38S1jiPGsKoHnpWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d2MWVX/btsNAhOesfE/TDQATk38S1jiPGsKoHnpWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd2MWVX%2FbtsNAhOesfE%2FTDQATk38S1jiPGsKoHnpWk%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;270&quot; height=&quot;138&quot; data-origin-width=&quot;270&quot; data-origin-height=&quot;138&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Archive/Linux</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/354</guid>
      <comments>https://novlog.tistory.com/entry/Linux-%ED%8A%B9%EC%A0%95-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%ED%8F%AC%ED%8A%B8%EB%B2%88%ED%98%B8-%ED%99%95%EC%9D%B8-%ED%9B%84-Kill-%EB%B0%A9%EB%B2%95-%EC%A0%95%EB%A6%AC-lsof-kill#entry354comment</comments>
      <pubDate>Sun, 27 Apr 2025 13:05:46 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 18115 카드 놓기 Cpp 문제풀이</title>
      <link>https://novlog.tistory.com/entry/BOJ-18115-%EC%B9%B4%EB%93%9C-%EB%86%93%EA%B8%B0-Cpp-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;INFO&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;난이도 : &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;SILVER3&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;문제유형 : &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;DataStructure Deque&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/18115&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/18115&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;SOLVE&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;앞 뒤가 모두 뚫려있는 Deque 자료구조를 사용해 문제를 풀이했다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;예제 입력2를 예시로 문제에서 제시해주는 값을 정리해보면 다음과 같다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;초기 상태 : 알 수 없음&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;기술 사용 순서 : 2 3 3 2 1&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;최종 상태 : 5 4 3 2 1&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;즉, &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;기술을 거꾸로 사용하며 반대로 적용&lt;/span&gt;시키면 초기 상태를 구할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZyg6n/btsM6He4yC9/JbIoO5p4xkbgd09mnHgNe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZyg6n/btsM6He4yC9/JbIoO5p4xkbgd09mnHgNe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZyg6n/btsM6He4yC9/JbIoO5p4xkbgd09mnHgNe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZyg6n%2FbtsM6He4yC9%2FJbIoO5p4xkbgd09mnHgNe1%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;495&quot; height=&quot;87&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;n개의 명령을 저장할 n크기의 cmd 벡터를 정의한 뒤 명령을 입력받는다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1743663309657&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    int n;
    cin &amp;gt;&amp;gt; n;
    
    vector&amp;lt;int&amp;gt; cmd(n);
    for(int i = 0; i &amp;lt; n; i++) {
        cin &amp;gt;&amp;gt; cmd[i];
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;명령을 뒤에서부터 반대로 적용시킨다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1743664322767&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    // 기술을 거꾸로 사용 &amp;amp; 거꾸로 적용
    int card = 1;
    deque&amp;lt;int&amp;gt; dq;
    for (int i = n - 1; i &amp;gt;= 0; i--) {
        // skill 1 : 맨 뒤에 원소를 추가 
        if (cmd[i] == 1) {
            dq.push_back(card);
        } 
        else if (cmd[i] == 2) {
            int tmp_cmd = dq.back();
            dq.pop_back();
            dq.push_back(card);
            dq.push_back(tmp_cmd);
        }
        else if (cmd[i] == 3) {
            dq.push_front(card);
        }
        card++;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;카드를 덱에서 위에서부터 출력한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1743664343815&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    // 초기 카드 상태를 위에서 부터 출력
    while(!dq.empty()) {
        cout &amp;lt;&amp;lt; dq.back() &amp;lt;&amp;lt; &quot; &quot;;
        dq.pop_back();
    }&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;CODE&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1743664404975&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;deque&amp;gt;
using namespace std; 

int main() {

    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);

    int n;
    cin &amp;gt;&amp;gt; n;
    
    vector&amp;lt;int&amp;gt; cmd(n);
    for(int i = 0; i &amp;lt; n; i++) {
        cin &amp;gt;&amp;gt; cmd[i];
    }
    
    // 기술을 거꾸로 사용 &amp;amp; 거꾸로 적용
    int card = 1;
    deque&amp;lt;int&amp;gt; dq;
    for (int i = n - 1; i &amp;gt;= 0; i--) {
        // skill 1 : 맨 뒤에 원소를 추가 
        if (cmd[i] == 1) {
            dq.push_back(card);
        } 
        else if (cmd[i] == 2) {
            int tmp_cmd = dq.back();
            dq.pop_back();
            dq.push_back(card);
            dq.push_back(tmp_cmd);
        }
        else if (cmd[i] == 3) {
            dq.push_front(card);
        }
        card++;
    }

    // 초기 카드 상태를 위에서 부터 출력
    while(!dq.empty()) {
        cout &amp;lt;&amp;lt; dq.back() &amp;lt;&amp;lt; &quot; &quot;;
        dq.pop_back();
    }
}

// 기술 목록
// 1. 제일 위의 카드 1장을 바닥에둔다. 
// 2. 위에서 2번째 카드를 바닥에 둔다. (2장 이상인 경우만 가능)
// 3. 제일 밑에 있는 카드를 바닥에 둔다. (2장 이상일때만 가능)

// 카드 N장이 있다. 
// 1~n까지의 정수가 중복되지 않게 있다. 
// 기수을 n번 사용해서 카드를 다 내려놓았을 때, 위에서부터 1.2...n이 적혀있었다. 

// 2 ... 1 2 3
// 3 ... 
// 기술 1번 사용 -&amp;gt; 맨뒤에 원소를 넣어준다. 
// 기술 2번 사용 -&amp;gt; 맨뒤에서 2번째에 원소를 넣어준다.
// 기술 3번 사용 -&amp;gt; 원소를 맨 앞에 넣어준다.&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/BOJ</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/353</guid>
      <comments>https://novlog.tistory.com/entry/BOJ-18115-%EC%B9%B4%EB%93%9C-%EB%86%93%EA%B8%B0-Cpp-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4#entry353comment</comments>
      <pubDate>Thu, 3 Apr 2025 16:14:40 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ] 30804 과일 탕후루 C++ 문제풀이</title>
      <link>https://novlog.tistory.com/entry/BOJ-30804-%EA%B3%BC%EC%9D%BC-%ED%83%95%ED%9B%84%EB%A3%A8-C-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;INFO&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;난이도 : &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;SILVER2&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;문제유형 : &lt;span style=&quot;background-color: #333333; color: #dddddd;&quot;&gt;Two Pointer&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&lt;span style=&quot;color: #dddddd;&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/30804&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/30804&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;SOLVE&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;단순 브루트포스 방식으로는 O(N^2) 만큼의 시간복잡도가 소요되기에, 시간초과가 발생한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;따라서 2개의 포인터를 두고 모든 경우의수를 탐색하는 투포인터 알고리즘 (Two Pointer Algorithm) 을 사용해서 문제를 풀이했다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;탕후루 정보를 저장할 v 벡터와 각 과일의 번호 개수를 저장할 fruits 벡터를 저장한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;문제 조건에서 총 1부터 9까지 번호가 붙어있다고 제시하였으니 fruits 벡터의 크기는 10으로 고정하고 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;Two Pointer 로직 내부에서 현재 탕후루 배열의 과일의 종류 개수를 판별하는 로직에서 활용한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1568&quot; data-origin-height=&quot;138&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2hmbc/btsMZEuOm07/HrCV21KscK9YQY56Tv5jW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2hmbc/btsMZEuOm07/HrCV21KscK9YQY56Tv5jW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2hmbc/btsMZEuOm07/HrCV21KscK9YQY56Tv5jW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2hmbc%2FbtsMZEuOm07%2FHrCV21KscK9YQY56Tv5jW1%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;1568&quot; height=&quot;138&quot; data-origin-width=&quot;1568&quot; data-origin-height=&quot;138&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1743060308841&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    vector&amp;lt;int&amp;gt; v(n, 0); // 탕후루 배열
    vector&amp;lt;int&amp;gt; fruits(10, 0); // 과일의 개수&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;다음은 투 포인터 로직이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;r_ptr을 증가시키는 방향으로 로직을 수행하며&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;과일 탕후루 배열의 과일 종류가 2를 초과하는 경우가 발생하면 l_ptr을 증가시킨다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;또한 루프를 돌때마다 result 변수와 두 포인터 길이의 차 (= 탕후루의 길이) (= r_ptr - l_ptr) 와의 길이를 비교해 길이가 더 큰 값을 result 변수에 갱신한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743060605674&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    int l_ptr = 0, r_ptr = 0;
    int result = 0;
    
    while(r_ptr &amp;lt; n) {
        fruits[v[r_ptr++]]++;
        
        // 과일의 종류가 2개를 초과하는 케이스
        // * count(fruits.begin(), fruits.end(), 0) -&amp;gt; 과일 탕후루 내부에서 원소의 개수가 0인 과일의 개수
        // * while (10 - count(fruits.begin(), fruits.end(), 0) &amp;gt; 2) -&amp;gt; 과일의 종류
        // * 즉, 과일의 종류가 2개를 초과하는 경우에는 l_ptr을 증가시킨다.
        
        while (10 - count(fruits.begin(), fruits.end(), 0) &amp;gt; 2) {
            fruits[v[l_ptr++]]--;
        }
        result = max(result, r_ptr - l_ptr);
    }
    
    cout &amp;lt;&amp;lt; result &amp;lt;&amp;lt; '\n';&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;CODE&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1743061394755&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cstring&amp;gt;
using namespace std;

vector&amp;lt;int&amp;gt; arr;

int main() {
    
    cin.tie(0);
    ios_base::sync_with_stdio(0);
    
    int n;
    cin &amp;gt;&amp;gt; n;
    
    
    // ** Two Pointer **
    // 과일 2가지가 포함된 과일의 최대값을 구한다.
    // 과일의 종류가 2개를 초과하면 left Pointer를 하나씩 줄인다.
    vector&amp;lt;int&amp;gt; v(n, 0); // 탕후루 배열
    vector&amp;lt;int&amp;gt; fruits(10, 0); // 과일의 개수
    
    for (int i = 0; i &amp;lt; n; i++) {
        cin &amp;gt;&amp;gt; v[i];
    }
    
    int l_ptr = 0, r_ptr = 0;
    int result = 0;
    
    while(r_ptr &amp;lt; n) {
        fruits[v[r_ptr++]]++;
        
        while (10 - count(fruits.begin(), fruits.end(), 0) &amp;gt; 2) {
            fruits[v[l_ptr++]]--;
        }
        result = max(result, r_ptr - l_ptr);
    }
    
    cout &amp;lt;&amp;lt; result &amp;lt;&amp;lt; '\n';
    
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/BOJ</category>
      <author>novs</author>
      <guid isPermaLink="true">https://novlog.tistory.com/352</guid>
      <comments>https://novlog.tistory.com/entry/BOJ-30804-%EA%B3%BC%EC%9D%BC-%ED%83%95%ED%9B%84%EB%A3%A8-C-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4#entry352comment</comments>
      <pubDate>Thu, 27 Mar 2025 16:43:55 +0900</pubDate>
    </item>
  </channel>
</rss>