Skip to content

gomin-solution/Backend

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

image

📃 고민접기, 당신의 고민을 접어드립니다.

thumbnail-01

오늘 뭐 먹지…?” “이럴 땐 어떻게 해야 하는 걸까…”
너무 사소하거나 너무 무거워서 남들에게 쉽게 꺼낼 수 없었던 고민이 있으신가요?
저희 고민접기는 여러분들의 크고 작은 다양한 고민을 접을 수 있도록 도와주는 커뮤니티입니다.

📱 고민접기 링크
📎 Frontend


## 💡 **_주요 기능_**
매일 색다른 행운의 편지열기 골라주기 작성 후 투표 답해주기 작성 및 의견받기
유저와 1:1 쪽지하기 미션 완료 후 종이접기 획득하기 미션 완료에 따른 등급 상승

+) 검색 기능, 알림 기능 도입

🛠️ 프로젝트 아키텍처

image


🛠️ ERD

main_project_erd 클릭시 확대됩니다☝


⚙️ 기술 스택

Backend








🔩 기술적 의사 결정

사용기술 선정이유
MySQL 데이터간의 관계를 형성하고 참조 무결성을 유지하기 위해 MySQL을 메인DB로 작업했습니다.
MongoDB 신고 데이터의 경우 유연한 스키마 구조가 필요했고 확장성과 작업 효율을 높이기 위해 부분적으로 MongoDB를 도입하였습니다.
Redis RefreshToken과 알람데이터등 휘발성이 있는 데이터를 효율적으로 관리하기 위해 Redis를 사용했습니다.
memcached와 비교했을때 다양한 데이터 타입을 지원한다는 점과 특정 시점에 데이터를 디스크에 저장할 수 있다는 점에서 Redis를 선택했습니다.
socket.io socket.io가 webSocket만 사용했을때 보다 연결 신뢰성이 높고 room기능을 제공하여 채팅기능을 구현하기에 적합했습니다. 브라우저마다 지원하지 않는 경우가 있는 웹소켓과 달리 socket.io는 대부분의 브라우저에서 작동합니다.
FCM 푸쉬 알람 기능을 도입하여 사용자 경험을 향상시키고, 활발한 서비스 이용을 유도하였습니다. FCM을 사용하여 백그라운드와 포그라운드 환경에서 알림을 받도록 적용하였습니다.
Lambda multer를 사용하여 업로드한 이미지를 리사이징하는 용도로 사용되었습니다. 서버에서 리사이즈를 진행 할 수 있었지만 리사이징이 될 때 서버 리소스를 많이 사용하고 다른 응답을 받지 못하게되는 상황이 있어 서버리스 함수인 람다를 선정하였습니다.

⚠️ 트러블 슈팅

미션 테이블 정규화

미션 테이블 데이터 삽입이상 현상
미션마다 가지는 퀘스트가 달라 해당 퀘스트가 없는 미션은 불필요 공간이 생김. image


💡정규화
미션테이블을 분리하고 퀘스트 마다 매핑테이블을 만들어 분리함.
image
테이블을 나눴기 때문에 Join이 많이 발생하지만, 기능 특성상 가져오는 데이터의 양이 많지 않아 성능차이가 거의 없음. 때문에 테이블을 나누는 것으로 결정.

리워드 페이지 속도 개선문제: 리워드 페이지의 서버 응답이 평균 2초 후반대가 걸림

원인파악: 코드를 주석해가며 찾은 결과 DB에서 유저활동 기록을 가져오는데 2초대가 걸림.

💡필요한 데이터만 가져오기 위해 attribute속성을 사용해 속도를 개선

  totalReword = async (userKey) => {
    return await User.findOne({
      where: { userKey: userKey },
      include: [
        {
          model: Comment,
          include: [{ model: CommentLike }, { model: CommentSelect }],
        },
        { model: isChoice },
        { model: Advice },
        { model: Choice },
        { model: CommentSelect },
      ],
    });
  };

image

    totalReword = async (userKey) => {
    const totalreward = await User.findOne({
      where: { userKey: userKey },
      include: [
        {
          model: Comment,
          include: [
            { model: CommentLike, attributes: ["userKey"] },
            { model: CommentSelect, attributes: ["userKey"] },
          ],
          attributes: ["userKey"],
        },
        { model: isChoice, attributes: ["userKey"] },
        { model: Advice, attributes: ["userKey"] },
        { model: Choice, attributes: ["isEnd"] },
        { model: CommentSelect, attributes: ["userKey"] },
      ],
    });
    return totalreward;
  };

image

문제 2: attribute 속성을 사용하여 속도가 개선 되었지만, 유저의 활동이 쌓일 수록 리워드 페이지 응답 시간이 길어지는 현상을 발견함.

