새소식

python/Django

파라코드 기술설명서

  • -

파라코드 기술설명서

✔️ 개요

💬 여러사람들이 다같이 채팅할수있고 1:1 채팅할수있는 커뮤니티 채팅사이트 구현

✔️ Github 리포지터리


✔️ 사용된기술

⚓️ 버전관리

- Git , Github

⚓️ 배포

- 👾 Github
- ☕️ Jenkins
- 🐳 Docker

♻️ 개발환경

-  Anaconda
-  Sqlyog
-  Chrome
-  Firefox
-  Window 11
-  PIP

🔥 서비스환경

- Nginx
- Mariadb
- Docker
- CentOS7

🔱 기술 스택

- Python 3
- Django 4
- Django MTV
- Django DRF
- MariaDB
- HTML,CSS,JS
- JQuery
- Tailwind
- Bootstrap
- daisy

✔️ 요구사항 현황

RQ-ID 분류 요구사항내용 날짜 작성자 진행사항
RQ-001 회원 회원가입 3/03 김채민 ✔️
RQ-002 회원 로그인/로그아웃 3/03 김채민 ✔️
RQ-003 회원 온/오프라인 상태 표시 3/03 김채민 ✔️
RQ-004 회원 전체 유저 목록 3/03 김채민 ✔️
RQ-005 회원 온라인 유저 목록 3/03 김채민 ✔️
RQ-006 회원 유저 검색 3/03 김채민 ✔️
RQ-007 회원 친구 추가 3/03 김채민 ✔️
RQ-008 회원 친구 목록 3/03 김채민 ✔️
RQ-101 채팅 단체 채팅 3/03 김채민 ✔️
RQ-102 채팅 다이렉트 채팅 3/03 김채민 ✔️
RQ-103 채팅 채팅방 생성 3/03 김채민 ✔️
RQ-104 채팅 채팅방 나가기 3/03 김채민 ✔️
RQ-105 채팅 채팅방 삭제 3/03 김채민 ✔️
RQ-106 채팅 채팅방 목록 3/03 김채민 ✔️
RQ-107 채팅 채팅방 검색 3/03 김채민 ✔️
RQ-108 채팅 시스템 메시지 3/03 김채민 ✔️

✔️ 기능

👤 회원

- 회원가입시 로그인 할수있는 회원이 만들어진다.
- 로그인시 회원으로 접속이 가능하다.
- 로그아웃이 된다.
- 온라인유저는 초록불, 오프라인유저는 빨간불 로 구별할수있다
- 친구 검색을 통해 친구를 검색할수있다
- 알림메세지를 통해 입 퇴장 메세지를 알려줄수있다
- 전체 회원을 보여줄수있다
- 친구 추가한 전체 친구목록을 보여줄수있다
- 전체 온라인 유저를 볼수있다

💬 채팅

- 채팅방만들기시 유저들과 채팅할수있는 채팅방을 만들수있다
- 채팅방나가기시 채팅방을 나갈수있다
- 채팅방검색을 통해 원하는 채팅방 을 찾을수있다
- 메인 아이콘을 을 통해 전체채팅방목록 을 볼수있다
- 단체채팅방을 만들어서 단체채팅을 할수있다
- 친구창에 다이렉트 메세지를 눌러 친구와 1:1 채팅 할수있다
- 시스템 메세지 구현을 통해 채팅방 입장 퇴장 알림메세지를 출력할수있다

✔️ ERD

ERD


✔️ 주요 폴더 및 파일

- accounts : 회원앱

- base : 프로젝트

- chat : 채팅앱

- static : 정적으로 발생하는 정적파일들을 담는 폴더

- requirement : 의존성 파일

- .env : 설정파일

- package.json : node 의존성

- nodemodules : node 패키지

- docker-compose.yaml : 가상컨테이너 오케스트레이션 파일

- dockerfile : Docker Image를 만들기 위한 설정 파일

- tailwind.config.js : 테일윈드 설정

🐳 배포 구조

  • 커밋, 푸시, 젠킨스 빌드 유발, 컨테이너 내의 소스코드 리빌드

서버 구조

서버 구조 uml 이미지

젠킨스 배포순서, 테스트 실패시

젠킨스 배포순서, 테스트 실패시 uml 이미지


