<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>anna_developer.log</title>
        <link>https://velog.io/</link>
        <description>웹 개발자(FE / BE) anna입니다.</description>
        <lastBuildDate>Tue, 23 May 2023 04:51:18 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>anna_developer.log</title>
            <url>https://velog.velcdn.com/images/anna_developer/profile/1a18f743-9228-490b-9cc7-373a5a831873/image.ico</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. anna_developer.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/anna_developer" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[프로그래머스 소수 만들기 파이썬]]></title>
            <link>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%86%8C%EC%88%98-%EB%A7%8C%EB%93%A4%EA%B8%B0-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%86%8C%EC%88%98-%EB%A7%8C%EB%93%A4%EA%B8%B0-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Tue, 23 May 2023 04:51:18 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/12977">문제링크</a></p>
<pre><code>import itertools

# 소수를 찾는 알고리즘
def prime_num(n):
    for i in range(2, n):
        if n % i == 0:
            return False
    return True

def solution(nums):
    answer = 0

    # itertools.combinations(nums, 3)은 3가지의 조합을 return 해 준다.
    for i in itertools.combinations(nums, 3):
        # 조합의 모든 합을 소수 알고리즘에 넣어서
        cur_sum = sum(i)
        # 소수이면 답에 +1을 해 준다.
        if prime_num(cur_sum):
            answer += 1

    return answer</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 로또의 최고 순위와 최저 순위 파이썬]]></title>
            <link>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%A1%9C%EB%98%90%EC%9D%98-%EC%B5%9C%EA%B3%A0-%EC%88%9C%EC%9C%84%EC%99%80-%EC%B5%9C%EC%A0%80-%EC%88%9C%EC%9C%84-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%A1%9C%EB%98%90%EC%9D%98-%EC%B5%9C%EA%B3%A0-%EC%88%9C%EC%9C%84%EC%99%80-%EC%B5%9C%EC%A0%80-%EC%88%9C%EC%9C%84-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Mon, 22 May 2023 14:06:48 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/77484">문제링크</a></p>
<pre><code>def solution(lottos, win_nums):
    answer = []

    # 맞춘 갯수와 순위 딕셔너리
    score = {6:1, 5:2, 4:3, 3:4, 2:5, 1:6, 0:6}

    # 맞춘 갯수를 세기 위한 로또배열
    new_lottos = []

    # 0을 카운트 하기 위한 임시변수
    zero_cnt = 0

    # 로또를 확인해서
    for l in lottos:

        # 0인경우 0카운트 변수에 +1
        if l == 0:
            zero_cnt += 1

        # 0이 아니라 일반 숫자인 경우 로또배열에 +1
        else:
            new_lottos.append(l)

    # 최저 점수를 계산하기 위한 임시변수
    low_score = 0

    # 0이 아닌 번호 중 맞은 갯수만 최저 점수에 저장하고
    for i in new_lottos:
        if i in win_nums:
            low_score += 1

    # 0이 모두 맞는 번호일 때 최고점수와, 내가 적은 것 만 맞았을 때 최저점수를 append
    answer.append(score[low_score + zero_cnt])
    answer.append(score[low_score])

    return answer</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 [1차] 다트 게임 파이썬]]></title>
            <link>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-1%EC%B0%A8-%EB%8B%A4%ED%8A%B8-%EA%B2%8C%EC%9E%84-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-1%EC%B0%A8-%EB%8B%A4%ED%8A%B8-%EA%B2%8C%EC%9E%84-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Mon, 22 May 2023 08:01:35 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/17682">문제링크</a></p>
<pre><code>def solution(dartResult):

    # 10이 예외적으로 두 자리 숫자라서 한자리 문자로 표기해야 반복 시 꺼내기 쉬움
    dartResult = dartResult.replace(&#39;10&#39;, &#39;A&#39;)

    # 점수를 차례로 입력하기 위한 배열
    answer_list = []

    # 각 문자별 제곱근 저장 딕셔너리
    score_area = {&#39;S&#39;:1, &#39;D&#39;:2, &#39;T&#39;:3}

    # 현재 점수를 계산
    cur_score = 0

    # 다트결과에서 하나씩 꺼내어 점수 및 로직을 계산
    for i, d in enumerate(dartResult):

        # 꺼낸 값이 숫자거나 위에서 치환한 A일 때
        if d.isdigit() or d == &#39;A&#39;:
            # 숫자를 넣거나 A를 10으로 치환해서
            cur_score = int(d) if d.isdigit() else 10

            # 현재 점수를 먼저 배열에 저장
            answer_list.append(cur_score)

        # S, D, T 문자열일 경우는 점수 배열의 가장 최신 값에 해당 값만급 제곱    
        elif d in score_area:
            answer_list[-1] **= score_area[d]

        # #이나 *일 경우는 점수 배열의 가장 최신값과 그 앞 값에 로직 추가
        elif d == &#39;#&#39;:
            answer_list[-1] *= -1
        elif d == &#39;*&#39;:
            answer_list[-1] *= 2
            if i &gt; 2:
                answer_list[-2] *= 2

    # 담긴 점수 배열을 모두 합해서 return
    return sum(answer_list)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 명예의 전당(1) 파이썬]]></title>
            <link>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%AA%85%EC%98%88%EC%9D%98-%EC%A0%84%EB%8B%B91-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%AA%85%EC%98%88%EC%9D%98-%EC%A0%84%EB%8B%B91-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Sun, 21 May 2023 05:58:46 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/138477">문제링크</a></p>
<pre><code>def solution(k, score):
    answer = []

    # 방문 한 점수 배열
    visited = []

    # 받은 점수를 완전탐색
    for s in score:

        # 우선 점수를 방문 배열에 넣고
        visited.append(s)

        # 만약 방문 배열의 갯수가 명예의 전당 점수 갯수보다 작거나 같으면
        if len(visited) &lt;= k:
            # 방문 배열에 점수를 그냥 추가
            answer.append(min(visited))

        # 방문 배열이 점수 갯수보다 크면
        else:
            # 점수 비교를 위해서 방문 배열 중 가장 작은 수를 꺼내서
            cur_node = min(visited)

            # 방문 배열을 완전탐색
            for i, v in enumerate(visited):
                # 탐색 중 가장 작은 점수가 현재 점수 == 위에서 넣은 s와 같으면
                if cur_node == v:

                    # 방문 배열 중 같은 점수를 삭제하고
                    del visited[i]

                    # 답안에 방문 배열 중 가장 작은 수를 추가하고 넘어가기
                    answer.append(min(visited))
                    break

    return answer</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 가장 가까운 글자 파이썬]]></title>
            <link>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B0%80%EC%9E%A5-%EA%B0%80%EA%B9%8C%EC%9A%B4-%EA%B8%80%EC%9E%90-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B0%80%EC%9E%A5-%EA%B0%80%EA%B9%8C%EC%9A%B4-%EA%B8%80%EC%9E%90-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Sun, 21 May 2023 05:23:47 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/142086">문제링크</a></p>
<pre><code>from collections import deque

def solution(s):
    answer = []

    # 방문 한 문자
    visited = []

    # 문자를 하나씩 꺼내서 비교하기 위해 deque 선언
    queue = deque(list(s))

    # queue가 다 떨어질 때 까지
    while queue:

        # 현재 노드는 큐에서 한 개 꺼내서 확인
        cur_node = queue.popleft()

        # 만약 꺼낸 노드가 이미 확인 한 문자배열에 있다면
        if cur_node in visited:

            # 방문한 배열을 뒤집어서 뒤에서부터 확인
            visited.reverse()

            # 앞에서부터 차례대로 비교하면서
            for i, v in enumerate(visited):

                # 만약 현재 노드와 확인 한 문자가 같다면
                if v == cur_node:

                    # 그 차이만큼 answer 배열에 더해주고
                    answer.append(i + 1)

                    # 방문 배열을 다시 원래대로하고 빠져나감
                    visited.reverse()
                    break

        # 한 번도 방문한 적 없는 노드라면 -1을 answer에 append
        else:
            answer.append(-1)
        visited.append(cur_node)

    return answer</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 개인정보 수집 유효기간]]></title>
            <link>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4-%EC%88%98%EC%A7%91-%EC%9C%A0%ED%9A%A8%EA%B8%B0%EA%B0%84</link>
            <guid>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4-%EC%88%98%EC%A7%91-%EC%9C%A0%ED%9A%A8%EA%B8%B0%EA%B0%84</guid>
            <pubDate>Sun, 21 May 2023 03:56:46 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/150370">문제링크</a></p>
