<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>back_end_sc.log</title>
        <link>https://velog.io/</link>
        <description>열심히 노력하는 백엔드입니다.</description>
        <lastBuildDate>Sun, 08 Mar 2026 06:38:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>back_end_sc.log</title>
            <url>https://velog.velcdn.com/images/back_end_sc/profile/1d84b4c3-c186-4c25-8fbd-f69bdd8c9ceb/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. back_end_sc.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/back_end_sc" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[TDD 실습 (4)]]></title>
            <link>https://velog.io/@back_end_sc/TDD-%EC%8B%A4%EC%8A%B5-4</link>
            <guid>https://velog.io/@back_end_sc/TDD-%EC%8B%A4%EC%8A%B5-4</guid>
            <pubDate>Sun, 08 Mar 2026 06:38:25 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>메모 업데이트</p>
<ul>
<li><p>Red</p>
<ul>
<li><p>메모 업데이트에 대한 필수 값 누락, 검증에 대한 테스트 진행</p>
<ul>
<li>id 필요</li>
<li>id 타입 / 범위 검증</li>
<li>수정할 데이터 필요<ul>
<li>title</li>
<li>period</li>
<li>content</li>
<li>priority</li>
<li>isSuccess</li>
</ul>
</li>
<li>존재하지 않는 id 에러</li>
<li>수정 성공 시 수정된 객체 반환</li>
</ul>
</li>
<li><p>코드</p>
<pre><code class="language-python">  const { updateMemo } = require(&quot;../../src/memo/updateMemo&quot;);

  describe(&quot;updateMemo&quot;, () =&gt; {
    test(&quot;정상 id와 수정값이면 memo 수정에 성공한다&quot;, async () =&gt; {
      const result = await updateMemo(1, {
        title: &quot;수정된 메모 제목&quot;,
        content: &quot;수정된 메모 내용&quot;,
        priority: 5,
        isSuccess: true,
      });

      expect(result).toMatchObject({
        id: 1,
        title: &quot;수정된 메모 제목&quot;,
        content: &quot;수정된 메모 내용&quot;,
        priority: 5,
        isSuccess: true,
      });
    });

    test(&quot;id가 없으면 에러 발생&quot;, async () =&gt; {
      await expect(
        updateMemo(undefined, {
          title: &quot;수정 제목&quot;,
        }),
      ).rejects.toThrow(&quot;id&quot;);
    });

    test(&quot;id가 문자열이면 에러 발생&quot;, async () =&gt; {
      await expect(
        updateMemo(&quot;1&quot;, {
          title: &quot;수정 제목&quot;,
        }),
      ).rejects.toThrow(&quot;id&quot;);
    });

    test(&quot;id가 0이면 에러 발생&quot;, async () =&gt; {
      await expect(
        updateMemo(0, {
          title: &quot;수정 제목&quot;,
        }),
      ).rejects.toThrow(&quot;id&quot;);
    });

    test(&quot;update data가 없으면 에러 발생&quot;, async () =&gt; {
      await expect(updateMemo(1)).rejects.toThrow(&quot;data&quot;);
    });

    test(&quot;존재하지 않는 id이면 에러 발생&quot;, async () =&gt; {
      await expect(
        updateMemo(999, {
          title: &quot;수정 제목&quot;,
        }),
      ).rejects.toThrow(&quot;memo&quot;);
    });

    test(&quot;title이 빈 문자열이면 에러 발생&quot;, async () =&gt; {
      await expect(
        updateMemo(1, {
          title: &quot;&quot;,
        }),
      ).rejects.toThrow(&quot;title&quot;);
    });

    test(&quot;content가 공백만 있으면 에러 발생&quot;, async () =&gt; {
      await expect(
        updateMemo(1, {
          content: &quot;   &quot;,
        }),
      ).rejects.toThrow(&quot;content&quot;);
    });

    test(&quot;priority가 범위를 벗어나면 에러 발생&quot;, async () =&gt; {
      await expect(
        updateMemo(1, {
          priority: 6,
        }),
      ).rejects.toThrow(&quot;priority&quot;);
    });

    test(&quot;isSuccess가 boolean이 아니면 에러 발생&quot;, async () =&gt; {
      await expect(
        updateMemo(1, {
          isSuccess: &quot;true&quot;,
        }),
      ).rejects.toThrow(&quot;isSuccess&quot;);
    });

    test(&quot;period 시작일이 종료일보다 뒤면 에러 발생&quot;, async () =&gt; {
      await expect(
        updateMemo(1, {
          period: {
            startDate: &quot;2026-03-20&quot;,
            endDate: &quot;2026-03-10&quot;,
          },
        }),
      ).rejects.toThrow(&quot;period&quot;);
    });
  });
