<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>hurwan 님의 블로그</title>
    <link>https://hurwan.tistory.com/</link>
    <description>개발자 준비생 허완의 블로그입니다

2026.04 까지 글: https://app.notion.com/p/272117a41d4d80b1b0faffde0630da77?v=272117a41d4d8049b8f8000cbceb88a3</description>
    <language>ko</language>
    <pubDate>Fri, 4 Sep 2026 07:12:30 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>hurwan</managingEditor>
    <image>
      <title>hurwan 님의 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/8697104/attach/badfd03e8e8d44ee8288e6a95b0f3fbf</url>
      <link>https://hurwan.tistory.com</link>
    </image>
    <item>
      <title>[머신러닝] OpenCV의 큰 그림</title>
      <link>https://hurwan.tistory.com/130</link>
      <description>&lt;h1&gt;[머신러닝] OpenCV 시작&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 OpenCV 진도를 시작함에 따라 복습할 겸 내용을 정리해보았습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 OpenCV는 &lt;code&gt;Open Source Computer Vision Library&lt;/code&gt;라는 컴퓨터가 사람의 눈처럼 이미지와 영상을 인식하고 처리할 수 있도록 돕는 오픈소스 컴퓨터 비전 라이브러리 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 이미지를 읽고 변환하는데 강점을 보이는 라이브러리입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 이미지 읽기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 &lt;code&gt;OpenCV&lt;/code&gt;는 이미지를 읽는 기능을 제공합니다.&lt;/p&gt;
&lt;pre class=&quot;haskell&quot;&gt;&lt;code&gt;import cv2

img = cv.imread(&quot;경로_문자열.png&quot;)

print(type(img))
# &amp;lt;class 'numpy.ndarray'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 로딩 옵션으로 다음과 같이 설정 또한 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;# 컬러 이미지 로드
cv2.imread(path, cv2.IMREAD_COLOR)

