<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>김민석</title>
    <link>https://minseok1015.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Fri, 17 Jul 2026 15:39:40 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>민석 개발일지</managingEditor>
    <item>
      <title>CS면접 질문 - 운영체제</title>
      <link>https://minseok1015.tistory.com/32</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로세스(Process)와 스레드(Thread)의 차이는 무엇인가요?&lt;/h3&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;스레드는 프로세스 내에서 Stack만 독립적으로 갖고 나머지 메모리는 공유하는 실행 단위&lt;/li&gt;
&lt;li&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스레드는 왜 Stack만 독립적으로 가지나요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스레드는 독립적인 실행 흐름을 가지므로 함수 호출 정보를 저장하는 Stack은 각자 가져야 합니다. Code, Data, Heap은 공유해도 실행에 문제가 없고 오히려 메모리 효율과 통신 속도를 높일 수 있어 공유합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;/li&gt;
&lt;li&gt;프로세스 간 통신(IPC)은 어떻게 하나요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IPC 방법으로는 파이프, 소켓, 공유 메모리, 메시지 큐, 시그널 등이 있습니다. 공유 메모리가 가장 빠르고, 소켓은 다른 머신 간 통신도 가능합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;멀티프로세스와 멀티스레드의 장단점을 설명해주세요.&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;멀티프로세스&lt;br /&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;장점 : 독립된 메모리로 안정성이 높음&lt;/li&gt;
&lt;li&gt;단점 : Context Switching 비용과 IPC 오버헤드가 큼&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;멀티스레드
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;장점 : 메모리를 공유해 통신이 빠르고 효율적&lt;/li&gt;
&lt;li&gt;단점 : 동기화 문제와 한 스레드 오류가 전체에 영향을 줄 수 있음&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CPU 코어 수와 스레드 수는 어떤 관계인가요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코어 수만큼만 진짜 병렬 실행이 가능합니다. 스레드가 더 많으면 Context Switching으로 동시에 실행되는 것처럼 보이는 동시성입니다. CPU-bound 작업은 코어 수에 맞추고, I/O-bound 작업은 더 많은 스레드가 유리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로세스의 상태(New, Ready, Running, Waiting, Terminated)에 대해 설명해주세요.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스는 생성(New) &amp;rarr; 준비(Ready) &amp;rarr; 실행(Running) &amp;rarr; 대기(Waiting) &amp;rarr; 종료(Terminated) 상태를 가집니다.&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;Running&lt;/b&gt; 중 타임슬라이스가 끝나면 &lt;b&gt;Ready&lt;/b&gt;로 돌아가고, I/O 요청이 생기면 &lt;b&gt;Waiting&lt;/b&gt;으로 전환됩니다. &lt;b&gt;Waiting&lt;/b&gt;은 I/O가 완료되면 다시 &lt;b&gt;Ready&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Ready 상태와 Waiting 상태의 차이는 무엇인가요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Ready는 CPU만 있으면 즉시 실행 가능한 상태이고, Waiting은 I/O 완료 등 외부 이벤트를 기다리는 상태로 CPU를 줘도 실행할 수 없습니다. 이벤트가 완료되면 Waiting &amp;rarr; Ready로 전환됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;좀비 프로세스(Zombie Process)란 무엇인가요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자식 프로세스가 종료됐지만 부모가 종료 상태를 수거하지 않아 PCB가 남아있는 상태입니다. 실행은 끝났지만 프로세스 테이블 자리를 차지해 너무 많아지면 새 프로세스 생성이 불가해집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;고아 프로세스(Orphan Process)란 무엇인가요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;부모 프로세스가 먼저 종료되어 부모가 없어진 자식 프로세스입니다. Unix/Linux에서는 init 프로세스가 새 부모가 되어 종료 처리를 담당합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;CPU 스케줄링 알고리즘에는 어떤 것들이 있나요? (FCFS, SJF, Round Robin 등)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FCFS(선착순) : First Come First Served&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SJF(짧은 작업 우선) : Shortest Job First&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Round Robin(타임슬라이스 순환)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Priority Scheduling(우선순위) :&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;FCFS는 단순하지만 Convoy Effect, SJF는 효율적이지만 Starvation 문제가 있습니다. 현대 OS는 공평하고 응답이 빠른 Round Robin을 주로 사용합니다.&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;비선점형은 프로세스가 자발적으로 CPU를 놓을 때까지 기다리고, 선점형은 OS가 타임슬라이스나 우선순위에 따라 강제로 CPU를 빼앗습니다. 현대 OS는 응답성이 좋은 선점형을 주로 사용합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;교착상태(Deadlock)란 무엇이며, 발생 조건 4가지를 설명해주세요.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 상호 배제 (Mutual Exclusion)&lt;/b&gt;: 자원을 한 번에 하나의 프로세스만 사용 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 점유와 대기 (Hold and Wait)&lt;/b&gt;: 자원을 가진 채로 다른 자원을 기다림&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 비선점 (No Preemption)&lt;/b&gt;: 다른 프로세스의 자원을 강제로 빼앗을 수 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 순환 대기 (Circular Wait)&lt;/b&gt;: 프로세스들이 원형으로 서로의 자원을 기다림&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;교착상태는 프로세스들이 서로의 자원을 기다리며 무한히 대기하는 상태입니다. 상호 배제, 점유와 대기, 비선점, 순환 대기 4가지 조건이 모두 충족될 때 발생&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;하나의 조건을 깨는 &lt;b&gt;예방&lt;/b&gt;, 안전 상태만 허용하는 &lt;b&gt;회피&lt;/b&gt;, 발생 후 회복하는 방법으로 해결할 수 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Deadlock과 Starvation의 차이는?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Deadlock은 프로세스들이 서로 자원을 기다리며 모두 멈추는 상태이고, Starvation은 특정 프로세스만 자원을 계속 못 받아 혼자 못 진행하는 상태입니다. Deadlock은 순환 대기, Starvation은 불공평한 자원 배분이 원인입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;실제 OS에서는 Deadlock을 어떻게 처리하나요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대부분의 범용 OS는 Deadlock을 무시하고 발생 시 재시작에 의존합니다. 오버헤드 대비 발생 빈도가 낮기 때문입니다. DB 같은 시스템은 Deadlock을 탐지해 트랜잭션을 롤백하는 방식을 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;세마포어(Semaphore)와 뮤텍스(Mutex)의 차이는 무엇인가요?&lt;/h3&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;b&gt;세마포어 &lt;/b&gt;&amp;nbsp;카운터로 N개까지 동시 접근을 허용하며 누구든 signal()로 해제할 수 있습니다.&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스핀락(Spinlock)이란 무엇인가요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;락을 획득할 때까지 루프를 돌며 대기하는 방식입니다. 락 대기 시간이 짧을 때 Context Switching 비용을 아낄 수 있지만, 대기 시간이 길면 CPU를 낭비합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;모니터(Monitor)란 무엇인가요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공유 자원과 접근 메서드를 묶어 한 번에 하나의 스레드만 실행되도록 보장하는 고수준 동기화 도구입니다. Java의 synchronized가 모니터 기반으로 동작하며 세마포어보다 사용이 안전하고 편리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;세마포어로 Deadlock이 발생할 수 있나요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;발생할 수 있습니다. 두 프로세스가 세마포어를 하나씩 점유한 채 서로의 세마포어를 기다리면 순환 대기가 생겨 Deadlock이 됩니다. 획득 순서를 통일하거나 타임아웃으로 예방합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;Race Condition이란 무엇이며 어떻게 해결할 수 있나요?&lt;/h3&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;Race Condition은 여러 스레드가 공유 자원에 동시 접근할 때 실행 순서에 따라 결과가 달라지는 문제&lt;/li&gt;
&lt;li&gt;임계 영역에 한 번에 하나의 스레드만 접근하도록 뮤텍스, 세마포어, synchronized 키워드 등으로 해결&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;&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;임계 영역(Critical Section)의 3가지 조건은 무엇인가요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;상호 배제(Mutual Exclusion)&lt;/b&gt;: 한 번에 하나의 프로세스만 임계 영역에 진입 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;진행(Progress)&lt;/b&gt;: 임계 영역에 아무도 없으면 진입 희망 프로세스 중 하나는 반드시 진입할 수 있어야 함 (무한 대기 방지)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;유한 대기(Bounded Waiting)&lt;/b&gt;: 진입 요청 후 유한한 시간 내에 반드시 진입할 수 있어야 함 (Starvation 방지)&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;h3 data-ke-size=&quot;size23&quot;&gt;가상 메모리(Virtual Memory)는 왜 필요한가요?&lt;/h3&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;가상 메모리는 RAM이 부족할 때 디스크 일부를 메모리처럼 사용하고, 필요한 페이지만 RAM에 올리는 기술&lt;/li&gt;
&lt;li&gt;프로세스마다 독립된 가상 주소 공간을 제공해 메모리 격리도 가능하고, 실제 RAM보다 큰 프로그램 실행도 가능&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스왑 영역(Swap Space)이란 무엇인가요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RAM이 부족할 때 사용하는 디스크 공간으로, 사용하지 않는 페이지를 디스크로 내리고 필요 시 다시 올립니다. 디스크 접근이라 RAM보다 훨씬 느립니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;메모리 매핑(Memory Mapping)이란 무엇인가요?
&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;/li&gt;
&lt;li&gt;스래싱(Thrashing)이란 무엇인가요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;페이지 폴트가 너무 자주 발생해 CPU가 페이지 교체만 반복하는 상태입니다. 프로세스가 너무 많아 RAM이 부족할 때 발생하며 CPU 사용률이 오히려 급격히 떨어집니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;페이징(Paging)과 세그멘테이션(Segmentation)의 차이를 설명해주세요.&lt;/h3&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;b&gt;세그멘테이션&lt;/b&gt;은 Code, Stack 등 논리 단위로 가변 크기로 나눠 내부 단편화는 없지만 외부 단편화가 발생&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; color: #333333; text-align: start;&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;내부 단편화가 심해져 메모리 낭비가 커집니다. 반대로 너무 작으면 페이지 테이블이 커지고 페이지 폴트가 잦아집니다. 보통 4KB를 표준으로 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;/li&gt;
&lt;li&gt;세그멘테이션 폴트(Segmentation Fault)란 무엇인가요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로세스가 접근 권한이 없는 메모리 영역에 접근할 때 발생하는 오류입니다. NULL 포인터 역참조, 배열 범위 초과 등이 원인이며 OS가 프로세스를 강제 종료합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;페이지 폴트(Page Fault)가 발생하면 운영체제는 어떻게 동작하나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지 폴트는 접근하려는 페이지가 RAM에 없을 때 발생하는 인터럽트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OS가 디스크에서 해당 페이지를 RAM으로 올리고, RAM이 가득 찼다면 LRU 같은 교체 알고리즘으로 페이지를 골라 스왑아웃한 뒤 해당 명령어를 재실행&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스래싱(Thrashing)을 방지하는 방법은 무엇인가요?&lt;br /&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;/li&gt;
&lt;li&gt;페이지 폴트가 많이 발생하면 어떤 성능 문제가 생기나요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;페이지 폴트마다 디스크 I/O가 발생해 매우 느려집니다. 폴트가 잦아지면 CPU가 페이지 교체만 반복하는 스래싱으로 이어져 전체 시스템 성능이 급격히 저하됩니다.&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;&amp;nbsp;&lt;/p&gt;</description>
      <author>민석 개발일지</author>
      <guid isPermaLink="true">https://minseok1015.tistory.com/32</guid>
      <comments>https://minseok1015.tistory.com/32#entry32comment</comments>
      <pubDate>Wed, 20 May 2026 19:33:00 +0900</pubDate>
    </item>
    <item>
      <title>CS면접 질문 - 컴퓨터 구조</title>
      <link>https://minseok1015.tistory.com/31</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. CPU의 구성 요소(ALU, 제어장치, 레지스터)에 대해 설명해주세요.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU는 ALU, 제어장치, 레지스터로 구성됩니다.&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;ALU&lt;/b&gt;는 산술&amp;middot;논리 연산을 실제로 수행하고,&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;b&gt;레지스터&lt;/b&gt;는 CPU 내부의 가장 빠른 임시 저장 공간으로, PC가 다음 명령어 주소를 가리키는 등 각각 특수한 역할을 담당합니다.&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;/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;&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;레지스터, 캐시, RAM의 속도와 용량 차이를 비교해주세요.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;속도는 레지스터 &amp;gt; 캐시 &amp;gt; RAM, 용량은 그 반대입니다. 빠를수록 CPU에 가깝고 비싸서 용량이 작습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;PC(Program Counter)는 언제 업데이트되나요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;명령어를 Fetch한 직후 자동으로 다음 주소로 증가하고, jump나 인터럽트 발생 시 특정 주소로 덮어씌워집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ALU가 연산 결과를 어디에 저장하나요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;범용 레지스터에 저장하고, 연산 결과의 상태(오버플로우, 0 여부 등)는 플래그 레지스터에 저장됩니다.&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;&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;2. 프로세스가 실행될 때 메모리에는 어떤 영역(Code, Data, Heap, Stack)이 생성되나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스가 실행되면 Code, Data, Heap, Stack 4가지 영역이 생성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Code&lt;/b&gt;는 실행 명령어, &lt;b&gt;Data&lt;/b&gt;는 전역&amp;middot;정적 변수, &lt;b&gt;Heap&lt;/b&gt;은 런타임 동적 할당 영역, &lt;b&gt;Stack&lt;/b&gt;은 함수 호출 시 지역변수와 리턴 주소를 저장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Heap은 위로, Stack은 아래로 자라며 서로 침범하면 overflow가 발생합니다.&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;꼬리질문&lt;/h4&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;전역 변수는 Data 영역에, 지역 변수는 Stack 영역에 저장됩니다. 전역 변수 중 초기화된 것은 Data, 초기화되지 않은 것은 BSS 영역에 들어갑니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;메모리 누수(Memory Leak)는 어느 영역에서 주로 발생하나요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Heap 영역에서 발생합니다. 동적 할당 후 해제하지 않거나 참조를 계속 유지하면 메모리가 반환되지 않아 누수가 생깁니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Java와 C에서 메모리 관리 방식이 어떻게 다른가요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C는 개발자가 직접 할당&amp;middot;해제를 관리하고, Java는 GC가 참조가 끊긴 객체를 자동으로 해제합니다. Java가 편리하지만 GC 실행 타이밍을 제어하기 어렵다는 단점이 있습니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;3. Stack 영역과 Heap 영역의 차이점은 무엇인가요?&lt;/h3&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;Stack&lt;/b&gt;은 컴파일러가 자동 관리하며 함수 호출 시 지역변수와 리턴 주소를 LIFO 구조로 빠르게 저장&amp;middot;해제합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Heap&lt;/b&gt;은 런타임에 개발자가 동적으로 할당하는 영역으로, 크기가 유연하지만 속도가 느리고 메모리 누수 위험이 있습니다.&lt;/p&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;꼬리질문&lt;/h4&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;Stack Overflow는 왜 발생하나요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Stack 영역은 크기가 제한되어 있어서, 재귀 함수처럼 함수 호출이 너무 깊어져 스택 프레임이 한계를 초과하면 Stack Overflow가 발생합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;메모리 단편화(Fragmentation)란 무엇인가요?&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리 할당&amp;middot;해제를 반복하면서 중간중간 작은 빈 공간이 흩어져, 총 여유 공간은 충분해도 연속된 공간이 없어 큰 메모리를 할당하지 못하는 현상입니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 캐시 메모리(Cache Memory)는 왜 사용하며, 지역성(Locality)이란 무엇인가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시 메모리는 CPU와 RAM의 속도 차이를 줄이기 위한 고속 임시 저장 장치입니다.&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: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;시간적 지역성&lt;/b&gt;은 최근 접근 데이터를 곧 다시 쓸 가능성이 높다는 것&lt;/li&gt;
