<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>JK-LEE98</title>
    <link>https://memo4667.tistory.com/</link>
    <description>백엔드 개발자</description>
    <language>ko</language>
    <pubDate>Mon, 17 Aug 2026 08:53:42 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>JK-LEE98</managingEditor>
    <image>
      <title>JK-LEE98</title>
      <url>https://tistory1.daumcdn.net/tistory/7579836/attach/eb6451e436754000acf7f596d520748d</url>
      <link>https://memo4667.tistory.com</link>
    </image>
    <item>
      <title>멀티테넌시(Multi-tenancy)란?</title>
      <link>https://memo4667.tistory.com/61</link>
      <description>&lt;h1&gt;멀티테넌시(Multi-tenancy)란?&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;멀티테넌시란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티테넌시는 하나의 애플리케이션과 하나의 인프라(DB, 서버)를 여러 고객이 함께 쓰면서도, 각 고객의 데이터와 설정은 완전히 분리해서 관리하는 아키텍처다. 여기서 &lt;b&gt;Tenant&lt;/b&gt;는 서비스를 사용하는 하나의 고객(회사&amp;middot;조직)을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헬스장 회원관리 SaaS를 예로 들면 이런 구조가 된다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;회원관리 서비스
├── 헬스장 A
├── 헬스장 B
└── 헬스장 C
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헬스장 A, B, C가 각각 Tenant다. 모두 같은 프로그램을 쓰지만 서로의 회원 정보는 절대 조회할 수 없다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;왜 멀티테넌시를 쓸까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헬스장마다 서버와 DB를 따로 운영한다고 해보자.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;헬스장 A &amp;rarr; 서버 + DB
헬스장 B &amp;rarr; 서버 + DB
헬스장 C &amp;rarr; 서버 + DB
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고객이 늘수록 비용이 폭발한다. 반면 멀티테넌시는 하나의 서버와 하나의 DB를 여러 고객이 나눠 쓴다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;회원관리 서비스
        │
   하나의 DB 서버
        │
 헬스장A  헬스장B  헬스장C
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비용이 싸고 유지보수가 쉽다. SaaS에서 가장 많이 쓰는 방식이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;데이터는 어떻게 분리될까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 흔한 방식은 모든 테이블에 tenant_id를 넣는 것이다. member 테이블이라면 이렇게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;id tenant_id name&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;gym_a&lt;/td&gt;
&lt;td&gt;김철수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;gym_a&lt;/td&gt;
&lt;td&gt;이영희&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;gym_b&lt;/td&gt;
&lt;td&gt;박민수&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조회할 때는 항상 tenant_id 조건을 건다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SELECT *
FROM member
WHERE tenant_id = 'gym_a';
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 gym_a 사용자는 gym_b 데이터를 절대 못 본다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;멀티테넌시 구현 방식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. Shared Database, Shared Schema.&lt;/b&gt; 가장 많이 쓰는 방식이다. 하나의 DB에 모든 테이블을 두고, 각 테이블에 tenant_id 컬럼을 붙인다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;DB
member
trainer
payment
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현이 가장 간단하고, 비용이 가장 싸고, 운영이 쉽다. 대신 tenant_id 조건을 하나라도 빼먹으면 다른 고객 데이터가 새어 나간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. Shared Database, Separate Schema.&lt;/b&gt; DB는 하나지만 스키마를 고객별로 나눈다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;DB
gym_a.member   gym_a.payment
gym_b.member   gym_b.payment
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분리가 조금 더 안전한 대신 관리가 복잡해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. Separate Database.&lt;/b&gt; 고객마다 DB를 통째로 분리한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;DB Server
gym_a_database
gym_b_database
gym_c_database
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 안전하지만 비용도 가장 크다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Role과 Tenant는 뭐가 다를까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티테넌시를 처음 보면 제일 헷갈리는 부분이다. 결론부터 말하면 둘은 푸는 문제가 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Tenant는 &quot;어느 고객의 데이터인가&quot;를 다룬다.&lt;/b&gt; BBQ, BHC, 교촌이 같은 서비스를 쓴다고 하자. tenant = BBQ인 사용자는 BBQ 데이터만 봐야 하고, BHC나 교촌 데이터는 절대 조회하면 안 된다. 즉 Tenant는 고객 간 데이터 경계를 만드는 개념이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Role은 &quot;같은 회사 안에서 누가 어디까지 보는가&quot;를 다룬다.&lt;/b&gt; BBQ 안에도 본사, 영업팀, 물류팀, 가맹점이 있고 ROLE_ADMIN, ROLE_MANAGER, ROLE_FRANCHISE 같은 권한이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ROLE_ADMIN &amp;rarr; BBQ 전체 조회&lt;/li&gt;
&lt;li&gt;ROLE_MANAGER &amp;rarr; 담당 가맹점 조회&lt;/li&gt;
&lt;li&gt;ROLE_FRANCHISE &amp;rarr; 자기 가맹점만 조회&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 Role은 같은 Tenant 내부에서 권한을 나누는 개념이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;둘은 함께 쓴다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT에 이렇게 담겨 있다고 하자.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;tenantId&quot;: &quot;BBQ&quot;,
  &quot;role&quot;: &quot;MANAGER&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조회는 두 단계로 간다. 먼저 tenant로 BBQ 데이터만 걸러내고, 그다음 Role로 범위를 좁힌다. ROLE_MANAGER면 담당 가맹점만 조회한다. 실제 순서는 Tenant &amp;rarr; Role이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;tenant_id만 있으면 Role은 필요 없을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니다. tenant_id는 &quot;다른 회사 데이터를 못 보게 하는 장치&quot;고, Role은 &quot;우리 회사 안에서 누가 어디까지 보는가&quot;를 정한다. Tenant는 회사 간 분리, Role은 회사 내부 권한. 서로 대체하는 게 아니라 같이 쓰는 개념이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;내가 만든 ERP는 멀티테넌시였을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 참여한 ERP는 본사 &amp;rarr; 영업팀 &amp;rarr; 가맹점 구조였고, 직급과 부서에 따라 조회 범위가 달랐다. 영업팀장은 모든 가맹점을, 담당자는 담당 가맹점을, 가맹점주는 자기 매장만 봤다. 데이터 접근 제한은 분명히 있었다. 하지만 이건 Role 기반 접근 제어(RBAC)다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 멀티테넌시가 아니냐면, 그 ERP는 하나의 회사(BBQ)만을 위한 시스템이었기 때문이다. tenant는 BBQ 하나뿐이었다. 만약 이걸 BBQ, BHC, 교촌, 굽네에 다 팔아서 한 시스템에서 굴린다면 그때부터 tenant = BBQ, tenant = BHC 같은 개념이 생긴다. 그게 멀티테넌시다. 정리하면 내 ERP는 RBAC는 있었지만 멀티테넌시는 없었다고 보는 게 맞다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;멀티테넌시로 확장한다면?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 구조가 BBQ &amp;rarr; 가맹점 &amp;rarr; 회원이었다면, 멀티테넌시에서는 맨 앞에 회사(Tenant) 계층이 하나 더 붙는다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;Tenant(BBQ) &amp;rarr; 가맹점 &amp;rarr; 회원