# 흑백 이미지 로드 ([H, W)
cv2.imread(path, cv2.IMREAD_GRAYSCALE)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배열 순서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 반환값은 기본적으로 Numpy 배열로 컬러 이미지면 보통 &lt;code&gt;[H, W, C]&lt;/code&gt; 형태로 반환되게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 특징은 PyTorch의 &lt;code&gt;[C, H, W]&lt;/code&gt; 형태와 대조되어 향후에는&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;tensor = torch.from_numpy(img)
tensor = tensor.permute(2, 0, 1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과 같은 코드가 나올 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;BGR&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenCV에서 채널의 기본 순서는 &lt;code&gt;{ 0: B, 1: G, 2: R }&lt;/code&gt; 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 익숙한 순서는 &lt;code&gt;R G B&lt;/code&gt; 순서이기 때문에 다음과 같은 방식으로 순서를 변환하여 시각화 할 때 사진이 이상하게 나오는 것을 막을 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;rgb = cv2.cvtColor(
  img,
  cv2.COLOR_BGR2RGB
)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;dtype&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 보통 이미지의 경우에는 &lt;code&gt;dtype=uint8&lt;/code&gt;이며 픽셀 범위 &lt;code&gt;0~255&lt;/code&gt;를 표현하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PyTorch 모델에 넣을 때는 보통 &lt;code&gt;uint8&lt;/code&gt;에서 &lt;code&gt;float32&lt;/code&gt;타입으로 변환한 뒤 다시 &lt;code&gt;0~1&lt;/code&gt;로 정규화 변환을 하게 됩니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;img = img.astype(&quot;float32&quot;) / 255.0&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;imread()&lt;/code&gt; 실패&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenCV는 &lt;code&gt;imread(path)&lt;/code&gt;에 이미지가 없는 경우, 경우에 따라 예외 대신 &lt;code&gt;None&lt;/code&gt; 값을 반환하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음과 같이 유효성 검사를 하는 것도 좋은 방법입니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;img = cv2.imread(&quot;???.jpg&quot;)

if img is None:
  print(&quot;image not exists&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 이미지 띄우기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OepnCV는 &lt;code&gt;imshow(panel_name, img_ndarray)&lt;/code&gt;를 통해 화면을 그리는 기능을 제공해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 띄워진 이미지 창은 프로세스의 종료와 함께 빠르게 종료될 수 있기 때문에 보통 키 입력을 기다리는 &lt;code&gt;waitKey()&lt;/code&gt; 메서드와 함께 쓰이는 경우가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;waitKey()&lt;/code&gt;의 인자로는 정수가 들어가며 &lt;code&gt;0&lt;/code&gt;을 넣을 시에는 무한정으로 키 입력을 대기하며 &lt;code&gt;1&lt;/code&gt; 이상을 넣을시에는 &lt;code&gt;N ms&lt;/code&gt;동안 키 입력을 기다리고 다음 코드를 진행하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;waitKey&lt;/code&gt;의 반환값은 눌린 키 정보를 정수로 반환해줍니다. 이를 이용하여 &lt;code&gt;waitKey(0) == 27&lt;/code&gt;과 같이 사용할 수 있으며 조금 더 보기 쉽게 하려면 &lt;code&gt;waitKey(0) &amp;amp; 0xFF == ord(&quot;q&quot;)&lt;/code&gt; 패턴을 이용할 수 있습니다. (&lt;code&gt;0xFF&lt;/code&gt;는 하위 8비트만 보기 위한 값입니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;destroyAllWindows()&lt;/code&gt;를 이용하여 OpenCV가 만든 GUI 창을 모두 닫을 수 있습니다. 특정 창만 제거하기 위해선 &lt;code&gt;cv2.destroyWindow(&quot;panel_name&quot;)&lt;/code&gt;를 이용할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 영상 읽기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenCV에서는 영상 읽기 또한 제공하여 다음과 같은 코드를 이용하여 영상 매체 정보를 가져올 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;# 파일 읽어오기
cap = cv2.VideoCapture(&quot;video.mp4&quot;)

# 웹 캠 읽어오기 (스트림 객체 번호)
cap = cv2.VideoCapture(0)

if not cap.isOpened():
  print(&quot;video not detected&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이후에는 &lt;code&gt;cap.read()&lt;/code&gt;를 이용하여 &lt;code&gt;ret, frame&lt;/code&gt; 값을 받을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ret&lt;/code&gt;는 &lt;code&gt;bool&lt;/code&gt; 타입의 읽기 성공 여부 값이며 &lt;code&gt;frame&lt;/code&gt;는 실제 한 프레임의 이미지 Numpy 배열입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ret&lt;/code&gt;는 마지막 영상의 마지막 또는 카메라 프레임을 읽는 것에 실패하게 된다면 &lt;code&gt;False&lt;/code&gt;를 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 보통 &lt;code&gt;if not ret: brerak&lt;/code&gt; &lt;code&gt;cap.read()&lt;/code&gt; 아래에 넣습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 마지막에 카메라를 잡은 경우에, 카메라를 계속 점유하지 않기 위해서 &lt;code&gt;cap.release()&lt;/code&gt;를 작성할 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 &lt;code&gt;cap.get(cv2.CAP_*)&lt;/code&gt; 을 이용하여 &lt;code&gt;fps&lt;/code&gt;, &lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt;, &lt;code&gt;frame_count&lt;/code&gt; 등을 꺼낼 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;fourcc&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FourCC는 영상 코덱을 4글자 코드로 식별하는 값을 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코덱이란 일반적으로 모든 프레임의 정보를 압축 알고리즘 없이 그대로 저장하면 용량이 너무 커지기 때문에 이를 codec를 이용하여 작언 영상으로 압축하여 보여주는 것을 말합니다.&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;code&gt;H.264&lt;/code&gt;: 흔하며 호환성 좋음&lt;/li&gt;
&lt;li&gt;&lt;code&gt;H.265 / HEVC&lt;/code&gt;: &lt;code&gt;H.264&lt;/code&gt;보다 더 높은 압축률 + 높은 연산량&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VP9&lt;/code&gt;: 높은 압축 효율 및 최신&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MJPEG&lt;/code&gt;: 각 프레임을 JPEG처럼 압축. 단순하고 용량이 큼&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 형태의 코덱들은 &lt;code&gt;fourcc = cv4.VideoWriter_fourcc(*&quot;CODC&quot;)&lt;/code&gt; 와 같이 사용할 수 있으며 &lt;code&gt;out = cv2.VideoWriter(&quot;mix.avi&quot;, fourcc, fps1, (width, height))&lt;/code&gt; 와 같이 사용할 수 있습니다. 여기에서 확인할 것은 마지막의 인자 순서가 &lt;code&gt;(width, height)&lt;/code&gt;라는 점입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;cv2의 연산&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;cv2&lt;/code&gt;는 이미지에 대한 연산기능을 제공해줍니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;cv2.resize(img, (w, h))&lt;/code&gt;: 이미지를 리사이징 해줍니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)&lt;/code&gt;: 2번째 속성을 이용하여 이미지의 색상 형식을 변경해줍니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;COLOR_RGB2GRAY&lt;/code&gt;: RGB를 GRAY로 변경해줍니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;COLOR_BGR2RGB&lt;/code&gt;: BGR을 RGB로 변경해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cv2.flip(img, 1)&lt;/code&gt;: 이미지를 뒤집어줍니다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;+1&lt;/code&gt;: 좌우 반전&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0&lt;/code&gt; : 상하 반전&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-1&lt;/code&gt;: 상하 + 좌우&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)&lt;/code&gt;: 이미지를 회전시켜줍니다. &lt;code&gt;COUNTERCLOCKWISE&lt;/code&gt;도 존재합니다.&lt;/li&gt;
&lt;/ul&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;code&gt;cv2.add(img_gray, 100)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cv2.add(img_color, 100)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cv2.subtract(img_gray, 100)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cv2.multiply(img_gray, 2)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cv2.divide(img_gray, 2)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;후기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 OpenCV는 cv2 모듈을 이용하여 상수, 함수, 클래스를 모두 사용하는 모듈이였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에 프로젝트를 하느라 블로그를 소홀히 한거같아 앞으로 한동안 머신러닝 및 AI 쪽으로 더 공부를 할 생각입니다.&lt;/p&gt;</description>
      <category>머신러닝</category>
      <author>hurwan</author>
      <guid isPermaLink="true">https://hurwan.tistory.com/130</guid>
      <comments>https://hurwan.tistory.com/130#entry130comment</comments>
      <pubDate>Thu, 3 Sep 2026 21:53:22 +0900</pubDate>
    </item>
    <item>
      <title>[OpenCV] 이미지와 영상의 데이터 조작</title>
      <link>https://hurwan.tistory.com/129</link>
      <description>&lt;h1&gt;[데이터 조작] 이미지와 영상의 데이터 조작&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 이미지와 영상 데이터의 구성에 대해서 알아보며 이것들 조작하는 방법에 대해서 알아보겠습니다. 글은 &lt;b&gt;OpenCV&lt;/b&gt; 코드와 함게 작성됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;목차&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;픽셀&lt;/li&gt;
&lt;li&gt;색 표현&lt;/li&gt;
&lt;li&gt;압축/코덱&lt;/li&gt;
&lt;li&gt;컨테이너&lt;/li&gt;
&lt;li&gt;디코딩&lt;/li&gt;
&lt;li&gt;영상 처리&lt;/li&gt;
&lt;li&gt;평활화&lt;/li&gt;
&lt;li&gt;다시 인코딩&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이미지와 영상&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 이미지부터 이야기를 하면서 시작하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지는 2차원 배열로 구성되어있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 2차원의 &lt;code&gt;H, W&lt;/code&gt; 프레임 크기와 함께 &lt;code&gt;RGB&lt;/code&gt;, &lt;code&gt;HSV&lt;/code&gt;, &lt;code&gt;GrayScale&lt;/code&gt; 등에 따른 다른 채널 개수/표현으로 구성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenCV 기준에서는 하나의 이미지 데이터의 순서는 &lt;code&gt;(H, W, C)&lt;/code&gt; 로 이루어져 있으며 &lt;code&gt;GrayScale&lt;/code&gt;의 경우에는 &lt;code&gt;(H, W)&lt;/code&gt; 로 이루어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 영상의 경우에는 이미지 여러장을 시간 순서대로 보여주는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영상에는 다음과 같은 정보가 들어있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fps: 어떤 속도로 다음 이미지를 보여줄 것인지 (Frame Per Second)&lt;/li&gt;
&lt;li&gt;frame: 영상을 구성하는 여러 이미지중 하나의 이미지입니다.&lt;/li&gt;
&lt;li&gt;time: 몇번째 또는 어떤 시점에 배치되는지에 대한 정보입니다.&lt;/li&gt;
&lt;li&gt;Codec: &lt;code&gt;COder + DECoder&lt;/code&gt; 에서 나온 말로 여러장의 이미지 데이터를 압축하며 이를 디코딩하는 알고리즘을 뜻합니다. 대표적인 알고리즘은 다음이 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;H.264&lt;/code&gt;: 흔하며 호환성 좋음&lt;/li&gt;
&lt;li&gt;&lt;code&gt;H.265 / HEVC&lt;/code&gt;: &lt;code&gt;H.264&lt;/code&gt;보다 더 높은 압축률 + 높은 연산량&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VP9&lt;/code&gt;: 높은 압축 효율 및 최신&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MJPEG&lt;/code&gt;: 각 프레임을 JPEG처럼 압축. 단순하고 용량이 큼&lt;/li&gt;
&lt;li&gt;&lt;i&gt;대표적인 인코딩 개념은 여러 이웃 프레임에서 변경된 정보를 표현하는 방식이 존재합니다.&lt;/i&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Encoding &amp;amp; Decoding: Codec 규칙을 이용하여 데이터를 압축 및 해석하는 과정을 말합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래에서 이를 구성하는 요소에 대해서 더 상세히 알아보도록 하겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;I / P / B 프레임&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;I-Frame: 독립적인 완전한 이미지에 가까움&lt;/li&gt;
&lt;li&gt;P-Frame: 이전 프레임을 참고하여 변형된 데이터&lt;/li&gt;
&lt;li&gt;B-Frame: 이전 + 이후 프렐임을 참고합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래로 갈수록 복원을 위해서 다른 프레임의 영향을 크게 받습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;FourCC&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FourCC는 &lt;code&gt;Four Character Code&lt;/code&gt;라는 4글자로 식별된 코ㅗ드를 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenCV에서는 다음과 같은 문법으로 표현하게 됩니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;# * 연산자는 *&quot;mo4v&quot; == &quot;m&quot;, &quot;p&quot;, &quot;4&quot;, &quot;v&quot;로 풀어주는 언패킹 연산자입니다.
fourcc = cv2.VideoWriter_fourcc(*&quot;mp4v&quot;)

fourcc = cv2.VideoWriter_fourcc(*&quot;H265&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주의할 점&lt;/b&gt;으로는 FourCC와 Codec를 동일시하면 안된다는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FourCC는 Codex/영상 포맷을 식별하기 위한 코드에 더 가까워 &lt;code&gt;H.265&lt;/code&gt; 계열도 환경이나 Container에 따라서 &lt;code&gt;H264&lt;/code&gt;, &lt;code&gt;X265&lt;/code&gt;, &lt;code&gt;avc1&lt;/code&gt;과 같은 식별자로 나타날 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Codec는 압축 기술, FourCC는 그 기술을 나타내기 위해서 사용되는 식별 코드라고 할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Container&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Container Format&lt;/b&gt;는 여러 데이터를 담는 상자를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서 &lt;code&gt;.mp4&lt;/code&gt;가 있으면 그 파일 안에는 다음과 같은 정보가 존재할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;# video.mp4
- VideoStream: H.264
- Audio Stream: ACC
- Subtitle: 부제목
- Metadata
  - 지속 시간
  - 생성 시간
  - 등등&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Container의 종류에 따라 여러가지 내부 Codec가 존재할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Pixel Format&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;픽셀의 종류에도 &lt;code&gt;BGR&lt;/code&gt;, &lt;code&gt;RGB&lt;/code&gt;, &lt;code&gt;GrayScale&lt;/code&gt;, &lt;code&gt;HSV&lt;/code&gt;, &lt;code&gt;YCbCr&lt;/code&gt; 등과 같은 표현 방식이 있으며 예를 들어 &lt;code&gt;RGB&lt;/code&gt;는 흔히 아는 빨강, 초록, 파랑을 의미하며, &lt;code&gt;YCbCr&lt;/code&gt;은 순서대로 밝기, 파랑, 빨강을 의미하는 방식으로 셀들이 구성되게 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;평활화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평활화는 데이터를 저장하는 방식이 아닌, 셀들의 분포가 한쪽으로 몰려있을 때, 이를 골고루 분포하게 만들어주는 기술을 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평활화에는 크게 2가지가 존재합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;히스토그램 평활화: 명암 분포를 고르게 만드는 것&lt;/li&gt;
&lt;li&gt;공간적 평활화: 주변 픽셀을 이용해서 노이즈를 줄이는 것&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;히스토그램 평활화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 CDF(누적 분포)를 이용하여 밝기 정보들을 넓게 펼쳐주는 방식을 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 결국 이미지를 명암 대비로 크게 만들어주는 효과를 만들어줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 특정 밝기 값에 대해서 그 밝기보다 작은 밝기들의 비율만큼 새로운 범위로 재배치하는 방식을 말합니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

result = cv2.equalizeHist(gray)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Smoothing 평화화&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노이즈 제거&lt;/li&gt;
&lt;li&gt;작은 변화 제거&lt;/li&gt;
&lt;li&gt;이미지를 부드럽게 만들기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 평활화를 위해 이동시키면서 계산을 해주는 &lt;b&gt;커널&lt;/b&gt;이라는 작은 행렬 (보통 &lt;code&gt;3x3&lt;/code&gt;, &lt;code&gt;5x5&lt;/code&gt;)이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커널은 아래 2가지를 통해 이미지의 전체 데이터를 새롭게 계산해줍니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;행렬의 위치별 값&lt;/li&gt;
&lt;li&gt;계산 방식&lt;/li&gt;
&lt;li&gt;평균 블러: 커널 크기만큼의 픽셀들의 평균을 구해서 가운데 픽셀로 사용하게 됩니다. 단순하고 빠르지만 경계선까지 많이 흐려집니다.&lt;/li&gt;
&lt;li&gt;가우시안 블러: 가운데 픽셀쪽으로 갈수록 더 높은 가중치를 주어 평균을 구합니다. 특별한 이유가 없다면 노이즈 제거에 많이 사용됩니다.&lt;/li&gt;
&lt;li&gt;중앙값 블러: 픽셀들을 정렬하여 중앙값을 사용합니다. &lt;code&gt;Salt-and-Pepper-Noise&lt;/code&gt;라는 흰색/검정 픽셀이 튀는 노이즈를 해결하는데 좋습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;# 평균 블러
mean_blur1 = cv2.blur(img, (3, 3))
mean_blur2 = cv2.boxFilter(img, -1, (3, 3))

# 가우시안 블러 (3번째 인자를 0으로 하면 opencv가 계산할 요소들을 기준으로 sigma를 자동 결정)
gaussian_blur = cv2.GaussianBlur(img, (3, 3), 0)

# 중앙값 블러 (숫자 하나만 사용)
median_blur = cv2.medianBlur(img, 3)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>머신러닝</category>
      <author>hurwan</author>
      <guid isPermaLink="true">https://hurwan.tistory.com/129</guid>
      <comments>https://hurwan.tistory.com/129#entry129comment</comments>
      <pubDate>Tue, 1 Sep 2026 09:16:29 +0900</pubDate>
    </item>
    <item>
      <title>[랭체인 중간프로젝트] 데이터베이스 설계 병합 전 체크</title>
      <link>https://hurwan.tistory.com/128</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;2026-08-31 15:54:13&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;데이터베이스 리뷰&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 제가 직접 작성하고 gpt한테 제약조건을 넣어달라 요청을 한 상태로 한번 리뷰를 해보겠습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;목차&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;개요&lt;/li&gt;
&lt;li&gt;service 스키마&lt;/li&gt;
&lt;li&gt;datasets 스키마&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;service&lt;/span&gt; 스키마의 테이블은 총 12개로&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용자 및 일반 서비스&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;users: 사용자 기본&lt;/li&gt;
&lt;li&gt;user_profiles: 사용자 러닝 선호도&lt;/li&gt;
&lt;li&gt;running_goals: 주간/월간 목표&lt;/li&gt;
&lt;li&gt;inquries: 문의경로 찾기 및 추천&lt;/li&gt;
&lt;li&gt;route_bookmarks: 사용자가 즐겨찾기한 경로&lt;/li&gt;
&lt;li&gt;point_bookmarks: 경유지 즐겨찾기&lt;/li&gt;
&lt;li&gt;route_requests: 사용자의 요청 정보&lt;/li&gt;
&lt;li&gt;route_request_points: 요청 정보에 달려있는 출발/목적지 포인트&lt;/li&gt;
&lt;li&gt;route_recommendations: 추천 경로 후보 (현재는 3개)&lt;/li&gt;
&lt;li&gt;route_points: 추천 경로 후보에 달려있는 포인트들 N개 (많을 수 있음)사용자 추적&lt;/li&gt;
&lt;li&gt;running_sessions: 사용자 러닝 1개 기록&lt;/li&gt;
&lt;li&gt;running_trackpoints: 사용자 러닝 기록 하나에 대해서 주기적으로 뽑은 gps 및 기타 정보&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 존재하며 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;datasets&lt;/span&gt;스키마는 총 8개 존재합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;convenience_stores: 편의점&lt;/li&gt;
&lt;li&gt;public_toilets: 공중화장실&lt;/li&gt;
&lt;li&gt;streetlights: 가로등&lt;/li&gt;
&lt;li&gt;security_lights: 보안등&lt;/li&gt;
&lt;li&gt;cctvs: CCTV&lt;/li&gt;
&lt;li&gt;parks: 도시공원 (polygon은 가능하면 넣기)&lt;/li&gt;
&lt;li&gt;pedestrian_roads: 보행자 전용 도로&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. &lt;code&gt;service.*&lt;/code&gt; 테이블&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;RunStop.service.png&quot; data-origin-width=&quot;1988&quot; data-origin-height=&quot;1451&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FtGRS/dJMcabMmFUg/OvgIfgMHtLt2BztPUsCMk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FtGRS/dJMcabMmFUg/OvgIfgMHtLt2BztPUsCMk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FtGRS/dJMcabMmFUg/OvgIfgMHtLt2BztPUsCMk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFtGRS%2FdJMcabMmFUg%2FOvgIfgMHtLt2BztPUsCMk1%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;1988&quot; height=&quot;1451&quot; data-filename=&quot;RunStop.service.png&quot; data-origin-width=&quot;1988&quot; data-origin-height=&quot;1451&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;users&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 아이디는 UNIQUE로 존재하며 탈퇴 시 사용자의 아이디는 &lt;code&gt;withdraw_[id]_&amp;lt;random&amp;gt;&lt;/code&gt;으로 갈 예정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전화번호는 이때 &lt;code&gt;NULLALE&lt;/code&gt;로 삭제시키며 &lt;code&gt;UNIQUE&lt;/code&gt; 또한 넣을 생각입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 정지를 먹을 때에는 영구정지를 당할 수 있으며 이때에는 &lt;code&gt;status=SUSPENDED&lt;/code&gt;, &lt;code&gt;suspended_until=NULL&lt;/code&gt; 상태가 됩니다. 이외의 경우에는 &lt;code&gt;status=SUSPENDED&lt;/code&gt;, &lt;code&gt;suspended_until=????-??-??&lt;/code&gt; 상태이빈다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;user_profiles&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 러닝을 할 때 서버에서 참고할 값입니다. 사용자마다 선호도가 다르기 때문에 이를 다르게 설정할 예정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;users 테이블과 &lt;code&gt;1:1&lt;/code&gt; 매칭이며 weight, height 는 기본으로 받고, 알고리즘 및 데이터에 따라 running_settings 형태를 바꿀 예정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;profile_image_url은 서버에 하드코딩을 박아둔 뒤 랜덤으로 이미지를 제공하는 형태로 사용할 예정입니다. 또는 프론트에서 직접 고정 이미지를 보여주는 방식을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;running_settings를 더 구체적으로 잡을 필요가 있는데 일단 이건 그대로 두고 늦어도 수요일까지 수정하는 방향으로 가려고 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;running_goals&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 월간 또는 주간 러닝 목표로 잡을 수 있게 만들 예정이며 일부러 기간은 2가지만 만들엇기 때문에 &lt;code&gt;goal_type&lt;/code&gt;는 &lt;code&gt;WEEKLY&lt;/code&gt;와 &lt;code&gt;MONTHLY&lt;/code&gt; 2가지만 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 아무때나 시작 및 종료가 가능하며 상태에 따라 종료 시간, 실패 시간, status 등이 설정되게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NULLABLE 한 &lt;code&gt;finished_at&lt;/code&gt;가 존재합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;point_bookmarks&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 사용자가 임시로 빠르게 찾아가기 위한 point 와 name 가 붙어잇는 데이터입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;route_requests&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자의 프롬프트, 알고리즘 입력 조건(사용자가 입력할수도 있고, llm이 넣어준 것일 수 있습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;selected_recommendations_idx는 사용자가 선택하기 전까지는 NULL이며 사용자가 중간에 앱을 끄면 특별한 일이 없으면 영원히 NULL로 남게 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;route_request_points&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 요청에 N개로 달려있는 포인트입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;route_recommendations&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘이 생성한 경로 후보로 메타데이터가 존재하며 정확히 지나는 지점들은 아래에 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;route라는 전체 연결 geometry(LineString, 4326) 가 존재합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;route_points&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후보 경로에 포함된 주요 지점입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;route_bookmarks&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;러닝 코스 자체를 북마크 합니다. 특별한 내용은 없습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;running_sessions&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자의 러닝 기록을 대표하는 내용입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 NULL이였다가 기록 시에 running_trackpoints로 집계되는 average_pace가 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;running_trackpoints&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주기적으로 프론트에서 보내주거나 캐싱하여 한번에 주는 사용자 러닝 실시간 기록입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;inquiries&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;h2 data-ke-size=&quot;size26&quot;&gt;3. &lt;code&gt;datasets.*&lt;/code&gt; 테이블&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정제한 &lt;code&gt;.csv&lt;/code&gt; 파일을 DB에 올린 테이블들 입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;RunStop.datasets.png&quot; data-origin-width=&quot;1872&quot; data-origin-height=&quot;852&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Kx5i3/dJMcagmq9xD/jykQwChiwo726sREhJYpEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Kx5i3/dJMcagmq9xD/jykQwChiwo726sREhJYpEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Kx5i3/dJMcagmq9xD/jykQwChiwo726sREhJYpEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKx5i3%2FdJMcagmq9xD%2FjykQwChiwo726sREhJYpEK%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;1872&quot; height=&quot;852&quot; data-filename=&quot;RunStop.datasets.png&quot; data-origin-width=&quot;1872&quot; data-origin-height=&quot;852&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;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;convenience_stores: 편의점&lt;/li&gt;
&lt;li&gt;public_toilets: 공중화장실&lt;/li&gt;
&lt;li&gt;streetlights: 가로등&lt;/li&gt;
&lt;li&gt;security_lights: 보안등&lt;/li&gt;
&lt;li&gt;cctvs: CCTV&lt;/li&gt;
&lt;li&gt;parks: 도시공원 (polygon은 가능하면 넣기)&lt;/li&gt;
&lt;li&gt;pedestrian_roads: 보행자 전용 도로&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 있으며 모두 위경도 데이터이며 마지막의 보행자 전용 도로는 &lt;code&gt;geometry(LineString, 4326)&lt;/code&gt;이 포함되어있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 &lt;code&gt;parks&lt;/code&gt;의 겨우에는 데이터를 찾아서 polygon으로 만들면 좋지 않을까 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 geometry 데이터에는 index가 붙어져있습니다. (초기에 데이터를 많이 넣고 수정은 사용자 사용시에 거의 일어나지 않기 때문입니다.)&lt;/p&gt;</description>
      <category>서비스</category>
      <author>hurwan</author>
      <guid isPermaLink="true">https://hurwan.tistory.com/128</guid>
      <comments>https://hurwan.tistory.com/128#entry128comment</comments>
      <pubDate>Mon, 31 Aug 2026 16:48:58 +0900</pubDate>
    </item>
    <item>
      <title>[랭체인 중간프로젝트] 중간 팀 과제와 주말 보고</title>
      <link>https://hurwan.tistory.com/127</link>
      <description>&lt;h1&gt;중간 팀 과제와 주말 보고&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;강사님이 내주신 과제 진행 방향과 그동안 한것들을 좀 정리 하였습니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/hurwan0629/RunStop/tree/docs/project-design/docs/02_%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8_%EC%84%A4%EA%B3%84/%EB%8F%84%EA%B5%AC&quot;&gt;아래에서 언급하는 템플릿&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/hurwan0629/RunStop/blob/docs/project-design/docs/00_%EA%B0%95%EC%82%AC%EB%8B%98%20%EA%B3%B5%EC%A7%80/2026-08-31_%ED%8C%80_%EA%B3%BC%EC%A0%9C_09_08_%EB%B0%9C%ED%91%9C.md&quot;&gt;과제&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;1. 중간 프로젝트&lt;/h1&gt;
&lt;h2&gt;각자 한 작업들&lt;/h2&gt;
&lt;h3&gt;재빈/건희&lt;/h3&gt;
&lt;p&gt;알고리즘 탐색&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;다익스트라 알고리즘을 하면 출력에 오류가 생길 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;가중치를 설정했을 때 도로 A가 거리가 300m + 가로등 적음 / 비용 등이 없을 수 있음 -&amp;gt; 점수에 따라서 다익스트라는 사용자 요구가 A보다 다른것에 가까운 알고리즘이 있을 수 잇음&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;파이프라인 설계 기법 고려중&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;한빈/승연&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;닉네임 중복은 아님. (잘못된 표기)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이메일은 없애는걸로&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로필 이미지는 서버에서 자유 지정&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;api 수요일 ~ 목요일&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;허완&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker&lt;/code&gt; + &lt;code&gt;fatapi&lt;/code&gt; + &lt;code&gt;node&lt;/code&gt; + &lt;code&gt;postgis&lt;/code&gt;의 프로젝트 실행 환경 및 인프라 구축&lt;/li&gt;
&lt;li&gt;TypeScript 등 공부하며 dto 더 깔끔하게 잡는중&lt;/li&gt;
&lt;li&gt;DB 업데이트 (스키마 2개짜리 단순한 마이그레이션)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;09/02 까지 할일&lt;/h2&gt;
&lt;h3&gt;전체&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Docker 깔기 (&lt;code&gt;09/07&lt;/code&gt;까지)&lt;h3&gt;백엔드&lt;/h3&gt;
&lt;/li&gt;
&lt;li&gt;알고리즘 양쪽 입출력 구체적으로&lt;/li&gt;
&lt;li&gt;대략적인 알고리즘 계획 (템플릿) 채워오기&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;프론트&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;만들어져있는 프론트 api 요구사항 명세서 작성해주기&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;인프라&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;TypeScript 및 인터페이스 규격 맞추기&lt;/li&gt;
&lt;li&gt;가능하면 repository 계층까지 와&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;2. 과제&lt;/h1&gt;
&lt;p&gt;강사님이 내주신 &lt;a href=&quot;https://github.com/hurwan0629/RunStop/blob/docs/project-design/docs/00_%EA%B0%95%EC%82%AC%EB%8B%98%20%EA%B3%B5%EC%A7%80/2026-08-31_%ED%8C%80_%EA%B3%BC%EC%A0%9C_09_08_%EB%B0%9C%ED%91%9C.md&quot;&gt;팀 과제&lt;/a&gt;를 하기 위해 역할을 분담하였습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;최한빈: 1, 3, 4번 환경 및 기본 구축&lt;/li&gt;
&lt;li&gt;이승연: 2번 영상 필터 3개 자유&lt;/li&gt;
&lt;li&gt;윤재빈/허완: 5번 AI 모델 2개 테스트해서 좋은거 채택하기 &lt;ul&gt;
&lt;li&gt;윤재빈: &lt;code&gt;Resnet&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;허완: 한번 찾아보고 AlexNet 복기 또는 겹치지 않는 무언가 사용해보기로함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;수요일까지 해오기로 하였습니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <category>회의록</category>
      <author>hurwan</author>
      <guid isPermaLink="true">https://hurwan.tistory.com/127</guid>
      <comments>https://hurwan.tistory.com/127#entry127comment</comments>
      <pubDate>Mon, 31 Aug 2026 16:03:20 +0900</pubDate>
    </item>
    <item>
      <title>[머신러닝] OpenCV 시작</title>
      <link>https://hurwan.tistory.com/126</link>
      <description>&lt;h1&gt;[머신러닝] OpenCV 시작&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 OpenCV 진도를 시작함에 따라 복습할 겸 내용을 정리해보았습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 OpenCV는 &lt;code&gt;Open Source Computer Vision Library&lt;/code&gt;라는 컴퓨터가 사람의 눈처럼 이미지와 영상을 인식하고 처리할 수 있도록 돕는 오픈소스 컴퓨터 비전 라이브러리 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 이미지를 읽고 변환하는데 강점을 보이는 라이브러리입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 이미지 읽기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 &lt;code&gt;OpenCV&lt;/code&gt;는 이미지를 읽는 기능을 제공합니다.&lt;/p&gt;
&lt;pre class=&quot;haskell&quot;&gt;&lt;code&gt;import cv2

img = cv.imread(&quot;경로_문자열.png&quot;)

print(type(img))
# &amp;lt;class 'numpy.ndarray'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 로딩 옵션으로 다음과 같이 설정 또한 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;# 컬러 이미지 로드
cv2.imread(path, cv2.IMREAD_COLOR)

# 흑백 이미지 로드 ([H, W)
cv2.imread(path, cv2.IMREAD_GRAYSCALE)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배열 순서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 반환값은 기본적으로 Numpy 배열로 컬러 이미지면 보통 &lt;code&gt;[H, W, C]&lt;/code&gt; 형태로 반환되게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 특징은 PyTorch의 &lt;code&gt;[C, H, W]&lt;/code&gt; 형태와 대조되어 향후에는&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;tensor = torch.from_numpy(img)
tensor = tensor.permute(2, 0, 1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과 같은 코드가 나올 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;BGR&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenCV에서 채널의 기본 순서는 &lt;code&gt;{ 0: B, 1: G, 2: R }&lt;/code&gt; 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 익숙한 순서는 &lt;code&gt;R G B&lt;/code&gt; 순서이기 때문에 다음과 같은 방식으로 순서를 변환하여 시각화 할 때 사진이 이상하게 나오는 것을 막을 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;rgb = cv2.cvtColor(
  img,
  cv2.COLOR_BGR2RGB
)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;dtype&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 보통 이미지의 경우에는 &lt;code&gt;dtype=uint8&lt;/code&gt;이며 픽셀 범위 &lt;code&gt;0~255&lt;/code&gt;를 표현하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PyTorch 모델에 넣을 때는 보통 &lt;code&gt;uint8&lt;/code&gt;에서 &lt;code&gt;float32&lt;/code&gt;타입으로 변환한 뒤 다시 &lt;code&gt;0~1&lt;/code&gt;로 정규화 변환을 하게 됩니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;img = img.astype(&quot;float32&quot;) / 255.0&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;imread()&lt;/code&gt; 실패&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenCV는 &lt;code&gt;imread(path)&lt;/code&gt;에 이미지가 없는 경우, 경우에 따라 예외 대신 &lt;code&gt;None&lt;/code&gt; 값을 반환하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음과 같이 유효성 검사를 하는 것도 좋은 방법입니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;img = cv2.imread(&quot;???.jpg&quot;)

if img is None:
  print(&quot;image not exists&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 이미지 띄우기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OepnCV는 &lt;code&gt;imshow(panel_name, img_ndarray)&lt;/code&gt;를 통해 화면을 그리는 기능을 제공해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 띄워진 이미지 창은 프로세스의 종료와 함께 빠르게 종료될 수 있기 때문에 보통 키 입력을 기다리는 &lt;code&gt;waitKey()&lt;/code&gt; 메서드와 함께 쓰이는 경우가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;waitKey()&lt;/code&gt;의 인자로는 정수가 들어가며 &lt;code&gt;0&lt;/code&gt;을 넣을 시에는 무한정으로 키 입력을 대기하며 &lt;code&gt;1&lt;/code&gt; 이상을 넣을시에는 &lt;code&gt;N ms&lt;/code&gt;동안 키 입력을 기다리고 다음 코드를 진행하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;waitKey&lt;/code&gt;의 반환값은 눌린 키 정보를 정수로 반환해줍니다. 이를 이용하여 &lt;code&gt;waitKey(0) == 27&lt;/code&gt;과 같이 사용할 수 있으며 조금 더 보기 쉽게 하려면 &lt;code&gt;waitKey(0) &amp;amp; 0xFF == ord(&quot;q&quot;)&lt;/code&gt; 패턴을 이용할 수 있습니다. (&lt;code&gt;0xFF&lt;/code&gt;는 하위 8비트만 보기 위한 값입니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;destroyAllWindows()&lt;/code&gt;를 이용하여 OpenCV가 만든 GUI 창을 모두 닫을 수 있습니다. 특정 창만 제거하기 위해선 &lt;code&gt;cv2.destroyWindow(&quot;panel_name&quot;)&lt;/code&gt;를 이용할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 영상 읽기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenCV에서는 영상 읽기 또한 제공하여 다음과 같은 코드를 이용하여 영상 매체 정보를 가져올 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;# 파일 읽어오기
cap = cv2.VideoCapture(&quot;video.mp4&quot;)

# 웹 캠 읽어오기 (스트림 객체 번호)
cap = cv2.VideoCapture(0)

if not cap.isOpened():
  print(&quot;video not detected&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이후에는 &lt;code&gt;cap.read()&lt;/code&gt;를 이용하여 &lt;code&gt;ret, frame&lt;/code&gt; 값을 받을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ret&lt;/code&gt;는 &lt;code&gt;bool&lt;/code&gt; 타입의 읽기 성공 여부 값이며 &lt;code&gt;frame&lt;/code&gt;는 실제 한 프레임의 이미지 Numpy 배열입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ret&lt;/code&gt;는 마지막 영상의 마지막 또는 카메라 프레임을 읽는 것에 실패하게 된다면 &lt;code&gt;False&lt;/code&gt;를 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 보통 &lt;code&gt;if not ret: brerak&lt;/code&gt; &lt;code&gt;cap.read()&lt;/code&gt; 아래에 넣습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 마지막에 카메라를 잡은 경우에, 카메라를 계속 점유하지 않기 위해서 &lt;code&gt;cap.release()&lt;/code&gt;를 작성할 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 &lt;code&gt;cap.get(cv2.CAP_*)&lt;/code&gt; 을 이용하여 &lt;code&gt;fps&lt;/code&gt;, &lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt;, &lt;code&gt;frame_count&lt;/code&gt; 등을 꺼낼 수 있습니다.&lt;/p&gt;</description>
      <category>머신러닝</category>
      <author>hurwan</author>
      <guid isPermaLink="true">https://hurwan.tistory.com/126</guid>
      <comments>https://hurwan.tistory.com/126#entry126comment</comments>
      <pubDate>Mon, 31 Aug 2026 08:36:32 +0900</pubDate>
    </item>
    <item>
      <title>[랭체인 중간프로젝트] 서버 구조 및 DB, 워커, 인터페이스 준비</title>
      <link>https://hurwan.tistory.com/125</link>
      <description>&lt;h1&gt;[랭체인 중간프로젝트] 서버 구조 및 DB, 워커, 인터페이스 준비&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그 - 2026-08-30 10:44:26&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어제까지 사전 공부를 하였던 &lt;code&gt;TypeScript&lt;/code&gt;, &lt;code&gt;Python Worker&lt;/code&gt; &amp;amp; &lt;code&gt;프로세스/FD/파이프 개념&lt;/code&gt;, &lt;code&gt;PostGIS&lt;/code&gt; 등을 이용하여 이제 프로젝트 구조를 대강 잡아보기 시작하려 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;목차&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#section-1&quot;&gt;현재 상태 복기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#section-2&quot;&gt;Docker 환경 준비&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#section-2-1&quot;&gt;Docker Compose 문법 간단히&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#section-2-2&quot;&gt;Docker Compose 대상 설정&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#section-2-3&quot;&gt;Docker Compose 먼저 작성해보기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#section-2-4&quot;&gt;Dockerfile 작성&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#section-3&quot;&gt;Python 서버 이미지 생성&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#section-4&quot;&gt;Node 서버 이미지 생성&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#section-5&quot;&gt;PostGIS 이미지 테스트&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#section-6&quot;&gt;Docker Compose로 묶어주기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#section-7&quot;&gt;다음으로 할일&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;section-1&quot; data-ke-size=&quot;size26&quot;&gt;1. 현재 상태 복기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 어제 문맥을 기억해내기 위해 &lt;code&gt;npm list&lt;/code&gt;를 찍어보았습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;PS C:\LANG_CHAIN_2026\MidProject\test\05_HeoWan\ServerArchitecture\test_01&amp;gt; npm list

runstop-server-design@1.0.0 C:\LANG_CHAIN_2026\MidProject\test\05_HeoWan\ServerArchitecture\test_01
├── @types/bcrypt@6.0.0
├── @types/express@5.0.6
├── @types/jsonwebtoken@9.0.10
├── @types/node@26.4.0
├── @types/pg@8.23.1
├── bcrypt@6.0.0
├── cross-env@10.1.0
├── dotenv@17.4.2
├── express@5.2.1
├── helmet@8.3.0
├── jsonwebtoken@9.0.3
├── pg@8.23.0
├── pino-http@11.0.0
├── pino@10.3.1
├── tsx@4.23.12
├── typescript@7.0.2
└── zod@4.5.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 깔려져 있는 것은 기억이 났습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 환경변수를 먼저 만들고, docker 및 서버를 한번 실행시킨 뒤 돌아가는 형태를 기준으로 늘려나가는 것이 합리적이라 이전에 맞아본 기억을 바탕으로 판단하여 일단 &lt;code&gt;.env*&lt;/code&gt;, &lt;code&gt;Dockerfile&lt;/code&gt;, &lt;code&gt;docker-compose&lt;/code&gt;, &lt;code&gt;db-init&lt;/code&gt;, &lt;code&gt;import&lt;/code&gt;, &lt;code&gt;seed&lt;/code&gt;, &lt;code&gt;migration&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;.env*&lt;/code&gt; 파일의 경우에는 &lt;code&gt;docker-compose.yml&lt;/code&gt;에서도 사용해야 하기 때문에 프로젝트 루트 레벨에 &lt;code&gt;docker-compose.yml&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;961&quot; data-origin-height=&quot;702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kCURF/dJMcafA6Bf4/SjMMG1VEV0aCvTzWB5v3kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kCURF/dJMcafA6Bf4/SjMMG1VEV0aCvTzWB5v3kK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kCURF/dJMcafA6Bf4/SjMMG1VEV0aCvTzWB5v3kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkCURF%2FdJMcafA6Bf4%2FSjMMG1VEV0aCvTzWB5v3kK%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;961&quot; height=&quot;702&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;961&quot; data-origin-height=&quot;702&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;code&gt;.env.example&lt;/code&gt;은 실제로 &lt;code&gt;.env&lt;/code&gt;에 쓰이진 않고, 다른 &lt;code&gt;NODE_ENV&lt;/code&gt;를 가지는 환경의 템플릿 정도로 사용하였으며 이후 &lt;code&gt;Dockerfile&lt;/code&gt;을 이용하여 &lt;code&gt;Node&lt;/code&gt;, &lt;code&gt;Python Worker&lt;/code&gt;, &lt;code&gt;React Native&lt;/code&gt;를 빌드하기 위해 사용할 예정입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;section-2&quot; data-ke-size=&quot;size26&quot;&gt;2. Docker 환경 준비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;section-2-1&quot; data-ke-size=&quot;size23&quot;&gt;Docker Compose 문법 간단히&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 &lt;code&gt;docker-compose&lt;/code&gt;의 환경 변수는 표기 및 사용법이 살짝 햇갈리기 때문에 미리 정리하고 가면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;${VAR}&lt;/code&gt;: Docker Compose가 &lt;code&gt;docker compose up&lt;/code&gt; 하기 전에 해석하여 치환됩니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$${VAR}&lt;/code&gt;: Compose가 &lt;code&gt;$&lt;/code&gt; 하나를 이스케이프 하며 이후 컨테이너 &lt;code&gt;shell&lt;/code&gt;에서 사용합니다. (컨테이너 내부 환경변수 참조)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;${{SECRETS.?}}&lt;/code&gt;: GitHub Actions에서 CI workflow 실행중 사용&lt;/li&gt;
&lt;li&gt;&lt;code&gt;environment&lt;/code&gt;: Docker Compose에서 컨테이너 실행 환경변수를 주입해줍니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;env_file&lt;/code&gt;: Docker Compose에서 파일의 env를 컨테이너에 주입해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 스크립트를 보면&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;services:
  api:
    image: my-api:${IMAGE_TAG}
    environment:
      NODE_ENV: ${NODE_ENV}
      DB_HOST: ${DB_HOST}
    command: sh -c 'echo $${NODE_ENV}'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;와 같이 사용될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;${}&lt;/code&gt;는 이미 넣어져서 실행되며 &lt;code&gt;$${NODE_ENV}&lt;/code&gt;는 컨테이너 내부의 &lt;code&gt;shell&lt;/code&gt;에서 환경변수를 참조하는 형태로 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특수하게 &lt;code&gt;${{}}&lt;/code&gt;를 통해 GitHub Actions에 쓸 수 있지만 일단 현재 상황과 관계 없으니 넘어가겠습니다.&lt;/p&gt;
&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;environment&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;리스트&lt;/code&gt;, &lt;code&gt;[]&lt;/code&gt;, &lt;code&gt;key-value&lt;/code&gt; 형식으로 사용할 수 있으며 다음과 같이 작성할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;-&lt;/code&gt; 리스트 방식 - Block Sequence&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;services:
  api:
    environment:
      - NODE_ENV=production
      - PORT=3000&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;[]&lt;/code&gt; 리스트 방식 - Flow Sequence&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;services:
  api:
    environment: [NODE_ENV=production, PORT=3000]&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;key-value&lt;/code&gt; 방식 - Mapping&lt;/h4&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;services:
  api:
    environment:
      NODE_ENV: production
      PORT: 3000&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 방식 3가지를 모두 지원하는 방식이 있으며 특정 요소만 지원하는 방식이 있으니 현재는 이런 방식의 나열법이 있는 것만 이해하는 것이 좋습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;section-2-2&quot; data-ke-size=&quot;size23&quot;&gt;Docker Compose 대상 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native의 경우에는 설치형이기 때문에 따로 &lt;code&gt;docker-compose&lt;/code&gt;가 필요 없다고 생각이 들어서 그냥 &lt;code&gt;Node&lt;/code&gt;, &lt;code&gt;Python Worker&lt;/code&gt;, &lt;code&gt;Database&lt;/code&gt;만 &lt;code&gt;compose&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;routeros&quot;&gt;&lt;code&gt;MidProject/
├─ frontend/                 # React Native
│
├─ backend/                  # Node.js Server
│  ├─ Dockerfile
│  ├─ src/
│  └─ package.json
│
├─ routing-worker/           # Python Worker
│  ├─ Dockerfile
│  ├─ src/
│  │  ├─ worker.py
│  │  ├─ algorithm/          # 경로 후보 생성
│  │  ├─ features/           # 경사/시설 등 계산
│  │  └─ ranking/            # AI 후보 선택
│  ├─ models/
│  └─ requirements.txt
│
├─ data/                     # CSV / SHP 등 원본 데이터
│
├─ infra/
│  └─ db/
│     ├─ migrations/
│     ├─ seeds/
│     └─ imports/
│
├─ docker-compose.yml
└─ README.md&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;와 같은 형태입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2026-08-30_dir.png&quot; data-origin-width=&quot;1191&quot; data-origin-height=&quot;657&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JleEm/dJMcafA6Bga/IR7eY3ozRqcuYP2FEPt3PK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JleEm/dJMcafA6Bga/IR7eY3ozRqcuYP2FEPt3PK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JleEm/dJMcafA6Bga/IR7eY3ozRqcuYP2FEPt3PK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJleEm%2FdJMcafA6Bga%2FIR7eY3ozRqcuYP2FEPt3PK%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;1191&quot; height=&quot;657&quot; data-filename=&quot;2026-08-30_dir.png&quot; data-origin-width=&quot;1191&quot; data-origin-height=&quot;657&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;section-2-3&quot; data-ke-size=&quot;size23&quot;&gt;Docker Compose 먼저 작성해보기&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;2026-08-30 12:40:01&lt;/code&gt;&lt;br /&gt;우선 계속 고민만 하고있으면 진행이 안되니 먼저 &lt;code&gt;docker-compose&lt;/code&gt;를 작성해보겠습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;# MidProject/docker-compose.yml

  # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 #     다음과 같은 구조를 가지고 있다는 가정 하에 동작합니다.    #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#  MidProject/                                            #
#  ├─ .env.development                                    #
#  ├─ .env.test                                           #
#  ├─ .env.production                                     #
#  ├─ docker-compose.yml                                  #
#  │                                                      #
#  ├─ backend/                                            #
#  │  └─ Dockerfile                                       #
#  │                                                      #
#  └─ routing-worker/                                     #
#     └─ Dockerfile                                       #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

services:
  backend: # Node 서버
    build: # `./backend/Dockerfile` 을 빌드합니다.
      context: ./backend
    env_file:
      - .env.development
    ports:
      - &quot;3000:3000&quot;
    depends_on: # DB와 워커 FastAPI가 먼저 시작 후 실행 (healthcheck를 이용해서 서버 정상동작까지 가능)
      - database
      - routing-worker

  routing-worker:
    build: # `/routing-worker/Dockerfile` 을 빌드합니다.
      context: ./routing-worker
    env_file: # 메인 폴더의 환경을 그대로 사용합니다.
      - .env.development

    # ports는 워커 테스트 시에만 사용
    # ports: # FastAPI의 기본 포트인 8000번을 호스트의 포트와 연결시킵니다.
    #   - &quot;8000:8000&quot;

  database:
    # PostGIS Extention이 붙어있는 17-3.5 버전을 거져옵니다.
    image: postgis/postgis:17-3.5
    env_file: # 동일한 파일에서 환경변수를 관리해줍니다.
      - .env.development
    ports:
      - &quot;5432:5432&quot;
    volumes: # 컨테이너 데비안의 /var/lib/postgrersql/data에 있는 데이터를 볼륨으로 영속화합니다.
      - postgres-data:/var/lib/postgresql/data

volumes:
  postgres-data:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;환경변수 템플릿은 다음과 같이 작성하였습니다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;# MidProject/.env.example

# # # # # # # # # # # # # # # # # # # # #
#        Node Server [API Server]       #
# # # # # # # # # # # # # # # # # # # # #

# 실행 환경
#   - 개발: development
#   - 테스트: test
#   - 배포: production
NODE_ENV=development

# Node 서버 포트
PORT=3000

# JWT 시크릿
JWT_SECRET=



# # # # # # # # # # # # # # # # # # # # #
#        Python Worker [FastAPI]        #
# # # # # # # # # # # # # # # # # # # # #

# Docker Compose 내부에서 localhost 대신 service 이름을 hostname로 설정
# &amp;gt; compose 내부에 있는 docker-network를 사용
WORKER_URL=http://routing-worker:8000

# 로컬용 WORKER_URL
# WORKER_URL=http://localhost:8000



# # # # # # # # # # # # # # # # # # # # #
#               데이터베이스              #
# # # # # # # # # # # # # # # # # # # # #

# Docker Compose 내부에서 서버/워커 -&amp;gt; database:5432 로 통신
DATABASE_URL=postgresql://postgres:1234@database:5432/runstop

# 로컬용 DATABASE_URL
# DATABASE_URL=postgresql://postgres:postgres@localhost:5432/runstop

POSTGRES_DB=runstop
POSTGRES_USER=postgres
POSTGRES_PASSWORD=1234&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;section-2-4&quot; data-ke-size=&quot;size23&quot;&gt;Dockerfile 작성&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;2026-08-30 13:06:24&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 &lt;code&gt;backend/&lt;/code&gt;와 &lt;code&gt;routing-worker/&lt;/code&gt;을 빌드하기 위한 &lt;code&gt;Dockerfile&lt;/code&gt;을 작성하겠습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;section-3&quot; data-ke-size=&quot;size26&quot;&gt;3. Python 서버 이미지 생성&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;2026-08-30 14:37:51&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 도커 이미지 빌드부터 실행까지 테스트하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1444&quot; data-origin-height=&quot;1476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUSb13/dJMb991Y8uK/XZplKqHAnl7x5eY0KIMCgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUSb13/dJMb991Y8uK/XZplKqHAnl7x5eY0KIMCgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUSb13/dJMb991Y8uK/XZplKqHAnl7x5eY0KIMCgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUSb13%2FdJMb991Y8uK%2FXZplKqHAnl7x5eY0KIMCgK%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;1444&quot; height=&quot;1476&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1444&quot; data-origin-height=&quot;1476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;section-4&quot; data-ke-size=&quot;size26&quot;&gt;4. Node 서버 이미지 생성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드서버 또한 이미지 생성을 테스트해주었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image-2.png&quot; data-origin-width=&quot;2550&quot; data-origin-height=&quot;1483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Gm9wm/dJMcab6xGzQ/ChHd48VZGfCEIgcoLjjj4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Gm9wm/dJMcab6xGzQ/ChHd48VZGfCEIgcoLjjj4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Gm9wm/dJMcab6xGzQ/ChHd48VZGfCEIgcoLjjj4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGm9wm%2FdJMcab6xGzQ%2FChHd48VZGfCEIgcoLjjj4K%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;2550&quot; height=&quot;1483&quot; data-filename=&quot;image-2.png&quot; data-origin-width=&quot;2550&quot; data-origin-height=&quot;1483&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;section-5&quot; data-ke-size=&quot;size26&quot;&gt;5. PostGIS 이미지 테스트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;postgis/postgis:17-3.5&lt;/code&gt; 도커 이미지를 테스트하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image-3.png&quot; data-origin-width=&quot;2130&quot; data-origin-height=&quot;883&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQQ1c2/dJMcab6xGz0/3Sx5XuuPxlqG6HD3se0CkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQQ1c2/dJMcab6xGz0/3Sx5XuuPxlqG6HD3se0CkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQQ1c2/dJMcab6xGz0/3Sx5XuuPxlqG6HD3se0CkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQQ1c2%2FdJMcab6xGz0%2F3Sx5XuuPxlqG6HD3se0CkK%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;2130&quot; height=&quot;883&quot; data-filename=&quot;image-3.png&quot; data-origin-width=&quot;2130&quot; data-origin-height=&quot;883&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;section-6&quot; data-ke-size=&quot;size26&quot;&gt;6. Docker Compose로 묶어주기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각 테스트했던 &lt;code&gt;Node&lt;/code&gt;, &lt;code&gt;Python Worker&lt;/code&gt;, &lt;code&gt;PostGIS&lt;/code&gt;를 &lt;code&gt;docker-compose.yml&lt;/code&gt;을 통해 묶어주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 올라가집니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image-4.png&quot; data-origin-width=&quot;2559&quot; data-origin-height=&quot;1522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bX44Yj/dJMcab6xGz3/LII9oT81PWnudogNYmkSY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bX44Yj/dJMcab6xGz3/LII9oT81PWnudogNYmkSY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bX44Yj/dJMcab6xGz3/LII9oT81PWnudogNYmkSY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbX44Yj%2FdJMcab6xGz3%2FLII9oT81PWnudogNYmkSY0%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;2559&quot; height=&quot;1522&quot; data-filename=&quot;image-4.png&quot; data-origin-width=&quot;2559&quot; data-origin-height=&quot;1522&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;section-7&quot; data-ke-size=&quot;size26&quot;&gt;7. 다음으로 할일&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재까지는 각각의 서버와 데이터베이스를 Docker 이미지로 만들고, &lt;code&gt;docker-compose&lt;/code&gt;를 통해 같이 실행할 수 있는 상태까지만 만들어주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에는 다음 순서로 진행하려 합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Node / Python 기본 구조&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Health 기준 최소 정의&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;env 규격 정리&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Docker Compose 통합&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DB schema / migration / seed / import&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DTO / interface 고정&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Node &amp;harr; Python &amp;harr; DB 통합&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;외부 API Adapter&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;테스트 CI&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터베이스 레포지토리 계층 작업&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는 일단 전체 기능을 구현하기보다는 &lt;code&gt;Node&lt;/code&gt;, &lt;code&gt;Python Worker&lt;/code&gt;, &lt;code&gt;Database&lt;/code&gt;가 정상적으로 실행되고 서로 연결될 수 있는 최소 구조부터 만들어나갈 생각입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;뭔가 시간을 약 5시간정도 쓴거같은데 이제 시작이 된 느낌이랄까요?&lt;/span&gt;&lt;/blockquote&gt;</description>
      <category>서비스</category>
      <author>hurwan</author>
      <guid isPermaLink="true">https://hurwan.tistory.com/125</guid>
      <comments>https://hurwan.tistory.com/125#entry125comment</comments>
      <pubDate>Sun, 30 Aug 2026 15:36:21 +0900</pubDate>
    </item>
    <item>
      <title>[Computer Science] 프로세스의 스케줄링과 CPU 모드 개요</title>
      <link>https://hurwan.tistory.com/124</link>
      <description>&lt;h1&gt;[Computer Science] 프로세스의스케줄링과 CPU 모드 개요&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 &lt;a href=&quot;https://hurwan.tistory.com/118&quot;&gt;자식 프로세스와 통신&lt;/a&gt;에 대한 글에서 이어집니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;병렬성과 동시성의 구분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터 안에서는 CPU코어가 여러개 존재하는 것을 알 수 있습니다.&lt;br /&gt;&lt;br /&gt;아래의 사진에서는 현재 컴퓨터의 cpu 코어 개수, L1, L2, L3 캐시 등을 확인할 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;309&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/esvGiJ/dJMcacqUVqj/KikcOTvkUkuoiVrrLgbaM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/esvGiJ/dJMcacqUVqj/KikcOTvkUkuoiVrrLgbaM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/esvGiJ/dJMcacqUVqj/KikcOTvkUkuoiVrrLgbaM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FesvGiJ%2FdJMcacqUVqj%2FKikcOTvkUkuoiVrrLgbaM1%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;652&quot; height=&quot;309&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;309&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 CPU 코어 1개는 동시에 여러개의 프로세스를 작업대상으로 둘 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;이때 프로세스가 4개가 등록되어있어도 하나의 코어는 하나의 프로세스만을 진행시킬 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;이를 프로세스의 &lt;b&gt;동시성&lt;/b&gt;이라고 불립니다.&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이러한 여러 코어가 동시에 동일한 시각에 다른 프로세스들을 실행시키고 있다면 그것을 &lt;b&gt;병렬성&lt;/b&gt;이라고 부르게 됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로세스의 CPU 점유&lt;/h2&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;RUNNING&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SLEEP&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;BLOCK&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RUNABLE&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 자열스럽게 흐름이&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;CPU가 실행중인 프로세스&lt;/li&gt;
&lt;li&gt;CPU 사용 시간이 끝난 프로세스&lt;/li&gt;
&lt;li&gt;다시 실행할 준비가 된 프로세스&lt;/li&gt;
&lt;li&gt;다시 CPU 할당 후 실행된 프로세스&lt;/li&gt;
&lt;/ol&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;I/O&lt;/b&gt;작업 시에는 &lt;code&gt;read()&lt;/code&gt;등과 같은 작업 시에 CPU를 통해서 해결할 수 없는 대기 작업이 있어야하는 경우, &lt;code&gt;SLEEP&lt;/code&gt; 또는 &lt;code&gt;BLOCKED&lt;/code&gt; 상태가 되어 대기가 끝날때까지 다음 실행을 하지 못하는 상태가 됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;스케줄러&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU에는 &lt;code&gt;Scheduler&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;time interrupt&lt;/code&gt;등과 같은 인터럽트가 발생하게 된다면 CPU가 커널모드로 변경되어 스케줄링을 판단하며 진행중인 프로세스 상태를 프로세스 메모리에 저장한 뒤 다음 프로세스 상태를 레지스터로 가져오게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 OS가 프로세스의 실행 상태를 강제로 관리하는 것을 &lt;b&gt;preemptive multitasking&lt;/b&gt;(선점형 멀티태스킹)이라고 부릅니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로세스를 예시로 보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 파이썬에서 &lt;code&gt;sys.stdin.readline()&lt;/code&gt;를 호출하게 된다면 파이썬 프로세스는 자신의 표준 입력창 버퍼를 확인하려 하게 됩니다. 내부적으로는 &lt;code&gt;read(fd, buffer, size)&lt;/code&gt;와 같은 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;하지만 이때 Pipe가 비어있는 상황이라면 파이썬은 &lt;code&gt;None&lt;/code&gt; 등을 반환하는 것이 아닌 &lt;code&gt;\n&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;이때 파이썬이 계속 CPU를 이용하며 데이터가 있는지 확인을 하면 CPU의 손실이 일어나게 됩니다. 이를 &lt;b&gt;busy waiting&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;위와 같은 방식보다는 보통 반대로 OS가 파이썬 프로세스에 데이터가 들어오기 전까지 SLEEP 상태로 변경시키게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 CPU는 다른 프로세스에서 사용되게 됩니다.&lt;/p&gt;
&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하는 것이 파이프 관련 커널 구조 안에 &lt;code&gt;Wait Queue&lt;/code&gt;와 같은 해당 파이프의 데이터를 기다리고 있는 &lt;code&gt;task&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;wait queue&lt;/code&gt;에 존재하는 프로세스(파이썬)를 깨워서 파이썬의 상태를 &lt;code&gt;RUNNABLE&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;여기에서 구분이 생기는데 프로세스 관점에서 I/O 작업을 2가지로 나눌 수 있습니다.&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;BLOCKING&lt;/b&gt;: 해당 요청에 대한 응답이 생길때까지 SLEEP 되어야하는 IO작업&lt;/li&gt;
&lt;li&gt;&lt;b&gt;NON-BLOCKING&lt;/b&gt;: 해당 요청에 대한 응답이 생기지 않아도 일단 진행하며 나중에 사용자 쪽에서 이벤트 또는 직접 확인 을 하여 결과물을 사용하는 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 &lt;code&gt;write()&lt;/code&gt; 작업의 경우에도 버퍼가 가득 찼을때 어떻게 할 것인지를 &lt;code&gt;BLOCKING&lt;/code&gt;, &lt;code&gt;NON-BLOCKING&lt;/code&gt;를 기준으로 나눌 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CPU의 사용자 모드와 커널 모드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU가 프로그램을 실행한다고 할 때 사용자 프로그램에 CPU의 모든 권한을 줄 수는 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 메모리, 디스크, 네트워크, CPU 점유 등과 같은 조작을 모르는 사용자의 프로그램을 대상으로 마음대로 가능하게 할 수 없기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해 CPU 자체가 권한 수준을 지원하도록 설계되어있습니다.&lt;/p&gt;
&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;x86&lt;/code&gt; cpu 아키텍처에는 &lt;code&gt;privilege ring&lt;/code&gt;(보호 링)이라는 개념으로 &lt;code&gt;Ring 3&lt;/code&gt;은 사용자모드, &lt;code&gt;Ring 0&lt;/code&gt;은 커널모드를 주로 사용한다고 합니다. (1, 2는 주로 사용하지 않는 것으로 알고있습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 사용자 모드(&lt;code&gt;Ring 3&lt;/code&gt;)에서 OS에서 제공하는 시스템 콜 명령어를 이용하여 커널에 특정 작업을 요청하게 되면 커널쪽에서 해당 작업을 대신 안전하게 처리해주는 방식입니다. (은닉화)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나중에는 한번 직접 시스템 콜을 이용한 특정 OS 프로그램을 만드는 것도 재밌을 것 같습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CPU가 &lt;code&gt;Ring 0&lt;/code&gt;, &lt;code&gt;Ring 3&lt;/code&gt;을 전환하는 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 CPU가 커널모드로 진입하는 과정은 &lt;b&gt;Exception&lt;/b&gt;, &lt;b&gt;Interrupt&lt;/b&gt;, &lt;b&gt;System Call&lt;/b&gt; 등과 연결되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 보통 이벤트라고 부르기도 하며 시스템 콜과 함께 의도적으로 커널에 진입하는 경우입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;System Call: 사용자 영역에서 의도적으로 커널 모드를 요청하는 것&lt;/li&gt;
&lt;li&gt;Interrupt: 외부 장치 등에서 CPU에 이벤트가 발생했음을 알리는 것 (키보드 입력, 네트워크 패킷, 타이머 등)&lt;/li&gt;
&lt;li&gt;Exception: CPU가 현재 명령 실행 중 문제 또는 특수 상황 감지 (&lt;code&gt;div 0&lt;/code&gt;, &lt;code&gt;Page Fault&lt;/code&gt;)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Fault: 명령을 완전히 끝내기 전에 발생시킨 후 다시 원래 명령 실행 (&lt;code&gt;Page Fault&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Trap: 실행하던 명령을 끝내고 커널로 넘어가기 (&lt;code&gt;breakpoint&lt;/code&gt;, &lt;code&gt;debugging&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Abort: 심각한 CPU/시스템 오류로 정확한 재시작이 어려운 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;마무리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매일 아침마다 이런식으로 CS 기초를 알아보는 시간을 가지는데 어느정도 아는 내용도 있고, 모르던 내용도 있습니다.&lt;br /&gt;&lt;br /&gt;하지만 가장 좋은 부분은 머릿속에서 약간씩 희미해지던 기억이 다시 선명해지는 기분을 느끼는 것 같습니다.&lt;br /&gt;&lt;br /&gt;또한 시스템 콜, 인터럽트, 페이지 폴트, 블로킹 등의 개념은 계속 더 파먹고 싶은 부분이여서 매일 이런 작업을 해나갈 생각입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음번에는 스케줄링 및 동시성, 경쟁상태 관리에 대해서 정리를 해보겠습니다.&lt;br /&gt;&lt;br /&gt;그리고 요즘 개념만 글로 쓰다보니까 약간 머릿속 구체화가 덜 되기도 하는거같아서 한번 기회를 잡아 더 구체적으로 그려낼 수 있게 실제로 코드를 봐야겠다는 생각도 들었습니다.&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>CS 기초</category>
      <author>hurwan</author>
      <guid isPermaLink="true">https://hurwan.tistory.com/124</guid>
      <comments>https://hurwan.tistory.com/124#entry124comment</comments>
      <pubDate>Sun, 30 Aug 2026 12:32:57 +0900</pubDate>
    </item>
    <item>
      <title>[랭체인 중간프로젝트] 서버 아키텍처 준비  - 이식성을 고려한 설계 01</title>
      <link>https://hurwan.tistory.com/123</link>
      <description>&lt;h1&gt;[랭체인 중간프로젝트] 서버 아키텍처 준비 - 이식성을 고려한 설계&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #e6f5ff; color: #333333; text-align: start;&quot;&gt;2026-08-29 19:37:32&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;목차&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;현재 생각중인 기능&lt;/li&gt;
&lt;li&gt;공부하면서 좋은 설계법 고려&lt;/li&gt;
&lt;li&gt;환경변수&lt;/li&gt;
&lt;li&gt;마이그레이션&lt;/li&gt;
&lt;li&gt;멱등성&lt;/li&gt;
&lt;li&gt;healthcheck&lt;/li&gt;
&lt;li&gt;파이썬 워커 오토스케일링&lt;/li&gt;
&lt;li&gt;DTO&lt;/li&gt;
&lt;li&gt;외부 API 및 어댑터 규격&lt;/li&gt;
&lt;li&gt;사용할 패키지&lt;/li&gt;
&lt;li&gt;노드 버전 및 npm&lt;/li&gt;
&lt;li&gt;&lt;code&gt;package.json&lt;/code&gt;과 &lt;code&gt;tsconfig.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tsconfig.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;package.json&lt;/code&gt;의 script 작성&lt;/li&gt;
&lt;li&gt;진짜 패키지 설치&lt;/li&gt;
&lt;li&gt;중간 마무리&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 &lt;code&gt;08/31&lt;/code&gt;까지 팀원들이 작업을 쉽게 할 수 있게 서버쪽 아키텍처를 설계하는 기간을 가지려 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;현재 생각중인 기능&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 다음과 같은 기능을 미리 생각중입니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;구분&lt;/td&gt;
&lt;td&gt;미리 생각중인 내용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;데이터베이스&lt;/td&gt;
&lt;td&gt;필요한 &lt;code&gt;.csv&lt;/code&gt; 파일 &lt;code&gt;import&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;데이터베이스&lt;/td&gt;
&lt;td&gt;&lt;code&gt;service&lt;/code&gt; 스키마에 미리 넣기로 한 요소들을 넣습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;서비스 자체 인증인가 기능&lt;/td&gt;
&lt;td&gt;jwt 토큰, bcrypt 해싱, 로테이션 키, solapi/coolsms 전화번호 인증, 사용자 정보 받기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;로깅 및 전역 예외/에러 catch 기능&lt;/td&gt;
&lt;td&gt;팀원이 코드를 짜는 공간을 래퍼로 감싸서 로깅 및 예외를 기본적으로 잡을 수 있게 처리하고싶음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;도커 자동화&lt;/td&gt;
&lt;td&gt;Dockerfile과 docker-compose를 이용하여 미리 배포가 되는 상태를 기반으로 늘려나가고싶음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;마이그레이션 및 &lt;code&gt;import&lt;/code&gt;, &lt;code&gt;seed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;방법을 몰라서 공부해야하며 node/python 중 선택을 하거나 양쪽 모두 만들어야함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;레포지토리 계층&lt;/td&gt;
&lt;td&gt;GIS 함수 사용을 위한 인터페이스와 같은 계층을 미리 만들어두기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;인터페이스 및 dto&lt;/td&gt;
&lt;td&gt;TypeScript 기준으로 먼저 만들어두기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;개발용 스크립트 모두 짜놓기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;node, npm&lt;/td&gt;
&lt;td&gt;버전 통일화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;worker&lt;/td&gt;
&lt;td&gt;요청 수에 따라 늘려야할지 고민중&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tmap/gpt_api&lt;/td&gt;
&lt;td&gt;어느정도 테스트 해야하긴 하지만 위의 것들이 선행되어야 할 예정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;aws/git actions&lt;/td&gt;
&lt;td&gt;또는 무언가 중에서 뭘 써야할지 고민중&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터베이스&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;필요한 &lt;code&gt;.csv&lt;/code&gt; 파일 &lt;code&gt;import&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;service&lt;/code&gt; 스키마에 미리 넣기로 한 요소들을 넣습니다. (아래는 현재 임시 테이블 명 입니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt; 임시 테이블 명 &lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt; 설명 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;users&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;사용자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user_profiles&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;사용자 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;inquery&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;문의&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;point_bookmark&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;경유지 즐겨찾기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;running_goal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;사용자 목표&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;route_requests&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;사용자의 러닝 코스 요청&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;running_session&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;사용자의 러닝 상태 기록&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;running_trackpoint&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;특정 주기별로 사용자 gps 및 상태 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;route_recommendation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;경로 후보 추린거 3개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;route_point&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;경로 지점&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;route_bookmak&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;경로 즐겨찾기&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;서비스 자체 인증인가 기능&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;jwt 토큰 (React Native와 일치하는지 확인해야함)&lt;/li&gt;
&lt;li&gt;bcrypt 해싱&lt;/li&gt;
&lt;li&gt;로테이션 키 (쓰면 아키텍처가 늘어서 고민중)&lt;/li&gt;
&lt;li&gt;solapi/coolsms 전화번호 인증&lt;/li&gt;
&lt;li&gt;사용자 정보 받기 (신체정보 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;추가로 고려중인 것들&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로깅 및 전역 예외/에러 catch 기능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;팀원이 코드를 짜는 공간을 래퍼로 감싸서 로깅 및 예외를 기본적으로 잡을 수 있게 처리하고싶음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;도커 자동화 (Dockerfile과 docker-compose를 이용하여 미리 배포가 되는 상태를 기반으로 늘려나가고싶음)&lt;/li&gt;
&lt;li&gt;마이그레이션 및 &lt;code&gt;import&lt;/code&gt;, &lt;code&gt;seed&lt;/code&gt; (방법을 몰라서 공부해야하며 node/python 중 선택을 하거나 양쪽 모두 만들어야함)&lt;/li&gt;
&lt;li&gt;레포지토리 계층 미리 만들어두기 (GIS 함수 사용을 위한 인터페이스와 같은 계층)&lt;/li&gt;
&lt;li&gt;인터페이스 및 dto 먼저 만들어두기 (TypeScript)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;package.json&lt;/code&gt; 에 미리 개발용 스크립트 모두 짜놓기 (너무 과하게 말고, &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, &lt;code&gt;deploy&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt; 정도 미리 고려중이며 &lt;code&gt;lint&lt;/code&gt; 같은것도 할지 고민됨. TS 프로젝트는 거의 처음이지만 아마 tsx가 있기때문에 테스트나 개발은 문제 없을 것 같음.)&lt;/li&gt;
&lt;li&gt;node, npm 버전 통일화&lt;/li&gt;
&lt;li&gt;worker을 요청 수에 따라 늘려야할지 고민중&lt;/li&gt;
&lt;li&gt;tmap/gpt_api 는 어느정도 테스트 해야하긴 하지만 위의 것들이 선행되어야 할 예정&lt;/li&gt;
&lt;li&gt;aws/git actions 또는 무언가 중에서 뭘 써야할지 고민중&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;공부하면서 좋은 설계법 고려&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 환경변수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경변수의 경우에는 3가지로&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;환경&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;사용할 내용&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;development&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;로컬DB, 자세한 로그, 개발 API Key를 사용할 수 있을 것 같습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;test&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;테스트 전용 DB, Monk API를 통한 프론트 도움, 테스트 보안 키 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;production&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;실제 DB/API Key와 구현된 API들, 진짜 필요한 로그들을 추려서 정리할 예정입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트의 &lt;code&gt;NODE_ENV&lt;/code&gt;를 이용하여 &lt;code&gt;development&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, &lt;code&gt;production&lt;/code&gt;으로 나누어낼 생각입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 마이그레이션&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 마이그레이션은 &lt;b&gt;DDL&lt;/b&gt;을 이용한 데이터베이스 버전관리인 것은 알지만 데이터베이스를 다루는 것은 뭔가 민감한 내용인 것 같아서 일단 피하고싶긴 하지만 해야한다면 공부를 해서 하려 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 멱등성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 파일을 DB에 올리는 &lt;code&gt;import&lt;/code&gt;와 기본 데이터를 올리는 &lt;code&gt;seed&lt;/code&gt; 쪽에서 관리를 하는 방법을 알야아할 것 같은데 멱등성이 &lt;i&gt;몇번 반복해서 실행해도 데이터에 문제가 생기지 않음&lt;/i&gt; 과 같은 규칙인 것은 아는데 직접 해본적이 없어서 이것도 마이그레이션과 함께 볼 요소입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 코드로 작업을 하는 느낌이라고 찾아보았습니다.&lt;/p&gt;
&lt;table style=&quot;height: 84px;&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt; 명령어 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt; 역할 &lt;/b&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;npm run db:migrate&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;DDL 실행&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;npm run db:seed&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;npm run db:import&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;code&gt;.csv&lt;/code&gt; 데이터베이스에 업롣&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마 UNIQUE 또는 UPSERT, ON CONFLICT 등과 같은 문법을 이용하는 것이 옳을 것 같습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. healthcheck&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node 서버에 &lt;code&gt;GET /health&lt;/code&gt;, &lt;code&gt;GET /ready&lt;/code&gt;를 받아서 &lt;code&gt;server&lt;/code&gt;, &lt;code&gt;databse&lt;/code&gt;, &lt;code&gt;worker&lt;/code&gt; 등의 상태를 반환하기 위해 사용하게 될 것 같습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 파이썬 워커 오토스케일링&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 워커또한 CPU 작업인데 반해 작업이 길어질 확률 또한 존재하기 때문에 결과를 캐싱하거나 오토스케일링할 수 있는 구조를 고려해야할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;일단 현재는 단일 작업을 확인한 다음 &lt;code&gt;Worker Manager&lt;/code&gt;을 고려할 수 있을 것 같습니다.&lt;/i&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. DTO&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 당연하게 도입해야 관리가 쉬울 것으로 보이며 팀원들이 TypeScript를 몰라도 진행할 수 있도록 어느정도 함수 등을 미리 작성해 놓고 규격 또한 만들어놓을 예정입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. 외부 API 및 어댑터 규격&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 또한 &lt;code&gt;infrastructure&lt;/code&gt;을 이용하여&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tmap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;llm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;routeMaker&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등과 같이 제작할까 고민중입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8. 사용할 패키지&lt;/h3&gt;
&lt;table style=&quot;height: 348px;&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;패키지&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;생각중인 내용&lt;/b&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;express&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;typescript&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: 42px;&quot;&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;&lt;code&gt;TypeScript&lt;/code&gt; 타입이 없는 패키지&lt;/td&gt;
&lt;td style=&quot;height: 42px;&quot;&gt;&lt;code&gt;-D @types/&lt;/code&gt;를 추가하기 (&lt;code&gt;package.json&lt;/code&gt;에 &lt;code&gt;types&lt;/code&gt; 또는 &lt;code&gt;typings&lt;/code&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;zod&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;런타임 데이터 검증 및 DTO 검증&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;bcrypt&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: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;code&gt;jsonwebtoken&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;jwt&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;helmet&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;빠른 로깅 라이브러리 (console.log 대신 구조화된 로그를 남김)&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;pino&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;Express에 보안 관련 HTTP Header을 자동으로 추가 (서버 기본 보안용)&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;pg&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;postgres 통신&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;pino-http&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;pino를 HTTP 요청/응답에 붙여주는 미들웨어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;자동 로깅&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;누가 어떤 API를 호출했고 얼마나 걸렸는지를 자동 로깅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;height: 36px;&quot;&gt;&lt;code&gt;express-rate-limit&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 36px;&quot;&gt;특정 IP가 API를 너무 많이 호출하는걸 제한&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;vitest&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;TypeScript/JavaScript 테스트 프레임워크 (Jest와 비슷하지만 Vite 생태계에서 가볍고 빠른편)&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;supertest&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;Express API를 서버 포트에 열지 않고 없이 테스트 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 zod(스키마 검사), &lt;code&gt;pino[-http]&lt;/code&gt; (기본 보안), &lt;code&gt;helmet&lt;/code&gt;(로깅)을 테스트하고 나중에 여건이 되면 &lt;code&gt;express-rate-limit&lt;/code&gt;, &lt;code&gt;vitest&lt;/code&gt;, &lt;code&gt;supertest&lt;/code&gt;를 넣는 것으로 하겠습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9. 노드 버전 및 npm&lt;/h3&gt;
&lt;table style=&quot;height: 103px;&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;항목&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;버전&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;Node&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;code&gt;24.18.0&lt;/code&gt; 고정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;npm&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;code&gt;11.16.0&lt;/code&gt; 고정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10. &lt;code&gt;package.json&lt;/code&gt;과 &lt;code&gt;tsconfig.json&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체적인 과정은 아무래도&lt;/p&gt;
&lt;table style=&quot;height: 63px;&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt; 순서 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt; 내용 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;TS가 &lt;code&gt;tsconfig.json&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;JS가 &lt;code&gt;package.json&lt;/code&gt;을 이용해서 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과 같은 느낌일 것 같습니다. 한번 알아보면&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10. &lt;code&gt;package.json&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;package.json&lt;/code&gt;은 플젝트 메타데이터와 실행 규칙을 정의하는 파일입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이곳에는 Node 프로그램이 &lt;code&gt;.js&lt;/code&gt;를 실행할 대 해당 파일이 속한 package scope의 &lt;code&gt;package.json&lt;/code&gt;을 찾아 참고하여 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 &lt;code&gt;npm install&lt;/code&gt;, &lt;code&gt;npm run *&lt;/code&gt; 또한 해당 경로를 찾아 &lt;code&gt;scripts&lt;/code&gt;를 읽게됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;package.json&lt;/code&gt;에는 다음과 같은 정보가 존재합니다.&lt;/p&gt;
&lt;table style=&quot;height: 160px;&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt; 항목 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt; 설명 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;name&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;프로젝트 이름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;version&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;버전&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;main&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;type&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;Node.js의 런타임의 해석 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;scripts&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;code&gt;test&lt;/code&gt; 등과 같은 예외를 빼고는 &lt;code&gt;npm run *&lt;/code&gt;을 정의&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;dependencies&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;devDependencies&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;개발/빌드할 때 필요한 것 (npm ci --omit=dev로 제외 가능)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;11. &lt;code&gt;tsconfig.json&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;tsconfig.json&lt;/code&gt;은 TypeScript 코드를 어떤 규칙으로 검사하고 어떤 JS로 빌드할지를 정하는 파일입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 TS 코드 위치, 빌드될 JS 위치, 빌드할 타입 (&lt;code&gt;module: NodeNext&lt;/code&gt;, , &lt;code&gt;moduleResolution: NodeNext&lt;/code&gt; 또는 &lt;code&gt;module: CommonJS&lt;/code&gt;), target을 통한 JavaScipt 버전 선택, &lt;code&gt;types: [&quot;node&quot;]&lt;/code&gt;를 통한 Node 타입 사용. &lt;code&gt;strict: true&lt;/code&gt;를 통한 타입 검사 엄격하게 진행, &lt;code&gt;sourceMap: true&lt;/code&gt;를 통한 에러를 &lt;code&gt;src/&lt;/code&gt;와 같은 &lt;code&gt;rootDir&lt;/code&gt;를 기준으로 출력하게 해줍니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;12. &lt;code&gt;package.json&lt;/code&gt;의 script 작성&lt;/h3&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;scripts&quot;: {
  &quot;test&quot;: &quot;tsx-env NODE_ENV=test vitest&quot;,
  &quot;dev&quot;: &quot;cross-env NODE_ENV=development tsx watch src/server.ts&quot;,
  &quot;build&quot;: &quot;tsc&quot;,
  &quot;start&quot;: &quot;cross-env NODE_ENV=production node dist/server.js&quot;,
  &quot;typecheck&quot;: &quot;tsc --noEmit&quot;
},&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과 같이 작성하였으며 &lt;code&gt;npm run start&lt;/code&gt;는 &lt;code&gt;docker-compose&lt;/code&gt;에서 &lt;code&gt;NODE_ENV&lt;/code&gt;를 넣어주지만 일단 로컬에서도 실행할 수 있기 때문에 넣어놓았습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;13. 진짜 패키지 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 위에서 말한 패키지들중 쓸것들만 설치했습니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;npm install zod pg pino pino-http helmet express jsonwebtoken bcrypt dotenv
npm install -D typescript @types/node @types/express cross-env @types/jsonwebtoken @types/bcrypt @types/pg&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;중간 마무리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;2026-08-29 21:41:21&lt;/code&gt;: 일단 시간이 늦어 정리하고 내일 아침에 바로 서버 구조 생성, 마이그레이션, 데이터베이스, 워커 준비를 하도록 하겠습니다.&lt;/p&gt;</description>
      <category>서비스</category>
      <author>hurwan</author>
      <guid isPermaLink="true">https://hurwan.tistory.com/123</guid>
      <comments>https://hurwan.tistory.com/123#entry123comment</comments>
      <pubDate>Sat, 29 Aug 2026 22:02:47 +0900</pubDate>
    </item>
    <item>
      <title>[타입스크립트] TypeScript 사용법</title>
      <link>https://hurwan.tistory.com/121</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;TypeScript&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 DTO 사용을 위해 타입스크립트 도입을 고려중입니다.&lt;/p&gt;
&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;h2 data-ke-size=&quot;size26&quot;&gt;환경 고려&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 node 및 npm 버전이 동일해야합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;node: &lt;code&gt;v24.18.0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;npm: &lt;code&gt;11.16.0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;단순한 테스트&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;npm install -D typescript tsx @types/node&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npx tsc --init&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/index.ts&lt;/code&gt; 코드 작성&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npx tsx src/index.ts&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Result Image&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;image.png&quot; data-origin-width=&quot;1317&quot; data-origin-height=&quot;622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ltlrb/dJMcabZQFNU/F8rIkUo565wvz1T8nwCpsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ltlrb/dJMcabZQFNU/F8rIkUo565wvz1T8nwCpsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ltlrb/dJMcabZQFNU/F8rIkUo565wvz1T8nwCpsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLtlrb%2FdJMcabZQFNU%2FF8rIkUo565wvz1T8nwCpsK%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;1317&quot; height=&quot;622&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1317&quot; data-origin-height=&quot;622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 일반적인 &lt;code&gt;js&lt;/code&gt;와 다르게 정적 파일검사 경고가 나오며 빌드 시 에러가 타입 에러가 납니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;명령어의 의미&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;-D&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;--save-dev&lt;/code&gt;라는 의미로 &lt;code&gt;pagckage.json&lt;/code&gt;에 &lt;code&gt;devDependencies&lt;/code&gt;에 패키지 정보가 추가됩니다. (보통 &lt;code&gt;dependencies&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;--save-dev&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;image-13.png&quot; data-origin-width=&quot;903&quot; data-origin-height=&quot;681&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0a63Z/dJMcacdgVBl/bgX7ahJqQHZB3L2JXJDvp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0a63Z/dJMcacdgVBl/bgX7ahJqQHZB3L2JXJDvp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0a63Z/dJMcacdgVBl/bgX7ahJqQHZB3L2JXJDvp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0a63Z%2FdJMcacdgVBl%2FbgX7ahJqQHZB3L2JXJDvp1%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;903&quot; height=&quot;681&quot; data-filename=&quot;image-13.png&quot; data-origin-width=&quot;903&quot; data-origin-height=&quot;681&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;typescript&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TypeScript 컴파일러&lt;/b&gt;를 설치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 컴파일러를 이용하여 VSCode가 실시간으로 정적 파일 검사를 할 수 있으며 직접 &lt;code&gt;tsc&lt;/code&gt; 명령어를 이용할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;tsx&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;tsx&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;tsx&lt;/code&gt;는 &lt;code&gt;npx tsx src/index.ts&lt;/code&gt;와 같은 명령어를 이용하여 직접 &lt;code&gt;npx tsc&lt;/code&gt;, &lt;code&gt;node dist/index.js&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;tsx test&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;63&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yDXnL/dJMcacqUDHB/f4k8xuyZSysFJbYu1eepFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yDXnL/dJMcacqUDHB/f4k8xuyZSysFJbYu1eepFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yDXnL/dJMcacqUDHB/f4k8xuyZSysFJbYu1eepFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyDXnL%2FdJMcacqUDHB%2Ff4k8xuyZSysFJbYu1eepFk%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;1042&quot; height=&quot;63&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;63&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;@types/node&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Node.js&lt;/code&gt; 환경에서 제공하는 API인 &lt;code&gt;node:fs&lt;/code&gt;, &lt;code&gt;process&lt;/code&gt;, &lt;code&gt;Buffer&lt;/code&gt; 등은 JavaScript에서 기본으로 제공하는 기능이 아니기 때문에 이를 알기 위해서 &lt;code&gt;@types/node&lt;/code&gt;를 이용하여 해당 기능들에 대한 TypeScript 타입 설명서를 알 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;npx&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;npx&lt;/code&gt;는 설치된 패키지의 실행 파일을 실행한다는 의미입니다. 아래 이미지에 설치된 &lt;code&gt;tsc&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;npx tsc&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;image-15.png&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XATbu/dJMcadC6AZq/TXNklAhIiaSSOlneNV3Lc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XATbu/dJMcadC6AZq/TXNklAhIiaSSOlneNV3Lc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XATbu/dJMcadC6AZq/TXNklAhIiaSSOlneNV3Lc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXATbu%2FdJMcadC6AZq%2FTXNklAhIiaSSOlneNV3Lc1%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;307&quot; height=&quot;412&quot; data-filename=&quot;image-15.png&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;412&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;code&gt;tsc&lt;/code&gt;는 TypeScript 프로젝트를 검사하고 설정에 따라 JavaScript를 생성하는 공식 컴파일러입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;--init&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeScript 설정 파일을 처음 만들라는 옵션입니다.&lt;/p&gt;
&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;tsconfig.json&lt;/code&gt;의 형태이며 이는 TypeScript 컴파일러에게 주는 설정파일입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;npx tsc&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;tsconfig.json&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;tsconfig.json&lt;/code&gt;에 &lt;code&gt;{ &quot;compilerOptions&quot;: { &quot;outDir&quot;: &quot;./dist&quot; } }&lt;/code&gt; 로 설정해둔다면 &lt;code&gt;dist/&lt;/code&gt; 폴더에 파일이 생성되게 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;변수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;let var: type = typeVar&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;Default Useage&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;image-5.png&quot; data-origin-width=&quot;429&quot; data-origin-height=&quot;106&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t8JF4/dJMcaikif7c/CVHVFxKcw0m2Urlzf6zF31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t8JF4/dJMcaikif7c/CVHVFxKcw0m2Urlzf6zF31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t8JF4/dJMcaikif7c/CVHVFxKcw0m2Urlzf6zF31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft8JF4%2FdJMcaikif7c%2FCVHVFxKcw0m2Urlzf6zF31%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;429&quot; height=&quot;106&quot; data-filename=&quot;image-5.png&quot; data-origin-width=&quot;429&quot; data-origin-height=&quot;106&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Auto Type Inference&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;image-4.png&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4kHN6/dJMcabZQFOC/eiFdVbkqKNralUoNm23kJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4kHN6/dJMcabZQFOC/eiFdVbkqKNralUoNm23kJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4kHN6/dJMcabZQFOC/eiFdVbkqKNralUoNm23kJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4kHN6%2FdJMcabZQFOC%2FeiFdVbkqKNralUoNm23kJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;190&quot; data-filename=&quot;image-4.png&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;190&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;함수 시그니처&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;function func(a: number, b: string): object {...}&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; Good Use &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;image-1.png&quot; data-origin-width=&quot;751&quot; data-origin-height=&quot;123&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUihVN/dJMcabZQFOJ/C3LjwTzRsZjwz5OpsmIKxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUihVN/dJMcabZQFOJ/C3LjwTzRsZjwz5OpsmIKxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUihVN/dJMcabZQFOJ/C3LjwTzRsZjwz5OpsmIKxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUihVN%2FdJMcabZQFOJ%2FC3LjwTzRsZjwz5OpsmIKxK%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;751&quot; height=&quot;123&quot; data-filename=&quot;image-1.png&quot; data-origin-width=&quot;751&quot; data-origin-height=&quot;123&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Occure Error 2&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;image-2.png&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;199&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YEZCQ/dJMcajpPeZN/rtB3LRlEbnH5ix3qjEelXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YEZCQ/dJMcajpPeZN/rtB3LRlEbnH5ix3qjEelXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YEZCQ/dJMcajpPeZN/rtB3LRlEbnH5ix3qjEelXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYEZCQ%2FdJMcajpPeZN%2FrtB3LRlEbnH5ix3qjEelXk%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;592&quot; height=&quot;137&quot; data-filename=&quot;image-2.png&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;199&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Occure Error 2&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;image-3.png&quot; data-origin-width=&quot;964&quot; data-origin-height=&quot;168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8oimB/dJMcajpPeZP/nfKlXrlKmDPfycTp0Ol251/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8oimB/dJMcajpPeZP/nfKlXrlKmDPfycTp0Ol251/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8oimB/dJMcajpPeZP/nfKlXrlKmDPfycTp0Ol251/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8oimB%2FdJMcajpPeZP%2FnfKlXrlKmDPfycTp0Ol251%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;636&quot; height=&quot;111&quot; data-filename=&quot;image-3.png&quot; data-origin-width=&quot;964&quot; data-origin-height=&quot;168&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;객체 타입&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반객체의 키들에 대한 속성을 줄 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 활용하여 여러가지 작업이 가능하게 됩니다.&lt;/p&gt;
&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;TS Type Test&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;image-6.png&quot; data-origin-width=&quot;1147&quot; data-origin-height=&quot;705&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbXAsn/dJMcacLel7Q/yVEXPleQMCne0XnzO63hd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbXAsn/dJMcacLel7Q/yVEXPleQMCne0XnzO63hd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbXAsn/dJMcacLel7Q/yVEXPleQMCne0XnzO63hd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbXAsn%2FdJMcacLel7Q%2FyVEXPleQMCne0XnzO63hd0%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;1147&quot; height=&quot;705&quot; data-filename=&quot;image-6.png&quot; data-origin-width=&quot;1147&quot; data-origin-height=&quot;705&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;Enum과 같은 방식의 사용 또한 가능합니다.&lt;/p&gt;
&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;TS Enum&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;image-7.png&quot; data-origin-width=&quot;913&quot; data-origin-height=&quot;277&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bldSqi/dJMcacLel71/eHX7XdnODmKKWOxBRywGXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bldSqi/dJMcacLel71/eHX7XdnODmKKWOxBRywGXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bldSqi/dJMcacLel71/eHX7XdnODmKKWOxBRywGXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbldSqi%2FdJMcacLel71%2FeHX7XdnODmKKWOxBRywGXK%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;583&quot; height=&quot;177&quot; data-filename=&quot;image-7.png&quot; data-origin-width=&quot;913&quot; data-origin-height=&quot;277&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입을 2개 이상 줄 수 있으며 배열 순서도 지정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TS Type&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;image-8.png&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;337&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GsWBN/dJMcadJXnm9/FgSxTB8Dd37GlFOOu38Mpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GsWBN/dJMcadJXnm9/FgSxTB8Dd37GlFOOu38Mpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GsWBN/dJMcadJXnm9/FgSxTB8Dd37GlFOOu38Mpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGsWBN%2FdJMcadJXnm9%2FFgSxTB8Dd37GlFOOu38Mpk%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;468&quot; height=&quot;183&quot; data-filename=&quot;image-8.png&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;337&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TS Array&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;image-9.png&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;328&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clDLKa/dJMcadpGRNg/tYmtaOYShijTq7HCm0VNK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clDLKa/dJMcadpGRNg/tYmtaOYShijTq7HCm0VNK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clDLKa/dJMcadpGRNg/tYmtaOYShijTq7HCm0VNK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclDLKa%2FdJMcadpGRNg%2FtYmtaOYShijTq7HCm0VNK1%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;488&quot; height=&quot;173&quot; data-filename=&quot;image-9.png&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;328&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;함수가 아닌 변수 인터페이스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 설명한 &lt;code&gt;type&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스는 기본적으로 &lt;b&gt;객체의 구조를 정의하는 용도&lt;/b&gt;이며 &lt;b&gt;같은 이름을 다시 선언&lt;/b&gt;할 수 있습니다.&lt;/p&gt;
&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;compare with ts&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;image-11.png&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BxTtY/dJMcajpPe1I/PTko2WWj9jTlWSCmdybzYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BxTtY/dJMcajpPe1I/PTko2WWj9jTlWSCmdybzYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BxTtY/dJMcajpPe1I/PTko2WWj9jTlWSCmdybzYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBxTtY%2FdJMcajpPe1I%2FPTko2WWj9jTlWSCmdybzYk%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;615&quot; height=&quot;354&quot; data-filename=&quot;image-11.png&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;556&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;Declaration Merging&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;Interface declaration merging&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;image-10.png&quot; data-origin-width=&quot;993&quot; data-origin-height=&quot;816&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zp2O4/dJMb99U9aWU/JJ6RKQhIz8xgcN6uo16dZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zp2O4/dJMb99U9aWU/JJ6RKQhIz8xgcN6uo16dZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zp2O4/dJMb99U9aWU/JJ6RKQhIz8xgcN6uo16dZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fzp2O4%2FdJMb99U9aWU%2FJJ6RKQhIz8xgcN6uo16dZ1%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;523&quot; height=&quot;430&quot; data-filename=&quot;image-10.png&quot; data-origin-width=&quot;993&quot; data-origin-height=&quot;816&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;함수 인터페이스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수의 경우에는 &lt;b&gt;call signature&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;Interface function&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;image-12.png&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;712&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r4i8P/dJMcaiEBQPC/1bfmYchkBEYAccfQk5w4hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r4i8P/dJMcaiEBQPC/1bfmYchkBEYAccfQk5w4hk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r4i8P/dJMcaiEBQPC/1bfmYchkBEYAccfQk5w4hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr4i8P%2FdJMcaiEBQPC%2F1bfmYchkBEYAccfQk5w4hk%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;570&quot; height=&quot;465&quot; data-filename=&quot;image-12.png&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;712&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>언어 및 라이브러리</category>
      <author>hurwan</author>
      <guid isPermaLink="true">https://hurwan.tistory.com/121</guid>
      <comments>https://hurwan.tistory.com/121#entry121comment</comments>
      <pubDate>Sat, 29 Aug 2026 18:29:13 +0900</pubDate>
    </item>
    <item>
      <title>[논문] AlexNet 인공신경망 논문 읽으면서 정리</title>
      <link>https://hurwan.tistory.com/120</link>
      <description>&lt;h1&gt;심층 합성곱 신경망을 이용한 ImageNet 분류&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;( ImageNet Classification with Deep Convolutional Neural Networks )&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논문을 읽으며 생각을 정리해보는 글입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://papers.nips.cc/paper/2012/file/c399862d3b9d6b76c8436e924a68c45b-Paper.pdf&quot;&gt;논문 링크&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;초록&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 부분은 어떤 데이터를 이용하여 어떤 결과를 만드는 모델을 만들었는지에 대한 설명입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 특별하게 보이는 것은 LSVRC - &lt;code&gt;ImageNet Large Scale Visual Recognition Challange&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;또한 1000개 종류의 클래스를 분류하는 softmax를 사용하며 비포화 뉴런 (이는 아마 학습률이 0에 가까워지는 현상을 막았다는 것을 말하는 것 같습니다.)과 GPU 기반 합성곱 구현 (GPU를 활용하였다는 이야기 같습니다.)을 이용하였다고 말합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인해보니 non-saturating neurons는 학습률 포화가 아닌 ReLU를 사용했다는 것을 말합니다. 향후 밑에서 말합니다. 이를 통해서 학습 속도가 빨라졌음을 시사합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 정규화 또는 규제 레이어로 알려져있는 Dropout를 사용하여 일부 노드들을 비활성화시켰다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2010 대회의 우승 모델은 1개만 정확히 맞출(정답을 가장 높은 확률로 예측할) 확률이 62.5%, 상위 예측 5개중 맞출 확률이 83%로 1000개 분류중에서 하나 또는 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;2012년 대회에서는 조금 더 개선된 모습을 보였습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 서론&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 맥락으로 보았을 때 Dropout 또는 모종의 활성화함수 등을 이용한 과적합 방지 + 많은 epochs를 이용할 수 있지 않을까 생각이 듭니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&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;이에 따라 22,000개가 넘는 범주에 1,500만장 이상의 라벨된 고해상도 이미지를 포함하는 &lt;b&gt;ImageNet&lt;/b&gt; 데이터셋에 대한 이야기를 언급하였습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.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;/p&gt;
&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;(통계의 정상성 - 같은 종류의 filter/edge가 동일한 패턴을 잡을 수 있다는 가정)과 픽셀 의존성이 국소성에 대해 강하고 대체로 타당하다는 가정을 둔다&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 특정 커널들(필터/엣지)가 서로 다른 이미지들에서 유효할 것이라는 가정임을 의미합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 일반 신경망보다 파라미터가 적은 CNN의 특징을 이용하여 효율적인 성능을 기대한다고 합니다. (성능은 약간 떨어지지만 학습은 매우 효율적)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다행히도 라벨링 데이터가 많아서 문제 없이 학습시킬 수 있었다고 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.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;2차원 합성곱과 CNN네트워크에 성능을 높이고 학습 시간을 줄였지만 120만개의 라벨 학습 예제에도 불구하고 과적합이 큰 문제가 되었다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과적합 방지를 위한 여러 기법 사용하였으며 4절에서 설명한다 합니다.&lt;/p&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% 이하만 포함하지만 그 모든것이 중요하다 합니다. (아마 CNN레이어가 FC 레이어에 비해 파라미터가 적은게 당연해서 그런 것으로 보입니다.)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.5문단&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;GTX 580 3GB GPU&lt;/code&gt; 2장으로 5~6일이 걸렸다고 하니 매우 적은 자원으로 구현한 요소라고 생각합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 데이터셋&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;22,000 범주, 1,500만장 이상의 고해상도 라벨링 이미지 데이터셋을 썼다고 합니다. (양이 굉장히 많습니다.)&lt;/p&gt;
&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;Amazom Mechanical Turk&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;ILSVRC&lt;/code&gt; 에서는 그중 1,000범주, 120만/5만/15만 장의 학습/검증/테스트 데이터셋을 사용하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;top-1/5 오류율로 보고했다고 합니다. (ImageNet에서 일반적인)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제각각인 해상도를 256x256의 고정된 해상도로 다운샘플링 하였다고 합니다. 방식은 자르기로 리사이징이 아니였습니다. 또한 RGB 픽셀 (3채널)을 그댈 사용하였습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 아키텍처&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아키텍처는 유명한 CNN레이어 5개, FC레이어 3개로 이루어져있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 ReLU 비선형성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\operatorname{Tanh}$ 또는 $\operatorname{Sigmoid}$를 사용하면 포화 비선형 함수에 비해 Saturation에 의한 학습속도 감소 때문에 비포화 비선형 함수를 사용하였다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 사용하는 함수를 ReLU(recified Linear Unit)를 사용하였습니다.&lt;/p&gt;
&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;반복 횟수 그래프 또한 존재하는데 에포크 횟수에 비해 Loss가 매우 감소하는 것으로 확인되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 그래프는 실선이 $\operatorname{Sigmoid}$이며 점선이 $\operatorname{Tanh}$였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차이는 약 6배라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;433&quot; data-origin-height=&quot;310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d12PKU/dJMcabSXCNi/t9ih8eW1KiPtgzKLTJoMc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d12PKU/dJMcabSXCNi/t9ih8eW1KiPtgzKLTJoMc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d12PKU/dJMcabSXCNi/t9ih8eW1KiPtgzKLTJoMc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd12PKU%2FdJMcabSXCNi%2Ft9ih8eW1KiPtgzKLTJoMc0%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;433&quot; height=&quot;310&quot; data-origin-width=&quot;433&quot; data-origin-height=&quot;310&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 착안하여 초반에 ReLU, 나중에 포화함수로 변형하는 것도 괜찮을까? 라는 생각이 들었습ㄴ디ㅏ.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 &lt;code&gt;Jarret et al&lt;/code&gt;에서도 고정 정규화 적용 때 $|tanh(x)|$가 효과가 좋지만 속도 때문에 비포화 비선형 함수를 사용하였다고 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 다중 GPU 학습&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 이미 2장의 GPU를 사용하였다고 합니다.&lt;/p&gt;
&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;또한 cpu 메모리에 거치지 않고 서로다른 gpu끼리의 통신이 되어 병렬화에 더 좋았다고 합니다.&lt;/p&gt;
&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;code&gt;Cire&amp;cedil;sanet al&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;top-1/5&lt;/code&gt; 에러를 각각 1.7%, 1.2%로 줄였다고 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.3 국소 응답 정규화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ReLU는 비포화이기 때문에 다시 정규화를 하지 않아도 괜찮지만 일반화를 위해 국소ㅗ 방정식을 사용하였다 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;441&quot; data-origin-height=&quot;99&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3MHdn/dJMcafnDzAr/P8S6zC2WJSgljyfqwsh1HK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3MHdn/dJMcafnDzAr/P8S6zC2WJSgljyfqwsh1HK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3MHdn/dJMcafnDzAr/P8S6zC2WJSgljyfqwsh1HK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3MHdn%2FdJMcafnDzAr%2FP8S6zC2WJSgljyfqwsh1HK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;441&quot; height=&quot;99&quot; data-origin-width=&quot;441&quot; data-origin-height=&quot;99&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;code&gt;k=2, n=5, &amp;alpha;=10^-4, &amp;beta;=0.75&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;국소 대비 정규화는 원래 값에서 평균을 빼지만 밝기 정규화는 평균을 빼지 않아 차이가 강조되지 않는다고 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 방식으로 4레이어 CNN의 테스트 오류율이 약 2% 감소하였다고 합니다. &lt;code&gt;top-1/5&lt;/code&gt; 오류율은 1.4%, 1.2% 줄였다고 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.4 중첨 풀링&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 방식은 &lt;code&gt;스트라이드=커널크기&lt;/code&gt;이지만 해당 네트워크에서는 &lt;code&gt;s=2, z=3&lt;/code&gt;을 사용하여 &lt;code&gt;s=2, z=2&lt;/code&gt; 대비 0.4%, 0.3%를 낮추었습니다. 하지만 과적합이 약간 더 어렵게 발생하는 경향도 관찰되었다 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.5 전체 아키텍처&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 설명하였듯 5CNN, 3FC 레이어로 이루어져 있으며 1000-way softmax에 입력되는 구조를 가져 정답 라벨의 로그 확률을 구해 평균을 최대화한다고 합니다.&lt;/p&gt;
&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;또한 max-polling 레이어를 이용하여 두 응답 정규화 레이어와 다섯번째 합성곱 레이어에 위치한다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;889&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/croAFH/dJMcafnDzAx/TGJLAIQuv9EwodEa2WdRK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/croAFH/dJMcafnDzAx/TGJLAIQuv9EwodEa2WdRK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/croAFH/dJMcafnDzAx/TGJLAIQuv9EwodEa2WdRK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcroAFH%2FdJMcafnDzAx%2FTGJLAIQuv9EwodEa2WdRK1%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;889&quot; height=&quot;312&quot; data-origin-width=&quot;889&quot; data-origin-height=&quot;312&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;이는 1/2 피처맵 사이, 2/3 피처맵 사이, CNN와 FC레이어 사이로 이루어지는 것으로 보입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에도 그림에 대한 내용을 자세히 설명해주며 첫번째 레이어에서 2개로 나뉘어지며 3번재 레이어에서 상태 병함, 이후 fc 레이어에서는 계속 서로의 상태를 주고 받다가 마지막에 1000개 logits로 반환함을 의미합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 과적합 감소&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 신경망 파라미터에는 6천만개의 파라미터가 있다고 합니다.&lt;/p&gt;
&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;h3 data-ke-size=&quot;size23&quot;&gt;4.1 데이터 증강&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 쉽고 흔한 방법을 사용하였다고 하며 &lt;b&gt;GPU의 학습시간동안 CPU의 파이썬 코드를 이용해서 변환&lt;/b&gt;하여 원본을 재생성 없이 증강을 구현했습니다. (실질적인 추가 계산비용이 거의 없다고 합니다.)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 파이프라인 형식으로 느껴집니다&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 방법으로는 &lt;b&gt;RGB 채널 강도 조절&lt;/b&gt;로 &lt;code&gt;mean=0&lt;/code&gt;, &lt;code&gt;std=0.1&lt;/code&gt;의 가우시안 난수를 이용하였다고 합니다. (원본에 유사한 이미지가 많이 나오게)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2 드롭아웃&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드롭아웃을 이용하여 매번 절반의 뉴런을(&lt;code&gt;p=0.5&lt;/code&gt;일때) 학습 강도를 &lt;code&gt;0&lt;/code&gt;으로 낮추어 매번 다른 네트워크를 이용하게 하는 방법으로 매 역전파마다 다른 아키텍처를 학습하는 형식으로 만들게 됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 세부사항&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;BATCH_SIZE=128&lt;/code&gt;를 사용하였으며 최적화 알고리즘으로는 &lt;code&gt;momentum=0.9&lt;/code&gt;, &lt;code&gt;weight_decay=0.0005&lt;/code&gt;를 사용하였다고 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 &lt;b&gt;SGD&lt;/b&gt;는 $w_i = w_{i-1} - \operatorname{lr} \times \operatorname{gradient}$ 이지만 옵션을 통해 이전 업데이트 방향을 누적하며 가중치 감쇠또한 적용시킬 수 있습니다. 그렇게 되면 다음과 같은 형태에 가까워집니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$w_i = 0.9995 \times(0.9 \times w_{i-1} - 0.1 \times \operatorname{lr} \times \operatorname{gradient})$$&lt;/p&gt;
&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;br /&gt;v_{i+1}​=0.9 \times v_i ​&amp;minus; 0.0005 \cdot ϵ \cdot w_i ​&amp;minus; ϵ \cdot \nabla L(w_i​)&lt;br /&gt;$$&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전의 이동량에 대해서 현재 학습량 + 기울기를 아주 조금 적용하여 더해주는 방식입니다. SGD의 특징인 튀는 값에 민감한 부분을 감쇠하는 수식입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 모든 가중치를 &lt;code&gt;mean=0&lt;/code&gt;, &lt;code&gt;std=0.01&lt;/code&gt; 정규분포에서 뽑았다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2, 4, 5 Conv/FC 레이어는 &lt;code&gt;bias=1&lt;/code&gt;로 시작하였으며 다른 레이어는 &lt;code&gt;bias=0&lt;/code&gt;를 사용하였습니다. 여기에서 &lt;code&gt;bias=1&lt;/code&gt;로 시작하는 레이어는 &lt;b&gt;ReLU&lt;/b&gt;를 사용하는 레이어로 이는 초기부터 &lt;b&gt;ReLU&lt;/b&gt; 뉴런이 비활성화 될 가능성을 낮추기 위함입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 결과&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SIFT, Saprse Coding은 CNN이 유행하기 전, 이미지 특징을 뽑는 대표적인 방법으로&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SIFT: Scale Invariant feature Transform 으로 이미지에서 코너나 무늬같은 눈에 띄는 지점을 찾고, 그 주변의 경계 방향/모양을 숫자 벡터로 표현하는 것을 말합니다. 물체의 특징을 잡아내느 방식입니다.&lt;/li&gt;
&lt;li&gt;Sparse Coding: 희소코딩이라는 의미로 이미지의 특징을 미리 준비된 여러 기본 패턴의 조합으로 표현하는 것을 말합니다. 이미지를 간단한 패턴들의 조합으로 계산합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 AlexNet는 사람이 설계한 특징 추출이 아닌 &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;Spare coding에 비해서 약 top-1/5 오류율이 모두 약 10% 낮은 오류율을 보였으며 SIFT 방식보다 8% 낮은 오류율을 보여주었습니다.&lt;/p&gt;
&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;비슷한 CNN 평균은 Top-5 오류율이 &lt;code&gt;16.4%&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;더 큰 데이터로 사전 학습 후 파인튜닝시 오류율 &lt;code&gt;16.6%&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;일반 CNN 5개 + 사전학습 CNN 2개를 통한 앙상블 사용 시 &lt;code&gt;15.3%&lt;/code&gt;를 나타낸다고 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앙상블 + 추가 데이터를 통해 더 좋은 성능이 나타내는 것을 볼 수 있습니다. 비고로 1,500만 이미지를 학습할 때에는 Conv5 이후에 Conv6를 임시로 추가하여 표현력을 늘렸습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6.1 정성적 평가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논문에는 &lt;code&gt;그림3&lt;/code&gt;이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;522&quot; data-origin-height=&quot;229&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OnWQZ/dJMcacxBhEK/aKUh4HjRlJHJQjwpPfEkEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OnWQZ/dJMcacxBhEK/aKUh4HjRlJHJQjwpPfEkEK/img.png&quot; data-alt=&quot;Figure 3&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OnWQZ/dJMcacxBhEK/aKUh4HjRlJHJQjwpPfEkEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOnWQZ%2FdJMcacxBhEK%2FaKUh4HjRlJHJQjwpPfEkEK%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;522&quot; height=&quot;229&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;522&quot; data-origin-height=&quot;229&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Figure 3&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 2장의 GPU가 각각 데이터를 학습한 것을 나타내며 네트워크는 다양한 주파수 및 방향 선택적 커널, 여러 색상 블롭을 학습하였다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPU1(위쪽 흑백분위기)은 색상과 무관하게 패턴을 학습하였으며 GPU2는 색상에 상대적으로 민감하다는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 결과는 사람이 두 GPU가 다른 특징을 추출하게 한것이 아닌, GPU 사이의 연결을 일부 제한하여 학습 결과가 자연스럽게 전문화된 것이라는 것을 알 수 있습니다. 특징적인 것은 GPU 번호가 바뀌는 경우를 제외하면 무작위 가중치 초기화를 해도 동일한 현상이 등장했다는 것입니다.&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>머신러닝</category>
      <author>hurwan</author>
      <guid isPermaLink="true">https://hurwan.tistory.com/120</guid>
      <comments>https://hurwan.tistory.com/120#entry120comment</comments>
      <pubDate>Sat, 29 Aug 2026 14:02:56 +0900</pubDate>
    </item>
  </channel>
</rss>