&lt;li&gt;&lt;b&gt;공간적 지역성&lt;/b&gt;은 인접 주소 데이터도 곧 접근할 가능성이 높다는 원리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;꼬리질문&lt;/h4&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;캐시 라인(Cache Line)이란 무엇인가요?&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐시가 메모리에서 데이터를 가져오는 최소 단위(보통 64바이트)입니다. 한 번에 인접 데이터를 묶어 올려 공간적 지역성을 활용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;/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;h3 data-ke-size=&quot;size23&quot;&gt;5. 캐시 히트(Cache Hit)와 캐시 미스(Cache Miss)는 무엇인가요?&lt;/h3&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;b&gt;Cache Hit&lt;/b&gt;는 CPU가 필요한 데이터를 캐시에서 찾은 경우&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Cache Miss&lt;/b&gt;는 캐시에 없어 RAM까지 접근해야 하는 경우&lt;/li&gt;
&lt;li&gt;미스가 발생하면 &lt;u&gt;데이터를 캐시 라인 단위로 올려&lt;/u&gt; 이후 접근 속도를 높임&lt;/li&gt;
&lt;li&gt;히트율을 높이려면 &lt;u&gt;지역성 원리를 고려한 코드 작성이 중요&lt;/u&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;꼬리질문&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐시 교체 정책(LRU, FIFO 등)에 대해 설명해주세요.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐시가 꽉 찼을 때 어떤 데이터를 내보낼지 결정하는 정책입니다. 가장 오래 사용 안 된 것을 교체하는 LRU가 시간적 지역성을 잘 반영해 가장 널리 쓰입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이중 for문에서 행 우선 vs 열 우선 접근이 성능에 미치는 영향은?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2차원 배열은 행 단위로 연속 저장되므로 행 우선 접근이 캐시 히트율이 높아 성능이 좋습니다. 열 우선 접근은 매번 캐시 미스가 발생해 느립니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. CPU 스케줄링에서 Context Switching이란 무엇이며, 왜 비용이 발생하나요?&lt;/h3&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;Context Switching 과정&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;인터럽트 또는 시스템 콜 발생&lt;/li&gt;
&lt;li&gt;현재 프로세스의 Context를 PCB에 저장&lt;/li&gt;
&lt;li&gt;스케줄러가 다음 실행할 프로세스 선택&lt;/li&gt;
&lt;li&gt;선택된 프로세스의 Context를 PCB에서 복원&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Context Switching&lt;/b&gt;은 CPU가 실행 중인 프로세스를 바꿀 때 현재 상태를 PCB에 저장하고 다음 프로세스 상태를 복원하는 작업&lt;/li&gt;
&lt;li&gt;PCB 저장&amp;middot;복원 시간과 함께 이전 프로세스가 올려놓은 캐시가 무효화되어 캐시 미스가 증가하기 때문에 비용이 발생&lt;/li&gt;
&lt;li&gt;스레드 전환은 메모리를 공유하므로 이보다 비용이 적음&lt;/li&gt;
&lt;/ul&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;꼬리질문&lt;/h4&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;Context Switching을 줄이는 방법은 무엇인가요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로세스 대신 스레드를 사용하거나, OS 개입 없이 전환하는 코루틴을 활용하는 방법이 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;프로세스와 스레드의 Context Switching 비용 차이는 왜 생기나요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로세스 전환은 메모리 공간이 바뀌어 페이지 테이블과 TLB까지 교체해야 하지만, 스레드는 메모리를 공유해 레지스터 상태만 교체하면 되므로 비용이 더 적습니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. 인터럽트(Interrupt)란 무엇이고, 폴링(Polling)과 어떤 차이가 있나요?&lt;/h3&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;b&gt;인터럽트&lt;/b&gt;는 이벤트 발생 시 장치가 &lt;u&gt;CPU에 신호를 보내 ISR을 실행&lt;/u&gt;하는 방식&lt;/li&gt;
&lt;li&gt;&lt;b&gt;폴링&lt;/b&gt;은 CPU가 &lt;u&gt;장치 상태를 주기적으로 직접 확인&lt;/u&gt;하는 방식&lt;/li&gt;
&lt;li&gt;인터럽트는 CPU 효율이 높고 반응이 빠르지만 구현이 복잡하며, 폴링은 단순하지만 CPU 자원을 낭비&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인터럽트 발생 시 CPU는 어떤 순서로 처리하나요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 상태를 저장 &amp;rarr; 인터럽트 벡터에서 ISR 주소 확인 &amp;rarr; ISR 실행 &amp;rarr; 원래 상태 복원 순서로 처리됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;/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;h3 data-ke-size=&quot;size23&quot;&gt;8. 시스템 콜(System Call)이란 무엇이며 왜 필요한가요?&lt;/h3&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;시스템 콜은 유저 모드 프로그램이 파일 I/O, 네트워크 등 커널 기능을 사용하기 위해 OS에 요청하는 인터페이스&lt;/li&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;&amp;nbsp;&lt;/p&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;꼬리질문&lt;/h4&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;모든 프로그램이 하드웨어에 직접 접근하면 보안과 안정성이 무너지기 때문입니다. 유저 모드를 분리해 OS를 통해서만 자원에 접근하게 강제합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;fork()와 exec()의 차이는 무엇인가요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fork()는 현재 프로세스를 복사해 자식 프로세스를 생성하고, exec()는 현재 프로세스를 새 프로그램으로 교체합니다. 보통 fork() 후 자식에서 exec()를 호출하는 패턴으로 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9. 컴파일 과정과 실행 과정을 설명해주세요. (전처리 &amp;rarr; 컴파일 &amp;rarr; 어셈블 &amp;rarr; 링크 등)&lt;/h3&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;1단계: 전처리 (Preprocessing)&lt;/b&gt; #include, #define, #ifdef 등 전처리 지시어를 처리합니다. 헤더 파일 내용을 소스에 삽입하고, 매크로를 치환합니다. 결과물: .i 파일 (확장된 C 소스)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2단계: 컴파일 (Compilation)&lt;/b&gt; 전처리된 소스 코드를 &lt;b&gt;어셈블리 코드&lt;/b&gt;로 변환합니다. 문법 검사, 타입 체크, 최적화 등이 이뤄집니다. 결과물: .s 파일 (어셈블리 코드)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3단계: 어셈블 (Assembly)&lt;/b&gt; 어셈블리 코드를 &lt;b&gt;기계어(오브젝트 파일)&lt;/b&gt;로 변환합니다. 결과물: .o 파일 (오브젝트 파일). 아직 다른 파일의 심볼(함수, 변수)은 미완성 상태.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4단계: 링킹 (Linking)&lt;/b&gt; 여러 오브젝트 파일과 라이브러리를 합쳐 하나의 실행 파일을 만듭니다. 미완성 심볼들의 주소를 연결(심볼 해석)합니다. 결과물: 실행 파일(.exe, ELF 등)&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;정적 링킹&lt;/b&gt;: 라이브러리 코드를 실행 파일에 포함 &amp;rarr; 파일 크기 큼, 독립 실행 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;동적 링킹&lt;/b&gt;: 런타임에 공유 라이브러리(.dll, .so)를 참조 &amp;rarr; 파일 작음, 라이브러리 필요&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;컴파일 과정은 &lt;u&gt;전처리 &amp;rarr; 컴파일 &amp;rarr; 어셈블 &amp;rarr; 링킹&lt;/u&gt; 순입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전처리는 헤더 삽입&amp;middot;매크로 치환&lt;/li&gt;
&lt;li&gt;컴파일은 어셈블리 코드 변환&lt;/li&gt;
&lt;li&gt;어셈블은 기계어 오브젝트 파일 생성&lt;/li&gt;
&lt;li&gt;링킹은 여러 오브젝트 파일을 합쳐 최종 실행 파일을 만드는 단계&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;꼬리질문&lt;/h4&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;정적 링킹은 라이브러리를 실행 파일에 포함시켜 독립 실행이 가능하지만 파일이 크고, 동적 링킹은 런타임에 공유 라이브러리를 참조해 파일이 작지만 라이브러리 의존성이 생깁니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;인터프리터 언어(Python, JS)는 컴파일 언어와 어떻게 다른가요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴파일 언어는 실행 전 전체를 기계어로 변환해 빠르고, 인터프리터 언어는 실행 시 한 줄씩 해석해 느리지만 플랫폼 독립적입니다. Java는 바이트코드 + JVM + JIT로 중간 형태입니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;10. 동기식(Synchronous)과 비동기식(Asynchronous)의 차이를 설명해주세요.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기는 작업 완료를 기다린 후 다음을 진행하고, 비동기는 완료를 기다리지 않고 다음 작업을 진행하다가 완료 시 콜백 등으로 결과를 받는 방식입니다. 비동기는 I/O 대기 시간에 CPU를 다른 작업에 활용할 수 있어 처리량이 높습니다.&lt;/p&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;꼬리질문&lt;/h4&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;코드 흐름이 비선형적이라 가독성과 디버깅이 어렵고, 콜백 중첩으로 콜백 지옥이 생길 수 있습니다. 또한 실행 순서가 보장되지 않아 경쟁 조건 같은 동시성 문제도 발생할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;11. Blocking과 Non-Blocking의 차이를 설명해주세요.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Blocking/Non-Blocking은 &lt;b&gt;호출된 함수가 호출한 함수의 제어권을 가져가느냐&lt;/b&gt;의 문제입니다. 동기/비동기와 혼동하기 쉬우므로 구분이 중요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Blocking&lt;/b&gt; 함수를 호출하면 &lt;b&gt;제어권이 호출된 함수로 넘어가고&lt;/b&gt;, 작업이 완료될 때까지 호출한 쪽은 아무것도 못 합니다. 결과가 반환될 때 제어권이 돌아옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Non-Blocking&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;동기/비동기 vs Blocking/Non-Blocking 조합&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Blocking&lt;/td&gt;
&lt;td&gt;Non-Blocking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Synchronous&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;결과 기다리며 대기 (일반적인 함수 호출)&lt;/td&gt;
&lt;td&gt;결과 날 때까지 반복 확인 (Busy-wait)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Asynchronous&lt;/b&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;/div&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;### 동기/비동기 vs Blocking/Non-Blocking&lt;br /&gt;&lt;br /&gt;&lt;/h3&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;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;Blocking/Non-Blocking&lt;/b&gt;은 함수를 호출했을 때 &lt;b&gt;제어권, 즉 다음 코드를 실행할 권리를 언제 돌려받느냐&lt;/b&gt;의 문제입니다. Blocking은 호출한 함수가 완료될 때까지 제어권을 넘겨줘 호출한 쪽이 대기하고, Non-Blocking은 즉시 제어권을 돌려줘 호출한 쪽이 계속 진행할 수 있습니다.&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>민석 개발일지</author>
      <guid isPermaLink="true">https://minseok1015.tistory.com/31</guid>
      <comments>https://minseok1015.tistory.com/31#entry31comment</comments>
      <pubDate>Wed, 20 May 2026 19:02:37 +0900</pubDate>
    </item>
    <item>
      <title>CS면접 질문 - 네트워크</title>
      <link>https://minseok1015.tistory.com/30</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q1. OSI 7계층에 대해 각 계층의 역할과 함께 설명해주세요.&lt;/h3&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;OSI 7계층은 네트워크 통신을 표준화한 &lt;b&gt;참조 모델&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;실제 구현에서는 TCP/IP 4계층으로 단순화되며, OSI는 이론적 참조 모델로 주로 사용
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;1계층&lt;/b&gt; 물리는 &lt;u&gt;비트를 전기 신호로 전송&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;2계층&lt;/b&gt; 데이터 링크는 &lt;u&gt;MAC 주소로 같은 네트워크 내 전달을 담당&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;3계층&lt;/b&gt; 네트워크는 &lt;u&gt;IP 주소로 경로를 결정(라우팅)&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;4계층&lt;/b&gt; 전송은 &lt;u&gt;TCP/UDP로 포트 기반의 신뢰성 있는 종단 간 통신을 담당&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;5계층&lt;/b&gt; 세션은 &lt;u&gt;연결 세션의 수립과 유지&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;6계층&lt;/b&gt; 표현은 &lt;u&gt;암호화&amp;middot;압축&amp;middot;인코딩&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt; 7계층&lt;/b&gt; 응용은 &lt;u&gt;HTTP, FTP 등 사용자와 맞닿는 서비스 프로토콜을 제공&lt;/u&gt;&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;b&gt;Q. 4계층(전송)과 3계층(네트워크)의 차이가 헷갈리는데요?&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3계층은 &lt;b&gt;다른 네트워크까지&lt;/b&gt; 어떤 경로로 갈지(IP 기반 라우팅), 4계층은 &lt;b&gt;목적지 호스트 내 어떤 프로세스&lt;/b&gt;에게 줄지(포트 기반)를 담당합니다. IP가 집 주소라면, 포트는 그 집의 몇 호실인지에 해당합니다.&lt;/p&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;&lt;b&gt;Q. SSL/TLS는 몇 계층인가요?&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OSI 기준으로는 6계층(표현 계층)에 해당하지만, TCP/IP 모델에서는 4계층(전송)과 7계층(응용) 사이에서 동작하는 것으로 봅니다. 계층 구분이 모델마다 다르기 때문에 &quot;응용 계층과 전송 계층 사이에서 동작한다&quot;고 설명하는 게 더 정확합니다.&lt;/p&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;h3 data-ke-size=&quot;size23&quot;&gt;Q2. OSI 7계층과 TCP/IP 4계층의 차이점은 무엇인가요?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OSI는 ISO가 만든&amp;nbsp;이론적 참조 모델이고, TCP/IP는 실제 인터넷 구현에 사용되는&amp;nbsp;실용적 모델&lt;/li&gt;
&lt;li&gt;가장 큰 차이는 계층 수
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OSI는 7계층인데, TCP/IP는 4계층으로 단순화&lt;/li&gt;
&lt;li&gt;OSI의 &lt;u&gt;세션&amp;middot;표현&amp;middot;응용&lt;/u&gt; 3계층이 TCP/IP에서는 &lt;u&gt;응용 계층 하나&lt;/u&gt;로 통합&lt;/li&gt;
&lt;li&gt;OSI의 &lt;u&gt;물리&amp;middot;데이터링크&lt;/u&gt; 계층이 &lt;u&gt;네트워크 접근 계층&lt;/u&gt;으로 합쳐집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;현재 인터넷은 TCP/IP 모델로 동작, OSI는 네트워크 문제를 계층별로 진단하거나 개념을 설명하는&amp;nbsp;공통 언어로 여전히 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q3. 캡슐화(Encapsulation)와 역캡슐화(Decapsulation) 과정을 설명해주세요.&lt;/h3&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;&lt;b&gt;전송 계층&lt;/b&gt;은 &lt;u&gt;포트 번호가 담긴 TCP 헤더&lt;/u&gt;를&lt;/li&gt;
&lt;li&gt;&lt;b&gt;네트워크 계층&lt;/b&gt;은 &lt;u&gt;IP 헤더&lt;/u&gt;를&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터링크 계층&lt;/b&gt;은 &lt;u&gt;MAC 헤더&lt;/u&gt;를 추가하고&lt;/li&gt;
&lt;li&gt;&lt;b&gt;물리 계층&lt;/b&gt;에서 최종적으로 비트 신호로 변환해 전송&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;역캡슐화는 수신 측에서 반대로 하위에서 상위 계층으로 올라가며 각 계층이 자신의 헤더를 확인하고 제거하는 과정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 계층은 헤더 정보를 보고 &lt;u&gt;자신에게 온 데이터가 맞는지 확인한 뒤 상위로 넘김&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;중간 장비도 자신이 동작하는 계층까지만 역캡슐화합니다. 스위치는 2계층까지, 라우터는 3계층까지 열어봄&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;h3 data-ke-size=&quot;size23&quot;&gt;Q4. 네트워크 장비 중 허브, 스위치, 라우터, L7 스위치의 차이를 설명해주세요.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네 장비는 OSI 계층 기준으로 구분&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;허브&lt;/b&gt;는 1계층 장비로 &lt;u&gt;들어온 신호를 모든 포트에 그대로 복사해 전달&lt;/u&gt;하며, 충돌이 발생할 수 있어 현재는 거의 사용하지 않음.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스위치&lt;/b&gt;는 2계층 장비로 &lt;u&gt;MAC 주소 테이블을 학습해 목적지 포트에만 데이터를 전달&lt;/u&gt;하며 &lt;b&gt;LAN 구성에 사용&lt;/b&gt;됨.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;라우터&lt;/b&gt;는 3계층 장비로 &lt;u&gt;IP 주소와 라우팅 테이블을 기반으로 서로 다른 네트워크 간 패킷을 전달&lt;/u&gt;하며 &lt;b&gt;인터넷 연결에 사용&lt;/b&gt;됨.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;L7 스위치&lt;/b&gt;는 7계층까지 &lt;u&gt;패킷을 열어 URL, 쿠키, HTTP 헤더 등 콘텐츠 내용을 보고 트래픽을 분산&lt;/u&gt;하는 &lt;b&gt;로드밸런서로 사용&lt;/b&gt;됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q5. TCP와 UDP의 차이점을 설명하고, 각각 어떤 상황에 사용하는 것이 적합한지 말해주세요.&lt;/h3&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;b&gt;TCP&lt;/b&gt;는 &lt;u&gt;연결 지향 프로토콜&lt;/u&gt;로 &lt;u&gt;3-way handshake로 연결을 수립&lt;/u&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;TCP는 정확성이 중요한 웹 브라우징, 파일 전송, 이메일에 적합&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;UDP&lt;/b&gt;는 &lt;u&gt;비연결 프로토콜&lt;/u&gt;로 &lt;u&gt;연결 수립 없이 데이터를 바로 전송&lt;/u&gt;하며 &lt;u&gt;손실돼도 재전송하지 않아&lt;/u&gt; 빠른 속도가 핵심
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UDP는 약간의 손실보다 지연이 더 치명적인 실시간 스트리밍, 온라인 게임, VoIP, DNS 조회에 적합&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;h3 data-ke-size=&quot;size23&quot;&gt;Q6. TCP의 3-way Handshake와 4-way Handshake 과정을 설명해주세요. 왜 4-way에서는 한 단계가 더 필요할까요?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;3-way Handshake는 TCP 연결 수립 과정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;클라이언트&lt;/b&gt;가 &lt;u&gt;SYN&lt;/u&gt;을 보냄&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서버&lt;/b&gt;가 &lt;u&gt;SYN&lt;/u&gt;+&lt;u&gt;ACK&lt;/u&gt;로 응답&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클라이언트&lt;/b&gt;가 &lt;u&gt;ACK&lt;/u&gt;를 보내면 연결이 수립&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;4-way Handshake는 연결 종료 과정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FIN &amp;rarr; ACK &amp;rarr; FIN &amp;rarr; ACK 4단계로 이루어짐.&lt;/li&gt;
&lt;li&gt;종료가 4단계인 이유는, &lt;u&gt;한쪽이 FIN을 보내도 상대방이 아직 보낼 데이터가 남아있을 수 있기 때문&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;서버는 클라이언트의 FIN에 ACK만 먼저 보내고, 남은 데이터를 다 전송한 뒤에 FIN을 보냄&lt;/li&gt;
&lt;li&gt;ACK와 FIN을 분리해야 하므로 3-way보다 한 단계가 더 필요함&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;&lt;b&gt;Q. TIME_WAIT 상태가 왜 필요한가요?&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 마지막 ACK를 보낸 뒤 바로 종료하면, 그 ACK가 유실됐을 때 서버가 FIN을 재전송해도 받아줄 주체가 없습니다. TIME_WAIT 동안 대기하면서 서버의 재전송 FIN을 받아줄 수 있습니다.&lt;/p&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;h3 data-ke-size=&quot;size23&quot;&gt;Q7. TCP의 흐름 제어(Flow Control)와 혼잡 제어(Congestion Control)의 차이를 설명해주세요.&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;흐름 제어&lt;/b&gt;는 &lt;u&gt;송신자와 수신자 간의 속도 차이를 조절&lt;/u&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;/li&gt;
&lt;li&gt;&lt;b&gt;혼잡 제어&lt;/b&gt;는 &lt;u&gt;송신자와 네트워크 간의 부하를 조절&lt;/u&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;u&gt;지수적&lt;/u&gt;으로 증가시키다가 임계값 이후엔 &lt;u&gt;선형적&lt;/u&gt;으로 증가시키고, 패킷 손실이 감지되면 &lt;u&gt;전송량을 줄임&lt;/u&gt;.&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;h3 data-ke-size=&quot;size23&quot;&gt;Q8. TIME_WAIT 상태가 무엇이며, 왜 필요한가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TIME_WAIT은 TCP 4-way Handshake에서 &lt;b&gt;능동적으로 연결을 종료한 쪽&lt;/b&gt;이 마지막 ACK를 보낸 후 바로 종료하지 않고 &lt;b&gt;2MSL 시간 동안 대기하는 상태&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;첫째, 마지막 ACK가 유실됐을 때 상대방이 FIN을 재전송하면 받아서 ACK를 다시 보낼 수 있어야 하기 때문&lt;/li&gt;
&lt;li&gt;둘째, 이전 연결의 지연 패킷이 새 연결에 섞이는 것을 방지하기 위해 네트워크에서 모든 패킷이 소멸될 때까지 기다리는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 대규모 서버 환경에서는 TIME_WAIT 소켓이 대량 누적되어 &lt;u&gt;포트 고갈로 이어질 수 있어&lt;/u&gt;, &lt;b&gt;Keep-Alive&lt;/b&gt;로 &lt;u&gt;연결을 재사용&lt;/u&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;Keep-Alive 사용&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;HTTP Keep-Alive로 연결을 재사용
&amp;rarr; 매 요청마다 연결/종료를 반복하지 않음
&amp;rarr; TIME_WAIT 소켓 수 감소