Tenant(BHC) &amp;rarr; 가맹점 &amp;rarr; 회원
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 구조 앞에 &quot;회사&quot;라는 층을 하나 얹는 것이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Role과 멀티테넌시는 다른 개념이다. Role은 &quot;누가 보는가&quot;, 멀티테넌시는 &quot;어느 고객의 데이터인가&quot;를 정한다. 실무에서는 대부분 tenant_id로 고객 데이터를 먼저 자르고, Role로 회사 내부 권한을 다시 자른다. 즉 Tenant &amp;rarr; Role &amp;rarr; Data 순서로 접근을 제어하는 게 일반적인 멀티테넌시 구조다.&lt;/p&gt;</description>
      <category>나의 지식 공유</category>
      <category>Multi-tenancy</category>
      <category>멀티테넌시</category>
      <author>JK-LEE98</author>
      <guid isPermaLink="true">https://memo4667.tistory.com/61</guid>
      <comments>https://memo4667.tistory.com/61#entry61comment</comments>
      <pubDate>Tue, 28 Jul 2026 09:45:57 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 주식가격</title>
      <link>https://memo4667.tistory.com/60</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1336&quot; data-origin-height=&quot;726&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VR8AW/dJMcacDQnev/nXSI4IRr5xsADLyOauNU6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VR8AW/dJMcacDQnev/nXSI4IRr5xsADLyOauNU6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VR8AW/dJMcacDQnev/nXSI4IRr5xsADLyOauNU6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVR8AW%2FdJMcacDQnev%2FnXSI4IRr5xsADLyOauNU6k%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;617&quot; height=&quot;335&quot; data-origin-width=&quot;1336&quot; data-origin-height=&quot;726&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 프로그래머스 LV2에 해당하는 코딩테스트 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'스택/큐'에 대한 개념이 아직 잘 잡혀있지 않아서 나는 문제를 처음 보고 이중for문을 사용하는 방식으로 접근했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는 이중for문을 활용한 방식이다.&lt;/p&gt;
