<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>lefoa-98.log</title>
        <link>https://velog.io/</link>
        <description>.</description>
        <lastBuildDate>Fri, 14 Nov 2025 07:32:45 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>lefoa-98.log</title>
            <url>https://velog.velcdn.com/images/lefoa-98/profile/21a7fbbf-6cea-4216-8376-a05916644a20/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. lefoa-98.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/lefoa-98" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Query] 실행 순서에 대해]]></title>
            <link>https://velog.io/@lefoa-98/Query-%EC%8B%A4%ED%96%89-%EC%88%9C%EC%84%9C%EC%97%90-%EB%8C%80%ED%95%B4</link>
            <guid>https://velog.io/@lefoa-98/Query-%EC%8B%A4%ED%96%89-%EC%88%9C%EC%84%9C%EC%97%90-%EB%8C%80%ED%95%B4</guid>
            <pubDate>Fri, 14 Nov 2025 07:32:45 GMT</pubDate>
            <description><![CDATA[<p>✅ 아무리 옵티마이저가 JOIN 순서·실행 순서를 바꿔도 결과는 절대 달라지지 않는다.</p>
<p>이건 표준 SQL의 보장 사항이다.</p>
<p>🎯 왜 절대 결과가 달라지지 않을까?
이유는 간단함:</p>
<p>SQL은 선언적 언어이기 때문.</p>
<p>즉,</p>
<p>“어떻게 실행할지” 가 아니라</p>
<p>“어떤 결과가 나와야 하는지” 를 명시하는 언어.</p>
<p>따라서 SQL 엔진은 논리적으로 같은 결과를 만들 수 있다면
아무리 실행순서를 바꿔도 된다.</p>
<p>🧠 순서가 바뀔 수 있지만 의미는 동일하다</p>
<p>SQL의 논리적 순서(개발자가 읽는 순서):</p>
<p>FROM</p>
<p>WHERE</p>
<p>GROUP BY</p>
<p>HAVING</p>
<p>SELECT</p>
<p>ORDER BY</p>
<p>하지만 실제 옵티마이저는 이렇게 안 한다.</p>
<p>예를 들어:</p>
<p>WHERE 조건을 먼저 적용할 수도 있고</p>
<p>JOIN 순서를 바꿀 수도 있고</p>
<p>서브쿼리를 먼저 실행할 수도 있고</p>
<p>인덱스를 먼저 읽고 필요한 것만 JOIN할 수도 있다</p>
<p>하지만 최종적으로는 “표현된 조건을 만족하는 row”만 출력해야 한다는 규칙은 바뀌지 않는다.</p>
<p>그래서 결과는 절대 변하지 않는다.</p>
<p>📌 상관 서브쿼리(Correlated Subquery)라면 더 안전하다</p>
<p>너가 사용한 쿼리 형태:</p>
<p>AND CMPP2.INSTCD = CMPP.INSTCD</p>
<p>이건 상관 서브쿼리이기 때문에
서브쿼리는 반드시 메인 CMPP의 값에 의존해서 실행된다.</p>
<p>이 때문에 더욱 결과가 변할 가능성이 없다.</p>
<p>📁 예시로 이해해보자</p>
<p>이 쿼리의 논리는:</p>
<p>메인 CMPP의 CMPYCD, INSTCD, APPYEAR 값과 정확히 같은 CMPP2 중에서 MIN(APPYEAR)을 구한다.</p>
<p>이 논리는 JOIN 순서가 어떻게 바뀌어도 달라지지 않는다.</p>
<p>잘못된 예측이 가능해보이는 경우</p>
<p>“옵티마이저가 CMPP2를 전체 스캔해서 먼저 모은 후 CMPP와 비교하면 어떻게 되지?”</p>
<p>→ 그래도 아래 조건이 있기 때문에 결과는 같음:</p>
<p>CMPP2.INSTCD = CMPP.INSTCD
CMPP2.APPYEAR = CMPP.APPYEAR
CMPP2.CMPYCD = CMPP.CMPYCD</p>
<p>즉 메인 CMPP에서 필터된 값만 비교 대상이 된다.</p>
<p>🚨 왜 실행 순서가 바뀌어도 결과는 같을 수밖에 없음?
SQL의 핵심 보장사항:</p>
<p>WHERE 절의 의미는 “이 조건을 만족하는 행만 결과에 포함하라”이지
“이 조건을 이 순서로 처리하라”는 뜻이 아니다.</p>
<p>즉:</p>
<p>어떤 테이블을 먼저 읽든</p>
<p>어떤 인덱스를 사용하든</p>
<p>어떤 조건을 먼저 적용하든</p>
<p>서브쿼리를 먼저 읽든</p>
<p>JOIN을 먼저 하든</p>
<p>조건의 논리적 의미만 지켜지면
결과는 100% 동일하다.</p>
<p>🎯 결론
✔ 옵티마이저가 실행순서를 바꿔도 결과는 절대 변하지 않는다</p>
<p>SQL의 선언적 특성 + WHERE 조건의 논리적 의미 때문.</p>
<p>✔ JOIN 순서가 바뀌어도 동등 조인(EQUI JOIN)은 결과가 항상 동일하게 나온다.
✔ 상관 서브쿼리라서 메인 row에 종속 → 더 안정적</p>
<p>메인 쿼리에서 CMPP와 CMPY가 JOIN되고 서브쿼리에서 CMPP2와 CMPP가 JOIN되는데 서브쿼리에서 JOIN당시의 CMPP2는 그냥 완전 처음 조회한 CMPP랑 JOIN이야 아니면 메인쿼리에서 좀 걸러진 기준인거야?</p>
<p>✅ 핵심 요약</p>
<p>서브쿼리는 메인 쿼리의 각 ROW마다 실행된다 (Scalar Subquery).</p>
<p>서브쿼리 안에서 사용하는 CMPP 값은
메인 쿼리에서 JOIN·WHERE까지 모두 적용된 결과의 CMPP 행이다.</p>
<p>즉, CMPP2는 “메인에서 필터링된 CMPP의 값”과 JOIN된다.</p>
<p>▶ 서브쿼리는 이렇게 실행됨</p>
<p>메인쿼리 B의 첫 번째 행(B.id = 10)을 기준으로 서브쿼리 실행:</p>
<p>SELECT ...
FROM C
WHERE C.id = 10   -- B.id(필터링된 값)</p>
<p>그리고 두 번째 행(B.id = 12)을 기준으로 다시 실행:</p>
<p>SELECT ...
FROM C
WHERE C.id = 12   -- B.id(필터링된 값)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] Query&Command]]></title>
            <link>https://velog.io/@lefoa-98/Spring-QueryCommand</link>
            <guid>https://velog.io/@lefoa-98/Spring-QueryCommand</guid>
            <pubDate>Sat, 25 Oct 2025 12:21:34 GMT</pubDate>
            <description><![CDATA[<h3 id="1-ddd의-기본-관점">1. DDD의 기본 관점</h3>
<ul>
<li>기존 구조: <strong>DB 테이블 기준으로 서비스</strong>가 나눠짐.</li>
<li>DDD: <strong>도메인이 맡은 역할</strong>을 기준으로 서비스와 엔티티가 나눠져야 함.<ul>
<li>예: 커뮤니티 도메인 → 질문, 답변, 게시글 등으로 각각 역할/엔티티/서비스 분리.</li>
<li>그 후 FACADE에서 이들을 조합.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="2-현재-문제-상황">2. 현재 문제 상황</h3>
<ul>
<li><code>CourseProgress(user-course 매핑 엔티티)</code>를 같은 방식으로 분리 시도.</li>
<li>그런데 <code>CourseComplexReader</code> 안에 있는 <code>searchMyCourse()</code>와 신규 기능 로직이 거의 동일.</li>
<li>문제:<ul>
<li>한 클래스(<code>CourseComplexReader</code>)에 합치기는 애매하다.</li>
<li>그렇다고 분리하려니 중복되는 로직을 어디에 둬야 할지 고민.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="3-구조적-의문">3. 구조적 의문</h3>
<ul>
<li><code>Course</code>와 <code>CourseTag</code>처럼 연관은 있지만, 도메인별로 <strong>엔티티와 서비스는 나눠야 한다</strong>는 게 기본 전제.</li>
<li>그런데 지금 <code>CourseComplexReader</code>는 사실상 <strong>도메인 서비스</strong>라기보다는 <strong>QueryService</strong>에 가까움.<ul>
<li>즉, Course 관련 여러 부수적인 데이터(tags, 썸네일 파일 등)를 다 조합해서 조회하는 역할.</li>
<li>그래서 “도메인 서비스는 순수해야 한다”는 원칙이 깨지고 있음.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="4-제안하는-대안">4. 제안하는 대안</h3>
<ul>
<li><p><strong>Reader/Writer(Service를 단순히 조회/저장으로 나누는 구조)</strong> 대신,</p>
<ul>
<li><p><strong>Manager</strong>: 순수 도메인 CRUD 관리</p>
</li>
<li><p>QueryService(공통 조회/DTO 전용 서비스)</p>
<p>로 나누는 게 더 맞아 보임.</p>
</li>
</ul>
</li>
<li><p>Manager는 구현부가 적어 보일 수 있지만, 원래 도메인 규칙은 간결해야 하며 추후 규칙 추가될 때 늘어난다.</p>
</li>
</ul>
<p>Comment | <a href="https://www.notion.so/Reader-Writer-CQRS-124-26a8206157048032a805edacb717ead0?pvs=21">Reader/Writer를 CQRS로 보면? (#124)</a> </p>
<hr>
<h3 id="5-현실적인-고민">5. 현실적인 고민</h3>
<ul>
<li>이 구조로 근본적 문제를 다 해결하려면 공수가 크다.</li>
<li>그래서 차선책으로는:<ul>
<li>기존 구조 유지</li>
<li>다만 <code>CourseComplexReader</code>를 <strong>QueryService</strong> 성격으로 인정하고, 신규 기능도 여기에 구현해서 <strong>중복을 줄인다</strong></li>
</ul>
</li>
</ul>
<hr>
<h3 id="readerwriter를-cqrs로-보면-124">Reader/Writer를 CQRS로 보면? (#124)</h3>
<ul>
<li><p>CQRS (Command Query Responsibility Segregation)</p>
<p>  <strong>“명령(Command)”과 “조회(Query)”를 분리하는 아키텍처 패턴</strong></p>
<ul>
<li><strong>Command(명령)</strong>: 데이터 <strong>변경</strong> 작업 (등록, 수정, 삭제)
→ 주로 @Transactional</li>
<li><strong>Query(조회)</strong>: 데이터 <strong>읽기</strong> 작업 (검색, 목록, 상세 조회) 
→ 주로 @Transactional(readOnly = true)</li>
</ul>
</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Architecture] Multi-Module Architecture vs MSA]]></title>
            <link>https://velog.io/@lefoa-98/Architecture-Multi-Module-Architecture-vs-MSA</link>
            <guid>https://velog.io/@lefoa-98/Architecture-Multi-Module-Architecture-vs-MSA</guid>
            <pubDate>Tue, 30 Sep 2025 06:37:50 GMT</pubDate>
            <description><![CDATA[<h2 id="🔑-1-멀티모듈-아키텍처-multi-module-architecture">🔑 1. 멀티모듈 아키텍처 (Multi-Module Architecture)</h2>
<blockquote>
</blockquote>
<ul>
<li>하나의 애플리케이션(프로젝트) 안을 여러 모듈로 나누어 관리하는 방식.</li>
<li>보통 Gradle, Maven 같은 빌드 툴에서 모듈을 정의해서 공통 코드, 도메인별 코드 등을 분리함.</li>
<li>하나의 프로세스로 동작 → 배포 시에도 하나의 애플리케이션으로 패키징/배포</li>
<li>모듈 간 의존성을 빌드 툴로 관리 → 컴파일 타임에 강하게 결합</li>
<li>주로 코드 재사용과 관심사 분리가 목적</li>
<li>CI/CD 파이프라인도 대부분 하나</li>
</ul>
<p>ex :
core 모듈 → DTO, 유틸, 공통 로직
domain-user, domain-order 모듈 → 각 도메인별 비즈니스 로직
api 모듈 → Controller, REST API endpoint
하나로 빌드해서 app.jar 로 배포 =&gt; 모듈로만 나누어져 있고, 동일 JVM 내에 메서드 호출로 동작</p>
<pre><code class="language-ruby">Multi-Module-App/
 ├── common/         # 공통 DTO, 유틸
 ├── user/           # 사용자 도메인 (UserService, UserRepository)
 ├── order/          # 주문 도메인 (OrderService, OrderRepository)
 └── api/            # Controller, REST API</code></pre>
<hr>
<h2 id="🔑-2-msa-microservice-architecture">🔑 2. MSA (Microservice Architecture)</h2>
<blockquote>
</blockquote>
<ul>
<li>애플리케이션을 완전히 독립적인 서비스 단위로 쪼개어 배포하는 아키텍처.</li>
<li>각 서비스가 독립된 프로세스로 동작 → 네트워크 통신(REST, gRPC, 메시지 큐 등)으로 연결</li>
<li>각각 별도 배포/스케일링 가능</li>
<li>DB까지 분리하는 경우가 많음 (폴리글랏 퍼시스턴스)</li>
<li>팀별로 독립적으로 개발할 수 있음</li>
<li>장애 격리 가능 (특정 서비스가 죽어도 전체 서비스는 유지 가능)</li>
<li>CI/CD 파이프라인도 서비스별로 따로 운영</li>
</ul>
<p>ex :</p>
<p>user-service, order-service, payment-service → 각각 Spring Boot 애플리케이션
각자 따로 빌드 &amp; 배포 → 컨테이너 기반(Kubernetes)으로 스케일링 가능</p>
<pre><code class="language-ruby">MSA-Architecture/
 ├── user-service/   # 사용자 서비스
 └── order-service/  # 주문 서비스</code></pre>
<ol>
<li><p>user-service/src/main/java/.../UserController.java</p>
<pre><code class="language-java">@RestController
public class UserController {
 @PostMapping(&quot;/users&quot;)
 public UserDto createUser(@RequestParam String name) {
     return new UserDto(1L, name);
 }
}</code></pre>
</li>
<li><p>order-service/src/main/java/.../OrderController.java</p>
<pre><code class="language-java">@RestController
@RequiredArgsConstructor
public class OrderController {
 private final RestTemplate restTemplate = new RestTemplate();

 @PostMapping(&quot;/orders&quot;)
 public String createOrder(@RequestParam String username) {
     // user-service 호출
     UserDto user = restTemplate.postForObject(
             &quot;http://localhost:8081/users?name=&quot; + username,
             null,
             UserDto.class
     );
     return user.name() + &quot;님의 주문 생성 완료&quot;;
 }
}</code></pre>
</li>
</ol>
<p>➡️ 실행 순서</p>
<p>user-service (8081 포트) 실행</p>
<p>order-service (8082 포트) 실행</p>
<p>POST /orders?username=철수 요청 → order-service → 내부에서 user-service HTTP 호출 → 결과 조합</p>
<p>서비스별 별도 배포 &amp; 실행</p>
<p>통신은 HTTP (네트워크)</p>
<p>user-service 만 따로 스케일링 가능</p>
<hr>
<p>결론 : 멀티모듈은 코드 구조 정리를 주된 목적으로 / MSA는 배포 단위를 쪼개는 것을 목적으로 하는 아키텍처임. 따라서 멀티모듈은 하나의 AP이고, MSA는 여러개의 AP들이 모여서 동작하는 형태</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 맨날 헷갈리는 Stream 중간연산]]></title>
            <link>https://velog.io/@lefoa-98/JAVA-Stream</link>
            <guid>https://velog.io/@lefoa-98/JAVA-Stream</guid>
            <pubDate>Tue, 30 Sep 2025 06:16:22 GMT</pubDate>
            <description><![CDATA[<h2 id="map">.map()</h2>
<p>1️⃣ .map()</p>
<p>: 기존 스트림의 요소를 받아서, 어떤 함수로 바꿔서 새로운 스트림을 만듦(중간 연산) =&gt; 변환</p>
<p>ex :</p>
<pre><code class="language-java">List&lt;String&gt; names = List.of(&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;);

// 각 이름을 대문자로 바꾸고 싶을 때
List&lt;String&gt; upperNames = names.stream()
                               .map(String::toUpperCase) // map이 각 요소를 변환
                               .toList();

System.out.println(upperNames); // [ALICE, BOB, CHARLIE]
</code></pre>
<hr>
<h2 id="collect">.collect()</h2>
<p>2️⃣ .collect()</p>
<p>: 스트림의 최종 결과를 리스트, 세트, 맵 등 컬렉션으로 모을 때 사용 =&gt; 결과 </p>
<p>ex : </p>
<pre><code class="language-java">List&lt;String&gt; names = List.of(&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;);

List&lt;String&gt; upperNames = names.stream()
                               .map(String::toUpperCase) // 요소 변환
                               .collect(Collectors.toList()); // 리스트로 모으기

System.out.println(upperNames); // [ALICE, BOB, CHARLIE]</code></pre>
<hr>
<h2 id="예시">예시</h2>
<ul>
<li>이러한 메서드는 많이 써보는게 도움이 된다고 생각해서 여러 예시를 써봄</li>
</ul>
<p>ex1:</p>
<pre><code class="language-java">List&lt;String&gt; names = List.of(&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;);

List&lt;String&gt; result = names.stream()
                             .map(String::toUpperCase)
                             .collect(Collectors.toList());

System.out.println(result); // [ALICE, BOB, CHARLIE]</code></pre>
<hr>
<p>ex2:</p>
<pre><code class="language-java">record User(String name, int age) {}

List&lt;User&gt; users = List.of(
    new User(&quot;Alice&quot;, 25),
    new User(&quot;Bob&quot;, 30),
    new User(&quot;Charlie&quot;, 20)
);

// User 객체 → 이름(String)만 추출
List&lt;String&gt; names = users.stream()
                          .map(User::name) // 중간 연산
                          .collect(Collectors.toList()); // 최종 연산

System.out.println(names); // [Alice, Bob, Charlie]</code></pre>
<hr>
<p>ex3:</p>
<pre><code class="language-java">List&lt;Integer&gt; numbers = List.of(1, 2, 3, 4, 5);

// 제곱한 값을 리스트로
List&lt;Integer&gt; squared = numbers.stream()
                               .map(n -&gt; n * n) // 변환
                               .collect(Collectors.toList());

System.out.println(squared); // [1, 4, 9, 16, 25]</code></pre>
<hr>
<p>ex4:</p>
<pre><code class="language-java">List&lt;String&gt; names = List.of(&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;);

// 이름 → 길이(Map)
Map&lt;String, Integer&gt; nameLengthMap = names.stream()
    .collect(Collectors.toMap(
        name -&gt; name,        // key
        name -&gt; name.length() // value
    ));

System.out.println(nameLengthMap); 
// {Alice=5, Bob=3, Charlie=7}</code></pre>
<hr>
<p>ex5:</p>
<pre><code class="language-java">List&lt;Integer&gt; numbers = List.of(1, 2, 3, 4, 5);

// map으로 변환 후 합계
int sum = numbers.stream()
                 .mapToInt(n -&gt; n * 2) // 중간 연산 (2배 변환)
                 .sum();               // 최종 연산 (합계)

System.out.println(sum); // 30</code></pre>
<hr>
<p>※ 여기서 4번 예시에는 처음 구현할 때, .map(name -&gt; Map.entry(name, name.length()))과 같이 변환해서 써야하는거 아닌가? 싶어서 collect와 map의 차이에 혼동이 오기 시작했다.
=&gt; 여기선 Collectors.toMap(k,v)가 각 요소를 받아 직접 만듦으로써, 변환과 수집(결과)를 동시에 내주어서 map을 쓰지 않고 간결하게 할 수 있음!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Architecture] DDD]]></title>
            <link>https://velog.io/@lefoa-98/Architecture-DDD</link>
            <guid>https://velog.io/@lefoa-98/Architecture-DDD</guid>
            <pubDate>Fri, 19 Sep 2025 06:03:02 GMT</pubDate>
            <description><![CDATA[<h2 id="기존-개발-방식의-문제점">기존 개발 방식의 문제점</h2>
<blockquote>
</blockquote>
<ul>
<li>해당 부분은 현재 회사에서도 지독하게 느끼는 부분이다</li>
<li>DB 스키마를 먼저 기준으로 설계하여 여기에 맞춘 MVC 패턴 구축하여 <strong>&quot;DB에 종속적임&quot;</strong> <blockquote>
<p>(말이 MVC이지 구조만 있고 Controller에서 대부분 로직 처리 + Service로직은 join,파이프라인,비즈니스로직 붙여놓은 Query만 왔다갔다만 함.. 이외는 또 화면에서 로직 처리...)</p>
</blockquote>
</li>
<li>프로젝트 규모가 크고, 도메인이 복잡한 상태에서 도메인 규칙 반영이 어렵고, 중복된 코드/어디서 일어나는지 알기 어려운 버그 쉽게 발생
ex: 비즈니스 로직이 Service 레이어에 로직 몰림</li>
</ul>
<hr>
<h2 id="데이터-저장소에-종속적이란">데이터 저장소에 종속적이란??</h2>
<blockquote>
<ul>
<li>기존 방식(특히 DB 주도 설계) 흐름 + JPA</li>
</ul>
</blockquote>
<ol>
<li>DB 스키마 먼저 설계 (테이블, 컬럼, PK, FK 등)</li>
<li>그걸 기반으로 Entity 클래스 자동 생성 (ORM 매핑)</li>
<li>Service, Controller 코드에서 Entity = 데이터 구조체처럼 사용</li>
<li>비즈니스 로직도 DB 테이블의 구조에 맞춰 작성</li>
</ol>
<p>즉, 비즈니스 로직이 DB 스키마 형태에 묶여서 설계됨.</p>
<pre><code class="language-java">// Account 테이블: id, user_id, balance
@Entity
@Table(name = &quot;account&quot;)
public class AccountEntity {
    @Id Long id;
    Long userId;
    BigDecimal balance;
}

// Service 레이어
public void transfer(Long fromId, Long toId, BigDecimal amount) {
    AccountEntity from = accountRepository.findById(fromId);
    AccountEntity to = accountRepository.findById(toId);

    if (from.getBalance().compareTo(amount) &lt; 0) { 
        throw new RuntimeException(&quot;잔액 부족&quot;);
    }

    from.setBalance(from.getBalance().subtract(amount)); // 여기
    to.setBalance(to.getBalance().add(amount));

    accountRepository.save(from);
    accountRepository.save(to);
}</code></pre>
<p>※ 상태 변경(balance 감소)이 서비스에서 이뤄짐 → 여러 서비스에서 중복될 가능성 큼
※ Account라는 비즈니스 개념이 단순 데이터 컨테이너로 전락
※ 서비스 로직에서 BigDecimal 직접 다룸 (도메인 개념 없음)</p>
<hr>
<ul>
<li><p>그럼 DDD면 어떤데??</p>
<pre><code class="language-java">public class Account {
  private Money balance;

  public void withdraw(Money amount) {
      if (balance.isLessThan(amount)) throw new InsufficientException();
      balance = balance.minus(amount);
  }
}
</code></pre>
</li>
</ul>
<p>public class TransferService {
    public void transfer(Account from, Account to, Money amount) {
        from.withdraw(amount);
        to.deposit(amount);
    }
}</p>
<p>```</p>
<ol>
<li><p>withdraw() 안에서 도메인 규칙 캡슐화</p>
</li>
<li><p>서비스에서는 단순히 account.withdraw(amount)만 호출 → 중복 방지</p>
</li>
<li><p>Money라는 값 객체를 써서 통화/단위 같은 도메인 개념까지 표현 가능</p>
</li>
<li><p>JPA 말고도 다른 저장소로 바꿀 때 (예: NoSQL, 외부 API) 도메인 로직은 그대로 재사용 가능</p>
</li>
</ol>
<hr>
<h2 id="정리--ddd-방식은-도메인-중심-설계">정리 : DDD 방식은? (도메인 중심 설계)</h2>
<blockquote>
</blockquote>
<ul>
<li>데이터 저장보다 도메인 <strong>&quot;규칙/행위&quot;</strong> 를 코드로 표현하는 데 집중
ex: 커뮤니티라는 도메인 =&gt; 질문, 답변, 좋아요 등등 해야하는 규칙/행위 정의 후에 각각의 도메인 모델 구성하여 각각을 책임질 모듈 구성(Controller, Service, Domain, Repository ,FacadeSerive ,infra 등..)으로 나누어 처리</li>
<li>도메인 로직은 Entity, Value Object, Domain Service로 분산 =&gt; Service Layer 책임 ↓
※ Entity에는 해당 로직이 도메인과 밀접하게 관련된 경우(상태 변경 등) / Domain Service에 여러 엔티티/VO가 함께 참여하는 로직을 작성한다</li>
<li>인프라(DB, 메시징, UI)는 도메인에 종속되지 않고 바꿀 수 있음 =&gt; 이전 Mysql에 종속된 SQL이였는데 Oracle로 바꾸래.. =&gt; 이전 구조였으면 끔찍..</li>
<li>DB 없이 도메인 모델 자체로 유닛 테스트 구성해서 안정성 ↑ </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Build] gradle vs yml]]></title>
            <link>https://velog.io/@lefoa-98/Build-gradle-vs-yml</link>
            <guid>https://velog.io/@lefoa-98/Build-gradle-vs-yml</guid>
            <pubDate>Thu, 18 Sep 2025 00:37:21 GMT</pubDate>
            <description><![CDATA[<h2 id="buildgradle">build.gradle</h2>
<blockquote>
</blockquote>
<ul>
<li>Gradle 빌드 스크립트 파일</li>
<li>프로젝트를 어떻게 빌드하고, 어떤 의존성(dependency) 을 가져올지 정의하는 곳 </li>
<li>실행 시점이 아니라 빌드 시점(compile, test, package) 에 영향을 줌</li>
</ul>
<p>ex) 소스코드에서 JPA관련 Repository 사용, Spring boot 기반 WAS 사용 etc...</p>
<pre><code>plugins {
    id &#39;java&#39;
    id &#39;org.springframework.boot&#39; version &#39;3.2.5&#39;
}

dependencies {
    implementation &#39;org.springframework.boot:spring-boot-starter-web&#39;
    implementation &#39;org.springframework.boot:spring-boot-starter-data-jpa&#39;
    runtimeOnly &#39;com.mysql:mysql-connector-j&#39;
    testImplementation &#39;org.springframework.boot:spring-boot-starter-test&#39;
}</code></pre><p>=&gt; 즉, 우리가 작성한 소스를 &quot;<strong>컴파일하여 실행가능한 jar 파일( build )을 만들 때</strong>&quot; 해당 프로젝트는 JPA, Mysql Driver, Spring Web Module 등이 필요하다는 정보를 나타내는 곳</p>
<hr>
<h2 id="applicationyml">application.yml</h2>
<blockquote>
</blockquote>
<ul>
<li>Spring Boot 실행 환경 설정 파일</li>
<li>애플리케이션이 실행될 때 필요한 설정값을 정의</li>
<li>DB 연결, 포트 번호, 로그 레벨, 외부 API 키, Profile별 환경 설정 등에 사용</li>
</ul>
<p>ex)</p>
<pre><code>spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: user
    password: pass

server:
  port: 8080

logging:
  level:
    root: INFO</code></pre><p>=&gt; 즉, jar를 &quot;<strong>실행할 때</strong>&quot; DB는 어디에 연결하고 포트는 몇 번을 쓸 지, 어떤 환경에서 어떻게 동작할까? 에 대한 설정이다</p>
<hr>
<h2 id="env">.env</h2>
<blockquote>
</blockquote>
<ul>
<li>yml파일에서 런타임시점 설정을 해주는 건 좋은데 해당 설정 파일에 중요한 정보가 너무 많이 들어가 있네...?</li>
<li>value 값들을 .env파일로 분리하고 ${<del>~</del>}로 바인딩 받아서 쓰자! 에서 나온 파일</li>
<li>해당 파일은 인코딩 과정을 거쳐서 서버에 올려야함</li>
</ul>
<p>.env</p>
<pre><code class="language-.env">SWAGGER_SERVER_URL= 도메인 주소
DB_URL=DB 연결정보
DB_USERNAME=이름
DB_PASSWORD=비밀번호</code></pre>
<p>application.yml</p>
<pre><code>  datasource:
    url: ${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
    driver-class-name: org.postgresql.Driver
    hikari:
      maximum-pool-size: 8
      minimum-idle: 2
      connection-timeout: 3000
      idle-timeout: 60000</code></pre><ul>
<li>Docker기반으로 EC2에 올렸다면?
=&gt; 해당 docker-compose 파일과 동일 경로에 .env 넣고 컴포즈 파일에 정의해주면 결국엔 런타임 시점에 설정하는( yml에 넣어주는거니까! ) 값들을 세팅해줌.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Design Pattern] Facade Service]]></title>
            <link>https://velog.io/@lefoa-98/Design-Pattern-Facade-Service</link>
            <guid>https://velog.io/@lefoa-98/Design-Pattern-Facade-Service</guid>
            <pubDate>Tue, 16 Sep 2025 08:55:44 GMT</pubDate>
            <description><![CDATA[<h2 id="찾아보게된-배경">찾아보게된 배경</h2>
<blockquote>
<p>실제 Legacy 환경의 현업에서 일한지 대략 1년 쯤 된 지금... 
어느정도 기능의 측면에서 패키지 구조가 분리되어 있지만, 패키지 안에 단일 Controller, Service, Repository로 구성된 곳에서 일하다보니 일단 하나의 서비스 클래스 파일에서 소스가 3~40000줄은 기본으로 넘는다.
여기서 특정 기능의 추가나 디버깅, 혹은 메서드 수정을 할 때 A라는 도메인에서 쓰여야할 private한 메서드가 기능적으로 어느정도 비슷해서 B,C,D 와 같은 로직에서도 사용하고.. 뭐 하나를 고치려고 찾는 것 조차도 힘들다.
그리고 실제 필요한 기능이 있어서 구현했더니 , 나중가서 보니 23000번째 라인에서 구현이 되어있다던가... </p>
</blockquote>
<ul>
<li>이러한 문제를 해결할 만한 패턴이 있나 찾아보던 중 <strong>Facade Service</strong>를 발견!</li>
</ul>
<hr>
<h2 id="what-is-facade-service">What is Facade Service?</h2>
<blockquote>
</blockquote>
<ul>
<li>여러 세부 서비스들을 한 번에 묶어서, 외부에서 보기에 단일한 인터페이스를 제공하는 것</li>
<li>이를 위해서는 각각의 순수 도메인에 관련하여 비즈니스 로직을 구현한 서비스가 존재해야함
=&gt; 이를 이용해 특정 기능을 구현할 때 해당 도메인 서비스( &quot;ModuleService&quot; )들을 조합하여 사용!</li>
</ul>
<ul>
<li>즉 일종의 오케스트레이터 역할임.</li>
<li>각 도메인 서비스는 본인 책임의 로직만 신경 쓰고, Facade에서 이들을 통합하여 사용하기 편하게 하면서 트랜잭션 경계나 순서 제어같은 흐름 제어를 할 수 있음.</li>
</ul>
<hr>
<h2 id="multi-module-architecture과-연계하면">Multi-Module Architecture과 연계하면?</h2>
<blockquote>
<p>ex. OrderFacadeService가 주문 + 결제 + 포인트를 함께 처리
즉 3개의 도메인의 협력이 필요할 때 해당 Facade에서 의존하고, 각각의 도메인 서비스는 순수하게 유지할 수 있음 (기존 단순 service + repo 구조는, 반드시 service끼리의 의존 혹은 service에서 필요한 repo에 의존을 걸어 데이터를 가져와야해서 경계가 무너짐) / @Transcation 흐름 묶기도 편함</p>
</blockquote>
<p>예시코드 : </p>
<pre><code class="language-java">@Service
@RequiredArgsConstructor
public class OrderFacadeService {
    private final OrderService orderService;
    private final MemberService memberService; 
    private final PaymentService paymentService; // 
    //해당 Service들은 만약 다형성이 필요해 구현체를 갈아끼울 가능성이 있다면 interface     
    //그 외에는 구체 클래스로 구현하여 관리포인트 down


    @Transactional
    public OrderResultDto placeOrder(OrderRequestDto request) {
        Member member = memberService.findById(request.getMemberId());
        Order order = orderService.createOrder(member, request.getItems());
        paymentService.pay(order);
        return OrderResultDto.of(order);
    }
}</code></pre>
<ul>
<li>+DDD
<img src="https://velog.velcdn.com/images/lefoa-98/post/fbcf2e1c-0b7e-47eb-b056-4dadeabfba68/image.png" alt=""></li>
<li>domain: 순수 비즈니스 로직, 규칙, 불변식 관리</li>
<li>application: 유스케이스 조율 (Facade처럼 orchestrator 역할) + @</li>
<li>infrastructure: JPA, DB, 외부 API 연동</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DevOps]도커 & 쿠버네티스]]></title>
            <link>https://velog.io/@lefoa-98/%EB%8F%84%EC%BB%A4-%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4</link>
            <guid>https://velog.io/@lefoa-98/%EB%8F%84%EC%BB%A4-%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4</guid>
            <pubDate>Mon, 25 Aug 2025 07:50:24 GMT</pubDate>
            <description><![CDATA[<h2 id="도커의-기초1--what-is-virtual-solution">도커의 기초1 , What is Virtual Solution?</h2>
<blockquote>
</blockquote>
<ul>
<li>가상화 솔루션도 결국 1개의 어플리케이션에 불과함(os 기준) =&gt; os한테 자원 받고 하드웨어가 없음에도 하나의 컴퓨터 처럼 있는거임(겜도 하고 통신도 하고 서버도 되고)</li>
<li>cpu, 메모리 ,스토리지 ,네트워크 +@ 등, 컴퓨터 리소스  관장하는 OS(host)에서 리소스들을 할당받아 마치 독립된 PC의 구성 == <strong>가상화 솔루션</strong>의 기초</li>
</ul>
<hr>
<h2 id="도커의-기초2--what-is-virtual-machine">도커의 기초2 , What is Virtual Machine?</h2>
<blockquote>
</blockquote>
<ul>
<li>가상화 솔루션 위에 os 설치하려면 cd/usb에 모든 파일을 설치를 넣어서 하는데, 이거처럼 무엇을 쓰던 결국 os 설치용 모든 파일을 1개에 묶어야함 =&gt; iso 파일(압축파일)</li>
<li>가상화 솔루션에 iso 이미지를 올려 가상화 머신으로 만듦 =&gt; window, linux , mac 등</li>
<li>머신끼리 통신 =&gt; 네트워크 필요 =&gt; NAT 기반이면 직접 IP에 쏘는게 아니고, NAT를 무조건 경유해야함(포트포워딩 , 라우터역할) =&gt; Host only를 기반으로 하면 직접 ip 통신 서로 가능</li>
<li>socket = ip + port</li>
</ul>
<hr>
<h2 id="그래서-도커가-뭔데">그래서 도커가 뭔데?</h2>
<blockquote>
</blockquote>
<ul>
<li>가상솔루션에 이미지 파일로 가상머신(win,linux,mac...)을 만들고 하는 과정이 도커와 정확히 동일.</li>
<li>도커 == 가상솔루션, 도커 이미지(코드+환경) == iso 이미지 / 컨테이너 실행 == 이미지 실행</li>
<li>다만 가상화 솔루션은 가상화 머신을 만들지만, docker는 프로세스(프로그램)를 컨테이너로 가상화( 가상화란? 어디서든 돌아갈 수 있게 추상화 했다는 말로 이해해도 무관할 듯) =&gt; 가상화 솔루션이 네트워크, cpu 등 리소스만 받으면 각각 독립적으로 돌아갈 수 있음. </li>
<li>도커로 인해 어느 os에도 동작하는 프로그램을 정확히 가능 + 스냅샷 찍어놓고 해당 버전으로 돌려서 마구 테스트 가능 + 어디서든 동작 가능(코드+ 실행환경)</li>
<li>컨테이너끼리 통신하려면 결국 완전 독립상태여서 네트워크(ip port=socket 필요)</li>
</ul>
<hr>
<h2 id="dockerfile-vs-dockerimage">DockerFile vs DockerImage</h2>
<ul>
<li>🔹 1. Dockerfile</li>
</ul>
<p>정의: Docker 이미지를 만들기 위한 설명서(레시피)</p>
<p>내용: 어떤 OS 기반으로 시작할지, 어떤 패키지를 설치할지, 어떤 포트/환경 변수를 쓸지, 실행할 명령어 등을 적어둔 텍스트 파일</p>
<p>확장자: 보통 Dockerfile (확장자 없음)</p>
<pre><code>베이스 이미지 선택
FROM node:20-alpine

소스코드 복사
COPY . /app

작업 디렉토리 설정
WORKDIR /app

의존성 설치
RUN npm install

컨테이너 실행 시 실행될 명령어
CMD [&quot;node&quot;, &quot;index.js&quot;]</code></pre><p>Entry point = 외부 overwrite 불가 , cmd = 외부 overwrite 가능
우선순위 : E.P &gt; CMD , 둘 다 있으면 EP 실행 + CMD는 Argument로</p>
<ul>
<li>🔹 2. Docker Image</li>
</ul>
<p>정의: Dockerfile을 빌드해서 나온 결과물(실행 가능한 패키지)</p>
<p>형태: 여러 개의 레이어(layer)로 쌓여 있음 (각 레이어는 변경 불가, overlayfs 사용)</p>
<p>용도: 컨테이너를 실행할 수 있는 실제 실행 파일 덩어리</p>
<pre><code>생성 방법

docker build -t myapp:1.0 .


→ 현재 디렉토리의 Dockerfile을 읽어 myapp:1.0이라는 이미지 생성</code></pre><ul>
<li>🔹 3. Dockerfile vs Docker Image 차이
이미지 만드는 레시피    / 컨테이너 실행 가능한 결과물
텍스트 파일    / 바이너리 패키지(레이어 구조)
쉽게 수정 가능 / 수정 불가 (새로 빌드 필요)
직접 실행 불가 / 바로 컨테이너 실행 가능
예시    FROM ubuntu:20.04 / ubuntu:20.04 이미지</li>
</ul>
<hr>
<h2 id="docker-compose-vs-k8s">Docker Compose vs K8s</h2>
<ul>
<li>🔹 1. Docker Compose</li>
</ul>
<p>용도: 개발 환경에서 여러 개의 컨테이너를 동시에 띄우고 관리하기 쉽게 해줌.</p>
<p>방식: docker-compose.yml 파일에 서비스(예: DB, 백엔드, 프론트엔드)를 정의하고 docker-compose up 한 번으로 실행.</p>
<p>특징:</p>
<p>주로 단일 서버에서 동작.</p>
<p>네트워크, 볼륨, 환경 변수 등을 쉽게 정의 가능.</p>
<p>운영보다는 개발·테스트 환경에서 많이 씀.</p>
<p>예시: 백엔드(Spring Boot), DB(Postgres), 캐시(Redis)를 한 번에 로컬에서 띄워서 개발.</p>
<ul>
<li>🔹 2. Kubernetes (K8s)</li>
</ul>
<p>용도: <strong>대규모 운영 환경(프로덕션)</strong>에서 수많은 컨테이너를 자동으로 배포, 확장, 관리.</p>
<p>방식: YAML 매니페스트 파일로 Pod, Deployment, Service, Ingress 등을 정의 → 쿠버네티스 클러스터가 자동으로 스케줄링, 배포, 헬스체크, 롤링 업데이트 등을 수행.</p>
<p>특징:</p>
<p>멀티 노드(여러 서버) 환경을 관리 가능.</p>
<p>자동 확장(autoscaling), 자동 복구(self-healing), 로드밸런싱 지원.</p>
<p>운영환경에 적합 → 클라우드(AWS EKS, GCP GKE, Azure AKS 등)와 잘 맞음.</p>
<p>복잡하지만 확장성, 안정성이 강점.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] Index]]></title>
            <link>https://velog.io/@lefoa-98/DB-Index</link>
            <guid>https://velog.io/@lefoa-98/DB-Index</guid>
            <pubDate>Mon, 28 Oct 2024 23:12:41 GMT</pubDate>
            <description><![CDATA[<h2 id="index란">Index란?</h2>
<blockquote>
<p>데이터를 빠르게 탐색하기 위해 사용한다. 특정 컬럼에 대해 값을 정렬하고 이를 통해 빠르게 데이터를 찾을 수 있도록 도와준다.</p>
</blockquote>
<ul>
<li>단점<ul>
<li>인덱스도 데이터기 때문에 저장하기 위해  별도의 저장 공간 필요</li>
<li>CUD가 빈번한 테이블에 인덱스를 걸게 되면 데이터 변경 시 인덱스도 함께 수정되야 하기 때문에 성능 저하가 발생한다. =&gt; 수정이 아닌 삭제 및 재등록</li>
</ul>
</li>
</ul>
<h2 id="db-btree-index가-범위-검색이-빠른-이유는">DB Btree Index가 범위 검색이 빠른 이유는?</h2>
<blockquote>
<ul>
<li>정렬된 순서를 유지하기 때문에 한번 찾은 값 다음 값이 동일한 경로 혹은 인접한 노드에 존재하므로 범위 검색에 효율적</li>
</ul>
</blockquote>
<ul>
<li>B+Tree에서는 모든 데이터가 리프 노드에 저장 되어 있고 리프 노드는 연결 리스트 형태로 연결 되어 있기 때문에 링크 통해 이동하며 빠르게 범위 검색을 할 수 있다.</li>
</ul>
<h2 id="동작방식">동작방식</h2>
<blockquote>
<ul>
<li>인덱스는 Btree 자료 구조로 저장되며</li>
</ul>
</blockquote>
<ul>
<li>Btree에서 데이터를 저장하는 곳을 노드 라고 합니다.</li>
<li>노드는 최상단에 있는 루트노드, (브랜치 노드), 리프 노드로 구성 되며 MySQL 에서는 이 노드를 페이지라고 합니다.</li>
<li>인덱스는 페이지 단위(8kbyte)로 저장되며 인덱스 키를 기준으로 정렬 됩니다.</li>
<li>따라서 인덱스 탐색 시 루트노드부터 시작하여 찾으려는 값과 키를 비교하여 작다면 왼쪽으로, 크다면 오른쪽으로 이동합니다.</li>
<li>이 과정을 거쳐 브랜치 노드에서 리프 노드에 도달하고</li>
<li>리프노드에 있는 인덱스 키와 pk 쌍을 사용하여  데이터 영역에서 실제 레코드에 접근하게 됩니다.<br><img src="https://velog.velcdn.com/images/lefoa-98/post/aaa067f5-a5c5-463c-9052-1220f0ab45e2/image.png" alt=""></li>
</ul>
<h2 id="cluster-index">Cluster Index</h2>
<blockquote>
<ul>
<li>테이블 당 하나의 클러스터 인덱스만 존재</li>
</ul>
</blockquote>
<ul>
<li>보통 PK가 클러스터 인덱스가 된다 ( PK가 없다면 not null Unique index  중 첫번째→ Unique index 없다면 임의로 유니크한 값을 생성하여 사용)</li>
<li>클러스터 인덱스 순서로 실제 데이터가 정렬된다</li>
<li>인덱스의 리프 페이지가 곧 데이터 이기 때문에 논 클러스터 인덱스 보다 용량을 적게 사용함
<img src="https://velog.velcdn.com/images/lefoa-98/post/8c51e31c-b1da-4dc0-83af-1cf16bd2e180/image.png" alt=""></li>
</ul>
<h2 id="어떤-값을-pk로-만들어야-할까요">어떤 값을 PK로 만들어야 할까요?</h2>
<blockquote>
</blockquote>
<ul>
<li><p>인덱스 영역에 PK를 저장하고 있으므로 PK의 크기가 너무 크지 않은 것이 좋다</p>
</li>
<li><p>PK를 기준으로 실제 데이터가 정렬 되기 때문에 계속 증가하는 값으로 생성하는 것이 좋다</p>
<ul>
<li>그렇지 않으면 중간에 PK가 추가되면 데이터를 다시 정렬되어야 하기 때문에 성능 오버헤드가 발생한다.</li>
</ul>
</li>
<li><p>변하지 않는 값으로 설정하는 것이 중요하다.</p>
<ul>
<li>PK는 레코드의 물리적인 저장 위치를 결정한다.</li>
<li>따라서 PK가 변경되면 단순 값을 변경하는 것이 아니라 레코드가 저장된 위치도 변경 되어야 하기 때문에 레코드를 삭제하고 다시 저장해야한다.</li>
<li>따라서 두번의 디스크 I/O가 발생하기 때문에 비용이 상당히 크다.</li>
<li>따라서 PK는 변하지 않는 값으로 설정하는 것이 중요하다.</li>
</ul>
</li>
<li><p>PK를 만들지 않아도 알아서 내부 PK를 생성해주는데 굳이 PK를 생성하는 이유는?(⭐️)
=&gt; 내부 PK는 사용자에게 노출 되지 않기 때문에 쿼리에 사용할 수 없다. 클러스터 인덱스는 테이블당 하나만 가질 수 있고 빠르게 데이터에 접근할 수있기 때문에 활용할 수 있도록 PK를 생성하는 것이 좋다.</p>
</li>
</ul>
<blockquote>
</blockquote>
<p>ref) <a href="https://mangkyu.tistory.com">https://mangkyu.tistory.com</a></p>
<ul>
<li>참고할만한 ref: </li>
</ul>
<ol>
<li><a href="https://velog.io/@leehyeonmin34/weather-reminder-multi-column-index">https://velog.io/@leehyeonmin34/weather-reminder-multi-column-index</a></li>
<li><a href="https://velog.io/@sh93/%EB%B3%B5%ED%95%A9%ED%82%A4%EC%99%80-%EC%9D%B8%EB%8D%B1%EC%8A%A4%ED%82%A4-feat.-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8">https://velog.io/@sh93/%EB%B3%B5%ED%95%A9%ED%82%A4%EC%99%80-%EC%9D%B8%EB%8D%B1%EC%8A%A4%ED%82%A4-feat.-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Network] OSI 7 Layer]]></title>
            <link>https://velog.io/@lefoa-98/Network-OSI-7Layer</link>
            <guid>https://velog.io/@lefoa-98/Network-OSI-7Layer</guid>
            <pubDate>Thu, 03 Oct 2024 04:35:40 GMT</pubDate>
            <description><![CDATA[<hr>
<blockquote>
<p>항상 이론으로만 대충 알고, 막상 설명은 하지 못했던 OSI 7Layer에 대해 좋은 강의가 있어 참고하여 정리한 글입니다.</p>
</blockquote>
<p>참고영상: 쉬운코드님의 [입문용] 프로토콜과 OSI 7 layer 설명
<a href="https://www.youtube.com/watch?v=6l7xP7AnB64&amp;list=PLcXyemr8ZeoSGlzhlw4gmpNGicIL4kMcX&amp;index=2">https://www.youtube.com/watch?v=6l7xP7AnB64&amp;list=PLcXyemr8ZeoSGlzhlw4gmpNGicIL4kMcX&amp;index=2</a></p>
<hr>
<h2 id="네트워크">네트워크?</h2>
<blockquote>
<p>네트워크: 시스템끼리 통신할 수 있게 해줌
통신을 위해서는 약속된 통신 방법이 있어야함 =&gt; 네트워크 프로토콜</p>
</blockquote>
<p>※ 모든 통신을 위한 하나의 프로토콜이 존재할 수 없음 =&gt; 백엔드 구현시 한 클래스에서 모든 요청을 통신하며 처리하는 것과 같은 이치 =&gt; 모듈화 필요 =&gt; 기능별로 분리</p>
<h2 id="네트워크-기능">네트워크 기능?</h2>
<blockquote>
<p>애플리케이션 목적에 맞는 통신 방법 제공 =&gt; app 사이에 필요한 기능
신뢰할 수 있는 데이터 전송 방법 제공 =&gt; 해당 어플끼리 어떤 방식으로 데이터 전송할래?
목적지로 데이터 전송 =&gt; 실제 데이터 보낼때 호스트끼리 어떻게?
노드 사이의 데이터 전송 =&gt; 노드끼리 어떻게 전송?</p>
</blockquote>
<p>※ 각 계층별로 기능하는구나 =&gt; OSI7 Layer
※ 각 레이어에 맞게 프로토콜 세분화 + 각 레이어 프로토콜은 하위 레이어의 프로토콜 제공하는 기능을 사용하여 자신의 기능 구현</p>
<h2 id="osi-7-layer">OSI 7 Layer</h2>
<blockquote>
<p>application layer: 애플리케이션 목적에 맞는 통신 방법 제공 / HTTP DNS SMTP FTP
=&gt;통신 방법을 제공할 뿐, 실제로 어떻게 데이터가 보내지는 지는 아래 계층에서 구현해서 얘는 쓰기만 할 뿐</p>
</blockquote>
<p>presentation layer: 애플리케이션 간의 통신에서 메시지 포맷 관리 / 인코딩&lt;=&gt;디코딩 , 암호화&lt;=&gt; 복호화 , 압축&lt;=&gt;압축풀기</p>
<blockquote>
</blockquote>
<p>session layer: 애플리케이션 간의 통신에서 세션 관리</p>
<blockquote>
</blockquote>
<p>transport layer: 애플리케이션 간의 통신 담당으로(port) 목적지 애플리케이션으로 데이터 전송 / TCP UDP
=&gt; 어떻게 목적지까지 실제로 보내지는지는 얘의 관심사X, 아래의 network에서 구현한걸 쓸 뿐</p>
<blockquote>
</blockquote>
<p>network layer: 호스트 간의 통신 담당(IP), 목적지 호스트로 실제 데이터 전송 담당, 네트워크 간의 최적의 경로 결정(어떤 노드를 거쳐서 갈래?)
=&gt; 노드 결정해서 호스트로 어떻게 보낼지에 관한 내용이지, 노드끼리의 통신은 얘의 관심사X, 아래의 레이어가 구현한걸 쓸 뿐</p>
<blockquote>
</blockquote>
<p>data link layer: 직접 연결된 노드 간의 통신 담당 / MAC주소 기반 통신
=&gt; 가고 싶은 곳이 IP 주소로 network 레이어에 있으니, 이를 MAC 주소로 변환해서 실제 어떤 노드를 거쳐서 보내는지 알아야함 =&gt; ARP 프로토콜 </p>
<blockquote>
</blockquote>
<p>physical layer: 케이블이나 무선 같은 실제 매개체를 통해 bits 단위로 데이터 전송 </p>
<p><img src="https://velog.velcdn.com/images/lefoa-98/post/87c5560c-d17e-4822-a324-8de56097ab5f/image.png" alt=""></p>
<hr>
<h2 id="통신-예시-encapsulation--decapsulation">통신 예시 (encapsulation &amp; decapsulation)</h2>
<p>1.
<img src="https://velog.velcdn.com/images/lefoa-98/post/90af9c7e-81d6-4bdd-b153-4531687b9aa2/image.png" alt=""></p>
<blockquote>
<p>application layer에서 보내고자 하는 메세지에 포함해야할 정보를 담아 포장하여 presentation layer에 내려줌.(ex, HTTP프로토콜 사용)</p>
</blockquote>
<ol start="2">
<li><img src="https://velog.velcdn.com/images/lefoa-98/post/b3d54373-6324-42c9-b28c-4ab35c1f300c/image.png" alt=""></li>
</ol>
<blockquote>
<p>presentation layer에서 application layer에서 받은 것에 자신이 포함해야할 정보를 담아 포장하여 session layer에 내려줌. (ex, encoding utf-8)</p>
</blockquote>
<ol start="3">
<li><img src="https://velog.velcdn.com/images/lefoa-98/post/ecbd5af2-9fd0-4762-b16f-21e0ac435233/image.png" alt=""></li>
</ol>
<blockquote>
<p>session layer에서 presentation layer에서 받은 것에 자신이 포함해야할 정보를 담아 포장하여 transport layer에 내려줌</p>
</blockquote>
<ol start="4">
<li><img src="https://velog.velcdn.com/images/lefoa-98/post/1ffde1c0-b703-44bc-8ea4-6e6bad3181cd/image.png" alt=""></li>
</ol>
<blockquote>
<p>transport layer에서 session layer에서 받은 것에 자신이 포함해야할 정보를 담아 포장하여 network layer에 내려줌 (ex, tcp 프로토콜 + HTTP 프로토콜을 위한 포트 80)</p>
</blockquote>
<ol start="5">
<li><img src="https://velog.velcdn.com/images/lefoa-98/post/06e9b4d9-03f3-4f30-a4f8-bf148a4af8a5/image.png" alt=""></li>
</ol>
<blockquote>
<p>network layer에서 transport layer에서 받은 것에 자신이 포함해야할 정보를 담아 포장하여 data link layer에 내려줌 (ex, 목적지 IP주소 기입)</p>
</blockquote>
<ol start="6">
<li><img src="https://velog.velcdn.com/images/lefoa-98/post/f43dad4b-1087-410a-943a-ffd81457ee9e/image.png" alt=""></li>
</ol>
<blockquote>
<p>data link layer에서 network layer에서 받은 것에 자신이 포함해야할 정보( 해당 layer에서는 header뿐만 아니라 오류 체크용 trailer도 같이 포함 )를 담아 포장하여 physical layer에 내려줌 (ex, 목적지 IP주소를 ARP 프로토콜을 이용해 MAC주소로 변경하여, 어떤 노드로 내 데이터를 보내야할 지) </p>
</blockquote>
<p>7.
<img src="https://velog.velcdn.com/images/lefoa-98/post/15e07cf7-4873-4a43-9fe9-746f7a2e76db/image.png" alt=""></p>
<blockquote>
<p>physical layer에서 위에서 받은 것을 실제 bits로 변경하여 해당 노드로 전송 / 수신한 노드는 해당 데이터를 bits에서 패킷으로 재구성하여 data link layer로 올림</p>
</blockquote>
<p>8.
<img src="https://velog.velcdn.com/images/lefoa-98/post/ad18fafd-2ecf-4681-8b05-d8de8da862bc/image.png" alt=""></p>
<blockquote>
<p>노드의 data link layer에서 오류 체크와 필요한 정보에 이상이 없으면 해당하는 header와 trailer 제거 후 network layer에 올려줌</p>
</blockquote>
<p>9.
<img src="https://velog.velcdn.com/images/lefoa-98/post/1a7fe425-e33c-4381-97a6-1841b1b7d1f7/image.png" alt=""></p>
<blockquote>
<p>노드의 network layer에서 목적지 IP 주소를 확인한 후, 어떻게 가야 목적지에 도달할 수 있는지 체크하여 다시 header 정보 수정 후 data link layer에 내려줌.</p>
</blockquote>
<ol start="10">
<li><img src="https://velog.velcdn.com/images/lefoa-98/post/d3925d54-d865-4786-981e-b9fe1e349130/image.png" alt=""></li>
</ol>
<blockquote>
<p>노드의 data link layer에서 목적지를 위해서 어떤 노드로 가야하는지 체크하여 header와 trailer 붙여서 physical layer로 내려줌</p>
</blockquote>
<ol start="11">
<li><img src="https://velog.velcdn.com/images/lefoa-98/post/f12eb990-8395-4c9b-b945-e87768df4913/image.png" alt=""></li>
</ol>
<blockquote>
<p>physical layer에서 해당하는 노드 or 목적지로 bits로 전송하고, 수신한 곳에서 다시 bits를 패킷으로 포장하여 위의 과정 반복</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[객체지향] 객체지향의 사실과 오해]]></title>
            <link>https://velog.io/@lefoa-98/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9D%98-%EC%82%AC%EC%8B%A4%EA%B3%BC-%EC%98%A4%ED%95%B4</link>
            <guid>https://velog.io/@lefoa-98/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9D%98-%EC%82%AC%EC%8B%A4%EA%B3%BC-%EC%98%A4%ED%95%B4</guid>
            <pubDate>Tue, 01 Oct 2024 05:09:57 GMT</pubDate>
            <description><![CDATA[<hr>
<blockquote>
<p>본 글은 객체지향의 사실과 오해를 읽고난 후 저에게 필요한 내용이나 기억에 남는걸 정리한 문서입니다.</p>
</blockquote>
<hr>
<h2 id="0-객체지향-객체지향">0. 객체지향.. 객체지향...</h2>
<blockquote>
<p>객체지향이란 시스템이 맡아야할 큰 책임을 결국 자율적인 객체들이 각자 책임을 맡고, 자신이 처리할 수 없는 일은 링크를 통해 메세지를 보내서 다른 객체에게 일임하여 협력하는 구조를 뜻한다.</p>
</blockquote>
<hr>
<h2 id="1-객체의-프로퍼티란">1. 객체의 프로퍼티란?</h2>
<blockquote>
<p>객체의 상태를 구성하는 모든 특징을 통틀어 객체의 프로퍼티라고 한다.
프로퍼티는 변경되지 않고 &quot;정적&quot;이다.
다만, 프로퍼티의 값 자체는 변하기 때문에 &quot;동적&quot;이다.
프로퍼티의 구성은 단순한 값인 속성(식별자 포함) + 다른 객체를 가리키는 링크로 이루어진다.</p>
</blockquote>
<p>※ 링크: 객체가 다른 객체를 참조하는 것 =&gt; 해당 링크를 통해 메세지를 주고 받음
※ 식별자 기반으로 두 객체의 상태가 다르지만 동일한 객체로 판단하는 성질을 <em>&quot;동일성&quot;</em> 이라고 한다
※ 참조 객체, 엔티티 =&gt; 식별자를 지닌 객체 / 값 객체(ex, Integer)은 식별자를 가지지 않는 값 </p>
<hr>
<h2 id="2-명령과-쿼리">2. 명령과 쿼리</h2>
<blockquote>
<p>명령: 객체 기계에 상태를 변경하는 명령
쿼리: 객체 기계에 상태를 조회하는 쿼리</p>
</blockquote>
<p>※ 명령과 쿼리는 객체가 외부에 제공하는 행동, 해당 방법 이외에 다른 방법으로는 객체 이용할 수 없음 =&gt; 직접 객체의 내부 상태에 접근X =&gt; 메세지를 통해서만 접근 가능 =&gt; 수신 가능한 메세지가 모여 객체의 인터페이스 구성</p>
<hr>
<h2 id="3-객체의-행동의-궁극적-의미">3. 객체의 행동의 궁극적 의미</h2>
<blockquote>
<p>객체에서 중요한 것은 객체의 행동( 협력하는 객체 간의 책임 측면도 포함 )이며, 상태는 행동의 결과로 초래된 부수효과일 뿐...
결국, 객체의 타입을 결정하는 것은 객체의 행동 / 어떤 데이터를 객체가 가지고 있던 타입 결정에는 영향을 미치지 않는다.</p>
</blockquote>
<p>※ 이는 같은 행동을 할 수 있는 동일한 타입에 속한 객체는 내부의 데이터 표현 방식이 다르더라도 동일한 메세지를 수신하고 처리할 수 있음을 의미함 (다만 내부 표현 방식이 다르니 동일한 메세지를 처리하는 방식은 다를 수 있음) =&gt; <em>다형성</em></p>
<hr>
<h2 id="4-일반화와-특수화--6과-연계">4. 일반화와 특수화 =&gt; 6과 연계</h2>
<blockquote>
<p>두 타입 간에 일반화/특수화 관계가 성립하려면, 한 타입( 서브타입 )이 다른 타입보다 더 특수하게 행동해야 하고, 한 타입( 슈퍼타입 )은 다른 타입보다 더 일반적으로 행동해야한다.
단, 특수한 타입은 일반적인 타입이 할 수 있는 모든 행동을 동일하게 수행할 수 있어야 한다.
이는 곧, 어떤 타입을 다른 타입의 서브타입이라고 말하려면 다른 타입을 대체할 수 있어야 하는 것을 의미한다.</p>
</blockquote>
<h2 id="5-객체의-책임">5. 객체의 책임</h2>
<blockquote>
<p>객체의 책임은 크게 &#39;무엇을 알고 있는가&#39; + &#39;무엇을 할 수 있는가&#39; 로 이루어짐
무엇을 알고 있는가 : 관련 객체에 대해 아는 것 or 개인적인 정보에 대한 것 
무엇을 할 수 있는가 : 다른 객체의 활동 제어하고 조절 or 객체 생성 계산 등 스스로 하는 것 </p>
</blockquote>
<p>※ 결국 객체의 책임은 외부에서 접근 가능한 공용 인터페이스의 관점이다. 즉, 책임은 객체가 외부에 제공해 줄 수 있는 정보와 서비스의 목록이다. =&gt; 내가 처리할 수 있거나 아는 거면 내가 처리하고, 모르면 관련 객체에게 던져주고</p>
<h2 id="6-객체의-역할">6. 객체의 역할</h2>
<blockquote>
<p>동일한 역할을 수행할 수 있다는 것은 해당 객체들이 협력 내에서 동일한 책임의 집합을 수행할 수 있다는 것을 의미한다.
즉, 동일한 역할을 수행하는 객체들이 동일한 메세지를 수신할 수 있기 때문에 동일한 책임을 수행할 수 있다는 개념 =&gt; ( 메세지 == 책임 )</p>
</blockquote>
<p>※ 역할을 이용하면 협력을 추상화함으로써 여러 케이스에서 재사용 가능하다.
※ 객체가 역할을 대체 가능하기 위해서는 협력 안에서 역할이 수행하는 모든 책임을 동일하게 수행할 수 있어야 하며, 주어진 책임 이외에 +＠로 다른 책임도 수행할 수 있다. ( 일반화와 추상화의 개념 )</p>
<h2 id="7-자율적인-객체란">7. 자율적인 객체란?</h2>
<blockquote>
<p>해당 객체가 어떠한 방법( 내부 구현 )으로 책임을 수행할 수 만 있다면, 구체적인 방법이나 절차는 객체가 자유롭게 선택할 수 있어야한다.
=&gt;  포괄적이고 추상적인 책임을 선택한다고 능사는 아니다. 책임이 수행 방법을 제한할 정도로 너무 구체적인 것도 문제지만, 협력의 의도를 명확하게 표현하지 못할 정도로 추상적인 것 또한 문제</p>
</blockquote>
<p>※ 즉, 자율적인 책임의 특징은 &#39;어떻게 해야하는지&#39;에 대한 것이 아닌 &#39;무엇을 해야하는가&#39;에 대해 설명하는 것!</p>
<h2 id="8-다형성-책임-역할-일반화추상화구체화">8. 다형성, 책임, 역할, 일반화/추상화/구체화</h2>
<blockquote>
<p>서로 다른 객체들이 다형성을 만족시킨다는 것은 객체들이 동일한 책임을 공유한다.
즉, 다형성은 동일한 역할을 수행할 수 있는 객체들 사이의 대체 가능성
특정 메세지 =&gt; 특정 책임 =&gt; 특정 역할 =&gt; 해당 행동을 할 수 있는 모든 객체가 속할 수 있음 =&gt; 이때 각 객체들은 다형성( 일반화, 추상화 ) 관계에 있다.</p>
</blockquote>
<hr>
<h2 id="9-인터페이스">9. 인터페이스</h2>
<blockquote>
<p>인터페이스는 사용법만 알면 내부 구조나 동작 방식을 몰라도 대상 조작 가능
인터페이스 자체 변경하지 않고, 내부 구성이나 작동 방식 변경해도 인터페이스 사용자에게 영향X
대상이 변경되더라도 동일한 인터페이스를 제공하기만 하면 아무런 문제 없이 상호작용 가능</p>
</blockquote>
<p>※ 객체끼리 상호작용하는 유일한 방법은 링크를 통한 메세지 전송 =&gt; 인터페이스는 필연적으로 해당 객체가 수신할 수 있는 메세지의 목록
※ 인터페이스와 구현의 분리 원칙 : 구현을 변경할 때 외부 파급 효과 최소화를 위해 객체끼리 협력시 공용 인터페이스에만 의존하고, 구현 세부 사항에 의존하면 안된다.</p>
<hr>
<h2 id="10-기능-설계-vs-구조-설계">10. 기능 설계 vs 구조 설계</h2>
<blockquote>
<p>기능 측면의 설계 : 제품이 사용자를 위해 무엇을 할 수 있는가? =&gt; 유스케이스 모델링
구조 측면의 설계: 제품의 형태가 어떠해야 하는가? =&gt; 도메인 모델링
구조 base 그 위에 기능 얹기</p>
</blockquote>
<h3 id="10-1-도메인이란">10-1) 도메인이란?</h3>
<blockquote>
<p>사용자가 프로그램을 사용하는 대상 분야</p>
</blockquote>
<h3 id="10-2-도메인-모델이란">10-2) 도메인 모델이란?</h3>
<blockquote>
<p>사용자가 프로그램을 사용하는 대상 영역에 대한 지식을 구조화한 형태
ex) 은행의 &#39;정기예금&#39; 도메인 모델은 정기예금 신청 -&gt; 계좌 -&gt; 이자율 -&gt; 이자 구조를 따르며, 이는 비즈니스 로직이 바뀌지 않는 한 불변한다.
ex) &#39;커피 주문&#39; 도메인 모델은 메뉴판 확인 -&gt; 메뉴 주문 -&gt; 바리스타 -&gt; 커피 구조를 따르며, 이는 비즈니스 로직이 바뀌지 않는 한 불변한다.</p>
</blockquote>
<h3 id="10-3-유스케이스">10-3) 유스케이스</h3>
<blockquote>
<p>사용자와 시스템 간의 상호작용을 보여주는 &#39;텍스트&#39;이다.
중요한 점은, 유스케이스 안에 포함되어있는 상호작용의 흐름 =&gt; 하나의 시나리오가 아닌 사용자의 목표와 관련된 모든 시나리오의 집합
다만, 유스케이스는 기능적 요구사항을 사용자의 목표라는 문맥을 중심으로 묶기위한 정리 기법일 뿐</p>
</blockquote>
<p>※ 유스케이스의 일차액터: 시스템의 서비스 중 하나를 요청하는 이해관계자로, 시스템과 연동하는 외부 시스템 역시 일차 액터의 범주</p>
<hr>
<h2 id="11-재료들-합치기--도메인-모델-유스케이스-책임-">11. 재료들 합치기 ( 도메인 모델, 유스케이스, 책임 )</h2>
<blockquote>
<p>우리는 유스케이스에 정리된 시스템의 기능들을 도메인 모델을 기반으로 한 객체들의 책임으로 분배해야한다.</p>
</blockquote>
<p>예시: 중도 해지 이자액을 계산하는 기능</p>
<ol>
<li>도메인 모델 생성: 정기예금 -&gt; 계좌 -&gt; 이자율 -&gt; 이자</li>
<li>해당 기능을 도메인 모델에 메세지와 책임(역할) 기준으로 분배
if) 이자율이 고정 이자율이 아닌 기간에 따른 이자율과 같이 여러가지 경우? =&gt; 이자율을 인터페이스로 등록하고, 하부에 구현 객체 만들기 =&gt; 다형성, 일반화/구체화, 인터페이스에 의존, 역할</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 위상 정렬]]></title>
            <link>https://velog.io/@lefoa-98/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%9C%84%EC%83%81-%EC%A0%95%EB%A0%AC</link>
            <guid>https://velog.io/@lefoa-98/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%9C%84%EC%83%81-%EC%A0%95%EB%A0%AC</guid>
            <pubDate>Wed, 10 Apr 2024 08:00:25 GMT</pubDate>
            <description><![CDATA[<h2 id="위상-정렬이란">위상 정렬이란?</h2>
<blockquote>
</blockquote>
<p>깔끔하게 정리해주신 블로그를 첨부(보고 이해가 안 될 수가 없을듯..)
<a href="https://bcp0109.tistory.com/21">https://bcp0109.tistory.com/21</a></p>
<h2 id="실전-문제-풀이백준-1005">실전 문제 풀이(백준 1005)</h2>
<pre><code class="language-java">import java.util.*;

public class Main {    

    static int n, w;
    static ArrayList&lt;Integer&gt;[] list; //연결 간선 정보
    static int[] building; //빌딩 짓는 비용 정보
    static int[] indegree;
    static int[] buildCost; //각 위치까지 빌딩을 짓는 비용의 최대값을 저장한다.

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);

        int t = scan.nextInt();
        for(int i = 0; i &lt; t; i++) {
            n = scan.nextInt();
            int k = scan.nextInt();

            building = new int[n + 1];
            list = new ArrayList[n + 1];
            for(int j = 1; j &lt;= n; j++) {
                building[j] = scan.nextInt();
                list[j] = new ArrayList&lt;&gt;();
            }

            indegree = new int[n + 1];
            for(int j = 0; j &lt; k; j++) {
                int s = scan.nextInt();
                int e = scan.nextInt();
                list[s].add(e); 
                indegree[e]++;
            }
            w = scan.nextInt(); //건설해야 할 건물의 번호

            buildCost = new int[n + 1]; 
            topologySort();
            System.out.println(buildCost[w]);
        }
    }

    public static void topologySort() {
        Queue&lt;Integer&gt; q = new LinkedList&lt;&gt;();
        for(int i = 1; i &lt; indegree.length; i++) {
            if(indegree[i] == 0) {
                buildCost[i] = building[i];
                q.offer(i);
            }
        }

        while(!q.isEmpty()) {
            int current = q.poll();

            for(int i = 0; i &lt; list[current].size(); i++) {
                int next = list[current].get(i);
                buildCost[next] = Math.max(buildCost[current] + building[next], buildCost[next]);
                indegree[next]--;
                if(indegree[next] == 0) q.offer(next);
            }
        }
    }
}
</code></pre>
<p>얻어갈 점:</p>
<ul>
<li>dfs와 다르게 위상 정렬은 양 방향 연결이 아니기에 list[s].add(e) 까지만!
<img src="https://velog.velcdn.com/images/lefoa-98/post/f5792e92-0718-4e6c-afb2-e98191b575f6/image.png" alt=""></li>
<li>핵심 로직은 이 부분인데 원래 최대 값을 담는 것은 int sum=0; 이런 식의 변수를 이용하여 구현해왔으나, static int[] buildCost와 같이 구현하여 각 배열의 인덱스들이 정렬되면서 누적합의 형태로 Math.max를 사용하여 최대값 저장</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 백 트래킹 문제 풀이]]></title>
            <link>https://velog.io/@lefoa-98/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%B0%B1-%ED%8A%B8%EB%9E%98%ED%82%B9-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@lefoa-98/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%B0%B1-%ED%8A%B8%EB%9E%98%ED%82%B9-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4</guid>
            <pubDate>Wed, 10 Apr 2024 07:37:12 GMT</pubDate>
            <description><![CDATA[<h2 id="백준-2580">백준 2580</h2>
<pre><code class="language-java">import java.io.*;
import java.util.*;

public class Main {
    static int[][] board;
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        board = new int[9][9];

        for(int i = 0; i&lt;9; i++){
            StringTokenizer st = new StringTokenizer(br.readLine());

            for(int j = 0; j&lt;9; j++)
                board[i][j] = Integer.parseInt(st.nextToken());
        }

        sudoku(0, 0);
    }

    static void sudoku(int row, int col){
        if(col == 9){
            sudoku(row+1, 0);
            return;
        }

        if(row == 9){
            for(int i = 0; i&lt;9; i++){
                for(int j = 0; j&lt;9; j++)
                    System.out.print(board[i][j]+&quot; &quot;);
                System.out.println();
            }

            System.exit(0);
        }
        if(board[row][col] == 0){
            for(int i = 1; i&lt;=9; i++){
                if(check(row, col, i)){
                    board[row][col] = i;
                    sudoku(row, col + 1);
                }
            }
            board[row][col] = 0;
            return;
        }

        sudoku(row, col+1);
    }

    static boolean check(int row, int col, int val){
        //가로 방향 탐색
        for(int i = 0; i&lt;9; i++){
            if(board[row][i] == val)
                return false;
        }
        //세로 방향 탐색
        for(int i = 0; i&lt;9; i++){
            if(board[i][col] == val)
                return false;
        }
        //3x3 박스 안 탐색
        int rowStart = (row/3)*3;
        int colStart = (col/3)*3;

        for(int i = rowStart; i&lt;rowStart+3; i++){
            for(int j = colStart; j&lt;colStart+3; j++)
                if(board[i][j] == val)
                    return false;
        }

        return true;
    }
}</code></pre>
<p>얻어갈 점:</p>
<ul>
<li>이전에 풀었던 NxN queen 문제와 비슷한 느낌이 들어 백 트래킹 알고리즘 채용</li>
<li>스도쿠를 풀어가며(dfs이용) 잘못 입력될 시 돌아오는 로직이기 때문</li>
<li>그러나 그때 NxN 문제에서는 초기화하고 돌아가는 로직이 없는데 같은 백트래킹이지만 구현이 다른 건가? 의문이 들어 둘 비교를 해봤음</li>
</ul>
<hr>
<h2 id="vs-백준-9663">vs 백준 9663</h2>
<p><img src="https://velog.velcdn.com/images/lefoa-98/post/ab3598fd-d98e-45ff-92a2-2180d8022e3e/image.png" alt=""></p>
<ul>
<li>해당 문제 풀이 당시에는 for문을 돌리면서 arr[depth]에 값을 넣고 가능한 값이면 재귀호출 하는 구나~ 하며 풀이했음=&gt; 즉, 모든 값이 불가능한 경우를 제외하고 로직을 구성(이전으로 돌아가는 것이 아닌 해당 depth의 값만 가능한 i값으로 바꾸면서 앞으로 쭉쭉 나가는 구나 생각)=&gt; 그러나 잘 생각해보면 만약 for문을 다 돌았음에도 possible한 값이 없다면 for문 종료되고 이후 로직이 없으니 dfs 종료 =&gt; 호출이 종료되면 그 함수를 호출했던 부분 즉 dfs(depth+1)로 돌아감 =&gt; 남은 i값에 대한 for문 다시 호출해서 이전 값들이 수정되는 로직(우연히 맞은 문제였다는...)</li>
</ul>
<hr>
<h2 id="다시-백준-2580-얻어갈-점">다시 백준 2580 얻어갈 점?</h2>
<ul>
<li>스도쿠 문제의 경우 비어있는 경우 board[row][col] == 0 를 기준으로 탐색하기 때문에 9663문제와 다르게 값이 잘못되었다면 0으로 초기화해줄 필요가 있음(9663의 경우 잘못된 값을 초기화하지 않아도 arr[depth]==i를 넣어가며 값이 수정되어감)</li>
<li>따라서 board[row][col] = 0; return; 추가 =&gt; for문이 돌아가며 check를 통과하는 값이 없다면 해당 row,col==0으로 초기화해 다음에 또 다시 if 조건에 걸려 값을 넣어줄 것이고,return을 통해 재귀 호출했던 장소로 돌아감(만약 돌아간 곳도 또 값이 안되면 해당 row,col도 같은 로직 수행)</li>
<li>9<em>9 스도쿠에서 3</em>3으로 쪼개서 봐야하는 것을 구현하는 방식을 고민했는데 전체가 3칸으로 나뉘었으니 row,col 기준으로 0,1,2,3,4,5,6,7,8 =&gt; 0,1,2/3,4,5/6,7,8 =&gt; 이는 3으로 나누면 000/111/222 =&gt; 다시 3을 곱해주면 000/333/666으로 구현 가능</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] DFS 문제 풀이]]></title>
            <link>https://velog.io/@lefoa-98/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-DFS-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@lefoa-98/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-DFS-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4</guid>
            <pubDate>Wed, 10 Apr 2024 07:15:36 GMT</pubDate>
            <description><![CDATA[<h2 id="백준-1967">백준 1967</h2>
<pre><code class="language-java">import java.util.*;
import java.io.*;

class Main{
    static boolean[] visited;
    static ArrayList&lt;Node&gt;[] nodes;
    static int n;
    static int max = 0;
    static int maxIdx = 0;

    public static void main(String args[])throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        n = Integer.parseInt(br.readLine());
        String line;

        visited = new boolean[n+1];
        nodes = new ArrayList[n+1];

        for(int i=0 ; i&lt;=n ; i++){
            nodes[i] = new ArrayList&lt;&gt;();
        }

        while((line = br.readLine()) != null){
            StringTokenizer st = new StringTokenizer(line);
            int parent = Integer.parseInt(st.nextToken());
            int child = Integer.parseInt(st.nextToken());
            int weight = Integer.parseInt(st.nextToken());
            nodes[parent].add(new Node(child,weight));
            nodes[child].add(new Node(parent,weight));
        }

        visited[1]=true;
        dfs(1,0);

        visited = new boolean[n+1];
        visited[maxIdx] = true;
        dfs(maxIdx,0);
        System.out.println(max);
    }

    public static void dfs(int idx, int cnt){

        //최대 값과 , 그때의 노드 번호 갱신 로직
        if(max &lt; cnt){
            max = cnt;
            maxIdx = idx;
        }

        for(Node node : nodes[idx]){
            if(!visited[node.idx]){
                visited[node.idx] = true;
                dfs(node.idx , cnt+node.cnt);
            }
        }
    }

}

class Node{

    int idx, cnt;

    Node(int idx, int cnt){
        this.idx = idx;
        this.cnt = cnt;
    }
}</code></pre>
<p>얻어갈 점: </p>
<ul>
<li>이전의 DFS 구현할 때 인접 행렬을 이용했으나, 이번엔 인접 리스트를 통해 구현했음(성능 상의 이점)</li>
<li>DFS 사용 이유? 그래프를 다 뒤져서 가장 거리가 먼 두 수를 찾아서 더해주는 문제라서 사용했음</li>
<li>노드의 값은 인덱스와 가중치 두 개의 속성이 있으니 Class로 묶어줬음</li>
<li>더 이상 입력이 없을때까지 조건 =&gt; String line, (line.readLine()) != null로 표현</li>
<li>인접 행렬로 구현시에도 1,2에 값이 1이 세팅되면 2,1에도 해줘야하듯이 인접 리스트도 동일하게 양방향으로 매칭해줘야함</li>
<li>1차로 dfs를 1번 노드 기준으로 돌리면 가장 거리가 먼 노드를 찾음(이때의 max값은 가장 큰 노드의 번호와 그 때의 가중치)</li>
<li>max값을 기준으로 dfs를 한 번 더 돌리면 1차로 찾은 가장 거리가 먼 노드에서 또 제일 먼 노드 찾기 가능 =&gt; 최대값!</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 투 포인터 / 이진 탐색(binary search) ]]></title>
            <link>https://velog.io/@lefoa-98/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%88%AC-%ED%8F%AC%EC%9D%B8%ED%84%B0-%EC%9D%B4%EC%A7%84-%ED%83%90%EC%83%89binary-search</link>
            <guid>https://velog.io/@lefoa-98/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%88%AC-%ED%8F%AC%EC%9D%B8%ED%84%B0-%EC%9D%B4%EC%A7%84-%ED%83%90%EC%83%89binary-search</guid>
            <pubDate>Sun, 31 Mar 2024 16:37:25 GMT</pubDate>
            <description><![CDATA[<h2 id="백준-2467-투-포인터-풀이">백준 2467 투 포인터 풀이</h2>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {

    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());

        long[] arr = new long[n];
        StringTokenizer st = new StringTokenizer(br.readLine());
        for(int i=0; i&lt;n; i++) {
            arr[i] = Long.parseLong(st.nextToken());
        }

        // left는 0번 right는 n-1번 인덱스 시작
        int left =0;
        int right =n-1;

        // 0에 제일 근접했던 start, end 인덱스 기록용 변수
        int ml =0, mr = 0;
        long min = Long.MAX_VALUE;
        while(left&lt;right) {
            long sum = arr[left]+arr[right];
            if(min &gt; Math.abs(sum)) {
                min = Math.abs(sum);
                ml = left; mr = right;
            }
            if(sum&gt;=0) {
                right--;    
            }else {
                left++;
            }
        }
        System.out.println(arr[ml] +&quot; &quot;+arr[mr]);
    }
}</code></pre>
<p>얻어갈 점:</p>
<ul>
<li>투 포인터로 풀이할 수 있는 이유? 두 개의 용액이 -부터 +까지 정렬된 상태에서 두 개를 골라 0에 최대한 근접한 값을 찾는다(구간 합과 유사) =&gt; 양 쪽 끝에서 두 개를 고른 다음 0보다 크면 start를 하나 땡기고 0보다 작으면 end를 하나 땡기고</li>
<li>left &gt; right while 조건문 작성할 때 ==일 때를 고려했는데 2개를 골라야하니 같으면 안 되어서 빼줌</li>
<li>if(sum&gt;=0) =&gt; 해당 부분 sum==0과 sum&gt;0로 나눌까 합칠까 고민했는데 합침 =&gt; 반복 도중 1번째로 0이 된 결과값이 있다면 위의 최소값과 좌우 인덱스 저장시 반영되고 , 2번째 나오는 0부터는 어차피 기록이 안 됨=&gt; min&gt;Math.abs(sum)을 만족 시키지 못하기 때문 =&gt; 굳이 나눌 필요없이 right--로 통일해도 문제 없음</li>
</ul>
<hr>
<h2 id="백준-2467-이진-탐색-풀이">백준 2467 이진 탐색 풀이</h2>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

    static int n;
    static long[] arr;
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        n = Integer.parseInt(br.readLine());

        arr = new long[n];
        StringTokenizer st = new StringTokenizer(br.readLine());
        for(int i=0; i&lt;n; i++) {
            arr[i] = Long.parseLong(st.nextToken());
        }

        long min = Long.MAX_VALUE;
        int ml =0, mr = 0;


        // i가 0일때부터 순차적으로 증가하며 기준이 됨
        for(int i=0; i&lt;n-1; i++) {
            int left =i+1;
            int right =n-1;
            while(left&lt;=right) {

            //기준이 되는 i를 제외한 left 와 right의 중간 지점 찾기
                int mid = (left+right)/2;
                long sum = Math.abs(arr[i]+arr[mid]);

                //최소값 발견시 해당 좌우 인덱스와 최소값 기록
                if(min &gt; sum) {
                    min = sum;
                    ml = i; mr = mid;
                }
                if(arr[mid]&gt;= -arr[i]) {
                    right = mid-1;
                }else{
                    left = mid+1;
                }
            }
        }
        System.out.println(arr[ml]+&quot; &quot;+arr[mr]);
    }
}

</code></pre>
<p>얻어갈 점:</p>
<ul>
<li>이진 탐색 풀이 가능 이유? 이진 탐색 자체가 기본적으로 정렬된 배열로 시작해야 가능 + 전반적으로 입력되는 수들이 굉장히 큰 것에 비해 시간 제한이 1초이므로, 시간 복잡도가 낮은 탐색만 가능(logN)</li>
<li>while의 조건문인 left &lt;= right 조건 선정에서 ==을 어떻게 할 것인가 고민했는데, 둘이 같은 값이 나오더라도 해당 값을 2로 나눈 mid 설정에는 문제가 없기 때문에 &lt;=로 해줌</li>
<li>if(arr[mid] &gt;= -arr[i])는 우변을 좌변으로 이항하면 결국 기준값과 미드값의 합이 &gt;0인 경우와 ==0인 경우를 합친 것인데, &gt;0인 경우는 right = mid-1이 문제가 안 되지만 ==0인 경우는 어떻게 할까 고민했음 =&gt; 어차피 처음으로 0인 값이 도출되면 if(min&gt;sum) 조건은 더이상 실행되지 않음 =&gt; 지속적으로 탐색이어가도 결과에 영향을 미치지 않음 </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 정규 표현식]]></title>
            <link>https://velog.io/@lefoa-98/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%A0%95%EA%B7%9C-%ED%91%9C%ED%98%84%EC%8B%9D</link>
            <guid>https://velog.io/@lefoa-98/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%A0%95%EA%B7%9C-%ED%91%9C%ED%98%84%EC%8B%9D</guid>
            <pubDate>Sun, 31 Mar 2024 14:14:47 GMT</pubDate>
            <description><![CDATA[<h2 id="백준-2671">백준 2671</h2>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String input = br.readLine();
        if (input.matches(&quot;^(100+1+|01)+$&quot;)) {
            System.out.println(&quot;SUBMARINE&quot;);
        } else {
            System.out.println(&quot;NOISE&quot;);
        }
    }
}</code></pre>
<p>얻어갈 점:</p>
<ul>
<li>정규 표현식을 사용하는 문제를 처음 접해서 노가다해서 풀려고 했는데 실패했다.</li>
<li>특정 규칙을 가지는 문자열을 체크할 수 있는 정규표현식의 존재를 구글링해서 찾아서 잘 정리된 글을 아래에 첨부함</li>
<li>정규 표현식에서 () 와 []의 차이가 궁금했는데 ()는 예를들어 &quot;(abc)+&quot; =&gt; &quot;abc&quot; 문자열의 하나 이상 연속되게 나온다는 것, 즉 그룹화/ []는 예를들어 &quot;[abc]&quot; =&gt; &quot;a&quot; &quot;b&quot; &quot;c&quot; 중 하나의 문자와 매치된다는 것.</li>
</ul>
<p><a href="https://adjh54.tistory.com/104#2)%20%EC%A0%95%EA%B7%9C%EC%8B%9D%EC%9D%84%20%EC%9C%84%ED%95%9C%20%EC%A0%95%EA%B7%9C%20%EB%A9%94%EC%84%9C%EB%93%9C-1">https://adjh54.tistory.com/104#2)%20%EC%A0%95%EA%B7%9C%EC%8B%9D%EC%9D%84%20%EC%9C%84%ED%95%9C%20%EC%A0%95%EA%B7%9C%20%EB%A9%94%EC%84%9C%EB%93%9C-1</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 유니온 파인드]]></title>
            <link>https://velog.io/@lefoa-98/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%9C%A0%EB%8B%88%EC%98%A8-%ED%8C%8C%EC%9D%B8%EB%93%9C</link>
            <guid>https://velog.io/@lefoa-98/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%9C%A0%EB%8B%88%EC%98%A8-%ED%8C%8C%EC%9D%B8%EB%93%9C</guid>
            <pubDate>Sun, 31 Mar 2024 13:23:42 GMT</pubDate>
            <description><![CDATA[<h2 id="백준-1043">백준 1043</h2>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

public class Main {

    static int[] parents;
    static List&lt;Integer&gt; eList;
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());

        // n+1인 이유 =&gt; 인덱스와 번호를 맞추기 위함
        parents = new int[n+1];
        for(int i=1; i&lt;n+1; i++) {
            parents[i] = i;
        }
        st = new StringTokenizer(br.readLine());
        int en= Integer.parseInt(st.nextToken());

        // 진실을 아는 사람을 담은 리스트
        eList = new ArrayList&lt;&gt;();
        if(en==0) {
            System.out.println(m);
            return;
        }
        else{
            for(int i=0; i&lt;en; i++) {
                eList.add(Integer.parseInt(st.nextToken()));
            }
        }

        // 각 파티에 참가한 사람들을 담을 리스트 배열(외계인 문제에서 줄마다 stack을 할당하기 위해 스택 배열을 생성하는 원리와 동일)
        List&lt;Integer&gt;[] partyList = new ArrayList[m];
        for(int i=0; i&lt;m; i++) {
            partyList[i] = new ArrayList&lt;&gt;();
        }

        for(int i=0; i&lt;m; i++) {
            st = new StringTokenizer(br.readLine());
            int pn = Integer.parseInt(st.nextToken());

            int x = Integer.parseInt(st.nextToken());
            partyList[i].add(x);
            for(int j=1; j&lt;pn; j++) {
                int y = Integer.parseInt(st.nextToken());
                union(x,y);
                partyList[i].add(y);
            }
        }

        int cnt=0;
        for(int i=0; i&lt;m; i++) {
            boolean flag = true;
            for(int num : partyList[i]) {
                if(!check(num)) {
                    flag = false;
                    break;
                }
            }
            if(flag) {
                cnt++;
            }
        }
        System.out.println(cnt);

    }

    // 유니온 파인드 find 메서드
    static int find(int x) {
        if(parents[x] ==x) return x;
        return find(parents[x]);
    }

    // 유니온 파인드 union 메서드
    static void union(int x, int y) {
        x = find(x);
        y = find(y);
        if(x!=y) {
            parents[y]=x;
        }
    }

    // 파라미터로 넘어온 값의 부모를 찾고, 진실을 아는 사람을 하나씩 꺼내서 부모를 찾은 후 두 값이 같다면 거짓말 불가
    static boolean check(int x){
        x = find(x);
        for(int y : eList){
            if(x == find(y)) return false;
        }
        return true;
    }
}
</code></pre>
<p>얻어갈 점:</p>
<ul>
<li>유니온 파인드를 쓸 수 있는 point =&gt; 진실을 알고 있는 사람과 한 번이라도 같은 파티라면 거짓말 불가능 =&gt; 그룹핑을 통해 한 번이라도 같은 파티였는지, 아닌지 묶는 알고리즘? =&gt; 유니온 파인드</li>
<li>유니온 파인드를 묶기 위해서는 union(x,y)와 같이 파라미터 2개 넘겨야 하기 때문에 이중 for문에서 외부 for문에서 하나를 미리 입력 받고 내부 for문에서 입력 받아 넘기는 형식 이용 / (1,2) (1,3) (1,4) --</li>
<li>유니온 파인드 알고리즘에서 find 메서드가 필수인 이유? parents 배열의 값만을 보고 누가 연결되어있는지 확실치 않기 때문에 find 메서드에서 재귀 호출을 해 parent 노드를 검색해야함</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 백 트래킹 문제]]></title>
            <link>https://velog.io/@lefoa-98/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%B0%B1-%ED%8A%B8%EB%9E%98%ED%82%B9-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@lefoa-98/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%B0%B1-%ED%8A%B8%EB%9E%98%ED%82%B9-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Sun, 31 Mar 2024 12:52:59 GMT</pubDate>
            <description><![CDATA[<h2 id="백준-9663">백준 9663</h2>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

    static int[] arr;
    static int N;
    static int de = 0;

    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str = br.readLine();

        N = Integer.parseInt(str);

        arr = new int[N];

        dfs(0);
        System.out.println(de);
}
    public static void dfs(int depth) {
        // dfs 재귀 함수로 계속 호출되면서 depth가 올라갈 때 depth==N면 정답인 경우의 수
        if(depth == N) {
            de++;
            return;
        }

        // 반복문 돌리면서, dfs 재귀 호출(possible에 부합할 시)
        for(int i = 0 ; i &lt; N; i++) {
            arr[depth] = i;
            if(possible(depth)) {
                dfs(depth+1);
            }
        }    
    }

    // 이전의 퀸들과 대각선, 같은 행에 없음을 check
    public static boolean possible(int col) {

        for(int i = 0 ; i &lt; col ; i++) {

        if(arr[i]==arr[col]) {
            return false;
        }

        else if(Math.abs(col-i) == Math.abs(arr[col]-arr[i])) {
            return false;
        }

        }

        return true;
    }
}</code></pre>
<p>얻어갈 점:</p>
<ul>
<li>dfs vs 백 트래킹 =&gt; dfs: 완전 탐색을 기본으로 그래프를 모두 순회하기 때문에 불필요한 경로를 사전에 차단 불가 / 백 트래킹: 경로를 찾아가는 도중에 해가 되지 않을 것 같은 경로라면 되돌아옴 </li>
<li>백 트래킹으로 풀어야겠다 catch point =&gt; N x N &quot;그래프&quot;에 퀸을 &quot;모두&quot; 놓는 경우의 수를 탐색하는 것 + 퀸을 놓지 못하겠다 싶으면 탐색 중지</li>
<li>2차원 배열을 사용하지 않고 , 1차원 배열을 사용할 수 있는 이유? =&gt; 1개의 열에 퀸을 놓으면 해당 열에는 더 이상 놓을 수 없음 =&gt; arr[4]={0,1,2,3}이라면 1번째 열, 1번째 행에 퀸 / 2번째 열, 2번째 행에 퀸 ---- / 즉, 인덱스를 열로 삼고 값을 행으로 만들면 됨</li>
<li>대각선 조건 =&gt; 행 - 행 = 열 - 열 check</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 투 포인터]]></title>
            <link>https://velog.io/@lefoa-98/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%88%AC-%ED%8F%AC%EC%9D%B8%ED%84%B0</link>
            <guid>https://velog.io/@lefoa-98/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%88%AC-%ED%8F%AC%EC%9D%B8%ED%84%B0</guid>
            <pubDate>Sun, 31 Mar 2024 12:10:21 GMT</pubDate>
            <description><![CDATA[<h2 id="백준-1806">백준 1806</h2>
<pre><code class="language-java">import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        int n = Integer.parseInt(st.nextToken());
        int s = Integer.parseInt(st.nextToken());

        int[] arr = new int[n+1];
        st = new StringTokenizer(br.readLine());
        for(int i=0; i&lt;n; i++) {
            arr[i] = Integer.parseInt(st.nextToken());
        }

        int start = 0;
        int end = 0;
        int len = Integer.MAX_VALUE;
        int sum = 0;
        while(start &lt;= end &amp;&amp; end &lt;= n) {
            if(sum &lt; s) {
                sum += arr[end++];
            } else if(sum &gt;= s) {
                len = Math.min(len, end-start);
                sum -= arr[start++];
            }  
        }
        System.out.println(len==Integer.MAX_VALUE ? 0 : len);
    }
}</code></pre>
<p>얻어갈 점</p>
<ul>
<li>투 포인터를 이용한 문제 풀이 =&gt; why? 특정 구간의 합을 구하는 것! =&gt; start, end 인덱스를 사용하여 길이를 조절하여 풀면 되겠다!</li>
<li>int[] arr = new int[n+1] =&gt; 처음엔 배열의 개수를 n개로 지정했다 그러나 아래의 핵심 로직 쪽에서 이 조건을 사용하여 결정하면 end&lt;n이 된다. 즉 n-1에서 n이 되면 종료하면 된다고 생각했음. </li>
<li>그러나 해당 방식은 end가 끝까지 간 후에 , start인덱스를 하나씩 밀면서 생길 수 있는 답을 빼먹게 된다. =&gt; 따라서 n+1로 하나 더 설정하고 end가 끝나더라도 start가 따라올 수 있게 조정</li>
<li>while 조건문은 start가 end를 따라가는 방식이니 start&lt;=end로 구성하고, end&lt;=n으로 작성(사실 여기서 start=end를 어떻게 처리할까 고민했는데 같은 것을 가리킬때 해당 인덱스의 값이 원하는 값이 나오면 그것도 정답이니 &lt;=로 처리)</li>
<li>sum&gt;0 , sum==0 로 분리할까 sum&gt;=0으로 할까 고민했었는데, 문제에서 원하는 값이 나오더라도 최소 길이를 구하는 것이니 &gt;=로 묶어주고 지속적으로 len을 비교하는 로직으로 구성</li>
<li>Math.min()함수는 파라미터로 온 값들 중 최소를 뱉어줌 </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] Stack 문제 풀이]]></title>
            <link>https://velog.io/@lefoa-98/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-Stack-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@lefoa-98/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-Stack-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4</guid>
            <pubDate>Wed, 27 Mar 2024 07:51:11 GMT</pubDate>
            <description><![CDATA[<h2 id="백준-2493">백준 2493</h2>
<pre><code class="language-java">import java.io.*;
import java.util.*;

class Top { // 탑에 대한 정보
    int num; // 탑의 번호
    int height; // 탑의 높이

    Top(int num, int height) {
        this.num = num;
        this.height = height;
    }
}

public class Main {

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st;
        int N = Integer.parseInt(br.readLine());

        Stack&lt;Top&gt; stack = new Stack&lt;&gt;();
        StringBuilder answer = new StringBuilder();
        st = new StringTokenizer(br.readLine());
        for (int i = 1; i &lt;= N; i++) {
            int height = Integer.parseInt(st.nextToken());

                while (true) { 
                    if (stack.isEmpty()) { // 스택이 비어있다면, 0을 출력하고 탑을 push한다.
                        answer.append(&quot;0 &quot;);
                        stack.push(new Top(i, height));
                        break;
                    }

                    Top top = stack.peek();

                    if (top.height &gt; height) { // peek한 탑의 높이가 현재 탑의 높이보다 높다면,
                        answer.append(top.num + &quot; &quot;); // peek한 탑의 번호를 출력하고,
                        stack.push(new Top(i, height)); // 현재 탑을 스택에 push한다.
                        break;
                    } else { // peek한 탑의 높이가 현재 탑의 높이보다 낮다면,
                        stack.pop(); // 스택에서 pop하고 다시 반복문을 돌린다.
                    }
                }

        }

        bw.write(answer.toString() + &quot;\n&quot;);
        bw.flush();
        bw.close();
        br.close();
    }

}</code></pre>
<p>얻어갈 점:</p>
<ul>
<li>처음에는 배열에 탑들의 정보를 미리 넣고, 맨 끝 인덱스부터 앞과 비교하여 신호를 받을 수 있는 탑을 찾으려 했으나 그렇게 하면 시간 초과가 날 수 밖에 없음(시간복잡도에 의해)</li>
<li>Stack을 사용할 수 있었던 이유? 입력된 변수가 가장 가까운 탑과 비교를 통해 꺼낼지, 말지를 결정하기 때문에 사용했음</li>
<li>탑의 인덱스와 높이를 어떻게 저장할지 고민 했었는데, 자바 언어의 특성인 객체를 사용하여 깔끔하게 정리할 수 있음.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>