<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jwkim_1018.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Wed, 14 Feb 2024 00:13:37 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. jwkim_1018.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jwkim_1018" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[uipath 꿀팁 모음]]></title>
            <link>https://velog.io/@jwkim_1018/uipath-%EA%B0%9C%EB%B0%9C-%ED%8C%81-%EB%AA%A8%EC%9D%8C</link>
            <guid>https://velog.io/@jwkim_1018/uipath-%EA%B0%9C%EB%B0%9C-%ED%8C%81-%EB%AA%A8%EC%9D%8C</guid>
            <pubDate>Wed, 14 Feb 2024 00:13:37 GMT</pubDate>
            <description><![CDATA[<h1 id="1-데이터-타입-확인">1. 데이터 타입 확인</h1>
<blockquote>
<p>변수명.GetType.ToString</p>
</blockquote>
<h1 id="2-컬럼-이름-바꾸기">2. 컬럼 이름 바꾸기</h1>
<blockquote>
<p>&lt; assign 액티비티 &gt;
dataTableVar.Columns(&quot;기존 컬럼 이름&quot;).ColumnName = &quot;바꿀 컬럼 이름&quot;</p>
</blockquote>
<h1 id="3-data-row-객체-접근하기">3. Data Row 객체 접근하기</h1>
<p>Data Row 객체를 다루는 방법은 두 가지 있음</p>
<ol>
<li><p>컬럼 인덱스 사용 </p>
<blockquote>
<p>CurrentRow(1)
→ 1번째 컬럼 값 접근</p>
</blockquote>
</li>
<li><p>컬럼 이름 사용 </p>
<blockquote>
<p>CurrentRow(&quot;비고&quot;)
→ &quot;비고&quot; 컬럼 값 접근 </p>
</blockquote>
</li>
</ol>
<h1 id="4-데이터-타입-변환-방법">4. 데이터 타입 변환 방법</h1>
<p>데이터 타입 변환 방법은 두 가지 있음 </p>
<p>예) 정수 → 실수 </p>
<blockquote>
<p>방법 1
Double.Parse(varINT)</p>
</blockquote>
<blockquote>
<p>방법 2
Convert.ToDouble(varINT)</p>
</blockquote>
<p>다른 타입들도 동일하게 사용 할 수 있음</p>
<p>위 방법의 차이점은 null값에 대한 처리인데 <code>Double.Parse</code>는 변환하는 값이 null이면 에러를 반환하고 <code>Convert.ToDouble</code>은 null일 때 0을 반환함 → <a href="https://junibong.tistory.com/9">해당 링크 참고</a></p>
<h1 id="5-string-메서드">5. String 메서드</h1>
<p><a href="https://velog.io/@jwkim_1018/UIPath-Academy-008.-Data-Manipulation-with-Strings-in-Studio">해당 링크 참고</a></p>
<h1 id="6-generic-value">6. Generic value</h1>
<ul>
<li>Generic 개념<ul>
<li>변수는 데이터 타입을 지정하고 그에 맞는 값을 담을 수 있음</li>
<li>하지만 generic 변수는 담는 변수에 따라 그 타입이 지정됨</li>
</ul>
</li>
<li>장점<ul>
<li>여러 타입의 값을 모두 담을 수 있기 때문에 코드 재사용성 커짐</li>
<li>형변환 고자어 생략되어 코드 줄임</li>
</ul>
</li>
<li>참고할 사이트<ul>
<li><a href="https://www.nextree.io/generic-ihaehagi/">https://www.nextree.io/generic-ihaehagi/</a></li>
</ul>
</li>
</ul>
<h1 id="7-csv-인코딩">7. csv 인코딩</h1>
<p>encoding 옵션에 &quot;euc-kr&quot;로 주기
(사용 가능한 인코딩 옵션은 <a href="https://docs.uipath.com/activities/other/latest/productivity/supported-character-encoding">여기에서 확인</a>)</p>
<h1 id="8-날짜-함수">8. 날짜 함수</h1>
<blockquote>
<p>// 오늘 날짜 
varSTR = Now.ToString(&quot;yyyyMMdd&quot;)</p>
</blockquote>
<ul>
<li>ToString 안에 원하는 형식 넣기 </li>
<li>시간, 분, 초까지 쓸 수 있으니 Today보다는 Now 쓰기</li>
</ul>
<blockquote>
<p>// 원하는 날짜를 받아서 날짜 타입으로 다루기 
dateSTR = &quot;2024.02.15&quot;
// 내일
tomorrowSTR = Convert.ToDateTime(dateSTR).AddDays(1).ToString(&quot;yyyy-MM-dd&quot;) </p>
</blockquote>
<h1 id="9-array-데이터-테이블-앞-부분-자르기skip">9. Array, 데이터 테이블 앞 부분 자르기(skip)</h1>
<p>반복문 테스트 할 때 전부 다 돌릴 순 없으니 앞 부분을 잘라내고 몇 개의 엘리먼트만 반복 돌릴 떄 유용함</p>
<blockquote>
<p>arrVarARR.skip(10)
→ 앞에서부터 10개의 데이터 잘라냄(0~9번째 데이터는 잘라내고 10번째 데이터부터 살려놓음)</p>
</blockquote>
<p>데이터 테이블도 이렇게 가능</p>
<blockquote>
<p>dtVarDT.AsEnumerable.Skip(10).CopyToDataTable
→ 0~9 번째 로우 없애고 10번째 로우부터 살려놓음</p>
</blockquote>
<h1 id="10-데이터-테이블-뒷-부분-자르기">10. 데이터 테이블 뒷 부분 자르기</h1>
<blockquote>
<p>VarDT.AsEnumerable().Take(500).CopyToDataTable()</p>
</blockquote>
<h1 id="11-type-into에서-키보드-조작하기">11. type into에서 키보드 조작하기</h1>
<p>예) <strong>&quot;[k(enter)]&quot;</strong> → 이러면 액티비티 하나 아낄 수 있음</p>
<ul>
<li>k : 그냥 한 번 누르기</li>
<li>d : key down</li>
<li>u : key up</li>
</ul>
<p>※ 이 방법은 simulate 일 때는 안됨</p>
<h1 id="12-데이터-테이블-만드는-순서">12. 데이터 테이블 만드는 순서</h1>
<ol>
<li>빈 데이터 테이블 선언 </li>
<li>컬럼 추가</li>
<li>비어있는 로우 추가 </li>
<li>데이터 추가 </li>
</ol>
<p>※ 이 때 반복문이나 코드를 최대한 줄일 수 있는 방법 고안 </p>
<h1 id="13-데이터-테이블-전치">13. 데이터 테이블 전치</h1>
<p>For Each를 중첩해서 작성(아래 사진 참고)
<img src="https://velog.velcdn.com/images/jwkim_1018/post/fbbb6960-f071-4c6b-a5a0-7a9530d0036d/image.png" alt=""></p>
<p>첫 번쨰 for each → 데이터 로우 객체를 받음
두 번째 for each → 하나의 필드(셀) 값을 받음 </p>
<h1 id="14-배열-선언">14. 배열 선언</h1>
<blockquote>
<p>// 길이가 정해져있는 배열 만들기 
sampleARR = New String(3){&quot;가&quot;, &quot;나&quot;, &quot;다&quot;}</p>
</blockquote>
<blockquote>
<p>// 길이가 정해져있지 않은 배열 만들기(그렇다고 길이가 가변적인 것은 아님 → 리스트가 아니니까)
sampleARR = New String(){}</p>
</blockquote>
<h1 id="15-데이터-테이블-컬럼-위치-변경">15. 데이터 테이블 컬럼 위치 변경</h1>
<p><code>Invoke Method</code> 액티비티 사용하면 됨
<img src="https://velog.velcdn.com/images/jwkim_1018/post/f79832b2-94ae-47ed-816a-95e798d838f2/image.png" alt=""></p>
<p>참고 : <a href="https://uipath.tistory.com/89">https://uipath.tistory.com/89</a></p>
<h1 id="16-select-문">16. Select 문</h1>
<blockquote>
<p>// &quot;측정소명&quot; 컬럼 중 값이 &quot;수내동&quot;이거나 &quot;인계동&quot;인 데이터만 선택하는 예제
sampleDT.Select(&quot;[측정소명] = &#39;수내동&#39; Or [측정소명] = &#39;인계동&#39;&quot;).CopyToDataTable</p>
</blockquote>
<p>컬럼 이름은 대괄호로 묶고 그 값은 작은 따옴표로 묶기</p>
<blockquote>
<p>// &quot;구매객수&quot; 컬럼 값만 가져오기 
sampleDT.AsEnumerable.Select(Function(row) row(&quot;구매객수&quot;))</p>
</blockquote>
<p>참고 : <a href="https://mpaper-blog.tistory.com/54">https://mpaper-blog.tistory.com/54</a></p>
<h1 id="17-securestring">17. SecureString</h1>
<blockquote>
<p>//  String을 SecureString으로 바꾸는 방법
New System.Net.NetworkCredential(String.Empty, &quot;Sample Password).SecurePassword.ToString</p>
</blockquote>
<blockquote>
<p>// SecureString을 String으로 바꾸는 방법
new System.Net.NetworkCredential(String.Empty, secureStringVar).Password.ToString </p>
</blockquote>
<p>참고 : <a href="https://forum.uipath.com/t/convert-securestring-to-string-get-secured-credential/1704">https://forum.uipath.com/t/convert-securestring-to-string-get-secured-credential/1704</a></p>
<h1 id="18-string-줄바꿈을-기준으로-split">18. String 줄바꿈을 기준으로 split</h1>
<p>왜인지 split 할 때 줄바꿈을 기준으로 하면 잘 안됨 </p>
<blockquote>
<p>stringVar.Split(Environment.NewLine.ToArray, StringSplitOptions.RemoveEmptyEntries</p>
</blockquote>
<p>이렇게 하면 됨 </p>
<h1 id="19-폴더-존재-여부-확인">19. 폴더 존재 여부 확인</h1>
<blockquote>
<p>Directory.Exists(folderDir)</p>
</blockquote>
<h1 id="20-datatable---json">20. DataTable &lt;-&gt; Json</h1>
<blockquote>
<p>// DT를 Json으로 바꿀 때
Newtonsoft.Json.JsonConvert.SerializeObject(varDT)</p>
</blockquote>
<blockquote>
<p>// Json을 DT로 바꿀 때</p>
</blockquote>
<ol>
<li>read text file로 json 파일 읽기</li>
<li>Newtonsoft.Json.JsonConvert.DeserializeObject(Of DataTable)(varSTR)</li>
</ol>
<h1 id="21-시간">21. 시간</h1>
<p>로딩 시간을 지정할 때 사용하는 방법</p>
<blockquote>
<p>// 현재 시간
startDTM = now</p>
</blockquote>
<blockquote>
<p>// while 조건으로 아래와 같이 줌
startDTM.substract(now).Minute &lt; 10 </p>
</blockquote>
<p>위와 같이 주면 10분까지는 while문이 수행되므로 시간을 기준으로 반복문 돌릴 수 있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘 공부/파이썬] DFS 구현(인접 행렬, 인접 리스트, stack, 재귀함수)]]></title>
            <link>https://velog.io/@jwkim_1018/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EA%B3%B5%EB%B6%80%ED%8C%8C%EC%9D%B4%EC%8D%AC-DFS-%EA%B5%AC%ED%98%84%EC%9D%B8%EC%A0%91-%ED%96%89%EB%A0%AC-%EC%9D%B8%EC%A0%91-%EB%A6%AC%EC%8A%A4%ED%8A%B8-stack-%EC%9E%AC%EA%B7%80%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@jwkim_1018/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EA%B3%B5%EB%B6%80%ED%8C%8C%EC%9D%B4%EC%8D%AC-DFS-%EA%B5%AC%ED%98%84%EC%9D%B8%EC%A0%91-%ED%96%89%EB%A0%AC-%EC%9D%B8%EC%A0%91-%EB%A6%AC%EC%8A%A4%ED%8A%B8-stack-%EC%9E%AC%EA%B7%80%ED%95%A8%EC%88%98</guid>
            <pubDate>Tue, 07 Nov 2023 08:10:23 GMT</pubDate>
            <description><![CDATA[<h1 id="dfs-구현">DFS 구현</h1>
<p>DFS는 깊이 우선 탐색의 줄임말로 그래프 중 깊은 곳을 먼저 탐색하는 방법이다. 그래프를 표현하는 방식은 인접 행렬, 인접 리스트 방법이 있다. DFS를 구현할 때에는 스택 구조를 사용하는 것이 일반적이다. 그래서 이번 실습에서는 스택과 재귀함수로 각각 구해보도록 하자.</p>
<p>일반적으로 동일한 레벨에 방문해야하는 노드가 두 개 이상 있을 때, 숫자가 작은 순서대로 방문한다. 이 점을 유의하며 구현을 해보자.</p>
<h2 id="인접-행렬">인접 행렬</h2>
<p>구현에 사용될 데이터는 아래와 같다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/c622a481-8d7d-4df7-86cb-1cf360c50018/image.png" alt=""></p>
<pre><code class="language-python"># 인접 행렬 - 행렬과 노드 별 방문 여부를 나타내는 리스트
matrix = [
    [0, 0, 0, 0, 0], 
    [0, 0, 1, 1, 1], 
    [0, 1, 0, 0, 1], 
    [0, 1, 0, 0, 1], 
    [0, 1, 1, 1, 0]]
visited = [False] * len(matrix)</code></pre>
<h3 id="stack으로-구현">Stack으로 구현</h3>
<pre><code class="language-python"># stack으로 구현
def dfs_matrix_stack(matrix, start_node, visited) :
    need_to_visit, result = list(), list() # 확인해야 하는 노드를 담을 리스트와 결과 리트스

    need_to_visit.append(start_node) # 시작 노드 추가

    while need_to_visit : # 확인해야 하는 노드가 없을 떄까지 반복
        node = need_to_visit.pop() # 현재 방문 중인 노드
        if not visited[node] : # 이 노드에 방문한 적이 없다면
            visited[node] = True # 방문 기록 남기기
            result.append(node)
            for i in range(len(matrix[node])-1, 0, -1) : # 작은 수부터 돌아야 하므로 뒤에서 앞으로 반복
                if matrix[node][i] == 1 and not visited[i] : # 현재 노드와 연결되어있고 그 노드가 방문한 노드가 아니라면
                    need_to_visit.append(i) # 이 노드도 돌아야 하므로 돌아야 하는 노드에 추가

    return result

dfs_matrix_stack(matrix, 1, visited)</code></pre>
<pre><code class="language-python"># 결과
[1, 2, 4, 3]</code></pre>
<p>처음에 이해하기 어려웠던 점은 <code>range(len(matrix[node]) -1, 0, -1)</code>이다. 리스트를 뒤에서부터 도는 이유는 stack의 특성 때문인데, <code>pop()</code>은 리스트에서 가장 뒤에 있는 원소를 추출하기 때문이다. 만약 뒤로 반복하지 않는다면, <code>pop()</code>의 결과가 큰 수부터 나오게 될 것이다.(이해가 안되면 <code>need_to_visit</code>에 원소가 추가되고 나가는 순서를 손으로 써보자)</p>
<h3 id="재귀함수로-구현">재귀함수로 구현</h3>
<pre><code class="language-python"># 재귀함수로 구현
result = list() # 결과를 담을 리스트
def dfs_matrix_recursive(matrix, start_node, visited, result) :
    result.append(start_node) # 시작 노드 추가
    visited[start_node] = True # 시작 노드에 접근 기록

    for i in range(len(matrix[start_node])) : # 현재 노드와 인접한 모든 노드를 반복
        if matrix[start_node][i] == 1 and not visited[i] : # 현재 노드와 인접해있고, 방문한 기록이 없는 노드일 때
            dfs_matrix_recursive(matrix, i, visited, result) # 그 노드에 대해 dfs 함수를 또 적용

    return result

dfs_matrix_recursive(matrix, 1, visited, result)</code></pre>
<pre><code class="language-python"># 결과
[1, 2, 4, 3]</code></pre>
<p>재귀함수는 결과를 다음 반복으로 결과 리스트도 넘겨야하기 때문에 인자로 결과 리스트를 추가해주어야 한다. 현재 제공받은 인접 행렬에서 현재 방문한 노드의 행을 처음부터 반복하며 인접 여부를 판단해야하기 때문에 이번에는 반복을 그대로 돌렸다.(이것도 헷갈리면 손으로 써보기)</p>
<p><br></br></p>
<h2 id="인접-리스트">인접 리스트</h2>
<p>구현에 사용되는 데이터는 아래와 같다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/e3ff8ad7-892f-4bcc-ade7-af6d42e79d9f/image.png" alt=""></p>
<pre><code class="language-python"># 인접 리스트 - 노드는 인덱스가 1부터 시작하므로 첫 번쨰 요소는 빈 리스트를 넣음
graph = [
    [],
    [2, 3, 8], 
    [1, 7],
    [1, 4, 5],
    [3, 5],
    [3, 4],
    [7],
    [2, 6, 8],
    [1, 7]
]</code></pre>
<h3 id="stack으로-구현-1">Stack으로 구현</h3>
<pre><code class="language-python"># stack으로 구현
def dfs_list_stack(graph, start_node) :
    need_to_visit, visited = list(), list() # 확인해야 하는 노드를 담을 리스트와 결과 리트스
    need_to_visit.append(start_node) # 시작 노드 추가

    while need_to_visit : # 확인해야 하는 노드가 없을 때까지 반복
        node = need_to_visit.pop() # 현재 방문 중인 노드
        if node not in visited : # 이 노드에 방문한 적이 없다면
            visited.append(node) # 방문기록 남기기
            need_to_visit.extend(graph[node][::-1]) # 작은 숫자를 먼저 돌게 하기 위해 리스트 순서를 바꿔서 저장
    return visited

dfs_list_stack(graph, 1)</code></pre>
<pre><code class="language-python"># 결과
[1, 2, 7, 6, 8, 3, 4, 5]</code></pre>
<p>인접 행렬과 그리 다르지 않다. 확이냏야 하는 노드와 현재 노드 간의 관계를 생각하며 코드를 보면 이해가 더 쉬울 것이다. 이 때도 작은 숫자를 먼저 확인할 수 있게 확인해야할 노드를 추가할 때 그 순서를 바꿔주었다.([ : : -1] 사용)</p>
<h3 id="재귀함수로-구현-1">재귀함수로 구현</h3>
<pre><code class="language-python"># 재귀함수로 구현
visited = list()
def dfs_list_recursive(graph, start_node, visited) :
    visited.append(start_node)
    for node in graph[start_node]: # for문은 원소를 앞에서 부터 돌기 때문에 [::-1] 필요 없음
        if node not in visited :
            dfs_list_recursive(graph, node, visited)
    return visited

dfs_list_recursive(graph, 1, visited)</code></pre>
<pre><code class="language-python"># 결과
[1, 2, 7, 6, 8, 3, 4, 5]</code></pre>
<p>이 부분에서는 인접 행렬과 마찬가지로 확인할 원소는 오름차순 정려되어 있기 때문에 [ : : -1]을 하지 않아도 된다.</p>
<p><br></br>
참고한 사이트</p>
<ul>
<li><a href="https://data-marketing-bk.tistory.com/entry/DFS-%EC%99%84%EB%B2%BD-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-%ED%8C%8C%EC%9D%B4%EC%8D%AC">https://data-marketing-bk.tistory.com/entry/DFS-완벽-구현하기-파이썬</a></li>
<li><a href="https://velog.io/@tks7205/dfs%EC%99%80-bfs%EB%A5%BC-%EA%B5%AC%ED%98%84%ED%95%98%EB%8A%94-%EC%97%AC%EB%9F%AC%EA%B0%80%EC%A7%80-%EB%B0%A9%EB%B2%95-in-python">https://velog.io/@tks7205/dfs와-bfs를-구현하는-여러가지-방법-in-python</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스(Lv 1)/파이썬] 완주하지 못한 선수]]></title>
            <link>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%99%84%EC%A3%BC%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%84%A0%EC%88%98</link>
            <guid>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%99%84%EC%A3%BC%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%84%A0%EC%88%98</guid>
            <pubDate>Thu, 02 Nov 2023 02:23:14 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-문제">📌 문제</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/42576">https://school.programmers.co.kr/learn/courses/30/lessons/42576</a></p>
<h1 id="📌-풀이">📌 풀이</h1>
<h2 id="코드">코드</h2>
<pre><code class="language-python">def solution(participant, completion):
    # { key - 선수 이름 : value - 동명이인 수 }인 딕셔너리 만들기
    p = {name : 0 for name in participant}
    for name in participant :
        p[name] += 1

    # 완주한 선수가 나오면 인원 빼기
    for c in completion :
        p[c] -= 1

    # 딕셔너리 value가 0보다 크다는 것은 완주를 못한 선수라는 뜻임
    for key, val in p.items() :
        if val &gt; 0 :
            return key</code></pre>
<h2 id="설명">설명</h2>
<p>처음 작성한 코드는 아래와 같다.</p>
<pre><code class="language-python">def solution(participant, completion):
    for c in completion :
        participant.remove(c)

    return participant[0]</code></pre>
<p>이 코드도 동작은 하는데, 시간초과가 뜬다. 시간 복잡도가 $O(n^2)$이기 떄문이다. remove가 메소드기 때문에 한 줄로 쓰인거지 내부 동작하는 걸 보면 리스트에서 원소를 하나하나 돌기 때문에 for문이 하나 더 있는 것과 같다.</p>
<p>그래서 이를 해결하기 위해 위와 같이 코드를 수정했다. { 선수 이름 : 동명이인 수 } 형태인 딕셔너리를 만들고 완주한 선수를 빼는 식으로 동작한다. 이 코드의 시간 복잡도를 보면 for문이 네 개나 있긴 하지만 각각 독립되어있으므로 $O(n)$이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스(Lv 1)/파이썬] 크레인 인형뽑기 게임]]></title>
            <link>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%ED%81%AC%EB%A0%88%EC%9D%B8-%EC%9D%B8%ED%98%95%EB%BD%91%EA%B8%B0-%EA%B2%8C%EC%9E%84</link>
            <guid>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%ED%81%AC%EB%A0%88%EC%9D%B8-%EC%9D%B8%ED%98%95%EB%BD%91%EA%B8%B0-%EA%B2%8C%EC%9E%84</guid>
            <pubDate>Thu, 02 Nov 2023 01:57:50 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-문제">📌 문제</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/64061">https://school.programmers.co.kr/learn/courses/30/lessons/64061</a></p>
