TIL

👀Today I Learn

동기와 비동기

1. 동기(Synchronous)

  • 코드가 순차적으로 실행됨
  • 이전 작업이 완료될 때까지 다음 작업이 대기
  • 실행 흐름이 예측 가능하지만, 하나의 작업이 오래 걸릴 경우 전체 응답 속도가 느려질 수 있음

2. 비동기(Asynchronous)

  • 작업이 완료될 때까지 기다리지 않고 다음 작업을 진행
  • 병렬적으로 실행할 수 있어 성능 향상 가능
  • 이벤트 기반 프로그래밍 방식 사용


Python에서의 비동기 처리 방식

1. asyncawait

  • Python에서는 asyncawait 키워드를 사용하여 비동기 코드를 작성할 수 있음
  • async 함수는 호출 시 즉시 실행되지 않고 await 키워드가 있는 지점에서 멈추었다가, 실행이 완료되면 다시 진행됨
  • 예제

      import asyncio
        
      async def my_async_function():
          print("Start")
          await asyncio.sleep(2)
          print("End")
        
      asyncio.run(my_async_function())
    

2. Multi-threading

  • 여러 스레드를 사용하여 동시에 여러 작업을 실행
  • Python의 threading 모듈을 사용하여 구현
  • GIL(Global Interpreter Lock) 때문에 CPU 작업보다는 I/O 작업에 적합
  • 예제

      import threading
      import time
        
      def worker():
          print("Thread 시작")
          time.sleep(2)
          print("Thread 종료")
        
      thread = threading.Thread(target=worker)
      thread.start()
      thread.join()
    

3. Multi-processing

  • 여러 프로세스를 사용하여 병렬로 작업 수행
  • multiprocessing 모듈을 사용하여 구현
  • GIL의 영향을 받지 않아 CPU 연산이 많은 작업에 적합
  • 예제

      import multiprocessing
      import time
        
      def worker():
          print("Process 시작")
          time.sleep(2)
          print("Process 종료")
        
      process = multiprocessing.Process(target=worker)
      process.start()
      process.join()
    


Django에서의 비동기 처리 방식

1. Celery + Redis

  • Celery는 비동기 작업을 처리하는 분산 작업 큐
  • Redis를 백엔드로 사용하여 작업을 저장하고 실행

  • 설치 및 실행

      pip install celery redis
    
  • celery.py 설정 (Django 프로젝트 루트)

      import os
      from celery import Celery
        
      os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
        
      celery_app = Celery('myproject')
      celery_app.config_from_object('django.conf:settings', namespace='CELERY')
      celery_app.autodiscover_tasks()
    
  • 태스크 정의

      from celery import shared_task
      import time
        
      @shared_task
      def my_task():
          time.sleep(5)
          return "작업 완료"
    

2. asyncawait

  • Django 3.1부터 일부 뷰에서 async 지원 가능

      from django.http import JsonResponse
      import asyncio
        
      async def async_view(request):
          await asyncio.sleep(2)
          return JsonResponse({"message": "비동기 작업 완료"})
    


Django와 Channels

1. 웹소켓(WebSocket)이란?

  • 실시간 양방향 통신을 가능하게 하는 프로토콜
  • HTTP와 달리 연결을 유지하면서 클라이언트-서버 간 데이터를 주고받을 수 있음
비교 항목 HTTP WebSocket
연결 방식 요청-응답 지속적 연결
속도 요청마다 연결 지속 연결로 빠름
데이터 교환 클라이언트가 요청해야 서버 응답 실시간 양방향 통신

2. Django Channels를 이용한 웹소켓 구현

  1. 설치 및 설정

    pip install channels channels-redis
    
  • settings.py

      INSTALLED_APPS = [
          'channels',
          'myapp',
      ]
        
      ASGI_APPLICATION = "myproject.asgi.application"
      CHANNEL_LAYERS = {
          "default": {
              "BACKEND": "channels_redis.core.RedisChannelLayer",
              "CONFIG": {
                  "hosts": ["redis://localhost:6379"],
              },
          },
      }
    
  • asgi.py

      import os
      from django.core.asgi import get_asgi_application
      from channels.routing import ProtocolTypeRouter, URLRouter
      import myapp.routing
        
      os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
        
      application = ProtocolTypeRouter({
          "http": get_asgi_application(),
          "websocket": URLRouter(
              myapp.routing.websocket_urlpatterns
          ),
      })
    
  • routing.py

      from django.urls import path
      from myapp.consumers import ChatConsumer
        
      websocket_urlpatterns = [
          path("ws/chat/", ChatConsumer.as_asgi()),
      ]
    
  • consumers.py

      import json
      from channels.generic.websocket import AsyncWebsocketConsumer
        
      class ChatConsumer(AsyncWebsocketConsumer):
          async def connect(self):
              await self.accept()
              await self.send(text_data=json.dumps({"message": "연결 성공"}))
        
          async def disconnect(self, close_code):
              pass
        
          async def receive(self, text_data):
              data = json.loads(text_data)
              message = data["message"]
              await self.send(text_data=json.dumps({"message": message}))
    


Redis를 활용한 다중 서버 웹소켓 운영

  • 여러 대의 서버에서 웹소켓을 처리할 경우 Redis를 이용하여 메시지를 브로드캐스트
  • channels_redis를 설정하여 여러 서버 간 동기화 가능


비동기 처리의 한계와 주의할 점

  1. CPU 바운드 작업
    • 비동기는 주로 I/O 작업에 적합하며, CPU 작업이 많다면 multiprocessing이 필요
  2. 동기-비동기 혼합 주의
    • 비동기 코드 내부에서 동기 함수를 실행하면 성능 저하 발생 가능
  3. 데드락 발생 가능
    • 잘못된 await 사용으로 인해 프로그램이 멈출 수 있음
  4. 오버헤드:
    • 비동기 실행을 위한 컨텍스트 스위칭 비용 존재



💡Today I Thought

오늘의 체크리스트

  • 알고리즘 코드카타 1문제
  • SQL 코드카타 1문제
  • 유저테스트 정리
  • 브로셔 수정
  • PickMate 임시 FE 작성
  • TIL 작성

회고

 오늘 엄마 생신이셔가지구 많이 못해서 조금 불안한 어른.. 내일 밤샘할 것 같은 이상한 느낌..ㅎ 이제부터 뭐 밤새서라도 해내야지…🥺 일정 너무너무너무너무 빡세다ㅠㅠ 유저테스트 피드백도 반영해야하는데…

댓글남기기