그 쪽도 Map을 아세요?

Daniel Woo·2023년 9월 2일
0

얼마 전 온보딩 과제를 진행하면서 자바스크립트의 Map 자료구조를 사용하기 좋은 문제를 접했다. ES6의 Map 자료구조를 사용해서 문제를 해결한 적이 많이 없었는데 이번 기회에 많이 알고 가게 되어 다른 간단한 문제를 통해 어떤 점에서 유용한지 공유한다.

ES6의 Map 자료구조

Map은 자바스크립트에서 사용할 수 있는 Hash Table 자료구조이다. Hash Map과 Table은 쓰레드 관점에서 안전성의 유무에 있다지만, 거의 동일한 것으로 간주하고 우리는 자바스크립트에서 다루는 Map에 대해서만 정리해보자. Map - Javascript | Mdn 문서에 따르면

Map 객체는 키-값 쌍과 키의 원래 삽입 순서를 기억합니다. 모든 값(객체 및 원시 값 모두)은 키 또는 값으로 사용될 수 있습니다.

조금 더 세분화 해보자

key: value 쌍

  • key는 오직 단 하나만 존재
const ES6Map = new Map();

ES6Map.set(1, 1)
ES6Map.set(1, 2)
ES6Map.set(1, 3)

console.log(ES6Map); // Map(1) { 1 => 3 }

키는 오직 하나만 같기 때문에 같은 키를 사용하여 또 다른 값을 넣으면 대체된다.

삽입 순서를 기억

  • 순서를 기억해야하는 상황에 유용할 수 있겠다

모든 값이 키 또는 값으로 사용될 수 있다.

  • 그러면 객체 자체도 키가 될 수 있으려나 => 될 수 있다!
const ES6Map = new Map();

ES6Map.set(1, 3);
ES6Map.set({
    name: 'Jackson'
}, {
    age: 18,
    favorites: ['apple', 'juice', 'apple-juice']
});

console.log(ES6Map);

// Map(2) {
//  1 => 3,
//  { name: 'Jackson' } => { age: 18, favorites: [ 'apple', 'juice', 'apple-juice' ] }
// }
  • 하지만, 보기에 같은 객체라고 해서 같은 키는 아니다.
const jackson = ES6Map.get({
	name: 'Jackson'
});

console.log(jackson); // undefined 

위에서 객체 형태가 key가 될 수 있는 것을 확인하여 '동일하게 보이는'객체 키를 사용했더니 undefined를 반환한다. 객체도 key로 사용할 수 있다고 했는데 왜 그럴까?
아마, 객체는 참조값이기 때문에 동일한 프로퍼티를 갖춘 형태의 객체더라도 다른 메모리 주소를 참조하기 때문이 아닐까 싶다. 따라서 객체를 key로 하는 경우는 다음과 같이 객체를 변수에 담아 값을 다시 조회할 수 있다.

// 생략
const jackson = {
    name: 'Jackson'
};
ES6Map.set(jackson, {
    age: 18,
    favorites: ['apple', 'juice', 'apple-juice']
})

console.log(ES6Map.get(jackson));
// { age: 18, favorites: [ 'apple', 'juice', 'apple-juice' ] }

어떤 문제를 해결할 수 있을까

자료구조는 어떠한 문제상황을 더 효율적으로 해결할 수 있는 기반을 만들어주는 것이라고 생각한다. 특정 자료구조가 좋다라는 것은 어떤 상황을 더 쉽게 해결해주기 때문이어야 한다.

그렇다면 ES6의 Map은 어떤 경우에 사용하는 것이 효율적일까? 최근 온보딩 과제를 하면서 Array와 Map 각각의 자료구조를 사용하여 문제를 해결한 경험이 있다.

간단한 퀴즈게임

친구들과 모이는 자리에서 이벤트를 위해 간단한 퀴즈 게임 애플리케이션을 만들고 싶다.

  1. 질문의 테마, 난이도, 개수를 선택하고 해당하는 질문 데이터를 요청한다.
  • 질문 데이터는 다음과 같은 형태를 가진다.
    interface Question {
        id: number;
        title: string;
        description: string;
        imageUrl: string;
    }
  1. 퀴즈 게임을 진행한다.
  • 유저는 각 질문 당 퀴즈의 정답을 고르고
  • 풀었던 질문의 id를 통해 각 질문정답을 불러온다.
	interface UserAnswer {
    	id: number;
      	choice: string;
    }
ex).
axios.get(`/baseUrl/questions?ids=[1234,2345,6789]`)
  • 정답은 다음과 같은 형태를 가진다.
    interface Answer {
		id: number;
      	problemImage: string;
      	answerImage: string;
      	description: string;
    }
  1. 유저가 선택한 정답과 실제 정답을 비교하여 문제 당 정오답을 화면에 노출한다.
  • 문제 리스트
  • 유저가 선택한 정답
  • 실제 정답

접근

처음에는 문제를 해결하기 위해서 다음과 같이 접근했다.

  1. 배열을 만들고 요청해서 받아온 질문을 넣어서 관리한다.
const questions:Question[] = [];
  1. 그리고 각 질문은 유저가 선택한 정답실제 정답을 가진다.
	interface QuestionWithAnswer extends Question{
    	userAnswer: UserAnswer;
      	answer: Answer;
    }
  1. 정오답 노출 화면에서 문제 리스트를 map을 사용하여 나열하고 정오답을 비교한다.
const QuestionList = () => {
	reutrn (
    	<ul>
        {questions.map((question)=> (
        	<li key={question.id}>
            {question.title}
            {question.description}
            {question.userAnswer.choice === question.answer.description ? 'O' : 'X'}
          	</li>
        ))}
      	</ul>
    )
}

이러한 방식으로도 문제를 해결할 수는 있으나, 정답 데이터를 활용하거나 변경할 때마다 질문 리스트를 순회해야한다는 점과 컴포넌트 안에서 로직과 UI가 분리되지 못하고 있다는 점이 효율적이지 못하다고 생각했다.

개선

질문이 유저가 선택한 정답과 실제 정답을 '소유'하고 있는 구조이다보니, 정답은 질문과 너무 강한 결합도를 가지고 있다. 이를 끊어내기 위해 Map자료 구조를 사용하여 질문 id를 key로 하여 각각의 Map 형태의 해시 테이블을 만들었다.

const questions = new Map<number, Question>();
const userAnswers = new Map<number, UserAnswer>();
const answers = new Map<number, Answer>();

그리고 이를 통합할 수 있는 하나의 객체를 생성하여 id값을 통해 질문, 유저가 선택한 답, 실제 답에 모두 접근할 수 있는 형태로 개선하였다.

class Composition {
  const list = new Map<number, {
      questions: Question;
      userAnswers: Answer;
      answers: Answer;
  }>();
  
  get all(){
  	return Array.from(list.values());
  }
} 
const QuestionList = () => {
	reutrn (
    	<ul>
        {Array.from().map((question)=> (
        	<li key={question.id}>
            {question.title}
            {question.description}
            {question.userAnswer.choice === question.answer.description ? 'O' : 'X'}
          	</li>
        ))}
      	</ul>
    )
}

profile
모두가행복한세상을만들고싶은사람

0개의 댓글