<h1 id="📌-풀이">📌 풀이</h1>
<h2 id="코드">코드</h2>
<pre><code class="language-python">def solution(board, moves):
    answer = 0
    stack = list()

    # 가로 기준으로 되어있는 칸 정보를 세로 기준으로 바꾸기
    new_board = list()
    for i in range(len(board)) :
        temp = list()
        for j in range(len(board)) :
            temp.append(board[j][i])
        new_board.append(temp)

    # moves를 돌면서 인형 빼기 - moves에서 1씩 뺀 후에 인덱싱(인덱스는 0부터니까)
    for m in moves :
        for i in range(len(board)) : # 칸 개수만큼 반복
            if new_board[m-1][i] != 0 : # 인형이 있는 칸이라면
                stack.append(new_board[m-1][i]) # 그 인형을 stack에 담고
                new_board[m-1][i] = 0 # 인형 판에서는 삭제

                # 맨 뒤 두 인형이 같은 인형이라면 빼주기
                if len(stack) &gt; 1 and stack[-1] == stack[-2] : 
                    stack.pop()
                    stack.pop()
                    answer += 2
                break

    return answer</code></pre>
<h2 id="설명">설명</h2>
<p>예제에서 든 예시를 그려보면 아래와 같다.
<img src="https://velog.velcdn.com/images/jwkim_1018/post/eaf57fe8-f35a-483f-9188-4300454f1f1e/image.png" alt=""></p>
<p>그런데 입력값으로 열 정보를 주고 있으므로 인덱싱해서 사용하기 위해 위 행렬을 전치해준다. 그러면 아래와 같은 행렬로 바뀐다.
<img src="https://velog.velcdn.com/images/jwkim_1018/post/26042765-a212-4bfe-8799-41dca334b76f/image.png" alt=""></p>
<p>이제 인덱싱을 사용할 수 있으므로 제시된 열에서 가장 마지막 숫자가 0이 아닌 경우에 빼서 stack에 넣는 방법으로 구현한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스(Lv 1)/파이썬] 키패드 누르기]]></title>
            <link>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%ED%82%A4%ED%8C%A8%EB%93%9C-%EB%88%84%EB%A5%B4%EA%B8%B0</link>
            <guid>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%ED%82%A4%ED%8C%A8%EB%93%9C-%EB%88%84%EB%A5%B4%EA%B8%B0</guid>
            <pubDate>Wed, 01 Nov 2023 13:18:19 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-문제">📌 문제</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/67256">https://school.programmers.co.kr/learn/courses/30/lessons/67256</a></p>
<h1 id="📌-풀이">📌 풀이</h1>
<h2 id="코드">코드</h2>
<pre><code class="language-python">def solution(numbers, hand):
    answer = &#39;&#39;

    # 키패드 위치를 좌표로 / 0을 기준점으로
    kp = {
        1 : (-1, 3),   2 : (0, 3),  3 : (1, 3) ,
        4 : (-1, 2),   5 : (0, 2),  6 : (1, 2) ,
        7 : (-1, 1),   8 : (0, 1),  9 : (1, 1) ,
        &#39;*&#39; : (-1, 0), 0 : (0, 0), &#39;#&#39; : (1, 0)
    }

    # 각 손의 좌표 초기화
    l = kp[&#39;*&#39;]
    r = kp[&#39;#&#39;]

    for n in numbers :
        if n in [1, 4, 7] : # 왼손으로 눌러야 하는 키패드들
            answer += &#39;L&#39;
            l = kp[n]
        elif n in [3, 6, 9] : # 오른손으로 눌러야 하는 키패드들
            answer += &#39;R&#39;
            r = kp[n]
        else : # 2, 5, 8, 0 중 하나일 때
            loc = kp[n] # 키패드 위치
            l_dist = abs(l[0] - loc[0]) + abs(l[1] - loc[1]) # 왼손에서 눌러야할 키패드 까지의 거리
            r_dist = abs(r[0] - loc[0]) + abs(r[1] - loc[1]) # 오른손에서 눌러야할 키패드 까지의 거리

            if l_dist &lt; r_dist : # 왼손이 가까우면
                answer += &#39;L&#39;
                l = loc
            elif r_dist &lt; l_dist : # 오른손이 가까우면
                answer += &#39;R&#39;
                r = loc
            else : # 왼손 오른손 둘 다 거리가 같으면
                if hand == &#39;left&#39; : # 왼손잡이라면
                    answer += &#39;L&#39;
                    l = loc
                else : # 오른손잡이라면
                    answer += &#39;R&#39;
                    r = loc

    return answer</code></pre>
<h2 id="설명">설명</h2>
<p>키패드를 좌표화 하는 것이 키포인트인듯 하다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스(Lv 1)/파이썬] 신고 결과 받기]]></title>
            <link>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%8B%A0%EA%B3%A0-%EA%B2%B0%EA%B3%BC-%EB%B0%9B%EA%B8%B0</link>
            <guid>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%8B%A0%EA%B3%A0-%EA%B2%B0%EA%B3%BC-%EB%B0%9B%EA%B8%B0</guid>
            <pubDate>Wed, 01 Nov 2023 03:58:07 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-문제">📌 문제</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/92334">https://school.programmers.co.kr/learn/courses/30/lessons/92334</a></p>
<h1 id="📌-풀이">📌 풀이</h1>
<h2 id="코드">코드</h2>
<pre><code class="language-python">def solution(id_list, report, k):
    answer = []

    id_dict = {i : set([]) for i in id_list} # id당 신고한 사람을 딕셔너리로 관리 / { 이름 : {} } -&gt; 중복을 제거해야하므로 set으로 설정
    num_dict = {i : 0 for i in id_list} # id당 신고 당한 횟수

    for r in report :
        p = r.split()
        temp = id_dict[p[0]] # 추가하기 이전 set
        id_dict[p[0]] = id_dict[p[0]].union({p[1]}) # 신고한 사람 추가
        if len(temp) != len(id_dict[p[0]]): # 이미 한 번 신고한 이력이 있으면 pass
            num_dict[p[1]] += 1 # 신고 당한 횟수 더하기

    # 신고 당한 횟수가 k번 이상인 사람
    p_list = [name for name, count in num_dict.items() if count &gt;= k] 

    # 개별 직원마다 반복
    for id in id_list :
        count = 0 # 안내 횟수 초기화
        for i in range(len(id_dict[id])) : # 이 사람이 신고한 사람 수만큼 반복
            if list(id_dict[id])[i] in p_list :
                count += 1
        answer.append(count)    

    return answer</code></pre>
<h2 id="설명">설명</h2>
<p>이번 문제는 딕셔너리 두 개를 가지고 풀었다. 각 딕셔너리는 아래와 같다.</p>
<ul>
<li><p>id_dict : 어떤 사람이 신고한 사람들을 담아놓음 </p>
<ul>
<li>key : 사람, value = set([])</li>
<li>여러번 신고해도 리스트업은 한 번만 되어야 하므로 리스트가 아니라 set으로 선언</li>
<li>set은 원소를 직접 추가할 수가 없고 대신 set과 set을 합치는 방법을 써야함 그래서 set.union()을 사용</li>
<li>예 : { &quot;muzi&quot; : [&quot;frodo&quot;, &quot;neo&quot;] }</li>
</ul>
</li>
<li><p>num_dict : 신고 당한 횟수를 담아놓음</p>
<ul>
<li>중복으로 신고한 경우 1회로 치기 때문에 이를 판단하기 위해 set의 길이를 기준으로 판단함</li>
<li>신고 대상자를 넣기 전 길이와 넣은 후 길이가 같다는 것은 중복 신고 했다는 것이므로 카운팅 하면 안 됨</li>
<li>예 : { &quot;muzi&quot; : 2 }</li>
</ul>
</li>
</ul>
<p>이렇게 두 개의 딕셔너리를 세팅했다면, 신고 당한 횟수가 k 이상인 직원을 찾아 p_list에 넣는다.</p>
<p>이제 id_dict에서 신고한 사람이 k 이상인지 여부를 판단하는데, 반복마다 k회 이상인 사람의 수를 셀 count를 초기화 해 넣어준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스(Lv 1)/파이썬] 성격유형 검사하기]]></title>
            <link>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%84%B1%EA%B2%A9%EC%9C%A0%ED%98%95-%EA%B2%80%EC%82%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%84%B1%EA%B2%A9%EC%9C%A0%ED%98%95-%EA%B2%80%EC%82%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 31 Oct 2023 07:03:20 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-문제">📌 문제</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/118666">https://school.programmers.co.kr/learn/courses/30/lessons/118666</a></p>