</code></pre>
</li>
<li><p>실행 결과</p>
<pre><code class="language-python">  hongseongchae@hongseongchaeui-MacBookPro tdd_study % npm test

  &gt; tdd_study@1.0.0 test
  &gt; jest

   FAIL  __test__/memo/updateMemoList.test.js
    ● updateMemo › 정상 id와 수정값이면 memo 수정에 성공한다

      TypeError: updateMemo is not a function

        3 | describe(&quot;updateMemo&quot;, () =&gt; {
        4 |   test(&quot;정상 id와 수정값이면 memo 수정에 성공한다&quot;, async () =&gt; {
      &gt; 5 |     const result = await updateMemo(1, {
          |                          ^
        6 |       title: &quot;수정된 메모 제목&quot;,
        7 |       content: &quot;수정된 메모 내용&quot;,
        8 |       priority: 5,

        at Object.updateMemo (__test__/memo/updateMemoList.test.js:5:26)

    ● updateMemo › id가 없으면 에러 발생

      TypeError: updateMemo is not a function

        21 |   test(&quot;id가 없으면 에러 발생&quot;, async () =&gt; {
        22 |     await expect(
      &gt; 23 |       updateMemo(undefined, {
           |       ^
        24 |         title: &quot;수정 제목&quot;,
        25 |       }),
        26 |     ).rejects.toThrow(&quot;id&quot;);

        at Object.updateMemo (__test__/memo/updateMemoList.test.js:23:7)

    ● updateMemo › id가 문자열이면 에러 발생

      TypeError: updateMemo is not a function

        29 |   test(&quot;id가 문자열이면 에러 발생&quot;, async () =&gt; {
        30 |     await expect(
      &gt; 31 |       updateMemo(&quot;1&quot;, {
           |       ^
        32 |         title: &quot;수정 제목&quot;,
        33 |       }),
        34 |     ).rejects.toThrow(&quot;id&quot;);

        at Object.updateMemo (__test__/memo/updateMemoList.test.js:31:7)

    ● updateMemo › id가 0이면 에러 발생

      TypeError: updateMemo is not a function

        37 |   test(&quot;id가 0이면 에러 발생&quot;, async () =&gt; {
        38 |     await expect(
      &gt; 39 |       updateMemo(0, {
           |       ^
        40 |         title: &quot;수정 제목&quot;,
        41 |       }),
        42 |     ).rejects.toThrow(&quot;id&quot;);

        at Object.updateMemo (__test__/memo/updateMemoList.test.js:39:7)

    ● updateMemo › update data가 없으면 에러 발생

      TypeError: updateMemo is not a function

        44 |
        45 |   test(&quot;update data가 없으면 에러 발생&quot;, async () =&gt; {
      &gt; 46 |     await expect(updateMemo(1)).rejects.toThrow(&quot;data&quot;);
           |                  ^
        47 |   });
        48 |
        49 |   test(&quot;존재하지 않는 id이면 에러 발생&quot;, async () =&gt; {

        at Object.updateMemo (__test__/memo/updateMemoList.test.js:46:18)

    ● updateMemo › 존재하지 않는 id이면 에러 발생

      TypeError: updateMemo is not a function

        49 |   test(&quot;존재하지 않는 id이면 에러 발생&quot;, async () =&gt; {
        50 |     await expect(
      &gt; 51 |       updateMemo(999, {
           |       ^
        52 |         title: &quot;수정 제목&quot;,
        53 |       }),
        54 |     ).rejects.toThrow(&quot;memo&quot;);

        at Object.updateMemo (__test__/memo/updateMemoList.test.js:51:7)

    ● updateMemo › title이 빈 문자열이면 에러 발생

      TypeError: updateMemo is not a function

        57 |   test(&quot;title이 빈 문자열이면 에러 발생&quot;, async () =&gt; {
        58 |     await expect(
      &gt; 59 |       updateMemo(1, {
           |       ^
        60 |         title: &quot;&quot;,
        61 |       }),
        62 |     ).rejects.toThrow(&quot;title&quot;);

        at Object.updateMemo (__test__/memo/updateMemoList.test.js:59:7)

    ● updateMemo › content가 공백만 있으면 에러 발생

      TypeError: updateMemo is not a function

        65 |   test(&quot;content가 공백만 있으면 에러 발생&quot;, async () =&gt; {
        66 |     await expect(
      &gt; 67 |       updateMemo(1, {
           |       ^
        68 |         content: &quot;   &quot;,
        69 |       }),
        70 |     ).rejects.toThrow(&quot;content&quot;);

        at Object.updateMemo (__test__/memo/updateMemoList.test.js:67:7)

    ● updateMemo › priority가 범위를 벗어나면 에러 발생

      TypeError: updateMemo is not a function

        73 |   test(&quot;priority가 범위를 벗어나면 에러 발생&quot;, async () =&gt; {
        74 |     await expect(
      &gt; 75 |       updateMemo(1, {
           |       ^
        76 |         priority: 6,
        77 |       }),
        78 |     ).rejects.toThrow(&quot;priority&quot;);

        at Object.updateMemo (__test__/memo/updateMemoList.test.js:75:7)

    ● updateMemo › isSuccess가 boolean이 아니면 에러 발생

      TypeError: updateMemo is not a function

        81 |   test(&quot;isSuccess가 boolean이 아니면 에러 발생&quot;, async () =&gt; {
        82 |     await expect(
      &gt; 83 |       updateMemo(1, {
           |       ^
        84 |         isSuccess: &quot;true&quot;,
        85 |       }),
        86 |     ).rejects.toThrow(&quot;isSuccess&quot;);

        at Object.updateMemo (__test__/memo/updateMemoList.test.js:83:7)

    ● updateMemo › period 시작일이 종료일보다 뒤면 에러 발생

      TypeError: updateMemo is not a function

        89 |   test(&quot;period 시작일이 종료일보다 뒤면 에러 발생&quot;, async () =&gt; {
        90 |     await expect(
      &gt; 91 |       updateMemo(1, {
           |       ^
        92 |         period: {
        93 |           startDate: &quot;2026-03-20&quot;,
        94 |           endDate: &quot;2026-03-10&quot;,

        at Object.updateMemo (__test__/memo/updateMemoList.test.js:91:7)

   PASS  __test__/memo/deleteMemo.test.js
   PASS  __test__/memo/createMemo.test.js
   PASS  __test__/memo/readMemo.test.js
   PASS  __test__/memo/readMemoList.test.js

  Test Suites: 1 failed, 4 passed, 5 total
  Tests:       11 failed, 43 passed, 54 total
  Snapshots:   0 total
  Time:        0.696 s, estimated 1 s
  Ran all test suites.</code></pre>
</li>
<li><p>Red인 경우 테스트 코드를 정의하는 단계(실패하는 케이스)이므로 실질적인 구현이 없으므로 모두 실패가 나온다</p>
</li>
</ul>
</li>
<li><p>Green</p>
<ul>
<li><p>Red를 바탕으로 테스트 코드를 구현한다</p>
</li>
<li><p>코드</p>
<pre><code class="language-python">  const mockMemos = [
    {
      id: 1,
      title: &quot;TDD 메모 작성&quot;,
      period: {
        startDate: &quot;2026-03-06&quot;,
        endDate: &quot;2026-03-10&quot;,
      },
      content: &quot;첫 번째 메모&quot;,
      priority: 3,
      isSuccess: false,
    },
    {
      id: 2,
      title: &quot;두 번째 메모&quot;,
      period: {
        startDate: &quot;2026-03-07&quot;,
        endDate: &quot;2026-03-12&quot;,
      },
      content: &quot;두 번째 내용&quot;,
      priority: 2,
      isSuccess: true,
    },
    {
      id: 3,
      title: &quot;세 번째 메모&quot;,
      period: {
        startDate: &quot;2026-03-08&quot;,
        endDate: &quot;2026-03-15&quot;,
      },
      content: &quot;세 번째 내용&quot;,
      priority: 3,
      isSuccess: true,
    },
    {
      id: 4,
      title: &quot;네 번째 메모&quot;,
      period: {
        startDate: &quot;2026-03-09&quot;,
        endDate: &quot;2026-03-18&quot;,
      },
      content: &quot;네 번째 내용&quot;,
      priority: 1,
      isSuccess: false,
    },
  ];

  async function updateMemo(id, data) {
    if (typeof id !== &quot;number&quot; || id &lt;= 0) {
      throw new Error(&quot;id&quot;);
    }

    if (!data) {
      throw new Error(&quot;data&quot;);
    }

    const index = mockMemos.findIndex((item) =&gt; item.id === id);

    if (index === -1) {
      throw new Error(&quot;memo&quot;);
    }

    if (data.title !== undefined) {
      if (typeof data.title !== &quot;string&quot; || data.title.trim() === &quot;&quot;) {
        throw new Error(&quot;title&quot;);
      }
    }

    if (data.content !== undefined) {
      if (typeof data.content !== &quot;string&quot; || data.content.trim() === &quot;&quot;) {
        throw new Error(&quot;content&quot;);
      }
    }

    if (data.priority !== undefined) {
      if (
        typeof data.priority !== &quot;number&quot; ||
        data.priority &lt; 1 ||
        data.priority &gt; 5
      ) {
        throw new Error(&quot;priority&quot;);
      }
    }

    if (data.isSuccess !== undefined) {
      if (typeof data.isSuccess !== &quot;boolean&quot;) {
        throw new Error(&quot;isSuccess&quot;);
      }
    }

    if (data.period !== undefined) {
      if (
        !data.period ||
        typeof data.period.startDate !== &quot;string&quot; ||
        typeof data.period.endDate !== &quot;string&quot;
      ) {
        throw new Error(&quot;period&quot;);
      }

      const start = new Date(data.period.startDate);
      const end = new Date(data.period.endDate);

      if (
        Number.isNaN(start.getTime()) ||
        Number.isNaN(end.getTime()) ||
        start &gt; end
      ) {
        throw new Error(&quot;period&quot;);
      }
    }

    mockMemos[index] = {
      ...mockMemos[index],
      ...data,
    };

    return mockMemos[index];
  }

  module.exports = { updateMemo };
</code></pre>
</li>
<li><p>실행결과</p>
<pre><code class="language-python">  hongseongchae@hongseongchaeui-MacBookPro tdd_study % npm test

  &gt; tdd_study@1.0.0 test
  &gt; jest

   PASS  __test__/memo/updateMemoList.test.js
   PASS  __test__/memo/createMemo.test.js
   PASS  __test__/memo/readMemo.test.js
   PASS  __test__/memo/readMemoList.test.js
   PASS  __test__/memo/deleteMemo.test.js

  Test Suites: 5 passed, 5 total
  Tests:       54 passed, 54 total
  Snapshots:   0 total
  Time:        0.599 s, estimated 1 s
  Ran all test suites.</code></pre>
</li>
</ul>
</li>
<li><p>blue</p>
<ul>
<li><p>Green을 통과한 기준으로 refactoring을 한다</p>
</li>
<li><p>validation 함수를 분리</p>
</li>
<li><p>코드</p>
<pre><code class="language-python">  const mockMemos = [
    {
      id: 1,
      title: &quot;TDD 메모 작성&quot;,
      period: {
        startDate: &quot;2026-03-06&quot;,
        endDate: &quot;2026-03-10&quot;,
      },
      content: &quot;첫 번째 메모&quot;,
      priority: 3,
      isSuccess: false,
    },
    {
      id: 2,
      title: &quot;두 번째 메모&quot;,
      period: {
        startDate: &quot;2026-03-07&quot;,
        endDate: &quot;2026-03-12&quot;,
      },
      content: &quot;두 번째 내용&quot;,
      priority: 2,
      isSuccess: true,
    },
    {
      id: 3,
      title: &quot;세 번째 메모&quot;,
      period: {
        startDate: &quot;2026-03-08&quot;,
        endDate: &quot;2026-03-15&quot;,
      },
      content: &quot;세 번째 내용&quot;,
      priority: 3,
      isSuccess: true,
    },
    {
      id: 4,
      title: &quot;네 번째 메모&quot;,
      period: {
        startDate: &quot;2026-03-09&quot;,
        endDate: &quot;2026-03-18&quot;,
      },
      content: &quot;네 번째 내용&quot;,
      priority: 1,
      isSuccess: false,
    },
  ];

  function validateId(id) {
    if (typeof id !== &quot;number&quot; || id &lt;= 0) {
      throw new Error(&quot;id&quot;);
    }
  }

  function validateUpdateData(data) {
    if (!data) {
      throw new Error(&quot;data&quot;);
    }
  }

  function validateTitle(title) {
    if (typeof title !== &quot;string&quot; || title.trim() === &quot;&quot;) {
      throw new Error(&quot;title&quot;);
    }
  }

  function validateContent(content) {
    if (typeof content !== &quot;string&quot; || content.trim() === &quot;&quot;) {
      throw new Error(&quot;content&quot;);
    }
  }

  function validatePriority(priority) {
    if (typeof priority !== &quot;number&quot; || priority &lt; 1 || priority &gt; 5) {
      throw new Error(&quot;priority&quot;);
    }
  }

  function validateIsSuccess(isSuccess) {
    if (typeof isSuccess !== &quot;boolean&quot;) {
      throw new Error(&quot;isSuccess&quot;);
    }
  }

  function validatePeriod(period) {
    if (
      !period ||
      typeof period.startDate !== &quot;string&quot; ||
      typeof period.endDate !== &quot;string&quot;
    ) {
      throw new Error(&quot;period&quot;);
    }

    const start = new Date(period.startDate);
    const end = new Date(period.endDate);

    if (
      Number.isNaN(start.getTime()) ||
      Number.isNaN(end.getTime()) ||
      start &gt; end
    ) {
      throw new Error(&quot;period&quot;);
    }
  }

  function validateUpdateFields(data) {
    if (data.title !== undefined) {
      validateTitle(data.title);
    }

    if (data.content !== undefined) {
      validateContent(data.content);
    }

    if (data.priority !== undefined) {
      validatePriority(data.priority);
    }

    if (data.isSuccess !== undefined) {
      validateIsSuccess(data.isSuccess);
    }

    if (data.period !== undefined) {
      validatePeriod(data.period);
    }
  }

  async function updateMemo(id, data) {
    validateId(id);
    validateUpdateData(data);

    const index = mockMemos.findIndex((item) =&gt; item.id === id);

    if (index === -1) {
      throw new Error(&quot;memo&quot;);
    }

    validateUpdateFields(data);

    mockMemos[index] = {
      ...mockMemos[index],
      ...data,
    };

    return mockMemos[index];
  }

  module.exports = { updateMemo };
</code></pre>
</li>
<li><p>실행 결과</p>
<pre><code class="language-python">  hongseongchae@hongseongchaeui-MacBookPro tdd_study % npm test

  &gt; tdd_study@1.0.0 test
  &gt; jest

   PASS  __test__/memo/updateMemoList.test.js
   PASS  __test__/memo/createMemo.test.js
   PASS  __test__/memo/readMemoList.test.js
   PASS  __test__/memo/readMemo.test.js
   PASS  __test__/memo/deleteMemo.test.js

  Test Suites: 5 passed, 5 total
  Tests:       54 passed, 54 total
  Snapshots:   0 total
  Time:        0.586 s, estimated 1 s
  Ran all test suites.</code></pre>
</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TDD 실습 (3)]]></title>
            <link>https://velog.io/@back_end_sc/TDD-%EC%8B%A4%EC%8A%B5-3</link>
            <guid>https://velog.io/@back_end_sc/TDD-%EC%8B%A4%EC%8A%B5-3</guid>
            <pubDate>Sun, 08 Mar 2026 06:37:23 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>Red</p>
<ul>
<li><p>메모 삭제에 대한 필수 값 누락, 검증에 대한 테스트 진행</p>
<ul>
<li>id 필요</li>
<li>id 타입 검증</li>
<li>id 범위 검증</li>
<li>존재하지 않는 id 에러</li>
<li>삭제 성공 시 true 반환</li>
</ul>
</li>
<li><p>코드</p>
<pre><code class="language-python">  const { deleteMemo } = require(&quot;../../src/memo/deleteMemo&quot;);

  describe(&quot;deleteMemo&quot;, () =&gt; {
    test(&quot;정상 id이면 memo 삭제에 성공한다&quot;, async () =&gt; {
      const result = await deleteMemo(1);

      expect(result).toBe(true);
    });

    test(&quot;id가 없으면 에러 발생&quot;, async () =&gt; {
      await expect(deleteMemo()).rejects.toThrow(&quot;id&quot;);
    });

    test(&quot;id가 null이면 에러 발생&quot;, async () =&gt; {
      await expect(deleteMemo(null)).rejects.toThrow(&quot;id&quot;);
    });

    test(&quot;id가 문자열이면 에러 발생&quot;, async () =&gt; {
      await expect(deleteMemo(&quot;1&quot;)).rejects.toThrow(&quot;id&quot;);
    });

    test(&quot;id가 0이면 에러 발생&quot;, async () =&gt; {
      await expect(deleteMemo(0)).rejects.toThrow(&quot;id&quot;);
    });

    test(&quot;존재하지 않는 id이면 에러 발생&quot;, async () =&gt; {
      await expect(deleteMemo(999)).rejects.toThrow(&quot;memo is not find&quot;);
    });
  });
</code></pre>
</li>
<li><p>실행 결과</p>
<pre><code class="language-python">  hongseongchae@hongseongchaeui-MacBookPro tdd_study % npm test

  &gt; tdd_study@1.0.0 test
  &gt; jest

   FAIL  __test__/memo/deleteMemoList.test.js
    ● deleteMemo › 정상 id이면 memo 삭제에 성공한다

      TypeError: deleteMemo is not a function

        3 | describe(&quot;deleteMemo&quot;, () =&gt; {
        4 |   test(&quot;정상 id이면 memo 삭제에 성공한다&quot;, async () =&gt; {
      &gt; 5 |     const result = await deleteMemo(1);
          |                          ^
        6 |
        7 |     expect(result).toBe(true);
        8 |   });

        at Object.deleteMemo (__test__/memo/deleteMemoList.test.js:5:26)

    ● deleteMemo › id가 없으면 에러 발생

      TypeError: deleteMemo is not a function

         9 |
        10 |   test(&quot;id가 없으면 에러 발생&quot;, async () =&gt; {
      &gt; 11 |     await expect(deleteMemo()).rejects.toThrow(&quot;id&quot;);
           |                  ^
        12 |   });
        13 |
        14 |   test(&quot;id가 null이면 에러 발생&quot;, async () =&gt; {

        at Object.deleteMemo (__test__/memo/deleteMemoList.test.js:11:18)

    ● deleteMemo › id가 null이면 에러 발생

      TypeError: deleteMemo is not a function

        13 |
        14 |   test(&quot;id가 null이면 에러 발생&quot;, async () =&gt; {
      &gt; 15 |     await expect(deleteMemo(null)).rejects.toThrow(&quot;id&quot;);
           |                  ^
        16 |   });
        17 |
        18 |   test(&quot;id가 문자열이면 에러 발생&quot;, async () =&gt; {

        at Object.deleteMemo (__test__/memo/deleteMemoList.test.js:15:18)

    ● deleteMemo › id가 문자열이면 에러 발생

      TypeError: deleteMemo is not a function

        17 |
        18 |   test(&quot;id가 문자열이면 에러 발생&quot;, async () =&gt; {
      &gt; 19 |     await expect(deleteMemo(&quot;1&quot;)).rejects.toThrow(&quot;id&quot;);
           |                  ^
        20 |   });
        21 |
        22 |   test(&quot;id가 0이면 에러 발생&quot;, async () =&gt; {

        at Object.deleteMemo (__test__/memo/deleteMemoList.test.js:19:18)

    ● deleteMemo › id가 0이면 에러 발생

      TypeError: deleteMemo is not a function

        21 |
        22 |   test(&quot;id가 0이면 에러 발생&quot;, async () =&gt; {
      &gt; 23 |     await expect(deleteMemo(0)).rejects.toThrow(&quot;id&quot;);
           |                  ^
        24 |   });
        25 |
        26 |   test(&quot;존재하지 않는 id이면 에러 발생&quot;, async () =&gt; {

        at Object.deleteMemo (__test__/memo/deleteMemoList.test.js:23:18)

    ● deleteMemo › 존재하지 않는 id이면 에러 발생

      TypeError: deleteMemo is not a function

        25 |
        26 |   test(&quot;존재하지 않는 id이면 에러 발생&quot;, async () =&gt; {
      &gt; 27 |     await expect(deleteMemo(999)).rejects.toThrow(&quot;memo&quot;);
           |                  ^
        28 |   });
        29 | });
        30 |

        at Object.deleteMemo (__test__/memo/deleteMemoList.test.js:27:18)

   PASS  __test__/memo/readMemo.test.js
   PASS  __test__/memo/readMemoList.test.js
   PASS  __test__/memo/createMemo.test.js

  Test Suites: 1 failed, 3 passed, 4 total
  Tests:       6 failed, 37 passed, 43 total
  Snapshots:   0 total
  Time:        0.606 s, estimated 1 s
  Ran all test suites.</code></pre>
</li>
<li><p>Red인 경우 테스트 코드를 정의하는 단계(실패하는 케이스)이므로 실질적인 구현이 없으므로 모두 실패가 나온다</p>
</li>
</ul>
</li>
<li><p>Green</p>
<ul>
<li><p>Red를 바탕으로 테스트 코드를 구현한다</p>
</li>
<li><p>코드</p>
<pre><code class="language-python">  const mockMemos = [
    {
      id: 1,
      title: &quot;TDD 메모 작성&quot;,
      period: {
        startDate: &quot;2026-03-06&quot;,
        endDate: &quot;2026-03-10&quot;,
      },
      content: &quot;첫 번째 메모&quot;,
      priority: 3,
      isSuccess: false,
    },
    {
      id: 2,
      title: &quot;두 번째 메모&quot;,
      period: {
        startDate: &quot;2026-03-07&quot;,
        endDate: &quot;2026-03-12&quot;,
      },
      content: &quot;두 번째 내용&quot;,
      priority: 2,
      isSuccess: true,
    },
    {
      id: 3,
      title: &quot;세 번째 메모&quot;,
      period: {
        startDate: &quot;2026-03-08&quot;,
        endDate: &quot;2026-03-15&quot;,
      },
      content: &quot;세 번째 내용&quot;,
      priority: 3,
      isSuccess: true,
    },
    {
      id: 4,
      title: &quot;네 번째 메모&quot;,
      period: {
        startDate: &quot;2026-03-09&quot;,
        endDate: &quot;2026-03-18&quot;,
      },
      content: &quot;네 번째 내용&quot;,
      priority: 1,
      isSuccess: false,
    },
  ];

  async function deleteMemo(id) {
    if (typeof id !== &quot;number&quot; || id &lt;= 0) {
      throw new Error(&quot;id&quot;);
    }

    const index = mockMemos.findIndex((item) =&gt; item.id === id);

    if (index === -1) {
      throw new Error(&quot;memo is not find&quot;);
    }

    mockMemos.splice(index, 1);

    return true;
  }

  module.exports = { deleteMemo };
</code></pre>
</li>
<li><p>실행결과</p>
<pre><code class="language-python">  hongseongchae@hongseongchaeui-MacBookPro tdd_study % npm test

  &gt; tdd_study@1.0.0 test
  &gt; jest

   PASS  __test__/memo/deleteMemo.test.js
   PASS  __test__/memo/createMemo.test.js
   PASS  __test__/memo/readMemo.test.js
   PASS  __test__/memo/readMemoList.test.js

  Test Suites: 4 passed, 4 total
  Tests:       43 passed, 43 total
  Snapshots:   0 total
  Time:        0.498 s, estimated 1 s
  Ran all test suites.</code></pre>
</li>
</ul>
</li>
<li><p>blue</p>
<ul>
<li><p>Green을 통과한 기준으로 refactoring을 한다</p>
</li>
<li><p>validation 함수를 분리</p>
</li>
<li><p>코드</p>
<pre><code class="language-python">  const mockMemos = [
    {
      id: 1,
      title: &quot;TDD 메모 작성&quot;,
      period: {
        startDate: &quot;2026-03-06&quot;,
        endDate: &quot;2026-03-10&quot;,
      },
      content: &quot;첫 번째 메모&quot;,
      priority: 3,
      isSuccess: false,
    },
    {
      id: 2,
      title: &quot;두 번째 메모&quot;,
      period: {
        startDate: &quot;2026-03-07&quot;,
        endDate: &quot;2026-03-12&quot;,
      },
      content: &quot;두 번째 내용&quot;,
      priority: 2,
      isSuccess: true,
    },
    {
      id: 3,
      title: &quot;세 번째 메모&quot;,
      period: {
        startDate: &quot;2026-03-08&quot;,
        endDate: &quot;2026-03-15&quot;,
      },
      content: &quot;세 번째 내용&quot;,
      priority: 3,
      isSuccess: true,
    },
    {
      id: 4,
      title: &quot;네 번째 메모&quot;,
      period: {
        startDate: &quot;2026-03-09&quot;,
        endDate: &quot;2026-03-18&quot;,
      },
      content: &quot;네 번째 내용&quot;,
      priority: 1,
      isSuccess: false,
    },
  ];
  function validateId(id) {
    if (typeof id !== &quot;number&quot; || id &lt;= 0) {
      throw new Error(&quot;id&quot;);
    }
  }
  async function deleteMemo(id) {
    validateId(id);

    const index = mockMemos.findIndex((item) =&gt; item.id === id);

    if (index === -1) {
      throw new Error(&quot;memo is not find&quot;);
    }

    mockMemos.splice(index, 1);

    return true;
  }

  module.exports = { deleteMemo };
</code></pre>
</li>
<li><p>실행 결과</p>
<pre><code class="language-python">  hongseongchae@hongseongchaeui-MacBookPro tdd_study % npm test

  &gt; tdd_study@1.0.0 test
  &gt; jest

   PASS  __test__/memo/deleteMemo.test.js
   PASS  __test__/memo/createMemo.test.js
   PASS  __test__/memo/readMemoList.test.js
   PASS  __test__/memo/readMemo.test.js

  Test Suites: 4 passed, 4 total
  Tests:       43 passed, 43 total
  Snapshots:   0 total
  Time:        0.556 s, estimated 1 s
  Ran all test suites.</code></pre>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TDD 실습 (2)]]></title>
            <link>https://velog.io/@back_end_sc/TDD-%EC%8B%A4%EC%8A%B5-2</link>
            <guid>https://velog.io/@back_end_sc/TDD-%EC%8B%A4%EC%8A%B5-2</guid>
            <pubDate>Sun, 08 Mar 2026 06:34:12 GMT</pubDate>
            <description><![CDATA[<h3 id="메모-단건-조회">메모 단건 조회</h3>
<h4 id="red">Red</h4>
<ul>
<li><p>메모 조회에 대한 필수 값 누락에 대한 테스트 진행</p>
<ul>
<li><p>단건 조회</p>
<ul>
<li>id 필요</li>
<li>없는 id 에러, 범위 검증</li>
<li>객체 1개 반환<ul>
<li>코드<pre><code>const { readMemo } = require(&quot;../../src/memo/readMemo&quot;);
</code></pre></li>
</ul>
</li>
</ul>
<p>describe(&quot;readMemo&quot;, () =&gt; {
  test(&quot;정상 id이면 memo 단건 조회에 성공한다&quot;, async () =&gt; {</p>
<pre><code>const result = await readMemo(1);

expect(result).toMatchObject({
  id: 1,
  title: &quot;TDD 메모 작성&quot;,
  period: {
    startDate: &quot;2026-03-06&quot;,
    endDate: &quot;2026-03-10&quot;,
  },
  content: &quot;첫 번째 메모&quot;,
  priority: 3,
  isSuccess: false,
});</code></pre><p>  });</p>
<p>  test(&quot;id가 없으면 에러 발생&quot;, async () =&gt; {</p>
<pre><code>await expect(readMemo()).rejects.toThrow(&quot;id&quot;);</code></pre><p>  });</p>
<p>  test(&quot;id가 null이면 에러 발생&quot;, async () =&gt; {</p>
<pre><code>await expect(readMemo(null)).rejects.toThrow(&quot;id&quot;);</code></pre><p>  });</p>
<p>  test(&quot;id가 문자열이면 에러 발생&quot;, async () =&gt; {</p>
<pre><code>await expect(readMemo(&quot;1&quot;)).rejects.toThrow(&quot;id&quot;);</code></pre><p>  });</p>
<p>  test(&quot;id가 0이면 에러 발생&quot;, async () =&gt; {</p>
<pre><code>await expect(readMemo(0)).rejects.toThrow(&quot;id&quot;);</code></pre><p>  });</p>
<p>  test(&quot;id가 음수이면 에러 발생&quot;, async () =&gt; {</p>
<pre><code>await expect(readMemo(-1)).rejects.toThrow(&quot;id&quot;);</code></pre><p>  });</p>
<p>  test(&quot;존재하지 않는 id이면 에러 발생&quot;, async () =&gt; {</p>
<pre><code>await expect(readMemo(999)).rejects.toThrow(&quot;memo&quot;);</code></pre><p>  });
});</p>
</li>
</ul>
</li>
<li><p>실행 로그
  ```
  hongseongchae@hongseongchaeui-MacBookPro tdd_study % npm test</p>
<blockquote>
<p><a href="mailto:tdd_study@1.0.0">tdd_study@1.0.0</a> test
jest</p>
</blockquote>
<p>   FAIL  <strong>test</strong>/memo/readMemo.test.js</p>
<pre><code>● readMemo › 정상 id이면 memo 단건 조회에 성공한다

  TypeError: readMemo is not a function

    3 | describe(&quot;readMemo&quot;, () =&gt; {
    4 |   test(&quot;정상 id이면 memo 단건 조회에 성공한다&quot;, async () =&gt; {
  &gt; 5 |     const result = await readMemo(1);
      |                          ^
    6 |
    7 |     expect(result).toMatchObject({
    8 |       id: 1,

    at Object.readMemo (__test__/memo/readMemo.test.js:5:26)

● readMemo › id가 없으면 에러 발생

  TypeError: readMemo is not a function

    19 |
    20 |   test(&quot;id가 없으면 에러 발생&quot;, async () =&gt; {
  &gt; 21 |     await expect(readMemo()).rejects.toThrow(&quot;id&quot;);
       |                  ^
    22 |   });
    23 |
    24 |   test(&quot;id가 null이면 에러 발생&quot;, async () =&gt; {

    at Object.readMemo (__test__/memo/readMemo.test.js:21:18)

● readMemo › id가 null이면 에러 발생

  TypeError: readMemo is not a function

    23 |
    24 |   test(&quot;id가 null이면 에러 발생&quot;, async () =&gt; {
  &gt; 25 |     await expect(readMemo(null)).rejects.toThrow(&quot;id&quot;);
       |                  ^
    26 |   });
    27 |
    28 |   test(&quot;id가 문자열이면 에러 발생&quot;, async () =&gt; {

    at Object.readMemo (__test__/memo/readMemo.test.js:25:18)

● readMemo › id가 문자열이면 에러 발생

  TypeError: readMemo is not a function

    27 |
    28 |   test(&quot;id가 문자열이면 에러 발생&quot;, async () =&gt; {
  &gt; 29 |     await expect(readMemo(&quot;1&quot;)).rejects.toThrow(&quot;id&quot;);
       |                  ^
    30 |   });
    31 |
    32 |   test(&quot;id가 0이면 에러 발생&quot;, async () =&gt; {

    at Object.readMemo (__test__/memo/readMemo.test.js:29:18)

● readMemo › id가 0이면 에러 발생

  TypeError: readMemo is not a function

    31 |
    32 |   test(&quot;id가 0이면 에러 발생&quot;, async () =&gt; {
  &gt; 33 |     await expect(readMemo(0)).rejects.toThrow(&quot;id&quot;);
       |                  ^
    34 |   });
    35 |
    36 |   test(&quot;id가 음수이면 에러 발생&quot;, async () =&gt; {

    at Object.readMemo (__test__/memo/readMemo.test.js:33:18)

● readMemo › id가 음수이면 에러 발생

  TypeError: readMemo is not a function

    35 |
    36 |   test(&quot;id가 음수이면 에러 발생&quot;, async () =&gt; {
  &gt; 37 |     await expect(readMemo(-1)).rejects.toThrow(&quot;id&quot;);
       |                  ^
    38 |   });
    39 |
    40 |   test(&quot;존재하지 않는 id이면 에러 발생&quot;, async () =&gt; {

    at Object.readMemo (__test__/memo/readMemo.test.js:37:18)

● readMemo › 존재하지 않는 id이면 에러 발생

  TypeError: readMemo is not a function

    39 |
    40 |   test(&quot;존재하지 않는 id이면 에러 발생&quot;, async () =&gt; {
  &gt; 41 |     await expect(readMemo(999)).rejects.toThrow(&quot;memo&quot;);
       |                  ^
    42 |   });
    43 | });
    44 |

    at Object.readMemo (__test__/memo/readMemo.test.js:41:18)</code></pre><p>   PASS  <strong>test</strong>/memo/createMemo.test.js</p>
<p>  Test Suites: 1 failed, 1 passed, 2 total
  Tests:       7 failed, 16 passed, 23 total
  Snapshots:   0 total
  Time:        0.498 s, estimated 1 s
  Ran all test suites.</p>
</li>
<li><p>리스트 조회</p>
<ul>
<li><p>page 필요</p>
</li>
<li><p>pageSize 필요</p>
</li>
<li><p>page 타입 검증</p>
</li>
<li><p>pageSize 타입 검증</p>
</li>
<li><p>page 범위 검증 (1 이상)</p>
</li>
<li><p>pageSize 범위 검증 (1 이상)</p>
</li>
<li><p>isSuccess 필터</p>
<ul>
<li>boolean 타입 검증</li>
<li>true → 성공 메모만 조회</li>
<li>false → 진행 중 메모 조회</li>
</ul>
</li>
<li><p>priority 필터</p>
<ul>
<li>숫자 타입 검증</li>
<li>범위 검증 (1 ~ 5)</li>
<li>priority 값에 해당하는 메모만 조회</li>
</ul>
</li>
<li><p>코드
```
const { listMemo } = require(&quot;../../src/memo/readMemo&quot;);</p>
<p>describe(&quot;listMemo&quot;, () =&gt; {
  test(&quot;page와 pageSize가 정상 입력이면 memo 목록 조회에 성공한다&quot;, async () =&gt; {</p>
<pre><code>const result = await listMemo({
  page: 1,
  pageSize: 10,
});

expect(Array.isArray(result)).toBe(true);</code></pre><p>  });</p>
<p>  test(&quot;page가 없으면 에러 발생&quot;, async () =&gt; {</p>
<pre><code>await expect(
  listMemo({
    pageSize: 10,
  }),
).rejects.toThrow(&quot;page&quot;);</code></pre><p>  });</p>
<p>  test(&quot;pageSize가 없으면 에러 발생&quot;, async () =&gt; {</p>
<pre><code>await expect(
  listMemo({
    page: 1,
  }),
).rejects.toThrow(&quot;pageSize&quot;);</code></pre><p>  });</p>
<p>  test(&quot;page가 숫자가 아니면 에러 발생&quot;, async () =&gt; {</p>
<pre><code>await expect(
  listMemo({
    page: &quot;1&quot;,
    pageSize: 10,
  }),
).rejects.toThrow(&quot;page&quot;);</code></pre><p>  });</p>
<p>  test(&quot;pageSize가 숫자가 아니면 에러 발생&quot;, async () =&gt; {</p>
<pre><code>await expect(
  listMemo({
    page: 1,
    pageSize: &quot;10&quot;,
  }),
).rejects.toThrow(&quot;pageSize&quot;);</code></pre><p>  });</p>
<p>  test(&quot;page가 1보다 작으면 에러 발생&quot;, async () =&gt; {</p>
<pre><code>await expect(
  listMemo({
    page: 0,
    pageSize: 10,
  }),
).rejects.toThrow(&quot;page&quot;);</code></pre><p>  });</p>
<p>  test(&quot;pageSize가 1보다 작으면 에러 발생&quot;, async () =&gt; {</p>
<pre><code>await expect(
  listMemo({
    page: 1,
    pageSize: 0,
  }),
).rejects.toThrow(&quot;pageSize&quot;);</code></pre><p>  });</p>
<p>  test(&quot;isSuccess가 boolean이 아니면 에러 발생&quot;, async () =&gt; {</p>
<pre><code>await expect(
  listMemo({
    page: 1,
    pageSize: 10,
    isSuccess: &quot;true&quot;,
  }),
).rejects.toThrow(&quot;isSuccess&quot;);</code></pre><p>  });</p>
<p>  test(&quot;isSuccess가 true이면 성공한 메모만 조회&quot;, async () =&gt; {</p>
<pre><code>const result = await listMemo({
  page: 1,
  pageSize: 10,
  isSuccess: true,
});

expect(Array.isArray(result)).toBe(true);
result.forEach((memo) =&gt; {
  expect(memo.isSuccess).toBe(true);
});</code></pre><p>  });</p>
<p>  test(&quot;isSuccess가 false이면 진행 중 메모만 조회&quot;, async () =&gt; {</p>
<pre><code>const result = await listMemo({
  page: 1,
  pageSize: 10,
  isSuccess: false,
});

expect(Array.isArray(result)).toBe(true);
result.forEach((memo) =&gt; {
  expect(memo.isSuccess).toBe(false);
});</code></pre><p>  });</p>
<p>  test(&quot;priority가 숫자가 아니면 에러 발생&quot;, async () =&gt; {</p>
<pre><code>await expect(
  listMemo({
    page: 1,
    pageSize: 10,
    priority: &quot;3&quot;,
  }),
).rejects.toThrow(&quot;priority&quot;);</code></pre><p>  });</p>
<p>  test(&quot;priority가 1보다 작으면 에러 발생&quot;, async () =&gt; {</p>
<pre><code>await expect(
  listMemo({
    page: 1,
    pageSize: 10,
    priority: 0,
  }),
).rejects.toThrow(&quot;priority&quot;);</code></pre><p>  });</p>
<p>  test(&quot;priority가 5보다 크면 에러 발생&quot;, async () =&gt; {</p>
<pre><code>await expect(
  listMemo({
    page: 1,
    pageSize: 10,
    priority: 6,
  }),
).rejects.toThrow(&quot;priority&quot;);</code></pre><p>  });</p>
<p>  test(&quot;priority가 주어지면 해당 priority 메모만 조회&quot;, async () =&gt; {</p>
<pre><code>const result = await listMemo({
  page: 1,
  pageSize: 10,
  priority: 3,
});

expect(Array.isArray(result)).toBe(true);
result.forEach((memo) =&gt; {
  expect(memo.priority).toBe(3);
});</code></pre><p>  });
});</p>
</li>
</ul>
</li>
<li><p>실행 로그
```
  hongseongchae@hongseongchaeui-MacBookPro tdd_study % npm test</p>
<blockquote>
<p><a href="mailto:tdd_study@1.0.0">tdd_study@1.0.0</a> test
jest</p>
</blockquote>
<p> PASS  <strong>test</strong>/memo/createMemo.test.js
 PASS  <strong>test</strong>/memo/readMemo.test.js
 FAIL  <strong>test</strong>/memo/readMemoList.test.js
  ● listMemo › page와 pageSize가 정상 입력이면 memo 목록 조회에 성공한다</p>
<pre><code>TypeError: listMemo is not a function

  3 | describe(&quot;listMemo&quot;, () =&gt; {
  4 |   test(&quot;page와 pageSize가 정상 입력이면 memo 목록 조회에 성공한다&quot;, async () =&gt; {
&gt; 5 |     const result = await listMemo({
    |                          ^
  6 |       page: 1,
  7 |       pageSize: 10,
  8 |     });

  at Object.listMemo (__test__/memo/readMemoList.test.js:5:26)</code></pre><p>  ● listMemo › page가 없으면 에러 발생</p>
<pre><code>TypeError: listMemo is not a function

  13 |   test(&quot;page가 없으면 에러 발생&quot;, async () =&gt; {
  14 |     await expect(
&gt; 15 |       listMemo({
     |       ^
  16 |         pageSize: 10,
  17 |       }),
  18 |     ).rejects.toThrow(&quot;page&quot;);

  at Object.listMemo (__test__/memo/readMemoList.test.js:15:7)</code></pre><p>  ● listMemo › pageSize가 없으면 에러 발생</p>
<pre><code>TypeError: listMemo is not a function

  21 |   test(&quot;pageSize가 없으면 에러 발생&quot;, async () =&gt; {
  22 |     await expect(
&gt; 23 |       listMemo({
     |       ^
  24 |         page: 1,
  25 |       }),
  26 |     ).rejects.toThrow(&quot;pageSize&quot;);

  at Object.listMemo (__test__/memo/readMemoList.test.js:23:7)</code></pre><p>  ● listMemo › page가 숫자가 아니면 에러 발생</p>
<pre><code>TypeError: listMemo is not a function

  29 |   test(&quot;page가 숫자가 아니면 에러 발생&quot;, async () =&gt; {
  30 |     await expect(
&gt; 31 |       listMemo({
     |       ^
  32 |         page: &quot;1&quot;,
  33 |         pageSize: 10,
  34 |       }),

  at Object.listMemo (__test__/memo/readMemoList.test.js:31:7)</code></pre><p>  ● listMemo › pageSize가 숫자가 아니면 에러 발생</p>
<pre><code>TypeError: listMemo is not a function

  38 |   test(&quot;pageSize가 숫자가 아니면 에러 발생&quot;, async () =&gt; {
  39 |     await expect(
&gt; 40 |       listMemo({
     |       ^
  41 |         page: 1,
  42 |         pageSize: &quot;10&quot;,
  43 |       }),

  at Object.listMemo (__test__/memo/readMemoList.test.js:40:7)</code></pre><p>  ● listMemo › page가 1보다 작으면 에러 발생</p>
<pre><code>TypeError: listMemo is not a function

  47 |   test(&quot;page가 1보다 작으면 에러 발생&quot;, async () =&gt; {
  48 |     await expect(
&gt; 49 |       listMemo({
     |       ^
  50 |         page: 0,
  51 |         pageSize: 10,
  52 |       }),

  at Object.listMemo (__test__/memo/readMemoList.test.js:49:7)</code></pre><p>  ● listMemo › pageSize가 1보다 작으면 에러 발생</p>
<pre><code>TypeError: listMemo is not a function

  56 |   test(&quot;pageSize가 1보다 작으면 에러 발생&quot;, async () =&gt; {
  57 |     await expect(
&gt; 58 |       listMemo({
     |       ^
  59 |         page: 1,
  60 |         pageSize: 0,
  61 |       }),

  at Object.listMemo (__test__/memo/readMemoList.test.js:58:7)</code></pre><p>  ● listMemo › isSuccess가 boolean이 아니면 에러 발생</p>
<pre><code>TypeError: listMemo is not a function

  65 |   test(&quot;isSuccess가 boolean이 아니면 에러 발생&quot;, async () =&gt; {
  66 |     await expect(
&gt; 67 |       listMemo({
     |       ^
  68 |         page: 1,
  69 |         pageSize: 10,
  70 |         isSuccess: &quot;true&quot;,

  at Object.listMemo (__test__/memo/readMemoList.test.js:67:7)</code></pre><p>  ● listMemo › isSuccess가 true이면 성공한 메모만 조회</p>
<pre><code>TypeError: listMemo is not a function

  74 |
  75 |   test(&quot;isSuccess가 true이면 성공한 메모만 조회&quot;, async () =&gt; {
&gt; 76 |     const result = await listMemo({
     |                          ^
  77 |       page: 1,
  78 |       pageSize: 10,
  79 |       isSuccess: true,

  at Object.listMemo (__test__/memo/readMemoList.test.js:76:26)</code></pre><p>  ● listMemo › isSuccess가 false이면 진행 중 메모만 조회</p>
<pre><code>TypeError: listMemo is not a function

  87 |
  88 |   test(&quot;isSuccess가 false이면 진행 중 메모만 조회&quot;, async () =&gt; {
&gt; 89 |     const result = await listMemo({
     |                          ^
  90 |       page: 1,
  91 |       pageSize: 10,
  92 |       isSuccess: false,

  at Object.listMemo (__test__/memo/readMemoList.test.js:89:26)</code></pre><p>  ● listMemo › priority가 숫자가 아니면 에러 발생</p>
<pre><code>TypeError: listMemo is not a function

  101 |   test(&quot;priority가 숫자가 아니면 에러 발생&quot;, async () =&gt; {
  102 |     await expect(
&gt; 103 |       listMemo({
      |       ^
  104 |         page: 1,
  105 |         pageSize: 10,
  106 |         priority: &quot;3&quot;,

  at Object.listMemo (__test__/memo/readMemoList.test.js:103:7)</code></pre><p>  ● listMemo › priority가 1보다 작으면 에러 발생</p>
<pre><code>TypeError: listMemo is not a function

  111 |   test(&quot;priority가 1보다 작으면 에러 발생&quot;, async () =&gt; {
  112 |     await expect(
&gt; 113 |       listMemo({
      |       ^
  114 |         page: 1,
  115 |         pageSize: 10,
  116 |         priority: 0,

  at Object.listMemo (__test__/memo/readMemoList.test.js:113:7)</code></pre><p>  ● listMemo › priority가 5보다 크면 에러 발생</p>
<pre><code>TypeError: listMemo is not a function

  121 |   test(&quot;priority가 5보다 크면 에러 발생&quot;, async () =&gt; {
  122 |     await expect(
&gt; 123 |       listMemo({
      |       ^
  124 |         page: 1,
  125 |         pageSize: 10,
  126 |         priority: 6,

  at Object.listMemo (__test__/memo/readMemoList.test.js:123:7)</code></pre><p>  ● listMemo › priority가 주어지면 해당 priority 메모만 조회</p>
<pre><code>TypeError: listMemo is not a function

  130 |
  131 |   test(&quot;priority가 주어지면 해당 priority 메모만 조회&quot;, async () =&gt; {
&gt; 132 |     const result = await listMemo({
      |                          ^
  133 |       page: 1,
  134 |       pageSize: 10,
  135 |       priority: 3,

  at Object.listMemo (__test__/memo/readMemoList.test.js:132:26)</code></pre><p>Test Suites: 1 failed, 2 passed, 3 total
Tests:       14 failed, 23 passed, 37 total
Snapshots:   0 total
Time:        0.488 s, estimated 1 s
Ran all test suites.</p>
<h4 id="green">Green</h4>
</li>
<li><p>단건 조회</p>
<ul>
<li><p>코드
```
async function readMemo(id) {
  if (typeof id !== &quot;number&quot; || id &lt;= 0) {</p>
<pre><code>throw new Error(&quot;id&quot;);</code></pre><p>  }</p>
<p>  const mockMemos = [</p>
<pre><code>{
  id: 1,
  title: &quot;TDD 메모 작성&quot;,
  period: {
    startDate: &quot;2026-03-06&quot;,
    endDate: &quot;2026-03-10&quot;,
  },
  content: &quot;조회 테스트용 메모&quot;,
  priority: 3,
  isSuccess: false,
},
{
  id: 2,
  title: &quot;두 번째 메모&quot;,
  period: {
    startDate: &quot;2026-03-07&quot;,
    endDate: &quot;2026-03-12&quot;,
  },
  content: &quot;두 번째 조회 테스트용 메모&quot;,
  priority: 2,
  isSuccess: false,
},</code></pre><p>  ];</p>
<p>  const memo = mockMemos.find((item) =&gt; item.id === id);</p>
<p>  if (!memo) {</p>
<pre><code>throw new Error(&quot;memo&quot;);</code></pre><p>  }</p>
<p>  return memo;
}</p>
<p>module.exports = { readMemo };</p>
</li>
<li><p>리스트 조회</p>
</li>
</ul>
</li>
<li><p>코드</p>
<pre><code>async function readMemo(id) {
  if (typeof id !== &quot;number&quot; || id &lt;= 0) {
    throw new Error(&quot;id&quot;);
  }

  const mockMemos = [
    {
      id: 1,
      title: &quot;TDD 메모 작성&quot;,
      period: {
        startDate: &quot;2026-03-06&quot;,
        endDate: &quot;2026-03-10&quot;,
      },
      content: &quot;조회 테스트용 메모&quot;,
      priority: 3,
      isSuccess: false,
    },
    {
      id: 2,
      title: &quot;두 번째 메모&quot;,
      period: {
        startDate: &quot;2026-03-07&quot;,
        endDate: &quot;2026-03-12&quot;,
      },
      content: &quot;두 번째 조회 테스트용 메모&quot;,
      priority: 2,
      isSuccess: false,
    },
  ];

  const memo = mockMemos.find((item) =&gt; item.id === id);

  if (!memo) {
    throw new Error(&quot;memo&quot;);
  }

  return memo;
}
async function listMemo(query) {
  const { page, pageSize, isSuccess, priority } = query;

  if (typeof page !== &quot;number&quot; || page &lt; 1) {
    throw new Error(&quot;page&quot;);
  }

  if (typeof pageSize !== &quot;number&quot; || pageSize &lt; 1) {
    throw new Error(&quot;pageSize&quot;);
  }

  if (isSuccess !== undefined &amp;&amp; typeof isSuccess !== &quot;boolean&quot;) {
    throw new Error(&quot;isSuccess&quot;);
  }

  if (priority !== undefined) {
    if (typeof priority !== &quot;number&quot; || priority &lt; 1 || priority &gt; 5) {
      throw new Error(&quot;priority&quot;);
    }
  }

  const mockMemos = [
    {
      id: 1,
      title: &quot;TDD 메모 작성&quot;,
      period: {
        startDate: &quot;2026-03-06&quot;,
        endDate: &quot;2026-03-10&quot;,
      },
      content: &quot;첫 번째 메모&quot;,
      priority: 3,
      isSuccess: false,
    },
    {
      id: 2,
      title: &quot;두 번째 메모&quot;,
      period: {
        startDate: &quot;2026-03-07&quot;,
        endDate: &quot;2026-03-12&quot;,
      },
      content: &quot;두 번째 내용&quot;,
      priority: 2,
      isSuccess: true,
    },
    {
      id: 3,
      title: &quot;세 번째 메모&quot;,
      period: {
        startDate: &quot;2026-03-08&quot;,
        endDate: &quot;2026-03-15&quot;,
      },
      content: &quot;세 번째 내용&quot;,
      priority: 3,
      isSuccess: true,
    },
    {
      id: 4,
      title: &quot;네 번째 메모&quot;,
      period: {
        startDate: &quot;2026-03-09&quot;,
        endDate: &quot;2026-03-18&quot;,
      },
      content: &quot;네 번째 내용&quot;,
      priority: 1,
      isSuccess: false,
    },
  ];

  let result = mockMemos;

  if (isSuccess !== undefined) {
    result = result.filter((memo) =&gt; memo.isSuccess === isSuccess);
  }

  if (priority !== undefined) {
    result = result.filter((memo) =&gt; memo.priority === priority);
  }

  const startIndex = (page - 1) * pageSize;
  const endIndex = startIndex + pageSize;

  return result.slice(startIndex, endIndex);
}

module.exports = { readMemo, listMemo };
</code></pre></li>
</ul>
<ul>
<li><p>실행 로그</p>
<ul>
<li><p>단건 조회</p>
<pre><code>  hongseongchae@hongseongchaeui-MacBookPro tdd_study % npm test

  &gt; tdd_study@1.0.0 test
  &gt; jest

   PASS  __test__/memo/readMemo.test.js
   PASS  __test__/memo/createMemo.test.js

  Test Suites: 2 passed, 2 total
  Tests:       23 passed, 23 total
  Snapshots:   0 total
  Time:        0.411 s, estimated 1 s
  Ran all test suites.
- 리스트 조회</code></pre><p>  hongseongchae@hongseongchaeui-MacBookPro tdd_study % npm test</p>
<blockquote>
<p><a href="mailto:tdd_study@1.0.0">tdd_study@1.0.0</a> test
jest</p>
</blockquote>
<p> PASS  <strong>test</strong>/memo/readMemoList.test.js
 PASS  <strong>test</strong>/memo/createMemo.test.js
 PASS  <strong>test</strong>/memo/readMemo.test.js</p>
<p>Test Suites: 3 passed, 3 total
Tests:       37 passed, 37 total
Snapshots:   0 total
Time:        0.364 s, estimated 1 s
Ran all test suites.</p>
<h4 id="blue">blue</h4>
</li>
<li><p>코드</p>
<pre><code>  const mockMemos = [
  {
    id: 1,
    title: &quot;TDD 메모 작성&quot;,
    period: {
      startDate: &quot;2026-03-06&quot;,
      endDate: &quot;2026-03-10&quot;,
    },
    content: &quot;첫 번째 메모&quot;,
    priority: 3,
    isSuccess: false,
  },
  {
    id: 2,
    title: &quot;두 번째 메모&quot;,
    period: {
      startDate: &quot;2026-03-07&quot;,
      endDate: &quot;2026-03-12&quot;,
    },
    content: &quot;두 번째 내용&quot;,
    priority: 2,
    isSuccess: true,
  },
  {
    id: 3,
    title: &quot;세 번째 메모&quot;,
    period: {
      startDate: &quot;2026-03-08&quot;,
      endDate: &quot;2026-03-15&quot;,
    },
    content: &quot;세 번째 내용&quot;,
    priority: 3,
    isSuccess: true,
  },
  {
    id: 4,
    title: &quot;네 번째 메모&quot;,
    period: {
      startDate: &quot;2026-03-09&quot;,
      endDate: &quot;2026-03-18&quot;,
    },
    content: &quot;네 번째 내용&quot;,
    priority: 1,
    isSuccess: false,
  },
];

function validatePage(page) {
  if (typeof page !== &quot;number&quot; || page &lt; 1) {
    throw new Error(&quot;page&quot;);
  }
}

function validatePageSize(pageSize) {
  if (typeof pageSize !== &quot;number&quot; || pageSize &lt; 1) {
    throw new Error(&quot;pageSize&quot;);
  }
}

function validateIsSuccess(isSuccess) {
  if (isSuccess !== undefined &amp;&amp; typeof isSuccess !== &quot;boolean&quot;) {
    throw new Error(&quot;isSuccess&quot;);
  }
}

function validatePriority(priority) {
  if (priority !== undefined) {
    if (typeof priority !== &quot;number&quot; || priority &lt; 1 || priority &gt; 5) {
      throw new Error(&quot;priority&quot;);
    }
  }
}

function filterByIsSuccess(memos, isSuccess) {
  if (isSuccess === undefined) {
    return memos;
  }

  return memos.filter((memo) =&gt; memo.isSuccess === isSuccess);
}

function filterByPriority(memos, priority) {
  if (priority === undefined) {
    return memos;
  }

  return memos.filter((memo) =&gt; memo.priority === priority);
}

async function readMemo(id) {
  if (typeof id !== &quot;number&quot; || id &lt;= 0) {
    throw new Error(&quot;id&quot;);
  }

  const memo = mockMemos.find((item) =&gt; item.id === id);

  if (!memo) {
    throw new Error(&quot;memo&quot;);
  }

  return memo;
}

async function listMemo(query) {
  const { page, pageSize, isSuccess, priority } = query;

  validatePage(page);
  validatePageSize(pageSize);
  validateIsSuccess(isSuccess);
  validatePriority(priority);

  let result = mockMemos;

  result = filterByIsSuccess(result, isSuccess);
  result = filterByPriority(result, priority);

  const startIndex = (page - 1) * pageSize;
  const endIndex = startIndex + pageSize;

  return result.slice(startIndex, endIndex);
}

module.exports = { readMemo, listMemo };</code></pre></li>
</ul>
</li>
<li><p>실행 로그
```
hongseongchae@hongseongchaeui-MacBookPro tdd_study % npm test</p>
<blockquote>
<p><a href="mailto:tdd_study@1.0.0">tdd_study@1.0.0</a> test
jest</p>
</blockquote>
<p> PASS  <strong>test</strong>/memo/readMemo.test.js
 PASS  <strong>test</strong>/memo/readMemoList.test.js
 PASS  <strong>test</strong>/memo/createMemo.test.js</p>
<p>Test Suites: 3 passed, 3 total
Tests:       37 passed, 37 total
Snapshots:   0 total
Time:        0.647 s, estimated 1 s
Ran all test suites.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TDD 실습 (1)]]></title>
            <link>https://velog.io/@back_end_sc/TDD-%EC%8B%A4%EC%8A%B5-1</link>
            <guid>https://velog.io/@back_end_sc/TDD-%EC%8B%A4%EC%8A%B5-1</guid>
            <pubDate>Sun, 08 Mar 2026 06:19:40 GMT</pubDate>
            <description><![CDATA[<h3 id="실습-주제">실습 주제</h3>
<ul>
<li>간단한 메모 CRUD</li>
</ul>
<h3 id="메모-등록">메모 등록</h3>
<h4 id="red">Red</h4>
<ul>
<li><p>메모 등록에 값의 범위, 필수 값 누락 등에 대한 테스트 진행</p>
<ul>
<li><p>title : 누락, 공백, 타입 오류</p>
</li>
<li><p>period : 누락, 내부 필든 누락, 날짜 순서 날짜 형식 오류</p>
</li>
<li><p>content : 누락, 공백, 타입 오류</p>
</li>
<li><p>priority : 누락, 타입 오류, 범위 오류</p>
</li>
<li><p>코드</p>
<pre><code>const { createMemo } = require(&quot;../../src/memo/createMemo&quot;);

describe(&quot;createMemo&quot;, () =&gt; {
  const validInput = {
    title: &quot;TDD 메모 작성&quot;,
    period: {
      startDate: &quot;2026-03-06&quot;,
      endDate: &quot;2026-03-10&quot;,
    },
    content: &quot;Red 단계 테스트 작성&quot;,
    priority: 3,
  };

  test(&quot;정상 입력이면 memo 생성에 성공한다&quot;, async () =&gt; {
    const result = await createMemo(validInput);

    expect(result).toMatchObject({
      title: &quot;TDD 메모 작성&quot;,
      period: {
        startDate: &quot;2026-03-06&quot;,
        endDate: &quot;2026-03-10&quot;,
      },
      content: &quot;Red 단계 테스트 작성&quot;,
      priority: 3,
      isSuccess: false,
    });
  });

  test(&quot;title이 없으면 에러 발생&quot;, async () =&gt; {
    await expect(
      createMemo({
        ...validInput,
        title: &quot;&quot;,
      }),
    ).rejects.toThrow(&quot;title&quot;);
  });

  test(&quot;title이 공백만 있으면 에러 발생&quot;, async () =&gt; {
    await expect(
      createMemo({
        ...validInput,
        title: &quot;   &quot;,
      }),
    ).rejects.toThrow(&quot;title&quot;);
  });

  test(&quot;title이 문자열이 아니면 에러 발생&quot;, async () =&gt; {
    await expect(
      createMemo({
        ...validInput,
        title: 1234,
      }),
    ).rejects.toThrow(&quot;title&quot;);
  });

  test(&quot;period가 없으면 에러 발생&quot;, async () =&gt; {
    await expect(
      createMemo({
        ...validInput,
        period: null,
      }),
    ).rejects.toThrow(&quot;period&quot;);
  });

  test(&quot;period의 startDate가 없으면 에러 발생&quot;, async () =&gt; {
    await expect(
      createMemo({
        ...validInput,
        period: {
          endDate: &quot;2026-03-10&quot;,
        },
      }),
    ).rejects.toThrow(&quot;period&quot;);
  });

  test(&quot;period의 endDate가 없으면 에러 발생&quot;, async () =&gt; {
    await expect(
      createMemo({
        ...validInput,
        period: {
          startDate: &quot;2026-03-06&quot;,
        },
      }),
    ).rejects.toThrow(&quot;period&quot;);
  });

  test(&quot;기간 시작일이 종료일보다 뒤면 에러 발생&quot;, async () =&gt; {
    await expect(
      createMemo({
        ...validInput,
        period: {
          startDate: &quot;2026-03-11&quot;,
          endDate: &quot;2026-03-10&quot;,
        },
      }),
    ).rejects.toThrow(&quot;period&quot;);
  });

  test(&quot;period 날짜 형식이 올바르지 않으면 에러 발생&quot;, async () =&gt; {
    await expect(
      createMemo({
        ...validInput,
        period: {
          startDate: &quot;wrong-date&quot;,
          endDate: &quot;2026-03-10&quot;,
        },
      }),
    ).rejects.toThrow(&quot;period&quot;);
  });

  test(&quot;content가 없으면 에러 발생&quot;, async () =&gt; {
    await expect(
      createMemo({
        ...validInput,
        content: &quot;&quot;,
      }),
    ).rejects.toThrow(&quot;content&quot;);
  });

  test(&quot;content가 공백만 있으면 에러 발생&quot;, async () =&gt; {
    await expect(
      createMemo({
        ...validInput,
        content: &quot;   &quot;,
      }),
    ).rejects.toThrow(&quot;content&quot;);
  });

  test(&quot;content가 문자열이 아니면 에러 발생&quot;, async () =&gt; {
    await expect(
      createMemo({
        ...validInput,
        content: 1234,
      }),
    ).rejects.toThrow(&quot;content&quot;);
  });

  test(&quot;priority가 없으면 에러 발생&quot;, async () =&gt; {
    await expect(
      createMemo({
        ...validInput,
        priority: undefined,
      }),
    ).rejects.toThrow(&quot;priority&quot;);
  });

  test(&quot;priority가 숫자가 아니면 에러 발생&quot;, async () =&gt; {
    await expect(
      createMemo({
        ...validInput,
        priority: &quot;3&quot;,
      }),
    ).rejects.toThrow(&quot;priority&quot;);
  });

  test(&quot;priority가 1보다 작으면 에러 발생&quot;, async () =&gt; {
    await expect(
      createMemo({
        ...validInput,
        priority: 0,
      }),
    ).rejects.toThrow(&quot;priority&quot;);
  });

  test(&quot;priority가 5보다 크면 에러 발생&quot;, async () =&gt; {
    await expect(
      createMemo({
        ...validInput,
        priority: 6,
      }),
    ).rejects.toThrow(&quot;priority&quot;);
  });
});</code></pre></li>
<li><p>실행 로그</p>
<pre><code>hongseongchae@hongseongchaeui-MacBookPro tdd_study % npm test

&gt; tdd_study@1.0.0 test
&gt; jest

 FAIL  __test__/memo/createMemo.test.js
  ● createMemo › 정상 입력이면 memo 생성에 성공한다

TypeError: createMemo is not a function

  13 |
  14 |   test(&quot;정상 입력이면 memo 생성에 성공한다&quot;, async () =&gt; {
&gt; 15 |     const result = await createMemo(validInput);
     |                          ^
  16 |
  17 |     expect(result).toMatchObject({
  18 |       title: &quot;TDD 메모 작성&quot;,

  at Object.createMemo (__test__/memo/createMemo.test.js:15:26)

  ● createMemo › title이 없으면 에러 발생

TypeError: createMemo is not a function

  29 |   test(&quot;title이 없으면 에러 발생&quot;, async () =&gt; {
  30 |     await expect(
&gt; 31 |       createMemo({
     |       ^
  32 |         ...validInput,
  33 |         title: &quot;&quot;,
  34 |       }),

  at Object.createMemo (__test__/memo/createMemo.test.js:31:7)

  ● createMemo › title이 공백만 있으면 에러 발생

TypeError: createMemo is not a function

  38 |   test(&quot;title이 공백만 있으면 에러 발생&quot;, async () =&gt; {
  39 |     await expect(
&gt; 40 |       createMemo({
     |       ^
  41 |         ...validInput,
  42 |         title: &quot;   &quot;,
  43 |       }),

  at Object.createMemo (__test__/memo/createMemo.test.js:40:7)

  ● createMemo › title이 문자열이 아니면 에러 발생

TypeError: createMemo is not a function

  47 |   test(&quot;title이 문자열이 아니면 에러 발생&quot;, async () =&gt; {
  48 |     await expect(
&gt; 49 |       createMemo({
     |       ^
  50 |         ...validInput,
  51 |         title: 1234,
  52 |       }),

  at Object.createMemo (__test__/memo/createMemo.test.js:49:7)

  ● createMemo › period가 없으면 에러 발생

TypeError: createMemo is not a function

  56 |   test(&quot;period가 없으면 에러 발생&quot;, async () =&gt; {
  57 |     await expect(
&gt; 58 |       createMemo({
     |       ^
  59 |         ...validInput,
  60 |         period: null,
  61 |       }),

  at Object.createMemo (__test__/memo/createMemo.test.js:58:7)

  ● createMemo › period의 startDate가 없으면 에러 발생

TypeError: createMemo is not a function

  65 |   test(&quot;period의 startDate가 없으면 에러 발생&quot;, async () =&gt; {
  66 |     await expect(
&gt; 67 |       createMemo({
     |       ^
  68 |         ...validInput,
  69 |         period: {
  70 |           endDate: &quot;2026-03-10&quot;,

  at Object.createMemo (__test__/memo/createMemo.test.js:67:7)

  ● createMemo › period의 endDate가 없으면 에러 발생

TypeError: createMemo is not a function

  76 |   test(&quot;period의 endDate가 없으면 에러 발생&quot;, async () =&gt; {
  77 |     await expect(
&gt; 78 |       createMemo({
     |       ^
  79 |         ...validInput,
  80 |         period: {
  81 |           startDate: &quot;2026-03-06&quot;,

  at Object.createMemo (__test__/memo/createMemo.test.js:78:7)

  ● createMemo › 기간 시작일이 종료일보다 뒤면 에러 발생

TypeError: createMemo is not a function

  87 |   test(&quot;기간 시작일이 종료일보다 뒤면 에러 발생&quot;, async () =&gt; {
  88 |     await expect(
&gt; 89 |       createMemo({
     |       ^
  90 |         ...validInput,
  91 |         period: {
  92 |           startDate: &quot;2026-03-11&quot;,

  at Object.createMemo (__test__/memo/createMemo.test.js:89:7)

  ● createMemo › period 날짜 형식이 올바르지 않으면 에러 발생

TypeError: createMemo is not a function

   99 |   test(&quot;period 날짜 형식이 올바르지 않으면 에러 발생&quot;, async () =&gt; {
  100 |     await expect(
&gt; 101 |       createMemo({
      |       ^
  102 |         ...validInput,
  103 |         period: {
  104 |           startDate: &quot;wrong-date&quot;,

  at Object.createMemo (__test__/memo/createMemo.test.js:101:7)

  ● createMemo › content가 없으면 에러 발생

TypeError: createMemo is not a function

  111 |   test(&quot;content가 없으면 에러 발생&quot;, async () =&gt; {
  112 |     await expect(
&gt; 113 |       createMemo({
      |       ^
  114 |         ...validInput,
  115 |         content: &quot;&quot;,
  116 |       }),

  at Object.createMemo (__test__/memo/createMemo.test.js:113:7)

  ● createMemo › content가 공백만 있으면 에러 발생

TypeError: createMemo is not a function

  120 |   test(&quot;content가 공백만 있으면 에러 발생&quot;, async () =&gt; {
  121 |     await expect(
&gt; 122 |       createMemo({
      |       ^
  123 |         ...validInput,
  124 |         content: &quot;   &quot;,
  125 |       }),

  at Object.createMemo (__test__/memo/createMemo.test.js:122:7)

  ● createMemo › content가 문자열이 아니면 에러 발생

TypeError: createMemo is not a function

  129 |   test(&quot;content가 문자열이 아니면 에러 발생&quot;, async () =&gt; {
  130 |     await expect(
&gt; 131 |       createMemo({
      |       ^
  132 |         ...validInput,
  133 |         content: 1234,
  134 |       }),

  at Object.createMemo (__test__/memo/createMemo.test.js:131:7)

  ● createMemo › priority가 없으면 에러 발생

TypeError: createMemo is not a function

  138 |   test(&quot;priority가 없으면 에러 발생&quot;, async () =&gt; {
  139 |     await expect(
&gt; 140 |       createMemo({
      |       ^
  141 |         ...validInput,
  142 |         priority: undefined,
  143 |       }),

  at Object.createMemo (__test__/memo/createMemo.test.js:140:7)

  ● createMemo › priority가 숫자가 아니면 에러 발생

TypeError: createMemo is not a function

  147 |   test(&quot;priority가 숫자가 아니면 에러 발생&quot;, async () =&gt; {
  148 |     await expect(
&gt; 149 |       createMemo({
      |       ^
  150 |         ...validInput,
  151 |         priority: &quot;3&quot;,
  152 |       }),

  at Object.createMemo (__test__/memo/createMemo.test.js:149:7)

  ● createMemo › priority가 1보다 작으면 에러 발생

TypeError: createMemo is not a function

  156 |   test(&quot;priority가 1보다 작으면 에러 발생&quot;, async () =&gt; {
  157 |     await expect(
&gt; 158 |       createMemo({
      |       ^
  159 |         ...validInput,
  160 |         priority: 0,
  161 |       }),

  at Object.createMemo (__test__/memo/createMemo.test.js:158:7)

● createMemo › priority가 5보다 크면 에러 발생

  TypeError: createMemo is not a function

  165 |   test(&quot;priority가 5보다 크면 에러 발생&quot;, async () =&gt; {
  166 |     await expect(
&gt; 167 |       createMemo({
      |       ^
  168 |         ...validInput,
  169 |         priority: 6,
  170 |       }),

  at Object.createMemo (__test__/memo/createMemo.test.js:167:7)

</code></pre></li>
</ul>
</li>
</ul>
<pre><code>            Test Suites: 1 failed, 1 total
            Tests:       16 failed, 16 total
            Snapshots:   0 total
            Time:        1.01 s</code></pre><p>Red인 경우 테스트 코드를 정의하는 단계(실패하는 케이스)이므로 코드가 구현되어있지않으므로 모두 실패로 나온다.</p>
<h4 id="green">Green</h4>
<ul>
<li><p>Red를 바탕으로 실행 코드를 작성한다</p>
</li>
<li><p>코드</p>
<pre><code>  async function createMemo(input) {
  const { title, period, content, priority } = input;

  if (typeof title !== &quot;string&quot; || title.trim() === &quot;&quot;) {
    throw new Error(&quot;title&quot;);
  }

  if (
    !period ||
    typeof period.startDate !== &quot;string&quot; ||
    typeof period.endDate !== &quot;string&quot;
  ) {
    throw new Error(&quot;period&quot;);
  }

  const start = new Date(period.startDate);
  const end = new Date(period.endDate);

  if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) {
    throw new Error(&quot;period&quot;);
  }

  if (start &gt; end) {
    throw new Error(&quot;period&quot;);
  }

  if (typeof content !== &quot;string&quot; || content.trim() === &quot;&quot;) {
    throw new Error(&quot;content&quot;);
  }

  if (typeof priority !== &quot;number&quot; || priority &lt; 1 || priority &gt; 5) {
    throw new Error(&quot;priority&quot;);
  }

  return {
    title,
    period,
    content,
    priority,
    isSuccess: false,
  };
}

module.exports = { createMemo };</code></pre></li>
<li><p>실행 로그</p>
<pre><code>hongseongchae@hongseongchaeui-MacBookPro tdd_study % npm test

&gt; tdd_study@1.0.0 test
&gt; jest

 PASS  __test__/memo/createMemo.test.js
  createMemo
    ✓ 정상 입력이면 memo 생성에 성공한다 (3 ms)
    ✓ title이 없으면 에러 발생 (8 ms)
    ✓ title이 공백만 있으면 에러 발생 (3 ms)
    ✓ title이 문자열이 아니면 에러 발생 (1 ms)
    ✓ period가 없으면 에러 발생
    ✓ period의 startDate가 없으면 에러 발생 (1 ms)
    ✓ period의 endDate가 없으면 에러 발생 (1 ms)
    ✓ 기간 시작일이 종료일보다 뒤면 에러 발생 (1 ms)
    ✓ period 날짜 형식이 올바르지 않으면 에러 발생 (1 ms)
    ✓ content가 없으면 에러 발생
    ✓ content가 공백만 있으면 에러 발생 (1 ms)
    ✓ content가 문자열이 아니면 에러 발생
    ✓ priority가 없으면 에러 발생
    ✓ priority가 숫자가 아니면 에러 발생 (1 ms)
    ✓ priority가 1보다 작으면 에러 발생 (1 ms)
    ✓ priority가 5보다 크면 에러 발생

  Test Suites: 1 passed, 1 total
  Tests:       16 passed, 16 total
  Snapshots:   0 total
  Time:        0.215 s, estimated 1 s
  Ran all test suites.
</code></pre></li>
</ul>
<h4 id="blue">blue</h4>
<ul>
<li><p>Green을 통과한 기준으로 refactoring를 진행한다.</p>
</li>
<li><p>검증함수 분리</p>
</li>
<li><p>코드</p>
<pre><code>  function validateTitle(title) {
    if (typeof title !== &quot;string&quot; || title.trim() === &quot;&quot;) {
      throw new Error(&quot;title&quot;);
    }
  }

  function validateContent(content) {
    if (typeof content !== &quot;string&quot; || content.trim() === &quot;&quot;) {
      throw new Error(&quot;content&quot;);
    }
  }

  function validatePriority(priority) {
    if (typeof priority !== &quot;number&quot; || priority &lt; 1 || priority &gt; 5) {
      throw new Error(&quot;priority&quot;);
    }
  }

  function validatePeriod(period) {
    if (
      !period ||
      typeof period.startDate !== &quot;string&quot; ||
      typeof period.endDate !== &quot;string&quot;
    ) {
      throw new Error(&quot;period&quot;);
    }

    if (!isValidDateRange(period.startDate, period.endDate)) {
      throw new Error(&quot;period&quot;);
    }
  }

  function isValidDateRange(startDate, endDate) {
    const start = new Date(startDate);
    const end = new Date(endDate);

    if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) {
      return false;
    }

    if (start &gt; end) {
      return false;
    }

    return true;
  }

  async function createMemo(input) {
    const { title, period, content, priority } = input;

    validateTitle(title);
    validatePeriod(period);
    validateContent(content);
    validatePriority(priority);

    return {
      title,
      period,
      content,
      priority,
      isSuccess: false,
    };
  }

  module.exports = { createMemo };</code></pre></li>
<li><p>실행 로그</p>
<pre><code>  hongseongchae@hongseongchaeui-MacBookPro tdd_study % npm test

  &gt; tdd_study@1.0.0 test
  &gt; jest

   PASS  __test__/memo/createMemo.test.js
    createMemo
      ✓ 정상 입력이면 memo 생성에 성공한다 (3 ms)
      ✓ title이 없으면 에러 발생 (8 ms)
      ✓ title이 공백만 있으면 에러 발생 (1 ms)
      ✓ title이 문자열이 아니면 에러 발생 (1 ms)
      ✓ period가 없으면 에러 발생 (1 ms)
      ✓ period의 startDate가 없으면 에러 발생 (1 ms)
      ✓ period의 endDate가 없으면 에러 발생 (1 ms)
      ✓ 기간 시작일이 종료일보다 뒤면 에러 발생 (1 ms)
      ✓ period 날짜 형식이 올바르지 않으면 에러 발생 (1 ms)
      ✓ content가 없으면 에러 발생 (1 ms)
      ✓ content가 공백만 있으면 에러 발생 (2 ms)
      ✓ content가 문자열이 아니면 에러 발생 (1 ms)
      ✓ priority가 없으면 에러 발생
      ✓ priority가 숫자가 아니면 에러 발생 (1 ms)
      ✓ priority가 1보다 작으면 에러 발생
      ✓ priority가 5보다 크면 에러 발생

  Test Suites: 1 passed, 1 total
  Tests:       16 passed, 16 total
  Snapshots:   0 total
  Time:        0.339 s, estimated 1 s
  Ran all test suites.</code></pre></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TDD(Test Driven development)란?]]></title>
            <link>https://velog.io/@back_end_sc/TDDTest-Driven-development%EB%9E%80</link>
            <guid>https://velog.io/@back_end_sc/TDDTest-Driven-development%EB%9E%80</guid>
            <pubDate>Sun, 08 Mar 2026 06:06:10 GMT</pubDate>
            <description><![CDATA[<h3 id="tdd란">TDD란?</h3>
<ul>
<li>Test Driven Development의 약자</li>
<li>테스트 코드를 먼저 작성하고 그 테스트를 통과하도록 실제 코드를 구현하는 개발 방법론</li>
<li>짧은 개발 주기의 반복에 의존하는 개발 프로세스</li>
<li>애자일 방법론 중 하나인 eXtream Programming의 Test-First 개념에 기반을 둔 단순한 설계를 중요시한다.</li>
</ul>
<h3 id="개발-사이클">개발 사이클</h3>
<h4 id="red---green---refactor">Red -&gt; Green -&gt; Refactor</h4>
<p><img src="https://velog.velcdn.com/images/back_end_sc/post/ed8313ed-d7b6-4e95-8b42-62e3add91048/image.png" alt=""></p>
<ul>
<li><p>Red</p>
<ul>
<li>먼저 실패하는 테스트를 작성</li>
<li>아직 기능이 구현되지 않았기 때문에 테스트는 실패</li>
<li>ex)<pre><code>test(&quot;두 숫자를 더하면 결과가 나온다&quot;, () =&gt; {
  expect(add(2,3)).toBe(5);
 });</code></pre></li>
</ul>
</li>
<li><p>Green</p>
<ul>
<li><p>테스트가 통과하도록 최소한의 코드만 작성</p>
</li>
<li><p>ex)</p>
<pre><code>function add(a,b){
  return a+b;
}</code></pre></li>
<li><p>Refactor</p>
<ul>
<li>테스트가 통과하는 상태에서 코드를 개선한다.</li>
<li>중복 제거</li>
<li>구조 개선</li>
<li>가독성 개선</li>
<li>ex)<pre><code>function add(a,b){
return a+b;
}
</code></pre></li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="일반적인-개발-방식-vs-tdd-개발-방식">일반적인 개발 방식 vs TDD 개발 방식</h3>
<ul>
<li><p>일반 개발 방식</p>
<ul>
<li><p>방식</p>
<ul>
<li>요구사항 분석 -&gt; 설계 -&gt; 개발 -&gt; 테스트 -&gt; 배포</li>
</ul>
</li>
<li><p>장점</p>
<ul>
<li>개발 속도가 빠르다.</li>
<li>테스트 코드 유지 비용이 없다.</li>
<li>유연한 개발이 가능하다.</li>
</ul>
</li>
<li><p>단점</p>
<ul>
<li>소비자의 요구 사항이 명확하지 않을 수 있다.</li>
<li>자체 버그 검출 능력 저하 또는 소스코드의 품질이 저하될 수 있다.</li>
<li>기능이 많아질수록 버그가 발견되는 시점이 늦어진다.</li>
<li>작은 기능 수정 시 모든 부분을 테스트 진행해야되므로 버그 발견이 어렵다</li>
</ul>
</li>
</ul>
</li>
<li><p>TDD 개발 방식</p>
<ul>
<li>장점<ul>
<li>테스트 코드를 작성하는 도중에 발생하는 예외 사항(버그, 수정사항)들은 테스트 케이스에 추가하고 설계를 개선한다.</li>
<li>테스트 통과된 코드만이 개발 단계에서 실제 코드로 작성되므로 안정성이 있다.</li>
</ul>
</li>
<li>단점<ul>
<li>테스트 코드를 먼저 작성해야되므로 초기 개발 속도가 느리다.</li>
<li>테스트 코드 작성 난이도가 높다.</li>
<li>모든 기능에 적용하기 어렵다.<h3 id="tdd-효과">TDD 효과</h3>
</li>
</ul>
</li>
<li>버그를 조기에 발견할 수 있다.</li>
<li>선 테스트를 진행하므로 초기에 버그를 발견할 수 있다.</li>
<li>안정적인 리팩토링이 가능하다.</li>
<li>코드 설계 품질이 향상된다.</li>
<li>의존성 감소, 모듈화, 유지보수 용이 장점</li>
<li>요구사항을 명확하게 정의할 수 있다.</li>
<li>추가 구현이 용이하다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 - 132267]]></title>
            <link>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-132267</link>
            <guid>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-132267</guid>
            <pubDate>Sun, 25 Jan 2026 12:24:57 GMT</pubDate>
            <description><![CDATA[<h2 id="콜라-문제">콜라 문제</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>오래전 유행했던 콜라 문제가 있습니다. 콜라 문제의 지문은 다음과 같습니다.</p>
<p>정답은 아무에게도 말하지 마세요.</p>
<p>콜라 빈 병 2개를 가져다주면 콜라 1병을 주는 마트가 있다. 빈 병 20개를 가져다주면 몇 병을 받을 수 있는가?</p>
<p>단, 보유 중인 빈 병이 2개 미만이면, 콜라를 받을 수 없다.</p>
<p>문제를 풀던 상빈이는 콜라 문제의 완벽한 해답을 찾았습니다. 상빈이가 푼 방법은 아래 그림과 같습니다. 우선 콜라 빈 병 20병을 가져가서 10병을 받습니다. 받은 10병을 모두 마신 뒤, 가져가서 5병을 받습니다. 5병 중 4병을 모두 마신 뒤 가져가서 2병을 받고, 또 2병을 모두 마신 뒤 가져가서 1병을 받습니다. 받은 1병과 5병을 받았을 때 남은 1병을 모두 마신 뒤 가져가면 1병을 또 받을 수 있습니다. 이 경우 상빈이는 총 10 + 5 + 2 + 1 + 1 = 19병의 콜라를 받을 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/back_end_sc/post/031ffccb-739d-4ad5-8c50-9ccc17e2cff9/image.png" alt=""></p>
<p>문제를 열심히 풀던 상빈이는 일반화된 콜라 문제를 생각했습니다. 이 문제는 빈 병 a개를 가져다주면 콜라 b병을 주는 마트가 있을 때, 빈 병 n개를 가져다주면 몇 병을 받을 수 있는지 계산하는 문제입니다. 기존 콜라 문제와 마찬가지로, 보유 중인 빈 병이 a개 미만이면, 추가적으로 빈 병을 받을 순 없습니다. 상빈이는 열심히 고심했지만, 일반화된 콜라 문제의 답을 찾을 수 없었습니다. 상빈이를 도와, 일반화된 콜라 문제를 해결하는 프로그램을 만들어 주세요.</p>
<p>콜라를 받기 위해 마트에 주어야 하는 병 수 a, 빈 병 a개를 가져다 주면 마트가 주는 콜라 병 수 b, 상빈이가 가지고 있는 빈 병의 개수 n이 매개변수로 주어집니다. 상빈이가 받을 수 있는 콜라의 병 수를 return 하도록 solution 함수를 작성해주세요.</p>
<h3 id="제한-사항">제한 사항</h3>
<ul>
<li>1 ≤ b &lt; a ≤ n ≤ 1,000,000</li>
<li>정답은 항상 int 범위를 넘지 않게 주어집니다.<h3 id="입출력-예">입출력 예</h3>
<img src="https://velog.velcdn.com/images/back_end_sc/post/baf1c50d-9c49-4c26-9377-004a6dc00d5e/image.png" alt=""></li>
</ul>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<p>입출력 예 #1</p>
<ul>
<li><p>본문에서 설명한 예시입니다.
입출력 예 #2</p>
</li>
<li><p>빈 병 20개 중 18개를 마트에 가져가서, 6병의 콜라를 받습니다. 이때 상빈이가 가지고 있는 콜라 병의 수는 8(20 – 18 + 6 = 8)개 입니다.</p>
</li>
<li><p>빈 병 8개 중 6개를 마트에 가져가서, 2병의 콜라를 받습니다. 이때 상빈이가 가지고 있는 콜라 병의 수는 4(8 – 6 + 2 = 4)개 입니다.</p>
</li>
<li><p>빈 병 4 개중 3개를 마트에 가져가서, 1병의 콜라를 받습니다. 이때 상빈이가 가지고 있는 콜라 병의 수는 2(4 – 3 + 1 = 2)개 입니다.</p>
</li>
<li><p>3번의 교환 동안 상빈이는 9(6 + 2 + 1 = 9)병의 콜라를 받았습니다.</p>
<h3 id="제출-코드">제출 코드</h3>
<pre><code>function solution(a, b, n) {
  let answer = 0;

  while (n &gt;= a) {
      const exchanged = Math.floor(n / a); // 교환 횟수
      const received = exchanged * b;      // 받은 콜라

      answer += received;
      n = (n % a) + received;               // 남은 병 + 새 병
  }

  return answer;
}</code></pre></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 - 82612]]></title>
            <link>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-82612</link>
            <guid>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-82612</guid>
            <pubDate>Sun, 25 Jan 2026 12:20:40 GMT</pubDate>
            <description><![CDATA[<h2 id="부족한-금액-채우기">부족한 금액 채우기</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>새로 생긴 놀이기구는 인기가 매우 많아 줄이 끊이질 않습니다. 이 놀이기구의 원래 이용료는 price원 인데, 놀이기구를 N 번 째 이용한다면 원래 이용료의 N배를 받기로 하였습니다. 즉, 처음 이용료가 100이었다면 2번째에는 200, 3번째에는 300으로 요금이 인상됩니다.
놀이기구를 count번 타게 되면 현재 자신이 가지고 있는 금액에서 얼마가 모자라는지를 return 하도록 solution 함수를 완성하세요.
단, 금액이 부족하지 않으면 0을 return 하세요.</p>
<p>제한사항</p>
<ul>
<li>놀이기구의 이용료 price : 1 ≤ price ≤ 2,500, price는 자연수</li>
<li>처음 가지고 있던 금액 money : 1 ≤ money ≤ 1,000,000,000, money는 자연수</li>
<li>놀이기구의 이용 횟수 count : 1 ≤ count ≤ 2,500, count는 자연수</li>
</ul>
<h3 id="제한-사항">제한 사항</h3>
<ul>
<li>1,000 ≤ a, b ≤ 1,000</li>
</ul>
<h3 id="입출력-예">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/back_end_sc/post/db880b21-b50e-4eb3-8d20-3eef70f549e9/image.png" alt=""></p>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<p>입출력 예 #1</p>
<ul>
<li>이용금액이 3인 놀이기구를 4번 타고 싶은 고객이 현재 가진 금액이 20이라면, 총 필요한 놀이기구의 이용 금액은 30 (= 3+6+9+12) 이 되어 10만큼 부족하므로 10을 return 합니다.</li>
</ul>
<h3 id="제출-코드">제출 코드</h3>
<pre><code>function solution(price, total_money, count) {
    var answer = -1;
    let use_money = 0;
    for(let i = 1; i &lt; count+1; i++){
         use_money+=i*price;
        console.log(use_money);

    }

    if(total_money &lt; use_money){
        return use_money-total_money;
    }
    return 0;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 - 86491]]></title>
            <link>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-86491</link>
            <guid>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-86491</guid>
            <pubDate>Sun, 25 Jan 2026 12:17:43 GMT</pubDate>
            <description><![CDATA[<h2 id="최소직사각형">최소직사각형</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>명함 지갑을 만드는 회사에서 지갑의 크기를 정하려고 합니다. 다양한 모양과 크기의 명함들을 모두 수납할 수 있으면서, 작아서 들고 다니기 편한 지갑을 만들어야 합니다. 이러한 요건을 만족하는 지갑을 만들기 위해 디자인팀은 모든 명함의 가로 길이와 세로 길이를 조사했습니다.</p>
<p>아래 표는 4가지 명함의 가로 길이와 세로 길이를 나타냅니다.
<img src="https://velog.velcdn.com/images/back_end_sc/post/0af7ad41-8e8e-4194-b5a7-89444dfb7781/image.png" alt=""></p>
<p>가장 긴 가로 길이와 세로 길이가 각각 80, 70이기 때문에 80(가로) x 70(세로) 크기의 지갑을 만들면 모든 명함들을 수납할 수 있습니다. 하지만 2번 명함을 가로로 눕혀 수납한다면 80(가로) x 50(세로) 크기의 지갑으로 모든 명함들을 수납할 수 있습니다. 이때의 지갑 크기는 4000(=80 x 50)입니다.</p>
<p>모든 명함의 가로 길이와 세로 길이를 나타내는 2차원 배열 sizes가 매개변수로 주어집니다. 모든 명함을 수납할 수 있는 가장 작은 지갑을 만들 때, 지갑의 크기를 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="제한-사항">제한 사항</h3>
<ul>
<li>sizes의 길이는 1 이상 10,000 이하입니다.<ul>
<li>sizes의 원소는 [w, h] 형식입니다.</li>
<li>w는 명함의 가로 길이를 나타냅니다.</li>
<li>h는 명함의 세로 길이를 나타냅니다.</li>
<li>w와 h는 1 이상 1,000 이하인 자연수입니다.</li>
</ul>
</li>
</ul>
<h3 id="입출력-예">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/back_end_sc/post/e5ccdd4f-c1cd-479d-8faa-074e310bb19b/image.png" alt=""></p>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<p>입출력 예 #1</p>
<ul>
<li>문제 예시와 같습니다.</li>
</ul>
<p>입출력 예 #2</p>
<ul>
<li>명함들을 적절히 회전시켜 겹쳤을 때, 3번째 명함(가로: 8, 세로: 15)이 다른 모든 명함보다 크기가 큽니다. 따라서 지갑의 크기는 3번째 명함의 크기와 같으며, 120(=8 x 15)을 return 합니다.</li>
</ul>
<p>입출력 예 #3</p>
<ul>
<li>명함들을 적절히 회전시켜 겹쳤을 때, 모든 명함을 포함하는 가장 작은 지갑의 크기는 133(=19 x 7)입니다.</li>
</ul>
<h3 id="제출-코드">제출 코드</h3>
<pre><code>function solution(sizes) {
  let max_width = 0;  // 긴 변들 중 최대
  let max_height = 0; // 짧은 변들 중 최대

  for (let i = 0; i &lt; sizes.length; i++) {
    const width = sizes[i][0];
    const height = sizes[i][1];

    const big = Math.max(width, height);
    const small = Math.min(width, height);

    if (big &gt; max_width) max_width = big;
    if (small &gt; max_height) max_height = small;
  }

  return max_width * max_height;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 - 181889]]></title>
            <link>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-181889-hh6652vy</link>
            <guid>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-181889-hh6652vy</guid>
            <pubDate>Sun, 25 Jan 2026 12:14:02 GMT</pubDate>
            <description><![CDATA[<h2 id="햄버거-만들기">햄버거 만들기</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>햄버거 가게에서 일을 하는 상수는 햄버거를 포장하는 일을 합니다. 함께 일을 하는 다른 직원들이 햄버거에 들어갈 재료를 조리해 주면 조리된 순서대로 상수의 앞에 아래서부터 위로 쌓이게 되고, 상수는 순서에 맞게 쌓여서 완성된 햄버거를 따로 옮겨 포장을 하게 됩니다. 상수가 일하는 가게는 정해진 순서(아래서부터, 빵 – 야채 – 고기 - 빵)로 쌓인 햄버거만 포장을 합니다. 상수는 손이 굉장히 빠르기 때문에 상수가 포장하는 동안 속 재료가 추가적으로 들어오는 일은 없으며, 재료의 높이는 무시하여 재료가 높이 쌓여서 일이 힘들어지는 경우는 없습니다.</p>
<p>예를 들어, 상수의 앞에 쌓이는 재료의 순서가 [야채, 빵, 빵, 야채, 고기, 빵, 야채, 고기, 빵]일 때, 상수는 여섯 번째 재료가 쌓였을 때, 세 번째 재료부터 여섯 번째 재료를 이용하여 햄버거를 포장하고, 아홉 번째 재료가 쌓였을 때, 두 번째 재료와 일곱 번째 재료부터 아홉 번째 재료를 이용하여 햄버거를 포장합니다. 즉, 2개의 햄버거를 포장하게 됩니다.</p>
<p>상수에게 전해지는 재료의 정보를 나타내는 정수 배열 ingredient가 주어졌을 때, 상수가 포장하는 햄버거의 개수를 return 하도록 solution 함수를 완성하시오.</p>
<h3 id="제한-사항">제한 사항</h3>
<ul>
<li>1 ≤ ingredient의 길이 ≤ 1,000,000</li>
<li>ingredient의 원소는 1, 2, 3 중 하나의 값이며, 순서대로 빵, 야채, 고기를 의미합니다.</li>
</ul>
<h3 id="입출력-예">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/back_end_sc/post/6f54e0b2-9427-442f-ae86-f84440145d01/image.png" alt=""></p>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<p>입출력 예 #1</p>
<ul>
<li>문제 예시와 같습니다.</li>
</ul>
<p>입출력 예 #2</p>
<ul>
<li>상수가 포장할 수 있는 햄버거가 없습니다.</li>
</ul>
<h3 id="제출-코드">제출 코드</h3>
<pre><code>function solution(ingredient) {
    var answer = 0;
    // 쌓이는 순서대로 1,2,3,1 이렇게 쌓여야됨
    // 본문 for문에서 1,2,3,1 이렇게 될 경우에 빼기
    // 4개 이상 쌓였을 경우 확인
    let top = 0;
    let bugger_array = [];
    let count = 0;
    for(let i = 0; i&lt;ingredient.length; i++){
        bugger_array[top] = ingredient[i];
        top++;
        if (top &gt;= 4 &amp;&amp; bugger_array[top - 4] === 1 &amp;&amp; bugger_array[top - 3] === 2 &amp;&amp; bugger_array[top - 2] === 3 &amp;&amp; bugger_array[top - 1] === 1) {
          top -= 4;    
          count++;
        }
    }
    return count;
}</code></pre><h3 id="스택-구조">스택 구조</h3>
<pre><code>function solution(ingredient) {
  const bugger_array = [];
  let count = 0;

  for (const x of ingredient) {
    bugger_array.push(x);

    const n = bugger_array.length;
    if (
      n &gt;= 4 &amp;&amp;
      bugger_array[n - 4] === 1 &amp;&amp;
      bugger_array[n - 3] === 2 &amp;&amp;
      bugger_array[n - 2] === 3 &amp;&amp;
      bugger_array[n - 1] === 1
    ) {
      bugger_array.pop();
      bugger_array.pop();
      bugger_array.pop();
      bugger_array.pop();
      count++;
    }
  }

  return count;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 - 134240]]></title>
            <link>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-134240</link>
            <guid>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-134240</guid>
            <pubDate>Wed, 14 Jan 2026 12:03:47 GMT</pubDate>
            <description><![CDATA[<h2 id="푸드-파이트-대회">푸드 파이트 대회</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>수웅이는 매달 주어진 음식을 빨리 먹는 푸드 파이트 대회를 개최합니다. 이 대회에서 선수들은 1대 1로 대결하며, 매 대결마다 음식의 종류와 양이 바뀝니다. 대결은 준비된 음식들을 일렬로 배치한 뒤, 한 선수는 제일 왼쪽에 있는 음식부터 오른쪽으로, 다른 선수는 제일 오른쪽에 있는 음식부터 왼쪽으로 순서대로 먹는 방식으로 진행됩니다. 중앙에는 물을 배치하고, 물을 먼저 먹는 선수가 승리하게 됩니다.</p>
<p>이때, 대회의 공정성을 위해 두 선수가 먹는 음식의 종류와 양이 같아야 하며, 음식을 먹는 순서도 같아야 합니다. 또한, 이번 대회부터는 칼로리가 낮은 음식을 먼저 먹을 수 있게 배치하여 선수들이 음식을 더 잘 먹을 수 있게 하려고 합니다. 이번 대회를 위해 수웅이는 음식을 주문했는데, 대회의 조건을 고려하지 않고 음식을 주문하여 몇 개의 음식은 대회에 사용하지 못하게 되었습니다.</p>
<p>예를 들어, 3가지의 음식이 준비되어 있으며, 칼로리가 적은 순서대로 1번 음식을 3개, 2번 음식을 4개, 3번 음식을 6개 준비했으며, 물을 편의상 0번 음식이라고 칭한다면, 두 선수는 1번 음식 1개, 2번 음식 2개, 3번 음식 3개씩을 먹게 되므로 음식의 배치는 &quot;1223330333221&quot;이 됩니다. 따라서 1번 음식 1개는 대회에 사용하지 못합니다.</p>
<p>수웅이가 준비한 음식의 양을 칼로리가 적은 순서대로 나타내는 정수 배열 food가 주어졌을 때, 대회를 위한 음식의 배치를 나타내는 문자열을 return 하는 solution 함수를 완성해주세요</p>
<h3 id="제한-사항">제한 사항</h3>
<ul>
<li>2 ≤ food의 길이 ≤ 9</li>
<li>1 ≤ food의 각 원소 ≤ 1,000</li>
<li>food에는 칼로리가 적은 순서대로 음식의 양이 담겨 있습니다.</li>
<li>food[i]는 i번 음식의 수입니다.</li>
<li>food[0]은 수웅이가 준비한 물의 양이며, 항상 1입니다.</li>
<li>정답의 길이가 3 이상인 경우만 입력으로 주어집니다.</li>
</ul>
<h3 id="입출력-예">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/back_end_sc/post/6839f729-0e59-4e91-9947-9070a7347220/image.png" alt=""></p>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<p>입출력 예 #1</p>
<ul>
<li>문제 예시와 같습니다.</li>
</ul>
<p>입출력 예 #2</p>
<ul>
<li>두 선수는 1번 음식 3개, 3번 음식 1개를 먹게 되므로 음식의 배치는 &quot;111303111&quot;입니다.</li>
</ul>
<h3 id="제출-코드">제출 코드</h3>
<pre><code>function solution(food) {
    let left_array = &#39;&#39;;

    for (let i = 1; i &lt; food.length; i++) {
        left_array += String(i).repeat(Math.floor(food[i] / 2));
    }

    const right_array = left_array.split(&#39;&#39;).reverse().join(&#39;&#39;);
    return left_array + &#39;0&#39; + right_array;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 - 135808]]></title>
            <link>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-135808</link>
            <guid>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-135808</guid>
            <pubDate>Wed, 14 Jan 2026 11:39:22 GMT</pubDate>
            <description><![CDATA[<h2 id="과일-장수">과일 장수</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>과일 장수가 사과 상자를 포장하고 있습니다. 사과는 상태에 따라 1점부터 k점까지의 점수로 분류하며, k점이 최상품의 사과이고 1점이 최하품의 사과입니다. 사과 한 상자의 가격은 다음과 같이 결정됩니다.</p>
<ul>
<li>한 상자에 사과를 m개씩 담아 포장합니다.</li>
<li>상자에 담긴 사과 중 가장 낮은 점수가 p (1 ≤ p ≤ k)점인 경우, 사과 한 상자의 가격은 p * m 입니다.</li>
</ul>
<p>과일 장수가 가능한 많은 사과를 팔았을 때, 얻을 수 있는 최대 이익을 계산하고자 합니다.(사과는 상자 단위로만 판매하며, 남는 사과는 버립니다)</p>
<p>예를 들어, k = 3, m = 4, 사과 7개의 점수가 [1, 2, 3, 1, 2, 3, 1]이라면, 다음과 같이 [2, 3, 2, 3]으로 구성된 사과 상자 1개를 만들어 판매하여 최대 이익을 얻을 수 있습니다.</p>
<ul>
<li>(최저 사과 점수) x (한 상자에 담긴 사과 개수) x (상자의 개수) = 2 x 4 x 1 = 8</li>
</ul>
<p>사과의 최대 점수 k, 한 상자에 들어가는 사과의 수 m, 사과들의 점수 score가 주어졌을 때, 과일 장수가 얻을 수 있는 최대 이익을 return하는 solution 함수를 완성해주세요.</p>
<h3 id="제한-사항">제한 사항</h3>
<ul>
<li>3 ≤ k ≤ 9</li>
<li>3 ≤ m ≤ 10</li>
<li>7 ≤ score의 길이 ≤ 1,000,000<ul>
<li>1 ≤ score[i] ≤ k</li>
</ul>
</li>
<li>이익이 발생하지 않는 경우에는 0을 return 해주세요.</li>
</ul>
<h3 id="입출력-예">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/back_end_sc/post/5128367e-f39e-420d-ba47-268cf46a3d91/image.png" alt=""></p>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<p>입출력 예 #1</p>
<ul>
<li>문제의 예시와 같습니다.</li>
</ul>
<p>입출력 예 #2</p>
<ul>
<li>다음과 같이 사과 상자를 포장하여 모두 팔면 최대 이익을 낼 수 있습니다.
<img src="https://velog.velcdn.com/images/back_end_sc/post/6d61fa40-bdcf-4ac0-b739-b10321eb8bd3/image.png" alt="">
따라서 (1 x 3 x 1) + (2 x 3 x 1) + (4 x 3 x 2) = 33을 return합니다.</li>
</ul>
<h3 id="제출-코드">제출 코드</h3>
<pre><code>function solution(k, m, score) {
    let answer = 0;

    // 내림차순 정렬
    score.sort((a, b) =&gt; b - a);

    // m개씩 묶기
    for (let i = 0; i + m &lt;= score.length; i += m) {
        // 묶음의 최저 점수는 i + m - 1
        answer += score[i + m - 1] * m;
    }

    return answer;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 - 150370]]></title>
            <link>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-150370</link>
            <guid>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-150370</guid>
            <pubDate>Tue, 13 Jan 2026 13:15:55 GMT</pubDate>
            <description><![CDATA[<h2 id="개인정보-수집-유효기간">개인정보 수집 유효기간</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>고객의 약관 동의를 얻어서 수집된 1~n번으로 분류되는 개인정보 n개가 있습니다. 약관 종류는 여러 가지 있으며 각 약관마다 개인정보 보관 유효기간이 정해져 있습니다. 당신은 각 개인정보가 어떤 약관으로 수집됐는지 알고 있습니다. 수집된 개인정보는 유효기간 전까지만 보관 가능하며, 유효기간이 지났다면 반드시 파기해야 합니다.</p>
<p>예를 들어, A라는 약관의 유효기간이 12 달이고, 2021년 1월 5일에 수집된 개인정보가 A약관으로 수집되었다면 해당 개인정보는 2022년 1월 4일까지 보관 가능하며 2022년 1월 5일부터 파기해야 할 개인정보입니다.
당신은 오늘 날짜로 파기해야 할 개인정보 번호들을 구하려 합니다.</p>
<p>모든 달은 28일까지 있다고 가정합니다.</p>
<p>다음은 오늘 날짜가 2022.05.19일 때의 예시입니다.
<img src="https://velog.velcdn.com/images/back_end_sc/post/430494d7-2476-4368-a00d-39f243c4def2/image.png" alt="">
<img src="https://velog.velcdn.com/images/back_end_sc/post/7c104e37-f8d6-4a60-adac-916cda638891/image.png" alt=""></p>
<ul>
<li>첫 번째 개인정보는 A약관에 의해 2021년 11월 1일까지 보관 가능하며, 유효기간이 지났으므로 파기해야 할 개인정보입니다.</li>
<li>두 번째 개인정보는 B약관에 의해 2022년 6월 28일까지 보관 가능하며, 유효기간이 지나지 않았으므로 아직 보관 가능합니다.</li>
<li>세 번째 개인정보는 C약관에 의해 2022년 5월 18일까지 보관 가능하며, 유효기간이 지났으므로 파기해야 할 개인정보입니다.</li>
<li>네 번째 개인정보는 C약관에 의해 2022년 5월 19일까지 보관 가능하며, 유효기간이 지나지 않았으므로 아직 보관 가능합니다.</li>
</ul>
<p>따라서 파기해야 할 개인정보 번호는 [1, 3]입니다.</p>
<p>오늘 날짜를 의미하는 문자열 today, 약관의 유효기간을 담은 1차원 문자열 배열 terms와 수집된 개인정보의 정보를 담은 1차원 문자열 배열 privacies가 매개변수로 주어집니다. 이때 파기해야 할 개인정보의 번호를 오름차순으로 1차원 정수 배열에 담아 return 하도록 solution 함수를 완성해 주세요.</p>
<h3 id="제한-사항">제한 사항</h3>
<ul>
<li>today는 &quot;YYYY.MM.DD&quot; 형태로 오늘 날짜를 나타냅니다.</li>
<li>1 ≤ terms의 길이 ≤ 20<ul>
<li>terms의 원소는 &quot;약관 종류 유효기간&quot; 형태의 약관 종류와 유효기간을 공백 하나로 구분한 문자열입니다.</li>
<li>약관 종류는 A~Z중 알파벳 대문자 하나이며, terms 배열에서 약관 종류는 중복되지 않습니다.</li>
<li>유효기간은 개인정보를 보관할 수 있는 달 수를 나타내는 정수이며, 1 이상 100 이하입니다.</li>
</ul>
</li>
<li>1 ≤ privacies의 길이 ≤ 100<ul>
<li>privacies[i]는 i+1번 개인정보의 수집 일자와 약관 종류를 나타냅니다.</li>
<li>privacies의 원소는 &quot;날짜 약관 종류&quot; 형태의 날짜와 약관 종류를 공백 하나로 구분한 문자열입니다.</li>
<li>날짜는 &quot;YYYY.MM.DD&quot; 형태의 개인정보가 수집된 날짜를 나타내며, today 이전의 날짜만 주어집니다.</li>
<li>privacies의 약관 종류는 항상 terms에 나타난 약관 종류만 주어집니다.</li>
</ul>
</li>
<li>today와 privacies에 등장하는 날짜의 YYYY는 연도, MM은 월, DD는 일을 나타내며 점(.) 하나로 구분되어 있습니다.<ul>
<li>2000 ≤ YYYY ≤ 2022</li>
<li>1 ≤ MM ≤ 12</li>
<li>MM이 한 자릿수인 경우 앞에 0이 붙습니다.</li>
<li>1 ≤ DD ≤ 28</li>
<li>DD가 한 자릿수인 경우 앞에 0이 붙습니다.</li>
<li>파기해야 할 개인정보가 하나 이상 존재하는 입력만 주어집니다</li>
</ul>
</li>
</ul>
<h3 id="입출력-예">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/back_end_sc/post/7f43c92f-c3c1-471d-8201-5b1728ddeb19/image.png" alt=""></p>
<h3 id="입출력-예-1">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/back_end_sc/post/08676e14-b193-42aa-9fd2-64dfff4c4c72/image.png" alt=""></p>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<p>입출력 예 #1
문제 예시와 같습니다.</p>
<p>입출력 예 #2
<img src="https://velog.velcdn.com/images/back_end_sc/post/e8e39b45-624d-45af-9b8d-f559f5b3426f/image.png" alt="">
<img src="https://velog.velcdn.com/images/back_end_sc/post/53c61f70-8980-4be8-8e07-162bf9c3a9da/image.png" alt="">
오늘 날짜는 2020년 1월 1일입니다.</p>
<ul>
<li>첫 번째 개인정보는 D약관에 의해 2019년 5월 28일까지 보관 가능하며, 유효기간이 지났으므로 파기해야 할 개인정보입니다.</li>
<li>두 번째 개인정보는 Z약관에 의해 2020년 2월 14일까지 보관 가능하며, 유효기간이 지나지 않았으므로 아직 보관 가능합니다.</li>
<li>세 번째 개인정보는 D약관에 의해 2020년 1월 1일까지 보관 가능하며, 유효기간이 지나지 않았으므로 아직 보관 가능합니다.</li>
<li>네 번째 개인정보는 D약관에 의해 2019년 11월 28일까지 보관 가능하며, 유효기간이 지났으므로 파기해야 할 개인정보입니다.</li>
<li>다섯 번째 개인정보는 Z약관에 의해 2019년 3월 27일까지 보관 가능하며, 유효기간이 지났으므로 파기해야 할 개인정보입니다.</li>
</ul>
<h3 id="제출-코드">제출 코드</h3>
<pre><code>function solution(today, terms, privacies) {
    var answer = [];
    let [current_year, current_month, current_day] = today.split(&quot;.&quot;).map(Number);

    let terms_obj = {};
    terms.forEach((value) =&gt; {
      terms_obj[value.split(&quot; &quot;)[0]] = Number(value.split(&quot; &quot;)[1]);
    });

    for (let i = 0; i &lt; privacies.length; i++) {
        let [pri_year, pri_month, pri_day] =
            privacies[i].split(&quot; &quot;)[0].split(&quot;.&quot;).map(Number);
        let pri_terms = privacies[i].split(&quot; &quot;)[1];

        // 약관 개월 수 더하기
        pri_month += terms_obj[pri_terms];

        // 년/월 보정
        if (pri_month &gt; 12) {
            pri_year += Math.floor((pri_month - 1) / 12);
            pri_month = ((pri_month - 1) % 12) + 1;
        }

        // 현재 날짜랑 계산된 날짜 비교
        let expired = pri_year &lt; current_year ||
            (pri_year === current_year &amp;&amp; pri_month &lt; current_month) ||
            (pri_year === current_year &amp;&amp;
             pri_month === current_month &amp;&amp;
             pri_day &lt;= current_day);

        if (expired) {
            answer.push(i + 1); // 문제 조건: 1번부터 시작
        }
    }

    return answer;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 - 140108]]></title>
            <link>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-140108</link>
            <guid>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-140108</guid>
            <pubDate>Tue, 13 Jan 2026 13:10:07 GMT</pubDate>
            <description><![CDATA[<h2 id="문자열-나누기">문자열 나누기</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>문자열 s가 입력되었을 때 다음 규칙을 따라서 이 문자열을 여러 문자열로 분해하려고 합니다.</p>
<ul>
<li>먼저 첫 글자를 읽습니다. 이 글자를 x라고 합시다.</li>
<li>이제 이 문자열을 왼쪽에서 오른쪽으로 읽어나가면서, x와 x가 아닌 다른 글자들이 나온 횟수를 각각 셉니다. 처음으로 두 횟수가 같아지는 순간 멈추고, 지금까지 읽은 문자열을 분리합니다.</li>
<li>s에서 분리한 문자열을 빼고 남은 부분에 대해서 이 과정을 반복합니다. 남은 부분이 없다면 종료합니다.</li>
<li>만약 두 횟수가 다른 상태에서 더 이상 읽을 글자가 없다면, 역시 지금까지 읽은 문자열을 분리하고, 종료합니다.</li>
</ul>
<p>문자열 s가 매개변수로 주어질 때, 위 과정과 같이 문자열들로 분해하고, 분해한 문자열의 개수를 return 하는 함수 solution을 완성하세요.</p>
<h3 id="제한-사항">제한 사항</h3>
<ul>
<li>1 ≤ s의 길이 ≤ 10,000</li>
<li>s는 영어 소문자로만 이루어져 있습니다.</li>
</ul>
<h3 id="입출력-예">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/back_end_sc/post/391c56e8-c604-4a43-8ae6-96788d15e6d1/image.png" alt=""></p>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<p>입출력 예 #1</p>
<ul>
<li>s=&quot;banana&quot;인 경우 ba - na - na와 같이 분해됩니다.</li>
</ul>
<p>입출력 예 #2</p>
<ul>
<li>s=&quot;abracadabra&quot;인 경우 ab - ra - ca - da - br - a와 같이 분해됩니다.</li>
</ul>
<p>입출력 예 #3</p>
<ul>
<li>s=&quot;aaabbaccccabba&quot;인 경우 aaabbacc - ccab - ba와 같이 분해됩니다.</li>
</ul>
<h3 id="제출-코드">제출 코드</h3>
<pre><code>function solution(s) {
    let answer = 0;
    let array = s.split(&quot;&quot;);

    let count = { x: 0, y: 0 };
    let target = array[0];

    for (let i = 0; i &lt; array.length; i++) {

        if (array[i] === target) {
            count.x++;
        } else {
            count.y++;
        }

        if (count.x === count.y) {
            answer++;
            // 다음 문자 기준으로 리셋
            target = array[i + 1];
            count.x = 0;
            count.y = 0;
        }
    }

    // 마지막 덩어리 처리
    if (count.x !== 0) {
        answer++;
    }

    return answer;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 - 138477]]></title>
            <link>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-138477</link>
            <guid>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-138477</guid>
            <pubDate>Tue, 13 Jan 2026 13:06:03 GMT</pubDate>
            <description><![CDATA[<h2 id="명예의-전당-1">명예의 전당 (1)</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>&quot;명예의 전당&quot;이라는 TV 프로그램에서는 매일 1명의 가수가 노래를 부르고, 시청자들의 문자 투표수로 가수에게 점수를 부여합니다. 매일 출연한 가수의 점수가 지금까지 출연 가수들의 점수 중 상위 k번째 이내이면 해당 가수의 점수를 명예의 전당이라는 목록에 올려 기념합니다. 즉 프로그램 시작 이후 초기에 k일까지는 모든 출연 가수의 점수가 명예의 전당에 오르게 됩니다. k일 다음부터는 출연 가수의 점수가 기존의 명예의 전당 목록의 k번째 순위의 가수 점수보다 더 높으면, 출연 가수의 점수가 명예의 전당에 오르게 되고 기존의 k번째 순위의 점수는 명예의 전당에서 내려오게 됩니다.</p>
<p>이 프로그램에서는 매일 &quot;명예의 전당&quot;의 최하위 점수를 발표합니다. 예를 들어, k = 3이고, 7일 동안 진행된 가수의 점수가 [10, 100, 20, 150, 1, 100, 200]이라면, 명예의 전당에서 발표된 점수는 아래의 그림과 같이 [10, 10, 10, 20, 20, 100, 100]입니다.
<img src="https://velog.velcdn.com/images/back_end_sc/post/4de9fcc8-0695-454b-99df-666078d0a613/image.png" alt="">
명예의 전당 목록의 점수의 개수 k, 1일부터 마지막 날까지 출연한 가수들의 점수인 score가 주어졌을 때, 매일 발표된 명예의 전당의 최하위 점수를 return하는 solution 함수를 완성해주세요.</p>
<h3 id="제한-사항">제한 사항</h3>
<ul>
<li>3 ≤ k ≤ 100</li>
<li>7 ≤ score의 길이 ≤ 1,000<ul>
<li>0 ≤ score[i] ≤ 2,000</li>
</ul>
</li>
</ul>
<h3 id="입출력-예">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/back_end_sc/post/ff08c8b2-922e-4d67-b8c0-f730faae6b7b/image.png" alt=""></p>
<h3 id="입출력-예-1">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/back_end_sc/post/74ea97a8-5123-4986-b312-4e4dd3fbc373/image.png" alt=""></p>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<ul>
<li>입출력 예 #1<ul>
<li>문제의 예시와 같습니다.</li>
</ul>
</li>
<li>입출력 예 #2<ul>
<li>아래와 같이, [0, 0, 0, 0, 20, 40, 70, 70, 150, 300]을 return합니다.
<img src="https://velog.velcdn.com/images/back_end_sc/post/5bb05667-d9cc-46be-af31-fd059f7c91da/image.png" alt=""></li>
</ul>
</li>
</ul>
<h3 id="제출-코드">제출 코드</h3>
<pre><code>function solution(k, score) {
    const hall = [];   // 명예의 전당
    const result = [];

    for (let i = 0; i &lt; score.length; i++) {
        hall.push(score[i]);          // 오늘 점수 추가
        hall.sort((a, b) =&gt; a - b);   // 오름차순 정렬

        // k명 초과하면 최하위 제거
        if (hall.length &gt; k) {
            hall.shift();
        }

        // 현재 명예의 전당 최하위 점수 기록
        result.push(hall[0]);
    }

    return result;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 - 161989]]></title>
            <link>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-161989</link>
            <guid>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-161989</guid>
            <pubDate>Sun, 04 Jan 2026 07:35:47 GMT</pubDate>
            <description><![CDATA[<h2 id="n번재-원소까지">n번재 원소까지</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>어느 학교에 페인트가 칠해진 길이가 n미터인 벽이 있습니다. 벽에 동아리 · 학회 홍보나 회사 채용 공고 포스터 등을 게시하기 위해 테이프로 붙였다가 철거할 때 떼는 일이 많고 그 과정에서 페인트가 벗겨지곤 합니다. 페인트가 벗겨진 벽이 보기 흉해져 학교는 벽에 페인트를 덧칠하기로 했습니다.</p>
<p>넓은 벽 전체에 페인트를 새로 칠하는 대신, 구역을 나누어 일부만 페인트를 새로 칠 함으로써 예산을 아끼려 합니다. 이를 위해 벽을 1미터 길이의 구역 n개로 나누고, 각 구역에 왼쪽부터 순서대로 1번부터 n번까지 번호를 붙였습니다. 그리고 페인트를 다시 칠해야 할 구역들을 정했습니다.</p>
<p>벽에 페인트를 칠하는 롤러의 길이는 m미터이고, 롤러로 벽에 페인트를 한 번 칠하는 규칙은 다음과 같습니다.</p>
<p>롤러가 벽에서 벗어나면 안 됩니다.
구역의 일부분만 포함되도록 칠하면 안 됩니다.
즉, 롤러의 좌우측 끝을 구역의 경계선 혹은 벽의 좌우측 끝부분에 맞춘 후 롤러를 위아래로 움직이면서 벽을 칠합니다. 현재 페인트를 칠하는 구역들을 완전히 칠한 후 벽에서 롤러를 떼며, 이를 벽을 한 번 칠했다고 정의합니다.</p>
<p>한 구역에 페인트를 여러 번 칠해도 되고 다시 칠해야 할 구역이 아닌 곳에 페인트를 칠해도 되지만 다시 칠하기로 정한 구역은 적어도 한 번 페인트칠을 해야 합니다. 예산을 아끼기 위해 다시 칠할 구역을 정했듯 마찬가지로 롤러로 페인트칠을 하는 횟수를 최소화하려고 합니다.</p>
<p>정수 n, m과 다시 페인트를 칠하기로 정한 구역들의 번호가 담긴 정수 배열 section이 매개변수로 주어질 때 롤러로 페인트칠해야 하는 최소 횟수를 return 하는 solution 함수를 작성해 주세요.</p>
<h3 id="제한-사항">제한 사항</h3>
<ul>
<li>1 ≤ m ≤ n ≤ 100,000</li>
<li>1 ≤ section의 길이 ≤ n<ul>
<li>1 ≤ section의 원소 ≤ n</li>
<li>section의 원소는 페인트를 다시 칠해야 하는 구역의 번호입니다.</li>
<li>section에서 같은 원소가 두 번 이상 나타나지 않습니다.</li>
<li>section의 원소는 오름차순으로 정렬되어 있습니다.</li>
</ul>
</li>
</ul>
<h3 id="입출력-예">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/back_end_sc/post/0479932b-8cb6-4c61-b2ec-57b1d4d59f56/image.png" alt=""></p>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<p>입출력 예 #1
예제 1번은 2, 3, 6번 영역에 페인트를 다시 칠해야 합니다. 롤러의 길이가 4미터이므로 한 번의 페인트칠에 연속된 4개의 구역을 칠할 수 있습니다. 처음에 3, 4, 5, 6번 영역에 페인트칠을 하면 칠해야 할 곳으로 2번 구역만 남고 1, 2, 3, 4번 구역에 페인트칠을 하면 2번 만에 다시 칠해야 할 곳에 모두 페인트칠을 할 수 있습니다.
<img src="https://velog.velcdn.com/images/back_end_sc/post/66cb4f61-4a67-4fa4-9ae9-ec4100a9114e/image.png" alt="">
2번보다 적은 횟수로 2, 3, 6번 영역에 페인트를 덧칠하는 방법은 없습니다. 따라서 최소 횟수인 2를 return 합니다.</p>
<p>입출력 예 #2
예제 2번은 1, 3번 영역에 페인트를 다시 칠해야 합니다. 롤러의 길이가 4미터이므로 한 번의 페인트칠에 연속된 4개의 구역을 칠할 수 있고 1, 2, 3, 4번 영역에 페인트칠을 하면 한 번에 1, 3번 영역을 모두 칠할 수 있습니다.
<img src="https://velog.velcdn.com/images/back_end_sc/post/68bfc0ea-6f2e-4ac2-961b-4cd31295feb4/image.png" alt="">
따라서 최소 횟수인 1을 return 합니다.</p>
<p>입출력 예 #3
예제 3번은 모든 구역에 페인트칠을 해야 합니다. 롤러의 길이가 1미터이므로 한 번에 한 구역밖에 칠할 수 없습니다. 구역이 4개이므로 각 구역을 한 번씩만 칠하는 4번이 최소 횟수가 됩니다.
<img src="https://velog.velcdn.com/images/back_end_sc/post/60cfde48-ae5b-4be3-afcc-29aff588f683/image.png" alt="">
따라서 4를 return 합니다.</p>
<h3 id="제출-코드">제출 코드</h3>
<pre><code>function solution(n, m, section) {
    var answer = 0;
    let value = 0;

    for (let i = 0; i &lt; section.length; i++) {
        if (section[i] &gt; value) {
            answer++;
            value = section[i] + m - 1;
        }
    }
    return answer;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 - 160586]]></title>
            <link>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-160586</link>
            <guid>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-160586</guid>
            <pubDate>Sun, 04 Jan 2026 07:31:43 GMT</pubDate>
            <description><![CDATA[<h2 id="대충-만든-자판">대충 만든 자판</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>휴대폰의 자판은 컴퓨터 키보드 자판과는 다르게 하나의 키에 여러 개의 문자가 할당될 수 있습니다. 키 하나에 여러 문자가 할당된 경우, 동일한 키를 연속해서 빠르게 누르면 할당된 순서대로 문자가 바뀝니다.</p>
<p>예를 들어, 1번 키에 &quot;A&quot;, &quot;B&quot;, &quot;C&quot; 순서대로 문자가 할당되어 있다면 1번 키를 한 번 누르면 &quot;A&quot;, 두 번 누르면 &quot;B&quot;, 세 번 누르면 &quot;C&quot;가 되는 식입니다.</p>
<p>같은 규칙을 적용해 아무렇게나 만든 휴대폰 자판이 있습니다. 이 휴대폰 자판은 키의 개수가 1개부터 최대 100개까지 있을 수 있으며, 특정 키를 눌렀을 때 입력되는 문자들도 무작위로 배열되어 있습니다. 또, 같은 문자가 자판 전체에 여러 번 할당된 경우도 있고, 키 하나에 같은 문자가 여러 번 할당된 경우도 있습니다. 심지어 아예 할당되지 않은 경우도 있습니다. 따라서 몇몇 문자열은 작성할 수 없을 수도 있습니다.</p>
<p>이 휴대폰 자판을 이용해 특정 문자열을 작성할 때, 키를 최소 몇 번 눌러야 그 문자열을 작성할 수 있는지 알아보고자 합니다.</p>
<p>1번 키부터 차례대로 할당된 문자들이 순서대로 담긴 문자열배열 keymap과 입력하려는 문자열들이 담긴 문자열 배열 targets가 주어질 때, 각 문자열을 작성하기 위해 키를 최소 몇 번씩 눌러야 하는지 순서대로 배열에 담아 return 하는 solution 함수를 완성해 주세요.</p>
<p>단, 목표 문자열을 작성할 수 없을 때는 -1을 저장합니다.</p>
<h3 id="제한-사항">제한 사항</h3>
<ul>
<li>1 ≤ keymap의 길이 ≤ 100<ul>
<li>1 ≤ keymap의 원소의 길이 ≤ 100</li>
<li>keymap[i]는 i + 1번 키를 눌렀을 때 순서대로 바뀌는 문자를 의미합니다.<ul>
<li>예를 들어 keymap[0] = &quot;ABACD&quot; 인 경우 1번 키를 한 번 누르면 A, 두 번 누르면 B, 세 번 누르면 A 가 됩니다.</li>
</ul>
</li>
<li>keymap의 원소의 길이는 서로 다를 수 있습니다.</li>
<li>keymap의 원소는 알파벳 대문자로만 이루어져 있습니다.</li>
</ul>
</li>
<li>1 ≤ targets의 길이 ≤ 100<ul>
<li>1 ≤ targets의 원소의 길이 ≤ 100</li>
<li>targets의 원소는 알파벳 대문자로만 이루어져 있습니다.</li>
</ul>
</li>
</ul>
<h3 id="입출력-예">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/back_end_sc/post/9dd2b97a-6417-4bca-b435-93eb4b690956/image.png" alt=""></p>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<p>입출력 예 #1
&quot;ABCD&quot;의 경우,
1번 키 한 번 → A
2번 키 한 번 → B
2번 키 두 번 → C
1번 키 다섯 번 → D
따라서 총합인 9를 첫 번째 인덱스에 저장합니다.
&quot;AABB&quot;의 경우,
1번 키 한 번 → A
1번 키 한 번 → A
2번 키 한 번 → B
2번 키 한 번 → B
따라서 총합인 4를 두 번째 인덱스에 저장합니다.
결과적으로 [9,4]를 return 합니다.</p>
<p>입출력 예 #2
&quot;B&quot;의 경우, &#39;B&#39;가 어디에도 존재하지 않기 때문에 -1을 첫 번째 인덱스에 저장합니다.
결과적으로 [-1]을 return 합니다.</p>
<p>입출력 예 #3
&quot;ASA&quot;의 경우,
1번 키 한 번 → A
2번 키 두 번 → S
1번 키 한 번 → A
따라서 총합인 4를 첫 번째 인덱스에 저장합니다.
&quot;BGZ&quot;의 경우,
2번 키 한 번 → B
1번 키 두 번 → G
1번 키 세 번 → Z
따라서 총합인 6을 두 번째 인덱스에 저장합니다.
결과적으로 [4, 6]을 return 합니다.</p>
<h3 id="제출-코드">제출 코드</h3>
<pre><code>function solution(keymap, targets) {
  // 문자별 최소 누름 횟수
  const char_min_press = {};

  // keymap 순회
  for (let i = 0; i &lt; keymap.length; i++) {
    const key = keymap[i];

    for (let j = 0; j &lt; key.length; j++) {
      const char = key[j];
      const press_count = j + 1;

      if (char_min_press[char] === undefined) {
        char_min_press[char] = press_count;
      } else if (char_min_press[char] &gt; press_count) {
        char_min_press[char] = press_count;
      }
    }
  }

  // targets 계산
  const result = [];

  for (let i = 0; i &lt; targets.length; i++) {
    const target = targets[i];
    let total_press = 0;
    let is_possible = true;

    for (let j = 0; j &lt; target.length; j++) {
      const char = target[j];

      if (char_min_press[char] === undefined) {
        is_possible = false;
        break;
      }

      total_press += char_min_press[char];
    }

    result.push(is_possible ? total_press : -1);
  }

  return result;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 - 142086]]></title>
            <link>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-142086</link>
            <guid>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-142086</guid>
            <pubDate>Sun, 04 Jan 2026 07:27:51 GMT</pubDate>
            <description><![CDATA[<h2 id="가장-가까운-같은-글자">가장 가까운 같은 글자</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>문자열 s가 주어졌을 때, s의 각 위치마다 자신보다 앞에 나왔으면서, 자신과 가장 가까운 곳에 있는 같은 글자가 어디 있는지 알고 싶습니다.
예를 들어, s=&quot;banana&quot;라고 할 때,  각 글자들을 왼쪽부터 오른쪽으로 읽어 나가면서 다음과 같이 진행할 수 있습니다.</p>
<p>b는 처음 나왔기 때문에 자신의 앞에 같은 글자가 없습니다. 이는 -1로 표현합니다.
a는 처음 나왔기 때문에 자신의 앞에 같은 글자가 없습니다. 이는 -1로 표현합니다.
n은 처음 나왔기 때문에 자신의 앞에 같은 글자가 없습니다. 이는 -1로 표현합니다.
a는 자신보다 두 칸 앞에 a가 있습니다. 이는 2로 표현합니다.
n도 자신보다 두 칸 앞에 n이 있습니다. 이는 2로 표현합니다.
a는 자신보다 두 칸, 네 칸 앞에 a가 있습니다. 이 중 가까운 것은 두 칸 앞이고, 이는 2로 표현합니다.
따라서 최종 결과물은 [-1, -1, -1, 2, 2, 2]가 됩니다.</p>
<p>문자열 s이 주어질 때, 위와 같이 정의된 연산을 수행하는 함수 solution을 완성해주세요.</p>
<h3 id="제한-사항">제한 사항</h3>
<ul>
<li>1 ≤ s의 길이 ≤ 10,000<ul>
<li>s은 영어 소문자로만 이루어져 있습니다.</li>
</ul>
</li>
</ul>
<h3 id="입출력-예">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/back_end_sc/post/059a3889-add2-4ac0-9430-047b207c6abf/image.png" alt=""></p>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<p>입출력 예 #1
지문과 같습니다.</p>
<p>입출력 예 #2
설명 생략</p>
<h3 id="제출-코드">제출 코드</h3>
<pre><code>function solution(s) {
    const answer = [];
    const last_index_map = {}; // 문자별 마지막 등장 위치

    for (let i = 0; i &lt; s.length; i++) {
        const current_char = s[i];

        if (last_index_map[current_char] === undefined) {
            // 처음 등장
            answer.push(-1);
        } else {
            // 이전 위치와의 거리
            answer.push(i - last_index_map[current_char]);
        }

        // 현재 위치로 갱신
        last_index_map[current_char] = i;
    }

    return answer;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 - 155652]]></title>
            <link>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-155652</link>
            <guid>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-155652</guid>
            <pubDate>Sun, 04 Jan 2026 07:24:45 GMT</pubDate>
            <description><![CDATA[<h2 id="둘만의-암호">둘만의 암호</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>두 문자열 s와 skip, 그리고 자연수 index가 주어질 때, 다음 규칙에 따라 문자열을 만들려 합니다. 암호의 규칙은 다음과 같습니다.</p>
<p>문자열 s의 각 알파벳을 index만큼 뒤의 알파벳으로 바꿔줍니다.
index만큼의 뒤의 알파벳이 z를 넘어갈 경우 다시 a로 돌아갑니다.
skip에 있는 알파벳은 제외하고 건너뜁니다.
예를 들어 s = &quot;aukks&quot;, skip = &quot;wbqd&quot;, index = 5일 때, a에서 5만큼 뒤에 있는 알파벳은 f지만 [b, c, d, e, f]에서 &#39;b&#39;와 &#39;d&#39;는 skip에 포함되므로 세지 않습니다. 따라서 &#39;b&#39;, &#39;d&#39;를 제외하고 &#39;a&#39;에서 5만큼 뒤에 있는 알파벳은 [c, e, f, g, h] 순서에 의해 &#39;h&#39;가 됩니다. 나머지 &quot;ukks&quot; 또한 위 규칙대로 바꾸면 &quot;appy&quot;가 되며 결과는 &quot;happy&quot;가 됩니다.</p>
<p>두 문자열 s와 skip, 그리고 자연수 index가 매개변수로 주어질 때 위 규칙대로 s를 변환한 결과를 return하도록 solution 함수를 완성해주세요.</p>
<h3 id="제한-사항">제한 사항</h3>
<ul>
<li>5 ≤ s의 길이 ≤ 50</li>
<li>1 ≤ skip의 길이 ≤ 10</li>
<li>s와 skip은 알파벳 소문자로만 이루어져 있습니다.<ul>
<li>skip에 포함되는 알파벳은 s에 포함되지 않습니다.</li>
</ul>
</li>
<li>1 ≤ index ≤ 20</li>
</ul>
<h3 id="입출력-예">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/back_end_sc/post/45497a02-5412-4e12-9ebf-4c4aa728aafc/image.png" alt=""></p>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<p>입출력 예 #1
본문 내용과 일치합니다.</p>
<h3 id="제출-코드">제출 코드</h3>
<pre><code>function solution(s, skip, index) {
    var answer = &#39;&#39;;
    let pass = [];

    // skip → 아스키 코드
    for (let i = 0; i &lt; skip.length; i++) {
        pass.push(skip[i].charCodeAt());
    }

    // 문자열은 수정 불가 → 배열로 변환
    let arr = [];
    for (let i = 0; i &lt; s.length; i++) {
        arr.push(s[i].charCodeAt());
    }

    let i = 0;
    let cnt = 0;

    while (i &lt; arr.length) {
        arr[i]++;

        // z 초과 시 a로
        if (arr[i] &gt; 122) arr[i] = 97;

        // skip 문자면 다시 증가
        if (pass.includes(arr[i])) continue;

        cnt++;
        if (cnt === index) {
            cnt = 0;
            i++;
        }
    }

    // 여기서 아스키 → 문자 변환 후 answer에 추가
    for (let i = 0; i &lt; arr.length; i++) {
        answer += String.fromCharCode(arr[i]);
    }

    return answer;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 - 159994]]></title>
            <link>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-159994</link>
            <guid>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-159994</guid>
            <pubDate>Sun, 04 Jan 2026 07:21:52 GMT</pubDate>
            <description><![CDATA[<h2 id="카드뭉치">카드뭉치</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>코니는 영어 단어가 적힌 카드 뭉치 두 개를 선물로 받았습니다. 코니는 다음과 같은 규칙으로 카드에 적힌 단어들을 사용해 원하는 순서의 단어 배열을 만들 수 있는지 알고 싶습니다.</p>
<p>원하는 카드 뭉치에서 카드를 순서대로 한 장씩 사용합니다.
한 번 사용한 카드는 다시 사용할 수 없습니다.
카드를 사용하지 않고 다음 카드로 넘어갈 수 없습니다.
기존에 주어진 카드 뭉치의 단어 순서는 바꿀 수 없습니다.
예를 들어 첫 번째 카드 뭉치에 순서대로 [&quot;i&quot;, &quot;drink&quot;, &quot;water&quot;], 두 번째 카드 뭉치에 순서대로 [&quot;want&quot;, &quot;to&quot;]가 적혀있을 때 [&quot;i&quot;, &quot;want&quot;, &quot;to&quot;, &quot;drink&quot;, &quot;water&quot;] 순서의 단어 배열을 만들려고 한다면 첫 번째 카드 뭉치에서 &quot;i&quot;를 사용한 후 두 번째 카드 뭉치에서 &quot;want&quot;와 &quot;to&quot;를 사용하고 첫 번째 카드뭉치에 &quot;drink&quot;와 &quot;water&quot;를 차례대로 사용하면 원하는 순서의 단어 배열을 만들 수 있습니다.</p>
<p>문자열로 이루어진 배열 cards1, cards2와 원하는 단어 배열 goal이 매개변수로 주어질 때, cards1과 cards2에 적힌 단어들로 goal를 만들 있다면 &quot;Yes&quot;를, 만들 수 없다면 &quot;No&quot;를 return하는 solution 함수를 완성해주세요.</p>
<h3 id="제한-사항">제한 사항</h3>
<ul>
<li>1 ≤ cards1의 길이, cards2의 길이 ≤ 10<ul>
<li>1 ≤ cards1[i]의 길이, cards2[i]의 길이 ≤ 10</li>
<li>cards1과 cards2에는 서로 다른 단어만 존재합니다.</li>
</ul>
</li>
<li>2 ≤ goal의 길이 ≤ cards1의 길이 + cards2의 길이<ul>
<li>1 ≤ goal[i]의 길이 ≤ 10</li>
<li>goal의 원소는 cards1과 cards2의 원소들로만 이루어져 있습니다.</li>
</ul>
</li>
<li>cards1, cards2, goal의 문자열들은 모두 알파벳 소문자로만 이루어져 있습니다.</li>
</ul>
<h3 id="입출력-예">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/back_end_sc/post/123ee19c-8a36-45d3-b656-0771652e9d8f/image.png" alt=""></p>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<p>입출력 예 #1
본문과 같습니다.</p>
<p>입출력 예 #2
cards1에서 &quot;i&quot;를 사용하고 cards2에서 &quot;want&quot;와 &quot;to&quot;를 사용하여 &quot;i want to&quot;까지는 만들 수 있지만 &quot;water&quot;가 &quot;drink&quot;보다 먼저 사용되어야 하기 때문에 해당 문장을 완성시킬 수 없습니다. 따라서 &quot;No&quot;를 반환합니다.</p>
<h3 id="제출-코드">제출 코드</h3>
<pre><code>function solution(cards1, cards2, goal) {
  let i = 0; // cards1 포인터
  let j = 0; // cards2 포인터

  for (const word of goal) {
    if (i &lt; cards1.length &amp;&amp; cards1[i] === word) {
      i++;
    } else if (j &lt; cards2.length &amp;&amp; cards2[j] === word) {
      j++;
    } else {
      return &quot;No&quot;;
    }
  }

  return &quot;Yes&quot;;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 - 172928]]></title>
            <link>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-172928</link>
            <guid>https://velog.io/@back_end_sc/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-172928</guid>
            <pubDate>Tue, 23 Dec 2025 12:33:40 GMT</pubDate>
            <description><![CDATA[<h2 id="공원-산책">공원 산책</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>지나다니는 길을 &#39;O&#39;, 장애물을 &#39;X&#39;로 나타낸 직사각형 격자 모양의 공원에서 로봇 강아지가 산책을 하려합니다. 산책은 로봇 강아지에 미리 입력된 명령에 따라 진행하며, 명령은 다음과 같은 형식으로 주어집니다.</p>
<p>[&quot;방향 거리&quot;, &quot;방향 거리&quot; … ]
예를 들어 &quot;E 5&quot;는 로봇 강아지가 현재 위치에서 동쪽으로 5칸 이동했다는 의미입니다. 로봇 강아지는 명령을 수행하기 전에 다음 두 가지를 먼저 확인합니다.</p>
<p>주어진 방향으로 이동할 때 공원을 벗어나는지 확인합니다.
주어진 방향으로 이동 중 장애물을 만나는지 확인합니다.
위 두 가지중 어느 하나라도 해당된다면, 로봇 강아지는 해당 명령을 무시하고 다음 명령을 수행합니다.
공원의 가로 길이가 W, 세로 길이가 H라고 할 때, 공원의 좌측 상단의 좌표는 (0, 0), 우측 하단의 좌표는 (H - 1, W - 1) 입니다.
<img src="https://velog.velcdn.com/images/back_end_sc/post/d501034f-d280-4c80-ba05-9b2bf151d2c9/image.png" alt="">
공원을 나타내는 문자열 배열 park, 로봇 강아지가 수행할 명령이 담긴 문자열 배열 routes가 매개변수로 주어질 때, 로봇 강아지가 모든 명령을 수행 후 놓인 위치를 [세로 방향 좌표, 가로 방향 좌표] 순으로 배열에 담아 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="제한-사항">제한 사항</h3>
<ul>
<li>3 ≤ park의 길이 ≤ 50<ul>
<li>3 ≤ park[i]의 길이 ≤ 50<ul>
<li>park[i]는 다음 문자들로 이루어져 있으며 시작지점은 하나만 주어집니다.<ul>
<li>S : 시작 지점</li>
<li>O : 이동 가능한 통로</li>
<li>X : 장애물</li>
</ul>
</li>
<li>park는 직사각형 모양입니다.</li>
</ul>
</li>
</ul>
</li>
<li>1 ≤ routes의 길이 ≤ 50<ul>
<li>routes의 각 원소는 로봇 강아지가 수행할 명령어를 나타냅니다.</li>
<li>로봇 강아지는 routes의 첫 번째 원소부터 순서대로 명령을 수행합니다.</li>
<li>routes의 원소는 &quot;op n&quot;과 같은 구조로 이루어져 있으며, op는 이동할 방향, n은 이동할 칸의 수를 의미합니다.<ul>
<li>op는 다음 네 가지중 하나로 이루어져 있습니다.<ul>
<li>N : 북쪽으로 주어진 칸만큼 이동합니다.</li>
<li>S : 남쪽으로 주어진 칸만큼 이동합니다.</li>
<li>W : 서쪽으로 주어진 칸만큼 이동합니다.</li>
<li>E : 동쪽으로 주어진 칸만큼 이동합니다.</li>
</ul>
</li>
<li>1 ≤ n ≤ 9</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="입출력-예">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/back_end_sc/post/c13414d3-d094-444b-a2cc-02d2596182cb/image.png" alt=""></p>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<p>입출력 예 #1
입력된 명령대로 동쪽으로 2칸, 남쪽으로 2칸, 서쪽으로 1칸 이동하면 [0,0] -&gt; [0,2] -&gt; [2,2] -&gt; [2,1]이 됩니다.</p>
<p>입출력 예 #2
입력된 명령대로라면 동쪽으로 2칸, 남쪽으로 2칸, 서쪽으로 1칸 이동해야하지만 남쪽으로 2칸 이동할 때 장애물이 있는 칸을 지나기 때문에 해당 명령을 제외한 명령들만 따릅니다. 결과적으로는 [0,0] -&gt; [0,2] -&gt; [0,1]이 됩니다.</p>
<p>입출력 예 #3
처음 입력된 명령은 공원을 나가게 되고 두 번째로 입력된 명령 또한 장애물을 지나가게 되므로 두 입력은 제외한 세 번째 명령만 따르므로 결과는 다음과 같습니다. [0,1] -&gt; [0,0]</p>
<h3 id="제출-코드">제출 코드</h3>
<pre><code>function solution(park, routes) {
    let current_row = 0;
    let current_col = 0;

    const park_height = park.length;
    const park_width = park[0].length;

    // 시작 위치 찾기
    for (let i = 0; i &lt; park_height; i++) {
        for (let j = 0; j &lt; park_width; j++) {
            if (park[i][j] === &quot;S&quot;) {
                current_row = i;
                current_col = j;
            }
        }
    }

    // 방향별 이동 정의
    const direction_map = {
        &quot;N&quot;: [-1, 0],
        &quot;S&quot;: [1, 0],
        &quot;W&quot;: [0, -1],
        &quot;E&quot;: [0, 1]
    };

    //  명령 수행
    for (let i = 0; i &lt; routes.length; i++) {
        const [direction, distance_str] = routes[i].split(&quot; &quot;);
        const distance = Number(distance_str);

        const [move_row, move_col] = direction_map[direction];

        let next_row = current_row;
        let next_col = current_col;
        let can_move = true;

        //  이동 가능 여부 미리 검사
        for (let step = 0; step &lt; distance; step++) {
            next_row += move_row;
            next_col += move_col;

            // 공원 벗어나는지 확인
            if (next_row &lt; 0 || next_row &gt;= park_height || next_col &lt; 0 || next_col &gt;= park_width) {
                can_move = false;
                break;
            }

            // 장애물 확인
            if (park[next_row][next_col] === &quot;X&quot;) {
                can_move = false;
                break;
            }
        }

        // 문제가 없을 때만 실제 이동
        if (can_move) {
            current_row = next_row;
            current_col = next_col;
        }
    }

    return [current_row, current_col];
}</code></pre>]]></description>
        </item>
    </channel>
</rss>