<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Opal1031</title>
    <link>https://opal1031.tistory.com/</link>
    <description>PKNU CE 22</description>
    <language>ko</language>
    <pubDate>Thu, 14 May 2026 04:07:05 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Opal1031</managingEditor>
    <image>
      <title>Opal1031</title>
      <url>https://tistory1.daumcdn.net/tistory/8641095/attach/cecf9b6f3b01491b9c5ecc20dd50a30b</url>
      <link>https://opal1031.tistory.com</link>
    </image>
    <item>
      <title>[GDGoC PKNU] 2026.05 Tech Talk</title>
      <link>https://opal1031.tistory.com/107</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;2026.05.08 테크톡 발표자료입니다.&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;GDGoC PKNU 8기 파이썬 스터디 중간 점검 발표입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/t83Ph/dJMcagk9Llh/wnHH44AnNXiPs7JfXvKKM1/Tech%20Talk.pdf?attach=1&amp;amp;knm=tfile.pdf&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;Tech Talk.pdf&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;2.75MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>GDGoC/etc</category>
      <category>GDGoC PKNU</category>
      <author>Opal1031</author>
      <guid isPermaLink="true">https://opal1031.tistory.com/107</guid>
      <comments>https://opal1031.tistory.com/107#entry107comment</comments>
      <pubDate>Mon, 11 May 2026 22:53:02 +0900</pubDate>
    </item>
    <item>
      <title>[Python] Algorithm #04: 완전탐색과 백트래킹</title>
      <link>https://opal1031.tistory.com/106</link>
      <description>&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;완전탐색&lt;/b&gt;은 가능한 경우를 모두 확인하는 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;백트래킹&lt;/b&gt;은 그 중 의미 없는 경우를 중간에 잘라내며 탐색 효율을 높이는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩테스트에서는 둘을 따로 쓰기도 하고, 함께 섞어서 쓰는 경우도 많습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 완전탐색이란&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완전탐색(Brute Force)&lt;/b&gt;은 가능한 모든 경우를 빠짐없이 확인하는 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 빠르게 푸는 핵심은 &quot;모든 경우를 직접 만들어볼 수 있는가&quot;를 판단하는 것입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;경우의 수가 작으면 가장 확실한 방법입니다.&lt;/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;예를 들어 숫자 3개를 골라 합을 구하거나, 문자열의 모든 부분집합을 확인하는 문제는 완전탐색으로 접근할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;nums = [1, 2, 3]

