모해유 프로젝트

[개발일지]랜덤으로 결과 보여주기

아보카도 있었어! 2023. 11. 12. 19:12

 사용자의 응답에 맞게 결과를 랜덤으로 보여주는 서비스를 제작 중이다.

 원하는 화면을 위해 구현되어야할 것은

 

1. 응답별 결과 매칭 설계

2. 사용자의 응답을 저장

3. 응답과 결과를 매칭하는 로직

 

이 세 가지가 필요하다.

1. 응답별 결과 매칭 목록은 기획을 담당한 팀원과 작성했다. 아래와 같은 화면이 구현되어야 하기 때문에,

'모해유 프로젝트' 와이어프레임(결과 페이지)

 

결과 응답으로 받을 json의 프로퍼티를 다음과 같이 구성하였다.(※ 서브 텍스트는 공통되므로 응답 값으로 받아오지 않는다.)

타입스크립트를 활용한 프로젝트가 아니기 때문에프로퍼티에만 집중하기 쉽도록 선언하였다.

  {
    id: number;
    activityName: string;
    characterGroup: characterGroupType;
    isOutside: boolean;
    imageUrl: string;
  },

 

 

characterGroupType 은 아래 중 하나의 값이다. 프로젝트에서는 상수로 선언하여 사용하였다.

group1 = ["ENFJ", "ESFJ", "ESTP", "ESFP"];
group2 = ["ESTJ", "ENTJ", "ENTP", "ENFP"];
group3 = ["ISFP", "INFJ", "ISFJ", "INFP"];
group4 = ["ISTP", "ISTJ", "INTJ", "INTP"];

 

응답마다 결과를 결정하는 프로퍼티를 설정했다.

  {
    id: number;
    surveyContent: string;
    optionContents: [
      {
        id: number;
        content: string;
      },
      {
        id: number;
        content: string;
      },
    ],
    selectResult: selectResultType
  },

 

selectResultType 은 아래 중 하나의 값이다. 프로젝트에서는 리터럴 값으로 대체했다.

["E", "I"], ["N", "S"], ["T", "F"], ["J", "P"]

 

사용자의 응답이 최종적으로 종료되면, 응답과 매치되는 결과의 characterType을 가지고 있는 활동 중 하나가 결과 페이지에 출력될 것이다.

 

2. 사용자의 응답 저장하기

 사용자의 응답을 '어디에' 저장할 것인가? 에 대해 고민했다. 응답을 state로 관리한다면, 사용자가 테스트를 하다가 페이지를 벗어났다가 테스트로 돌아왔을 때 처음부터 응답해야했기 때문이다. 그 외에도 여러 경우의 수들이 존재했다.

  • 사용자가 url을 조작하여 1번 문제 페이지에 2번 문제 페이지를 건너뛰고, 3번 문제로 이동하는 경우
  • 3번 문제 페이지에서 새로고침을 하는 경우
  • 위 현상을 프론트 측에서 방지하지 않아서 1번 문제와 3번 문제 등 일부 응답만 전달될 경우

등등 전역상태이든, 로컬 상태이든 사용자의 응답 값을 '변수'로 관리한다면 새로고침 시에 사용자는 다시 1번 페이지로 넘어갈 수밖에 없었다. 이에 값을 새로고침 시에도 유지할 수 있도록 구현하기로 했고, 브라우저 창을 닫고 나서까지 값을 유지할 필요없는 서비스의 성격을 판단해 웹스토리지 중에서도 세션 스토리지를 선택하게 되었다.

 

 세션 스토리지에 사용자의 응답을 저장하고, 마지막 질문 페이지에서 사용자가 답을 선택하면 결과 페이지로 넘어갈 수 있도록 하였다. 

 

 사용자가 문제에 응답을 하면, 응답한 버튼의 index를 받아와 index가 같은 값을 찾아 저장한다.

questionId를 url에 활용하고 있기 때문에, 1부터 시작한다.

 

 

3. 응답과 결과를 매칭하는 로직

 사용자가 마지막 문제에 응답을 하면, selectResult selectOption의 value에 저장된 index와 일치하는 항목을 결과로 하여 도출하고, 해당 결과 값이 속해있는 characterGroup에 해당하는 활동 중 하나를 랜덤으로 출력한다.

 

 마지막 질문은 날씨를 묻는 질문으로, 실내/외 활동 목록을 구분 짓는 항목이다. 만약 외향적인 성격이라 실외에 해당하는 활동을 추천받아야 하지만, 사용자의 날씨 응답에 따라 실내 활동으로 결과를 출력할 수 있도록 하였다.

 

 즉, 결과에 E가 포함되고, 날씨도 실외 활동이 가능한 응답을 받았을 경우, charcterGroup에 해당하는 리스트를 가지고 해당 리스트 안에서 랜덤으로 결과를 출력한다.

반면에 결과에 E가 포함되지만, 날씨가 실외 활동이 가능하지 않은 응답이라면, isOutside 값에 따라 분기된 활동 리스트를 가지고 해당 리스트 안에서 랜덤으로 결과를 출력할 것이다.

  function getResult() {
    const getStorage = sessionStorage.getItem(SESSION_SELECTED_OPTIONS_KEY);

    if (!getStorage) navigate(`/question/1}`);

    const resultCharacter = JSON.parse(getStorage).reduce((prev, current) => {
      const question = surveyQuestions.find(
        (q) => q.id + 1 === current.questionId
      );
      return prev + question.selectResult[current.selectedOption];
    }, "");

    // 1. 마지막 글자에 따라 활동이 실내인지 실외인지 확인한다
    const isOutside = resultCharacter.charAt(resultCharacter.length - 1);
    // 2. 결과 MBTI와 실내/외가 일치하는지 확인한다
    const character = resultCharacter.slice(0, 4);
    // 3. 일치한다면, 해당 MBTI를 가진 활동을 모아, 랜덤 리스트를 만든다.
    const randomList =
      isOutside === character.charAt(0)
        ? surveyResults
            .filter((result) => result.characterGroup.includes(character))
            .map((activity) => activity.id + 1)
        : // 4. 일치하지 않는다면, 현재 날씨가 들어간 활동을 모아, 랜덜 리스트를 만든다.
          surveyResults
            .filter((result) =>
              isOutside === "E" ? result.isOutside : !result.isOutside
            )
            .map((activity) => activity.id + 1);
    // 5. 랜덤 결과를 출력한다.
    const nextResultId = getRandomIndex(randomList);
    return nextResultId;
  }

 

 

랜덤 결과를 출력하는 함수는 다음과 같이 Math.random() 메서드를 활용하였다.

function getRandomIndex(arr) {
  const randomIndex = Math.floor(Math.random() * arr.length);
  return arr[randomIndex];
}