<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://jynoh00.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://jynoh00.github.io/" rel="alternate" type="text/html" /><updated>2025-11-04T15:07:02+09:00</updated><id>https://jynoh00.github.io/feed.xml</id><title type="html">ad astra</title><subtitle>Per aspera</subtitle><entry><title type="html">[WoowaCourse] 우아한테크코스 - 프리코스 3주차 리뷰</title><link href="https://jynoh00.github.io/woowacourse/2025/11/04/woowa3.html" rel="alternate" type="text/html" title="[WoowaCourse] 우아한테크코스 - 프리코스 3주차 리뷰" /><published>2025-11-04T14:32:00+09:00</published><updated>2025-11-04T14:32:00+09:00</updated><id>https://jynoh00.github.io/woowacourse/2025/11/04/woowa3</id><content type="html" xml:base="https://jynoh00.github.io/woowacourse/2025/11/04/woowa3.html"><![CDATA[<h3 id="우아한테크코스---프리코스-3주차--로또">우아한테크코스 - 프리코스 3주차 : 로또</h3>
<blockquote>
  <p>미션은 <a href="https://github.com/woowacourse-precourse/java-lotto-8">로또</a>를 포크하고 클론하는 것으로 시작한다.<br />
기능을 구현하기 전 README.md에 구현할 기능 목록을 정리해 추가한다.<br />
Git의 커밋 단위는 앞 단계에서 README.md에 정리한 기능 목록 단위로 추가한다.<br />
<a href="https://gist.github.com/stephenparish/9941e89d80e2bc58a153">AngularJS Git Commit Message Conventions</a>을 참고해 커밋 메시지를 작성한다.<br />
자세한 과제 진행 방법은 프리코스 진행 가이드 문서를 참고한다.</p>
</blockquote>

<hr />

<h3 id="기능-요구-사항">기능 요구 사항</h3>
<p>간단한 로또 발매기를 구현한다.<br />
<br /></p>
<ul>
  <li>로또 번호의 숫자 범위는 1~45까지이다.</li>
  <li>1개의 로또를 발행할 때 중복되지 않는 6개의 숫자를 뽑는다.</li>
  <li>당첨 번호 추첨 시 중복되지 않는 숫자 6개와 보너스 번호 1개를 뽑는다.</li>
  <li>당첨은 1등부터 5등까지 있다. 당첨 기준과 금액은 아래와 같다.
    <ul>
      <li>1등: 6개 번호 일치 / 2,000,000,000원</li>
      <li>2등: 5개 번호 + 보너스 번호 일치 / 30,000,000원</li>
      <li>3등: 5개 번호 일치 / 1,500,000원</li>
      <li>4등: 4개 번호 일치 / 50,000원</li>
      <li>5등: 3개 번호 일치 / 5,000원</li>
    </ul>
  </li>
  <li>로또 구입 금액을 입력하면 구입 금액에 해당하는 만큼 로또를 발행해야 한다.</li>
  <li>로또 1장의 가격은 1,000원이다.</li>
  <li>당첨 번호와 보너스 번호를 입력받는다.</li>
  <li>사용자가 구매한 로또 번호와 당첨 번호를 비교하여 당첨 내역 및 수익률을 출력하고 로또 게임을 종료한다.</li>
  <li>사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시키고, “[ERROR]”로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다.
    <ul>
      <li>Exception이 아닌 IllegalArgumentException, IllegalStateException 등과 같은 명확한 유형을 처리한다.</li>
    </ul>
  </li>
</ul>

<p><br /></p>

<h4 id="입출력-요구-사항">입출력 요구 사항</h4>
<h5 id="입력">입력</h5>
<ul>
  <li>로또 구입 금액을 입력 받는다.</li>
  <li>구입 금액은 1,000원 단위로 입력 받으며 1,000원으로 나누어 떨어지지 않는 경우 예외 처리한다.
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>14000
</code></pre></div>    </div>
  </li>
  <li>당첨 번호를 입력 받는다.</li>
  <li>번호는 쉼표,를 기준으로 구분한다.
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1,2,3,4,5,6
</code></pre></div>    </div>
  </li>
  <li>보너스 번호를 입력 받는다.
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>7
</code></pre></div>    </div>
    <h5 id="출력">출력</h5>
  </li>
  <li>발행한 로또 수량 및 번호를 출력한다.</li>
  <li>로또 번호는 오름차순으로 정렬하여 보여준다.
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>8개를 구매했습니다.
[8, 21, 23, 41, 42, 43] 
[3, 5, 11, 16, 32, 38] 
[7, 11, 16, 35, 36, 44] 
[1, 8, 11, 31, 41, 42] 
[13, 14, 16, 38, 42, 45] 
[7, 11, 30, 40, 42, 43] 
[2, 13, 22, 32, 38, 45] 
[1, 3, 5, 14, 22, 45]
</code></pre></div>    </div>
  </li>
  <li>당첨 내역을 출력한다.
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>3개 일치 (5,000원) - 1개
4개 일치 (50,000원) - 0개
5개 일치 (1,500,000원) - 0개
5개 일치, 보너스 볼 일치 (30,000,000원) - 0개
6개 일치 (2,000,000,000원) - 0개
</code></pre></div>    </div>
  </li>
  <li>수익률은 소수점 둘째 자리에서 반올림한다. (ex. 100.0%, 51.5%, 1,000,000.0%)
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>총 수익률은 62.5%입니다.
</code></pre></div>    </div>
  </li>
  <li>예외 상황 시 에러 문구를 출력해야 한다.</li>
  <li>단, 에러 문구는 “[ERROR]”로 시작해야 한다.
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다.
</code></pre></div>    </div>
  </li>
</ul>

<h5 id="실행-결과-예시">실행 결과 예시</h5>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>구입금액을 입력해 주세요.
8000

8개를 구매했습니다.
[8, 21, 23, 41, 42, 43] 
[3, 5, 11, 16, 32, 38] 
[7, 11, 16, 35, 36, 44] 
[1, 8, 11, 31, 41, 42] 
[13, 14, 16, 38, 42, 45] 
[7, 11, 30, 40, 42, 43] 
[2, 13, 22, 32, 38, 45] 
[1, 3, 5, 14, 22, 45]

당첨 번호를 입력해 주세요.
1,2,3,4,5,6

보너스 번호를 입력해 주세요.
7

당첨 통계
---
3개 일치 (5,000원) - 1개
4개 일치 (50,000원) - 0개
5개 일치 (1,500,000원) - 0개
5개 일치, 보너스 볼 일치 (30,000,000원) - 0개
6개 일치 (2,000,000,000원) - 0개
총 수익률은 62.5%입니다.
</code></pre></div></div>

<hr />

<h3 id="프로그래밍-요구-사항-1">프로그래밍 요구 사항 1</h3>
<ul>
  <li>JDK 21 버전에서 실행 가능해야 한다.</li>
  <li>프로그램 실행의 시작점은 Application의 main()이다.</li>
  <li>build.gradle 파일은 변경할 수 없으며, 제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다.</li>
  <li>프로그램 종료 시 System.exit()를 호출하지 않는다.</li>
  <li>프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다.</li>
  <li>자바 코드 컨벤션을 지키면서 프로그래밍한다.</li>
  <li>기본적으로 <a href="https://github.com/woowacourse/woowacourse-docs/tree/main/styleguide/java">Java Style Guide</a>를 원칙으로 한다.</li>
</ul>

<h3 id="프로그래밍-요구-사항-2">프로그래밍 요구 사항 2</h3>

<ul>
  <li>indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다.</li>
  <li>예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.</li>
  <li>힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다.</li>
  <li>3항 연산자를 쓰지 않는다.</li>
  <li>함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라.</li>
  <li>JUnit 5와 AssertJ를 이용하여 정리한 기능 목록이 정상적으로 작동하는지 테스트 코드로 확인한다.</li>
  <li>테스트 도구 사용법이 익숙하지 않다면 아래 문서를 참고하여 학습한 후 테스트를 구현한다.
    <ul>
      <li>JUnit 5 User Guide</li>
      <li>AssertJ User Guide</li>
      <li>AssertJ Exception Assertions</li>
      <li>Guide to JUnit 5 Parameterized Tests</li>
    </ul>
  </li>
</ul>

<h3 id="프로그래밍-요구-사항-3">**프로그래밍 요구 사항 3</h3>

<ul>
  <li>함수(또는 메서드)의 길이가 15라인을 넘어가지 않도록 구현한다.
    <ul>
      <li>함수(또는 메서드)가 한 가지 일만 잘 하도록 구현한다.</li>
    </ul>
  </li>
  <li>else 예약어를 쓰지 않는다.
    <ul>
      <li>else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다.</li>
      <li>힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다.</li>
    </ul>
  </li>
  <li>Java Enum을 적용하여 프로그램을 구현한다.</li>
  <li>구현한 기능에 대한 단위 테스트를 작성한다. 단, UI(System.out, System.in, Scanner) 로직은 제외한다.
    <ul>
      <li>단위 테스트 작성이 익숙하지 않다면 <code class="language-plaintext highlighter-rouge">LottoTest</code>를 참고하여 학습한 후 테스트를 작성한다.</li>
    </ul>
  </li>
</ul>

<h4 id="라이브러리">라이브러리</h4>
<ul>
  <li>camp.nextstep.edu.missionutils에서 제공하는 Randoms 및 Console API를 사용하여 구현해야 한다.</li>
  <li>Random 값 추출은 camp.nextstep.edu.missionutils.Randoms의 pickNumberInRange()를 활용한다.</li>
  <li>사용자가 입력하는 값은 camp.nextstep.edu.missionutils.Console의 readLine()을 활용한다.</li>
</ul>

<hr />

<h3 id="구현-세부사항">구현 세부사항</h3>

<p>구매 금액에 따른 로또 자동 구매 및 결과 실험 애플리케이션<br /></p>

<p>사용자로부터 구매할 로또의 금액과 당첨 번호, 보너스 번호를 입력받아 구매 금액 대비 누적 상금 금액을 최종적으로 출력한다.<br /></p>

<p><strong>주요 기능:</strong></p>
<ol>
  <li>사용자 입력 받기</li>
</ol>

<ul>
  <li>로또 구입 금액 입력 (1,000원 단위이며 int 범위 내 양의 정수)</li>
  <li>당첨 번호 입력 (쉼표,를 기준으로 6개의 번호, 1~45 내 중복이 없는 - 정수, 숫자 내부가 아닌 공백 문자 )</li>
  <li>보너스 번호 입력 (숫자 내부가 아닌 공백 문자 , 1~45 내 당첨 번호와 중복이 없는 정수)</li>
</ul>

<ol>
  <li>출력 기능</li>
</ol>

<ul>
  <li>구매한 로또의 수량 출력 (구입 금액 / 개당 로또 가격)</li>
  <li>자동 생성된 개별 로또의 전체 번호 출력</li>
  <li>자동 생성된 전체 로또의 당첨 통계 출력</li>
  <li>총 수익률 출력</li>
</ul>

<ol>
  <li>입력값 형식 검증</li>
</ol>

<ul>
  <li>로또 구입 금액 입력값이 올바른 형식인지 확인한다.</li>
  <li>당첨 번호 입력값이 올바른 형식인지 확인한다.</li>
  <li>보너스 번호 입력값이 올바른 형식인지 확인한다.</li>
</ul>

<ol>
  <li>로또 번호 자동 생성 기능</li>
</ol>

<ul>
  <li>로또 구입 금액 입력값에 맞는 개수로 각 6개의 숫자를 가지는 로또 인스턴스를 생성한다.</li>
</ul>

<ol>
  <li>로또 별 당첨 확인 기능</li>
</ol>

<ul>
  <li>각 로또의 숫자를 확인한다.</li>
  <li>숫자의 일치 개수에 따라 당첨된 금액, 당첨된 로또의 개수를 수정한다.</li>
</ul>

<ol>
  <li>총 수익률 계산 기능</li>
</ol>

<ul>
  <li>총 당첨된 금액과 로또 구입 금액을 확인한다.</li>
  <li>수익률로또 구입 금액 / 총 당첨된 금액을 계산한다.</li>
</ul>

<h4 id="명시되지-않은-요구사항에-대한-구현">명시되지 않은 요구사항에 대한 구현</h4>

<p>입력</p>
<ul>
  <li>로또 구입 금액의 범위는 int 범위(-2,147,483,648 ~ 2,147,483,647)를 넘지 않는다.</li>
  <li>로또 구입 금액의 입력값이 숫자형 포맷이 아닐 경우 예외 처리한다.</li>
  <li>로또 구입 금액의 앞뒤 공백 문자 는 제거한다.</li>
  <li>당첨 번호 입력 시 쉼표,를 기준으로 나누어진 개별 당첨 번호의 앞뒤에 있는 공백 문자 는 제거한다.</li>
  <li>쉼표,와 숫자, 숫자의 앞뒤에 존재하는 공백 문자 를 제외한 문자가 당첨 번호 입력값으로 주어질 경우 예외 처리한다.</li>
  <li>보너스 번호 입력 시 숫자의 앞뒤에 존재하는 공백 문자 는 제거한다.</li>
  <li>보너스 번호 입력 시 숫자 사이에 존재하는 공백 문자 와 숫자형 포맷이 아닌 문자를 입력값으로 할 경우 예외 처리한다.</li>
</ul>

<p>기타</p>
<ul>
  <li>수익률은 총 당첨 금액 / 구입 금액으로 정의한다.</li>
</ul>

<h4 id="예외-상황">예외 상황</h4>

<p>다음의 경우 <code class="language-plaintext highlighter-rouge">IllegalArgumentException</code> 발생:</p>

<h4 id="-구매-금액-예외-상황">  구매 금액 예외 상황</h4>

<ul>
  <li>숫자가 아닌 문자 입력: abc, 1a00</li>
  <li>1000원 단위가 아닌 금액: 1500, 2300</li>
  <li>음수 입력: -1000</li>
  <li>0원 입력: 0</li>
  <li>숫자 사이에 공백 존재: 10 00</li>
  <li>당첨 번호 예외 상황</li>
  <li>6개가 아닌 번호 개수: 1,2,3,4,5, 1,2,3,4,5,6,7</li>
  <li>중복된 번호: 1,2,3,4,5,5</li>
  <li>범위를 벗어난 번호: 0,1,2,3,4,5, 1,2,3,4,5,46</li>
  <li>잘못된 형식: 1 2 3 4 5 6, 1-2-3-4-5-6</li>
  <li>개별 숫자 사이 공백 존재: 1,2 0,3,4,5,6</li>
</ul>

<h4 id="-보너스-번호-예외-상황">  보너스 번호 예외 상황</h4>
<ul>
  <li>당첨 번호와 중복: 당첨 번호가 1,2,3,4,5,6일 때 보너스 번호 6
범위를 벗어난 번호: 0, 46</li>
  <li>숫자가 아닌 입력: abc</li>
  <li>null값 및 단일 공백</li>
</ul>

<hr />

<h3 id="코드-구조">코드 구조</h3>

<h4 id="패키지-구조">패키지 구조</h4>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>src
    ├── main
    │   └── java
    │       └── lotto
    │           ├── Application.java
    │           ├── common
    │           │   ├── ErrorMessage.java
    │           │   ├── InputMessage.java
    │           │   ├── LottoConstants.java
    │           │   └── OutputMessage.java
    │           ├── controller
    │           │   └── LottoController.java
    │           ├── model
    │           │   ├── Lotto.java
    │           │   ├── LottoSimulator.java
    │           │   ├── Rank.java
    │           │   └── Statistics.java
    │           ├── service
    │           │   ├── InputService.java
    │           │   └── LottoService.java
    │           ├── util
    │           │   ├── StringToIntegerConverter.java
    │           │   └── WinningNumbersParser.java
    │           ├── validator
    │           │   ├── InputValidator.java
    │           │   └── LottoValidator.java
    │           └── view
    │               ├── InputView.java
    │               └── OutputView.java
    └── test
        └── java
            └── lotto
                ├── ApplicationTest.java
                ├── controller
                │   └── LottoControllerTest.java
                ├── model
                │   ├── LottoSimulatorTest.java
                │   ├── LottoTest.java
                │   ├── RankTest.java
                │   └── StatisticsTest.java
                ├── service
                │   ├── InputServiceTest.java
                │   └── LottoServiceTest.java
                ├── util
                │   ├── StringToIntegerConverterTest.java
                │   └── WinningNumbersParserTest.java
                └── validator
                    ├── InputValidatorTest.java
                    └── LottoValidatorTest.java
</code></pre></div></div>

<h4 id="계층별-역할">계층별 역할</h4>

<h4 id="1-controller">1. Controller</h4>
<ul>
  <li>LottoController: 전체 프로그램 흐름 제어 및 예외 처리</li>
</ul>

<h4 id="2-model">2. Model</h4>
<ul>
  <li>Lotto: 로또 번호 6개를 관리하는 도메인 객체</li>
  <li>LottoSimulator: 구매한 로또들과 당첨 정보를 관리</li>
  <li>Rank: 당첨 등수별 일치 개수, 보너스 일치 여부, 당첨 금액 관리</li>
  <li>Statistics: 당첨 통계 및 수익률 계산</li>
</ul>

<h4 id="3-service">3. Service</h4>
<ul>
  <li>InputService: 사용자 입력값 검증 및 변환</li>
  <li>LottoService: 로또 생성 및 당첨 통계 계산 로직</li>
</ul>

<h4 id="4-view">4. View</h4>
<ul>
  <li>InputView: 사용자 입력</li>
  <li>OutputView: 결과 출력</li>
</ul>

<h4 id="5-validator">5. Validator</h4>
<ul>
  <li>InputValidator: 입력값 유효성 검증</li>
  <li>LottoValidator: 로또 번호 유효성 검증</li>
</ul>

<h4 id="6-util">6. Util</h4>
<ul>
  <li>StringToIntegerConverter: 문자열을 정수로 변환</li>
  <li>WinningNumbersParser: 당첨 번호 문자열 파싱</li>
</ul>

<h4 id="7-common">7. Common</h4>
<ul>
  <li>ErrorMessage: 에러 메시지 상수</li>
  <li>InputMessage: 입력 안내 메시지 상수</li>
  <li>OutputMessage: 출력 메시지 상수</li>
  <li>LottoConstants: 로또 관련 상수 (가격, 번호 범위 등)</li>
</ul>

<h4 id="주요-설계-결정">주요 설계 결정</h4>