젠킨스 배포순서, 테스트 성공시

젠킨스 배포순서, 테스트 성공시 uml 이미지


🔧 주요 기능 / 화면

▶️ 메인(로그인)

메인화면 이미지

<!--base/tamplates/main.html-->
<div class="hero min-h-screen bg-base-200">
            <div class="flex-col justify-center hero-content lg:flex-row">
                <div class="text-center lg:text-left">
                    <i class="spin__icon fa-solid fa-feather-pointed fa-4x mb-8 duration-[30s]"></i>
                    <a href="{% url 'chat:list' %}">
                        <h1 class="mb-5 text-5xl font-bold text-green-400 finger-snapping-title duration-[30s]">
                            Paracord
                        </h1>
                    </a>
                    <p class="mb-5 finger-snapping-body duration-[30s]">
                        Provident cupiditate voluptatem et in. Quaerat fugiat ut
                        assumenda excepturi 
                    </p>
                </div>
                <div
                    class="card flex-shrink-0 w-full max-w-sm shadow-2xl bg-base-100 ml-20"
                >
                <form
                method="post"
                class="post-form"
                action="{% url 'accounts:login' %}"
            >
            {% csrf_token %}
                    <div class="card-body">
                        <div class="form-control">
                            <label class="label">
                                <span class="label-text">아이디</span>
                            </label>
                            <input
                                placeholder="아이디를 입력해주세요."
                                type="text"
                                class="form-control input input-bordered bg-gray-800"
                                name="username"
                                id="username"
                                value="{{ form.username.value|default_if_none:'' }}"
                            />
                        </div>
                        <div class="form-control">
                            <label class="label">
                                <span class="label-text">비밀번호</span>
                            </label>
                            <input
                                placeholder="비밀번호를 입력해주세요."
                                type="password"
                                class="form-control input input-bordered bg-gray-800"
                                name="password"
                                id="password"
                                value="{{ form.password.value|default_if_none:'' }}"
                            />
                        </div>
                        <div class="d-flex justify-content-evenly mt-5">
                            <button class="btn btn-ghost bg-green-500 hover:bg-white hover:text-green-500">
                                로그인
                            </button>
                            <a
                                href="{% url 'accounts:signup' %}"
                                class="btn btn-ghost border-green-500 hover:bg-white hover:text-green-500"
                                >회원 가입</a
                            >
                        </div>
                    </div>
                </div>
            </div>
        </div>
🔨 Tailwind와 Daisy Ui를 사용한 메인 페이지 HTML 코드 
# base/urls.py

from django.contrib import admin
from django.urls import path, include
from django.contrib.auth import views as auth_views

urlpatterns = [
    path('', auth_views.LoginView.as_view(template_name='main.html'), name='accounts'),
]
🔨 `url/`을 입력하면 main.html을 보여주는 path 

▶️ 전체 채팅방 목록

전체 채팅방 목록

<!--chat/templates/chat/room_list.html-->
<div class="m-24">
    <h1
        class="text-center text-4xl text-white m-10 text-gray-200 font-semibold"
    >
        Welcome to Paracord
    </h1>
    <h2 class="text-center mb-4">
        파라코드에서는 여러 사람들이랑 소통할 수 있습니다
    </h2>
    <form action="{% url 'chat:list' %}" method="GET" class="text-center">
        <input
            name="kw"
            type="text"
            placeholder="채팅방 찾기"
            class="p-4 text-lg rounded bg-gray-900 text-gray-200 w-4/5 h-12 py-2"
        />
        <button class="-ml-24 text-lg bg-green-400 text-white rounded w-20">
            검색하기
        </button>
    </form>
    <div class="text-white grid grid-cols-4 gap-4 mt-12">
        {% if rooms %} {% for room in rooms %}
        <div
            class="w-[80%] bg-gray-700 text-center border-gray-800 border rounded-[3%]"
        >
            <i class="fas fa-users fa-4x mt-10 mb-12"></i>
            <div class="mb-10">
                <div>{{ room.name }}</div>
                <div class="mb-4 mt-4">
                    {% if room.part_server %}

                    <button class="bg-green-500 rounded-full w-32 text-lg">
                        <a href="{% url 'chat:detail' room.id %}">
                            서버 참가중
                        </a>
                    </button>
                </div>
                <div>
                    <button class="bg-red-500 rounded-full w-32 text-lg">
                        <a href="{% url 'chat:exit_server' room.id %}" onclick="if (confirm('정말 나가시겠습니까?') == false) return false;">
                            서버 나가기
                        </a>
                    </button>
                    {% else %}
                    <button class="bg-green-500 rounded-full w-32 text-lg">
                        <a href="{% url 'chat:access_server' room.id %}">
                            서버 참가
                        </a>
                    </button>

                    {% endif %}
                </div>
            </div>
        </div>
        {% endfor %} {% else %}
        <h1>채팅방 is lonely...</h1>
        {% endif %}
    </div>