<pre><code>def solution(today, terms, privacies):
    answer = []

    # 개인정보 약관을 보관할 딕셔너리 선언
    term_dic = {}

    # 연, 월, 일을 각자 계산하기 귀찮으니 하나의 긴 숫자로 표현한다.
    year, month, date = today.split(&#39;.&#39;)
    today = int(year)*12*28 + int(month)*28 + int(date)

    # 개인정보 약관을 딕셔너리로 변환해야 한다.
    for t in terms:
        term_dic[t.split(&#39; &#39;)[0]] = t.split(&#39; &#39;)[1]

    # enumerate를 통해 인덱스화 함께, 받은 날짜와 약관만큼 반복을 돌게 한다.
    for i, p in enumerate(privacies):

        # 받은 연월일을 똑같이 나눠주고
        new_year, new_month, new_date = p.split(&#39;.&#39;)

        # 받은 일에는 약관기호가 포함되어 있으므로 분리 해 준다.
        new_new_date, new_privacie = new_date.split(&#39; &#39;)

        # 검사해야 할 날짜를 오늘 날짜와 똑같이 하나의 숫자로 변경 해 준다.
        privacies_date = int(new_year)*12*28 + int(new_month)*28 + int(new_new_date)

        # 오늘이랑 같거나 오늘이 더 크면 약관을 파기해야 하므로 인덱스 + 1만큼 넣어준다.
        if today &gt;= privacies_date + int(term_dic[new_privacie]) * 28:
            answer.append(i + 1)

    return answer</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 숫자 짝꿍 파이썬]]></title>
            <link>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%88%AB%EC%9E%90-%EC%A7%9D%EA%BF%8D-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%88%AB%EC%9E%90-%EC%A7%9D%EA%BF%8D-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Sat, 20 May 2023 17:25:05 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/131128">문제링크</a></p>
<blockquote>
<p>시간초과가 걸린 내 답</p>
</blockquote>
<pre><code>def solution(X, Y):
    new_Y = list(Y)
    visited = []

    for x in X:
        if x in new_Y:
            visited.append(int(x))
            new_Y.remove(x)

    visited.sort(reverse = True)
    if len(visited) == 0:
        return &#39;-1&#39;
    elif sum(visited) == 0:
        return &#39;0&#39;
    else:
        return &#39;&#39;.join(str(s) for s in visited)
</code></pre><blockquote>
<p>다른 사람들이 해결 한 정답</p>
</blockquote>
<pre><code>from collections import Counter

def solution(X, Y):
    # 숫자 개수 세기
    nums = Counter(X) &amp; Counter(Y)
    if not nums: return &#39;-1&#39; # 공통 없는 경우
    elif list(nums) == [&#39;0&#39;]: return &#39;0&#39; # 0만 공통인 경우

    nums_order = sorted(list(nums),reverse=True) # 내림차순 정렬
    answer = &#39;&#39;
    for num in nums_order:
        answer += num * nums[num]
    return answer</code></pre><p>Counter에 리스트나 문자열을 입력으로 주게 되면 그 안에 포함된 원소의 개수를 세어 dictionary와 유사한 Counter 객체를 반환한다.</p>
<p>예를 들어 [1,2,2,3,3,3] 라는 리스트가 있을 때 이를 Counter의 입력으로 주면 Counter({3: 3, 2: 2, 1: 1})가 반환된다.</p>
<p>이때 dictionary와 마찬가지로 key로 접근할 수 있다.</p>
<p>따라서 문자열 X, Y에 포함된 글자의 개수를 Counter로 세고 교집합 기능(&amp;)을 이용하여 중복을 찾아낸다.</p>
<p>1) 중복이 없는 경우엔 -1을 반환한다.</p>
<p>2) 중복이 0으로 유일한 경우엔 0을 반환한다.</p>
<p>3) 그 외의 경우 큰 숫자부터 Counter에 key로 접근하여 개수만큼 정답 문자열에 추가하면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[에라토스테네스의 체.. 라는데]]></title>
            <link>https://velog.io/@anna_developer/%EC%97%90%EB%9D%BC%ED%86%A0%EC%8A%A4%ED%85%8C%EB%84%A4%EC%8A%A4%EC%9D%98-%EC%B2%B4..-%EB%9D%BC%EB%8A%94%EB%8D%B0</link>
            <guid>https://velog.io/@anna_developer/%EC%97%90%EB%9D%BC%ED%86%A0%EC%8A%A4%ED%85%8C%EB%84%A4%EC%8A%A4%EC%9D%98-%EC%B2%B4..-%EB%9D%BC%EB%8A%94%EB%8D%B0</guid>
            <pubDate>Tue, 09 May 2023 06:14:20 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/12921">문제링크</a></p>
<p>소수를 찾는 과정이라고 한다.</p>
<pre><code>def solution(n):
    answer = 0
    new_list = [0] * (n + 1)
    for i in range(2, n + 1):
        if new_list[i] == 0:
            answer += 1
            for j in range(i, n+1, i):
                new_list[j] = 1

    return answer</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 신고 결과 받기 파이썬]]></title>
            <link>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%8B%A0%EA%B3%A0-%EA%B2%B0%EA%B3%BC-%EB%B0%9B%EA%B8%B0-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%8B%A0%EA%B3%A0-%EA%B2%B0%EA%B3%BC-%EB%B0%9B%EA%B8%B0-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Mon, 08 May 2023 09:57:48 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/92334">문제링크</a></p>
<pre><code>def solution(id_list, report, k):

    # 답과 신고당한 사람 목록:횟수를 딕셔너리로 초기화

    answer = {a: 0 for a in id_list}
    reported = {r: 0 for r in id_list}

    # 중복신고는 1회로 처리하므로, set으로 중복을 제거한 반복
    for i in set(report):

        # a, b는 각각 신고한사람, 신고당한 사람
        a,b = i.split()

        # 신고당한 사람의 딕셔너리에 횟수를 추가함
        reported[b] += 1

    # 중복신고는 1회로 처리하므로, set으로 중복을 제거한 반복
    for i in set(report):

        # a, b는 각각 신고한사람, 신고당한 사람
        a,b = i.split()

        # 만약 신고당한 횟수가 정지먹을 만큼의 횟수만큼 이상이라면
        if reported[b] &gt;= k:

            # 답 딕셔너리에 있는 사람에게 메일을 한 개 보냄
            answer[a] += 1

    # 답 딕셔너리의 values만 list로 출력
    return list(answer.values())</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 성격 유형 검사하기 파이썬]]></title>
            <link>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%84%B1%EA%B2%A9-%EC%9C%A0%ED%98%95-%EA%B2%80%EC%82%AC%ED%95%98%EA%B8%B0-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%84%B1%EA%B2%A9-%EC%9C%A0%ED%98%95-%EA%B2%80%EC%82%AC%ED%95%98%EA%B8%B0-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Mon, 08 May 2023 08:55:56 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/118666">문제링크</a></p>
<pre><code>def solution(survey, choices):
    answer = &#39;&#39;

    # 성격유형을 딕셔너리로 각 유형마다 점수를 저장
    mbti_dic = {&#39;R&#39;:0, &#39;T&#39;:0, &#39;C&#39;:0, &#39;F&#39;:0, &#39;J&#39;:0, &#39;M&#39;:0, &#39;A&#39;:0, &#39;N&#39;:0}

    # 선택지만큼 반복
    for i in range(len(choices)):

        # 만약 선택지가 1, 2, 3 중 하나라면
        if choices[i] &lt;= 3:

            # 4에서 점수를 뺀 만큼의 점수를 survey의 첫번째 문자값에 저장
            mbti_dic[survey[i][0]] += 4 - choices[i]

            # 선택지가 5, 6, 7 중 하나라면
        elif choices[i] &gt;= 5:

            # 점수에서 4를 뺀 점수를 survey의 두 번째 문자값에 저장
            mbti_dic[survey[i][1]] += choices[i] - 4

    # answer 조합을 위해 key와 value를 분리하고, 반복문을 사용하기 위해 list 형변환
    keys = list(mbti_dic.keys())
    values = list(mbti_dic.values())

    # key와 value는 8가지가 있으므로 8번을 도는데, 앞뒤 숫자를 비교하기 위해 step을 2로 지정
    for i in range(0, 7, 2):

        # 만약 앞 숫자가 더 크다면 앞번의 key를 추가
        if values[i] &gt;= values[i + 1]:
            answer += keys[i]
        # 뒤 숫자가 더 크다면 뒤번의 key를 추가
        else:
            answer += keys[i + 1]

    return answer</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 신규 아이디 추천 파이썬]]></title>
            <link>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%8B%A0%EA%B7%9C-%EC%95%84%EC%9D%B4%EB%94%94-%EC%B6%94%EC%B2%9C-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%8B%A0%EA%B7%9C-%EC%95%84%EC%9D%B4%EB%94%94-%EC%B6%94%EC%B2%9C-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Mon, 08 May 2023 07:48:30 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/72410">문제링크</a></p>
<pre><code>from collections import deque

def solution(new_id):

    # 1단계: 모든 대문자를 소문자로 치환
    new_array = list(new_id.lower())

    # 숫자목록
    num_list = [str(i) for i in range(10)]

    # 소문자 목록
    char_list = [chr(i) for i in range(97, 123)]

    # 특수문자 목록
    str_list = [&#39;-&#39;, &#39;_&#39;, &#39;.&#39;]

    # 아이디 생성을 위한 queue 생성
    check_list = deque()

    # 주어진 아이디의 길이만큼
    for i in range(len(new_array)):

        # 2단계: 소문자, 숫자, 빼기, 밑줄, 마침표가 있을 때만
        if new_array[i] in num_list or new_array[i] in char_list or new_array[i] in str_list:

            # 새로운 아이디 queue에 추가
            check_list.append(new_array[i])

            # 3단계: 아이디의 마침표가 연속으로 있을 경우 제거
            if len(check_list) &gt; 1:
                if check_list[-1] == &#39;.&#39; and check_list[-2] == &#39;.&#39;:
                    check_list.pop()

    # 4단계: 마침표가 처음이나 마지막에 위치한다면 제거
    if check_list[0] == &#39;.&#39;:
        check_list.popleft()
    elif check_list[-1] == &#39;.&#39;:
        check_list.pop()     

    # 5단계: 아이디가 빈 문자열이라면 a를 추가
    if len(check_list) == 0:
        check_list.append(&#39;a&#39;)

    # 6단계 이후 검증을 위한 새로운 queue 생성
    new_check_list = deque()

    # 6단계: 아이디가 16자 이상이라면 15자까지만 새로운 queue에 추가
    if len(check_list) &gt;= 16:
        for i in range(15):
            new_check_list.append(check_list[i])
    else:
        for i in check_list:
            new_check_list.append(i)


    # 6단계: 아이디 문자열 제거 후 마침표가 마지막에 있다면 제거
    if new_check_list[-1] == &#39;.&#39;:
        new_check_list.pop()

    # 7단계: 아이디가 두 글자 이하일 경우 마지막 문자를 아이디가 3자리가 될 때 까지 추가
    if len(new_check_list) &lt;= 2:
        cur_chr = new_check_list[-1]
        for i in range(3 - len(new_check_list)):
            new_check_list.append(cur_chr)

    # 1 - 7단계까지 거친 queue를 하나의 문자로 합쳐서 return    
    return &#39;&#39;.join(new_check_list)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 크레인 인형뽑기 게임 파이썬]]></title>
            <link>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%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-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%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-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Mon, 08 May 2023 03:39:56 GMT</pubDate>
            <description><![CDATA[<pre><code>from collections import deque
def solution(board, moves):

    answer = 0

    # 뽑은 인형
    visited = []

    # 뽑을 인형의 열위치 queue
    new_deque = deque(moves)

    for i in range(len(moves)):

        # 뽑을 인형의 열 위치
        cur_num = new_deque.popleft()

        for j in range(len(board)):

            # 이번 행 / 열에 인형이 있으면 뽑아서 뽑은 인형에 담음
            if board[j][cur_num - 1] != 0:
                visited.append(board[j][cur_num - 1])
                board[j][cur_num - 1] = 0
                break

            # 뽑을 인형이 없으면 넘어감 
            else:
                continue

        # 뽑은 인형의 갯수가 두 개 이상일 때 만        
        if len(visited) &gt; 1:

            # 마지막 두 개의 인형이 같은 인형이면
            if visited[-1] == visited[-2]:

                # 인형 두 개를 삭제하고
                visited.pop(-1)
                visited.pop(-1)

                # 두 개가 사라지므로 +2씩
                answer +=2

    return answer</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 숫자 문자열과 영단어 파이썬]]></title>
            <link>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%88%AB%EC%9E%90-%EB%AC%B8%EC%9E%90%EC%97%B4%EA%B3%BC-%EC%98%81%EB%8B%A8%EC%96%B4-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%88%AB%EC%9E%90-%EB%AC%B8%EC%9E%90%EC%97%B4%EA%B3%BC-%EC%98%81%EB%8B%A8%EC%96%B4-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Sun, 30 Apr 2023 09:55:23 GMT</pubDate>
            <description><![CDATA[<p>네오와 프로도가 숫자놀이를 하고 있습니다. 네오가 프로도에게 숫자를 건넬 때 일부 자릿수를 영단어로 바꾼 카드를 건네주면 프로도는 원래 숫자를 찾는 게임입니다.</p>
<p>다음은 숫자의 일부 자릿수를 영단어로 바꾸는 예시입니다.</p>
<p>1478 → &quot;one4seveneight&quot;
234567 → &quot;23four5six7&quot;
10203 → &quot;1zerotwozero3&quot;
이렇게 숫자의 일부 자릿수가 영단어로 바뀌어졌거나, 혹은 바뀌지 않고 그대로인 문자열 s가 매개변수로 주어집니다. s가 의미하는 원래 숫자를 return 하도록 solution 함수를 완성해주세요.</p>
<p>참고로 각 숫자에 대응되는 영단어는 다음 표와 같습니다.</p>
<p>숫자    영단어
0    zero
1    one
2    two
3    three
4    four
5    five
6    six
7    seven
8    eight
9    nine
제한사항
1 ≤ s의 길이 ≤ 50
s가 &quot;zero&quot; 또는 &quot;0&quot;으로 시작하는 경우는 주어지지 않습니다.
return 값이 1 이상 2,000,000,000 이하의 정수가 되는 올바른 입력만 s로 주어집니다.
입출력 예
s    result
&quot;one4seveneight&quot;    1478
&quot;23four5six7&quot;    234567
&quot;2three45sixseven&quot;    234567
&quot;123&quot;    123</p>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<h4 id="입출력-예-1">입출력 예 #1</h4>
<p>문제 예시와 같습니다.</p>
<h4 id="입출력-예-2">입출력 예 #2</h4>
<p>문제 예시와 같습니다.</p>
<h4 id="입출력-예-3">입출력 예 #3</h4>
<p>&quot;three&quot;는 3, &quot;six&quot;는 6, &quot;seven&quot;은 7에 대응되기 때문에 정답은 입출력 예 #2와 같은 234567이 됩니다.
입출력 예 #2와 #3과 같이 같은 정답을 가리키는 문자열이 여러 가지가 나올 수 있습니다.
입출력 예 #4</p>
<p>s에는 영단어로 바뀐 부분이 없습니다.
제한시간 안내
정확성 테스트 : 10초</p>
<pre><code>def solution(s):
    answer = s 
    dix = {&#39;zero&#39;:0, &#39;one&#39;:1, &#39;two&#39;:2, &#39;three&#39;:3, &#39;four&#39;:4, &#39;five&#39;:5, &#39;six&#39;:6, &#39;seven&#39;:7, &#39;eight&#39;:8, &#39;nine&#39;:9}

    for i in dix.items():
        answer = answer.replace(i[0], str(i[1]))   

    return int(answer)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 카드뭉치 파이썬]]></title>
            <link>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%B9%B4%EB%93%9C%EB%AD%89%EC%B9%98-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%B9%B4%EB%93%9C%EB%AD%89%EC%B9%98-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Sun, 30 Apr 2023 06:06:36 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<p>코니는 영어 단어가 적힌 카드 뭉치 두 개를 선물로 받았습니다. 코니는 다음과 같은 규칙으로 카드에 적힌 단어들을 사용해 원하는 순서의 단어 배열을 만들 수 있는지 알고 싶습니다.</p>
<p>원하는 카드 뭉치에서 카드를 순서대로 한 장씩 사용합니다.
한 번 사용한 카드는 다시 사용할 수 없습니다.
카드를 사용하지 않고 다음 카드로 넘어갈 수 없습니다.
기존에 주어진 카드 뭉치의 단어 순서는 바꿀 수 없습니다.
예를 들어 첫 번째 카드 뭉치에 순서대로 [&quot;i&quot;, &quot;drink&quot;, &quot;water&quot;], 두 번째 카드 뭉치에 순서대로 [&quot;want&quot;, &quot;to&quot;]가 적혀있을 때 [&quot;i&quot;, &quot;want&quot;, &quot;to&quot;, &quot;drink&quot;, &quot;water&quot;] 순서의 단어 배열을 만들려고 한다면 첫 번째 카드 뭉치에서 &quot;i&quot;를 사용한 후 두 번째 카드 뭉치에서 &quot;want&quot;와 &quot;to&quot;를 사용하고 첫 번째 카드뭉치에 &quot;drink&quot;와 &quot;water&quot;를 차례대로 사용하면 원하는 순서의 단어 배열을 만들 수 있습니다.</p>
<p>문자열로 이루어진 배열 cards1, cards2와 원하는 단어 배열 goal이 매개변수로 주어질 때, cards1과 cards2에 적힌 단어들로 goal를 만들 있다면 &quot;Yes&quot;를, 만들 수 없다면 &quot;No&quot;를 return하는 solution 함수를 완성해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<p>1 ≤ cards1의 길이, cards2의 길이 ≤ 10
1 ≤ cards1[i]의 길이, cards2[i]의 길이 ≤ 10
cards1과 cards2에는 서로 다른 단어만 존재합니다.
2 ≤ goal의 길이 ≤ cards1의 길이 + cards2의 길이
1 ≤ goal[i]의 길이 ≤ 10
goal의 원소는 cards1과 cards2의 원소들로만 이루어져 있습니다.
cards1, cards2, goal의 문자열들은 모두 알파벳 소문자로만 이루어져 있습니다.</p>
<h3 id="입출력-예">입출력 예</h3>
<p>cards1    cards2    goal    result
[&quot;i&quot;, &quot;drink&quot;, &quot;water&quot;]    [&quot;want&quot;, &quot;to&quot;]    [&quot;i&quot;, &quot;want&quot;, &quot;to&quot;, &quot;drink&quot;, &quot;water&quot;]    &quot;Yes&quot;
[&quot;i&quot;, &quot;water&quot;, &quot;drink&quot;]    [&quot;want&quot;, &quot;to&quot;]    [&quot;i&quot;, &quot;want&quot;, &quot;to&quot;, &quot;drink&quot;, &quot;water&quot;]    &quot;No&quot;</p>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<h4 id="입출력-예-1">입출력 예 #1</h4>
<p>본문과 같습니다.</p>
<h4 id="입출력-예-2">입출력 예 #2</h4>
<p>cards1에서 &quot;i&quot;를 사용하고 cards2에서 &quot;want&quot;와 &quot;to&quot;를 사용하여 &quot;i want to&quot;까지는 만들 수 있지만 &quot;water&quot;가 &quot;drink&quot;보다 먼저 사용되어야 하기 때문에 해당 문장을 완성시킬 수 없습니다. 따라서 &quot;No&quot;를 반환합니다.</p>
<h3 id="나의-풀이">나의 풀이</h3>
<pre><code>from collections import deque

def solution(cards1, cards2, goal):
    queue1, queue2 = deque(cards1), deque(cards2)

    answer = &quot;Yes&quot;

    for g in goal:
        if queue1 and g == queue1[0]:
            queue1.popleft()
            continue
        if queue2 and g == queue2[0]:
            queue2.popleft()
            continue
        answer = &quot;No&quot;

    return answer</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 바탕화면 정리 파이썬]]></title>
            <link>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%B0%94%ED%83%95%ED%99%94%EB%A9%B4-%EC%A0%95%EB%A6%AC-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%B0%94%ED%83%95%ED%99%94%EB%A9%B4-%EC%A0%95%EB%A6%AC-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Sun, 30 Apr 2023 03:46:41 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<p>코딩테스트를 준비하는 머쓱이는 프로그래머스에서 문제를 풀고 나중에 다시 코드를 보면서 공부하려고 작성한 코드를 컴퓨터 바탕화면에 아무 위치에나 저장해 둡니다. 저장한 코드가 많아지면서 머쓱이는 본인의 컴퓨터 바탕화면이 너무 지저분하다고 생각했습니다. 프로그래머스에서 작성했던 코드는 그 문제에 가서 다시 볼 수 있기 때문에 저장해 둔 파일들을 전부 삭제하기로 했습니다.</p>
<p>컴퓨터 바탕화면은 각 칸이 정사각형인 격자판입니다. 이때 컴퓨터 바탕화면의 상태를 나타낸 문자열 배열 wallpaper가 주어집니다. 파일들은 바탕화면의 격자칸에 위치하고 바탕화면의 격자점들은 바탕화면의 가장 왼쪽 위를 (0, 0)으로 시작해 (세로 좌표, 가로 좌표)로 표현합니다. 빈칸은 &quot;.&quot;, 파일이 있는 칸은 &quot;#&quot;의 값을 가집니다. 드래그를 하면 파일들을 선택할 수 있고, 선택된 파일들을 삭제할 수 있습니다. 머쓱이는 최소한의 이동거리를 갖는 한 번의 드래그로 모든 파일을 선택해서 한 번에 지우려고 하며 드래그로 파일들을 선택하는 방법은 다음과 같습니다.</p>
<p>드래그는 바탕화면의 격자점 S(lux, luy)를 마우스 왼쪽 버튼으로 클릭한 상태로 격자점 E(rdx, rdy)로 이동한 뒤 마우스 왼쪽 버튼을 떼는 행동입니다. 이때, &quot;점 S에서 점 E로 드래그한다&quot;고 표현하고 점 S와 점 E를 각각 드래그의 시작점, 끝점이라고 표현합니다.</p>
<p>점 S(lux, luy)에서 점 E(rdx, rdy)로 드래그를 할 때, &quot;드래그 한 거리&quot;는 |rdx - lux| + |rdy - luy|로 정의합니다.</p>
<p>점 S에서 점 E로 드래그를 하면 바탕화면에서 두 격자점을 각각 왼쪽 위, 오른쪽 아래로 하는 직사각형 내부에 있는 모든 파일이 선택됩니다.</p>
<p>예를 들어 wallpaper = [&quot;.#...&quot;, &quot;..#..&quot;, &quot;...#.&quot;]인 바탕화면을 그림으로 나타내면 다음과 같습니다.</p>
<p>이러한 바탕화면에서 다음 그림과 같이 S(0, 1)에서 E(3, 4)로 드래그하면 세 개의 파일이 모두 선택되므로 드래그 한 거리 (3 - 0) + (4 - 1) = 6을 최솟값으로 모든 파일을 선택 가능합니다.
eg1-2.png
(0, 0)에서 (3, 5)로 드래그해도 모든 파일을 선택할 수 있지만 이때 드래그 한 거리는 (3 - 0) + (5 - 0) = 8이고 이전의 방법보다 거리가 늘어납니다.</p>
<p>머쓱이의 컴퓨터 바탕화면의 상태를 나타내는 문자열 배열 wallpaper가 매개변수로 주어질 때 바탕화면의 파일들을 한 번에 삭제하기 위해 최소한의 이동거리를 갖는 드래그의 시작점과 끝점을 담은 정수 배열을 return하는 solution 함수를 작성해 주세요. 드래그의 시작점이 (lux, luy), 끝점이 (rdx, rdy)라면 정수 배열 [lux, luy, rdx, rdy]를 return하면 됩니다.</p>
<h3 id="제한사항">제한사항</h3>
<p>1 ≤ wallpaper의 길이 ≤ 50
1 ≤ wallpaper[i]의 길이 ≤ 50
wallpaper의 모든 원소의 길이는 동일합니다.
wallpaper[i][j]는 바탕화면에서 i + 1행 j + 1열에 해당하는 칸의 상태를 나타냅니다.
wallpaper[i][j]는 &quot;#&quot; 또는 &quot;.&quot;의 값만 가집니다.
바탕화면에는 적어도 하나의 파일이 있습니다.
드래그 시작점 (lux, luy)와 끝점 (rdx, rdy)는 lux &lt; rdx, luy &lt; rdy를 만족해야 합니다.</p>
<h3 id="입출력-예">입출력 예</h3>
<p>wallpaper    result
[&quot;.#...&quot;, &quot;..#..&quot;, &quot;...#.&quot;]    [0, 1, 3, 4]
[&quot;..........&quot;, &quot;.....#....&quot;, &quot;......##..&quot;, &quot;...##.....&quot;, &quot;....#.....&quot;]    [1, 3, 5, 8]
[&quot;.##...##.&quot;, &quot;#..#.#..#&quot;, &quot;#...#...#&quot;, &quot;.#.....#.&quot;, &quot;..#...#..&quot;, &quot;...#.#...&quot;, &quot;....#....&quot;]    [0, 0, 7, 9]
[&quot;..&quot;, &quot;#.&quot;]    [1, 0, 2, 1]</p>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<h4 id="입출력-예-1">입출력 예 #1</h4>
<p>문제 설명의 예시와 같은 예제입니다. (0, 1)에서 (3, 4)로 드래그 하면 모든 파일을 선택할 수 있고 드래그 한 거리는 6이었고, 6보다 적은 거리로 모든 파일을 선택하는 방법은 없습니다. 따라서 [0, 1, 3, 4]를 return합니다.</p>
<h3 id="입출력-예-2">입출력 예 #2</h3>
<p>예제 2번의 바탕화면은 다음과 같습니다.</p>
<p>(1, 3)에서 (5, 8)로 드래그하면 모든 파일을 선택할 수 있고 이보다 적은 이동거리로 모든 파일을 선택하는 방법은 없습니다. 따라서 가장 적은 이동의 드래그로 모든 파일을 선택하는 방법인 [1, 3, 5, 8]을 return합니다.</p>
<h3 id="입출력-예-3">입출력 예 #3</h3>
<p>예제 3번의 바탕화면은 다음과 같습니다.</p>
<p>모든 파일을 선택하기 위해선 바탕화면의 가장 왼쪽 위 (0, 0)에서 가장 오른쪽 아래 (7, 9)로 드래그 해야만 합니다. 따라서 [0, 0, 7, 9]를 return합니다.</p>
<h3 id="입출력-예-4">입출력 예 #4</h3>
<p>예제 4번의 바탕화면은 다음과 같이 2행 1열에만 아이콘이 있습니다.</p>
<p>이를 드래그로 선택하기 위해서는 그 칸의 왼쪽 위 (1, 0)에서 오른쪽 아래 (2, 1)로 드래그 하면 됩니다. (1, 0)에서 (2, 2)로 드래그 해도 아이콘을 선택할 수 있지만 이전보다 이동거리가 늘어납니다. 따라서 [1, 0, 2, 1]을 return합니다.</p>
<h3 id="나의-풀이">나의 풀이</h3>
<pre><code>def solution(wallpaper):

    x, y = [], []

    for i in range(len(wallpaper)):
        for j in range(len(wallpaper[0])):
            if wallpaper[i][j] == &#39;#&#39;:
                x.append(i)
                y.append(j)

    return [min(x), min(y), max(x) + 1, max(y) + 1]</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 공원 산책 파이썬]]></title>
            <link>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B3%B5%EC%9B%90-%EC%82%B0%EC%B1%85-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@anna_developer/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B3%B5%EC%9B%90-%EC%82%B0%EC%B1%85-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Sat, 29 Apr 2023 13:08:10 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<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>공원을 나타내는 문자열 배열 park, 로봇 강아지가 수행할 명령이 담긴 문자열 배열 routes가 매개변수로 주어질 때, 로봇 강아지가 모든 명령을 수행 후 놓인 위치를 [세로 방향 좌표, 가로 방향 좌표] 순으로 배열에 담아 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<p>3 ≤ park의 길이 ≤ 50
3 ≤ park[i]의 길이 ≤ 50
park[i]는 다음 문자들로 이루어져 있으며 시작지점은 하나만 주어집니다.
S : 시작 지점
O : 이동 가능한 통로
X : 장애물
park는 직사각형 모양입니다.
1 ≤ routes의 길이 ≤ 50
routes의 각 원소는 로봇 강아지가 수행할 명령어를 나타냅니다.
로봇 강아지는 routes의 첫 번째 원소부터 순서대로 명령을 수행합니다.
routes의 원소는 &quot;op n&quot;과 같은 구조로 이루어져 있으며, op는 이동할 방향, n은 이동할 칸의 수를 의미합니다.
op는 다음 네 가지중 하나로 이루어져 있습니다.
N : 북쪽으로 주어진 칸만큼 이동합니다.
S : 남쪽으로 주어진 칸만큼 이동합니다.
W : 서쪽으로 주어진 칸만큼 이동합니다.
E : 동쪽으로 주어진 칸만큼 이동합니다.
1 ≤ n ≤ 9
입출력 예
park    routes    result
[&quot;SOO&quot;,&quot;OOO&quot;,&quot;OOO&quot;]    [&quot;E 2&quot;,&quot;S 2&quot;,&quot;W 1&quot;]    [2,1]
[&quot;SOO&quot;,&quot;OXX&quot;,&quot;OOO&quot;]    [&quot;E 2&quot;,&quot;S 2&quot;,&quot;W 1&quot;]    [0,1]
[&quot;OSO&quot;,&quot;OOO&quot;,&quot;OXO&quot;,&quot;OOO&quot;]    [&quot;E 2&quot;,&quot;S 3&quot;,&quot;W 1&quot;]    [0,0]</p>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<h4 id="입출력-예-1">입출력 예 #1</h4>
<p>입력된 명령대로 동쪽으로 2칸, 남쪽으로 2칸, 서쪽으로 1칸 이동하면 [0,0] -&gt; [0,2] -&gt; [2,2] -&gt; [2,1]이 됩니다.</p>
<h4 id="입출력-예-2">입출력 예 #2</h4>
<p>입력된 명령대로라면 동쪽으로 2칸, 남쪽으로 2칸, 서쪽으로 1칸 이동해야하지만 남쪽으로 2칸 이동할 때 장애물이 있는 칸을 지나기 때문에 해당 명령을 제외한 명령들만 따릅니다. 결과적으로는 [0,0] -&gt; [0,2] -&gt; [0,1]이 됩니다.</p>
<h4 id="입출력-예-3">입출력 예 #3</h4>
<p>처음 입력된 명령은 공원을 나가게 되고 두 번째로 입력된 명령 또한 장애물을 지나가게 되므로 두 입력은 제외한 세 번째 명령만 따르므로 결과는 다음과 같습니다. [0,1] -&gt; [0,0]</p>
<h3 id="나의-풀이">나의 풀이</h3>
<pre><code>def solution(park, routes):
    x = 0
    y = 0 

    for i in range(len(park)):
        for j in range(len(park[i])):
            if park[i][j] == &#39;S&#39;:
                x = j
                y = i
                break

    for route in routes:
        new_x = x
        new_y = y
        for r in range(int(route[2])):
            if route[0] == &#39;E&#39; and new_x &lt; len(park[0])-1 and park[new_y][new_x+1] != &#39;X&#39;:
                new_x += 1
                if r == int(route[2])-1:
                    x = new_x
            if route[0] == &#39;W&#39; and new_x &gt; 0 and park[new_y][new_x-1] != &#39;X&#39;:
                new_x -= 1
                if r == int(route[2])-1:
                    x = new_x
            if route[0] == &#39;N&#39; and new_y &gt; 0 and park[new_y-1][new_x] != &#39;X&#39;:
                new_y -= 1
                if r == int(route[2])-1:
                    y = new_y
            if route[0] == &#39;S&#39; and new_y &lt; len(park)-1 and park[new_y+1][new_x] != &#39;X&#39;:
                new_y += 1
                if r == int(route[2])-1:
                    y = new_y

    return [y, x]</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[자주 불러오는 데이터는 캐싱으로 조회 성능을 높여보자]]></title>
            <link>https://velog.io/@anna_developer/%EC%9E%90%EC%A3%BC-%EB%B6%88%EB%9F%AC%EC%98%A4%EB%8A%94-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%8A%94-%EC%BA%90%EC%8B%B1%EC%9C%BC%EB%A1%9C-%EC%A1%B0%ED%9A%8C-%EC%84%B1%EB%8A%A5%EC%9D%84-%EB%86%92%EC%97%AC%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@anna_developer/%EC%9E%90%EC%A3%BC-%EB%B6%88%EB%9F%AC%EC%98%A4%EB%8A%94-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%8A%94-%EC%BA%90%EC%8B%B1%EC%9C%BC%EB%A1%9C-%EC%A1%B0%ED%9A%8C-%EC%84%B1%EB%8A%A5%EC%9D%84-%EB%86%92%EC%97%AC%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Wed, 18 Jan 2023 07:11:43 GMT</pubDate>
            <description><![CDATA[<p>우리는 API를 통해 조회한 데이터를 화면에 입혀 클라이언트에게 보여준다.</p>
<p>그런데 변경이 잘 되지 않으면서 자주 조회되는 데이터가 있다면 매번 api를 요청 할 때 마다 쿼리를 날리는 것이 서버에서는 부담이 될 수 있기에 조회 성능이 좋지 않다.</p>
<p>자주 사용하는 데이터는 캐싱을 통해 캐시저장소에 데이터를 저장 해 놓고 불러와 조회성능을 높여보자</p>
<blockquote>
<p>회사에서 대학교 평가시스템 업무를 담당하고 있다. 대학교 학과정보는 변동되지 않으므로, 학과정보를 캐시에 넣어 사용 해 보자.</p>
</blockquote>
<h3 id="1-로컬-캐시-라이브러리-ehcache">1. 로컬 캐시 라이브러리 ehcache</h3>
<p>ehcache는 로컬 캐시 라이브러리이다. </p>
<p>캐싱을 사용 할 디렉토리나 모듈의 build.gradle에</p>
<pre><code>implementation(&quot;org.ehcache:ehcache:3.9.5&quot;)</code></pre><p>를 추가하자</p>
<h3 id="2-cacheconfig">2. CacheConfig</h3>
<p>다음은 언제나 그랬듯 캐시 라이브러리를 사용하기 위한 설정파일이다.</p>
<pre><code>class CacheConfig

class CacheLogger : CacheEventListener&lt;Any, Any&gt; {

    private val log = LoggerFactory.getLogger(javaClass)

    override fun onEvent(cacheEvent: CacheEvent&lt;out Any, out Any&gt;) {
        log.info(&quot;Key: [${cacheEvent.key}] | EventType: [${cacheEvent.type}] | Old value: [${cacheEvent.oldValue}] | New value: [${cacheEvent.newValue}]&quot;)
    }
}</code></pre><p>정확히는 캐시 로그 파일이다. 캐싱 처리에 대한 모니터링을 할 수 있다.</p>
<p><code>@Configuration</code>을 통해 설정파일임을 알리고 <code>@EnableCaching</code> 어노테이션으로 캐싱을 사용할 수 있도록 한다. ApiAppication 클래스에 작성 해 줘도 된다.</p>
<h3 id="3-ehcachexml">3. ehcache.xml</h3>
<p>다음은 xml파일로 스프링에게 캐싱정보를 알려준다. 캐싱 할 이벤트와 저장시간, 저장공간을 명명 해 놓는 것으로 보인다. dept라는 이름으로 10분간 데이터를 임시 저장소에 저장 해 놓고 꺼내다 쓴다.
key-type 태그를 보니 api를 호출할 때 intercept해서 판별하는 것 같다.</p>
<pre><code>&lt;config
        xmlns:xsi=&#39;http://www.w3.org/2001/XMLSchema-instance&#39;
        xmlns=&#39;http://www.ehcache.org/v3&#39;
        xmlns:jsr107=&#39;http://www.ehcache.org/v3/jsr107&#39;&gt;
    &lt;cache-template name=&quot;default&quot;&gt;
        &lt;listeners&gt;
            &lt;listener&gt;
                &lt;class&gt;kr.co.~.config.CacheLogger&lt;/class&gt;
                &lt;event-firing-mode&gt;ASYNCHRONOUS&lt;/event-firing-mode&gt;
                &lt;event-ordering-mode&gt;UNORDERED&lt;/event-ordering-mode&gt;
                &lt;events-to-fire-on&gt;CREATED&lt;/events-to-fire-on&gt;
                &lt;events-to-fire-on&gt;UPDATED&lt;/events-to-fire-on&gt;
                &lt;events-to-fire-on&gt;EXPIRED&lt;/events-to-fire-on&gt;
                &lt;events-to-fire-on&gt;REMOVED&lt;/events-to-fire-on&gt;
                &lt;events-to-fire-on&gt;EVICTED&lt;/events-to-fire-on&gt;
            &lt;/listener&gt;
        &lt;/listeners&gt;

    &lt;/cache-template&gt;

        &lt;cache alias=&quot;dept&quot; uses-template=&quot;default&quot;&gt;
        &lt;key-type&gt;java.lang.String&lt;/key-type&gt;
        &lt;value-type&gt;java.util.ArrayList&lt;/value-type&gt;
        &lt;expiry&gt;
            &lt;ttl unit=&quot;minutes&quot;&gt;10&lt;/ttl&gt;
        &lt;/expiry&gt;
        &lt;resources&gt;
            &lt;heap&gt;100&lt;/heap&gt;
            &lt;offheap unit=&quot;MB&quot;&gt;1&lt;/offheap&gt;
        &lt;/resources&gt;
    &lt;/cache&gt;</code></pre><h3 id="4-service">4. Service</h3>
<p>다음은 서비스단에서 <code>@Cacheable</code> 어노테이션에 캐싱 할 이름을 정해주면 해당 정보가 조회될 때 미리 저장 된 캐시 저장소에서 이름으로 데이터를 찾는다. 아래 예시는 CQRS 개념에 따라 읽기 전용으로 만든 서비스이다. 대학교 정보로 해당 대학교에 소속 된 학과를 모두 찾는다.</p>
<pre><code>@Service
@Transactional(readOnly = true)
class SchoolInfoQueryService(
    private val deptRepository: DeptRepository,
) {

    @Cacheable(cacheNames = [&quot;dept&quot;])
    fun findDepts(collegeCode: String): List&lt;DeptOut&gt; {
        return deptRepository.getListByCollegeCode(collegeCode)
            .map { DeptOut.fromEntity(it) }
    }

}</code></pre><p>이제 위 서비스를 불러올 때 마다 매번 쿼리를 날리지 않고, 정해진 기한 안에서 캐싱 된 정보를 불러온다. </p>
<p>캐시를 사용하면 조회 성능이 높아진다는 장점이 있지만 단점으로는 업데이트가 일어나도 미리 캐싱된 정보를 가져오기 때문에 조회 타이밍이 맞지 않을 수 있다. 이 경우 서버를 다시 돌려야 했었다. 따라서 극단적으로 변경되지 않는 정보가 아니면 사용 할 경우가 많지는 않을 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring에 FCM 자동 푸시 서버 구현하기]]></title>
            <link>https://velog.io/@anna_developer/Spring%EC%97%90-FCM-%EC%9E%90%EB%8F%99-%ED%91%B8%EC%8B%9C-%EC%84%9C%EB%B2%84-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@anna_developer/Spring%EC%97%90-FCM-%EC%9E%90%EB%8F%99-%ED%91%B8%EC%8B%9C-%EC%84%9C%EB%B2%84-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 13 Jan 2023 14:01:57 GMT</pubDate>
            <description><![CDATA[<p>드디어!!!!! 따북에 정해진 시간마다 책 구절을 보내줄 서버를 구현할 수 있게 되었다!!!!</p>
<p>기쁜 마음으로 포스팅을 해본다~~ </p>
<blockquote>
<p>참고: <a href="https://gksdudrb922.tistory.com/192">https://gksdudrb922.tistory.com/192</a></p>
</blockquote>
<h3 id="1-bundlegradle">1. bundle.gradle</h3>
<pre><code>implementation(&quot;com.google.firebase:firebase-admin:7.1.0&quot;)</code></pre><p>스프링에서 firebase를 사용할 수 있도록 디펜던시를 추가 해 준다.</p>
<h3 id="2-scheduledconfig">2. ScheduledConfig</h3>
<p>다음은 설정파일이다.</p>
<pre><code>@Configuration
class ScheduledConfig {

    private val POOL_SIZE = 10

    fun scheduler(): TaskScheduler {
        val scheduler = ThreadPoolTaskScheduler()
        scheduler.poolSize = POOL_SIZE
        return scheduler
    }

}</code></pre><p>POOL_SIZE는 한 번에 처리할 수 있는 메세지 수라고 하는데 10개로 지정 해 두었다. 사용자가 그렇게 많지 않아서 상관 없을 것 같다^^ 나중에 사용자 수가 많아지면 늘려야지..</p>
<p>그리고 설정파일이므로 Configuration 어노테이션을 꼭 붙여줘야한다. 경험상 이 어노테이션이 없으면 작동하지 않았다.</p>
<h3 id="3-메세지-형식-notificationmessage">3. 메세지 형식 NotificationMessage</h3>
<pre><code>data class NotificationMessage(
     val title: String,
     val message: String
)</code></pre><p>역시나 data class로 구현했고 제목과 메세지를 담도록 했다. 찾아보니 파이어베이스에서 보낼 수 있는 메세지 형식이 있는데 제목과 본문이 필요했다.</p>
<h3 id="4-notificationscheduler">4. NotificationScheduler</h3>
<pre><code>@Service
class NotificationScheduler (

    private val bookCommandService: BookCommandService

) {

    private var instance: FirebaseMessaging? = null

    @PostConstruct
    @Throws(IOException::class)
    fun firebaseSetting() {
        val googleCredentials =
            GoogleCredentials.fromStream(ClassPathResource(&quot;firebase/어쩌구저쩌구.json&quot;).inputStream)
                .createScoped(listOf(&quot;https://www.googleapis.com/auth/firebase.messaging&quot;))
        val secondaryAppConfig = FirebaseOptions.builder()
            .setCredentials(googleCredentials)
            .build()
        val app = FirebaseApp.initializeApp(secondaryAppConfig)
        instance = FirebaseMessaging.getInstance(app)
    }

    fun getRandomMessage(): NotificationMessage {
        val title = &quot;${bookCommandService.getRandomBookNoAuth().title}_${bookCommandService.getRandomBookNoAuth().author}&quot;
        val message = bookCommandService.getRandomBookNoAuth().content
        return NotificationMessage(title, message)
    }

    @Scheduled(cron = &quot;0 0 08 * * ?&quot;)
    @Throws(FirebaseMessagingException::class)
    fun pushMorningAlarm() {
        pushAlarm(getRandomMessage())
    }

    @Scheduled(cron = &quot;0 0 13 * * ?&quot;)
    @Throws(FirebaseMessagingException::class)
    fun pushLunchAlarm() {
        pushAlarm(getRandomMessage())
    }

    @Scheduled(cron = &quot;0 00 19 * * ?&quot;)
    @Throws(FirebaseMessagingException::class)
    fun pushDinnerAlarm() {
        pushAlarm(getRandomMessage())
    }

    @Throws(FirebaseMessagingException::class)
    fun pushAlarm(data: NotificationMessage) {
        val message = getMessage(data)
        sendMessage(message)
    }

    fun getMessage(data: NotificationMessage): Message {
        val notification = Notification.builder().setTitle(data.title).setBody(data.message).build()
        val builder = Message.builder()
        val topic = &quot;토픽명&quot;
        return builder.setTopic(topic).setNotification(notification).build()
    }

    @Throws(FirebaseMessagingException::class)
    fun sendMessage(message: Message?): String? {
        return instance!!.send(message)
    }

}</code></pre><p>대망의 파이어베이스 서비스를 구동시킬 부분이다. 서비스이므로 Service 어노테이션을 붙여준다.</p>
<p>Firebase 비공개 키를 생성해야 한다.
<img src="https://velog.velcdn.com/images/anna_developer/post/d4419672-53d4-4618-9a00-924681f35b38/image.png" alt=""></p>
<p>위 이미지처럼 비공개 키 파일을 생성하는데 자바로 생성 해 준다.
다음은 원하는 디렉토리에 넣어주면 되는데, 나는 API 모듈의 resource부분에 디렉토리를 따로 만들어 넣어주었다.</p>
<p>알림을 보내는 방식은 토큰방식과 토픽방식이 있다고 하는데 나는 참고한 포스팅처럼 토픽방식을 채택했다.</p>
<p>토큰방식은 db에 사용자별 토큰을 저장하고, 그 값을 불러와 비교해야하는데 로직이 복잡해지므로 토픽방식을 추천한다.</p>
<p>파이어베이스 토픽은 앱이 구동될 때 생성되어 알림을 보낼 때 토픽을 체크해 해당 토픽을 가진 앱이 있으면 그 앱들에게 알림을 보내는 방식으로 구현된다.</p>
<p>따라서 안드로이드 스튜디오 앱이 켜질 때 onCreate 하위에</p>
<pre><code>FirebaseMessaging.getInstance().subscribeToTopic(&quot;토픽명&quot;);</code></pre><p>을 기재 해 줘야한다.</p>
<p>위에서부터 살펴보면 <code>GoogleCredentials.fromStream</code> 구문을 통해 파이어베이스에 접속하고</p>
<p><code>Scheduled</code> 어노테이션을 통해 주기적으로, 내가 cron 형식으로 정해 준 시간마다 특정 메서드를 실행하도록 한다. 물론 여기서는 푸시 알림을 보낸다.</p>
<p><code>getRandomMessage</code> 구절은 따북 프로젝트에서 랜덤으로 책 내용을 찾아주는 기능이므로 원하는 로직을 작성 해 주면 되겠다.</p>
<h3 id="5-apiapplication">5. ApiApplication</h3>
<pre><code>@EnableScheduling
@ConfigurationPropertiesScan
@SpringBootApplication(
    scanBasePackages = [&quot;kr.co.book.list.domain&quot;, &quot;kr.co.book.list.lib&quot;, &quot;kr.co.book.list.api&quot;]
)
class ApiApplication

fun main(args: Array&lt;String&gt;) {
    runApplication&lt;ApiApplication&gt;(*args)
}</code></pre><p>스프링 부트를 실행할 때 <code>@EnableScheduling</code> 어노테이션이 필요하다. 이 어노테이션이 붙어있어야 스케줄링이 가능해진다.</p>
<p><img src="https://velog.velcdn.com/images/anna_developer/post/14409a8b-5809-4de7-ac8f-9243703268a3/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/anna_developer/post/01122d5e-1bb1-4ac3-a3e5-307eaeee3e47/image.jpg" alt=""></p>
<p>이제 출근길 8시, 점심먹고 1시, 저녁먹고 7시에 다른 사람들이 작성한 예쁜 책 내용을 푸시 알림으로 받을 수 있다<del>~ 야호</del></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[안드로이드 FCM 푸시알림 보내기!]]></title>
            <link>https://velog.io/@anna_developer/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-FCM-%ED%91%B8%EC%8B%9C%EC%95%8C%EB%A6%BC-%EB%B3%B4%EB%82%B4%EA%B8%B0</link>
            <guid>https://velog.io/@anna_developer/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-FCM-%ED%91%B8%EC%8B%9C%EC%95%8C%EB%A6%BC-%EB%B3%B4%EB%82%B4%EA%B8%B0</guid>
            <pubDate>Fri, 13 Jan 2023 09:11:31 GMT</pubDate>
            <description><![CDATA[<p>내 작고 소중한 따북앱에 푸시알림을 보내보자!
가끔 공지사항을 수동으로 보내거나, 추후 서버단에 연결해서 지정된 시간마다 책 제목과 내용을 보내줄 수 있도록 커스텀 할 예정이다.</p>
<blockquote>
<p>참고: <a href="https://seopseop911.tistory.com/20">https://seopseop911.tistory.com/20</a>
<a href="https://kkh0977.tistory.com/716">https://kkh0977.tistory.com/716</a>
<a href="https://blog.naver.com/PostView.naver?blogId=ndb796&amp;logNo=221553341369&amp;redirect=Dlog&amp;widgetTypeCall=true&amp;directAccess=false">https://blog.naver.com/PostView.naver?blogId=ndb796&amp;logNo=221553341369&amp;redirect=Dlog&amp;widgetTypeCall=true&amp;directAccess=false</a></p>
</blockquote>
<h3 id="1-bundlegradle">1. bundle.gradle</h3>
<pre><code>    dependencies {
        classpath &#39;com.google.gms:google-services:4.3.2&#39;

    }

    implementation platform(&#39;com.google.firebase:firebase-bom:31.1.1&#39;)
    implementation &#39;com.google.firebase:firebase-analytics&#39;
    implementation &#39;com.google.firebase:firebase-messaging:17.0.0&#39;

    apply plugin: &#39;com.google.gms.google-services&#39;</code></pre><p>나는 위 5가지를 추가 해 줬는데 필요없는 implementation이 있을 수 있다,, 안드로이드는 익숙하지 않기에,,😂</p>
<h3 id="2-androidmanifestxml">2. AndroidManifest.xml</h3>
<pre><code>&lt;uses-permission android:name=&quot;android.permission.WAKE_LOCK&quot; /&gt;</code></pre><p>먼저 백그라운드에서 작동할 수 있도록 퍼미션을 등록 해 준 후</p>
<pre><code>        &lt;service android:name=&quot;.FirebaseMessagingService&quot;
            android:exported=&quot;true&quot;&gt;
            &lt;intent-filter&gt;

                &lt;action android:name=&quot;com.google.firebase.MESSAGING_EVENT&quot;/&gt;
            &lt;/intent-filter&gt;

        &lt;/service&gt;</code></pre><p>파이어베이스 서비스를 연동 해 준다.</p>
<h3 id="3-firebasemessagingservice">3. FirebaseMessagingService</h3>
<pre><code>public class FirebaseMessagingService extends com.google.firebase.messaging.FirebaseMessagingService {

    private static final String TAG = &quot;FirebaseMsgService&quot;;

    private String msg, title;


    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {

        title = remoteMessage.getNotification().getTitle();
        msg = remoteMessage.getNotification().getBody();

        Intent intent = new Intent(this,MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

        PendingIntent contentIntent = PendingIntent.getActivity(this,0,new Intent(this,MainActivity.class),0);

        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this).setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle(title)
                .setContentText(msg)
                .setAutoCancel(true)
                .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
                .setVibrate(new long[]{1,1000});

        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        notificationManager.notify(0,mBuilder.build());

        mBuilder.setContentIntent(contentIntent);

    }

}</code></pre><p>다음은 파이어베이스에서 서비스를 받아 푸시 알림을 보내줄 수 있도록 작성 해 준다.
파이어베이스에서 내 어플을 등록하는 과정은
<a href="https://seopseop911.tistory.com/20">여기</a>를 참조하면 자세히 알 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/anna_developer/post/5b37f957-e849-485f-b246-976e87209cd9/image.png" alt="">
<img src="https://velog.velcdn.com/images/anna_developer/post/609c7ede-086c-4cf8-9992-5b2a8deb96dd/image.png" alt="">
<img src="https://velog.velcdn.com/images/anna_developer/post/89737edf-530d-4a10-ae0f-ef49758e8bd8/image.png" alt="">
<img src="https://velog.velcdn.com/images/anna_developer/post/f3a09881-b828-4f43-bc87-5ae463ac2ba2/image.png" alt="">
<img src="https://velog.velcdn.com/images/anna_developer/post/fb427182-4b59-4a3a-9b9f-d09ffe5ee23e/image.png" alt="">
<img src="https://velog.velcdn.com/images/anna_developer/post/95a17771-7d98-442c-9f56-eafdc9c29b2a/image.png" alt=""></p>
<h3 id="4-메세지-보내기">4. 메세지 보내기</h3>
<p>이제 파이어베이스 홈페이지에서 테스트 메세지를 보내보자
<img src="https://velog.velcdn.com/images/anna_developer/post/b01c256d-8a0d-4863-b391-191f2af6d731/image.png" alt="">
<img src="https://velog.velcdn.com/images/anna_developer/post/1451c43e-d9ff-4499-9134-f897281a1301/image.png" alt="">
<img src="https://velog.velcdn.com/images/anna_developer/post/5dd27692-b2b7-45e2-a8a3-3afde89179df/image.png" alt="">
<img src="https://velog.velcdn.com/images/anna_developer/post/435798e2-9a27-4808-93eb-73bc5e19a71a/image.png" alt="">
<img src="https://velog.velcdn.com/images/anna_developer/post/4011d796-6cec-45cc-8d73-20055ce73a2c/image.png" alt=""></p>
<p>메세지 제목과 내용, 옵션을 순서대로 작성 해 주고 검토를 클릭하면</p>
<p><img src="https://velog.velcdn.com/images/anna_developer/post/60695608-e7ef-4306-8c75-af7c2b46f2f6/image.jpg" alt=""></p>
<p>이렇게 알림이 오게 된다~~</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[예쁜 alert창 SweetAlert을 알고 계시는가]]></title>
            <link>https://velog.io/@anna_developer/%EC%98%88%EC%81%9C-alert%EC%B0%BD-SweetAlert%EC%9D%84-%EC%95%8C%EA%B3%A0-%EA%B3%84%EC%8B%9C%EB%8A%94%EA%B0%80</link>
            <guid>https://velog.io/@anna_developer/%EC%98%88%EC%81%9C-alert%EC%B0%BD-SweetAlert%EC%9D%84-%EC%95%8C%EA%B3%A0-%EA%B3%84%EC%8B%9C%EB%8A%94%EA%B0%80</guid>
            <pubDate>Tue, 10 Jan 2023 08:21:09 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>참고: <a href="https://sweetalert2.github.io/">https://sweetalert2.github.io/</a></p>
</blockquote>
<p>우리가 일반적으로 생각하는 alert창은 너무나 못생겼다^^!
sweetAlert을 통해 예쁜 알랏창을 구현 해 보자</p>
<p>자세한 사용법은 SweetAlert2 공식 깃헙 홈페이지를 참고하면 아주아주 친절히 설명되어 있으므로, 여기서는 지난 포스팅인 <a href="https://velog.io/@anna_developer/Vue.js">ajaxWrapper처럼</a> 커스텀 하는 방법을 알아보자</p>
<pre><code>import Swal from &#39;sweetalert2&#39;

const swalButton = Swal.mixin({
  customClass: {
    confirmButton: &#39;버튼 스타일 클래스&#39;,
    cancelButton: &#39;버튼 스타일 클래스&#39;
  },
  buttonsStyling: false
})

const confirm = (
  icon,
  title,
  text,
  confirmButtonText = &#39;확인&#39;,
  showConfirmButton = true
) =&gt; {
  return swalButton.fire({
    icon,
    title,
    text,
    confirmButtonText,
    showConfirmButton,
    timer: showConfirmButton ? &#39;&#39; : 1500
  })
}
const sweetAlert = {
  success (title, text, showConfirmButton, buttonText) {
    return confirm(&#39;success&#39;, title, text, buttonText, showConfirmButton)
  },
  error (title, text, buttonText) {
    return confirm(&#39;error&#39;, title, text, buttonText)
  },
  warning (title, text, buttonText) {
    return confirm(&#39;warning&#39;, title, text, buttonText)
  },
  question (
    title,
    text,
    confirmButtonText = &#39;네&#39;,
    cancelButtonText = &#39;아니오&#39;
  ) {
    return swalButton.fire({
      icon: &#39;question&#39;,
      title,
      text,
      showCancelButton: true,
      confirmButtonText,
      cancelButtonText
    })
  }
}
export default sweetAlert
</code></pre><p>먼저 sweetAlert2에서 제공하는, 커스텀 할 수 있는 범위 내 속성들을 <code>const swalButton</code> 쪽에 초기화 해 준다.
Swal.mixin을 불러와 안에 여러가지 속성을 넣을 수 있다.</p>
<p>다음 <code>const confirm</code>에 화면에서 각종 인자들을 받아 Swal.fire라는 sweetAlert 내장 API로 예쁜 alert창을 출력할 수 있도록 해 준다.</p>
<p>이제 <code>sweetAlert</code> 이라는 이름으로 export해 주면 되는데 이 경우 성공, 실패, 경고, 질문(confirm창)로 구분해 sweetAler.success() 같은 방법으로 불러다 쓸 수 있다.</p>
<p>예시 코드와 화면은 다음과 같다!</p>
<blockquote>
<p>성공</p>
</blockquote>
<pre><code>sweetAlert.success(&#39;제목입니다.&#39;, &#39;내용입니다.&#39;, &#39;확인&#39;)</code></pre><p><img src="https://velog.velcdn.com/images/anna_developer/post/49e170d6-37ff-44ae-8bb5-d920dd531d80/image.png" alt=""></p>
<blockquote>
<p>실패</p>
</blockquote>
<pre><code>sweetAlert.error(&#39;제목입니다.&#39;, &#39;내용입니다.&#39;, &#39;확인&#39;)</code></pre><p><img src="https://velog.velcdn.com/images/anna_developer/post/5acd3936-c7ad-4537-bc5e-d316683c15c1/image.png" alt=""></p>
<blockquote>
<p>질문</p>
</blockquote>
<pre><code>sweetAlert.question(&#39;제목입니다.&#39;, &#39;질문입니다.&#39;, &#39;예&#39;, &#39;아니오&#39;)</code></pre><p><img src="https://velog.velcdn.com/images/anna_developer/post/cdaf78a1-7a5f-4ee5-a885-12dd150c7b85/image.png" alt=""></p>
<p>기본 창 외에도 버튼 3개짜리나 html을 직접 작성해서 넣는 등 다양한 sweetAlert이 공식 홈페이지에 있고, ajax처럼 then문을 통해 각 버튼 클릭 시 후처리도 가능하다.</p>
<p>각자 잘 커스텀 해서 사용 해 보자!</p>
]]></description>
        </item>
    </channel>
</rss>