<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>HJ</title>
    <link>https://gudwns5533.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Fri, 17 Jul 2026 06:04:37 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>gudwns5533</managingEditor>
    <item>
      <title>WebRTC</title>
      <link>https://gudwns5533.tistory.com/66</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;WebRTC란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Web Real-Time Communication의 약자로, 브라우저나 앱에서 실시간 음성, 영상, 화면 공유, 파일/데이터 전송을 구현할 수 있게 해주는 기술을 말한다. Zoom, Google Meet, Discord 음성/영상 통화 같은 기능을 웹에서 만들 수 있게 해주는 기술이라 생각하면 된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;WebRTC의 기능&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 120px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.2558%; height: 20px; text-align: center;&quot;&gt;기능&lt;/td&gt;
&lt;td style=&quot;width: 71.7442%; height: 20px; text-align: center;&quot;&gt;예시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.2558%; height: 20px; text-align: center;&quot;&gt;화상 통화&lt;/td&gt;
&lt;td style=&quot;width: 71.7442%; height: 20px;&quot;&gt;1:1 영상 통화, 온라인 수업&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.2558%; height: 20px; text-align: center;&quot;&gt;음성 통화&lt;/td&gt;
&lt;td style=&quot;width: 71.7442%; height: 20px;&quot;&gt;브라우저 기반 전화, 음성 채팅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.2558%; height: 20px; text-align: center;&quot;&gt;화면 공유&lt;/td&gt;
&lt;td style=&quot;width: 71.7442%; height: 20px;&quot;&gt;회의 화면 공유, 원격 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.2558%; height: 20px; text-align: center;&quot;&gt;실시간 데이터 전송&lt;/td&gt;
&lt;td style=&quot;width: 71.7442%; height: 20px;&quot;&gt;채팅, 파일 전송, 게임 상태 동기화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.2558%; height: 20px; text-align: center;&quot;&gt;AI 서비스 연동&lt;/td&gt;
&lt;td style=&quot;width: 71.7442%; height: 20px;&quot;&gt;웹캠 영상 기반 자세 분석, 손 인식, 얼굴 인식&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;WebRTC의 핵심 개념&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;mediaStream&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카메라, 마이크, 화면 공유 같은 미디어 데이터 흐름을 말한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783480608547&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;navigator.mediaDevices.getUserMedia({
    video: true,
    audio: true
});&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;웹캠을 켜면 브라우저는 카메라 영상을 MediaStream으로 가져온다. 이렇게 가져온 영상은 &amp;lt;video&amp;gt; 태그에 보여주거나, 다른 사용자에게 전송할 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;RTCPeerConnection&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebRTC의 가장 핵심 객체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 사용자, 즉 Peer A와 Peer B를 연결해서 영상, 음성, 데이터를 주고받게 해주는 객체이다.&lt;/p&gt;
&lt;pre id=&quot;code_1783480775946&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const peerConnection = new RTCPeerConnection();&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;RTCPeerConnection을 로컬 컴퓨터와 원격 피어 사이의 WebRTC 연결을 나타내며, 효율적인 데이터 스트리밍을 처리하는 인터페이스로 설명한다. 여기에 카메라 영상, 마이크 음성, 데이터 채널 등을 붙여서 상대방과 통신한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, RTCPeerConnection의 역할은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내 영상/음성 트랙을 연결에 추가&lt;/li&gt;
&lt;li&gt;상대방 영상/음성 트랙 수신&lt;/li&gt;
&lt;li&gt;Offer 생성&lt;/li&gt;
&lt;li&gt;Answer 생성&lt;/li&gt;
&lt;li&gt;ICE Candidate 수집&lt;/li&gt;
&lt;li&gt;연결 상태 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;RTCDataChannel&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영상이나 음성이 아니라 일반 데이터를 실시간으로 주고받을 때 사용한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;WebRTC의 연결 원리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebRTC는 최종적으로 P2P, 즉 사용자끼리 직접 연결되는 구조를 목표로 한다. 하지만 처음 연결할 때는 서로의 정보를 교환해야 한다. 이 과정을 시그널링(Signaling)이라 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783487544657&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;사용자 A 브라우저
   │
   │ 연결 정보 교환
   ▼
시그널링 서버
   ▲
   │ 연결 정보 교환
   │
사용자 B 브라우저

이후 연결 성공

사용자 A 브라우저 ◀────────▶ 사용자 B 브라우저
        영상 / 음성 / 데이터 직접 전송&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;WebRTC는 P2P 통신을 지원하지만, 실제 연결을 조율하기 위한 메타데이터 교환에는 시그널링 서버가 필요하다. 또한 NAT나 방화벽 환경을 처리하기 위해 STUN/TURN 서버가 사용된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Signaling 서버란?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Signaling 서버는 WebRTC 연결을 만들기 위해 브라우저 사이에서 연결 정보를 전달하는 서버를 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Signaling 서버가 전달하는 대표적인 정보&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Offer&lt;/li&gt;
&lt;li&gt;Answer&lt;/li&gt;
&lt;li&gt;ICE Candidate&lt;/li&gt;
&lt;li&gt;방 입장 정보&lt;/li&gt;
&lt;li&gt;사용자 나가기 정보&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Offer와&amp;nbsp; Answer&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Offer란?&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Offer는 연결을 먼저 시작하는 쪽이 만드는 연결 제안 정보를 말한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783494371034&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);

socket.emit(&quot;offer&quot;, {
    roomId,
    offer
});&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;Offer 안에는 다음과 같은 정보가 들어간다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내가 영상/음성을 보낼 수 있는지&lt;/li&gt;
&lt;li&gt;어떤 코덱을 사용할 수 있는지&lt;/li&gt;
&lt;li&gt;어떤 해상도나 미디어 타입을 사용할 수 있는지&lt;/li&gt;
&lt;li&gt;WebRTC 연결을 위한 세션 정보&lt;/li&gt;
&lt;li&gt;ICE Candidate 수집을 위한 기반정보&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Offer는 보통 SDP(Session Description Protocol)형식으로 만들어진다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Answer란?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Answer는 Offer를 받은 쪽이 만드는 응답 정보를 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B 브라우저가 A의 Offer를 받으면 먼저 Offer를 자신의 remoteDescription으로 등록한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783495253082&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;await peerConnection.setRemoteDescription(offer);&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;그 후에 Answer를 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1783495279726&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);&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;그리고 이 Answer를 다시 A에게 보낸다.&lt;/p&gt;
&lt;pre id=&quot;code_1783495299185&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;socket.emit(&quot;answer&quot;, {
  roomId,
  answer
});&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;즉, Answer는 Offer에 대한 수락 및 연결 조건 응답이라고 생각하면 된다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;SDP&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SDP는 Session Description Protocol의 줄임말이다. Offer와 Answer안에 들어있는 실제 연결 설명 정보이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SDP안에는 다음과 같은 내용이 들어있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오디오를 쓸지&lt;/li&gt;
&lt;li&gt;비디오를 쓸지&lt;/li&gt;
&lt;li&gt;어떤 코덱을 쓸지&lt;/li&gt;
&lt;li&gt;어떤 암호화 정보를 쓸지&lt;/li&gt;
&lt;li&gt;어떤 미디어 라인을 사용할지&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1783653725932&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const offer = await peerConnection.createOffer();&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;STUN, TURN, ICE Candidate란?&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;STUN 서버&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 공유기나 NAT 뒤에 있을 때, 외부 인터넷에서 어떤 IP와 포트로 보이는지 알려주는 서버이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICE는 두 피어를 연결하기 위한 후보 경로를 찾는 프레임워크이고, STUN은 NAT 뒤의 사용자가 자신의 공개 주소와 포트를 알 수 있게 도와준다.&lt;/p&gt;
&lt;pre id=&quot;code_1783488432820&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const peerConnection = new RTCPeerConnection({
    iceServers: [
    	{ urls: &quot;stun:stun.l.google.com:19302&quot; }
    ]
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;TURN 서버&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저끼리 직접 연결이 안 될 때 사용하는 중계 서버를 말한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783488338568&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;사용자 A ─── TURN 서버 ─── 사용자 B&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;직접 연결보다 비용과 지연시간이 더 들지만, 회사망, 학교망, 강한 방화벽 환경에서는 TURN이 필요할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1783488737794&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const peerConnection = new RTCPeerConnection ({
    iceServers: [{
    	urls: &quot;turn:your-turn-server.com:3478&quot;,
        username: &quot;turn-user&quot;,
        credential: &quot;turn-password&quot;
    }]
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;ICE Candidate&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Offer와 Answer만 교환한다고 연결이 되는 것이 아니다. 브라우저끼리 실제로 통신할 수 있는 네트워크 경로를 찾아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 사용하는 것이 ICE Candidate이다. 쉽게 말하자면 연결 가능한 네트워크 후보군을 찾는거라 생각하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1783654315517&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 보내는 코드

peerConnection.onicecandidate = (event) =&amp;gt; {
    if (event.candidate) {
    	socket.emit(&quot;ice-candidate&quot;, {
            roomId,
            candidate: event.candidate
        });
    }
};

// 받는 코드

await peerConnection.addIceCandidate(candidate);&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;ICE Candidate는 두 브라우저가 실제로 연결 가능한 네트워크 경로 후보를 말한다. 후보는 하나가 아니라 여러 개 생길 수 있고, 양쪽 브라우저가 이 후보들을 서로 교환하면서 가장 적절한 경로를 찾게된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;WebSocket과 WebRTC&lt;/b&gt;&lt;/h2&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.1007%; text-align: center;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 37.6357%; text-align: center;&quot;&gt;WebSocket&lt;/td&gt;
&lt;td style=&quot;width: 44.2635%; text-align: center;&quot;&gt;WebRTC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.1007%; text-align: center;&quot;&gt;구조&lt;/td&gt;
&lt;td style=&quot;width: 37.6357%; text-align: center;&quot;&gt;클라이언트 &amp;harr; 서버&lt;/td&gt;
&lt;td style=&quot;width: 44.2635%;&quot;&gt;클라이언트 &amp;harr; 클라이언트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.1007%; text-align: center;&quot;&gt;주요 용도&lt;/td&gt;
&lt;td style=&quot;width: 37.6357%; text-align: center;&quot;&gt;채팅, 알림, 실시간 상태 업데이트&lt;/td&gt;
&lt;td style=&quot;width: 44.2635%;&quot;&gt;영상통화, 음성통화, 화면 공유, P2P 데이터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.1007%; text-align: center;&quot;&gt;서버 필요성&lt;/td&gt;
&lt;td style=&quot;width: 37.6357%; text-align: center;&quot;&gt;서버가 핵심&lt;/td&gt;
&lt;td style=&quot;width: 44.2635%;&quot;&gt;연결 조율용 서버 필요 이후 P2P 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.1007%; text-align: center;&quot;&gt;대용량 미디어&lt;/td&gt;
&lt;td style=&quot;width: 37.6357%; text-align: center;&quot;&gt;직접 구현 부담 큼&lt;/td&gt;
&lt;td style=&quot;width: 44.2635%;&quot;&gt;미디어 전송에 특화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.1007%; text-align: center;&quot;&gt;지연시간&lt;/td&gt;
&lt;td style=&quot;width: 37.6357%; text-align: center;&quot;&gt;낮은 편&lt;/td&gt;
&lt;td style=&quot;width: 44.2635%;&quot;&gt;매주 낮은 실시간 통신 지향&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;전체 WebRTC 연결 흐름&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1783495407744&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[1] 웹페이지 접속
      &amp;darr;
[2] Socket.IO 서버 연결
      &amp;darr;
[3] 방 생성 / 방 입장
      &amp;darr;
[4] getUserMedia() 또는 getDisplayMedia()로 미디어 스트림 획득
      &amp;darr;
[5] RTCPeerConnection 생성
      &amp;darr;
[6] addTrack()으로 영상/음성 track 추가
      &amp;darr;
[7] Offer 생성
      &amp;darr;
[8] Offer를 시그널링 서버로 전달
      &amp;darr;
[9] 상대방이 Offer 수신
      &amp;darr;
[10] 상대방이 Answer 생성
      &amp;darr;
[11] Answer를 시그널링 서버로 전달
      &amp;darr;
[12] Offer 생성자가 Answer 등록
      &amp;darr;
[13] ICE Candidate 교환
      &amp;darr;
[14] STUN/TURN을 통해 연결 경로 확정
      &amp;darr;
[15] WebRTC 연결 성공
      &amp;darr;
[16] 영상/음성/화면/데이터 실시간 전송
      &amp;darr;
[17] 필요 시 화면 공유 track 교체 또는 DataChannel 사용
      &amp;darr;
[18] 종료 시 track.stop() + peerConnection.close()&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 사용자가 서비스에 접속한다&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1783655443925&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;사용자 A &amp;rarr; http://localhost:3000 접속
사용자 B &amp;rarr; http://localhost:3000 접속&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;이 단계에서는 아직 WebRTC 연결이 된게 아니다. 그냥 웹페이지에만 접속한 상태이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저는 HTML, CSS, JavaScript를 로드하고, Socket.IO 같은 시그널링 서버와 먼저 연결된다.&lt;/p&gt;
&lt;pre id=&quot;code_1783655509533&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const socket = io();&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;여기서 Socket.IO는 영상이나 음성을 보내는 용도가 아니라 Offer, Answer, ICE Candidate 같은 연결 정보를 전달하는 용도이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 방 생성 / 방 입장&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebRTC는 기본적으로 &quot;누구와 연결할지&quot;를 스스로 찾아주지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 보통 방 코드나 사용자 ID를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783655609132&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;A: study123 방 생성
B: study123 방 입장&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;이때, Signaling 서버에서는 같은 방에 들어온 사용자끼리 연결 정보를 주고받도록 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783655664328&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;socket.emit(&quot;join-room&quot;, roomId);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버는 같은 방에 있는 다른 사용자에게 알려준다.&lt;/p&gt;
&lt;pre id=&quot;code_1783655685997&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;socket.to(roomId).emit(&quot;user-joined&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 방에 참가한 A와 B를 연결 대상으로 묶는다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 카메라, 마이크, 화면 공유 스트림 준비&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 이 서비스가 화상 채팅이라면 먼저 카메라와 마이크를 가져온다.&lt;/p&gt;
&lt;pre id=&quot;code_1783655754390&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 화상채팅

const localStream = await navigator.mediaDevices.getUserMedia({
  video: true,
  audio: true
});

// 화면 공유

const screenStream = await navigator.mediaDevices.getDisplayMedia({
  video: true,
  audio: false
});&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;이렇게 가져온 결과는 MediaStream이다. MediaStream 안에는 실제로는 여러 개의 MediaStreamTrack이 들어 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1783655859761&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MediaStream
 ├─ video track
 └─ audio track
 
// 화상 채팅의 경우
카메라 영상 track
마이크 음성 track

// 화면 공유의 경우
화면 영상 track&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;이 트랙들을 나중에 RTCPeerConnection에 추가해서 상대방에게 보낸다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. RTCPeerConnection 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 WebRTC 연결 객체를 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1783655953461&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const peerConnection = new RTCPeerConnection({
  iceServers: [
    {
      urls: &quot;stun:stun.l.google.com:19302&quot;
    }
  ]
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RTCPeerConnection은 두 브라우저 사이의 WebRTC 연결을 나타내는 핵심 객체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 iceServers에 STUN이나 TURN 서버 정보를 넣는다.&lt;/p&gt;
&lt;pre id=&quot;code_1783656408911&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;STUN:
내가 외부에서 어떤 IP/포트로 보이는지 확인

TURN:
직접 연결이 안 될 때 중계 서버 역할&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5. 내 미디어 track을 PeerConnection에 추가&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카메라나 마이크 스트림을 가져왔다면 그 안의 track을 WebRTC 연결에 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783656588779&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 화상 채팅의 경우
localStream.getTracks().forEach((track) =&amp;gt; {
  peerConnection.addTrack(track, localStream);
});

// 화면 공유의 경우
screenStream.getTracks().forEach((track) =&amp;gt; {
  peerConnection.addTrack(track, screenStream);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계의 의미는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1783656684682&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;video track &amp;rarr; 상대방 video 태그에 표시됨
audio track &amp;rarr; 상대방 스피커로 출력됨&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;6. 이벤트 리스너 등록&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebRTC에서는 중요한 이벤트를 미리 등록해둔다.&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;&lt;b&gt;6-1. ICE Candidate 이벤트&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1783656751377&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;peerConnection.onicecandidate = (event) =&amp;gt; {
  if (event.candidate) {
    socket.emit(&quot;ice-candidate&quot;, {
      roomId,
      candidate: event.candidate
    });
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 이벤트는 브라우저가 연결 가능한 네트워크 후보를 찾을 때마다 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;6-2. 상대방 미디어 수신 이벤트&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1783656847293&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;peerConnection.ontrack = (event) =&amp;gt; {
  remoteVideo.srcObject = event.streams[0];
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 이벤트는 상대방의 영상이나 음성이 도착했을 때 실행된다.&lt;/p&gt;
&lt;pre id=&quot;code_1783656880939&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;상대방 video track 도착
   &amp;darr;
remoteVideo에 출력&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;size18&quot;&gt;&lt;b&gt;6-3. 연결 상태 확인 이벤트&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1783656895729&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;peerConnection.onconnectionstatechange = () =&amp;gt; {
  console.log(peerConnection.connectionState);
};&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;7. Offer 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 연결을 먼저 시작하는 쪽이 Offer를 생성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783656935860&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);&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;Offer는 쉽게 말하면 연결 제안서다.&lt;/p&gt;
&lt;pre id=&quot;code_1783656955027&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;ldquo;나는 영상과 음성을 보낼 수 있고,
이런 코덱과 이런 보안 방식으로 통신할 수 있어.
연결할래?&amp;rdquo;&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;Offer 안에는 SDP라는 형식의 정보가 들어간다.&lt;/p&gt;
&lt;pre id=&quot;code_1783657077054&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SDP 안에 들어가는 대표 정보:
- 오디오 사용 여부
- 비디오 사용 여부
- 코덱 정보
- 미디어 방향
- 암호화 관련 정보
- ICE 관련 정보&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;Offer를 만든 뒤에는 반드시 setLocalDescription()을 호출한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783657104736&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;await peerConnection.setLocalDescription(offer);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;8. Offer를 시그널링 서버로 전달&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Offer는 WebRTC가 자동으로 상대방에게 보내주지 않는다. 직접 서버를 통해 전달해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783657250166&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;socket.emit(&quot;offer&quot;, {
  roomId,
  offer
});&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;서버는 같은 방의 상대방에게 이 Offer를 전달해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1783657271511&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;socket.on(&quot;offer&quot;, ({ roomId, offer }) =&amp;gt; {
  socket.to(roomId).emit(&quot;offer&quot;, offer);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정의 흐름은 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1783657299028&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;A 브라우저
   &amp;darr; offer
시그널링 서버
   &amp;darr; offer
B 브라우저&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;9. 상대방이 Offer를 받는다&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B 브라우저는 A가 보낸 Offer를 받는다.&lt;/p&gt;
&lt;pre id=&quot;code_1783657321112&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;socket.on(&quot;offer&quot;, async (offer) =&amp;gt; {
  await peerConnection.setRemoteDescription(offer);
});&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;여기서 setRemoteDescription()은 &quot;상대방의 연결 정보는 이 offer야&quot; 라는 의미를 갖는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, B의 입장에서는&lt;/p&gt;
&lt;pre id=&quot;code_1783657388579&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;localDescription = 내 정보
remoteDescription = 상대방 정보&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;10. Answer 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B는 Offer를 받은 뒤 Answer를 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1783657441384&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);&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;Answer는 연결 응답서이다. 그리고 이 Answer를 다시 A에게 보낸다.&lt;/p&gt;
&lt;pre id=&quot;code_1783658430316&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;socket.emit(&quot;answer&quot;, {
  roomId,
  answer
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정의 흐름은 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1783658457725&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;B 브라우저
   &amp;darr; answer
시그널링 서버
   &amp;darr; answer
A 브라우저&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;11. A가 Answer를 받는다&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A는 B가 보낸 Answer를 받는다.&lt;/p&gt;
&lt;pre id=&quot;code_1783658631257&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;socket.on(&quot;answer&quot;, async (answer) =&amp;gt; {
  await peerConnection.setRemoteDescription(answer);
});&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;이 단계가 끝나면 Offer / Answer 주고받기는 완료된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 까지를 정리하자면&lt;/p&gt;
&lt;pre id=&quot;code_1783658719182&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;A:
createOffer()
setLocalDescription(offer)
offer 전송

B:
setRemoteDescription(offer)
createAnswer()
setLocalDescription(answer)
answer 전송

A:
setRemoteDescription(answer)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;12. ICE Candidate 교환&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Offer / Answer가 &quot;통신 조건 협상&quot;이라면, ICE Candidate는 &quot;실제 연결 경로 찾기&quot;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 두 사용자가 각자 공유기 뒤에 있을 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1783658876112&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;A 내부 IP: 192.168.x.x
B 내부 IP: 192.168.x.x&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;이 내부 IP는 인터넷에서 바로 접근할 수 없다. 그래서 WebRTC는 STUN 서버를 통해 외부에서 보이는 주소를 찾는다.&lt;/p&gt;
&lt;pre id=&quot;code_1783659010547&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;A:
내가 외부에서는 211.xxx.xxx.xxx:53000으로 보이는구나.

B:
나는 외부에서 118.xxx.xxx.xxx:62000으로 보이는구나.&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;이런 후보 주소들을 ICE Candidate라고 한다. 브라우저가 후보를 찾으면 onicecandidate 이벤트가 발생하고, 이 후보를 상대방에게 보낸다.&lt;/p&gt;
&lt;pre id=&quot;code_1783659042553&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;peerConnection.onicecandidate = (event) =&amp;gt; {
  if (event.candidate) {
    socket.emit(&quot;ice-candidate&quot;, {
      roomId,
      candidate: event.candidate
    });
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상대방은 받은 후보를 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783659055433&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;socket.on(&quot;ice-candidate&quot;, async (candidate) =&amp;gt; {
  await peerConnection.addIceCandidate(candidate);
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;13. ICE가 최적의 경로를 선택한다&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICE는 여러 후보 중에서 실제로 연결 가능한 경로를 테스트한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783659100521&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;후보 1: 같은 네트워크 내부 연결
후보 2: 공인 IP를 통한 직접 연결
후보 3: TURN 서버 중계 연결&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;가능하다면 직접 P2P 연결을 선택한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783659120823&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;A 브라우저 ───────────── B 브라우저&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;직접 연결이 안 되면 TURN 서버를 거쳐 연결한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783659139672&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;A 브라우저 ───── TURN 서버 ───── B 브라우저&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;즉, WebRTC가 항상 100% 직접 P2P로만 연결되는 것은 아니다. 네트워크 환경에 따라 TURN 서버를 중계 경로로 사용할 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;14. 연결 성공 후 미디어 전송 시작&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연결이 성공하면 실제 영상, 음성, 화면 공유 데이터가 흐르기 시작한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783659210098&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;A의 카메라 영상 ─────▶ B의 remoteVideo
A의 마이크 음성 ─────▶ B의 스피커

B의 카메라 영상 ─────▶ A의 remoteVideo
B의 마이크 음성 ─────▶ A의 스피커&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;이때 중요한 점은 시그널링 서버는 더 이상 영상 / 음성 데이터를 중계하지 않는다는 점이다. 시그널링 서버는 처음 연결 정보를 전달하는 역할이고, 실제 미디어는 WebRTC 연결을 통해 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, TURN 서버를 사용하는 경우에는 실제 미디어가 TURN 서버를 통해 중계된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;15. DataChannel이 있다면 데이터도 전송 가능&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화상 / 음성 뿐만 아니라 RTCDataChannel을 사용하면 텍스트나 파일 같은 일반 데이터도 보낼 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;16. 화면 공유를 시작할 때&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 화상 채팅이 연결된 상태에서 화면 공유를 시작하는 경우가 있을 수 있다. 처음에는 카메라 track을 보내가 있다고 가정해보면,&lt;/p&gt;
&lt;pre id=&quot;code_1783659443486&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;카메라 video track &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;이때, 화면 공유를 시작하면 getDisplayMedia()로 화면 track을 가져온다.&lt;/p&gt;
&lt;pre id=&quot;code_1783659465801&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const screenStream = await navigator.mediaDevices.getDisplayMedia({
  video: true,
  audio: false
});&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;그리고 기존 video sender의 track을 교체한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783659484050&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;await videoSender.replaceTrack(screenTrack);&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;그러면 상대방은 같은 remoteVideo에서 이제 카메라 대신 화면 공유를 보게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1783659519161&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;기존:
상대방이 내 카메라 영상 시청

화면 공유 후:
상대방이 내 화면 공유 시청&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 보통 재협상 없이 작동하게된다. 즉, 새 Offer / Answer 없이도 카메라 영상 track을 화면 공유 track으로 교체할 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;17. 연결 종료&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 나가거나 통화를 종료하면 다음 처리를 하게된다.&lt;/p&gt;
&lt;pre id=&quot;code_1783659580959&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;localStream.getTracks().forEach((track) =&amp;gt; {
  track.stop();
});

peerConnection.close();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드의 역할을 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1783659608876&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;1. 카메라 track 종료
2. 마이크 track 종료
3. 화면 공유 track 종료
4. PeerConnection 닫기
5. 상대방에게 나갔다고 알림
6. UI 초기화&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;  이 전체 과정을 요약하자면, WebRTC는 시그널링 서버로 연결 정보를 교환한 뒤, RTCPeerConnection이 ICE를 통해 실제 연결 경로를 찾고, 그 경로로 영상 / 음성 / 화면 / 데이터를 실시간 전송하는 구조라고 생각하면 된다.&lt;/p&gt;</description>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/66</guid>
      <comments>https://gudwns5533.tistory.com/66#entry66comment</comments>
      <pubDate>Wed, 8 Jul 2026 17:47:52 +0900</pubDate>
    </item>
    <item>
      <title>Node.js 기초 1</title>
      <link>https://gudwns5533.tistory.com/65</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Node.js란?&lt;/b&gt;&lt;/h2&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;구글의 V8 엔진을 기반으로 동작하며, 웹 서버, API 서버, 실시간 채팅, 스트리밍 서비스 등에서 활용된다.&lt;/li&gt;
&lt;li&gt;npm (Node Package Manager)을 통해 수많은 오픈소스 패키지를 쉽게 사용할 수 있어 빠른 개발이 가능하다.&lt;/li&gt;
&lt;li&gt;프론트엔드와 동일한 언어(JavaScript)를 사용한다는 점에서 풀스택 개발에 매우 유리한 환경이다.&lt;/li&gt;
&lt;/ul&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;size18&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;사용자가 직접 보고 상호작용하는 화면(UI)을 구현하는 영역.&lt;/li&gt;
&lt;li&gt;HTML / CSS / JavaScript를 사용해 버튼, 입력창, 화면 레이아웃, 애니메이션 등 사용자 경험을 담당함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&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;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;패키지 매니저&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Node.js 환경에서 필요한 라이브러리(패키지)를 설치, 관리, 업데이트 할 수 있게 도와주는 도구&lt;/li&gt;
&lt;li&gt;npm (Node Package Manager) : Node.js 설치 시 자동으로 같이 설치 됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;package.json&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Node.js 프로젝트의 정보를 담고 있는 핵심 파일&lt;/li&gt;
&lt;li&gt;패키지에 대한 설명서 (프로젝트 이름, 버전, 필요한 패키지 목록, 실행 스크립트 등 다양한 정보를 담고있다)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1782618693906&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;7_nodejs&quot;,   / 프로젝트 이름(소문자로 작성, 공백 x)
  &quot;version&quot;: &quot;1.0.0&quot;,   / 프로젝트 버전
  &quot;description&quot;: &quot;&quot;,    / 간단한 설명
  &quot;main&quot;: &quot;index.js&quot;,   / 진입점 파일
  &quot;author&quot;: &quot;&quot;,         / 만든 사람 이름
  &quot;license&quot;: &quot;ISC&quot;,     / 사용 라이센스
  &quot;type&quot;: &quot;commonjs&quot;    / 프로젝트 방식
  &quot;scripts&quot;: {          / 터미널에서 사용할 실행 명령어 (예 : npm start, npm run dev)
    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;amp;&amp;amp; exit 1&quot;
  },
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;웹서버&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자의 브라우저 요청을 받아 웹 페이지, 이미지, CSS, JavaScript 같은 파일을 전달해주는 서버 프로그램 또는 시스템&lt;/li&gt;
&lt;li&gt;사용자가 주소(URL)을 입력하면 웹서버는 해당 요청을 처리하고 필요한 파일이나 데이터를 응답으로 보내며, 이 과정을 통해 브라우저에 웹사이트 화면이 표시됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;모듈&lt;/b&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;/li&gt;
&lt;li&gt;특정 기능(계산, 인증, 데이터 처리 등)을 하나의 파일로 분리해두면, 다른 파일에서 필요할 때 불러와 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;require() 또는 import를 통해 모듈을 가져올 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;CommonJS vs ES6&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;CommonJS&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;node.js 기본 방식&lt;/li&gt;
&lt;li&gt;모듈 가져오기: require('파일')&lt;/li&gt;
&lt;li&gt;모듈 내보내기: module.exports 또는 exports.이름&lt;/li&gt;
&lt;li&gt;확장자: .js&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;ES6&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ES6를 지원하는 브라우저 또는 node.js 환경&lt;/li&gt;
&lt;li&gt;모듈 가져오기: import (이름) from '파일'&lt;/li&gt;
&lt;li&gt;모듈 내보내기: export 또는 export default&lt;/li&gt;
&lt;li&gt;확장자: .mjs&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;http 모듈&lt;/b&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;/li&gt;
&lt;li&gt;클라이언트의 요청을 받고 응답을 반환하는 기능을 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Content-Type&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버가 브라우저에 &quot;지금 보내는 데이터의 형식이 무엇인지 알려주는 HTTP 헤더&quot;&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;text/html: html 문서&lt;/li&gt;
&lt;li&gt;text/plain: 일반 텍스트&lt;/li&gt;
&lt;li&gt;application/json: JSON 데이터&lt;/li&gt;
&lt;li&gt;text/css: css 파일&lt;/li&gt;
&lt;li&gt;application/javascript: JavaScript 파일&lt;/li&gt;
&lt;li&gt;image/png: PNG 이미지&lt;/li&gt;
&lt;li&gt;image/jpeg: JPEG 이미지&lt;/li&gt;
&lt;li&gt;multipart/form-data: 파일 업로드&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Header&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인터넷에서 데이터를 주고받을 때 본문(내용)보다 먼저 전달되는 추가 정보 영역으로, &quot;이 데이터가 무엇인지, 어떻게 처리해야하는지&quot;를 설명해주는 안내문과 같다.&lt;/li&gt;
&lt;li&gt;브라우저가 서버에 요청을 보낼 때는 어떤 형식을 원하는지, 로그인 정보가 있는지 같은 정보를 헤더에 담고, 서버는 응답할 때 데이터 형식이 무엇인지, 캐시 여부는 어떤지 등의 정보를 헤더에 담아서 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;요청 응답&lt;/b&gt;&lt;/h2&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;http 모듈에서 경로 설정(라우팅)은 사용자의 요청 주소를 나타내는 req.url과 요청 방식인 req.method를 확인하여 조건문으로 분기 처리하는 방식으로 이루어진다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;GET과 POST&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;GET&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;GET 요청은 필요한 값을 URL 뒤에 ?key=value 형태의 쿼리 문자열로 함께 전달하며, 브라우저 주소창에 그대로 표시된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;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;/li&gt;
&lt;li&gt;POST 요청은 데이터를 URL이 아니라 요청 본문(body)에 담아 보내기 때문에 주소창에 내용이 보이지 않으며, GET보다 보안성이 조금 더 높고 전송할 수 있는 데이터의 양에 제한이 거의 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;쿼리 문자열&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿼리 문자열(Query String)은 URL 뒤에 ?기호를 기준으로 붙는 추가 데이터 전달 방식으로, 서버에 필요한 값을 함께 보내기 위해 사용된다.&lt;/li&gt;
&lt;li&gt;key=value 형태로 작성하며 여러 개의 값은 &amp;amp; 기호로 연결한다. (예: ?name=김사과&amp;amp;age=20)&lt;/li&gt;
&lt;li&gt;주로 GET 요청에서 사용되며 검색 조건, 페이지 번호, 필터 값 등을 전달할 때 많이 활용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;nodemon&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;node.js 개발 시 주로 사용하는 유틸리티로, 소스 코드가 변경될 때마다 자동으로 서버를 재시작해주는 도구&lt;/p&gt;
&lt;pre id=&quot;code_1782625864797&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install -g nodemon (모든 프로젝트에서 사용 가능)
npm install --save-dev nodemon (해당 프로젝트에서만 사용)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;JSON&lt;/b&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;/li&gt;
&lt;li&gt;일반적으로 서버와 클라이언트 간에 데이터를 주고 받을 때 사용한다.&lt;/li&gt;
&lt;li&gt;구조는 키-값 쌍으로 이루어진 객체 형태나 배열 형태를 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1782626724017&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const user = { name: &quot;김사과&quot;, age: 20 }
const jsonStr = JSON.stringify(user)  // 객체를 문자열로 변환

const jsonStr = &quot;{ name: &quot;김사과&quot;, age: 20 }&quot;
const userObj = JSON.parse(jsonStr)   // 문자열을 객체로 변환&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1782626850058&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const http = require(&quot;http&quot;)
const url = require(&quot;url&quot;)

const server = http.createServer((req, res) =&amp;gt; {

    if(req.url === &quot;/&quot;){    // 루트 경로로 접속 (127.0.0.1:3000)
        res.writeHead(200, {&quot;content-type&quot;: &quot;text/html&quot;})
        res.end(&quot;&amp;lt;h2&amp;gt;Hello Node.js&amp;lt;/h2&amp;gt;&quot;)
    }else if(req.url === &quot;/about&quot;){    // 127.0.0.1:3000/about
        res.writeHead(200, {&quot;content-type&quot;: &quot;text/html&quot;})
        res.end(&quot;&amp;lt;h2&amp;gt;about page&amp;lt;/h2&amp;gt;&quot;)
    }else if(req.url === &quot;/api/user&quot;){    // 127.0.0.1:3000/api/user
        const user = {
            userid: &quot;apple&quot;,
            name: &quot;김사과&quot;,
            age: 20,
            job: &quot;AI개발자&quot;
        }
        res.writeHead(200, {&quot;Content-Type&quot;:&quot;application/json&quot;})
        res.end(JSON.stringify(user))
    }else{
        res.writeHead(404, {&quot;content-type&quot;: &quot;text/html&quot;})
        res.end(&quot;&amp;lt;h2&amp;gt;error  &amp;lt;/h2&amp;gt;&quot;)
    }
})

server.listen(3000, () =&amp;gt; {
    console.log(&quot;서버 실행 중...&quot;)
})&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;파일 입출력&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fs (File system) 모듈을 사용해서 파일을 읽고 쓰는 작업을 수행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1782627407372&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fs = require(&quot;fs&quot;)&lt;/code&gt;&lt;/pre&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;size18&quot;&gt;&lt;b&gt;동기 방식 readFileSync&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1782627918318&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const data = fs.readFileSync(&quot;./example1.txt&quot;, &quot;utf8&quot;)
console.log(&quot;파일 내용: &quot;, data)
// (에러 처리를 갖고있지 않음 &amp;gt; 에러 처리를 위해선 try-catch문을 사용해야 함)&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;size18&quot;&gt;&lt;b&gt;비동기 방식 readFile&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1782627959081&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fs.readFile(&quot;example2.txt&quot;, &quot;utf8&quot;, (err, data) =&amp;gt; {
    if(err) {
        console.log(&quot;파일 읽기 실패: &quot;, err)
        return
    }
    console.log(&quot;파일 내용: &quot;, data)
})
// (기본적으로 에러 처리 가능)&lt;/code&gt;&lt;/pre&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;size18&quot;&gt;&lt;b&gt;동기 방식 writeFileSync&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1782628000584&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fs.writeFileSync(&quot;output1.txt&quot;, &quot;이 내용이 파일에 저장됩니다. 동기 방식&quot;)
console.log(&quot;파일 저장 완료 (동기)&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;비동기 방식 writeFile&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1782628042826&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fs.writeFile(&quot;output2.txt&quot;, &quot;비동기 방식으로 저장합니다.&quot;, (err) =&amp;gt; {
    if (err) {
        console.log(&quot;저장 실패: &quot;, err)
        return
    }
    console.log(&quot;파일 저장 완료 (비동기)&quot;)
})&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;파일에 내용 추가&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782628095024&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 비동기 방식으로 파일에 내용 추가
fs.appendFile(&quot;output2.txt&quot;, &quot;\n새로운 줄이 추가됩니다.&quot;, (err) =&amp;gt; {
    if (err) {
        console.log(&quot;내용 추가 완료&quot;)
    }
})&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;파일 삭제&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782628118504&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fs.unlink(&quot;output2.txt&quot;, (err) =&amp;gt; {
    if (err) throw err
    console.log(&quot;파일 삭제 완료&quot;)
})&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Express&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Node.js 환경에서 가장 널리 사용되는 웹 애플리케이션 프레임워크로, 서버를 쉽고 빠르게 구축할 수 있도록 다양한 기능을 제공한다.&lt;/li&gt;
&lt;li&gt;기본적인 HTTP 모듈보다 훨씬 간단하게 라우팅 처리, 요청/응답 객체 관리, 미들웨어 설정, 정적 파일 제공, 템플릿 엔진 연결 등을 할 수 있어 개발 생산성을 크게 높여준다.&lt;/li&gt;
&lt;/ul&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;클라이언트가 어떤 URL과 HTTP 메서드(GET, POST)로 요청을 보냈을 때 그 요청을 어떤 코드가 처리할지 연결해주는 규칙이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;미들웨어&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요청(request)과 응답(response) 사이에서 중간에 실행되는 함수로, 클라이언트의 요청을 직접 처리하기보다는 가공 / 검사 / 추가 작업을 담당하는 역할을 한다.&lt;/li&gt;
&lt;li&gt;요청 로그를 남기거나, JSON 데이터를 파싱하거나, 로그인 여부를 검사하거나, 에러를 처리하는 기능들이 모두 미들웨어의 기능이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;정적 파일&lt;/b&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;/li&gt;
&lt;li&gt;대표적으로 HTML, CSS, JavaScript, 이미지, 폰트 파일 등이 있으며, 사용자의 요청이 들어오면 서버는 내용을 가공하지 않고 저장된 그대로 응답한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;EJS&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;EJS (Embedded JavaScript)는 HTML 안에 JavaScript 코드를 삽입해 서버 데이터를 동적으로 렌더링 할 수 있게 해주는 node.js용 템플릿 엔진이다.&lt;/li&gt;
&lt;li&gt;&amp;lt;%= %&amp;gt;, &amp;lt;% %&amp;gt; 안에서 로직을 작성하여 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;템플릿 엔진&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTML과 데이터를 결합해 동적인 화면을 만들어주는 도구.&lt;/li&gt;
&lt;li&gt;서버에서 전달한 값을 HTML안에 삽입해서 최종 페이지를 생성하며, 반복문 / 조건문 같은 로직도 템플릿 내부에서 처리할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1782629701643&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const express = require(&quot;express&quot;)
const path = require(&quot;path&quot;)
const app = express()
const port = 3000

// 미들웨어 등록
// app.use(express.static('public')) // root에서 접근
app.use(&quot;/static&quot;, express.static('public')) // http://127.0.0.1:3000/static/spring.png
app.use(express.urlencoded({ extended: true }))

// EJS 설정
app.set(&quot;view engine&quot;, &quot;ejs&quot;)
app.set(&quot;views&quot;, path.join(__dirname, &quot;view&quot;))

app.get(&quot;/&quot;, (req, res) =&amp;gt; {
    res.send(&quot;Hello Express!&quot;)
})

app.get(&quot;/hello&quot;, (req, res) =&amp;gt; {
    res.render(&quot;hello&quot;, { name: &quot;김사과&quot; })
})

app.get(&quot;/submit&quot;, (req, res) =&amp;gt; {
    res.render(&quot;submit&quot;)
})

app.post(&quot;/submit&quot;, (req, res) =&amp;gt; {
    const { name, age } = req.body
    console.log(&quot;name: &quot;, name)
    console.log(&quot;age: &quot;, age)
    res.send(&quot;post로 호출!&quot;)
})

app.listen(port, () =&amp;gt; {
    console.log(&quot;서버 실행중...&quot;)
})&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Node.js</category>
      <category>GET</category>
      <category>HTTP</category>
      <category>node.js</category>
      <category>NPM</category>
      <category>post</category>
      <category>모듈</category>
      <category>백엔드</category>
      <category>파일 입출력</category>
      <category>프론트엔드</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/65</guid>
      <comments>https://gudwns5533.tistory.com/65#entry65comment</comments>
      <pubDate>Sun, 28 Jun 2026 15:12:18 +0900</pubDate>
    </item>
    <item>
      <title>웹 크롤링 (Crawling)</title>
      <link>https://gudwns5533.tistory.com/64</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;크롤링이란?&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;크롤링(Crawling)은 웹사이트의 HTML 문서나 API 데이터를 자동으로 수집하여 원하는 정보를 추출하는 기술이다.&lt;/li&gt;
&lt;li&gt;사람이 직접 웹페이지를 열어 뉴스, 상품 정보, 댓글, 주가, 날씨 등을 확인하는 과정을 프로그램이 대신 수행한다고 생각하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Requests&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Requests는 Python에서 웹 서버에 HTTP 요청을 보내고 응답을 받아오기 위해 사용하는 대표적인 라이브러리이다.&lt;/li&gt;
&lt;li&gt;웹 브라우저가 사이트에 접속할 때처럼 Python 코드에서도 웹페이지, 이미지, JSON 데이터 등을 서버로부터 가져올 수 있게 해주며, 웹 크롤링과 API 연동에서 가장 많이 사용되는 기본 도구 중 하나이다.&lt;/li&gt;
&lt;li&gt;requests.get()으로 웹페이지를 요청하거나, requests.post()로 데이터를 서버에 전송할 수 있으며, 응답 결과로 HTML 코드, 이미지 데이터, 상태 코드 등을 쉽게 다룰 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;BeautifulSoup&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;BeautifulSoup의 from bs4 impor BeautifulSoup 코드는 Python에서 HTML이나 XML 문서를 쉽게 분석하고 원하는 데이터를 추출하기 위해 BeautifulSoup 클래스를 불러오는 코드이다.&lt;/li&gt;
&lt;li&gt;여기서 bs4는 BeautifulSoup4 라이브러리를 의미하며, 웹 크롤링에서 가장 많이 사용하는 라이브러리 중 하나이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1782578830020&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import requests
from bs4 import BeautifulSoup

# 가상 서버 주소
url = &quot;http://127.0.0.1:5500/1.html&quot;

# 서버에 요청 보내기
response = requests.get(url)

# HTML 코드 가져오기
html = response.text

# HTML 분석
soup = BeautifulSoup(html, &quot;html.parser&quot;)

# 데이터 추출
h1 = soup.find(&quot;h1&quot;).text
p = soup.find(&quot;p&quot;).text

# 출력
print(&quot;제목:&quot;, h1)
print(&quot;내용:&quot;, p)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;데이터 추출 기능&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 105px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 13.7209%; height: 21px; text-align: center;&quot;&gt;메서드&lt;/td&gt;
&lt;td style=&quot;width: 15.9303%; height: 21px; text-align: center;&quot;&gt;찾는 개수&lt;/td&gt;
&lt;td style=&quot;width: 26.6279%; height: 21px; text-align: center;&quot;&gt;찾는 방식&lt;/td&gt;
&lt;td style=&quot;width: 20.9883%; height: 21px; text-align: center;&quot;&gt;반환 형식&lt;/td&gt;
&lt;td style=&quot;width: 22.7326%; text-align: center;&quot;&gt;데이터가 없을 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 13.7209%; height: 21px; text-align: center;&quot;&gt;find()&lt;/td&gt;
&lt;td style=&quot;width: 15.9303%; height: 21px; text-align: center;&quot;&gt;최초 1개&lt;/td&gt;
&lt;td style=&quot;width: 26.6279%; height: 21px; text-align: center;&quot;&gt;HTML 태그, 속성&lt;/td&gt;
&lt;td style=&quot;width: 20.9883%; height: 21px; text-align: center;&quot;&gt;Tag 객체 (.text 가능)&lt;/td&gt;
&lt;td style=&quot;width: 22.7326%; text-align: center;&quot;&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 13.7209%; height: 21px; text-align: center;&quot;&gt;find_all()&lt;/td&gt;
&lt;td style=&quot;width: 15.9303%; height: 21px; text-align: center;&quot;&gt;일치하는 모두&lt;/td&gt;
&lt;td style=&quot;width: 26.6279%; height: 21px; text-align: center;&quot;&gt;HTML 태그, 속성&lt;/td&gt;
&lt;td style=&quot;width: 20.9883%; height: 21px; text-align: center;&quot;&gt;리스트 형식&lt;/td&gt;
&lt;td style=&quot;width: 22.7326%; text-align: center;&quot;&gt;빈 리스트 ([])&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 13.7209%; height: 21px; text-align: center;&quot;&gt;select_one()&lt;/td&gt;
&lt;td style=&quot;width: 15.9303%; height: 21px; text-align: center;&quot;&gt;최초 1개&lt;/td&gt;
&lt;td style=&quot;width: 26.6279%; height: 21px; text-align: center;&quot;&gt;CSS 선택자 (CSS Selector)&lt;/td&gt;
&lt;td style=&quot;width: 20.9883%; height: 21px; text-align: center;&quot;&gt;Tag 객체 (.text 가능)&lt;/td&gt;
&lt;td style=&quot;width: 22.7326%; text-align: center;&quot;&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 13.7209%; height: 21px; text-align: center;&quot;&gt;select()&lt;/td&gt;
&lt;td style=&quot;width: 15.9303%; height: 21px; text-align: center;&quot;&gt;일치하는 모두&lt;/td&gt;
&lt;td style=&quot;width: 26.6279%; height: 21px; text-align: center;&quot;&gt;CSS 선택자 (CSS Selector)&lt;/td&gt;
&lt;td style=&quot;width: 20.9883%; height: 21px; text-align: center;&quot;&gt;리스트 형식&lt;/td&gt;
&lt;td style=&quot;width: 22.7326%; text-align: center;&quot;&gt;빈 리스트 ([])&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1782580571765&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import os
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin

# HTML 페이지 요청
url = &quot;http://127.0.0.1:5500/4.html&quot;

response = requests.get(url)

# HTML 파싱
soup = BeautifulSoup(response.text, &quot;html.parser&quot;)

# img 태그 찾기
img_tag = soup.find(&quot;img&quot;)

# 이미지 주소 가져오기
img_src = img_tag[&quot;src&quot;]

# 상대경로 &amp;rarr; 절대경로 변환
img_url = urljoin(url, img_src)

print(&quot;이미지 주소:&quot;, img_url)

# images 폴더 생성
os.makedirs(&quot;images&quot;, exist_ok=True)

# 이미지 다운로드
img_response = requests.get(img_url)

# 파일 이름 추출
file_name = img_src.split(&quot;/&quot;)[-1]

# 저장 경로
save_path = os.path.join(&quot;images&quot;, file_name)

# 이미지 저장
with open(save_path, &quot;wb&quot;) as file:
    file.write(img_response.content)

print(&quot;이미지 저장 완료&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;open()&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;open() 함수는 파일을 읽거나 저장할 때 사용하는 함수이다.&lt;/li&gt;
&lt;li&gt;텍스트 파일(txt), CSV, 이미지 파일 등을 열어서 데이터를 읽거나 저장할 수 있으며, 크롤링, 데이터분석, AI 개발 등 거의 모든 분야에서 사용되는 매우 중요한 기능이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1782580670623&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;파일객체 = open(&quot;파일명&quot;, &quot;모드&quot;, encoding=&quot;utf-8&quot;)&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;1172&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cO6GL1/dJMcacQ7sy7/2lIgz5M2w2roPXcpSXBaL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cO6GL1/dJMcacQ7sy7/2lIgz5M2w2roPXcpSXBaL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cO6GL1/dJMcacQ7sy7/2lIgz5M2w2roPXcpSXBaL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcO6GL1%2FdJMcacQ7sy7%2F2lIgz5M2w2roPXcpSXBaL0%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;1172&quot; height=&quot;480&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;headers&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782580788026&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;headers = {
        &quot;User-Agent&quot;: &quot;Mozilla/5.0&quot;,
        &quot;Referer&quot;: &quot;https://music.bugs.co.kr/&quot;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;headers는 웹 브라우저가 서버에 보내는 추가 정보이며, 크롤링에서는 서버가 요청을 정상적인 브라우저 접속으로 인식하도록 도와주는 역할을 한다.&lt;/li&gt;
&lt;li&gt;User-Agent는 &quot;어떤 브라우저와 운영체제로 접속했는가&quot;를 나타내는 정보로, 이를 설정하지 않으면 일부 사이트는 프로그램(봇) 접속으로 판단하여 데이터를 차단하거나 빈 페이지를 반환할 수 있다.&lt;/li&gt;
&lt;li&gt;Referer는 &quot;어떤 페이지에서 이동해 왔는가&quot;를 나타내는 정보이며, 벅스 같은 사이트는 내부 페이지 이동처럼 보여야 정상 응답을 주는 경우가 있어 함께 설정하는 경우가 많다.&lt;/li&gt;
&lt;li&gt;즉, headers는 크롤링 프로그램이 실제 사람이 사용하는 브라우저처럼 보이게 만들어 사이트 차단을 줄이고 안정적으로 데이터를 가져오기 위해 사용하는 설정이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;robots.txt&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;robots.txt는 웹사이트 운영자가 검색 엔진이나 크롤링 프로그램(봇)에게 &quot;어떤 페이지는 접근 가능하고, 어떤 페이지는 접근하지 말아야 하는지&quot;를 알려주는 규칙 파일이다.&lt;/li&gt;
&lt;li&gt;보통 사이트 주소 뒤에 /robots.txt를 붙여 확인할 수 있다.&lt;/li&gt;
&lt;li&gt;이 파일에는 특정 경로를 수집 금지(Disallow) 하거나 허용(Allow) 하는 규칙이 작성되어 있으며, 검색 엔진이나 크롤러는 이를 참고하여 사이트에 과도한 부하를 주지 않고 운영 정책을 존중하며 데이터를 수집한다.&lt;/li&gt;
&lt;li&gt;다만 robots.txt는 강제 차단 시스템이 아니라 &quot;권장 규칙&quot;에 가까우므로 기술적으로 접근은 가능할 수 있지만, 실무에서는 반드시 robots.txt와 사이트 이용약관을 확인하고 서버에 무리를 주지 않는 범위에서 크롤링하는 것이 중요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;셀레니움 (Selenium)&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;셀레니움이란?&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;셀레니움(Selenium)은 파이썬 코드로 실제 웹 브라우저(Chrome, Edge 등)를 자동으로 제어할 수 있게 해주는 웹 자동화 라이브러리이다.&lt;/li&gt;
&lt;li&gt;일반적인 requests 크롤링은 서버에서 받은 HTML만 분석하지만, Selenium은 브라우저를 직접 실행하여 버튼 클릭, 검색 입력, 스크롤, 로그인, 페이지 이동 등의 사용자 동작을 그대로 수행할 수 있기 때문에 JavaScript로 동적으로 생성되는 데이터까지 가져올 수 있다.&lt;/li&gt;
&lt;li&gt;따라서 멜론, 유튜브, 쇼핑몰처럼 JavaScript 렌더링이 많은 사이트의 크롤링이나 웹 자동화 테스트에서 매우 많이 사용된다.&lt;/li&gt;
&lt;li&gt;보통 webdriver.Chrome()으로 브라우저를 실행하고, find_element()로 요소를 찾으며, click(), send_keys() 등을 통해 자동화 작업을 수행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;xpath&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;XPath는 XML 또는 HTML 문서 내에서 특정 요소나 속성을 선택하기 위해 사용되는 경로 표현 언어이다.&lt;/li&gt;
&lt;li&gt;웹 크롤링이나 자동화 도구에서 주로 사용되며, 요소를 효율적으로 찾을 수 있도록 도와준다.&lt;/li&gt;
&lt;li&gt;일반적인 XPath는 특정 위치나 속성을 기준으로 요소를 선택하는 상대적인 경로를 사용한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;또한 full xpath는 루트 요소에서 시작하여 대상 요소까지의 절대적인 경로를 나타낸다. 따라서 문서 구조가 변경되면 경로가 깨질 가능성이 높다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;find_elements()&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Selenium에서 HTML 요소를 여러 개 찾을 때 사용하는 메서드이다.&lt;/li&gt;
&lt;li&gt;하나의 요소만 찾는 find_element()와 달리, 조건에 맞는 요소들을 리스트 형태로 모두 반환한다.&lt;/li&gt;
&lt;li&gt;여러 개의 이미지, 게시글, 테이블 행(tr) 등을 반복해서 크롤링할 때 자주 사용된다.&lt;/li&gt;
&lt;li&gt;찾는 방식은 By.ID, By.CLASS_NAME, By.CSS_SELECTOR, By.XPATH 등 다양한 선택자를 사용할 수 있으며, 반환 결과는 리스트이므로 for문으로 반복 처리하는 경우가 많다.&lt;/li&gt;
&lt;li&gt;find_element()는 요소를 찾지 못하면 에러가 발생하지만, find_elements()는 빈 리스트([])를 반환하기 때문에 반복 크롤링에서 더 안정적으로 사용된다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>데이터 분석</category>
      <category>beautifulsoup</category>
      <category>headers</category>
      <category>Requests</category>
      <category>robots.txt</category>
      <category>XPath</category>
      <category>셀레니움</category>
      <category>웹크롤링</category>
      <category>크롤링</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/64</guid>
      <comments>https://gudwns5533.tistory.com/64#entry64comment</comments>
      <pubDate>Sun, 28 Jun 2026 02:36:36 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript 기초 9 (Promise, async/await)</title>
      <link>https://gudwns5533.tistory.com/63</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Promise&lt;/b&gt;&lt;/h2&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;size18&quot;&gt;&lt;b&gt;콜백 지옥&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콜백 지옥은 자바스크립트에서 비동기 작업을 콜백 함수로 계속 중첩해 처리하다 보니 코드가 오른쪽으로 깊게 들여 쓰기 되며 복잡해지고, 흐름을 한눈에 이해하기 어려워지는 문제를 말한다.&lt;/p&gt;
&lt;pre id=&quot;code_1782570864135&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const promise = new Promise((resolve, reject) =&amp;gt; {
    if(성공){
        resolve(결과값)  // 작업 성공 시 호출
    }else{
        reject(에러)     // 작업 실패 시 호출
    }
})

promise.then(result =&amp;gt; {
    console.log(&quot;성공: &quot;, result)
})
.catch(error =&amp;gt; {
    console.log(&quot;실패: &quot;, error)
})&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;promise 메서드&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Promise.all()&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782572663248&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Promise.all([p1, p2, p3])
    .then(results =&amp;gt; {
    console.log(results)
})&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 Promise가 성공해야 then을 실행&lt;/li&gt;
&lt;li&gt;하나라도 실패하면 catch를 실행&lt;/li&gt;
&lt;li&gt;병렬 처리용&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Promise.race()&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782572746989&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Promise.race([p1, p2])
    .then(result =&amp;gt; console.log(result))&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 먼저 완료된 Promise 하나의 결과를 사용&lt;/li&gt;
&lt;li&gt;성공 / 실패 상관없이 먼저 끝난 것을 기준&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Promise.allSettled()&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782572792528&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Promise.allSettled([p1, p2])
    .then(results =&amp;gt; console.log(results))&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 Promise의 성공 / 실패 결과를 모두 반환&lt;/li&gt;
&lt;li&gt;실패해도 중단되지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Promise.any()&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782572828337&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Promise.any([p1, p2])
    .then(result =&amp;gt; console.log(result))
    .catch(err =&amp;gt; console.error(err))&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나라도 성공하면 then 실행&lt;/li&gt;
&lt;li&gt;모두 실패하면 catch 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1782572932723&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function getApple() {
    return new Promise((resolve) =&amp;gt; {
        setTimeout(() =&amp;gt; {
            resolve(' ')
        }, 3000)
    })
}

function getBanana() {
    return new Promise((resolve) =&amp;gt; {
        setTimeout(() =&amp;gt; {
            resolve(' ')
        }, 1000)
    })
}

function getOrange() {
    return Promise.reject(new Error(&quot;오렌지 없음&quot;))
}

// 순차 처리 (Promise 체이닝)
getBanana().then((banana) =&amp;gt; getApple().then((apple) =&amp;gt; [banana, apple]))
    .then(console.log)

// 병렬 처리 (하나라도 실패하면 전체 실패)
Promise.all([getBanana(), getApple()])
    .then((fruits) =&amp;gt; console.log(&quot;all&quot;, fruits))

// Promise.all([getBanana(), getApple(), getOrange()])
// .then((fruits) =&amp;gt; console.log(&quot;all&quot;, fruits))

// 가장 빨리 수행된 것이 실행
Promise.race([getBanana(), getApple()])
    .then((fruit) =&amp;gt; console.log('race', fruit))

// 여러 프로미스를 병렬로 처리하되 하나의 프로미스가 실패해도 무조건 실행
Promise.allSettled([getBanana(), getApple(), getOrange()])
    .then((fruits) =&amp;gt; console.log('allsettled', fruits))
    .catch(console.log)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;async / await&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Promise를 기반으로 한 자바스크립트의 비동기 처리 문법으로, 비동기 코드를 동기 코드처럼 읽기 쉽고 직관적으로 작성할 수 있게 한다.&lt;/li&gt;
&lt;li&gt;async가 붙은 함수는 항상 Promise를 반환하며, await는 Promise가 처리될 때까지 실행을 일시적으로 기다린 뒤 결과 값을 반환받아 다음 코드로 넘어가게 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1782576829339&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function orderCoffee(menu) {
    console.log(`${menu} 주문을 접수했습니다. 잠시만 기다려주세요`)
    const availableMenu = ['아메리카노', '디카페인 아메리카노', '카페라떼', '바닐라라떼']
    return new Promise((resolve, reject) =&amp;gt; {
        setTimeout(() =&amp;gt; {
            if (availableMenu.includes(menu)) {
                resolve(`${menu}가 준비되었습니다. 맛있게 드세요 :)`)
            } else {
                reject(`죄송합니다. ${menu}는 판매하지 않습니다 :(`)
            }
        }, 2000)
    })
}

async function startOrder() {
    try {
        const result = await orderCoffee(&quot;녹차&quot;)
        console.log(result)
    } catch (error) {
        console.log(error)
    } finally {
        console.log(&quot;주문이 완료되었습니다&quot;)
    }
}

startOrder()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>HTML,CSS,JavaScript</category>
      <category>async</category>
      <category>await</category>
      <category>JavaScript</category>
      <category>Promise</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/63</guid>
      <comments>https://gudwns5533.tistory.com/63#entry63comment</comments>
      <pubDate>Sun, 28 Jun 2026 01:10:13 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript 기초 8 (localStorage, sessionStorage, Cookie)</title>
      <link>https://gudwns5533.tistory.com/62</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;localStorage&lt;/b&gt;&lt;/h2&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;브라우저별로 약 5MB 내외 저장 제한이 있음&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;데이터 저장하는 방법&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782566199058&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;localStorage.setItem(&quot;key&quot;, &quot;value&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;데이터 불러오는 방법&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782566204422&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const value = localStorage.getItem(&quot;key&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;데이터 삭제하는 방법&amp;nbsp; /&amp;nbsp; 모든 데이터 삭제하는 방법&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782566210070&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;localStorage.removeItem(&quot;key&quot;)
localStorage.clear()&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;localStorage를 이용한 테마 변경&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782566286187&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;button onclick=&quot;toggleTheme()&quot;&amp;gt;테마 변경&amp;lt;/button&amp;gt;
&amp;lt;p&amp;gt;현재 테마: &amp;lt;span id=&quot;themeText&quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;script&amp;gt;
    const body = document.body

    function applyTheme(theme) {
        body.style.backgroundColor = theme === &quot;dark&quot; ? &quot;#222&quot; : &quot;#fff&quot;
        body.style.color = theme === &quot;dark&quot; ? &quot;#fff&quot; : &quot;#000&quot;
        document.getElementById(&quot;themeText&quot;).innerHTML = theme
    }

    function toggleTheme() {
        const current = localStorage.getItem(&quot;theme&quot;) || &quot;white&quot;
        const next = current === &quot;white&quot; ? &quot;dark&quot; : &quot;white&quot;
        localStorage.setItem(&quot;theme&quot;, next)
        applyTheme(next)
    }

    applyTheme(localStorage.getItem(&quot;theme&quot;) || &quot;white&quot;)
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;sessionStorage&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저가 제공하는 웹 스토리지 중 하나로, 탭(세션) 단위로 데이터를 임시 저장하는 저장소&lt;/li&gt;
&lt;li&gt;페이지를 새로고침해도 데이터는 유지되지만, 브라우저 탭이나 창을 닫는 순간 자동으로 삭제되는 것이 특징이며, 같은 사이트라도 다른 탭과 데이터가 공유되지 않음&lt;/li&gt;
&lt;li&gt;저장 용량은 약 5MB&lt;/li&gt;
&lt;li&gt;문자열만 저장 가능&lt;/li&gt;
&lt;li&gt;보안이 필요한 민감한 정보는 저장하지 않는 것이 원칙&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;데이터 저장하는 방법&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782566483665&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sessionStorage.setItem(&quot;key&quot;, &quot;value&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;데이터 불러오는 방법&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782566515819&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const value = sessionStorage.getItem(&quot;key&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;데이터 삭제하는 방법 / 모든 데이터 삭제하는 방법&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782566549374&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sessionStorage.removeItem(&quot;key&quot;)
sessionStorage.clear()&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Cookie&lt;/b&gt;&lt;/h2&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;저장 용량이 약 4KB로 매우 작고, 모든 요청에 포함되어 전송되기 때문에 보안과 성능 측면에서 주의가 필요함.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;쿠키 저장하기&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782568942465&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;document.cookie = &quot;userid=apple&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;만료 시간 설정하기&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782568961130&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const date = new Date() // 2026-06-23 00:00:00
date.setDate(date.getDate() + 7) // 2026-06-23 + 7, 7일 후 =&amp;gt; 2026-06-30
document.cookie = &quot;userid=apple; expires=&quot; + date.toUTCString()&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;쿠키 읽기&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782569015379&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(document.cookie)&lt;/code&gt;&lt;/pre&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;expires 값을 과거 날짜로 설정하면 자동으로 삭제됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>HTML,CSS,JavaScript</category>
      <category>Cookie</category>
      <category>JavaScript</category>
      <category>localStorage</category>
      <category>sessionStorage</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/62</guid>
      <comments>https://gudwns5533.tistory.com/62#entry62comment</comments>
      <pubDate>Sat, 27 Jun 2026 23:18:17 +0900</pubDate>
    </item>
    <item>
      <title>다익스트라 알고리즘 (Dijkstra Algorithm)</title>
      <link>https://gudwns5533.tistory.com/61</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;최단 경로 알고리즘이란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최단 경로 알고리즘은 그래프 상에서 특정 정점에서 다른 정점까지 가장 적은 비용(거리, 시간 등)으로 도달할 수 있는 경로를 찾는 알고리즘을 말한다. 일반적으로 네비게이션과 같은 길찾기에 적용된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;다익스트라 알고리즘의 동작 원리&lt;/b&gt;&lt;/h2&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;초기화 : 출발 정점의 거리는 0, 나머지 모든 정점의 거리는 무한대로 설정한다.&lt;/li&gt;
&lt;li&gt;정점 선택 : 아직 방문하지 않은 정점 중 최단 거리가 가장 짧은 정점을 선택한다.&lt;/li&gt;
&lt;li&gt;거리 갱신 : 선택한 정점을 거쳐서 인접한 다른 정점으로 이동하는 거리가, 기존에 기록된 거리보다 짧다면 그 값을 갱신한다.&lt;/li&gt;
&lt;li&gt;반복 : 모든 정점을 방문할 때까지 2~3번의 과정을 반복한다.&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;653&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VKosP/dJMcabkpYqH/mTbK0j9KrBJTf4gZK4L2U1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VKosP/dJMcabkpYqH/mTbK0j9KrBJTf4gZK4L2U1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VKosP/dJMcabkpYqH/mTbK0j9KrBJTf4gZK4L2U1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVKosP%2FdJMcabkpYqH%2FmTbK0j9KrBJTf4gZK4L2U1%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;653&quot; height=&quot;620&quot; data-origin-width=&quot;653&quot; data-origin-height=&quot;620&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 노드 간의 거리는 무한대의 값으로 초기화하고 출발 노드는 1번 노드에서 시작한다. 1번 노드는 본인과의 거리가 0이기 때문에 거리를 0으로 채워놓는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;605&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/budzQ0/dJMcah50sv6/ck6Sx79gTeiXE4WMUnqe41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/budzQ0/dJMcah50sv6/ck6Sx79gTeiXE4WMUnqe41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/budzQ0/dJMcah50sv6/ck6Sx79gTeiXE4WMUnqe41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbudzQ0%2FdJMcah50sv6%2Fck6Sx79gTeiXE4WMUnqe41%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;603&quot; height=&quot;605&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;605&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번 노드와 직접 연결된 노드는 2,3,4번 노드이고, 1번 노드에서 각각의 노드까지 걸리는 거리를 갱신해준다. 그 후 최단 거리인 3번 노드로 이동해서 다음 단계를 진행한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;559&quot; data-origin-height=&quot;595&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7mi36/dJMcadPW7I8/kTtdcYgTn6C2FArI0XtKm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7mi36/dJMcadPW7I8/kTtdcYgTn6C2FArI0XtKm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7mi36/dJMcadPW7I8/kTtdcYgTn6C2FArI0XtKm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7mi36%2FdJMcadPW7I8%2FkTtdcYgTn6C2FArI0XtKm0%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;559&quot; height=&quot;595&quot; data-origin-width=&quot;559&quot; data-origin-height=&quot;595&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3번 노드와 직접 연결된 노드는 4번 노드와 6번 노드이다. 또 각각의 노드까지의 거리를 최단거리로 갱신해주고, 거리 테이블 상의 다음 작은 값을 가진 2번 노드를 방문한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;591&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k3U3w/dJMcaiRjRis/ALduyKiZVZ64Wjr1uVoBKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k3U3w/dJMcaiRjRis/ALduyKiZVZ64Wjr1uVoBKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k3U3w/dJMcaiRjRis/ALduyKiZVZ64Wjr1uVoBKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk3U3w%2FdJMcaiRjRis%2FALduyKiZVZ64Wjr1uVoBKk%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;558&quot; height=&quot;591&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;591&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;같은 방법으로 2번 노드와 연결되어있는 4번과 6번 노드의 거리를 최단거리로 갱신해주고, 거리 테이블 상의 다음 작은 값인 4번 노드를 방문한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2oyKW/dJMcajbxAWC/JZjbWnMkyN976LA0OsXT20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2oyKW/dJMcajbxAWC/JZjbWnMkyN976LA0OsXT20/img.png&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;586&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.986%; margin-right: 10px;&quot; data-widthpercent=&quot;49.56&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2oyKW/dJMcajbxAWC/JZjbWnMkyN976LA0OsXT20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2oyKW%2FdJMcajbxAWC%2FJZjbWnMkyN976LA0OsXT20%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;560&quot; height=&quot;586&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpngeO/dJMcacXScpU/6oKOXcq3K1aT2NToJbWo8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpngeO/dJMcacXScpU/6oKOXcq3K1aT2NToJbWo8K/img.png&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;582&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.8512%;&quot; data-widthpercent=&quot;50.44&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpngeO/dJMcacXScpU/6oKOXcq3K1aT2NToJbWo8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpngeO%2FdJMcacXScpU%2F6oKOXcq3K1aT2NToJbWo8K%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;566&quot; height=&quot;582&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 방법으로 노드를 이동해가면서 최단거리를 갱신해주고, 모든 노드를 방문할 때 까지 반복한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;가중치(Graph Weight)란 무엇인가?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가중치는 그래프의 정점과 정점을 연결하는 간선에 부여된 고유한 값을 의미한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;BFS와 다익스트라 알고리즘의 차이점&lt;/b&gt;&lt;/h2&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;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.2636%; text-align: center;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 40.0774%; text-align: center;&quot;&gt;BFS (너비 우선 탐색)&lt;/td&gt;
&lt;td style=&quot;width: 45.6589%; text-align: center;&quot;&gt;다익스트라 (Dijkstra)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.2636%; text-align: center;&quot;&gt;가중치 조건&lt;/td&gt;
&lt;td style=&quot;width: 40.0774%; text-align: center;&quot;&gt;가중치가 없거나 모두 동일할 때 (1로 동일)&lt;/td&gt;
&lt;td style=&quot;width: 45.6589%; text-align: center;&quot;&gt;가중치가 양수일 때 (제각각 다름)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.2636%; text-align: center;&quot;&gt;탐색 기준&lt;/td&gt;
&lt;td style=&quot;width: 40.0774%; text-align: center;&quot;&gt;시작점으로부터의 간선개수가 적은 순&lt;/td&gt;
&lt;td style=&quot;width: 45.6589%; text-align: center;&quot;&gt;시작점으로부터의 누적 가중치 합이 작은 순&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.2636%; text-align: center;&quot;&gt;자료구조&lt;/td&gt;
&lt;td style=&quot;width: 40.0774%; text-align: center;&quot;&gt;일반적인 큐 (Queue)&lt;/td&gt;
&lt;td style=&quot;width: 45.6589%; text-align: center;&quot;&gt;우선순위 큐(Priority Queue)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;우선순위 큐(Priority Queue)와 힙(Heap)이 왜 사용되는가?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다익스트라 알고리즘의 핵심은 &quot;아직 방문하지 않은 노드들 중 출발지로부터의 거리가 가장 짧은 노드를 선택하는 것&quot;이다. 이 작업을 효율적으로 처리하기 위해 두 자료구조가 사용된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;우선순위 큐(Prioirty Queue)&lt;/b&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;/li&gt;
&lt;li&gt;추상적인 개념(인터페이스)이며, 이를 실제로 컴퓨터 메모리 상에서 가장 빠르게 구현한 물리적 자료구조가 힙(Heap)이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;힙(Heap)을 사용하는 이유&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반 배열을 사용할 때: 노드가 V개 있을 때 최솟값을 찾으려면 배열을 처음부터 끝까지 다 뒤져야 하므로 O(V)의 시간이 걸린다. 이를 모든 노드마다 반복하게되면 비효율적이다.&lt;/li&gt;
&lt;li&gt;최소 힙(Min-Heap)을 사용할 때 : 부모 노드가 항상 자식 노드보다 작은 완전 이진 트리 구조를 유지한다. 덕분에 최솟값을 찾는 연산은 항상 루트 노드만 보면 되므로 O(1)이며, 데이터를 삽입하고 최솟값을 삭제하며 트리를 재정렬하는 데 O(logV)밖에 걸리지 않는다&lt;/li&gt;
&lt;li&gt;결론적으로 탐색 속도를 크게 끌어올려 전체 알고리즘의 병목 현상을 해결해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;다익스트라 알고리즘의 시간복잡도&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다익스트라 알고리즘의 시간복잡도는 그래프의 정점(Vertex) 개수 V와 간선(Edge) 개수 E,그리고 어떤 자료구조를 쓰느냐에 따라 결정된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;순차 탐색(배열) 방식을 사용할 때 : O(V^2)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;방문하지 않은 노드 중 최단 거리 노드를 찾기 위해 V번 돌며 배열을 탐색한다 &amp;rarr; O(V)&lt;/li&gt;
&lt;li&gt;이 작업을 모든 노드(V개)에 대해 수행하므로 기본적인 노드 탐색에만 O(V^2)이 걸린다.&lt;/li&gt;
&lt;li&gt;간선을 확인하는 연산은 총 O(E)이므로 전체 복잡도는 O(V^2+E)가 되며, 대게 V^2이 더 크므로 O(V^2)이 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;우선순위 큐(힙) 방식을 사용할 때 : O(ElogV)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존의 O(V) 걸리던 최단 노드 탐색이 힙 때문에 O(logV)로 줄어든다.&lt;/li&gt;
&lt;li&gt;그래프의 모든 간선(E개)을 검사하면서, 더 짧은 경로를 발견할 때마다 힙에 거리 정보를 추가하거나 갱신하는 연산이 일어난다.&lt;/li&gt;
&lt;li&gt;힙에 최대 E번 데이터가 들어갔다 나올 수 있으므로, 힙 연산 성능인 logV와 결합하여 최종 시간 복잡도는 O(ElogV)가 된다. 정점 대비 간선이 아주 빽빽한 완전 그래프가 아니라면 대부분 O(ElogV)가 O(V^2)보다 훨씬 빠르다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;코드 구현&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;heapq를 사용하지 않은 다익스트라 구현 (미구현)&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782210969273&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 무한대를 의미하는 값 설정
INF = int(1e9)

# 동일한 그래프 데이터 정의
graph = {
    1: [(2, 4), (3, 2)],
    2: [(3, 3), (4, 2), (5, 3)],
    3: [(2, 1), (4, 4), (5, 5)],
    4: [(5, 1)],
    5: []
}

def dijkstra_no_heap(start, n):
    # 최단 거리 테이블 초기화
    distance = [INF] * (n + 1)
    distance[start] = 0
    
    # 각 노드의 방문 여부를 체크할 배열 생성
    visited = [False] * (n + 1)
    
    # 경로 추적을 위한 직전 노드 기록 배열
    parent = [None] * (n + 1)
    
    # 핵심 변경 포인트: 방문하지 않은 노드 중 최단 거리 노드를 '수동'으로 찾는 함수
    def get_smallest_node():
        min_value = INF
        index = 0  # 가장 최단 거리가 짧은 노드의 번호
        for i in range(1, n + 1):
            if distance[i] &amp;lt; min_value and not visited[i]:
                min_value = distance[i]
                index = i
        return index

    # 노드의 개수만큼 반복
    for _ in range(n):
        # 현재 가장 거리가 짧은 노드를 선형 탐색으로 찾아옴 -&amp;gt; O(V) 발생
        now = get_smallest_node()
        if now == 0: # 더 이상 갈 수 있는 노드가 없다면 종료
            break
            
        visited[now] = True # 방문 처리
        
        # 현재 노드와 연결된 다른 노드 확인
        for next_node, weight in graph.get(now, []):
            cost = distance[now] + weight
            if cost &amp;lt; distance[next_node]:
                distance[next_node] = cost
                parent[next_node] = now # 경로 기록

    return distance, parent

# 경로 역추적 함수
def find_actual_path(parent, target):
    path = []
    current = target
    while current is not None:
        path.append(current)
        current = parent[current]
    return path[::-1]

# 실행 및 결과 출력
start_node = 1
num_of_nodes = 5
distances, parent_records = dijkstra_no_heap(start_node, num_of_nodes)

print(f&quot;=== [heapq 미사용] 최단 거리 및 실제 이동 경로 ===&quot;)
for i in range(1, num_of_nodes + 1):
    if distances[i] == INF:
        print(f&quot;정점 {i}: 도달 불가능&quot;)
    else:
        path = find_actual_path(parent_records, i)
        print(f&quot;정점 {i} | 최단 거리: {distances[i]:&amp;lt;2} | 실제 경로: {' -&amp;gt; '.join(map(str, path))}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결괏값&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782211996648&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;=== [heapq 미사용] 최단 거리 및 실제 이동 경로 ===
정점 1 | 최단 거리: 0  | 실제 경로: 1
정점 2 | 최단 거리: 3  | 실제 경로: 1 -&amp;gt; 3 -&amp;gt; 2
정점 3 | 최단 거리: 2  | 실제 경로: 1 -&amp;gt; 3
정점 4 | 최단 거리: 5  | 실제 경로: 1 -&amp;gt; 3 -&amp;gt; 2 -&amp;gt; 4
정점 5 | 최단 거리: 6  | 실제 경로: 1 -&amp;gt; 3 -&amp;gt; 2 -&amp;gt; 5&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;heapq를 사용한 다익스트라 구현 (미구현)&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782210518761&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import heapq

# 무한대를 의미하는 값으로 10억 설정
INF = int(1e9)

# 그래프 데이터 정의 (인접 리스트 방식)
graph = {
    1: [(2, 4), (3, 2)],
    2: [(3, 3), (4, 2), (5, 3)],
    3: [(2, 1), (4, 4), (5, 5)],
    4: [(5, 1)],
    5: []
}

def dijkstra_heap(start, n):
    # 최단 거리 테이블을 모두 무한대로 초기화
    distance = [INF] * (n + 1)
    distance[start] = 0
    
    # 실제 경로 추적을 위한 직전 노드 기록 배열 (None으로 초기화)
    parent = [None] * (n + 1)
    
    # heapq를 위한 우선순위 큐 생성 및 시작 노드 삽입 (거리, 노드)
    queue = []
    heapq.heappush(queue, (0, start))
    
    while queue:
        # 가장 최단 거리가 짧은 노드 정보 꺼내기
        dist, now = heapq.heappop(queue)
        
        # 현재 꺼낸 거리가 이미 최단 거리로 처리된 적이 있다면 패스
        if distance[now] &amp;lt; dist:
            continue
            
        # 현재 노드와 연결된 다른 인접 노드들을 확인
        for next_node, weight in graph.get(now, []):
            cost = dist + weight
            # 더 짧은 경로를 발견한 경우
            if cost &amp;lt; distance[next_node]:
                distance[next_node] = cost
                parent[next_node] = now  # 어떤 노드(now)를 거쳐왔는지 기록
                heapq.heappush(queue, (cost, next_node))
                
    return distance, parent

# parent 배열을 바탕으로 시작점부터 목적지까지 역추적하여 경로를 만드는 함수
def find_actual_path(parent, target):
    path = []
    current = target
    while current is not None:
        path.append(current)
        current = parent[current]
    return path[::-1]  # 역순으로 모인 경로를 올바른 순서로 뒤집음

# --- 실행 및 결과 출력 ---
start_node = 1
num_of_nodes = 5
distances, parent_records = dijkstra_heap(start_node, num_of_nodes)

# 결과 출력
print(f&quot;=== 시작 정점 {start_node}번 기준 최단 거리 및 실제 이동 경로 ===&quot;)
for i in range(1, num_of_nodes + 1):
    if distances[i] == INF:
        print(f&quot;정점 {i}: 도달할 수 없는 노드입니다.&quot;)
    else:
        path = find_actual_path(parent_records, i)
        path_str = &quot; -&amp;gt; &quot;.join(map(str, path))
        print(f&quot;정점 {i} | 최단 거리: {distances[i]:&amp;lt;2} | 실제 경로: {path_str}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결괏값&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782212035386&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;=== 시작 정점 1번 기준 최단 거리 및 실제 이동 경로 ===
정점 1 | 최단 거리: 0  | 실제 경로: 1
정점 2 | 최단 거리: 3  | 실제 경로: 1 -&amp;gt; 3 -&amp;gt; 2
정점 3 | 최단 거리: 2  | 실제 경로: 1 -&amp;gt; 3
정점 4 | 최단 거리: 5  | 실제 경로: 1 -&amp;gt; 3 -&amp;gt; 2 -&amp;gt; 4
정점 5 | 최단 거리: 6  | 실제 경로: 1 -&amp;gt; 3 -&amp;gt; 2 -&amp;gt; 5&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;heapq를 사용하지 않은 방식과 성능 비교 (미구현)&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1782212049353&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import heapq
import random
import time
import sys

# 파이썬의 기본 재귀 제한 및 출력 제한 해제 (대규모 데이터 처리용)
sys.setrecursionlimit(10**6)
INF = int(1e9)

# ==========================================
# 1. 무작위 그래프 생성 함수
# ==========================================
def generate_random_graph(num_nodes, num_edges):
    &quot;&quot;&quot;
    num_nodes: 정점 개수
    num_edges: 간선 개수
    &quot;&quot;&quot;
    graph = {i: [] for i in range(1, num_nodes + 1)}
    
    # 중복 간선을 방지하면서 랜덤하게 간선 생성
    edge_set = set()
    while len(edge_set) &amp;lt; num_edges:
        u = random.randint(1, num_nodes)
        v = random.randint(1, num_nodes)
        if u != v and (u, v) not in edge_set:
            weight = random.randint(1, 10) # 가중치는 1~10 사이 랜덤
            graph[u].append((v, weight))
            edge_set.add((u, v))
            
    return graph

# ==========================================
# 2. Heapq를 사용한 다익스트라 (O(E log V))
# ==========================================
def dijkstra_with_heap(graph, start, n):
    distance = [INF] * (n + 1)
    distance[start] = 0
    queue = []
    heapq.heappush(queue, (0, start))
    
    while queue:
        dist, now = heapq.heappop(queue)
        if distance[now] &amp;lt; dist:
            continue
        for next_node, weight in graph.get(now, []):
            cost = dist + weight
            if cost &amp;lt; distance[next_node]:
                distance[next_node] = cost
                heapq.heappush(queue, (cost, next_node))
    return distance

# ==========================================
# 3. Heapq를 사용하지 않은 다익스트라 (O(V^2))
# ==========================================
def dijkstra_no_heap(graph, start, n):
    distance = [INF] * (n + 1)
    distance[start] = 0
    visited = [False] * (n + 1)
    
    def get_smallest_node():
        min_value = INF
        index = 0
        for i in range(1, n + 1):
            if distance[i] &amp;lt; min_value and not visited[i]:
                min_value = distance[i]
                index = i
        return index

    for _ in range(n):
        now = get_smallest_node()
        if now == 0:
            break
        visited[now] = True
        for next_node, weight in graph.get(now, []):
            cost = distance[now] + weight
            if cost &amp;lt; distance[next_node]:
                distance[next_node] = cost
    return distance

# ==========================================
# 4. 100개, 3000개, 50000개 실험 진행
# ==========================================
# 실험 케이스: (정점 수, 간선 수)
test_cases = [
    (100, 400),       # 1) 정점 100개, 간선 400개
    (3000, 12000),    # 2) 정점 3000개, 간선 12000개
    (50000, 200000)    # 3) 정점 50000개, 간선 200000개
]

for V, E in test_cases:
    print(f&quot;\n==========================================&quot;)
    print(f&quot; 실험 케이스: 정점(V) {V}개 / 간선(E) {E}개&quot;)
    print(f&quot;==========================================&quot;)
    
    # 그래프 데이터 생성
    graph = generate_random_graph(V, E)
    start_node = 1
    
    # ----------------------------------------
    # [A] Heapq 사용 방식 측정
    # ----------------------------------------
    start_time = time.time()
    res_heap = dijkstra_with_heap(graph, start_node, V)
    heap_time = time.time() - start_time
    print(f&quot;▶ heapq 사용 알고리즘 소요 시간  : {heap_time:.5f}초&quot;)
    
    # ----------------------------------------
    # [B] Heapq 미사용 방식 측정
    # ----------------------------------------        
    start_time = time.time()
    res_no_heap = dijkstra_no_heap(graph, start_node, V)
    no_heap_time = time.time() - start_time
    print(f&quot;▶ heapq 미사용 알고리즘 소요 시간: {no_heap_time:.5f}초&quot;)
    
    # 두 알고리즘의 결과가 완전히 일치하는지 검증
    if res_heap == res_no_heap:
        print(&quot; 결과 검증: 두 방식의 최단 거리 결과가 완벽히 일치합니다.&quot;)
    else:
        print(&quot; 결과 검증: 오류 발생 (결과가 다릅니다.)&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결괏값&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782212154407&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;==========================================
 실험 케이스: 정점(V) 100개 / 간선(E) 400개
==========================================
▶ heapq 사용 알고리즘 소요 시간  : 0.00000초
▶ heapq 미사용 알고리즘 소요 시간: 0.00000초
 결과 검증: 두 방식의 최단 거리 결과가 완벽히 일치합니다.

==========================================
 실험 케이스: 정점(V) 3000개 / 간선(E) 12000개
==========================================
▶ heapq 사용 알고리즘 소요 시간  : 0.00399초
▶ heapq 미사용 알고리즘 소요 시간: 0.24955초
 결과 검증: 두 방식의 최단 거리 결과가 완벽히 일치합니다.

==========================================
 실험 케이스: 정점(V) 50000개 / 간선(E) 200000개
==========================================
▶ heapq 사용 알고리즘 소요 시간  : 0.10066초
▶ heapq 미사용 알고리즘 소요 시간: 69.22850초
 결과 검증: 두 방식의 최단 거리 결과가 완벽히 일치합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/61</guid>
      <comments>https://gudwns5533.tistory.com/61#entry61comment</comments>
      <pubDate>Wed, 24 Jun 2026 00:05:27 +0900</pubDate>
    </item>
    <item>
      <title>그리디 알고리즘 (Greedy Algorithm)</title>
      <link>https://gudwns5533.tistory.com/60</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;그리디 알고리즘이란?&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최적의 값을 구해야 하는 상황에서 사용되는 근시안적인 방법론으로 '각 단계에서 최적이라고 생각되는 것을 선택'해 나가는 방식으로 진행하여 최종적인 해답에 도달하는 알고리즘이다.&lt;/li&gt;
&lt;li&gt;이때, 항성 최적의 값을 보장하는 것이 아니라 최적의 값의 '근사한 값'을 목표로 한다.&lt;/li&gt;
&lt;li&gt;주로 문제를 분할 가능한 문제들로 분할한 뒤, 각 문제들에 대한 최적해를 구한 뒤 이를 결합하여 전체 문제의 최적해를 구하는 경우에 주로 사용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;그리디 알고리즘의 동작 방식&lt;/b&gt;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;선택 절차(Selection Procedure)&lt;/b&gt; : 현재 상태에서의 최적의 해답을 선택한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;적절성 검사(Feasibility Check)&lt;/b&gt; : 선택된 해가 문제의 조건을 만족하는지 검사한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;해답 검사(Solution Check)&lt;/b&gt; : 원래의 문제가 해결되었는지 검사하고, 해결되지 않았다면 선택 절차로 돌아가 위의 과정을 반복한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;그리디 알고리즘의 특징&lt;/b&gt;&lt;/h2&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;/li&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;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;그리디 알고리즘의 장점&lt;/b&gt;&lt;/h2&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;효율성&lt;/b&gt; : 많은 문제에서 그리디 알고리즘은 적절한 해를 빠르게 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;그리디 알고리즘의 단점&lt;/b&gt;&lt;/h2&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;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;거스름돈 문제 코드 구현&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;동작 원리&lt;/b&gt;&lt;/h4&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;/li&gt;
&lt;li&gt;남은 금액을 다시 같은 방법으로 계산한다.&lt;/li&gt;
&lt;li&gt;남은 금액이 0이 될 때까지 반복한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1782135109566&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def calculate_change(amount, coins):
    coins.sort(reverse=True) # 값이 큰 동전부터 사용하기 위해 내림차순으로 정렬

    order = {}
    total_count = 0

    for coin in coins:
        count = amount // coin # 동전으로 거슬러 줄 수 있는 개수 계산
        amount %= coin # 남은 거스름돈
        order[coin] = count # 동전별 사용 개수
        total_count += count # 전체 동전 사용 개수

    return order, total_count


coins = [500, 100, 50, 10]
amount = 760

change, total_count = calculate_change(amount, coins)

print(f&quot;거스름돈: {amount}원&quot;)
print(f&quot;동전 개수: {total_count}개&quot;)

for coin, count in change.items():
    print(f&quot;{coin}원: {count}개&quot;)
    
'''
거스름돈: 760원
동전 개수: 5개
500원: 1개
100원: 2개
50원: 1개
10원: 1개
'''&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;회의실 배정 문제 코드 구현&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;동작 원리&lt;/b&gt;&lt;/h4&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;/li&gt;
&lt;li&gt;선택한 회의의 종료 시간보다 시작 시간이 같거나 늦은 회의를 다음 순서로 선택한다.&lt;/li&gt;
&lt;li&gt;같은 방법으로 더 이상 선택할 수 있는 회의가 없을 때까지 반복한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1782141524433&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def assign_meetings(meetings):
    meetings.sort(key=lambda meeting: (meeting[1], meeting[0]))
    
    selected_meetings = []
    end_time = 0  # 직전에 선택된 회의가 끝난 시간
    
    for start, end, name in meetings:
        
        # 현재 회의의 시작 시간이 이전 회의의 종료 시간과 같거나 늦다면 선택 가능
        if start &amp;gt;= end_time:
            selected_meetings.append((start, end, name))
            end_time = end  # 종료 시간 업데이트
            
    return selected_meetings



meetings = [
    (1, 4, &quot;자료구조 스터디&quot;),
    (3, 5, &quot;알고리즘 풀이&quot;),
    (0, 6, &quot;프로젝트 회의&quot;),
    (5, 7, &quot;Python 복습&quot;),
    (3, 8, &quot;DB 설계&quot;),
    (5, 9, &quot;코드 리뷰&quot;),
    (6, 10, &quot;면접 준비&quot;),
    (8, 11, &quot;Git 특강&quot;),
    (8, 12, &quot;웹 기초&quot;),
    (12, 14, &quot;최종 정리&quot;)
]

assigned = assign_meetings(meetings)

print(f&quot;배정 가능한 회의 수: {len(assigned)}개&quot;)
for start, end, name in assigned:
    print(f&quot;{start}시 ~ {end}시: {name}&quot;)
    
'''
배정 가능한 회의 수: 4개
1시 ~ 4시: 자료구조 스터디
5시 ~ 7시: Python 복습
8시 ~ 11시: Git 특강
12시 ~ 14시: 최종 정리
'''&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;배낭 문제 코드 구현&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;동작 원리&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;물건마다 (가치 / 무게) 를 계산해서 1kg당 가치가 가장 높은 순서대로 내림차순으로 정렬한다.&lt;/li&gt;
&lt;li&gt;가성비가 높은 물건부터 차례대로 배낭에 넣는다.&lt;/li&gt;
&lt;li&gt;배낭의 남은 용량이 물건의 무게보다 크다면 물건을 배낭에 넣는다&lt;/li&gt;
&lt;li&gt;물건 전체를 넣을 수 없다면 남은 무게만큼 물건의 일부만 배낭에 넣는다.&lt;/li&gt;
&lt;li&gt;배낭이 가득 찰 때까지 반복한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1782142417314&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def fractional_knapsack(capacity, items):
    items.sort(key=lambda item: item[&quot;value&quot;] / item[&quot;weight&quot;], reverse=True)
    
    total_value = 0.0
    selected_items = []
    
    for item in items:
        if capacity &amp;lt;= 0:
            break
        
        name = item[&quot;name&quot;]
        weight = item[&quot;weight&quot;]
        value = item[&quot;value&quot;]
        
        if capacity &amp;gt;= weight: # 물건 전체를 배낭에 넣을 수 있는 경우
            capacity -= weight
            total_value += value
            selected_items.append((name, weight, value, 100))
            
        else:  # 물건의 일부만 넣어야 하는 경우
            ratio = capacity / weight
            taken_weight = capacity
            taken_value = value * ratio
            total_value += taken_value
            selected_items.append((name, taken_weight, taken_value, ratio*100))
            capacity = 0  # 배낭이 가득 찼으므로 용량을 0으로 설정
            
    return selected_items, total_value



items = [
    {&quot;name&quot;: &quot;노트북&quot;, &quot;weight&quot;: 3, &quot;value&quot;: 600},
    {&quot;name&quot;: &quot;카메라&quot;, &quot;weight&quot;: 2, &quot;value&quot;: 500},
    {&quot;name&quot;: &quot;책&quot;, &quot;weight&quot;: 4, &quot;value&quot;: 300},
    {&quot;name&quot;: &quot;이어폰&quot;, &quot;weight&quot;: 1, &quot;value&quot;: 150}
]
capacity = 5

selected_items, total_value = fractional_knapsack(capacity, items)

print(f&quot;배낭 최대 무게: {capacity}kg&quot;)
print(f&quot;총 가치: {total_value}&quot;)

for name, weight, value, percent in selected_items:
    print(f&quot;{name}: {weight}kg, 가치 {value:.1f}, 사용 비율 {percent:.1f}%&quot;)
    
    
'''
배낭 최대 무게: 5kg
총 가치: 1100.0
카메라: 2kg, 가치 500.0, 사용 비율 100.0%
노트북: 3kg, 가치 600.0, 사용 비율 100.0%
'''&lt;/code&gt;&lt;/pre&gt;</description>
      <category>자료구조, 알고리즘</category>
      <category>그리디 알고리즘</category>
      <category>알고리즘</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/60</guid>
      <comments>https://gudwns5533.tistory.com/60#entry60comment</comments>
      <pubDate>Tue, 23 Jun 2026 01:19:08 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript 기초 7 (정규식)</title>
      <link>https://gudwns5533.tistory.com/59</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;정규식&lt;/b&gt;&lt;/h2&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;정규 표현식은 문자열에서 특정한 패턴을 찾거나, 올바른 형식인지 검사하거나, 원하는 형태로 치환하기 위해 사용하는 정형화된 표현식이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;정규 표현식의 생성 방식&lt;/b&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;/li&gt;
&lt;li&gt;생성자 방식 : new RegExp(&quot;패턴&quot;,&amp;nbsp; &quot;옵션&quot;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;패턴의 종류&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1782110919007&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;a: 문자 a
abc: 문자열abc
.: 아무 문자 1개
[abc]: a 또는 b 또는 c
[a-z]: a 부터 z
[가-힣]: 한글
[0-9]: 숫자
[^0-9]: 숫자가 아닌 문자
*: 0번 이상
+: 1번 이상
?: 0 또는 1
{n}: 정확히 n번
{n,}: n번 이상
{n.m}: n~m번
^: 문자열 시작
$: 문자열 끝
\d: 숫자
\D: 숫자 아님
\w: 문자 또는 숫자 또는 _
\W: 문자 또는 숫자 또는 _ 외 문자
\s: 공백
\S: 공백 아닌 문자
g: 전체 검색
i: 대소문자 무시
m: 여러 줄 처리


(?=.*): 어디엔가 원하는 패턴이 하나라도 있어야 함
(?=.*[A-Za-z]): 영문자가 최소 1개 이상 있어야 함
(?=.*\d): 숫자가 최소 1개 이상 있어야 함
(?=.*[!@#$%^&amp;amp;*()]): 제시된 특수 문자중 최소 1개 이상 있어야 함&lt;/code&gt;&lt;/pre&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;size18&quot;&gt;&lt;b&gt;reg.test(string)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패턴에 문자열이 존재하면 true, 없으면 false를 반환한다.&lt;/p&gt;
&lt;pre id=&quot;code_1782111102840&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const reg = /^[0-9]+$/
reg.test(&quot;a123&quot;) // false
reg.test(&quot;1234&quot;) // true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;string.match(reg)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열에서 정규식과 일치하는 부분을 찾아 배열로 반환한다.&lt;/p&gt;
&lt;pre id=&quot;code_1782111162588&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;abc123&quot;.match(/\d+/) // [&quot;123&quot;]

const result = &quot;abc123xyz456&quot;.match(/\d+/g); 
console.log(result); // [&quot;123&quot;, &quot;456&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;string.replace(reg, replacement)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패턴과 일치하는 부분을 원하는 문자열로 교체한다.&lt;/p&gt;
&lt;pre id=&quot;code_1782111257176&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;010-1234-5678&quot;.replace(/-/g, &quot;&quot;) //01012345678&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;string.search(reg)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패턴이 발견되는 인덱스 번호를 반환한다.&lt;/p&gt;
&lt;pre id=&quot;code_1782111843486&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;hello&quot;.search(/e/) // 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>HTML,CSS,JavaScript</category>
      <category>JavaScript</category>
      <category>정규식</category>
      <category>정규식 메서드</category>
      <category>패턴</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/59</guid>
      <comments>https://gudwns5533.tistory.com/59#entry59comment</comments>
      <pubDate>Mon, 22 Jun 2026 16:25:14 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript 기초 6 (BOM, DOM, 화살표함수, 콜백함수, 이벤트)</title>
      <link>https://gudwns5533.tistory.com/58</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;BOM (Browser Object Model)&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;BOM이란?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트가 브라우저 자체를 제어하고 정보를 얻기 위해 사용하는 객체 모델을 말한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;API (Application Programming Interface)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 프로그램이나 시스템이 데이터를 주고받고 기능을 사용할 수 있도록 연결해주는 중간 통로&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;BOM을 구성하는 4대 객체&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. window 객체&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;HTML 페이지를 로드하면 브라우저는 자동으로 window 객체를 생성하며, 자바스크립트 코드에서 명시적으로 window를 쓰지 않아도 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. location 객체&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;자바스크립트에서 현재 웹 페이지의 URL(주소) 정보를 다루고 제어할 수 있게 해주는 window 객체의 하위 객체&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. history 객체&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저의 방문 기록을 제어할 수 있는 객체로, 사용자가 이전에 방문했던 페이지로 이동하거나 앞으로 다시 이동할 수 있는 기능을 제공.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4. navigator 객체&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저에 대한 정보를 제공하는 객체로, 사용자의 브라우저 종류, 버전, 운영체제, 언어 설정, 온라인 여부 등 다양한 환경 정보를 확인할 수 있도록 해준다.&lt;/li&gt;
&lt;li&gt;이 정보의 존재 유무로 사용자인지 봇인지 구분 가능하다.&lt;/li&gt;
&lt;li&gt;geolocation : 사용자의 위치 정보를 가져오기 위한 API로, GPS, Wifi, IP 주소 등을 이용해 현재 위치를 알아낸다. 사용자의 명시적인 동의가 있어야 작동한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;DOM (Document Object Model)&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DOM이란?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML 문서를 트리 구조의 객체로 표현한 것으로, 자바스크립트를 통해 웹 페이지의 요소를 선택하고 내용이나 스타일을 변경하며, 요소를 추가 / 삭제하는 등 동적인 화면 조작을 가능하게 해주는 인터페이스를 말한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DOM의 작동 원리&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;브라우저가 HTML 파일을 읽음&lt;/li&gt;
&lt;li&gt;HTML 태그를 하나하나 분석하여 트리 구조의 객체를 생성&lt;/li&gt;
&lt;li&gt;각 태그는 노드라는 객체가 됨&lt;/li&gt;
&lt;li&gt;자바스크립트는 DOM 객체를 통해 화면을 제어함&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Document 객체&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 페이지 전체를 표현하는 최상위 DOM 객체로, HTML 문서에 접근하고 조작할 수 있는 핵심 객체&lt;/li&gt;
&lt;li&gt;브라우저가 HTML 파일을 로드하면 자동으로 document 객체가 생성되며, 이 객체를 통해 웹 페이지의 요소들을 선택하거나 생성, 수정, 삭제할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;화살표함수와 콜백함수&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;자바스크립트에서 this가 바뀌는 문제&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;this가 선언 시점이 아니라 &quot;호출되는 방식&quot;에 따라 결정됨&lt;/li&gt;
&lt;li&gt;같은 함수라도 어떻게 호출하느냐에 따라 this가 전혀 다른 대상을 가리킬 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781983867714&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 브라우저 환경: this === window, 일반 함수 호출은 전역 객체를 가리킴
function show() {
    console.log(this)
}
show()

// this === obj, 점 앞의 객체가 this가 됨
const obj1 = {
    name: &quot;김사과&quot;,
    show() {
        console.log(this.name)
    }
}
obj1.show()&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;화살표 함수&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;function 키워드 대신 =&amp;gt; 기호를 사용하여 함수를 더 간결하게 작성하는 방법&lt;/li&gt;
&lt;li&gt;ES6부터 도입된 문법으로 코드를 짧게 만들고, this 바인딩 문제를 해결함&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781983947964&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 콜백함수를 호출하면 this가 바뀌는 문제가 발생
// setTimeout안에 함수는 일반 함수 호출 -&amp;gt; this === window
const obj2 = {
    name: &quot;김사과&quot;,
    show() {
        setTimeout(function () {
            console.log(this.name)
        }, 1000)
    }
}
obj2.show()

// 화살표 함수
const obj3 = {
    name: &quot;김사과&quot;,
    show() {
        setTimeout(() =&amp;gt; {
            console.log(this.name)
        }, 1000)
    }
}
obj3.show()&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;동기 vs 비동기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;동기 (Synchronous)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동기는 작업이 순서대로 하나씩 실행되는 방식&lt;/li&gt;
&lt;li&gt;앞에 있는 작업이 끝나야 다음 작업을 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;비동기 (Asynchronous)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;느린 작업을 기다리지 않고, 나중에 따로 실행될 작업은 뒤로 미뤄놓고 다음 코드부터 먼저 실행하는 방식&lt;/li&gt;
&lt;li&gt;기본적으로 한 줄씩 처리하는 단일 스레드 언어. 모든 작업을 동기로 처리하면, 느린 작업이 있으면 전체 프로그램이 멈추게 됨.&lt;/li&gt;
&lt;li&gt;비동기가 필요한 작업
&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;li&gt;타이머, 사용자 이벤트 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;콜백 함수&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 함수의 인자로 전달되어, 그 함수 내부에서 나중에 호출되는 함수&lt;/li&gt;
&lt;li&gt;어떤 작업이 끝난 후 실행되어야 할 코드를 함수로 전달해주는 방식&lt;/li&gt;
&lt;li&gt;비동기 처리나 배열 메서드에서 자주 사용됨&lt;/li&gt;
&lt;li&gt;일반 함수나 화살표 함수 모두 콜백 함수로 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781984508566&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 콜백 함수
function greet(name, callback) {
    console.log(&quot;안녕, &quot; + name + &quot;!&quot;)
    callback()
}

function sayBye() {
    console.log(&quot;빠이 ~&quot;)
}

greet(&quot;김사과&quot;, sayBye)

const numbers = [1, 2, 3, 4]
const doubled = numbers.map(n =&amp;gt; n * 2)
console.log(doubled)

function doSomething(callback) {
    console.log(&quot;작업 시작!&quot;)
    callback()
}

doSomething(() =&amp;gt; {
    console.log(&quot;작업 완료!&quot;)
})

const calc_add = (a, b) =&amp;gt; a + b
const calc_multiply = (a, b) =&amp;gt; a * b

function calculater(num1, num2, action) {
    if (num1 &amp;lt; 0 || num2 &amp;lt; 0) return
    const result = action(num1, num2)
    return result
}
console.log(calculater(10, 3, calc_add))
console.log(calculater(10, 3, calc_multiply))&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;이벤트 (Event)&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 웹 페이지와 상호작용할 때 발생하는 특정 동작이나 상황을 의미한다.&lt;/li&gt;
&lt;li&gt;버튼 클릭, 키보드 입력, 마우스 이동, 폼 제출, 창 닫기 등&lt;/li&gt;
&lt;li&gt;특정 반응(동작)을 하도록 코드를 작성한다.
&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;addEventListener()&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;마우스&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 51.6279%; height: 246px;&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: 40.991%; text-align: center;&quot;&gt;함수&lt;/td&gt;
&lt;td style=&quot;width: 59.009%; text-align: center;&quot;&gt;동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 40.991%; text-align: center;&quot;&gt;click&lt;/td&gt;
&lt;td style=&quot;width: 59.009%; text-align: center;&quot;&gt;클릭&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 40.991%; text-align: center;&quot;&gt;dblclick&lt;/td&gt;
&lt;td style=&quot;width: 59.009%; text-align: center;&quot;&gt;더블 클릭&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 40.991%; text-align: center;&quot;&gt;mousedown&lt;/td&gt;
&lt;td style=&quot;width: 59.009%; text-align: center;&quot;&gt;마우스 버튼을 눌렀을 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 40.991%; text-align: center;&quot;&gt;mouseup&lt;/td&gt;
&lt;td style=&quot;width: 59.009%; text-align: center;&quot;&gt;마우스 버튼을 뗐을 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 40.991%; text-align: center;&quot;&gt;mouseenter&lt;/td&gt;
&lt;td style=&quot;width: 59.009%; text-align: center;&quot;&gt;마우스를 요소 위에 올렸을 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 40.991%; text-align: center;&quot;&gt;mouseleave&lt;/td&gt;
&lt;td style=&quot;width: 59.009%; text-align: center;&quot;&gt;요소에서 마우스가 벗어났을 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 40.991%; text-align: center;&quot;&gt;mousemove&lt;/td&gt;
&lt;td style=&quot;width: 59.009%; text-align: center;&quot;&gt;마우스를 움직일 때&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;키보드&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 51.6279%; height: 47px;&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: 40.7658%; text-align: center;&quot;&gt;함수&lt;/td&gt;
&lt;td style=&quot;width: 59.2342%; text-align: center;&quot;&gt;동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 40.7658%; text-align: center;&quot;&gt;keydown&lt;/td&gt;
&lt;td style=&quot;width: 59.2342%; text-align: center;&quot;&gt;키를 누르는 순간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 40.7658%; text-align: center;&quot;&gt;keyup&lt;/td&gt;
&lt;td style=&quot;width: 59.2342%; text-align: center;&quot;&gt;키를 눌렀다 뗐을 때&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;폼&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 51.3953%; height: 106px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 21.267%; height: 21px; text-align: center;&quot;&gt;함수&lt;/td&gt;
&lt;td style=&quot;width: 30.1284%; height: 21px; text-align: center;&quot;&gt;동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 21.267%; height: 17px; text-align: center;&quot;&gt;submit&lt;/td&gt;
&lt;td style=&quot;width: 30.1284%; height: 17px; text-align: center;&quot;&gt;폼이 제출될 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 21.267%; height: 17px; text-align: center;&quot;&gt;input&lt;/td&gt;
&lt;td style=&quot;width: 30.1284%; height: 17px; text-align: center;&quot;&gt;입력값이 변경될 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 21.267%; height: 17px; text-align: center;&quot;&gt;change&lt;/td&gt;
&lt;td style=&quot;width: 30.1284%; height: 17px; text-align: center;&quot;&gt;입력이 끝나고 값이 변경될 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 21.267%; height: 17px; text-align: center;&quot;&gt;focus&lt;/td&gt;
&lt;td style=&quot;width: 30.1284%; height: 17px; text-align: center;&quot;&gt;입력창에 포커스가 생길 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 21.267%; height: 17px; text-align: center;&quot;&gt;blur&lt;/td&gt;
&lt;td style=&quot;width: 30.1284%; height: 17px; text-align: center;&quot;&gt;입력창에서 포커스가 사라질 때&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;문서&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 51.7442%;&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: 21.3483%; text-align: center;&quot;&gt;함수&lt;/td&gt;
&lt;td style=&quot;width: 30.3959%; text-align: center;&quot;&gt;동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.3483%; text-align: center;&quot;&gt;load&lt;/td&gt;
&lt;td style=&quot;width: 30.3959%; text-align: center;&quot;&gt;페이지가 모두 로드되었을 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.3483%; text-align: center;&quot;&gt;resize&lt;/td&gt;
&lt;td style=&quot;width: 30.3959%; text-align: center;&quot;&gt;브라우저 크기를 변경할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.3483%; text-align: center;&quot;&gt;scroll&lt;/td&gt;
&lt;td style=&quot;width: 30.3959%; text-align: center;&quot;&gt;페이지를 스크롤할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;터치 (모바일)&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 52.3256%;&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: 21.3334%; text-align: center;&quot;&gt;함수&lt;/td&gt;
&lt;td style=&quot;width: 30.9922%; text-align: center;&quot;&gt;동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.3334%; text-align: center;&quot;&gt;touchstart&lt;/td&gt;
&lt;td style=&quot;width: 30.9922%; text-align: center;&quot;&gt;화면을 터치했을 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.3334%; text-align: center;&quot;&gt;touchmove&lt;/td&gt;
&lt;td style=&quot;width: 30.9922%; text-align: center;&quot;&gt;터치한 채로 움직일 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.3334%; text-align: center;&quot;&gt;touchend&lt;/td&gt;
&lt;td style=&quot;width: 30.9922%; text-align: center;&quot;&gt;터치를 뗐을 때&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;b&gt;드래그&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 52.5582%; height: 96px;&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: 41.0013%; text-align: center;&quot;&gt;함수&lt;/td&gt;
&lt;td style=&quot;width: 60.8005%; text-align: center;&quot;&gt;동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 41.0013%; text-align: center;&quot;&gt;dragstart&lt;/td&gt;
&lt;td style=&quot;width: 60.8005%; text-align: center;&quot;&gt;요소를 누를 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 41.0013%; text-align: center;&quot;&gt;drag&lt;/td&gt;
&lt;td style=&quot;width: 60.8005%; text-align: center;&quot;&gt;요소를 움직일 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 41.0013%; text-align: center;&quot;&gt;dragend&lt;/td&gt;
&lt;td style=&quot;width: 60.8005%; text-align: center;&quot;&gt;요소에서 뗐을 때&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1781985511952&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const eventBtn = document.getElementById(&quot;eventBtn&quot;)
eventBtn.addEventListener(&quot;click&quot;, function () {
    alert(&quot;addEventListener로 연결된 이벤트&quot;)
})

eventBtn.addEventListener(&quot;mouseenter&quot;, function () {
    eventBtn.style.backgroundColor = 'deeppink'
})

eventBtn.addEventListener(&quot;mouseleave&quot;, function () {
    eventBtn.style.backgroundColor = ''
})

document.getElementById(&quot;clickArea&quot;).addEventListener(&quot;click&quot;, function (event) {
    alert(`클릭한 위치: X=${event.clientX}, Y=${event.clientY}\n` +
        `이벤트 타입: ${event.type}\n` +
        `이벤트 대상: ${event.target.id}`
    )
})

document.getElementById('inputBox').addEventListener(&quot;keydpown&quot;, function (event) {
    console.log(&quot;입력된 키: &quot;, event.key)
    console.log(&quot;코드: &quot;, event.code)
    console.log(&quot;이벤트 발생한 요소: &quot;, event.target)
})

document.getElementById(&quot;inputBox&quot;).addEventListener(&quot;keypress&quot;, function(event) {
    if(event.key === 'Enter'){
        event.preventDefault()
        alert(&quot;엔터키는 막았어요&quot;)
    }
})&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;이벤트 객체&lt;/b&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;/li&gt;
&lt;li&gt;어떤 요소에서 이벤트가 발생했는지(target), 어떤 종류의 이벤트인지(type), 마우스 좌표나 키 입력 정보 등 다양한 데이터를 담고 있다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>HTML,CSS,JavaScript</category>
      <category>bom</category>
      <category>DOM</category>
      <category>JavaScript</category>
      <category>이벤트</category>
      <category>콜백함수</category>
      <category>화살표함수</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/58</guid>
      <comments>https://gudwns5533.tistory.com/58#entry58comment</comments>
      <pubDate>Sun, 21 Jun 2026 05:26:02 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript 기초 5 (class, 프로토타입, Object 객체)</title>
      <link>https://gudwns5533.tistory.com/57</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;JavaScript에서 class&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1781973827382&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class 클래스명 {
    constructor(매개변수1, 매개변수2, ...) {
        속성 정의
    }
    메서드명(매개변수1, 매개변수2, ...){
        객체가 할 수 있는 동작 정의
    }
}&lt;/code&gt;&lt;/pre&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;클래스는 객체를 만들기 위한 '템플릿'이다. constructor를 통해 초기 속성을 정의하고, 객체가 할 수 있는 동작을 메서드로 정의한다.&lt;/p&gt;
&lt;pre id=&quot;code_1781974262135&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Animal {
    constructor(type) {
        this.type = type; // 속성 정의
    }
    bark() {
        console.log(`${this.type} 멍멍!`); // 메서드 정의
    }
    speak() {
        console.log(&quot;동물이 소리를 냅니다&quot;);
    }
}

const dog = new Animal(&quot;강아지&quot;);
console.log(dog.type); // 강아지
dog.bark();            // 강아지 멍멍!&lt;/code&gt;&lt;/pre&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;상속을 사용하면 기존 클래스(부모)의 속성과 메서드를 물려받아 새로운 클래스(자식)를 확장할 수 있다. 부모 생성자를 호출할 때는 super()를 사용하며, 부모의 메서드를 자식 클래스에서 재정의하는 것을 메서드 오버라이딩이라 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1781975085739&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Dog extends Animal {
    constructor(type, color) {
        super(type); // 부모(Animal)의 constructor 호출
        this.color = color;
    }
    // 부모의 speak 메서드를 재정의 (오버라이딩)
    speak() {
        console.log(&quot;멍멍!&quot;);
    }
    showInfo() {
        console.log(`${this.type}는 ${this.color} 입니다`);
    }
}

const rucy = new Dog(&quot;강아지&quot;, &quot;흰색&quot;);
rucy.speak();    // 멍멍! (오버라이딩된 메서드 호출)
rucy.bark();     // 강아지 멍멍! (부모에게 물려받은 메서드 호출)
rucy.showInfo(); // 강아지는 흰색 입니다&lt;/code&gt;&lt;/pre&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;인스턴스(객체)를 생성하지 않고, 클래스 이름으로 직접 호출하는 메서드와 속성이다. 전역적인 유틸리티 기능을 만들거나 전체 인스턴스를 관리할 때 유용하다.&lt;/p&gt;
&lt;pre id=&quot;code_1781975383497&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Mathtool {
    static add(a, b) {
        return a + b;
    }
}
console.log(Mathtool.add(3, 4)); // 7 (new 없이 바로 호출 가능)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;get / set / private 필드&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;get과 set은 같은 이름을 공유해야 한다.&lt;/li&gt;
&lt;li&gt;내부에서 값을 저장할 땐 보통 _name처럼 언더스코어를 붙여서 구분한다.&lt;/li&gt;
&lt;li&gt;get만 정의하면 읽기 전용 속성이 된다.&lt;/li&gt;
&lt;li&gt;private은 클래스 내부에서만 접근 가능한 비공개 속성이다.&lt;/li&gt;
&lt;li&gt;#을 붙이면 반드시 동일한 클래스 안에서만 접근 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781976127994&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class User {
    #password = &quot;&quot;; // 클래스 내부에서만 접근 가능한 비공개 속성

    constructor(name, password) {
        this._name = name;
        this.#password = password;
    }

    // getter: 값을 읽을 때 호출
    get name() {
        return this._name;
    }

    // setter: 값을 변경할 때 호출 (유효성 검사 기능 추가)
    set name(newName) {
        if (newName.length &amp;lt; 2) {
            console.log(&quot;이름은 두 글자 이상이어야 합니다.&quot;);
        } else {
            this._name = newName;
        }
    }

    checkPassword(input) {
        return this.#password === input; // 비공개 필드는 내부 메서드로만 확인 가능
    }
}

const user = new User(&quot;김사과&quot;, &quot;1111&quot;);
user.name = &quot;반&quot;; // 메시지 출력: 이름은 두 글자 이상이어야 합니다.
// console.log(user.#password); // 외부 접근 불가능&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;프로토타입&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;프로토타입이란?&lt;/b&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;/li&gt;
&lt;li&gt;모든 객체는 내부에 prototype이라는 숨겨진 연결을 가지며, 자신에게 없는 속성이나 메서드를 요청받으면 이 연결을 따라 프로토타입 객체에서 찾아오게 된다.&lt;/li&gt;
&lt;li&gt;여러 객체가 동일한 메서드를 각각 따로 가지지 않고, 하나의 원본을 공유할 수 있어 메모리를 절약하고 구조적인 코드 작성이 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781976478092&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Person(name) {
    this.name = name;
}

// 공용 객체 공간(Prototype)에 메서드 등록
Person.prototype.study = function() {
    console.log(`${this.name} 열심히 공부합니다.`);
};

const p1 = new Person(&quot;김사과&quot;);
const p2 = new Person(&quot;반하나&quot;);

p1.study(); // 김사과 열심히 공부합니다.
p2.study(); // 반하나 열심히 공부합니다.
/*
    - person 생성자로 만든 객체 p1, p2는 study()라는 메서드를 직접 가지고 있지 않았음
    - person.prototype이라는 공용 객체에 저장된 메서드를 참조해서 사용
        p1 -&amp;gt; Person.prototype -&amp;gt; Object.prototype -&amp;gt; null (프로토타입 체인)
*/&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Object 객체&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Object 객체란?&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바스크립트의 내장 객체인 Object는 모든 객체의 최상위 부모(프로토타입의 기반)이자, 객체를 생성하고 조작하기 위한 기본 도구이다.&lt;/li&gt;
&lt;li&gt;Object는 객체를 만들 수 있는 생성자이면서, 객체의 속성을 다루기 위한 다양한 메서드를 제공한다.&lt;/li&gt;
&lt;li&gt;모든 객체는 Object에서 출발하며, 공통 기능은 Object.prototype에 정의한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Object의 주요 메서드&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Object.keys(obj)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 모든 키(속성 이름)를 배열로 반환한다.&lt;/p&gt;
&lt;pre id=&quot;code_1781977127593&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const user = { name: &quot;김사과&quot;, age: 20 }
console.log(Object.keys(user)) // ['name', 'age']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Object.values(obj)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 모든 값(속성 값)을 배열로 반환한다.&lt;/p&gt;
&lt;pre id=&quot;code_1781977161462&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(Object.values(user)) // ['김사과', 20]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Object.entries(obj)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 키와 값 쌍을 [key, value] 형태의 배열로 반환한다.&lt;/p&gt;
&lt;pre id=&quot;code_1781977201523&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(Object.entries(user)) // [['name', '김사과'], ['age', 20]]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Object.assign(obj)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 복사하거나 병합할 때 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Object.hasOwn(obj, key)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체가 특정 속성을 직접 가지고 있는지 확인&lt;/p&gt;
&lt;pre id=&quot;code_1781977409971&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(Object.hasOwn(user, &quot;name&quot;)) // true
console.log(Object.hasOwn(user, &quot;job&quot;)) // false&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;size18&quot;&gt;&lt;b&gt;Object.freeze(obj)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 동결시켜 속성을 변경 / 추가 / 삭제할 수 없게 만듦&lt;/p&gt;
&lt;pre id=&quot;code_1781977580966&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const config = { debug: true }
Object.freeze(config)
config.debug = false
console.log(config.debug)  // true (변경 안됨)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;</description>
      <category>HTML,CSS,JavaScript</category>
      <category>class</category>
      <category>JavaScript</category>
      <category>object 객체</category>
      <category>프로토타입</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/57</guid>
      <comments>https://gudwns5533.tistory.com/57#entry57comment</comments>
      <pubDate>Sun, 21 Jun 2026 02:56:50 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript 기초 4 (객체, 얕은 복사, 깊은 복사, 스프레드 문법)</title>
      <link>https://gudwns5533.tistory.com/56</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;프로그래밍 방법론&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 절차 지향 프로그래밍&lt;/b&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;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781966865783&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const names = ['김사과', '반하나', '오렌지', '이메론']
for(let i=0; i&amp;lt;names.length; i++) {
    if(names[i].startsWith(&quot;김&quot;)) {
        console.log(&quot;안녕하세요,&quot; + names[i] + &quot;님!&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 객체 지향 프로그래밍&lt;/b&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;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781967007707&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const greeter = {
    names: ['김사과', '반하나', '오렌지', '이메론'],
    greetKim: function() {
        for(let i=0; i&amp;lt;names.length; i++) {
            if(names[i].startsWith(&quot;김&quot;)) {
                console.log(&quot;안녕하세요,&quot; + names[i] + &quot;님!&quot;)
             }
        }    
    }
}
greeter.greetKim()&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 함수형 프로그래밍&lt;/b&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;/li&gt;
&lt;li&gt;데이터를 변경하지 않고, 순수 함수를 조합하여 문제를 해결&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781967049032&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const names = ['김사과', '반하나', '오렌지', '이메론']
names
    .filter(name =&amp;gt; name.startsWith(&quot;감&quot;))
    .forEach(name =&amp;gt; console.log(&quot;안녕하세요, &quot; + name + &quot;님!&quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;객체&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 값에 여러 데이터를 담을 수 있는 자료형&lt;/li&gt;
&lt;li&gt;속성(프로퍼티), 동작(메서드)을 함께 표현할 수 있는 구조&lt;/li&gt;
&lt;li&gt;키-값 쌍의 형태로 데이터를 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1) 객체 리터럴&lt;/b&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;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781968911225&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const person1 = {
    name: &quot;김사과&quot;,
    age: 20,
    greet: function () {
        console.log(&quot;안녕하세요, 저는 &quot; + this.name + &quot;입니다.&quot;)
    }
}
person1.greet()  // 안녕하세요, 저는 김사과입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2) new Object() 생성자 사용&lt;/b&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;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781968928873&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const person2 = new Object()
person2.name = &quot;김사과&quot;
person2.age = 20
person2.greet = function () {
    console.log(&quot;안녕하세요, 저는 &quot; + this.name + &quot;입니다.&quot;)
}
person2.greet()  // 안녕하세요, 저는 김사과입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3) 생성자 함수 사용&lt;/b&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;/li&gt;
&lt;li&gt;new 키워드와 함께 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781968967206&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Person(name, age) {
    this.name = name
    this.age = age
    this.greet = function () {
        console.log(&quot;안녕하세요, 저는 &quot; + this.name + &quot;입니다.&quot;)
    }
}

const person3 = new Person(&quot;김사과&quot;, 20)
person3.greet()  // 안녕하세요, 저는 김사과입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4) 클래스(class, ES6)&lt;/b&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;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781968978012&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Person2 {
    constructor(name, age) {
        this.name = name
        this.age = age
    }

    greet() {
        console.log(&quot;안녕하세요, 저는 &quot; + this.name + &quot;입니다.&quot;)
    }
}
const person4 = new Person2(&quot;김사과&quot;, 20)
person4.greet()  // 안녕하세요, 저는 김사과입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;얕은 복사 vs 깊은 복사&lt;/b&gt;&lt;/h2&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;객체의 1단계 속성만 복사하고, 속성 값이 또 다른 객체나 배열이면 참조만 복사한다.&lt;/p&gt;
&lt;pre id=&quot;code_1781969748200&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const obj1 = { name: &quot;김사과&quot;, age: 20 }
const obj2 = Object.assign({}, obj1)
console.log(obj2)
obj2.name = &quot;오렌지&quot;
console.log(obj1)

const obj3 = { ...obj1 }
obj3.age = 30
console.log(obj3)
console.log(obj1)

// 얕은 복사
// address는 객체이기 때문에, obj4와 obj5가 같은 주소를 공유
const obj4 = { name: &quot;김사과&quot;, address: { city: &quot;서울&quot; } }
const obj5 = { ...obj4 }
obj5.address.city = &quot;부산&quot;
console.log(obj5)
console.log(obj4)&lt;/code&gt;&lt;/pre&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;객체 내부의 모든 중첩된 값들까지 복사하는 방법. 즉, 완전히 새로운 객체를 만드는 것&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;스프레드 문법&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;점 3개를 사용해서 배열이나 객체의 값을 펼쳐서 복사하거나 전달할 수 있는 문법&lt;/li&gt;
&lt;li&gt;배열이나 객체 안의 요소들을 하나하나 꺼내서 다른 배열, 객체, 함수 등으로 옮겨줄 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781970179286&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fruits = ['사과', '바나나']
const newFruits = [ ...fruits, &quot;포도&quot; ]
console.log(newFruits) // ['사과', '바나나', '포도']

const arr1 = [1, 2, 3]
const arr2 = [ ...arr1 ] // 새 배열 생성

const 1 = [1, 2]
const 2 = [3, 4]
const result = [ ...a, ...b ] // [1, 2, 3, 4]&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1781971335316&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function add(x, y, z) {
    return x + y + z
}

const nums = [1, 2, 3]
console.log(add(...nums))  // 6

const a = { name: &quot;김사과&quot; }
const b = { age: 20 }
const merged = { ...a, ...b }
console.log(merged)  // {name: '김사과', age: 20}

const numbers = [1, 2, 3, 4, 5]
const [first, second, ...rest] = numbers
console.log(first)   // 1
console.log(second)  // 2
console.log(rest)    // [3, 4, 5]

const user = {
    name: &quot;김사과&quot;,
    age: 20,
    city: &quot;서울&quot;
}

const { name, ...rest2 } = user
console.log(name)   // 김사과
console.log(rest2)  // {age: 20, city: &quot;서울&quot;}

function display({name, age, address, job}) {
    console.log(name, age, address, job)
}

const apple = {name: &quot;김사과&quot;, age: 20, address: {si: &quot;서울시&quot;, gu: &quot;서초구&quot;, dong: &quot;양재동&quot;}}
console.log(apple)  // {name: &quot;김사과&quot;, age: 20, address: {si: &quot;서울시&quot;, gu: &quot;서초구&quot;, dong: &quot;양재동&quot;}}
const new_apple = { ...apple, job: &quot;프로그래머&quot; }
console.log(new_apple) // {name: &quot;김사과&quot;, age: 20, address: {si: &quot;서울시&quot;, gu: &quot;서초구&quot;, dong: &quot;양재동&quot;}, job: &quot;프로그래머&quot;}
display(new_apple)

// new_apple에 pet 속성이 없으면 pet='루시'를 기본값으로 사용
const { pet = '루시' } = new_apple
// new_apple.job 속성 값을 변수 hobby에 저장
// 원래 이름은 job, 내가 사용하고 싶은 이름은 hobby
const { job: hobby } = new_apple
console.log(new_apple) // {name: &quot;김사과&quot;, age: 20, address: {si: &quot;서울시&quot;, gu: &quot;서초구&quot;, dong: &quot;양재동&quot;}, job: &quot;프로그래머&quot;}
console.log(hobby) // 프로그래머

const component = {
    name: &quot;Button&quot;,
    styles: {
        size: 20,
        color: 'black'
    }
}

function changeColor({ styles: { color } }) {
    console.log(color)
}

changeColor(component) // black&lt;/code&gt;&lt;/pre&gt;</description>
      <category>HTML,CSS,JavaScript</category>
      <category>JavaScript</category>
      <category>객체</category>
      <category>깊은 복사</category>
      <category>스프레드 문법</category>
      <category>얕은 복사</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/56</guid>
      <comments>https://gudwns5533.tistory.com/56#entry56comment</comments>
      <pubDate>Sun, 21 Jun 2026 01:03:44 +0900</pubDate>
    </item>
    <item>
      <title>탐색 알고리즘 (Search Algorithm)</title>
      <link>https://gudwns5533.tistory.com/55</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;탐색 알고리즘이란?&lt;/b&gt;&lt;/h2&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;탐색 또는 검색이란 주어진 데이터 집합에서 원하는 데이터를 찾는 과정을 말한다. 탐색은 데이터베이스 검색, 네트워크 경로 탐색, 정보 검색 등 다양한 분야에서 필수적으로 활용된다. 탐색 알고리즘이란 이러한 탐색 작업을 효율적으로 수행할 수 있도록 설계된 알고리즘이다. 탐색 알고리즘은 매커니즘에 따라 선형(Linear), 이진(Binary), 해싱(Hashing)으로 구분된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;탐색 알고리즘의 중요성&lt;/b&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;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;다양한 분야로의 응용&lt;/b&gt; : 체스나 바둑과 같은 게임 AI부터 웹 검색 엔진 크롤링까지, 논리적인 문제 해결 과정을 컴퓨터가 수행할 수 있도록 돕는 범용적인 기반 기술이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;선형 탐색 (Linear Search)&lt;/b&gt;&lt;/h2&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;가장 단순하고 직관적인 방법으로, 데이터의 첫 번째 요소부터 마지막 요소까지 순서대로 하나씩 원하는 값과 비교하며 찾는 방식이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;438&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4ZblD/dJMcabEy06p/07ykLS43pe0Fr4BRHhyuGk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4ZblD/dJMcabEy06p/07ykLS43pe0Fr4BRHhyuGk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4ZblD/dJMcabEy06p/07ykLS43pe0Fr4BRHhyuGk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/4ZblD/dJMcabEy06p/07ykLS43pe0Fr4BRHhyuGk/img.gif&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;438&quot; height=&quot;180&quot; data-origin-width=&quot;438&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;size18&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터가 정렬되어 있지 않아도 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;알고리즘이 매우 단순하여 구현하기 쉽다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&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;데이터의 양이 많아질수록 탐색 시간이 정비례하게 늘어난다. 시간 복잡도 O(n)&lt;/li&gt;
&lt;li&gt;최악의 경우 모든 데이터를 다 확인해야 하므로 비효율적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;선형 탐색을 사용하는 상황&lt;/b&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;/li&gt;
&lt;li&gt;데이터가 정렬되어 있지 않고, 정렬하는 비용이 더 클 때&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;선형 탐색의 코드 구현&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1781617904815&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def linear_search(list, target):
    for i in range(len(list)):
        if list[i] == target:
            return i
    return -1&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;이진 탐색&lt;/b&gt;&lt;/h2&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;데이터의 중간값(Mid)을 선택한 뒤, 찾고자 하는 값과 비교한다. 대소 관계에 따라 탐색 범위를 절반씩 줄여나가며 값을 찾는 방식이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;1148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Asj01/dJMcaijo9Ut/M0COxIU57as15QOKuQOl11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Asj01/dJMcaijo9Ut/M0COxIU57as15QOKuQOl11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Asj01/dJMcaijo9Ut/M0COxIU57as15QOKuQOl11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAsj01%2FdJMcaijo9Ut%2FM0COxIU57as15QOKuQOl11%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;669&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;1148&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;이진 탐색은 &quot;중간값보다 크면 오른쪽에 있고, 작으면 왼쪽에 있다&quot;라는 규칙을 전제로 탐색을 진행하게 된다. 이때 데이터가 무작위로 섞여 있다면 이 규칙이 성립하지 않으므로, 반드시 데이터가 오름차순이나 내림차순으로 정렬되어 있어야한다.&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;size18&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;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반드시 정렬된 데이터여야만 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;데이터가 자주 추가되거나 삭제되는 환경에서는 정렬을 유지하는 비용이 더 클 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;이진 탐색의 코드 구현&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1781617908765&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def binary_search(list, target):
    low = 0
    high = len(list) - 1

    while low &amp;lt;= high:
        mid = (low + high) // 2

        if list[mid] == target:
            return mid
        elif list[mid] &amp;lt; target:
            low = mid + 1
        else:
            high = mid - 1

    return -1&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;선형 탐색과 이진 탐색의 차이&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 68px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 16.4728%; height: 21px; text-align: center;&quot;&gt;분류&lt;/td&gt;
&lt;td style=&quot;width: 40.5426%; height: 21px; text-align: center;&quot;&gt;선형 탐색&lt;/td&gt;
&lt;td style=&quot;width: 42.9845%; height: 21px; text-align: center;&quot;&gt;이진 탐색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;width: 16.4728%; height: 10px; text-align: center;&quot;&gt;동작 원리&lt;/td&gt;
&lt;td style=&quot;width: 40.5426%; height: 10px; text-align: center;&quot;&gt;데이터를 처음부터 끝까지 순차적으로 검색&lt;/td&gt;
&lt;td style=&quot;width: 42.9845%; height: 10px; text-align: center;&quot;&gt;정렬된 배열에서 중간값을 기준으로 탐색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;width: 16.4728%; height: 10px; text-align: center;&quot;&gt;탐색 방법&lt;/td&gt;
&lt;td style=&quot;width: 40.5426%; height: 10px; text-align: center;&quot;&gt;한 개씩 순차 비교&lt;/td&gt;
&lt;td style=&quot;width: 42.9845%; height: 10px; text-align: center;&quot;&gt;중간값 기준 분할 탐색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.4728%; text-align: center;&quot;&gt;속도&lt;/td&gt;
&lt;td style=&quot;width: 40.5426%; text-align: center;&quot;&gt;느림&lt;/td&gt;
&lt;td style=&quot;width: 42.9845%; text-align: center;&quot;&gt;매우 빠름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;width: 16.4728%; height: 10px; text-align: center;&quot;&gt;시간 복잡도&lt;/td&gt;
&lt;td style=&quot;width: 40.5426%; height: 10px; text-align: center;&quot;&gt;O(n)&lt;/td&gt;
&lt;td style=&quot;width: 42.9845%; height: 10px; text-align: center;&quot;&gt;O(logn)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.4728%; height: 17px; text-align: center;&quot;&gt;정렬 필요 여부&lt;/td&gt;
&lt;td style=&quot;width: 40.5426%; height: 17px; text-align: center;&quot;&gt;필요 없음 (무작위 데이터도 가능)&lt;/td&gt;
&lt;td style=&quot;width: 42.9845%; height: 17px; text-align: center;&quot;&gt;무조건 정렬이 필요함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.4728%; text-align: center;&quot;&gt;사용하기 좋은 상황&lt;/td&gt;
&lt;td style=&quot;width: 40.5426%; text-align: center;&quot;&gt;데이터의 양이 적거나 정렬되지 않았을 때&lt;/td&gt;
&lt;td style=&quot;width: 42.9845%; text-align: center;&quot;&gt;데이터의 양이 많고 이미 정렬되어 있을 때&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CMyTS/dJMcagMIevy/RqS5wXOQ4YlXYxgzPt7L2k/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CMyTS/dJMcagMIevy/RqS5wXOQ4YlXYxgzPt7L2k/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CMyTS/dJMcagMIevy/RqS5wXOQ4YlXYxgzPt7L2k/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/CMyTS/dJMcagMIevy/RqS5wXOQ4YlXYxgzPt7L2k/img.gif&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;400&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;깊이 우선 탐색 (DFS)&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DFS란 무엇인가?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Depth-First Search의 약자로, 루트 노드(혹은 다른 임시의 노드)에서 시작해서 다음 분기(branch)로 넘어가기 전에 해당 분기를 완벽하게 탐색하는 방법을 의미한다.&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;즉, 넓게(wide) 탐색하기 전에 깊게(deep) 탐색하는 방법이다.&lt;/li&gt;
&lt;li&gt;사용하는 경우 : 모든 노드를 방문 하고자 하는 경우에 이 방법을 선택한다.&lt;/li&gt;
&lt;li&gt;깊이 우선 탐색(DFS)이 너비 우선 탐색(BFS)보다 좀 더 간단하다.&lt;/li&gt;
&lt;li&gt;단순 검색 속도 자체는 너비 우선 탐색(BFS)에 비해서 느리다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DFS의 특징&lt;/b&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;/li&gt;
&lt;li&gt;전위 순회를 포함한 다른 형태의 트리 순회는 모두 DFS의 한 종류이다.&lt;/li&gt;
&lt;li&gt;이 알고리즘을 구현할 때 가장 큰 차이점은, 그래프 탐색의 경우 어떤 노드를 방문했었는지 여부를 반드시 검새햐아 한다는 것이다. 검사하지 않을 경우 무한루프에 빠질 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DFS의 과정&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bV5eYz/dJMcahri38D/NUP3Fy1Ism7zu2IcauQgs1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bV5eYz/dJMcahri38D/NUP3Fy1Ism7zu2IcauQgs1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bV5eYz/dJMcahri38D/NUP3Fy1Ism7zu2IcauQgs1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bV5eYz/dJMcahri38D/NUP3Fy1Ism7zu2IcauQgs1/img.gif&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;400&quot; height=&quot;400&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 시작 노드를 방문하고 방문 처리를 한다.&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;3. 끝까지 내려갔다면 가장 최근의 갈림길로 돌아와서 다른 방향의 끝까지 내려간다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;스택(Stack)과의 관계&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS는 '가장 최근의 갈림길'로 돌아가야 하므로, LIFO(후입선출) 구조인 스택(Stack) 자료구조를 사용한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DFS가 사용되는 곳&lt;/b&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;/li&gt;
&lt;li&gt;경로의 특징을 저장해야 할 때&lt;/li&gt;
&lt;li&gt;그래프의 사이클 검사&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;너비 우선 탐색 (BFS)&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;BFS란 무엇인가?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Breadth-First Search의 약자로, 루트 노드(혹은 다른 임의의 노드)에서 시작해서 인접한 노드를 먼저 탐색하는 방법&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;깊게(deep) 탐색하기 전에 넓게(wide) 탐색하는 방법이다.&lt;/li&gt;
&lt;li&gt;사용하는 경우 : 두 노드 사이의 최단 경로 혹은 임의의 경로를 찾고 싶을 때 이 방법을 선택한다.&lt;/li&gt;
&lt;li&gt;너비 우선 탐색(BFS)이 깊이 우선 탐색(DFS)보다 좀 더 복잡하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;BFS의 특징&lt;/b&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: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;BFS는 시작 노드에서 시작해서 거리에 따라 단계별로 탐색한다고 볼 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;BFS는 재귀적으로 동작하지 않는다.&lt;/li&gt;
&lt;li&gt;이 알고리즘을 구현할 때 가장 큰 차이점은, 그래프 탐색의 경우 어떤 노드를 방문했었는지 여부를 반드시 검사해야 한다는 것이다. 이를 검사하지 않을 경우 무한루프에 빠질 수 있다.&lt;/li&gt;
&lt;li&gt;BFS는 방문한 노드들을 차례로 저장한 후 꺼낼 수 있는 자료 구조인 큐(Queue)를 사용한다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;선입선출(FIFO) 원칙으로 탐색&lt;/li&gt;
&lt;li&gt;일반적으로 큐를 이용해서 반복적 형태로 구현하는 것이 가장 잘 동작한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;BFS의 과정&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhQmxt/dJMcajvQKRy/bPj3wy4evxvqu6kNd7Esik/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhQmxt/dJMcajvQKRy/bPj3wy4evxvqu6kNd7Esik/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhQmxt/dJMcajvQKRy/bPj3wy4evxvqu6kNd7Esik/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bhQmxt/dJMcajvQKRy/bPj3wy4evxvqu6kNd7Esik/img.gif&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;400&quot; height=&quot;400&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 시작 노드를 방문 처리하고 저장소(큐)에 넣는다.&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;3. 저장소가 빌 때까지 이 과정을 반복한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;큐(Queue)와의 관계&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 발견한 이웃 노드들을 순서대로 먼저 방문해야 하므로, FIFO(선입선출) 구조인 큐(Queue) 자료구조를 사용한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;BFS가 사용되는 곳&lt;/b&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;/li&gt;
&lt;li&gt;최소 횟수 구하기&lt;/li&gt;
&lt;li&gt;소셜 네트워크 서비스 (SNS)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;DFS vs BFS&lt;/b&gt;&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 147px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 16.5891%; text-align: center; height: 21px;&quot;&gt;비교 항목&lt;/td&gt;
&lt;td style=&quot;width: 40.4263%; text-align: center; height: 21px;&quot;&gt;깊이 우선 탐색 (DFS)&lt;/td&gt;
&lt;td style=&quot;width: 42.9845%; text-align: center; height: 21px;&quot;&gt;너비 우선 탐색 (BFS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 16.5891%; text-align: center; height: 21px;&quot;&gt;탐색 방식&lt;/td&gt;
&lt;td style=&quot;width: 40.4263%; height: 21px; text-align: center;&quot;&gt;한쪽 방향으로 끝까지 깊게 탐색&lt;/td&gt;
&lt;td style=&quot;width: 42.9845%; height: 21px; text-align: center;&quot;&gt;가로로 한 층 한 층 탐색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 16.5891%; text-align: center; height: 21px;&quot;&gt;사용하는 자료구조&lt;/td&gt;
&lt;td style=&quot;width: 40.4263%; height: 21px; text-align: center;&quot;&gt;스택(Stack) / 재귀 함수 (Call Stack)&lt;/td&gt;
&lt;td style=&quot;width: 42.9845%; height: 21px; text-align: center;&quot;&gt;큐 (Queue)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 16.5891%; text-align: center; height: 21px;&quot;&gt;특징&lt;/td&gt;
&lt;td style=&quot;width: 40.4263%; height: 21px; text-align: center;&quot;&gt;깊은 경로를 먼저 확인&lt;/td&gt;
&lt;td style=&quot;width: 42.9845%; height: 21px; text-align: center;&quot;&gt;같은 거리의 노드를 먼저 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 16.5891%; text-align: center; height: 21px;&quot;&gt;장점&lt;/td&gt;
&lt;td style=&quot;width: 40.4263%; height: 21px; text-align: center;&quot;&gt;경로 탐색에 적합함&lt;/td&gt;
&lt;td style=&quot;width: 42.9845%; height: 21px; text-align: center;&quot;&gt;최단 거리 탐색에&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 16.5891%; text-align: center; height: 21px;&quot;&gt;단점&lt;/td&gt;
&lt;td style=&quot;width: 40.4263%; height: 21px; text-align: center;&quot;&gt;최단 경로를 보장하지 않음&lt;/td&gt;
&lt;td style=&quot;width: 42.9845%; height: 21px; text-align: center;&quot;&gt;메모리 공간의 소모가 심함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 16.5891%; text-align: center; height: 21px;&quot;&gt;사용 예시&lt;/td&gt;
&lt;td style=&quot;width: 40.4263%; height: 21px; text-align: center;&quot;&gt;미로 찾기, 백트래킹 등&lt;/td&gt;
&lt;td style=&quot;width: 42.9845%; height: 21px; text-align: center;&quot;&gt;최단 경로 찾기, SNS&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자료구조, 알고리즘</category>
      <category>BFS</category>
      <category>DFS</category>
      <category>선형 탐색</category>
      <category>이진 탐색</category>
      <category>탐색 알고리즘</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/55</guid>
      <comments>https://gudwns5533.tistory.com/55#entry55comment</comments>
      <pubDate>Wed, 17 Jun 2026 02:06:21 +0900</pubDate>
    </item>
    <item>
      <title>JaveScript 기초 3 (함수, 스코프, 배열)</title>
      <link>https://gudwns5533.tistory.com/54</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;JavaScript에서 함수의 기본 형태&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1781594942039&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function 함수명(매개변수1, 매개변수2, ...){
    실행할 코드
    return 반환값
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  return을 생략하면 함수는 자동으로 undefined를 반환한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;함수의 작성 방식&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 함수 선언식&lt;/b&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;/li&gt;
&lt;li&gt;코드 어디서든 호출할 수 있다는 특징 (호이스팅 적용)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781595147583&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function add(a, b){
    return a + b
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;  호이스팅&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호이스팅은 자바스크립트가 코드를 실행하기 전에 변수와 함수 선언을 해당 스코프의 최상단으로 끌어올려 처리하는 동작방식이다. 이로 인해 함수 선언문은 코드의 위치와 상관없이 호출할 수 있으며, var로 선언된 변수 또한 선언 자체는 끌어 올려지지만 값은 초기화되지 않아 undefined 상태가 된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 함수 표현식&lt;/b&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;/li&gt;
&lt;li&gt;함수가 변수에 저장되기 때문에 선언 이전에는 사용할 수 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781595312478&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const add = function(a, b) {
    return a + b
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 화살표 함수&lt;/b&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;/li&gt;
&lt;li&gt;this를 자체적으로 가지지 않고 상위 스코프인 this를 그대로 사용하기 때문에 콜백 함수나 간단한 연산 로직에 자주 사용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781595443095&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const add = (a, b) =&amp;gt; { return a + b }
const add = (a, b) =&amp;gt; a + b (return 생략)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. 익명 함수&lt;/b&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;/li&gt;
&lt;li&gt;한 번만 사용되거나 특정 상황에서 즉시 전달해야 하는 로직에 적합하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781595494486&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const print = function() { console.log(&quot;익명 함수 실행&quot;) }&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5. 즉시 실행 함수&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1781595524254&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(function() { console.log(&quot;즉시 실행&quot;) })()&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;6. 콜백 함수&lt;/b&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;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781595544606&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function greet(callback) { callback() }
greet(function() { console.log(&quot;안녕하세요!&quot;) })
greet(() =&amp;gt; { console.log(&quot;안녕하세요!&quot;) })&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;7. 가변 매개변수를 가진 함수&lt;/b&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;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781595627878&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function 함수명(...매개변수명) {
    매개변수명은 배열을 나타냄
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;8. 중첩 함수&lt;/b&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;/li&gt;
&lt;li&gt;내부 함수는 외부 함수의 영역 안에서만 사용할 수 있다는 특징이 있다.&lt;/li&gt;
&lt;li&gt;외부 함수의 변수에 접근할 수 있어 관련된 로직을 하나의 기능 단위로 묶을 수 있으며, 불필요한 전역 변수 사용을 줄이고 코드의 안정성과 가독성을 높일 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781595844030&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function 외부함수명(매개변수) {
    외부 함수 코드
    function 내부함수명(매개변수) {
        내부 함수 코드
    }
    내부함수명()
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;9. 고차 함수&lt;/b&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;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781595877630&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function 함수명(매개변수) {
    return function(매개변수) {
        return 반환값
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;스코프의 종류&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;전역 스코프&lt;/b&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;/li&gt;
&lt;li&gt;브라우저 환경에서는 전역에서 var로 선언한 변수는 보통 window의 프로퍼티가 되지만 let/const는 전역에 선언해도 전역 객체 프로퍼티로 붙지 않는 차이가 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781596555095&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var a = 1 // window.a = 1
let b = 2 // window.b (x)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;함수 스코프&lt;/b&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;/li&gt;
&lt;li&gt;var는 블록의 중괄호를 무시하고 함수 단위로만 스코프가 결정되는 함수 스코프를 가진다.&lt;/li&gt;
&lt;li&gt;var를 블록 안에서 선언해도 함수 밖에서 접근 가능한 경우가 많아 버그의 원인이 될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781596636670&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function test() {
    if(true) {
        var x = 10
        console.log(x)
    }
    console.log(x) // 블록 밖에서도 접근됨
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;블록 스코프&lt;/b&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;/li&gt;
&lt;li&gt;let과 const는 블록 스코프를 가지므로 if, for, while, try/catch 등의 블록 안에서 선언하면 그 블록 밖에서는 접근할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781596732551&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if(true) {
    let y = 20
    const z = 30
}
console.log(y) // error
console.log(z) // error&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;지역 스코프&lt;/b&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;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781596760503&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function outer() {
    const local = &quot;지역 변수&quot;
    console.log(local) // ok
}
console.log(local) // error&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;렉시컬 스코프&lt;/b&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;/li&gt;
&lt;li&gt;내부 함수는 자신이 선언된 위치 기준으로 바깥 변수에 접근할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781596801343&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const x = &quot;전역&quot;
function outer() {
    const x &quot;outer&quot;
    function inner() {
        console.log(x) // &quot;outer&quot; 선언 위치 기준으로 상위
    }
    inner()
}
outer()&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;스코프 체인&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;변수를 찾을 때 자바스크립트는 현재 스코프 -&amp;gt; 상위 스코프 -&amp;gt; ... -&amp;gt; 전역 스코프 순서로 올라가며 검색한다.&lt;/li&gt;
&lt;li&gt;이 연결 구조를 스코프 체인이라 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;배열&lt;/b&gt;&lt;/h2&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;index라는 번호로 구분되며, 대부분 0으로 시작한다.&lt;/li&gt;
&lt;li&gt;여러 가지 자료형을 함께 저장 할 수 있다.&lt;/li&gt;
&lt;li&gt;인덱스를 기준으로 값을 저장하고 접근한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;배열 메서드&lt;/b&gt;&lt;/h2&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;li&gt;pop() : 배열의 마지막 요소를 제거&lt;/li&gt;
&lt;li&gt;unshift() : 배열의 앞에 요소를 추가&lt;/li&gt;
&lt;li&gt;shift() : 배열의 첫 번째 요소를 제거&lt;/li&gt;
&lt;li&gt;forEach() : 배열의 각 요소를 순서대로 하나씩 처리&lt;/li&gt;
&lt;li&gt;map() : 각 요소를 가공하여 새로운 배열을 반환&lt;/li&gt;
&lt;li&gt;filter() : 조건을 만족하는 요소만 골라 새 배열 반환&lt;/li&gt;
&lt;li&gt;reduce() : 배열을 하나의 값으로 누적 계산 (합계, 객체 생성)&lt;/li&gt;
&lt;li&gt;find() : 조건을 반족하는 첫 번째 요소 반환&lt;/li&gt;
&lt;li&gt;findIndex() : 조건을 만족하는 첫 번째 요소의 인덱스를 반환&lt;/li&gt;
&lt;li&gt;includes() : 특정 값이 포함되어 있는지 여부 반환&lt;/li&gt;
&lt;li&gt;indexOf(): 특정 값의 첫 인덱스 반환 (없으면 -1)&lt;/li&gt;
&lt;li&gt;sort() : 배열 요소를 정렬 (원본 배열 변경)&lt;/li&gt;
&lt;li&gt;reverse() : 배열 순서 반전&lt;/li&gt;
&lt;li&gt;slice() : 배열의 일부를 잘라 새 배열 반환 (원본 유지)&lt;/li&gt;
&lt;li&gt;splice() : 요소 추가 / 삭제 / 교체 (원본 배열 변경)&lt;/li&gt;
&lt;li&gt;join() : 배열을 문자열로 변환&lt;/li&gt;
&lt;li&gt;concat() : 배열을 합쳐서 새 배열 반환&lt;/li&gt;
&lt;li&gt;flat() : 중첩 배열을 평탄화&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>HTML,CSS,JavaScript</category>
      <category>JavaScript</category>
      <category>배열</category>
      <category>스코프</category>
      <category>함수</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/54</guid>
      <comments>https://gudwns5533.tistory.com/54#entry54comment</comments>
      <pubDate>Tue, 16 Jun 2026 17:13:40 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript 기초 2 (연산자, 제어문)</title>
      <link>https://gudwns5533.tistory.com/53</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;연산자&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 산술 연산자 &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+,&amp;nbsp; -,&amp;nbsp; *,&amp;nbsp; /,&amp;nbsp; %,&amp;nbsp; **&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 대입 연산자&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=,&amp;nbsp; +=,&amp;nbsp; -=,&amp;nbsp; *=,&amp;nbsp; /=,&amp;nbsp; %=&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 비교 연산자&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;==,&amp;nbsp; !=,&amp;nbsp; &amp;gt;,&amp;nbsp; &amp;lt;,&amp;nbsp; &amp;gt;=,&amp;nbsp; &amp;lt;=&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;== : 값만 비교&lt;/li&gt;
&lt;li&gt;=== : 값 + 타입까지 비교&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781592852998&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;10 == '10' -&amp;gt; true
10 === '10' -&amp;gt; false&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;!= : 값만 비교&lt;/li&gt;
&lt;li&gt;!== : 값 + 타입까지 비교&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781592862014&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;10 != '10' -&amp;gt; false
10 !== '10' -&amp;gt; true  !(a === b)와 동일한 의미&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1781593027702&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 비교 연산자
console.log(5 == &quot;5&quot;) // true
console.log(5 === &quot;5&quot;) // false&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. 논리 연산자&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;amp;&amp;amp; : AND&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;|| : OR&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;! : NOT&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5. 증감 연산자&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;++ : 값을 1 증가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-- : 값을 1 감소&lt;/p&gt;
&lt;pre id=&quot;code_1781593214526&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 증감 연산자
let count = 1
count ++
console.log(count)    // count: 2
count --
console.log(count)    // count: 1
let result = ++count
console.log(result)   // result: 2
console.log(count)    // count: 2
result = count++
console.log(result)   // result: 2
console.log(count)    // count: 3&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;6. 문자열 연산자&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ : 문자열을 연결할 때 사용&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;7. 조건(삼항) 연산자&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건에 따라 다른 값을 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건 ? 값1 : 값2 (조건이 true면 값1, 조건이 false면 값2)&lt;/p&gt;
&lt;pre id=&quot;code_1781593225870&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 조건(삼항) 연산자
let score = 85
result = score &amp;gt;= 60 ? &quot;합격&quot; : &quot;불합격&quot;
console.log(result) // 합격&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;8. 타입 관련 연산자&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;typeof : 변수의 타입을 반환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;instanceof : 변수가 특정객체의 타입인지 확인&lt;/p&gt;
&lt;pre id=&quot;code_1781593236190&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 타입 관련 연산자
let num = 10
let arr = []
console.log(typeof num) // number
console.log(arr instanceof Array) // true&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;9. 기타 연산자&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;, : 여러 표현식 나열&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;? : 옵셔널 체이닝 (null인지 아닌지 체크)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;?? : null 병합 연산자 (null일 경우 특정값을 반환&lt;/p&gt;
&lt;pre id=&quot;code_1781593268342&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let x = (1, 2, 3, 10)  // 마지막 요소가 출력
console.log(x) // 10

let user = null
console.log(user?.name) // undefined

let value = null
let resultValue = value ?? &quot;기본값&quot;
console.log(resultValue) // 기본값&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;제어문 - 조건문&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1781593596446&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if 문
if(조건) {
    조건이 참일 때 실행
}


if(조건) {
    조건이 참일 때 실행
}else{
    조건이 거짓일 때 실행
}


if(조건1) {
    조건1이 참일 때 실행
}else if(조건2){
    조건1은 거짓이고, 조건2가 참일 때 실행
}else{
    위 모든 조건이 거짓일 때 실행
}


switch 문
여러 개의 경우(case)를 비교할 때 사용하는 조건문

switch(값) {
    case 값1:
        값이 값1과 일치할 때 실행할 코드
        break
    case 값2:
        값이 값2와 일치할 때 실행할 코드
        break
    default:
        위에 모든 case에 값이 일치하지 않을 때 실행할 코드
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;제어문 - 반복문&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1781593803494&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;while 문
while(조건식){
    조건식이 true일 동안 실행될 코드
}


do - while 문
do {
    반복 실행할 코드
}while(조건식)

for(초기식; 조건식; 증감식){
    조건식이 true일 동안 반복 실행할 코드
}

// `${변수}` : Python의 f-string 처럼 사용 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1781593932526&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;h1&amp;gt;while 문&amp;lt;/h1&amp;gt;
&amp;lt;script&amp;gt;
    let dan = 7
    let i = 1

    while (i &amp;lt;= 9) {
        console.log(`${dan} * ${i} = ${dan * i}`)
        i++
    }
&amp;lt;/script&amp;gt;


&amp;lt;h1&amp;gt;Do ~ while 문&amp;lt;/h1&amp;gt;
&amp;lt;script&amp;gt;
    let num = 10
    do {
        console.log(`현재 num: ${num}`)
        num++
    } while (num &amp;lt;= 9)
&amp;lt;/script&amp;gt;


&amp;lt;h1&amp;gt;for 문&amp;lt;/h1&amp;gt;
&amp;lt;script&amp;gt;
    while (i &amp;lt;= 5) {
        console.log(`현재 i의 값:${i}`)
        i++
    }

    for (let i = 1; i &amp;lt;= 5; i++) {
        console.log(`현재 i의 값: ${i}`)
    }
&amp;lt;/script&amp;gt;


&amp;lt;h1&amp;gt;중첩 for문 구구단&amp;lt;/h1&amp;gt;
&amp;lt;script&amp;gt;
    for (let dan = 2; dan &amp;lt;= 9; dan++) {
        for (let i = 1; i &amp;lt;= 9; i++) {
            console.log(`${dan} * ${i} = ${dan * 1}`)
        }
    }
&amp;lt;/script&amp;gt;


&amp;lt;h1&amp;gt;break 문&amp;lt;/h1&amp;gt;
&amp;lt;script&amp;gt;
    for (let i = 1; i &amp;lt;= 10; i++) {
        if (i &amp;gt; 5) break
        console.log(`i의 값: ${i}`)
    }
&amp;lt;/script&amp;gt;


&amp;lt;h1&amp;gt;continue 문&amp;lt;/h1&amp;gt;
&amp;lt;script&amp;gt;
    for (let i = 1; i &amp;lt;= 100; i++) {
        if (i % 3 == 0) {
            continue
        }
        console.log(`i의 값: ${i}`)
    }
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>HTML,CSS,JavaScript</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/53</guid>
      <comments>https://gudwns5533.tistory.com/53#entry53comment</comments>
      <pubDate>Tue, 16 Jun 2026 16:27:34 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript 기초 1 (개요, 변수, 자료형)</title>
      <link>https://gudwns5533.tistory.com/52</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;JavaScript&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트(JavaScript)는 웹 브라우저에서 동작하는 프로그래밍 언어로, HTML와 CSS로 만들어진 정적인 웹 페이지에 동적인 기능과 사용자와의 상호작용을 추가하는 역할을 한다. 버튼 클릭, 입력값 처리, 화면 내용 변경, 애니메이션 제어, 서버와의 데이터 통신 등 다양한 기능을 수행할 수 있으며, 오늘날에는 브라우저를 넘어 서버(Node.js), 모바일, 데스크톱, 애플리케이션까지 활용 범위가 확장된 핵심 웹 기술이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;JaveScript의 실행&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 브라우저가 페이지를 여는 순간&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 URL을 입력하면 브라우저는 서버로부터 HTML 문서를 받아온다. 이때 브라우저는 HTML을 위에서 아래로 한 줄씩 읽기 시작한다.   이 과정을 파싱이라고 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. HTML 파싱 중 JavaScript를 만나면?&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTML을 읽다가 &amp;lt;Script&amp;gt; 태그를 만나면 아래과 같이 진행한다.&lt;/li&gt;
&lt;li&gt;HTML 파싱 일시중단&lt;/li&gt;
&lt;li&gt;JavaScript 파일 다운로드&lt;/li&gt;
&lt;li&gt;JavaScript 실행&lt;/li&gt;
&lt;li&gt;실행이 끝난 뒤 다시 HTML 파싱 재개&lt;/li&gt;
&lt;li&gt;  JavaScript는 HTML 파싱을 멈추게 한다는 것이 핵심&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;콘솔 출력&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript에서는 Python의 print()처럼 console.log(&quot;내용&quot;)으로 내용을 출력할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1781528114866&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(&quot;안녕하세요. 자바스크립트입니다.&quot;)
console.log(&quot;현재 페이지가 정상적으로 로드되었습니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;외부 자바스크립트 가져오기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 css를 가져오는방법과 비슷하다. &amp;lt;head&amp;gt;나 &amp;lt;body&amp;gt;에 &amp;lt;script&amp;gt; 태그로 불러오면 된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1781530347893&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;./js/script.js&quot;&amp;gt; &amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;변수&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1781530505946&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;변수
let, const, var

변수 선언
let age
let name

값 할당
age = 20
name = &quot;김사과&quot;

변수 선언과 할당
let age = 20
let name = &quot;김사과&quot;

변수 이름 작성 규칙
- 문자, 숫자, _, $ 사용 가능
- 숫자로 시작 불가
- 공백 사용 불가
- 예약어 사용 불가

var와 let/const의 차이
var와 let/const의 가장 큰 차이는 변수의 유효 범위(scope)와 예측 가능성에 있습니다. 
var는 함수 단위 스코프를 가지며 선언 전에 사용해도 동작하는 호이스팅 특성 때문에 의도치 않은 오류를 만들기 쉽고, 중복 선언도 허용됩니다. 
반면 let과 const는 블록 단위 스코프를 가져 {} 안에서만 유효하며 중복 선언이 불가능해 코드의 안정성이 높습니다. 
또한 const는 한 번 할당한 값을 변경할 수 없어 의도를 명확히 표현할 수 있어, 현대 자바스크립트에서는 var 대신 let과 const 사용이 권장됩니다.&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;자료형 - 원시 타입&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1781531376292&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;원시 타입
원시 타입은 하나의 값만 저장하며, 값 자체가 변수에 직접 저장됩니다.

Number : 숫자형 데이터. 정수, 실수, 음수, 양수 모두 포함
    let age = 20
    let price = 3.14

String : 문자열 데이터. 큰따옴표(&quot;&quot;), 작은따옴표(''), 백팃()``)
    let name = &quot;김사과&quot;
    let name = String(&quot;김사과&quot;)

Boolean : 참 (true) / 거짓 (false)
    let isLogin = true

undefined : 값이 할당되지 않은 상태, 변수를 선언했지만 값을 할당하지 않았을 때 자동으로 undefined
    let vaule

Null : 의도적으로 값이 없음을 표현
    let data = null

BigInt : 매우 큰 정수를 표현. 일반 number로 표현할 수 없는 큰 정수를 다룰 때 사용
    let bigNum = 118473875827289374592n
    Number.MAX_SAFE_INTEGER&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1781592154390&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
    let age = 20;
    let name = &quot;김사과&quot;
    let isStudent = true
    let result
    let data = null
    let age2 = age

    //typeof
    console.log(&quot;age:&quot;, age, typeof age) // 20 number
    console.log(&quot;age2:&quot;, age2, typeof age2) // 20 number
    console.log(&quot;name:&quot;, name, typeof name) // 김사과 string
    console.log(&quot;isStudent:&quot;, isStudent, typeof isStudent) // true boolean
    console.log(&quot;result:&quot;, result, typeof result) // undefined undefined
    console.log(&quot;data:&quot;, data, typeof data) // null object
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;원시 타입의 저장 방식&lt;/b&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;li&gt;다른 변수에 복사하면 완전히 새로운 값이 복사되며, 한쪽을 변경해도 다른 쪽에는 영향을 주지 않음.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;자료형 - 참조 타입&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1781591506118&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;참조 타입
값의 주소(참조)를 저장하며, 여러 값을 묶어 관리할 수 있음

Object: 키와 값이 쌍으로 이루어진 객체
    let user = { name: &quot;김사과&quot;, age: 20 }

Array: 여러 값을 순서대로 저장
    let scores = [90, 80, 70]

Function: 실행 가능한 코드 블록
    function hello() { console.log(&quot;안녕하세요&quot;) }
    let hello = function() { console.log(&quot;안녕하세요&quot;) }&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1781592071542&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
    // 객체
    const user = { name: &quot;김사과&quot;, age: 20 }
    console.log(&quot;user 객체:&quot;, user) // Object
    console.log(&quot;user 객체 타입:&quot;, typeof user) // object

    // 배열
    const scores = [90, 80, 100, 60]
    console.log(&quot;scores 배열:&quot;, scores) // Array
    console.log(&quot;scores 배열 타입:&quot;, typeof scores) // object

    // 함수
    function greet() {
        console.log(&quot;안녕하세요&quot;)
    }
    console.log(&quot;greet 함수:&quot;, greet) // function greet(){console.log(&quot;안녕하세요&quot;)}
    console.log(&quot;greet 함수 타입:&quot;, typeof greet) // function

    // 참조 타입의 특징 (같은 주소를 가리키기 때문에 복사본을 바꾸면 원본도 바뀐다.)
    const copyUser = user
    copyUser.age = 25
    console.log(&quot;원본 user:&quot;, user) // Object
    console.log(&quot;복사본 copyUser:&quot;, copyUser) // object
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;참조 타입의 저장 방식&lt;/b&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;li&gt;다른 변수에 할당하면 같은 데이터를 가리키게 되고, 한쪽에서 값을 수정하면 다른 쪽에서도 변경된 결과가 보임&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;스택 vs 힙&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;스택 (Stack)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램 실행 중 함수 호출과 지역 변수, 매개변수 같은 임시 데이터가 저장되는 메모리 공간&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 실행되는 순서대로 쌓였다가 함수가 종료되면 자동으로 제거되는 후입선출 구조를 가지며, 관리가 단순하고 접근 속도가 빠르다는 장점이 있음.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;힙 (Heap)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체, 배열처럼 크기가 유동적이거나 오래 유지되어야 하는 데이터가 저장되는 메모리 공간&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램 실행 중 필요할 때 할당되고, 더 이상 사용되지 않으면 가비지 컬렉터에 의해 정리되며, 스택보다 관리가 복잡하지만 큰 데이터와 공유 데이터를 다루기에 적합.&lt;/p&gt;</description>
      <category>HTML,CSS,JavaScript</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/52</guid>
      <comments>https://gudwns5533.tistory.com/52#entry52comment</comments>
      <pubDate>Mon, 15 Jun 2026 22:52:20 +0900</pubDate>
    </item>
    <item>
      <title>HTML 과 CSS 기초 3</title>
      <link>https://gudwns5533.tistory.com/51</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;flexbox&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flexbox란 CSS에서 요소들을 한 방향(가로 또는 세로)으로 유연하게 배치하고 정렬하기 위한 레이아웃 모델로, 부모 요소에 display: flex; 를 적용해 자식 요소들의 크기, 정렬, 간격을 쉽게 제어할 수 있다.&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;&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;flexbox에서 display: flex가 적용된 부모 요소를 의미.&lt;/li&gt;
&lt;li&gt;내부에 있는 자식 요소들을 어떻게 배치하고 정렬할지에 대한 기준과 규칙을 정하는 역할.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&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;flex 컨테이너 안에 포함된 자식 요소들을 의미하며, 컨테이너가 정한 배치 규칙에 따라 실제로 화면에 놓이는 대상.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;justify-content&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가로 또는 세로 방향 정렬&lt;/p&gt;
&lt;pre id=&quot;code_1781521880047&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;justify-content: flex-start;    /* 기본값, 왼쪽으로부터 정렬 */
justify-content: center;        /* 가운데 */
justify-content: space-between  /* 양 끝 요소를 끝에 붙이고 사이 간격만 동일하게 배치 */
justify-content: space-around   /* 요소 주변에 동일한 여백을 줌 */&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;flex-direction&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아이템이 배치되는 방향 결정&lt;/p&gt;
&lt;pre id=&quot;code_1781523050882&quot; class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;css&quot;&gt;&lt;code&gt;flex-direction: row; /* 기본값, 가로 */
flex-direction: column; /* 세로 */&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;flex-wrap&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;줄바꿈 처리&lt;/p&gt;
&lt;pre id=&quot;code_1781521935685&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;flex-wrap: nowrap;   /* 기본값, 줄바꿈 하지 않음 */
flex-wrap: wrap;     /* 줄바꿈 */&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;flex-grow&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;남는 공간을 얼마나 차지할지 결정&lt;/p&gt;
&lt;pre id=&quot;code_1781522858973&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;flex-grow: 1;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;flex-shrink&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공간 부족 시 줄어드는 정도&lt;/p&gt;
&lt;pre id=&quot;code_1781522879348&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;flex-shrink: 1;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;flex-basis&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이템의 시작 크기&lt;/p&gt;
&lt;pre id=&quot;code_1781522893815&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;flex-basis: 200px;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;flex&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 세가지 축약 속성&lt;/p&gt;
&lt;pre id=&quot;code_1781522909727&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;flex 1 1 200px;   /* flex-grow flex-shrink flex-basis */&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;align-items&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;주축과 수직 방향 정렬&lt;/p&gt;
&lt;pre id=&quot;code_1781522959833&quot; class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;css&quot;&gt;&lt;code&gt;align-items: stretch;   /* 기본값, 위로부터 정렬, 자식의 height가 없을 때 동작 */
align-items: center;    /* 가운데 */
align-items: flex-end;  /* 아래로 정렬 */&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;align-content&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여러 줄 정렬&lt;/p&gt;
&lt;pre id=&quot;code_1781522996261&quot; class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;css&quot;&gt;&lt;code&gt;align-content: center;
align-content: space-between;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;align-self&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;특정 아이템만 정렬 다르게 적용&lt;/p&gt;
&lt;pre id=&quot;code_1781522972866&quot; class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;css&quot;&gt;&lt;code&gt;align-self: center;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;gap&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아이템 간 간격&lt;/p&gt;
&lt;pre id=&quot;code_1781523024768&quot; class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;css&quot;&gt;&lt;code&gt;gap: 20px;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;gridbox&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS에서 요소를 행과 열을 기준으로 배치하는 2차원 레이아웃 시스템. display: grid;로 적용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1781523363552&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;align-* : 세로(수직)
justify-* : 가로(수평)
*-items : 모든 아이템의 기본 정렬(컨테이너에 설정)
*-self : 특정 아이템 1개만 설정&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;grid-template&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1781523886239&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 열의 개수와 크기를 설정 */
grid-template-columns: 1fr 2fr 1fr /* 값을 공백으로 나열한 개수 = 열 개수 */

/* 행의 개수와 크기를 설정 */
grid-template-rows: auto 200px;

/* grid를 '좌표/라인 번호'가 아니라 '이름(영역)'으로 설계하게 해주는 속성 */
grid-template-areas

grid-template-areas:
    &quot;header header&quot;  /* 큰따옴표 한 줄 = 한 행 */
    &quot;nav    main&quot;    /* 따옴표 안의 토큰 개수 = 열 */
    &quot;footer footer&quot;;  
/*  &quot;header header&quot;  2칸(2열)이며 둘 다 header라서 가로로 2칸이 합쳐진 형태 */&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;justify-items&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;grid 컨테이너에서 각 grid 셀 안에서 아이템을 가로 방향으로 어떻게 정렬할지를 정하는 속성&lt;/p&gt;
&lt;pre id=&quot;code_1781524075861&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;justify-items: center;    /* 각 셀의 가로 중앙에 위치 */
justify-items: row-gap;   /* 행과 행 사이 간격을 동일 */
justify-items: stretch;   /* 아이템이 각 칸의 가로폭을 꽉 채우도록 늘림 */
justify-items: start;     /* 각 셀의 왼쪽에 붙음 */
justify-items: end;       /* 각 셀의 오른쪽에 붙음 */&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;place-items&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세로/가로 정렬을 한 번에 설정&lt;/p&gt;
&lt;pre id=&quot;code_1781524992325&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;place-items: center;
place-items = align-items(세로) + justify-items(가로)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;아이템에 사용하는 속성&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1781525052330&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;grid-column
grid 아이템이 가로 방향으로 어디서 시작해서 어디까지 차지할지를 정하는 속성
grid-column: 1 / 3;
1번에서 시작, 3번 전까지 차지(1열 + 2열 &amp;gt; 2칸 차지)

grid-low
grid 아이템이 세로 방향으로 어디서 시작해서 어디까지 차지할지를 정하는 속성

grid-area
grid-area: header;
영역 이름을 지정(grid-template-areas와 세트)

justify-self
justify-self: center;
grid 아이템 한 개를 셀 안에서 가로 방향으로 어떻게 정렬할지 정하는 속성

align-self
grid 아이템 한 개를 셀 안에서 세로 방향으로 어떻게 정렬할지 정하는 속성

place-self
place-self: center;
grid 아이템 한 개의 정렬을 align-self + justify-self 정하는 속성

* 반응형 그리드
fr: 남는 공간을 비율로 나누는 단위
    1fr 2fr;

repeat()
반복 정의
repeat(3, 1fr) -&amp;gt; 1fr 1fr 1fr과 동일

minmax()
트랙(열/행)의 크기를 최소 ~ 최대 범위로 지정하는 함수
minmax(200px, 1fr) -&amp;gt; 1열은 최소 200px, 공간이 남으면 최대 1fr까지 늘어남

auto-fit / auto-fill
repeat()에서 열(또는 행) 개수를 자동으로 만들 때 쓰는 키워드
repeat(auto-fit, minmax(200px, 1fr))
컨테이너 폭을 보고 minmax(200px, 1fr)짜리 열이 한 줄에 몇 개 들어갈 수 있는지 계산, 그 개수만큼 열을 반복해서 생성
auto-fit: 남는 빈 칸이 생기면 그 칸을 없애서 아이템들이 더 넓게 늘어나게 함
auto-fill: 가능한 만큼 열(칸)을 끝까지 채워서 만들어 둠&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;반응형&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹이나 앱 화면이 사용자의 기기 환경에 맞춰 화면 크기, 해상도, 방향(가로, 세로) 등에 따라 레이아웃과 요소 배치가 자동으로 변화하도록 설계하는 사고방식을 의미한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;미디어 쿼리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS에서 화면의 너비, 높이, 해상도, 기기 방향 같은 조건에 따라 서로 다른 스타일을 적용할 수 있게 해주는 기능&lt;/p&gt;
&lt;pre id=&quot;code_1781525272930&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@media (조건) {
    조건을 만족할 때 적용되는 CSS
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;브레이크 포인트&lt;/b&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;주로 미디어 쿼리에서 min-width나 max-width 값으로 설정되며, 이 지점을 기준으로 요소의 배치, 크기, 정렬 방식이 달라져 모바일 / 태블릿 / 데스크탑 등 각 기기에 맞는 화면 구성을 만들 수 있음
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;480px 이하 : 모바일&lt;/li&gt;
&lt;li&gt;768px 이하 : 태블릿&lt;/li&gt;
&lt;li&gt;1024px 이상 : 데스크탑&lt;/li&gt;
&lt;li&gt;1200px 이상 : 와이드 데스크탑&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781525466008&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;max-width(이하)
@media (max-width: 768px){...}
768px 이하로 작아지면 적용

min-width(이상)
@media (min-width: 1024px){...}
1024px 이상으로 커지면 적용

AND
@media (min-width: 768px) and (max-width: 1023px){...}
768px ~ 1023px 사이에만

OR
@media (max-width: 480px) or (min-width: 1200px){...}
480px 이하 또는 1200px 이상&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;변수&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자주 사용하는 스타일 값을 이름으로 정의해 두고 필요할 때마다 재사용할 수 있게 해주는 기능으로, --변수이름 형태로 선언하고 var() 함수로 불러와서 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1781525670488&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;선언 방법
:root {
    --main-color: #1234db;
    --font-size-base: 15px;
    --spacing: 20px;
}

사용 방법
body {
    background-color: var(--main-color);
    font-size: var(--font-size-base);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;transition&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요소의 상태가 변경될 때 스타일 값이 즉시 바뀌지 않고 일정 시간에 걸쳐 부드럽게 변화하도록 만드는 기능&lt;/p&gt;
&lt;pre id=&quot;code_1781525896019&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;선택자 { trasition: 속성 시간; }&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1781526019371&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;transition-property: width, background-color;
어떤 속성을 애니메이션 할지 지정

transition-duration: 0.5s;
변화가 진행되는 시간

transition-timing-function: ease;
변화 속도의 흐름
    - ease: 시작과 끝은 느리고 중간은 빠르게 움직임
    - linear: 처음부터 끝까지 같은 속도로 움직임
    - ease-in: 시작은 느리고 점점 빨라짐
    - ease-out: 시작은 빠르고 끝은 느려짐
    - ease-in-out: 시작과 끝은 느리고 중간은 빠르게 움직임

transition-delay: 0.2s
시작 지연 시간

transition: 속성 시간 타이밍 지연;
축약 문법&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;transform&lt;/b&gt;&lt;/h2&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;문서의 실제 레이아웃 흐름에는 영향을 주지 않고 시각적으로만 변형되기 때문에 다른 요소의 배치가 깨지지 않으며, GPU 가속을 활용해 성능이 좋아 애니메이션이나 인터렉션 효과를 구현할 때 자주 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781526233151&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;선택자 { transform: 변형함수(값); }

translate: 이동
scale: 크기 변경
rotate: 회전
skew: 기울이기
transform: 여러 변형 함께 사용&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;animation&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@keyframes를 이용해 여러 단계의 스타일 변화를 정의하고 이를 자동으로 재생하는 기능으로, 사용자 상호작용이 없어도 시간의 흐름에 따라 요소가 움직이거나 변형되도록 만들 수 있음&lt;/p&gt;
&lt;pre id=&quot;code_1781526595611&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@keyframes 애니메이션이름 {
    단계 {
        스타일;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1781526803718&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;animation-name
animation-name: fade
적용할 @keyframes 이름을 지정

animation-duration
animation-duration: 2s;
애니메이션 한 번 실행되는 시간

animation-timing-function
animation-timing-function: ease-in-out
애니메이션 속도 변화 방식

animation-delay
animation-delay: 1s
애니메이션 시작 전 대기 시간

animation-iteration-count
animation-iteration-count: infinite;
반복 횟수 지정. 숫자 또는 infinite(무제한)

animation-direction
animation-direction: alternate:
재생 방향. forward(앞으로), alternate(앞으로 뒤로)

animation-fill-mode
animation-fill-mode: forwards;
시작/종료 후 스타일 유지 여부

animation-play-state
animation-play-state: paused;
애니메이션 재생/정지. play(진행), paused(멈춤)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>HTML,CSS,JavaScript</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/51</guid>
      <comments>https://gudwns5533.tistory.com/51#entry51comment</comments>
      <pubDate>Mon, 15 Jun 2026 21:34:58 +0900</pubDate>
    </item>
    <item>
      <title>재귀 함수 (Recursive Function)</title>
      <link>https://gudwns5533.tistory.com/50</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;재귀 함수란?&lt;/b&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;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;재귀 함수의 기본 구조&lt;/b&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;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;종료 조건(Base Case)&lt;/b&gt; : 재귀 호출을 멈추는 조건. 이 조건이 충족되면 함수는 더 이상 자신을 호출하지 않고 종료된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;재귀 조건(Recursive Case)&lt;/b&gt; : 함수가 자기 자신을 호출하는 부분. 이 부분이 종료 조건을 향해 문제를 축소시킨다.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;재귀 함수와 일반 함수의 차이&lt;/b&gt;&lt;/blockquote&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: 10.6589%; text-align: center;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 42.6356%; text-align: center;&quot;&gt;재귀 함수&lt;/td&gt;
&lt;td style=&quot;width: 46.7054%; text-align: center;&quot;&gt;일반 함수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 10.6589%; text-align: center;&quot;&gt;정의&lt;/td&gt;
&lt;td style=&quot;width: 42.6356%;&quot;&gt;함수 내에서 자기 자신을 다시 호출&lt;/td&gt;
&lt;td style=&quot;width: 46.7054%;&quot;&gt;정의된 로직을 순차적으로 실행하거나 다른 함수를 호출&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 10.6589%; text-align: center;&quot;&gt;메모리&lt;/td&gt;
&lt;td style=&quot;width: 42.6356%;&quot;&gt;호출될 때마다 스택 메모리에 새로운 공간이 할당됨&lt;/td&gt;
&lt;td style=&quot;width: 46.7054%;&quot;&gt;한 번 할당된 메모리 영역을 재사용하며 루프를 돎&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 10.6589%; text-align: center;&quot;&gt;종료 조건&lt;/td&gt;
&lt;td style=&quot;width: 42.6356%;&quot;&gt;반드시 종료 조건(Base Case)이 필요&lt;/td&gt;
&lt;td style=&quot;width: 46.7054%;&quot;&gt;루프의 조건식이 거짓이 될 때 종료&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 10.6589%; text-align: center;&quot;&gt;가독성&lt;/td&gt;
&lt;td style=&quot;width: 42.6356%;&quot;&gt;수학적 공식이나 트리/그래프 구조를 다룰 때 매우 직관적이고 간결함&lt;/td&gt;
&lt;td style=&quot;width: 46.7054%;&quot;&gt;복잡한 알고리즘의 경우 반복문이 오히려 더 길고 복잡해질 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 10.6589%; text-align: center;&quot;&gt;성능/안정성&lt;/td&gt;
&lt;td style=&quot;width: 42.6356%;&quot;&gt;깊이가 깊어지면 스택 오버플로우 에러 발생 위험&lt;/td&gt;
&lt;td style=&quot;width: 46.7054%;&quot;&gt;메모리 오버헤드가 적어 비교적 안전하고 빠름&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;재귀 함수를 사용하는 이유&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. 경우에 따라 가독성을 높일 수 있다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. 변수의 사용을 줄여준다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수의 사용을 줄여준다는 것은 변수가 저장되는 메모리에 대한 이야기가 아니라 변경 가능한 상태(Mutable State)를 제거하여 프로그램 오류가 발생할 수 있는 가능성을 줄여준다는 의미이다. 이는 변수의 수를 줄이는 것 뿐만 아니라 변수가 가질 수 있는 값의 종류 또는 범위도 제한하게 되어 함수를 단순하게 만들고, 불변적으로 유지될 수 있도록 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;종료 조건(Base Case)이 필요한 이유&lt;/b&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;종료 조건(Base Case)이란 더 이상 재귀 함수를 호출하지 않고 값을 반환하는 조건을 의미한다. 재귀 함수는 함수가 호출 될 때마다 컴퓨터 메모리의 스택 영역에 함수의 실행 정보가 쌓이게 되는데, 종료 조건이 없다면 메모리가 꽉 차버리는 스택 오버플로우 에러가 발생하여 프로그램이 강제로 종료 될 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;재귀 함수의 장단점&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;while문이나 for문과 같은 반복문을 사용하지 않아도 되기 때문에 코드가 간결해진다.&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;size18&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;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;팩토리얼과 피보나치 수열&lt;/b&gt;&lt;/blockquote&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;팩토리얼 함수는 양의 정수 n에 대해 1부터 n까지의 정수를 모두 곱한 결과를 반환하는 함수이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;0! = 1 (종료 조건)&lt;/li&gt;
&lt;li&gt;n! = n * (n-1)! (재귀 조건)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781371775538&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def factorial(n):
    if n == 0:
        return 1
    
    return n * factorial(n-1)&lt;/code&gt;&lt;/pre&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;피보나치 수열이란 1, 1, 2, 3, 5, 8, 13, ... 처럼 앞의 두 항의 합이 다음 항이 되는 수열을 말한다.&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;F(1) = 1 (종료 조건)&lt;/li&gt;
&lt;li&gt;F(n) = F(n-1) + F(n-2) (재귀 조건)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781373324321&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def Fibonacci(n):
    if n &amp;lt;= 1:
        return n
    
    return Fibonacci(n-1) + Fibonacci(n-2)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자료구조, 알고리즘</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/50</guid>
      <comments>https://gudwns5533.tistory.com/50#entry50comment</comments>
      <pubDate>Sun, 14 Jun 2026 02:56:43 +0900</pubDate>
    </item>
    <item>
      <title>퀵 정렬 (Quick Sort)</title>
      <link>https://gudwns5533.tistory.com/49</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;퀵 정렬 알고리즘의 기본 개념&lt;/b&gt;&lt;/blockquote&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: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;병합 정렬과 다르게 퀵 정렬은 리스트를 비균등하게 분할한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;문제를 작은 2개의 문제로 분리하고 각각을 해결한 다음, 결과를 모아서 원래의 문제를 해결하는 전략&lt;/li&gt;
&lt;li&gt;분할 정복 방법은 대게 순환 호출을 이용하여 구현한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;리스트 안에 있는 한 원소를 선택한다. 이렇게 고른 원소를 피벗(pivot)이라고 한다.&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;분할된 부분 리스트에 대하여 재귀적 호출을 이용하여 정렬을 반복한다.&lt;/li&gt;
&lt;li&gt;부분 리스트에서도 다시 피벗을 정하고 피벗을 기준으로 2개의 부분 리스트로 나누는 과정을 반복한다.&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;리스트의 크기가 0이나 1이 될 때까지 반복한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;파이썬으로 코드 구현&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;코드 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781365679067&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def quicksort(data):
    if len(data) &amp;lt;= 1:
        return data
    
    pivot = data[len(data) // 2]   # 피벗값을 중앙값으로 잡기
    
    left = []   # 피벗값보다 작은 데이터는 피벗의 왼쪽으로 정렬
    for num in data:
        if num &amp;lt; pivot:
            left.append(num)
    
    mid = []    # 피벗과 같은값이 있으면 mid 리스트로 추가
    for num in data:
        if num == pivot:
            mid.append(num)

    right =[]   # 피벗값보다 큰 데이터는 피벗의 오른쪽으로 정렬
    for num in data:
        if num &amp;gt; pivot:
            right.append(num)

    return quicksort(left) + mid + quicksort(right)   # 이 과정을 재귀 호출로 반복&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;size18&quot;&gt;&lt;b&gt;알고리즘 테스트 1&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781365712294&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;data = [1, 9, 5, 8, 4, 6, 5, 3, 2, 7, 1, 9, 10]
print(quicksort(data))&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;size18&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781365784577&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[1, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10]&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;size18&quot;&gt;&lt;b&gt;알고리즘 테스트 2&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래의 네가지 데이터를 넣고 테스트도 진행해 보았다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: decimal; color: #000000;&quot;&gt;정렬이 끝난 배열&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal; color: #000000;&quot;&gt;역순으로 정렬된 배열&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal; color: #000000;&quot;&gt;원소가 전부 같은 배열&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal; color: #000000;&quot;&gt;랜덤으로 구성된 배열&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1781365861738&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import time

def quicksort(data):
    if len(data) &amp;lt;= 1:
        return data
    
    pivot = data[len(data) // 2]
    
    left = []
    for num in data:
        if num &amp;lt; pivot:
            left.append(num)
    
    mid = []
    for num in data:
        if num == pivot:
            mid.append(num)

    right =[]
    for num in data:
        if num &amp;gt; pivot:
            right.append(num)

    return quicksort(left) + mid + quicksort(right)
    
test1 = [1, 2, 3, 4, 5, 6, 7]
test2 = [7, 6, 5, 4, 3, 2, 1]
test3 = [1, 1, 1, 1, 1, 1, 1, 1]
test4 = [23, 15, 38, 94, 62, 123, 243, 234, 112]

start_time1 = time.time()
mergesort(test1)
end_time1 = time.time()
total_time1 = (end_time1 - start_time1) * 1000

start_time2 = time.time()
mergesort(test2)
end_time2 = time.time()
total_time2 = (end_time2 - start_time2) * 1000

start_time3 = time.time()
mergesort(test3)
end_time3 = time.time()
total_time3 = (end_time3 - start_time3) * 1000

start_time4 = time.time()
mergesort(test4)
end_time4 = time.time()
total_time4 = (end_time4 - start_time4) * 1000

print(f'{total_time1:.4f}ms')
print(f'{total_time2:.4f}ms')
print(f'{total_time3:.4f}ms')
print(f'{total_time4:.4f}ms')&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;size18&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781365895279&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;0.0532ms
0.0448ms
0.0448ms
0.0451ms&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;퀵 정렬의 장단점&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;시간 복잡도는 O(nlogn)&lt;/b&gt;이며, 다른 O(nlogn) 알고리즘에 비해 대체적으로 속도가 매우 빠르다. 시간 복잡도가 O(nlogn) 정렬 알고리즘 중 분할 정복 방식인 병합정렬에 비해 2배 정도 빠르게 테스트 결과가 나왔다.&lt;/li&gt;
&lt;li&gt;추가적인 별도의 메모리를 필요로 하지 않으며 재귀 호출 stack 프레임에 의한 &lt;b&gt;공간 복잡도는 logn&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;size18&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;특정 조건(정렬된 상태)에서 성능이 떨어진다(O(n^2))&lt;/li&gt;
&lt;li&gt;재귀를 사용하기 때문에 재귀를 사용하지 못하는 환경일 경우 그 구현이 매우 복잡해진다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>자료구조, 알고리즘</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/49</guid>
      <comments>https://gudwns5533.tistory.com/49#entry49comment</comments>
      <pubDate>Sun, 14 Jun 2026 00:58:31 +0900</pubDate>
    </item>
    <item>
      <title>병합 정렬 (Merge Sort)</title>
      <link>https://gudwns5533.tistory.com/48</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;병합 정렬 알고리즘의 기본 개념&lt;/b&gt;&lt;/blockquote&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: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;분할 정복 방법&lt;/b&gt;
&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;li&gt;분할 정복 방법은 대게 재귀적 호출을 이용하여 구현한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;알고리즘의 과정&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;리스트의 길이가 0 또는 1이면 이미 정렬된 것으로 본다.&lt;/li&gt;
&lt;li&gt;그렇지 않은 경우 정렬되지 않은 리스트를 절반으로 잘라 비슷한 크기의 두 부분 리스트로 나눈다.&lt;/li&gt;
&lt;li&gt;각 부분 리스트를 재귀적으로 병합 정렬을 이용해 정렬한다.&lt;/li&gt;
&lt;li&gt;두 부분 리스트를 다시 하나의 정렬된 리스트로 병합한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;파이썬으로 코드 구현&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;코드 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781349001925&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def mergesort(list):
    if len(list) &amp;lt;= 1:   # 배열의 길이가 1 이하면 리스트 배열
        return list
    
    mid = len(list)//2   # 리스트를 2등분한뒤 재귀 호출을 통해 왼쪽 배열과 오른쪽 배열을 정렬한다. 
    left = mergesort(list[:mid])
    right = mergesort(list[mid:])
    return merge(left, right)  # 정렬되어있는 왼쪽 배열과 오른쪽 배열을 병합한다.

def merge(left, right):
    result = []
    left_ = 0
    right_ = 0

    # 양쪽 배열의 값들을 비교하면서 작은값을 결과에 추가해 나간다.
    while left_ &amp;lt; len(left) and right_ &amp;lt; len(right): 
        if left[left_] &amp;lt;= right[right_]:
            result.append(left[left_])
            left_ += 1
        else:
            result.append(right[right_])
            right_ += 1

    result += left[left_:]
    result += right[right_:]
    return result&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;알고리즘 테스트 1&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781349132915&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;test = [14, 2, 644, 65, 12, 99, 9, 34, 23, 65]
print(mergesort(test))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781349238310&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[2, 9, 12, 14, 23, 34, 65, 65, 99, 644]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;알고리즘 테스트 2&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래의 네가지 데이터를 넣고 테스트도 진행해 보았다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: decimal; color: #000000;&quot;&gt;정렬이 끝난 배열&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal; color: #000000;&quot;&gt;역순으로 정렬된 배열&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal; color: #000000;&quot;&gt;원소가 전부 같은 배열&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal; color: #000000;&quot;&gt;랜덤으로 구성된 배열&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1781349312737&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import time

def mergesort(list):
    if len(list) &amp;lt;= 1:
        return list
    
    mid = len(list)//2
    left = mergesort(list[:mid])
    right = mergesort(list[mid:])
    return merge(left, right)

def merge(left, right):
    result = []
    left_ = 0
    right_ = 0

    while left_ &amp;lt; len(left) and right_ &amp;lt; len(right):
        if left[left_] &amp;lt;= right[right_]:
            result.append(left[left_])
            left_ += 1
        else:
            result.append(right[right_])
            right_ += 1

    result += left[left_:]
    result += right[right_:]
    return result

test1 = [1, 2, 3, 4, 5, 6, 7]
test2 = [7, 6, 5, 4, 3, 2, 1]
test3 = [1, 1, 1, 1, 1, 1, 1, 1]
test4 = [23, 15, 38, 94, 62, 123, 243, 234, 112]

start_time1 = time.time()
mergesort(test1)
end_time1 = time.time()
total_time1 = (end_time1 - start_time1) * 1000

start_time2 = time.time()
mergesort(test2)
end_time2 = time.time()
total_time2 = (end_time2 - start_time2) * 1000

start_time3 = time.time()
mergesort(test3)
end_time3 = time.time()
total_time3 = (end_time3 - start_time3) * 1000

start_time4 = time.time()
mergesort(test4)
end_time4 = time.time()
total_time4 = (end_time4 - start_time4) * 1000

print(f'{total_time1:.4f}ms')
print(f'{total_time2:.4f}ms')
print(f'{total_time3:.4f}ms')
print(f'{total_time4:.4f}ms')&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;size18&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781349341193&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;0.0923ms
0.0865ms
0.0889ms
0.0880ms&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;병합정렬의 장단점&lt;/b&gt;&lt;/blockquote&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;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;안정적인 성능 보장 : 최선, 최악, 평균 모든 경우에서 &lt;b&gt;시간 복잡도가 O(nlogn)&lt;/b&gt;으로 일정하다.&lt;/li&gt;
&lt;li&gt;안정 정렬(Stable Sort) : 정렬 후에도 값이 같은 데이터들의 원래 순서가 유지된다.&lt;/li&gt;
&lt;li&gt;연결 리스트(Linked List) 정렬에 최적화 : 데이터 이동이 많은 연결 리스트를 정렬할 때 퀵 정렬 대비 효율이 높다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;공간 복잡도가 O(n)&lt;/b&gt;이다. 데이터가 아주 크면 메모리 낭비가 심해질 수 있다.&lt;/li&gt;
&lt;li&gt;제자리 정렬 불가 : 메모리를 추가로 사용하지 않는 정렬 알고리즘에 비해 상대적으로 비효율적일 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>자료구조, 알고리즘</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/48</guid>
      <comments>https://gudwns5533.tistory.com/48#entry48comment</comments>
      <pubDate>Sat, 13 Jun 2026 20:26:54 +0900</pubDate>
    </item>
    <item>
      <title>HTML 과 CSS 기초 2</title>
      <link>https://gudwns5533.tistory.com/47</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;표&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;표 만들기 태그&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lt;table&amp;gt; : 표를 만드는 태그&lt;/li&gt;
&lt;li&gt;&amp;lt;tr&amp;gt; : 표의 행을 만드는 태그&lt;/li&gt;
&lt;li&gt;&amp;lt;td&amp;gt; : 표의 열을 만드는 태그&lt;/li&gt;
&lt;li&gt;&amp;lt;th&amp;gt; : 표의 제목 을 만드는 태그, 굵은 글씨로 가운데 정렬이된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781246298535&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;table&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;td colspan=&quot;2&quot;&amp;gt;1&amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;td&amp;gt;3&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;4&amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;td rowspan=&quot;2&quot;&amp;gt;5&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;6&amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;td&amp;gt;8&amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;&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;610&quot; data-origin-height=&quot;196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DdWkp/dJMcaglwtI3/gE8zEBYP061ZGkKHW2k3VK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DdWkp/dJMcaglwtI3/gE8zEBYP061ZGkKHW2k3VK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DdWkp/dJMcaglwtI3/gE8zEBYP061ZGkKHW2k3VK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDdWkp%2FdJMcaglwtI3%2FgE8zEBYP061ZGkKHW2k3VK%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;610&quot; height=&quot;196&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;196&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;colspan : 오른쪽에 있는 칸들을 합쳐서 가로로 길게 만든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781246453326&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;td colspan=&quot;2&quot;&amp;gt;&amp;lt;/td&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;rowspan : 아래쪽에 있는 칸들을 합쳐서 세로로 길게 만든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781246458070&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;td rowspan=&quot;2&quot;&amp;gt;&amp;lt;/td&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;시맨틱 구조&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 의미 없는 컨테이너 태그&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lt;div&amp;gt; : HTML에서 의미를 가지지 않는 블록 단위의 컨테이너 태그로, 여러 요소를 하나의 그룹으로 묶어 페이지의 구조를 나누거나 레이아웃을 구성할 때 사용&lt;/li&gt;
&lt;li&gt;&amp;lt;span&amp;gt; : HTML에서 특별한 의미나 레이아웃 기능 없이 텍스트의 일부분을 감싸는 인라인 컨테이너 태그&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 의미를 가지는 구조 태그 : 시맨틱 태그&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML에서 요소의 겉모양이 아니라 의미와 역할을 명확히 드러내는 태그로, 해당 영역이 어떤 목적을 가지는지 브라우저/검색엔진/접근성 도구가 이해할 수 있도록 돕는 태그를 말한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lt;header&amp;gt; : 페이지의 머리글 영역, 제목, 로고&lt;/li&gt;
&lt;li&gt;&amp;lt;nav&amp;gt; : 메뉴, 링크 목록&lt;/li&gt;
&lt;li&gt;&amp;lt;main&amp;gt; : 페이지에서 가장 핵심이 되는 주요 내용&lt;/li&gt;
&lt;li&gt;&amp;lt;section&amp;gt; : 주제별로 나눈 컨텐츠 영역&lt;/li&gt;
&lt;li&gt;&amp;lt;article&amp;gt; : 게시글, 블로그처럼 독립적으로 의미를 가지는 컨텐츠&lt;/li&gt;
&lt;li&gt;&amp;lt;aside&amp;gt; : 본문과 간접적 또는 관련없는 사이드 컨텐츠&lt;/li&gt;
&lt;li&gt;&amp;lt;footer&amp;gt; : 저작권, 작성자 정보, 하단 정보 영역&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;박스 모델&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;박스모델은 HTML 요소 하나하나를 사각형 상자로 보고 내용(content), 안쪽 여백(padding), 테두리(border), 바깥 여백(margin)으로 구성된 구조로 이해하는 개념이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  브라우저는 위 네 영역을 합쳐 요소의 실제 크기와 화면에서 차지하는 공간을 계산한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;CSS 활용&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;안쪽 여백 (padding)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;박스 모델에서 내용과 테두리 사이의 안쪽 여백을 의미하며, 요소 내부의 컨텐츠가 테두리에 너무 붙지 않도록 공간을 만들어 가독성과 디자인 완성도를 높인다.&lt;/p&gt;
&lt;pre id=&quot;code_1781248832423&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;h1 { padding: 20px; } /* 네 방향 모두 */
h1 { padding: 10px 20px; } /* 상하, 좌우 */
h1 { padding: 10px 20px 30px; } /* 상, 좌우, 하 */
h1 { padding: 10px 20px 30px 40px; } /* 상, 우, 하, 좌(시계 방향) */&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;테두리 (border)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;박스 모델에서 요소의 테두리 영역을 의미하며, padding과 margin 사이에 위치해 요소의 경계를 시각적으로 구분해 주는 역할을 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1781248944111&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;border-width: 테두리 두께
border-style: 테두리 형태 (solid, dashed, dotted 등)
border-color: 테두리 색상
border: 위 세 속성을 한 번에 지정하는 축약 속성
border-radius: 모서리를 둥글게 처리&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;바깥 여백 (margin)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;박스 모델에서 요소의 바깥쪽 여백을 의미하며, 요소와 요소 사이의 간격을 조절하는데 사용한다. 위 아래 마진은 상황에 따라 마진 병합(마진이 더 큰 쪽으로 합쳐짐)이 발생할 수 있어 레이아웃 이해에 중요하다.&lt;/p&gt;
&lt;pre id=&quot;code_1781249193167&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;h1 { margin: 20px; } /* 네 방향 모두 */
h1 { margin: 10px 20px; } /* 상하, 좌우 */
h1 { margin: 10px 20px 30px; } /* 상, 좌우, 하 */
h1 { margin: 10px 20px 30px 40px; } /* 상, 우, 하, 좌(시계 방향) */
h1 { margin: 0 auto; } /* 가로 가운데 정렬 */&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;box-sizing&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS에서 요소의 너비와 높이를 어떤 기준으로 계산할지 결정하는 속성으로, 박스 모델의 크기와 계산 방식을 제어한다.&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;contect-box&lt;/b&gt; : width와 height에 내용(content)만 포함하고 padding과 border를 바깥으로 더함. (실제 크기와 맞지않음)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;border-box&lt;/b&gt; : padding과 border를 너비/높이에 포함시켜 요소의 실제 크기를 예측하기 쉽게 만듦. (더 많이 사용)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;float&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;float는 CSS에서 요소를 문서의 일반적인 흐름에서 살짝 분리해 왼쪽이나 오른쪽으로 띄우는 속성으로, 주로 이미지 옆에 텍스트를 배치하거나 과거에는 다단 레이아웃을 구성하는 데 사용되었다.&lt;/p&gt;
&lt;pre id=&quot;code_1781249855111&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;style&amp;gt;
    img {
        float: left;
        margin-right: 20px;
    }
&amp;lt;/style&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1781249875119&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    &amp;lt;img src=&quot;./spring1.png&quot; alt=&quot;봄&quot;&amp;gt;Lorem, ipsum dolor sit amet consectetur adipisicing elit.
    Omnis voluptate adipisci ex aperiam praesentium error, expedita impedit, 
    repudiandae libero aspernatur illo ratione ut explicabo quasi. 
    Quidem neque rem ut reprehenderit!&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;914&quot; data-origin-height=&quot;161&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjZ5ZO/dJMcadh40iJ/KvYrz1RLhi1VBZr6bO3Dl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjZ5ZO/dJMcadh40iJ/KvYrz1RLhi1VBZr6bO3Dl1/img.png&quot; data-alt=&quot;float 사용전 (이미지의 bottom에 텍스트가 붙어서 출력되는걸 알 수 있다.)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjZ5ZO/dJMcadh40iJ/KvYrz1RLhi1VBZr6bO3Dl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjZ5ZO%2FdJMcadh40iJ%2FKvYrz1RLhi1VBZr6bO3Dl1%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;914&quot; height=&quot;161&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;161&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;float 사용전 (이미지의 bottom에 텍스트가 붙어서 출력되는걸 알 수 있다.)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;128&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5cXuL/dJMcaf1cHSJ/b25lgpjUCg1H8F3X6KsKXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5cXuL/dJMcaf1cHSJ/b25lgpjUCg1H8F3X6KsKXk/img.png&quot; data-alt=&quot;float 사용 후 (텍스트가 이미지 바로 옆에 배치되었지만 만약 아래 컨텐츠가 더 있었다면 같이 올라 가니 주의해야함)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5cXuL/dJMcaf1cHSJ/b25lgpjUCg1H8F3X6KsKXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5cXuL%2FdJMcaf1cHSJ%2Fb25lgpjUCg1H8F3X6KsKXk%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;948&quot; height=&quot;128&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;128&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;float 사용 후 (텍스트가 이미지 바로 옆에 배치되었지만 만약 아래 컨텐츠가 더 있었다면 같이 올라 가니 주의해야함)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;clear&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;clear 속성은 float가 적용된 요소로 인해 흐트러진 레이아웃을 정리하기 위해 사용하는 CSS 속성으로, 앞에 떠 있는 (float된) 요소의 영향을 받아 나란히 배치되지 않도록 하고 그 아래로 내려가게 만드는 역할을 함. float가 적용된 요소들의 흐름이 끝나는 지점에 빈 태그 &amp;lt;div&amp;gt;를 하나 넣고 아래와 같이 스타일을 부여한다&lt;/p&gt;
&lt;pre id=&quot;code_1781250437248&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.clear { clear: both; }&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;텍스트 스타일&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;font-family&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 텍스트에 사용할 글꼴의 우선순위 목록을 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 앞의 글꼴이 없으면 다음 글꼴로 대체된다.&lt;/p&gt;
&lt;pre id=&quot;code_1781250829695&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;h1 { family: &quot;Noto Sans KR&quot;, Arial, sans-serif }&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;웹폰트&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자의 컴퓨터에 해당 글꼴이 설치되어 있지 않아도, 웹페이지가 서버에서 글꼴 파일을 직접 내려받아 동일한 글꼴로 표시해 주는 기술을 말한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;CSS 텍스트 속성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;font-size&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 글자의 크기를 지정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- px, em, rem, % 등 단위 사용&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;px : 화면의 고정된 크기 단위, 지정한 값이 그대로 적용&lt;/li&gt;
&lt;li&gt;em : 부모 요소의 글자 크기를 기준으로 계상되는 상대 단위. 부모가 16px이면 1em = 16px, 1.5em = 24px이 됨.&lt;/li&gt;
&lt;li&gt;rem : 문서 최상위 요소(HTML)의 글자 크기를 기준으로 계산&lt;/li&gt;
&lt;li&gt;% : 기준 요소 대비 비율로 크기를 지정. 글자 크기에서는 보통 부모의 글자 크기를 기준으로 계산되며, 레이아웃에서는 부모의 크기 대비 비율로 동작&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1781251491599&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;h1 { font-size: 20px; }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;font-weight&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;- normal, bold 또는 숫자 (100~900) 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실제 지원 여부는 글꼴에 따라 다름&lt;/p&gt;
&lt;pre id=&quot;code_1781252571679&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p { font-weight: 700; } /* bold */&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;font-style&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 글자를 기울일지 여부 설정&lt;/p&gt;
&lt;pre id=&quot;code_1781252587751&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p { font-style: italic; }&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;size18&quot;&gt;&lt;b&gt;for-variant&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 소문자를 작은 대문자 형태로 표시&lt;/p&gt;
&lt;pre id=&quot;code_1781252603919&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p { font-variant: small-caps }&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;size18&quot;&gt;&lt;b&gt;line-height&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 줄과 줄 사이의 간격을 지정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 가독성에 매우 큰 영향&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 숫자만 쓰면 글자 크기의 배수로 계산됨&lt;/p&gt;
&lt;pre id=&quot;code_1781252654663&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p { line-height: 1.6; }&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;size18&quot;&gt;&lt;b&gt;letter-spacing&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 글자와 글자 사이의 간격 조절&lt;/p&gt;
&lt;pre id=&quot;code_1781252665847&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p { letter-spacing: 1px; }&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;size18&quot;&gt;&lt;b&gt;word-spacing&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 단어 사이의 간격을 조절&lt;/p&gt;
&lt;pre id=&quot;code_1781252677839&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p { word-spacing: 4px; }&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;size18&quot;&gt;&lt;b&gt;text-decoration&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 밑줄, none, underline, overline 등 장식효과&lt;/p&gt;
&lt;pre id=&quot;code_1781252689232&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p { text-decoration: underline; }&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;position&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;position은 HTML 요소로 문서의 기본 흐름에서 어떻게 배치할지 결정하는 CSS 속성으로, 요소의 위치 기준과 이동 방식을 제어한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. position: static;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 HTML 요소의 기본 위치 지정 방식으로, 요소가 문서의 일반적인 흐름에 따라 위에서 아래, 왼쪽에서 오른쪽 순서대로 배치된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. position: relative;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요소를 문서의 일반적인 흐름에 두면서, top / left / right / bottom 속성을 이용해 자기 자신의 원래 위치를 기준으로 이동시키는 위치 지정 방식이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. position: fixed;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요소를 브라우저 화면(뷰포트)을 기준으로 고정시키는 위치 지정 방식으로, 스크롤을 내려도 해당 요소가 항상 같은 위치에 머무는 특징을 가지는 방식이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. position: absolute;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요소를 문서의 일반 흐름에서 완전히 제거한 뒤, 가장 가까운 position이 지정된 부모 요소 (relative, absolute, fixed 중 하나)를 기준으로 위치를 잡는 방식. 만약 해당 부모가 없다면 브라우저 화면을 기준으로 배치되며, top / left / right / bottom 값을 이용해 정확한 위치를 지정할 수 있어 아이콘 겹치기나 특정 요소 위에 배치해야 할 UI를 만들 때 사용된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;z-index&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS에서 position을 쓰다 보면 요소들이 화면에서 겹치는 상황이 발생하는데 이때 어떤 요소를 위에 표시할지 결정하는 속성으로, 값이 클수록 더 위에 배치된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>HTML,CSS,JavaScript</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/47</guid>
      <comments>https://gudwns5533.tistory.com/47#entry47comment</comments>
      <pubDate>Fri, 12 Jun 2026 17:35:57 +0900</pubDate>
    </item>
    <item>
      <title>HTML 과 CSS 기초 1</title>
      <link>https://gudwns5533.tistory.com/46</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;HTML의 기본 구조&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML(HyperText Markup Language)은 웹페이지의 구조와 컨텐츠를 정의하는 표준 마크업 언어이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹의 기본 뼈대인 HTML과 함께 CSS(디자인) 및 JavaScript(기능)를 결합하여 완전한 웹사이트를 완성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1781154621247&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- html version5를 의미, 첫줄에서 html의 버전을 알 수 있다 --&amp;gt;
&amp;lt;!-- (태그 속성)식으로 코드 작성 --&amp;gt;
&amp;lt;!DOCTYPE html&amp;gt;

&amp;lt;!-- 언어정보: 시각장애인을 위한 언어를 설정해줄 수 있다 --&amp;gt;
&amp;lt;html lang=&quot;ko&quot;&amp;gt;

&amp;lt;!-- head: 문서 정보 --&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;!-- meta: 정보를 세팅 --&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt; 
    &amp;lt;!-- 반응형 화면을 위한 정보 --&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;
    &amp;lt;!-- 제목표시줄에 들어가는 정보 --&amp;gt;
    &amp;lt;title&amp;gt;HTML&amp;lt;/title&amp;gt; 
&amp;lt;/head&amp;gt;

&amp;lt;!-- body: 실제 화면에 나오는 내용 --&amp;gt;
&amp;lt;body&amp;gt;            
    &amp;lt;!--
        HTML 주석문
        작성일: 2026-06-10
        작성자: 나
    --&amp;gt;
    HTML 이란?
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

&amp;lt;!-- Live server 여는 법: Alt+L+O --&amp;gt;
&amp;lt;!-- 주석은 소스코드보기로 모두 보이므로 중요한 정보는 주석에 넣으면 안된다 --&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;HTML의 공간 개념 : 박스 모델&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1781163122588&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;+---------------------------------------------------------+
|  Margin (바깥쪽 여백: 이웃한 다른 요소와의 간격)           |
|   +-------------------------------------------------+   |
|   |  Border (테두리: 눈에 보이는 외곽선)              |   |
|   |   +-----------------------------------------+   |   |
|   |   |  Padding (안쪽 여백: 내용과 테두리 사이)  |   |   |
|   |   |   +---------------------------------+   |   |   |
|   |   |   |  Content (내용: 텍스트나 이미지)  |   |   |   |
|   |   |   +---------------------------------+   |   |   |
|   |   +-----------------------------------------+   |   |
|   +-------------------------------------------------+   |
+---------------------------------------------------------+&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;content : 내용이 나오는 공간&lt;/li&gt;
&lt;li&gt;padding : 내용과 테두리 사이의 여백&lt;/li&gt;
&lt;li&gt;border : 내용에 대한 테두리 공간&lt;/li&gt;
&lt;li&gt;margin : 여백 공간&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;블록 태그와 인라인 태그&lt;/b&gt;&lt;/h2&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;텍스트 태그&lt;/b&gt;&lt;/h2&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;&amp;lt;h1&amp;gt;부터 &amp;lt;h6&amp;gt;까지 있으며, 숫자가 작을수록 상위제목이라 생각하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1781163870491&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;h1&amp;gt;안녕하세요. Heading&amp;lt;/h1&amp;gt;
&amp;lt;h2&amp;gt;안녕하세요. Heading&amp;lt;/h2&amp;gt;
&amp;lt;h3&amp;gt;안녕하세요. Heading&amp;lt;/h3&amp;gt;
&amp;lt;h4&amp;gt;안녕하세요. Heading&amp;lt;/h4&amp;gt;
&amp;lt;h5&amp;gt;안녕하세요. Heading&amp;lt;/h5&amp;gt;
&amp;lt;h6&amp;gt;안녕하세요. Heading&amp;lt;/h6&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제목 태그는 블록 태그이기 때문에 옆에 붙여 쓰더라도 줄 바꿈이 돼서 아래로 내려간다.&lt;/p&gt;
&lt;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;&amp;lt;p&amp;gt;태그를 사용하고, 하나의 독립된 문단을 만든다. (블록 태그)&lt;/p&gt;
&lt;pre id=&quot;code_1781163960747&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; &amp;lt;p&amp;gt;문단을 만드는 태그&amp;lt;/p&amp;gt;일반 텍스트&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;서식 태그&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1781163986924&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;굵게&amp;lt;/b&amp;gt; 표현하기&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;굵게&amp;lt;/strong&amp;gt; 표현하기&amp;lt;/p&amp;gt; &amp;lt;!-- strong태그를 쓰면 장애인 음성지원시 더 강하게 읽어줌 --&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;i&amp;gt;이탤릭체&amp;lt;/i&amp;gt; 표현하기&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;이탤릭체&amp;lt;/em&amp;gt; 표현하기&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;ins&amp;gt;밑줄&amp;lt;/ins&amp;gt; 표현하기&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;del&amp;gt;삭제&amp;lt;/del&amp;gt; 표현하기&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;x&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; + y&amp;lt;sup&amp;gt;3&amp;lt;/sup&amp;gt; = z&amp;lt;/p&amp;gt; &amp;lt;!-- 윗첨자 --&amp;gt;
&amp;lt;p&amp;gt;H&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;O&amp;lt;/p&amp;gt; &amp;lt;!-- 아랫첨자 --&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lt;b&amp;gt; : 굵게 표시&lt;/li&gt;
&lt;li&gt;&amp;lt;strong&amp;gt; : 굵게 표시 + 강조(스크린 리더에서 더 강하게 읽어준다)&lt;/li&gt;
&lt;li&gt;&amp;lt;i&amp;gt; : 이탤릭체&lt;/li&gt;
&lt;li&gt;&amp;lt;em&amp;gt; : 이탤릭체 + 강조(strong태그와 동일한 역할)&lt;/li&gt;
&lt;li&gt;&amp;lt;ins&amp;gt; : 밑줄 추가&lt;/li&gt;
&lt;li&gt;&amp;lt;del&amp;gt; : 취소선 추가&lt;/li&gt;
&lt;li&gt;&amp;lt;sup&amp;gt; : 윗첨자&lt;/li&gt;
&lt;li&gt;&amp;lt;sub&amp;gt; : 아랫첨자&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;목록 태그&lt;/b&gt;&lt;/h2&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;&amp;lt;ul&amp;gt; 태그 안에 각 항목을 &amp;lt;li&amp;gt; 태그로 감싸서 사용&lt;/p&gt;
&lt;pre id=&quot;code_1781164661315&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;ul&amp;gt;
    &amp;lt;li&amp;gt;김사과&amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;오렌지&amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;반하나&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;83&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjBOnm/dJMcag6S1Y2/1DbWamP421a9ziDHc0k2D1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjBOnm/dJMcag6S1Y2/1DbWamP421a9ziDHc0k2D1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjBOnm/dJMcag6S1Y2/1DbWamP421a9ziDHc0k2D1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjBOnm%2FdJMcag6S1Y2%2F1DbWamP421a9ziDHc0k2D1%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;83&quot; height=&quot;73&quot; data-origin-width=&quot;83&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;&amp;lt;ol&amp;gt;태그 안에 각 항목을 &amp;lt;li&amp;gt; 태그로 감싸서 사용&lt;/p&gt;
&lt;pre id=&quot;code_1781165588746&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;ol&amp;gt;
    &amp;lt;li&amp;gt;김사과&amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;오렌지&amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;반하나&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;86&quot; data-origin-height=&quot;75&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EwoT9/dJMcadh3YOX/tLQeW9gYxpbAaf5u1AmsV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EwoT9/dJMcadh3YOX/tLQeW9gYxpbAaf5u1AmsV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EwoT9/dJMcadh3YOX/tLQeW9gYxpbAaf5u1AmsV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEwoT9%2FdJMcadh3YOX%2FtLQeW9gYxpbAaf5u1AmsV1%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;86&quot; height=&quot;75&quot; data-origin-width=&quot;86&quot; data-origin-height=&quot;75&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;&amp;lt;dl&amp;gt; : 설명 목록의 시작&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;dt&amp;gt; : 제목&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;dd&amp;gt; : 상세 설명 (기본적으로 들여쓰기 적용)&lt;/p&gt;
&lt;pre id=&quot;code_1781165785634&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;dl&amp;gt;
    &amp;lt;dt&amp;gt;류정원 선생님&amp;lt;/dt&amp;gt;
    &amp;lt;dd&amp;gt;김사과&amp;lt;/dd&amp;gt;
    &amp;lt;dd&amp;gt;오렌지&amp;lt;/dd&amp;gt;
    &amp;lt;dd&amp;gt;반하나&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;112&quot; data-origin-height=&quot;98&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Aj0BY/dJMcabLmuSj/ivNLMI3sMHk1BEJBMkDu21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Aj0BY/dJMcabLmuSj/ivNLMI3sMHk1BEJBMkDu21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Aj0BY/dJMcabLmuSj/ivNLMI3sMHk1BEJBMkDu21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAj0BY%2FdJMcabLmuSj%2FivNLMI3sMHk1BEJBMkDu21%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;112&quot; height=&quot;98&quot; data-origin-width=&quot;112&quot; data-origin-height=&quot;98&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;이미지 태그&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML에서 이미지를 넣고 싶을 땐 &amp;lt;img&amp;gt;태그를 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1781165967873&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;img src=&quot;이미지_경로&quot; alt=&quot;대체_텍스트&quot;&amp;gt;&lt;/code&gt;&lt;/pre&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;&lt;b&gt;절대경로&lt;/b&gt; : 이미지의 고유한 전체 주소 (디렉토리상의 주소나, 웹상의 URL)&lt;/p&gt;
&lt;pre id=&quot;code_1781166311881&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;img src=&quot;C:\SHJ\4_HTML_CSS\spring1.png&quot; alt=&quot;봄&quot;&amp;gt;
&amp;lt;img src=&quot;https://tcpschool.com/img/logo.png&quot; alt=&quot;TCP스쿨 로고&quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;절대경로에 디렉토리상의 주소를 사용하게 되면 서버에 올렸을 때 다른 사람들은 이미지를 볼 수 없다.&lt;/li&gt;
&lt;li&gt;인터넷에 공개된 이미지는 웹주소를 통해 넣어도 되지만, 그 이미지가 그 주소상에서 사라졌을 때는 이미지 표시가 안된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상대경로&lt;/b&gt; : 작성중인 HTML 파일을 기준으로 이미지의 위치를 찾아가는 방식&lt;/p&gt;
&lt;pre id=&quot;code_1781166539584&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;img src=&quot;spring1.png&quot; alt=&quot;봄&quot;&amp;gt;
&amp;lt;img src=&quot;images/spring2.png&quot; alt=&quot;봄2&quot;&amp;gt;&lt;/code&gt;&lt;/pre&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;웹에서 사용 가능한 주요 이미지 포맷들은 다음과 같다&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 105px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style7&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.4186%; text-align: center; height: 17px;&quot;&gt;포맷&lt;/td&gt;
&lt;td style=&quot;width: 85.5814%; height: 17px; text-align: center;&quot;&gt;특징&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 14.4186%; text-align: center; height: 20px;&quot;&gt;PNG&lt;/td&gt;
&lt;td style=&quot;width: 85.5814%; height: 20px;&quot;&gt;투명도 표현이 가능하고, 화질 손실이 없다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.4186%; text-align: center; height: 17px;&quot;&gt;JPEG / JPG&lt;/td&gt;
&lt;td style=&quot;width: 85.5814%; height: 17px;&quot;&gt;압축률이 우수하여 용량이 작다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.4186%; text-align: center; height: 17px;&quot;&gt;GIF&lt;/td&gt;
&lt;td style=&quot;width: 85.5814%; height: 17px;&quot;&gt;여러 프레임을 합쳐 움짤을 만들 수 있지만, 해상도가 낮다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.4186%; text-align: center; height: 17px;&quot;&gt;WebP&lt;/td&gt;
&lt;td style=&quot;width: 85.5814%; height: 17px;&quot;&gt;JPG, PNG, GIF의 장점을 모두 합친 포맷, 하지만 아직 호환성이 떨어진다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.4186%; text-align: center; height: 17px;&quot;&gt;SVG&lt;/td&gt;
&lt;td style=&quot;width: 85.5814%; height: 17px;&quot;&gt;코드로 이루어진 벡터 이미지로, 확대해도 깨지지 않는다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;하이퍼링크 태그&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML에서 링크를 만들 때는 &amp;lt;a&amp;gt; 태그를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1781167158200&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;a href=&quot;이동할_주소&quot;&amp;gt;클릭할_텍스트_또는_이미지&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/pre&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;&lt;b&gt;절대 경로&lt;/b&gt; : 인터넷상의 고유한 전체 URL 주소를 그대로 적어준다.&lt;/p&gt;
&lt;pre id=&quot;code_1781167287288&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;a href=&quot;https://www.naver.com&quot;&amp;gt;네이버&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;https://tcpschool.com&quot;&amp;gt;&amp;lt;img src=&quot;https://tcpschool.com/img/logo.png&quot; alt=&quot;TCP스쿨 로고&quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상대 경로&lt;/b&gt; : 현재 HTML 파일이 위치한 곳을 기준으로 다른 파일의 위치를 찾아 연결해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1781167334360&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; &amp;lt;a href=&quot;./4_이미지.html&quot;&amp;gt;이미지 페이지&amp;lt;/a&amp;gt;
 &amp;lt;a href=&quot;./pages/subpage.html&quot;&amp;gt;서브페이지&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CSS&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS(Cascading Style Sheets)는 HTML과 함께 사용되며 웹 페이지의 스타일과 레이아웃을 정의하는 스타일시트 언어.&lt;/p&gt;
&lt;pre id=&quot;code_1781167617695&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;선택자 { 
    속성: 값; 
    속성: 값;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;선택자 : 스타일을 적용할 HTML 요소를 선택.&lt;/li&gt;
&lt;li&gt;속성 : 바꾸고 싶은 디자인 항목.&lt;/li&gt;
&lt;li&gt;값 : 속성에 부여할 구체적인 스타일 내용.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CSS 스타일 적용 방법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 인라인 스타일 : HTML 태그 안에 직접 style 속성으로 CSS를 작성하는 방식&lt;/p&gt;
&lt;pre id=&quot;code_1781168059935&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;p style=&quot;color: gold;&quot;&amp;gt;안녕하세요. css입니다.&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 내부 스타일 : &amp;lt;style&amp;gt; 태그를 사용하여 CSS를 작성하는 방식&lt;/p&gt;
&lt;pre id=&quot;code_1781168068895&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;style&amp;gt;
    body { background: deepskyblue; }
    h1 { color: white; text-align: center; }
&amp;lt;/style&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 외부 스타일 : CSS 코드를 별도의 .css 파일에 작성하고, HTML문서에서 link 태그로 연결하는 방식&lt;/p&gt;
&lt;pre id=&quot;code_1781168795238&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;./css/test.css&quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  CSS 스타일 적용 우선순위&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 인라인 스타일&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 내부 스타일시트 / 외부 스타일시트 (우선순위가 같으며, HTML 코드 상 더 아래쪽에 쓴 것이 최종 반영됨)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CSS 선택자&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 태그 선택자&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 HTML 태그 전체에 스타일을 적용.&lt;/p&gt;
&lt;pre id=&quot;code_1781169452525&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;h1 {
    color: deepskyblue;
    text-align: center;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 클래스 선택자&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML 태그에 클래스를 붙여서 그룹으로 꾸밀 때 사용. 이름 앞에 ( . ) 을 붙인다.&lt;/p&gt;
&lt;pre id=&quot;code_1781169617341&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.intro {
    font-weight: bold;
    color: deeppink;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 아이디 선택자&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단 하나밖에 없는 요소에 고유한 이름을 붙여 꾸밀 때 사용. 이름 앞에 ( # ) 을 붙인다.&lt;/p&gt;
&lt;pre id=&quot;code_1781169703645&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#favorite-list {
    background-color: ivory;
    padding: 10px;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. 그룹 선택자&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 선택자에 똑같은 스타일을 한 번에 적용하고 싶을 때 ( , ) 로 연결해서 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1781170044892&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p,
li {
    font-size: 20px;
    /* pc기본 글꼴 사이즈는 16px */
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5. 의사 클래스 선택자&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요소의 특정한 '상태'에 스타일을 부여한다. ( :hover ) 는 마우스를 올렸을 때 작동한다.&lt;/p&gt;
&lt;pre id=&quot;code_1781170166636&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;a:hover {
    color: yellowgreen;
    text-decoration: none;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;6. 속성 선택자&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML 태그가 가진 특정 속성과 그 값을 확인해서 일치하는 요소만 선택한다.&lt;/p&gt;
&lt;pre id=&quot;code_1781170206340&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;h1[title=&quot;제목&quot;] {
    background-color: ivory;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;7. 자식 선택자&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모 바로 밑에 있는 직계 자식만 선택한다.&lt;/p&gt;
&lt;pre id=&quot;code_1781170251788&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ul#favorite-list&amp;gt;li {
    list-style-type: square;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;8. 자손 선택자(자식 포함)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택자 사이에 공백을 주면, 자식뿐 아니라 그 아래 자손까지 선택한다.&lt;/p&gt;
&lt;pre id=&quot;code_1781170295508&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ul#favorite-list p {
    color: violet;
    font-weight: bold;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;9. 형제 선택자&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모가 같은 형제들 중, 인접한 형제를 선택한다.( + ) 사용. 모든 형제 선택 시 ( ~ )&amp;nbsp; 사용.&lt;/p&gt;
&lt;pre id=&quot;code_1781170388772&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p.intro+p {
    background-color: aqua;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>HTML,CSS,JavaScript</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/46</guid>
      <comments>https://gudwns5533.tistory.com/46#entry46comment</comments>
      <pubDate>Thu, 11 Jun 2026 18:33:50 +0900</pubDate>
    </item>
    <item>
      <title>버블 정렬 vs 선택 정렬 vs 삽입 정렬 성능 테스트 코드</title>
      <link>https://gudwns5533.tistory.com/45</link>
      <description>&lt;pre id=&quot;code_1781104876340&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import random
import time

def bubblesort(list, N):
    for i in range(N-1):
        for j in range(N-1-i):
            if list[j] &amp;lt;= list[j+1]:
                continue
            else:
                list[j], list[j+1] = list[j+1], list[j]

def selectionsort(list):
    for i in range(len(list)):
        min_index = i

        for j in range(i+1, len(list)):
            if list[min_index] &amp;gt; list[j]:
                min_index = j
    
        list[i], list[min_index] = list[min_index], list[i]

def insertionsort(list):
    for i in range(1, len(list)):
        for j in range(i, 0, -1):
            if list[j] &amp;lt; list[j - 1]:
                list[j], list[j - 1] = list[j - 1], list[j]
            else:
                break

random.seed(50)
data = [random.randint(1, 1000000) for _ in range(10000)]
start_time = time.time()
bubblesort(data, len(data))
end_time = time.time()
total_time = end_time - start_time
print(f'{total_time:.4f}초')
print(data)

random.seed(50)
data = [random.randint(1, 1000000) for _ in range(10000)]
start_time = time.time()
selectionsort(data)
end_time = time.time()
total_time = end_time - start_time
print(f'{total_time:.4f}초')
print(data)

random.seed(50)
data = [random.randint(1, 1000000) for _ in range(10000)]
start_time = time.time()
insertionsort(data)
end_time = time.time()
total_time = end_time - start_time
print(f'{total_time:.4f}초')
print(data)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결과값&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1781104900633&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1차
4.1660초
1.8248초
2.8128초

# 2차
4.2410초
1.8557초
2.7622초

# 3차
4.2034초
1.8522초
2.8110초

# 4차
4.1582초
1.8763초
2.7562초

# 5차
4.2101초
1.8806초
2.7803초&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자료구조, 알고리즘</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/45</guid>
      <comments>https://gudwns5533.tistory.com/45#entry45comment</comments>
      <pubDate>Thu, 11 Jun 2026 00:21:59 +0900</pubDate>
    </item>
    <item>
      <title>삽입 정렬 (Insertion Sort)</title>
      <link>https://gudwns5533.tistory.com/44</link>
      <description>&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자료 배열의 모든 요소를 앞에서부터 차례대로 이미 정렬된 배열 부분과 비교하여, 자신의 위치를 찾아 삽입함으로써 정렬을 완성하는 알고리즘.&lt;/li&gt;
&lt;li&gt;매 순서마다 해당 원소를 삽입할 수 있는 위치를 찾아 해당 위치에 넣는다.&lt;/li&gt;
&lt;li&gt;알고리즘의 과정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;처음의 탐색 시작 값은 두 번째 자료부터 시작한다.&lt;/li&gt;
&lt;li&gt;두 번째 자료부터 시작하여 그 앞의 자료들과 비교하여 삽입할 위치를 지정한 후, 자료를 뒤로 옮기고 지정한 자리에 자료를 삽입하여 정리.&lt;/li&gt;
&lt;li&gt;두 번째 자료는 첫 번째 자료, 세 번째 자료는 두 번째 자료와 첫 번째 자료, 네 번째 자료는 세 번째 자료와 두 번째, 첫 번째 자료, ... 이렇게 비교를 한 후 자료가 삽입될 위치를 찾는다&lt;/li&gt;
&lt;li&gt;자료의 삽입 위치를 찾았다면 그 위치에 자료를 삽입하기 위해 다른 자료를 한 칸씩 뒤로 이동시킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;파이썬으로 코드 구현&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;코드 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781099892687&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def insertionsort(list):
    for i in range(1, len(list)): # 두 번째 자리부터 탐색을 시작
        for j in range(i, 0, -1): # 인덱스 i부터 1까지 역순으로 탐색 진행
            if list[j] &amp;lt; list[j - 1]:  # 현재 원소가 왼쪽의 원소보다 더 작은지 확인
                list[j], list[j - 1] = list[j - 1], list[j] # 정렬을 위해 더 작은 원소를 왼쪽으로 위치 교환
            else:  # 현재 원소가 왼쪽의 원소보다 크다면 다음 회차로 바로 진행
                break&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;size18&quot;&gt;&lt;b&gt;알고리즘 테스트&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781100046003&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;test = [3, 0, 11, 8, 3, 16, 1, 8, 4, 7]
insertionsort(test)
print(test)&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;size18&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781100083667&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[0, 1, 3, 3, 4, 7, 8, 8, 11, 16]&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;알고리즘 테스트 2&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래의 네가지 데이터를 넣고 테스트도 진행해 보았다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: decimal; color: #000000;&quot;&gt;정렬이 끝난 배열&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal; color: #000000;&quot;&gt;역순으로 정렬된 배열&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal; color: #000000;&quot;&gt;원소가 전부 같은 배열&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal; color: #000000;&quot;&gt;랜덤으로 구성된 배열&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1781102910727&quot; class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import time

def insertionsort(list):
    for i in range(1, len(list)):
        for j in range(i, 0, -1):
            if list[j] &amp;lt; list[j - 1]:
                list[j], list[j - 1] = list[j - 1], list[j]
            else:
                break

test1 = [1, 2, 3, 4, 5, 6, 7]
test2 = [7, 6, 5, 4, 3, 2, 1]
test3 = [1, 1, 1, 1, 1, 1, 1, 1]
test4 = [23, 15, 38, 94, 62, 123, 243, 234, 112]

start_time1 = time.time()
selectionsort(test1)
end_time1 = time.time()
total_time1 = (end_time1 - start_time1) * 100

start_time2 = time.time()
selectionsort(test2)
end_time2 = time.time()
total_time2 = (end_time2 - start_time2) * 1000

start_time3 = time.time()
selectionsort(test3)
end_time3 = time.time()
total_time3 = (end_time3 - start_time3) * 1000

start_time4 = time.time()
selectionsort(test4)
end_time4 = time.time()
total_time4 = (end_time4 - start_time4) * 1000

print(f'{total_time1:.4f}ms')
print(f'{total_time2:.4f}ms')
print(f'{total_time3:.4f}ms')
print(f'{total_time4:.4f}ms')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781102948331&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;0.0077ms
0.0372ms
0.0362ms
0.0370ms&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;random 데이터로 성능 테스트&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781100145965&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import random
import time

def insertionsort(list):
    for i in range(1, len(list)):
        for j in range(i, 0, -1):
            if list[j] &amp;lt; list[j - 1]:
                list[j], list[j - 1] = list[j - 1], list[j]
            else:
                break

data = [random.randint(1, 1000000) for _ in range(1000)]
start_time = time.time()
insertionsort(data)
print(data)
end_time = time.time()
total_time = end_time - start_time
print(f'{total_time:.4f}초')&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터가 1000개 일 때 : 0.0271초&lt;/li&gt;
&lt;li&gt;데이터가 2000개 일 때 : 0.1137초&lt;/li&gt;
&lt;li&gt;데이터가 4000개 일 때 : 0.4614초&lt;/li&gt;
&lt;li&gt;데이터가 5000개 일 때 : 0.7170초&lt;/li&gt;
&lt;li&gt;데이터가 10000개 일 때 : 2.8012초&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;버블 정렬 vs 선택 정렬 vs 삽입 정렬&lt;/b&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;여러가지 상황의 데이터도 입력해보고, 데이터의 개수를 늘려가면서도 성능 테스트를 해본 결과 데이터의 정렬 상태와 데이터의 양에 따라서 결과가 모두 다르게 나왔다. 세 알고리즘 모두 O(n^2)의 시간 복잡도를 가지지만, 그 안에서도 다른 조건에 의해서 다른 결과치를 보여줬고, 만일 입력되는 데이터가 정렬되어 있다면 삽입 정렬의 시간 복잡도는 O(n)으로 압도적으로 빠른 시간을 보여줬다. 선택 정렬은 역순으로 정렬되어 있을 때 왼쪽부터 시작해서 배열의 끝까지 왔다갔다를 계속 해야 하므로 셋 중 가장 시간이 오래 걸린다는 걸 테스트 결과로도 확인했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이론상의 시간 복잡도와 실제 소요되는 시간에는 차이가 있다는 걸 알게 됐고, 이론상 결과만 보고 알고리즘을 사용하는게 아니라, 데이터의 상태에 맞춰서 어떤 알고리즘이 더 알맞을지 판단을 잘 해내는게 필요하다는 생각이다.&lt;/p&gt;</description>
      <category>자료구조, 알고리즘</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/44</guid>
      <comments>https://gudwns5533.tistory.com/44#entry44comment</comments>
      <pubDate>Wed, 10 Jun 2026 23:23:21 +0900</pubDate>
    </item>
    <item>
      <title>선택 정렬 (Selection Sort)</title>
      <link>https://gudwns5533.tistory.com/43</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;선택 정렬 알고리즘의 기본 개념&lt;/b&gt;&lt;/blockquote&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: 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;주어진 배열 중에서 최솟값을 찾는다.&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;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;파이썬으로 코드 구현&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;코드 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781098156016&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def selectionsort(list):
    for i in range(len(list)):
        min_index = i  # 배열의 최솟값과 첫 번째 자리를 바꿔주기 위해 미리 첫 번째 위치 고정

        for j in range(i+1, len(list)):   # 첫 번째 자리의 다음 자리부터 끝까지 탐색
            if list[min_index] &amp;gt; list[j]: # 배열을 돌며 최솟값을 찾는다
                min_index = j
    
        list[i], list[min_index] = list[min_index], list[i] # 처음 고정해둔 앞자리와 최솟값의 자리를 교체 후 다음 회차 진행&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;size18&quot;&gt;&lt;b&gt;알고리즘 테스트&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781098301587&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;test = [2, 1, 9, 5, 3, 6, 1, 8, 4, 7]
selectionsort(test)
print(test)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781098321475&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[1, 1, 2, 3, 4, 5, 6, 7, 8, 9]&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;알고리즘 테스트 2&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래의 네가지 데이터를 넣고 테스트도 진행해 보았다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: decimal; color: #000000;&quot;&gt;정렬이 끝난 배열&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal; color: #000000;&quot;&gt;역순으로 정렬된 배열&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal; color: #000000;&quot;&gt;원소가 전부 같은 배열&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal; color: #000000;&quot;&gt;랜덤으로 구성된 배열&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1781102726630&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import time

def selectionsort(list):
    for i in range(len(list)):
        min_index = i

        for j in range(i+1, len(list)):
            if list[min_index] &amp;gt; list[j]:
                min_index = j
    
        list[i], list[min_index] = list[min_index], list[i]

test1 = [1, 2, 3, 4, 5, 6, 7]
test2 = [7, 6, 5, 4, 3, 2, 1]
test3 = [1, 1, 1, 1, 1, 1, 1, 1]
test4 = [23, 15, 38, 94, 62, 123, 243, 234, 112]

start_time1 = time.time()
selectionsort(test1)
end_time1 = time.time()
total_time1 = (end_time1 - start_time1) * 1000

start_time2 = time.time()
selectionsort(test2)
end_time2 = time.time()
total_time2 = (end_time2 - start_time2) * 1000

start_time3 = time.time()
selectionsort(test3)
end_time3 = time.time()
total_time3 = (end_time3 - start_time3) * 1000

start_time4 = time.time()
selectionsort(test4)
end_time4 = time.time()
total_time4 = (end_time4 - start_time4) * 1000

print(f'{total_time1:.4f}ms')
print(f'{total_time2:.4f}ms')
print(f'{total_time3:.4f}ms')
print(f'{total_time4:.4f}ms')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781102779392&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;0.0644ms
0.1113ms
0.0377ms
0.0963ms&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;random 데이터로 성능 테스트&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781098542431&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import random
import time

def selectionsort(list):
    for i in range(len(list)):
        min_index = i

        for j in range(i+1, len(list)):
            if list[min_index] &amp;gt; list[j]:
                min_index = j
    
        list[i], list[min_index] = list[min_index], list[i]

data = [random.randint(1, 1000000) for _ in range(1000)]
start_time = time.time()
selectionsort(data)
print(data)
end_time = time.time()
total_time = end_time - start_time
print(f'{total_time:.4f}초')&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터가 1000개 일 때 : 0.0240초&lt;/li&gt;
&lt;li&gt;데이터가 2000개 일 때 : 0.1096초&lt;/li&gt;
&lt;li&gt;데이터가 4000개 일 때 : 0.3155초&lt;/li&gt;
&lt;li&gt;데이터가 5000개 일 때 : 0.5005초&lt;/li&gt;
&lt;li&gt;데이터가 10000개 일 때 : 1.9639초&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>자료구조, 알고리즘</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/43</guid>
      <comments>https://gudwns5533.tistory.com/43#entry43comment</comments>
      <pubDate>Wed, 10 Jun 2026 22:37:59 +0900</pubDate>
    </item>
    <item>
      <title>버블 정렬 (Bubble Sort)</title>
      <link>https://gudwns5533.tistory.com/42</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;버블 정렬 알고리즘의 기본 개념&lt;/b&gt;&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서로 인접한 두 원소를 검사해 정렬하는 알고리즘&amp;nbsp;&lt;/li&gt;
&lt;li&gt;인접한 2개의 원소의 대소를 비교해 크기가 원하는 순서대로 되어있지 않다면 서로 교환&lt;/li&gt;
&lt;li&gt;알고리즘의 과정 (총 N개일 때, 오름차순 정렬)&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1회전에 첫 번째 원소와 두 번째 원소, 두 번째 원소와 세 번째 원소, 세 번째 원소와 네 번째 원소, ... 이렇게 쭉 진행해 가며 (N-1)번째 원소와 N번째 원소를 비교하면서 조건에 맞지 않는다면 조건에 맞춰서 서로 위치 변경.&lt;/li&gt;
&lt;li&gt;1회전이 끝나면 가장 큰 원소가 맨 뒤로 이동&amp;nbsp;&amp;rarr; 마지막 원소는 정렬 완료이므로 제외됨.&lt;/li&gt;
&lt;li&gt;2회전도 마찬가지로 진행 후 뒤에서 두 번째 원소까지 정렬 완료.&lt;/li&gt;
&lt;li&gt;정렬이 1회전 수행될 때 마다 정렬해야할 데이터가 하나씩 줄어듬.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;파이썬으로 코드 구현&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;코드 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781093903139&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def bubblesort(list, N):              # N은 len(list)
    for i in range(N-1):              # 마지막으로 남는 원소는 자동으로 정렬되므로 N-1까지 
        for j in range(N-1-i):        # i가 한 바퀴 돌 때마다 정렬이 한 개씩 되므로 범위를 줄여나간다
            if list[j] &amp;lt;= list[j+1]:  # 오름차순을 위해 j와 j+1을 비교해서 앞의 원소가 더 작거나 같다면 패스
                continue
            else:
                list[j], list[j+1] = list[j+1], list[j] # 그 외의 경우는 앞의 원소가 큰 경우 밖에 없으므로 위치 교환&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;알고리즘 테스트 1&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781094176298&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;test = [2, 1, 9, 5, 3, 6, 2]
bubblesort(test, len(test))
print(test)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781094200539&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[1, 2, 2, 3, 5, 6, 9]&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;size18&quot;&gt;&lt;b&gt;알고리즘 테스트 2&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;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;/li&gt;
&lt;li&gt;원소가 전부 같은 배열&lt;/li&gt;
&lt;li&gt;랜덤으로 구성된 배열&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1781102521522&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import time

def bubblesort(list, N):              
    for i in range(N-1):             
        for j in range(N-1-i):        
            if list[j] &amp;lt;= list[j+1]:  
                continue
            else:
                list[j], list[j+1] = list[j+1], list[j] 

test1 = [1, 2, 3, 4, 5, 6, 7]
test2 = [7, 6, 5, 4, 3, 2, 1]
test3 = [1, 1, 1, 1, 1, 1, 1, 1]
test4 = [0, 130, 26, 59, 556, 123, 2396, 16, 9801]

start_time1 = time.time()
bubblesort(test1, len(test1))
end_time1 = time.time()
total_time1 = (end_time1 - start_time1) * 1000

start_time2 = time.time()
bubblesort(test2, len(test2))
end_time2 = time.time()
total_time2 = (end_time2 - start_time2) * 1000

start_time3 = time.time()
bubblesort(test3, len(test3))
end_time3 = time.time()
total_time3 = (end_time3 - start_time3) * 1000

start_time4 = time.time()
bubblesort(test4, len(test4))
end_time4 = time.time()
total_time4 = (end_time4 - start_time4) * 1000

print(f'{total_time1:.4f}ms')
print(f'{total_time2:.4f}ms')
print(f'{total_time3:.4f}ms')
print(f'{total_time4:.4f}ms')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781102587621&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;0.0498ms
0.0446ms
0.0391ms
0.0403ms&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;random 데이터로 성능 테스트&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;성능 테스트를 위해 time 모듈과 random 모듈을 불러와서 알고리즘 코드에 붙여준다.&lt;/p&gt;
&lt;pre id=&quot;code_1781095682488&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import random
import time

def bubblesort(list, N):
    for i in range(N-1):
        for j in range(N-1-i):
            if list[j] &amp;lt;= list[j+1]:
                continue
            else:
                list[j], list[j+1] = list[j+1], list[j]

data = [random.randint(1, 1000000) for _ in range(1000)]
start_time = time.time()
bubblesort(data, len(data))
print(data)
end_time = time.time()
total_time = end_time - start_time
print(f'{total_time:.4f}초')&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 1000개 일 때 : 0.0700초&lt;/li&gt;
&lt;li&gt;데이터 2000개 일 때 : 0.1716초&lt;/li&gt;
&lt;li&gt;데이터 4000개 일 때 : 0.6912초&lt;/li&gt;
&lt;li&gt;데이터 5000개 일 때 : 1.0585초&lt;/li&gt;
&lt;li&gt;데이터 10000개 일 때 : 4.2511초&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>자료구조, 알고리즘</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/42</guid>
      <comments>https://gudwns5533.tistory.com/42#entry42comment</comments>
      <pubDate>Wed, 10 Jun 2026 21:54:12 +0900</pubDate>
    </item>
    <item>
      <title>정렬 알고리즘</title>
      <link>https://gudwns5533.tistory.com/41</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;알고리즘이란?&lt;/b&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;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;정렬 알고리즘이란?&lt;/b&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;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;정렬 알고리즘이 필요한 이유는?&lt;/b&gt;&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;탐색 효율 극대화 : 데이터가 정렬되어 있어야 이진 탐색(Binary Search)과 같은 빠르고 효율적인 탐색 알고리즘을 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;데이터 관리 : 대량의 데이터를 이름순, 가격순, 시간순 등으로 정렬하려 가시성과 활용도를 높인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;정렬 알고리즘의 종류와 특징&lt;/b&gt;&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 비교 정렬 알고리즘&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;버블 정렬(Bubble Sort)&lt;/b&gt;&lt;/h4&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-origin-width=&quot;480&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bw0crP/dJMcaftmGkx/gEcNhW8qP1Lo1a1ou3wYb0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bw0crP/dJMcaftmGkx/gEcNhW8qP1Lo1a1ou3wYb0/img.gif&quot; data-alt=&quot;버블 정렬&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bw0crP/dJMcaftmGkx/gEcNhW8qP1Lo1a1ou3wYb0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bw0crP/dJMcaftmGkx/gEcNhW8qP1Lo1a1ou3wYb0/img.gif&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;480&quot; height=&quot;150&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;버블 정렬&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;최선, 평균, 최악의 시간복잡도는 모두 O(n^2)&lt;/li&gt;
&lt;li&gt;직관적이나 거의 사용하지 않는 정렬 알고리즘이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&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;n개의 원소에 대해 n개의 메모리를 사용하기에 데이터를 하나씩 정밀 비교가 가능하다.&lt;/li&gt;
&lt;li&gt;배열 내에서 교환하는 방식(제자리 정렬)으로 추가적인 메모리 공간을 필요로 하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&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;최선이든 최악이든 O(n^2)의 시간 복잡도를 가진다.&lt;/li&gt;
&lt;li&gt;n개의 원소에 대해 n개의 메모리는 사용하기에 원소의 개수가 많아지면 비교 횟수가 많아져 성능이 저하된다.&lt;/li&gt;
&lt;li&gt;하나의 요소가 끝으로 이동하기 위해 배열의 모든 요소와 교환이 이뤄져야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;선택 정렬(Selection Sort)&lt;/b&gt;&lt;/h4&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-origin-width=&quot;946&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bD8Odt/dJMcabdnX73/QmiBwrdvSKcC2WfKH0K4kk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bD8Odt/dJMcabdnX73/QmiBwrdvSKcC2WfKH0K4kk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bD8Odt/dJMcabdnX73/QmiBwrdvSKcC2WfKH0K4kk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bD8Odt/dJMcabdnX73/QmiBwrdvSKcC2WfKH0K4kk/img.gif&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;138&quot; data-origin-width=&quot;946&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;최선, 평균, 최악의 시간 복잡도는 모두 O(n^2)&lt;/li&gt;
&lt;li&gt;비교 횟수는 많지만, 실제 교환되는 횟수는 적다. 따라서 똑같은 O(n^2)의 시간 복잡도를 가진 버블 정렬보다 적은 시간이 소요된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;알고리즘이 단순하고, 구현이 쉽다.&lt;/li&gt;
&lt;li&gt;정렬된 배열을 반대로 재정렬할 때 효율이 좋다.&lt;/li&gt;
&lt;li&gt;배열 내에서 교환하는 방식(제자리 정렬)으로 추가적인 메모리 공간을 필요로 하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배열의 길이가 길어질수록 지수적으로 시간이 증가하기 때문에 비효율적이다.&lt;/li&gt;
&lt;li&gt;중복된 값이 있을 경우, 기존 요소의 순서가 바뀔 수 있는 불안정성을 지닌다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;삽입 정렬(Insertion Sort)&lt;/b&gt;&lt;/h4&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-origin-width=&quot;772&quot; data-origin-height=&quot;506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HXdwb/dJMcah5QiN2/O6I4ZzEGsm15BHI2OkWeM1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HXdwb/dJMcah5QiN2/O6I4ZzEGsm15BHI2OkWeM1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HXdwb/dJMcah5QiN2/O6I4ZzEGsm15BHI2OkWeM1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/HXdwb/dJMcah5QiN2/O6I4ZzEGsm15BHI2OkWeM1/img.gif&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;328&quot; data-origin-width=&quot;772&quot; data-origin-height=&quot;506&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;최선의 시간복잡도는 O(n)이며, 평균 및 최악의 시간 복잡도는 O(n^2)&lt;/li&gt;
&lt;li&gt;O(n^2)의 시간 복잡도를 갖는 선택 정렬과 버블 정렬에 비해 상대적으로 속도가 빠르다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&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;이미 정렬된 배열일 경우, 매번 1번의 비교로 정렬되기 때문에 O(n)의 시간 복잡도를 가지며 매우 효율적이다.&lt;/li&gt;
&lt;li&gt;배열 내에서 교환하는 방식(제자리 정렬)으로 추가적인 메모리 공간을 필요로 하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 정렬 알고리즘에 비해 비교적 많은 교환이 발생한다.&lt;/li&gt;
&lt;li&gt;배열의 길이가 길어질수록 지수적으로 시간이 증가하기 때문에 비효율적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;퀵 정렬(Quick Sort)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기준값(Pivot)을 정해 그보다 작은 그룹과 큰 그룹으로 나누어 정렬을 반복하는 방식. 피벗을 기준으로 왼쪽은 피벗보다 작은 값, 오른쪽은 피벗보다 큰 값을 가질 수 있도록 값을 이동시킨다. 분할과 동시에 정렬이 이뤄지며, 분할된 배열의 크기가 1이 되면 정렬이 완료된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uzima/dJMcagFR0zI/Ikbq9t2pXOCSsBKkHTsBK0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uzima/dJMcagFR0zI/Ikbq9t2pXOCSsBKkHTsBK0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uzima/dJMcagFR0zI/Ikbq9t2pXOCSsBKkHTsBK0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/uzima/dJMcagFR0zI/Ikbq9t2pXOCSsBKkHTsBK0/img.gif&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;139&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;최선 및 평균 시간 복잡도는 O(nlogn)이며, 최악의 시간복잡도는 O(n^2)&lt;/li&gt;
&lt;li&gt;O(nlogn)의 시간 복잡도를 가지는 병합 정렬보다 상대적으로 속도가 빠르다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배열 내에서 교환하는 방식(제자리 정렬)으로 추가적인 메모리 공간을 필요로 하지 않는다&lt;/li&gt;
&lt;li&gt;다른 알고리즘과 비교했을 때 가장 빠르다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&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;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;병합 정렬(Merge Sort)&lt;/b&gt;&lt;/h4&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-origin-width=&quot;780&quot; data-origin-height=&quot;506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qkYta/dJMcaaeyOTw/Bgkqb7hzmzKBbcknqodMX1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qkYta/dJMcaaeyOTw/Bgkqb7hzmzKBbcknqodMX1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qkYta/dJMcaaeyOTw/Bgkqb7hzmzKBbcknqodMX1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/qkYta/dJMcaaeyOTw/Bgkqb7hzmzKBbcknqodMX1/img.gif&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;324&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;506&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;최선, 평균, 최악의 시간 복잡도는 모두 O(nlogn)이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연결리스트를 이용할 경우, 링크 인덱스만 변경하면 되므로 이동 연산이 줄어든다.(추가적인 메모리 필요없음)&lt;/li&gt;
&lt;li&gt;퀵 정렬과 비교했을 때 데이터 분포에 영향을 받지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배열을 이용할 경우, 추가적인 메모리(임시 배열)이 필요하다.&lt;/li&gt;
&lt;li&gt;이동 연산이 많아, 레코드 개수가 클 경우에는 비효율적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 비비교 정렬 알고리즘&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;계수 정렬(Counting Sort)&lt;/h4&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-origin-width=&quot;1030&quot; data-origin-height=&quot;129&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c14PAO/dJMcagshmXf/F7ja1X9QcOWO2oQvku5dd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c14PAO/dJMcagshmXf/F7ja1X9QcOWO2oQvku5dd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c14PAO/dJMcagshmXf/F7ja1X9QcOWO2oQvku5dd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc14PAO%2FdJMcagshmXf%2FF7ja1X9QcOWO2oQvku5dd1%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;550&quot; height=&quot;69&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;129&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1225&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnfFAZ/dJMb997KVQR/ZbEKIx4Lp6dQL1nqp4dbU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnfFAZ/dJMb997KVQR/ZbEKIx4Lp6dQL1nqp4dbU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnfFAZ/dJMb997KVQR/ZbEKIx4Lp6dQL1nqp4dbU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnfFAZ%2FdJMb997KVQR%2FZbEKIx4Lp6dQL1nqp4dbU0%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;78&quot; data-origin-width=&quot;1225&quot; data-origin-height=&quot;190&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1225&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AI8lL/dJMcadoIKju/W44vw0vBmfoRROum4gYtuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AI8lL/dJMcadoIKju/W44vw0vBmfoRROum4gYtuK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AI8lL/dJMcadoIKju/W44vw0vBmfoRROum4gYtuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAI8lL%2FdJMcadoIKju%2FW44vw0vBmfoRROum4gYtuK%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;78&quot; data-origin-width=&quot;1225&quot; data-origin-height=&quot;190&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEWoGW/dJMcaiwR3nw/B6bFy1QFblbG1qgmO4LwTk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEWoGW/dJMcaiwR3nw/B6bFy1QFblbG1qgmO4LwTk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEWoGW/dJMcaiwR3nw/B6bFy1QFblbG1qgmO4LwTk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEWoGW%2FdJMcaiwR3nw%2FB6bFy1QFblbG1qgmO4LwTk%2Fimg.jpg&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;213&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;시간 복잡도가 O(n)으로 어떤 정렬 알고리즘보다 빠르고 간단하게 정렬한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&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;입력되는 배열이 0 부터 k 사이의 정수라는 가정하에 정렬을 시작한다. 이를 위해 배열에서 가장 큰 값이 무엇인지 알아야만 한다. 그렇지 않다면 계수 정렬을 사용할 수 없다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>자료구조, 알고리즘</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/41</guid>
      <comments>https://gudwns5533.tistory.com/41#entry41comment</comments>
      <pubDate>Wed, 10 Jun 2026 20:12:53 +0900</pubDate>
    </item>
    <item>
      <title>더블링크드 리스트 파이썬 스터디</title>
      <link>https://gudwns5533.tistory.com/40</link>
      <description>&lt;pre id=&quot;code_1781052868235&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Node:
    def __init__(self, value):
        self.value = value
        self.prev = None
        self.next = None


class DoubleLinkedList:
    def __init__(self):
        self.head = None
        self.tail = None

    def append(self, value):
        # TODO: 리스트의 맨 뒤에 새 노드를 추가한다.
        new_node = Node(value)

        if self.head is None:
            self.head = new_node
            self.tail = new_node
            return
        
        current = self.head
        while current.next is not None:
            current = current.next

        current.next = new_node
        new_node.prev = current
        self.tail = new_node

    def prepend(self, value):
        # TODO: 리스트의 맨 앞에 새 노드를 추가한다.
        new_node = Node(value)
        self.head.prev = new_node
        new_node.next = self.head
        self.head = new_node

    def insert(self, index, value):
        # TODO: 원하는 인덱스에 새 노드를 삽입한다.
        if index &amp;lt; 0:
            return False
        
        if index == 0:
            self.prepend(value)
            return True
        
        if self.length() == index:
            self.append(value)
            return True
        
        new_node = Node(value)
        current = self.head
        current_index = 0

        while current_index &amp;lt; index:
            current = current.next
            current_index += 1

        if current is None:
            return False
        
        if current_index == index:
            new_node.next = current
            new_node.prev = current.prev
            current.prev.next = new_node
            current.prev = new_node
            return True

    def delete(self, value):
        # TODO: 입력한 값을 가진 첫 번째 노드를 삭제한다.
        if self.head is None:
            return False
        
        if self.head.value == value:
            self.head = self.head.next
            self.head.prev = None
            return True
        
        current = self.head.next
        
        # if current.value == self.tail:
        #     current.prev = self.tail
        #     return True
        
        while current.next is not None:
            
            # if current.value == value and current.next == None:
            #     self.tail = current.prev
            #     current.prev.next = None
            #     return True
            
            if current.value == value: 
                current.prev.next = current.next
                current.next.prev = current.prev
                return True
            
            current = current.next

        if current.value == self.tail.value:
            self.tail = current.prev
            current.prev.next = None
            return True 
        
        return False    
            # if current.value != value:
            #     return False
            

    def find(self, value):
        # TODO: 입력한 값이 저장된 첫 번째 인덱스를 찾는다.
        current = self.head
        index = 0
        while current is not None:
            if current.value == value:
                return index
            if current.value != value:
                current = current.next
                index += 1

    def get(self, index):
        # TODO: 입력한 인덱스에 저장된 값을 반환한다.
        if index &amp;lt; 0:
            return None
        
        current = self.head
        current_index = 0
        while current is not None:
            if current_index == index:
                return current.value
            
            current = current.next
            current_index += 1

        return None

    def get_next(self, index):
        node = self.get_node(index)

        if node is None:
            return None

        return node.next

    def get_prev(self, index):
        node = self.get_node(index)

        if node is None:
            return None

        return node.prev

    def get_node(self, index):
        if index &amp;lt; 0:
            return None

        current = self.head
        current_index = 0

        while current is not None:
            if current_index == index:
                return current

            current = current.next
            current_index += 1

        return None

    def length(self):
        count = 0
        current = self.head

        while current is not None:
            count += 1
            current = current.next

        return count

    def display(self):
        if self.head is None:
            return &quot;HEAD -&amp;gt; None&quot;

        result = [&quot;HEAD&quot;]
        current = self.head

        while current is not None:
            result.append(f&quot;[{current.value}]&quot;)
            current = current.next

        result.append(&quot;None&quot;)
        return &quot; &amp;lt;-&amp;gt; &quot;.join(result)

    def display_reverse(self):
        if self.tail is None:
            return &quot;TAIL -&amp;gt; None&quot;

        result = [&quot;TAIL&quot;]
        current = self.tail

        while current is not None:
            result.append(f&quot;[{current.value}]&quot;)
            current = current.prev

        result.append(&quot;None&quot;)
        return &quot; &amp;lt;-&amp;gt; &quot;.join(result)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>스터디</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/40</guid>
      <comments>https://gudwns5533.tistory.com/40#entry40comment</comments>
      <pubDate>Wed, 10 Jun 2026 09:54:41 +0900</pubDate>
    </item>
    <item>
      <title>링크드 리스트 파이썬 스터디</title>
      <link>https://gudwns5533.tistory.com/39</link>
      <description>&lt;pre id=&quot;code_1781052749899&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# linked_list.py

# 링크드리스트의 노드 하나를 표현하는 클래스
class Node:
    def __init__(self, value):
        # 노드가 가지고 있는 실제 값
        self.value = value

        # 다음 노드를 가리키는 변수, 초기값은 None
        self.next = None


# 단일 링크드리스트를 표현하는 클래스
class LinkedList:

    def __init__(self):
        # 링크드리스트의 첫 번째 노드를 가리킨다.
        self.head = None

    # 링크드리스트의 맨 뒤에 값을 추가
    def append(self, value):
        new_node = Node(value)

        # 리스트가 비어 있으면 새 노드가 첫 번째 노드
        if self.head is None:
            self.head = new_node
            return

        # 마지막 노드까지 이동
        current = self.head
        while current.next is not None:
            current = current.next

        # 마지막 노드 뒤에 새 노드를 연결
        current.next = new_node

    # 링크드리스트의 맨 앞에 값을 추가
    def prepend(self, value):
        new_node = Node(value)

        # 새 노드가 기존 head를 가리킨다.
        new_node.next = self.head

        # head를 새 노드로 변경
        self.head = new_node

    # 원하는 인덱스 위치에 값을 추가
    def insert(self, index, value):
        if index &amp;lt; 0:
            return False

        # 0번 인덱스는 맨 앞에 추가하는 것과 같다.
        if index == 0:
            self.prepend(value)
            return True

        new_node = Node(value)
        current = self.head
        current_index = 0

        # 삽입할 위치의 바로 앞 노드까지 이동
        while current is not None and current_index &amp;lt; index - 1:
            current = current.next
            current_index += 1

        # current가 None이면 인덱스가 현재 리스트 길이보다 큰 경우
        if current is None:
            return False

        # current와 current.next 사이에 새 노드를 끼워 넣는다.
        new_node.next = current.next
        current.next = new_node
        return True

    # 입력한 값을 가진 첫 번째 노드를 삭제
    def delete(self, value):
        if self.head is None:
            return False

        # 삭제할 값이 첫 번째 노드에 있는 경우
        if self.head.value == value:
            self.head = self.head.next
            return True

        current = self.head

        while current.next is not None:
            if current.next.value == value:
                current.next = current.next.next
                return True

            current = current.next

        return False

    # 입력한 값이 있는 인덱스를 반환. 없으면 -1을 반환
    def find(self, value):
        current = self.head
        index = 0

        while current is not None:
            if current.value == value:
                return index

            current = current.next
            index += 1

        return -1

    # 입력한 인덱스에 있는 노드의 값을 반환. 인덱스가 유효하지 않으면 None을 반환
    def get(self, index):
        if index &amp;lt; 0:
            return None

        current = self.head
        current_index = 0

        while current is not None:
            if current_index == index:
                return current.value

            current = current.next
            current_index += 1

        return None
    
    # 입력한 인덱스에 있는 노드가 갖고 있는 reference 를 반환. 없으면 None을 반환
    def get_next(self, index):
        if index &amp;lt; 0:
            return None
        
        current = self.head
        current_index = 0
        
        while current is not None:
            if current_index == index:
                return current.next
            
            current = current.next
            current_index += 1
        
        return None

    # 현재 링크드리스트의 노드 개수를 반환
    def length(self):
        count = 0
        current = self.head

        while current is not None:
            count += 1
            current = current.next

        return count
    
    # 링크드리스트를 터미널에서 보기 좋은 문자열로 반환
    def display(self):
        if self.head is None:
            return &quot;HEAD -&amp;gt; None&quot;

        result = [&quot;HEAD&quot;]
        current = self.head

        while current is not None:
            result.append(f&quot;[{current.value}]&quot;)
            current = current.next

        result.append(&quot;None&quot;)
        return &quot; -&amp;gt; &quot;.join(result)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>스터디</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/39</guid>
      <comments>https://gudwns5533.tistory.com/39#entry39comment</comments>
      <pubDate>Wed, 10 Jun 2026 09:52:41 +0900</pubDate>
    </item>
    <item>
      <title>MySQL 연동</title>
      <link>https://gudwns5533.tistory.com/38</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;mysqlclient&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파이썬에서 MySQL 데이터베이스와 상호작용하기 위해 사용하는 공식 라이브러리&lt;/li&gt;
&lt;li&gt;PyMySQL, mysqlclient를 가장 많이 사용함&lt;/li&gt;
&lt;li&gt;사용법은 비슷하나 mysqlclient가 속도상으로 유리하기 때문에 mysqlclient를 권장함&lt;/li&gt;
&lt;li&gt;실무에서는 SQLAlchemy를 가장 많이 사용함&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780991560268&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import MySQLdb&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. MySQL 접속&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;MySQLdb.connect(host='IP주소', user='사용자명', password='비밀번호', db='데이터베이스명')&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780991940916&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db = MySQLdb.connect(host='localhost', user='root', password='1234', db='ai')
db

# &amp;lt;_mysql.connection open to 'localhost' at 0000022775350F40&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. cursor 생성&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 데이터베이스 connection에 대해 독립적으로 SQL문을 실행할 수 있는 작업환경을 제공하는 객체&lt;/li&gt;
&lt;li&gt;하나의 connection에 동시에 한 개의 cursor만 생성할 수 있으며, cursor를 통해 SQL문을 실행하면 실행결과를 튜플단위로 반환해준다.&lt;/li&gt;
&lt;li&gt;cur.execute() : cursor를 통해 SQL문을 실행시키는 명령어&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780991960268&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cur = db.cursor()
cur.execute('select * from member')

# 7 (데이터베이스의 행의 수를 반환해준다)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. SQL문의 결과 반환&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fetchall() : 한 번에 모든 tuple을 가져옴. 검색 결과가 매우 많다면 메모리 오버헤드가 발생할 수 있음.&lt;/li&gt;
&lt;li&gt;techone() : 한 번에 하나의 tuple을 가져옴. 다시 fetchone() 메서드를 호출하면 다음 데이터를 가져옴&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780992858203&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 모든 tuple을 가져오기
data = cur.fetchall()

# 하나의 tuple만 가져오기 (다음 tuple을 가져오려면 같은 명령어 재실행)
row = cur.fetchone()

# 반복문을 통해서 데이터를 가져오는것도 가능하다
while True:
    row = cur.fetchone()
    if row:
        print(row)
    else:
        break&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. 딕셔너리 형태로 결과 반환&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;cursor(MySQLdb.cursors.DictCursor)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780993024643&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cur = db.cursor(MySQLdb.cursors.DictCursor)
cur.execute(sql)

result = cur.fetchall()
result

'''
({'userid': 'apple', 'name': '김사과', 'gender': '여자', 'hp': '010-1111-1111'},
 {'userid': 'banana', 'name': '반하나', 'gender': '여자', 'hp': '010-2222-2222'},
 {'userid': 'orange', 'name': '오렌지', 'gender': '남자', 'hp': '010-3333-3333'},
 {'userid': 'melon', 'name': '이메론', 'gender': '남자', 'hp': '010-4444-4444'},
 {'userid': 'cherry', 'name': '채리', 'gender': '여자', 'hp': '010-5555-5555'},
 {'userid': 'berry', 'name': '배애리', 'gender': '여자', 'hp': '010-6666-6666'},
 {'userid': 'avocado', 'name': '안가도', 'gender': '남자', 'hp': '010-7777-7777'})
 '''&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;딕셔너리 형태로 반환 했으므로, key값으로 value를 나눌수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1780993094794&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cur.execute(sql)

while True:
    row = cur.fetchone()
    if row:
        print(f&quot;아이디:{row['userid']}, 이름:{row['name']}, 성별:{row['gender']}, 전화번호:{row['hp']}&quot;)
    else:
        break
        
'''
아이디:apple, 이름:김사과, 성별:여자, 전화번호:010-1111-1111
아이디:banana, 이름:반하나, 성별:여자, 전화번호:010-2222-2222
아이디:orange, 이름:오렌지, 성별:남자, 전화번호:010-3333-3333
아이디:melon, 이름:이메론, 성별:남자, 전화번호:010-4444-4444
아이디:cherry, 이름:채리, 성별:여자, 전화번호:010-5555-5555
아이디:berry, 이름:배애리, 성별:여자, 전화번호:010-6666-6666
아이디:avocado, 이름:안가도, 성별:남자, 전화번호:010-7777-7777
'''&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5. Cursor와 Connection 닫기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불필요한 메모리 사용을 줄이기 위해 데이터베이스 사용이 끝나면 Cursor 와 Connection은 닫아줘야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1780993191122&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cur.close()
db.close()&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;데이터 삽입하기&lt;/b&gt;&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;회원가입 프로그램&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1780993905066&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 데이터베이스와 상호작용을 위해 MySQL에 접속을 항상 해줘야한다.
db = MySQLdb.connect(host='localhost', user='root', password='1234', db='ai')
# SQL문 실행을 위해 cursor생성
cur = db.cursor()

while True:
    try:
        userid = input('아이디를 입력하세요: ')
        userpw = input('비밀번호를 입력하세요: ')
        name = input('이름을 입력하세요: ')
        hp = input('전화번호를 입력하세요: ')
        email = input('이메일을 입력하세요: ')
        ssn1 = input('주민등록번호 앞자리를 입력하세요: ')
        ssn2 = input('주빈등록번호 뒷자리를 입력하세요: ')
        zipcode = input('우편번호를 입력하세요: ')
        address1 = input('주소를 입력하세요: ')
        address2 = input('상세주소를 입력하세요: ')
        address3 = input('참고사항을 입력하세요: ')
        gender = input('성별을 입력하세요 (남자 또는 여자): ')
        
        # 입력받은 데이터를 데이터베이스에 넘겨주기 위해 sql문을 작성해주고
        # 이때 입력값들은 보안을 위해 %s로 받아준다.
        sql = 'insert into member (userid, userpw, name, hp, email, gender, ssn1, ssn2, zipcode, address1, address2, address3) values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)'
        data = (userid, userpw, name, hp, email, gender, ssn1, ssn2, zipcode, address1, address2, address3)
        cur.execute(sql, data)
        
        # db.commit() : 데이터베이스상 실제 데이터 수정
        db.commit()
        
        print('가입되었습니다!')
        yn = input('추가로 가입하시겠습니까? (y/n): ')
        if yn.lower() == 'n':
            print('프로그램을 종료합니다')
            break
    except Exception as e:
        print('다시 입력하세요')

# 항상 cursor와 connection을 닫으면서 끝내기
cur.close()
db.close()&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;로그인 프로그램&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1780994028745&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db = MySQLdb.connect(host='localhost', user='root', password='1234', db='ai')
cur = db.cursor()
cur.execute('select userid, userpw from member')

userid = input('아이디를 입력하세요: ')
userpw = input('비밀번호를 입력하세요: ')


while True:
    row = cur.fetchone()
    if userid and userpw in row:
        print('로그인에 성공했습니다')
        break
    else:
        pass

cur.close()
db.close()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;while문을 통해 fetchone()으로 데이터를 한줄 한줄 가져오면서 값을 비교하게된다.&lt;/li&gt;
&lt;li&gt;만일 데이터의 양이 많아진다면 속도면에서 비효율적이므로 사용하지 않는 코딩 방법이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780994076601&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db = MySQLdb.connect(host='localhost', user='root', password='1234', db='ai')
cur = db.cursor()

userid = input('아이디를 입력하세요: ')
userpw = input('비밀번호를 입력하세요: ')

sql = 'select idx from member where userid=%s and userpw=%s'
data = (userid, userpw)
result = cur.execute(sql, data)

if result &amp;gt; 0:
    print('로그인 되었습니다')
else:
    print('아이디 또는 비밀번호를 확인하세요')

cur.close()
db.close()&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터베이스에서 userid 그리고 userpw가 동시에 맞는 데이터의 idx를 불러오게 된다.&lt;/li&gt;
&lt;li&gt;만일 아이디와 비밀번호를 모두 맞게 입력했다면 result값은 1이 나오게 된다.&lt;/li&gt;
&lt;li&gt;입력값과 데이터베이스의 데이터를 모두 비교해볼 필요없이 로그인 여부를 알 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;회원정보 수정&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1780994587081&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# MySQL 접속과 cursor 생성
db = MySQLdb.connect(host='localhost', user='root', password='1234', db='ai')
cur = db.cursor()

while True:
    try:
        userid = input('변경할 아이디를 입력하세요: ')
        name = input('변경할 이름을 입력하세요: ')
        hp = input('변경할 전화번호를 입력하세요: ')
        email = input('변경할 이메일을 입력하세요: ')
        ssn1 = input('변경할 주민등록번호 앞자리를 입력하세요: ')
        ssn2 = input('변경할 주빈등록번호 뒷자리를 입력하세요: ')
        zipcode = input('변경할 우편번호를 입력하세요: ')
        address1 = input('변경할 주소를 입력하세요: ')
        address2 = input('변경할 상세주소를 입력하세요: ')
        address3 = input('변경할 참고사항을 입력하세요: ')
        gender = input('성별을 입력하세요 (남자 또는 여자): ')
        
        # 아이디를 기준으로 나머지 정보들을 수정해야 하므로
        # update memeber set 정보1, 정보2... where userid=%s로 update문 작성
        sql = 'update member set name=%s, hp=%s, email=%s, gender=%s, ssn1=%s, ssn2=%s, zipcode=%s, address1=%s, address2=%s, address3=%s where userid=%s'
        data = (name, hp, email, gender, ssn1, ssn2, zipcode, address1, address2, address3, userid)
        result = cur.execute(sql, data)
        db.commit()

        # 수정값이 있다면 result가 0이 아닌값이 반환된다.
        if result &amp;gt; 0:
            print('변경되었습니다!')
        else:
            print('에러!')
            
        yn = input('추가로 변경하시겠습니까? (y/n): ')
        if yn.lower() == 'n':
            print('프로그램을 종료합니다')
            break

    except Exception as e:
        print('다시 입력하세요')
        
cur.close()
db.close()&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;회원 탈퇴&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1780994792481&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db = MySQLdb.connect(host='localhost', user='root', password='1234', db='ai')
cur = db.cursor()

userid = input('탈퇴할 아이디를 입력하세요')

sql = 'delete from member where userid=%s'

# (userid)라고 적는다면 튜플이 아닌 문자열로 인식하기때문에 꼭 뒤에 , 를 붙여서 튜플로 만들어줘야함
data = (userid,)
result = cur.execute(sql, data)
db.commit()

if result &amp;gt; 0:
    print('탈퇴되었습니다')
else:
    print('아이디를 확인하세요')

cur.close()
db.close()&lt;/code&gt;&lt;/pre&gt;</description>
      <category>MySQL</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/38</guid>
      <comments>https://gudwns5533.tistory.com/38#entry38comment</comments>
      <pubDate>Tue, 9 Jun 2026 17:38:02 +0900</pubDate>
    </item>
    <item>
      <title>뷰 (view)</title>
      <link>https://gudwns5533.tistory.com/37</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;뷰(view)&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;select 문을 저장해둔 가상의 테이블&lt;/p&gt;
&lt;pre id=&quot;code_1780989847783&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;create view 뷰이름 as select문&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;view를 사용하는 이유&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복잡한 SQL을 단순화&lt;/li&gt;
&lt;li&gt;재사용 (자주 쓰는 테이블 조회 저장)&lt;/li&gt;
&lt;li&gt;가독성 (SQL을 보기 쉽게 구성)&lt;/li&gt;
&lt;li&gt;보안 (특정 컬럼만 공개) &amp;rarr; view로 전달하게되면 개발자는 전체 테이블에 대한 정보를 알 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;view의 특징&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테이블을 직접 저장하지 않음 (원본 테이블의 select 결과를 보여주는 가상 테이블)&lt;/li&gt;
&lt;li&gt;member 테이블 데이터 변경 &amp;rarr; view 결과도 같이 변경됨&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780990311254&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;create view vip_member as select userid, name, point from member where point &amp;gt;= 100;
-- 위와 같이 내가 원하는 컬럼을 담은 view를 만들어 놓으면
-- 아래와 같이 view만 호출해서 사용하면 된다.
select * from vip_member;

-- 또한 view는 select처럼 사용도 가능하다
select * from vip_member where point &amp;gt;= 150;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780990625278&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 회원 주소 view(member_address) 만들기
-- 들어갈 데이터: userid, name, address1 + address2 + address3 as addresss

-- concat을 통해 먼저 주소를 합쳐준다.
select concat(address1, ' ' , address2, ' ', address3) as address from member;

-- userid, name, address 로 view를 생성해준다.
create view member_address as select userid, name, concat(address1, ' ' , address2, ' ', address3) as address from member;
select * from member_address;&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;view 수정 (덮어쓰기)&lt;/p&gt;
&lt;pre id=&quot;code_1780990778293&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;create or replace view vip_member as
select userid, name, point, email from member where point &amp;gt;= 100;
-- create or replace view 뷰이름 as select 수정할 데이터
-- 이때 내용이 덮어쓰기로 저장되므로, 실무에서는 사용시 조심해야한다.

select * from vip_member;&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;view 삭제&lt;/p&gt;
&lt;pre id=&quot;code_1780990900445&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;drop view vip_member;&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;view 구조 확인&lt;/p&gt;
&lt;pre id=&quot;code_1780990917381&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;show create view vip_member;&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;view 목록 확인&lt;/p&gt;
&lt;pre id=&quot;code_1780990946054&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;show full tables where table_type='VIEW';
-- view의 이름은 꼭 대문자로 적어줘야한다&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;view를 통한 단순화&lt;/p&gt;
&lt;pre id=&quot;code_1780991285917&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 기존 select문과 join을 통한 데이터 출력
select m.userid, m.name, o.product_name, o.price, o.order_date from member m
join orders o on m.idx = o.member_idx;

-- view를 통해 단순화
create view member_order as
select m.userid, m.name, o.product_name, o.price, o.order_date from member m
join orders o on m.idx = o.member_idx;

-- 이후 동일한 데이터를 조회할때는 다음과 같이 사용하면 된다
select * from member_order;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MySQL</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/37</guid>
      <comments>https://gudwns5533.tistory.com/37#entry37comment</comments>
      <pubDate>Tue, 9 Jun 2026 16:48:10 +0900</pubDate>
    </item>
    <item>
      <title>MySQL 함수</title>
      <link>https://gudwns5533.tistory.com/36</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 문자열 함수&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;concat()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열을 이어 붙이는 함수&lt;/p&gt;
&lt;pre id=&quot;code_1780981652802&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select concat('안녕하세요', 'MySQL');
-- 안녕하세요MySQL&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;left(), right()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열의 왼쪽 / 오른쪽 일부를 가져옴&lt;/p&gt;
&lt;pre id=&quot;code_1780981804641&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select left('ABCDEFGHIJ', 3);   -- ABC
select right('ABCDEFGHIJ', 4);  -- GHIJ&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;size18&quot;&gt;&lt;b&gt;substring()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열의 일부를 추출&lt;/p&gt;
&lt;pre id=&quot;code_1780981855201&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select substring('ABCDEFGHIJ', 3, 4); 
-- 3번째 문자부터 4글자 추출
-- CDEF&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;size18&quot;&gt;&lt;b&gt;char_length(), length()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자 개수, 바이트 수&lt;/p&gt;
&lt;pre id=&quot;code_1780981894601&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select char_length('가나다');
select length('가나가');  -- 한글은 한글자에 3byte
-- 3
-- 9&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;size18&quot;&gt;&lt;b&gt;trim(), ltrim(), rtrim()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공백 제거&lt;/p&gt;
&lt;pre id=&quot;code_1780981936785&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select trim('         mysql          ');
-- mysql&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;size18&quot;&gt;&lt;b&gt;replace()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 치환&lt;/p&gt;
&lt;pre id=&quot;code_1780982068673&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select replace('010-1111-1111', '-', '');
select hp, replace(hp, '-', '') as 번호 from member;

-- 01011111111&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;size18&quot;&gt;&lt;b&gt;lower(), upper()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소문자, 대문자 변환&lt;/p&gt;
&lt;pre id=&quot;code_1780982104865&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select upper('mysql');
select email, upper(email) as '대문자' from member;

-- MYSQL&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 수학 함수&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;abs()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;절댓값&lt;/p&gt;
&lt;pre id=&quot;code_1780982145521&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select abs(-100);
-- 100&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;size18&quot;&gt;&lt;b&gt;round()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반올림&lt;/p&gt;
&lt;pre id=&quot;code_1780982174609&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select round(3.141592, 2);
-- 3.14&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;size18&quot;&gt;&lt;b&gt;ceil(), floor()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올림, 내림&lt;/p&gt;
&lt;pre id=&quot;code_1780982224257&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select ceil(3.1);
select floor(3.9);

-- 4
-- 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;size18&quot;&gt;&lt;b&gt;mod()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나머지&lt;/p&gt;
&lt;pre id=&quot;code_1780982420281&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select mod(10,3);

-- 1&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;size18&quot;&gt;&lt;b&gt;rand()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;랜덤값 생성&lt;/p&gt;
&lt;pre id=&quot;code_1780982554000&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select rand();
select * from member order by rand() limit 1;

-- 0.05728084506213744&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;size18&quot;&gt;&lt;b&gt;truncate()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버림&lt;/p&gt;
&lt;pre id=&quot;code_1780982602184&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select truncate(3.141592, 2);

-- 3.14&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 날짜 함수&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;now()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 날짜 + 시간 (운영체제 시간 기준)&lt;/p&gt;
&lt;pre id=&quot;code_1780982740600&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select now();

-- '2026-06-09 14:25:34'&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;size18&quot;&gt;&lt;b&gt;curdate(), curtime()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 날짜와 시간을 각각 알려줌&lt;/p&gt;
&lt;pre id=&quot;code_1780983037920&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select curdate();
select curtime();

-- '2026-06-09'
--  '14:30:25'&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;size18&quot;&gt;&lt;b&gt;date_format()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜 포멧 변경&lt;/p&gt;
&lt;pre id=&quot;code_1780987332274&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select date_format(now(), '%Y년 %m월 %d일 %H시 %i분 %s초');
-- '2026년 06월 09일 15시 42분 04초'&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;size18&quot;&gt;&lt;b&gt;datediff()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜 차이 계산&lt;/p&gt;
&lt;pre id=&quot;code_1780987367146&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select datediff('2026-12-17', now());
select datediff('2026-12-25', now());

-- 191
-- 199&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;size18&quot;&gt;&lt;b&gt;adddate()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜 더하기&lt;/p&gt;
&lt;pre id=&quot;code_1780987631889&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select adddate(now(), 30);
-- '2026-07-09 15:47:03'&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;size18&quot;&gt;&lt;b&gt;subdate()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜 빼기&lt;/p&gt;
&lt;pre id=&quot;code_1780987698962&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select subdate(now(), 7);
-- '2026-06-02 15:48:07'&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;size18&quot;&gt;&lt;b&gt;dayofweek()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요일 숫자 반환&lt;/p&gt;
&lt;pre id=&quot;code_1780987733162&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select dayofweek(now());
-- 3
-- 1: 일요일, 2: 월요일, 3: 화요일, 4: 수요일, 5: 목요일, 6: 금요일, 7: 토요일&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;size18&quot;&gt;&lt;b&gt;year(), month(), day()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연, 월, 일 반환&lt;/p&gt;
&lt;pre id=&quot;code_1780987813441&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select year(now());
select month(now());
select day(now());

-- 2026
-- 6
-- 9&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. 조건 함수&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;if()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건 처리&lt;/p&gt;
&lt;pre id=&quot;code_1780988843152&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select userid, if(point &amp;gt;= 100, 'VIP', '일반') from member;&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;size18&quot;&gt;&lt;b&gt;ifnull()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;null인지 확인, null 처리&lt;/p&gt;
&lt;pre id=&quot;code_1780988886664&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select ifnull(regdate, '가입일 없음') from member;
-- null을 '가입일 없음'으로 처리&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;size18&quot;&gt;&lt;b&gt;nullif()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 값이 같으면 null 반환&lt;/p&gt;
&lt;pre id=&quot;code_1780988922008&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select nullif(10, 10);&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;size18&quot;&gt;&lt;b&gt;case when&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 조건 처리&lt;/p&gt;
&lt;pre id=&quot;code_1780988945264&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select userid, 
case
    when point &amp;gt;= 200 then 'VIP'
    when point &amp;gt;= 100 then 'Gold'
    else 'Normal'
end as 등급 from member;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5. 형변환 함수&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;cast()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자료형 변경&lt;/p&gt;
&lt;pre id=&quot;code_1780989264192&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select cast('2026-06-08' as datetime);
-- '2026-06-08 00:00:00'&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;size18&quot;&gt;&lt;b&gt;convert()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;형 변환&lt;/p&gt;
&lt;pre id=&quot;code_1780989358455&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select convert('-123', signed);
select convert('-123', unsigned);

-- '-123'
-- '18446744073709551493'&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;6. 집계 함수&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;count()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행 개수&lt;/p&gt;
&lt;pre id=&quot;code_1780989390039&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select count(idx) from member;
-- 10&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;size18&quot;&gt;&lt;b&gt;avg()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평균&lt;/p&gt;
&lt;pre id=&quot;code_1780989435199&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select avg(point) from member;
-- 125&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;size18&quot;&gt;&lt;b&gt;sum()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;합계&lt;/p&gt;
&lt;pre id=&quot;code_1780989453807&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select sum(point) from member;
-- 1250&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;size18&quot;&gt;&lt;b&gt;max(), min()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최댓값 / 최솟값&lt;/p&gt;
&lt;pre id=&quot;code_1780989482943&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select max(point) from member;
select min(point) from member;

-- 250
-- 50&lt;/code&gt;&lt;/pre&gt;</description>
      <category>MySQL</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/36</guid>
      <comments>https://gudwns5533.tistory.com/36#entry36comment</comments>
      <pubDate>Tue, 9 Jun 2026 16:18:22 +0900</pubDate>
    </item>
    <item>
      <title>서브쿼리 (Sub Query)</title>
      <link>https://gudwns5533.tistory.com/35</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;서브쿼리&lt;b&gt; (Sub Query)&lt;br /&gt;&lt;/b&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;서브쿼리는 SQL문 안에 들어가는 또 다른 SELECT문을 의미한다.&lt;/p&gt;
&lt;pre id=&quot;code_1780928091078&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select ... from where 컬럼 = (select ... );&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780928637447&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 김사과보다 포인트가 많은 회원을 검색

select point from member where name='김사과'; -- 김사과의 포인트 검색
select * from member wherer point &amp;gt; 150; -- 150보다 많은 멤버의 정보 검색
select * from member where point &amp;gt; (select point from member where name='김사과');
-- 서브 쿼리를 써서 김사과보다 포인트가 많은 회원을 검색할 수 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 단일 행 서브쿼리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과가 하나의 값만 나오는 서브쿼리&lt;/p&gt;
&lt;pre id=&quot;code_1780929375556&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 포인트가 가장 높은 회원을 검색
select * from member where point = (select max(point) from member);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 다중 행 서브쿼리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과가 여러 개 나오는 서브쿼리 (in, any, all을 사용)&lt;/p&gt;
&lt;pre id=&quot;code_1780929427815&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 포인트가 200 이상인 userid 목록에 포함된 회원을 검색
select * from member where userid in (select userid from member where point &amp;gt;= 150);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. from절 서브쿼리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브쿼리 결과를 임시 테이블처럼 사용하는 방식&lt;/p&gt;
&lt;pre id=&quot;code_1780929493419&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select name, point from (select name, point from member where point &amp;gt;= 150) as high_point_member;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. select절 서브쿼리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조회 결과에 서브쿼리 결과를 함께 보여주는 방식&lt;/p&gt;
&lt;pre id=&quot;code_1780929515235&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 각 회원의 포인트와 전체 평균 포인트를 함께 검색
select name, point, (select avg(point) from member) as avg_point from member;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5. exists 서브쿼리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;존재 여부를 확인할 때 사용&lt;/p&gt;
&lt;pre id=&quot;code_1780929846746&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 주문 내역이 있는 회원을 검색
select * from member m where not exists (select 1 from orders o where o.member_idx = m.idx);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;6. 주문 테이블 생성&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1780931174293&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;create table orders (
    order_id int auto_increment primary key,
    member_idx int not null,
    product_name varchar(100) not null,
    price int not null,
    order_date datetime default now(),
    foreign key (member_idx) references member(idx)
);

insert into orders(member_idx, product_name, price)
values
(1, '키보드', 50000),
(1, '마우스', 30000),
(2, '모니터', 250000),
(2, '노트북 거치대', 40000),
(3, 'USB', 15000),
(4, '노트북', 1200000),
(4, '헤드셋', 90000);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;6-1. 주문한 적이 있는 회원들만 조회 (in 사용)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780932545738&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select * from member where idx in (select member_idx from orders);&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;size18&quot;&gt;&lt;b&gt;6-2. 주문한 적이 없는 회원들만 조회 (not in 사용)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780932648866&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select * from member where idx not in (select member_idx from orders);&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;size18&quot;&gt;&lt;b&gt;6-3. 가장 비싼 상품을 주문한 회원 조회&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780932958324&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 1. orders에서 가장 비싼 가격을 찾기
-- 2. 그 가격의 주문을 한 회원 번호를 찾기
-- 3. 해당 회원 번호를 가진 회원 정보를 조회하기
select * from member where idx = 
(select member_idx from orders where price = (select max(price) from orders));&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;size18&quot;&gt;&lt;b&gt;6-4. 가장 비싼 상품을 주문한 회원의 이름과 상품명을 조회&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780933509518&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- join을 활용해서 조회
select m.name, o.product_name, o.price from member m join orders o on m.idx = 
o.member_idx where o.price=(select max(price) from orders);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;7. 테이블 복사&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1780933819491&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;create table orders_copy (
    order_id int auto_increment primary key,
    member_idx int not null,
    product_name varchar(100) not null,
    price int not null,
    order_date datetime default now(),
    foreign key (member_idx) references member(idx)
);

-- 기존 테이블의 데이터를 다른 테이블로 복사할 수 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780933844553&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;insert into orders_copy(select * from orders);
-- 테이블의 형태가 동일하면 insert 서브쿼리도 가능하다

create table orders_copy_copy(select * from orders);
-- 복사한 테이블로 새로운 테이블을 만들 수도 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;8&lt;/b&gt;&lt;b&gt;. UNION&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 개의 select 결과를 하나로 합쳐주는 기능&lt;/li&gt;
&lt;li&gt;조회 결과를 위아래로 이어 붙이는 기능&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780934037681&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select name, address1 from member where address1 like '서울%'
union
select name, address1 from member where address1 like '부산%';&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;이때, UNION은 중복데이터를 제거하고 합친다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;9. UNION ALL&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UNION과 동일하게 select 결과를 합쳐주지만 중복데이터를 제거하지않고 모두 합쳐준다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;10. 사용자 계정&lt;/b&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;/li&gt;
&lt;li&gt;root 계정은 모든 권한을 가진 계정이기 때문에 실제 사용 시 위험할 수 있음&lt;/li&gt;
&lt;li&gt;프로젝트별로 계정을 따로 만들고, 필요한 권한만 부여하는 것이 일반적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780934595264&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- create user '계정명'@'접속위치' identified by '비밀번호';

-- localhost : 같은 컴퓨터(내 컴퓨터)에서만 접속 &amp;gt; 외부에서 접근할 수 없음.

-- 'apple'@'%' : 어디서든 접속 가능.

-- 'apple'@'192.168.0.%' : 198.168.0.으로 시작하는 내부망에서만 접속이 가능

-- 'apple'@'192.168.0.10' : 특정 ip에서만 접속 가능 (해킹을 막기 위해)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;권한부여 방법&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780934626230&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- grant 권한종류 on 데이터베이스명/테이블명 to '계정명'@'접속위치';

-- all : 모든 일반 권한, select, insert, update, delete, create, drop, alger, index

-- ai.* : ai 데이터베이스 안의 모든 테이블 (특정 테이블만 원한 다면 * 대신 테이블 명으로 기입)

-- 권한 회수
revoke delete on 데이터베이스명/테이블명 from '계정명'@'접속위치';

-- 사용자 비밀번호 변경
alter user '계정명'@'접속위치' identified by '변경할 비밀번호';

-- 사용자 삭제
drop user '계정명'@'접속위치';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MySQL</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/35</guid>
      <comments>https://gudwns5533.tistory.com/35#entry35comment</comments>
      <pubDate>Tue, 9 Jun 2026 01:07:52 +0900</pubDate>
    </item>
    <item>
      <title>힙 (Heap)</title>
      <link>https://gudwns5533.tistory.com/34</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;힙(Heap)이란 무엇인가?&lt;/b&gt;&lt;/blockquote&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;b&gt;반정렬 상태(느슨한 정렬 상태)&lt;/b&gt;이다&lt;/li&gt;
&lt;li&gt;이진 탐색 트리(BST)와 다르게 &lt;b&gt;중복값이 허용&lt;/b&gt;된다.&lt;/li&gt;
&lt;li&gt;트리구조를 가지고 있기 때문에 삽입/삭제 &lt;b&gt;시간 복잡도는 O(logn)&lt;/b&gt;이다.&lt;/li&gt;
&lt;li&gt;힙 자료구조는 보통 배열을 사용하며 0번째 인덱스는 계산을 편하게 하기 위새 사용하지 않는다. 즉, 루트노드의 인덱스가 1이다.&lt;/li&gt;
&lt;li&gt;부모 자식간의 인덱스 관계는 &lt;b&gt;부모 인덱스가 N일 때, 왼쪽 자식은 2N 오른쪽 자식은 2N+1&lt;/b&gt;을 가진다.&lt;/li&gt;
&lt;li&gt;완전 이진트리이기 때문에 노드마다 포인터를 따로 저장하지 않아도 배열의 인덱스만으로 부모와 자식의 위치를 찾아아낼 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;힙 (Heap) vs 이진 탐색 트리 (Binary Search Tree)&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 137px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 28.3333%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 36.124%; height: 17px; text-align: center;&quot;&gt;힙&lt;/td&gt;
&lt;td style=&quot;width: 35.5426%; height: 17px; text-align: center;&quot;&gt;이진 탐색 트리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.3333%; text-align: center; height: 20px;&quot;&gt;트리 형태&lt;/td&gt;
&lt;td style=&quot;width: 36.124%; height: 20px; text-align: center;&quot;&gt;완전 이진 트리&lt;/td&gt;
&lt;td style=&quot;width: 35.5426%; height: 20px; text-align: center;&quot;&gt;이진 트리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.3333%; text-align: center; height: 20px;&quot;&gt;원소의 중복 여부&lt;/td&gt;
&lt;td style=&quot;width: 36.124%; height: 20px; text-align: center;&quot;&gt;중복 가능&lt;/td&gt;
&lt;td style=&quot;width: 35.5426%; height: 20px; text-align: center;&quot;&gt;중복 불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.3333%; text-align: center; height: 20px;&quot;&gt;원소의 정렬 여부&lt;/td&gt;
&lt;td style=&quot;width: 36.124%; height: 20px; text-align: center;&quot;&gt;정렬 X&lt;/td&gt;
&lt;td style=&quot;width: 35.5426%; height: 20px; text-align: center;&quot;&gt;정렬 O (왼쪽자식 &amp;lt; 부모 &amp;lt; 오른쪽 자식)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.3333%; text-align: center; height: 20px;&quot;&gt;원소 탐색 시간 복잡도&lt;/td&gt;
&lt;td style=&quot;width: 36.124%; height: 20px; text-align: center;&quot;&gt;O(n) (순차 탐색)&lt;/td&gt;
&lt;td style=&quot;width: 35.5426%; height: 20px; text-align: center;&quot;&gt;O(logn) (이진 탐색)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.3333%; text-align: center; height: 20px;&quot;&gt;원소의 삽입 및 삭제 시간 복잡도&lt;/td&gt;
&lt;td style=&quot;width: 36.124%; height: 20px; text-align: center;&quot;&gt;O(logn)&lt;/td&gt;
&lt;td style=&quot;width: 35.5426%; height: 20px; text-align: center;&quot;&gt;O(logn) / O(n) (편항트리)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.3333%; text-align: center; height: 20px;&quot;&gt;최댓값/최솟값 참조 시간 복잡도&lt;/td&gt;
&lt;td style=&quot;width: 36.124%; height: 20px; text-align: center;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;width: 35.5426%; height: 20px; text-align: center;&quot;&gt;O(logn) / O(n) (편향트리)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;최대 힙 (Max Heap)과 최소 힙 (Min Heap)&lt;/b&gt;&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;최대 힙 (Max Heap)&lt;/b&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;/li&gt;
&lt;li&gt;루트노드에 항상 가장 큰 값이 존재&lt;/li&gt;
&lt;li&gt;key(부모 노드) &amp;gt;= key(자식 노드)&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;443&quot; data-origin-height=&quot;373&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m8dl1/dJMcahdD8Go/VNdSKWTby9LkBikUGbWe71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m8dl1/dJMcahdD8Go/VNdSKWTby9LkBikUGbWe71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m8dl1/dJMcahdD8Go/VNdSKWTby9LkBikUGbWe71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm8dl1%2FdJMcahdD8Go%2FVNdSKWTby9LkBikUGbWe71%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;443&quot; height=&quot;373&quot; data-origin-width=&quot;443&quot; data-origin-height=&quot;373&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;최소 힙(Min Heap)&lt;/b&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;/li&gt;
&lt;li&gt;루트노드에 항상 가장 작은 값이 존재&lt;/li&gt;
&lt;li&gt;key(부모 노드) &amp;lt;= key(자식 노드)&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;452&quot; data-origin-height=&quot;379&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGCvrl/dJMcagFP9SD/oNkrkQvIKggCyTwMATNYu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGCvrl/dJMcagFP9SD/oNkrkQvIKggCyTwMATNYu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGCvrl/dJMcagFP9SD/oNkrkQvIKggCyTwMATNYu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGCvrl%2FdJMcagFP9SD%2FoNkrkQvIKggCyTwMATNYu0%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;452&quot; height=&quot;379&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;379&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;힙의 동작 과정&lt;/b&gt;&lt;/blockquote&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;size18&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;최대 힙에 35라는 값을 추가한다고 생각해보자. 완전 이진 트리의 규칙에 맞춰 가장 왼쪽 아래에 노드가 생기지만 이는 최대 힙 규칙에 맞지 않는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;441&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btUKaU/dJMcacwDLMY/zFtXzWRcCSqO2ivxWkam70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btUKaU/dJMcacwDLMY/zFtXzWRcCSqO2ivxWkam70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btUKaU/dJMcacwDLMY/zFtXzWRcCSqO2ivxWkam70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtUKaU%2FdJMcacwDLMY%2FzFtXzWRcCSqO2ivxWkam70%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;441&quot; height=&quot;358&quot; data-origin-width=&quot;441&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 새로 입력된 노드와 부모 노드의 값을 비교하게 되고 최대 힙 조건인 부모 노드의&amp;nbsp; key &amp;gt;= 자식 노드의 key를 만족하기 위해 위치를 바꿔준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OobIz/dJMcagMBGYD/9o05tZCxsFVk9anwHplp9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OobIz/dJMcagMBGYD/9o05tZCxsFVk9anwHplp9K/img.png&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;345&quot; data-is-animation=&quot;false&quot; style=&quot;width: 34.1362%; margin-right: 10px;&quot; data-widthpercent=&quot;34.95&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OobIz/dJMcagMBGYD/9o05tZCxsFVk9anwHplp9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOobIz%2FdJMcagMBGYD%2F9o05tZCxsFVk9anwHplp9K%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;345&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GjcYw/dJMcabEs5zG/BCtyBpvRku0kpHDOsBY0cK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GjcYw/dJMcabEs5zG/BCtyBpvRku0kpHDOsBY0cK/img.png&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;356&quot; data-is-animation=&quot;false&quot; style=&quot;width: 31.3703%; margin-right: 10px;&quot; data-widthpercent=&quot;32.12&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GjcYw/dJMcabEs5zG/BCtyBpvRku0kpHDOsBY0cK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGjcYw%2FdJMcabEs5zG%2FBCtyBpvRku0kpHDOsBY0cK%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;440&quot; height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dEQ6mA/dJMcafNHT9l/IZFJzQFOt4dxOi8DaegvaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dEQ6mA/dJMcafNHT9l/IZFJzQFOt4dxOi8DaegvaK/img.png&quot; data-origin-width=&quot;474&quot; data-origin-height=&quot;374&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.1679%;&quot; data-widthpercent=&quot;32.93&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dEQ6mA/dJMcafNHT9l/IZFJzQFOt4dxOi8DaegvaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdEQ6mA%2FdJMcafNHT9l%2FIZFJzQFOt4dxOi8DaegvaK%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;474&quot; height=&quot;374&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 계속해서 부모 노드와 키 값을 비교하면서 최대 힙 조건에 충족할 때 까지 자리를 swap 한다.&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;&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;이번에는 최소 힙에 데이터를 삽입 하는 경우를 생각해보자. 이것도 최대 힙과 개념은 크게 다르지 않다. 다만 최소 힙의 조건인 부모 노드의 key &amp;lt;= 자식 노드의 key만 만족해주면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GqW08/dJMcaijiQQi/pkMw1g1T1ZJQjOzYsuWYu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GqW08/dJMcaijiQQi/pkMw1g1T1ZJQjOzYsuWYu0/img.png&quot; data-origin-width=&quot;414&quot; data-origin-height=&quot;354&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.2021%; margin-right: 10px;&quot; data-widthpercent=&quot;46.75&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GqW08/dJMcaijiQQi/pkMw1g1T1ZJQjOzYsuWYu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGqW08%2FdJMcaijiQQi%2FpkMw1g1T1ZJQjOzYsuWYu0%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;414&quot; height=&quot;354&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M92T1/dJMcadCb0Hh/CBi7XSoB64zv1YSj1KtanK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M92T1/dJMcadCb0Hh/CBi7XSoB64zv1YSj1KtanK/img.png&quot; data-origin-width=&quot;441&quot; data-origin-height=&quot;331&quot; data-is-animation=&quot;false&quot; style=&quot;width: 52.6351%;&quot; data-widthpercent=&quot;53.25&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M92T1/dJMcadCb0Hh/CBi7XSoB64zv1YSj1KtanK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM92T1%2FdJMcadCb0Hh%2FCBi7XSoB64zv1YSj1KtanK%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;441&quot; height=&quot;331&quot;/&gt;&lt;/span&gt;&lt;/div&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;이 과정을 계속 반복해주며 최소 힙 조건이 충족 될 때 까지 값을 이동시켜 준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bL8juK/dJMcagsfzrV/HqjceClHIvwHU4YVoYDl7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bL8juK/dJMcagsfzrV/HqjceClHIvwHU4YVoYDl7K/img.png&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;339&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.4525%; margin-right: 10px;&quot; data-widthpercent=&quot;49.02&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bL8juK/dJMcagsfzrV/HqjceClHIvwHU4YVoYDl7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbL8juK%2FdJMcagsfzrV%2FHqjceClHIvwHU4YVoYDl7K%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;428&quot; height=&quot;339&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CDs0j/dJMcabLjOIH/TKBOToT3j8tCbVKp7DuXDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CDs0j/dJMcabLjOIH/TKBOToT3j8tCbVKp7DuXDK/img.png&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;326&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.3847%;&quot; data-widthpercent=&quot;50.98&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CDs0j/dJMcabLjOIH/TKBOToT3j8tCbVKp7DuXDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCDs0j%2FdJMcabLjOIH%2FTKBOToT3j8tCbVKp7DuXDK%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;428&quot; height=&quot;326&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9APtO/dJMcadCb0KO/lsqPwCTNiT00q8T5m2zock/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9APtO/dJMcadCb0KO/lsqPwCTNiT00q8T5m2zock/img.png&quot; data-origin-width=&quot;379&quot; data-origin-height=&quot;337&quot; data-is-animation=&quot;false&quot; style=&quot;width: 29.629%; margin-right: 10px; margin-top: 10px;&quot; data-widthpercent=&quot;30.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9APtO/dJMcadCb0KO/lsqPwCTNiT00q8T5m2zock/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9APtO%2FdJMcadCb0KO%2FlsqPwCTNiT00q8T5m2zock%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;379&quot; height=&quot;337&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cq7csw/dJMcafUrHTq/LLHEADeWf1eC1GXrsz5PN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cq7csw/dJMcafUrHTq/LLHEADeWf1eC1GXrsz5PN0/img.png&quot; data-origin-width=&quot;463&quot; data-origin-height=&quot;334&quot; data-is-animation=&quot;false&quot; style=&quot;width: 36.5209%; margin-right: 10px; margin-top: 10px;&quot; data-widthpercent=&quot;37.39&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cq7csw/dJMcafUrHTq/LLHEADeWf1eC1GXrsz5PN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcq7csw%2FdJMcafUrHTq%2FLLHEADeWf1eC1GXrsz5PN0%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;463&quot; height=&quot;334&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmto4X/dJMcaaew1Th/53a3GPw4qmPfmfUziYEdSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmto4X/dJMcaaew1Th/53a3GPw4qmPfmfUziYEdSk/img.png&quot; data-origin-width=&quot;420&quot; data-origin-height=&quot;351&quot; data-is-animation=&quot;false&quot; style=&quot;width: 31.5246%; margin-top: 10px;&quot; data-widthpercent=&quot;32.28&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmto4X/dJMcaaew1Th/53a3GPw4qmPfmfUziYEdSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcmto4X%2FdJMcaaew1Th%2F53a3GPw4qmPfmfUziYEdSk%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;420&quot; height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&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;size18&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;루트 값(최댓값)인 50을 반환하고 삭제한다고 생각해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bitkOF/dJMcacQTxoh/abTLc773oziKSBj7m2jtUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bitkOF/dJMcacQTxoh/abTLc773oziKSBj7m2jtUk/img.png&quot; data-origin-width=&quot;423&quot; data-origin-height=&quot;343&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.8782%; margin-right: 10px;&quot; data-widthpercent=&quot;49.45&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bitkOF/dJMcacQTxoh/abTLc773oziKSBj7m2jtUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbitkOF%2FdJMcacQTxoh%2FabTLc773oziKSBj7m2jtUk%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;423&quot; height=&quot;343&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EL35H/dJMcadvrLrO/G9GYnboSKCVnERGHIKWxn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EL35H/dJMcadvrLrO/G9GYnboSKCVnERGHIKWxn1/img.png&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;357&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.959%;&quot; data-widthpercent=&quot;50.55&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EL35H/dJMcadvrLrO/G9GYnboSKCVnERGHIKWxn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEL35H%2FdJMcadvrLrO%2FG9GYnboSKCVnERGHIKWxn1%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;450&quot; height=&quot;357&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루트가 제거되면 최대 힙의 마지막 노드의 값을 루트 노드로 가져오게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;376&quot; data-origin-height=&quot;275&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1HchT/dJMcada9vAP/LJn6wGIOKM94zGWKyFIgkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1HchT/dJMcada9vAP/LJn6wGIOKM94zGWKyFIgkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1HchT/dJMcada9vAP/LJn6wGIOKM94zGWKyFIgkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1HchT%2FdJMcada9vAP%2FLJn6wGIOKM94zGWKyFIgkk%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;376&quot; height=&quot;275&quot; data-origin-width=&quot;376&quot; data-origin-height=&quot;275&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후, 새로운 루트 노드와 자식 노드의 값을 비교하고, 만일 부모 노드보다 자식 노드가 둘다 크다면 둘중에 큰 값을, 둘중에 하나만 크다면 그 값과 swap 한다. 이를 최대 힙 조건이 만족할 때 까지 계속 반복한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjQScS/dJMcagls6Q9/fAIFdMqFkLNdAB1lC3j26k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjQScS/dJMcagls6Q9/fAIFdMqFkLNdAB1lC3j26k/img.png&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;285&quot; data-is-animation=&quot;false&quot; style=&quot;width: 31.0968%; margin-right: 10px;&quot; data-widthpercent=&quot;31.84&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjQScS/dJMcagls6Q9/fAIFdMqFkLNdAB1lC3j26k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjQScS%2FdJMcagls6Q9%2FfAIFdMqFkLNdAB1lC3j26k%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;408&quot; height=&quot;285&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nSv8X/dJMcaiXQtxN/jC19NHLA8I3RXlpkK6s230/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nSv8X/dJMcaiXQtxN/jC19NHLA8I3RXlpkK6s230/img.png&quot; data-origin-width=&quot;412&quot; data-origin-height=&quot;268&quot; data-is-animation=&quot;false&quot; style=&quot;width: 33.3935%; margin-right: 10px;&quot; data-widthpercent=&quot;34.19&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nSv8X/dJMcaiXQtxN/jC19NHLA8I3RXlpkK6s230/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnSv8X%2FdJMcaiXQtxN%2FjC19NHLA8I3RXlpkK6s230%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;412&quot; height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjlzuG/dJMcaaFCSSg/3FwPkbDbBpyTLllRKkmjVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjlzuG/dJMcaaFCSSg/3FwPkbDbBpyTLllRKkmjVk/img.png&quot; data-origin-width=&quot;414&quot; data-origin-height=&quot;271&quot; data-is-animation=&quot;false&quot; style=&quot;width: 33.1842%;&quot; data-widthpercent=&quot;33.97&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjlzuG/dJMcaaFCSSg/3FwPkbDbBpyTLllRKkmjVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjlzuG%2FdJMcaaFCSSg%2F3FwPkbDbBpyTLllRKkmjVk%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;414&quot; height=&quot;271&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;루트 값(최솟값)인 1을 반환하고 삭제한다고 생각해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sICk7/dJMcafz8aO3/25zkZqdMfbUBxgjAZMEBy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sICk7/dJMcafz8aO3/25zkZqdMfbUBxgjAZMEBy1/img.png&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;343&quot; data-is-animation=&quot;false&quot; style=&quot;width: 51.1798%; margin-right: 10px;&quot; data-widthpercent=&quot;51.78&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sICk7/dJMcafz8aO3/25zkZqdMfbUBxgjAZMEBy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsICk7%2FdJMcafz8aO3%2F25zkZqdMfbUBxgjAZMEBy1%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;428&quot; height=&quot;343&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chaW8P/dJMcaffRhS9/w7VT6Vikdd5ZK34992Yub0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chaW8P/dJMcaffRhS9/w7VT6Vikdd5ZK34992Yub0/img.png&quot; data-origin-width=&quot;409&quot; data-origin-height=&quot;352&quot; data-is-animation=&quot;false&quot; style=&quot;width: 47.6574%;&quot; data-widthpercent=&quot;48.22&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chaW8P/dJMcaffRhS9/w7VT6Vikdd5ZK34992Yub0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchaW8P%2FdJMcaffRhS9%2Fw7VT6Vikdd5ZK34992Yub0%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;409&quot; height=&quot;352&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때도 최대 힙에서 삭제할때와 마찬가지로 마지막 노드를 루트 노드로 가져와서 값을 비교해가며 최소 힙의 조건이 맞을 때 까지 진행한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;361&quot; data-origin-height=&quot;259&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cA1r1R/dJMcahrcDHJ/tjXHo4joyPn3HQPRt5a0m0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cA1r1R/dJMcahrcDHJ/tjXHo4joyPn3HQPRt5a0m0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cA1r1R/dJMcahrcDHJ/tjXHo4joyPn3HQPRt5a0m0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcA1r1R%2FdJMcahrcDHJ%2FtjXHo4joyPn3HQPRt5a0m0%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;361&quot; height=&quot;259&quot; data-origin-width=&quot;361&quot; data-origin-height=&quot;259&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최소 힙 경우는 자식 노드와 부모 노드를 비교하여 자식 노드중 가장 작은값이 위로 올라오게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Tve7p/dJMcaaMkBNd/SA2g8YOGDqteIIUK48u2bK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Tve7p/dJMcaaMkBNd/SA2g8YOGDqteIIUK48u2bK/img.png&quot; style=&quot;width: 34.5461%; margin-right: 10px;&quot; data-origin-width=&quot;368&quot; data-origin-height=&quot;242&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;35.37&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Tve7p/dJMcaaMkBNd/SA2g8YOGDqteIIUK48u2bK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTve7p%2FdJMcaaMkBNd%2FSA2g8YOGDqteIIUK48u2bK%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;368&quot; height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xsWh1/dJMcacQTB2d/sgATfUi6iyR7nn2A1SMxL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xsWh1/dJMcacQTB2d/sgATfUi6iyR7nn2A1SMxL0/img.png&quot; style=&quot;width: 31.0012%; margin-right: 10px;&quot; data-origin-width=&quot;378&quot; data-origin-height=&quot;277&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;31.74&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xsWh1/dJMcacQTB2d/sgATfUi6iyR7nn2A1SMxL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxsWh1%2FdJMcacQTB2d%2FsgATfUi6iyR7nn2A1SMxL0%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;378&quot; height=&quot;277&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BHmA7/dJMcaf09qcq/jKOHkhDKDO8GNHIkn7t8wk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BHmA7/dJMcaf09qcq/jKOHkhDKDO8GNHIkn7t8wk/img.png&quot; data-origin-width=&quot;379&quot; data-origin-height=&quot;268&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.1271%;&quot; data-widthpercent=&quot;32.89&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BHmA7/dJMcaf09qcq/jKOHkhDKDO8GNHIkn7t8wk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBHmA7%2FdJMcaf09qcq%2FjKOHkhDKDO8GNHIkn7t8wk%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;379&quot; height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Heapify&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;Heapify란 이미 값들이 채워진 완전 이진트리가 있을 때, 새로운 값을 추가하거나 삭제하는 경우에 완전 이진트리의 성질이 깨지는 경우가 존재한다. 이때, 깨진 트리의 값을 다시 맞추는 작업을 말한다. 위에서 삽입, 삭제시에 값을 비교해 가며 힙의 구조를 유지해가는 과정이라고 생각하면 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;힙의 연산별 시간 복잡도&lt;/b&gt;&lt;/blockquote&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: 7.86818%; text-align: center;&quot;&gt;연산&lt;/td&gt;
&lt;td style=&quot;width: 22.1705%; text-align: center;&quot;&gt;시간 복잡도&lt;/td&gt;
&lt;td style=&quot;width: 69.9612%; text-align: center;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 7.86818%; text-align: center;&quot;&gt;삽입&lt;/td&gt;
&lt;td style=&quot;width: 22.1705%; text-align: center;&quot;&gt;O(logn)&lt;/td&gt;
&lt;td style=&quot;width: 69.9612%; text-align: left;&quot;&gt;새로운 요소를 힙의 가장 끝에 추가한 뒤, 부모 노드와 비교하면서 노드를 위로 올린다. 이때 최악의 경우 트리의 높이인 logn까지 연산을 해야 하므로 시간 복잡도는 O(logn)이 된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 7.86818%; text-align: center;&quot;&gt;삭제&lt;/td&gt;
&lt;td style=&quot;width: 22.1705%; text-align: center;&quot;&gt;O(logn)&lt;/td&gt;
&lt;td style=&quot;width: 69.9612%; text-align: left;&quot;&gt;루트 노드에서 값을 꺼내고 가장 마지막에 있는 노드를 루트로 올린 뒤 자식 노드와 비교하며 아래로 내린다. 이때도 삽입연산과 마찬가지로 트리의 높이까지 연산을 해야할 수 있으므로 시간 복잡도는 O(logn)이 된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 7.86818%; text-align: center;&quot;&gt;탐색&lt;/td&gt;
&lt;td style=&quot;width: 22.1705%; text-align: center;&quot;&gt;최댓값/최솟값 탐색 : O(1)&lt;br /&gt;일반 값 탐색 : O(n)&lt;/td&gt;
&lt;td style=&quot;width: 69.9612%; text-align: left;&quot;&gt;최댓값, 최솟값은 항상 루트 노드에 있으니 시간 복잡도는 O(1), 하지만 특정한 값을 찾을 때는 전체를 다 뒤져야 하므로 시간 복잡도가 O(n)이 된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;코드 구현&lt;/b&gt;&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;최대 힙 코드&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1780923672177&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MaxHeap:
    def __init__(self):
        self.heap = [None]  # 루트 노드의 인덱스를 1로 하기 위해 None을 넣어둔다. 


    def insert(self, value):
        self.heap.append(value)
        idx = len(self.heap) - 1  # 새로 추가된 노드의 인덱스값(None이 이미 들어있었기 때문)

        while idx &amp;gt; 1:
            parent_idx = idx // 2  # 부모 인덱스가 N일때, 왼쪽 자식은 2N, 오른쪽 자식은 2N+1

            if self.heap[idx] &amp;gt; self.heap[parent_idx]:  # 새로 추가된 노드의 값이 부모 노드보다 크다면 swap 해줘야함
                self.heap[idx], self.heap[parent_idx] = self.heap[parent_idx], self.heap[idx]
                idx = parent_idx  # 새로 추가된 노드가 이제 부모 노드가 되었으므로 인덱스를 바꿔준다.
            else:
                break


    def delete(self):
        if len(self.heap) &amp;lt;= 1:  # 인덱스가 1 이하라는 뜻은 기존에 미리 넣어둔 None말고는 아무값도 없다는 뜻
            return None
        
        max_heap = self.heap[1]  # 최대 힙에서 최댓값은 항상 루트 노드에 존재하기 때문

        if len(self.heap) &amp;lt;= 1:  # 뽑은 값이 최댓값이자 유일한 값이었다면 바로 최댓값 리턴
            return max_heap
        
        last_val = self.heap.pop() # 삭제 후에 힙의 가장 마지막 값을 루트로 올려줘야 하기 때문
        self.heap[1] = last_val    # 힙 구조를 유지하기 위해 마지막 값을 루트 노드로 올림
        self.down_heapify(1)       # 위의 insert를 다시 돌며 제자리 찾기

        return max_heap            # 노드가 힙 구조에 맞춰지고 최댓값 반환
    
    
    def down_heapify(self, idx):
        while idx * 2 &amp;lt; len(self.heap):
            left_child = idx * 2
            right_child = idx * 2 + 1
            current = idx

            # 왼쪽 자식과 현재 값과의 1차 비교
            if self.heap[left_child] &amp;gt; self.heap[current]:
                current = left_child
            
            # 오른쪽 자식과의 2차 비교
            if right_child &amp;lt; len(self.heap) and self.heap[right_child] &amp;gt; self.heap[current]:
                current = right_child

            # 자식과 비교 후 부모가 가장 크다면 그대로 반목문 종료
            if current == idx:
                break
            
            # 더 큰 자식이 있다면 그 자식과 현재 값을 swap
            self.heap[idx], self.heap[current] = self.heap[current], self.heap[idx]
            idx = current

    def print_heap(self):
        print(self.heap[1:])


Mh = MaxHeap()

Mh.insert(10)
Mh.insert(20)
Mh.insert(30)
Mh.insert(40)
Mh.insert(50)

Mh.print_heap()

print(Mh.delete())
Mh.print_heap()

print(Mh.delete())
Mh.print_heap()

'''
결과값
[50, 40, 20, 10, 30]
50
[40, 30, 20, 10]
40
[30, 10, 20]
'''&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;최소 힙 코드 (부모와 자식을 비교하는 곳의 부등호 방향만 반대로 해주면 된다.)&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1780924253672&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MinHeap:
    def __init__(self):
        self.heap = [None]  # 루트 노드의 인덱스를 1로 하기 위해 None을 넣어둔다. 


    def insert(self, value):
        self.heap.append(value)
        idx = len(self.heap) - 1  # 새로 추가된 노드의 인덱스값(None이 이미 들어있었기 때문)

        while idx &amp;gt; 1:
            parent_idx = idx // 2  # 부모 인덱스가 N일때, 왼쪽 자식은 2N, 오른쪽 자식은 2N+1

            if self.heap[idx] &amp;lt; self.heap[parent_idx]:  # 새로 추가된 노드의 값이 부모 노드보다 작다면 swap 해줘야함
                self.heap[idx], self.heap[parent_idx] = self.heap[parent_idx], self.heap[idx]
                idx = parent_idx  # 새로 추가된 노드가 이제 부모 노드가 되었으므로 인덱스를 바꿔준다.
            else:
                break


    def delete(self):
        if len(self.heap) &amp;lt;= 1:  # 인덱스가 1 이하라는 뜻은 기존에 미리 넣어둔 None말고는 아무값도 없다는 뜻
            return None
        
        min_heap = self.heap[1]  # 최소 힙에서 최솟값은 항상 루트 노드에 존재하기 때문

        if len(self.heap) &amp;lt;= 1:  # 뽑은 값이 최솟값이자 유일한 값이었다면 바로 최솟값 리턴
            return min_heap
        
        last_val = self.heap.pop() # 삭제 후에 힙의 가장 마지막 값을 루트로 올려줘야 하기 때문
        self.heap[1] = last_val    # 힙 구조를 유지하기 위해 마지막 값을 루트 노드로 올림
        self.down_heapify(1)       # 위의 insert를 다시 돌며 제자리 찾기

        return min_heap            # 노드가 힙 구조에 맞춰지고 최솟값 반환
    
    
    def down_heapify(self, idx):
        while idx * 2 &amp;lt; len(self.heap):
            left_child = idx * 2
            right_child = idx * 2 + 1
            current = idx

            # 왼쪽 자식과 현재 값과의 1차 비교
            if self.heap[left_child] &amp;lt; self.heap[current]:
                current = left_child
            
            # 오른쪽 자식과의 2차 비교
            if right_child &amp;lt; len(self.heap) and self.heap[right_child] &amp;lt; self.heap[current]:
                current = right_child

            # 자식과 비교 후 부모가 가장 작다면 그대로 반목문 종료
            if current == idx:
                break
            
            # 더 작은 자식이 있다면 그 자식과 현재 값을 swap
            self.heap[idx], self.heap[current] = self.heap[current], self.heap[idx]
            idx = current

    def print_heap(self):
        print(self.heap[1:])


mh = MinHeap()

mh.insert(10)
mh.insert(20)
mh.insert(30)
mh.insert(40)
mh.insert(50)

mh.print_heap()

print(mh.delete())
mh.print_heap()

print(mh.delete())
mh.print_heap()

'''
결과값
[10, 20, 30, 40, 50]
10
[20, 40, 30, 50]
20
[30, 40, 50]
'''&lt;/code&gt;&lt;/pre&gt;</description>
      <category>자료구조, 알고리즘</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/34</guid>
      <comments>https://gudwns5533.tistory.com/34#entry34comment</comments>
      <pubDate>Mon, 8 Jun 2026 22:15:03 +0900</pubDate>
    </item>
    <item>
      <title>공간 복잡도</title>
      <link>https://gudwns5533.tistory.com/33</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;공간 복잡도란?&lt;/b&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공간 복잡도는 '총 공간 = 고정 공간 + 가변 공간' 으로 나타낼 수 있고, 수식으로는 S(P) = c + Sp(n)으로 표현한다.&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;공간 복잡도에 영향을 미치는 요소&lt;/b&gt;&lt;/blockquote&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;자료 구조(Data Structure)&lt;/li&gt;
&lt;li&gt;함수 호출(Function Call)&lt;/li&gt;
&lt;li&gt;할당(Allocation)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;공간 복잡도의 주요 특징&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;공간 복잡도는 일반적으로 입력 데이터 크기(n)에 따라 표현됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&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;O(1), O(n), O(n^2)등의 표기법으로 나타냄&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&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;/ul&gt;
&lt;p data-ke-size=&quot;size18&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;/li&gt;
&lt;li&gt;&lt;b&gt;가변 공간&lt;/b&gt; : 입력 크기에 따라 변화하는 동적으로 필요한 공간. 즉, 해결하려는 문제의 특정 인스턴스에 의존하는 크기를 가진 구조화 변수들을 위해 필요한 공간, 함수가 순환 호출을 할 경우 요구되는 추가 공간을 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;공간 복잡도의 종류&lt;/b&gt;&lt;/blockquote&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;선형 공간 O(n) : 입력 크기에 비례하여 메모리 사용&lt;/li&gt;
&lt;li&gt;로그 공간 O(logn) : 입력 크기의 로그에 비례하려 메모리 사용&lt;/li&gt;
&lt;li&gt;이차 공간 O(n^2) : 입력 크기의 제곱에 비례하여 메모리 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;비효율적인 코드와 효율적인 코드&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1780826097643&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def inefficient(text):
    result_list = []
    
    for i in range(len(text) -1, -1, -1):   # 글자를 맨뒤에서부터 거꾸로 하나씩 가져온다.
        result_list.append(text[i])         # 가져온 글자를 리스트에 저장한다.
        
    return ''.join(result_list)
    
print(inefficient('hello'))
# olleh&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;위의 코드는 입력받은 문자열을 뒤에서부터 하나씩 읽어서, 새로운 리스트에 하나씩 담고, 다시 합치는 방식으로 결과를 출력해준다. 지금은 입력받은 문자열이 5글자지만, 입력받는 문자열이 n글자라면 생겨나는 공간역시 n글자 짜리의 공간이 필요하므로, 이때 공간 복잡도는 O(n)이 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1780826250045&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def reverse_string_efficient(text):
    return text[::-1]

print(reverse_string_efficient(&quot;hello&quot;))
# olleh&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;하지만 파이썬의 슬라이싱 기능을 활용해서 위와 같이 코드를 작성해준다면 결과는 동일하게 출력되지만, 새로운 리스트를 만들고 그 값을 저장해서 반환하는 절차가 사라지기 때문에 공간 복잡도는 O(1)이 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;시소 관계 (Trade-off)&lt;/b&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;시간 복잡도와 공간 복잡도는 한쪽을 최적화하면 다른 한쪽이 희생되는 시소 관계 (Trade-off)에 있다.&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;&lt;b&gt;시간을 아끼고 공간을 더 쓰기 (Time-efficient, Space-consuming)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 대부분의 고성능 알고리즘이 택하는 방향.&lt;/li&gt;
&lt;li&gt;매번 복잡한 연산을 새로 하는 대신, 계산 결과를 메모리에 미리 캐싱해두고 필요할 때 바로 꺼내 써서 속도를 올리는 방식이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;공간을 아끼고 시간을 더 쓰기 (Space-efficient, Time-consuming)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리가 극도로 제한된 환경에서 사용.&lt;/li&gt;
&lt;li&gt;추가 메모리를 전혀 쓰지 않고, 값을 찾을 때까지 데이터를 처음부터 끝까지 다시 돌며 계산을 반복한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;현대 프로그래밍에서의 방향성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;과거엔 코드가 메모리를 조금만 많이 써도 프로그램이 작동하지 않았기 때문에 공간복잡도가 우선이었음.&lt;/li&gt;
&lt;li&gt;현대는 하드웨어의 발전으로 속도(레이턴시)의 기준이 훨씬 올라감.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;따라서, 메모리(공간)를 조금 더 여유롭게 쓰더라도, 더 빠른 속도(시간)를 제공하기 위해 설계한다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&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; : 처리해야 할 데이터가 수천만, 수억 건이 되면 보조 공간을 쓰는 것만으로도 엄청난 크기의 메모리가 사용되어 시스템이 다운(OOM, Out Of Memory)될 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;임베디드 및 모바일 환경&lt;/b&gt; : 스마트 워치, IoT 기기, 스마트폰 앱 환경은 컴퓨터 서버만큼 메모리가 여유롭지 않다. 따라서 제한된 메모리 안에서 앱이 부드럽게 돌아가게 하기 위해 공간 최적화가 필수이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 결론은, 하드웨어가 아무리 좋아졌다 해도 좋은 개발자라면 시간 복잡도, 공간 복잡도를 항상 염두해두고, 내가 개발하고 있는 서비스가 어떤 환경에서 돌아가는지, 데이터의 총 크기는 얼마인지, 메모리는 얼마나 필요한지 모든걸 따져보고 시간과 공간의 균형을 잘 찾는 것이라 생각한다.&lt;/p&gt;</description>
      <category>자료구조, 알고리즘</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/33</guid>
      <comments>https://gudwns5533.tistory.com/33#entry33comment</comments>
      <pubDate>Sun, 7 Jun 2026 19:22:48 +0900</pubDate>
    </item>
    <item>
      <title>시간 복잡도</title>
      <link>https://gudwns5533.tistory.com/32</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;알고리즘의 효율성&lt;/b&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;이다. 알고리즘의 효율성은 크게 &lt;b&gt;시간 복잡도&lt;/b&gt;와 &lt;b&gt;공간 복잡도&lt;/b&gt;로 나누어 평가할 수 있다. 이 두 가지 복잡도는 알고리즘의 성능을 결정짓는 핵심 요소로, 알고리즘을 설계하고 최적화하는 데 있어 중요한 역할을 한다. 시간 복잡도는 &lt;b&gt;알고리즘을 실행하는 데 필요한 시간&lt;/b&gt;을, 공간 복잡도는 &lt;b&gt;알고리즘을 실행하는 데 필요한 메모리 공간&lt;/b&gt;을 의미한다. 이 두 가지 복잡도를 이해하고 최적화하는 것은 효율적인 알고리즘을 설계하는 데 필수적이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;시간 복잡도 (Time Complexity)&lt;/b&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;시간 복잡도란 알고리즘의 효율성을 판단하기 위한 지표로서, 알고리즘의 절대적인 실행 시간이 아닌, 알고리즘을 수행하는데 사용되는 연산들이 몇 번 이루어지는가에 대한 것을 수로 표기한 것이다. 여기서 연산은 산술, 대입, 비교, 이동을 말한다. 시간 복잡도 성능 측정에 사용되는 표기법은 크게 세 가지로 나뉘며, Big-O, Big-Omega( &lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;&amp;Omega; ), Big-Theta( &lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;&amp;theta; )가 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Big-Omega(&amp;nbsp;&amp;Omega; ) : 최선(Best Case)의 경우&lt;/li&gt;
&lt;li&gt;Big-Theta(&amp;nbsp;&amp;theta; ) : 평균(Average Case)의 경우&lt;/li&gt;
&lt;li&gt;Big-O : 최악(Worst Case)의 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘 성능 평가시에는 데이터가 최악으로 주어졌을때도 처리가 가능한지를 판단해야 하므로 빅-오 표기법이 가장 중요하다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Big-O 표기법&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;783&quot; data-origin-height=&quot;454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xPzTN/dJMcadPKHFO/AkKCIRdwRUYF994gZ7QRG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xPzTN/dJMcadPKHFO/AkKCIRdwRUYF994gZ7QRG1/img.png&quot; data-alt=&quot;시간 복잡도 함수의 그래프&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xPzTN/dJMcadPKHFO/AkKCIRdwRUYF994gZ7QRG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxPzTN%2FdJMcadPKHFO%2FAkKCIRdwRUYF994gZ7QRG1%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;783&quot; height=&quot;454&quot; data-origin-width=&quot;783&quot; data-origin-height=&quot;454&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;시간 복잡도 함수의 그래프&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;Better&amp;gt;&amp;nbsp; &amp;nbsp;O(1) &amp;lt; O(logn) &amp;lt; O(n) &amp;lt; O(n &amp;times; logn) &amp;lt; O(n^2) &amp;lt; O(2^n) O(n!)&amp;nbsp; &amp;nbsp;&amp;lt;Worse&amp;gt;&lt;/p&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;O(1)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상수 시간(Constant Time) : 입력값(n)의 크기 증가와 관계 없이 실행 시간이 동일하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfCVfA/dJMcabRXjo8/czSkOkBe12KCo2fMzIKvRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfCVfA/dJMcabRXjo8/czSkOkBe12KCo2fMzIKvRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfCVfA/dJMcabRXjo8/czSkOkBe12KCo2fMzIKvRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfCVfA%2FdJMcabRXjo8%2FczSkOkBe12KCo2fMzIKvRK%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;245&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;322&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;입력값(n)의 크기와 관계 없이 동일한 수의 스텝이 필요하다.&lt;/li&gt;
&lt;li&gt;문제를 해결하는데 오직 한 단계만 처리함.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780751900494&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def Constant_Time(lst):
    print(lst[0])&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;O(logn)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그 시간(Logarithmic Time) : 입력값(n)의 크기가 증가함에 따라 매 실행 시간이 1/2 줄어든다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;328&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BVJ9K/dJMcaf7TrKl/Ggnx7DBrp5k1r3EcKRkCGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BVJ9K/dJMcaf7TrKl/Ggnx7DBrp5k1r3EcKRkCGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BVJ9K/dJMcaf7TrKl/Ggnx7DBrp5k1r3EcKRkCGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBVJ9K%2FdJMcaf7TrKl%2FGgnx7DBrp5k1r3EcKRkCGK%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;472&quot; height=&quot;328&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;328&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;문제를 해결하는데 필요한 단계들이 연산마다 특정 요인에 의해 줄어듬.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이진 검색 알고리즘&lt;/b&gt;(Binary Search)이 O(logn)의 시간 복잡도를 가진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이진 검색 알고리즘에서는 원하는 값을 탐색할 때, 매 스텝마다 절반의 아이템을 없앤다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;따라서 입력값이 2배가 되어도 작업을 수행하는 데 필요한 스텝은 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;611&quot; data-origin-height=&quot;332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsYIAL/dJMcadh0d5D/gTfbOqREPJWHG4dKkONKz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsYIAL/dJMcadh0d5D/gTfbOqREPJWHG4dKkONKz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsYIAL/dJMcadh0d5D/gTfbOqREPJWHG4dKkONKz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsYIAL%2FdJMcadh0d5D%2FgTfbOqREPJWHG4dKkONKz1%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;400&quot; height=&quot;217&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1780753387670&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def log_time(lst, target):
    low = 0
    high = len(lst)-1
    
    while low &amp;lt;= high:
    	mid = (low + high) // 2
        if lst[mid] == target:   # 타겟이 정중앙에 위치
            return mid
        elif lst[mid] &amp;lt; target:  # 타겟이 중간보다 오른쪽에 있음 (왼쪽 절반을 버림)
            low = mid + 1
        else:                    # 타겟이 중간보다 왼쪽에 있음 (오른쪽 절반을 버림)
            high = mid - 1
    
    return False&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;O(n)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선형 시간(Linear Time) : 입력값(n)의 크기가 증가함에 따라 실행 시간 또한 같은 비율로 증가한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;463&quot; data-origin-height=&quot;316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6ToOg/dJMcacciBbe/4WiuD81BLoAiwiVgqHRhi1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6ToOg/dJMcacciBbe/4WiuD81BLoAiwiVgqHRhi1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6ToOg/dJMcacciBbe/4WiuD81BLoAiwiVgqHRhi1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6ToOg%2FdJMcacciBbe%2F4WiuD81BLoAiwiVgqHRhi1%2Fimg.jpg&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;463&quot; height=&quot;316&quot; data-origin-width=&quot;463&quot; data-origin-height=&quot;316&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;입력값이 증가함에 따라 스텝의 수도 비례해서 증가한다.&lt;/li&gt;
&lt;li&gt;문제를 해결하기 위한 단계의 수와 입력값 n이 1:1 관계를 가짐.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780753579879&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def Linear_time(lst):
    for item in lst
    	print(item)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;O(nlogn)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선형 로그 시간(Linearithmic Time) : 입력값(n)의 크기가 증가함에 따라 실행 시간이 nlogn 만큼 증가한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;857&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bH3BNx/dJMcahdC4E9/dhAC8XObP41AjkKmfKHMo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bH3BNx/dJMcahdC4E9/dhAC8XObP41AjkKmfKHMo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bH3BNx/dJMcahdC4E9/dhAC8XObP41AjkKmfKHMo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbH3BNx%2FdJMcahdC4E9%2FdhAC8XObP41AjkKmfKHMo1%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;298&quot; data-origin-width=&quot;857&quot; data-origin-height=&quot;426&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;데이터를 반으로 나누어 각각 정렬하고 합치는 정렬 알고리즘에서 주로 사용된다.&lt;/li&gt;
&lt;li&gt;문제를 해결하기 위한 단계의 수가 (n &amp;times; logn)번 만큼의 수행시간을 가진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;O(nlogn)의 시간 복잡도를 가지는 알고리즘&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;병합 정렬 (Merge Sort)&lt;/li&gt;
&lt;li&gt;힙 정렬 (Heap Sort)&lt;/li&gt;
&lt;li&gt;퀵 정렬 (Quick Sort)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780754998188&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def n_logn(n):
    for i in range(n):                # 바깥 루프는 n번 반복
    	j = 1
        while j &amp;lt; n:                  # 안쪽 루프는 2배씩 커지면서 실행횟수가 logn이 됨.
            print(f'i: {i}, j: {j}')
            j = j * 2&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;O(n^2)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이차 시간(Quadratic Time&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;) : 입력값(n)의 크기가 증가함에 따라 실행 시간이 제곱만큼 증가한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;314&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZ6MO3/dJMcagZ1ZqF/ZNm3sCgcmxCpDkywuMNyI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZ6MO3/dJMcagZ1ZqF/ZNm3sCgcmxCpDkywuMNyI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZ6MO3/dJMcagZ1ZqF/ZNm3sCgcmxCpDkywuMNyI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZ6MO3%2FdJMcagZ1ZqF%2FZNm3sCgcmxCpDkywuMNyI0%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;245&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;314&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;중첩 반복문에서 발생하며, 입력값의 제곱만큼의 스텝이 필요하다.&lt;/li&gt;
&lt;li&gt;문제를 해결하기 위한 단계의 수는 입력값 n의 제곱.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;O(n^2)의 시간 복잡도를 가지는 알고리즘&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;선택 정렬 (Selection Sort)&lt;/li&gt;
&lt;li&gt;버블 정렬 (Bubble Sort)&lt;/li&gt;
&lt;li&gt;삽입 정렬 (Insertion Sort)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780755409720&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;def Quadratic_time(lst):
    for i in lst:
    	for j in lst:
        	print(f'({i}, {j})')
        print()&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;반복문의 개수와 시간 복잡도의 관계&lt;/b&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;/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;&lt;b&gt;1. 독립적인 루프 &amp;rarr; O(n)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1780756034634&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for i in range(n):
    pass
for j in range(n):
    pass&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;반복문이 중첩되지 않고 단순히 여러개 있다면 계산은 단순히 n번만 이루어 지므로 시간 복잡도는 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;size18&quot;&gt;&lt;b&gt;2. 중첩 반복문 &amp;rarr; O(n^2)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780756157496&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for i in range(n):
    for j in range(n):
    	pass&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;반복문 안에 또 반복문이 들어간 중첩구조이다. 이러면 계산이 n^2번 이루어 지므로 시간 복잡도는 O(n^2)이 된다.&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;&lt;b&gt;3. 변수가 2배씩 증가 / 감소하는 반복문 &amp;rarr; O(logn)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780756452953&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;i = 1
while i &amp;lt; n:
    i = i * 2   # or i = i // 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수가 1씩 커지는게 아니라 매번 2배 혹은 절반으로 줄어드는 구조. 시간 복잡도는 O(logn)이 된다.&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;&lt;b&gt;4. 위의 상황의 중첩 반복문 &amp;rarr; O(nlogn)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780756625670&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for i in range(n):
    j = 1
    while j &amp;lt; n:
    	j = j * 2    # or j = j // 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 루프는 실행 횟수가 logn이 되고 외부 루프는 실행 회수가 n이 되므로 시작 복잡도는 O(nlogn)이 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;리스트(List)의 주요 함수들의 시간 복잡도&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 208px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 9.37981%; height: 21px; text-align: center;&quot;&gt;함수&lt;/td&gt;
&lt;td style=&quot;width: 10.5425%; height: 21px; text-align: center;&quot;&gt;시간 복잡도&lt;/td&gt;
&lt;td style=&quot;width: 80.0776%; height: 21px; text-align: center;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 9.37981%; height: 17px; text-align: center;&quot;&gt;append()&lt;/td&gt;
&lt;td style=&quot;width: 10.5425%; height: 17px; text-align: center;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;width: 80.0776%; height: 17px;&quot;&gt;리스트의 맨 뒤에 원소만 추가하므로 데이터를 옮길 필요가 없다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 9.37981%; height: 17px; text-align: center;&quot;&gt;insert()&lt;/td&gt;
&lt;td style=&quot;width: 10.5425%; height: 17px; text-align: center;&quot;&gt;O(n)&lt;/td&gt;
&lt;td style=&quot;width: 80.0776%; height: 17px;&quot;&gt;특정 인덱스에 원소를 삽입하게되면 모든 데이터를 뒤로 밀어내야 하므로 데이터의 개수(n)에 비례해서 시간이 걸린다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 9.37981%; height: 17px; text-align: center;&quot;&gt;pop()&lt;/td&gt;
&lt;td style=&quot;width: 10.5425%; height: 17px; text-align: center;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;width: 80.0776%; height: 17px;&quot;&gt;리스트의 맨 뒤 요소만 꺼내서 삭제하면 되므로 데이터 이동이 없음.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 9.37981%; height: 17px; text-align: center;&quot;&gt;&lt;span style=&quot;background-color: #efefef; color: #333333; text-align: start;&quot;&gt;remove()&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 10.5425%; height: 17px; text-align: center;&quot;&gt;O(n)&lt;/td&gt;
&lt;td style=&quot;width: 80.0776%; height: 17px;&quot;&gt;리스트에서 첫 번째부터 타겟값을 찾기위해 O(n) 시간 복잡도가 생기므로 결국 O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 9.37981%; height: 17px; text-align: center;&quot;&gt;&lt;span style=&quot;background-color: #efefef; color: #333333; text-align: start;&quot;&gt;sort()&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 10.5425%; height: 17px; text-align: center;&quot;&gt;O(nlogn)&lt;/td&gt;
&lt;td style=&quot;width: 80.0776%; height: 17px;&quot;&gt;파이썬은 Timsort라는 정렬 알고리즘을 사용하므로 최악의 상황에도 O(nlogn)보장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 9.37981%; height: 17px; text-align: center;&quot;&gt;len()&lt;/td&gt;
&lt;td style=&quot;width: 10.5425%; height: 17px; text-align: center;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;width: 80.0776%; height: 17px;&quot;&gt;리스트를 만들 때 내부적으로 원소의 개수를 저장해두므로 데이터가 많아도 O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 9.37981%; text-align: center; height: 17px;&quot;&gt;extend()&lt;/td&gt;
&lt;td style=&quot;width: 10.5425%; text-align: center; height: 17px;&quot;&gt;O(n)&lt;/td&gt;
&lt;td style=&quot;width: 80.0776%; height: 17px;&quot;&gt;추가되는 새로운 데이터 개수에 비례하므로 시간 복잡도는 O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 9.37981%; text-align: center; height: 17px;&quot;&gt;index()&lt;/td&gt;
&lt;td style=&quot;width: 10.5425%; text-align: center; height: 17px;&quot;&gt;O(n)&lt;/td&gt;
&lt;td style=&quot;width: 80.0776%; height: 17px;&quot;&gt;리스트의 맨 앞부터 타겟값이 나올 때까지 하나씩 선형 탐색 해야 하므로 최악의 경우 O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 9.37981%; text-align: center; height: 17px;&quot;&gt;reverse()&lt;/td&gt;
&lt;td style=&quot;width: 10.5425%; text-align: center; height: 17px;&quot;&gt;O(n)&lt;/td&gt;
&lt;td style=&quot;width: 80.0776%; height: 17px;&quot;&gt;리스트의 순서를 거꾸로 뒤집어야 하므로 데이터의 개수 n에 비례해서 시간 복잡도도 O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 9.37981%; text-align: center; height: 17px;&quot;&gt;sorted()&lt;/td&gt;
&lt;td style=&quot;width: 10.5425%; text-align: center; height: 17px;&quot;&gt;O(nlogn)&lt;/td&gt;
&lt;td style=&quot;width: 80.0776%; height: 17px;&quot;&gt;sort와 동일한 알고리즘을 사용하므로 O(nlogn)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 9.37981%; text-align: center; height: 17px;&quot;&gt;count()&lt;/td&gt;
&lt;td style=&quot;width: 10.5425%; text-align: center; height: 17px;&quot;&gt;O(n)&lt;/td&gt;
&lt;td style=&quot;width: 80.0776%; height: 17px;&quot;&gt;처음부터 끝까지 모든 원소를 전부 확인해야 하므로 O(n)소요.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;비효율적인 코드와 효율적인 코드&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1780761266578&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def inefficient(n):
    total = 0
    
    for i in range(1, n+1):  # for문을 활용해 1부터 n까지 하나씩 더해나간다.
    	total += i
    return total
    
print(inefficient(10))
# 55&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;테스트처럼 n=10이라고 하면 반복문이 10회 반복된다. 입력 데이터 크기 n에 비례해서 연산 횟수가 늘어나기 때문에 이 코드의 시간 복잡도는 O(n)이라고 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1780762038578&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def efficient(n):
    return n * (n + 1) // 2  # for문 대신 공식으로 한번에 계산
    
print(efficient(10))
# 55&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;이렇게 코드를 구성한다면 n의 값과는 상관없이 사칙연산만 하고 결과를 알 수 있기 때문에, 데이터 크기와 상관없이 실행 속도가 일정한 시간 복잡도인 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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자료구조, 알고리즘</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/32</guid>
      <comments>https://gudwns5533.tistory.com/32#entry32comment</comments>
      <pubDate>Sun, 7 Jun 2026 01:29:15 +0900</pubDate>
    </item>
    <item>
      <title>정규화</title>
      <link>https://gudwns5533.tistory.com/31</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;정규화(Normalization)란 데이터베이스 테이블을 효율적으로 구조화하는 작업을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중복 데이터를 줄이고 데이터가 꼬이지 않도록 테이블을 나누는 과정을 통해 데이터 무결성을 유지하고 유지보수의 편리성을 증가시킨다.&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;size18&quot;&gt;&lt;b&gt;1. 제1정규형 (1NF)&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. 제2정규형 (2NF)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 제1정규형을 만족하고, 기본키가 아닌 모든 속성이 기본키에 완전 종속되어야 한다.&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;&lt;b&gt;3. 제3정규형 (3NF)&lt;/b&gt;&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;* 이행적 종속 : A &amp;rarr; B 이고 B &amp;rarr; C 이면 A &amp;rarr; C 가 성립하는 관계를 말한다.&lt;/p&gt;
&lt;pre id=&quot;code_1780661802094&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE student_course (
    student_id INT,
    student_name VARCHAR(50),
    course_name VARCHAR(50),
    professor_name VARCHAR(50),
    professor_phone VARCHAR(20)
);&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;size18&quot;&gt;&lt;b&gt;학생 테이블&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780663859505&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE student (
    student_id INT PRIMARY KEY,
    student_name VARCHAR(20)
);&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;size18&quot;&gt;&lt;b&gt;교수 테이블&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780663886411&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE professor (
    professor_id INT PRIMARY KEY,
    professor_name VARCHAR(20),
    professor_phone VARCHAR(20)
);&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;size18&quot;&gt;&lt;b&gt;과목 테이블&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780663907207&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE course (
    course_id INT PRIMARY KEY,
    course_name VARCHAR(50),
    professor_id INT,
    FOREIGN KEY (professor_id) REFERENCES professor(professor_id)
);&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;과목 테이블은 professor_id를 통해 교수 테이블과 연결된다.&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;&lt;b&gt;수강 테이블&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780663920383&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE enroll (
    student_id INT,
    course_id INT,
    FOREIGN KEY (student_id) REFERENCES student(student_id),
    FOREIGN KEY (course_id) REFERENCES course(course_id),
    PRIMARY KEY (student_id)
);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;외래키 (Foreign Key)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외래키는 관계형 데이터베이스에서 한 테이블의 컬럼 중 다른 테이블의 행을 식별할 수 있는 키를 말한다.&lt;/p&gt;
&lt;pre id=&quot;code_1780666531179&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE profile (
    idx INT NOT NULL,
    height DOUBLE,
    weight DOUBLE,
    mbti VARCHAR(10),
    FOREIGN KEY (idx) REFERENCES member(idx)
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 프로필 테이블을 작성한다면 profile.idx는 member.idx를 참조하게 된다. 즉 member에 있는 회원만 profile을 추가할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1780666822217&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;insert into profile values(1, 160, 50, 'ISTJ');
insert into profile values(3, 170, 70, 'ESTP');
insert into profile values(4, 175, 80, 'ENFP');
insert into profile values(6, 175, 80, 'ENFJ');
-- insert into profile values (7, 175, 80, 'ENFJ'); 외래키 제약조건 위배&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 member테이블에 idx=7에 해당하는 회원이 없으므로 마지막 쿼리는 외래키 제약조건 위배로 인해 실패하게된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;JOIN&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JOIN은 두 개 이상의 테이블을 연결해서 조회하는 SQL문법이다. 정규화로 인해 여러개로 나눠진 테이블에서 필요한 정보를 한 번에 보기 위해 JOIN을 사용한다.&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;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bR0X3s/dJMcaiczjVB/pSqs53kQHO5xqwEvYuLEh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bR0X3s/dJMcaiczjVB/pSqs53kQHO5xqwEvYuLEh0/img.png&quot; data-alt=&quot;왼쪽 테이블 (member)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bR0X3s/dJMcaiczjVB/pSqs53kQHO5xqwEvYuLEh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbR0X3s%2FdJMcaiczjVB%2FpSqs53kQHO5xqwEvYuLEh0%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;122&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;왼쪽 테이블 (member)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;201&quot; data-origin-height=&quot;87&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/by81a0/dJMcaaS6glk/ckaW7D0FztzwE8qXjGDcik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/by81a0/dJMcaaS6glk/ckaW7D0FztzwE8qXjGDcik/img.png&quot; data-alt=&quot;오른쪽 테이블 (profile)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/by81a0/dJMcaaS6glk/ckaW7D0FztzwE8qXjGDcik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fby81a0%2FdJMcaaS6glk%2FckaW7D0FztzwE8qXjGDcik%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;201&quot; height=&quot;87&quot; data-origin-width=&quot;201&quot; data-origin-height=&quot;87&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;오른쪽 테이블 (profile)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;inner join&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;두 테이블에 모두 연결되는 데이터만 조회한다. (교집합)&lt;/p&gt;
&lt;pre id=&quot;code_1780723543470&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select m.idx, m.userid, m.name, m.gender, p.mbti from member as m inner join profile as p on m.idx = p.idx;&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;260&quot; data-origin-height=&quot;87&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctCX5Z/dJMcadhZ5z5/Ehktc3oWHdtkfWCXkqw9f1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctCX5Z/dJMcadhZ5z5/Ehktc3oWHdtkfWCXkqw9f1/img.png&quot; data-alt=&quot;inner join의 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctCX5Z/dJMcadhZ5z5/Ehktc3oWHdtkfWCXkqw9f1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctCX5Z%2FdJMcadhZ5z5%2FEhktc3oWHdtkfWCXkqw9f1%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;260&quot; height=&quot;87&quot; data-origin-width=&quot;260&quot; data-origin-height=&quot;87&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;inner join의 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;left join&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;왼쪽 테이블의 데이터는 모두 보여주고, 오른쪽 테이블에 연결되는 데이터가 있다면 함께 보여준다. 이때 프로필이 없는 회원은 NULL값으로 출력된다.&lt;/p&gt;
&lt;pre id=&quot;code_1780723895770&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select m.idx, m.userid, m.name, m.gender, p.mbti from member as m left join profile as p on m.idx = p.idx;&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;261&quot; data-origin-height=&quot;143&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3eJxT/dJMcafGTEiH/prDkRWKvawdqezdrlkT3W0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3eJxT/dJMcafGTEiH/prDkRWKvawdqezdrlkT3W0/img.png&quot; data-alt=&quot;left join의 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3eJxT/dJMcafGTEiH/prDkRWKvawdqezdrlkT3W0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3eJxT%2FdJMcafGTEiH%2FprDkRWKvawdqezdrlkT3W0%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;261&quot; height=&quot;143&quot; data-origin-width=&quot;261&quot; data-origin-height=&quot;143&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;left join의 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;right join&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;오른쪽 테이블의 데이터는 모두 보여주고, 왼쪽 테이블에 연결되는 데이터가 있다면 함께 보여준다. inner join과 같은 결과가 나온다.&lt;/p&gt;
&lt;pre id=&quot;code_1780724100921&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select m.idx, m.userid, m.name, m.gender, p.mbti from member as m right join profile as p on m.idx = p.idx;&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;262&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMpVQE/dJMcabRXcle/n1iik9WK9FavsWXoKRWxi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMpVQE/dJMcabRXcle/n1iik9WK9FavsWXoKRWxi0/img.png&quot; data-alt=&quot;right join의 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMpVQE/dJMcabRXcle/n1iik9WK9FavsWXoKRWxi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMpVQE%2FdJMcabRXcle%2Fn1iik9WK9FavsWXoKRWxi0%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;262&quot; height=&quot;88&quot; data-origin-width=&quot;262&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;right join의 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;cross join&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 테이블의 모든 조합.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1780724266859&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select userid, name, gender, mbti from member cross join profile;&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;221&quot; data-origin-height=&quot;533&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w8EXZ/dJMcag6PsOJ/hC3e2fJA6Y5CEtDkRZOaP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w8EXZ/dJMcag6PsOJ/hC3e2fJA6Y5CEtDkRZOaP1/img.png&quot; data-alt=&quot;cross join의 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w8EXZ/dJMcag6PsOJ/hC3e2fJA6Y5CEtDkRZOaP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw8EXZ%2FdJMcag6PsOJ%2FhC3e2fJA6Y5CEtDkRZOaP1%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;221&quot; height=&quot;533&quot; data-origin-width=&quot;221&quot; data-origin-height=&quot;533&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;cross join의 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>MySQL</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/31</guid>
      <comments>https://gudwns5533.tistory.com/31#entry31comment</comments>
      <pubDate>Sat, 6 Jun 2026 14:38:28 +0900</pubDate>
    </item>
    <item>
      <title>DML</title>
      <link>https://gudwns5533.tistory.com/30</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 voca라는 테이블을 먼저 만들어 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1780647078982&quot; class=&quot;sql&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;-- 테이블 생성
create table voca (
	eng varchar(50) primary key,
    kor varchar(50) not null,
    lev int default 1,
    regdate datetime default now()
);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DML (Date Manipulation Language, 데이터 조작어)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. select : 데이터를 조회(검색)&lt;/p&gt;
&lt;pre id=&quot;code_1780646484469&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 테이블 전체 데이터를 조회 (서버에 부하를 주기때문에 좋지 않은 방법)
select * from 테이블명;

-- 특정 컬럼의 데이터만 조회
select 컬럼명 from 테이블명;

-- 여러 컬럼 데이터 조회
select 컬럼명1, 컬럼명2, ... from 테이블명;&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;2. insert : 데이터를 테이블에 삽입&lt;/p&gt;
&lt;pre id=&quot;code_1780646868852&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 컬럼명 생략
-- insert into 테이블명 values (값1, 값2, ...);
insert into voca values ('apple', '사과', 1, now());
-- 값 대신 null입력 가능
insert into voca values ('orange', '오렌지', null, null);

-- 컬럼명 지정
-- insert int 테이블명(컬럼1, 컬럼2, ...) values (값1, 값2, ...);
insert into voca (eng, kor) values('banana', '바나나');
insert into voca (eng, kor)values ('melon', '메론');
insert into voca (eng, kor, lev)values ('avocado', '아보카도', 2);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. update : 데이터를 수정&lt;/p&gt;
&lt;pre id=&quot;code_1780647641291&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- update 테이블명 set 바꾸고자 하는 데이터;
update voca set lev=1;

-- update 테이블명 set 데이터값 where 조건
update voca set lev=2 where eng='melon';&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOLNo8/dJMcabErujO/QBHcafUcH2nxWIxzPxkuf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOLNo8/dJMcabErujO/QBHcafUcH2nxWIxzPxkuf1/img.png&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;122&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.6267%; margin-right: 10px;&quot; data-widthpercent=&quot;33.4&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOLNo8/dJMcabErujO/QBHcafUcH2nxWIxzPxkuf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOLNo8%2FdJMcabErujO%2FQBHcafUcH2nxWIxzPxkuf1%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;302&quot; height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Pjcis/dJMcabLh1KQ/yFBspxbrLkYq5OTwklsBmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Pjcis/dJMcabLh1KQ/yFBspxbrLkYq5OTwklsBmk/img.png&quot; data-origin-width=&quot;303&quot; data-origin-height=&quot;122&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.7347%; margin-right: 10px;&quot; data-widthpercent=&quot;33.51&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Pjcis/dJMcabLh1KQ/yFBspxbrLkYq5OTwklsBmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPjcis%2FdJMcabLh1KQ%2FyFBspxbrLkYq5OTwklsBmk%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;303&quot; height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bx94pg/dJMcaftjdOc/N3CeDsV9AVBC7Wyh7MuKY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bx94pg/dJMcaftjdOc/N3CeDsV9AVBC7Wyh7MuKY0/img.png&quot; data-origin-width=&quot;304&quot; data-origin-height=&quot;124&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.313%;&quot; data-widthpercent=&quot;33.09&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bx94pg/dJMcaftjdOc/N3CeDsV9AVBC7Wyh7MuKY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbx94pg%2FdJMcaftjdOc%2FN3CeDsV9AVBC7Wyh7MuKY0%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;304&quot; height=&quot;124&quot;/&gt;&lt;/span&gt;&lt;/div&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;4. delete : 데이터를 삭제&lt;/p&gt;
&lt;pre id=&quot;code_1780648205019&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- delete from 테이블명;
delete from voca;

-- delete from 테이블명 where 조건;
delete from voca where eng = 'banana'&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;202&quot; data-origin-height=&quot;42&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKzm6H/dJMcahkqiFN/uIXQ7SDxPwkKwIgDx2ZkY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKzm6H/dJMcahkqiFN/uIXQ7SDxPwkKwIgDx2ZkY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKzm6H/dJMcahkqiFN/uIXQ7SDxPwkKwIgDx2ZkY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKzm6H%2FdJMcahkqiFN%2FuIXQ7SDxPwkKwIgDx2ZkY1%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;202&quot; height=&quot;42&quot; data-origin-width=&quot;202&quot; data-origin-height=&quot;42&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;305&quot; data-origin-height=&quot;106&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G85rX/dJMcadPJ9cd/FQbKkjupgovvbyBh4efmF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G85rX/dJMcadPJ9cd/FQbKkjupgovvbyBh4efmF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G85rX/dJMcadPJ9cd/FQbKkjupgovvbyBh4efmF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG85rX%2FdJMcadPJ9cd%2FFQbKkjupgovvbyBh4efmF0%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;305&quot; height=&quot;106&quot; data-origin-width=&quot;305&quot; data-origin-height=&quot;106&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DML 연산자&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;산술 연산자 : +, -, *, /, mod (나눈 나머지 값), div (몫)&lt;/li&gt;
&lt;li&gt;비교 연산자 : =, &amp;lt;, &amp;gt;, &amp;lt;=, &amp;gt;=, &amp;lt;&amp;gt; (같지않다)&lt;/li&gt;
&lt;li&gt;대입 연산자 : =&lt;/li&gt;
&lt;li&gt;논리 연산자 : and, or, not, xor&lt;/li&gt;
&lt;li&gt;is : 양쪽의 피연산자가 모두 같으면 True, 아니면 False&lt;/li&gt;
&lt;li&gt;between A and B : A 보다는 크거나 같고, B 보다는 작거나 같으면 True, 아니면 False&lt;/li&gt;
&lt;li&gt;in : 매개변수로 전달된 리스트에 값이 존재하면 True, 아니면 False&lt;/li&gt;
&lt;li&gt;like : 패턴으로 문자열을 검색하여 값이 존재하면 True, 아니면 False&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780648841642&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 포인트가 150이상인 멤버의 아이디, 이름, 포인트를 검색
select userid, name, point from member where point &amp;gt;= 150;

-- 포인트가 150이상이고, 200이하인 멤버의 아이디, 이름, 포인트, 성별을 검색
select userid, name, point, gender from member where point &amp;gt;= 150 and point &amp;lt;= 200;
select userid, name, point, gender from member where point between 150 and 200;

-- 로그인
select userid from member where userid='apple' and userpw='1111';  -- select가 된다면 로그인 성공
select userid from member where userid='apple' and userpw='1112';  -- select가 안된다면 로그인 불가

-- 이름이 김사과, 반하나, 오렌지인 사람의 모든 정보를 검색
select * from member where name='김사과' or name='반하나' or name='오렌지';
select * from member where name in ('김사과', '반하나', '오렌지');&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780648887890&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- regdate가 null인 멤버 검색
select * from member where regdate = null;  -- select되지않음
select * from member where regdate is null;  -- null값을 가진 멤버를 검색할 때는 is 연산자 사용
select * from member where regdate is not null;

-- 아이디가 a로 시작하는 멤버의 정보를 검색
select * from member where userid like 'a%';
-- 아이디가 a로 끝나는 멤버의 정보를 검색
select * from member where userid like '%a';
-- 아이디가 a를 포함하는 멤버의 정보를 검색
select * from member where userid like '%a%';
-- 이메일이 '.com'으로 끝나는 멤버의 정보를 검색
select * from member where email like '%.com';
-- 이름이 3자인 멤버를 검색 (_3개 입력)
select * from member where name like '___';
-- 이름이 첫 글자가 '김'씨이며 3자인 멤버를 검색
select * from member where name like '김__';

select * from member order by idx;   -- 오름차순 asc 생략
select * from member order by idx desc;  -- 내림차순
-- 멤버를 포인트 순으로 정렬
select * from member order by point desc;
-- 멤버를 포인트 순으로 정렬. 단 포인트가 같으면 가입 최신순으로 정렬
select * from member order by point desc, regdate desc;

-- limit: 일부 로우(레코드)를 검색
-- limit 검색할 로우의 개수, limit 시작로우(인덱스) 가져올 로우의 개수
select * from member limit 2;
select * from member limit 2, 2;
-- 멤버에서 포인트순으로 내림차순 정렬하고 포인트가 같다면 가입순으로 내림차순 한 뒤 top 3를 검색
select * from member order by point desc, regdate limit 3;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;group&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1780648931873&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select 그룹을 맺을 컬럼 또는 집계함수 from 테이블 group by 그룹을 맺을 컬럼
select 그룹을 맺을 컬럼 또는 집계함수 from 테이블 group by 그룹을 맺을 컬럼 having 그룹에 대한 조건&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780648946842&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select gender from member group by gender;
select gender, count(idx) as 인원수 from member group by gender;
select gender, count(idx) as 인원수 from member group by gender having gender='여자';
select gender, count(idx) as 인원수 from member where gender='여자' group by gender;
-- 성별로 그룹을 맺고, 그 인원수가 3명 이상인 성별을 검색
select gender, count(idx) as 인원수 from member group by gender having count(idx) &amp;gt;= 3;
select gender, count(idx) as 인원수 from member group by gender having 인원수 &amp;gt;= 3;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1780648993546&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/*
멤버 중 포인트가 50이상인 멤버중에서 성별로 그룹을 나눈 뒤, 
각 그룹의 포인트 평균을 구하고 평균의 포인트가 100이상인 성별만 출력
(단, 성별이 남자, 여자 모두 출력된다면 포인트가 높은 성별을 우선으로 출력)
*/

select gender, avg(point) as 평균 from member where point &amp;gt;= 50 group by gender having 평균 &amp;gt;= 100 order by 평균 desc;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬과 비슷한점도 많고 차이점도 많다. 문법이 파이썬에 비해 훨씬 직관적인 느낌이다.&lt;/p&gt;</description>
      <category>MySQL</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/30</guid>
      <comments>https://gudwns5533.tistory.com/30#entry30comment</comments>
      <pubDate>Fri, 5 Jun 2026 17:44:20 +0900</pubDate>
    </item>
    <item>
      <title>DDL</title>
      <link>https://gudwns5533.tistory.com/29</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;테이블&lt;/b&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;/p&gt;
&lt;pre id=&quot;code_1780641887425&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;create table 테이블명(
    컬럼명 데이터타입 제약조건,
    컬럼명2 데이터타입 제약조건,
    컬럼명3 데이터타입 제약조건,
    ...)&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;데이터 타입&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;- 정수형&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;int : -21억부터 +21억까지의 수를 표현&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bigint : 21억이 넘어가는 수를 표현&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;&lt;b&gt;- 실수형&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;float : 소수점 6자리까지 표현&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;double : 부동 소수점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;decimal :&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;size18&quot;&gt;&lt;b&gt;- 문자형&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;char : 고정 길이 문자형, 지정한 크기보다 짧은 글자가 들어와도 무조건 크기만큼 메모리 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;varchar : 가변 길이 문자형, 입력된 글자의 길이에 맞춰 공간을 조절해줌 (최대 65535byte 까지)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;text : 길이를 예측할 수 없는 긴 글&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;binary / varbinary :&amp;nbsp; 글자 이외의 것들을 저장할 때 사용 (binary는 고정 / varbinary 는 가변)&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;&lt;b&gt;- 날짜형&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;date : 연, 월, 일만 저장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;time : 시, 분, 초만 저장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;datetime / timestamp : 연, 월, 일, 시, 분, 초 모두 저장 (timestamp는 컴퓨터가 계산할 수 있도록 저장한다.)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;제약조건&lt;/b&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- not null : Null 값을 허용하지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- unique : 중복값을 허용하지 않음. Null값은 허용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- default : Null 값을 삽입할 때 기본이 되는 값을 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- primary key&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Null값을 허용하지 않는다&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;p data-ke-size=&quot;size16&quot;&gt;- foreign key : 기본키와 쌍으로 연결&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- auto-innocent : 데이터를 직접 삽입하지 못함, 자동 증가되는 숫자를 말한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;데이터베이스 생성하기&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1780644788398&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;create database ai;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;데이터베이스 선택하기&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1780644815566&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;use ai;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DDL&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DDL (Data Definition Language, 데이터 정의어)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. create : 데이터베이스, 테이블, 뷰 등을 생성&lt;/p&gt;
&lt;pre id=&quot;code_1780644830806&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;create table member(
    idx int auto_increment primary key,
    userid varchar(20) unique not null,
    userpw varchar(20) not null,
    name varchar(20) not null,
    hp varchar(20) not null,
    email varchar(50) not null,
    ssn1 char(6) not null,
    ssn2 char(7) not null,
    zipcode varchar(5),
    address1 varchar(100),
    address2 varchar(100),
    address3 varchar(100),
    regdate datetime default now(),
    point int default 1000
);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780644900318&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 테이블 확인하기
desc member;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnZUel/dJMcacDo6q4/1K2EiNWq0z8xRjGcK5tRhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnZUel/dJMcacDo6q4/1K2EiNWq0z8xRjGcK5tRhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnZUel/dJMcacDo6q4/1K2EiNWq0z8xRjGcK5tRhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnZUel%2FdJMcacDo6q4%2F1K2EiNWq0z8xRjGcK5tRhK%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;456&quot; height=&quot;276&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;276&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;2. alter : 기존 객체(테이블 등)의 구조를 변경&lt;/p&gt;
&lt;pre id=&quot;code_1780644998046&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 컬럼 삭제하기
alter table member drop point;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;259&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T27OO/dJMcahEDb9g/0MkPXZIH43LfhR0qcnKQok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T27OO/dJMcahEDb9g/0MkPXZIH43LfhR0qcnKQok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T27OO/dJMcahEDb9g/0MkPXZIH43LfhR0qcnKQok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FT27OO%2FdJMcahEDb9g%2F0MkPXZIH43LfhR0qcnKQok%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;454&quot; height=&quot;259&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;259&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1780645044102&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 컬럼 추가하기
alter table member add point int default 1000;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;457&quot; data-origin-height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/of20I/dJMcabdkCq8/rark1u40vPWgG0F9DwXLX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/of20I/dJMcabdkCq8/rark1u40vPWgG0F9DwXLX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/of20I/dJMcabdkCq8/rark1u40vPWgG0F9DwXLX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fof20I%2FdJMcabdkCq8%2Frark1u40vPWgG0F9DwXLX1%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;457&quot; height=&quot;280&quot; data-origin-width=&quot;457&quot; data-origin-height=&quot;280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1780645090078&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 컬럼 수정하기
alter table member modify column point int default 100;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;455&quot; data-origin-height=&quot;276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zBoUc/dJMcajoRnpQ/QanAmXJ6g3oOR9NT9Lhtn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zBoUc/dJMcajoRnpQ/QanAmXJ6g3oOR9NT9Lhtn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zBoUc/dJMcajoRnpQ/QanAmXJ6g3oOR9NT9Lhtn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzBoUc%2FdJMcajoRnpQ%2FQanAmXJ6g3oOR9NT9Lhtn0%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;455&quot; height=&quot;276&quot; data-origin-width=&quot;455&quot; data-origin-height=&quot;276&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;3. drop : 객체(데이터베이스, 테이블, 뷰, 인덱스 등)를 완전히 삭제&lt;/p&gt;
&lt;pre id=&quot;code_1780645453286&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 테이블 삭제하기
drop table member;&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;4. truncate : 테이블의 모든 데이터를 삭제하지만, 구조는 남김(롤백 불가)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MySQL</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/29</guid>
      <comments>https://gudwns5533.tistory.com/29#entry29comment</comments>
      <pubDate>Fri, 5 Jun 2026 16:46:35 +0900</pubDate>
    </item>
    <item>
      <title>해시 테이블 (Hash Table)</title>
      <link>https://gudwns5533.tistory.com/28</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;해싱과 해시함수 그리고 해시 테이블&lt;/b&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;(Hash Function)란 데이터의 효율적 관리를 목적으로 임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 함수를 말한다. 이 때 매핑 전 원래 데이터의 값을 키(Key), 매핑 후 데이터의 값을 해시값(Hash Value), 매핑하는 과정 자체를 &lt;b&gt;해싱&lt;/b&gt;(Hashing)이라고 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;165&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdg0R1/dJMcaiQ5aYf/4WTGuaEjSvyPcnvIqYnMe0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdg0R1/dJMcaiQ5aYf/4WTGuaEjSvyPcnvIqYnMe0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdg0R1/dJMcaiQ5aYf/4WTGuaEjSvyPcnvIqYnMe0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcdg0R1%2FdJMcaiQ5aYf%2F4WTGuaEjSvyPcnvIqYnMe0%2Fimg.jpg&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;550&quot; height=&quot;165&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;165&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;&lt;b&gt;해시 테이블은&lt;/b&gt; (Key, Value)로 데이터를 저장하는 자료구조 중 하나로 빠르게 데이터를 검색할 수 있는 자료구조이다. 해시 테이블이 빠른 검색속도를 제공하는 이유는 내부적으로 배열(버킷)을 사용하여 데이터를 저장하기 때문이다. 해시 테이블은 각각의 Key값에 해시 함수를 적용해 배열의 고유한 index를 생성하고, 이 index를 활용해 값을 저장하거나 검색, 삭제하게 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;해시 함수(Hash Function)의 역할&lt;/b&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;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;201&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1txSR/dJMb99T9jln/S8wnw30xNvkxzy8p1Bhjy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1txSR/dJMb99T9jln/S8wnw30xNvkxzy8p1Bhjy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1txSR/dJMb99T9jln/S8wnw30xNvkxzy8p1Bhjy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1txSR%2FdJMb99T9jln%2FS8wnw30xNvkxzy8p1Bhjy1%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;745&quot; height=&quot;201&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;201&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;나눗셈법 (Division Method) : 해시 함수 중에서 가장 간단한 알고리즘. 나눗셈을 이용하는 방법은 입력값을 테이블의 크기로 나누고, 그 '나머지'를 테이블의 주소로 사용한다. (주소 = 입력값 % 테이블의 크기) 테이블의 크기를 소수로 정하고 2의 제곱수와 먼 값을 사용해야 효과가 좋다고 알려져 있다.&lt;/li&gt;
&lt;li&gt;자릿수 접기 (Digit Folding) : 각 Key의 문자열을 ASCII 코드로 바꾸고 값을 합한 데이터를 테이블 내의 주소로 사용하는 방법.&lt;/li&gt;
&lt;li&gt;곱셈법 (Multiplication Method) : 숫자로 된 Key값 K와 0과 1사이의 실수 A, 보통 2의 제곱수인 m을 사용하여 다음과 같은 계산을 해준다. h(k)=(kAmod1) x m&lt;/li&gt;
&lt;li&gt;유니버셜 해싱 (Universal Hashing) : 다수의 해시함수를 만들어 집합 H에 넣어두고, 무작위로 해시함수를 선택해 해시값을 만드는 기법.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Key와 Value의 구조&lt;/b&gt;&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Key&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Key는 데이터의 저장 위치를 결정하는 주소&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;고유성 : 하나의 해시 테이블 안에서 Key는 절대 중복될 수 없다. 만약 이미 존재하는 Key로 새로운 Value를 넣는다면 기존 Value는 지워지고 새로운 Value로 덮어씌워지게 된다.&lt;/li&gt;
&lt;li&gt;불변성 : 해시 테이블의 핵심은 Key값이 변하지 않아야 index를 똑같게 구할 수 있다는 것이다. 파이썬의 경우는 str, int, tuple처럼 Immutable 타입만 Key가 될 수 있다. 리스트나 딕셔너리는 Key로 쓸 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Value&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Value는 저장하고 싶은 실제 정보&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;수동성 : Value 자체로는 해시 테이블 내에서 아무런 역할을 할 수 없다. Key가 없다면 Value는 반환될 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780581425439&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;키(Key)         해시 함수 + 나머지 연산         실제 메모리 배열 (Buckets)
+--------+                                     +----------------------------------+
| 김사과 | ----&amp;gt; [ Hash Function ] ----&amp;gt; [0]   | 인덱스 [0]: (&quot;김사과&quot;, &quot;apple&quot;)   |
+--------+                                     +----------------------------------+
| 이메론 | ----&amp;gt; [ Hash Function ] ----&amp;gt; [4]   | 인덱스 [1]: 빈 방 (None)          |
+--------+                                     +----------------------------------+
| 반하나 | ----&amp;gt; [ Hash Function ] ----&amp;gt; [2]   | 인덱스 [2]: (&quot;반하나&quot;, &quot;banana&quot; ) |
+--------+                                     +----------------------------------+
                                               | 인덱스 [3]: 빈 방 (None)          |
                                               +----------------------------------+
                                               | 인덱스 [4]: (&quot;이메론&quot;, &quot;melon&quot;)   |
                                               +----------------------------------+&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;충돌 (Collision)이 발생하는 이유&lt;/b&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;해시 함수가 서로 다른 입력 값에 대해 동일한 해시 테이블 주소를 반환하는 것을 일컫어 '충돌(Collision)'이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 해시 함수든, 그 알고리즘이 아무리 정교하게 설계되었다 하더라도 모든 입력 값에 대해 고유한 해시 값을 만들지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무한한 종류의 Key를 유한한 크기의 테이블 인덱스로 압축하다보니, 확률적으로 같은 인덱스를 배정받는 충돌이 일어날 수 밖에 없다.&lt;/p&gt;
&lt;pre id=&quot;code_1780586228400&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class HashTable:
    def __init__(self):
        self.size = 5
        self.table = [[] for _ in range(self.size)]     # 5개의 비어있는 리스트 생성 (버킷 생성)

    def put(self, key, value):
        bucket_number = key % self.size                 # key를 5로 나눈 나머지로 버킷 번호 계산


        for pair in self.table[bucket_number]:          # 버킷에 같은 key 확인
            if pair[0] == key:
                print(f'[{key}] 키가 이미 존재. 데이터를 업데이트 합니다.')
                pair[1] = value                         # 값 업데이트
                return

        if len(self.table[bucket_number]) &amp;gt; 0:          # 버킷에 같은 key가 있다면 충돌 발생
            print(f'{value} 충돌 이미 데이터가 있습니다.')

        self.table[bucket_number].append([key,value])   # 새로운 데이터면 버킷에 추가


test_table = HashTable()

print(&quot;----- 최종 해시 테이블 상태 -----&quot;)
for i, bucket in enumerate(test_table.table):
    print(f&quot;{i}번 버킷 : {bucket}&quot;)

test_table.put(0, 'apple')
test_table.put(1, 'banana')
test_table.put(2, 'melon')
test_table.put(6, 'orange')     # 충돌 발생
test_table.put(1, 'cherry')     # 값 업데이트 발생

print(&quot;----- 최종 해시 테이블 상태 -----&quot;)
for i, bucket in enumerate(test_table.table):
    print(f&quot;{i}번 버킷 : {bucket}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780586592610&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;----- 최종 해시 테이블 상태 -----
0번 버킷 : []
1번 버킷 : []
2번 버킷 : []
3번 버킷 : []
4번 버킷 : []

orange 충돌 이미 데이터가 있습니다.
[1] 키가 이미 존재. 데이터를 업데이트 합니다.
----- 최종 해시 테이블 상태 -----
0번 버킷 : [[0, 'apple']]
1번 버킷 : [[1, 'cherry'], [6, 'orange']]
2번 버킷 : [[2, 'melon']]
3번 버킷 : []
4번 버킷 : []&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;충돌 해결 방법&lt;/b&gt;&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;체이닝 (Chaining)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;체이닝(Chaining)은 해시 함수로 서로 다른 키에 대해 같은 주소값을 반환해서 충돌이 발생하면 각 데이터를 해당 주소에 있는 링크드 리스트에 삽입하여 문제를 해결하는 기법이다. 충돌이 일어나면 링크드 리스트에 사슬처럼 주렁주렁 엮는다는 의미에서 붙여진 이름이다. 체이닝 기반의 해시 테이블은 데이터 대신 링크드 리스트에 대한 포인터를 관리한다. 체이닝은 오픈 해싱 기법인 동시에 폐쇠 주소법(Closed Addressing) 알고리즘이기도 하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;충돌이 나면 같은 버킷의 데이터를 Linked List 형태로 저장하는 방식.&lt;/li&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;342&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/djL9eF/dJMcaicx1UO/oFh5sPoIHZFsSeNgCkcRpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/djL9eF/dJMcaicx1UO/oFh5sPoIHZFsSeNgCkcRpk/img.png&quot; data-alt=&quot;체이닝 (Chaining)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/djL9eF/dJMcaicx1UO/oFh5sPoIHZFsSeNgCkcRpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdjL9eF%2FdJMcaicx1UO%2FoFh5sPoIHZFsSeNgCkcRpk%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;342&quot; height=&quot;396&quot; data-origin-width=&quot;342&quot; data-origin-height=&quot;396&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;체이닝 (Chaining)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;체이닝 기반 해시 테이블의 탐색&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;찾고자 하는 목표값을 해싱하여 링크드 리스트가 저장되어 있는 주소를 찾는다.&lt;/li&gt;
&lt;li&gt;이 주소를 이용하여 해시 테이블에 저장되어 있는 링크드 리스트에 대한 포인터를 생성한다.&lt;/li&gt;
&lt;li&gt;링크드 리스트의 앞에서부터 뒤까지 차례대로 이동하며 목표값이 저장되어 있는지 비교한다. 목표값과 링크드 리스트 내의 노드 값이 일치하면 해당 노드의 주소를 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;개방 주소법 (Open Addressing)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개방 주소법(Open Addressing)은 충돌이 일어날 때 해시 함수에 의해 얻어진 주소가 아니더라도 얼마든지 다른 주소를 사용할 수 있도록 허용하는 충돌 해결 알고리즘이다. 개방 주소법은 충돌이 일어나면 해시 테이블 내의 새로운 주소를 탐사(Probe)하여 충돌된 데이터를 입력하는 방식으로 동작한다.&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;&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;선형 탐색(Linear Probing)은 가장 간단한 탐사 방법이다. 해시 함수로부터 얻어낸 주소에 이미 다른 데이터가 입력되어 있음을 발견하면, 현재 주소에서 고정 폭으로 다음 주소로 이동한다. 그 주소에 다른 데이터가 있어 충돌이 또 발생한다면 또 다시 그 다음 주소로 이동한다. 이렇게 해서 비어 있는 주소를 찾아내면 데이터를 입력한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;424&quot; data-origin-height=&quot;82&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oO5Z7/dJMcad3g3Fc/5T34lWHZgt4dUxEDkkm5u0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oO5Z7/dJMcad3g3Fc/5T34lWHZgt4dUxEDkkm5u0/img.png&quot; data-alt=&quot;55가 입력될 때 충돌이 발생하면 고정된 폭만큼 이동하면서 빈 주소를 찾는다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oO5Z7/dJMcad3g3Fc/5T34lWHZgt4dUxEDkkm5u0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoO5Z7%2FdJMcad3g3Fc%2F5T34lWHZgt4dUxEDkkm5u0%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;424&quot; height=&quot;82&quot; data-origin-width=&quot;424&quot; data-origin-height=&quot;82&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;55가 입력될 때 충돌이 발생하면 고정된 폭만큼 이동하면서 빈 주소를 찾는다.&lt;/figcaption&gt;
&lt;/figure&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-origin-width=&quot;391&quot; data-origin-height=&quot;109&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYgSvi/dJMcad3g3Fi/Ak3K33BkycKKmhKxwrQK2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYgSvi/dJMcad3g3Fi/Ak3K33BkycKKmhKxwrQK2K/img.png&quot; data-alt=&quot;빈 주소를 찾았다면 데이터를 입력한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYgSvi/dJMcad3g3Fi/Ak3K33BkycKKmhKxwrQK2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYgSvi%2FdJMcad3g3Fi%2FAk3K33BkycKKmhKxwrQK2K%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;391&quot; height=&quot;109&quot; data-origin-width=&quot;391&quot; data-origin-height=&quot;109&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;빈 주소를 찾았다면 데이터를 입력한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;제곱 탐색(Quadratic Probing)의 기본 개념은 선형 탐사와 비교해서 크게 다르지 않다. 선형 탐사가 다음 주소를 위해 고정폭만큼 이동하는 것에 비해 제곱 탐색은 이동폭이 제곱수로 늘어나는것 뿐이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;391&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dKpryp/dJMcageGyni/MWUivcliboCPoIZnBfvfyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dKpryp/dJMcageGyni/MWUivcliboCPoIZnBfvfyK/img.png&quot; data-alt=&quot;충돌 발생 시 1^2, 2^2, 3^2 만큼 이동해가면서 빈 공간을 찾는다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dKpryp/dJMcageGyni/MWUivcliboCPoIZnBfvfyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdKpryp%2FdJMcageGyni%2FMWUivcliboCPoIZnBfvfyK%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;391&quot; height=&quot;130&quot; data-origin-width=&quot;391&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;충돌 발생 시 1^2, 2^2, 3^2 만큼 이동해가면서 빈 공간을 찾는다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;이중 해싱(Double Hashing)은 탐사할 해시값의 규칙성을 없애버려서 클러스터링(해시 테이블의 특정 부분에 데이터가 몰리는 상황)을 방지하는 기법이다. 2개의 해시 함수를 준비해서 하나는 최초의 해시값을 얻을 때, 또 다른 하나는 해시충돌이 일어났을 때 탐사 이동폭을 얻기 위해 사용한다. 이렇게 되면 최초 해시값이 같더라도 탐사 이동폭이 달라지고, 탐사 이동폭이 같더라도 최초 해시값이 달라져 클러스터링을 완화할 수 있다.&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;&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;남은 공간이 없는 해시 테이블에서 충돌이 많이 발생하게 된다. 리해싱(Rehashing)은 이 문제를 해결하고자 해시 테이블의 크기 자체를 늘려서 늘어난 해시 테이블의 크기에 맞추어 테이블 내의 모든 데이터를 다시 해싱한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;391&quot; data-origin-height=&quot;101&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8e34o/dJMcah5LQ9z/aVvzATSn8d5s7wGoFUqA60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8e34o/dJMcah5LQ9z/aVvzATSn8d5s7wGoFUqA60/img.png&quot; data-alt=&quot;해시 테이블의 크기를 늘려 전체 데이터를 다시 해싱하는 기법&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8e34o/dJMcah5LQ9z/aVvzATSn8d5s7wGoFUqA60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8e34o%2FdJMcah5LQ9z%2FaVvzATSn8d5s7wGoFUqA60%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;391&quot; height=&quot;101&quot; data-origin-width=&quot;391&quot; data-origin-height=&quot;101&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;해시 테이블의 크기를 늘려 전체 데이터를 다시 해싱하는 기법&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;파이썬 Dictionary와 해시 테이블&lt;/b&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;자료 조사를 하면서 해시 테이블의 생김새나 기능이 파이썬의 Dictionary와 유사하다는 느낌을 많이 받았다. 그래서 찾아보니 파이썬의 Dictionary는 해시 테이블로 구현된 내장 자료구조라는 걸 알게되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬은 해시 테이블을 딕셔너리에 적용하면서 몇 가지 고유한 방식을 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빠른 속도와 공간의 교환 : 키-값 쌍을 저장할 때 여유 공간(메모리)을 미리 할당해 탐색 시간을 줄인다.&lt;/li&gt;
&lt;li&gt;해시 충돌 처리 : 서로 다른 키가 해시 함수를 거쳐 같은 같은 인덱스를 가리키는 해시 충돌이 발생하면, 파이썬은 다른 빈 공간을 탐색하여 데이터를 넣는 개방 주소법 중 선형 탐색 방법과 리해싱을 사용한다.&lt;/li&gt;
&lt;li&gt;동적 크기 조절 : 테이블의 공간 일정 수준(약 2/3) 이상 차오르면, 자동으로 테이블 크기를 두 배로 늘리고 모든 데이터를 다시 해싱하여 재배치한다.(Rehashing)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;해시 테이블의 장점과 단점&lt;/b&gt;&lt;/blockquote&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 style=&quot;width: 50%;&quot;&gt;장점&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;단점&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;검색 및 접근 속도 : 키를 해시 함수를 통해 인덱스로 변환하므로, 평균 O(1)의 시간 복잡도로 데이터의 겁색, 삽입, 삭제가 가능&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;해시 충돌 : 서로 다른 키가 해시 함수를 거쳐 같은 인덱스를 가리키는 현상이 발생할 수 있다. 이 경우 체이닝이나 개방 주소법 등을 통해 해결해야 하며, 충돌이 잦아지면 성능이 O(N)으로 떨어진다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;키 중심의 데이터 관리 : 중복되지 않는 고유한 키를 통해 데이터를 다루기 때문에 직관적이고 관리가 편하다.&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;데이터 순서 미보장 : 데이터를 저장하는 인덱스가 해시 함수에 의해 무작위로 결정되므로, 데이터 간의 정렬이나 순차적 접근이 필요한 상황에는 적합하지 않다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;효율적인 중복 확인 : 특정 데이터가 이미 존재하는지 (중복 검사) 빠르게 확인할 수 있다.&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;공간 낭비 : 성능 저하를 방지하기 위해 데이터가 채워지기 전부터 미리 빈 메모리 공간(버킷)을 확보해 두어야 하므로 메모리 효율성이 떨어진다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;해시 함수 의존도 : 해시 함수의 성능이 테이블 전체의 성능을 좌우한다. 함수가 복잡하면 해시 값을 계산하는데 시간이 더 소요된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;해시 테이블의 실제 사용&lt;/b&gt;&lt;/blockquote&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; : 방대한 회원 정보 중 ID로 회원을 검색할 때 사용된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;캐싱&lt;/b&gt; : 웹 브라우저나 서버가 자주 찾는 이미지 / 데이터를 URL(Key)과 파일(Value) 형태로 저장해 두고 빠르게 꺼내 쓸 때 사용된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;블록체인 및 암호화&lt;/b&gt;: 사용자의 비밀번호를 해시화해서 안전하게 저장하거나 데이터 위변조를 막을 때 사용된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;라우팅 테이블&lt;/b&gt; : 네트워크 라우터 안에는 목적지 IP 주소(Key)와 데이터를 보낼 다음 목적지 패킷 경로(Value)를 짝지어 놓은 '라우팅 테이블'에 사용된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컴파일러 및 인터프리터의 심볼 테이블&lt;/b&gt; : 프로그래밍 언어를 실행 파일로 컴파일하거나 해석할 때, 컴퓨터는 사용자가 선언한 변수와 함수를 기억해야한다. 이때, 컴퓨터는 코드를 읽으면서 변수 이름(Key)과 그 변수가 메모리 어디에 위치해 있는지 혹은 어떤 값인지(Value)를 해시 테이블 형태인 심볼 테이블에 기록해 둔다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터베이스의 해시 조인&lt;/b&gt; : 대용량 데이터베이스에서 두 개의 거대한 테이블을 서로 연결(Join)하여 데이터를 뽑아내야 할 때 해시 테이블이 사용된다. 한쪽 테이블의 열(Column)을 기준으로 메모리에 해시 테이블을 먼저 빌드하고 반대쪽 테이블을 훑으면서 매칭되는 데이터가 있는지 탐색(Probe)하는 방식을 사용한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;게임 및 그래픽스의 공간 분할&lt;/b&gt; : 오픈월드 게임이나 수많은 유닛이 등장하는 게임에서 캐릭터 간의 충돌을 계산할 때 해시 테이블이 사용된다. 맵 전체 화면을 바둑판 모양의 격자로 나누고, 각 격자의 좌표(Key)에 현재 그 자리에 있는 유닛 리스트(Value)를 해시 테이블로 관리한다. 내 캐릭터가 공격할 때 맵 전체를 다 조사하는 것이 아니라, 캐릭터가 서 있는 격자 좌표(Key) 주변만 해시 테이블에서 확인하므로 게임이 끊기지 않고 부드럽게 돌아간다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자료구조, 알고리즘</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/28</guid>
      <comments>https://gudwns5533.tistory.com/28#entry28comment</comments>
      <pubDate>Fri, 5 Jun 2026 01:36:06 +0900</pubDate>
    </item>
    <item>
      <title>트리(Tree)</title>
      <link>https://gudwns5533.tistory.com/27</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;트리의 정의&lt;/b&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;트리는 '계층적인 구조(Hierarchical Structure)'를 가지는 비선형 자료구조이다. 가장 상위 노드에서 시작하여 여러 노드가 사방으로 뻗어나가는 형태를 취한다. 또한 사이클이 없이 모든 정점이 연결되어있는 그래프이다. 사이클이 없는 그래프이므로 정점의 개수가 N개면 간선의 개수는 N-1개이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2as7czLTuQUR8drLFFNtznyXMTe11EetX6Zq9DnnTf6KfPbccEiriNftxyzezmvO2qtBlAh91-RuJavcdvFjHDW2fbAXreTXa8EyMgP7eWfyCFvhishGUtCfN7vsu-3u7y1WVTYR_eMp9eOC_J8T6g.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;673&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3Bva2/dJMcaiXM8L2/UHHDJCnBxUjcDfkXSB8fJ1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3Bva2/dJMcaiXM8L2/UHHDJCnBxUjcDfkXSB8fJ1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3Bva2/dJMcaiXM8L2/UHHDJCnBxUjcDfkXSB8fJ1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3Bva2%2FdJMcaiXM8L2%2FUHHDJCnBxUjcDfkXSB8fJ1%2Fimg.webp&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;1000&quot; height=&quot;673&quot; data-filename=&quot;2as7czLTuQUR8drLFFNtznyXMTe11EetX6Zq9DnnTf6KfPbccEiriNftxyzezmvO2qtBlAh91-RuJavcdvFjHDW2fbAXreTXa8EyMgP7eWfyCFvhishGUtCfN7vsu-3u7y1WVTYR_eMp9eOC_J8T6g.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;673&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;노드(Node) : 트리를 구성하는 하나의 데이터 단위&lt;/li&gt;
&lt;li&gt;간선(Edge) : 노드와 노드를 잇는 선&lt;/li&gt;
&lt;li&gt;루트(Root) : 부모가 없는 최상위 정점&lt;/li&gt;
&lt;li&gt;부모(Parent) : 정점 A에서 정점 B로 가는 간선이 있을 때 A를 B의 부모라고 한다.&lt;/li&gt;
&lt;li&gt;자식(Child) : 정점 A에서 정점 B로 가는 간선이 있을 때 B를 A의 자식이라고 한다.&lt;/li&gt;
&lt;li&gt;형제(Siblings) : 같은 부모를 갖는 정점들&lt;/li&gt;
&lt;li&gt;차수(Degree) : 정점의 자식의 개수&lt;/li&gt;
&lt;li&gt;리프(Leaf) : 자식이 없는 정점. 단말 노드라는 용어로도 쓰인다.&lt;/li&gt;
&lt;li&gt;깊이(Depth) : 루트에서 해당 정점으로 가는 최단 경로의 길이&lt;/li&gt;
&lt;li&gt;레벨(Level) : 루트 노드(Level 0)를 시작으로 아래로 내려갈수록 1씩 증가하는 층을 의&lt;/li&gt;
&lt;li&gt;높이(Height) : 정점들의 깊이의 최댓값&lt;/li&gt;
&lt;li&gt;트리의 차수(Degree of tree) : 트리의 최대 차수 =&amp;nbsp; max(deg1, deg2, ... degn)&lt;/li&gt;
&lt;li&gt;너비(Width) : 각 레벨에 속한 정점의 수의 최댓값&lt;/li&gt;
&lt;li&gt;내부 정점(Internal vertex) : 차수가 2 이상인 정점&lt;/li&gt;
&lt;li&gt;경로(Path) : 시작 정점에서 끝 정점에 이르는 길 사이에 있는 정점들의 순서. 일반적으로 하나의 간선을 두 번 이상 지나는 경로는 생각하지 않는다. 시작 정점과 끝 정점은 동일할 수도 있다.&lt;/li&gt;
&lt;li&gt;경로의 길이(Length of path) : 경로가 지나가는 간선의 개수&lt;/li&gt;
&lt;li&gt;서브 트리(Subtree) : 전체 트리의 일부분을 떼어낸 작은 트리 구조, 모든 트리는 또 다른 서브 트리의 집합이라 볼 수 있다.&lt;/li&gt;
&lt;li&gt;포레스트(Forest) : 서로 독립인 트리들의 집합&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;트리의 종류&lt;/b&gt;&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&amp;middot; 순서 트리 (Ordered Tree)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 각 자식 노드에 순서가 부여되어 저장 위치가 고정되는 트리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 통상, 좌측부터 순서 있게 위치 함&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&amp;middot; 경사 트리 (Skew Tree)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 모든 노드가 1개 자식 노드 만을 갖고, 경사지게 보이는 트리&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&amp;middot; 이진 트리 (Binary Tree)&lt;/b&gt;&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;size18&quot;&gt;&lt;b&gt;전 이진 트리 (Full Binary Tree)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 모든 노드의 차수/자식이 0 또는 2 이어야 함&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;포화 이진 트리 (Perfect Binary Tree)&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;- 모든 내부 노드가 자식 2개를 가지며, 모든 리프 노드가 동일한 레벨(깊이)에 위치한 트리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 높이가 h일 때, 2^h - 1개 노드를 갖음&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 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;완전 이진 트리 (Complete Binary Tree)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 포화 이진 트리의 맨아래 단말 노드들을, 오른쪽부터 하나씩 제거하면, 얻어지는 트리&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 마지막 레벨을 제외한 모든 레벨이 꽉 차 있으며, 마지막 레벨은 왼쪽부터 연속적으로 채워지는 트리&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 중간에 빈 자리가 있으면 안됨&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 즉, 부모 &amp;rarr; 왼쪽 자식 &amp;rarr; 오른쪽 자식 순으로 꽉 채워짐&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 높이가 h일 때, h-1 레벨까지 2^(h-1) - 1개 노드를 갖음&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 쉽게, 왼쪽부터 빈틈없이 채워지는 이진 트리&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;이진 힙 (Binary Heap)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 완전 이진 트리 구조를 기반으로 한 자료구조&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 부모와 자식 간에는 대소 관계가 유지됨&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 형제 노드 간에는 대소 관계가 정해지지 않음&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 최대 힙 (Max Heap) : 부모 &amp;ge; 자식&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 최소 힙 (Min Heap) : 부모 &amp;le; 자식&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 따라서, 부분 순서 트리 (Partially Ordered Tree) 라고도 함&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&amp;middot; 이진 탐색 트리 (Binary Search Tree, BST)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이진 트리 구조를 갖으나, 자료의 검색, 삭제, 삽입에 효율적이게 한 트리 자료구조&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 좌측 자식 노드 값이 부모 노드 값 보다 작고, 우측 자식 노드 값이 무보 노드 값 보다 큼&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 각 노드는 2개의 자식 노드를 각각 가리키는 2개의 포인터를 갖음&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&amp;middot; 균형 트리 (Balanced Tree)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이진 트리 구조를 갖으나, 하위 노드 구조가 좌우 대칭이 되도록 한 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이진 탐색 트리를 보다 일반화시킨 트리 자료구조&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 탐색 성능 저하를 방지하기 위해 사용됨&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&amp;middot; N항 트리 (N-ary Tree, N-way Tree)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 일반적으로, 자식 노드 수(차수)가 3 이상일 때를 말함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 한편, 자식 노드가 3 이상인 트리 구조를 총칭해서, 다중 트리 (Multi-Branch Tree)라고 함&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;b&gt;트리의 사용 이유&lt;/b&gt;&lt;/blockquote&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;트리와 배열(Array)의 차이점&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 185px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 16.3566%; height: 21px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 42.2867%; height: 21px;&quot;&gt;배열(Array)&lt;/td&gt;
&lt;td style=&quot;width: 41.3566%; height: 21px;&quot;&gt;트리(Tree)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;width: 16.3566%; height: 42px;&quot;&gt;구조 형태&lt;/td&gt;
&lt;td style=&quot;width: 42.2867%; height: 42px;&quot;&gt;선형 구조 (데이터가 일렬로 연속적으로 배치)&lt;/td&gt;
&lt;td style=&quot;width: 41.3566%; height: 42px;&quot;&gt;비선형/계층 구조 (데이터가 위아래 부모-자식 관계로 배치됨)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 16.3566%; height: 21px;&quot;&gt;관계 표현&lt;/td&gt;
&lt;td style=&quot;width: 42.2867%; height: 21px;&quot;&gt;앞과 뒤(1:1관계)의 순서만 존재&lt;/td&gt;
&lt;td style=&quot;width: 41.3566%; height: 21px;&quot;&gt;상위와 하위(1:N 관계)의 계층이 존재&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.3566%; height: 17px;&quot;&gt;데이터 접근 속도&lt;/td&gt;
&lt;td style=&quot;width: 42.2867%; height: 17px;&quot;&gt;인덱스(Index)로 즉시 접근&lt;/td&gt;
&lt;td style=&quot;width: 41.3566%; height: 17px;&quot;&gt;루트 노드부터 탐색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 16.3566%; height: 21px;&quot;&gt;데이터 찾기(검색)&lt;/td&gt;
&lt;td style=&quot;width: 42.2867%; height: 21px;&quot;&gt;순서대로 다 찾아야 하므로 데이터가 많아질수록 느려짐&lt;/td&gt;
&lt;td style=&quot;width: 41.3566%; height: 21px;&quot;&gt;트리 구조를 타고 내려가므로 데이터가 많아도 빠름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 16.3566%; height: 21px;&quot;&gt;크기 변경&lt;/td&gt;
&lt;td style=&quot;width: 42.2867%; height: 21px;&quot;&gt;처음에 크기를 정하면 늘리거나 줄이기 어려움 (정적)&lt;/td&gt;
&lt;td style=&quot;width: 41.3566%; height: 21px;&quot;&gt;필요할 때마다 새 노드를 연결하거나 끊으면 됨 (동적)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.3566%;&quot;&gt;메모리 저장&lt;/td&gt;
&lt;td style=&quot;width: 42.2867%;&quot;&gt;연속적인 메모리 공간에 할당&lt;/td&gt;
&lt;td style=&quot;width: 41.3566%;&quot;&gt;메모리 내에 분산 저장 후 노드로 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;width: 16.3566%; height: 42px;&quot;&gt;주요 용도&lt;/td&gt;
&lt;td style=&quot;width: 42.2867%; height: 42px;&quot;&gt;순서가 중요하고, 데이터의 개수가 변하지 않는 단순 목록&lt;/td&gt;
&lt;td style=&quot;width: 41.3566%; height: 42px;&quot;&gt;정렬, 대용량 데이터 검색(데이터 베이스 인덱스), 계층 구조 표현&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;b&gt;트리 순회 방식&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;565&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/by1VL8/dJMcaffNNfX/4YtII2OsiyZviIi3njLJG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/by1VL8/dJMcaffNNfX/4YtII2OsiyZviIi3njLJG0/img.png&quot; data-alt=&quot;예시를 위한 이진 트리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/by1VL8/dJMcaffNNfX/4YtII2OsiyZviIi3njLJG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fby1VL8%2FdJMcaffNNfX%2F4YtII2OsiyZviIi3njLJG0%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;365&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;565&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예시를 위한 이진 트리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;전위 순회 (Preorder)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순서 : 루트 노드 &amp;rarr; 왼쪽 자식 &amp;rarr; 오른쪽 자식&lt;/li&gt;
&lt;li&gt;전위 순회한 결과 : A B D C E F G&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;중위 순회 (Inorder)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순서 : 왼쪽 자식 &amp;rarr; 루트 노드 &amp;rarr; 오른쪽 자식&lt;/li&gt;
&lt;li&gt;중위 순회한 결과 : D B A E C F G&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;후위 순회 (Postorder)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순서 : 왼쪽 자식 &amp;rarr; 오른쪽 자식 &amp;rarr; 루트 노드&lt;/li&gt;
&lt;li&gt;후위 순회한 결과 : D B E G F C A&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;트리 코딩 구현&lt;/b&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;/p&gt;
&lt;pre id=&quot;code_1780488846190&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Node 클래스 생성
class Node:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780495831113&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 트리 클래스 생성
class Tree:
    def __init__(self):
        self.root = None              # 루트는 비워둔 채로 설정

    def preorder(self, node):         # 전위 순회는 루트 &amp;gt; 왼쪽 &amp;gt; 오른쪽 순
        if node is not None:
            print(node.value, end='')
            self.preorder(node.left)
            self.preorder(node.right)

    def inorder(self, node):          # 중위 순회는 왼쪽 &amp;gt; 루트 &amp;gt; 오른쪽 순
        if node is not None:
            self.inorder(node.left)
            print(node.value, end='')
            self.inorder(node.right)
    
    def postorder(self, node):        # 후위 순회는 왼쪽 &amp;gt; 오른쪽 &amp;gt; 루트 순
        if node is not None:
            self.postorder(node.left)
            self.postorder(node.right)
            print(node.value, end='')&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780495843578&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 트리 생성
root = Node('A')
nodeB = Node('B')
nodeC = Node('C')
nodeD = Node('D')
nodeE = Node('E')
nodeF = Node('F')
nodeG = Node('G')

# 노드 연결
root.left = nodeB
root.right = nodeC
nodeB.left = nodeD
nodeC.left = nodeE
nodeC.right = nodeF
nodeF.right = nodeG&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780495864332&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 전위 순회 테스트
test_tree = Tree()
print('전위 순회: ', end='')
test_tree.preorder(root)
print()

# 중위 순회 테스트
print('중위 순회: ', end='')
test_tree.inorder(root)
print()

# 후위 순회 테스트
print('후위 순회: ', end='')
test_tree.postorder(root)
print()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과값&lt;/p&gt;
&lt;pre id=&quot;code_1780495910115&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;전위 순회: ABDCEFG
중위 순회: DBAECFG
후위 순회: DBEGFCA

# 이론상으로 계산했던것과 동일하게 결과가 나오는걸 확인할 수 있다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자료구조, 알고리즘</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/27</guid>
      <comments>https://gudwns5533.tistory.com/27#entry27comment</comments>
      <pubDate>Wed, 3 Jun 2026 23:20:50 +0900</pubDate>
    </item>
    <item>
      <title>링크드 리스트(Linked List) 란?</title>
      <link>https://gudwns5533.tistory.com/26</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;링크드 리스트(Linked List)란 무엇인가&lt;/b&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;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bw8J2P/dJMcacDl9vT/rQV5BJoraWPLU0Wj18B3m1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bw8J2P/dJMcacDl9vT/rQV5BJoraWPLU0Wj18B3m1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bw8J2P/dJMcacDl9vT/rQV5BJoraWPLU0Wj18B3m1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbw8J2P%2FdJMcacDl9vT%2FrQV5BJoraWPLU0Wj18B3m1%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;1280&quot; height=&quot;234&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;234&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;링크드 리스트의 구조&lt;/b&gt;&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드(Node) : 링크드 리스트를 구성하는 가장 작은 단위 독립된 객체. 노드라는 객체 안에는 데이터를 담는 공간과 다음 주소를 담는 공간, 두가지 종류의 공간(컴포넌트)만 존재한다.&lt;/li&gt;
&lt;li&gt;데이터(Data) : 자료구조에 저장하고자 하는 실제 값&lt;/li&gt;
&lt;li&gt;넥스트 포인터(Next Pointer) : 다음 노드가 메모리의 어느 주소에 있는지 가리키는 참조값&lt;/li&gt;
&lt;li&gt;헤드(Head) : 리스트의 시작점. 가장 첫 번째 노드를 가리킨다. Head를 잃어버리면 리스트 전체를 잃어버리게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;링크드 리스트의 동작 원리&lt;/b&gt;&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 데이터 탐색&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열처럼 인덱싱으로 데이터에 접근할 수 없다. 무조건 Head (첫 번째 노드)부터 시작해서 원하는 값이 나올 때까지 Next를 타고 하나씩 접근해야한다. (이를 '순차 접근'이라고 한다)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;138&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Axg2r/dJMcahxScE3/ojkMMD7MyKAECVams5lCH1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Axg2r/dJMcahxScE3/ojkMMD7MyKAECVams5lCH1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Axg2r/dJMcahxScE3/ojkMMD7MyKAECVams5lCH1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/Axg2r/dJMcahxScE3/ojkMMD7MyKAECVams5lCH1/img.gif&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;138&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;138&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 데이터 추가 (삽입)&lt;/b&gt;&lt;/h4&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;새 노드의 Next가 삽입할 위치의 다음 노드를 가리키게 한다.&lt;/li&gt;
&lt;li&gt;삽입할 위치 이전 노드의 Next가 새 노드를 가리키게 업데이트 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YlBNU/dJMcabLe1BY/xK9tSKdlq9Xk0WdgLQgj7k/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YlBNU/dJMcabLe1BY/xK9tSKdlq9Xk0WdgLQgj7k/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YlBNU/dJMcabLe1BY/xK9tSKdlq9Xk0WdgLQgj7k/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/YlBNU/dJMcabLe1BY/xK9tSKdlq9Xk0WdgLQgj7k/img.gif&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;194&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;194&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 데이터 삭제&lt;/b&gt;&lt;/h4&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;이전 노드의 Next를 삭제할 노드의 '다음 노드'로 직접 연결한다.&lt;/li&gt;
&lt;li&gt;연결이 끊어진 노드는 메모리에서 자동으로 정리된다. (Garbage Collection)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;162&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEHyl5/dJMcac4mGZ5/7g2wG64sIUtJzetNP3KvZK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEHyl5/dJMcac4mGZ5/7g2wG64sIUtJzetNP3KvZK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEHyl5/dJMcac4mGZ5/7g2wG64sIUtJzetNP3KvZK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bEHyl5/dJMcac4mGZ5/7g2wG64sIUtJzetNP3KvZK/img.gif&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;162&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;162&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;배열(Array)과 링크드 리스트 비교&lt;/b&gt;&lt;/blockquote&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 style=&quot;width: 16.124%;&quot;&gt;특징&lt;/td&gt;
&lt;td style=&quot;width: 37.8681%;&quot;&gt;배열 (Array)&lt;/td&gt;
&lt;td style=&quot;width: 46.0078%;&quot;&gt;링크드 리스트 (Linked List)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.124%;&quot;&gt;메모리 할당&lt;/td&gt;
&lt;td style=&quot;width: 37.8681%;&quot;&gt;선언 시점에 연속된 공간을 통째로 잡음 (정적)&lt;/td&gt;
&lt;td style=&quot;width: 46.0078%;&quot;&gt;필요할 때마다 동적으로 노드를 생성함 (동적)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.124%;&quot;&gt;데이터 접근 (조회)&lt;/td&gt;
&lt;td style=&quot;width: 37.8681%;&quot;&gt;인덱스로 즉시 접근 가능 (O(1))&lt;/td&gt;
&lt;td style=&quot;width: 46.0078%;&quot;&gt;Head부터 순서대로 찾아가야 함 (O(N))&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.124%;&quot;&gt;삽입 / 삭제&lt;/td&gt;
&lt;td style=&quot;width: 37.8681%;&quot;&gt;데이터를 밀고 당겨야 해서 느림 (O(N))&lt;/td&gt;
&lt;td style=&quot;width: 46.0078%;&quot;&gt;포인터만 바꾸면 되므로 빠름 (O(1)) 단, 위치 찾는 시간 제외&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.124%;&quot;&gt;메모리 효율&lt;/td&gt;
&lt;td style=&quot;width: 37.8681%;&quot;&gt;빈 공간이 남을 수 있어 낭비 가능성 있음&lt;/td&gt;
&lt;td style=&quot;width: 46.0078%;&quot;&gt;다음 노드 주소 (Pointer)를 저장하는 오버헤드 발생&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;배열에 비해 링크드 리스트의 장점&lt;/b&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;/li&gt;
&lt;li&gt;삽입/삭제 용이 : 중간에 있는 요소를 삽입하거나 삭제할 때, 링크드 리스트는 포인터만 조정하면 되므로 상대적으로 간편하다.&lt;/li&gt;
&lt;li&gt;메모리 효율 : 링크드 리스트는 불필요한 메모리를 미리 할당하지 않아 메모리 사용 효율성이 높다.&lt;/li&gt;
&lt;li&gt;데이터 타입의 다양성 : 링크드 리스트는 다양한 타입의 데이터를 한 리스트 안에서 저장할 수 있어, 유연성이 높다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;배열에 비해 링크드 리스트의 단점&lt;/b&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;/li&gt;
&lt;li&gt;코드 복잡성 : 링크드 리스트를 사용하면 삽입과 삭제를 위해 추가적인 코드가 필요하므로 배열에 비해 코드가 복잡해질 수 있다.&lt;/li&gt;
&lt;li&gt;시간 복잡도 : 특정 요소를 찾는데 있어서 배열에 비해 시간이 더 걸릴 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;링크드 리스트의 장, 단점&lt;/b&gt;&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;장점&lt;/b&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;/li&gt;
&lt;li&gt;빠른 삽입 / 삭제 : 빈번하게 데이터가 추가되고 삭제되는 환경에서 성능이 뛰어나다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;느린 접근 속도 : 데이터가 100만개 있다면 100만 번째 데이터를 찾기 위해 앞의 99만 9999개를 다 거쳐가야 한다.&lt;/li&gt;
&lt;li&gt;메모리 추가 소비 : 데이터 외에 '다음 주소'를 저장할 공간이 추가로 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;실제 사용 사례&lt;br /&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 브라우저의 '뒤로가기' / '앞으로 가기' : 방문한 페이지들을 링크로 연결하여 관리한다.&lt;/li&gt;
&lt;li&gt;음악 플레이어의 재생 목록 : 노래들이 순서대로 연결되어 있고, 중간에 곡을 추가하거나 삭제하기 편하다.&lt;/li&gt;
&lt;li&gt;운영체제의 메모리 관리 : 동적 메모리 할당 시 빈 메모리 블록들을 관리 (Free List)하기 위해 사용된다.&lt;/li&gt;
&lt;li&gt;다른 자료구조의 구현 : 큐(Queue), 스택(Stack) 등의 추상 자료구조를 내부적으로 구현할 때 활용된다.&lt;/li&gt;
&lt;li&gt;텍스트 에디터의 'Ctrl + Z' (Undo) : 사용자의 행동 이력을 링크 형태로 관리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;링크드 리스트(Singly Linked List) 구현&lt;/b&gt;&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Node 클래스 구현&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1780324150148&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Node:
    def __init__(self, data):
    	self.data = data       # 저장할 데이터
        self.next = None       # 다음 노드를 가리키는 포인터&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;LinkedList 클래스 구현&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1780326854102&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class LinkedList:
    def __init__(self):
    	self.head = None     # 시작 노드&lt;/code&gt;&lt;/pre&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;&lt;b&gt;데이터 삽입&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780330865015&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    def insert(self, target_data=None, new_data=None):
        &quot;&quot;&quot;삽입
        target_data=None : 맨 뒤 추가
        target_data 지정 : 해당 노드 뒤 삽입
        &quot;&quot;&quot;

        new_node = Node(new_data)

        if self.head is None:                # 빈 리스트라면 target과 상관없이 새 노드가 Head
            self.head = new_node
            return True

        if target_data is None:              # 맨 뒤에 데이터를 추가하고자 하는 경우
            current = self.head

            while current.next:              # 맨 마지막 노드가 나올 때까지 진행
                current = current.next

            current.next = new_node          # 마지막 노드 뒤에 새 노드 연결
            return True

        current = self.head

        while current:                       # 원하는 데이터 뒤에 새로운 데이터 추가
            if current.data == target_data:
                new_node.next = current.next
                current.next = new_node
                return True

            current = current.next

        print(f&quot;[삽입 실패] '{target_data}'를 찾지 못했습니다.&quot;)
        return False&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 id=&quot;code_1780331329989&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    def delete(self, data):
        &quot;&quot;&quot;삭제&quot;&quot;&quot;

        if self.head is None:
            print(&quot;[삭제 실패] 리스트가 비어있습니다.&quot;)
            return False

        if self.head.data == data:      # 삭제할 데이터가 첫 번째 노드인 경우
            self.head = self.head.next  # Head를 다음 노드로 변경
            return True

        current = self.head

        while current.next:             # 삭제할 데이터가 첫 번째가 아닌 경우
            if current.next.data == data:
                current.next = current.next.next  # 노드를 다다음 노드로 건너뛰어준다.
                return True

            current = current.next

        print(f&quot;[삭제 실패] '{data}'를 찾지 못했습니다.&quot;)
        return False&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 id=&quot;code_1780331586395&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    def search(self, data):
        &quot;&quot;&quot;데이터 조회&quot;&quot;&quot;

        current = self.head
        index = 0

        while current:         # 원하는 데이터의 인덱스 검색
            if current.data == data:
                return index

            current = current.next
            index += 1

        return -1              # 데이터가 없다면 -1 값 반환&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;더블 링크드 리스트 (Doubly Linked List)&lt;/b&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;단일 링크드 리스트(Singly Linked List)는 뒤로만 갈 수 있고, 앞으로는 돌아가지 못한다는 단점이 있다. 이를 해결하고자 양방향으로 모두 진행 가능한 리스트가 더블 링크드 리스트이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;더블 링크드 리스트의 구조&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;헤드 (Head) : 가장 앞의 노드&lt;/li&gt;
&lt;li&gt;이전 포인터 (Prev Pointer) : 내 바로 앞 노드가 메모리 어디에 있는지 가리킨다. (첫 번째 노드의 prev는 None)&lt;/li&gt;
&lt;li&gt;데이터 (Data) : 저장하고자 하는 실제 값.&lt;/li&gt;
&lt;li&gt;다음 포인터 (Next Pointer) : 내 바로 뒤 노드가 메모리 어디에 있는지 가리킨다. (마지막 노드의 next는 None)&lt;/li&gt;
&lt;li&gt;테일 (Tail) : 가장 마지막 노드&lt;/li&gt;
&lt;/ul&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;size18&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;/ul&gt;
&lt;p data-ke-size=&quot;size18&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;메모리 사용량 증가 : 노드마다 주소를 저장할 공간 (prev)이 하나씩 더 추가되어 메모리 사용량이 많아진다.&lt;/li&gt;
&lt;li&gt;구현 및 관리 복잡성 : 데이터를 삽입하거나 삭제할 때, 앞뒤 포인터를 모두 끊고 다시 연결해 주어야 하므로, 조건문 처리가 까다롭고 코드가 복잡해진다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>자료구조, 알고리즘</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/26</guid>
      <comments>https://gudwns5533.tistory.com/26#entry26comment</comments>
      <pubDate>Tue, 2 Jun 2026 01:47:25 +0900</pubDate>
    </item>
    <item>
      <title>Pandas</title>
      <link>https://gudwns5533.tistory.com/25</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Pandas는 Python에서 데이터를 쉽고 효율적으로 분석하고 처리하기 위해 사용하는 대표적인 데이터 분석 라이브러리다. 엑셀과 비슷한 형태의 표(Tabular Data)를 다룰 수 있으며, Series와 DataFrame이라는 강력한 자료구조를 제공한다. CSV, Excel, SQL 등의 다양한 데이터 파일을 불러와 정렬, 필터링, 그룹화, 결측치 처리, 통계 분석 등을 간편하게 수행할 수 있으며, 대량의 데이터를 빠르게 가공할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1780302572400&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 설치
pip install pandas
# 또는
python -m pip install pandas&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Series&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;Series는 Pandas에서 사용하는 가장 기본적인 1차원 데이터 구조로, 값(Value)과 인덱스(Index)가 함께 구성된 자료형이다. NumPy의 1차원 배열과 비슷하지만, 각 데이터에 이름표 역할을 하는 인덱스가 추가되어 데이터를 더 체계적으로 관리할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;514&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjfyJA/dJMcaipZGvg/vkw5ZyCUTPQ9dEN7kMKWO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjfyJA/dJMcaipZGvg/vkw5ZyCUTPQ9dEN7kMKWO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjfyJA/dJMcaipZGvg/vkw5ZyCUTPQ9dEN7kMKWO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjfyJA%2FdJMcaipZGvg%2Fvkw5ZyCUTPQ9dEN7kMKWO0%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;514&quot; height=&quot;290&quot; data-origin-width=&quot;514&quot; data-origin-height=&quot;290&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1780302699231&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;idx = ['김사과', '반하나', '오렌지', '이메론', '배애리']
data = [67, 75, 90, 62, 98]

# pd.Series(데이터, 인덱스, ...)
pd.Series(data)

'''
0    67
1    75
2    90
3    62
4    98
dtype: int64
'''&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780302901471&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ser1 = pd.Series(data, idx)
print(ser1)
print(ser1.index)    # index 객체가 따로 생성
print(ser1.values)   # values는 ndarray로 객체 생성
print(type(ser1.values))

'''
김사과    67
반하나    75
오렌지    90
이메론    62
배애리    98
dtype: int64
Index(['김사과', '반하나', '오렌지', '이메론', '배애리'], dtype='str')
[67 75 90 62 98]
&amp;lt;class 'numpy.ndarray'&amp;gt;
'''&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Index 객체&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Index 객체는 Series와 DataFrame에서 각 데이터의 위치를 식별하고 관리하기 위한 '이름표(Label)' 역할을 하는 자료구조이다. 일반 리스트처럼 보이지만 단순한 데이터 저장용이 아니라, 데이터를 빠르게 검색하고 정렬하며 서로 다른 데이터끼리 매칭할 수 있도록 최적화된 Pandas 전용 객체다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;291&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgxsDV/dJMcafUmRnG/lNyIXzRZ7LllVGSJV78Eak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgxsDV/dJMcafUmRnG/lNyIXzRZ7LllVGSJV78Eak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgxsDV/dJMcafUmRnG/lNyIXzRZ7LllVGSJV78Eak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgxsDV%2FdJMcafUmRnG%2FlNyIXzRZ7LllVGSJV78Eak%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;1000&quot; height=&quot;291&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;291&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;DataFrame&lt;/b&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;데이터프레임(DataFrame)은 판다스(Pandas) 라이브러리에서 제공하는 중요하고 강력한 데이터 구조로, 2차원 테이블 형태 데이터를 다루는 데 사용된다. 또한 각 요소는 인덱스(Index), 열(Column), 값(Value)으로 구성되어 있다. 데이터프레임은 행과 열로 이루어져 있으며, 각 열은 다양한 데이터 타입을 가질 수 있다. 값은 넘파이의 ndarray기반으로 저장된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;383&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYtsyQ/dJMcahxR4iI/ZZji4NSyBfFOFo2146v580/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYtsyQ/dJMcahxR4iI/ZZji4NSyBfFOFo2146v580/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYtsyQ/dJMcahxR4iI/ZZji4NSyBfFOFo2146v580/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYtsyQ%2FdJMcahxR4iI%2FZZji4NSyBfFOFo2146v580%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;1000&quot; height=&quot;383&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;383&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1780303321582&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;data = [[67, 93, 91],
        [75, 68, 96],
        [87, 81, 82],
        [62, 70, 75],
        [98, 56, 87]]

idx = ['김사과', '반하나', '오렌지', '이메론', '배애리']
col = ['국어', '영어', '수학']

# pd.DataFrame(데이터, 인덱스, 컬럼, ...)
pd.DataFrame(data)

'''
	 0	 1 	 2
0	67	93	91
1	75	68	96
2	87	81	82
3	62	70	75
4	98	56	87
'''&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780303359334&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pd.DataFrame(data, idx)
# idx값 추가

'''
        0	1	2
김사과	67	93	91
반하나	75	68	96
오렌지	87	81	82
이메론	62	70	75
배애리	98	56	87
'''&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780303431542&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pd.DataFrame(data, idx, col)
# column값 추가

'''

	국어	영어	수학
김사과	67	93	91
반하나	75	68	96
오렌지	87	81	82
이메론	62	70	75
배애리	98	56	87
'''&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;CSV 파일 읽어오기&lt;/b&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;CSV 파일은 Comma-Separated Values (쉼표로 구분된 값) 파일의 약자로, 데이터를 단순한 텍스트 형식으로 저장하는데 사용되는 파일 형식이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1780375908945&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# csv 파일 메모리에 올리기
df = pd.read_csv('csv 파일 주소')&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780375966265&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type(df)
# pandas.DataFrame&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780376189673&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# info(): 데이터를 파악 할 수 있는 메서드
df.info()

# 클래스 정보, entry 정보, 컬럼의 개수, 데이터타입, 메모리 사용량 등이 출력된다.
'''
&amp;lt;class 'pandas.DataFrame'&amp;gt;
RangeIndex: 20 entries, 0 to 19
Data columns (total 7 columns):
 #   Column   Non-Null Count  Dtype
---  ------   --------------  -----
 0   이름       20 non-null     str  
 1   소속사      20 non-null     str  
 2   성별       20 non-null     str  
 3   생년월일     20 non-null     str  
 4   키        20 non-null     str  
 5   혈액형      18 non-null     str  
 6   브랜드평판지수  20 non-null     str  
dtypes: str(7)
memory usage: 1.2 KB
```&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780376268136&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df.columns

# Index(['이름', '소속사', '성별', '생년월일', '키', '혈액형', '브랜드평판지수'], dtype='str')&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780376803313&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 컬럼의 값을 바꾸고 싶을때는 전체 컬럼을 만들어서 바꿔줘야 함.
new_columns = ['name', 'company', 'gender', 'birthday', 'height', 'blood', 'brand']
df.colums = new_columns&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780376944944&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# describe() : 통계 정보를 반환, 기본값은 숫자정보를 중심으로 통계정보가 반환됨
df.describe()
# df.describe(include=str) &amp;gt; 숫자정보가 있을때 문자열들의 정보를 반환하고 싶을 때&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;997&quot; data-origin-height=&quot;289&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnyYCP/dJMcabdioOB/SsOwYctwEd5tcyXBJubnB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnyYCP/dJMcabdioOB/SsOwYctwEd5tcyXBJubnB0/img.png&quot; data-alt=&quot;describe() 반환 시 항목들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnyYCP/dJMcabdioOB/SsOwYctwEd5tcyXBJubnB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnyYCP%2FdJMcabdioOB%2FSsOwYctwEd5tcyXBJubnB0%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;997&quot; height=&quot;289&quot; data-origin-width=&quot;997&quot; data-origin-height=&quot;289&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;describe() 반환 시 항목들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1780377058033&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 원하는 개수의 데이터 보기
df.head() # 상위 5개의 row를 출력
df.head(n) # 상위 n개의 row를 출력
df.tail() # 하위 5개의 row를 출력
df.tail(n) # 하위 n개의 row를 출력&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;정렬하기&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1780377228232&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# index로 오름차순 정렬 : 기본값
df.sort_index()

# index로 내림차순 정렬
df.sort_index(ascending=False)

# 원하는 series로 오름차순 정렬
df.sort_values(by='column_name') # 실제 데이터값의 순서변경은 없고 정렬된 복사본만 출력됨

# 원하는 series로 내림차순 정렬
df.sort_values(by='column_name', ascending=False)
# 문자열 비교는 ASCII기준으로 사전 순서처럼 비교하기 때문에 '182cm' &amp;gt; '182.2cm'처럼 이상한 결과가 나올 수 있음.

# 함수형 프로그래밍 (Chaining)
# height에서 cm를 제거하고 문자열을 실수형으로 바꾸는 과정
df['height'] = df['height'].str.replace('cm','').astype(float)

# 1차 정렬 : 키(내림차순), 2차 정렬 : 브랜드(내림차순)
df.sort_values(by=['height', 'brand'], ascending=[False, False], na_position='first')
# na_position: 값이 NaN이면 제일 아래 깔리는게 default, 그걸 위로 올리고 싶을때는 first

# describe(): 데이터프레임의 기술 통계량을 요약해주는 함수&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;996&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSF4S3/dJMcahxSLFr/1GkOx9n7ojKQK2ICJVlTwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSF4S3/dJMcahxSLFr/1GkOx9n7ojKQK2ICJVlTwk/img.png&quot; data-alt=&quot;describe()에 출력되는 통계량&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSF4S3/dJMcahxSLFr/1GkOx9n7ojKQK2ICJVlTwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSF4S3%2FdJMcahxSLFr%2F1GkOx9n7ojKQK2ICJVlTwk%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;996&quot; height=&quot;500&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;describe()에 출력되는 통계량&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;인덱싱&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1780384967365&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 한개의 컬럼(series)만 확인하기
df['column_name']
# df.column_name 으로도 확인이 가능하지만 컬럼명이 한글이거나, 공백이 있거나,
# 특수문자가 있으면 사용할 수 없으므로 위의 방식이 비교적 안전함.

# 슬라이싱을 통해 데이터 확인 가능
df[:3]&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780385558931&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# loc 인덱싱

df.loc[:, 'name']  # loc[행에대한 기준, 열에대한 기준]

'''
loc는 DataFrame이나 Series에서 인덱스 이름(Label)과 컬럼명을 기준으로 데이터를 선택하는 인덱서(Indexer)
즉, 숫자 위치가 아니라 실제 행 이름과 열 이름을 사용하여 데이터를 가져온다.
loc는 사람이 읽기 쉽고 데이터 의미를 기준으로 접근하기 때문에 데이터 분석과 전처리 작업에서 가장 많이 사용된다.
'''

df.loc[2:5, 'name'] # 5번을 포함 (2부터 5까지)
df.loc[2:5, ['name', 'gender', 'height']] # 여러개를 넣을 땐 리스트로 묶어서 사용
df.loc[[2, 5], ['name', 'gender', 'height']] # 2번, 5번에 해당하는 각각의 정보를 가져올 때
df.loc[2:5, 'name':'gender'] # 2부터 5까지 + name부터 gender까지&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780385701995&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# iloc 인덱싱 : index로 인덱싱
df.iloc[:, 0] # 첫번째 열의 모든 데이터를 가져옴

'''
iloc는 DataFrame이나 Series에서 숫자 위치(Index Position)를 기준으로 데이터를 선택하는 인덱서(Indexer)
즉, 실제 인덱스 이름이 아니라 행과 열의 순서 번호를 사용하여 데이터를 가져온다.
iloc는 데이터의 실제 이름과 상관없이 순서 기준으로 접근하기 때문에 반복처리나 특정 위치 데이터를 다룰 때 많이 사용.
'''

df.iloc[:, 0:3] # 3을 포함하지 않음 (0부터 2까지)
df.iloc[:, [0,3]] # 0과 3열
df.iloc[1:5, 0:2] # 1부터 4행, 0부터 1열&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780386459106&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df['height'] &amp;gt;= 180  # 180 이상인 bool 값 출력
df[df['height'] &amp;gt;= 180]  # 180 이상인 데이터값 출력
df[df['height'] &amp;gt;= 180]['name']  # 180 이상인 데이터의 이름만 출력
df[df['height'] &amp;gt;= 180][['name', 'gender', 'height']]
df.loc[df['height'] &amp;gt;= 180, ['name', 'gender', 'height]]  # 위의 코드와 동일한 결과 출력&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780386627162&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;blood = ['A', 'B']
df['blood'].isin(blood)  # 해당 값이 데이터 안에 있는지 bool로 출력

df[df['blood'].isin(blood)] # True에 해당하는 데이터 출력
df.loc[df['blood'].isin(blood), :]&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;결측값&lt;/b&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;결측값(Missing Value)은 데이터가 존재하지 않거나 비어있는 값을 의미하며, 보통 NaN(Not a Number) 형태로 표현된다. 예를 들어 키, 몸무게, 나이 같은 정보가 입력되지 않았거나 수집 과정에서 누락된 경우 결측값이 발생한다. 데이터 분석과 머신러닝에서는 결측값이 매우 중요하며, 그대로 두면 평균 계산, 통계 분석, 모델 학습 과정에서 오류나 성능 저하가 발생할 수 있다. 결측값 처리는 데이터 품질을 높이고 정확한 분석 결과를 얻기 위한 매우 중요한 데이터 전처리 과정이다.&lt;/p&gt;
&lt;pre id=&quot;code_1780387999969&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df.isna() # True 부분이 결측치
df.isnull() # True 부분이 결측치
df.notna() # False 부분이 결측치
df.notnull() # False 부분이 결측치

df['column_name'].isnull() # series별로 조회 가능

import numpy as np
df.loc[[8,19], 'height'] = np.nan  # 8,19행의 height를 NaN으로 변경
df[df['height'].notnull()]  # 결측치가 있는 행을 제외한 데이터 조회
df[~df['height'].isnull()]  # ~ : 반대 결과 &amp;gt; notnull()&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780388024121&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 데이터프레임 복사
df_copy = df.copy()&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780388272489&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# fillna() : 결측값을 채워주는 함수
df_copy['height'].fillna(0)  # 결측값을 0으로 채움

# mean() : 데이터의 평균
height = df_copy['height'].mean()
df_copy['height'] = df_copy['height'].fillna(height) # 결측값을 데이터의 평균값으로 채움

# median() : 50%값, 중위값
height = df_copy['height'].median()
df_copy['height'] = df_copy['height'].fillna(height) # 결측값을 데이터의 중앙값으로 채움&lt;/code&gt;&lt;/pre&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;평균(Mean)은 데이터의 모든 값을 더한 뒤 데이터 개수로 나눈 값으로, 전체 데이터의 중심적인 경향을 나타내는 대표적인 통계값이다. 예를 들어 [10, 20, 30]의 평균은 (10+20+30)/3 = 20이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 중앙값(Median)은 데이터를 크기순으로 정렬했을 때 가장 가운데 위치한 값을 의미하며, &lt;b&gt;이상치(Outlier)의 영향을 적게 받는 특징&lt;/b&gt;이 있다. 예를 들어 [10, 20, 30]의 중앙값은 20이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 데이터 개수가 짝수라 가운데 값이 두 개라면, 두 가운데 값의 평균을 중앙값으로 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 [10, 20, 30, 40]에서는 가운데 값인 20과 30의 평균인 25가 중앙값이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 평균은 전체 데이터를 모두 반영하는 대표값이고, 중앙값은 데이터의 정중앙 위치를 나타내는 대표값이라 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1780388664257&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# dropna() : 결측값이 있는 행 또는 열을 제거. 결측값이 한개라도 있는 경우 삭제
# axis=0 (행 삭제), axis=1 (열 삭제)

df_copy.dropna() # 결측값이 있는 행 삭제, df_copy.dropna(axis=0)과 동일
df_copy.dropna(axis=1) # 결측값이 있는 열 삭제&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;행, 열 추가 및 삭제&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1780388887409&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 추가하고자 하는 데이터를 먼저 딕셔너리형태로 생성
dic = {
    'name': '김사과',
    'company': '애플',
    'gender': '여자',
    'birthday': '2000-01-01',
    'height': 160.0,
    'blood': 'A',
    'brand': 1234567
}

df_copy.loc[len(df_copy)] = dic   # 맨 뒤 행으로 데이터 추가
df_copy['nation'] = '대한민국'    # 새로운 열 추가
df_copy.loc[df_copy['name'] == '김사과', 'nation'] = '미국'
# 이름이 '김사과'에 해당하는 데이터 행의 'nation' 데이터 값을 '미국'으로 변경

# 행 제거하기
df_copy.drop(20, axis=0) # 20행 데이터 제거

# 여러 행 제거
df_copy.drop([1, 3, 5, 7, 20], axis=0)

# 열 제거하기
df_copy.drop('nation', axis=1) # 'nation'열 제거

# 여러 열 제거
df_copy.drop(['nation', 'company'], axis = 1)&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;통계 함수&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1780389491616&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(df_copy['height'].sum())    # 합계
print(df_copy['height'].count())  # 개수, NaN은 포함하지 않음
print(df_copy['height'].mean())   # 평균
print(df_copy['height'].median()) # 중앙값
print(df_copy['height'].max())    # 최대값
print(df_copy['height'].min())    # 최소값
print(df_copy['height'].var())    # 분산
print(df_copy['height'].std())    # 표준편차&lt;/code&gt;&lt;/pre&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;분산(Variance)은 데이터가 평균으로부터 얼마나 퍼져 있는지를 수치로 나타낸 값으로, 각 데이터와 평균의 차이를 제곱한 뒤 평균을 내어 계산한다. 값이 클수록 데이터의 흩어짐이 크다는 의미이며, 값이 작을수록 데이터가 평균 근처에 모여 있다는 뜻이다. 하지만 분산은 차이를 제곱해서 계산하기 때문에 단위가 원래 데이터와 달라진다는 단점이 있다. 이를 해결하기 위해 사용하는 것이 표준편차(Standard Deviation)이며, 표준편차는 분산에 제곱근(sqrt)을 취한 값이다. 따라서 원래 데이터와 같은 단위를 사용하므로 해석이 더 직관적이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;266&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uHbM9/dJMcaccfNyj/WKlNOIxrOVvkBmHGIpMjf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uHbM9/dJMcaccfNyj/WKlNOIxrOVvkBmHGIpMjf0/img.png&quot; data-alt=&quot;편차, 분산, 표준편차&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uHbM9/dJMcaccfNyj/WKlNOIxrOVvkBmHGIpMjf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuHbM9%2FdJMcaccfNyj%2FWKlNOIxrOVvkBmHGIpMjf0%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;682&quot; height=&quot;266&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;266&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;편차, 분산, 표준편차&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1780389955720&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# groupby(): 데이터를 그룹으로 묶어 분석할 때 사용
df_copy.groupby('blood')

# 그룹을 맺으면 통계함수를 사용 할 수 있다.
df_copy.groupby('blood').count()

df_copy.groupby('blood').mean(numeric_only=True) # numeric_only = 수치화 할 수 있는 자료만&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780390078056&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# drop_duplicates() : 중복된 데이터를 제거, keep='first'가 기본값
df_copy['blood'].drop_duplicates(keep='last')
df_copy['blood'].drop_duplicates()  # 데이터 내에서 Unique한 값을 찾을 때 좋음

# value_counts() : 열의 각 값에 대한 데이터의 개수를 반환, 기본은 NaN을 생략
df_copy['blood'].value_counts(dropna=False)  # NaN을 포함&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;데이터프레임 다루기&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1780465555376&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# concat(): 여러개의 데이터프레임이나 시리즈를 하나로 합칠 때 사용하는 함수
pd.concat([df1, df1_copy]) # axis=0 (기본값) # 데이터가 위아래로 붙는다.

# reset_index(): index를 새롭게 적용
# drop=True 옵션을 사용하여 기존 index가 컬럼으로 만들어지는 것을 방지
df1_concat = pd.concat([df1, df1_copy])
df1_concat.reset_index(drop=True)

pd.concat([df1_copy, df2_copy], axis=1) # 같은 index와 옆으로 결합

# merge(): 특정 고유한 키(unique, id)값을 기준으로 합침
# merge(데이터프레임1, 데이터프레임2, on='유니크값', how='병합의 기준')
# 병합의 기준: left(왼쪽 기준), right(오른쪽 기준), inner(교집합), cross(key별로 모두 합침)&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;날짜타입&lt;/b&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;/p&gt;
&lt;pre id=&quot;code_1780466764049&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# to_datetime(): str타입에서 datetime타입으로 변환
df1_copy['생년월일'] = pd.to_datetime(df1_copy['생년월일'])
print(type(df1_copy['생년월일']))
print(df1_copy['생년월일'].dtypes)
# &amp;lt;class 'pandas.Series'&amp;gt;
# datetime64[us]

df1_copy['생년월일'].dt.year        # 년 정보만 가져옴
df1_copy['생년월일'].dt.month       # 월 정보만 가져옴
df1_copy['생년월일'].dt.day         # 일 정보만 가져옴
df1_copy['생년월일'].dt.hour        # 시간 정보만 가져옴
df1_copy['생년월일'].dt.minute      # 분 정보만 가져옴
df1_copy['생년월일'].dt.second      # 초 정보만 가져옴
df1_copy['생년월일'].dt.dayofweek   # 요일 정보를 0~6으로 매핑

df1_copy.select_dtypes(include='str') # 문자열 칼럼만 가져오기
df1_copy.select_dtypes(exclude='str') # 문자열 칼럼만 빼고 가져오기

# 문자열을 가지고 있는 칼럼의 이름만 변수에 저장하여 출력
str_cols = df1_copy.select_dtypes(include='str').columns
str_cols
# Index(['이름', '소속사', '성별', '키', '혈액형', '브랜드평판지수'], dtype='str')&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;자주 사용하는 함수&lt;/b&gt;&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. apply()&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pandas의 apply() 함수는 데이터프레임이나 시리즈의 데이터를 사용자 정의 함수 또는 내장 함수에 적용하여 새로운 값을 계산하거나 변환할 때 사용된다. 데이터를 행(row) 또는 열(column) 단위로 처리할 수 있는 강력한 도구이다.&lt;/p&gt;
&lt;pre id=&quot;code_1780467079062&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# '남'은 1로 '여'는 0으로 표기하도록 설정
def male_or_female(x):
    if x == '남':
        return 1
    elif x == '여':
        return 0
    else:
        return None
df1_copy['성별'].apply(male_or_female)

# lambda함수를 통해 위의 코드를 한줄로 표현
df1_copy['성별'].apply(lambda x: 1 if x == '남' else 0)

df1_copy['성별2'] = df1_copy['성별'].apply(lambda x: 1 if x == '남' else 0)  # 파생 변수&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. map()&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pandas의 map() 함수는 Series 객체에서 사용할 수 있는 함수로, 각 요소에 대해 함수나 매핑 규칙을 적용하여 새로운 값을 계산하거나 변환할 때 사용된다. map()은 데이터의 각 요소를 순회하며 특정 작업을 수행하므로, 데이터를 가공하거나 변환하는데 유용.&lt;/p&gt;
&lt;pre id=&quot;code_1780467456637&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;map_gender = {'남':1, '여':0}
df1_copy['성별'].map(map_gender)

df1_copy['성별3'] = df1_copy['성별'].map(map_gender)
df1_copy.head()&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;데이터프레임의 산술연산&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1780468417845&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df1 = pd.DataFrame({
    '파이썬':[60, 70, 80, 90, 95],
    '데이터분석':[40, 60, 70, 55, 87],
    '머신러닝딥러닝':[35, 40, 30, 70, 55]
})

df2 = pd.DataFrame({
    '파이썬':['C', 'B', 'B', 'A', 'A'],
    '데이터분석':[40, 60, 70, 55, 87],
    '머신러닝딥러닝':[35, 40, 30, 70, 55]
})

df1['파이썬'] + df1['데이터분석'] + df1['머신러닝딥러닝']

# df1에 총점, 평균이라는 파생변수를 만들고 파생변수에 총점, 평균을 구해서 저장
df1['총점'] = df1['파이썬'] + df1['데이터분석'] + df1['머신러닝딥러닝']
df1['평균'] = df1['총점'] / 3

print(df1['파이썬'].sum()) # df1['파이썬'].sum(axis=0)
print(df1['파이썬'].mean())

# df1 + df2 # TypeError: unsupported operand type(s) for +: 'int' and 'str'
df1 + 10
# df2 + 10 # TypeError: can only concatenate str (not &quot;int&quot;) to str

df1 = pd.DataFrame({
    '데이터분석':[40, 60, 70, 55, 87],
    '머신러닝딥러닝':[35, 40, 30, 70, 55]
})

df2 = pd.DataFrame({
    '데이터분석':[40, 60, 70, 55],
    '머신러닝딥러닝':[35, 40, 30, 70]
})

df1 + df2 # 행의 갯수가 다를 경우 빠진 데이터를 NaN으로 취급하기 때문에 결과는 NaN&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;get_dummies&lt;/b&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;머신러닝 모델은 숫자 데이터는 처리할 수 있지만 문자열 데이터는 직접 처리할 수 없다. 따라서 문자열로 이루어진 범주형 데이터를 숫자 형태로 변환하는 과정이 필요하며, 이때 많이 사용하는 방법이 원-핫 인코딩(One-Hot Encoding)이다. Pandas에서는 pd.get_dummies() 함수를 이용해 쉽게 원-핫 인코딩을 수행할 수 있다.&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;범주형 데이터를 각 항목별로 새로운 컬럼으로 분리하고, 해당 값이면 1, 아니면 0을 부여하는 방법. 원-핫 인코딩은 범주형 데이터(Categorical Data)에 적용한다. 범주형 데이터란 데이터의 값이 크기나 순서를 의미하는 것이 아니라 단순히 종류(Category)를 나타내는 데이터를 말한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;983&quot; data-origin-height=&quot;329&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O9YOA/dJMcaar0Gnu/1uHReQ1BcAe0kwKrmiY7j1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O9YOA/dJMcaar0Gnu/1uHReQ1BcAe0kwKrmiY7j1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O9YOA/dJMcaar0Gnu/1uHReQ1BcAe0kwKrmiY7j1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO9YOA%2FdJMcaar0Gnu%2F1uHReQ1BcAe0kwKrmiY7j1%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;983&quot; height=&quot;329&quot; data-origin-width=&quot;983&quot; data-origin-height=&quot;329&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  문자열이라고 무조건 원-핫 인코딩하는 것은 아니다. 예를 들어 Pandas에서 문자열(str) 타입으로 인식할 수 있지만, 생년월일은 시간의 흐름이라는 의미가 있다. 따라서 단순히 문자열이라는 이유만으로 원-핫 인코딩을하면 안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 숫자형 데이터라고 해서 항상 수치형 데이터는 아니다. 동급 또는 학년 같은 값은 실제 숫자라기보다는 특정 범주를 나타내는 경우가 많다. 이 경우에는 원-핫 인코딩을 고려할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1780469219470&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;blood_map = {'A':0, 'B':1, 'AB':2, 'O':3}
df1_copy['blood_code'] = df1_copy['blood'].map(blood_map)  # 라벨인코딩

pd.get_dummies(df1_copy['blood'])
'''
    	A	AB	B	O
0	False	False	True	False   &amp;gt; B형
1	True	False	False	False   &amp;gt; A형
'''

df1_copy = pd.get_dummies(df1_copy, columns=['blood'])&lt;/code&gt;&lt;/pre&gt;</description>
      <category>데이터 분석</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/25</guid>
      <comments>https://gudwns5533.tistory.com/25#entry25comment</comments>
      <pubDate>Mon, 1 Jun 2026 17:44:58 +0900</pubDate>
    </item>
    <item>
      <title>넘파이 (NumPy)</title>
      <link>https://gudwns5533.tistory.com/24</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;넘파이&lt;/b&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;넘파이(NumPy)는 Python에서 수치 계산과 데이터 분석을 효율적으로 수행하기 위해 사용되는 대표적인 라이브러리이다. 특히 다차원 배열(Array) 객체를 중심으로 동작하며, 대용량 데이터를 빠르고 메모리 효율적으로 처리할 수 있도록 설계되어 있다. 일반 Python 리스트보다 훨씬 빠른 연산 속도를 제공하며, 벡터 연산, 행렬 계산, 통계 처리, 선형대수, 난수 생성 등 다양한 수학 기능을 지원한다.&lt;/p&gt;
&lt;pre id=&quot;code_1780299443759&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 설치
pip install numpy
# 또는
python -m pip install numpy&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;ndarray&lt;/b&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;NumPy의 ndarray(N-dimensional array)는 NumPy의 핵심 자료구조로, 동일한 자료형(dtype)을 가지는 데이터를 다차원 형태로 저장하고 빠르게 연산할 수 있도록 만든 배열 객체이다. 일반 Python 리스트보다 메모리 사용량이 적고 연산 속도가 매우 빠르며, 벡터 연산과 브로드캐스팅 기능을 통해 반복문 없이도 대량의 데이터를 효율적으로 처리할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1780299675910&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np

ndarr1 = np.array([1, 2, 3, 4])
print(ndarr1)              # [1 2 3 4]
print(type(ndarr1))        # &amp;lt;class 'numpy.ndarray'&amp;gt;
print(type(ndarr1[0]))     # &amp;lt;class 'numpy.int64'&amp;gt;

ndarr2 = np.array([[1, 2, 3], [4, 5, 6]])
print(ndarr2)              # [[1 2 3]
                           #  [4 5 6]]
print(type(ndarr2))        # &amp;lt;class 'numpy.ndarray'&amp;gt;
print(type(ndarr2[0]))     # &amp;lt;class 'numpy.ndarray'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780299773806&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 리스트를 ndarray로 변환

list1 = [1, 2, 3, 4]
ndarr1 = np.array(list1)
print(ndarr1)         # [1 2 3 4]
print(type(ndarr1))   # &amp;lt;class 'numpy.ndarray'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780299799070&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ndarray를 리스트로 변환

list2 = ndarr1.tolist()
print(list2)         # [1, 2, 3, 4]
print(type(list2))   # &amp;lt;class 'list'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780300621700&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ndarr1 = np.array([1, 2, 3, 4])
print(ndarr1)             # [1 2 3 4]
print(type(ndarr1))       # &amp;lt;class 'numpy.ndarray'&amp;gt;
print(type(ndarr1[0]))    # &amp;lt;class 'numpy.int64'&amp;gt;
print(type(ndarr1[1]))    # &amp;lt;class 'numpy.int64'&amp;gt;
print(type(ndarr1[2]))    # &amp;lt;class 'numpy.int64'&amp;gt;
print(type(ndarr1[3]))    # &amp;lt;class 'numpy.int64'&amp;gt;    
# 모두 int일때는 int로

ndarr2 = np.array([1, 2, 3.14, 4])
print(ndarr2)             # [1.   2.   3.14 4.  ]
print(type(ndarr2))       # &amp;lt;class 'numpy.ndarray'&amp;gt;
print(type(ndarr2[0]))    # &amp;lt;class 'numpy.float64'&amp;gt;
print(type(ndarr2[1]))    # &amp;lt;class 'numpy.float64'&amp;gt;
print(type(ndarr2[2]))    # &amp;lt;class 'numpy.float64'&amp;gt;
print(type(ndarr2[3]))    # &amp;lt;class 'numpy.float64'&amp;gt;  
# int, float이 섞여있을땐 더 큰 범위인 float으로

ndarr3 = np.array([1, 2, 3.14, True])
print(ndarr3)             # [1.   2.   3.14 1.  ]
print(type(ndarr3))       # &amp;lt;class 'numpy.ndarray'&amp;gt;
print(type(ndarr3[0]))    # &amp;lt;class 'numpy.float64'&amp;gt;
print(type(ndarr3[1]))    # &amp;lt;class 'numpy.float64'&amp;gt;
print(type(ndarr3[2]))    # &amp;lt;class 'numpy.float64'&amp;gt;
print(type(ndarr3[3]))    # &amp;lt;class 'numpy.float64'&amp;gt;
# int, float, bool이 섞여있을땐 float

ndarr4 = np.array(['1', 2, 3.14, True])
print(ndarr4)             # ['1' '2' '3.14' 'True']
print(type(ndarr4))       # &amp;lt;class 'numpy.ndarray'&amp;gt;
print(type(ndarr4[0]))    # &amp;lt;class 'numpy.str_'&amp;gt;
print(type(ndarr4[1]))    # &amp;lt;class 'numpy.str_'&amp;gt;
print(type(ndarr4[2]))    # &amp;lt;class 'numpy.str_'&amp;gt;
print(type(ndarr4[3]))    # &amp;lt;class 'numpy.str_'&amp;gt;
# int, float, bool, str이 섞여있을땐 가장 큰 범위인 str&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;인덱싱과 슬라이싱&lt;/b&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;넘파이의 ndarray는 리스트와 유사하게 인덱싱과 슬라이싱을 지원한다.&lt;/p&gt;
&lt;pre id=&quot;code_1780301537306&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 2차원 배열
ndarr2d = np.array([[1, 2, 3, 4],
                    [5, 6, 7, 8],
                    [9, 10, 11, 12]])
print(ndarr2d)
print(ndarr2d.shape)

# [[ 1  2  3  4]
#  [ 5  6  7  8]
#  [ 9 10 11 12]]
# (3, 4)

# 0행 가져오기
print(ndarr2d[0, :])
print(ndarr2d[0,])
print(ndarr2d[0])

# [1 2 3 4]
# [1 2 3 4]
# [1 2 3 4]

# 0열 가져오기
print(ndarr2d[:, 0])

# [1 5 9]

# fancy indexing
ndarr1 = np.array([10, 15, 2, 8, 20, 90, 85, 44, 23, 32])
idx = [2, 5, 9]
print(ndarr1[idx]) # ndarr1[[2, 5, 9]]

# [ 2 90 32]

ndarr2d = np.array([[1, 2, 3, 4], 
                    [5, 6, 7, 8], 
                    [9, 10, 11, 12]])
print(ndarr2d[[0, 1], :])

# [[1 2 3 4]
#  [5 6 7 8]]

# logical indexing
ndarr1 = np.array([' ', ' ', ' ', ' ', ' '])
sel = [True, False, True, True, False]  # 전체 데이터의 숫자와 bool 데이터의 숫자가 동일해야 함.
ndarr1[sel]

# array([' ', ' ', ' '], dtype='&amp;lt;U1')

ndarr2d = np.array([[1, 2, 3, 4],
                    [5, 6, 7, 8],
                    [9, 10, 11, 12]])

print(ndarr2d &amp;gt; 7)          
print(ndarr2d[ndarr2d &amp;gt; 7])

# [[False False False False]
#  [False False False  True]
#  [ True  True  True  True]]
# [ 8  9 10 11 12]&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;행렬 연산&lt;/b&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;넘파이에서는 다차원 배열인 ndarray를 사용하여 행렬 연산을 수행할 수 있다. 행렬 연산은 선형 대수와 관련이 깊어, 데이터 과학, 머신러닝, 통계 등 다양한 분야에서 사용된다.&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;선형 대수는 벡터와 행렬을 사용해 공간과 변환을 다루는 수학의 한 분야이다. 쉽게 말해, 여러 숫자를 체계적으로 배열한 벡터(리스트 같은 것)와 행렬(숫자가 격자로 배열된 표)을 이용해 데이터를 표현하고 조작하는 방법이다. 선형 대수는 컴퓨터 그래픽스, 머신러닝, 물리학, 공학 등 다양한 분야에서 사용된다. 예를 들어, 사진에서 색상을 조정하거나, 로봇이 움직일 경로를 계산하거나, AI가 데이터를 분석하는 데도 선형 대수가 필요하다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;스칼라(Scalar)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스칼라는 하나의 숫자만으로 이루어진 가장 기본적인 데이터 형태이다. 방향(direction)이나 구조(shape)는 없고 오직 크기(magnitude)만 가지며, 일반적인 정수나 실수 값이 모두 스칼라에 해당한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;벡터(Vector)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벡터는 여러 개의 숫자가 1차원으로 나열된 구조이며, 크기와 방향을 함께 표현하는 데이터다. 수학에서는 화살표 개념으로 사용되며, 프로그래밍에서는 1차원 배열 형태로 표현된다. 예를 들어 [1, 2, 3]은 3차원 벡터다. NumPy에서는 보통 1차원 ndarray 형태로 다루며, 벡터 간에는 덧셈, 내적(dot product), 크기 계산 등의 연산을 수행할 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 행렬&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1780302108833&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ndarr3 = np.array([[1, 2, 3],
                   [1, 2, 3],
                   [2, 3, 4]])
ndarr4 = np.array([[1, 2],
                   [3, 4],
                   [5, 6]])
                   
                   
print(ndarr3 @ ndarr4)  # 행렬 곱셈

# [[22 28]
#  [22 28]
#  [31 40]]

# 내적: 두 벡터를 곱해서 하나의 숫자(스칼라)를 만드는 연산
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

print(np.dot(a, b)) # 1*4+2*5+3*6&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;정렬&lt;/b&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;NumPy의 np.sort() 함수는 배열을 정렬하는 데 사용된다. 이 함수는 기본적을 원래 배열을 변경하지 않고 정렬된 배열의 복사본을 반환한다.&lt;/p&gt;
&lt;pre id=&quot;code_1780302192057&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ndarr1 = np.array([1, 10, 5, 7, 2, 4, 3, 6, 8, 9])
print(ndarr1)
print(np.sort(ndarr1)) # 오름차순 정렬
print(ndarr1) # 원래 배열은 그대로
print(np.sort(ndarr1)[::-1]) # 내림차순 정렬

# [ 1 10  5  7  2  4  3  6  8  9]
# [ 1  2  3  4  5  6  7  8  9 10]
# [ 1 10  5  7  2  4  3  6  8  9]
# [10  9  8  7  6  5  4  3  2  1]&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780302352624&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ndarr2d = np.array([[11, 10, 12, 9],
                    [3, 1, 4, 2],
                    [5, 6, 7, 8]])
print(ndarr2d.shape)       # (3, 4)
np.sort(ndarr2d, axis=0) 
# 모든 행의 1열만 보고 오름차순 정렬 / axis=0 은 행을 가리킴 &amp;gt; 행 단위 sort
# array([[ 3,  1,  4,  2],
#        [ 5,  6,  7,  8],
#        [11, 10, 12,  9]])

np.sort(ndarr2d, axis=1) 
# 각 행을 오름차순으로 정렬 / axis=1 은 열을 가리킴 &amp;gt; 열 단위 sort       
# array([[ 9, 10, 11, 12],
#        [ 1,  2,  3,  4],
#        [ 5,  6,  7,  8]])

np.sort(ndarr2d, axis=1)[:, ::-1]
# array([[12, 11, 10,  9],
#        [ 4,  3,  2,  1],
#        [ 8,  7,  6,  5]])

np.sort(ndarr2d, axis=-1)
# array([[ 9, 10, 11, 12],
#        [ 1,  2,  3,  4],
#        [ 5,  6,  7,  8]])&lt;/code&gt;&lt;/pre&gt;</description>
      <category>데이터 분석</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/24</guid>
      <comments>https://gudwns5533.tistory.com/24#entry24comment</comments>
      <pubDate>Mon, 1 Jun 2026 17:27:42 +0900</pubDate>
    </item>
    <item>
      <title>모듈</title>
      <link>https://gudwns5533.tistory.com/23</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서 모듈(module)은 관련된 함수, 클래스, 변수들을 하나의 파일(.py)로 묶어 재사용할 수 있게 만든 코드 단위이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈을 사용하면 프로그램을 기능별로 나누어 관리할 수 있어 코드의 가독성과 유지보수성이 향상되며, 다른 파일에서도 import 문을 통해 쉽게 불러와 사용할 수 있다. 파이썬은 기본적으로 다양한 표준 라이브러리 모듈을 제공하고, 사용자가 직접 만든 모듈도 동일한 방식으로 활용할 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;모듈 파일 만들기&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1780297501579&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# fruit.py

PI = 3.14

def print_fruit(name):
    print(f&quot;{name}입니다.&quot;)

def add_quantity(quantity, amount):
    return quantity + amount

class Fruit:
    def __init__(self, name, quantity):
        self.name = name
        self.quantity = quantity

    def print_info(self):
        print(f&quot;과일 이름: {self.name}&quot;)
        print(f&quot;수량: {self.quantity}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;다른 파일에서 모듈 사용하기&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1780297538811&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# main.py

import fruit

fruit.print_fruit(&quot;사과&quot;)

result = fruit.add_quantity(10, 5)
print(result)

apple = fruit.Fruit(&quot;사과&quot;, 10)
apple.print_info()&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780297572059&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fruit import Fruit, add_quantity # 메모리의 효율성을 위해 필요한 함수나 클래스만 import

apple = Fruit('사과', 10)
apple.print_info()

print(add_quantity(10, 5))

#   모듈 전체가 아니라 필요한 함수나 클래스만 가져올 수도 있습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;__name__&lt;/b&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;__name__은 파이썬에서 현재 실행 중인 모듈의 이름을 담고 있는 내장 변수로, 이 파일이 직접 실행된 것인지, import된 것인지 구분하는데 사용된다. 만약 파일을 직접 실행하면 __name__의 값은 &quot;__main__&quot;이 되고, 다른 파일에서 import되어 사용되면 해당 모듈의 파일 이름이 들어간다.&lt;/p&gt;
&lt;pre id=&quot;code_1780297785403&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# fruit.py

def hello():
    print(&quot;과일 모듈입니다.&quot;)

print(&quot;__name__ 값:&quot;, __name__)

if __name__ == &quot;__main__&quot;:
    print(&quot;직접 실행되었습니다.&quot;)
    hello() 
--------------------------------------------------
# 직접 실행
python fruit.py

# 결과
__name__ 값: __main__
직접 실행되었습니다.
과일 모듈입니다.
--------------------------------------------------
# 다른 파일에서 import
import fruit

# 결과
__name__ 값: fruit

#   if __name__ == &quot;__main__&quot;: 블록은 실행되지 않음&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;패키지&lt;/b&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;패키지(package)는 관련된 여러 모듈(.py 파일)을 하나의 폴더로 묶어 계층적으로 관리하는 단위로, 코드 규모가 커질 때 기능별로 구조를 나누어 정리하고 재사용성을 높이기 위해 사용된다. 패키지는 디렉토리로 구성되며, 내부에 모듈과 서브패키지를 포함할 수 있고, 필요에 따라 __init__.py 파일을 통해 패키지 초기화나 공개 API를 정의할 수 있다. 이를 통해 import package.module처럼 이름공간(namespace)을 구분하여 충돌을 방지하고, 프로젝트를 깔끔하게 구조화할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1780298027378&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;myproject/
├── main.py
└── fruits/
    ├── __init__.py
    ├── apple.py
    └── banana.py&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780298109513&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# fruits/__init__.py

from .apple import info as apple_info
from .banana import info as banana_info

__all__ = [&quot;apple_info&quot;, &quot;banana_info&quot;]

#   패키지에서 바로 사용할 API 정의&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780298138042&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# fruits/apple.py

def info():
    return &quot;사과입니다&quot;
    
    
# fruits/banana.py

def info():
    return &quot;바나나입니다&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780298191402&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# main.py

from fruits import apple_info, banana_info

print(apple_info())
print(banana_info())

# 사과입니다
# 바나나입니다&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;random 모듈&lt;/b&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;random 모듈은 파이썬에서 무작위 값을 생성할 때 사용하는 표준 라이브러리 모듈이다. 별도 설치 없이 import random으로 사용할 수 있으며, 임의의 숫자 생성, 리스트에서 랜덤 선택, 데이터 섞기, 난수 범위 지정 등에 자주 사용된다.&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;&lt;b&gt;1. 자주 사용하는 함수&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780298340929&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import random     # random 모듈 import부터 진행

# 1. random.random() &amp;rarr; 0 이상 1 미만 실수
print(&quot;random():&quot;, random.random())                   # random(): 0.46855839871769966

# 2. random.randint(a, b) &amp;rarr; a 이상 b 이하 정수
print(&quot;randint(1, 6):&quot;, random.randint(1, 6))         # randint(1, 6): 5

# 3. random.randrange(a, b) &amp;rarr; a 이상 b 미만 정수
print(&quot;randrange(1, 10):&quot;, random.randrange(1, 10))   # randrange(1, 10): 9

# 4. random.choice() &amp;rarr; 리스트에서 1개 선택
fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;오렌지&quot;]
print(&quot;choice:&quot;, random.choice(fruits))               # choice: 바나나

# 5. random.sample() &amp;rarr; 중복 없이 여러 개 선택
numbers = list(range(1, 11))
print(&quot;sample:&quot;, random.sample(numbers, 3))           # sample: [6, 5, 2]

# 6. random.shuffle() &amp;rarr; 리스트 섞기
cards = [&quot;A&quot;, &quot;K&quot;, &quot;Q&quot;, &quot;J&quot;]
random.shuffle(cards)
print(&quot;shuffle:&quot;, cards)                              # shuffle: ['Q', 'A', 'K', 'J']&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;size18&quot;&gt;&lt;b&gt;2. seed()&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;random.seed()는 난수 생성기의 초기값(시드)을 설정하여 동일한 난수 결과를 재현 가능하게 만드는 함수이다. 파이썬의 random은 의사난수(pseudo-random)를 사용하므로 시드를 고정하면 실행할 때마다 같은 순서의 난수가 생성된다. 이 기능은 테스트, 디버깅, 머신러닝 실험처럼 결과를 반복 재현해야 하는 상황에서 매우 중요하다.&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;&lt;b&gt;2 - 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;의사난수(pseudo-random number)는 완전히 무작위로 생성된 것이 아니라, 일정한 알고리즘에 의해 만들어진 &quot;랜덤처럼 보이는 숫자&quot;를 의미한다. 즉, 내부적으로는 이전 값과 수학적 계산을 기반으로 다음 값을 생성하기 때문에 같은 시작값(시드, seed)을 주면 항상 동일한 난수 순서가 만들어진다. 이러한 특성 덕분에 결과를 재현할 수 있어 테스트나 머신러닝 실험에 매우 유용하며, 속도도 빠르기 때문에 일반적인 프로그래밍에서 널리 사용된다.&lt;/p&gt;
&lt;pre id=&quot;code_1780298778216&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(random.randint(1, 10))
print(random.randint(1, 10))
print(random.randint(1, 10))
#   실행할 때마다 결과가 바뀜


# 시드 설정
random.seed(2026)

print(random.randint(1, 10))
print(random.randint(1, 10))
print(random.randint(1, 10))
#   실행 결과 (항상 동일)&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;모듈 설치&lt;/b&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;1. 가상환경 생성&lt;/p&gt;
&lt;pre id=&quot;code_1780298846704&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;python -m venv venv&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;2. 가상환경 활성화&lt;/p&gt;
&lt;pre id=&quot;code_1780298866128&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# windows
venv\Scripts\activate 

# mac
source venv/bin/activate&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;3. pip 위치 확인&lt;/p&gt;
&lt;pre id=&quot;code_1780298882968&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;where pip      # Windows
which pip      # Mac/Linux
#   venv 안 pip인지 확인&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;4. python 경로 확인&lt;/p&gt;
&lt;pre id=&quot;code_1780298917656&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;where python
#   반드시 venv 경로여야 함&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;5. numpy, pandas 설치&lt;/p&gt;
&lt;pre id=&quot;code_1780298939008&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip install numpy pandas # pip이 다른 python을 가리키고 있을 수도 있음
python -m pip install numpy pandas # 현재 python이 사용하는 환경에 설치됨&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;6. 패키지 목록과 각 버전 확인&lt;/p&gt;
&lt;pre id=&quot;code_1780298968192&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip list
# 현재 환경 기준으로만 보여줌 (전역/다른 venv와 독립)
# 패키지 이름 + 버전 확인 가능
# 설치 여부 빠르게 체크 가능&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;requirements.txt&lt;/b&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;requirements.txt는 파이썬 프로젝트에서 필요한 외부 패키지와 그 버전을 기록해 두는 파일로, 동일한 개발 환경을 다른 사람이나 다른 시스템에서도 쉽게 재현할 수 있게 해준다.&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. requirements.txt 생성&lt;/p&gt;
&lt;pre id=&quot;code_1780299036191&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip freeze &amp;gt; requirements.txt&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;2. requirements.txt 사용 방법&lt;/p&gt;
&lt;pre id=&quot;code_1780299061312&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip install -r requirements.txt
#   다른 환경에서 실행하면 파일에 있는 모든 패키지 자동 설치&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Python</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/23</guid>
      <comments>https://gudwns5533.tistory.com/23#entry23comment</comments>
      <pubDate>Mon, 1 Jun 2026 16:26:49 +0900</pubDate>
    </item>
    <item>
      <title>좌석 예약 관리 프로그램 스터디</title>
      <link>https://gudwns5533.tistory.com/22</link>
      <description>&lt;h1&gt;좌석 예약 관리 프로그램&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 좌석 목록으로 좌석 예약 관리 프로그램을 작성하시오.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;seats = [&quot;A1&quot;, &quot;A2&quot;, &quot;A3&quot;, &quot;B1&quot;, &quot;B2&quot;, &quot;B3&quot;, &quot;C1&quot;, &quot;C2&quot;, &quot;C3&quot;]
reserved = []&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;요구사항&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램이 시작되면 사용자는 메뉴 번호를 입력할 수 있다.&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 style=&quot;width: 50%;&quot;&gt;메뉴번호&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;기능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;좌석 예약&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;예약 취소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;예약 현황 보기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;프로그램 종료&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;code&gt;match-case&lt;/code&gt; 문을 사용하여 처리한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 좌석 예약&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 &lt;code&gt;1&lt;/code&gt;을 입력하면 좌석 예약을 처리한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예약 가능한 좌석 목록을 먼저 출력한다.&lt;/li&gt;
&lt;li&gt;예약 가능한 좌석은 &lt;code&gt;seats&lt;/code&gt;에는 있지만 &lt;code&gt;reserved&lt;/code&gt;에는 없는 좌석이다.&lt;/li&gt;
&lt;li&gt;예약 가능한 좌석 목록은 &lt;code&gt;available_seats&lt;/code&gt; 변수에 저장한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;available_seats&lt;/code&gt;는 리스트 컴프리헨션을 사용하여 생성한다.&lt;/li&gt;
&lt;li&gt;좌석 번호를 입력받는다.&lt;/li&gt;
&lt;li&gt;좌석 번호는 대소문자를 구분하지 않도록 처리한다.&lt;/li&gt;
&lt;li&gt;입력한 좌석이 예약 가능한 좌석이면 &lt;code&gt;reserved&lt;/code&gt;에 추가하고 &lt;code&gt;&quot;{좌석} 좌석 예약 완료&quot;&lt;/code&gt;를 출력한다.&lt;/li&gt;
&lt;li&gt;입력한 좌석이 이미 예약된 좌석이면 &lt;code&gt;&quot;{좌석}는 이미 예약된 좌석입니다.&quot;&lt;/code&gt;를 출력한다.&lt;/li&gt;
&lt;li&gt;입력한 좌석이 좌석 목록에 없으면 &lt;code&gt;&quot;없는 좌석입니다.&quot;&lt;/code&gt;를 출력한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 예약 취소&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 &lt;code&gt;2&lt;/code&gt;를 입력하면 예약 취소를 처리한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 예약된 좌석 목록을 먼저 출력한다.&lt;/li&gt;
&lt;li&gt;취소할 좌석 번호를 입력받는다.&lt;/li&gt;
&lt;li&gt;좌석 번호는 대소문자를 구분하지 않도록 처리한다.&lt;/li&gt;
&lt;li&gt;입력한 좌석이 예약된 좌석이면 &lt;code&gt;reserved&lt;/code&gt;에서 제거하고 &lt;code&gt;&quot;{좌석} 좌석 예약 취소 완료&quot;&lt;/code&gt;를 출력한다.&lt;/li&gt;
&lt;li&gt;입력한 좌석이 좌석 목록에는 있지만 예약되어 있지 않으면 &lt;code&gt;&quot;{좌석}는 예약되지 않은 좌석입니다.&quot;&lt;/code&gt;를 출력한다.&lt;/li&gt;
&lt;li&gt;입력한 좌석이 좌석 목록에 없으면 &lt;code&gt;&quot;없는 좌석입니다.&quot;&lt;/code&gt;를 출력한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 예약 현황 보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 &lt;code&gt;3&lt;/code&gt;을 입력하면 예약 현황을 출력한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 예약된 좌석 목록을 출력한다.&lt;/li&gt;
&lt;li&gt;예약된 좌석 수를 출력한다.&lt;/li&gt;
&lt;li&gt;남은 좌석 수를 출력한다.&lt;/li&gt;
&lt;li&gt;예약 가능한 좌석 목록을 출력한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 프로그램 종료&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 &lt;code&gt;4&lt;/code&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;code&gt;&quot;프로그램 종료&quot;&lt;/code&gt;를 출력한다.&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;잘못된 입력&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;, &lt;code&gt;3&lt;/code&gt;, &lt;code&gt;4&lt;/code&gt; 이외의 값을 입력하면 &lt;code&gt;&quot;잘못된 메뉴입니다. 다시 입력해주세요.&quot;&lt;/code&gt;를 출력한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;출력 형식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메뉴는 다음 형식으로 출력한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 좌석 예약
2. 예약 취소
3. 예약 현황 보기
4. 종료&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예약 현황은 다음 형식으로 출력한다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;예약 현황: 예약좌석목록
예약 좌석수: 예약좌석수
남은 좌석수: 남은좌석수
예약 가능한 좌석: 예약가능좌석목록&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 예약 현황은 다음 형식으로 출력한다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;프로그램 종료
최종 예약 현황: 예약좌석목록
최종 예약 좌석수: 예약좌석수&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실행 예시&lt;/h2&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;1. 좌석 예약
2. 예약 취소
3. 예약 현황 보기
4. 종료
메뉴 입력: 1
예약 가능한 좌석: ['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']
좌석 입력: a2
A2 좌석 예약 완료

1. 좌석 예약
2. 예약 취소
3. 예약 현황 보기
4. 종료
메뉴 입력: 1
예약 가능한 좌석: ['A1', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']
좌석 입력: A2
A2는 이미 예약된 좌석입니다.

1. 좌석 예약
2. 예약 취소
3. 예약 현황 보기
4. 종료
메뉴 입력: 1
예약 가능한 좌석: ['A1', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']
좌석 입력: c1
C1 좌석 예약 완료

1. 좌석 예약
2. 예약 취소
3. 예약 현황 보기
4. 종료
메뉴 입력: 2
예약된 좌석: ['A2', 'C1']
취소할 좌석 입력: a2
A2 좌석 예약 취소 완료

1. 좌석 예약
2. 예약 취소
3. 예약 현황 보기
4. 종료
메뉴 입력: 2
예약된 좌석: ['C1']
취소할 좌석 입력: b1
B1는 예약되지 않은 좌석입니다.

1. 좌석 예약
2. 예약 취소
3. 예약 현황 보기
4. 종료
메뉴 입력: 3
예약 현황: ['C1']
예약 좌석수: 1
남은 좌석수: 8
예약 가능한 좌석: ['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C2', 'C3']

1. 좌석 예약
2. 예약 취소
3. 예약 현황 보기
4. 종료
메뉴 입력: 5
잘못된 메뉴입니다. 다시 입력해주세요.

1. 좌석 예약
2. 예약 취소
3. 예약 현황 보기
4. 종료
메뉴 입력: 4
프로그램 종료
최종 예약 현황: ['C1']
최종 예약 좌석수: 1&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780207402289&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;seats = ['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']
reserved = ['D1']

print(&quot;1. 좌석 예약\n2. 예약 취소\n3. 예약 현황 보기\n4. 종료&quot;)
menu = int(input(&quot;메뉴 번호를 입력하세요: &quot;))

match menu:
    case 1:
        print(&quot;메뉴 입력: 1&quot;)

        available_seats = [seat for seat in seats if seat not in reserved]
        print(&quot;예약 가능한 좌석: &quot;, available_seats)

        seat = input('좌석 입력: ').upper()

        if seat in available_seats:
            reserved.append(seat)
            print(f'{seat} 좌석 예약 완료')
        elif seat in reserved:
            print(f'{seat}는 이미 예약된 좌석입니다.')
        else:
            print(f'없는 좌석입니다.')

    case 2:
        print(&quot;메뉴 입력: 2&quot;)
        
        print(&quot;예약된 좌석:&quot;, reserved)

        cancel_seats = input(&quot;취소할 좌석 입력: &quot;).upper()

        if cancel_seats in reserved:
            reserved.remove(cancel_seats)
            print(f'{cancel_seats} 좌석 예약 취소 완료')
        elif cancel_seats in seats:
            print(f'{cancel_seats}는 예약되지 않은 좌석입니다.')
        else:
            print('없는 좌석입니다.')     

    case 3:
        print(&quot;메뉴 입력: 3&quot;)

        print('예약 현황:', reserved)
        print('예약 좌석수:', len(reserved))
        print('남은 좌석수:', len(seats))
        print('예약 가능한 좌석:', seats)

    case 4:
        print(&quot;메뉴 입력: 4&quot;)
        
        print('프로그램 종료')
        print('최종 예약 현황:', reserved)
        print('최종 예약 좌석수:', len(reserved))
        
    case _:
        print('잘못된 메뉴입니다. 다시 입력해주세요.')&lt;/code&gt;&lt;/pre&gt;</description>
      <category>스터디</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/22</guid>
      <comments>https://gudwns5533.tistory.com/22#entry22comment</comments>
      <pubDate>Sun, 31 May 2026 13:26:53 +0900</pubDate>
    </item>
    <item>
      <title>자료구조의 개념과 동작 원리</title>
      <link>https://gudwns5533.tistory.com/21</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;자료구조의 정의&lt;/b&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;자료구조(Data Structure)란 쉽게 말해 &quot;컴퓨터에서 데이터를 효율적으로 다루기 위해 데이터를 메모리에 저장, 조직, 관리하는 구조적인 방법을 뜻한다. 단순히 데이터를 쌓아두는 것이 아니라, 우리가 원하는 작업을 가장 빠르고 메모리를 적게 쓰면서 처리할 수 있도록 정리하는 규칙 체계이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;자료구조가 필요한 이유&lt;/b&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;컴퓨터의 핵심 자원은 CPU와 메모리이다. 데이터의 특성에 맞는 적절한 자료구조를 선택하면 &lt;b&gt;효율성&lt;/b&gt;과 &lt;b&gt;추상화&lt;/b&gt;라는 두 가지의 이점을 얻을 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;자료구조의 분류&lt;/b&gt;&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 단순 구조 (Primitive Data Structure)&lt;/b&gt;&lt;/h4&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;int, float, str, bool 등&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 선형 구조 (Linear Data Structure)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 한 줄로 차례대로 나열한 구조. 데이터 간의 관계가 1:1이다.&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;&lt;b&gt;2 - 1 배열 (Array)&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;여러 원소들을 순차적으로 메모리에 저장(contiguous memory locations)하는 구조. 기본적으로 배열은 모든 원소가 같은 자료형이어야 하며, 배열의 크기를 변경할 수 없다. Array를 담은 변수에는 배열의 첫 원소의 메모리 주소를 담고 있기 때문에 index를 사용하여 원소에 빠르게 접근할 수 있다. 배열의 원소가 모두 같은 자료형이기 때문에, 원소의 자료형이 무엇인지에 따라 다음 index의 원소가 위치한 물리적인 위치로 얼마만큼 이동해야 하는지 알 수 있다.&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;i&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/i&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;Index를 활용하면 랜덤으로 원소에 접근(Random access)이 가능하여 검색에서 성능이 좋다.&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;&lt;b&gt;&lt;i&gt;단점&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(원소를 이동해야 하는 연산작업이 필요하므로) 삽입, 삭제 시 비효율적.&lt;/li&gt;
&lt;li&gt;초기에 선언한 크기를 변경할 수 없다. (사용안한 만큼의 메모리가 낭비될 수 있다.)&lt;/li&gt;
&lt;li&gt;(배열 요소를 삭제했더라도 변수의 선언이 해제되지 않는 이상 점유하고 있는 메모리이므로) 메모리의 재사용이 불가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;Array와 List의 차이&lt;/i&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;Python의 List 자료형도 Array처럼 index를 사용하여 원소에 접근할 수 있지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 서로 다른 자료형을 원소로 가질 수 있으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 동적 배열(Dynamic Array)로써 Array와는 다르게 자유롭게 크기를 확장할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 리스트 안의 원소들은 그 값들을 자유롭게 변경도 가능(Mutable)하다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2 - 2&amp;nbsp;스택 (Stack)&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;한쪽이 막힌 통처럼, 늦게 들어간 데이터가 먼저 나오는 구조 (LIFO). 데이터의 삽입과 삭제가 저장소의 맨 윗부분에서만 일어나는 자료구조. Stack은 연속으로 저장된 데이터 구조를 가지고 있고 맨 위 요소에 대한 포인터(주소값)를 가지고 있는 Array로 구현할 수 있다.&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;i&gt;장점&lt;/i&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;단점&lt;/i&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;LIFO 구조를 사용하는 이유&lt;/i&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;'가장 최근에 했던 작업'이나 '현재 맥락'을 기억하고 되돌아 가야하기 때문. 컴퓨터가 어떤 작업을 하다가 잠시 다른 작업을 하러 갈 때, 방금 전까지 무엇을 하고 있었는지를 기억해야 돌아올 수 있다. 이때 가장 최근에 중단한 작업이 항상 맨 위에 쌓여 있어야 바로 꺼내서 이어 할 수 있기 때문에 LIFO 구조를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 히스토리(이력) 관리와 되돌리기(Undo), 그리고 함수 호출 시 이전 메모리 상태를 복구하기 위함.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2 - 3 큐 (Queue)&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;목록 한쪽 끝에서만 자료를 넣고 다른 한쪽 끝에서만 자료를 빼낼 수 있는 자료구조. 먼저 집어넣은 데이터가 먼저 나오는 (FIFO : First In, First Out) 구조로 데이터를 저장한다. 데이터가 입력한 순서대로 처리되어야 할 경우에 사용한다. Queue에 새로운 데이터가 들어오면 Queue의 끝에 데이터가 추가되며(enqueue), 반대로 삭제될 때는 첫 번째 위치의 데이터가 삭제된다(dequeue).&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;i&gt;&lt;b&gt;FIFO 구조를 사용하는 이유&lt;/b&gt;&lt;/i&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;rarr; 요청이 들어온 시간 순서대로 데이터를 안전하게 버퍼링(대기)하고 처리하기 위함.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2 - 4 연결 리스트 (Linked List)&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;p data-ke-size=&quot;size16&quot;&gt;연결되는 방향에 따라, Singly linked list (단일 연결 리스트), Doubly linked list (이중 연결 리스트), Circular linked list (환형 연결 리스트) 가 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 비선형 구조 (Non-Linear Data Structure)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 일렬로 나열하지 않고 복잡한 관계(1:N 또는 N:N)로 연결한 구조.&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;트리(Tree)&lt;/b&gt; : 회사 조직도나 컴퓨터의 폴더 디렉토리처럼 계층적 구조를 가진다 (1:N 관계)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;그래프(Graph)&lt;/b&gt; : 지하철 노선도처럼 데이터들이 거미줄처럼 복잡하게 연결된 구조 (N:N 관계)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. 파일 구조 (File Organization)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리가 아닌 하드디스크나 SSD같은 보조기억장치에 데이터를 대량으로 저장할 때 사용하는 구조.&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;자료구조의 동작 방식&lt;/b&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;대부분의 자료구조는 목적과 형태는 다르지만, 공통적으로 다음과 같은 4가지 기본 동작(연산)을 수행하며 작동한다. 이를 흔히 CRUD 연산 또는 자료구조의 기본 연산이라고 부른다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;접근 / 조회 (Access / Search) : 특정 위치에 있는 데이터를 읽거나, 원하는 데이터가 어디에 있는지 찾는 동작&lt;/li&gt;
&lt;li&gt;삽입 (Insert) : 새로운 데이터를 자료구조 안에 집어넣는 동작&lt;/li&gt;
&lt;li&gt;삭제 (Delete) : 기존에 있던 데이터를 자료구조에서 제거하는 동작&lt;/li&gt;
&lt;li&gt;수정 (Update) : 기존 데이터를 새로운 데이터로 변경하는 동작&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;자료구조의 장점과 단점&lt;/b&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;각 자료구조는 트레이드오프(Trade-off, 하나를 얻으면 하나를 잃는 관계)를 가진다.&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;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그램 성능 최적화 : 상황에 맞는 자료구조를 쓰면 데이터 처리 속도가 비약적으로 빨라진다.&lt;/li&gt;
&lt;li&gt;메모리 절약 : 데이터를 규칙성 있게 저장하므로 낭비되는 메모리 공간을 최소화할 수 있다.&lt;/li&gt;
&lt;li&gt;코드의 재사용성과 가독성 : 정형화된 구조를 사용하므로 다른 개발자가 코드를 봐도 구조를 쉽게 이해할 수 있다.&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;size18&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;설계의 복잡도 증가 : 데이터 구조가 복잡해질수록 이를 구현하고 관리하는 코드가 어려워진다.&lt;/li&gt;
&lt;li&gt;특정 연산의 비효율성 : 예를 들어 데이터 조회가 빠른 자료구조는 데이터 삽입 / 삭제가 느릴 수 있고, 반대로 삽입 / 삭제가 빠른 구조는 조회가 느릴 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;자료구조의 실제 활용 사례&lt;/b&gt;&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Array : 주소록의 연락처 목록, 음악 재생 목록 등 순서가 중요하고 자주 꺼내 보는 데이터&lt;/li&gt;
&lt;li&gt;Stack : 인터넷 브라우저의 '뒤로가기' (가장 최근 방문한 페이지로 이동), 문서 편집기의 되돌리기 (Ctrl + Z)&lt;/li&gt;
&lt;li&gt;Queue : 프린터의 인쇄 대기열 (먼저 인쇄를 누른 문서부터 출력), 수강신청이나 티켓팅의 디지털 대기 시스템&lt;/li&gt;
&lt;li&gt;Tree : 컴퓨터의 파일 탐색기 (폴더 구조), 회사의 조직도, 데이터베이스의 인덱스 검색&lt;/li&gt;
&lt;li&gt;Graph : 네이버 지도나 구글 맵의 길 찾기 (최단 경로), 페이스북이나 인스타그램의 추천 친구 (인맥 네트워크)&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;배열, 큐, 스택의 차이&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 164px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 18.4884%; height: 21px;&quot;&gt;비교 항목&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%; height: 21px;&quot;&gt;배열 (Array / List)&lt;/td&gt;
&lt;td style=&quot;width: 27.093%; height: 21px;&quot;&gt;스택 (Stack)&lt;/td&gt;
&lt;td style=&quot;width: 26.1628%; height: 21px;&quot;&gt;큐 (Queue)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 18.4884%; height: 21px;&quot;&gt;구조적 형태&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%; height: 21px;&quot;&gt;데이터가 일렬로 연속 배열됨&lt;/td&gt;
&lt;td style=&quot;width: 27.093%; height: 21px;&quot;&gt;한쪽 끝만 뚫려 있는 통 모양&lt;/td&gt;
&lt;td style=&quot;width: 26.1628%; height: 21px;&quot;&gt;양쪽 끝이 모두 뚫려 있는 파이프 모양&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;width: 18.4884%; height: 42px;&quot;&gt;데이터 접근&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%; height: 42px;&quot;&gt;임의 접근 가능&lt;br /&gt;(인덱스로 바로 접근)&lt;/td&gt;
&lt;td style=&quot;width: 27.093%; height: 42px;&quot;&gt;제한적 접근&lt;br /&gt;(가장 위쪽 데이터만 가능)&lt;/td&gt;
&lt;td style=&quot;width: 26.1628%; height: 42px;&quot;&gt;제한적 접근&lt;br /&gt;(가장 앞쪽 / 뒤쪽만 가능)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 38px;&quot;&gt;
&lt;td style=&quot;width: 18.4884%; height: 38px;&quot;&gt;핵심 원칙&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%; height: 38px;&quot;&gt;순서 지정 및 인덱싱&lt;/td&gt;
&lt;td style=&quot;width: 27.093%; height: 38px;&quot;&gt;LIFO&lt;br /&gt;(후입선출 - 마지막이 먼저 나옴)&lt;/td&gt;
&lt;td style=&quot;width: 26.1628%; height: 38px;&quot;&gt;FIFO&lt;br /&gt;(선입선출 - 처음이 먼저 나옴)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;width: 18.4884%; height: 42px;&quot;&gt;데이터 삽입 / 삭제위치&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%; height: 42px;&quot;&gt;아무 위치나 가능&lt;br /&gt;(단, 중간은 밀어내기 필요)&lt;/td&gt;
&lt;td style=&quot;width: 27.093%; height: 42px;&quot;&gt;오직 한쪽 끝(Top)에서만 삽입 / 삭제&lt;/td&gt;
&lt;td style=&quot;width: 26.1628%; height: 42px;&quot;&gt;한쪽 끝(Rear)에서 삽입,&lt;br /&gt;반대쪽(Front)에서 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;코드 구현&lt;/b&gt;&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 배열 (Array)&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1780146651840&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 데이터 삽입

test1 = ['a', 'b', 'c']

# 맨 뒤에 삽입
test1.append('d')     # append 메서드로 맨뒤에 데이터 추가
print(test1)

# index의 위치에 삽입 (list 범위를 초과하는 index는 맨 뒤에 삽입)
test1.insert(0, 'f')  # insert 메서드로 원하는 위치에 데이터 추가
print(test1)

# 결과값
# ['a', 'b', 'c', 'd']
# ['f', 'a', 'b', 'c', 'd']&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780146680524&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 데이터 삭제

test2 = ['a', 'b', 'c']

# del 키워드를 통한 index의 데이터 삭제
del test2[0]
print(test2)

# remove 메서드로 데이터를 찾아서 삭제 (데이터가 없다면 ValueError 발생)
test2.remove('b')
print(test2)

# pop() 메서드로 데이터를 꺼내서 삭제 (데이터가 없다면 IndexError 발생)
test2.pop(0)
print(test2)

# 결과값
# ['b', 'c']
# ['c']
# []&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780146713399&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 데이터 조회

test3 = ['a', 'b', 'c']

# index() 메서드를 통한 인덱스 검색
test3.index('b')
print(test3.index('b'))

# []를 이용한 인덱스 조회
target_data = test3[0]
print(target_data)

# enumerate() 함수를 통한 데이터 전체 조회
for idx, data in enumerate(test3):
    print(f'{idx}번 데이터 : {data}')
    
# 결과값
# 1
# a
# 0번 데이터 : a
# 1번 데이터 : b
# 2번 데이터 : c&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  배열이 없었을 때는 모든 데이터의 메모리 주소를 기억하거나 메모리 전체를 뒤져서 데이터를 찾았어야 했지만, 데이터를 메모리에 배열로 저장하면 첫 번째 데이터의 주소와 인덱스만 알고 있다면 빠르게 데이터에 접근할 수 있게 된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 큐 (Queue)&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1780153767149&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Queue:
    def __init__(self):
        self.queue = []                # 데이터를 저장할 빈 리스트 생성

    def enqueue(self, data):
        self.queue.append(data)        # .append()를 이용해 리스트 가장 뒤에 데이터 추가

    def dequeue(self):
        if len(self.queue) == 0:       # 큐가 비어있다면 None을 리턴
            print('큐가 비어있습니다.')
            return None
        
        Take_data = self.queue.pop(0)  # FIFO에 맞춰 0번 인덱스 데이터를 제거하고 반환
        return Take_data
    
    def display(self):
        print(self.queue)

q = Queue()

q.enqueue('a')
q.enqueue('b')
q.enqueue('c')
q.display()

q.dequeue()
q.display()

# 결과값
# ['a', 'b', 'c']
# ['b', 'c']             # 가장 먼저 들어온 데이터가 가장 먼저 나간다.&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;  프린터의 대기열이나 티켓팅 시스템과 같이 수많은 요청이 한번에 몰려드는 상황에 가장 먼저 들어온 요청을 가장 먼저 출력해 주는 순서(FIFO)로 진행하면서 데이터가 깨지거나 순서가 뒤섞이지 않고 처리할 수 있게 되었다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 스택(Stack)&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1780159753592&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Stack:
    def __init__(self):
        self.stack = []                  # 데이터 저장용 빈 리스트 생성
    
    def push(self, data):                # 스택의 맨 위에 데이터 추가
        self.stack.append(data)

    def pop(self):
        if len(self.stack) == 0:         # 스택이 비어있다면 None 리턴
            print('스택이 비어있습니다.')
            return None
        return self.stack.pop()          # 스택의 위쪽 데이터를 제거하고 반환 (LIFO)

    def display(self):
        print(self.stack)

s = Stack()

s.push('a')
s.push('b')
s.push('c')
s.display()

s.pop()
s.display()

# 결과값
# ['a', 'b', 'c']
# ['a', 'b']          # 가장 나중에 들어온 데이터가 가장 먼저 나간걸 알 수 있다.&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;  홈페이지 뒤로가기나 Ctrl+Z를 누르면 삭제취소가 돼 듯, 가장 나중에 들어온 데이터를 가장 먼저 출력해 주면서 데이터의 손실 없이 역순으로 안전하게 돌아갈 수 있게 되었다.&lt;/p&gt;</description>
      <category>자료구조, 알고리즘</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/21</guid>
      <comments>https://gudwns5533.tistory.com/21#entry21comment</comments>
      <pubDate>Sun, 31 May 2026 02:06:31 +0900</pubDate>
    </item>
    <item>
      <title>매직 메서드</title>
      <link>https://gudwns5533.tistory.com/20</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;매직 메서드(magic method)는 파이썬에서 특정 문법이나 연산이 실행될 때 자동으로 호출되는 특별한 메서드로, __len__, __str__, __getitem__처럼 양쪽에 밑줄(__)이 붙는 형태를 가진다. 이 메서드들은 클래스에 구현하면 len(obj), print(obj), obj[0], obj + obj 같은 파이썬의 기본 문법과 자연스럽게 연결되어 객체의 동작을 직접 정의할 수 있다. 즉, 매직 메서드는 객체를 리스트처럼 사용하거나, 연산을 수행하거나, 출력 형태를 바꾸는 등 파이썬의 문법과 객체를 이어주는 핵심 기능이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;매직 메서드의 사용이유&lt;br /&gt;&lt;/b&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;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;객체 출력&lt;br /&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. __str__()&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;__str__()는 객체를 사람이 읽기 쉬운 문자열로 표현하기 위해 사용하는 매직 메서드로, print(obj)나 str(obj)가 호출될 때 자동으로 실행된다. 이 메서드를 클래스에 정의하면 객체를 출력할 때 기본 주소값 대신 원하는 형태로 내용을 보여줄 수 있어 디버깅이나 사용자 출력에 매우 유용하다.&lt;/p&gt;
&lt;pre id=&quot;code_1780043547836&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Fruit:
    def __init__(self, name, quantity):
        self.name = name
        self.quantity = quantity

    def __str__(self):
        return f&quot;{self.name} {self.quantity}개&quot;       # str() 함수와 연결되어있는 매직 메서드
        
apple = Fruit('사과', 10)
print(apple)       # print(str(apple))이 생략되어있다.

# 사과 10개&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;size18&quot;&gt;&lt;b&gt;2. __repr__()&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;__repr__()은 객체를 개발자 관점에서 정확하게 표현하는 문자열을 반환하는 매직 메서드로, 주로 디버깅이나 로그에서 사용된다. repr(obj)를 호출하거나 인터프리터에서 객체를 직접 평가할 때 자동으로 호출되며, 가능하다면 객체를 다시 생성할 수 있는 형태의 문자열을 반환하는 것이 권장된다. __str__()이 사람이 읽기 좋은 출력이라면, __repr__()은 객체의 구조와 상태를 명확하게 보여주는 공식적인 표현이다.&lt;/p&gt;
&lt;pre id=&quot;code_1780043858348&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Fruit:
    def __init__(self, name, quantity):
        self.name = name
        self.quantity = quantity
        
    def __str__(self):
        return f&quot;{self.name} {self.quantity}개&quot;

    def __repr__(self):
        return f&quot;Fruit(name='{self.name}', quantity={self.quantity})&quot;     # 객체를 만드는 형태의 문자열

apple = Fruit('사과', 10)
print(repr(apple))  # __repr__ 호출
print(apple)        # __str__ 없으면 __repr__ 호출  (같이있을때는 __str__이 우선시 됨)

# Fruit(name='사과', quantity=10)
# 사과 10개&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;제너레이터&lt;/b&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;제너레이터(generator)는 yield 키워드를 사용해 값을 한 번에 하나씩 생성해 반환하는 반복 객체로, 함수 형태로 간단하게 이터레이터를 만들 수 있는 파이썬의 기능이다. 일반 함수처럼 보이지만 yield를 만나면 값을 반환하면서도 실행 상태를 유지하고, 다음 호출 시 이어서 실행되기 때문에 전체 데이터를 한 번에 만들지 않고 필요할 때마다 생성할 수 있어 메모리 효율이 매우 높다.&lt;/p&gt;
&lt;pre id=&quot;code_1780044204715&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Counter:
    def __init__(self, max):
        self.max = max
        self.current = 0

    def __iter__(self):
        return self           # 자기 자신을 iterator로 사용

    def __next__(self):
        if self.current &amp;lt; self.max:
            self.current += 1
            return self.current
        else:
            raise StopIteration
            
counter = Counter(3)

# for문: iter(), next()함수를 자동으로 호출, StopIteration으로 멈춤
for num in counter:
    print(num)
    
# 1
# 2
# 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  for문이 내부적으로 자동으로 iter()를 호출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  이터레이터는 이터러블로부터 실제로 값을 하나씩 꺼내는 객체로, __iter__()와 __next__() 메서드를 모두 구현.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  __iter__()는 반복을 시작하게 하고, __next__()는 값을 하나씩 꺼내며, 더 이상 반환할 값이 없으면 StopIteration 예외를 발생시켜 반복을 종료.&lt;/p&gt;
&lt;pre id=&quot;code_1780044336843&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def counter(max):
    current = 0
    while current &amp;lt; max:
        current += 1
        yield current

for num in counter(3):
    print(num)&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;  yield의 의미&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;값을 반환한다.&lt;/li&gt;
&lt;li&gt;함수 상태를 저장한다.&lt;/li&gt;
&lt;li&gt;다음 호출 시 이어서 실행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  제너레이터는 자동으로 __iter__ + __next__를 만들어준다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;인덱싱&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. __getitem__()&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;__getitem__()은 객체에서 인덱스나 키를 이용해 값을 조회할 때 호출되는 매직 메서드로, obj[index] 또는 obj[key] 형태로 접근하면 자동으로 실행된다. 이 메서드를 구현하면 사용자 정의 객체도 리스트나 딕셔너리처럼 데이터를 꺼낼 수 있으며, 내부적으로는 전달된 인덱스나 키를 기반으로 값을 반환하도록 작성하면 된다.&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;&lt;b&gt;2. __setitem__()&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;__setitem__()은 객체에서 인덱스나 키를 이용해 값을 할당할 때 호출되는 매직 메서드로, obj[index] = value 형태의 코드가 실행되면 자동으로 호출된다. 이 메서드를 구현하면 사용자 정의 객체에 값을 저장하거나 수정하는 동작을 제어할 수 있으며, 전달된 인덱스와 값을 이용해 내부 데이터를 업데이트 하도록 구현한다.&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;&lt;b&gt;3. __delitem__()&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;__delitem__()은 객체에서 인덱스나 키를 이용해 값을 삭제할 때 호출되는 매직 메서드로, del obj[index] 형태의 코드가 실행되면 자동으로 호출된다. 이 메서드를 구현하면 사용자 정의 객체에서 특정 데이터를 삭제하는 로직을 정의할 수 있으며, 전달된 인덱스를 기반으로 내부 데이터를 제거하도록 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1780054008615&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MyList:
    def __init__(self):
        self.data = []

    def __getitem__(self, index):
        return self.data[index]

    def __setitem__(self, index, value):
        self.data[index] = value

    def __delitem__(self, index):
        del self.data[index]
        
ml = MyList()
ml.data = [10, 20, 30]
print(ml[0])     # __getitem__ -&amp;gt; 10
ml[1] = 99       # __setitem__
print(ml.data)   # [10, 99, 30]
del ml[0]        # __delitem__
print(ml.data)   # [99, 30]

# 10
# [10, 99, 30]
# [99, 30]&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;객체를 함수, 값처럼 사용&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. __call__()&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;__call__()은 객체를 함수처럼 호출할 수 있게 만들어주는 매직 매서드로, obj() 형태로 객체를 실행하면 자동으로 호출된다. 이 메서드를 구현하면 클래스 인스턴스가 일반 함수처럼 동작할 수 있으며, 내부 데이터의 개수를 반환하도록 작성하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1780055244504&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Multiplier:
    def __init__(self, n):
        self.n = n

    def __call__(self, x):
        return x * self.n

m = Multiplier(3)

print(m(10))  # 30

# 30&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;size18&quot;&gt;&lt;b&gt;2. __len__()&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;__len__()은 객체의 길이를 정의하는 매직 메서드로, len(obj)를 호출할 때 자동으로 실행된다. 이 메서드를 구현하면 사용자 정의 객체도 리스트나 문자열처럼 길이를 가질 수 있으며, 내부 데이터의 개수를 반환하도록 작성하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1780055359731&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Basket:
    def __init__(self):
        self.items1 = []
        self.items2 = []

    def __len__(self):
        return len(self.items1)  # __len__이 없이 print(len(b))를 출력하고자 하면 items1과 items2중 무엇인지 모르니 오류발생
    
b = Basket()
b.items1 = [&quot;사과&quot;, &quot;바나나&quot;, &quot;오렌지&quot;]
b.items2 = [&quot;메론&quot;, &quot;체리&quot;]

print(len(b))

# 3&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;with 문&lt;/b&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;with 문은 파일, 네트워크, DB 연결처럼 사용 후 반드시 정리(clean-up)가 필요한 자원을 안전하게 관리하기 위한 문법으로, 블록이 시작될 때 필요한 작업을 수행하고 종료될 때 자동으로 정리 작업을 실행해준다. 이를 통해 try-finally를 직접 작성하지 않아도 되고, 예외가 발생하더라도 자원이 정상적으로 해제되도록 보장할 수 있다.&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;&lt;b&gt;1. __enter__()&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;__enter__()는 with 문에 진입할 때 자동으로 호출되는 매직 메서드로, 자원을 준비하거나 초기화하는 역할을 한다. 이 메서드에서 반환한 값은 as 뒤에 변수에 할당되어 with 블록 내부에서 사용된다.&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;&lt;b&gt;2. __exit__()&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;__exit__()는 with 블록이 끝날 때 자동으로 호출되는 매직 메서드로, 사용한 자원을 정리(clean-up)하는 역할을 한다. 이 메서드는 예외 발생 여부와 관계없이 반드시 실행되며, 예외가 발생한 경우에는 예외 타입, 값, traceback 정보를 인자로 받아 처리할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1780055752652&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MyResource:
    def __enter__(self):
        print(&quot;자원 열기&quot;)
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print(&quot;자원 정리&quot;)

with MyResource() as res:
    print(&quot;자원 사용 중&quot;)
    
# 자원 열기
# 자원 사용 중
# 자원 정리&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780055973791&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MyResource:
    def __enter__(self):
        print(&quot;자원 열기&quot;)
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print(&quot;자원 정리&quot;)
        print(&quot;예외 타입:&quot;, exc_type)

with MyResource():
    print(&quot;작업 중&quot;)
    # 1 / 0  # 에러 발생

#   에러가 나도 __exit__()는 반드시 실행됩니다.
# 자원 열기
# 작업 중
# 자원 정리
# 예외 타입: None&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Python</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/20</guid>
      <comments>https://gudwns5533.tistory.com/20#entry20comment</comments>
      <pubDate>Fri, 29 May 2026 21:29:28 +0900</pubDate>
    </item>
    <item>
      <title>예외처리</title>
      <link>https://gudwns5533.tistory.com/19</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;예외&lt;/b&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;예외(Exceptioin)는 프로그램 실행 중 발생할 수 있는 예상치 못한 문제 또는 오류 상황을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외가 발생하면 프로그램은 중단되기 때문에 이를 적절하게 처리하여 중단을 방지하거나 오류에 대한 정보를 사용자에게 제공해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1780039027390&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int(10 / 3)
print(5 / 0)
print(4 / 2)

# ZeroDivisionError: division by zero
# 둘째줄 코드에서 예외가 발생하고 프로그램은 세번째줄로 넘어가지않고 중단된다.&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;예외 계층 구조&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1780039498684&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;BaseException

BaseException
 ├── Exception        &amp;larr; 우리가 보통 사용하는 예외
 │    ├── ValueError
 │    ├── TypeError
 │    ├── ZeroDivisionError
 │    └── ...
 ├── KeyboardInterrupt
 └── SystemExit&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;  BaseException이 따로 있는 이유는 &amp;ldquo;진짜 에러&amp;rdquo;와 &amp;ldquo;프로그램 제어용 이벤트&amp;rdquo;를 구분하기 위해서이다.&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;1. Exception&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 실행 중 발생한 일반적인 오류&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;0으로 나누기&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;&lt;b&gt;2. KeyboardInterrupt&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 Ctrl + C 눌렀을 때 발생&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  프로그램을 강제로 멈추는 신호&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. SystemExit&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;  내부적으로 exit() 호출 시 발생&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  KeyboardInterrupt와 SystemExit는 사실 '에러'가 아니라 프로그램을 멈추거나 종료하기 위한 신호&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;예외 처리 기본 구조&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1780040679275&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try:
    # 예외가 발생할 가능성이 있는 코드
except ExceptionType1:  # 'ExceptionType1'에는 실제 예외 유형이 들어갑니다.
    # ExceptionType1 예외가 발생했을 때 실행될 코드
except ExceptionType2:  # 'ExceptionType2'에는 다른 예외 유형이 들어갑니다.
    # ExceptionType2 예외가 발생했을 때 실행될 코드
# 추가적인 except 블록을 계속 추가할 수 있습니다.
else:         # 필수 x
    # try 블록에서 예외가 발생하지 않았을 때 실행될 코드
finally:      # 필수 x
    # 예외 발생 여부와 관계없이 항상 실행될 코드&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780040769842&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try:
    raise KeyboardInterrupt       # raise: 예외를 강제로 발생시키는 코드
except Exception:
    print(&quot;Exception 잡힘&quot;)
except BaseException:
    print(&quot;BaseException 잡힘&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  이때 except BaseException: 을 except:로 사용한다면 시스템을 정상적으로 종료해야하는 신호까지 모두 막아버릴 수 있으므로 생략해선 안된다.&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;&lt;b&gt;raise&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;raise는 파이썬에서 의도적으로 예외를 발생시키거나(throw), 현재 발생한 예외를 다시 전달(re-raise)하기 위해 사용하는 키워드이다. 이를 통해 잘못된 입력이나 비정상적인 상태를 명확하게 알리고, 호출한 쪽에서 적절히 처리하도록 흐름을 제어할 수 있다.&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;&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;867&quot; data-origin-height=&quot;633&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhgqa0/dJMcaayJgWM/dvwbCkk7DrIcR5DefJuvEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhgqa0/dJMcaayJgWM/dvwbCkk7DrIcR5DefJuvEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhgqa0/dJMcaayJgWM/dvwbCkk7DrIcR5DefJuvEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbhgqa0%2FdJMcaayJgWM%2FdvwbCkk7DrIcR5DefJuvEk%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;867&quot; height=&quot;633&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;633&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1780041140681&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try:
    data = [10, 20, 30, 40, 50]
    # print(data[5])        -&amp;gt; IndexError
    # print(int('안녕'))    -&amp;gt; ValueError
    # print(5 / 0)          -&amp;gt; ZeroDivisionError
except IndexError:
    print('인덱스 오류')
except ValueError:
    print('입력 오류')
except ZeroDivisionError:
    print('0으로 나눌 수 없음')
except Exception:
    print('예외 발생!')
else:
    print('에러가 발생하지 않은 정상적인 프로그램')
finally:
    print('에러에 관계없이 무조건 실행되는 문장')
print('프로그램을 종료합니다')&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;예외 객체 내부 구조&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1780041235001&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Exception 객체
├── args            &amp;rarr; 에러 메시지
├── __str__()       &amp;rarr; 사용자용 메시지
├── __repr__()      &amp;rarr; 개발자용 표현
├── __class__       &amp;rarr; 에러 타입
└── __traceback__   &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;1. args (에러 메세지 튜플)&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;예외 객체를 생성할 때 전달한 인자(Arguments)들이 담기는 튜플이다. 보통은 에러의 원인을 설명하는 문자열 메세지가 들어간다.&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;2. __str__() (사용자용 메세지)&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;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. __repr__() (개발자용 공식 표현)&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;이 객체가 어떤 클래스의 객체이고, 어떤 인자를 가지고 생성되었는지 명확하게 보여주는 개발자용 문자열. 주로 디버깅이나 로그(Log)를 남길 때 유용하다.&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;4. __class__ (에러 타입)&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;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;5. __traceback__ (에러 발생 위치 및 경로)&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;코드가 실행되다가 정확히 몇 번째 줄에서, 어떤 함수들을 거쳐 에러가 났는지에 대한 발자취(Traceback 객체)를 담고 있다. 그냥 출력하면 객체 주소만 보이기 때문에, 파이썬의 내장 모듈인 traceback과 함께 사용하면 리포트 형태로 출력된다.&lt;/p&gt;
&lt;pre id=&quot;code_1780042373488&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import traceback

try:
    int(&quot;abc&quot;)
except Exception as e:    # as는 발생한 예외 객체를 변수로 받아서, 그 안에 담긴 정보를 활용하기 위함.
    print(&quot;에러 메시지:&quot;, e)
    print(&quot;에러 타입:&quot;, type(e))
    print(&quot;args:&quot;, e.args)
    print(e.__str__())
    print(e.__repr__())
    print(e.__class__)
    print(e.__traceback__)
    traceback.print_tb(e.__traceback__)
    
# 에러 메시지: invalid literal for int() with base 10: 'abc'
# 에러 타입: &amp;lt;class 'ValueError'&amp;gt;
# args: (&quot;invalid literal for int() with base 10: 'abc'&quot;,)
# invalid literal for int() with base 10: 'abc'
# ValueError(&quot;invalid literal for int() with base 10: 'abc'&quot;)
# &amp;lt;class 'ValueError'&amp;gt;
# &amp;lt;traceback object at 0x000001FD7D205840&amp;gt;
#   File &quot;C:\Users\Administrator\AppData\Local\Temp\ipykernel_324\1305646378.py&quot;, line 4, in &amp;lt;module&amp;gt;
#     int(&quot;abc&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780042616671&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def func1():
    func2()
    # try:
    #     func2()
    # except TypeError:
    #     print('타입이 올바르지 않습니다')

def func2():
    func3()
    # try:
    #     func3()
    # except TypeError:
    #     print('타입이 올바르지 않습니다')

def func3():
    print('%d' % '문자열')
    # try:
    #     print('%d' % '문자열')
    # except TypeError:
    #     print('타입이 올바르지 않습니다')

# func1()

try:
    func1()
except TypeError:
    print('타입이 올바르지 않습니다')&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;  func3에서 에러가 발생하면 이를 해결할 try-except가 있을 때까지 func3 &amp;rarr; func2 &amp;rarr; func1 &amp;rarr; 호출부 순서로 에러를 위로 전파한다. 따라서 어느 부분에서 예외처리를 해도 상관이 없다.&lt;/p&gt;</description>
      <category>Python</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/19</guid>
      <comments>https://gudwns5533.tistory.com/19#entry19comment</comments>
      <pubDate>Fri, 29 May 2026 17:20:39 +0900</pubDate>
    </item>
    <item>
      <title>객체지향 프로그램 4대 패러다임</title>
      <link>https://gudwns5533.tistory.com/18</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;객체지향 프로그래밍(OOP) 4대 패러다임은 &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;&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;&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;캡슐화&lt;/b&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;캡슐화(Encapsulation)는 객체지향 프로그래밍에서 데이터(속성)와 이를 처리하는 메서드를 하나로 묶고, 외부에서 내부 구현에 직접 접근하지 못하도록 제한하여 객체를 보호하는 개념이다. 파이썬에서는 public, _protected, __private와 같은 네이밍 규칙을 통해 접근 수준을 구분하며, 필요할 경우 getter / setter 메서드나 @property를 사용해 안전하게 값을 읽고 수정할 수 있다. 이를 통해 객체의 상태를 임의로 변경하는 것을 방지하고, 코드의 안정성과 유지보수성을 높일 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1779970990769&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Fruit:
    def __init__(self, name, price):
        self.name = name          # public
        self.__price = price      # private (캡슐화)

    # getter
    def get_price(self):
        return self.__price

    def set_price(self, price):
        if price &amp;gt; 0:
            self.__price = price
        else:
            print(&quot;가격은 0보다 커야 합니다.&quot;)

    def print_info(self):
        print(f&quot;과일: {self.name}&quot;)
        print(f&quot;가격: {self.__price}&quot;)


class Apple(Fruit):                           # Fruit 클래스 상속
    def __init__(self, name, price, origin):
        super().__init__(name, price)
        self.origin = origin

    def print_origin(self):
        print(f&quot;{self.name}의 원산지: {self.origin}&quot;)
        
apple = Apple(&quot;사과&quot;, 3000, &quot;한국&quot;)   

apple.print_info()
apple.print_origin()

print(&quot;가격 조회:&quot;, apple.get_price())

print(&quot;----- 가격 수정 -----&quot;)
apple.set_price(3500)
print(&quot;수정된 가격:&quot;, apple.get_price())

print(&quot;----- 잘못된 값 입력 -----&quot;)
apple.set_price(-1000)                        # 검증 실패

print(&quot;----- 외부 접근 시도 -----&quot;)
apple.__price = 10000                         # 새로운 변수 생성 (실제 private 변경 아님)

print(&quot;getter로 확인:&quot;, apple.get_price())    # 여전히 3500
print(&quot;직접 접근:&quot;, apple.__price)            # 10000 (다른 변수)

# 과일: 사과
# 가격: 3000
# 사과의 원산지: 한국
# 가격 조회: 3000
# ----- 가격 수정 -----
# 수정된 가격: 3500
# ----- 잘못된 값 입력 -----
# 가격은 0보다 커야 합니다.
# ----- 외부 접근 시도 -----
# 실제 가격: 3500
# 외부에서 만든 값: 10000&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;  self.__price는 실제로 private이 되는 것이 아니라 내부적으로 _Fruit__price로 바뀐다.&lt;/p&gt;
&lt;pre id=&quot;code_1780035437861&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Fruit:
    def __init__(self, name, price):
        self.name = name
        self.__price = price  # private

    # getter &amp;rarr; 속성처럼 접근 가능 (변수처럼 사용 가능하게 해줌)
    @property
    def price(self):
        return self.__price

    # setter &amp;rarr; 값 할당 시 자동 호출 (getter와 이름이 일치해야함) 
    @price.setter
    def price(self, value):
        if value &amp;gt; 0:
            self.__price = value
        else:
            print(&quot;가격은 0보다 커야 합니다.&quot;)

    def print_info(self):
        print(f&quot;과일: {self.name}&quot;)
        print(f&quot;가격: {self.price}&quot;)  # 함수가 아니라 속성처럼 사용


class Apple(Fruit):
    def __init__(self, name, price, origin):
        super().__init__(name, price)
        self.origin = origin

    def print_origin(self):
        print(f&quot;{self.name}의 원산지: {self.origin}&quot;)
        
apple = Apple(&quot;사과&quot;, 3000, &quot;한국&quot;)

apple.print_info()
apple.print_origin()

print(&quot;가격 조회:&quot;, apple.price)  # getter 호출

print(&quot;----- 가격 수정 -----&quot;)
apple.price = 3500               # setter 호출
print(&quot;수정된 가격:&quot;, apple.price)

print(&quot;----- 잘못된 값 입력 -----&quot;)
apple.price = -1000              # 검증 실패

print(&quot;----- 외부 접근 시도 -----&quot;)
apple.__price = 10000            # 새로운 변수 생성

print(&quot;실제 가격:&quot;, apple.price)  # 여전히 3500
print(&quot;외부에서 만든 값:&quot;, apple.__price)

# 과일: 사과
# 가격: 3000
# 사과의 원산지: 한국
# 가격 조회: 3000
# ----- 가격 수정 -----
# 수정된 가격: 3500
# ----- 잘못된 값 입력 -----
# 가격은 0보다 커야 합니다.
# ----- 외부 접근 시도 -----
# 실제 가격: 3500
# 외부에서 만든 값: 10000&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;@property는&amp;nbsp;getter/setter를&amp;nbsp;함수가&amp;nbsp;아닌&amp;nbsp;&amp;ldquo;속성처럼&amp;rdquo;&amp;nbsp;사용하게&amp;nbsp;해주는&amp;nbsp;Pythonic한&amp;nbsp;캡슐화&amp;nbsp;방식입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;상속&lt;/b&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;상속(inheritance)은 객체지향 프로그래밍에서 기존 클래스(부모 클래스)의 속성과 메서드를 새로운 클래스(자식 클래스)가 물려받아 재사용하고 확장하는 개념입니다. 이를 통해 공통 기능을 중복 작성하지 않고 코드 재사용성을 높일 수 있으며, 자식 클래스는 부모의 기능을 그대로 사용하거나 필요한 부분만 추가하거나 오버라이딩(재정의)하여 동작을 변경할 수 있습니다. 파이썬에서는 class 자식클래스(부모클래스) 형태로 상속을 구현하며, super()를 사용해 부모 클래스의 초기화나 메서드를 호출할 수 있습니다. 이러한 구조는 코드의 유지보수성과 확장성을 크게 향상시키는 핵심 개념입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1779971382383&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Parent:
    pass

class Child(Parent):
    pass&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;size18&quot;&gt;&lt;b&gt;1. 상속 구조&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779971414056&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Fruit:
    def __init__(self, name, quantity):
        self.name = name
        self.quantity = quantity

    def store(self, place):
        print(f'{self.name}를 {place}에 보관합니다')

    def sell(self, amount):
        print(f'{self.name}를 {amount}개 판매합니다')

fruit = Fruit('과일', 100)
fruit.store('창고')
fruit.sell(10)

# 과일를 창고에 보관합니다
# 과일를 10개 판매합니다

class Apple(Fruit):
    pass

# Fruit 클래스를 상속 받았기 때문에
# Fruit 생성자의 매개변수를 전달해야 함
apple = Apple('사과', 50)

apple.store('냉장고')
apple.sell(5)

# 사과를 냉장고에 보관합니다
# 사과를 5개 판매합니다&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;size18&quot;&gt;&lt;b&gt;2. 생성자 호출&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779972190642&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Fruit:
    def __init__(self, name):
        print(&quot;Fruit 생성자 호출&quot;)
        self.name = name

class Apple(Fruit):
    pass

apple = Apple(&quot;사과&quot;)

# Fruit 생성자 호출

#   자식 클래스에 __init__이 없을 때 자동 호출


class Fruit:
    def __init__(self, name):
        print(&quot;Fruit 생성자 호출&quot;)
        self.name = name

class Apple(Fruit):
    def __init__(self, name):
        print(&quot;Apple 생성자 호출&quot;)
        self.name = name

apple = Apple(&quot;사과&quot;)
print(apple.name)

# Apple 생성자 호출
# 사과

class Fruit:
    def __init__(self, name):
        print(&quot;Fruit 생성자 호출&quot;)
        self.name = name

class Apple(Fruit):
    def __init__(self):
        print(&quot;Apple 생성자 호출&quot;)

apple = Apple()
# print(apple.name)    # AttributeError: 'Apple' object has no attribute 'name'

# Apple 생성자 호출

#   자식 클래스에 __init__을 직접 정의하면 부모 생성자 호출 안됨


class Fruit:
    def __init__(self, name):
        print(&quot;Fruit 생성자 호출&quot;)
        self.name = name

class Apple(Fruit):
    def __init__(self, name):
        super().__init__(name)  # 부모 생성자 호출
        print(&quot;Apple 생성자 호출&quot;)

apple = Apple('사과')
print(apple.name)

# Fruit 생성자 호출
# Apple 생성자 호출
# 사과

#    super() 함수는 현재 클래스의 부모 클래스를 참조하며, 부모 클래스의 생성자를 호출할 수 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. 오버라이딩&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;오버라이딩(overriding)은 상속 관계에서 부모 클래스에 이미 정의된 메서드를 자식 클래스에서 동일한 이름으로 다시 정의하여, 동작을 변경하는 것을 의미한다. 이를 통해 자식 클래스는 부모의 기능을 그대로 사용할 수도 있고, 필요에 따라 일부 또는 전체 동작을 자신에게 맞게 수정할 수 있다. 파이썬에서는 별도의 키워드 없이 같은 메서드 이름을 정의하면 자동으로 오버라이딩이 이루어지며, 필요할 경우 super()를 사용해 부모의 메서드를 함께 호출할 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1779973690475&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Fruit:
    def __init__(self, name, quantity):
        self.name = name
        self.quantity = quantity

    def eat(self):
        print(f'{self.name}를 먹습니다')

    def store(self, place):
        print(f'{self.name}를 {place}에 보관합니다')

class Apple(Fruit):
    def wash(self):
        print(f'{self.name}를 깨끗이 씻습니다')

    # 오버라이딩
    def eat(self):
        print(f'{self.name}를 아주 맛있게 먹습니다')

    # 부모 메서드 호출
    def superEat(self):
        super().eat()
        
apple = Apple('사과', 10)
apple.eat()                # 오버라이딩된 메서드
apple.store('냉장고')       # 부모 메서드
apple.wash()               # 자식 메서드
apple.superEat()           # 부모 eat 호출

# 사과를 아주 맛있게 먹습니다
# 사과를 냉장고에 보관합니다
# 사과를 깨끗이 씻습니다
# 사과를 먹습니다

fruit = Fruit('과일', 10)
fruit.eat()
fruit.store('창고')
# fruit.wash()  # AttributeError: 'Fruit' object has no attribute 'wash'
# 부모는 자식클래스에 접근할 수 없다.

# 과일를 먹습니다
# 과일를 창고에 보관합니다&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;size18&quot;&gt;&lt;b&gt;4. 다중 상속&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_1779973881988&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Parent1:
    pass

class Parent2:
    pass

class Child(Parent1, Parent2):
    pass&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1779974159378&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Fruit:
    def __init__(self, name, quantity):
        self.name = name
        self.quantity = quantity

    def eat(self):
        print(f'{self.name}를 먹습니다')

    def sleep(self, hour):
        print(f'{self.name}를 {hour}시간 동안 보관합니다')

    def supersleep(self, hour):
        super().sleep(hour)    


class Storage:
    def __init__(self, name, quantity):
        self.name = name
        self.quantity = quantity

    def process(self, hour):
        print(f'{self.name}를 {hour}시간 동안 가공합니다')

    def sleep(self, hour):
        print(f'{self.name}를 {hour}시간 동안 저온 숙성합니다')

# 다중 상속
class ProcessedFruit(Fruit, Storage):
    pass
    
fruit = ProcessedFruit('사과', 10)
fruit.eat()       # Fruit class
fruit.process(2)  # Storage class
fruit.sleep(8)    # MRO에 따라 Fruit class의 sleep 실행
fruit.supersleep(5)

# 사과를 먹습니다
# 사과를 2시간 동안 가공합니다
# 사과를 8시간 동안 보관합니다
# 사과를 5시간 동안 저온 숙성합니다&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;size18&quot;&gt;&lt;b&gt;object 클래스&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;파이썬의 object 클래스는 모든 클래스의 최상위 부모 클래스(루트 클래스)로, 사용자가 클래스를 정의할 때 명시적으로 상속하지 않아도 자동으로 상속되는 기본 클래스이다. 이 클래스는 객체가 가져야 할 최소한의 공통 기능을 제공하며, __str__, __repr__, __eq__ 같은 기본 매직 메서드의 기본 구현을 포함하고 있다. 따라서 모든 파이썬 객체는 내부적으로 object를 기반으로 동작하며, 새로운 클래스를 정의할 때도 이 기본 기능들을 물렵잗아 사용할 수 있다. 즉, object 클래스는 파이썬 객체 시스템의 가장 기초가 되는 공통 부모이다.&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;&lt;b&gt;MRO&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;MRO(Method Resolution Order)는 파이썬에서 다중 상속을 사용할 때, 메서드나 속성을 찾는 순서를 정의하는 규칙이다. MRO는 특히 여러 부모 클래스를 상속받는 경우에 어떤 부모 클래스에서 메서드를 먼저 찾을지를 결정하며, 이를 통해 클래스 간의 메서드 충동을 해결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1780035779028&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Base:
    def hello(self):
        print(&quot;Base&quot;)
        
class Clean(Base):
    def hello(self):
        print(&quot;Clean&quot;)
        super().hello()
        print(&quot;Clean 끝&quot;)

class Pack(Base):
    def hello(self):
        print(&quot;Pack&quot;)
        super().hello()
        print(&quot;Pack 끝&quot;)

class Product(Clean, Pack):
    def hello(self):
        print(&quot;Product&quot;)
        super().hello()
        print(&quot;Product 끝&quot;)
        
p = Product()
p.hello()
print(Product.mro())


# Product
# Clean
# Pack
# Base
# Pack 끝
# Clean 끝
# Product 끝
# [&amp;lt;class '__main__.Product'&amp;gt;, &amp;lt;class '__main__.Clean'&amp;gt;, &amp;lt;class '__main__.Pack'&amp;gt;, &amp;lt;class '__main__.Base'&amp;gt;, &amp;lt;class 'object'&amp;gt;]

# MRO에 따라 Product &amp;gt; Clean &amp;gt; Pack &amp;gt; Base 순으로 정의된다&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;다형성&lt;/b&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;다형성(polymorphism)은 같은 이름의 메서드나 함수를 호출하더라도, 객체의 종류에 따라 서로 다른 방식으로 동작하는 특성을 의미한다. 파이썬은 정적 타입 언어처럼 명시적인 인터페이스 구현을 요구하지 않고, 동일한 메서드를 가지고 있으면 타입과 관계없이 사용할 수 있는 덕 타이핑(duck typing)을 기반으로 다형성을 자연스럽게 지원한다. 예를 들어 여러 객체가 pay()나 speak() 같은 동일한 메서드를 가지고 있으면, 하나의 함수에서 객체의 타입을 구분하지 않고도 공통된 방식으로 처리할 수 있다.이러한 특징은 코드의 유연성과 확장성을 높여주며, 새로운 기능을 추가할 때 기존 코드를 수정하지 않아도 되는 장점을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  덕 타이핑(duck typing) : 객체의 실제 클래스가 무엇인지 중요하지 않고, 필요한 메서드나 기능을 가지고 있으면 같은 방식으로 사용하는 것&lt;/p&gt;
&lt;pre id=&quot;code_1780037140249&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class CardPayment:
    def pay(self, amount):
        print(f&quot;카드로 {amount}원 결제합니다&quot;)

class CashPayment:
    def pay(self, amount):
        print(f&quot;현금으로 {amount}원 결제합니다&quot;)

class KakaoPay:
    def pay(self, amount):
        print(f&quot;카카오페이로 {amount}원 결제합니다&quot;)

def process_payment(payment, amount):
    payment.pay(amount)    # 어떤 결제 수단이든 그냥 .pay()를 호출
    
card = CardPayment()
cash = CashPayment()
kakao = KakaoPay()

process_payment(card, 10000)
process_payment(cash, 5000)
process_payment(kakao, 2000)

# 카드로 10000원 결제합니다
# 현금으로 5000원 결제합니다
# 카카오페이로 2000원 결제합니다&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;  payment라는 매개변수로 어떤 객체가 들어올지 미리 따지지 않고, 카드/현금/카카오페이 상관없이 .pay(amount)라는 동일한 명령(메서드)을 내린다. 호출하는 방식은 똑같이만 어떤 객체가 들어가느냐에 따라 실제 실행되는 결과가 달라지는 것이 바로 다형성이다.&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;  추후에 새로운 기능이 추가되어도 결제를 처리하는 process_payment함수는 고칠 필요가 없다. 새로운 클래스를 만들고 그 안에 .pay() 메서드만 정의해주면 되기 때문에 확장성면에서 큰 장점이 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;추상화&lt;/b&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;추상화(abstraction)는 복잡한 내부 구현은 숨기고, 외부에서는 필요한 핵심 기능만 간단하게 사용할 수 있도록 만든 개념이다. 사용자는 객체가 내부적으로 어떻게 동작하는지 몰라도, 제공된 메서드만으로 기능을 사용할 수 있으며, 이를 통해 코드의 복잡도를 줄이고 이해와 사용을 쉽게 한다. 파이썬에서는 일반 클래스 설계뿐 아니라 abc 모듈의 ABC와 @abstractmethod를 사용해 추상 클래스를 정의하고, 반드시 구현해야 하는 메서드를 강제함으로써 일관된 구조를 유지할 수 있다. 즉, 추상화는 &quot;어떻게 동작하는 지&quot; 보다 &quot;무엇을 할 수 있는지&quot;에 집중하게 만드는 설계 방식이다.&lt;/p&gt;
&lt;pre id=&quot;code_1780038558494&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from abc import ABC, abstractmethod  # abc모듈에서 import

# 추상 클래스
class Payment(ABC):
    
    @abstractmethod
    def pay(self, amount):
        pass  # 반드시 구현해야 하는 메서드 (강제성 부여)

# 카드 결제
class CardPayment(Payment):
    def pay(self, amount):
        print(f&quot;카드로 {amount}원 결제합니다&quot;)

# 현금 결제
class CashPayment(Payment):
    def pay(self, amount):
        print(f&quot;현금으로 {amount}원 결제합니다&quot;)

# 카카오페이
class KakaoPay(Payment):
    def pay(self, amount):
        print(f&quot;카카오페이로 {amount}원 결제합니다&quot;)

# 공통 처리 함수
def process_payment(payment: Payment, amount):
    payment.pay(amount)
    
# 사용
card = CardPayment()
cash = CashPayment()
kakao = KakaoPay()

process_payment(card, 10000)
process_payment(cash, 5000)
process_payment(kakao, 2000)

# 카드로 10000원 결제합니다
# 현금으로 5000원 결제합니다
# 카카오페이로 2000원 결제합니다&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;  추후에 결제수단을 추가하기 위해서는 @abstractmethod로 강제되어있는 메서드를 무조건 오버라이딩 해야한다. 그렇지않으면 에러가 나서 프로그램이 실행되지 않도록 막는다.&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;</description>
      <category>Python</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/18</guid>
      <comments>https://gudwns5533.tistory.com/18#entry18comment</comments>
      <pubDate>Thu, 28 May 2026 23:39:31 +0900</pubDate>
    </item>
    <item>
      <title>객체지향 프로그래밍</title>
      <link>https://gudwns5533.tistory.com/17</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;프로그래밍 방법론&lt;/b&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;/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;&lt;b&gt;1. 절차적 프로그래밍 (Procedural Programming)&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;절차(Procedure)를 중시하는 방식으로, 작업을 순서대로 실행하도록 프로그램을 구성한다. 코드가 명령문으로 이루어져 있고, 작업 순서가 중요하다. 변수와 함수가 함께 사용되며, 함수를 통해 프로그램의 기능을 분리하고, 코드 중복을 줄인다.&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;&lt;b&gt;2. 객체지향 프로그래밍 (OOP, Object-Oriented Programming)&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;객체(Object)라는 개념을 사용해 프로그램을 구성한다. 객체는 데이터(속성)와 함수(메서드)를 하나의 단위로 묶은 것을 말한다. 클래스라는 틀을 이용해 객체를 정의하고, 이를 통해 여러 개의 객체를 생성할 수 있다. 캡슐화(Encapsulation), 상속(Inheritance), 다형성(Polymorphism), 추상화 (Abstractioni) 등의 특징을 활용해 코드를 재사용하고 확장할 수 있다.&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;&lt;b&gt;3. 함수형 프로그래밍 (Functional Programming)&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;함수를 중심으로 프로그램을 구성한다. 수학적인 함수 개념에 기초하고 있으며 코드가 간결하고 직관적이다. 예를 들어 map, filter, reduce 등의 함수나 재귀를 주로 사용하여 데이터 처리를 간결하게 수행한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;클래스&lt;/b&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;클래스(class)는 객체지향 프로그래밍에서 데이터(속성)와 기능(메서드)을 하나로 묶어 객체를 만들기 위한 설계도(템플릿)이다. 클래스를 정의하면 그 구조를 기반으로 여러 개의 객체(인스턴스)를 생성할 수 있으며, 각 객체는 동일한 구조를 가지면서도 서로 다른 값을 가질 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스(class) : 객체를 만들기 위한 설계도 또는 틀&lt;/li&gt;
&lt;li&gt;객체(object) / 인스턴스(instance) : 설계도를 바탕으로 메모리에 실제로 구현된 실체&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;객체(인스턴스) 생성&lt;/b&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;/p&gt;
&lt;pre id=&quot;code_1779957554312&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Fruit:
    pass         # 내용이 없는 블록을 만들 때 사용
    
Fruit()          # 객체로서 생성
# &amp;lt;__main__.Fruit at 0x2abbc5e0d70&amp;gt;

apple = Fruit()
print(apple)
print(type(apple))
print(id(apple))

# &amp;lt;__main__.Fruit object at 0x000002ABBC644B30&amp;gt;
# &amp;lt;class '__main__.Fruit'&amp;gt;
# 2936623352624&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;size18&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;객체(object)와 인스턴스(instance)는 거의 같은 의미로 사용되지만, 관점에 따라 구분된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체는 메모리에 생성된 모든 실체를 의미하는 포괄적인 개념으로, 파이썬에서 리스트, 함수, 클래스 등 모든 값이 객체.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인스턴스는 특정 클래스에 의해 생성된 객체를 가리키는 관계 중심의 용어로, &quot;어떤 클래스의 인스턴스이다&quot;라고 표현.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 모든 인스턴스는 객체이지만, 모든 객체가 특정 클래스의 인스턴스라고 강조해서 부르는 것은 아니며, 인스턴스라는 용어는 주로 클래스와의 관계를 설명할 때 사용된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;생성자&lt;/b&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;파이썬에서 생성자(Constructor)는 클래스의 인스턴스가 생성될 때 자동으로 호출되는 특별한 메서드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자는 객체의 초기화를 담당하며, 객체가 생성될 때 필요한 속성을 초기화하고 설정하는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서 생성자 메서드는 __init__이라고 이름이 정해져 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;속성을 self를 사용하여 접근하고 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 초기화된 속성은 객체가 생성될 때 마다 다른 값을 가질 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1779957808928&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class 클래스이름:
    # 생성자
    def __init__(self, 매개변수1, 매개변수2):
        self.속성1 = 매개변수1
        self.속성2 = 매개변수2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;인스턴스 변수는 각 인스턴스(객체)마다 별도로 생성되는 변수로, 객체를 만들 때마다 self 키워드를 통해 생성자(init 메서드)에서 정의되며, 각 객체가 고유한 값을 따로 저장한다. 인스턴스 변수는 인스턴스명으로만 접근 가능하며, 다른 인스턴스에 영향을 주지 않고 자신만의 데이터를 가질 수 있어 객체마다 다른 속성값을 저장할 때 사용된다.&lt;/p&gt;
&lt;pre id=&quot;code_1779968030272&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Fruit:
    def __init__(self):
        self.name = ''
        self.quantity = 0
        
        # name, quantity = 인스턴스 변수

apple = Fruit()
print(apple.name)
print(apple.quantity)

banana = Fruit()
print(banana)
print(banana.name)
print(banana.quantity)&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;메서드&lt;/b&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;메서드(method)는 클래스 내부에 정의된 함수로, 객체(인스턴스)나 클래스와 관련된 동작(행동)을 수행하는 기능이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서는 메서드의 종류에 따라 self를 사용하는 인스턴스 메서드, cls를 사용하는 클래스 메서드(@classmethod), 그리고 별도의 참조 없이 독립적으로 동작하는 정적 메서드(@staticmethod)로 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드는 단순한 함수와 달리 특정 객체나 클래스와 연결되어 동작하며, 객체의 상태(속성)를 읽거나 변경하는 역할을 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1779968650589&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Fruit:
    def __init__(self, name, quantity):
        self.name = name
        self.quantity = quantity

    # 인스턴스 메서드
    def print_info(self):
        print(f&quot;과일 이름: {self.name}&quot;)
        print(f&quot;수량: {self.quantity}&quot;)

    # 인스턴스 메서드
    def add_quantity(self, amount):
        self.quantity += amount
        print(f&quot;{amount}개 추가되었습니다.&quot;)
        
apple = Fruit(&quot;사과&quot;, 10)
apple.print_info()
apple.add_quantity(5)
apple.print_info()

# 과일 이름: 사과
# 수량: 10
# 5개 추가되었습니다.
# 과일 이름: 사과
# 수량: 15

banana = Fruit('바나나', 3)
banana.print_info()
banana.add_quantity(2)
banana.print_info()           # 메소드는 공유하지만 메소드의 self때문에 각 객체마다의 값은 다르게 관리된다.

# 과일 이름: 바나나
# 수량: 3
# 2개 추가되었습니다.
# 과일 이름: 바나나
# 수량: 5&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1779968691146&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Order:
    tax_rate = 0.1  # 클래스 변수 (세율: 모든 주문 공통)    # 공용으로 쓰는 변수

    def __init__(self, price, quantity):
        self.price = price        # 상품 가격
        self.quantity = quantity  # 수량                  # 생성자 안의 변수 : 인스턴스 변수

    # 인스턴스 메서드
    def total_price(self):
        return self.price * self.quantity

    # 클래스 메서드 -&amp;gt; 객체를 따로 생성하지않고 클래스 이름으로 바로 호출할 수 있다.
    @classmethod   # 데코레이터
    def set_tax_rate(cls, rate):              # cls : 클래스의 주소가 들어있음 &amp;gt; 클래스 변수에 접근할 수 있음
        cls.tax_rate = rate

    # 정적 메서드 -&amp;gt; 클래스 이름으로 접근은 가능하지만, 클래스 변수나 self 모든 걸 사용할 수 없음. 독립적으로 사용되는 함수를 만들 때 사용
    @staticmethod
    def calculate_discount(price, discount_rate):
        return price * (1 - discount_rate)
        
# 인스턴스 생성
order1 = Order(10000, 2)  # 10000원 상품 2개

# 인스턴스 메서드 호출
print(&quot;총 가격:&quot;, order1.total_price())  # 20000

# 정적 메서드 사용 (할인 계산)
discounted = Order.calculate_discount(order1.total_price(), 0.2)
print(&quot;할인 적용 가격:&quot;, discounted)  # 16000

# 클래스 메서드 사용 (세율 변경)
Order.set_tax_rate(0.2)
print(&quot;변경된 세율:&quot;, Order.tax_rate)  # 0.2

# 총 가격: 20000
# 할인 적용 가격: 16000.0
# 변경된 세율: 0.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;데코레이터(decorator)는 기존 함수나 메서드를 수정하지 않고, 그 기능을 감싸서(포장해서) 추가 동작을 붙여주는 기능으로, 실행 전후에 로그를 출력하거나 권한 체크, 시간 측정 등의 공통 기능을 쉽게 재사용할 수 있게 해준다. 내부적으로는 함수를 인자로 받아 새로운 함수를 반환하는 구조로 동작하며, @데코레이터이름 형태로 사용하면 해당 함수가 자동으로 데코레이터에 전달되어 기능이 확장된다. 또한 데코레이터는 클로저의 형태로 만들어져 있다.&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;&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;/p&gt;
&lt;pre id=&quot;code_1779969228394&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Fruit:
    origin = &quot;한국&quot;  # 클래스 변수 (공통 속성)

    def __init__(self, name, quantity):
        # 인스턴스 변수 (각 객체마다 다름)
        self.name = name
        self.quantity = quantity

    def print_info(self):
        print(f'원산지: {Fruit.origin}')   # 클래스 변수
        print(f'이름: {self.name}')        # 인스턴스 변수
        print(f'수량: {self.quantity}')    # 인스턴스 변수
        
apple = Fruit('사과', 10)
banana = Fruit('바나나', 5)

apple.print_info()
banana.print_info()

# 원산지: 한국
# 이름: 사과
# 수량: 10
# 원산지: 한국
# 이름: 바나나
# 수량: 5&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1779969311778&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Fruit.origin = '필리핀'   # 클래스 변수 변경

apple.print_info()
banana.print_info()

# 원산지: 필리핀           # 한국 &amp;gt; 필리핀으로 모두 변경된걸 알 수 있다 
# 이름: 사과
# 수량: 10
# 원산지: 필리핀
# 이름: 바나나
# 수량: 5&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Python</category>
      <author>gudwns5533</author>
      <guid isPermaLink="true">https://gudwns5533.tistory.com/17</guid>
      <comments>https://gudwns5533.tistory.com/17#entry17comment</comments>
      <pubDate>Thu, 28 May 2026 20:56:19 +0900</pubDate>
    </item>
  </channel>
</rss>