</div>
{% endblock %}
🔨 Tailwind와 Daisy Ui를 사용한 로그인 후 보이는 화면 HTML 코드
# chat/views.py

def room_list(request):

    kw = request.GET.get('kw')
    if kw:
        rooms = Room.objects.filter(name__icontains=kw) | Room.objects.filter(name__startswith=kw)
        if not rooms:
            messages.error(request, "검색된 채팅방이 없습니다.")
    else:
        rooms = Room.objects.prefetch_related(
            Prefetch('part_user', queryset=User.objects.filter(id=request.user.id), to_attr='part_server'))
    rooms = rooms.exclude(room_type="direct")
    users = User.objects.all()
    context = {'rooms': rooms, 'users': users}
    return render(request, 'chat/room_list.html', context)
🔨 모든 Room object를 받아와 HTML로 넘겨주는 python 코드 (kw는 채팅방 검색에서 이용된다.)

▶️ 채팅

단체 채팅

단체 채팅 이미지


다이렉트 메세지 채팅

다이렉트 메세지 채팅 이미지1다이렉트 메세지 채팅 이미지2

<!-- chat/templates/chat/room_detail.html -->

{% block app %}
    {% include 'chat/js/chat_logic.html' %}
    <div class="chat-messages text-gray-300" id="jax"></div>
{% endblock %}
🔨 chat_logic.html을 실행시켜 채팅 내용들을 받아오는 Tailwind를 사용한 HTML 코드

// chat/tamplates/chat/js/chat_logic.html

let ChickChat__lastMessageId = 0;

  function ChickChat__loadMore() {
    $.get(
      "{% url 'chat:chat' room.id %}",
      {
        from_id: ChickChat__lastMessageId,
      },
      function (data) {
        for (const chatKey in data.chats) {
          const chat = data.chats[chatKey];
          ChickChat__lastMessageId = chat.id;
          ChickChat__renderMessage(chat);
        }
        console.log(1);
        setTimeout(ChickChat__loadMore, 200);
      },
      "json"
    );
  }

  let lastNickname = null;
  let lastTimeStamp = null;
  function ChickChat__renderMessage(chat) {
    let chat_timestamp = moment(chat.timestamp).format("LT");
    console.log(chat["m_type"]);
    if (chat.m_type != "NOMAL") {
      if (chat.m_type == "ENTER") {
        $(".chat-messages").append(`
            <div class="m-8">
                <form class="bg-gray-800 text-center">
                ${chat.message}
                    <span class="text-gray-500 text-xs">
                    ${chat_timestamp}
                    </span>
                </form>
            </div>`);
      } else if (chat.m_type == "EXIT") {
        $(".chat-messages").append(`
            <div class="m-8">
                <form class="bg-gray-800 text-center">
                ${chat.message}
                    <span class="text-gray-500 text-xs">
                    ${chat_timestamp}
                    </span>
                </form>
            </div>`);
      } else {
        if (lastNickname != chat.nickname || lastTimeStamp != chat_timestamp) {
          $(".chat-messages").append(`
            <div class="flex-1 flex justify-between">
                <div class="bg-gray-700 flex-1 flex flex-col justify-between">
                    <div class="text-sm overflow-y-auto">
                        <div class="flex mx-6 pt-3 mt-3 border-gray-500 border-t">
                            <div class="flex-none">
                                <i class="fas fa-user-circle fa-3x text-gray-400"></i>
                            </div>
                            <div class="ml-5">
                                <a class="text-white hover:underline text-sm">${chat.nickname}<a>
                                <span class="text-xs text-gray-500 ml-1">
                                    ${chat_timestamp}
                                </span>
                                <div>
                                    <div class="mt-2">
                                        ${chat.message}
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            `);
        } else {
          $(".chat-messages").append(`
            <div class="flex-1 flex justify-between">
                <div class="bg-gray-700 flex-1 flex flex-col justify-between">
                    <div class="text-sm overflow-y-auto mt-1">
                        <div class="flex mx-6">
                            <div class="ml-[3em]">
                                <div class="ml-5">
                                    ${chat.message}
                                </div>
                            </div>
                        </div> 
                    </div>
                </div>
            </div>     
            `);
        }

        lastNickname = chat.nickname;
        lastTimeStamp = chat_timestamp;
      }
    }
    console.log($("#chat_scroll").scrollTop($("#chat_scroll")[0].scrollHeight));
  }
  ChickChat__loadMore();