<ul>
  <li>책임 분리
    <ul>
      <li>Controller는 흐름 제어만 담당</li>
      <li>View는 입출력만 담당</li>
      <li>Validator는 검증 로직만 담당</li>
    </ul>
  </li>
  <li>예외 처리 전략
    <ul>
      <li>입력 단계 직후 모두 유효성 검사</li>
      <li>IllegalArgumentException으로 통일된 예외 처리</li>
      <li>[ERROR] 형식을 통한 테스트 코드에서의 의도한 예외 처리 확인</li>
      <li>Controller에서 예외를 catch하여 에러 메시지 출력</li>
    </ul>
  </li>
  <li>상수 관리
    <ul>
      <li>매직 넘버 제거를 위해 Common 패키지에 상수 클래스 분리</li>
      <li>메시지 변경 시 한 곳만 수정하면 되도록 구성</li>
    </ul>
  </li>
  <li>테스트 가능성
    <ul>
      <li>각 계층별로 단위 테스트 작성</li>
      <li>NsTest를 활용한 통합 테스트 구현</li>
    </ul>
  </li>
</ul>

<hr />

<h3 id="소스-코드">소스 코드</h3>
<p><a href="https://github.com/jynoh00/java-lotto-8/tree/jynoh00">https://github.com/jynoh00/java-lotto-8/tree/jynoh00</a>
<br /></p>

<ul>
  <li>프로그램 진입점: src/main/java/lotto/Application.java</li>
  <li>테스트 코드: src/test/java/lotto/…</li>
</ul>

<hr />

<h3 id="회고">회고</h3>

<p>3주차에선 기존 코드 작성 방식에서 벗어나 새로운 방법으로 프로젝트를 진행해 보았으며, 프로그래밍 요구 사항 3의 Java Enum 활용 및 단위 테스트에 집중하여 코드를 작성하고자 노력하였다. 이에 대해 회고하여보겠다.<br />
<br />
먼저 코드 작성 방식에 대한 내용이다. 2주차 프로젝트에선 문제를 파악한 후 알고리즘을 생각하여 하나의 파일에 절차적으로 코드를 작성한 이후 리팩토링 과정에서 기능 별로 나누어 패키지 구조를 세부화 하였다. 이번 주차 프로젝트에선 문제를 파악한 직후 알고리즘을 생각할 때 MVC 패턴에 기반하여 구상한 패키지 기준으로 각자의 역할을 나누어 객체당 최대한 간단한 역할만 수행하게 구조화 한 후 코드를 작성하였다. 먼저 패키지 구조를 나누어 각 객체의 코드를 작성하는 것은 처음해보는 방식이어서 그런지 초반 개발 속도는 기존 방식보다 현저히 느렸지만, 전체 틀을 작성한 후 프로그램을 실행해보며 생각하지 못했던 오류들을 발견하고 고칠 때 각 역할을 맡은 객체의 코드만 수정하였더니 결과적으로 기존 방식보다 빠른 속도로 전체 코드를 작성할 수 있었다. 2주차에서 입력값과 출력값을 처리하는 뷰(view) 패키지에서 검증을 포함하여 코드가 다소 복잡하고 해당 검증 코드를 다른 객체에서 재사용하기에 무리가 있었는데, 검증(validator) 패키지를 따로 나누어 당첨 번호에 관련된 로또 값과 구매한 로또 번호들에 해당하는 값에 동일한 로직의 검증 코드를 재사용하여 효율적인 측면을 보였던 것 같다.<br />
<br />
Enum 열거형 타입은 앞선 2주차에서도 예외 처리한 에러 메시지 상수들을 관리하는데 사용하였다. 이번 로또 과제에서도 역시 열거형 타입을 사용하여 에러 메시지의 출력값들을 상수화하여 관리했으며, 입출력에서의 메시지들 또한 열거형으로 묶어 common 패키지에  포함시켰다. 추가로 전체 코드에서 사용하는 숫자형 상수값들 또한 LottoConstants로 묶어 사용하여보았는데 개별 로또의 가격이나 로또 숫자의 개수 등 값이 바뀌어도 하나의 파일의 코드만 바꾸면 전체에 적용되는 이점이 개발된 코드를 수정함에 있어 시간적 이점 뿐 아닌 몇 값들을 수정하지 못해 발생하는 오류를 크게 줄여줄 수 있다는 것을 다시금 깨닫는 계기가 되었다.<br />
<br />
테스트 코드 작성 과정은 특히 이번 주차에서 가장 힘들었던 부분이었다. 문법적으로도 익숙하지 않았고, 거시적인 코드만 콘솔을 통해 테스트를 해오던 방식에서 객체들로 세분화하여 각각의 테스트 코드를 작성하는 것은 보다 많은 시간을 소요했다. 그럼에도 테스트 코드 작성 과정에서 보다 많은 점을 배울 수 있었는데, 개별 객체들의 기능을 중점으로 해당 내용에 대한 테스트 코드를 작성하는 것이 중복된 기능과 단일 책임적이 못한 메서드의 기능들을 캐치할 수 있었다. 실제로 구매한 로또 번호들과 당첨 번호에 관련된 정보를 담당하는 LottoSimulator 모델에서 구매한 로또를 생성하는 메서드가 포함되어 있었고 이는 service 담당에 해당하는 LottoService 객체에서 이루어 졌어야 됐다. 또한 생성된 로또 번호에 대한 검증을 생성한 객체의 메서드로 진행하여 검증 과정의 중복이 있음을 알고 수정할 수 있었다.<br />
<br />
전체적으로 다양한 내용에 대해 배우고 적용하며 많은 시행 착오를 겪었단 3주차였다. 특히 테스트 코드를 작성한 후 실행할 때 생각하지 못한 오류가 발생하였는데 Random 라이브러리의 pickUniqueNumbersInRange() 메서드에서 반환된 리스트를 직접 Collections.sort() 하였더니 UnsupportedOperationException이 발생하였다. 이를 해결하고자 라이브러리 코드를 한줄씩 살펴보며 각 메서드의 반환 내용과 구조에 대해 알아보았고 List 라이브러리의 메서드인 subList()가 반환하는 구조적 변경을 지원하지 않는다는 것을 알게되었다. 이후 Stream을 사용하여 새로운 독립 리스트를 생성해 사용하며 해결할 수 있었다. 알고리즘 파악과 패키지 구조 또한 중요하지만 기본 라이브러리의 메서드 기능과 같은 기본적 코드 지식 역시 공부가 필요하다는 점을 상기할 수 있는 기회였다.<br /></p>

<hr />
<blockquote>
  <p>우아한테크코스 - 프리코스 3주차 : 로또</p>
</blockquote>]]></content><author><name></name></author><category term="WoowaCourse" /><summary type="html"><![CDATA[우아한테크코스 - 프리코스 3주차 : 로또 미션은 로또를 포크하고 클론하는 것으로 시작한다. 기능을 구현하기 전 README.md에 구현할 기능 목록을 정리해 추가한다. Git의 커밋 단위는 앞 단계에서 README.md에 정리한 기능 목록 단위로 추가한다. AngularJS Git Commit Message Conventions을 참고해 커밋 메시지를 작성한다. 자세한 과제 진행 방법은 프리코스 진행 가이드 문서를 참고한다.]]></summary></entry><entry><title type="html">[WoowaCourse] 우아한테크코스 - 프리코스 2주차 리뷰</title><link href="https://jynoh00.github.io/woowacourse/2025/10/26/woowa2.html" rel="alternate" type="text/html" title="[WoowaCourse] 우아한테크코스 - 프리코스 2주차 리뷰" /><published>2025-10-26T22:39:00+09:00</published><updated>2025-10-26T22:39:00+09:00</updated><id>https://jynoh00.github.io/woowacourse/2025/10/26/woowa2</id><content type="html" xml:base="https://jynoh00.github.io/woowacourse/2025/10/26/woowa2.html"><![CDATA[<h3 id="우아한테크코스---프리코스-2주차--자동차-경주">우아한테크코스 - 프리코스 2주차 : 자동차 경주</h3>
<blockquote>
  <p>미션은 <a href="https://github.com/woowacourse-precourse/java-racingcar-8">자동차 경주</a>를 포크하고 클론하는 것으로 시작한다.<br />
기능을 구현하기 전 README.md에 구현할 기능 목록을 정리해 추가한다.<br />
Git의 커밋 단위는 앞 단계에서 README.md에 정리한 기능 목록 단위로 추가한다.<br />
<a href="https://gist.github.com/stephenparish/9941e89d80e2bc58a153">AngularJS Git Commit Message Conventions</a>을 참고해 커밋 메시지를 작성한다.<br />
자세한 과제 진행 방법은 프리코스 진행 가이드 문서를 참고한다.</p>
</blockquote>

<hr />

<h3 id="기능-요구-사항">기능 요구 사항</h3>
<p>초간단 자동차 경주 게임을 구현한다.<br />
<br /></p>
<ul>
  <li>주어진 횟수 동안 n대의 자동차는 <code class="language-plaintext highlighter-rouge">전진</code> 또는 <code class="language-plaintext highlighter-rouge">멈출</code> 수 있다.<br /></li>
  <li>각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 <code class="language-plaintext highlighter-rouge">자동차 이름</code>을 같이 출력한다.<br /></li>
  <li>자동차 이름은 쉼표<code class="language-plaintext highlighter-rouge">,</code>를 기준으로 구분하며 이름은 <code class="language-plaintext highlighter-rouge">5자 이하</code>만 가능하다.<br /></li>
  <li>사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다.<br /></li>
  <li>전진하는 조건은 <code class="language-plaintext highlighter-rouge">0에서 9 사이</code>에서 무작위 값을 구한 후 무작위 값이 <code class="language-plaintext highlighter-rouge">4 이상</code>일 경우이다.<br /></li>
  <li>자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다.<br /></li>
  <li>우승자가 여러 명일 경우 쉼표<code class="language-plaintext highlighter-rouge">,</code>를 이용하여 구분한다.<br /></li>
  <li>사용자가 잘못된 값을 입력할 경우 <code class="language-plaintext highlighter-rouge">IllegalArgumentException</code>을 발생시킨 후 애플리케이션은 종료되어야 한다.<br /></li>
</ul>

<p><br /></p>

<h4 id="입출력-요구-사항">입출력 요구 사항</h4>
<h5 id="입력">입력</h5>
<ul>
  <li>경주할 자동차 이름(이름은 쉼표(,) 기준으로 구분)
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pobi,woni,jun
</code></pre></div>    </div>
  </li>
  <li>시도할 횟수
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>5
</code></pre></div>    </div>
  </li>
</ul>

<h5 id="출력">출력</h5>
<ul>
  <li>차수별 실행 결과
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pobi : --
woni : ----
jun : ---
</code></pre></div>    </div>
  </li>
  <li>단독 우승자 안내 문구
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>최종 우승자 : pobi
</code></pre></div>    </div>
  </li>
  <li>공동 우승자 안내 문구
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>최종 우승자 : pobi, jun
</code></pre></div>    </div>
  </li>
</ul>

<h5 id="실행-결과-예시">실행 결과 예시</h5>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)
pobi,woni,jun
시도할 횟수는 몇 회인가요?
5

실행 결과
pobi : -
woni :
jun : -

pobi : --
woni : -
jun : --

pobi : ---
woni : --
jun : ---

pobi : ----
woni : ---
jun : ----

pobi : -----
woni : ----
jun : -----

최종 우승자 : pobi, jun
</code></pre></div></div>

<hr />

<h3 id="프로그래밍-요구-사항-1">프로그래밍 요구 사항 1</h3>
<ul>
  <li>JDK 21 버전에서 실행 가능해야 한다.</li>
  <li>프로그램 실행의 시작점은 Application의 main()이다.</li>
  <li>build.gradle 파일은 변경할 수 없으며, 제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다.</li>
  <li>프로그램 종료 시 System.exit()를 호출하지 않는다.</li>
  <li>프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다.</li>
  <li>자바 코드 컨벤션을 지키면서 프로그래밍한다.</li>
  <li>기본적으로 <a href="https://github.com/woowacourse/woowacourse-docs/tree/main/styleguide/java">Java Style Guide</a>를 원칙으로 한다.</li>
</ul>

<h3 id="프로그래밍-요구-사항-2">프로그래밍 요구 사항 2</h3>

<ul>
  <li>indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다.</li>
  <li>예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.</li>
  <li>힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다.</li>
  <li>3항 연산자를 쓰지 않는다.</li>
  <li>함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라.</li>
  <li>JUnit 5와 AssertJ를 이용하여 정리한 기능 목록이 정상적으로 작동하는지 테스트 코드로 확인한다.</li>
  <li>테스트 도구 사용법이 익숙하지 않다면 아래 문서를 참고하여 학습한 후 테스트를 구현한다.
    <ul>
      <li>JUnit 5 User Guide</li>
      <li>AssertJ User Guide</li>
      <li>AssertJ Exception Assertions</li>
      <li>Guide to JUnit 5 Parameterized Tests</li>
    </ul>
  </li>
</ul>

<h4 id="라이브러리">라이브러리</h4>
<ul>
  <li>camp.nextstep.edu.missionutils에서 제공하는 Randoms 및 Console API를 사용하여 구현해야 한다.</li>
  <li>Random 값 추출은 camp.nextstep.edu.missionutils.Randoms의 pickNumberInRange()를 활용한다.</li>
  <li>사용자가 입력하는 값은 camp.nextstep.edu.missionutils.Console의 readLine()을 활용한다.</li>
</ul>

<hr />

<h3 id="구현-세부사항">구현 세부사항</h3>

<p>자동차 경주 게임을 구현한 Java 애플리케이션<br /></p>

<p>사용자로부터 자동차 이름과 시도 횟수를 입력받아, 각 라운드마다 랜덤 값에 따라 자동차를 전진시키고 최종 우승자를 출력한다.<br /></p>

<p><strong>주요 기능:</strong></p>
<ul>
  <li>쉼표<code class="language-plaintext highlighter-rouge">,</code>로 구분된 여러 자동차 이름 입력 (최대 5자)</li>
  <li>사용자 지정 라운드 수만큼 경주 진행</li>
  <li>랜덤 값(0~9)이 4 이상일 때 자동차 전진</li>
  <li>매 라운드 종료 시 결과 출력</li>
  <li>최종 우승자 결정 (공동 우승 지원)</li>
</ul>

<p><strong>입력 검증:</strong></p>
<ul>
  <li>자동차 이름: 빈 값, 단일 공백, 5자 초과 방지</li>
  <li>시도 횟수: 양의 정수만 허용</li>
</ul>

<h4 id="명시되지-않은-요구사항에-대한-구현">명시되지 않은 요구사항에 대한 구현</h4>

<ul>
  <li>입력값 중 시행 횟수는 양의 정수로 한다.</li>
  <li>자동차의 이름이 단일 공백 문자<code class="language-plaintext highlighter-rouge"> </code>이거나, 빈 값일 경우 에러 처리한다.</li>
  <li>자동차의 이름의 앞 뒤에 공백 문자<code class="language-plaintext highlighter-rouge"> </code>가 존재하는 경우 해당 부분을 제거하고 이름으로 설정한다.</li>
  <li>자동차의 수는 <code class="language-plaintext highlighter-rouge">int</code> 범위(-2,147,483,648 ~ 2,147,483,647)를 넘지 않는다.</li>
  <li>시행 횟수는 <code class="language-plaintext highlighter-rouge">int</code> 범위(-2,147,483,648 ~ 2,147,483,647)를 넘지 않는다.</li>
  <li>범위를 넘어가는 시행 횟수를 입력할 경우 에러 처리한다.</li>
  <li>쉼표<code class="language-plaintext highlighter-rouge">,</code>는 자동차의 이름이 될 수 없다.</li>
  <li>자동차가 전진을 할 경우 <code class="language-plaintext highlighter-rouge">-</code>를 <code class="language-plaintext highlighter-rouge">1</code>개 추가한다. (이동 거리는 1씩 증가한다)</li>
  <li>우승자 선정 기준은 가장 전진 횟수가 많은 차량(차량들)으로 한다.</li>
  <li>우승자가 여러 차량들일 경우, 사용자가 이름 입력을 먼저한 순서로 우승자가 출력된다.</li>
  <li>중복된 차량의 이름이 있을 경우, 처음 입력한 순서를 가지는 하나의 차량으로 설정한다.</li>
</ul>

<p>*시행 횟수: 몇 번의 이동을 할 것인지 사용자가 입력한 값</p>

<h4 id="예외-상황">예외 상황</h4>

<p>다음의 경우 <code class="language-plaintext highlighter-rouge">IllegalArgumentException</code> 발생:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"" // 빈 문자열 입력
-&gt; IllegalArgumentException: [ERROR] 자동차 이름 입력은 빈 값일 수 없습니다.
  
" " // 공백 문자 입력
-&gt; IllegalArgumentException: [ERROR] 자동차 이름 입력은 공백만으로 구성될 수 없습니다.

"po  bi, woni,jun " // 개별 자동차 이름 (내부 공백 포함) 길이 초과 입력
-&gt; IllegalArgumentException: [ERROR] 자동차 이름은 5글자를 초과할 수 없습니다.

"pobi,,jun" // 개별 자동차 이름 빈값 입력
"pobi,"
"pobi,,"
"pobi, ,asd"
-&gt; IllegalArgumentException: [ERROR] 자동차 이름은 빈 값일 수 없습니다.

"0" // 0 입력
"-1" // 공백 문자 입력
-&gt; IllegalArgumentException: [ERROR] 시도 횟수는 양수여야 합니다.
  
"" // 빈값 입력
-&gt; IllegalArgumentException: [ERROR] 시도 횟수는 빈 값일 수 없습니다.

" " // 올바르지 않은 형식들
"asd"
" 30"
"5 "
"*^&amp;*"
"99999999999" // int 범위를 벗어난 경우
-&gt; IllegalArgumentException: [ERROR] 시도 횟수는 올바른 숫자 형식이어야 합니다.

</code></pre></div></div>

<hr />

<h3 id="코드-구조">코드 구조</h3>

<h4 id="패키지-구조">패키지 구조</h4>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>└── src
    ├── main
    │   └── java
    │       └── racingcar
    │           ├── Application.java
    │           ├── controller
    │           │   └── RacingController.java
    │           ├── domain
    │           │   ├── RacingCar.java
    │           │   └── RacingGame.java
    │           ├── exception
    │           │   └── ErrorMessage.java
    │           └── view
    │               ├── InputView.java
    │               └── OutputView.java
</code></pre></div></div>