<h1 id="📌-풀이">📌 풀이</h1>
<h2 id="코드">코드</h2>
<pre><code class="language-python">def solution(survey, choices):

    dict_l = { 1 : 3, 2 : 2, 3 : 1} # 왼쪽에 더할 점수 환산
    dict_r = { 5 : 1, 6 : 2, 7 : 3} # 오른쪽에 더할 점수 환산

    # 각 검사별 점수
    RT = {&#39;R&#39; : 0, &#39;T&#39; : 0}
    CF = {&#39;C&#39; : 0, &#39;F&#39; : 0}
    JM = {&#39;J&#39; : 0, &#39;M&#39; : 0}
    AN = {&#39;A&#39; : 0, &#39;N&#39; : 0}

    # 검사들을 담아놓은 딕셔너리
    d = {&#39;R&#39; : RT, &#39;T&#39; : RT, &#39;C&#39; : CF, &#39;F&#39; : CF, &#39;J&#39; : JM, &#39;M&#39; : JM, &#39;A&#39; : AN, &#39;N&#39; : AN}

    # 점수 계산
    for i in range(len(choices)) : 
        s = survey[i] 
        if choices[i] &lt; 4 : # 앞 글자에 점수 더하기
            d[s[0]][s[0]] = d[s[0]][s[0]] + dict_l[choices[i]]
        elif choices[i] &gt; 4 : # 뒷 글자에 점수 더하기
            d[s[1]][s[1]] = d[s[1]][s[1]] + dict_r[choices[i]]

    # 결과 반환
    rt = sorted(RT.items(), key=lambda x : x[1], reverse=True)[0][0]
    cf = sorted(CF.items(), key=lambda x : x[1], reverse=True)[0][0]
    jm = sorted(JM.items(), key=lambda x : x[1], reverse=True)[0][0]
    an = sorted(AN.items(), key=lambda x : x[1], reverse=True)[0][0]

    answer = rt + cf + jm + an    

    return answer</code></pre>
<h2 id="설명">설명</h2>
<p> 이번 문제는 딕셔너리를 활용해 풀어보았다.</p>
<p>점수별로 왼쪽에 점수가 들어가야하는지, 오른쪽에 들어가야 하는지 다르다. 그래서 그 더해야 하는 점수를 환산하기 위해 dict_l과 dict_r을 각각 만든다.</p>
<p>그리고 각 성격 유형별로 점수를 계산하기 위해 4 개의 성격 유형별로 각각 딕셔너리를 따로 만든다.(RT, CF, JM, AN) 또한 각 문자열마다 점수를 계산하는 딕셔너리를 가져와야하므로(예를 들어 &#39;R&#39;이 나오면 RT를, &#39;F&#39;가 나오면 CF를 가져와야 하므로) 각각 알파벳에 맞게 딕셔너리를 연결해준다.</p>
<p>점수를 돌면서 각각 점수를 더한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스(Lv 1)/파이썬] 기사단원의 무기]]></title>
            <link>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EA%B8%B0%EC%82%AC%EB%8B%A8%EC%9B%90%EC%9D%98-%EB%AC%B4%EA%B8%B0</link>
            <guid>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EA%B8%B0%EC%82%AC%EB%8B%A8%EC%9B%90%EC%9D%98-%EB%AC%B4%EA%B8%B0</guid>
            <pubDate>Wed, 25 Oct 2023 05:43:47 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-문제">📌 문제</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/136798">https://school.programmers.co.kr/learn/courses/30/lessons/136798</a></p>
<h1 id="📌-풀이">📌 풀이</h1>
<h2 id="코드">코드</h2>
<pre><code class="language-python">def solution(number, limit, power):
    answer = 0

    for num in range(1, number + 1) :
        count = 0 # 각 숫자별 약수 개수
        for i in range(1, int(num ** 0.5) + 1) : # 숫자의 절반만 확인하면 됨
            if num % i == 0 : 
                count += 1
                if  i ** 2 != num : # 거듭제곱이 약수가 아니라면 약수는 쌍으로 존재하기 때문에 한 개 더해줌
                    count += 1

        if count &gt; limit :
            answer += power
        else :
            answer += count


    return answer</code></pre>
<h2 id="설명">설명</h2>
<p>어떤 자연수 N의 약수를 구할 때, 1부터 N까지 나누어 떨어지는지 확인해야 한다. 그런데 이렇게 되면 시간이 매우 오래걸린다. 그래서 약수의 아래와 같은 약수의 특징을 생각해보자.</p>
<ul>
<li>약수는 항상 쌍으로 존재한다.(나누어 떨어진다는 것은 나누는 수와 몫이 있어야 되기 때문)</li>
<li>그런데 N이 어떤 수의 제곱수이면 약수는 쌍이 아니라 한 개만 존재한다.(중복이 없어야 하기 때문)</li>
<li>약수를 판단할 때 약수는 쌍으로 존재하기 때문에 $\sqrt N$ 까지만 확인하면 된다.</li>
</ul>
<p>그래서의 개수를 확인할 때(두 번쨰 for) 반복 조건에 num ** 0.5 를 준 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[기초자치단체 민원 자동 분류기]]></title>
            <link>https://velog.io/@jwkim_1018/%EA%B8%B0%EC%B4%88%EC%9E%90%EC%B9%98%EB%8B%A8%EC%B2%B4-%EB%AF%BC%EC%9B%90-%EC%9E%90%EB%8F%99-%EB%B6%84%EB%A5%98%EA%B8%B0</link>
            <guid>https://velog.io/@jwkim_1018/%EA%B8%B0%EC%B4%88%EC%9E%90%EC%B9%98%EB%8B%A8%EC%B2%B4-%EB%AF%BC%EC%9B%90-%EC%9E%90%EB%8F%99-%EB%B6%84%EB%A5%98%EA%B8%B0</guid>
            <pubDate>Tue, 24 Oct 2023 02:42:31 GMT</pubDate>
            <description><![CDATA[<h1 id="1-배경">1. 배경</h1>
<h2 id="1-1-민원인-입장">1-1. 민원인 입장</h2>
<p>구청에 민원을 넣어본 사람이라면, 답변을 받기까지 오랜시간이 걸리는 것을 한 번 쯤은 경험해 보았을 것이다. 실제로 우리 조의 한 조원은 민원 처리에만 50일이 걸렸고, 정확한 처리 기관을 미리 알 수 없어 하염없이 기다려야 했다. 물론 민원을 처리하는 구청 입장에서는 민원인에게 정확한 답변을 주기 위해 시간이 오래 걸리는 것일 수도 있지만, 민원인 입장에서는 소위 &quot;민원 뺑뺑이&quot;라는 느낌을 받는다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/79bb1b45-c0d8-4500-a51b-c195b409f44f/image.png" alt=""></p>
<h2 id="1-2-민원-처리인-입장">1-2. 민원 처리인 입장</h2>
<p>민원을 처리하는 공무원들은 어떨까? 공무원들은 아래 자료처럼 업무 과중을 겪는 사람들이 많고, 그 중 민원을 분류하는 과정에서 소모되는 인력이 많고 민원 분류 관련된 지원도 적다고 한다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/59238e59-87dd-4459-89e6-0bff076275c4/image.png" alt=""></p>
<p>아래 자료를 보면 확실히 기초자치단체의 민원 업무 강도가 높다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/bbdac53a-f1a0-4bff-a632-b4ae23c009a8/image.png" alt=""></p>
<h1 id="2-데이터">2. 데이터</h1>
<p>민원 자동 분류기를 만들기 위해 필요한 데이터는 크게 두 가지이다. 민원 데이터와 그 민원을 처리한 처리 부서 데이터가 그것이다.</p>
<h2 id="2-1-raw-데이터">2-1. raw 데이터</h2>
<p>민원 데이터는 각 지자체에 접수되는 민원을 통합적으로 관리하는 시스템인 &#39;새올 전자민원창구&#39;(이하 새올)를 이용한다. 새올의 특징은 아래와 같다.</p>
<ul>
<li>통합 관리 시스템이긴 하지만, 각 지자체마다 민원을 접수하는 페이지는 모두 다르다.(서울시는 25개의 구이므로 25개의 페이지를 각각 크롤링 해야함)</li>
<li>민원인이 &#39;공개민원&#39;으로 지정한 민원만 확인 가능(새올에서 확인 가능한 것보다 더 많은 민원이 있을 것)</li>
<li>민원이 처리 되었다면, 민원 처리 부서(A국 B과 형태)와 답변 내용이 댓글처럼 달림</li>
</ul>
<p>민원 처리 부서 데이터는 구청 홈페이지에서 조직도를 통해 과별 담당 업무를 가져왔다. 그런데 각 구청마다 홈페이지 구조, 표기 방식 등이 전부 다르기 때문에 일일이 손으로 가져올 수 밖에 없었다. 구청의 조직 구조는 다행이 같았는데, ~국 ~과 ~ 팀 형태였다. 예를 들면 &#39;행정자치국 교육미래과 교육지원팀&#39; 형식이다. 우리는 조직 구조 중 과에 집중하기로 결정했다.(국은 너무 단위가 커서 업무 분류가 거의 의미 없고, 팀은 너무 많아서 오히려 분류에 방해가 됨 / 민원 데이터에 답변한 과까지 나와있으므로 과를 기준으로 삼음)</p>
<p>이렇게 얻은 데이터는 민원 데이터 124,698건, 민원 처리 부서(과) 934건을 확보했다.</p>
<h2 id="2-2-데이터-전처리">2-2. 데이터 전처리</h2>
<p>민원 데이터에 거친 전처리 방법은 아래와 같다.</p>
<ol>
<li><p>결측치 처리
민원 데이터 중 새올 자체의 문제로 제대로 처리되지 않은 데이터는 결측치로 나와 결측치는 제거했다.(답변 부서에 대한 정보가 없는 경우가 대다수였으므로 사용이 불가능한 데이터임) </p>
</li>
<li><p>이송이첩 및 행정동에서 처리한 데이터 제거
이송이첩이나 행정동에 이관한 경우에도 구청에서 담당한 과가 있어야 한다. 그런데 새올에는 구청의 담당과가 아니라 이송이첩 받은 기관이나 행정동이 나와있었다. 따라서 구청의 과를 알아야 하는 우리로서는 사용할 수 없는 데이터이므로 제거했다.</p>
</li>
<li><p>과거에 개편되거나 통합된 과 확인 및 최신화
민원 데이터 중 약 4만개의 데이터가 현재 존재하지 않는 부서이다. 그래서 각 구청에 문의하여 해당 업무를 담당하고 있는 현재 부서를 맵핑했다.</p>
</li>
</ol>
<p>위와 같은 과정을 거쳐 민원 데이터는 88,093건이 남았다.</p>
<p>처리 부서와 관련된 데이터에서 가장 중요한 것은 이 과가 어떤 일을 하는지 나타내는 &#39;과별 업무&#39;이다. 과별 업무 데이터는 지울 것이 없으므로 텍스트 정제 과정만 거친다. 과별 업무에서 한글, 숫자만 남겼다.</p>
<h1 id="3-구현">3. 구현</h1>
<h2 id="3-1-데이터-흐름">3-1. 데이터 흐름</h2>
<p><img src="https://velog.velcdn.com/images/jwkim_1018/post/1f566072-4768-45cd-90ba-e0833b21c6c7/image.png" alt="">
데이터는 위와 같이 흘러간다. 과별 업무 데이터는 토픽 모델링을 통해 군집화를 진행한다. 이렇게 나온 과 군집을 민원 데이터에 join하고 BERT 기반 모델에 넣어 fine tuning 한다. </p>
<h2 id="3-2-토픽-모델링">3-2. 토픽 모델링</h2>
<p>과별 업무에 토픽 모델링을 적용해 군집화한다. 토픽 모델링 필요한 이유는 아래와 같다.</p>
<ul>
<li>기초자치단체의 업무는 어느 구청이던 대부분 공통된 업무가 대부분임</li>
<li>그런데 기초자치단체 별로 부서 명칭이 다르거나 조직 구성이 약간씩 다른 경우가 있음</li>
</ul>
<p>전국 모든 기초자치단체에 적용할 수 있는 프로세스를 만들기 위해서는 위와 같은 이슈를 해결할 필요가 있다. 그래서 과별 업무를 기준으로 과들을 군집화 하는 것이다. </p>
<p>토픽 모델링 방법론은 다양하다. 머신러닝 기반, 통계 기반 등 기초를 둔 분야도 다양하다. 그 중 우리는 NMF 방식을 선택했다. 다른 방법들도 사용해보았는데, NMF 방식이 제일 성능이 좋았기에 해당 방법을 사용하기로 했다.
<img src="https://velog.velcdn.com/images/jwkim_1018/post/7f3d3e70-025b-4ce7-8077-c4a74704b1e0/image.png" alt=""></p>
<p>그런데 토픽 모델링 결과에는 문제가 있었다. 군집화 점수가 82%인 만큼 잘 되었는데 데이터 불균형 문제가 크다. 그래서 데이터 수가 적은 클래스에 대해서는 chat GPT에 데이터 증강을 요청해 데이터 불균형 문제를 해결하고자 했다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/aee774a5-392c-42c0-922d-923fa0489951/image.png" alt=""></p>
<h2 id="3-3-분류-모델">3-3. 분류 모델</h2>
<p><img src="https://velog.velcdn.com/images/jwkim_1018/post/7f07c513-096c-44b0-84df-15b7507289b2/image.png" alt=""> 위와 같이 여러 pre-trained 모델들을 fine-tuning 했다. 그 결과 DistilKoBERT 모델이 가장 우수한 성능을 보여 해당 모델로 선택했다. 그런데 대체로 성능이 별로 좋지 않은 것을 볼 수 있는데, 시간이나 GPU 장비의 한계로 각 모델마다 5 epoch까지 밖에 학습하지 않았기에 성능이 그리 좋지 않은 것이다. </p>
<p>모델 학습에는 민원 데이터를 넣는다. 이 때 target은 민원 처리 부서, feature는 민원 제목과 민원 내용이다. </p>
<h2 id="3-4-최종-워크플로우">3-4. 최종 워크플로우</h2>
<p><img src="https://velog.velcdn.com/images/jwkim_1018/post/80d06e9d-d072-4c29-b359-514bb53bb62d/image.png" alt=""></p>
<h1 id="4-시연">4. 시연</h1>
<p>시연 영상은 아래 링크에서 확인할 수 있다.
<a href="https://youtu.be/uWeIt_C9EBI">https://youtu.be/uWeIt_C9EBI</a></p>
<h1 id="5-기대효과">5. 기대효과</h1>
<h2 id="5-1-민원인-입장">5-1. 민원인 입장</h2>
<p>민원이 접수 되었을 때 처리할 부서가 특정되므로 민원 뼁뺑이가 발생할 일이 적어질 것이다. 또한 다른 부서로 이송이첩 하는 경우도 많이 줄어들 것이므로 빠른 민원 처리를 경험할 수 있다.</p>
<p>담당 부서를 예측하여 처리할 부서를 미리 제시함으로써 민원 처리를 하염없이 기다리는 것이 아니라 추가 정보를 얻는 측면에서 고객 경험을 강화하고, 민원인은 민원을 접수하기 전에 민원 처리 부서를 확인하므로써 필요한 자료 등을 문의할 수 있다.</p>
<h2 id="5-2-기초자치단체">5-2. 기초자치단체</h2>
<p>대부분의 민원을 정확하게 분류할 수 있으므로 민원 분류 업무 강도를 획기적으로 줄일 수 있고, 민원 분류와 같은 단순 업무에서 벗어나 가치가 더 높은 업무에 집중할 수 있다.</p>
<p>민원 접수를 자동화 할 수 있는 기회를 제공할 수 있다. 민원 접수 자동화 시스템은 시스템 도입이 어려운 작은 기초자치단체에게 분류 모델 하나만 제공해도 큰 효과를 거둘 수 있기 때문에 경제적이며 민원인들에게 답변을 빠르게 제공해 신뢰성을 확보할 수 있다.</p>
<p>또한 과중한 업무에 퇴사를 고려하는 공무원 수가 많은 만큼 한 부분이라도 업무 강도를 줄여 장기적인 인력 손실을 방지할 수 있다.</p>
<h1 id="6-느낀점">6. 느낀점</h1>
<h2 id="6-1-시간-관리의-중요성">6-1. 시간 관리의 중요성</h2>
<p>이번 프로젝트에서는 시간 관리의 중요성을 절실히 느꼈다. 시간 관리 중에서도 특히 큰 틀에서의 계획과 업무 수행 중 계획 수정이 중요함을 느꼈다</p>
<p>이 프로젝트는 약 한 달 반 동안 진행되었는데, 프로젝트를 완성하기에 매우 짧은 시간이었기에 시간관리가 무척 중요했다. 그래서 우리 팀은 시작에 앞서 주차 단위로 주요 과업을 선정해 스케쥴을 작성했다. 프로젝트 주제는 우리 모두가 제대로 잘 알고 있는 분야도 아니었고 자연어 처리 기술도 제대로 몰랐기에 공부와 학습을 병행해야 하는 상황에서 그 둘의 균형을 맞추는 것이 관건이었다. 각 주차마다 어떤 일을 해야하는지 큰 틀이 정해지니 시간이 부족하다는 불안감이 덜하고 오히려 현재 상황에 집중할 수 있었다.</p>
<p>계획을 세우긴 했지만, 그대로 실천하기에는 어려운 점이 많았다. 우리의 체력을 과대평가 했던 점, 공부하는 분야를 명확히 알지 못해 제대로된 로드맵을 그리지 못했던 점 등이 계획대로 되지 않았던 주요한 원인이다. 그래도 우리는 앞서 세웠던 계획을 최대한 유지하면서 현재 상황에 맞춰 조금씩 계획을 수정했다. 이렇게 유연하게 업무 계획을 수정한 덕분에 한 명도 무리하지 않고 무사히 프로젝트를 마무리했다고 생각한다.</p>
<h2 id="6-2-몰입">6-2. 몰입</h2>
<p>그동안 진행한 프로젝트는 대부분 학교 수업과 병행한 것이기 때문에 온전히 시간과 노력을 쏟기 어려웠다. 하지만 이번 프로젝트는 여름방학 동안에 진행한 프로젝트이기에 온전히 집중할 수 있었다. 팀원들과 아침부터 밤까지 함께 고민하고 공부하며 프로젝트 준비를 했던 기억이 좋게 남아있다. 그리고 이러한 경험 덕분에 앞으로 프로젝트와 다른 일을 병행하더라도 프로젝트에 집중할 수 있을 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스(Lv 1)/파이썬] 대충 만든 자판]]></title>
            <link>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%8C%80%EC%B6%A9-%EB%A7%8C%EB%93%A0-%EC%9E%90%ED%8C%90</link>
            <guid>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%8C%80%EC%B6%A9-%EB%A7%8C%EB%93%A0-%EC%9E%90%ED%8C%90</guid>
            <pubDate>Sat, 21 Oct 2023 03:09:30 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-문제">📌 문제</h1>
<p>아래 링크 참고</p>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/160586">https://school.programmers.co.kr/learn/courses/30/lessons/160586</a></p>
<h1 id="📌-풀이">📌 풀이</h1>
<h2 id="코드">코드</h2>
<pre><code class="language-python">def solution(keymap, targets):
    answer = []

    al_dict = {chr(i) : -1 for i in range(ord(&#39;A&#39;), ord(&#39;Z&#39;) + 1)} # 알파벳 딕셔너리 만들기

    # 알파벳 별로 출력을 위한 최소 횟수 업데이트
    for key in keymap :
        for k in key :
            if al_dict[k] == -1 : # 키가 처음 나왔을 때는 그 값으로 대체
                al_dict[k] = key.find(k) + 1
            else : # 두 번 이상 나왔을 때
                al_dict[k] = min(al_dict[k], key.find(k) + 1)

    for target in targets :
        num = 0
        for t in target :
            if al_dict[t] == -1 : # 자판에 없는 문자라면
                num = -1
                break
            else :
                num += al_dict[t]
        answer.append(num)

    return answer</code></pre>
<h2 id="설명">설명</h2>
<p>input으로는 알파벳만 받기 때문에 각 알파벳 별로 자판을 누르는 최소 값을 업데이트 한다. 그 후 target에서 누를 수 있는 자판이 있는지 확인하고 조건에 맞게 값을 출력한다.</p>
<h2 id="추가-코드">추가 코드</h2>
<pre><code class="language-python">def solution(keymap, targets):
    answer = []

    al_dict = {chr(i) : 101 for i in range(ord(&#39;A&#39;), ord(&#39;Z&#39;) + 1)} # 알파벳 딕셔너리 만들기

    # 알파벳 별로 출력을 위한 최소 횟수 업데이트
    for key in keymap :
        for k in key :
            al_dict[k] = min(al_dict[k], key.find(k) + 1)

    for target in targets :
        num = 0
        for t in target :
            if al_dict[t] == 101 : # 자판에 없는 문자라면
                num = -1
                break
            else :
                num += al_dict[t]
        answer.append(num)

    return answer</code></pre>
<p>첫 번째 코드와 달라진 점은 딕셔너리 초기화 값과 비교값이 -1에서 101로 바뀐 점이다. 왜 101로 넣냐면 keymap으로 받는 원소의 최대 길이가 100이기 때문이다.</p>
<p>만약 keymap에서 구현할 수 있는 문자열이 있다면, key.find(k)가 무조건 100보다는 작을 것이다. 그래서 min()을 할 때 그 알파벳이 처음 나온 알파벳인지 조건으로 제시하지 않아도 된다!!!! </p>
<p>이 스킬은 다른 문제에서도 얼마든 활용할 수 있으니 꼭 알아두자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스(Lv 1)/파이썬] 바탕화면 정리]]></title>
            <link>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%B0%94%ED%83%95%ED%99%94%EB%A9%B4-%EC%A0%95</link>
            <guid>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%B0%94%ED%83%95%ED%99%94%EB%A9%B4-%EC%A0%95</guid>
            <pubDate>Fri, 20 Oct 2023 12:34:07 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-문제">📌 문제</h1>
<h2 id="문제-설명">문제 설명</h2>
<p>코딩테스트를 준비하는 머쓱이는 프로그래머스에서 문제를 풀고 나중에 다시 코드를 보면서 공부하려고 작성한 코드를 컴퓨터 바탕화면에 아무 위치에나 저장해 둡니다. 저장한 코드가 많아지면서 머쓱이는 본인의 컴퓨터 바탕화면이 너무 지저분하다고 생각했습니다. 프로그래머스에서 작성했던 코드는 그 문제에 가서 다시 볼 수 있기 때문에 저장해 둔 파일들을 전부 삭제하기로 했습니다.</p>
<p>컴퓨터 바탕화면은 각 칸이 정사각형인 격자판입니다. 이때 컴퓨터 바탕화면의 상태를 나타낸 문자열 배열 wallpaper가 주어집니다. 파일들은 바탕화면의 격자칸에 위치하고 바탕화면의 격자점들은 바탕화면의 가장 왼쪽 위를 (0, 0)으로 시작해 (세로 좌표, 가로 좌표)로 표현합니다. 빈칸은 &quot;.&quot;, 파일이 있는 칸은 &quot;#&quot;의 값을 가집니다. 드래그를 하면 파일들을 선택할 수 있고, 선택된 파일들을 삭제할 수 있습니다. 머쓱이는 최소한의 이동거리를 갖는 한 번의 드래그로 모든 파일을 선택해서 한 번에 지우려고 하며 드래그로 파일들을 선택하는 방법은 다음과 같습니다.</p>
<ul>
<li><p>드래그는 바탕화면의 격자점 S(lux, luy)를 마우스 왼쪽 버튼으로 클릭한 상태로 격자점 E(rdx, rdy)로 이동한 뒤 마우스 왼쪽 버튼을 떼는 행동입니다. 이때, &quot;점 S에서 점 E로 드래그한다&quot;고 표현하고 점 S와 점 E를 각각 드래그의 시작점, 끝점이라고 표현합니다.</p>
</li>
<li><p>점 S(lux, luy)에서 점 E(rdx, rdy)로 드래그를 할 때, &quot;드래그 한 거리&quot;는 |rdx - lux| + |rdy - luy|로 정의합니다.</p>
</li>
<li><p>점 S에서 점 E로 드래그를 하면 바탕화면에서 두 격자점을 각각 왼쪽 위, 오른쪽 아래로 하는 직사각형 내부에 있는 모든 파일이 선택됩니다.</p>
</li>
</ul>
<p>예를 들어 wallpaper = [&quot;.#...&quot;, &quot;..#..&quot;, &quot;...#.&quot;]인 바탕화면을 그림으로 나타내면 다음과 같습니다.
<img src="https://velog.velcdn.com/images/jwkim_1018/post/adb4a31a-a2e4-4f4b-80ce-ee0e6ba456ea/image.png" alt=""></p>
<p>이러한 바탕화면에서 다음 그림과 같이 S(0, 1)에서 E(3, 4)로 드래그하면 세 개의 파일이 모두 선택되므로 드래그 한 거리 (3 - 0) + (4 - 1) = 6을 최솟값으로 모든 파일을 선택 가능합니다.
<img src="https://velog.velcdn.com/images/jwkim_1018/post/563f4337-9f86-43e2-bdad-29ff3f80330b/image.png" alt="">
(0, 0)에서 (3, 5)로 드래그해도 모든 파일을 선택할 수 있지만 이때 드래그 한 거리는 (3 - 0) + (5 - 0) = 8이고 이전의 방법보다 거리가 늘어납니다.</p>
<p>머쓱이의 컴퓨터 바탕화면의 상태를 나타내는 문자열 배열 wallpaper가 매개변수로 주어질 때 바탕화면의 파일들을 한 번에 삭제하기 위해 최소한의 이동거리를 갖는 드래그의 시작점과 끝점을 담은 정수 배열을 return하는 solution 함수를 작성해 주세요. 드래그의 시작점이 (lux, luy), 끝점이 (rdx, rdy)라면 정수 배열 [lux, luy, rdx, rdy]를 return하면 됩니다.</p>
<h2 id="제한사항">제한사항</h2>
<ul>
<li>1 ≤ wallpaper의 길이 ≤ 50</li>
<li>1 ≤ wallpaper[i]의 길이 ≤ 50<ul>
<li>wallpaper의 모든 원소의 길이는 동일합니다.</li>
</ul>
</li>
<li>wallpaper[i][j]는 바탕화면에서 i + 1행 j + 1열에 해당하는 칸의 상태를 나타냅니다.</li>
<li>wallpaper[i][j]는 &quot;#&quot; 또는 &quot;.&quot;의 값만 가집니다.</li>
<li>바탕화면에는 적어도 하나의 파일이 있습니다.</li>
<li>드래그 시작점 (lux, luy)와 끝점 (rdx, rdy)는 lux &lt; rdx, luy &lt; rdy를 만족해야 합니다.</li>
</ul>
<h2 id="입출력-예">입출력 예</h2>
<table>
<thead>
<tr>
<th>wallpaper</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>[&quot;.#...&quot;, &quot;..#..&quot;, &quot;...#.&quot;]</td>
<td>[0, 1, 3, 4]</td>
</tr>
<tr>
<td>[&quot;..........&quot;, &quot;.....#....&quot;, &quot;......##..&quot;, &quot;...##.....&quot;, &quot;....#.....&quot;]</td>
<td>[1, 3, 5, 8]</td>
</tr>
<tr>
<td>[&quot;.##...##.&quot;, &quot;#..#.#..#&quot;, &quot;#...#...#&quot;, &quot;.#.....#.&quot;, &quot;..#...#..&quot;, &quot;...#.#...&quot;, &quot;....#....&quot;]</td>
<td>[0, 0, 7, 9]</td>
</tr>
<tr>
<td>[&quot;..&quot;, &quot;#.&quot;]</td>
<td>[1, 0, 2, 1]</td>
</tr>
</tbody></table>
<h2 id="입출력-예-설명">입출력 예 설명</h2>
<p>입출력 예 #1</p>
<ul>
<li>문제 설명의 예시와 같은 예제입니다. (0, 1)에서 (3, 4)로 드래그 하면 모든 파일을 선택할 수 있고 드래그 한 거리는 6이었고, 6보다 적은 거리로 모든 파일을 선택하는 방법은 없습니다. 따라서 [0, 1, 3, 4]를 return합니다.</li>
</ul>
<p>입출력 예 #2</p>
<ul>
<li>예제 2번의 바탕화면은 다음과 같습니다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/f95bb75c-497b-4fe9-bbb6-eaaeac689cf5/image.png" alt="">(1, 3)에서 (5, 8)로 드래그하면 모든 파일을 선택할 수 있고 이보다 적은 이동거리로 모든 파일을 선택하는 방법은 없습니다. 따라서 가장 적은 이동의 드래그로 모든 파일을 선택하는 방법인 [1, 3, 5, 8]을 return합니다.</li>
</ul>
<p>입출력 예 #3</p>
<ul>
<li>예제 3번의 바탕화면은 다음과 같습니다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/d81983c3-3e67-4eb0-a4db-c69d6cb95fac/image.png" alt="">모든 파일을 선택하기 위해선 바탕화면의 가장 왼쪽 위 (0, 0)에서 가장 오른쪽 아래 (7, 9)로 드래그 해야만 합니다. 따라서 [0, 0, 7, 9]를 return합니다.</li>
</ul>
<p>입출력 예 #4</p>
<ul>
<li>예제 4번의 바탕화면은 다음과 같이 2행 1열에만 아이콘이 있습니다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/82daab6f-20fb-49b7-bc0a-7522136867c5/image.png" alt="">이를 드래그로 선택하기 위해서는 그 칸의 왼쪽 위 (1, 0)에서 오른쪽 아래 (2, 1)로 드래그 하면 됩니다. (1, 0)에서 (2, 2)로 드래그 해도 아이콘을 선택할 수 있지만 이전보다 이동거리가 늘어납니다. 따라서 [1, 0, 2, 1]을 return합니다.</li>
</ul>
<h1 id="📌-풀이">📌 풀이</h1>
<h2 id="코드">코드</h2>
<pre><code class="language-python">def solution(wallpaper):

    W = len(wallpaper[0]) # 바탕화면 가로 칸 수
    H = len(wallpaper) # 바탕화면 세로 칸 수

    # 1이 있는 격자 위치 담기 =&gt; 파일이 있으면 그 파일 앞 뒤 격자점은 1
    grid_x, grid_y = list(), list() 

    for r in range(H) :
        if &#39;#&#39; in wallpaper[r] : # 로우에 파일이 있으면 현재 로우와 다음 로우를 저장
            grid_x.append(r)
            grid_x.append(r + 1)
            for c in range(W) : # 컬럼에 파일이 있으면 컬럼과 다음 컬럼 저장
                if &#39;#&#39; == wallpaper[r][c] :
                    grid_y.append(c)
                    grid_y.append(c + 1)


    answer = [min(grid_x), min(grid_y), max(grid_x), max(grind_y)]
    return answer</code></pre>
<h2 id="설명">설명</h2>
<p>제시되는 wallpaper는 파일의 위치이다. 그런데 드래그는 파일 위에서 하는 것이 아니라 격자점에서 한다. 그래서 우리가 알아야 할 위치는 파일의 좌표가 아니라 격자점의 좌표가 필요하다. </p>
<p>하나의 파일이 있을 때 드래그 할 수 있는 지점은 아래 그림과 같이파일이 있는 칸의 네 개의 격자점 모두이다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/1fec55e6-a706-4854-a29c-d4495f55fdcc/image.png" alt="">
따라서 파일을 돌면서 파일이 있는 인덱스와 그 다음 인덱스를 저장해준다.(x,y 모두) 그리고 최대값, 최소값으로 lux, luy, rdx, rdy를 구한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스(Lv 1)/파이썬] 공원 산책]]></title>
            <link>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EA%B3%B5%EC%9B%90-%EC%82%B0%EC%B1%85</link>
            <guid>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EA%B3%B5%EC%9B%90-%EC%82%B0%EC%B1%85</guid>
            <pubDate>Fri, 20 Oct 2023 04:13:58 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-문제">📌 문제</h1>
<h2 id="문제-설명">문제 설명</h2>
<p>지나다니는 길을 &#39;O&#39;, 장애물을 &#39;X&#39;로 나타낸 직사각형 격자 모양의 공원에서 로봇 강아지가 산책을 하려합니다. 산책은 로봇 강아지에 미리 입력된 명령에 따라 진행하며, 명령은 다음과 같은 형식으로 주어집니다.</p>
<p>[&quot;방향 거리&quot;, &quot;방향 거리&quot; … ]
예를 들어 &quot;E 5&quot;는 로봇 강아지가 현재 위치에서 동쪽으로 5칸 이동했다는 의미입니다. 로봇 강아지는 명령을 수행하기 전에 다음 두 가지를 먼저 확인합니다.</p>
<p>주어진 방향으로 이동할 때 공원을 벗어나는지 확인합니다.
주어진 방향으로 이동 중 장애물을 만나는지 확인합니다.
위 두 가지중 어느 하나라도 해당된다면, 로봇 강아지는 해당 명령을 무시하고 다음 명령을 수행합니다.
공원의 가로 길이가 W, 세로 길이가 H라고 할 때, 공원의 좌측 상단의 좌표는 (0, 0), 우측 하단의 좌표는 (H - 1, W - 1) 입니다.</p>
<p><img src="https://velog.velcdn.com/images/jwkim_1018/post/f7fb9c32-c864-4541-905b-debf715ea051/image.png" alt=""></p>
<p>공원을 나타내는 문자열 배열 park, 로봇 강아지가 수행할 명령이 담긴 문자열 배열 routes가 매개변수로 주어질 때, 로봇 강아지가 모든 명령을 수행 후 놓인 위치를 [세로 방향 좌표, 가로 방향 좌표] 순으로 배열에 담아 return 하도록 solution 함수를 완성해주세요.</p>
<h2 id="제한-사항">제한 사항</h2>
<ul>
<li>3 ≤ park의 길이 ≤ 50<ul>
<li>3 ≤ park[i]의 길이 ≤ 50<ul>
<li>park[i]는 다음 문자들로 이루어져 있으며 시작지점은 하나만 주어집니다.<ul>
<li>S : 시작 지점</li>
<li>O : 이동 가능한 통로</li>
<li>X : 장애물</li>
</ul>
</li>
</ul>
</li>
<li>park는 직사각형 모양입니다.</li>
</ul>
</li>
<li>1 ≤ routes의 길이 ≤ 50<ul>
<li>routes의 각 원소는 로봇 강아지가 수행할 명령어를 나타냅니다.</li>
<li>로봇 강아지는 routes의 첫 번째 원소부터 순서대로 명령을 수행합니다.</li>
<li>routes의 원소는 &quot;op n&quot;과 같은 구조로 이루어져 있으며, op는 이동할 방향, n은 이동할 칸의 수를 의미합니다.<ul>
<li>op는 다음 네 가지중 하나로 이루어져 있습니다.<ul>
<li>N : 북쪽으로 주어진 칸만큼 이동합니다.</li>
<li>S : 남쪽으로 주어진 칸만큼 이동합니다.</li>
<li>W : 서쪽으로 주어진 칸만큼 이동합니다.</li>
<li>E : 동쪽으로 주어진 칸만큼 이동합니다.</li>
</ul>
</li>
<li>1 ≤ n ≤ 9</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="입출력-예">입출력 예</h2>
<table>
<thead>
<tr>
<th>park</th>
<th>routes</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>[&quot;SOO&quot;,&quot;OOO&quot;,&quot;OOO&quot;]</td>
<td>[&quot;E 2&quot;,&quot;S 2&quot;,&quot;W 1&quot;]</td>
<td>[2,1]</td>
</tr>
<tr>
<td>[&quot;SOO&quot;,&quot;OXX&quot;,&quot;OOO&quot;]</td>
<td>[&quot;E 2&quot;,&quot;S 2&quot;,&quot;W 1&quot;]</td>
<td>[0,1]</td>
</tr>
<tr>
<td>[&quot;OSO&quot;,&quot;OOO&quot;,&quot;OXO&quot;,&quot;OOO&quot;]</td>
<td>[&quot;E 2&quot;,&quot;S 3&quot;,&quot;W 1&quot;]</td>
<td>[0,0]</td>
</tr>
</tbody></table>
<h2 id="입출력-예-설명">입출력 예 설명</h2>
<p>입출력 예 #1</p>
<p>입력된 명령대로 동쪽으로 2칸, 남쪽으로 2칸, 서쪽으로 1칸 이동하면 [0,0] -&gt; [0,2] -&gt; [2,2] -&gt; [2,1]이 됩니다.</p>
<p>입출력 예 #2</p>
<p>입력된 명령대로라면 동쪽으로 2칸, 남쪽으로 2칸, 서쪽으로 1칸 이동해야하지만 남쪽으로 2칸 이동할 때 장애물이 있는 칸을 지나기 때문에 해당 명령을 제외한 명령들만 따릅니다. 결과적으로는 [0,0] -&gt; [0,2] -&gt; [0,1]이 됩니다.</p>
<p>입출력 예 #3</p>
<p>처음 입력된 명령은 공원을 나가게 되고 두 번째로 입력된 명령 또한 장애물을 지나가게 되므로 두 입력은 제외한 세 번째 명령만 따르므로 결과는 다음과 같습니다. [0,1] -&gt; [0,0]</p>
<h1 id="📌-풀이">📌 풀이</h1>
<h2 id="코드">코드</h2>
<pre><code class="language-python">def solution(park, routes):
    # {S : 0, O : 1, X : -1} 인 차원 배열 만들기
    dict_p = { &quot;S&quot; : 0, &quot;O&quot; : 1, &quot;X&quot; : -1 }
    park_arr = list()
    for y in park :
        temp_arr = list()
        for x in y :
            temp_arr.append(dict_p[x])
        park_arr.append(temp_arr)

    H = len(park_arr) - 1     # 높이(인덱스로 맞춰주기 위해 1 빼줌)
    W = len(park_arr[0]) - 1  # 넓이(인덱스로 맞춰주기 위해 1 빼줌)


    # 시작점 찾기
    for y in range(len(park)) :
        if &#39;S&#39; in park[y] :
            for x in range(len(park[y])) :
                if park[y][x] == &#39;S&#39;:
                    start = [y, x] 

    y, x = start[0], start[1] # 강아지 위치 - 시작점으로 초기화

    # 동작
    for r in routes :
        direc, dist = r.split()[0], int(r.split()[1]) # 방향과 거리


        if direc == &#39;E&#39; : 
            if x + dist &lt;= W and not -1 in park_arr[y][x : x + dist + 1] : # 공원을 넘지도 않고, 진행 방향에 -1이 없는 경우에만 이동
                x += dist
        if direc == &#39;W&#39; :
            if x - dist &gt;= 0 and not -1 in park_arr[y][x - dist : x] :
                x -= dist
        if direc == &#39;S&#39; :
            y_arr = [park_arr[i][x] for i in range(len(park_arr))] # 해당 위치의 y 값들
            if y + dist &lt;= H and not -1 in y_arr[y : y + dist + 1] :
                y += dist
        if direc == &#39;N&#39; :
            y_arr = [park_arr[i][x] for i in range(len(park_arr))] # 해당 위치의 y 값들
            if y - dist &gt;= 0 and not -1 in y_arr[y - dist : y] :
                y -= dist


    # 마지막 동작까지 다 한 후의 위치
    answer = [y, x]       

    return answer</code></pre>
<h2 id="설명">설명</h2>
<p>입력받은 park를 0, 1, -1,로 바꾼다.(지금 생각하니 굳이 안해도 될 것 같다.) 그리고 시작지점을 찾아 <code>start</code>에 넣어준다. 강아지 초기 위치는 start이다. 그리고 routes를 돌면서 갈 수 있는지 판단하는데, 주요 조건은 아래와 같다.</p>
<ol>
<li>방향(<code>direc</code>) - E, W, S, N 중 하나를 받는다.</li>
<li>명령 수행 시 공원 범위를 넘어가는지 - 각 방향마다 H와 W를 현재 위치와 명령 거리(<code>dist</code>)를 계산해서 비교</li>
<li>가는 길에 장애물이 있는지 확인 - 슬라이싱을 해주고, 슬라이싱 한 결과에 -1이 포함되어있는지 확인, -1이 슬라이싱한 리스트 안에 있으면 장애물을 만난 것이므로 갈 수 없음</li>
</ol>
<p>위 조건을 모두 만족하면 강아지 위치인 y와 x를 업데이트 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스(Lv 1)/파이썬] 달리기 경주]]></title>
            <link>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%8B%AC%EB%A6%AC%EA%B8%B0-%EA%B2%BD%EC%A3%BC</link>
            <guid>https://velog.io/@jwkim_1018/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Lv-1%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%8B%AC%EB%A6%AC%EA%B8%B0-%EA%B2%BD%EC%A3%BC</guid>
            <pubDate>Fri, 20 Oct 2023 02:39:53 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-문제">📌 문제</h1>
<h2 id="문제-설명">문제 설명</h2>
<p>얀에서는 매년 달리기 경주가 열립니다. 해설진들은 선수들이 자기 바로 앞의 선수를 추월할 때 추월한 선수의 이름을 부릅니다. 예를 들어 1등부터 3등까지 &quot;mumu&quot;, &quot;soe&quot;, &quot;poe&quot; 선수들이 순서대로 달리고 있을 때, 해설진이 &quot;soe&quot;선수를 불렀다면 2등인 &quot;soe&quot; 선수가 1등인 &quot;mumu&quot; 선수를 추월했다는 것입니다. 즉 &quot;soe&quot; 선수가 1등, &quot;mumu&quot; 선수가 2등으로 바뀝니다.</p>
<p>선수들의 이름이 1등부터 현재 등수 순서대로 담긴 문자열 배열 players와 해설진이 부른 이름을 담은 문자열 배열 callings가 매개변수로 주어질 때, 경주가 끝났을 때 선수들의 이름을 1등부터 등수 순서대로 배열에 담아 return 하는 solution 함수를 완성해주세요.</p>
<h2 id="제한-사항">제한 사항</h2>
<ul>
<li>5 ≤ players의 길이 ≤ 50,000<ul>
<li>players[i]는 i번째 선수의 이름을 의미합니다.</li>
<li>players의 원소들은 알파벳 소문자로만 이루어져 있습니다.</li>
<li>players에는 중복된 값이 들어가 있지 않습니다.</li>
<li>3 ≤ players[i]의 길이 ≤ 10</li>
</ul>
</li>
<li>2 ≤ callings의 길이 ≤ 1,000,000<ul>
<li>callings는 players의 원소들로만 이루어져 있습니다.</li>
<li>경주 진행중 1등인 선수의 이름은 불리지 않습니다.</li>
</ul>
</li>
</ul>
<h2 id="입출력-예">입출력 예</h2>
<table>
<thead>
<tr>
<th>players</th>
<th>callings</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>[&quot;mumu&quot;, &quot;soe&quot;, &quot;poe&quot;, &quot;kai&quot;, &quot;mine&quot;]</td>
<td>[&quot;kai&quot;, &quot;kai&quot;, &quot;mine&quot;, &quot;mine&quot;]</td>
<td>[&quot;mumu&quot;, &quot;kai&quot;, &quot;mine&quot;, &quot;soe&quot;, &quot;poe&quot;]</td>
</tr>
</tbody></table>
<h2 id="입출력-설명">입출력 설명</h2>
<p>4등인 &quot;kai&quot; 선수가 2번 추월하여 2등이 되고 앞서 3등, 2등인 &quot;poe&quot;, &quot;soe&quot; 선수는 4등, 3등이 됩니다. 5등인 &quot;mine&quot; 선수가 2번 추월하여 4등, 3등인 &quot;poe&quot;, &quot;soe&quot; 선수가 5등, 4등이 되고 경주가 끝납니다. 1등부터 배열에 담으면 [&quot;mumu&quot;, &quot;kai&quot;, &quot;mine&quot;, &quot;soe&quot;, &quot;poe&quot;]이 됩니다.</p>
<h1 id="📌-풀이">📌 풀이</h1>
<h2 id="코드">코드</h2>
<pre><code class="language-python">def solution(players, callings):
    dict_p = {players[i] : i for i in range(len(players))} # { &quot;선수 이름 &quot; : 현재 등수 }

    for c in callings :
        idx = dict_p[c] # 이름을 불린 선수의 인덱스
        pp = players[idx-1] # 앞에 있던 선수 이름

        # 딕셔너리와 리스트 모두 인덱스에 맞게 변경
        dict_p[c] -= 1
        dict_p[pp] += 1
        players[idx-1] = c
        players[idx] = pp

    return players</code></pre>
<h2 id="설명">설명</h2>
<p>이 문제는 해시 개념으로 접근해야 한다. 해시는 key - value 쌍으로 이루어진 자료구조로, value에 해당 데이터가 들어갈 위치를 가지고 있다. </p>
<p>이번 문제에서는 해시를 딕셔너리로 대체했다. 리스트에 각 반복마다 선수 위치가 변경되는데, 이 위치를 딕셔너리가 가지고 있고 딕셔너리를 기준으로 리스트의 순서를 변경해준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Python - deque 자료형의 rotate() 메소드]]></title>
            <link>https://velog.io/@jwkim_1018/Python-deque-%EC%9E%90%EB%A3%8C%ED%98%95%EC%9D%98-rotate-%EB%A9%94%EC%86%8C%EB%93%9C</link>
            <guid>https://velog.io/@jwkim_1018/Python-deque-%EC%9E%90%EB%A3%8C%ED%98%95%EC%9D%98-rotate-%EB%A9%94%EC%86%8C%EB%93%9C</guid>
            <pubDate>Thu, 19 Oct 2023 01:35:27 GMT</pubDate>
            <description><![CDATA[<p>deque 자료형에는 rotate() 메소드가 있다. 말 그대로 배열을 이리 저리 돌리는 것</p>
<p>파라미터에 양수가 들어오면 오른쪽 회전, 음수이면 왼쪽 회전이다.</p>
<pre><code class="language-python"># 파라미터가 양수일 때 - 오른쪽 회전
&gt;&gt;&gt; lst = [1, 2, 3, 4, 5]
&gt;&gt;&gt; deq = deque(lst)
&gt;&gt;&gt; deq.rotate(2)
&gt;&gt;&gt; print(deq)

deque([4, 5, 1, 2, 3])</code></pre>
<pre><code class="language-python"># 파라미터가 음수일 때 - 왼쪽 회전
&gt;&gt;&gt; lst = [1, 2, 3, 4, 5]
&gt;&gt;&gt; deq = deque(lst)
&gt;&gt;&gt; deq.rotate(-4)
&gt;&gt;&gt; print(deq)

deque([5, 1, 2, 3, 4])</code></pre>
<p>&#39;돌려돌려 돌림판&#39;을 생각해보면, 화살표는 한 지점에 고정해두고 선물을 적어놓은 칸이 돈다. 그것을 연상해보자.</p>
<p>돌림판의 화살표는 deque에서 첫번째 원소라는 뜻이다.(index = 0) </p>
<p>파라미터가 양수이면 오른쪽으로 회전, 음수이면 왼쪽으로 회전이다. 위의 첫 번째 예시에서 오른쪽으로 두 칸 이동하니 첫 번째 원소는 4가 되는 것이다. 두 번째 예시도 마찬기자로 왼쪽으로 네 칸 이동하니 첫 번째 원소는 5가 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[UIPath Academy : 009. Data Manipulation With Lists and Dictionaries in Studio]]></title>
            <link>https://velog.io/@jwkim_1018/UIPath-Academy-009.-Data-Manipulation-With-Lists-and-Dictionaries-in-Studio</link>
            <guid>https://velog.io/@jwkim_1018/UIPath-Academy-009.-Data-Manipulation-With-Lists-and-Dictionaries-in-Studio</guid>
            <pubDate>Mon, 16 Oct 2023 11:34:50 GMT</pubDate>
            <description><![CDATA[<h1 id="1-learn">1. Learn</h1>
<h2 id="1-1-lists">1-1. Lists</h2>
<p>Lists(혹은 List&lt;\T&gt;)는 같은 데이터 타입인 객체들을 하나의 저장소에 넣어 다루는 데이터 구조이다. Lists는 아래와 같은 특징이 있다.</p>
<ul>
<li>엘리먼트들의 데이터 타입이 모두 동일</li>
<li>엘리먼트들의 위치가 고정되어있기 때문에 인덱싱 가능</li>
<li>Array와 다르게 길이가 가변적임 그래서 엘리먼트 추가 가능</li>
</ul>
<p>Lists는 문자, 숫자, 시간 등 다양한 데이터 타입을 저장할 수 있고 많은 수의 엘리먼트도 넣을 수 있다. 주요하게 사용하는 메소드는 아래와 같다.</p>
<ul>
<li>Adding and removing items</li>
<li>Searching for an elements</li>
<li>Looping theough the items</li>
<li>Sorting the objects</li>
<li>Extracing items and converting them to other data types</li>
</ul>
<p>Lists에 메소드를 사용할 떄에는 Invoke Method 액티비티가 있다.Invoke Method 액티비티는 Lists 클래스에 구현된 메소드를 실행시켜주는 것이다. 주로 메소드 중 return값이 없을 때 사용한다.</p>
<p>array 데이터 타입은 동일한 데이터 타입인 여러 값들을 한 번에 저장할 수 있는 데이터 타입이다.(그래서 변수를 선언할 떄 array에 오는 엘리먼트들의 데이터 타입을 지정하는 것이 매우 중요함)</p>
<p>변수에 값을 초기화 하는 데에는 두 가지 유형이 있다. Value Type과 Reference Type이다.</p>
<ul>
<li><p>Value Type : Int, Stirng, Boolean 등 하나의 값을 갖는 데이터 타입은 초기값을 넣어주지 않아도 컴파일러가 데이터 타입에 맞는 값을 알아서 지정한다. 예를 들어 int 타입은 0을, String 타입은 &quot;&quot;을 자동으로 할당한다.</p>
</li>
<li><p>Reference Type : Lists나 Dictionary 같은 데이이터 타입은 초기화 할 때 객체화를 해주어야 한다.(<code>new List()</code>해줄 때 ()를 붙이는 것이 객체화) Reference Type 변수는 변수를 만들 때 객체화를 해준 후 값을 추가해 주어야 한다.</p>
</li>
</ul>
<p>위 내용을 참고해 Refenrence Type 변수를 만들 때는 아래와 같이 사용한다.(List를 예로)</p>
<ol>
<li><p>Lists 변수 선언
System.Colleciton.Generic.List&lt;T&gt; 타입으로 리스트 변수를 선언하는데, 엘리먼트의 타입을 T에 지정하기</p>
</li>
<li><p>값 초기화
Lists를 초기화 하는 방법은 <code>new List(of String) from {&quot;A&quot;, &quot;B&quot;, &quot;C&quot;}</code>와 같이 하면 된다.(리스트만 만들고 싶을 때에는 from절을 쓰지 말고 이후에 Add to Colleciton 액티비티에서 값 추가하기)</p>
</li>
</ol>
<p>UIPath에서는 Collection을 다루는 액티비티들이 있다.</p>
<table>
<thead>
<tr>
<th>액티비티</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>Add to Collenction</td>
<td>Collection에 엘리먼트를 추가함, 이 액티비티는 List.Add() 메소드와 동일한 동작을 함</td>
</tr>
<tr>
<td>Remove From Collection</td>
<td>Collection에서 특정한 엘리먼트를 제거하고 동작 수행 여부를 Boolean 타입으로 반환함</td>
</tr>
<tr>
<td>Exists in Colleciton</td>
<td>어떤 값이 Colleciton 내에 있는지 확인하고 Boolean 값으로 결과 반환</td>
</tr>
<tr>
<td>Clear Collection</td>
<td>Collection 안에 있는 모든 엘리먼트를 삭제함</td>
</tr>
</tbody></table>
<h2 id="1-2-dictionaries">1-2. Dictionaries</h2>
<p>Dictionaries(혹은 Dictionary&lt;TKey, TValue&gt;)는 key와 value가 쌍으로 이루어진 collection이다.(key는 유니크함) Key와 Value의 데이터 타입은 Dictionary가 선언될 때 선택된다.</p>
<p>다행히도 Dictionary를 사용하는 방법은 python과 동일하다.(엘리먼트를 추가하고, value에 접근하는 방법, key들을 가져오는 방법 등)</p>
<ul>
<li>엘리먼트 추가
Assign 액티비티에 Dict(key) = value 넣어주기</li>
<li>value에 접근
Dict(key)로 접근 가능</li>
<li>key 반환
Dict.keys</li>
</ul>
<p>Dictionary는 Reference type이기 때문에 초기화를 할 때 위에서 이야기한 특정한 방법을 사용해야한다. key value 모두 데이터 타입을 지정해준다.(예를 들어 key와 value 모두 string)
<code>new Dictionary(of Stirng, String)</code></p>
<p>당연하게도 value에는 List가 들어올 수도 있다. 이 때는 초기화를 어떻게 할까?
<code>new Dictionaru(of Stirng, List(of String))</code> 이렇게 해주면 된다.</p>
<p>Dictionary에서 주요하게 사용되는 메소드는 아래와 같다.</p>
<table>
<thead>
<tr>
<th>메소드</th>
<th>설명</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td>Add()</td>
<td>key와 value를 쌍으로 하나 추가, return 값이 없기 때문에 Invoke Method 액티비티에 사용</td>
<td>VariableName.Add(Key, Value)</td>
</tr>
<tr>
<td>Remove()</td>
<td>key를 입력받아서 dictionary에서 제거, Assign 액티비티에서 사용 가능</td>
<td>VariableName.Remove(key)</td>
</tr>
<tr>
<td>Item()</td>
<td>Dictionary에서 key에 해당하는 아이템을 반환</td>
<td>VariableName.Item(key)</td>
</tr>
<tr>
<td>Count</td>
<td>아이템 개수를 int32로 반환</td>
<td>VariableName.Count</td>
</tr>
<tr>
<td>ContainsKey()</td>
<td>key가 존재하는지 확인, Boolean으로 결과 반환</td>
<td>VariableName.ContainsKey(key)</td>
</tr>
<tr>
<td>TryGetValue()</td>
<td>주어진 key와 맞는 아이템이 있는지 확인하고 Boolean으로 결과 반환</td>
<td>VariableName.TryGetValue(key, value)</td>
</tr>
</tbody></table>
<h1 id="2-practice">2. Practice</h1>
<h2 id="2-1-lists">2-1. Lists</h2>
<h3 id="2-1-1-과업-설명">2-1-1. 과업 설명</h3>
<p>리스트 {&quot;Germany&quot;, &quot;Spain&quot;, &quot;Japan&quot;, &quot;Brazil&quot;, &quot;India&quot;, &quot;China&quot;}가 있다. 이 리스트를 내림차순으로 정렬하고 3 개의 국가만 새로운 리스트에 담아 출력하라</p>
<h3 id="2-1-2-방법-1">2-1-2. 방법 1</h3>
<ol>
<li><p>변수 설정
<img src="https://velog.velcdn.com/images/jwkim_1018/post/de266c65-3bd8-4ffc-a645-cce81ed27ced/image.png" alt=""></p>
</li>
<li><p>CountryList 정렬
Invoke Method를 두 번 써서 내림차순 정렬을 해준다.(Sort는 오름차순을 기준으로 정렬하는 것이고, Reverse로 그 순서를 뒤집어줌)
<img src="https://velog.velcdn.com/images/jwkim_1018/post/df8b3bf0-9237-4347-8cef-8abb1c9cf1a9/image.png" alt=""></p>
</li>
<li><p>3개의 엘리먼트 슬라이싱
List에는 GetRange() 메소드가 있다. 이 메소드는 슬라이싱을 지원하는 메소드이다. 아래와 같이 쓴다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/fdf1f2df-3749-41ae-9bc8-3523de57d683/image.png" alt=""></p>
</li>
<li><p>출력
String.Join()을 사용해서 한 줄로 출력한다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/197e69a9-65e8-417f-bce9-9383996877dd/image.png" alt=""></p>
</li>
</ol>
<h3 id="2-1-3-방법-2">2-1-3. 방법 2</h3>
<p>다른 부분은 방법 1과 같은데, 슬라이싱 하는 방법만 조금 다르다.</p>
<p><img src="https://velog.velcdn.com/images/jwkim_1018/post/f825a9c4-79e3-4074-ba8a-2b5c924d481f/image.png" alt="">
위 내용은 <a href="https://velog.io/@jwkim_1018/Tip-List-concat">이 링크를 참조하기 바란다.</a></p>
<h2 id="2-2-dictionaries-and-integers">2-2. Dictionaries and Integers</h2>
<h3 id="2-2-1-과업-설명">2-2-1. 과업 설명</h3>
<p>아래와 같이 {년도 : 우승자}으로 짝지어있는 원본 데이터가 있다. 이 데이터를 활용하여 &quot;우승자 - 우승 횟수 x회&quot;와 같이 출력하라</p>
<blockquote>
</blockquote>
<p>{2006,&quot;Oscar Pereiro&quot;},{2007,&quot;Alberto Contador&quot;}, {2008, &quot;Carlos Sastre&quot;}, {2009,&quot;Alberto Contador&quot;}, {2010, &quot;Andy Schleck&quot;}, {2011, &quot;Cadel Evans&quot;}, {2012,&quot;Bradley Wiggins&quot;}, {2013,&quot;Chris Froome&quot;}, {2014,&quot;Vincenzo Nibali&quot;},{2015,&quot;Chris Froome&quot;},{2016,&quot;Chris Froome&quot;},{2017,&quot;Chris Froome&quot;}, {2018,&quot;Geraint Thomas&quot;}</p>
<h3 id="2-2-2-구현">2-2-2. 구현</h3>
<ol>
<li><p>변수 설정
원본 데이터는 dict_RawData에 넣어두고 처리를 거친 데이터는 dict_Result에 넣는다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/c8516b51-7626-44e3-9c69-5cfca7ca6b0a/image.png" alt=""></p>
</li>
<li><p>RawData 반복 &amp; Result에 key 확인
RawData에 For Each를 돈다. 각 반복마다 Result에 우승자가 key에 있는지 확인한다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/26865957-0339-4da4-8fe7-96b9675fb709/image.png" alt=""><img src="https://velog.velcdn.com/images/jwkim_1018/post/bb7ecc15-9e8b-4c3b-b54c-88b1bfec598b/image.png" alt=""></p>
</li>
<li><p>분기 처리
만약 이번 반복의 우승자가 Result에 이미 key로 존재한다면, value에 1을 더해주고, key로 존재하지 않는다면 value에 1을 지정해 dictionary에 추가한다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/4b15e9d4-266d-4d63-89f5-55d1aadd4ef0/image.png" alt=""></p>
</li>
<li><p>출력
이렇게 만들어진 Result를 엘리먼트 하나씩 돌면서 출력한다.
<img src="https://velog.velcdn.com/images/jwkim_1018/post/8963628a-e16e-4d12-8496-dfa6dbb94ef5/image.png" alt=""><img src="https://velog.velcdn.com/images/jwkim_1018/post/5516ea5f-9700-4a37-9b36-f21c56cf9c12/image.png" alt=""></p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Tip - List concat]]></title>
            <link>https://velog.io/@jwkim_1018/Tip-List-concat</link>
            <guid>https://velog.io/@jwkim_1018/Tip-List-concat</guid>
            <pubDate>Mon, 16 Oct 2023 10:43:37 GMT</pubDate>
            <description><![CDATA[<p>Enumerable.Concat(TargetList.AsEnumerable, {CountryList(i)}.AsEnumerable).ToList</p>
<p>두 개의 리스트를 이어붙여야 할 때가 많다.(아니면 엘리먼트 하나를 뒤에 추가하거나) 이 Enumerable.Concat()을 사용하면 된다.</p>
<pre><code class="language-c">Enumerable.Concat(FirstList.AsEnumerable, SecondList.AsEnumerable).ToList</code></pre>
<p><br></br></p>
<p>경우에 따라 하나의 엘리먼트를 추가해야하는 경우도 있는데, 이 때도 동일하게 사용하면 된다. 두 번째 리스트에서 i번째 값을 이어붙인다고 가정하자.</p>
<pre><code class="language-c">Enumerable.Concat(FirstList.AsEnumerable, {SecondList(i)}.AsEnumerable).ToList</code></pre>
<p>대괄호로 묶으면 array형태가 된다. 그래서 원소 하나를 꺼내 array 형태로 바꿔주고 다시 Enumerable 형태로 바꿔준 코드이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[UIPath Academy : 008. Data Manipulation with Strings in Studio]]></title>
            <link>https://velog.io/@jwkim_1018/UIPath-Academy-008.-Data-Manipulation-with-Strings-in-Studio</link>
            <guid>https://velog.io/@jwkim_1018/UIPath-Academy-008.-Data-Manipulation-with-Strings-in-Studio</guid>
            <pubDate>Thu, 12 Oct 2023 15:30:40 GMT</pubDate>
            <description><![CDATA[<h1 id="1-introduction">1. Introduction</h1>
<h2 id="1-1-what-is-data-manipulation">1-1. What is Data Manipulation?</h2>
<p>데이터 처리(munipulation)라는 것은 데이터의 사용을 편리하게 하고 관리할 수 있는 기능을 향상시키기 위해 선언, 구조화, 포메팅, 정렬하는 과정을 의미한다.</p>
<p>데이터 처리를 공부하려면 객체, 메소드, 속성에 대한 구분을 할 수 있어야 한다.</p>
<ul>
<li>객체(Object) : Visual Basic에서 어떤 값을 가지고 있고 특정한 동작을 하는 개체를 의미한다.</li>
<li>메소드(Method) : 메소드는 객체가 수행할 수 있는 어떤 동작을 의미한다. </li>
<li>속성(Property) : 속성은 객체가 갖는 특성이다. 예를 들어 문자열 클래스는 Lenghth라는 속성이 있는데, 해당 &quot;문자열의 길이&quot;라는 특성을 갖고 있다.</li>
</ul>
<h1 id="2-data-manipulation-with-strings">2. Data Manipulation with Strings</h1>
<h2 id="2-1-string-method-and-properties">2-1. String Method and Properties</h2>
<p>문자열(String)이란 어떤 택스트를 다루는 데이터 타입이다. 자동화를 개발하는 동안 문자열은 매우 잦게 사용되므로 반드시 알아야 한다.</p>
<p>UIPath에서는 VB.Net에 있는 String 메소드를 빌려와 사용한다. 자주 사용되는 String 메소드는 아래와 같다.</p>
<table>
<thead>
<tr>
<th>메소드</th>
<th>설명</th>
<th>사용 예시</th>
<th>output 데이터 타입</th>
</tr>
</thead>
<tbody><tr>
<td>String.Concat</td>
<td>두 문자열 객체를 받아 합침</td>
<td>String.Concat(VarName1, VarName2)</td>
<td>String</td>
</tr>
<tr>
<td>Contains</td>
<td>특정 문자열이 푸함되어있는지 확인</td>
<td>VarName.Contains(&quot;text&quot;)</td>
<td>Boolean</td>
</tr>
<tr>
<td>String.Format</td>
<td>문자열 객체의 값을 변경함(혹은 원하는 자리에 텍스를 집어넣음), python의 <code>string.format</code>과 동일</td>
<td>String.Format(&quot;{0} is {1}&quot;,VarName1, VarName2)</td>
<td>String</td>
</tr>
<tr>
<td>IndexOf</td>
<td>문자열에서 입력받은 문자열이 처음으로 나오는 위치의 인덱스를 반환(0부터 작하는 인덱스), 입력받은 문자열은 유니코드로 변환하여 비교함</td>
<td>VarName1.IndexOf(&quot;a&quot;)</td>
<td>Int32</td>
</tr>
<tr>
<td>LasgIndexOf</td>
<td>IndexOf 메소드를 뒤에서부터 조회함</td>
<td>VarName1.LastIndexOf(&quot;a&quot;)</td>
<td>Int32</td>
</tr>
<tr>
<td>String.Join</td>
<td>컬렉션 내의 요소들을 입력받은 텍스트로 이어붙여 하나의 문자열로 반환함, python의 <code>str.join(list)</code>와 동일</td>
<td>String.Join(&quot;</td>
<td>&quot;, ColVarName1)</td>
</tr>
<tr>
<td>Repalce</td>
<td>특정 문자열을 다른 문자열로 전부 대체</td>
<td>VarName.Replace(&quot;original&quot;, &quot;replaced&quot;)</td>
<td>String</td>
</tr>
<tr>
<td>Split</td>
<td>문자열을 입력받은 인덱스(위치)에 입력받은 텍스트로 나눔</td>
<td>VarName.Split(&quot;</td>
<td>&quot;)(index)</td>
</tr>
<tr>
<td>Substring</td>
<td>문자열을 시작지점부터 입력받은 글자수 만큼 추출</td>
<td>VarName1.Substring(startindex, length)</td>
<td>String</td>
</tr>
</tbody></table>
<p>더 많은 메소드는 다음 링크에서 추가로 확인할 수 있음 <a href="https://alin-j-alin.tistory.com/13">https://alin-j-alin.tistory.com/13</a> <img src="https://velog.velcdn.com/images/jwkim_1018/post/3bc74657-a610-49b5-a2e8-86ecd84f294e/image.png" alt=""></p>
<h1 id="3-the-regex-builder">3. The RegEx Builder</h1>
<h2 id="3-1-the-regex-builder">3-1. The RegEx Builder</h2>
<p>정규표현식(Regular Expression = REGEX = regexp)은 문자열을 다룰 때 원하는 패턴에 일치하는 문자열을 조회하는 데에 특화된 방법이다.(근데 사용법이 좀 어려움)</p>
<p>UIPath에서는 정규표현식과 비슷한 RegEx builder를 제공한다. RegEx은 입력 데이터에 대한 유효성 검사, 문자열 파싱, 데이터 스크래핑 및 처리(manipulation)에 사용된다.</p>
<p>RegEx builder는 RegExBuilder Wizard 기능이 있어 조금 더 편리하고 빠른 정규표현식 사용이 가능하다.(Matches 액티비티를 사용해보면 알 수 있음)</p>
<p>아래는 RegEx Builder를 지원하는 액티비티들이다.</p>
<table>
<thead>
<tr>
<th>액티비티</th>
<th>설명</th>
<th>output 데이터 타입</th>
</tr>
</thead>
<tbody><tr>
<td>Matches</td>
<td>입력된 문자열의 모든 부분을 검사해 패턴과 일치한 것들을 전부 반환</td>
<td>System.Collections.Generic.IEnumerable&lt;System.Text.RegularExpressions.Match&gt;</td>
</tr>
<tr>
<td>IsMatches</td>
<td>입력된 정규표현식이 문자열에 일치하는지 여부 판단</td>
<td>Boolean</td>
</tr>
<tr>
<td>Replace</td>
<td>정규표현식 패턴과 일치하는 문자열을 입력받은 문자열로 대체</td>
<td>String</td>
</tr>
</tbody></table>
<p><strong>※ 주의! 현재 UIPath Academy와 UIPath Studio의 액티비티 이름이 맞지 않음을 확인했음(아래 참고)</strong>
Matches → Find Matching Patterns
IsMatches → Is Text Matching
Replace → Replace Matching Patterns</p>
<h1 id="4-practice">4. Practice</h1>
<h2 id="4-1-extract-email-address-using-string-methods">4-1. Extract Email Address Using String Methods</h2>
<h3 id="4-1-1-과업-설명">4-1-1. 과업 설명</h3>
<p>이메일을 추출하는 프로세스를 만드는데, 정규표현식을 사용하지 않고 구현해보자. 입력 데이터는 아래와 같다.(이메일을 임의로 하나 더 추가함)</p>
<blockquote>
<p>&quot;Please use the following address to contact me <a href="mailto:john.doe@localcompany.com">john.doe@localcompany.com</a>, it&#39;s the company email <a href="mailto:rlawldn@naver.com">rlawldn@naver.com</a>&quot;</p>
</blockquote>
<h3 id="4-1-2-구현">4-1-2. 구현</h3>
<ol>
<li><p>input 데이터와 결과를 담을 리스트 생성
이메일이 여러 개 나올 수도 있기 때문에 검출된 이메일은 리스트에 넣어 관리한다. 이를 위해 Creat List 액티비티를 사용했다. <img src="https://velog.velcdn.com/images/jwkim_1018/post/a322ea67-2ace-46f1-a7ff-073e8a1b5e35/image.png" alt=""></p>
</li>
<li><p>공백을 기준으로 나눠주기
공백을 기준으로 각 문자열을 나눠준다. 이메일은 중간에 띄어쓰기가 없으므로 가능한 방법이다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/53b481a3-d2f7-4c52-9aed-1c2b1b8eeec8/image.png" alt=""></p>
</li>
<li><p>반복을 돌며 각 엘리먼트가 이메일 형태인지 확인
위에서 나눈 문자열들을 하나씩 돌면서 @와 .이 있는지 확인한다. 이메일이라 함은 어떤 경우던 @와 .을 포함하고 있기 때문이고, 일반적으로 이메일을 제외하고는 @와 .을 붙여서 쓰는 경우는 없기 때문에 조건으로 사용이 가능하다. @와 .이 있는지 여부는 b_Contain 변수에 Boolean 타입으로 저장된다. <img src="https://velog.velcdn.com/images/jwkim_1018/post/6c880f6b-dcaf-4ccf-9eea-da5e76577d5e/image.png" alt=""></p>
</li>
<li><p>조건에 부합하면 리스트에 추가
@와 .이 있다면(b_Contain이 True라면) 리스트에 해당 문자열을 추가한다. 이를 위해 Append Item to List 액티비티를 사용했다.
<img src="https://velog.velcdn.com/images/jwkim_1018/post/999b57eb-d208-4b7f-8b0f-49f263100ae9/image.png" alt=""></p>
</li>
<li><p>리스트의 엘리먼트를 한 줄로 출력
문자열 메소드 중 join을 사용해 저장되어있는 이메일들을 한 줄로 출력한다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/f134b588-8010-4ace-813b-d69b440b86a2/image.png" alt=""></p>
</li>
</ol>
<h2 id="4-2-extract-email-address-with-regex">4-2. Extract Email Address with RegEx</h2>
<h3 id="4-2-1-과업-설명">4-2-1. 과업 설명</h3>
<p>이메일을 추출하는 프로세스를 만드는데, 이번에는 정규표현식을 사용해보자. 입력받는 텍스트는 텍스트 파일로 받는다.</p>
<h3 id="4-2-2-구현">4-2-2. 구현</h3>
<ol>
<li><p>텍스트 데이터 읽기, 텍스트 데이터 split, 결과를 담을 리스트 생성
이번에는 텍스트 파일로 input text가 제공되었기 때문에 Read Text File 액티비티로 텍스트를 읽어온다. 읽어온 텍스트를 개행으로 split 한다. Visual Basic에서 개행은 vbCrLf로 쓴다. 그리고 과제 1과 마찬가지로 이메일이 여러 개가 올 수 있기 때문에 이메일은 리스트에 담아 관리한다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/3c67129f-1d03-4849-9924-2860caa9590e/image.png" alt=""></p>
</li>
<li><p>반복문, 이메일 판단
개행으로 나눈 리스트를 엘리먼트마다 돌면서 이메일인지 판단한다. 판단 기준은 @와 .이 있는지 여부이다. 만약 @와 .이 있다면 다음 동작을 수행한다.
<img src="https://velog.velcdn.com/images/jwkim_1018/post/0d69df44-73a1-42aa-9d54-8ec79e4a09c8/image.png" alt=""></p>
</li>
<li><p>정규표현식으로 이메일 검출, 리스트 추가
정규표현식으로 해당 텍스트에서 이메일을 검출한다.(액티비티 이름이 Find Matching Patterns로 바뀌었음을 주의) 검출된 이메일은 이메일 리스트에 담는다.
<img src="https://velog.velcdn.com/images/jwkim_1018/post/cc3ee960-080b-4b55-976f-4341c2a8e72e/image.png" alt=""></p>
</li>
<li><p>출력
리스트를 한 줄로 출력한다. <img src="https://velog.velcdn.com/images/jwkim_1018/post/5dd1c40f-b748-432b-899a-e4697d0b4d39/image.png" alt=""></p>
</li>
</ol>
<h2 id="4-3-repace-placeholdes-with-regex">4-3. Repace Placeholdes with RegEx</h2>
<h3 id="4-3-1-과업-설명">4-3-1. 과업 설명</h3>
<p>사용자로부터 이름, 성, 일자를 받아 포멧팅을 한다. 원하는 스크립트는 아래와 같다.</p>
<blockquote>
<p>&quot;안녕하세요. <last_name><first_name>님, 다음 주 <day_of_week> 제품 출시 행사에 여러분을 초대하고 싶습니다. 이번 주 말까지 확인 부탁드립니다.&quot;</p>
</blockquote>
<h3 id="4-2-2-구현-1">4-2-2. 구현</h3>
<ol>
<li><p>사용자 입력
사용자에게 입력받는 성과 이름은 Null값이면 안된다. 이를 방지하기 위해 Do while의 조건으로 입력받은 문자열이 Null인지 여부를 넣는다. 요일은 드롭다운에서 하나를 선택하는 것으로 한다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/e71a9f5b-367d-409d-87c5-fa7a43e06ea0/image.png" alt=""><img src="https://velog.velcdn.com/images/jwkim_1018/post/3ca82499-3655-4e7b-84ed-1546f9b70b8d/image.png" alt=""></p>
</li>
<li><p>텍스트 대치
스크립트에 Replace 메서드를 사용해 포멧팅 할 준비를 한다. 아래와 같이 성, 이름, 요일을 전부 바꿔준다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/9b020c31-c304-480c-b442-7be3835b2a49/image.png" alt=""></p>
</li>
<li><p>출력
String.Format 메서드를 사용해 입력받은 성, 이름, 요일을 넣어 출력한다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/bd4d6a6b-9e76-4591-8d7b-afeb18c15e9d/image.png" alt=""></p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[UIPath Academy : 007. DataTables and Excel Automation With Studio]]></title>
            <link>https://velog.io/@jwkim_1018/UIPath-Academy-007-DataTables-and-Excel-Automation-With-Studio</link>
            <guid>https://velog.io/@jwkim_1018/UIPath-Academy-007-DataTables-and-Excel-Automation-With-Studio</guid>
            <pubDate>Wed, 11 Oct 2023 07:47:03 GMT</pubDate>
            <description><![CDATA[<h1 id="1-datatables">1. DataTables</h1>
<h2 id="1-1-what-are-datatables">1-1. What Are DataTables?</h2>
<p>데이터 테이블은 2차원 데이터를 의미한다.(python pandas의 DataFrame) 데이터 테이블에는 Cell이라는 개념이 있다. 그냥 column과 row를 인덱싱해서 하나의 값에 접근한 것을 cell이라고 한다.</p>
<p>데이터 테이블을 만드는 방법은 아래와 같다.</p>
<ol>
<li><p>Build Data Table Activity
이 액티비티로 데이터 테이블 변수를 만들 수 있다. 컬럼과 컬럼의 제약조건을 설정할 수 있으며 기본적으로 들어가는 row도 지정할 수 있다.</p>
</li>
<li><p>Read Range Activity
엑셀 파일에서 어떤 범위 값을 지정해서 데이터 테이블로 읽을 때 사용한다. </p>
</li>
<li><p>Read CSV Activity
csv 파일에서 값을 읽어들여 데이터 테이블로 반환하는 액티비티이다.</p>
</li>
<li><p>Data Scraping Action
브라우저, 어플리캐이선, 여러 문서에서 정형 데이터를 추출해 데이터 테이블로 반환하는 액션이다. [디자인 &gt; 스크래핑 &gt; 데이터 스크래핑] 리본 메뉴에서 사용 가능하다.</p>
</li>
<li><p>Generate Data table From Text Activity
텍스트 데이터로부터 데이터 테이블을 추출하는 액티비티이다. pandas.read_csv()에서 <code>sep=</code> 파라미터 조정하듯 사용자가 분리 문자를 지정할 수 있다.</p>
</li>
</ol>
<h2 id="1-2-working-with-datatables">1-2. Working With DataTables</h2>
<p>데이터 테이블을 다루는 매우 다양한 액티비티가 있다. 아래는 데이터 테이블에 사용할 수 있는 액티비티들이다.</p>
<table>
<thead>
<tr>
<th>Activity</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>Build Data Table</td>
<td>데이터 테이블의 구조를 만드는 액티비티, 컬럼과 컬럼의 제약조건 지정</td>
</tr>
<tr>
<td>For Each Row in Data Table</td>
<td>액티빝티를 입력받은 데이터 테이블의 로우로 For Each 액티비티 수행</td>
</tr>
<tr>
<td>Filter Data Table</td>
<td>기준을 제시해서 데이터 테이블을 필터링, 출력으로 새로운 데이터 테이블로 지정할 수 있음</td>
</tr>
<tr>
<td>Add Data Column</td>
<td>이미 존재하는 데이터 테이블을 입력으로 받고, 해당 데이터 테이블에 새로운 컬럼을 지정, <a href="https://velog.io/@jwkim_1018/UIPath-%ED%8C%81">해당 링크 참고</a></td>
</tr>
<tr>
<td>Add Data Row</td>
<td>이미 존재하는 데이터 테이블을 입력으로 받고, 해당 데이터 테이블에 새로운 로우 추가, 추가되는 데이터는 DataRow 타입이거나 Array Row 타입이어야 하고 데이터 테이블 구조와 동일해야 함</td>
</tr>
<tr>
<td>Clear Data Table</td>
<td>입력받은 데이터 테이블을 초기화</td>
</tr>
<tr>
<td>Generate Data Table From Text</td>
<td>텍스트 데이터로부터 데이터 테이블을 추출, 분리 문자 지정 가능</td>
</tr>
<tr>
<td>Join Data Tables</td>
<td>두 개의 데이터 테이블을 입력 받고 join하는 액티비티, left/right/inner 중 옵션 선택 가능</td>
</tr>
<tr>
<td>Lookup Data Table</td>
<td>엑셀의 VLookop과 비슷한 동작을 함, 출력으로 로우의 인덱스를 반환하거나 값 자체를 반환할 수 있음</td>
</tr>
<tr>
<td>Merge Data Table</td>
<td>하나의 데이터 테이블을 다른 데이터 테이블에 이어 붙일 ㄸ대 사용함, 이어붙일 때 스키마가 다른 경우를 대비해놔서 Join Data Table보다 더 편리함</td>
</tr>
<tr>
<td>Output Data Table</td>
<td>데이터 테이블을 csv 포멧의 문자열 값으로 변환함(변환된 문자열을 다른 변수에 넣고 출력하면 데이터 테이블의 상태를 확인할 수 있음)</td>
</tr>
<tr>
<td>Remove Data Column</td>
<td>입력 받은 데이터 테이블에서 특정 컬럼을 삭제함, 삭제할 컬럼을 타게팅 하는 방법으로는 컬럼 인덱스 제시/컬럼 이름 제시/Data Column 변수를 사용하는 방법이 있음</td>
</tr>
<tr>
<td>Remove Data Row</td>
<td>입력 받은 데이터 테이블에서 특정 로우를 삭제함, 로우 인덱스나 Data Row 변수를 지정해서 동작</td>
</tr>
<tr>
<td>Remove Duplicate Rows</td>
<td>입력 받은 데이터 테이블에서 중복된 로우를 제거하고 첫 번째 로우만 남김</td>
</tr>
<tr>
<td>Sort Data Table</td>
<td>입력받은 데이터 테이블에서 특정 컬럼을 기준으로 오름차순/내림차순 정렬</td>
</tr>
<tr>
<td>Get Row Item</td>
<td>하나의 로우에서 지정된 컬럼 이름이나 순서에 해당하는 값을 반환</td>
</tr>
<tr>
<td>Update Row Item</td>
<td>데이터 테이블 로우에서 특정 컬럼의 값을 변경</td>
</tr>
</tbody></table>
<h1 id="2-workbooks">2. Workbooks</h1>
<h2 id="2-1-workbooks-and-common-activity">2-1. Workbooks and Common Activity</h2>
<p>많은 경우 데이터베이스는 워크북에 저장되어있다. 이 데이터베이스는 앞서 살펴본 데이터 테이블을 다루는 액티비티들로 처리할 수 있다. 그러면 워크북은 어떻게 다룰 수 있을까?
<img src="https://velog.velcdn.com/images/jwkim_1018/post/f7ffb26e-18c8-476b-9937-181f51bcca17/image.png" alt=""></p>
<p>UIPath는 두 가지 디자인이 있고 각 디자인은 워크북에 접근하고 처리하기 위해 워크북과 엑셀 두 가지 방법을 제공한다.</p>
<ul>
<li><p>Workbook</p>
<ul>
<li>파일 수준의 접근</li>
<li>workbook 액티비티는 모두 백그라운드에서 동작함</li>
<li>엑셀 어플리케이션이 설치되어있지 않아도 사용 가능</li>
<li>엑셀 어플리케이션을 열지 않으므로 더 빠르고 정확한 동작 가능</li>
<li>.xls, .xlsx 파일에만 작업 가능(.xlsm 파일은 작업 불가)</li>
<li>모든 파일은 엑셀에서 열리면 액티비티 수행 불가</li>
</ul>
</li>
<li><p>Excel(Moder design과 classic design이 제공하는 액티비티가 다름)</p>
<ul>
<li>사람이 동작하는 것과 같이 엑셀 어플리케이션을 켜서 동작</li>
<li>.xls, .xlsx, .xlsm 파일 모두 작업 가능(몇 가지 액티비티는 .csv 파일에도 동작 가능)</li>
<li>엑셀 어플리케이션이 설치되어 있어야함 </li>
</ul>
</li>
</ul>
<p>엑셀에 접근하는 액티비티는 modern design과 classic design에서 제공하는 액티비티가 다른데, 아래는 classic design에서 제공하는 액티비티이다.</p>
<table>
<thead>
<tr>
<th>Activity</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>Append Range</td>
<td>지정된 엑셀 시트 끝 부분에 입력받은 데이터 테이블을 추가함, 지정된 엑셀시트가 존재하지 않는 시트라면, 그 시트를 생성하고 데이터를 추가함</td>
</tr>
<tr>
<td>Get Table Range</td>
<td>엑셀에서 테이블을 추출</td>
</tr>
<tr>
<td>Read Cell</td>
<td>하나의 셀에서 값을 읽어들여 문자열로 저장</td>
</tr>
<tr>
<td>Read Cell Formula</td>
<td>지정된 셀에서 수식을 읽어들여 문자열로 저장</td>
</tr>
<tr>
<td>Read Column</td>
<td>시작지점을 입력받고(예 : &quot;A2&quot;) 그 이후부터 해당 컬럼의 값을 읽어들여 IEnumerable &lt;object&gt; 데이터 타입으로 저장</td>
</tr>
<tr>
<td>Read Range</td>
<td>특정 범위의 데이터들을 데이터 테이블 타입으로 저장함, classic design에서는 Use Filter 속성이 있어 추출 시 데이터 필터링 가능</td>
</tr>
<tr>
<td>Read Row</td>
<td>시작지점을 입력받고(예 : &quot;B4&quot;) 해당 로우의 값을 읽어들여 IEnumerable &lt;object&gt; 데이터 타입으로 저장</td>
</tr>
<tr>
<td>Write Cell</td>
<td>지정한 셀에 원하는 값을 넣음, 이미 값이 있는 경우에는 덮어쓰고 시트가 없는 경우에는 시트를 새로 생성함</td>
</tr>
<tr>
<td>Write Range</td>
<td>데이터 테이블을 원하는 위치에 입력</td>
</tr>
</tbody></table>
<h2 id="2-2-excel-application-scope-and-specific-activities">2-2. Excel Application Scope and Specific Activities</h2>
<p>classic design에는 Excel Application Scope라는 액티비티가 있다. 원래 파일 작업을 할 때 파일을 열고, 저장하고, 닫는 동작이 필요하고 사용자는 이런 명령을 내려주어야 오류없이 파일 작업을 할 수 있다. 그런데 일일이 이런 동작을 추가하는 것은 귀찮고 잊기도 쉽다. 그래서 해당 동작을 대신해주는 액티비티가 Excel Application Scope이다. 우리는 Excel Application Scope 컨테이너 안에 원하는 동작만 넣어주면 된다.
<img src="https://velog.velcdn.com/images/jwkim_1018/post/28268a66-88a1-442c-b503-27a7aa4d0dd8/image.png" alt=""></p>
<p>엑셀 어플리케이션에서 사용이 가능한 액티비티가 아래와 같이 카테고리별로 존재한다. 카테고리에 대한 설명을 먼저 해보자.</p>
<table>
<thead>
<tr>
<th>Activity Category</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>csv Activities</td>
<td>CSV 파일을 읽고 쓰는 액티비티들, Excel Application Scope 컨테이너 안에 없더라도 독립적으로 사용이 가능</td>
</tr>
<tr>
<td>Range Activities</td>
<td>데이터를 읽기 쓰기 추가 삭제 복사/붙여넣기, 데이터 테이블을 다루는 액티비티들과 비슷하지만 엑셀 어플리케이션에서 작동한다는 차이가 있음</td>
</tr>
<tr>
<td>Table Activities</td>
<td>엑셀 파일을 추가, 필터링, 정렬함, Range Activities와 동일하게 엑셀 파일 안에서 사용되는 액티비티들</td>
</tr>
<tr>
<td>File Activities</td>
<td>엑셀 파일을 열고 닫음</td>
</tr>
<tr>
<td>Cell Color Activities</td>
<td>엑셀 파일에서 셀의 색깔 지정</td>
</tr>
<tr>
<td>Sheet Activities</td>
<td>엑셀 파일의 시트에 동작</td>
</tr>
<tr>
<td>Pivot Table Activities</td>
<td>피벗 테이블을 조작</td>
</tr>
<tr>
<td>Macro Activities</td>
<td>이미 지정되어있는 엑셀 메크로를 실행하거나 다른 파일의 메크로를 가져옴, xslm 파일에서만 동작</td>
</tr>
</tbody></table>
<p>이번엔 각 카테고리에 속하는 액티비티와 그 설명을 보자.</p>
<table>
<thead>
<tr>
<th>Activity Category</th>
<th>Activity</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>CSV Activities</td>
<td>Append to CSV</td>
<td>데이터 테이블을 csv 파일에 추가함 / 데이터 테이블이 없으면 추가 / 덮어쓰기가 아님</td>
</tr>
<tr>
<td>CSV Activities</td>
<td>Read CSV</td>
<td>csv파일 전체를 읽어들여 데이터 테이블로 반환</td>
</tr>
<tr>
<td>CSV Activities</td>
<td>Write CSV</td>
<td>데이터 테이블 데이터를 csv 파일에 덮어 쓰기</td>
</tr>
<tr>
<td>Range Activities</td>
<td>Delete Column</td>
<td>이름을 기준으로 컬럼을 제거함</td>
</tr>
<tr>
<td>Range Activities</td>
<td>Insert Column</td>
<td>엑셀 파일에서 특정 위치에 빈 컬럼 추가</td>
</tr>
<tr>
<td>Range Activities</td>
<td>Insert/Delete Columns</td>
<td>빈 컬럼들을 추가하거나 이미 존재하는 컬럼들을 삭제</td>
</tr>
<tr>
<td>Range Activities</td>
<td>Read Column</td>
<td>입력받은 시작 셀을 시작지점으로 그 열의 값들을 IEnumberable &lt;Object&gt; 변수로 반환</td>
</tr>
<tr>
<td>Range Activities</td>
<td>Insert/Delete Rows</td>
<td>비어있는 로우들을 추가하거나 이미 존재하는 로우들을 삭제</td>
</tr>
<tr>
<td>Range Activities</td>
<td>Select Range</td>
<td>엑셀 파일에서 특정한 범위를 선택, 선택한 데이터들로 다른 동작을 할 때 함께 사용되는 액티비티</td>
</tr>
<tr>
<td>Range Activities</td>
<td>Get Select Range</td>
<td>문자열로 원하는 범위를 출력</td>
</tr>
<tr>
<td>Range Activities</td>
<td>Delete Range</td>
<td>특정한 범위를 제거</td>
</tr>
<tr>
<td>Range Activities</td>
<td>Auto Fill Range</td>
<td>주어진 범위에 주어진 수식 채워넣기</td>
</tr>
<tr>
<td>Range Activities</td>
<td>Copy Paste Range</td>
<td>전체 범위를 복사/붙여넣음(값, 수식, 포멧 전부)</td>
</tr>
<tr>
<td>Range Activities</td>
<td>Lookup Range</td>
<td>주어진 범위에서 셀 값을 검색</td>
</tr>
<tr>
<td>Range Activities</td>
<td>Remove Duplicate Range</td>
<td>주어진 범위에서 중복 제거</td>
</tr>
<tr>
<td>Range Activities</td>
<td>Read Range</td>
<td>원하는 범위의 값들을 데이터 테이블 변수로 저장, 범위가 지정되지 않거나 셀 하나만 지정된 경우 엑셀시트 전체를 선택</td>
</tr>
<tr>
<td>Range Activities</td>
<td>Append Range</td>
<td>데이터 테이블 변수에 저장된 값을 스프레드시트 아래에 추가, 시트가 없으면 시트를 만들고 추가</td>
</tr>
<tr>
<td>Range Activities</td>
<td>Write Range</td>
<td>입력받은 시작 셀을 시작지점으로 데이터 테이블 변수에 있는 데이터를 붙여넣음, 시작 셀을 입력받지 못했으면 A1 셀에서부터 시작</td>
</tr>
<tr>
<td>Table Activities</td>
<td>Filter Table</td>
<td>컬럼으로부터 값들에 필터 적용, 필터링 된 데이터들은 삭제되는 것이 아니라 보이지 않게 처리한 것 뿐임</td>
</tr>
<tr>
<td>Table Activities</td>
<td>Sort Table</td>
<td>주어진 컬럼을 기준으로 값을 정렬</td>
</tr>
<tr>
<td>Table Activities</td>
<td>Creage Table</td>
<td>특정 범위에 테이블을 만듦</td>
</tr>
<tr>
<td>File Activities</td>
<td>Close Workbook</td>
<td>엑셀 닫기</td>
</tr>
<tr>
<td>File Activities</td>
<td>Save Workbook</td>
<td>엑셀 저장</td>
</tr>
<tr>
<td>Cell Color Activities</td>
<td>Get Cell Color</td>
<td>원하는 셀 주소를 입력받고 그 셀의 셀 색깔을 변수로 저장</td>
</tr>
<tr>
<td>Cell Color Activities</td>
<td>Set Range Color</td>
<td>범위를 입력받고 그 범위의 셀 색깔을 입력받은 변수값으로 변경</td>
</tr>
<tr>
<td>Sheet Activities</td>
<td>Get Workbook Sheets</td>
<td>인덱스로 시트의 이름 읽기</td>
</tr>
<tr>
<td>Sheet Activities</td>
<td>Get Workbook Sheets</td>
<td>시트 이름들을 추출하고 정렬</td>
</tr>
<tr>
<td>Sheet Activities</td>
<td>Copy Sheet</td>
<td>시트를 복사하고 다른 엑셀 파일로 붙여넣음</td>
</tr>
<tr>
<td>Pivot Table Activities</td>
<td>Refresh Pivot Table</td>
<td>피벗 테이블을 초기화, 피벗 테이블 데이터가 변경될 때 사용함(자동으로 적용되는 것이 아님)</td>
</tr>
<tr>
<td>Pivot Table Activities</td>
<td>Create Pivot Table</td>
<td>원하는 시트에 피벗 테이블을 생성</td>
</tr>
<tr>
<td>Macro Activities</td>
<td>Execute Macro</td>
<td>메크로 실행</td>
</tr>
<tr>
<td>Macro Activities</td>
<td>Invoke VBA</td>
<td>다른 파일로부터 메트로를 가져옴</td>
</tr>
</tbody></table>
<h1 id="3-excel-modern-design-experience">3. Excel Modern Design Experience</h1>
<h2 id="3-1-modern-design-excel-activities">3-1. Modern Design Excel Activities</h2>
<p>Use Excel File 액티비티는 엑셀 파일에 대해 여러 작업을 할 수 있게 해주는 액티비터이다.</p>
<p>아래는 modern design애서 주요하게 사용되는 액티비티들이다.</p>
<ul>
<li>Auto Fill</li>
<li>Creat Pivot Table</li>
<li>Delete Column</li>
<li>Delete Sheet</li>
<li>Export to CSV</li>
<li>Filter</li>
<li>FOr Each Excel Row</li>
<li>Format as Table</li>
<li>Get Chart</li>
<li>Insert Rows</li>
<li>Read Cell Value</li>
<li>Read Range</li>
<li>Remove Duplicates</li>
<li>Save Excel File As</li>
<li>Sort Range</li>
<li>VLookup</li>
<li>Write Cell</li>
<li>Write Data Table to Excel</li>
</ul>
<p>classic design 액티비티들을 더 많이 사욜하므로 modern design 액티비티들은 뭐가 있는지만 알고 가자.(필요하면 찾아보기)</p>
<h1 id="4-practice">4. Practice</h1>
<h2 id="4-1-calcuate-the-sum-in-two-excel-files">4-1. Calcuate the sum in two Excel files</h2>
<h3 id="4-1-1-과업-설명">4-1-1. 과업 설명</h3>
<p>이번 실습 과제에서는 A열의 데이터를 B열에 더한 값을 C열에 붙여넣도록 하자. </p>
<p><img src="https://velog.velcdn.com/images/jwkim_1018/post/8e7ce2c7-a521-4ba2-8f4f-eef4f0e141fe/image.png" alt=""></p>
<p>이 과제는 아래와 같이 세 가지 방법으로 처리할 수 있으므로 고민해보자.</p>
<ol>
<li>데이터 하나씩 더해서 C열에 붙여넣는 과정이 눈에 보이도록</li>
<li>엑셀파일이 닫혀있는 상태로 작업하는데, 데이터 테이블을 메모리에 저장해놓고 마지막에 한 번에 붙여넣기</li>
<li>원본 파일에서 엑셀 수식을 사용해서 계산</li>
</ol>
<h3 id="4-1-2-방법-1">4-1-2. 방법 1</h3>
<p>데이터 테이블로 값을 받아와서 개별적으로 계산하고 셀에 입력한다.</p>
<ol>
<li><p>Excel Application Scope 
엑셀 파일을 열어 작업하기 위해 Excel Application Scope 액티비티 사용</p>
</li>
<li><p>Read Range
원하는 값을 데이터 테이블로 가져온다.</p>
</li>
<li><p>For Each Row in Data Table
데이터를 하나씩 가져와서 계산 후 C 열에 붙여야하기 때문에 행을 가져오기 위해 해당 액티비티를 사용한다.(Row의 데이터타입은 RowData임)
<img src="https://velog.velcdn.com/images/jwkim_1018/post/808f122b-3bd2-489e-8f01-c17b89f8ea93/image.png" alt=""></p>
</li>
</ol>
<ol start="4">
<li><p>Assign
계산한 값을 원하는 위치에 입력하기 위해 데이터 테이블에서 현재 행의 인덱스에 접근한다. 이 때 아래와 같이 쓴다. 1을 더해주는 이유는 인덱스는 0부터 시작인데, 엑셀에서는 인덱스가 1부터 시작하기 때문이다.
<img src="https://velog.velcdn.com/images/jwkim_1018/post/6bcfb980-2905-4b6b-b0f8-464c6c452050/image.png" alt=""></p>
</li>
<li><p>Get Row Item
RowData 타입의 변수에서 원하는 위치의 값을 가져온다. A값과 B값을 저장한다.</p>
</li>
<li><p>Assign &amp; Write Cell
계산한 값을 원하는 위치에 작성한다. 이 때 4에서 구한 RowIndex 값을 사용한다.
<img src="https://velog.velcdn.com/images/jwkim_1018/post/fc43bfe3-0ba7-4001-8d4d-d5f5605dbad4/image.png" alt=""></p>
</li>
</ol>
<h3 id="4-1-3-방법-2">4-1-3. 방법 2</h3>
<ol>
<li><p>Read Range Workbook
엑셀 어플리케이션이 닫혀있는 상태로 작업하기 위해서 excel application scope를 사용하지 않고 워크북 관련 액티비티를 사용한다.</p>
</li>
<li><p>Add Data Column
가져온 데이터 테이블에 컬럼 C를 추가한다. 데이터 테이블 안에서 계산을 한 뒤 한 번에 붙여넣을 것이기 때문이다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/f43e9f65-7775-4053-8d3b-accfc69044f2/image.png" alt=""></p>
</li>
</ol>
<p>3, For Each Row in Data Table
가져온 데이터 테이블의 로우를 하나씩 돌며 컬럼 C에 데이터를 채워 넣는다.</p>
<ol start="4">
<li><p>Assign
DataRow 변수에 담긴 데이터는 인덱싱으로 편리하게 가져올 수 있다. 인덱싱 하는 방법은 Array와 동일하게 소괄호를 사용해 인덱싱한다. 이번 실습에서는 변수를 사용하지 않고 직접 값에 접근해 넣어보자.<img src="https://velog.velcdn.com/images/jwkim_1018/post/fbc124a0-b44a-411b-906a-6a8de75e0986/image.png" alt=""><img src="https://velog.velcdn.com/images/jwkim_1018/post/bfd1a3ff-e98d-45f1-af30-492bed730447/image.png" alt="">
위와 같이 Row(0)은 A열, Row(1)은 B열이다. 이 값들은 문자열 타입이므로 double형으로 바꿔주기 위해 <code>Convert.ToDouble</code> 메소드를 사용해주었다.</p>
</li>
<li><p>Write Range Workbook
이렇게 만든 데이터 테이블을 엑셀을 열지 않고 붙여넣기 위해 이번에도 workbook 액티비티를 사용했다.</p>
</li>
</ol>
<h3 id="4-1-4-방법-3">4-1-4. 방법 3</h3>
<ol>
<li><p>Excel Application Scope
엑셀을 연다.</p>
</li>
<li><p>Read Range
데이터 테이블로 값들을 가져온다.</p>
</li>
<li><p>For Each Row in Data Table
로우를 하나씩 돌면서 작업한다.</p>
</li>
<li><p>Assign
현재 로우의 인덱스를 가져오는데, 방법 1에서 사용한 방식으로 가져오면 된다.</p>
</li>
<li><p>Write Cell
원하는 지점에 수식을 써준다. <img src="https://velog.velcdn.com/images/jwkim_1018/post/da9cfa2f-0b5e-4456-b0f6-988610a33835/image.png" alt=""></p>
</li>
</ol>
<h2 id="4-2-calcuate-loss-invoices">4-2. Calcuate Loss Invoices</h2>
<h3 id="4-2-1-과업-설명">4-2-1. 과업 설명</h3>
<p>이번 과제는 &#39;Clients.csv&#39;에서 고객별로 파산 여부를 확인해 파산한 고객에게 발행된 대금을 &#39;Invoice.xslx&#39;에서 종합하는 과제이다.</p>
<h3 id="4-2-2-구현">4-2-2. 구현</h3>
<ol>
<li><p>Read CSV, Read Range Workbook
두 데이터 테이블을 읽어옴</p>
</li>
<li><p>Add Data Column 
Client 데이터 테이블에 각 클라이언트 마다 미수금을 넣기 위한 &#39;Total&#39; 컬럼을 추가한다. 만약 파산 여부가 TRUE이면 미수금 총액이, FALSE면 0이 들어간다.</p>
</li>
<li><p>For Each Row in Data Table
Client 데이터 테이블에서 로우를 하나씩 돌며 각 클라이언트마다 파산 여부를 판단한다.</p>
</li>
<li><p>Assign
매 반복마다 미수금  총액을 0으로 초기화해준다.</p>
</li>
<li><p>If, Filter Data Table
파산 여부가 TURE인지 확인한다. 파산 여부가 TRUE일 떄만 미수금 총액을 계산한다. 계산을 위해 파산한 클라이언트 이름만 invoice 데이터 테이블에서 걸러낸다. <img src="https://velog.velcdn.com/images/jwkim_1018/post/d339d5b3-5f5c-4bc9-83c2-525370a0ebc7/image.png" alt=""></p>
</li>
<li><p>For Each Row in Data Table, Assign
걸러낸 데이터 테이블을 한 번 더 돌며 각 미수금을 더해준다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/688f6d3e-6036-4d04-94af-dd775a609d8c/image.png" alt=""></p>
</li>
</ol>
<ol start="7">
<li><p>Assign 
미수금 총액을 Taotal 열에 추가해준다. 여기서 포인트는 각 for each row 안에 위치해서 클라이언트마다 총액이 들어간다는 점이다. 파산을 하지 않았다면 0, 파산을 했다면 총액이 들어간다.
<img src="https://velog.velcdn.com/images/jwkim_1018/post/456ca03b-380b-4c0a-9b0b-61d453e2890d/image.png" alt=""></p>
</li>
<li><p>Write CSV
client 데이터 테이블을 원본 client 파일에 붙여넣는다. 컬럼만 추가해서 넣으려 헀으나 csv 파일은 데이터 테이블을 전부 넣어야하므로(덮어쓰기이므로) 해당 액티비티를 사용했다.</p>
</li>
</ol>
<h2 id="4-3-calculating-percentages-of-expenses">4-3. Calculating Percentages of Expenses</h2>
<h3 id="4-3-1-과업-설명">4-3-1. 과업 설명</h3>
<p>카드, 현금 지출 내역에서 각 학목별, 월별로 모아 각각의 비율을 구해보자.</p>
<h3 id="4-3-2-구현">4-3-2. 구현</h3>
<ol>
<li><p>Read Range, Merge Data Table
두 개의 엑셀 파일을 읽고, 카드 데이터에서 현금 데이터를 합친다.</p>
</li>
<li><p>Add Data Column
카드 데이터에 월을 저장할 컬럼을 새로 지정한다.
<img src="https://velog.velcdn.com/images/jwkim_1018/post/93ba927c-e45e-4ffb-904a-4f68ba729a46/image.png" alt=""></p>
</li>
<li><p>Build Data Table
결과를 담을 데이터 테이블을 선언한다. 스키마는 아래와 같다.
<img src="https://velog.velcdn.com/images/jwkim_1018/post/ae3278d2-8f33-40e1-baec-163ea01519c4/image.png" alt=""></p>
</li>
<li><p>For Each Row in Data Table
카드 데이터에서 로우를 하나씩 돌며 필요한 작업을 한다. 필요한 작업이라 함은, 날짜 컬럼에서 월•년 부분만 걸러내기, 퍼센트를 구하기 위한 총액 구하기, 결과 데이터에 데이터 추가하기가 있다.</p>
</li>
<li><p>Get Row Item, Assign
월•년만 필요하므로 날짜 데이터를 가져온다. 그리고 VBA 문법을 사용하여 필요한 부분만 빼내어 저장한다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/678cbd80-7c09-44b2-9529-d5de36249bf6/image.png" alt=""></p>
</li>
<li><p>Assign
퍼센트 계산을 위한 총액을 계산한다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/f8f159e9-ea61-41c4-9a2d-a73dcfeb7962/image.png" alt=""><img src="https://velog.velcdn.com/images/jwkim_1018/post/9cb4084e-2ec8-401f-84b2-9a8c584c21e4/image.png" alt=""></p>
</li>
</ol>
<ol start="7">
<li><p>Add Data Row
결과 데이터 테이블에 지출 내역, 월 정보를 넣는다. 열을 추가할 때는 배열 모양으로 넣어야하므로 대괄호({})로 묶어준다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/c209d0d8-ac2f-46f8-8be9-d936c7d528ae/image.png" alt=""></p>
</li>
<li><p>Remove Duplicatie Rows
결과 데이터 테이블에서 중복을 제거해준다. 왜냐하면 카테고리별로, 월 별로 하나의 로우만 있어야하기 때문이다. </p>
</li>
<li><p>For Each Row In Data Table, Filter Data Table
결과 데이터 테이블을 돌면서 지출 카테고리, 월을 기준으로 지출 내역 데이터 테이블을 필터링한다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/3504fff5-9496-40bf-851e-dd1178b0fa13/image.png" alt=""></p>
</li>
<li><p>For Each Row in Data Table
위에서 필터링한 데이터는 동일한 지출 카테고리, 월 데이터들이다. 이를 소계 내서 결과 데이터 테이블에 넣어주면 된다. 그래서 필터링 된 데이터 테이블로 반복을 돌리고 소계를 내준다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/badbfa2a-ee7a-426a-abe7-689824130179/image.png" alt=""></p>
</li>
<li><p>Assign
위에서 계산한 소계와 그에 대한 퍼센트 값을 결과 데이터 테이블에 입력해준다.
<img src="https://velog.velcdn.com/images/jwkim_1018/post/0033dcf3-81b3-42a4-b726-0ac0d3bd1b88/image.png" alt=""></p>
</li>
<li><p>Write Range Workbook
결과 데이터 테이블을 새로운 엑셀 파일에 저장한다.<img src="https://velog.velcdn.com/images/jwkim_1018/post/ac20b731-76e2-4b67-bcc9-dafd9f9630e2/image.png" alt=""></p>
</li>
</ol>
<h1 id="5-느낀점">5. 느낀점</h1>
<p>나는 반복문을 너무 중첩해서 많이 사용하는 것 같다. 이를 해결하는 방법을 고안해야할 필요가 있을 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Tip - For Each Row in Data Table에서 원하는 값에 접근하기]]></title>
            <link>https://velog.io/@jwkim_1018/Tip-For-Each-Row-in-Data-Table%EC%97%90%EC%84%9C-%EC%9B%90%ED%95%98%EB%8A%94-%EA%B0%92%EC%97%90-%EC%A0%91%EA%B7%BC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jwkim_1018/Tip-For-Each-Row-in-Data-Table%EC%97%90%EC%84%9C-%EC%9B%90%ED%95%98%EB%8A%94-%EA%B0%92%EC%97%90-%EC%A0%91%EA%B7%BC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 10 Oct 2023 13:45:43 GMT</pubDate>
            <description><![CDATA[<p>데이터 테이블을 입력받아 각 로우별로 반복문을 수행하는 액티비티인 For Each Row in Data Table. 자동으로 컨테이너가 생성되는데, 이 안에 작업할 액티비티가 위치한다.</p>
<p>각 로우에서 원하는 값을 가져와야하는 경우가 상당히 많다. 그 때 사용할 수 있는 방법이 아래와 같다.</p>
<p><img src="https://velog.velcdn.com/images/jwkim_1018/post/09722d7d-c39e-4e49-a011-e8814184d299/image.png" alt=""></p>
<ol>
<li>데이터 테이블에서 로우 인덱스 가져오기
Assign 액티비티에 아래 코드와 같이 입력한다.<pre><code class="language-c">DataTable.Rows.IndexOf(Row) + 1</code></pre>
</li>
</ol>
<p>Row의 데이터 타입은 DataRow이다. 해당 행이 데이터 테이블에서 위치한 지점의 인덱스를 반환한다. (+1은 엑셀에서 사용하기 위해 더한 값임)</p>
<ol start="2">
<li>Get Row Item
DataRow 타입 데이터를 받아서 원하는 열 위치를 입력해 값을 가져온다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[UIPath Academy : 006-2. UI Automation With the Classic Experience]]></title>
            <link>https://velog.io/@jwkim_1018/UIPath-Academy-006-2.-UI-Automation-With-the-Classic-Experience</link>
            <guid>https://velog.io/@jwkim_1018/UIPath-Academy-006-2.-UI-Automation-With-the-Classic-Experience</guid>
            <pubDate>Sat, 07 Oct 2023 07:05:11 GMT</pubDate>
            <description><![CDATA[<h1 id="1-selection-option-screen-in-the-modern-design-experience">1. Selection Option Screen in the Modern Design Experience</h1>
<h2 id="1-1-input-methods-and-activities">1-1. Input Methods and Activities</h2>
<p>Input method는 기본적으로 Hardware events로 설정되어있고, 다른 method를 사용하려면 액티비티 속성창에서 변경하면 된다. 아래는 Input method의 주요 특징이다.</p>
<ol>
<li><p>Hardware Events
작동 방식 : 마우스와 키보드 입력 모두 사람이 하는 것과 동일하게 동작
장점 : 모든 어플리케이션과 호환,&#39;Enter&#39;나 &#39;Tab&#39; 같은 특수키 지원 o
단점 : 백그라운드에서 동작 x, 사용자는 자동화 동작 중 컴퓨터 사용 x, , UI 엘리먼트에 이미 텍스트가 있다면 자동으로 지우고 입력 x</p>
</li>
<li><p>Send Window Messages
작동 방식 : 마우스나 키보드를 사용할 때 window system에서 생성되는 window message를 받아서 어플리케이션에 입력, 클릭과 타이핑이 즉각적으로 됨
장점 : 백그라운드에서 동작 o, 사용자는 자동화 동작 중 컴퓨터 사용 o, &#39;Enter&#39;나 &#39;Tab&#39; 같은 특수키 지원 o
단점 : 모든 어플리케이션이 이 옵션을 제공하는 것은 아님, UI 엘리먼트에 이미 텍스트가 있다면 자동으로 지우고 입력 x</p>
</li>
<li><p>Simulate
작동 방식 : 어플리케이션에 API 수준에서 입력을 보냄, 수행 속도가 가장 빠름 / 클릭과 타이핑이 즉각적으로 됨
장점 : 백그라운드에서 동작 o, 사용자는 자동화 동작 중 컴퓨터 사용 o, UI 엘리먼트에 이미 텍스트가 있다면 자동으로 지우고 입력 o
단점 : API를 사용하는 것이기 때문에 단축키 사용 불가, 모든 어플리케이션이 이 옵션을 제공하는 것은 아님, &#39;Enter&#39;나 &#39;Tab&#39; 같은 특수키 지원 x</p>
</li>
</ol>
<p>UI 자동화를 할 때 어플리케이션에 어떤 값을 넣는 액티비티는 Click, Type Into, Send Hotckey, Hover가 있다.</p>
<p>Click, Type Into, Send HotKey의 주요 속성은 아래와 같다.(Hover는 그냥 마우스를 UI 엘리먼트 위로 올려놓는 것이므로 크게 신경 쓸 속성이 없음 / 나는 UIPath를 한국어 버전으로 사용하기 때문에 속성 이름은 한국어로 표기할 것임)</p>
<ol>
<li><p>Click 
커서동작유형 : 마우스 커서가 움직이는 방식 지정 - Instant와 Smooth가 있음
키 한정자 : 마우스 클릭을 할 때 함께 누를 특수키 지정(alt, ctrl, shift 등)
윈도우 메시지 전송(Bool) : 앞서 소개한 input method 중 SendWindowMessages 타입으로 변경
클릭 시뮬레이트(Bool) : 앞서 소개한 input method 중 Simulate 타입으로 변경</p>
</li>
<li><p>TypeInto
활성화(Bool) : True로 되어있으면 지정한 UI 엘리먼트를 forground로 가져와 글씨를 입력하고, False로 되어있으면 현재 열려있는 윈도우에서 글씨를 입력함 / default가 True / (입력할 때 창을 띄울건지 안띄울건지 제어하는 것인가봄)
비활성화된 경우 변경(Bool) : 지정한 UI 엘리먼트가 비활성화 되어도 실행이 가능
입력 전 클릭(Bool) : 지정한 UI 엘리먼트에 글씨를 입력하기 전 클릭함
키 간 지연 : 입력하는 키 사이에 얼마나 시간 간격을 둘지 지정(밀리초)
빈 필드 : 입력하기 전 UI 엘리먼트에 있는 글씨를 다 삭제
윈도우 메시지 전송(Bool) : 앞서 소개한 input method 중 SendWindowMessages 타입으로 변경
클릭 시뮬레이트(Bool) : 앞서 소개한 input method 중 Simulate 타입으로 변경</p>
</li>
<li><p>Send HotKey
활성화(Bool) : Type Into와 동일
입력 전 클릭(Bool) : Type Into와 동일
키 간 지연 : Type Into와 동일
키 한정자 : Click과 동일
윈도우 메시지 전송(Bool) : 앞서 소개한 input method 중 SendWindowMessages 타입으로 변경
클릭 시뮬레이트(Bool) : 앞서 소개한 input method 중 Simulate 타입으로 변경</p>
</li>
</ol>
<p>그리고 모든 input 액티비티는 작업 앞 뒤로 대기 시간을 설정할 수 있다. 
<img src="https://velog.velcdn.com/images/jwkim_1018/post/b2b1a393-f6b4-4ab2-be7b-77e428bd519c/image.png" alt=""></p>
<h2 id="1-2-output-methods-and-activities">1-2. Output Methods and Activities</h2>
<p>Output method에는 FullText, Native, OCR 방식이 있다. 각 방식의 특징은 아래와 같다.</p>
<table>
<thead>
<tr>
<th>Output Method</th>
<th>Full Text</th>
<th>Native</th>
<th>OCR</th>
</tr>
</thead>
<tbody><tr>
<td>호환성</td>
<td>대부분의 경우 사용 가능, default 임</td>
<td>Grapgics Design Interface(GDI)를 사용하는 어플리케이션에만 사용 가능</td>
<td>텍스트가 이미지로 되어있는 경우에만 사용 가능</td>
</tr>
<tr>
<td>자동화 속도</td>
<td>가장 빠름</td>
<td>Full Text 보다는 느림</td>
<td>가장 느림</td>
</tr>
<tr>
<td>정확도</td>
<td>100%</td>
<td>100%</td>
<td>상황에 따라 다름</td>
</tr>
<tr>
<td>백그라운드에서 실행 가능 여부</td>
<td>O</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td>숨겨진 텍스트에 대한 작업 여부</td>
<td>O</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td>가상환경에서 사용 가능 여부</td>
<td>X</td>
<td>X</td>
<td>O</td>
</tr>
<tr>
<td>기타</td>
<td>&quot;숨겨진 메시지 무시&quot; 옵션을 제공</td>
<td>default로 모든 문자(콤마, 따옴표, 공백 등도 포함)를 처리할 수 있지만, 원하는 문자만 지정해서 추출 가능</td>
<td>OCR 엔진으로는 두 개가 있음</td>
</tr>
</tbody></table>
<p>앞서 Input 액티비티에 Input method를 지정하는 방법을 봤다. Input 액티비티는 각 액티비티마다 method를 따로 지정할 수 있었지만, Output 액티비티는 각 액티비티마다 method가 정해져있다. 그리고 method는 액티비티 이름에서 대체로 알 수 있다.(예를 들어 Get OCR Text 액티비티는 OCR method를 사용함)</p>
<p>아래는 주요 Output 액티비티들이다.</p>
<ol>
<li><p>Get Text
UI 엘리먼트에서 String 값을 추출</p>
</li>
<li><p>Get Full Text
Full Text method로 UI 엘리먼트 값 추출(숨겨진 텍스트도 기본적으로 추출), 컨테이너가 자동으로 생성됨</p>
</li>
<li><p>Get Visible Text 
Native method로 UI 엘리먼트 값 추출, 컨테이너가 자동으로 생성됨</p>
</li>
<li><p>Get OCR Text
OCR method로 UI 엘리먼트 값 추출, 컨테이너가 자동으로 생성됨, UIPath Screen OCR 엔진 사용</p>
</li>
<li><p>Data Scraping Wizrd
어플리케이션, 브라우저, 문서 등에서 테이블 데이터를 추출, UIPath 기본 기능이며 디자인 &gt; 스크래핑 &gt; 데이터 스크래핑 메뉴에서 사용 가능</p>
</li>
<li><p>Extract Attributes
텍스트를 추출하지 않고 UI 엘리먼트의 속성값(색상, 폰트 등)을 추출하고 싶을 때 사용하는 액티비티, Get Ancestor/Get Attribute/Get Position 옵션이 있음</p>
</li>
</ol>
<h2 id="1-3-ui-element-synchronization">1-3. UI Element Synchronization</h2>
<p>UI 엘리먼트 동기화는 복잡한 상황에서도 자동화를 구현할 수 있게 지원하는 것이다.</p>
<p>우선, UI 자동화에서 입력/출력 과정은 UI 엘리먼트 인식, 데이터 입력/출력 두 단계로 나눠진다. 지금까지의 실습에서는 UI 엘리먼트 인식 과정을 액티비티 내에서 하거나 지연시간을 줘서 해결할 수 있었다. 하지만 구현 해야하는 프로세스가 복잡해지면 이렇게 간단하게 해결할 수 없다. 그래서 UI 엘리먼트 인식과 데이터 입력/출력 과정을 나눠야 할 필요가 생긴다.</p>
<p>아래는 UI 엘리먼트를 찾기 위해 사용할 수 있는 액티비티들이다.(입력 출력은 1-1과 1-2에서 살펴봤음)</p>
<ol>
<li><p>Find Element
지정한 UI 엘리먼트가 화면에 나타날 때까지 기다리고, 화면에 나타나면 UIElemnet 변수로 반환 / 반환된 UIElement 변수로 어떤 작업을 이어갈 때 유용함</p>
</li>
<li><p>Element Exists
지정한 UI 엘리먼트가 화면에 존재하는지 Boolean 값으로 반환, if문과 사용하면 좋음</p>
</li>
<li><p>Wait Element Vanish
지정한 UI 엘리먼트가 화면에서 사라질 때까지 대기 </p>
</li>
<li><p>On Element Appear
UI 엘리먼트가 화면에 나타날 때까지 대기, 컨테이너가 자동으로 생성됨</p>
</li>
<li><p>On Element Vanish
UI 엘리먼트가 화면에서 사라질 때까지 대기, 컨테이너가 자동으로 생성됨</p>
</li>
<li><p>Text Exsits
텍스트가 지정된 UI 엘리먼트에 있는지 확인, OCR 기술을 사용해서 확인하는 버전이 있음</p>
</li>
</ol>
]]></description>
        </item>
    </channel>
</rss>