🔨 지속적으로 서버와 통신해(Ajax) 해당 채팅방의 채팅 오브젝트를 가져오는 js코드

# chat/views.py
def room_detail(request, room_id):
    room = Room.objects.get(id=room_id)
    room_detail = "True"
    context = {'room': room, 'room_detail': room_detail}
    return render(request, 'chat/room_detail.html', context)
🔨 url에 입력받은 room.id로 Room object를 찾아 html로 넘겨주는 python 코드

▶️ 채팅방 검색

채팅방검색

)

채팅방검색2

<!--chat/templates/chat/room_list.html-->
<form action="{% url 'chat:list' %}" method="GET" class="text-center">
        <input
            name="kw"
            type="text"
            placeholder="채팅방 찾기"
            class="p-4 text-lg rounded bg-gray-900 text-gray-200 w-4/5 h-12 py-2"
        />
        <button class="-ml-24 text-lg bg-green-400 text-white rounded w-20">
            검색하기
        </button>
    </form>
🔨 입력받은 kw(room.name)를 chat:list(views.py)로 넘겨주는 Tailwind를 사용한 html 코드

# chat/views.py
kw = request.GET.get('kw')
    if kw:
        rooms = Room.objects.filter(name__icontains=kw) | Room.objects.filter(name__startswith=kw)
        if not rooms:
            messages.error(request, "검색된 채팅방이 없습니다.")
    else:
        rooms = Room.objects.prefetch_related(
            Prefetch('part_user', queryset=User.objects.filter(id=request.user.id), to_attr='part_server'))
🔨 입력받은 kw에 해당되는 Room objects가 있으면 objects를 list로 넘겨주고, 없다면 안내메시지를 넘겨주는 python 코드

▶️ 채팅방 검색 (Modal)

채팅방 검색 모달 이미지

<!--chat/templates/chat/modal/serch_room_modal.html-->
<label
    for="my-modals-2"
    class="modal_button cursor-pointer flex items-center justify-center overflow-hidden whitespace-nowrap w-14 h-14 mx-auto mt-3 text-green-400 transition-all hover:duration-[300ms] bg-gray-700 rounded-[50%] hover:text-white hover:bg-teal-500 hover:rounded-[30%] modal-button"
>
    <i class="fa-solid fa-magnifying-glass"></i>
</label>
<input type="checkbox" id="my-modals-2" class="modal-toggle" />
<div class="modal">
    <div class="modal-box flex justify-center">
        <form action="{% url 'chat:list' %}" method="GET" class="text-center">
            {% csrf_token %}
            <input
                name="kw"
                type="text"
                placeholder="채팅방 찾기"
                class="p-4 text-lg rounded bg-gray-900 text-gray-200 h-12 py-1 w-[16em]"
            />
            <label for="my-modals-2" class="btn btn-ghost bg-green-500 hover:bg-white hover:text-green-500"> 검색하기 </label>
            <label for="my-modals-2" class="btn close-are">취소</label>
        </form>
    </div>
</div>
🔨 버튼 클릭 시 kw(room.name)를 입력받는 input 창을 출력하는 Tailwind, Daisy Ui를 사용한 HTML 코드(Modal)