&lt;pre id=&quot;code_1783646360383&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public int[] solution(int[] prices) {
        int[] arr = new int[prices.length];
        
        for (int i=0; i&amp;lt;arr.length-1; i++) {
            int count = 0;
            
            for (int j = i+1; j&amp;lt;prices.length; j++) {
                count++;
                if (prices[i] &amp;gt; prices[j]) {
                    break;
                }
            }
            
            arr[i] = count;
        }
        
        return arr;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-section-id=&quot;1szrtox&quot; data-end=&quot;763&quot; data-start=&quot;747&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-end=&quot;825&quot; data-start=&quot;765&quot; data-ke-size=&quot;size16&quot;&gt;그러나 이 로직의 시간복잡도는 &lt;b&gt;O(n&amp;sup2;)&lt;/b&gt; 이기 때문에 스택을 활용한 방식으로 접근하는 것이 더 좋다.&lt;/p&gt;
&lt;p data-end=&quot;890&quot; data-start=&quot;827&quot; data-ke-size=&quot;size16&quot;&gt;이중 for문은 각 원소마다 뒤의 모든 원소를 탐색한다. 따라서 이미 확인했던 원소를 또 확인하는 일이 반복된다.&lt;/p&gt;
&lt;p data-end=&quot;936&quot; data-start=&quot;892&quot; data-ke-size=&quot;size16&quot;&gt;반면 스택을 사용하면 &lt;b&gt;아직 가격이 떨어지지 않은 인덱스만 관리&lt;/b&gt;하면 된다.&lt;/p&gt;
&lt;p data-end=&quot;979&quot; data-start=&quot;938&quot; data-ke-size=&quot;size16&quot;&gt;여기서 가장 중요한 점은 &lt;b&gt;가격이 아니라 인덱스를 저장한다는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;1028&quot; data-start=&quot;981&quot; data-ke-size=&quot;size16&quot;&gt;문제에서 필요한 것은 가격 자체가 아니라 &lt;b&gt;몇 초 동안 가격이 유지되었는지&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;1042&quot; data-start=&quot;1030&quot; data-ke-size=&quot;size16&quot;&gt;몇 초가 유지되었는지는&lt;/p&gt;
&lt;pre id=&quot;code_1783649132701&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;현재 인덱스 - 이전 인덱스&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 계산할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 가격만 저장해서는 몇 초가 지났는지 계산할 수 없으며, 반드시 인덱스를 저장해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가격은 필요할 때마다&lt;/p&gt;
&lt;pre id=&quot;code_1783649166100&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;prices[index]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 언제든지 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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_1783649191017&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;prices = [1, 2, 3, 2, 3]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택은 비어있는 상태다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;i=0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 가격은 1이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택이 비어 있으므로 비교할 대상이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 현재 인덱스(0)를 스택에 넣는다.&lt;/p&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;i=1&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 가격은 2이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택의 맨 위에는 인덱스 0이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가격을 비교하면 현재 가격이 더 크므로 아직 가격이 떨어지지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 인덱스 1을 스택에 넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;i=2&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 가격은 3이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택의 맨 위는 인덱스 1이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비교하면 가격이 떨어지지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스 2를 스택에 넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;i=3&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 가격은 2이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택의 맨 위는 인덱스 2이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비교하면 가격이 떨어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 인덱스 2의 답을 계산할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택에서 꺼내면&lt;/p&gt;
&lt;pre id=&quot;code_1783649519233&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;index = 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 위치는&lt;/p&gt;
&lt;pre id=&quot;code_1783649531700&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;i = 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이므로&lt;/p&gt;
&lt;pre id=&quot;code_1783649550083&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;3 - 2 = 1초&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가격이 유지되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서&lt;/p&gt;
&lt;pre id=&quot;code_1783649570516&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;answer[2] = 1;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 스택의 다음 원소를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 스택의 맨 위는 인덱스 1이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가격이 떨어진 것이 아니므로 반복문을 종료하고 현재 인덱스(3)을 스택에 넣는다.&lt;/p&gt;
&lt;pre id=&quot;code_1783649507298&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;stack = [3, 1, 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;b&gt;i=4&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 가격은 3이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택의 맨 위는 인덱스 3이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가격이 떨어지지 않았으므로 인덱스 4를 스택에 넣는다.&lt;/p&gt;
&lt;pre id=&quot;code_1783649636182&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;stack = [4, 3, 1, 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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탐색이 끝났는데도 스택에 원소가 남아 있다는 것은 끝까지 가격이 한 번도 떨어지지 않았다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 남아있는 인덱스들은&lt;/p&gt;
&lt;pre id=&quot;code_1783649678466&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;prices.length - 1 - index&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만큼 가격이 유지된 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이 문제는 가격을 저장하는 문제가 아니라, 아직 가격이 떨어지지 않은 인덱스를 관리하는 문제이다.&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783650699507&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.*;

class Solution {
    public int[] solution(int[] prices) {
        int[] answer = new int[prices.length];
        Deque&amp;lt;Integer&amp;gt; stack = new ArrayDeque&amp;lt;&amp;gt;();

        for (int i = 0; i &amp;lt; prices.length; i++) {

            while (!stack.isEmpty() &amp;amp;&amp;amp; prices[stack.peek()] &amp;gt; prices[i]) {
                int index = stack.pop();
                answer[index] = i - index;
            }

            stack.push(i);
        }

        while (!stack.isEmpty()) {
            int index = stack.pop();
            answer[index] = prices.length - 1 - index;
        }

        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 코드다.&lt;/p&gt;</description>
      <category>PS</category>
      <category>주식가격</category>
      <category>코딩테스트</category>
      <category>코테</category>
      <category>프로그래머스</category>
      <author>JK-LEE98</author>
      <guid isPermaLink="true">https://memo4667.tistory.com/60</guid>
      <comments>https://memo4667.tistory.com/60#entry60comment</comments>
      <pubDate>Fri, 10 Jul 2026 11:32:03 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 숫자 문자열과 영단어</title>
      <link>https://memo4667.tistory.com/59</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1256&quot; data-origin-height=&quot;1646&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjOxIZ/dJMcac4MBHk/6uFxGUfhSMR1hAJKhk0XQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjOxIZ/dJMcac4MBHk/6uFxGUfhSMR1hAJKhk0XQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjOxIZ/dJMcac4MBHk/6uFxGUfhSMR1hAJKhk0XQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjOxIZ%2FdJMcac4MBHk%2F6uFxGUfhSMR1hAJKhk0XQ0%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;1256&quot; height=&quot;1646&quot; data-origin-width=&quot;1256&quot; data-origin-height=&quot;1646&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 유형의 문제를 처음 보면 머릿 속에 미리 one, two, three, four... 숫자에 해당하는 영단어를 미리 숫자와 매칭을 시켜놓고 들어가야 되나? for문을 이용해서 반복문을 돌릴 때 반복문 안에서 어떻게 단위(숫자 하나) 별로 자를 수 있지? 어떻게 영어로 된 부분을 숫자로 교체하지? 등등 다양한 고민이 생길 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나 역시 그랬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 별거 없다. 그냥 java에서 교체하는 함수 replace만 알고있으면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;String 배열에 영단어들을 저장하고 반복문을 10번 돌리면 되는 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복문이 한 번 돌 때마다 일치하는 것이 있다면 숫자로 대체되게끔 하는 것을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 범했던 실수가 하나있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 for문 안에 if문을 사용했던 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;만약 words에 있다면 교체한다&quot; 라는 로직을 사용하고 싶었던 것인데 그럴 필요가 전혀없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;java의 replace()라는 함수는 일치하는 것이 없을 경우 그냥 패스하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 하나 유의할 점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;replace함수의 특성상 두 번째로 와야하는 것은 문자열이어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 i가 아닌 i+&quot;&quot; 를 사용하여 문자열 형태로 써야 한다.&lt;/p&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_1783384618427&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public int solution(String s) {
        String[] words = {&quot;zero&quot;, &quot;one&quot;, &quot;two&quot;, &quot;three&quot;, &quot;four&quot;, &quot;five&quot;, &quot;six&quot;
                            , &quot;seven&quot;, &quot;eight&quot;, &quot;nine&quot;};
        
        for (int i=0; i&amp;lt;words.length; i++) {
            s = s.replace(words[i], i+&quot;&quot;);
        }
        
        int answer = Integer.parseInt(s);
        
        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS</category>
      <author>JK-LEE98</author>
      <guid isPermaLink="true">https://memo4667.tistory.com/59</guid>
      <comments>https://memo4667.tistory.com/59#entry59comment</comments>
      <pubDate>Tue, 7 Jul 2026 09:44:44 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 푸드 파이트 대회</title>
      <link>https://memo4667.tistory.com/58</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;1598&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfXiD8/dJMcaicVXUd/BHy4sB3EJXYwZvILXf5W90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfXiD8/dJMcaicVXUd/BHy4sB3EJXYwZvILXf5W90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfXiD8/dJMcaicVXUd/BHy4sB3EJXYwZvILXf5W90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfXiD8%2FdJMcaicVXUd%2FBHy4sB3EJXYwZvILXf5W90%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;1354&quot; height=&quot;1598&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;1598&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 딱히 중요해 보이진 않았지만 reverse()라는 존재를 잊지 않기 위해 블로그에 남긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 나는 reverse()를 사용하지 않았을 뿐더러 불필요한 arr배열까지 만들어서 반복해야 하는 수를 담기 까지 했는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 정제된 코드로 reverse()를 사용했을 때와 안 했을 때를 비교해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783233858104&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public String solution(int[] food) {
        StringBuilder sb = new StringBuilder();

        // 왼쪽
        for (int i = 1; i &amp;lt; food.length; i++) {
            for (int j = 0; j &amp;lt; food[i] / 2; j++) {
                sb.append(i);
            }
        }

        // 물
        sb.append(0);

        // 오른쪽
        for (int i = food.length - 1; i &amp;gt;= 1; i--) {
            for (int j = 0; j &amp;lt; food[i] / 2; j++) {
                sb.append(i);
            }
        }

        return sb.toString();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 사용하지 않았을 때다. arr을 제거한 대신 범위에 food[i] / 2를 사용한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783233895955&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public String solution(int[] food) {
        StringBuilder left = new StringBuilder();

        // 왼쪽만 생성
        for (int i = 1; i &amp;lt; food.length; i++) {
            for (int j = 0; j &amp;lt; food[i] / 2; j++) {
                left.append(i);
            }
        }

        // 오른쪽은 왼쪽을 뒤집어서 생성
        StringBuilder right = new StringBuilder(left).reverse();

        return left.append(0).append(right).toString();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 유사하지만 반대로 동작하는 반복문을 사용할 필요가 없어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;left를 만들고 left를 뒤집은 right를 정의한 후 return할 때 left + 0 + right 하면 더 간단한 코드를 완성시킬 수 있다.&lt;/p&gt;</description>
      <category>PS</category>
      <category>reverse</category>
      <category>StringBuilder</category>
      <category>프로그래머스</category>
      <author>JK-LEE98</author>
      <guid isPermaLink="true">https://memo4667.tistory.com/58</guid>
      <comments>https://memo4667.tistory.com/58#entry58comment</comments>
      <pubDate>Sun, 5 Jul 2026 15:47:09 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 시저 암호</title>
      <link>https://memo4667.tistory.com/57</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;854&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XAekL/dJMcacQ8CEl/MhwWUOEFf2RWXjVKL6KUpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XAekL/dJMcacQ8CEl/MhwWUOEFf2RWXjVKL6KUpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XAekL/dJMcacQ8CEl/MhwWUOEFf2RWXjVKL6KUpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXAekL%2FdJMcacQ8CEl%2FMhwWUOEFf2RWXjVKL6KUpk%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;1350&quot; height=&quot;854&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;854&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 프로그래머스 LV1 단계에 해당하는 시저 암호 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡한 알고리즘을 요구하는 문제는 아니지만, 문자의 코드값(ASCII/Unicode)과 문자 연산을 이해하는 데 도움이 되는 문제라고 생각해 정리해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java의 char는 Unicode를 사용하지만, 영문 알파벳(A~Z, a~z)의 코드값은 ASCII와 동일하다. 따라서 시저 암호와 같은 문제에서는 ASCII 코드의 개념으로 이해해도 무방하다. 문자도 내부적으로는 숫자로 저장되기 때문에 숫자처럼 덧셈과 뺄셈 연산을 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이 문제는 영문 알파벳이 연속된 코드값을 가진다는 점과, 문자를 숫자처럼 더하고 뺄 수 있다는 점을 이용해 해결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;문자&lt;/td&gt;
&lt;td&gt;코드&lt;/td&gt;
&lt;td&gt;'A'를 뺀 결과&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;65&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;66&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;67&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;X&lt;/td&gt;
&lt;td&gt;88&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;89&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Z&lt;/td&gt;
&lt;td&gt;90&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 표를 보면 c - 'A'의 결과가 알파벳의 순서(0~25)를 의미한다는 것을 쉽게 이해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&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;이 문제 특성상 공백은 공백으로 두고 대문자 혹은 소문자만 n만큼 이동한 문자로 이루어진 새로운 문자열을 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 for 문 안 if 문을 통해 공백, 대문자, 소문자일 때를 구분해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 그냥 더하기만 하면 안 된다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 읽어보면 &quot;z&quot;를 1만큼 밀면 &quot;a&quot;가 된다는 조건 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 알파벳 마지막 문자를 넘어가면 그 다음 ASCII 코드로 넘어가는 것이 아닌 대문자, 소문자 각각 그 루프 안에서 순환한다는 것을 유의해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 알파벳은 a~z, A~Z 각각 26개로 이루어져있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 0번 알파벳부터 25번 알파벳이라고 생각하면 문제 접근하는 것이 좀 더 쉬워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 24번 알파벳을 3만큼 이동시키면 24 -&amp;gt; 25 -&amp;gt; 1 -&amp;gt; 2 가 되므로 b혹은 B가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이해를 돕기 위한 버전이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는 문자를 n만큼 이동시킨 결과가 대문자의 마지막 문자('Z') 또는 소문자의 마지막 문자('z')를 넘어가는 경우 26을 빼 다시 처음으로 돌아오도록 구현한 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1782756975868&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;char c = s.charAt(i);

if (c == ' ') {
    sb.append(' ');
} else if ('A' &amp;lt;= c &amp;amp;&amp;amp; c &amp;lt;= 'Z') {

    if (c + n &amp;gt; 'Z') {
        sb.append((char)(c + n - 26));
    } else {
        sb.append((char)(c + n));
    }

} else {

    if (c + n &amp;gt; 'z') {
        sb.append((char)(c + n - 26));
    } else {
        sb.append((char)(c + n));
    }

}&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;위의 코드를 굳이 26보다 클 때와 작을 때로 구분하지 않고 하나로 나타낼 수 있는 것은 '%' 연산자를 이용하는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1782757111885&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(char)('A' + (c - 'A' + n) % 26)
(char)('a' + (c - 'a' + n) % 26)&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-end=&quot;1380&quot; data-start=&quot;1262&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;1380&quot; data-start=&quot;1264&quot; data-ke-size=&quot;size16&quot;&gt;c - 'A'를 통해 문자를 0~25 범위의 숫자로 변환한 뒤 n만큼 이동시키고, % 26으로 다시 0~25 범위 안에서 순환하도록 만든 후 마지막으로 'A'를 더해 다시 문자로 변환하는 방식이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 한 줄로 표현 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 내가 정답으로 제출한 코드다.&lt;/p&gt;
&lt;pre id=&quot;code_1782756663751&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public String solution(String s, int n) {
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i &amp;lt; s.length(); i++) {
            char c = s.charAt(i);

            if (c == ' ') {
                sb.append(' ');
            } else if ('A' &amp;lt;= c &amp;amp;&amp;amp; c &amp;lt;= 'Z') {
                sb.append((char)('A' + (c - 'A' + n) % 26));
            } else {
                sb.append((char)('a' + (c - 'a' + n) % 26));
            }
        }

        return sb.toString();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 (char) 형변환 부분에 대해 첨언해보자면&lt;/p&gt;
&lt;pre id=&quot;code_1782757292936&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;char c = 'A';
sb.append(c + 1);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에 대한 결과는 66이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 자바의 자동형변환 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;byte&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;darr;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;short&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;darr;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;char&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;darr;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;int&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;darr;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;long&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;darr;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;float&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;darr;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;double&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;Java에서는 연산 과정에서 위와 같은 순서로 자동 형변환이 일어난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;char + int의 결과는 int이기 때문에 명시적으로 char 형태로 형변환을 해야 우리가 원하는 결과를 얻을 수 있다.&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;즉, 우리가 원하는 것은 66이아닌 B이므로 char 형태로 형변환을 해야한다.&lt;/p&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;문자도 내부적으로는 숫자로 저장된다는 점과, Java의 자동 형변환 및 문자 연산을 이해하는 데 좋은 문제&lt;/b&gt;였다.&lt;/p&gt;</description>
      <category>PS</category>
      <category>Java</category>
      <category>ps</category>
      <category>시저 암호</category>
      <category>코딩테스트</category>
      <category>코테</category>
      <category>프로그래머스</category>
      <author>JK-LEE98</author>
      <guid isPermaLink="true">https://memo4667.tistory.com/57</guid>
      <comments>https://memo4667.tistory.com/57#entry57comment</comments>
      <pubDate>Tue, 30 Jun 2026 03:33:49 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 이상한 문자 만들기</title>
      <link>https://memo4667.tistory.com/56</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;962&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRfWSb/dJMcahZbFmH/ZKr7Cbbf0hAGURJllmcAs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRfWSb/dJMcahZbFmH/ZKr7Cbbf0hAGURJllmcAs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRfWSb/dJMcahZbFmH/ZKr7Cbbf0hAGURJllmcAs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRfWSb%2FdJMcahZbFmH%2FZKr7Cbbf0hAGURJllmcAs1%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;1300&quot; height=&quot;962&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;962&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 대충 읽고 풀면 나처럼 틀릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 처음 풀 때 문제를 대충 읽고 홀수 인덱스는 소문자 짝수 인덱스는 대문자로 하면 되는구나 하고 로직을 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1782390010163&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public String solution(String s) {

        char[] arr = s.toCharArray();

        for (int i = 0; i &amp;lt; arr.length; i++) {
            if (i % 2 == 0) {
                arr[i] = Character.toUpperCase(arr[i]);
            } else {
                arr[i] = Character.toLowerCase(arr[i]);
            }
        }

        return new String(arr);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 코드는 내가 처음 잘못 푼 코드다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;char 배열을 만들고 반복문을 통해 홀수 인덱스는 소문자로 짝수 인덱스는 대문자로 변경했고 마지막 char 배열을 문제의 요구사항에 맞게 String 형태로 return 했다.&lt;/p&gt;
&lt;p data-ke-size=&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대충 읽고 넘어갔던 빈 공백에 대한 조건을 신경쓰지 않았던 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 공백 다음에 나오는 문자열에 대해 다시 홀수, 짝수 인덱스를 고려해야하는 문제였던 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`&lt;b&gt;.split(&quot; &quot;)&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_1782390218597&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public String solution(String s) {
        StringBuilder sb = new StringBuilder();
        int idx = 0;
        
        for (int i=0; i&amp;lt;s.length(); i++) {
            char c = s.charAt(i);    
            
            if (c == ' ') {
                sb.append(c);
                idx = 0;        
            } else {
                if (idx % 2 == 0) {
                    sb.append(Character.toUpperCase(c));
                } else {
                    sb.append(Character.toLowerCase(c));
                }
                
                idx++;
            }
        }
        
        return sb.toString();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 최종 정답에 해당하는 로직이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sb와 idx라는 변수를 선언하고 반복문을 통해 공백일 경우 sb에 넣고 idx를 초기화 시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공백이 아닐 경우 idx는 초기화된 상태이기 때문에 짝수인덱스부터 시작하게 된다. (0도 짝수에 포함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 공백 이후 혹은 첫번째 문자열은 무조건 대문자가 되고 idx를 증가시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음은 소문자 다시 대문자 .... 이렇게 흘러가게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 우리가 원하는 알고리즘을 만들 수 있으며 최종 답을 반환할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS</category>
      <category>Java</category>
      <category>이상한 문자 만들기</category>
      <category>코테</category>
      <category>프로그래머스</category>
      <author>JK-LEE98</author>
      <guid isPermaLink="true">https://memo4667.tistory.com/56</guid>
      <comments>https://memo4667.tistory.com/56#entry56comment</comments>
      <pubDate>Thu, 25 Jun 2026 21:27:14 +0900</pubDate>
    </item>
    <item>
      <title>북한산 향로봉 등산일기</title>
      <link>https://memo4667.tistory.com/55</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;저번 사패산 등산 이후로 등산의 맛을 알게되어&lt;span&gt; &lt;/span&gt;&lt;/span&gt;아버지와 함께 북한산을 오르기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아버지와 둘이 처음으로 하는 등산이라 설레이는 마음으로 출발했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진관사에서 시작하여 향로봉으로 올라가는 코스였는데 올라가다보니 길이 사라졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뒤로 돌아와 아무리 찾아도 새로운 길도 안 보이고 사람도 보이지 않았다 ㅠㅠ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 정상까지 오르지 못 하고 하산했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내려와서 진관사에 있는 카페에 가서 시원한 커피와 팥빙수를 먹고 근처에 있는 고깃집에 가서 맛있는 소갈비도 먹었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에는 코스를 잘 알아보고 사람이 많은 곳으로 가야겠다 :)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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/lNgLy/dJMb991bGcZ/4v9MKYDLg367nToQ5KROK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lNgLy/dJMb991bGcZ/4v9MKYDLg367nToQ5KROK0/img.png&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;400&quot; data-is-animation=&quot;false&quot; data-filename=&quot;blob&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lNgLy/dJMb991bGcZ/4v9MKYDLg367nToQ5KROK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlNgLy%2FdJMb991bGcZ%2F4v9MKYDLg367nToQ5KROK0%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;400&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biYsNX/dJMcaa6SlBE/9GJOjRVuHgWsLukWPAs7Sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biYsNX/dJMcaa6SlBE/9GJOjRVuHgWsLukWPAs7Sk/img.png&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;400&quot; data-is-animation=&quot;false&quot; data-filename=&quot;blob&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biYsNX/dJMcaa6SlBE/9GJOjRVuHgWsLukWPAs7Sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiYsNX%2FdJMcaa6SlBE%2F9GJOjRVuHgWsLukWPAs7Sk%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;400&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&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/bNHQh4/dJMcaaZ2Zak/21iFtl9kKIiTyyoq6F4Il1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNHQh4/dJMcaaZ2Zak/21iFtl9kKIiTyyoq6F4Il1/img.png&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;400&quot; data-is-animation=&quot;false&quot; data-filename=&quot;blob&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNHQh4/dJMcaaZ2Zak/21iFtl9kKIiTyyoq6F4Il1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNHQh4%2FdJMcaaZ2Zak%2F21iFtl9kKIiTyyoq6F4Il1%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;400&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c62r3L/dJMcaa6SlBF/4fzi3H9MO40ueoRfw46sak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c62r3L/dJMcaa6SlBF/4fzi3H9MO40ueoRfw46sak/img.png&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;400&quot; data-is-animation=&quot;false&quot; data-filename=&quot;blob&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c62r3L/dJMcaa6SlBF/4fzi3H9MO40ueoRfw46sak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc62r3L%2FdJMcaa6SlBF%2F4fzi3H9MO40ueoRfw46sak%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;400&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>건강한 나 되기 </category>
      <category>북한산</category>
      <category>진관사</category>
      <category>향로봉</category>
      <author>JK-LEE98</author>
      <guid isPermaLink="true">https://memo4667.tistory.com/55</guid>
      <comments>https://memo4667.tistory.com/55#entry55comment</comments>
      <pubDate>Tue, 23 Jun 2026 18:06:41 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 최대공약수와 최소공배수</title>
      <link>https://memo4667.tistory.com/54</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;974&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfl53D/dJMcajioheg/hUmswa5s4RqMzcwJosXQP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfl53D/dJMcajioheg/hUmswa5s4RqMzcwJosXQP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfl53D/dJMcajioheg/hUmswa5s4RqMzcwJosXQP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcfl53D%2FdJMcajioheg%2FhUmswa5s4RqMzcwJosXQP1%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;1276&quot; height=&quot;974&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;974&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 아주 쉬운 기초문제지만 최소공배수를 표현하는 법을 잊지 않기 위해 블로그에 남기기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 내 코드&lt;/p&gt;
&lt;pre id=&quot;code_1782204487209&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public int[] solution(int n, int m) {
        int[] arr = new int[2];
        
        if (n % m == 0) {
            arr[0] = m;
            arr[1] = n;
        } else if (m % n == 0) {
            arr[0] = n;
            arr[1] = m;
        } else {
            for (int i=1; i&amp;lt;Math.max(n, m); i++) {
                if (m % i == 0 &amp;amp;&amp;amp; n % i ==0) {
                    arr[0] = i;
                    int Num1 = m / i;
                    int Num2 = n / i;
                    arr[1] = i * Num1 * Num2;
                }
            }
        }

        return arr;
    }
}&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;최소 공배수를 구하는 코드를 보면 두 수를 최대 공약수로 나눈 값을 저장하는 Num1, Num2 변수 두 개를 생성하여&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;최대 공약수 * Num1 * Num2&quot;&lt;/b&gt;&amp;nbsp;와 같이 세 수를 곱하는 방식을 선택했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 더 좋은 표현 방법이 있다는 것을 알게되었다.&lt;/p&gt;
&lt;pre id=&quot;code_1782204645702&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;arr[1] = n * m / arr[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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초등 수학 레벨이지만 내가 작성했던 직관적인 방식 보다 코드도 짧고 변수를 새로 만들 필요도 없어서 유용해보인다.&lt;/p&gt;</description>
      <category>PS</category>
      <category>Java</category>
      <category>최소공배수</category>
      <category>프로그래머스</category>
      <author>JK-LEE98</author>
      <guid isPermaLink="true">https://memo4667.tistory.com/54</guid>
      <comments>https://memo4667.tistory.com/54#entry54comment</comments>
      <pubDate>Tue, 23 Jun 2026 17:53:23 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 두 개 뽑아서 더하기</title>
      <link>https://memo4667.tistory.com/53</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;1180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dUp4ML/dJMcaf7X0tb/Yx9tSNsiqQ7fDLypcvdpjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dUp4ML/dJMcaf7X0tb/Yx9tSNsiqQ7fDLypcvdpjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dUp4ML/dJMcaf7X0tb/Yx9tSNsiqQ7fDLypcvdpjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdUp4ML%2FdJMcaf7X0tb%2FYx9tSNsiqQ7fDLypcvdpjK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1272&quot; height=&quot;1180&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;1180&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 이 문제를 보고 예전 확률과 통계 같은 곳에서 풀던 문제들이 생각났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스의 위치를 [0], [1] 이런식으로 표현해보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크기가 5인 배열이라고 가정해보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[0] + [1], [0] + [2], [0] + [3], [0] + [4]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[1] + [2], [1] + [3], [1] + [4]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[2] + [3], [2] + [4]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[3] + [4]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식의 흐름을 떠올렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 생각을 기반으로 작성한 내 최종 코드다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크기가 정해진 배열이 아니므로 ArrayList를 사용했고 앞서 설명한 방식으로 더하기 위해 이중 for문을 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 문제 요구사항에 맞게 정렬을 했고 return 하기 위한 배열을 새로 만들어 list에 있는 값들을 넣음으로써 로직을 완성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1781270391675&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.*;

class Solution {
    public int[] solution(int[] numbers) {
        List&amp;lt;Integer&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();
        
        for (int i=0; i&amp;lt;numbers.length-1; i++) {
            int sum = 0;
            
            for (int j=i+1; j&amp;lt;numbers.length; j++) {
                sum = numbers[i] + numbers[j];
                
                if (!list.contains(sum)) {
                    list.add(sum);
                }
            }
        }
        
        Collections.sort(list);
        
        int[] arr = new int[list.size()];
        
        for (int i=0; i&amp;lt;list.size(); i++) {
            arr[i] = list.get(i);       
        }
        
        return arr;
    }
}&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;나는 난이도와 상관없이 문제를 풀고 나서 찝찝한 느낌이 들면 언제나 gpt를 활용하여 최적화된 방법을 찾아본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 `HashSet`의 활용에 대해 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 코딩테스트 경험이 적은 나에게는 생소한 것이었기에 기회 삼아 HashSet에 대한 것을 정리해보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;HashSet이란?&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;중복을 허용하지 않는 집합(Set)&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;List&lt;/h3&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;List&amp;lt;Integer&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();

list.add(1);
list.add(1);
list.add(2);
&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 class=&quot;json&quot;&gt;&lt;code&gt;[1, 1, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중복이 허용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HashSet&lt;/h3&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;Set&amp;lt;Integer&amp;gt; set = new HashSet&amp;lt;&amp;gt;();

set.add(1);
set.add(1);
set.add(2);
&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 class=&quot;json&quot;&gt;&lt;code&gt;[1, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중복이 허용되지 않는다.&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;왜 HashSet이 빠른가?&lt;/h1&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;if (!list.contains(sum)) {
    list.add(sum);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;contains()는 앞에서부터 하나씩 검사한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[3, 4, 5, 6, 7]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에서 7을 찾으려면&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;3 확인
4 확인
5 확인
6 확인
7 확인
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최악의 경우 전부 검사한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 HashSet은&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;set.contains(7);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 거의 즉시 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부적으로 &lt;b&gt;해시(Hash)&lt;/b&gt; 를 사용하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;HashSet을 사용한 코드&lt;/h1&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import java.util.*;

class Solution {
    public int[] solution(int[] numbers) {

        Set&amp;lt;Integer&amp;gt; set = new HashSet&amp;lt;&amp;gt;();

        for (int i = 0; i &amp;lt; numbers.length - 1; i++) {
            for (int j = i + 1; j &amp;lt; numbers.length; j++) {
                set.add(numbers[i] + numbers[j]);
            }
        }

        List&amp;lt;Integer&amp;gt; list = new ArrayList&amp;lt;&amp;gt;(set);

        Collections.sort(list);

        int[] answer = new int[list.size()];

        for (int i = 0; i &amp;lt; list.size(); i++) {
            answer[i] = list.get(i);
        }

        return answer;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS</category>
      <category>hashset</category>
      <category>코딩테스트</category>
      <category>프로그래머스</category>
      <author>JK-LEE98</author>
      <guid isPermaLink="true">https://memo4667.tistory.com/53</guid>
      <comments>https://memo4667.tistory.com/53#entry53comment</comments>
      <pubDate>Fri, 12 Jun 2026 22:43:03 +0900</pubDate>
    </item>
    <item>
      <title>사패산 등산일기</title>
      <link>https://memo4667.tistory.com/52</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;친구와 등산을 주기적으로 하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힘든 개발자 취업을 이겨내기 위해 등산은 아주 좋은 취미라고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더운 날씨에 객기를 부리면 안 되기 때문에 초보자 등산코스로 추천받은 사패산을 가기로 결정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사패산은 둘레길 수준의 평지라고 알고 있었는데 친구가 갑자기 일반적인 코스가 아닌 원각사 코스로 갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코스는 정확하진 않지만 일반 코스에 비해 시간을 짧게 걸리되 가파른 코스이다...ㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왕복 한시간 반 정도라길래 이정도면 충분하지 싶었는데 페이스 조절에 실패했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 등산에 페이스 조절이 필요한지도 잘 몰랐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 사진은 가다가 도저히 못 가겠다고 뻗어버린 나의 모습이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;머릿 속에는 119를 부르고 싶을 정도로 숨막히고 힘들었지만 친구의 도움 덕에 정상까지 오를 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상은 매우 아름다웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 죽고 싶을 만큼 힘들었던 시간들을 전부 잊게 해주는 풍경이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'고진감래' 라는 단어를 몸소 체험하고 느낄 수 있는 계기였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점진적으로 난이도를 높이며 등산의 맛을 느껴야겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ATsw0/dJMcafGWvUa/YE3nKRb5SPkIZZrDVCNwQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ATsw0/dJMcafGWvUa/YE3nKRb5SPkIZZrDVCNwQ1/img.png&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2138&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2026-06-10-15-57-47.png&quot; width=&quot;370&quot; height=&quot;656&quot; style=&quot;width: 42.4267%; margin-right: 10px;&quot; data-widthpercent=&quot;42.93&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ATsw0/dJMcafGWvUa/YE3nKRb5SPkIZZrDVCNwQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FATsw0%2FdJMcafGWvUa%2FYE3nKRb5SPkIZZrDVCNwQ1%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;1206&quot; height=&quot;2138&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dxbr9E/dJMcafz9Flk/iLeUbJMuAMWQMROc4LhfQk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dxbr9E/dJMcafz9Flk/iLeUbJMuAMWQMROc4LhfQk/img.jpg&quot; data-origin-width=&quot;4284&quot; data-origin-height=&quot;5712&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2026-06-10-15-57-59.jpeg&quot; width=&quot;383&quot; data-widthpercent=&quot;57.07&quot; style=&quot;width: 56.4106%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dxbr9E/dJMcafz9Flk/iLeUbJMuAMWQMROc4LhfQk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdxbr9E%2FdJMcafz9Flk%2FiLeUbJMuAMWQMROc4LhfQk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4284&quot; height=&quot;5712&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>건강한 나 되기 </category>
      <category>등산</category>
      <category>사패산</category>
      <category>원각사</category>
      <author>JK-LEE98</author>
      <guid isPermaLink="true">https://memo4667.tistory.com/52</guid>
      <comments>https://memo4667.tistory.com/52#entry52comment</comments>
      <pubDate>Wed, 10 Jun 2026 16:04:41 +0900</pubDate>
    </item>
  </channel>
</rss>