<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Songbination | 제조 데이터&amp;middot;설비 자동화</title>
    <link>https://songbination.tistory.com/</link>
    <description>제조 설비 엔지니어가 Python, SQL, Tableau, PLC/FINS로 데이터 수집&amp;middot;업무 자동화&amp;middot;예지보전을 구현한 과정을 기록합니다.</description>
    <language>ko</language>
    <pubDate>Thu, 3 Sep 2026 12:18:44 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Songmin</managingEditor>
    <image>
      <title>Songbination | 제조 데이터&amp;middot;설비 자동화</title>
      <url>https://tistory1.daumcdn.net/tistory/8901421/attach/9ac79efc59fe4c15bfb9f5c9d27846f0</url>
      <link>https://songbination.tistory.com</link>
    </image>
    <item>
      <title>PLC 사이클 타임으로 설비 이상 감지하기 &amp;mdash; 중앙값&amp;middot;IQR&amp;middot;EWMA [설비 신호 4]</title>
      <link>https://songbination.tistory.com/98</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;margin: 18px 0; padding: 14px 16px; border-left: 4px solid #2563eb; border-radius: 8px; background: #f8fafc; line-height: 1.8;&quot;&gt;&lt;b&gt;시리즈 안내&lt;/b&gt; &amp;middot; 이 글은 설비 신호 4입니다.&lt;br /&gt;&lt;a href=&quot;https://songbination.tistory.com/category/PdM%28Predictive%20Maintenance%29&quot;&gt;예지보전&amp;middot;설비 데이터 전체 글 보기 &amp;rarr;&lt;/a&gt;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;완료 신호는 정상인데 설비가 조금씩 느려질 때&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설비는 멈추지 않았고 알람도 없습니다. 그런데 같은 제품을 만들 때 걸리는 시간이 31초, 32초, 34초처럼 조금씩 길어질 수 있습니다. 한 번의 사이클만 보면 작업 편차처럼 보이지만, 며칠치 흐름으로 놓으면 설비가 보내는 초기 신호일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 신호와 완료 신호가 있다면 두 시각의 차이로 사이클 타임을 만들 수 있습니다. 문제는 평균 하나만 그어 놓으면 제품 종류, 교체 작업과 수동 운전까지 모두 섞인다는 것입니다. 정상적인 조건 차이가 이상으로 잡히고, 긴 정지가 평균을 끌어올리기도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 유효한 사이클을 먼저 정의한 뒤 중앙값과 IQR로 튀는 값을 찾고, EWMA로 서서히 느려지는 흐름을 확인합니다. 고장 판정을 대신하는 모델이 아니라 사람이 점검할 시점을 조금 일찍 찾기 위한 기준입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시리즈 안내&lt;/b&gt; 이 글은 「설비 신호로 열화 읽기」 시리즈의 4번째 글입니다. 앞선 글에서 전류, 온도와 알람 패턴을 봤다면 이번에는 PLC의 시작&amp;middot;완료 신호만으로 만들 수 있는 시간 지표를 다룹니다.&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;h2 data-ke-size=&quot;size26&quot;&gt;1. 사이클 타임을 먼저 정의하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 단순한 정의는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;fix&quot;&gt;&lt;code&gt;사이클 타임 = 완료 시각 - 시작 시각&lt;/code&gt;&lt;/pre&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;재시작&amp;middot;중복 신호를 어떻게 처리할지&lt;/li&gt;