# chat/views.py
kw = request.GET.get('kw')
    if kw:
        rooms = Room.objects.filter(name__icontains=kw) | Room.objects.filter(name__startswith=kw)
        if not rooms:
            messages.error(request, "검색된 채팅방이 없습니다.")
    else:
        rooms = Room.objects.prefetch_related(
            Prefetch('part_user', queryset=User.objects.filter(id=request.user.id), to_attr='part_server'))
🔨 입력받은 kw에 해당되는 Room objects가 있으면 objects를 list로 넘겨주고, 없다면 안내메시지를 넘겨주는 python 코드

▶️ 채팅방 생성 (Modal)

채팅방 생성 이미지

<!--chat/templates/chat/modal/create_room_modal.html-->
<label
    for="my-modal-2"
    class="cursor-pointer flex items-center justify-center overflow-hidden whitespace-nowrap w-14 h-14 mt-3 mx-auto text-green-400 transition-all hover:duration-[300ms] bg-gray-700 rounded-[50%] hover:text-white hover:bg-teal-500 hover:rounded-[30%]"
>
    <i class="fas fa-plus fa-1x"></i>
</label>
<input type="checkbox" id="my-modal-2" class="modal-toggle hidden" />
<div class="modal">
    <div class="modal-box flex justify-center">
        <form method="post" action="{% url 'chat:create' %}">
            {% csrf_token %}
            <input
                name="name"
                type="text"
                placeholder="채팅방 생성"
                class="p-4 text-lg rounded bg-gray-900 text-gray-200 h-12 py-1 w-[16em]"
            />
            <label for="my-modal-2" class="btn btn-ghost bg-green-500 hover:bg-white hover:text-green-500">생성하기</label>
            <label for="my-modal-2" class="btn">취소</label>
        </form>
    </div>
</div>
🔨 버튼 클릭 시 name(room.name)을 입력받는 input 창을 출력하는 Tailwind, Daisy Ui를 사용한 HTML 코드(Modal)

# chat/views.py

@login_required(login_url='accounts:login')
def room_create(request):
    if request.method == "POST":
        form = RoomForm(request.POST)
        if form.is_valid():
            room = form.save(commit=False)
            room.host = request.user
            room.reg_date = timezone.now()
            room.save()
            request.user.part_server.add(room)
            messages.success(request, f"{room.name} 채팅방이 생성되었습니다.")
            return redirect('chat:detail', room_id=room.id)
        else: 
            messages.error(request, "이미 존재하는 채팅방 이름입니다.")
    else:
        messages.error(request, "정상적이지 않은 접근입니다.")
        return redirect('chat:list')
    return redirect(request.META.get('HTTP_REFERER'))
🔨입력받은 room.name으로 room object를 생성하고 해당 채팅방으로 이동 시켜주는 python 코드 / room.name이 없거나, 중복되는 room.name이 있다면 에러 메시지를 출력한다.

▶️ 채팅방나가기

채팅방 나가기 이미지

🔨 Modal 을 이용하여 다른페이지로가는 불편함을 없애고 바로 방을나갈수있다

▶️ 시스템메세지

시스템 메세지 이미지

🔨 단체채팅방에서 시스템 메세지를 통해 입/퇴장 메시지를 출력한다.

▶️ 유저 검색

유저 검색 이미지

<!--accounts/templates/friend_search.html-->
{% extends 'layout.html' %} {% block content %}
<div class="ml-8 mt-4 mb-2 text-white text-lg">친구 추가하기</div>
<div class="ml-8 mb-6">
  Paracord 닉네임을 입력하시면 친구추가를 할수있어요.
</div>
<form
  action="{% url 'accounts:searching_user' %}"
  method="GET"
  class="text-center"
>
  <input
    name="kw"
    type="text"
    placeholder="사용자명"
    class="p-4 text-lg rounded bg-gray-900 text-gray-200 w-4/5 h-12 py-2"
  />
  <button
    class="-ml-24 text-lg bg-green-400 text-white rounded w-20"
  >
    검색하기
  </button>
</form>

<div class="border-b mt-10 border-gray-600"></div>

