학교폭력 문제 해결을 위한 앱, 마음이 Github ➡ https://github.com/askges20/Maeumi

 

GitHub - askges20/Maeumi: 2021 한이음 ICT 멘토링 프로젝트 [21_HF114] : Android Studio 앱

2021 한이음 ICT 멘토링 프로젝트 [21_HF114] : Android Studio 앱 - GitHub - askges20/Maeumi: 2021 한이음 ICT 멘토링 프로젝트 [21_HF114] : Android Studio 앱

github.com

 

 

앱 기능 구현을 마무리하고 8월 말에는 한이음 공모전 접수를 완료했다.

9월부터는 기존에 구현된 기능을 테스트해서 버그를 찾아내고,

개발자가 아닌 일반 사용자에게 테스트를 맡겨서 피드백을 받아보기로 했었다.

그리고 한이음 공모전 1차 발표가 나는 대로 2차 접수를 준비하기로 했다.

 


 

사용자 테스트

각자 지인으로부터 앱의 피드백을 받고 그 내용을 바탕으로 앱을 개선하기로 했다.

 

피드백 내용을 총 정리한 결과 다양한 의견을 들을 수 있었다.

각 의견의 우선순위를 파악해보고, 앱의 로직 상 수정하기 어려운 부분을 제외하고

앱에 반영한 의견은 다음과 같았다.

 

디자인

  • 메인화면 버튼에 그림자 효과 또는 테두리를 넣어서 배경과 구분하면 좋겠다.
  • (가장 많이 나온 의견) 앱의 폰트가 삐뚤빼뚤하고 전체적으로 가독성이 낮다. 다른 것으로 바꾸는게 나을듯

진단테스트

  • 테스트 이용안내 페이지 가독성을 높일 필요가 있다. (줄 간격, 유의사항 배치 등)
  • 테스트 결과 멘트의 수정이 필요할 것 같다.

일기장

  • 일기장 기능이 제공 목적을 안내하면 좋겠다.
  • 일기장 캘린더에서 주말은 다른 색으로 표시하면 좋겠다.
  • 학교폭력 증거물로 활용되니까 일기 작성 시간을 기록하여 타임스탬프를 남기면 좋겠다.

게시판

  • 게시판의 글 목록에서 글 내용을 미리보기로 한 줄 정도 넣으면 좋을 것 같다.

상담

  • 채팅 메세지 길이가 한줄이면 말풍선 길이가 그에 맞게 짧아지도록 수정하면 좋을 것 같다.

 

이미 몇 달간 이 앱을 개발한 개발자 입장에서는 기존 화면이 너무 눈에 익어서 수정할 부분을 찾기 어려웠는데

사용자 테스트를 통해 디자인적으로 개선할 부분을 많이 찾을 수 있었다.

그리고 팀원끼리 피드백을 했을 때도 앱의 전체적인 색감과 메인 화면 배치를 수정하면 좋겠다는 의견이 나왔기 때문에 앱 전체적으로 수정을 했다.

 

 

이 외에도 사용자 피드백에서 상담 진행 시 AI의 목소리를 넣으면 어떨까,

사용자의 의견을 들을 수 있는 기능을 추가하면 어떨까,

주기적으로 테스트를 할 수 있게 하고 결과를 그래프로 나타내서 학생이 자신의 변화된 모습을 파악할 수 있게 하면 어떨까 등

생각도 못하고 있던 다양한 아이디어를 들을 수 있었다.

 

 

한이음 프로젝트를 진행하면서 여러 사람의 의견을 모으다 보면 새로운 아이디어를 창출해낼 수 있다는 것을 체감했고

이번에 사용자 테스트를 통해 개발자가 아닌 일반 사용자들의 관점에서의 의견을 들어보고 앱을 개선할 수 있는 기회를 갖게 되어 좋았다!

 

(사용자 테스트에 참여해주신 모든 분들께 감사 인사를...😘)

 


UI 개선

메인 화면 수정 전 (8월) -> 수정 중 (9월) -> 현재 모습 비교 (10월)

 

수정 전의 메인 화면은 폰트의 가독성이 낮고, 버튼의 크기가 너무 크다는 의견이 있었다.

따라서 앱 전체 폰트를 깔끔한 나눔스퀘어 체로 바꾸었고 버튼의 배치를 수정했다.

 

최종적으로는 버튼에 테두리를 넣음으로써 배경과 구분할 수 있도록 했고 색감은 베이지 계열, 파스텔톤으로 통일했다.

이렇게 비교 과정을 살펴보니 많이 개선된게 느껴진다😏

 

일기장 캘린더, 일기 내용 페이지

 

일기장 캘린더에는 일요일날 빨간 글씨로 표시하도록 수정했고

일기 작성/수정 시에 타임스탬프가 남아서 내용을 조작할 수 없도록 의도했다.

 

상담 수정 전 -> 후

 

채팅으로 진행되는 상담은 텍스트 내용이 많기 때문에 가독성이 중요한데

기존 폰트를 적용했을 땐 가독성이 매우 저하되었다.

 

폰트를 바꾸고 줄 간격을 조절하니 훨씬 깔끔하고 읽기 편안해졌다.

그리고 메세지가 짧을 경우 말풍선이 그 길이에 맞게 너비를 갖도록 수정했다.

 

게시판 글 목록

 

게시판에는 각 게시글의 내용 미리보기를 추가했다.

 

