<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dan_d.log</title>
        <link>https://velog.io/</link>
        <description>협업, 문제해결, 지속적 학습을 추구하는 개발자 지망생 단디입니다.</description>
        <lastBuildDate>Wed, 29 Oct 2025 12:16:49 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dan_d.log</title>
            <url>https://velog.velcdn.com/images/dan_d/profile/8878c034-d999-4d78-955f-5efcbacf49ec/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dan_d.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dan_d" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[순열과 조합]]></title>
            <link>https://velog.io/@dan_d/%EC%88%9C%EC%97%B4%EA%B3%BC-%EC%A1%B0%ED%95%A9</link>
            <guid>https://velog.io/@dan_d/%EC%88%9C%EC%97%B4%EA%B3%BC-%EC%A1%B0%ED%95%A9</guid>
            <pubDate>Wed, 29 Oct 2025 12:16:49 GMT</pubDate>
            <description><![CDATA[<h1 id="npr-ncr-참고">nPr, nCr 참고</h1>
<h2 id="순열-의사코드">순열 의사코드</h2>
<pre><code>function permute(남은문자, 현재단어, r):
    if length(현재단어) == r:
        print(현재단어)
        return

    for each 문자 in 남은문자:
        새남은문자 = 남은문자에서 문자 하나 제거
        새단어 = 현재단어 + 문자
        permute(새남은문자, 새단어, r)
</code></pre><h2 id="조합-의사코드">조합 의사코드</h2>
<pre><code>function combine(전체문자열, startIndex, 현재단어, r):
    if length(현재단어) == r:
        print(현재단어)
        return

    for i from startIndex to 전체문자열의 끝:
        새단어 = 현재단어 + 전체문자열[i]
        combine(전체문자열, i + 1, 새단어, r)</code></pre><h2 id="c-version">C version</h2>
<pre><code class="language-c">#include &lt;stdio.h&gt;
#include &lt;string.h&gt;

// ---------- 수학 유틸 ----------

long factorial(int n) {
    if (n &lt;= 1) return 1;
    return n * factorial(n - 1);
}

long nPr(int n, int r) {
    if (r &gt; n) return 0;
    return factorial(n) / factorial(n - r);
}

long nCr(int n, int r) {
    if (r &gt; n) return 0;
    return factorial(n) / (factorial(r) * factorial(n - r));
}

// ---------- 순열 (permutation) 출력 ----------
// remaining: 아직 안 쓴 문자들
// current: 지금까지 만든 문자열
// r: 목표 길이
void print_permute(const char *remaining, const char *current, int r) {
    if ((int)strlen(current) == r) {
        printf(&quot;%s\n&quot;, current);
        return;
    }

    int len = (int)strlen(remaining);

    for (int i = 0; i &lt; len; i++) {
        char next_remaining[64];
        char next_current[64];

        // next_remaining = remaining에서 i번째 문자 제거
        // 예: remaining=&quot;ABC&quot;, i=1 -&gt; &quot;AC&quot;
        // 앞부분 복사
        strncpy(next_remaining, remaining, i);
        // 뒷부분 복사
        strcpy(next_remaining + i, remaining + i + 1);

        // next_current = current + remaining[i]
        strcpy(next_current, current);
        int cur_len = (int)strlen(next_current);
        next_current[cur_len] = remaining[i];
        next_current[cur_len + 1] = &#39;\0&#39;;

        print_permute(next_remaining, next_current, r);
    }
}

// ---------- 조합 (combination) 출력 ----------
// str: 전체 문자열
// start: 이번에 고를 위치 시작 인덱스
// current: 지금까지 고른 문자들
// r: 목표 길이
void print_combine(const char *str, int start, const char *current, int r) {
    if ((int)strlen(current) == r) {
        printf(&quot;%s\n&quot;, current);
        return;
    }

    int n = (int)strlen(str);

    for (int i = start; i &lt; n; i++) {
        char next_current[64];

        // next_current = current + str[i]
        strcpy(next_current, current);
        int cur_len = (int)strlen(next_current);
        next_current[cur_len] = str[i];
        next_current[cur_len + 1] = &#39;\0&#39;;

        // 다음은 i+1부터 (앞에서 쓴 건 다시 안 쓰기 위해)
        print_combine(str, i + 1, next_current, r);
    }
}

int main() {
    char str[64];
    int r;

    printf(&quot;문자열 입력: &quot;);
    scanf(&quot;%63s&quot;, str);

    printf(&quot;r 입력: &quot;);
    scanf(&quot;%d&quot;, &amp;r);

    int n = (int)strlen(str);

    printf(&quot;\n--- 순열 개수 (nPr) ---\n&quot;);
    printf(&quot;%ld\n&quot;, nPr(n, r));

    printf(&quot;\n--- 조합 개수 (nCr) ---\n&quot;);
    printf(&quot;%ld\n&quot;, nCr(n, r));

    printf(&quot;\n--- 순열 문자열 (permutations, order matters) ---\n&quot;);
    print_permute(str, &quot;&quot;, r);

    printf(&quot;\n--- 조합 문자열 (combinations, order doesn&#39;t matter) ---\n&quot;);
    print_combine(str, 0, &quot;&quot;, r);

    return 0;
}
</code></pre>
<h2 id="java-version">Java version</h2>
<pre><code class="language-java">import java.util.*;

public class PermutationCombination {

    // ✅ 팩토리얼 함수
    static long factorial(int n) {
        if (n &lt;= 1) return 1;
        return n * factorial(n - 1);
    }

    // ✅ 순열 nPr
    static long nPr(int n, int r) {
        if (r &gt; n) return 0;
        return factorial(n) / factorial(n - r);
    }

    // ✅ 조합 nCr
    static long nCr(int n, int r) {
        if (r &gt; n) return 0;
        return factorial(n) / (factorial(r) * factorial(n - r));
    }

    // ✅ 문자열 순열
    static void permute(String remaining, String current, int r) {
        if (current.length() == r) {
            System.out.println(current);
            return;
        }

        for (int i = 0; i &lt; remaining.length(); i++) {
            String nextRemaining = remaining.substring(0, i) + remaining.substring(i + 1);
            permute(nextRemaining, current + remaining.charAt(i), r);
        }
    }

    // ✅ 문자열 조합
    static void combine(String str, int start, String current, int r) {
        if (current.length() == r) {
            System.out.println(current);
            return;
        }

        for (int i = start; i &lt; str.length(); i++) {
            combine(str, i + 1, current + str.charAt(i), r);
        }
    }

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

        System.out.print(&quot;문자열 입력: &quot;);
        String str = sc.next();
        int n = str.length();

        System.out.print(&quot;r 입력: &quot;);
        int r = sc.nextInt();

        System.out.println(&quot;\n--- 순열 개수 (nPr) ---&quot;);
        System.out.println(nPr(n, r));

        System.out.println(&quot;\n--- 조합 개수 (nCr) ---&quot;);
        System.out.println(nCr(n, r));

        System.out.println(&quot;\n--- 순열 문자열 ---&quot;);
        permute(str, &quot;&quot;, r);

        System.out.println(&quot;\n--- 조합 문자열 ---&quot;);
        combine(str, 0, &quot;&quot;, r);
    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 8기] BE 1주차 회고]]></title>
            <link>https://velog.io/@dan_d/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-8%EA%B8%B0-BE-1%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dan_d/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-8%EA%B8%B0-BE-1%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 27 Oct 2025 14:08:11 GMT</pubDate>
            <description><![CDATA[<h1 id="좋았던-점-배운-점">좋았던 점, 배운 점</h1>
<h2 id="1-tools-컨벤션">1. Tools, 컨벤션</h2>
<p>아래의 Tools과 컨벤션에 대해서 공부하게 되어서 개인의 생산성과 협업 시 유리할 수 있는 이점을 알 수 있어 좋았습니다.
개인적으로 neovim 편집기를 사용하는 것을 좋아했는데 이번 우테코 미션을 진행하면서 ideavim을 알게 됐고 기본 IntelliJ 기능과 vim 기능을 함께 사용하면서 많은 시간이 소모되었지만 유의미한 생산성 향상이 느껴져서 뜻깊은 시간이 됐습니다.
저의 환경설정을 참고 하셔서 도움이 되신다면 저 또한 기쁘기때문에 궁금하신 분들을 위해서 링크를 남기겠습니다.</p>
<ul>
<li>Lazygit</li>
<li>IntelliJ + Ideavim</li>
<li>Git + Java 컨벤션</li>
</ul>
<p><a href="https://github.com/miniminjae92/dotfiles">링크: my dotfiles</a></p>
<h2 id="2-객체지향-방법론에-대한-독서">2. 객체지향 방법론에 대한 독서</h2>
<p>1주차를 진행하면서 객체지향적 사고를 위해 여러 책들을 참고를 했습니다.
특히 &#39;객체지향의 사실과 오해&#39; 추천서를 다시 읽으면서 시스템, 책임, 협력, 역할, 객체, 클래스, 메세지 등 여러가지 개념들을 정리하면서 객체지향 세계에 재미를 들일 수 있었습니다.</p>
<p>예를 들어서 시스템이란 어떤 목적이 있고 그것을 이루기 위한, 공통된 목적을 가진 객체들로 구성된다고 상상을 해봤습니다. 시스템의 목적을 효율적으로 달성하는 협력이 중요하다고 느꼈습니다. 그러기 위해서 책임들이 분리가 되고 그 책임을 수행할 수 있는 역할이 존재하므로 해당 책임을 완벽하게 수행할 수 있다면 어떤 객체든 대체 가능하다고 생각했습니다. 제가 앞으로 함께 깊이 공감하고 해결하고 싶은 문제를 가진 회사를 찾고 백엔드 개발자 역할을 찾고 있다면 그 백엔드 역할을 수행할 수 있는 책임을 파악하고 해당 책임을 수행할 수 있는 능력을 키운다면 직무를 시작할 수 있겠다는 구체적인 생각도 할 수 있는 계기가 됐습니다.</p>
<h2 id="3-code">3. Code</h2>
<h3 id="test-code에서-배운-점">Test Code에서 배운 점</h3>
<ol>
<li>assertThat(result).isEqualTo(List.of());</li>
<li>@CsvSource, value = {}, delimiter = &#39;</li>
<li>@MethodSource</li>
</ol>
<pre><code>
  // assertThat(result).isEqualTo(List.of());의 발견

  @ParameterizedTest // @CsvSource의 발견
  @CsvSource( 
      value = {&quot;1,2|1:2&quot;, &quot;1,2,3|1:2:3&quot;, &quot;1,2:3|1:2:3&quot;},
      delimiter = &#39;|&#39;)
  void 쉼표가_포함된_문자열이_들어올경우_구분한다(String input, String expected) {
    ...
  }

  @ParameterizedTest
  @CsvSource(
      value = {
        &quot;&#39;//;\\n1;2;3&#39;|1:2:3&quot;,
        &quot;&#39;//;\\n1;2;3;&#39;|1:2:3&quot;,
        &quot;&#39;//;\\n1;2,3:4;&#39;|1:2:3:4&quot;,
        &quot;&#39;//abc\\n1abc2,3:4abc&#39;|1:2:3:4&quot;,
      },
      delimiter = &#39;|&#39;)
  void 커스텀구분자가_있는경우_구분한다(String input, String expected) {
    ...
  }
}

public class ConverterTest {

    private final Converter converter = new Converter();

    private static Stream&lt;Arguments&gt; nomalConversionProvider() {
        return Stream.of(
                Arguments.of(List.of(&quot;1&quot;, &quot;2&quot;, &quot;3&quot;), List.of(1, 2, 3)),
                Arguments.of(List.of(&quot;5&quot;, &quot;10&quot;), List.of(5, 10)),
                Arguments.of(List.of(&quot;7&quot;), List.of(7)),
                Arguments.of(List.of(&quot;0&quot;, &quot;100&quot;), List.of(0, 100)),
                Arguments.of(List.of(&quot;2147483647&quot;, &quot;1&quot;), List.of(2147483647, 1))
        );
    }

    @ParameterizedTest
    @MethodSource(&quot;nomalConversionProvider&quot;) // @MethodSource의 발견
    void 정상적인_변환_테스트(List&lt;String&gt; input, List&lt;Integer&gt; expected) {
        List&lt;Integer&gt; result = converter.convertPositiveNumbers(input);
        assertThat(result).isEqualTo(expected);
    }

    @Test
    void 숫자가_아닐경우_예외를_반환한다() {
        List&lt;String&gt; invalid = List.of(&quot;1&quot;, &quot;a&quot;, &quot;2&quot;);

        assertThatThrownBy(() -&gt; {
            converter.convertPositiveNumbers(invalid);
        }).isInstanceOf(IllegalArgumentException.class);
    }


// assertThatThrownBy(() -&gt; {run}).isInstanceOf(Exception.class)의 발견
        assertThatThrownBy(() -&gt; {
            converter.convertPositiveNumbers(negative);
        }).isInstanceOf(IllegalArgumentException.class);
    }

}</code></pre><h3 id="src-code에서-배운점">Src Code에서 배운점</h3>
<ol>
<li>Pattern 패턴객체 = Pattern.compile(정규표현식);
Matcher 매처객체 = 패턴객체.matcher(검사문자열)
if (매처객체.find()) 활용, 정규표현식 () group 활용법</li>
<li>this.delimiters = new HashSet&lt;&gt;(DEFAULT_DELIMITERS);
new HashSet&lt;&gt;(Set<String>)으로 새로운 Set<String> 만드는 법</li>
<li>stream().map(Pattern::quote).collect(Collectors.joining(&quot;|&quot;));</li>
<li>stream().map(this::parseToken).map(this::validatePositive)</li>
<li>stream().mapToLong(Integer::longValue).sum();</li>
</ol>
<pre><code>    private static final Set&lt;String&gt; DEFAULT_DELIMITERS = Set.of(&quot;,&quot;, &quot;:&quot;);
    private static final Pattern CUSTOM_DELIMITER = Pattern.compile(REGEX_CUSTOM);
    private final Set&lt;String&gt; delimiters;

    public Delimiter() {
        this.delimiters = new HashSet&lt;&gt;(DEFAULT_DELIMITERS);
    }


    ...

      delimiters.stream().map(Pattern::quote).collect(Collectors.joining(&quot;|&quot;));
        return Stream.of(payLoad.split(regex))
                .filter(s -&gt; !s.isEmpty())
                .collect(Collectors.toList());
    }

    private String checkCustomDelimiter(String string) {
        Matcher matcher = CUSTOM_DELIMITER.matcher(string);
        if (matcher.find()) {
            String customDelimiter = matcher.group(1);
            String payLoad = matcher.group(2);
            if (customDelimiter.isEmpty()) {
                return payLoad;
            }
            delimiters.add(customDelimiter);
            return payLoad;
        }
        return string;
    }

}

  public class Converter {

    public List&lt;Integer&gt; convertPositiveNumbers(List&lt;String&gt; payLoad) {
        ...
        return payLoad.stream()
                .map(this::parseToken)
                .map(this::validatePositive)
                .collect(Collectors.toList());
    }

    private int parseToken(String token) {
    ...
    }

    private int validatePositive(int number) {
    ...
    }
}</code></pre><h2 id="4-에러사항">4. 에러사항</h2>
<p>Delimiter 클래스를 구현하면서 정규표현식에 대해서 어려움을 느꼈습니다.
정규표현식에서 , 백슬래시 기호가 escape로 취급되어 &quot;\\n&quot;로 표현해야하는 점에 있어서 여러 차례 try가 발생했습니다.
정규표현식에서 사용하는 특수문자들과 예외 케이스들에 대해서 생각을 못했습니다.
다음에는 신경을 쓰면 좋을 것 같다고 생각하여 코드리뷰처럼 다양한 관점을 볼 수 있는 기회가 중요하다고 더욱 느꼈습니다.</p>
<p>&lt;참고&gt;</p>
<blockquote>
<p>본문: . ^ $ * + ? ( ) [ ] { } | \는 기본적으로 이스케이프 대상.
  컨텍스트(문자클래스, 확장모드, 치환문자열, 언어 문자열)에 따라 추가 문자(] - ^ / # 공백 $ )도 이스케이프 필요.
  길면 \Q...\E로 한 번에 리터럴화.</p>
</blockquote>
<h2 id="5-마무리">5. 마무리</h2>
<ul>
<li>기본 자료형, 객체 참조 자료형, 컬렉션, 스트림에 대해서 반복적으로 학습을 해서 구현력을 올려야겠다고 생각이 들었습니다.</li>
<li>새로운 IDE, 세팅에 맞춰서 단축키와 기능들에 대해서 적응하는 연습이 필요하다고 느꼈습니다.</li>
<li>동료들의 코드들을 읽어보고 디스코드의 좋은 자료들을 읽고 생각하고 사용하는 연습을 하면서 발전해야겠다고 생각했습니다.</li>
<li>남은 미션들도 진행하면서 많이 배울 수 있으면 좋겠고 멘탈 관리, 건강 관리!! 화이팅</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 7기] BE 최종 코딩테스트 후기]]></title>
            <link>https://velog.io/@dan_d/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-BE-%EC%B5%9C%EC%A2%85-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@dan_d/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-BE-%EC%B5%9C%EC%A2%85-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Thu, 19 Dec 2024 03:12:50 GMT</pubDate>
            <description><![CDATA[<h3 id="내가-최종-코테를">내가 최종 코테를?</h3>
<p>4주차 편의점 미션을 정해진 규칙을 지키지 못했고 기능 구현이 잘못된 상태로 끝났었다.
테스트는 통과를 했지만 기능들 구현이 부족하고 프로그래밍 요구 사항도 지키질 못해서 기대를 내려놨었다.</p>
<p>학교 공부를 했고 그래도 혹시나? 라는 생각에 조금 시간을 내어서 우테코 코테 준비를 하려했었다.
하지만 학교 공부도 시간이 많이 부족하여 예상과 다르게 학교 공부에만 전념하게 되었다.</p>
<p>결과는 최종코테 참가 기회가 주어졌다!</p>
<p>정말 너무나도 기뻤다.</p>
<p>특히 내가 퇴사를 하고 전혀 무관한 분야에 발을 내딛기도 했고 자바를 처음 경험했는데 내 선택이 틀리지 않았고
앞으로도 노력을 하면 된다고 처음으로 인정해준 것 같아서 그 순간 벅차오르고 기뻤던 것 같다.</p>
<h3 id="제대로-넘어지다">제대로 넘어지다</h3>
<p>하지만 지금 생각해보면 나에겐 몇가지 큰 문제가 있었다.</p>
<p>첫째, 애초에 난 프리코스 준비과정에서 모르는 지식이 생길 때마다 GPT를 통해서 개념을 쌓아왔다.
당연히 구글링과 독서, 유튜브등 여러 가지 공간에서 정보를 습득하였었지만 주로 GPT를 통해서 배웠다.</p>
<p>둘째, 연습을 TDD로 단위테스트에 집중해서 천천히 꼼꼼하게 새롭게 연습을 했다.
하지만 마지막 일주일전부터 테스트 진행을 생략하고 컨트롤러에 모두 구현, 서비스에 모두 구현 등 방법을 바꿔가기 시작했다.</p>
<p>셋째, 뒤늦게 공부한다고 매일 매일 새벽 4시에 자거나 정서적으로 신체적으로 컨디션 조절을 못했다.</p>
<p>이러한 것들이 실제로 최종 테스트를 치러간 날, 나에게 큰 영향을 줬던 것 같다.</p>
<p>당일 아침부터 카페에 가서 준비를 했고 시작 전부터 허리 통증도 있고 눈 앞도 흐려지는 등 그때부터도 컨디션이 좋지 않았던 것 같다.
시작할 때 머리가 멍했고, 1시가 되면서 지원페이지를 새로고침하였지만 변화가 없어서 당황을 했다.
주변엔 다 시작을 한 것 같은데 나는 허둥지둥했다.
몇 번 더 새로고침을 했고 심호흡을 하면서 차분하게 요구사항을 읽어갔다.
이상하게 읽어도 읽어도 머리가 멍했고 연습 부족의 결과가 그때 찾아온 것 같다.
연습을 할 때엔 협력 메세지를 먼저 작성하고 거기에서 책임을 나누고 그곳에서 도메인을 식별을 했다.
그리고 도메인을 통해서 기능과 검증을 진행하고 서비스에서 비즈니스로직을 진행한다.
컨트롤러에서 뷰와 연결을 하여 디버깅을 하면서 연습을 했었는데 실전에서는 기능 목록 작성부터 망했던 것 같다.
생각이 많았고 나눠서 단순하게 생각하질 못했다, 다 나의 실력 부족이다 🥲</p>
<h3 id="나의-맥북-에어가-처음으로">나의 맥북 에어가 처음으로..</h3>
<p>시작을 하고 노션 페이지가 멈췄고 이어서 크롬도 멈췄다.
처음 겪는 일이었다.
다행인지 불행인지 인텔리제이는 멀쩡했지만 빠르게 재부팅을 했어야했는데 
나는 여태 재부팅을 하거나 프로그램 강제종료를 하는 법도 모르고 있었다.
인텔리제이는 작동을 하여서 멍한 정신 상태로 계속해서 진행했다... 막무가내로...</p>
<h3 id="막혔던-점">막혔던 점</h3>
<p>LocalDateTime, 시간을 다루는 것이 부족했다.
빠르게 구글링을 했어야했다.
기능 목록 작성을 못했고 그 상태에서 어거지로 입출력을 보면서 차례대로 절차적으로 프로그래밍하려고 했다.
변수명도 구별하기 힘들게 적었고 총체적 난국이었다.
그럴수록 머릿속은 더욱 망가져갔고 멘탈적으로 지금 생각해보면 너무 이해가 안가는데...
지나간 일이니깐 좋은 경험이라고 생각할 수 밖에 없다.</p>
<p>다 잘못된 연습 방법과 부족한 연습량, 학교 공부를 한다고 모든 것을 쏟지 못한 부분, 다른 사람들의 코드를 보며 급하게 지금 껏 해왔던 연습 방법을 바꾸고 모든 것을 바꾸려고 했던 점 등 나의 지난 행동들에 의해서 그런 경험을 겪게 되었던 것 같다.</p>
<p><strong>가장 아쉬운 부분은 시간이 걸리더라도 요구 사항 기능 목록을 차분하게 작성했어야 했다.</strong>
연습을 할 때, 기존 나의 방법에서는 단위테스트를 하면서 오히려 시스템의 요구사항과 구조들이 정립이 되었는데
그것을 안하니깐 구조가 어지러워서 아무것도 못했다.
즉, 나누어서 생각을 못한 것 같다.</p>
<p>요구 사항을 여러 번 읽고 머리속으로 이해를 하고 하던지 도메인 식별하여 하나씩 하던지 했어야했는데 그것이 아쉽다.
그리고 .을 찍어서 여러 메소드를 직접 프린트 해보면서 메소드를 찾았던 게 후회된다.
GPT에게 물어보듯이 그냥 구글링을 했어야 했는데 시간 낭비가 너무너무 심했다.
<del>이제부터는 모두 구글링 할래! GPT와 결별 선언 😭</del></p>
<p>연습을 할 때에 enum이 너무 좋았기 때문에 Month 와 같이 여러 가지들을 모두 enum으로 구현을 하려했는데
Time 기존 유틸과 겹치면서 당황을 한 점에서 특히 시간을 많이 뺐겼다.</p>
<p>안하던 짓으로 MVC 구조를 먼저 잡아두고 했더니 아무것도 못했다.
<del>돌아가는 쓰레기를 만들어야한다고!!!!</del></p>
<p>멍한 정신 상태에서 겪은 문제들에 대한 대처들이 나의 선택들이 너무 후회가 된다.
실컷 잠도 자고 컨디션 회복하고 다시 보니깐 너무 간단하게 보이는 것들을 왜 생각 못 했는지... 지금 보니깐 너무 아쉽다.</p>
<p>항상 컨디션이 안좋더라도 시험 당시만 되면 정신 번쩍, 무엇이든 잘 했었던 경험이 있는데
내 생각보다 컨디션도 중요하구나 라는 것을 이번에 크게 경험을 해보게 됐다.
<del>실전과 같은 연습과 계속해서 밀고 나가기 그리고 잠을 잘 자자, 운동도 하고!</del></p>
<h3 id="소감">소감</h3>
<p>결국 시험은 끝이 났고 일어나서 가방에 짐을 챙기고나서 마음 속 깊은 곳에서 울컥함이 강하게 올라왔다.
그동안의 노력이 스쳐갔고 실력 발휘를 평소의 반의 반, 그것의 반의 반도 못한 것이 너무나도 슬펐던 것 같다.
어렵거나 문제가 생기더라도 그래도 있는 실력을 어떻게해서든 보여주는 경험을 했었는데
처음으로 이러한 경험도 해보니깐 좀 더 힘들었던 것 같다.</p>
<p>부끄럽지만 이미 지나간 일이다.
나를 위로하기 위해서 여러 가지 생각을 했고 그 중 나를 위로해줬던 생각을 적어본다.</p>
<blockquote>
<p>인생은 마라톤이라고 생각한다.
지금 한 구간에서 넘어졌다.
내가 무엇을 하면 되는가?
단순하다, 일어서서 다시 달리면 된다.</p>
</blockquote>
<p>현재는 마음을 추스린 상태다.
이번 경험에 대해서 정말 좋은 기억으로 생각한다.
이러한 경험을 못했더라면 언젠가 경험했을 수도 있다고 생각하고
우테코 프리코스에 참여한 좋은 분들에게서 너무나도 좋은 기운들을 얻었고
프리코스를 진행하면서 깨닫게된 많은 지식이 있다.</p>
<p>앞으로의 계획도 세웠고 하나씩 꾸준하게 해나갈 것이다.
Stay Hard!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024년 학습 키워드]]></title>
            <link>https://velog.io/@dan_d/2024%EB%85%84-%ED%95%99%EC%8A%B5-%ED%82%A4%EC%9B%8C%EB%93%9C</link>
            <guid>https://velog.io/@dan_d/2024%EB%85%84-%ED%95%99%EC%8A%B5-%ED%82%A4%EC%9B%8C%EB%93%9C</guid>
            <pubDate>Mon, 09 Dec 2024 06:07:50 GMT</pubDate>
            <description><![CDATA[<p>새로운 것을 인풋하는 것도 중요하지만 망각하는 것을 방지하면서 배운 것을 하나라도 더 가져가는 것이 하나를 더 배우는 것보다 중요할 수 있다고 생각한다.</p>
<p>특히 아웃풋 학습(키워드를 가지고 백지에 작성을 하거나 개념을 추상화한 것을 설명하기 등)을 진행하는 것이 진정한 지식으로 거듭날 수 있는 가장 빠른 지름길 인 것 같다.</p>
<h2 id="아웃풋-복습-목차-키워드">아웃풋 복습 목차 키워드</h2>
<ul>
<li>C, Python, C++, Java 프로그래밍 언어</li>
<li>운영체제, 정보통신망, 소프트웨어공학</li>
<li>미적분, 이산수학, 선형대수</li>
<li>자료구조, 프로그래밍 언어론</li>
<li>통계, 빅데이터, 머신러닝</li>
<li>웹크롤링, Pytorch, MySQL, 장고</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[SSH]]></title>
            <link>https://velog.io/@dan_d/SSH</link>
            <guid>https://velog.io/@dan_d/SSH</guid>
            <pubDate>Mon, 09 Dec 2024 05:58:16 GMT</pubDate>
            <description><![CDATA[<h2 id="ssh-튜토리얼-핵심-요약">SSH 튜토리얼 핵심 요약</h2>
<h3 id="ssh-키-생성">SSH 키 생성</h3>
<pre><code>ssh-keygen -t rsa -b 4096 -C &quot;your_email@example.com&quot;</code></pre><p>-t rsa: RSA 알고리즘 사용.
-b 4096: 키 크기 4096비트 설정.
-C: 키에 주석 추가(이메일 또는 컴퓨터 이름 등 식별 정보).</p>
<h3 id="키-저장-위치-선택">키 저장 위치 선택</h3>
<p>기본 경로(~/.ssh/id_rsa)에 저장하려면 Enter 키.
다른 경로를 지정하려면 입력 후 Enter.</p>
<h3 id="암호-설정">암호 설정</h3>
<p>키를 보호하기 위한 암호(Passphrase)를 입력.
암호 없이 사용하려면 그냥 Enter.</p>
<h3 id="공개-키-확인">공개 키 확인</h3>
<pre><code>cat ~/.ssh/id_rsa.pub</code></pre><p>공개 키 파일(id_rsa.pub)의 내용을 복사.</p>
<h3 id="github에-ssh-키-등록">GitHub에 SSH 키 등록</h3>
<p>GitHub → Settings → SSH and GPG keys → New SSH Key 클릭.</p>
<p>Title: 키의 이름 입력(예: &quot;MacBook SSH Key&quot;).</p>
<p>Key: 복사한 공개 키 내용을 붙여넣기.</p>
<p>원격 저장소 URL 변경 (HTTPS → SSH)</p>
<pre><code>git remote set-url origin git@github.com:&lt;username&gt;/&lt;repository&gt;.git</code></pre><h3 id="ssh-연결-테스트">SSH 연결 테스트</h3>
<pre><code>ssh -T git@github.com</code></pre><p>성공 메시지:</p>
<pre><code>Hi &lt;username&gt;! You&#39;ve successfully authenticated, but GitHub does not provide shell access.</code></pre><h3 id="추가-팁">추가 팁</h3>
<p>권한 설정: 개인 키 파일의 권한을 안전하게 설정:</p>
<pre><code>chmod 600 ~/.ssh/id_rsa</code></pre><p>여러 SSH 키 사용:</p>
<p>~/.ssh/config 파일 생성:</p>
<pre><code>Host github.com
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_rsa</code></pre><p>키 관리: GitHub에서 사용하지 않는 키는 삭제:</p>
<p>GitHub → Settings → SSH and GPG keys → 키 삭제.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[구현 문제 접근법]]></title>
            <link>https://velog.io/@dan_d/%EA%B5%AC%ED%98%84-%EB%AC%B8%EC%A0%9C-%EC%A0%91%EA%B7%BC%EB%B2%95</link>
            <guid>https://velog.io/@dan_d/%EA%B5%AC%ED%98%84-%EB%AC%B8%EC%A0%9C-%EC%A0%91%EA%B7%BC%EB%B2%95</guid>
            <pubDate>Mon, 09 Dec 2024 05:48:49 GMT</pubDate>
            <description><![CDATA[<h2 id="객체지향-패러다임에-맞춘-방법">객체지향 패러다임에 맞춘 방법</h2>
<h3 id="지속적으로-업데이트-중">지속적으로 업데이트 중</h3>
<ol>
<li>전체 기본 요구 사항을 가볍게 읽어본다.</li>
<li>입력, 출력, 입출력 예시를 읽어본다.</li>
<li>시나리오, 입출력 흐름대로 창조주의 입장에서 메세지를 작성해본다. ( 메세지로 하나의 시스템을 동작할 수 있도록)</li>
<li>메세지를 개별 책임으로 추상화한다. (단일 기능 느낌)</li>
<li>도메인 식별을 한다. (재사용성, 상태, 행동을 가지는지)</li>
<li>도메인 별로 책임을 기반한 기능 목록을 재구성한다.</li>
<li>Application 의 main, controller, view, domain을 구성한다.</li>
<li>작성한 기능목록을 기반으로 domain 단위 테스트를 진행한다. (RED -&gt; GREEN -&gt; REFACTOR)</li>
<li>TDD를 진행 시 입력 검증, 비즈니스 검증을 분리한다.</li>
<li>전체적인 최소 기능, 돌아가는 쓰레기를 controller에 모두 작성한다.</li>
<li>시간적 여유에 따라 service, util, dto, enum, constants 리팩토링 작업을 실시한다.</li>
</ol>
<h3 id="우선적으로-공부할-것들">우선적으로 공부할 것들</h3>
<ul>
<li>Enum, Errormessage, PromptMessage 틀 짜기.</li>
<li>int, char, Integer, String, List, Set, Map 자유자재로 변형 및 처리해보기</li>
<li>Stream, Builder, file IO 공부해보기</li>
<li>Junit5, AssertJ 경험 쌓기</li>
<li>생성자, 접근제어자에 대한 고찰</li>
</ul>
<h3 id="계획하고-실천하면서-바뀐-생각-자유롭게-작성">계획하고 실천하면서 바뀐 생각, 자유롭게 작성</h3>
<p>도메인 식별을 먼저하려고 했고 기능 목록을 작성해서 TDD 방식으로 개발을 진행했다.
경험 부족과 연습 부족때문인지 시간이 오래걸렸다.
작은 단위테스트 진행은 익숙해지고 있는데 항상 전체적인 테스트를 확인해야할 때 마지막 도메인과 서비스, 컨트롤러, 뷰, 앱을 연결하는 작업에서 TDD를 어떻게 진행해야하는지 어려움을 겪고 있다.
위에 적은 순서대로 진행을 하고 후기 작성해보자.</p>
<p>구현 문제를 풀면서 어려웠던 점이 데이터를 원하는대로 저장하고 처리하는 것이 어려웠다.
그래서 원하는 자료로 변환하고 뽑아내고 처리하는 것을 중점으로 연습하기로 했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[IntelliJ Shortcut Tip]]></title>
            <link>https://velog.io/@dan_d/IntelliJ-Shortcut-Tip</link>
            <guid>https://velog.io/@dan_d/IntelliJ-Shortcut-Tip</guid>
            <pubDate>Tue, 03 Dec 2024 08:17:37 GMT</pubDate>
            <description><![CDATA[<h2 id="인텔리제이-단축키">인텔리제이 단축키</h2>
<ul>
<li>shift + F6</li>
<li>ctrl + alt + C, V, M, F</li>
<li>command + N</li>
<li>F2</li>
<li>ctrl + space</li>
<li>ctrl + shift + enter</li>
<li>alt + enter</li>
<li>ctrl + alt + L</li>
<li>command + shift + F8 : view breakpoint</li>
<li>command + 5 : Debug tool</li>
<li>command + B</li>
<li>command + alt +B</li>
<li>command + alt + 방향키</li>
<li>shift + shift</li>
</ul>
<p>break point 우클릭으로 조건
발생한 Exception을 복사해서 디버깅 툴에서 검색</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024-11-13 학습 기록]]></title>
            <link>https://velog.io/@dan_d/2024-11-13-%ED%95%99%EC%8A%B5-%EA%B8%B0%EB%A1%9D</link>
            <guid>https://velog.io/@dan_d/2024-11-13-%ED%95%99%EC%8A%B5-%EA%B8%B0%EB%A1%9D</guid>
            <pubDate>Wed, 13 Nov 2024 08:38:19 GMT</pubDate>
            <description><![CDATA[<h2 id="notion-수학-기호-단축키">Notion 수학 기호 단축키</h2>
<p>command + shift + E</p>
<h2 id="latex">LaTex</h2>
<p>\subset: $\subset$</p>
<p>\supset: $\supset$</p>
<p>\leq: $\leq$</p>
<p>\geq: $\geq$</p>
<p>\sqrt{x}: $\sqrt{x}$</p>
<p>e^{x}: $e^{x}$</p>
<h2 id="미분법의-응용">미분법의 응용</h2>
<p>임계점 $\supset$ 극값</p>
<p>열린구간 (0, 1) 0 &lt; x &lt; 1</p>
<p>닫힌구간 [0,1] 0 $\leq$ x $\leq$ 1 </p>
<p>닫힌구간일 때 최대, 최솟값 찾는 알고리즘은 쉽다.</p>
<p>임계점만 구해서 그 중에서 알아내면 된다.</p>
<p><strong>최대-최소 정리 1</strong></p>
<p>닫힌 구간 [a,b]에서 정의된 연속 함수는 반드시 최댓값과 최솟값을 가진다.</p>
<p>이 최댓값과 최솟값은 임계점이나 구간의 끝점 a와 b에서 발생할 수 있다.</p>
<p><strong>최대-최소 정리 2</strong></p>
<p>열린구간에서 임계점이 유일한 경우, 극솟값이면 최솟값이고 극댓값이면 최댓값이다.</p>
<p>$dy=f′(a)⋅dx$, $Δy=f(a+Δx)−f(a)$</p>
<h3 id="근사-관계">근사 관계</h3>
<p>$Δy≈dy$</p>
<p>이 관계는 $Δx$<strong>가 아주 작을 때</strong> 성립합니다. $Δx$가 매우 작아지면, 함수의 변화량 $Δy$와 접선의 변화량 $dy$가 거의 동일하게 되며, 따라서 <strong>함수의 곡선이 직선(접선)에 의해 근사될 수 있다는 것</strong>을 의미합니다.</p>
<h3 id="요약">요약</h3>
<p>$Δy$는 실제 변화량이고, $dy$는 근사적 변화량이지만, $Δx$가 작을수록 두 값이 비슷해집니다.</p>
<p>Δy를 구하는 것보다 Δx 충분히 작을 때 dy를 구해서 Δy를 계산하면 거의 비슷하다.</p>
<p>어려운 계산을 손으로도 쉽게 풀 수 있다.</p>
<p>ex) $\sqrt{3.998}, e^{0.001}$ dx를 아주 작은 값, a를 계산하기 편한 값</p>
<p>미분이 가능하다면 복잡한 곡선을 단순한 선형으로 볼 수 있다는 것이고 기울기를 통해 변수들 간의 상관관계를 알 수 있다.  x의 변화량에 따른 y를 예측이 가능하다는 것이다.</p>
<p>인생은 선택의 연속이며, 우리는 매 순간 다양한 길 앞에 서게 된다. 선택마다의 결과가 우리의 길을 조금씩 다르게 만든다.</p>
<p>머신러닝도 마찬가지다. 수많은 선택지 속에서 가능성을 탐색하며, 그 결과들을 끊임없이 학습한다. 마치 인생의 길을 걷듯, 최종 목표에 다가가기 위해 최선의 길을 찾아가는 과정이다. 어마어마한 갈림길 속에서, 각각의 선택을 선형 근사로 평가하며, 가장 바람직한 방향으로 나아가는 것.</p>
<p>머신러닝은 이토록 방대한 가능성의 바다 속에서도, 최적의 길을 걸어 원하는 목표에 도달하려는 여정이다.</p>
<p>머신러닝은 마치 목적지로 가는 가장 빠르고 효율적인 지도를 만들어가는 과정과 같다는 생각이 든다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 7기] 4주차 편의점 회고]]></title>
            <link>https://velog.io/@dan_d/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-4%EC%A3%BC%EC%B0%A8-%ED%8E%B8%EC%9D%98%EC%A0%90-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dan_d/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-4%EC%A3%BC%EC%B0%A8-%ED%8E%B8%EC%9D%98%EC%A0%90-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Wed, 13 Nov 2024 03:10:53 GMT</pubDate>
            <description><![CDATA[<h1 id="서론">서론</h1>
<p><strong>1, 2, 3주차 미션과 달랐다.</strong></p>
<p>부족한 능력으로 제대로 구현하지 못하고 제출하게 됐다.</p>
<p>이전 미션 과제를 진행하면서 우아한테크코스에서 제공한 <strong>학습 목표와 공통 피드백</strong>을 <strong>어떠한 방법</strong>으로 <strong>소화</strong>했는가에 따라 이번 미션의 결과에 큰 영향을 미쳤을 것이라고 개인적으로 생각한다.</p>
<p><strong>미션의 목표</strong></p>
<blockquote>
<p>편의점 결제 시스템을 구현한다.</p>
</blockquote>
<p><strong>할인 혜택</strong>이 있고 재고를 고려해야 한다. 
할인 혜택에는 프로모션 증정 할인이 있고 멤버십 적용 할인이 있다. 
<strong>재고 관리</strong>는 초기에 제공된 Resource 파일을 이용해서 등록해야 한다. 
일반, 프로모션 상품을 각각 차감하고 업데이트할 수 있어야 한다. 
결제를 진행할 때 조건에 따라 안내해야 한다. 
프로모션 적용과 멤버십 적용에 따른 안내가 필요하고 최종적으로 계산하여 영수증을 제공하는 시스템이 목표다.</p>
<p>한 주 동안 미션을 통해 경험한 것들을 아래의 내용에 설명하려 한다.</p>
<h1 id="본론">본론</h1>
<h2 id="과제-진행-요구-사항">과제 진행 요구 사항</h2>
<p>이전 미션과 같이 나의 원격 저장소에 생성을 하면서 시작된다.</p>
<p><strong>기능 구현을 하기 전 README에 구현할 기능 목록을 정리해야 한다.</strong></p>
<ul>
<li>한 번에 완벽하게 하려는 생각보다는 수정을 통해 조금씩 나아가며 완성한다는 마음으로 시작하는 것이 중요한 것 같다.</li>
<li>처음 보는 다른 사람이 리드미를 보고 프로젝트에 대해 이해하고 똑같은 환경에서 시작할 수 있는 것이 중요한 것 같다.</li>
</ul>
<p><strong>AngularJS Git Commit Message Conventions을 참고하여 진행해야 한다.</strong></p>
<ul>
<li>버전 관리가 왜 중요한지 이번 과제를 통해 중요성을 느꼈다. 나의 생각과 달라지는 현상이 발생할 때 돌아갈 수 있느냐 없느냐, 어디로 돌아갈 수 있는가에 대한 소중함과 후회를 다양하게 겪을 수 있었다.</li>
</ul>
<h2 id="기능-요구-사항">기능 요구 사항</h2>
<p>재고 상황을 고려해서 구매자가 상품을 장바구니에 담을 수 있고 결제를 진행할 때 할인 혜택을 적용할 수 있다. 구매자는 영수증을 통해 정보를 획득할 수 있다.</p>
<p>미션을 진행하기 전 계획이 있었다.</p>
<p>기능 명세를 문장으로 작성하고 책임을 작은 단위로 계속 나눈다. 
단일 책임을 지닌 개념을 식별해서 객체를 할당한다. 
책임을 수행하기 위한 행동을 정의한다. 
행동할 때 필요한 상태를 가지게 한다. 
쉬운 도메인 객체부터 테스트 및 구현한다. 
테스트는 Red, Green, Refactor의 과정을 진행한다. 
입출력 예시를 보고 시나리오, 워크플로우를 작성한다. 
객체 메시지를 통해 서비스와 컨트롤러를 구축한다. 
도메인 개념은 객체지향적 설계를 하고 서비스와 컨트롤러는 절차지향적 설계를 한다. 
서비스는 비즈니스 로직을 다룬다. 
컨트롤러는 사용자의 인터페이스 입력 처리(서비스) 출력만을 다룬다.</p>
<blockquote>
<p>&quot;누구나 그럴싸한 계획을 갖고 있다. 쳐맞기 전까지는.&quot;</p>
</blockquote>
<p>미션이 시작됐고 계획대로 진행을 시도했다. 하지만 구체적으로 정의하는 것들이 어려웠다. 단일 책임, 작은 단위 테스트 설정, 비즈니스 로직 정리 등 적고 수정하는 것을 반복했다.</p>
<p>시작하기 전에 공통 피드백을 봤고 동영상처럼 하고 싶어서 따라 해봤다. 기능 명세 문장에서 작은 테스트로 정리하는 것이 생각처럼 잘되지 않아 어려움을 겪었다.</p>
<p>상품에 가격이라는 필드를 설정하고 상품별 총구매액을 계산하는 행위를 하다가 서비스에 연산을 옮기고 상품에서는 getter만을 사용했다가 상태를 가지는데 행위가 없는 것을 이상하게 생각하고 바꾸는 등 이랬다 저랬다하는 혼란을 다양한 부분에서 느꼈다.</p>
<p>변경에 초점을 두고 설계를 하자는 등 설계에 신경을 쓴 탓에 잦은 설계 변경이 일어났고, 오히려 그것이 패착을 부른 것 같아 아쉽게 생각한다. 아래는 제출하기 전 나의 프로젝트 구조 모습이다.</p>
<h3 id="프로젝트-구조">프로젝트 구조</h3>
<p><img src="https://velog.velcdn.com/images/dan_d/post/29c3e8c0-d14d-48ed-88de-2d2071833590/image.png" alt=""></p>
<p>상품, 장바구니, 프로모션 할인, 멤버십 할인, 인벤토리(재고), 영수증에 대해서 많이 고민했다.</p>
<p><strong>보유한 상품 정보 출력</strong>
&lt;계획&gt;
리소스 파일에 있는 정보를 가져와 상품과 프로모션 레포지토리에 저장하려 했고 레포지토리는 CRUD에 집중하려고 했다. 상품 레포지토리에서 일반, 프로모션 각각을 나눠서 다루려고 했다.</p>
<p>&lt;실행 결과&gt;
보유 상품을 순서대로 출력해야 하는데 각각을 따로 관리하려다 보니 출력이 원하는 순서대로 나오지 않았다. 데이터로더를 통해서 레포지토리에 저장하고 원하는 순서로 출력하는 것이 어려웠다. 입출력 예시를 보면서 뒤늦게 프로모션 상품이 있으면 일반 재고가 없더라도 재고 없음으로 출력하고 프로모션 상품이 없을 때만 일반 재고를 출력하는 것을 늦게 알아서 데이터로더와 상품 레포지토리를 자주 고쳤다.</p>
<pre><code class="language-java">    private void addProduct(List&lt;Product&gt; products, String name, int price, int quantity, String promotionType) {
        if (promotionType != null) {
            Product promoProduct = new Product(name, price, quantity, promotionType);
            products.add(promoProduct);

            Product regularProduct = new Product(name, price, 0, null);
            products.add(regularProduct);
            return;
        }
        Product regularProduct = new Product(name, price, quantity, null);
        products.add(regularProduct);
    }</code></pre>
<p>위와 같은 방법으로 프로모션과 일반 상품 모두를 표시하도록 했고 레포지토리에 순서대로 입력된 원본 리스트를 가지도록 하였다.</p>
<p>Promotion, Regular Repository를 각각 만들고 ProductService를 통해서 재고 관리를 하려고 했는데 실패했다.</p>
<p>총재고를 기존의 ProductRepository에서 저장해 놓은 각각의 레포지토리에서 가져와 더하는 메소드를 따로 만드는 방법을 이용했다.</p>
<p>보유 상품 출력에 관해서도 Product, ProductDTO, ProductRepository, ProductService, Controller, outputView의 도움을 받아서 출력했다.</p>
<p><strong>구매할 상품 정보 입력</strong>
&lt;계획&gt;
Cart 객체를 만들어 각 상품명의 수량을 전달하는 목적으로 만들려고 했다.</p>
<p>미션 초기에 상품에서 상품별 총구매액을 계산하고 장바구니에서 장바구니 총 구매액을 계산하는 시도도 했다가 CheckoutService에서 금액 계산에 대한 책임을 넘겼다.</p>
<p>상품을 입력할 때 Repository에 존재하는 상품인지 수량도 함께 검증하도록 계획했다.</p>
<p>&lt;실행 결과&gt;
ProductService에서 Repository 정보를 이용했다. Cart 클래스를 만들고 CartService를 사용했다. 
CartService에서 Repository에서 바로 정보를 가져와 장바구니 역할을 맡게 하다 보니 Cart 클래스를 사용하지 않게 되어서 최종적으로는 Cart 클래스를 지우게 됐다.</p>
<p><strong>프로모션 할인</strong>
&lt;계획&gt;
프로모션의 정보는 PromotionRepository에 생성한다. 프로모션 객체는 프로모션의 기간, 증정품 계산, 검증을 맡겼다.</p>
<p>&lt;실행 결과&gt;
PromotionService에서 Promotion 클래스의 메소드를 이용하여 기간이 해당하는지 증정품 계산과 검증을 이용했다. PromotionResult라는 클래스를 만들어 프로모션 정보를 관리하도록 했다.</p>
<p>프로모션의 재고가 충분하고 2+1일 경우, 고객이 2개만 가져왔을 때 프로모션 적용으로 하나를 추가하거나 총재고에는 들어가나 프로모션의 재고가 부족할 경우 정가로 결제할 것인지 안내를 하는 기능 구현에는 실패했다.</p>
<p>초기에 기능 요구 사항을 잘못 이해한 부분이 있어서 나중에 고치다가 정작 기능 요구 사항을 마무리하지 못하였다. 
아쉬운 마음이 많이 남는다. 가장 중요한 기능인데 허술하게 했다.</p>
<p><strong>멤버십 할인</strong>
&lt;계획&gt;
프로모션 미적용 상품에 대해서 멤버십 할인 여부에 따라 진행한다.</p>
<p>&lt;실행 결과&gt;
처음에는 가격으로 접근했다. 
총 구매액을 구하고 프로모션 할인 금액을 구하고 그것을 차감한 금액에 멤버십을 적용했다. 
하지만 프로모션 적용에 해당하는 품목은 모두 프로모션 적용인 것을 잘못 생각했다. 
그 결과 새롭게 수정하는 과정을 거쳤다.</p>
<p><strong>계산대 최종 금액 계산</strong>
&lt;계획&gt;
장바구니 상품 별 프로모션 할인을 체크하고 멤버십 체크 후 최종 결제 금액을 계산하여 영수증 생성에 필요한 정보를 넘기는 것으로 설계했다.</p>
<p>&lt;실행 결과&gt;
재고 관리와 프로모션 적용이 미흡하여 최종 결제 금액과 영수증 출력에도 영향을 미쳤다.</p>
<p><strong>영수증 출력</strong>
&lt;계획&gt;
CheckoutService에서 생성된 구매 내역, 증정 상품 내역, 금액 정보를 출력할 계획이었다.</p>
<p>&lt;실행 결과&gt;
실행 예시의 형태와 완전히 일치해야 하는 줄 알았다. 
공백까지 맞추려고 노력했는데, 그것보다 올바른 정보와 값에 집중할 걸 그랬다. 
프로모션 적용을 거치면서 상품 총 구매액과 총 수량 등 값이 올바르게 전달되지 않아 아쉽게 생각한다. 
마음이 너무 급했던 것 같다. 
테스트 주도 개발과 디버깅을 통해 단단하게 구축했으면 좋았을 것 같다.</p>
<p><strong>추가 구매 여부</strong>
&lt;계획&gt;
구매 결과에 대한 ProductRepository 재고 업데이트를 한다. 추가 구매 여부에 대한 질문을 하고 &#39;Y&#39;를 입력할 시 처음부터 돌아가서 반복한다.</p>
<p>&lt;실행 결과&gt;
ProductRepository에 대한 업데이트는 결제할 영수증에 넘길 정보를 계산하는 과정의 시작 부분에서 차감하도록 구현했다.</p>
<p>checkout이라는 메소드에서 영수증 객체에 데이터를 넘겨서 영수증 객체를 반환하도록 했다.</p>
<h1 id="결론">결론</h1>
<h2 id="아쉬운-점">아쉬운 점</h2>
<p>모든 것이 아쉽다.</p>
<p><strong>클래스 상속을 이용한 상품 설계</strong></p>
<p>Product, PromotionProduct 상속을 이용하기로 했는데 장바구니를 Map을 이용하여 관리하는 것에서 문제가 발생했다.
equals, hashcode, Map에 대한 이해가 부족하여 제대로 활용하지 못한 점이 아쉽다.
무한 루프에서 빠져나오지 못하여 결국 새롭게 다 엎고 Product만을 사용하는 설계로 변경했다.</p>
<p>위의 내용에 대해서는 추가적으로 공부하여 새로운 글로 올리도록 해야겠다.</p>
<p><strong>테스트 주도 개발</strong>
도메인과 레포지토리, 특정 서비스까지 실패할 테스트를 선 작성하고 통과하게 하고 리팩토링하는 과정을 거쳤다.
여러 가지 문제가 발생했고, 시간에 대한 멘탈적 압박으로 인해 구현에만 집중했고, 그로 인해 버전 관리 및 테스트가 망가졌다.
커밋에 대해서도 보여진다는 생각으로 좀 더 완성하고 커밋하려다가 돌아갈 포인트를 놓치는 경험도 많이 했다.
설계가 자주 변경되면서 특정 테스트만 실행하려 해도 이전 테스트들의 에러로 인해 진행하지 못하여 @Disable을 이용하기도 했고, 그 또한 먹히지 않아 해당 테스트들을 다시 잡는 과정을 여러 번 하다 보니 그 당시에는 방해로 느껴졌다.
마음이 최종적으로 너무 급해졌고 테스트를 족족 삭제해버리는 과정을 거쳤다.</p>
<p>지금 돌이켜보면 버전 관리와 테스트들을 잃고 나니 기능 구현이 더 갈피를 잡지 못했던 것 같다.
기능 구현을 못하더라도 그동안 배운 것들을 실천하는 것이 더 중요한 것이 아니었나? 
내가 출제자라면 과연 요구 사항을 열심히 지키면서 구현을 다 못한 코드와 수단과 방법을 가리지 않고 테스트 통과에만 급급한 코드 중 무엇을 좋게 바라볼 것인지를 생각해보니 후회가 된다.
기본 도메인의 기능을 탄탄히 하고 비즈니스 로직에 대한 탄탄함을 쌓아야 하는 점에 대해 깊은 후회를 느낀다.</p>
<p>테스트와 디버깅에 대해서 추가적으로 많이 공부하고 실전 연습을 쌓아야겠다.
생각의 속도를 구현으로 따라가지 못하는 것이 지금의 답답한 심정이다.</p>
<p><strong>생성형 인공지능</strong></p>
<p>개발을 시작한 지 얼마 되지 않았고, 그동안 인공지능의 도움을 많이 받아왔다. 
이번 프리코스 과정에서도 AI에게 의존하는 부분이 많았다. 그 결과가 이번 미션의 참패라는 것을 깨달았다. 
사실 이전 미션에서도 계속 느끼고 있었다. 
이렇게 의존하면 안 된다고. 
의존 없이 진행하다가 주말까지 몰리면 결국 다시 꺼내게 됐다. 아직 나의 역량이 그만큼 모자란 것 같다.</p>
<p>이번에 AI와 다툼을 하며 디테일한 문제에 대해 몇 시간을 해결하지 못한 것도 직접 잠깐의 디버깅으로 해결되는 일을 겪었다. 
그때라도 직접 디버깅을 계속했었어야 했는데, 급한 마음에 그렇게 하지 못했다. 
그 점이 후회된다.</p>
<p>예전 수학 관련 문제에서도 같은 현상을 겪어서 항상 의심하는 것을 생각했지만, 지난 미션 동안 계속 사용했던 것이 쌓여 나의 역량을 부족하게 했고, 이번 미션에서 오히려 시간 낭비를 불러오고 기존 설계가 엉망이 되는 것을 느꼈다. 
그런 도구를 사용하는 것도 기본이 갖춰져야 한다는 것을...</p>
<p>앞으로 AI 사용을 최소화하고 직접 다시 구현하는 작업을 진행할 것이다. 
미션을 다시 진행하면서 필요한 문법들 하나하나를 책과 구글링을 통해 배우는 과정을 겪어보려 한다. 
이번 동일성, 동등성 등 개념 문제도 구글링을 통해 직접 깊이 이해하고 올바른 질문을 했다면 달랐을 것 같다. 
인공지능을 사용하면 좋은 부분과 아닌 부분에 대한 경계점을 더욱 명확하게 인식하게 된 것 같다.</p>
<h2 id="마무리">마무리</h2>
<p>4주차까지 프리코스 과정을 거치면서 시간의 소중함을 더욱 절실히 느꼈다. 
원하는 것을 이루기 위해서 다른 무언가를 포기해야 했는데 담배를 끊었었다. 
이번 프리코스 기간을 통해서는 술을 끊게 됐다. 시간의 소중함을 느꼈기 때문이다.</p>
<p>이번 프리코스를 통해 객체지향과 여러 가지를 배우면서 읽고 싶고 배우고 싶은 지식들이 아주 많이 생겼다. 
읽고 배우며 공유하면서 내 지식으로 단단하게 만드는 과정을 겪자. 
읽고 배우고 그것이 내가 아는 것인 듯 넘기지 말고, 공유하면서 진짜 나의 지식인지 확인하는 과정을 거치자.</p>
<p>늦은 시작이지만 앞으로의 나의 인생을 개발자로 사는 것에 올인하고 싶다. 
프리코스 과정을 거치면서 마음이 더 커졌다. 앞으로도 나의 의지는 강해지고 꺾일 일이 없다고 자부한다고 생각한다. 
다만 건강, 신체가 튼튼하고 강건해야 정신도 곧게 설 수 있다고 프리코스 과정에서 느꼈다. 
목이 아프고 눈이 아프고 이곳저곳이 아프다. 
오랫동안 꾸준히 성장하고 싶다면 건강 관리를 잘해야겠다고 느꼈다. 건강이 무엇보다 우선이다!</p>
<blockquote>
<p>혹시 방문하여 긴 글을 읽어주셨다면 감사합니다. 
여러분도 다른 건 모르겠지만 꼭 건강 관리하시길! 
아프지 마시길 바랍니다 😊</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[4주차 미션 상세 가이드]]></title>
            <link>https://velog.io/@dan_d/4%EC%A3%BC%EC%B0%A8-%EB%AF%B8%EC%85%98-%EC%83%81%EC%84%B8-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
            <guid>https://velog.io/@dan_d/4%EC%A3%BC%EC%B0%A8-%EB%AF%B8%EC%85%98-%EC%83%81%EC%84%B8-%EA%B0%80%EC%9D%B4%EB%93%9C</guid>
            <pubDate>Tue, 05 Nov 2024 15:52:05 GMT</pubDate>
            <description><![CDATA[<h1 id="목표">목표</h1>
<hr>
<h3 id="학습-목표">학습 목표</h3>
<ul>
<li>관련 함수를 묶어 클래스를 만들고, 객체들이 협력하여 하나의 큰 기능을 수행하도록 한다.</li>
<li>클래스와 함수에 대한 단위 테스트를 통해 의도한 대로 정확하게 작동하는 영역을 확보한다.</li>
<li><a href="https://docs.google.com/document/d/1MsfVKpgiDyhq6ArbTwsf9EEqDBD85vyt9Oj25i0zkEM/edit?usp=sharing">3주 차 공통 피드백</a>을 최대한 반영한다.</li>
<li><a href="https://docs.google.com/document/d/1cmg0VpPkuvdaetxwp4hnyyFC_G-1f2Gr8nIDYIWcKC8/edit?usp=sharing">비공개 저장소 과제 진행 가이드</a>를 참고하여 새로운 방식으로 과제 제출물을 제출한다.</li>
</ul>
<h3 id="회고">회고</h3>
<p>아래 질문에 대한 최종 회고를 진행하고 소감에 구체적인 결과를 작성한다. 소감은 텍스트로 작성해야 하며 외부 링크는 허용하지 않는다.</p>
<ul>
<li>지원서나 중간 회고에서 현실적인 목표를 설정하고 이를 달성했다고 생각하나요? 그 이유는 무엇인가요?</li>
<li>중간 회고에서 조정한 목표가 실제 목표 달성에 도움이 되었나요? 목표를 달성하는 데 어떤 점이 효과적이었다고 생각하나요?</li>
<li>각 미션의 목표를 달성하기 위해 세운 계획을 잘 이행했나요? 그 과정에서 어떤 전략이 효과가 있었나요?</li>
<li>몰입하고 함께 성장하는 과정을 통해 인상 깊었던 경험이나 변화가 있었나요?</li>
</ul>
<hr>
<h2 id="프리코스-진행-방식">프리코스 진행 방식</h2>
<h3 id="진행-방식">진행 방식</h3>
<ul>
<li>미션은 <strong>과제 진행 요구 사항</strong>, <strong>기능 요구 사항</strong>, <strong>프로그래밍 요구 사항</strong> 세 가지로 구성되어 있다.</li>
<li>세 개의 요구 사항을 만족하기 위해 노력한다. 특히 기능을 구현하기 전에 기능 목록을 만들고, 기능 단위로 커밋 하는 방식으로 진행한다.</li>
<li><strong>기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다.</strong></li>
<li>매주 진행할 미션은 화요일 오후 3시부터 확인할 수 있으며, 다음 주 월요일까지 구현을 완료하여 제출해야 한다. <strong>제출은 일요일 오후 3시부터 가능하다.</strong><ul>
<li><strong>정해진 시간을 지키지 않을 경우 미션을 제출하지 않은 것으로 간주한다.</strong></li>
<li><strong>정해진 시간 내에 우아한테크코스 계정을 협력자로 초대하지 않으면 제출하지 않은 것으로 간주한다.</strong></li>
<li>종료 일시 이후에는 추가 푸시를 허용하지 않는다.</li>
</ul>
</li>
</ul>
<h3 id="미션-제출-방법">미션 제출 방법</h3>
<ul>
<li>미션 구현을 완료한 후 GitHub을 통해 제출해야 한다.<ul>
<li>GitHub을 활용한 제출 방법은 <a href="https://docs.google.com/document/d/1cmg0VpPkuvdaetxwp4hnyyFC_G-1f2Gr8nIDYIWcKC8/edit?usp=sharing">프리코스 과제 제출</a> 문서를 참고해 제출한다.</li>
</ul>
</li>
<li>GitHub에 미션을 제출한 후 <a href="https://apply.techcourse.co.kr">우아한테크코스 지원 플랫폼</a>에서 과제 제출물을 최종 제출한다.<ul>
<li>자세한 안내는 <a href="https://github.com/woowacourse/woowacourse-docs/tree/master/precourse#%EC%A0%9C%EC%B6%9C-%EA%B0%80%EC%9D%B4%EB%93%9C">제출 가이드</a>를 참고한다.</li>
<li>과제를 수행하면서 느낀 점, 배운 점, 많은 시간을 투자한 부분 등 자유롭게 작성한다.</li>
</ul>
</li>
</ul>
<h3 id="과제-제출-전-체크-리스트">과제 제출 전 체크 리스트</h3>
<ul>
<li>기능을 올바르게 구현했더라도 <strong>요구 사항에 명시된 출력 형식을 따르지 않으면 0점</strong>을 받게 된다.</li>
<li>기능 구현을 완료한 후 아래 가이드에 따라 모든 테스트가 성공적으로 실행되는지 확인한다.</li>
<li><strong>테스트가 실패하면 점수가 0점</strong>이 되므로 제출하기 전에 반드시 확인한다.</li>
</ul>
<h4 id="테스트-실행-가이드">테스트 실행 가이드</h4>
<ul>
<li>터미널에서 <code>java -version</code>을 실행하여 Java 버전이 21인지 확인한다. Eclipse 또는 IntelliJ IDEA와 같은 IDE에서 Java 21로 실행되는지 확인한다.</li>
<li>터미널에서 Mac 또는 Linux 사용자의 경우 <code>./gradlew clean test</code> 명령을 실행하고, Windows 사용자의 경우 <code>gradlew.bat clean test</code> 또는 <code>.\gradlew.bat clean test</code> 명령을 실행할 때 모든 테스트가 아래와 같이 통과하는지 확인한다.</li>
</ul>
<pre><code>BUILD SUCCESSFUL in 0s
</code></pre><hr>
<h1 id="편의점">편의점</h1>
<h2 id="과제-진행-요구-사항">과제 진행 요구 사항</h2>
<ul>
<li>미션은 <a href="https://github.com/woowacourse-precourse/java-convenience-store-7">편의점</a> 저장소를 생성하는 것으로 시작한다.</li>
<li><strong>기능을 구현하기 전 <code>README.md</code>에 구현할 기능 목록을 정리</strong>해 추가한다.</li>
<li>Git의 커밋 단위는 앞 단계에서 <code>README.md</code>에 정리한 기능 목록 단위로 추가한다.<ul>
<li><a href="https://gist.github.com/stephenparish/9941e89d80e2bc58a153">AngularJS Git Commit Message Conventions</a>을 참고해 커밋
메시지를 작성한다.</li>
</ul>
</li>
<li>자세한 과제 진행 방법은 프리코스 진행 가이드 문서를 참고한다.</li>
</ul>
<h2 id="기능-요구-사항">기능 요구 사항</h2>
<p>구매자의 할인 혜택과 재고 상황을 고려하여 최종 결제 금액을 계산하고 안내하는 결제 시스템을 구현한다.</p>
<ul>
<li>사용자가 입력한 상품의 가격과 수량을 기반으로 최종 결제 금액을 계산한다.<ul>
<li>총구매액은 상품별 가격과 수량을 곱하여 계산하며, 프로모션 및 멤버십 할인 정책을 반영하여 최종 결제 금액을 산출한다.</li>
</ul>
</li>
<li>구매 내역과 산출한 금액 정보를 영수증으로 출력한다.</li>
<li>영수증 출력 후 추가 구매를 진행할지 또는 종료할지를 선택할 수 있다.</li>
<li>사용자가 잘못된 값을 입력할 경우 <code>IllegalArgumentException</code>를 발생시키고, &quot;[ERROR]&quot;로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다.<ul>
<li><code>Exception</code>이 아닌 <code>IllegalArgumentException</code>, <code>IllegalStateException</code> 등과 같은 명확한 유형을 처리한다.</li>
</ul>
</li>
</ul>
<h4 id="재고-관리">재고 관리</h4>
<ul>
<li>각 상품의 재고 수량을 고려하여 결제 가능 여부를 확인한다.</li>
<li>고객이 상품을 구매할 때마다, 결제된 수량만큼 해당 상품의 재고에서 차감하여 수량을 관리한다.</li>
<li>재고를 차감함으로써 시스템은 최신 재고 상태를 유지하며, 다음 고객이 구매할 때 정확한 재고 정보를 제공한다.</li>
</ul>
<h4 id="프로모션-할인">프로모션 할인</h4>
<ul>
<li>오늘 날짜가 프로모션 기간 내에 포함된 경우에만 할인을 적용한다.</li>
<li>프로모션은 N개 구매 시 1개 무료 증정(Buy N Get 1 Free)의 형태로 진행된다.</li>
<li>1+1 또는 2+1 프로모션이 각각 지정된 상품에 적용되며, 동일 상품에 여러 프로모션이 적용되지 않는다.</li>
<li>프로모션 혜택은 프로모션 재고 내에서만 적용할 수 있다.</li>
<li>프로모션 기간 중이라면 프로모션 재고를 우선적으로 차감하며, 프로모션 재고가 부족할 경우에는 일반 재고를 사용한다.</li>
<li>프로모션 적용이 가능한 상품에 대해 고객이 해당 수량보다 적게 가져온 경우, 필요한 수량을 추가로 가져오면 혜택을 받을 수 있음을 안내한다.</li>
<li>프로모션 재고가 부족하여 일부 수량을 프로모션 혜택 없이 결제해야 하는 경우, 일부 수량에 대해 정가로 결제하게 됨을 안내한다.</li>
</ul>
<h4 id="멤버십-할인">멤버십 할인</h4>
<ul>
<li>멤버십 회원은 프로모션 미적용 금액의 30%를 할인받는다.</li>
<li>프로모션 적용 후 남은 금액에 대해 멤버십 할인을 적용한다.</li>
<li>멤버십 할인의 최대 한도는 8,000원이다.</li>
</ul>
<h4 id="영수증-출력">영수증 출력</h4>
<ul>
<li>영수증은 고객의 구매 내역과 할인을 요약하여 출력한다.</li>
<li>영수증 항목은 아래와 같다.<ul>
<li>구매 상품 내역: 구매한 상품명, 수량, 가격</li>
<li>증정 상품 내역: 프로모션에 따라 무료로 제공된 증정 상품의 목록</li>
<li>금액 정보<ul>
<li>총구매액: 구매한 상품의 총 수량과 총 금액</li>
<li>행사할인: 프로모션에 의해 할인된 금액</li>
<li>멤버십할인: 멤버십에 의해 추가로 할인된 금액</li>
<li>내실돈: 최종 결제 금액</li>
</ul>
</li>
</ul>
</li>
<li>영수증의 구성 요소를 보기 좋게 정렬하여 고객이 쉽게 금액과 수량을 확인할 수 있게 한다.</li>
</ul>
<h3 id="입출력-요구-사항">입출력 요구 사항</h3>
<h4 id="입력">입력</h4>
<ul>
<li>구현에 필요한 상품 목록과 행사 목록을 파일 입출력을 통해 불러온다.<ul>
<li><code>src/main/resources/products.md</code>과 <code>src/main/resources/promotions.md</code> 파일을 이용한다.</li>
<li>두 파일 모두 내용의 형식을 유지한다면 값은 수정할 수 있다.</li>
</ul>
</li>
<li>구매할 상품과 수량을 입력 받는다. 상품명, 수량은 하이픈(-)으로, 개별 상품은 대괄호([])로 묶어 쉼표(,)로 구분한다.</li>
</ul>
<pre><code>[콜라-10],[사이다-3]
</code></pre><ul>
<li>프로모션 적용이 가능한 상품에 대해 고객이 해당 수량보다 적게 가져온 경우, 그 수량만큼 추가 여부를 입력받는다.<ul>
<li>Y: 증정 받을 수 있는 상품을 추가한다.</li>
<li>N: 증정 받을 수 있는 상품을 추가하지 않는다.</li>
</ul>
</li>
</ul>
<pre><code>Y
</code></pre><ul>
<li>프로모션 재고가 부족하여 일부 수량을 프로모션 혜택 없이 결제해야 하는 경우, 일부 수량에 대해 정가로 결제할지 여부를 입력받는다.<ul>
<li>Y: 일부 수량에 대해 정가로 결제한다.</li>
<li>N: 정가로 결제해야하는 수량만큼 제외한 후 결제를 진행한다.</li>
</ul>
</li>
</ul>
<pre><code>Y
</code></pre><ul>
<li>멤버십 할인 적용 여부를 입력 받는다.<ul>
<li>Y: 멤버십 할인을 적용한다.</li>
<li>N: 멤버십 할인을 적용하지 않는다.</li>
</ul>
</li>
</ul>
<pre><code>Y
</code></pre><ul>
<li>추가 구매 여부를 입력 받는다.<ul>
<li>Y: 재고가 업데이트된 상품 목록을 확인 후 추가로 구매를 진행한다.</li>
<li>N: 구매를 종료한다.</li>
</ul>
</li>
</ul>
<pre><code>Y
</code></pre><h4 id="출력">출력</h4>
<ul>
<li>환영 인사와 함께 상품명, 가격, 프로모션 이름, 재고를 안내한다. 만약 재고가 0개라면 <code>재고 없음</code>을 출력한다.</li>
</ul>
<pre><code>안녕하세요. W편의점입니다.
현재 보유하고 있는 상품입니다.

- 콜라 1,000원 10개 탄산2+1
- 콜라 1,000원 10개
- 사이다 1,000원 8개 탄산2+1
- 사이다 1,000원 7개
- 오렌지주스 1,800원 9개 MD추천상품
- 오렌지주스 1,800원 재고 없음
- 탄산수 1,200원 5개 탄산2+1
- 탄산수 1,200원 재고 없음
- 물 500원 10개
- 비타민워터 1,500원 6개
- 감자칩 1,500원 5개 반짝할인
- 감자칩 1,500원 5개
- 초코바 1,200원 5개 MD추천상품
- 초코바 1,200원 5개
- 에너지바 2,000원 5개
- 정식도시락 6,400원 8개
- 컵라면 1,700원 1개 MD추천상품
- 컵라면 1,700원 10개

구매하실 상품명과 수량을 입력해 주세요. (예: [사이다-2],[감자칩-1])
</code></pre><ul>
<li>프로모션 적용이 가능한 상품에 대해 고객이 해당 수량만큼 가져오지 않았을 경우, 혜택에 대한 안내 메시지를 출력한다.</li>
</ul>
<pre><code>현재 {상품명}은(는) 1개를 무료로 더 받을 수 있습니다. 추가하시겠습니까? (Y/N)
</code></pre><ul>
<li>프로모션 재고가 부족하여 일부 수량을 프로모션 혜택 없이 결제해야 하는 경우, 일부 수량에 대해 정가로 결제할지 여부에 대한 안내 메시지를 출력한다.</li>
</ul>
<pre><code>현재 {상품명} {수량}개는 프로모션 할인이 적용되지 않습니다. 그래도 구매하시겠습니까? (Y/N)
</code></pre><ul>
<li>멤버십 할인 적용 여부를 확인하기 위해 안내 문구를 출력한다.</li>
</ul>
<pre><code>멤버십 할인을 받으시겠습니까? (Y/N)
</code></pre><ul>
<li>구매 상품 내역, 증정 상품 내역, 금액 정보를 출력한다.</li>
</ul>
<pre><code>===========W 편의점=============
상품명        수량    금액
콜라        3     3,000
에너지바         5     10,000
===========증    정=============
콜라        1
==============================
총구매액        8    13,000
행사할인            -1,000
멤버십할인            -3,000
내실돈             9,000
</code></pre><ul>
<li>추가 구매 여부를 확인하기 위해 안내 문구를 출력한다.</li>
</ul>
<pre><code>감사합니다. 구매하고 싶은 다른 상품이 있나요? (Y/N)
</code></pre><ul>
<li>사용자가 잘못된 값을 입력했을 때, &quot;[ERROR]&quot;로 시작하는 오류 메시지와 함께 상황에 맞는 안내를 출력한다.<ul>
<li>구매할 상품과 수량 형식이 올바르지 않은 경우: <code>[ERROR] 올바르지 않은 형식으로 입력했습니다. 다시 입력해 주세요.</code></li>
<li>존재하지 않는 상품을 입력한 경우: <code>[ERROR] 존재하지 않는 상품입니다. 다시 입력해 주세요.</code></li>
<li>구매 수량이 재고 수량을 초과한 경우: <code>[ERROR] 재고 수량을 초과하여 구매할 수 없습니다. 다시 입력해 주세요.</code></li>
<li>기타 잘못된 입력의 경우: <code>[ERROR] 잘못된 입력입니다. 다시 입력해 주세요.</code></li>
</ul>
</li>
</ul>
<h4 id="실행-결과-예시">실행 결과 예시</h4>
<pre><code>안녕하세요. W편의점입니다.
현재 보유하고 있는 상품입니다.

- 콜라 1,000원 10개 탄산2+1
- 콜라 1,000원 10개
- 사이다 1,000원 8개 탄산2+1
- 사이다 1,000원 7개
- 오렌지주스 1,800원 9개 MD추천상품
- 오렌지주스 1,800원 재고 없음
- 탄산수 1,200원 5개 탄산2+1
- 탄산수 1,200원 재고 없음
- 물 500원 10개
- 비타민워터 1,500원 6개
- 감자칩 1,500원 5개 반짝할인
- 감자칩 1,500원 5개
- 초코바 1,200원 5개 MD추천상품
- 초코바 1,200원 5개
- 에너지바 2,000원 5개
- 정식도시락 6,400원 8개
- 컵라면 1,700원 1개 MD추천상품
- 컵라면 1,700원 10개

구매하실 상품명과 수량을 입력해 주세요. (예: [사이다-2],[감자칩-1])
[콜라-3],[에너지바-5]

멤버십 할인을 받으시겠습니까? (Y/N)
Y 

===========W 편의점=============
상품명        수량    금액
콜라        3     3,000
에너지바         5     10,000
===========증    정=============
콜라        1
==============================
총구매액        8    13,000
행사할인            -1,000
멤버십할인            -3,000
내실돈             9,000

감사합니다. 구매하고 싶은 다른 상품이 있나요? (Y/N)
Y

안녕하세요. W편의점입니다.
현재 보유하고 있는 상품입니다.

- 콜라 1,000원 7개 탄산2+1
- 콜라 1,000원 10개
- 사이다 1,000원 8개 탄산2+1
- 사이다 1,000원 7개
- 오렌지주스 1,800원 9개 MD추천상품
- 오렌지주스 1,800원 재고 없음
- 탄산수 1,200원 5개 탄산2+1
- 탄산수 1,200원 재고 없음
- 물 500원 10개
- 비타민워터 1,500원 6개
- 감자칩 1,500원 5개 반짝할인
- 감자칩 1,500원 5개
- 초코바 1,200원 5개 MD추천상품
- 초코바 1,200원 5개
- 에너지바 2,000원 재고 없음
- 정식도시락 6,400원 8개
- 컵라면 1,700원 1개 MD추천상품
- 컵라면 1,700원 10개

구매하실 상품명과 수량을 입력해 주세요. (예: [사이다-2],[감자칩-1])
[콜라-10]

현재 콜라 4개는 프로모션 할인이 적용되지 않습니다. 그래도 구매하시겠습니까? (Y/N)
Y

멤버십 할인을 받으시겠습니까? (Y/N)
N

===========W 편의점=============
상품명        수량    금액
콜라        10     10,000
===========증    정=============
콜라        2
==============================
총구매액        10    10,000
행사할인            -2,000
멤버십할인            -0
내실돈             8,000

감사합니다. 구매하고 싶은 다른 상품이 있나요? (Y/N)
Y

안녕하세요. W편의점입니다.
현재 보유하고 있는 상품입니다.

- 콜라 1,000원 재고 없음 탄산2+1
- 콜라 1,000원 7개
- 사이다 1,000원 8개 탄산2+1
- 사이다 1,000원 7개
- 오렌지주스 1,800원 9개 MD추천상품
- 오렌지주스 1,800원 재고 없음
- 탄산수 1,200원 5개 탄산2+1
- 탄산수 1,200원 재고 없음
- 물 500원 10개
- 비타민워터 1,500원 6개
- 감자칩 1,500원 5개 반짝할인
- 감자칩 1,500원 5개
- 초코바 1,200원 5개 MD추천상품
- 초코바 1,200원 5개
- 에너지바 2,000원 재고 없음
- 정식도시락 6,400원 8개
- 컵라면 1,700원 1개 MD추천상품
- 컵라면 1,700원 10개

구매하실 상품명과 수량을 입력해 주세요. (예: [사이다-2],[감자칩-1])
[오렌지주스-1]

현재 오렌지주스은(는) 1개를 무료로 더 받을 수 있습니다. 추가하시겠습니까? (Y/N)
Y

멤버십 할인을 받으시겠습니까? (Y/N)
Y

===========W 편의점=============
상품명        수량    금액
오렌지주스        2     3,600
===========증    정=============
오렌지주스        1
==============================
총구매액        2    3,600
행사할인            -1,800
멤버십할인            -0
내실돈             1,800

감사합니다. 구매하고 싶은 다른 상품이 있나요? (Y/N)
N
</code></pre><h2 id="프로그래밍-요구-사항-1">프로그래밍 요구 사항 1</h2>
<ul>
<li>JDK 21 버전에서 실행 가능해야 한다.</li>
<li>프로그램 실행의 시작점은 <code>Application</code>의 <code>main()</code>이다.</li>
<li><code>build.gradle</code> 파일은 변경할 수 없으며, <strong>제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다.</strong></li>
<li>프로그램 종료 시 <code>System.exit()</code>를 호출하지 않는다.</li>
<li>프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다.</li>
<li>자바 코드 컨벤션을 지키면서 프로그래밍한다.<ul>
<li>기본적으로 <a href="https://github.com/woowacourse/woowacourse-docs/blob/main/styleguide/java">Java Style Guide</a>를 원칙으로 한다.</li>
</ul>
</li>
</ul>
<h2 id="프로그래밍-요구-사항-2">프로그래밍 요구 사항 2</h2>
<ul>
<li>indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다.<ul>
<li>예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.</li>
<li>힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다.</li>
</ul>
</li>
<li>3항 연산자를 쓰지 않는다.</li>
<li>함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라.</li>
<li>JUnit 5와 AssertJ를 이용하여 정리한 기능 목록이 정상적으로 작동하는지 테스트 코드로 확인한다.<ul>
<li>테스트 도구 사용법이 익숙하지 않다면 아래 문서를 참고하여 학습한 후 테스트를 구현한다.<ul>
<li><a href="https://junit.org/junit5/docs/current/user-guide">JUnit 5 User Guide</a></li>
<li><a href="https://assertj.github.io/doc">AssertJ User Guide</a></li>
<li><a href="https://www.baeldung.com/assertj-exception-assertion">AssertJ Exception Assertions</a></li>
<li><a href="https://www.baeldung.com/parameterized-tests-junit-5">Guide to JUnit 5 Parameterized Tests</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="프로그래밍-요구-사항-3">프로그래밍 요구 사항 3</h2>
<ul>
<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) 로직은 제외한다.</li>
</ul>
<h2 id="프로그래밍-요구-사항-4">프로그래밍 요구 사항 4</h2>
<ul>
<li>함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다.<ul>
<li>함수(또는 메서드)가 한 가지 일만 잘 하도록 구현한다.</li>
</ul>
</li>
<li>입출력을 담당하는 클래스를 별도로 구현한다.<ul>
<li>아래 <code>InputView</code>, <code>OutputView</code> 클래스를 참고하여 입출력 클래스를 구현한다.</li>
<li>클래스 이름, 메소드 반환 유형, 시그니처 등은 자유롭게 수정할 수 있다.<pre><code>public class InputView {
public String readItem() {
  System.out.println(&quot;구매하실 상품명과 수량을 입력해 주세요. (예: [사이다-2],[감자칩-1])&quot;);
  String input = Console.readLine();    
  // ...
}
// ...
}
</code></pre></li>
</ul>
</li>
</ul>
<pre><code></code></pre><p>public class OutputView {
    public void printProducts() {
        System.out.println(&quot;- 콜라 1,000원 10개 탄산2+1&quot;);
        // ...
    }
    // ...
}</p>
<p>```</p>
<h3 id="라이브러리">라이브러리</h3>
<ul>
<li><code>camp.nextstep.edu.missionutils</code>에서 제공하는 <code>DateTimes</code> 및 <code>Console</code> API를 사용하여 구현해야 한다.<ul>
<li>현재 날짜와 시간을 가져오려면 <code>camp.nextstep.edu.missionutils.DateTimes</code>의 <code>now()</code>를 활용한다.</li>
<li>사용자가 입력하는 값은 <code>camp.nextstep.edu.missionutils.Console</code>의 <code>readLine()</code>을 활용한다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[3주차 공통 피드백]]></title>
            <link>https://velog.io/@dan_d/3%EC%A3%BC%EC%B0%A8-%EA%B3%B5%ED%86%B5-%ED%94%BC%EB%93%9C%EB%B0%B1</link>
            <guid>https://velog.io/@dan_d/3%EC%A3%BC%EC%B0%A8-%EA%B3%B5%ED%86%B5-%ED%94%BC%EB%93%9C%EB%B0%B1</guid>
            <pubDate>Tue, 05 Nov 2024 14:53:20 GMT</pubDate>
            <description><![CDATA[<p>3주차 공통 피드백 리뷰에 들어가기 앞서 설레는 마음 한 가득이다.
피드백이 왜 이렇게 날 설레게 할까?🤭</p>
<p><strong>3주차 미션의 학습 목표</strong>
클래스 분리, 단위 테스트를 시작해 보는 것</p>
<p><strong>해야할 일</strong></p>
<ol>
<li>TDD 방식으로 진행 된 피드백 강의 영상 시청하기</li>
<li>새로운 미션 제출 방법에 대해서 알아보기</li>
</ol>
<h2 id="공통-피드백">공통 피드백</h2>
<h3 id="함수메서드-라인에-대한-기준도-적용한다">함수(메서드) 라인에 대한 기준도 적용한다.</h3>
<p>공백 라인도 한 라인으로 간주하는 것을 기억하자.
이 규칙의 의도에 대해서 생각해보자.
내 생각에는 단일 책임이 중요하기 때문이라고 생각한다.
하나의 메소드는 하나의 동사와 목적어를 사용하도록 하자.</p>
<h3 id="예외-상황에-대한-고민을-한다">예외 상황에 대한 고민을 한다.</h3>
<blockquote>
<p>코드를 작성할 때는 예상되는 예외를 미리 고려를 한다.</p>
</blockquote>
<p>테스트 작성을 할 때에 작게 나눠진 단위테스트를 하는 것에 집중을 하자.
그리고 그 기능에 대한 테스트를 진행할 때 미리 예외까지 생각해보는 것으로 하자.</p>
<h3 id="비즈니스-로직과-ui-로직을-분리한다">비즈니스 로직과 UI 로직을 분리한다.</h3>
<p>toString()은 로그나 디버깅을 위한 상태 표시용.
getter는 UI 계층에서 필요한 데이터만 가져올 때 사용.</p>
<p>ex) toString</p>
<pre><code class="language-java">public class User {
    private String name;
    private String email;

    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }

    @Override
    public String toString() {
        return &quot;User{name=&#39;&quot; + name + &quot;&#39;, email=&#39;&quot; + email + &quot;&#39;}&quot;;
    }
}</code></pre>
<p>콘솔은 익숙한데 로그는 아직 잘 모르겠다.
로그를 분석하는 것도 배워보자.
View에서 getter 사용하는 것과 Controller에서 getter 사용해서 View로 전달하는 것 중 어떤 것이 좋을지 고민이 된다.
상황에 따라 둘 다 사용되는 건지 어떠한지 생각해볼 필요가 있는 것 같다.</p>
<h3 id="연관성이-있는-상수는-static-final-대신-enum을-활용한다">연관성이 있는 상수는 static final 대신 enum을 활용한다.</h3>
<p>그룹화하여 한 곳에서 연관된 값들을 관리할 수 있는 장점이 돋보인다.
잘못된 값의 사용도 방지할 수 있고 각 상수가 무엇을 의미하는지 명확히 알 수 있어 좋은 것 같다.</p>
<h3 id="final-키워드를-사용해-값의-변경을-막는다">final 키워드를 사용해 값의 변경을 막는다.</h3>
<p>예기치 않은 값의 변경으로 인한 오류를 방지할 수 있다.
불변성에 대해서 강조되는 것이 많이 보이므로 한 번 알아보자.</p>
<blockquote>
<ol>
<li><strong>Thread Safety</strong>: 값이 변경되지 않으므로 동기화 없이도 안전하게 사용 가능</li>
<li><strong>예측 가능성과 디버깅 용이성</strong>: 불변 객체는 생성 이후 값이 변하지 않기 때문에 코드의 흐름을 예측하기 쉽다.</li>
<li>안전성과 보안성: <strong>안전한 객체 전달을 가능</strong>하게 한다. 메서드나 다른 클래스에서 값을 변경할 위험이 없다.</li>
<li>성능 최적화 가능성: 불변 객체는 <strong>캐싱에 유리</strong>하다. 한 번 계산된 결과를 재사용할 수 있다. 자주 사용되는 값이나 메서드의 반환 결과를 불변 객체로 관리하면 메모리 사용 효율이 높아진다.</li>
<li><strong>함수형 프로그래밍에 적합</strong>: 함수형 프로그래밍은 부작용(side effect)를 피하는 것을 지향하는데 이러한 부작용을 줄여준다. </li>
</ol>
</blockquote>
<h3 id="객체의-상태-접근을-제한한다">객체의 상태 접근을 제한한다.</h3>
<p>인스턴스 변수의 접근 제어자를 private으로 설정한다.
캡슐화의 중요한 원칙, 객체의 상태는 외부에서 통제되지 않도록 주의하자. <strong>(객체의 자율성)</strong></p>
<h3 id="객체는-객체답게-사용한다">객체는 객체답게 사용한다.</h3>
<p>자기 역할에 책임을 다 할 수 있게 하는 것이 중요하다고 얘기하는 듯 싶다.
<strong>상태를 가지고 있다면 그 데이터를 활용하는 로직이 필요하다</strong>는 것 같다.
또한 내부 구현이 변경되어도 메서드의 인터페이스가 동일하다면 외부 코드에 영향을 미치지 않는 점도 중요하다고 생각된다.</p>
<p>이번 로또 미션에서 Lotto 객체를 전혀 Lotto 객체답게 사용 못 한 것 같아 아쉽다.
레이싱카를 진행할 때에는 이러한 점에 신경을 쓴 것 같은데 마음이 급했나보다.</p>
<p>이번 피드백이 나에게 아주 강렬한 인상을 주었다. 감사합니다😊 </p>
<blockquote>
<p>자율적, 책임 주도 설계, 메세지와 협력에 대해서 고민하자.</p>
</blockquote>
<p><a href="https://tecoble.techcourse.co.kr/post/2020-04-28-ask-instead-of-getter/">참고블로그</a></p>
<h3 id="필드인스턴스-변수의-수를-줄이기-위해-노력한다">필드(인스턴스 변수)의 수를 줄이기 위해 노력한다.</h3>
<p>코드를 깔끔하게 하고 싶어서 lottoController 클래스에서 인스턴스 변수의 수를 늘렸다.
하지만 이 피드백에서 말한 것처럼 잠시 필요한 정보라고 판단됐다.
즉 인스턴스 변수로 불필요하다고 생각되어 지역 변수로 사용을 했는데 좋은 선택이었던 것 같다.
*<em>필드에 중복과 필요 유무에 대해서 확인하자. *</em></p>
<h3 id="성공하는-케이스-뿐만-아니라-예외-케이스도-테스트한다">성공하는 케이스 뿐만 아니라 예외 케이스도 테스트한다.</h3>
<p>나는 오히려 예외 케이스만 테스트 작성하려고 했다.
그래서 테스트 작성을 어렵게 생각한 것 같다.
성공하는 케이스를 선 작성하는 것이 좋은 것 같고 예외 케이스 작성도 꾸준하게 해보자.
<strong>즉, 기본을 준수하되 예외(변동)에 대비를 하자.</strong></p>
<h3 id="테스트-코드도-코드다">테스트 코드도 코드다.</h3>
<p>테스트 코드 또한 리팩터링을 할 필요가 있다.
효율적인 테스트 코드 작성을 위해 지속적으로 노력하자.</p>
<h3 id="테스트를-위한-코드는-구현-코드에서-분리되어야-한다">테스트를 위한 코드는 구현 코드에서 분리되어야 한다.</h3>
<blockquote>
<p>테스트를 위해 구현 코드를 변경하는 것은 좋지 않은 습관이다.</p>
</blockquote>
<p>무엇이 먼저인가? 본질에 대해서 생각하자.</p>
<h3 id="단위-테스트하기-어려운-코드를-단위-테스트하기">단위 테스트하기 어려운 코드를 단위 테스트하기</h3>
<p>메서드 시그니처란?
메서드를 구별하는 핵심 정보, 예를 들어서 메서드의 이름, 매개변수의 타입 및 순서
이를 통해 유일하게 식별할 수 있다.</p>
<blockquote>
<p>제어할 수 없는 요소를 분리나 주입같은 기법을 통해서 조정 가능하게 만들어 예측 가능한 환경을 만든다.</p>
</blockquote>
<p><a href="https://tecoble.techcourse.co.kr/post/2020-05-07-appropriate_method_for_test_by_parameter/">참고블로그</a></p>
<h3 id="private-함수를-테스트-하고-싶다면-클래스객체-분리를-고려한다">private 함수를 테스트 하고 싶다면 클래스(객체) 분리를 고려한다.</h3>
<p>이 주제는 <strong>단일 책임 원칙</strong>과 <strong>테스트 가능성</strong>, 그리고 <strong>코드 응집도</strong>에 대한 깊이 있는 논의로 이어질 수 있는 흥미로운 부분이다. </p>
<p>핵심은, private 메서드가 단순히 구현 세부사항이 아니라 중요한 역할을 수행할 때, 이를 분리하여 독립된 클래스나 객체로 만드는 것이 좋은 설계가 될 수 있다는 점이다.</p>
<p>왜 private 메서드를 테스트하기 어려운가?</p>
<ul>
<li>접근 제한: private 메서드는 외부에서 접근할 수 없으므로, 단위 테스트에서 직접 호출이 불가능하다.</li>
<li>간접 테스트: 일반적으로는 public 메서드에서 private 메서드를 호출하여 간접적으로 테스트를 수행하지만, 이 방식은 모든 로직을 철저히 검증하기에 불완전할 수 있다.</li>
</ul>
<p>중요한 역할을 수행하는 private 메서드란?</p>
<p>주요 로직을 처리하는 메서드가 private으로 정의된 경우, 이 로직이 얼마나 잘 작동하는지를 확인하기 위해서는 세밀하게 검증할 필요가 있다.
단순히 가독성을 위해 코드를 분리한 것이라면 문제가 없겠지만, 중요한 계산 로직이나 데이터 처리를 담당한다면 단순한 구현 세부사항 이상의 역할을 수행하고 있다고 볼 수 있다.</p>
<p>이럴 때 클래스 분리가 왜 중요한가?</p>
<p>SRP를 적용하여 각 클래스가 하나의 책임만 가지도록 하면, private 메서드에서 수행하던 중요한 로직을 독립된 클래스의 public 메서드로 분리할 수 있다.
이렇게 하면 테스트 가능성이 높아지며, 독립된 역할을 부여받은 클래스를 직접적으로 테스트할 수 있게 된다.</p>
<p>예시로 이해해 보기</p>
<blockquote>
<p>주문 처리 시스템에서 할인을 계산하는 로직이 있다고 가정하자. 
이 할인 계산이 Order 클래스의 private 메서드로 구현되어 있다면, 할인 로직 자체를 테스트하기가 어려워진다. 
이럴 때, 할인 계산을 담당하는 DiscountCalculator라는 클래스를 따로 분리하면 DiscountCalculator는 public 메서드로 할인 계산을 수행하므로 독립적으로 테스트할 수 있고 Order 클래스는 주문과 관련된 책임만 가지므로 응집도가 높아진다.</p>
</blockquote>
<p>중요한 역할을 수행하는 private 메서드를 분리하는 것은 단순히 테스트 가능성을 높이는 것뿐 아니라, 클래스가 하나의 책임을 가지도록 설계하여 코드의 구조를 개선하고 유지보수성을 높이는 데 기여한다.</p>
<h2 id="마무리하며">마무리하며</h2>
<p>테스트 주도 개발과 객체지향적 사고의 중요성을 느끼게 된 피드백이었다.
앞으로 테스트가 필요한 이유에 대한 주관적 견해를 쌓자.
객체지향 프로그래밍을 할 때에 객체의 자율적, 책임과 메세지를 통한 협력, 역할에 대해 고려하자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 7기] 3주차 로또 회고]]></title>
            <link>https://velog.io/@dan_d/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-3%EC%A3%BC%EC%B0%A8-%EB%A1%9C%EB%98%90-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dan_d/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-3%EC%A3%BC%EC%B0%A8-%EB%A1%9C%EB%98%90-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Tue, 05 Nov 2024 08:43:26 GMT</pubDate>
            <description><![CDATA[<h1 id="서론">서론</h1>
<p><a href="https://github.com/miniminjae92/java-lotto-7/tree/miniminjae92">GitHub - minjae/precourse-7th-lotto</a></p>
<h2 id="공통-피드백">공통 피드백</h2>
<p>3주차 회고를 하면서 먼저 든 생각이 있다.
공통피드백을 3주차 회고에 함께 작성하기보다는 공통피드백이 나올 당시에 피드백에 대한 회고를 바로 진행하는 것이다.
그렇게 된다면 다음 미션을 진행할 때 피드백 반영을 더욱 도울 것이라 생각이 들었다. 
앞으로의 일정에서는 그렇게 하도록 노력해야겠다.</p>
<h3 id="2주차-목표">2주차 목표</h3>
<p>함수 분리와 테스트 도구의 사용법 학습하기
다른 사람과의 비교보다는 어제의 나와 비교하며 자신의 속도에 맞추기
메타인지의 최고의 도구 중 하나인 회고를 활용하기 <a href="https://techblog.woowahan.com/?paged=1&amp;ptag=%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4">참고링크</a></p>
<h3 id="2주차-10가지-피드백-회고">2주차 10가지 피드백 회고</h3>
<p><strong>1. README.md 를 상세히 작성한다.</strong></p>
<p>README 작성을 할 때에 기능 목록을 작성에 집중을 했다.
아쉬운 점은 <strong>해당 프로젝트가 어떤 프로젝트인지에 대한 설명</strong>을 보충을 한다면 좋을 것 같다는 점이다.
예를 들면 아래와 같은 방법을 들어서 도입부에 넣었으면 좋았을텐데 라고 생각하였다.</p>
<blockquote>
<p><strong>Lotto Program</strong>
이 프로젝트는 Java를 사용하여 로또 발매기 프로그램을 구현하는 과제입니다. 
로또 번호 발행, 당첨 번호 입력, 당첨 결과 출력, 수익률 계산 등의 기능을 포함합니다.</p>
</blockquote>
<p><strong>2. 기능 목록을 재검토한다.</strong></p>
<blockquote>
<p>핵심 기능을 파악하고 목록 작성을 하는 것에 집중을 한다.
그리고 테스트를 진행하며 예외 상항을 함께 업데이트하는 것에 중점을 두자.</p>
</blockquote>
<p>나도 모르게 아주 구체적으로 들어가는 것 같다.
현재 시점에 다시 읽어보니 새삼 느껴지는 것이 많다.</p>
<p><strong>3. 기능 목록을 업데이트한다.</strong></p>
<blockquote>
<p>시작부터 모든 기능을 완벽하게 정리해야 한다는 부담을 갖지말자.
문서를 업데이트하는 것을 목표로 하자.</p>
</blockquote>
<p><strong>4. 값을 하드 코딩하지 않는다.</strong></p>
<p>프로젝트가 커지면 생각보다 값의 변경이 많을 것 같다는 생각이 들었다.
미리미리 <strong>하드 코딩하지 않는 습관</strong>을 들이는 것이 좋겠다고 생각했다.</p>
<p><strong>5. 구현 순서도 코딩 컨벤션이다.</strong></p>
<p><strong>&quot;코드를 많이 읽어보자&quot;</strong> 생각이 들었다.
마치 문장, 책을 많이 읽으면 문법에 대해서 체화되는 것처럼 🤗</p>
<p><strong>6. 변수 이름에 자료형은 사용하지 않는다.</strong></p>
<blockquote>
<p>변수 이름은 &#39;What&#39;에 포인트를 두자고 생각했다.</p>
</blockquote>
<p>자료형이 변경되면 변수명도 매 번 변경 할 것인가?
프로그래밍은 항상 최우선적으로 변경에 중점을 둬야 하는 것 같다.</p>
<p><strong>7. 한 메서드가 한가지 기능만 담당하게 한다.</strong></p>
<p>구현에 몰입할 때 이 문구를 되뇌일 필요가 있다.</p>
<p><strong>8. 메서드가 한 가지 기능을 하는지 확인하는 기준을 세운다.</strong></p>
<p>명확한 이름, 예를 들어 동사와 목적어 패턴이 필요하다고 생각한다.</p>
<blockquote>
<p>메서드 이름에 하나의 동사와 목적어가 들어가는지 확인하기</p>
</blockquote>
<p>정작 로또 미션에서 이 사항을 미리 적용했다면 어떨까? 후회가 된다. 🥲</p>
<p><strong>9. 테스트를 작성하는 이유에 대해 본인의 경험을 토대로 정리해본다.</strong></p>
<blockquote>
<p>첫번 째, 구현할 기능 파악에 도움을 준다.
두번 째, 살아있는 문서가 된다.</p>
</blockquote>
<p>고로 단위테스트를 통해서 구현해야할 기능에 집중을 한다.
그리고 살아숨쉬는 문서를 통해 새롭게 발생되는 문제 해결에 도움을 받는다.</p>
<p><strong>10. 처음부터 큰 단위의 테스트를 만들지 않는다.</strong></p>
<p>작은 단위의 테스트를 FIRST 하게 하자.</p>
<blockquote>
<p>Fast: 테스트는 빠르게 실행되어야 한다.
Independent: 테스트는 서로 독립적이어야 한다.
Repeatable: 테스트는 반복 실행해도 같은 결과가 나와야 한다.
Self-validating: 테스트는 성공과 실패를 스스로 판단할 수 있어야 한다.
Timely: 기능 구현 전이나 중에 테스트를 작성해야 한다.</p>
</blockquote>
<h3 id="생각의-씨앗-심기">생각의 씨앗 심기</h3>
<p>1, 2, 3주차 미션을 진행하면서 본격적인 미션 시작을 늦게 한 점이다.
배울 것이 많고 다양한 할 일 들이 존재한다.
코드 리뷰, 기술적 지식들 등 하지만 앞으로는 나의 퍼포먼스를 위해서 우선 순위에 집중하기로 했다. </p>
<p>예를 들면, &quot;무슨 일이 있더라도 우선 화요일에 리드미 작성과 개념까지는 그려놓고 다른 짓을 하기&quot; 처럼 😼</p>
<p>왜냐하면 중요한 부분을 머리에 먼저 담아두면, 무의식적으로 아이디어가 발전하기 때문이다!</p>
<h1 id="본론">본론</h1>
<h2 id="요구-사항에-대한-회고">요구 사항에 대한 회고</h2>
<p><strong>간단한 로또 발매기를 구현한다.</strong></p>
<h3 id="과제-진행-요구-사항">과제 진행 요구 사항</h3>
<ul>
<li>기능을 구현하기 전 README.md에 구현할 기능 목록을 정리해 추가한다.</li>
<li>Git의 커밋 단위는 앞 단계에서 README.md에 정리한 기능 목록 단위로 추가한다.<ul>
<li>AngularJS Git Commit Message Conventions 을 참고하여 작성한다.</li>
</ul>
</li>
</ul>
<hr>
<p>커밋 메세지 작성을 부족한 영어실력에도 불구하고 영어를 사용하였다.
진행을 하면서 당연하게도 어려움을 겪었다.
커밋을 하는 것 자체가 어색한 단계인데 여유가 생길 때까지 한글을 사용하기로 마음을 먹었다.
<del>테스트 메소드도 영어로 작성을 했다...그러다보니 점점 커밋과 테스트 코드 작성에 손길이 안 간 점 😭</del>
다음 목표는 거침없이 작성해보는 것이다.</p>
<p>궁금한 사항이 생겼는데 커밋을 무조건 많이 자주하는 것이 좋은 것일까?
테스트를 많이 만드는 것이 좋은 것일까? 에 대한 의문이 생겼다.
여러분의 생각들은 어떠한지 시간이 되시면 댓글로 알려주세요 😊</p>
<h3 id="기능-요구-사항">기능 요구 사항</h3>
<ul>
<li>로또 번호는 1부터 45까지의 숫자 중 중복되지 않은 6개로 구성됩니다.</li>
<li>사용자가 입력한 구입 금액에 따라 발행된 로또 수량을 출력합니다.</li>
<li>당첨 번호와 보너스 번호를 입력받아, 사용자가 구입한 로또 번호와 비교하여 당첨 결과와 수익률을 출력합니다.</li>
<li>사용자가 잘못된 값을 입력할 경우 <code>IllegalArgumentException</code>을 발생시키고, &quot;[ERROR]&quot;로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다.<ul>
<li><code>Exception</code>이 아닌 <code>IllegalArgumentException</code>, <code>IllegalStateException</code> 등과 같은 명확한 유형을 처리한다.</li>
</ul>
</li>
</ul>
<p><strong>실행 예시</strong></p>
<pre><code class="language-java">구입금액을 입력해 주세요.
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>
<hr>
<p>워크플로우를 먼저 작성을 했다.</p>
<ol>
<li>구입금액 입력</li>
<li>당첨번호 입력</li>
<li>보너스번호 입력</li>
<li>발행한 로또 수량 및 번호를 출력(오름차순)</li>
<li>당첨내역 출력</li>
<li>수익률 출력</li>
</ol>
<p>이제는 입출력 예시를 보고 워크플로우를 작성을 하는 것을 가장 먼저 진행하고 있다.
하지만 4번이 먼저 나와야하는데 당시에 잘못 작성을 하여 테스트 코드 및 구현에 실제로 난항을 겪었다.
입출력 예시를 더 꼼꼼히 보면서 논리적으로 생각해 보는 것이 중요하다고 느꼈다.
다음 번 미션에서는 워크플로우로 핵심 기능을 작성을 하고나서 기능과 예외사항에 대한 문서 업데이트를 자주 하는 것을 목표로 해야겠다.</p>
<p><del>draw.io 처음 활용해보고 README 이미지 올리는 것을 이번 기회에 배우게 되어서 좋았다. 😁</del></p>
<p><strong>내가 놓친 부분</strong></p>
<blockquote>
<p>수익률은 소수점 둘째 자리에서 반올림한다. (ex. 100.0%, 51.5%, 1,000,000.0%)
&#39; , &#39; 사용하여 출력하는 구현을 못해서 아쉽게 생각한다.</p>
</blockquote>
<blockquote>
<p>사용자가 잘못된 값을 입력할 경우 <code>IllegalArgumentException</code>을 발생시키고, &quot;[ERROR]&quot;로 시작하는 에러 메시지를 출력 후 <strong>그 부분부터 입력을 다시 받는다.</strong>
    - <code>Exception</code>이 아닌 <code>IllegalArgumentException</code>, <strong><code>IllegalStateException</code></strong> 등과 같은 명확한 유형을 처리한다.
<strong>&quot;그 부분부터 다시 입력을 받는다&quot;</strong>  이 요구사항을 초기에 읽지 못하여 많은 고생을 하였다.
상태 예외 처리에 대해서 활용을 못한 것 같아 아쉽다.
코드 리뷰를 통해 배워봐야겠다.</p>
</blockquote>
<h3 id="프로그래밍-요구-사항">프로그래밍 요구 사항</h3>
<ul>
<li><a href="https://github.com/woowacourse/woowacourse-docs/blob/main/styleguide/java">Java Style Guide</a></li>
<li>함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라</li>
<li>JUnit5, AssertJ 이용하여 정리한 기능 목록이 정상 작동하는지 테스트 코드로 확인하기<ul>
<li><a href="https://junit.org/junit5/docs/current/user-guide">JUnit 5 User Guide</a></li>
<li><a href="https://assertj.github.io/doc">AssertJ User Guide</a></li>
<li><a href="https://www.baeldung.com/assertj-exception-assertion">AssertJ Exception Assertions</a></li>
<li><a href="https://www.baeldung.com/parameterized-tests-junit-5">Guide to JUnit 5 Parameterized Tests</a></li>
</ul>
</li>
<li>Java Enum을 적용하여 프로그램을 구현한다.</li>
<li><code>camp.nextstep.edu.missionutils</code>에서 제공하는 <code>Randoms</code> 및 <code>Console</code> API를 사용하여 구현해야 한다.<ul>
<li>Random 값 추출은 <code>camp.nextstep.edu.missionutils.Randoms</code>의 <code>pickUniqueNumbersInRange()</code>를 활용한다.</li>
<li>사용자가 입력하는 값은 <code>camp.nextstep.edu.missionutils.Console</code>의 <code>readLine()</code>을 활용한다.</li>
</ul>
</li>
</ul>
<pre><code class="language-java">Randoms.pickUniqueNumbersInRange(1, 45, 6);</code></pre>
<p><strong>Lotto 클래스</strong></p>
<ul>
<li>제공된 <code>Lotto</code> 클래스를 사용하여 구현해야 한다.</li>
<li><code>Lotto</code>에 <code>numbers</code> 이외의 필드(인스턴스 변수)를 추가할 수 없다.</li>
<li><code>numbers</code>의 접근 제어자인 <code>private</code>은 변경할 수 없다.</li>
<li><code>Lotto</code>의 패키지를 변경할 수 있다.</li>
</ul>
<hr>
<p><strong>1. Java Style Guide</strong></p>
<p>포맷터 사용에 대해서 구글링을 하여서 조금 더 공부가 필요한 것을 느꼈다.
우아한테크코스 전용으로 만들었는데 공백과 탭중에 무엇을 사용할 지를 고민을 하였고 탭을 선택했다.
하지만 공백을 주로 디폴트로 사용한다는 이야기를 듣기도 했다.</p>
<p>여러분은 어떤 방식으로 스타일 관리를 하고 계신가요? 🧐</p>
<p><strong>2. JUnit5, AssertJ</strong></p>
<p>JUnit5, AssertJ 에 대해서 이번에 제공해주신 PDF 파일을 이용해서 공부를 했다.
지금은 테스트 흐름은 JUnit으로 관리를 하고 AssertJ를 이용한 검증을 하는 식으로 나만의 간단 정리를 했다.</p>
<p>이번 미션에서 테스트를 선 진행을 하고싶었다.
하지만 인터페이스가 어떠한 지 생각하는 것이 어려웠고 타입과 네이밍을 테스트 코드에서 먼저 생각하는 것이 어려웠다.
간단한 테스트를 작성하고 구현에 들어가서 구현을 많이 진행하고 나오는 경우가 많았다.
이 방법에 대해서는 앞으로도 많은 연습이 필요할 것 같다.</p>
<p><strong>3. Enum</strong></p>
<p>ErrorMessage 와 Lotto Rank 라는 Enum 클래스를 만들어서 사용을 했다.
Rank에 따른 금액과 메세지들까지 함께 관리를 하여 사용을 했는데 특히 개인적으로 메세지 출력 관리에 있어 편리했다.</p>
<p>미션을 진행하며 EnumMap 의 존재에 대해서도 알게 되어서 사용을 했다.
출력이 6등부터 나오고 있어 출력 순서에 대해서 헤매고 있었는데 EnumMap을 사용을 하면서 출력 순서를 편하게 관리했다.</p>
<p>EnumMap은 배열을 기반으로 구현되어 고정된 인덱스 매핑을 활용한다고 한다.
Hash 충돌 관리가 필요없어서 HashMap 보다 속도가 빠르다는 장점이 있다.
Enum을 사용하는 경우 EnumMap을 이용하는 것이 더 빠르고 메모리 낭비를 줄이는 결과를 보이는 것 같다.</p>
<p><strong>4. 라이브러리 제공 메소드</strong></p>
<p><strong>Randoms.pickUniqueNumbersInRange(1, 45, 6);</strong></p>
<p>이전 미션과 같은 메소드로 보고 넘어갔다.
자세히 읽지 않은 내 잘못이 크다.
다른 메소드를 이용해야하는데 이전 랜덤번호생성하는 메소드를 사용했다.
하나의 랜덤 번호를 생성하고 중복되지 않은 6개의 숫자가 생성될 때까지 while 문을 사용을 했다.
그 결과 기능 테스트에서 문제가 계속해서 발생하여 고생을 했다.</p>
<p>미리 좋은 경험을 하여 앞으로 요구사항에 대해서 경각심을 가지고 내 머릿 속 캐시를 자주 비우며 읽게 될 것 같다.
말처럼 쉽진 않을 것 같지만 회고를 통해 자주 돌이켜보면 좋은 결과가 있지 않을까 싶다. 🤭</p>
<p><strong>5. 로또 클래스 제한 사항</strong></p>
<ul>
<li>필드를 추가할 수 없다.</li>
<li><code>numbers</code>의 접근 제어자 <code>private</code> 변경 불가</li>
<li>패키지 변경 가능</li>
</ul>
<p>이러한 제약 사항으로 인해 Lotto 클래스에 신중하게 접근하게 됐다. 
특히 로또 번호 자체에 초점을 두고 설계를 진행하게 되어, 로또 번호의 데이터와 규격을 표현하는 클래스라는 본질적인 역할에 집중할 수 있었다.</p>
<p>패키지 변경 가능성을 고려해 Lotto 클래스를 도메인 패키지로 이동하여 관리했습니다. 
초기에는 검증 패키지로 옮기는 것도 가능할까 생각했지만, 제한 사항을 반영하며 구현을 진행하다 보니 Lotto 클래스는 로또 번호의 규격과 데이터 자체를 나타내는 도메인 객체라는 결론에 도달했다. 
검증 관련 기능은 Lotto 클래스 외부에 위치한 LottoNumberValidator와 같은 별도의 검증 객체에서 수행하도록 하여 단일 책임을 명확히 했다.</p>
<p>처음에는 Lotto 클래스에서 보너스 번호를 함께 관리하는 구조를 생각했지만, Lotto 클래스의 본질을 고려해 이를 WinningNumbers 객체로 분리했다. 
이를 통해 보너스 번호와 당첨 번호를 별도로 관리하게 되었고, 각 객체가 역할에 충실한 구조로 개선됐다.</p>
<p>이번 경험을 통해 제약 사항이 설계와 구현 방향에 큰 영향을 미칠 수 있음을 체감했다. 
제한 덕분에 Lotto 클래스의 역할이 더욱 명확해졌고, 이를 통해 필요한 기능을 다른 객체로 분리하여 설계하는 것의 중요성을 배울 수 있었다. </p>
<h1 id="결론">결론</h1>
<p>가장 기억에 남는 점은 가장 나를 힘들게 했던 점들 같다.</p>
<blockquote>
<p>테스트를 선 작성 후 구현 (캐싱 문제)
잘못된 입력일 때 다시 입력 받기
랜덤 라이브러리, 제공 메소드 사용하기</p>
</blockquote>
<p>테스트를 선 작성 후 구현을 시도하다가 return 0L; 을 이용을 했다.
실패를 하도록 작성을 하고 진행을 했는데 성공을 했다.
내 예측상 이런 결과가 나오면 안된다고 생각했는데 다른 결과가 나와서 당황을 했다.
구현 코드의 리턴 값도 변경 된 후라서 생각을 못했던 것 같다.
제공해준 테스트 ./gradlew clean test 를 진행했고 캐싱되는 것이 문제였다고 판단된다.
메모리가 저장되는 것을 생각을 하는 계기가 됐다.</p>
<p>요구 사항들을 놓쳐서 발생했던 문제로 마지막에 대대적으로 코드 변경이 일어났다.
원인을 빠르게 알아내는 디버깅 실력이 중요한 것 같다.
단위 테스트의 중요성도 이러한 문제점을 해결할 때 도움이 될 것 같다.</p>
<p>테스트 작성을 하면서 테스트를 위해 본 코드를 변경하고 싶었던 적도 있고 구현이 변경되면서 테스트 코드들이 함께 변경해야 하는 경우도 경험을 할 수 있었다.
미션에 들어가기 앞서 먼저 핵심 기능에 대해서 파악을 한다.
그리고 작게 나눠서 테스트 작성과 구현을 동시에 하는 것이 올바른 방향이라고 느껴졌다.</p>
<p>내가 하고 있는 것들이 정답이고 올바른 방향인지 지금의 나로선 알 수 없지만 계속해서 한 걸음씩 나아가고 싶다.
초입에 공통 피드백에서 해준 말을 떠올리며 이번 회고를 마치고 싶다.</p>
<blockquote>
<p>다른 사람과 비교하다 보면 조바심이 생길 수 있습니다. 
그렇지만 다른 사람과의 비교보다는 어제의 나와 비교하며 자신의 속도에 맞추어서 마무리하는 것을 목표로 삼아 보세요. 
이번 경험이 좋은 프로그래머로 성장하는 중요한 역량을 키우는 과정임을 기억해 주세요.</p>
</blockquote>
<p>지금 과정이 나에게 녹록치 않다.
어려운 길을 걷게 한다.
그것이 어제의 나와 지금의 나의 차이를 만드는 것 같다.
큰 차이를 만들자.</p>
<p><strong>Stay Hard!</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 7기] 프리코스 2주차 회고]]></title>
            <link>https://velog.io/@dan_d/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-2%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dan_d/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-2%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Thu, 31 Oct 2024 02:28:13 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dan_d/post/74c2d0f3-6bf3-4202-b334-7bfb8f6726f2/image.png" alt=""></p>
<blockquote>
<p>우테코 7기 2주차 미션 &lt;자동차 경주&gt;</p>
</blockquote>
<p><a href="https://github.com/miniminjae92/java-racingcar-7/tree/miniminjae92">2주차 미션 깃헙 Link</a></p>
<h1 id="서론">서론</h1>
<hr>
<h2 id="2차-공통-피드백을-보며">2차 공통 피드백을 보며</h2>
<details>
  <summary> 자세히 보기 </summary>

<p>2주 차 미션의 학습 목표는 함수 분리와 테스트 도구의 사용법을 익혀 보는 것이었습니다. 새로운 개념을 학습하고 미션에 적용하는 과정이 쉽지 않았을 텐데요. 특히 다른 사람과 비교하다 보면 조바심이 생길 수 있습니다. 그렇지만 다른 사람과의 비교보다는 어제의 나와 비교하며 자신의 속도에 맞추어서 마무리하는 것을 목표로 삼아 보세요. 이번 경험이 좋은 프로그래머로 성장하는 중요한 역량을 키우는 과정임을 기억해 주세요.</p>
<p>입학 설명회에서 설명하였듯이 메타인지를 위한 최고의 도구 중 하나는 회고입니다. 회고를 통해 우리는 학습과 경험을 그냥 지나치지 않고 반성하고 개선할 수 있습니다. 아직 중간 회고를 작성하지 않았거나 회고를 공유하고 싶다면 프리코스 커뮤니티의 회고 채널에서 공유해 주세요. 한 주 한 주 지나면서 성장하는 여러분의 모습을 기대하겠습니다. 우아한테크코스 크루들이 쓴 내용이 궁금하다면 이 링크에서 회고록을 확인할 수 있습니다.</p>
<p>단위 테스트와 같은 용어가 아직 낯설 수 있지만, 작은 기능부터 테스트를 작성하는 연습을 차근차근 해 나가면  빠르게 성장하실 수 있습니다. 1주 차 피드백에서 제공된 문자열 덧셈 계산기 피드백 강의에 단위 테스트를 작성하는 내용이 있으니 이를 참고해 주세요.</p>
<p>이번 주도 여러분에게 의미 있는 시간이 되기를 바랍니다.</p>
<p>공통 피드백
README.md를 상세히 작성한다
미션 저장소의 README.md는 소스 코드 이전에 프로젝트의 개요를 소개하는 문서다. 이 문서를 통해 해당 프로젝트가 어떤 프로젝트인지, 주요 기능이 무엇인지 소개할 수 있다. 효과적으로 작성하기 위해 마크다운 문법을 검색하여 학습하고, 이를 활용해 README.md를 작성해 본다.
기능 목록을 재검토한다
기능 목록을 작성할 때 클래스 설계와 구현, 메서드 설계와 구현 같은 상세한 내용은 포함하지 않는다. 클래스 이름이나 메서드 시그니처, 반환값 등은 언제든지 변경될 수 있기 때문이다. 구현해야 할 기능 목록을 중심으로 작성하되, 정상적인 경우뿐만 아니라 예외 상황도 함께 정리한다. 예외 상황은 시작 단계에서 파악하기 어려우므로, 기능을 구현하면서 지속적으로 업데이트하는 것이 좋다.
기능 목록을 업데이트한다
README.md 파일의 기능 목록은 구현 과정에서 변경될 수 있다. 시작부터 모든 기능을 완벽하게 정리해야 한다는 부담을 갖기보다는, 기능을 구현하면서 문서를 지속적으로 업데이트하는 것을 목표로 한다. 이를 통해 죽은 문서가 아닌 살아있는 문서로 유지될 수 있도록 노력해 보자.
값을 하드 코딩하지 않는다
코드 내에서 문자열이나 숫자 값을 하드 코딩하지 않는다. 대신 상수(static final)를 정의하고 의미 있는 이름을 부여하여 해당 값이 어떤 역할을 하는지 명확히 드러낸다. 구글에 &quot;java 상수&quot;등의 키워드로 검색하여 상수 구현 방법을 학습하고 코드에 적용해 본다.
구현 순서도 코딩 컨벤션이다
클래스는 상수, 멤버 변수, 생성자, 메서드 순으로 작성한다. </p>
<p>class A {
    상수(static final) 또는 클래스 변수
    인스턴스 변수
    생성자
    메서드
}</p>
<p>변수 이름에 자료형은 사용하지 않는다
변수 이름에 자료형, 자료 구조 등을 포함하지 않는다. 변수 이름은 의미를 명확히 드러낼 수 있도록 하고, 자료형은 코드 작성 시점에 자연스럽게 이해될 수 있도록 한다.</p>
<p>String carNameList = Console.readLine();
String[] arrayString = carNameList.split(&quot;,&quot;);</p>
<p>한 메서드가 한 가지 기능만 담당하게 한다
함수의 길이가 길어진다면 여러 기능을 한 함수에서 처리하려는 신호일 가능성이 높다. 예를 들어, 안내 문구 출력, 사용자 입력 처리, 유효값 검증 등의 작업을 한 함수에 모두 포함하는 대신, 이를 각기 다른 함수로 분리해 본다.</p>
<p>public List<String> userInput() {
    System.out.println(&quot;경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분).&quot;);
    String userInput = Console.readLine().trim();
    String[] splittedName = userInput.split(&quot;,&quot;);
    for (int index = 0; index &lt; splittedName.length; index++) {
        if (splittedName.length &lt; 1 || splittedName.length &gt; 5) {
            throw new IllegalArgumentException(&quot;[ERROR] 자동차 이름은 1자 이상 5자 이하만 가능합니다.&quot;);
        }
    }
    return Arrays.asList(splittedName);
}</p>
<p>메서드가 한 가지 기능을 하는지 확인하는 기준을 세운다
여러 메서드에서 중복되는 코드가 있다면 이를 별도 메서드로 분리하는 것을 고려한다. 메서드의 길이가 길어지면 여러 기능을 포함하고 있을 가능성이 커지므로, 15라인이 넘지 않도록 구현하면 의식적으로 메서드를 분리하는 연습을 할 수 있다.
테스트를 작성하는 이유에 대해 본인의 경험을 토대로 정리해본다
테스트를 작성하면 기능의 정확성을 점검함을 넘어 코드의 즉각적인 피드백을 받을 수 있다. 테스트 작성 과정을 통해 구현한 기능의 문제를 빠르게 발견할 수 있을 뿐만 아니라, 코드의 구조와 의도를 명확히 이해하는 데도 도움을 받을 수 있다. 학습 도구로도 활용할 수 있는데, 수 많은 테스트의 장점 중 본인이 가장 공감하는 작성 이유를 작성해 본다.</p>
<p>학습테스트를 통해 JUnit 학습하기.pdf
처음부터 큰 단위의 테스트를 만들지 않는다
테스트의 핵심 목적 중 하나는 코드에 대해 빠르고 자주 피드백을 받는 것이다. 처음부터 큰 단위의 테스트를 작성하게 되면, 작성한 코드의 문제를 발견하기까지 시간이 오래 걸린다. 따라서 문제를 작게 나누어 핵심 기능부터 작게 테스트를 만들어 가는 것이 효과적이다.</p>
<p>큰 단위의 테스트
자동차 경주 게임을 시작하여, 사용자가 이름과 진행 횟수를 입력하고, 게임을 진행한 후 결과를 확인한다.</p>
<p>작은 단위의 테스트
무작위 값이 4 이상이면 자동차가 전진한다.
무작위 값이 3 이하이면 자동차가 전진하지 않는다.</p>
</details>

<blockquote>
<p>2주 차 미션의 학습 목표는 <strong>함수 분리</strong>와 <strong>테스트 도구의 사용법</strong>을 익혀 보는 것이었습니다. 새로운 개념을 학습하고 미션에 적용하는 과정이 쉽지 않았을 텐데요. 특히 <strong>다른 사람과 비교하다 보면 조바심이 생길 수 있습니다. 그렇지만 다른 사람과의 비교보다는 어제의 나와 비교하며 자신의 속도에 맞추어서 마무리하는 것을 목표로 삼아 보세요.</strong> 이번 경험이 좋은 프로그래머로 성장하는 중요한 역량을 키우는 과정임을 기억해 주세요.</p>
</blockquote>
<p>어찌 이렇게 내 마음을 알았는지 깜짝 놀랐고 내가 항상 나에게 하는 말을 적어주셔서 더 감명 깊었다.
힘들 때나 조급해질 때마다 스스로에게 하는 말인데..
좋다! 마무리하는 것을 목표로 좋은 경험을 하는 것을 목표로 달려보자.
아래의 공통 피드백을 보면서 2주차 회고를 시작하려 한다.</p>
<ul>
<li>README.md를 상세히 작성한다</li>
<li>기능 목록을 재검토한다
구현해야 할 기능 목록을 중심으로 작성하되, 정상적인 경우뿐만 아니라 예외 상황도 함께 정리한다.</li>
<li>기능 목록을 업데이트한다.</li>
<li>값을 하드 코딩하지 않는다</li>
<li>구현 순서도 코딩 컨벤션이다
클래스는 상수, 멤버 변수, 생성자, 메서드 순으로 작성한다. </li>
<li>변수 이름에 자료형은 사용하지 않는다</li>
<li>한 메서드가 한 가지 기능만 담당하게 한다</li>
<li>메서드가 한 가지 기능을 하는지 확인하는 기준을 세운다</li>
<li>테스트를 작성하는 이유에 대해 본인의 경험을 토대로 정리해본다</li>
<li>처음부터 큰 단위의 테스트를 만들지 않는다</li>
</ul>
<p>1주차에 비해서 노력을 한 결과인지 코드 리뷰를 통한 발전때문인지 공통피드백에 있는 부분들을 상당히 신경을 쓴 편이다.
그럼에도 불구하고 부족한 부분들이 꽤나 있다.
하드 코딩 미흡, 변수 이름에 자료형을 사용하고 싶어하는 마음이 있었다.
메서드가 한 가지 기능을 하는지 확인하는 기준 세우기.
처음부터 큰 단위의 테스트를 만들지 말기!
1, 2주차 미션을 하면서 가장 어려워하는 부분이 바로 기능을 나눠서 작은 테스트와 구현을 조화롭게 사용하는 것이다.
앞으로는 작은 테스트와 구현, 커밋을 실천해보자.</p>
<h1 id="본론">본론</h1>
<hr>
<h2 id="문제-요구-사항">문제 요구 사항</h2>
<p>초간단 자동차 경주 게임을 구현한다.</p>
<ul>
<li>주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다.</li>
<li>각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다.</li>
<li>자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다.</li>
<li>사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다.</li>
<li>전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우이다.</li>
<li>자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다.</li>
<li>우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분한다.</li>
<li>사용자가 잘못된 값을 입력할 경우 <code>IllegalArgumentException</code>을 발생시킨 후 애플리케이션은 종료되어야 한다.</li>
</ul>
<h3 id="입출력-요구-사항"><strong>입출력 요구 사항</strong></h3>
<h3 id="입력"><strong>입력</strong></h3>
<ul>
<li>경주할 자동차 이름(이름은 쉼표(,) 기준으로 구분)</li>
</ul>
<pre><code>pobi,woni,jun
</code></pre><ul>
<li>시도할 횟수</li>
</ul>
<pre><code>5
</code></pre><h3 id="출력"><strong>출력</strong></h3>
<ul>
<li>차수별 실행 결과</li>
</ul>
<pre><code>pobi : --
woni : ----
jun : ---
</code></pre><ul>
<li>단독 우승자 안내 문구</li>
</ul>
<pre><code>최종 우승자 : pobi
</code></pre><ul>
<li>공동 우승자 안내 문구</li>
</ul>
<pre><code>최종 우승자 : pobi, jun
</code></pre><h3 id="실행-결과-예시"><strong>실행 결과 예시</strong></h3>
<pre><code>경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)
pobi,woni,jun
시도할 횟수는 몇 회인가요?
5

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

pobi : --
woni : -
jun : --

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

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

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

최종 우승자 : pobi, jun
</code></pre><h2 id="진행-과정">진행 과정</h2>
<p>지난 미션에서 시작하기에 앞서 시간이 너무 지체된 경험이 있었다.
앞으로도 마주칠 문제들을 어떻게 하면 빠르게 파악하고 기능 목록을 작성할 수 있을까? 에 대한 고민을 가지고 있었다.
1주차 미션을 끝나고 코드 리뷰를 하면서 MVC 패턴에 대해서 알게 되었다.
그래서 나도 아키텍처 설계에 대해서 생각하고 나만의 정리를 하였다.</p>
<p>MVC 가 무엇일까?
Model, View, Controller 구나
각자가 뜻하는 바는 무엇이지?
코드 리뷰를 하면서 도메인과 서비스도 봤는데 그건 무엇일까?
모델과 도메인이 다른건가? 컨트롤러와 서비스가 다른걸까?
나의 아키텍처는 우선 어떤 방식으로 하고싶니?
이러한 사고 과정을 거쳐 다음과 같이 정해졌다.</p>
<p><img src="https://velog.velcdn.com/images/dan_d/post/4b91c0e2-f22a-4fc8-81f7-b8d51709eff6/image.png" alt=""></p>
<p>Application -&gt; Controller
View &lt;- Controller -&gt; Service -&gt; Domain
Util, Config</p>
<p>이러한 형태로 구조를 최종 설계를 하였다.</p>
<p>기능 목록 작성에 대해서도 1차 미션 때 책에서 읽은 내용을 바탕으로 적용하려 했다.
처음에는 &quot;관점을 나누어서 해야지&quot; 라고 생각을 했다.
큰 틀은 개념(도메인)관점에서 생각하여 객체들을 정하고
명세(책임 주도 설계)관점에서 메세지들을 정리하여 기능 목록을 작성을 하려 했다.
과제가 시작되었고 절차에 맞춰서 진행해보았지만 생각처럼 진행이 매끄럽진 않았다.
좀 더 명시적인 방법이 필요했던 것 같아서 방법론에 대해서 정리를 하였다.</p>
<h3 id="2차-수정안">2차 수정안</h3>
<ol>
<li>핵심 기능 분석 및 객체 도출 : 무엇을 해야 하는가? 프로그램의 핵심 기능을 파악, 이를 수행하기 위한 객체를 구상</li>
<li>객체의 책임 할당 : 각 객체는 어떤 책임을 가져야 하는가? 각 객체가 자율적이고 독립적인 역할을 가질 수 있도록 책임 할당</li>
<li>객체 간 협력 정의 : 객체들이 서로 어떻게 소통할 것인가? 협력하는 방식 (메서드 호출, 데이터 전달 등)</li>
<li>의존성 주입(DI) 설계 : 어떻게 객체 간 결합도를 낮출 수 있을까?</li>
<li>팩토리 클래스 구성 : 객체 생성을 한 곳에서 관리하면 어떨까?</li>
<li>구현 및 테스트 : 이제 각 객체가 제대로 동작하는지 테스트해보자.</li>
</ol>
<p>기능파악 -&gt; 역할할당 -&gt; 협력정의 -&gt; DI 및 팩토리설계 -&gt; 구현 및 테스트</p>
<h3 id="구현-및-테스트-상세-단계">구현 및 테스트 상세 단계</h3>
<ol>
<li>클래스별 구현 : 각 클래스가 설계된 대로 잘 동작하는지 확인하며 코드를 작성해야 해, 단일 책임 원칙 준수</li>
<li>단위 테스트 작성 및 수행 : 각 클래스가 개별적으로 잘 동작하는지 확인해야 해
ex) Domain 객체에 대한 Test</li>
<li>객체 간 통합 테스트 : 객체들끼리 협력할 때도 잘 동작하는지 검증해야 해
ex) Service 에 대한 Test</li>
<li>UI와의 통합 테스트 : 사용자에게 보여질 결과도 올바르게 나오는지 확인해야 해
ex) View 에 대한 Test</li>
<li>예외 및 엣지 케이스 테스트 : 모든 상황에 잘 대응하도록 예외 상황도 꼼꼼히 테스트해야 해</li>
<li>최종 통합 테스트 및 리팩토링 : 전체 기능이 제대로 작동하는지 확인하고, 코드 개선이 필요하면 리팩토링하자</li>
</ol>
<blockquote>
<p>2차 수정안을 위와 같이 사고의 흐름을 정해두고 흐름이 끊겨도 확인하며 절차를 진행했다.
도움이 된 것 같은데 회고를 하면서 확인한 결과 전체적인 과정을 미션이나 다른 문제들을 해결하면서 보완해야할 것 같다.
결론적으로는 큰 틀을 잡아놓고서 시작하는 것은 초보자인 나에게 네비게이션과 같이 좋은 효과를 보여준 것 같다.
어느정도 몸에 체득이 될 때까지는 이렇게 절차를 작성하는 것도 좋은 것 같다고 생각한다.</p>
</blockquote>
<h3 id="racingcar-code">RacingCar Code</h3>
<pre><code class="language-java">  public static GameController createGameController() {
  InputView inputView = new InputView();
  OutputView outputView = new OutputView();

  GameBoard gameBoard = new GameBoard();
  RaceJudge raceJudge = new RaceJudge();

  GameService gameService = new GameService(gameBoard, raceJudge);

  return new GameController(inputView, outputView, gameService);
  }</code></pre>
<blockquote>
<p>의존성 주입을 하고 계속해서 의존성에 대해서 생각하다보니 누가 누구에게 의존을 하는지가 점점 보이기 시작했다.
그러면서 그렇다면 생성하고 주입하는 것을 한 곳에서 할 순 없을까? 란 생각으로 이런 설계를 하게 되었다.
이것이 좋은 것인지 나쁜 것인지는 아직까지 객관적으로는 모르지만 주관적으로 편했다.
그렇기 때문에 다음 미션에도 사용할 예정이다.</p>
</blockquote>
<pre><code class="language-java">private void playGame() {
    for (int i = 0; i &lt; totalRounds; i++) {  </code></pre>
<p>변수 i 에 대해서 round 같은 이름은 어떤가에 대해서 리뷰를 들었다.
좋은 생각이라 들면서도 정말 필요한가에 고민을 아직 하는 중이다.</p>
<pre><code class="language-java">public void move() {
    int randomValue = RandomNumberGenerator.generate(RANDOM_MIN, RANDOM_MAX);</code></pre>
<p>이 부분에 대해서 랜덤 값을 내부에 받는 것보다 외부에서 받는 것에 대한 고민을 구현하는 나도 했었고 지적도 받았다.
랜덤 값에 대해서는 외부에서 받는 것이 테스트하기에 좋을 것 같다는 판단이 든다.</p>
<pre><code class="language-java">    public void initializeGame(List&lt;String&gt; carNames) {
        List&lt;Car&gt; cars = carNames.stream()
                .map(Car::new)
                .toList();
        gameBoard.setupCars(cars);                                    
</code></pre>
<p>스트림에 대해서 노션에 간단히 정리를 해보고 처음 사용을 해봤다.</p>
<ul>
<li>배열이나 컬렉션의 요소들을 순차적으로 처리하거나 병렬 처리 가능</li>
<li>Java 8에서 도입된 데이터 처리 추상화</li>
<li>메서드 체이닝 방식, 지연 연산 방식 ( 필요한 때만 데이터 처리 불필요한 중간 결과 생성 안함)</li>
<li>데이터가 배열(int[], Stirng[]) → Arrays.stream(), List, Set→ 바로.stream()</li>
<li>map.keySet().stream(), map.values().stream(), map.entrySet().stream()
forEach 와 같이 많이 적용해보면서 장단점, 어떠할 때가 적시적소에 사용하기 용이한지에 대해 앞으로 배워나가야 한다.</li>
</ul>
<p>이 외에도</p>
<ul>
<li>정규식 추가 공부 및 Pattern, matcher 첫 사용</li>
<li>isEmpty, isBlank의 차이, strip 메소드 알게 됨</li>
<li>Message, values 상수화 사용</li>
<li>메서드 호출 순서를 놓침</li>
<li>@parameterizedtest 에 대한 부분</li>
<li>No newline at a end of file!! <del>(일부러 지웠던 마지막 공백라인이...컴퓨터 입장을 다시 생각해보는 계기가 됐다.)</del></li>
</ul>
<p>2주차에서 많은 것을 배울 수 있었다.
코드 리뷰 최고 👍</p>
<h2 id="성찰">성찰</h2>
<h3 id="디버깅과-테스트">디버깅과 테스트</h3>
<p>설계한대로 구현을 해나가며 기능 별 커밋에 신경을 썼다.
다만 테스트에 대해서는 생각을 정리를 안한 결과인지 이번 미션에서도 미흡한 부분이 컸고 숙제를 하듯이 구현을 끝낸 후에 했다.
지금 돌이켜보면 가장 부끄럽고 후회가 되는 점이다.
아직까지 디버깅보다 런을, 테스트보다 구현을
프리코스에서 요구하는 점들은 다 이유가 있다.
이유에 대해서 깊이 생각을 해봤다.
내 생각은 디버깅은 문제의 원인을 파악하는데 장점이 있는 것 같다.
그리고 테스트를 먼저 작성하는 것은 코드의 신뢰성을 높이고 명확한 요구사항 반영에 대한 장점이 있는 것 같다.
3주차에 잘 적용을 해봐야겠다.</p>
<h3 id="코드-리뷰">코드 리뷰</h3>
<p>이제 2번째 하는 코드 리뷰지만 처음과 별다른 차이없이 어려웠다
깃이나 커밋 등 여러가지들은 차이가 느껴지는데 왜 코드리뷰는 어려울까 생각을 해봤다</p>
<ol>
<li>자바 기본 문법 및 메소드 지식 부족</li>
<li>편한 것만 하려는 성향
어떤 코드는 술술 읽히고 어떤 사람의 코드는 의식의 흐름이 따라가질 못한다.
코드를 읽으면서 그 사람의 사고를 느낄 수 있는 것 같다는 생각을 했다.
세상에는 다양한 사람들이 많고 역지사지의 중요성을 살아가면서 느낀다.
코드 리뷰를 하면서 이러한 경험을 할 수 있는 것을 처음 알았고 상당히 힘들었다.
나와 비슷한 경우는 쉽게 읽히지만 다른 경우나 모르는 경우 어려워서 1주차 코드 리뷰에서는 대충 이런 느낌인 것만 알면 넘어갔다.
마치 내가 아는 것 처럼...
그렇게 힘든 길을 피한 결과 2주차 코드 리뷰를 할 때에도 다른 부분들보다 성장이 느렸던 것 같다.
그래서 이번엔 정면 돌파를 하기로 했고 며칠을 코드리뷰에 사용을 했다.
<del>(로또 미션 얼른 해야하는데...벌써 목요일이라고오!!)</del>
설명회에서 말한 <strong>내가 모르는 것을 아는 것</strong>이 왜 중요한지 다시 한 번 느꼈다.
그래서 빠르게 3주차 미션을 시작도 해야하지만 미뤄두고 코드리뷰에 집중을 했다. 🥲
그 결과 다른 분의 코드지만 마치 내 코드처럼 술술 읽히는 성과를 거둔 것 같다.
더불어 모르는 용법에 대해서도 재밌게 공부할 수 있었다.
앞으로 코드만 있으면 무엇이든 가능하겠다는 무서운 생각도 들긴 했다.</li>
</ol>
<h1 id="마무리">마무리</h1>
<hr>
<h2 id="아쉬운-점">아쉬운 점</h2>
<p>기억에 남는 것은</p>
<ol>
<li>README 체크박스 만들어두고 체크를 하지 않았다...</li>
<li>경주 게임인데 참가자가 한명일 경우를 생각 못한 것</li>
</ol>
<h2 id="다음-목표">다음 목표</h2>
<ul>
<li>체력이 곧 정신력이다. &quot;틈날 때마다 강한 체력을 기르자!&quot;</li>
<li>몰입, 메타인지, 집단지성, 설득(논리적글쓰기) 기존 목표를 꾸준히 각인하고 실천하기!</li>
</ul>
<blockquote>
<p>마지막으로 스스로에게 하고 싶은 말이자 여러분에게도 도움이 되고자 한 마디 남기고 이번 회고를 마치겠다.
&quot;Do what is hard, and your life will be easy. Do what is easy and your life will be hard.&quot;</p>
</blockquote>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 7기] 프리코스 1주차 회고]]></title>
            <link>https://velog.io/@dan_d/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-1%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dan_d/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-1%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Wed, 30 Oct 2024 14:15:56 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dan_d/post/1e631525-2368-4576-96e1-afc67fde1c1c/image.png" alt=""></p>
<p><a href="https://github.com/miniminjae92/java-calculator-7">1주차 과제 깃허브 링크</a>    </p>
<h1 id="시작하며">시작하며</h1>
<hr>
<h2 id="how-to-start">How to start?</h2>
<p>우연한 기회에 우아한테크코스 입학설명회를 시청하게 됐어요.</p>
<p>왜 성장하고 싶은가요? 남들을 따라서 하고 있나요?
생각에 대한 생각을 자주하시나요?</p>
<p>평소 가지고 있던 몰입, 메타인지, 집단지성, 명시적 논리적 글쓰기라는 키워드들에 꽂혀있던 저에게 설명회는 큰 파문으로 찾아왔어요.</p>
<p>&quot;그래! 프리코스는 누구나 참여 가능한데 한 번 도전해보자&quot;란 생각으로 아무것도 모르지만 뛰어 들었답니다.</p>
<p>우아한테크코스의 모토가 정말 마음에 들었어요.
특히 뛰어난 사람들과 같이 협업을 할 기회가 생긴다는 것이 너무 탐이 났죠.
시간이 지날수록 기대하고 욕심이 나는 것이 저뿐이 아니겠죠?
그러면 처음해보는 1주차 회고를 시작해 보겠습니다.</p>
<h2 id="1주차를-진행하며">1주차를 진행하며</h2>
<h3 id="개발-과정-요구-사항">개발 과정 요구 사항</h3>
<p>과제 진행, 기능, 프로그래밍 요구 사항 세 가지 모두 충족하기
기능 구현 전 기능 목록을 만들기
기능 단위로 커밋하는 방식으로 진행하기
기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현하기</p>
<ul>
<li>Git</li>
<li>IntellJ</li>
<li>Java JDK 21</li>
<li>README.md 마크 다운 다루기</li>
<li>AngularJS Git Commit Message Conventions</li>
<li>자바 코드 컨벤션 (Java Style Guide)</li>
</ul>
<p>1주차 과제가 진행되면서 필요한 사항들을 준비하게 되었어요.
Git 을 다루는 방법을 배우고 Github 계정을 만들었어요.<br>가장 도움이 많이 된 사이트를 첨부할게요. <a href="https://learngitbranching.js.org/?locale=ko">[Link]</a></p>
<p>우테코에서 제공해주는 제출가이드와 각종 안내문(링크)들을 보면서 하나씩 배워갔어요. (도움이 정말 많이 됩니다. 👍)
IntellJ 설치 및 적응, Java JDK 21 로 설정
우선 JDK 가 무엇인 지 23 버전으로 나오는 것을 고치고
git command 를 배운 것을 적용해보고
README 작성을 도대체 어떻게 하는거지?!! 라면서 우선 해보고
커밋을 하고 취소하고 넘어져보고 하면서 어찌저찌 과제를 시작하게 되었어요.
디스코드에서 함께 나누기와 토론, 구글에 많은 정보들이 많은 도움이 된 것 같아요.</p>
<p>특히 바로 시작을 안하고 <code>객체지향의사실과오해</code> 책을 읽으면서 객체지향에 사랑에 빠진 게 된 것 같아요. (강추입니다! 책 추천 받아요 ☺️☺️)</p>
<p>낯설었지만 설렘을 가지고 배우다보니 점점 익숙해진 것 같아요.
아직도 많이 부족하지만 점점 좋아지겠죠?</p>
<h3 id="과제-기능-요구-사항">과제 기능 요구 사항</h3>
<blockquote>
<p>입력한 문자열에서 숫자를 추출하여 더하는 계산기를 구현한다.</p>
</blockquote>
<ul>
<li>쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환한다.</li>
<li>예: &quot;&quot; =&gt; 0, &quot;1,2&quot; =&gt; 3, &quot;1,2,3&quot; =&gt; 6, &quot;1,2:3&quot; =&gt; 6</li>
<li>앞의 기본 구분자(쉼표, 콜론) 외에 커스텀 구분자를 지정할 수 있다. 커스텀 구분자는 문자열 앞부분의 &quot;//&quot;와 &quot;\n&quot; 사이에 위치하는 문자를 커스텀 구분자로 사용한다.</li>
<li>예를 들어 &quot;//;\n1;2;3&quot;과 같이 값을 입력할 경우 커스텀 구분자는 세미콜론(;)이며, 결과 값은 6이 반환되어야 한다.</li>
<li>사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시킨 후 애플리케이션은 종료되어야 한다.</li>
</ul>
<p><code>객체지향사실과오해</code>를 읽고 책임, 협력, 역할을 생각하면서 시작을 하려 했어요.
하지만 생각과 구현은 처음에는 갭이 컸어요.
자바라는 언어가 처음이고 너무 완벽하게 하려고 실수를 하면 안된다는 생각이 있었던 것 같아요.
프리코스 과정은 원래라는 건 지워버리고 부딪힘에 망설임이 없어야 한다고 생각해요. (모두 화이팅입니다!🫡)</p>
<h1 id="본론">본론</h1>
<p>무엇을 해야할 지 몰라서 과제에 적힌 것을 실천하는 것에 집중을 했어요.</p>
<p><strong>과제 진행, 기능, 프로그래밍 요구 사항 세 가지 모두 충족하기</strong>
3가지 부분들을 꼼꼼히 읽어보면서 하나씩 했어요.
그리고 책을 바탕으로 먼저 객체 메세지 흐름을 만들어봤어요.</p>
<blockquote>
<p>객체 메세지 흐름
Main -&gt; InputHandler : 문자열 입력 받아줘
InputHandler -&gt; Main : 입력 문자열 받아줘
Main -&gt; Parser : 문자열 파싱해줘
Parser -&gt; Main : 파싱된 문자열 배열 받아줘
Main -&gt; Validator : 양수 검증해줘
Validator -&gt; Main : 검증된 양수 배열 받아줘
Main -&gt; Calculator : 양수 배열 합해줘
Calculator -&gt; Main : 계산 결과 받아줘
Main -&gt; Printer : 계산 결과 출력해줘</p>
</blockquote>
<p>이것을 바탕으로 기능 목록도 작성을 하고 프로그래밍에 들어갔습니다.</p>
<blockquote>
<p>기능 목록 List
Application(main)
InputHandler
Parser
Validator
Calculator
Printer</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dan_d/post/3e4df9f5-9502-466f-bbab-69962a902f8c/image.png" alt=""></p>
<hr>
<h3 id="1주차에서-작성한-코드">1주차에서 작성한 코드</h3>
<pre><code class="language-java">package calculator;

public class Application {
    public static void main(String[] args) {
        InputHandler inputHandler = new InputHandler();
        String userInput = inputHandler.getInput();

        Parser parser = new Parser();
        String[] parsedTokens = parser.parseInput(userInput);

        PositiveNumberValidator validator = new PositiveNumberValidator();
        int[] validatedNumbers = validator.validateNumber(parsedTokens);

        SumCalculator calculator = new SumCalculator();
        int result = calculator.calculateSum(validatedNumbers);

        Printer printer = new Printer();
        printer.printResult(result);
    }
}</code></pre>
<p>MVC 패턴에 대해서 모르는 상태에서 작성을 했어요.
빈 공백을 넣어도 되는 지 클래스 선언, 메소드 사용 등 전부 새로 배워가면서 시작했어요.
객체지향이 아니라 절차지향으로 작성이 된 것 같아요. 🥲</p>
<pre><code class="language-java">package calculator;

import camp.nextstep.edu.missionutils.Console;

public class InputHandler {
    public String getInput() {
        System.out.println(&quot;덧셈할 문자열을 입력해 주세요.&quot;);
        return Console.readLine();
    }
}</code></pre>
<p>화면에 처음 출력을 하면서부터 재미를 느끼기 시작했어요.</p>
<pre><code class="language-java">package calculator;

public class Printer {
    public void printResult(int result) {
        System.out.println(&quot;결과 : &quot; + result);
    }
}</code></pre>
<p>최대한 단일 책임을 주려고 설계에 많은 시간을 들였어요.</p>
<pre><code>package calculator;

import java.util.ArrayList;
import java.util.List;

public class Parser {
    //기본 구분자, 참조/내용 변경 불가능
    private final List&lt;String&gt; defaultDelimiters = List.of(&quot;,&quot;,&quot;:&quot;);
    //커스텀 구분자, 참조 변경 불가능
    private final List&lt;String&gt; customDelimiters = new ArrayList&lt;&gt;();

    public String[] parseInput(String input) {
        if (input.startsWith(&quot;//&quot;)){
            input = extractCustomDelimiter(input);
            String regex = String.join(&quot;|&quot;, defaultDelimiters) + &quot;|&quot; + String.join(&quot;|&quot;, customDelimiters);
            return input.split(regex);
        } else{
            String regex = String.join(&quot;|&quot;, defaultDelimiters);
            return input.split(regex);
        }
    }
    private String extractCustomDelimiter(String input) {
        int delimiterEndIndex = input.indexOf(&quot;\\n&quot;);

        if (delimiterEndIndex == -1)
            throw new IllegalArgumentException(&quot;Invalid delimiter: &quot; + input);

        String customDelimiter = input.substring(2, delimiterEndIndex);

        if (!customDelimiters.contains(customDelimiter)){
            customDelimiters.add(customDelimiter);
        }

        return input.substring(delimiterEndIndex+2);
    }
}</code></pre><p>private, final, 네이밍이 너무 어려웠어요.
파이썬을 조금 배워둔 것이 도움이 많이 되었어요.</p>
<pre><code class="language-java">package calculator;

public class PositiveNumberValidator {
    public int[] validateNumber(String[] tokens) {
        // 빈 문자열 or 숫자가 없는 경우 예외 처리
        if (tokens == null || tokens.length == 1 &amp;&amp; tokens[0].isEmpty()) {
            return new int[]{0};
        }

        int[] validatedNumbers = new int[tokens.length];

        for (int i = 0; i &lt; tokens.length; i++) {
            try {
                int number = Integer.parseInt(tokens[i]);
                if (number &gt; 0) {
                    validatedNumbers[i] = number;
                } else {
                    throw new IllegalArgumentException(&quot;The number must be positive: &quot; + tokens[i]);
                }
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException(&quot;Invalid number format: &quot; + tokens[i], e);
            }
        }
        return validatedNumbers;
    }
}</code></pre>
<p>여러 메서드를 배우고 예외를 처리하는 경험을 했어요.</p>
<pre><code class="language-java">package calculator;

public class SumCalculator {
    public int calculateSum(int[] numbers) {
        int sum = 0;
        for (int number : numbers) {
            sum += number;
        }
        return sum;
    }
}</code></pre>
<pre><code>package calculator;

import camp.nextstep.edu.missionutils.test.NsTest;
import org.junit.jupiter.api.Test;

import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class ApplicationTest extends NsTest {
    @Test
    void 커스텀_구분자_사용() {
        assertSimpleTest(() -&gt; {
            run(&quot;//;\\n1&quot;);
            assertThat(output()).contains(&quot;결과 : 1&quot;);
        });
    }

    @Test
    void 예외_테스트() {
        assertSimpleTest(() -&gt;
            assertThatThrownBy(() -&gt; runException(&quot;-1,2,3&quot;))
                .isInstanceOf(IllegalArgumentException.class)
        );
    }

    @Override
    public void runMain() {
        Application.main(new String[]{});
    }
}</code></pre><p>./gradlew clean test 를 돌리면서 수정을 할 수 있었어요.
이때 테스트에 대해서 중요성을 느끼게 됐어요.</p>
<h3 id="예외-처리">예외 처리</h3>
<ul>
<li>입력된 문자열에서 숫자를 추출하여 더하는 계산기가 요구 사항이기때문에 숫자가 없거나 빈 문자열일 경우 0으로 가정한다.</li>
<li>사용자가 잘못된 값을 입력한 경우 IllegaArgumentException을 발생시킨 후 애플리케이션 종료</li>
<li>구분자는 잘못된 입력에서 제외로 본다.</li>
<li>음수의 경우</li>
<li>커스텀 구분자의 /// or \n\n 경우</li>
<li>숫자가 없는 경우, 커스텀구분자를 //x\n 이 있을 때 x 가 여러 개의 특수문자일 경우 하나로 볼지 여러 개로 볼지 결정.</li>
</ul>
<p>당시 처음 예외 사항에 대해서 생각해봐서 미흡했어요.
아직도 가장 어려운 부분이 이 예외 사항인 것 같아요.
요구 사항과 예외 사항을 만족하느 것이 중요한 것 같아요.</p>
<hr>
<h2 id="1주차-끝나자마자-기록했던-부분">1주차 끝나자마자 기록했던 부분</h2>
<ul>
<li>README 작성에 대해서 공부가 필요하고 Markdown 형식에 익숙해져야한다는 것을 느꼈습니다.</li>
<li>Git, Github 을 이용한 개발을 진행하면서 Mac, Window 다양한 플랫폼과 기기에서 편리하게 할 수 있는 것을 느꼈고 협업시에는 적극 활용해야겠다고 느꼈습니다.</li>
<li>새로운 IDE 및 언어에 겁을 먹지말고 적극적으로 다가감으로써 빠르게 적응할 수 있어야한다고 생각하게 되었습니다.</li>
<li>기능 목록 하나씩 구현을 하면서 테스트와 커밋에 대해서 미흡함을 느꼈습니다, 더욱 복잡한 개발을 할 때에 이런식으로 하면 어려움이 생길 것이라고 느꼈습니다.</li>
<li>객체지향의 사실과 오해를 읽으면서 객체의 메세지와 책임을 생각하며 기능 목록을 만들어보고 싶어 시간을 많이 투자를 했습니다, 다만 추상적으로 실제로 만드는 부분에서 많이 경험을 해봐야겠다고 느꼈습니다.</li>
<li>생각한 것을 구현하는데에 있어 기본 형식과 Method에 대한 공부가 부족하여 시간이 많이 지체되었습니다.</li>
<li>출력: 출력 : 정확한 요구사항 검토가 필요한 것을 느꼈습니다.
Validator, Calculator, Printer, ErrorHandler 를 기능 목록에 넣어두고 실제 구현에서는 급하고 낯설단 이유로 그대로 진행을 안했습니다, 계획대로 리팩토링하면서 본래 의도대로 진행하고 다음 과제부턴 좀 더 신경을 쓰도록 해야겠습니다.</li>
<li>README 수정, git commit 등 제출해야한다는 생각에 실패를 피할려고 하는 모습이 있었고 그로인해 시간이 많이 소요되었습니다, 지금은 배우는 단계이므로 조금 더 거침없이 부딪혀가는 모습을 가지고 프리코스에 임해야겠다고 느꼈습니다.</li>
<li>Markdown 들여쓰기가 원하는대로 나오질 않아 커밋을 여러 번 진행하게 되었고 그것들을 합치기 위해서 rebase, squash, push --force 를 처음 사용해봤습니다, 개인 과제라서 괜찮은 행동인 것 같지만 여러 방면으로 무엇을 조심하고 어떻게 하면 좋은지에 대해서 검색을 통해서 공부를 해야겠다고 이번 기회에 느꼈습니다. 이번 프리코스에서 느낀점같은 회고는 README 말고 다른 곳에 해야하는지에 대해선 1주차 프리코스가 끝나고 다른 동료들을 보면서 깨달아야겠습니다.</li>
<li>ErrorHandler 클래스를 만들다가 전체적으로 꼬임이 발생해서 에러핸들러는 기능 목록에서 지웠다. 현재 지식이 부족하여 발생한 것으로 생각된다. 자바 책과 여러 지식을 쌓으면서 다시 회고를 통해 돌아봐야겠다. 또한 생각해보니 메인을 통한 중앙집중식으로 프로그램을 만든 것 같다는 생각이 들었다. 그래서 인풋핸들러가 파서에게 파서가 검증기, 검증기가 계산기 등 객체의 협력 구조로 바꿀려고 생각을 했다. 하지만 의존성에 대해서 아직 지식이 미비하여 무엇이 더 의존성이 낮고 여러모로 좋은지에 대해서 정확하게 모르겠어서 지금의 로직을 지키고 이것또한 회고를 통해서 꼭 다시 돌이켜 볼 예정이다.</li>
<li>클래스 인스턴스를 메인 밑에 함께 형성할 지 로직의 순서대로 형성할 지에 대해서도 고민을 하다가 위와 같은 이유로 유지를 하였다. 한편으로는 프로그램이 커지고 DI를 하게된다면 모으는 것이 보기에 좋겠단 생각을 하였다.</li>
</ul>
<hr>
<h1 id="마무리하며">마무리하며</h1>
<h2 id="1차-공통-피드백">1차 공통 피드백</h2>
<blockquote>
<p>1주 차 미션의 학습 목표는 개발 환경과 프로그래밍 언어에 익숙해지는 것이었습니다.</p>
</blockquote>
<ul>
<li>요구 사항을 정확하게 준수한다</li>
<li>기본적인 Git 명령어를 숙지한다</li>
<li>Git으로 관리할 자원을 고려한다</li>
<li>커밋 메시지를 의미 있게 작성한다</li>
<li>오류를 찾을 때 출력 함수 대신 디버거를 사용한다</li>
<li>이름을 통해 의도를 드러낸다</li>
<li>축약하지 않는다</li>
<li>공백도 코딩 컨벤션이다</li>
<li>스페이스와 탭을 혼용하지 않는다</li>
<li>의미 없는 주석을 달지 않는다</li>
<li>코드 포매팅을 사용한다</li>
<li>Java에서 제공하는 API를 적극 활용한다</li>
<li>배열 대신 컬렉션을 사용한다</li>
</ul>
<p>개발 환경과 프로그래밍 언어에 익숙해지는 것이 학습 목표였어요.
요구 사항을 따르기 위해서 노력을 하다보면 자연스럽게 목표를 이룰 수 있게 과정이 잘 짜여진 것 같아요.
1차 공통 피드백에 있는 내용들이 지금 봐도 매우 중요한 부분들이라고 느껴지네요.</p>
<blockquote>
<p>일주일 동안에 많은 것을 할 수 있었고 가장 큰 것은 <strong>자기 주도적으로 학습을 얼마든지 할 수 있다는 용기</strong> 인 것 같아요.</p>
</blockquote>
<p>결과를 떠나서 프리코스 적극 추천합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[도커가 뭘까?]]></title>
            <link>https://velog.io/@dan_d/%EB%8F%84%EC%BB%A4%EA%B0%80-%EB%AD%98%EA%B9%8C</link>
            <guid>https://velog.io/@dan_d/%EB%8F%84%EC%BB%A4%EA%B0%80-%EB%AD%98%EA%B9%8C</guid>
            <pubDate>Sat, 05 Oct 2024 07:16:02 GMT</pubDate>
            <description><![CDATA[<p>저는 아직 도커에 대해서 공부하기 전이지만 추후에 공부하고 나서 지금의 글과 비교를 해보고싶어 작성을 합니다.</p>
<p>프로그래밍 언어 차원에서의 가상환경을 시스템적인 차원에서 컨테이너라는 가상머신으로 사용하는 것을 도커라고 하는 것 같습니다.</p>
<p>컨테이너를 배에 실어서 운송하듯이, Docker는 소프트웨어 컨테이너를 시스템에 실어 어디서든 실행할 수 있도록 해주는 도구 같습니다.</p>
<p>쿠버네티스는 여러 가지 도커, 컨테이너들을 사용하기 위해 있는 것 같습니다.</p>
<p>도커는 커널을 사용자의 OS의 커널을 이용하여 필수적인 것들만 설치를 하여 가볍고 협업하거나 시험 개발하기 좋을 것 같습니다.</p>
<p>커널은 하드웨어로 가는 길목같이 느껴집니다.
운영체제(Operating System)은 커널+유틸리티+사용자인터페이스로 이뤄져 있는 것 같습니다.</p>
<p>모놀리식 커널과 마이크로식 커널의 차이점은 비유를 하자면 공산주의(중앙 집중형 시스템)와 자유주의(분권형 시스템)처럼 느껴집니다.</p>
<p>예를 들어 모놀리식 커널은 모든 자원과 기능을 중앙에서 통제합니다. 커널이 모든 주요 역할을 담당하며, 하나의 큰 시스템으로 작동합니다.
이는 중앙에서 계획하고 모든 자원을 통제하는 중앙 집중형 정치 체제(공산주의)에 비유할 수 있습니다. 모든 것이 중앙 권력에 의해 계획되고 실행되며, 자원이 한 곳에서 관리되는 체제입니다.
장점: 빠른 결정과 일관성을 유지할 수 있고, 전체 시스템이 조율되기 때문에 효율성이 높습니다.
단점: 중앙에서의 복잡성이 증가하고, 중앙에 문제가 생기면 전체 시스템에 문제가 발생할 수 있습니다.</p>
<p>마이크로커널은 커널이 최소한의 역할만 하고, 나머지 기능들은 분산된 모듈들이 자율적으로 관리합니다. 커널은 기본적인 관리만 담당하고, 대부분의 작업은 독립적인 구성 요소들이 수행합니다.
이는 자유주의나 분권형 정치 체제에 비유할 수 있습니다. 각 구성 요소들이 독립적으로 운영되고, 중앙에서 모든 것을 통제하기보다는 각 지역이나 조직이 자율적으로 관리하는 방식입니다.
장점: 시스템이 더 유연하고, 각 구성 요소가 독립적으로 운영되므로, 하나의 문제가 전체 시스템을 무너뜨리지 않습니다.
단점: 구성 요소들 간의 조정이 필요하고, 모듈 간 통신 오버헤드로 인해 성능이 저하될 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[노크도 없이 들어온]]></title>
            <link>https://velog.io/@dan_d/%EB%85%B8%ED%81%AC%EB%8F%84-%EC%97%86%EC%9D%B4-%EB%93%A4%EC%96%B4%EC%98%A8</link>
            <guid>https://velog.io/@dan_d/%EB%85%B8%ED%81%AC%EB%8F%84-%EC%97%86%EC%9D%B4-%EB%93%A4%EC%96%B4%EC%98%A8</guid>
            <pubDate>Fri, 04 Oct 2024 06:47:17 GMT</pubDate>
            <description><![CDATA[<p>2의 제곱은 10씩 증가할 때마다 1,000배씩 늘어나고, 상용로그는 3씩, 자연로그는 6씩 증가할 때마다 1,000배씩 커진다.</p>
<p>멀티프로세스 - 다중 코어 - parallel, </p>
<p>멀티쓰레싱 - 다중 쓰레드 - concrrency</p>
<p>tensor 에서 dimension = 0, 1</p>
<p>0 일때 모든 행을 사용한다, 행 방향을 따라가면서 </p>
<p>1 일때 모든 열을 사용한다, 열 방향을 따라가면서</p>
<p>error &lt; loss &lt; cost</p>
<p>SELECT - Field 를 선택, WHERE - Row 를 선택</p>
<p>산술, 비교, 논리, 집합, SQL 연산자들은 조건</p>
<p>조건은 결과(Instance, Row)를 원하는 형태로 Change 변경 하는 도구</p>
<p>행렬 연산할 때 벡터와 벡터를 곱 연산 시 90도 회전 후 행끼리 면적을 겹친다, 겹친 면적을 더한 값을 하나의 곱 연산 밸류로 만든다, 첫번째 벡터의 한 행마다 다른 벡터의 모든 행을 겹친다.</p>
<p>django web application</p>
<p>&lt;사용자 관점&gt;</p>
<ol>
<li>클라이언트 -&gt; 2. URL 매핑 -&gt; 3. 뷰 함수 실행 -&gt; 4. 모델/DB 조회 -&gt; 5. 템플릿 렌더링 -&gt; 6. HTTP 응답</li>
</ol>
<p>&lt;서버 관점&gt;</p>
<p>거꾸로 생각하면 됨</p>
<p>필요조건은 &quot;없으면 안 되는 조건&quot;으로,  최소한의 요구사항
충분조건은 &quot;있으면 반드시 되는 조건&quot;으로,  결과를 확실하게 만드는 조건</p>
<ul>
<li>필요조건 (Necessary Condition): N으로 시작하므로 &quot;No without it&quot; — 그것 없이는 안 된다.</li>
<li>충분조건 (Sufficient Condition): S로 시작하므로 &quot;Sure with it&quot; — 그것이 있으면 확실하다.</li>
</ul>
<p>우연히 일어난다고 생각하고 관찰했는데 관찰 결과가 우연히 일어날 확률이 엄청 낮으면 이것은 우연이 아니다</p>
<p>낮은 확률(EX: NEWS)을 이해하고 측정하는 것은 매우 중요하다.</p>
<p>확률분포에서 기각역 안에서 발생하는 오류가 제 1종, 밖에 분포하는 것을 잘못 판단하면 제 2종오류</p>
<p>Small data(통계): 절차중심
Big data(머신러닝): 결과중심</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[통계에 대한 나의 이해]]></title>
            <link>https://velog.io/@dan_d/%ED%86%B5%EA%B3%84%EC%97%90-%EB%8C%80%ED%95%9C-%EB%82%98%EC%9D%98-%EC%9D%B4%ED%95%B4</link>
            <guid>https://velog.io/@dan_d/%ED%86%B5%EA%B3%84%EC%97%90-%EB%8C%80%ED%95%9C-%EB%82%98%EC%9D%98-%EC%9D%B4%ED%95%B4</guid>
            <pubDate>Fri, 04 Oct 2024 06:24:37 GMT</pubDate>
            <description><![CDATA[<p>&quot;이 세상 모든 것을 측정할 수 없다&quot;는 것에서 통계는 시작되었다.
그러므로 이 세상은 불확실하다.
우리는 불확실한 세상속에서 확실한 세상을 꿈꾼다.
불확실한 세상에서 세상의 일부를 이용해서 세상 전체를 예측하기 위해 통계가 시작되었다.</p>
<p>모집단(Population): 내가 관심있는 집단의 전체
표본(Sample): 랜덤하게 잘 섞인 모집단을 대표할 수 있는 일부
ex) 잘 섞어서 간 보기</p>
<p>내가 관심있는 집단에서 표본을 추출해서 표본의 통계량을 구하고 분석하여 모집단을 추정 및 검증을 한다.</p>
<p>우리는 낮은 확률에 관심을 가져야한다.
우리는 이것이 &quot;우연이 아니다&quot;란 것을 밝히고자 가설 검정을 한다.</p>
<p>귀무가설(H_0): Null Hypothesis ex) 아무 효과가 없는 것
대립가설(H_1): 밝히고자 하는 가설</p>
<p>우리가 궁금한 것은 우연하다란 조건에서 우연히 일어날 가능성이 매우 낮으면 그것은 유의하다, 즉 효과가 있다, 귀무가설을 기각하기 위한 것이다.</p>
<p>모집단의 확률 분포가 정규분포를 따른다면 기각역 안에서 발생하는 오류가 제 1종오류, 기각역 밖에 분포하는 것을 잘못 판단하면 그것은 제 2종오류이다.</p>
<p>무죄인 사람은 유죄, 유죄인 사람은 무죄
무엇이 더 치명적인가
무죄추정의 원칙, 제 1종 오류를 더 중요하게 취급</p>
<p>양품을 불량으로, 불량을 양품으로
암이 아닌 사람을 암진단, 암인 사람을 건강 진단
무엇이 더 치명적인가
제 2종 오류를 중요하게 생각해야하는 이유</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[빅데이터의 이해와 활용]]></title>
            <link>https://velog.io/@dan_d/%EB%B9%85%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%9D%98-%EC%9D%B4%ED%95%B4%EC%99%80-%ED%99%9C%EC%9A%A9</link>
            <guid>https://velog.io/@dan_d/%EB%B9%85%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%9D%98-%EC%9D%B4%ED%95%B4%EC%99%80-%ED%99%9C%EC%9A%A9</guid>
            <pubDate>Fri, 04 Oct 2024 06:09:42 GMT</pubDate>
            <description><![CDATA[<p>big data: 크고 다양하고 빠르게 생산<em>유통</em>소비되는 특성을 가진다.</p>
<p>무슨 활동이든 흔적이 남고 그것은 데이터가 된다. 수집, 저장된 빅데이터에서 Insight 통찰을 얻어야한다.</p>
<p>DIKW 피라미드: 물(data) -&gt; 생수병의 물(infomation) -&gt; 우물안에 물(knowledge) -&gt; 우물을 파는 법(wisdom)</p>
<p>빅데이터의 역사: 인쇄의 대중화-&gt;책,도서관-&gt;인터넷, 검색의 대중화-&gt;스마트폰, 모빌리티, 메타버스</p>
<p>5V: Volume, Variety, Velocity, Veracity, Value</p>
<p>빅데이터에는 숨겨진 패턴이 존재한다.</p>
<p>빅데이터를 수집, 저장을 하고 넘어가서 빅데이터 기술(데이터시각화, 데이터분석, 머신러닝, 딥러닝)을 사용해서 숨겨진 패턴을 찾는 것이 핵심이다.</p>
<p>빅데이터는 21세기 원유이다.</p>
<p>빅데이터 이전의 통계에서는 데이터 수집의 어려움때문에 small data를 이용했다.</p>
<p>표본조사, 실험계획법의 발달, 양질의 데이터 + 공정한 통계모형-&gt;의미있는 결과 도출, 절차중심</p>
<p>빅데이터는 인과구조를 따지지 않고 상관관계, 결과중심 즉 예측력만 좋으면 된다.</p>
<p>분산이 낮고 편의(편향성)이 존재한다.</p>
<p>Natural Language Processing</p>
<p>활용: 텍스트요약과 분류, 감성분석, 의미연결망분석, 기계번역, 질의응답과 챗봇, 음성인식</p>
<p>핵심: 토큰화, 정규화, 단어임베딩, 단어의 빈도수, 언어 모형</p>
<p>데이터의 시각화의 핵심: 정직(Honesty), 간결(Simplicity), 정확(Accurancy)</p>
<p>시간 시각화: trend 파악(경향) ex) 선, 막대, 점, 누적, 버블그래프</p>
<p>참고: 한스로슬링, 팩트풀리스, 갭마인드</p>
<p>텍스트 시각화: WordCloud, WordTree</p>
<p>소셜네트워크시각화: direction, undirection, Node &amp; Vertice, Edge &amp; link, Degree of edge, weighted graph</p>
<p>시각화 도구</p>
<p>R - ggplot2</p>
<p>python - pandas, matplotlib, seabon, plotly</p>
<p>more interactive-&gt; Javascript + Processing ex) D3.js</p>
<p>Tableau, PowerBI-Azure</p>
<p>Recommendation System : Associative Analysis method(모집단), Collabolative Filtering method(개인)</p>
<p>연관성 분석: if-then, 규칙이 있을 때, X-&gt;Y, 규칙=P(XY교집합) 
지지도: 규칙/전체
신뢰도: 규칙/원인
향상도: 신뢰도/결과</p>
<p>향상도=1 -&gt; 독립적인 관계
향상도&gt;1 -&gt; 양의 상관 관계
향상도&lt;1 -&gt; 음의 상관 관계</p>
<p>코사인유사도: 벡터가 크기와 상관없이 같은 방향인 지가 중요
상관계수: 방향성보다 편차가 얼마나 비슷한 지가 중요</p>
<p>데이터의 희소성
고객의 수 &gt; 상품의 수 : 상품 중심 방법, 해석력 좋음
고객의 수 &lt; 상품의 수 : 고객 중심 방법, 놀라움 좋음</p>
<p>상대적 평가: 모델 간의 비교를 통해 예측력(성능)을 평가함.
절대적 평가: 각 모델을 독립적으로 평가하며, 비즈니스 성과나 기대되는 효과를 평가하는 데 적합
그러므로 추후 프로젝트 할 때에 상대적 평가와 절대적 평가를 적재적소에 모두를 이용하는 것이 중요한 포인트일 것</p>
<p>정확도와 재현율: 내가 좋아하는 것을 추천할 확률이 높으면 기회비용이 낮게 될 확률이 높다, 즉 재현율이 낮을 수 있다.
반면에 실제로 내가 좋아하는 것을 추천을 모두 한 경우에 재현율이 높지만 그만큼 많은 것을 추천해서 정확도가 낮아질 수 있다. 그렇다면 재현율도 높고 정확도도 높은 것이 가능할까?</p>
<p>추천 시스템이 정확도가 높다면 신뢰도가 높아질 것이고
재현율이 높다면 다양하고 새로운 것을 추천할 수 있기 때문에 지루함 방지가 될 것이다.</p>
<p>이러한 추천 시스템 자체를 평가하기 위해서는 A/B 검증이 필요하다.
ex) 추천시스템을 사용한 경우와 아닌 경우의 매출 비교
하지만 A/B 검증을 하기 위해서는 기업은 비용을 감수해야 한다. 추천시스템이 효과적인 경우 적용되지 않은 그룹의 매출 감소를 감수해야 한다. 따라서 상대적 평가를 이용하고, 마지막으로 절대적 평가를 하는 것이 좋다고 본다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[나만의 텐키리스 키보드 숫자 손가락 배치]]></title>
            <link>https://velog.io/@dan_d/%EB%82%98%EB%A7%8C%EC%9D%98-%ED%85%90%ED%82%A4%EB%A6%AC%EC%8A%A4-%ED%82%A4%EB%B3%B4%EB%93%9C-%EC%88%AB%EC%9E%90-%EC%86%90%EA%B0%80%EB%9D%BD-%EB%B0%B0%EC%B9%98</link>
            <guid>https://velog.io/@dan_d/%EB%82%98%EB%A7%8C%EC%9D%98-%ED%85%90%ED%82%A4%EB%A6%AC%EC%8A%A4-%ED%82%A4%EB%B3%B4%EB%93%9C-%EC%88%AB%EC%9E%90-%EC%86%90%EA%B0%80%EB%9D%BD-%EB%B0%B0%EC%B9%98</guid>
            <pubDate>Sat, 29 Jun 2024 01:56:13 GMT</pubDate>
            <description><![CDATA[<p>컴퓨터를 다룸에 있어 상단의 숫자/특수기호에 손가락을 지정하여 익숙해질 필요가 있다.</p>
<p>텐키리스 키보드를 사용하면서 넘버패드가 없기때문에 숫자 입력할 때 불편함을 느꼈다.</p>
<p>나는 하기 싫은 일을 해야 실력이 늘어난다는 생각을 가지고 있다. 숫자 or 특수문자를 입력할 때 키보드로 눈이 향하는데 왜냐하면 보지 않으면 불편하기 때문이다.</p>
<p>게임을 많이 했기때문에 (WASD) 1 버튼에 약지가 자꾸 간다.</p>
<p>1, 2, 3, 4 &emsp;&emsp;&emsp;&emsp;&emsp;소지부터 검지
5, 6 &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;검지
7, 8 &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;검지 중지
9, 0 &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;약지
-, =, Backspace &emsp;&emsp;소지</p>
<p>자꾸 의식하고 실행해보자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이산수학 공부 중 궁금한 것들]]></title>
            <link>https://velog.io/@dan_d/%EC%88%98%EC%97%B4%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@dan_d/%EC%88%98%EC%97%B4%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Tue, 30 Apr 2024 07:31:41 GMT</pubDate>
            <description><![CDATA[<p>수열이란 정의역 -&gt; 자연수, 공역 -&gt; 실수 인 함수이다.</p>
<p>순열과 조합 - 뽑아서 줄 세우기, 뽑아서 줄 세우기 취소
원순열은 직순열로 변환하여 계산</p>
]]></description>
        </item>
    </channel>
</rss>