<div class="text-white">
  <div class="ml-8 mt-4 mb-2 text-white text-lg">찾은 유저</div>
  {% if find_friend %}
  <div class="ml-8 mb-6 text-gray-400">
    친구를 찾았어요! 친구추가 버튼을 누르면 우린 친구!
  </div>
  {% for i in find_friend %}
  <br />
  <div
    class="hover:rounded ml-24 bg-gray-700 hover:bg-gray-600 w-4/5 border-b border-gray-500 text-white flex justify-between"
  >
    <button class="mt-4 mb-4 flex">
      {% if i.is_active == 0 %}
      <i class="fas fa-circle text-red-600"></i>
      <div class="ml-8">{{i.nickname}}</div>
      {% else %}
      <div>
        <i class="fas fa-circle text-green-400 mt-1"></i>
      </div>
      <div class="ml-8">
        {{i.nickname}} {% if user == i %}
        <span class="bg-gray-700">[본인]</span>
        {% endif %}
      </div>
      {% endif %}
    </button>
    <a href="{% url 'accounts:add_friend' i.id %}">
      <button class="bg-green-500 rounded w-14 mt-4">친구추가</button>
    </a>
  </div>

  {% endfor %} {% else %}
  <div class="ml-8 mb-6 text-gray-400">
    검색된 친구가 없어요.. 외롭네요ㅠㅠ
  </div>
  {% endif %}
</div>
{% endblock %}
🔨 kw(user.nickname)를 입력받는 input 창을 출력하는 Tailwind, Daisy Ui를 사용한 HTML 코드

# accounts/views.py

def searching_user(request):
    kw = request.GET.get('kw')
    find_friend = User.objects.filter(nickname__icontains=kw) | User.objects.filter(nickname__startswith=kw)

    return render(request, 'friend_search.html', {'find_friend': find_friend})
🔨 입력받은 kw에 해당되는 user object를 찾아 html로 넘겨주는 python 코드

▶️ 모든 유저 목록

https://i.postimg.cc/YC8xpT9t/unknown.png

<!--accounts/templates/all_user_list.html-->
{% extends 'layout.html' %}
{% block content %}

<div class="text-gray-200 mx-4 mt-2 h-12 my-4 ">
    <div class="ml-4 mt-4 mb-2 text-white text-lg">
        모든유저
    </div>
    <div class="text-gray-400 ml-4 mb-6 ">
        Paracord 모든 유저 목록이에요!
    </div>
</div>
{% for use in users %}
<br>
<div class="hover:rounded ml-24 bg-gray-700 hover:bg-gray-600 w-4/5 border-b border-gray-500 text-white flex justify-between">
    <button class="mt-4 mb-4 flex">
        {% if use.is_active == 0 %}
        <i class="fas fa-circle text-red-600"></i>
        <div class="ml-8">
         {{use.nickname}}
        </div>
        {% else %}
        <div>
        <i class="fas fa-circle text-green-400 mt-1"></i>
        </div>
        <div class="ml-8">
         {{use.nickname}}
         {% if user == use %}
        <span class="bg-gray-700">[본인]</span>
        {% endif %}
        </div>
        {% endif %}
    </button>
    <a href="{% url 'accounts:add_friend' use.id %}">
        <button class="bg-green-500 rounded w-14 mt-4 ">친구추가</button>
    </a>
</div>

{% endfor %}
{% endblock %}
🔨user objects를 출력하는 Tailwind, Daisy Ui를 사용한 HTML 코드

# accounts/views.py
def user_list_all(request):
    users = User.objects.all()
    context = {'users': users}
    return render(request, 'all_user_list.html', context)
🔨 모든 user object를 HTML로 넘겨주는 python 코드

▶️ 친구 목록

https://i.postimg.cc/D0qqRMHk/unknown.png

<!--accounts/templates/friend_list.html-->

{% extends 'layout.html' %} {% block content %}
<div class="text-gray-200 mx-4 mt-2 h-12 my-4">
  <div class="ml-6 mt-4 mb-2 text-white text-lg">친구</div>
  <div class="text-gray-400 ml-6 mb-6">나의 친구들이에요!</div>
