메모밍
Aiden's LAB
  • All Posts
    • Develop
      • Preferences
      • Linux & Ubuntu
      • Python
      • Web & Server
      • ML & DL
    • Notes
      • Diary
      • Books
      • Memo
      • Review
      • Tips

최근 글

  • [Slack Bot] Django에 슬렉봇 연결하기 (python sl⋯
    2022.03.17
  • [Django Channels] 실시간 채팅 구현하기 -2
    2022.03.17
  • [Django Channels] 실시간 채팅 구현하기 -1
    2022.03.16
  • [데이터사이언스] 데이터 전처리 대전
    2020.11.22
  • [리액트 서적] Learning React (러닝 리액트)
    2020.11.22
  • [딥러닝] 그로킹 딥러닝 :: 딥러닝 입문 추천 서적
    2020.03.20
  • [Babel] 초간단 바벨(Babel) 사용법
    2020.01.22
  • [Numpy] 파이썬 벡터 표현
    2020.01.20
  • [GITHUB] Git Branch(브랜치) 삭제하기 (Local / R⋯
    2020.01.18
  • [GITHUB] 원격 저장소의 Branch 가져오기
    2020.01.16
Theme Designed By 정상우
메모밍
[Django Channels] 실시간 채팅 구현하기 -1
Develop/Web & Server

[Django Channels] 실시간 채팅 구현하기 -1

2022. 3. 16. 02:14

들어가며

이 글은 내가 현재 만들고 있는 서비스에서 필요한 기능을 구현하며 적은 일지 같은 형태다. 고로... 깔끔하게 개발 흐름이 정리되지 않았으며 튜토리얼의 형태를 띠고 있지 않다. 열심히 한 방향으로 가다가 중간에 멈추고 다른 방법을 적용하기도 한다. 만약 당장 급하게 이 글과 같은 기능을 구현해야 한다면 전체 시리즈를 읽고 감을 잡은 후 부디 한 번이라도 삽질을 덜하면서 적용하길 바란다.

 

Django Channels 살펴보기

개발 중인 서비스에서 실시간 채팅을 구현해야 할 타이밍이 되었다. 그동안 나중에 하자 나중에 하자 미뤄두었었는데, 이제는 미룰 수 없게 된 거지. 당연히 Web Socket과 비동기 방식을 이용해야 하니 기존 Django만 가지고는 힘들어 보였고 Channels라는 패키지를 사용하기로 했다. Github에 들어가 보니 활발히 업데이트되고 있는 패키지이고, 서비스되고 있는 앱들도 있어 보여서 나중에 업데이트가 중단되거나 하는 위험성이 적어 보였다. 

- Django Channels Github (https://github.com/django/channels)

 

좋은 패키지는 문서를 보면 알 수 있다고 했다. 공식 문서를 보자마자 마음을 정하고 바로 튜토리얼을 따라 구현해보았다.

- Django Channels Docs (https://channels.readthedocs.io/en/stable/)

튜토리얼을 따라 구현하면 간단한 채팅을 구현할 수 있다. 이 과정을 상세히 포스팅할 수 도 있지만 솔직히 공식 문서보다 깔끔하게 정리가 가능한지 모르겠다. 위 문서에 너무나 친절한 설명과 함께 나와있으니 참고해서 따라 해 보면 Channels의 기본 사용법을 익힐 수 있다.

 

두 개의 브라우저를 켜고 동일한 방 이름으로 들어가면 채팅이 가능하다. 한쪽에 입력한 내용이 다른 쪽에도 보인다. 물론 구현이 되고 신기한 건 딱 30초 정도 갔다. 머릿속에 바로 여러 가지 추가 구현해야 하는 것들이 한가득 떠올랐기 때문...

 

(1) 새로 방을 입장하게 되면 이전 채팅 내용이 전혀 보이지 않는다.

(2) 새로고침을 하면 이전 채팅 내용이 모두 날아간다.

(3) 아무 유저나 들어올 수 있으면 안 된다, 검증하는 로직이 필요하다.

(4) 내 서비스는 웹이 아닌 모바일 기기에서 서비스되고 있으므로 엔드포인트를 다시 구성해야 한다.

(5) 이걸 그래서 현재 DRF + RESTfull 하게 구현한 상태에 어떻게 붙인담...?

 

사실 여기서부터가 진짜지

모든 서비스 구현이 그렇듯 패키지의 튜토리얼을 보는 것은 라면을 끓이기 전 어떤 라면을 먹을지 고르는 것 까지가 끝난 셈이다. 사실 이제부터가 진짜 고생길의 시작이다. 

 

Redis 데이터 저장 시간 변경

Channels 튜토리얼을 따라가면 기본적으로 Django - Channels - Redis를 연동해서 사용하게 되는데 Channles와 Redis를 쉽게 연동하기 위한 중간 패키지로 channels_redis 패키지를 사용한다. (공식 Github https://github.com/django/channels_redis) channles_redis가 하는 기본적인 역할은 Channels의 그룹(Group)과 채널(Channel) 그리고 소비자(Consumer)에 대한 맵핑을 저장하는 것이다. Channels의 그룹, 채널, 소비자에 대한 개념은 시간이 될 때 조금 더 설명하는 걸로... 사실 수많은 블로그에서 이미 설명을 하고 있고, 공식 문서에 자세히 나와있다.


기본적인 채팅을 구현하면 아래와 같은 형태로 Redis가 메모리에 데이터를 저장한다. 그룹과 채널에 대한 정보가 보이게 되는데 이때`value` 항목에 한글이 있다면 인코딩이 맞지 않아 한글이 표기되지 않는다. redis-cli에서 `--raw` 옵션을 주면 제대로 보이니 이 부분은 어렵지 않다. 패스. 내가 필요한 부분은 ttl 부분이었다.

 

이 ttl은 초단위로 매초 줄어들게 되어 0이 되면 해당 row가 redis에서 삭제된다. 메모리라는 소중한 자원을 저장소로 사용하므로 필요한 옵션이다. 하지만 기본으로 설정되어있는 60초는 너무 짧았고, 나는 이러한 설정을 해준 적이 없으니 패키지 어딘가에서 기본값으로 지정이 되어있는 건데 생각을 해보면 redis를 현재 조작하고 있는 것은 channels_redis 패키 지니까 이 패키지의 github을 살펴봄으로써 힌트를 얻을 수 있었다.

결론적으로 settings.py의 redis 설정 부분에 expiry 옵션을 부여함으로써 변경이 가능했다. 일단은 5분(300초) 정도로 설정을 해두고 전체적인 구현이 완료되면 다시 적절한 시간으로 다시 변경하기로 했다. 하지만 미래의 나를 믿는 것은 아주 지양해야 하기에 TODO로 남겨두었다.

...
# Channels
ASGI_APPLICATION = "boogle.asgi.application"
# TODO :: secret_key로 빼기, expiry 다시 설정하기
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("127.0.0.1", 6379)],
            "expiry": 300,
        },
    },
}
...

 

Redis 데이터 백업

데이터 저장 시간도 변경했으니, 이제 채팅 데이터를 저장하는 일이 필요하다. 채팅방에 입장해 있을 때는 계속 Web Socket을 이용해서 데이터를 뿌려주면 되지만 채팅방을 나갔다가 다시 들어왔을 때는 어딘가에 저장되어있는 데이터를 가져와서 보여줘야 하니까. 그래서 우선 Redis가 메모리에 저장하고 있는 데이터를 어떻게 RDB로 뽑아야 하는지 Redis 문서를 찾아봤다. 역시나 Redis에서도 기본적으로 제공하고 있었다. (Redis 공식문서 https://redis.io/topics/persistence) 개발하기 좋은 세상이다 - _- b 

 

Redis가 제공하는 데이터 저장 방식(백업 방식)은 두 가지가 있는데, RDB(Snapshotting) 방식과 AOF(Append Only File) 방식이다. 간단히 설명하자면 RDB는 우리가 익숙한 방식으로 현재 메모리에 올라가 있는 데이터를 모두 파일에 쓰는 방식이다. 파일에 저장하는 주기를 설정할 수 있고, 특정 시점으로 데이터를 복구하는 것도 가능하므로 편리한 방식이다. Redis가 기본값으로 설정하는 방식이기도 하다. 다음으로 AOF는 Redis가 메모리에 데이터를 쓰는 과정 자체를 순차적으로 저장하는 방식이다. 예를 들어 1부터 10까지의 합을 구하는 것을 저장한다고 하면 RDB로 백업했다면 55라는 값에 바로 접근할 수 있는 반면 AOF는 과정이 저장되어 있으므로 1부터 10을 입력하는 과정을 다시 거쳐서 55라는 값을 얻을 수 있다. 장단점은 명확하다. RDB 방식은 사용하기 편한 반면 엄밀하게 모든 데이터를 백업할 수 없다. 데이 털 르 백업하고 있는 과정에서 에러가 나버리면 이전에 백업한 이후의 데이터를 잃어버리게 되는 단점이 있다. AOF를 사용하면 엄밀하게 데이터를 모두 백업할 수 있으나, 백업한 데이터로부터 내가 원하는 데이터를 얻는 후처리 과정이 필요하고 백업 용량도 RDB보다 더 크다. 나는 채팅 데이터이므로 RDB로도 충분하다.

 

이제 남은것은 Redis에서 해당 저장 방식으로 백업을 하도록 설정하는 거다. 이 역시 공식 문서를 보면 자세히 나와있다. redis.conf 설정 파일에서 save 옵션을 주면 되는데 이때 save A B 형식으로 사용이 가능하다. A에는 몇 초 동안의 의미가, B에는 적어도 몇 개의 데이터가 변경되면 이라는 의미가 들어간다. 즉 `save 60 100`이라고 적게 되면 60초 동안 100개의 데이터가 변경되면 RDB 파일로 백업한다라는 의미가 된다. 

 

redis.conf 설정 파일 찾기

자, 그러면 redis.conf 파일을 찾아야 하는데... 이게 뭔가 이상하다. AWS 서버에서는 쉽게 찾을 수 있었는데 실제 배포 전에 개발하는 mac에서 로컬로 돌아가는 redis의 설정 파일을 찾을 수가 없었다. 구글에서 많이 말하는 /usr/local/etc/ 위치에서도 찾을 수 없었다. 그러던중 실리콘 맥은 기존 인텔 맥과 다른 경로에 설정 파일이 있다는 내용을 찾을 수 있었다. 실리콘 맥의 경우 /opt/homebrew/etc/ 아래에서 redis.conf 설정 파일을 찾을 수 있다.

열어서 확인 해보면 다음과 같이 RDB 저장 방식에 대한 부분을 찾을 수 있다. 설명도 자세한 편.

일단 테스트를 위해 아래와 같이 설정했다. 1분 동안 100개 이상의 변화가 일어나면 저장, 5분 동안 50개 이상의 변화가 일어나면 저장, 1시간 동안 1개 이상의 변화가 일어나면 저장.

설정을 수정했으므로 redis를 재실행해준다.

brew services stop redis
brew services start redis

이제 메시지를 보내면 dump.rdb 파일이 잘 생성됨을 알 수 있다. 

 

다시 돌아온 고민, 그리고 결정

Redis가 관리하고 있는 메모리 데이터를 영구적으로 저장할 방법은 찾았으나 이 방식은 바로 내 서비스에 붙이기에 쉽지 않았다. 일단 데이터 save 트리거가 작동할 때마다 Redis는 다시 save 하는 과정으로 들어가게 되고, 이 과정이 진행되는 동안 request를 처리하지 않았다. 물론 더 찾아보니 백그라운드 작업으로 처리하는 방법이 있었으나, 그다음 문제는 redis는 항상 기존의 데이터 파일을 지우고 새로 데이터 백업을 만드는 방식으로 데이터를 처리한다는 것. 그러니까 채팅 데이터를 온전히 모두 보관하기 위해서는 redis의 저장 시간을 매우 길게 늘여 놓고 사용하거나 다음 save 트리거가 오기 전에 저장되어 있는 백업 데이터를 RDBMS로 옮기는 작업을 해야 한다는 것이었다. 어느 쪽이든 원하는 기능을 구현할 수 있겠지만 우선 실시간 채팅을 넣고 유저의 반응을 빠르게 보고 싶었기에 다시 고민을 하기 시작했다.

 

그렇게 고민을 하다 나온 것은 어차피 비동기로 처리를 할 것이므로, 지금 매우 여유롭게 사용하고 있는 AWS 머신을 조금 더 가혹하게(?) 사용하는 방법을 택했다. 유저의 채팅 데이터를 받는 것을 트리거로 사용해서 RDBMS에 채팅 데이터를 저장하고 유저는 redis 위에서 서비스를 사용하게끔 구현을 해보기로 했다. 이렇게 결정하고 나니 마음이 편-안

저작자표시
    'Develop/Web & Server' 카테고리의 다른 글
    • [Django Channels] 실시간 채팅 구현하기 -2
    • [Babel] 초간단 바벨(Babel) 사용법
    • [Django] 빨리 배워 빨리 써먹는 Django :: 설치부터 실행까지
    django, Django Channels, Django Channels Chat, Django 채팅, 장고 채팅
    메모밍
    메모밍
    :: Aiden Lim's LAB ::
    댓글쓰기
    다음 글
    [Django Channels] 실시간 채팅 구현하기 -2
    이전 글
    [데이터사이언스] 데이터 전처리 대전
    • 이전
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • ···
    • 38
    • 다음

    티스토리툴바