이 외에 앱에서 수정된 점은

  • 일기장 안내 페이지 추가
  • 마음 채우기 안내 페이지 내용 추가
  • 알림 아이콘 이미지 수정
  • 테스트 결과 문구 수정

등이 있었다. 이로서 개선할 점은 다 고친 것 같고 디버깅만 더 하면 개발은 끝날 것 같다.

 


 

한이음 공모전

8월 26일 공모전 접수를 하고 9월 14일에 1차 합격 결과를 받았다.

2차 평가는 제출했던 개발보고서를 수정하고, 시연 동영상!!을 제출해야한다.

따라서 앱 UI 개선을 완료한 후에 앱 시연 영상을 녹화하고 편집하는 중이다.

2차 평가에서 합격하면 입선 수상은 확보하는건데 꼭 합격했으면 좋겠다🙏

 


 

논문 작성

9월 말에 멘토님과 온라인 미팅이 있었다. 그 때 학술 대회에 참가하는 것도 좋을거라는 멘토님의 말씀에

늦게나마 한국정보처리학회에서 주관하는 ACK 2021(KIPS 추계학술발표대회)를 접수해보기로 했다.

 

지금까지 개발 보고서는 종종 작성해보았으나 논문을 작성하는 것은 처음이라

어떻게 구성해야 할지, 어떤 관점에서 내용을 작성해야할지 많은 고민이 되었다.

 

아직 논문을 한창 작성하고 있고 고쳐야할 부분도 많이 있지만

논문을 작성해보았다는 경험 자체가 큰 의미가 있을 것이라 생각한다!


 

10월 계획

한이음 ICT 멘토링이 11월 30일까지 진행되는데, 벌써 10월이 되었다.

앞서 언급한대로 한이음 공모전 2차 접수를 위해 영상을 제작하고 개발보고서를 수정할 것이며 ACK 2021 논문 투고를 하려고 한다.

결과 나오는거에 따라서 추후 일정이 달라질 것 같다! 모든 일이 잘 풀렸으면 좋겠다 🍀

채팅 기능 구현을 얼추 마무리하고 다음으로 구현한 것은 마이페이지였다.

현재 마이페이지에 들어간 내용은 자신의 프로필을 확인하고, 프로필을 수정하는 것이다.

MyProfile.js에서 프로필 확인 페이지(ShowProfile.js) 또는 프로필 수정 페이지(EditProfile.js)가 뜨도록 했다.

 

 

ShowProfile.js

 

레이아웃은 배경 이미지, 프로필 사진, 소개글, 이메일, 가입 날짜가 출력되도록 배치를 했고

현재 실제 데이터를 출력하는 부분은 프로필 사진, 소개글, 이메일이다.

 

배경 이미지는 기본으로 되어있어서 이 이미지를 고정으로 할지, 사용자가 변경할 수 있게 할지 고민중이고

가입 날짜는 나중에 회원가입 구현 코드를 수정해서 날짜를 저장하고 불러올 예정이다.

 

 

EditProfile.js

 

프로필 수정 화면이다. 수정할 수 있는 항목이 몇 가지 없어서 굉장히 심플한 모습..^^

Material UI의 TextField를 이용했다.

 

 

프로필 이미지는 <input type="file">을 이용해서 위와 같이 파일을 선택할 수 있었고

파일 선택 직후에 적용될 프로필 미리보기는 http://yoonbumtae.com/?p=3304 이 포스팅을 참고해서 작성했다.

수정 완료 버튼을 클릭하면 Firebase DB에 이름과 소개글을 업데이트하고

Firebase Storage에 이미지를 저장하도록 구현했다.

 

Storage와 관련된 주요 코드만 나타낸 것 👇

 

services - firebase.js

import firebase from 'firebase/compat/app';
import 'firebase/compat/storage';

const firebaseConfig = {
	...
    storageBucket: "버킷아이디",
    ...
};

...
firebase.initializeApp(firebaseConfig);
const storage = firebase.storage();

export {storage};

 

profile - EditProfile.js 에서 이미지 업로드하기

import { firestore, storage } from "../services/firebase";