<h4 id="전체-흐름">전체 흐름</h4>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Application (main)
    ↓
RacingController (전체 흐름 제어)
    ↓
InputView (입력) → RacingController (검증 및 파싱)
    ↓
RacingGame (게임 로직) → RacingCar (개별 차량)
    ↓
OutputView (출력)
</code></pre></div></div>
<h4 id="시행-프로세스">시행 프로세스</h4>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1. 사용자 입력
   - InputView.readCarNames(): 자동차 이름 입력
   - InputView.readRoundCount(): 시도 횟수 입력

2. 데이터 검증 및 파싱
   - RacingController.parseCarNames(): 쉼표 분리, trim, 유효성 검증
   - InputView.validateInputRoundCountFormat(): 숫자 포맷 및 양수 검증

3. 게임 초기화
   - RacingGame 생성: 입력된 이름으로 RacingCar 인스턴스 생성

4. 라운드 실행 (roundCount만큼 반복)
   - RacingGame.run():
     * 각 자동차에 대해 랜덤 값(0~9) 생성
     * RacingCar.move(): 랜덤 값 &gt;= 4이면 position++
     * OutputView.displayOutput(): 현재 위치 출력 (이름 : ---)

5. 결과 산출
   - RacingGame.getWinner(): 최대 position을 가진 자동차 찾기
   - OutputView.displayResult(): 우승자 출력 (쉼표로 구분)

6. 종료
   - Console.close()
</code></pre></div></div>

<hr />

<h3 id="주요-컴포넌트">주요 컴포넌트</h3>

<p><strong>Application</strong></p>
<ul>
  <li>프로그램 진입점</li>
  <li>RacingController 실행</li>
</ul>

<p><strong>RacingController</strong></p>
<ul>
  <li>MVC 패턴의 Controller 역할</li>
  <li>입력/출력 View와 게임 로직 연결</li>
  <li>입력 데이터 검증 및 파싱 책임</li>
  <li>예외 처리 및 리소스 정리 (Console.close)</li>
</ul>

<p><strong>InputView</strong></p>
<ul>
  <li>사용자 입력 담당</li>
  <li>시도 횟수 포맷 검증 (숫자 형식, 양수)</li>
  <li>자동차 이름 검증은 Controller에 위임</li>
</ul>

<p><strong>OutputView</strong></p>
<ul>
  <li>게임 진행 상황 및 결과 출력</li>
  <li>매 라운드 차량 위치 시각화 (<code class="language-plaintext highlighter-rouge">-</code> 사용)</li>
  <li>최종 우승자 출력</li>
</ul>

<p><strong>RacingGame</strong></p>
<ul>
  <li>게임 로직 관리</li>
  <li>RacingCar 인스턴스 리스트 보유</li>
  <li>한 라운드 실행 (모든 차량 이동 로직 시행)</li>
  <li>우승자 결정 로직 (최대 position 탐색, 공동 우승 처리)</li>
</ul>

<p><strong>RacingCar</strong></p>
<ul>
  <li>개별 자동차 도메인 모델</li>
  <li>name(이름), position(위치) 상태 관리</li>
  <li>move() 메서드: 전진 조건 판단 (랜덤 값 &gt;= 4)</li>
</ul>

<hr />

<h3 id="사용-기술-스택">사용 기술 스택</h3>

<ul>
  <li><strong>Language:</strong> Java</li>
  <li><strong>External Libraries:</strong>
    <ul>
      <li><code class="language-plaintext highlighter-rouge">camp.nextstep.edu.missionutils.Console</code>: 콘솔 입력 유틸리티</li>
      <li><code class="language-plaintext highlighter-rouge">camp.nextstep.edu.missionutils.Randoms</code>: 랜덤 값 생성 유틸리티</li>
    </ul>
  </li>
  <li><strong>Design Pattern:</strong> MVC (Model-View-Controller)
    <ul>
      <li>Model: RacingCar, RacingGame</li>
      <li>View: InputView, OutputView</li>
      <li>Controller: RacingController</li>
    </ul>
  </li>
  <li><strong>Java Features:</strong>
    <ul>
      <li>Stream API (입력 파싱)</li>
      <li>Exception Handling (IllegalArgumentException)</li>
      <li>Collections (ArrayList, List)</li>
    </ul>
  </li>
</ul>

<hr />

<h3 id="applicationjava">Application.java</h3>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">racingcar</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">racingcar.controller.RacingController</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Application</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">RacingController</span> <span class="n">racingController</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">RacingController</span><span class="o">();</span>
        <span class="n">racingController</span><span class="o">.</span><span class="na">run</span><span class="o">();</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="racingcontrollerjava">RacingController.java</h3>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">racingcar.controller</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">camp.nextstep.edu.missionutils.Console</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">racingcar.exception.ErrorMessage</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">racingcar.view.InputView</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">racingcar.view.OutputView</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">racingcar.domain.RacingGame</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.util.Arrays</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.List</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.stream.Collectors</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">RacingController</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">int</span> <span class="no">NAME_LENGTH_THRESHOLD</span> <span class="o">=</span> <span class="mi">5</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">String</span> <span class="no">SEPARATOR</span> <span class="o">=</span> <span class="s">","</span><span class="o">;</span>

    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">InputView</span> <span class="n">inputView</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">OutputView</span> <span class="n">outputView</span><span class="o">;</span>
    <span class="kd">private</span> <span class="nc">RacingGame</span> <span class="n">racingGame</span><span class="o">;</span> <span class="c1">// RacingCar 클래스 사용, 1 Round 처리</span>

    <span class="kd">public</span> <span class="nf">RacingController</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">inputView</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">InputView</span><span class="o">();</span>
        <span class="k">this</span><span class="o">.</span><span class="na">outputView</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">OutputView</span><span class="o">();</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">try</span> <span class="o">{</span>
            <span class="c1">// 검증 및 파싱된 입력값</span>
            <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">parsedCarNamesList</span> <span class="o">=</span> <span class="n">parseCarNames</span><span class="o">(</span><span class="n">inputView</span><span class="o">.</span><span class="na">readCarNames</span><span class="o">());</span>
            <span class="kt">int</span> <span class="n">roundCount</span> <span class="o">=</span> <span class="n">inputView</span><span class="o">.</span><span class="na">readRoundCount</span><span class="o">();</span>

            <span class="n">racingGame</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">RacingGame</span><span class="o">(</span><span class="n">parsedCarNamesList</span><span class="o">);</span>

            <span class="n">loopGameRunner</span><span class="o">(</span><span class="n">roundCount</span><span class="o">);</span>

            <span class="n">outputView</span><span class="o">.</span><span class="na">displayResult</span><span class="o">(</span><span class="n">racingGame</span><span class="o">.</span><span class="na">getWinner</span><span class="o">());</span>
        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">IllegalArgumentException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
            <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">e</span><span class="o">.</span><span class="na">getMessage</span><span class="o">());</span>
            <span class="k">throw</span> <span class="n">e</span><span class="o">;</span>
        <span class="o">}</span> <span class="k">finally</span> <span class="o">{</span>
            <span class="nc">Console</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
        <span class="o">}</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="kd">static</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="nf">parseCarNames</span><span class="o">(</span><span class="nc">String</span> <span class="n">input</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">validateInputCarNameEmptyOrBlank</span><span class="o">(</span><span class="n">input</span><span class="o">);</span>

        <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">carNames</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">stream</span><span class="o">(</span><span class="n">input</span><span class="o">.</span><span class="na">split</span><span class="o">(</span><span class="no">SEPARATOR</span><span class="o">,</span> <span class="o">-</span><span class="mi">1</span><span class="o">))</span>
                <span class="o">.</span><span class="na">map</span><span class="o">(</span><span class="nl">String:</span><span class="o">:</span><span class="n">trim</span><span class="o">)</span>
                <span class="o">.</span><span class="na">distinct</span><span class="o">()</span> <span class="c1">// 중복 제거</span>
                <span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">toList</span><span class="o">());</span>

        <span class="n">validateSplitNamesCheck</span><span class="o">(</span><span class="n">carNames</span><span class="o">);</span>

        <span class="k">return</span> <span class="n">carNames</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="c1">// 기본 상태 입력 문자열 검증</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">validateInputCarNameEmptyOrBlank</span><span class="o">(</span><span class="nc">String</span> <span class="n">carNames</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">carNames</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="n">carNames</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">())</span> <span class="o">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="nc">ErrorMessage</span><span class="o">.</span><span class="na">CAR_NAMES_INPUT_NULL_OR_EMPTY</span><span class="o">.</span><span class="na">getMessage</span><span class="o">());</span>
        <span class="o">}</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">carNames</span><span class="o">.</span><span class="na">isBlank</span><span class="o">())</span> <span class="o">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="nc">ErrorMessage</span><span class="o">.</span><span class="na">CAR_NAMES_INPUT_BLANK</span><span class="o">.</span><span class="na">getMessage</span><span class="o">());</span>
        <span class="o">}</span>
    <span class="o">}</span>

    <span class="c1">// 스플릿한 문자열 개별 검증</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">validateSplitNamesCheck</span><span class="o">(</span><span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">carNames</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">for</span> <span class="o">(</span><span class="nc">String</span> <span class="n">carName</span> <span class="o">:</span> <span class="n">carNames</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">carName</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="n">carName</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">())</span> <span class="o">{</span> <span class="c1">// 앞선 trim 과정으로 isBlank() 해당 x</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="nc">ErrorMessage</span><span class="o">.</span><span class="na">CAR_NAME_NULL_OR_EMPTY</span><span class="o">.</span><span class="na">getMessage</span><span class="o">());</span>
            <span class="o">}</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">carName</span><span class="o">.</span><span class="na">length</span><span class="o">()</span> <span class="o">&gt;</span> <span class="no">NAME_LENGTH_THRESHOLD</span><span class="o">)</span> <span class="o">{</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="nc">ErrorMessage</span><span class="o">.</span><span class="na">CAR_NAME_TOO_LONG</span><span class="o">.</span><span class="na">getMessage</span><span class="o">());</span>
            <span class="o">}</span>
        <span class="o">}</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="kt">void</span> <span class="nf">loopGameRunner</span><span class="o">(</span><span class="kt">int</span> <span class="n">roundCount</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">roundCount</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">i</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> <span class="n">outputView</span><span class="o">.</span><span class="na">printInitOutputMessage</span><span class="o">();</span>
            <span class="n">racingGame</span><span class="o">.</span><span class="na">run</span><span class="o">();</span> <span class="c1">// 전체 차량 이동</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="racinggamejava">RacingGame.java</h3>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">racingcar.domain</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">camp.nextstep.edu.missionutils.Randoms</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">racingcar.view.OutputView</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.util.ArrayList</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.List</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">RacingGame</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">int</span> <span class="no">MAXIMUM_RANDOM_NUM</span> <span class="o">=</span> <span class="mi">9</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">OutputView</span> <span class="n">outputView</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">RacingCar</span><span class="o">&gt;</span> <span class="n">racingCars</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">RacingGame</span><span class="o">(</span><span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">carNames</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">outputView</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">OutputView</span><span class="o">();</span>
        <span class="k">this</span><span class="o">.</span><span class="na">racingCars</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;&gt;();</span>

        <span class="k">for</span> <span class="o">(</span><span class="nc">String</span> <span class="n">carName</span> <span class="o">:</span> <span class="n">carNames</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">this</span><span class="o">.</span><span class="na">racingCars</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="k">new</span> <span class="nc">RacingCar</span><span class="o">(</span><span class="n">carName</span><span class="o">));</span>
        <span class="o">}</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">for</span> <span class="o">(</span><span class="nc">RacingCar</span> <span class="n">racingCar</span> <span class="o">:</span> <span class="k">this</span><span class="o">.</span><span class="na">racingCars</span><span class="o">)</span> <span class="o">{</span>
            <span class="kt">int</span> <span class="n">randomValue</span> <span class="o">=</span> <span class="nc">Randoms</span><span class="o">.</span><span class="na">pickNumberInRange</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="no">MAXIMUM_RANDOM_NUM</span><span class="o">);</span>
            <span class="n">racingCar</span><span class="o">.</span><span class="na">move</span><span class="o">(</span><span class="n">randomValue</span><span class="o">);</span>

            <span class="n">outputView</span><span class="o">.</span><span class="na">displayOutput</span><span class="o">(</span><span class="n">racingCar</span><span class="o">.</span><span class="na">getName</span><span class="o">(),</span> <span class="n">racingCar</span><span class="o">.</span><span class="na">getPosition</span><span class="o">());</span>
        <span class="o">}</span>

        <span class="n">outputView</span><span class="o">.</span><span class="na">printNewLine</span><span class="o">();</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="nf">getWinner</span><span class="o">()</span> <span class="o">{</span>
        <span class="kt">int</span> <span class="n">winnerPosition</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">racingCars</span><span class="o">.</span><span class="na">getFirst</span><span class="o">().</span><span class="na">getPosition</span><span class="o">();</span>

        <span class="c1">// 공동 우승자 고려 List&lt;String&gt;으로 작성</span>
        <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">winnerNames</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;&gt;();</span>
        <span class="n">winnerNames</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">racingCars</span><span class="o">.</span><span class="na">getFirst</span><span class="o">().</span><span class="na">getName</span><span class="o">());</span>

        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="k">this</span><span class="o">.</span><span class="na">racingCars</span><span class="o">.</span><span class="na">size</span><span class="o">();</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
            <span class="nc">RacingCar</span> <span class="n">racingCar</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">racingCars</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">);</span>
            <span class="kt">int</span> <span class="n">tmpPosition</span> <span class="o">=</span> <span class="n">racingCar</span><span class="o">.</span><span class="na">getPosition</span><span class="o">();</span>

            <span class="k">if</span> <span class="o">(</span><span class="n">tmpPosition</span> <span class="o">==</span> <span class="n">winnerPosition</span><span class="o">)</span> <span class="n">winnerNames</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">racingCar</span><span class="o">.</span><span class="na">getName</span><span class="o">());</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">tmpPosition</span> <span class="o">&gt;</span> <span class="n">winnerPosition</span><span class="o">)</span> <span class="o">{</span>
                <span class="n">winnerPosition</span> <span class="o">=</span> <span class="n">tmpPosition</span><span class="o">;</span>
                <span class="n">winnerNames</span><span class="o">.</span><span class="na">clear</span><span class="o">();</span>
                <span class="n">winnerNames</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">racingCar</span><span class="o">.</span><span class="na">getName</span><span class="o">());</span>
            <span class="o">}</span>
        <span class="o">}</span>

        <span class="k">return</span> <span class="n">winnerNames</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="racingcarjava">RacingCar.java</h3>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">racingcar.domain</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">RacingCar</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">int</span> <span class="no">FORWARD_THRESHOLD</span> <span class="o">=</span> <span class="mi">4</span><span class="o">;</span>

    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">String</span> <span class="n">name</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kt">int</span> <span class="n">position</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">RacingCar</span><span class="o">(</span><span class="nc">String</span> <span class="n">name</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">name</span> <span class="o">=</span> <span class="n">name</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">position</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">move</span><span class="o">(</span><span class="kt">int</span> <span class="n">randomValue</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">randomValue</span> <span class="o">&gt;=</span> <span class="no">FORWARD_THRESHOLD</span><span class="o">)</span> <span class="n">position</span><span class="o">++;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getName</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="k">this</span><span class="o">.</span><span class="na">name</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">int</span> <span class="nf">getPosition</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="k">this</span><span class="o">.</span><span class="na">position</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="inputviewjava">InputView.java</h3>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">racingcar.view</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">camp.nextstep.edu.missionutils.Console</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">racingcar.exception.ErrorMessage</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">InputView</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">String</span> <span class="no">CAR_NAME_PROMPT</span> <span class="o">=</span> <span class="s">"경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">String</span> <span class="no">ROUND_COUNT_PROMPT</span> <span class="o">=</span> <span class="s">"시도할 횟수는 몇 회인가요?"</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">readCarNames</span><span class="o">()</span> <span class="o">{</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="no">CAR_NAME_PROMPT</span><span class="o">);</span>
        <span class="k">return</span> <span class="nc">Console</span><span class="o">.</span><span class="na">readLine</span><span class="o">();</span> <span class="c1">// 입력값 검증 -&gt; RacingController</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">int</span> <span class="nf">readRoundCount</span><span class="o">()</span> <span class="o">{</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="no">ROUND_COUNT_PROMPT</span><span class="o">);</span>
        <span class="k">return</span> <span class="nf">validateInputRoundCountFormat</span><span class="o">(</span><span class="nc">Console</span><span class="o">.</span><span class="na">readLine</span><span class="o">());</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">int</span> <span class="nf">validateInputRoundCountFormat</span><span class="o">(</span><span class="nc">String</span> <span class="n">roundCount</span><span class="o">)</span> <span class="o">{</span>
        <span class="c1">// 기본 입력 포맷 검증 로직 (숫자 포맷 일치 여부, 음이 아닌 정수)</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">roundCount</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="n">roundCount</span><span class="o">.</span><span class="na">trim</span><span class="o">().</span><span class="na">isEmpty</span><span class="o">())</span> <span class="o">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="nc">ErrorMessage</span><span class="o">.</span><span class="na">ROUND_COUNT_EMPTY</span><span class="o">.</span><span class="na">getMessage</span><span class="o">());</span>
        <span class="o">}</span>

        <span class="k">try</span> <span class="o">{</span>
            <span class="kt">int</span> <span class="n">num</span> <span class="o">=</span> <span class="nc">Integer</span><span class="o">.</span><span class="na">parseInt</span><span class="o">(</span><span class="n">roundCount</span><span class="o">);</span>
            <span class="n">checkPositive</span><span class="o">(</span><span class="n">num</span><span class="o">);</span>

            <span class="k">return</span> <span class="n">num</span><span class="o">;</span>
        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">NumberFormatException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="nc">ErrorMessage</span><span class="o">.</span><span class="na">ROUND_COUNT_INVALID_FORMAT</span><span class="o">.</span><span class="na">getMessage</span><span class="o">());</span>
        <span class="o">}</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">checkPositive</span><span class="o">(</span><span class="kt">int</span> <span class="n">roundCount</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">roundCount</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="o">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">IllegalArgumentException</span><span class="o">(</span><span class="nc">ErrorMessage</span><span class="o">.</span><span class="na">ROUND_COUNT_NOT_POSITIVE</span><span class="o">.</span><span class="na">getMessage</span><span class="o">());</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="outputviewjava">OutputView.java</h3>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">racingcar.view</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.util.List</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">OutputView</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">char</span> <span class="no">MOVING_LINE</span> <span class="o">=</span> <span class="sc">'-'</span><span class="o">;</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">printNewLine</span><span class="o">()</span> <span class="o">{</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">();</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">printInitOutputMessage</span><span class="o">()</span> <span class="o">{</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"실행 결과"</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">displayOutput</span><span class="o">(</span><span class="nc">String</span> <span class="n">carName</span><span class="o">,</span> <span class="kt">int</span> <span class="n">carPosition</span><span class="o">)</span> <span class="o">{</span>
        <span class="c1">// 매 Round마다 호출될 결과 출력</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">carName</span> <span class="o">+</span> <span class="s">" : "</span> <span class="o">+</span> <span class="nc">String</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="no">MOVING_LINE</span><span class="o">).</span><span class="na">repeat</span><span class="o">(</span><span class="n">carPosition</span><span class="o">));</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">displayResult</span><span class="o">(</span><span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">winnerNames</span><span class="o">)</span> <span class="o">{</span>
        <span class="c1">// 최종 우승자 결과 출력</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">print</span><span class="o">(</span><span class="s">"최종 우승자 : "</span> <span class="o">+</span> <span class="nc">String</span><span class="o">.</span><span class="na">join</span><span class="o">(</span><span class="s">", "</span><span class="o">,</span> <span class="n">winnerNames</span><span class="o">));</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="errormessagejava">ErrorMessage.java</h3>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">racingcar.exception</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">enum</span> <span class="nc">ErrorMessage</span> <span class="o">{</span>
    <span class="no">CAR_NAME_NULL_OR_EMPTY</span><span class="o">(</span><span class="s">"자동차 이름은 빈 값일 수 없습니다."</span><span class="o">),</span>
    <span class="no">CAR_NAME_TOO_LONG</span><span class="o">(</span><span class="s">"자동차 이름은 5글자를 초과할 수 없습니다."</span><span class="o">),</span>

    <span class="no">CAR_NAMES_INPUT_NULL_OR_EMPTY</span><span class="o">(</span><span class="s">"자동차 이름 입력은 빈 값일 수 없습니다."</span><span class="o">),</span>
    <span class="no">CAR_NAMES_INPUT_BLANK</span><span class="o">(</span><span class="s">"자동차 이름 입력은 공백만으로 구성될 수 없습니다."</span><span class="o">),</span>

    <span class="no">ROUND_COUNT_EMPTY</span><span class="o">(</span><span class="s">"시도 횟수는 빈 값일 수 없습니다."</span><span class="o">),</span>
    <span class="no">ROUND_COUNT_INVALID_FORMAT</span><span class="o">(</span><span class="s">"시도 횟수는 올바른 숫자 형식이어야 합니다."</span><span class="o">),</span>
    <span class="no">ROUND_COUNT_NOT_POSITIVE</span><span class="o">(</span><span class="s">"시도 횟수는 양수여야 합니다."</span><span class="o">);</span>

    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">String</span> <span class="no">ERROR_PREFIX</span> <span class="o">=</span> <span class="s">"[ERROR] "</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">String</span> <span class="n">message</span><span class="o">;</span>

    <span class="nc">ErrorMessage</span><span class="o">(</span><span class="nc">String</span> <span class="n">message</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">message</span> <span class="o">=</span> <span class="n">message</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getMessage</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="no">ERROR_PREFIX</span> <span class="o">+</span> <span class="n">message</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<hr />

<h3 id="회고">회고</h3>

<p>2주차 프리코스에선 1주차 종료와 함께 제공된 공통 피드백을 최대한 고려하여 반영하고자 노력했다. 그와 동시에 1주차에서 느꼈던 부족한 점(절차 지향에 가까운 프로그래밍, 유지 보수하기 어려운 코드 및 폴더 구조 etc.)을 개선할 수 있는 기회로 만들고 싶었다. 이에 대한 내용으로 2주차 프리코스를 진행하면 느낀 점과 개발 경험에 대해 회고해보고자 한다.<br />
<br />
“오류를 찾을 때 출력 함수 대신 디버거를 사용한다”, 기존 개발을 할 때나 1주차 프리코스 과정 진행 중에도 가장 지키지 못한 내용이었던 것 같다. 처음 사용해보는 인텔리제이 환경 뿐 아니라 기존에 익숙했던 VSCode 환경에서도 오류를 찾을 때, 확인하고자 하는 부분에 출력 함수를 활용해서 디버깅을 하였다. 이번 2주차에서는 이러한 습관을 고치고자 우아한테크 유튜브의 인텔리제이 디버깅 영상을 보고 학습한 후 확인하고자 하는 부분에서 실제 데이터들이 가지는 값을 확인하는 과정을 거치며 코드를 작성하였다. 실제로 Controller에서 입력값 검증을 할 때 단일 자동차 이름 입력 시 검증 과정 중 하나를 실행하지 않아 잘못된 데이터 값이 담기는 것을 바로 확인할 수 있었고 보다 빠른 개발에 도움이 되었다.<br />
<br />
또한 1주차와 마찬가지로 자바 코딩 컨벤션을 최대한 지키고자 더욱 노력하였다. 이 과정에서 변수, 메서드, 클래스 명을 작성하는데 공통 피드백 중 하나인 “축약하지 않는다” 부분을 의식해서 작성하였다. 코드의 길이가 길어지는 것보다 해당 네이밍의 의도를 드러내는 것이 추후 리펙토링, 디버깅 과정에서 훨씬 편리함을 가져다 주었다.<br />
<br />
이외에도 스트림과 메서드를 활용한 문자열 처리, 리스트 컬렉션으로 데이터 관리 등 처음 써보는 기능들을 학습하고 반영하여 코드의 가독성 측면에서 큰 향상성을 직접 체감하였다.<br />
<br />
다음으론 1주차에서 직접 느꼈던 부족한 점을 개선하고자 개발 시 고려하였던 사항들에 대한 내용이다.<br />
<br />
1주차 코드에선 하나의 Application.java의 클래스 내 메서드로 모든 기능을 모아서 구현하였다. 기능상으론 문제가 없다 생각하여 그렇게 구현하였으나 다른 참가자들의 코드를 보며 하나의 클래스에 되도록 여러 기능을 담지 않는 점을 확인할 수 있었다. 이후 찾아본 내용으론 Application.java는 프로그램의 진입점으로 애플리케이션을 시작하는 것 하나의 책임을 가지는 것이 추후 유지보수성과 테스트에서의 용이성 때문에 불리하다는 것을 확인하였다. 그래서 자바 패기지의 여러 폴더 구조를 확인해보며 MVC 패턴이 상당 수 사용된다는 것을 알게 되었다. 이를 반영하여 Model(domain) - 핵심 로직 담당 객체, View - 입출력 로직, Controller - View와 domain의 연결 및 전체 플로우 컨트롤 담당 구조로 패키지를 나누어 코드를 작성하였다.<br />
<br />
레이어별로 나눈 구조에 맞게 테스트 코드 또한 추가로 작성하였다. 1주차에선 콘솔에 직접 입력하여 표준 예외처리 및 기능 검사를 진행하였으나 테스트 코드 사용법을 공부하고 적용하여 미리 만들어 둔 테스트 코드로 추후 리팩토링 된 코드에 동일 내용을 적용하였을 때 결과를 바탕으로 리펙토링 과정에서 생략된 기능들을 찾을 수 있었다.<br />
<br />
추가로 enum 열거형을 사용해 표준 예외 처리 메시지를 모아 관리하는 등, 기존에 알 지 못했던 다양한 방식을 최대한 적용하고자 노력하였다. 이러한 새로 알게된 방식을 적용하는 과정에서 InputView에 View 기능 뿐이 아닌 검증 로직까지 존재해 코드가 무거워 지는 등 확실한 책임 분리가 적용되지 못한게 아쉽게 느껴졌다. 추후 프리코스 주차에선 View에 해당하는 코드는 최대한 입출력과 관련한 기능만 담도록 개선해 나가야겠다. 이번 주차에서 긍정적으로 느낀 점은 README 파일을 먼저 작성한 후 구현 내용을 보고 하나씩 커밋하여 개발한 것이 계획적으로 코드를 작성한 것 같아 조금이나마 나아간 것 같다.<br />
<br />
<br /></p>

<hr />
<blockquote>
  <p>우아한테크코스 - 프리코스 2주차 : 자동차 경주</p>
</blockquote>]]></content><author><name></name></author><category term="WoowaCourse" /><summary type="html"><![CDATA[우아한테크코스 - 프리코스 2주차 : 자동차 경주 미션은 자동차 경주를 포크하고 클론하는 것으로 시작한다. 기능을 구현하기 전 README.md에 구현할 기능 목록을 정리해 추가한다. Git의 커밋 단위는 앞 단계에서 README.md에 정리한 기능 목록 단위로 추가한다. AngularJS Git Commit Message Conventions을 참고해 커밋 메시지를 작성한다. 자세한 과제 진행 방법은 프리코스 진행 가이드 문서를 참고한다.]]></summary></entry><entry><title type="html">[WoowaCourse] 우아한테크코스 - 프리코스 1주차 리뷰</title><link href="https://jynoh00.github.io/woowacourse/2025/10/20/woowa1.html" rel="alternate" type="text/html" title="[WoowaCourse] 우아한테크코스 - 프리코스 1주차 리뷰" /><published>2025-10-20T18:42:00+09:00</published><updated>2025-10-20T18:42:00+09:00</updated><id>https://jynoh00.github.io/woowacourse/2025/10/20/woowa1</id><content type="html" xml:base="https://jynoh00.github.io/woowacourse/2025/10/20/woowa1.html"><![CDATA[<h3 id="우아한테크코스---프리코스-1주차--문자열-덧셈-계산기">우아한테크코스 - 프리코스 1주차 : 문자열 덧셈 계산기</h3>
<blockquote>
  <p>미션은 <a href="https://github.com/woowacourse-precourse/java-calculator-8">문자열 덧셈 계산기 저장소</a>를 포크하고 클론하는 것으로 시작한다.<br />
