카테고리 없음

immutable (불변성)을 지키며 작성하는 코드 (feat.javascript)

shiningjean 2022. 6. 29. 16:53

Immutable

npm: immutable

왜 immutable을 사용하는가??
=> 로직 외의 부분에서 내용이 수정될 수 있다.

 

function sleep(ms) {
  return new Promise((r) => setTimeout(r, ms));
}

async function logicA1(mymap) {
  await sleep(100);
  mymap['a'] = 1;
  await sleep(100);
  console.log("logicA1", mymap['a']);
};
async function logicA2(mymap) {
  await sleep(100);
  mymap['a'] = 2;
  await sleep(100);
  console.log("logicA2", mymap['a']);
};

async function main() {
  const mymap = {};
  await Promise.all([logicA1(mymap), logicA2(mymap)])
};

main();

 

 

 

Aggregation 

데이터들을 조합해서 원하는 데이터 형태로 만들어주는 과정.

const { Map } = require('immutable');

const users = Map([
  ["user1", {name: "A", phone: "010-xxxx-xxx1"}],
  ["user2", {name: "B", phone: "010-xxxx-xxx2"}],
  ["user3", {name: "B", phone: undefined}],
]);

console.log(users.map(e => e.phone).filter(e => e !== undefined).reduce((r, n) => r + n, '')) // 010-xxxx-xxx1010-xxxx-xxx2

 

mongodb에서도 aggregation이란게 있는데 nosql에는 join이 없어서 map.reduce방식으로 join해서 결과를 만들어낸다!

천만 건이 넘을 경우는 RDBMS의 조인보다 훨씬 성능이 좋다! map.reduce는 다양하게 쓰임. (피보나치 수열같은 동작원리)

 

 

List -> Map (List를 Map으로)

const { Map, List } = require('immutable');

const users = List([
  {id: "user1", name: "A", phone: "010-xxxx-xxx1"},
  {id: "user2", name: "B", phone: "010-xxxx-xxx2"},
  {id: "user3", name: "B", phone: undefined},
]);

console.log(
    Map(users.map(e => [e.id, { name: e.name, phone: e.phone }])).toJS()
)
// {
//   user1: { name: 'A', phone: '010-xxxx-xxx1' },
//   user2: { name: 'B', phone: '010-xxxx-xxx2' },
//   user3: { name: 'B', phone: undefined }
// }

 

실제 사용 사례

RESTful하게 바꾸면 모든 join된 데이터를 한번에 주는게 아니라 front단에서 여러 API를 가져와 aggregation처럼 join해서 전처리 해서 사용

 

ex) getUser에서 userId를 주면 (immutable한 param) Map으로 value로 유저 정보를 내려주겠다.

const { Map } = require('immutable');

async function getFeeds(...): Map<string, {...}>;
async function getLikes(currentUserId: string, feedIds: Set<string>): Map<string, boolean>;
async function getUsers(userIds: Set<string>): Map<string, {...}>;
async function getBrokers(userIds: Set<string>): Map<string, {...}>;

async function main() {
  const feeds = await getFeeds();
  // Map([
  //   ["feed1", {writer: "user1", contents: "아아아아아..."}],
  //   ["feed2", {writer: "user1", contents: "가가가가가..."}],
  //   ["feed3", {writer: "user2", contents: "나나나나나..."}],
  //   ["feed4", {writer: "user2", contents: "다다다다다..."}],
  //]);
  
  const [users, likes] = await Promise.all([
    getUsers(feeds.map(e => e.writer)).toSet(),
    //Map([
    //  ["user1", {name: "A", type: "BROKER", phone: "010-xxxx-xxxx"}],
    //  ["user2", {name: "B", type: "CUSTOMER", phone: "010-xxxx-xxxx"}],
    //]);
    getLikes("currentUserId", feeds.keySeq().toSet())),
    //Map([
    //  ["feed1", true],
    //  ["feed2", false],
    //  ["feed3", false],
    //  ["feed4", false],
    //]);
  ]);

  const brokers = await getBrokers(users.filter(e => e.type === "BROKER").keySeq().toSet());
  //Map([
  //  ["user1", {score: 4.5}]
  //]);
  
  const combinedFeeds = feeds.map((e, k) => {
    const user = users.get(e.writer);
    const like = likes.get(k);
    const broker = brokers.get(e.writer);
    return {
      writer: {
        id: e.writer,
        type: user.type,
        score: broker?.score,
        name: user.name,
        phone: user.phone
      },
      like: like === true,
      contents: e.contents
    }
  })
  
  console.log(combinedFeeds.get("feed1"));
}

 

* get보다 upert같은 업데이트 치는 로직이 어떻게 보면 더 중요하다(사견)

동남아 같은 네트워크 상황이 낙후된 지역에서는 데이터를 묶어서 보내주기도 하는게 나을 수 있음.