<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>jinnify 블로그</title>
    <link>https://jinnify.tistory.com/</link>
    <description>기억하고 싶은것 정리하려 합니다.</description>
    <language>ko</language>
    <pubDate>Mon, 29 Jun 2026 09:08:37 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>jinnify</managingEditor>
    <image>
      <title>jinnify 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/3169208/attach/d0f7c5bf032e4cf0976d295913bfb75b</url>
      <link>https://jinnify.tistory.com</link>
    </image>
    <item>
      <title>알고리즘과 시간 복잡도</title>
      <link>https://jinnify.tistory.com/100</link>
      <description>&lt;h1&gt;Part 1. 알고리즘과 시간 복잡도&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;코딩테스트를 위한 알고리즘 스터디 — &lt;strong&gt;알고리즘 개념, 복잡도 분석, Big-O 표기법&lt;/strong&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h2&gt;  목차&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#1-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EC%9D%B4%EB%9E%80&quot;&gt;알고리즘이란?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#2-%EC%8B%9C%EA%B0%84-%EB%B3%B5%EC%9E%A1%EB%8F%84&quot;&gt;시간 복잡도&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#3-%EA%B3%B5%EA%B0%84-%EB%B3%B5%EC%9E%A1%EB%8F%84&quot;&gt;공간 복잡도&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#4-%EC%8B%9C%EA%B0%84-vs-%EA%B3%B5%EA%B0%84-trade-off&quot;&gt;시간 vs 공간 Trade-off&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#5-big-o-%ED%91%9C%EA%B8%B0%EB%B2%95&quot;&gt;Big-O 표기법&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#6-big-o-%EC%BD%94%EB%93%9C-%EC%98%88%EC%A0%9C&quot;&gt;Big-O 코드 예제&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#7-%EB%B3%B5%EC%9E%A1%EB%8F%84-%EB%B9%84%EA%B5%90--%EC%8B%A4%EC%A0%84-%EA%B0%90%EA%B0%81&quot;&gt;복잡도 비교 &amp;amp; 실전 감각&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#8-%EC%8B%A4%EC%A0%84-%ED%8C%81--%EC%9E%90%EC%A3%BC-%ED%95%98%EB%8A%94-%EC%8B%A4%EC%88%98&quot;&gt;실전 팁 &amp;amp; 자주 하는 실수&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;1. 알고리즘이란?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;알고리즘(Algorithm)&lt;/strong&gt; 은 &lt;strong&gt;문제를 해결하는 절차/방법&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;자주 쓰이는 문제 해결 방법은 &lt;strong&gt;패턴화&lt;/strong&gt;되어 있습니다.&lt;/li&gt;
&lt;li&gt;코딩테스트에서는 이 패턴을 파악하고 적용하는 능력이 중요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;대표적인 알고리즘 패턴&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;패턴&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;th&gt;대표 문제&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;완전 탐색 (Brute Force)&lt;/td&gt;
&lt;td&gt;모든 경우의 수를 확인&lt;/td&gt;
&lt;td&gt;순열, 조합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;그리디 (Greedy)&lt;/td&gt;
&lt;td&gt;매 순간 최선의 선택&lt;/td&gt;
&lt;td&gt;거스름돈, 회의실 배정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;분할 정복 (Divide &amp;amp; Conquer)&lt;/td&gt;
&lt;td&gt;문제를 작게 나눠서 해결&lt;/td&gt;
&lt;td&gt;병합 정렬, 퀵 정렬&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;동적 프로그래밍 (DP)&lt;/td&gt;
&lt;td&gt;하위 문제의 결과를 저장하여 재활용&lt;/td&gt;
&lt;td&gt;피보나치, 배낭 문제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BFS / DFS&lt;/td&gt;
&lt;td&gt;그래프/트리 탐색&lt;/td&gt;
&lt;td&gt;미로 탐색, 연결 요소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이진 탐색&lt;/td&gt;
&lt;td&gt;정렬된 데이터에서 절반씩 줄여가며 탐색&lt;/td&gt;
&lt;td&gt;특정 값 찾기, 파라메트릭 서치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;투 포인터&lt;/td&gt;
&lt;td&gt;두 개의 포인터로 범위를 좁혀가며 탐색&lt;/td&gt;
&lt;td&gt;부분합, 정렬된 배열에서의 쌍 찾기&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;알고리즘의 평가 기준&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;시간 복잡도 (Time Complexity)&lt;/strong&gt;: 실행 시간이 얼마나 걸리는가?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;공간 복잡도 (Space Complexity)&lt;/strong&gt;: 메모리를 얼마나 사용하는가?&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;5. 시간 복잡도&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;어떤 문제를 해결할 때, &lt;strong&gt;입력 크기(n)에 따라 연산 횟수가 어떻게 변하는지&lt;/strong&gt; 나타내는 척도&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;왜 중요한가?&lt;/h3&gt;
&lt;p&gt;코딩테스트에서는 보통 &lt;strong&gt;시간 제한&lt;/strong&gt;이 있습니다 (1~2초).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;일반적으로 1초에 약 1억(10^8)번의 연산이 가능하다고 가정합니다.&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;  10⁸ ≈ 1초 법칙&lt;/h3&gt;
&lt;p&gt;코딩테스트에서 가장 중요한 감각 중 하나!&lt;br&gt;&lt;strong&gt;내 알고리즘의 연산 횟수가 약 10⁸(1억)을 넘으면 1초를 초과할 가능성이 높다&lt;/strong&gt;는 뜻입니다.&lt;/p&gt;
&lt;h4&gt;이걸 어디에 쓰나요?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;코드를 짜기 전에&lt;/strong&gt; 시간 초과 여부를 미리 예측할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;실전 예시&lt;/h4&gt;
&lt;p&gt;문제 조건: &lt;code&gt;n ≤ 100,000 (10만)&lt;/code&gt;, 시간 제한: &lt;code&gt;1초&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;❌ O(n²) 풀이 → 100,000² = 10,000,000,000 (100억) → 10⁸ 초과 → 시간 초과!
✅ O(n log n) 풀이 → 100,000 × 17 ≈ 1,700,000 (170만) → 10⁸ 이하 → 여유롭게 통과!&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;판단하는 방법 (3단계)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;1단계: 문제에서 n의 범위를 확인한다
2단계: 내 알고리즘의 Big-O에 n을 대입하여 연산 횟수를 계산한다
3단계: 연산 횟수가 10⁸ 이하인지 확인한다
   → 이하면 ✅ 통과 가능
   → 초과면 ❌ 더 빠른 알고리즘 필요&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;언어별 속도 차이&lt;/h4&gt;