for i in range(len(nums)):
    for j in range(len(nums)):
        if (i != j):
            print(nums[i], nums[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;size16&quot;&gt;&amp;nbsp;&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;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 완전탐색의 대표 방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완전탐색은 문제에 따라 여러 형태로 나타납니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;반복문 중첩&lt;/b&gt;: 작은 범위의 경우를 직접 모두 확인할 때 사용합니다.&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;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;from itertools import permutations, combinations

nums = [1, 2, 3]

print(list(permutations(nums, 2)))
print(list(combinations(nums, 2)))&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;파이썬에서는 itertools를 활용하면 순열과 조합을 간단하게 만들 수 있습니다.&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;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 백트래킹이란&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;백트래킹(Backtracking)&lt;/b&gt;은 탐색 도중 더 볼 필요가 없는 경우를 만나면 그 경로를 중단하고 되돌아가는 기법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &quot;아무거나 다 해보는 완전탐색&quot;에 &quot;불필요한 가지치기&quot;를 더한 방식이라고 볼 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 선택이 조건을 위반하면 더 깊게 들어가지 않습니다.&lt;/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;백트래킹이 필요한 대표 문제는 순열 생성, N과 M 시리즈, N-Queen, 스도쿠 등이 있습니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;def dfs(nums, path, used):
	# 경우의 수를 모두 모았으면 출력
	if (len(path) == 2):
		print(path)
		return

	# 아직 사용하지 않은 숫자를 선택
	for num in nums:
		if (num in used):
			continue

		# 선택
		path.append(num)
		used.add(num)

		# 다음 선택으로 재귀
		dfs(nums, path, used)

		# 선택 취소 (백트래킹)
		path.pop()
		used.remove(num)

nums = [1, 2, 3]
dfs(nums, [], set())&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 순열과 조합 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백트래킹은 순열과 조합을 직접 만드는 데 자주 쓰입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예시는 모두 재귀를 기반으로 백트래킹을 적용한 구현입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 선택 후 자신을 다시 호출하고, 원상복구하는 방식으로 모든 경우를 탐색합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순열은 순서가 중요하고, 조합은 순서가 중요하지 않습니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;def permutations_backtracking(nums, path, used):
	# 모든 원소를 선택했으면 순열 완성
	if (len(path) == len(nums)):
		print(path)
		return

	# 아직 사용하지 않은 각 원소를 시도
	for num in nums:
		if (used[num]):
			continue

		# 현재 원소 선택
		used[num] = True
		path.append(num)

		# 다음 위치에 올 원소 선택
		permutations_backtracking(nums, path, used)

		# 선택 취소
		path.pop()
		used[num] = False

nums = [1, 2, 3]
used = {1: False, 2: False, 3: False}

permutations_backtracking(nums, [], used)&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 class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;def combinations_backtracking(nums, start, path):
	# 2개를 선택했으면 조합 완성
	if (len(path) == 2):
		print(path)
		return

	# start 이후의 원소만 선택 (중복 제거)
	for i in range(start, len(nums)):
		path.append(nums[i])
        
		# 현재 위치 다음부터 선택하도록 (순서 고정)
		combinations_backtracking(nums, i + 1, path)
		path.pop()

nums = [1, 2, 3]
combinations_backtracking(nums, 0, [])&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 백트래킹의 핵심: 가지치기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백트래킹에서 가장 중요한 건 가지치기입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 상태가 이미 조건을 만족할 수 없다고 판단되면, 더 내려가지 않고 바로 종료합니다.&lt;/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 class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;def dfs(total, idx, nums, target):
	# 가지치기: 합이 목표를 넘으면 더 볼 필요 없음
	if (total &amp;gt; target):
		return

	# 모든 원소를 확인했으면 합이 목표와 같은지 확인
	if (idx == len(nums)):
		if (total == target):
			print(&quot;가능한 경우&quot;)
            
		return

	# 현재 원소를 선택하는 경우
	dfs(total + nums[idx], idx + 1, nums, target)
    
	# 현재 원소를 선택하지 않는 경우
	dfs(total, idx + 1, nums, target)

nums = [1, 3, 5]
dfs(0, 0, nums, 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;size16&quot;&gt;가지치기를 잘하면 완전탐색보다 훨씬 적은 경우만 확인할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 자주 하는 실수/주의점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull; 재귀 호출 후 원상복구를 빼먹으면 다음 탐색 결과가 틀어질 수 있습니다.&lt;br /&gt;&amp;bull; 방문 여부를 관리할 때는 선택과 해제를 짝으로 맞춰야 합니다.&lt;br /&gt;&amp;bull; 중복을 허용하지 않는 문제에서는 같은 경우를 여러 번 세지 않도록 주의해야 합니다.&lt;br /&gt;&amp;bull; 가지치기를 너무 늦게 하면 백트래킹의 의미가 줄어듭니다.&lt;br /&gt;&amp;bull; 경우의 수가 너무 큰 문제는 완전탐색으로는 해결이 어렵습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. 종합 예제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 1부터 4까지의 숫자에서 2개를 골라 합이 5가 되는 경우를 찾는 예제입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완전탐색 방식&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;def find_cases(nums, start, path, target):
	# 2개를 선택했으면 합 확인
	if (len(path) == 2):
		if (sum(path) == target):
			print(path)
            
		return

	# start 이후의 원소만 선택하는 조합
	for i in range(start, len(nums)):
		path.append(nums[i])
		find_cases(nums, i + 1, path, target)
		path.pop()

nums = [1, 2, 3, 4]
find_cases(nums, 0, [], 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;size16&quot;&gt;&lt;b&gt;백트래킹 방식&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778500586098&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def find_cases_backtracking(nums, start, path, target):
	# 가지치기: 합이 목표를 이미 넘었으면 이후 더할 수 있는 건 양수뿐이므로 의미 없음
	if (sum(path) &amp;gt; target):
		return

	# 2개를 선택했으면 합 확인
	if (len(path) == 2):
		if (sum(path) == target):
			print(path)
            
		return

	# start 이후의 원소만 선택하는 조합
	for i in range(start, len(nums)):
		path.append(nums[i])
		find_cases_backtracking(nums, i + 1, path, target)
		path.pop()

nums = [1, 2, 3, 4]
find_cases_backtracking(nums, 0, [], 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;size16&quot;&gt;이 문제는 완전탐색으로 모든 조합을 확인하고, 조건에 맞는 경우만 출력하는 방식입니다.&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;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8. 종합 요약&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull; &lt;b&gt;완전탐색&lt;/b&gt;: 가능한 모든 경우를 확인하는 방법&lt;br /&gt;&amp;bull; &lt;b&gt;백트래킹&lt;/b&gt;: 불필요한 경우를 중간에 잘라내는 완전탐색&lt;br /&gt;&amp;bull; 재귀와 함께 자주 사용됨&lt;br /&gt;&amp;bull; 순열, 조합, 부분집합 문제에 특히 유용함&lt;br /&gt;&amp;bull; 가지치기를 잘하면 탐색 효율을 크게 높일 수 있음&lt;/p&gt;</description>
      <category>GDGoC/Python</category>
      <category>Python</category>
      <author>Opal1031</author>
      <guid isPermaLink="true">https://opal1031.tistory.com/106</guid>
      <comments>https://opal1031.tistory.com/106#entry106comment</comments>
      <pubDate>Mon, 11 May 2026 21:02:36 +0900</pubDate>
    </item>
    <item>
      <title>[Python] Basic #04: 딕셔너리 (Dictionary)</title>
      <link>https://opal1031.tistory.com/105</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Python에서 핵심 자료구조 중 하나인 딕셔너리에 대해 다룹니다.&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 딕셔너리 (Dictionary)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리는 데이터를 &lt;b&gt;Key-Value&lt;/b&gt;쌍으로 저장하며, 여러 값들을 효과적으로 관리할 수 있는 자료구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Key&lt;/b&gt;&lt;br /&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;Value&lt;/b&gt;&lt;br /&gt;Key와 연결된 데이터&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;리스트 (List) vs 딕셔너리 (Dictionary)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1861&quot; data-origin-height=&quot;797&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DETS4/dJMcagyGiJN/Sk0mZNaQrCXkDKPtFl1m21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DETS4/dJMcagyGiJN/Sk0mZNaQrCXkDKPtFl1m21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DETS4/dJMcagyGiJN/Sk0mZNaQrCXkDKPtFl1m21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDETS4%2FdJMcagyGiJN%2FSk0mZNaQrCXkDKPtFl1m21%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;760&quot; height=&quot;325&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1861&quot; data-origin-height=&quot;797&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;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# 딕셔너리 생성
info = {&quot;name&quot;: &quot;Alice&quot;, &quot;age&quot;: 25}

# 값 접근
print(info[&quot;name&quot;])  # 출력: Alice

# 값 수정
info[&quot;age&quot;] = 26
print(info[&quot;age&quot;])  # 출력: 26&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 딕셔너리 주요 메서드 및 기능&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리가 기본적으로 제공하는 몇 가지 유용한 메서드를 알아봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;.keys(), .values(), .items()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리에 포함되는 값들에 대한 직접적인 접근을 하는 메서드입니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;info = {&quot;name&quot;: &quot;Alice&quot;, &quot;age&quot;: 25}

print(info.keys())   # dict_keys(['name', 'age'])
print(info.values()) # dict_values(['Alice', 25])
print(info.items())  # dict_items([('name', 'Alice'), ('age', 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;size16&quot;&gt;&lt;b&gt;.get()을 사용한 안전한 키 접근&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;size16&quot;&gt;이를 통해 KeyError 예외를 방지하고, 안전하게 기본값(default)을 반환받아 프로그램의 안정성과 보안성을 높일 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;print(info.get(&quot;city&quot;, &quot;Unknown&quot;))  # 출력: Unknown&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;찾고자 하는 key(city)가 있으면 값을 출력하고, 없으면 기본값(Unknown)을 출력합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;항목 추가/수정/삭제&lt;/b&gt;&lt;br /&gt;각 값에 대한 데이터 관리를 키를 이용하여 처리할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;info[&amp;ldquo;key&amp;rdquo;] = value        # 값 수정
del info[&amp;ldquo;key&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;&lt;b&gt;in 키워드를 이용한 키 확인&lt;/b&gt;&lt;br /&gt;특정 키가 딕셔너리에 존재하는지 확인할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;if &quot;name&quot; in info:
    print(&quot;Key exists!&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 딕셔너리 활용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;딕셔너리 컴프리헨션 (Dictionary Comprehension)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리를 빠르고 간결하게 생성할 수 있는 파이썬 만의 문법입니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;# 리스트 데이터를 딕셔너리로 변환

students = [{&quot;name&quot;: &quot;Alice&quot;, &quot;score&quot;: 85}, {&quot;name&quot;: &quot;Bob&quot;, &quot;score&quot;: 90}]
scores = {s[&quot;name&quot;]: s[&quot;score&quot;] for s in students}

print(scores)  # 출력: {'Alice': 85, 'Bob': 90}&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;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;: 키(key)를 알면 데이터의 양과 상관없이 즉시 값을 찾음&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 분류와 집계&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;perl&quot;&gt;&lt;code&gt;# 문자열에서 각 알파벳의 빈도수 세기

text = &quot;hello world&quot;
freq = {}

for char in text:
    if char in freq:
        freq[char] += 1

    else:
        freq[char] = 1

print(freq)  # 출력: {'h': 1, 'e': 1, 'l': 3, 'o': 2, ...}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 딕셔너리 활용 패턴&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문자열에서 각 문자 개수 세기&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;text = &quot;hello world&quot;
freq = {}

for char in text:
    freq[char] = freq.get(char, 0) + 1

print(freq) # 출력: {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 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;size16&quot;&gt;&lt;b&gt;값 기준 정렬하기&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;scores = {&quot;Alice&quot;: 85, &quot;Bob&quot;: 90, &quot;Charlie&quot;: 80}

# 딕셔너리를 값 기준으로 내림차순 정렬
sorted_scores = dict(sorted(scores.items(), key = lambda x: x[1], reverse = True))

print(sorted_scores)
# 출력: {'Bob': 90, 'Alice': 85, 'Charlie': 80}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;두 배열의 공통 요소 찾기&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;# 배열 두 개
arr1 = [1, 2, 3, 4, 5]
arr2 = [3, 4, 5, 6, 7]

# 딕셔너리로 배열 1의 모든 값을 저장
map_dict = {x: True for x in arr1}

# 배열 2를 순회하며 공통 요소 찾아내기
common_elements = [x for x in arr2 if x in map_dict]

print(common_elements)
# 출력: [3, 4, 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;size16&quot;&gt;&lt;b&gt;Two Sum&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;합이 target이 되는 두 숫자의 인덱스를 찾는 전형적인 코딩 테스트 문제입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;내가 필요한 숫자(diff)가 이미 나온 적 있는지 확인하는 동시에 현재 숫자와 그 위치를 기록하는 방식입니다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;nums = [2, 7, 11, 15]
target = 9

num_dict = {}

for i, num in enumerate(nums):
    diff = target - num

    if diff in num_dict:  # 이전에 봤던 숫자가 현재 값의 차이(diff)라면 인덱스 반환
        print([num_dict[diff], i])

        break

    num_dict[num] = i

# 출력: [0, 1]&lt;/code&gt;&lt;/pre&gt;</description>
      <category>GDGoC/Python</category>
      <category>Python</category>
      <author>Opal1031</author>
      <guid isPermaLink="true">https://opal1031.tistory.com/105</guid>
      <comments>https://opal1031.tistory.com/105#entry105comment</comments>
      <pubDate>Mon, 11 May 2026 17:47:13 +0900</pubDate>
    </item>
    <item>
      <title>[Python] Algorithm #03: 이분 탐색 (Binary Search)</title>
      <link>https://opal1031.tistory.com/102</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 정렬을 간단히 짚고, 이분 탐색을 중심으로 다룹니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬은 이분 탐색을 위한 전제 조건이 되는 경우가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 정렬 자체는 짧게 보고, 이분 탐색의 원리와 활용에 더 집중해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬에 대한 세부적인 내용은 기초 트랙을 참고하시면 되겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://opal1031.tistory.com/101&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Python] Basic #03: 정렬&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1778056958373&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Python] Basic #03: 정렬&quot; data-og-description=&quot;Python에서 정렬을 다루는 방법을 다룹니다. 리스트를 정렬할 때 자주 사용하는 sort()와 sorted(), 정렬 기준을 바꾸는 key, 간단한 함수를 만드는 lambda, 그리고 역순 정렬까지 함께 살펴보겠습니다.1. &quot; data-og-host=&quot;opal1031.tistory.com&quot; data-og-source-url=&quot;https://opal1031.tistory.com/101&quot; data-og-url=&quot;https://opal1031.tistory.com/101&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/biwBPo/dJMb85WXk5M/wfL5U8lTZnkAILdk8Ek0K0/img.jpg?width=800&amp;amp;height=499&amp;amp;face=0_0_800_499,https://scrap.kakaocdn.net/dn/FJVEP/dJMb84qcDhL/0m8M538AKi1vBNu8AJV0Fk/img.jpg?width=800&amp;amp;height=499&amp;amp;face=0_0_800_499,https://scrap.kakaocdn.net/dn/mXNuA/dJMb81G1lmS/Uz6xWYDaMkk3zJdx6gAVgK/img.jpg?width=878&amp;amp;height=878&amp;amp;face=371_205_497_342&quot;&gt;&lt;a href=&quot;https://opal1031.tistory.com/101&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://opal1031.tistory.com/101&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/biwBPo/dJMb85WXk5M/wfL5U8lTZnkAILdk8Ek0K0/img.jpg?width=800&amp;amp;height=499&amp;amp;face=0_0_800_499,https://scrap.kakaocdn.net/dn/FJVEP/dJMb84qcDhL/0m8M538AKi1vBNu8AJV0Fk/img.jpg?width=800&amp;amp;height=499&amp;amp;face=0_0_800_499,https://scrap.kakaocdn.net/dn/mXNuA/dJMb81G1lmS/Uz6xWYDaMkk3zJdx6gAVgK/img.jpg?width=878&amp;amp;height=878&amp;amp;face=371_205_497_342');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Python] Basic #03: 정렬&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Python에서 정렬을 다루는 방법을 다룹니다. 리스트를 정렬할 때 자주 사용하는 sort()와 sorted(), 정렬 기준을 바꾸는 key, 간단한 함수를 만드는 lambda, 그리고 역순 정렬까지 함께 살펴보겠습니다.1.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;opal1031.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 정렬은 왜 필요한가&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 class=&quot;angelscript&quot;&gt;&lt;code&gt;nums = [5, 2, 9, 1, 3]

nums.sort()
print(nums)  # [1, 2, 3, 5, 9]

result = sorted(nums)
print(result)  # [1, 2, 3, 5, 9]&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;sort()는 원본 리스트를 바꾸고, sorted()는 새로운 리스트를 반환합니다.&lt;/li&gt;
&lt;/ul&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;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 이분 탐색의 기본 원리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 탐색(Binary Search)은 정렬된 배열에서 원하는 값을 빠르게 찾는 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가운데 값을 확인한 뒤, 찾는 값이 더 작으면 왼쪽, 더 크면 오른쪽만 남기고 탐색 범위를 절반씩 줄입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열이 정렬 되어있기에 크기 비교를 통해 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간복잡도는 O(log n)입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매번 탐색 범위가 절반으로 줄어들기 때문에 데이터가 커질수록 효율이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;nums = [1, 2, 3, 5, 9]
target = 5

left = 0
right = len(nums) - 1
found = False

while (left &amp;lt;= right):
    mid = (left + right) // 2

    if (nums[mid] == target):
        found = True
        break
        
    elif (nums[mid] &amp;lt; target):
        left = mid + 1
        
    else:
        right = mid - 1

print(found)  # True&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 직접 구현하는 이분 탐색&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 class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;def binary_search(arr, target):
    left = 0
    right = len(arr) - 1

    while (left &amp;lt;= right):
        mid = (left + right) // 2

        if (arr[mid] == target):
            return mid
            
        elif (arr[mid] &amp;lt; target):
            left = mid + 1
            
        else:
            right = mid - 1

    return -1

nums = [1, 2, 3, 5, 9]

print(binary_search(nums, 3))  # 2
print(binary_search(nums, 4))  # -1&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;반환값을 True/False로 둘 수도 있고, 위치 인덱스로 둘 수도 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. bisect 모듈 활용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python에서는 &lt;code&gt;bisect&lt;/code&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;code&gt;bisect_left()&lt;/code&gt;는 삽입될 왼쪽 위치를, &lt;code&gt;bisect_right()&lt;/code&gt;는 오른쪽 위치를 반환합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;import bisect

nums = [1, 2, 2, 2, 3, 5]

left = bisect.bisect_left(nums, 2)
right = bisect.bisect_right(nums, 2)

print(left)   # 1
print(right)  # 4&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;bisect_left()와 bisect_right()는 같은 값이 여러 개 있을 때 유용합니다.&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;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 이분 탐색 응용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 탐색은 단순히 값을 찾는 것뿐 아니라, 조건을 만족하는 최소값이나 최대값을 찾는 데도 자주 쓰입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &amp;ldquo;이 값 이상이 처음 나오는 위치&amp;rdquo;를 찾는 문제는 정렬과 이분 탐색을 함께 활용하면 쉽게 풀 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import bisect

scores = [10, 20, 20, 35, 40, 50]
target = 20

idx = bisect.bisect_left(scores, target)
print(idx)  # 1

if (idx &amp;lt; len(scores)):
    print(scores[idx])  # 20&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;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 자주 하는 실수/주의점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull; 이분 탐색은 정렬된 데이터에서만 사용할 수 있습니다.&lt;br /&gt;&amp;bull; 중간값은 보통 &lt;code&gt;(left + right) // 2&lt;/code&gt;로 구합니다.&lt;br /&gt;&amp;bull; &lt;code&gt;left&lt;/code&gt;, &lt;code&gt;right&lt;/code&gt; 갱신을 잘못하면 무한 루프가 생길 수 있습니다.&lt;br /&gt;&amp;bull; &lt;code&gt;bisect_left()&lt;/code&gt;와 &lt;code&gt;bisect_right()&lt;/code&gt;의 차이를 구분해야 합니다.&lt;br /&gt;&amp;bull; 중복 값이 있을 때는 &amp;ldquo;찾는다&amp;rdquo;보다 &amp;ldquo;어디에 들어가는가&amp;rdquo;를 기준으로 생각하면 편합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. 종합 예제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예시는 학생 점수 목록에서 특정 점수가 처음 등장하는 위치를 찾는 예제입니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import bisect

scores = [45, 50, 50, 60, 70, 80, 80, 90]
target = 80

# target이 들어갈 가장 왼쪽 위치
idx = bisect.bisect_left(scores, target)

if (idx &amp;lt; len(scores) and scores[idx] == target):
    print(idx)  # 5
    
else:
    print(-1)&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;직접 구현한 이분 탐색과 bisect의 역할을 모두 알고 있으면, 문제에 따라 더 빠르게 선택할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8. 종합 요약&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull; 정렬: 이분 탐색의 전제 조건&lt;br /&gt;&amp;bull; 이분 탐색: 정렬된 데이터에서 범위를 절반씩 줄이며 탐색&lt;br /&gt;&amp;bull; 시간복잡도: O(log n)&lt;br /&gt;&amp;bull; &lt;code&gt;bisect_left()&lt;/code&gt;: 왼쪽 삽입 위치&lt;br /&gt;&amp;bull; &lt;code&gt;bisect_right()&lt;/code&gt;: 오른쪽 삽입 위치&lt;br /&gt;&amp;bull; 반복문 구현: 코딩테스트에서 자주 사용&lt;/p&gt;</description>
      <category>GDGoC/Python</category>
      <category>Python</category>
      <author>Opal1031</author>
      <guid isPermaLink="true">https://opal1031.tistory.com/102</guid>
      <comments>https://opal1031.tistory.com/102#entry102comment</comments>
      <pubDate>Wed, 6 May 2026 18:12:09 +0900</pubDate>
    </item>
    <item>
      <title>[Python] Basic #03: 정렬</title>
      <link>https://opal1031.tistory.com/101</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Python에서 정렬을 다루는 방법을 다룹니다.&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;code&gt;sort()&lt;/code&gt;와 &lt;code&gt;sorted()&lt;/code&gt;, 정렬 기준을 바꾸는 &lt;code&gt;key&lt;/code&gt;, 간단한 함수를 만드는 &lt;code&gt;lambda&lt;/code&gt;, 그리고 역순 정렬까지 함께 살펴보겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. sort()와 sorted()&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;sort()&lt;/code&gt;와 &lt;code&gt;sorted()&lt;/code&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;sort()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트 자체를 직접 정렬&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;nums = [5, 2, 9, 1, 3]

nums.sort()

print(nums)  # [1, 2, 3, 5, 9]&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;list형의 method 중 하나이므로 list.sort()의 형태로 작성한다.&lt;/li&gt;
&lt;/ul&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;b&gt;sorted()&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;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;nums = [5, 2, 9, 1, 3]

result = sorted(nums)

print(result)  # [1, 2, 3, 5, 9]
print(nums)    # [5, 2, 9, 1, 3]&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;내장 함수이므로, sorted(list)의 형태로 작성한다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;sort()&lt;/code&gt;는 원본을 바꾸기 때문에 이후에도 같은 리스트를 계속 사용할 계획이라면 주의해야 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. key 활용&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;code&gt;key&lt;/code&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;code&gt;key&lt;/code&gt;에는 정렬 기준이 되는 함수를 넣습니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;words = [&quot;banana&quot;, &quot;apple&quot;, &quot;kiwi&quot;, &quot;pear&quot;]

result = sorted(words, key = len)

print(result)  # ['kiwi', 'pear', 'apple', 'banana']&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;students = [
    (&quot;Alice&quot;, 90),
    (&quot;Bob&quot;, 75),
    (&quot;Charlie&quot;, 85)
]

result = sorted(students, key = lambda x: x[1])

print(result)  # [('Bob', 75), ('Charlie', 85), ('Alice', 90)]&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;code&gt;key&lt;/code&gt;를 사용하면 문자열 길이, 숫자, 튜플의 특정 위치처럼 다양한 기준으로 정렬할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. lambda 함수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;lambda&lt;/code&gt;는 짧은 함수를 한 줄로 작성할 때 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬 기준이 간단할 때 &lt;code&gt;lambda&lt;/code&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 class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;nums = [1, -4, 3, -2, 5]

result = sorted(nums, key = lambda x: abs(x))

print(result)  # [1, -2, 3, -4, 5]&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;points = [(3, 4), (1, 2), (5, 1)]

result = sorted(points, key = lambda x: x[0] + x[1])

print(result)  # [(1, 2), (3, 4), (5, 1)]&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&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;reverse = True&lt;/code&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 class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;nums = [5, 2, 9, 1, 3]

result = sorted(nums, reverse = True)

print(result)  # [9, 5, 3, 2, 1]&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;words = [&quot;banana&quot;, &quot;apple&quot;, &quot;kiwi&quot;, &quot;pear&quot;]

result = sorted(words, key = len, reverse = True)

print(result)  # ['banana', 'apple', 'pear', 'kiwi']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;reverse = True&lt;/code&gt;는 &lt;code&gt;key&lt;/code&gt;와 함께 자주 사용됩니다. 정렬 기준은 유지한 채 순서만 반대로 바꿀 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 종합 예제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예시는 학생 정보를 이름, 점수 기준으로 정리하는 간단한 예제입니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;students = [
    {&quot;name&quot;: &quot;Alice&quot;, &quot;score&quot;: 90},
    {&quot;name&quot;: &quot;Bob&quot;, &quot;score&quot;: 75},
    {&quot;name&quot;: &quot;Charlie&quot;, &quot;score&quot;: 85},
    {&quot;name&quot;: &quot;Dave&quot;, &quot;score&quot;: 90}
]

# 점수 오름차순 정렬
by_score = sorted(students, key = lambda x: x[&quot;score&quot;])
print(by_score)

# 점수가 같으면 이름 순으로 정렬
by_score_name = sorted(students, key = lambda x: (x[&quot;score&quot;], x[&quot;name&quot;]))
print(by_score_name)

# 점수 내림차순 정렬
desc_score = sorted(students, key = lambda x: x[&quot;score&quot;], reverse = True)
print(desc_score)&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;정렬 문제에서는 어떤 기준으로 정렬할지 먼저 정하고, 그 기준을 key와 lambda로 표현하는 습관을 들이면 좋습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 자주 하는 실수/주의점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull; &lt;code&gt;sort()&lt;/code&gt;는 원본 리스트를 직접 바꾸고, &lt;code&gt;sorted()&lt;/code&gt;는 새 리스트를 만듭니다.&lt;br /&gt;&amp;bull; &lt;code&gt;key&lt;/code&gt;에는 정렬 기준을 반환하는 함수를 넣어야 합니다.&lt;br /&gt;&amp;bull; &lt;code&gt;reverse = True&lt;/code&gt;는 내림차순 정렬을 의미합니다.&lt;br /&gt;&amp;bull; 문자열 숫자를 정렬할 때는 실제 숫자 기준인지 문자 기준인지 확인해야 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. 종합 요약&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull; &lt;code&gt;sort()&lt;/code&gt;: 리스트 자체를 정렬&lt;br /&gt;&amp;bull; &lt;code&gt;sorted()&lt;/code&gt;: 정렬된 새 리스트 반환&lt;br /&gt;&amp;bull; &lt;code&gt;key&lt;/code&gt;: 정렬 기준 지정&lt;br /&gt;&amp;bull; &lt;code&gt;lambda&lt;/code&gt;: 간단한 정렬 기준을 짧게 작성&lt;br /&gt;&amp;bull; &lt;code&gt;reverse = True&lt;/code&gt;: 역순 정렬&lt;/p&gt;</description>
      <category>GDGoC/Python</category>
      <category>Python</category>
      <author>Opal1031</author>
      <guid isPermaLink="true">https://opal1031.tistory.com/101</guid>
      <comments>https://opal1031.tistory.com/101#entry101comment</comments>
      <pubDate>Wed, 6 May 2026 17:13:23 +0900</pubDate>
    </item>
    <item>
      <title>[GDGoC PKNU] 7기 비기너 지원 과정 및 합격 후기</title>
      <link>https://opal1031.tistory.com/100</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;들어가면서&amp;hellip;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2025년 복학을 하면서, 개발자로 성장하고 싶다는 마음이 더욱 커졌다.&lt;br /&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;br /&gt;개인의 성장을 잠시 미루더라도, 다수와 함께하는 책임을 다하는 것이 우선이라고 생각했다.&lt;br /&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;그리고 마침내 회장 임기를 마치고 나서, 바로 GDGoC PKNU에 지원하게 된 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 많은 사람들이 GDGoC에 관심은 있지만, 중앙 동아리가 아니다 보니 정보가 부족한 경우가 많은 것 같았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그래서 직접 지원하며 겪은 과정을 되돌아보며 정리하게 되었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;GDGoC PKNU를 선택한 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이 내용은 면접 때도 받았던 질문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 희망 진로는 결국 정보 보안 전문가(화이트 해커)다.&lt;br /&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;br /&gt;교수님의 연구실과 연계된 과 동아리도 있고,&lt;br /&gt;대학생 연합 개발자 동아리도 있다.&lt;br /&gt;그럼에도 나는 GDGoC PKNU의 모집 공고만을 기다렸다.&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;그 이유는 2022년으로 거슬러 올라간다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.jpeg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vCMjP/dJMcab5ag4f/KGn6AQk9la0C4koZirImZk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vCMjP/dJMcab5ag4f/KGn6AQk9la0C4koZirImZk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vCMjP/dJMcab5ag4f/KGn6AQk9la0C4koZirImZk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvCMjP%2FdJMcab5ag4f%2FKGn6AQk9la0C4koZirImZk%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;4032&quot; height=&quot;3024&quot; data-filename=&quot;1.jpeg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 당시 GDSC가 주최한 제 1회 &amp;lsquo;놀러와요 해커톤&amp;rsquo; 에 참여했다.&lt;br /&gt;다양한 학교의 개발자들과 소통하며 해커톤을 진행했고, 고등학생 시절 웹 공부를 위해 자주 참고했던 생활코딩 이고잉(egoing) 님의 Git 세션 특강까지 들을 기회도 있었다.&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;이 경험을 통해 GDSC라는 동아리가 체계적이고, 네트워킹이 활발하며, 얻어갈 수 있는 게 많은 곳이라는 확신이 생겼다.&lt;br /&gt;그리고 &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;br /&gt;하지만 나는 아직 그 정도 단계가 아니라고 생각했고, 처음 진로를 컴퓨터 쪽으로 정할 때도 &amp;lsquo;정보보호학과&amp;rsquo;가 아닌 &amp;lsquo;컴퓨터공학과&amp;rsquo;를 선택했던 이유 역시 기초 이론을 먼저 다지자는 생각 때문이었다.&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;이번에도 같은 맥락에서, 보안 이전에 기본기를 더 넓히고 쌓을 수 있는 GDGoC PKNU가 나에게 맞는 선택이라고 생각했다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;GDGoC PKNU란&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GDGoC(Google Developer Groups on Campus) PKNU는 함께 배우고, 만들고, 성장하는 부경대학교 학생 개발자 커뮤니티입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비기너부터 현업 개발자 분들까지 약 110여명의 멤버들이 함께하고 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;지원 과정&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;01. 트랙 결정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비기너 / 멤버 중 어느 과정으로 지원할 지 고민이 많았다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/opal1031/post/1d8d989d-0af7-4e64-98bc-cd6e5c304167/image.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 멤버 과정을 지원하려 했다.&lt;br /&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;br /&gt;프론트엔드는 고등학생 때나 1학기 전공 수업에서 간단히 다뤄본 적이 있어 기본은 알고 있었지만, 백엔드 분야는 한 번도 경험해본 적이 없었기 때문이다.&lt;br /&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;물론 프론트엔드 트랙에서 리액트를 배우고 싶은 마음도 있었다. 하지만 해당 과정은 HTML/CSS/JS 같은 기초부터 시작하는 커리큘럼으로 보였기에, 나에게는 백엔드가 더 적합하다고 생각했다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;02. 서류 지원&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.jpeg&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;116&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BpjCM/dJMcafsW6Go/PPgp6qcWObntYJK7ZnwwZ0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BpjCM/dJMcafsW6Go/PPgp6qcWObntYJK7ZnwwZ0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BpjCM/dJMcafsW6Go/PPgp6qcWObntYJK7ZnwwZ0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBpjCM%2FdJMcafsW6Go%2FPPgp6qcWObntYJK7ZnwwZ0%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;652&quot; height=&quot;116&quot; data-filename=&quot;2.jpeg&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;116&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서류 지원 과정에서 큰 어려움은 없었다.&lt;br /&gt;기본적인 인적 사항을 작성한 뒤, 총 세 가지 문항과 마지막으로 블로그 및 Github를 첨부하는 항목이 있었다.&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;span style=&quot;letter-spacing: 0px;&quot;&gt;지원 동기 서술&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;500자 내외 분량이었기에, 앞서 정리했던 GDGoC PKNU를 선택한 이유를 중심으로 솔직하게 서술했다.&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;span style=&quot;letter-spacing: 0px;&quot;&gt;프로젝트 및 대회 참여 등의 활동 서술&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평소 기록을 남기는 습관이 있어 Github에 백업해두었던 내용을 바탕으로 풀어썼다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/opal1031/post/358ae51a-ecf5-457a-979a-7fea71d159ef/image.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빈 수레가 요란하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 아직 수상 경력이나 대규모 프로젝트 경험은 없다.&lt;br /&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;span style=&quot;letter-spacing: 0px;&quot;&gt;갈등 해결 경험 및 리더십 관련 내용 서술&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자로 살아가기 위해서는 팀 프로젝트가 필수적이다.&lt;br /&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;&lt;br /&gt;이러한 경험을 바탕으로, 동아리 임원 및 회장직을 맡아 활동했던 사례를 함께 녹여 서술했다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;03. 면접&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.jpeg&quot; data-origin-width=&quot;1391&quot; data-origin-height=&quot;1201&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAkhoi/dJMcaayrr6u/36hPDPXkt9DBfarxdCKYNK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAkhoi/dJMcaayrr6u/36hPDPXkt9DBfarxdCKYNK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAkhoi/dJMcaayrr6u/36hPDPXkt9DBfarxdCKYNK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAkhoi%2FdJMcaayrr6u%2F36hPDPXkt9DBfarxdCKYNK%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;1391&quot; height=&quot;1201&quot; data-filename=&quot;3.jpeg&quot; data-origin-width=&quot;1391&quot; data-origin-height=&quot;1201&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;냅다 합격해버렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 알아가는 과정이며 부담없이 참가해달라는 멘트에 갑자기 새로운 활동을 하는 모습을 그리며 설레기 시작했다.&lt;br /&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;br /&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;span style=&quot;letter-spacing: 0px;&quot;&gt;자기소개 및 서류 지원 내용&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 나를 소개하고, 제출한 서류에 관한 이야기를 다시 하였다.&lt;br /&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;span style=&quot;letter-spacing: 0px;&quot;&gt;GDGoC PKNU에 지원한 이유&lt;/span&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두가 의문을 가질만한 내용이었다.&lt;br /&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;br /&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;span style=&quot;letter-spacing: 0px;&quot;&gt;비기너로 지원한 이유&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서류 지원 때 개인 블로그 및 Github가 있다면 첨부해달라는 항목에 반가운 마음으로 모두 작성하였다.&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;면접을 진행하면서 한 분께서 직접 Github를 구경(?)하는 것 같았다.&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;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;백엔드 트랙으로 지원한 이유&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞의 질문과 함께 이어지는 질문이었다.&lt;br /&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;백엔드 트랙을 진행하기 위해서 Java를 사용하는데 기본적인 지식이 있나는 질문에, 사실 나는 Python을 주로 사용하고, 전공 수업을 위해 C++을 간간히 쓰는 정도였기에 사실대로 말하고, 자습하면서 따라갈 수 있다고 답변했다.&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;br /&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~2개 수준의 분량이 될 수도 있다는 것이었다. 프론트엔드를 기본적으로 할 줄 안다지만, 정작 실무에서 많이 사용되는 React와 같은 내용은 모르기에 프론트엔드 트랙을 들어보는 것은 어떠하냐는 내용이었다.&lt;br /&gt;이어서 옆에 있던 프론트엔드 멘토분께서 커리큘럼을 설명해주시는데 주된 내용은 HTML / CSS / JS에 관한 내용은 초반에 일부이고 React를 주로 다루게 될 거라고 설명해주셨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비기너 과정을 한 번 한 이후에는 멤버로 활동해야한다고 생각했었기에, 혹시 이번에 비기너 과정에서 프론트엔드 트랙을 들어도 다음 학기에 다른 트랙의 내용을 들을 수 있냐고 질문한 결과, 가능하다는 답변을 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 나는 이러한 이유에서 백엔드 트랙에 지원했었는데, 여러번의 수강이 가능하다면 프론트엔드를 먼저 하고 싶은 생각도 있다라고 답변하며 즉석에서 지원을 변경하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 코어 멤버분의 조언에 매우 감사하게 생각하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;기타 질문&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 외에는 면접을 하면 당연히 물어보는 그러한 내용들이었고, 궁금한 내용들을 질문하는 시간이었다.&lt;br /&gt;트랙 변경을 하는 과정에서 이미 고민이 해결돼서 크게 더 할 말은 없었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;최종 합격&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.jpeg&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;995&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uIPco/dJMcagk5H6I/djXOC2We3CC59RwCd9vc70/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uIPco/dJMcagk5H6I/djXOC2We3CC59RwCd9vc70/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uIPco/dJMcagk5H6I/djXOC2We3CC59RwCd9vc70/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuIPco%2FdJMcagk5H6I%2FdjXOC2We3CC59RwCd9vc70%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;1408&quot; height=&quot;995&quot; data-filename=&quot;4.jpeg&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;995&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;면접 다음 날이 바로 개강이었고, 수강 정정도 해야했기에 정신없는 와중에도 메일함을 계속 새로고침 하였다.&lt;br /&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;br /&gt;이후 프론트엔드 트랙 멘토와 비기너들이 모인 단톡방에도 초대되며 본격적인 활동이 시작되었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마치며&amp;hellip;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 프론트엔드 트랙을 수강하며 배운 내용을 이어서 기록할 예정이다.&lt;br /&gt;GDGoC는 예전부터 참여하고 싶던 커뮤니티였고, 나름 열심히 준비한 끝에 합격했기에 더 열심히 임하려 한다.&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>GDGoC/etc</category>
      <category>GDGoC PKNU</category>
      <author>Opal1031</author>
      <guid isPermaLink="true">https://opal1031.tistory.com/100</guid>
      <comments>https://opal1031.tistory.com/100#entry100comment</comments>
      <pubDate>Wed, 6 May 2026 16:36:04 +0900</pubDate>
    </item>
    <item>
      <title>[Frontend] #06. React - 비동기 통신</title>
      <link>https://opal1031.tistory.com/99</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;비동기 통신&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Axios를 사용하여 서버와 HTTP 통신을 수행하고 데이터를 주고받을 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1️⃣ 비동기 통신 개요&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;동기 vs 비동기&lt;/b&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;동기 통신&lt;/th&gt;
&lt;th&gt;비동기 통신&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;처리 방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;요청 &amp;rarr; 응답 대기 &amp;rarr; 다음 작업&lt;/td&gt;
&lt;td&gt;요청 &amp;rarr; 다른 작업 진행 &amp;rarr; 응답 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;적합한 경우&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;은행 서비스 (정확성 중요)&lt;/td&gt;
&lt;td&gt;SNS 타임라인 (속도 중요)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;사용자 경험&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;응답 대기 중 화면 멈춤&lt;/td&gt;
&lt;td&gt;응답 대기 중에도 다른 작업 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;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;HTTP 요청&lt;/b&gt;을 통해 서버와 통신한다.&lt;/li&gt;
&lt;li&gt;서버는 &lt;b&gt;JSON, HTML, 이미지&lt;/b&gt; 등의 형식으로 응답을 반환한다.&lt;/li&gt;
&lt;li&gt;웹 페이지에서는 사용자 경험을 고려하여 &lt;b&gt;비동기 통신&lt;/b&gt;을 주로 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2️⃣ RESTful API&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HTTP 메서드&lt;/b&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;메서드&lt;/th&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;th&gt;CRUD&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;리소스 조회&lt;/td&gt;
&lt;td&gt;Read&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;리소스 생성&lt;/td&gt;
&lt;td&gt;Create&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PUT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;리소스 전체 수정&lt;/td&gt;
&lt;td&gt;Update&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PATCH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;리소스 일부 수정&lt;/td&gt;
&lt;td&gt;Update&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;리소스 삭제&lt;/td&gt;
&lt;td&gt;Delete&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;b&gt;RESTful API 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;GET /users           &amp;rarr; 사용자 목록 조회
GET /users/1         &amp;rarr; ID가 1인 사용자 조회
POST /users          &amp;rarr; 새 사용자 생성
PUT /users/1         &amp;rarr; 사용자 정보 전체 수정
PATCH /users/1       &amp;rarr; 사용자 정보 일부 수정
DELETE /users/1      &amp;rarr; ID가 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;size16&quot;&gt;&lt;b&gt;HTTP 상태 코드&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;height: 150px;&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr style=&quot;height: 24px;&quot;&gt;
&lt;th style=&quot;height: 24px;&quot;&gt;코드&lt;/th&gt;
&lt;th style=&quot;height: 24px;&quot;&gt;의미&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;code&gt;200 OK&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;요청 성공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;code&gt;201 Created&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;리소스 생성 성공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;code&gt;400 Bad Request&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;잘못된 요청&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;code&gt;401 Unauthorized&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;인증 실패&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;code&gt;404 Not Found&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;리소스 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;code&gt;500 Internal Server Error&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&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;b&gt;JSON 데이터 구조&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;id&quot;: 1,
  &quot;title&quot;: &quot;할 일 작성하기&quot;,
  &quot;completed&quot;: false
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3️⃣ Axios를 이용한 API 호출&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;설치&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;npm install axios&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;Axios의 특징&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;Promise 기반으로 동작&lt;/li&gt;
&lt;li&gt;응답이 JSON으로 자동 변환&lt;/li&gt;
&lt;li&gt;API 요청 취소 가능&lt;/li&gt;
&lt;li&gt;상태 코드 기반 오류 구분 용이&lt;/li&gt;
&lt;li&gt;Timeout 기능 지원&lt;/li&gt;
&lt;li&gt;다양한 브라우저 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GET 요청 (데이터 조회)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import axios from 'axios';

axios.get('https://jsonplaceholder.typicode.com/todos/1')
  .then(res =&amp;gt; console.log(res.data))
  .catch(err =&amp;gt; console.error(err));&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;POST 요청 (데이터 생성)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;axios.post('https://jsonplaceholder.typicode.com/todos', {
  title: '새로운 할 일',
  completed: false
})
  .then(res =&amp;gt; console.log(res.data))
  .catch(err =&amp;gt; console.error(err));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;PUT 요청 (전체 수정)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;axios.put('https://jsonplaceholder.typicode.com/todos/1', {
  id: 1,
  title: '할 일 수정됨',
  completed: true
})
  .then(res =&amp;gt; console.log(res.data))
  .catch(err =&amp;gt; console.error(err));&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;PATCH 요청 (일부 수정)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;axios.patch('https://jsonplaceholder.typicode.com/todos/1', {
  completed: true
})
  .then(res =&amp;gt; console.log(res.data))
  .catch(err =&amp;gt; console.error(err));&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;DELETE 요청 (데이터 삭제)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;axios.delete('https://jsonplaceholder.typicode.com/todos/1')
  .then(res =&amp;gt; console.log('삭제 완료'))
  .catch(err =&amp;gt; console.error(err));&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;Timeout 설정&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;axios.get('https://jsonplaceholder.typicode.com/todos', {
  timeout: 3000 // 3초 안에 응답 없으면 에러
})
  .then(response =&amp;gt; console.log('성공:', response.data))
  .catch(error =&amp;gt; {
    if (error.code === 'ECONNABORTED') {
      console.error('타임아웃 발생');
    } else {
      console.error('기타 오류:', error.message);
    }
  });&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4️⃣ async/await를 이용한 비동기 처리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Promise 방식&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;axios.get('https://jsonplaceholder.typicode.com/todos/1')
  .then(res =&amp;gt; console.log('응답:', res.data))
  .catch(err =&amp;gt; console.error('에러:', err));&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;async/await 방식&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;pre class=&quot;javascript&quot;&gt;&lt;code&gt;async function getTodo() {
  try {
    const res = await axios.get('https://jsonplaceholder.typicode.com/todos/1');
    console.log('응답:', res.data);
  } catch (err) {
    console.error('에러:', err);
  }
}

getTodo();&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;try/catch를 통한 에러 처리&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;async function getTodo() {
  try {
    const res = await axios.get('https://jsonplaceholder.typicode.com/todos/1');
    console.log('응답 데이터:', res.data);
  } catch (err) {
    if (err.response) {
      // 서버가 응답했지만 에러 상태 코드
      console.error('서버 오류:', err.response.status);
      console.error('에러 데이터:', err.response.data);
    } else if (err.request) {
      // 요청은 보냈지만 응답 없음
      console.error('응답 없음:', err.request);
    } else {
      // 요청 자체에 문제
      console.error('요청 오류:', err.message);
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5️⃣ React에서 Axios 사용하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본 예제: 데이터 불러오기&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { useState, useEffect } from 'react';
import axios from 'axios';

function TodoList() {
  const [todos, setTodos] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() =&amp;gt; {
    fetchTodos();
  }, []);

  const fetchTodos = async () =&amp;gt; {
    setLoading(true);
    setError(null);
    try {
      const res = await axios.get('https://jsonplaceholder.typicode.com/todos?_limit=5');
      setTodos(res.data);
    } catch (err) {
      setError('데이터를 불러오는데 실패했습니다.');
      console.error(err);
    } finally {
      setLoading(false);
    }
  };

  if (loading) return &amp;lt;p&amp;gt;로딩 중...&amp;lt;/p&amp;gt;;
  if (error) return &amp;lt;p style={{ color: 'red' }}&amp;gt;{error}&amp;lt;/p&amp;gt;;

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;Todo 리스트&amp;lt;/h1&amp;gt;
      &amp;lt;ul&amp;gt;
        {todos.map(todo =&amp;gt; (
          &amp;lt;li key={todo.id}&amp;gt;
            {todo.title}
          &amp;lt;/li&amp;gt;
        ))}
      &amp;lt;/ul&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default TodoList;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6️⃣ Axios vs Fetch API&lt;/h4&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;비교&lt;/th&gt;
&lt;th&gt;Axios&lt;/th&gt;
&lt;th&gt;Fetch API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;설치&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;필요 (&lt;code&gt;npm install axios&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;불필요 (내장)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;JSON 파싱&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;자동 (&lt;code&gt;res.data&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;수동 (&lt;code&gt;.json()&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;에러 처리&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;HTTP 에러 자동 감지&lt;/td&gt;
&lt;td&gt;수동 확인 (&lt;code&gt;res.ok&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Timeout&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;지원&lt;/td&gt;
&lt;td&gt;미지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;코드 길이&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;짧고 간결&lt;/td&gt;
&lt;td&gt;다소 김&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;브라우저 호환성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;폴리필 포함&lt;/td&gt;
&lt;td&gt;최신 브라우저만 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>GDGoC/FrontEnd</category>
      <category>FE</category>
      <author>Opal1031</author>
      <guid isPermaLink="true">https://opal1031.tistory.com/99</guid>
      <comments>https://opal1031.tistory.com/99#entry99comment</comments>
      <pubDate>Wed, 6 May 2026 16:30:14 +0900</pubDate>
    </item>
    <item>
      <title>[Frontend] #05. React - React Router와 전역 상태 관리</title>
      <link>https://opal1031.tistory.com/98</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;React Router와 전역 상태 관리&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;React Router를 사용하여 SPA를 구현하고, useContext로 전역 상태를 관리할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1️⃣ React Router&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React는 &lt;b&gt;SPA(Single Page Application)&lt;/b&gt; 방식으로 동작한다.&lt;/li&gt;
&lt;li&gt;SPA는 하나의 HTML 파일로 여러 화면을 구현하기 때문에, URL과 화면을 연결하는 &lt;b&gt;라우팅 관리&lt;/b&gt;가 필요하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;React Router&lt;/b&gt;는 URL 경로에 따라 적절한 컴포넌트를 렌더링해주는 도구다.&lt;/li&gt;
&lt;li&gt;페이지 새로고침 없이 화면을 전환할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;설치&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;npm install react-router-dom&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본 설정&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';

function App() {
  return (
    &amp;lt;BrowserRouter&amp;gt;
      &amp;lt;Routes&amp;gt;
        &amp;lt;Route path=&quot;/&quot; element={&amp;lt;Home /&amp;gt;} /&amp;gt;
        &amp;lt;Route path=&quot;/about&quot; element={&amp;lt;About /&amp;gt;} /&amp;gt;
        &amp;lt;Route path=&quot;*&quot; element={&amp;lt;div&amp;gt;404 Not Found&amp;lt;/div&amp;gt;} /&amp;gt;
      &amp;lt;/Routes&amp;gt;
    &amp;lt;/BrowserRouter&amp;gt;
  );
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;요소&lt;/th&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;BrowserRouter&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;앱 전체에 라우팅 기능 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Routes&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;라우트 목록을 감싸는 컨테이너&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Route&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;URL 경로와 컴포넌트를 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;path=&quot;*&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;정의되지 않은 경로 처리 (404)&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;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2️⃣ 페이지 이동 방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. Link 컴포넌트&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의 &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; 태그와 비슷하지만, 페이지 새로고침이 발생하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { Link } from 'react-router-dom';

function Nav() {
  return (
    &amp;lt;nav&amp;gt;
      &amp;lt;Link to=&quot;/&quot;&amp;gt;홈&amp;lt;/Link&amp;gt;
      &amp;lt;span&amp;gt; | &amp;lt;/span&amp;gt;
      &amp;lt;Link to=&quot;/about&quot;&amp;gt;소개&amp;lt;/Link&amp;gt;
    &amp;lt;/nav&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;2. useNavigate Hook&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;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { useNavigate } from 'react-router-dom';

function LoginButton() {
  const navigate = useNavigate();

  const handleLogin = () =&amp;gt; {
    // 로그인 로직...
    navigate('/home'); // 로그인 성공 후 홈으로 이동
  };

  return &amp;lt;button onClick={handleLogin}&amp;gt;로그인&amp;lt;/button&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;letter-spacing: 0px;&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&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;/th&gt;
&lt;th&gt;Link&lt;/th&gt;
&lt;th&gt;useNavigate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;형태&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;JSX 태그&lt;/td&gt;
&lt;td&gt;함수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;사용처&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;클릭 가능한 링크&lt;/td&gt;
&lt;td&gt;조건부/이벤트 기반 이동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Link to=&quot;/about&quot;&amp;gt;소개&amp;lt;/Link&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;navigate('/about')&lt;/code&gt;&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;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3️⃣ 컴포넌트 구조화&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;b&gt;유지보수성&lt;/b&gt;을 높인다.&lt;/li&gt;
&lt;li&gt;공통 레이아웃(Header, Footer 등)을 별도 컴포넌트로 분리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function Header() {
  return &amp;lt;header&amp;gt;나의 React 앱&amp;lt;/header&amp;gt;;
}

function Footer() {
  return &amp;lt;footer&amp;gt;&amp;copy; 2025&amp;lt;/footer&amp;gt;;
}

function AppShell({ children }) {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;Header /&amp;gt;
      {children}
      &amp;lt;Footer /&amp;gt;
    &amp;lt;/&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;Router와 결합&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;import { BrowserRouter, Routes, Route } from 'react-router-dom';

function App() {
  return (
    &amp;lt;BrowserRouter&amp;gt;
      &amp;lt;AppShell&amp;gt;
        &amp;lt;Routes&amp;gt;
          &amp;lt;Route path=&quot;/&quot; element={&amp;lt;Home /&amp;gt;} /&amp;gt;
          &amp;lt;Route path=&quot;/about&quot; element={&amp;lt;About /&amp;gt;} /&amp;gt;
        &amp;lt;/Routes&amp;gt;
      &amp;lt;/AppShell&amp;gt;
    &amp;lt;/BrowserRouter&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4️⃣ 폴더 구조 설계&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본 구조&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;src/
├── components/      # 공통 UI 컴포넌트
│   ├── Header.jsx
│   └── Footer.jsx
├── pages/           # 페이지 컴포넌트
│   ├── Home.jsx
│   └── About.jsx
├── contexts/        # 전역 상태 관리
│   └── ThemeContext.jsx
└── App.jsx          # 라우팅 설정&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;대규모 프로젝트 구조&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;glsl&quot;&gt;&lt;code&gt;src/
├── features/        # 기능별 폴더
│   ├── auth/
│   │   ├── pages/
│   │   ├── components/
│   │   └── services/  # API 통신
└── shared/          # 공용 컴포넌트&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5️⃣ useContext (전역 상태 관리)&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;Props Drilling&lt;/b&gt; 문제가 발생한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useContext&lt;/code&gt;를 사용하면 상위에서 제공한 값을 하위 어디서나 직접 꺼내 쓸 수 있다.&lt;/li&gt;
&lt;li&gt;여러 컴포넌트에서 공통적으로 사용해야 하는 데이터(테마, 로그인 정보 등)를 관리할 때 유용하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Context 생성 (contexts/ThemeContext.jsx)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { createContext, useContext, useState } from 'react';

const ThemeContext = createContext(null);

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  const value = { theme, setTheme };

  return (
    &amp;lt;ThemeContext.Provider value={value}&amp;gt;
      {children}
    &amp;lt;/ThemeContext.Provider&amp;gt;
  );
}

export function useTheme() {
  return useContext(ThemeContext);
}&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;Context 사용 (components/Header.jsx)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { Link } from 'react-router-dom';
import { useTheme } from '../contexts/ThemeContext';

function Header() {
  const { theme, setTheme } = useTheme();
  const nextTheme = theme === 'light' ? 'dark' : 'light';

  return (
    &amp;lt;header style={{
      background: theme === 'light' ? '#f9f9f9' : '#1a1a1a',
      color: theme === 'light' ? '#111' : '#f0f0f0',
      padding: '12px 16px'
    }}&amp;gt;
      &amp;lt;nav&amp;gt;
        &amp;lt;Link to=&quot;/&quot;&amp;gt;홈&amp;lt;/Link&amp;gt; | &amp;lt;Link to=&quot;/about&quot;&amp;gt;소개&amp;lt;/Link&amp;gt;
      &amp;lt;/nav&amp;gt;
      &amp;lt;div&amp;gt;
        &amp;lt;span&amp;gt;현재 테마: {theme}&amp;lt;/span&amp;gt;
        &amp;lt;button onClick={() =&amp;gt; setTheme(nextTheme)}&amp;gt;테마 전환&amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/header&amp;gt;
  );
}

export default Header;&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;App에서 Provider로 감싸기&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { ThemeProvider } from './contexts/ThemeContext';
import Header from './components/Header';
import Home from './pages/Home';
import About from './pages/About';

function App() {
  return (
    &amp;lt;ThemeProvider&amp;gt;
      &amp;lt;BrowserRouter&amp;gt;
        &amp;lt;Header /&amp;gt;
        &amp;lt;Routes&amp;gt;
          &amp;lt;Route path=&quot;/&quot; element={&amp;lt;Home /&amp;gt;} /&amp;gt;
          &amp;lt;Route path=&quot;/about&quot; element={&amp;lt;About /&amp;gt;} /&amp;gt;
          &amp;lt;Route path=&quot;*&quot; element={&amp;lt;div&amp;gt;404 Not Found&amp;lt;/div&amp;gt;} /&amp;gt;
        &amp;lt;/Routes&amp;gt;
      &amp;lt;/BrowserRouter&amp;gt;
    &amp;lt;/ThemeProvider&amp;gt;
  );
}

export default App;&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;페이지 컴포넌트에서 Context 사용 (pages/Home.jsx)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { useTheme } from '../contexts/ThemeContext';

function Home() {
  const { theme } = useTheme();

  const style = {
    padding: 24,
    minHeight: '100vh',
    background: theme === 'light' ? '#ffffff' : '#111111',
    color: theme === 'light' ? '#111111' : '#f5f5f5'
  };

  return (
    &amp;lt;main style={style}&amp;gt;
      &amp;lt;h1&amp;gt;홈&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;React Router로 연결된 홈 화면입니다.&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;헤더의 테마 전환 버튼으로 전역 상태를 변경할 수 있습니다.&amp;lt;/p&amp;gt;
    &amp;lt;/main&amp;gt;
  );
}

export default Home;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;useContext 사용 시 주의사항&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;너무 많은 값을 한 Context에 넣지 말 것&lt;/b&gt;: 하위 컴포넌트 전체가 불필요하게 리렌더링될 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;변경 빈도에 따라 Context 분리&lt;/b&gt;: 자주 변경되는 값과 그렇지 않은 값을 별도 Context로 관리한다.&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;code&gt;useMemo&lt;/code&gt;를 활용하여 Context value 객체를 메모이제이션할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { useMemo } from 'react';

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  const value = useMemo(() =&amp;gt; ({ theme, setTheme }), [theme]);

  return (
    &amp;lt;ThemeContext.Provider value={value}&amp;gt;
      {children}
    &amp;lt;/ThemeContext.Provider&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>GDGoC/FrontEnd</category>
      <category>FE</category>
      <author>Opal1031</author>
      <guid isPermaLink="true">https://opal1031.tistory.com/98</guid>
      <comments>https://opal1031.tistory.com/98#entry98comment</comments>
      <pubDate>Wed, 6 May 2026 16:25:10 +0900</pubDate>
    </item>
    <item>
      <title>[Frontend] #04. React - State와 Hooks</title>
      <link>https://opal1031.tistory.com/97</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;State와 Hooks&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;React Hooks를 사용하여 함수형 컴포넌트에서 상태 관리와 부수 효과를 처리할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1️⃣ State (useState)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;State&lt;/b&gt;는 컴포넌트 내부에서 관리되는 동적인 데이터다.&lt;/li&gt;
&lt;li&gt;State가 변경되면 React는 자동으로 컴포넌트를 &lt;b&gt;리렌더링&lt;/b&gt;한다.&lt;/li&gt;
&lt;li&gt;일반 변수는 변경되어도 UI가 업데이트되지 않지만, State는 변경 시 자동으로 화면을 갱신한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0); // 초기값 0

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;현재 카운트: {count}&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; setCount(count + 1)}&amp;gt;증가&amp;lt;/button&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; setCount(count - 1)}&amp;gt;감소&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;State의 특징&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;code&gt;setState&lt;/code&gt; 함수를 통해 값 변경&lt;/li&gt;
&lt;li&gt;&lt;b&gt;추적&lt;/b&gt;: State 값이 변하면 UI가 자동으로 업데이트&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비동기성&lt;/b&gt;: &lt;code&gt;setState&lt;/code&gt;는 즉시 반영되지 않고, 나중에 일괄 처리됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;State의 비동기성&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;const [count, setCount] = useState(0);

const handleClick = () =&amp;gt; {
  setCount(count + 1);
  console.log(count); // 아직 이전 값 출력
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;함수형 업데이트&lt;/b&gt;&lt;/p&gt;
&lt;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 class=&quot;reasonml&quot;&gt;&lt;code&gt;const handleClick = () =&amp;gt; {
  setCount(prev =&amp;gt; prev + 1);
  setCount(prev =&amp;gt; prev + 1);
  setCount(prev =&amp;gt; prev + 1);
  // count는 3 증가
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2️⃣ useEffect&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;useEffect&lt;/code&gt;는 렌더링 후 &lt;b&gt;부수 효과(side effect)&lt;/b&gt;를 처리하는 Hook이다.&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;DOM 직접 조작&lt;/li&gt;
&lt;li&gt;외부 라이브러리 호출&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { useState, useEffect } from 'react';

function Timer() {
  const [count, setCount] = useState(0);

  // count가 변경될 때마다 실행
  useEffect(() =&amp;gt; {
    console.log(`count가 ${count}로 변경되었습니다.`);
  }, [count]); // 의존성 배열

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;현재 값: {count}&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; setCount(count + 1)}&amp;gt;+1&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;의존성 배열&lt;/b&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;의존성 배열&lt;/th&gt;
&lt;th&gt;실행 시점&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[]&lt;/code&gt; (빈 배열)&lt;/td&gt;
&lt;td&gt;컴포넌트 마운트 시 1회만 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[state]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;해당 state가 변경될 때마다 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;생략&lt;/td&gt;
&lt;td&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;b&gt;마운트 시 1회만 실행&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;useEffect(() =&amp;gt; {
  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;size16&quot;&gt;&lt;b&gt;useEffect 무한 루프 주의&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// ❌ 잘못된 예: 무한 루프 발생
function Example() {
  const [count, setCount] = useState(0);

  useEffect(() =&amp;gt; {
    setCount(prev =&amp;gt; prev + 1); // count 변경
  }, [count]); // count가 변경될 때마다 실행 &amp;rarr; 무한 반복

  return &amp;lt;p&amp;gt;{count}&amp;lt;/p&amp;gt;;
}

// ✅ 올바른 예: 빈 배열로 1회만 실행
useEffect(() =&amp;gt; {
  setCount(prev =&amp;gt; prev + 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;size16&quot;&gt;&lt;b&gt;cleanup 함수&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;useEffect에서 반환하는 함수는 컴포넌트가 언마운트되거나 다음 effect가 실행되기 전에 호출된다.&lt;/li&gt;
&lt;li&gt;타이머 해제, 구독 취소 등에 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;useEffect(() =&amp;gt; {
  const timer = setInterval(() =&amp;gt; {
    console.log('1초마다 실행');
  }, 1000);

  // cleanup 함수
  return () =&amp;gt; {
    clearInterval(timer);
    console.log('타이머 해제');
  };
}, []);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3️⃣ 이벤트 처리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React에서 이벤트는 &lt;b&gt;camelCase&lt;/b&gt;로 작성한다 (&lt;code&gt;onClick&lt;/code&gt;, &lt;code&gt;onChange&lt;/code&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;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function Button() {
  const handleClick = () =&amp;gt; {
    alert('버튼 클릭!');
  };

  return &amp;lt;button onClick={handleClick}&amp;gt;클릭&amp;lt;/button&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;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function Greeting() {
  const sayHello = (name) =&amp;gt; {
    alert(`안녕, ${name}!`);
  };

  // ❌ 잘못된 예: 렌더링 시 즉시 실행
  // return &amp;lt;button onClick={sayHello('민서')}&amp;gt;인사&amp;lt;/button&amp;gt;;

  // ✅ 올바른 예: 클릭 시 실행
  return &amp;lt;button onClick={() =&amp;gt; sayHello('민서')}&amp;gt;인사&amp;lt;/button&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;이벤트 객체 (e)&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;code&gt;e&lt;/code&gt;&lt;/b&gt;를 받는다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;e.target&lt;/code&gt;으로 이벤트가 발생한 요소에 접근할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function InputExample() {
  const [text, setText] = useState('');

  const handleChange = (e) =&amp;gt; {
    setText(e.target.value); // 입력값 가져오기
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;input type=&quot;text&quot; onChange={handleChange} placeholder=&quot;입력하세요&quot; /&amp;gt;
      &amp;lt;p&amp;gt;입력값: {text}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이벤트 전파 (Bubbling)&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;code&gt;e.stopPropagation()&lt;/code&gt;으로 전파를 막을 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function App() {
  const handleCloseMenu = () =&amp;gt; console.log(&quot;메뉴 닫힘&quot;);
  const handleLogin = (e) =&amp;gt; {
    e.stopPropagation(); // 부모로 이벤트 전달 차단
    console.log(&quot;로그인 클릭&quot;);
  };

  return (
    &amp;lt;div onClick={handleCloseMenu}&amp;gt;
      &amp;lt;button onClick={handleLogin}&amp;gt;로그인&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4️⃣ 조건부 렌더링&lt;/h3&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;주로 &lt;b&gt;삼항 연산자&lt;/b&gt;를 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function LoginButton({ isLoggedIn }) {
  return (
    &amp;lt;div&amp;gt;
      {isLoggedIn ? (
        &amp;lt;button&amp;gt;로그아웃&amp;lt;/button&amp;gt;
      ) : (
        &amp;lt;button&amp;gt;로그인&amp;lt;/button&amp;gt;
      )}
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;조건부 렌더링 예제&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function App() {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  return (
    &amp;lt;div&amp;gt;
      {isLoggedIn ? &amp;lt;p&amp;gt;환영합니다!&amp;lt;/p&amp;gt; : &amp;lt;p&amp;gt;로그인해주세요&amp;lt;/p&amp;gt;}
      &amp;lt;button onClick={() =&amp;gt; setIsLoggedIn(!isLoggedIn)}&amp;gt;
        {isLoggedIn ? &quot;로그아웃&quot; : &quot;로그인&quot;}
      &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;amp;&amp;amp; 연산자를 이용한 조건부 렌더링&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function Notification({ hasUnread }) {
  return (
    &amp;lt;div&amp;gt;
      {hasUnread &amp;amp;&amp;amp; &amp;lt;p&amp;gt;읽지 않은 메시지가 있습니다.&amp;lt;/p&amp;gt;}
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>GDGoC/FrontEnd</category>
      <category>FE</category>
      <author>Opal1031</author>
      <guid isPermaLink="true">https://opal1031.tistory.com/97</guid>
      <comments>https://opal1031.tistory.com/97#entry97comment</comments>
      <pubDate>Wed, 6 May 2026 16:21:46 +0900</pubDate>
    </item>
    <item>
      <title>[Frontend] #03. React 기초</title>
      <link>https://opal1031.tistory.com/96</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;들어가면서...&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;React&lt;/b&gt;는 처음 다뤄보는 내용이었기에, 제공된 학습 자료 이외에도 Github Copilot등을 통해 다양한 예제를 알아보는 방식으로 공부하였다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;React&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;React는 사용자 인터페이스를 구축하기 위한 JavaScript 라이브러리다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;React 소개&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React는 Facebook(현 Meta)에서 개발한 오픈소스 JavaScript 라이브러리로, &lt;b&gt;컴포넌트 기반&lt;/b&gt;으로 UI를 구성하고 &lt;b&gt;상태 관리&lt;/b&gt;를 효율적으로 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SPA(Single Page Application)&lt;/b&gt; 개발에 주로 사용되며, &lt;b&gt;Virtual DOM&lt;/b&gt;을 통해 빠른 렌더링 성능을 제공한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;React의 주요 특징&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;: 재사용 가능한 독립적인 UI 조각으로 구성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단방향 데이터 흐름&lt;/b&gt;: 상태 변화를 명시적으로 관리하여 인과 관계 파악이 용이&lt;/li&gt;
&lt;li&gt;&lt;b&gt;JSX 문법&lt;/b&gt;: JavaScript에 HTML을 가미한 문법으로 초보자도 쉽게 학습 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Virtual DOM&lt;/b&gt;: 실제 DOM 조작을 최소화하여 성능 최적화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;강력한 커뮤니티&lt;/b&gt;: Meta의 적극적인 지원과 활발한 개발자 커뮤니티&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;React 프로젝트 시작하기&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Vite를 사용하여 React 프로젝트를 생성할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;# React 프로젝트 생성
npm create vite@latest

# 프로젝트 디렉토리로 이동
cd project-name

# 패키지 설치
npm install

# 개발 서버 실행
npm run dev&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프로젝트 구조&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;src/
├── components/      # 공통 UI 컴포넌트
│   ├── Header.jsx
│   └── Footer.jsx
├── pages/           # 페이지 컴포넌트
│   ├── Home.jsx
│   └── About.jsx
├── contexts/        # 전역 상태 관리
│   └── ThemeContext.jsx
├── App.jsx          # 메인 컴포넌트
└── main.jsx         # 앱 진입점&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1️⃣ JSX (JavaScript XML)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JSX는 JavaScript의 확장 문법으로, HTML과 유사한 문법을 사용할 수 있게 해준다.&lt;/li&gt;
&lt;li&gt;JSX는 트랜스파일러를 거쳐 JavaScript 코드로 변환된다.&lt;/li&gt;
&lt;li&gt;JSX 내에서는 &lt;b&gt;표현식(Expression)&lt;/b&gt;만 사용 가능하며, if문이나 for문 같은 &lt;b&gt;문(Statement)&lt;/b&gt;은 사용할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function App() {
  const name = &quot;React&quot;;
  const isLoggedIn = true;

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;Hello, {name}!&amp;lt;/h1&amp;gt;
      {isLoggedIn ? &amp;lt;p&amp;gt;환영합니다!&amp;lt;/p&amp;gt; : &amp;lt;p&amp;gt;로그인해주세요&amp;lt;/p&amp;gt;}
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;JSX에서 표현식과 문의 차이&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// ✅ 가능: 표현식 사용
function Greeting() {
  const user = { name: &quot;민서&quot;, login: true };
  const message = user.login ? &quot;환영합니다&quot; : &quot;로그인 필요&quot;;

  return &amp;lt;p&amp;gt;{message}&amp;lt;/p&amp;gt;;
}

// ❌ 불가능: JSX 내부에서 if문 사용
function Greeting() {
  return (
    &amp;lt;div&amp;gt;
      {if (isLoggedIn) { &quot;환영&quot; } else { &quot;로그인&quot; }}
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;JSX는 반드시 하나의 최상위 요소만 반환&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// ❌ 잘못된 예시
return (
  &amp;lt;h1&amp;gt;안녕&amp;lt;/h1&amp;gt;
  &amp;lt;h2&amp;gt;리액트&amp;lt;/h2&amp;gt;
);

// ✅ 해결 1: div로 감싸기
return (
  &amp;lt;div&amp;gt;
    &amp;lt;h1&amp;gt;안녕&amp;lt;/h1&amp;gt;
    &amp;lt;h2&amp;gt;리액트&amp;lt;/h2&amp;gt;
  &amp;lt;/div&amp;gt;
);

// ✅ 해결 2: Fragment 사용
return (
  &amp;lt;&amp;gt;
    &amp;lt;h1&amp;gt;안녕&amp;lt;/h1&amp;gt;
    &amp;lt;h2&amp;gt;리액트&amp;lt;/h2&amp;gt;
  &amp;lt;/&amp;gt;
);&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2️⃣ Virtual DOM&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React의 가장 큰 특징 중 하나가 &lt;b&gt;Virtual DOM&lt;/b&gt;이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DOM(Document Object Model)&lt;/b&gt;은 HTML 문서를 JavaScript가 이용할 수 있는 객체 형태로 만든 것이다.&lt;/li&gt;
&lt;li&gt;웹 애플리케이션에서 사용자와의 상호작용이 많아질수록, DOM을 직접 조작하는 비용이 커진다.&lt;/li&gt;
&lt;li&gt;React는 &lt;b&gt;가상 DOM&lt;/b&gt;을 메모리에 유지하고, 변경 사항을 계산한 후 실제 DOM에 &lt;b&gt;변경된 부분만&lt;/b&gt; 반영한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;렌더링 과정&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;[상태 변경] &amp;rarr; [Virtual DOM 업데이트] &amp;rarr; [이전 Virtual DOM과 비교] &amp;rarr; [변경된 부분만 실제 DOM 업데이트]&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3️⃣ 컴포넌트 (Component)&lt;/h3&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;React에서는 &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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;함수형 컴포넌트&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;function Welcome({ name }) {
  return &amp;lt;h1&amp;gt;안녕하세요, {name}님!&amp;lt;/h1&amp;gt;;
}

function App() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Welcome name=&quot;민서&quot; /&amp;gt;
      &amp;lt;Welcome name=&quot;React 사용자&quot; /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;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;import&lt;/b&gt;: 외부 파일의 모듈을 불러올 때 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컴포넌트 정의&lt;/b&gt;: 함수형 컴포넌트로 상태와 로직을 포함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;return&lt;/b&gt;: JSX 표현식을 반환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;export&lt;/b&gt;: 다른 파일에서 이 컴포넌트를 사용할 수 있도록 내보내기&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;카운트: {count}&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; setCount(count + 1)}&amp;gt;증가&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default Counter;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4️⃣ Props&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Props는 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달하는 방법이다.&lt;/li&gt;
&lt;li&gt;Props는 &lt;b&gt;읽기 전용&lt;/b&gt;이며, 자식 컴포넌트에서 직접 수정할 수 없다.&lt;/li&gt;
&lt;li&gt;주로 &lt;b&gt;부모 &amp;rarr; 자식의 단방향 데이터 흐름&lt;/b&gt;을 따른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;// 부모 컴포넌트
function App() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Greeting name=&quot;민서&quot; age={20} /&amp;gt;
      &amp;lt;Greeting name=&quot;사용자&quot; age={21} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

// 자식 컴포넌트
function Greeting({ name, age }) {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;이름: {name}&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;나이: {age}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;배열 렌더링&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function App() {
  const names = ['민서', '사용자1', '사용자2'];

  return (
    &amp;lt;div&amp;gt;
      {names.map((name, index) =&amp;gt; (
        &amp;lt;Greeting key={index} name={name} /&amp;gt;
      ))}
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Props로 함수 전달&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function Button({ onClick }) {
  return &amp;lt;button onClick={onClick}&amp;gt;클릭&amp;lt;/button&amp;gt;;
}

function App() {
  const handleClick = () =&amp;gt; alert(&quot;버튼 클릭!&quot;);
  return &amp;lt;Button onClick={handleClick} /&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>GDGoC/FrontEnd</category>
      <category>FE</category>
      <author>Opal1031</author>
      <guid isPermaLink="true">https://opal1031.tistory.com/96</guid>
      <comments>https://opal1031.tistory.com/96#entry96comment</comments>
      <pubDate>Wed, 6 May 2026 16:19:24 +0900</pubDate>
    </item>
  </channel>
</rss>