기능을 구현하기 전 README.md에 구현할 기능 목록을 정리해 추가한다.<br />
Git의 커밋 단위는 앞 단계에서 README.md에 정리한 기능 목록 단위로 추가한다.<br />
<a href="https://gist.github.com/stephenparish/9941e89d80e2bc58a153">AngularJS Git Commit Message Conventions</a>을 참고해 커밋 메시지를 작성한다.<br />
자세한 과제 진행 방법은 프리코스 진행 가이드 문서를 참고한다.</p>
</blockquote>

<hr />

<h3 id="기능-요구-사항">기능 요구 사항</h3>
<p>입력한 문자열에서 숫자를 추출하여 더하는 계산기를 구현한다.<br />
<br />
쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환한다.<br />
예: “” =&gt; 0, “1,2” =&gt; 3, “1,2,3” =&gt; 6, “1,2:3” =&gt; 6<br />
앞의 기본 구분자(쉼표, 콜론) 외에 커스텀 구분자를 지정할 수 있다. 커스텀 구분자는 문자열 앞부분의 “//”와 “\n” 사이에 위치하는 문자를 커스텀 구분자로 사용한다.<br />
예를 들어 “//;\n1;2;3”과 같이 값을 입력할 경우 커스텀 구분자는 세미콜론(;)이며, 결과 값은 6이 반환되어야 한다.<br />
사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시킨 후 애플리케이션은 종료되어야 한다.<br /></p>

<p><br /></p>

<h4 id="입출력-요구-사항">입출력 요구 사항</h4>
<h5 id="입력">입력</h5>
<ul>
  <li>구분자와 양수로 구성된 문자열</li>
</ul>

<h5 id="출력">출력</h5>
<ul>
  <li>덧셈 결과
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>결과 : 6
</code></pre></div>    </div>
  </li>
</ul>

<h5 id="실행-결과-예시">실행 결과 예시</h5>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>덧셈할 문자열을 입력해 주세요.
1,2:3
결과 : 6
</code></pre></div></div>

<hr />

<h3 id="프로그래밍-요구-사항">프로그래밍 요구 사항</h3>
<ul>
  <li>JDK 21 버전에서 실행 가능해야 한다.</li>
  <li>프로그램 실행의 시작점은 Application의 main()이다.</li>
  <li>build.gradle 파일은 변경할 수 없으며, 제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다.</li>
  <li>프로그램 종료 시 System.exit()를 호출하지 않는다.</li>
  <li>프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다.</li>
  <li>자바 코드 컨벤션을 지키면서 프로그래밍한다.</li>
  <li>기본적으로 <a href="https://github.com/woowacourse/woowacourse-docs/tree/main/styleguide/java">Java Style Guide</a>를 원칙으로 한다.</li>
</ul>

<h4 id="라이브러리">라이브러리</h4>
<ul>
  <li>camp.nextstep.edu.missionutils에서 제공하는 Console API를 사용하여 구현해야 한다.</li>
  <li>사용자가 입력하는 값은 camp.nextstep.edu.missionutils.Console의 readLine()을 활용한다.</li>
</ul>

<hr />

<h3 id="구현-세부사항">구현 세부사항</h3>

<h4 id="명시되지-않은-요구사항에-대한-구현">명시되지 않은 요구사항에 대한 구현</h4>

<h5 id="커스텀-구분자">커스텀 구분자</h5>
<ul>
  <li>커스텀 구분자와 기본 구분자(<code class="language-plaintext highlighter-rouge">,</code>, <code class="language-plaintext highlighter-rouge">:</code>)를 혼합하여 사용 가능</li>
  <li>커스텀 구분자는 반드시 입력 문자열의 맨 앞에 정의되어야 함</li>
  <li>커스텀 구분자는 <strong>문자 1개</strong>만 가능하며, 숫자는 불가</li>
  <li>공백 문자(<code class="language-plaintext highlighter-rouge">' '</code>)도 커스텀 구분자로 사용 가능</li>
  <li>커스텀 구분자를 정의했으나 사용하지 않아도 정상 동작 (기본 구분자만 사용하거나 구분자 없이 숫자 하나만 입력)</li>
</ul>

<h5 id="입출력-범위">입출력 범위</h5>
<ul>
  <li><strong>입력 숫자</strong>: <code class="language-plaintext highlighter-rouge">int</code> 범위 (-2,147,483,648 ~ 2,147,483,647)</li>
  <li><strong>출력 결과</strong>: <code class="language-plaintext highlighter-rouge">long</code> 범위 (-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807)</li>
</ul>

<h4 id="예외-상황">예외 상황</h4>

<p>다음의 경우 <code class="language-plaintext highlighter-rouge">IllegalArgumentException</code> 발생:</p>

<table>
  <thead>
    <tr>
      <th>예외 상황</th>
      <th>예시</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>커스텀 구분자 문자의 길이가 1이 아닌 경우</td>
      <td><code class="language-plaintext highlighter-rouge">"//ab\n1"</code>, <code class="language-plaintext highlighter-rouge">"// ;\n1"</code></td>
    </tr>
    <tr>
      <td>커스텀 구분자 형식이 올바르지 않은 경우</td>
      <td><code class="language-plaintext highlighter-rouge">"\n//"</code>, <code class="language-plaintext highlighter-rouge">"// \n\n"</code>, <code class="language-plaintext highlighter-rouge">"////\n"</code></td>
    </tr>
    <tr>
      <td>커스텀 구분자가 숫자인 경우</td>
      <td><code class="language-plaintext highlighter-rouge">"//5\n1:2"</code></td>
    </tr>
    <tr>
      <td>구분자가 연속으로 나오는 경우</td>
      <td><code class="language-plaintext highlighter-rouge">"1,,2"</code>, <code class="language-plaintext highlighter-rouge">"1::2:3"</code></td>
    </tr>
    <tr>
      <td>양수가 아닌 값(0 또는 음수)이 입력된 경우</td>
      <td><code class="language-plaintext highlighter-rouge">"1,-2,3"</code>, <code class="language-plaintext highlighter-rouge">"0,1,2"</code></td>
    </tr>
    <tr>
      <td>구분자와 숫자의 개수가 맞지 않는 경우</td>
      <td><code class="language-plaintext highlighter-rouge">"1,2,"</code>, <code class="language-plaintext highlighter-rouge">",1,2"</code></td>
    </tr>
  </tbody>
</table>

<hr />

<h3 id="코드-구조">코드 구조</h3>
<h4 id="메인-흐름-main">메인 흐름: main()</h4>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>main()
  ├─ readInput()           // 문자열 입력 받아오기
  ├─ calculate()           // 연산 수행
  └─ printAnswer()         // 결과 출력
</code></pre></div></div>
<h4 id="연산-프로세스-calculate">연산 프로세스: calculate()</h4>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>calculate()
  ├─ extractCustomSeparator()    // 커스텀 구분자 추출
       ├─ checkCustomSeparatorFormat()    // 커스텀 구분자 정의 형식 확인
       └─ checkSeparatorIsNotDigit()      // 커스텀 구분자 숫자 여부 확인
  ├─ getNumberString()           // 숫자 문자열 추출 (커스텀 구분자 정의 제외 부분)
  └─ sumNumbers()                // 덧셈 연산 수행
       ├─ buildSeparatorPattern()     // 구분자 패턴 생성
       ├─ checkNumberCount()          // 개수 검증
       └─ parseAndCheckNumber()       // 숫자 파싱 및 검증
</code></pre></div></div>

<hr />

<h3 id="주요-메서드-설명">주요 메서드 설명</h3>
<h4 id="입력된-문자열-처리">입력된 문자열 처리</h4>

<table>
  <thead>
    <tr>
      <th>메서드</th>
      <th>설명</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">readInput()</code></td>
      <td>사용자로부터 문자열 입력 받기</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">calculate()</code></td>
      <td>입력 문자열을 처리하여 합계 연산 수행</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">printAnswer()</code></td>
      <td>계산 결과 출력</td>
    </tr>
  </tbody>
</table>

<h4 id="구분자-처리">구분자 처리</h4>

<table>
  <thead>
    <tr>
      <th>메서드</th>
      <th>설명</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">extractCustomSeparator()</code></td>
      <td>커스텀 구분자 추출 및 검증</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">checkCustomSeparatorFormat()</code></td>
      <td>커스텀 구분자 형식 확인</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">checkSeparatorIsNotDigit()</code></td>
      <td>구분자가 숫자인지 확인</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">buildSeparatorPattern()</code></td>
      <td>정규식 패턴으로 구분자 설정</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">getNumberString()</code></td>
      <td>구분자 설정 부분 제거 후 숫자 문자열 반환</td>
    </tr>
  </tbody>