&lt;p&gt;실제로는 프로그래밍 언어에 따라 속도가 다릅니다:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;언어&lt;/th&gt;
&lt;th&gt;1초에 가능한 연산 수&lt;/th&gt;
&lt;th&gt;비고&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;C / C++&lt;/td&gt;
&lt;td&gt;약 &lt;strong&gt;10⁸ ~ 10⁹&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;가장 빠름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Java&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;약 &lt;strong&gt;10⁷ ~ 10⁸&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;C++보다 2~3배 느림&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;약 &lt;strong&gt;10⁶ ~ 10⁷&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;가장 느림&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;⚠️ 그래서 같은 문제라도 &lt;strong&gt;Java는 C++보다 시간 제한을 2~3배 더 주는&lt;/strong&gt; 경우가 많습니다!&lt;br&gt;Java로 코딩테스트를 볼 때는 이 점을 고려해야 합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;코딩테스트를 위한 시간 복잡도 — 제약조건을 보고 알고리즘 예측하기&lt;/h3&gt;
&lt;p&gt;코딩테스트 문제의 절반은 &lt;strong&gt;제약조건(입력값의 범위)&lt;/strong&gt; 만 보고도 어떤 시간 복잡도로 풀어야 하는지 힌트를 얻을 수 있습니다!&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;입력 크기 (N)&lt;/th&gt;
&lt;th&gt;시간 복잡도&lt;/th&gt;
&lt;th&gt;대표 알고리즘&lt;/th&gt;
&lt;th&gt;연산 횟수 예시&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;N ≤ 10&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(N!)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;순열 완전 탐색&lt;/td&gt;
&lt;td&gt;10! = 360만 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N ≤ 20&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(2^N)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;재귀 완전 탐색, 비트마스킹 DP&lt;/td&gt;
&lt;td&gt;2²⁰ ≈ 100만 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N ≤ 500&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(N³)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3중 반복문, 플로이드-워셜&lt;/td&gt;
&lt;td&gt;500³ = 1.25억 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N ≤ 2,000&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(N²)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2중 반복문(완전 탐색), 2차원 DP&lt;/td&gt;
&lt;td&gt;2000² = 400만 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N ≤ 100,000&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(N log N)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;정렬, 우선순위 큐(Heap), 다익스트라, 이분 탐색&lt;/td&gt;
&lt;td&gt;10⁵ × 17 ≈ 170만 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N ≤ 10,000,000&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(N)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1중 반복문, 해시 테이블, 투 포인터, DFS/BFS&lt;/td&gt;
&lt;td&gt;10⁷ ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N ≥ 10억&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(log N)&lt;/code&gt; / &lt;code&gt;O(1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;이진 탐색(탐색 범위), 수학 공식&lt;/td&gt;
&lt;td&gt;30번 이하 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h4&gt;보충 설명&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;플로이드-워셜&lt;/strong&gt; &lt;code&gt;O(N³)&lt;/code&gt;: 모든 정점 간 최단 경로. N ≤ 500일 때만 사용 가능&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;비트마스킹 DP&lt;/strong&gt; &lt;code&gt;O(2^N)&lt;/code&gt;: 집합의 부분집합을 비트로 표현. N ≤ 20이면 2²⁰ ≈ 100만이라 가능&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;다익스트라&lt;/strong&gt; &lt;code&gt;O(E log V)&lt;/code&gt;: 우선순위 큐를 사용한 최단 경로&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;N ≥ 10억&lt;/strong&gt;: 반복문 자체가 불가능 → 수학 공식이나 이진 탐색처럼 &lt;strong&gt;범위를 절반씩 줄이는&lt;/strong&gt; 접근 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;실전 사용법&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;1. 문제를 읽는다
2. 제약조건에서 N의 범위를 확인한다
3. 위 표에서 허용 가능한 시간 복잡도를 찾는다
4. 불가능한 알고리즘을 먼저 제거한다 ← 핵심!
5. 남은 복잡도에 맞는 알고리즘 중 문제에 적합한 것을 선택한다&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;실전 예시: 마라톤 문제&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  &lt;strong&gt;제약조건&lt;/strong&gt;: &amp;quot;마라톤 경기에 참여한 선수의 수는 1명 이상 &lt;strong&gt;100,000명&lt;/strong&gt; 이하입니다.&amp;quot;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;strong&gt;Step 1 — N = 100,000 (10⁵) 확인!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 2 — 불가능한 알고리즘 제거:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;❌ O(N²)  → (10⁵)² = 10¹⁰ (100억) → TLE! 절대 안 됨
❌ O(N³)  → (10⁵)³ = 10¹⁵       → TLE! 말도 안 됨
❌ O(2^N) → 2^100000             → TLE! 우주가 멸망해도 안 끝남&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Step 3 — 가능한 알고리즘만 남김:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;✅ O(N log N) → 10⁵ × 17 ≈ 170만   → 통과! (정렬, 힙, 이분탐색)
✅ O(N)       → 10⁵ = 10만          → 통과! (해시, 투포인터)
✅ O(log N)   → 약 17                → 통과! (이진탐색)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Step 4 — 문제 유형에 맞는 알고리즘 선택:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;문제 유형&lt;/th&gt;
&lt;th&gt;추천 알고리즘&lt;/th&gt;
&lt;th&gt;Big-O&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;완주하지 못한 선수 찾기&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;해시 테이블&lt;/strong&gt; (HashMap)&lt;/td&gt;
&lt;td&gt;O(N)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;정렬 후 비교&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;정렬&lt;/strong&gt; (Arrays.sort)&lt;/td&gt;
&lt;td&gt;O(N log N)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  &lt;strong&gt;핵심&lt;/strong&gt;: 제약조건을 보면 &lt;strong&gt;&amp;quot;이건 절대 안 되겠다&amp;quot;&lt;/strong&gt; 를 먼저 판단하는 것이 중요합니다!&lt;br&gt;N = 100,000이면 이중 for문(O(N²))은 무조건 TLE → 다른 방법을 찾아야 합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;⚠️ 이 표는 &lt;strong&gt;절대적인 기준이 아닌 대략적인 가이드&lt;/strong&gt;입니다. 실제로는 상수 계수나 구현 방식에 따라 달라질 수 있습니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h2&gt;6. 공간 복잡도&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;입력값에 따른 &lt;strong&gt;메모리 할당량을 계산&lt;/strong&gt;하는 것&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;ul&gt;
&lt;li&gt;메모리는 한정적이기 때문에 &lt;strong&gt;최대한 적은 용량의 메모리를 사용&lt;/strong&gt;하는 것이 좋습니다.&lt;/li&gt;
&lt;li&gt;일반적으로 코딩테스트 메모리 제한은 &lt;strong&gt;128MB ~ 512MB&lt;/strong&gt; 정도입니다.&lt;/li&gt;
&lt;li&gt;공간 복잡도가 중요하지만, 이것까지 보는 회사는 많이 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;메모리 사용량 감 잡기&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;int 배열 1,000,000개 ≈ 4MB
int 배열 10,000,000개 ≈ 40MB
2차원 배열 1000 × 1000 ≈ 4MB
2차원 배열 10000 × 10000 ≈ 400MB →   메모리 초과 가능!&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  &lt;strong&gt;실전에서는&lt;/strong&gt;: 공간 복잡도보다 시간 복잡도가 더 중요하게 평가되는 경우가 많습니다. 하지만 메모리 초과(MLE)로 틀리는 경우도 있으니 기본적인 감각은 필요합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h2&gt;7. 시간 vs 공간 Trade-off&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;시간과 공간은 보통 &lt;strong&gt;trade-off(상충 관계)&lt;/strong&gt; 입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;pre&gt;&lt;code&gt;실행시간 ↓ (줄이려면) → 메모리 사용량 ↑ (늘어남)
메모리 사용량 ↓ (줄이려면) → 실행시간 ↑ (늘어남)&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;대표적인 예시&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;상황&lt;/th&gt;
&lt;th&gt;시간 우선 (메모리 더 사용)&lt;/th&gt;
&lt;th&gt;공간 우선 (시간 더 사용)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;중복 검사&lt;/td&gt;
&lt;td&gt;&lt;code&gt;HashSet&lt;/code&gt;에 저장 후 검사 &lt;code&gt;O(1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;매번 리스트 순회 &lt;code&gt;O(n)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DP&lt;/td&gt;
&lt;td&gt;메모이제이션 테이블 사용&lt;/td&gt;
&lt;td&gt;재귀로만 계산 (중복 연산)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;그래프 표현&lt;/td&gt;
&lt;td&gt;인접 행렬 &lt;code&gt;O(V²)&lt;/code&gt; 공간&lt;/td&gt;
&lt;td&gt;인접 리스트 &lt;code&gt;O(V+E)&lt;/code&gt; 공간&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;8. Big-O 표기법&lt;/h2&gt;
&lt;h3&gt;Big-O란?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;알고리즘의 최악의 경우 성능&lt;/strong&gt;을 표현하는 점근적 표기법입니다.&lt;/p&gt;
&lt;h3&gt;핵심 규칙&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;최고차항만 남긴다&lt;/strong&gt; (작은 차수 무시)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;계수(상수)를 무시한다&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;Input &lt;code&gt;n&lt;/code&gt;의 크기가 커지면 작은 차수의 항들이 runtime에 미치는 영향이 미미해지기 때문입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;예제&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;원래 식&lt;/th&gt;
&lt;th&gt;Big-O&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;T(n) = 2n³ + 4n² + 3n + 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(n³)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;T(n) = 17n⁴ + 4n³ + 3n + 8&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(n⁴)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;T(n) = 16n + log₂n&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(n)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;T(n) = 2ⁿ + 12n⁷ + 3n&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(2ⁿ)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;T(n) = 2n + 4n² + 3n! + 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(n!)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;Big-O 이외의 표기법 (참고)&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;표기법&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Big-O&lt;/strong&gt; &lt;code&gt;O(f(n))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;상한 (최악의 경우)&lt;/td&gt;
&lt;td&gt;&amp;quot;최대 이만큼 걸린다&amp;quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Big-Ω&lt;/strong&gt; &lt;code&gt;Ω(f(n))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;하한 (최선의 경우)&lt;/td&gt;
&lt;td&gt;&amp;quot;최소 이만큼 걸린다&amp;quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Big-Θ&lt;/strong&gt; &lt;code&gt;Θ(f(n))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;평균 (정확한 차수)&lt;/td&gt;
&lt;td&gt;&amp;quot;대략 이만큼 걸린다&amp;quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;코딩테스트에서는 &lt;strong&gt;Big-O (최악의 경우)&lt;/strong&gt; 만 사용하면 됩니다. 최악의 경우를 기준으로 시간 안에 들어오는지 판단해야 하기 때문입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h2&gt;9. Big-O 코드 예제&lt;/h2&gt;
&lt;h3&gt;O(1) — 상수 시간&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;입력 크기에 관계없이 항상 같은 시간&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;int a = 5;
a += 1;
System.out.println(a);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;배열에서 인덱스로 접근: &lt;code&gt;arr[3]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;해시 테이블에서 키로 접근: &lt;code&gt;map.get(&amp;quot;key&amp;quot;)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;O(log n) — 로그 시간&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;탐색해야 되는 데이터가 &lt;strong&gt;절반씩 줄어드는&lt;/strong&gt; 경우&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;int binarySearch(int[] data, int target) {
    int start = 0;
    int end = data.length - 1;

    while (start &amp;lt;= end) {
        int mid = (start + end) / 2;
        if (data[mid] == target) {
            return mid;        // 찾음!
        } else if (data[mid] &amp;gt; target) {
            end = mid - 1;     // 왼쪽 절반만 탐색
        } else {
            start = mid + 1;   // 오른쪽 절반만 탐색
        }
    }
    return -1;  // 못 찾음
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;n = 1,000,000 일 때
→ 약 20번의 비교만으로 답을 찾을 수 있다! (log₂(1,000,000) ≈ 20)&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;O(n) — 선형 시간&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;입력 크기에 비례하여 시간이 증가&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;for (int i = 0; i &amp;lt; n; i++) {
    System.out.println(&amp;quot;hi&amp;quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;전체 데이터를 한 번 순회하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;O(n log n) — 선형 로그 시간&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;효율적인 정렬 알고리즘의 시간 복잡도&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;int[] arr = {5, 3, 8, 1, 2};
Arrays.sort(arr);  // Java의 내장 정렬 = Dual-Pivot Quicksort = O(n log n)&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;병합 정렬(Merge Sort), 퀵 정렬(Quick Sort - 평균), 힙 정렬(Heap Sort)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;O(n²) — 이차 시간&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;이중 반복문&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;for (int i = 0; i &amp;lt; n; i++) {
    for (int j = 0; j &amp;lt; n; j++) {
        System.out.println(&amp;quot;hi&amp;quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;버블 정렬, 선택 정렬, 삽입 정렬&lt;/li&gt;
&lt;li&gt;2차원 배열 전체 순회&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;O(2ⁿ) — 지수 시간&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;재귀로 모든 경우를 탐색하는 경우&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;int fibo(int n) {
    if (n &amp;lt;= 1) return n;
    return fibo(n - 1) + fibo(n - 2);
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;              fibo(5)
            /         \
       fibo(4)        fibo(3)
      /      \        /     \
   fibo(3)  fibo(2) fibo(2) fibo(1)
   /    \
fibo(2) fibo(1)&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;⚠️ 같은 값이 반복 계산됩니다! → DP(메모이제이션)로 &lt;code&gt;O(n)&lt;/code&gt;으로 최적화 가능&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// DP로 최적화한 피보나치 → O(n)
int fiboDp(int n) {
    int[] dp = new int[n + 1];
    dp[0] = 0;
    dp[1] = 1;
    for (int i = 2; i &amp;lt;= n; i++) {
        dp[i] = dp[i - 1] + dp[i - 2];
    }
    return dp[n];
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;O(n!) — 팩토리얼 시간&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;모든 순열을 구하는 경우&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 백트래킹으로 순열 구하기 → n!
void permutation(int[] arr, int depth, int n) {
    if (depth == n) {
        System.out.println(Arrays.toString(arr));
        return;
    }
    for (int i = depth; i &amp;lt; n; i++) {
        swap(arr, depth, i);
        permutation(arr, depth + 1, n);
        swap(arr, depth, i);  // 원상 복구
    }
}
// 4! = 24개&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;10. 복잡도 비교 &amp;amp; 실전 감각&lt;/h2&gt;
&lt;h3&gt;  복잡도 순위 (빠른 순)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;O(1) &amp;lt; O(log n) &amp;lt; O(n) &amp;lt; O(n log n) &amp;lt; O(n²) &amp;lt; O(n³) &amp;lt; O(2ⁿ) &amp;lt; O(n!)
 ↑                                                              ↑
최고                                                           최악&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;  n에 따른 연산 횟수 비교&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Big-O&lt;/th&gt;
&lt;th&gt;n=10&lt;/th&gt;
&lt;th&gt;n=100&lt;/th&gt;
&lt;th&gt;n=1,000&lt;/th&gt;
&lt;th&gt;n=10,000&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;O(1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;O(log n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;O(n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;1,000&lt;/td&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;O(n log n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;700&lt;/td&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;130,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;O(n²)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;td&gt;100,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;O(2ⁿ)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1,024&lt;/td&gt;
&lt;td&gt;1.27 × 10³⁰&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;O(n!)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3,628,800&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  &lt;code&gt;n=20&lt;/code&gt; 정도만 되어도 &lt;code&gt;2ⁿ&lt;/code&gt;은 약 100만, &lt;code&gt;20!&lt;/code&gt;은 약 2.4 × 10¹⁸ → 사실상 계산 불가능&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;실전 판단 요령&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;문제를 읽고 n의 범위를 확인한다
→ 위 표를 참고하여 허용 가능한 시간 복잡도를 추정한다
→ 해당 복잡도에 맞는 알고리즘을 선택한다&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;n ≤ 1,000,000&lt;/code&gt; → &lt;code&gt;O(n log n)&lt;/code&gt; 이하 알고리즘 필요 → 정렬, 이진 탐색 등&lt;/li&gt;
&lt;li&gt;&lt;code&gt;n ≤ 10,000&lt;/code&gt; → &lt;code&gt;O(n²)&lt;/code&gt; 가능 → 이중 루프 OK&lt;/li&gt;
&lt;li&gt;&lt;code&gt;n ≤ 20&lt;/code&gt; → &lt;code&gt;O(2ⁿ)&lt;/code&gt; 가능 → 완전 탐색(백트래킹) 고려&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;11. 코딩테스트에서 자주 쓰이는 자료구조-알고리즘 맵핑&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;문제 유형&lt;/th&gt;
&lt;th&gt;자료구조&lt;/th&gt;
&lt;th&gt;알고리즘&lt;/th&gt;
&lt;th&gt;시간 복잡도&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;최단 경로 (가중치 有)&lt;/td&gt;
&lt;td&gt;우선순위 큐&lt;/td&gt;
&lt;td&gt;다익스트라&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(E log V)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;최단 경로 (가중치 無)&lt;/td&gt;
&lt;td&gt;큐&lt;/td&gt;
&lt;td&gt;BFS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(V + E)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;연결 요소 / 경로 탐색&lt;/td&gt;
&lt;td&gt;스택 / 재귀&lt;/td&gt;
&lt;td&gt;DFS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(V + E)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;정렬&lt;/td&gt;
&lt;td&gt;배열&lt;/td&gt;
&lt;td&gt;정렬 알고리즘&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(n log n)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;빠른 검색 / 중복 확인&lt;/td&gt;
&lt;td&gt;해시 테이블&lt;/td&gt;
&lt;td&gt;해싱&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(1)&lt;/code&gt; 평균&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;구간 합 / 최적 부분 구조&lt;/td&gt;
&lt;td&gt;배열 / 테이블&lt;/td&gt;
&lt;td&gt;DP&lt;/td&gt;
&lt;td&gt;문제마다 다름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;부분합 / 연속 구간&lt;/td&gt;
&lt;td&gt;배열&lt;/td&gt;
&lt;td&gt;투 포인터 / 슬라이딩 윈도우&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(n)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;서로소 집합 / 사이클 판별&lt;/td&gt;
&lt;td&gt;배열 (부모)&lt;/td&gt;
&lt;td&gt;Union-Find&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(α(n))&lt;/code&gt; ≈ &lt;code&gt;O(1)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;12. 실전 팁 &amp;amp; 자주 하는 실수&lt;/h2&gt;
&lt;h3&gt;✅ 꼭 기억할 것&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ArrayList.remove(0)&lt;/code&gt; 은 &lt;code&gt;O(n)&lt;/code&gt; 이다!&lt;/strong&gt; → 큐가 필요하면 &lt;code&gt;LinkedList&lt;/code&gt; 또는 &lt;code&gt;ArrayDeque&lt;/code&gt; 사용&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;List.contains()&lt;/code&gt; 는 &lt;code&gt;O(n)&lt;/code&gt;&lt;/strong&gt; → 빠른 검색이 필요하면 &lt;code&gt;HashSet&lt;/code&gt;이나 &lt;code&gt;HashMap&lt;/code&gt; 사용 &lt;code&gt;O(1)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;입력 속도&lt;/strong&gt;: &lt;code&gt;Scanner&lt;/code&gt; 대신 &lt;code&gt;BufferedReader&lt;/code&gt; 사용 (대량 입력 시 필수!)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;String&lt;/code&gt; 연결은 &lt;code&gt;StringBuilder&lt;/code&gt;&lt;/strong&gt; 사용 → 반복문에서 &lt;code&gt;+&lt;/code&gt; 연산은 &lt;code&gt;O(n²)&lt;/code&gt;이 될 수 있음&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;int&lt;/code&gt; 범위 주의&lt;/strong&gt;: &lt;code&gt;int&lt;/code&gt;는 약 21억까지 → 더 큰 수는 &lt;code&gt;long&lt;/code&gt; 사용&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// Scanner (느림)
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();

// BufferedReader (빠름) ← 코딩테스트에서 추천!
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;❌ 자주 하는 실수&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;실수&lt;/th&gt;
&lt;th&gt;결과&lt;/th&gt;
&lt;th&gt;해결&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;List.contains()&lt;/code&gt; 반복 사용&lt;/td&gt;
&lt;td&gt;시간 초과 (TLE)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;HashSet&lt;/code&gt;으로 변환 후 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Scanner&lt;/code&gt; 사용 (대량 입력)&lt;/td&gt;
&lt;td&gt;시간 초과 (TLE)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BufferedReader&lt;/code&gt; 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;반복문에서 &lt;code&gt;String&lt;/code&gt; + 연결&lt;/td&gt;
&lt;td&gt;시간 초과 (TLE)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;StringBuilder&lt;/code&gt; 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;int&lt;/code&gt; 오버플로우&lt;/td&gt;
&lt;td&gt;오답 (WA)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;long&lt;/code&gt; 타입 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;O(n²)&lt;/code&gt; 풀이로 큰 &lt;code&gt;n&lt;/code&gt; 처리&lt;/td&gt;
&lt;td&gt;시간 초과 (TLE)&lt;/td&gt;
&lt;td&gt;더 효율적인 알고리즘 탐색&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;  Java 코딩테스트 기본 템플릿&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;import java.io.*;
import java.util.*;

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

        // 여기서부터 풀이 시작

        // 한 줄에 하나의 값 읽기
        int n = Integer.parseInt(br.readLine());

        // 공백으로 구분된 여러 값 읽기
        StringTokenizer st = new StringTokenizer(br.readLine());
        int a = Integer.parseInt(st.nextToken());
        int b = Integer.parseInt(st.nextToken());

        // 출력 (StringBuilder에 모아서 한번에 출력)
        sb.append(a + b).append(&amp;quot;\n&amp;quot;);
        System.out.print(sb);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;  QUIZ 1 — Big-O 판별 &amp;amp; 시간 초과 예측&lt;/h2&gt;
&lt;h3&gt;  문제&lt;/h3&gt;
&lt;p&gt;다음 코드의 &lt;strong&gt;Big-O 표기법&lt;/strong&gt;은 무엇일까요?&lt;br&gt;그리고 제약조건이 &lt;code&gt;1 ≤ n ≤ 10⁵&lt;/code&gt; 일 때, &lt;strong&gt;시간 초과가 날까요?&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;int solution(int n) {
    int count = 0;
    for (int i = 0; i &amp;lt; n; i++) {
        for (int j = i; j &amp;lt; n; j++) {
            count++;
        }
    }
    return count;
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;  정답 보기 (클릭)&lt;/b&gt;&lt;/summary&gt;

&lt;h3&gt;① Big-O 분석&lt;/h3&gt;
&lt;p&gt;안쪽 루프가 &lt;code&gt;j = i&lt;/code&gt; 부터 시작하므로, 반복 횟수는 다음과 같습니다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;i=0 → n번
i=1 → n-1번
i=2 → n-2번
...
i=n-1 → 1번

합계 = n + (n-1) + (n-2) + ... + 1 = n(n+1)/2&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;n(n+1)/2 = (n² + n) / 2&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Big-O 규칙 적용:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;최고차항만 남긴다 → &lt;strong&gt;n²&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;계수 &lt;code&gt;/2&lt;/code&gt; 무시&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;✅ &lt;strong&gt;정답: O(n²)&lt;/strong&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;② 시간 초과 여부&lt;/h3&gt;
&lt;p&gt;제약조건: &lt;code&gt;1 ≤ n ≤ 10⁵&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Worst Case: n = 10⁵
→ n² = (10⁵)² = 10¹⁰ (100억)
→ 10¹⁰ &amp;gt;&amp;gt; 10⁸ (1억)
→ 약 100초 소요 예상&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;❌ &lt;strong&gt;시간 초과 (TLE) 발생!&lt;/strong&gt; 100억 연산은 10⁸ 기준의 100배이므로 시간 제한 안에 절대 통과할 수 없습니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;③ 그렇다면 어떻게 풀어야 할까?&lt;/h3&gt;
&lt;p&gt;사실 이 코드의 &lt;code&gt;count&lt;/code&gt; 값은 수학 공식으로 바로 구할 수 있습니다:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;int solution(int n) {
    return n * (n + 1) / 2;  // O(1) — 상수 시간!
}&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;풀이&lt;/th&gt;
&lt;th&gt;Big-O&lt;/th&gt;
&lt;th&gt;n=10⁵ 연산 횟수&lt;/th&gt;
&lt;th&gt;결과&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;이중 for문&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(n²)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;10¹⁰ (100억)&lt;/td&gt;
&lt;td&gt;❌ TLE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;수학 공식&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;✅ 통과&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  &lt;strong&gt;교훈&lt;/strong&gt;: 같은 문제라도 접근 방식에 따라 &lt;code&gt;O(n²)&lt;/code&gt; → &lt;code&gt;O(1)&lt;/code&gt; 까지 줄일 수 있습니다!&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/details&gt;

&lt;hr&gt;
&lt;h2&gt;  QUIZ 2 — 상수 루프 vs 입력 의존 루프&lt;/h2&gt;
&lt;h3&gt;  문제&lt;/h3&gt;
&lt;p&gt;다음 코드의 &lt;strong&gt;Big-O 표기법&lt;/strong&gt;은 무엇일까요?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;제약조건:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;배열 &lt;code&gt;arr&lt;/code&gt;의 길이 &lt;code&gt;m&lt;/code&gt;: &lt;code&gt;1 ≤ m ≤ 10⁴&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;배열 각 원소 &lt;code&gt;arr[i]&lt;/code&gt;의 크기 &lt;code&gt;n&lt;/code&gt;: &lt;code&gt;1 ≤ n ≤ 10⁵&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;int solution(int arr[]) {
    for (int i = 0; i &amp;lt; 10; i++) {
        for (int num : arr) {
            System.out.println(num);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;  정답 보기 (클릭)&lt;/b&gt;&lt;/summary&gt;

&lt;h3&gt;① Big-O 분석&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;루프&lt;/th&gt;
&lt;th&gt;반복 횟수&lt;/th&gt;
&lt;th&gt;입력에 의존?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;바깥 &lt;code&gt;for (i &amp;lt; 10)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10번&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ 고정값 (상수)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;안쪽 &lt;code&gt;for-each&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;m번&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ arr 길이에 의존&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;총 연산 = &lt;code&gt;10 × m = 10m&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Big-O 규칙: 계수 무시 → &lt;code&gt;10m&lt;/code&gt; → &lt;strong&gt;O(m)&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;✅ &lt;strong&gt;정답: O(m)&lt;/strong&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;② &amp;quot;n은 어디 갔지?&amp;quot;&lt;/h3&gt;
&lt;p&gt;제약조건에 &lt;code&gt;n&lt;/code&gt;(원소의 크기)이 있지만, 이 코드에서는 &lt;strong&gt;값을 출력만&lt;/strong&gt; 하고 있습니다.&lt;br&gt;원소의 값이 크든 작든 &lt;code&gt;System.out.println(num)&lt;/code&gt; 은 &lt;strong&gt;O(1)&lt;/strong&gt; 이므로 &lt;code&gt;n&lt;/code&gt;은 시간 복잡도에 영향을 주지 않습니다.&lt;/p&gt;
&lt;p&gt;만약 &lt;code&gt;n&lt;/code&gt;이 영향을 주려면 이런 식이어야 합니다:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 이 경우는 O(m × n)
for (int num : arr) {               // m번
    for (int j = 0; j &amp;lt; num; j++) {  // 최대 n번 (원소 값만큼 반복)
        System.out.println(j);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;③ 시간 초과 여부&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;m = 10⁴, O(m) → 10⁴ (1만) → 10⁸보다 훨씬 작음 → ✅ 여유롭게 통과!&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  &lt;strong&gt;교훈&lt;/strong&gt;: 고정된 숫자(상수)로 도는 루프는 Big-O에서 무시합니다!&lt;br&gt;제약조건에 있는 변수가 실제 코드에서 &lt;strong&gt;어떻게 사용되는지&lt;/strong&gt; 잘 확인해야 합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/details&gt;

&lt;hr&gt;
&lt;h2&gt;  QUIZ 3 — for문 안의 while문&lt;/h2&gt;
&lt;h3&gt;� 문제&lt;/h3&gt;
&lt;p&gt;다음 코드의 &lt;strong&gt;Big-O 표기법&lt;/strong&gt;은? 그리고 제약조건 &lt;code&gt;1 ≤ n ≤ 10⁵&lt;/code&gt; 에서 &lt;strong&gt;시간 초과가 날까요?&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;int solution(int n) {
    for (int i = 0; i &amp;lt; n; i++) {
        int j = 0;
        while (j &amp;lt; n) {
            j++;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;  정답 보기 (클릭)&lt;/b&gt;&lt;/summary&gt;

&lt;h3&gt;① Big-O 분석&lt;/h3&gt;
&lt;p&gt;핵심은 &lt;strong&gt;&lt;code&gt;int j = 0;&lt;/code&gt; 이 for문 안에 있다&lt;/strong&gt;는 점입니다!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;for (int i = 0; i &amp;lt; n; i++) {   // n번 반복
    int j = 0;                   // ⚠️ 매번 j를 0으로 초기화!
    while (j &amp;lt; n) {              // → 매번 n번 반복
        j++;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;루프&lt;/th&gt;
&lt;th&gt;반복 횟수&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;바깥 &lt;code&gt;for&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;n번&lt;/td&gt;
&lt;td&gt;i가 0부터 n-1까지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;안쪽 &lt;code&gt;while&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;n번&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;매번&lt;/strong&gt; j=0에서 시작하므로 항상 n번&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;총 연산 = &lt;code&gt;n × n = n²&lt;/code&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;✅ &lt;strong&gt;정답: O(n²)&lt;/strong&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;② 만약 &lt;code&gt;j&lt;/code&gt;가 바깥에 선언되어 있다면?&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;int solution(int n) {
    int j = 0;                       // ⚠️ 바깥에서 한 번만 초기화
    for (int i = 0; i &amp;lt; n; i++) {
        while (j &amp;lt; n) {
            j++;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 경우 &lt;code&gt;while&lt;/code&gt;문은 &lt;strong&gt;i=0일 때 한 번만 실행&lt;/strong&gt;되고, 이후로는 &lt;code&gt;j&lt;/code&gt;가 이미 &lt;code&gt;n&lt;/code&gt;이라 실행되지 않습니다.&lt;br&gt;→ 총 연산 = &lt;code&gt;n(for) + n(while) = 2n&lt;/code&gt; → &lt;strong&gt;O(n)&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  &lt;strong&gt;교훈&lt;/strong&gt;: 변수의 &lt;strong&gt;선언 위치(초기화 위치)&lt;/strong&gt; 하나로 &lt;code&gt;O(n²)&lt;/code&gt; ↔ &lt;code&gt;O(n)&lt;/code&gt; 이 바뀔 수 있습니다!&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;③ 시간 초과 여부&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;n = 10⁵, O(n²) → (10⁵)² = 10¹⁰ (100억) → 10⁸ 초과 → ❌ 시간 초과!&lt;/code&gt;&lt;/pre&gt;&lt;/details&gt;

&lt;hr&gt;
&lt;h2&gt;  QUIZ 4 — Quiz 3의 함정 변형! j의 선언 위치&lt;/h2&gt;
&lt;h3&gt;  문제&lt;/h3&gt;
&lt;p&gt;Quiz 3과 거의 같은 코드인데, &lt;strong&gt;딱 한 줄&lt;/strong&gt;이 다릅니다. Big-O는 무엇일까요?&lt;/p&gt;
&lt;p&gt;제약조건: &lt;code&gt;1 ≤ n ≤ 10⁵&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;int solution(int n) {
    int j = 0;                        // ⚠️ 여기!
    for (int i = 0; i &amp;lt; n; i++) {
        while (j &amp;lt; n) {
            j++;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;  정답 보기 (클릭)&lt;/b&gt;&lt;/summary&gt;

&lt;h3&gt;① Big-O 분석&lt;/h3&gt;
&lt;p&gt;Quiz 3과의 차이: &lt;strong&gt;&lt;code&gt;int j = 0;&lt;/code&gt;이 for문 바깥에&lt;/strong&gt; 있습니다!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;i=0일 때: j가 0 → while문 실행 → j가 0에서 n까지 증가 (n번 실행)
i=1일 때: j가 이미 n → while 조건 불만족 → 실행 안 됨 (0번)
i=2일 때: j가 이미 n → 실행 안 됨 (0번)
...
i=n-1일 때: 실행 안 됨 (0번)&lt;/code&gt;&lt;/pre&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;루프&lt;/th&gt;
&lt;th&gt;총 실행 횟수&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;for&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;n번 (i를 증가시키는 것)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;while&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;전체에서 딱 n번&lt;/strong&gt; (j는 0→n까지 한 번만 증가)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;총 연산 = &lt;code&gt;n(for) + n(while) = 2n&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Big-O: 계수 무시 → &lt;strong&gt;O(n)&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;✅ &lt;strong&gt;정답: O(n)&lt;/strong&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;② Quiz 3 vs Quiz 4 비교&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// Quiz 3: O(n²) — j가 매번 초기화됨
for (int i = 0; i &amp;lt; n; i++) {
    int j = 0;              // ← 안쪽에서 매번 초기화
    while (j &amp;lt; n) { j++; }
}

// Quiz 4: O(n) — j가 한 번만 초기화됨
int j = 0;                  // ← 바깥에서 한 번만 초기화
for (int i = 0; i &amp;lt; n; i++) {
    while (j &amp;lt; n) { j++; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;Quiz 3&lt;/th&gt;
&lt;th&gt;Quiz 4&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;j&lt;/code&gt; 선언 위치&lt;/td&gt;
&lt;td&gt;for문 &lt;strong&gt;안&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;for문 &lt;strong&gt;밖&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;while 실행 횟수&lt;/td&gt;
&lt;td&gt;매번 n번&lt;/td&gt;
&lt;td&gt;전체에서 n번&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Big-O&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;O(n²)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;O(n)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;③ 시간 초과 여부&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;n = 10⁵, O(n) → 10⁵ (10만) → 10⁸보다 훨씬 작음 → ✅ 여유롭게 통과!&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  &lt;strong&gt;교훈&lt;/strong&gt;: 코드가 비슷해 보여도 &lt;strong&gt;변수 초기화 위치 하나&lt;/strong&gt;로 시간 복잡도가 완전히 달라집니다. 코딩테스트에서 자주 나오는 함정이니 꼭 주의하세요!&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/details&gt;

&lt;hr&gt;
&lt;h2&gt;  QUIZ 5 — j *= 2 는 무엇을 의미할까?&lt;/h2&gt;
&lt;h3&gt;  문제&lt;/h3&gt;
&lt;p&gt;다음 코드의 &lt;strong&gt;Big-O 표기법&lt;/strong&gt;은? 제약조건 &lt;code&gt;1 ≤ n ≤ 10³&lt;/code&gt; 에서 &lt;strong&gt;시간 초과가 날까요?&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;int solution(int n) {
    for (int i = 0; i &amp;lt; n; i++) {
        int j = 1;
        while (j &amp;lt; n) {
            j *= 2;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;  정답 보기 (클릭)&lt;/b&gt;&lt;/summary&gt;

&lt;h3&gt;① Big-O 분석&lt;/h3&gt;
&lt;p&gt;핵심은 &lt;strong&gt;&lt;code&gt;j *= 2&lt;/code&gt;&lt;/strong&gt; — j가 1씩 증가가 아니라 &lt;strong&gt;2배씩 커진다&lt;/strong&gt;는 점!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;j의 변화: 1 → 2 → 4 → 8 → 16 → 32 → ... → n 이상이 되면 종료&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;j = 2^k 이고, 2^k ≥ n 일 때 멈추므로 → &lt;strong&gt;k = log₂(n)번&lt;/strong&gt; 반복&lt;/p&gt;
&lt;p&gt;예시) n = 1000일 때:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1 → 2 → 4 → 8 → 16 → 32 → 64 → 128 → 256 → 512 → 1024 (종료)
→ 약 10번만에 끝! (log₂(1000) ≈ 10)&lt;/code&gt;&lt;/pre&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;루프&lt;/th&gt;
&lt;th&gt;반복 횟수&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;바깥 &lt;code&gt;for&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;n번&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;안쪽 &lt;code&gt;while&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;log₂(n)번&lt;/strong&gt; (2배씩 증가하니까)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;총 연산 = &lt;code&gt;n × log n&lt;/code&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;✅ &lt;strong&gt;정답: O(n log n)&lt;/strong&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;② j++ vs j *= 2 비교&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;코드&lt;/th&gt;
&lt;th&gt;while 반복 횟수&lt;/th&gt;
&lt;th&gt;Big-O (for문 포함)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;j++&lt;/code&gt; (1씩 증가)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;n번&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n²)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;j *= 2&lt;/code&gt; (2배씩 증가)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;log n번&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n log n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;j *= 3&lt;/code&gt; (3배씩 증가)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;log₃n번&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n log n)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  곱하기로 증가하면 &lt;strong&gt;log&lt;/strong&gt;, 더하기로 증가하면 &lt;strong&gt;선형&lt;/strong&gt;!&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;③ 시간 초과 여부&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;n = 10³, O(n log n) → 1000 × 10 = 10,000 → 10⁸보다 훨씬 작음 → ✅ 여유롭게 통과!&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  &lt;strong&gt;교훈&lt;/strong&gt;: &lt;code&gt;j *= 2&lt;/code&gt; 처럼 &lt;strong&gt;배수로 증가/감소&lt;/strong&gt;하는 패턴은 &lt;code&gt;log n&lt;/code&gt;입니다.&lt;br&gt;이것이 바로 &lt;strong&gt;이진 탐색이 O(log n)&lt;/strong&gt;인 이유와 같습니다! (탐색 범위가 절반씩 줄어들기 때문)&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/details&gt;

&lt;hr&gt;
&lt;h2&gt;  QUIZ 6 — Quiz 5의 함정 변형! j *= 2 + 바깥 선언&lt;/h2&gt;
&lt;h3&gt;  문제&lt;/h3&gt;
&lt;p&gt;Quiz 5와 거의 같은 코드인데, &lt;strong&gt;&lt;code&gt;j&lt;/code&gt;의 선언 위치&lt;/strong&gt;가 다릅니다. Big-O는?&lt;/p&gt;
&lt;p&gt;제약조건: &lt;code&gt;1 ≤ n ≤ 10³&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;int solution(int n) {
    int j = 1;                            // ⚠️ 바깥에서 선언!
    for (int i = 0; i &amp;lt; n; i++) {
        while (j &amp;lt; n) {
            j *= 2;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;&lt;b&gt;  정답 보기 (클릭)&lt;/b&gt;&lt;/summary&gt;

&lt;h3&gt;① Big-O 분석&lt;/h3&gt;
&lt;p&gt;Quiz 3→4와 같은 원리! &lt;code&gt;j&lt;/code&gt;가 for문 &lt;strong&gt;바깥&lt;/strong&gt;에 선언되어 있으므로:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;i=0일 때: j=1 → while 실행 → j가 1→2→4→8→...→n 이상 (log n번 실행)
i=1일 때: j가 이미 n 이상 → while 실행 안 됨 (0번)
i=2일 때: 실행 안 됨 (0번)
...
i=n-1일 때: 실행 안 됨 (0번)&lt;/code&gt;&lt;/pre&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;루프&lt;/th&gt;
&lt;th&gt;총 실행 횟수&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;for&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;n번&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;while&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;전체에서 딱 log n번&lt;/strong&gt; (첫 번째 i에서만)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;총 연산 = &lt;code&gt;n + log n&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Big-O: &lt;code&gt;n &amp;gt; log n&lt;/code&gt; 이므로 → &lt;strong&gt;O(n)&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;✅ &lt;strong&gt;정답: O(n)&lt;/strong&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;② Quiz 5 vs Quiz 6 비교&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;Quiz 5&lt;/th&gt;
&lt;th&gt;Quiz 6&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;j&lt;/code&gt; 선언 위치&lt;/td&gt;
&lt;td&gt;for문 &lt;strong&gt;안&lt;/strong&gt; (&lt;code&gt;int j = 1;&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;for문 &lt;strong&gt;밖&lt;/strong&gt; (&lt;code&gt;int j = 1;&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;while 실행&lt;/td&gt;
&lt;td&gt;매번 log n번&lt;/td&gt;
&lt;td&gt;전체에서 log n번 (1회만)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Big-O&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;O(n log n)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;O(n)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;③ 시간 초과 여부&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;n = 10³, O(n) → 1,000 → 10⁸보다 훨씬 작음 → ✅ 여유롭게 통과!&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  &lt;strong&gt;교훈&lt;/strong&gt;: Quiz 3→4, Quiz 5→6 모두 같은 패턴! &lt;strong&gt;변수 초기화 위치&lt;/strong&gt;가 복잡도를 결정합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/details&gt;

&lt;hr&gt;
&lt;h2&gt;  Part 1 핵심 정리&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;1. 자료구조 = 데이터를 저장/관리하는 방식 → 알고리즘 선택에 직결
2. 선형(배열, 스택, 큐, 리스트) vs 비선형(트리, 그래프, 해시)
3. 시간 복잡도 &amp;gt; 공간 복잡도 (중요도)
4. Big-O = 최고차항만, 계수 무시
5. O(1) &amp;lt; O(log n) &amp;lt; O(n) &amp;lt; O(n log n) &amp;lt; O(n²) &amp;lt; O(2ⁿ) &amp;lt; O(n!)
6. 문제의 n 범위를 보고 허용 가능한 복잡도를 역산한다!&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  &lt;strong&gt;다음 파트 예고&lt;/strong&gt;: 주요 알고리즘 패턴 (완전 탐색, 정렬, 재귀, BFS/DFS) 상세 학습&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <category>Algorithm</category>
      <author>jinnify</author>
      <guid isPermaLink="true">https://jinnify.tistory.com/100</guid>
      <comments>https://jinnify.tistory.com/100#entry100comment</comments>
      <pubDate>Sat, 21 Feb 2026 11:43:33 +0900</pubDate>
    </item>
    <item>
      <title>[Next.js] - Next.js에서 React Compiler 적용하기</title>
      <link>https://jinnify.tistory.com/99</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. React Compiler란 무엇인가?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;React Compiler&lt;/b&gt;는 React 애플리케이션의 성능을 자동으로 최적화하는&amp;nbsp;컴파일러입니다. 개발자가 수동으로 작성해야&amp;nbsp;했던&amp;nbsp;useMemo,&amp;nbsp;useCallback,&amp;nbsp;React.memo&amp;nbsp;등의 메모이제이션을 자동으로 처리해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 특징:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자동 메모이제이션: 컴포넌트와 함수를 자동으로 최적화&lt;/li&gt;
&lt;li&gt;번들 크기 감소: 불필요한 리렌더링 방지&lt;/li&gt;
&lt;li&gt;개발자 경험 향상: 수동&amp;nbsp;최적화 코드 작성 불필요&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 버전&amp;nbsp;요구사항&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;React Compiler&amp;nbsp;사용 가능 버전:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Next.js&lt;/b&gt;:&amp;nbsp;15.0.0 이상&lt;/li&gt;
&lt;li&gt;&lt;b&gt;React&lt;/b&gt;:&amp;nbsp;18.3.0 이상 (권장: 19.0.0 이상)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Node.js&lt;/b&gt;:&amp;nbsp;18.17.0 이상&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. Next.js 15로 업그레이드하는 방법&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nextjs.org/docs/app/guides/upgrading/version-15&quot;&gt;Upgrading: Version 15&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Codemod를 통한 업그레이드 방법:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;npx @next/codemod@canary upgrade latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js 15에서는&amp;nbsp;&lt;b&gt;Codemod 도구&lt;/b&gt;가 함께 제공되어 반복적이고 오류가 생기기 쉬운 코드 수정을 자동으로 처리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;수동 방법:&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1760516879510&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm i next@latest react@latest react-dom@latest eslint-config-next@latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;React Compiler 설정 방법&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nextjs.org/docs/app/api-reference/config/next-config-js/reactCompiler&quot;&gt;next.config.js: reactCompiler&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;npm install babel-plugin-react-compiler
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 next.config.mjs에 experimental.reactCompile 을 추가한다.&lt;/p&gt;
&lt;pre class=&quot;haskell&quot;&gt;&lt;code&gt;/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    reactCompiler: true,
  },
};

export default nextConfig;

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;끝!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;잘 적용되었는지 확인해보기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트로 Page 만들어서 확인해보기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ReactCompilerTestPage.tsx&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1760516978977&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;use client&quot;;

import { useState } from &quot;react&quot;;

import ReactCompilerTest from &quot;@components/ReactCompilerTest&quot;;

export default function ReactCompilerTestPage() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState(&quot;&quot;);

  const handleIncrement = () =&amp;gt; {
    setCount(prev =&amp;gt; prev + 1);
  };

  const handleNameChange = (e: React.ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
    setName(e.target.value);
  };

  return (
    &amp;lt;div className=&quot;container mx-auto p-8&quot;&amp;gt;
      &amp;lt;p&amp;gt;name: {name}&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;count: {count}&amp;lt;/p&amp;gt;

      &amp;lt;ReactCompilerTest handleIncrement={handleIncrement} handleNameChange={handleNameChange} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ReactCompilerTest.tsx&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1760517001336&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;use client&quot;;

type Props = {
  handleIncrement: () =&amp;gt; void;
  handleNameChange: (e: React.ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; void;
};

export default function ReactCompilerTest({ handleIncrement, handleNameChange }: Props) {
  console.log(&quot;ReactCompilerTest render&quot;);

  return (
    &amp;lt;div className=&quot;p-4 border rounded-lg&quot;&amp;gt;
      &amp;lt;h2 className=&quot;text-xl font-bold mb-4&quot;&amp;gt;React Compiler Test&amp;lt;/h2&amp;gt;

      &amp;lt;div className=&quot;space-y-4&quot;&amp;gt;
        &amp;lt;div&amp;gt;
          &amp;lt;label className=&quot;block text-sm font-medium mb-2&quot;&amp;gt;Name:&amp;lt;/label&amp;gt;
          &amp;lt;input
            type=&quot;text&quot;
            onChange={handleNameChange}
            className=&quot;border rounded px-3 py-2 w-full&quot;
            placeholder=&quot;Enter your name&quot;
          /&amp;gt;
        &amp;lt;/div&amp;gt;

        &amp;lt;div&amp;gt;
          &amp;lt;button onClick={handleIncrement} className=&quot;bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600&quot;&amp;gt;
            Increment
          &amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Compiler가 미 적용되어있다면:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;input을 입력하든, Increment을 누르면 ReactCompilerTest 컴포넌트가 지속적으로 렌더링 되어 ReactCompilerTest render로그가 계속해서 보여질텐데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Compiler가 잘 적용되어있다면:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 생성 빼고는 알아서 잘 최적화가 되어있어 로그가 발생하지 않는다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 방법은 개발자도구에서 Components를 선택했을때 이미지와 같이 &lt;span style=&quot;background-color: #f3c000;&quot;&gt;Memo 아이콘&lt;/span&gt;이 보이면 최적화가 잘 적용된것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-10-15 오후 5.31.17.png&quot; data-origin-width=&quot;1748&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bx6oRt/btsRaa5ypq5/Sc3SkVHfKX1KtaKVFzL7ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bx6oRt/btsRaa5ypq5/Sc3SkVHfKX1KtaKVFzL7ik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bx6oRt/btsRaa5ypq5/Sc3SkVHfKX1KtaKVFzL7ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbx6oRt%2FbtsRaa5ypq5%2FSc3SkVHfKX1KtaKVFzL7ik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1748&quot; height=&quot;318&quot; data-filename=&quot;스크린샷 2025-10-15 오후 5.31.17.png&quot; data-origin-width=&quot;1748&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Web</category>
      <category>next.js</category>
      <category>React Complier</category>
      <category>react.js</category>
      <author>jinnify</author>
      <guid isPermaLink="true">https://jinnify.tistory.com/99</guid>
      <comments>https://jinnify.tistory.com/99#entry99comment</comments>
      <pubDate>Wed, 15 Oct 2025 17:32:15 +0900</pubDate>
    </item>
    <item>
      <title>리액트 컴포넌트 성능 최적화</title>
      <link>https://jinnify.tistory.com/98</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서 제공하는 API 중 &lt;span style=&quot;color: #ee2323; background-color: #dddddd;&quot;&gt;&lt;b&gt;useMemo&lt;/b&gt;&lt;/span&gt;, &lt;span style=&quot;background-color: #dddddd; color: #ee2323;&quot;&gt;&lt;b&gt;useCallback&lt;/b&gt;&lt;/span&gt;, &lt;span style=&quot;background-color: #dddddd; color: #ee2323;&quot;&gt;&lt;b&gt;memo&lt;/b&gt;&lt;/span&gt;는 리액트에서 발생하는 렌더링을 최소화 하기 위해 최적화 기법으로 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세 가지가 모두 최적화 기법이라는것은 알지만 대부분은 정확히 언제 사용하는지에 대해서는 알아 보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 컴포넌트는 다음과 같은 상황에서 &lt;b&gt;리렌더링&lt;/b&gt;이 발생합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;전달 받은 props가 변경될 때&lt;/li&gt;
&lt;li&gt;자신의 state가 변경될 때&lt;/li&gt;
&lt;li&gt;부모 컴포넌트가 리렌더링될 때&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. React.memo&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전달 받은 props의 변경이 아닌 다른 상황(2, 3번)으로 인해 리렌더링이 되는 부분을 방지하는 함수를 말합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴포넌트를 만들고 React.memo()로 감싸주면 끝!&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1736580231821&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Item = ({ data, onClick }: { data: { id: string; name: string }; onClick: () =&amp;gt; void }) =&amp;gt; {
  return &amp;lt;div onClick={onClick}&amp;gt;{data.name}&amp;lt;/div&amp;gt;;
};

export default React.memo(Item);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Item 컴포넌트는 data, onClick이 바뀌지 않으면 리렌더링이 발생하지 않습니다.&lt;/li&gt;
&lt;li&gt;하지만 props의 data, onClick가 업데이트되는 상황이 존재하기 때문에 최적화는 여기서 끝이 아닙니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. useCallback&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;useCallback은 함수를 메모이제이션 하기 위한 Hook입니다.&lt;/li&gt;
&lt;li&gt;첫번째 인수는 함수이고, 두번째 인수는 의존 배열을 전달합니다.&lt;/li&gt;
&lt;li&gt;useCallback을 사용하면 함수가 처음 생성될때 한번 생성되며, 이후에는 동일한 함수 인스턴스를 사용하게 된다.&lt;/li&gt;
&lt;li&gt;props로 함수를 전달하는 경우 해당 함수가 변경되면 하위 컴포넌트가 리렌더링되기 때문에 useCallback으로 넘기면 리렌더링을 방지할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const List = () =&amp;gt; {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() =&amp;gt; {
    setCount(prev =&amp;gt; prev + 1);
  }, []);

  return (
    &amp;lt;div&amp;gt;
      {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(item =&amp;gt; (
        &amp;lt;Item key={item} data={{ id: item, name: `Item ${item}` }} onClick={handleClick} /&amp;gt;
      ))}
    &amp;lt;/div&amp;gt;
  );
};

const Item = ({ data, onClick }: { data: { id: number; name: string }; onClick: () =&amp;gt; void }) =&amp;gt; {
  return &amp;lt;div onClick={onClick}&amp;gt;{data.name}&amp;lt;/div&amp;gt;;
};

export default React.memo(Item);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;handleClick이 List컴포넌트에서 생성되어 Item컴포넌트에 props로 넘기고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 useCallback을 사용하지 않았다면 List가 리렌더링 될때마다 handleClick함수가 재생성됨으로 Item컴포넌트 onClick props가 변경되어 Item컴포넌트가 불필요한 리렌더링이 발생하게 됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;하위 컴포넌트에 props로 함수를 전달하는 경우는 useCallback로 감싸서 내려주면 좋다.&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. useMemo&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;useMemo는 값을 메모제이션 합니다.&lt;/li&gt;
&lt;li&gt;첫번째 인수는 함수이고, 두번째 인수는 의존 배열을 전달합니다.&lt;/li&gt;
&lt;li&gt;의존 배열의 값이 모두 같으면 함수를 실행하지 않고, 메모된 값을 반환합니다.위 코드에서, p1와 p2가 변경되지 않는 한 computeExpensiveValue 함수는 다시 호출되지 않습니다. 왜냐하면 useMemo는 디펜던시 배열을 기반으로 메모이제이션된 값을 반환하기 때문입니다.&lt;/li&gt;
&lt;li&gt;복잡한 계산을 수행하거나, 대규모 데이터를 처리하는 경우등 useMemo는 특히 렌더링 비용이 높은 컴포넌트에서 유용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1736580474655&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const memoizedVale = useMemo(() =&amp;gt; { 
  return computeExpensiveValue(p1, p2)
}, [p1, p2]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 렌더링 속도가 빠르기 때문에 모든 컴포넌트를 개발할때 최적화 작업에 대해 스트레스를 받지 않아도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만! 리스트와 관련된 컴포넌트를 만들때와 리스트 하위의 아이템 컴포넌트들이 업데이트가 자주 발생한다면 그땐 최적화가 꼭 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web</category>
      <category>React</category>
      <category>React.memo</category>
      <category>useCallback</category>
      <category>useMemo</category>
      <author>jinnify</author>
      <guid isPermaLink="true">https://jinnify.tistory.com/98</guid>
      <comments>https://jinnify.tistory.com/98#entry98comment</comments>
      <pubDate>Sat, 11 Jan 2025 16:28:34 +0900</pubDate>
    </item>
    <item>
      <title>개발자들의 버전 표기 방법 (SemVer)</title>
      <link>https://jinnify.tistory.com/97</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://semver.org/lang/ko/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;유의적 버전(Sementic Versioning, SemVer)&lt;/a&gt;을 통해 버전 표기방법을 많이 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버저닝 규칙은 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  Major.Minor.Patch-Label&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Major, Minor, Patch는 각각 숫자를 통해 나뉘게 됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;ex) 1.11.2, 1.8.0-beta&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Major&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 큰 변화를 뜻하고, 보통 이전 버전과 호환이 불가능하거나 거대한 변화가 있을때 하나 증가시킵니다.&lt;/li&gt;
&lt;li&gt;Minor와 Patch는 다시 0으로 초기화 시켜줍니다.&lt;/li&gt;
&lt;li&gt;ex) 2.8.7 &amp;rarr; 3.0.0&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Minor&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기능이 추가되었을때 숫자를 증가시킵니다.&lt;/li&gt;
&lt;li&gt;기능이 추가되었다고해서 이전 버전의 하위 호환성을 깨트리지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Patch&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버그 수정, 텍스트 변경등의 자잘한 수정을 할때 증가 시킵니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Label&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;선택사항으로 alpha, beta&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;^ 표기법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;package.json에 보면 ^표기법을 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;perl&quot;&gt;&lt;code&gt;&quot;@nestjs/cli&quot;: &quot;^10.0.0&quot;,
&quot;@nestjs/schematics&quot;: &quot;^10.0.0&quot;,
&quot;@nestjs/testing&quot;: &quot;^10.0.0&quot;,
&quot;@types/express&quot;: &quot;^5.0.0&quot;,
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;^5.0.0 &amp;rArr; 5.0.0 이상 6.0 미만의 버전&lt;/li&gt;
&lt;li&gt;^1.0.2 &amp;rArr; 1.0.2이상 2.0 미만의 버전&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>CS</category>
      <author>jinnify</author>
      <guid isPermaLink="true">https://jinnify.tistory.com/97</guid>
      <comments>https://jinnify.tistory.com/97#entry97comment</comments>
      <pubDate>Tue, 7 Jan 2025 22:06:21 +0900</pubDate>
    </item>
    <item>
      <title>[Next.js] - Pages Router 기반 ISR (+On-demand Revalidation)</title>
      <link>https://jinnify.tistory.com/96</link>
      <description>&lt;h2 id=&quot;section-0&quot; style=&quot;color: #3a4954; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; ISR(Incremental Static Regeneration, 증분 정적 재생성)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말이 다소 어렵게 보이지만 단순히 SSG방식의 응용이라 할 수 있는 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ISR은 SSG방식에 정적인 페이지를 생성하는 방법에 &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;revalidate&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;를 통해 유효기간을 설정하여 유효기간이 지난 생태에서는 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;getStaticProps()&lt;/b&gt;&lt;/span&gt;를 통해 화면을 다시 그리고 페이지를 업데이트합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-15 오후 6.21.08.png&quot; data-origin-width=&quot;1682&quot; data-origin-height=&quot;828&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJpTq7/btsJ7a40iPX/GW3EClLOHcOKIM1LtBCuu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJpTq7/btsJ7a40iPX/GW3EClLOHcOKIM1LtBCuu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJpTq7/btsJ7a40iPX/GW3EClLOHcOKIM1LtBCuu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJpTq7%2FbtsJ7a40iPX%2FGW3EClLOHcOKIM1LtBCuu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1682&quot; height=&quot;828&quot; data-filename=&quot;스크린샷 2024-10-15 오후 6.21.08.png&quot; data-origin-width=&quot;1682&quot; data-origin-height=&quot;828&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;revalidate: 60초라고 했을때 60초 이내에 새로고침을 하더라도 동일한 데이터가 보입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유통기한이 끝난 60초 이후에 새로운 데이터를 받아오게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-15 오후 6.26.31.png&quot; data-origin-width=&quot;1226&quot; data-origin-height=&quot;452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dIv7Tg/btsJ7uvf3O8/6OPt3abk5PoPOWs7ZxXXY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dIv7Tg/btsJ7uvf3O8/6OPt3abk5PoPOWs7ZxXXY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dIv7Tg/btsJ7uvf3O8/6OPt3abk5PoPOWs7ZxXXY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdIv7Tg%2FbtsJ7uvf3O8%2F6OPt3abk5PoPOWs7ZxXXY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1226&quot; height=&quot;452&quot; data-filename=&quot;스크린샷 2024-10-15 오후 6.26.31.png&quot; data-origin-width=&quot;1226&quot; data-origin-height=&quot;452&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728984981112&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const getStaticProps = async () =&amp;gt; {
  return {
    props: { },
    revalidate: 3, // 초단위로, 3초마다 재생성(재검증)
  };
};&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ISR (주문형 재 검증, On-Demand-ISR)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유효기간이 아니라 사용자의 행동에 따라 데이터가 업데이트 되는 페이지를 만들고 싶을때 사용하는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 요구(on-demand)사항이 주어졌을때 마다 페이지를 다시 생성하는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR를 사용해도 되지만 많은 트래픽이 요구되는 화면일 경우 서버 과부하등으로 인해 정적인 방법사용할 수 있으면 정적인 방법을 사용하는것이 더 좋은 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 SSG방식으로 &lt;b&gt;revalidate&lt;/b&gt;를 제거해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1728985117348&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const getStaticProps = async () =&amp;gt; {
  console.log(&quot;getStaticProps 한번만 호출되는지 확인하는 로그&quot;);

  const allBooks = await fetchBooks()

  return {
    props: {
      allBooks
    }
  };
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pages폴더 -&amp;gt; api 폴더에 &lt;b&gt;revalidate.ts&lt;/b&gt;를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;res.revalidate()를 통해서 어떤 페이지를 revalidate할지 정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1728985291723&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { NextApiRequest, NextApiResponse } from &quot;next&quot;;

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  try {
    await res.revalidate(&quot;/&quot;); // 어떤 페이지를 revalidate 할지 정함
    return res.json({ revalidated: true });
  } catch {
    return res.status(500).send(&quot;Error Revalidating&quot;);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이후 어떠한 요청 로직에 revalidate api를 호출하면 유효기간에 상관없이 바로 업데이트된 페이지를 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 조건에 따라서 업데이트되어야 하는 정적페이지로써 유지하고 싶을때 유용한 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ISR방식은 대부분의 케이스를 커버할 수 있는 강력한 사전렌더링 방식이기 때문에 많이 사용되고 있습니다.&lt;/p&gt;</description>
      <author>jinnify</author>
      <guid isPermaLink="true">https://jinnify.tistory.com/96</guid>
      <comments>https://jinnify.tistory.com/96#entry96comment</comments>
      <pubDate>Tue, 15 Oct 2024 18:45:01 +0900</pubDate>
    </item>
    <item>
      <title>[Next.js] - Pages Router 기반 SSG</title>
      <link>https://jinnify.tistory.com/95</link>
      <description>&lt;h2 id=&quot;section-0&quot; style=&quot;color: #3a4954; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;SSG(Static Site Generation, 정적 사이트 생성)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR의 단점을 해결하기 위한 사전 렌더링 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 시 API등으로 부터 데이터를 얻어, 페이지를 미리 사전 렌더링 해두는 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;SSG 빌드시 흐름&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-15 오후 4.07.21.png&quot; data-origin-width=&quot;1768&quot; data-origin-height=&quot;978&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9DPMu/btsJ44dVd0A/C6ZHJ4QE3K3k5bacXaujPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9DPMu/btsJ44dVd0A/C6ZHJ4QE3K3k5bacXaujPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9DPMu/btsJ44dVd0A/C6ZHJ4QE3K3k5bacXaujPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9DPMu%2FbtsJ44dVd0A%2FC6ZHJ4QE3K3k5bacXaujPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1768&quot; height=&quot;978&quot; data-filename=&quot;스크린샷 2024-10-15 오후 4.07.21.png&quot; data-origin-width=&quot;1768&quot; data-origin-height=&quot;978&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 시 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;getStaticProps()&lt;/b&gt;&lt;/span&gt;라는 함수가 호출되며, 그 함수 안에서 API 호출 등을 수행하고, 페이지를 그리는데 필요한 props를 반환합니다. 그 뒤 이 props를 페이지 컴포넌트에 전달해서 화면을 그립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면을 그린 결과는 정적 파일의 형태로 빌드 결과로 저장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 결과는 &lt;b&gt;.next 폴더 &amp;rarr; server 폴더&lt;/b&gt;에서 확인 가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-15 오후 3.27.08.png&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LQnJN/btsJ7n3PRQo/dlJoOJvTqkddzq4L4Dkz2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LQnJN/btsJ7n3PRQo/dlJoOJvTqkddzq4L4Dkz2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LQnJN/btsJ7n3PRQo/dlJoOJvTqkddzq4L4Dkz2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLQnJN%2FbtsJ7n3PRQo%2FdlJoOJvTqkddzq4L4Dkz2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;320&quot; height=&quot;483&quot; data-filename=&quot;스크린샷 2024-10-15 오후 3.27.08.png&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;483&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본 골격&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSG가 필요한 Page에 아래와 같은 getStaticProps() 함수를 추가해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1728976004257&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기본 골격
export const getStaticProps = async (context: GetStaticPropsContext) =&amp;gt; {
 
  return {
    props: { }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;SSG로 배포된 프로젝트 흐름&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSG로 배포된 프로젝트가 배포되어 아래와 같이 접속 요청을 했을때,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지에 접근이 발생하면, 미리 생성한 정적 파일을 클라이언트에 보내고 브라우저는 그것을 기반으로 화면을 그립니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-15 오후 4.10.34.png&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAz0G8/btsJ7NurIGY/RmDV350OGH7d95q5K3mFXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAz0G8/btsJ7NurIGY/RmDV350OGH7d95q5K3mFXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAz0G8/btsJ7NurIGY/RmDV350OGH7d95q5K3mFXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAz0G8%2FbtsJ7NurIGY%2FRmDV350OGH7d95q5K3mFXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1282&quot; height=&quot;414&quot; data-filename=&quot;스크린샷 2024-10-15 오후 4.10.34.png&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;414&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 사전 렌더링에 많은 시간이 소요되는 페이지더라도 사용자의 요청에는 매우 빠른속도로 응답 가능하다는 장점이 존재하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매번 요청이 있더라도 동일한 페이지(완성된 페이지)를 보여주기 때문에 실시간 최신 데이터 반영은 어렵다라는 단점이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;동적(Dynamic) SSG&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그의 상세 화면을 만든다고 생각해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 상세 페이지마다 존재하는 ID들이 있을것이고, 그 ID에 맞춰 동적으로 데이터를 가져와야할 것 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next서버측에서 빌드 타임에 SSG방식으로 렌더링하기 위해서는 먼저 선수 과정으로 각 페이지에 어떤 Params들이 존재할 수 있는지, 그러므로써 어떠한 경로들이 존재할 수 있는지 설정하는 과정이 반드시 필요하다 (SSG 이기 때문에!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 이 역할을 하는 함수가 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;getStaticPaths()&lt;/b&gt;&lt;/span&gt; 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본 골격&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getStaticPaths는 getStaticProps 가 실행전에 호출되는 함수로, 생성할 페이지의 경로 파라미터의 조합(path)과 폴백(fallback)을 반환합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;paths &amp;rArr; 경로 파라미터의 조합&lt;/li&gt;
&lt;li&gt;fallback &amp;rArr; 생성하는 페이지가 존재하지 않는 경우의 처리를 기술&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1728976342918&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기본 골격
export const getStaticPaths = () =&amp;gt; {
  return {
    paths: [
      { params: { ... } }
    ],
    fallback: true | false | 'blocking'
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;fallback&lt;/b&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;3가지 옵션&lt;/b&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;이 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;&amp;nbsp; -&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;false&lt;/b&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;: 없는 옵션경로로 요청시 404 에러 발생&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;&amp;nbsp; -&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;true&lt;/b&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;: 없는 옵션경로로 요청이 오면 페이지를 먼저 보여주고 getStaticProps를 실행하여 페이지를 만들어줌, isFallback이 상태로 로딩화면 추가 가능&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;&amp;nbsp; -&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&quot;blocking&quot;&lt;/b&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;: true와 비슷하지만 페이지 생성중에 fallback 페이지를 보여주지 않고(이전화면이 멈춘것처럼 보임), SSR처럼 동작해서 아무것도 미리 보여주지 않게 된다. isFallback이 상태로 로딩화면 불가&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1728976554355&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const getStaticPaths = () =&amp;gt; {
  return {
    // 리턴으로 배열의 params 객체에 문자열만 가능
    paths: [
      { params: { id: &quot;1&quot; } },
      { params: { id: &quot;2&quot; } },
      { params: { id: &quot;3&quot; } },
    ],
    // fallback(대비책)
    fallback: true,
  };
};

export const getStaticProps = async (context: GetStaticPropsContext) =&amp;gt; {
  console.log(&quot;Context&quot;, context.params!.id);

  const id = Number(context.params!.id);
  const book = await fetchOneBook(id);

  if (!book) {
    return {
      notFound: true,
    };
  }

  return {
    props: {
      book,
    },
  };
};

export default function Page({
  book,
}: InferGetStaticPropsType&amp;lt;typeof getStaticProps&amp;gt;) {
  const router = useRouter();

  if (router.isFallback) {
    return &amp;lt;div&amp;gt;로딩중...&amp;lt;/div&amp;gt;;
  }

  if (!book) {
    return &amp;lt;div&amp;gt;문제가 발생하였습니다. 다시 시도해주세요.&amp;lt;/div&amp;gt;;
  }

  const { id, title, subTitle, description, author, publisher, coverImgUrl } =
    book;

  return (
    &amp;lt;div className={style.container}&amp;gt;
	    {...}
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 실시간성 콘텐츠에는 적합하지 않고 정적 데이터를 다루는데 적합합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능이 뛰어나므로 SSR보다 SSG를 권장합니다.&lt;/p&gt;</description>
      <author>jinnify</author>
      <guid isPermaLink="true">https://jinnify.tistory.com/95</guid>
      <comments>https://jinnify.tistory.com/95#entry95comment</comments>
      <pubDate>Tue, 15 Oct 2024 16:16:11 +0900</pubDate>
    </item>
    <item>
      <title>[Next.js] - Pages Router 기반 SSR</title>
      <link>https://jinnify.tistory.com/94</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;SSR(Server Side Rendering, 서버 사이드 렌더링)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR은 가장 기본적인 사전 렌더링 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지 접근(요청)이 발생할 때마다 서버에서 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;getServerSideProps()&lt;/b&gt;&lt;/span&gt;를 호출하고 결과값을 클라이언트에 전달&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 요청이 들어올때마다 화면이 보이기 전에 사전 렌더링하는 방식&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-15 오후 1.57.40.png&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;850&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFGTio/btsJ7BHsGEa/GQ0KrjequrEAhaBhpf0EhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFGTio/btsJ7BHsGEa/GQ0KrjequrEAhaBhpf0EhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFGTio/btsJ7BHsGEa/GQ0KrjequrEAhaBhpf0EhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFGTio%2FbtsJ7BHsGEa%2FGQ0KrjequrEAhaBhpf0EhK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1844&quot; height=&quot;850&quot; data-filename=&quot;스크린샷 2024-10-15 오후 1.57.40.png&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;850&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본 골격&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;SSR이 필요한 Page에 아래와 같은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot; data-token-index=&quot;1&quot;&gt;getServerSideProps()&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;함수를 추가해 준다&lt;/p&gt;
&lt;pre id=&quot;code_1728968365995&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기본 골격
export const getServerSideProps = async () =&amp;gt; {

  return {
    props: { }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1728968469939&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;// ========================================
// SSR(Server Side Rendering) 예제
// ========================================
export const getServerSideProps = async () =&amp;gt; {
  // 컴포넌트보다 먼저 실행되어서, 컴포넌트에 필요한 데이터를 불러오는 함수
  // 사전렌더링 시 실행되는 함수이므로 한번만 실행된다
  
  const allBooks = await fetchBooks();

  return {
    props: {
      allBooks,
    },
  };
};


export default function Home({
  allBooks
}: InferGetServerSidePropsType&amp;lt;typeof getServerSideProps&amp;gt;) {
  console.log(allBooks);
  
  return (
    &amp;lt;div className={style.container}&amp;gt;
      &amp;lt;section&amp;gt;
        &amp;lt;h3&amp;gt;책 목록&amp;lt;/h3&amp;gt;
        {allBooks.map((book) =&amp;gt; (
          &amp;lt;BookItem key={book.id} {...book} /&amp;gt;
        ))}
      &amp;lt;/section&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접근 시마다 서버에서 데이터를 얻어 화면을 그리기 때문에, 항상 최신 데이터를 기반으로 페이지 초기화면을 그리는데 적합합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 백엔드 서버에서 데이터 요청이 늦어지는 경우에는 클라이언트 &amp;rarr; 브라우저까지 렌더링 흐름이 전부 늦어지는 단점이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web</category>
      <author>jinnify</author>
      <guid isPermaLink="true">https://jinnify.tistory.com/94</guid>
      <comments>https://jinnify.tistory.com/94#entry94comment</comments>
      <pubDate>Tue, 15 Oct 2024 14:02:32 +0900</pubDate>
    </item>
    <item>
      <title>일하기 좋은 카페 리스트</title>
      <link>https://jinnify.tistory.com/93</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 멋있는 분이 제보를 통해서 한번에 취합후 네이버 지도로 공유를 하고 있네요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://map.naver.com/p/favorite/myPlace/folder/3fb30df4b4734a27ab4f0f5e758b971f?c=15.00,0,0,0,dh&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://map.naver.com/p/favorite/myPlace/folder/3fb30df4b4734a27ab4f0f5e758b971f?c=15.00,0,0,0,dh&lt;/a&gt;&lt;/p&gt;</description>
      <category>일상</category>
      <author>jinnify</author>
      <guid isPermaLink="true">https://jinnify.tistory.com/93</guid>
      <comments>https://jinnify.tistory.com/93#entry93comment</comments>
      <pubDate>Tue, 8 Oct 2024 13:39:35 +0900</pubDate>
    </item>
    <item>
      <title>[TS] - 함수 오버로딩(Overloading)</title>
      <link>https://jinnify.tistory.com/92</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 들어가기에 앞서,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 아래와 같이 매개변수나 리턴타입을 &lt;b&gt;Union타입&lt;/b&gt;을 이용하여 상황에 맞게 함수를 사용하였을 겁니다.&lt;/p&gt;
&lt;pre id=&quot;code_1728108538057&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function coordinate(x: number | string, y: number | string): string | number {
  if (typeof x === &quot;string&quot; &amp;amp;&amp;amp; typeof y === &quot;string&quot;) {
    return x + y;
  } else {
    return Number(x) + Number(y);
  }
}

console.log(coordinate(1, 2));       // 3
console.log(coordinate(&quot;1&quot;, &quot;2&quot;));   // 12
console.log(coordinate(1, &quot;2&quot;));     // 3 (의도되지 않은 매개변수)
console.log(coordinate(&quot;1&quot;, 2));     // 3 (의도되지 않은 매개변수)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 타입을 잘 맞게 사용하면 되지만, 보통은 매개변수에 number면 number, string이면 string으로 구성된 매개변수를 사용할 텐데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Union타입이다보니 number 타입과 string타입을 같이 사용할 수 있는 의도치 않은 매개변수로 동작 할 수 있는 가능성이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴때 필요한 방법이 오버로딩입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;오버로딩 (Overloading)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 이름은 같지만 매개변수 또는 리턴타입의 타입이 다른 함수를 말합니다. 호출할 수 있는 함수의 타입을 미리 여러 개를 만들어두는 기법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트는 다른 언어와 다르게 &lt;b&gt;선언부&lt;/b&gt;와 &lt;b&gt;구현부&lt;/b&gt;로 나뉩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선언부&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능한 매개변수 타입만 지정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;구현부&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 선언부를 표현할 수 있어야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1728217283112&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function coordinate2(x: number, y: number): number;
function coordinate2(x: string, y: string): string;
function coordinate2(x: any, y: any): any {
  return x + y;
}

console.log(coordinate2(1, 2));
console.log(coordinate2(&quot;1&quot;, &quot;2&quot;));
console.log(coordinate2(1, &quot;2&quot;));   // 에러
console.log(coordinate2(&quot;1&quot;, 2));   // 에러&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오버로딩을 사용할때 주의할 점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트는 함수에 오버로딩이 있을 때 위에서부터 순서대로 검사합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1728220736151&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function log(a: string): void;
function log(a: number): void;
function log(a: string | number): void {
  console.log(a);
}

function error(a: string | number) {
  log(a); // 에러 발생
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;error함수의 파라미터는 string | number 인데, log함수의 param은 string 이나 number여서 에러가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애초에 오버로딩을할 필요가 없는데 오버로딩을 했다가 문제가 발생하는 경우를 봤는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Union타입을 활용할 수 있는 경우라면 먼저 Union타입을 사용 후 오버로딩을 사용하는 편이 좋습니다.&lt;/p&gt;</description>
      <category>Web</category>
      <author>jinnify</author>
      <guid isPermaLink="true">https://jinnify.tistory.com/92</guid>
      <comments>https://jinnify.tistory.com/92#entry92comment</comments>
      <pubDate>Sun, 6 Oct 2024 22:49:52 +0900</pubDate>
    </item>
    <item>
      <title>반응형 디자인을 위한 개발자 TIP</title>
      <link>https://jinnify.tistory.com/91</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. &lt;b&gt;모바일 퍼스트&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 먼저 모바일을 위한 디자인을 하고 더 큰 화면에 맞게 확장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. &lt;b&gt;유연한 그리드&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 레이아웃에 고정 단위 대신 백분율 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. &lt;b&gt;미디어 쿼리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모바일(소형)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브레이크 포인트 (최대 320px)&lt;/li&gt;
&lt;li&gt;iPhone5 시대의 스마트폰&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;모바일(중형)&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브레이크 포인트 (321px ~ 480px)&lt;/li&gt;
&lt;li&gt;대부분의 스마트폰&lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;ex) iPhone 15 Pro(393x852), iPhone 15 Pro Max(430x932),&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #363636; text-align: start;&quot;&gt;Samsung Galaxy S9+(360x740)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;모바일(대형)/태블릿&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브레이크 포인트 (481px ~ 768px)&lt;/li&gt;
&lt;li&gt;큰 스마트폰 및 소형 태블릿&lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;ex)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #363636; text-align: start;&quot;&gt;iPad Mini(&lt;span style=&quot;background-color: #ffffff; color: #363636; text-align: start;&quot;&gt;768x1024)&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;태블릿&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브레이크 포인트 (769px ~ 1024px)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;표준 태블릿&lt;br /&gt;ex)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #363636; text-align: start;&quot;&gt;iPad Pro(&lt;span style=&quot;background-color: #fafafa; color: #363636; text-align: start;&quot;&gt;1024 x 1366&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #363636; text-align: start;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데스트톱&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브레이크 포인트 (1025px 이상)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://experienceleague.adobe.com/ko/docs/target/using/experiences/vec/mobile-viewports&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://experienceleague.adobe.com/ko/docs/target/using/experiences/vec/mobile-viewports&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Web</category>
      <category>break point</category>
      <category>반응형</category>
      <category>반응형 대응</category>
      <author>jinnify</author>
      <guid isPermaLink="true">https://jinnify.tistory.com/91</guid>
      <comments>https://jinnify.tistory.com/91#entry91comment</comments>
      <pubDate>Fri, 16 Aug 2024 14:29:05 +0900</pubDate>
    </item>
  </channel>
</rss>