&lt;li&gt;수동 운전, 금형&amp;middot;품종 교체, 계획 정지 구간을 제외할지&lt;/li&gt;
&lt;li&gt;비정상적으로 짧거나 긴 미완성 사이클을 어떻게 표시할지&lt;/li&gt;
&lt;li&gt;제품&amp;middot;레시피&amp;middot;설비별 기준을 따로 둘지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정의가 불명확하면 정교한 이상 탐지 공식을 사용해도 결과를 신뢰하기 어렵습니다.&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;다음은 설명용 데이터 구조입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;completed_at,equipment,product,cycle_sec
2026-08-31 09:00:31,EQ-DEMO-01,A,31.2
2026-08-31 09:01:02,EQ-DEMO-01,A,30.8
2026-08-31 09:01:34,EQ-DEMO-01,A,32.1
2026-08-31 09:02:26,EQ-DEMO-01,A,51.9&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;import pandas as pd
​
df = pd.read_csv(
 &amp;nbsp;  &quot;cycle_demo.csv&quot;,
 &amp;nbsp;  parse_dates=[&quot;completed_at&quot;],
)
​
df = df.sort_values([&quot;equipment&quot;, &quot;product&quot;, &quot;completed_at&quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 분석에서는 정상 운전과 조건이 다른 수동 작업&amp;middot;교체 작업을 별도 상태 열로 구분한 뒤 기준선 계산에서 제외하는 편이 좋습니다.&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;한두 번의 긴 정지가 포함되면 평균은 쉽게 올라갑니다. 중앙값은 극단값의 영향을 덜 받기 때문에 사이클 타임의 일반적인 수준을 요약하는 첫 기준으로 사용하기 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;summary = (
 &amp;nbsp;  df.groupby([&quot;equipment&quot;, &quot;product&quot;])[&quot;cycle_sec&quot;]
 &amp;nbsp; &amp;nbsp;  .agg(count=&quot;count&quot;, median=&quot;median&quot;, mean=&quot;mean&quot;, std=&quot;std&quot;)
 &amp;nbsp; &amp;nbsp;  .reset_index()
)
​
print(summary)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평균과 중앙값의 차이가 커졌다면 일부 긴 사이클이나 한쪽으로 치우친 분포가 있는지 함께 확인합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. IQR로 튀는 사이클 찾기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IQR은 3사분위수와 1사분위수의 차이입니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;IQR = Q3 - Q1
하한 = Q1 - 1.5 &amp;times; IQR
상한 = Q3 + 1.5 &amp;times; IQR&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제품과 설비별로 경계를 계산합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;def add_iqr_flag(group):
 &amp;nbsp;  q1 = group[&quot;cycle_sec&quot;].quantile(0.25)
 &amp;nbsp;  q3 = group[&quot;cycle_sec&quot;].quantile(0.75)
 &amp;nbsp;  iqr = q3 - q1
​
 &amp;nbsp;  lower = q1 - 1.5 * iqr
 &amp;nbsp;  upper = q3 + 1.5 * iqr
​
 &amp;nbsp;  group = group.copy()
 &amp;nbsp;  group[&quot;iqr_lower&quot;] = lower
 &amp;nbsp;  group[&quot;iqr_upper&quot;] = upper
 &amp;nbsp;  group[&quot;is_iqr_outlier&quot;] = ~group[&quot;cycle_sec&quot;].between(lower, upper)
 &amp;nbsp;  return group
​
df = (
 &amp;nbsp;  df.groupby([&quot;equipment&quot;, &quot;product&quot;], group_keys=False)
 &amp;nbsp; &amp;nbsp;  .apply(add_iqr_flag)
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IQR 경계를 벗어났다는 것은 확인 후보라는 뜻이지 곧바로 설비 고장이라는 뜻은 아닙니다. 재료 공급 대기, 작업자 개입, 점검, 데이터 누락 같은 원인도 함께 확인해야 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. EWMA로 서서히 느려지는 흐름 보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EWMA는 최근 값에 더 큰 가중치를 두는 지수 가중 이동평균입니다. 단일 값의 급등뿐 아니라 사이클 타임이 조금씩 증가하는 흐름을 부드럽게 확인할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;alpha = 0.30
​
df[&quot;cycle_ewma&quot;] = (
 &amp;nbsp;  df.groupby([&quot;equipment&quot;, &quot;product&quot;])[&quot;cycle_sec&quot;]
 &amp;nbsp; &amp;nbsp;  .transform(
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;  lambda values: values.ewm(alpha=alpha, adjust=False).mean()
 &amp;nbsp; &amp;nbsp;  )
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;alpha&lt;/code&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;이상 구간이 섞인 전체 데이터를 그대로 기준선으로 사용하면 상한이 함께 올라가 이상을 놓칠 수 있습니다. 다음 순서가 안전합니다.&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;설비&amp;middot;제품&amp;middot;레시피&amp;middot;교대 등 영향 조건을 나눕니다.&lt;/li&gt;
&lt;li&gt;조건별 중앙값&amp;middot;IQR 또는 관리 한계를 계산합니다.&lt;/li&gt;
&lt;li&gt;이후 데이터에 같은 기준을 적용합니다.&lt;/li&gt;
&lt;li&gt;정비나 조건 변경 후에는 기준 재학습 여부를 검토합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제품 A와 제품 B의 정상 사이클이 다르다면 둘을 한 기준으로 비교하지 않습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 알람은 단일 이상치보다 패턴으로&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;IQR 상한 초과가 연속 3회 발생&lt;/li&gt;
&lt;li&gt;최근 10회 중 4회 이상 상한 초과&lt;/li&gt;
&lt;li&gt;EWMA가 기준 중앙값보다 일정 비율 이상 상승한 상태가 지속&lt;/li&gt;
&lt;li&gt;사이클 증가와 특정 정지&amp;middot;알람 코드가 동시에 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임계값과 지속 횟수는 설비 위험도와 오탐 비용을 고려해 검증합니다. 분석 결과는 정비 판단을 돕는 신호이며 안전 제어나 비상정지 로직을 대체하지 않습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 시각화 예제&lt;/h2&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;import matplotlib.pyplot as plt
​
sample = df.query(
 &amp;nbsp;  &quot;equipment == 'EQ-DEMO-01' and product == 'A'&quot;
).copy()
​
plt.figure(figsize=(11, 5))
plt.plot(
 &amp;nbsp;  sample[&quot;completed_at&quot;],
 &amp;nbsp;  sample[&quot;cycle_sec&quot;],
 &amp;nbsp;  marker=&quot;o&quot;,
 &amp;nbsp;  linewidth=1,
 &amp;nbsp;  label=&quot;Cycle time&quot;,
)
plt.plot(
 &amp;nbsp;  sample[&quot;completed_at&quot;],
 &amp;nbsp;  sample[&quot;cycle_ewma&quot;],
 &amp;nbsp;  linewidth=2,
 &amp;nbsp;  label=&quot;EWMA&quot;,
)
plt.scatter(
 &amp;nbsp;  sample.loc[sample[&quot;is_iqr_outlier&quot;], &quot;completed_at&quot;],
 &amp;nbsp;  sample.loc[sample[&quot;is_iqr_outlier&quot;], &quot;cycle_sec&quot;],
 &amp;nbsp;  color=&quot;red&quot;,
 &amp;nbsp;  label=&quot;IQR candidate&quot;,
)
plt.ylabel(&quot;Cycle time (sec)&quot;)
plt.legend()
plt.tight_layout()
plt.show()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프에서는 원시 사이클 타임, EWMA, IQR 이상 후보를 함께 표시하면 순간적인 튐과 장기적인 흐름을 구분하기 쉽습니다.&lt;/p&gt;
&lt;section data-songbination-revision=&quot;260820&quot;&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;실제 감속 후보는 p05&amp;middot;p25 같은 하위 분위수의 바닥이 함께 올라가고 긴 꼬리와 변동 폭도 커지는 형태였습니다. 갑자기 한 번 계단 상승한 뒤 평평하게 유지되면 마모보다 조정이나 설정 변경을 먼저 의심했습니다.&lt;/p&gt;
&lt;table style=&quot;width: 100%; border-collapse: collapse;&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th style=&quot;border: 1px solid #d9e2ec; padding: 8px;&quot;&gt;밴드는 고정, 비중만 변화&lt;/th&gt;
&lt;td style=&quot;border: 1px solid #d9e2ec; padding: 8px;&quot;&gt;생산 믹스 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th style=&quot;border: 1px solid #d9e2ec; padding: 8px;&quot;&gt;하위 분위수와 꼬리 동시 상승&lt;/th&gt;
&lt;td style=&quot;border: 1px solid #d9e2ec; padding: 8px;&quot;&gt;실제 감속 후보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th style=&quot;border: 1px solid #d9e2ec; padding: 8px;&quot;&gt;갑작스러운 계단 후 유지&lt;/th&gt;
&lt;td style=&quot;border: 1px solid #d9e2ec; padding: 8px;&quot;&gt;조정&amp;middot;정비&amp;middot;레시피 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표본 수와 라인 가동 여부가 부족한 날은 EWMA가 움직여도 판정을 보류했습니다.&lt;/p&gt;
&lt;/section&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;오늘의 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이클 타임 이상 감지는 공식보다 데이터 정의가 먼저입니다. 시작과 완료를 정확히 짝지은 뒤 제품과 운전 조건별 정상 기준을 만들고, IQR은 개별 이상 후보를 찾는 데, EWMA는 서서히 변하는 흐름을 보는 데 활용할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시작&amp;middot;완료 신호를 한 사이클로 정확히 묶기&lt;/li&gt;
&lt;li&gt;수동 운전&amp;middot;교체&amp;middot;계획 정지는 정상 기준선에서 분리&lt;/li&gt;
&lt;li&gt;조건별 중앙값으로 일반적인 수준 확인&lt;/li&gt;
&lt;li&gt;IQR은 개별 이상 후보, EWMA는 완만한 변화 확인&lt;/li&gt;
&lt;li&gt;한 번의 초과보다 연속 횟수와 다른 신호의 동시 발생 확인&lt;/li&gt;
&lt;/ul&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.itl.nist.gov/div898/handbook/pmc/section3/pmc314.htm&quot;&gt;NIST/SEMATECH e-Handbook &amp;mdash; EWMA Control Charts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.itl.nist.gov/div898/handbook/eda/section3/boxplot.htm&quot;&gt;NIST/SEMATECH e-Handbook &amp;mdash; Box Plot and IQR&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pandas.pydata.org/docs/reference/api/pandas.Series.ewm.html&quot;&gt;pandas 공식 문서 &amp;mdash; Series.ewm&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다음 글 예고&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지표를 만들면 다음 문제는 매일 그 그래프를 사람이 들여다봐야 한다는 것입니다. 9월부터는 AI Agent가 사이클 타임과 오류 로그를 어떤 입력으로 받고, 무엇을 요약하며, 어느 판단을 사람에게 남겨야 하는지 이어서 정리합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이전 글&amp;middot;다음 글 바로가기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;larr; &lt;a href=&quot;https://songbination.tistory.com/71&quot;&gt;&lt;b&gt;이전 글: 설비 알람 빈도와 패턴으로 열화 신호 읽기 [설비 신호 3]&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;a href=&quot;https://songbination.tistory.com/81&quot;&gt;&lt;b&gt;설비 신호 시리즈 전체 목차&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://songbination.tistory.com/81&quot;&gt;&lt;b&gt;다음 글: 예지보전 실무&amp;middot;이론 전체 목차&lt;/b&gt;&lt;/a&gt; &amp;rarr;&lt;/p&gt;</description>
      <category>PdM(Predictive Maintenance)</category>
      <category>EWMA</category>
      <category>iQR</category>
      <category>PLC</category>
      <category>Python</category>
      <category>데이터분석</category>
      <category>사이클타임</category>
      <category>설비이상감지</category>
      <category>예지보전</category>
      <author>Songmin</author>
      <guid isPermaLink="true">https://songbination.tistory.com/98</guid>
      <comments>https://songbination.tistory.com/98#entry98comment</comments>
      <pubDate>Mon, 31 Aug 2026 18:00:30 +0900</pubDate>
    </item>
    <item>
      <title>MSSQL 연결 안 됨 해결 &amp;mdash; TCP/IP&amp;middot;1433 포트&amp;middot;방화벽 점검 [MSSQL 32]</title>
      <link>https://songbination.tistory.com/97</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;margin: 18px 0; padding: 14px 16px; border-left: 4px solid #2563eb; border-radius: 8px; background: #f8fafc; line-height: 1.8;&quot;&gt;&lt;b&gt;시리즈 안내&lt;/b&gt; &amp;middot; 이 글은 MSSQL 32입니다.&lt;br /&gt;&lt;a href=&quot;https://songbination.tistory.com/category/SQL%20%C2%B7%20MES&quot;&gt;SQL&amp;middot;MES 전체 글 보기 &amp;rarr;&lt;/a&gt;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;연결 오류에서 방화벽부터 끄면 안 되는 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSMS에 서버 이름을 넣었는데 한참 기다린 뒤 &amp;ldquo;서버를 찾을 수 없거나 액세스할 수 없습니다&amp;rdquo;라는 메시지가 뜹니다. 이때 가장 먼저 나오는 말이 &amp;ldquo;1433 포트를 열어 보자&amp;rdquo;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 SQL Server 서비스가 멈춰 있거나 TCP/IP가 꺼져 있다면 포트를 열어도 달라지는 것이 없습니다. 명명된 인스턴스가 동적 포트를 쓰는데 1433만 확인하고 있을 수도 있습니다. 방화벽 전체를 끄면 원인은 그대로인데 보호만 사라집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 &lt;code&gt;서비스 실행 &amp;rarr; 로컬 연결 &amp;rarr; TCP/IP &amp;rarr; 실제 수신 포트 &amp;rarr; 원격 포트 테스트 &amp;rarr; 로그인&lt;/code&gt; 순서로 확인합니다. 어디까지 성공했는지를 기준으로 다음 점검 항목을 고르는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시리즈 안내&lt;/b&gt; 이 글은 「MSSQL 입문&amp;middot;실무」 시리즈의 32번째 글입니다. 31편의 오류 18456은 서버에 도착한 뒤 로그인에서 거절된 경우였습니다. 이번에는 그보다 앞에서 서버 자체에 도달하지 못하는 경우를 다룹니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 서버명은 설명용입니다. 회사 서버명, 실제 IP, 인스턴스명과 계정이 보이는 화면은 외부 글에 사용하지 않습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 오류를 세 구간으로 나누기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연결 실패는 대체로 다음 세 구간에서 발생합니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구간&lt;/th&gt;
&lt;th&gt;대표 증상&lt;/th&gt;
&lt;th&gt;먼저 확인할 항목&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SQL Server 자체&lt;/td&gt;
&lt;td&gt;로컬에서도 연결 실패&lt;/td&gt;
&lt;td&gt;서비스 상태, 인스턴스명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;네트워크&lt;/td&gt;
&lt;td&gt;서버를 찾지 못함, 시간 초과&lt;/td&gt;
&lt;td&gt;TCP/IP, 포트, 방화벽, 경로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;인증&lt;/td&gt;
&lt;td&gt;오류 18456&lt;/td&gt;
&lt;td&gt;인증 방식, 로그인, 권한, 기본 DB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 접속도 실패한다면 원격 방화벽을 먼저 수정할 이유가 없습니다. 반대로 로컬은 성공하고 원격만 실패한다면 TCP/IP와 네트워크 구간을 집중적으로 확인합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. SQL Server 서비스가 실행 중인지 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows의 &lt;code&gt;서비스&lt;/code&gt; 또는 SQL Server Configuration Manager에서 대상 인스턴스가 실행 중인지 확인합니다.&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;SQL Server (MSSQLSERVER)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;명명된 인스턴스 예시: &lt;code&gt;SQL Server (DEMO)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인스턴스명을 혼동하면 서비스가 정상이어도 다른 서버를 찾게 됩니다. 기본 인스턴스와 명명된 인스턴스의 접속 형식도 구분합니다.&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;기본 인스턴스: 서버이름 또는 서버이름,포트
명명된 인스턴스: 서버이름\인스턴스이름&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 같은 PC에서 먼저 연결하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL Server가 설치된 PC에서 로컬 연결을 시도합니다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;sqlcmd -S localhost -E -Q &quot;SELECT @@SERVERNAME, DB_NAME(), GETDATE();&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명시적으로 TCP와 포트를 테스트하려면 다음 형식을 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;sqlcmd -S tcp:localhost,1433 -E -Q &quot;SELECT @@SERVERNAME, GETDATE();&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째는 성공하지만 두 번째가 실패한다면 SQL Server는 실행 중이어도 TCP/IP 또는 해당 포트의 수신 설정이 맞지 않을 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. TCP/IP 사용 여부 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL Server Configuration Manager에서 다음 경로를 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;SQL Server 네트워크 구성
└─ 대상 인스턴스의 프로토콜
 &amp;nbsp; └─ TCP/IP&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP/IP를 사용으로 바꿨다면 SQL Server 서비스를 다시 시작해야 반영됩니다. 운영 서버에서는 서비스 재시작이 접속 중인 사용자와 작업에 영향을 줄 수 있으므로 반드시 승인된 작업 시간에 진행합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 1433이 항상 정답은 아니다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 1433은 SQL Server 기본 인스턴스에서 흔히 사용하는 포트이지만 모든 환경이 1433을 쓰는 것은 아닙니다.&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;보안&amp;middot;운영 정책에 따라 고정된 다른 포트를 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;SQL Server Browser는 인스턴스 탐색에 UDP 1434를 사용할 수 있지만, 방화벽과 서비스 구성에 영향을 받습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 실제 수신 포트를 확인한 뒤 &lt;code&gt;서버이름,포트&lt;/code&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;Windows PowerShell에서는 &lt;code&gt;Test-NetConnection&lt;/code&gt;으로 TCP 연결 가능 여부를 확인할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;Test-NetConnection db-demo.local -Port 1433&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 결과는 다음처럼 해석합니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;결과&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;th&gt;다음 확인&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TcpTestSucceeded : True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;해당 주소와 포트까지 TCP 연결 가능&lt;/td&gt;
&lt;td&gt;SQL 인증&amp;middot;DB 권한&amp;middot;연결 문자열&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TcpTestSucceeded : False&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;포트까지 도달하지 못함&lt;/td&gt;
&lt;td&gt;실제 포트, 방화벽, 라우팅, 서버 수신&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이름 해석 실패&lt;/td&gt;
&lt;td&gt;서버 이름을 주소로 변환하지 못함&lt;/td&gt;
&lt;td&gt;DNS 또는 올바른 서버 주소&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;h2 data-ke-size=&quot;size26&quot;&gt;7. 방화벽은 필요한 범위만 허용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 방화벽에서 SQL Server가 실제 사용하는 TCP 포트의 인바운드 규칙을 확인합니다. 문제 확인을 위해 방화벽 전체를 끄거나 모든 프로필과 모든 원격 주소에 포트를 공개하는 방식은 피합니다.&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;대상 프로그램 또는 실제 TCP 포트&lt;/li&gt;
&lt;li&gt;필요한 네트워크 프로필&lt;/li&gt;
&lt;li&gt;접속이 필요한 원격 주소 범위&lt;/li&gt;
&lt;li&gt;규칙의 목적과 관리 담당자&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷에 SQL Server 포트를 직접 공개하는 구성은 지양하고, 조직의 VPN&amp;middot;사설망&amp;middot;접근제어 정책을 따릅니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 연결 문자열도 포트와 암호화를 맞추기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;pyodbc&lt;/code&gt;에서 포트를 명시한 예시는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;connection_string = (
 &amp;nbsp;  &quot;DRIVER={ODBC Driver 18 for SQL Server};&quot;
 &amp;nbsp;  &quot;SERVER=db-demo.local,1433;&quot;
 &amp;nbsp;  &quot;DATABASE=BlogDemoDB;&quot;
 &amp;nbsp;  &quot;Trusted_Connection=yes;&quot;
 &amp;nbsp;  &quot;Encrypt=yes;&quot;
 &amp;nbsp;  &quot;TrustServerCertificate=yes;&quot;
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;TrustServerCertificate=yes&lt;/code&gt;는 로컬&amp;middot;검증 환경의 자체 서명 인증서를 전제로 한 예시입니다. 운영 환경에서는 유효한 인증서와 서버 이름 검증을 사용하는 편이 안전합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. 빠른 진단 체크리스트&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;접속하려는 SQL Server 인스턴스가 실행 중인가?&lt;/li&gt;
&lt;li&gt;같은 PC에서 로컬 접속이 되는가?&lt;/li&gt;
&lt;li&gt;TCP/IP가 사용 상태이며 서비스 재시작이 반영됐는가?&lt;/li&gt;
&lt;li&gt;실제 수신 포트가 1433인지 다른 포트인지 확인했는가?&lt;/li&gt;
&lt;li&gt;클라이언트의 &lt;code&gt;Test-NetConnection&lt;/code&gt;이 성공하는가?&lt;/li&gt;
&lt;li&gt;필요한 범위에만 방화벽 규칙이 적용됐는가?&lt;/li&gt;
&lt;li&gt;네트워크 성공 후 오류 18456이 나온다면 인증을 따로 확인했는가?&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;오늘의 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSSQL 연결 문제는 &lt;code&gt;서버가 실행 중인가&lt;/code&gt;, &lt;code&gt;TCP 포트까지 갈 수 있는가&lt;/code&gt;, &lt;code&gt;로그인이 허용되는가&lt;/code&gt;를 분리하는 것이 핵심입니다. 1433 포트를 무조건 열기보다 실제 인스턴스의 포트를 확인하고, 방화벽은 필요한 네트워크 범위에만 적용해야 합니다.&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;로컬은 성공, 원격 포트는 실패: TCP/IP&amp;middot;실제 포트&amp;middot;방화벽 확인&lt;/li&gt;
&lt;li&gt;포트는 성공, 18456 발생: 인증과 권한 확인&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;code&gt;TcpTestSucceeded&lt;/code&gt;와 로그인 성공을 분리해서 봐야 합니다. 포트까지 도달한 뒤에야 인증 오류를 볼 수 있고, 그 전에는 비밀번호를 바꿔도 아무 효과가 없습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/troubleshoot/sql/database-engine/connect/resolve-connectivity-errors-overview&quot;&gt;Microsoft Learn &amp;mdash; SQL Server 연결 문제 해결&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/sql/database-engine/configure-windows/configure-a-server-to-listen-on-a-specific-tcp-port?view=sql-server-ver16&quot;&gt;Microsoft Learn &amp;mdash; 특정 TCP 포트에서 수신하도록 SQL Server 구성&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/sql/sql-server/install/configure-the-windows-firewall-to-allow-sql-server-access?view=sql-server-ver16&quot;&gt;Microsoft Learn &amp;mdash; SQL Server 액세스를 위한 Windows 방화벽 구성&lt;/a&gt;&lt;/li&gt;
&lt;/ul&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;h2 data-ke-size=&quot;size26&quot;&gt;이전 글&amp;middot;다음 글 바로가기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;larr; &lt;a href=&quot;https://songbination.tistory.com/93&quot;&gt;&lt;b&gt;이전 글: MSSQL 로그인 실패 18456 해결 [MSSQL 31]&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;a href=&quot;https://songbination.tistory.com/78&quot;&gt;&lt;b&gt;MSSQL 입문&amp;middot;실무 시리즈 전체 목차&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://songbination.tistory.com/78&quot;&gt;&lt;b&gt;다음 글: MSSQL 입문&amp;middot;실무 전체 목차&lt;/b&gt;&lt;/a&gt; &amp;rarr;&lt;/p&gt;</description>
      <category>SQL &amp;middot; MES</category>
      <category>1433포트</category>
      <category>MSSQL</category>
      <category>SQLServer</category>
      <category>SSMS</category>
      <category>TCPIP</category>
      <category>TestNetConnection</category>
      <category>방화벽</category>
      <category>연결오류</category>
      <author>Songmin</author>
      <guid isPermaLink="true">https://songbination.tistory.com/97</guid>
      <comments>https://songbination.tistory.com/97#entry97comment</comments>
      <pubDate>Sun, 30 Aug 2026 18:00:23 +0900</pubDate>
    </item>
    <item>
      <title>Python에서 MSSQL 연결하기 &amp;mdash; pyodbc 설치&amp;middot;연결 오류 해결 [Python 실무 4]</title>
      <link>https://songbination.tistory.com/96</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;margin: 18px 0; padding: 14px 16px; border-left: 4px solid #2563eb; border-radius: 8px; background: #f8fafc; line-height: 1.8;&quot;&gt;&lt;b&gt;시리즈 안내&lt;/b&gt; &amp;middot; 이 글은 Python 실무 4입니다.&lt;br /&gt;&lt;a href=&quot;https://songbination.tistory.com/category/Python%20Automation&quot;&gt;Python Automation 전체 글 보기 &amp;rarr;&lt;/a&gt;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;pyodbc를 설치했는데도 연결되지 않을 때&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;pip install pyodbc&lt;/code&gt;까지 끝냈는데 SQL Server에는 연결되지 않을 때가 있습니다. 어떤 PC에서는 &lt;code&gt;IM002&lt;/code&gt;가 뜨고, 드라이버를 설치하고 나면 이번에는 &lt;code&gt;08001&lt;/code&gt;, 서버에 도착하고 나면 &lt;code&gt;18456&lt;/code&gt;이 나옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류 메시지가 계속 바뀌면 문제가 더 생긴 것처럼 보입니다. 그런데 연결 과정으로 보면 한 단계를 통과할 때마다 다음 오류가 보이는 것입니다. Python 패키지, ODBC Driver, 서버&amp;middot;포트, 암호화와 로그인은 서로 다른 층입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 이 순서를 그대로 따라갑니다. 먼저 설치된 드라이버를 확인하고, Windows 인증으로 가장 작은 쿼리를 실행한 뒤, 오류 코드가 가리키는 단계만 좁혀 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시리즈 안내&lt;/b&gt; 이 글은 「Python 실무」 시리즈의 4번째 글입니다. 3편에서 여러 엑셀 파일을 합쳤다면, 이번에는 파일을 거치지 않고 MSSQL에서 필요한 데이터만 직접 읽는 단계로 넘어갑니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 서버명과 데이터는 설명용 예시입니다. 실제 계정, 비밀번호와 IP 주소는 코드나 화면 캡처에 남기지 않습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 먼저 필요한 구성요소&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python 패키지를 설치합니다.&lt;/p&gt;
&lt;pre class=&quot;mel&quot;&gt;&lt;code&gt;python -m pip install pyodbc python-dotenv&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;pyodbc&lt;/code&gt;와 별도로 Microsoft ODBC Driver for SQL Server도 PC에 설치되어 있어야 합니다. 설치된 드라이버는 다음 코드로 확인할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;import pyodbc
​
for driver in pyodbc.drivers():
 &amp;nbsp;  print(driver)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과에 &lt;code&gt;ODBC Driver 18 for SQL Server&lt;/code&gt;와 같은 항목이 없다면 Python 코드보다 드라이버 설치 여부를 먼저 확인합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Windows 인증으로 연결하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인 PC의 로컬 테스트나 도메인 환경에서는 Windows 인증을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import pyodbc
​
connection_string = (
 &amp;nbsp;  &quot;DRIVER={ODBC Driver 18 for SQL Server};&quot;
 &amp;nbsp;  &quot;SERVER=localhost,1433;&quot;
 &amp;nbsp;  &quot;DATABASE=BlogDemoDB;&quot;
 &amp;nbsp;  &quot;Trusted_Connection=yes;&quot;
 &amp;nbsp;  &quot;Encrypt=yes;&quot;
 &amp;nbsp;  &quot;TrustServerCertificate=yes;&quot;
)
​
with pyodbc.connect(connection_string, timeout=5) as connection:
 &amp;nbsp;  cursor = connection.cursor()
 &amp;nbsp;  cursor.execute(&quot;SELECT DB_NAME(), SUSER_SNAME(), GETDATE()&quot;)
 &amp;nbsp;  database_name, login_name, server_time = cursor.fetchone()
​
 &amp;nbsp;  print(database_name, login_name, server_time)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;TrustServerCertificate=yes&lt;/code&gt;는 자체 서명 인증서를 사용하는 로컬&amp;middot;검증 환경에서 연결을 확인하기 위한 예시입니다. 운영 환경에서는 유효한 서버 인증서를 구성하고 인증서 검증을 유지하는 편이 안전합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. SQL Server 인증 정보는 코드에서 분리하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL Server 로그인을 사용해야 한다면 비밀번호를 소스에 직접 적지 않습니다. 예를 들어 &lt;code&gt;.env&lt;/code&gt; 파일을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;DB_SERVER=localhost,1433
DB_NAME=BlogDemoDB
DB_USER=demo_reader
DB_PASSWORD=여기에_로컬_비밀번호&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;import os
from dotenv import load_dotenv
import pyodbc
​
load_dotenv()
​
connection_string = (
 &amp;nbsp;  &quot;DRIVER={ODBC Driver 18 for SQL Server};&quot;
 &amp;nbsp;  f&quot;SERVER={os.environ['DB_SERVER']};&quot;
 &amp;nbsp;  f&quot;DATABASE={os.environ['DB_NAME']};&quot;
 &amp;nbsp;  f&quot;UID={os.environ['DB_USER']};&quot;
 &amp;nbsp;  f&quot;PWD={os.environ['DB_PASSWORD']};&quot;
 &amp;nbsp;  &quot;Encrypt=yes;&quot;
 &amp;nbsp;  &quot;TrustServerCertificate=yes;&quot;
)
​
with pyodbc.connect(connection_string, timeout=5) as connection:
 &amp;nbsp;  print(&quot;연결 성공&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;.env&lt;/code&gt;는 Git 저장소에 올리지 않고 &lt;code&gt;.gitignore&lt;/code&gt;에 추가합니다. 운영 환경에서는 환경 변수나 별도의 비밀정보 관리 도구를 사용하는 것이 더 적합합니다.&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;문자열을 이어 붙여 SQL을 만들면 따옴표 오류뿐 아니라 SQL 삽입 위험도 생깁니다. &lt;code&gt;pyodbc&lt;/code&gt;에서는 값이 들어갈 자리에 &lt;code&gt;?&lt;/code&gt;를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from datetime import datetime, timedelta
import pyodbc
​
since = datetime.now() - timedelta(days=1)
equipment_code = &quot;EQ-DEMO-01&quot;
​
query = &quot;&quot;&quot;
SELECT LoggedAt, EquipmentCode, CycleSeconds
FROM dbo.CycleLog
WHERE LoggedAt &amp;gt;= ?
  AND EquipmentCode = ?
ORDER BY LoggedAt;
&quot;&quot;&quot;
​
with pyodbc.connect(connection_string, timeout=5) as connection:
 &amp;nbsp;  cursor = connection.cursor()
 &amp;nbsp;  cursor.execute(query, since, equipment_code)
​
 &amp;nbsp;  for row in cursor.fetchall():
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  print(row.LoggedAt, row.EquipmentCode, row.CycleSeconds)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블명이나 열 이름 같은 SQL 식별자는 &lt;code&gt;?&lt;/code&gt;로 전달할 수 없습니다. 식별자를 동적으로 선택해야 한다면 프로그램 내부의 허용 목록과 대조한 뒤 사용합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 오류 메시지별 점검 순서&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;IM002&lt;/code&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;pyodbc.drivers()&lt;/code&gt; 결과와 정확히 같은지 확인합니다.&lt;/li&gt;
&lt;li&gt;Python과 ODBC 드라이버의 32비트&amp;middot;64비트 구성이 맞는지 확인합니다.&lt;/li&gt;
&lt;li&gt;Microsoft ODBC Driver가 실제로 설치되어 있는지 확인합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;08001&lt;/code&gt;, 서버를 찾을 수 없거나 액세스할 수 없음&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SQL Server 서비스가 실행 중인지 확인합니다.&lt;/li&gt;
&lt;li&gt;서버명과 인스턴스명, 포트가 맞는지 확인합니다.&lt;/li&gt;
&lt;li&gt;TCP/IP 사용 여부와 방화벽 허용 범위를 확인합니다.&lt;/li&gt;
&lt;li&gt;원격 연결 전에는 같은 PC에서 먼저 연결해 네트워크 문제와 SQL 문제를 분리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;18456&lt;/code&gt;, 로그인 실패&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Windows 인증과 SQL Server 인증 중 어떤 방식을 사용하는지 확인합니다.&lt;/li&gt;
&lt;li&gt;로그인 이름&amp;middot;비밀번호&amp;middot;기본 데이터베이스 상태를 확인합니다.&lt;/li&gt;
&lt;li&gt;SQL Server 오류 로그의 상태 코드를 함께 확인하면 원인을 더 좁힐 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인증서 체인을 신뢰할 수 없음&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ODBC Driver 18 계열에서는 암호화 설정 때문에 이전 드라이버에서 없던 인증서 오류가 보일 수 있습니다. 운영 환경이라면 신뢰할 수 있는 인증서를 구성하는 것이 우선입니다. 로컬 테스트에서만 목적과 위험을 알고 &lt;code&gt;TrustServerCertificate=yes&lt;/code&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;처음부터 대용량 테이블을 읽기보다 아래처럼 연결과 권한만 확인하는 쿼리부터 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SELECT
 &amp;nbsp;  @@SERVERNAME AS ServerName,
 &amp;nbsp;  DB_NAME() AS DatabaseName,
 &amp;nbsp;  SUSER_SNAME() AS LoginName,
 &amp;nbsp;  GETDATE() AS ServerTime;&lt;/code&gt;&lt;/pre&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;code&gt;SELECT TOP (10)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;시간 범위 조건을 포함한 조회&lt;/li&gt;
&lt;li&gt;필요한 열만 선택한 실제 조회&lt;/li&gt;
&lt;li&gt;pandas 적재와 후속 가공&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;오늘의 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python에서 MSSQL 연결 오류가 발생하면 코드를 계속 바꾸기보다 &lt;code&gt;ODBC 드라이버 &amp;rarr; 서버&amp;middot;포트 &amp;rarr; 암호화 &amp;rarr; 인증 &amp;rarr; 데이터베이스 권한&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;IM002&lt;/code&gt;: 설치된 ODBC Driver 이름부터 확인&lt;/li&gt;
&lt;li&gt;&lt;code&gt;08001&lt;/code&gt;: 서버&amp;middot;인스턴스&amp;middot;포트와 TCP 경로 확인&lt;/li&gt;
&lt;li&gt;&lt;code&gt;18456&lt;/code&gt;: 인증 모드와 로그인 상태 확인&lt;/li&gt;
&lt;li&gt;인증서 오류: 암호화를 끄기보다 인증서 구성과 검증 조건 확인&lt;/li&gt;
&lt;li&gt;비밀번호는 코드에서 분리하고 쿼리 값은 &lt;code&gt;?&lt;/code&gt;로 전달&lt;/li&gt;
&lt;/ul&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-gb/sql/connect/python/pyodbc/python-sql-driver-pyodbc-quickstart?view=sql-server-ver16&quot;&gt;Microsoft Learn &amp;mdash; Python SQL 드라이버 및 pyodbc 빠른 시작&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/sql/connect/odbc/dsn-connection-string-attribute?view=sql-server-ver17&quot;&gt;Microsoft Learn &amp;mdash; ODBC 연결 문자열의 Encrypt&amp;middot;TrustServerCertificate 속성&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mkleehammer/pyodbc/wiki&quot;&gt;pyodbc 프로젝트 문서&lt;/a&gt;&lt;/li&gt;
&lt;/ul&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;08001&lt;/code&gt;에서 더 이상 넘어가지 못한다면 Python 코드를 계속 고칠 차례가 아닙니다. 다음 글에서는 같은 PC의 로컬 연결부터 시작해 TCP/IP, 실제 수신 포트와 방화벽을 하나씩 분리합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이전 글&amp;middot;다음 글 바로가기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;larr; &lt;a href=&quot;https://songbination.tistory.com/91&quot;&gt;&lt;b&gt;이전 글: Python으로 여러 엑셀 파일 합치기 [Python 실무 3]&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;a href=&quot;https://songbination.tistory.com/79&quot;&gt;&lt;b&gt;Python 입문&amp;middot;자동화 시리즈 전체 목차&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://songbination.tistory.com/79&quot;&gt;&lt;b&gt;다음 글: Python 입문&amp;middot;자동화 전체 목차&lt;/b&gt;&lt;/a&gt; &amp;rarr;&lt;/p&gt;</description>
      <category>Python Automation</category>
      <category>MSSQL</category>
      <category>ODBCDriver18</category>
      <category>pyodbc</category>
      <category>Python</category>
      <category>SQLServer</category>
      <category>데이터베이스연결</category>
      <category>파이썬자동화</category>
      <author>Songmin</author>
      <guid isPermaLink="true">https://songbination.tistory.com/96</guid>
      <comments>https://songbination.tistory.com/96#entry96comment</comments>
      <pubDate>Sat, 29 Aug 2026 18:00:16 +0900</pubDate>
    </item>
    <item>
      <title>Tableau 날짜별&amp;middot;월별 그룹화 &amp;mdash; DATEPART&amp;middot;DATETRUNC 차이 [Tableau 6]</title>
      <link>https://songbination.tistory.com/95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;margin: 18px 0; padding: 14px 16px; border-left: 4px solid #2563eb; border-radius: 8px; background: #f8fafc; line-height: 1.8;&quot;&gt;&lt;b&gt;시리즈 안내&lt;/b&gt; &amp;middot; 이 글은 Tableau 6입니다.&lt;br /&gt;&lt;a href=&quot;https://songbination.tistory.com/category/Tableau&quot;&gt;Tableau 전체 글 보기 &amp;rarr;&lt;/a&gt;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;월별 차트인데 여러 해의 1월이 합쳐진 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tableau에서 날짜 필드를 열에 놓고 &lt;code&gt;월&lt;/code&gt;을 선택했더니 2025년 1월과 2026년 1월이 하나로 합쳐질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 월을 &lt;code&gt;1~12&lt;/code&gt;라는 날짜 부분으로 사용했을 가능성이 큽니다. 월별 추세를 보려면 &lt;code&gt;2026-08-01&lt;/code&gt;처럼 연도까지 포함된 날짜 값으로 묶어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시리즈 안내&lt;/b&gt; 이 글은 「Tableau 실무」 시리즈의 6번째 글입니다. 5편에서 비어 있는 값을 구분했다면, 이번에는 값은 있는데 날짜 수준을 잘못 골라 서로 다른 해의 같은 달이 합쳐지는 문제를 다룹니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DATEPART: 날짜의 한 부분을 숫자로 반환&lt;/h2&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;DATEPART('month', [기록시각])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과는 1부터 12까지의 숫자입니다. 연도 정보가 포함되지 않으므로 여러 해의 같은 달을 계절별로 비교할 때 적합합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;DATEPART('hour', [기록시각])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간대별 설비 패턴을 0~23시로 비교할 때도 사용할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DATETRUNC: 지정한 수준의 날짜로 맞추기&lt;/h2&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;DATETRUNC('month', [기록시각])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 8월의 모든 날짜를 &lt;code&gt;2026-08-01&lt;/code&gt;이라는 월 시작일로 변환합니다. 반환값이 날짜이므로 시간 순서가 유지됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;월별 추세에는 다음 계산 필드를 권장합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;// 월 시작일
DATETRUNC('month', [기록시각])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주별 추세:&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;DATETRUNC('week', [기록시각], 'monday')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 원본의 주 시작일 설정에 따라 결과가 달라질 수 있으므로 월요일 또는 일요일 기준을 명시합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DATEPART와 DATETRUNC 비교&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;목적&lt;/th&gt;
&lt;th&gt;함수&lt;/th&gt;
&lt;th&gt;결과 예시&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;여러 해의 1월끼리 비교&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DATEPART('month', 날짜)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025-01과 2026-01을 구분&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DATETRUNC('month', 날짜)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2026-01-01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;시간대별 분포&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DATEPART('hour', 날짜시간)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;일별 시계열&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DATETRUNC('day', 날짜시간)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2026-08-28&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;불연속 날짜와 연속 날짜&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tableau에서 파란색 날짜 필드는 불연속 값으로 머리글을 만들고, 초록색 날짜 필드는 연속 축을 만듭니다.&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;code&gt;월&lt;/code&gt;이라는 표시라도 불연속&amp;middot;연속 선택에 따라 차트 구조가 달라집니다.&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;[기록시각]&lt;/code&gt;이 DateTime이라면 종료일 자정 이후 데이터가 빠질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 8월 31일까지 조회하려고 다음 조건을 사용하면:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;[기록시각] &amp;lt;= #2026-08-31#&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8월 31일 00:00:00 이후 데이터는 제외될 수 있습니다. 다음 달 시작 전까지로 조건을 쓰는 편이 명확합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;[기록시각] &amp;gt;= #2026-08-01#
AND [기록시각] &amp;lt; #2026-09-01#&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문자열 날짜라면 먼저 변환 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열을 날짜처럼 표시만 바꿔서는 날짜 계층과 필터를 제대로 사용할 수 없습니다. 원본에서 Date 또는 DateTime 형식인지 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;DATEPARSE(&quot;yyyy-MM-dd HH:mm:ss&quot;, [기록시각_문자열])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;DATEPARSE&lt;/code&gt; 지원 여부는 데이터 원본에 따라 다를 수 있습니다. 가능하면 SQL 또는 데이터 준비 단계에서 날짜형으로 변환합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;월별 설비 가동률 예시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계산 필드 1:&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;// 월
DATETRUNC('month', [기록시각])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계산 필드 2:&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;// 가동률
SUM([가동시간]) / SUM([계획시간])&lt;/code&gt;&lt;/pre&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;yyyy-MM&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;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;행은 있지만 측정값이 NULL&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;이전 글의 NULL 처리와 함께 확인합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;오늘의 정리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;날짜의 일부 숫자: &lt;code&gt;DATEPART&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;해당 기간의 시작 날짜: &lt;code&gt;DATETRUNC&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;여러 해의 월별 추세: &lt;code&gt;DATETRUNC('month', 날짜)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;시간대 비교: &lt;code&gt;DATEPART('hour', 날짜시간)&lt;/code&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;code&gt;DATEPART('month', ...)&lt;/code&gt;는 1부터 12까지의 숫자이고, 여러 해의 추세에는 연도까지 남는 &lt;code&gt;DATETRUNC('month', ...)&lt;/code&gt;가 맞습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://help.tableau.com/current/pro/desktop/en-us/functions_functions_date.htm&quot;&gt;Tableau 공식 도움말 &amp;mdash; Date Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://help.tableau.com/current/pro/desktop/en-us/dates_custom.htm&quot;&gt;Tableau 공식 도움말 &amp;mdash; Custom Dates&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다음 글 예고&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜 수준을 바로잡으면 데이터가 늘어날수록 화면이 느려지는 문제가 남습니다. 다음 Tableau 글에서는 라이브 연결과 추출을 다시 비교하고, 갱신 주기와 성능 사이의 기준을 정리합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이전 글&amp;middot;다음 글 바로가기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;larr; &lt;a href=&quot;https://songbination.tistory.com/90&quot;&gt;&lt;b&gt;이전 글: Tableau NULL 처리 방법 [Tableau 5]&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;a href=&quot;https://songbination.tistory.com/82&quot;&gt;&lt;b&gt;Tableau 실무 시리즈 전체 목차&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://songbination.tistory.com/82&quot;&gt;&lt;b&gt;다음 글: Tableau 실무 시리즈 전체 목차&lt;/b&gt;&lt;/a&gt; &amp;rarr;&lt;/p&gt;</description>
      <category>Tableau</category>
      <category>datepart</category>
      <category>DATETRUNC</category>
      <category>tableau</category>
      <category>날짜필터</category>
      <category>날짜함수</category>
      <category>시계열</category>
      <category>월별그룹화</category>
      <author>Songmin</author>
      <guid isPermaLink="true">https://songbination.tistory.com/95</guid>
      <comments>https://songbination.tistory.com/95#entry95comment</comments>
      <pubDate>Fri, 28 Aug 2026 18:00:02 +0900</pubDate>
    </item>
    <item>
      <title>Windows 작업 스케줄러로 Python 자동 실행하기 [데이터 자동화 6]</title>
      <link>https://songbination.tistory.com/94</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;margin: 18px 0; padding: 14px 16px; border-left: 4px solid #2563eb; border-radius: 8px; background: #f8fafc; line-height: 1.8;&quot;&gt;&lt;b&gt;시리즈 안내&lt;/b&gt; &amp;middot; 이 글은 데이터 자동화 6입니다.&lt;br /&gt;&lt;a href=&quot;https://songbination.tistory.com/category/PdM%28Predictive%20Maintenance%29&quot;&gt;예지보전&amp;middot;설비 데이터 전체 글 보기 &amp;rarr;&lt;/a&gt;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;터미널에서는 되는데 예약 실행만 실패하는 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python 파일을 직접 실행하면 정상인데 Windows 작업 스케줄러에서는 바로 종료되거나 결과 파일이 생기지 않을 수 있습니다.&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;사용한 Python 인터프리터가 다름&lt;/li&gt;
&lt;li&gt;가상환경이 적용되지 않음&lt;/li&gt;
&lt;li&gt;로그인한 사용자와 작업 실행 계정의 권한이 다름&lt;/li&gt;
&lt;li&gt;네트워크 드라이브와 환경변수를 찾지 못함&lt;/li&gt;
&lt;li&gt;오류가 화면에 표시되지 않고 종료됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 예약 실행 전에 경로와 로그를 명시해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시리즈 안내&lt;/b&gt; 이 글은 「설비 데이터 수집&amp;middot;자동화」 시리즈의 6번째 글입니다. 5편에서 CSV를 안정적으로 읽었다면, 이번에는 그 코드를 사람이 로그인해 있지 않은 시간에도 같은 환경으로 실행되게 만듭니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;테스트용 Python 스크립트&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from datetime import datetime
from pathlib import Path
import logging
import sys
​
BASE_DIR = Path(__file__).resolve().parent
LOG_DIR = BASE_DIR / &quot;logs&quot;
LOG_DIR.mkdir(exist_ok=True)
​
logging.basicConfig(
 &amp;nbsp;  filename=LOG_DIR / &quot;daily_job.log&quot;,
 &amp;nbsp;  level=logging.INFO,
 &amp;nbsp;  format=&quot;%(asctime)s %(levelname)s %(message)s&quot;,
 &amp;nbsp;  encoding=&quot;utf-8&quot;,
)
​
​
def main() -&amp;gt; None:
 &amp;nbsp;  output = BASE_DIR / &quot;last_run.txt&quot;
 &amp;nbsp;  output.write_text(
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  datetime.now().isoformat(),
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  encoding=&quot;utf-8&quot;,
 &amp;nbsp;  )
 &amp;nbsp;  logging.info(&quot;작업 완료: %s&quot;, output)
​
​
if __name__ == &quot;__main__&quot;:
 &amp;nbsp;  try:
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  main()
 &amp;nbsp;  except Exception:
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  logging.exception(&quot;작업 실패&quot;)
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  sys.exit(1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Path(__file__).resolve().parent&lt;/code&gt;를 기준 폴더로 사용하면 실행 위치가 달라도 파일 경로가 안정적입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;어떤 Python을 실행하는지 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서 다음을 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;llvm&quot;&gt;&lt;code&gt;python -c &quot;import sys; print(sys.executable)&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시:&lt;/p&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;C:\BlogAutomation\.venv\Scripts\python.exe&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 스케줄러에는 &lt;code&gt;python&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;code&gt;작업 만들기&lt;/code&gt;에서 다음처럼 입력합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로그램/스크립트&lt;/h3&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;C:\BlogAutomation\.venv\Scripts\python.exe&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인수 추가&lt;/h3&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;&quot;C:\BlogAutomation\daily_job.py&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시작 위치&lt;/h3&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;C:\BlogAutomation&lt;/code&gt;&lt;/pre&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;여러 명령이 필요하거나 실행 결과를 별도 로그로 남기고 싶다면 배치 파일을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;@echo off
cd /d C:\BlogAutomation
​
C:\BlogAutomation\.venv\Scripts\python.exe ^
  C:\BlogAutomation\daily_job.py ^
 &amp;nbsp;&amp;gt;&amp;gt; C:\BlogAutomation\logs\scheduler_stdout.log 2&amp;gt;&amp;amp;1
​
exit /b %ERRORLEVEL%&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 스케줄러의 프로그램에는 이 배치 파일의 절대경로를 지정합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;schtasks 명령으로 등록하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows의 &lt;code&gt;schtasks&lt;/code&gt; 명령으로도 작업을 만들 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;schtasks /create `
  /tn &quot;BlogDemo-PythonDailyJob&quot; `
  /tr &quot;C:\BlogAutomation\run_daily_job.bat&quot; `
  /sc daily `
  /st 18:00 `
  /f&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등록 후 수동으로 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;schtasks /run /tn &quot;BlogDemo-PythonDailyJob&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태를 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;schtasks /query /tn &quot;BlogDemo-PythonDailyJob&quot; /v /fo list&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;schtasks&lt;/code&gt;는 지정한 프로그램 경로나 계정 비밀번호가 실제로 올바른지 등록 시점에 완전히 검증하지 않을 수 있습니다. 작업이 생성됐다는 메시지만 보고 성공으로 판단하지 않습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;0x1과 경로 오류 점검&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 결과가 &lt;code&gt;0x1&lt;/code&gt;이라면 Python에서 1을 반환했거나 배치 파일&amp;middot;경로&amp;middot;권한 문제로 일반 실패가 발생했을 수 있습니다.&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;Python 실행 파일 절대경로&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;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;작업을 실행하는 계정에는 다음 권한이 필요합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Python과 스크립트 파일 읽기&amp;middot;실행&lt;/li&gt;
&lt;li&gt;입력 폴더 읽기&lt;/li&gt;
&lt;li&gt;출력&amp;middot;로그 폴더 쓰기&lt;/li&gt;
&lt;li&gt;필요한 네트워크 자원 접근&lt;/li&gt;
&lt;/ul&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;PC가 완전히 절전 또는 종료돼 있으면 일반적인 로컬 작업은 실행되지 않습니다. 작업 조건에서 절전 모드 해제 여부를 검토하고, 노트북은 전원 조건도 확인합니다.&lt;/p&gt;
&lt;section data-songbination-revision=&quot;260820&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예약 실행에서만 실패했던 실제 원인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;손으로 실행하면 되는데 작업 스케줄러에서는 아무 로그 없이 멈추는 일이 있었습니다. Python 실행 파일과 작업 디렉터리를 절대 경로로 지정하지 않은 것, 한글이 든 배치 파일, 네트워크 공유에 필요한 로그온 세션이 주요 원인이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자정 부근 작업은 절전과 잠금으로 밀려 앞 작업이 끝나기 전에 다음 작업이 시작됐고, 스케줄러가 장기 작업을 유실로 판단해 한 번 더 실행하면서 산출물이 겹친 적도 있었습니다. 최종 설정은 부팅&amp;middot;로그온&amp;middot;주기 감시자 트리거, 중복 실행 금지, 실행 시간 제한 해제, 락 파일의 조합이었습니다.&lt;/p&gt;
&lt;ul style=&quot;line-height: 1.9;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Python 실행 파일과 스크립트 경로를 모두 절대 경로로 지정&lt;/li&gt;
&lt;li&gt;작업 디렉터리를 명시하고 코드에서도 파일 기준 경로 사용&lt;/li&gt;
&lt;li&gt;표준 출력&amp;middot;오류를 파일에 남기고 실패 로그 분리&lt;/li&gt;
&lt;li&gt;놓친 회차는 소급하지 않고 다음 정시부터 재개&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows 잠금 자체보다 로그오프&amp;middot;절전&amp;middot;앱 종료가 문제였습니다. 운영 PC에서는 며칠 뒤 설정이 정책으로 되돌아오지 않았는지도 재확인했습니다.&lt;/p&gt;
&lt;/section&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;오늘의 정리&lt;/h2&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;터미널 실행 확인
&amp;rarr; sys.executable 확인
&amp;rarr; 모든 경로를 절대경로로 지정
&amp;rarr; 시작 위치 지정
&amp;rarr; 로그와 종료 코드 추가
&amp;rarr; 예약 작업 수동 실행
&amp;rarr; 작업 기록과 Python 로그 함께 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예약 실행에서 가장 쓸모 있는 것은 성공 팝업이 아니라 남아 있는 로그입니다. &lt;code&gt;python.exe&lt;/code&gt;, 스크립트, 시작 위치를 각각 절대경로로 적고 출력 로그를 남겨야 화면 없는 실행을 다시 확인할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/taskschd/schtasks&quot;&gt;Microsoft Learn &amp;mdash; schtasks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/schtasks-create&quot;&gt;Microsoft Learn &amp;mdash; schtasks create&lt;/a&gt;&lt;/li&gt;
&lt;/ul&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;h2 data-ke-size=&quot;size26&quot;&gt;이전 글&amp;middot;다음 글 바로가기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;larr; &lt;a href=&quot;https://songbination.tistory.com/88&quot;&gt;&lt;b&gt;이전 글: CSV 한글 깨짐 해결 [데이터 자동화 5]&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;a href=&quot;https://songbination.tistory.com/81&quot;&gt;&lt;b&gt;설비 데이터 수집&amp;middot;자동화 시리즈 전체 목차&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://songbination.tistory.com/81&quot;&gt;&lt;b&gt;다음 글: 예지보전 실무&amp;middot;이론 전체 목차&lt;/b&gt;&lt;/a&gt; &amp;rarr;&lt;/p&gt;</description>
      <category>PdM(Predictive Maintenance)</category>
      <category>Python</category>
      <category>Python예약실행</category>
      <category>schtasks</category>
      <category>Windows자동화</category>
      <category>업무자동화</category>
      <category>작업스케줄러</category>
      <author>Songmin</author>
      <guid isPermaLink="true">https://songbination.tistory.com/94</guid>
      <comments>https://songbination.tistory.com/94#entry94comment</comments>
      <pubDate>Thu, 27 Aug 2026 18:00:55 +0900</pubDate>
    </item>
    <item>
      <title>MSSQL 로그인 실패 18456 해결 &amp;mdash; 인증 모드&amp;middot;사용자 권한 점검 [MSSQL 31]</title>
      <link>https://songbination.tistory.com/93</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;margin: 18px 0; padding: 14px 16px; border-left: 4px solid #2563eb; border-radius: 8px; background: #f8fafc; line-height: 1.8;&quot;&gt;&lt;b&gt;시리즈 안내&lt;/b&gt; &amp;middot; 이 글은 MSSQL 31입니다.&lt;br /&gt;&lt;a href=&quot;https://songbination.tistory.com/category/SQL%20%C2%B7%20MES&quot;&gt;SQL&amp;middot;MES 전체 글 보기 &amp;rarr;&lt;/a&gt;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;같은 18456이라도 원인은 하나가 아니다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSMS에서 SQL Server에 연결할 때 다음 오류가 나타날 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Login failed for user 'blogdemo_login'.
Microsoft SQL Server, Error: 18456&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트 화면은 보안을 위해 자세한 실패 원인을 숨기는 경우가 많습니다. 비밀번호만 반복해서 입력하기보다 SQL Server 오류 로그에 기록된 &lt;code&gt;State&lt;/code&gt;와 이유를 확인해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시리즈 안내&lt;/b&gt; 이 글은 「MSSQL 입문&amp;middot;실무」 시리즈의 31번째 글입니다. 30편에서 설치 로그로 실패 위치를 찾았다면, 이번에는 서버는 살아 있는데 로그인 단계에서 거절되는 경우만 따로 봅니다.&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;Windows 인증과 SQL Server 인증 중 무엇을 사용하는가&lt;/li&gt;
&lt;li&gt;해당 로그인이 서버에 존재하고 활성화돼 있는가&lt;/li&gt;
&lt;li&gt;로그인에 지정된 기본 데이터베이스가 사용 가능한가&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SQL Server 인증 모드 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL Server가 Windows 인증 전용인데 SQL 로그인으로 접속하면 실패합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SELECT
 &amp;nbsp;  SERVERPROPERTY('IsIntegratedSecurityOnly')
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  AS IsWindowsAuthenticationOnly;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과가 &lt;code&gt;1&lt;/code&gt;이면 Windows 인증 전용입니다. &lt;code&gt;0&lt;/code&gt;이면 Windows 인증과 SQL Server 인증을 함께 사용하는 혼합 모드입니다.&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;관리 권한이 있는 계정으로 접속해 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SELECT
 &amp;nbsp;  name,
 &amp;nbsp;  type_desc,
 &amp;nbsp;  is_disabled,
 &amp;nbsp;  default_database_name
FROM sys.server_principals
WHERE name = N'blogdemo_login';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과가 없다면 해당 SQL Server 인스턴스에 로그인이 없는 것입니다. 개발 서버의 연결 문자열을 운영 서버에 그대로 사용했거나 인스턴스 이름을 잘못 지정했을 때도 이런 상황이 생깁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비활성화된 로그인을 다시 사용할 필요가 있고 보안 승인을 받았다면 다음처럼 활성화할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;ALTER LOGIN [blogdemo_login] ENABLE;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계정이 왜 비활성화됐는지 먼저 확인합니다. 사용하지 않는 계정이나 차단된 계정을 오류 해결 목적으로 바로 활성화하지 않습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;오류 로그의 State 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL Server 오류 로그에는 클라이언트보다 자세한 State가 남습니다. 대표적인 구분은 다음과 같습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;대표적인 의미&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2, 5&lt;/td&gt;
&lt;td&gt;로그인 이름을 찾지 못함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;로그인 비활성 또는 비밀번호 문제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;비밀번호 불일치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11, 12&lt;/td&gt;
&lt;td&gt;로그인은 유효하지만 서버 접근 실패&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;비밀번호 변경 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;38, 46&lt;/td&gt;
&lt;td&gt;요청한 데이터베이스를 열 수 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;58&lt;/td&gt;
&lt;td&gt;Windows 인증 전용 서버에 SQL 인증으로 접속&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;State만으로 모든 상황을 단정하지 말고 같은 시각의 오류 로그 이유 문장과 함께 확인합니다.&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;SSMS의 연결 속성에서 임시로 &lt;code&gt;master&lt;/code&gt;를 지정해 접속을 시험하거나 관리자가 기본 데이터베이스를 변경합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;ALTER LOGIN [blogdemo_login]
WITH DEFAULT_DATABASE = [master];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 원래 사용할 데이터베이스의 상태와 권한을 수정해야 합니다. &lt;code&gt;master&lt;/code&gt;로 바꾸는 것은 원인 확인 단계이지 업무 DB 권한을 해결한 것은 아닙니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;로그인과 데이터베이스 사용자는 다르다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 로그인에 성공해도 특정 데이터베이스에 대응하는 사용자가 없으면 접근할 수 없습니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;USE [BlogDemoDB];
GO
​
SELECT
 &amp;nbsp;  name,
 &amp;nbsp;  type_desc
FROM sys.database_principals
WHERE name = N'blogdemo_login';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요하고 승인된 계정이라면 로그인에 대응하는 사용자를 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;USE [BlogDemoDB];
GO
​
CREATE USER [blogdemo_login]
FOR LOGIN [blogdemo_login];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 최소 권한 원칙에 따라 필요한 역할 또는 객체 권한만 부여합니다. 문제 해결을 위해 바로 &lt;code&gt;sysadmin&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버&amp;middot;인스턴스 이름&lt;/li&gt;
&lt;li&gt;데이터베이스 이름&lt;/li&gt;
&lt;li&gt;Windows 인증 또는 SQL 인증 설정&lt;/li&gt;
&lt;li&gt;사용자 이름 앞뒤 공백&lt;/li&gt;
&lt;li&gt;배포 환경의 비밀값이 최신인지&lt;/li&gt;
&lt;li&gt;연결 문자열을 생성하는 환경변수와 설정 파일&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비밀번호를 코드와 로그에 출력하지 않습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;sa 계정부터 켜지 않기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 결과에서 &lt;code&gt;sa를 활성화하고 비밀번호를 바꾸라&lt;/code&gt;는 해결법을 자주 볼 수 있습니다. 하지만 18456의 원인이 서버 주소, 기본 DB 또는 사용자 매핑이라면 sa 활성화와 관계가 없습니다.&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;pre class=&quot;excel&quot;&gt;&lt;code&gt;서버&amp;middot;인스턴스 확인
&amp;rarr; 인증 방식 확인
&amp;rarr; SQL 오류 로그의 State 확인
&amp;rarr; 로그인 존재&amp;middot;활성 상태 확인
&amp;rarr; 비밀번호 또는 Windows 계정 확인
&amp;rarr; 기본 DB 상태 확인
&amp;rarr; DB 사용자 매핑과 최소 권한 확인&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;오늘의 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류 18456은 &lt;code&gt;로그인 실패&lt;/code&gt;라는 결과를 나타내며 원인은 여러 가지입니다. 클라이언트 메시지만 보지 말고 서버 오류 로그의 State와 이유를 확인한 뒤 인증 모드, 로그인, 기본 DB와 사용자 매핑을 순서대로 점검해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;18456을 보면 비밀번호부터 의심하게 됩니다. 하지만 같은 시각의 서버 오류 로그를 먼저 보면 잘못된 서버, 인증 모드, 비활성 로그인과 기본 데이터베이스 문제를 서로 나눌 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/sql/relational-databases/errors-events/mssqlserver-18456-database-engine-error&quot;&gt;Microsoft Learn &amp;mdash; MSSQLSERVER_18456&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다음 글 예고&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 창까지 가지 못하고 서버 자체를 찾을 수 없다는 메시지가 뜬다면 인증보다 앞단의 문제입니다. 다음 글에서는 로컬 연결, TCP/IP, 실제 포트와 방화벽을 차례로 확인합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이전 글&amp;middot;다음 글 바로가기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;larr; &lt;a href=&quot;https://songbination.tistory.com/89&quot;&gt;&lt;b&gt;이전 글: SQL Server 2022 설치 오류 해결 [MSSQL 30]&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;a href=&quot;https://songbination.tistory.com/78&quot;&gt;&lt;b&gt;MSSQL 입문&amp;middot;실무 시리즈 전체 목차&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://songbination.tistory.com/97&quot;&gt;&lt;b&gt;다음 글: MSSQL 연결 안 됨 해결 [MSSQL 32]&lt;/b&gt;&lt;/a&gt; &amp;rarr;&lt;/p&gt;</description>
      <category>SQL &amp;middot; MES</category>
      <category>LoginFailed</category>
      <category>MSSQL</category>
      <category>SQLServer</category>
      <category>SQL인증</category>
      <category>SSMS연결</category>
      <category>사용자권한</category>
      <category>오류18456</category>
      <author>Songmin</author>
      <guid isPermaLink="true">https://songbination.tistory.com/93</guid>
      <comments>https://songbination.tistory.com/93#entry93comment</comments>
      <pubDate>Wed, 26 Aug 2026 18:00:48 +0900</pubDate>
    </item>
    <item>
      <title>PLC D메모리 쓰기를 하지 않은 이유 &amp;mdash; 읽기 전용과 안전 설계 [PLC&amp;middot;FINS 12]</title>
      <link>https://songbination.tistory.com/92</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 PLC 쓰기 방법을 안내하는 글이 아닙니다. 실제 프로젝트에서 D메모리 쓰기를 검토했지만 &lt;b&gt;가동 설비에는 한 번도 실행하지 않은 이유&lt;/b&gt;를 기록한 회고입니다. 공개 글에는 외부에서 그대로 재현할 수 있는 명령 코드·메모리 주소·프레임·절환 순서를 싣지 않습니다.&lt;/p&gt;&lt;div style=&quot;margin:18px 0;padding:14px 16px;border-left:4px solid #2563eb;border-radius:8px;background:#f8fafc;line-height:1.8;&quot;&gt;&lt;b&gt;시리즈 안내&lt;/b&gt; · PLC·FINS 통신 실무 12편입니다.&lt;br&gt;&lt;a href=&quot;https://songbination.tistory.com/80&quot;&gt;PLC·FINS 전체 목차 보기 →&lt;/a&gt;&lt;/div&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;무엇보다 예지보전 수집기가 제어 권한을 갖지 않는다는 점이 현장 접근 승인의 전제였습니다. 그래서 수집기는 메모리 읽기만 사용했고 쓰기와 운전 모드 변경 경로를 넣지 않았습니다.&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;b&gt;정전류 조건의 전압·등가저항 추세&lt;/b&gt;로 옮겼습니다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;개체별 등가저항, 인접 쌍의 편차, 이상 코드 빈도, 적산값 증가율을 보조 축으로 사용했습니다. 실제 분석에서는 뚜렷한 상승 추세가 없어 ‘열화 신호 없음’으로 기록했습니다. 리플율과 연속 온도는 현재 신호로 얻을 수 없어 센서 증설 과제로 분리했습니다.&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;더 안전한 우선순위&lt;/h2&gt;&lt;ol style=&quot;line-height:1.9;&quot;&gt;&lt;li&gt;사람이 승인된 HMI에서 진단 모드를 바꾸고 수집기는 계속 읽기만 한다.&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;h2 data-ke-size=&quot;size26&quot;&gt;도입 전 검토한 안전장치&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;아래 항목은 &lt;b&gt;설계 검토 수준이며 구현하지 않았습니다.&lt;/b&gt;&lt;/p&gt;&lt;ul style=&quot;line-height:1.9;&quot;&gt;&lt;li&gt;읽기 전용을 기본값으로 두고 쓰기 모듈 자체를 분리&lt;/li&gt;&lt;li&gt;승인된 단일 주소와 이산 허용값만 화이트리스트로 관리&lt;/li&gt;&lt;li&gt;변경 전 값 확보, 변경 뒤 되읽기, 불일치 시 원복&lt;/li&gt;&lt;li&gt;정지 상태 인터록과 사람의 이중 확인&lt;/li&gt;&lt;li&gt;무인·예약 실행 경로에서는 쓰기를 원천 차단&lt;/li&gt;&lt;li&gt;실행자·시각·전후 값·검증 결과의 감사 로그&lt;/li&gt;&lt;/ul&gt;&lt;div style=&quot;margin:16px 0;padding:14px;background:#fee2e2;border-left:4px solid #dc2626;border-radius:8px;line-height:1.8;&quot;&gt;&lt;b&gt;현장 적용 금지&lt;/b&gt;&lt;br&gt;이 글만으로 실제 설비의 쓰기 기능을 구현하거나 시험해서는 안 됩니다. 대상 PLC 프로그램 전체 검증, 설비팀·제작사 승인, 정지 창, 위험성 평가와 원복 책임이 먼저입니다.&lt;/div&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;오늘의 정리&lt;/h2&gt;&lt;ul style=&quot;line-height:1.9;&quot;&gt;&lt;li&gt;실제 수집기는 읽기 전용으로만 운영했다.&lt;/li&gt;&lt;li&gt;쓰기 기능은 검토했지만 가동 설비에 실행하지 않았다.&lt;/li&gt;&lt;li&gt;관측 권한의 한계 안에서 전압·등가저항 지표로 문제를 다시 정의했다.&lt;/li&gt;&lt;li&gt;안전장치는 구현 사례가 아니라 향후 도입 전 설계 체크리스트다.&lt;/li&gt;&lt;/ul&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;이전 글·다음 글 바로가기&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;← &lt;a href=&quot;https://songbination.tistory.com/87&quot;&gt;이전 글: FINS End Code별 대응 방법 [PLC·FINS 11]&lt;/a&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;a href=&quot;https://songbination.tistory.com/80&quot;&gt;PLC·FINS 통신 실무 전체 목차&lt;/a&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://songbination.tistory.com/98&quot;&gt;다음 관련 글: PLC 사이클 타임으로 이상 감지하기&lt;/a&gt; →&lt;/p&gt;</description>
      <category>PLC &amp;middot; FINS</category>
      <category>D메모리쓰기</category>
      <category>Fins</category>
      <category>MemoryAreaWrite</category>
      <category>omron</category>
      <category>PLC</category>
      <category>PythonPLC</category>
      <category>산업안전</category>
      <author>Songmin</author>
      <guid isPermaLink="true">https://songbination.tistory.com/92</guid>
      <comments>https://songbination.tistory.com/92#entry92comment</comments>
      <pubDate>Tue, 25 Aug 2026 18:00:34 +0900</pubDate>
    </item>
    <item>
      <title>Python으로 여러 엑셀 파일 합치기 &amp;mdash; pandas&amp;middot;openpyxl 비교 [Python 실무 3]</title>
      <link>https://songbination.tistory.com/91</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;margin: 18px 0; padding: 14px 16px; border-left: 4px solid #2563eb; border-radius: 8px; background: #f8fafc; line-height: 1.8;&quot;&gt;&lt;b&gt;시리즈 안내&lt;/b&gt; &amp;middot; 이 글은 Python 실무 3입니다.&lt;br /&gt;&lt;a href=&quot;https://songbination.tistory.com/category/Python%20Automation&quot;&gt;Python Automation 전체 글 보기 &amp;rarr;&lt;/a&gt;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;매일 쌓이는 엑셀을 하나씩 복사하지 않기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설비별&amp;middot;날짜별 엑셀 파일이 한 폴더에 쌓이면 월간 보고서를 만들 때 여러 파일을 열어 한 시트로 복사하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 구조가 같은 파일이라면 pandas로 읽어 &lt;code&gt;concat()&lt;/code&gt;하는 방식이 가장 단순합니다. 반대로 기존 양식의 셀 서식과 수식을 유지하는 것이 목적이라면 openpyxl이 더 적합합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시리즈 안내&lt;/b&gt; 이 글은 「Python 실무」 시리즈의 3번째 글입니다. 2편에서 한 파일의 행과 열을 정리했다면, 이번에는 같은 구조의 일일 파일을 월간 데이터 한 장으로 이어 붙입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;pandas와 openpyxl 선택 기준&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;목적&lt;/th&gt;
&lt;th&gt;추천 도구&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;같은 열 구조의 데이터를 세로로 합치기&lt;/td&gt;
&lt;td&gt;pandas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;필터&amp;middot;그룹화&amp;middot;자료형 변환까지 수행&lt;/td&gt;
&lt;td&gt;pandas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기존 셀 서식&amp;middot;시트 위치를 유지하며 값 입력&lt;/td&gt;
&lt;td&gt;openpyxl&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;차트&amp;middot;병합 셀&amp;middot;양식 중심 파일 수정&lt;/td&gt;
&lt;td&gt;openpyxl 후 검증&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 데이터 통합에 초점을 맞춰 pandas를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install pandas openpyxl&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기본 통합 코드&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from pathlib import Path
import pandas as pd
​
input_dir = Path(&quot;daily_files&quot;)
output_path = Path(&quot;monthly_equipment_log.xlsx&quot;)
​
frames = []
​
for path in sorted(input_dir.glob(&quot;*.xlsx&quot;)):
 &amp;nbsp;  if path.name == output_path.name:
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  continue
​
 &amp;nbsp;  df = pd.read_excel(
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  path,
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  sheet_name=&quot;Data&quot;,
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  dtype={
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;  &quot;equipment_code&quot;: &quot;string&quot;,
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;  &quot;alarm_code&quot;: &quot;string&quot;,
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  },
 &amp;nbsp;  )
​
 &amp;nbsp;  df[&quot;source_file&quot;] = path.name
 &amp;nbsp;  frames.append(df)
​
if not frames:
 &amp;nbsp;  raise FileNotFoundError(&quot;합칠 엑셀 파일이 없습니다.&quot;)
​
merged = pd.concat(frames, ignore_index=True)
merged.to_excel(output_path, index=False)
​
print(f&quot;파일 수: {len(frames)}&quot;)
print(f&quot;통합 행 수: {len(merged):,}&quot;)
print(f&quot;저장 위치: {output_path}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;source_file&lt;/code&gt; 열을 추가하면 이상한 데이터가 발견됐을 때 어느 원본 파일에서 왔는지 추적할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;열 구조가 다른 파일 걸러내기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 이름만 같고 열 구조가 달라진 경우가 있습니다. 필수 열을 검증한 뒤 합칩니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;required_columns = {
 &amp;nbsp;  &quot;equipment_code&quot;,
 &amp;nbsp;  &quot;logged_at&quot;,
 &amp;nbsp;  &quot;temperature&quot;,
 &amp;nbsp;  &quot;current&quot;,
}
​
valid_frames = []
invalid_files = []
​
for path in sorted(input_dir.glob(&quot;*.xlsx&quot;)):
 &amp;nbsp;  df = pd.read_excel(path, sheet_name=&quot;Data&quot;)
 &amp;nbsp;  missing = required_columns - set(df.columns)
​
 &amp;nbsp;  if missing:
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  invalid_files.append(
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;  {
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;  &quot;file&quot;: path.name,
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;  &quot;missing&quot;: &quot;, &quot;.join(sorted(missing)),
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;  }
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  )
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  continue
​
 &amp;nbsp;  df[&quot;source_file&quot;] = path.name
 &amp;nbsp;  valid_frames.append(df)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누락 파일을 조용히 제외하지 말고 별도 보고서로 저장합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;invalid_df = pd.DataFrame(invalid_files)
​
with pd.ExcelWriter(output_path, engine=&quot;openpyxl&quot;) as writer:
 &amp;nbsp;  pd.concat(valid_frames, ignore_index=True).to_excel(
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  writer,
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  sheet_name=&quot;Merged&quot;,
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  index=False,
 &amp;nbsp;  )
​
 &amp;nbsp;  invalid_df.to_excel(
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  writer,
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  sheet_name=&quot;InvalidFiles&quot;,
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  index=False,
 &amp;nbsp;  )&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;날짜와 코드 자료형 정리&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;merged[&quot;logged_at&quot;] = pd.to_datetime(
 &amp;nbsp;  merged[&quot;logged_at&quot;],
 &amp;nbsp;  errors=&quot;coerce&quot;,
)
​
invalid_date_count = merged[&quot;logged_at&quot;].isna().sum()
​
if invalid_date_count:
 &amp;nbsp;  print(f&quot;날짜 변환 실패: {invalid_date_count}행&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설비코드에 앞자리 0이 있다면 처음 읽을 때 문자열로 지정합니다. 숫자로 읽힌 뒤에는 원래 자릿수를 정확히 복원하기 어려울 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;중복 행 제거 기준&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 파일의 기간이 겹치면 같은 데이터가 두 번 들어갈 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;key_columns = [
 &amp;nbsp;  &quot;equipment_code&quot;,
 &amp;nbsp;  &quot;logged_at&quot;,
 &amp;nbsp;  &quot;alarm_code&quot;,
]
​
duplicate_count = merged.duplicated(
 &amp;nbsp;  subset=key_columns,
 &amp;nbsp;  keep=False,
).sum()
​
print(f&quot;중복 후보: {duplicate_count}행&quot;)
​
deduplicated = merged.drop_duplicates(
 &amp;nbsp;  subset=key_columns,
 &amp;nbsp;  keep=&quot;last&quot;,
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중복 제거 키는 업무 데이터의 실제 유일성 기준에 맞춰야 합니다. 같은 시각에 같은 알람이 두 번 발생할 수 있다면 별도 이벤트 ID가 필요합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최종 검증&lt;/h2&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;input_row_count = sum(len(df) for df in valid_frames)
output_row_count = len(merged)
​
if input_row_count != output_row_count:
 &amp;nbsp;  raise AssertionError(
 &amp;nbsp; &amp;nbsp; &amp;nbsp;  f&quot;행 수 불일치: 입력={input_row_count}, 출력={output_row_count}&quot;
 &amp;nbsp;  )&lt;/code&gt;&lt;/pre&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 통합은 pandas, 기존 양식 수정은 openpyxl 중심으로 선택한다.&lt;/li&gt;
&lt;li&gt;출력 파일이 입력 폴더에 있으면 다시 읽지 않도록 제외한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;source_file&lt;/code&gt; 열로 원본 추적성을 남긴다.&lt;/li&gt;
&lt;li&gt;필수 열&amp;middot;자료형&amp;middot;날짜 변환 실패를 검증한다.&lt;/li&gt;
&lt;li&gt;중복 제거는 실제 업무의 유일성 기준을 먼저 정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;월간 통합 파일에서 나중에 가장 고마운 열은 &lt;code&gt;source_file&lt;/code&gt;입니다. 결과 숫자가 이상할 때 어느 원본에서 들어왔는지 되짚을 수 있는 가장 싼 보험이기 때문입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://pandas.pydata.org/docs/reference/api/pandas.concat.html&quot;&gt;pandas 공식 문서 &amp;mdash; concat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pandas.pydata.org/docs/reference/api/pandas.read_excel.html&quot;&gt;pandas 공식 문서 &amp;mdash; read_excel&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다음 글 예고&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엑셀을 합치는 데 익숙해지면 다음 질문은 대개 같습니다. &amp;ldquo;이 데이터를 DB에서 바로 가져오면 안 될까.&amp;rdquo; 다음 글에서는 &lt;code&gt;pyodbc&lt;/code&gt;가 ODBC Driver를 거쳐 MSSQL에 연결되는 구조부터 확인합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이전 글&amp;middot;다음 글 바로가기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;larr; &lt;a href=&quot;https://songbination.tistory.com/86&quot;&gt;&lt;b&gt;이전 글: Python으로 엑셀 행&amp;middot;열 삭제하기 [Python 실무 2]&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;a href=&quot;https://songbination.tistory.com/79&quot;&gt;&lt;b&gt;Python 입문&amp;middot;자동화 시리즈 전체 목차&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://songbination.tistory.com/96&quot;&gt;&lt;b&gt;다음 글: Python에서 MSSQL 연결하기 [Python 실무 4]&lt;/b&gt;&lt;/a&gt; &amp;rarr;&lt;/p&gt;</description>
      <category>Python Automation</category>
      <category>concat</category>
      <category>openpyxl</category>
      <category>PANDAS</category>
      <category>Python</category>
      <category>업무자동화</category>
      <category>엑셀자동화</category>
      <category>엑셀합치기</category>
      <author>Songmin</author>
      <guid isPermaLink="true">https://songbination.tistory.com/91</guid>
      <comments>https://songbination.tistory.com/91#entry91comment</comments>
      <pubDate>Mon, 24 Aug 2026 18:00:27 +0900</pubDate>
    </item>
    <item>
      <title>Tableau NULL 처리 방법 &amp;mdash; IFNULL&amp;middot;ZN&amp;middot;ISNULL 차이 [Tableau 5]</title>
      <link>https://songbination.tistory.com/90</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;margin: 18px 0; padding: 14px 16px; border-left: 4px solid #2563eb; border-radius: 8px; background: #f8fafc; line-height: 1.8;&quot;&gt;&lt;b&gt;시리즈 안내&lt;/b&gt; &amp;middot; 이 글은 Tableau 5입니다.&lt;br /&gt;&lt;a href=&quot;https://songbination.tistory.com/category/Tableau&quot;&gt;Tableau 전체 글 보기 &amp;rarr;&lt;/a&gt;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;NULL을 모두 0으로 바꾸면 문제가 해결될까&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설비 대시보드에서 가동시간이나 생산수량이 비어 있으면 차트가 끊기거나 계산 결과가 NULL이 됩니다. 이때 &lt;code&gt;ZN()&lt;/code&gt;으로 모두 0으로 바꾸면 화면은 채워지지만 의미가 달라질 수 있습니다.&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;0&lt;/code&gt;: 측정했고 결과가 0&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NULL&lt;/code&gt;: 행은 있지만 값이 없음&lt;/li&gt;
&lt;li&gt;행 없음: 해당 시점의 레코드 자체가 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 상태를 구분하지 않으면 센서 미수집을 정상적인 0으로 오해할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시리즈 안내&lt;/b&gt; 이 글은 「Tableau 실무」 시리즈의 5번째 글입니다. 4편에서 계산 수준을 맞췄다면, 이번에는 계산식은 맞는데 값이 비어 보일 때 &lt;code&gt;IFNULL&lt;/code&gt;, &lt;code&gt;ZN&lt;/code&gt;, &lt;code&gt;ISNULL&lt;/code&gt;을 어디에 써야 하는지 구분합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;IFNULL: NULL일 때 원하는 값으로 대체&lt;/h2&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;IFNULL([설비상태], &quot;미수집&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 값이 NULL이 아니면 원래 값을 반환하고, NULL이면 두 번째 값을 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자에도 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;IFNULL([생산수량], 0)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 인수의 자료형은 서로 호환되어야 합니다. 숫자 필드에 문자열을 바로 대체하면 자료형 오류가 날 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ZN: 숫자 NULL을 0으로 대체&lt;/h2&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;ZN([생산수량])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ZN()&lt;/code&gt;은 숫자 계산에서 NULL을 0으로 바꾸는 데 간결합니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;ZN(SUM([생산수량])) / SUM([계획수량])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 NULL이 실제로 미수집을 뜻한다면 0으로 대체하지 않는 편이 맞을 수 있습니다. Tableau 공식 도움말도 NULL이 항상 0과 같은 의미는 아니라고 주의합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ISNULL: NULL인지 판단&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ISNULL()&lt;/code&gt;은 대체값을 반환하지 않고 TRUE 또는 FALSE를 반환합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;ISNULL([종료시각])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건문과 함께 상태를 구분할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;IF ISNULL([종료시각]) THEN &quot;진행 중 또는 미수집&quot;
ELSE &quot;완료&quot;
END&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료시각이 NULL인 이유가 작업 진행 중인지 수집 실패인지 추가 필드로 구분해야 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;함수 차이 한눈에 보기&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;함수&lt;/th&gt;
&lt;th&gt;반환값&lt;/th&gt;
&lt;th&gt;적합한 상황&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IFNULL(A, B)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A 또는 B&lt;/td&gt;
&lt;td&gt;숫자&amp;middot;문자&amp;middot;날짜를 지정한 값으로 대체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ZN(A)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A 또는 0&lt;/td&gt;
&lt;td&gt;숫자 NULL을 0으로 대체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ISNULL(A)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TRUE/FALSE&lt;/td&gt;
&lt;td&gt;NULL 여부를 조건으로 분기&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;집계 전과 집계 후의 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 두 식은 비슷해 보입니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;SUM(IFNULL([생산수량], 0))&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;ZN(SUM([생산수량]))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째는 행 수준의 NULL을 0으로 바꾼 뒤 합산합니다. 두 번째는 집계 결과가 NULL일 때 0으로 바꿉니다. 화면의 데이터 구성과 필터에 따라 결과를 표시하는 과정이 다를 수 있으므로 의도에 맞는 위치를 선택합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;NULL과 누락된 행은 다르다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1월&amp;middot;2월&amp;middot;3월 데이터가 있고 2월 값만 NULL이라면 &lt;code&gt;ZN()&lt;/code&gt;으로 0을 표시할 수 있습니다. 하지만 2월 행 자체가 없다면 &lt;code&gt;ZN()&lt;/code&gt;만으로 새로운 2월 행이 생기지 않습니다.&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;Tableau의 누락 값 표시 기능&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;NULL 처리 함수는 존재하는 행의 값을 다루는 도구이지 누락된 레코드를 생성하는 도구가 아닙니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설비 데이터의 권장 구분&lt;/h2&gt;
&lt;pre class=&quot;vbscript&quot;&gt;&lt;code&gt;IF [수집성공] = FALSE THEN &quot;수집 실패&quot;
ELSEIF ISNULL([측정값]) THEN &quot;값 없음&quot;
ELSEIF [측정값] = 0 THEN &quot;정상 0&quot;
ELSE &quot;측정됨&quot;
END&lt;/code&gt;&lt;/pre&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대체값 지정: &lt;code&gt;IFNULL&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;숫자 NULL을 0으로: &lt;code&gt;ZN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;NULL 여부 판단: &lt;code&gt;ISNULL&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;NULL을 0으로 바꾸기 전에 0이 실제 의미와 맞는지 확인&lt;/li&gt;
&lt;li&gt;누락 행은 별도의 날짜 구조나 원본 데이터 보완 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NULL 처리에서 가장 먼저 답할 질문은 &amp;ldquo;이 빈칸이 무엇을 뜻하는가&amp;rdquo;입니다. 측정 결과 0과 측정 실패를 같은 막대로 그리면 화면은 깔끔해져도 판단은 틀릴 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://help.tableau.com/current/pro/desktop/en-us/functions_functions_logical.htm&quot;&gt;Tableau 공식 도움말 &amp;mdash; Logical Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://help.tableau.com/current/pro/desktop/en-us/missing_values.htm&quot;&gt;Tableau 공식 도움말 &amp;mdash; Missing Values&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다음 글 예고&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NULL을 처리하고 나면 월별 차트에서 또 이상한 일이 생깁니다. 2025년 1월과 2026년 1월이 한 막대로 합쳐지는 문제입니다. 다음 글에서는 &lt;code&gt;DATEPART&lt;/code&gt;와 &lt;code&gt;DATETRUNC&lt;/code&gt;의 차이를 날짜축에서 확인합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이전 글&amp;middot;다음 글 바로가기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;larr; &lt;a href=&quot;https://songbination.tistory.com/85&quot;&gt;&lt;b&gt;이전 글: Tableau 집계 및 비집계 오류 해결 [Tableau 4]&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;a href=&quot;https://songbination.tistory.com/82&quot;&gt;&lt;b&gt;Tableau 실무 시리즈 전체 목차&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://songbination.tistory.com/95&quot;&gt;&lt;b&gt;다음 글: Tableau 날짜별&amp;middot;월별 그룹화 [Tableau 6]&lt;/b&gt;&lt;/a&gt; &amp;rarr;&lt;/p&gt;</description>
      <category>Tableau</category>
      <category>IFNULL</category>
      <category>isnull</category>
      <category>NULL</category>
      <category>tableau</category>
      <category>ZN</category>
      <category>계산된필드</category>
      <category>데이터시각화</category>
      <author>Songmin</author>
      <guid isPermaLink="true">https://songbination.tistory.com/90</guid>
      <comments>https://songbination.tistory.com/90#entry90comment</comments>
      <pubDate>Sun, 23 Aug 2026 18:00:20 +0900</pubDate>
    </item>
    <item>
      <title>예지보전(PdM) 용어집 &amp;mdash; P-F 곡선&amp;middot;기준선&amp;middot;진동&amp;middot;FFT&amp;middot;RUL 총정리</title>
      <link>https://songbination.tistory.com/124</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요. Songbination입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예지보전 글에서 반복되는 보전 전략, P-F 곡선, 센서 신호, 통계 기준선과 평가 지표를 한곳에 모았습니다. 용어 정의뿐 아니라 현장 데이터에서 오해하기 쉬운 조건과 적용 순서를 함께 정리했습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;찾는 방법&lt;/b&gt;&lt;br /&gt;PC에서는 Ctrl+F, 모바일에서는 브라우저 메뉴의 &lt;b&gt;페이지에서 찾기&lt;/b&gt;를 누른 뒤 궁금한 용어를 입력하세요. 영문 원형&amp;middot;쉬운 뜻&amp;middot;실무에서의 역할&amp;middot;혼동하기 쉬운 부분 순서로 정리했습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;빠른 목차&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#section-1&quot;&gt;1. 보전 전략과 고장 개념&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#section-2&quot;&gt;2. P-F 곡선과 시간&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#section-3&quot;&gt;3. 센서와 신호&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#section-4&quot;&gt;4. 통계 특징과 기준선&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#section-5&quot;&gt;5. 데이터 품질과 모델 평가&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#section-6&quot;&gt;6. 설비 운영 지표&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;section-1&quot; data-ke-size=&quot;size26&quot;&gt;1. 보전 전략과 고장 개념&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Corrective Maintenance &amp;mdash; 사후 보전&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 고장이 발생한 뒤 수리하거나 교체하는 전략입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;약자 주의:&lt;/b&gt; CM은 문서에 따라 Corrective Maintenance 또는 Condition Monitoring을 뜻할 수 있으므로 단독 약자보다 원문을 함께 씁니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;적합:&lt;/b&gt; 고장 영향이 작고 교체가 쉬우며 예방 비용이 더 큰 대상에 선택할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;PM &amp;mdash; Preventive Maintenance&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;직역:&lt;/b&gt; 예방 보전&lt;/li&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 사용 시간이나 달력 주기에 따라 점검&amp;middot;교체해 고장을 예방하는 전략입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;한계:&lt;/b&gt; 실제 상태와 무관하게 너무 이르거나 늦게 정비할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CBM &amp;mdash; Condition-Based Maintenance&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;직역:&lt;/b&gt; 상태 기반 보전&lt;/li&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 진동&amp;middot;온도&amp;middot;오일&amp;middot;전류 등 현재 상태 지표가 기준을 벗어날 때 정비합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PdM과 관계:&lt;/b&gt; PdM이 미래 고장 시점과 위험을 예측한다면 CBM은 현재 상태 조건에 더 가깝습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;PdM &amp;mdash; Predictive Maintenance&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;직역:&lt;/b&gt; 예지보전 또는 예측 보전&lt;/li&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 상태 데이터와 고장 이력을 이용해 이상과 고장 위험을 미리 판단하고 정비 시점을 결정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;목표:&lt;/b&gt; 모델 정확도보다 계획 가능한 Lead Time과 불필요한 정비 감소가 중요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;PHM &amp;mdash; Prognostics and Health Management&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;직역:&lt;/b&gt; 예후 및 건전성 관리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 설비 상태 진단, 고장 예측, RUL 추정과 의사결정을 포함하는 넓은 체계입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;관계:&lt;/b&gt; PdM은 PHM의 결과를 실제 정비 계획에 적용하는 영역과 겹칩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Failure Mode &amp;mdash; 고장 모드&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 설비가 기능을 잃는 구체적인 방식입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예:&lt;/b&gt; 베어링 손상, 축 정렬 불량, 윤활 부족, 센서 단선&lt;/li&gt;
&lt;li&gt;&lt;b&gt;중요:&lt;/b&gt; 서로 다른 Failure Mode를 하나의 정상&amp;middot;이상 라벨로 묶으면 센서와 Lead Time이 달라 해석이 어려워집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;FMEA &amp;mdash; Failure Mode and Effects Analysis&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;직역:&lt;/b&gt; 고장 형태 및 영향 분석&lt;/li&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 가능한 고장 모드, 원인, 영향, 검출 방법과 우선순위를 구조적으로 검토합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PdM에서는:&lt;/b&gt; 어떤 센서를 수집하고 어떤 이상을 조기에 찾을지 정하는 출발점이 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Anomaly Detection &amp;mdash; 이상 탐지&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 정상 패턴과 다른 데이터나 상태를 찾는 방법입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 이상은 고장과 같지 않습니다. 생산 조건 변경, 센서 교체, 정비 후 정상 변화도 이상으로 보일 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;운영:&lt;/b&gt; 알람 후 어떤 확인과 조치를 할지 Workflow가 함께 있어야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;section-2&quot; data-ke-size=&quot;size26&quot;&gt;2. P-F 곡선과 시간&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;P-F Curve &amp;mdash; Potential Failure&amp;ndash;Functional Failure Curve&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;P:&lt;/b&gt; Potential Failure. 상태 변화가 검출 가능해지는 시점입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;F:&lt;/b&gt; Functional Failure. 요구 기능을 수행하지 못하는 시점입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;의미:&lt;/b&gt; P와 F 사이에 발견&amp;middot;판단&amp;middot;정비할 수 있는 시간이 얼마나 있는지 보여 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;P-F Interval&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 잠재 고장을 검출할 수 있는 P 시점부터 기능 고장 F 시점까지의 시간입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;수집 주기:&lt;/b&gt; 점검&amp;middot;샘플링 주기는 P-F Interval보다 충분히 짧아야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 고장 모드와 검출 기술에 따라 P 시점이 달라 하나의 고정 값으로 보기 어렵습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Lead Time &amp;mdash; 리드타임&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 이상 알림 후 실제 고장이나 조치 필요 시점까지 확보된 시간입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가치:&lt;/b&gt; 부품 준비, 생산 일정 조정, 정비 인력 배치가 가능한 수준이어야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;평가:&lt;/b&gt; 너무 이른 경보의 오탐 비용과 너무 늦은 경보의 조치 불가능성을 함께 봅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;RUL &amp;mdash; Remaining Useful Life&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;직역:&lt;/b&gt; 잔여 유효 수명&lt;/li&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 현재 시점부터 설비가 요구 기능을 유지할 것으로 예상되는 남은 시간이나 사이클입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 단일 숫자보다 불확실성 범위와 사용 조건을 함께 제시해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Degradation &amp;mdash; 열화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 시간과 사용에 따라 설비 상태나 성능이 점진적으로 나빠지는 과정입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예:&lt;/b&gt; 진동 RMS 상승, 절연 저항 저하, 윤활유 오염 증가&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 모든 고장이 점진적 열화를 보이지 않으며 갑작스러운 고장은 예측이 제한적입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Window &amp;mdash; 분석 구간&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 특징을 계산하기 위해 묶는 일정 시간 또는 샘플 구간입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예:&lt;/b&gt; 1초 진동 신호마다 RMS와 Kurtosis를 계산합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; Window가 짧으면 노이즈가 크고 길면 빠른 변화를 늦게 반영합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;section-3&quot; data-ke-size=&quot;size26&quot;&gt;3. 센서와 신호&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Vibration &amp;mdash; 진동&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 설비의 반복적인 기계 움직임을 가속도&amp;middot;속도&amp;middot;변위로 측정한 신호입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;활용:&lt;/b&gt; 불평형, 정렬 불량, 베어링&amp;middot;기어 이상을 탐지합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 센서 방향, 설치 강성, 회전 속도와 운전 부하를 함께 기록해야 비교할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Temperature &amp;mdash; 온도&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 마찰, 냉각, 전기 부하와 환경 변화를 반영하는 상태 지표입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장점:&lt;/b&gt; 비교적 수집이 쉽고 장기 추세 확인에 유용합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 외기 온도, 가동 시간, 부하 조건을 보정하지 않으면 정상 변화가 이상으로 보일 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Current &amp;mdash; 전류&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 모터와 전기 설비의 부하와 동작 상태를 반영하는 신호입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;활용:&lt;/b&gt; 과부하, 공정 변화, 구속, 반복 동작과 Motor Current Signature Analysis에 사용합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 인버터 주파수와 생산 조건에 따라 정상 전류 패턴이 크게 달라집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Oil Analysis &amp;mdash; 오일 분석&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 윤활유의 점도, 오염, 수분, 금속 마모 입자를 분석해 내부 상태를 판단합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장점:&lt;/b&gt; 기어와 베어링의 마모 정보를 직접적으로 제공할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 채취 위치&amp;middot;온도&amp;middot;절차와 교체 이력을 일정하게 관리해야 추세가 의미 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Sampling Frequency &amp;mdash; 샘플링 주파수&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 1초 동안 신호를 측정하는 횟수이며 Hz로 표현합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예:&lt;/b&gt; 10 kHz는 초당 10,000개 샘플입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;선택:&lt;/b&gt; 보고 싶은 최고 주파수와 센서&amp;middot;수집 장치의 Anti-aliasing 조건을 함께 봅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Nyquist Frequency &amp;mdash; 나이퀴스트 주파수&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; Sampling Frequency의 절반에 해당하는 이론적 최고 표현 주파수입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;원리:&lt;/b&gt; 최고 관심 주파수보다 최소 2배 이상 빠르게 샘플링해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 실무에서는 필터 여유와 신호 품질 때문에 2배보다 높은 비율을 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Aliasing &amp;mdash; 에일리어싱&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 샘플링이 충분히 빠르지 않아 높은 주파수가 낮은 가짜 주파수로 보이는 현상입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대응:&lt;/b&gt; 충분한 Sampling Frequency와 Anti-aliasing Filter를 사용합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;위험:&lt;/b&gt; 후처리만으로 원래 주파수를 완전히 복구하기 어렵습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;FFT &amp;mdash; Fast Fourier Transform&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;직역:&lt;/b&gt; 고속 푸리에 변환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 시간에 따른 신호를 주파수별 성분으로 바꾸는 계산 방법입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;활용:&lt;/b&gt; 회전수 성분, 고조파, 베어링 특성 주파수를 확인합니다. Window 함수와 주파수 해상도를 함께 봅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;section-4&quot; data-ke-size=&quot;size26&quot;&gt;4. 통계 특징과 기준선&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Baseline &amp;mdash; 기준선&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 정상 상태를 비교하기 위한 대표 범위와 패턴입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;구성:&lt;/b&gt; 설비, 제품, 속도, 부하, 계절과 정비 상태별로 나눌 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 고장이 섞인 과거 데이터 전체 평균을 정상 기준으로 사용하지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Threshold &amp;mdash; 임계값&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 정상&amp;middot;주의&amp;middot;이상 상태를 구분하기 위해 정한 경계값입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;종류:&lt;/b&gt; 고정 기준, 통계 기준, 운전 조건별 동적 기준이 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;운영:&lt;/b&gt; 한 번 초과보다 지속 시간, 반복 횟수, 복귀 조건을 함께 정의합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Mean&amp;middot;Median &amp;mdash; 평균&amp;middot;중앙값&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Mean:&lt;/b&gt; 값의 합을 개수로 나눈 산술 평균이며 극단값의 영향을 받습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Median:&lt;/b&gt; 정렬했을 때 가운데 값으로 극단값에 비교적 강합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;선택:&lt;/b&gt; 고장 시간과 Cycle Time처럼 치우친 데이터는 Median과 분위수를 함께 봅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Standard Deviation &amp;mdash; 표준편차&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 값이 평균 주변에 얼마나 퍼져 있는지 나타냅니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;활용:&lt;/b&gt; 정상 변동을 기준으로 Z-score나 관리 한계를 계산합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 분포가 비대칭이거나 극단값이 많으면 표준편차 기반 기준이 불안정할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Z-score&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 값이 평균에서 표준편차 몇 배만큼 떨어져 있는지 나타냅니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;식:&lt;/b&gt; (x - 평균) / 표준편차&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 정규분포 가정, 정상 데이터 오염, 설비 조건 차이를 확인합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;IQR &amp;mdash; Interquartile Range&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;직역:&lt;/b&gt; 사분위 범위&lt;/li&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 75% 지점 Q3에서 25% 지점 Q1을 뺀 값으로 중앙 50%의 퍼짐입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;활용:&lt;/b&gt; 극단값에 비교적 강한 이상치 경계 Q1-1.5&amp;times;IQR, Q3+1.5&amp;times;IQR를 만듭니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Moving Average &amp;mdash; 이동평균&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 최근 일정 개수 또는 시간의 평균을 계속 계산해 단기 노이즈를 줄입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;효과:&lt;/b&gt; 추세를 보기 쉽지만 급격한 변화를 늦게 반영할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;선택:&lt;/b&gt; Window 크기는 설비 Cycle과 필요한 Lead Time에 맞춥니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;EWMA &amp;mdash; Exponentially Weighted Moving Average&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;직역:&lt;/b&gt; 지수 가중 이동평균&lt;/li&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 최근 값에 더 큰 가중치를 주며 과거 값을 점차 작게 반영합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;튜닝:&lt;/b&gt; 평활 계수 alpha가 크면 빠르게 반응하고 작으면 부드럽게 움직입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;RMS &amp;mdash; Root Mean Square&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;직역:&lt;/b&gt; 제곱평균제곱근&lt;/li&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 신호의 전체 에너지나 크기를 대표하는 값입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;진동:&lt;/b&gt; 가속도&amp;middot;속도 신호의 전반적인 진동 수준을 추적하지만 특정 주파수 원인은 FFT와 함께 봅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Kurtosis&amp;middot;Crest Factor&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Kurtosis:&lt;/b&gt; 분포의 꼬리와 충격성 정도를 보는 첨도 지표입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Crest Factor:&lt;/b&gt; Peak를 RMS로 나눈 값으로 충격성 변화를 봅니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 단일 특징 하나로 고장을 확정하지 않고 회전수&amp;middot;부하&amp;middot;다른 특징과 함께 해석합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;section-5&quot; data-ke-size=&quot;size26&quot;&gt;5. 데이터 품질과 모델 평가&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dead Sensor &amp;mdash; 죽은 센서&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 센서가 실제 변화를 반영하지 못하고 고정값, 0, 최대값만 내는 상태입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;탐지:&lt;/b&gt; 분산 0, 장시간 동일값, 변화율, 다른 센서와의 불일치를 확인합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 설비가 실제로 정지한 정상 고정값과 구분합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Sensor Drift &amp;mdash; 센서 드리프트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 실제 상태가 같아도 센서 출력 기준이 시간에 따라 서서히 이동하는 현상입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;원인:&lt;/b&gt; 노화, 온도, 설치 변화, 교정 문제&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대응:&lt;/b&gt; 교정 이력, 기준 센서, 중복 센서와 정비 전후 분포를 비교합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Missing Data &amp;mdash; 결측 데이터&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 측정&amp;middot;전송&amp;middot;저장 과정에서 값이 없는 상태입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 0으로 채우면 실제 0과 통신 실패가 섞입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;처리:&lt;/b&gt; 누락 원인, 지속 시간, 분석 목적에 따라 제거&amp;middot;보간&amp;middot;상태값 분리를 선택합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;False Positive&amp;middot;False Negative&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;False Positive:&lt;/b&gt; 정상인데 이상이라고 판단한 오탐입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;False Negative:&lt;/b&gt; 실제 이상인데 정상이라고 놓친 미탐입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;운영:&lt;/b&gt; 오탐은 알람 피로, 미탐은 고장 위험을 만들므로 비용과 안전에 맞춰 기준을 정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Precision &amp;mdash; 정밀도&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 이상이라고 알린 사례 중 실제 이상인 비율입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;질문:&lt;/b&gt; 알람을 믿고 확인했을 때 얼마나 자주 맞는가를 나타냅니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 알람을 거의 내지 않으면 높아질 수 있어 Recall과 함께 봅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Recall &amp;mdash; 재현율&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 실제 이상 사례 중 모델이 찾아낸 비율입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;질문:&lt;/b&gt; 놓치면 안 되는 고장을 얼마나 잡았는가를 나타냅니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 기준을 낮추면 Recall은 높아지지만 False Positive가 증가할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Data Leakage &amp;mdash; 데이터 누수&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 예측 시점에는 알 수 없는 미래 정보가 학습&amp;middot;평가 입력에 들어가는 문제입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예:&lt;/b&gt; 고장 후 작성된 조치 코드를 고장 전 예측 특징에 포함합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대응:&lt;/b&gt; 시간 순서로 학습&amp;middot;검증을 나누고 특징의 실제 생성 시점을 확인합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Concept Drift &amp;mdash; 개념 변화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 시간이 지나며 입력과 고장의 관계 또는 정상 패턴이 달라지는 현상입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;원인:&lt;/b&gt; 제품 변경, 속도 변경, 정비, 센서 교체, 공정 개선&lt;/li&gt;
&lt;li&gt;&lt;b&gt;운영:&lt;/b&gt; 성능&amp;middot;분포&amp;middot;오탐 변화를 모니터링하고 기준선과 모델 재검토 조건을 정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;section-6&quot; data-ke-size=&quot;size26&quot;&gt;6. 설비 운영 지표&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;MTBF &amp;mdash; Mean Time Between Failures&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;직역:&lt;/b&gt; 평균 고장 간격&lt;/li&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 수리 가능한 설비의 한 고장부터 다음 고장까지 평균 운전 시간입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 집계 기간, 고장 정의, 계획정지 포함 여부를 통일해야 비교할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;MTTR &amp;mdash; Mean Time To Repair&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;직역:&lt;/b&gt; 평균 수리 시간&lt;/li&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 고장 발생 후 기능 복구까지 걸린 평균 시간입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 대기&amp;middot;부품 조달&amp;middot;실제 작업 중 어느 시간을 포함하는지 정의합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;OEE &amp;mdash; Overall Equipment Effectiveness&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;직역:&lt;/b&gt; 설비 종합 효율&lt;/li&gt;
&lt;li&gt;&lt;b&gt;구성:&lt;/b&gt; Availability &amp;times; Performance &amp;times; Quality로 계산합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; 각 요소의 기준 시간, 이론 Cycle, 양품 정의가 다르면 OEE를 직접 비교하기 어렵습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Availability &amp;mdash; 가동률&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;뜻:&lt;/b&gt; 계획 생산 시간 중 실제 가동한 비율을 나타냅니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PdM 연결:&lt;/b&gt; 예기치 않은 고장 정지 감소 효과를 보는 지표가 될 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의:&lt;/b&gt; Calendar Time과 Planned Production Time 중 어떤 분모를 쓰는지 명시합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;용어를 외우기보다 흐름으로 보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설비의 Failure Mode와 P-F Interval을 먼저 정의하고 진동&amp;middot;온도&amp;middot;전류 같은 상태 데이터를 수집합니다. 정상 Baseline과 Threshold를 만든 뒤 Moving Average&amp;middot;EWMA&amp;middot;FFT 같은 방법으로 변화를 봅니다. False Positive와 데이터 누락을 검증하고 Lead Time&amp;middot;Precision&amp;middot;Recall&amp;middot;RUL로 실제 보전 가치가 있는지 평가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 용어가 시리즈에 등장할 때마다 이 페이지도 같은 기준으로 업데이트하겠습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://songbination.tistory.com/category/PdM(Predictive%20Maintenance)&quot;&gt;PdM(Predictive Maintenance) 전체 글&lt;/a&gt;&lt;a href=&quot;https://songbination.tistory.com/&quot;&gt;Songbination 홈&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성 &amp;middot; Songmin&lt;br /&gt;제조 현장에서 설비 데이터 수집과 자동화를 담당하며 얻은 경험을 기록합니다.&lt;/p&gt;</description>
      <category>PdM(Predictive Maintenance)</category>
      <category>PDM</category>
      <category>설비 데이터</category>
      <category>예지보전</category>
      <category>용어집</category>
      <author>Songmin</author>
      <guid isPermaLink="true">https://songbination.tistory.com/124</guid>
      <comments>https://songbination.tistory.com/124#entry124comment</comments>
      <pubDate>Sat, 22 Aug 2026 23:24:52 +0900</pubDate>
    </item>
  </channel>
</rss>