</table>

<h4 id="계산-및-검증">계산 및 검증</h4>

<table>
  <thead>
    <tr>
      <th>메서드</th>
      <th>설명</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">sumNumbers()</code></td>
      <td>문자열을 분리하여 덧셈 수행</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">parseAndCheckNumber()</code></td>
      <td>문자열을 숫자로 변환 및 검증</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">checkPositive()</code></td>
      <td>양수 여부 확인</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">checkNumberCount()</code></td>
      <td>구분자와 숫자 개수 일치 여부 확인</td>
    </tr>
  </tbody>
</table>

<h4 id="유틸리티">유틸리티</h4>

<table>
  <thead>
    <tr>
      <th>메서드</th>
      <th>설명</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">countSeparators()</code></td>
      <td>문자열 내 구분자 개수 계산</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">isSeparator()</code></td>
      <td>특정 문자가 구분자인지 판단</td>
    </tr>
  </tbody>
</table>

<hr />

<h3 id="내부-클래스">내부 클래스</h3>
<h4 id="customseparator">CustomSeparator</h4>
<p>커스텀 구분자 정보를 담는 클래스</p>

<p><strong>필드:</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">separator</code>: 커스텀 구분자 문자</li>
  <li><code class="language-plaintext highlighter-rouge">hasCustomSeparator</code>: 커스텀 구분자 존재 여부</li>
</ul>

<p><strong>메서드:</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">getSeparator()</code>: 구분자 반환</li>
  <li><code class="language-plaintext highlighter-rouge">hasCustomSeparator()</code>: 커스텀 구분자 존재 여부 반환</li>
</ul>

<hr />

<h3 id="applicationjava">Application.java</h3>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">calculator</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">camp.nextstep.edu.missionutils.Console</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.regex.Pattern</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Application</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">String</span> <span class="no">CUSTOM_SEPARATOR_BEFORE</span> <span class="o">=</span> <span class="s">"//"</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">String</span> <span class="no">CUSTOM_SEPARATOR_AFTER</span> <span class="o">=</span> <span class="s">"\\n"</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">int</span> <span class="no">CUSTOM_SEPARATOR_LENGTH</span> <span class="o">=</span> <span class="mi">5</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">String</span> <span class="no">DEFAULT_SEPARATORS</span> <span class="o">=</span> <span class="s">",|:"</span><span class="o">;</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">try</span><span class="o">{</span>
            <span class="nc">String</span> <span class="n">inputStr</span> <span class="o">=</span> <span class="n">readInput</span><span class="o">();</span>
            <span class="kt">long</span> <span class="n">answer</span> <span class="o">=</span> <span class="n">calculate</span><span class="o">(</span><span class="n">inputStr</span><span class="o">);</span>
            <span class="n">printAnswer</span><span class="o">(</span><span class="n">answer</span><span class="o">);</span>
        <span class="o">}</span><span class="k">catch</span><span class="o">(</span><span class="nc">IllegalArgumentException</span> <span class="n">e</span><span class="o">){</span>
            <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"잘못된 입력: "</span> <span class="o">+</span> <span class="n">e</span><span class="o">.</span><span class="na">getMessage</span><span class="o">());</span>
            <span class="k">throw</span> <span class="n">e</span><span class="o">;</span>
        <span class="o">}</span><span class="k">finally</span><span class="o">{</span>
            <span class="nc">Console</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
        <span class="o">}</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="kd">static</span> <span class="nc">String</span> <span class="nf">readInput</span><span class="o">(){</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"덧셈할 문자열을 입력해 주세요."</span><span class="o">);</span>
        <span class="k">return</span> <span class="nc">Console</span><span class="o">.</span><span class="na">readLine</span><span class="o">();</span>
    <span class="o">}</span>

    <span class="c1">// 합 연산 결과 리턴</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">long</span> <span class="nf">calculate</span><span class="o">(</span><span class="nc">String</span> <span class="n">inputStr</span><span class="o">){</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">inputStr</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">())</span> <span class="k">return</span> <span class="mi">0</span><span class="o">;</span>

        <span class="nc">CustomSeparator</span> <span class="n">separator</span> <span class="o">=</span> <span class="n">extractCustomSeparator</span><span class="o">(</span><span class="n">inputStr</span><span class="o">);</span>
        <span class="nc">String</span> <span class="n">numberString</span> <span class="o">=</span> <span class="n">getNumberString</span><span class="o">(</span><span class="n">inputStr</span><span class="o">,</span> <span class="n">separator</span><span class="o">);</span>

        <span class="k">if</span> <span class="o">(</span><span class="n">numberString</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">())</span> <span class="k">return</span> <span class="mi">0</span><span class="o">;</span>

        <span class="k">return</span> <span class="nf">sumNumbers</span><span class="o">(</span><span class="n">numberString</span><span class="o">,</span> <span class="n">separator</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="c1">// 커스텀 구분자 추출</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="nc">CustomSeparator</span> <span class="nf">extractCustomSeparator</span><span class="o">(</span><span class="nc">String</span> <span class="n">inputStr</span><span class="o">){</span>
        <span class="k">if</span> <span class="o">(!</span><span class="n">inputStr</span><span class="o">.</span><span class="na">startsWith</span><span class="o">(</span><span class="no">CUSTOM_SEPARATOR_BEFORE</span><span class="o">))</span> <span class="k">return</span> <span class="k">new</span> <span class="nc">CustomSeparator</span><span class="o">(</span><span class="kc">null</span><span class="o">,</span> <span class="kc">false</span><span class="o">);</span>

        <span class="n">checkCustomSeparatorFormat</span><span class="o">(</span><span class="n">inputStr</span><span class="o">);</span>
        <span class="kt">char</span> <span class="n">separator</span> <span class="o">=</span> <span class="n">inputStr</span><span class="o">.</span><span class="na">charAt</span><span class="o">(</span><span class="mi">2</span><span class="o">);</span>
        <span class="n">checkSeparatorIsNotDigit</span><span class="o">(</span><span class="n">separator</span><span class="o">);</span>

        <span class="k">return</span> <span class="k">new</span> <span class="nf">CustomSeparator</span><span class="o">(</span><span class="n">separator</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="c1">// 커스텀 구분자 형식 확인</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">checkCustomSeparatorFormat</span><span class="o">(</span><span class="nc">String</span> <span class="n">inputStr</span><span class="o">){</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">inputStr</span><span class="o">.</span><span class="na">length</span><span class="o">()</span> <span class="o">&lt;</span> <span class="no">CUSTOM_SEPARATOR_LENGTH</span><span class="o">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">IllegalArgumentException</span><span class="o">(</span><span class="s">"커스텀 구분자 형식 오류."</span><span class="o">);</span>
        <span class="k">if</span> <span class="o">(!</span><span class="n">inputStr</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="mi">3</span><span class="o">,</span> <span class="mi">5</span><span class="o">).</span><span class="na">equals</span><span class="o">(</span><span class="no">CUSTOM_SEPARATOR_AFTER</span><span class="o">))</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">IllegalArgumentException</span><span class="o">(</span><span class="s">"커스텀 구분자 형식 오류."</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="c1">// 커스텀 구분자 숫자 여부 확인</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">checkSeparatorIsNotDigit</span><span class="o">(</span><span class="kt">char</span> <span class="n">separator</span><span class="o">){</span>
        <span class="k">if</span> <span class="o">(</span><span class="nc">Character</span><span class="o">.</span><span class="na">isDigit</span><span class="o">(</span><span class="n">separator</span><span class="o">))</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">IllegalArgumentException</span><span class="o">(</span><span class="s">"커스텀 구분자 숫자 불가."</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="c1">// 구분자 설정 부분 문자열에서 제거</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="nc">String</span> <span class="nf">getNumberString</span><span class="o">(</span><span class="nc">String</span> <span class="n">inputStr</span><span class="o">,</span> <span class="nc">CustomSeparator</span> <span class="n">separator</span><span class="o">){</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">separator</span><span class="o">.</span><span class="na">hasCustomSeparator</span><span class="o">())</span> <span class="k">return</span> <span class="n">inputStr</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="no">CUSTOM_SEPARATOR_LENGTH</span><span class="o">);</span>
        <span class="k">return</span> <span class="n">inputStr</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="c1">// 문자열 분리 후 합 연산</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">long</span> <span class="nf">sumNumbers</span><span class="o">(</span><span class="nc">String</span> <span class="n">numberString</span><span class="o">,</span> <span class="nc">CustomSeparator</span> <span class="n">separator</span><span class="o">){</span>
        <span class="nc">String</span> <span class="n">separators</span> <span class="o">=</span> <span class="n">buildSeparatorPattern</span><span class="o">(</span><span class="n">separator</span><span class="o">);</span>
        <span class="nc">String</span><span class="o">[]</span> <span class="n">numbers</span> <span class="o">=</span> <span class="n">numberString</span><span class="o">.</span><span class="na">split</span><span class="o">(</span><span class="n">separators</span><span class="o">);</span>

        <span class="n">checkNumberCount</span><span class="o">(</span><span class="n">numberString</span><span class="o">,</span> <span class="n">numbers</span><span class="o">.</span><span class="na">length</span><span class="o">,</span> <span class="n">separator</span><span class="o">);</span>

        <span class="kt">long</span> <span class="n">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
        <span class="k">for</span> <span class="o">(</span><span class="nc">String</span> <span class="n">number</span> <span class="o">:</span> <span class="n">numbers</span><span class="o">){</span>
            <span class="n">sum</span> <span class="o">+=</span> <span class="n">parseAndCheckNumber</span><span class="o">(</span><span class="n">number</span><span class="o">);</span>
        <span class="o">}</span>

        <span class="k">return</span> <span class="n">sum</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="c1">// 구분자 설정 값 리턴</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="nc">String</span> <span class="nf">buildSeparatorPattern</span><span class="o">(</span><span class="nc">CustomSeparator</span> <span class="n">separator</span><span class="o">){</span>
        <span class="k">if</span> <span class="o">(!</span><span class="n">separator</span><span class="o">.</span><span class="na">hasCustomSeparator</span><span class="o">())</span> <span class="k">return</span> <span class="no">DEFAULT_SEPARATORS</span><span class="o">;</span>
        <span class="k">return</span> <span class="no">DEFAULT_SEPARATORS</span> <span class="o">+</span> <span class="s">"|"</span> <span class="o">+</span> <span class="nc">Pattern</span><span class="o">.</span><span class="na">quote</span><span class="o">(</span><span class="nc">String</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">separator</span><span class="o">.</span><span class="na">getSeparator</span><span class="o">()));</span>
    <span class="o">}</span>

    <span class="c1">// 연산자, 피연산자 개수 확인</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">checkNumberCount</span><span class="o">(</span><span class="nc">String</span> <span class="n">numberString</span><span class="o">,</span> <span class="kt">int</span> <span class="n">numberCount</span><span class="o">,</span> <span class="nc">CustomSeparator</span> <span class="n">separator</span><span class="o">){</span>
        <span class="kt">int</span> <span class="n">separatorCount</span> <span class="o">=</span> <span class="n">countSeparators</span><span class="o">(</span><span class="n">numberString</span><span class="o">,</span> <span class="n">separator</span><span class="o">);</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">separatorCount</span><span class="o">+</span><span class="mi">1</span> <span class="o">!=</span> <span class="n">numberCount</span><span class="o">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">IllegalArgumentException</span><span class="o">(</span><span class="s">"구분자와 숫자 개수 형식 불일치."</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="c1">// 구분자 개수 리턴</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">int</span> <span class="nf">countSeparators</span><span class="o">(</span><span class="nc">String</span> <span class="n">numberString</span><span class="o">,</span> <span class="nc">CustomSeparator</span> <span class="n">separator</span><span class="o">){</span>
        <span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
        <span class="k">for</span> <span class="o">(</span><span class="kt">char</span> <span class="n">c</span> <span class="o">:</span> <span class="n">numberString</span><span class="o">.</span><span class="na">toCharArray</span><span class="o">()){</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">isSeparator</span><span class="o">(</span><span class="n">c</span><span class="o">,</span> <span class="n">separator</span><span class="o">))</span> <span class="n">count</span><span class="o">++;</span>
        <span class="o">}</span>

        <span class="k">return</span> <span class="n">count</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="c1">// 구분자 여부 boolean 리턴</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">boolean</span> <span class="nf">isSeparator</span><span class="o">(</span><span class="kt">char</span> <span class="n">c</span><span class="o">,</span> <span class="nc">CustomSeparator</span> <span class="n">separator</span><span class="o">){</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">c</span> <span class="o">==</span> <span class="sc">','</span> <span class="o">||</span> <span class="n">c</span> <span class="o">==</span> <span class="sc">':'</span><span class="o">)</span> <span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
        <span class="k">return</span> <span class="n">separator</span><span class="o">.</span><span class="na">hasCustomSeparator</span><span class="o">()</span> <span class="o">&amp;&amp;</span> <span class="n">c</span> <span class="o">==</span> <span class="n">separator</span><span class="o">.</span><span class="na">getSeparator</span><span class="o">();</span>
    <span class="o">}</span>

    <span class="c1">// 입력 값 int 범위 여부 확인 및 숫자형 포맷 여부 확인</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">int</span> <span class="nf">parseAndCheckNumber</span><span class="o">(</span><span class="nc">String</span> <span class="n">number</span><span class="o">){</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">number</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">())</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">IllegalArgumentException</span><span class="o">(</span><span class="s">"빈 값 입력 불가."</span><span class="o">);</span>

        <span class="k">try</span><span class="o">{</span>
            <span class="kt">int</span> <span class="n">num</span> <span class="o">=</span> <span class="nc">Integer</span><span class="o">.</span><span class="na">parseInt</span><span class="o">(</span><span class="n">number</span><span class="o">);</span>
            <span class="n">checkPositive</span><span class="o">(</span><span class="n">num</span><span class="o">);</span>

            <span class="k">return</span> <span class="n">num</span><span class="o">;</span>
        <span class="o">}</span><span class="k">catch</span><span class="o">(</span><span class="nc">NumberFormatException</span> <span class="n">e</span><span class="o">){</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"올바른 숫자 형식이 아님."</span><span class="o">);</span>
        <span class="o">}</span>
    <span class="o">}</span>

    <span class="c1">// 양수 여부 판단</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">checkPositive</span><span class="o">(</span><span class="kt">int</span> <span class="n">number</span><span class="o">){</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">number</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="o">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">IllegalArgumentException</span><span class="o">(</span><span class="s">"양수만 입력 가능."</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="c1">// output 출력</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">printAnswer</span><span class="o">(</span><span class="kt">long</span> <span class="n">answer</span><span class="o">){</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"결과 : "</span> <span class="o">+</span> <span class="n">answer</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="c1">// 커스텀 구분자 정보 class</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">CustomSeparator</span><span class="o">{</span>
        <span class="kd">private</span> <span class="kd">final</span> <span class="nc">Character</span> <span class="n">separator</span><span class="o">;</span>
        <span class="kd">private</span> <span class="kd">final</span> <span class="kt">boolean</span> <span class="n">hasCustomSeparator</span><span class="o">;</span>

        <span class="kd">public</span> <span class="nf">CustomSeparator</span><span class="o">(</span><span class="nc">Character</span> <span class="n">separator</span><span class="o">,</span> <span class="kt">boolean</span> <span class="n">hasCustomSeparator</span><span class="o">){</span>
            <span class="k">this</span><span class="o">.</span><span class="na">separator</span> <span class="o">=</span> <span class="n">separator</span><span class="o">;</span>
            <span class="k">this</span><span class="o">.</span><span class="na">hasCustomSeparator</span> <span class="o">=</span> <span class="n">hasCustomSeparator</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="kd">public</span> <span class="kt">char</span> <span class="nf">getSeparator</span><span class="o">(){</span> <span class="k">return</span> <span class="n">separator</span><span class="o">;</span> <span class="o">}</span>
        <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">hasCustomSeparator</span><span class="o">(){</span> <span class="k">return</span> <span class="n">hasCustomSeparator</span><span class="o">;</span> <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="회고">회고</h3>

<p>1주차 프리코스를 마무리하며 크게 두 가지 분류(코드를 작성하며 개발할 때 겪었던 경험, 깃허브를 통한 작업 과정)를 통해 회고해보고자 한다.<br />
<br />
평소 JS, C++ 위주로 알고리즘 공부와 개발을 진행하였기에 오랜만에 마주한 자바 코드의 문법들이 낯설어 다시 기본 문법부터 정리하는 시간을 가질 수 있었다. 특히 입력값에 대한 형식 검증을 위해 정규표현식을 사용하였는데 리터럴로 입력된 특수문자를 일반 문자로 처리하는 과정에서 Pattern 클래스의 quote()메서드를 활용하면 사용자가 입력한 문자를 안전하게 일반 문자로 처리할 수 있다는 것을 알게 되었다.<br />
<br />
또한 기본적 문법 외에도 코드를 작성하며 자바 코드 컨벤션을 지키며 변수명, 메서드명 등을 선언하는 것이 추후 개발 작업에 있어서 공동 작업자와 커뮤니케이션을 위한 중요한 습관임을 알 수 있었다. (기존에 혼자 개발해오던 정해지지 않은 코드 스타일이 커뮤니케이션 뿐 아니라 가독성 측면에서도 좋지 않다는 것)<br />
<br />
사용자의 입력값을 처리하는 과정에서 모든 입력 사항을 고려하는 것이 이번 개발의 주요 포인트라 생각하였다. 따라서 다양한 표준 오류 처리, 예외 처리를 진행하였는데, 모든 입력 예시가 명확히 제시되지 않은 만큼 스스로 다양한 입력 경우를 생각하여 입력 형식의 제한을 두고 예외 처리를 진행한 경험이 실무에서 실제 개발을 할 때 생길 수 있는 의도하지 않은 실행 및 버그를 찾아낼 수 있는 초석이 될 것이라 생각하였다.<br />
<br />
다음 내용은 깃허브를 활용한 작업 수행에 대한 회고이다. 깃허브 레포지토리에 혼자서만 개발해오던 프로젝트, 알고리즘 공부 파일 등을 단순 클라우드처럼 사용하여 저장해왔다. 이번 1주차 프리코스를 진행하며 git fork를 통해 레포지토리를 복사하여 PR(Pull Request)하는 일련의 과정을 통해 깃허브를 통한 협력 개발의 진행 방식을 알 수 있었다.<br />
<br />
추가로 커밋 메시지 작성에 있어 항상 신경쓰지 않고 깃허브에 push하던 이전의 습관들이 추후 코드를 리팩토링하거나 진행 사항을 인지하는데 매우 부적합 했다는 것을 커밋 메시지 컨벤션 가이드를 확인하며 알게 되었다. 실무에서는 개발 능력 만큼 중요한 것이 커뮤니케이션 능력이라는 것을 재차 확인할 수 있는 기회였다.<br />
<br />
마지막으로 1주차를 마무리하며 느낀 부족한 점은, 유지보수성을 위해 최대한 기능별로 구분하여 메서드를 작성하였으나 전체적인 코드가 객체 지향보다는 절차 지향에 가깝다 느껴졌으며 내부에 선언한 클래스가 단순 데이터 컨테이너 역할만 수행하고 외부 메서드에 의존한다는 점이 더 큰 프로젝트나 복잡한 로직, 팀 프로젝트에 있어 문제가 된다 느꼈다. 앞으로의 프리코스에서는 코드의 확장성과 재사용 등을 고려하여 객체 지향적으로 개발하며 개선할 수 있도록 해야겠다.
<br />
<br /></p>

<hr />
<blockquote>
  <p>우아한테크코스 - 프리코스 1주차 : 문자열 덧셈 계산기</p>
</blockquote>]]></content><author><name></name></author><category term="WoowaCourse" /><summary type="html"><![CDATA[우아한테크코스 - 프리코스 1주차 : 문자열 덧셈 계산기 미션은 문자열 덧셈 계산기 저장소를 포크하고 클론하는 것으로 시작한다. 기능을 구현하기 전 README.md에 구현할 기능 목록을 정리해 추가한다. Git의 커밋 단위는 앞 단계에서 README.md에 정리한 기능 목록 단위로 추가한다. AngularJS Git Commit Message Conventions을 참고해 커밋 메시지를 작성한다. 자세한 과제 진행 방법은 프리코스 진행 가이드 문서를 참고한다.]]></summary></entry><entry><title type="html">[시] Invictus</title><link href="https://jynoh00.github.io/poetry/2024/10/28/poet5.html" rel="alternate" type="text/html" title="[시] Invictus" /><published>2024-10-28T10:00:00+09:00</published><updated>2024-10-28T10:00:00+09:00</updated><id>https://jynoh00.github.io/poetry/2024/10/28/poet5</id><content type="html" xml:base="https://jynoh00.github.io/poetry/2024/10/28/poet5.html"><![CDATA[<h4 id="invictus---william-ernest-henley"><center>Invictus - William Ernest Henley</center></h4>
<p><br /></p>
<center>
Out of the night that covers me,<br />
Black as the pit from pole to pole,<br />
I thank whatever gods may be<br />
For my unconquerable soul.<br />
<br />
In the fell clutch of circumstance<br />
I have not winced nor cried aloud.<br />
Under the bludgeonings of chance<br />
My head is bloody, but unbowed.<br />
<br />
Beyond this place of wrath and tears<br />
Looms but the Horror of the shade,<br />
And yet the menace of the years<br />
Finds and shall find me unafraid.<br />
<br />
It matters not how strait the gate,<br />
How charged with punishments the scroll,<br />
I am the master of my fate,<br />
I am the captain of my soul.
</center>
<p><br /></p>
<hr />]]></content><author><name></name></author><category term="poetry" /><summary type="html"><![CDATA[Invictus - William Ernest Henley Out of the night that covers me, Black as the pit from pole to pole, I thank whatever gods may be For my unconquerable soul. In the fell clutch of circumstance I have not winced nor cried aloud. Under the bludgeonings of chance My head is bloody, but unbowed. Beyond this place of wrath and tears Looms but the Horror of the shade, And yet the menace of the years Finds and shall find me unafraid. It matters not how strait the gate, How charged with punishments the scroll, I am the master of my fate, I am the captain of my soul.]]></summary></entry><entry><title type="html">[Security] 대칭키, 공개키 암호화</title><link href="https://jynoh00.github.io/security/2024/10/28/security1.html" rel="alternate" type="text/html" title="[Security] 대칭키, 공개키 암호화" /><published>2024-10-28T09:00:00+09:00</published><updated>2024-10-28T09:00:00+09:00</updated><id>https://jynoh00.github.io/security/2024/10/28/security1</id><content type="html" xml:base="https://jynoh00.github.io/security/2024/10/28/security1.html"><![CDATA[<h3 id="암호화">암호화</h3>
<blockquote>
  <p>한 노드(sender)에서 다른 노드(receiver)로 메시지를 전송하려 할 때, 제 3자에게 보여지면 안되는 기밀성이 필요한<br />