const EditProfile = (props) => {
    const [image, setImage] = useState('');	//이미지를 변경하면 상태 변경
    const id = useSelector(state => state.user.id);	//redux에 저장된 사용자 아이디

    const uploadImg = () => {
        if (image === '') {
            return;
        }
        //사용자 아이디를 이미지 이름으로 지정해서 storage 업로드
        storage.ref(`/profile/${id}`).put(image).then(() => {   
        	//업로드 이후 처리
        });
    }

 

profile - MyProfile.js 에서 Storage 이미지 가져오기

  const id = useSelector(state => state.user.id);

  function changeImg() {
    imgUrl = storage.ref(`profile/${id}`).getDownloadURL().then((url) => {
      //받아온 url 처리
    })
  }

 

코드는 간단하다. ref로 경로를 지정하고 put으로 업로드, getDownloadURL로 가져오면 된다.

 

이미지 변경을 완료하고 나서 사이트에 바로 반영하기 위해

이미지 url을 상태로 가지고 프로필 수정 완료 시 상태를 변경하도록 했다.

 

 

프로필 수정은 쉽게 구현했고 이제 웹사이트 전체 레이아웃을 수정한 다음, 단체방 생성 기능 구현을 고려해봐야겠다.

 

채팅방 목록 띄우기

사용자가 속한 채팅방 목록을 조회하고, 최신순으로 정렬하는 것을 구현했다.

Firebase의 onValue를 두 번 사용해야해서 이번에도 함수형 컴포넌트를 클래스형 컴포넌트로 변경하는 과정이 필요했다.

 

첫번째 onValue는 사용자가 속한 채팅방 번호를 모두 불러와서 상태에 저장하고, 두번째 onValue를 호출한다.

두번째 onValue는 각 채팅방의 가장 마지막 메세지를 구해서 최신순으로 정렬하고 상태에 저장하는 것이다.

라이프 사이클 함수인 componentDidMount 안에서 첫번째 onValue를 호출하도록 했다.

 

채팅방 번호와 각 채팅방의 마지막 메세지를 모두 state로 관리하므로

새로운 채팅방이 생겼거나, 기존 채팅방에서 새로운 메세지가 온다면 re-rendering 하는 것이다.

 

전체 코드는 여기 👇

 

GitHub - askges20/messenger-web-react: React 개인 프로젝트 웹 메신저 만들기

React 개인 프로젝트 웹 메신저 만들기. Contribute to askges20/messenger-web-react development by creating an account on GitHub.

github.com

 

크롬과 엣지에서 서로 다른 계정으로 접속해서 테스트를 해봤는데

아직 계정 2개까지만 테스트를 해봐서 그 이상은 오류가 발생할 수도 있다.

나중에 테스트해보고 오류가 있으면 수정할 예정!

 


 

채팅방 최신순 정렬

리액트로 구현한 채팅 시스템 코드를 참고하려고 구글링하다가 채팅방 최신순 정렬 코드를 발견했다.

그리고 약간 수정해서 적용했더니 다행히 잘 작동된다.

 

lastMessageFromFB.sort((a, b) => { return sortChatRoom(a, b)})

 

sortChatRoom 함수

export const sortChatRoom = (a, b) => {
  if (a && b) {
    const aDateTime = a.dateTime;
    const bDateTime = b.dateTime;

    if (aDateTime.slice(0, 8) === bDateTime.slice(0, 8)) {  //날짜
      if (aDateTime.slice(8, 10) === bDateTime.slice(8, 10)) {  //시
        if (aDateTime.slice(11, 13) === bDateTime.slice(11, 13)) {  //분
          return bDateTime.slice(14, 16) - aDateTime.slice(14, 16); //초
        }
        return bDateTime.slice(11, 13) - aDateTime.slice(11, 13);
      }
      return bDateTime.slice(8, 10) - aDateTime.slice(8, 10);
    }
    return bDateTime.slice(0, 8) - aDateTime.slice(0, 8);
  }
}

 

나는 전송 날짜, 시간을 하나의 문자열로 이어붙여서 계속 slice를 해주었는데

형식을 보자면 중첩 if문을 작성해서 연도부터 월, 일, 시, 분, 초 순서대로 비교해주면 된다.

 

자바스크립트에서 sort를 이용할 때 그 기준을 지정하는 방법을 알게 되었다.

 


 

디자인 개선

기능 개발만 계속 하다가 오랜만에 웹사이트 디자인을 개선해보았다.

일단 전체적인 색감은 핑크 -> 블루로 맞췄고, 리액트 Material UI의 TextField와 LinearProgress를 사용해보았다.

 

TextField 공식 문서 : https://mui.com/components/text-fields/

 

Text Field React component - MUI

Text fields let users enter and edit text.

mui.com

LinearProgress 공식 문서 : https://mui.com/components/progress/

 

Circular, Linear progress React components - MUI

Progress indicators commonly known as spinners, express an unspecified wait time or display the length of a process.

mui.com

 

 

현재 폼 양식이 쓰이는 곳은 회원가입, 로그인 화면이 있어서 TextField를 적용해서 디자인을 개선했다.

같은 TextField 컴포넌트이지만 variant 속성을 다르게 해서 다른 디자인의 input을 만들 수 있었다.

 

폼은 Formik 라이브러리를 사용해서 에러 메세지를 출력했는데, 이것을 커스텀하기 위해 ErrorMessage 컴포넌트를 이용해서 글씨를 파란색으로 바꿨다.

공식 문서 : https://formik.org/docs/api/errormessage

 

| Formik

Copyright © 2020 Formium, Inc. All rights reserved.

formik.org

 

 

 

친구 검색 화면은 배경색과 버튼색만 바꿨다.

 

 

 

그리고 오늘 구현한 채팅 목록 화면~!

테스트 결과 실시간으로 채팅이 전송할/전송받을 때마다 최신순으로 정렬되는 것을 확인했다.

아직 안읽은 메세지 개수 구하는건 구현하지 않아서 일단 레이아웃만 잡고 숫자 2가 뜨도록 했다ㅋㅋ

 

 

 

채팅방 목록을 클릭해서 해당 채팅방으로 이동할 수 있다.

아직 참여자 이름 대신 아이디가 뜨도록 되어있는데 나중에 고치려고 한다.

 


로딩 화면 만들기

Material UI의 LinearProgress은 로딩 화면을 만들 때 사용했다.

App.js에서 리액트의 라우팅 처리로 사용자 정보가 없을 땐 (로그인하지 않았을 때) 웰컴 화면이, 사용자 정보를 불러왔을 땐 (로그인했을 때) 메인 화면이 뜨도록 코드를 작성했었다.

 

그런데 로그인한 사용자 정보를 redux로 저장해놓고 불러오다보니 로그인한 상태여도 웰컴 화면이 잠깐 떴다가 메인 화면이 뜨는 경우가 자주 발생했다.

따라서 redux에 저장된 사용자 정보를 조회하기 전에는 로딩 화면이, 그 이후엔 웰컴 or 메인 화면이 뜨도록 수정했다.

 


 

채팅방 목록 구현에 성공해서 일단 큰 고비(?)는 넘겼고

이제 프로필 수정 기능을 구현할지, 안읽은 메세지 개수 구하는 것을 구현할지 고민해봐야겠다.

현재 채팅이 자동 스크롤되도록 구현된 상태인데

이것은 Firebase Realtime Database의 onValue 메소드를 이용해서

새로운 채팅이 등록될 때마다 스크롤이 되는 것이었다.

 

평소에 카톡을 이용해보면

1. 채팅방에 입장했을 때

2. 내가 채팅을 전송했을 때만

자동 스크롤이 되고, 다른 사람으로부터 채팅을 전송받았을 때는 스크롤이 되지 않는다.

그래서 이것을 구현하기로 했다.

 

채팅방에 입장했을 때 채팅 기록을 state로 관리하고 있다.

따라서 가장 마지막으로 저장된 채팅의 senderId를 현재 로그인한 사용자의 Id와 비교해서 같을 때만 자동 스크롤이 되도록 수정했다.

 

 


 

그리고 현재는 사이드바에 있는 친구 목록을 클릭해서 해당 친구와 1:1 채팅방으로 이동할 수 있는 상태이다.

일반적인 채팅 앱에서는 친구 목록과 별개로 채팅방 목록을 보여주므로 이것을 구현하기 위해 DB 구조를 추가했다.

 

1. 사용자마다 자신이 속한 채팅방 번호를 가지도록 추가

루트

ㄴMyChatRooms

  ㄴ아이디

    ㄴ채팅방 번호

      ㄴisMember: true

 

로그인한 사용자가 속한 채팅방을 빠르게 구하기 위해 추가한 것이다.

 

2. 채팅방마다 마지막으로 전송된 메세지를 따로 가지도록 추가

루트

ㄴChatRooms

  ㄴ채팅방 번호

  ㄴChatMembers

  ㄴChatMessages

  ㄴLastMessage

    ㄴcontent: 채팅 내용

    ㄴdateTime: 전송 시간 (yyyyMMddhh:mm:ss)

    ㄴsenderId: 아이디

 

마지막 메세지를 알아야하는 이유는 채팅방 목록을 최신순으로 정렬해야하기 때문이다.

기존의 채팅 내역은 ChatRooms - ChatMessages 하위에 저장되어 있는데

채팅방 목록을 불러올 때 사용자가 속한 모든 채팅방의 채팅 내역을 읽는 것은 비효율적이라고 생각했다.

따라서 ChatRooms - LastMessage 를 따로 추가해준 것이다.

한이음 프로젝트로 앱 개발을 거의 마무리하고 사용자 테스트를 진행하고 있는데

오늘 회의에서 얘기한 결과, 폰트의 가독성에 대한 피드백이 많이 있어서 폰트를 수정했다.

 

나눔스퀘어체로 수정했는데 글자 자체는 깔끔하지만 줄간격이 너무 좁아서 빼곡한 느낌이 있었다.

 

 

앱에서 제공하는 진단테스트, 상담 채팅 기능에서는 한 화면에 많은 양의 텍스트가 보이는데

이렇게 글자가 빼곡하면 가독성이 저하된다고 판단해서 줄간격을 넓히는 방법을 찾아보았다.

 

방법은 android:lineSpacingExtra 속성을 사용하는 것이었다.

 

<TextView
...
android:lineSpacingExtra="5dp"
/>

 

이런 식으로 특정 TextView에 직접 속성을 지정해줘도 되고

나는 앱 전체 텍스트에 적용이 되도록 style.xml에 TextView의 스타일을 지정했다.

 

values > style.xml

  <style name="TextViewFontStyle" parent="@android:style/Widget.DeviceDefault.TextView">
      <item name="android:fontFamily">@font/custom_font_family</item>
      <item name="android:textSize">16sp</item>
      <item name="android:lineSpacingExtra">5sp</item>
  </style>

 

참고로 custom_font_family는 폰트자체 속성을 지정하기 위해 작성한 것으로, 코드는 다음과 같다.

 

font > custom_font.family.xml

<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <font
        android:font="@font/nanumsquare"
        android:fontStyle="normal"
        android:fontWeight="500"
        app:font="@font/nanumsquare"
        app:fontStyle="normal"
        app:fontWeight="500"
        tools:ignore="ResourceCycle" />

</font-family>

 

font 폴더에 있는 나눔스퀘어 ttf를 지정했다.

 


 

 

줄간격 수정 전후를 비교해보았을 때 가독성이 훨씬 좋아졌다.

8월의 주요 개발 목표는 다음과 같았다.

  • 테스트 결과 게시판 기능 제한
  • 교화 프로그램 시청 (마음 채우기 = 유튜브 영상 시청)
  • 알림 기능 추가
  • 기타 레이아웃 보완 및 디버깅

 


 

테스트 결과 기능 제한

학교폭력 진단테스트 결과를 바탕으로 마음 온도를 산출한다.

마음 온도가 낮은 사용자는 학교폭력 관련 영상 (교화 프로그램) 시청을 통해 마음 온도를 높이고

60점 이상을 달성해야 게시판 기능을 이용할 수 있게 제한하도록 구현했다.

 

 

테스트 결과 가해 정도에 따라 마음 온도가 산출되는데

이때 마음 온도가 60점 보다 낮으면 마음 채우기 안내 페이지로 이동하며

60점 이상을 달성하기 전까진 게시판 접근이 제한된다.

 

 

교화 프로그램 시청 (마음 채우기)

 

유튜브에 있는 학교폭력 영상을 앱 안에서 시청할 수 있도록 YouTube API를 활용하여 기능을 구현했다.

유튜브 썸네일은 YouTubeThumbnailView를 이용했고 각 영상의 제목, 설명, 시청 여부를 함께 표시한다.

전체 영상 개수와 시청한 영상 개수도 상단에 표시했다.

 

(YouTubeThumbnailView 공식 문서 : https://developers.google.cn/youtube/android/player/reference/com/google/android/youtube/player/YouTubeThumbnailView.html?hl=ko

 

YouTubeThumbnailView  |  YouTube for Android  |  Google Developers

Javadoc API documentation for YouTube Android Player API.

developers.google.cn

 

영상 목록도 진단테스트와 마찬가지로 ListView를 이용하여 Adapter 패턴을 적용했다.

 

 

영상 시청 화면으로 이동하면 유튜브 영상 플레이어와 (YouTubePlayerView 이용) 누적 시청 시간을 나타내는 seekbar을 확인할 수 있다.

 

영상 시청 시간을 체크하도록 구현하는 부분이 생각보다 어려웠는데

단순히 현재의 영상 타임라인을 구하는 것은 쉽지만만약 제대로 시청하지 않고 타임라인을 건너뛰어서 영상의 맨 뒤로 이동하면 그것을 알 수 있는 방법이 없었기 때문이다.

 

 

따라서 나는 스레드를 이용해서 직접 이 코드를 구현했다.

 

영상 시청 화면 액티비티는 YouTubeBaseActivity를 상속받았고YoutubePlayerView에 setPlaybackEventListener, setPlayerStateChangeListener를 설정하여영상 재생 상태와 사용자로 인한 이벤트에 대한 처리를 할 수 있었다.

 

👇

 

 

GitHub - askges20/Maeumi: 2021 한이음 ICT 멘토링 프로젝트 [21_HF114] : Android Studio 앱

2021 한이음 ICT 멘토링 프로젝트 [21_HF114] : Android Studio 앱 - GitHub - askges20/Maeumi: 2021 한이음 ICT 멘토링 프로젝트 [21_HF114] : Android Studio 앱

github.com

 

이것과 스레드를 이용해서 만든 누적 시청 시간 카운터를 연결했다.카운터는 1초에 1번씩 영상 재생 상태를 확인하여 누적 시간을 카운트한다.

 

👇

 

 

GitHub - askges20/Maeumi: 2021 한이음 ICT 멘토링 프로젝트 [21_HF114] : Android Studio 앱

2021 한이음 ICT 멘토링 프로젝트 [21_HF114] : Android Studio 앱 - GitHub - askges20/Maeumi: 2021 한이음 ICT 멘토링 프로젝트 [21_HF114] : Android Studio 앱

github.com

 

최대한 YouTube API 자체에서 제공하는 메소드를 활용하고 싶었는데

이 부분은 아무리 구글링해봐도 실제 누적 시청 시간을 구하는 코드가 없는 것 같았다.

 

(혹시나 저와 같은 어려움을 겪고 있는 개발자분이 있으시다면 제 코드를 참고해보시길...!)

 

 

 

그렇게 정직하게 영상을 끝까지 시청해야만 마음 온도가 5점씩 오르고,

60점을 달성하면 위와 같은 팝업창으로 사용자에게 기능 해제를 알리도록 구현했다.

 

 

게시판 카테고리 세분화

기존 게시판은 자유게시판과 익명게시판으로 나뉘어져서 익명게시판은 댓글이 없다는 점에서 차이가 있었다.

그런데 이렇게 게시판을 나누는 것은 사용자 입장에서 큰 의미가 없을 것 같아서

게시판을 어떻게 수정할까 고민하다가 카테고리를 조금 더 늘리기로 했다.

 

 

질문, 꿀팁 게시판이 추가되어 게시판 카테고리가 총 4개로 늘었다.

원래는 마음 온도에 따라 기능 제한을 할 때 일부 카테고리만 제한 or 댓글 작성만 제한하는 것으로 얘기를 했었는데

다시 회의를 거쳐서 기능 제한 시에는 게시판 자체를 접근하지 못하는 것으로 계획을 변경했다.

 

그게 개발자 입장에서 구현할 때 가장 편한 방법이기도 하고

사용자 입장에서 댓글을 달지 못하더라도 게시글 내용을 확인할 수 있으면

기능 제한을 해제하기 위해 마음 채우기 기능을 이용하고 싶다는 동기부여가 되지 않을 것 같았기 때문이다.

 

 

알림 기능

앱이 전체적으로 정적이라는 생각이 들어서, 어떻게 하면 동적으로 만들 수 있을까 고민해보니

기능 제한 또는 내 게시글에 새로운 댓글이 추가되었다는 것을 알리는 알림 기능의 구현이 필요해보였다.

 

 

회원가입 / 댓글 달렸을 때 / 마음 온도 60점 달성 시 알림이 추가되도록 했고

알림을 길게 누르면 개별 알림 삭제가 가능하도록 했다.

 

 

기타 개선 사항

  • 일기장, 게시판에 등록된 이미지 확대 기능 (image-zoom-view 라이브러리 활용)
  • 게시판 레이아웃 보완, 유효성 검사
  • 디버깅 및 오류 픽스 (댓글 알림을 클릭해서 해당 게시글로 이동했을 때 DB에서 공감 수를 제대로 받아오지 못하는 현상 해결 등)

 


 

8월 개발 일지 목록

  • 8/1 ~ 8/2 (로딩 화면 DB 데이터 조회 문제 해결, 챗봇에서 일기장으로 이동)
 

2021.08.01 - 08.02 개발 일지 (로딩 화면 DB 데이터 조회 문제 해결, 챗봇에서 일기장으로 이동)

1. 로딩 화면(스플래시 화면)에서 firebase DB 데이터 불러오는 문제 해결 - 스레드의 run 메소드 안에 DB 조회 코드가 들어있던 것이 문제... 스레드 밖으로 해당 코드를 뺐더니 해결! - 하지만 지금은

askges20.tistory.com

 

  • 8/6 (마음 온도 산출, 마음 채우기 안내, 메인화면 디자인 보완)
 

2021.08.06 개발 일지 (마음 온도 산출, 마음 채우기 안내, 메인화면 디자인 보완)

테스트 결과를 바탕으로 마음 온도를 산출하고, 마음 채우기 페이지를 안내하는 것을 중점적으로 구현했다!!😎 1. 테스트 결과 확인 후 마음 온도 DB에 저장 2. 테스트 결과 화면에 "마음 채우기

askges20.tistory.com

 

  • 8/8 (YouTube API 적용 완료, 챗봇 링크 활성화, 게시판 카테고리 세분화, 게시판 기능 제한)
 

2021.08.08 개발 일지 (YouTube API 적용 완료, 챗봇 링크 활성화, 게시판 카테고리 세분화, 게시판 기능

회의 내용 오늘 오전 9시에 회의에서 마음 채우기 프로그램의 영상 개수, 마음 온도 높이는 기준과 게시판에 대해 얘기했다. 교화 프로그램(마음 채우기)에서 제공할 영상의 개수는 10개, 사용자

askges20.tistory.com

 

  • 8/9 ~ 8/10 (마음 채우기 유튜브 영상 시청 기능)
 

2021.08.09 - 08.10 개발 일지 (마음 채우기 유튜브 영상 시청 기능)

안드로이드 스튜디오에서 YouTube API를 이용하여 유튜브 영상을 앱 화면에 띄우는 것은 어제 완료했었다. 오늘은 본격적으로 마음 채우기 (영상 시청) 기능을 구현했다. 구현하면서 고려한 점 1.

askges20.tistory.com

 

  • 8/12 ~ 8/13 (에뮬레이션 이용, 마음 채우기 팝업 추가, 화면 회전 방지)
 

2021.08.12 - 08.13 개발 일지 (에뮬레이션 이용, 마음 채우기 팝업 추가, 화면 회전 방지)

며칠 전에 YouTube API 연결을 시도할 때만 해도 갈 길이 멀다고 생각했는데 생각보다 빠른 기간 안에 기능을 거의 다 구현한 듯 하다. 오늘은 주로 영상 시청 관련 디테일을 추가하고, 디버깅을 하

askges20.tistory.com

 

  • 8/14 ~ 8/15 (게시판 레이아웃 보완, 유효성 검사, image-zoom-view 적용)
 

2021.08.14 - 08.15 개발 일지 (게시판 레이아웃 보완, 유효성 검사, image-zoom-view 적용)

앱에서 제공할 기능들은 다 완성한 상태라 이제 앱 전반적인 레이아웃과 디자인만 보완하면 될 듯 하다. 그래서 오늘은 게시판 레이아웃을 전체적으로 손보고 유효성 검사를 추가했다. 개발 내

askges20.tistory.com

 

  • 8/16 ~ 8/17 (알림 기능 추가)
 

2021.08.16 - 08.17 개발 일지 (알림 기능 추가)

앱에서 게시판이라는 커뮤니티 기능을 제공해서 글을 작성하고 댓글로 소통할 수 있긴 하지만 어딘가 정적이라는 느낌이 들어서 해결 방법을 여러가지 생각해보았고 그 결과 알림 기능을 추가

askges20.tistory.com

 

이 외에도 앱 레이아웃 개선 등을 했는데 자잘한 오류를 픽스하는 과정이라 개발 일지는 따로 작성하지 않았다.

 


 

한이음 공모전 참가

8월 30일까지 한이음 공모전 참가 서류를 제출해야해서 8월 중순부터 말까지는 계속 개발보고서와 제작설계서를 작성했다.

7월 중간점검때 제출하기 위해 중간보고서를 한번 작성하긴 했지만

7~8월에 워낙 많은 기능을 구현했다보니 추가 작성해야할 내용이 꽤 많았다.

팀원 모두 열심히 작성한 덕분에 마감일로부터 4일 전인 8월 26일에 접수 완료😎

 

 

9월 이후 계획

이제 기능 자체는 구현이 모두 완료되었으니, 주변 지인들에게 배포하여 사용자 테스트를 진행하며 피드백을 받아보려 한다.

몇 달간 개발을 하다보니 이미 메인화면, 기타 화면의 UI에 너무 익숙해졌다...

사용자 테스트를 통해 앱을 아예 처음 접하는 사용자가 객관적으로 평가를 해줄 필요성을 느꼈다.

UI/UX를 개선해서 2차 공모전 접수 시 시연 영상을 촬영할 수 있도록 해야겠다.

 

추가로 2학기에 교내에서 진행하는 비교과 프로그램에도 참가할 예정이다.

1학기 비교과 프로그램에서 시작한 프로젝트인데 이렇게 개발을 거의 마무리하고 공모전에 참가하게 되다니ㅎㅎ

비교과 프로그램에서 두 차례에 걸쳐 공모전 참가 관련 컨설팅도 해준다고 하니 도움이 될 것이라고 기대한다.

 

막학기에 취준도 하면서, 한이음 프로젝트도 끝까지 열심히 참여해서 이번 학기에 마무리하자 파이팅!😉

채팅 내역 불러오기 및 실시간 업데이트

채팅 내역 Realtime Database에서 불러오고 실시간 업데이트를 하도록 구현 완료했다!

Realtime DB의 onValue 메소드를 이용하면 쉽게 구현할 수 있었다.

 

export function getChatHistory(chatRoomNum) {
    return ref(database, '/ChatRooms/' + chatRoomNum + '/ChatMessages/');
}

ref를 이용해서 DB 경로를 지정하고

 

import { onValue } from '@firebase/database';

const ChatRoom = (props) => {
	...
    const chatHistoryRef = getChatHistory(chatRoomNum);

    useEffect(() => {
        onValue(chatHistoryRef, (snapshot) => {
            let chatFromFB = []
            snapshot.forEach((chatDate) => {
                chatDate.forEach((chat) => {
                    const content = chat.val().content;
                    const senderId = chat.val().senderId;
                    const time = chat.val().time;
                    chatFromFB.push({content, senderId, time});
                })
            })
            setChatHistory(chatFromFB);	//상태 업데이트 -> 리렌더링
        });
 	...

채팅 내역을 불러올 js의 useEffect에서 onValue 메소드를 작성해주면 지속적으로 업데이트가 된다.

Firestore을 이용해서 전체 조회를 할 수 있도록 그렇게 고민을 했었는데 이렇게 쉽게 만들 수 있는거였다니...🥺

 

forEach로 돌릴 수 있게 되어서 각 날짜 별 채팅 가장 상단에 날짜를 표시하면 좋겠다 싶어서 ChatDateLine.js를 새로 만들고

 

{
    this.state.chatHistory.map((value, i) => {
        if (!value.senderId){
            return (
                <ChatDateLine key={i} year={value.year} month={value.month} date={value.date}/>
            )
        } else if (value.senderId == this.props.loginId){
            return (
                <SendChatMessage key={i} content={value.content} time={value.time}/>
            );
        } else {
            return (
                <ReceiveChatMessage key={i} content={value.content} time={value.time} friendName={this.friendName}/>
            );
        }
    })
}

JSX 안에서 조건부 렌더링을 하는데

수신자 아이디가 없으면 날짜를, 아이디가 현재 로그인한 아이디와 같으면 내가 보낸 메세지로,

아이디가 다르면 받은 메세지를 출력하도록 한 것이다.

map을 할 때는 유니크한 key를 지정해주는 것을 잊지 말자!

 


 

채팅 자동 스크롤 & react-custom-scrollbars을 이용한 커스텀 스크롤

일반적으로 채팅방에 입장했을 때, 내가 채팅을 전송했을 때 채팅의 가장 아래 부분으로 자동 스크롤이 된다.

이것을 구현하기 위해 여러 방법을 시도해보았는데

useEffect 안에서 상태 업데이트 후 스크롤 코드를 작성했더니 마지막 메세지 바로 위까지만 스크롤이 되는 것이다.

 

이것은 상태가 바뀌기 전의 렌더링 상태에서 스크롤 높이 계산을 하기 때문이었고

결국 클래스형 컴포넌트의 ComponentDidUpdate 라이프 사이클 함수를 사용하기 위해

함수형 컴포넌트를 클래스형 컴포넌트로 변경하는 수정 작업을 해야했다.

 

클래스형 컴포넌트로 변경하면서

1. useSelector 대신 connect 함수와 mapStateToProps 이용

2. useRef 대신 createRef 이용

3. 상태를 변경하기 위해 useState 대신 setState 이용

4. constructor 작성 등

수정할 부분이 은근히 많아서 시간이 꽤 걸렸다.

 

 

수정을 완료한 후에는 HTML의 기본 스크롤이 너무 투박해보여서 react-custom-scrollbar 라이브러리를 적용했다.

 

https://github.com/malte-wessel/react-custom-scrollbars

 

GitHub - malte-wessel/react-custom-scrollbars: React scrollbars component

React scrollbars component. Contribute to malte-wessel/react-custom-scrollbars development by creating an account on GitHub.

github.com

 

스크롤 모양도 훨씬 나아졌고 autoHide 속성을 이용해서 일정 시간이 지나면 자동으로 스크롤을 숨길 수 있었다.

자동 스크롤은 scrollToBottom() 메소드를 이용해서 구현할 수 있었다.

 

 

이 라이브러리를 이용하다보니 자동으로 가로 스크롤도 생성이 되길래 이것을 없애는 방법을 찾아보았고

 

https://github.com/malte-wessel/react-custom-scrollbars/issues/213

 

How to disable the horizontal scrollbar? · Issue #213 · malte-wessel/react-custom-scrollbars

 

github.com

해당 issue에 나온 첫번째 답변대로 renderTrackHorizontal 속성 안의 style에서 display: 'none'을 지정하여 해결했다.

다른 답변처럼 overflowX: 'hidden'도 작성해보았는데 이것은 작동하지 않았다.

 


기타 수정 사항

1. 채팅방 입장 시 input 박스에 focus 주기

2. 엔터키 입력 시 채팅 전송하기

3. input 박스가 전송 버튼에 가려지지 않도록 수정

 

개선해야할 사항

채팅방 팝업 띄우는 부분을 window.open으로 작성했는데,

창 크기를 고정하기 위해 resizeable=no 옵션을 추가했지만 적용되지 않길래 찾아봤더니

 

https://stackoverflow.com/questions/15480252/make-window-not-resizable-in-chrome/15481333

 

Make window not resizable in Chrome

I have some JavaScript that I use to make a window not resizable, something along the lines of: window.open(URL, id, "resizable=no"); This works in most browsers but it appears to not be supporte...

stackoverflow.com

이것은 IE에서만 작동하는 옵션이고, 최신 브라우저는 지원하지 않는다고 한다.

window.open 자체가 old school JS라며 다른 방법을 이용할 것을 권장하는 것으로 보인다.

새로운 창을 띄우는 라이브러리들도 존재하는 것 같은데 다음에 자세히 찾아봐야겠다.

 


 

크롬과 엣지에서 다른 계정으로 접속하고 채팅을 보낸 것이다. 제대로 작동한다~

채팅 기능이 완성되어서 친구들에게 배포할 수 있는 상태가 되긴 했으나

아직 레이아웃 보완 또는 세부적인 기능 구현할게 많이 남아있어서 천천히 배포할 생각이다ㅎㅎ

오늘 열심히 개발해서 뿌듯

Firebase Realtime Database를 import할 때 오류가 발생해서 막혀있었는데

최근에 나온 9 버전을 이용해보면 괜찮을까 싶어서 새로 9 버전을 설치했다.

 

Firestore 관련 코드는 이미 8 버전으로 작성을 했기 때문에 건드리지 않고

Realtime DB만 9 버전 코드를 작성했는데 다행히도? 어째서인지? import 오류가 더이상 발생하지 않았다.

 

import firebase from 'firebase/compat/app';
import 'firebase/compat/firestore';
import 'firebase/compat/auth';
import { getDatabase } from 'firebase/database';

const firebaseConfig = {
	//config
};

firebase.initializeApp(firebaseConfig);
const firestore = firebase.firestore();  //firestore DB 연결
const auth = firebase.auth;
const database = getDatabase();

export {firestore, auth, database};

 

9 버전으로 업데이트를 해도 compat 폴더를 import하면 이전 버전을 사용할 수 있다.

Realtime DB 공식 문서에 9 버전 코드가 업데이트 되어있어서 해당 내용을 참고하여 작성했다.

 

https://firebase.google.com/docs/database/web/read-and-write#web-v9_1

 

웹에서 데이터 읽기 및 쓰기  |  Firebase 실시간 데이터베이스

의견 보내기 웹에서 데이터 읽기 및 쓰기 (선택사항) Firebase 로컬 에뮬레이터 도구 모음으로 프로토타입 제작 및 테스트 앱에서 실시간 데이터베이스의 데이터를 읽고 쓰는 방법에 대해 논의하

firebase.google.com

 

import 에러 해결 이후엔 DB와의 연결은 순조로웠다.

오늘은 1. 친구 추가 시 DB에 채팅방 멤버 등록하기, 2. 채팅 전송 시 DB에 채팅 기록 저장하기

이렇게 2개의 기능을 구현해서 DB에 데이터 등록이 제대로 되는지 확인해보았다.

 

채팅방과 관련된 DB 구조는 크게 ChatMembers와 ChatMessages로 나눌 수 있다.

 

ChatRooms

ㄴ채팅방 번호

  ㄴChatMembers (채팅방 멤버)

    ㄴ멤버 아이디

      ㄴemail : 멤버 이메일

      ㄴisMember : true

  ㄴChatMessages (채팅 메세지)

    ㄴ채팅 날짜 (yyyyMMdd)

      ㄴ채팅 번호

        ㄴcontent : 내용

        ㄴsenderId : 보낸 사람 아이디

        ㄴtime : 보낸 시간

 

아직 1:1 채팅만 가능한 상태이긴 하지만 추후 단톡방 기능을 추가하여 확장하는 것을 고려해서 ChatMembers를 만들었다.

 

DB에 데이터를 저장하는 코드 :

import { database } from '../services/firebase';
import { ref, set } from 'firebase/database';

export function addChatMember(chatRoomNum, id, email) {
    set(ref(database, '/ChatRooms/' + chatRoomNum + '/ChatMembers/' + id + '/'), {
        email: email,
        isMember: true
    });
} 

export function addChatMessage(chatRoomNum, date, messageCode, content, id, sendTime) {
    set(ref(database, '/ChatRooms/' + chatRoomNum + '/ChatMessages/' + date + '/' + messageCode + '/'), {
        content: content,
        senderId: id,
        time: sendTime
    });
 }

 

이제 DB에 저장된 데이터를 불러오는 기능을 구현해야 한다.

+ Recent posts