</div>
<div class="ml-10 mt-8">
  <div id="tooltip-default" role="tooltip" class="inline-block invisible z-10 py-1 px-3 -ml-[33px] text-sm font-medium text-white bg-gray-900 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip dark:bg-gray-700 ">
      다이렉트 메시지
      <div class="tooltip-arrow -ml-1" data-popper-arrow></div>
  </div>
  {% for friend in user.friends.all %}
  <button class="hover:rounded ml-20 bg-gray-700 hover:bg-gray-600 w-3/4 mt-6 border-b border-gray-500 flex justify-between">
    <div class="flex mt-4">
      <div class="ml-4 text-gray-300 mb-2">
        <i class="fas fa-user-circle fa-2x"></i>
      </div>
      <div class="ml-4 text-white text-sm y-4 m-0 mt-1">{{friend}}</div>
    </div>
    <a
      class="w-10 h-10 bg-gray-800 rounded-full mt-1 z-9 mr-4"
      href="{% url 'chat:create_dm' friend.nickname %}"
      data-tooltip-target="tooltip-default"
    >
      <i class="far fa-envelope fa-lg bg-gray-700 mt-5"></i>
    </a>
  </button>
  <form action=""></form>

  {% endfor %}
</div>
{% endblock %}
🔨 로그인 유저의 friends 필터에 존재하는 user object를 출력하는 Tailwind, Daisy Ui를 사용한 HTML 코드

# accounts/views.py
def user_list(request):\
    return render(request, 'friend_list.html')
🔨friend_list.html로 이동시키는 python 코드

▶️ 온라인 유저 목록

https://i.postimg.cc/8cDFfPK9/unknown.png

<!--accounts/templates/online_user_list.html-->
{% extends 'layout.html' %}
{% block content %}

<div class="ml-8 mt-4 mb-2 text-white text-lg">
    온라인
</div>
<div class="text-gray-400 ml-8 mb-2">
    온라인 상태인 사람들이예요!
</div>
{% for use in users %}
<br>
<div
    class="hover:rounded ml-24 bg-gray-700 hover:bg-gray-600 w-4/5 border-b border-gray-500 text-white flex justify-between">
    <button class="mt-4 mb-4 flex">
        {% if use.is_active == 0 %}
        <i class="fas fa-circle text-red-300"></i>
        <div class="ml-20">
            {{use.nickname}}
        </div>
        {% else %}
        <div>
            <i class="fas fa-circle text-green-400 mt-1"></i>
        </div>
        <div class="ml-8">
            {{use.nickname}}
            {% if user == use %}
            <span class="bg-gray-700">[본인]</span>
            {% endif %}
        </div>
        {% endif %}
    </button>
    <a href="{% url 'accounts:add_friend' use.id %}">
        <button class="bg-green-500 rounded w-14 mt-4 ">친구추가</button>
    </a>
</div>
{% endfor %}
{% endblock %}
🔨user objects를 출력하는 Tailwind를 사용한 HTML 코드

# accounts/views.py
def user_list_online(request):
    users = User.objects.filter(is_active=1)
    context = {'users': users}
    return render(request, 'online_user_list.html', context)
🔨모든 user object 중 is_active값이 1인 object만 필터링하여 HTML로 넘겨주는 python 코드

⚙ 기능 테스트

👬 회원

  • ✅회원가입 : 2022-02-23
  • ✅로그인 : 2022-02-23
  • ✅로그아웃 : 2022-02-23
  • ✅로그인 로그아웃 상태 표시 : 2022-02-23
  • ✅유저 검색 : 2022-02-23
  • ✅안내 메시지 : 2022-02-23
  • ✅모든 회원 리스팅 : 2022-02-23
  • ✅모든 친구 리스팅 : 2022-02-23
  • ✅모든 온라인 유저 리스팅 : 2022-02-23

💻 채팅

  • ✅채팅방 만들기 : 2022-02-23
  • ✅채팅방 나가기 : 2022-02-23
  • ✅채팅방 삭제 : 2022-02-23
  • ✅채팅방 찾기 : 2022-02-23
  • ✅단체 채팅 : 2022-02-23
  • ✅다이렉트 메세지 채팅 : 2022-02-23
  • ✅친구찾기 : 2022-02-23
  • ✅친구추가 : 2022-02-23
  • ✅시스템 메시지 추가 : 2022-02-23
  • ✅모든 채팅방 리스팅 : 2022-02-23
  • ✅내가 참가중인 채팅방 리스팅 : 2022-02-23
  • ✅내가 만든 채팅방 리스팅 : 2022-02-23
반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.