메시지일 경우 이를 암호화하여 보내고 받는 상대방이 복호화하는 과정이 필요하다.<br />
이러한 암호화의 가장 간단한 분류인 대칭키(Symmetric key), 공개키(public key) 암호화 알고리즘에 대해 알아보겠다.</p>
</blockquote>

<p><br /></p>

<h4 id="대칭키symmetric-key-암호화">대칭키(Symmetric key) 암호화</h4>
<p>대칭키는 정보를 교환하고자 하는 두 노드가 서로 공유하는 하나의 비밀 Key를 말한다.<br />
sender가 receiver에게 정보를 전송하기 전에, 이러한 비밀키 Ks를 가지고 있다는 가정에서 출발한다.<br /></p>
<center><img src="/public/img/security1-1.png" height="300px" /></center>
<p>위 그림은 대칭키 알고리즘의 암호화, 복호화 과정으로,<br />
Sender와 Receiver가 공유하는 키이자 비밀키 즉 대칭키를 이용하는 것이 해당 알고리즘의 핵심이다.<br />
<br />
보내고자 하는 메시지를 m이라 하면, 이를 대칭키로 암호화한 cipher text가 Ks(m)이 된다.<br />
이후 cipher text를 receiver에게 보내고, cipher text를 sender에게서 받은 reveiver는<br />
대칭키를 이용해 Ks(Ks(m)) = m을 얻어낸다.</p>

<p><br />
이를 exclusive or 연산을 이용해 간략히 C++로 구현하여 보았다.<br /></p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Symmetric key Stream Ciphers</span>
<span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;random&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;ctime&gt;</span><span class="cp">
</span>
<span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="n">string</span> <span class="nf">generate_Ks</span><span class="p">(</span><span class="kt">int</span> <span class="n">k</span><span class="p">){</span> <span class="c1">// 랜덤 Symmetric key 생성</span>
    <span class="n">string</span> <span class="n">s</span> <span class="o">=</span> <span class="s">""</span><span class="p">;</span>
    <span class="n">srand</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">int</span><span class="p">)</span><span class="n">time</span><span class="p">(</span><span class="nb">NULL</span><span class="p">));</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">k</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">s</span> <span class="o">+=</span> <span class="n">rand</span><span class="p">()</span><span class="o">%</span><span class="mi">2</span> <span class="o">+</span> <span class="sc">'0'</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">s</span><span class="p">;</span>
<span class="p">}</span>

<span class="n">string</span> <span class="n">en_decrypt</span><span class="p">(</span><span class="n">string</span> <span class="n">txt</span><span class="p">,</span> <span class="n">string</span> <span class="n">ks</span><span class="p">){</span> <span class="c1">// 암호화, 복호화 과정이 동일 (exclusive or)</span>
    <span class="n">string</span> <span class="n">ans_txt</span> <span class="o">=</span> <span class="s">""</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">txt</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span>
        <span class="n">ans_txt</span> <span class="o">+=</span> <span class="p">(((</span><span class="kt">int</span><span class="p">)</span><span class="n">txt</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">-</span><span class="mi">48</span><span class="p">)</span><span class="o">^</span><span class="p">((</span><span class="kt">int</span><span class="p">)</span><span class="n">ks</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">-</span><span class="mi">48</span><span class="p">))</span><span class="o">+</span><span class="sc">'0'</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">ans_txt</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="n">main</span><span class="p">(){</span>
    <span class="kt">int</span> <span class="n">a</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="kt">char</span> <span class="n">b</span> <span class="o">=</span> <span class="sc">'0'</span><span class="p">;</span>
    <span class="n">string</span> <span class="n">message</span><span class="p">;</span>
    <span class="n">string</span> <span class="n">keyS</span><span class="p">;</span>
    <span class="n">string</span> <span class="n">decryption_txt</span><span class="p">;</span>
    <span class="n">string</span> <span class="n">encryption_txt</span><span class="p">;</span>

    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"input binary code : "</span><span class="p">;</span>
    <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">message</span><span class="p">;</span>

    <span class="n">keyS</span> <span class="o">=</span> <span class="n">generate_Ks</span><span class="p">(</span><span class="n">message</span><span class="p">.</span><span class="n">size</span><span class="p">());</span>
    <span class="n">encryption_txt</span> <span class="o">=</span> <span class="n">en_decrypt</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">keyS</span><span class="p">);</span> <span class="c1">// 암호화</span>
    <span class="n">decryption_txt</span> <span class="o">=</span> <span class="n">en_decrypt</span><span class="p">(</span><span class="n">encryption_txt</span><span class="p">,</span> <span class="n">keyS</span><span class="p">);</span> <span class="c1">// 복호화</span>

    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"------------"</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"plain text: %s</span><span class="se">\n</span><span class="s">symmetric key: %s</span><span class="se">\n</span><span class="s">cipher text: %s</span><span class="se">\n</span><span class="s">decryption text: %s\
    </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="n">message</span><span class="p">.</span><span class="n">c_str</span><span class="p">(),</span> <span class="n">keyS</span><span class="p">.</span><span class="n">c_str</span><span class="p">(),</span> <span class="n">encryption_txt</span><span class="p">.</span><span class="n">c_str</span><span class="p">(),</span> <span class="n">decryption_txt</span><span class="p">.</span><span class="n">c_str</span><span class="p">());</span> 
<span class="p">}</span>   
</code></pre></div></div>

<p><strong>output</strong></p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">input</span> <span class="n">binary</span> <span class="n">code</span> <span class="o">:</span> <span class="mo">01010110</span>
<span class="o">------------</span>
<span class="n">plain</span> <span class="n">text</span><span class="o">:</span> <span class="mo">01010110</span>
<span class="n">symmetric</span> <span class="n">key</span><span class="o">:</span> <span class="mo">01010011</span>
<span class="n">cipher</span> <span class="n">text</span><span class="o">:</span> <span class="mo">00000101</span>
<span class="n">decryption</span> <span class="n">text</span><span class="o">:</span> <span class="mo">01010110</span>
</code></pre></div></div>
<p>output을 보면 원문인 plain text( 010101110 )가 cipher text( 00000101 )로 바뀌게 되어 <br />
암호화를 성공하였다 볼 수 있다. 복호화 과정 역시 암호화와 같은데, 이는 exclusive or 연산에<br />
암호화할 때와 같은 binary code를 적용하였기에 대칭키로 작용한다.
<br /><br /><br /></p>

<h4 id="공개키public-key-암호화">공개키(Public key) 암호화</h4>
<p>공개키는 각 노드가 두개의 key를 가진다. 하나는 본인만 알고있는 private키, 다른 하나는 모두에게 알리는 public key이다.<br />
sender는 receiver가 공개한 receiver의 public key를 이용하여 보낼 메시지를 암호화하고, receiver는 이를<br />
본인만 알고 있는 본인의 private key를 사용하여 복호화하는 알고리즘이다.</p>
<center><img src="/public/img/security1-2.png" height="300px" /></center>
<p>이러한 공개키 암호화 방식은, 기존 대칭키 암호화 방식의 문제점인<br />
어떻게 key를 공유할 것인가의 문제를 해결할 수단으로 나타났다. 암호문을 복호하는 키가 공유될 때 노출될 위험이 없기에<br />
대칭키 암호화 방식에서 쓰일 대칭키를 전달할 때 쓰이기도 한다. (대칭키 방식이 속도가 공개키보다 빠른 장점이 있기에)<br />
<br />
다음 게시글에서 Public key 암호화 알고리즘의 대표적 예시인 RSA 알고리즘에 대하여 자세히 다루도록 하겠다.
<br /><br /></p>
<hr />

<blockquote>
  <p>암호화 방식은 크게 대칭키(Symmetric key), 공개키(Public key) 알고리즘이 있다.
<br />대칭키 방식은 공유하는 비밀의 key 하나,
<br />공개키는 각 노드마다 공개키, 비밀키를 가지고 암호-복호화를 한다.</p>
</blockquote>]]></content><author><name></name></author><category term="Security" /><summary type="html"><![CDATA[암호화 한 노드(sender)에서 다른 노드(receiver)로 메시지를 전송하려 할 때, 제 3자에게 보여지면 안되는 기밀성이 필요한 메시지일 경우 이를 암호화하여 보내고 받는 상대방이 복호화하는 과정이 필요하다. 이러한 암호화의 가장 간단한 분류인 대칭키(Symmetric key), 공개키(public key) 암호화 알고리즘에 대해 알아보겠다.]]></summary></entry><entry><title type="html">[C++] auto</title><link href="https://jynoh00.github.io/c++/2024/08/03/cpp6.html" rel="alternate" type="text/html" title="[C++] auto" /><published>2024-08-03T09:00:00+09:00</published><updated>2024-08-03T09:00:00+09:00</updated><id>https://jynoh00.github.io/c++/2024/08/03/cpp6</id><content type="html" xml:base="https://jynoh00.github.io/c++/2024/08/03/cpp6.html"><![CDATA[<h3 id="auto">auto</h3>
<blockquote>
  <p>auto는 C++11에서 등장한 문법으로, 변수를 생성하는 방법 중 하나이다.<br />
변수의 자료형을 컴파일러가 타입 추론 방식을 통해 결정하기에 선언과 동시에 초기화를 하여야한다.</p>
</blockquote>

<p><br /></p>
<h4 id="1-변수-선언-및-초기화-예시">1. 변수 선언 및 초기화 예시</h4>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">auto</span> <span class="n">a</span><span class="p">;</span> <span class="c1">// error, 선언만 할 경우 타입 추론이 불가</span>

<span class="kt">int</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">c</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="k">auto</span> <span class="n">d</span> <span class="o">=</span> <span class="n">b</span> <span class="o">+</span> <span class="n">c</span><span class="p">;</span> <span class="c1">// 변수 선언과 동시에 초기화</span>

<span class="n">string</span> <span class="n">s</span> <span class="o">=</span> <span class="s">"123"</span><span class="p">;</span>
<span class="k">auto</span> <span class="n">str0</span> <span class="o">=</span> <span class="n">s</span><span class="p">;</span> <span class="c1">// string str0</span>
<span class="k">auto</span> <span class="n">str1</span> <span class="o">=</span> <span class="s">"hello, world"</span><span class="p">;</span> <span class="c1">// const char *str1</span>
<span class="k">auto</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">30</span><span class="p">;</span> <span class="c1">// int i</span>
</code></pre></div></div>
<p><br /></p>

<h4 id="2-포인터">2. 포인터</h4>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Test</span><span class="p">{</span>
<span class="nl">private:</span>
    <span class="n">string</span> <span class="n">my_str</span><span class="p">;</span>
<span class="nl">public:</span>
    <span class="n">Test</span><span class="p">(</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">s</span><span class="p">)</span> <span class="o">:</span> <span class="n">my_str</span><span class="p">(</span><span class="n">s</span><span class="p">){}</span>
    <span class="o">~</span><span class="n">Test</span><span class="p">(){}</span>
    <span class="kt">void</span> <span class="n">print</span><span class="p">(){</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">my_str</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;}</span>
<span class="p">};</span>

<span class="kt">int</span> <span class="n">main</span><span class="p">(){</span>
    <span class="n">string</span> <span class="n">s</span> <span class="o">=</span> <span class="s">"hello, world!"</span><span class="p">;</span>
    <span class="n">Test</span> <span class="n">t</span><span class="p">(</span><span class="n">s</span><span class="p">);</span>

    <span class="k">auto</span> <span class="n">auto1</span> <span class="o">=</span> <span class="n">t</span><span class="p">;</span>
    <span class="k">auto</span><span class="o">*</span> <span class="n">auto2</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">t</span><span class="p">;</span>
    <span class="k">auto</span> <span class="n">auto3</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">t</span><span class="p">;</span>

    <span class="n">auto1</span><span class="p">.</span><span class="n">print</span><span class="p">();</span> <span class="c1">// hello, world!</span>
    <span class="n">auto2</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">();</span> <span class="c1">// hello, world!</span>
    <span class="n">auto3</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">();</span> <span class="c1">// hello, world!</span>
<span class="p">}</span>
</code></pre></div></div>
<p>포인터 역시 auto를 통해 받아 올 수 있다. <br />
이때 auto2와 auto3는 모두 포인터 형식 자료형인데, 선언 시 auto와 auto<em>의 차이점은 무엇일까<br />
<br />
기능적으로 말하면 차이점은 없다. auto나 auto</em>나 초기화 시 포인터를 넘겨주면<br />
컴파일러가 데이터 타입을 추론하기 때문이다. 그럼에도 auto*를 쓰는 이유는 아래 코드를 예시로 설명하겠다.<br /></p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">auto</span> <span class="n">a</span> <span class="o">=</span> <span class="n">testFunc</span><span class="p">();</span>
</code></pre></div></div>
<p>testFunc()의 반환값을 모르는 상태에서 위 코드만 보았을 때 <br />
a의 자료형이 단순 값인지, 포인터값인지 알 수가 없다. 즉 가동성이 떨어지기에 auto*를 사용한다.
<br /><br /></p>
<h4 id="3-참조자">3. 참조자</h4>
<p>참조자 역시 auto로 사용할 수 있는데 아래 코드를 살펴보자.</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Test</span><span class="p">{</span>
<span class="nl">private:</span>
    <span class="n">string</span> <span class="n">my_str</span><span class="p">;</span>
<span class="nl">public:</span>
    <span class="n">Test</span><span class="p">(</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">s</span><span class="p">)</span><span class="o">:</span><span class="n">my_str</span><span class="p">(</span><span class="n">s</span><span class="p">){}</span>
    <span class="o">~</span><span class="n">Test</span><span class="p">(){}</span>
    <span class="kt">void</span> <span class="n">print</span><span class="p">(){</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">my_str</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;}</span>
<span class="p">};</span>

<span class="kt">int</span> <span class="n">main</span><span class="p">(){</span>
    <span class="n">string</span> <span class="n">str</span><span class="p">(</span><span class="s">"hello world"</span><span class="p">);</span>
    <span class="n">Test</span> <span class="n">t1</span><span class="p">(</span><span class="n">str</span><span class="p">);</span>
    <span class="n">Test</span><span class="o">&amp;</span> <span class="n">t2</span> <span class="o">=</span> <span class="n">t1</span><span class="p">;</span> <span class="c1">// t1 참조</span>
    
    <span class="k">auto</span> <span class="n">auto1</span> <span class="o">=</span> <span class="n">t2</span><span class="p">;</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="o">&amp;</span><span class="n">t1</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> <span class="c1">// 0x7ff7b7623310</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="o">&amp;</span><span class="n">t2</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> <span class="c1">// 0x7ff7b7623310</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="o">&amp;</span><span class="n">auto1</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> <span class="c1">// 0x7ff7b76232e0</span>
<span class="p">}</span>
</code></pre></div></div>
<p>주석 처리한 output에서 보이는 것처럼, auto1은 t1을 참조한 t2를 참조한 값이 아닌 것을 알 수 있다.<br />
참조의 성공하였다면 같은 주소값을 출력하여야 하기 때문이다.
<br />
<br />
이는 포인터와는 다르게 참조자에선 auto와 auto&amp;가 기능적으로 다르게 적용된다는 것을 알 수 있다.<br />
아래 코드는 올바르게 auto를 사용하여 참조하는 방법이다.</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">auto</span><span class="o">&amp;</span> <span class="n">auto1</span> <span class="o">=</span> <span class="n">t2</span><span class="p">;</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="o">&amp;</span><span class="n">t1</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> <span class="c1">// 0x7ff7bb6fd310</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="o">&amp;</span><span class="n">t2</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> <span class="c1">// 0x7ff7bb6fd310</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="o">&amp;</span><span class="n">auto1</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> <span class="c1">// 0x7ff7bb6fd310</span>
</code></pre></div></div>

<p><br /></p>
<h4 id="4-가독성-iterator">4. 가독성, iterator</h4>
<p>auto는 자료형을 직접 작성하지 않아도 된다는 장점이 있지만, <br />
가독성을 무너뜨린다는 큰 단점이 존재한다. 이는 코드가 길어질 수록 치명적 단점이 될 수 있다.<br />
<br />
따라서 auto를 사용할 때는 auto라 할 지라도 가독성이 뛰어난 경우, 단점 없이 장점만 요긴하게 사용할 수 있다. <br />
auto 사용이 이득이 되는 가장 좋은 예시로는 iterator가 있다.</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">test_v</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">};</span>

<span class="k">for</span> <span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;::</span><span class="n">iterator</span> <span class="n">iter</span> <span class="o">=</span> <span class="n">test_v</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> <span class="n">iter</span> <span class="o">!=</span> <span class="n">test_v</span><span class="p">.</span><span class="n">end</span><span class="p">();</span> <span class="n">iter</span><span class="o">++</span><span class="p">){</span><span class="o">~~</span><span class="p">}</span> <span class="c1">// a</span>
<span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">iter</span> <span class="o">=</span> <span class="n">test_v</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> <span class="n">iter</span> <span class="o">!=</span> <span class="n">test_v</span><span class="p">.</span><span class="n">end</span><span class="p">();</span> <span class="n">iter</span><span class="o">++</span><span class="p">){</span><span class="o">~~</span><span class="p">}</span> <span class="c1">// b</span>
</code></pre></div></div>
<p>a의 코드가 기본적으로 iterator를 사용하여 벡터를 순회하는 방식이다. <br />
iter 선언 시 코드가 상당히 길어진다. 이를 auto로 사용하면 전체적으로 훨씬 간결해지고<br />
auto로 선언한 변수가 가리키는 값이 무엇인지 확실하기에, 가독성 면에서도 단점이 없다. (begin, end iterator 메소드를 통해 유추 가능)</p>

<p><br /></p>
<hr />
<p><br /></p>
<blockquote>
  <p>auto는 자료형을 컴파일러가 추론하기에 편리하지만, 코드 가독성상 단점이 존재한다.<br />
iterator와 같은 경우를 제외하고는 기존 자료형으로 명시하는 것이 좋다.</p>
</blockquote>]]></content><author><name></name></author><category term="C++" /><summary type="html"><![CDATA[auto auto는 C++11에서 등장한 문법으로, 변수를 생성하는 방법 중 하나이다. 변수의 자료형을 컴파일러가 타입 추론 방식을 통해 결정하기에 선언과 동시에 초기화를 하여야한다.]]></summary></entry><entry><title type="html">[코딩 테스트] OX퀴즈</title><link href="https://jynoh00.github.io/coding-test/2024/07/26/cote7.html" rel="alternate" type="text/html" title="[코딩 테스트] OX퀴즈" /><published>2024-07-26T13:00:00+09:00</published><updated>2024-07-26T13:00:00+09:00</updated><id>https://jynoh00.github.io/coding-test/2024/07/26/cote7</id><content type="html" xml:base="https://jynoh00.github.io/coding-test/2024/07/26/cote7.html"><![CDATA[<blockquote>
  <p>덧셈, 뺄셈 수식들이 ‘X [연산자] Y = Z’ 형태로 들어있는 문자열 배열 quiz가 매개변수로 주어집니다. 수식이 옳다면 “O”를 틀리다면 “X”를 순서대로 담은 배열을 return하도록 solution 함수를 완성해주세요.</p>
</blockquote>

<h4 id="제한사항">제한사항</h4>
<center><strong>
연산 기호와 숫자 사이는 항상 하나의 공백이 존재합니다,<br />단 음수를 표시하는 마이너스 기호와 숫자 사이에는 공백이 존재하지 않습니다. <br />
1 ≤ quiz의 길이 ≤ 10<br />
X, Y, Z는 각각 0부터 9까지 숫자로 이루어진 정수를 의미하며,<br />각 숫자의 맨 앞에 마이너스 기호가 하나 있을 수 있고 이는 음수를 의미합니다.<br />
X, Y, Z는 0을 제외하고는 0으로 시작하지 않습니다.<br />
-10,000 ≤ X, Y ≤ 10,000<br />
-20,000 ≤ Z ≤ 20,000<br />
[연산자]는 + 와 - 중 하나입니다.
</strong></center>

<h4 id="입출력-예">입출력 예</h4>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">quiz</span><span class="p">,</span> <span class="n">result</span>
<span class="p">[</span><span class="s">"3 - 4 = -3"</span><span class="p">,</span> <span class="s">"5 + 6 = 11"</span><span class="p">],</span> <span class="p">[</span><span class="s">"X"</span><span class="p">,</span> <span class="s">"O"</span><span class="p">]</span>
<span class="p">[</span><span class="s">"19 - 6 = 13"</span><span class="p">,</span> <span class="s">"5 + 66 = 71"</span><span class="p">,</span> <span class="s">"5 - 15 = 63"</span><span class="p">,</span> <span class="s">"3 - 1 = 2"</span><span class="p">],</span> <span class="p">[</span><span class="s">"O"</span><span class="p">,</span> <span class="s">"O"</span><span class="p">,</span> <span class="s">"X"</span><span class="p">,</span> <span class="s">"O"</span><span class="p">]</span>
</code></pre></div></div>

<p><br /></p>

<h4 id="풀이">풀이</h4>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;string&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sstream&gt;</span><span class="cp">
</span>
<span class="n">vector</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">solution</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">quiz</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">vector</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">answer</span><span class="p">(</span><span class="n">quiz</span><span class="p">.</span><span class="n">size</span><span class="p">());</span>
    <span class="kt">int</span> <span class="n">num1</span><span class="p">,</span> <span class="n">num2</span><span class="p">,</span> <span class="n">num3</span><span class="p">;</span>
    <span class="kt">char</span> <span class="n">calcu</span><span class="p">,</span> <span class="n">garbage</span><span class="p">;</span>
    
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">quiz</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span>
        <span class="n">stringstream</span> <span class="n">ss</span><span class="p">(</span><span class="n">quiz</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
        <span class="n">ss</span> <span class="o">&gt;&gt;</span> <span class="n">num1</span> <span class="o">&gt;&gt;</span> <span class="n">calcu</span> <span class="o">&gt;&gt;</span> <span class="n">num2</span> <span class="o">&gt;&gt;</span> <span class="n">garbage</span> <span class="o">&gt;&gt;</span> <span class="n">num3</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">calcu</span> <span class="o">==</span> <span class="sc">'+'</span><span class="p">)</span> <span class="n">answer</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">((</span><span class="n">num1</span><span class="o">+</span><span class="n">num2</span><span class="p">)</span> <span class="o">==</span> <span class="n">num3</span><span class="p">)</span> <span class="o">?</span> <span class="s">"O"</span> <span class="o">:</span> <span class="s">"X"</span><span class="p">;</span>
        <span class="k">else</span> <span class="n">answer</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">((</span><span class="n">num1</span><span class="o">-</span><span class="n">num2</span><span class="p">)</span> <span class="o">==</span> <span class="n">num3</span><span class="p">)</span> <span class="o">?</span> <span class="s">"O"</span> <span class="o">:</span> <span class="s">"X"</span><span class="p">;</span>
        <span class="n">ss</span><span class="p">.</span><span class="n">clear</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">answer</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>answer 벡터를 quiz 벡터의 크기와 같게 모두 0으로 초기화한다.<br /><br /></p>

<p>문제에서 quiz 벡터를 공백 기준으로 나누면 총 5개의 원소가 되는데, 이때 각 원소는 제한 조건에 따라<br />
순서에 맞게 타입이 정해져 있다. 따라서 <a href="/c++/2024/07/26/cpp5.html">sstream</a>으로 타입에 맞게 배분해준다.<br /><br /></p>

<p>추가로 설명할 만한 부분은 없지만, 반환 타입에 주의하여 char과 string을 잘 표시하여야 한다. ( ‘c’, “string” )</p>

<p><br /></p>
<hr />
<blockquote>
  <p>문제 제한 사항 이용</p>
</blockquote>]]></content><author><name></name></author><category term="coding-test" /><summary type="html"><![CDATA[덧셈, 뺄셈 수식들이 ‘X [연산자] Y = Z’ 형태로 들어있는 문자열 배열 quiz가 매개변수로 주어집니다. 수식이 옳다면 “O”를 틀리다면 “X”를 순서대로 담은 배열을 return하도록 solution 함수를 완성해주세요.]]></summary></entry><entry><title type="html">[코딩 테스트] 숨어있는 숫자의 덧셈(2)</title><link href="https://jynoh00.github.io/coding-test/2024/07/26/cote6.html" rel="alternate" type="text/html" title="[코딩 테스트] 숨어있는 숫자의 덧셈(2)" /><published>2024-07-26T12:00:00+09:00</published><updated>2024-07-26T12:00:00+09:00</updated><id>https://jynoh00.github.io/coding-test/2024/07/26/cote6</id><content type="html" xml:base="https://jynoh00.github.io/coding-test/2024/07/26/cote6.html"><![CDATA[<blockquote>
  <p>문자열 my_string이 매개변수로 주어집니다. my_string은 소문자, 대문자, 자연수로만 구성되어있습니다.<br />my_string안의 자연수들의 합을 return하도록 solution 함수를 완성해주세요.</p>
</blockquote>

<h4 id="제한사항">제한사항</h4>
<center><strong>
1 ≤ my_string의 길이 ≤ 1,000<br />
1 ≤ my_string 안의 자연수 ≤ 1000<br />
연속된 수는 하나의 숫자로 간주합니다.<br />
000123과 같이 0이 선행하는 경우는 없습니다.<br />
문자열에 자연수가 없는 경우 0을 return 해주세요.
</strong></center>

<h4 id="입출력-예">입출력 예</h4>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">my_string</span><span class="p">,</span> <span class="n">result</span>
<span class="s">"aAb1B2cC34oOp"</span><span class="p">,</span> <span class="mi">37</span>
<span class="s">"1a2b3c4d123Z"</span><span class="p">,</span> <span class="mi">133</span>
</code></pre></div></div>

<p><br /></p>

<h4 id="풀이">풀이</h4>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;string&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sstream&gt;</span><span class="cp">
</span>
<span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="kt">int</span> <span class="nf">solution</span><span class="p">(</span><span class="n">string</span> <span class="n">my_string</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">answer</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">my_string</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span>
        <span class="k">if</span><span class="p">((</span><span class="n">my_string</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">-</span><span class="sc">'0'</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">10</span><span class="p">)</span> <span class="n">my_string</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="sc">' '</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">stringstream</span> <span class="n">ss</span><span class="p">(</span><span class="n">my_string</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">tmp</span><span class="p">;</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">ss</span> <span class="o">&gt;&gt;</span> <span class="n">tmp</span><span class="p">)</span> <span class="n">answer</span> <span class="o">+=</span> <span class="n">tmp</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">answer</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>인자로 받은 my_string을 순회한다. ( 이때 index를 사용하여 순회 )<br /><br />
my_string[i]-‘0’ 을 사용하여, char값을 아스키코드로 변환한다. <code class="language-plaintext highlighter-rouge">0 ~ 9</code>의 아스키코드는 그대로 <code class="language-plaintext highlighter-rouge">0 ~ 9</code> 이기에<br />
값이 10보다 클 경우, 주어진 제한 조건에 따라 해당 char은 숫자가 아닌 문자이고, 이를 공백으로 처리한다.<br /><br />
for 반복문을 끝내면 my_string은 다음과 같이 바뀌게 된다.<br />
<code class="language-plaintext highlighter-rouge">"aAb1B2cC34oOp" -&gt; "   1 2  34   "</code> 이후 <a href="/c++/2024/07/26/cpp5.html">stringstream</a>을 이용하면 공백을 분리할 수 있게 된다.<br />
<br />
while (ss » tmp) 반복문을 사용하여, 공백 기준 분리된 문자열을 임시 정수형 변수 tmp에 저장하고<br />
tmp값을 최종 결과 변수에 더하여 준다. 마지막으로 answer를 반환하며 함수를 종료한다.</p>

<p><br /></p>
<hr />
<blockquote>
  <p>아스키코드를 이용한 문자의 타입 확인, <code class="language-plaintext highlighter-rouge">아스키코드는 16진수이기에 0 ~ 9의 값은 기존 10진수 숫자와 동일</code></p>
</blockquote>]]></content><author><name></name></author><category term="coding-test" /><summary type="html"><![CDATA[문자열 my_string이 매개변수로 주어집니다. my_string은 소문자, 대문자, 자연수로만 구성되어있습니다.my_string안의 자연수들의 합을 return하도록 solution 함수를 완성해주세요.]]></summary></entry><entry><title type="html">[C++] stringstream, 문자열 처리</title><link href="https://jynoh00.github.io/c++/2024/07/26/cpp5.html" rel="alternate" type="text/html" title="[C++] stringstream, 문자열 처리" /><published>2024-07-26T10:00:00+09:00</published><updated>2024-07-26T10:00:00+09:00</updated><id>https://jynoh00.github.io/c++/2024/07/26/cpp5</id><content type="html" xml:base="https://jynoh00.github.io/c++/2024/07/26/cpp5.html"><![CDATA[<h3 id="stringstream">stringstream</h3>
<blockquote>
  <p>stringstream STL은 C++에서 문자열을 다루는데 유용하게 사용된다.<br />
주로 공백을 포함한 문자열을 처리하기 위한 작업을 이행하고, 필요한 정보를 추출하기에 적합하다.</p>
</blockquote>