원인: 유저활동기록(ex.게시글 작성횟수, 조언해준 횟수 등)를 기존 테이블들을 Join하여 얻을 수 있는 데이터라 판단하여 따로 저장하지 않았지만, 유저의 활동이 쌓일 수록 Join해서 가져오는 데이터도 같이 증가하여 위와 같은 문제가 생김.

💡유저 활동기록 테이블을 따로 만들어 유저의 활동이 있을때 마다 활동 기록 데이터를 업데이트시킴.
리워드 페이지 요청시 유저 활동 정보를 가져오기 위한 불필요한 Join이 없어지고 이미 업데이트된 데이터를 가져와 연산을 줄여 최종적으로 평균 2초 후반 에서 0.2초 후반으로 속도를 약 90% 개선할 수 있었음.

image

스케줄 초기화 문제

문제: 투표하기 게시글 자동 마감기능이 정상적으로 작동하지 않아 마감시간이 -로 표시됨.

원인파악: 서버가 꺼지면 스케줄 스택이 초기화 되어 스케줄이 작동하지 않아 발생한 문제.

💡문제해결: 서버 실행시 스케줄이 재설정되도록 수정하고
마감기한이 이미 지난 게시글은 현재시간과 비교하여 마감처리 되도록 로직을 추가하여 문제를 해결.

  const findAllChoice = await Choice.findAll({
    attributes: ["choiceId", "endTime", "isEnd", "userKey"],
  });

  for (const choice of findAllChoice) {
    const scheduleDate = dayjs(choice.endTime).format();

    //마감기한이 이미 지난 게시물은 마감처리
    if (scheduleDate < dayjs().tz().format() && !choice.isEnd) {

      //isEnd업데이트
      await new ChoiceRepository().updateEnd(choice.choiceId);

      //작성자 마감횟수 +1
      await new MissionRepository().choiceEndActivity(choice.userKey);
    } else if (!choice.isEnd) {

      //스케줄 재설정
      schedule.scheduleJob(
        dayjs(choice.endTime).subtract(9, "hour").format(),
        async () => {
          //isEnd업데이트
          await new ChoiceRepository().updateEnd(choice.choiceId);

          //작성자 마감횟수 +1
          await new MissionRepository().choiceEndActivity(choice.userKey);
        }
      );
    }
  }

Safari 브라우저의 쿠키 전송 이슈

문제: Safari 브라우저에서 쿠키가 전달되지 않는 문제

원인파악: Safari 브라우저의 ITP에 의해 교차 도메인의 쿠키를 차단하여 생긴문제

💡의사결정: 이를 해결하기 위해 서버와 클라이언트의 도메인을 동일 출처로 맞춰야 했지만,배포환경을 변경하는것이 코스트가 크다고 판단하여 도메인을 변경하지 않고 토큰 발급 방식을 바디로 전송하는 것으로 변경하여 문제 해결.

다른 문제: 토큰을 바디로 발급하는 것으로 Safari의 토큰전달 문제를 해결하였지만, 토큰 재발급시 서버에서 다음 미들웨어를 응답할 수 없는 문제가 발생.

원인파악: 기존의 방식은 토큰 재발급시 재발급후 바로 쿠키에 토큰을 싣고 다음 미들웨어를 호출해 기존의 요청을 처리하였으나, 토큰을 바디로 보내게 되면서 다음 미들웨어에서 이중으로 응답할 수 없는 문제가 발생.

💡문제해결: 클라이언트 에서 Interceptor를 적용하여 서버에서 주는 재발급 토큰을 감지하고 재발급 받은 토큰을 헤더에 실어 서버에 재요청 하는 방식으로 문제해결!


📃 유저 피드백 개선

총 피드백 78개 중, 유효 피드백 58개, 반영 피드백 46개

  • 인트로 페이지 이미지 리사이징 및 서버인증 미들웨어 개선을 통한 서비스 속도 향상
  • Intro에서 슬라이드 버튼과 스킵 버튼을 추가하여 편의성을 증대
  • 골라주기 항목 타이틀의 가독성을 높이고 본인이 선택한 항목 표기
  • 받아올 데이터가 없어서 빈 화면일 때 단순 텍스트가 아닌 이미지를 넣어서 완성도 향상

📌 유저 피드백 정리


📸 고민 접기의 팀원

🍀홍정표 정도은 🍀이승표 이준 손민성 이현서
스크린샷 2022-12-16 오후 1 58 16 스크린샷 2022-12-16 오후 1 58 20 스크린샷 2022-12-16 오후 1 58 26 스크린샷 2022-12-16 오후 1 56 58 스크린샷 2022-12-16 오후 1 58 12 스크린샷 2022-12-16 오후 2 08 05
FE FE BE BE BE DE

About

고민접기 프로젝트

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •