<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Info-Tech</title>
    <link>https://info-tech.tistory.com/</link>
    <description>개인적인 블로그 공간으로 IT에 전반적인 기술설명들을 하는 블로그입니다.</description>
    <language>ko</language>
    <pubDate>Sat, 20 Jun 2026 02:57:48 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>개발 로그를 쌓고 싶은 블로거</managingEditor>
    <image>
      <title>Info-Tech</title>
      <url>https://tistory1.daumcdn.net/tistory/2961103/attach/b7228f7005504c328ecc9ecdd5e62caf</url>
      <link>https://info-tech.tistory.com</link>
    </image>
    <item>
      <title>[실전 예제로 보는 Race Condition] 중복 보상 지급, 어떻게 막을까?</title>
      <link>https://info-tech.tistory.com/44</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Race Condition이란?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Race Condition(경쟁 상태)&lt;/b&gt;은 둘 이상의 프로세스나 스레드가 동시에 &lt;b&gt;공통 자원에 접근&lt;/b&gt;할 때, 실행 순서에 따라 결과가 달라지는 상황입니다.&lt;/li&gt;
&lt;li&gt;쉽게 말해, &lt;b&gt;누가 먼저 처리하느냐의 '경쟁' 상태&lt;/b&gt;에서 발생하는 버그로, 특히 &lt;b&gt;데이터 무결성&lt;/b&gt;이 중요한 서비스에서 치명적일 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;REST API에서 Race Condition?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;REST API에서도 &lt;b&gt;동시 요청&lt;/b&gt;이 들어와 동일 리소스를 처리할 때, 순서가 꼬이면 &lt;b&gt;의도치 않은 결과&lt;/b&gt;가 발생할 수 있습니다.&lt;/li&gt;
&lt;li&gt;예) &lt;b&gt;중복 결제&lt;/b&gt;, &lt;b&gt;포인트 중복 지급&lt;/b&gt;, &lt;b&gt;중복 예약&lt;/b&gt; 등이 대표적인 사례입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실전 예시 &amp;ndash; 미션 완료 후 보상 지급&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;상황 설명&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 &lt;b&gt;미션 완료 후 보상 지급&lt;/b&gt; 요청을 보내는 API가 있습니다.&lt;br /&gt;정상적으로는 &lt;b&gt;1회만 지급&lt;/b&gt;돼야 하지만, &lt;b&gt;동시 요청&lt;/b&gt;이 발생하면 어떻게 될까요?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;API Endpoint (예시용)&lt;/b&gt;:&lt;br /&gt;POST /api/v1/missions/complete&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Request Body&lt;/b&gt;:&lt;br /&gt;{&quot;type&quot;:&quot;investment_complete&quot;}&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;서버 처리 흐름 (예시 코드)&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. API View&lt;/h4&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@router.post('/missions/complete', auth=AuthBearer())
def complete_mission(request, request_data: MissionCompleteRequest):
    user = user_repo.get_by_id(request.user_id)
    mission_service.complete(user, request_data.type)
    return {&quot;result&quot;: &quot;success&quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 비즈니스 로직&lt;/h4&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;def complete(self, user: User, mission_type: str):
    mission = self.get_mission_by_type(mission_type)
    
    if not mission:
        raise ValueError(&quot;미션 정보 없음&quot;)

    if self.is_already_completed(user.id, mission_type):
        raise ConflictError(&quot;이미 완료된 미션입니다.&quot;)

    with transaction.atomic():
        # 완료 기록 저장
        self.log_repository.add_completion(user.id, mission.id)

        # 보상 지급
        if mission.reward_type == &quot;point&quot;:
            user.add_points(mission.reward_amount)
        elif mission.reward_type == &quot;credit&quot;:
            self.credit_service.grant(user.id, mission.reward_amount)
        else:
            raise ValueError(&quot;알 수 없는 리워드 타입&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정상 요청 흐름&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;단일 요청&lt;/b&gt; 시 정상적으로 보상 지급 후 완료 처리됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;[Client]  &amp;rarr; POST /missions/complete  &amp;rarr; [Server] 200 OK
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 요청을 &lt;b&gt;다시 보내면&lt;/b&gt;, 이미 완료된 상태이므로 오류 반환:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;[Client]  &amp;rarr; POST /missions/complete  &amp;rarr; [Server] 409 Conflict (&quot;이미 완료된 미션입니다.&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;비정상 요청 흐름 (Race Condition 발생)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;악의적 사용자 또는 실수로&lt;/b&gt; &lt;b&gt;동시에 여러 요청&lt;/b&gt;을 보내면?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시나리오&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;[Client]  &amp;rarr; 30개의 동시 요청 발생
   &amp;darr;
[Server]  &amp;rarr; 중복 요청 모두 처리됨
   &amp;darr;
보상 30회 지급  
&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;테스트 스크립트 (예시)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 Python 스크립트를 통해 &lt;b&gt;동시 요청 테스트&lt;/b&gt;를 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;from concurrent.futures import ThreadPoolExecutor
import requests

def send_request():
    headers = {
        &quot;Authorization&quot;: &quot;Bearer &amp;lt;token&amp;gt;&quot;,
        &quot;Content-Type&quot;: &quot;application/json&quot;
    }
    payload = {&quot;type&quot;: &quot;investment_complete&quot;}
    response = requests.post(&quot;http://localhost:8000/api/v1/missions/complete&quot;, json=payload, headers=headers)
    print(f&quot;Response: {response.status_code}&quot;)

if __name__ == &quot;__main__&quot;:
    with ThreadPoolExecutor(max_workers=30) as executor:
        for _ in range(30):
            executor.submit(send_request)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요청 30회 중 &lt;b&gt;모두 200 OK 응답&lt;/b&gt;, 보상 30회 지급 &amp;rarr; Race Condition 발생.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Race Condition 방지법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Redis Lock 사용&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;분산 락 메커니즘&lt;/b&gt;을 통해 &lt;b&gt;하나의 요청만 처리&lt;/b&gt;하도록 제어합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import redis
import redis_lock

redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)

def complete(self, user: User, mission_type: str):
    lock = redis_lock.Lock(redis_client, f&quot;lock:mission:{user.id}:{mission_type}&quot;)
    
    with lock:
        if self.is_already_completed(user.id, mission_type):
            raise ConflictError(&quot;이미 완료됨&quot;)
        ...
&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 데이터베이스 락 (select_for_update)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;DB 수준에서 레코드 락&lt;/b&gt;을 걸어 &lt;b&gt;동시 처리 방지&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from django.db import transaction

def complete(self, user: User, mission_type: str):
    with transaction.atomic():
        user = User.objects.select_for_update().get(id=user.id)
        
        if self.is_already_completed(user.id, mission_type):
            raise ConflictError(&quot;이미 완료됨&quot;)
        ...
&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 단순 트랜잭션 (불충분할 수 있음)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;트랜잭션만 사용할 경우, &lt;b&gt;완전한 Race Condition 방지 어렵습니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from django.db import transaction

def complete(self, user: User, mission_type: str):
    with transaction.atomic():
        if self.is_already_completed(user.id, mission_type):
            raise ConflictError(&quot;이미 완료됨&quot;)
        ...
&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 서비스에서는 Race Condition으로 인해 &lt;b&gt;데이터 오류, 과금 문제&lt;/b&gt; 등이 발생할 수 있습니다.&lt;/li&gt;
&lt;li&gt;특히 &lt;b&gt;포인트/현금과 관련된 로직&lt;/b&gt;에서는 &lt;b&gt;반드시 동시성 처리&lt;/b&gt;를 고려해야 합니다.&lt;/li&gt;
&lt;li&gt;Redis 락, DB 락 등 상황에 맞는 방식을 선택해 &lt;b&gt;안정적인 서비스 운영&lt;/b&gt;을 해봅시다!&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  참고&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트 도구로는 Locust, JMeter, 간단한 Python 멀티스레드 활용 가능.&lt;/li&gt;
&lt;li&gt;Redis 락 구현 시, &lt;b&gt;락 타임아웃 설정&lt;/b&gt;도 함께 고려하면 더 안전합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  정리 포인트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Race Condition&lt;/b&gt;은 실제 서비스에서 빈번히 발생&lt;/li&gt;
&lt;li&gt;특히 &lt;b&gt;보상, 결제, 예약&lt;/b&gt; 등 &lt;b&gt;한정 리소스 처리&lt;/b&gt;에서 중요&lt;/li&gt;
&lt;li&gt;상황에 맞는 락 메커니즘으로 &lt;b&gt;미리 방지 &amp;rarr; 나중에 고통 줄이기!&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/파이썬 &amp;amp; 장고</category>
      <category>django</category>
      <category>Python</category>
      <category>race condition</category>
      <category>redis lock</category>
      <category>REST API</category>
      <category>멀티스레드</category>
      <category>백엔드 개발</category>
      <category>보안 취약점</category>
      <category>서버 개발</category>
      <category>트랜잭션</category>
      <author>개발 로그를 쌓고 싶은 블로거</author>
      <guid isPermaLink="true">https://info-tech.tistory.com/44</guid>
      <comments>https://info-tech.tistory.com/44#entry44comment</comments>
      <pubDate>Mon, 17 Mar 2025 14:54:43 +0900</pubDate>
    </item>
    <item>
      <title>검색엔진 도입기와 인간 뉴렐릭 시절 회고</title>
      <link>https://info-tech.tistory.com/43</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&amp;ndash; Elasticsearch, Haystack, 그리고 htop으로 버텨낸 그 시절 이야기&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;시작하며&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;지금이야&amp;nbsp;Sentry,&amp;nbsp;Datadog,&amp;nbsp;CloudWatch&amp;nbsp;같은&amp;nbsp;모니터링&amp;nbsp;툴&amp;nbsp;없으면&amp;nbsp;불안해서&amp;nbsp;서버&amp;nbsp;못&amp;nbsp;만지지만,&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;그때는 2017년&lt;/b&gt;. 그런 거 잘 안 쓰던 시절이었다. 서버 상태는 감으로 느끼고, 장애는 몸으로 겪었다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그&amp;nbsp;시절,&amp;nbsp;나는&amp;nbsp;모니터링&amp;nbsp;시스템이자&amp;nbsp;에러&amp;nbsp;알림&amp;nbsp;툴,&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그야말로&amp;nbsp;인간&amp;nbsp;뉴렐릭이었다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이&amp;nbsp;글은&amp;nbsp;내가&amp;nbsp;검색&amp;nbsp;성능&amp;nbsp;때문에&amp;nbsp;Elasticsearch를&amp;nbsp;도입하면서&amp;nbsp;겪은&amp;nbsp;삽질과,&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;서버가 터질 때마다 htop으로 위기 넘긴 &lt;b&gt;그 시절, 2017년 &lt;/b&gt;이야기다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;검색 성능 터지기 직전&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;참고로 당시 내가 담당했던 서비스는 해당 &lt;a href=&quot;https://blog.naver.com/alsldl31/220906639087&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;블로그&lt;/a&gt; 에서 확인이 가능하다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;지금은&amp;nbsp;사라졌지만,&amp;nbsp;그&amp;nbsp;시절엔&amp;nbsp;실제&amp;nbsp;사용자도&amp;nbsp;많았고&amp;nbsp;트래픽도&amp;nbsp;꽤&amp;nbsp;됐다.&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;상품도 100만 건 넘게 등록돼 있어서, &lt;b&gt;검색 한 번 누르면 서버가 한숨 쉬는&lt;/b&gt;&amp;nbsp;상황이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;당시 운영하던 서비스에서 &lt;b&gt;상품 수가 100만 건&lt;/b&gt;을 넘기면서 검색이 슬슬 버벅대기 시작했다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;상품 검색 속도가 눈에 띄게 느려졌고,&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;연관 검색은 아예 못 보여주고 있었음.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;예를 들어, 유저가 &quot;빨간 원피스&quot; 검색하면&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;red, 빨강, 붉은 등도 함께 검색돼야 했는데,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;ORM으로 OR 조건 조합해서 쿼리 짜는 건 한계가 있었고,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;속도도 느리고 검색 결과도 부정확했다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그때 처음으로 &lt;b&gt;&quot;이건 ORM으로 해결할 게 아니다&quot;&lt;/b&gt; 라는 걸 느꼈다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;검색엔진을 알아보다&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;검색 성능 이슈를 해결하려고 &lt;b&gt;검색엔진 도입&lt;/b&gt;을 고민했다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;지금처럼 Elasticsearch가 당연한 선택지는 아니었고,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그냥 검색엔진이라는 단어조차 익숙하지 않았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;당시 알아봤던 건 대충 이 3가지였다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;1. elasticsearch-py&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;공식 Python 클라이언트인데, &lt;b&gt;너무 로우레벨&lt;/b&gt;이라&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;직접 쿼리 짜고 통신 처리해야 해서 조금 버거웠다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;2. AWS Elasticsearch&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;지금은 OpenSearch로 바뀌었지만,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그때는 &lt;b&gt;비용이 부담스럽기도 했고&lt;/b&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;기능 제약&lt;/b&gt;도 있어서 패스.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;3. Django Haystack + Elasticsearch&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이건 &lt;b&gt;Django ORM처럼&lt;/b&gt; 검색을 다룰 수 있어서&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;처음 도입할 때 편할 것 같았다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;결국 Haystack 선택&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;결국 &lt;b&gt;Haystack&lt;/b&gt;을 선택했다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;검색엔진 경험도 없었고, 일단 익숙한 Django 스타일로&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;시작하는 게 맞다고 판단했다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Django 모델인 Product 기반으로&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;search_indexes.py에 인덱스 정의&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Elasticsearch랑 연동해서 검색 처리&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;여기까지는 괜찮았다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;문제는 인덱싱 작업에서 터졌다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;rebuild_index, 그리고 서버 다운&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Haystack에는 rebuild_index라는 명령어가 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;검색 인덱스를 밀고 새로 쌓는 작업&lt;/b&gt;이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;쉽게 말하면 기존 걸 다 날리고,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;전체 데이터를 로드해서 다시 Elasticsearch에 보내는 작업&lt;/b&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;문제는, 이걸 돌리자마자 &lt;b&gt;서버가 터졌다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;전체 데이터를 메모리에 올리면서 &lt;b&gt;OOM(Out of Memory)&lt;/b&gt; 발생&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;DB I/O도 많고, Elasticsearch로 &lt;b&gt;bulk 요청&lt;/b&gt;이 미친 듯이 나감&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;근데 문제는 이걸 &lt;b&gt;서버랑 Elasticsearch가 같은 EC2 인스턴스에서 돌리고 있었다는 거&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;결국 &lt;b&gt;서버 자체가 멈춤.&lt;/b&gt; SSH 접속도 안 됨. 공포.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;모니터링은 htop으로&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;당시엔 Sentry도 없고, CloudWatch 알람 같은 것도 안 썼다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;서버 이상하면 그냥 &lt;b&gt;직접 SSH 접속해서 htop&lt;/b&gt; 켰다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ssh ubuntu@서버아이피
htop
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;딱 보면 메모리, CPU 사용률 다 나옴.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;CPU: 98%
MEM: 91%
Swap: 40%
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Elasticsearch가 메모리를 다 먹고 있었음.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;바로 F9 &amp;rarr; 9 (Kill)로 죽이고 다시 띄움.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이게 그때 내가 하던 &lt;b&gt;모니터링의 전부&lt;/b&gt;였다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그래도 해결은 해야 하니까&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;서버 죽어나가니까 방법을 찾아야 했다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;찾아보니 rebuild_index 명령어에&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;--batch-size 옵션이 있다는 걸 발견&lt;/b&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이걸로 &lt;b&gt;한 번에 인덱싱하는 양을 줄이니까&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;서버가 버틸 수 있었고, 시간은 오래 걸렸지만&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;어쨌든 &lt;b&gt;서버 다운 없이 인덱싱 성공&lt;/b&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;지금 생각하면&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;지금은 Datadog, Sentry, CloudWatch 알람으로&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;서버 상태 실시간으로 확인하고,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;에러는 Slack으로 실시간 알림 받는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;근데 그때는 진짜 &lt;b&gt;나 자신이 모니터링 툴&lt;/b&gt;이었다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;서버 이상한지 감으로 느끼고, htop으로 실시간 확인하고,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;직접 문제 찾아서 고치던 그 시절.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;정리&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그때 경험하면서 느낀 건:&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;검색엔진 도입은 단순히 라이브러리 추가하는 게 아니라 구조 설계까지 고려해야 한다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;서버 자원, 인덱싱 전략, 데이터 양 다 포함해서 계획해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그리고 진짜 &lt;b&gt;모니터링 툴은 필수&lt;/b&gt;다. 감으로는 못 버틴다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;마무리&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;지금 돌아보면 진짜 고생했지만,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;덕분에 실전에서 많이 배운 프로젝트였다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그리고 서버가 터질 때마다&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;htop과 함께 했던 그 시절,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;나는 진짜로 &lt;b&gt;인간 뉴렐릭&lt;/b&gt;이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>서버이야기/회고</category>
      <category>ElasticSearch</category>
      <category>haystack</category>
      <category>개발회고</category>
      <category>서버모니터링</category>
      <author>개발 로그를 쌓고 싶은 블로거</author>
      <guid isPermaLink="true">https://info-tech.tistory.com/43</guid>
      <comments>https://info-tech.tistory.com/43#entry43comment</comments>
      <pubDate>Sun, 16 Mar 2025 17:55:01 +0900</pubDate>
    </item>
    <item>
      <title>개발자가 비즈니스적 고민도 필요할까?(2)</title>
      <link>https://info-tech.tistory.com/41</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;나무를 넘어서 숲을 봐야하는 이유&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거에 필자는 코드를 보다 깔끔하고 우아하게 작성하려는 노력의 일환으로, DDD(도메인 주도 설계)와 클린 아키텍처(Clean Architecture)에 관한 자료들을 크롬 탭에 수십 개 열어놓고, 단 한 줄의 코드를 작성하기 위해 몇 시간을 할애한 적이 있다. 이 글을 읽는 많은 개발자분들 역시 필자와 유사한 경험을 하셨을 것이라 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필자는 깔끔하고 완벽한 코드를 작성하기 위한 노력하다가 프로젝트를 기한 내에 완수하지 못하게 한 경험이 있다. 이러한 경험을 통해, 단순히 &lt;i&gt;&lt;b&gt;&lt;u&gt;코딩(나무)&lt;/u&gt;&lt;/b&gt;&lt;/i&gt;에만 집중하는 것이 아니라, &lt;i&gt;&lt;b&gt;&lt;u&gt;코딩+&amp;alpha;(숲&lt;/u&gt;&lt;/b&gt;&lt;u&gt;&lt;b&gt;)&lt;/b&gt;&lt;/u&gt;&lt;/i&gt;를 고려하는 관점에서 사고하는 것이 중요함을 깨달았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;개발자로서 앞으로 발생할 수 있는 상황을 고려하여 유지보수가 쉽고 확장성 있는 설계를 기반으로 코딩하는 것은 중요하다. 하지만, 숲을 보는 관점에서 벗어나 코딩 중심적인 관점만을 생각하기보다는 비즈니스 및 기술적 요구 사항의 중요성을 인식하고, 균형 있는 우선순위를 설정하는 것이 더욱 중요하다고 생각된다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러면 나무만 보고 숲을 못봤을 때 어떤 문제가 뒤따를까? 예시로 영화&quot;콰이강의 다리&quot;를 통해서 확인해보자.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;영화 &quot;콰이강의 다리&quot; 줄거리&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;제 2차 세계 대전 당시, 영국군 장교인 &lt;span style=&quot;color: #006dd7;&quot;&gt;니콜슨 대령&lt;/span&gt;과 그의 부하들은 &lt;span style=&quot;color: #ee2323;&quot;&gt;일본군&lt;/span&gt;에게 포로로 잡혀 콰이강 근처의 포로 수용소로 끌려가게 되었다.&lt;br /&gt;&lt;br /&gt;포로 수용소에서 일본군 지휘관인 사이토 대령은 포로들에게 콰이강을 가로지르는 철도 다리를 만들라는 명령을 내렸고, 처음에는 명령에 불복했지만, 니콜슨 대령은 부하들의 사기와 영국군의 명예를 위해 다리 건설에 동의하게 되었다.&lt;br /&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;니콜슨&lt;/span&gt;은 다리가 &lt;u&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;일본군&lt;/span&gt;에게 전략적으로 유리하게 작용할 수 있다는 사실을 알면서도 다리를 완성하는 데에만 집중&lt;/u&gt;했다.&lt;br /&gt;&lt;br /&gt;얼마 후, &lt;span style=&quot;color: #006dd7;&quot;&gt;미국 특공대&lt;/span&gt;가 포로들을 구하기 위해 콰이강에 진입했고, 특공대는 다리에 폭탄을 설치했지만, 다리에 강한 집착을 보이던 &lt;span style=&quot;color: #006dd7;&quot;&gt;니콜슨 대령&lt;/span&gt;은 판단력을 잃어버리고 &lt;span style=&quot;color: #006dd7;&quot;&gt;특공대원&lt;/span&gt;들을 막으려 했다. 그러나 결국 특공대는 &lt;span style=&quot;color: #ee2323;&quot;&gt;일본군&lt;/span&gt;에게 발각되어 모두 사살되었고, 니콜슨 대령은 포격에 의한 파편을 맞고 다리에 설치된 기폭제 위로 쓰러지면서 다리를 폭파시켰다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;272&quot; data-origin-height=&quot;185&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ec2LkE/btsFgKDzGOA/8EkoOvkZXBgSz5SNbVJgd1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ec2LkE/btsFgKDzGOA/8EkoOvkZXBgSz5SNbVJgd1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ec2LkE/btsFgKDzGOA/8EkoOvkZXBgSz5SNbVJgd1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fec2LkE%2FbtsFgKDzGOA%2F8EkoOvkZXBgSz5SNbVJgd1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;575&quot; height=&quot;391&quot; data-origin-width=&quot;272&quot; data-origin-height=&quot;185&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&quot;콰이강의 다리 실수&quot; (a.k.a 숲을 지나치고 나무만 본 경우)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;콰이강의 다리&quot; 문제에서, 니콜슨은 다리 건설이라는 구체적인 과제에만 몰두하여, 그것이 전쟁의 전체적인 맥락에서 어떤 의미를 가지는지를 잊어버린다. 다시말해, 그는 자신의 임무와 기술적인 도전에 성공하는 것에만 집중하다가 그의 노력이 실제로는 적에게 이익을 주고 있다는 사실을 간과하게 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;나무 = 다리 건설이라는 구체적인 과제에만 몰두&lt;br /&gt;숲 = 다리가 전쟁의 전체적인 맥락에서 어떤 의미를 가지는지를 잊어버림&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;이러한 상황은 소프트웨어 개발에서도 발생할 수 있다. 개발자나 팀이 프로젝트의 본래 목적이나 목표를 잊고, 기술적인 도전이나 복잡성에 매몰되어 프로젝트가 본래 해결하려고 했던 문제를 간과하게 되는 경우이다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;예를 들면 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;1. 목표에서 벗어남&lt;/b&gt;: 개발자가 프로젝트의 궁극적인 목표나 사용자의 필요를 무시하고, 기술적인 완성도나 개인적인 성취에만 집중하는 경우&lt;br /&gt;&lt;b&gt;2. Over-engineering&lt;/b&gt;: 프로젝트에 필요 이상으로 복잡하거나 고급 기술을 사용하여, 결과적으로 유지보수나 확장성에 문제를 일으키는 경우&lt;br /&gt;&lt;b&gt;3. 프로젝트 관리 실패&lt;/b&gt;: 명확한 우선순위나 목표 설정 없이 프로젝트를 진행하며, 자원이나 시간을 낭비하는 경우&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 문제는 기술적인 성취에만 초점을 맞추고, 그 과정에서 본래의 목적이나 더 큰 가치를 간과하는 상황을 의미한다. 이는 개발자 뿐만 아니라 다양한 분야에서 일하는 사람들이 경계해야 할 일반적인 함정이다. 프로젝트의 비즈니스적 가치와 궁극적인 목적에 대한 인식을 잃지 않기 위해서는, 개발자들이 정기적으로 자신의 작업을 평가하고 전체적인 관점에서 프로젝트를 바라볼 필요가 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;숲을 보기 위한 자신만의 원칙을 세워보자.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나무도 중요하지만, 전체적인 숲을 보기 위한 원칙을 하나쯤 만들면 어떨까? 이미 무수한 방법들이 있지만, 필자는 아래와 같은 방법들로 숲을 보기 위해 노력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;1. &lt;b&gt;정기적인 목표 재평가&lt;/b&gt;: 프로젝트의 진행 상황을 정기적으로 검토하고, 목표가 여전히 유효한지, 혹은 조정이 필요한지 평가한다.&lt;br /&gt;&lt;br /&gt;2. &lt;b&gt;사용자 중심 설계&lt;/b&gt;: 사용자의 필요와 경험을 중심으로 생각하며, 기술적인 결정을 내린다.&lt;br /&gt;&lt;br /&gt;3. &lt;b&gt;팀 내 커뮤니케이션 강화&lt;/b&gt;: 다양한 배경을 가진 팀원들과의 지속적인 소통을 통해 다양한 관점을 수렴하고, 프로젝트의 목표에 대한 공통된 이해를 구축한다.&lt;br /&gt;&lt;br /&gt;4. &lt;b&gt;단계별 목표 설정&lt;/b&gt;: 큰 목표를 달성하기 위한 구체적이고 달성 가능한 단계별 목표를 설정하여, 전체적인 목표에서 벗어나지 않도록 한다.&lt;br /&gt;&lt;br /&gt;5. &lt;b&gt;피드백 반영&lt;/b&gt;: 사용자와 이해관계자로부터의 피드백을 정기적으로 수집하고 반영하여, 프로젝트가 올바른 방향으로 나아가고 있는지 확확인한다. 이러한 접근 방식을 통해 개발자는 기술적인 세부 사항에만 몰두하는 것이 아니라, 프로젝트의 더 큰 비즈니스적 가치와 목적을 지속적으로 염두한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 접근 방식을 통해 필자는 기술적인 세부 사항에만 몰두하는 것이 아니라, 프로젝트의 더 큰 비즈니스적 가치와 목적을 지속적으로  생각는 습관을 만들기 위해 노력한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;마치며&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;나무를 보지 말고 숲을 봐야 한다&quot;는 교훈은 개발자와 더불어 프로젝트를 같이 진행하는 모두에게 중요하다. 우리가 작업하는 개별적인 요소나 기술적인 세부 사항에만 집중하는 대신, 프로젝트의 궁극적인 목적과 사용자의 필요를 계속 염두에 두어야 한다는 것을 상기시켜야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트가 전체적으로 어떤 비즈니스적 가치를 창출하는지, 그리고 그것이 사용자나 사회에 어떤 영향을 미치는지를 이해하는 것이 중요하다. 이러한 관점은 더 효율적이고 사용자 중심적인 비즈니스를 개발하는 데 도움이 될 것이라 확신한다.&lt;/p&gt;</description>
      <category>에세이</category>
      <category>개발자</category>
      <category>개발자와 비즈니스 관계</category>
      <category>개발자의 마인드</category>
      <category>비즈니스</category>
      <category>비즈니스적 관점에서 생각하는 개발자</category>
      <category>숲을 바라보는 개발자</category>
      <author>개발 로그를 쌓고 싶은 블로거</author>
      <guid isPermaLink="true">https://info-tech.tistory.com/41</guid>
      <comments>https://info-tech.tistory.com/41#entry41comment</comments>
      <pubDate>Sat, 24 Feb 2024 20:18:31 +0900</pubDate>
    </item>
    <item>
      <title>개발자가 비즈니스적 고민도 필요할까? (1)</title>
      <link>https://info-tech.tistory.com/40</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;당신은 방망이 깎는 노인이신가요?&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수필 &lt;b&gt;&quot;방망이 깎는 노인&quot;&lt;/b&gt;의 스토리는 대략적으로 이렇다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;동대문 맞은편 길가에 앉아서 방망이를 깎아 파는 노인이 있었다. &lt;br /&gt;방망이를 한 벌 사 가지고 가려고 깎아 달라고 부탁을 했다. 값을 굉장히 비싸게 부르는 것 같았다. &lt;br /&gt;&lt;br /&gt;나: &lt;span style=&quot;color: #0593d3;&quot;&gt;&quot;좀 싸게 해 줄 수 없습니까?&quot;&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;노인: &lt;span style=&quot;color: #0593d3;&quot;&gt;&quot;방망이 하나 가지고 에누리하겠소? 비싸거든 다른 데 가 사우.&quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;대단히 무뚝뚝한 노인이었다. 값을 흥정하지도 못하고 잘 깎아나 달라고만 부탁했다. 그는 잠자코 열심히 깎고 있었다. &lt;br /&gt;처음에는 빨리 깎는 것 같더니, 저물도록 이리 돌려 보고 저리 돌려 보고 굼뜨기 시작하더니, 마냥 늑장이다. &lt;br /&gt;내가 보기에는 그만하면 다 됐는데, 자꾸만 더 깎고 있었다. 인제 다 됐으니 그냥 달라고 해도 통 못 들은 척 대꾸가 없다. &lt;br /&gt;타야 할 차 시간이 빠듯해 왔다. 갑갑하고 지루하고 초조할 지경이었다. &lt;br /&gt;&lt;br /&gt;나: &lt;span style=&quot;color: #0593d3;&quot;&gt;&quot;더 깎지 않아도 좋으니 그만 주십시오.&quot;&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;노인: &lt;span style=&quot;color: #0593d3;&quot;&gt;&quot;끓을 만큼 끓어야 밥이 되지, 생쌀이 재촉한다고 밥이 되나.&quot;&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;나: &lt;span style=&quot;color: #0593d3;&quot;&gt;&quot;살 사람이 좋다는데 무얼 더 깎는다는 말이오? 노인장, 외고집이시구먼. 차시간이 없다니까요.&quot;&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;노인: &lt;span style=&quot;color: #0593d3;&quot;&gt;&quot;다른 데 가서 사우. 난 안 팔겠소.&quot;&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;나: &lt;span style=&quot;color: #0593d3;&quot;&gt;&quot;그럼, 마음대로 깎아 보시오.&quot;&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;노인: &lt;span style=&quot;color: #0593d3;&quot;&gt;&quot;글쎄, 재촉을 하면 점점 거칠고 늦어진다니까. 물건이란 제대로 만들어야지, 깎다가 놓치면 되나.&quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;.. 중략 .. 어쨌든 기다려서 방망이를 샀고 집에 가져갔더니 잘 만들었다 카더라 라는 내용&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;425&quot; data-origin-height=&quot;359&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m5qVQ/btsELqkcZGW/Lh4ZSjMAQFsuf61zETHai1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m5qVQ/btsELqkcZGW/Lh4ZSjMAQFsuf61zETHai1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m5qVQ/btsELqkcZGW/Lh4ZSjMAQFsuf61zETHai1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm5qVQ%2FbtsELqkcZGW%2FLh4ZSjMAQFsuf61zETHai1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;425&quot; height=&quot;359&quot; data-origin-width=&quot;425&quot; data-origin-height=&quot;359&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 내용은 이렇다. 고객이 물건을 사야하는데 장인정신으로 제품을 만드는 노인으로 인해 시간이 지체되었고, 오랜 기다림을 통해 얻은 제품(방망이)은 매우 만족스러웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 수필을 지은 작가의 교훈은 '&lt;b&gt;무슨 일이든, 장인정신(=자기만의 확고한 신념)을 가지고 한다면 좋은 결과를 얻을 것이다&lt;/b&gt;' 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;i&gt;&lt;b&gt;만약, 개발자가 방망이를 깎는 노인이라면?&amp;nbsp;&lt;/b&gt;&lt;/i&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;서울의 한 사무실 좋은 의자에 앉아서 개발만 주구장장 하는 개발자가 있었다. &lt;br /&gt;&lt;br /&gt;고객:&lt;span style=&quot;color: #0593d3;&quot;&gt; &quot;그 기능 언제 출시하시나요?&quot;&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;개발자: &lt;span style=&quot;color: #0593d3;&quot;&gt;&quot;기능 하나 가지고 배포하겠소? 정교한 코드설계를 하는중이에요. 기다리세요&quot;&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;대단히 무뚝뚝한 개발자였다. 그는 잠자코 열심히 개발하고 있었다. 처음에는 빨리 코딩하는 것 같더니, 저물도록 이리 돌려 보고 저리 돌려 보고 굼뜨기 시작하더니, 마냥 늑장이다. 내가 보기에는 그만하면 다 됐는데, 자꾸만 더 개발하고 있었다. &lt;br /&gt;인제 다 됐으니 그냥 출시하라고 해도 통 못 들은 척 대꾸가 없다. 갑갑하고 지루하고 초조할 지경이었다. &lt;br /&gt;&lt;br /&gt;고객: &lt;span style=&quot;color: #0593d3;&quot;&gt;&quot;더 하지 않아도 좋으니 그만 주십시오.&quot;&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;개발자:&lt;span style=&quot;color: #0593d3;&quot;&gt; &quot;끓을 만큼 끓어야 밥이 되지, 생쌀이 재촉한다고 밥이 되나.&quot;&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;고객: &lt;span style=&quot;color: #0593d3;&quot;&gt;&quot;살 사람이 좋다는데 무얼 더 개발한다는 말이오? 개발자님, 외고집이시구먼. 시간이 없다니까요.&quot;&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;개발자: &lt;span style=&quot;color: #0593d3;&quot;&gt;&quot;글쎄, 재촉을 하면 점점 거칠고 늦어진다니까. 물건이란 제대로 만들어야지, 깎다가 놓치면 되나.&quot;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장인정신&lt;/b&gt;은 개발자에게 필요한 중요한 덕목이다. 정확한 설계를 통해 코딩을 하고, 레거시 코드를 줄이며 제품의 코드 품질을 향상시키는 것은 매우 중요하다. 이는 빠른 개발 속도를 유지하고 다른 제품을 개발 할때 확장을 위한 도움을 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 위와 같이 개발자가 장인정신으로 방망이만 깎고 있다면 현실에서는 잠재 고객을 잃게 될 것이다. 현실 속 고객은 인내심이 길지 않기 때문이다. 개발자가 정성스럽게 설계한 제품을 이해해주지 않을 뿐더러 아마도 옆 업체에서 먼저 만든 비슷한 방망이를 구매해서 쓸 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;그렇다면&amp;nbsp; 장인정신을 포기해야 할까?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마치 위의 글에서는 고객의 만족이나 사업의 성장을 위해 장인정신을 포기하라는 것처럼 들릴 수 있다. 하지만 정확히는 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 좋은 장인이란, 개발자로서의 역할을 충실히 하고 고객도 생각하는 비즈니스 마인드를 포함 해야한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;비즈니스적으로 생각을 한다는것 = 고객의 관점에서 생각해보는 것&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;개발자는 개발만 잘하면 되는거 아니에요?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;네, 아닙니다.&amp;nbsp;&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;라고 답변해주고 싶다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 회사에 소속되어 개발을 하는 이유는 우리가 지닌 개발 역량을 통해 제품을 만들고, 이익을 창출 하는 것이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위에서 말한 장인정신이 투철한 노인은 단순히 물건을 만들기만 할 뿐 제때 못팔 수 있다. 이러한 개발자가 되지 않기 위해 개발능력 이외에도 사용자 만족도나 회사의 비즈니스 목표 등 여러 요소를 고려해야 한다. 따라서 회사에는 단순히 프로그래밍이 뛰어난 개발자보다 프로그래밍 능력을 통해 고객을 만족시키고, 이로 인해 발생하는 매출을 창출 할 수 있는 개발자가 필요하다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;이러한 이유로 많은 회사에서는 개발적 역량 외에도 협업 능력이나 커뮤니케이션, 오너십, 리더십 등을 개발자에게 요구한다. 어떻게 보면 당연한 얘기들이다. 이런 능력들은 회사에 당장 수익을 창출하지 않더라도, 장기적인 관점에서 회사의 문화와 조직의 전반적인 성과에 긍정적인 영향을 미치기 때문이다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;결국, 개발자는 자신이 속한 조직의 제품이 시장에서 성공을 거두기 위해 노력하는 많은 팀원 중 하나이다. 개발자는 프로그래밍 기술뿐만 아니라, 조직의 비즈니스 목표와 전략을 이해하고, &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;전체 조직의 성공을 위해 다방면으로 기여할 수 있는 넓은 시야를 갖출 필요가 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;비즈니스 관점을 지닌 방망이 깎는 개발자&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;보통 개발자라면, 고객이나 회사의 필요에 맞춰 빠르게 개발하고 배포하는 경험을 한 번쯤 해봤을 것이다. 특히 스타트업에서는 신속한 시도와 실패를 통해 학습하는 문화가 자리 잡고 있는 경우가 많다. 이러한 환경에서 개발자는 자주 고민에 빠지게 되는데, 시장의 요구 사항을 빠르게 파악하기 위해 신속하게 개발하고 배포해야 할지, 아니면 시간을 들여 안정적인 설계를 통해 추후 확장성을 고려해야 할지 기로에 선다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 아래처럼 급박한 비즈니스 이슈가 있는 상황에서는 어떻게 대처해야 할까?&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;교토의 한 사무실에 있는 회사는 운영 자금이 바닥나가는 상황에 직면해있다.&lt;br /&gt;&lt;br /&gt;A: &lt;span style=&quot;color: #0593d3;&quot;&gt;&quot;그 기능 언제 출시되나요?&quot;&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;개발자: &lt;span style=&quot;color: #0593d3;&quot;&gt;&quot;좋은 설계를 위해 노력 중이니, 조금만 기다려주세요.&quot;&lt;br /&gt;&lt;/span&gt; &lt;br /&gt;개발자는 훌륭한 코드 작성을 위해 세심하게 설계 작업을 진행했다. 하지만 빠른 기능 출시로 시장 반응을 테스트해야 하는 A는 점점 더 조급해졌다.&lt;br /&gt;&lt;br /&gt;A:&lt;span style=&quot;color: #0593d3;&quot;&gt; &quot;우리 회사 상황을 고려할 때, 이 기능이 급히 필요합니다. 무한정 기다릴 수는 없어요.&quot;&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;개발자는 심사숙고 끝에 고객이 요구하는 기능 배포를 우선시해야 한다는 사실을 깨달았습니다.&lt;br /&gt;&lt;br /&gt;개발자:&lt;span style=&quot;color: #0593d3;&quot;&gt; &quot;알겠습니다. 최대한 빨리 마무리 지어 배포하도록 하겠습니다.&quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;A: &lt;span style=&quot;color: #0593d3;&quot;&gt;&quot;바로 피드백을 반영해주셔서 정말 감사합니다.&quot;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;개발자는 자신이 속한 회사의 현재 상황이나 고객의 요구사항에 맞는 비즈니스 전략을 우선순위로 삼아야 한다. 위와 같은 상황에서는 신속한 출시를 통한 시장 검증이 회사의 성장을 지원하는 최선의 방법이 될 수 있다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>에세이</category>
      <category>개발자에세이</category>
      <category>개발자와 비즈니스</category>
      <category>비즈니스</category>
      <category>비즈니스적 관점에서 생각하는 개발자 #개발자 마인드</category>
      <category>에세이</category>
      <author>개발 로그를 쌓고 싶은 블로거</author>
      <guid isPermaLink="true">https://info-tech.tistory.com/40</guid>
      <comments>https://info-tech.tistory.com/40#entry40comment</comments>
      <pubDate>Mon, 12 Feb 2024 02:05:28 +0900</pubDate>
    </item>
    <item>
      <title>GoogleNet, DenseNet, VGG, ResNet을 활용하여 무궁화, 장미의 이미지를 분류하는 실습</title>
      <link>https://info-tech.tistory.com/39</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;대학원 학부 수업중, 여러가지 딥러닝을 활용하여 이미지를 분류하는 실습과제 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 영상으로 설명을 대체합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=esc6orXnST8&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=esc6orXnST8&lt;/a&gt;&lt;/p&gt;</description>
      <category>인공지능/공부</category>
      <author>개발 로그를 쌓고 싶은 블로거</author>
      <guid isPermaLink="true">https://info-tech.tistory.com/39</guid>
      <comments>https://info-tech.tistory.com/39#entry39comment</comments>
      <pubDate>Mon, 12 Feb 2024 00:03:24 +0900</pubDate>
    </item>
    <item>
      <title>DJango 5.0 주요 변화</title>
      <link>https://info-tech.tistory.com/38</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;&lt;b&gt;db_default&amp;nbsp; 필드가 추가됨&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Example&lt;/blockquote&gt;
&lt;pre id=&quot;code_1701761693759&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# db_default 값은 데이터베이스에서 계산된 기본값으로 터럴 값이나 Now와 같은 데이터베이스 함수일 수 있다.
class MyModel(models.Model):
    age = models.IntegerField(db_default=18)
    created = models.DateTimeField(db_default=Now())
    circumference = models.FloatField(db_default=2 * Pi())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b data-stringify-type=&quot;bold&quot;&gt;GeneratedField&lt;/b&gt;&lt;b data-stringify-type=&quot;bold&quot;&gt;&amp;nbsp; 필드가 추가됨&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1701761450197&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MyModel(models.Model):
    age = models.IntegerField(db_default=18)
    created = models.DateTimeField(db_default=Now())
    circumference = models.FloatField(db_default=2 * Pi())

GeneratedField  필드가 추가됨
Example
class Square(models.Model):
    side = models.IntegerField()
    area = models.GeneratedField(
        expression=F(&quot;side&quot;) * F(&quot;side&quot;),
        output_field=models.BigIntegerField(),
        db_persist=True,
    )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;area의&amp;nbsp;경우&amp;nbsp;side&amp;nbsp;*&amp;nbsp;side인데,&amp;nbsp;해당값을&amp;nbsp;하나의&amp;nbsp;데이터가&amp;nbsp;생성시&amp;nbsp;자동적으로&amp;nbsp;area을&amp;nbsp;계산해서&amp;nbsp;넣어주는&amp;nbsp;방식.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;choices 필드의 유연성 증가&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. Field.choices (모델 필드용) 및 ChoiceField.choices (폼 필드용)는 값을 선언할 때 더 많은 유연성을 제공합니다.&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Exameple&lt;/blockquote&gt;
&lt;pre id=&quot;code_1701761519012&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from django.db import models

Medal = models.TextChoices(&quot;Medal&quot;, &quot;GOLD SILVER BRONZE&quot;)

SPORT_CHOICES = [
    (&quot;Martial Arts&quot;, [(&quot;judo&quot;, &quot;Judo&quot;), (&quot;karate&quot;, &quot;Karate&quot;)]),
    (&quot;Racket&quot;, [(&quot;badminton&quot;, &quot;Badminton&quot;), (&quot;tennis&quot;, &quot;Tennis&quot;)]),
    (&quot;unknown&quot;, &quot;Unknown&quot;),
]


class Winner(models.Model):
    name = models.CharField(...)
    medal = models.CharField(..., choices=Medal.choices)
    sport = models.CharField(..., choices=SPORT_CHOICES)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;보시면,&amp;nbsp;SPORT_CHOICES에서&amp;nbsp;tuple&amp;nbsp;형식&amp;nbsp;이외에도&amp;nbsp;List[Tuple]&amp;nbsp;형식등&amp;nbsp;다양하게 &amp;nbsp;제공&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2.&amp;nbsp;호출&amp;nbsp;가능한&amp;nbsp;객체를&amp;nbsp;받아들이는&amp;nbsp;기능을&amp;nbsp;추가했으며,&amp;nbsp;또한&amp;nbsp;열거형&amp;nbsp;타입을&amp;nbsp;확장하기&amp;nbsp;위해&amp;nbsp;.choices를&amp;nbsp;직접&amp;nbsp;사용할&amp;nbsp;필요가&amp;nbsp;없어졌습니다&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Example&lt;/blockquote&gt;
&lt;pre id=&quot;code_1701761720022&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from django.db import models

Medal = models.TextChoices(&quot;Medal&quot;, &quot;GOLD SILVER BRONZE&quot;)

SPORT_CHOICES = {  # Using a mapping instead of a list of 2-tuples.
    &quot;Martial Arts&quot;: {&quot;judo&quot;: &quot;Judo&quot;, &quot;karate&quot;: &quot;Karate&quot;},
    &quot;Racket&quot;: {&quot;badminton&quot;: &quot;Badminton&quot;, &quot;tennis&quot;: &quot;Tennis&quot;},
    &quot;unknown&quot;: &quot;Unknown&quot;,
}


def get_scores():
    return [(i, str(i)) for i in range(10)]


class Winner(models.Model):
    name = models.CharField(...)
    medal = models.CharField(..., choices=Medal)  # Using `.choices` not required.
    sport = models.CharField(..., choices=SPORT_CHOICES)
    score = models.IntegerField(choices=get_scores)  # A callable is allowed.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/5.0/releases/5.0/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.djangoproject.com/en/5.0/releases/5.0/&lt;/a&gt;&lt;/p&gt;</description>
      <category>서버이야기</category>
      <category>django #django 5.0 #django 5.0 요약</category>
      <author>개발 로그를 쌓고 싶은 블로거</author>
      <guid isPermaLink="true">https://info-tech.tistory.com/38</guid>
      <comments>https://info-tech.tistory.com/38#entry38comment</comments>
      <pubDate>Tue, 5 Dec 2023 16:45:29 +0900</pubDate>
    </item>
    <item>
      <title>Mysql 5.6 설치 및 세팅  [우분투]</title>
      <link>https://info-tech.tistory.com/35</link>
      <description>&lt;div style=&quot;-en-clipboard:true;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;5.6 설치&amp;nbsp;
&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);-en-codeblock:true;&quot;&gt;&lt;div&gt;sudo add-apt-repository 'deb http://archive.ubuntu.com/ubuntu trusty universe'
&lt;/div&gt;&lt;div&gt;sudo apt-get update
&lt;/div&gt;&lt;div&gt;sudo apt install mysql-server-5.6 mysql-client-5.6
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;sudo service mysql start
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;UTF -8 로 설정하기&amp;nbsp;
&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;(/etc/mysql/my.cnf)
&lt;/div&gt;&lt;div style=&quot;box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);-en-codeblock:true;&quot;&gt;&lt;div&gt;[client]
&lt;/div&gt;&lt;div&gt;default-character-set=utf8
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;[mysqld]
&lt;/div&gt;&lt;div&gt;character-set-client-handshake = FALSE
&lt;/div&gt;&lt;div&gt;init_connect=&quot;SET collation_connection = utf8_general_ci&quot;
&lt;/div&gt;&lt;div&gt;init_connect=&quot;SET NAMES utf8&quot;
&lt;/div&gt;&lt;div&gt;character-set-server = utf8
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;[mysql]
&lt;/div&gt;&lt;div&gt;default-character-set=utf8
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;[mysqldump]
&lt;/div&gt;&lt;div&gt;default-character-set = utf8
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;##변경후
&lt;/div&gt;&lt;div&gt;sudo service mysql restart
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;root 계정 외부에서 호스트 접근하도록 하기
&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);-en-codeblock:true;&quot;&gt;&lt;div&gt;1) 특정 IP 접근 허용 설정
&lt;/div&gt;&lt;div&gt;mysql&amp;gt; grant all privileges on *.* to ‘root’@‘192.168.56.101’ identified by ‘root의 패스워드’;
&lt;/div&gt;&lt;div&gt;2) 특정 IP 대역 접근 허용 설정
&lt;/div&gt;&lt;div&gt;mysql&amp;gt; grant all privileges on *.* to ‘root’@‘192.168.%’ identified by ‘root의 패스워드’;
&lt;/div&gt;&lt;div&gt;3) 모든 IP의 접근 허용 설정
&lt;/div&gt;&lt;div&gt;mysql&amp;gt; grant all privileges on *.* to ‘root’@‘%’ identified by 'root의 패스워드’;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;!--?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?--&gt;

&lt;/p&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>데이터베이스</category>
      <category>MySQL</category>
      <category>mysql UTF-8</category>
      <category>mysql 설치</category>
      <category>우분투</category>
      <author>개발 로그를 쌓고 싶은 블로거</author>
      <guid isPermaLink="true">https://info-tech.tistory.com/35</guid>
      <comments>https://info-tech.tistory.com/35#entry35comment</comments>
      <pubDate>Tue, 19 Mar 2019 00:20:48 +0900</pubDate>
    </item>
    <item>
      <title>Ruby on Rails 시작하기</title>
      <link>https://info-tech.tistory.com/34</link>
      <description>&lt;div style=&quot;-en-clipboard:true;&quot;&gt;&lt;!--?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?--&gt;

&lt;div&gt;참조 :&amp;nbsp;&lt;a href=&quot;https://github.com/rbenv/rbenv&quot;&gt;https://github.com/rbenv/rbenv&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);-en-codeblock:true;&quot;&gt;brew install rbenv ruby-build
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);-en-codeblock:true;&quot;&gt;&lt;div&gt;#rbenv를 bash에 추가하기
&lt;/div&gt;&lt;div&gt;echo 'if which rbenv &amp;gt; /dev/null; then eval &quot;$(rbenv init -)&quot;; fi' &amp;gt;&amp;gt; ~/.bash_profile source ~/.bash_profile
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);-en-codeblock:true;&quot;&gt;&lt;div&gt;#적용시키기
&lt;/div&gt;&lt;div&gt;source ~/.bash_profile
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);-en-codeblock:true;&quot;&gt;&lt;div&gt;#루비 버전 업데이트 (변경)
&lt;/div&gt;&lt;div&gt;rbenv install 2.5.3
&lt;/div&gt;&lt;div&gt;rbenv global 2.5.3
&lt;/div&gt;&lt;div&gt;rbenv rehash
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);-en-codeblock:true;&quot;&gt;&lt;div&gt;#레일즈 설치
&lt;/div&gt;&lt;div&gt;gem install rails
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);-en-codeblock:true;&quot;&gt;&lt;div&gt;#mysql로 시작하기
&lt;/div&gt;&lt;div&gt;rails new projectname -d mysql
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);-en-codeblock:true;&quot;&gt;&lt;div&gt;#sqlite3 오류시 Gemfile을 아래와 같이 수정한다. (만약 sqlite로 시작할시)
&lt;/div&gt;&lt;div&gt;gem &quot;sqlite3&quot;, &quot;~&amp;gt; 1.3.6&quot;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);-en-codeblock:true;&quot;&gt;&lt;div&gt;#rails c 에러 대처법 (Library not loaded: /usr/local/opt/readline/lib/libreadline.7.dylib)
&lt;/div&gt;&lt;div&gt;ln -s /usr/local/opt/readline/lib/libreadline.8.0.dylib /usr/local/opt/readline/lib/libreadline.7.dylib
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);-en-codeblock:true;&quot;&gt;&lt;div&gt;#db migrate
&lt;/div&gt;&lt;div&gt;rake db:migrate
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);-en-codeblock:true;&quot;&gt;&lt;div&gt;# 발판을 이용해서 빠르게 구성하기 (새로운 리소스를 위해 모델, 뷰, 컨트롤러를 만들기)
&lt;/div&gt;&lt;div&gt;rails generate scaffold Post name:string title:string content:text
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);-en-codeblock:true;&quot;&gt;&lt;div&gt;모델 만들기
&lt;/div&gt;&lt;div&gt;레일즈에서 모델은 단수형 이름을 사용합니다
&lt;/div&gt;&lt;div&gt;rails generate model Comment commenter:string body:text post:references&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>프로그래밍/Ruby on Rails</category>
      <category>Rails</category>
      <category>Ruby</category>
      <category>루비</category>
      <category>루비온레일즈</category>
      <author>개발 로그를 쌓고 싶은 블로거</author>
      <guid isPermaLink="true">https://info-tech.tistory.com/34</guid>
      <comments>https://info-tech.tistory.com/34#entry34comment</comments>
      <pubDate>Sun, 17 Mar 2019 12:57:04 +0900</pubDate>
    </item>
    <item>
      <title>MySQL 서버를 AWS Aurora로 이전 (마이그레이션) 하기</title>
      <link>https://info-tech.tistory.com/33</link>
      <description>&lt;div style=&quot;-en-clipboard:true;&quot;&gt;&lt;!--?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?--&gt;

&lt;div&gt;EC2 m4 인스턴스에서 사용중인 mysql 디비서버를 RDS로 옮기는 과정을 적어봤습니다.
&lt;/div&gt;&lt;div&gt;서버이전은 AWS에서 제공하는 Data Migration Service(DMS)를 이용하여 이전작업을 진행했습니다.
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;DMS는 데이터를 AWS로 빠르고 나름 안전하게(?) 그리고 프로덕션을 무중단 상태로 운영하면서 마이그레이션을 도와줍니다.
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 14px;&quot;&gt;동종 마이그레이션뿐 아니라 Oracle 또는 Microsoft SQL Server에서 Amazon Aurora로의 마이그레이션과 같은 이기종 데이터베이스 플랫폼 간의 마이그레이션도 지원합니다&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 14px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 24px;&quot;&gt;&lt;b&gt;준비단계&lt;/b&gt;&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&lt;ol&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;RDS 인스턴스 생성&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;DMS 인스턴스 생성&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;DMS의 엔드포인트 생성&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;1 RDS 인스턴스 생성&lt;/b&gt;&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;원본 데이터베이스에서 데이터를 마이그레이션 할 인스턴스를 생성합니다.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;저희 회사에서는 엔진옵션으로 Aurora를 이용했습니다.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;다음으로 세부 인스턴스의 정보를 입력합니다.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;최종적으로 원하는 스펙을 생성합니다.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;2 DMS 인스턴스 생성&lt;/b&gt;&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;원본 -&amp;gt; 타겟으로 마이그레이션을 도와줄 인스턴스를 생성합니다.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;크기에 알맞게 DMS 인스턴스를 생성합니다.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;3 DMS의 엔드포인트 생성&lt;/b&gt;&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;원본(EC2 mysql)의 엔드포인트 생성을 합니다.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;font-size: 14px; background-color: rgb(254, 249, 249); color: rgb(68, 68, 68); font-family: &amp;quot;Helvetica Neue&amp;quot;, Roboto, Arial, sans-serif; font-weight: bold;&quot;&gt;원본/타겟 엔드포인트 식별자&lt;/span&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;는&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;caret-color: rgb(35, 47, 62); color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;‘&lt;/span&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;source/target’와 같은 인식이 가능하게 단어로 입력합니다.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;소스엔진은&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;caret-color: rgb(35, 47, 62); color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;‘&lt;/span&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;mysql’(해당 사용하고 있는 것과 맞게 끔)을 선택합니다&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;서버이름은 해당 퍼블릭 주소를 입력해줍니다.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;포트는 3306 혹은 사용하고 계신 포트를 입력하시면 됩니다.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;그다음 계정 / 비밀번호를 입력하고 테스트 연결을 해줍니다.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;테스트 연결이 되어 있지않다면 계정에 대한 grant와 같은 옵션을 확인해 줍니다.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;타겟 엔드포인트도 위와 같은 설정으로 진행합니다.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 14px;&quot;&gt;&lt;b&gt;*주의사항*&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 14px;&quot;&gt;DMS는 원본 데이터베이스의 인덱스나 외래키정보도 같이 넘겨주지 않습니다 ㅠㅠ 이부분에서 엄청 삽질 했다는..&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62);&quot;&gt;만약 현재의 인덱스나 외래키 설정을 그대로 가져가고 싶다면 AWS에서 지원해주는 Schema convert tool을 이용하거나 (저같은 경우에는 맥을 사용하는데 오류가 심해서 설치만 하고 안씀)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62);&quot;&gt;아래와 같은 명령어를 통해 dump를 통해 스키마를 빼내는 방법이 있습니다.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);-en-codeblock:true;&quot;&gt;mysqldump -u root -p --no-data dbname &amp;gt; schema.sql
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;그 다음 해당 Aurora 인스턴스에 저 스키마를 미리 import 하는 방법이 있습니다.
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;그 후 엔드포인트의 Aurora의 고급설정에서 외래키 점검을 비활성화 해줍니다.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);-en-codeblock:true;&quot;&gt;initstmt=SET FOREIGN_KEY_CHECKS=0(외래키 점검 비활성화)
&lt;/div&gt;&lt;div style=&quot;margin-left: 2em&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 24px;&quot;&gt;&lt;b&gt;작업단계&lt;/b&gt;&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&lt;ol&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;DMS 작업 생성&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;DMS 작업 설정&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;DMS 테이블 매핑&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;1 DMS 작업생성&lt;/b&gt;&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;작업이름 : 해당 사용할 작업이름을 적습니다.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;복제인스턴스 : 아까 생성한 DMS 인스턴스 선택합니다.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;소스 엔드포인트 : 아까 만든 소스의 엔드포인트를 적습니다.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;타겟 엔드포인트 : 아까 만든 타겟의 엔드포인트를 적습니다.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;마이그레이션 유형:&lt;/span&gt;&lt;/li&gt;&lt;li style=&quot;list-style: none&quot;&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;기존데이터 마이그레이션 : 현재 시작지점까지의 데이터를 마이그레이션 해줍니다 (만약 소스 디비상태가 shutdown 상태인 경우 유리)&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;기존데이터 마이그레이션 및 지속적인 변경사항 복제 : 무중단 상태의 마이그레이션을 보장해줍니다.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;데이터 변경사항 복제 : 현재 시작시점으로 부터 쌓이는 데이터의 마이그레이션 입니다.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;2 DMS 작업 설정&lt;/b&gt;&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;대상 테이블 준비 모드:&lt;/span&gt;&lt;/li&gt;&lt;li style=&quot;list-style: none&quot;&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;아무 작업 안함 : 타겟 데이터베이스의 테이블을 건드리지 않고 데이터를 복제합니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;대상 테이블에서 삭제 : 만약 대상테이블이 존재하면 삭제되고 새롭게 쓰입니다.&lt;/span&gt; &lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;; font-weight: bold;&quot;&gt;(*주의 : 만약 이걸로 하면 위에서 dump떠서 적용한 스키마가 다 날라감 주의)&lt;/span&gt;
&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;자르기 : 테이블의 메타정보를 보존한 상태로 데이터만 잘라줍니다&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;복제에 LOB 열 포함:&lt;/span&gt;&lt;/li&gt;&lt;li style=&quot;list-style: none&quot;&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;해당 설정에 맞게 LOB열을 포함 할지 말지 정해주시면 됩니다.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;검증 활성화 : 데이터 마이그레이션이 다 수행 된후 무결성을 체크(?) 해주는 부분인데 만약 테이블 크기가 크다면 이 작업이 엄청 오래 걸릴 수 있습니다.. (저는 체크 안했습니다)&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;로깅 활성화 : CloudWatch를 볼수 있게 기능을 활성화 할 수 있습니다. (마이그레이션 작업시 오류가 어디서 났는 지 확인 가능)&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;고급설정 : 고급설정 탭을 통해 마이그레이션의 병렬노드할 테이블 수나 속도 커밋을 제어 할 수 있습니다.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;3 테이블 매핑&lt;/b&gt;&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;스키마 이름 : 복제할 스키마를 선택합니다.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;테이블 이름 형식&lt;/span&gt;&lt;/li&gt;&lt;li style=&quot;list-style: none&quot;&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;% : 모든 테이블 복사&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;Member이런식으로 마이그레이션 할 테이블 이름 지정 가능&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;작업&lt;/span&gt;&lt;/li&gt;&lt;li style=&quot;list-style: none&quot;&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;포함 : 해당 작업을 포함 시킵니다.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;제외 : 해당 테이블의 작업을 제외시킵니다.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: rgb(35, 47, 62); font-family: &amp;quot;Malgun Gothic&amp;quot;;&quot;&gt;위 설정을 마친 후 작업 생성을 클릭하면 작업이 시작 됩니다.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>서버이야기/AWS</category>
      <category>AWS Aurora</category>
      <category>AWS DMS</category>
      <category>aws mysql</category>
      <category>DMS</category>
      <category>migrate</category>
      <category>MySQL</category>
      <category>RDS</category>
      <category>마이그레이션</category>
      <author>개발 로그를 쌓고 싶은 블로거</author>
      <guid isPermaLink="true">https://info-tech.tistory.com/33</guid>
      <comments>https://info-tech.tistory.com/33#entry33comment</comments>
      <pubDate>Sun, 3 Mar 2019 18:02:02 +0900</pubDate>
    </item>
    <item>
      <title>upbit websocket api사용방법</title>
      <link>https://info-tech.tistory.com/32</link>
      <description>&lt;p&gt;Upbit Websocket통신 할 수 있는 소스를 구현해봤습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;b&gt;출처 :&amp;nbsp;https://docs.upbit.com/docs/upbit-quotation-websocket&lt;/b&gt;&lt;/p&gt;&lt;div style=&quot;-en-clipboard:true;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;!--?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?--&gt;

&lt;/p&gt;&lt;div style=&quot;box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; color: rgb(51, 51, 51); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.14902);-en-codeblock:true;&quot;&gt;&lt;div&gt;import websocket
&lt;/div&gt;&lt;div&gt;import json
&lt;/div&gt;&lt;div&gt;try:
&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;import thread
&lt;/div&gt;&lt;div&gt;except ImportError:
&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;import _thread as thread
&lt;/div&gt;&lt;div&gt;import time
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;def on_message(ws, message):
&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; get_message&amp;nbsp;= json.loads(message.decode('utf-8'))
&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(get_message)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;def on_error(ws, error):
&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(error)
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;def on_close(ws):
&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&quot;close&quot;)
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;def on_open(ws):
&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def run(*args):
&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sendData = '[{&quot;ticket&quot;:&quot;test&quot;},{&quot;type&quot;:&quot;ticker&quot;,&quot;codes&quot;:[&quot;KRW-CPT&quot;,&quot;KRW-ADA&quot;]}]'
&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ws.send(sendData)
&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;time.sleep(2)
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ws.close()
&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;thread.start_new_thread(run, ())
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;if __name__ == &quot;__main__&quot;:
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ws = websocket.WebSocketApp(&quot;wss://api.upbit.com/websocket/v1&quot;,
&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;on_message = on_message,
&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;on_error = on_error,
&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;on_close = on_close)
&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ws.on_open = on_open
&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ws.run_forever()
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>프로그래밍/파이썬 &amp;amp; 장고</category>
      <category>Websocket</category>
      <category>업비트</category>
      <category>업비트 웹소켓</category>
      <category>웹소켓</category>
      <category>웹소켓 api</category>
      <author>개발 로그를 쌓고 싶은 블로거</author>
      <guid isPermaLink="true">https://info-tech.tistory.com/32</guid>
      <comments>https://info-tech.tistory.com/32#entry32comment</comments>
      <pubDate>Sat, 16 Feb 2019 03:48:29 +0900</pubDate>
    </item>
  </channel>
</rss>