<h4 id="1-문자열에서-원하는-데이터-추출">1. 문자열에서 원하는 데이터 추출</h4>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;string&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sstream&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(){</span>
    <span class="kt">int</span> <span class="n">num</span><span class="p">;</span>
    <span class="n">string</span> <span class="n">s1</span> <span class="o">=</span> <span class="s">"123 456"</span><span class="p">;</span>
    <span class="n">string</span> <span class="n">s2</span> <span class="o">=</span> <span class="s">"abc 123 456"</span><span class="p">;</span>
    <span class="n">stringstream</span> <span class="n">ss</span><span class="p">(</span><span class="n">s1</span><span class="p">);</span>

    <span class="k">while</span> <span class="p">(</span><span class="n">ss</span> <span class="o">&gt;&gt;</span> <span class="n">num</span><span class="p">){</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">num</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span><span class="p">;}</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
    <span class="n">ss</span><span class="p">.</span><span class="n">clear</span><span class="p">();</span>
    
    <span class="n">ss</span><span class="p">.</span><span class="n">str</span><span class="p">(</span><span class="n">s2</span><span class="p">);</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">ss</span> <span class="o">&gt;&gt;</span> <span class="n">num</span><span class="p">){</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"출력 x"</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">num</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<h5 id="output">output</h5>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">123</span> <span class="mi">456</span>


</code></pre></div></div>
<p>stringstream은 주어진 문자열에서 필요한 자료형을 빼내는 것에 유용하다. 문자열에서 공백과 ‘\n’을 제외하고<br />
원하는 자료형을 추출하는 방법이다. s2문자열을 stream으로 추출할 때, 첫 abc가 int형 변수인 num에 들어갈 수 없기에<br />
반복문이 실행되지 않고 다음 코드인 cout « endl;으로 넘어간다.<br />
<br /></p>

<h4 id="2-clear">2. clear();</h4>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">main</span><span class="p">(){</span>
    <span class="kt">int</span> <span class="n">num</span><span class="p">;</span>
    <span class="n">string</span> <span class="n">s1</span> <span class="o">=</span> <span class="s">"123 456"</span><span class="p">;</span>
    <span class="n">string</span> <span class="n">s2</span> <span class="o">=</span> <span class="s">"999 123 456"</span><span class="p">;</span>
    <span class="n">stringstream</span> <span class="n">ss</span><span class="p">(</span><span class="n">s1</span><span class="p">);</span>

    <span class="k">while</span> <span class="p">(</span><span class="n">ss</span> <span class="o">&gt;&gt;</span> <span class="n">num</span><span class="p">){</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">num</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span><span class="p">;}</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
    <span class="c1">//ss.clear();</span>
    
    <span class="n">ss</span><span class="p">.</span><span class="n">str</span><span class="p">(</span><span class="n">s2</span><span class="p">);</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">ss</span> <span class="o">&gt;&gt;</span> <span class="n">num</span><span class="p">){</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"출력 x"</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">num</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<h5 id="output-1">output</h5>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">123</span> <span class="mi">456</span>


</code></pre></div></div>
<p>그렇다면 위 코드는 왜 두 번째 반복문이 출력되지 않을까?<br /><br />
그 이유는 stream에서 첫 루프 반복이 끝난 이후 상태가 EOF로 설정되고 <br />
이후 상태를 초기화하지 않으면 문제가 발생한다. 따라서 주석 처리한 ss.clear();를 실행하면 두 번째 반복문까지 실행된다.<br />
<br /><br /></p>

<h4 id="3-getline">3. getline</h4>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">main</span><span class="p">(){</span>
    <span class="n">string</span> <span class="n">s</span><span class="p">;</span>
    <span class="n">getline</span><span class="p">(</span><span class="n">cin</span><span class="p">,</span> <span class="n">s</span><span class="p">);</span>
    <span class="n">stringstream</span> <span class="n">ss</span><span class="p">(</span><span class="n">s</span><span class="p">);</span>
    
    <span class="n">string</span> <span class="n">word</span><span class="p">;</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">ss</span> <span class="o">&gt;&gt;</span> <span class="n">word</span><span class="p">){</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">word</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>string을 입력받을 때, 공백을 포함하여 입력받고 싶다면 getline함수를 사용한다.<br />
이후 문자열 s를 stringstream ss에 등록하고 데이터를 추출한다.</p>

<p><br /><br /></p>

<h4 id="4-코딩-테스트-예시">4. 코딩 테스트 예시</h4>
<p>D 기업 프로그래밍 콘테스트 예선 문제 중 간단한 테스트를 예제로 사용법에 대하여 서술하겠다.</p>
<blockquote>
  <p>N명의 학생 정보가 있다. 학생 정보는 학생의 이름과 학생의 성적으로 구분된다.<br /> 각 학생의 이름과 성적 정보를 입력받아, 성적이 낮은 순서대로 출력하는 프로그램 작성하시오.</p>
</blockquote>

<h5 id="입력-예시">입력 예시</h5>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">2</span>
<span class="err">홍길도</span> <span class="mi">95</span>
<span class="err">이순신</span> <span class="mi">77</span>
</code></pre></div></div>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">main</span><span class="p">(){</span>
    <span class="n">map</span><span class="o">&lt;</span><span class="n">string</span><span class="p">,</span> <span class="n">string</span><span class="o">&gt;</span> <span class="n">m</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">n</span><span class="p">;</span>
    <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">n</span><span class="p">;</span>
    <span class="n">cin</span><span class="p">.</span><span class="n">ignore</span><span class="p">();</span> <span class="c1">// </span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span>
        <span class="n">string</span> <span class="n">s</span><span class="p">;</span>
        <span class="n">getline</span><span class="p">(</span><span class="n">cin</span><span class="p">,</span> <span class="n">s</span><span class="p">);</span>
        <span class="n">stringstream</span> <span class="n">ss</span><span class="p">(</span><span class="n">s</span><span class="p">);</span>
        <span class="n">string</span> <span class="n">score</span><span class="p">,</span> <span class="n">name</span><span class="p">;</span>
        <span class="n">ss</span> <span class="o">&gt;&gt;</span> <span class="n">name</span> <span class="o">&gt;&gt;</span> <span class="n">score</span><span class="p">;</span>
        <span class="n">m</span><span class="p">.</span><span class="n">insert</span><span class="p">({</span><span class="n">score</span><span class="p">,</span> <span class="n">name</span><span class="p">});</span>
    <span class="p">}</span>

    <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">iter</span> <span class="o">:</span> <span class="n">m</span><span class="p">){</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">iter</span><span class="p">.</span><span class="n">second</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span><span class="p">;}</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>첫 입력을 int형 정수 n으로 cin한다. 이때 cin » n; 이후 cin.ignore();을 작성한 이유는 <br />
cin » n;에서 남아있는 개행 문자(‘\n’)로 인해 getline함수가 올바르지 않게 실행될 수 있기 때문이다. <br />
<br />
반복문을 n번 실행한다. 반복문안에서 getline함수를 통해 입력받은 문자열을 s에 저장<br />
stringstream에 s를 넣고 처음 값을 name에 다음 값을 score에 넣은 뒤, map에 score, name 페어를 insert한다.<br /><br />
이때 <a href="/c++/2024/07/23/cpp4.html">map</a>을 사용한 이유는 key, value 페어를 저장하기도 하고<br />
key값을 기준으로 자동 오름차순 정렬되는 map에 특성이 있어, key에 해당하는 데이터에 score를 대입하여<br />
문제에서 원하는 점수 기준 오름차순을 구현하였다.<br /><br />
마지막으로 map을 순회하며, second값인 name을 출력시킨다.</p>
<h5 id="input-output">input, output</h5>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">2</span>
<span class="err">홍길동</span> <span class="mi">95</span>
<span class="err">이순신</span> <span class="mi">77</span>

<span class="p">.</span> <span class="p">.</span> <span class="p">.</span>

<span class="err">이순신</span> <span class="err">홍길동</span>
</code></pre></div></div>

<p><br /></p>
<hr />
<blockquote>
  <p>stringstream STL을 사용하면 문자열을 공백 기준으로 처리하기 유용하다.<br />
string을 입력받을 땐, 개행 문자에 주의하여 입력받을 수 있도록 한다. ( cin.ignore(); 사용 )</p>
</blockquote>]]></content><author><name></name></author><category term="C++" /><summary type="html"><![CDATA[stringstream stringstream STL은 C++에서 문자열을 다루는데 유용하게 사용된다. 주로 공백을 포함한 문자열을 처리하기 위한 작업을 이행하고, 필요한 정보를 추출하기에 적합하다.]]></summary></entry><entry><title type="html">[알고리즘] 동적 계획법(Dynamic Programming)</title><link href="https://jynoh00.github.io/algorithm/2024/07/26/algo4.html" rel="alternate" type="text/html" title="[알고리즘] 동적 계획법(Dynamic Programming)" /><published>2024-07-26T09:00:00+09:00</published><updated>2024-07-26T09:00:00+09:00</updated><id>https://jynoh00.github.io/algorithm/2024/07/26/algo4</id><content type="html" xml:base="https://jynoh00.github.io/algorithm/2024/07/26/algo4.html"><![CDATA[<h3 id="동적-계획법dynamic-programming">동적 계획법(Dynamic Programming)</h3>
<blockquote>
  <p>동적 계획법(Dynamic Programming), 줄여서 DP는 메모리 공간을 약간 더 사용하여 연산 속도를<br />
타 알고리즘과 비교해 비약적으로 상승시킬 수 있는 프로그래밍 기법으로
크게 탑다운(Top-down) 방식과<br /> 바텀업(Bottom-up) 방식으로 나눌 수 있다.
추가로 DP를 위해 자주 사용되는 메모이제이션 기법에 대하여 알아보도록 하겠다.</p>
</blockquote>

<p><br /></p>
<h4 id="1-피보나치-수열">1. 피보나치 수열</h4>
<p>다이나믹 프로그래밍을 이용하여 문제를 해결할 때 확연하게 성능적 향샹을 보이는 대표적 예시로,<br />
피보나치 수열이 있다. 피보나치 수열은 이전 두항의 합을 현재의 항으로 설정하는 특징의 수열이다.
<br /><br />
이러한 피보나치 수열은 아래와 같은 점화식으로 나타낼 수 있다.</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">a_n</span><span class="o">+</span><span class="mi">2</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">a_n</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">a_n</span><span class="p">)</span> <span class="o">=</span> <span class="n">a_n</span><span class="o">+</span><span class="mi">1</span> <span class="o">+</span> <span class="n">a_n</span>
</code></pre></div></div>
<p>피보나치 수열은 1항과 2항이 모두 1이기에 각 원소가<br />
<code class="language-plaintext highlighter-rouge">1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...</code> 인 수열이 된다.
<br /><br />
이러한 수학적 점화식은 재귀 함수를 이용하여 표현할 수 있는데 다음 코드는 C++언어로 표현한 피보나치 수열이다.</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">fibo</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">){</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">||</span> <span class="n">x</span><span class="o">==</span><span class="mi">2</span><span class="p">){</span>
        <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">fibo</span><span class="p">(</span><span class="n">x</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="n">fibo</span><span class="p">(</span><span class="n">x</span><span class="o">-</span><span class="mi">2</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>재귀함수를 이용하여 표현한 피보나치 수열은 그냥 보기엔 이상이 없어 보이지만<br />
x값이 커질 수록 심각한 문제가 발생할 수 있다. 이는 x값이 커지면 코드의 수행 시간이 기하급수적으로 늘어나기 때문인데<br />
해당 fibo함수의 <a href="/algorithm/2024/07/06/algo1.html">시간 복잡도</a>를 빅오 표기법으로 나타내면 <strong>O(2^N)</strong>이 되고 이는 지수 시간이 소요된다는 것을 알 수 있다.
<br /><br />
fibo함수를 f라 하고 x의 값을 6이라 할 때, 함수 f의 호출과정은 아래와 같다.</p>
<center><img src="/public/img/algo4-1.png" height="300px" /></center>
<p>그래프를 보면 동일 함수가 반복적으로 호출되는 것을 확인할 수 있는데, 이는 한 번 계산한 함수를 중복적으로<br />
계속 호출할 때 마다 계산하게 된다. 그래프에서 f(3) 함수는 3번 호출되었다. 즉 x의 값이 커질 수록 반복 호출이 더 많이 일어나게 된다.<br />
<br />
이렇게 매번 계산을 반복하여 재귀함수로 문제를 해결하면, 계산량이 너무 늘어나 시간적으로 오랜 시간이 걸린다.<br />
이를 다이나믹 프로그래밍으로 해결하면 훨씬 효율적으로 문제를 해결할 수 있다.<br />
<br />
다만, 다이나믹 프로그래밍을 할 때는 몇 가지 주의해야 할 점이 있다.
<br /><br /></p>
<center><strong>
1. 큰 문제를 작은 문제로 나눌 수 있어야 하며<br />
2. 작은 문제에서 구한 정답이 그것을 포함하는 큰 문제에서 동일하여야 한다.
</strong></center>
<p><br /><br /></p>

<h4 id="2-메모이제이션">2. 메모이제이션</h4>
<p>이제 피보나치 수열을 메모이제이션 기법을 활용한 다이나믹 프로그래밍으로 해결하여 보겠다.<br />
메모이제이션 기법은, 다이나믹 프로그래밍 구현 방법 중 하나로<br />
한 번 구한 결과를 메모리 공간에 저장해두고 다시 호출할 때 저장된 결과를 가져오는 방식이다.<br /></p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">long</span> <span class="kt">long</span> <span class="nf">fibo</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">long</span> <span class="kt">long</span><span class="o">&gt;&amp;</span> <span class="n">v</span><span class="p">){</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">&lt;=</span> <span class="mi">1</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">v</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="n">v</span><span class="p">[</span><span class="n">x</span><span class="p">];</span>
    <span class="n">v</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">=</span> <span class="n">fibo</span><span class="p">(</span><span class="n">x</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="o">+</span> <span class="n">fibo</span><span class="p">(</span><span class="n">x</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">v</span><span class="p">[</span><span class="n">x</span><span class="p">];</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="n">main</span><span class="p">(){</span>
    <span class="n">vector</span><span class="o">&lt;</span><span class="kt">long</span> <span class="kt">long</span><span class="o">&gt;</span> <span class="n">v</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="mi">92</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">fibo</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>위 코드에서 vector<long long=""> v에 0으로 초기화한 100개의 공간을 미리 만드는데, 이 벡터가 바로 메모이제이션 용도의 벡터이다.<br /><br />
fibo함수에 값을 넘겨줄 때, 원하고자 하는 수열의 항 n값과 벡터 v를 넘겨준다.<br /><br />이때 벡터는 재귀호출 시 같은 벡터를 가리킬 수 있게 <a href="/c++/2024/07/08/cpp1.html">참조</a>로 인자를 넘겨준다.<br /><br />
이후 n값을 받아와 fibo함수를 재귀 호출하며 수열을 계산한다.<br /><br />main함수에서 for문을 사용해 벡터를 순회하면 피보나치 수열을 얻을 수 있다.<br /><br />
단, long long값이 나타낼 수 있는 정수의 크기가 제한되기에<br />n = 92까지의 피보나치 수열을 계산할 수 있고, 더 큰 n값은 별도의 데이터 처리가 필요하다.</long></p>

<p><br /><br />
앞서 예로 들었던 f(6)의 경우를, 메모이제이션 기법을 활용한 다이나믹 프로그래밍 방식 피보나치 수열의 그래프로 보면<br /></p>
<center><img src="/public/img/algo4-2.png" height="300px" /></center>
<p>실질적 실행 함수의 수는 <code class="language-plaintext highlighter-rouge">15 -&gt; 4</code>로 확연하게 줄어듦을 알 수 있다.<br />
그럼에도 정확히 <code class="language-plaintext highlighter-rouge">4</code>개의 함수만 호출하는 것은 아니기에, n값이 클 때 오버헤드가 발생할 수 있다.<br /><br />
이러한 오버헤드를 줄이기 위해선, 재귀함수 대신 반복문을 사용하는 것이 좋다.<br />
(일반적으로, 다이나믹 프로그래밍은 반복문을 이용한 것이 재귀함수를 이용한 것보다 더 성능이 좋다)<br /><br />
다이나믹 프로그래밍을 적용한 피보나치 수열 알고리즘의 시간 복잡도는 O(N)이다.<br /><br />
그 이유는 f(1)을 구한 값이 f(2)를 구하는데 사용되고, f(2)의 값이 f(3)을 구하는 것에 사용되기에<br />
한 번 구한 결과는 다시 구해지지 않기 때문이다. 실제로 호출되는 함수에 대해서 확인하기 위해<br />
아래와 같은 코드를 실행하여 보겠다.</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">long</span> <span class="kt">long</span> <span class="nf">fibo</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">long</span> <span class="kt">long</span><span class="o">&gt;&amp;</span> <span class="n">v</span><span class="p">){</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"f("</span> <span class="o">&lt;&lt;</span> <span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="s">")"</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">||</span> <span class="n">x</span> <span class="o">==</span> <span class="mi">2</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">v</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="n">v</span><span class="p">[</span><span class="n">x</span><span class="p">];</span>
    <span class="n">v</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">=</span> <span class="n">fibo</span><span class="p">(</span><span class="n">x</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="o">+</span> <span class="n">fibo</span><span class="p">(</span><span class="n">x</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">v</span><span class="p">[</span><span class="n">x</span><span class="p">];</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="n">main</span><span class="p">(){</span>
    <span class="n">vector</span><span class="o">&lt;</span><span class="kt">long</span> <span class="kt">long</span> <span class="kt">int</span><span class="o">&gt;</span> <span class="n">v</span><span class="p">(</span><span class="mi">200</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="mi">6</span><span class="p">;</span>
    <span class="n">fibo</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<h5 id="output">output</h5>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">f</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span> <span class="n">f</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="n">f</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span> <span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="n">f</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="n">f</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="n">f</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="n">f</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span> 
</code></pre></div></div>

<h4 id="3-탑다운-바텀업">3. 탑다운, 바텀업</h4>
<p>output에서 보이는 것처럼, 재귀 함수를 이용하여 다이나믹 프로그래밍을 작성하는 방법을<br />
큰 문제를 해결하기 위해 작은 함수를 호출한다하여 <strong>탑다운(Top-down) 방식</strong>이라 한다.<br /><br /></p>

<p>반대로 단순히 반복문을 이용하여 다이나믹 프로그래밍으로 문제를 해결하는 방식을<br />
작은 문제부터 답을 도출한다하여 <strong>바텀업(Bottom-up) 방식</strong>이라고 말한다.<br /><br /></p>

<p>바텀업 방식의 반복문 활용 다이나믹 프로그래밍을 이용한 피보나치 수열의 코드는 아래와 같다.</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">main</span><span class="p">(){</span>
    <span class="n">vector</span><span class="o">&lt;</span><span class="kt">long</span> <span class="kt">long</span><span class="o">&gt;</span> <span class="n">v</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>
    <span class="n">v</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="n">v</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="mi">6</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span>
        <span class="n">v</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">v</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">v</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">2</span><span class="p">];</span>
    <span class="p">}</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">long</span> <span class="kt">long</span> <span class="n">i</span> <span class="o">:</span> <span class="n">v</span><span class="p">){</span>
        <span class="k">if</span><span class="p">(</span><span class="n">i</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">i</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<h5 id="output-1">output</h5>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">1</span> <span class="mi">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">5</span> <span class="mi">8</span>
</code></pre></div></div>
<p><br /></p>
<h4 id="4-dp-테이블">4. DP 테이블</h4>
<p>탑다운 방식은 하향식이라고도 하며, 바텀업 방식은 상향식이라고도 한다.<br />
다이나믹 프로그래밍의 전형적 형태는 바텀업 방식으로, 바텀업 방식에서 사용되는 결과 저장용 배열을 <strong>DP 테이블</strong>이라 부르며,<br />
메모이제이션 기법은 탑 다운 방식에 국한되어 사용하는 표현이다.
<br /></p>

<ul>
  <li>DP 테이블 - 바텀업</li>
  <li>메모이제이션 - 탑다운</li>
</ul>

<p>( 엄밀하게는 메모이제이션은 이전 계산 결과를 일시적으로 기록하는 넓은 개념이기에 다이나믹 프로그래밍과는 별도의 개념이다. )</p>

<p><br /></p>
<hr />
<blockquote>
  <p>다이나믹 프로그래밍은 조금의 메모리 공간을 추가로 사용하여, 시간 복잡도를 확연히 줄일 수 있는 프로그래밍 기법이다.<br />
탑다운 방식보단 바텀업 방식이 권장되며( 시스템 상 재귀함수 스택 크기가 한정적일 수 있기 때문 )<br />
최단 경로 알고리즘이나 플로이드 워셜 알고리즘 등 다양한 문제 해결 방식에 도움을 준다.</p>
</blockquote>]]></content><author><name></name></author><category term="algorithm" /><summary type="html"><![CDATA[동적 계획법(Dynamic Programming) 동적 계획법(Dynamic Programming), 줄여서 DP는 메모리 공간을 약간 더 사용하여 연산 속도를 타 알고리즘과 비교해 비약적으로 상승시킬 수 있는 프로그래밍 기법으로 크게 탑다운(Top-down) 방식과 바텀업(Bottom-up) 방식으로 나눌 수 있다. 추가로 DP를 위해 자주 사용되는 메모이제이션 기법에 대하여 알아보도록 하겠다.]]></summary></entry></feed>