[연결 1번으로 여러 요청 처리]
──연결──▶ 요청1 &amp;rarr; 응답1 &amp;rarr; 요청2 &amp;rarr; 응답2 &amp;rarr; 요청3 &amp;rarr; 응답3 ──종료──&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q9. HTTP와 HTTPS의 차이점은 무엇이며, HTTPS는 어떻게 보안을 제공하나요?&lt;/h3&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;b&gt;HTTP&lt;/b&gt;는 &lt;u&gt;데이터를 평문으로 전송해서 도청과 변조에 취약&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;HTTPS는 &lt;u&gt;HTTP에 TLS 암호화 계층을 추가&lt;/u&gt;해 기밀성, 무결성, 인증을 보장&lt;/li&gt;
&lt;li&gt;HTTPS의 보안 동작 방식
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;먼저 &lt;b&gt;TLS Handshake&lt;/b&gt;에서 서버가 CA가 서명한 인증서를 전달하면 클라이언트가 이를 검증해 &lt;u&gt;서버의 신원을 확인&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;이후 &lt;u&gt;비대칭키로 Session Key를 안전하게 교환&lt;/u&gt;하고, 실제 데이터는 이 &lt;u&gt;대칭키로 암호화해서 주고받음&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비대칭키&lt;/b&gt;로 &lt;u&gt;키 교환 문제를 해결&lt;/u&gt;하고, &lt;b&gt;대칭키&lt;/b&gt;로 &lt;u&gt;빠른 암호화 통신&lt;/u&gt;을 하는 방식&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;&lt;b&gt;Q. HTTPS인데도 보안이 뚫리는 경우가 있나요?&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네, 몇 가지 경우가 있습니다. 중간자 공격(MITM)으로 가짜 인증서를 심거나, SSL Stripping으로 HTTPS를 HTTP로 다운그레이드시키거나, 인증기관(CA) 자체가 해킹당하는 경우입니다. 또한 HTTPS는 전송 구간만 암호화하므로, 서버에 도달한 후의 데이터 보안은 별개의 문제입니다.&lt;/p&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;h3 data-ke-size=&quot;size23&quot;&gt;Q10. HTTP 메서드(GET, POST, PUT, PATCH, DELETE)의 차이를 설명해주세요.&lt;/h3&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;HTTP 메서드는 리소스에 대한 행위를 정의
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;GET&lt;/b&gt;은 데이터 조회&lt;/li&gt;
&lt;li&gt;&lt;b&gt;POST&lt;/b&gt;는 생성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PUT&lt;/b&gt;은 전체 수정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PATCH&lt;/b&gt;는 부분 수정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DELETE&lt;/b&gt;는 삭제에 사용&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;&lt;b&gt;GET과 POST의 차이&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;GET&lt;/b&gt;은 데이터를 URL 쿼리스트링에 담아 &lt;u&gt;캐시 가능하고 멱등&lt;/u&gt;하지만 &lt;u&gt;URL에 노출&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;POST&lt;/b&gt;는 바디에 담아 &lt;u&gt;URL에 노출되지 않으며&lt;/u&gt; 호출마다 새 리소스를 생성하므로 &lt;u&gt;멱등하지 않음&lt;/u&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;PUT과 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;b&gt;PUT&lt;/b&gt;은 리소스 전체를 교체해서 보내지 않은 필드가 사라짐&lt;/li&gt;
&lt;li&gt;&lt;b&gt; PATCH&lt;/b&gt;는 보낸 필드만 수정해서 나머지는 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부 필드만 수정할 때는 PATCH가 더 적합함&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;Q. GET 요청에도 바디를 넣을 수 있나요?&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술적으로는 가능하지만 권장하지 않습니다. HTTP 스펙상 금지는 아니지만, 많은 서버와 프록시가 GET 바디를 무시하거나 거부합니다. Elasticsearch처럼 GET에 바디를 쓰는 예외 케이스도 있지만, 표준적인 REST API에서는 GET 바디 사용을 피하는 것이 좋습니다.&lt;/p&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;&lt;b&gt;Q. POST로 조회, PUT으로 삭제하면 안 되나요?&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술적으로 동작은 하지만 REST 설계 원칙에 어긋납니다. HTTP 메서드의 의미론(Semantics)을 지키는 것이 API의 가독성, 유지보수성, 캐시/프록시 동작에 중요합니다. 특히 캐시 서버나 미들웨어가 메서드를 기준으로 동작을 결정하므로, 메서드를 잘못 사용하면 예상치 못한 동작이 발생할 수 있습니다.&lt;/p&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q11. HTTP 상태 코드 1xx ~ 5xx의 의미를 설명하고, 자주 보는 코드를 말해주세요.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 상태 코드는 1xx 정보성, 2xx 성공, 3xx 리다이렉션, 4xx 클라이언트 오류, 5xx 서버 오류로 구분&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;200(OK)&lt;/b&gt; 성공&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;201(Created)&lt;/b&gt; 리소스 생성 성공&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;204(No Content)&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;301(Moved Permanently)&lt;/b&gt; 영구 이동&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;304(Not Modified)&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;400(Bad Request)&lt;/b&gt; 잘못된 요청&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;401(Unauthorized)&lt;/b&gt; 인증 필요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;403(Forbidden)&lt;/b&gt; 권한 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;404(Not Found)&lt;/b&gt; 리소스 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;409(Conflict)&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;500(Internal Server Error)&lt;/b&gt; 서버 내부 오류&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;502(Bad Gateway&lt;/b&gt; 게이트웨이 오류&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;503(Service Unavailable)&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;401과 403의 차이 :&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;401은 로그인 자체가 안 된 상태고 403은 로그인은 됐지만 해당 리소스에 접근 권한이 없는 상태&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;Q. 401과 403을 실무에서 어떻게 구분해서 쓰나요?&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;401은 JWT 토큰이 없거나 만료된 경우, 403은 토큰은 유효하지만 해당 리소스에 접근 권한이 없는 경우에 씁니다. 다만 보안상 공격자에게 리소스 존재 여부를 숨기기 위해 둘 다 404로 통일하거나, 403 대신 404를 반환하는 경우도 있습니다.&lt;/p&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;&lt;b&gt;Q. 500 에러가 났을 때 상세 에러 메시지를 클라이언트에 보여줘도 되나요?&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안 됩니다. 스택 트레이스나 DB 오류 메시지를 그대로 노출하면 내부 구조, DB 종류, 파일 경로 등이 공격자에게 노출되어 보안 취약점이 됩니다. 클라이언트에는 &quot;서버 오류가 발생했습니다&quot; 같은 일반 메시지만 보내고, 상세 로그는 서버 내부에만 기록해야 합니다.&lt;/p&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;&lt;b&gt;Q. REST API 설계 시 어떤 상태 코드를 써야 할지 헷갈릴 때 기준이 있나요?&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 원칙은 &quot;HTTP 스펙의 의미에 맞게 쓰기&quot;입니다. 성공은 2xx, 클라이언트 문제는 4xx, 서버 문제는 5xx를 씁니다. 자주 쓰는 패턴은 조회 성공&amp;rarr;200, 생성&amp;rarr;201, 삭제&amp;rarr;204, 유효성 실패&amp;rarr;400, 인증&amp;rarr;401, 권한&amp;rarr;403, 없음&amp;rarr;404, 중복&amp;rarr;409입니다. 모든 응답을 200으로 통일하고 바디에 성공/실패를 담는 방식은 HTTP 의미론을 무시하는 안티패턴입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q12. HTTP/1.1, HTTP/2, HTTP/3의 주요 차이점은 무엇인가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HTTP/1.1&lt;/b&gt;은 &lt;u&gt;텍스트 기반으로 Keep-Alive로 연결을 재사용&lt;/u&gt;하지만, 요청을 순차 처리해서 앞 요청이 느리면 뒤 요청이 모두 대기하는 &lt;u&gt;HOL Blockin&lt;/u&gt;g이 발생하고 헤더를 매번 중복 전송하는 문제가 있음.&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;HTTP/2&lt;/b&gt;는 &lt;u&gt;바이너리 기반으로 &lt;b&gt;멀티플렉싱&lt;/b&gt;으로 하나의 TCP 연결에서 여러 요청을 동시에 처리&lt;/u&gt;하고, HPACK으로 헤더를 압축합니다. 다만 TCP 패킷 손실 시 모든 스트림이 대기하는 TCP 레벨 HOL Blocking은 남아있음&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;HTTP/3&lt;/b&gt;는 &lt;u&gt;TCP 대신 UDP 기반의 &lt;b&gt;QUIC&lt;/b&gt; 프로토콜을 사용해 각 스트림을 완전히 독립적으로 처리&lt;/u&gt;해 HOL Blocking을 완전히 해결하고, TLS가 기본 내장되어 연결 수립이 빠르며, Connection ID로 네트워크가 바뀌어도 연결을 유지할 수 있습니다.&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Q. HTTP/3가 UDP를 쓰면 신뢰성이 없는 거 아닌가요?&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP 자체는 신뢰성이 없지만, QUIC이 애플리케이션 레벨에서 신뢰성을 직접 구현합니다. 재전송, 순서 보장, 흐름 제어, 혼잡 제어를 모두 QUIC이 처리합니다. 오히려 TCP보다 더 유연하게 구현할 수 있어서 성능이 좋습니다.&lt;/p&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;&lt;b&gt;Q. 현재 HTTP/3 사용률은 어느 정도인가요?&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글, 유튜브, 페이스북 등 대형 서비스들은 이미 HTTP/3를 사용 중입니다. 크롬, 파이어폭스 등 주요 브라우저도 지원합니다. 다만 일부 방화벽이나 네트워크 장비가 UDP 443 포트를 차단하는 경우 HTTP/2로 폴백되는 경우도 있습니다.&lt;/p&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;h3 data-ke-size=&quot;size23&quot;&gt;Q13. REST API와 RESTful이란 무엇이며, REST의 6가지 제약 조건은 무엇인가요?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;REST는 웹 아키텍처 설계 원칙&lt;/li&gt;
&lt;li&gt;이를 따르는 API를 REST API라 함&lt;/li&gt;
&lt;li&gt;제약 조건을 잘 지킨 API를 RESTful하다고 함&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6가지 제약 조건&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;클라이언트-서버&lt;/b&gt;로 &lt;u&gt;UI와 비즈니스 로직을 분리&lt;/u&gt;하고&lt;/li&gt;
&lt;li&gt;&lt;b&gt;무상태&lt;/b&gt;로 서버가 &lt;u&gt;클라이언트 상태를 저장하지 않음&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;캐시 가능&lt;/b&gt;으로 응답에 &lt;u&gt;캐시 가능 여부를 명시&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;균일한 인터페이스&lt;/b&gt;로 &lt;u&gt;URI로 리소스를 식별하고 HTTP 메서드를 의미에 맞게 사용&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;계층화 시스템&lt;/b&gt;으로 로드밸런서나 게이트웨이 같은 &lt;u&gt;중간 계층을 자유롭게 추가&lt;/u&gt;할 수 있고&lt;/li&gt;
&lt;li&gt;선택적으로 &lt;b&gt;코드 온 디맨드&lt;/b&gt;로 &lt;u&gt;서버가 실행 코드를 전송&lt;/u&gt;할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 HATEOAS 구현이 복잡해 완전한 RESTful보다는 REST 원칙을 최대한 따르는 방향으로 설계&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;Q14. 대칭키 암호화와 공개키(비대칭키) 암호화의 차이를 설명하고, HTTPS는 이 두 방식을 어떻게 함께 사용하나요?&lt;/h3&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: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;장점 : 암호화와 복호화에 같은 키를 사용해 속도가 빠름&lt;/li&gt;
&lt;li&gt;단점 : 키를 안전하게 전달하는 키 배송 문제가 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;비대칭키 암호화
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;장점 :&amp;nbsp;공개키로 암호화하고 개인키로 복호화해서 키 배송 문제를 해결&lt;/li&gt;
&lt;li&gt;단점 : 속도가 느립니다.&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTPS는 두 방식을 조합
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;먼저 비대칭키로 대칭키(Session Key)를 안전하게 교환하고, 이후 실제 데이터 통신은 교환한 대칭키로 빠르게 암호화&lt;/li&gt;
&lt;li&gt;비대칭키의 안전성과 대칭키의 속도를 모두 확보하는 방식&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;h3 data-ke-size=&quot;size23&quot;&gt;Q15. SSL/TLS Handshake 과정을 설명해주세요.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS Handshake는 TCP 연결 이후 암호화 통신을 위한 준비 과정&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;TLS 1.2 기준&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 &lt;b&gt;ClientHello&lt;/b&gt;로 지원하는 TLS 버전과 암호화 방식 목록, 랜덤값을 보내면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 &lt;b&gt;ServerHello&lt;/b&gt;로 선택한 방식과 랜덤값을, &lt;b&gt;Certificate&lt;/b&gt;로 공개키가 담긴 인증서를 전송&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트는 인증서를 CA로 검증한 뒤 &lt;b&gt;Pre-Master Secret&lt;/b&gt;을 서버 공개키로 암호화해서 전송하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;양측이 Client Random, Server Random, Pre-Master Secret으로 동일한 &lt;b&gt;Session Key&lt;/b&gt;를 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 &lt;b&gt;Finished&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;TLS 1.3&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ClientHello에 키 교환 정보를 포함시켜 1-RTT로 단축하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RSA 키 교환을 제거해 Perfect Forward Secrecy를 강제.&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;Q16. CORS(Cross-Origin Resource Sharing)란 무엇이며, 왜 발생하고 어떻게 해결하나요?&lt;/h3&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;CORS는 브라우저의 SOP(동일 출처 정책)를 기반으로, 다른 Origin의 리소스 접근을 제어하는 메커니즘
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Origin은 프로토콜, 호스트, 포트가 모두 같아야 동일 Origin으로 인정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CORS 에러는 브라우저가 다른 Origin으로 요청 시 &lt;u&gt;서버 응답에 Access-Control-Allow-Origin 헤더가 없으면&lt;/u&gt; JS의 응답 접근을 차단하면서 발생&lt;/li&gt;
&lt;li&gt;PUT, DELETE나 커스텀 헤더 사용 시엔 실제 요청 전 &lt;u&gt;OPTIONS 메서드로 Preflight 요청을 먼저 보내 서버의 허용 여부를 확인&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;해결 방법은 &lt;u&gt;서버에서 허용할 Origin을 Access-Control-Allow-Origin 헤더로 명시&lt;/u&gt;하거나, 개발 환경에서는 &lt;u&gt;프록시 서버를 사용해 같은 Origin에서 요청하는 것처럼 우회&lt;/u&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;&lt;b&gt;Q. CORS 에러는 서버가 막는 건가요, 브라우저가 막는 건가요?&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저가 막습니다. 서버는 요청을 받아서 응답을 보내지만, 브라우저가 응답 헤더에 Access-Control-Allow-Origin이 없거나 Origin이 불일치하면 JS에서 응답에 접근하는 것을 차단합니다. 그래서 Postman 같은 도구에서는 CORS 에러가 없는 것입니다.&lt;/p&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;Q. 프록시 서버를 만들어서 우회하는 방식으로 개발할 때 사용한다고 하셨는데, 실제로 악의적인 사용자가 프록시 서버를 만들어 타깃 서버에 요청을 보내는 것도 가능하지 않나요?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;네, 기술적으로는 가능합니다. 다만 그렇게 해도 결국 보안은 서버의 인증/인가에서 담보됩니다.&lt;br /&gt;인증이 필요없는 공개 API라면 프록시로 우회가 가능하지만, 애초에 공개 API는 누구나 호출할 수 있는 데이터이므로 보안 문제가 아닙니다. 인증이 필요한 민감한 API라면 프록시 서버가 사용자의 토큰이나 쿠키를 가져올 방법이 없어서 결국 401이 반환됩니다.&lt;br /&gt;따라서 CORS는 브라우저 레벨의 1차 방어선일 뿐이고, 민감한 데이터를 다루는 API는 반드시 인증/인가로 보호해야 합니다. CORS만 믿고 인증 없이 민감한 데이터를 반환하는 API 설계 자체가 잘못된 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q17. 쿠키(Cookie)와 세션(Session)의 차이는 무엇이며, 각각 언제 사용하는 게 좋을까요?&lt;/h3&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: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;데이터를 클라이언트(브라우저)에 저장&lt;/li&gt;
&lt;li&gt;쿠키는 서버 부하가 없고 만료 기간 동안 유지되어 자동 로그인, 사용자 설정 저장에 적합&lt;/li&gt;
&lt;li&gt;데이터가 클라이언트에 노출되어 민감한 정보 저장에는 부적합&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;세션
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;데이터를 서버에 저장하고 클라이언트는 Session ID만 보관&lt;/li&gt;
&lt;li&gt;세션은 민감한 데이터를 서버에서 관리해 보안이 높고 강제 로그아웃이 가능&lt;/li&gt;
&lt;li&gt;서버 메모리를 사용하고 분산 환경에서 Redis 같은 별도 저장소가 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;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;Q18. 브라우저 주소창에 &lt;a href=&quot;http://www.google.com%EC%9D%84&quot;&gt;www.google.com을&lt;/a&gt; 입력하고 Enter를 누르면 어떤 일이 일어나나요?&lt;/h3&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&gt;&lt;span&gt;① URL 파싱&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;② 브라우저 캐시 확인&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;③ DNS 조회 (IP 주소 획득)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;④ TCP 연결 (3-way Handshake)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;⑤ TLS Handshake (HTTPS)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;⑥ HTTP 요청 전송&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;⑦ 서버 응답 수신&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;- URL을 파싱해 프로토콜과 도메인을 확인하고, 브라우저/OS DNS 캐시를 먼저 확인합니다.&lt;br /&gt;- 캐시가 없으면 DNS 조회를 통해 로컬 DNS 서버 &amp;rarr; Root DNS &amp;rarr; .com DNS &amp;rarr; google.com DNS 순서로 IP 주소를 얻습니다.&lt;br /&gt;- IP를 얻으면 TCP 3-way Handshake로 연결을 수립하고, HTTPS라면 TLS Handshake로 암호화 채널을 만듭니다.&lt;br /&gt;- 이후 HTTP GET 요청을 보내 HTML을 수신하면, 브라우저가 DOM과 CSSOM을 생성하고 Render Tree를 만들어 화면에 출력합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;Q19. JWT(JSON Web Token)란 무엇이며, 세션 기반 인증과 비교했을 때 장단점은 무엇인가요?&lt;/h3&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;JWT는 인증 정보를 Header, Payload, Signature 세 부분으로 나눠 토큰 자체에 담는 방식&lt;/li&gt;
&lt;li&gt;서버가 비밀키로 서명하고, 이후 요청마다 서명 검증만으로 인증하므로 DB 조회가 필요 없음&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;/h4&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;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;gt; 실무에서는 만료가 짧은 Access Token과 만료가 긴 Refresh Token을 함께 사용해서 보안과 편의성을 모두 챙깁니다. Refresh Token은 서버 DB에 저장해 강제 만료가 필요할 때 삭제하는 방식으로 JWT의 단점을 보완&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;Q20. DNS의 동작 원리를 설명해주세요. (재귀적 질의, 반복적 질의, 캐싱 포함)&lt;/h3&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;DNS는 도메인을 IP 주소로 변환하는 계층형 분산 시스템&lt;/li&gt;
&lt;li&gt;Root DNS, TLD DNS(.com, .kr), 권한 DNS(도메인 소유자 운영) 순서로 계층화되어 있음&lt;/li&gt;
&lt;/ul&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: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;클라이언트가 DNS Resolver에게 결과만 요청하는 재귀적 질의&lt;/li&gt;
&lt;li&gt;DNS Resolver가 Root &amp;rarr; TLD &amp;rarr; 권한 DNS를 단계별로 직접 찾아다니는 반복적 질의가 있음&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제로는 클라이언트&amp;harr;Resolver는 재귀적, Resolver&amp;harr;각 DNS 서버는 반복적으로 동작&lt;/li&gt;
&lt;li&gt;성능을 위해 각 단계에서 TTL 시간 동안 캐싱
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;브라우저 캐시 &amp;rarr; OS 캐시 &amp;rarr; DNS Resolver 캐시 순서로 확인&lt;/li&gt;
&lt;li&gt;캐시에 있으면 Root DNS까지 가지 않고 바로 반환합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;Q21. IPv4와 IPv6의 차이점은 무엇이며, IPv6가 등장한 이유는 무엇인가요? 서브넷팅에 대해서도 간단히 설명해주세요.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4는 32비트로 약 43억 개의 주소를 제공하는데, 스마트폰과 IoT 기기 폭증으로 2011년 주소가 고갈됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 이를 해결하기 위해 128비트의 IPv6가 등장했으며 사실상 무한에 가까운 주소를 제공.&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;IPv6는 주소 확장 외에도 &lt;u&gt;헤더 단순화&lt;/u&gt;, &lt;u&gt;IPSec 기본 내장&lt;/u&gt;, &lt;u&gt;브로드캐스트 제거&lt;/u&gt;, &lt;u&gt;SLAAC 자동 주소 설정 등이 개선&lt;/u&gt;됐음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 IPv4와 직접 호환되지 않아 현재는 듀얼 스택으로 공존 중입니다.&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;/24처럼 CIDR 표기법으로 네트워크 비트 수를 표시하며, 나머지 비트가 호스트 주소가 됨.&lt;/li&gt;
&lt;li&gt;부서별 트래픽 분리, IP 주소 효율적 사용, 보안 정책 분리 등을 위해 활용&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>민석 개발일지</author>
      <guid isPermaLink="true">https://minseok1015.tistory.com/30</guid>
      <comments>https://minseok1015.tistory.com/30#entry30comment</comments>
      <pubDate>Tue, 12 May 2026 19:11:34 +0900</pubDate>
    </item>
    <item>
      <title>CS면접 질문 - 자료구조</title>
      <link>https://minseok1015.tistory.com/29</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 자료구조란 무엇이며, 선형 자료구조와 비선형 자료구조의 차이점을 설명해주세요. 각각의 대표적인 예시도 함께 들어주세요.&lt;/h3&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;b&gt;자료구조 &lt;/b&gt;:&amp;nbsp;데이터를 효율적으로 저장하고 관리하기 위한 구조&lt;/li&gt;
&lt;li&gt;&lt;b&gt;선형 자료구조 &lt;/b&gt;: 데이터가 순차적으로 나열되어 1:1 관계를 가짐 (배열&amp;middot;스택&amp;middot;큐가 대표적)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비선형 자료구조 &lt;/b&gt;: 데이터 간 1:N 또는 N:M 관계로 계층적이거나 망 형태를 가짐 (트리&amp;middot;그래프가 대표적)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;선형&lt;/b&gt;은 순서 기반 처리에, &lt;b&gt;비선형&lt;/b&gt;은 계층적 관계나 복잡한 연결 관계 표현에 적합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 배열(Array)과 ArrayList, LinkedList의 차이점을 설명하고, 각각의 시간 복잡도(접근, 삽입, 삭제)를 비교해주세요. 어떤 상황에서 어떤 자료구조를 선택해야 하나요?&lt;/h3&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;Array : 고정 크기의 연속 메모리 구조로 인덱스 접근이 O(1)이지만 크기 변경이 불가&lt;/li&gt;
&lt;li&gt;ArrayList : 내부적으로 배열을 사용하는 동적 배열로, 인덱스 접근은 O(1)이지만 중간 삽입&amp;middot;삭제는 shift가 발생해 O(n)&lt;/li&gt;
&lt;li&gt;LinkedList : 노드와 포인터로 연결된 구조로 중간 삽입&amp;middot;삭제가 O(1)이지만, 인덱스 접근은 순회가 필요해 O(n)&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;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;b&gt;조회가 잦으면 ArrayList&lt;/b&gt;, &lt;b&gt;삽입&amp;middot;삭제가 잦은 양 끝 연산이면 LinkedList&lt;/b&gt;를 선택.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;다만 실제로는 캐시 효율 때문에 ArrayList가 전반적으로 더 빠른 경우가 많음.&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;Q1. ArrayList는 크기가 꽉 찼을 때 어떻게 동작하나요?&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;rarr; 내부적으로 현재 크기의 &lt;b&gt;1.5배&lt;/b&gt;짜리 새 배열을 생성하고, 기존 데이터를 전부 복사해. 이 시점에 O(n) 비용이 발생하지만, amortized(분할 상환)하면 평균 O(1)로 봄.&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;Q2. Java에서 LinkedList가 ArrayList보다 느린 경우가 많은 이유가 뭔가요?&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;rarr; LinkedList는 메모리가 불연속적으로 분산되어 있어서 &lt;b&gt;CPU 캐시 효율이 낮음&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ArrayList는 연속 메모리라 캐시 히트율이 높아서 실제로는 더 빠른 경우가 많음.&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;Q3. ArrayList와 Vector의 차이는 무엇인가요?&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;rarr; Vector는 &lt;b&gt;동기화(synchronized)&lt;/b&gt; 가 적용된 ArrayList.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티스레드 환경에서 안전하지만, 그만큼 성능이 느림.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘은 Vector보다 &lt;b&gt;CopyOnWriteArrayList&lt;/b&gt; 나 &lt;b&gt;Collections.synchronizedList()&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;Q4. 배열과 ArrayList 중 primitive 타입을 다룰 때 어느 게 효율적인가요?&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;rarr; 배열이 효율적.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ArrayList는 &lt;b&gt;제네릭 타입&lt;/b&gt;이라 int 같은 primitive를 쓰면 &lt;b&gt;Integer로 오토박싱&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 단일 연결 리스트(Singly Linked List), 이중 연결 리스트(Doubly Linked List), 원형 연결 리스트(Circular Linked List)의 차이점과 각각의 장단점을 설명해주세요.&lt;/h3&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;: next 포인터만 가져 단방향 순회만 가능하고, 메모리가 가장 효율적&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이중 연결 리스트&lt;/b&gt; : prev, next 포인터를 모두 가져 양방향 순회가 가능(Java의 LinkedList가 이 방식으로 구현)&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&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;Q1. Java의 LinkedList는 어떤 연결 리스트로 구현되어 있나요?&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;rarr; &lt;b&gt;이중 연결 리스트&lt;/b&gt;로 구현되어 있어. 그래서 getFirst(), getLast(), addFirst(), addLast() 같은 양방향 연산을 O(1)로 지원해.&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;Q2. 원형 연결 리스트는 실제로 어디에 쓰이나요?&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;rarr; 대표적으로 &lt;b&gt;라운드 로빈 스케줄링&lt;/b&gt; (CPU가 프로세스를 순서대로 돌아가며 실행), 미디어 플레이어 반복 재생, 원형 버퍼(Circular Buffer) 등에 활용돼.&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;4. 스택(Stack)이 무엇이며 LIFO 구조를 어떻게 활용하나요? 실제 사용 사례(예: 재귀 호출, 괄호 검사, 뒤로 가기)를 들어 설명해주세요.&lt;/h3&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;스택은 LIFO(후입선출) 구조로 가장 마지막에 삽입된 데이터가 먼저 나오는 자료구조.&lt;/li&gt;
&lt;li&gt;push, pop, peek 모두 O(1)임&lt;/li&gt;
&lt;li&gt;대표적인 활용 사례로는 함수 호출 시 사용되는 콜 스택, 괄호 유효성 검사, 브라우저 뒤로 가기, 실행 취소(Undo) 기능이 있음&lt;/li&gt;
&lt;li&gt;Java에서는 Stack 클래스보다 동기화 오버헤드가 없는 ArrayDeque 사용을 권장합니다.&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;&lt;b&gt;Q1. Stack 클래스 대신 ArrayDeque를 권장하는 이유가 뭔가요?&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;rarr; Stack은 &lt;b&gt;Vector를 상속&lt;/b&gt;받아 모든 연산에 &lt;b&gt;synchronized&lt;/b&gt;가 걸려 있어. 싱글 스레드 환경에서도 불필요한 동기화 비용이 발생해서 느려. ArrayDeque는 동기화가 없고 배열 기반이라 훨씬 빠르고 캐시 효율도 좋아.&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;5. 큐(Queue)와 스택의 차이점을 설명하고, 일반 큐의 단점을 보완한 원형 큐(Circular Queue)와 연결 리스트 기반 큐의 동작 방식을 설명해주세요.&lt;/h3&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;스택은 LIFO, 큐는 FIFO 구조로 삽입은 rear에서, 삭제는 front에서 일어남&lt;/li&gt;
&lt;li&gt;배열 기반 일반 큐는 dequeue 후 앞 공간을 재사용하지 못하는 공간 낭비 문제가 있음.&lt;/li&gt;
&lt;li&gt;원형 큐는 (index+1) % size 연산으로 배열을 순환시켜 이 문제를 해결하지만 크기는 여전히 고정.&lt;/li&gt;
&lt;li&gt;연결 리스트 기반 큐는 동적으로 크기가 늘어나 크기 제한이 없지만 포인터 오버헤드가 존재.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. Deque(덱)이란 무엇이며, 스택과 큐로 어떻게 활용될 수 있는지 설명해주세요.&lt;/h3&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;덱은 양쪽 끝에서 삽입과 삭제가 모두 가능한 자료구조로, 모든 연산이 O(1).&lt;/li&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Q1. ArrayDeque와 LinkedList 기반 덱의 차이는 뭔가요?&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;rarr; ArrayDeque는 &lt;b&gt;배열 기반&lt;/b&gt;이라 캐시 효율이 좋고 메모리 오버헤드가 적어서 대부분의 경우 더 빠름. LinkedList는 &lt;b&gt;포인터 기반&lt;/b&gt;이라 메모리 오버헤드가 있지만 크기 제한이 없고 중간 삽입/삭제가 자유로워. 단순 스택/큐 용도라면 &lt;b&gt;ArrayDeque가 더 효율적&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;h3 data-ke-size=&quot;size23&quot;&gt;7. 우선순위 큐(Priority Queue)는 무엇이며, 일반 큐와 어떻게 다른가요? 어떤 자료구조로 구현하는 것이 가장 효율적이며 그 이유는 무엇인가요?&lt;/h3&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;우선순위 큐는 FIFO가 아닌 우선순위가 높은 데이터가 먼저 나오는 자료구조.&lt;/li&gt;
&lt;li&gt;배열로 구현하면 삽입 또는 삭제 시 O(n)이 발생하지만, 힙으로 구현하면 삽입과 삭제 모두 O(log n), 최솟값 조회는 O(1)로 가장 효율적.&lt;/li&gt;
&lt;li&gt;실제로 다익스트라 알고리즘의 최단 경로 탐색, CPU 프로세스 스케줄링 등에 활용.&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;&lt;span style=&quot;color: #666666;&quot;&gt;Java에서는 PriorityQueue 클래스가 내부적으로 최소 힙으로 구현되어 있음&lt;/span&gt;&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;8. 해시 테이블(Hash Table)의 동작 원리와 해시 함수(Hash Function)에 대해 설명해주세요. 평균/최악의 시간 복잡도는 어떻게 되나요?&lt;/h3&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;해시 테이블은 해시 함수로 Key를 배열 인덱스로 변환해서 Value를 저장하는 자료구조&lt;/li&gt;
&lt;li&gt;평균 O(1)로 탐색/삽입/삭제가 가능&lt;/li&gt;
&lt;li&gt;좋은 해시 함수는 빠른 계산, 균등한 분포, 결정론적 특성을 가져야 함&lt;/li&gt;
&lt;li&gt;Java HashMap은 로드 팩터 0.75를 초과하면 배열을 2배로 확장하고 rehashing을 수행&lt;/li&gt;
&lt;li&gt;최악의 경우 모든 Key가 같은 인덱스로 해싱되면 O(n)이 발생, 이게 해시 충돌 문제&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;h4 data-ke-size=&quot;size20&quot;&gt;로드 팩터 (Load Factor)&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;로드 팩터 = 저장된 데이터 수 / 배열 크기

ex) 데이터 7개, 배열 크기 10 &amp;rarr; 로드 팩터 = 0.7&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로드 팩터가 높아질수록 &lt;b&gt;충돌 확률이 증가&lt;/b&gt;해서 성능이 저하. Java HashMap은 기본 로드 팩터가 &lt;b&gt;0.75&lt;/b&gt;이고, 이를 초과하면 배열을 &lt;b&gt;2배로 확장(rehashing).&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot; style=&quot;color: #14181f;&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;rehashing:
1. 2배 크기의 새 배열 생성
2. 기존 데이터를 새 배열에 전부 재삽입
3. 해시값이 바뀌기 때문에 전부 다시 계산
&amp;rarr; O(n) 비용 발생! (ArrayList 확장과 비슷한 개념)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;Q. 로드 팩터를 너무 낮게 설정하면 어떤 문제가 있나요?&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;rarr; 충돌은 줄어들어서 탐색 성능은 좋아지지만, &lt;b&gt;메모리 낭비&lt;/b&gt;가 심해지고 rehashing이 자주 발생해서 오히려 성능이 저하될 수 있어. 0.75가 시간/공간 트레이드오프의 균형점.&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;9. 해시 충돌(Collision)이란 무엇이며, 이를 해결하는 방법(Chaining, Open Addressing)을 비교 설명해주세요.&lt;/h3&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;해시 충돌은 서로 다른 Key가 같은 인덱스로 해싱되는 현상.&lt;/li&gt;
&lt;li&gt;Chaining은 충돌 시 연결 리스트로 데이터를 연결하는 방식으로 구현이 단순하지만 포인터 오버헤드와 캐시 효율 저하가 있음.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java HashMap이 이 방식을 사용하며, Java 8부터 버킷 크기가 8 이상이면 Red-Black Tree로 변환해 O(log n)으로 성능을 보완.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Open Addressing은 빈 슬롯을 탐사해서 저장하는 방식으로 메모리 효율과 캐시 효율이 좋지만, 로드 팩터가 1 미만이어야 하고 삭제 시 Tombstone 처리가 필요.&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;&lt;span style=&quot;color: #ee2323;&quot;&gt;그렇다면 Hash 버킷에서 데이터가 줄어들때 어떻게 되나?&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;: 트리&amp;rarr;리스트 변환 기준은 &lt;b&gt;6개 이하&lt;/b&gt;. 8(트리)과 6(리스트) 사이에 버퍼를 둬서 데이터가 경계값 근처에서 왔다갔다할 때 변환이 반복되는 thrashing을 방지해.&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10. 트리(Tree) 자료구조의 기본 용어(루트, 부모, 자식, 리프, 깊이, 레벨, 차수)를 설명하고, 트리와 그래프의 차이점은 무엇인가요?&lt;/h3&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;/li&gt;
&lt;li&gt;트리와 그래프의 핵심 차이는 트리는 사이클이 없고, 루트가 존재하며, 노드 간 경로가 유일하고, 간선 수가 노드 수 - 1이라는 점.&lt;/li&gt;
&lt;li&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;&lt;b&gt;Q3. 실생활에서 트리 구조의 예시를 들어주세요.&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;rarr; &lt;b&gt;파일 시스템&lt;/b&gt; (폴더 안에 폴더), &lt;b&gt;조직도&lt;/b&gt; (대표 &amp;rarr; 부서장 &amp;rarr; 팀원), &lt;b&gt;HTML DOM 트리&lt;/b&gt; (html &amp;rarr; head/body &amp;rarr; 각 태그), &lt;b&gt;댓글의 대댓글 구조&lt;/b&gt; 등&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;11. 이진 트리(Binary Tree)의 종류(완전 이진 트리, 포화 이진 트리, 정 이진 트리)를 설명하고, 트리 순회 방식 4가지(전위, 중위, 후위, 레벨 순회)를 설명해주세요.&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;포화 이진 트리(Perfect Binary Tree)&lt;/b&gt; : 모든 노드가 자식 2개이고 리프가 같은 레벨&lt;/li&gt;
&lt;li&gt;&lt;b&gt;완전 이진 트리(Complete Binary Tree)&lt;/b&gt; : 마지막 레벨만 왼쪽부터 채워진 구조&lt;/li&gt;
&lt;li&gt;&lt;b&gt;정 이진 트리(Full Binary Tree)&lt;/b&gt; : 자식이 0개 또는 2개만 존재하는 구조&lt;/li&gt;
&lt;li&gt;순회 방식은 전위(루트&amp;rarr;왼&amp;rarr;오), 중위(왼&amp;rarr;루트&amp;rarr;오), 후위(왼&amp;rarr;오&amp;rarr;루트), 레벨 순회(너비 우선) 4가지
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;전위/중위/후위 -&amp;gt; 스택(재귀)&lt;/li&gt;
&lt;li&gt;레벨 순회 -&amp;gt; 큐를 사용&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;h3 data-ke-size=&quot;size23&quot;&gt;12. 이진 탐색 트리(BST)의 특징과 핵심 연산의 시간 복잡도를 설명해주세요. BST의 단점과 그 단점이 발생하는 상황은 무엇인가요?&lt;/h3&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;BST는 왼쪽 서브트리의 모든 노드가 부모보다 작고, 오른쪽 서브트리의 모든 노드가 부모보다 큰 이진 트리.&lt;/li&gt;
&lt;li&gt;탐색할 때마다 범위가 절반씩 줄어들어 평균 O(log n)이지만, 정렬된 데이터를 순서대로 삽입하면 편향 트리가 되어 최악 O(n)이 됨.&lt;/li&gt;
&lt;li&gt;이 편향 문제를 해결하기 위해 삽입/삭제 시 자동으로 균형을 맞추는 AVL Tree와 Red-Black Tree가 등장함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;13. BST 노드 삭제의 3가지 케이스(자식 노드가 없는 경우, 1개인 경우, 2개인 경우)에 대해 설명해주세요.&lt;/h3&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;BST 노드 삭제는 자식 수에 따라 3가지 케이스로 나뉨&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자식이 없으면&lt;/b&gt; 그냥 제거&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자식이 1개면&lt;/b&gt; 자식을 삭제된 자리에 연결&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자식이 2개면&lt;/b&gt; &lt;u&gt;오른쪽 서브트리의 최솟값(후계자)&lt;/u&gt; 또는 &lt;u&gt;왼쪽 서브트리의 최댓값(전임자)&lt;/u&gt;으로 대체&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;14. 균형 이진 탐색 트리(AVL Tree, Red-Black Tree)가 왜 필요한가요? Red-Black Tree의 주요 규칙을 설명해주세요.&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;BST는 편향 트리가 되면 O(n)으로 성능이 저하되는 문제가 있어서 균형 이진 탐색 트리가 필요&lt;/li&gt;
&lt;li&gt;AVL Tree는 Balance Factor를 -1~1로 엄격하게 유지해서 탐색이 빠르지만, 삽입/삭제 시 회전이 빈번해 쓰기 작업이 많으면 비효율적&lt;/li&gt;
&lt;li&gt;Red-Black Tree는 색상 규칙으로 느슨하게 균형을 유지해서 삽입/삭제 시 회전이 적고 실무에서 더 많이 쓰입니다.&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;span style=&quot;color: #666666;&quot;&gt;Java의 TreeMap, TreeSet이 Red-Black Tree로 구현되어 있고, Java 8부터 HashMap의 버킷도 크기가 8 이상이면 Red-Black Tree로 변환&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;15. 힙(Heap)이란 무엇이며, 최대 힙(Max Heap)과 최소 힙(Min Heap)의 차이점은 무엇인가요? 힙의 삽입과 삭제 과정을 설명해주세요.&lt;/h3&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;b&gt; 최대 힙&lt;/b&gt;은 부모가 항상 자식보다 크거나 같고, &lt;b&gt;최소 힙&lt;/b&gt;은 항상 작거나 같음
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;삽입 시&lt;/b&gt; 마지막 위치에 추가 후 Heapify Up&lt;/li&gt;
&lt;li&gt;&lt;b&gt;삭제 시&lt;/b&gt; 루트를 제거하고 마지막 노드를 루트로 올린 뒤 Heapify Down을 수행&lt;/li&gt;
&lt;li&gt;둘 다 O(log n)입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루트가 항상 최댓값/최솟값이라 O(1) 조회가 가능해서 우선순위 큐 구현에 사용되고, 힙 정렬에도 활용.&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;Q1. 힙이 완전 이진 트리여야 하는 이유가 있나요?&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;rarr; 완전 이진 트리여야 &lt;b&gt;배열로 빈 공간 없이 표현&lt;/b&gt;할 수 있음. 또한 &lt;b&gt;완전 이진 트리 조건을 항상 유지하기 때문에&lt;/b&gt; 높이가 항상 O(log n)으로 보장되기 때문에 삽입/삭제의 시간복잡도가 O(log n)이 보장됨.&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;Q2. 힙과 BST의 차이점은 뭔가요?&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;rarr; BST는 &lt;b&gt;왼쪽 &amp;lt; 부모 &amp;lt; 오른쪽&lt;/b&gt; 규칙으로 완전한 정렬 구조를 유지하고 탐색에 특화돼 있음. 힙은 &lt;b&gt;부모 &amp;gt;= 자식(또는 &amp;lt;=)&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;Q3. 힙 생성이 O(n log n)이 아니라 O(n)인 이유는?&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;rarr; 하나씩 삽입하면 O(n log n)이지만, &lt;b&gt;아래에서 위로 Heapify&lt;/b&gt; 하는 방식을 쓰면 O(n)이야. 리프 노드는 Heapify가 필요 없고, 위로 올라갈수록 노드 수가 줄어들기 때문에 수학적으로 O(n)으로 수렴.&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;Q4. 퀵 정렬 대비 힙 정렬의 장단점은?&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;rarr; 힙 정렬은 &lt;b&gt;최악에도 O(n log n)&lt;/b&gt; 을 보장하고 추가 메모리가 O(1). 퀵 정렬은 평균 O(n log n)이지만 최악 O(n&amp;sup2;)이고 추가 메모리가 O(log n) 필요해. 하지만 퀵 정렬이 &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;h3 data-ke-size=&quot;size23&quot;&gt;16. B-Tree와 B+Tree의 차이점은 무엇이며, 데이터베이스의 인덱스로 B+Tree가 주로 사용되는 이유를 설명해주세요.&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;B-Tree&lt;/b&gt; : 하나의 노드에 여러 키와 데이터를 저장하는 균형 트리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;B+Tree&lt;/b&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;DB 인덱스로 B+Tree를 쓰는 이유&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;리프 노드 연결 리스트로 &lt;u&gt;범위 검색이 빠름&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;내부 노드에 키만 저장해서 같은 공간에 더 많은 키를 저장할 수 있어 &lt;u&gt;트리 높이가 낮아짐&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;트리 높이가 낮아서 &lt;u&gt;디스크 I/O 횟수가 최소화&lt;/u&gt;됨&lt;/li&gt;
&lt;/ol&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;Q. B-Tree가 이진 탐색 트리(BST)보다 DB에 적합한 이유는?&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;rarr; BST는 노드당 키가 1개라 트리 높이가 높아져서 &lt;u&gt;디스크 I/O가 많이 발생&lt;/u&gt;. B-Tree는 하나의 노드에 여러 키를 저장해서 트리 높이가 낮고, 하나의 디스크 블록에 노드 전체를 올릴 수 있어서 &lt;u&gt;I/O 횟수가 훨씬 적음&lt;/u&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;17. 트라이(Trie) 자료구조란 무엇이며, 주로 어떤 상황에서 활용되나요? (예: 자동완성, 사전 검색) 그래프 / Java Collections / 응용&lt;b&gt;&lt;/b&gt;&lt;/h3&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;b&gt;트라이&lt;/b&gt;는 문자열 탐색에 특화된 트리 자료구조&lt;/li&gt;
&lt;li&gt;&lt;b&gt;각 노드가 문자 하나를 나타내&lt;/b&gt;고 루트에서 노드까지의 경로가 문자열을 표현&lt;/li&gt;
&lt;li&gt;&lt;b&gt;삽입과 탐색 모두 문자열 길이 L에만 비례하는 O(L)&lt;/b&gt;로, 저장된 문자열 수와 무관하게 빠른 탐색이 가능&lt;/li&gt;
&lt;li&gt;자동완성, 검색어 추천, 사전 검색처럼 접두사(prefix) 기반 탐색이 필요한 경우에 주로 활용&lt;/li&gt;
&lt;li&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;&lt;span style=&quot;color: #ee2323;&quot;&gt;트라이에서&amp;nbsp;발생하는&amp;nbsp;오버헤드&amp;nbsp;-&amp;nbsp;노드마다&amp;nbsp;자식&amp;nbsp;포인터&amp;nbsp;배열을&amp;nbsp;유지해야&amp;nbsp;해서&amp;nbsp;메모리&amp;nbsp;사용량이&amp;nbsp;크다는&amp;nbsp;단점&lt;/span&gt;&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;18. 그래프(Graph) 자료구조의 표현 방법(인접 행렬 vs 인접 리스트)을 설명하고, 각각의 장단점과 어떤 상황에 적합한지 설명해주세요.&lt;/h3&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;b&gt;인접 행렬&lt;/b&gt; : V&amp;times;V 배열로 간선 존재 확인이 O(1)이지만 공간이 항상 O(V&amp;sup2;)라 노드가 많고 간선이 적은 &lt;u&gt;희소 그래프에서 메모리 낭비&lt;/u&gt;가 심함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인접 리스트&lt;/b&gt; : 연결된 노드만 저장해서 &lt;u&gt;O(V+E) 공간을 사용&lt;/u&gt;하고 탐색이 효율적이라 대부분의 경우에 적합&lt;/li&gt;
&lt;li&gt;따라서 밀집 그래프이거나 간선 존재 확인이 잦으면 &lt;u&gt;인접 행렬&lt;/u&gt;, 그 외 대부분의 경우엔 &lt;u&gt;인접 리스트&lt;/u&gt;를 사용함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;19. DFS(깊이 우선 탐색)와 BFS(너비 우선 탐색)의 차이점은 무엇이며, 각각 어떤 자료구조(스택/큐)를 활용하나요? 어떤 상황에서 어떤 알고리즘을 사용해야 하나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DFS&lt;/b&gt;는 깊이 우선으로 &lt;u&gt;스택(재귀)&lt;/u&gt;을 사용, &lt;b&gt;BFS&lt;/b&gt;는 너비 우선으로 &lt;u&gt;큐&lt;/u&gt;를 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 시간복잡도는 O(V+E)입니다.&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;DFS는 &lt;u&gt;모든 경로 탐색&lt;/u&gt;, &lt;u&gt;백트래킹&lt;/u&gt;, &lt;u&gt;사이클 감지&lt;/u&gt;에 적합&lt;/li&gt;
&lt;li&gt;BFS는 가중치 없는 그래프에서 &lt;u&gt;최단 경로 탐색에 적합&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;BFS가 최단 경로를 보장하는 이유는 레벨 단위로 탐색하기 때문에 목적지를 처음 발견하는 순간이 곧 최단 경로이기 때문&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;&lt;b&gt;Q. DFS와 BFS의 시간복잡도가 둘 다 O(V+E)인 이유는?&lt;/b&gt; &amp;rarr; 두 알고리즘 모두 모든 노드(V)를 한 번씩 방문하고, 각 노드에서 모든 간선(E)을 한 번씩 확인하기 때문이야. 인접 리스트 기준이고, 인접 행렬이면 O(V&amp;sup2;)야.&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;20. Java의 Collection Framework에서 List, Set, Map 인터페이스의 차이점과 각각의 대표적인 구현체(ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap)의 특징을 비교 설명해주세요.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java Collection Framework는 List, Set, Map 인터페이스로 나뉨&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;List는 순서가 있고 중복을 허용
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;ArrayList는 배열 기반으로 조회가 O(1)&lt;/li&gt;
&lt;li&gt;LinkedList는 이중 연결 리스트로 앞뒤 삽입/삭제가 O(1)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Set은 중복을 허용하지 않음
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;HashSet은 O(1) 탐색&lt;/li&gt;
&lt;li&gt;TreeSet은 Red-Black Tree 기반으로 정렬을 보장하지만 O(log n)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Map은 Key-Value 쌍으로 저장
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;HashMap은 O(1) 탐색, TreeMap은 Key 기준 정렬을 보장.&lt;/li&gt;
&lt;li&gt;순서 유지가 필요하면 Linked 계열을 사용&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;&lt;b&gt;Q. HashSet이 내부적으로 HashMap으로 구현된다는 게 무슨 뜻인가요?&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;rarr; HashSet은 내부에 &lt;b&gt;HashMap을 가지고 있고&lt;/b&gt;, 저장할 값을 HashMap의 &lt;b&gt;Key로 사용&lt;/b&gt;. Value는 의미 없는 더미 객체(PRESENT)를 넣음. Key의 중복을 허용하지 않는 HashMap의 특성을 그대로 활용.&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;21. HashMap과 HashTable, ConcurrentHashMap의 차이점은 무엇이며, 각각 어떤 상황에서 사용해야 하나요? Java 8 이후 HashMap의 내부 구조 변화(Treeify)에 대해서도 설명해주세요.&lt;/h3&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;/h4&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.4884%;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 26.5116%;&quot;&gt;HashMap&lt;/td&gt;
&lt;td style=&quot;width: 20.6977%;&quot;&gt;HashTable&lt;/td&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;ConcurrentHashMap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.4884%;&quot;&gt;동기화&lt;/td&gt;
&lt;td style=&quot;width: 26.5116%;&quot;&gt;❌&lt;/td&gt;
&lt;td style=&quot;width: 20.6977%;&quot;&gt;메서드 전체&lt;/td&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;버킷 단위&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.4884%;&quot;&gt;null 허용&lt;/td&gt;
&lt;td style=&quot;width: 26.5116%;&quot;&gt;Key/Value 모두 ✅&lt;/td&gt;
&lt;td style=&quot;width: 20.6977%;&quot;&gt;❌&lt;/td&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.4884%;&quot;&gt;성능&lt;/td&gt;
&lt;td style=&quot;width: 26.5116%;&quot;&gt;가장 빠름&lt;/td&gt;
&lt;td style=&quot;width: 20.6977%;&quot;&gt;가장 느림&lt;/td&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;중간 (실용적)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.4884%;&quot;&gt;스레드 안전&lt;/td&gt;
&lt;td style=&quot;width: 26.5116%;&quot;&gt;❌&lt;/td&gt;
&lt;td style=&quot;width: 20.6977%;&quot;&gt;✅&lt;/td&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.4884%;&quot;&gt;사용 시점&lt;/td&gt;
&lt;td style=&quot;width: 26.5116%;&quot;&gt;단일 스레드&lt;/td&gt;
&lt;td style=&quot;width: 20.6977%;&quot;&gt;사용 비권장&lt;/td&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;멀티 스레드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.4884%;&quot;&gt;등장 버전&lt;/td&gt;
&lt;td style=&quot;width: 26.5116%;&quot;&gt;JDK 1.2&lt;/td&gt;
&lt;td style=&quot;width: 20.6977%;&quot;&gt;JDK 1.0&lt;/td&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;JDK 1.5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&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;HashMap &lt;/b&gt;: 동기화가 없어 단일 스레드에서 가장 빠르고 null Key/Value를 허용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HashTable &lt;/b&gt;:&amp;nbsp;모든 메서드에 synchronized가 적용되어 스레드 안전하지만, 테이블 전체에 락을 걸어 성능이 낮아 현재는 사용을 권장하지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ConcurrentHashMap &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;span style=&quot;color: #666666;&quot;&gt;Java 8부터는 Segment 방식에서 버킷 단위 락과 CAS 연산으로 변경되어 더욱 효율적&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Java 8에서 HashMap은 버킷의 연결 리스트 길이가 8 이상이면 Red-Black Tree로 변환(Treeify)하여 최악의 경우 O(n)에서 O(log n)으로 성능을 개선&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;span style=&quot;color: #ee2323;&quot;&gt;hashtable은 컬렉션 프레임워크 이전에 있던 것임&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Q1. ConcurrentHashMap에서 null을 허용하지 않는 이유가 있나요?&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;rarr; 멀티스레드 환경에서 map.get(key)가 null을 반환했을 때 &lt;b&gt;Key가 없는 건지, Value가 null인 건지 구분이 불가능&lt;/b&gt;해. 단일 스레드면 containsKey()로 확인할 수 있지만 멀티스레드에서는 확인하는 사이에 다른 스레드가 값을 바꿀 수 있어서 null 자체를 허용하지 않도록 설계했어.&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;Q2. ConcurrentHashMap의 버킷 단위 락이 HashTable보다 빠른 이유는?&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;rarr; HashTable은 &lt;b&gt;테이블 전체에 락&lt;/b&gt;을 걸어서 한 번에 스레드 하나만 접근 가능. ConcurrentHashMap은 &lt;b&gt;버킷 단위로 락&lt;/b&gt;을 걸어서 서로 다른 버킷에 접근하는 스레드들은 동시에 작업할 수 있어. 병렬 처리가 가능하니까 훨씬 빠름&lt;/p&gt;</description>
      <author>민석 개발일지</author>
      <guid isPermaLink="true">https://minseok1015.tistory.com/29</guid>
      <comments>https://minseok1015.tistory.com/29#entry29comment</comments>
      <pubDate>Wed, 6 May 2026 19:40:14 +0900</pubDate>
    </item>
    <item>
      <title>CS면접 질문 - 자바</title>
      <link>https://minseok1015.tistory.com/27</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;Q.&amp;nbsp;OOP&amp;nbsp;4대&amp;nbsp;특징(캡슐화,&amp;nbsp;상속,&amp;nbsp;다형성,&amp;nbsp;추상화)을&amp;nbsp;설명해&amp;nbsp;주세요&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OOP는 캡슐화, 상속, 다형성, 추상화 네 가지 특징을 가집니다.&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;캡슐화 : 데이터와 메서드를 하나의 클래스로 묶고, 내부 구현을 숨기는 것입니다. private필드 +getter/setter로 외부 접근을 제어해 객체의 무결성을 보장합니다.&lt;/li&gt;
&lt;li&gt;상속 : 부모 클래스의 속성과 메서드를 자식이 물려받는 것으로 코드 재사용성을 높입니다. 단, 부모-자식 간 강한 결합이 생기는 단점이 있습니다.&lt;/li&gt;
&lt;li&gt;다형성 : 같은 타입으로 서로 다른 객체를 다룰 수 있는 능력입니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오버로딩(Overloading) &amp;rarr; 컴파일타임 다형성&lt;/li&gt;
&lt;li&gt;오버라이딩(Overriding) &amp;rarr; 런타임 다형성 (동적 바인딩)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot;&gt;추상화 : 복잡한 구현을 숨기고 필요한 인터페이스만 노출하는 것입니다.&lt;/span&gt;&lt;span data-token-index=&quot;0&quot;&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span data-token-index=&quot;0&quot;&gt;&quot;무엇을 할 수 있는가(What)&quot;에 집중하게 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;Q. SOLID 원칙에 대해 설명해 주세요.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SOLID는 객체지향 설계의 다섯 가지 원칙입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div id=&quot;code_1775738473294&quot; data-ke-type=&quot;html&quot; data-source=&quot;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;ko&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;gt;
  &amp;lt;title&amp;gt;SOLID 원칙&amp;lt;/title&amp;gt;
  &amp;lt;style&amp;gt;
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans KR', sans-serif;
      background: #f5f5f3;
      display: flex;
      justify-content: center;
      align-items: flex-start;
      min-height: 100vh;
      padding: 3rem 1rem;
      color: #1a1a18;
    }

    .card {
      background: #ffffff;
      border-radius: 14px;
      border: 0.5px solid rgba(0,0,0,0.1);
      padding: 2rem 2rem 1rem;
      width: 100%;
      max-width: 780px;
    }

    .card-title {
      font-size: 22px;
      font-weight: 500;
      letter-spacing: -0.3px;
      color: #1a1a18;
      margin-bottom: 0.25rem;
    }

    .card-sub {
      font-size: 13px;
      color: #888780;
      margin-bottom: 1.75rem;
    }

    table {
      width: 100%;
      border-collapse: collapse;
      font-size: 14px;
    }

    thead th {
      text-align: left;
      font-weight: 500;
      font-size: 12px;
      color: #888780;
      padding: 0 16px 10px;
      border-bottom: 1px solid rgba(0,0,0,0.15);
      text-transform: uppercase;
      letter-spacing: 0.04em;
    }

    tbody td {
      padding: 15px 16px;
      border-bottom: 0.5px solid rgba(0,0,0,0.07);
      vertical-align: top;
      line-height: 1.6;
    }

    tbody tr:last-child td { border-bottom: none; }

    tbody tr:hover td { background: #f9f9f7; }

    .badge {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 36px;
      height: 36px;
      border-radius: 50%;
      font-weight: 500;
      font-size: 17px;
    }

    .badge-s { background: #EEEDFE; color: #3C3489; }
    .badge-o { background: #E1F5EE; color: #085041; }
    .badge-l { background: #E6F1FB; color: #0C447C; }
    .badge-i { background: #FAEEDA; color: #633806; }
    .badge-d { background: #FAECE7; color: #712B13; }

    .name-ko {
      font-weight: 500;
      font-size: 14px;
      color: #1a1a18;
    }

    .name-en {
      font-size: 12px;
      color: #888780;
      margin-top: 2px;
    }

    .core-text {
      color: #2c2c2a;
      font-size: 14px;
    }

    @media (prefers-color-scheme: dark) {
      body { background: #1a1a18; }
      .card { background: #242422; border-color: rgba(255,255,255,0.1); }
      .card-title { color: #f1efe8; }
      .card-sub { color: #5f5e5a; }
      thead th { color: #5f5e5a; border-bottom-color: rgba(255,255,255,0.12); }
      tbody td { border-bottom-color: rgba(255,255,255,0.07); }
      tbody tr:hover td { background: #2e2e2c; }
      .badge-s { background: #3C3489; color: #CECBF6; }
      .badge-o { background: #085041; color: #9FE1CB; }
      .badge-l { background: #0C447C; color: #B5D4F4; }
      .badge-i { background: #633806; color: #FAC775; }
      .badge-d { background: #712B13; color: #F5C4B3; }
      .name-ko { color: #f1efe8; }
      .name-en { color: #5f5e5a; }
      .core-text { color: #d3d1c7; }
    }
  &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;div class=&amp;quot;card&amp;quot;&amp;gt;
    &amp;lt;table&amp;gt;
      &amp;lt;thead&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;th style=&amp;quot;width: 52px;&amp;quot;&amp;gt;원칙&amp;lt;/th&amp;gt;
          &amp;lt;th style=&amp;quot;width: 220px;&amp;quot;&amp;gt;이름&amp;lt;/th&amp;gt;
          &amp;lt;th&amp;gt;핵심 내용&amp;lt;/th&amp;gt;
        &amp;lt;/tr&amp;gt;
      &amp;lt;/thead&amp;gt;
      &amp;lt;tbody&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;td&amp;gt;&amp;lt;span class=&amp;quot;badge badge-s&amp;quot;&amp;gt;S&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;
            &amp;lt;div class=&amp;quot;name-ko&amp;quot;&amp;gt;단일 책임 원칙&amp;lt;/div&amp;gt;
            &amp;lt;div class=&amp;quot;name-en&amp;quot;&amp;gt;Single Responsibility Principle&amp;lt;/div&amp;gt;
          &amp;lt;/td&amp;gt;
          &amp;lt;td class=&amp;quot;core-text&amp;quot;&amp;gt;클래스는 단 하나의 목적을 가져야 하며, 클래스를 변경하는 이유는 단 하나의 이유여야 한다.&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;td&amp;gt;&amp;lt;span class=&amp;quot;badge badge-o&amp;quot;&amp;gt;O&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;
            &amp;lt;div class=&amp;quot;name-ko&amp;quot;&amp;gt;개방-폐쇄 원칙&amp;lt;/div&amp;gt;
            &amp;lt;div class=&amp;quot;name-en&amp;quot;&amp;gt;Open/Closed Principle&amp;lt;/div&amp;gt;
          &amp;lt;/td&amp;gt;
          &amp;lt;td class=&amp;quot;core-text&amp;quot;&amp;gt;확장에는 열려 있고, 수정에는 닫혀 있어야 한다&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;td&amp;gt;&amp;lt;span class=&amp;quot;badge badge-l&amp;quot;&amp;gt;L&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;
            &amp;lt;div class=&amp;quot;name-ko&amp;quot;&amp;gt;리스코프 치환 원칙&amp;lt;/div&amp;gt;
            &amp;lt;div class=&amp;quot;name-en&amp;quot;&amp;gt;Liskov Substitution Principle&amp;lt;/div&amp;gt;
          &amp;lt;/td&amp;gt;
          &amp;lt;td class=&amp;quot;core-text&amp;quot;&amp;gt;자식 클래스는 부모 클래스를 완전히 대체할 수 있어야 한다&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;td&amp;gt;&amp;lt;span class=&amp;quot;badge badge-i&amp;quot;&amp;gt;I&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;
            &amp;lt;div class=&amp;quot;name-ko&amp;quot;&amp;gt;인터페이스 분리 원칙&amp;lt;/div&amp;gt;
            &amp;lt;div class=&amp;quot;name-en&amp;quot;&amp;gt;Interface Segregation Principle&amp;lt;/div&amp;gt;
          &amp;lt;/td&amp;gt;
          &amp;lt;td class=&amp;quot;core-text&amp;quot;&amp;gt;사용하지 않는 메서드에 의존하면 안 된다&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;td&amp;gt;&amp;lt;span class=&amp;quot;badge badge-d&amp;quot;&amp;gt;D&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;
            &amp;lt;div class=&amp;quot;name-ko&amp;quot;&amp;gt;의존 역전 원칙&amp;lt;/div&amp;gt;
            &amp;lt;div class=&amp;quot;name-en&amp;quot;&amp;gt;Dependency Inversion Principle&amp;lt;/div&amp;gt;
          &amp;lt;/td&amp;gt;
          &amp;lt;td class=&amp;quot;core-text&amp;quot;&amp;gt;추상화에 의존해야 한다 &amp;mdash; 스프링 DI가 대표 예시&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
      &amp;lt;/tbody&amp;gt;
    &amp;lt;/table&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&quot;&gt;
&lt;style&gt;
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans KR', sans-serif;
      background: #f5f5f3;
      display: flex;
      justify-content: center;
      align-items: flex-start;
      min-height: 100vh;
      padding: 3rem 1rem;
      color: #1a1a18;
    }

    .card {
      background: #ffffff;
      border-radius: 14px;
      border: 0.5px solid rgba(0,0,0,0.1);
      padding: 2rem 2rem 1rem;
      width: 100%;
      max-width: 780px;
    }

    .card-title {
      font-size: 22px;
      font-weight: 500;
      letter-spacing: -0.3px;
      color: #1a1a18;
      margin-bottom: 0.25rem;
    }

    .card-sub {
      font-size: 13px;
      color: #888780;
      margin-bottom: 1.75rem;
    }

    table {
      width: 100%;
      border-collapse: collapse;
      font-size: 14px;
    }

    thead th {
      text-align: left;
      font-weight: 500;
      font-size: 12px;
      color: #888780;
      padding: 0 16px 10px;
      border-bottom: 1px solid rgba(0,0,0,0.15);
      text-transform: uppercase;
      letter-spacing: 0.04em;
    }

    tbody td {
      padding: 15px 16px;
      border-bottom: 0.5px solid rgba(0,0,0,0.07);
      vertical-align: top;
      line-height: 1.6;
    }

    tbody tr:last-child td { border-bottom: none; }

    tbody tr:hover td { background: #f9f9f7; }

    .badge {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 36px;
      height: 36px;
      border-radius: 50%;
      font-weight: 500;
      font-size: 17px;
    }

    .badge-s { background: #EEEDFE; color: #3C3489; }
    .badge-o { background: #E1F5EE; color: #085041; }
    .badge-l { background: #E6F1FB; color: #0C447C; }
    .badge-i { background: #FAEEDA; color: #633806; }
    .badge-d { background: #FAECE7; color: #712B13; }

    .name-ko {
      font-weight: 500;
      font-size: 14px;
      color: #1a1a18;
    }

    .name-en {
      font-size: 12px;
      color: #888780;
      margin-top: 2px;
    }

    .core-text {
      color: #2c2c2a;
      font-size: 14px;
    }

    @media (prefers-color-scheme: dark) {
      body { background: #1a1a18; }
      .card { background: #242422; border-color: rgba(255,255,255,0.1); }
      .card-title { color: #f1efe8; }
      .card-sub { color: #5f5e5a; }
      thead th { color: #5f5e5a; border-bottom-color: rgba(255,255,255,0.12); }
      tbody td { border-bottom-color: rgba(255,255,255,0.07); }
      tbody tr:hover td { background: #2e2e2c; }
      .badge-s { background: #3C3489; color: #CECBF6; }
      .badge-o { background: #085041; color: #9FE1CB; }
      .badge-l { background: #0C447C; color: #B5D4F4; }
      .badge-i { background: #633806; color: #FAC775; }
      .badge-d { background: #712B13; color: #F5C4B3; }
      .name-ko { color: #f1efe8; }
      .name-en { color: #5f5e5a; }
      .core-text { color: #d3d1c7; }
    }
  &lt;/style&gt;
&lt;div class=&quot;card&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;width: 52px;&quot;&gt;원칙&lt;/th&gt;
&lt;th style=&quot;width: 220px;&quot;&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;span class=&quot;badge badge-s&quot;&gt;S&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;
&lt;div class=&quot;name-ko&quot;&gt;단일 책임 원칙&lt;/div&gt;
&lt;div class=&quot;name-en&quot;&gt;Single Responsibility Principle&lt;/div&gt;
&lt;/td&gt;
&lt;td class=&quot;core-text&quot;&gt;클래스는 단 하나의 목적을 가져야 하며, 클래스를 변경하는 이유는 단 하나의 이유여야 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class=&quot;badge badge-o&quot;&gt;O&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;
&lt;div class=&quot;name-ko&quot;&gt;개방-폐쇄 원칙&lt;/div&gt;
&lt;div class=&quot;name-en&quot;&gt;Open/Closed Principle&lt;/div&gt;
&lt;/td&gt;
&lt;td class=&quot;core-text&quot;&gt;확장에는 열려 있고, 수정에는 닫혀 있어야 한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class=&quot;badge badge-l&quot;&gt;L&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;
&lt;div class=&quot;name-ko&quot;&gt;리스코프 치환 원칙&lt;/div&gt;
&lt;div class=&quot;name-en&quot;&gt;Liskov Substitution Principle&lt;/div&gt;
&lt;/td&gt;
&lt;td class=&quot;core-text&quot;&gt;자식 클래스는 부모 클래스를 완전히 대체할 수 있어야 한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class=&quot;badge badge-i&quot;&gt;I&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;
&lt;div class=&quot;name-ko&quot;&gt;인터페이스 분리 원칙&lt;/div&gt;
&lt;div class=&quot;name-en&quot;&gt;Interface Segregation Principle&lt;/div&gt;
&lt;/td&gt;
&lt;td class=&quot;core-text&quot;&gt;사용하지 않는 메서드에 의존하면 안 된다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class=&quot;badge badge-d&quot;&gt;D&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;
&lt;div class=&quot;name-ko&quot;&gt;의존 역전 원칙&lt;/div&gt;
&lt;div class=&quot;name-en&quot;&gt;Dependency Inversion Principle&lt;/div&gt;
&lt;/td&gt;
&lt;td class=&quot;core-text&quot;&gt;추상화에 의존해야 한다 &amp;mdash; 스프링 DI가 대표 예시&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&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;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;Q. 추상 클래스와 인터페이스의 차이는 무엇인가요?&lt;/span&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;&amp;nbsp;&lt;/p&gt;
&lt;div id=&quot;code_1775738729448&quot; data-ke-type=&quot;html&quot; data-source=&quot;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;ko&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;gt;
  &amp;lt;title&amp;gt;추상 클래스 vs 인터페이스&amp;lt;/title&amp;gt;
  &amp;lt;style&amp;gt;
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans KR', sans-serif;
      background: #f5f5f3;
      display: flex;
      justify-content: center;
      align-items: flex-start;
      min-height: 100vh;
      padding: 3rem 1rem;
      color: #1a1a18;
    }

    .card {
      background: #ffffff;
      border-radius: 14px;
      border: 0.5px solid rgba(0,0,0,0.1);
      padding: 1.75rem 2rem 1rem;
      width: 100%;
      max-width: 740px;
    }

    table {
      width: 100%;
      border-collapse: collapse;
      font-size: 14px;
    }

    thead th {
      text-align: left;
      font-weight: 500;
      font-size: 13px;
      color: #888780;
      padding: 0 16px 10px;
      border-bottom: 1px solid rgba(0,0,0,0.15);
    }

    tbody td {
      padding: 14px 16px;
      border-bottom: 0.5px solid rgba(0,0,0,0.07);
      vertical-align: middle;
      line-height: 1.6;
      color: #2c2c2a;
    }

    tbody tr:last-child td { border-bottom: none; }
    tbody tr:hover td { background: #f9f9f7; }

    .row-label {
      font-weight: 500;
      color: #888780;
      font-size: 13px;
    }

    code {
      font-family: 'SFMono-Regular', Consolas, monospace;
      font-size: 12px;
      background: #FCEBEB;
      color: #A32D2D;
      padding: 2px 6px;
      border-radius: 4px;
    }

    @media (prefers-color-scheme: dark) {
      body { background: #1a1a18; }
      .card { background: #242422; border-color: rgba(255,255,255,0.1); }
      thead th { color: #5f5e5a; border-bottom-color: rgba(255,255,255,0.12); }
      tbody td { border-bottom-color: rgba(255,255,255,0.07); color: #d3d1c7; }
      tbody tr:hover td { background: #2e2e2c; }
      .row-label { color: #5f5e5a; }
      code { background: #501313; color: #F7C1C1; }
    }
  &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;div class=&amp;quot;card&amp;quot;&amp;gt;
    &amp;lt;table&amp;gt;
      &amp;lt;thead&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;th style=&amp;quot;width: 130px;&amp;quot;&amp;gt;구분&amp;lt;/th&amp;gt;
          &amp;lt;th style=&amp;quot;width: 45%;&amp;quot;&amp;gt;추상 클래스&amp;lt;/th&amp;gt;
          &amp;lt;th&amp;gt;인터페이스&amp;lt;/th&amp;gt;
        &amp;lt;/tr&amp;gt;
      &amp;lt;/thead&amp;gt;
      &amp;lt;tbody&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;td class=&amp;quot;row-label&amp;quot;&amp;gt;구현 포함&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;O (일부 가능)&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;X (Java 8+ &amp;lt;code&amp;gt;default&amp;lt;/code&amp;gt; 제외)&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;td class=&amp;quot;row-label&amp;quot;&amp;gt;상속/구현 수&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;단일 상속&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;다중 구현 가능&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;td class=&amp;quot;row-label&amp;quot;&amp;gt;상태(필드)&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;가질 수 있음&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;가질 수 없음&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;td class=&amp;quot;row-label&amp;quot;&amp;gt;관계&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;IS-A&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;CAN-DO (능력 정의)&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;td class=&amp;quot;row-label&amp;quot;&amp;gt;사용 시기&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;공통 상태/구현 공유&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;관련 없는 클래스에 공통 행동 부여&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
      &amp;lt;/tbody&amp;gt;
    &amp;lt;/table&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&quot;&gt;
&lt;style&gt;
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans KR', sans-serif;
      background: #f5f5f3;
      display: flex;
      justify-content: center;
      align-items: flex-start;
      min-height: 100vh;
      padding: 3rem 1rem;
      color: #1a1a18;
    }

    .card {
      background: #ffffff;
      border-radius: 14px;
      border: 0.5px solid rgba(0,0,0,0.1);
      padding: 1.75rem 2rem 1rem;
      width: 100%;
      max-width: 740px;
    }

    table {
      width: 100%;
      border-collapse: collapse;
      font-size: 14px;
    }

    thead th {
      text-align: left;
      font-weight: 500;
      font-size: 13px;
      color: #888780;
      padding: 0 16px 10px;
      border-bottom: 1px solid rgba(0,0,0,0.15);
    }

    tbody td {
      padding: 14px 16px;
      border-bottom: 0.5px solid rgba(0,0,0,0.07);
      vertical-align: middle;
      line-height: 1.6;
      color: #2c2c2a;
    }

    tbody tr:last-child td { border-bottom: none; }
    tbody tr:hover td { background: #f9f9f7; }

    .row-label {
      font-weight: 500;
      color: #888780;
      font-size: 13px;
    }

    code {
      font-family: 'SFMono-Regular', Consolas, monospace;
      font-size: 12px;
      background: #FCEBEB;
      color: #A32D2D;
      padding: 2px 6px;
      border-radius: 4px;
    }

    @media (prefers-color-scheme: dark) {
      body { background: #1a1a18; }
      .card { background: #242422; border-color: rgba(255,255,255,0.1); }
      thead th { color: #5f5e5a; border-bottom-color: rgba(255,255,255,0.12); }
      tbody td { border-bottom-color: rgba(255,255,255,0.07); color: #d3d1c7; }
      tbody tr:hover td { background: #2e2e2c; }
      .row-label { color: #5f5e5a; }
      code { background: #501313; color: #F7C1C1; }
    }
  &lt;/style&gt;
&lt;div class=&quot;card&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;width: 130px;&quot;&gt;구분&lt;/th&gt;
&lt;th style=&quot;width: 45%;&quot;&gt;추상 클래스&lt;/th&gt;
&lt;th&gt;인터페이스&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;row-label&quot;&gt;구현 포함&lt;/td&gt;
&lt;td&gt;O (일부 가능)&lt;/td&gt;
&lt;td&gt;X (Java 8+ &lt;code&gt;default&lt;/code&gt; 제외)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;row-label&quot;&gt;상속/구현 수&lt;/td&gt;
&lt;td&gt;단일 상속&lt;/td&gt;
&lt;td&gt;다중 구현 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;row-label&quot;&gt;상태(필드)&lt;/td&gt;
&lt;td&gt;가질 수 있음&lt;/td&gt;
&lt;td&gt;가질 수 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;row-label&quot;&gt;관계&lt;/td&gt;
&lt;td&gt;IS-A&lt;/td&gt;
&lt;td&gt;CAN-DO (능력 정의)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;row-label&quot;&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;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java&amp;nbsp;8&amp;nbsp;default&amp;nbsp;메서드가&amp;nbsp;생긴&amp;nbsp;이유:&amp;nbsp;기존&amp;nbsp;인터페이스에&amp;nbsp;메서드를&amp;nbsp;추가하면&amp;nbsp;모든&amp;nbsp;구현체를&amp;nbsp;수정해야&amp;nbsp;하는&amp;nbsp;문제를&amp;nbsp;해결하기&amp;nbsp;위함.&amp;nbsp;하위&amp;nbsp;호환성&amp;nbsp;유지가&amp;nbsp;목적입니다.&lt;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&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;/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;인터페이스끼리&amp;nbsp;default&amp;nbsp;메서드가&amp;nbsp;충돌하면&amp;nbsp;어떻게&amp;nbsp;되나요?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 구현 클래스에서 반드시 오버라이딩해야 합니다. 안 하면 컴파일 에러.&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;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;Q. 상속과 컴포지션, 무엇을 선호하나요?&lt;/span&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상속 (Inheritance)&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;IS-A 관계. &lt;b&gt;부모의 구현을 물려받음&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;❌ 부모에 강하게 결합 &amp;rarr; 부모 변경 시 자식 영향&lt;/li&gt;
&lt;li&gt;❌ 캡슐화가 깨질 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;컴포지션 (Composition)&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;HAS-A 관계. &lt;b&gt;다른 객체를 필드로 가져 기능을 위임&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;✅ 인터페이스에만 의존 &amp;rarr; 유연성 높음&lt;/li&gt;
&lt;li&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;GoF 원칙: &lt;b&gt;&quot;Favor composition over inheritance&quot;&lt;/b&gt; 명확한 IS-A 관계가 아닌 이상 컴포지션을 우선 선택합니다.&lt;/p&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;&lt;b&gt;나쁜 예시&lt;/b&gt;: Java의 Stack이 Vector를 상속하는 구조 &amp;rarr; Stack은 Vector의 모든 메서드를 노출할 필요가 없어서 컴포지션으로 구현했어야 합니다.&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;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 명확한 IS-A 관계이고, 부모 API를 그대로 노출할 의도가 있으며, 부모 변경 가능성이 낮을 때.&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;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;Q. equals()와 hashCode()의 관계를 설명해 주세요.&lt;/span&gt;&lt;b&gt;&lt;/b&gt;&lt;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;equals()&lt;/b&gt; : 두 객체의 논리적 동등성(내용)을 비교합니다. ==은 참조(주소)를 비교, equals()는 값을 비교합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;hashCode()&lt;/b&gt; : 객체를 정수로 변환한 해시값. HashMap / HashSet 같은 해시 기반 컬렉션에서 버킷을 찾는 데 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;&lt;span&gt;⚠️&lt;/span&gt; 핵심 계약&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;equals()가 true &amp;rarr; hashCode()도 반드시 같아야 한다
(반대는 성립하지 않아도 됨 &amp;mdash; 해시 충돌 허용)
&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;rarr; HashMap에서 같은 키를 찾지 못하는 버그 발생&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;equals()를 재정의할 때는 반드시 hashCode()도 함께 재정의해야 합니다.&lt;/b&gt;&lt;/p&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;h4 data-ke-size=&quot;size20&quot;&gt;String의&amp;nbsp;==과&amp;nbsp;equals()&amp;nbsp;차이는?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; &lt;b&gt;==&lt;/b&gt;은 참조 비교, &lt;b&gt;equals()&lt;/b&gt;는 내용 비교. 리터럴은 String Pool에서 같은 참조를 쓰지만 new String()은 항상 새 객체를 만듭니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;471&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dirRAw/dJMb99TwOqX/ShoisJjyxAHxWnkyKnRYR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dirRAw/dJMb99TwOqX/ShoisJjyxAHxWnkyKnRYR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dirRAw/dJMb99TwOqX/ShoisJjyxAHxWnkyKnRYR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdirRAw%2FdJMb99TwOqX%2FShoisJjyxAHxWnkyKnRYR1%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;800&quot; height=&quot;471&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;471&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;Q. JVM 메모리 구조(Heap, Stack, Method Area)를 설명해 주세요.&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Method Area (메서드 영역)&lt;/b&gt;:&amp;nbsp; 클래스 정보, static 변수, 상수, 메서드 코드가 저장되는 영역입니다.JVM이 클래스를 로드할 때 이곳에 저장되며 모든 스레드가 공유합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Heap (힙)&lt;/b&gt;: 객체 인스턴스와 배열이 저장되는 영역입니다.GC(가비지 컬렉터)가 관리하며 모든 스레드가 공유합니다.Young/Old Generation으로 나뉩니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Young Generation: 새로 생성된 객체가 저장되는 곳. Minor GC 발생&lt;/li&gt;
&lt;li&gt;Old Generation: Young에서 오래 살아남은 객체가 이동. Major GC 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Stack (스택)&lt;/b&gt;: 메서드 호출 시 스택 프레임이 쌓이는 영역입니다.지역 변수, 매개변수, 리턴 주소가 저장됩니다. 스레드마다 독립적으로 생성됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;661&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8f04c/dJMcacJwhv4/KZzz63HvBVObooA6rvSoO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8f04c/dJMcacJwhv4/KZzz63HvBVObooA6rvSoO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8f04c/dJMcacJwhv4/KZzz63HvBVObooA6rvSoO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8f04c%2FdJMcacJwhv4%2FKZzz63HvBVObooA6rvSoO1%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;800&quot; height=&quot;661&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;661&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;GC가 Heap만 관리하는 이유는?&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; Stack은 메서드가 끝나면 자동으로 해제되고, Method Area의 클래스 정보는 명시적으로 언로드되기 때문입니다. 생성/소멸이 예측 불가능한 객체를 담은 Heap만 GC가 관리합니다.&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;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;Q. JRE, JDK, JVM의 차이를 설명해 주세요.&lt;/span&gt;&lt;/h3&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;b&gt;JVM (Java Virtual Machine):&lt;/b&gt;&amp;nbsp;자바 바이트코드를 OS에 맞게 해석하고 실행하는 가상 머신입니다. &quot;Write Once, Run Anywhere&quot;를 가능하게 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;JRE (Java Runtime Environment):&lt;/b&gt; JVM + 표준 라이브러리(java.lang, java.util 등)의 묶음입니다. 자바 프로그램을 실행하는 데 필요한 최소 환경입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;JDK (Java Development Kit):&lt;/b&gt; JRE + 개발 도구(javac 컴파일러, javadoc, debugger 등)의 묶음입니다. 자바 프로그램을 개발할 때 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포 서버에는 JRE만, 개발 환경에는 JDK가 필요합니다. Java 11부터 JRE 단독 배포가 없어지고 JDK만 제공됩니다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;606&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ea5fOy/dJMcaaEV7w0/wb31TvutyUmfLgIWwUvb0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ea5fOy/dJMcaaEV7w0/wb31TvutyUmfLgIWwUvb0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ea5fOy/dJMcaaEV7w0/wb31TvutyUmfLgIWwUvb0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fea5fOy%2FdJMcaaEV7w0%2Fwb31TvutyUmfLgIWwUvb0k%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;994&quot; height=&quot;606&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;606&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/blockquote&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;span data-token-index=&quot;0&quot;&gt;&quot;Write Once, Run Anywhere&quot;가 어떻게 가능한가요?&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;-&amp;gt; 자바 소스코드는 OS 독립적인 바이트코드(.class)로 컴파일되고, 각 OS에 맞는 JVM이 이 바이트코드를 해석해서 실행하기 때문입니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;JIT 컴파일러가 뭔가요?&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;-&amp;gt; JVM 내부의 최적화 장치입니다. 자주 실행되는 바이트코드를 런타임에 네이티브 코드로 변환해서 성능을 높입니다.&lt;/span&gt;&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;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;Q. Java 코드가 컴파일되고 실행되는 과정을 설명해 주세요. ClassLoader도 포함해서요.&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java 실행 과정은 크게 컴파일과 실행으로 나뉩니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;먼저 javac 컴파일러가 .java 파일을 플랫폼 독립적인 바이트코드인 .class 파일로 변환합니다.&lt;/li&gt;
&lt;li&gt;실행 시점에는 ClassLoader가 .class 파일을 JVM의 Method Area에 로드합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ClassLoader는 Loading, Linking, Initialization 세 단계를 거치며 특히 static 변수 초기화와 static 블록 실행이 이 시점에 일어납니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;로드된 바이트코드는 인터프리터가 한 줄씩 실행하고, JIT 컴파일러가 자주 실행되는 코드를 네이티브 코드로 변환해서 성능을 최적화합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;① 소스코드 작성  &amp;rarr;  Hello.java
② javac 컴파일  &amp;rarr;  Hello.class (바이트코드)
③ ClassLoader   &amp;rarr;  .class 파일을 JVM 메모리에 로드
④ Bytecode Verifier  &amp;rarr;  바이트코드 유효성 검증
⑤ 인터프리터 / JIT  &amp;rarr;  바이트코드를 네이티브 코드로 실행
&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;b&gt;ClassLoader 역할:&lt;/b&gt;.class 파일을 읽어 Method Area에 올리는 역할입니다. 세 단계로 동작합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Loading: .class 파일을 메모리에 읽어들임&lt;/li&gt;
&lt;li&gt;Linking: 검증(Verify) &amp;rarr; 준비(Prepare) &amp;rarr; 분석(Resolve)&lt;/li&gt;
&lt;li&gt;Initialization: static 변수 초기화, static 블록 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ClassLoader 계층 구조:&lt;/b&gt; Bootstrap &amp;rarr; Extension &amp;rarr; Application 순으로 위임 방식(Delegation)으로 동작합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1028&quot; data-origin-height=&quot;946&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b46q7w/dJMcacpcdMI/36oUdSv7wsKLp3XV6N9xU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b46q7w/dJMcacpcdMI/36oUdSv7wsKLp3XV6N9xU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b46q7w/dJMcacpcdMI/36oUdSv7wsKLp3XV6N9xU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb46q7w%2FdJMcacpcdMI%2F36oUdSv7wsKLp3XV6N9xU1%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;1028&quot; height=&quot;946&quot; data-origin-width=&quot;1028&quot; data-origin-height=&quot;946&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;인터프리터와 JIT의 차이는?&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;-&amp;gt; 인터프리터는 바이트코드를 한 줄씩 번역해서 실행하고, JIT은 자주 쓰이는 코드를 미리 네이티브 코드로 컴파일해서 캐시합니다. JIT 덕분에 초기 실행 이후 속도가 빨라집니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;static 블록은 언제 실행되나요?&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;-&amp;gt; 클래스가 처음 로드될 때 단 한 번 실행됩니다. 인스턴스 생성 이전에 실행되므로 클래스 초기화 용도로 사용합니다.&lt;/span&gt;&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;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;Java는 Call by Value인가요, Call by Reference인가요?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Java는 항상 Call by Value입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 전달되는 &quot;값&quot;이 무엇이냐에 따라 헷갈릴 수 있습니다.&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;기본형(primitive)&lt;/b&gt;: 값 자체가 복사되어 전달됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;참조형(reference)&lt;/b&gt;: 객체의 참조(주소)가 복사되어 전달됩니다. 참조를 통해 객체 내부를 변경하면 원본에 반영되지만, 참조 자체를 다른 객체로 바꿔도 원본 참조는 변하지 않습니다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;444&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDlTAZ/dJMcaibTDyH/MedfKzJOemsRuL7IGIsvzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDlTAZ/dJMcaibTDyH/MedfKzJOemsRuL7IGIsvzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDlTAZ/dJMcaibTDyH/MedfKzJOemsRuL7IGIsvzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDlTAZ%2FdJMcaibTDyH%2FMedfKzJOemsRuL7IGIsvzk%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;1016&quot; height=&quot;444&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;444&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참조형 - 필드 변경&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vvzTa/dJMcajoeS8c/RTF0TTYbKGdpYnDMvAvSmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vvzTa/dJMcajoeS8c/RTF0TTYbKGdpYnDMvAvSmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vvzTa/dJMcajoeS8c/RTF0TTYbKGdpYnDMvAvSmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvvzTa%2FdJMcajoeS8c%2FRTF0TTYbKGdpYnDMvAvSmK%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;1010&quot; height=&quot;476&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참조형 - 참조 교체&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uNsE7/dJMb990kV08/g5fejBGcITX9IZuu1VAgA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uNsE7/dJMb990kV08/g5fejBGcITX9IZuu1VAgA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uNsE7/dJMb990kV08/g5fejBGcITX9IZuu1VAgA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuNsE7%2FdJMb990kV08%2Fg5fejBGcITX9IZuu1VAgA0%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;1008&quot; height=&quot;562&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;562&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;C++의 Call by Reference와 Java의 차이는?&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; C++은 변수 자체의 참조를 전달해서 참조를 교체해도 원본이 바뀝니다. Java는 참조의 복사본을 전달하므로 교체해도 원본에 영향이 없습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dn9vsg/dJMcahqvSpi/2KcQ0Sf33eRy8DOk1YISRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dn9vsg/dJMcahqvSpi/2KcQ0Sf33eRy8DOk1YISRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dn9vsg/dJMcahqvSpi/2KcQ0Sf33eRy8DOk1YISRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdn9vsg%2FdJMcahqvSpi%2F2KcQ0Sf33eRy8DOk1YISRK%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;942&quot; height=&quot;506&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;506&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;Q. Mutable과 Immutable의 차이는 무엇이고, String은 왜 Immutable인가요?&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mutable (가변): 생성 후 상태를 변경할 수 있는 객체. 예) StringBuilder, ArrayList&lt;/li&gt;
&lt;li&gt;Immutable (불변): 생성 후 상태를 변경할 수 없는 객체. 예) String, Integer, LocalDate&lt;/li&gt;
&lt;li&gt;String이 Immutable인 이유:
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;String Pool 재사용 가능: 같은 문자열을 여러 변수가 공유하는데, 한 곳에서 바꾸면 다른 곳도 영향받으면 안 되기 때문입니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Thread-safe: 값이 변하지 않으므로 동기화 없이도 멀티스레드 환경에서 안전합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;HashCode&amp;nbsp;캐싱: HashMap의 키로 자주 쓰이는데, 값이 불변이어야 해시값도 일관되게 유지됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;String s = &quot;hello&quot;;
s = s + &quot; world&quot;;  // 새로운 &quot;hello world&quot; 객체 생성, 기존 &quot;hello&quot;는 그대로
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 반복적인 문자열 연결이 필요하면 &lt;b&gt;StringBuilder&lt;/b&gt; (단일 스레드) 또는 &lt;b&gt;StringBuffer&lt;/b&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;span data-token-index=&quot;0&quot;&gt;StringBuilder와 StringBuffer의 차이는?&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 둘 다 가변 문자열 객체지만, StringBuffer는 메서드에 synchronized가 붙어있어 멀티스레드 환경에서 안전합니다. 단일 스레드에서는 오버헤드가 없는 StringBuilder가 더 빠릅니다.&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;span data-token-index=&quot;0&quot;&gt;String + 연산이 느린 이유는?&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;String은 불변이라 + 할 때마다 새 String 객체가 Heap에 생성됩니다. n번 이어 붙이면 n개의 객체가 생기고 GC 부담도 늘어납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 연산시 과정내부적으로 이렇게 됨&lt;/p&gt;
&lt;pre id=&quot;code_1775742228459&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;s = new StringBuilder(&quot;hello&quot;) .append(&quot; world&quot;) .toString(); //&amp;larr; 여기서 new String() 호출
String s = &quot;hello&quot;; s = s + &quot; world&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;String Pool이 아닌 일반 Heap&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;Q. Java Collection Framework 구조를 설명해 주세요. List, Set, Map의 차이도 포함해서요.&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1775742293086&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Collection
├── List     순서 O, 중복 O
│   ├── ArrayList
│   ├── LinkedList
│   └── Vector (레거시)
├── Set      순서 X, 중복 X
│   ├── HashSet
│   ├── LinkedHashSet  (입력 순서 유지)
│   └── TreeSet        (정렬 순서 유지)
└── Queue    선입선출
    ├── LinkedList
    └── ArrayDeque  (권장)

Map  (Collection 상속 아님, 별도 계층)
├── HashMap         (순서 X, 빠름)
├── LinkedHashMap   (입력 순서 유지)
└── TreeMap         (키 정렬 순서 유지)&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;b&gt;List&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;ArrayList: 내부적으로 배열. 조회 O(1), 중간 삽입/삭제 O(n)&lt;/li&gt;
&lt;li&gt;LinkedList: 노드 연결. 중간 삽입/삭제 O(1), 조회 O(n)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Set&lt;/b&gt;: 순서가 없고 중복을 허용하지 않습니다. equals()와 hashCode()로 중복 판단합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HashSet: 가장 빠름. 내부적으로 HashMap 사용&lt;/li&gt;
&lt;li&gt;TreeSet: 정렬 상태 유지. Red-Black Tree 기반&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Map&lt;/b&gt;: Key-Value 쌍으로 저장. Key는 중복 불가, Value는 중복 가능합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HashMap: 가장 많이 씀. 평균 O(1) 조회&lt;/li&gt;
&lt;li&gt;TreeMap: 키 기준 정렬. O(log n) 조회&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Map은 Collection 인터페이스를 상속받지 않습니다. 별도 계층입니다.&lt;/p&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;ArrayList vs LinkedList 언제 각각 쓰나요?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조회가 많으면 ArrayList, 앞/중간 삽입&amp;middot;삭제가 많으면 LinkedList.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 실무에서는 대부분 ArrayList가 더 빠릅니다. LinkedList는 노드마다 포인터를 저장해 메모리도 더 쓰고, 캐시 지역성도 나쁘기 때문입니다.&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;HashSet이 중복을 어떻게 판단하나요?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hashCode()로 버킷 위치를 찾고, 같은 버킷에 있는 요소와 equals()로 비교합니다. 둘 다 같으면 중복으로 판단해 추가하지 않습니다.&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;Map이 Collection을 상속받지 않는 이유는?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Collection은 단일 요소를 다루는 인터페이스인데, Map은 Key-Value 쌍을 다루는 구조라 설계가 근본적으로 달라서 별도 계층으로 분리되었습니다.&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;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;Q. HashMap의 내부 동작 원리를 설명해 주세요. 충돌은 어떻게 해결하나요?&lt;/span&gt;&lt;/h3&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;HashMap은 내부적으로 &lt;b&gt;배열(버킷) + 연결리스트(또는 트리)&lt;/b&gt; 구조입니다.&lt;/p&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;버킷 배열 (기본 크기 16)
index 0  &amp;rarr;  null
index 1  &amp;rarr;  [Key:&quot;apple&quot;, Value:1] &amp;rarr; [Key:&quot;apricot&quot;, Value:2]  (충돌 시 연결)
index 2  &amp;rarr;  null
index 3  &amp;rarr;  [Key:&quot;banana&quot;, Value: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;&lt;b&gt;put(key, value) 과정&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;① key.hashCode() 호출          &amp;rarr; 정수 해시값 생성
② hash % 배열크기              &amp;rarr; 버킷 인덱스 결정
③ 해당 버킷에 저장
   - 비어있으면 바로 저장
   - 이미 있으면 (충돌) &amp;rarr; equals()로 키 비교
     - 같은 키면 Value 덮어씀
     - 다른 키면 연결리스트로 이어붙임
&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;b&gt;충돌 해결 &amp;mdash; Separate Chaining&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;Java 8부터 연결리스트 길이가 &lt;b&gt;8 이상&lt;/b&gt;이 되면 &lt;b&gt;Red-Black Tree&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;연결리스트: O(n) 탐색&lt;/li&gt;
&lt;li&gt;Red-Black Tree: O(log n) 탐색&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Load Factor &amp;amp; Rehashing&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Load Factor = 저장된 요소 수 / 버킷 크기
기본값: 0.75
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장 요소가 전체 버킷의 75%를 넘으면 배열 크기를 2배로 늘리고 모든 요소를 재배치(Rehashing)합니다. 이 작업은 O(n)으로 비용이 크기 때문에, 저장할 데이터 크기를 미리 알면 초기 용량을 지정하는 게 좋습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;equals()와 hashCode()를 반드시 같이 재정의해야 하는 이유가 HashMap과 어떻게 연결되나요?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hashCode()가 다르면 버킷 위치가 달라서 같은 키로 넣어도 다른 버킷에 저장됩니다. 꺼낼 때 엉뚱한 버킷을 뒤져서 못 찾는 버그가 생깁니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;HashMap은 Thread-safe한가요?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아닙니다. 멀티스레드 환경에서 동시에 Rehashing이 일어나면 무한루프가 생길 수 있습니다. Thread-safe가 필요하면 ConcurrentHashMap을 사용합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;HashMap의 평균 시간복잡도가 O(1)인 이유는?&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시 충돌이 없다고 가정하면 hashCode()로 버킷을 바로 찾아서 O(1)입니다. 충돌이 많아지면 O(n) 또는 트리화 후 O(log n)으로 떨어집니다.&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;Rehashing은 O(n) 비용이 드는 작업입니다. 데이터 크기를 미리 알면 new HashMap&amp;lt;&amp;gt;(initialCapacity)로 지정해서 Rehashing 횟수를 줄일 수 있습니다.&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;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;Q. String, StringBuilder, StringBuffer의 차이를 설명해 주세요.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;String &amp;mdash; Immutable (불변)&lt;/b&gt; 한 번 생성하면 값을 변경할 수 없습니다. + 연산 시마다 새 객체가 Heap에 생성됩니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;String s = &quot;hello&quot;;
s = s + &quot; world&quot;;  // 새 객체 생성, 기존 &quot;hello&quot;는 GC 대상
&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;b&gt;StringBuilder &amp;mdash; Mutable, Thread-unsafe&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;StringBuffer &amp;mdash; Mutable, Thread-safe&lt;/b&gt; StringBuilder와 동일하지만 메서드에 synchronized가 붙어 있어 멀티스레드 환경에서 안전합니다. 동기화 오버헤드가 있어서 단일 스레드에선 StringBuilder보다 느립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;불변 여부&lt;/td&gt;
&lt;td&gt;Immutable&lt;/td&gt;
&lt;td&gt;Mutable&lt;/td&gt;
&lt;td&gt;Mutable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thread-safe&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;X&lt;/td&gt;
&lt;td&gt;O&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;/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;/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;b&gt;StringBuilder&lt;/b&gt;를 씁니다. 멀티스레드 환경에서 문자열을 공유하는 경우는 드물고, 필요하면 다른 동기화 방식을 씁니다.&lt;/p&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;Q. Wrapper Class가 무엇이고, AutoBoxing/Unboxing은 어떻게 동작하나요?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Wrapper Class&lt;/b&gt; 기본형(primitive)을 객체로 감싼 클래스입니다. 컬렉션은 객체만 저장할 수 있기 때문에 필요합니다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;int    &amp;rarr;  Integer
long   &amp;rarr;  Long
double &amp;rarr;  Double
boolean &amp;rarr; Boolean
char   &amp;rarr;  Character
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AutoBoxing / Unboxing&lt;/b&gt; 기본형 &amp;harr; Wrapper 클래스 간 변환을 컴파일러가 자동으로 처리합니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;// AutoBoxing: int &amp;rarr; Integer (컴파일러가 Integer.valueOf(1) 삽입)
Integer a = 1;

// Unboxing: Integer &amp;rarr; int (컴파일러가 a.intValue() 삽입)
int b = a;

// 컬렉션에서 자주 발생
List&amp;lt;Integer&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();
list.add(1);      // AutoBoxing
int x = list.get(0);  // Unboxing
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주의: Integer 캐싱&lt;/b&gt;Integer.valueOf()는 -128 ~ 127 범위의 값을 캐싱합니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Integer a = 127;
Integer b = 127;
System.out.println(a == b);   // true  (캐시된 같은 객체)

Integer c = 128;
Integer d = 128;
System.out.println(c == d);   // false (캐시 범위 초과, 새 객체)
System.out.println(c.equals(d)); // true
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wrapper 객체 비교는 항상 equals()를 사용해야 합니다.&lt;/p&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;Q. GC(Garbage Collection)란 무엇이고, Minor GC와 Full GC의 차이는 무엇인가요?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GC란?&lt;/b&gt; 더 이상 참조되지 않는 객체를 자동으로 메모리에서 해제하는 JVM의 메커니즘입니다.&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;Heap 구조&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;Heap
├── Young Generation  (새 객체)
│   ├── Eden         (new로 생성된 객체가 처음 위치)
│   ├── Survivor 0
│   └── Survivor 1
└── Old Generation   (오래된 객체)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;770&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Opyas/dJMcabDPdFO/iIKqJqZkBKZIhNNDvDOYFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Opyas/dJMcabDPdFO/iIKqJqZkBKZIhNNDvDOYFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Opyas/dJMcabDPdFO/iIKqJqZkBKZIhNNDvDOYFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOpyas%2FdJMcabDPdFO%2FiIKqJqZkBKZIhNNDvDOYFk%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;992&quot; height=&quot;770&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;770&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Minor GC&lt;/b&gt; Young Generation이 꽉 찼을 때 발생합니다. Eden의 살아남은 객체를 Survivor로 이동시킵니다. Survivor에서 일정 횟수(기본 15회) 이상 살아남으면 Old Generation으로 이동(Promotion)합니다.&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;Full GC (Major GC)&lt;/b&gt; Old Generation이 꽉 찼을 때 발생합니다. Heap 전체를 검사하므로 Minor GC보다 훨씬 오래 걸립니다. GC 동안 애플리케이션이 멈추는 &lt;b&gt;STW(Stop The World)&lt;/b&gt; 가 발생합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Full GC는 애플리케이션을 멈추기 때문에 최대한 발생 빈도를 줄여야 합니다.&lt;/p&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;Q. Java에서 스레드를 만드는 방법을 설명해 주세요.&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;방법 1 &amp;mdash; Thread 클래스 상속&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println(&quot;Thread 실행&quot;);
    }
}
new MyThread().start();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;방법 2 &amp;mdash; Runnable 인터페이스 구현 (권장)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;class MyTask implements Runnable {
    @Override
    public void run() {
        System.out.println(&quot;Thread 실행&quot;);
    }
}
new Thread(new MyTask()).start();

// 람다로 더 간결하게
new Thread(() -&amp;gt; System.out.println(&quot;Thread 실행&quot;)).start();
&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;이유 1 &amp;mdash; Java는 단일 상속이라서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유 2 &amp;mdash; 역할 분리(Runnable로 작업만 정의해두면, 나중에 실행 방식을 바꿔도 작업 코드를 건드릴 필요가 없음. OCP랑 연결되는 부분)&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;방법 3 &amp;mdash; ExecutorService (실무 권장)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;ExecutorService executor = Executors.newFixedThreadPool(4);
executor.submit(() -&amp;gt; System.out.println(&quot;Thread 실행&quot;));
executor.shutdown();
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thread를 직접 만드는 것보다 &lt;b&gt;ExecutorService&lt;/b&gt;로 스레드 풀을 관리하는 것이 실무 표준입니다. 스레드 생성 비용이 크기 때문에 재사용하는 것이 효율적입니다.&lt;/p&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;Q. 고유 락(Intrinsic Lock)이란 무엇인가요?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java의 모든 객체는 고유 락, 즉 모니터 락을 하나씩 가지고 있습니다. synchronized 키워드를 사용하면 해당 객체의 고유 락을 획득하고, 블록을 벗어나면 자동으로 반납합니다.&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;Java의 모든 객체는 &lt;b&gt;고유 락(모니터 락)&lt;/b&gt; 을 하나씩 가지고 있습니다. synchronized 키워드를 사용하면 이 락을 획득/해제하는 방식으로 동기화합니다.&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;// 메서드 전체에 락
public synchronized void increment() {
    count++;
}

// 특정 블록에만 락 (더 좁은 범위 &amp;rarr; 더 효율적)
public void increment() {
    synchronized (this) {
        count++;
    }
}
&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;b&gt;동작 방식&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;스레드 A가 synchronized 진입  &amp;rarr;  락 획득
스레드 B가 같은 블록 진입 시도  &amp;rarr;  락 반납될 때까지 대기
스레드 A가 블록 종료  &amp;rarr;  락 반납
스레드 B가 락 획득  &amp;rarr;  진입
&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;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;재진입 가능(Reentrant): 같은 스레드가 이미 가진 락을 다시 획득할 수 있음&lt;/li&gt;
&lt;li&gt;락은 객체 단위: synchronized(this)는 해당 객체, synchronized(MyClass.class)는 클래스 단위&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;synchronized는 간단하지만 성능 병목이 생길 수 있습니다. 실무에서는 ReentrantLock, ConcurrentHashMap 같은 더 세밀한 도구를 많이 씁니다.&lt;/p&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;synchronized의 단점은?&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;락을&amp;nbsp;기다리는&amp;nbsp;스레드가&amp;nbsp;많을수록&amp;nbsp;성능이&amp;nbsp;저하됩니다.&amp;nbsp;또한&amp;nbsp;데드락&amp;nbsp;가능성이&amp;nbsp;있습니다.&amp;nbsp;더&amp;nbsp;세밀한&amp;nbsp;제어가&amp;nbsp;필요하면&amp;nbsp;ReentrantLock을&amp;nbsp;사용합니다.&lt;span data-token-index=&quot;0&quot;&gt;&lt;/span&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;span data-token-index=&quot;0&quot;&gt;데드락이란?&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 스레드가 서로 상대방이 가진 락을 기다리며 영원히 대기하는 상태입니다. A가 락1을 갖고 락2를 기다리고, B가 락2를 갖고 락1을 기다리는 경우입니다.&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;span style=&quot;background-color: #f6e199;&quot; data-token-index=&quot;0&quot;&gt;Q. JVM의 Xms, Xmx 옵션이 무엇인지 설명해 주세요.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Xms는 JVM 시작 시 Heap의 초기 크기, Xmx는 Heap의 최대 크기를 설정하는 옵션입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;-Xms512m   JVM 시작 시 Heap 초기 크기 (Initial heap size)
-Xmx2g     Heap이 늘어날 수 있는 최대 크기 (Maximum heap size)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;java -Xms512m -Xmx2g -jar myapp.jar
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Xms와 Xmx를 같게 설정하는 이유&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;Xms &amp;lt; Xmx 일 때
&amp;rarr; Heap이 부족해지면 JVM이 Heap을 동적으로 늘림
&amp;rarr; 이 과정에서 Full GC 발생 가능
&amp;rarr; 애플리케이션 응답 지연

Xms = Xmx 일 때
&amp;rarr; 처음부터 최대 크기로 할당
&amp;rarr; Heap 크기 조정 과정 없음
&amp;rarr; GC 빈도 감소, 응답 안정적
&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;b&gt;Xms와 Xmx를 동일하게&lt;/b&gt; 설정하는 것이 일반적입니다.&lt;/p&gt;
&lt;/blockquote&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;Xmx를 너무 크게 설정하면 Full GC 발생 시 STW 시간이 길어집니다.&lt;/li&gt;
&lt;li&gt;OS 메모리보다 크게 설정하면 OOM(Out of Memory) 발생합니다.&lt;/li&gt;
&lt;li&gt;Xmx를 너무 작게 설정하면 OutOfMemoryError: Java heap space 발생합니다.&lt;/li&gt;
&lt;/ul&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;span style=&quot;background-color: #f6e199;&quot;&gt;Q. Exception과 Error의 차이는 무엇인가요?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 Throwable을 상속받지만 성격이 다릅니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot; style=&quot;color: #14181f;&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;Throwable
├── Error           JVM 레벨의 심각한 오류 &amp;mdash; 복구 불가
│   ├── OutOfMemoryError
│   ├── StackOverflowError
│   └── VirtualMachineError
└── Exception       애플리케이션 레벨의 오류 &amp;mdash; 복구 가능
    ├── IOException         (Checked)
    ├── SQLException        (Checked)
    └── RuntimeException    (Unchecked)
        ├── NullPointerException
        ├── ArrayIndexOutOfBoundsException
        └── IllegalArgumentException&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;Error&lt;/b&gt; JVM이나 시스템 레벨의 심각한 문제입니다. 개발자가 처리할 수 없고, 처리하려 해서도 안 됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OutOfMemoryError: Heap 메모리 부족&lt;/li&gt;
&lt;li&gt;StackOverflowError: 재귀 호출 무한 반복으로 Stack 초과&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Exception&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;h4 data-ke-size=&quot;size20&quot;&gt;Error를 catch로 잡으면 안 되는 이유는?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OutOfMemoryError를 catch해도 이미 메모리가 부족한 상태라 정상 동작을 보장할 수 없습니다. 잡는 척만 하고 실제로는 아무것도 못 합니다. 근본적인 원인(메모리 설정, 코드 개선)을 해결해야 합니다.&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;span style=&quot;background-color: #f6e199;&quot;&gt;Checked Exception과 Unchecked Exception의 차이를 설명해 주세요.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Checked Exception&lt;/b&gt; 컴파일러가 처리를 강제하는 예외입니다. try-catch로 잡거나 throws로 선언해야 합니다. RuntimeException을 상속받지 않은 Exception입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;java&quot; style=&quot;color: #14181f;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// IOException은 Checked &amp;rarr; 반드시 처리해야 컴파일됨
void readFile() throws IOException {
    FileInputStream fis = new FileInputStream(&quot;file.txt&quot;);
}

// 또는
void readFile() {
    try {
        FileInputStream fis = new FileInputStream(&quot;file.txt&quot;);
    } catch (IOException e) {
        e.printStackTrace();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;Unchecked Exception (RuntimeException)&lt;/b&gt; 컴파일러가 처리를 강제하지 않는 예외입니다. RuntimeException과 그 하위 클래스입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;java&quot; style=&quot;color: #14181f;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// NullPointerException은 Unchecked &amp;rarr; 처리 강제 안 함
void process(String s) {
    s.length();  // s가 null이면 NPE 발생하지만 컴파일 에러는 없음
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;CheckedUnchecked
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;상속&lt;/td&gt;
&lt;td&gt;Exception&lt;/td&gt;
&lt;td&gt;RuntimeException&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;컴파일 강제&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;X&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;예시&lt;/td&gt;
&lt;td&gt;IOException, SQLException&lt;/td&gt;
&lt;td&gt;NPE, IllegalArgumentException&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;발생 원인&lt;/td&gt;
&lt;td&gt;외부 환경 (파일, DB, 네트워크)&lt;/td&gt;
&lt;td&gt;개발자 실수, 프로그래밍 오류&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 &lt;b&gt;Unchecked Exception&lt;/b&gt;을 선호합니다. Checked는 throws가 메서드 시그니처에 노출되어 계층 간 결합도가 높아지기 때문입니다. Spring도 대부분 Unchecked Exception을 사용합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;커스텀 예외를 만들 때 어떤 걸 상속받나요?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 대부분 RuntimeException을 상속받아 Unchecked로 만듭니다. class OrderNotFoundException extends RuntimeException { ... } 처럼요.&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;Checked를 Unchecked로 바꾸는 패턴이 있나요?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네. Checked를 catch해서 Unchecked로 감싸 던지는 방식을 씁니다. Spring의 DataAccessException이 대표적으로, SQLException(Checked)을 감싸서 RuntimeException으로 변환합니다.&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;span style=&quot;background-color: #f6e199;&quot;&gt;Q. Lambda와 Functional Interface를 설명해 주세요.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Functional Interface는 추상 메서드가 하나인 인터페이스입니다. Lambda는 이 Functional Interface의 구현을 익명 클래스 없이 간결하게 표현하는 문법입니다.&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;Functional Interface&lt;/b&gt; 추상 메서드가 딱 하나인 인터페이스입니다. @FunctionalInterface 어노테이션을 붙입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;java&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;@FunctionalInterface
interface Calculator {
    int calculate(int a, int b);  // 추상 메서드 1개
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;Lambda&lt;/b&gt; Functional Interface의 구현을 익명 클래스 대신 간결하게 표현하는 방식입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;cs&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// 익명 클래스 방식 (Java 8 이전)
Calculator add = new Calculator() {
    @Override
    public int calculate(int a, int b) {
        return a + b;
    }
};

// 람다 방식 (Java 8+)
Calculator add = (a, b) -&amp;gt; a + b;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;Java 표준 Functional Interface (java.util.function)&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;xl&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// Function&amp;lt;T, R&amp;gt;  T를 받아 R을 반환
Function&amp;lt;String, Integer&amp;gt; toInt = s -&amp;gt; Integer.parseInt(s);

// Predicate&amp;lt;T&amp;gt;    T를 받아 boolean 반환
Predicate&amp;lt;Integer&amp;gt; isEven = n -&amp;gt; n % 2 == 0;

// Consumer&amp;lt;T&amp;gt;     T를 받아 소비 (반환 없음)
Consumer&amp;lt;String&amp;gt; print = s -&amp;gt; System.out.println(s);

// Supplier&amp;lt;T&amp;gt;     아무것도 안 받고 T 반환
Supplier&amp;lt;String&amp;gt; hello = () -&amp;gt; &quot;hello&quot;;

// BiFunction&amp;lt;T,U,R&amp;gt;  두 개 받아 R 반환
BiFunction&amp;lt;Integer, Integer, Integer&amp;gt; add = (a, b) -&amp;gt; a + b;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;메서드 참조 (Method Reference)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;람다를 더 간결하게 표현합니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// 람다
list.forEach(s -&amp;gt; System.out.println(s));

// 메서드 참조
list.forEach(System.out::println);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;람다는 this가 람다를 감싼 외부 클래스를 가리키지만, 익명 클래스는 this가 익명 클래스 자신을 가리킵니다. 람다는 새 스코프를 만들지 않아요.&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;@FunctionalInterface를 꼭 붙여야 하나요?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필수는 아닙니다. 단, 붙이면 컴파일러가 추상 메서드가 2개 이상이 되는 실수를 잡아줍니다.&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;Q. Stream API가 무엇이고 어떻게 사용하나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream API는 컬렉션을 선언적으로 처리하는 API입니다. 스트림 생성, 중간 연산, 최종 연산 세 단계로 동작합니다.&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;Stream이란?&lt;/b&gt; 컬렉션 데이터를 선언적으로 처리하는 API입니다. for문 없이 필터링, 변환, 집계를 간결하게 표현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본 구조&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;gcode&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;컬렉션.stream()          // ① 스트림 생성
    .중간연산()           // ② 데이터 가공 (여러 개 가능, 지연 실행)
    .최종연산();          // ③ 결과 수집 (1개만 가능, 이때 실행됨)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;중간 연산 (Intermediate Operation)&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;lasso&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; names = List.of(&quot;Kim&quot;, &quot;Lee&quot;, &quot;Park&quot;, &quot;Choi&quot;, &quot;Kang&quot;);

names.stream()
    .filter(s -&amp;gt; s.startsWith(&quot;K&quot;))   // 필터링: K로 시작하는 것만
    .map(String::toUpperCase)          // 변환: 대문자로
    .sorted()                          // 정렬
    .limit(2)                          // 2개만
    .forEach(System.out::println);     // 최종 연산

// 출력
// KANG
// KIM&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;최종 연산 (Terminal Operation)&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;List&amp;lt;Integer&amp;gt; numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// collect: 결과를 리스트로
List&amp;lt;Integer&amp;gt; evens = numbers.stream()
    .filter(n -&amp;gt; n % 2 == 0)
    .collect(Collectors.toList());  // [2, 4, 6, 8, 10]

// reduce: 하나의 값으로 합산
int sum = numbers.stream()
    .reduce(0, Integer::sum);  // 55

// count, min, max, anyMatch, allMatch
long count = numbers.stream().filter(n -&amp;gt; n &amp;gt; 5).count();  // 5
boolean anyOver9 = numbers.stream().anyMatch(n -&amp;gt; n &amp;gt; 9);  // true&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;지연 실행 (Lazy Evaluation)&lt;/b&gt; 중간 연산은 최종 연산이 호출될 때까지 실행되지 않습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; result = names.stream()
    .filter(s -&amp;gt; { System.out.println(&quot;filter: &quot; + s); return true; })
    .map(s -&amp;gt; { System.out.println(&quot;map: &quot; + s); return s.toUpperCase(); })
    .limit(2)  // 2개만 필요하므로 전체를 처리하지 않음
    .collect(Collectors.toList());

// filter, map이 3번째 요소부터는 실행되지 않음 &amp;rarr; 효율적&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;for문과 비교&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;lasso&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// for문 방식
List&amp;lt;String&amp;gt; result = new ArrayList&amp;lt;&amp;gt;();
for (String name : names) {
    if (name.startsWith(&quot;K&quot;)) {
        result.add(name.toUpperCase());
    }
}

// Stream 방식
List&amp;lt;String&amp;gt; result = names.stream()
    .filter(s -&amp;gt; s.startsWith(&quot;K&quot;))
    .map(String::toUpperCase)
    .collect(Collectors.toList());&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Stream과 for문 중 성능은 어떤 게 더 좋나요?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소규모 데이터는 for문이 오버헤드가 없어서 빠릅니다. 대용량 데이터는 parallelStream()으로 병렬 처리 시 Stream이 유리할 수 있습니다. 단, parallelStream은 공유 상태가 있으면 동시성 문제가 생기므로 주의해야 합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Stream은 재사용이 가능한가요?&lt;/h4&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;h4 data-ke-size=&quot;size20&quot;&gt;map과 flatMap의 차이는?&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;map은 1:1 변환, flatMap은 1:N 변환입니다. List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt;를 List&amp;lt;Integer&amp;gt;로 평탄화할 때 flatMap을 씁니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>민석 개발일지</author>
      <guid isPermaLink="true">https://minseok1015.tistory.com/27</guid>
      <comments>https://minseok1015.tistory.com/27#entry27comment</comments>
      <pubDate>Thu, 9 Apr 2026 21:41:32 +0900</pubDate>
    </item>
    <item>
      <title>MySQL FullText에서Elasticsearch로키워드 검색 개선기</title>
      <link>https://minseok1015.tistory.com/25</link>
      <description>&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
&lt;title&gt;MySQL FullText에서 Elasticsearch로 — 키워드 검색 개선기&lt;/title&gt;
&lt;link href=&quot;https://fonts.googleapis.com/css2?family=Noto+Serif+KR:wght@400;700;900&amp;family=JetBrains+Mono:wght@400;600&amp;family=Noto+Sans+KR:wght@300;400;500;700&amp;display=swap&quot; rel=&quot;stylesheet&quot;&gt;
&lt;style&gt;
  :root {
    --bg: #f0f1f4;
    --surface: #ffffff;
    --surface2: #e8e9ed;
    --border: #dde0e8;
    --accent: #5b4ef0;
    --accent2: #1a9e75;
    --accent3: #e05c2a;
    --text: #1a1a1a;
    --text-muted: #6b6b6b;
    --text-dim: #b0b3bf;
    --mysql: #e05c2a;
    --es: #1a9e75;
  }

  * { box-sizing: border-box; margin: 0; padding: 0; }

  body {
    background: var(--bg);
    color: var(--text);
    font-family: 'Noto Sans KR', sans-serif;
    font-size: 16px;
    line-height: 1.8;
  }

  /* HEADER */
  .hero {
    position: relative;
    padding: 100px 0 80px;
    overflow: hidden;
    border-bottom: 1px solid var(--border);
  }

  .hero::before {
    content: '';
    position: absolute;
    top: -100px; left: -100px;
    width: 600px; height: 600px;
    background: radial-gradient(circle, rgba(124,106,247,0.12) 0%, transparent 70%);
    pointer-events: none;
  }
  .hero::after {
    content: '';
    position: absolute;
    bottom: -100px; right: -50px;
    width: 400px; height: 400px;
    background: radial-gradient(circle, rgba(79,195,161,0.08) 0%, transparent 70%);
    pointer-events: none;
  }

  .container {
    max-width: 780px;
    margin: 0 auto;
    padding: 0 32px;
  }

  .tag {
    display: inline-block;
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.15em;
    text-transform: uppercase;
    color: var(--accent);
    border: 1px solid rgba(124,106,247,0.3);
    padding: 4px 12px;
    border-radius: 3px;
    margin-bottom: 24px;
  }

  h1 {
    font-family: 'Noto Serif KR', serif;
    font-size: clamp(28px, 5vw, 44px);
    font-weight: 900;
    line-height: 1.3;
    margin-bottom: 20px;
    letter-spacing: -0.02em;
  }

  h1 em {
    font-style: normal;
    color: var(--accent);
  }

  .hero-sub {
    font-size: 17px;
    color: var(--text-muted);
    line-height: 1.7;
    max-width: 600px;
    margin-bottom: 40px;
  }

  .meta {
    display: flex;
    gap: 24px;
    font-size: 13px;
    color: var(--text-dim);
    font-family: 'JetBrains Mono', monospace;
  }

  /* RESULT CARD */
  .result-hero {
    background: linear-gradient(135deg, rgba(124,106,247,0.08), rgba(79,195,161,0.08));
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 32px;
    margin: 48px 0;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 24px;
    position: relative;
    overflow: hidden;
  }

  .result-hero::before {
    content: '⚡ 핵심 성과';
    position: absolute;
    top: 16px; right: 20px;
    font-size: 11px;
    font-family: 'JetBrains Mono', monospace;
    color: var(--text-dim);
    letter-spacing: 0.1em;
  }

  .stat-block { text-align: center; padding: 8px; }
  .stat-num {
    font-family: 'Noto Serif KR', serif;
    font-size: 48px;
    font-weight: 900;
    display: block;
    background: linear-gradient(135deg, var(--accent), var(--accent2));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    line-height: 1.1;
    margin-bottom: 6px;
  }
  .stat-label { font-size: 13px; color: var(--text-muted); }

  /* ARTICLE */
  article { padding: 60px 0; }

  h2 {
    font-family: 'Noto Serif KR', serif;
    font-size: 26px;
    font-weight: 700;
    margin: 60px 0 20px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--border);
    position: relative;
  }

  h2::before {
    content: '';
    position: absolute;
    bottom: -1px; left: 0;
    width: 40px; height: 2px;
    background: var(--accent);
  }

  h3 {
    font-size: 18px;
    font-weight: 700;
    margin: 36px 0 12px;
    color: var(--text);
  }

  p { margin-bottom: 16px; color: var(--text); }

  /* CODE BLOCK */
  .code-block {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 20px 24px;
    margin: 20px 0;
    font-family: 'JetBrains Mono', monospace;
    font-size: 13px;
    line-height: 1.7;
    overflow-x: auto;
    position: relative;
  }

  .code-block .lang {
    position: absolute;
    top: 10px; right: 14px;
    font-size: 10px;
    color: var(--text-dim);
    letter-spacing: 0.1em;
    text-transform: uppercase;
  }

  .code-block .k { color: #c792ea; }
  .code-block .s { color: #c3e88d; }
  .code-block .n { color: #82aaff; }
  .code-block .v { color: #f78c6c; }
  .code-block .c { color: #637777; font-style: italic; }

  /* LOG BLOCK */
  .log-block {
    background: var(--surface2);
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: 8px;
    padding: 16px 20px;
    margin: 16px 0;
    font-family: 'JetBrains Mono', monospace;
    font-size: 12px;
    line-height: 1.9;
    overflow-x: auto;
  }

  .log-block .ts { color: var(--text-dim); }
  .log-block .info { color: #185fa5; }
  .log-block .label { color: var(--text-muted); }
  .log-block .mysql-t { color: var(--mysql); font-weight: 600; }
  .log-block .es-t { color: var(--es); font-weight: 600; }
  .log-block .winner { color: #3b6d11; font-weight: 600; }

  /* COMPARISON TABLE */
  .cmp-table {
    width: 100%;
    border-collapse: collapse;
    margin: 24px 0;
    font-size: 14px;
  }

  .cmp-table th {
    background: var(--surface2);
    padding: 12px 16px;
    text-align: left;
    font-weight: 600;
    border: 1px solid var(--border);
    font-size: 13px;
  }

  .cmp-table td {
    padding: 12px 16px;
    border: 1px solid var(--border);
    vertical-align: top;
  }

  .cmp-table tr:nth-child(even) td { background: rgba(255,255,255,0.02); }

  .badge-mysql {
    display: inline-block;
    background: rgba(247,140,108,0.15);
    color: var(--mysql);
    border: 1px solid rgba(247,140,108,0.3);
    padding: 2px 8px;
    border-radius: 3px;
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    font-weight: 600;
  }

  .badge-es {
    display: inline-block;
    background: rgba(79,195,161,0.15);
    color: var(--es);
    border: 1px solid rgba(79,195,161,0.3);
    padding: 2px 8px;
    border-radius: 3px;
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    font-weight: 600;
  }

  /* CALLOUT */
  .callout {
    border-left: 3px solid var(--accent);
    background: rgba(124,106,247,0.06);
    padding: 16px 20px;
    border-radius: 0 8px 8px 0;
    margin: 20px 0;
    font-size: 14.5px;
  }

  .callout.warn {
    border-left-color: var(--accent3);
    background: rgba(247,140,108,0.06);
  }

  .callout.success {
    border-left-color: var(--accent2);
    background: rgba(79,195,161,0.06);
  }

  .callout strong { color: var(--accent); }
  .callout.warn strong { color: var(--accent3); }
  .callout.success strong { color: var(--accent2); }

  /* CHART */
  .perf-chart {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 24px;
    margin: 24px 0;
  }

  .perf-chart h4 {
    font-family: 'JetBrains Mono', monospace;
    font-size: 12px;
    color: var(--text-dim);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    margin-bottom: 20px;
  }

  .bar-row {
    display: grid;
    grid-template-columns: 80px 1fr 60px;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;
    font-size: 13px;
  }

  .bar-label { color: var(--text-muted); font-size: 12px; text-align: right; }

  .bar-track {
    background: var(--surface2);
    border-radius: 3px;
    height: 24px;
    overflow: hidden;
    position: relative;
  }

  .bar-fill {
    height: 100%;
    border-radius: 3px;
    display: flex;
    align-items: center;
    padding-left: 8px;
    font-size: 11px;
    font-family: 'JetBrains Mono', monospace;
    font-weight: 600;
    transition: width 1s ease;
  }

  .bar-fill.mysql { background: rgba(247,140,108,0.6); color: var(--mysql); }
  .bar-fill.es { background: rgba(79,195,161,0.6); color: var(--es); }
  .bar-time { font-family: 'JetBrains Mono', monospace; font-size: 12px; color: var(--text-muted); }

  /* TOKEN TABLE */
  .token-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
    margin: 16px 0;
    font-family: 'JetBrains Mono', monospace;
  }
  .token-table th {
    background: var(--surface2);
    padding: 10px 14px;
    border: 1px solid var(--border);
    font-size: 11px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-muted);
    font-weight: 600;
  }
  .token-table td {
    padding: 10px 14px;
    border: 1px solid var(--border);
  }
  .token { color: var(--accent2); }
  .token-miss { color: var(--accent3); }

  /* SECTION DIVIDER */
  .divider {
    display: flex;
    align-items: center;
    gap: 16px;
    margin: 48px 0 0;
    color: var(--text-dim);
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    letter-spacing: 0.1em;
  }
  .divider::before, .divider::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--border);
  }

  /* FOOTER */
  footer {
    border-top: 1px solid var(--border);
    padding: 40px 0;
    margin-top: 60px;
    text-align: center;
    color: var(--text-dim);
    font-size: 13px;
    font-family: 'JetBrains Mono', monospace;
  }

  .conclusion-box {
    background: linear-gradient(135deg, rgba(91,78,240,0.06), rgba(26,158,117,0.04));
    border: 1px solid rgba(124,106,247,0.25);
    border-radius: 12px;
    padding: 32px;
    margin: 40px 0;
  }

  .conclusion-box h3 {
    color: var(--accent);
    margin-top: 0;
    font-size: 20px;
    margin-bottom: 16px;
  }

  ul {
    padding-left: 20px;
    margin-bottom: 16px;
  }

  ul li {
    margin-bottom: 8px;
    color: var(--text);
  }

  .inline-code {
    font-family: 'JetBrains Mono', monospace;
    font-size: 13px;
    background: var(--surface2);
    border: 1px solid var(--border);
    padding: 1px 6px;
    border-radius: 3px;
    color: var(--accent2);
  }

  @media (max-width: 600px) {
    .result-hero { grid-template-columns: 1fr 1fr; }
    .stat-num { font-size: 36px; }
  }
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;div class=&quot;hero&quot;&gt;
  &lt;div class=&quot;container&quot;&gt;
    &lt;div class=&quot;tag&quot;&gt;백엔드 개선기&lt;/div&gt;
    &lt;h1&gt;MySQL FullText에서&lt;br&gt;&lt;em&gt;Elasticsearch&lt;/em&gt;로&lt;br&gt;키워드 검색 개선기&lt;/h1&gt;
    &lt;p class=&quot;hero-sub&quot;&gt;
      기존 MySQL FullText 검색의 한계를 넘어, Elasticsearch를 도입해 검색 속도를 4~5배 개선하고
      한국어 형태소 분석 기반의 정확한 검색 품질을 확보하기까지의 과정을 정리합니다.
    &lt;/p&gt;
    &lt;div class=&quot;meta&quot;&gt;
      &lt;span&gt;2026.03.26&lt;/span&gt;
      &lt;span&gt;Elasticsearch · MySQL · Spring Boot&lt;/span&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;container&quot;&gt;

  &lt;div class=&quot;result-hero&quot;&gt;
    &lt;div class=&quot;stat-block&quot;&gt;
      &lt;span class=&quot;stat-num&quot;&gt;4~5×&lt;/span&gt;
      &lt;span class=&quot;stat-label&quot;&gt;검색 속도 개선&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;stat-block&quot;&gt;
      &lt;span class=&quot;stat-num&quot;&gt;~0.17s&lt;/span&gt;
      &lt;span class=&quot;stat-label&quot;&gt;대량 키워드 응답시간&lt;br&gt;(기존 ~0.8s)&lt;/span&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;article&gt;

    &lt;h2&gt;배경 — 왜 바꾸게 됐나&lt;/h2&gt;
    &lt;p&gt;
      서비스에서 키워드 자동완성 검색을 제공하고 있었는데, 기존 구현은 MySQL의 FullText 검색을 사용하고 있었습니다.
      운영하면서 두 가지 문제가 뚜렷하게 드러났습니다.
    &lt;/p&gt;

    &lt;div class=&quot;callout warn&quot;&gt;
      &lt;strong&gt;문제 1 — 한 글자 검색 불가&lt;/strong&gt;&lt;br&gt;
      MySQL FullText는 기본적으로 최소 토큰 길이(&lt;span class=&quot;inline-code&quot;&gt;ft_min_word_len&lt;/span&gt;) 제한이 있어,
      &quot;금&quot;, &quot;주&quot;, &quot;암&quot; 같은 한 글자 키워드를 검색하면 결과가 아예 반환되지 않았습니다.
      자동완성 기능 특성상 사용자가 첫 글자만 입력한 시점에도 결과를 보여줘야 하는데, 이게 원천적으로 막혀 있었습니다.
    &lt;/div&gt;

    &lt;div class=&quot;callout warn&quot;&gt;
      &lt;strong&gt;문제 2 — 대량 결과 검색 시 느린 응답&lt;/strong&gt;&lt;br&gt;
      데이터가 늘어나면서 &quot;대통령&quot; 같이 결과 수가 1,000개를 넘는 키워드를 검색할 때 응답이 눈에 띄게 느려졌습니다.
      실제로 API를 반복 호출해보니 응답 시간이 &lt;strong&gt;700~1,200ms&lt;/strong&gt; 수준이었습니다.
    &lt;/div&gt;

    &lt;p&gt;
      두 문제를 한꺼번에 해결하기 위해 Elasticsearch 도입을 검토하게 됐습니다.
    &lt;/p&gt;

    &lt;div class=&quot;callout&quot;&gt;
      &lt;strong&gt;검색 대상:&lt;/strong&gt; keywords 인덱스 — 뉴스 관련 키워드 데이터. 대표 키워드 &quot;대통령&quot; 검색 시 총 1,049개 결과.
    &lt;/div&gt;

    &lt;h2&gt;1단계 — Elasticsearch 연동 및 성능 비교&lt;/h2&gt;

    &lt;h3&gt;Elasticsearch 검색 확인&lt;/h3&gt;
    &lt;p&gt;먼저 Elasticsearch에 키워드 인덱스를 구성하고, 같은 쿼리를 날려 결과를 확인했습니다.&lt;/p&gt;

    &lt;div class=&quot;code-block&quot;&gt;
      &lt;span class=&quot;lang&quot;&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# &quot;대통령&quot; 키워드로 match 검색, 상위 3개 반환&lt;/span&gt;
curl -s 'http://localhost:9200/keywords/_search' \
  -H 'Content-Type: application/json' \
  -d '{
    &lt;span class=&quot;k&quot;&gt;&quot;query&quot;&lt;/span&gt;: {&lt;span class=&quot;k&quot;&gt;&quot;match&quot;&lt;/span&gt;: {&lt;span class=&quot;k&quot;&gt;&quot;keyword&quot;&lt;/span&gt;: &lt;span class=&quot;s&quot;&gt;&quot;대통령&quot;&lt;/span&gt;}},
    &lt;span class=&quot;k&quot;&gt;&quot;size&quot;&lt;/span&gt;: &lt;span class=&quot;v&quot;&gt;3&lt;/span&gt;
  }'&lt;/pre&gt;
    &lt;/div&gt;

    &lt;p&gt;결과는 &lt;strong&gt;총 1,049개&lt;/strong&gt;로 MySQL과 동일하게 나왔고, 상위 결과도 &quot;대통령 대통령실 공사&quot;, &quot;대통령 폴란드 대통령과&quot; 등 관련도 높은 키워드들이 잘 잡혔습니다.&lt;/p&gt;

    &lt;h3&gt;실제 서비스 API와 연동 후 성능 측정&lt;/h3&gt;
    &lt;p&gt;Spring Boot 서비스에서 같은 요청을 MySQL과 ES 양쪽 모두 실행하고 시간을 로깅해 비교했습니다.&lt;/p&gt;

    &lt;div class=&quot;log-block&quot;&gt;
&lt;span class=&quot;ts&quot;&gt;2026-03-26T22:48:33&lt;/span&gt; &lt;span class=&quot;info&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;label&quot;&gt;KeywordService : === 검색 성능 비교: '대통령' ===&lt;/span&gt;&lt;br/&gt;
&lt;span class=&quot;ts&quot;&gt;2026-03-26T22:48:33&lt;/span&gt; &lt;span class=&quot;info&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;label&quot;&gt;KeywordService : [MySQL FullText]&lt;/span&gt; &lt;span class=&quot;mysql-t&quot;&gt;1049개 결과, 1193ms&lt;/span&gt;&lt;br/&gt;
&lt;span class=&quot;ts&quot;&gt;2026-03-26T22:48:33&lt;/span&gt; &lt;span class=&quot;info&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;label&quot;&gt;KeywordService : [Elasticsearch]&lt;/span&gt;  &lt;span class=&quot;es-t&quot;&gt;1049개 결과, 310ms&lt;/span&gt;&lt;br/&gt;
&lt;span class=&quot;ts&quot;&gt;2026-03-26T22:48:33&lt;/span&gt; &lt;span class=&quot;info&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;label&quot;&gt;KeywordService :&lt;/span&gt; &lt;span class=&quot;winner&quot;&gt;속도 차이: ES가 883ms 빠름 (MySQL 1193ms vs ES 310ms)&lt;/span&gt;&lt;br/&gt;
    &lt;/div&gt;

    &lt;div class=&quot;log-block&quot;&gt;
&lt;span class=&quot;ts&quot;&gt;2026-03-26T22:49:01&lt;/span&gt; &lt;span class=&quot;info&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;label&quot;&gt;KeywordService : [MySQL FullText]&lt;/span&gt; &lt;span class=&quot;mysql-t&quot;&gt;1049개 결과, 776ms&lt;/span&gt;&lt;br/&gt;
&lt;span class=&quot;ts&quot;&gt;2026-03-26T22:49:01&lt;/span&gt; &lt;span class=&quot;info&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;label&quot;&gt;KeywordService : [Elasticsearch]&lt;/span&gt;  &lt;span class=&quot;es-t&quot;&gt;1049개 결과, 138ms&lt;/span&gt;&lt;br/&gt;
&lt;span class=&quot;ts&quot;&gt;2026-03-26T22:49:01&lt;/span&gt; &lt;span class=&quot;info&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;label&quot;&gt;KeywordService :&lt;/span&gt; &lt;span class=&quot;winner&quot;&gt;속도 차이: ES가 638ms 빠름 (MySQL 776ms vs ES 138ms)&lt;/span&gt;&lt;br/&gt;
    &lt;/div&gt;

    &lt;p&gt;반복 요청할수록 MySQL은 600~1,200ms를 오가는 반면, ES는 꾸준히 130~310ms 대를 유지했습니다. 전체적으로 &lt;strong&gt;약 0.8초 → 0.17초&lt;/strong&gt;로, 4~5배 수준의 개선이 확인됐습니다.&lt;/p&gt;

    &lt;div class=&quot;perf-chart&quot;&gt;
      &lt;h4&gt;대통령 키워드 검색 — 응답시간 비교 (ms)&lt;/h4&gt;
      &lt;div class=&quot;bar-row&quot;&gt;
        &lt;span class=&quot;bar-label&quot;&gt;MySQL&lt;/span&gt;
        &lt;div class=&quot;bar-track&quot;&gt;&lt;div class=&quot;bar-fill mysql&quot; style=&quot;width:100%&quot;&gt;FullText&lt;/div&gt;&lt;/div&gt;
        &lt;span class=&quot;bar-time&quot;&gt;~800ms&lt;/span&gt;
      &lt;/div&gt;
      &lt;div class=&quot;bar-row&quot;&gt;
        &lt;span class=&quot;bar-label&quot;&gt;ES&lt;/span&gt;
        &lt;div class=&quot;bar-track&quot;&gt;&lt;div class=&quot;bar-fill es&quot; style=&quot;width:21%&quot;&gt;nori&lt;/div&gt;&lt;/div&gt;
        &lt;span class=&quot;bar-time&quot;&gt;~170ms&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;h2&gt;2단계 — 결과 수가 적을 때는 ES가 느리다?&lt;/h2&gt;
    &lt;p&gt;
      &quot;주식&quot;, &quot;아이폰&quot;, &quot;부동산&quot; 등 결과 수가 적은 키워드(10~20개)도 테스트해봤더니 예상 외의 결과가 나왔습니다.
    &lt;/p&gt;

    &lt;div class=&quot;log-block&quot;&gt;
&lt;span class=&quot;ts&quot;&gt;2026-03-26T22:35:39&lt;/span&gt; &lt;span class=&quot;info&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;label&quot;&gt;KeywordService : === 검색 성능 비교: '주식' ===&lt;/span&gt;&lt;br/&gt;
&lt;span class=&quot;ts&quot;&gt;2026-03-26T22:35:39&lt;/span&gt; &lt;span class=&quot;info&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;label&quot;&gt;KeywordService : [MySQL FullText]&lt;/span&gt; &lt;span class=&quot;mysql-t&quot;&gt;18개 결과, 77ms&lt;/span&gt;&lt;br/&gt;
&lt;span class=&quot;ts&quot;&gt;2026-03-26T22:35:39&lt;/span&gt; &lt;span class=&quot;info&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;label&quot;&gt;KeywordService : [Elasticsearch]&lt;/span&gt;  &lt;span class=&quot;es-t&quot;&gt;18개 결과, 85ms&lt;/span&gt;&lt;br/&gt;
&lt;span class=&quot;ts&quot;&gt;2026-03-26T22:35:39&lt;/span&gt; &lt;span class=&quot;info&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;label&quot;&gt;KeywordService :&lt;/span&gt; 속도 차이: ES가 8ms 느림
    &lt;/div&gt;

    &lt;div class=&quot;callout warn&quot;&gt;
      &lt;strong&gt;결과 수가 적을 때는 MySQL이 더 빠를 수 있습니다.&lt;/strong&gt;&lt;br&gt;
      MySQL FullText는 같은 프로세스 내 직접 DB 호출이라 오버헤드가 없지만,
      ES는 HTTP 통신 (localhost라도 네트워크 왕복) + JSON 직렬화/역직렬화 고정 비용이 30~50ms 붙습니다.
      결과가 18개처럼 소량이면 MySQL이 쿼리를 금방 끝내지만, ES는 이 고정비용 때문에 오히려 느려집니다.
    &lt;/div&gt;

    &lt;table class=&quot;cmp-table&quot;&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;span class=&quot;badge-mysql&quot;&gt;MySQL&lt;/span&gt;&lt;/td&gt;
          &lt;td&gt;HTTP 오버헤드 없음, 직접 DB 호출&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;대량 (수백~수천 개)&lt;/td&gt;
          &lt;td&gt;&lt;span class=&quot;badge-es&quot;&gt;Elasticsearch&lt;/span&gt;&lt;/td&gt;
          &lt;td&gt;역인덱스 기반 검색이 압도적으로 빠름&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;

    &lt;h2&gt;3단계 — 갯수 차이가 발생하는 이유 (반도체 케이스)&lt;/h2&gt;
    &lt;p&gt;
      &quot;삼성&quot; (366개 vs 355개), &quot;반도체&quot; (49개 vs 46개)처럼 MySQL과 ES의 결과 수가 미묘하게 달랐습니다.
      왜 그런지 파고들었습니다.
    &lt;/p&gt;

    &lt;h3&gt;원인: 한국어 토크나이저의 차이&lt;/h3&gt;

    &lt;table class=&quot;cmp-table&quot;&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;span class=&quot;badge-mysql&quot;&gt;MySQL FullText&lt;/span&gt;&lt;/td&gt;
          &lt;td&gt;BOOLEAN MODE&lt;/td&gt;
          &lt;td&gt;단순 문자열 포함 여부 매칭&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&lt;span class=&quot;badge-es&quot;&gt;Elasticsearch&lt;/span&gt;&lt;/td&gt;
          &lt;td&gt;nori 형태소 분석&lt;/td&gt;
          &lt;td&gt;형태소 분해 후 토큰 단위로 매칭&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;

    &lt;p&gt;직접 Elasticsearch의 analyze API로 확인해봤습니다.&lt;/p&gt;

    &lt;div class=&quot;code-block&quot;&gt;
      &lt;span class=&quot;lang&quot;&gt;bash&lt;/span&gt;
&lt;pre&gt;curl -s 'http://localhost:9200/keywords/_analyze' \
  -H 'Content-Type: application/json' \
  -d '{&quot;analyzer&quot;:&quot;korean_analyzer&quot;,&quot;text&quot;:&quot;반도체법&quot;}'

&lt;span class=&quot;c&quot;&gt;# 결과:&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# tokens: [&quot;반&quot;, &quot;도&quot;, &quot;체법&quot;]  ← &quot;반도체&quot; 토큰이 없음!&lt;/span&gt;&lt;/pre&gt;
    &lt;/div&gt;

    &lt;div class=&quot;callout warn&quot;&gt;
      &lt;strong&gt;핵심 원인:&lt;/strong&gt; nori가 &lt;span class=&quot;inline-code&quot;&gt;decompound_mode: mixed&lt;/span&gt;로 설정돼 있어
      &quot;반도체법&quot;을 &lt;strong&gt;반 / 도 / 체법&lt;/strong&gt;으로 분해합니다. &quot;반도체&quot; 토큰이 생성되지 않아
      &quot;반도체법&quot;이 &quot;반도체&quot; 검색 결과에 포함되지 않습니다.
      MySQL은 단순 문자열 포함이라 &quot;반도체법&quot;에서도 &quot;반도체&quot;를 찾아냅니다.
    &lt;/div&gt;

    &lt;table class=&quot;token-table&quot;&gt;
      &lt;thead&gt;
        &lt;tr&gt;&lt;th&gt;분석 대상&lt;/th&gt;&lt;th&gt;nori 토큰 결과&lt;/th&gt;&lt;th&gt;&quot;반도체&quot; 매칭?&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;span class=&quot;token&quot;&gt;반도체&lt;/span&gt;, &lt;span class=&quot;token&quot;&gt;반&lt;/span&gt;, &lt;span class=&quot;token&quot;&gt;도체&lt;/span&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;span class=&quot;token-miss&quot;&gt;반&lt;/span&gt;, &lt;span class=&quot;token-miss&quot;&gt;도&lt;/span&gt;, &lt;span class=&quot;token-miss&quot;&gt;체법&lt;/span&gt;&lt;/td&gt;
          &lt;td&gt;❌ 미매칭&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;

    &lt;h2&gt;4단계 — 해결방법: ngram 서브필드 추가&lt;/h2&gt;
    &lt;p&gt;
      &quot;반도체법&quot;에서 &quot;반도체&quot;를 찾으려면 부분 문자열 매칭이 필요합니다.
      nori 분석기는 그대로 두되, &lt;strong&gt;ngram 서브필드를 추가&lt;/strong&gt;해 두 가지를 함께 활용하는 방식을 선택했습니다.
    &lt;/p&gt;

    &lt;h3&gt;쿼리 전략: nori에 높은 가중치, ngram에 낮은 가중치&lt;/h3&gt;

    &lt;div class=&quot;code-block&quot;&gt;
      &lt;span class=&quot;lang&quot;&gt;elasticsearch query&lt;/span&gt;
&lt;pre&gt;{
  &lt;span class=&quot;k&quot;&gt;&quot;query&quot;&lt;/span&gt;: {
    &lt;span class=&quot;k&quot;&gt;&quot;bool&quot;&lt;/span&gt;: {
      &lt;span class=&quot;k&quot;&gt;&quot;should&quot;&lt;/span&gt;: [
        {
          &lt;span class=&quot;k&quot;&gt;&quot;match&quot;&lt;/span&gt;: {
            &lt;span class=&quot;k&quot;&gt;&quot;keyword&quot;&lt;/span&gt;: { &lt;span class=&quot;k&quot;&gt;&quot;query&quot;&lt;/span&gt;: &lt;span class=&quot;s&quot;&gt;&quot;반도체&quot;&lt;/span&gt;, &lt;span class=&quot;k&quot;&gt;&quot;boost&quot;&lt;/span&gt;: &lt;span class=&quot;v&quot;&gt;3&lt;/span&gt; }
          }
        },
        {
          &lt;span class=&quot;k&quot;&gt;&quot;match&quot;&lt;/span&gt;: {
            &lt;span class=&quot;k&quot;&gt;&quot;keyword.ngram&quot;&lt;/span&gt;: { &lt;span class=&quot;k&quot;&gt;&quot;query&quot;&lt;/span&gt;: &lt;span class=&quot;s&quot;&gt;&quot;반도체&quot;&lt;/span&gt;, &lt;span class=&quot;k&quot;&gt;&quot;boost&quot;&lt;/span&gt;: &lt;span class=&quot;v&quot;&gt;1&lt;/span&gt; }
          }
        }
      ],
      &lt;span class=&quot;k&quot;&gt;&quot;minimum_should_match&quot;&lt;/span&gt;: &lt;span class=&quot;v&quot;&gt;1&lt;/span&gt;
    }
  }
}&lt;/pre&gt;
    &lt;/div&gt;

    &lt;ul&gt;
      &lt;li&gt;nori로 매칭된 결과 → boost 3 → 상위 노출 (정확 매칭 우선)&lt;/li&gt;
      &lt;li&gt;ngram으로만 매칭된 결과 (&quot;한반도&quot;, &quot;도체&quot;) → boost 1 → 하위 노출&lt;/li&gt;
      &lt;li&gt;&lt;span class=&quot;inline-code&quot;&gt;min_score&lt;/span&gt; 설정 시 점수 낮은 과매칭 결과 자동 필터링 가능&lt;/li&gt;
    &lt;/ul&gt;

    &lt;h3&gt;인덱스 크기 영향 측정&lt;/h3&gt;

    &lt;table class=&quot;cmp-table&quot;&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;nori만 (기존)&lt;/td&gt;
          &lt;td&gt;106.9 MB&lt;/td&gt;
          &lt;td&gt;1x&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;nori + ngram&lt;/td&gt;
          &lt;td&gt;188.0 MB&lt;/td&gt;
          &lt;td&gt;1.76x&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;

    &lt;div class=&quot;callout&quot;&gt;
      예상했던 2~3배보다 적은 &lt;strong&gt;약 76% 증가 (81MB 추가)&lt;/strong&gt;로, 허용 가능한 수준입니다.
    &lt;/div&gt;

    &lt;h3&gt;ngram 추가 후 결과&lt;/h3&gt;

    &lt;div class=&quot;log-block&quot;&gt;
&lt;span class=&quot;ts&quot;&gt;2026-03-26T23:48:09&lt;/span&gt; &lt;span class=&quot;info&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;label&quot;&gt;KeywordService : === 검색 성능 비교: '반도체' ===&lt;/span&gt;&lt;br/&gt;
&lt;span class=&quot;ts&quot;&gt;2026-03-26T23:48:09&lt;/span&gt; &lt;span class=&quot;info&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;label&quot;&gt;KeywordService : [MySQL FullText]&lt;/span&gt; &lt;span class=&quot;mysql-t&quot;&gt;49개 결과, 141ms&lt;/span&gt;&lt;br/&gt;
&lt;span class=&quot;ts&quot;&gt;2026-03-26T23:48:09&lt;/span&gt; &lt;span class=&quot;info&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;label&quot;&gt;KeywordService : [Elasticsearch]&lt;/span&gt;  &lt;span class=&quot;es-t&quot;&gt;74개 결과, 290ms&lt;/span&gt;&lt;br/&gt;
    &lt;/div&gt;

    &lt;div class=&quot;callout warn&quot;&gt;
      &lt;strong&gt;과매칭 주의:&lt;/strong&gt; ngram 추가 후 ES 결과가 74개로 MySQL(49개)보다 오히려 많아졌습니다.
      &quot;도체 저항 실험&quot;, &quot;한반도 평화&quot; 같이 관련 없는 키워드까지 포함된 것입니다.
      boost + min_score로 관련도 낮은 결과를 잘라내는 튜닝이 필요합니다.
    &lt;/div&gt;

    &lt;h2&gt;최종 결론 및 의사결정&lt;/h2&gt;

    &lt;div class=&quot;conclusion-box&quot;&gt;
      &lt;h3&gt;  정리&lt;/h3&gt;
      &lt;table class=&quot;cmp-table&quot; style=&quot;margin-top:0&quot;&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;/td&gt;&lt;td&gt;&lt;span class=&quot;badge-es&quot;&gt;ES 4~5배 빠름&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
          &lt;tr&gt;&lt;td&gt;소량 키워드 검색 속도&lt;/td&gt;&lt;td&gt;&lt;span class=&quot;badge-mysql&quot;&gt;MySQL이 오히려 빠름&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
          &lt;tr&gt;&lt;td&gt;한국어 형태소 매칭 정확도&lt;/td&gt;&lt;td&gt;&lt;span class=&quot;badge-es&quot;&gt;ES (nori) 우위&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
          &lt;tr&gt;&lt;td&gt;복합어 부분 매칭&lt;/td&gt;&lt;td&gt;ngram 서브필드로 보완 (과매칭 주의)&lt;/td&gt;&lt;/tr&gt;
          &lt;tr&gt;&lt;td&gt;인덱스 크기 증가&lt;/td&gt;&lt;td&gt;nori+ngram 기준 약 76% 증가&lt;/td&gt;&lt;/tr&gt;
        &lt;/tbody&gt;
      &lt;/table&gt;
    &lt;/div&gt;

    &lt;h3&gt;권장 적용 전략&lt;/h3&gt;
    &lt;ul&gt;
      &lt;li&gt;결과 수가 많은 대형 키워드 검색 → &lt;strong&gt;Elasticsearch 사용&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;결과 수가 적은 정확 매칭 검색 → MySQL FullText도 충분&lt;/li&gt;
      &lt;li&gt;복합어 포함 포괄적 검색이 필요하면 → &lt;strong&gt;nori + ngram + boost 조합&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;ngram 과매칭 제어 → &lt;span class=&quot;inline-code&quot;&gt;min_score&lt;/span&gt; 또는 상위 N개만 반환&lt;/li&gt;
      &lt;li&gt;nori의 &lt;span class=&quot;inline-code&quot;&gt;decompound_mode&lt;/span&gt;를 &lt;span class=&quot;inline-code&quot;&gt;none&lt;/span&gt;으로 바꾸면 갯수는 맞출 수 있지만 검색 품질(복합어 분해) 저하&lt;/li&gt;
    &lt;/ul&gt;

  &lt;/article&gt;

&lt;/div&gt;

&lt;footer&gt;
  &lt;div class=&quot;container&quot;&gt;
    Elasticsearch 도입기 · 2026.03 · store.itpick.backend
  &lt;/div&gt;
&lt;/footer&gt;

&lt;/body&gt;
&lt;/html&gt;</description>
      <author>민석 개발일지</author>
      <guid isPermaLink="true">https://minseok1015.tistory.com/25</guid>
      <comments>https://minseok1015.tistory.com/25#entry25comment</comments>
      <pubDate>Thu, 2 Apr 2026 22:52:35 +0900</pubDate>
    </item>
    <item>
      <title>[코딩 테스트] 트리의 부모 찾기</title>
      <link>https://minseok1015.tistory.com/23</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;a href=&quot;https://www.acmicpc.net/problem/11725&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/11725&lt;/a&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;h1 style=&quot;color: #585f69;&quot;&gt;&lt;span&gt;트리의 부모 찾기&lt;/span&gt;&lt;/h1&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;
&lt;div&gt;시간 제한메모리 제한제출정답맞힌 사람정답 비율
&lt;table id=&quot;problem-info&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 초&lt;/td&gt;
&lt;td&gt;256 MB&lt;/td&gt;
&lt;td&gt;101747&lt;/td&gt;
&lt;td&gt;46543&lt;/td&gt;
&lt;td&gt;32592&lt;/td&gt;
&lt;td&gt;43.306%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div id=&quot;problem-body&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h2 style=&quot;color: #585f69;&quot; data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;/div&gt;
&lt;div id=&quot;problem_description&quot;&gt;
&lt;p style=&quot;color: #555555;&quot; data-ke-size=&quot;size16&quot;&gt;루트 없는 트리가 주어진다. 이때, 트리의 루트를 1이라고 정했을 때, 각 노드의 부모를 구하는 프로그램을 작성하시오.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h2 style=&quot;color: #585f69;&quot; data-ke-size=&quot;size26&quot;&gt;입력&lt;/h2&gt;
&lt;/div&gt;
&lt;div id=&quot;problem_input&quot;&gt;
&lt;p style=&quot;color: #555555;&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에 노드의 개수 N (2 &amp;le; N &amp;le; 100,000)이 주어진다. 둘째 줄부터 N-1개의 줄에 트리 상에서 연결된 두 정점이 주어진다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h2 style=&quot;color: #585f69;&quot; data-ke-size=&quot;size26&quot;&gt;출력&lt;/h2&gt;
&lt;/div&gt;
&lt;div id=&quot;problem_output&quot;&gt;
&lt;p style=&quot;color: #555555;&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄부터 N-1개의 줄에 각 노드의 부모 노드 번호를 2번 노드부터 순서대로&amp;nbsp;출력한다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h2 style=&quot;color: #585f69;&quot; data-ke-size=&quot;size26&quot;&gt;예제 입력 1&lt;/h2&gt;
&lt;/div&gt;
&lt;pre id=&quot;sample-input-1&quot; class=&quot;bash&quot; style=&quot;background-color: #f7f7f9; color: #333333;&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;7
1 6
6 3
3 5
4 1
2 4
4 7&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h2 style=&quot;color: #585f69;&quot; data-ke-size=&quot;size26&quot;&gt;예제 출력 1&lt;/h2&gt;
&lt;/div&gt;
&lt;pre id=&quot;sample-output-1&quot; class=&quot;bash&quot; style=&quot;background-color: #f7f7f9; color: #333333;&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;4
6
1
3
1
4&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h2 style=&quot;color: #585f69;&quot; data-ke-size=&quot;size26&quot;&gt;예제 입력 2&lt;/h2&gt;
&lt;/div&gt;
&lt;pre id=&quot;sample-input-2&quot; class=&quot;bash&quot; style=&quot;background-color: #f7f7f9; color: #333333;&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;12
1 2
1 3
2 4
3 5
3 6
4 7
4 8
5 9
5 10
6 11
6 12&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h2 style=&quot;color: #585f69;&quot; data-ke-size=&quot;size26&quot;&gt;예제 출력 2&lt;/h2&gt;
&lt;/div&gt;
&lt;pre id=&quot;sample-output-2&quot; class=&quot;bash&quot; style=&quot;background-color: #f7f7f9; color: #333333;&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;1
1
2
3
3
4
4
5
5
6
6&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;처음에 나는 Find-Union을 생각하여 나오는 노드 중 하나를 부모로 지정하려고 하였다. 밑에 코드와 같이&lt;/div&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;입력받은 노드 2개중에 하나가 parent가 지정돼있는지 확인&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;지정이 안돼있으면 둘 중하나가 나머지의 부모이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1741609067006&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;


class Main {
  public static void main(String[] args)throws IOException {
    BufferedReader br= new BufferedReader(new InputStreamReader(System.in));
    int N =Integer.parseInt(br.readLine());
    int[] parent = new int[N+1];
    Arrays.fill(parent,-1);
    parent[1] = 0;
    for(int i = 0 ; i &amp;lt;N-1;i++){
      String[] input = br.readLine().split(&quot; &quot;);
      int front = Integer.parseInt(input[0]);
      int back = Integer.parseInt(input[1]);
      if(parent[front]!=-1){
        parent[back] =front;
      }else{
        parent[front] = back;
      }
    }

    for(int i = 2 ; i&amp;lt;=N;i++){
      System.out.println(parent[i]);
    }

  }
}&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;이렇게 하면 예제문제는 간단하게 풀리게 됨.&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;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;7&lt;br /&gt;1&amp;nbsp;6&lt;br /&gt;6&amp;nbsp;3&lt;br /&gt;3&amp;nbsp;5&lt;br /&gt;4&amp;nbsp;1&lt;br /&gt;2&amp;nbsp;4&lt;br /&gt;4&amp;nbsp;7&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;1 6 // 1은 root로 지정돼있으므로, 1은 6의 부모이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6 3 // 6은 부모가 1로 지정돼있으므로, 6은 3의 부모이다.&lt;br /&gt;3 5 // 3은 부모가 6으로 지정돼있으므로, 3은 5의 부모이다.&lt;br /&gt;4 1 // ...&lt;br /&gt;2 4 // ...&lt;br /&gt;4 7 // ...&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;3&amp;nbsp;5&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6 3&lt;br /&gt;4&amp;nbsp;1&lt;br /&gt;2&amp;nbsp;4&lt;br /&gt;4&amp;nbsp;7&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;그러면 3이랑 5는 둘 다 부모가 아직 없기 때문에 , 꼬여버리게 된다.&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;그래서 어떻게 할까? 하다가 나는 BFS로 문제를 풀어보았다.&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;


class Main {
    public static void main(String[] args)throws IOException {
        BufferedReader br= new BufferedReader(new InputStreamReader(System.in));
        int N =Integer.parseInt(br.readLine());
        boolean[] visited = new boolean[N+1];
        int[] parent = new int[N+1];

        ArrayList&amp;lt;Integer&amp;gt;[] adList = new ArrayList[N+1];
        for(int i = 0;  i &amp;lt;=N ; i++){
            adList[i]=new ArrayList&amp;lt;&amp;gt;();
        }
        for(int i = 0 ; i &amp;lt; N-1 ; i ++){
            String[] input= br.readLine().split(&quot; &quot;);
            int v1 = Integer.parseInt(input[0]);
            int v2 = Integer.parseInt(input[1]);
            adList[v1].add(v2);
            adList[v2].add(v1);
        }

        ArrayDeque&amp;lt;Integer&amp;gt; queue= new ArrayDeque&amp;lt;&amp;gt;();
        queue.add(1);

        while(!queue.isEmpty()){
            int now = queue.poll();


            for(int next: adList[now]){
                if(!visited[next]){
                    visited[next] =true;
                    parent[next] = now;
                    queue.add(next);
                }
            }


        }
        for(int i = 2 ; i &amp;lt;= N ; i++){
            System.out.println(parent[i]);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;</description>
      <category>코팅테스트</category>
      <author>민석 개발일지</author>
      <guid isPermaLink="true">https://minseok1015.tistory.com/23</guid>
      <comments>https://minseok1015.tistory.com/23#entry23comment</comments>
      <pubDate>Tue, 11 Mar 2025 13:04:35 +0900</pubDate>
    </item>
    <item>
      <title>[코딩 테스트] 좋은 친구</title>
      <link>https://minseok1015.tistory.com/22</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 출처&lt;/h3&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://www.acmicpc.net/problem/3078&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/3078&lt;/a&gt;&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;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한명씩 조사하면서, K 범위에있는 친구를 조사하는건 절대 안될 거라는 생각이 들었다.&lt;/li&gt;
&lt;li&gt;K가 N만큼 커지게 된다면 N^2 만큼 조사하게 되고, &lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(3 &amp;le; N &amp;le; 300,000, 1 &amp;le; K &amp;le; N) 같은 범위에서는 무조건 시간초과가 나게 될 것이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;어떻게 할까 고민하다가 결국에 생각해내지 못하고 힌트를 참고하였다....&lt;/span&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;예시를 보면서 설명하는게 편할 것 같아서 문제에서 보여준 2번째 예시를 사용해보도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #585f69; font-size: 1.62em; letter-spacing: -1px; background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif;&quot;&gt;예제 입력 2&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;sample-input-2&quot; class=&quot;angelscript&quot; style=&quot;background-color: #f7f7f9; color: #333333; text-align: start;&quot;&gt;&lt;code&gt;6 3
CYNTHIA
LLOYD
STEVIE
KEVIN
MALCOLM
DABNEY&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;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;다음과 같은 예시에 먼저 배열을 두개를 생성해준다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&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;최대 20자라고 하였으니 int[20+1]의 배열을 선언해주면 되겠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;307&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H3PvB/btsL5s4TRpe/8qq9zL6jtfGURrbprzKiUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H3PvB/btsL5s4TRpe/8qq9zL6jtfGURrbprzKiUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H3PvB/btsL5s4TRpe/8qq9zL6jtfGURrbprzKiUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH3PvB%2FbtsL5s4TRpe%2F8qq9zL6jtfGURrbprzKiUk%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;500&quot; height=&quot;307&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;307&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;K만큼 조사하면서 확인한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MbIIw/btsL6CZLtIy/eL2jb5ieCDF0ugWu63rph1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MbIIw/btsL6CZLtIy/eL2jb5ieCDF0ugWu63rph1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MbIIw/btsL6CZLtIy/eL2jb5ieCDF0ugWu63rph1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMbIIw%2FbtsL6CZLtIy%2FeL2jb5ieCDF0ugWu63rph1%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;600&quot; height=&quot;371&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;371&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;첫 번째 학생(1등 학생, 이름길이:7)의 count 수를 보고서 answer에 추가하면된다. 여기서 중요한 점은 자신을 제외하여야 하기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;answer += nameLengthCount[ 7 ]&lt;span style=&quot;background-color: #ffffff;&quot;&gt; -&lt;/span&gt;&lt;/span&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;span style=&quot;background-color: #99cefa;&quot;&gt;1&lt;/span&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;/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;다음 과정을 하기위해서 1등 친구(이름 길이 7)의 count를 빼주고, 4등 친구(이름 길이 5)의 count를 추가해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x1PSt/btsL8epWGwe/HnvFYqHyEzJVwRTKJ0BFX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x1PSt/btsL8epWGwe/HnvFYqHyEzJVwRTKJ0BFX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x1PSt/btsL8epWGwe/HnvFYqHyEzJVwRTKJ0BFX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx1PSt%2FbtsL8epWGwe%2FHnvFYqHyEzJVwRTKJ0BFX1%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;600&quot; height=&quot;420&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;420&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;answer + = nameLengthCount[5]&amp;nbsp; - 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, answer+ = 2 - 1&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;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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0fcEW/btsL6aXcWEs/XpraK23mZbl1dXt5wKAENk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0fcEW/btsL6aXcWEs/XpraK23mZbl1dXt5wKAENk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0fcEW/btsL6aXcWEs/XpraK23mZbl1dXt5wKAENk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0fcEW%2FbtsL6aXcWEs%2FXpraK23mZbl1dXt5wKAENk%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;600&quot; height=&quot;377&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;377&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;소스코드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1738660754760&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;


public class Main {
    public static void main(String[] args)throws IOException{
        BufferedReader br= new BufferedReader(new InputStreamReader(System.in));
        String[] input =br.readLine().split(&quot; &quot;);
        int N =Integer.parseInt(input[0]);
        int K =Integer.parseInt(input[1]);

        long answer=0;

        //이름 길이를 저장할 배열 생성(최대 20자니까 20+1으로 생성)
        int[] nameLengthCount = new int[21];

        int[] nameLength = new int[N];
        for(int i =0;i&amp;lt;N;i++){
            nameLength[i]=br.readLine().length();
        }

        for(int i=0;i&amp;lt;K;i++){
            nameLengthCount[nameLength[i]]++;
        }

        for(int i=0;i&amp;lt;N-1;i++){
            if((i+K)&amp;lt;=N-1) nameLengthCount[nameLength[i+K]]++;
            answer+=nameLengthCount[nameLength[i]]-1;
            nameLengthCount[nameLength[i]]--;
        }

        System.out.println(answer);

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>코팅테스트</category>
      <author>민석 개발일지</author>
      <guid isPermaLink="true">https://minseok1015.tistory.com/22</guid>
      <comments>https://minseok1015.tistory.com/22#entry22comment</comments>
      <pubDate>Tue, 4 Feb 2025 18:19:26 +0900</pubDate>
    </item>
    <item>
      <title>[코딩 테스트] 오큰수</title>
      <link>https://minseok1015.tistory.com/21</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&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://www.acmicpc.net/problem/17298&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/17298&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;h3 data-ke-size=&quot;size23&quot;&gt;문제 풀이 생각과 과정&lt;/h3&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;&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;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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스택에 지금 내가 확인한 수를 push한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;스택의 최상단에 있는 수가 내가 지금 확인하는 수보다 작거나 같으면, 스택에 있는 수를 빌때까지 pop한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스택이 비게 되면 -1를 저장하게 됨(오큰수가 없는 경우임)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&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;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8faY4/btsL6bn3jZW/r3r63JX0sFVM0WmFr9qloK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8faY4/btsL6bn3jZW/r3r63JX0sFVM0WmFr9qloK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8faY4/btsL6bn3jZW/r3r63JX0sFVM0WmFr9qloK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8faY4%2FbtsL6bn3jZW%2Fr3r63JX0sFVM0WmFr9qloK%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;614&quot; height=&quot;432&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;432&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그 다음 스택이 비어져 있으므로, answer 배열에 -1을 추가한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;340&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRxkOX/btsL7RBwF2f/yVe4J2S8YKYNGOIKAaYUR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRxkOX/btsL7RBwF2f/yVe4J2S8YKYNGOIKAaYUR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRxkOX/btsL7RBwF2f/yVe4J2S8YKYNGOIKAaYUR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRxkOX%2FbtsL7RBwF2f%2FyVe4J2S8YKYNGOIKAaYUR0%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;464&quot; height=&quot;340&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;340&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음 2를 확인하러가고, 스택에는 7이 있다.&lt;/li&gt;
&lt;li&gt;스택의 최상단(7) 이랑, 현재 비교하는 수(2) 중에서 스택에 있는 수가 더 크므로 answer배열에 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;401&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ptf4L/btsL5k6YX7F/gtKamJMAU2Xt0zOhKIgKn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ptf4L/btsL5k6YX7F/gtKamJMAU2Xt0zOhKIgKn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ptf4L/btsL5k6YX7F/gtKamJMAU2Xt0zOhKIgKn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fptf4L%2FbtsL5k6YX7F%2FgtKamJMAU2Xt0zOhKIgKn0%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;500&quot; height=&quot;401&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;401&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;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;다음 5랑 스택의 최상단(2)이랑 확인하는데 스택에 있는 수가 더 작다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;404&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lKFjh/btsL5CfjQnp/FCcYxs4KGnthjhT7bQy35k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lKFjh/btsL5CfjQnp/FCcYxs4KGnthjhT7bQy35k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lKFjh/btsL5CfjQnp/FCcYxs4KGnthjhT7bQy35k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlKFjh%2FbtsL5CfjQnp%2FFCcYxs4KGnthjhT7bQy35k%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;540&quot; height=&quot;404&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;404&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스택에서 2를 pop해준다음&lt;/li&gt;
&lt;li&gt;7이랑 비교를 한다.&lt;/li&gt;
&lt;li&gt;7이 5보다 더 크니까 7을 answer에 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;374&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8RNnP/btsL5BgmcGD/1mHHNYdb0vMKmsyfh0d9x0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8RNnP/btsL5BgmcGD/1mHHNYdb0vMKmsyfh0d9x0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8RNnP/btsL5BgmcGD/1mHHNYdb0vMKmsyfh0d9x0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8RNnP%2FbtsL5BgmcGD%2F1mHHNYdb0vMKmsyfh0d9x0%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;500&quot; height=&quot;374&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;374&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그 다음 5랑 3이랑 비교를 하면 5가 더 크니까, 5를 저장하면 끝나게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;357&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9fqhZ/btsL6rRIyO6/W5NK9G8ZH3krKza0WKQuXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9fqhZ/btsL6rRIyO6/W5NK9G8ZH3krKza0WKQuXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9fqhZ/btsL6rRIyO6/W5NK9G8ZH3krKza0WKQuXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9fqhZ%2FbtsL6rRIyO6%2FW5NK9G8ZH3krKza0WKQuXK%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;500&quot; height=&quot;357&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;357&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;/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;5 7 7 -1 을 출력해주면 된다.&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;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1738649595464&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        int[] numbers = Arrays.stream(br.readLine().split(&quot; &quot;)).mapToInt(Integer::parseInt).toArray();
        int[] answer = new int[n];
        ArrayDeque&amp;lt;Integer&amp;gt; stack =new ArrayDeque&amp;lt;&amp;gt;();
        A:for(int i = n-1  ; i&amp;gt;=0; i--){
            while(!stack.isEmpty()){
                if(stack.peek()&amp;gt;numbers[i]){
                    answer[i]=stack.peek();
                    stack.push(numbers[i]);
                    continue A;
                }else{
                    stack.pop();
                }
            }
            stack.push(numbers[i]);
            answer[i] =-1;
        }

        StringBuilder sb =new StringBuilder();
        for(int i = 0 ; i&amp;lt;n;i++){
            sb.append(answer[i]);
            sb.append(&quot; &quot;);
        }
        System.out.println(sb.toString().trim());
    }
}&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;</description>
      <category>코팅테스트</category>
      <author>민석 개발일지</author>
      <guid isPermaLink="true">https://minseok1015.tistory.com/21</guid>
      <comments>https://minseok1015.tistory.com/21#entry21comment</comments>
      <pubDate>Tue, 4 Feb 2025 15:13:29 +0900</pubDate>
    </item>
    <item>
      <title>[코딩 테스트] 칸토어 집합</title>
      <link>https://minseok1015.tistory.com/20</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 출처&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/4779&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/4779&lt;/a&gt;&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;/h3&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;재귀 종료 조건은 마지막 한 개 남았을 경우 재귀 종료&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;예시는 N이 2일 경우이고, 즉 3^2 = 9 개의 집합이 있을 때이다.&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;blob&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;270&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgHMJg/btsL1y4aqoe/BG7MqeCjo0nXv3YlOVOaiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgHMJg/btsL1y4aqoe/BG7MqeCjo0nXv3YlOVOaiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgHMJg/btsL1y4aqoe/BG7MqeCjo0nXv3YlOVOaiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgHMJg%2FbtsL1y4aqoe%2FBG7MqeCjo0nXv3YlOVOaiK%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;700&quot; height=&quot;270&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;270&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;이런식으로 재귀를 호출하면서 분할 정복으로 마지막 정복이 됐을 때 결과를 Print해주면 된다. StringBuilder를 통해서 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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;자바 문법 알게된 점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;StringBuilder&lt;/b&gt;에 있는 &lt;b&gt;replace&lt;/b&gt;(start,end,..) 메서드를 통해서 빈 문자열로 바꾸는 작업을 했는데 여기서 주의할 점이 있었다.&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;replace(start, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;end&lt;/span&gt; ,바꿀 문자열) 인데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;b&gt;end&lt;/b&gt;는 &lt;b&gt;end-1&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;/p&gt;
&lt;pre id=&quot;code_1737821761171&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;StringBuilder sb = new StringBuilder();
sb.append(&quot;Hello!&quot;);
sb.replace(0,3,&quot;X&quot;);&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;나는 &quot;&lt;span style=&quot;background-color: #99cefa;&quot;&gt;Hello!&lt;/span&gt;&quot; 문자열에서 0번부터 3번 인덱스. 즉, &lt;span style=&quot;background-color: #99cefa;&quot;&gt;H&lt;/span&gt;,&lt;span style=&quot;background-color: #99cefa;&quot;&gt;e&lt;/span&gt;,&lt;span style=&quot;background-color: #99cefa;&quot;&gt;l&lt;/span&gt;,&lt;span style=&quot;background-color: #99cefa;&quot;&gt;l&lt;/span&gt; 까지 &lt;b&gt;X&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;&quot;&lt;span style=&quot;background-color: #99cefa;&quot;&gt;Xo!&lt;/span&gt;&quot; 라는 문자열이 나올 줄 알았지만&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;end-1&lt;/b&gt; 인덱스 까지. 즉, &lt;span style=&quot;background-color: #99cefa;&quot;&gt;H&lt;/span&gt;,&lt;span style=&quot;background-color: #99cefa;&quot;&gt;e&lt;/span&gt;,&lt;span style=&quot;background-color: #99cefa;&quot;&gt;l&lt;/span&gt; 까지 &lt;b&gt;X&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;결과는 &quot;&lt;b&gt;Xlo!&lt;/b&gt;&quot;라는 문자열이 출력이 되게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;또한 String에서 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;repeat()메서드&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;/p&gt;
&lt;pre id=&quot;code_1737822041386&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;String space = &quot;-&quot;.repeat(10);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 하게되면&lt;/p&gt;
&lt;pre id=&quot;code_1737822074414&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;String space = &quot;----------&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 선언한 것과 같게 된다.&lt;/p&gt;</description>
      <category>코팅테스트</category>
      <author>민석 개발일지</author>
      <guid isPermaLink="true">https://minseok1015.tistory.com/20</guid>
      <comments>https://minseok1015.tistory.com/20#entry20comment</comments>
      <pubDate>Sun, 26 Jan 2025 01:21:47 +0900</pubDate>
    </item>
  </channel>
</rss>